feat: add durable CMS-free image video subsystem and UI components
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import * as harness from "../src/index.js";
|
||||
|
||||
describe("image video remote boundary", () => {
|
||||
it("rejects incompatible live tool input types and new required fields before calling them", () => {
|
||||
expect(harness).toHaveProperty("assertImageVideoArguments");
|
||||
const schema = { type: "object", properties: { duration_seconds: { type: "number" }, reference_asset_ids: { anyOf: [{ type: "array", items: { type: "string" } }, { type: "null" }] } }, required: ["duration_seconds"] };
|
||||
expect(() => harness.assertImageVideoArguments(schema, { duration_seconds: 10, reference_asset_ids: ["asset_1"] })).not.toThrow();
|
||||
expect(() => harness.assertImageVideoArguments(schema, { duration_seconds: "10" })).toThrow();
|
||||
expect(() => harness.assertImageVideoArguments(schema, { duration_seconds: 10, external_account_id: "unapproved" })).toThrow();
|
||||
expect(() => harness.assertImageVideoArguments({ ...schema, required: ["new_field"] }, { duration_seconds: 10 })).toThrow();
|
||||
});
|
||||
it("accepts signed HTTPS queries privately but rejects credentials, private DNS and localhost", async () => {
|
||||
expect(harness).toHaveProperty("validateImageVideoUrl");
|
||||
const result = await harness.validateImageVideoUrl("https://cdn.example/video?token=private", () => Promise.resolve([{ address: "8.8.8.8", family: 4 }]));
|
||||
expect(result.url.search).toBe("?token=private"); expect(result.address).toBe("8.8.8.8");
|
||||
for (const url of ["http://cdn.example/v", "https://user:pass@cdn.example/v", "https://127.0.0.1/v", "https://[::ffff:127.0.0.1]/v"]) await expect(harness.validateImageVideoUrl(url)).rejects.toBeDefined();
|
||||
await expect(harness.validateImageVideoUrl("https://cdn.example/v", () => Promise.resolve([{ address: "192.168.1.2", family: 4 }]))).rejects.toBeDefined();
|
||||
});
|
||||
it("extracts only the documented response envelope and rejects malformed or error responses", () => {
|
||||
expect(harness).toHaveProperty("imageVideoResponse");
|
||||
expect(harness.imageVideoResponse({ content: [{ type: "text", text: '{"job_id":"j1"}' }] })).toEqual({ job_id: "j1" });
|
||||
for (const value of [{ isError: true, content: [{ type: "text", text: "token=private" }] }, { content: [{ type: "text", text: "not JSON" }] }, { structuredContent: [] }]) expect(() => harness.imageVideoResponse(value)).toThrow();
|
||||
});
|
||||
it("refuses a false-duration or incomplete technical validation instead of releasing a video", () => {
|
||||
expect(harness).toHaveProperty("parseImageVideoValidation");
|
||||
const result = { video_seconds: 10, audio_seconds: 10, width: 720, height: 1280, fps: "24", decoded: true, sha256: "a".repeat(64) };
|
||||
expect(harness.parseImageVideoValidation(result, 10).visualReview).toBe("pending");
|
||||
for (const changed of [{ video_seconds: 7.292 }, { audio_seconds: 0 }, { decoded: false }, { width: 1280, height: 720 }, { fps: "0" }]) expect(() => harness.parseImageVideoValidation({ ...result, ...changed }, 10)).toThrow();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,12 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import * as harness from "../src/index.js";
|
||||
describe("image video tool permissions", () => {
|
||||
it("separates local draft/status from separately approved upload and generation", () => {
|
||||
expect(harness.decideToolPolicy("i2v_create_task")).toBe("allow");
|
||||
expect(harness.decideToolPolicy("i2v_status")).toBe("allow");
|
||||
expect(harness.decideToolPolicy("i2v_upload_reference")).toBe("ask");
|
||||
expect(harness.decideToolPolicy("i2v_submit")).toBe("ask");
|
||||
expect(harness.decideToolPolicy("video_h3_submit")).toBe("deny");
|
||||
expect(harness.decideToolPolicy("creative_upload_from_url")).toBe("deny");
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,33 @@
|
||||
import { EventEmitter } from "node:events";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
const response = vi.hoisted(() => ({ status: 200, type: "video/mp4", body: Buffer.from("mock-MP4"), connectedAddress: "" }));
|
||||
vi.mock("node:dns/promises", () => ({ lookup: () => Promise.resolve([{ address: "8.8.8.8", family: 4 }]) }));
|
||||
vi.mock("node:https", () => ({ request: (_url: URL, options: { family?: number; lookup: (name: string, options: { all?: boolean }, callback: (error: Error | null, addresses: string | { address: string }[]) => void) => void }, receive: (value: EventEmitter) => void) => {
|
||||
const req = new EventEmitter() as EventEmitter & { end: () => void; destroy: (error?: Error) => void };
|
||||
let destroyed = false;
|
||||
req.destroy = (error) => { destroyed = true; if (error) queueMicrotask(() => req.emit("error", error)); };
|
||||
req.end = () => queueMicrotask(() => {
|
||||
// Node 22 requests all addresses unless a concrete address family is supplied.
|
||||
const all = options.family !== 4 && options.family !== 6;
|
||||
options.lookup("cdn.example", { all }, (error, addresses) => {
|
||||
if (error || (all && !Array.isArray(addresses))) { req.destroy(new Error("Invalid IP callback shape")); return; }
|
||||
response.connectedAddress = typeof addresses === "string" ? addresses : addresses[0]?.address ?? "";
|
||||
const incoming = Object.assign(new EventEmitter(), { statusCode: response.status, headers: { "content-type": response.type, "content-length": response.body.length }, destroy() {} });
|
||||
receive(incoming); if (!destroyed) { incoming.emit("data", response.body); incoming.emit("end"); }
|
||||
});
|
||||
});
|
||||
return req;
|
||||
} }));
|
||||
import { imageVideoTransfer } from "../src/image-video/media.js";
|
||||
describe("bounded pinned HTTPS media transfer", () => {
|
||||
it("connects with the checked public IP using Node's actual lookup callback contract", async () => {
|
||||
response.status = 200; response.type = "video/mp4";
|
||||
expect(await imageVideoTransfer("https://cdn.example/video?token=private")).toEqual(Buffer.from("mock-MP4"));
|
||||
expect(response.connectedAddress).toBe("8.8.8.8");
|
||||
});
|
||||
it("rejects redirects, HTML masquerading as a video and oversized bodies", async () => {
|
||||
response.status = 302; await expect(imageVideoTransfer("https://cdn.example/video")).rejects.toThrow();
|
||||
response.status = 200; response.type = "text/html"; await expect(imageVideoTransfer("https://cdn.example/video")).rejects.toThrow();
|
||||
response.type = "video/mp4"; await expect(imageVideoTransfer("https://cdn.example/video", { maxBytes: 2 })).rejects.toThrow();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,98 @@
|
||||
import { mkdtemp, mkdir, readFile, writeFile } from "node:fs/promises";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { Readable } from "node:stream";
|
||||
import sharp from "sharp";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import * as harness from "../src/index.js";
|
||||
import type { ImageVideoRemote, ImageVideoRecord } from "../src/image-video/contracts.js";
|
||||
|
||||
async function fixture() {
|
||||
expect(harness).toHaveProperty("ImageVideoService");
|
||||
const root = await mkdtemp(join(tmpdir(), "i2v-test-"));
|
||||
await mkdir(join(root, "ses_owner"));
|
||||
const attachments = new harness.FileAttachmentStore({ sessionsRoot: root });
|
||||
const attachment = await attachments.add("ses_owner", { name: "test.png", mediaType: "image/png", stream: Readable.from(await sharp({ create: { width: 8, height: 8, channels: 3, background: "red" } }).png().toBuffer()) });
|
||||
const calls: string[] = [];
|
||||
const remote: ImageVideoRemote = {
|
||||
preflight() { calls.push("identity"); return Promise.resolve({ tenantId: "tenant", platform: "tiktok" }); },
|
||||
initialize() { calls.push("initialize"); return Promise.resolve({ uploadId: "upload_1", objectKey: "obj", uploadUrl: "https://public.example/put?token=private" }); },
|
||||
put() { calls.push("put"); return Promise.resolve(); }, finalize() { calls.push("finalize"); return Promise.resolve(); },
|
||||
importStatus() { calls.push("import"); return Promise.resolve({ state: "ready", assetId: "asset_1" }); },
|
||||
submit() { calls.push("submit"); return Promise.resolve({ jobId: "remote_job_1" }); },
|
||||
status() { calls.push("status"); return Promise.resolve({ state: "queued" }); },
|
||||
};
|
||||
const options = { root: join(root, "tasks"), attachments, remote, collect: () => Promise.reject(new Error("No fake video")) };
|
||||
const service = new harness.ImageVideoService(options);
|
||||
const draft = { attachmentId: attachment.attachmentId, title: "Motion", prompt: "Natural movement with ambient sound", durationSeconds: 10 };
|
||||
return { service, options, draft, calls, remote, root };
|
||||
}
|
||||
|
||||
describe("durable image video tasks", () => {
|
||||
it("copies a local reference without transmitting it and hides private bindings", async () => {
|
||||
const f = await fixture(); const task = await f.service.create("ses_owner", f.draft);
|
||||
expect(f.calls).toEqual([]); expect(task.state).toBe("draft");
|
||||
await f.options.attachments.removeAll("ses_owner");
|
||||
expect((await f.service.media(task.id, "reference")).size).toBeGreaterThan(0);
|
||||
expect(JSON.stringify(await f.service.list())).not.toMatch(/ownerSessionId|uploadUrl|objectKey|referenceHash|\/private\//);
|
||||
});
|
||||
it("rejects foreign owners before any external operation", async () => {
|
||||
const f = await fixture(); const task = await f.service.create("ses_owner", f.draft);
|
||||
await expect(f.service.upload("ses_other", task.id)).rejects.toMatchObject({ code: "I2V_TASK_NOT_FOUND" });
|
||||
await expect(f.service.submit("ses_other", task.id)).rejects.toMatchObject({ code: "I2V_TASK_NOT_FOUND" });
|
||||
expect(f.calls).toEqual([]);
|
||||
});
|
||||
it("requires upload first and coalesces concurrent submission, restoring only the known job", async () => {
|
||||
const f = await fixture(); const task = await f.service.create("ses_owner", f.draft);
|
||||
await expect(f.service.submit("ses_owner", task.id)).rejects.toMatchObject({ code: "I2V_NOT_READY" });
|
||||
await f.service.upload("ses_owner", task.id); await f.service.refresh(task.id);
|
||||
const [a, b] = await Promise.all([f.service.submit("ses_owner", task.id), f.service.submit("ses_owner", task.id)]);
|
||||
expect(a.jobId).toBe("remote_job_1"); expect(b.jobId).toBe(a.jobId);
|
||||
const restored = new harness.ImageVideoService(f.options);
|
||||
expect((await restored.view(task.id)).jobId).toBe(a.jobId);
|
||||
await restored.submit("ses_owner", task.id);
|
||||
expect(f.calls.filter(call => call === "submit")).toHaveLength(1);
|
||||
expect(JSON.stringify(await restored.view(task.id))).not.toContain("private");
|
||||
});
|
||||
it("isolates unknown submissions after timeout and after process interruption", async () => {
|
||||
const f = await fixture(); const task = await f.service.create("ses_owner", f.draft);
|
||||
await f.service.upload("ses_owner", task.id); await f.service.refresh(task.id);
|
||||
f.remote.submit = () => { f.calls.push("submit"); return Promise.reject(new Error("token=private")); };
|
||||
expect((await f.service.submit("ses_owner", task.id)).state).toBe("unknown_submission");
|
||||
await expect(new harness.ImageVideoService(f.options).submit("ses_owner", task.id)).rejects.toMatchObject({ code: "I2V_UNKNOWN_SUBMISSION" });
|
||||
expect(f.calls.filter(call => call === "submit")).toHaveLength(1);
|
||||
const file = join(f.options.root, task.id, "record.json"); const record = JSON.parse(await readFile(file, "utf8")) as ImageVideoRecord;
|
||||
record.state = "submitting"; await writeFile(file, JSON.stringify(record));
|
||||
expect((await new harness.ImageVideoService(f.options).view(task.id)).state).toBe("unknown_submission");
|
||||
});
|
||||
it("never reinitializes uncertain uploads and blocks stale persisted revisions", async () => {
|
||||
const f = await fixture(); const task = await f.service.create("ses_owner", f.draft);
|
||||
f.remote.initialize = () => { f.calls.push("initialize"); return Promise.reject(new Error("timeout")); };
|
||||
expect((await f.service.upload("ses_owner", task.id)).state).toBe("unknown_upload");
|
||||
await expect(f.service.upload("ses_owner", task.id)).rejects.toMatchObject({ code: "I2V_UNKNOWN_UPLOAD" });
|
||||
expect(f.calls.filter(call => call === "initialize")).toHaveLength(1);
|
||||
const file = join(f.options.root, task.id, "record.json"); const record = JSON.parse(await readFile(file, "utf8")) as ImageVideoRecord;
|
||||
record.requestHash = "invalid"; await writeFile(file, JSON.stringify(record));
|
||||
await expect(f.service.view(task.id)).rejects.toMatchObject({ code: "I2V_RECORD_INVALID" });
|
||||
});
|
||||
it("throttles read-only polling even across instances and accepts no arbitrary identifiers", async () => {
|
||||
const f = await fixture(); const task = await f.service.create("ses_owner", f.draft);
|
||||
await f.service.upload("ses_owner", task.id); await f.service.refresh(task.id); await f.service.submit("ses_owner", task.id);
|
||||
await Promise.all([f.service.refresh(task.id), new harness.ImageVideoService(f.options).refresh(task.id)]);
|
||||
expect(f.calls.filter(call => call === "status")).toHaveLength(1);
|
||||
await expect(f.service.view("../../etc/passwd")).rejects.toMatchObject({ code: "I2V_TASK_NOT_FOUND" });
|
||||
});
|
||||
it("rejects a late submit result from overwriting a newer revision, while retaining its known receipt", async () => {
|
||||
const f = await fixture(); const task = await f.service.create("ses_owner", f.draft);
|
||||
await f.service.upload("ses_owner", task.id); await f.service.refresh(task.id);
|
||||
f.remote.submit = async () => {
|
||||
const file = join(f.options.root, task.id, "record.json"); const newer = JSON.parse(await readFile(file, "utf8")) as ImageVideoRecord;
|
||||
newer.revision += 1; newer.state = "unknown_submission"; await writeFile(file, JSON.stringify(newer));
|
||||
return { jobId: "late_job" };
|
||||
};
|
||||
await expect(f.service.submit("ses_owner", task.id)).rejects.toMatchObject({ code: "I2V_STALE_REVISION" });
|
||||
expect((await f.service.view(task.id)).state).toBe("unknown_submission");
|
||||
const receipt = JSON.parse(await readFile(join(f.options.root, task.id, "submission-receipt.json"), "utf8")) as { jobId: string };
|
||||
expect(receipt.jobId).toBe("late_job");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user