把散在各处的会话收进 DSH 的同一棵树。
36.5/100
综合分
5
GitHub 星标
174
npm 周下载
—/5
用户评分
数据截至 2026/11/3 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add dsh-session-hubnpm 源 · dsh-session-hub 的发布者账号已校验与本仓库一致
状态速览
判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态信任档位:已验证
是什么
dsh 原生插件 · market
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 1 天前
档位由哪些信号合成
本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。
社区实装还没有人打卡 · 装过的第一个就是你
安装兼容性检查✓ 自动检查通过展开查看检查明细 ↓
以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。
✓npm 包dsh-session-hub @ 0.1.0
✓Node 引擎要求 ^22.19 || >=24 · 基线 Node 22.19 满足
✓dsh CLI 依赖未声明 dsh 版本约束
✓入口文件main/exports/bin 已声明
验证方式:npm registry 存在性 + package.json 静态校验 · 最后检查 2026/11/3 15:16:47
安全扫描自动 · 静态分析源码v2.7.0🔵 扫描:含敏感能力尚无人工评估展开查看扫描说明与证据 ↓
安全扫描自动 · 静态分析源码
v2.7.0静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。
读写/删除本地文件发起外部网络请求
查看扫描证据(共 16 处,展示前 11 条)
- 发起网络请求lib/index.js:74
return globalThis.fetch(input, { - 读取本地文件lib/index.js:670
privateKey = readFileSync2(expandHome(this.target.privateKeyPath)); - 读取本地文件lib/index.js:940
const rows = await readFile(file2, "utf8"); - 读取本地文件lib/index.js:1032
const text = await readFile2(file2, "utf8"); - 读取本地文件lib/index.js:1251
const text = await readFile3(file2, "utf8"); - 读取本地文件lib/index.js:1339
entries = await readdir(dir, { withFileTypes: true }); - 读取本地文件lib/index.js:1524
mtime = (await stat(file2)).mtimeMs; - 发起网络请求lib/index.js:2735
const response = await fetch(`${baseUrl}/hub/events?token=${encodeURIComponent(eventToken)}`, { - 写入本地文件lib/index.js:3065
mkdirSync(dirname(this.dataFile), { recursive: true }); - 写入本地文件lib/index.js:3067
writeFileSync(tmp, JSON.stringify(stored, null, 2), { mode: 384 }); - 写入本地文件lib/index.js:3068
renameSync(tmp, this.dataFile);
扫描于 2026/11/3(1 天前) · 范围 入口闭包 + 全包扫描 · 来源 npm 发布包
静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。
依赖的 DSH / Cordis 模块
@deepseek-ai/cordis@deepseek-ai/dsh-typert-protocol@deepseek-ai/dsh-typert-registry@deepseek-ai/dsh-host-apiproxy@deepseek-ai/dsh-client-connection@deepseek-ai/dsh-client-runtime@deepseek-ai/dsh-client-ui-slots@deepseek-ai/dsh-client-locale@deepseek-ai/schemastery@deepseek-ai/dsh-host-webserver@deepseek-ai/dsh-client-ui-primitives给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个
功能亮点
- 聚合多服务器会话
- 并入官方会话树
- 原生操控远端会话
从一个官方 Web UI 聚合多个远程 DeepSeek Harness(DSH)服务器的会话并原生操控——hub 网关 + 官方 UI 桥接。多服务器 DSH 会话聚合与原生操控
最近更新
在 GitHub 查看全部 →v0.1.02026-08-15
· dsh-session-hub 0.1.0
从 alpha 毕业的第一个正式版本。聚合多台远程 DSH 服务器、导入本机其他工具的历史会话,都在一个面板里完成。
· 功能
· 多服务器聚合:每台已配置的远程 DSH 服务器一个虚拟分组;会话历史、发消息、取消、重命名、fork、换模型、审批应答全部路由到所属服务器
· SSH 隧道托管:添加服务器填 user@host + 私钥即可,插件自己开隧道、保活重连
· 外部会话导入:Codex CLI / Claude Code / opencode / Pi 历史只读视图,按项目分组;工具调用渲染为真实卡片(参数/结果/错误标记取自源日志);勾选 Auto 后 60 秒周期扫描跟进;发消息自动转正为真实 DSH 会话
· 模型配置增量同步:只补本机有而远端缺的提供方/默认模型/API Key
· 四功能开关(aggregate/tunnel/modelSync/importer),默认全开;全关时不拦截任何 /api
· 自环防护 + 路由冲突降级:指向自己的配置被拒绝;路由撞车时静默降级而非拖垮 dsh 启动
· 安装
npm i dsh-session-hub
· 变更记录
自 0.1.0-alpha.3 毕业;自 alpha.2 以来的变化(实时流式回滚、工具卡片、侧边栏实时刷新、导入会话可发消息)见 CHANGELOG.md。
v0.1.0-alpha.12026-08-14预发布
首个公开 alpha 版本。
安装
dsh plugin --profile web add dsh-session-hub@alpha
能力
· 多台 DSH 服务器的会话聚合进本机官方 Web UI(侧边栏零改动、对话区零替换)
· 远端会话支持原生树操作:新建 / 归档 / 重命名 / 删除
· 导入 Codex CLI / Claude Code / opencode 历史会话(按软件手动开启,只读;续聊时转为真实 DSH 会话)
· 模型配置与凭据增量同步到远端(只补缺,不覆盖)
已知限制
仅在单一环境验证(Windows 本机 ↔ 腾讯云 Linux,SSH 隧道)。配置格式与帧协议在 1.0 前可能破坏性变更。
数据来自仓库的 GitHub Release,可能与插件实际版本不一致。
README
由 DeepSeek 最新模型翻译生成· English
dsh-session-hub
把散在各处的会话收进 DSH 的同一棵树。
远端服务器上的 DSH 会话,本机 Codex CLI、Claude Code、opencode、Pi 的历史对话,一起进官方 Web UI。侧边栏和对话区都用官方的,插件只搬数据。两件事互相独立,可以只装一件。
[!NOTE]
正式版 0.1.0(变更记录)。核心链路都在真机上跑通过:网关路由、官方 UI 桥接、实时帧注入、审批应答、跨机对话。但只验证过一套环境(Windows 本机 + 腾讯云 Linux,SSH 隧道),配置格式和安全边界在小版本内仍可能调整。
这是什么
DSH 的会话树只认这一台机器、这一个工具产生的会话。于是:
服务器上跑了一半的任务,要开新标签页、连隧道、从头找起。关掉笔记本再回来,不知道它现在到哪一步。
上周用 Codex CLI 聊过的方案,在 DSH 里根本不存在,只能去翻 ~/.codex 底下的 JSONL。
展开完整文档(README 剩余 1 节)
· English dsh-session-hub 把散在各处的会话收进 DSH 的同一棵树。 远端服务器上的 DSH 会话,本机 Codex CLI、Claude Code、opencode、Pi 的历史对话,一起进官方 Web UI。侧边栏和对话区都用官方的,插件只搬数据。两件事互相独立,可以只装一件。 [!NOTE] 正式版 0.1.0(变更记录)。核心链路都在真机上跑通过:网关路由、官方 UI 桥接、实时帧注入、审批应答、跨机对话。但只验证过一套环境(Windows 本机 + 腾讯云 Linux,SSH 隧道),配置格式和安全边界在小版本内仍可能调整。 这是什么 DSH 的会话树只认这一台机器、这一个工具产生的会话。于是: 服务器上跑了一半的任务,要开新标签页、连隧道、从头找起。关掉笔记本再回来,不知道它现在到哪一步。 上周用 Codex CLI 聊过的方案,在 DSH 里根本不存在,只能去翻 ~/.codex 底下的 JSONL。 这个插件把这两类会话都接进同一棵树: 工作区 ├── my-project ← 本机工作区(官方原样) ├── another-repo ← 本机工作区 ├── tencent ← 远端服务器,点开就是那台机器的会话 │ ├── 部署脚本调试 │ └── 日志分析 └── audio-engine ← 本机工作区,混着导入的历史对话 ├── 重构方案讨论 (Codex CLI) └── 修 CI 那次 (Claude Code) 点开任意一个会话,包括远端的,官方对话区照常渲染:历史、逐 token 流式输出、审批卡片、提问问卷,全是官方组件。插件只搬数据,不画界面。 远端会话并不是你本机在代跑。agent 循环在那台机器自己的 dsh 里,本机只是个看板。 所以可以关掉本机去吃饭,回来再接上看它跑到哪了:还在跑就从当前这一刻续上实时流,跑完了就看完整历史。 两件独立的事 插件做两件事,互相不依赖,装了可以只开一件(见配置): 🖧 把远端机器接进来 每台服务器成为工作区树里的一个分组(标题带 🖥️ 标识,一眼可辨是远程),组内是那台机器的会话。 - 树内操作是原生语义:+ 新建会话在那台机器上创建、归档 / 删除路由到会话所在机器、重命名分组即重命名服务器、删除分组即断开该连接。 - 会话跑在远端,不依赖你开着:agent 循环在那台机器自己的 dsh 里推进。本机关掉、断网、换台电脑,远端该跑的照跑。回来时它可能已经跑完了。 - 中途接得上:hub 启动时若远端正在生成,从当前这一刻续上实时流,缺席期间的历史由 session.history 补齐,和官方断线重连是同一套语义。 - 对话区零替换:远端会话的历史、实时流、审批与提问卡片全部由官方组件渲染。没有自绘聊天窗,没有 shadow slot。 - 隧道不用你建:填 user@host 与私钥路径,插件自己开 SSH 隧道、自己保活重连,本地端口它自己分配。 - 模型配置增量同步:服务器连上后,把本机有而远端缺的提供方、默认模型与 API Key 补过去。只补缺,不覆盖远端已有配置。 📥 把其他工具的历史接进来 本机 Codex CLI / Claude Code / opencode 聊过的对话,按项目目录归入对应工作区。 - 按软件逐个手动开启,未点「导入」的软件日志一个字节都不会被读; - 找不到对应工作区的项目会自动建成真实工作区(只登记,不创建目录);目录已不存在的会话自动隐藏; - 导入的会话在树里只读;直接向它发消息会自动转成真实 DSH 会话(保留用户/助手对话,原只读副本隐藏); - 源日志只读打开,从不改写、从不删除。 两件事共用的底子 - 纯 /api 协议:不靠 SSH 执行命令、不做屏幕抓取、不改远端配置,远端不装任何插件,就是一个未经修改的 dsh web。 - 官方 UI 零改动:不替换侧边栏、不替换对话区。插件只在数据层做合并与路由。 只有一台机器? 第二件事照样成立:不填任何服务器,只开导入,把本机 Codex / Claude Code / opencode 的历史接进来即可。 安装 dsh plugin --profile web add dsh-session-hub 装完重启 dsh web(kill -TERM <pid> 并等待退出,别用 kill -9,会在写入中途撕裂会话 zstd 日志),刷新页面。 设置 → 插件 里出现 会话枢纽 标签页即成功。 dsh plugin 把参数原样转发给 profile 目录里的 pnpm(本机需要 pnpm)。不带版本标签装的是正式版 latest;预发布版(如有)需显式 dsh-session-hub@alpha。 不走 npm:直接装 GitHub tarball dsh plugin --profile web add https://github.com/Asaiuta/dsh-session-hub/archive/refs/tags/v0.1.0.tar.gz 仓库已提交构建产物,tarball 安装同样无需本地构建。 从源码安装(改代码调试) git clone https://github.com/Asaiuta/dsh-session-hub && cd dsh-session-hub npm install && npm run build dsh plugin --profile web add file:$(pwd) 或手动挂载:把 cordis.patch.yml 的 insert 条目并入 profile 的 patch 层。 升级 / 禁用 / 彻底移除 升级:重跑 add 并重启 dsh web: dsh plugin --profile web add dsh-session-hub 临时禁用:从 profile 的 bundle 列表 / cordis.patch.yml 移除 dsh-session-hub 条目后重启。 服务器注册表保留,重新启用即恢复。 彻底移除:移除条目并重启后,删掉这两个文件即可(都在 $DSH_HOME/plugins/): dsh-session-hub.json(服务器注册表)、dsh-session-hub-imports.json(导入解析缓存)。 插件从不写入你的项目目录,也不改动任何工具的原始日志。 环境要求 | 项 | 要求 | | 本机 DSH | 实测 @deepseek-ai/dsh@0.1.0-rc.6;mainline 未逐 commit 跟踪 | | Node | ^22.19 \|\| >=24(用到内置 WebSocket / fetch) | | Profile | 仅 web:插件 inject webServer / apiProxy,装进 headless / tui 会一直 pending 并导致该 profile 启动失败 | | 浏览器 | 官方 Web UI,无版本约束(插件不替换任何 UI) | | 远端 DSH | 仅接远端时需要:任何能应答标准 /api 的 dsh web,远端无需安装本插件 | | 外部工具 | 仅导入时需要:本机装过 Codex CLI / Claude Code / opencode / Pi 中的任意一个,读它们默认的日志位置 | 最后验证 2026-08-14:远端侧,本机 Windows + Node v24.9.0 对远端 OpenCloudOS 9.4 + Node v24.9.0,经 SSH 隧道跑通跨机对话、审批应答与实时流全链路;导入侧,本机解析 615 个会话(Codex 523 / Claude Code 67 / opencode 25),归入 21 个工作区,只读会话转真实会话已验证。 Alpha 期间:配置格式、路由表、/hub/events 帧协议在 1.0 前可能破坏性变更。 快速开始 装完插件后按你要的那件事往下走,两条路互不依赖: - 只想把本机 Codex / Claude Code / opencode 的历史接进来,直接跳到导入本机其他工具的会话,三次点击就完事,不用配服务器、不用隧道。 - 想接远端机器,从下面这个最小示例开始。 接一台远端机器 最小可复现示例:一台远端 + 本机 hub + SSH 隧道。 ① 远端(假设 10.0.0.5),什么都不用装,保持默认即可: dsh web --port 3080 # 只监听环回,这也是当前唯一允许的绑定 ② 本机,只装插件: dsh plugin --profile web add https://github.com/Asaiuta/dsh-session-hub/archive/refs/tags/v0.1.0.tar.gz 重启 dsh web 隧道不用自己建,下一步填好 SSH 信息,插件会自己开、自己保活。 ③ 浏览器 http://127.0.0.1:3080: 1. 设置 → 插件 → 会话枢纽 → 添加服务器,保持默认的 SSH 隧道 方式,填: | 字段 | 例 | | 名称 | tencent | | 主机 | 10.0.0.5 | | SSH 用户 | root | | 私钥路径 | ~/.ssh/id_ed25519(留空则用 ssh agent) | | 远端 dsh 端口 | 3080 | 点测试(返回远端 DSH 版本即通)→ 添加。本地端口由插件自行分配,你不必知道它是多少。 2. 官方工作区树里出现名为 tencent 的分组,远端会话就在组内; 3. 点开任一会话,官方对话区照常工作:历史、实时流、审批卡片、发送 / 取消 / 重命名。 已经手动开着 ssh -L 的话,切到直连地址填 http://127.0.0.1:<你的端口> 也可以,插件不会去碰那条隧道。 为什么一定要隧道:当前 dsh(0.1.0-rc.6)拒绝把 Web 服务绑到环回以外。--host 0.0.0.0 被 CLI 挡下("would expose remote code execution to the network"),具体 LAN IP 连配置校验都过不了(host 只接受 127.0.0.1 与 0.0.0.0 两个字面量)。所以远端保持默认,由隧道把它带到本机环回;这也意味着不存在把 3080 暴露公网的选项,上游已经先一步堵死了。 隧道进程活在 dsh 里:dsh 退出时一并关闭,启动时按保存的配置自动重建(端口每次重新分配,所以配置存的是 SSH 目标而不是 URL)。SSH 掉线会以退避重连,恢复后链接自动指向新端口。 导入本机其他工具的会话 不需要任何服务器或隧道。打开 设置 → 插件 → 会话枢纽 → 外部会话,按软件点「导入」: | 软件 | 读取位置 | | Codex CLI | ~/.codex/sessions//rollout-.jsonl | | Claude Code | ~/.claude/projects//.jsonl | | opencode | ~/.local/share/opencode/opencode.db | | Pi Coding Agent | ~/.pi/agent/sessions//.jsonl | - 未点「导入」的软件,日志一个字节都不会被读; - 勾选自动:60 秒周期扫描 + 导入时增量解析跟进新写入的日志,不勾选就只在你点刷新时更新; - 导入的会话在树里只读,工具调用(参数、结果、错误标记)以真实工具卡片显示,文本与卡片按源日志顺序排列,卡片的 call id / 参数 / 输出均取自原始记录,不合成; - 移除:把该软件的会话撤出树,不影响其他软件,也不动原始日志; - 导入的会话在树里只读;直接向它发消息会自动转成真实 DSH 会话(保留用户/助手对话和工具调用,原只读副本隐藏)。 配置 四个功能开关默认全开,装完即用。只要其中一件时把另一件关掉: 只要导入,不要多服务器(单机用户的典型配置) - id: dsh-session-hub config: features: aggregate: false tunnel: false modelSync: false 只要多服务器,不碰本机其他工具的日志 - id: dsh-session-hub config: features: importer: false 关闭等于不存在,不是闲置:关掉的功能不构造服务、不读缓存、不扫目录、不注册路由,也不在设置页出现。 四个全关时插件等于没装(不拦截任何 /api)。 | 配置项 | 类型 | 默认 | 说明 | | dataFile | string? | $DSH_HOME/plugins/dsh-session-hub.json | 服务器注册表持久化位置 | | trustedHosts | string[]? | 仅环回 | 网关拦截的 /api 再校验白名单(裸 host[:port],格式同 client-connection.trustedHosts;SSH 隧道部署无需配置) | | features.aggregate | boolean | true | 多服务器聚合:关闭后不注册 /api 拦截、不建虚拟分组,服务器卡片从设置里消失 | | features.tunnel | boolean | true | SSH 隧道托管:关闭后添加表单只剩直连地址,已保存的 SSH 条目会被跳过并在日志里说明 | | features.modelSync | boolean | true | 模型配置同步:关闭后不读本机凭据、不推送远端,同步卡片消失 | | features.importer | boolean | true | 外部会话导入:关闭后不读解析缓存、不扫描日志目录,导入卡片消失(缓存文件保留,重新开启即恢复) | 在 profile 的 cordis.yml / cordis.patch.yml 中配置: - id: dsh-session-hub config: dataFile: /srv/dsh-hub-servers.json trustedHosts: ['192.168.1.10:3080'] 唯一相关的环境变量是 DSH_HOME(影响注册表默认路径,默认 ~/.dsh)。插件不需要任何 API key 或令牌环境变量。 运行期 /hub/events 用的随机 token 每进程生成一次,只经快照下发给浏览器,不落盘。 它访问什么 读你本机的会话日志(需你逐个授权)、和你配置的服务器通信、不碰你的项目文件。 下面每一项都只在对应功能开着时才发生:关掉导入就不读任何日志,关掉聚合就不发起任何出站连接。 完整清单(文件 / 网络 / 凭据 / 会话内容) 文件访问: - 读/写 $DSH_HOME/plugins/dsh-session-hub.json(0600,原子写:tmp + rename); - 读/写 $DSH_HOME/plugins/dsh-session-hub-imports.json(导入会话解析缓存,0600); - 只读扫描本机会话日志:~/.codex/sessions//rollout-.jsonl、~/.claude/projects//.jsonl、~/.local/share/opencode/opencode.db(SQLite 只读打开)、~/.pi/agent/sessions//.jsonl,用于生成只读的导入会话视图;绝不写回这些文件。按软件逐个手动导入:未在设置里点「导入」的软件,其日志不会被读取。 网络: - 出站(对每个已配置服务器 baseUrl):HTTP POST /api/(unary RPC)+ WebSocket 升级 /api/events.mux、/api/events.host; - 入站(本机进程内):/hub/events SSE(仅环回 Host + 随机 token + 浏览器 same-origin 三重校验)、Typert /api/sessionHub/(环回); - 入站(浏览器):被拦截的 /api/session. 与 /api/respond 会经网关再校验环回/trustedHosts。 SSH 密钥:SSH 隧道条目会按你填写的路径读取私钥(仅用于建立那条隧道),密钥内容不落盘、不外传、不写进插件配置,配置里只存路径。留空则走 ssh agent,插件完全不接触密钥材料。 凭据:模型同步功能会读取本机 $DSH_HOME/.credentials.yaml 明文(仅用于提取 llm- 命名空间 apiKeyEnv 引用的密钥值),并在远端未配置该引用时经 credentials.set 写入远端(唯一的密钥出站方向,隧道/HTTPS 下加密)。密钥从不随响应回传,hub 也不落盘密钥副本。若不想自动推送密钥,可在服务器配置中不启用模型同步(或拆掉该服务器的 llm- 命名空间)。 用户数据:远端会话列表、历史内容、实时流会经由 hub 进程与浏览器中转显示。跨机传输走 SSH 隧道(加密,也是目前唯一的连接方式);我方不明文落盘任何会话内容。 和其他插件共存 生态里已经有 1800+ 个 dsh 插件,其中不少也改前端。这个插件改的是数据层,不是画面层: | 层 | 谁在改 | 冲突吗 | | 会话/工作区数据 | 本插件(网关合并 session.list / workspace.list) | 与 UI 插件不冲突 | | 侧边栏/对话区画面 | 各类 UI 插件(换树、换主题、加 Tab) | 本插件一个都不占 | 本插件在浏览器侧只做一件事:往 settings.plugins.tab 这个 list 槽加一个 id: 'session-hub' 的条目。 不 shadow sidebar.workspaces,不 shadow conversation,不碰任何单槽,所以换侧边栏、换对话区、换主题的插件都能与它并存。 换树的插件反而会自动显示远端与导入的会话:它们通过官方 useSessions / useWorkspaces 取数,而这两个 hook 的上游正是本插件合并过的 /api。例如 dsh-plugin-ya-workspace-sidebar 完整替换了 sidebar.workspaces,本插件的服务器分组和导入会话照样出现在它画的树里,双方互不知情。 唯一的冲突:也拦截 /api 的插件 DSH 的 web server 对同一条 exact 路径只允许一个注册者(重复注册直接抛错,这是刻意的组合约定)。 本插件为实现会话合并,接管了这些路径: /api/session.list /api/session.history /api/session.prompt /api/session.cancel /api/session.rename /api/session.fork /api/session.models /api/session.selectModel /api/session.updateQueue /api/session.attachment /api/session.search /api/session.create /api/workspace.list /api/workspace.rename /api/workspace.delete /api/workspace.archiveSession /api/respond 另一个插件若也拦截其中任意一条,本插件会让出已占的路由并降级,dsh 与对方插件照常运行: [dsh-session-hub] gateway DISABLED — webserver: duplicate exact route "/api/respond". Another plugin intercepts the same route, so remote servers and imported sessions will not appear. Set features.aggregate and features.importer to false to silence this, or remove the conflicting plugin. 此时远端会话与导入会话不会出现(这两项功能依赖那些路由),但 DSH 本身、对方插件、以及本插件的设置页都正常。 把 features.aggregate 与 features.importer 设为 false 可消除这条告警。 这条路径实机验证过:用一个抢占 /api/respond 的测试插件复现,dsh 首页仍 200、官方 session.list 正常应答、 sessionHub 端点存活,路由撞车不会拖垮宿主。 绝大多数插件不走这条路:加工具、加技能、加 UI、加 Tab 都不需要拦截 /api。 Typert 命名空间:本插件独占 sessionHub(对应 /api/sessionHub/),SSE 独占 /hub/events。命名空间撞车的可能性极低。 和 dsh-remote 的分界 dsh-remote 同样做“远程”,但做的是另一件事。两者技术上不冲突(它占 /dsh-remote/ 与工作区选择器槽,我们占 /api/ 与设置页 Tab,实机同装验证过),可以一起装。 区别在于 agent 循环在谁家跑: | | dsh-remote | dsh-session-hub | | 远端需要什么 | 只要 sshd | 一个跑着的 dsh web | | 谁在干活 | 本机的 agent,通过 SSH 伸手过去 | 远端自己的 agent | | 会话存在哪 | 都在本机 | 各机器自己的 ~/.dsh | | 本机关机后 | 任务停了 | 远端继续跑 | | 别人在远端发起的会话 | 看不到(远端没有会话) | 树里直接出现 | 选型规则: 远端能装 dsh → 本插件(远端自己有会话,你去聚合)远端只有 sshd → dsh-remote(本机伸手过去操作) 客户的生产机、不允许装东西的跳板机,适合用 dsh-remote。而想要“关机之后任务继续跑”,则必须让远端自己有一个 dsh。 常见问题 | 症状 | 原因 / 处理 | | 添加服务器报 self-loop | baseUrl 指向 hub 自身。插件启动时也会自动检测并跳过自环条目(日志 warn) | | 历史加载失败 signal timed out | 最常见:SSH 隧道断了。检查 netstat -ano \| grep :3333;重启隧道后远端自动重连 | | 历史加载失败 invalid_value … expected "server-response" | 旧版本网关直通缺陷,升级到 0.1.0-alpha.1+(已修复:出口统一补 type: 'server-response') | | 会话列表少了一项 | 冷启动后远端首个 session.list 拉取未完成;打开会话本身会触发重拉 | | 实时流断开(LIVE 徽标变灰) | SSE 自动重连;发送后 900ms 无实时事件自动回退历史重载 | | 本机关了一阵子,远端会话现在怎么看 | 直接点开就行。远端还在跑就从当前这一刻续上实时流,缺席那段由历史补齐;已跑完则直接看到完整结果 | | 远端明明停了,UI 还在转圈 | 0.1.0-alpha.2 之前的缺陷:host/session-status 帧送错了入口被丢弃。升级即可;临时解法是刷新页面 | | 插件未生效 | 检查 dsh plugin 后是否重启 web;看启动日志有无 dsh-session-hub 加载与 gateway 使能信息 | | 点了「导入」但树里没有 | 该软件的会话所属项目目录已不存在,目录不在磁盘上的会话会被自动隐藏;恢复目录后一个扫描周期内回来 | | 导入的会话发不出消息 | 正常:导入会话只读。直接发送会自动转成真实 DSH 会话,之后照常对话 | | 某个软件显示「未安装」 | 只按默认路径查找(~/.codex / ~/.claude / ~/.local/share/opencode / ~/.pi);装在别处目前无法指定 | | 移除某个软件后别的也少了 | 0.1.0-alpha.1 之前的缺陷(跨源误删),升级即可 | 日志位置:dsh web 进程 stdout/stderr(systemd 部署看 journalctl -u dsh-web,nohup 部署看输出文件,本地终端部署看控制台)。 回滚:dsh plugin --profile web add dsh-session-hub@<上一版本> 并重启即可。注册表文件向后兼容(未知字段忽略),降级不会丢配置。 各版本的破坏性变更与降级注意事项见 CHANGELOG。 开发 git clone https://github.com/Asaiuta/dsh-session-hub && cd dsh-session-hub npm install # devDeps:esbuild / typescript / zod / react npm run typecheck # tsc -p tsconfig.json --noEmit npm run build # esbuild → lib/index.js + lib/client.js + lib/types/ - 类型检查走仓库自带 stubs/(按 harness 源码抄写的最小声明面,经 tsconfig paths 映射);对真实 DSH checkout 构建可删 paths/stubs、把 @deepseek-ai/ 引回 link: devDeps(参考 dsh-interconnect)。 - @deepseek-ai/dsh- 未发布到 npm,运行时由 profile 提供(peerDeps 因此全部 optional)。 - 测试:当前无自动化套件;冒烟路径(网关合并去重、SSE 三重鉴权、跨机实时对话、审批应答、self-loop 拒绝)为实机手动验证。欢迎贡献测试与 PR。 许可证与安全 - License:MIT。 - 安全边界见上方「它访问什么」;设计不变量:不中继特权域、审批一律人工应答(插件不做自动放行)、自环/未授权源一律拒绝。 - 私密报告:请通过 GitHub Issues 提交(标注 [security]),或直接联系维护者 @Asaiuta;修复前不会公开细节。 架构 ┌──────────────────────── 本地 DSH 进程 ────────────────────────┐ │ host 插件 (src/index.ts → hub/) │ │ │ │ ServerRegistry ──持久化── $DSH_HOME/plugins/dsh-session-hub.json│ │ │ 每个 ServerLink ── RemoteApiClient(AbstractApiClient) │ │ │ · HTTP unary → 远端 /api/session. │ │ │ · WS mux/host 双流 + 指数退避重连 │ │ │ · 会话列表缓存 / pending 交互表 (rpcId→服务器索引) │ │ ├── HubGateway(exact 路由优先于官方 /api prefix) │ │ │ 接管 session.list/history/prompt/cancel/rename/fork/ │ │ │ models/selectModel/updateQueue/attachment/search/respond│ │ │ + workspace.list/rename/delete/archiveSession │ │ │ 按会话归属三路分派:远端 → ServerLink、导入 → ImportStore│ │ │ 、本地 → 官方 ApiProxy │ │ │ session.list 合并去重(官方 + 远端 + 导入) │ │ ├── ImportStore(外部会话导入,按软件手动开启) │ │ │ 只读解析 codex/claude/opencode/pi 日志 → 规范会话模型 │ │ │ 文件监听 + mtime 增量扫描,缓存 dsh-session-hub-imports.json│ │ │ 按 cwd 最长前缀归入工作区;无对应工作区的项目自动登记 │ │ │ 首次发消息 → promote 成真实 DSH 会话(官方 create+replay)│ │ └── SessionHubRuntime (TypertRemoteService @Remote) │ │ 暴露 wire 命名空间 sessionHub(服务器管理/导入开关/同步)│ │ SSE /hub/events(随机 token + 环回 + same-origin 三重围栏) │ └──────────────┬────────────────────────────────────────────────┘ │ 官方 /api unary(浏览器→网关→路由) │ SSE 远端 mux 帧(原样转发) ┌──────────────▼────────────────────────────────────────────────┐ │ browser:官方 UI(零替换 / 零 shadow) │ │ · 官方工作区树:/api/session.list 由网关合并 → 远端会话直接 │ │ 出现在官方树;点击打开 │ │ · 官方对话区:远端会话 open() 走 /api/session.history 网关路由,│ │ 实时 mux 帧由 client 桥 (startOfficialBridge) 注入官方 │ │ sessions.handleMuxEnvelope → 官方逐 token 流式渲染/审批卡 │ │ · 设置 → 插件 → 会话枢纽:服务器增删/状态/探活、按软件导入、 │ │ 模型同步 │ └────────────────────────────────────────────────────────────────┘ 实时通道:每条远端链路的 mux/host WS 帧经 HubEventBus fan-out 到本地 SSE /hub/events;浏览器按 event.seq 与历史基线去重(打开会话先拉尾部历史,live 事件缓冲后按 seq > tailSeq 应用),assistant/chunk 增量折叠逐 token 气泡,审批/提问帧到达即上卡;SSE 断线自动重连。 导入会话的树变化(新增、移除、转正隐藏)由 hub 帧 watcher 以 host 帧经同一通道推给官方工作区运行时。 dsh-session-hub 你手上的每个会话,都在同一棵 DSH 树里。 远端 DSH 服务器上的会话,加上 Codex CLI、Claude Code、opencode 和 Pi 的本地历史,全部放进官方 Web UI。侧边栏和对话区都是官方的——插件只搬数据。两个功能彼此独立,你可以只开其中一个。 [!NOTE] 稳定版 0.1.0(更新日志)。核心路径已在真机上跑通——网关路由、官方 UI 桥接、实时帧注入、审批应答、跨机器对话。 目前只验证了一种环境(Windows 本地 + 腾讯云 Linux,走 SSH 隧道);配置格式和安全边界在小版本内仍可能变动。 这是什么 DSH 的会话树只认本机、本工具创建的会话。于是: 服务器上做到一半的活,得开新标签页、建隧道、翻日志。合上笔记本再回来, back——现在不知道跑哪去了。上周你和 Codex CLI 讨论过的方案,在 DSH 里根本不存在;你得去 ~/.codex 底下翻 JSONL 文件。 这个插件把两类会话放进同一棵树: Workspaces ├── my-project ← 本地工作区(官方,原样不动) ├── another-repo ← 本地工作区 ├── 🖥️ tencent ← 远程服务器分组;展开就能看到那台机器的会话 │ ├── deploy script debugging │ └── log analysis └── audio-engine ← 本地工作区,混入了导入的历史 ├── refactor discussion (Codex CLI) └── fixing that CI run (Claude Code) 打开任意会话——远程的也一样——官方对话面板照常渲染:历史、逐 token 流式输出、审批卡片、提问表单。全是官方组件。插件只搬数据,不画 UI。 远程会话不在本地代理。智能体循环跑在那台机器自己的 dsh 里;你的机器只是个仪表盘。合上笔记本去吃午饭;回来时如果会话还在跑,实时流从当前时刻接上,如果已经跑完,你看到的是完整结果。 两个独立功能 插件做两件事。两者互不依赖——你可以只开其中一个(见配置): 🖧 把远程机器接进来 每台服务器在工作区树里变成一个分组(标题带 🖥️ 标记,一眼就能看出是远程来源),里面是那台机器的会话。 - 树操作都是原生语义:+ 新建会话在那台机器上创建,归档 / 删除路由到所属机器,重命名分组就是重命名服务器,删除分组就是断开连接。 - 会话在远程跑,不在你的机器上:智能体循环在那台机器自己的 dsh 里推进。关掉你的机器、断网、换电脑,都不会让它停下。你回来时它可能已经跑完了。 - 中途加入:如果 hub 启动时远程正在生成,实时流从当前时刻接上,错过的历史通过 session.history 回填——和官方重连的语义一致。 - 对话面板零替换:远程会话的历史、实时流、审批与提问卡片全部由官方组件渲染。没有自绘聊天窗口,没有影子插槽。 - 你这边不用配隧道:填 user@host 和私钥路径,插件自己开 SSH 隧道、保活、重连,本地端口也自己分配。 - 模型配置增量同步:服务器连上时,本地有、远程缺的服务商、默认模型和 API key 会推过去。只补缺的,远程配置绝不覆盖。 📥 把其他工具的历史导进来 本地 Codex CLI / Claude Code / opencode / Pi 的对话,按目录落进对应的项目工作区。 - 按工具逐个启用,手动操作。你没点过 Import 的工具永远不会被读取——一个字节都不读。 - 没有匹配工作区的项目会自动注册为真实工作区(只注册,不创建目录);项目目录已不存在的会话会自动隐藏。 - 导入的会话在树里是只读的;给它发消息会自动升级为真实 DSH 会话(用户/助手轮次和工具调用都保留;只读原件隐藏)。 - 源日志以只读方式打开——绝不重写,绝不删除。 共同基础 - 纯 /api 协议:不执行 SSH 命令,不抓屏,不改远程配置。远程完全不需要插件——它就是未改动的 dsh web。 - 官方 UI 不动:不替换侧边栏,不替换对话面板。插件只在数据层做合并与路由。 只有一台机器? 第二个功能照样能用:不加服务器,只启用导入器,把本地 Codex / Claude Code / opencode / Pi 历史拉进来。 安装 dsh plugin --profile web add dsh-session-hub 之后重启 dsh web(kill -TERM <pid> 并等它退出——绝不要 kill -9,它可能在写入中途撕坏会话的 zstd 日志),然后刷新页面。成功的话,Settings → Plugins 下会出现 Session Hub 标签页。 dsh plugin 会把参数原样转发给配置档目录里的 pnpm(本机需要装 pnpm)。不带标签安装会拿到稳定的 latest;预发布版(如果有)需要显式写 dsh-session-hub@alpha。 跳过 npm:从 GitHub tarball 安装 dsh plugin --profile web add https://github.com/Asaiuta/dsh-session-hub/archive/refs/tags/v0.1.0.tar.gz 构建产物已提交,所以 tarball 安装不需要本地构建。 从源码安装(折腾 / 调试) git clone https://github.com/Asaiuta/dsh-session-hub && cd dsh-session-hub npm install && npm run build dsh plugin --profile web add file:$(pwd) 或者手动挂载:把 cordis.patch.yml 里的 insert 条目合并进配置档的补丁层。 升级 / 禁用 / 彻底移除 升级:重新跑一遍 add,再重启 dsh web: dsh plugin --profile web add dsh-session-hub 临时禁用:从配置档的 bundle 列表 / cordis.patch.yml 里删掉 dsh-session-hub 条目并重启。服务器注册表会保留;重新启用即可恢复。 彻底移除:删掉条目,重启,然后删除这两个文件(都在 $DSH_HOME/plugins/ 下):dsh-session-hub.json(服务器注册表)和 dsh-session-hub-imports.json(导入解析缓存)。插件从不写入你的项目目录,也从不修改任何工具的原始日志。 要求 | 项目 | 要求 | | 本地 DSH | 已针对 @deepseek-ai/dsh@0.1.0-rc.6 测试;主线不逐提交跟踪 | | Node | ^22.19 \|\| >=24(使用内置 WebSocket / fetch) | | 配置档 | 仅 web:插件会注入 webServer / apiProxy;装进 headless / tui 会一直挂起,并导致该配置档启动失败 | | 浏览器 | 官方 Web UI,无版本限制(插件不替换任何 UI) | | 外部工具 | 仅导入时需要:本地装好 Codex CLI / Claude Code / opencode / Pi 中任意一个,插件会读取它们的默认日志位置 | 最后验证于 2026-08-14:远端侧——本地 Windows + Node v24.9.0,通过 SSH 隧道连接远端 OpenCloudOS 9.4 + Node v24.9.0:跨机器对话、审批应答、实时流全部端到端跑通;导入侧——解析本地 615 个会话(Codex 523 / Claude Code 67 / opencode 25),归入 21 个工作区,只读 → 真实会话的升级流程已验证。 配置格式、路由表和 /hub/events 帧协议仍可能在小版本内变动。 快速开始 挑你要的功能——两条路径互不影响: - 只想导入本地 Codex / Claude Code / opencode / Pi 历史:跳到从其他工具导入会话,三步搞定,不用服务器,不用隧道。 - 要连接远程机器:从下面的最小示例开始。 连接一台远程机器 最小可复现示例:一台远程 + 本地 hub + SSH 隧道。 ① 远程(假设是 10.0.0.5):什么都不用装,保持默认即可: dsh web --port 3080 # listens on loopback only — the only binding currently allowed ② 本地:只装插件: dsh plugin --profile web add https://github.com/Asaiuta/dsh-session-hub/archive/refs/tags/v0.1.0.tar.gz restart dsh web 不用手动开隧道——接着填好 SSH 信息,插件会自己建立隧道并保持连接。 ③ 浏览器打开 http://127.0.0.1:3080: 1. Settings → Plugins → Session Hub → Add server,保持默认的 SSH tunnel 模式,填写: | 字段 | 示例 | | Name | tencent | | Host | 10.0.0.5 | | SSH user | root | | Private key path | ~/.ssh/id_ed25519(留空则使用 ssh agent) | | Remote dsh port | 3080 | 点 Test(能连通就返回远端 DSH 版本)→ Add。本地端口由插件自行分配,你无需知道。 2. 官方工作区树里会出现名为 tencent 的分组,远程会话就在里面; 3. 打开任意会话——官方对话面板直接可用:历史、实时流、审批卡片、发送 / 取消 / 重命名。 如果你已经手动跑着 ssh -L,切到 Direct address,填 http://127.0.0.1:<你的端口>;插件不会碰那条隧道。 为什么必须用隧道:当前 dsh(0.1.0-rc.6)拒绝把 Web 服务器绑定到 loopback 之外。 --host 0.0.0.0 会被命令行拦截(“would expose remote code execution to the network”),而具体的局域网 IP 又过不了配置校验(host 只接受 127.0.0.1 和 0.0.0.0 这两个字面量)。 所以远程端保持默认配置,由隧道把它带到本地回环地址——这也意味着没有选项能把 :3080 公开出去;上游已经把这扇门关上了。 隧道进程跑在 dsh 内部:dsh 退出它就关闭,启动时从保存的配置重建(端口每次都会重新分配,所以配置里存的是 SSH 目标,不是 URL)。SSH 断开后按退避策略重连,链路跟着新端口走。 从其他工具导入会话 不需要服务器,也不需要隧道。打开 Settings → Plugins → Session Hub → External sessions,按工具逐个点 Import: | 工具 | 读取位置 | | Codex CLI | ~/.codex/sessions//rollout-.jsonl | | Claude Code | ~/.claude/projects//.jsonl | | opencode | ~/.local/share/opencode/opencode.db | | Pi Coding Agent | ~/.pi/agent/sessions//.jsonl | - 没点过 Import 的工具永远不会被读取——一个字节都不读; - 勾选 Auto:每 60 秒周期扫描一次,导入时再做增量重新解析,新写入的日志行会被收进来;不勾选则只在你点刷新时更新; - 导入的会话在树里是只读的,工具调用会渲染成真实卡片(参数、结果、错误标记),按源日志顺序排列。调用 id、参数和输出全部来自原始记录——没有任何合成内容; - Remove:把该工具的会话从树里移出,不影响其他工具,原始日志原封不动; - 导入的会话是只读的;给它发消息会自动升级成真正的 DSH 会话(保留 user/assistant 轮次和工具调用,只读的原件隐藏)。 配置 四个功能开关,默认全开;用不到的关掉: imports only, no multi-server (typical single-machine setup) - id: dsh-session-hub config: features: aggregate: false tunnel: false modelSync: false multi-server only, never touch local tool logs - id: dsh-session-hub config: features: importer: false 禁用就是彻底不存在,不是闲置:禁用的功能不构建服务、不读缓存、不扫目录、不注册路由,也不出现在设置页。四个全关,插件等同于没装(不拦截任何 /api)。 | 键 | 类型 | 默认值 | 含义 | | dataFile | string? | $DSH_HOME/plugins/dsh-session-hub.json | 服务器注册表的持久化位置 | | trustedHosts | string[]? | 仅回环 | 被拦截的 /api 的额外允许列表(裸 host[:port],格式同 client-connection.trustedHosts;SSH 隧道部署不需要) | | features.aggregate | boolean | true | 多服务器聚合:关闭后不再拦截 /api,不生成虚拟分组,设置里也不再显示服务器卡片 | | features.tunnel | boolean | true | SSH 隧道托管:关闭后添加表单只提供直连地址;已保存的 SSH 条目会被跳过,并记一行日志 | | features.modelSync | boolean | true | 模型配置同步:关闭后不读取本地凭据,也不推送任何内容;同步卡片消失 | | features.importer | boolean | true | external session import: off → no parse cache reads, no log directory scans; the import card disappears (cache file is kept; re-enabling restores it) | Configure in the profile's cordis.yml / cordis.patch.yml: - id: dsh-session-hub config: dataFile: /srv/dsh-hub-servers.json trustedHosts: ['192.168.1.10:3080'] The only relevant environment variable is DSH_HOME (affects the registry default path, ~/.dsh by default). The plugin needs no API-key or token environment variables. The runtime /hub/events random token is generated once per process and delivered to the browser only through the snapshot; it never touches disk. What it accesses It reads your local session logs (per tool, as you authorize), talks to the servers you configure, and never touches your project files. Every item below happens only while its feature is enabled: with the importer off no logs are read; with aggregation off no outbound connections are made. Full list (files / network / credentials / session content) File access: - Reads/writes $DSH_HOME/plugins/dsh-session-hub.json (0600, atomic: tmp + rename); - Reads/writes $DSH_HOME/plugins/dsh-session-hub-imports.json (imported-session parse cache, 0600); - Read-only scans of local session logs: ~/.codex/sessions//rollout-.jsonl, ~/.claude/projects//.jsonl, ~/.local/share/opencode/opencode.db (SQLite opened read-only), ~/.pi/agent/sessions//.jsonl, to build read-only* imported session views; never written back. Enabled per tool, manually: a tool you have not clicked Import for is never read. Network: - Outbound (per configured server baseUrl): HTTP POST /api/ (unary RPC) + WebSocket upgrade /api/events.mux, /api/events.host; - Inbound (local, in-process): /hub/events SSE (loopback host + random token + browser same-origin, a three-way fence), Typert /api/sessionHub/ (loopback); - Inbound (browser): intercepted /api/session. and /api/respond are re-checked by the gateway against loopback / trustedHosts. SSH keys: tunnel entries read the private key at the path you provide (only to establish that tunnel); key material is never persisted, never sent anywhere, never written into the plugin config — the config stores the path only. Left empty, the ssh agent is used and the plugin never touches key material. Credentials: model sync reads the local $DSH_HOME/.credentials.yaml plaintext (only to resolve secret values referenced via apiKeyEnv in the llm- namespaces) and writes them via credentials.set to the remote only when that reference is not yet configured there (the only outbound direction for secrets; encrypted under tunnel/HTTPS). Secrets never ride response traffic back, and the hub keeps no copy of them. To avoid any automatic secret push, turn off model sync for the server (or drop its llm- namespaces). User data: remote session lists, history content and live streams transit the hub process to the browser. Cross-machine transport runs over the SSH tunnel (encrypted — currently the only connection mode); the plugin never writes session content to disk in cleartext. Coexistence with other plugins The ecosystem already has 1800+ dsh plugins and many touch the frontend. This one changes the data layer, not the presentation layer: | Layer | Who changes it | Conflict? | | Session/workspace data | this plugin (gateway merging session.list / workspace.list) | no conflict with UI plugins | | Sidebar/conversation presentation | assorted UI plugins (trees, themes, tabs) | this plugin occupies none | On the browser side the plugin does exactly one thing: adds a id: 'session-hub' entry to the settings.plugins.tab list slot. It shadows no sidebar.workspaces, no conversation, no single slot — so sidebar, conversation and theme plugins all coexist with it. Tree-replacing plugins actually show remote and imported sessions for free: they read through the official useSessions / useWorkspaces hooks, whose upstream is precisely this plugin's merged /api. For example, dsh-plugin-ya-workspace-sidebar fully replaces sidebar.workspaces, and this plugin's server groups and imported sessions still appear in the tree it draws — neither knows about the other. The one conflict: plugins that also intercept /api DSH's web server allows exactly one registrant per exact route (duplicate registration throws on purpose — a deliberate composition rule). To merge sessions this plugin takes over these routes: /api/session.list /api/session.history /api/session.prompt /api/session.cancel /api/session.rename /api/session.fork /api/session.models /api/session.selectModel /api/session.updateQueue /api/session.attachment /api/session.search /api/session.create /api/workspace.list /api/workspace.rename /api/workspace.delete /api/workspace.archiveSession /api/respond If another plugin intercepts any of them, this plugin yields the occupied routes and degrades; dsh and the other plugin keep working: [dsh-session-hub] gateway DISABLED — webserver: duplicate exact route "/api/respond". Another plugin intercepts the same route, so remote servers and imported sessions will not appear. Set features.aggregate and features.importer to false to silence this, or remove the conflicting plugin. Remote sessions and imported sessions then do not appear (both features depend on those routes), but DSH itself, the other plugin, and this plugin's settings page all stay healthy. Set features.aggregate and features.importer to false to silence the warning. This path was exercised on a real machine: with a test plugin hogging /api/respond, the dsh homepage still returned 200, the official session.list answered normally, the sessionHub endpoint stayed alive — a route collision cannot take the host down. The vast majority of plugins never go this route: tools, skills, UI, tabs do not need /api interception. Typert namespace: this plugin owns sessionHub (= /api/sessionHub/) exclusively; SSE owns /hub/events. A namespace collision is extremely unlikely. Boundary with dsh-remote dsh-remote also does "remote", but a different thing. The two are technically non-conflicting (it occupies /dsh-remote/ and the workspace selector slot; we occupy /api/ and a settings tab — verified side-by-side on a real machine), and can be installed together. The difference is whose machine runs the agent loop: | | dsh-remote | dsh-session-hub | | What the remote needs | just sshd | a running dsh web | | Who does the work | your agent, reaching over SSH | the remote's own agent | | Where sessions live | all on your machine | in each machine's own ~/.dsh | | Your machine off | work stops | the remote keeps going | | Sessions started by others remotely | not visible (the remote has no sessions) | appear directly in the tree | Decision rule: Remote can run dsh → this plugin (the remote owns its sessions; you aggregate them) Remote has only sshd → dsh-remote (reach over from your machine) For production boxes you cannot install on, or jump hosts, dsh-remote fits. To have work continue after you shut down, the remote must run its own dsh. FAQ | Symptom | Cause / fix | | Add server fails with self-loop | baseUrl points at the hub itself. The plugin also auto-detects and skips self-loop entries at startup (log warn) | | History load fails signal timed out | most common: the SSH tunnel dropped. Check netstat -ano \| grep :3333; the remote reconnects after the tunnel restarts | | History load fails invalid_value … expected "server-response" | a gateway passthrough bug in old versions; upgrade to 0.1.0-alpha.1+ (fixed: the exit uniformly stamps type: 'server-response') | | A session is missing from the list | on cold start the remote's first session.list pull may not have finished; opening the session itself triggers a re-pull | | Live stream dropped (LIVE badge went grey) | SSE auto-reconnects; if no live event arrives within 900ms after sending, it falls back to a history reload | | How do I see a remote session after my machine was off for a while | just open it. Still running: the live stream resumes from the current moment, the gap is backfilled from history. Already done: you see the full result | | Remote clearly stopped, UI still spins | defect before 0.1.0-alpha.2: the host/session-status frame was fed to the wrong entry and dropped. Upgrade; temp workaround is a page refresh | | Plugin not effective | check whether dsh web was restarted after dsh plugin; look for dsh-session-hub load and gateway-enabled lines in the startup log | | Clicked Import but nothing in the tree | sessions of that tool whose project directory no longer exists on disk are hidden automatically; restore the directory and they return within one scan cycle | | Imported session cannot send messages | expected: imported sessions are read-only. Sending directly promotes to a real DSH session, then it converses normally | | A tool shows "not installed" | only default paths are probed (~/.codex / ~/.claude / ~/.local/share/opencode / ~/.pi); custom locations cannot be specified yet | | Removing one tool took others with it | defect before 0.1.0-alpha.1 (cross-source deletion); upgrade | Logs: dsh web process stdout/stderr (systemd: journalctl -u dsh-web; nohup: the output file; local terminal: the console). Rollback: dsh plugin --profile web add dsh-session-hub@<previous version> and restart. The registry file is backwards compatible (unknown fields are ignored); downgrading loses no config. Breaking changes and downgrade notes per version are in CHANGELOG. Development git clone https://github.com/Asaiuta/dsh-session-hub && cd dsh-session-hub npm install # devDeps: esbuild / typescript / zod / react npm run typecheck # tsc -p tsconfig.json --noEmit npm run build # esbuild → lib/index.js + lib/client.js + lib/types/ - Type checking runs against the repo's own stubs/ (a minimal declaration surface transcribed from the harness sources, mapped via tsconfig paths); building against a real DSH checkout means dropping paths/stubs and pointing @deepseek-ai/ back to link: devDeps (see dsh-interconnect). - @deepseek-ai/dsh- are not published to npm; the runtime profile provides them, which is why every peerDep is optional. - Tests: no automated suite yet; the smoke paths (gateway merge dedupe, SSE three-way auth, real-time cross-machine dialogue, approval answering, self-loop rejection) are verified manually on real machines. Test and PR contributions welcome. Architecture ┌──────────────────────── local DSH process ────────────────────────┐ │ host plugin (src/index.ts → hub/) │ │ │ │ ServerRegistry ──persists── $DSH_HOME/plugins/dsh-session-hub.json │ │ │ one ServerLink per remote ── RemoteApiClient(AbstractApiClient)│ │ │ · HTTP unary → remote /api/session.* │ │ │ · WS mux/host dual stream + exponential-backoff reconnect │ │ │ · session list cache / pending interaction table (rpcId→server)│ │ ├── HubGateway(exact routes take priority over official /api) │ │ │ takes over session.list/history/prompt/cancel/rename/fork/ │ │ │ models/selectModel/updateQueue/attachment/search/respond │ │ │ + workspace.list/rename/delete/archiveSession │ │ │ three-way dispatch by session ownership: remote → ServerLink,│ │ │ imported → ImportStore, local → official ApiProxy │ │ │ session.list merged & deduped (official + remote + imported) │ │ ├── ImportStore(external session import, enabled per tool) │ │ │ read-only parse of codex/claude/opencode/pi logs → canon │ │ │ mtime incremental scan, cache dsh-session-hub-imports.json │ │ │ grouped into workspaces by longest cwd prefix; projects │ │ │ without a workspace auto-register; first prompt → promote │ │ │ to a real DSH session (official create+replay) │ │ └── SessionHubRuntime (TypertRemoteService @Remote) │ │ exposes the sessionHub wire namespace (servers/import/sync) │ │ SSE /hub/events (random token + loopback + same-origin three-way) │ └──────────────┬─────────────────────────────────────────────────────┘ │ official /api unary (browser → gateway → route) │ SSE remote mux frames (forwarded verbatim) ┌──────────────▼─────────────────────────────────────────────────────┐ │ browser: official UI (zero replacement / zero shadow) │ │ · official workspace tree: /api/session.list merged by the gateway │ │ → remote sessions appear directly; click to open │ │ · official conversation pane: remote session open() routes via │ │ /api/session.history; live mux frames are injected into the │ │ official sessions.handleMuxEnvelope by the client bridge │ │ (startOfficialBridge) → official token-by-token stream/approval │ │ · Settings → Plugins → Session Hub: servers add/remove/status, │ │ per-tool import, model sync │ └─────────────────────────────────────────────────────────────────────┘ Live channel: every remote link's mux/host WS frames fan out through HubEventBus to the local SSE /hub/events; the browser dedupes by event.seq against the history baseline (opening a session pulls the tail history first; live events buffer and apply when seq > tailSeq), assistant/chunk deltas fold into per-token bubbles, approval/question frames surface as cards on arrival; SSE auto-reconnects on drop. License & security - License: MIT. - Security boundary: see "What it accesses" above. Design invariants: no privileged-domain relay, approvals are always answered by a human (the plugin never auto-approves), and self-loop / unauthorized sources are always rejected. - Private reports: please file via GitHub Issues (prefix [security]), or contact the maintainer @Asaiuta; details stay private until fixed.
预览分享卡片长什么样 ↓
分享 · DeepSeek Harness Hub
Asaiuta/dsh-session-hub
把散在各处的会话收进 DSH 的同一棵树。
相关插件 · 市场 / 管理
插件精选列表awesome-dsh-plugin/awesome-dsh-plugin
DeepSeek Harness(dsh)插件精选列表
★ 22,225未汉化DSH
73.6
新词雷达zhuyansen/new-term-radar
new-term-radar新词雷达
★ 2,049已汉化MCP
72.8
插件生态导航0xsline/awesome-deepseek-harness
安装 贡献指南 DeepSeek Docs 公开插件目录 Issues
★ 1,188已汉化DSH
72.6
技能注册中心iflytek/skillhub
企业级开源智能体技能注册中心——发布、发现并管理组织内可复用的技能包。
★ 5,370已汉化DSH
71.9
插件市场目录imsai-sh/awesome-deepseek-harness-plugins
面向 DeepSeek Harnessdsh生态的社区插件目录,共收录 13941 个插件含 PR 收录与…
★ 355已汉化DSH
71.8
插件精选清单Anil-matcha/awesome-dsh-plugin
DeepSeek Harnessdsh精选指南——DeepSeek…
★ 1,013已汉化DSH
71.7
这是你的仓库?认领条目