121 lines
4.5 KiB
Markdown
121 lines
4.5 KiB
Markdown
# Agent handshake — Firefox Agent Bridge
|
|
|
|
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.
|
|
|
|
## Preconditions
|
|
|
|
1. Native host installed: `powershell -NoProfile -File tools/install-native-host.ps1`
|
|
2. Extension loaded in the target Firefox profile (`about:debugging` temporary add-on, or a signed install)
|
|
3. Firefox **running** (the host process is started by the extension)
|
|
|
|
If `http://127.0.0.1:17634/health` fails, the host is not up — open Firefox and confirm the extension is loaded. If `/health` works but `/v1/ready` fails, the stdio pipe is down; reload the extension.
|
|
|
|
## Auth
|
|
|
|
Token file (created by the installer):
|
|
|
|
`%LOCALAPPDATA%\firefox-agent-bridge\token`
|
|
|
|
Send it on every request except `/health`:
|
|
|
|
```
|
|
Authorization: Bearer <token>
|
|
```
|
|
|
|
Override with env `FAB_TOKEN` or `FAB_URL` (default `http://127.0.0.1:17634`).
|
|
|
|
## Preferred caller
|
|
|
|
From this repo:
|
|
|
|
```text
|
|
python tools/client.py METHOD [ARGS_JSON]
|
|
```
|
|
|
|
`ARGS_JSON` is a JSON **array** matching the WebExtension function arguments.
|
|
|
|
Examples:
|
|
|
|
```text
|
|
python tools/client.py meta.methods
|
|
python tools/client.py bookmarks.search "[{\"title\":\"10.132.x.x\"}]"
|
|
python tools/client.py bookmarks.getChildren "[\"FOLDER_ID\"]"
|
|
python tools/client.py bookmarks.create "[{\"parentId\":\"FOLDER_ID\",\"title\":\"NPM\",\"url\":\"http://10.132.99.80:81/\"}]"
|
|
python tools/client.py bookmarks.remove "[\"BOOKMARK_ID\"]"
|
|
python tools/client.py bookmarks.removeTree "[\"FOLDER_ID\"]"
|
|
```
|
|
|
|
HTTP equivalent:
|
|
|
|
```http
|
|
POST /v1/call
|
|
Content-Type: application/json
|
|
|
|
{"method": "bookmarks.search", "args": [{"title": "10.132.x.x"}]}
|
|
```
|
|
|
|
## Allowlisted methods (v0.1)
|
|
|
|
| Method | Args | Notes |
|
|
|--------|------|--------|
|
|
| `meta.ping` | (none) | Bridge alive |
|
|
| `meta.methods` | (none) | This table |
|
|
| `bookmarks.getTree` | (none) | Full tree |
|
|
| `bookmarks.getSubTree` | `id` | |
|
|
| `bookmarks.getChildren` | `id` | |
|
|
| `bookmarks.get` | `id` or `[id, …]` | |
|
|
| `bookmarks.search` | `{title? url? query?}` or string | |
|
|
| `bookmarks.create` | `{parentId?, title, url?}` | Omit `url` to create a folder |
|
|
| `bookmarks.update` | `id`, `{title? url?}` | |
|
|
| `bookmarks.move` | `id`, `{parentId? index?}` | |
|
|
| `bookmarks.remove` | `id` | Bookmark or empty folder |
|
|
| `bookmarks.removeTree` | `id` | Folder and descendants |
|
|
| `bookmarks.getRecent` | `numberOfItems` | |
|
|
|
|
Do not invent other `browser.*` names. Adding an API means editing `ALLOWED` in `extension/background.js` and reloading the extension.
|
|
|
|
REST aliases (same auth):
|
|
|
|
| HTTP | Maps to |
|
|
|------|---------|
|
|
| `GET /health` | host process only (no token) |
|
|
| `GET /v1/ready` | `meta.ping` |
|
|
| `GET /v1/methods` | `meta.methods` |
|
|
| `GET /v1/bookmarks/tree` | `bookmarks.getTree` |
|
|
| `GET /v1/bookmarks/{id}` | `bookmarks.get` |
|
|
| `POST /v1/bookmarks/search` | body is the query object |
|
|
| `POST /v1/bookmarks/create` | body is `CreateDetails` |
|
|
| `POST /v1/bookmarks/update` | `{id, changes}` |
|
|
| `POST /v1/bookmarks/move` | `{id, destination}` |
|
|
| `POST /v1/bookmarks/remove` | `{id}` |
|
|
| `POST /v1/bookmarks/remove-tree` | `{id}` |
|
|
| `POST /v1/call` | `{method, args}` |
|
|
|
|
## Typical folder replace
|
|
|
|
1. `bookmarks.search` `{title: "…"}` — take the hit **without** a `url` (that is the folder). Fail if zero or many.
|
|
2. `bookmarks.getChildren` on that id.
|
|
3. `bookmarks.remove` each bookmark; `bookmarks.removeTree` each child folder.
|
|
4. `bookmarks.create` each new item with `parentId` set.
|
|
|
|
`examples/replace_named_folder.py` does exactly that. It is an example, not a sync service.
|
|
|
|
You cannot modify Firefox's bookmark root (`The bookmark root cannot be modified`). Operate on a named subfolder (toolbar / menu / a folder the user already created).
|
|
|
|
## Failure modes
|
|
|
|
| Symptom | Cause | What to do |
|
|
|---------|--------|------------|
|
|
| Connection refused on `:17634` | Firefox closed or extension not loaded | Open Firefox; load/reload the add-on |
|
|
| 401 | Missing/wrong bearer | Read the token file |
|
|
| 502 `method not allowed` | Typo or API not in `ALLOWED` | Use `meta.methods` |
|
|
| 502 timeout | Extension died mid-call | Reload the add-on |
|
|
| Temporary add-on gone after restart | Unsigned on Firefox Release | Load again, or sign via AMO unlisted |
|
|
| Native host not found | Installer not run | `tools/install-native-host.ps1` |
|
|
|
|
## What this project is not
|
|
|
|
- Not a bidirectional sync implementation
|
|
- Not a general Firefox remote-control surface (no tabs, history, cookies, native file access)
|
|
- Not a reason to keep editing `places.sqlite` on a live profile
|