Files
qiming/qimingclaw/crates/agent-electron-client/src/main/ipc/sandboxHandlers.ts

560 lines
15 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 沙箱 IPC 通道
*
* 提供沙箱工作区管理的 IPC 接口
*
* @version 1.0.0
* @updated 2026-03-22
*/
import { ipcMain } from "electron";
import log from "electron-log";
import type {
Workspace,
CreateWorkspaceOptions,
ExecuteOptions,
ExecuteResult,
FileInfo,
PermissionType,
PermissionResult,
PermissionRequest,
CleanupResult,
SandboxStatus,
SandboxPolicy,
SandboxCapabilities,
} from "@shared/types/sandbox";
import {
SandboxError,
SandboxErrorCode,
isSandboxError,
} from "@shared/errors/sandbox";
import { t } from "../services/i18n";
// 服务单例(在服务实现完成后注入)
let sandboxService: {
createWorkspace(
sessionId: string,
options?: CreateWorkspaceOptions,
): Promise<Workspace>;
destroyWorkspace(sessionId: string): Promise<void>;
listWorkspaces(): Workspace[];
getWorkspace(sessionId: string): Workspace | undefined;
execute(
sessionId: string,
command: string,
args: 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(): Promise<SandboxStatus>;
cleanup(): Promise<CleanupResult>;
} | null = null;
let permissionService: {
checkPermission(
sessionId: string,
type: PermissionType,
target: string,
): Promise<PermissionResult>;
requestPermission(
sessionId: string,
type: PermissionType,
target: string,
reason?: string,
): Promise<unknown>;
getPendingRequests(sessionId?: string): PermissionRequest[];
approve(
requestId: string,
approvedBy?: "user" | "system",
reason?: string,
): Promise<void>;
deny(requestId: string, reason?: string): Promise<void>;
} | null = null;
let sandboxControlService: {
getPolicy(): Promise<SandboxPolicy>;
setPolicy(patch: Partial<SandboxPolicy>): Promise<SandboxPolicy>;
getCapabilities(): Promise<SandboxCapabilities>;
setup(params?: {
windows?: { sandbox?: { mode?: "read-only" | "workspace-write" } };
}): Promise<{ success: boolean; message?: string }>;
} | null = null;
/**
* 设置沙箱服务实例
*/
export function setSandboxService(service: typeof sandboxService): void {
sandboxService = service;
log.info("[IPC] Sandbox service injected");
}
/**
* 设置权限服务实例
*/
export function setPermissionService(service: typeof permissionService): void {
permissionService = service;
log.info("[IPC] Permission service injected");
}
/**
* 设置沙箱控制服务(策略/能力/setup
*/
export function setSandboxControlService(
service: typeof sandboxControlService,
): void {
sandboxControlService = service;
log.info("[IPC] Sandbox control service injected");
}
/**
* 获取沙箱服务(带检查)
*/
function getSandboxService(): NonNullable<typeof sandboxService> {
if (!sandboxService) {
throw new SandboxError(
"Sandbox service not initialized",
SandboxErrorCode.SANDBOX_UNAVAILABLE,
);
}
return sandboxService;
}
/**
* 获取权限服务(带检查)
*/
function getPermissionService(): NonNullable<typeof permissionService> {
if (!permissionService) {
throw new SandboxError(
"Permission service not initialized",
SandboxErrorCode.INTERNAL_ERROR,
);
}
return permissionService;
}
function getSandboxControlService(): NonNullable<typeof sandboxControlService> {
if (!sandboxControlService) {
throw new SandboxError(
"Sandbox control service not initialized",
SandboxErrorCode.SANDBOX_UNAVAILABLE,
);
}
return sandboxControlService;
}
/**
* 错误处理包装器
*/
function handleError(
error: unknown,
operation: string,
): { success: false; error: string; code?: string } {
log.error(`[IPC] sandbox:${operation} failed:`, error);
if (isSandboxError(error)) {
return {
success: false,
error: error.getUserMessage(),
code: error.code,
};
}
return {
success: false,
error: error instanceof Error ? error.message : String(error),
};
}
/**
* 注册沙箱 IPC 通道
*/
export function registerSandboxHandlers(): void {
// ============================================================================
// 工作区管理
// ============================================================================
/**
* 创建工作区
* @channel sandbox:create
* @param sessionId 会话 ID
* @param options 创建选项
*/
ipcMain.handle(
"sandbox:create",
async (_, sessionId: string, options?: CreateWorkspaceOptions) => {
log.info("[IPC] sandbox:create:", { sessionId, options });
try {
const service = getSandboxService();
const workspace = await service.createWorkspace(sessionId, options);
return { success: true, data: workspace };
} catch (error) {
return handleError(error, "create");
}
},
);
/**
* 销毁工作区
* @channel sandbox:destroy
* @param sessionId 会话 ID
*/
ipcMain.handle("sandbox:destroy", async (_, sessionId: string) => {
log.info("[IPC] sandbox:destroy:", { sessionId });
try {
const service = getSandboxService();
await service.destroyWorkspace(sessionId);
return { success: true };
} catch (error) {
return handleError(error, "destroy");
}
});
/**
* 列出所有工作区
* @channel sandbox:list
*/
ipcMain.handle("sandbox:list", async () => {
log.info("[IPC] sandbox:list");
try {
const service = getSandboxService();
const workspaces = service.listWorkspaces();
return { success: true, data: workspaces };
} catch (error) {
return handleError(error, "list");
}
});
/**
* 获取工作区信息
* @channel sandbox:info
* @param sessionId 会话 ID
*/
ipcMain.handle("sandbox:info", async (_, sessionId: string) => {
log.info("[IPC] sandbox:info:", { sessionId });
try {
const service = getSandboxService();
const workspace = service.getWorkspace(sessionId);
if (!workspace) {
return {
success: false,
error: t("Claw.Sandbox.workspaceNotFound", sessionId),
code: SandboxErrorCode.WORKSPACE_NOT_FOUND,
};
}
return { success: true, data: workspace };
} catch (error) {
return handleError(error, "info");
}
});
// ============================================================================
// 命令执行
// ============================================================================
/**
* 执行命令
* @channel sandbox:execute
* @param sessionId 会话 ID
* @param command 命令
* @param args 参数
* @param options 执行选项
*/
ipcMain.handle(
"sandbox:execute",
async (
_,
sessionId: string,
command: string,
args: string[],
options?: ExecuteOptions,
) => {
log.info("[IPC] sandbox:execute:", { sessionId, command, args, options });
try {
const service = getSandboxService();
const result = await service.execute(sessionId, command, args, options);
return { success: true, data: result };
} catch (error) {
return handleError(error, "execute");
}
},
);
// ============================================================================
// 文件操作
// ============================================================================
/**
* 读取文件
* @channel sandbox:readFile
* @param sessionId 会话 ID
* @param path 文件路径
*/
ipcMain.handle(
"sandbox:readFile",
async (_, sessionId: string, path: string) => {
log.info("[IPC] sandbox:readFile:", { sessionId, path });
try {
const service = getSandboxService();
const content = await service.readFile(sessionId, path);
return { success: true, data: content };
} catch (error) {
return handleError(error, "readFile");
}
},
);
/**
* 写入文件
* @channel sandbox:writeFile
* @param sessionId 会话 ID
* @param path 文件路径
* @param content 文件内容
*/
ipcMain.handle(
"sandbox:writeFile",
async (_, sessionId: string, path: string, content: string) => {
log.info("[IPC] sandbox:writeFile:", { sessionId, path });
try {
const service = getSandboxService();
await service.writeFile(sessionId, path, content);
return { success: true };
} catch (error) {
return handleError(error, "writeFile");
}
},
);
// ============================================================================
// 权限管理
// ============================================================================
/**
* 检查权限
* @channel sandbox:checkPermission
* @param sessionId 会话 ID
* @param type 权限类型
* @param target 目标资源
*/
ipcMain.handle(
"sandbox:checkPermission",
async (_, sessionId: string, type: PermissionType, target: string) => {
log.info("[IPC] sandbox:checkPermission:", { sessionId, type, target });
try {
const service = getPermissionService();
const result = await service.checkPermission(sessionId, type, target);
return { success: true, data: result };
} catch (error) {
return handleError(error, "checkPermission");
}
},
);
/**
* 请求权限
* @channel sandbox:requestPermission
* @param sessionId 会话 ID
* @param type 权限类型
* @param target 目标资源
* @param reason 请求原因
*/
ipcMain.handle(
"sandbox:requestPermission",
async (
_,
sessionId: string,
type: PermissionType,
target: string,
reason?: string,
) => {
log.info("[IPC] sandbox:requestPermission:", {
sessionId,
type,
target,
reason,
});
try {
const service = getPermissionService();
const permission = await service.requestPermission(
sessionId,
type,
target,
reason,
);
return { success: true, data: permission };
} catch (error) {
return handleError(error, "requestPermission");
}
},
);
/**
* 获取待审批权限列表
* @channel sandbox:getPendingPermissions
* @param sessionId 可选的会话 ID 过滤
*/
ipcMain.handle(
"sandbox:getPendingPermissions",
async (_, sessionId?: string) => {
log.info("[IPC] sandbox:getPendingPermissions:", { sessionId });
try {
const service = getPermissionService();
const requests = service.getPendingRequests(sessionId);
return { success: true, data: requests };
} catch (error) {
return handleError(error, "getPendingPermissions");
}
},
);
/**
* 批准权限请求
* @channel sandbox:approvePermission
* @param requestId 请求 ID
* @param reason 原因
*/
ipcMain.handle(
"sandbox:approvePermission",
async (_, requestId: string, reason?: string) => {
log.info("[IPC] sandbox:approvePermission:", { requestId, reason });
try {
const service = getPermissionService();
await service.approve(requestId, "user", reason);
return { success: true };
} catch (error) {
return handleError(error, "approvePermission");
}
},
);
/**
* 拒绝权限请求
* @channel sandbox:denyPermission
* @param requestId 请求 ID
* @param reason 原因
*/
ipcMain.handle(
"sandbox:denyPermission",
async (_, requestId: string, reason?: string) => {
log.info("[IPC] sandbox:denyPermission:", { requestId, reason });
try {
const service = getPermissionService();
await service.deny(requestId, reason);
return { success: true };
} catch (error) {
return handleError(error, "denyPermission");
}
},
);
// ============================================================================
// 清理与状态
// ============================================================================
/**
* 清理资源
* @channel sandbox:cleanup
*/
ipcMain.handle("sandbox:cleanup", async () => {
log.info("[IPC] sandbox:cleanup");
try {
const service = getSandboxService();
const result = await service.cleanup();
return { success: true, data: result };
} catch (error) {
return handleError(error, "cleanup");
}
});
/**
* 获取沙箱状态
* @channel sandbox:status
*/
ipcMain.handle("sandbox:status", async () => {
log.info("[IPC] sandbox:status");
try {
const service = getSandboxService();
const status = await service.getStatus();
return { success: true, data: status };
} catch (error) {
return handleError(error, "status");
}
});
/**
* 获取沙箱策略
* @channel sandbox:policy:get
*/
ipcMain.handle("sandbox:policy:get", async () => {
log.info("[IPC] sandbox:policy:get");
try {
const control = getSandboxControlService();
const policy = await control.getPolicy();
return { success: true, data: policy };
} catch (error) {
return handleError(error, "policy:get");
}
});
/**
* 更新沙箱策略
* @channel sandbox:policy:set
*/
ipcMain.handle(
"sandbox:policy:set",
async (_, patch: Partial<SandboxPolicy>) => {
log.info("[IPC] sandbox:policy:set:", { patch });
try {
const control = getSandboxControlService();
const policy = await control.setPolicy(patch);
return { success: true, data: policy };
} catch (error) {
return handleError(error, "policy:set");
}
},
);
/**
* 获取后端能力
* @channel sandbox:capabilities
*/
ipcMain.handle("sandbox:capabilities", async () => {
log.info("[IPC] sandbox:capabilities");
try {
const control = getSandboxControlService();
const capabilities = await control.getCapabilities();
return { success: true, data: capabilities };
} catch (error) {
return handleError(error, "capabilities");
}
});
/**
* 执行后端 setup当前主要用于 Windows Sandbox
* @channel sandbox:setup
*/
ipcMain.handle(
"sandbox:setup",
async (
_,
params?: {
windows?: { sandbox?: { mode?: "read-only" | "workspace-write" } };
},
) => {
log.info("[IPC] sandbox:setup:", { params });
try {
const control = getSandboxControlService();
const result = await control.setup(params);
return { success: true, data: result };
} catch (error) {
return handleError(error, "setup");
}
},
);
log.info("[IPC] Sandbox handlers registered");
}