@plumbus/browser-extension
v0.1.2
Published
Plumbus browser-extension scaffolder — emit wxt Chrome/Firefox extensions wired to capabilities
Readme
@plumbus/browser-extension
Ship a Chrome/Firefox extension wired to your Plumbus capabilities. Scaffold a WXT extension that authenticates with a bearer token and calls your hosted backend through an explicit, type-checked capability registry — with no runtime change to
@plumbus/coreor@plumbus/ui.
What is this?
Plumbus is an AI-native, contract-driven TypeScript application framework. You declare your app's primitives — capabilities (queries/actions/jobs/event handlers), entities, events, flows, prompts, translations — through define*() functions. The framework gives you HTTP routes, Zod validation, deny-by-default security, audit, governance, and AI integration out of the box.
@plumbus/browser-extension is a dev-time scaffolder for that framework. Point it at your app and it emits a ready-to-build WXT Chrome/Firefox extension wired to your capabilities. The extension is a thin authenticated client — capabilities still execute server-side on your hosted Plumbus backend.
Like @plumbus/ui, it is a generator: the code it emits is owned by your app and does not depend on @plumbus/browser-extension at runtime. If you're not using Plumbus, this package can't be used in isolation.
Why?
A browser extension can't talk to your backend the way a same-origin web app does — there's no relative API origin, no SPA navigation, and no cross-site cookies from a chrome-extension:// page. Hand-rolling that means writing:
- A bearer-token store backed by
browser.storage.local(extensions can't uselocalStoragecookies) - A background message router so popup/content scripts never touch the token directly
- A typed client with absolute URLs and
Authorizationinjection host_permissions+ CORS that match your API origin- A Chrome and Firefox build setup
This package generates all of it, correctly, from your existing capability contracts. One scaffold command, two browsers, your real types.
What you get
| File / surface | What it does |
|---|---|
| entrypoints/popup/ (React) | Login form + a sample capability call against a zero-input query |
| entrypoints/background.ts | Message router + explicit capability registry; attaches Authorization; one-shot refresh-and-retry on 401 |
| entrypoints/content.ts | Stub showing how a content script invokes a capability via invoke() — never reads the token directly |
| src/auth-store.ts | AuthState over browser.storage.local — login token, headers, single-flight refreshAuth, logout |
| src/invoke.ts | Typed popup/content → background bridge; validates the response envelope |
| src/client/api.ts | Typed fetch client generated by @plumbus/ui with absolute URLs — always regenerated |
| wxt.config.ts | Manifest function: storage + API origin in host_permissions (Chrome MV3) or permissions (Firefox MV2) |
| package.json | WXT scripts for Chrome + Firefox (dev:*, build:*, zip:*) |
extension/
├── wxt.config.ts
├── package.json # dev:chrome / dev:firefox / build:* / zip:*
├── tsconfig.json
├── .env.example
├── entrypoints/
│ ├── background.ts # capability registry + Authorization + 401 refresh
│ ├── content.ts
│ └── popup/ # index.html · main.tsx · App.tsx
└── src/
├── auth-store.ts # browser.storage.local
├── invoke.ts
└── client/api.ts # always regenerated — do not editStatus
Optional dev-time scaffolder, version-locked 0.1.x. Peer @plumbus/core at ^0.5.0 <0.6.0; pairs with @plumbus/ui (which supplies the generated typed client). The generated extension is app-owned and has no runtime dependency on this package.
Out of scope (the scaffold deliberately does not invent these): OAuth/cookie auth, cross-site cookies, refresh-token rotation, and offline capability execution.
Install
pnpm add @plumbus/ui @plumbus/browser-extensionBoth are dev-time peers of @plumbus/core — apps that don't ship an extension install neither. The CLI command lives in @plumbus/core and dynamically loads both packages; it prints pnpm add @plumbus/ui @plumbus/browser-extension if either is missing.
Quick start
1. Scaffold
From your Plumbus app root:
plumbus browser-extension scaffold ./extension --app-name my-app --api-base-url https://api.example.com| Option | Default | Description |
|---|---|---|
| output-dir (positional) | extension | Where to write the project |
| --app-name <name> | inferred from nearest package.json | Extension display name |
| --api-base-url <url> | — | Required. Absolute http:/https: API base URL |
| --browser <target> | both | chrome, firefox, or both — controls which dev:* / build:* scripts are emitted |
| --force | false | Overwrite hand-editable shell files (local edits may be lost) |
| --json | false | Machine-readable result on stdout |
Re-run the same command after capability changes — src/client/api.ts is always regenerated; the shell files are skipped unless --force.
2. Build and run
cd extension
pnpm install
pnpm dev:chrome # or pnpm dev:firefoxLoad the unpacked build from .output/ (WXT prints the path).
3. Wire the app-owned prerequisites
Plumbus core does not provide auth routes or CORS — your API host owns them. Before the extension can log in and call capabilities, the host must serve /api/auth/login (and optional refresh/logout), allow the extension origin via CORS, and apply access policies that admit authenticated JWT callers. Full contract + dev/production CORS examples: see Documentation.
How it works
popup login ──fetch──▶ ${apiBaseUrl}/api/auth/login
│ token + user │
└──────────────────────────▶ browser.storage.local
▲
popup / content ─invoke(key,input)─▶ background service worker
│ authHeaders() reads token
│ capabilityRegistry[key] ← explicit, generated
▼
client.fn(input, { Authorization: Bearer … })
│
▼
${apiBaseUrl}/api/<domain>/<capability>
│
200 ─▶ { ok: true, data }
401 ─▶ refresh once ─▶ retry ─▶ else { ok: false, code: 'AUTH_EXPIRED' }Content scripts only ever call invoke(); the token lives behind the background worker.
Public API
The primary entry point is the CLI (plumbus browser-extension scaffold, shipped in @plumbus/core). These exports are for custom tooling that drives generation directly:
| Export | Purpose |
|---|---|
| generateBrowserExtensionScaffold(input) | Emit all shell files for a scaffold — returns GeneratedFile[]. |
| selectSampleCapability(capabilities) | Pick the first zero-input query for the popup's working sample (stable name order). |
| hostPermission(apiBaseUrl) | Origin-only host-permission string, e.g. https://api.example.com/*. |
| apiOrigin(apiBaseUrl) | Normalize a base URL to its origin. |
| INVOKE_MESSAGE_TYPE | Message-type constant for the popup/content → background bridge. |
| BrowserExtensionScaffoldConfig, BrowserExtensionScaffoldInput, RegistryEntry, FlowTriggerInput, GeneratedFile | Type surface. |
Key gotchas
- Plumbus core does not provide
/api/auth/*or CORS. Those are app/BFF-owned. The scaffold assumes a host that serves them. - The token in
browser.storage.localis not encrypted — readable via the browser profile/devtools. Standard for bearer-token extensions; don't store anything you wouldn't put in an API key. - Deny-by-default applies. Capabilities invoked from the extension need access policies that admit authenticated JWT callers, or calls fail authorization even on a valid scaffold.
- API host access is origin-only (
https://host/*). Chrome MV3 useshost_permissions; Firefox MV2 uses the same pattern inpermissions(WXT does not translate between them). The generated client keeps the full--api-base-urlpath segment separately. src/client/api.tsis always regenerated — do not edit it. The hand-editable shell files (popup, background, auth-store, …) are overwrite-protected unless you pass--force.@plumbus/uimust be installed alongside this package — the CLI loads both to generate the typed client.
Documentation
- CLI reference (in the monorepo):
docs/cli/commands.md— theplumbus browser-extension scaffoldcommand, options, and output. - Agent recipe (ships in this package, readable from
node_modules/@plumbus/browser-extension/instructions/):instructions/browser-extension.md— the full auth contract, dev/production CORS examples, access-policy prerequisites, and the token-storage security note.
The Plumbus ecosystem
| Package | Purpose | When to install |
|---|---|---|
| @plumbus/core | Foundation — capabilities, entities, events, flows, prompts, translations, runtime, CLI, audit, governance. | Always (required). |
| @plumbus/ui | Next.js/React UI — typed API clients, auth helpers, form metadata, scaffolds. | When building a Plumbus web UI (required alongside this package). |
| @plumbus/api | Partner external API — manifest, OpenAPI, docs, compatibility diff, test intent. | Optional peer 0.1.x — when publishing a documented partner-facing HTTP API. |
| @plumbus/mcp | MCP runtime — serve capabilities to AI agents (tools/*, tasks/*, transports). | Optional peer 0.5.x — when exposing capabilities to MCP clients. |
| @plumbus/chat | Conversational runtime — defineChat, policy guards, context sources, streamed events. | Optional peer 0.1.x — when adding a chat surface. |
| @plumbus/chat-ui | React chat UI — hooks and <ChatPanel /> for the @plumbus/chat turn protocol. | Peer of @plumbus/chat — when adding a browser chat client. |
| @plumbus/knowledge-base | Knowledge providers — scoped sources, registry, chat knowledgeContext integration. | Optional peer of @plumbus/chat 0.1.x — when sharing named knowledge across features. |
| @plumbus/browser-extension | You are here. Extension scaffolder — WXT Chrome/Firefox project wired to your capabilities. | With @plumbus/ui (0.1.x) — when shipping a browser extension UI. |
Links
- Plumbus framework — github.com/plumbus-framework/plumbus
- Full documentation — docs/ in the monorepo
- Top-level README —
../../README.md - WXT — wxt.dev
- Issues — github.com/plumbus-framework/plumbus/issues
License
MIT
