import { createInterface } from "node:readline"; import assert from "node:assert/strict"; // Test the web proxy/API and real model without sending licensed CMS content to it. // Supply credentials only over hidden stdin. Never log response bodies or secrets. const base = "http://127.0.0.1:5173"; const reader = createInterface({ input: process.stdin }); let sessionId; let stage = "input"; const abort = new AbortController(); async function request(path, method = "GET", body) { const response = await fetch(base + path, { method, redirect: "error", signal: AbortSignal.timeout(30_000), headers: { Origin: base, ...(body === undefined ? {} : { "Content-Type": "application/json" }) }, ...(body === undefined ? {} : { body: JSON.stringify(body) }), }); const result = await response.json(); if (!response.ok) { const error = new Error("API request failed"); error.code = /^[A-Z_]{1,80}$/.test(result.error?.code ?? "") ? result.error.code : "API_REQUEST_FAILED"; throw error; } return result; } console.log("Provide CMS email/password as one JSON line on hidden stdin."); try { const input = JSON.parse((await reader[Symbol.asyncIterator]().next()).value); reader.close(); stage = "discovery"; const catalog = await request("/api/skills"); assert(catalog.skills.some((skill) => skill.name === "swads-short-drama-editor")); ({ sessionId } = await request("/api/sessions", "POST")); stage = "login"; let login; try { login = await request(`/api/sessions/${sessionId}/cms/connection`, "POST", { email: input.email, password: input.password }); } finally { input.email = ""; input.password = ""; } assert.equal(login.connection.connected, true); stage = "catalog"; const dramas = await request(`/api/sessions/${sessionId}/cms/dramas`); assert(dramas.dramas.length > 0); console.log(JSON.stringify({ stage, connected: true, dramaCount: dramas.dramas.length, skillDiscovered: true })); stage = "model"; const timeout = setTimeout(() => abort.abort(), 150_000); const stream = await fetch(`${base}/api/sessions/${sessionId}/events`, { signal: abort.signal }); const streamReader = stream.body.getReader(); const sent = await request(`/api/sessions/${sessionId}/messages`, "POST", { text: "做 swads-short-drama-editor 接入验收:先完整读取该 Skill 的 SKILL.md 和 references/cms-source-contract.md,然后调用 cms_connection_status。不要读取 CMS 剧集、字幕、任何业务数据,不要调用其他工具或生成文件。最后用中文简短说明已经连接和仍然缺失的能力,不要声称成片功能已经完成。", attachmentIds: [], }); let pending = ""; let terminal = false; let skillLoaded = false; let referenceRead = false; const referenceCalls = new Set(); let statusCompleted = false; let answerReceived = false; const decoder = new TextDecoder(); try { while (!terminal) { const { value, done } = await streamReader.read(); assert(!done, "Unexpected stream end"); pending += decoder.decode(value, { stream: true }); let boundary; while ((boundary = pending.indexOf("\n\n")) !== -1) { const frame = pending.slice(0, boundary); pending = pending.slice(boundary + 2); const data = frame.split("\n").find((line) => line.startsWith("data: "))?.slice(6); if (!data) continue; const event = JSON.parse(data); if (event.runId !== sent.runId) continue; assert.notEqual(event.type, "approval.required", "Unexpected content access request"); if (event.type === "skill.loaded") skillLoaded = true; if (event.type === "tool.completed" && event.payload.toolName === "cms_connection_status") { const result = event.payload.result?.details ?? event.payload.result; statusCompleted = result?.connected === true && result?.capabilities?.rendering === false; } if (event.type === "tool.requested" && event.payload.toolName === "read" && JSON.stringify(event.payload.arguments).includes("references/cms-source-contract.md")) referenceCalls.add(event.payload.toolCallId); if (event.type === "tool.completed" && referenceCalls.has(event.payload.toolCallId)) referenceRead = true; if (event.type === "assistant.delta" && event.payload.delta?.trim()) answerReceived = true; if (["run.completed", "run.failed", "run.cancelled"].includes(event.type)) { console.log(JSON.stringify({ terminal: event.type, skillLoaded, referenceRead, statusCompleted, answerReceived })); assert.equal(event.type, "run.completed"); terminal = true; } } } assert(skillLoaded && referenceRead && statusCompleted && answerReceived, "Required Skill/tool events missing"); console.log(JSON.stringify({ result: "PASS", skillLoaded, referenceRead, statusCompleted, answerReceived, cmsContentSentToModel: false, videoPipelineVerified: false })); } finally { clearTimeout(timeout); abort.abort(); } } catch (error) { console.error(JSON.stringify({ result: "FAIL", stage, code: error.code ?? "ASSERTION_OR_NETWORK_FAILURE" })); process.exitCode = 1; } finally { reader.close(); abort.abort(); if (sessionId) { await request(`/api/sessions/${sessionId}/cancel`, "POST").catch(() => {}); await request(`/api/sessions/${sessionId}/cms/connection`, "DELETE").catch(() => {}); await request(`/api/sessions/${sessionId}`, "DELETE").catch(() => {}); } }