主题
进程管理
本文引用的源码文件
本文引用的文件
目录
引言
本文聚焦 Agent 端的进程管理能力,围绕命令执行、Shell 选择、环境变量白名单、运行监控、优雅与强制终止、进程树清理、生命周期与资源回收等主题展开。Agent 采用双进程架构:Chrome 扩展端通过本地 HTTP/WebSocket 调用 Agent;Agent 内部通过 Node.js child_process 派生子进程执行命令,并提供 WebSocket 流式输出、超时策略、安全分级与跨平台兼容处理。
项目结构
与进程管理直接相关的代码主要分布在以下模块:
- agent/src/executor.js:命令执行器,负责 Shell 选择、子进程创建、输出缓冲、WebSocket 流式广播、超时策略、进程停止与查询。
- agent/src/process-tree.js:进程树终止工具,提供整树 SIGTERM/SIGKILL 能力与优雅等待。
- agent/src/security.js:安全管控(路径沙箱与命令分级),对危险命令进行黑名单/灰名单拦截。
- agent/bin/agent.js:CLI 入口,负责 Agent 进程启停、健康检查、PID 管理与跨平台终止。
- agent/src/server.js:HTTP + WebSocket 服务,聚合 executor、security、process-tree 等能力对外暴露 API。
graph TB subgraph "扩展端" BG["background/tool-executor.js<br/>扩展侧工具执行"] end subgraph "Agent 进程" CLI["agent/bin/agent.js<br/>CLI 启动/停止/重启"] SRV["agent/src/server.js<br/>HTTP + WebSocket 路由"] EXEC["agent/src/executor.js<br/>命令执行器"] PTREE["agent/src/process-tree.js<br/>进程树终止"] SEC["agent/src/security.js<br/>路径沙箱 + 命令分级"] end OS["操作系统 / Shell<br/>Git Bash / PowerShell / cmd / zsh / bash"] BG --> |HTTP/WebSocket| SRV CLI --> |spawn/kill| SRV SRV --> EXEC SRV --> SEC SRV --> PTREE EXEC --> OS
图表来源
- agent/bin/agent.js#L302-L384
- agent/src/server.js#L1-L30
- agent/src/executor.js#L1-L10
- agent/src/process-tree.js#L1-L10
- agent/src/security.js#L1-L10
章节来源
核心组件
- 命令执行器(executor.js):实现 Shell 选择、安全环境变量构建、子进程创建、输出流式广播、超时策略、进程停止与运行状态查询。
- 进程树工具(process-tree.js):封装跨平台整树终止逻辑,支持 SIGTERM → 延迟 → SIGKILL 的优雅关闭流程。
- 安全管控(security.js):路径白名单/硬阻止与命令分级(黑名单 deny、灰名单 confirm、允许 allow)。
- CLI 入口(agent/bin/agent.js):负责 Agent 进程守护、健康检查、PID 文件、优雅/强制终止与跨平台兼容。
- 服务端(server.js):聚合上述能力,对外暴露 HTTP/WebSocket 接口,并设置语言上下文。
章节来源
- agent/src/executor.js#L198-L386
- agent/src/process-tree.js#L18-L73
- agent/src/security.js#L62-L229
- agent/bin/agent.js#L167-L188
- agent/src/server.js#L1-L30
架构总览
下图展示一次命令执行的端到端流程:扩展端发起请求,Agent 服务端校验安全策略后调度执行器,执行器选择 Shell、构建环境、派生子进程,并通过 WebSocket 流式推送 stdout/stderr,同时维护超时与终止策略。
sequenceDiagram
participant Ext as "扩展端"
participant Srv as "Agent 服务端(server.js)"
participant Sec as "安全管控(security.js)"
participant Exec as "命令执行器(executor.js)"
participant PT as "进程树(process-tree.js)"
participant OS as "系统 Shell"
Ext->>Srv : "POST /api/exec(command, cwd, timeoutMs?)"
Srv->>Sec : "checkCommand(command, force)"
Sec-->>Srv : "{safe, level, reason}"
alt 允许或用户已确认
Srv->>Exec : "executeCommand(command, cwd, wsClient, opts)"
Exec->>Exec : "pickShell() + buildSafeEnv()"
Exec->>OS : "spawn(shell, args, {env, detached, windowsHide})"
OS-->>Exec : "stdout/stderr 事件"
Exec-->>Srv : "WS 广播 stdout/stderr/exit/error"
Note over Exec,PT : "超时到期或显式 kill → 优雅→强制终止"
else 拒绝或需确认
Srv-->>Ext : "错误/确认提示"
end
图表来源
- agent/src/server.js#L1-L30
- agent/src/security.js#L685-L758
- agent/src/executor.js#L198-L386
- agent/src/process-tree.js#L18-L73
详细组件分析
Shell 选择与环境变量白名单
- Shell 选择逻辑(纯函数 pickShell):
- Windows:优先检测 SHELL 是否为 Git Bash/Powershell/cmd 且存在;否则回退到常见 Git Bash 安装路径;若仍不可用则回退 COMSPEC 或 cmd.exe。
- macOS:优先 SHELL 中的 zsh/bash,否则默认 /bin/zsh。
- Linux:优先 SHELL 中的 bash/zsh/fish,否则探测 /bin/bash、/usr/bin/bash、/bin/sh,最终兜底 /bin/sh。
- 环境变量白名单(SAFE_ENV_KEYS):仅透传 PATH、HOME、USER、SHELL、LANG、COMSPEC、PSModulePath、JAVA_HOME、PYTHONPATH 等必要键,避免泄露宿主敏感信息;强制 TERM=dumb、FORCE_COLOR=0。
flowchart TD
Start(["进入 pickShell"]) --> CheckWin{"平台为 Windows?"}
CheckWin --> |是| WinCheck["检查 SHELL 是否包含 bash/powershell/cmd 且存在"]
WinCheck --> |命中| UseShell["返回 shell + 对应参数"]
WinCheck --> |未命中| TryGitBash["尝试常见 Git Bash 路径"]
TryGitBash --> FoundGB{"找到 Git Bash?"}
FoundGB --> |是| UseShell
FoundGB --> |否| UseCmd["回退 COMSPEC 或 cmd.exe"]
CheckWin --> |否| CheckMac{"平台为 macOS?"}
CheckMac --> |是| MacCheck["检查 SHELL 是否包含 zsh/bash 且存在"]
MacCheck --> |命中| UseShell
MacCheck --> |未命中| DefaultZsh["默认 /bin/zsh"]
CheckMac --> |否| LinCheck["检查 SHELL 是否包含 bash/zsh/fish 且存在"]
LinCheck --> |命中| UseShell
LinCheck --> |未命中| ProbeSh["探测 /bin/bash -> /usr/bin/bash -> /bin/sh"]
ProbeSh --> FoundSh{"找到任一?"}
FoundSh --> |是| UseShell
FoundSh --> |否| Fallback["兜底 /bin/sh"]
图表来源
章节来源
- agent/src/executor.js#L32-L91
- agent/src/executor.js#L118-L156
- agent/test/unit/executor-shell.unit.test.js#L11-L56
- agent/test/unit/executor-shell.unit.test.js#L58-L104
进程创建与运行监控
- 子进程创建:
- 使用 spawn(shell, [...args, command]),根据平台与 shell 类型决定是否 detached 与 windowsHide。
- 标准输出/错误以 pipe 方式读取,按 execId 广播给所有 WebSocket 客户端。
- 运行状态跟踪:
- runningProcesses 映射记录 execId → {process, wsClients, stdoutBuf, stderrBuf, startTime, baseTimeout, managed, extensions, outputInPeriod, detached, timedOut}。
- completedProcesses 缓存已完成进程的输出与退出码,供延迟连接的 WS 客户端回放。
- 资源使用与异常检测:
- 输出缓冲区上限 MAX_OUTPUT_BUFFER = 5MB,超过时截断并广播 truncated 消息。
- 超时策略:受管模式(传入 timeoutMs)下依据 decideTimeoutAction 决策延长或终止;旧模式直接终止。
- 进程 close/error 事件统一清理定时器、移出 runningProcesses、写入 completedProcesses 并回调 onComplete。
flowchart TD
A["executeCommand 开始"] --> B["生成 execId"]
B --> C["pickShell + buildSafeEnv"]
C --> D["spawn(shell, args, env, stdio, detached/windowsHide)"]
D --> E["注册 stdout/stderr 监听"]
E --> F["广播 stdout/stderr 片段"]
F --> G{"收到输出?"}
G --> |是| H["标记 outputInPeriod=true"]
G --> |否| I["保持静默"]
H --> J["armTimeout(timeout)"]
I --> J
J --> K{"超时到达?"}
K --> |是| L["decideTimeoutAction 决策"]
L --> |extend| M["延长定时器"]
L --> |kill| N["killProcess(execId)"]
K --> |否| O["继续运行"]
O --> P["close/error 事件"]
P --> Q["清理定时器/移出 runningProcesses/写入 completedProcesses"]
Q --> R["onComplete 回调"]
图表来源
章节来源
进程终止策略与跨平台兼容
- 优雅关闭(SIGTERM):
- POSIX:优先向进程组发送 SIGTERM(-pid),失败时回退单进程 kill。
- Windows:taskkill /T /PID 递归终止整个进程树。
- 强制终止(SIGKILL):
- POSIX:5 秒后向进程组发送 SIGKILL,再失败回退单进程。
- Windows:taskkill /F /T /PID 强制终止整棵树。
- 进程树清理:
- process-tree.js 提供 killProcessTree 与 stopProcessTree,后者在 graceMs 后升级 SIGKILL,并保证 close/error 不永久挂起。
- CLI 层终止:
- agent/bin/agent.js 的 killByPid 在 Windows 使用 taskkill /F /T,POSIX 使用 process.kill(pid, 'SIGTERM')。
flowchart TD
S["stopProcessTree(child, graceMs)"] --> T{"child.pid 有效?"}
T --> |否| End["立即 resolve"]
T --> |是| U["注册 close/error 完成回调"]
U --> V["killProcessTree(SIGTERM)"]
V --> W["graceMs 后 killProcessTree(SIGKILL)"]
W --> X["hardTimer(graceMs+5s) 兜底完成"]
X --> End
图表来源
- agent/src/process-tree.js#L50-L73
- agent/src/process-tree.js#L18-L42
- agent/src/executor.js#L495-L549
- agent/bin/agent.js#L167-L188
章节来源
- agent/src/process-tree.js#L18-L73
- agent/src/executor.js#L495-L549
- agent/bin/agent.js#L167-L188
- agent/test/unit/process-tree.unit.test.js#L56-L88
- agent/test/unit/process-tree.unit.test.js#L90-L118
安全管控与命令分级
- 路径沙箱:
- 硬阻止目录(HARD_BLOCKED_PATHS):Agent 配置、日志、回收站等关键路径不可访问。
- 工作目录放行:workdir 始终可访问,但 realpath 仍需过硬阻止检查。
- allowedPaths 前缀匹配 + realpath 校验,防止符号链接绕过。
- 命令分级:
- 黑名单(deny):高危破坏性命令绝对禁止,force 不可绕过。
- 灰名单(confirm):sudo、rm -rf、chmod 777、网络外联、脚本解释器等需用户确认。
- 白名单(allow):普通只读或低风险命令。
- 链式命令拆分、包装器剥壳、内联载荷解析、命令替换扫描,确保复杂命令不被绕过。
flowchart TD
A["checkCommand(command, force)"] --> B["collectCommandScopes(原始串 + 拆分段 + 内联载荷)"]
B --> C{"命中黑名单?"}
C --> |是| Deny["level='deny'"]
C --> |否| D{"force=true?"}
D --> |是| Allow["level='allow'"]
D --> |否| E{"命中灰名单?"}
E --> |是| Confirm["level='confirm'"]
E --> |否| Allow
图表来源
章节来源
- agent/src/security.js#L62-L229
- agent/src/security.js#L285-L384
- agent/src/security.js#L392-L683
- agent/src/security.js#L685-L758
进程生命周期管理
- 进程池管理:
- runningProcesses 作为“运行中进程表”,completedProcesses 作为“已完成缓存”(TTL 30s)。
- addWsClient/disconnectWsClient 支持动态加入/移除 WS 监听;断开连接时仅在非受管模式或显式 detach 才取消超时。
- 资源回收:
- clearTimers 清理超时与强制终止定时器。
- completedProcesses 条目在 TTL 后自动清理。
- 僵尸进程清理:
- process-tree.js 的 stopProcessTree 保证 close/error 事件触发后不再等待,避免孤儿进程长期残留。
- 内存泄漏防护:
- 输出缓冲区上限截断 + trunc 标志广播。
- 受管模式下超时策略基于 decideTimeoutAction,避免无限延长。
classDiagram
class Executor {
+executeCommand(command, cwd, wsClient, onComplete, collectOutput, execOpts) string
+executeCommandSync(command, cwd, timeoutMs) Promise
+addWsClient(execId, wsClient) boolean
+disconnectWsClient(execId, wsClient) boolean
+killProcess(execId) boolean
+detachProcess(execId) boolean
+getRunningProcesses() array
}
class ProcessTree {
+killProcessTree(child, signal) boolean
+stopProcessTree(child, options) Promise
}
class Security {
+checkCommand(command, force, tFn) object
+checkPath(pathStr, tFn) object
}
Executor --> ProcessTree : "终止策略"
Executor --> Security : "命令安全校验"
图表来源
章节来源
依赖关系分析
- server.js 依赖 executor、security、process-tree,对外暴露 HTTP/WebSocket 接口。
- executor 依赖 os、fs、crypto,内部使用 child_process.spawn 派生子进程。
- process-tree 依赖 child_process.spawn 与 process.kill,封装跨平台终止逻辑。
- security 依赖 path、fs/promises、os,实现路径与命令安全策略。
- agent/bin/agent.js 依赖 child_process、fs、os,负责 CLI 与进程守护。
graph LR Server["server.js"] --> Executor["executor.js"] Server --> Security["security.js"] Server --> ProcessTree["process-tree.js"] Executor --> Child["child_process"] ProcessTree --> Child Security --> PathFS["path/fs/promises/os"] CLI["agent/bin/agent.js"] --> Server CLI --> Child
图表来源
- agent/src/server.js#L1-L30
- agent/src/executor.js#L1-L10
- agent/src/process-tree.js#L1-L10
- agent/src/security.js#L1-L10
- agent/bin/agent.js#L1-L10
章节来源
- agent/src/server.js#L1-L30
- agent/src/executor.js#L1-L10
- agent/src/process-tree.js#L1-L10
- agent/src/security.js#L1-L10
- agent/bin/agent.js#L1-L10
性能与资源特性
- 输出缓冲限制:MAX_OUTPUT_BUFFER = 5MB,避免长时间命令导致内存无限增长;超限时广播 truncated 消息。
- 超时策略:
- 受管模式(execOpts.timeoutMs):基于 decideTimeoutAction,期内有输出才延长,静默即杀;最大延长次数 MAX_TIMEOUT_EXTENSIONS = 5,总预算封顶 TOTAL_BUDGET_CAP_MS = 600000ms。
- 旧模式:到期直接 kill。
- 进程树终止:
- POSIX 优先进程组信号,Windows 使用 taskkill /T /PID;5 秒后升级为 SIGKILL 或 /F /T /PID。
- 资源回收:
- completedProcesses 缓存 TTL 30s,避免长期驻留;disconnectWsClient 在非受管模式或 detach 时才释放超时。
章节来源
- agent/src/executor.js#L93-L116
- agent/src/executor.js#L165-L186
- agent/src/executor.js#L297-L323
- agent/src/executor.js#L459-L479
- agent/src/executor.js#L495-L549
故障排查指南
- 命令被拒绝:
- 检查 security.js 的黑名单/灰名单规则;必要时通过前端确认流程(force)放行灰名单。
- 命令无输出或卡住:
- 检查 decideTimeoutAction 是否因静默而 kill;确认 WS 客户端是否正确订阅 stdout/stderr。
- 进程未完全退出:
- 使用 process-tree.js 的 stopProcessTree 确保 SIGTERM → SIGKILL 升级;Windows 下确认 taskkill /T /PID 生效。
- Shell 选择异常:
- 在 Windows 上 SHELL 可能为 MSYS 风格路径(如 /usr/bin/bash),无法直接执行;pickShell 会回退到 Git Bash 或 cmd.exe。
- 环境变量缺失导致工具失败:
- 确认 SAFE_ENV_KEYS 是否包含所需键(如 JAVA_HOME、PYTHONPATH、PSModulePath 等)。
章节来源
- agent/src/security.js#L685-L758
- agent/src/executor.js#L165-L186
- agent/src/process-tree.js#L50-L73
- agent/src/executor.js#L32-L91
- agent/src/executor.js#L118-L156
结论
Agent 的进程管理以 executor.js 为核心,结合 process-tree.js 的跨平台终止能力与 security.js 的安全分级,实现了从 Shell 选择、环境变量白名单、运行监控到优雅/强制终止的完整闭环。CLI 层(agent/bin/agent.js)与 server.js 提供了进程守护与健康检查,确保扩展端与 Agent 端的稳定通信。建议在复杂任务中优先使用受管模式(timeoutMs)并结合 WS 流式输出进行实时监控,同时在安全敏感场景严格遵循黑名单/灰名单策略。
附录:API 与使用建议
- 命令执行(Executor)
- executeCommand(command, cwd?, wsClient?, onComplete?, collectOutput?, execOpts?)
- executeCommandSync(command, cwd?, timeoutMs?)
- addWsClient(execId, wsClient)
- disconnectWsClient(execId, wsClient)
- killProcess(execId)
- detachProcess(execId)
- getRunningProcesses()
- 进程树终止(ProcessTree)
- killProcessTree(child, signal?)
- stopProcessTree(child, {graceMs?})
- 安全策略(Security)
- checkCommand(command, force?, tFn?)
- checkPath(pathStr, tFn?)
- 使用建议
- 始终传入 execOpts.timeoutMs 启用受管模式,避免 SW 生命周期影响超时权威。
- 对高风险命令提前走确认流程,减少误报摩擦。
- 长耗时任务建议使用 WS 流式输出,并在断开连接时注意 detach 语义。
章节来源