chore: initialize qiming workspace repository

This commit is contained in:
Codex
2026-05-29 14:22:48 +08:00
commit bfd67a0f2c
10750 changed files with 1885711 additions and 0 deletions

View File

@@ -0,0 +1,535 @@
# QimingClaw Sandbox API 文档
> **版本**: 1.0.0
> **更新日期**: 2026-03-27
---
## 1. 概述
QimingClaw Sandbox API 提供统一的沙箱执行接口,支持 macOS、Linux 和 Windows 平台。
---
## 2. TypeScript API
### 2.1 QimingSandbox 类
```typescript
import { QimingSandbox, SandboxConfig, ExecuteResult } from '@qiming/sandbox-native';
const sandbox = new QimingSandbox();
```
#### 2.1.1 构造函数
```typescript
constructor()
```
创建沙箱实例。
**示例**:
```typescript
const sandbox = new QimingSandbox();
```
---
#### 2.1.2 execute 方法
```typescript
async execute(
command: string,
cwd: string,
config?: Partial<SandboxConfig>
): Promise<ExecuteResult>
```
在沙箱中执行命令。
**参数**:
| 参数 | 类型 | 必需 | 说明 |
|------|------|------|------|
| `command` | string | ✅ | 要执行的命令 |
| `cwd` | string | ✅ | 工作目录 |
| `config` | SandboxConfig | ❌ | 沙箱配置 |
**返回值**:
```typescript
interface ExecuteResult {
exitCode: number; // 退出码
stdout: string; // 标准输出
stderr: string; // 标准错误
}
```
**示例**:
```typescript
const result = await sandbox.execute(
'npm install',
'/Users/user/project',
{
networkEnabled: true,
memoryLimit: '2g',
timeout: 300,
}
);
console.log(result.exitCode); // 0
console.log(result.stdout); // "added 100 packages..."
```
---
#### 2.1.3 isAvailable 方法
```typescript
isAvailable(): boolean
```
检查沙箱是否可用。
**返回值**: `true` 表示可用
**示例**:
```typescript
if (sandbox.isAvailable()) {
console.log('Sandbox is available');
}
```
---
### 2.2 配置类型
#### SandboxConfig
```typescript
interface SandboxConfig {
// 网络配置
networkEnabled?: boolean; // 是否启用网络
allowedDomains?: string[]; // 允许的域名
deniedDomains?: string[]; // 禁止的域名
// 资源限制
memoryLimit?: string; // 内存限制 (e.g., "2g")
cpuLimit?: number; // CPU 核心数限制
timeout?: number; // 超时时间(秒)
// 文件系统
allowRead?: string[]; // 允许读取的路径
denyRead?: string[]; // 禁止读取的路径
allowWrite?: string[]; // 允许写入的路径
denyWrite?: string[]; // 禁止写入的路径
}
```
**默认值**:
```typescript
const DEFAULT_CONFIG: SandboxConfig = {
networkEnabled: false,
memoryLimit: '2g',
cpuLimit: 2,
timeout: 300,
allowRead: ['.'],
denyRead: ['~/.ssh', '~/.aws', '~/.gnupg'],
allowWrite: ['.', '/tmp'],
denyWrite: ['.env', '*.pem', '*.key'],
};
```
---
## 3. 配置管理 API
### 3.1 SandboxConfigManager
```typescript
import { SandboxConfigManager } from '@main/services/sandbox/SandboxConfigManager';
const configManager = new SandboxConfigManager();
```
#### 3.1.1 getConfig
```typescript
getConfig(): SandboxConfig
```
获取当前沙箱配置。
**示例**:
```typescript
const config = configManager.getConfig();
console.log(config.mode); // "non-main"
```
---
#### 3.1.2 updateConfig
```typescript
updateConfig(updates: Partial<SandboxConfig>): void
```
更新沙箱配置。
**示例**:
```typescript
configManager.updateConfig({
mode: 'all',
network: {
enabled: true,
allowedDomains: ['github.com'],
},
});
```
---
#### 3.1.3 setMode
```typescript
setMode(mode: SandboxMode): void
```
设置沙箱模式。
**参数**:
- `mode`: `"off"` | `"on-demand"` | `"non-main"` | `"all"`
**示例**:
```typescript
configManager.setMode('non-main');
```
---
#### 3.1.4 isEnabled
```typescript
isEnabled(sessionId?: string): boolean
```
检查沙箱是否启用。
**参数**:
- `sessionId`: 会话 ID可选
**返回值**:
- `true` 表示启用
**示例**:
```typescript
// 检查全局是否启用
const globalEnabled = configManager.isEnabled();
// 检查特定会话是否启用
const sessionEnabled = configManager.isEnabled('session-123');
```
---
## 4. IPC API
### 4.1 渲染进程调用
#### 4.1.1 获取配置
```typescript
const config = await window.electron.invoke('sandbox:get-config');
```
#### 4.1.2 设置模式
```typescript
await window.electron.invoke('sandbox:set-mode', 'non-main');
```
#### 4.1.3 更新配置
```typescript
await window.electron.invoke('sandbox:update-config', {
network: {
enabled: true,
},
});
```
#### 4.1.4 重置配置
```typescript
await window.electron.invoke('sandbox:reset-config');
```
#### 4.1.5 获取状态
```typescript
const status = await window.electron.invoke('sandbox:get-status');
// {
// enabled: true,
// mode: 'non-main',
// platform: 'darwin',
// available: true,
// type: 'seatbelt'
// }
```
---
## 5. Rust API (内部使用)
### 5.1 SandboxInterface
```rust
pub trait SandboxInterface {
/// 初始化沙箱
fn initialize(&mut self, config: SandboxConfig) -> Result<(), SandboxError>;
/// 执行命令
fn execute(&self, command: &str, cwd: &str, config: &SandboxConfig) -> Result<ExecuteResult, SandboxError>;
/// 读取文件
fn read_file(&self, session_id: &str, path: &str) -> Result<String, SandboxError>;
/// 写入文件
fn write_file(&self, session_id: &str, path: &str, content: &str) -> Result<(), SandboxError>;
/// 检查是否可用
fn is_available(&self) -> bool;
/// 获取状态
fn get_status(&self) -> SandboxStatus;
/// 清理资源
fn cleanup(&mut self) -> Result<(), SandboxError>;
}
```
### 5.2 配置结构
```rust
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SandboxConfig {
pub network_enabled: Option<bool>,
pub memory_limit: Option<String>,
pub cpu_limit: Option<i32>,
pub timeout: Option<i32>,
pub allowed_domains: Option<Vec<String>>,
pub denied_domains: Option<Vec<String>>,
pub allow_read: Option<Vec<String>>,
pub deny_read: Option<Vec<String>>,
pub allow_write: Option<Vec<String>>,
pub deny_write: Option<Vec<String>>,
}
```
### 5.3 执行结果
```rust
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExecuteResult {
pub exit_code: i32,
pub stdout: String,
pub stderr: String,
}
```
---
## 6. 错误处理
### 6.1 错误类型
```typescript
enum SandboxErrorCode {
SANDBOX_UNAVAILABLE = 'SANDBOX_UNAVAILABLE',
EXECUTION_FAILED = 'EXECUTION_FAILED',
TIMEOUT = 'TIMEOUT',
PERMISSION_DENIED = 'PERMISSION_DENIED',
RESOURCE_LIMIT = 'RESOURCE_LIMIT',
}
```
### 6.2 错误示例
```typescript
try {
const result = await sandbox.execute('rm -rf /', '/tmp');
} catch (error) {
if (error.code === 'PERMISSION_DENIED') {
console.error('Permission denied');
}
}
```
---
## 7. 使用示例
### 7.1 基本使用
```typescript
import { QimingSandbox } from '@qiming/sandbox-native';
const sandbox = new QimingSandbox();
// 执行简单命令
const result = await sandbox.execute('ls -la', '/Users/user');
console.log(result.stdout);
```
### 7.2 网络访问
```typescript
// 允许网络访问
const result = await sandbox.execute(
'npm install',
'/Users/user/project',
{
networkEnabled: true,
allowedDomains: ['registry.npmjs.org'],
}
);
```
### 7.3 资源限制
```typescript
// 限制资源使用
const result = await sandbox.execute(
'cargo build --release',
'/Users/user/rust-project',
{
memoryLimit: '4g',
cpuLimit: 4,
timeout: 600, // 10 分钟
}
);
```
### 7.4 文件系统隔离
```typescript
// 限制文件访问
const result = await sandbox.execute(
'python script.py',
'/Users/user/project',
{
allowRead: ['/Users/user/project', '/usr/local/lib'],
denyRead: ['~/.ssh', '~/.aws'],
allowWrite: ['/Users/user/project', '/tmp'],
denyWrite: ['.env'],
}
);
```
---
## 8. 平台特定行为
### 8.1 macOS (sandbox-exec)
```typescript
// macOS 使用 Seatbelt 配置文件
// 自动生成 .sb 配置文件
// 示例配置:
(version 1)
(allow default)
(allow file-read* (subpath "/Users/user/project"))
(deny file-read* (subpath "~/.ssh"))
```
### 8.2 Linux (bubblewrap)
```bash
# Linux 使用 bubblewrap 命令
bwrap \
--ro-bind /usr /usr \
--bind /workspace /workspace \
--unshare-all \
--die-with-parent \
bash -c "command"
```
### 8.3 Windows (Codex Sandbox)
```rust
// Windows 使用 Codex 实现
// 基于 Windows Job Objects 和 Restricted Tokens
// 自动处理进程隔离和权限限制
```
---
## 9. 性能指标
| 操作 | macOS | Linux | Windows |
|------|-------|-------|---------|
| **启动时间** | <50ms | <100ms | <200ms |
| **命令执行** | 原生 | 原生 | 轻微开销 |
| **内存占用** | ~10MB | ~20MB | ~50MB |
| **网络过滤** | 系统 | iptables | WinAPI |
| **文件隔离** | 系统 | 命名空间 | Job Objects |
---
## 10. 最佳实践
### 10.1 始终检查可用性
```typescript
if (!sandbox.isAvailable()) {
console.warn('Sandbox not available, running unsandboxed');
}
```
### 10.2 设置合理的超时
```typescript
const result = await sandbox.execute(command, cwd, {
timeout: 300, // 5 分钟
});
```
### 10.3 限制网络访问
```typescript
// 仅允许必要的域名
const result = await sandbox.execute(command, cwd, {
networkEnabled: true,
allowedDomains: ['github.com', 'registry.npmjs.org'],
});
```
### 10.4 处理错误
```typescript
try {
const result = await sandbox.execute(command, cwd, config);
if (result.exitCode !== 0) {
console.error('Command failed:', result.stderr);
}
} catch (error) {
console.error('Sandbox error:', error);
}
```
---
**文档版本**: 1.0.0
**最后更新**: 2026-03-27

