feat(agents): 完成云智能体工作台与交互流程

This commit is contained in:
2026-09-10 19:25:12 +08:00
parent 46fbae9dec
commit 64a3cf4934
26 changed files with 1955 additions and 49 deletions

View File

@@ -1,7 +1,7 @@
import type { IncomingMessage, ServerResponse } from 'node:http';
import { CLOUD_AGENTS_PATH } from '../../../shared/cloud-agents';
import { CloudAgentsError, CloudAgentsModule } from '../../services/cloud-agents';
import { parseJsonBody, sendJson } from '../route-utils';
import { parseJsonBody, sendJson, flushStreamingHeaders, writeStreamingChunk } from '../route-utils';
const cloudAgents = new CloudAgentsModule();
@@ -11,7 +11,39 @@ export async function handleCloudAgentsRoutes(req: IncomingMessage, res: ServerR
const path = url.pathname.slice(CLOUD_AGENTS_PATH.length);
try {
let data: unknown;
if ((path === '/bootstrap' || path === '/agents') && req.method === 'GET') {
const stream = /^\/runs\/([a-zA-Z0-9_-]{1,64})\/events$/.exec(path);
if (stream && req.method === 'GET') {
const controller = new AbortController();
const decoder = new TextDecoder();
const close = () => controller.abort();
res.once('close', close);
try {
const after = (typeof req.headers['last-event-id'] === 'string' ? req.headers['last-event-id'] : null)
?? url.searchParams.get('after_seq') ?? '0-0';
for await (const chunk of cloudAgents.events(stream[1], after, controller.signal)) {
if (!res.headersSent) {
res.setHeader('Content-Type', 'text/event-stream; charset=utf-8');
res.setHeader('Connection', 'keep-alive');
flushStreamingHeaders(res);
}
if (!await writeStreamingChunk(res, decoder.decode(chunk, { stream: true }))) break;
}
} finally {
controller.abort();
res.off('close', close);
}
res.end();
return true;
}
if (path === '/attachments/pick' && req.method === 'POST') {
data = await cloudAgents.upload();
} else if (path === '/knowledge/pick' && req.method === 'POST') {
data = await cloudAgents.uploadKnowledge(await parseJsonBody(req));
} else if (path === '/files/save' && req.method === 'POST') {
data = await cloudAgents.download(await parseJsonBody(req));
} else if (path === '/actions' && req.method === 'POST') {
data = await cloudAgents.execute(await parseJsonBody(req));
} else if ((path === '/bootstrap' || path === '/agents') && req.method === 'GET') {
data = await cloudAgents.list(url.searchParams.get('cursor'));
} else if (path === '/agents' && req.method === 'POST') {
data = await cloudAgents.create(await parseJsonBody(req));
@@ -28,7 +60,8 @@ export async function handleCloudAgentsRoutes(req: IncomingMessage, res: ServerR
} catch (error) {
const failure = error instanceof CloudAgentsError ? error
: new CloudAgentsError(error instanceof SyntaxError ? 400 : 502, error instanceof SyntaxError ? 'invalid_input' : 'cloud_service_unavailable');
sendJson(res, failure.status, { success: false, error: failure.message, code: failure.code });
if (res.headersSent) res.end();
else sendJson(res, failure.status, { success: false, error: failure.message, code: failure.code });
}
return true;
}