feat: publish Agent Studio integrations and setup guide
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
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(() => {});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user