import { expect, test } from "bun:test"; import { mkdir, mkdtemp, rm } from "node:fs/promises"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { Host } from "@opencode/plugin/host"; import { z } from "zod"; import { build } from "../scripts/build.js"; import { install } from "../scripts/install.js"; test("packed package has independent native plugins and quota-only TUI discovery", async () => { const directory = await mkdtemp(join(tmpdir(), "plugin-package-")); try { await build(); const archive = join(directory, "stanp-opencode.tgz"); const pack = Bun.spawn([process.execPath, "pm", "pack", "--filename", archive], { stdout: "pipe", stderr: "pipe", }); const output = new Response(pack.stderr).text(); expect(await pack.exited, await output).toBe(0); const unpack = Bun.spawn(["tar", "-xzf", archive, "-C", directory], { stdout: "ignore", stderr: "pipe", }); expect(await unpack.exited).toBe(0); const source = join(directory, "package"); const manifest = z .object({ exports: z.record(z.string(), z.object({ import: z.string() }).loose()) }) .loose() .parse(await Bun.file(join(source, "package.json")).json()); const entrypoint = (name: string) => join(source, z.string().parse(manifest.exports[name]?.import)); expect(manifest.exports["."]).toBeUndefined(); expect(await Bun.file(join(source, "index.js")).exists()).toBe(false); expect(await Bun.file(join(source, "dist", "index.js")).exists()).toBe(false); for (const name of ["agents", "multi-work", "quota"]) { const { default: plugin } = z .object({ default: z.object({ id: z.string() }).loose() }) .parse(await import(entrypoint(`./${name}`))); expect(plugin.id).toBe(`stanp-opencode.${name}`); } const config = join(directory, "config"); await mkdir(config); await install(source, config, "agents,multi-work,quota"); for (const name of ["agents", "multi-work", "quota"]) { const entries = Host.resolve({ directory: join(config, "plugins", "stanp-opencode", name), }); expect(entries.server).toBeDefined(); expect(entries.tui !== undefined).toBe(name === "quota"); } const { QuotaRpc } = z .object({ QuotaRpc: z.object({ id: z.string() }).loose() }) .parse(await import(entrypoint("./quota-rpc"))); expect(QuotaRpc.id).toBe("stanp-opencode.quota"); } finally { await rm(directory, { recursive: true, force: true }); } }, 30_000);