58 KiB
svc.sh 使用说明
scripts/svc.sh 是本仓的本机服务管理器:把 后端(agent)、前端静态服务(static)、
PostgreSQL 数据库(postgres)三类服务统一为"服务"模型——按提交/版本部署、生命周期管理、
配置分层持久化、标签、事件追溯,以及一套完整的数据库运维能力(换端口 / 备份 / 恢复 /
克隆 / 跨实例搬库)。
设计原则(也是安全边界,详见 §11 安全与边界):
- 服务自包含:每个服务的产物、数据、日志、登记都在
runtime/<name>/一个目录里, 服务之间互不串版本;删目录 = 完全移除。 - 登记持久化:所有状态落盘(
svc.env当前态 +history.jsonl事件流),list/history全部从文件重建,脚本无内存状态、跨会话可追溯。 - 不碰现场:不改主检出 git 状态;只停"自己 pidfile + cmdline 双校验"通过的进程;
端口冲突报错不抢;除
--from local外的构建全部在隔离 worktree(物化自独立部署 源仓,与当前工作区状态无关——脏树、任意分支均可部署/更新)。
分主题速查:scripts/svc.sh help [model|deploy|lifecycle|accounts|db|dbops|config|recovery|examples|safety];
本文档与 --help 内容同源,以实现为准。
目录
1. 前提条件
| 依赖 | 用途 | 必需性 |
|---|---|---|
| bash ≥ 4、git | 脚本本体与提交物化 | 必需 |
| curl | 健康检查、设置播种 | 必需 |
| python3 | static 服务(http.server)、播种 payload | static/播种需要 |
| ss | 端口占用检测(iproute2) |
建议(缺失时跳过检测) |
| PostgreSQL 工具链(initdb/pg_ctl/psql/pg_dump/pg_restore/pg_isready) | 数据库功能 | 数据库功能需要 |
PostgreSQL 工具链定位顺序:SVC_PG_BIN 环境变量 → pg_config --bindir →
/usr/lib/postgresql/*/bin 等常见路径。都没有时 agent/static 功能不受影响。
2. 90 秒上手
# 一套演示后端(缺省部署远端 main 最新:独立部署源仓物化构建 → 启动 → 健康检查;
# 与当前工作区状态无关。要构建当前工作区:--from local)
scripts/svc.sh dev deploy --mock
# 一个数据库 + 让后端连上它
scripts/svc.sh db create pg1 --port 8890 # initdb,只登记不启动
scripts/svc.sh pg1 start # pg_ctl 启动 + 自动建库 + pg_isready
scripts/svc.sh prod deploy --from dev@<sha> \
--dsn "$(scripts/svc.sh db dsn pg1 --reveal)" # DSN 直接组合进部署
scripts/svc.sh prod db pg1 --db prod # 或事后绑定/切换(缺库自动建,重启生效)
scripts/svc.sh prod config .svc/prod.json # 指定/更换配置文件(校验+重启即时生效)
# 日常操作
scripts/svc.sh list # 全服务总览(含 DB 绑定列)
scripts/svc.sh info # 综合报告:运行服务/数据库/配置快照
scripts/svc.sh prod db # 查看 prod 的数据库绑定与可用实例
scripts/svc.sh pg1 restart # 重启(复用上次配置)
scripts/svc.sh prod logs 100 # 跟日志
scripts/svc.sh history # 事件追溯
# 备份 / 恢复 / 克隆
scripts/svc.sh db backup pg1 # pg_dump -Fc,自动自检
scripts/svc.sh db restore pg1 --file <dump> --force # 覆盖恢复需 --force
scripts/svc.sh db clone pg1 pg1test --port 8896 # 克隆一套到新端口
# 把服务刷到远端最新(不依赖当前工作区状态:主检出脏树、任意分支均可)
scripts/svc.sh update --check # 先看哪些服务落后
scripts/svc.sh update # 自动更新过时且运行中的服务
3. 概念与目录布局
3.1 服务名
^[a-z][a-z0-9-]{0,15}$(小写字母开头,可含数字与连字符,≤16 位)。保留字不可用作
服务名:status list add set db rm create dsn commits history update deploy stop start restart logs help info version。
三个名字有默认端口:dev = 127.0.0.1:8788,prod = 127.0.0.1:8787;其余名字按
名称哈希在 8800–9099 确定性派生端口,与已登记端口或正在监听的端口冲突时自动向后
扫描;SVC_<NAME>_ADDR 可显式覆盖。
3.2 目录布局
每个服务自包含于一个目录(--purge 删目录即完全移除):
runtime/<name>/
├── bin/agent-system agent 产物(deploy 复制就位;start 只跑这份)
├── htdocs/ static 前端产物(--dir 模式登记外部目录,不复制)
├── data/ agent 数据(SQLite 与工件;--data 可改)
├── pgdata/ postgres 数据(initdb 产物,700)
├── plugins/ agent 插件(随 deploy 复制)
├── backups/ pg_dump 备份(目录 700,文件 600)
├── svc.pid pidfile
├── svc.log | pg.log 日志(postgres 用 pg.log)
├── svc.env 登记 + 持久化配置(dotenv,600)——见 §10
└── history.jsonl 事件流(append-only JSON Lines)——见 §10
因为产物在环境目录内,start/restart 不依赖主检出;旧版脚本部署过的环境没有环境
产物,升级后首次需重新 deploy 一次(脚本会明确提示)。
3.3 三类服务(kind)
| kind | 产物 | 数据 | 生命周期实现 |
|---|---|---|---|
agent |
单二进制(内嵌前端) | data/(SQLite)或外部 DSN |
nohup + TERM→KILL |
static |
htdocs/ |
无 | python3 http.server |
postgres |
系统工具链 | pgdata/ |
pg_ctl(fast stop,绝不 KILL) |
登记里没有 kind= 的旧条目自动视为 agent。同名服务不能换类型,需先 rm。
4. 命令参考:服务级
以下 <name> 均指服务名;action 可省略的只有 deploy。
4.1 deploy(部署并启动)
scripts/svc.sh <name> deploy [--kind agent|static] [--from 源] [--tag T]... [参数...]
流程:解析来源 →(dry-run 则只打印计划)→ 停旧实例 → 物化/构建/校验 → 产物复制到
runtime/<name>/ → 启动 → 健康等待(agent 20s / static 10s)→ 播种设置(agent)→
登记。任何一步失败都会写 deploy fail 事件并保留现场。
agent:来源见 §5;启动参数组装见 §9 配置系统;
健康后按 --model-* / --light-* / --tavily-keys 播种(幂等,设置存在 agent 数据目录)。
密钥表 secret_keys 的生效行优先于设置明文;换 key 后以 /api/secrets 为准。
当前后端在 PUT /api/settings/system 收到非掩码密钥时,会把它写成系统行的生效密钥。
嵌入式库中生效密钥与环境变量指纹不一致时,播种后告警,不打印明文。
static:
scripts/svc.sh ui deploy --kind static --from dev@<sha> # 构建该提交的 frontend dist → htdocs/
scripts/svc.sh ui deploy --kind static --dir ../dist # 直接服务既有目录(不构建不复制)
--dir模式登记原始路径(source=dir@<路径>),目录内容由你自己管理;release源对 static 无意义(制品内嵌前端),明确拒绝。
通用参数:
| 参数 | 说明 |
|---|---|
--tag T |
标签,可重复;登记合并去重,list --tag 过滤 |
--addr host:port / --host H / --port P |
监听地址 |
--data DIR |
agent 数据目录(默认 runtime/<name>/data) |
--plugins DIR |
agent 插件源(默认随 deploy 复制环境目录的 plugins/) |
--dsn DSN / --auth on|off|auto / --mock |
agent 运行参数(非 dev 环境默认 --auth auto) |
SVC_<NAME>_INSECURE_LISTEN |
设为 1 时允许该服务在非回环地址上不开启认证。值传给进程的 AGENT_SYSTEM_INSECURE_LISTEN,不写进 exec=。旧二进制会忽略 |
SVC_<NAME>_ALLOWED_HOSTS |
逗号分隔的额外主机名,传给 AGENT_SYSTEM_ALLOWED_HOSTS,供反向代理的 Host / Origin 校验 |
--model-base/--model-key/--model-name、--light-*、--tavily-keys |
启动健康后播种 |
--dry-run |
只解析并打印部署计划(来源、物化路径、产物去向、启动参数) |
--pick [dev|release|rdm] |
交互式编号选提交(需 TTY) |
--config FILE / --env FILE |
配置文件(见 §9) |
-- <剩余参数> |
原样透传给 agent-system serve |
4.2 start / stop / restart / logs / status
scripts/svc.sh <name> start # 已在运行则提示并返回 0
scripts/svc.sh <name> stop # agent/static: TERM→10s→KILL;postgres: pg_ctl -m fast
scripts/svc.sh <name> restart # stop + start
scripts/svc.sh <name> logs [N] # tail -f,从最近 N 行起(默认 50)
scripts/svc.sh <name> status # 单服务详情
- 裸
restart复用上次启动配置(svc.envexec=),换过端口/参数不用重敲; - 显式给任何运行参数(或
--config文件、SVC_*变量)时整体重建参数; - agent/static 换端口:
restart --port N(进程与登记同步更新); - postgres 换端口:
set --apply(见 §7.4)或restart --port N同样有效。 - 超级管理员凭据文件(agent,
superadmin_envfile=):在 deploy/start/restart 时传一次AGENT_SYSTEM_ENV_FILE=<文件>,svc 先校验(普通文件、属当前用户、权限 0600 即无组/其他位, 不合格则在停旧服务前拒绝),再只把绝对路径记入 svc.env(从不读取、复制或打印文件内容); 此后裸restart、recover/watchdog 拉起都复用该路径。本次环境显式设置的SUPER_ADMIN_OWNER/SUPER_ADMIN_PASSWORD/AGENT_SYSTEM_ENV_FILE优先于登记;AGENT_SYSTEM_ENV_FILE=(空值)清除登记;登记文件已不存在时告警且不转发(serve 按缺凭据拒启)。
status 输出示例(postgres):
[pg1] postgres 运行中 pid=3115294
地址 127.0.0.1:8890 数据 /…/runtime/pg1/pgdata
来源 postgres@local 版本 18.6
标签 core,local
库 app 用户 agent
DSN postgresql://agent:***@127.0.0.1:8890/app(db dsn pg1 --reveal 看密码)
4.3 set(变更登记)
scripts/svc.sh set <name> --tag T... # 三类服务通用:改标签(不动进程)
scripts/svc.sh set <pg名> --port N [--host H] # 未运行:直接改登记
scripts/svc.sh set <pg名> --port N --apply # 运行中:停止 → 改登记 → 重启
scripts/svc.sh set <pg名> --host 0.0.0.0 --apply # 换绑定主机同理
- 运行中的 postgres 必须
--apply,否则拒绝(防止登记地址与实际监听不一致); - 新端口先做双重校验:未被其他服务登记占用、且当前无人监听;
--apply的重启走pg_ctl优雅停机,数据安全;- agent/static 的地址变更用
restart --port N(set 只改登记,会提示)。
4.4 rm(注销)
scripts/svc.sh rm <name> # 停止 + 移除登记(svc.env/svc.pid);目录数据历史保留
scripts/svc.sh rm <name> --purge # 连 runtime/<name>/ 整个目录一起删除(含备份!)
只管理本脚本登记过的服务;未登记名字会明确报错。
4.5 db(agent 服务的数据库绑定/切换)
scripts/svc.sh <agent名> db # 查看当前绑定 + 可用实例
scripts/svc.sh <agent名> db <pg名> [--db X] # 绑定/切换数据库
scripts/svc.sh <agent名> db <pg名> --no-restart # 只改登记,下次 start 生效
scripts/svc.sh <agent名> db --clear # 解除绑定(回内嵌 SQLite 档)
每个 agent 服务都可以对应一个数据库实例中的库;绑定登记在 svc.env
(dbref=<pg名>/<库名> + db=pg,权限 600),跨重启保留——DSN 不落 exec=,
启动时由实例登记重建并经 AGENT_SYSTEM_DSN 环境变量交付(实例端口/密码变更自动跟随)。
- 缺库自动建:实例运行中且目标库不存在时自动
CREATE DATABASE(幂等); 实例未运行且目标库非其默认库 → 提示先start实例; - 运行中自动重启生效(按新登记重建 DSN);
--no-restart只写登记; - 产物能力预检:当前二进制不消费环境变量 DSN 时拒绝绑定(先 deploy 新版本);
mock 档与绑定共存同样拒绝(先
--clear或去 mock); - 展示:
list的 DB 列、status的"数据库"行、info数据库节的绑定服务行 (反向映射:哪些服务指向该实例)都能看到服务↔数据库对应关系; - 未用本命令、直接
deploy --dsn的服务:命中本机实例登记的会升级为托管绑定 (dbref=),否则持久化为dsn=字段并在显示层标注外部@host:port; --clear显式解绑:记录db=sqlite、清除dbref/dsn(数据在原--data目录, 切回即见旧数据);此后残留SVC_*_DSN不再悄悄重新选库。
4.6 config(agent 服务的配置文件更新/生效)
scripts/svc.sh <agent名> config # 查看当前配置文件(掩码内容 + agent 视角生效值)
scripts/svc.sh <agent名> config <file.json> # 更换使用的配置文件(校验通过才切换)
scripts/svc.sh <agent名> config <file> --no-restart # 只改登记,下次 start 生效
scripts/svc.sh <agent名> config --clear # 移除(回命令行参数形态)
配置文件是 agent 二进制的 --config(JSON,schema 严格校验;可用
agent-system config explain -config <file> 预览每个键的来源与生效值)。
- 切换前强校验:文件必须存在且通过二进制的 schema 校验(未知键/非法值直接拒绝,
不会把服务重启进坏配置);旧产物不识别
--config时警告; - 即时生效:运行中的服务自动重启应用新配置(agent 无 SIGHUP 热重载,重启秒级);
绑定后日常改配置 = 编辑文件 +
restart(或重跑config <同一文件>再校验一遍); - 持久化:
exec=的--config <绝对路径>+conffile=键(跨重启保留,600); - 展示:
status/info <name>的"配置文件"行、info配置节的文件内容快照 (敏感值掩码,info --reveal显示明文);文件丢失时明确告警并提示恢复或--clear。
5. 命令参考:部署来源
deploy --from 支持四种来源(缺省 = dev 远端最新):dev/local/release 通用于
agent 与 static,rdm 仅限 agent。
5.1 dev(缺省)—— 远端最新 / 指定提交
scripts/svc.sh <name> deploy # 缺省 = dev 远端最新(origin/main)
--from dev # 同缺省
--from dev@<sha> # 指定提交(短/长 sha 均可)
部署源来自独立部署源仓:脚本自有的干净克隆,默认 ~/.cache/agent-system-svc/src
(SVC_SRC_DIR 覆盖;首次使用自动从本仓 origin 克隆,可随时删除、下次自动重建)。
dev 源的提交解析、commits 列举与构建 worktree 物化全部基于它——更新与部署因此
不依赖当前工作区状态:主检出脏树、任意分支、甚至未 fetch 都不影响。
流程:源仓自动 fetch → git worktree add --detach runtime/.worktrees/<sha> <sha> →
递归 git submodule update --init --recursive(子模块精确落位该提交的 gitlink 组合)→
make build/make frontend → 产物复制 → worktree 回收。全程不触碰当前工作区。
- 可用提交列表:
scripts/svc.sh commits dev [N](列源仓 origin/main,自动确保+fetch); - 需要 npm/go 网络(构建在 worktree 内进行);
- worktree 回收失败会提示手动命令,不阻塞部署结果。
5.2 local —— 当前工作区(显式)
--from local
在主检出跑 make build(agent)或 make frontend(static),产物复制到服务目录。
这是唯一会写主检出的路径,写的都是 gitignore 目录(bin/、node_modules/、
modules/frontend/dist/),不产生任何 git 状态变化;也是唯一依赖当前工作区状态
的来源。适合"把我现在的工作区跑起来"。
5.3 release@<版本> —— Releases 仓制品
--from release # Releases 仓最新(HEAD)
--from release@0.1.0 # 指定版本号
--from release@43ac28d # 指定 Releases 仓提交也可
制品取 <Releases>/build/<v>/agent-system(+ plugins/),sha256 对
versions/<v>/manifest.json 校验,不符即拒绝部署;并回显该版本对应的 Dev 提交。
仓库定位:SVC_RELEASES_DIR > ../Agent-System-Releases。制品缺失时提示先在
Releases 仓跑 scripts/build-release.sh <v>。
5.4 rdm —— 下游组合仓 Agent-System-RDM(agent 专用)
--from rdm # RDM 源仓 origin/main 最新
--from rdm@<sha> # 指定提交
部署下游组合二进制(基座 + RDM 领域插件:两树一网与 rdm.* 追溯审计命令面)。
与 dev 源同构的独立源仓机制:默认 ~/.cache/agent-system-svc/rdm-src
(SVC_RDM_SRC_DIR 覆盖路径;克隆来源 SVC_RDM_ORIGIN 覆盖,缺省为 RDM 远端 ssh
地址),流程 = fetch → worktree add --detach runtime/.worktrees/rdm-<sha> →
git submodule update --init --recursive(物化 pins/ 基座钉入组合)→ make build
(纯 Go,无需 npm)→ bin/agent-system-rdm 复制为环境二进制 → worktree 回收。
与基座部署的差异:
- 插件目录按空集安装:RDM 组合插件编译进二进制(
extension.RegisterCompiled), 无声明式插件资产;管理页「插件管理」列表只反映目录插件,组合插件经/api/registry与命令面可见; - web/ 工作台:RDM 自带零构建静态工作台(直连后端 API),deploy 时复制到
runtime/<name>/htdocs/,并自动部署伴生 static 服务 (scripts/svc.sh <name>web deploy --kind static --dir runtime/<name>/htdocs); 伴生失败或GET /非 200 时 deploy 记ui-missing并以非 ok 结束。 注意htdocs/随本环境rm --purge一并删除; - 换源门禁:同名服务切换源种类(如
dev@*→rdm@*)须加--allow-source-switch;同种类换 sha(含update)不需要; - 启动兼容:组合二进制容忍基座风格的首位
serve子命令并提供version子命令;svc 的version/serve -h探测带 timeout 护栏; - 不支持
--mock与--config(组合 CLI 无此旗标);模型设置播种 (--model-*/--tavily-keys)仅基座产物支持,rdm 源跳过(登记态source=rdm@*即识别形态,跨裸 start/restart 稳定); - PG 档:
db <pg实例>绑定同样走AGENT_SYSTEM_DSN环境交付;RDM CLI 在-dsn为空时回退读该环境变量(无config explain子命令时,svc 以 ELF 内静态 引用作能力证据)。
5.4.1 常用入口 cutover 到 RDM 组合(审计能力)
增量 2 后,基座 React 前端仅在 GET /api/commands 齐备七个 rdm.inspect.* 且
enabled 时展示「追溯审计」。要把常用入口(如 dev)切到组合二进制:
- 钉 pins:Agent-System-RDM
pins/对齐 Dev 上含能力门控的 Frontend(及兼容 Backend/Core/UI),合入 RDM main 后记下rdm@<sha>(scripts/svc.sh commits rdm)。 - 备份:
scripts/svc.sh <name> backup(SQLite 一致性快照在runtime-backups/)。 - 候选(禁止双写):先
scripts/svc.sh <name> stop,再
scripts/svc.sh <cand> deploy --from rdm@<sha> --data runtime/<name>/data --port <空闲> --auth on;
验登录 //api/projects/rdmAuditReady后停候选。 - Cutover:若当前为非 rdm 源,加
--allow-source-switch:scripts/svc.sh <name> deploy --from rdm@<sha> --allow-source-switch(同名 redeploy 保留 data/auth;伴生<name>web自动就位)。失败回滚:scripts/svc.sh <name> deploy --from dev@<旧源> --allow-source-switch。 - 基座 FE 验收:
PLAYWRIGHT_API_TARGET=http://127.0.0.1:<port> npm run preview(或 Vite dev 代理);svcstatic 无/api反代。RDMweb/工作台是另一套 UI (htdocs+ 自动伴生 static),与 React SPA 门控并行可用。
5.5 辅助
scripts/svc.sh commits [dev|release|rdm] [N] # 可部署提交表(默认 20 条)
scripts/svc.sh <name> deploy --pick # 交互式编号选择
scripts/svc.sh <name> deploy --dry-run # 只打印计划
commits 表格:dev 源标注 [最新](源仓 origin/main 顶)与 [已部署:<服务名>]
(从各服务 svc.env source= 汇总);release 源额外给出版本号与对应 Dev 提交。
5.6 编译插件组合(--with)
plugins/registry.json 登记可以编进基座的插件。deploy --with 在本次 --from local
或 --from dev 的基座上生成空白导入并一次 go build -mod=mod,产物仍是
agent-system。插件不自带基座 pin;对不上就构建失败。
scripts/svc.sh dev deploy --from dev@<sha> --with rdm --dry-run
scripts/svc.sh dev deploy --from local --with rdm@<sha>
scripts/svc.sh dev deploy --with none --allow-source-switch
- 终端上没给
--with时按编号勾选,回车沿用上次;非交互沿用svc.env的plugins=,没有则不编入。 --with与--from rdm、--from release互斥。旧--from rdm仍用 RDM 仓自己的pins/构建。- 部署后核对
/api/plugins、/api/registry的compiled和/api/commands。未启用且认证开启时,用临时管理员 key 启用。失败走 release 回退。 source写成dev@<基座短提交>+rdm@<插件短提交>。plugins=记id@完整提交。update原样带上,不再询问。- 已经在跑的服务要改插件 id 集合,必须加
--allow-source-switch。第一次部署不需要。 - 登记了
web的插件最多一个,复用伴生<name>web。
SVC_COMPOSE_STUB=1 只给测试跳过真实 go build。真实组合构建与 make test-lifecycle 不在桩测试里。
6. 命令参考:全局命令
6.1 list / status(总览)
scripts/svc.sh list [--tag T] # status 等价
扫描 runtime/*/svc.env 枚举全部已登记服务:
NAME KIND STATE PID ADDR DB SOURCE TAGS
pg1 postgres 运行中 3115294 127.0.0.1:8890 - postgres@local core,local
prod agent 运行中 3117059 127.0.0.1:8791 pg1/prod release@0.1.0 stable
ui-qa static 未运行 - 127.0.0.1:9054 - dir@/tmp/static-demo ui
DB 列:agent 服务当前绑定的 <pg名>/<库名>(见 §4.5;外部 DSN 显示 外部@host:port,
未绑定显示 -)。
STATE 的判定:pidfile 存在且 /proc/<pid>/cmdline 匹配登记的 pidcheck 标记
(agent=二进制路径、postgres=pgdata 路径、static=--directory <目录>)。进程被人手动
杀掉后 STATE 自动落回"未运行",不会误报。
6.2 info(综合信息报告,只读)
scripts/svc.sh info [name] [--tag T] [--reveal]
一条命令看全本机现场:正在运行哪些服务、有哪些数据库(含实例内部库)、每个服务 的配置文件与内容快照。全程只读,不改任何登记、进程与文件。
| 用法 | 行为 |
|---|---|
info |
全景报告(下述五节全量) |
info pg1 |
单服务深度报告:状态详情 +(postgres)实例与内部库 + 配置快照 + 最近事件 |
info --tag core |
服务名/数据库/配置/事件均按标签过滤(与 list --tag 同语义) |
info --reveal |
显示默认掩码的敏感值(DB 密码、API key、DSN 密码) |
报告分节(整体走 stdout,可直接 > snapshot.txt 整页保存):
- 服务——
list同款表格(含 DB 绑定列)+ 汇总行(N 已登记 / M 运行中(agent a、postgres p、static s)); - 数据库——每个 postgres 实例一屏:运行状态/地址、版本/用户/认证/默认库/来源、 绑定服务(哪些 agent 服务指向该实例,见 §4.5)、数据目录与体量、备份份数与 最近一份;运行中的实例直接列出内部数据库及大小(未运行则注明内部库不可见);
- 配置——逐服务
runtime/<name>/svc.env的文件路径、权限与逐行内容快照,加上 全局层:.svc.env(自动加载层)、本次--config/--env文件、shell 已导出的SVC_*变量; - 最近事件——各服务 history 最近 5 条(完整历史走
history)。
敏感值默认掩码:password= 键、键名含 KEY/PASS/SECRET/TOKEN/DSN 的值整段掩为
***,exec= 里 DSN 的密码段同样掩码;--reveal 才显示明文。掩码规则与
db dsn(默认掩密码)一致,便于把报告贴进工单/日志而不泄密。
6.3 history(事件追溯)
scripts/svc.sh history [name] [N] # 默认全部服务、最近 20 条;首参为纯数字时视为条数
合并各服务 history.jsonl(按时间排序),字段:时间 / 服务 / kind / 事件 / 结果 /
来源 / pid / 详情。事件类型见 §10.2。
6.4 update(自动检索并更新)
scripts/svc.sh update [name...] [--check] [--all] [--no-fetch]
把服务刷到远端最新:fetch 独立部署源仓 → 逐服务比较登记来源与最新 → 报告并(可选) 更新。全程不依赖当前工作区状态(主检出脏树、任意分支均可更新)。
| 用法 | 行为 |
|---|---|
update --check |
只输出检测表,不改任何状态 |
update |
自动更新所有"过时且运行中"的服务 |
update dev pg1 |
点名更新(已停止的过时服务也会更新并启动) |
update --all |
范围扩大到已停止服务 |
update --no-fetch |
跳过网络 fetch,用本地已有引用比较(报告注明) |
检测表输出:服务 / 当前来源 / 最新 / 状态。状态含义:
✓ 最新——登记来源与最新一致,不动作;可更新——本地来源是远端最新的祖先,将被更新;领先远端 main(不动作)/与远端 main 分叉(不自动更新)/提交不在远端(不自动更新)——不盲目降级或强更;不适用——postgres(无代码版本)与 static--dir(外部目录)不参与。
各来源的更新动作(复用 deploy 全链路:停旧 → 构建/校验 → 启动 → 健康检查;持久化配置
与标签保留;写 update 事件入 history):
| 来源 | 更新方式 |
|---|---|
local@<sha> / dev@<sha> |
一律 deploy --from dev@<远端 main 最新>(独立部署源仓 + 隔离 worktree 构建;不 pull 主检出、不看工作树状态) |
release@<版本> |
deploy --from release@<Releases 最新版本>(sha256 校验) |
rdm@<sha> |
deploy --from rdm@<RDM 远端 main 最新>(组合二进制;见 §5.4) |
任一服务失败不中断其余服务;最终汇总 成功/失败/跳过。退出码:0 全部成功或最新;
1 有失败;2 用法错误。
6.5 recover(自恢复:拉回死亡服务)
scripts/svc.sh recover # 扫描全部已登记服务
scripts/svc.sh recover dev pg1 # 点名恢复
scripts/svc.sh recover --quiet # 静默(watchdog timer 用)
svc 服务是裸后台进程(nohup):进程崩溃、被 OOM killer 杀掉或机器重启后, 没有人把它拉回来——recover 补上这一环。
判定依据是 svc.env 的 autostart 期望态键:
| 动作 | autostart |
|---|---|
| start / restart / deploy / update 健康检查通过 | 置 1(期望在运行) |
显式 stop(含 deploy/update 的前置停机) |
移除(保持停止是明确意图) |
| 进程自行死亡(崩溃 / OOM 被杀 / 重启后) | 保留 → recover 据此拉回 |
rm 注销 |
键随 svc.env 消失,不再触碰 |
行为细节:
- 扫描顺序 postgres 在前、agent/static 在后(绑定实例先起,agent 重建 DSN 才有效);
- 只对
autostart=1且 pidfile+cmdline 校验已死的服务执行裸 start(复用持久化exec=与数据库绑定,不改任何配置、不重建产物); - 逐服务持
runtime/<name>/.start.lock(非阻塞):deploy(含构建窗口)/手动 start 持锁期间跳过本轮,不抢跑; - 每次尝试写
recover ok|fail事件(history可追溯);进程反复即退时自然限频 约 1 次/分钟(健康探测 20s + timer 周期); - deploy/update 失败(构建失败、预检拒绝)→ 服务保持停止等人工介入,watchdog 不盲目复活(前置停机已清 autostart);
- 端口被外部进程占用时 start 报错放弃(不抢不杀),下轮重试。
6.6 watchdog(自恢复看护 timer)
scripts/svc.sh watchdog install # 安装并启用 systemd 用户 timer(每分钟 recover)
scripts/svc.sh watchdog install --print # 只打印单元文件与计划
scripts/svc.sh watchdog remove # 停用并删除 timer
scripts/svc.sh watchdog status # timer 状态 + autostart=1 的服务清单
- 单元写入
~/.config/systemd/user/agent-system-svc-recover.{service,timer}(OnBootSec=1min+OnUnitActiveSec=60s),ExecStart=<本仓>/scripts/svc.sh recover --quiet; - 开机自启依赖 linger(
sudo loginctl enable-linger <user>);未开时安装会提示, 登出不受影响(KillUserProcesses 默认关,进程本就不随登出死); - 换检出目录后重装一次即可(status 会提示 timer 指向的 ROOT 与当前不一致);
- 无 systemd 用户会话的环境(如某些容器)install 会拒绝并给出 cron 降级命令:
* * * * * <ROOT>/scripts/svc.sh recover --quiet >/dev/null 2>&1。
一键自恢复配方:
scripts/svc.sh dev deploy # 启动成功 → autostart=1
scripts/svc.sh watchdog install # 之后 kill -9 / OOM / 重启,约 1 分钟内自动拉回
6.7 help
scripts/svc.sh # 一屏速览(短帮助)
scripts/svc.sh -h # 同上
scripts/svc.sh --help # 完整帮助(全部命令/参数/约束)
scripts/svc.sh help # 分主题清单
scripts/svc.sh help <主题> # 主题详解:model deploy lifecycle accounts db dbops config recovery examples safety
# accounts:实例绑定账户 CLI(create/initialize-account/list/revoke-login-key/bootstrap-admin)
agent-system 的账户命令仍接受 --dsn 和 --password,并在 stderr 打印弃用警告。
新调用使用 AGENT_SYSTEM_DSN 或模式 0600 的 --dsn-file,密码使用 --password-stdin,
明文写入独占创建的 --out-file。agent-system capabilities --json 不打开数据库。
scripts/svc.sh 仍传 --dsn / --password,切换留给账户 U5。
7. 命令参考:数据库
7.1 db create(创建实例)
scripts/svc.sh db create pg1 [--port 8890] [--host 127.0.0.1] \
[--user agent] [--password <pw>] [--db app] \
[--auth-mode scram-sha-256|md5] [--tag T]...
initdb到runtime/pg1/pgdata(目录 700;密码经 pwfile 传递,即用即删);- 默认用户
agent、默认库agent、认证scram-sha-256(pg_hba 全线); --password缺省时自动生成(18 字节十六进制随机);- 只登记不启动——
scripts/svc.sh pg1 start启动;端口占用预检在 create 时已做。
7.2 start / stop / restart
start:pg_ctl -w -t 30 -l pg.log -o "-p <port> -k <环境目录> -c listen_addresses=…"——socket 目录指向环境目录(不落 pgdata,克隆时不随数据目录传播); 首次启动自动创建登记库(已存在则跳过);就绪判定pg_isready;stop:pg_ctl -m fast(回滚事务、断连接、优雅退出);失败退 SIGTERM; SIGKILL 永远不用于 postmaster;restart:stop + start。
7.3 db dsn / db ls
scripts/svc.sh db dsn pg1 # postgresql://agent:***@127.0.0.1:8890/agent(掩码)
scripts/svc.sh db dsn pg1 --reveal # 显示真实密码
scripts/svc.sh db dsn pg1 --db other # 指定库名
scripts/svc.sh db ls pg1 # 实例内数据库列表(须运行中)
DSN 走 stdout(提示走 stderr),可直接组合:--dsn "$(scripts/svc.sh db dsn pg1 --reveal)"。
7.4 换端口 / 换主机
见 §4.3 set。要点:运行中必须 --apply;登记、DSN、pg_ctl 启动参数联动更新;
数据不动。换端口 = 同机内"迁移"。
8. 数据库运维手册
8.1 备份
scripts/svc.sh db backup pg1 # 备份登记库
scripts/svc.sh db backup pg1 --db app # 指定库
scripts/svc.sh db backup pg1 --out /tmp/x.dump # 自定义输出路径
scripts/svc.sh db backups pg1 # 列表(时间 / 大小 / 文件名)
- 实现:
pg_dump -Fc走 TCP(实例须运行中),默认落到runtime/pg1/backups/<UTC时间戳>-<库>.dump; - 完成后
pg_restore --list自检归档有效性,失败即删除该文件并记backup fail; - 备份只增不删;随
rm --purge一并删除。
8.1.1 agent 数据目录快照(SQLite 一致性备份)
scripts/svc.sh dev backup # 快照 data/ + svc.env + history.jsonl
scripts/svc.sh dev backup --keep 3 # 备份后只保留最近 3 份
scripts/svc.sh dev backup --out /mnt/bk/dev1 # 自定义快照目录(不参与自动清理)
scripts/svc.sh dev backups # 快照列表(时间 / 大小 / 文件数)
scripts/svc.sh dev backups --prune --keep 3 # 手动清理旧快照
- 位置:
runtime-backups/<name>/<UTC时间戳>/——在 runtime/ 之外。2026-10-11 发生过runtime/被整体误删的事故(SQLite 聊天/设置数据全失,且无任何备份); 放在服务目录内的备份会跟着一起消失,因此快照根独立于 runtime/; - 一致性:每个
*.db经sqlite3 .backup导出(并入 WAL 内容,服务运行中也可 安全备份)并做PRAGMA integrity_check自检;-wal/-shm不入快照。无 sqlite3 时拒绝备份,不回退裸拷贝(裸拷会漏 WAL 得到撕裂副本); - MANIFEST:快照内
MANIFEST(600)记录服务、来源版本与逐文件 sha256,恢复时 可校验完整性。非 SQLite 的*.db文件(无文件头)保留原样副本并告警; - 保留策略:默认保留最近 7 份(
--keep N调整;只清理符合时间戳命名的快照目录, 不动--out指定的自定义目录);rm <name> --purge会连带删除runtime-backups/<name>/,rm(不带 --purge)保留快照; - 边界:本命令是数据面快照,不含 restore(恢复到新目标、manifest 合同与
外部效果对账属恢复方案 U6,见
modules/documents/docs/proposals/2026-10-12-service-restart-data-recovery.md); postgres 实例请用db backup,static 服务无数据目录。
8.2 恢复
scripts/svc.sh db restore pg1 --file <dump> # 恢复到登记库
scripts/svc.sh db restore pg1 --file <dump> --db app2 # 恢复到指定库
scripts/svc.sh db restore pg1 --file <dump> --db app --force # 覆盖非空库
目标语义:
| 目标库状态 | 行为 |
|---|---|
| 不存在 | 建库 → 恢复 |
| 存在且无用户表 | 直接恢复 |
| 存在且有用户表 | 拒绝;--force 才 DROP DATABASE … WITH (FORCE) → 重建 → 恢复 |
恢复前先校验文件是合法 pg_dump 归档;完成后回显恢复出的表数;全程事件入 history。
这是脚本里唯一可能删除数据的操作,必须显式 --force 解锁。
8.3 克隆实例(绑新端口)
scripts/svc.sh db clone pg1 pg1test --port 8896 # 物理克隆(默认)
scripts/svc.sh db clone pg1 pg1test --password newpw --tag test # 换密码/加标签
scripts/svc.sh db clone pg1 pg1test --online # 在线克隆(不停源)
物理克隆(默认):源在运行则短暂 pg_ctl -m fast 停止(一致性要求)→
cp -a pgdata(剔除 postmaster.pid / postmaster.opts;socket 本就不在 pgdata)→
登记目标(端口 = --port 或派生;用户/库/认证继承,密码可 --password 覆盖;标签
继承 + --tag 追加;source=clone@<源名>)→ 自动复启源。目标不自动启动——按需
scripts/svc.sh pg1test start。
--online:不停源,把源的登记库 dump/restore 到目标——要求目标已
db create + start。适合源不能停的场景。
8.4 跨实例搬库
scripts/svc.sh db copy pgdev pgprod --db app # pgdev.app → pgprod.app
scripts/svc.sh db copy pgdev pgprod --db app --force # 目标非空时覆盖
- 双方实例都须运行中(走 TCP dump/restore);
--db X同时命名源库与目标库;缺省取源的登记库;- 目标库非空须
--force(语义同 restore);成功后两侧 history 各记一条copy。
8.5 端口规划建议
- 默认派生区 8800–9099 按服务名哈希,天然分散;多实例并存时建议显式
--port分配 并记入标签(如--tag port-8896); - 换端口用
set --apply;克隆用--port指定;两者都会做占用双重校验。
8.6 物理备份 + WAL 归档 / PITR 到新目标
db backup 只做 pg_dump -Fc。物理备份、WAL 归档和按时间点恢复到新数据目录不在 svc.sh 里,2026-10-12 在隔离实例 u7bk 上实跑过,入口是 tests/ops/run_backup_drill.sh,结果目录 tests/ops/results/backup-20261012-153441(SELF)。合同数据量(100 万消息 / 10 万知识分块)下:基础备份与 pg_dump 各约 1 秒,逻辑恢复约 4 秒,从 pg_ctl stop -m immediate 到恢复后应用冒烟通过约 4 秒,最后一条仍在库里的标记比故障时刻早 64 秒。
在源实例上打开归档后必须重启(wal_level 与 archive_mode 不热加载):
psql -d agent -c "ALTER SYSTEM SET wal_level = 'replica'" \
-c "ALTER SYSTEM SET archive_mode = 'on'" \
-c "ALTER SYSTEM SET archive_timeout = '60s'" \
-c "ALTER SYSTEM SET archive_command = 'test ! -f /secure/wal/%f && cp %p /secure/wal/%f && chmod 600 /secure/wal/%f'"
scripts/svc.sh u7bk stop && scripts/svc.sh u7bk start
归档目录先 chmod 700。备份:
pg_basebackup -h 127.0.0.1 -p <源端口> -U agent -D /secure/base -Fp -X stream -c fast
pg_verifybackup /secure/base
scripts/svc.sh db backup u7bk --db <应用库> --out /secure/logical.dump
pg_restore --list /secure/logical.dump
演练里 pg_stat_archiver.failed_count 为 0,archived_count 为 80。备份目录 700、文件 600。静态加密和凭据轮换这次没有做。归档和数据在同一块盘上,不能代表存储整盘损坏。
制造故障用的是 pg_ctl -D <pgdata> stop -m immediate,然后把源数据目录挪走。恢复到新目录:
cp -a /secure/base/. /secure/restore-pgdata/
# postgresql.auto.conf 末尾追加(后写的覆盖前面的 archive_mode):
# archive_mode = 'off'
# restore_command = 'cp /secure/wal/%f %p'
touch /secure/restore-pgdata/recovery.signal
rm -f /secure/restore-pgdata/postmaster.pid
pg_ctl -D /secure/restore-pgdata -l /secure/postgres.log -t 600 -w \
-o "-p <新端口> -k /tmp/u7bk-18997" start
-k 必须是短路径。结果目录下的 socket 路径超过 107 字节时,postmaster 会拒绝启动(同日 backup-20261012-150713 的 restore/postgres.log)。等到 SELECT pg_is_in_recovery() 返回 f 再让应用连这个新端口。逻辑路径是在恢复实例上建空库,再 pg_restore --no-owner;表计数与 dump 当时的快照一致。
工件正文的库内 content_path 是绝对路径(blobstore.Local.Put 返回最终文件路径)。快照要还原到原来的目录,换目录后按库里的路径取文件会失败。2026-10-12 那次没有跑通上传:未设置 BLOBSTORE_S3_ENDPOINT 时,空的 *S3 放进对象存储接口后不再等于 nil,上传在 blobstore.(*S3).Put 空指针 panic,K4 记 FAIL。2026-10-13 已在 store 边界把这种带类型空指针当作未配置并回落本地目录(Backend 46073d30;合入 main 的 dc736e3 与该提交同树)。同日在 Dev a83588dd 上重跑 tests/ops/run_backup_drill.sh,结果目录 tests/ops/results/backup-20261013-013354(SELF,原始结果另存于仓外 u7-k4-retest-preserved-20261013)。K1–K7 都过:RPO 65 秒,RTO 3 秒;K4 为 5 个工件 checked=5、missing=0、mismatch=0,跨用户访问项目和工件均为 404。绝对路径这一条仍然有效,快照继续还原到原目录。
8.7 PG / Redis 断连处置
2026-10-12 隔离实例 u7drill 实跑,入口 tests/ops/run_fault_drill.sh --fault all,结果目录 tests/ops/results/fault-20261012-142734(SELF)。PostgreSQL 用 svc.sh <name> stop(pg_ctl -m fast)和直接对自有 postmaster 发 SIGKILL 各做一次,断连窗口 60 秒。Redis 用 --save '' --appendonly no 拉起后再 SIGKILL。
PostgreSQL:
- 断连期间写入在 10 秒内返回明确错误(这次是 401),两个应用进程都还在。窗口从停机返回后再等 2 秒才开始计,避免把已经在途的成功写算进去。
- team 档新进程日志为「拒绝就绪(不回落 SQLite)」,工作目录不出现
*.db。 /api/health不探数据库。恢复是否完成,用新会话上的真实写入判断:同一会话上连续写会被进行中的 mock run 以 409 挡住,那不是库没起来。这次从 postgres 就绪到两侧各 5 次写入成功不到 1 秒,没有重启应用。- 断连窗口里失败的 requestId 用同一 id 重放后各对应一条消息;
dup_seq=0,seq 无洞,非终态 run 为 0。
Redis(限额 20 次/分钟,两实例各突发 40 个读):
- 连通时两实例合计放行 20、拒绝 60,限额是共享的。
- 进程被杀掉之后合计放行 40、拒绝 40,没有 5xx。限额变成按实例计。已确认的写入仍在库里。
- 只把 Redis 拉起来、不重启应用,合计放行回到 20。
rl:*的 PTTL 都大于 0,没有永久计数键。 - 日志里看不到降级计数。
Degraded()没有暴露点。 - Redis 不通时新起的 team 进程退出,日志含
redis ... connection refused。 runtime.redisAddr和runtime.rateLimitPerMin属于默认 ownerlocal。PUT /api/settings/user只写调用者自己的设置。演练是在隔离库里用 SQL 写入settings(owner='local', scope='user'),并且保留runtime.mock=true。这不是产品配置入口。
9. 配置系统
9.1 优先级
命令行参数
> --config FILE(--env FILE 别名)
> svc.env 持久化(addr=、exec=、dbref=/dsn=/db=——数据库配置专键)
> SVC_<NAME>_* 环境变量(.svc.env 自动加载,同层)
> 默认值
数据库配置由脚本在启动时统一解析(start/restart/deploy/update/db
共用单点):托管绑定(dbref=)从实例登记重建 DSN,外部直连读受保护 dsn= 字段,
配置文件读 conffile 内的 dsn 键。连接串不进 exec= 与命令行,经
AGENT_SYSTEM_DSN 环境变量交付进程。修改端口、监听地址或部署版本不改变数据库
选择;绑定失效或配置冲突时拒绝启动,绝不静默回退内嵌 SQLite(见 §9.4)。
9.2 配置文件
dotenv 格式:KEY=VALUE,支持 # 注释、成对引号、export 前缀;只识别合法变量名,
不 source 执行文件内容(防注入)。建议 .env.dev / .env.prod 等加入 gitignore。
scripts/svc.sh prod deploy --config .env.prod
scripts/svc.sh prod deploy --env .env.prod # 等价
9.3 环境变量
| 变量 | 说明 |
|---|---|
SVC_<NAME>_ADDR/DATA/DSN/AUTH/MOCK |
按服务覆盖(NAME 大写、-→_,如 staging → SVC_STAGING_ADDR) |
SVC_RELEASES_DIR |
Releases 仓路径(默认 ../Agent-System-Releases) |
SVC_SRC_DIR |
独立部署源仓路径(默认 ~/.cache/agent-system-svc/src) |
SVC_PG_BIN |
PostgreSQL 工具链 bindir |
SVC_MODEL_BASE/KEY/NAME |
主模型(播种 settings.models.chat) |
SVC_MODEL_FALLBACKS |
主模型回退链(逗号分隔,播种 settings.models.chat.fallbacks) |
SVC_LIGHT_BASE/KEY/NAME |
轻量模型 |
SVC_VISION_MODEL |
视觉兜底模型(播种 settings.models.vision.model) |
SVC_VISION_CAPABLE |
可看图模型清单(逗号分隔,播种 settings.models.vision.capable) |
SVC_MINIMAX_BASE/KEY |
原 MiniMax 端点(播种 settings.models.reserve.minimax,不替换当前对话模型) |
SVC_MINIMAX_CHAT/FAST |
原 MiniMax 对话模型与快速模型名 |
SVC_TAVILY_KEYS |
Tavily 密钥池(逗号分隔) |
9.4 持久化语义
deploy/start成功后把当前态写入svc.env(600,临时文件原子替换):addr、exec(agent 上次完整启动参数,只存非敏感项)等;启动失败不覆盖上次配置;- 裸
start/restart复用exec=;任何显式参数(CLI、--config、SVC_*)出现即 整体重建启动参数;两种路径共用同一数据库配置解析——数据库选择与认证设置不随 端口/地址/版本变化,conffile引用随重建保留; - 数据库配置可恢复:托管绑定
dbref=<pg>/<库>在启动时由实例登记重建 DSN (实例端口/密码变更自动跟随,密码不重复保存);显式外部直连(deploy --dsn)持久化 为受保护dsn=字段;显式解绑记录db=sqlite(清除dbref/dsn,此后残留的SVC_*_DSN不再悄悄重新选库,显式--dsn仍可覆盖);存量exec=里的--dsn首次启动时自动迁移为上述形态(幂等); - 绑定失效拒绝启动(返回非零并输出脱敏原因,不静默回退内嵌 SQLite):
dbref实例未登记/类型错误/登记字段不全、已选 PostgreSQL 但无可恢复 DSN、直接 DSN 与dbref指向不同库、配置文件dsn与绑定不一致、mock 档与数据库配置共存、 产物不消费环境变量 DSN(AGENT_SYSTEM_DSN,旧制品)。restart/deploy在停止 旧进程之前完成这些静态检查——配置性拒绝不制造停机窗口; - 标签合并去重、跨重启保留;postgres 的用户/密码/库名/认证模式持久化,重启不丢。
10. 登记文件与事件流
10.1 svc.env 字段
| 键 | 说明 |
|---|---|
kind |
agent / static / postgres(缺省视为 agent) |
env |
服务名 |
pid |
最近启动 pid |
addr |
监听地址(持久化,下次 start 复用) |
tags |
标签(逗号分隔) |
source |
部署来源:local@<sha> / dev@<sha> / release@<版本> / rdm@<sha> / dir@<路径> / postgres@local / clone@<名> |
version |
产物版本串(agent-system version 输出) |
started |
最近启动时间 |
pidcheck |
进程判定标记(见 §6.1) |
exec |
agent 上次完整启动参数(非敏感项;DSN 不在其中,经环境变量交付) |
db |
agent 数据库模式:pg / sqlite(显式解绑)/ mock;从未配置则无此键 |
dbref |
agent 托管数据库绑定 <pg名>/<库名>(启动时由实例登记重建 DSN;见 §4.5) |
dsn |
agent 外部直连 DSN(受保护;未命中本机实例登记的直连存放于此) |
conffile |
agent 配置文件绝对路径(<name> config 命令写入,跨重启保留;见 §4.6) |
storage_mode |
legacy(缺省,SQLite/宿主 PG)/ container(三库+文件根合同;见 deploy/data-ownership.md) |
env_class |
环境身份:prod / test / verify(container 必需) |
files_root |
持久文件根绝对路径(不相对 checkout 解析;container 必需) |
compose_project |
Compose 项目名(同 runtime 唯一;冲突在修改前拒绝) |
base_dbref / base_dsn_ref / base_role_ref / base_port |
基座逻辑库绑定(dsn/role 仅为受保护文件路径引用) |
rdm_dbref / rdm_dsn_ref / rdm_role_ref / rdm_port |
RDM 领域库绑定 |
kb_dbref / kb_dsn_ref / kb_role_ref / kb_port |
知识库绑定 |
user / password / dbname / authmode / pgdata |
postgres 专属 |
htdocs |
static 专属 |
容器存储绑定由 manifest.json 冻结为身份锚点(与 data_root/db_* 同级);旧配置
升级保留 instance_id。storage_mode=container 时缺任一必需绑定、错库、错文件根、
foreign marker、同名 Compose 或端口冲突均拒绝启动/修改,不静默建空库、不回退默认。
示例键见 deploy/env-bindings.example.env。
10.2 history.jsonl 事件
一行一个 JSON:ts / event / env / kind / source / result / pid / addr / detail。
| event | result | 触发 |
|---|---|---|
create |
ok | db create / 物理克隆登记 |
deploy |
begin / ok / fail | agent/static 部署全程 |
start |
ok / fail / warn | 启动(fail 含"进程即退/健康检查超时";warn 为进程起但探活未过) |
stop |
ok / fail | 停止(含 pg_ctl fast / SIGTERM 兜底说明) |
set |
ok | 登记变更(addr 旧 → 新;db bind → <pg>/<库> / db unbind) |
backup |
ok / fail | 备份(文件、大小) |
restore |
ok / fail | 恢复(库、文件、表数) |
clone |
ok / fail | 克隆(物理/在线) |
copy |
ok / fail | 搬库 |
update |
begin / ok / fail | 自动更新(from → to) |
rm |
ok | 注销(是否 purge) |
11. 安全与边界
- 写入边界:只写
runtime/<name>/、runtime/.worktrees/与独立部署源仓 (SVC_SRC_DIR,默认~/.cache/agent-system-svc/src,可随时删除重建);不改主检出 git 状态;--from local是唯一写主检出构建产物的路径(均 gitignore 目录),也是 唯一依赖当前工作区状态的来源;rm --purge只删已登记服务的runtime/<name>/。 - 进程边界:只停"pidfile + cmdline 标记"双校验通过的进程;postgres 走
pg_ctl -m fast,失败退 SIGTERM,SIGKILL 不用于 postmaster;agent/static TERM 10s 后才 KILL。 - 端口边界:被他人占用报错不抢;显式端口双重校验(登记 + 监听);派生端口避让。
- 秘密与数据:
svc.env(含 DB 密码)与备份 600;环境/数据目录 700;DSN 默认掩码; dotenv 不执行文件内容;restore/copy覆盖非空库必须显式--force;备份/克隆只增不删。 - 兼容承诺:
kind=缺省视为 agent;旧格式svc.env(仅 addr/data)在list/status正常显示;存量exec=里的--dsn首次启动自动迁移为可恢复配置(幂等); 旧产物二进制不识别的--auth/--config自动省略并提示;不消费环境变量 DSN 的 旧产物在需要交付数据库配置时拒绝启动/部署(不静默按内嵌 SQLite 运行)。
12. 退出码
| 码 | 含义 |
|---|---|
| 0 | 成功(含"已在运行,无需操作") |
| 1 | 运行失败(构建失败、启动失败、校验不过、拒绝覆盖等) |
| 2 | 用法错误(未知参数/服务名/来源、缺参数、服务未登记等) |
13. 故障排查
SQLite 档的活动服务锁在打开数据库之前取得,锁文件是解析后的
agent-system.db.lock(不删除)。同一数据文件只允许一个 serve 或 migrate。
svc.sh 的 .start.lock 只串行化脚本,挡不住直接启动二进制。账户短事务命令不拿这把锁。
显式迁移:
agent-system migrate --data <数据目录>
agent-system migrate --dsn "$AGENT_SYSTEM_DSN" # PostgreSQL,不取文件锁
数据库被替换,或守卫建立后锁文件被删掉重建,启动会拒绝。确认是同一次实例后:
agent-system serve --data <数据目录> --adopt-instance-guard
agent-system migrate --data <数据目录> --adopt-instance-guard
硬链接别名不能用 adopt 绕过。PostgreSQL 档在应用连接上读到 fsync、
full_page_writes 或 synchronous_commit 为 off 时拒绝启动。
| 症状 | 处置 |
|---|---|
端口被占用(非本服务) |
按提示的占用者处理,或换端口(--port / set --port --apply) |
instance guard: sqlite service already running |
已有进程占用同一 SQLite 文件:停掉那个服务,或换独立数据目录。migrate 同样会被拒绝 |
instance guard: sqlite database replaced |
库文件 inode 与锁记录不符:确认不是拿错目录后,用 --adopt-instance-guard |
instance guard: recorded sqlite database is missing |
锁记录指向的库不在了:恢复该文件,或在确认后 adopt |
instance guard: lock file was rebuilt |
守卫已建立后锁文件被重建:确认后 --adopt-instance-guard |
instance guard: sqlite database has a hardlink alias |
去掉硬链接别名后再启动;adopt 不能放行 |
durability: |
SQLite 未保持 WAL + synchronous FULL,或 PostgreSQL 的 fsync / full_page_writes / synchronous_commit 为 off |
进程启动后即退出 |
scripts/svc.sh <name> logs 200 / postgres 看 runtime/<name>/pg.log;history 里有 fail 记录 |
20s 健康检查未通过 |
进程活着但未响应:查日志与端口;agent 常见原因是 DSN 指向未启动的库 |
该产物不支持部分启动参数 |
提示而非错误:旧版本制品自动省略了不支持的 flag;需要该能力请部署新提交 |
拒绝启动:dbref=… 无法重建 DSN |
实例未登记/登记不全:恢复实例登记,或 db <pg名> 重新绑定;不会回退 SQLite |
拒绝启动:直接 DSN 与 dbref 指向不同数据库 |
配置冲突:重新绑定或 db --clear 后重新直连,二者取一 |
拒绝启动:产物不消费环境变量 DSN |
旧制品:deploy 新提交,或改用携带 dsn 的配置文件(<name> config) |
拒绝启动:mock 档与数据库配置冲突 |
先 db --clear 再用 mock,或去掉 --mock / SVC_*_MOCK |
拒绝启动:非回环地址且认证判定为关 |
绑定数据库 / 显式 --auth on / 回环地址 / SVC_*_INSECURE_LISTEN=1 显式放行 |
环境产物缺失 |
该环境没有部署过(或旧版脚本部署):先 deploy |
pg_ctl start 失败 |
看 pg.log 常见:端口被占、pgdata 权限(须 700)、postmaster 残留 |
pidfile 指向的进程已不存在 |
脚本自动清理 pidfile;直接重新 start 即可 |
sha256 校验失败 |
制品与 manifest 不符:不要部署;在 Releases 仓重新 build |
部署源仓无此提交 |
源仓已自动 fetch 仍无此对象:确认 sha 正确(scripts/svc.sh commits dev) |
worktree 回收失败 |
按提示手动 git -C <部署源仓> worktree remove --force runtime/.worktrees/<sha> |
子模块物化失败 |
网络或子模块对象问题:重试(部署源仓会自动 fetch);或检查 git 远端可达性 |
目标库已有 N 张表——拒绝覆盖 |
确认后加 --force(会 drop 重建) |
提交不在远端(不自动更新) |
该服务部署自未推送的本地提交(如 --from local):重新 deploy(缺省即远端最新)即可纳入更新轨道 |
list 里服务显示未运行但进程在 |
手工起的进程不满足 pidcheck 标记:脚本不会动它;纳入管理请走 svc 部署 |
14. 配方索引
# ① 演示后端(缺省部署远端 main 最新;--from local 构建当前工作区)
scripts/svc.sh dev deploy --mock
scripts/svc.sh dev deploy --from local --mock
# ② 建库 + 后端连库
scripts/svc.sh db create pg1 --port 8890
scripts/svc.sh pg1 start
scripts/svc.sh prod deploy --from dev@<sha> --dsn "$(scripts/svc.sh db dsn pg1 --reveal)"
# ③ 按提交部署 + 回退
scripts/svc.sh commits dev
scripts/svc.sh v1 deploy --from dev@73b2802 --tag test
scripts/svc.sh v1 deploy --from release@0.1.0
# ④ 双版本并行对比
scripts/svc.sh a deploy --from dev@<shaA> --port 8801
scripts/svc.sh b deploy --from dev@<shaB> --port 8802
# ⑤ 前端静态服务
scripts/svc.sh ui deploy --kind static --from dev@<sha>
scripts/svc.sh ui2 deploy --kind static --dir ../frontend/dist
# ⑥ 备份 → 改坏 → 恢复
scripts/svc.sh db backup pg1
scripts/svc.sh db restore pg1 --file runtime/pg1/backups/<最新>.dump --force
# ⑦ 克隆隔离环境
scripts/svc.sh db clone pg1 pg1test --port 8896
scripts/svc.sh pg1test start
scripts/svc.sh qa deploy --from dev@<sha> --dsn "$(scripts/svc.sh db dsn pg1test --reveal)"
# ⑧ 开发库搬到 prod 实例
scripts/svc.sh db copy pgdev pgprod --db app --force
# ⑨ 换端口
scripts/svc.sh set pg1 --port 8895 --apply
# ⑩ 把服务刷到远端最新(主检出脏树/任意分支均可)
scripts/svc.sh update --check # 看哪些落后
scripts/svc.sh update # 更新过时且运行中的(数据与配置保留)
scripts/svc.sh update dev # 只刷新指定服务
# ⑪ 总览与追溯
scripts/svc.sh list --tag test
scripts/svc.sh history pg1 50
本文档对应 scripts/svc.sh(Agent-System-Dev 仓)。命令行为以实现为准;发现文档与
实现不一致时,以任务卡记录的方式提出修正。