chore: initialize qiming workspace repository
This commit is contained in:
535
qimingclaw/crates/agent-electron-client/docs/sandbox/API.md
Normal file
535
qimingclaw/crates/agent-electron-client/docs/sandbox/API.md
Normal 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
|
||||
@@ -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_RESTRICTED,APPDATA 不在 ALLOW ACEs | `writable_roots` 全部 + WRITE_RESTRICTED,APPDATA 在 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
|
||||
@@ -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
|
||||
@@ -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 开发指南
|
||||
@@ -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 | 初始版本 |
|
||||
@@ -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 | 初始版本 |
|
||||
@@ -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 全量执行链路为主,其他后端已完成策略与资源接入框架。
|
||||
@@ -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 | 初始版本 |
|
||||
Reference in New Issue
Block a user