View File

@@ -0,0 +1,590 @@
# QimingClaw Agent 沙箱架构设计
> **版本**: 1.1.1
> **更新日期**: 2026-04-10
> **状态**: 已实现(持续迭代)
---
## 1. 概述
### 1.1 目标
为 QimingClaw Agent Electron 客户端提供一个**安全、可配置、开箱即用**的沙箱执行环境。
### 1.2 设计原则
| 原则 | 说明 | 实现方式 |
|------|------|---------|
| **多平台支持** | macOS / Linux / Windows | `PlatformAdapter` 统一抽象 + 各平台实现 |
| **可配置** | 用户可控制是否启用 | 四种模式配置 |
| **开箱即用** | 无需手动安装依赖 | 预编译 + 系统内置 |
| **渐进增强** | 自动选择最优方案 | 平台检测 + 降级 |
| **安全隔离** | 保护用户系统安全 | 系统级沙箱 |
---
## 2. 架构设计
### 2.1 整体架构
```
┌─────────────────────────────────────────────────────────────┐
│ QimingClaw 沙箱架构 │
├─────────────────────────────────────────────────────────────┤
│ │
│ Layer 1: 配置管理 (SandboxConfig) │
│ ├─ 模式: off / on-demand / non-main / all │
│ ├─ 存储: electron-store │
│ └─ UI: 设置界面 + 首次引导 │
│ │
│ Layer 2: 平台抽象 (PlatformAdapter) │
│ ├─ 统一平台能力isWindows/isMacOS/isLinux
│ ├─ 统一命令探测与路径分隔符 │
│ ├─ 统一 helper/bwrap 资源解析 │
│ └─ 统一推荐 backend/type │
│ │
│ Layer 3: 沙箱实现 (SandboxInterface) │
│ ├─ macOS: sandbox-exec (系统内置) │
│ ├─ Linux: bubblewrap (系统可用) │
│ ├─ Windows: Codex Sandbox (预编译二进制) │
│ └─ None: 无沙箱 (关闭模式) │
│ │
│ Layer 4: 权限管理 (PermissionManager) │
│ ├─ 权限检查 │
│ ├─ 用户审批 │
│ └─ 审计日志 │
│ │
└─────────────────────────────────────────────────────────────┘
```
### 2.2 目录结构
```
qiming-agent/
├── crates/
│ ├── qiming-sandbox/ # 沙箱源码(独立维护)
│ │ ├── Cargo.toml
│ │ ├── README.md
│ │ ├── LICENSE (Apache-2.0)
│ │ │
│ │ ├── crates/
│ │ │ ├── windows-sandbox/ # Windows 实现(基于 Codex
│ │ │ │ ├── Cargo.toml
│ │ │ │ ├── src/
│ │ │ │ │ ├── lib.rs
│ │ │ │ │ ├── sandbox.rs
│ │ │ │ │ ├── process.rs
│ │ │ │ │ └── network.rs
│ │ │ │ └── tests/
│ │ │ │
│ │ │ ├── macos-sandbox/ # macOS 封装
│ │ │ └── linux-sandbox/ # Linux 封装
│ │ │
│ │ ├── bindings/ # Node.js 绑定
│ │ │ ├── package.json
│ │ │ ├── native/ # .gitignore
│ │ │ └── index.ts # TypeScript 封装
│ │ │
│ │ └── prebuilt/ # 预编译产物(入库)
│ │ ├── darwin-x64/
│ │ ├── linux-x64/
│ │ └── win32-x64/
│ │ ├── index.node
│ │ └── qiming-sandbox.exe
│ │
│ └── agent-electron-client/
│ ├── docs/sandbox/ # 沙箱文档
│ │ ├── ARCHITECTURE.md # 本文档
│ │ ├── IMPLEMENTATION.md # 实施指南
│ │ └── API.md # API 文档
│ │
│ ├── scripts/
│ │ └── prepare-sandbox.ts # 复制预编译产物
│ │
│ └── resources/
│ └── sandbox/ # 运行时产物(不入库)
│ ├── darwin-x64/
│ ├── linux-x64/
│ └── win32-x64/
│ └── qiming-sandbox.exe
```
### 2.3 统一平台抽象v1.1.1
`src/main/services/system/platformAdapter.ts` 提供统一平台能力抽象:
- `createPlatformAdapter()`:统一 `darwin/linux/win32` 分支入口
- `getCommandProbe()`:统一 `which/where` 命令探测
- `getRecommendedSandboxBackend()/Type()`:统一 backend/type 推荐
- `resolveBundledLinuxBwrapPath()`:统一 Linux bwrap 内置路径解析
- `resolveBundledSandboxHelperPath()`:统一 helper 内置路径解析
当前已接入 sandbox 关键链路:
- `system/shellEnv.ts`
- `sandbox/policy.ts`
- `sandbox/SandboxManager.ts`
- `sandbox/serviceBootstrap.ts`
- `sandbox/SandboxInvoker.ts`
- `engines/acp/acpTerminalManager.ts`
- `engines/acp/acpClient.ts`
- `engines/acp/strictPermissionGuard.ts`
- `utils/processTree.ts`
---
## 3. 平台实现方案
### 3.1 macOS: sandbox-exec
**技术**: Seatbelt (sandbox-exec)
**优势**:
- ✅ 系统内置macOS 10.5+
- ✅ 零依赖
- ✅ 性能最优(~5% 开销)
**配置示例**:
```scheme
(version 1)
(allow default)
; 允许网络访问(白名单)
(allow network-outbound
(remote tcp "github.com" 443)
(remote tcp "npmjs.org" 443)
)
; 工作区读写
(allow file-read* (subpath "/Users/.../workspace"))
(allow file-write* (subpath "/Users/.../workspace"))
; 禁止敏感目录
(deny file-read* (subpath "~/.ssh"))
(deny file-read* (subpath "~/.aws"))
```
**开箱即用**: ⭐⭐⭐⭐⭐
---
### 3.2 Linux: bubblewrap
**技术**: bubblewrap (bwrap)
**优势**:
- ✅ 主流发行版可用
- ✅ 轻量级(~5% 开销)
- ✅ 命名空间隔离
**依赖安装**:
```bash
# Debian/Ubuntu
sudo apt install bubblewrap socat ripgrep
# Fedora
sudo dnf install bubblewrap socat ripgrep
# Arch Linux
sudo pacman -S bubblewrap socat ripgrep
```
**执行示例**:
```bash
bwrap \
--ro-bind /usr /usr \
--bind /workspace /workspace \
--unshare-all \
--die-with-parent \
bash -c "command"
```
**开箱即用**: ⭐⭐⭐⭐
---
### 3.3 Windows: Codex Sandbox
**技术**: Rust + Windows API
**来源**: OpenAI Codex (Apache-2.0)
**优势**:
- ✅ 完整复用 Codex 实现
- ✅ 预编译二进制
- ✅ Windows 原生 API
**实现特性**:
- Job Objects (进程管理)
- Restricted Tokens (权限限制)
- Network Proxy (网络过滤)
- Seccomp-like (系统调用过滤)
**开箱即用**: ⭐⭐⭐⭐⭐ (预编译提供)
---
## 4. 配置系统
### 4.1 沙箱模式
#### 启用/禁用SandboxPolicy
| 模式 | 主会话 | 其他会话 | 安全性 | 便利性 | 适用场景 |
|------|--------|---------|--------|--------|---------|
| **off** | 无沙箱 | 无沙箱 | ⭐ | ⭐⭐⭐⭐⭐ | 完全信任环境 |
| **on-demand** | 询问 | 询问 | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ | 混合场景 |
| **non-main** ⭐ | 无沙箱 | 有沙箱 | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | **推荐默认** |
| **all** | 有沙箱 | 有沙箱 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | 高安全需求 |
#### 严格度模式SandboxMode
> v0.10+ 新增,跨平台统一接口,各平台实现语义有差异。
| Mode | 说明 | 默认 |
|------|------|------|
| **compat** | 兼容优先,平衡安全与可用性 | ✅ 默认 |
| **strict** | 最小权限,最大限制 | |
| **permissive** | 宽松模式,仅排障用途 | |
**各平台实现差异**
| 平台 | strict | compat | permissive |
|------|--------|--------|-----------|
| Linux (bwrap) | 最小 ro-bind`/usr` `/bin` `/sbin` `/lib` `/lib64` `/etc` `/opt` `/usr/local` | 全局 ro-bind `--ro-bind / /` | 完整 rw bind无 namespace 隔离 |
| macOS (seatbelt) | exec allowlist 仅命令本身(不含 `startupExecAllowlist` | exec allowlist 含启动链 | 全局 file-write + unrestricted process-exec |
| Windows (helper `run`) | `writable_roots` 仅首个路径workspace-first+ WRITE_RESTRICTEDAPPDATA 不在 ALLOW ACEs | `writable_roots` 全部 + WRITE_RESTRICTEDAPPDATA 在 ALLOW ACEs | `writable_roots` 全部 + `--no-write-restricted` |
| Windows (helper `serve`) | `--write-restricted`,仅 workspace + TEMP/TMP | `--write-restricted`workspace + TEMP/TMP + APPDATA | 无 WRITE_RESTRICTED进程级不限制写入 |
> **v1.1.0 变更**: Windows `serve` 子命令新增 `--write-restricted` flag。
> Policy JSON 新增 `sandbox_mode` 字段Rust helper 的 `compute_allow_paths()` 根据此字段
> 决定是否将 APPDATA/LOCALAPPDATA 加入 ALLOW ACEs。
> strict 模式下 APPDATA 不可写进程级真正限制compat 模式下可写。
> **v1.1.1 同步**:
> 1) macOS strict 的 seatbelt exec allowlist 不再纳入 startup chain仅命令本身
> 2) Windows helper `run` strict 下 `writable_roots` 仅保留首个路径workspace-first
> 3) 平台分支入口统一收敛到 `PlatformAdapter`。
> `qimingcode` 在 strict 模式下还包含 ACP 权限层的二次写入门控(`strictPermissionGuard`
> 写入路径必须位于 `workspace/temp/isolatedHome`strict 下不含 `appData`
> 路径缺失时 fail-closed写入权限仅 `allow_once`。
>
> `qimingcode` warmup 复用同样受 sandbox policy 约束warmup 会记录 policy 指纹,
> 当用户修改 sandbox mode/policy 后,若指纹不一致则立即放弃复用并冷启动新引擎。
### 4.2 配置结构
```typescript
interface SandboxConfig {
// 沙箱类型
type: SandboxType;
// 运行平台
platform: Platform;
// 是否启用
enabled: boolean;
// 工作区根目录
workspaceRoot: string;
// 沙箱严格度模式v0.10+
mode?: "strict" | "compat" | "permissive";
// 网络策略
networkEnabled?: boolean;
// 资源限制
memoryLimit?: string;
cpuLimit?: number;
diskQuota?: string;
}
```
### 4.3 默认配置
```typescript
const DEFAULT_SANDBOX_CONFIG: SandboxConfig = {
mode: "non-main",
platform: {
darwin: { enabled: true, type: "seatbelt" },
linux: { enabled: true, type: "bubblewrap" },
win32: { enabled: true, type: "codex" },
},
network: {
enabled: true,
allowedDomains: [
"github.com",
"*.github.com",
"npmjs.org",
"registry.npmjs.org",
"pypi.org",
],
deniedDomains: [],
},
filesystem: {
allowRead: ["."],
denyRead: ["~/.ssh", "~/.aws", "~/.gnupg"],
allowWrite: [".", "/tmp"],
denyWrite: [".env", "*.pem", "*.key"],
},
resources: {
memory: "2g",
cpu: 2,
timeout: 300,
},
preferences: {
showNotifications: true,
askForDangerousOps: true,
auditLogging: true,
},
};
```
---
## 5. 统一 API
### 5.1 SandboxInterface
```typescript
interface SandboxInterface {
// 初始化沙箱
initialize(config: SandboxConfig): Promise<void>;
// 执行命令
execute(
command: string,
cwd: string,
options?: ExecuteOptions
): Promise<ExecuteResult>;
// 文件操作
readFile(sessionId: string, path: string): Promise<string>;
writeFile(sessionId: string, path: string, content: string): Promise<void>;
// 状态查询
isAvailable(): Promise<boolean>;
getStatus(): SandboxStatus;
// 生命周期
cleanup(): Promise<void>;
}
interface ExecuteOptions {
timeout?: number;
signal?: AbortSignal;
onOutput?: (data: string) => void;
}
interface ExecuteResult {
exitCode: number;
stdout: string;
stderr: string;
}
interface SandboxStatus {
available: boolean;
type: "seatbelt" | "bubblewrap" | "codex" | "none";
platform: string;
version?: string;
}
```
### 5.2 自动选择实现
```typescript
class AutoSandbox implements SandboxInterface {
private sandbox: SandboxInterface;
async initialize(config: SandboxConfig): Promise<void> {
const platform = os.platform();
switch (platform) {
case "darwin":
this.sandbox = new MacSandbox();
break;
case "linux":
this.sandbox = new LinuxSandbox();
break;
case "win32":
this.sandbox = new WindowsSandbox();
break;
default:
this.sandbox = new NoneSandbox();
}
await this.sandbox.initialize(config);
}
// 代理所有接口方法...
}
```
---
## 6. 构建和发布
### 6.1 构建流程
```bash
# 1. 构建 Rust 沙箱(所有平台)
cd crates/qiming-sandbox
make build
# 或构建特定平台
make build-darwin
make build-linux
make build-windows
# 2. 复制到预编译目录
make prebuilt
# 3. 准备 Electron 资源
cd ../agent-electron-client
npm run prepare:sandbox
```
### 6.2 Makefile
```makefile
# crates/qiming-sandbox/Makefile
.PHONY: build clean prebuilt
build:
cargo build --release
cd bindings && npm run build
build-darwin:
cargo build --release --target x86_64-apple-darwin
cd bindings && npm run build -- --target x86_64-apple-darwin
build-linux:
cargo build --release --target x86_64-unknown-linux-gnu
cd bindings && npm run build -- --target x86_64-unknown-linux-gnu
build-windows:
cargo build --release --target x86_64-pc-windows-msvc
cd bindings && npm run build -- --target x86_64-pc-windows-msvc
prebuilt: build
mkdir -p prebuilt/darwin-x64
mkdir -p prebuilt/linux-x64
mkdir -p prebuilt/win32-x64
cp target/release/qiming-sandbox prebuilt/darwin-x64/ || true
cp target/release/qiming-sandbox prebuilt/linux-x64/ || true
cp target/x86_64-pc-windows-msvc/release/qiming-sandbox.exe prebuilt/win32-x64/ || true
cp bindings/native/*.node prebuilt/darwin-x64/ || true
cp bindings/native/*.node prebuilt/linux-x64/ || true
cp bindings/native/*.node prebuilt/win32-x64/ || true
clean:
cargo clean
rm -rf bindings/native/*.node
rm -rf prebuilt/*
```
### 6.3 prepare-sandbox.ts
```typescript
// scripts/prepare-sandbox.ts
import * as fs from "fs";
import * as path from "path";
const SANDBOX_SRC = path.join(__dirname, "..", "..", "qiming-sandbox", "prebuilt");
const SANDBOX_DEST = path.join(__dirname, "..", "resources", "sandbox");
const PLATFORM = process.platform;
const ARCH = process.arch;
function prepareSandbox(): void {
const sourceDir = path.join(SANDBOX_SRC, `${PLATFORM}-${ARCH}`);
const targetDir = path.join(SANDBOX_DEST, `${PLATFORM}-${ARCH}`);
fs.mkdirSync(targetDir, { recursive: true });
if (fs.existsSync(sourceDir)) {
const files = fs.readdirSync(sourceDir);
files.forEach(file => {
fs.copyFileSync(
path.join(sourceDir, file),
path.join(targetDir, file)
);
});
console.log(`✅ Sandbox binaries copied for ${PLATFORM}-${ARCH}`);
} else {
console.log(` No sandbox binaries needed for ${PLATFORM}-${ARCH}`);
fs.writeFileSync(path.join(targetDir, ".gitkeep"), "");
}
}
prepareSandbox();
```
---
## 7. 许可证
### 7.1 许可证兼容性
| 项目 | 许可证 | 兼容性 |
|------|--------|--------|
| **Codex** | Apache-2.0 | ✅ |
| **QimingClaw** | Apache-2.0 / MIT | ✅ |
| **qiming-sandbox** | Apache-2.0 | ✅ |
### 7.2 版权声明
```
Copyright 2024-2026 QimingClaw Team
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
---
This project includes code from OpenAI Codex:
https://github.com/openai/codex
Original copyright notice:
Copyright 2024 OpenAI
Licensed under the Apache License, Version 2.0
```
---
## 8. 致谢
本沙箱方案基于以下开源项目:
- **OpenAI Codex** - Windows Sandbox 实现
- **Anthropic** - @anthropic-ai/sandbox-runtime
- **LobsterAI** - QEMU 虚拟机沙箱参考
- **pi-mono** - 配置系统参考
---
## 9. 参考
- [OpenAI Codex](https://github.com/openai/codex)
- [Anthropic Sandbox Runtime](https://www.npmjs.com/package/@anthropic-ai/sandbox-runtime)
- [LobsterAI 本地源码](/Users/apple/workspace/LobsterAI)
- [pi-mono 本地源码](/Users/apple/workspace/pi-mono)
- [macOS Seatbelt](https://developer.apple.com/documentation/security/app_sandbox)
- [bubblewrap](https://github.com/containers/bubblewrap)
---
**文档版本**: 1.0.0
**最后更新**: 2026-03-27

View File

@@ -0,0 +1,743 @@
# QimingClaw Sandbox 实施指南
> **版本**: 1.0.0
> **更新日期**: 2026-03-27
---
## 1. 实施概述
### 1.1 实施阶段
| 阶段 | 内容 | 预计时间 | 状态 |
|------|------|---------|------|
| **Phase 1** | 创建 qiming-sandbox 子模块 | 1 小时 | 待开始 |
| **Phase 2** | 实现 macOS Sandbox | 1 小时 | 待开始 |
| **Phase 3** | 实现 Linux Sandbox | 1-2 小时 | 待开始 |
| **Phase 4** | 实现 Windows Sandbox (Codex) | 2-3 小时 | 待开始 |
| **Phase 5** | 配置系统 + UI | 2 小时 | 待开始 |
| **Phase 6** | 测试 + 文档 | 2 小时 | 待开始 |
| **总计** | - | **9-11 小时** | - |
---
## 2. Phase 1: 创建子模块
### 2.1 创建目录结构
```bash
cd /Users/apple/workspace/qiming-agent
# 创建源码目录
mkdir -p crates/qiming-sandbox/crates
mkdir -p crates/qiming-sandbox/bindings/native
mkdir -p crates/qiming-sandbox/prebuilt
# 创建运行时目录
mkdir -p crates/agent-electron-client/resources/sandbox/darwin-x64
mkdir -p crates/agent-electron-client/resources/sandbox/linux-x64
mkdir -p crates/agent-electron-client/resources/sandbox/win32-x64
# 创建 .gitkeep
touch crates/agent-electron-client/resources/sandbox/darwin-x64/.gitkeep
touch crates/agent-electron-client/resources/sandbox/linux-x64/.gitkeep
touch crates/agent-electron-client/resources/sandbox/win32-x64/.gitkeep
```
### 2.2 初始化 Cargo Workspace
```bash
cd crates/qiming-sandbox
# 初始化 Cargo.toml
cat > Cargo.toml << 'EOF'
[workspace]
members = [
"crates/windows-sandbox",
"crates/macos-sandbox",
"crates/linux-sandbox",
"bindings/native",
]
resolver = "2"
[workspace.package]
version = "0.1.0"
edition = "2021"
license = "Apache-2.0"
repository = "https://github.com/qiming-ai/qimingclaw"
authors = ["QimingClaw Team"]
description = "QimingClaw Agent Sandbox - based on OpenAI Codex"
[workspace.dependencies]
napi = "2"
napi-derive = "2"
tokio = { version = "1", features = ["full"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
[target.'cfg(windows)'.workspace.dependencies]
winapi = "0.3"
EOF
```
### 2.3 创建 README 和 LICENSE
```bash
# README.md
cat > README.md << 'EOF'
# QimingClaw Sandbox
基于 OpenAI Codex 沙箱方案的安全执行环境。
## 许可证
Apache-2.0 (继承自 OpenAI Codex)
## 平台支持
| 平台 | 实现方式 | 状态 |
|------|---------|------|
| **macOS** | sandbox-exec ||
| **Linux** | bubblewrap ||
| **Windows** | Codex Sandbox ||
## 构建
```bash
make build # 构建所有平台
make build-darwin # 构建 macOS
make build-linux # 构建 Linux
make build-windows # 构建 Windows
```
## 致谢
本项目基于 [OpenAI Codex](https://github.com/openai/codex) 的沙箱实现。
EOF
# LICENSE
cat > LICENSE << 'EOF'
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
Copyright 2024-2026 QimingClaw Team
Copyright 2024 OpenAI
Licensed under the Apache License, Version 2.0 (the "License");
...
EOF
```
---
## 3. Phase 2: macOS Sandbox
### 3.1 创建 macOS 模块
```bash
mkdir -p crates/qiming-sandbox/crates/macos-sandbox/src
cat > crates/macos-sandbox/Cargo.toml << 'EOF'
[package]
name = "qiming-macos-sandbox"
version.workspace = true
edition.workspace = true
license.workspace = true
[dependencies]
tokio = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
EOF
cat > crates/macos-sandbox/src/lib.rs << 'EOF'
use std::process::Command;
use std::fs;
pub struct MacSandbox {
profile_path: String,
}
impl MacSandbox {
pub fn new() -> Self {
Self {
profile_path: String::new(),
}
}
pub async fn execute(&self, command: &str, cwd: &str, config: &SandboxConfig) -> Result<ExecuteResult, SandboxError> {
// 生成 sandbox-exec 配置文件
let profile = self.generate_profile(cwd, config);
let profile_path = format!("/tmp/sandbox-{}.sb", chrono::Utc::now().timestamp());
fs::write(&profile_path, &profile)?;
// 执行命令
let output = Command::new("sandbox-exec")
.arg("-f")
.arg(&profile_path)
.arg("bash")
.arg("-c")
.arg(command)
.current_dir(cwd)
.output()?;
// 清理配置文件
fs::remove_file(&profile_path)?;
Ok(ExecuteResult {
exit_code: output.status.code().unwrap_or(-1),
stdout: String::from_utf8_lossy(&output.stdout).to_string(),
stderr: String::from_utf8_lossy(&output.stderr).to_string(),
})
}
fn generate_profile(&self, cwd: &str, config: &SandboxConfig) -> String {
let network_rules = if config.network_enabled {
self.generate_network_rules(&config.allowed_domains, &config.denied_domains)
} else {
"(deny network*)".to_string()
};
format!(r#"
(version 1)
(allow default)
; 系统库读取
(allow file-read* (subpath "/usr") (subpath "/System"))
; 网络规则
{}
; 工作区访问
(allow file-read* (subpath "{}"))
(allow file-write* (subpath "{}"))
; 禁止敏感目录
(deny file-read* (subpath "~/.ssh"))
(deny file-read* (subpath "~/.aws"))
(deny file-read* (subpath "~/.gnupg"))
"#, network_rules, cwd, cwd)
}
fn generate_network_rules(&self, allowed: &[String], denied: &[String]) -> String {
let mut rules = String::from("(allow network-outbound\n");
for domain in allowed {
rules.push_str(&format!(" (remote tcp \"{}\" 443)\n", domain));
}
rules.push_str(")\n");
for domain in denied {
rules.push_str(&format!("(deny network-outbound (remote tcp \"{}\"))\n", domain));
}
rules
}
}
EOF
```
---
## 4. Phase 3: Linux Sandbox
### 4.1 创建 Linux 模块
```bash
mkdir -p crates/qiming-sandbox/crates/linux-sandbox/src
cat > crates/linux-sandbox/Cargo.toml << 'EOF'
[package]
name = "qiming-linux-sandbox"
version.workspace = true
edition.workspace = true
license.workspace = true
[dependencies]
tokio = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
which = "5"
EOF
cat > crates/linux-sandbox/src/lib.rs << 'EOF'
use std::process::Command;
use which::which;
pub struct LinuxSandbox {
use_bubblewrap: bool,
}
impl LinuxSandbox {
pub fn new() -> Self {
let use_bubblewrap = which("bwrap").is_ok();
Self { use_bubblewrap }
}
pub async fn execute(&self, command: &str, cwd: &str, config: &SandboxConfig) -> Result<ExecuteResult, SandboxError> {
if self.use_bubblewrap {
self.execute_in_bubblewrap(command, cwd, config).await
} else {
self.execute_unsandboxed(command, cwd, config).await
}
}
async fn execute_in_bubblewrap(&self, command: &str, cwd: &str, config: &SandboxConfig) -> Result<ExecuteResult, SandboxError> {
let mut args = vec![
"--ro-bind".to_string(), "/usr".to_string(), "/usr".to_string(),
"--ro-bind".to_string(), "/lib".to_string(), "/lib".to_string(),
"--ro-bind".to_string(), "/bin".to_string(), "/bin".to_string(),
"--bind".to_string(), cwd.to_string(), cwd.to_string(),
"--dev".to_string(), "/dev".to_string(),
"--proc".to_string(), "/proc".to_string(),
"--unshare-all".to_string(),
"--die-with-parent".to_string(),
];
if config.network_enabled {
args.push("--share-net".to_string());
}
args.push("bash".to_string());
args.push("-c".to_string());
args.push(command.to_string());
let output = Command::new("bwrap")
.args(&args)
.current_dir(cwd)
.output()?;
Ok(ExecuteResult {
exit_code: output.status.code().unwrap_or(-1),
stdout: String::from_utf8_lossy(&output.stdout).to_string(),
stderr: String::from_utf8_lossy(&output.stderr).to_string(),
})
}
async fn execute_unsandboxed(&self, command: &str, cwd: &str, config: &SandboxConfig) -> Result<ExecuteResult, SandboxError> {
let output = Command::new("bash")
.arg("-c")
.arg(command)
.current_dir(cwd)
.output()?;
Ok(ExecuteResult {
exit_code: output.status.code().unwrap_or(-1),
stdout: String::from_utf8_lossy(&output.stdout).to_string(),
stderr: String::from_utf8_lossy(&output.stderr).to_string(),
})
}
}
EOF
```
---
## 5. Phase 4: Windows Sandbox (Codex)
### 5.1 克隆 Codex
```bash
# 克隆 Codex 仓库
cd /tmp
git clone https://github.com/openai/codex
cd codex/codex-rs
```
### 5.2 复制 Windows Sandbox
```bash
# 复制到 qiming-sandbox
cp -r windows-sandbox-rs /Users/apple/workspace/qiming-agent/crates/qiming-sandbox/crates/windows-sandbox
```
### 5.3 修改 Cargo.toml
```bash
cd /Users/apple/workspace/qiming-agent/crates/qiming-sandbox/crates/windows-sandbox
# 修改 Cargo.toml 以符合 workspace
cat > Cargo.toml << 'EOF'
[package]
name = "qiming-windows-sandbox"
version.workspace = true
edition.workspace = true
license.workspace = true
description = "Windows sandbox for QimingClaw Agent (based on Codex)"
[dependencies]
tokio = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
[target.'cfg(windows)'.dependencies]
winapi = { workspace = true, features = ["winbase", "processthreadsapi", "jobapi2"] }
[build-dependencies]
# 保持 Codex 的原始构建依赖
EOF
```
### 5.4 编译测试
```bash
# 编译 Windows 版本(需要 Windows 交叉编译环境)
cargo build --release --target x86_64-pc-windows-msvc
```
---
## 6. Phase 5: Node.js Bindings
### 6.1 初始化 npm 包
```bash
cd crates/qiming-sandbox/bindings
cat > package.json << 'EOF'
{
"name": "@qiming/sandbox-native",
"version": "0.1.0",
"main": "index.js",
"types": "index.d.ts",
"napi": {
"name": "sandbox",
"triples": {
"defaults": false,
"additional": [
"x86_64-apple-darwin",
"x86_64-unknown-linux-gnu",
"x86_64-pc-windows-msvc"
]
}
},
"scripts": {
"build": "napi build --platform --release",
"build:debug": "napi build --platform"
},
"devDependencies": {
"@napi-rs/cli": "^2.0.0"
}
}
EOF
npm install
```
### 6.2 创建 Rust Binding
```bash
mkdir -p native/src
cat > native/Cargo.toml << 'EOF'
[package]
name = "qiming-sandbox-native"
version.workspace = true
edition.workspace = true
[lib]
crate-type = ["cdylib"]
[dependencies]
napi = { workspace = true }
napi-derive = { workspace = true }
[features]
default = []
[build-dependencies]
napi-build = "2"
EOF
cat > native/src/lib.rs << 'EOF'
use napi::bindgen_prelude::*;
#[macro_use]
extern crate napi_derive;
#[napi]
pub struct Sandbox;
#[napi]
impl Sandbox {
#[napi(constructor)]
pub fn new() -> Self {
Self
}
#[napi]
pub async fn execute(
&self,
command: String,
cwd: String,
config: SandboxConfig,
) -> Result<ExecuteResult> {
// 根据平台调用对应实现
#[cfg(target_os = "macos")]
{
// 调用 macOS 实现
}
#[cfg(target_os = "linux")]
{
// 调用 Linux 实现
}
#[cfg(target_os = "windows")]
{
// 调用 Windows 实现
}
Ok(ExecuteResult {
exit_code: 0,
stdout: String::new(),
stderr: String::new(),
})
}
}
#[napi(object)]
pub struct SandboxConfig {
pub network_enabled: Option<bool>,
pub memory_limit: Option<String>,
pub cpu_limit: Option<i32>,
pub timeout: Option<i32>,
}
#[napi(object)]
pub struct ExecuteResult {
pub exit_code: i32,
pub stdout: String,
pub stderr: String,
}
EOF
```
### 6.3 TypeScript 封装
```bash
cat > index.ts << 'EOF'
import { Sandbox as NativeSandbox, SandboxConfig, ExecuteResult } from './native';
export class QimingSandbox {
private sandbox: NativeSandbox;
constructor() {
this.sandbox = new NativeSandbox();
}
async execute(
command: string,
cwd: string,
config?: Partial<SandboxConfig>
): Promise<ExecuteResult> {
const fullConfig: SandboxConfig = {
networkEnabled: config?.networkEnabled ?? false,
memoryLimit: config?.memoryLimit ?? '2g',
cpuLimit: config?.cpuLimit ?? 2,
timeout: config?.timeout ?? 300,
};
return this.sandbox.execute(command, cwd, fullConfig);
}
isAvailable(): boolean {
return true;
}
}
export type { SandboxConfig, ExecuteResult };
EOF
```
---
## 7. Phase 6: 配置和构建
### 7.1 创建 Makefile
```bash
cd crates/qiming-sandbox
cat > Makefile << 'EOF'
.PHONY: build clean prebuilt
build:
cargo build --release
cd bindings && npm run build
build-darwin:
cargo build --release --target x86_64-apple-darwin
cd bindings && npm run build -- --target x86_64-apple-darwin
build-linux:
cargo build --release --target x86_64-unknown-linux-gnu
cd bindings && npm run build -- --target x86_64-unknown-linux-gnu
build-windows:
cargo build --release --target x86_64-pc-windows-msvc
cd bindings && npm run build -- --target x86_64-pc-windows-msvc
prebuilt: build
mkdir -p prebuilt/darwin-x64
mkdir -p prebuilt/linux-x64
mkdir -p prebuilt/win32-x64
cp bindings/native/*.node prebuilt/darwin-x64/ || true
cp bindings/native/*.node prebuilt/linux-x64/ || true
cp bindings/native/*.node prebuilt/win32-x64/ || true
cp target/x86_64-pc-windows-msvc/release/qiming-sandbox.exe prebuilt/win32-x64/ || true
clean:
cargo clean
rm -rf bindings/native/*.node
rm -rf prebuilt/*
EOF
```
### 7.2 创建 prepare-sandbox.ts
```bash
cd crates/agent-electron-client/scripts
cat > prepare-sandbox.ts << 'EOF'
import * as fs from "fs";
import * as path from "path";
const SANDBOX_SRC = path.join(__dirname, "..", "..", "qiming-sandbox", "prebuilt");
const SANDBOX_DEST = path.join(__dirname, "..", "resources", "sandbox");
const PLATFORM = process.platform;
const ARCH = process.arch;
function prepareSandbox(): void {
const sourceDir = path.join(SANDBOX_SRC, `${PLATFORM}-${ARCH}`);
const targetDir = path.join(SANDBOX_DEST, `${PLATFORM}-${ARCH}`);
fs.mkdirSync(targetDir, { recursive: true });
if (fs.existsSync(sourceDir)) {
const files = fs.readdirSync(sourceDir);
files.forEach(file => {
fs.copyFileSync(
path.join(sourceDir, file),
path.join(targetDir, file)
);
});
console.log(`✅ Sandbox binaries copied for ${PLATFORM}-${ARCH}`);
} else {
console.log(` No sandbox binaries needed for ${PLATFORM}-${ARCH}`);
fs.writeFileSync(path.join(targetDir, ".gitkeep"), "");
}
}
prepareSandbox();
EOF
```
### 7.3 更新 package.json
```json
{
"scripts": {
"prepare:sandbox": "ts-node scripts/prepare-sandbox.ts",
"postinstall": "npm run prepare:sandbox"
}
}
```
---
## 8. 测试
### 8.1 单元测试
```bash
cd crates/qiming-sandbox
# 运行 Rust 测试
cargo test
# 运行 Node.js 测试
cd bindings
npm test
```
### 8.2 集成测试
```bash
# macOS 测试
make build-darwin
node test/sandbox.test.js
# Linux 测试 (需要 Linux 环境)
make build-linux
node test/sandbox.test.js
# Windows 测试 (需要 Windows 环境)
make build-windows
node test/sandbox.test.js
```
---
## 9. 发布
### 9.1 提交代码
```bash
cd /Users/apple/workspace/qiming-agent
# 添加新文件
git add crates/qiming-sandbox/
git add crates/agent-electron-client/scripts/prepare-sandbox.ts
git add crates/agent-electron-client/docs/sandbox/
# 提交
git commit -m "feat(sandbox): add qiming-sandbox module based on Codex"
```
### 9.2 打包发布
```bash
# 构建 Electron 应用
cd crates/agent-electron-client
npm run build
npm run package
# 测试安装包
# macOS: open release/QimingClaw-0.9.3.dmg
# Windows: release/QimingClaw-0.9.3.exe
# Linux: release/QimingClaw-0.9.3.AppImage
```
---
## 10. 常见问题
### Q1: macOS 提示 sandbox-exec 权限错误?
A: 检查应用是否正确签名sandbox-exec 需要有效的开发者签名。
### Q2: Linux 提示 bwrap 未找到?
A: 安装 bubblewrap:
```bash
sudo apt install bubblewrap socat ripgrep
```
### Q3: Windows 编译失败?
A: 确保安装了:
- Visual Studio Build Tools
- Windows SDK
- Rust target: `rustup target add x86_64-pc-windows-msvc`
---
**文档版本**: 1.0.0
**最后更新**: 2026-03-27

View File

@@ -0,0 +1,75 @@
# 沙箱方案文档
> 多平台 Agent 沙箱工作空间技术文档
---
## 文档索引
| 文档 | 说明 |
|------|------|
| [ARCHITECTURE.md](./ARCHITECTURE.md) | 沙箱架构设计 |
| [IMPLEMENTATION.md](./IMPLEMENTATION.md) | 沙箱实现说明 |
| [API.md](./API.md) | 通用 API 文档 |
| [SANDBOX-API.md](./SANDBOX-API.md) | 沙箱 API 接口文档 |
| [SANDBOX-COMMANDS.md](./SANDBOX-COMMANDS.md) | 沙箱命令白名单 |
| [SANDBOX-SUBMODULE-INTEGRATION.md](./SANDBOX-SUBMODULE-INTEGRATION.md) | 三端沙箱子模块接入 |
| [TROUBLESHOOTING.md](./TROUBLESHOOTING.md) | 故障排查 |
| [../operations/SANDBOX-SUBMODULE-UPDATE-RUNBOOK.md](../operations/SANDBOX-SUBMODULE-UPDATE-RUNBOOK.md) | 子模块升级与回滚 Runbook |
---
## 最近同步2026-04-10
- 已补充 `PlatformAdapter` 统一平台抽象层在 sandbox 主链路的接入说明
- 已同步 strict 语义:
- macOS seatbelt strict 不包含 startup chain exec allowlist
- Windows helper `run` strict 的 `writable_roots` 仅保留首个路径workspace-first
- 对应细节见:
- [ARCHITECTURE.md](./ARCHITECTURE.md)
- [../operations/ACP-TERMINAL-SANDBOX.md](../operations/ACP-TERMINAL-SANDBOX.md)
---
## 三区隔离模型
```
┌─────────────────────────────────────────────────────────────┐
│ 用户空间User Space
│ │
│ ┌─────────────────┐ │
│ │ 应用核心区 │ 只读、不可变 │
│ ├─────────────────┤ │
│ │ Agent 工作区 │ 可写、隔离 │
│ ├─────────────────┤ │
│ │ 用户系统区 │ 只读访问 │
│ └─────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
```
---
## 平台支持
| 平台 | 主要沙箱 | 备选 |
|------|---------|------|
| macOS | sandbox-exec | Docker |
| Windows | Windows Sandbox helper | Docker |
| Linux | bubblewrap | Docker |
---
## 阅读顺序
1. **ARCHITECTURE.md** - 了解沙箱核心架构
2. **IMPLEMENTATION.md** - 查看实现细节
3. **SANDBOX-API.md** - 参考 API 接口
---
## 相关文档
- [../architecture/ISOLATION.md](../architecture/ISOLATION.md) - 三区隔离模型
- [../v2/01-ARCHITECTURE.md](../v2/01-ARCHITECTURE.md) - 应用架构
- [../AGENTS.md](../AGENTS.md) - Agent 开发指南

View File

@@ -0,0 +1,627 @@
# 沙箱 API 接口文档
> **版本**: 1.0.0
> **更新**: 2026-03-22
> **状态**: 待实现
---
## 1. 概览
本文档定义 Qiming Agent 沙箱的 TypeScript/JavaScript API 接口。
---
## 2. 类型定义
### 2.1 核心类型
```typescript
// src/shared/types/sandbox.ts
export type Platform = 'darwin' | 'win32' | 'linux';
export type SandboxType = 'docker' | 'wsl' | 'firejail' | 'none';
export type PermissionLevel = 0 | 1 | 2 | 3;
export interface SandboxConfig {
type: SandboxType;
platform: Platform;
enabled: boolean;
workspaceRoot: string;
memoryLimit?: string; // 如 "2g"
cpuLimit?: number; // 如 2
diskQuota?: string; // 如 "10g"
networkEnabled?: boolean;
mode?: SandboxMode; // "strict" | "compat" | "permissive"
}
export interface Workspace {
id: string;
rootPath: string;
projectsPath: string;
nodeModulesPath: string;
pythonEnvPath: string;
binPath: string;
cachePath: string;
sandboxConfig: SandboxConfig;
createdAt: Date;
lastAccessedAt: Date;
retentionPolicy: RetentionPolicy;
}
export interface ExecuteOptions {
cwd?: string;
env?: Record<string, string>;
timeout?: number;
maxMemory?: string;
stdio?: 'pipe' | 'inherit';
permissionLevel?: PermissionLevel;
}
export interface ExecuteResult {
stdout: string;
stderr: string;
exitCode: number;
timedOut: boolean;
duration: number;
}
export interface RetentionPolicy {
mode: 'always' | 'timeout' | 'manual';
maxAge?: number;
maxWorkspaces?: number;
maxSize?: string;
preserveOnError?: boolean;
}
export interface Permission {
type: PermissionType;
target: string;
sessionId: string;
approvedBy: 'system' | 'user' | 'policy' | 'denied';
timestamp: Date;
reason?: string;
}
export type PermissionType =
| 'file:read'
| 'file:write'
| 'file:delete'
| 'command:execute'
| 'network:access'
| 'network:download'
| 'package:install:npm'
| 'package:install:python'
| 'package:install:system';
```
---
## 3. SandboxManager 接口
### 3.1 类签名
```typescript
// src/main/services/sandbox/SandboxManager.ts
export abstract class SandboxManager {
protected config: SandboxConfig;
protected workspaces: Map<string, Workspace>;
constructor(config: SandboxConfig);
// 初始化
abstract init(): Promise<void>;
// 检查沙箱是否可用
abstract isAvailable(): Promise<boolean>;
// 创建工作区
abstract createWorkspace(sessionId: string): Promise<Workspace>;
// 销毁工作区
abstract destroyWorkspace(sessionId: string): Promise<void>;
// 执行命令
abstract execute(
sessionId: string,
command: string,
args: string[],
options?: ExecuteOptions
): Promise<ExecuteResult>;
// 文件操作
abstract readFile(sessionId: string, path: string): Promise<string>;
abstract writeFile(sessionId: string, path: string, content: string): Promise<void>;
abstract readDir(sessionId: string, path: string): Promise<FileInfo[]>;
// 工作区信息
abstract getWorkspace(sessionId: string): Workspace | undefined;
abstract listWorkspaces(): Workspace[];
// 清理
abstract cleanup(): Promise<void>;
}
```
### 3.2 Docker 实现
```typescript
// src/main/services/sandbox/DockerSandbox.ts
export class DockerSandbox extends SandboxManager {
private containerIds: Map<string, string>;
constructor(config: SandboxConfig & {
dockerImage: string;
dockerHost?: string;
});
async init(): Promise<void>;
async isAvailable(): Promise<boolean>;
async createWorkspace(sessionId: string): Promise<Workspace>;
async destroyWorkspace(sessionId: string): Promise<void>;
async execute(
sessionId: string,
command: string,
args: string[],
options?: ExecuteOptions
): Promise<ExecuteResult>;
async readFile(sessionId: string, path: string): Promise<string>;
async writeFile(sessionId: string, path: string, content: string): Promise<void>;
// Docker 特有
async listContainers(): Promise<ContainerInfo[]>;
async getContainerLogs(sessionId: string): Promise<string>;
}
```
### 3.3 WSL 实现Windows
```typescript
// src/main/services/sandbox/WslSandbox.ts
export class WslSandbox extends SandboxManager {
private distribution: string;
private instanceId: string;
constructor(config: SandboxConfig & {
distribution: string;
});
async init(): Promise<void>;
async isAvailable(): Promise<boolean>;
async createWorkspace(sessionId: string): Promise<Workspace>;
async destroyWorkspace(sessionId: string): Promise<void>;
async execute(
sessionId: string,
command: string,
args: string[],
options?: ExecuteOptions
): Promise<ExecuteResult>;
}
```
### 3.4 Firejail 实现Linux
```typescript
// src/main/services/sandbox/FirejailSandbox.ts
export class FirejailSandbox extends SandboxManager {
private profiles: Map<string, string>;
constructor(config: SandboxConfig & {
profileDir?: string;
});
async init(): Promise<void>;
async isAvailable(): Promise<boolean>;
async createWorkspace(sessionId: string): Promise<Workspace>;
async destroyWorkspace(sessionId: string): Promise<void>;
async execute(
sessionId: string,
command: string,
args: string[],
options?: ExecuteOptions
): Promise<ExecuteResult>;
}
```
---
## 4. PermissionManager 接口
### 4.1 类签名
```typescript
// src/main/services/sandbox/PermissionManager.ts
export class PermissionManager {
private policy: PermissionPolicy;
private pendingRequests: Map<string, PermissionRequest>;
private approvedCache: Map<string, Permission>;
constructor(policy: PermissionPolicy);
// 检查权限
async checkPermission(
sessionId: string,
permission: PermissionType,
target: string
): Promise<PermissionResult>;
// 请求权限
async requestPermission(
sessionId: string,
permission: PermissionType,
target: string,
reason?: string
): Promise<Permission>;
// 批准权限
async approve(
requestId: string,
approvedBy: 'user' | 'system',
reason?: string
): Promise<void>;
// 拒绝权限
async deny(requestId: string, reason?: string): Promise<void>;
// 批量批准
async approveBatch(requestIds: string[]): Promise<void>;
// 获取待审批列表
getPendingRequests(sessionId?: string): PermissionRequest[];
// 清除缓存
clearCache(): void;
}
export interface PermissionPolicy {
autoApprove: PermissionType[]; // 自动批准的类型
requireConfirm: PermissionType[]; // 需要确认的类型
denyList: PermissionType[]; // 禁止的类型
workspaceOnly: boolean; // 是否只允许工作区操作
safeCommands: string[]; // 安全命令白名单
}
export interface PermissionResult {
allowed: boolean;
reason: string;
requestId?: string;
}
export interface PermissionRequest {
id: string;
sessionId: string;
type: PermissionType;
target: string;
reason?: string;
requestedAt: Date;
status: 'pending' | 'approved' | 'denied';
}
```
---
## 5. WorkspaceManager 接口
### 5.1 类签名
```typescript
// src/main/services/sandbox/WorkspaceManager.ts
export class WorkspaceManager {
private sandboxManager: SandboxManager;
private permissionManager: PermissionManager;
private workspaceStore: WorkspaceStore;
constructor(
sandboxManager: SandboxManager,
permissionManager: PermissionManager
);
// 工作区生命周期
async create(sessionId: string, options?: CreateWorkspaceOptions): Promise<Workspace>;
async destroy(sessionId: string, force?: boolean): Promise<void>;
async get(sessionId: string): Promise<Workspace | undefined>;
async list(): Promise<Workspace[]>;
// 工作区操作(带权限检查)
async readFile(sessionId: string, path: string): Promise<string>;
async writeFile(sessionId: string, path: string, content: string): Promise<void>;
async execute(
sessionId: string,
command: string,
args: string[],
options?: ExecuteOptions
): Promise<ExecuteResult>;
// 清理
async cleanupExpired(): Promise<CleanupResult>;
async cleanupAll(): Promise<void>;
// 保留策略
async setRetentionPolicy(sessionId: string, policy: RetentionPolicy): Promise<void>;
async getRetentionPolicy(sessionId: string): Promise<RetentionPolicy>;
}
export interface CreateWorkspaceOptions {
retention?: Partial<RetentionPolicy>;
platform?: Platform;
sandboxType?: SandboxType;
}
export interface CleanupResult {
deletedCount: number;
freedSpace: number;
errors: string[];
}
```
---
## 6. IPC 接口
### 6.1 IPC 通道定义
```typescript
// src/main/ipc/channels.ts
export const SANDBOX_CHANNELS = {
// 工作区管理
'sandbox:create': {
request: { sessionId: string },
response: Workspace
},
'sandbox:destroy': {
request: { sessionId: string, force?: boolean },
response: { success: boolean }
},
'sandbox:list': {
request: void,
response: Workspace[]
},
'sandbox:info': {
request: { sessionId: string },
response: Workspace | null
},
// 文件操作
'sandbox:readFile': {
request: { sessionId: string, path: string },
response: { content: string }
},
'sandbox:writeFile': {
request: { sessionId: string, path: string, content: string },
response: { success: boolean }
},
// 执行
'sandbox:execute': {
request: {
sessionId: string,
command: string,
args: string[],
options?: ExecuteOptions
},
response: ExecuteResult
},
// 权限
'sandbox:checkPermission': {
request: { sessionId: string, type: PermissionType, target: string },
response: PermissionResult
},
'sandbox:requestPermission': {
request: { sessionId: string, type: PermissionType, target: string, reason?: string },
response: Permission
},
'sandbox:getPendingPermissions': {
request: { sessionId?: string },
response: PermissionRequest[]
},
'sandbox:approvePermission': {
request: { requestId: string },
response: { success: boolean }
},
'sandbox:denyPermission': {
request: { requestId: string },
response: { success: boolean }
},
// 清理
'sandbox:cleanup': {
request: void,
response: CleanupResult
},
// 状态
'sandbox:status': {
request: void,
response: SandboxStatus
},
// 策略
'sandbox:policy:get': {
request: void,
response: SandboxPolicy
},
'sandbox:policy:set': {
request: Partial<SandboxPolicy>,
response: SandboxPolicy
},
'sandbox:capabilities': {
request: void,
response: SandboxCapabilities
},
// 后端初始化Windows Codex setup
'sandbox:setup': {
request: {
windows?: { codex?: { mode?: 'unelevated' | 'elevated' } }
},
response: { success: boolean; message?: string }
}
} as const;
export interface SandboxStatus {
available: boolean;
type: SandboxType;
backend?: SandboxBackend;
platform: Platform;
activeWorkspaces: number;
degraded?: boolean;
reason?: string;
capabilities?: SandboxCapabilities;
totalMemory?: string;
diskUsage?: string;
}
export interface SandboxPolicy {
enabled: boolean;
mode: SandboxMode; // "strict" | "compat" | "permissive"
backend: SandboxBackend; // "auto" | "docker" | "macos-seatbelt" | "linux-bwrap" | "windows-sandbox"
autoFallback: SandboxAutoFallback; // "startup-only" | "session" | "manual"
windowsMode: WindowsSandboxMode; // "read-only" | "workspace-write"
}
```
---
## 7. 事件
### 7.1 事件类型
```typescript
// src/shared/events/sandbox.ts
export const SANDBOX_EVENTS = {
// 工作区事件
'workspace:created': { workspace: Workspace };
'workspace:destroyed': { workspaceId: string };
'workspace:accessed': { workspaceId: string; timestamp: Date };
// 权限事件
'permission:requested': { request: PermissionRequest };
'permission:approved': { permission: Permission };
'permission:denied': { requestId: string; reason?: string };
// 执行事件
'execute:start': { sessionId: string; command: string };
'execute:complete': { sessionId: string; result: ExecuteResult };
'execute:error': { sessionId: string; error: string };
// 清理事件
'cleanup:start': void;
'cleanup:complete': { result: CleanupResult };
// 沙箱事件
'sandbox:unavailable': { reason: string };
'sandbox:recovered': void;
} as const;
```
---
## 8. 错误类型
### 8.1 错误类
```typescript
// src/shared/errors/sandbox.ts
export class SandboxError extends Error {
constructor(
message: string,
public code: SandboxErrorCode,
public sessionId?: string
);
}
export enum SandboxErrorCode {
SANDBOX_UNAVAILABLE = 'SANDBOX_UNAVAILABLE',
WORKSPACE_NOT_FOUND = 'WORKSPACE_NOT_FOUND',
WORKSPACE_EXISTS = 'WORKSPACE_EXISTS',
PERMISSION_DENIED = 'PERMISSION_DENIED',
EXECUTION_FAILED = 'EXECUTION_FAILED',
EXECUTION_TIMEOUT = 'EXECUTION_TIMEOUT',
FILE_NOT_FOUND = 'FILE_NOT_FOUND',
FILE_WRITE_FAILED = 'FILE_WRITE_FAILED',
CLEANUP_FAILED = 'CLEANUP_FAILED',
CONFIG_INVALID = 'CONFIG_INVALID'
}
```
---
## 9. 使用示例
### 9.1 创建工作区
```typescript
import { WorkspaceManager } from './services/sandbox/WorkspaceManager';
const workspaceManager = new WorkspaceManager(
new DockerSandbox({ type: 'docker', platform: 'darwin', enabled: true }),
new PermissionManager({ /* policy */ })
);
// 创建工作区
const workspace = await workspaceManager.create('session-123', {
retention: { mode: 'timeout', maxAge: 7 * 24 * 60 * 60 * 1000 }
});
console.log(`Workspace created at: ${workspace.rootPath}`);
```
### 9.2 执行命令
```typescript
// 检查权限
const result = await workspaceManager.execute(
'session-123',
'npm',
['install', 'lodash'],
{ cwd: workspace.projectsPath }
);
console.log(`Exit code: ${result.exitCode}`);
console.log(`Output: ${result.stdout}`);
```
### 9.3 权限请求
```typescript
// 请求安装包权限
const permission = await workspaceManager.permissionManager.requestPermission(
'session-123',
'package:install:npm',
'lodash',
'Need lodash for utility functions'
);
// 等待用户批准
if (permission.approvedBy === 'user') {
// 执行安装
}
```
---
## 10. 变更记录
| 日期 | 版本 | 变更 |
|------|------|------|
| 2026-03-22 | 1.0.0 | 初始版本 |

View File

@@ -0,0 +1,551 @@
# 沙箱命令参考
> **版本**: 1.0.0
> **更新**: 2026-03-22
---
## 1. IPC 命令
### 1.1 工作区管理
#### `sandbox:create`
创建新的沙箱工作区。
**请求:**
```typescript
{
sessionId: string;
platform: 'darwin' | 'win32' | 'linux';
sandboxType: 'docker' | 'wsl' | 'firejail' | 'none';
memoryLimit?: string;
diskQuota?: string;
}
```
**响应:**
```typescript
{
id: string;
rootPath: string;
projectsPath: string;
nodeModulesPath: string;
pythonEnvPath: string;
binPath: string;
sandboxType: string;
createdAt: string;
}
```
**示例:**
```typescript
const workspace = await window.api.invoke('sandbox:create', {
sessionId: 'session-123',
platform: 'darwin',
sandboxType: 'docker',
memoryLimit: '2g',
diskQuota: '10g'
});
```
---
#### `sandbox:destroy`
销毁沙箱工作区。
**请求:**
```typescript
{
sessionId: string;
force?: boolean;
}
```
**响应:**
```typescript
{
success: boolean;
freedSpace?: string;
}
```
**示例:**
```typescript
await window.api.invoke('sandbox:destroy', {
sessionId: 'session-123',
force: true
});
```
---
#### `sandbox:list`
列出所有工作区。
**请求:** `void`
**响应:**
```typescript
Workspace[]
```
**示例:**
```typescript
const workspaces = await window.api.invoke('sandbox:list');
console.log(`Active workspaces: ${workspaces.length}`);
```
---
#### `sandbox:info`
获取工作区详细信息。
**请求:**
```typescript
{
sessionId: string;
}
```
**响应:**
```typescript
Workspace | null
```
---
### 1.2 文件操作
#### `sandbox:readFile`
在工作区内读取文件。
**请求:**
```typescript
{
sessionId: string;
path: string;
}
```
**响应:**
```typescript
{
content: string;
}
```
**示例:**
```typescript
const { content } = await window.api.invoke('sandbox:readFile', {
sessionId: 'session-123',
path: 'projects/myapp/package.json'
});
```
---
#### `sandbox:writeFile`
在工作区内写入文件。
**请求:**
```typescript
{
sessionId: string;
path: string;
content: string;
}
```
**响应:**
```typescript
{
success: boolean;
}
```
**示例:**
```typescript
await window.api.invoke('sandbox:writeFile', {
sessionId: 'session-123',
path: 'projects/myapp/index.js',
content: 'console.log("Hello");'
});
```
---
#### `sandbox:readDir`
列出目录内容。
**请求:**
```typescript
{
sessionId: string;
path: string;
}
```
**响应:**
```typescript
{
files: Array<{
name: string;
isDirectory: boolean;
size: number;
modifiedAt: string;
}>;
}
```
---
### 1.3 命令执行
#### `sandbox:execute`
在工作区中执行命令。
**请求:**
```typescript
{
sessionId: string;
command: string;
args: string[];
options?: {
cwd?: string;
env?: Record<string, string>;
timeout?: number;
stdio?: 'pipe' | 'inherit';
};
}
```
**响应:**
```typescript
{
stdout: string;
stderr: string;
exitCode: number;
timedOut: boolean;
duration: number;
}
```
**示例:**
```typescript
const result = await window.api.invoke('sandbox:execute', {
sessionId: 'session-123',
command: 'npm',
args: ['install', 'lodash'],
options: {
cwd: 'projects/myapp',
timeout: 120000
}
});
if (result.exitCode === 0) {
console.log('Install successful');
} else {
console.error('Install failed:', result.stderr);
}
```
---
### 1.4 权限管理
#### `sandbox:checkPermission`
检查权限状态。
**请求:**
```typescript
{
sessionId: string;
type: PermissionType;
target: string;
}
```
**响应:**
```typescript
{
allowed: boolean;
reason: string;
requestId?: string;
}
```
---
#### `sandbox:requestPermission`
请求权限。
**请求:**
```typescript
{
sessionId: string;
type: PermissionType;
target: string;
reason?: string;
}
```
**响应:**
```typescript
Permission
```
---
#### `sandbox:getPendingPermissions`
获取待审批的权限请求。
**请求:**
```typescript
{
sessionId?: string;
}
```
**响应:**
```typescript
PermissionRequest[]
```
---
#### `sandbox:approvePermission`
批准权限请求。
**请求:**
```typescript
{
requestId: string;
}
```
**响应:**
```typescript
{
success: boolean;
}
```
---
#### `sandbox:denyPermission`
拒绝权限请求。
**请求:**
```typescript
{
requestId: string;
reason?: string;
}
```
**响应:**
```typescript
{
success: boolean;
}
```
---
### 1.5 清理与状态
#### `sandbox:cleanup`
清理所有过期工作区。
**请求:** `void`
**响应:**
```typescript
{
deletedCount: number;
freedSpace: string;
errors: string[];
}
```
---
#### `sandbox:status`
获取沙箱状态。
**请求:** `void`
**响应:**
```typescript
{
available: boolean;
type: SandboxType;
platform: Platform;
activeWorkspaces: number;
totalMemory?: string;
diskUsage?: string;
}
```
---
## 2. 命令行工具
### 2.1 Docker 命令
```bash
# 列出容器
docker ps
# 查看容器日志
docker logs <container-id>
# 进入容器
docker exec -it <container-id> /bin/sh
# 停止容器
docker stop <container-id>
# 删除容器
docker rm <container-id>
# 清理未使用资源
docker system prune -a
```
### 2.2 WSL 命令 (Windows)
```powershell
# 列出发行版
wsl --list --verbose
# 启动发行版
wsl -d Ubuntu-22.04
# 关闭所有实例
wsl --shutdown
# 导出发行版
wsl --export Ubuntu-22.04 ubuntu.tar
```
### 2.3 Firejail 命令 (Linux)
```bash
# 使用 profile 启动
firejail --profile=/path/to/profile command
# 查看可用 profiles
ls /etc/firejail/
# 测试 profile
firejail --debug-profile=/path/to/profile command
```
---
## 3. 权限类型
| 类型 | 说明 | 示例 |
|------|------|------|
| `file:read` | 读取文件 | 读取 package.json |
| `file:write` | 写入文件 | 修改配置文件 |
| `file:delete` | 删除文件 | 删除临时文件 |
| `command:execute` | 执行命令 | 运行 npm install |
| `network:access` | 访问网络 | 调用 API |
| `network:download` | 下载资源 | 下载包 |
| `package:install:npm` | 安装 npm 包 | npm install lodash |
| `package:install:python` | 安装 Python 包 | pip install flask |
| `package:install:system` | 安装系统包 | apt install nginx |
---
## 4. 配置示例
### 4.1 创建 Docker 沙箱
```typescript
const workspace = await window.api.invoke('sandbox:create', {
sessionId: 'session-123',
platform: 'darwin',
sandboxType: 'docker',
memoryLimit: '4g',
diskQuota: '20g'
});
```
### 4.2 执行 npm install
```typescript
// 先检查权限
const perm = await window.api.invoke('sandbox:checkPermission', {
sessionId: 'session-123',
type: 'package:install:npm',
target: 'lodash'
});
if (!perm.allowed) {
// 请求权限
const request = await window.api.invoke('sandbox:requestPermission', {
sessionId: 'session-123',
type: 'package:install:npm',
target: 'lodash',
reason: 'Need lodash for utility functions'
});
// 等待批准...
}
// 执行安装
const result = await window.api.invoke('sandbox:execute', {
sessionId: 'session-123',
command: 'npm',
args: ['install', 'lodash'],
options: {
cwd: 'projects/myapp',
timeout: 120000
}
});
```
### 4.3 批量清理
```typescript
// 获取所有工作区
const workspaces = await window.api.invoke('sandbox:list');
// 销毁所有
for (const ws of workspaces) {
await window.api.invoke('sandbox:destroy', {
sessionId: ws.id,
force: true
});
}
// 或者使用 cleanup
const cleanup = await window.api.invoke('sandbox:cleanup');
console.log(`Freed: ${cleanup.freedSpace}`);
```
---
## 5. 变更记录
| 日期 | 版本 | 变更 |
|------|------|------|
| 2026-03-22 | 1.0.0 | 初始版本 |

View File

@@ -0,0 +1,61 @@
# Sandbox 子模块集成说明
**范围**:文中的 `qiming-sandbox-helper``resources/sandbox-helper` 以及 manifest 里 `win32-*` 产物**仅用于 Windows 客户端**沙箱macOS / Linux 分别走 seatbelt / bwrap不依赖上述 exe。
## 目标
将三端沙箱运行时统一从 `crates/agent-sandbox-runtime` 同步到 Electron `resources`
- 目标目录: `resources/sandbox-runtime/bin`
- 打包映射: `build.extraResources -> sandbox-runtime`
## 当前接入点
1. 脚本: `scripts/prepare/prepare-sandbox-runtime.js`
2. npm script: `prepare:sandbox-runtime`
3. Windows 内置 helper: `crates/windows-sandbox-helper``npm run build:sandbox-helper`Windows 上 `prepare:all` 会通过 `prepare:sandbox-helper-win` 自动构建),产出 `resources/sandbox-helper/qiming-sandbox-helper.exe`Windows 安装包 `extraResources` 映射为 `sandbox-helper/*.exe`
4. 构建链: `prepare:all` 已串联 `prepare:sandbox-helper-win``prepare:sandbox-runtime`
5. 签名: `scripts/build/after-sign.js` 已覆盖 `resources/sandbox-runtime``resources/sandbox-helper`Windows
## 子模块清单约定
`crates/agent-sandbox-runtime/manifest.json` 支持以下最小结构:
```json
{
"version": "x.y.z",
"platforms": {
"linux-x64": {
"source": "artifacts/linux/x64/bwrap",
"sha256": "..."
},
"win32-x64": {
"source": "artifacts/windows/x64/qiming-sandbox-helper.exe",
"sha256": "...",
"targetName": "qiming-sandbox-helper.exe"
}
}
}
```
## 运行时策略与后端
- 策略键: `settings.sandbox_policy`
- 默认策略:
- `enabled=true`
- `mode=non-main`
- `backend=auto`
- `fallback=degrade_to_off`
- `windows.sandbox.mode=read-only``workspace-write`
`backend=auto` 映射:
- macOS -> `macos-seatbelt`
- Linux -> `linux-bwrap`
- Windows -> `windows-sandbox`(探测 `qiming-sandbox-helper.exe`
## 注意事项
1. 子模块未初始化时,`prepare-sandbox-runtime` 会告警并跳过,不中断构建。
2. Windows helper setup 目前为占位实现,后续由 `sandbox:setup` 补全真实 setup 流程。
3. 当前 WorkspaceManager 仍以 Docker 全量执行链路为主,其他后端已完成策略与资源接入框架。

View File

@@ -0,0 +1,451 @@
# 沙箱故障排查指南
> **版本**: 1.0.0
> **更新**: 2026-03-22
---
## 1. Docker 相关问题
### 1.1 Docker 不可用
**症状:** `SandboxError: SANDBOX_UNAVAILABLE`
**排查步骤:**
```bash
# 1. 检查 Docker 是否安装
docker --version
# 2. 检查 Docker 服务状态
# macOS
docker status
# Linux
sudo systemctl status docker
# 3. 检查 Docker 权限
docker ps
```
**解决方案:**
| 平台 | 命令 |
|------|------|
| macOS | 安装 Docker Desktop 并启动 |
| Linux | `sudo systemctl start docker` |
| Windows | 安装 Docker Desktop 并启用 WSL2 |
---
### 1.2 容器启动失败
**症状:** `SandboxError: WORKSPACE_EXISTS` 或容器无法创建
**排查步骤:**
```bash
# 1. 检查容器列表
docker ps -a
# 2. 检查 Docker 日志
docker logs <container-id>
# 3. 检查磁盘空间
docker system df
```
**解决方案:**
```bash
# 清理未使用的容器和镜像
docker system prune -a
# 增加 Docker 磁盘配额Docker Desktop -> Settings -> Disk
```
---
### 1.3 容器内命令执行失败
**症状:** `SandboxError: EXECUTION_FAILED`
**排查步骤:**
```bash
# 1. 检查容器是否运行
docker ps | grep <container-id>
# 2. 进入容器调试
docker exec -it <container-id> /bin/sh
# 3. 检查容器资源
docker stats <container-id>
```
---
## 2. Windows WSL 相关问题
### 2.1 WSL 未安装
**症状:** `SandboxError: SANDBOX_UNAVAILABLE` (Windows)
**排查步骤:**
```powershell
# 检查 WSL 状态
wsl --status
# 列出已安装的发行版
wsl --list --verbose
```
**解决方案:**
```powershell
# 安装 WSL
wsl --install -d Ubuntu-22.04
# 重启后检查
wsl --set-default Ubuntu-22.04
```
---
### 2.2 WSL 发行版启动失败
**症状:** `SandboxError: WORKSPACE_EXISTS`
**排查步骤:**
```powershell
# 关闭所有 WSL 实例
wsl --shutdown
# 检查发行版状态
wsl --list --verbose
```
---
## 3. Linux Firejail 相关问题
### 3.1 Firejail 未安装
**症状:** `SandboxError: SANDBOX_UNAVAILABLE` (Linux)
**排查步骤:**
```bash
# 检查 Firejail
firejail --version
# 安装 Firejail
# Ubuntu/Debian
sudo apt install firejail
# Fedora
sudo dnf install firejail
```
---
### 3.2 Firejail 配置文件错误
**症状:** `SandboxError: EXECUTION_FAILED`
**排查步骤:**
```bash
# 检查 profile 语法
firejail --debug-profile=/path/to/profile 2>&1
# 测试 profile
firejail --profile=/path/to/profile /bin/ls
```
---
## 4. 权限相关问题
### 4.1 权限被拒绝
**症状:** `SandboxError: PERMISSION_DENIED`
**排查步骤:**
```bash
# 检查工作区目录权限
ls -la ~/.qimingclaw/workspaces/
# 检查用户组
groups $USER
```
**解决方案:**
```bash
# 修复目录权限
chmod -R 755 ~/.qimingclaw/workspaces/
# 将用户加入 docker 组
sudo usermod -aG docker $USER
```
---
### 4.2 权限请求无响应
**症状:** 命令挂起等待权限确认
**排查步骤:**
```bash
# 检查待处理的权限请求
# 在渲染进程中查看 PendingPermissions 状态
```
**解决方案:**
- 检查用户是否看到了权限确认弹窗
- 增加权限请求超时时间
- 设置自动批准常见操作
---
## 5. 工作区问题
### 5.1 工作区创建失败
**症状:** `SandboxError: WORKSPACE_EXISTS`
**排查步骤:**
```bash
# 检查工作区目录
ls -la ~/.qimingclaw/workspaces/
# 检查是否有残留进程
ps aux | grep qimingclaw
```
**解决方案:**
```bash
# 删除残留目录
rm -rf ~/.qimingclaw/workspaces/<session-id>
# 重启应用
```
---
### 5.2 工作区磁盘空间不足
**症状:** `SandboxError: EXECUTION_FAILED` (disk full)
**排查步骤:**
```bash
# 检查磁盘空间
df -h ~/.qimingclaw/
# 检查工作区大小
du -sh ~/.qimingclaw/workspaces/*
```
**解决方案:**
```bash
# 清理临时文件
rm -rf ~/.qimingclaw/workspaces/*/tmp/*
# 增加磁盘限额配置
# 在 sandbox.json 中设置 maxDiskUsage
```
---
### 5.3 工作区清理失败
**症状:** 退出应用后工作区仍然存在
**排查步骤:**
```bash
# 检查保留策略配置
cat ~/.qimingclaw/workspaces/<session-id>/sandbox.json
# 检查是否有进程占用
lsof +D ~/.qimingclaw/workspaces/<session-id>
```
---
## 6. 性能问题
### 6.1 沙箱启动慢
**症状:** 首次创建沙箱需要很长时间
**原因:** Docker 镜像下载、WSL 初始化等
**解决方案:**
```bash
# 预热:提前拉取镜像
docker pull node:20-slim
# WSL 预初始化
wsl -d Ubuntu-22.04
```
---
### 6.2 命令执行超时
**症状:** `SandboxError: EXECUTION_TIMEOUT`
**排查步骤:**
```bash
# 检查命令复杂度
# 增加超时时间
```
**解决方案:**
```typescript
// 增加默认超时
const executeOptions: ExecuteOptions = {
timeout: 600000, // 10 分钟
};
```
---
## 7. 网络问题
### 7.1 沙箱内无法访问网络
**症状:** `npm install` 失败
**排查步骤:**
```bash
# 检查 Docker 网络
docker network ls
# 测试网络连通性
docker exec <container-id> ping 8.8.8.8
```
**解决方案:**
```typescript
// 在配置中启用网络
const config: SandboxConfig = {
networkEnabled: true,
};
```
---
## 8. 跨平台路径问题
### 8.1 Windows 路径错误
**症状:** 文件找不到
**排查步骤:**
```bash
# 检查路径格式
echo $PATH
```
**解决方案:**
```typescript
// 使用 path 模块
import path from 'path';
const sandboxPath = path.join(workspacePath, 'projects');
// 避免手动拼接字符串
```
---
## 9. 日志获取
### 9.1 主进程日志
```bash
# 查看主进程日志
tail -f ~/.qimingclaw/logs/main.$(date +%Y-%m-%d).log
```
### 9.2 Docker 容器日志
```bash
# 查看容器日志
docker logs <container-id>
# 实时跟踪
docker logs -f <container-id>
```
---
## 10. 常见错误代码
| 错误代码 | 说明 | 解决方案 |
|---------|------|---------|
| `SANDBOX_UNAVAILABLE` | 沙箱不可用 | 检查 Docker/WSL/Firejail 安装 |
| `WORKSPACE_NOT_FOUND` | 工作区不存在 | 创建工作区或检查 ID |
| `WORKSPACE_EXISTS` | 工作区已存在 | 销毁现有工作区 |
| `PERMISSION_DENIED` | 权限不足 | 检查权限配置或用户确认 |
| `EXECUTION_FAILED` | 执行失败 | 查看详细日志 |
| `EXECUTION_TIMEOUT` | 执行超时 | 增加超时或优化命令 |
| `FILE_NOT_FOUND` | 文件不存在 | 检查路径 |
| `FILE_WRITE_FAILED` | 文件写入失败 | 检查磁盘空间和权限 |
| `CLEANUP_FAILED` | 清理失败 | 手动清理或检查进程 |
| `CONFIG_INVALID` | 配置无效 | 检查配置参数 |
---
## 11. 获取帮助
### 11.1 收集诊断信息
```bash
# 1. 系统信息
uname -a
# 2. Docker 信息
docker version
docker info
# 3. 应用日志
tar -czf diagnostics.tar.gz ~/.qimingclaw/logs/
# 4. 配置文件
cat ~/.qimingclaw/config.json
```
### 11.2 报告问题
报告问题时请包含:
- 操作系统版本
- Docker/WSL/Firejail 版本
- 错误日志
- 复现步骤
- 预期行为
---
## 12. 变更记录
| 日期 | 版本 | 变更 |
|------|------|------|
| 2026-03-22 | 1.0.0 | 初始版本 |