深度解析 Claude Code 架构:从终端输入到 LLM 响应的完整链路,涵盖沙箱安全、工具系统、流式引擎与状态管理,附详细架构图与时序图。
一、整体架构概览
Claude Code 是一个基于终端的 AI 编程助手,其架构设计遵循分层解耦和事件驱动的原则。整个系统可以划分为以下几个核心层次:
┌─────────────────────────────────────────────────────────────────────────────┐
│ CLI Entry Layer 入口层 │
│ (cli.tsx → main.tsx → REPL.tsx) │
├─────────────────────────────────────────────────────────────────────────────┤
│ Terminal UI 终端界面层 │
│ (Ink + React 渲染引擎) │
├─────────────────────────────────────────────────────────────────────────────┤
│ Query Engine 查询引擎层 │
│ (query.ts → QueryEngine.ts) │
├─────────────────────────────────────────────────────────────────────────────┤
│ Tool System 工具系统层 │
│ (Tool.ts → StreamingToolExecutor.ts → 30+ Tools) │
├─────────────────────────────────────────────────────────────────────────────┤
│ Services 服务层 │
│ (API Client / MCP / OAuth / Memory / Permission) │
├─────────────────────────────────────────────────────────────────────────────┤
│ State Management 状态管理层 │
│ (AppStateStore.ts → Custom Store) │
└─────────────────────────────────────────────────────────────────────────────┘
为什么这样设计? 每一层都有明确的职责边界,上层通过定义良好的接口调用下层,避免了紧耦合。这种设计使得系统易于测试、扩展和维护。
二、详细架构图
2.1 整体架构图
graph TB
subgraph Entry["入口层 Entry Layer"]
CLI[cli.tsx<br/>命令行入口]
MAIN[main.tsx<br/>主初始化流程]
end
subgraph UI["终端界面层 Terminal UI"]
REPL[REPL.tsx<br/>交互式循环]
INK[Ink 渲染引擎]
PROMPT[PromptInput<br/>输入组件]
end
subgraph Query["查询引擎层 Query Engine"]
QE[QueryEngine.ts]
QLOOP[query.ts<br/>查询循环]
STREAM[流式响应处理]
end
subgraph ToolSys["工具系统层 Tool System"]
TREG[Tool Registry<br/>工具注册中心]
STE[StreamingToolExecutor]
FILE[File Tools<br/>文件操作]
SHELL[Shell Tools<br/>命令执行]
AGENT[Agent Tools<br/>智能体]
COMM[Comm Tools<br/>通信]
end
subgraph Sandbox["Sandbox Layer"]
SB_ADAPTER[sandbox-adapter.ts]
SB_RUNTIME[sandbox-runtime]
SB_MACOS[sandbox-exec]
SB_LINUX[bwrap]
SB_POLICY[Policy Config]
end
subgraph Services["服务层 Services"]
API[API Client<br/>Anthropic API]
MCP[MCP Client<br/>模型上下文协议]
AUTH[OAuth<br/>认证服务]
MEM[Memory<br/>记忆系统]
PERM[Permission<br/>权限系统]
end
subgraph State["状态管理层 State"]
STORE[Custom Store<br/>自定义状态存储]
APPSTATE[AppState<br/>100+ 字段]
REACT[React Context<br/>Providers]
end
CLI --> MAIN
MAIN --> REPL
REPL --> INK
REPL --> QE
QE --> QLOOP
QLOOP --> STREAM
STREAM --> API
STREAM --> TREG
TREG --> STE
STE --> FILE
STE --> SHELL
STE --> AGENT
STE --> COMM
TREG --> PERM
API --> MEM
REPL --> STORE
STORE --> APPSTATE
APPSTATE --> REACT
REACT --> INK
SHELL --> SB_ADAPTER
SB_ADAPTER --> SB_RUNTIME
SB_RUNTIME --> SB_MACOS
SB_RUNTIME --> SB_LINUX
SB_RUNTIME --> SB_POLICY
2.2 核心模块关系图
graph LR
subgraph Input["输入处理"]
A[Early Input<br/>早期输入捕获]
B[StructuredIO<br/>结构化IO]
C[Slash Commands<br/>斜杠命令]
end
subgraph Process["请求处理"]
D[Context Assembly<br/>上下文组装]
E[System Prompt<br/>系统提示词]
F[Message History<br/>消息历史]
end
subgraph LLM["LLM 交互"]
G[Streaming API<br/>流式API调用]
H[Tool Use Detection<br/>工具使用检测]
I[Response Parser<br/>响应解析]
end
subgraph Output["输出处理"]
J[Tool Execution<br/>工具执行]
K[Permission Check<br/>权限检查]
L[Ink Render<br/>终端渲染]
end
A --> B
B --> C
C --> D
D --> E
D --> F
E --> G
F --> G
G --> H
H --> I
I --> J
J --> K
K --> L
H -->|Multi-turn| G
三、时序图:从终端输入到 LLM 响应
3.1 完整请求生命周期时序图
sequenceDiagram
autonumber
participant User as 用户
participant Term as 终端
participant CLI as cli.tsx
participant Main as main.tsx
participant REPL as REPL.tsx
participant QE as QueryEngine
participant API as API Client
participant LLM as Anthropic API
participant Tool as Tool System
participant State as AppState
Note over User,State: 阶段1: 启动与初始化
User->>Term: 输入 claude 命令
Term->>CLI: 启动进程
CLI->>CLI: startCapturingEarlyInput()
CLI->>Main: 动态导入并执行
Main->>Main: 加载配置、认证
Main->>Main: 初始化 GrowthBook
Main->>Main: 运行迁移脚本
Main->>REPL: launchRepl()
REPL->>State: createStore(initialState)
REPL->>REPL: 渲染 Ink UI
Note over User,State: 阶段2: 用户输入处理
User->>Term: 输入提示词
Term->>REPL: onSubmit(input)
REPL->>REPL: processInput(input)
REPL->>REPL: 检测斜杠命令
REPL->>State: setMessages([...])
REPL->>QE: onQuery(messages)
Note over User,State: 阶段3: 上下文组装
QE->>QE: 组装 systemPrompt
QE->>QE: 收集 userContext
QE->>QE: 收集 systemContext
QE->>State: 获取 conversation history
QE->>API: 构建 API 请求参数
Note over User,State: 阶段4: API 调用与流式响应
API->>LLM: POST /v1/messages (streaming)
LLM-->>API: SSE 流式响应
API-->>QE: yield stream events
QE-->>REPL: 实时更新消息
REPL-->>State: setStreamingText()
REPL-->>Term: Ink 实时渲染
Note over User,State: 阶段5: 工具使用检测与执行
LLM-->>API: tool_use block
API-->>QE: 检测到工具调用
QE->>Tool: executeTool(toolUse)
Tool->>Tool: validateInput()
Tool->>Tool: checkPermissions()
Tool->>Tool: 执行具体工具
Tool-->>QE: 返回 tool_result
QE-->>API: 追加到 messages
API->>LLM: 继续对话 (多轮)
Note over User,State: 阶段6: 响应完成与渲染
LLM-->>API: message_stop
API-->>QE: 流结束
QE->>QE: handleStopHooks()
QE-->>REPL: 最终消息
REPL->>State: update messages
REPL-->>Term: 完整渲染
Term-->>User: 显示响应
3.2 工具执行详细时序图
sequenceDiagram
autonumber
participant QE as QueryEngine
participant STE as StreamingToolExecutor
participant TREG as ToolRegistry
participant Tool as 具体工具
participant PERM as PermissionSystem
participant User as 用户
participant SB as SandboxManager
QE->>STE: addTool(toolUseBlock)
STE->>TREG: findToolByName(name)
TREG-->>STE: 返回 Tool 定义
STE->>STE: 验证 inputSchema
STE->>STE: 检查 isConcurrencySafe
STE->>STE: 加入执行队列
loop 工具执行循环
STE->>STE: processQueue()
STE->>Tool: validateInput(input, context)
Tool-->>STE: 验证结果
alt 需要权限检查
STE->>PERM: checkPermissions(tool, input)
PERM->>User: 请求用户确认
User-->>PERM: 批准/拒绝
PERM-->>STE: 权限结果
end
STE->>Tool: call(input, context, canUseTool)
Tool->>Tool: 执行实际操作
Tool-->>STE: 返回 ToolResult
STE->>STE: mapToolResultToBlockParam()
STE-->>QE: yield tool_result message
end
3.3 状态更新与渲染时序图
sequenceDiagram
autonumber
participant User as 用户
participant REPL as REPL.tsx
participant HOOK as Custom Hooks
participant STORE as Custom Store
participant APPSTATE as AppState
participant INK as Ink Renderer
participant TERM as 终端
User->>REPL: 输入/操作
REPL->>HOOK: 调用状态更新 hook
HOOK->>STORE: setState(partialState)
STORE->>STORE: 合并状态变更
STORE->>APPSTATE: 更新对应字段
STORE->>STORE: 通知所有 subscribers
REPL->>INK: 触发重新渲染
INK->>APPSTATE: 读取最新状态
APPSTATE-->>INK: 返回当前状态
INK->>INK: 计算虚拟 DOM 差异
INK->>TERM: 输出 ANSI 转义序列
TERM-->>User: 更新终端显示
四、核心模块功能分析
4.1 入口层 (Entry Layer)
文件: src/entrypoints/cli.tsx, src/main.tsx
// cli.tsx - 最早期的入口点
async function main(): Promise<void> {
const args = process.argv.slice(2);
// 快速路径:--version 不需要加载任何模块
if (args.length === 1 && args[0] === '--version') {
console.log(`${MACRO.VERSION} (Claude Code)`);
return;
}
// 早期输入捕获 - 用户可能在 REPL 启动前就开始输入
const { startCapturingEarlyInput } = await import('../utils/earlyInput.js');
startCapturingEarlyInput();
// 加载主 CLI
const { main: cliMain } = await import('../main.js');
await cliMain();
}
为什么这样设计?
- 快速路径优化:
--version等简单命令零依赖加载,响应时间 < 50ms - 早期输入捕获: 解决用户输入
claude后立即打字但 REPL 尚未就绪的问题 - 动态导入: 减少启动时的模块加载开销,采用按需加载策略
4.2 终端界面层 (Terminal UI)
文件: src/screens/REPL.tsx, src/ink.ts
// REPL.tsx 核心渲染循环
function REPL() {
const [appState, setAppState] = useAppState();
const [streamingText, setStreamingText] = useState<string | null>(null);
const onQuery = useCallback(async (input: string, messages: Message[]) => {
// 重置流式状态
setStreamingText(null);
// 启动查询引擎
const stream = queryEngine.stream({
messages,
systemPrompt,
canUseTool: checkToolPermission,
});
// 实时处理流式响应
for await (const event of stream) {
if (event.type === 'content_block_delta') {
setStreamingText(prev => prev + event.delta.text);
}
}
}, []);
return (
<Box flexDirection="column">
<MessageList messages={appState.messages} />
{streamingText && <StreamingText text={streamingText} />}
<PromptInput onSubmit={onQuery} />
</Box>
);
}
为什么使用 Ink + React?
- 声明式 UI: 用 React 组件模型描述终端界面,代码更易维护
- 增量更新: 只重渲染变化的部分,避免全屏刷新导致的闪烁
- 组件复用: 消息、输入框、加载状态都可以封装为独立组件
4.3 查询引擎层 (Query Engine)
文件: src/query.ts, src/QueryEngine.ts
// query.ts - 核心查询循环
export async function* query(params: QueryParams): AsyncGenerator<StreamEvent> {
const { messages, systemPrompt, canUseTool, toolUseContext } = params;
// 组装 API 请求
const request = prepareApiRequest({
messages: formatMessagesForAPI(messages),
system: systemPrompt,
tools: getAvailableTools(),
});
// 流式调用 API
const stream = await streamMessages(request);
for await (const event of stream) {
switch (event.type) {
case 'content_block_start':
if (event.content_block.type === 'tool_use') {
// 检测到工具使用,准备执行
yield* handleToolUse(event.content_block, toolUseContext);
}
break;
case 'content_block_delta':
// 普通文本增量,直接透传
yield event;
break;
case 'message_stop':
// 消息结束,执行 stop hooks
yield* executeStopHooks(messages);
break;
}
}
}
查询引擎的设计亮点:
- Generator 函数: 使用
AsyncGenerator实现真正的流式处理,内存占用低 - 工具拦截: 在流中实时检测
tool_useblock,无缝切换到工具执行 - 错误恢复: 内置重试机制和错误处理,API 失败时自动降级
4.4 工具系统层 (Tool System)
文件: src/Tool.ts, src/services/tools/StreamingToolExecutor.ts
// Tool.ts - 工具定义接口
export type Tool<Input extends AnyObject = AnyObject, Output = unknown> = {
// 身份标识
name: string;
aliases?: string[];
searchHint?: string;
// 能力声明
isEnabled(): boolean;
isConcurrencySafe(input): boolean;
isReadOnly(input): boolean;
isDestructive(input): boolean;
// 生命周期
validateInput(input, context): Promise<void>;
checkPermissions(input, context): Promise<PermissionResult>;
call(input, context, canUseTool, parentMessage): Promise<ToolResult<Output>>;
// 渲染
renderToolUseMessage(input): React.ReactNode;
renderToolResultMessage(result): React.ReactNode;
mapToolResultToToolResultBlockParam(result, toolUseID): ToolResultBlockParam;
// 类型安全
inputSchema: ZodSchema<Input>;
};
工具系统的核心设计原则:
- 自描述: 每个工具自带描述、验证、渲染逻辑,框架零侵入
- 权限感知:
checkPermissions钩子支持自动和手动权限模式 - 并发安全:
isConcurrencySafe标记允许并行执行独立的工具 - 类型安全: Zod Schema 确保输入输出类型在编译期和运行期都安全
4.5 服务层 (Services)
4.5.1 API Client
// src/services/api/claude.ts
export async function* streamMessages(params: MessageStreamParams) {
const response = await anthropic.messages.create({
model: params.model,
messages: params.messages,
system: params.system,
tools: params.tools,
stream: true, // 关键:启用流式
...getBetaHeaders(),
});
for await (const event of response) {
yield event;
}
}
4.5.2 Permission System
// src/utils/permissions/PermissionMode.ts
export const PERMISSION_MODES = {
// 自动模式:只读工具自动批准
auto: 'auto',
// 自动模式 + 破坏性操作需确认
auto_with_diff: 'auto_with_diff',
// 手动模式:所有工具都需确认
manual: 'manual',
} as const;
export function canUseTool(tool: Tool, input: unknown, mode: PermissionMode): boolean {
if (mode === 'manual') return false;
if (tool.isReadOnly(input)) return true;
if (mode === 'auto_with_diff' && tool.isDestructive(input)) return false;
return true;
}
4.6 状态管理层 (State Management)
文件: src/state/AppStateStore.ts, src/state/store.ts
// store.ts - 自定义 Store 实现(仅34行)
export function createStore<T>(initialState: T, onChange?: OnChangeCallback<T>): Store<T> {
let state = initialState;
const listeners = new Set<() => void>();
return {
getState: () => state,
setState: (updater) => {
const prevState = state;
state = typeof updater === 'function'
? (updater as Function)(state)
: { ...state, ...updater };
if (state !== prevState) {
onChange?.({ newState: state, oldState: prevState });
listeners.forEach(fn => fn());
}
},
subscribe: (fn) => {
listeners.add(fn);
return () => listeners.delete(fn);
},
};
}
为什么选择自定义 Store 而不是 Redux/Zustand?
- 极简实现: 只有 34 行代码,零依赖
- React 集成: 通过
useSyncExternalStore与 React 无缝集成 - 性能优化: 细粒度订阅,避免不必要的重渲染
- 类型安全: 完整的 TypeScript 类型推断
五、数据流全景图
flowchart TB
subgraph InputPhase["输入阶段"]
A1[用户键盘输入] --> A2[终端 Raw Mode]
A2 --> A3[Ink 事件处理]
A3 --> A4[PromptInput 组件]
A4 --> A5[onSubmit 回调]
end
subgraph ProcessPhase["处理阶段"]
B1[REPL.onQuery] --> B2[QueryEngine.stream]
B2 --> B3[上下文组装]
B3 --> B4[System Prompt 构建]
B4 --> B5[工具列表收集]
end
subgraph APIPhase["API 阶段"]
C1[Anthropic API 调用] --> C2[SSE 流式响应]
C2 --> C3[事件解析]
C3 --> C4{类型判断}
C4 -->|text| C5[文本增量]
C4 -->|tool_use| C6[工具调用]
end
subgraph ToolPhase["工具阶段"]
D1[ToolRegistry 查找] --> D2[输入验证]
D2 --> D3[权限检查]
D3 --> D4[沙箱执行]
D4 --> D5[结果格式化]
end
subgraph OutputPhase["输出阶段"]
E1[消息追加] --> E2[State 更新]
E2 --> E3[React 重渲染]
E3 --> E4[Ink 虚拟 DOM]
E4 --> E5[ANSI 输出]
E5 --> E6[终端显示]
end
A5 --> B1
B5 --> C1
C6 --> D1
D5 --> C1
C5 --> E1
D5 --> E1
六、关键设计决策与权衡
6.1 为什么使用自定义 Store 而非 Redux?
| 维度 | Custom Store | Redux |
|---|---|---|
| 代码量 | 34 行 | ~500 行 + 依赖 |
| 学习成本 | 低 | 高 |
| 性能 | 手动优化订阅 | 需配合 reselect |
| DevTools | 无 | 有 |
| 类型安全 | 原生支持 | 需额外配置 |
决策理由: Claude Code 的状态结构相对扁平,不需要 Redux 的复杂特性。自定义 Store 提供了足够的功能,同时保持了代码的简洁性。
6.2 为什么使用 Ink 而非传统终端库?
- React 生态: 复用 React 的组件化思维,降低心智负担
- 声明式渲染: 描述"应该显示什么"而非"如何绘制"
- 增量更新: 自动处理差异更新,避免全屏重绘
6.3 流式处理 vs 批处理
// 流式处理 - 实际采用
for await (const chunk of stream) {
render(chunk); // 即时渲染
}
// 批处理 - 未采用
const fullResponse = await fetchFullResponse();
render(fullResponse); // 等待全部数据
流式优势: 首字节时间 (TTFB) 更快,用户体验更流畅,内存占用更低。
七、Sandbox 沙箱系统深度解析
7.1 沙箱架构设计
Claude Code 的沙箱系统是其安全架构的核心组件,用于隔离和限制 shell 命令的执行环境,防止恶意或意外操作对宿主系统造成损害。
7.1.1 沙箱整体架构图
graph TB
subgraph CLI["Claude Code CLI"]
BASH[BashTool.tsx]
PS[PowerShellTool.tsx]
SHELL[Shell.ts]
end
subgraph Adapter["Adapter Layer"]
ADAPTER[sandbox-adapter.ts]
SETTINGS[Settings]
PERM_CHECK[shouldUseSandbox]
end
subgraph Runtime["Runtime Layer"]
BASE[BaseSandboxManager]
CONFIG[SandboxRuntimeConfig]
POLICY[Policy Engine]
end
subgraph Platform["Platform Layer"]
subgraph macOS["macOS"]
SX[sandbox-exec]
SX_PROFILE[seatbelt profile]
end
subgraph Linux["Linux-WSL2"]
BW[bwrap]
SECCOMP[seccomp-bpf]
SOC[socat]
end
end
subgraph Restrictions["Restrictions"]
FS_READ[FsReadRestriction]
FS_WRITE[FsWriteRestriction]
NET[NetworkRestriction]
UNIX_SOCK[Unix Socket]
end
BASH --> ADAPTER
PS --> ADAPTER
SHELL --> ADAPTER
ADAPTER --> SETTINGS
ADAPTER --> PERM_CHECK
ADAPTER --> BASE
BASE --> CONFIG
BASE --> POLICY
BASE --> SX
BASE --> BW
SX --> SX_PROFILE
BW --> SECCOMP
BW --> SOC
CONFIG --> FS_READ
CONFIG --> FS_WRITE
CONFIG --> NET
CONFIG --> UNIX_SOCK
7.1.2 沙箱核心组件说明
| 组件 | 文件路径 | 职责 |
|---|---|---|
| SandboxAdapter | src/utils/sandbox/sandbox-adapter.ts | Claude Code 与沙箱运行时的桥梁,处理配置转换和平台适配 |
| BaseSandboxManager | @anthropic-ai/sandbox-runtime | 底层沙箱运行时,提供跨平台抽象 |
| shouldUseSandbox | src/tools/BashTool/shouldUseSandbox.ts | 决定是否启用沙箱的策略逻辑 |
| BashTool | src/tools/BashTool/BashTool.tsx | 调用沙箱执行 Bash 命令 |
| Shell.ts | src/utils/Shell.ts | 命令执行的核心,集成沙箱包装逻辑 |
7.2 沙箱实现原理
7.2.1 平台差异处理
macOS (sandbox-exec):
// macOS 使用系统内置的 sandbox-exec 工具
// 通过 seatbelt profile 定义沙箱规则
// 支持文件系统访问控制、网络访问控制、进程创建限制等
Linux/WSL2 (bubblewrap):
// Linux 使用 bubblewrap (bwrap) 创建用户命名空间
// 结合 seccomp-bpf 进行系统调用过滤
// 使用 socat 代理网络请求实现网络隔离
Windows:
// Windows 原生不支持沙箱(bwrap/sandbox-exec 都是 POSIX-only)
// 如果企业策略强制启用沙箱且禁止非沙箱命令,则拒绝执行
7.2.2 沙箱配置体系
// 沙箱配置结构(来自 settings.json)
interface SandboxSettings {
enabled: boolean // 是否启用沙箱
autoAllowBashIfSandboxed: boolean // 沙箱内自动允许 Bash
allowUnsandboxedCommands: boolean // 允许非沙箱命令作为 fallback
failIfUnavailable: boolean // 沙箱不可用时是否失败
enabledPlatforms: Platform[] // 启用沙箱的平台列表
excludedCommands: string[] // 排除的命令模式
// 文件系统限制
filesystem: {
allowRead: string[] // 允许读取的路径
allowWrite: string[] // 允许写入的路径
denyWriteWithin: string[] // 在 allowWrite 内禁止写入的子路径
}
// 网络限制
network: {
allowHosts: Array<{domain: string, ports?: number[]}>
denyHosts: Array<{domain: string, ports?: number[]}>
httpProxyPort?: number // HTTP 代理端口
socksProxyPort?: number // SOCKS 代理端口
}
// seccomp 配置(Linux)
seccomp?: {
bpfPath: string // seccomp BPF 文件路径
applyPath: string // apply 脚本路径
}
}
7.3 沙箱调用时序图
7.3.1 命令执行沙箱化完整流程
sequenceDiagram
autonumber
participant User as 用户
participant REPL as REPL.tsx
participant BT as BashTool
participant SUS as shouldUseSandbox
participant ADAPTER as SandboxAdapter
participant BASE as BaseSandboxManager
participant SHELL as Shell.ts
participant PROVIDER as BashProvider
participant PLATFORM as 平台沙箱
User->>REPL: 输入 Bash 命令
REPL->>BT: 调用 BashTool.call()
BT->>BT: 构建命令参数
BT->>SUS: shouldUseSandbox(input)
alt 沙箱未启用
SUS-->>BT: return false
BT->>SHELL: exec(command, {shouldUseSandbox: false})
SHELL->>PROVIDER: buildExecCommand
PROVIDER-->>SHELL: 普通命令字符串
SHELL->>SHELL: spawn(binShell, args)
else 沙箱启用
SUS-->>BT: return true
BT->>SHELL: exec(command, {shouldUseSandbox: true})
SHELL->>PROVIDER: buildExecCommand(command, {useSandbox: true})
PROVIDER->>PROVIDER: 构建命令字符串
PROVIDER-->>SHELL: {commandString, cwdFilePath}
SHELL->>ADAPTER: wrapWithSandbox(commandString, binShell)
ADAPTER->>ADAPTER: 检查初始化状态
ADAPTER->>BASE: wrapWithSandbox(command, binShell, config)
alt macOS
BASE->>PLATFORM: sandbox-exec -f profile command
PLATFORM->>PLATFORM: 应用 seatbelt 策略
PLATFORM-->>BASE: 包装后的命令
else Linux/WSL2
BASE->>PLATFORM: bwrap --bind ... --seccomp ... command
PLATFORM->>PLATFORM: 创建用户命名空间
PLATFORM->>PLATFORM: 应用 seccomp 过滤器
PLATFORM-->>BASE: 包装后的命令
end
BASE-->>ADAPTER: 沙箱包装后的命令字符串
ADAPTER-->>SHELL: 完整沙箱命令
SHELL->>SHELL: mkdir(sandboxTmpDir, 0o700)
SHELL->>SHELL: spawn(spawnBinary, shellArgs, {env, cwd})
SHELL-->>BT: 返回 ShellCommand
end
BT->>BT: 等待命令执行完成
alt 沙箱执行
BT->>ADAPTER: cleanupAfterCommand()
ADAPTER->>BASE: cleanupAfterCommand()
BASE->>PLATFORM: 清理沙箱残留
end
BT-->>REPL: 返回执行结果
REPL-->>User: 显示输出
7.3.2 沙箱初始化流程
sequenceDiagram
autonumber
participant REPL as REPL.tsx
participant ADAPTER as SandboxAdapter
participant BASE as BaseSandboxManager
participant SETTINGS as Settings
participant PLATFORM as 平台沙箱
REPL->>REPL: useEffect 检查沙箱状态
alt 沙箱已启用但依赖缺失
REPL->>ADAPTER: getSandboxUnavailableReason()
ADAPTER->>ADAPTER: isSandboxingEnabled()
ADAPTER->>SETTINGS: getSettings_DEPRECATED()
ADAPTER->>ADAPTER: checkDependencies()
ADAPTER-->>REPL: 返回不可用原因
REPL->>REPL: 显示警告通知
else 沙箱已启用且可用
REPL->>ADAPTER: initialize(sandboxAskCallback)
ADAPTER->>ADAPTER: 检查 initializationPromise
alt 已初始化
ADAPTER-->>REPL: 返回现有 Promise
else 未初始化
ADAPTER->>BASE: initialize()
BASE->>BASE: 检查平台支持
alt macOS
BASE->>PLATFORM: 检查 sandbox-exec 可用性
PLATFORM-->>BASE: 返回检查结果
else Linux
BASE->>PLATFORM: 检查 bwrap 可用性
BASE->>PLATFORM: 检查 socat 可用性
BASE->>PLATFORM: 检查 seccomp 可用性
PLATFORM-->>BASE: 返回检查结果
end
BASE->>BASE: 加载安全策略配置
BASE->>BASE: 初始化网络代理(如需要)
BASE-->>ADAPTER: 初始化完成
ADAPTER-->>REPL: Promise 完成
end
end
7.3.3 沙箱权限决策流程
sequenceDiagram
autonumber
participant BT as BashTool
participant SUS as shouldUseSandbox
participant ADAPTER as SandboxAdapter
participant SETTINGS as Settings
BT->>SUS: shouldUseSandbox({command, dangerouslyDisableSandbox})
SUS->>ADAPTER: isSandboxingEnabled()
ADAPTER->>ADAPTER: isSupportedPlatform()
ADAPTER->>ADAPTER: checkDependencies()
ADAPTER->>SETTINGS: getSandboxEnabledSetting()
ADAPTER-->>SUS: 返回启用状态
alt 沙箱未启用
SUS-->>BT: return false
else 沙箱已启用
alt dangerouslyDisableSandbox && allowUnsandboxedCommands
SUS-->>BT: return false
else 命令在排除列表中
SUS->>SUS: containsExcludedCommand(command)
SUS->>SETTINGS: getExcludedCommands()
SUS-->>BT: return false
else 启用沙箱
SUS-->>BT: return true
end
end
7.4 代码调用栈详解
7.4.1 沙箱命令执行调用栈
BashTool.call() [src/tools/BashTool/BashTool.tsx:881]
└── exec() [src/utils/Shell.ts:181]
├── provider.buildExecCommand() [src/utils/shell/bashProvider.ts:77]
│ └── 构建命令字符串 + cwd 追踪
├── SandboxManager.wrapWithSandbox() [src/utils/sandbox/sandbox-adapter.ts:704]
│ └── BaseSandboxManager.wrapWithSandbox()
│ ├── macOS: 生成 sandbox-exec 命令
│ └── Linux: 生成 bwrap 命令
├── mkdir(sandboxTmpDir, 0o700) [src/utils/Shell.ts:268]
└── spawn(spawnBinary, shellArgs) [src/utils/Shell.ts:316]
└── child_process.spawn()
7.4.2 沙箱初始化调用栈
REPL.tsx useEffect [src/screens/REPL.tsx:2316]
└── SandboxManager.initialize() [src/utils/sandbox/sandbox-adapter.ts:730]
└── BaseSandboxManager.initialize()
├── isSupportedPlatform() 检查平台支持
├── checkDependencies() 检查依赖
│ ├── macOS: 检查 sandbox-exec
│ └── Linux: 检查 bwrap, socat, seccomp
└── 加载配置文件
7.4.3 沙箱决策调用栈
BashTool.call() [src/tools/BashTool/BashTool.tsx:896]
└── shouldUseSandbox() [src/tools/BashTool/shouldUseSandbox.ts:130]
├── SandboxManager.isSandboxingEnabled()
│ ├── isSupportedPlatform() [src/utils/sandbox/sandbox-adapter.ts:491]
│ ├── checkDependencies() [src/utils/sandbox/sandbox-adapter.ts:451]
│ └── getSandboxEnabledSetting() [src/utils/sandbox/sandbox-adapter.ts:435]
├── SandboxManager.areUnsandboxedCommandsAllowed()
└── containsExcludedCommand() [src/tools/BashTool/shouldUseSandbox.ts:21]
7.5 关键源码分析
7.5.1 沙箱包装逻辑 (Shell.ts)
// src/utils/Shell.ts:259-273
if (shouldUseSandbox) {
// 使用 SandboxManager 包装命令
commandString = await SandboxManager.wrapWithSandbox(
commandString,
sandboxBinShell,
undefined,
abortSignal,
)
// 创建沙箱临时目录(仅沙箱进程可写)
try {
const fs = getFsImplementation()
await fs.mkdir(sandboxTmpDir, { mode: 0o700 })
} catch (error) {
logForDebugging(`Failed to create ${sandboxTmpDir} directory: ${error}`)
}
}
7.5.2 沙箱决策逻辑 (shouldUseSandbox.ts)
// src/tools/BashTool/shouldUseSandbox.ts:130-153
export function shouldUseSandbox(input: Partial<SandboxInput>): boolean {
// 1. 检查沙箱是否启用
if (!SandboxManager.isSandboxingEnabled()) {
return false
}
// 2. 检查是否显式禁用沙箱且允许非沙箱命令
if (
input.dangerouslyDisableSandbox &&
SandboxManager.areUnsandboxedCommandsAllowed()
) {
return false
}
// 3. 检查命令是否在用户排除列表中
if (containsExcludedCommand(input.command)) {
return false
}
return true
}
7.5.3 沙箱适配器核心 (sandbox-adapter.ts)
// src/utils/sandbox/sandbox-adapter.ts:704-725
async function wrapWithSandbox(
command: string,
binShell?: string,
customConfig?: Partial<SandboxRuntimeConfig>,
abortSignal?: AbortSignal,
): Promise<string> {
// 确保沙箱初始化完成
if (isSandboxingEnabled()) {
if (initializationPromise) {
await initializationPromise
} else {
throw new Error('Sandbox failed to initialize.')
}
}
// 委托给底层沙箱运行时
return BaseSandboxManager.wrapWithSandbox(
command,
binShell,
customConfig,
abortSignal,
)
}
7.6 安全设计要点
- 分层安全策略: 沙箱作为深度防御的一环,与权限系统、命令验证形成多层防护
- 最小权限原则: 沙箱内进程只能访问明确允许的文件路径和网络端点
- 平台原生方案: 使用各平台原生沙箱机制(sandbox-exec/bwrap),而非自定义实现
- 配置驱动: 所有沙箱规则通过配置文件管理,支持企业策略锁定
- 优雅降级: 支持
allowUnsandboxedCommands配置,在沙箱不可用时允许非沙箱执行
八、总结
Claude Code 的架构设计体现了以下核心原则:
- 分层清晰: 每一层职责单一,接口明确
- 流式优先: 从输入到输出全链路流式处理
- 类型安全: TypeScript + Zod 全链路类型保障
- 性能导向: 早期输入捕获、动态导入、细粒度订阅
- 扩展友好: 工具系统插件化,新功能易于接入
- 安全优先: 多层沙箱防护,企业级安全策略支持
这种架构使得 Claude Code 能够在保持代码可维护性的同时,提供流畅且安全的交互体验。