npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

dsh-remote-fleet

v0.5.2

Published

Drive remote DeepSeek Harness / Codex agents over ACP: machine registry, workstream routing, escalation policy, direct chat.

Readme

dsh-remote-fleet

让主机 DSH 通过 SSH + ACP 给远程机器上的 DSH 派活、续会话、处理审批。 一个插件,十个工具,没有额外的 HTTP 服务、没有端口、没有证书。

主机 DSH(调度)
   │  SSH(传输)
   │  ACP(会话:session/new · resume · prompt · permission)
   ▼
pc4090 上的 `dsh --profile acp`

它做什么

  • 机器注册:记住每台机器的 host、用户、默认目录、标签。
  • 委托:fleet_delegate 立即返回 handle,后台跑;fleet_wait 跟进。
  • 会话路由:优先用工作流绑定,其次找同目录下最近的、标题相关的会话,最后才新建。
  • 上下文胶囊:每次委托都带任务、约束、上文,即使路由错了也能把任务拉回来。
  • 升级策略:不可逆操作(删库、force push、动 schema、碰密钥…)一律停下来问人,其余自动放行。
  • 可审计:返回里带 mode、route_reason、candidates,走错了能一眼看出来。

它不做什么

  • 不接 Codex(v2 加适配器;本轮只做 ACP)。
  • 不做客户端 UI(工具 + 对话已经能跑通)。
  • 不做多用户权限(假设单人多机)。
  • 不管远程 DSH 的安装和登录(前置条件,见下)。

前置条件

主机(跑 DSH 的那台)

  • DSH 能加载第三方 bundle(dsh plugin 可用)。
  • ssh 可用,能免密登录到目标机器。

远程机器(每台)

  1. 装了 DSH,且 dsh --profile acp 能启动:

    ssh pc4090 'cd ~/work/app && dsh --profile acp'
    # 什么都不打印、进程挂着 = 正常:stdout 只走 ACP 协议
  2. SSH key 已配置(BatchMode=yes 能登录)。

  3. 工作目录存在且是绝对路径。

  4. 建议用 Tailscale 组网,主机和远程都在同一个 tailnet。


接入机器的三种形态

插件只认一件事:那台机器上有一条命令能说 ACP。装的是 DSH 还是 Codex,对插件是透明的。

| 形态 | 装什么 | command | |---|---|---| | DSH | 桌面 App 或 CLI(自带 ACP server) | dsh --profile acp | | Codex | @agentclientprotocol/codex-acp(Codex 的 ACP 适配器) | codex-acp | | WSL 里的 DSH / Codex | 在发行版里装上面任意一种 | 同左,但 SSH 要进到发行版里 |

Codex 机器

Codex 自己没有 ACP server,需要一个适配器。官方维护的是 @agentclientprotocol/codex-acp(由 Codex App Server 驱动),另有一个 Go 单文件的 codex-acp(要求 Codex CLI 在 PATH)。

# 在 Codex 那台机器上
npm install -g @agentclientprotocol/codex-acp
codex login                 # 已有登录状态就跳过
which codex-acp             # 记下绝对路径

# 自检:挂住、不打印任何东西 = 正常
codex-acp

注册:

{
  "id": "pc-codex",
  "host": "pc4090",
  "kind": "codex",
  "cwd": "/home/luis/work/app",
  "command": "/home/luis/.npm-global/bin/codex-acp"
}

对话里说「把 pc4090 加进来,用 codex,命令是 …」即可。

它会复用 ~/.codex 的登录态和会话(rollout),所以 fleet_sessions 能看到你之前在 Codex 里聊过的会话,session/resume 也能接上。工具审批会走 ACP 的 session/request_permission,和 DSH 机器完全同一条升级策略。

两个版本差异:官方包自带 @openai/codex 依赖,装完体积大但省事;Go 版要求你机器上已经有 Codex CLI,并且用 codex-acp login 登录。

WSL2 机器

推荐顺序:

A. 直接 SSH 进发行版(最干净)

在 WSL 里跑自己的 sshd,端口 2222,主机侧做端口转发(或 Win11 的 mirrored 网络):

