会话连接卡片宿主 Noob-stupid/dsh-connection-card-host
DeepSeek Harnessv1.0.82把「连接」做成 DSH 里的一等对象:会话是节点,连接是容器,卡片是连接级插件 —— 一个「连接级插件宿主」。
30.7/100
综合分
1
GitHub 星标
254
npm 周下载
—/5
用户评分
数据截至 2026/10/18 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add @noob-stupid/dsh-connection-card-hostnpm 源 · @noob-stupid/dsh-connection-card-host 的发布者账号已校验与本仓库一致
状态速览
判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态信任档位:已验证
是什么
dsh 原生插件 · other
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 16 天前
档位由哪些信号合成
本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。
社区实装还没有人打卡 · 装过的第一个就是你
安装兼容性检查✓ 自动检查通过展开查看检查明细 ↓
以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。
✓npm 包@noob-stupid/dsh-connection-card-host @ 1.0.82
✓Node 引擎要求 >=20 <25 · 基线 Node 22.19 满足
✓dsh CLI 依赖要求 >=0.2.0-rc.1 <0.3.0 · 最新 0.2.1-rc.2 兼容
✓入口文件main/exports/bin 已声明
验证方式:npm registry 存在性 + package.json 静态校验 · 最后检查 2026/10/20 02:48:27
安全扫描自动 · 静态分析源码v2.7.0🔵 扫描:含敏感能力尚无人工评估展开查看扫描说明与证据 ↓
安全扫描自动 · 静态分析源码
v2.7.0静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。
可执行外部命令读写/删除本地文件发起外部网络请求
查看扫描证据(共 126 处,展示前 86 条)
- 写入本地文件lib/index.js:37
appendFileSync(DEBUG_LOG, `[${new Date().toISOString()}] ${msg}\n`); - 写入本地文件lib/index.js:82
appendFileSync(auditFile, `[${new Date().toISOString()}] ${msg}\n`); - 写入本地文件lib/index.js:112
appendFileSync(clientLogFile, `[${new Date().toISOString()}] ${msg}\n`); - 读取本地文件lib/core/persistence.js:90
if (existsSync(this.dataPath)) { - 读取本地文件lib/core/persistence.js:91
const raw = readFileSync(this.dataPath, 'utf8'); - 读取本地文件lib/core/persistence.js:98
if (existsSync(this.backupPath)) { - 读取本地文件lib/core/persistence.js:99
const raw = readFileSync(this.backupPath, 'utf8'); - 写入本地文件lib/core/persistence.js:111
mkdirSync(getBaseDir(), { recursive: true }); - 读取本地文件lib/core/persistence.js:113
if (existsSync(this.dataPath)) { - 写入本地文件lib/core/persistence.js:115
renameSync(this.dataPath, this.backupPath); - 写入本地文件lib/core/persistence.js:119
writeFileSync(this.dataPath, JSON.stringify(this.data, null, 2), 'utf8'); - 读取本地文件lib/tools/connection-skill.js:82
content: await readFile(SKILL_BODY_URL, 'utf8'), - 读取本地文件lib/card-host/loader.js:171
if (!existsSync(root)) - 读取本地文件lib/card-host/loader.js:175
entries = readdirSync(root); - 读取本地文件lib/card-host/loader.js:194
const target = readFileSync(join(root, name), 'utf8').trim(); - 读取本地文件lib/card-host/loader.js:229
if (!existsSync(pkgPath)) - 读取本地文件lib/card-host/loader.js:231
const pkg = JSON.parse(readFileSync(pkgPath, 'utf8')); - 读取本地文件lib/card-host/loader.js:454
if (!existsSync(recordPath)) - 读取本地文件lib/adapter/flag-file.js:41
if (!existsSync(path)) { - 读取本地文件lib/adapter/flag-file.js:47
text = readFileSync(path, 'utf8'); - 读取本地文件lib/card-host/installer.js:43
if (existsSync(resolve(s))) { - 读取本地文件lib/card-host/installer.js:77
entries = readdirSync(target, { withFileTypes: true }); - 删除本地文件lib/card-host/installer.js:87
rmdirSync(p); - 删除本地文件lib/card-host/installer.js:95
unlinkSync(p); - 删除本地文件lib/card-host/installer.js:103
rmdirSync(target); - 删除本地文件lib/card-host/installer.js:129
unlinkSync(target); - 读取本地文件lib/card-host/installer.js:133
if (!existsSync(target)) - 写入本地文件lib/card-host/installer.js:138
renameSync(target, `${target}.trash-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`); - 读取本地文件lib/card-host/installer.js:142
return !existsSync(target); - 执行外部命令固定命令lib/card-host/installer.js:156
execFile('tar', ['-tvf', tgzPath], { timeout: 60_000, killSignal: 'SIGKILL', maxBuffer: MAX_CHILD_BUFFER, encoding: 'utf8' }, (err, stdout) => { - 执行外部命令固定命令lib/card-host/installer.js:178
execFile('tar', ['-tzf', tgzPath], { timeout: 60_000, killSignal: 'SIGKILL', maxBuffer: MAX_CHILD_BUFFER, encoding: 'utf8' }, (err, stdout) => { - 读取本地文件lib/card-host/installer.js:274
const rs = createReadStream(path); - 读取本地文件lib/card-host/installer.js:391
items = readdirSync(dir, { withFileTypes: true }); - 写入本地文件lib/card-host/installer.js:450
mkdirSync(destDir, { recursive: true }); - 执行外部命令固定命令lib/card-host/installer.js:478
execFile('tar', ['-xzf', tgzPath, '-C', destDir], { - 写入本地文件lib/card-host/installer.js:668
writeFileSync(memoPathFor(cardsRoot), JSON.stringify({ channelId, at: Date.now() }, null, 2), 'utf8'); - 执行外部命令来自配置/变量lib/card-host/installer.js:789
execFile(CURL_BIN, args, { - 执行外部命令来自配置/变量lib/card-host/installer.js:847
execFileSync(CURL_BIN, ['--version'], { stdio: 'pipe', timeout: 10_000 }); - 写入本地文件lib/card-host/installer.js:1178
renameSync(partPath, destPath); - 发起网络请求lib/card-host/installer.js:1226
const resp = await fetch(ch.url); - 写入本地文件lib/card-host/installer.js:1237
writeFileSync(partPath, buf); - 写入本地文件lib/card-host/installer.js:1238
renameSync(partPath, destPath); - 发起网络请求lib/card-host/installer.js:1374
const metaResp = await fetch(metaUrl, { - 发起网络请求lib/card-host/installer.js:1386
const resp = await fetch(tarball); - 读取本地文件lib/card-host/updates.js:27
if (!existsSync(p)) - 读取本地文件lib/card-host/updates.js:30
const raw = JSON.parse(readFileSync(p, 'utf8')); - 读取本地文件lib/card-host/updates.js:50
const t = readFileSync(p, 'utf8').trim(); - 执行外部命令固定命令lib/card-host/updates.js:62
const out = execFileSync('npm', ['view', name, 'version'], { - 读取本地文件lib/card-host/updates.js:115
if (!existsSync(rec.spec)) { - 读取本地文件lib/card-host/updates.js:126
if (!existsSync(rec.spec)) { - 读取本地文件lib/card-host/updates.js:130
const st = statSync(rec.spec); - 读取本地文件lib/card-host/card-paths.js:54
names = readdirSync(cur); - 读取本地文件lib/card-host/card-paths.js:65
st = statSync(full); - 读取本地文件lib/card-host/client-artifact.js:67
if (!existsSync(pkgPath)) - 读取本地文件lib/card-host/client-artifact.js:71
pkg = JSON.parse(readFileSync(pkgPath, 'utf8')); - 读取本地文件lib/card-host/client-artifact.js:89
if (!existsSync(abs)) - 读取本地文件lib/card-host/client-artifact.js:92
const st = statSync(abs); - 读取本地文件lib/card-host/client-artifact.js:108
const source = readFileSync(abs, 'utf8'); - 读取本地文件lib/card-host/suitability.js:223
return existsSync(join(dir, 'src')) && !existsSync(join(dir, 'lib')); - 读取本地文件lib/card-host/suitability.js:232
return existsSync(file) ? readFileSync(file, 'utf8') : undefined; - 读取本地文件lib/card-host/suitability.js:232
return existsSync(file) ? readFileSync(file, 'utf8') : undefined; - 读取本地文件lib/adapter/mount.js:39
if (!existsSync(pkgPath)) - 读取本地文件lib/adapter/mount.js:43
pkg = JSON.parse(readFileSync(pkgPath, 'utf8')); - 读取本地文件lib/adapter/mount.js:58
if (existsSync(abs) && statSync(abs).isFile()) - 读取本地文件lib/adapter/mount.js:58
if (existsSync(abs) && statSync(abs).isFile()) - 读取本地文件lib/adapter/mount.js:94
if (!existsSync(pluginDir)) { - 读取本地文件lib/card-host/package-check.js:59
if (!existsSync(abs)) - 读取本地文件lib/card-host/package-check.js:63
const text = readFileSync(abs, 'utf8'); - 读取本地文件lib/card-host/package-check.js:90
if (existsSync(join(dir, rel))) - 读取本地文件lib/card-host/package-check.js:102
if (!existsSync(pkgPath)) - 读取本地文件lib/card-host/package-check.js:106
pkg = JSON.parse(readFileSync(pkgPath, 'utf8')); - 读取本地文件lib/card-host/package-check.js:145
const hasSrcDir = existsSync(join(dir, 'src')); - 写入本地文件lib/card-host/package-check.js:213
writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n', 'utf8'); - 读取本地文件lib/adapter/shim.js:108
entries = readdirSync(dir); - 读取本地文件lib/adapter/shim.js:117
st = statSync(full); - 读取本地文件lib/adapter/shim.js:158
text = readFileSync(full, 'utf8'); - 读取本地文件lib/adapter/shim.js:219
if (existsSync(resolved)) - 读取本地文件lib/adapter/shim.js:278
if (existsSync(cand) && statSync(cand).isFile()) - 读取本地文件lib/adapter/shim.js:278
if (existsSync(cand) && statSync(cand).isFile()) - 写入本地文件lib/adapter/shim.js:448
mkdirSync(nm, { recursive: true }); - 写入本地文件lib/adapter/shim.js:449
writeFileSync(marker, JSON.stringify({ - 写入本地文件lib/adapter/shim.js:461
mkdirSync(dir, { recursive: true }); - 写入本地文件lib/adapter/shim.js:507
writeFileSync(path, content, 'utf8'); - 写入本地文件lib/adapter/shim.js:521
mkdirSync(dirname(dir), { recursive: true }); - 删除本地文件递归lib/adapter/shim.js:549
rmSync(nm, { recursive: true, force: true }); - 读取本地文件cards/connection-etiquette/test.mjs:36
const readNormalized = (p) => readFileSync(p, 'utf8').replace(/ /g, '\n')
扫描于 2026/11/2(1 天前) · 范围 入口闭包 + 全包扫描 · 来源 npm 发布包
静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。
依赖的 DSH / Cordis 模块
@deepseek-ai/cordis@deepseek-ai/dsh@deepseek-ai/dsh-client-ui-slots@deepseek-ai/dsh-client-runtime给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个
功能亮点
- 会话间有状态连接
- 连接级卡片插件
DSH 会话连接卡宿主——会话间有状态连接,支持连接级卡片插件
最近更新
在 GitHub 查看全部 →v1.0.822026-10-17
npm: —— npm 上的字节与本 Release 资产的字节来自同一次 npm pack。
v1.0.762026-10-12
npm: —— npm 上的字节与本 Release 资产的字节来自同一次 npm pack。
v1.0.752026-10-12
npm: —— npm 上的字节与本 Release 资产的字节来自同一次 npm pack。
数据来自仓库的 GitHub Release,可能与插件实际版本不一致。
README
由 DeepSeek 最新模型翻译生成dsh-connection-card-host
把「连接」做成 DSH 里的一等对象:会话是节点,连接是容器,卡片是连接级插件 —— 一个「连接级插件宿主」。
Awesome DSH Plugin
dsh.so risk
为什么是这个插件:别的插件把能力写死在插件里;这个插件让能力变成「连接上可安装的卡片」—— 装、卸、隔离都是连接粒度的。
在一个 DSH 里同时开着好几个会话(一个查资料、一个写代码、一个跑实验)是常态。
缺的不是"会话之间能互相看见"这个功能,而是 DSH 的会话之间可编程的关系层:没有一个可以挂东西的"关系对象",权限边界、共享前提、可装载的能力就都无处安放。
在这个关系层之上,连接的两端互相看得见、说得上话、共用得上工具 —— 而互不打扰。
展开完整文档(README 剩余 1 节)
dsh-connection-card-host
把「连接」做成 DSH 里的一等对象:会话是节点,连接是容器,卡片是连接级插件 —— 一个「连接级插件宿主」。
Awesome DSH Plugin
dsh.so risk
为什么是这个插件:别的插件把能力写死在插件里;这个插件让能力变成「连接上可安装的卡片」—— 装、卸、隔离都是连接粒度的。
在一个 DSH 里同时开着好几个会话(一个查资料、一个写代码、一个跑实验)是常态。
缺的不是"会话之间能互相看见"这个功能,而是 DSH 的会话之间可编程的关系层:没有一个可以挂东西的"关系对象",权限边界、共享前提、可装载的能力就都无处安放。
在这个关系层之上,连接的两端互相看得见、说得上话、共用得上工具 —— 而互不打扰。
实际操作(录屏)
交互结构(架构图)
左:真实操作 · 右:同一动作的交互结构示意,标出了「开关语义」
目录
- 它能做什么
- 三十秒上手
- 三个层次:感知 / 约定 / 传话
- 连接上的卡片
- 卡片能做什么 / 不能做什么
- 架构与成本
- 安装
- 怎么用
- 配置项
- 卡片开发
- 卸载
- FAQ
- 文档
- 维护
它能做什么
| 互相看得见 | 查得到对方正在改哪个文件、计划进行到第几步、最近用了什么工具 —— 自动采集,对方不需要专门告诉你 |
| 说得上话 | 给对端发消息,紧急度自己判断:只告知(不打断)/排队/插话/抢占式中断(第四档,默认关闭) |
| 共用得上工具 | 连接上可以挂卡片:卡片能给会话提供工具,甚至带几十 MB 的真依赖 |
| 共用前提 | 「公约盒」存放双方说好的事:接口、单位、命名、分工边界 |
| 互不打扰 | 感知是拉取式的 —— 对方不查就零成本;不相关的连接不会吵到你 |
同一个关系层的两个例子:A 问 B 现在在干什么;或者 B 交给 A 一个只存在于这条连接上的工具。
三十秒上手
1. 建连接:按住输入框左侧的圆点(或会话行上的「…」),拖到左侧会话列表里的某一行。
起手式与开关语义
- 输入框左侧的圆点 → 拖到某一行
- 会话行上的「…」→ 拖到另一行
- 落点即开关:未连的行 = 连接;已连的行 = 断开(悬停时会提示)
- 也可以从侧栏「连接」面板里选两个会话
从会话行拖 → 连上之后(录屏)
2. 完事。两端各自收到一条静默通知(说清了连上了谁、能做什么),不打断任何人。
3. 想看得更细:点侧栏「连接」,或者让会话自己调 connection_peer_work。
连接建立后,会话行右侧会出现竖轨,两端各一个彩色圆点 —— 那是该方向的权限:
连接会持久化:下次 DSH 启动时自动恢复。
三个层次:感知 / 约定 / 传话
这三层是分开的,因为它们的代价差别很大:
| 层 | 机制 | 进对方上下文吗 | 迫使对方行动吗 | 成本 |
| A 工作状态 | 拉取(对端主动查) | 只在它查的时候 | ❌ 不会 | 0 |
| B 公约盒 | 拉取(对端主动查) | 只在它查的时候 | ❌ 不会 | 0 |
| C 传话 | 推送(进对方收件箱) | 无条件 | ✅ 必然 | 每条都花 |
关键事实:在 DSH 里投递一条消息 = 迫使对方跑一轮(agent loop 没有"看到但不理"这个状态)。
所以「说话」和「感知」必须分开做 —— 想要对方知道,用 A/B;想要对方做事,才用 C。
A. 工作状态(自动,0 成本)
自动从运行事件里采集,不要求模型额外产出任何东西:
【session-1a2b3c4d】状态:正在执行命令(2 秒前)最近动过的文件:<workspace>/src/example.js 进度:第 12 轮 / 第 4 步
B. 公约盒(显式,0 成本)
双方说好的标准:接口签名、单位、坐标系、命名、谁的活归谁。
面板上可增删;会话用 connection_conventions / connection_declare 读写。
C. 传话(四档紧急度,由发起方判断)
| 档位 | 底层 | 对方会怎样 |
| quiet | inject | 放进上下文但不唤醒 —— 它下次干活时看到,不被打断 |
| normal | followup | 排队 —— 处理完手头的事就看到 |
| urgent | steer | 插话 —— 插进它正在跑的那一轮,当场读到 |
| preempt | steer + 可选 cancel | 抢占 —— 打断它正在跑的这一轮(第四档,默认关闭;不满足条件时自动退化为 urgent,消息照样送到) |
urgent 而对端空闲时自动降级为排队(下一轮立刻开始,效果等同即时),不会失败。
preempt 的四个条件 —— 必须全部满足,否则退化为 urgent 投递,永不失败:
| 条件 | 取值 |
| 连接权限 | 需要写权限(只读连接不能停别人的活) |
| 频率上限 | 每个连接 5 分钟最多 1 次 |
| 对端正执行工具 | 绝不打断 —— 跑一半的工具被 cancel 会留下悬空调用 |
| 对端空闲或状态未知 | 不打断,只投递 |
preempt 是破坏性的:被打断的那一轮,已经做的工作白费。
取消一轮时必须带 keepInbox —— 它默认清空收件箱,会把用户自己排队的输入和别的会话发来的消息一起丢掉。
连接上的卡片
卡片 = 挂在连接上、且能分端可见的插件。
| | DSH 插件 | 卡片 |
| 装在哪 | 整个 DSH(profile) | 一条连接 |
| 谁能调 | 所有会话 | 只这条连接上的会话 |
| 可见性 | 全局 | 可分端:两端 / 仅 A / 仅 B |
| 生命周期 | 随 DSH 起停 | 随连接上的装载卸载 |
卡片能给会话提供工具
卡片里 api.registerTool(name, fn) 注册的工具,连接上的会话可以用 一个常驻桥接工具按需发现并调用:
connection_card_tool ← 唯一常驻的(1 个 schema) ├─ 不传 tool → 列出本连接上「对你在的这一端可见」的卡片与工具 └─ 传 tool → 调用它
为什么是一个桥接、而不是每个工具各占一个 schema:后者会让每个会话都为每个卡片工具付常驻成本,而卡片是随连接动态装载的。桥接只占 1 个,而且天然能在桥接层强制可见范围。
卡片里可以塞真依赖
卡片可以自带依赖(几十 MB 也可以),装在 $DSH_HOME/connection-cards/cards/, 不碰 DSH profile。
面板内安装与更新
卡片有三种来源 —— 与连接面板里卡片选择器的三种来源一致:
| 来源 | 给什么 | 实现 |
| 包名(注册表) | monitor-card / @scope/monitor-card | 从 registry 拉 tarball(一次 HTTPS GET) |
| 仓库 tgz 地址 | https://example.com/card.tgz | 下载后解压 |
| 本地目录 | D:\my-cards\monitor-card | 直接拷贝 |
- 装:包名 / 仓库 tgz 地址 / 本地目录 → 装进我们自己的目录,不跑 pnpm、不改 profile;装完立刻可用,不需要重启 DSH
- 更新:已安装的卡片带「检查更新」入口,三态分列「检查更新」→「↑ 更新到 x.y.z」/「已是最新」/「无法检查」
"无法检查"绝不显示成"已是最新" —— 那是谎报。
- 卸:已安装的卡片带卸载入口,点击前说清会移除什么(包括它在各条连接上的卡片实例)并确认一次。
内置卡随插件分发,不给卸载。卸载结果如实回报:清了什么、什么还被占用没清掉。
声明:第三方 / 社区卡片可以直接下载安装,装完即用
可以在内部直接下载安装外部 DSH 会话相关的插件卡片,并立即使用。 这不是"官方卡片市场",也不是一个需要审核上架的中心 —— 它是开放的分发:任何按卡片协议写出来的包,都能从上面三种来源装进你自己的 DSH。
装完即可用 —— 卡片装到一条连接上之后:
- 连接上的会话立刻能用它提供的工具(经 connection_card_tool 桥接调用)—— 不需要重启 DSH,不需要改 DSH 配置:不跑 pnpm、不写 dsh.profile.bundles
- 卡片装在自己的目录 $DSH_HOME/connection-cards/cards/<id>/,与 DSH profile 完全隔离
第三方 / 社区卡片与 DSH 官方项目没有隶属关系;本插件不提供审核、背书,也不存在"官方目录"这种东西。
卡片能做什么 / 不能做什么
卡片就是普通 DSH 插件,所以不是每个插件都适合当卡片。一个候选能不能挂到连接上、挂上去之后有多少能用,由下面这张规格表决定。候选列表会提前标出 适配 / 能力 / 局部 / 全局 / 未判定,但只标注、不阻断 —— 是否挂载始终由你决定,判定本身是启发式的。
挂载:三扇门
| 门 | 要求 | 不过会怎样 |
| ① 作用域 | 注册到连接级位置(conversation. / message. / input.)或纯能力(无客户端半边) | 注册到 App 级位置(侧栏 / 布局 / 设置页 / 主题 / 标题栏 / 工作区)⇒ 不建议当卡片:全局 UI 塞进连接级面板既装不下、也会和 App 布局打架 |
| ② 模块 | 宿主入口的 import 闭包内依赖必须全部可解析 | 拒绝挂载,并分类说清:致命(缺宿主能力,改设计才行)/ 可选(缺第三方依赖,补上就能过) |
| ③ 能力 | inject ⊆ {tools, effect, llm, prompt} | 拒绝挂载 —— 它要的是本插件没有的宿主服务。拒绝是正确行为,答案不是放宽能力面 |
② 的扫描面:只看入口真正会加载到的文件 —— tests/ bin/ client/build.mjs .d.ts 里的 import 不算运行时依赖。
不跑 pnpm、不执行构建:宿主不装依赖、也不构建任何包。所以作者没提交构建产物 (只有源码)的包挂不上 —— 拒绝原因会写明这一点。
客户端 UI 能到哪一层
能挂 ≠ 能取源码 ≠ 能注册槽位 ≠ 能渲染
| 情况 | 结果 |
| 只用 slots / effect | 能渲染 ✓ |
| 依赖客户端 hook(useScene / useEnabled / locale / configForms …) | 挂得上、槽位也注册了,但渲染失败 —— 显示原因、不白屏 ✓ |
| 客户端制品上限 | 32 MB(自包含 bundle 到几 MB 是正常的:内联字体/素材) |
| 组件的调用方式 | createElement(component, {}) ⇒ 不给 props:需要槽位 props 的组件会报错,被错误边界接住 |
支持 / 不支持
| | 支持 | 不支持 |
| 宿主工具 | api.registerTool(name, fn) → 会话经 connection_card_tool 调用 | 把工具注册进 DSH 全局 Loader |
| 面板 | 可选的面板 HTML(宿主侧渲染后取回) | 面板里执行任意浏览器侧代码 |
| 客户端 UI | 只用 slots / effect 的组件 | 需要客户端 hook、或需要槽位 props 的组件 |
| 连接级事件 | on(event, handler) / emit(event, data) | 跨连接的事件 |
| 消息 | send(kind, text) / read()(以某一端身份) | 会话内容的自动镜像(见配置项) |
| 依赖 | 卡片自带的任意依赖 | 在你机器上跑 pnpm 或构建步骤 |
| 分发 | registry / 仓库 tgz / 本地目录 | 审核上架的中心或市场 |
| 隔离 | 分端可见、版本门控、import / apply 崩溃隔离 | 卡片抛异常拖垮宿主 |
明确写出的边界
| 边界 | 含义 |
| 分端可见 | 可见范围分端:两端 / 仅 A 端 / 仅 B 端。A 端可见 ≠ B 端可见;不可见的一端硬调会被拒绝,并说明原因 |
| 版本门控 | DSH 版本不匹配会被明确拒绝并说明原因,而不是装上再崩。卡片另有一条 CardAPI 版本护栏(要更高版本的卡片会被拒绝装载并说明原因) |
| 崩溃隔离 | 卡片 import / apply 抛异常不会拖垮宿主 |
| 不重写 DSH | 不碰 DSH 的通信、权限、插件系统;卡片不注册到 DSH 全局 Loader |
架构与成本
三层,边界很硬 —— 每层只跟下一层说话:
┌─────────────────────────────────────────────────────────────┐
│ 卡片层(连接级插件) │ │ 只依赖 CardAPI,绝不 import @deepseek-ai/* │ │ → DSH 升级不影响卡片;我们改 CardAPI 才影响(有版本护栏) │ ├─────────────────────────────────────────────────────────────┤ │ 连接层(本插件) │ │ 连接 / 权限 / 感知 / 公约盒 / 卡片宿主 / 消息投递 │ │ → DSH 升级时,只需要改这一层 │ ├─────────────────────────────────────────────────────────────┤ │ DSH 适配层(DSHAdapter + 白名单 + 审计) │ │ 所有 DSH 交互的唯一出口 │ └─────────────────────────────────────────────────────────────┘
权限是分方向的
一根线两端各一个圆点,颜色是那个方向的权限 —— 两个方向互不影响,可以做成「甲能发、乙只能看」:
| 该方向的权限 | 允许什么 |
| 只读 | 感知与公约盒(两层都是拉取式,与权限无关) |
| 可建议 | 不修改对端工作的传话 |
| 可写入 | 能改变对端行为的传话,包括 preempt |
降低权限立即生效;提高权限需要被授权的一方确认。拒绝会说明原因。
成本
| 项 | 成本 | 说明 |
| 感知(A + B 两层) | 0 上下文 | 拉取式;对端不查就不产生任何成本 |
| 卡片工具 | 1 个常驻 schema | 而不是每个卡片工具各占一个 |
| 不相关的连接 | 0 打扰 | 搭线不唤醒;不相关的会话照常干活 |
| 常驻工具 schema | 5 个 connection_,对没有连接的会话隐藏 | 工具面按会话 scope 过滤:一条连接都没有的会话不背这份 schema |
有至少一条连接的会话,会带上这 5 个 connection_ 工具的 schema。
安装
npm(推荐,可锁版本):
dsh plugin --profile web add @noob-stupid/dsh-connection-card-host
固定版本:用 Releases 的 tgz 附件:
dsh plugin --profile web add https://github.com/Noob-stupid/dsh-connection-card-host/releases/download/<tag>/noob-stupid-dsh-connection-card-host-<version>.tgz
同一份 tgz 先下载到本地再装,效果相同
dsh plugin --profile web add ./noob-stupid-dsh-connection-card-host-<version>.tgz
GitHub 直装(装默认分支最新提交,不是固定版本):
dsh plugin --profile web add github:Noob-stupid/dsh-connection-card-host
同上,GitHub 简写(可省 github:)—— 带斜杠就走 GitHub 仓库
dsh plugin --profile web add Noob-stupid/dsh-connection-card-host
前置条件
- pnpm 在 PATH 上 —— dsh plugin 会调它。
- 兼容性:peerDependencies 声明 @deepseek-ai/dsh >=0.2.0-rc.1 <0.3.0 (另有 @deepseek-ai/cordis 与两个 @deepseek-ai/dsh-client-)—— DSH 会在安装时按版本门控,不匹配会明确拒绝并说明原因(而不是装上再崩)。
- 这四个 peer 都标了 peerDependenciesMeta.optional:这样按包名安装只增加 1 个包, 不会把 @deepseek-ai/ 依赖树拖进你的 profile。本包运行时一个 @deepseek-ai/ 都不 import(浏览器端那两个由 DSH 的 __ModuleLoader__ 注入),标 optional 不改门控 —— DSH 的 evaluatePluginCompatibility 只读 peerDependencies。
细节见 docs/compatibility.md。
- lib/ 已随仓库提交并在 npm 包里带上:装完即可用,不需要构建步骤,本包也没有需要授权的构建脚本。
怎么用
建连接 —— 三十秒上手写了两个拖拽起手式和侧栏面板。落点即开关;连接会在重启后恢复。
设权限 —— 在连接面板里按方向分别设置。降低立即生效;提高需要对方确认。
让会话干活 —— 连接建立后,会话拿到 connection_ 这一组工具:
| 工具 | 用途 |
| connection_peer_work | 读对端工作状态(A 层) |
| connection_conventions | 读公约盒(B 层) |
| connection_declare | 写公约盒(B 层) |
| connection_send | 发消息,带紧急度(C 层) |
| connection_card_tool | 列出并调用本连接上卡片提供的工具 |
感知类工具(connection_peer_work、connection_conventions)是只读的,改不了对端。
agent 的 skill 随插件一起装好
装上插件就够了:它通过 DSH 的 skill-provider 通道注册一个 connection-card skill,所以那个实例里任何 agent 都能在会话目录里看到这套连接工具怎么用 ——不用往 ~/.dsh/skills/ 拷文件,也不用多做一步。
- 文本的唯一出处:skills/connection-card.md。
- 插件加载期间它就在;卸载插件它就消失(注册绑定在插件生命周期上)。
- 内容覆盖:五个宿主工具各自什么时候用、四档紧急度怎么挑(quiet 只告知 / normal 排队 / urgent 插话 / preempt 抢占)。
- 想当普通本地 skill 用也行:同一个文件就是一份合法的扁平 skill,拷成 ~/.dsh/skills/connection-card.md 即可。
agent 的 skill 随插件一起装好
装上插件就够了:它通过 DSH 的 skill-provider 通道注册一个 connection-card skill,所以那个实例里任何 agent 都能在会话目录里看到这套连接工具怎么用 ——不用往 ~/.dsh/skills/ 拷文件,也不用多做一步。
- 文本的唯一出处:skills/connection-card.md。
- 插件加载期间它就在;卸载插件它就消失(注册绑定在插件生命周期上)。
- 内容覆盖:五个宿主工具各自什么时候用、四档紧急度怎么挑(quiet 只告知 / normal 排队 / urgent 插话 / preempt 抢占)。
- 想当普通本地 skill 用也行:同一个文件就是一份合法的扁平 skill,拷成 ~/.dsh/skills/connection-card.md 即可。
配置项
本插件没有配置文件,也不接收任何设置 schema。可配置的东西都在界面上配,其余的都在同一个目录里:
| 位置 | 存什么 |
| 连接面板(侧栏) | 连接、分方向权限、公约盒、卡片挂载 |
| 卡片选择器(连接面板内) | 卡片的安装与更新 |
| $DSH_HOME/connection-cards/ | 全部持久状态:connections.json、已安装的卡片、防篡改的 audit.log |
有两个行为刻意默认关闭,需要一次明确动作才打开:
| 行为 | 默认 | 怎么打开 |
| preempt(抢占式中断) | 关 | 需要该连接上的写权限(见 C 层) |
| 第三方卡片适配 | 关 | 创建 $DSH_HOME/connection-cards/adapter.enabled;删掉该文件即关闭 |
会话内容的自动镜像永久关闭 —— 除非会话主动调 connection_send,否则不在会话之间转发任何东西。感知(A/B 两层)是拉取式的替代方案。
卡片开发
一张卡片就是一个带 dshCard 清单的 npm 包:
{
"name": "my-card",
"version": "1.0.0",
"main": "index.js",
"dshCard": { "id": "my-card", "name": "我的卡片", "entry": "index.js", "api": 1 }
}
// index.js —— ⚠️ 绝不 import 任何 @deepseek-ai/,所有交互走 api
export function apply(api) {
api.log(已装载(scope=${api.scope}))
// 给连接上的会话提供工具
api.registerTool('greet', async (args) => {
return 你好,${args?.name ?? '世界'} }) }
// 面板 HTML(可选)
export function renderPanel(api) {
return 可见范围:${api.scope} }
| api 成员 | 说明 |
| registerTool(name, fn) | 注册工具 → 会话经 connection_card_tool 调用 |
| send(kind, text) / read() | 以某一端身份收发连接消息 |
| on(event, handler) / emit(event, data) | 连接级事件 |
| scope | 本实例作用在哪一端(both / a / b) |
| log(...) | 写宿主日志 |
dshCard.api 是卡片声明自己需要的 CardAPI 版本(缺省 1):
- 加东西不升版本 —— 老卡片照常跑
- 删或改语义才升版本 —— 宿主会拒绝装载要更高版本的卡片,并说明原因
这是我们自己的兼容性护栏:卡片不依赖 DSH 内部包,所以 DSH 升级不影响卡片;但我们改 CardAPI 会影响卡片,而 DSH 的版本门控管不到这一层。
细节见 docs/card-protocol.md。
卸载
移除插件
dsh plugin --profile web remove @noob-stupid/dsh-connection-card-host
移除它的状态 —— 插件的所有状态都在一个目录里,所以删掉它也就删掉了连接、已安装的卡片和审计日志:
rm -rf "$DSH_HOME/connection-cards"
如果你打算重装并保留连接,就别删这个目录。
FAQ
没有连接能用卡片吗? 不能。卡片挂在连接上,只有这条连接上的会话能调它提供的工具。
挂卡片会动我的 DSH profile 吗? 不会。卡片装在 $DSH_HOME/connection-cards/cards/<id>/ 下;不跑 pnpm、不写 dsh.profile.bundles、不重启。
为什么某个常用插件挂不上?
大概率是卡片能做什么 / 不能做什么里的 ② 或 ③ 门没过。
拒绝信息会写明是哪扇门、为什么。最常见的原因是包只提交了源码、没提交构建产物 ——宿主不构建任何东西。
卡片挂上了但界面上什么都没有。 看客户端 UI 那张表:需要客户端 hook 的卡片槽位注册成功但渲染失败。
面板会显示失败原因,不会白屏。
连接会额外花我的上下文吗? 感知(A/B 两层)是拉取式的,会话不问就不花。一条连接都没有的会话不背任何 connection_ 工具 schema;有连接的会话带这 5 个;卡片工具合计只占 1 个桥接 schema。
对端会话离线时还能用吗? 消息照样送到,但对端界面上显示为普通消息而不是连接卡片,内容开头会标明这一点。
东西不会丢。
第三方卡片有人审核吗? 没有。没有市场、没有审核、没有背书 —— 只从你信得过的来源装。
文档
| 文档 | 内容 |
| docs/capabilities.md | 能力报告:逐项结果、上下文成本总账、已知限制 |
| docs/card-protocol.md | 卡片协议:清单、CardAPI、安装校验、分发来源 |
| docs/compatibility.md | 兼容性:DSH 版本门控机制、两条防线 |
| docs/adapter-api.md | DSH 适配层:稳定接口与白名单 |
维护
本仓库是稳定门面,只在发版时按晋级流程同步。开发发生在预览线仓库 dsh-connection-card-host-preview。
改这个插件时
- lib/ 随仓库提交,CI 会断言源码与构建产物对齐(scripts/ci/check-src-lib-parity.mjs),所以改了 src/ 必须同时更新重新构建出来的 lib/。
- 宿主侧改动要重启 DSH 或重载插件才生效;客户端侧改动还需要刷新页面。
- CI 有四道硬门槛:语法检查、单元/契约测试 + 产物对齐、插件 patch 清单、被跟踪文件的本机路径扫描。
- npm test 不需要任何依赖、不需要网络。
MIT · 与 DSH 官方无隶属关系预览分享卡片长什么样 ↓
分享 · DeepSeek Harness Hub
Noob-stupid/dsh-connection-card-host
把「连接」做成 DSH 里的一等对象:会话是节点,连接是容器,卡片是连接级插件 —— 一个「连接级插件宿主」。
相关插件 · 其他
模型破甲清洗YuJunZhiXue/dsh-purge
⚔️ DeepSeek Harness 网络安全红队工具dsh-purge
★ 16,148已汉化DeepSeek 客户端
73
女娲蒸馏alchaincyf/nuwa-skill
输入名人名字,提炼其思维模型与决策方式供你调用
★ 37,308已汉化DSH
72
逆向分析桥接Fanqi89/dsh-rea
REA(Reverse Engineer Anything)MCP 桥接,用来把 REA 逆向工具接入 DSH 插件
★ 51已汉化DSH
72
语音女友beiyege-01/dsh-voice-ai-girlfriend
语音 AI 女友Voice AI girlfriend for DeepSeek Harness:Whisper…
★ 888已汉化DSH
71.6
红队越狱测试套件Minglink/dsh-infinite-gen-5
DeepSeek v4.1 flash 逆向工具无限五代 求 Star 收藏 ⭐欢迎大家提交项目的改进
★ 3,759已汉化DSH
71.4
协同办公空间dream-num/univer-workspace
一个让人类与 AI Agent 共同创作、协作和审阅的开源 Office 工作空间。
★ 2,266已汉化DSH
70.7