feat(ai-workspace): support SEO codex provider bridge

This commit is contained in:
Codex
2026-07-03 08:43:37 +03:00
parent 1aceabee44
commit a7c6c34c82
26 changed files with 1649 additions and 36 deletions
+42 -3
View File
@@ -8,6 +8,8 @@ const MAX_EVENTS_PER_AGENT = 5000;
const MAX_REQUEST_META_PER_AGENT = 1000;
const HEARTBEAT_INTERVAL_MS = 30000;
const AGENT_STALE_MS = 75000;
const DEFAULT_AGENT_EVENT_TEXT_MAX = Number(process.env.AI_WORKSPACE_AGENT_EVENT_TEXT_MAX || 12000);
const CODEX_MESSAGE_EVENT_TEXT_MAX = Number(process.env.AI_WORKSPACE_CODEX_MESSAGE_EVENT_TEXT_MAX || 250000);
const ASSISTANT_RELAY_POLL_TIMEOUT_MS = Number(process.env.AI_WORKSPACE_ASSISTANT_RELAY_POLL_TIMEOUT_MS || 25000);
const ASSISTANT_RELAY_ACTION_TIMEOUT_MS = Number(process.env.AI_WORKSPACE_ASSISTANT_RELAY_ACTION_TIMEOUT_MS || 45000);
const ASSISTANT_RELAY_MAX_PENDING = Number(process.env.AI_WORKSPACE_ASSISTANT_RELAY_MAX_PENDING || 200);
@@ -67,6 +69,14 @@ app.post("/api/ai-workspace/hub/v1/agents/:pairingCode/dispatch", requireInterna
});
app.post("/api/ai-workspace/assistant/v1/actions", requireInternalApi, asyncRoute(proxyAssistantActions));
app.get("/api/ai-workspace/assistant/v1/executors", requireInternalApi, asyncRoute(proxyAssistantControlPlane));
app.post("/api/ai-workspace/assistant/v1/executors", requireInternalApi, asyncRoute(proxyAssistantControlPlane));
app.post("/api/ai-workspace/assistant/v1/executors/:executorId/agent/setup-command", requireInternalApi, asyncRoute(proxyAssistantControlPlane));
app.post("/api/ai-workspace/assistant/v1/executors/:executorId/check", requireInternalApi, asyncRoute(proxyAssistantControlPlane));
app.post("/api/ai-workspace/assistant/v1/threads", requireInternalApi, asyncRoute(proxyAssistantControlPlane));
app.get("/api/ai-workspace/assistant/v1/threads/:threadId/messages", requireInternalApi, asyncRoute(proxyAssistantControlPlane));
app.post("/api/ai-workspace/assistant/v1/threads/:threadId/messages", requireInternalApi, asyncRoute(proxyAssistantControlPlane));
app.post("/api/ai-workspace/assistant/v1/threads/:threadId/dispatch", requireInternalApi, asyncRoute(proxyAssistantControlPlane));
app.post("/api/ai-workspace/setup-codes/redeem", asyncRoute(proxySetupCodeRedeem));
app.get("/api/ai-workspace/bridge-package.tgz", asyncRoute(proxyBridgePackage));
@@ -302,6 +312,33 @@ async function proxyAssistantActions(req, res) {
res.send(text);
}
async function proxyAssistantControlPlane(req, res) {
if (!config.assistantInternalUrl || !config.assistantInternalAccessToken) {
res.status(503).json({ ok: false, error: "assistant_control_plane_proxy_not_configured" });
return;
}
const method = String(req.method || "GET").toUpperCase();
const hasBody = !["GET", "HEAD"].includes(method);
const targetUrl = `${config.assistantInternalUrl.replace(/\/+$/, "")}${String(req.originalUrl || req.url || "")}`;
const upstream = await fetch(targetUrl, {
method,
redirect: "manual",
headers: {
Accept: "application/json",
Authorization: `Bearer ${config.assistantInternalAccessToken}`,
...forwardOwnerHeaders(req),
...(hasBody ? { "Content-Type": "application/json" } : {}),
},
...(hasBody ? { body: JSON.stringify(req.body || {}) } : {}),
});
const contentType = upstream.headers.get("content-type") || "application/json; charset=utf-8";
const text = await upstream.text();
res.status(upstream.status);
res.setHeader("content-type", contentType);
res.send(text);
}
async function proxySetupCodeRedeem(req, res) {
if (!config.assistantInternalUrl || !config.assistantInternalAccessToken) {
res.status(503).json({ ok: false, error: "assistant_setup_proxy_not_configured" });
@@ -572,6 +609,8 @@ function pushAgentEvent(agent, requestId, rawEvent = {}) {
const now = new Date().toISOString();
const cleanRequestId = String(requestId || rawEvent.requestId || "");
const meta = cleanRequestId ? agent.requestMeta?.get(cleanRequestId) : null;
const kind = cleanString(rawEvent.kind || rawEvent.type || "event", 40);
const eventTextMax = kind === "codex_message" ? CODEX_MESSAGE_EVENT_TEXT_MAX : DEFAULT_AGENT_EVENT_TEXT_MAX;
const event = {
id: ++eventSeq,
pairingCode: agent.pairingCode,
@@ -581,10 +620,10 @@ function pushAgentEvent(agent, requestId, rawEvent = {}) {
threadTitle: rawEvent.threadTitle == null ? cleanString(meta?.threadTitle, 240) : cleanString(rawEvent.threadTitle, 240),
userMessage: rawEvent.userMessage == null ? cleanString(meta?.userMessage, 12000) : cleanString(rawEvent.userMessage, 12000),
remoteControlMode: rawEvent.remoteControlMode == null ? cleanString(meta?.remoteControlMode, 40) : cleanString(rawEvent.remoteControlMode, 40),
kind: cleanString(rawEvent.kind || rawEvent.type || "event", 40),
kind,
stream: rawEvent.stream ? cleanString(rawEvent.stream, 40) : "",
text: rawEvent.text == null ? "" : cleanString(rawEvent.text, 12000),
message: rawEvent.message == null ? "" : cleanString(rawEvent.message, 12000),
text: rawEvent.text == null ? "" : cleanString(rawEvent.text, eventTextMax),
message: rawEvent.message == null ? "" : cleanString(rawEvent.message, eventTextMax),
command: rawEvent.command == null ? "" : cleanString(rawEvent.command, 1000),
cwd: rawEvent.cwd == null ? "" : cleanString(rawEvent.cwd, 500),
currentFile: rawEvent.currentFile == null ? "" : cleanString(rawEvent.currentFile, 1000),