Implement attachment analysis task flow

This commit is contained in:
kris
2026-03-29 16:21:05 +08:00
parent 8273340f7f
commit 9e4b64ba9e
5 changed files with 453 additions and 5 deletions

View File

@@ -0,0 +1,141 @@
#!/usr/bin/env node
import assert from "node:assert/strict";
import fs from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { createRequire } from "node:module";
const rootDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
const runtimeDir = await fs.mkdtemp(path.join(os.tmpdir(), "boss-attachment-analysis-"));
const stateFile = path.join(runtimeDir, "data", "boss-state.json");
const require = createRequire(import.meta.url);
process.env.BOSS_RUNTIME_ROOT = runtimeDir;
process.env.BOSS_STATE_FILE = stateFile;
process.env.BOSS_AUTH_AUTO_LOGIN = "0";
const { NextRequest } = require("next/server");
const authLoginRoute = require(path.join(rootDir, ".next/standalone/.next/server/app/api/auth/login/route.js"));
const attachmentsRoute = require(
path.join(rootDir, ".next/standalone/.next/server/app/api/v1/projects/[projectId]/attachments/route.js"),
);
const analyzeRoute = require(
path.join(
rootDir,
".next/standalone/.next/server/app/api/v1/projects/[projectId]/attachments/[attachmentId]/analyze/route.js",
),
);
const loginHandler = authLoginRoute.routeModule.userland.POST;
const uploadHandler = attachmentsRoute.routeModule.userland.POST;
const analyzeHandler = analyzeRoute.routeModule.userland.POST;
async function invokeRoute(handler, url, init = {}, context) {
const request = new NextRequest(url, {
method: init.method ?? "GET",
headers: init.headers,
body: init.body,
});
return handler(request, context);
}
function parseCookieValue(setCookieHeader, cookieName) {
assert.ok(setCookieHeader, "set-cookie header is missing");
const match = setCookieHeader.match(new RegExp(`${cookieName}=([^;]+)`));
assert.ok(match, `${cookieName} cookie is missing`);
return match[1];
}
async function loginAsAdmin() {
const response = await invokeRoute(
loginHandler,
"http://localhost/api/auth/login",
{
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({
account: "17600003315",
password: "boss123456",
method: "password",
}),
},
);
assert.equal(response.status, 200, "login should succeed");
const payload = await response.json();
assert.equal(payload.ok, true, "login payload should be ok");
const cookie = parseCookieValue(response.headers.get("set-cookie"), "boss_session");
return { cookie, payload };
}
async function uploadAttachment(cookie, projectId, fileName, type, bytes) {
const form = new FormData();
form.set("file", new File([bytes], fileName, { type }));
const response = await invokeRoute(
uploadHandler,
`http://localhost/api/v1/projects/${projectId}/attachments`,
{
method: "POST",
headers: { cookie: `boss_session=${cookie}` },
body: form,
},
{ params: Promise.resolve({ projectId }) },
);
assert.equal(response.status, 200, `upload ${fileName} should succeed`);
return response.json();
}
const { cookie } = await loginAsAdmin();
const textUpload = await uploadAttachment(
cookie,
"master-agent",
"analysis-note.txt",
"text/plain",
Buffer.from("text attachment for automatic analysis"),
);
assert.equal(textUpload.attachment.analysisState, "queued_auto", "text attachment should queue automatically");
assert.ok(textUpload.analysisTask, "queued auto attachment should create a master agent task");
assert.equal(textUpload.analysisTask.taskType, "attachment_analysis", "queued task type should be attachment_analysis");
assert.equal(
textUpload.analysisTask.attachmentFileName,
"analysis-note.txt",
"queued task should carry attachment file name",
);
const manualUpload = await uploadAttachment(
cookie,
"master-agent",
"manual-binary.bin",
"application/octet-stream",
Buffer.from([0, 1, 2, 3]),
);
assert.equal(manualUpload.attachment.analysisState, "ready_manual", "binary attachment should be manually analyzable");
const analyzeResponse = await invokeRoute(
analyzeHandler,
`http://localhost/api/v1/projects/master-agent/attachments/${manualUpload.attachment.attachmentId}/analyze`,
{
method: "POST",
headers: { cookie: `boss_session=${cookie}` },
},
{
params: Promise.resolve({
projectId: "master-agent",
attachmentId: manualUpload.attachment.attachmentId,
}),
},
);
assert.equal(analyzeResponse.status, 200, "manual analyze should succeed");
const analyzePayload = await analyzeResponse.json();
assert.ok(analyzePayload.taskId, "manual analyze should return a taskId");
assert.ok(analyzePayload.task, "manual analyze should return a task payload");
assert.equal(analyzePayload.task.taskType, "attachment_analysis", "manual analyze task should be attachment_analysis");
assert.equal(
analyzePayload.task.attachmentId,
manualUpload.attachment.attachmentId,
"manual task should link the attachment",
);
console.log("attachment analysis validation passed");