十、统一 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:让调用方知道实际执行的是什么模式
相关推荐
回眸&啤酒鸭2 天前
【回眸】Minicart 电商购物车核心功能落地指南
人工智能
一隅论数智2 天前
给AI一张“业务概念地图“:本体如何从哲学走向企业智能
大数据·人工智能·经验分享·笔记·学习·学习方法·政务
AI的探索之旅2 天前
97 个 OpenCV 实例(三十):双目立体,从标定到点云
人工智能·opencv·计算机视觉
AlbertZein2 天前
Step-5-Preview 上手实测:3D 游戏、金融分析、网页设计一次跑完
人工智能·aigc
LaughingZhu2 天前
Product Hunt 每日热榜 | 2026-09-19
人工智能·深度学习·神经网络·搜索引擎·百度
美狐美颜SDK开放平台2 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
wukangjupingbb2 天前
智能网联汽车安全能力框架
人工智能
龙亘川2 天前
明月照湾区,智启新赛道:从顶流文旅IP盛会看智慧文旅升级路径
人工智能·智慧城市·开源软件·数据可视化
飞猫的边缘AI2 天前
边缘AI应用:家用AI摄像头怎么做数据训练?
人工智能·边缘计算·ai算法·边缘ai