Add read-only svc.sh info report for services, databases, and config snapshots

Why: svc.sh could show a service table (list), per-instance DB names one at a
time (db ls), and per-service status, but had no single view answering "what
is running, which databases exist, and what is configured" -- the questions
asked when inspecting or handing over a machine.

Evidence: exercised in the task worktree runtime -- empty-state report; fake
agent/static/postgres registrations proving masking (exec DSN password,
password=, SVC_TAVILY_KEYS -> ***, non-secret values verbatim; --reveal shows
plaintext); a real postgres instance (db create r-pg --port 18901, start,
extra orders db, one backup) whose info section lists inner databases with
sizes, pgdata size and backup stats; regressions list/history/--help/help
lifecycle/make svc-info; info stdout carries the full report (stderr 0 bytes)
so it can be redirected as a snapshot. Full log in the task card.

Impact: new read-only `scripts/svc.sh info [name] [--tag T] [--reveal]`
command (also `make svc-info`): service table + running summary, postgres
instances incl. inner databases, per-service svc.env content snapshots plus
global .svc.env / --config / exported SVC_* vars, and recent events. Secrets
are masked by default. No deploy/lifecycle/db-ops behavior changes; `info`
becomes a reserved service name; docs/svc.md gains section 6.2 (former
6.2-6.4 shifted to 6.3-6.5).

