chore: initialize qiming workspace repository
This commit is contained in:
@@ -0,0 +1,559 @@
|
||||
/**
|
||||
* 沙箱 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");
|
||||
}
|
||||
Reference in New Issue
Block a user