// Which grant a session starts with, and whether the "● Chrome Bridge" footer announces it, across // /reload and session switches. Pi re-evaluates the extension for each runtime but keeps globalThis // for the life of the process, so each runtime here is a fresh evaluation of the shipped module in // one shared context. The bridge binds an ephemeral loopback port; no Chrome is involved. import assert from "node:assert/strict"; import fs from "node:fs"; import vm from "node:vm"; import { createRequire, stripTypeScriptTypes } from "node:module"; import { test } from "node:test"; const indexUrl = new URL("../../extensions/chrome-profile-bridge/index.ts", import.meta.url); const stubbed = stripTypeScriptTypes(fs.readFileSync(indexUrl, "utf8"), { mode: "transform" }) .replace(/^import \{ Text \} from "@earendil-works\/pi-tui";$/m, "const Text = class {};") .replace(/^import \{ Type \} from "typebox";$/m, "const Type = new Proxy({}, { get: () => (value = {}) => value });") .replace(/^import \{([^}]*)\} from "(node:[^"]+)";$/gm, (_m, names, mod) => `const {${names.replace(/\btype\s+\w+\s*,?/g, "")}} = require("${mod}");`) .replace(/^export default function/m, "module.exports.default = function"); assert.doesNotMatch(stubbed, /^import /m, "every import must be stubbed"); // One pi process: a shared context, so globalThis outlives each runtime. function piProcess(env) { const fakeProcess = Object.create(process, { env: { value: { ...env, PI_CHROME_BRIDGE_PORT: "0" } } }); const context = vm.createContext({ process: fakeProcess, console, Buffer, URL, AbortController, fetch, setTimeout, clearTimeout, setInterval, clearInterval, }); let runtime; // Evaluate the module again and run its factory, as pi does at startup, on /reload and on a // session switch. const load = () => { const module = { exports: {} }; vm.runInContext(`(function (module, exports, require, __dirname) {\n${stubbed}\n})`, context)( module, module.exports, createRequire(indexUrl), new URL(".", indexUrl).pathname, ); const handlers = {}, commands = {}, status = {}; let active = []; const pi = { on: (name, fn) => { handlers[name] = fn; }, registerTool() {}, registerEntryRenderer() {}, appendEntry() {}, registerCommand: (name, command) => { commands[name] = command; }, getActiveTools: () => active, setActiveTools: (tools) => { active = tools; }, }; module.exports.default(pi); const ctx = { cwd: process.cwd(), ui: { setStatus: (key, value) => { status[key] = value; }, notify() {}, confirm: async () => true, theme: { fg: (_color, text) => text }, }, }; runtime = { footer: () => status.chrome, chromeTools: () => active.some((name) => name.startsWith("chrome_")), start: () => handlers.session_start({ type: "session_start" }, ctx), shutdown: (reason) => handlers.session_shutdown({ type: "session_shutdown", reason }), chrome: (args) => commands.chrome.handler(args, ctx), }; return runtime; }; // End the current runtime for `reason`, then start the next one. const replace = async (reason) => { await runtime.shutdown(reason); const next = load(); await next.start(); return next; }; // Stops the bridge and timers of the live runtime, so a failed assertion cannot hang the run. return { load, replace, end: () => runtime?.shutdown("quit") }; } test("PI_CHROME_AUTHORIZE grants every runtime without announcing it", async (t) => { const pi = piProcess({ PI_CHROME_AUTHORIZE: "indefinite" }); t.after(() => pi.end()); const first = pi.load(); await first.start(); assert.equal(first.chromeTools(), true); assert.equal(first.footer(), undefined); for (const reason of ["reload", "new", "resume", "fork"]) { const next = await pi.replace(reason); assert.equal(next.chromeTools(), true, `tools after ${reason}`); assert.equal(next.footer(), undefined, `footer after ${reason}`); } }); test("/chrome authorize survives /reload and ends with its session", async (t) => { const pi = piProcess({}); t.after(() => pi.end()); const first = pi.load(); await first.start(); assert.equal(first.chromeTools(), false); await first.chrome("authorize indefinite"); assert.equal(first.chromeTools(), true); assert.equal(first.footer(), "● Chrome Bridge (indefinite)"); const reloaded = await pi.replace("reload"); assert.equal(reloaded.chromeTools(), true); assert.equal(reloaded.footer(), "● Chrome Bridge (indefinite)"); for (const reason of ["new", "resume", "fork"]) { await (await pi.replace("reload")).chrome("authorize indefinite"); const next = await pi.replace(reason); assert.equal(next.chromeTools(), false, `tools after ${reason}`); assert.equal(next.footer(), undefined, `footer after ${reason}`); } }); test("/chrome authorize over PI_CHROME_AUTHORIZE is announced, and a switch falls back to the env grant", async (t) => { const pi = piProcess({ PI_CHROME_AUTHORIZE: "indefinite" }); t.after(() => pi.end()); const first = pi.load(); await first.start(); await first.chrome("authorize 15m"); assert.match(first.footer(), /^● Chrome Bridge \(\d+m\)$/); const reloaded = await pi.replace("reload"); assert.match(reloaded.footer(), /^● Chrome Bridge \(\d+m\)$/); const next = await pi.replace("new"); assert.equal(next.chromeTools(), true); assert.equal(next.footer(), undefined); });