Refs: task card .task/feature-svc-info.md (same doc as the PR); docs/svc.md
This commit is contained in:
2026-10-04 12:42:49 +00:00
parent 0c253bf407
commit 106ef07ef0
4 changed files with 344 additions and 18 deletions
+34 -5
View File
@@ -6,11 +6,29 @@
- 权威仓:ssh://git@git-ssh.u1680832.nyat.app:31411/weichen/Agent-System-Dev.git;目标分支:main;R0(initial_base_sha)= 1263e7f7d10eb4d58fcfc39a7cd4c5c963a30ff6
- 工作区:/home/weichen/projects/Agent-System-Dev-wt-feature-svc-info(worktree);分支:feature/svc-info
- 授权与交付终点:<本次允许的动作与终点;未授权事项写明>
- 授权与交付终点:实现 `scripts/svc.sh info` 综合信息报告(只读);终点 = PR 合入 main(附 precheck / task-sync / 功能验证三段证据)
## 目标与非目标
<预期结果、受影响入口/模块/契约;明确不做什么。>
目标:
- 新增全局子命令 `scripts/svc.sh info [name] [--tag T] [--reveal]`,一条命令展示本机现场:
运行中的服务(list 表格 + 运行汇总)、postgres 实例详情(含运行中实例的**内部数据库
列表与大小**、数据目录/备份体量)、配置文件与内容快照(逐服务 svc.env、全局 .svc.env、
本次 --config、shell 层 SVC_* 变量)、最近事件。
- 敏感值默认掩码(password=、键名含 KEY/PASS/SECRET/TOKEN/DSN、DSN 内密码段),
`--reveal` 才显示;报告整体走 stdout 便于整页保存。
- 同步更新:usage/usage_short/help(lifecycle、model 保留字补全 update/info)、docs/svc.md
(新 §6.2,原 6.2-6.4 顺延 6.3-6.5)、Makefile(svc-info 目标 + help)。
非目标:不改 deploy/生命周期/db 运维任何行为;不做 JSON 输出;不涉 Web UI。
## 实现摘要
- `info` 加入保留字(RESERVED_ENVS 与 help model 文档同步补全)。
- 复用与抽取:`registered_names`(global_list 复用)、`history_print_rows`(cmd_history 复用);
新增 mask_dsn/mask_kv(掩码)、info_pg_block/info_config_block/info_global_config/
info_names_filtered、cmd_info。
- main() 分派 `info` 子命令;make svc-info。
## 同步记录
@@ -20,6 +38,17 @@
## 结果与验证(完成后填写)
- 结果:
- 验证:
- 影响与剩余项:
- 结果:已实现。`svc.sh info`(全景:服务表+汇总/数据库实例+内部库/配置快照/最近事件)、
`info <name>`(单服务深度)、`--tag` 过滤、`--reveal` 显示敏感值;退出码:0 成功、2 用法错误。
- 验证(worktree runtime 内实测,2026-10-04):
- `bash -n` 通过(shellcheck 本机不可用);
- 空 runtime:五节报告友好空态,exit 0;
- 伪造登记(agent+DSN 密码 / static / postgres 停机 + 备份文件 + .svc.env 含
SVC_TAVILY_KEYS):全景报告正确;掩码实测——exec 内 DSN 密码→`***`、`password=***`、
`SVC_TAVILY_KEYS=***`、非敏感值原样;`--reveal` 显示明文;
- 真实 postgres:`db create r-pg --port 18901` → start → 建额外库 orders → db backup →
info 数据库节显示"运行中 pid、内部库 postgres/agent/orders 及大小、pgdata 43M、
备份 1 份";`rm r-pg --purge` 回收;测试现场(伪造登记/.svc.env)全部清除;
- 回归:`list` / `history` / `--help` / `help lifecycle` / `make svc-info` 均正常;
`info` stdout 纯净(stderr 0 字节),可整页重定向。
- 影响与剩余项:无行为变更(info 只读);剩余项无。
+5 -1
View File
@@ -13,7 +13,7 @@ PLUGINS := plugins
COMBO := core=$(shell git -C $(CORE) rev-parse --short HEAD 2>/dev/null)+backend=$(shell git -C $(BACKEND) rev-parse --short HEAD 2>/dev/null)+frontend=$(shell git -C $(FRONTEND) rev-parse --short HEAD 2>/dev/null)+ui=$(shell git -C $(UI) rev-parse --short HEAD 2>/dev/null)+agentcmd=$(shell git -C deps/agent-command rev-parse --short HEAD 2>/dev/null)+documents=$(shell git -C modules/documents rev-parse --short HEAD 2>/dev/null)+native=$(shell git -C deps/agent-native rev-parse --short HEAD 2>/dev/null)+squad=$(shell git -C deps/agent-squad rev-parse --short HEAD 2>/dev/null)+intent=$(shell git -C deps/agent-intent rev-parse --short HEAD 2>/dev/null)
VERSION := $(shell git describe --tags --always --dirty 2>/dev/null)+$(COMBO)
.PHONY: help sync status build frontend svc svc-list svc-commits svc-history svc-update test-backend test-frontend test-ui test-core test-agentcmd test-pins test-libs dev pin clean setup precheck task-start task-sync task-close task-finish version
.PHONY: help sync status build frontend svc svc-list svc-info svc-commits svc-history svc-update test-backend test-frontend test-ui test-core test-agentcmd test-pins test-libs dev pin clean setup precheck task-start task-sync task-close task-finish version
version:
@echo "agent-system $(VERSION)"
@@ -47,6 +47,7 @@ help:
@echo "make test 全量测试(boundaries + pins + libs + core + backend + frontend + ui + agent-command)"
@echo "make svc 服务级操作:make svc ENV=<name> [ACTION=deploy|start|stop|restart|status|logs] [ARGS='--from dev@<sha> --tag ...']"
@echo "make svc-list 服务总览:make svc-list [ARGS='--tag core'];数据库运维:scripts/svc.sh db create|dsn|backup|backups|restore|clone|copy|ls|set"
@echo "make svc-info 综合信息报告(只读):make svc-info [ARGS='pg1 --reveal']——运行服务/数据库实例与内部库/配置快照"
@echo "make svc-commits 列出可部署提交:make svc-commits SRC=dev|release [N=20]"
@echo "make svc-history 事件历史:make svc-history [ENV] [N=20](完整文档:docs/svc.md)"
@echo "make svc-update 自动更新过时服务:make svc-update [NAMES='dev pg1'] [ARGS='--check --all']"
@@ -128,6 +129,9 @@ svc:
svc-list:
scripts/svc.sh list $(ARGS)
svc-info:
scripts/svc.sh info $(ARGS)
svc-commits:
@test -n "$(SRC)" || (echo "用法:make svc-commits SRC=dev|release [N=20]"; exit 2)
scripts/svc.sh commits $(SRC) $(N)
+36 -4
View File
@@ -67,6 +67,7 @@ scripts/svc.sh prod deploy --from dev@<sha> \
# 日常操作
scripts/svc.sh list # 全服务总览
scripts/svc.sh info # 综合报告:运行服务/数据库/配置快照
scripts/svc.sh pg1 restart # 重启(复用上次配置)
scripts/svc.sh prod logs 100 # 跟日志
scripts/svc.sh history # 事件追溯
@@ -87,7 +88,7 @@ scripts/svc.sh update # 自动更新过时且运行
`^[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 version`。
restart logs help info version`。
三个名字有默认端口:`dev` = `127.0.0.1:8788`,`prod` = `127.0.0.1:8787`;其余名字按
名称哈希在 **8800–9099** 确定性派生端口,与已登记端口或正在监听的端口冲突时自动向后
@@ -296,7 +297,38 @@ STATE 的判定:pidfile 存在且 `/proc/<pid>/cmdline` 匹配登记的 `pidch
(agent=二进制路径、postgres=pgdata 路径、static=`--directory <目录>`)。进程被人手动
杀掉后 STATE 自动落回"未运行",不会误报。
### 6.2 history(事件追溯)
### 6.2 info(综合信息报告,只读)
```bash
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` 整页保存):
1. **服务**——`list` 同款表格 + 汇总行(`N 已登记 / M 运行中(agent a、postgres p、static s)`);
2. **数据库**——每个 postgres 实例一屏:运行状态/地址、版本/用户/认证/默认库/来源、
数据目录与体量、备份份数与最近一份;**运行中的实例直接列出内部数据库及大小**
(未运行则注明内部库不可见);
3. **配置**——逐服务 `runtime/<name>/svc.env` 的文件路径、权限与逐行内容快照,加上
全局层:`.svc.env`(自动加载层)、本次 `--config/--env` 文件、shell 已导出的
`SVC_*` 变量;
4. **最近事件**——各服务 history 最近 5 条(完整历史走 `history`)。
敏感值默认掩码:`password=` 键、键名含 `KEY/PASS/SECRET/TOKEN/DSN` 的值整段掩为
`***`,`exec=` 里 DSN 的密码段同样掩码;`--reveal` 才显示明文。掩码规则与
`db dsn`(默认掩密码)一致,便于把报告贴进工单/日志而不泄密。
### 6.3 history(事件追溯)
```bash
scripts/svc.sh history [name] [N] # 默认全部服务、最近 20 条;首参为纯数字时视为条数
@@ -305,7 +337,7 @@ scripts/svc.sh history [name] [N] # 默认全部服务、最近 20 条;首
合并各服务 `history.jsonl`(按时间排序),字段:时间 / 服务 / kind / 事件 / 结果 /
来源 / pid / 详情。事件类型见 §10.2。
### 6.3 update(自动检索并更新)
### 6.4 update(自动检索并更新)
```bash
scripts/svc.sh update [name...] [--check] [--all] [--no-fetch]
@@ -341,7 +373,7 @@ scripts/svc.sh update [name...] [--check] [--all] [--no-fetch]
任一服务失败不中断其余服务;最终汇总 `成功/失败/跳过`。退出码:0 全部成功或最新;
1 有失败;2 用法错误。
### 6.4 help
### 6.5 help
```bash
scripts/svc.sh # 一屏速览(短帮助)
+269 -8
View File
@@ -6,6 +6,7 @@
# ════════════════════ 命令一览 ════════════════════
# scripts/svc.sh <name> [action] [参数...] 服务级:deploy(默认)|start|stop|restart|status|logs [N]
# scripts/svc.sh list [--tag T] 服务总览(status 等价)
# scripts/svc.sh info [name] [--tag T] [--reveal] 综合报告(只读):服务/数据库/配置快照/最近事件
# scripts/svc.sh set <name> [--port N --apply] [--tag T] 变更登记(换端口/标签)
# scripts/svc.sh rm <name> [--purge] 注销服务(--purge 连数据目录删除)
# scripts/svc.sh db create <name> [参数] 创建 PostgreSQL 实例(initdb)
@@ -71,6 +72,7 @@
# scripts/svc.sh db copy pg1 pg2 --db app # 把 pg1 的 app 库搬到 pg2
# scripts/svc.sh db restore pg2 --file <dump> --db app --force
# scripts/svc.sh list --tag core && scripts/svc.sh history
# scripts/svc.sh info && scripts/svc.sh info pg1 --reveal # 全景报告 / 单服务深度(含掩码值)
# scripts/svc.sh pg1 restart && scripts/svc.sh rm tmp1 --purge
# make svc ENV=dev ARGS="--env .env.dev --port 9000"
#
@@ -117,6 +119,7 @@ svc.sh —— 本机服务管理:后端(agent) / 前端(static) / 数据库(po
常用:
scripts/svc.sh list [--tag T] 服务总览
scripts/svc.sh info [name] [--tag T --reveal] 综合报告(只读):服务/数据库/配置快照
scripts/svc.sh <name> deploy [--from dev@<sha>] 部署并启动(缺省=远端 main 最新)
scripts/svc.sh <name> start|stop|restart|logs [N] 生命周期与日志
scripts/svc.sh <name> status 服务详情(版本/来源/DSN 等)
@@ -159,7 +162,7 @@ EOF
服务名:^[a-z][a-z0-9-]{0,15}$(小写字母开头,可含数字与连字符,≤16 位)。
保留字不可用作服务名:status list add set db rm create dsn commits history
deploy stop start restart logs help version。
update deploy stop start restart logs help info version。
目录布局(每个服务自包含,互不串版本):
runtime/<name>/
@@ -253,6 +256,9 @@ EOF
scripts/svc.sh <name> logs [N] 跟随日志(默认 50 行起;如 svc.sh pg1 logs 200)
scripts/svc.sh <name> status 单服务详情(kind/状态/地址/来源/版本/标签/DSN/目录)
scripts/svc.sh list [--tag T] 全服务表格:NAME KIND STATE PID ADDR SOURCE TAGS
scripts/svc.sh info [name] [--tag T --reveal] 综合报告(只读):服务表+运行汇总 /
postgres 实例与内部库 / 配置文件内容快照(敏感值默认掩码)/
最近事件;带 name 时输出单服务深度报告
scripts/svc.sh history [name] [N] 事件历史(首参为纯数字时视为条数)
变更登记(不动进程,除 --apply):
@@ -532,7 +538,7 @@ addr_compose() {
}
# ---- 名称与端口 ----
RESERVED_ENVS=" status list add set db rm create dsn commits history update deploy stop start restart logs help version "
RESERVED_ENVS=" status list add set db rm create dsn commits history update deploy stop start restart logs help info version "
# valid_env NAME:服务名合法性(小写字母开头、a-z0-9-、≤16 位;保留字不可用)。
valid_env() {
@@ -1996,17 +2002,24 @@ svc_pid_state() { # NAME → "alive <pid>" | "dead"
return 0
}
global_list() {
c_info "服务总览(root: $ROOT)"
printf ' %-12s %-9s %-7s %-8s %-20s %-24s %s\n' NAME KIND STATE PID ADDR SOURCE TAGS
local names=() d e name kind addr src tags st
# registered_names:runtime 下已登记服务名(stdout 每行一个;list/info 共用)。
registered_names() {
local d e
for d in "$RUNTIME"/*/; do
[ -f "${d}svc.env" ] || continue
e="$(basename "$d")"
[ "$e" = ".worktrees" ] && continue
valid_env "$e" || continue
names+=("$e")
printf '%s\n' "$e"
done
return 0
}
global_list() {
c_info "服务总览(root: $ROOT)"
printf ' %-12s %-9s %-7s %-8s %-20s %-24s %s\n' NAME KIND STATE PID ADDR SOURCE TAGS
local names=() d name kind addr src tags st
while IFS= read -r name; do names+=("$name"); done < <(registered_names)
for name in ${names[@]+"${names[@]}"}; do
d="$RUNTIME/$name"
kind="$(sed -n 's/^kind=//p' "$d/svc.env" | head -1)"; [ -n "$kind" ] || kind=agent
@@ -2079,12 +2092,259 @@ cmd_history() {
fi
fi
c_info "事件历史($([ -n "$env" ] && echo "服务 $env" || echo "全部服务"),最近 $n 条)"
cat "${files[@]}" | sort | tail -n "$n" | while IFS= read -r l; do
history_print_rows "$n" "${files[@]}"
}
# history_print_rows N FILES...:合并多个 history.jsonl、按时间排序、取最近 N 条打印
# (cmd_history 与 info 共用;行格式:时间/服务/kind/事件/结果/来源/pid/详情)。
history_print_rows() {
local n="$1"; shift
cat "$@" | sort | tail -n "$n" | while IFS= read -r l; do
[ -n "$l" ] || continue
printf ' %s %-10s %-9s %-8s %-6s %-24s %-7s %s\n' \
"$(jfield ts "$l")" "$(jfield env "$l")" "$(jfield kind "$l")" "$(jfield event "$l")" \
"$(jfield result "$l")" "$(jfield source "$l")" "$(jfield pid "$l")" "$(jfield detail "$l")"
done
return 0
}
# ---- info:综合信息报告(只读) ----
# 报告整体走 stdout(区别于提示走 stderr 的惯例)——info 的产物就是报告本身,
# 便于整页重定向/保存为快照;敏感值默认掩码,--reveal 才显示。
# mask_dsn STRING:把 postgres DSN 里的密码掩为 ***(postgresql://user:pass@ → user:***@)。
mask_dsn() {
printf '%s' "$1" | sed -E 's#(postgres(ql)?://[^:/@[:space:]]+:)[^@[:space:]]+@#\1***@#g'
}
# mask_kv LINE:KEY=VALUE 行的敏感值掩码——password 键与键名含 KEY/PASS/SECRET/
# TOKEN/DSN 的值整段掩为 ***;其余行只掩值内 DSN 密码(exec= 里可能带完整连接串)。
mask_kv() {
local line="$1" k="${1%%=*}"
if [ "$k" != "$1" ]; then
case "$k" in
password|*KEY*|*PASS*|*SECRET*|*TOKEN*|*DSN*)
printf '%s=***' "$k"; return 0 ;;
esac
fi
mask_dsn "$line"
}
# info_section TITLE:stdout 分节标题。
info_section() { printf '\n────── %s ──────\n' "$*"; }
# info_du PATH:目录/文件大小(du -sh;不存在或失败回 '-')。
info_du() {
local s=""
[ -e "$1" ] && s="$(du -sh "$1" 2>/dev/null | cut -f1)"
printf '%s' "${s:--}"
}
# info_file_mode PATH:八进制权限(不存在回 '-')。
info_file_mode() {
local m=""
[ -f "$1" ] && m="$(stat -c %a "$1" 2>/dev/null || true)"
printf '%s' "${m:--}"
}
# env_kv_of NAME KEY:读 runtime/<name>/svc.env 单键首个值(不依赖 ENV_* 全局,
# info 遍历多服务时使用;无则空)。
env_kv_of() {
[ -f "$RUNTIME/$1/svc.env" ] || return 0
sed -n "s/^$2=//p" "$RUNTIME/$1/svc.env" | head -1
}
# info_snapshot_lines FILE:逐行打印 KEY=VALUE 内容快照(按 CLI_REVEAL 决定掩码)。
info_snapshot_lines() {
local line
while IFS= read -r line || [ -n "$line" ]; do
[ -n "$line" ] || continue
if [ "$CLI_REVEAL" = 1 ]; then printf ' %s\n' "$line"
else printf ' %s\n' "$(mask_kv "$line")"; fi
done < "$1"
return 0
}
# info_pg_block NAME:postgres 实例详情块——登记信息、数据目录与备份体量;
# 运行中则列出实例内部数据库(含大小,按大小降序)。
info_pg_block() {
local name="$1" d="$RUNTIME/$1"
local addr user db auth ver pgdata src st pass rows
addr="$(env_kv_of "$name" addr)"
user="$(env_kv_of "$name" user)"
db="$(env_kv_of "$name" dbname)"
auth="$(env_kv_of "$name" authmode)"
ver="$(env_kv_of "$name" version)"
pgdata="$(env_kv_of "$name" pgdata)"
src="$(env_kv_of "$name" source)"
st="$(svc_pid_state "$name")"
if [ "${st%% *}" = alive ]; then
printf ' [%s] postgres 运行中 pid=%s → %s\n' "$name" "${st#alive }" "$addr"
else
printf ' [%s] postgres 未运行(内部库不可见,start 后可列出) → %s\n' "$name" "${addr:--}"
fi
printf ' 版本 %s 用户 %s 认证 %s 默认库 %s 来源 %s\n' \
"${ver:--}" "${user:--}" "${auth:--}" "${db:--}" "${src:--}"
[ -n "$pgdata" ] && printf ' 数据目录 %s(%s)\n' "$pgdata" "$(info_du "$pgdata")"
local nb=0 bsize="" latest="" f
if [ -d "$d/backups" ]; then
for f in "$d/backups"/*.dump; do
[ -f "$f" ] || continue
nb=$((nb + 1))
latest="$f"
done
[ "$nb" -gt 0 ] && bsize="$(info_du "$d/backups")"
fi
if [ "$nb" -gt 0 ]; then
printf ' 备份 %d 份(合计 %s,最近 %s)\n' "$nb" "$bsize" "$(basename "$latest")"
else
printf ' 备份 0 份\n'
fi
if [ "${st%% *}" = alive ]; then
pass="$(env_kv_of "$name" password)"
rows="$(pg_run "${addr%:*}" "${addr##*:}" "$user" "$pass" postgres \
"SELECT datname || '|' || pg_size_pretty(pg_database_size(datname)) FROM pg_database WHERE NOT datistemplate ORDER BY pg_database_size(datname) DESC" tA)" || rows=""
if [ -n "$rows" ]; then
printf ' 内部库:\n'
printf '%s\n' "$rows" | while IFS='|' read -r dn ds; do
printf ' %-24s %s\n' "$dn" "$ds"
done
else
printf ' 内部库:查询失败(psql 连接异常)\n'
fi
fi
return 0
}
# info_config_block NAME:单服务 svc.env 内容快照(文件路径+权限+逐行内容)。
info_config_block() {
local name="$1" f="$RUNTIME/$1/svc.env"
printf ' [%s] %s(权限 %s,kind=%s)\n' "$name" "$f" "$(info_file_mode "$f")" "$(svc_kind "$name")"
info_snapshot_lines "$f"
return 0
}
# info_global_config:全局配置层快照——$ROOT/.svc.env(自动加载层)、本次 --config
# 文件、shell 层已导出的 SVC_* 变量;值同掩码规则。
info_global_config() {
local f="$ROOT/.svc.env" exported=""
if [ -f "$f" ]; then
printf ' %s(自动加载层,权限 %s)\n' "$f" "$(info_file_mode "$f")"
info_snapshot_lines "$f"
else
printf ' .svc.env(自动加载层):不存在\n'
fi
[ -n "${SVC_ENVFILE:-}" ] && printf ' 本次 --config/--env 文件:%s(值已并入生效配置)\n' "$SVC_ENVFILE"
exported="$(env | grep '^SVC_' | sort || true)"
if [ -n "$exported" ]; then
printf ' shell 层已导出的 SVC_* 变量:\n'
if [ "$CLI_REVEAL" = 1 ]; then
printf '%s\n' "$exported" | sed 's/^/ /'
else
printf '%s\n' "$exported" | while IFS= read -r line; do printf ' %s\n' "$(mask_kv "$line")"; done
fi
else
printf ' shell 层 SVC_* 变量:无(.svc.env 里的值是脚本内层变量,不出现在 env)\n'
fi
return 0
}
# info_names_filtered:LIST_TAG 过滤后的已登记服务名(stdout 每行一个;与
# global_list 的 --tag 过滤同一语义)。
info_names_filtered() {
local name tags
while IFS= read -r name; do
if [ -n "${LIST_TAG:-}" ]; then
tags="$(env_kv_of "$name" tags)"
case ",$tags," in *",${LIST_TAG},"*) ;; *) continue ;; esac
fi
printf '%s\n' "$name"
done < <(registered_names)
return 0
}
# cmd_info [NAME] [--tag T] [--reveal]:综合信息报告(只读)。
# 无名 = 全景:服务表+运行汇总 → postgres 实例与内部库 → 各服务 svc.env 快照 +
# 全局配置层 → 最近事件;有名 = 单服务深度:状态详情 +(postgres)实例 + 快照 + 事件。
cmd_info() {
CLI_REVEAL=0; LIST_TAG=""
local name=""
while [ $# -gt 0 ]; do
case "$1" in
--reveal) CLI_REVEAL=1; shift ;;
--tag) need_value "$@"; LIST_TAG="$2"; shift 2 ;;
-h|--help) cmd_help lifecycle; return 0 ;;
-*) c_err "info 未知参数:$1(可用:--tag T --reveal)"; return 2 ;;
*) [ -z "$name" ] || { c_err "info 只接受一个服务名"; return 2; }
name="$1"; shift ;;
esac
done
if [ -n "$name" ]; then
valid_env "$name" || { c_err "非法服务名:$name"; return 2; }
[ -f "$RUNTIME/$name/svc.env" ] || { c_err "[$name] 无登记(scripts/svc.sh list 查看)"; return 2; }
env_config "$name"
info_section "服务 $name"
env_status_detail
if [ "$SVC_KIND" = postgres ]; then
info_section "数据库实例 $name"
info_pg_block "$name"
fi
info_section "配置快照 $name"
info_config_block "$name"
info_section "最近事件 $name"
local hf="$RUNTIME/$name/history.jsonl"
if [ -f "$hf" ]; then history_print_rows 5 "$hf"
else printf ' (暂无事件)\n'; fi
return 0
fi
# 全景报告。
local names=() n kind st total=0 running=0 ka=0 kp=0 ks=0
while IFS= read -r n; do names+=("$n"); done < <(info_names_filtered)
info_section "服务"
global_list
for n in ${names[@]+"${names[@]}"}; do
total=$((total + 1))
st="$(svc_pid_state "$n")"
[ "${st%% *}" = alive ] || continue
running=$((running + 1))
case "$(svc_kind "$n")" in
agent) ka=$((ka + 1)) ;;
postgres) kp=$((kp + 1)) ;;
static) ks=$((ks + 1)) ;;
esac
done
[ "$total" -gt 0 ] && printf ' 汇总:%d 已登记 / %d 运行中(agent %d、postgres %d、static %d)\n' \
"$total" "$running" "$ka" "$kp" "$ks"
info_section "数据库"
local pgs=()
for n in ${names[@]+"${names[@]}"}; do
[ "$(svc_kind "$n")" = postgres ] && pgs+=("$n")
done
if [ "${#pgs[@]}" -eq 0 ]; then
printf ' (无 postgres 实例;scripts/svc.sh db create <name> 创建)\n'
else
for n in ${pgs[@]+"${pgs[@]}"}; do info_pg_block "$n"; done
fi
info_section "配置"
if [ "${#names[@]}" -eq 0 ]; then
printf ' (无已登记服务)\n'
else
for n in ${names[@]+"${names[@]}"}; do info_config_block "$n"; done
fi
info_global_config
info_section "最近事件"
local files=() f
for n in ${names[@]+"${names[@]}"}; do
f="$RUNTIME/$n/history.jsonl"
[ -f "$f" ] && files+=("$f")
done
if [ "${#files[@]}" -eq 0 ]; then
printf ' (暂无事件)\n'
else
history_print_rows 5 "${files[@]}"
printf ' (更多:scripts/svc.sh history%s)\n' "${LIST_TAG:+ --tag 对应服务名}"
fi
return 0
}
# ---- 自动检索与更新 ----
@@ -2297,6 +2557,7 @@ main() {
db) shift; cmd_db "$@"; exit 0 ;;
set) shift; cmd_set "$@"; exit 0 ;;
rm) shift; cmd_rm "$@"; exit 0 ;;
info) shift; cmd_info "$@" || exit $?; exit 0 ;;
commits) shift; cmd_commits "$@"; exit 0 ;;
history) shift; cmd_history "$@"; exit 0 ;;
update) shift; cmd_update "$@" || exit $?; exit 0 ;;