主题
Agent 服务架构
本文引用的源码文件
本文引用的文件
目录
引言
本文件面向“AI Helper”的双进程架构中的独立本地代理端(Agent),版本为 2.3.1。Agent 是独立的 Node.js 服务,提供 HTTP 与 WebSocket 接口,供 Chrome 扩展端调用本地文件系统、系统命令执行、长期记忆、RAG 知识库、MCP 工具与技能系统。文档重点解释:
- 独立 Node.js 服务的设计模式与请求处理管道;
- HTTP/WebSocket 服务器实现;
- 认证机制、Token 验证流程与权限控制;
- 命令执行引擎的安全分级、进程树管理与资源隔离;
- 配置管理、环境变量与运行时参数注入;
- 服务启动、优雅重启、监控日志与错误处理;
- 与 Chrome 扩展端的通信契约、协议版本兼容性与错误传播策略。
项目结构
Agent 源码位于 agent/src,入口为 server.js,通过 bin/agent.js 暴露 CLI 命令(start/stop/status)。扩展端通过 src/background/local-agent-client.js 封装 HTTP/WebSocket 调用,并通过 tool-executor.js 将命令执行纳入 ReAct 工具链。
graph TB subgraph "Chrome 扩展端" BG["background/local-agent-client.js"] TE["background/tool-executor.js"] end subgraph "Agent 服务Node.js" BIN["bin/agent.js"] SRV["server.js"] AUTH["auth.js"] SEC["security.js"] EXEC["executor.js"] CFG["config.js"] PT["process-tree.js"] end BG --> |"HTTP /api/*"| SRV BG --> |"WebSocket /ws/exec/:id"| SRV TE --> BG BIN --> SRV SRV --> AUTH SRV --> SEC SRV --> EXEC SRV --> CFG EXEC --> PT
图示来源
- agent/src/server.js#L388-L425
- agent/src/server.js#L2086-L2153
- src/background/local-agent-client.js#L399-L443
- src/background/local-agent-client.js#L662-L728
- agent/bin/agent.js#L384-L419
章节来源
- agent/src/server.js#L388-L425
- agent/src/server.js#L2086-L2153
- src/background/local-agent-client.js#L399-L443
- src/background/local-agent-client.js#L662-L728
- agent/bin/agent.js#L384-L419
核心组件
- HTTP 路由与中间件:统一解析 body、CORS、国际化语言、认证校验、业务路由分发。
- 认证模块:配对码轮换、Token 生成与持久化、活跃连接判定。
- 安全网关:路径沙箱、白名单/硬阻止、命令黑白灰名单分级。
- 命令执行器:子进程管理、流式输出、超时策略、跨平台终止。
- 配置管理:内存缓存、写锁、敏感字段加密落盘。
- 进程树工具:npm 等子进程整树终止,避免孤儿进程。
- 扩展端客户端:多代理管理、可达性缓存、HTTP/WebSocket 封装。
章节来源
- agent/src/server.js#L417-L548
- agent/src/auth.js#L30-L127
- agent/src/security.js#L109-L229
- agent/src/executor.js#L198-L386
- agent/src/config.js#L107-L149
- agent/src/process-tree.js#L18-L42
- src/background/local-agent-client.js#L399-L443
架构总览
Agent 服务采用“单进程 + 模块化子系统”的架构:
- server.js 作为 HTTP 路由器与 WebSocket 升级入口;
- auth.js 负责配对与 Token 生命周期;
- security.js 提供路径与命令安全策略;
- executor.js 管理子进程与流式输出;
- config.js 提供带缓存与写锁的配置读写;
- process-tree.js 提供进程树终止能力;
- 扩展端 local-agent-client.js 封装所有 API 调用,并维护多代理状态。
sequenceDiagram
participant Ext as "扩展端"
participant Client as "local-agent-client.js"
participant Server as "server.js"
participant Auth as "auth.js"
participant Sec as "security.js"
participant Exec as "executor.js"
Ext->>Client : 发起命令执行请求
Client->>Server : POST /api/exec {command,cwd,force,timeoutMs}
Server->>Auth : verifyToken(Bearer)
Auth-->>Server : extId
Server->>Sec : checkCommand(command, force)
Sec-->>Server : level=allow|confirm|deny
alt deny
Server-->>Client : {success : false,level : "deny"}
else confirm
Server-->>Client : {success : true,level : "confirm",reason}
Ext->>Client : 用户确认后重试 force=true
Client->>Server : POST /api/exec {force : true,...}
Server->>Exec : executeCommand(...)
Exec-->>Server : execId, wsUrl
Server-->>Client : {execId, wsUrl}
else allow
Server->>Exec : executeCommand(...)
Exec-->>Server : execId, wsUrl
Server-->>Client : {execId, wsUrl}
end
图示来源
- agent/src/server.js#L1605-L1674
- agent/src/auth.js#L117-L127
- agent/src/security.js#L691-L758
- agent/src/executor.js#L198-L386
- src/background/tool-executor.js#L4755-L4774
详细组件分析
认证机制与 Token 验证流程
- 配对码生成与轮换:使用密码学安全的随机数生成 6 位配对码,支持定时刷新;当存在已配对且最近活跃的扩展时暂停轮换。
- 配对请求:校验失败次数限制,达到阈值后临时锁定并立即轮换配对码;成功后保存扩展 ID 与 Token,再次轮换配对码。
- Token 验证:每次请求从 Bearer Token 中解析扩展 ID,更新 lastAuthTime 以维持“插件在线”状态。
flowchart TD
Start(["开始"]) --> GenCode["生成配对码<br/>crypto.randomBytes"]
GenCode --> RotateTimer["定时轮换配对码"]
RotateTimer --> PairReq{"收到配对请求?"}
PairReq --> |否| RotateTimer
PairReq --> |是| CheckLock{"是否处于锁定窗口?"}
CheckLock --> |是| Deny["拒绝:tooFrequent"]
CheckFailCount{"失败计数 >= 阈值?"}
CheckLock --> |否| CheckFailCount
CheckFailCount --> |是| LockAndRotate["锁定 + 立即轮换配对码"]
CheckFailCount --> |否| ValidateCode{"配对码正确?"}
ValidateCode --> |否| IncFail["失败计数+1"]
ValidateCode --> |是| SavePairing["保存扩展ID与Token"]
IncFail --> RotateTimer
SavePairing --> RotateTimer
图示来源
章节来源
权限控制系统与安全网关
- 路径沙箱:
- 硬阻止目录:Agent 系统目录、配置文件、日志、回收站等不可访问;
- 工作目录永远放行;
- allowedPaths 白名单前缀匹配;
- realpath 防符号链接绕过;
- 对新建/不存在路径做父目录递归探测。
- 命令分级管控:
- 黑名单:绝对禁止(如 rm -rf /、格式化磁盘、fork bomb、shebang 直接执行、curl/wget | sh 等);
- 灰名单:需用户确认(sudo、rm -rf、git push -f、npm i -g、eval、kill、systemctl、脚本执行等);
- 白名单:普通命令直接放行;
- 链式命令拆分、包装器剥壳、解释器内联载荷、命令替换 $( ) 与反引号均送检。
flowchart TD
A["输入命令"] --> B["collectCommandScopes<br/>原始串 + 链式拆分 + 内联载荷"]
B --> C{"命中黑名单?"}
C --> |是| D["deny不可绕过"]
C --> |否| E{"force 模式?"}
E --> |是| F["allow跳过灰名单"]
E --> |否| G{"命中灰名单?"}
G --> |是| H["confirm需用户确认"]
G --> |否| I["allow"]
图示来源
章节来源
命令执行引擎与进程树管理
- 子进程选择:根据平台与环境变量选择 bash/zsh/fish/cmd/powershell,确保 shell 真实存在。
- 流式输出:stdout/stderr 实时广播给 WebSocket 客户端,缓冲区上限 5MB,超限标记 truncated。
- 超时策略:
- 旧语义:commandTimeout 到期直接杀进程;
- 受管模式:根据 decideTimeoutAction 决策 extend/kill,期内有输出才延长,静默即杀;
- 最大延长次数与总预算封顶。
- 进程终止:
- Unix:SIGTERM 到进程组,5s 后 SIGKILL;
- Windows:taskkill /T /PID,必要时 /F 强制;
- process-tree.js 提供通用整树终止能力,用于 npm 安装等场景。
classDiagram
class Executor {
+executeCommand(command, cwd, wsClient, onComplete, collectOutput, execOpts) string
+executeCommandSync(command, cwd, timeoutMs) Promise
+addWsClient(execId, wsClient) bool
+disconnectWsClient(execId, wsClient) bool
+killProcess(execId) bool
+detachProcess(execId) bool
+getRunningProcesses() Array
}
class ProcessTree {
+killProcessTree(child, signal) bool
+stopProcessTree(child, options) Promise
}
Executor --> ProcessTree : "使用整树终止"
图示来源
章节来源
配置管理系统与运行时参数注入
- 配置加载:内存缓存 + mtime 检测,首次启动写入默认配置;
- 写锁:并发写保护,防止 TOCTOU;
- 敏感字段:memoryEmbedding.apiKey 与 pairings.token 落盘前混淆,内存中保持明文;
- 自动允许列表:skills 与 workspace 目录自动加入 allowedPaths;
- 环境变量:executor 构建 SAFE_ENV_KEYS 白名单,过滤宿主敏感信息,强制 TERM=dumb、FORCE_COLOR=0。
章节来源
- agent/src/config.js#L107-L149
- agent/src/config.js#L154-L188
- agent/src/config.js#L215-L269
- agent/src/executor.js#L118-L156
服务启动流程图
flowchart TD
S["CLI bin/agent.js start"] --> L["loadConfig()"]
L --> M["setConsoleOutput(true)"]
M --> N["initSearchTools()"]
N --> O["detectRagAvailable()"]
O --> P["http.createServer(handleRequest)"]
P --> Q["WebSocketServer({noServer:true})"]
Q --> R["listen(port, host)"]
R --> T["startPairCodeRotation()"]
T --> U["startPeriodicCleanup()"]
U --> V["initializeMcpRegistry()"]
V --> W["initializeSkillRegistry()"]
图示来源
章节来源
请求处理时序图
sequenceDiagram
participant C as "扩展端"
participant CL as "local-agent-client.js"
participant S as "server.js"
participant A as "auth.js"
participant X as "executor.js"
C->>CL : agentRequest('/api/exec', body)
CL->>S : POST /api/exec (Bearer token)
S->>A : verifyToken(token)
A-->>S : extId
S->>X : executeCommand(command, cwd, null, cb, false, {timeoutMs})
X-->>S : execId, wsUrl
S-->>CL : {success : true, execId, wsUrl}
CL->>S : WebSocket /ws/exec/{execId}?token=...
S->>X : addWsClient(execId, ws)
X-->>S : stdout/stderr/exit 事件
S-->>CL : 流式消息推送
图示来源
- src/background/local-agent-client.js#L399-L443
- src/background/local-agent-client.js#L662-L728
- agent/src/server.js#L1605-L1674
- agent/src/server.js#L2086-L2153
- agent/src/executor.js#L198-L386
章节来源
- src/background/local-agent-client.js#L399-L443
- src/background/local-agent-client.js#L662-L728
- agent/src/server.js#L1605-L1674
- agent/src/server.js#L2086-L2153
- agent/src/executor.js#L198-L386
权限验证流程图
flowchart TD
Req["HTTP 请求"] --> CORS{"OPTIONS/CORS?"}
CORS --> |是| AllowCors["返回允许头"]
CORS --> |否| PathCheck{"路径免认证?"}
PathCheck --> |是| HandlePublic["处理公开接口"]
PathCheck --> |否| AuthCheck{"Authorization: Bearer ?"}
AuthCheck --> |否| Deny401["401 noToken"]
AuthCheck --> |是| Verify["verifyToken(token)"]
Verify --> |null| Deny403["403 invalidToken"]
Verify --> |extId| Route["路由分发"]
图示来源
章节来源
与 Chrome 扩展端的通信契约
- 认证:
- 配对:POST /api/pair,返回 token;
- 健康检查:GET /api/status(无认证,仅返回必要平台信息);
- 详细状态:GET /api/status/detail(需认证,含 workdir、allowedPaths、runningProcesses 等)。
- 文件系统:
- /api/fs/read/write/list/delete/search_files/search_content/download/download-stream/mkdir/rename/move/preview-xlsx;
- /api/files/upload(multipart/form-data);
- /api/fs/upload-stream(raw binary,path 通过 query 传递)。
- 命令执行:
- /api/exec(异步/同步 wait)、/api/exec/stop、/api/exec/detach;
- WebSocket /ws/exec/{execId},token 通过 Authorization 或 ?token= 传递。
- MCP/Skill/RAG/Memory:
- /api/mcp/、/api/skill/、/api/rag/、/api/memory/。
章节来源
- src/background/local-agent-client.js#L43-L93
- agent/src/server.js#L751-L862
- agent/src/server.js#L985-L1601
- agent/src/server.js#L1603-L1689
- agent/src/server.js#L2004-L2072
依赖关系分析
- server.js 依赖 auth.js、security.js、executor.js、config.js、logger.js、search.js、rag/detect.js、memory/routes.js、mcp/registry.js、skill/registry.js、skill/loader.js、i18n.js。
- executor.js 依赖 child_process、os、crypto、config.js、i18n.js。
- security.js 依赖 path、fs/promises、os、config.js、i18n.js。
- auth.js 依赖 crypto、config.js、i18n.js、sys-lang.js。
- config.js 依赖 os、path、fs、i18n.js、crypto-lite.js。
- process-tree.js 依赖 child_process。
- local-agent-client.js 依赖 shared/logger、shared/i18n、shared/secret。
graph LR SERVER["server.js"] --> AUTH["auth.js"] SERVER --> SEC["security.js"] SERVER --> EXEC["executor.js"] SERVER --> CFG["config.js"] EXEC --> PT["process-tree.js"] CLIENT["local-agent-client.js"] --> SERVER
图示来源
- agent/src/server.js#L1-L57
- agent/src/executor.js#L1-L8
- agent/src/security.js#L1-L7
- agent/src/auth.js#L1-L6
- agent/src/config.js#L1-L7
- agent/src/process-tree.js#L1-L11
- src/background/local-agent-client.js#L1-L4
章节来源
- agent/src/server.js#L1-L57
- agent/src/executor.js#L1-L8
- agent/src/security.js#L1-L7
- agent/src/auth.js#L1-L6
- agent/src/config.js#L1-L7
- agent/src/process-tree.js#L1-L11
- src/background/local-agent-client.js#L1-L4
性能与资源管理
- 文件上传:
- multipart/form-data 与 raw binary 流式上传,Content-Length 与实际接收大小双重校验;
- 脚本文件写入后剥离执行权限(chmod 0o644)。
- 下载优化:
- 大文件使用 createReadStream 流式传输;
- 目录打包为 zip,分块写入避免一次性 res.end(buffer)。
- 命令输出缓冲:
- stdout/stderr 缓冲区上限 5MB,超限标记 truncated 并广播提示;
- completedProcesses 缓存已完成进程输出,支持延迟连接的 WS 客户端回放。
- 超时与保活:
- 命令执行超时策略支持 extend/kill;
- WebSocket 心跳 ping/pong 检测半开连接,30s 未响应则 terminate。
- 优雅关闭:
- 停止配对码轮换、回收站清理、RAG 安装、运行进程、MCP 连接;
- 强制断开所有 WebSocket 与活跃 HTTP socket;
- 10s 超时兜底 process.exit(1)。
章节来源
- agent/src/server.js#L751-L862
- agent/src/server.js#L1267-L1385
- agent/src/executor.js#L93-L116
- agent/src/executor.js#L248-L275
- agent/src/server.js#L2239-L2288
故障排查指南
- 认证失败:
- 401 noToken:缺少 Authorization 头或格式错误;
- 403 invalidToken:Token 无效或未配对;
- 检查 /api/pair 配对流程与 pairings.json 是否有效。
- 命令被拦截:
- deny:命中黑名单(破坏性目标);
- confirm:命中灰名单,需在扩展端弹出用户确认框;
- 使用 force=true 可跳过灰名单,但黑名单仍不可绕过。
- 进程卡住:
- 检查 commandTimeout 与 decideTimeoutAction 策略;
- 使用 /api/exec/stop 或 detach 显式控制;
- 查看 runningProcesses 列表定位 execId。
- 文件操作失败:
- 403 pathNotInAllowedDir:路径不在 allowedPaths;
- 404 fileNotFound:文件不存在;
- 400 contentTooLarge/fileTooLarge:超出大小限制。
- 服务无法启动:
- EADDRINUSE:端口占用;
- 检查 bin/agent.js 启动逻辑与 server.js listen 回调。
章节来源
- agent/src/server.js#L417-L548
- agent/src/server.js#L1605-L1674
- agent/src/security.js#L691-L758
- agent/src/executor.js#L499-L549
- agent/bin/agent.js#L384-L419
结论
Agent 服务通过清晰的模块化设计实现了高内聚、低耦合的本地代理能力。其安全模型以路径沙箱与命令分级为核心,结合 Token 认证与 CORS 限制,有效防护恶意网页与横向攻击。命令执行引擎支持流式输出、超时策略与跨平台进程树管理,保障资源隔离与稳定性。配置管理提供内存缓存与写锁,敏感字段加密落盘,环境变量白名单过滤。扩展端客户端封装了完整的 HTTP/WebSocket 契约,支持多代理管理与可达性缓存。整体架构在安全性、性能与可维护性之间取得平衡,适合作为 Chrome 扩展的本地能力扩展层。