diff --git a/.task/feature-svc-info.md b/.task/feature-svc-info.md index d8bd295d..76ffd9de 100644 --- a/.task/feature-svc-info.md +++ b/.task/feature-svc-info.md @@ -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 `(单服务深度)、`--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 只读);剩余项无。 diff --git a/Makefile b/Makefile index 86409aae..33f3a064 100644 --- a/Makefile +++ b/Makefile @@ -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= [ACTION=deploy|start|stop|restart|status|logs] [ARGS='--from dev@ --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) diff --git a/docs/svc.md b/docs/svc.md index bfd623f8..3e63f87d 100644 --- a/docs/svc.md +++ b/docs/svc.md @@ -67,6 +67,7 @@ scripts/svc.sh prod deploy --from dev@ \ # 日常操作 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//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//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 # 一屏速览(短帮助) diff --git a/scripts/svc.sh b/scripts/svc.sh index b8815546..2c730f5f 100755 --- a/scripts/svc.sh +++ b/scripts/svc.sh @@ -6,6 +6,7 @@ # ════════════════════ 命令一览 ════════════════════ # scripts/svc.sh [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 [--port N --apply] [--tag T] 变更登记(换端口/标签) # scripts/svc.sh rm [--purge] 注销服务(--purge 连数据目录删除) # scripts/svc.sh db create [参数] 创建 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 --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 deploy [--from dev@] 部署并启动(缺省=远端 main 最新) scripts/svc.sh start|stop|restart|logs [N] 生命周期与日志 scripts/svc.sh 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// @@ -253,6 +256,9 @@ EOF scripts/svc.sh logs [N] 跟随日志(默认 50 行起;如 svc.sh pg1 logs 200) scripts/svc.sh 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 " | "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//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 创建)\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 ;;