本篇讲解
src/config/index.ts。你会了解:这个包有哪些可调参数、默认值是什么、resolveSandboxConfig怎么把用户传的配置"填满"成运行时配置。
1. 为什么需要配置系统?
不同场景对沙箱的要求不同:
- CI 环境 :可能想用
autonomous策略(不问人),因为没人坐在终端前 - 开发环境 :可能想用
on-request策略(危险命令要问人),保护安全 - 演示场景 :可能想用
danger-full-access,图方便
配置系统就是让这些差异变成"改一个参数"的事,而不是"改一堆代码"的事。
2. 默认值常量
config/index.ts 一开头就定义了一堆默认值常量。我们逐个看:
2.1 超时与输出限制
typescript
export const DEFAULT_TIMEOUT_MS = 120_000; // 命令默认超时:2 分钟
export const DEFAULT_MAX_STDOUT_BYTES = 512 * 1024; // stdout 最大 512KB
export const DEFAULT_MAX_STDERR_BYTES = 512 * 1024; // stderr 最大 512KB
为什么需要超时? 防止一条命令跑飞了(比如死循环),把整个系统卡住。
为什么需要输出限制? 防止一条命令输出太多(比如 cat /dev/urandom),把磁盘写满。超过限制的内容会被截断,但会有一个 truncated 标志会告诉你是"不完整"的。
2.2 沙箱模式与审批策略
typescript
export const DEFAULT_SANDBOX_MODE: SandboxMode = 'workspace-write';
export const DEFAULT_APPROVAL_POLICY: ApprovalPolicy = 'on-request';
export const DEFAULT_EXECUTION_SURFACE: ExecutionSurface = 'direct';
- 默认模式是
workspace-write(可以在项目里改文件,不能改外面的) - 默认审批策略是
on-request(L0/L1 自动过,L2+ 要审批) - 默认执行面是
direct(直接在项目根目录执行)
2.3 Git Worktree 相关
typescript
export const DEFAULT_WORKTREE_BRANCH_PREFIX = '${元数据目录}-agent';
worktree 分支名的前缀。创建的分支会叫 ${元数据目录}-agent/1704830400000(前缀 + 时间戳)。
2.4 元数据目录
typescript
export const SANDBOX_META_DIR = 自定义目录(个人自己决定);
项目内沙箱元数据的目录名。审计日志、worktree 等都放在这个目录下:
c
your-project/
└── ${元数据目录}/
├── audit/ ← 审计日志
│ ├── audit.jsonl
│ └── <runId>/
│ ├── stdout.txt
│ ├── stderr.txt
│ └── metadata.json
└── worktrees/ ← git worktree 副本
2.5 代理地址
typescript
export const DEFAULT_PROXY_URL = 'http://127.0.0.1:8787';
开网时,子进程的 HTTP 流量会走这个本地代理。代理会检查"你要连的域名在不在白名单里"。
2.6 环境变量白名单
typescript
export const DEFAULT_ENV_ALLOWLIST = [
'PATH', // 可执行文件搜索路径
'LANG', // 区域与语言
'LC_ALL', // 覆盖全部 locale 类别
'LC_CTYPE', // 字符分类与转码
'TERM', // 终端类型
'TERM_PROGRAM', // 终端程序名(如 iTerm、vscode)
'COLORTERM', // 是否支持真彩色
'TMPDIR', // macOS 临时目录
'TMP', // 通用临时目录
'TEMP', // Windows 临时目录
'SYSTEMROOT', // Windows 系统根目录
'ComSpec', // Windows 默认 shell
'PATHEXT', // Windows 可执行扩展名列表
] as const;
只有这些环境变量会被传给子进程。像 GITHUB_TOKEN、SSH_AUTH_SOCK 这些敏感变量会被剥离。
3. DEFAULT_SANDBOX_CONFIG------默认配置快照
typescript
export const DEFAULT_SANDBOX_CONFIG = {
executionSurface: 'direct',
mode: 'workspace-write',
approvalPolicy: 'on-request',
timeoutMs: 120_000,
maxStdoutBytes: 512 * 1024,
maxStderrBytes: 512 * 1024,
network: false, // 默认断网
proxyUrl: 'http://127.0.0.1:8787',
envAllowlist: [...DEFAULT_ENV_ALLOWLIST],
extraEnv: {},
enforceProtectedPaths: true, // 默认开启路径预检
protectedHomePaths: [],
allowDangerousFallback: false, // 默认不允许回退
worktreeBranchPrefix: '${元数据目录}-agent',
} as const;
注意 network: false------默认是断网 的!想联网必须显式设置 network: true。
4. resolveSandboxConfig------把用户配置"填满"
用户只传了 projectRoot,其他字段怎么办?resolveSandboxConfig 就是来补全的:
typescript
export function resolveSandboxConfig(config: SandboxConfig): ... {
const projectRoot = config.projectRoot;
return {
projectRoot, // 必填,原样保留
executionSurface: config.executionSurface ?? 'direct',
mode: config.mode ?? 'workspace-write',
approvalPolicy: config.approvalPolicy ?? 'on-request',
timeoutMs: config.timeoutMs ?? 120_000,
maxStdoutBytes: config.maxStdoutBytes ?? 512 * 1024,
maxStderrBytes: config.maxStderrBytes ?? 512 * 1024,
network: config.network ?? false,
proxyUrl: config.proxyUrl ?? 'http://127.0.0.1:8787',
allowDangerousFallback: config.allowDangerousFallback ?? false,
auditDir: config.auditDir ?? `${projectRoot}/${元数据目录}/audit`,
worktreesDir: config.worktreesDir ?? `${projectRoot}/${元数据目录}/worktrees`,
worktreeBranchPrefix: config.worktreeBranchPrefix ?? '${元数据目录}-agent',
envAllowlist: config.envAllowlist ?? [...DEFAULT_ENV_ALLOWLIST],
extraEnv: config.extraEnv ?? {},
enforceProtectedPaths: config.enforceProtectedPaths ?? true,
protectedHomePaths: config.protectedHomePaths ?? [],
profilesDir: config.profilesDir, // 可选;不传则 runner 自动解析
onApproval: config.onApproval, // 可选;不传则除 L0 外默认拒绝
onOsSandboxUnavailable: config.onOsSandboxUnavailable,
};
}
模式很简单 :每个字段用 ??(空值合并运算符)来处理------用户传了就用用户的,没传就用默认的。
4.1 自动生成的路径
有两个路径是自动生成的:
typescript
auditDir: `${projectRoot}/${元数据目录}/audit`
worktreesDir: `${projectRoot}/${元数据目录}/worktrees`
也就是说,如果你把 projectRoot 设为 /home/user/my-project,审计日志会写到 /home/user/my-project/${元数据目录}/audit/。
4.2 两个可选回调
onApproval:不传的话,除了 L0 命令,其他一律拒绝。这是一个 fail-closed 的设计------宁可多拦,不可漏放。onOsSandboxUnavailable:不传的话,如果 OS 沙箱不可用(比如不在 macOS 上),直接抛错。也是一个 fail-closed 设计。
5. 手写一遍
现在让我们自己实现一遍 resolveSandboxConfig:
typescript
import type { SandboxConfig, SandboxMode, ApprovalPolicy, ExecutionSurface } from '../types/index.js';
// 1. 定义默认值常量
const DEFAULT_TIMEOUT_MS = 120_000;
const DEFAULT_MAX_STDOUT_BYTES = 512 * 1024;
const DEFAULT_MAX_STDERR_BYTES = 512 * 1024;
const DEFAULT_SANDBOX_MODE: SandboxMode = 'workspace-write';
const DEFAULT_APPROVAL_POLICY: ApprovalPolicy = 'on-request';
const DEFAULT_EXECUTION_SURFACE: ExecutionSurface = 'direct';
// 2. 实现解析函数
function resolveSandboxConfig(config: SandboxConfig) {
const projectRoot = config.projectRoot;
return {
projectRoot,
executionSurface: config.executionSurface ?? DEFAULT_EXECUTION_SURFACE,
mode: config.mode ?? DEFAULT_SANDBOX_MODE,
approvalPolicy: config.approvalPolicy ?? DEFAULT_APPROVAL_POLICY,
timeoutMs: config.timeoutMs ?? DEFAULT_TIMEOUT_MS,
// ... 其余字段同理
};
}
就是这么简单------定义默认值 → 用 ?? 补全。
6. 配置使用示例
6.1 最简配置
typescript
const config = resolveSandboxConfig({
projectRoot: '/home/user/my-project',
});
// 其他字段全部使用默认值
6.2 自定义配置
typescript
const config = resolveSandboxConfig({
projectRoot: '/home/user/my-project',
mode: 'read-only', // 只读模式
approvalPolicy: 'autonomous', // 不问人
network: true, // 允许联网
timeoutMs: 60_000, // 超时 1 分钟
onApproval: async (ctx) => {
// 自己实现审批逻辑,比如调 API
console.log(`需要审批: ${ctx.command} (${ctx.riskLevel})`);
return 'allow_once';
},
});
7. 小结
| 概念 | 要点 |
|---|---|
| 默认超时 | 2 分钟(120,000 ms) |
| 默认输出限制 | stdout/stderr 各 512KB |
| 默认模式 | workspace-write |
| 默认审批策略 | on-request |
| 默认执行面 | direct |
| 默认网络 | 断网 (false) |
| 默认路径预检 | 开启 (true) |
| 默认危险回退 | 不允许 (false) |
resolveSandboxConfig |
用 ?? 把用户配置补全为运行时配置 |
核心思想:安全的默认值 + 用户可覆盖。不传的参数都会倾向更安全的选项。