1 Commits

Author SHA1 Message Date
0549975555 feat(deploy): 插件规范化部署 —— 生产跑仓库外快照,可回滚
# 问题(实测的加载形态)

    pi        systemd: node /home/program/agentmail/plugins/pi-mail-bridge/src/index.mjs
    opencode  opencode.jsonc: "file:///home/program/agentmail/plugins/opencode-mail-bridge"
    dsh       profiles/web/package.json: "dsh-mail-bridge": "link:/home/program/.../dsh-mail-bridge"

三个桥跑的都是**仓库工作区**。于是:一次编辑 + 重启就是上线(没有构建、没有评审、
没有版本);**没有回滚目标**(网关有 `.bak-<时间戳>`,三个桥一个都没有);
`git checkout` / `git stash` / 半成品编辑会静默改变线上行为;仓库同时兼作构建目录
(`dist/`、`node_modules/` 都在里面)。

对照:homeagent 插件本来就是这个规范形态(跑 `/home/newqqagent/plugins/
homeagent-mail-bridge/plugin.bin` 部署副本)—— 所以这里不是发明新办法,
而是把已有的那个形态推广到三个 JS 桥。

# 本提交只交付工具与门禁,**未切换生产**

`deploy/redeploy-plugin.sh <pi|opencode|dsh> [--stage-only]`:

  1 前置断言 → 2 staging(仓库外)→ 3 门禁 → 4 原子切换 `ln -sfn <ts> current`
  → 5 重启 → 6 后置验证 → 任一步失败即切回 `.prev` 并重启

后置验证不只看 `systemctl is-active`:桥可能进程活着却没连上 Gateway
(密钥失效、Gateway 未起、依赖在惰加载时才暴露),所以**必须以日志出现
「已接入」为准**,45 秒轮询。

需要编译的插件(dsh)**产出到 staging**,不写仓库的 `dist/`:先在仓库构建再拷贝
会有两个问题 —— 失败的构建也会 emit(tsc 默认 `noEmitOnError=false`)导致仓库产物
被半成品覆盖;以及生产产物与工作区之间多一条看不见的耦合。

# 依赖门禁:不能用 require.resolve

`deploy/check-plugin-snapshot.mjs` 从入口递归收集静态 import/export/动态
import/require 的说明符并逐个解析。

**判据用 ESM 而非 CJS 解析**——这是实测教训:`@earendil-works/pi-coding-agent`
的 `exports` 只声明 `"import"`,`require.resolve` 抛 ERR_PACKAGE_PATH_NOT_EXPORTED,
而插件是 `import` 它的、实际毫无问题。用错 API 会让门禁**报假缺陷**,
而假缺陷比不检查更糟(会让人去修一个没坏的东西)。

也不能只比对 `package.json` 的 dependencies:pi 的 dependencies 是 `{}`,
而它 import 了 `@earendil-works/pi-coding-agent` —— 声明是假的,只查声明等于空跑。

# 已验证(干跑,未切换、未重启)

  pi       20/20 说明符可解析          opencode 23/23          dsh 26/26
  dsh 经 tsc 构建到 staging 后通过

反向验证:拿掉一个依赖 → 门禁 rc=1 并点名该说明符(不是空转)。
2026-09-12 11:36:25 +08:00