Files
MailUI4Agents/deploy/systemd/pi-mail-bridge.service
JianFeeeee 51789ee72e deploy: 标准目录部署 —— 运行时不再依赖源码目录
用户注意到:「当前 agentmail 是在源码目录部署的,应当改为标准目录部署」。
查证后有三处实证(都不是猜测):

1. ★ **失败通知钩子执行的是仓库里的脚本**
   (`/home/program/agentmail/deploy/service-failure-notify.mjs`,8 处引用:
   4 个 drop-in + zcode/zcode-mail-bridge 单元 + agentmail-failure-flush)。
   仓库一挪/一改名,故障通知就**静默失效** —— 而那条管线正是用来报告服务故障的。
2. **opencode-serve 的 cwd 就是源码目录**(`WorkingDirectory=/home/program/agentmail`)。
3. ★ **仓库里的 `deploy/*.service` 是旧的源码目录版本**(ExecStart 指向
   `/home/program/agentmail/plugins/...`),而机器上的已被改过 —— 也就是说
   **谁跑一次 install.sh 就会把部署退回源码目录**。drop-in 更是只存在于 /etc 里,
   仓库完全没有它们。

## 改动

- **唯一真相**:`deploy/systemd/` 镜像 systemd 目录结构,收进全部单元与 drop-in
  (8 个单元 + 12 个 drop-in),路径全部改到 `/opt`。
- 运行时脚本装到 **`/opt/agentmail/bin/service-failure-notify.mjs`**(自包含,
  无相对导入);`install.sh` 与 `redeploy-gateway.sh` 都会幂等地装它。
- opencode 的 cwd 改为 `/opt/agentmail`(与网关一致),已重启生效
  (`/proc/<pid>/cwd` 已核)。
- 删掉 `deploy/*.service` 的旧副本,避免两个真相。
- dsh 的 `cordis.patch.yml` 注释里的安装示例也改到标准位置(运行时用的是环境变量,
  那条注释是唯一残留)。

## 判据(`deploy/check-deploy-drift.mjs` 新增「标准目录部署」四条 + 自检)

① 任何 unit/drop-in 都不得引用源码目录;② 已安装单元与 `deploy/systemd/` 逐字节一致;
③ 通知脚本在标准位置且可执行;④ 各服务的 cwd/ExecStart 不在源码目录
(homeagent/dsh/zcode 是**别的产品**的标准位置,按白名单放行)。
自检两个样本:引用源码目录的必须红、干净样本必须绿(证明不是恒真)。

顺带修掉一处**真漂移**:仓库里 dsh 的 `dist/index.js` 落后于部署件(改了 src 没重建),
重建后 `check-deploy-drift` 报「四个宿主都在跑当前代码」。

## 复核

- `/etc/systemd/system/` 引用仓库:**0** 个文件;`/opt/agentmail` 下只剩旧二进制/备份里
  的构建路径(Go 嵌的源码路径,无害)与一条注释。
- 四个宿主都在跑当前代码;标准目录四项全绿。
- 全部服务 active,opencode/网关 cwd 均已在安装根下。
2026-09-14 08:38:33 +08:00

57 lines
2.5 KiB
Desktop File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

[Unit]
Description=pi mail-bridge (AgentMail ↔ @earendil-works/pi-coding-agent)
After=network-online.target agentmail-gateway.service
Wants=network-online.target
[Service]
Type=simple
# 桥自己不需要工作目录 —— 每条会话的 cwd 来自邮件寻址的 path 位。
# 但 systemd 要求一个存在的目录,且 pi 的 SettingsManager 会在这里找
# 项目级配置,因此指向仓库而不是 /。
WorkingDirectory=/opt/agentmail/plugins/pi-mail-bridge/current
ExecStart=/usr/bin/node /opt/agentmail/plugins/pi-mail-bridge/current/src/index.mjs
# pi 靠 HOME 定位 ~/.pi/agentsettings.json、auth.json、models.json、sessions/)。
# systemd 不会自动注入 HOME不显式给就
# - 读不到 provider 凭证 → 每封邮件都"没有可用模型"
# - 会话落到 /.pi 或直接失败 → C-8 的会话快照永远是空的
Environment=HOME=/root
# pi 的全局扩展 pi-a2a / pi-acp 绑死 127.0.0.1:12010 / 12011。
# 桥用 noExtensions:true 起会话,本进程不会去 bind 那两个端口;
# 这两个变量是给「同机还跑着 pi CLI」的情况留的隔离位
# 万一将来放开扩展加载,端口也不会和交互式 pi 撞。
Environment=PI_A2A_PORT=13010
Environment=PI_ACP_PORT=13011
EnvironmentFile=/etc/agentmail/pi.env
Restart=always
RestartSec=10
# 桥用 O_EXCL 建 <AGENTMAIL_CONFIG_DIR>/pi-bridge.lock 保证单实例
# (两个实例各自订阅 SSE 会让同一封邮件各起一条会话,发件人收到两封回信,
# 而去重集合在各自内存里、彼此看不到)。
#
# 被 SIGKILL 或主机掉电时锁文件留在盘上 —— 桥自己会检查锁里的 pid 是否还活着,
# 但那要求 /proc 可读且 pid 未被复用。systemd 重启前清一次更直接:
# ExecStartPre 里的 pid 必然已经死了systemd 确认进程退出后才重启)。
#
# 用 `-` 前缀:文件不存在时 rm 失败不该拦住启动。
ExecStartPre=-/bin/rm -f /root/.agentmail-pi/pi-bridge.lock
# 桥是长驻守护进程,启动即注册 + 立刻打一次心跳 + 订阅 SSEB-1
# 不像 opencode 那样惰加载,因此不需要 ExecStartPost 预热。
# pi 会话在内存里持有整条对话,长跑之后常驻几百 MB。给一个上限让它被
# OOM killer 挑中而不是拖垮整机Restart=always 会把它拉回来。
MemoryMax=4G
# 优雅停机:桥收到 SIGTERM 后要注销 SSE、清锁文件。
# 默认 90 秒太长停机时人在等10 秒足够那两件事。
TimeoutStopSec=10
[Install]
WantedBy=multi-user.target