管理后台运行脚本(Agent 原生执行)
管理后台运行脚本(Agent 原生执行)
状态:已实施,未合并。决策与限制见 ADR 0005,协议见 Agent 脚本执行协议。
目标
管理员在 /agents 列表对在线 Agent 运行一段 shell 脚本,并在管理后台看到输出与退出码。
已确认的决策
- Agent 原生执行(
os/exec),仅管理员可用。 - 按钮只对实际宣告
agent_exec.v1的在线 Agent 显示。 - Agent 默认开启,
agent.exec.enabled可关闭;服务端另有server.exec.enabled。
实施结果
- Agent:
internal/agent/exec_run.go为执行器,固定环境变量、独立工作目录、stdin 传脚本、超时与输出上限、进程组终止(Unix)、并发上限;exec_stream.go把请求与输出记录适配为io.ReadWriteCloser,接入分发器;dialer.go与dial_executor.go完成分发与能力宣告。 - 协议:
internal/protocol/exec.go,请求头、输出记录与agent_exec.v1能力。 - Server:
agent_exec.go为票据(仅存哈希、单次使用、30 秒)、WebSocket 桥接与审计;agent_exec_api.go为管理员接口;api.go在列表中输出execCapable;agent_relay_transport.go与connection_selector.go允许无目标的exec流,并按能力选择会话。 - 前端:
ScriptRunDialog.vue、api/agentExec.ts、utils/scriptRun.ts,列表按钮仅在管理员、在线、execCapable时显示。
与原计划的偏差
- 对话框输出使用样式化的
<pre>,没有使用 xterm。原因是输出只是文本,不需要终端仿真,而且 xterm 在测试环境里挂载不稳定。 - 服务端票据为内存实现,只在签发节点有效(见 ADR 0005 的已知限制)。
- Windows 不终止进程树(见 ADR 0005)。
测试
- Agent:环境清洗、超时杀整个进程组、输出截断、并发 busy、脚本大小上限、关闭即取消、请求头分片到达、开始后拒绝写入。
- 协议:请求头与记录的编解码、长度边界。
- Server:票据单次使用与过期、只存哈希、桥接与审计不含正文、脚本与超时限制、管理员与开关检查、真实 Agent 通过能力协商后执行脚本并回传输出与退出码。
- 前端:按钮显示条件、多字节字符分片解码、请求与帧处理、对话框输出与状态。
验证
go test ./... -count=1
go test ./internal/agent -run Exec -race
go test ./internal/server -run 'AgentExec|RealAgentRunsScript|NeedsNoTarget' -race
cd web && npm test -- --run && npm run build && cd .. && ./scripts/verify-web-embed.sh
go test ./scripts -count=1