用户注意到:「当前 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 均已在安装根下。
57 lines
2.5 KiB
Desktop File
57 lines
2.5 KiB
Desktop File
[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/agent(settings.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
|
||
|
||
# 桥是长驻守护进程,启动即注册 + 立刻打一次心跳 + 订阅 SSE(B-1),
|
||
# 不像 opencode 那样惰加载,因此不需要 ExecStartPost 预热。
|
||
|
||
# pi 会话在内存里持有整条对话,长跑之后常驻几百 MB。给一个上限让它被
|
||
# OOM killer 挑中而不是拖垮整机;Restart=always 会把它拉回来。
|
||
MemoryMax=4G
|
||
|
||
# 优雅停机:桥收到 SIGTERM 后要注销 SSE、清锁文件。
|
||
# 默认 90 秒太长(停机时人在等),10 秒足够那两件事。
|
||
TimeoutStopSec=10
|
||
|
||
[Install]
|
||
WantedBy=multi-user.target
|