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

多窗对话墙 daetz-coder/dsh-multi-chat

DeepSeek Harnessv1.0.8
daetz-coder33 天前有提交已提供中文文档扫描:含敏感能力

DeepSeek Harness 多窗口墙:一屏并排运行并监控 N 个 DSH 会话,另有带鉴权的局域网网关,手机平板也能访问。npx dsh-multi-chat install

36.5/100
综合分
5
GitHub 星标
338
npm 周下载
—/5
用户评分

数据截至 2026/10/2 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。

安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add dsh-multi-chat
npm 源 · dsh-multi-chat 的发布者账号已校验与本仓库一致

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:已验证
是什么
dsh 原生插件 · market
装得上吗
本站已在 dsh 0.2.1-rc.5 下真实安装成功
非静态推断
L5 · 运行时冒烟等级说明 →
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
更新放缓:最近一次提交在 33 天前
档位由哪些信号合成

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

验证判据与等级说明 →
社区实装还没有人打卡 · 装过的第一个就是你
安装兼容性检查✓ 自动检查通过
展开查看检查明细 ↓

以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。

✓npm 包dsh-multi-chat @ 1.0.8
✓Node 引擎要求 >=20 · 基线 Node 22.19 满足
✓dsh CLI 依赖未声明 dsh 版本约束
✓入口文件main/exports/bin 已声明

验证方式:npm registry 存在性 + package.json 静态校验 · 最后检查 2026/10/31 04:50:26

安全扫描自动 · 静态分析源码
v2.7.0
🔵 扫描:含敏感能力尚无人工评估
展开查看扫描说明与证据 ↓

静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。

