byollm 0.1.0-alpha.7 → 0.1.0-alpha.71
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 +194 -19
- package/dist/bin.js +1 -1
- package/dist/chunk-YJ3OSRLT.js +6431 -0
- package/dist/chunk-YJ3OSRLT.js.map +1 -0
- package/dist/index.d.ts +736 -92
- package/dist/index.js +3 -5
- package/package.json +2 -2
- package/dist/chunk-3E2PGDHL.js +0 -3089
- package/dist/chunk-3E2PGDHL.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
> [!WARNING]
|
|
2
|
-
> **Alpha (`0.1.0-alpha.
|
|
2
|
+
> **Alpha (`0.1.0-alpha.71`) — under active development. Don't use this yet.**
|
|
3
3
|
>
|
|
4
4
|
> Install it deliberately: `npx byollm@alpha`, or `npm install byollm@alpha`.
|
|
5
5
|
>
|
|
@@ -11,14 +11,116 @@
|
|
|
11
11
|
> npm assigns `latest` on a first publish and won't let it be removed, so a
|
|
12
12
|
> bare install resolves here too. This notice is the only guard — deliberately
|
|
13
13
|
> not an npm deprecation, which would read as *abandoned* rather than *early*.
|
|
14
|
-
> Ask for `@alpha` explicitly so your lockfile records that you meant to
|
|
14
|
+
> Ask for `@alpha` explicitly so your lockfile records that you meant to.>
|
|
15
|
+
> **`alpha.15` is a breaking wire change, and it breaks daemons and relays —
|
|
16
|
+
> not app authors.** If you call `app.enqueue(...)` and read results, nothing
|
|
17
|
+
> in your code changes. If you run a daemon or an upstream, every package must
|
|
18
|
+
> move together: a mixed pair refuses on both sides, because both ends parse
|
|
19
|
+
> `.strict()`.
|
|
20
|
+
>
|
|
21
|
+
> What moved, all of it reconciling the frozen `byollm_009` with its code:
|
|
22
|
+
> `JobStub` gains `site` (the site's identity key id) and loses
|
|
23
|
+
> `audienceAllow`; `ResultRequest` gains `leaseId`; `HeartbeatResponse` loses
|
|
24
|
+
> `leases`, which nothing read; `WireErrorCode` gains `not-ready`,
|
|
25
|
+
> `clock-skew` and `forbidden`, and `403` is `forbidden` rather than
|
|
26
|
+
> `unauthorized`. `RESULT_PROVENANCE` is superseded by
|
|
27
|
+
> `PROVENANCE_NAMES_DEVICE`. See `byollm_009` Amendment A.>
|
|
28
|
+
> **`alpha.16` is a breaking wire change — daemons and relays again, not app
|
|
29
|
+
> authors.** `app.enqueue(...)` and reading results are unchanged. All five
|
|
30
|
+
> packages move together: both ends parse `.strict()`, so a mixed pair
|
|
31
|
+
> refuses.
|
|
32
|
+
>
|
|
33
|
+
> What moved, all of it Tier 2 of `cloud_008`: `model`, `backendClass` and
|
|
34
|
+
> `durationMs` come off `ResultRequest` and are sealed **inside** the result
|
|
35
|
+
> envelope as `SealedOutcome = { outcome, ran }` — so a daemon can no longer
|
|
36
|
+
> declare a model it did not sign, and a relay carries neither.
|
|
37
|
+
> `HeartbeatResponse` loses `leases` (nothing read it) and now reports real
|
|
38
|
+
> cancellations instead of an empty list. `WireErrorCode` gains `forbidden`
|
|
39
|
+
> for 403, leaving `unauthorized` at exactly 401. The relay gained a
|
|
40
|
+
> site-plane `cancel` endpoint, honours `stub.deadlineAt`, honours
|
|
41
|
+
> `stub.audience`, and remembers a refusal.>
|
|
42
|
+
> **`alpha.17` is additive** — no wire change. It exports `ReleaseReason`,
|
|
43
|
+
> which `RoutingStore.releaseLeases` names and the package did not export, so
|
|
44
|
+
> the interface was unimplementable outside this repo.>
|
|
45
|
+
> **`alpha.18` is a breaking wire change — daemons and relays, not app
|
|
46
|
+
> authors.** `app.enqueue(...)` and reading results are unchanged. All five
|
|
47
|
+
> packages move together.
|
|
48
|
+
>
|
|
49
|
+
> The **bearer token is gone**: off `PairPollResponse`, off the runner row,
|
|
50
|
+
> off the daemon's pairings file, out of the adapter's schema. It was minted,
|
|
51
|
+
> hashed and stored on two disks and never sent, looked up or compared —
|
|
52
|
+
> `REQUESTS_SIGNED_NOT_BEARER` was enforced by signatures the whole time. If
|
|
53
|
+
> you run the Supabase adapter, apply
|
|
54
|
+
> `20260819000000_drop_runner_token.sql`; `byollm_approve_pairing` now takes
|
|
55
|
+
> one argument. A pairings file written by an older daemon still loads.
|
|
56
|
+
>
|
|
57
|
+
> `model`, `backendClass` and `durationMs` moved **inside** the sealed result
|
|
58
|
+
> (`SealedOutcome = { outcome, ran }`), so a daemon cannot declare a model it
|
|
59
|
+
> did not sign and a relay carries none of them. Writing a `RoutingStore`?
|
|
60
|
+
> `releaseLeases` takes an optional `reason` and `complete` requires
|
|
61
|
+
> `leaseId`, and **an implementation that ignores either still typechecks** —
|
|
62
|
+
> run the store contract tests.>
|
|
63
|
+
> **`alpha.19` is additive on the wire and a behaviour change in every
|
|
64
|
+
> store.** `ResultResponse` gains an optional `duplicate`. Nothing is removed,
|
|
65
|
+
> so an older daemon keeps working — but the *order* two rules are checked in
|
|
66
|
+
> has changed, and a `RoutingStore` implementation must change with it.
|
|
67
|
+
>
|
|
68
|
+
> `complete` now checks **terminal state before holder**, scoped to the device
|
|
69
|
+
> that finished the job: a replay from that device is answered `duplicate:
|
|
70
|
+
> true` with a 2xx, and anyone else gets exactly the refusal they would get
|
|
71
|
+
> for a job that is not terminal. Previously `RESULT_IDEMPOTENT` held only
|
|
72
|
+
> because the lease is nulled on success, so the holder check tripped first —
|
|
73
|
+
> deleting the idempotency branch failed no test. Run the store contract
|
|
74
|
+
> tests; the compiler cannot see this.
|
|
15
75
|
>
|
|
16
76
|
> **`alpha.3` is a breaking change.** A config naming `openai-http` with a
|
|
17
|
-
> remote base URL and an offer scope wider than `
|
|
77
|
+
> remote base URL and an offer scope wider than `private` is narrowed to `private`
|
|
18
78
|
> until you acknowledge the spend and set a daily ceiling:
|
|
19
79
|
> `byollm offer <backend> public --cap <cents>`. Local base URLs are
|
|
20
80
|
> unaffected. `byollm backends` shows the cost class per route.
|
|
21
81
|
|
|
82
|
+
<!-- release-note 0.1.0-alpha.21 -->
|
|
83
|
+
> [!NOTE]
|
|
84
|
+
> **`0.1.0-alpha.20` is not a complete release — do not pin it.** Four
|
|
85
|
+
> packages published and `@byollm/server` did not: a Sigstore
|
|
86
|
+
> transparency-log 409 on its provenance attestation. The workflow's
|
|
87
|
+
> "already published" guard correctly refuses to resume a partial publish,
|
|
88
|
+
> so `0.1.0-alpha.71` is that release, whole.
|
|
89
|
+
>
|
|
90
|
+
> If you run the Supabase adapter, `alpha.21` needs
|
|
91
|
+
> `20260819010000_completed_by_lease_id.sql`: alpha.19 shipped §3.6's
|
|
92
|
+
> ordering without the column it stores the grant in.
|
|
93
|
+
|
|
94
|
+
<!-- release-note 0.1.0-alpha.40 -->
|
|
95
|
+
**`byollm install` — stop keeping a terminal open.** The daemon can now run
|
|
96
|
+
under your computer's own supervisor and restart itself if it stops: a launchd
|
|
97
|
+
agent on macOS, a `systemd --user` unit on Linux, a logon task on Windows. All
|
|
98
|
+
user-level — no root, no system directories, and `byollm uninstall` takes it
|
|
99
|
+
away. `byollm status` gained a line saying whether it is actually supervised
|
|
100
|
+
right now, including the state that matters most: installed but not running,
|
|
101
|
+
which looks fine from an app's dashboard and serves nothing.
|
|
102
|
+
|
|
103
|
+
If you are running via `npx`, install properly first (`npm install -g
|
|
104
|
+
byollm@alpha`) — `install` refuses to supervise a copy in npx's cache, because
|
|
105
|
+
npm deletes that directory and the service would fail at some later boot.
|
|
106
|
+
|
|
107
|
+
<!-- release-note 0.1.0-alpha.41 -->
|
|
108
|
+
**`onNoRunner` takes a string.** Your fallback answer is your own value, not
|
|
109
|
+
wire data, and handing back a whole result record for it was ceremony — the
|
|
110
|
+
README's own example got the shape wrong, which is how this was found.
|
|
111
|
+
|
|
112
|
+
```ts
|
|
113
|
+
const { outcome, fallback } = await job.result({
|
|
114
|
+
onNoRunner: () => runOnHostedModel(transcript),
|
|
115
|
+
});
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Whatever you return, `result()` labels it `fallback: true` — the stamp is
|
|
119
|
+
applied by the wait, not taken from you, so an answer that did not run on
|
|
120
|
+
somebody's device cannot be reported as though it did (`FALLBACK_LABELED`).
|
|
121
|
+
Both delivery channels do it, polling and Supabase Realtime. Records still
|
|
122
|
+
work; they just get labelled too.
|
|
123
|
+
|
|
22
124
|
# `byollm`
|
|
23
125
|
|
|
24
126
|
What end users run. Connects **outbound** to an app you trust, claims only the
|
|
@@ -79,12 +181,48 @@ password and never accepts a pasted secret.
|
|
|
79
181
|
}
|
|
80
182
|
```
|
|
81
183
|
|
|
82
|
-
A job's `kind` selects a route **you defined
|
|
83
|
-
URL, a path or a flag — there is no field on the wire for any of
|
|
184
|
+
A job's `kind` selects a route **you defined**, and a job can never name a
|
|
185
|
+
model, a URL, a path or a flag — there is no field on the wire for any of
|
|
186
|
+
them. It cannot name one of your services either: a site declares what it
|
|
187
|
+
*needs*, you decide which of your services answers that need, and the job
|
|
188
|
+
carries the site's word for the need rather than yours for the answer.
|
|
189
|
+
|
|
190
|
+
`byollm services` shows what is configured, what is healthy, what each one
|
|
191
|
+
answers, and which is your own default. A service that is down is never
|
|
192
|
+
advertised, so you never get work you cannot run.
|
|
193
|
+
|
|
194
|
+
## Keep it running
|
|
195
|
+
|
|
196
|
+
By default `byollm connect` and `byollm run` hold a terminal — close the
|
|
197
|
+
window and the device stops serving. Nothing breaks (your pairings live in
|
|
198
|
+
`~/.byollm/pairings.json` and survive), but the device goes quiet without
|
|
199
|
+
telling anyone, and if it is on a team's roster, the first person to notice is
|
|
200
|
+
a teammate whose job did not run.
|
|
201
|
+
|
|
202
|
+
```bash
|
|
203
|
+
byollm install # keep running in the background, and restart if it stops
|
|
204
|
+
byollm status # says whether it is actually supervised right now
|
|
205
|
+
byollm uninstall
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
It installs at the user level on every platform — a launchd `LaunchAgent` on
|
|
209
|
+
macOS, a `systemd --user` unit on Linux, a logon task on Windows. No root, no
|
|
210
|
+
system directories: it runs as you, it stops when you say so, and you can read
|
|
211
|
+
every file it wrote. Output goes to `~/.byollm/service.log` on all three.
|
|
212
|
+
|
|
213
|
+
Two things worth knowing:
|
|
84
214
|
|
|
85
|
-
`byollm
|
|
86
|
-
|
|
87
|
-
|
|
215
|
+
- **Install `byollm` properly first.** `byollm install` refuses to supervise a
|
|
216
|
+
copy running from `npx`'s cache, because npm deletes that directory without
|
|
217
|
+
warning and the service would stop working at some later boot with nothing
|
|
218
|
+
to show for it. `npm install -g byollm@alpha` first.
|
|
219
|
+
- **On Linux, `systemd --user` stops when you log out** unless lingering is
|
|
220
|
+
enabled. `byollm install` prints the one command for that rather than
|
|
221
|
+
running it — it changes something outside your session, so it is your call.
|
|
222
|
+
|
|
223
|
+
`byollm status` reports three states, not two: running under supervision,
|
|
224
|
+
*installed but not running* (the one that looks fine from an app's dashboard
|
|
225
|
+
and serves nothing), and not installed at all.
|
|
88
226
|
|
|
89
227
|
## The trust surface
|
|
90
228
|
|
|
@@ -92,32 +230,69 @@ The meter is the product, and it gets the same care as the loop.
|
|
|
92
230
|
|
|
93
231
|
```bash
|
|
94
232
|
byollm status # what's connected, what's running, what you've done for others
|
|
233
|
+
byollm sites # which sites this device serves, and which are waiting on you
|
|
234
|
+
byollm approve <site> # say yes to a site that asked
|
|
95
235
|
byollm log # every prompt that has ever run here
|
|
96
236
|
byollm log --full # the whole text, not the first line
|
|
97
237
|
byollm pause # stop claiming work
|
|
98
238
|
byollm resume
|
|
99
239
|
```
|
|
100
240
|
|
|
241
|
+
### A site cannot add itself
|
|
242
|
+
|
|
243
|
+
Pairing is with an *app* — a hub, a relay, your own server — and one pairing
|
|
244
|
+
can cover several sites. Which sites arrives on the heartbeat, from the same
|
|
245
|
+
party that routes the work.
|
|
246
|
+
|
|
247
|
+
So a site that turns up after pairing **waits**. It is listed by
|
|
248
|
+
`byollm sites` with its fingerprint, nothing is claimed for it, and it starts
|
|
249
|
+
being served the moment you run `byollm approve <site>`. Compare the
|
|
250
|
+
fingerprint against what the site itself shows you before you do.
|
|
251
|
+
|
|
252
|
+
The reason is narrow and worth stating: the daemon pins each site's keys so
|
|
253
|
+
that the party routing a job cannot choose which key signed it. If that party
|
|
254
|
+
could also *add* a site, it could generate a keypair, announce it, sign its
|
|
255
|
+
own work with it, and every pin check downstream would pass — because the
|
|
256
|
+
list they check against is the thing it wrote. Approving is the one step it
|
|
257
|
+
cannot perform for you.
|
|
258
|
+
|
|
259
|
+
A key that moves under a site you already approved is refused rather than
|
|
260
|
+
replaced, for the life of the pairing — including when the site leaves the
|
|
261
|
+
list and comes back. Rotation is an explicit path, not a silent swap.
|
|
262
|
+
|
|
101
263
|
Every prompt is appended to `~/.byollm/ingress.log` **before** it executes, so
|
|
102
|
-
a job that wedges the
|
|
264
|
+
a job that wedges the computer still leaves a record of what it was. The file is
|
|
103
265
|
JSONL, `0600`, and yours to read, grep and delete.
|
|
104
266
|
|
|
105
|
-
## Lending your
|
|
267
|
+
## Lending your computer to other people
|
|
106
268
|
|
|
107
269
|
Off by default. A fresh daemon runs your work and nobody else's.
|
|
108
270
|
|
|
109
271
|
```bash
|
|
110
|
-
byollm
|
|
111
|
-
byollm
|
|
112
|
-
byollm offer openai public --cap 250 # share a paid backend, with a ceiling
|
|
113
|
-
byollm disallow https://your-app.com alice
|
|
272
|
+
byollm offer qwen team --cap 250 # share a service with your team
|
|
273
|
+
byollm offer qwen private # back to your work only
|
|
114
274
|
```
|
|
115
275
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
276
|
+
Who "your team" is lives in the dashboard, not on this machine. Add somebody
|
|
277
|
+
to your roster there and their next job can run here; remove them and their
|
|
278
|
+
next claim fails, including work already queued.
|
|
279
|
+
|
|
280
|
+
`byollm allow` and `byollm disallow` are gone. A device no longer keeps its own
|
|
281
|
+
list of who may use it — that list had to be kept in step with the roster by
|
|
282
|
+
hand, and two lists that must agree are one list and a bug waiting. Both
|
|
283
|
+
commands leave a tombstone pointing at where the decision lives now.
|
|
284
|
+
|
|
285
|
+
What replaced it is stronger than a list. Every teammate's job arrives carrying
|
|
286
|
+
a **grant**: short-lived, single-use, signed by the control plane your device
|
|
287
|
+
pinned when it paired, naming the site, the person, the job and the service to
|
|
288
|
+
run. Your daemon checks that signature itself before it runs anything, so an
|
|
289
|
+
app saying "this runner is allowed" is still not enough — and neither is a
|
|
290
|
+
relay saying it. Jobs from other people are additionally rate-limited, capped
|
|
291
|
+
daily, given a tighter resource budget, and their prompts are reduced to hashes
|
|
292
|
+
after 7 days so you are not holding anybody else's content indefinitely.
|
|
293
|
+
|
|
294
|
+
`public` is not a scope. It was one until 2026-08-26, and `byollm offer <service>
|
|
295
|
+
public` now refuses by name rather than silently doing something else.
|
|
121
296
|
|
|
122
297
|
**Your subscription-backed models are never part of this.** `claude-cli` is
|
|
123
298
|
locked to your own work — a protocol rule, not a setting you can change.
|