import { logger } from 'ee-core/log'; import { listAllArtifacts, listArtifacts, openArtifact, openArtifactRoot, readArtifactBase64, readArtifactText, revealArtifact, saveArtifactAs, } from '../service/codex/codexArtifactService'; import { asMessage, fail, ok, type Rpc } from '../service/codex/types'; /** * 产物(会话工作目录里的文件)读写控制器。 * * 与 codexCtl 同为 ee-core 自动注册的 controller,通道前缀是 controller/codexArtifactCtl/。 * 这里只做参数校验与异常包装,安全边界(root 白名单 + 路径穿越校验)在 service 里。 */ async function guard(label: string, run: () => Promise): Promise> { try { return ok(await run()); } catch (error) { const message = asMessage(error); logger.error(`[codexArtifactCtl] ${label} failed:`, message); return fail(message); } } function requireString(value: unknown, label: string): string { if (typeof value !== 'string' || !value.trim()) throw new Error(`${label} 不能为空`); return value.trim(); } function optionalNumber(value: unknown): number | undefined { return typeof value === 'number' && Number.isFinite(value) ? value : undefined; } class CodexArtifactCtl { async list(params?: { root?: string; dir?: string }): Promise> { return guard('list', async () => listArtifacts(requireString(params?.root, 'root'), typeof params?.dir === 'string' ? params.dir : ''), ); } /** 递归列出工作空间全部文件(「本会话产物」视图用) */ async listAll(params?: { root?: string }): Promise> { return guard('listAll', async () => listAllArtifacts(requireString(params?.root, 'root'))); } async readText(params?: { root?: string; path?: string; maxBytes?: number }): Promise> { return guard('readText', async () => readArtifactText( requireString(params?.root, 'root'), requireString(params?.path, 'path'), optionalNumber(params?.maxBytes), ), ); } async readBinary(params?: { root?: string; path?: string; maxBytes?: number }): Promise> { return guard('readBinary', async () => readArtifactBase64( requireString(params?.root, 'root'), requireString(params?.path, 'path'), optionalNumber(params?.maxBytes), ), ); } async saveAs(params?: { root?: string; path?: string; name?: string }): Promise> { return guard('saveAs', async () => saveArtifactAs( requireString(params?.root, 'root'), requireString(params?.path, 'path'), typeof params?.name === 'string' ? params.name : undefined, ), ); } async reveal(params?: { root?: string; path?: string }): Promise>> { return guard('reveal', async () => { await revealArtifact(requireString(params?.root, 'root'), requireString(params?.path, 'path')); return {}; }); } async open(params?: { root?: string; path?: string }): Promise>> { return guard('open', async () => { await openArtifact(requireString(params?.root, 'root'), requireString(params?.path, 'path')); return {}; }); } async openRoot(params?: { root?: string }): Promise>> { return guard('openRoot', async () => { await openArtifactRoot(requireString(params?.root, 'root')); return {}; }); } } CodexArtifactCtl.toString = () => 'CodexArtifactCtl'; export default CodexArtifactCtl;