Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions extension/build.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/**
* Build script for the Zero Companion MV3 extension.
*
* Bundles `src/worker.ts` into a single classic service-worker script and emits
* a generated module the `zero` CLI bundles in — `zero/src/companion/
* extension-assets.ts` — mapping each extension filename to its contents. At
* runtime `bridge-engine.ts` writes those files (plus a per-run `bridge.json`)
* into `~/.zero/extension/`, which `zero browser connect` hands to Chrome via
* `--load-extension`. Embedding the built extension in the CLI bundle keeps it
* self-contained — no assumptions about the installer's on-disk layout.
*
* Run with `bun build.ts` (preferred) or via esbuild fallback.
*/
import { mkdir, readFile, writeFile } from "node:fs/promises";
import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";

const __dirname = dirname(fileURLToPath(import.meta.url));
const root = __dirname;
const manifestPath = resolve(root, "manifest.json");
const workerEntry = resolve(root, "src/worker.ts");
const assetsOut = resolve(root, "..", "zero", "src", "companion", "extension-assets.ts");

async function bundleWorker(): Promise<string> {
if (typeof (globalThis as any).Bun !== "undefined") {
const Bun = (globalThis as any).Bun;
const out = await Bun.build({
entrypoints: [workerEntry],
target: "browser",
format: "iife",
minify: false,
});
if (!out.success) throw new AggregateError(out.logs, "worker bundle failed");
return await out.outputs[0].text();
}
const esbuild = await import("esbuild");
const res = await esbuild.build({
entryPoints: [workerEntry],
bundle: true,
write: false,
platform: "browser",
target: "chrome116",
format: "iife",
});
return res.outputFiles![0]!.text;
}

const workerJs = await bundleWorker();
const manifestJson = await readFile(manifestPath, "utf8");

const assets: Record<string, string> = {
"manifest.json": manifestJson,
"worker.js": workerJs,
};

const banner = "// AUTO-GENERATED by extension/build.ts — do not edit by hand.\n" +
"// Run `cd extension && bun build.ts` to regenerate.\n";
const body = `export const EXTENSION_ASSETS: Record<string, string> = ${JSON.stringify(assets, null, 2)};\n`;

await mkdir(dirname(assetsOut), { recursive: true });
await writeFile(assetsOut, banner + body);
console.log(`extension: built worker (${workerJs.length} bytes) → ${assetsOut}`);
11 changes: 11 additions & 0 deletions extension/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"manifest_version": 3,
"name": "Zero Companion",
"version": "1.0.0",
"description": "Lets the Zero agent drive a tab in your real Chrome — your logins, your session, no separate browser.",
"permissions": ["debugger", "tabs", "scripting", "activeTab", "alarms"],
"host_permissions": ["<all_urls>"],
"background": {
"service_worker": "worker.js"
}
}
Loading
Loading