可执行外部命令读写/删除本地文件写入剪贴板(不计风险)发起外部网络请求
查看扫描证据(共 57 处,展示前 55 条)
  • 读取本地文件
    lib/index.js:518
    const parsed = JSON.parse(readFileSync(join(home, INSTANCE_TOKENS_FILE), "utf8"));
  • 发起网络请求
    lib/index.js:531
    const res = await fetch(url, { signal: controller.signal });
  • 执行外部命令来自配置/变量
    lib/index.js:598
    execFile(file, args, { timeout: 5e3 }, (error, stdout) => {
  • 读取本地文件
    lib/index.js:643
    text = readFileSync(file, "utf8");
  • 读取本地文件
    lib/index.js:677
    entries = readdirSync("/proc");
  • 读取本地文件
    lib/index.js:687
    fds = readdirSync(`/proc/${entry}/fd`);
  • 读取本地文件
    lib/index.js:831
    if (first !== void 0 && existsSync(first)) return {
  • 执行外部命令来自配置/变量
    lib/index.js:955
    const child = spawn(launcher.file, [...launcher.args, String(port)], {
  • 发起网络请求
    lib/client.js:174
    const res = await fetch(`${base}/multi/api/ports`);
  • 发起网络请求
    lib/client.js:180
    const res = await fetch(`${base}/multi/api/status?ports=${ports.join(",")}`);
  • 发起网络请求
    lib/client.js:186
    const res = await fetch(`${base}/multi/api/stop?port=${port}`, { method: "POST" });
  • 发起网络请求
    lib/client.js:200
    const res = await fetch(`${base}/multi/api/create`, { method: "POST" });
  • 发起网络请求
    lib/client.js:229
    const res = await fetch(`${base}/multi/api/link`);
  • 写入剪贴板
    lib/client.js:531
    await navigator.clipboard.writeText(url);
  • 执行外部命令来自配置/变量
    bin/dsh-multi-chat.mjs:50
    const result = spawnSync(file, args, { cwd, shell: isWin, encoding: 'utf8' })
  • 发起网络请求
    bin/dsh-multi-chat.mjs:68
    const res = await fetch(url, { signal: AbortSignal.timeout(timeoutMs) })
  • 执行外部命令来自配置/变量
    bin/dsh-multi-chat.mjs:94
    const child = spawn(file, args, {
  • 执行外部命令来自配置/变量
    bin/dsh-multi-chat.mjs:116
    const lookup = spawnSync(isWin ? 'where' : 'which', ['dsh'], { encoding: 'utf8' })
  • 读取本地文件
    bin/dsh-multi-chat.mjs:127
    return candidates.find(candidate => candidate !== '' && existsSync(candidate)) ?? null
  • 读取本地文件
    bin/dsh-multi-chat.mjs:148
    try { text = readFileSync(logFile, 'utf8') } catch { }
  • 写入本地文件
    bin/dsh-multi-chat.mjs:170
    mkdirSync(dirname(file), { recursive: true })
  • 写入本地文件
    bin/dsh-multi-chat.mjs:171
    writeFileSync(file, JSON.stringify({ updatedAt: new Date().toISOString(), tokens }, null, 2), 'utf8')
  • 执行外部命令固定命令
    bin/dsh-multi-chat.mjs:176
    spawnSync('taskkill', ['/PID', String(pid), '/F', '/T'], { stdio: 'ignore' })
  • 写入本地文件
    bin/dsh-multi-chat.mjs:184
    writeFileSync(STATE_FILE, JSON.stringify(state, null, 2), 'utf8')
  • 读取本地文件
    bin/dsh-multi-chat.mjs:189
    return JSON.parse(readFileSync(STATE_FILE, 'utf8'))
  • 读取本地文件
    bin/dsh-multi-chat.mjs:227
    const existing = existsSync(wsFile) ? readFileSync(wsFile, 'utf8') : null
  • 读取本地文件
    bin/dsh-multi-chat.mjs:227
    const existing = existsSync(wsFile) ? readFileSync(wsFile, 'utf8') : null
  • 写入本地文件
    bin/dsh-multi-chat.mjs:232
    writeFileSync(wsFile, `${existing ?? 'packages:\n - .\n'}${anchor}`, 'utf8')
  • 写入本地文件
    bin/dsh-multi-chat.mjs:249
    mkdirSync(stableDir, { recursive: true })
  • 读取本地文件
    bin/dsh-multi-chat.mjs:253
    if (!existsSync(tarball)) throw new Error('npm pack produced no tarball')
  • 写入本地文件
    bin/dsh-multi-chat.mjs:347
    mkdirSync(logDir, { recursive: true })
  • 执行外部命令固定命令
    bin/dsh-multi-chat.mjs:421
    spawn('cmd', ['/c', 'start', '', `http://127.0.0.1:${started[0]}`], { stdio: 'ignore', detached: true }).unref()
  • 删除本地文件
    bin/dsh-multi-chat.mjs:483
    rmSync(STATE_FILE, { force: true })
  • 执行外部命令来自配置/变量
    bin/dsh-multi-chat.mjs:494
    const child = spawn(process.execPath, [script, ...args], { stdio: 'inherit' })
  • 读取本地文件
    lib/types/index.js:46
    const parsed = JSON.parse(readFileSync(join(home, INSTANCE_TOKENS_FILE), 'utf8'));
  • 发起网络请求
    lib/types/index.js:63
    const res = await fetch(url, { signal: controller.signal });
  • 执行外部命令来自配置/变量
    lib/types/index.js:111
    execFile(file, args, { timeout: 5000 }, (error, stdout) => {
  • 读取本地文件
    lib/types/index.js:143
    text = readFileSync(file, 'utf8');
  • 读取本地文件
    lib/types/index.js:178
    entries = readdirSync('/proc');
  • 读取本地文件
    lib/types/index.js:190
    fds = readdirSync(`/proc/${entry}/fd`);
  • 读取本地文件
    lib/types/index.js:300
    if (first !== undefined && existsSync(first)) {
  • 执行外部命令来自配置/变量
    lib/types/index.js:381
    const child = spawn(launcher.file, [...launcher.args, String(port)], {
  • 发起网络请求
    src/client/wall-injected.ts:66
    const res = await fetch(`${base}/multi/api/ports`)
  • 发起网络请求
    src/client/wall-injected.ts:72
    const res = await fetch(`${base}/multi/api/status?ports=${ports.join(',')}`)
  • 发起网络请求
    src/client/wall-injected.ts:78
    const res = await fetch(`${base}/multi/api/stop?port=${port}`, { method: 'POST' })
  • 发起网络请求
    src/client/wall-injected.ts:87
    const res = await fetch(`${base}/multi/api/create`, { method: 'POST' })
  • 发起网络请求
    src/client/wall-injected.ts:110
    const res = await fetch(`${base}/multi/api/link`)
  • 读取本地文件
    src/index.ts:98
    const parsed = JSON.parse(readFileSync(join(home, INSTANCE_TOKENS_FILE), 'utf8')) as {
  • 发起网络请求
    src/index.ts:116
    const res = await fetch(url, { signal: controller.signal })
  • 执行外部命令来自配置/变量
    src/index.ts:176
    execFile(file, args, { timeout: 5000 }, (error, stdout) => {
  • 读取本地文件
    src/index.ts:210
    text = readFileSync(file, 'utf8')
  • 读取本地文件
    src/index.ts:243
    entries = readdirSync('/proc')
  • 读取本地文件
    src/index.ts:253
    fds = readdirSync(`/proc/${entry}/fd`)
  • 读取本地文件
    src/index.ts:367
    if (first !== undefined && existsSync(first)) {
  • 执行外部命令来自配置/变量
    src/index.ts:450
    const child = spawn(launcher.file, [...launcher.args, String(port)], {
扫描于 2026/11/3(0 天前) · 范围 入口闭包 + 全包扫描 · 来源 npm 发布包

静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。

依赖的 DSH / Cordis 模块
@deepseek-ai/cordis@deepseek-ai/dsh-client-locale@deepseek-ai/dsh-client-store@deepseek-ai/dsh-client-ui-conversation@deepseek-ai/dsh-client-ui-layout@deepseek-ai/dsh-client-ui-primitives@deepseek-ai/dsh-client-ui-renderer@deepseek-ai/dsh-client-ui-sidebar@deepseek-ai/dsh-client-ui-slots@deepseek-ai/dsh-host-webserver@deepseek-ai/dsh-invariants@deepseek-ai/dsh-util-workspace-path
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

界面预览

3 张 · 点击查看大图

功能亮点

  • 一屏并排运行多会话
  • 监控多个会话
  • 局域网网关鉴权访问
v1.0.82026-10-01
· v1.0.8 — 平台 seed 表验证基线对齐 DSH 0.2.0-rc.2 本项目现只面向 DSH 0.2.x(桌面应用那条线),但 platform-seeds.json 的 verifiedAgainst 仍停在 0.1.5-rc.2 / 0.1.7-rc.1 —— 意味着 check:platform:against 是在拿 0.1.x 的基线去比对 0.2.x 的运行实例。 用项目自带脚本重新校准: node scripts/check-platform-contract.mjs --sync · ✓ platform-seeds.json updated from … (dsh 0.2.0-rc.2) · 结论:seed 表本身一个字没变 0.2.0-rc.2 与 0.1.7-rc.1 是同一张 9 项表: react, react/jsx-runtime, react-dom, react-dom/client, @deepseek-ai/cordis, @deepseek-ai/dsh-client-store, @deepseek-ai/dsh-client-ui-slots, @deepseek-ai/dsh-client-ui-primitives, @deepseek-ai/dsh-client-ui-dockkit 这与先前从桌面应用包中独立解出 staticModules 得到的比对结果一致(逐条相同)。所以本次只是补上验证记录,不改变任何 require,不影响产物字节。 保留 0.1.x 的记录是有意的:它如实说明这张表在那两个版本上同样是这份内容;将来换版本时 --against 才能分辨「真的漂移了」还是「本来就一致」。 --- 产物无变化:24/24 测试通过,构建产物逐字节不变。
v1.0.72026-10-01
· v1.0.7 — 修复 DSH 0.2 下多窗口墙永远为空 · 问题 自 DSH 0.2 起,每个 dsh web 进程会生成一个 launch token,未带令牌的 / 返回 401,只有 ?token= 才下发界面。 墙的探活判定是 status === 200 && body.includes('__DSH_BOOT__'),不带令牌一律判为离线 —— 于是实例明明在跑,网格却是空的。 实测(DSH 0.2.0-rc.2): | 探测方式 | 结果 | |---|---| | 无 token | HTTP 401 → 判定离线 | | 带 token | HTTP 200 + __DSH_BOOT__ ✅ | · 现在开箱即用 dsh-multi-chat start --ports 3080,3081 start 会从各实例的控制台输出中读取 launch token,记录到 $DSH_HOME/multi-wall-instances.json(按端口索引),node 半边自动读取 —— 无需任何配置。 用其他方式启动的实例需要手动登记,可以直接编辑那个 JSON,或在 profile patch 层里写: · id: ui-multi-wall name: dsh-multi-chat config: tokens: "3085": "" 令牌是每进程的:重启实例会生成新的。start 同时把每个实例的完整控制台日志留在 $DSH_HOME/multi-wall-logs/instance-.log,便于取用。 · 实现要点 · 令牌只存在于进程控制台,DSH 不落盘,因此只能从启动输出取。这是把 start 作为交接点的原因。 · 交接文件放在 $DSH_HOME 而非包内:CLI 可能从 npx 缓存运行、插件装在 profile 里,只有这个路径两边解析一致。 · node 半边每次请求重新读取令牌表 —— start 可以在插件启动之后运行,而令牌只对它打印过的那个实例有效。 · 顺带修掉的两个坑 · detached + shell 的子进程拿不到 stdio,launch token 永远读不到(日志文件 0 字节)。改为解析出 dsh CLI 的 JS 入口,用 node 直接 spawn,不经 shell。 · 冷启动时服务器接了连接但还没组装完 index.html,600ms 的探活超时永远等不到完整响应。就绪探测单独放宽到 10s;墙对已运行实例的存活轮询仍用 600ms。 · 测试:node 半边迁出 jsdom 新增 tests/node-plugin.spec.ts,node 半边不再跑在 jsdom 环境。这是必须的,不是顺手整理:jsdom 自带的 AbortSignal 与 Node 的 fetch 不是同一个类,undici 会抛 TypeError: RequestInit: Expected signal to be an instance of AbortSignal. 于是每一条探活都会失败 —— node 半边在 jsdom 里的断言本来就是假的。 新增回归用例:起一个会对无 token 请求返回 401 的本地实例,验证「无 token 判死、有 token 判活」,锁住本次空墙的成因。 --- ⚠️ 本版本面向 DSH 0.2.x。若你的 dsh CLI 低于 0.2.0,请先升级 CLI —— 旧版 CLI 用的是另一套凭据格式,dsh web 无法启动。
v1.0.62026-10-01
· v1.0.6 — 官方支持桌面版 profile 市场现在同时向 web 和 desktop 两个 profile 分发本插件: · 适用于 dsh web dsh plugin --profile web add dsh-multi-chat · 适用于桌面版应用 dsh plugin --profile desktop add dsh-multi-chat 卸载同理,把 --profile web 换成 --profile desktop 即可。 · 为什么之前只声明 web platform-seeds.json 取自 web shell 的 staticModules 表,当时据此推断"桌面端没有这张表"。这次逐条实测后推翻了该结论 —— 桌面端跑的是同一套浏览器界面、同一张模块表。 | | web(dsh web) | desktop(桌面版应用) | |---|---|---| | 宿主侧 /multi/api/* 路由 | ✅ 依赖 webServer | ✅ dsh-web-app 提供该服务 | | 浏览器侧 slot 注册 | ✅ | ✅ seed 表逐条相同 | | 市场列表展示 | ✅ | ✅ 本版本新增 | · 实测依据 · 宿主侧:桌面 profile 启动图里确实存在 webserver(@deepseek-ai/dsh-host-webserver)、web-runtime 与 web-startup(@deepseek-ai/dsh-web-app),所以 /multi/api/* 路由可注册。 · 浏览器侧:从桌面应用包解出的 staticModules seed 表与 platform-seeds.json 逐条相同(同样 9 项);两个 profile 的客户端包集合也逐条相同。 · compose 实跑:dsh plugin --profile desktop add dsh-multi-chat 后,dsh --profile desktop --dump-config 成功合成出 ui-multi-wall 行。 · 一个此前容易误判的点 dsh.client.inject 里列的包(dsh-client-ui-renderer 等)不从宿主启动图解析 —— web profile 里同样是 0 引用,插件照常工作。所以"某个 inject 目标没出现在启动图里"并不构成桌面不支持的证据,这正是之前差点得出错误结论的地方。 · 先例 dshmarket 同样声明 dsh.client.platform: "web"(与本插件一致),自带 client/client.js,就运行在桌面 profile 里。 --- 本版本只改动分发声明与文档,产物字节不变(构建确定性已在 v1.0.5 验证:两次干净构建 22 个产物 + sourcemap 全部一致)。 ⚠️ 安装后需重启对应的 profile 才会加载。
数据来自仓库的 GitHub Release,可能与插件实际版本不一致。

README

由 DeepSeek 最新模型翻译生成
💬 dsh-multi-chat —— 多对话,一屏驾驭 在 DeepSeek Harness 里同时开 N 个对话,并排盯住每一个 Agent 的实时进度,还能用手机/平板躺着看。 一个浏览器,从「一次一个对话」升级成「全景多对话驾驶舱」。 给 DeepSeek Harness (DSH) 官方 Web 界面装上一面多窗口墙:在一张网格里同时显示 N 个正在运行的 DSH 对话实例(每个实例独立跑一个任务),所有 Agent 的实时进度、对话、输出一眼尽收,不用在无数标签页/窗口之间切来切去。 ✨ 它能做什么 | 能力 | 说明 | | 📺 多窗口 | 侧边栏一键进入,右侧对话区原位变成窗口网格,一个端口一格,并排看全部任务 | | 🔍 自动发现 | 扫描端口区间自动发现正在运行的 DSH 实例,也可手动管理 | | ➕ 一键新建窗口 | 墙内直接启动全新 DSH 实例,凑成你的多对话矩阵 | | 📱 手机访问 | 点「手机访问」自动起一个内置带口令认证的局域网网关,手机打开 URL、输入口令即可看进度 | | 🛑 窗口控制 | 单窗口放大、刷新、新标签页打开、关闭实例、列数切换(自动/1/2/3/4/6)| 多对话 = 多端口。 启动 N 个 dsh web --port <n>,每个实例独立跑一个对话/任务;在任意一个实例里打开多窗口墙,即可并排看到全部。
展开完整文档(README 剩余 1 节)
💬 dsh-multi-chat —— 多对话,一屏驾驭

在 DeepSeek Harness 里同时开 N 个对话,并排盯住每一个 Agent 的实时进度,还能用手机/平板躺着看。 一个浏览器,从「一次一个对话」升级成「全景多对话驾驶舱」。

给 DeepSeek Harness (DSH) 官方 Web 界面装上一面多窗口墙:在一张网格里同时显示 N 个正在运行的 DSH 对话实例(每个实例独立跑一个任务),所有 Agent 的实时进度、对话、输出一眼尽收,不用在无数标签页/窗口之间切来切去。

✨ 它能做什么

| 能力 | 说明 |
| 📺 多窗口 | 侧边栏一键进入,右侧对话区原位变成窗口网格,一个端口一格,并排看全部任务 |
| 🔍 自动发现 | 扫描端口区间自动发现正在运行的 DSH 实例,也可手动管理 |
| ➕ 一键新建窗口 | 墙内直接启动全新 DSH 实例,凑成你的多对话矩阵 |
| 📱 手机访问 | 点「手机访问」自动起一个内置带口令认证的局域网网关,手机打开 URL、输入口令即可看进度 |
| 🛑 窗口控制 | 单窗口放大、刷新、新标签页打开、关闭实例、列数切换(自动/1/2/3/4/6)|

多对话 = 多端口。 启动 N 个 dsh web --port <n>,每个实例独立跑一个对话/任务;在任意一个实例里打开多窗口墙,即可并排看到全部。

📸 运行效果

🖥️ Windows · 双对话并排 —— 两个正在运行的 DSH 实例并排列出,每格都是完整的官方对话界面,带实时在线状态点与单窗控制(放大 / 刷新 / 新标签页 / 移除):

Windows 双对话:两个 DSH 实例并排显示

📱 iPad · 双对话移动端 —— 同一局域网内,iPad 打开带口令认证的网关地址,即可在平板上一屏并排盯住两个 Agent 的实时进度:

iPad 双对话:平板端并排显示两个 DSH 实例

🖥️ Windows · 三对话全景 —— 3 列网格并排显示 3 个正在运行的实例,一屏尽收全部 Agent,把「一次一个对话」升级成「全景多对话驾驶舱」:

Windows 三对话:3 列网格并排显示 3 个 DSH 实例

🚀 30 秒上手

1. 安装 —— 从 npm 官方源一条命令搞定(无需下载任何 CLI):
dsh plugin --profile web add dsh-multi-chat # 适用于 dsh web
dsh plugin --profile desktop add dsh-multi-chat # 适用于桌面版应用

……或通过插件自带的 npx CLI(先打包再安装):
npx dsh-multi-chat install

2. 启动几个实例
npx dsh-multi-chat start --ports 3080,3081,3082

3.(重新)打开任意实例,点侧边栏底部「多窗口」→ 完成 🎉

为什么这样做

- 不改动任何官方逻辑:插件只注册两个增量列表槽位(conversation.view 视图环条目、sidebar.footer.action 侧边栏快捷入口)和五个只读 JSON 路由(/multi/api/ports、/multi/api/status、/multi/api/stop、/multi/api/create、/multi/api/link)。不替换任何既有槽位、不改写任何行、不触碰会话/代理/工具等核心逻辑。
- 界面就是官方界面:墙是官方视图环的一个视图,渲染在对话主面板内(不是弹层),主题、字号、图标、控件全部走官方 --dsw- token 与官方 primitives(Button/Input/Menu/StateDot)。
- 递归防护:墙永远不嵌入自身端口;被嵌入页面带 ?multi-wall=embed 标记,不注册任何墙界面,杜绝「墙中墙」无限递归。
- 最小改动:一个声明式 client 插件包 —— 包内自带 dsh.bundle.patch + cordis.patch.yml,DSH 启动时自动作为 bundle 层挂载,无需手改任何文件。

目录结构

dsh-multi-chat/ # 单包结构 lib/ # 已构建产物(lib/index.js + lib/client.js + 类型) src/ # 源码(node half + browser half) bin/dsh-multi-chat.mjs # 跨平台 npx CLI(install/start/stop/gateway) scripts/ install-plugin.ps1 # 打包 + 装进 profile(DSH 自动挂载 bundle 层) start-multi.ps1 / stop-multi.ps1 # 启停多个 dsh web 实例 gateway.mjs # 带令牌认证的反向代理网关(手机/远程访问) cordis.patch.yml # DSH bundle 层声明 platform-seeds.json # 本插件构建所依据的 DSH 模块表契约

安装与启用(Windows)

1) 打包并装进 web profile。插件声明了 dsh.bundle.patch, DSH 会自动挂载其 bundle 层 —— 无需手改 patch。
.\scripts\install-plugin.ps1

2) 重启 dsh web,打开任意实例
dsh web --port 3084
浏览器打开 http://127.0.0.1:3084 ,侧边栏底部出现「多窗口」按钮

或手动:

npm pack # 得到 tarball (dsh-multi-chat-<version>.tgz)
dsh plugin --profile web add ./dsh-multi-chat-.tgz # DSH 自动把包加入 bundle 层栈

卸载(一条命令,无需手动清理任何文件 —— 重启 dsh web 后生效):

dsh plugin --profile web remove dsh-multi-chat

使用

1. 先启动若干实例:.\scripts\start-multi.ps1 -Ports "3080,3081,3082,3084"(或手动 dsh web --port <n>)。
2. 打开任意实例,点侧边栏底部的「多窗口」快捷入口(或点对话区头部的「多窗口」标签页)。
3. 墙视图内:自动发现实例(自动排除自身端口)、列数切换(自动/1/2/3/4/6,默认横向铺满)、点标题放大、⟳ 单独刷新、↗ 新标签页打开、✕ 从视图移除、全部刷新、实时在线状态点。布局保存在 localStorage。
4. 退出墙:点工具栏右上角的「退出」按钮,一键切回对话视图。

启动令牌(DSH 0.2)

从 DSH 0.2 起,每个 dsh web 进程会生成一个启动令牌(launch token),不带令牌的 / 请求会得到 401,只有带上令牌才返回界面。因此不带令牌的探活会把每一个真实运行中的实例都判成离线,墙就会一直是空的。

墙会自动带上这个令牌。用 dsh-multi-chat start 启动时,它会从各实例的控制台读到令牌并记录到 $DSH_HOME/multi-wall-instances.json,node 半边会自动读取,无需任何配置:

dsh-multi-chat start --ports 3080,3081 # 令牌自动捕获

用其他方式启动的实例需要手动登记 —— 可以直接编辑上面那个 JSON 文件,或者在 profile 的 patch 层里写:

- id: ui-multi-wall
name: dsh-multi-chat
config:
tokens:
"3085": "<dsh web 启动时打印的令牌>" "3086": "<dsh web 启动时打印的令牌>"

令牌是每进程的:重启实例会生成新的,记得从该实例的启动输出里重新读取。dsh-multi-chat start 同时会把每个实例的完整控制台日志留在 $DSH_HOME/multi-wall-logs/instance-<port>.log。

手机 / 远程访问(内置认证网关)

官方 dsh web 出于安全刻意禁止 --host 0.0.0.0(会向网络暴露远程代码执行)。本插件内置了一个带令牌认证的内联网关:点工具栏「手机访问」按钮,它会自动为本实例启动一个网关(监听 0.0.0.0,反向代理到 127.0.0.1:<本实例端口>),并返回局域网 URL + 登录口令。

点击「手机访问」→ 得到:手机在同一网络时可用:http://10.105.7.204:9477 口令:2efb23eade16

手机打开该 URL、输入口令即可进入完整 DSH 界面。网关的安全模型:

- HMAC 签名的 HttpOnly/SameSite 会话 Cookie(默认 12h),?token= 供脚本快捷使用,按 IP 限流登录失败
- 所有代理请求把 Host/Origin 重写为回环目标,官方 /api 浏览器信任栅栏(DNS-rebinding 防线)判定为本地请求,无需重启加 --trusted-host
- WebSocket 升级与 SSE 流原样透传
- 目标端口撞上 Windows 排除段或已占用时,自动回退到 OS 分配的空闲端口

也有独立的 scripts/gateway.mjs(带可选 TLS)供进阶场景手动使用。

分发与安装

dsh-multi-chat 已发布到 npm(无作用域公开包),并在 GitHub 按 tag 发布 Release(源码 zip/tarball)。下面每条渠道殊途同归:包成为目标 profile 的依赖 → DSH 自动调和进 bundle 层栈(包声明了 dsh.bundle.patch,自带 cordis.patch.yml 自动挂载,无需手改任何文件)→ 重启该 profile 生效。

用哪个 profile?(web 还是 desktop)

两者都支持,而且跑的是同一套浏览器界面、同一张平台模块表:

| | web(dsh web) | desktop(桌面版应用) |
| 宿主侧 —— /multi/api/ 路由 | ✅ 依赖 webServer 服务 | ✅ dsh-web-app 已提供 |
| 浏览器侧 —— slot 注册 | ✅ | ✅ seed 表完全相同 |
| 市场列表展示 | ✅ | ✅ |

下面任何命令把 --profile web 换成 --profile desktop 即可。可参照的先例是 dshmarket:它同样声明 dsh.client.platform: "web",却在两端都正常运行。

安装 / 卸载速查表

| 渠道 | 安装 | 卸载 |
| 一条命令(npm 官方源) | dsh plugin --profile web add dsh-multi-chat | dsh plugin --profile web remove dsh-multi-chat |
| npx(免下载) | npx dsh-multi-chat install | dsh plugin --profile web remove dsh-multi-chat |
| 全局 CLI(npm) | npm i -g dsh-multi-chat 然后 dsh-multi-chat install | dsh plugin --profile web remove dsh-multi-chat 然后 npm rm -g dsh-multi-chat |
| Tarball(离线) | npm pack → dsh plugin --profile web add ./dsh-multi-chat-.tgz | dsh plugin --profile web remove dsh-multi-chat |
| Git clone | node bin/dsh-multi-chat.mjs install | dsh plugin --profile web remove dsh-multi-chat |

上表每一行都可以换成 --profile desktop;详见用哪个 profile?。

dsh plugin --profile web remove dsh-multi-chat 是所有渠道统一的卸载命令:
它从 profile 移除依赖,DSH 会自动把它从 dsh.profile.bundles 层栈里剔除(层栈按已安装依赖实时调和,详见 dsh plugin --help)。之后重启 dsh web 卸载生效。

pnpm ≥ 11 注意:pnpm 11 的 minimumReleaseAge 供应链保护会跳过刚发布的版本
(默认阈值 1 天),静默回退到最新的「已过成熟期」版本。如果
dsh plugin --profile web add dsh-multi-chat 装到的版本比最新版旧,请在
~/.dsh/profiles/web/pnpm-workspace.yaml 里加上 minimumReleaseAge: 0 后重试。
自带 CLI(npx dsh-multi-chat install)会在安装时自动写入该设置。

仓库还内置了一个跨平台 CLI dsh-multi-chat(bin/dsh-multi-chat.mjs)。它的 install 命令会探测 $DSH_HOME(缺省 ~/.dsh),把包打包进 profile 的 plugins/ 目录,再执行 dsh plugin --profile web add <tarball>(与 install-plugin.ps1 行为一致)。

渠道一:npm / npx(推荐,最省事)

已发布到 npm —— 任意机器一句话安装(需 node + pnpm)
npx dsh-multi-chat install

或全局装 CLI,再从任意目录安装插件
npm i -g dsh-multi-chat
dsh-multi-chat install

或直接用 npx 跑单条命令(无需安装插件)
npx dsh-multi-chat start --remote --token <口令> --ports 3080,3081
npx dsh-multi-chat gateway --target 127.0.0.1:3080 --token <口令>

维护者发布/再发布:npm publish(无作用域公开包 dsh-multi-chat)。

渠道二:GitHub Release

从 Releases 下载源码 zip/tarball,解压后进目录:

node bin/dsh-multi-chat.mjs install # 打包 + dsh plugin add(见速查表)
node bin/dsh-multi-chat.mjs start --ports 3080,3081

打 tag 后,GitHub 会自动生成 source zip/tarball 资产;也可在 Release 附加 npm pack 产出的 .tgz 作为离线安装包。

渠道三:git 直接安装

git clone https://github.com/daetz-coder/dsh-multi-chat.git
cd dsh-multi-chat

node bin/dsh-multi-chat.mjs install # 打包 + dsh plugin add(见速查表)
node bin/dsh-multi-chat.mjs start --ports 3080,3081
node bin/dsh-multi-chat.mjs gateway --target 127.0.0.1:3080 --token <口令>

在 git clone 出来的仓库里也可以直接走一条命令:dsh plugin --profile web add dsh-multi-chat —— 直接从已发布版本安装,无需自己打包。

本仓库直接运行(开发)

node bin/dsh-multi-chat.mjs install
node bin/dsh-multi-chat.mjs start --ports 3080,3081
node bin/dsh-multi-chat.mjs stop
node bin/dsh-multi-chat.mjs gateway --target 127.0.0.1:3080 --token <口令>

🔍 发现与生态

本插件遵循 DeepSeek Harness 官方 client 插件规范:

- 在 GitHub 插件生态中被发现:给本仓库添加 dsh-plugin topic,即可在官方 dsh-plugin topic 页 被搜索到(官方推荐的第三方插件发现方式)。
- 双语技术文档:本仓库在根目录提供 README.md(英文)和 README.zh.md(中文),与官方 packages/client/ 插件的双语惯例一致。
- 纯增量、不碰核心:只注册 conversation.view / sidebar.footer.action 两个列表槽位 + /multi/api/ 只读路由,不改动任何官方核心逻辑。

从源码构建

单包结构使用 tsdown 进行打包,tsc 生成类型声明:

npm install
npm run build # tsc + tsdown → lib/,并跑平台契约守卫
npm test # 先构建,再跑 vitest

npm test 会先构建再测,保证测的始终是真正要发布的产物,而不是过期的 lib/。

- tests/browser-plugin.client.spec.tsx —— 浏览器半边(视图环条目、侧边栏快捷入口、墙 store、递归守卫、HMR 卸载)跑在 jsdom 里的真实 cordis Context 上,外加 node 半边(探针路由、配置 schema、停止语义)。
- tests/client-bundle.spec.ts —— 把构建产物 lib/client.js 送进 tests/dsh-module-loader.ts(web shell 模块表的替身)加载,是 v1.0.3 启动失败的回归测试。

平台契约

客户端插件 bundle 不是 ES module。web shell 把它当普通脚本加载,其唯一的顶层副作用是
window.__ModuleLoader__.load({ id, factory });factory 里每一个 require(spec) 都对着
shell 的模块表解析。该模块表只有两个来源:

1. 平台 seed 表 —— shell 硬编码进自己 Vite 产物的固定 specifier 集合;
2. boot graph 里的包行 —— 其他插件的 bundle。

插件无法凭空造出别人的包行,所以 require 任何不在 seed 表里的名字都会抛 missed the module table;而 boot 是 fail-closed 的,整个 Web UI 会停在 "Failed to load plugins" 卡片上。

v1.0.3 就是这么坏的:它 require 了上游已删除的 @deepseek-ai/dsh-client-runtime/client,而构建期毫无察觉 —— 类型来自一份未版本化的 harness 单仓本地检出,插件对着一个已经不存在的平台编译通过了。

platform-seeds.json 钉住这张 seed 表,同时是 tsdown.config.ts(哪些 specifier 不打包)与 scripts/check-platform-contract.mjs(产物 require 越界即让构建失败)的唯一事实来源:

npm run check:platform # 已包含在 npm run build 里
npm run check:platform:against -- <已安装的 dsh 路径> # 与真实安装做漂移比对
npm run sync:platform -- <已安装的 dsh 路径> # 采纳新版本的 seed 表

@deepseek-ai/* 平台包以精确版本钉在 devDependencies 里,插件编译所依据的平台面因此可版本化、可评审,而不再依赖本地检出。

License

MIT
预览分享卡片长什么样 ↓daetz-coder/dsh-multi-chat
分享 · DeepSeek Harness Hub
daetz-coder/dsh-multi-chat

DeepSeek Harness 多窗口墙:一屏并排运行并监控 N 个 DSH 会话,另有带鉴权的局域网网关,手机平板也能访问。npx dsh-multi-chat install

这是你的仓库?认领条目

回到顶部