Claude Code 架构深度剖析:从终端输入到大模型响应的完整过程

53 阅读3分钟

深度解析 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_use block,无缝切换到工具执行
  • 错误恢复: 内置重试机制和错误处理,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 StoreRedux
代码量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 沙箱核心组件说明

组件文件路径职责
SandboxAdaptersrc/utils/sandbox/sandbox-adapter.tsClaude Code 与沙箱运行时的桥梁,处理配置转换和平台适配
BaseSandboxManager@anthropic-ai/sandbox-runtime底层沙箱运行时,提供跨平台抽象
shouldUseSandboxsrc/tools/BashTool/shouldUseSandbox.ts决定是否启用沙箱的策略逻辑
BashToolsrc/tools/BashTool/BashTool.tsx调用沙箱执行 Bash 命令
Shell.tssrc/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 安全设计要点

  1. 分层安全策略: 沙箱作为深度防御的一环,与权限系统、命令验证形成多层防护
  2. 最小权限原则: 沙箱内进程只能访问明确允许的文件路径和网络端点
  3. 平台原生方案: 使用各平台原生沙箱机制(sandbox-exec/bwrap),而非自定义实现
  4. 配置驱动: 所有沙箱规则通过配置文件管理,支持企业策略锁定
  5. 优雅降级: 支持 allowUnsandboxedCommands 配置,在沙箱不可用时允许非沙箱执行

八、总结

Claude Code 的架构设计体现了以下核心原则:

  1. 分层清晰: 每一层职责单一,接口明确
  2. 流式优先: 从输入到输出全链路流式处理
  3. 类型安全: TypeScript + Zod 全链路类型保障
  4. 性能导向: 早期输入捕获、动态导入、细粒度订阅
  5. 扩展友好: 工具系统插件化,新功能易于接入
  6. 安全优先: 多层沙箱防护,企业级安全策略支持

这种架构使得 Claude Code 能够在保持代码可维护性的同时,提供流畅且安全的交互体验。