repositories / pi-ext
pi-ext
bugabingas pi extensions
owned by admin
extensions/ephemlets/__serial__/package.test.ts
Rawimport { 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,
);