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
2 changes: 1 addition & 1 deletion .claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "firefox-cli",
"description": "Firefox control CLI skill for AI agents",
"version": "0.1.1",
"version": "0.2.0",
"repository": "https://github.com/respawn-llc/firefox-cli"
}
63 changes: 0 additions & 63 deletions AMO_SOURCE_REVIEW.md

This file was deleted.

2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ Register the native messaging host:
firefox-cli setup native-host
```

Open the `firefox-cli` extension popup in Firefox and approve the native host. The approval pairs the extension with the local native host and enables CLI requests from the machine.
Run `firefox-cli connect` and respond to the approval request in Firefox. The approval pairs the extension with the local native host and enables CLI requests from the machine.

Verify the installation:

Expand Down
2 changes: 1 addition & 1 deletion biome.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"$schema": "https://biomejs.dev/schemas/2.4.15/schema.json",
"$schema": "https://biomejs.dev/schemas/2.4.16/schema.json",
"vcs": {
"enabled": true,
"clientKind": "git",
Expand Down
341 changes: 179 additions & 162 deletions bun.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docs/all-commands-qa.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ Create `UPLOAD_FILE` with any small text payload. Capture IDs from JSON output w
- [ ] Run `$CLI doctor --json`; expect the disposable extension connection to be `"connected"`.
- [ ] Run `$CLI doctor --fix --json`; expect the manifest status to be healthy and the connection to remain `"connected"`.
- [ ] Run `$CLI capabilities --json`; expect MVP capabilities plus explicit unsupported entries.
- [ ] Run `$CLI connect`; expect an already-approved rejection that identifies the extension instance.
- [ ] Run `$CLI window new "$BASE" --json`; save `WINDOW` and `TAB`.
- [ ] Run `$CLI window --json`; expect `WINDOW` in the window list.
- [ ] Run `$CLI window select "id:$WINDOW" --json`; expect `WINDOW` to be selected.
Expand Down Expand Up @@ -140,7 +141,6 @@ Create `UPLOAD_FILE` with any small text payload. Capture IDs from JSON output w
- [ ] Run `$CLI close`; expect `UNSUPPORTED_CAPABILITY`.
- [ ] Run `$CLI quit`; expect `UNSUPPORTED_CAPABILITY`.
- [ ] Run `$CLI exit`; expect `UNSUPPORTED_CAPABILITY`.
- [ ] Run `$CLI connect`; expect `UNSUPPORTED_CAPABILITY`.
- [ ] Run `$CLI inspect`; expect `UNSUPPORTED_CAPABILITY`.
- [ ] Run `$CLI tab close "id:$TAB3" --json`; expect `TAB3` to close.
- [ ] Run `$CLI tab close "id:$TAB2" --json`; expect `TAB2` to close.
Expand Down
6 changes: 3 additions & 3 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

- CLI: parses commands, prints terminal output, stores local configuration, and sends requests to the native host IPC endpoint.
- Native host: owns Firefox native messaging, local IPC, pair state, auth tokens, native-host manifest setup, and file writes for binary outputs such as screenshots.
- Extension: owns Firefox APIs, popup approval, tab/window targeting, content-script injection, command routing, and browser permission errors.
- Extension: owns Firefox APIs, first-use approval, tab/window targeting, content-script injection, command routing, and browser permission errors.
- Protocol: defines command IDs, request/response schemas, capability metadata, stable errors, and runtime validation.

## Transport
Expand All @@ -15,9 +15,9 @@ Native-host stdout is reserved for Firefox native messaging frames. Human-readab

## Pairing

The first popup approval creates a pair token. The extension stores the token in extension storage; the native host stores a hash and extension identity in user-local state. CLI requests are forwarded only when the connected extension has presented a valid token.
The first approval request creates a pair token. The extension stores the token in extension storage; the native host stores a hash and extension identity in user-local state. CLI requests are forwarded only when the connected extension has presented a valid token.

`firefox-cli unpair` clears native-host pair state. The extension popup can approve again and receive a new token.
`firefox-cli unpair` clears native-host pair state. Run `firefox-cli connect` to request approval again.

## Targeting

Expand Down
4 changes: 3 additions & 1 deletion docs/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ Private windows are listed and readable. Mutating commands against private windo
| `firefox-cli setup` | Print extension setup guidance and the native-host setup command. |
| `firefox-cli setup native-host [--dry-run] [--json]` | Write or print the native messaging manifest. |
| `firefox-cli doctor [--fix] [--json]` | Diagnose native-host manifest and extension connection state. |
| `firefox-cli connect [--json]` | Request Firefox control approval and wait for the user's decision. |
| `firefox-cli unpair` | Clear CLI/native-host pair state. |
| `firefox-cli capabilities [--json]` | List supported and gated protocol capabilities. |

Expand Down Expand Up @@ -158,6 +159,7 @@ Example:
| `firefox-cli network list|clear [--url glob] [--json]` | List or clear observed web requests. |
| `firefox-cli console|errors list|clear [--json]` | List or clear page console/error capture buffers. |
| `firefox-cli highlight <selector\|@ref> [--json]` | Outline an element. |
| `firefox-cli notify [--id id] <title> [message...] [--json]` | Show a native Firefox notification. |
| `firefox-cli set viewport <width> <height> [--json]` | Request a target browser window resize and report Firefox's observed window dimensions. Tiling/window-manager rules can prevent the requested size from taking effect. |
| `firefox-cli diff url|title|snapshot <expected> [--json]` | Compare URL, title, or snapshot text with an expected value. |
| `firefox-cli pdf <path> [--json]` | Returns `UNSUPPORTED_CAPABILITY`; Firefox saves PDFs through a browser dialog rather than a requested CLI path. |
Expand All @@ -166,4 +168,4 @@ Network route/mock/block and HAR export are unsupported.

## Unsupported Families

The CLI returns `UNSUPPORTED_CAPABILITY` for unsupported command families and options, including `screenshot --full`, `pdf`, `connect`, `inspect`, top-level `close`, `quit`, and `exit`.
The CLI returns `UNSUPPORTED_CAPABILITY` for unsupported command families and options, including `screenshot --full`, `pdf`, `inspect`, top-level `close`, `quit`, and `exit`.
39 changes: 39 additions & 0 deletions docs/dependency-migrations.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Dependency Migrations

This page records major dependency migration plans required by the dependency-upgrade policy in `docs/development.md`.

## 2026-06-11 Development Tooling

Scope:

- `eslint` 10 and `@eslint/js` 10.
- `@types/node` 25.
- `typescript` 6.
- `vite` 8.

Release-age evidence as of 2026-06-11:

- `eslint` 10.0.0 was published on 2026-02-06; installed `eslint` 10.4.1 was published on 2026-05-29.
- `@eslint/js` 10.0.1 was published on 2026-02-06.
- `@types/node` 25.0.0 was published on 2025-12-10; installed `@types/node` 25.9.1 was published on 2026-05-19.
- Installed `typescript` 6.0.3 was published on 2026-04-16.
- `vite` 8.0.0 was published on 2026-03-12; installed `vite` 8.0.16 was published on 2026-06-01.

Migration plan:

- Keep the existing ESLint flat config and attach caught errors as `cause` where ESLint 10 reports `preserve-caught-error`.
- Keep TypeScript path aliases and set `ignoreDeprecations` for the TypeScript 6 `baseUrl` deprecation until path aliasing is redesigned.
- Normalize Marionette socket chunks before `Buffer.concat` for the Node 25 type surface.
- Remove explicit `manualChunks: undefined` from the Vite extension build output, preserving Rollup's default chunking behavior.
- Update the Biome schema URL to match the installed Biome CLI.
- Use a Bun override for `shell-quote` 1.8.4 because latest `web-ext` pins `fx-runner` 1.4.0, which pins vulnerable `shell-quote` 1.7.3.

Verification:

- `bun run deps:check`
- `bun run check`
- `bun run release:check:local`

Rollback scope:

- Revert `package.json`, `bun.lock`, and the TypeScript, ESLint, Biome, Vite, and Marionette compatibility edits in the same change.
18 changes: 9 additions & 9 deletions docs/firefox-cli-spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ The happy path:
1. User installs the npm package and gets one executable: `firefox-cli`.
2. User manually installs or temporarily loads the Firefox extension from the URL printed by `firefox-cli setup`.
3. User runs `firefox-cli setup native-host` or `firefox-cli doctor --fix` to register the native messaging host.
4. User opens the extension popup and approves the first connection after seeing native-host identity details.
4. User runs `firefox-cli connect` and responds to the Firefox approval request, or opens the extension popup and approves the first connection.
5. Commands control the active Firefox tab/window unless a command or flag selects another target.

Example workflow:
Expand Down Expand Up @@ -96,8 +96,8 @@ Unpaired handshake:

1. Extension connects to the native host.
2. Native host sends identity metadata: host name, executable path, package version, protocol min/max, native manifest path, extension ID, and a generated pairing nonce.
3. Extension popup shows the metadata and asks the user to approve first use.
4. Until approval, the native host rejects or queues CLI commands with `NOT_APPROVED`.
3. The extension approval UI asks the user to approve first use.
4. Until approval, the native host rejects CLI commands with `NOT_APPROVED`, except for the dedicated approval request command.
5. On approval, extension and native host persist the minimum pair state needed to reconnect.

After approval, all commands are allowed without per-action confirmations, domain allowlists, or action policies.
Expand Down Expand Up @@ -138,15 +138,15 @@ Keep the extension UI smaller than a control panel. The popup should show:
- Approval/reset action for the first connection.
- Copy diagnostics action.

Do not mirror CLI commands in the popup. Setup text should tell users to click the Firefox extension popup to approve; do not depend on auto-opening the popup.
Do not mirror CLI commands in the popup. Setup text can tell users to run `firefox-cli connect` or click the Firefox extension popup to approve.

## Permissions

Use broad host access for the MVP full-control model after explicit first-use approval.

Expected manifest shape:

- `permissions`: native messaging, content scripting, tab access, local extension storage, downloads, cookies, clipboard, and web request observation.
- `permissions`: native messaging, content scripting, tab access, local extension storage, downloads, cookies, notifications, clipboard, and web request observation.
- `host_permissions`: broad web access for normal web pages.
- `browser_specific_settings.gecko.strict_min_version`: Firefox `150.0`.
- `browser_specific_settings.gecko.data_collection_permissions`: browsing activity, website activity, and website content because command results can leave the extension through the local native host and CLI.
Expand Down Expand Up @@ -322,7 +322,7 @@ Agent-browser family compatibility summary:
| Dialogs, downloads, clipboard, cookies, storage, network list/clear | MVP with Firefox/WebExtension limits; HAR unsupported |
| Debug/repro: console/errors, `highlight`, diff, trace/profiler, vitals | MVP for listed commands; deferred as listed below |
| Auth/state/session/profile/security gates/content boundaries | Deferred or unsupported in MVP because this controls the existing Firefox session after pairing |
| Chrome/CDP/provider/browser-install features: `connect`, `get cdp-url`, `inspect`, `--extension`, external providers, iOS, Chrome profile import, browser install/upgrade | Unsupported unless Firefox provides an equivalent |
| Chrome/CDP/provider/browser-install features: CDP attach, `get cdp-url`, `inspect`, `--extension`, external providers, iOS, Chrome profile import, browser install/upgrade | Unsupported unless Firefox provides an equivalent |

Global options:

Expand Down Expand Up @@ -403,7 +403,7 @@ Unsupported unless Firefox provides an equivalent:
- Top-level `close`, `quit`, `exit`, and `close --all`; use explicit `tab close` and `window close`.
- `confirm` and `deny`, because MVP has no per-action confirmation queue.
- Chrome `debugger`/CDP-specific commands.
- `connect <port|url>` and `get cdp-url`.
- CDP attach by port/URL and `get cdp-url`.
- DevTools opening/inspection behavior equivalent to `agent-browser inspect`.
- Chrome extension loading flags such as `--extension`.
- External browser providers and iOS provider.
Expand Down Expand Up @@ -480,8 +480,8 @@ Errors should be concise and actionable:

- If extension is not installed: print the matching extension download URL.
- If native host is not registered: print `firefox-cli setup native-host`.
- If Firefox is not running or extension is disconnected: tell the user to open Firefox and check the extension popup.
- If first-use approval is pending: tell the user to open the extension popup and approve.
- If Firefox is not running or extension is disconnected: tell the user to open Firefox and run `firefox-cli connect`.
- If first-use approval is pending: tell the user to run `firefox-cli connect` or open the extension popup and approve.
- If a page is restricted: name the restriction and suggest trying a normal web page/tab.
- If a ref is stale: tell the user to run `firefox-cli snapshot -i` again.
- If a command is unsupported: name the Firefox limitation or missing implementation gate.
Expand Down
9 changes: 9 additions & 0 deletions docs/firefox-cli/updates.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,15 @@
"strict_min_version": "150.0"
}
}
},
{
"applications": {
"gecko": {
"strict_min_version": "150.0"
}
},
"version": "0.2.0",
"update_link": "https://github.com/respawn-llc/firefox-cli/releases/download/v0.2.0/firefox-cli-0.2.0.xpi"
}
]
}
Expand Down
8 changes: 4 additions & 4 deletions docs/setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
- the CLI/native host executable from the npm package;
- the Firefox extension that connects Firefox to the native host.

The CLI cannot inspect Firefox until the extension is loaded, the native messaging manifest is installed, and the extension popup has approved the pair.
The CLI cannot inspect Firefox until the extension is loaded, the native messaging manifest is installed, and the user approves the Firefox control request.

## Install The CLI

Expand Down Expand Up @@ -60,9 +60,9 @@ firefox-cli doctor --fix

`doctor --fix` repairs missing or stale native-host manifests.

## Approve Pairing
## Connect Firefox

Open the `firefox-cli` extension popup in Firefox and approve the native host. The extension stores the pair token in Firefox extension storage; the native host stores pair state under the user-local `firefox-cli` state directory.
Run `firefox-cli connect` and respond to the approval request in Firefox, or open the `firefox-cli` extension popup in Firefox and approve the native host. The extension stores the pair token in Firefox extension storage; the native host stores pair state under the user-local `firefox-cli` state directory.

Verify the connection:

Expand All @@ -86,7 +86,7 @@ firefox-cli tab
: Load or enable the extension and keep Firefox running.

`Extension connection: not-approved`
: Open the extension popup and approve the native host.
: Run `firefox-cli connect` and respond to the approval request in Firefox.

`Version mismatch`
: Upgrade or rebuild the CLI, native host, and extension from the same package version.
Expand Down
25 changes: 14 additions & 11 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "firefox-cli-workspace",
"version": "0.1.1",
"version": "0.2.0",
"private": true,
"type": "module",
"packageManager": "bun@1.3.14",
Expand Down Expand Up @@ -53,19 +53,22 @@
"check": "bun run ci"
},
"devDependencies": {
"@biomejs/biome": "^2.3.8",
"@eslint/js": "^9.39.4",
"@types/bun": "^1.3.4",
"@biomejs/biome": "^2.4.16",
"@eslint/js": "^10.0.1",
"@types/bun": "^1.3.14",
"@types/jsdom": "^28.0.3",
"@types/node": "^24.10.2",
"eslint": "^9.39.4",
"@types/node": "^25.9.1",
"eslint": "^10.4.1",
"eslint-config-prettier": "^10.1.8",
"jsdom": "^29.1.1",
"typescript": "^5.9.3",
"typescript-eslint": "^8.48.0",
"vite": "^7.2.4",
"vitest": "^4.0.14",
"typescript": "^6.0.3",
"typescript-eslint": "^8.60.1",
"vite": "^8.0.16",
"vitest": "^4.1.8",
"web-ext": "^10.3.0",
"zod": "^4.1.13"
"zod": "^4.4.3"
},
"overrides": {
"shell-quote": "1.8.4"
}
}
2 changes: 1 addition & 1 deletion packages/cli/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@firefox-cli/cli",
"version": "0.1.1",
"version": "0.2.0",
"private": true,
"type": "module",
"exports": {
Expand Down
5 changes: 5 additions & 0 deletions packages/cli/src/argv-contracts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ export const routeParserSpecs = {
console: parser("console"),
errors: parser("errors"),
highlight: parser("highlight", { valueOptions: ["--generation", "--duration"] }),
notify: parser("notify", {
valueOptions: ["--id"],
payload: { payloadStartPositionals: 0, minPositionals: 1, variadicAfterMin: true },
}),
pdf: parser("pdf"),
"set.viewport": parser("set"),
diff: parser("diff", {
Expand All @@ -81,6 +85,7 @@ export const routeParserSpecs = {
flags: ["--bail", "--stdin"],
valueOptions: ["--timeout", "--max-output"],
}),
connect: parser("connect"),
click: parser("click", { valueOptions: ["--generation"] }),
dblclick: parser("dblclick", { valueOptions: ["--generation"] }),
focus: parser("focus", { valueOptions: ["--generation"] }),
Expand Down
Loading
Loading