import { mkdtempSync, readFileSync, rmSync } from "node:fs"; import { tmpdir } from "node:os"; import { resolve } from "node:path"; import { pathToFileURL } from "node:url"; import { getModels } from "@earendil-works/pi-ai/compat"; import { createAgentSession, DefaultResourceLoader, SessionManager, } from "@earendil-works/pi-coding-agent"; import { scriptedProvider } from "./scripted-provider.mjs"; const [scenarioPath] = process.argv.slice(2); if (!scenarioPath) throw new Error("usage: node verify-package.mjs scenario.mjs"); const scenario = await import(pathToFileURL(resolve(scenarioPath)).href); const pkg = JSON.parse(readFileSync("package.json", "utf8")); if (pkg.private || pkg.scripts) throw new Error("distribution package must be public and script-free"); if (!Array.isArray(pkg.files) || !pkg.files.length) throw new Error("distribution package needs explicit npm files"); if (typeof scenario.verify !== "function") throw new Error( "scenario must export verify({ session, provider, package })", ); const cwd = mkdtempSync(`${tmpdir()}/pi-ext-standalone-`); let session; try { const loader = new DefaultResourceLoader({ cwd, agentDir: resolve(cwd, "agent"), noExtensions: true, noSkills: true, noPromptTemplates: true, noThemes: true, noContextFiles: true, additionalExtensionPaths: (pkg.pi?.extensions ?? ["index.js"]).map( (entry) => resolve(entry), ), }); await loader.reload(); if (loader.getExtensions().errors.length) throw new Error(loader.getExtensions().errors.join("\n")); const model = getModels("openai")[0] ?? getModels("anthropic")[0]; if (!model) throw new Error("independently installed Pi has no model catalogue"); ({ session } = await createAgentSession({ cwd, agentDir: resolve(cwd, "agent"), model, resourceLoader: loader, sessionManager: SessionManager.inMemory(cwd), })); await session.bindExtensions({ mode: "print", onError: (error) => { throw error; }, }); await scenario.verify({ session, provider: scriptedProvider, package: pkg }); console.log("PI_EXT_STANDALONE_VERIFY=ok"); } finally { await session?.extensionRunner.emit({ type: "session_shutdown", reason: "quit", }); session?.dispose(); rmSync(cwd, { recursive: true, force: true }); }