🎁 福利专区全网大模型免费应用 + 新用户福利 + 注册活动入口,低成本玩转 AI

临时配置实验台 wqzhellohhwy/dsh-profile-lab

Cordis
wqzhellohhwy20 天前有提交已提供中文文档扫描:含敏感能力

隔离环境试装插件,验证无误再进日常配置

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
装得上吗
曾在 dsh 0.2.0-rc.2 下真实安装成功,但当前 dsh 已更新至 0.2.1-rc.5,本站尚未在新版下复验
L4 · 真实安装等级说明 →
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 20 天前
档位由哪些信号合成

本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。

验证判据与等级说明 →
社区实装还没有人打卡 · 装过的第一个就是你
安全扫描自动 · 静态分析源码
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
预览分享卡片长什么样 ↓wqzhellohhwy/dsh-profile-lab
分享 · DeepSeek Harness Hub
wqzhellohhwy/dsh-profile-lab

隔离环境试装插件,验证无误再进日常配置

回到顶部