十、统一 Runner 入口——能力检测与模式回退

本篇讲解 src/runner.ts------沙箱命令的统一执行入口。它把 Seatbelt runner、shell fallback、能力检测、danger-full-access 回退等逻辑组合在一起。

1. Runner 解决什么问题?

不同平台的沙箱能力不同:

平台 原生沙箱 可用模式
macOS Seatbelt(sandbox-exec) read-only / workspace-write
Linux 无原生实现 需要 bubblewrap 等
Windows 无原生实现 ---

当 OS 沙箱不可用时(比如在 Linux 上),怎么办?

  1. 直接报错(fail-closed,最安全)
  2. 问用户要不要回退到 danger-full-access(用户说了算)
  3. 自动回退到 danger-full-access(最不安全,不推荐)

runner.ts 就是来处理这些决策的。

2. runSandboxedCommand------统一入口

typescript 复制代码
export async function runSandboxedCommand(options: {
  commandSpec: SandboxCommandSpec;
  mode: SandboxMode;
  timeoutMs: number;
  executionRoot: string;
  profilesDir: string;
  allowDangerousFallback?: boolean;
  spawnEnv?: NodeJS.ProcessEnv;
  io?: RunSandboxedIoHooks;
  onOsSandboxUnavailable?: (ctx: OsSandboxUnavailableContext) => Promise<OsSandboxFallbackDecision>;
}): Promise<{ exitCode: number; effectiveMode: SandboxMode }> {

输入 :命令规格 + 沙箱配置 输出:退出码 + 实际执行模式

2.1 核心流程

sql 复制代码
请求的模式
    │
    ▼
resolveEffectiveMode()
    │
    ├── danger-full-access → 直接 spawn
    │
    ├── read-only / workspace-write 且 OS 沙箱可用 → Seatbelt runner
    │
    ├── read-only / workspace-write 且 OS 沙箱不可用 → 问回调
    │       │
    │       ├── 回调返回 danger-full-access → 直接 spawn
    │       ├── 回调返回 abort → 退出码 1
    │       └── 无回调 → 抛错(fail-closed)
    │
    └── null(abort)→ 退出码 1

3. resolveEffectiveMode------决定实际执行模式

typescript 复制代码
async function resolveEffectiveMode(options: {
  requestedMode: SandboxMode;
  profilesDir: string;
  allowDangerousFallback?: boolean;
  onOsSandboxUnavailable?: (ctx: OsSandboxUnavailableContext) => Promise<OsSandboxFallbackDecision>;
}): Promise<SandboxMode | null> {
  // 1. danger-full-access 直接返回
  if (options.requestedMode === 'danger-full-access') {
    return 'danger-full-access';
  }

  // 2. 检查 OS 沙箱能力
  const capability = checkOsSandboxCapability(options.requestedMode, options.profilesDir);
  if (capability.available) {
    return options.requestedMode;  // OS 沙箱可用,用请求的模式
  }

  // 3. OS 沙箱不可用,没回调 → 抛错
  if (!options.onOsSandboxUnavailable) {
    throw new Error(
      `${capability.reason} (fail-closed: set onOsSandboxUnavailable to explicitly fallback or abort)`,
    );
  }

  // 4. 问回调
  const decision = await options.onOsSandboxUnavailable({
    reason: capability.reason,
    requestedMode: options.requestedMode,
    platform: process.platform,
  });

  // 5. 回调想回退但配置不允许 → 抛错
  if (decision === 'danger-full-access' && !options.allowDangerousFallback) {
    throw new Error(
      `${capability.reason} (danger-full-access fallback requested but disabled by configuration)`,
    );
  }

  // 6. 返回最终决策
  return decision === 'abort' ? null : 'danger-full-access';
}

返回值

  • SandboxMode → 用这个模式执行
  • null → 用户选择 abort,不执行

4. checkOsSandboxCapability------能力检测

typescript 复制代码
export function checkOsSandboxCapability(
  mode: Exclude<SandboxMode, 'danger-full-access'>,
  profilesDir: string,
): OsSandboxCapability {
  if (process.platform === 'darwin') {
    return { ...checkSeatbeltCapability(mode, profilesDir), platform: process.platform };
  }

  return {
    available: false,
    platform: process.platform,
    reason: `OS sandbox is not implemented on ${process.platform} for mode "${mode}".`,
  };
}

目前只支持 macOS(Seatbelt)。其他平台返回 available: false

5. runWithOsSandbox------通过 OS 沙箱执行

typescript 复制代码
function runWithOsSandbox(options: {
  commandSpec: SandboxCommandSpec;
  mode: Exclude<SandboxMode, 'danger-full-access'>;
  profilesDir: string;
  workspace: string;
  timeoutMs: number;
  spawnEnv?: NodeJS.ProcessEnv;
  io?: RunSandboxedIoHooks;
}): Promise<number> {
  switch (process.platform) {
    case 'darwin': {
      const spec = buildSeatbeltSpawnSpec({
        commandSpec: options.commandSpec,
        mode: options.mode,
        profilesDir: options.profilesDir,
        workspace: options.workspace,
      });
      return spawnAndWait(
        spec.command,
        spec.args,
        options.timeoutMs,
        { cwd: options.workspace, env: options.spawnEnv },
        options.io,
      );
    }
    default:
      return Promise.reject(new Error('OS sandbox not implemented on this platform.'));
  }
}

6. 完整执行流程

typescript 复制代码
export async function runSandboxedCommand(options) {
  // 1. 确保执行根目录存在
  const executionRoot = path.resolve(options.executionRoot);
  if (!fs.existsSync(executionRoot)) {
    fs.mkdirSync(executionRoot, { recursive: true });
  }

  // 2. 决定实际执行模式
  const effectiveMode = await resolveEffectiveMode({
    requestedMode: options.mode,
    profilesDir: options.profilesDir,
    allowDangerousFallback: options.allowDangerousFallback,
    onOsSandboxUnavailable: options.onOsSandboxUnavailable,
  });

  // 3. abort → 返回退出码 1
  if (effectiveMode === null) {
    return { exitCode: 1, effectiveMode: options.mode };
  }

  // 4. danger-full-access → 直接 spawn
  if (effectiveMode === 'danger-full-access') {
    const exitCode = options.commandSpec.kind === 'exec'
      ? await spawnAndWait(options.commandSpec.executable, options.commandSpec.args, ...)
      : await spawnShellUnsafe(options.commandSpec.shellCommand, ...);
    return { exitCode, effectiveMode };
  }

  // 5. OS 沙箱模式 → Seatbelt
  const exitCode = await runWithOsSandbox({ ...options, mode: effectiveMode });
  return { exitCode, effectiveMode };
}

7. 模式回退示例

7.1 macOS 上正常执行

arduino 复制代码
请求: workspace-write
OS 沙箱可用 → Seatbelt 执行
effectiveMode = workspace-write

7.2 Linux 上无 OS 沙箱,用户同意回退

ini 复制代码
请求: workspace-write
OS 沙箱不可用 → 问回调
回调返回 danger-full-access → 直接 spawn
effectiveMode = danger-full-access
dangerousFallbackUsed = true

7.3 Linux 上无 OS 沙箱,用户拒绝

ini 复制代码
请求: workspace-write
OS 沙箱不可用 → 问回调
回调返回 abort → 不执行
exitCode = 1

8. 小结

函数 作用
runSandboxedCommand 统一入口:决定执行方式并执行
resolveEffectiveMode 决定实际执行模式(可能回退)
checkOsSandboxCapability 检查 OS 沙箱是否可用
runWithOsSandbox 通过 OS 沙箱执行(目前只有 macOS)

关键设计:

  1. Fail-closed:没回调就抛错,不自动回退
  2. 用户决策:有回调就让用户选------回退还是中止
  3. 配置守卫allowDangerousFallback 必须为 true 才允许回退
  4. 返回 effectiveMode:让调用方知道实际执行的是什么模式
相关推荐
装不满的克莱因瓶1 小时前
了解 LangChain 中的 LLM 与 ChatModel 的差异
人工智能·python·ai·langchain·llm·agent·chatmodel
dingzd951 小时前
跨境社媒运营越到后面 越比拼账号的表达稳定性
大数据·人工智能·矩阵·内容营销
云烟成雨TD1 小时前
Spring AI 1.x 系列【54】Retry 机制分析
java·人工智能·spring
没事别瞎琢磨1 小时前
八、环境隔离——构建安全的子进程环境
人工智能·node.js
手写码匠1 小时前
从零实现 Prompt 工程引擎:结构化提示、自动优化与多轮自省体系
人工智能·深度学习·算法·aigc
甲维斯1 小时前
Claude Fable5首测,GPT5.5和国产模型弱爆了!
人工智能
2301_818527781 小时前
瑜伽服面料科技——AI加速创新材料研发
人工智能
键盘侠伍十七1 小时前
Gandalf Lakera AI Prompt Injection 靶场深度教程:从 Level 1 到 Level 8 全面攻防解析
人工智能·prompt·ai安全
调试优选官1 小时前
2026年上海GEO优化公司全景透视:技术路线、选型逻辑与实施路径
人工智能·技术分享·geo·上海