@frod.io/bridge 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +269 -0
- package/dist/cli.js +8257 -0
- package/package.json +43 -0
package/README.md
ADDED
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
# Frod.io bridge — your computer is a desk
|
|
2
|
+
|
|
3
|
+
You already have Claude Code on your computer, with your own sign-in, skills,
|
|
4
|
+
memory and tools. The bridge makes that Claude a **session of the platform**:
|
|
5
|
+
agents you choose run on your computer, take `@mentions` from Teams and
|
|
6
|
+
Telegram, read their Inbox, sit in the Office, run blueprint steps and file
|
|
7
|
+
into the map — governed and metered like every other agent — and nothing of
|
|
8
|
+
yours leaves your machine.
|
|
9
|
+
|
|
10
|
+
## One command to join
|
|
11
|
+
|
|
12
|
+
On an agent's page in Mission Control, **Join from your computer** gives you a
|
|
13
|
+
one-time code and shows the command to run (the code lasts 15 minutes and
|
|
14
|
+
works once). The bridge is a public package, `@frod.io/bridge` — nobody needs a
|
|
15
|
+
checkout of the platform repository:
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
npx @frod.io/bridge@latest join <code> --api https://api.agentic.frod.io [--name "Wafeek's Mac"] [--workdir ~/frod-work] [--max-sessions 2] [--no-install]
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
`npx` only bootstraps the command. `join` itself:
|
|
22
|
+
|
|
23
|
+
- checks that `claude` is installed and signed in (`claude auth login`);
|
|
24
|
+
- exchanges the code for a token and keeps it in your OS keychain — macOS's
|
|
25
|
+
`security`, set up so the item trusts `/usr/bin/security` and never pops a
|
|
26
|
+
dialog; Linux's `secret-tool`; anywhere else a 0600 file, and it says so;
|
|
27
|
+
- writes `~/.frod/bridge.json` (no secret in it);
|
|
28
|
+
- **installs the very package it just ran** into
|
|
29
|
+
`~/.frod/bridge/versions/<version>/`, with `~/.frod/bridge/current` a
|
|
30
|
+
symlink to it (a junction on Windows) — the npx cache is never where the
|
|
31
|
+
bridge actually runs from;
|
|
32
|
+
- writes the run-at-login entry so it points at
|
|
33
|
+
`node ~/.frod/bridge/current/dist/cli.js run` (macOS launchd agent
|
|
34
|
+
`io.frod.bridge`, Linux systemd user unit `frod-bridge.service`, Windows
|
|
35
|
+
scheduled task *Frod.io bridge*);
|
|
36
|
+
- and starts it.
|
|
37
|
+
|
|
38
|
+
Pass `--no-install` to join without any of the install/run-at-login steps —
|
|
39
|
+
useful if you'd rather run `install` or `run` by hand afterwards.
|
|
40
|
+
|
|
41
|
+
**You join once.** A computer is *yours* on the platform, not an agent's.
|
|
42
|
+
Every further agent is attached in Mission Control — on that agent's page,
|
|
43
|
+
**Join from your computer → Attach an existing computer** — and the change
|
|
44
|
+
reaches a running bridge at once, no restart. `status` lists the agents this
|
|
45
|
+
computer hosts and how each runs.
|
|
46
|
+
|
|
47
|
+
## Other commands
|
|
48
|
+
|
|
49
|
+
All of these work either as `npx @frod.io/bridge@latest <command>` or, once
|
|
50
|
+
you've joined, as `node ~/.frod/bridge/current/dist/cli.js <command>`:
|
|
51
|
+
|
|
52
|
+
- `install` — on a computer already joined (from an old checkout, or an
|
|
53
|
+
earlier package), stages this package into `versions/`, rewrites the
|
|
54
|
+
run-at-login entry to point at it, leaves the keychain item exactly as it
|
|
55
|
+
is, and restarts the bridge — any session running here ends, so do it
|
|
56
|
+
when the desk is clear.
|
|
57
|
+
- `run` — runs the bridge in the foreground.
|
|
58
|
+
- `status` — what the platform sees, and which agents run here.
|
|
59
|
+
- `leave` — see below. `uninstall` — removes the run-at-login entry only
|
|
60
|
+
(the installed versions under `~/.frod/bridge` stay; delete the folder if
|
|
61
|
+
you like). `--version` — prints the version this command is.
|
|
62
|
+
|
|
63
|
+
## What the platform decides, per agent (in Mission Control)
|
|
64
|
+
|
|
65
|
+
For every agent on your computer the workspace owner chooses:
|
|
66
|
+
|
|
67
|
+
- **Model** — the computer's default, Opus, Sonnet or Haiku (an alias Claude
|
|
68
|
+
Code understands), **Effort** — low, medium, high.
|
|
69
|
+
- **Ask / Never ask.** *Ask* (the default): a shell command, the web, a file
|
|
70
|
+
outside the session folder, a connector that sends or changes something —
|
|
71
|
+
each becomes a decision on the owner's phone (Approve / Reject) before it
|
|
72
|
+
runs. *Never ask — acts on its own*: the same calls run without waiting, and
|
|
73
|
+
**every one of them is recorded** — an activity line the owner can read and a
|
|
74
|
+
notice in the session's transcript with the exact words the gate would have
|
|
75
|
+
used. Only the workspace owner can set it; an agent can never set it for
|
|
76
|
+
itself; it is never the default.
|
|
77
|
+
|
|
78
|
+
## What the platform sees
|
|
79
|
+
|
|
80
|
+
- The **transcript** of every session it opens here (what the agent said, the
|
|
81
|
+
tools it called and their results), its **cost** at the model's list rate
|
|
82
|
+
from the token counts Claude Code reports (the ledger says *billed to
|
|
83
|
+
subscription (list rate)*), and its **working folder** (`<workdir>/sessions/<id>`,
|
|
84
|
+
or the run checkout for a repository step).
|
|
85
|
+
- That this computer is **connected or offline**, its name, OS and Claude Code
|
|
86
|
+
version, and how many sessions it is running (up to `--max-sessions`, across
|
|
87
|
+
all its agents).
|
|
88
|
+
- Every permission decision, and — under *Never ask* — every call that would
|
|
89
|
+
have asked.
|
|
90
|
+
|
|
91
|
+
## What it never sees
|
|
92
|
+
|
|
93
|
+
- Your Claude sign-in, your `~/.claude` settings, skills, plugins and MCP
|
|
94
|
+
servers — they load as they always do; the platform only *adds* its own
|
|
95
|
+
system prompt and its own tools on top.
|
|
96
|
+
- Any file outside the session folder unless the agent asks and a person
|
|
97
|
+
approves (or the owner chose *Never ask*).
|
|
98
|
+
- Your git credentials: repository steps clone and push **with your own
|
|
99
|
+
access**, only ever the run branch, and the platform verifies the commit on
|
|
100
|
+
its side before it counts.
|
|
101
|
+
- Any platform secret: the session's tools reach the Knowledge Hub, the
|
|
102
|
+
Library and every connector through doors on the platform, on a per-session
|
|
103
|
+
token that dies with the session. No database credential, no connector
|
|
104
|
+
credential and no API key is ever on this computer (the platform walks every
|
|
105
|
+
spec for them before it is sent).
|
|
106
|
+
|
|
107
|
+
## It updates itself
|
|
108
|
+
|
|
109
|
+
Every time the bridge connects, the platform announces the current version,
|
|
110
|
+
signed. If this computer is behind, the bridge downloads the new tarball and
|
|
111
|
+
checks it two ways before trusting it — its sha512 against the hash the
|
|
112
|
+
platform announced, and an ed25519 signature over `version|integrity` against
|
|
113
|
+
a public key built into the bridge. Both have to pass: a compromised registry
|
|
114
|
+
alone can't push a version, and neither can a compromised platform alone. A
|
|
115
|
+
tarball that fails either check is deleted, the old version keeps running,
|
|
116
|
+
the log says `bridge.update_refused` with the reason, and the computer card
|
|
117
|
+
says "Update to `<v>` refused: `<reason>`."
|
|
118
|
+
|
|
119
|
+
A tarball that passes is extracted to `versions/<new>/`, and then the bridge
|
|
120
|
+
**waits** — never in the middle of a turn, never with a prompt — until this
|
|
121
|
+
computer hosts no live session, logging
|
|
122
|
+
`update ready, waiting for the desk to clear` once an hour (the computer card
|
|
123
|
+
says the same). When the desk is clear, it swaps `current` atomically, logs
|
|
124
|
+
`bridge.updated {from, to}`, and restarts through the supervisor: it exits
|
|
125
|
+
with code 75, a non-zero exit that launchd's `KeepAlive SuccessfulExit=false`
|
|
126
|
+
and systemd's `Restart=on-failure` both bring straight back up — a clean exit
|
|
127
|
+
(code 0) is deliberately a final goodbye, not a restart signal. Windows'
|
|
128
|
+
scheduled task has no restart-on-failure, so there the bridge relaunches the
|
|
129
|
+
new version itself, detached; if that fails, the next logon starts it.
|
|
130
|
+
|
|
131
|
+
The keychain item is never touched by an update. You'll see `Updated to <v>`
|
|
132
|
+
in `~/.frod/bridge.log` and the new version on the computer card. Two
|
|
133
|
+
versions are kept under `versions/`; older ones are pruned.
|
|
134
|
+
|
|
135
|
+
A bridge still run from a checkout — the old way,
|
|
136
|
+
`npx tsx apps/bridge/src/cli.ts run` — never updates itself; its card says
|
|
137
|
+
"update it by hand: `npx @frod.io/bridge@latest install`."
|
|
138
|
+
|
|
139
|
+
## The compatibility contract
|
|
140
|
+
|
|
141
|
+
The platform decides what a computer's bridge can be trusted with by the
|
|
142
|
+
version it announces when it says hello — for example
|
|
143
|
+
`0.7.0/claude 2.1.261/codex 0.153.2/grok 1.0.13`. Three lines are drawn on the
|
|
144
|
+
platform side (`apps/orchestration-api/src/services/session-doors.ts`):
|
|
145
|
+
|
|
146
|
+
- below bridge `0.2.0` — connectors are never attached (an older bridge
|
|
147
|
+
couldn't gate them, so it isn't offered the choice);
|
|
148
|
+
- below `0.6.0` — no repository tools;
|
|
149
|
+
- below `0.7.0` — the Inbox and orchestration tools run from a checkout of the
|
|
150
|
+
platform as separate processes on your computer; at or above, they're
|
|
151
|
+
served by the platform the same way the Knowledge Hub and the Library are,
|
|
152
|
+
which is exactly why the package needs no checkout at all.
|
|
153
|
+
|
|
154
|
+
The rule behind these lines: one is only ever raised in the same commit as
|
|
155
|
+
the feature that needs it, and a computer running an older bridge is told, in
|
|
156
|
+
its own session's prompt, plainly, what it doesn't have — it is never handed
|
|
157
|
+
a door it can't work.
|
|
158
|
+
|
|
159
|
+
## Leaving
|
|
160
|
+
|
|
161
|
+
```
|
|
162
|
+
npx @frod.io/bridge@latest leave
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
revokes the token on the platform, forgets the computer there (each agent goes
|
|
166
|
+
back to running where it did before — on the platform, or from its desk),
|
|
167
|
+
removes the token from the keychain and the run-at-login entry. The workspace
|
|
168
|
+
owner can do the same from Mission Control (**Settings → Your computers →
|
|
169
|
+
Disconnect**), which takes the computer off the floor at once.
|
|
170
|
+
|
|
171
|
+
## Files on this computer
|
|
172
|
+
|
|
173
|
+
| Path | What |
|
|
174
|
+
|---|---|
|
|
175
|
+
| `~/.frod/bridge.json` | settings; no secret |
|
|
176
|
+
| the OS keychain, item `io.frod.bridge` | the host token |
|
|
177
|
+
| `~/.frod/bridge/versions/<version>/` | one installed copy of the package |
|
|
178
|
+
| `~/.frod/bridge/current` | a symlink (junction on Windows) to the version that's running |
|
|
179
|
+
| `~/.frod/sessions.json` | which Claude Code session each platform session ran as (for resume) |
|
|
180
|
+
| `~/.frod/bridge.log` | the log — tokens are redacted before anything is written |
|
|
181
|
+
| `<workdir>/sessions/<id>` | a session's folder (the default) |
|
|
182
|
+
| the folder the owner chose | a session's folder when the agent's binding names a **working folder on this computer** (see below) |
|
|
183
|
+
| `<workdir>/runs/<run id>` | a repository step's checkout (pruned after a week) |
|
|
184
|
+
|
|
185
|
+
## A working folder per agent (day block 2026-09-03)
|
|
186
|
+
|
|
187
|
+
The workspace owner may give an agent a **working folder on this computer** in
|
|
188
|
+
Mission Control (the agent's page → Your computer → Change). The platform stores
|
|
189
|
+
the path as typed and sends it with each session; **this computer decides**
|
|
190
|
+
whether it allows it (`src/workdir.ts`):
|
|
191
|
+
|
|
192
|
+
- allowed: the folder is, or is under, `workdir` in `bridge.json`
|
|
193
|
+
(`~/frod-work` by default) **or** one of the folders listed under
|
|
194
|
+
`"allowedRoots"` in `bridge.json` — a list you edit by hand; `~/` means your
|
|
195
|
+
home. A folder that does not exist yet is created (0700).
|
|
196
|
+
- refused: anything else — outside every root, a relative path, a climb out with
|
|
197
|
+
`..`, a symlink that escapes. The session fails at once with the words
|
|
198
|
+
*"Wafeek's Mac does not allow that folder — allowed: …"*; nothing runs in a
|
|
199
|
+
folder you did not choose.
|
|
200
|
+
|
|
201
|
+
The folder becomes the session's cwd and the free zone of its permission policy
|
|
202
|
+
(reads and edits there are free; outside it, they ask — and Claude Code's own
|
|
203
|
+
files and any `.git` **at any depth** under it ask too). A repository step
|
|
204
|
+
ignores it and works in `runs/<run id>` as before. `status` prints the allowed
|
|
205
|
+
folders.
|
|
206
|
+
|
|
207
|
+
Two quirks of the containment check, both fail-closed and deliberate:
|
|
208
|
+
`"allowedRoots": ["/"]` allows **nothing under it** (the check is `root + "/"`, and
|
|
209
|
+
`"//"` matches no path — list real folders); and the check is
|
|
210
|
+
case-sensitive, so on a case-insensitive disk (macOS default) a folder typed as
|
|
211
|
+
`~/Frod-Work/x` is refused when the root is `~/frod-work` — type it as the root
|
|
212
|
+
is spelled.
|
|
213
|
+
|
|
214
|
+
## Adapters
|
|
215
|
+
|
|
216
|
+
The bridge talks to a tool through one small interface — `start`, `prompt`,
|
|
217
|
+
`cancel`, `stop` (`src/adapters/types.ts`). **`claude`**
|
|
218
|
+
(`src/adapters/claude.ts`) is required — `join` checks it's installed and
|
|
219
|
+
signed in, and it drives Claude Code's headless CLI with your subscription.
|
|
220
|
+
**`codex`** (`src/adapters/codex.ts`) and **`grok`** (`src/adapters/grok.ts`)
|
|
221
|
+
are optional: each is offered only when it's installed *and* signed in on
|
|
222
|
+
this computer. Which agent runs on which adapter is decided in Mission
|
|
223
|
+
Control, not here. Grok Build runs in its `workspace` sandbox, and the bridge
|
|
224
|
+
answers its permission questions by the same Ask / Never-ask policy as the
|
|
225
|
+
others. Each adapter is one file.
|
|
226
|
+
|
|
227
|
+
## Running at login
|
|
228
|
+
|
|
229
|
+
- **macOS** — a launchd user agent `io.frod.bridge`, running
|
|
230
|
+
`node ~/.frod/bridge/current/dist/cli.js run` — the same entry shape that
|
|
231
|
+
has run from a checkout on Wafeek's Mac since 2026-09-03; the packaged
|
|
232
|
+
entry runs there when his Mac is upgraded last, by him.
|
|
233
|
+
- **Linux** — a systemd user unit `~/.config/systemd/user/frod-bridge.service`
|
|
234
|
+
running the same command (`systemctl --user enable --now
|
|
235
|
+
frod-bridge.service`; on a headless box `loginctl enable-linger $USER` once).
|
|
236
|
+
Written and enabled by `install`; proven in a container on 2026-09-06 — the
|
|
237
|
+
unit is written correctly, but `systemctl --user` doesn't exist in a
|
|
238
|
+
container, so the manual command is printed instead. Not yet proven on a
|
|
239
|
+
real Linux machine.
|
|
240
|
+
- **Windows** — a scheduled task *Frod.io bridge* at logon
|
|
241
|
+
(`schtasks /Create`), also pointing at `current`. If it refuses, the exact
|
|
242
|
+
command is printed. Not yet proven on a real Windows machine.
|
|
243
|
+
|
|
244
|
+
## Publishing
|
|
245
|
+
|
|
246
|
+
`.github/workflows/deploy-bridge.yml` runs on every push to `main` that
|
|
247
|
+
touches `apps/bridge/**`: it runs the tests, the type check and the bundle,
|
|
248
|
+
then `npm pack` and a smoke test of the packed tarball in a clean directory,
|
|
249
|
+
writes release notes from the commit subjects since the last published
|
|
250
|
+
version, runs `npm publish --access public`, signs the release
|
|
251
|
+
with an ed25519 key held in the `BRIDGE_SIGNING_KEY` secret, and calls
|
|
252
|
+
`POST /api/bridge/releases` (with the `BRIDGE_RELEASE_TOKEN` secret) so the
|
|
253
|
+
platform starts announcing it to connecting bridges.
|
|
254
|
+
|
|
255
|
+
The version published is always the package's own: bump
|
|
256
|
+
`apps/bridge/package.json` in the commit that should ship. The workflow
|
|
257
|
+
refuses to publish a version already on the registry, and never bumps
|
|
258
|
+
anything itself. Three secrets gate the workflow's last three steps, and if
|
|
259
|
+
one is missing the workflow says in words which one.
|
|
260
|
+
|
|
261
|
+
## For developers
|
|
262
|
+
|
|
263
|
+
- `npm test -w @frod.io/bridge`
|
|
264
|
+
- `npm run build -w @frod.io/bridge` — bundles `dist/cli.js` with esbuild and
|
|
265
|
+
runs `tsc` for types.
|
|
266
|
+
- `npm run pack:check` — packs the tarball and smoke-tests it in a clean
|
|
267
|
+
directory, the same check the publish workflow runs.
|
|
268
|
+
- The bundle test asserts that every adapter and every door starts correctly
|
|
269
|
+
from `dist/cli.js` alone, with no checkout of the platform on disk.
|