feat(workbench): integrate operational workflows and map state
This commit is contained in:
@@ -11,6 +11,7 @@ type MockAgentApiOptions = {
|
||||
};
|
||||
|
||||
export async function mockAgentApi(page: Page, options: MockAgentApiOptions = {}) {
|
||||
const corsHeaders = { "Access-Control-Allow-Origin": "*" };
|
||||
const model = options.model ?? {
|
||||
id: "test/model",
|
||||
label: "测试模型",
|
||||
@@ -19,10 +20,41 @@ export async function mockAgentApi(page: Page, options: MockAgentApiOptions = {}
|
||||
};
|
||||
const triggeredSessions = new Map<string, { sessionId: string; triggerId: string }>();
|
||||
await page.route("**/api/v1/agent/chat/**", async (route) => {
|
||||
if (route.request().method() === "OPTIONS") {
|
||||
await route.fulfill({
|
||||
status: 204,
|
||||
headers: {
|
||||
"Access-Control-Allow-Headers": "Authorization, Content-Type, X-Project-Id",
|
||||
"Access-Control-Allow-Methods": "DELETE, GET, PATCH, POST, OPTIONS",
|
||||
"Access-Control-Allow-Origin": "*"
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const pathname = new URL(route.request().url()).pathname;
|
||||
|
||||
const collaborationMatch = pathname.match(/\/task\/([^/]+)\/collaboration\/session$/);
|
||||
if (collaborationMatch && route.request().method() === "POST") {
|
||||
await route.fulfill({
|
||||
status: 201,
|
||||
headers: corsHeaders,
|
||||
json: {
|
||||
id: `collaboration-${collaborationMatch[1]}`,
|
||||
session_id: `collaboration-${collaborationMatch[1]}`,
|
||||
title: "任务协作问答",
|
||||
purpose: "task_collaboration",
|
||||
task_id: collaborationMatch[1],
|
||||
status: "active",
|
||||
created_at: "2026-09-23T01:11:00Z",
|
||||
updated_at: "2026-09-23T01:11:00Z"
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (pathname.endsWith("/session") && route.request().method() === "POST") {
|
||||
const body = route.request().postDataJSON() as {
|
||||
const body = (route.request().postDataJSON() ?? {}) as {
|
||||
trigger_context?: { trigger_id?: string };
|
||||
};
|
||||
const triggerId = body.trigger_context?.trigger_id;
|
||||
@@ -31,6 +63,7 @@ export async function mockAgentApi(page: Page, options: MockAgentApiOptions = {}
|
||||
if (triggerId && !existing) triggeredSessions.set(triggerId, { sessionId, triggerId });
|
||||
await route.fulfill({
|
||||
status: existing ? 200 : 201,
|
||||
headers: corsHeaders,
|
||||
json: {
|
||||
session_id: sessionId,
|
||||
title: "水质投诉事件调查",
|
||||
@@ -47,11 +80,15 @@ export async function mockAgentApi(page: Page, options: MockAgentApiOptions = {}
|
||||
|
||||
const sessionMatch = pathname.match(/\/session\/([^/]+)$/);
|
||||
if (sessionMatch && route.request().method() === "GET") {
|
||||
const collaboration = sessionMatch[1].startsWith("collaboration-");
|
||||
await route.fulfill({
|
||||
headers: corsHeaders,
|
||||
json: {
|
||||
id: sessionMatch[1],
|
||||
session_id: sessionMatch[1],
|
||||
title: "水质投诉事件调查",
|
||||
title: collaboration ? "任务协作问答" : "水质投诉事件调查",
|
||||
purpose: collaboration ? "task_collaboration" : "ordinary_chat",
|
||||
task_id: collaboration ? sessionMatch[1].slice("collaboration-".length) : undefined,
|
||||
status: "active",
|
||||
created_at: "2026-09-22T02:00:00Z",
|
||||
updated_at: "2026-09-22T02:00:00Z",
|
||||
@@ -66,6 +103,7 @@ export async function mockAgentApi(page: Page, options: MockAgentApiOptions = {}
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
headers: {
|
||||
...corsHeaders,
|
||||
"Cache-Control": "no-cache",
|
||||
"Content-Type": "text/event-stream",
|
||||
"X-Vercel-AI-UI-Message-Stream": "v1"
|
||||
@@ -84,12 +122,13 @@ export async function mockAgentApi(page: Page, options: MockAgentApiOptions = {}
|
||||
}
|
||||
|
||||
if (pathname.endsWith("/sessions")) {
|
||||
await route.fulfill({ json: { sessions: options.sessions ?? [] } });
|
||||
await route.fulfill({ headers: corsHeaders, json: { sessions: options.sessions ?? [] } });
|
||||
return;
|
||||
}
|
||||
|
||||
if (pathname.endsWith("/models")) {
|
||||
await route.fulfill({
|
||||
headers: corsHeaders,
|
||||
json: {
|
||||
default_model: model.id,
|
||||
models: [model]
|
||||
@@ -100,6 +139,7 @@ export async function mockAgentApi(page: Page, options: MockAgentApiOptions = {}
|
||||
|
||||
if (pathname.endsWith("/ui-registry")) {
|
||||
await route.fulfill({
|
||||
headers: corsHeaders,
|
||||
json: {
|
||||
schema_version: "agent-ui-registry@1",
|
||||
chart_grammars: [],
|
||||
@@ -112,6 +152,7 @@ export async function mockAgentApi(page: Page, options: MockAgentApiOptions = {}
|
||||
|
||||
if (pathname.endsWith("/frontend-action-registry")) {
|
||||
await route.fulfill({
|
||||
headers: corsHeaders,
|
||||
json: {
|
||||
schema_version: "frontend-action-registry@1",
|
||||
actions: []
|
||||
@@ -120,6 +161,6 @@ export async function mockAgentApi(page: Page, options: MockAgentApiOptions = {}
|
||||
return;
|
||||
}
|
||||
|
||||
await route.fulfill({ json: {} });
|
||||
await route.fulfill({ headers: corsHeaders, json: {} });
|
||||
});
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@ const emptyRuntimeSnapshot = {
|
||||
work_orders: []
|
||||
};
|
||||
|
||||
export const TEST_PROJECT_ID = "324ff7ee-c2d4-5184-a84b-48fb14a2933e";
|
||||
|
||||
export async function mockScadaApi(
|
||||
page: Page,
|
||||
devices: unknown[] = [],
|
||||
@@ -25,13 +27,27 @@ export async function mockScadaApi(
|
||||
analysisRuns: Record<string, unknown> = {},
|
||||
analysisHistory: unknown[] = []
|
||||
) {
|
||||
const corsHeaders = { "Access-Control-Allow-Origin": "*" };
|
||||
await page.route("https://tjwater-api.invalid/**", async (route) => {
|
||||
if (route.request().method() === "OPTIONS") {
|
||||
await route.fulfill({
|
||||
status: 204,
|
||||
headers: {
|
||||
...corsHeaders,
|
||||
"Access-Control-Allow-Headers": "Authorization, Content-Type, Last-Event-ID, X-Project-Id",
|
||||
"Access-Control-Allow-Methods": "DELETE, GET, PATCH, POST, OPTIONS"
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const url = new URL(route.request().url());
|
||||
if (url.pathname === "/api/v1/projects") {
|
||||
await route.fulfill({
|
||||
contentType: "application/json",
|
||||
headers: corsHeaders,
|
||||
body: JSON.stringify({
|
||||
items: [{ project_id: "project-1", gs_workspace: "tjwater_next", status: "active" }],
|
||||
items: [{ project_id: TEST_PROJECT_ID, gs_workspace: "tjwater_next", status: "active" }],
|
||||
total: 1,
|
||||
limit: 1000,
|
||||
offset: 0
|
||||
@@ -42,6 +58,7 @@ export async function mockScadaApi(
|
||||
if (url.pathname === "/api/v1/scada-devices") {
|
||||
await route.fulfill({
|
||||
contentType: "application/json",
|
||||
headers: corsHeaders,
|
||||
body: JSON.stringify({ items: devices, total: devices.length, limit: 1000, offset: 0 })
|
||||
});
|
||||
return;
|
||||
@@ -49,6 +66,7 @@ export async function mockScadaApi(
|
||||
if (url.pathname === "/api/v1/projects/current/metadata" && projectPresentation) {
|
||||
await route.fulfill({
|
||||
contentType: "application/json",
|
||||
headers: corsHeaders,
|
||||
body: JSON.stringify(projectPresentation.metadata)
|
||||
});
|
||||
return;
|
||||
@@ -56,6 +74,7 @@ export async function mockScadaApi(
|
||||
if (url.pathname === "/api/v1/access-context" && projectPresentation) {
|
||||
await route.fulfill({
|
||||
contentType: "application/json",
|
||||
headers: corsHeaders,
|
||||
body: JSON.stringify(projectPresentation.accessContext)
|
||||
});
|
||||
return;
|
||||
@@ -63,6 +82,7 @@ export async function mockScadaApi(
|
||||
if (url.pathname === "/api/v1/timeseries/scada-readings") {
|
||||
await route.fulfill({
|
||||
contentType: "application/json",
|
||||
headers: corsHeaders,
|
||||
body: JSON.stringify(readings)
|
||||
});
|
||||
return;
|
||||
@@ -70,16 +90,41 @@ export async function mockScadaApi(
|
||||
if (url.pathname === "/api/v1/workbench/runtime") {
|
||||
await route.fulfill({
|
||||
contentType: "application/json",
|
||||
headers: corsHeaders,
|
||||
body: JSON.stringify(runtimeSnapshot)
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (url.pathname === "/api/v1/workbench/sync-snapshot") {
|
||||
await route.fulfill({
|
||||
contentType: "application/json",
|
||||
headers: corsHeaders,
|
||||
body: JSON.stringify({
|
||||
schema: "workbench-sync-snapshot@1",
|
||||
event_cursor: 0,
|
||||
runtime: runtimeSnapshot,
|
||||
tasks: [],
|
||||
notifications: [],
|
||||
generated_at: "2026-09-17T02:30:00Z"
|
||||
})
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (url.pathname === "/api/v1/workbench/events") {
|
||||
await route.fulfill({
|
||||
contentType: "text/event-stream",
|
||||
headers: { ...corsHeaders, "Cache-Control": "no-cache" },
|
||||
body: ": test keepalive\n\n"
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (url.pathname === "/api/v1/workbench/analysis-runs") {
|
||||
const limit = Number(url.searchParams.get("limit") ?? 10);
|
||||
const offset = Number(url.searchParams.get("offset") ?? 0);
|
||||
const items = analysisHistory.slice(offset, offset + limit);
|
||||
await route.fulfill({
|
||||
contentType: "application/json",
|
||||
headers: corsHeaders,
|
||||
body: JSON.stringify({
|
||||
items,
|
||||
limit,
|
||||
@@ -96,17 +141,24 @@ export async function mockScadaApi(
|
||||
if (analysisRun) {
|
||||
await route.fulfill({
|
||||
contentType: "application/json",
|
||||
headers: corsHeaders,
|
||||
body: JSON.stringify(analysisRun)
|
||||
});
|
||||
} else {
|
||||
await route.fulfill({
|
||||
status: 404,
|
||||
contentType: "application/json",
|
||||
headers: corsHeaders,
|
||||
body: JSON.stringify({ detail: "Analysis run not found" })
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
await route.fulfill({ status: 404, contentType: "application/json", body: "{}" });
|
||||
await route.fulfill({
|
||||
status: 404,
|
||||
contentType: "application/json",
|
||||
headers: corsHeaders,
|
||||
body: "{}"
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user