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

@tetherline/agent

v0.3.2

Published

牵星 Tetherline dsh bundle:一条命令把 router / backend-dsh / 渠道 / 网页配置界面装进 dsh profile

Readme

@tetherline/agent —— 牵星的 dsh 入口包

一行装完牵星:router + backend-dsh + 网页配置界面 + 移动端网页,一次装进 dsh profile。

dsh plugin --profile web add @tetherline/agent   # 一行装完
dsh web                                          # 设置 → 牵星 → 填配置 → 拨开关 → 保存

IM 渠道不在这一层(open-items 第 18 条)。要飞书就再装一次,装完重启 dsh:

dsh plugin --profile web add @tetherline/channel-feishu --allow-build=protobufjs

不用记这条命令——设置页上没装的渠道会显示成一张「未安装」卡片,上面就是它,带复制按钮。 装好飞书并拨开开关,就得到 PRD 的 M1 能力——手机飞书里下发任务 → 流式观察 → 按钮审批 → 收到完成通知,全程不需要公网服务器。

这是仓库里唯一允许同时引用多个 packages 的地方(CONTRIBUTING §1)。 形态与设计见 ../../docs/web-config.md。

2026-09-05 起同一条安装命令也带上了移动端网页:@tetherline/pwa 作为默认未启用的 宿主插件进了本 bundle,设置页因此多一张「mobile-web」卡片。启用它还需要一台公开网关, 而网关侧(登录、反代)属 WS-12D,尚未落地——见下面第 6 节。边界与 tarball 安全门禁见 ../../docs/mobile-web.md。

它由什么组成

| 文件 | 作用 | | --- | --- | | package.json | dsh bundle manifest(dsh.bundle.patch)+ 四个包的 workspace:^ 依赖(发布时被替换成 ^版本号) | | cordis.patch.yml | bundle 层:装哪四个插件、按什么顺序,以及还能装哪些渠道的目录。装完即可启动——会对外连接的那一项默认未启用,没有任何必须先手工替换的占位符 | | scripts/install.sh | 构建 workspace → dsh plugin add → --dump-config 自检(只服务本地 checkout 开发) | | test/patch.test.ts | 装配层的结构测试:依赖表 ↔ patch 行必须是同一份清单(见下) | | test/e2e.test.ts | keyless e2e:录制的飞书事件回放进真实装配,跑完整闭环 | | test/fixtures/session.ndjson | 四条录制事件(全部是 test-only 假值,无任何真实凭证或用户数据) |

patch 层装的五行(顺序即 ../../docs/mobile-web.md §3.3):

| id | 包 | 作用 | | --- | --- | --- | | tetherline-router | @tetherline/router | 会话路由 / 出站编排 / 审批,提供 tetherline 服务 | | tetherline-backend-dsh | @tetherline/backend-dsh | 把任务交给 dsh 的 agent 执行 | | tetherline-ui | @tetherline/ui-dsh | 网页配置卡片 | | tetherline-mobile-web | @tetherline/pwa | 移动端网页 + frpc 隧道,默认未启用(第 6 节) | | tetherline-channel-feishu | @tetherline/channel-feishu | 飞书渠道,默认未启用 |

这两份清单——package.json 的 dependencies 与上面这张表——很容易各写各的:@tetherline/pwa 的宿主插件写完、发布形态也做好了,却因为两边都没加,一行安装装出来根本没有这个插件,而且 没有任何东西会发现。test/patch.test.ts 因此把它们断言成双射,顺带守住行的顺序、 tetherline- 前缀、「有卡片的行 entry id 必须等于该包的 settings 命名空间」、「对外连接的行 默认未启用」和「没有占位符」。以后再加包,漏了哪一边都会红。

