Rename the add-on Bookmarks API and drop CLI copy from the product UI.

This commit is contained in:
alexveley
2026-09-22 07:33:35 -04:00
parent 9242c01015
commit de0814f3ed
7 changed files with 27 additions and 54 deletions
+4 -2
View File
@@ -1,4 +1,6 @@
# Agent handshake — Firefox Agent Bridge
# Agent handshake — Bookmarks API for Scripting and AI
Use this when working **in this repo**. The shipped product is the add-on under `extension/` only. Do not treat `tools/` as part of the listing.
Use this when a script or coding agent needs to **create, update, move, or delete Firefox bookmarks while Firefox is open**. Do not edit `places.sqlite` if this bridge is reachable.
@@ -15,7 +17,7 @@ If `http://127.0.0.1:17634/health` fails, the host is not up. If `/health` works
This is a normal API token, not an SSH key.
1. In Firefox, open the Agent Bridge toolbar icon → **Manage clients**.
1. In Firefox, open the Bookmarks API toolbar icon → **Manage clients**.
2. Name the client (e.g. `cursor-agent`) and click **Generate secret**.
3. Copy the `fab_…` value **once**. Store it however you already store secrets: env var, Vaultwarden, `.env`, CI secret, Cursor env.
4. Give tooling only that string:
+9 -38
View File
@@ -1,53 +1,24 @@
# Firefox Agent Bridge
# Bookmarks API for Scripting and AI
A small Firefox add-on that exposes an **allowlisted** WebExtension API to local scripts and agents — after you issue them a secret from inside Firefox.
A Firefox add-on that lets **local** scripts and agents call an allowlisted `browser.bookmarks` API — after you issue them a secret from inside the browser.
This is **not** a bookmark sync engine. Firefox already has `browser.bookmarks`. The add-on is a bridge: while Firefox is open, a script on the same machine can call those functions over `http://127.0.0.1:17634` with a Bearer token.
The published product is the add-on in `extension/`. It does not ship helper scripts. Callers use ordinary HTTP and a Bearer token, stored however they already keep secrets.
Agents: start at [AGENTS.md](AGENTS.md).
## Why this exists
Firefox will not let an outside process talk to Places. Editing `places.sqlite` while the browser is running loses deletes. The supported path is `browser.bookmarks` inside an extension, plus [native messaging](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Native_messaging) so a localhost HTTP host can reach that extension.
This is not a bookmark sync engine, and it is not a Mozilla product. Mozillas add-on naming rule is “Name for Firefox,” never “Firefox Name”; this listing uses neither form in the title.
## Setup
1. Register the native host (once):
1. Register the native host once (`tools/install-native-host.ps1` on this workstation).
2. Load the add-on (temporary via `about:debugging`, or a signed `.xpi` — [docs/signing.md](docs/signing.md)).
3. Toolbar icon → **Manage clients** → generate a secret → store it with your secrets.
```powershell
powershell -NoProfile -File tools\install-native-host.ps1
```
2. Load the add-on (`about:debugging` temporary, or a signed `.xpi` — [docs/signing.md](docs/signing.md)).
3. Click the toolbar icon → **Manage clients** → name a client → **Generate secret**. Copy the `fab_…` token into whatever secret store you already use.
4. Leave Firefox open. `GET http://127.0.0.1:17634/health` should return JSON.
## Call it
```powershell
$env:FAB_TOKEN = "fab_…" # the value from the Manage clients modal
python tools/client.py meta.methods
python tools/client.py bookmarks.search '[{"title":"10.132.x.x"}]'
```
`curl` works the same: `Authorization: Bearer fab_…`.
While Firefox is open, `http://127.0.0.1:17634/health` answers if the host is up. Authenticated calls use `Authorization: Bearer <secret>` on `/v1/call`. Method list: the repo [AGENTS.md](AGENTS.md) (for people working on this codebase, not for the AMO listing).
## Security
- Binds **127.0.0.1** only. Browser `Origin` headers are rejected.
- Each caller is an issued token. The add-on stores a hash, not the secret.
- Only `bookmarks.*` (plus `meta.*`) in `extension/background.js`. No history, cookies, tabs, or `onMessageExternal`.
## Pieces
| Piece | Role |
|-------|------|
| `extension/` | Add-on, popup, Manage clients page |
| `host/` | Native host: stdio to Firefox, HTTP to scripts |
| `tools/client.py` | Bearer caller (`FAB_TOKEN`) |
| `examples/replace_named_folder.py` | Sample folder replace |
- Only `bookmarks.*` (plus `meta.*`). No history, cookies, tabs, or `onMessageExternal`.
## License
+1 -1
View File
@@ -42,4 +42,4 @@ Bump `version`, sign again on the same channel and id. For unlisted self-distrib
## 5. Listed (later)
`web-ext sign --channel=listed` plus an AMO metadata JSON (name, summary, license MIT). Expect listing copy that says: local scripts only, loopback HTTP, bearer token, bookmarks allowlist. Do not claim it is a general Firefox remote-control tool.
`web-ext sign --channel=listed` plus an AMO metadata JSON (name **Bookmarks API for Scripting and AI**, license MIT). Listing copy: local scripts only, loopback HTTP, bearer token, bookmarks allowlist. Do not title it “Firefox …”. Mozilla only allows “Name for Firefox”; this add-on uses neither form.
+5 -4
View File
@@ -1,8 +1,9 @@
{
"manifest_version": 3,
"name": "Firefox Agent Bridge",
"version": "0.2.0",
"description": "Let local scripts and agents call an allowlisted WebExtension API after you issue them a secret.",
"name": "Bookmarks API for Scripting and AI",
"short_name": "Bookmarks API",
"version": "0.3.0",
"description": "Issue a secret, then let local scripts and agents call an allowlisted bookmarks API.",
"browser_specific_settings": {
"gecko": {
"id": "firefox-agent-bridge@easygoingaming.com",
@@ -14,7 +15,7 @@
"scripts": ["background.js"]
},
"action": {
"default_title": "Firefox Agent Bridge",
"default_title": "Bookmarks API",
"default_popup": "popup.html"
},
"options_ui": {
+5 -6
View File
@@ -2,18 +2,18 @@
<html lang="en">
<head>
<meta charset="utf-8">
<title>Firefox Agent Bridge</title>
<title>Bookmarks API</title>
<link rel="stylesheet" href="ui.css">
</head>
<body class="page">
<header>
<h1>Firefox Agent Bridge</h1>
<p class="muted">Issue a secret to each script or agent. The add-on keeps only a hash. Put the secret in an env var, vault, or <code>.env</code> — whatever you already use.</p>
<h1>Bookmarks API for Scripting and AI</h1>
<p class="muted">Issue a secret to each client. Store it with your secrets. This add-on keeps only a hash.</p>
<p id="status" class="muted"></p>
</header>
<section class="row">
<input id="name" type="text" maxlength="64" placeholder="Client name, e.g. cursor-agent" autocomplete="off">
<input id="name" type="text" maxlength="64" placeholder="Client name" autocomplete="off">
<button id="create" type="button">Generate secret</button>
</section>
<p id="error" class="error" hidden></p>
@@ -34,10 +34,9 @@
<dialog id="secret-modal">
<h2>Copy this secret now</h2>
<p class="muted">It will not be shown again. Store it the same way you store any other API token.</p>
<p class="muted">It will not be shown again. Store it with your secrets.</p>
<label class="sr">Secret</label>
<textarea id="secret" readonly rows="3"></textarea>
<p class="hint">Example: <code>set FAB_TOKEN=&lt;secret&gt;</code> then <code>python tools/client.py meta.methods</code></p>
<div class="row">
<button id="copy" type="button">Copy</button>
<button id="close" type="button">I saved it</button>
+1 -1
View File
@@ -5,7 +5,7 @@
<link rel="stylesheet" href="ui.css">
</head>
<body class="popup">
<h1>Agent Bridge</h1>
<h1>Bookmarks API</h1>
<p id="status" class="muted">Checking host…</p>
<button id="manage" type="button">Manage clients</button>
<script src="popup.js"></script>
+2 -2
View File
@@ -23,7 +23,7 @@ if (Test-Path $TokenPath) {
$manifest = @{
name = $HostName
description = "Firefox Agent Bridge native host"
description = "Bookmarks API native host"
path = $CmdPath
type = "stdio"
allowed_extensions = @($ExtensionId)
@@ -38,4 +38,4 @@ Write-Host "registered $regPath"
Write-Host ""
Write-Host "Load the add-on, then use the toolbar icon → Manage clients to generate a secret."
Write-Host "Put that fab_… token in FAB_TOKEN for scripts. Do not store it under $StateDir."
Write-Host "Store that secret with your other secrets. Do not keep it under $StateDir."