# WSL 内
sudo apt install openssh-server
sudo sed -i 's/^#\?Port .*/Port 2222/' /etc/ssh/sshd_config
sudo service ssh restart

主 DSH 上:

ssh -p 2222 luis@wsl-host 'cd /home/luis/work/app && codex-acp'

注册时 host: "wsl-host", port: 2222, cwd: "/home/luis/work/app"。对插件来说这就是一台普通 Linux 机器,没有任何特殊处理。

B. Windows OpenSSH + 默认 shell 改成 wsl.exe

New-ItemProperty -Path "HKLM:\SOFTWARE\OpenSSH" -Name DefaultShell `
  -Value "C:\Windows\System32\wsl.exe" -PropertyType String -Force

之后 ssh winbox 直接落在发行版里,cd /home/... && codex-acp 正常工作。

C. Windows OpenSSH + 包装命令(不改默认 shell)

Windows 的 cmd 不认 POSIX 的 cd '...',所以让命令自己 cd,并关掉前缀:

{
  "id": "win-wsl",
  "host": "winbox",
  "kind": "codex",
  "cwd": "/home/luis/work/app",
  "cwd_prefix": false,
  "command": "wsl.exe -d Ubuntu -- bash -lc \"cd /home/luis/work/app && exec codex-acp\""
}

cwd_prefix: false 时插件只发 exec <command>,cd 由你负责。

WSL 的四个坑

  1. 空闲会停:WSL 没有前台进程会被回收,SSH 直接连不上。要么在发行版里挂个常驻进程,要么接受冷启动失败后重试。
  2. 别用 /mnt/c:跨文件系统 IO 慢十倍、权限语义也不对。工作目录放 /home/<user>/work。
  3. Tailscale 要装在发行版里:装 Windows 侧的话,宿主机进不来(除非 mirrored 网络)。
  4. 登录态独立:WSL 里的 codex login 和 Windows 侧的 ~/.codex 不是一份。

Windows 原生机器

Windows 上也能当 worker(DSH 有 Windows 构建),但同样要 cwd_prefix: false,命令用 PowerShell 包一层:

{
  "command": "powershell -NoProfile -Command \"Set-Location C:\\work\\app; codex-acp\"",
  "cwd_prefix": false
}

安装

dsh plugin --profile web add /path/to/plugins/dsh-remote-fleet

dsh plugin 会初始化 profile(首次)、把包 link 进去,并把它加进 dsh.profile.bundles。

验证:

dsh --profile web --dump-config | grep -A2 remote-fleet
# # == dsh-remote-fleet
# - id: remote-fleet
#   name: dsh-remote-fleet

重启 DSH,模型就能看到 fleet_* 工具。

本地开发时 add 一个目录会生成 link: 依赖,改代码后重启 DSH 即可生效。


用起来是什么样

你:把 pc4090 加进来,用户 luis,默认目录 /home/luis/work/app

DSH:好,加好了。连一下试试?
     → fleet_add_machine {id:"pc4090", host:"pc4090", user:"luis", cwd:"/home/luis/work/app"}
     → fleet_health {machine:"pc4090"}

你:去 4090 上把登录重构收尾,补个 e2e

DSH:→ fleet_delegate {machine:"pc4090", task:"收尾登录重构,补 e2e", workstream:"login-refactor"}
     ← {delegate_id:"dl-3f2a", status:"queued"}
     → fleet_wait {delegate_id:"dl-3f2a", timeout_ms:60000}
     ...

远程 agent 要删东西时,fleet_wait 会返回:

{
  "status": "needs_input",
  "question": {
    "kind": "permission",
    "risk": "irreversible",
    "reason": "recursive delete",
    "title": "rm -rf build",
    "options": [{ "optionId": "allow-once", "kind": "allow_once" }, ...]
  }
}

主机 DSH 把这个问题转给你,你回一句“允许”,它调:

{ "delegate_id": "dl-3f2a", "decision": "allow" }

原生面板「远程机器」

装好之后,DSH 侧边栏会出现一个 远程机器 面板(客户端插件,dsh.client), 它直接长在 DSH 的界面里,不是外挂网页:

  • 机器列表:连接状态、类型、主机、工作目录
  • 对话:点机器卡片上的「对话」进入——新会话 / 历史会话 / 直接发消息 / 停止
  • 授权卡片:远程 agent 要跑危险命令时,面板里直接出「允许 / 拒绝」
  • 添加机器:SSH → 探测 → 目录选择 → 保存,四步向导
  • 删除机器、断开连接、重新检测都在卡片上

对话走的是同一套 ACP:session/new 或 session/load(回放历史), 每次发送就是一次 session/prompt,session/update 的增量文本按 800ms 轮询回显。 和 fleet_delegate 用的是同一个连接,互不干扰。

面板设置与状态在 ~/.dsh/remote-fleet/state.json。

对话里的工具卡片

fleet_* 工具在主机对话里不再显示成一坨 JSON,而是渲染成一张卡片:

◆ 远程 Agent   [商品组]   [完成]                    112 步 · 152.3s
介绍你这个工作区(D:\商品组)是干什么的…
会话 4149492f · new
─────────────────────────────────────────
结果:这是商品组围绕灵云智本体平台做航空客户工作台/数字员工的…

实现分两层:

  • host:每个工具声明 output.render(紧凑文本)和 output.presentationMeta (给卡片的结构化字段:agent、状态、会话、步数、耗时、待决定的问题)。
  • client:按工具名注册 tool.call.toolview 插槽:
ctx.slots.register({ name: "tool.call.toolview", key: "fleet_delegate" }, FleetToolCard)

卡片读取 block.meta(就是 host 的 presentationMeta)、block.argsRaw、 block.content 和 block.isError,所以运行中 / 完成 / 失败三种状态都能画。

独立页面(备用)

同样一套能力也注册在 DSH 的 Web 服务上,浏览器可以直接开:

<DSH 的 origin>/remote-fleet/

能力:

  • 机器卡片列表:连接状态、类型、主机、工作目录、标签
  • 每台机器:连接 / 重新检测 / 看会话 / 断开 / 删除
  • 添加向导四步:填 SSH → 探测环境 → 选目录 → 保存并连接
  • 探测会告诉你目标机器缺什么;缺 codex-acp 可以在页面上后台安装并看日志
  • 目录浏览器直接列目标机器的文件系统(走 SSH,不需要额外服务)

让模型给你链接:fleet_ui。

鉴权沿用 DSH 的 cookie。如果打开是 401,先在同一个浏览器里打开一次 DSH 启动时打印的 带 ?token= 的地址,再访问 /remote-fleet/。

工具

| 工具 | 作用 | |---|---| | fleet_probe | 只用 SSH 探测:系统、node、codex-acp / dsh 是否存在、Codex 是否登录 | | fleet_browse | 列目标机器的目录,用于选工作区 | | fleet_bootstrap | 在目标机器后台安装 codex-acp(立即返回,轮询状态) | | fleet_bootstrap_status | 看安装进度与日志 | | fleet_ui | 返回远程机器独立页面地址 | | fleet_add_machine | 注册机器(id / host / user / port / command / cwd / tags) | | fleet_list_machines | 列出机器、连接状态、agent 信息 | | fleet_remove_machine | 移除机器及其工作流绑定 | | fleet_health | 建连 + ACP 握手,报告 agent 与能力 | | fleet_sessions | 列出可续会话(ACP session/list) | | fleet_delegate | 派活:立即返回 delegate_id | | fleet_wait | 等待完成或需要输入(默认 60s,上限 120s) | | fleet_status | 不等待,直接读快照 | | fleet_answer | 回答 permission(allow/reject)或 question(text) | | fleet_cancel | 取消(发 session/cancel) |


路由与升级

会话路由(三层)

  1. 绑定:workstream → 远程 session id(写在 state.json 里,确定性)
  2. 匹配:同 cwd + 7 天内活跃 + 标题相似度达阈值
  3. 新建:都不满足

不传 workstream 时,插件会用「同机器 + 同 cwd + 标题相似」去找已有工作流;找不到就新建一个。 返回里的 route_reason 和 candidates 是给你纠偏用的:

{
  "mode": "binding",
  "route_reason": "workstream is bound to this session",
  "candidates": []
}

升级策略

默认规则(src/policy.js):

  • 只读 → 直接允许
  • 工作区内编辑/命令 → 直接允许(autoEdit: true)
  • 工作区外 → 升级
  • 不可逆 → 一律升级:rm -rf、git push --force、git reset --hard、删分支、drop/truncate、sudo、curl | sh、deploy、migration、密钥、.env

配置方式(优先级从低到高):

# 环境变量:JSON
export DSH_FLEET_POLICY='{"autoEdit":false}'
export DSH_FLEET_DEFAULT_CWD='/home/luis/work'
export DSH_FLEET_STATE='/var/lib/dsh/fleet.json'
export DSH_FLEET_SSH='/usr/bin/ssh'

或在 bundle 行上配 config(profile patch 同 id 覆盖):

- id: remote-fleet
  config:
    policy:
      autoEdit: false
    defaultCwd: /home/luis/work

状态文件

$DSH_HOME/remote-fleet/state.json(可用 DSH_FLEET_STATE 覆盖),权限 0600:

{
  "version": 1,
  "machines": {
    "pc4090": { "id": "pc4090", "host": "pc4090", "user": "luis", "cwd": "/home/luis/work/app", "command": "dsh --profile acp" }
  },
  "workstreams": {
    "login-refactor": { "machine": "pc4090", "sessionId": "session-1f2e...", "cwd": "/home/luis/work/app", "title": "收尾登录重构", "updatedAt": "2026-10-31T01:20:00.000Z" }
  }
}

删掉它不会损坏任何远程会话,只会丢掉「工作流 → 会话」的绑定。


目录结构

src/
  index.js            # cordis 插件入口(name / inject / apply)
  tools.js            # 10 个模型侧工具定义
  fleet.js            # 机器注册表、连接池、委托编排
  delegate.js         # 一次委托的状态机(routing → running → needs_input → done)
  router.js           # 会话选择:绑定 / 相似度 / 新建
  policy.js           # 升级规则
  capsule.js          # 上下文胶囊
  store.js            # JSON 持久化(原子写)
  acp/client.js       # ACP v1 客户端(JSON-RPC over stdio)
  acp/transport.js    # SSH / 本地进程传输
  util/ndjson.js      # 换行分隔 JSON 编解码
test/
  mock-agent.js       # 最小 ACP agent(测试用)
  *.test.js           # 28 个用例

测试

npm test

覆盖:ACP 握手、prompt 流、permission 往返、会话列表、路由三层、策略分级、委托全流程(新建 / 绑定续接 / 提问 / 审批允许 / 审批拒绝)、插件工具注册形状。

测试用 test/mock-agent.js 当远程 agent,走真实子进程和真实 NDJSON 编解码,只有 SSH 被换成本地进程。


已知限制

  1. 同一会话没有强制互斥:两个委托同时命中同一个 session 会并发写。v1 加按 session 的队列。
  2. 提问检测是启发式的:远程以 ?/? 结尾的消息会被判为 needs_input。误判时用 fleet_answer 回一句即可。
  3. ACP elicitation 未处理:DSH 的 ACP server 目前把问题当普通消息发出,所以走的是「回合结束 + 问号」这条路。
  4. 不支持 resume 的 agent 会退化成新会话,并在 recent_events 里记一条 warning。
  5. 客户端 UI 未做:所有操作走对话和工具。
  6. Codex 未接:需要另一个适配器(codex exec resume + 事件流)。

安全

  • 插件在 DSH 进程内运行,不受 workspace 沙箱限制;安装即授权。
  • SSH 能做的事 = 这台机器能做的事,用专用 key、最小权限用户。
  • 默认策略宁可多问:不可逆操作永不自动放行。
  • state.json 只存主机名和工作流绑定,不存密钥。

设计文档