隔离环境试装插件,验证无误再进日常配置
29.9/100
综合分
0
GitHub 星标
—
npm 周下载
—/5
用户评分
数据截至 2026/10/12 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add wqzhellohhwy/dsh-profile-lab该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)
状态速览
判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态信任档位:待复验
是什么
dsh 原生插件 · dev
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 20 天前
档位由哪些信号合成
本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。
社区实装还没有人打卡 · 装过的第一个就是你
安全扫描自动 · 静态分析源码v2.7.0🔵 扫描:含敏感能力尚无人工评估展开查看扫描说明与证据 ↓
安全扫描自动 · 静态分析源码
v2.7.0静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。
可执行外部命令读写/删除本地文件发起外部网络请求
查看扫描证据(共 99 处,展示前 51 条)
- 读取本地文件lib/lab.js:102
if (!existsSync(file)) return { version: MANIFEST_VERSION, profiles: {} } - 读取本地文件lib/lab.js:103
const raw = readFileSync(file, 'utf8') - 写入本地文件lib/lab.js:119
mkdirSync(dirname(file), { recursive: true }) - 写入本地文件lib/lab.js:121
writeFileSync(tmp, `${JSON.stringify(manifest, undefined, 2)}\n`, 'utf8') - 写入本地文件lib/lab.js:122
renameSync(tmp, file) - 写入本地文件lib/lab.js:132
mkdirSync(dirname(file), { recursive: true }) - 写入本地文件lib/lab.js:133
writeFileSync(file, `${JSON.stringify({ at: new Date().toISOString(), ...entry })}\n`, { flag: 'a', encoding: 'utf8' }) - 读取本地文件lib/lab.js:144
if (!existsSync(file)) return undefined - 读取本地文件lib/lab.js:146
const json = JSON.parse(readFileSync(file, 'utf8')) - 读取本地文件lib/lab.js:168
return statSync(dir).isDirectory() && !name.startsWith('.') - 读取本地文件lib/lab.js:224
if (!existsSync(srcDir)) return { linked, failed } - 写入本地文件lib/lab.js:225
mkdirSync(dstDir, { recursive: true }) - 删除本地文件lib/lab.js:533
unlinkSync(path) - 删除本地文件lib/lab.js:537
rmdirSync(path) - 删除本地文件递归lib/lab.js:575
rmSync(dir, { recursive: true, force: true }) - 删除本地文件递归lib/lab.js:660
if (existsSync(sessionDir)) rmSync(sessionDir, { recursive: true, force: true }) - 删除本地文件lib/lab.js:674
rmdirSync(dir) - 读取本地文件lib/sessions.js:54
if (!existsSync(file)) throw new Error(`会话日志不存在:${file}`) - 读取本地文件lib/sessions.js:55
const buf = readFileSync(file) - 读取本地文件lib/sessions.js:223
const stat = statSync(file) - 读取本地文件lib/sessions.js:224
const buf = readFileSync(file) - 执行外部命令来自配置/变量lib/web.js:207
child = spawn(dsh.execPath, args, { cwd, env, windowsHide: true, stdio: ['ignore', 'pipe', 'pipe'] }) - 发起网络请求lib/web.js:282
const response = await fetch(url, { - 读取本地文件lib/guards.js:137
return lstatSync(target).isSymbolicLink() - 读取本地文件lib/guards.js:209
return readdirSync(dir) - 读取本地文件lib/guards.js:222
const buf = readFileSync(file) - 读取本地文件lib/guards.js:236
if (existsSync(file)) return file - 读取本地文件lib/guards.js:252
if (!existsSync(root)) continue - 读取本地文件lib/guards.js:254
if (existsSync(guess)) return guess - 写入本地文件lib/guards.js:299
mkdirSync(dirname(lockPath), { recursive: true }) - 写入本地文件lib/guards.js:305
writeFileSync(fd, JSON.stringify({ pid: process.pid, at: new Date().toISOString() })) - 删除本地文件lib/guards.js:317
unlinkSync(lockPath) - 删除本地文件lib/guards.js:336
unlinkSync(lockPath) - 读取本地文件lib/runner.js:80
for (const entry of readdirSync(runtimeRoot)) { - 执行外部命令来自配置/变量lib/runner.js:109
child = spawn(candidate.execPath, [candidate.binPath, '-V'], { env, windowsHide: true, stdio: ['ignore', 'pipe', 'pipe'] }) - 执行外部命令固定命令lib/runner.js:166
spawn('taskkill', ['/pid', String(pid), '/T', '/F'], { windowsHide: true, stdio: 'ignore' }) - 读取本地文件lib/runner.js:229
const st = lstatSync(path) - 删除本地文件lib/runner.js:231
rmSync(path, { force: true }) - 删除本地文件lib/runner.js:234
rmSync(path, { recursive: st.isDirectory(), force: true }) - 读取本地文件lib/runner.js:256
const st = lstatSync(p, { throwIfNoEntry: false }) - 删除本地文件lib/runner.js:257
if (st && st.isSymbolicLink()) rmSync(p, { force: true }) - 写入本地文件lib/runner.js:264
mkdirSync(selfIso, { recursive: true }) - 读取本地文件lib/runner.js:270
names = readdirSync(selfReal) - 读取本地文件lib/runner.js:279
if (existsSync(dst) || lstatSync(dst, { throwIfNoEntry: false }) !== undefined) { - 读取本地文件lib/runner.js:279
if (existsSync(dst) || lstatSync(dst, { throwIfNoEntry: false }) !== undefined) { - 写入本地文件lib/runner.js:283
else copyFileSync(src, dst) - 写入本地文件lib/runner.js:318
else copyFileSync(real, link) - 写入本地文件lib/runner.js:350
mkdirSync(labDshHome, { recursive: true }) - 执行外部命令来自配置/变量lib/runner.js:397
child = spawn(dsh.execPath, [dsh.binPath, '--profile', profile, '--help'], { - 执行外部命令来自配置/变量lib/runner.js:570
child = spawn(dsh.execPath, args, { cwd, env, windowsHide: true, stdio: ['ignore', 'pipe', 'pipe'] }) - 执行外部命令来自配置/变量lib/runner.js:648
child = spawn(dsh.execPath, [dsh.binPath, ...args], {
扫描于 2026/11/1(0 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库
静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个
功能亮点
- 一键建隔离profile
- 装插件跑探针会话
- 读回摘要并安全回收
DSH 临时 profile 实验台:一键建隔离 profile(模板建 / 从现有 profile 克隆)、装插件、跑 headless 探针会话、读回会话摘要并安全回收——改插件不再怕把本体改暴毙。零第三方依赖。
README
由 DeepSeek 最新模型翻译生成dsh-profile-lab
给 DeepSeek Harness(DSH)用的临时 profile 实验台:一条命令建出隔离的 profile,装插件、跑 headless 探针会话、读回结构化摘要、安全回收。
它存在的唯一理由:改插件时怕把本体改暴毙——先在隔离环境里验,确认无误再装进日常用的 profile。
- 宿主半侧插件(Cordis),注册 6 个 tmpprofile_* 模型工具;没有浏览器半侧,不声明 dsh.client。
- inject 只写 ['tools'](不写 webServer)——所以在 headless / tui 这类没有 HTTP 服务的 profile 里不会整体 pending。
- 零第三方运行时依赖:只用 node:fs / node:path / node:child_process / node:zlib / node:crypto。
展开完整文档(README 剩余 1 节)
dsh-profile-lab
给 DeepSeek Harness(DSH)用的临时 profile 实验台:一条命令建出隔离的 profile,装插件、跑 headless 探针会话、读回结构化摘要、安全回收。
它存在的唯一理由:改插件时怕把本体改暴毙——先在隔离环境里验,确认无误再装进日常用的 profile。
- 宿主半侧插件(Cordis),注册 6 个 tmpprofile_* 模型工具;没有浏览器半侧,不声明 dsh.client。
- inject 只写 ['tools'](不写 webServer)——所以在 headless / tui 这类没有 HTTP 服务的 profile 里不会整体 pending。
- 零第三方运行时依赖:只用 node:fs / node:path / node:child_process / node:zlib / node:crypto。
它解决什么
| 老做法(每次现写脚本) | 本插件 |
| 想隔离测试,但官方 --from-default-profile 只能从 5 个内置模板初始化、不能从现有 profile 克隆,还要求目标目录不存在 → 手工 New-Item + 写 4 个文件 | tmpprofile_create:模板建 或 clone: "desktop" 克隆(配置复制 + node_modules 逐项链接复用) |
| 以为 --from-default-profile 必然启动,没法"只建不跑" | 内部走 --dump-config 短路 boot(只建、不启动) |
| 跑探针要手拼 DSH_HOME + 环境变量 + dsh --profile X "任务",跑完还要自己去 sessions/ 里翻会话 | tmpprofile_run:一次调用完成"装配 → 跑 → 返回 sessionId 与事件摘要" |
| 读回验证要自己解多帧 zstd(session.v4.jsonl.zstd),几乎每个会话都重写一遍 Python | tmpprofile_session:内置逐帧解码,直接给 preset / 工具面 / 上下文 / token |
| 清理靠自觉(历史上真的漏过两次,残留至今) | tmpprofile_clean:清单白名单 + junction 感知删除 + 残留自检 |
| junction 直接 Remove-Item -Recurse 有删进源仓库的风险 | 先 lstat 判链接、解链接,再递归删目录;绝不跟随链接 |
安装
从本地目录装(开发态)
dsh plugin --profile desktop add link:C:/repository/dsh-profile-lab
或从 GitHub 装
dsh plugin --profile desktop add github:<你的账号>/dsh-profile-lab
装完重启 DSH(插件在启动时装配)。桌面版的 profile 由 Electron 独占管理,命令行直连会被拒——用桌面版插件页装,或走应用内通道。
六个工具
| 工具 | 干什么 | 关键参数 |
| tmpprofile_create | 建隔离 profile(模板 / 克隆)+可选装包 | name、template(默认 headless)、clone、packages、purpose、overwrite |
| tmpprofile_install | 往已登记的临时 profile 追加安装 / 移除 | name、packages、remove |
| tmpprofile_run | 跑一次 headless 探针会话(同步、带超时) | name、task、sessionId、env、timeoutMs、json |
| tmpprofile_session | 读会话日志给结构化摘要 | sessionId 或 name、sections、limit |
| tmpprofile_list | 列出「本插件管的」与「未登记的」profile | — |
| tmpprofile_clean | 清理(默认 dry-run,需 confirm: true) | names、confirm、sessions、archiveTo |
| tmpprofile_fingerprint | 采集 profiles/ 指纹,自查有没有碰到别的 profile | exclude |
工具名以 tmpprofile_ 开头,便于在 dsh-tool-manager 里整组启用/禁用。
典型闭环(模型视角):
tmpprofile_create { name: "probeA", template: "headless", purpose: "验证 dsh-xxx 的工具面", packages: ["link:C:/plugins/dsh-xxx"] }
tmpprofile_run { name: "probeA", task: "只回复两个字:OK" }
tmpprofile_session { name: "probeA", sections: ["preset","tools","contexts"] }
tmpprofile_clean { names: ["probeA"], confirm: true }
更多配方见 docs/RECIPES.md。
安全护栏(都是刻意的)
1. 只动自己建的:只有登记在 $DSH_HOME/profile-lab.json 里的临时 profile 才可被写/删;desktop / backup / web / headless / dsh-tui / rescue / reserve / acp / sdk / sdk-minimal 一律硬拒。
2. 删链接不删目标:node_modules 里的 junction/symlink 先 lstat 判定再解链接,然后才递归删目录。
3. 克隆不共享写面:克隆时 node_modules 里的每一项单独链接(不是整目录一个链接),所以随后 pnpm add 只改写新 profile,源 profile 一个字节都不动。
4. clean 默认演练:不传 confirm: true 只返回「将要删除什么」。
5. 覆盖要显式:overwrite: true 只允许覆盖已登记的同名 profile,旧的挪到 $DSH_HOME/profile-lab-trash/,不直接删。
6. 失败留痕:profile 先登记再装包,装包失败也留在清单里,能被 tmpprofile_clean 回收。
7. 进程内 + 跨进程锁:同一 profile 不允许并发运行;跨进程写操作走 $DSH_HOME/profile-lab.lock(陈旧锁 120s 自动抢占)。
8. 审计日志:每次 create / install / run / clean 追加一行到 $DSH_HOME/profile-lab-log.jsonl。
9. 不做后台任务:DSH 的子进程在宿主的 Job Object 里(宿主退出会连带杀死),所以 tmpprofile_run 一律同步 + 超时(默认 300s,上限 900s,超时 taskkill /T /F 杀整棵进程树)。
配置(cordis 行 config,全部可选)
- insert:
- id: dsh-profile-lab
name: 'dsh-profile-lab'
config:
home: 'C:/Users/you/.dsh' # 缺省 $DSH_HOME 或 ~/.dsh dshBin: '' # 显式指定 dsh 的 lib/bin.js(缺省自动探测) nodePath: '' # 显式指定跑子进程的 node(缺省 process.execPath) tools:
disabled: ['tmpprofile_fingerprint'] # 按名字关掉工具
dsh 可执行文件是怎么找的
按优先级探测,每条都用 -V 实测(app.asar 是文件,existsSync 看不到内部路径,只能试跑):
1. config.dshBin / 环境变量 DSH_LAB_DSH_BIN
2. 宿主安装锚点:宿主进程的 profileContext.installAnchor → <安装>/…/@deepseek-ai/dsh/lib/bin.js,用 process.execPath + ELECTRON_RUN_AS_NODE=1(版本与宿主一致,兼容性最好)
3. npm 全局安装:%APPDATA%\npm\node_modules\@deepseek-ai\dsh\lib\bin.js
4. C:\dsh-runtime\<版本>\...
实测样例(Windows + 桌面版宿主):宿主锚点 → 0.2.0-rc.2(host 同版本);兜底 npm 全局 → 0.2.1-alpha.1。
会话日志是怎么读的
session.v4.jsonl.zstd 是多帧 zstd(DSH 每追加一批事件压一帧)。Node 的 zstdDecompressSync 对多帧输入只解第一帧且不报错,也不暴露「剩余未消费字节」——整段丢进去只拿到 254 字节的 header。
官方 @deepseek-ai/dsh-session-persistence-jsonl 的做法是:按 zstd 魔数(0xFD2FB528,小端 28 B5 2F FD)扫描帧起点,再逐帧解压。本插件用同一思路并利用「one-shot 只解第一帧」的特性把实现压到最短:对每个魔数偏移试解一次,成功即得该帧,伪魔数(解出来不是 { 开头)直接跳过。
实测:6.51 MiB 压缩 → 4322 帧 → 20 MiB 文本 → 6901 事件,摘要正确。
两个 DSH 侧的坑(本插件都已堵掉)
1. 工具返回值必须是 lossless JSON。只要返回值里出现 undefined(哪怕只是某个可选字段),DSH 会把整条结果拒收,模型看到的是 tool "x" returned invalid output: value is not lossless JSON 而不是数据。本插件在所有工具出口统一做了一次净化(丢 undefined、非有限数转 null)。
2. headless 的 --json 是投影,不是日志。它用自己的一套事件名(tool_call、tool_result、status、thinking…),不含日志里的 tool/call;官方文档也明说它「caps every string except the terminal final at 8 KiB and omits events the projection does not model」。
所以「到底调了哪些工具」必须以磁盘上的完整会话日志为准——tmpprofile_run 已经替你把这一步做了:返回值里的 session.tools 就是完整日志的工具面。
自检
装好之后(或改动代码之后)可以直接跑内置自检——它会真建一个临时 profile、装本插件、跑一次 headless 让模型调用 tmpprofile_list、再清理:
node scripts/selfcheck.mjs
退出码 0 表示插件在真实 DSH 里能加载、工具可被调用、清理无残留。
测试
单元测试(全部在临时 DSH_HOME 里跑,不碰真实环境)
npm test
真实闭环(会真建 profile、真跑一次 headless、真清理;消耗极少量 token) $env:DSH_LAB_LIVE='1'; node --test tests/live.test.js
live 测试的硬断言:除本次建的临时 profile 外,profiles/ 里其他 profile 的指纹(hash + mtime)必须一个字节不变。
限制(明确不做)
- 不是危机救援工具:主力 profile 起不来时,本插件也起不来。要救援请用 rescue / reserve 实例。
- 不做后台/脱离宿主的任务:见上文 Job Object。
- 不自动纳管已有的临时 profile:profiles/ 里历史残留(例如早年手工建的目录)会出现在 unmanaged 列表里,需要你确认后用 adoptProfile 显式纳管,本插件不会自动动它们。
- 共享 DSH_HOME:探针会话仍写在主力的 sessions/ / storages/ 下(同一 home 无跨进程锁)。要彻底隔离得用独立 DSH_HOME,尚未实现。
文件位置
| 内容 | 路径 |
| 清单 | $DSH_HOME/profile-lab.json |
| 审计日志 | $DSH_HOME/profile-lab-log.jsonl |
| 跨进程锁 | $DSH_HOME/profile-lab.lock |
| 覆盖时丢弃的旧 profile | $DSH_HOME/profile-lab-trash/ |
| 临时 profile | $DSH_HOME/profiles/<name>/ |
许可
MIT预览分享卡片长什么样 ↓
分享 · DeepSeek Harness Hub
wqzhellohhwy/dsh-profile-lab
隔离环境试装插件,验证无误再进日常配置
相关插件 · 开发 / 工程
隐私简历生成器reactive-resume/reactive-resume
Reactive Resume 已迁至 GitHub 上的…
★ 45,125已汉化MCP
71.7
AI 架构文档生成器sopaco/deepwiki-rs
🚀 Litho 已进化为 Terrain —— 给你的 AI 智能体一张会呼吸的代码库地图。
★ 3,157已汉化DSH
70.8
长时任务执行闭环AMAP-ML/LongHorizon-Harness
面向 Computer-Use Agent 的 Loop Engineering
★ 1,764已汉化DSH
70.5
AI 编程纪律方法包GanyuanRan/Aegis
让 AI 编程 agent 变得可信:少返工、更安全、说“完成”前先给证据。
★ 1,384已汉化DSH
69.6
多代理编排运行时Enderfga/claw-orchestrator
面向编程智能体的运行时。把 Claude Code、Codex、Antigravity、Grok…
★ 586已汉化MCP
69.4
记忆进化插件csyangwen/dsh-memory-evolve
dsh-memory-evolve 使用场景指南
★ 461已汉化DSH
67.9