Luigit
repositories / pi-ext

pi-ext

bugabingas pi extensions

owned by admin

extensions/ephemlets/__serial__/package.test.ts

Raw
import { cp, mkdtemp, readFile, rm } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { fileURLToPath } from "node:url";
import { fauxAssistantMessage, fauxToolCall } from "@earendil-works/pi-ai";
import type { ExtensionUIContext } from "@earendil-works/pi-coding-agent";
import { expect, it } from "vitest";
import { createPiSession } from "../../../test/fixture/create-pi-session.ts";

it.skipIf(process.env["PI_EPHEMLETS_PACKAGE_TEST"] !== "1")(
	"relocated package compiles native source and runs delegated builder",
	async () => {
		const cwd = await mkdtemp(join(tmpdir(), "ephemlets-package-"));
		let fixture: Awaited<ReturnType<typeof createPiSession>> | undefined;
		try {
			const target = join(cwd, "relocated");
			await cp(
				fileURLToPath(new URL("../../../dist/ephemlets", import.meta.url)),
				target,
				{ recursive: true },
			);
			const pkg = JSON.parse(
				await readFile(join(target, "package.json"), "utf8"),
			) as {
				pi: { extensions: string[] };
			};
			const entry = pkg.pi.extensions[0];
			if (!entry) throw new Error("Ephemlets Pi extension entry missing");
			fixture = await createPiSession({
				cwd,
				extensionPaths: [join(target, entry)],
			});
			expect(fixture.errors).toEqual([]);
			await fixture.session.bindExtensions({
				mode: "tui",
				uiContext: {
					...fixture.session.extensionRunner.getUIContext(),
					custom: async () => undefined,
				} as ExtensionUIContext,
			});
			const owner = fixture;
			const tool = owner.extensions[0]?.tools.get("ephemlet")?.definition;
			if (!tool) throw new Error("Ephemlet tool missing");
			const call = (params: Record<string, unknown>) =>
				tool.execute(
					"relocated",
					params,
					undefined,
					undefined,
					owner.session.extensionRunner.createToolContext(
						"relocated",
						undefined,
					),
				);
			const source =
				'import { Text } from "@earendil-works/pi-tui"; export default (_ctx: unknown) => new Text("relocated", 0, 0)';
			const put = await call({
				action: "put",
				kind: "interactive",
				title: "Native",
				purpose: "Relocation",
				source,
			});
			expect(put.content[0]?.type).toBe("text");
			const listed = await call({ action: "list" });
			if (listed.content[0]?.type !== "text") throw new Error("No list result");
			const guidance = JSON.parse(listed.content[0].text).guidance as string;
			expect(guidance.startsWith(target)).toBe(true);
			expect(await readFile(guidance, "utf8")).toContain(
				"Native component contract",
			);
			fixture.setResponses([
				fauxAssistantMessage(fauxToolCall("submit_ephemlet", { source }), {
					stopReason: "toolUse",
				}),
			]);
			const built = await call({
				action: "build",
				title: "Delegated",
				purpose: "Relocation",
				brief: "Native Text component",
				wait: true,
			});
			if (built.content[0]?.type !== "text") throw new Error("No build result");
			expect(JSON.parse(built.content[0].text).status).toBe("ready");
		} finally {
			await fixture?.dispose();
			await rm(cwd, { recursive: true, force: true });
		}
	},
	30_000,
);