上下文工具箱dsh-context-toolkit
29.9/100
综合分
1
GitHub 星标
—
npm 周下载
—/5
用户评分
数据截至 2026/10/27 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add chimneyQ/dsh-context-toolkit该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)
状态速览
判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态信任档位:待复验
是什么
dsh 原生插件 · dev
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 13 天前
档位由哪些信号合成
本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。
社区实装还没有人打卡 · 装过的第一个就是你
安全扫描自动 · 静态分析源码v2.7.0🔵 扫描:含敏感能力尚无人工评估展开查看扫描说明与证据 ↓
安全扫描自动 · 静态分析源码
v2.7.0静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。
可执行外部命令读写/删除本地文件发起外部网络请求
查看扫描证据(共 92 处,展示前 59 条)
- 读取本地文件index.js:49
const parsed = JSON.parse(readFileSync(join(here, 'package.json'), 'utf8')) - 读取本地文件index.js:79
for (const profile of readdirSync(join(dshHome, 'profiles'))) { - 读取本地文件index.js:461
const st = statSync(path) - 读取本地文件index.js:462
if (st.isFile()) parts.push(readFileSync(path, 'utf8').slice(0, SCRIPT_READ_MAX)) - 读取本地文件index.js:707
const st = statSync(p) - 读取本地文件index.js:897
if (!existsSync(abs)) return { text: 'no such file: ' + abs, isError: true } - 写入本地文件index.js:1175
mkdirSync(dirname(path), { recursive: true }) - 写入本地文件index.js:1176
writeFileSync(path, pem, 'utf8') - 发起网络请求index.js:1463
const response = await fetch(url, { redirect: 'follow', headers: { 'user-agent': 'dsh-context-toolkit' } }) - 删除本地文件递归index.js:1863
rmSync(probe, { recursive: true, force: true }) - 发起网络请求client.js:104
.then(() => fetch(STATUS_PATH, { headers: { accept: "application/json" } })) - 发起网络请求client.js:205
.then(() => fetch(STATUS_PATH, { headers: { accept: "application/json" } })) - 发起网络请求client.js:221
const response = await fetch(EXPORT_CA_PATH, { - 读取本地文件lib/runner.js:27
if (existsSync(candidate)) return candidate - 执行外部命令来自配置/变量lib/runner.js:97
child = spawn(command, args, { - 写入本地文件lib/runner.js:242
mkdirSync(candidate, { recursive: true }) - 删除本地文件递归lib/runner.js:244
rmSync(probe, { recursive: true, force: true }) - 写入本地文件lib/runner.js:258
writeFileSync(file, body, 'utf8') - 删除本地文件递归lib/runner.js:261
.finally(() => { try { rmSync(dir, { recursive: true, force: true }) } catch { } }) - 写入本地文件lib/store.js:85
mkdirSync(storageDir, { recursive: true }) - 读取本地文件lib/store.js:236
if (!existsSync(abs)) throw new Error('no such file: ' + abs) - 读取本地文件lib/store.js:237
const st = statSync(abs) - 读取本地文件lib/store.js:239
const text = readFileSync(abs, 'utf8') - 读取本地文件release-check.mjs:19
const read = (f) => readFileSync(pub + '/' + f, 'utf8') - 读取本地文件release-check.mjs:33
ck('1 dsh.bundle patch declared and on disk', manifest.dsh?.bundle?.patch === './cordis.patch.yml' && existsSync(pub + '/cordis.patch.yml')) - 读取本地文件release-check.mjs:35
ck('3 every files[] entry exists', (manifest.files ?? []).every((f) => f.includes('*') || existsSync(pub + '/' + f)), JSON.stringify(manifest.files)) - 读取本地文件release-check.mjs:38
const expandsTo = (entry) => { if (!entry.includes('*')) return existsSync(join(pub, entry)); const [head, tail] = entry.split('*'); const abs = join( - 读取本地文件release-check.mjs:38
const expandsTo = (entry) => { if (!entry.includes('*')) return existsSync(join(pub, entry)); const [head, tail] = entry.split('*'); const abs = join( - 读取本地文件release-check.mjs:42
ck('7 the entry point resolves', existsSync(pub + '/index.js') && existsSync(pub + '/client.js')) - 执行外部命令来自配置/变量release-check.mjs:68
const smokeRun = execFileSync(process.execPath, ['smoke.mjs'], { cwd: pub, encoding: 'utf8' }) - 写入本地文件smoke.mjs:40
writeFileSync(join(workDir, 'flood-script.sh'), '#!/usr/bin/env bash\ncurl -s https://example.com\n') - 写入本地文件smoke.mjs:41
writeFileSync(join(workDir, 'clean-script.sh'), '#!/usr/bin/env bash\necho hello\n') - 写入本地文件smoke.mjs:45
mkdirSync(join(workDir, 'nested')) - 写入本地文件smoke.mjs:46
writeFileSync(join(workDir, 'nested', 'flood-rel.sh'), '#!/usr/bin/env bash\ncurl -s https://example.com\n') - 读取本地文件smoke.mjs:96
assert.ok(existsSync(dir), 'scratch dir does not exist: ' + dir) - 写入本地文件smoke.mjs:146
writeFileSync(big, Array.from({ length: 4000 }, (_, i) => 'row ' + i + ' v=' + i).join('\n'), 'utf8') - 删除本地文件递归smoke.mjs:475
} finally { rmSync(dir, { recursive: true, force: true, maxRetries: 5 }) } - 读取本地文件smoke.mjs:486
const pem = readFileSync(wrote.path, 'utf8') - 删除本地文件递归smoke.mjs:493
} finally { rmSync(dir, { recursive: true, force: true, maxRetries: 5 }) } - 删除本地文件递归smoke.mjs:505
} finally { rmSync(dir, { recursive: true, force: true, maxRetries: 5 }) } - 读取本地文件smoke.mjs:510
const client = readFileSync(join(DIR, 'client.js'), 'utf8') - 写入本地文件smoke.mjs:524
mkdirSync(target, { recursive: true }) - 删除本地文件递归smoke.mjs:533
} finally { rmSync(dir, { recursive: true, force: true, maxRetries: 5 }) } - 读取本地文件smoke.mjs:537
const source = readFileSync(join(DIR, 'lib', 'store.js'), 'utf8') - 读取本地文件smoke.mjs:555
const source = readFileSync(join(DIR, 'index.js'), 'utf8') - 读取本地文件smoke.mjs:565
const source = readFileSync(join(DIR, 'index.js'), 'utf8') - 执行外部命令固定命令smoke.mjs:587
execFileSync('openssl', ['req', '-x509', '-newkey', 'rsa:2048', '-nodes', - 删除本地文件递归smoke.mjs:594
rmSync(dir, { recursive: true, force: true, maxRetries: 5 }) - 删除本地文件递归smoke.mjs:615
rmSync(dir, { recursive: true, force: true, maxRetries: 5 }) - 执行外部命令固定命令smoke.mjs:628
execFileSync('openssl', ['req', '-x509', '-newkey', 'rsa:2048', '-nodes', - 读取本地文件test-client.mjs:19
const source = readFileSync(join(DIR, 'client.js'), 'utf8') - 读取本地文件test-client.mjs:56
const zh = JSON.parse(readFileSync(join(DIR, 'locale', 'zh.json'), 'utf8')).strings - 读取本地文件test-client.mjs:57
const en = JSON.parse(readFileSync(join(DIR, 'locale', 'en.json'), 'utf8')).strings - 读取本地文件test-package.mjs:13
const pkg = JSON.parse(readFileSync(join(dir, 'package.json'), 'utf8')) - 读取本地文件test-package.mjs:22
check('1d the entry point exists on disk', existsSync(join(dir, exportsMap['.'] || './index.js')), String(exportsMap['.'])) - 读取本地文件test-package.mjs:23
check('1e the bundle patch is declared and exists', Boolean(pkg.dsh && pkg.dsh.bundle && pkg.dsh.bundle.patch) && existsSync(join(dir, pkg.dsh.bundle. - 读取本地文件test-package.mjs:31
check('2c the client target exists on disk', Boolean(clientTarget && existsSync(join(dir, clientTarget))), String(clientTarget)) - 读取本地文件test-package.mjs:46
if (!entry.includes('*')) return existsSync(join(dir, entry)) ? [entry] : [] - 读取本地文件test-package.mjs:49
if (!existsSync(abs)) return []
扫描于 2026/10/31(0 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库
静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个
DeepSeek Harness 套件:原生 ctk_* 沙箱工具、本地 FTS5 知识库,还有一道路由闸门,把大批量输出挡在对话之外。无第三方运行时依赖。
README
由 DeepSeek 最新模型翻译生成上下文工具箱(dsh-context-toolkit)
一个给 DeepSeek Harness 用的插件:让大块输出不进对话。
它的做法是把"原始字节"和"结论"分开——命令在子进程里跑、文件在沙箱里分析、网页与长材料归档进本地知识库,对话里只留下结论、片段和计数。另有一道路由门禁,在工具调用前挡掉那些注定会灌满对话的操作。
零第三方运行时依赖:知识库用 Node 自带的 node:sqlite(含 FTS5),跑码用系统已有的解释器,安装后不需要再拉任何包。
它有什么用
| 问题 | 这个插件的做法 |
| 跑个命令,输出几千行,只想知道"有几个" | ctk_execute 在子进程里跑,只把结论带回来 |
| 读大文件做统计,不想把全文塞进上下文 | ctk_execute_file 让文件留在沙箱里 |
| 一次要问很多条命令 | ctk_batch_execute 一条调用跑完,逐条报状态 |
| 网页/长文档看完就废,下次还要重看 | ctk_index 归档,ctk_search 按需取片段 |
| curl / wget 直接把整页灌进对话 | 路由门禁硬拦,并给出替代方案 |
| 不知道到底省了多少 | ctk_stats 报台账;设置页有实时占用面板 |
工具
| 工具 | 用途 |
展开完整文档(README 剩余 1 节)
上下文工具箱(dsh-context-toolkit)
一个给 DeepSeek Harness 用的插件:让大块输出不进对话。
它的做法是把"原始字节"和"结论"分开——命令在子进程里跑、文件在沙箱里分析、网页与长材料归档进本地知识库,对话里只留下结论、片段和计数。另有一道路由门禁,在工具调用前挡掉那些注定会灌满对话的操作。
零第三方运行时依赖:知识库用 Node 自带的 node:sqlite(含 FTS5),跑码用系统已有的解释器,安装后不需要再拉任何包。
它有什么用
| 问题 | 这个插件的做法 |
| 跑个命令,输出几千行,只想知道"有几个" | ctk_execute 在子进程里跑,只把结论带回来 |
| 读大文件做统计,不想把全文塞进上下文 | ctk_execute_file 让文件留在沙箱里 |
| 一次要问很多条命令 | ctk_batch_execute 一条调用跑完,逐条报状态 |
| 网页/长文档看完就废,下次还要重看 | ctk_index 归档,ctk_search 按需取片段 |
| curl / wget 直接把整页灌进对话 | 路由门禁硬拦,并给出替代方案 |
| 不知道到底省了多少 | ctk_stats 报台账;设置页有实时占用面板 |
工具
| 工具 | 用途 |
| ctk_execute | 在沙箱子进程里跑代码(javascript / esm / typescript / shell),只回结论 |
| ctk_execute_file | 对某个文件跑代码做统计或抽取,文件原文不出沙箱 |
| ctk_batch_execute | 一次跑多条命令,逐条状态,输出只有结论 |
| ctk_index | 把文本或文件存进本地 FTS5 知识库 |
| ctk_search | 多查询检索知识库,返回围绕命中词的片段 |
| ctk_retrieve | 取回被瘦身压缩掉的原文(逐字节,按 [ctk_retrieve id=…] 标记的 id) |
| ctk_retrieve | 取回被瘦身压缩掉的原文(逐字节,按 [ctk_retrieve id=…] 标记的 id) |
| ctk_fetch_and_index | 抓网页 → 去标签转文本 → 直接入库 |
| ctk_purge | 删除已归档内容(必须 confirm: true) |
| ctk_stats | 节约台账:拦截次数、沙箱保留字节、归档字节、调用计数 |
| ctk_doctor | 自检:解释器能否真启动、临时目录可写、知识库能否往返 |
| ctk_context_report | 当前会话的上下文占用与构成 |
安装
从 npm 安装(推荐):
dsh plugin --profile desktop add dsh-context-toolkit
从 GitHub 安装:
dsh plugin --profile desktop add github:chimneyQ/dsh-context-toolkit
本地 checkout 时可直接指定目录:
dsh plugin --profile desktop add D:\path\to\dsh-context-toolkit
装完确认 <DSH_HOME>/profiles/desktop/package.json 的 dsh.profile.bundles 里出现 dsh-context-toolkit。
卸载:
dsh plugin --profile desktop remove dsh-context-toolkit
怎么用更划算
默认就该走 ctk_。尤其是要分析、计数、过滤、看摘要的场合——直接把输出读进对话是这套插件要解决的原始问题。
// 想知道一个大目录里有多少 .log 文件、总共多大 await ctk_execute({ language: 'javascript', code: const fs = require('fs'); const dir = process.argv[2]; let n = 0, bytes = 0;
for (const f of fs.readdirSync(dir)) {
if (!f.endsWith('.log')) continue;
n++; bytes += fs.statSync(dir + '/' + f).size;
}
console.log(n + ' files, ' + bytes + ' bytes');
});
长材料先归档,之后按需取:
await ctk_index({ path: 'D:/specs/api.md' }); // 全文留在库里 await ctk_search({ queries: ['rate limit', 'auth'] }); // 只取相关片段
代理拦截网络下的 TLS
如果每次 ctk_fetch_and_index 都失败于 UNABLE_TO_VERIFY_LEAF_SIGNATURE,说明本机处在 TLS 中间人检查的环境里,Node 内置根证书无法验证代理出示的证书链。caPemPath 用来指定一份要信任的 PEM。
auto(默认)指向 <DSH_HOME>/context-mode/ca/windows-roots.pem。本包不附带这个文件 —— 它是 本机自己根证书库的一份快照,所以在全新机器上它根本不存在,抓取会退回 Node 默认根证书。
设置 → 上下文工具箱,caPemPath 下方有一行:
- 一个徽标,显示该文件是否存在(以及大小)—— 打开页面时只做一次 stat,不会为了渲染页面去读整份证书包;
- 一个按钮,按下时才从系统根证书库(tls.getCACertificates('system'),约 500 张证书、约 820 KB)写出它。
已存在的证书包不会被一次点击覆盖:首次点击只是询问,只有明确确认才会覆盖。
这个按钮不做什么: 它导出的是系统证书库。若代理的根证书从未被装进该系统库(例如 IT 通过组策略推送),导出的包仍然无法验证该代理。请先安装根证书,再导出。
配置
| 键 | 默认 | 含义 |
| routing | true | 路由门禁总开关 |
| denyCurlWget | true | 硬拦 curl / wget |
| bashNudgeMinCommandBytes | 4096 | 超长命令的引导阈值(0 = 关闭) |
| maxReadBytesBeforeAsk | 51200 | 大文件整读的引导阈值(0 = 关闭) |
| nudgeFetchTools | true | 把宿主取网页工具(web_fetch / read_page)也纳入整页引导 |
| trustedReadBasenames | package.json 等 | 不受整读门禁限制的文件名 |
| compressNudgeTokens | 120000 | 占用提示阈值(tokens) |
| searchLimit | 5 | ctk_search 每次查询的片段数上限 |
| snippetChars | 900 | 每个片段的字符上限 |
| fetchMaxBytes | 2000000 | 单页抓取上限 |
| caPemPath | auto | ctk_fetch_and_index 的 TLS 信任链:auto = <DSH_HOME>/context-mode/ca/windows-roots.pem;也可填 PEM 绝对路径;留空 = Node 默认根证书。企业代理拦截 HTTPS 导致每次抓取都报证书错误时配它。 |
| storageDir | 空 | 知识库根目录,空 = <DSH_HOME>/context-toolkit |
| tools. | 见下 | 逐个工具的开关 |
工具开关:tools.execute、tools.executeFile、tools.batchExecute、tools.index、tools.search、tools.fetchAndIndex、tools.purge、tools.stats、tools.contextReport、tools.doctor,默认全部开启。
校验
离线套件不依赖 DSH 运行时:
node smoke.mjs # 104 项:契约、运行时探测、工具、门禁(分段匹配 + 路径基准对齐 + script_path 与取网页工具覆盖)、输出瘦身与可逆取回及保留期、证书包存在性与导出、资源释放、知识库、观测、版本自述
node test-client.mjs # 27 项:浏览器半体契约与双语字典一致性
node test-package.mjs # 20 项:发布清单与零依赖约束
node release-check.mjs # 34 项:发布就绪(清单/依赖/工具数/门禁匹配/版本/SHA 覆盖/无硬编码路径)
输出瘦身(工具执行后)
门禁在入口拦下次就会灌满对话的调用;瘦身则处理已经进来的输出——两者抓的是不同流量:门禁只能看调用的参数,瘦身看的是真实回来的字节(来自门禁不认识的工具、它没覆盖的入口,或本来就无法从调用预判的输出)。
在 tools/post-execute 处按形态选择策略:
| 形态 | 做法 |
| 同构 JSON 数组 | 无损表化重编码:键只写一次,信息零损失 |
| 表格 | 保留表头 + 首尾,中段按原始行号步进采样(模型拿到的是带坐标的地图) |
| grep/search | 按文件折叠,每文件保留前若干命中 |
| 日志 | 折叠普通行,诊断行逐字保留 |
| 长散文 | 头尾窗口 + 显式省略标记 |
| 代码 / 键值对 | 不压(模型可能正要按精确字节编辑) |
双重门:候选必须同时满足「字节 ≤ 72%」且「估算 token 严格下降」,否则放弃——压不动就不压,台账绝不虚报节省。
可逆红线:任何有损路径先把原文逐字节存好并注入 [ctk_retrieve id=…] 标记,用 ctk_retrieve 取回。原文与 FTS5 索引同在同一个数据库(一个文件两个职责:FTS5 找段落,blob 逐字节还原)。
- 原文保留期:slimOriginalTtlMinutes(默认 1440 = 一天,0 = 永久)由插件在每次加载时执行一次清理;标记投递失败时按 id 精确回滚,不会牵连其他原文。
已知边界
- 出站抓取受本机网络影响:若本机把域名解析到回环地址,ctk_fetch_and_index 会失败并明确说明原因(而不是只报 "fetch failed");此时改用 ctk_index 归档本地材料。
- ctk_execute 的超时是硬杀:超时会终止子进程并把 timedOut 标出来。
- 单次捕获上限 256 KB,超出部分只计数不保留;回给对话的文本另有 4000 字符硬帽,因此 1 MB 的单行输出也不会整段进入上下文。
- 知识库按路径去重:同一路径再次归档是替换,不是追加。
- 已被上游截断的输出不再压缩:上游工具自行截断并落盘的产物([output truncated; full output: …])与沙箱捕获上限的收尾(独立的 ~~~ 行)都视为已完成的工作,本层跳过并在台账「跳过原因」中计数。
- 门禁精确而非穷尽:只有当洪水工具是某个分段的「命令词」时才拒绝(会先剥掉 sudo/exec 等前缀、归一化路径与 .exe 等扩展名),script_path 最多读取前 256 KB。程序内部发起的调用(如 python -c 'import wget')不在其职责内。
- 路径基准与宿主一致:门禁读取 script_path 与 read 的 file_path 时,按会话工作目录解析相对路径(与宿主实际打开文件的基准相同);否则「宿主能跑、门禁看不见」就是一条现成的绕过路径。
许可
MIT预览分享卡片长什么样 ↓
分享 · DeepSeek Harness Hub
chimneyQ/dsh-context-toolkit
上下文工具箱dsh-context-toolkit
相关插件 · 开发 / 工程
隐私简历生成器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