1. 前提

  • 本机装了 dsh CLI,且已有一个能跑通模型的 dsh 配置(provider/API key 由 dsh 自己管,本 bundle 不碰)。
  • 装进带网页外壳的 profile。dsh 自带的 web profile 正合适(模板即 [dsh-base, dsh-web-app]);换别的 profile 就得自己往它的 bundles 里补 @deepseek-ai/dsh-web-app 一行——那是 in-box bundle,dsh plugin add 永远加不进来 (../../docs/web-config.md §1.3)。
  • 一个飞书自建应用,拿到 appId(cli_ 开头)与 appSecret。
  • 从本仓库 checkout 装时还需要:pnpm install && pnpm -r build(bundle 用 link: 指向 packages/*/lib)。

飞书应用需要开哪些能力

以飞书开放平台后台为准。以下清单已于 2026-08-31 在真企业自建应用上走通:

  1. 机器人能力已启用;
  2. 事件订阅采用长连接方式(本 channel 用官方 SDK 的 WSClient,不监听公网端口, 因此不需要配置请求网址)。连上时 SDK 会打印 ws client ready;
  3. 订阅 im.message.receive_v1 事件与卡片回传交互(card.action.trigger);
  4. 权限(应用身份 / tenant):
    • cardkit:card:write —— 流式卡片的全部三个调用都要它 (cardkit.v1.card.create 建实体、cardElement.content 追加正文、card.settings 收尾)。 漏开它是最容易踩的一个:入站、回执、agent 执行全都正常,只有流式卡片发不出来;
    • 以应用身份发送消息的权限(im.v1.message.create / .reply)。

排错:日志里出现 code: 99991672 就是权限没开,飞书会在 msg 里直接给出缺哪个 scope 以及一条指向你自己应用的申请链接,照着开即可。自建应用改权限后通常要发布一个版本才生效。

2. 装进 profile

两条路径,选一条。

A. 从 npm 装(推荐;2026-09-01 已实测)

dsh plugin --profile web add @tetherline/agent

不需要 clone 本仓库:dsh 给 profile 写的 pnpm 设置是 nodeLinker: hoisted (deepseek-harness packages/boot/app-boot/src/profile.ts,0.2.1-alpha.1 在 :243,行号随版本漂移),传递依赖会被 扁平化到 profile 根,loader 解析得到。

要飞书的话再来一条(这一条必须带 --allow-build,见下面「必踩的一步」),装完重启 dsh:

dsh plugin --profile web add @tetherline/channel-feishu --allow-build=protobufjs

装完 dsh.profile.bundles 会变成 [dsh-base, dsh-web-app, @tetherline/agent]—— 声明了 dsh.bundle 的直接依赖会被自动追加成一层(apps/cli/src/plugin.ts:59-90), 而模板自带的两个 in-box bundle 不受影响。

必踩的一步:装飞书时的 allowBuilds

只影响装 IM 渠道那一条命令,默认安装碰不到它(2026-09-10 起,open-items 第 18 条)。 不带 --allow-build=protobufjs 跑装飞书那条命令会停在这里:

[ERR_PNPM_IGNORED_BUILDS] Ignored build scripts: [email protected]
dsh: pnpm failed in profile directory ~/.dsh/profiles/web

原因:protobufjs 是飞书 SDK @larksuiteoapi/node-sdk 的直接依赖,带 build script; 而 dsh 装 profile 用的是 pnpm 11,它把「忽略了 build script」从警告升级成了错误退出 (pnpm 10 只是警告)。

后果不只是报个错——pnpm 非零退出会让 dsh 的 bundle 层登记步骤不执行:包全装上了、 node_modules 里都能解析到,但 dsh.profile.bundles 里不会出现 @tetherline/agent, 启动时牵星根本不加载。装完务必回头确认那一行在。

最省事的办法是把这个决定直接写在命令行上——dsh plugin 是 pnpm 的透传壳,标志原样到达 pnpm,而 pnpm 会把决定写回 profile 的 pnpm-workspace.yaml,所以只有第一次要带:

dsh plugin --profile web add @tetherline/channel-feishu --allow-build=protobufjs

已经撞上了的话,pnpm 也把待办写进了 profile 的 pnpm-workspace.yaml:

allowBuilds:
  protobufjs: set this to true or false

把它改成 false(我们不需要 protobufjs 的 build script),再跑一遍上面那条命令:

# ~/.dsh/profiles/web/pnpm-workspace.yaml
allowBuilds:
  protobufjs: false

这一步没法由 bundle 代劳:allowBuilds 只在 workspace 根配置里生效,包自己声明不了; 而 profile 的 pnpm-workspace.yaml 由 dsh 在初始化时生成,之后不再改写。

装完自检(bundles 里应有 agent;装了飞书的话还有 channel-feishu):

dsh --profile web --dump-config | grep tetherline

新版发布后的 24 小时窗口

pnpm 11 有个供应链防护叫 minimumReleaseAge,默认拒绝安装发布不足 24 小时的版本。 所以刚发出来的修复,装的时候大概率还拿不到——pnpm 会在剩下的候选里挑一个更早的版本, 过程中不报错,只在 pnpm-workspace.yaml 里留一行 minimumReleaseAgeExclude 记录它选了谁:

Added N entries to minimumReleaseAgeExclude in pnpm-workspace.yaml
  @tetherline/[email protected]        ← 挑中的是旧版,不是刚发的 0.1.1

这不是缺陷,是 pnpm 有意的防护,默认就该让它拦着。装完想确认自己拿到了哪一版:

cat ~/.dsh/profiles/web/node_modules/@tetherline/backend-dsh/package.json | grep '"version"'

急着要某个刚发布的修复时,显式钉版本可以绕过(这等于替这一个包放弃 24 小时防护, 只在你确知要装哪一版时才这么做):

dsh plugin --profile web add @tetherline/[email protected]

把版本加进 minimumReleaseAgeExclude 是没用的:那份名单只放行已经被选中的版本, 不改变解析时的优先级。删掉 lockfile 重新解析同样无效。实测(2026-09-01)只有显式钉 版本这一条路走得通。

B. 从本仓库 checkout 装(改本仓库代码时用)

cd bundles/agent
./scripts/install.sh            # 参数是 profile 名,默认 web

# dsh 不在 PATH 上时(比如你跑的是 deepseek-harness 源码树):
DSH_BIN=~/.local/bin/dsh ./scripts/install.sh

脚本做四件事:构建 workspace、把 workspace 包与 bundle 一起 dsh plugin add 进 profile、 自检这些包能否从 profile 根解析到、打印 --dump-config 让你确认插件行都在。 默认不装飞书(与 npm 那条路一致);要连飞书一起装就 WITH_FEISHU=1 ./scripts/install.sh。

为什么 workspace 包要单独装:loader 是从 profile 根目录解析插件包名的,而 pnpm 对 link: 目标不处理它自己的 dependencies——bundle 里的 link:../../packages/* 不会被 提升到 profile 的 node_modules,只装 bundle 会在启动时报 Cannot find package '@tetherline/router'。发布到 npm 后 pnpm add <bundle> 会自动 带上这些传递依赖,只有本地 link 开发这条路有这个洞。dsh 对这些包会提示 「installed as a plain dependency, not a profile layer」,那正是我们要的:它们由 bundle 的 patch 层按包名引用,不自成一层。

3. 在网页上配置

不用编辑任何文件。 装完直接起:

dsh web            # 或 dsh --profile <你的 profile 名>

浏览器里进 设置 → 牵星(与「通用设置 / 模型 / 插件 / Agent 预设」并列)。默认安装完 会看到两张卡片:

  • mobile-web —— 移动端网页,「未启用」。见第 6 节。
  • channel-feishu —— 「未安装」。卡片上是安装命令与一个复制按钮;在这台电脑的终端里 跑完、重启一次 dsh,它就换成下面这张配置卡片(同样默认「未启用」)。

channel-feishu 装好之后的卡片:

卡片上只有四项要填,其余都收在「高级设置」折叠区里(都有可用默认值):

| 填什么 | 说明 | | --- | --- | | App ID | cli_ 开头的 16 位十六进制 | | App Secret | 直接填密钥。它写进 dsh 的凭证面,不进任何配置文件,保存后也不再显示出来(留空即不改动) | | 白名单 | 允许下发任务的飞书 open_id,一行一个。先随便给机器人发条消息,从 dsh 日志里读出你的 open_id | | 工作目录 | 新会话默认绑定的目录 |

填好后拨右上角的开关启用。

点保存。渠道未启用时不会建立任何连接,字段也不校验;一旦打开开关,配置必须完整合法—— 不合法的写入会被当场拒绝、值不会落地,卡片提示「没保存成功」并保留草稿,进程不受影响 (宿主不回传拒绝原因,具体原因看 dsh 日志) (../../docs/web-config.md §4)。改完即时生效,按新配置重连, 不必重启 dsh。

字段说明来自各渠道 Config schema 的 description,界面自己不存文案——所以卡片上看到的 文字就是 packages/*/src/config.ts 里写的那句。

还是想写文件?

可以,网页上的值只是叠在装配层之上的用户覆盖层,装配层仍然是 profile 自己的 cordis.patch.yml:

$EDITOR "${DSH_HOME:-$HOME/.dsh}/profiles/web/cordis.patch.yml"
- id: tetherline-channel-feishu
  config:
    enabled: true
    appId: 'cli_0123456789abcdef'
    appSecretEnv: 'TETHERLINE_FEISHU_APP_SECRET'
    allowedSenders: ['ou_你的飞书 open_id']
    defaultCwd: '/abs/path/to/your/workspace'

两处要注意:

  • config 是整体替换,不是深合并(dsh 的 applyEntryPatches:target[key] = value, 见 deepseek-harness vendor/include/src/index.ts)。覆盖某个插件时必须把它的字段写全。
  • 配置文件里写 enabled: true 却漏了必填项,会在插件加载期 fail loud——那是错配置, 该崩;网页写入的同类错误则在写入时被拒。两条路的拦截点不同,都不会让错配置进入运行期。

密钥去哪了

配置里没有任何一处写密钥:appSecretEnv 是凭证引用(一个环境变量名),插件在每次建立 长连接时解析一次——宿主有 ctx.credentials 就问它,没有该服务时读启动环境变量,两处都拿不到 则 fail loud。所以改了密钥不必改配置,也不必重启进程,下次连接即生效(决策记录见 ../../docs/open-items.md 第 6 条)。

网页上填的密钥就写在这个引用名下。也可以手写受管文档:

# $DSH_HOME/.credentials.yaml(默认 ~/.dsh/.credentials.yaml,仅本人可读)
version: 1

refs:
  TETHERLINE_FEISHU_APP_SECRET: 你的-app-secret

外部编辑会热重载,改完不必重启 dsh。临时调试也可以直接 TETHERLINE_FEISHU_APP_SECRET=… dsh web,继承的环境优先级最高。

4. 启动与冒烟

dsh web

真机 checklist(对应 PRD 的用户故事,需人工逐条走)。2026-08-31 首次真企业验收结果:

  • [x] US-1 下发:手机飞书私聊机器人发一句任务,收到流式更新的卡片。
  • [x] US-4 完成通知:任务结束时卡片停止流式,正文末尾出现状态与 token 用量 (**完成** · 输入 305 / 输出 325 tokens)。
  • [x] US-2 审批:让 agent 执行一条需要审批的命令(写 workspace 之外的路径即可触发, /tmp 不行——沙箱把平台临时区也算可写),收到审批卡片;点「批准」后 agent 提权执行、 卡片原地变成「✅ 已批准」,点「拒绝」后 agent 停下、卡片变红。
  • [x] 至少有一次审批是在手机上、不在电脑旁完成的(roadmap M1 的产品验收项)。
  • [ ] 用非白名单账号发一条消息,收到明确的拒绝回执,且没有产生 agent 会话。 (keyless e2e 已覆盖这条路径;真机验证需要第二个飞书账号。)
  • [x] 网页配置界面(2026-08-31 在本机浏览器实测):设置里出现「牵星」一项,卡片显示 「未启用」,四个必填项 + 高级折叠 + 开关都正常,改一个参数保存后落进 settings.yaml、 「恢复默认」能清掉。
  • [ ] 网页配置 → 真飞书:填完 appId / 密钥 / 白名单 / 工作目录并打开开关后渠道能连上; 改白名单后按新配置重连;故意填错 appId 时保存被拒、值不落地、进程没崩。
  • [x] /new /sessions /abort /status 各跑一次,回执符合预期。 /use <绝对路径或 ~ 路径> 切工作目录并新建会话(未跑)。
  • [x] 杀掉 dsh 进程再启动:上一条 pending 审批被判失效并通知,不再重发可点卡片 ——agent 侧的审批等待方随进程消失,跨不了进程,详见 architecture §5.3。

5. keyless e2e(无需飞书凭证)

pnpm --filter @tetherline/agent test

test/e2e.test.ts 把 test/fixtures/session.ndjson 里的录制事件回放进真实装配: dsh 自己的 AgentLoop、ApprovalService、工具管线与会话日志(@tetherline/backend-dsh/testing 的运行时夹具),router 与 backend-dsh 两个 cordis 插件按 profile 的方式挂载, channel-feishu 的归一化、降级与 CardKit 卡片渲染全走真实代码。

只有两处是替身,且都在系统边界上:

  • 模型换成脚本化 LlmAdapter(不联网、可断言);
  • 飞书传输层换成 ReplayTransport(不联网,出站只记录,入站由录制事件驱动)。

覆盖三条路径:US-1/US-4 的流式闭环、US-2 的审批闭环(含点击回传)、非白名单拒绝。

想加事件:把真实事件按 RecordedFeishuEvent 格式({"type":…,"event":…})追加进 NDJSON, 去标识化后再提交——录制里不得出现真实 open_id、聊天内容或凭证。

6. 移动端网页(mobile-web 卡片)

装完就有这张卡片,默认未启用。关着的时候它不挂 /tetherline 路由、不解析凭证、 不起 frpc 子进程、不碰网络——所以一行安装本身不会产生任何对外连接。

打开它需要三样本 bundle 给不了的东西,缺一样就不要拨那个开关:

  1. 一台公开网关:跑 frps、有域名和证书、把某个远端端口反代成 HTTPS。部署件在 deploy/vps, 登录层与反代宿主 PWA 的那份配置见其 SETUP 第 12 节(2026-09-06 已在真 VPS 上验收: 网页登录、/tetherline/ 反代宿主插件、WebSocket、免登录探测路径全通)。 切过去之前记得停掉旧形态那条常驻 frpc,否则两个 frpc 抢同一个远端端口。
  2. frpc 可执行文件:六个平台二进制包还没发布,暂时只能用高级项 frpcPath 指一个 自备的 frpc。这是开发选项,不是产品出口(../../docs/mobile-web.md §3.4)。
  3. dsh 信任那个公开 authority,也就是下面这一节。

必踩的一步:让 dsh 信任公开 authority

dsh 按外部 authority 校验浏览器会话与来源。手机经网关过来时 Host 是 gateway.example.com,而 dsh 默认只信任自己绑的那个地址,于是每个请求直接 403—— 隧道通、卡片绿、手机上却什么都打不开,是这条链路上最难查的一种故障。

这一步 bundle 替不了你:公开地址是你填在卡片里的值,装配层在安装时并不知道它。 而且它是装配期事实,不能热更新——填完公开地址要重启一次 dsh 才生效 (../../docs/mobile-web.md §8,已用 0.1.2-alpha.2 的实际 tarball 实证)。

两条路,选一条。

A. 启动参数(临时验证用最省事):

dsh web --trusted-host gateway.example.com

B. 写进 profile 自己的 patch 层(长期部署用这条,重启后仍在):

$EDITOR "${DSH_HOME:-$HOME/.dsh}/profiles/web/cordis.patch.yml"
- id: connection
  config:
    trustedHosts: !!js ['gateway.example.com', ...ctx.webRuntime.trustedHosts]

connection 是 @deepseek-ai/dsh-web-app 装的行,它的 config 只有 trustedHosts 一个键,所以照抄上面这三行就是完整的——但请记住 patch 是整体替换而非深合并: 哪天 dsh 给这一行加了第二个键,你这份覆盖会把它抹掉。...ctx.webRuntime.trustedHosts 不能省,它是 dsh 自己算出来的本机与局域网地址,去掉就登不上本机的 dsh 了。

只填了公开地址、没做这一步会怎样:手机上 403。宿主计划在设置卡片的「验证」里 直接把这个情况判出来(合成一个只带 Host 的探测请求,403 就指到本节), 那是 §6 分层验证的第 1 层,尚未实现。在它落地之前,这一步只能靠本节记着。

现状 / 已完成 / 下一步

  • 现状:M1 与 WS-11 都已在真飞书企业上完成验收。M1 是 2026-08-31(当时还叫 @tetherline/feishu-agent、装在自定义 tetherline profile 上、配置靠手工编辑文件); WS-11 的一行装完 + 网页配置是 2026-09-01:install.sh web 装入后,全程在浏览器里 填配置、拨开关,没有编辑过任何文件,手机上收发正常。 2026-09-05 起 WS-12C 也接上了:@tetherline/pwa 进依赖表与 patch 层,同一条安装 命令现在也装移动端网页。2026-09-06 这一半也完成了真机验收:真 VPS 网关 + 真 dsh + 真 frpc,装配后分层验证第 1~5 层全绿,手机浏览器上的第 6 层仍待人工完成。装的是本地 tarball(@tetherline/pwa 还没发到 npm),一并把「从 tarball 的一行安装 e2e」实跑了一遍: 七个包解析得到、五行插件齐全、第一次照旧卡在 allowBuilds。frpc 平台包仍未发布,所以 还得靠高级项 frpcPath 指一个自备的 frpc。 那次真机撞出并修掉的一个缺陷正好属于本层:@tetherline/ui-dsh 的动作 RPC 频道与 @tetherline/pwa 的静态前缀都叫 /tetherline,装到一起时 dsh 直接起不来——两个包各自 测都对,只有装配层同时认识它们,断言因此落在 test/webserver-paths.test.ts。
  • 已完成:bundle manifest 与 patch 层(五行,装完即可启动、无占位符)、装配层结构测试 (依赖表 ↔ patch 行双射,10 用例)、安装脚本(shellcheck 通过,含解析自检)、 录制事件回放 e2e(3 用例,迁移后仍全绿)、真机冒烟 checklist、 经真机订正的飞书能力/权限清单与踩坑表。
  • 下一步:
    1. ~~安装链路已实测,网页配置未实测~~:2026-09-01 后半程也走完了,装进本机真实 web profile → 浏览器「牵星」页填配置 → 拨开关 → 手机收发 → 热重连,全过。 当场抓到一个缺陷(写入被拒时没有任何原因留痕),已修;
    2. ~~尚未发布到 npm~~:2026-09-01 首发完成(七个包,channel-feishu 起于 0.1.1, 其余 0.1.0),第 2 节 A 路径已用干净的临时 DSH_HOME 实跑验过——一条 dsh plugin add @tetherline/agent 把六个包扁平化装进 profile 根、bundle 层登记正确、 --dump-config 四行插件齐全。但第一次必然失败于 allowBuilds,已写进第 2 节。 加上 @tetherline/pwa 之后是七个包、五行插件,这一版还没实跑过 A 路径—— 因为 @tetherline/pwa 还没发到 npm。发布 PR(changesets 的「发布版本」PR)一合并它 就会以 0.1.0 上 npm,那之后要重跑一次 A 路径才算数;
    3. ~~从 tarball 的一行安装 e2e 尚未做~~:2026-09-06 实跑完成(见上方现状)。用本地 pnpm pack 的七个 tarball 装进临时 DSH_HOME,七个包解析得到、五行插件齐全、第一次照旧卡在 allowBuilds。它仍不是自动化门禁——需要 dsh CLI 与网络,本仓库单测跑不动这个形状, 目前是一条手工验收路径,要不要固化成 CI 作业没定;
    4. 非白名单账号的拒绝路径真机未验(keyless e2e 已覆盖,真机需要第二个飞书账号);
    5. /use <目录> 未实跑(其余四个斜杠命令已验);
    6. 尚未验证的长尾场景:群聊与话题(当前只验过私聊)、多会话并发、长时间运行后的连接迁移。

真机验收踩过的坑(照着装的人会遇到同样的)

| 现象 | 根因 | 出处 | | --- | --- | --- | | 渠道一直连不上,日志里什么都没有 | channel-feishu 漏把 credentials 声明进 inject,建连时凭证服务还没就绪 | 已修 | | agent 报 has no provider/model | backend-dsh 的「沿用 dsh 部署默认」是空的,dsh 不会自己补默认 | 已修 | | 流式卡片发不出,日志里一个 400 | 飞书应用没开 cardkit:card:write(code 99991672 会直接告诉你缺哪个 scope) | 见第 1 节 | | 审批卡片发不出(400),agent 停在等审批 | 卡片声明 schema 2.0 却用了 1.0 的 tag: action 容器(code 230099) | 已修 | | 点审批按钮弹「该应用尚未配置卡片回调」 | 开放平台需要一键配置卡片回调,配完要重新发布应用版本 | 见第 1 节 | | 代码块渲染成行内代码、工具行和正文粘一起 | 方言转换被逐片作用在流式增量上 | 已修 | | 启动即 Cannot find package '@tetherline/router' | 只装 bundle 不够,workspace 包要各自装进 profile(只影响本地 link 这条路) | 见第 2 节 | | 设置页里飞书一直显示「未安装」,命令明明跑过了 | bundle 层是启动时合成的,装完要重启 dsh;仍不出现就看 dsh --profile <名> --dump-config 里有没有 tetherline-channel-feishu 那一行 | 见第 2 节 | | 从 npm 装时 [ERR_PNPM_IGNORED_BUILDS] protobufjs,且包装上了却不加载 | dsh 装 profile 用 pnpm 11,它把「忽略 build script」升成错误退出,连带跳过 bundle 层登记 | 见第 2 节 A | | 问「列一下当前目录」,回来的是工具调用标记原文(<||DSML||tool_calls> 或 <tool_req>) | 建会话时没加入 agent preset,agent 的工具集是空的——preset 决定 model-facing 插件集,而它要 meta.agentPreset + mount() 两步 | 已修(backend-dsh 0.1.1) | | 修复已发布,装完却还是老样子 | pnpm 11 的 minimumReleaseAge 默认拦下发布不足 24 小时的版本,静默退回更早的一版 | 见第 2 节 A | | 审批收束后卡片一片空白,只有「请升级至最新版本客户端」 | card.update 是整体替换,卡片 JSON 漏了 schema: '2.0' | 已修 |

另有一条不是 bug 而是设计,第一次遇到容易困惑:杀掉 dsh 再启动后,之前那条 pending 审批会被判失效(收到「因进程重启已失效,请重新发起任务」),而不是重发一张可点的卡片。 原因是 agent 侧挂在 waterfall 上的审批等待方随进程一起消失了——dsh 的会话能 resume, 等待方不能,重发的卡片点下去只会得到「回包失败,会话不在线」。详见 architecture §5.3。