@basein/runner 0.2.8 → 0.2.11
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 +86 -22
- package/dist/auth/client.d.ts +40 -1
- package/dist/auth/client.js +77 -9
- package/dist/bin/bir-hooks.d.ts +18 -3
- package/dist/bin/bir-hooks.js +124 -38
- package/dist/bin/bir-scenario.d.ts +18 -2
- package/dist/bin/bir-scenario.js +374 -4
- package/dist/bin/bir.d.ts +12 -0
- package/dist/bin/bir.js +501 -81
- package/dist/bin/investigate.js +1 -1
- package/dist/bin/scenario-edit.d.ts +173 -0
- package/dist/bin/scenario-edit.js +771 -0
- package/dist/bin/setup.d.ts +72 -0
- package/dist/bin/setup.js +286 -0
- package/dist/config/adapters/claude-code.d.ts +90 -4
- package/dist/config/adapters/claude-code.js +164 -16
- package/dist/config/generate.d.ts +114 -1
- package/dist/config/generate.js +106 -3
- package/dist/control/client.d.ts +5 -0
- package/dist/control/client.js +8 -0
- package/dist/control/daemon.d.ts +116 -0
- package/dist/control/daemon.js +339 -0
- package/dist/control/discovery.d.ts +26 -0
- package/dist/control/discovery.js +41 -9
- package/dist/control/ensure-hook.d.ts +39 -0
- package/dist/control/ensure-hook.js +98 -0
- package/dist/control/paths.d.ts +14 -0
- package/dist/control/paths.js +20 -0
- package/dist/control/server.d.ts +28 -0
- package/dist/control/server.js +15 -2
- package/dist/proxy/session.d.ts +8 -1
- package/dist/proxy/session.js +28 -6
- package/docs/calculatedReplay.md +51 -0
- package/docs/calculatedReplayGuide.md +471 -74
- package/docs/installRun.md +457 -111
- package/docs/loginWeb.md +1 -1
- package/docs/quickstart.md +195 -158
- package/package.json +2 -1
- package/scripts/install.ps1 +669 -0
- package/scripts/install.sh +586 -0
package/docs/installRun.md
CHANGED
|
@@ -10,9 +10,9 @@ bare to recording, how it gets updated, and what to do when there is no registry
|
|
|
10
10
|
to reach. For one person setting up one machine, [quickstart.md](quickstart.md)
|
|
11
11
|
is shorter and assumes nothing.
|
|
12
12
|
|
|
13
|
-
**Status.** Everything here is implemented
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
**Status.** Everything here is implemented except the last item of §7: a
|
|
14
|
+
non-interactive grant for machines with no person attached needs a service-side
|
|
15
|
+
decision rather than a change here.
|
|
16
16
|
|
|
17
17
|
---
|
|
18
18
|
|
|
@@ -22,10 +22,10 @@ service-side decision rather than a change here.
|
|
|
22
22
|
|---|---|---|
|
|
23
23
|
| Runtime dependencies | **none** | The tarball is self-contained. No lockfile resolution, no transitive CVEs, no native builds, and offline install just works. |
|
|
24
24
|
| Module format | pure ESM (`"type": "module"`) | No dual-build gymnastics. |
|
|
25
|
-
| Node floor | `>=20` |
|
|
25
|
+
| Node floor | `>=20` | The one prerequisite — and the served line installs a private copy when the machine has none, or only one the hook must not pin: a version-managed, Homebrew or snap Node, or one with no npm beside it (§5.1). |
|
|
26
26
|
| Build | `tsc` → `dist/`, `prepublishOnly` wired | `npm publish` cannot ship a stale `dist/`. |
|
|
27
27
|
| Bins | `bir`, `bir-proxy`, `bir-hooks`, `bir-scenario`, all with `#!/usr/bin/env node` | npm creates the shims, including `.cmd`/`.ps1` on Windows. |
|
|
28
|
-
| Tarball |
|
|
28
|
+
| Tarball | 120 files, ~390 kB | `dist/` + types + docs + `scripts/` (the install scripts, so a tarball can bootstrap an offline machine). Sourcemaps and `docs/next.md` are excluded. |
|
|
29
29
|
|
|
30
30
|
A zero-dependency, pure-JS, four-bin package is the best-behaved thing npm
|
|
31
31
|
distributes. The delivery does not need to be clever.
|
|
@@ -34,7 +34,7 @@ distributes. The delivery does not need to be clever.
|
|
|
34
34
|
|
|
35
35
|
`files` carries two negations:
|
|
36
36
|
|
|
37
|
-
- `!dist/**/*.map` —
|
|
37
|
+
- `!dist/**/*.map` — 106 files, ~490 kB. The maps carry no `sourcesContent`, and
|
|
38
38
|
`src/` is not published, so they cannot resolve a single stack frame. They were
|
|
39
39
|
bytes that did nothing on every install and every `npx` cold start.
|
|
40
40
|
- `!docs/next.md` — a working scratchpad, not documentation.
|
|
@@ -59,7 +59,7 @@ documentation exists.
|
|
|
59
59
|
|
|
60
60
|
The package is public; the repository is private. Those are separate decisions
|
|
61
61
|
and this project makes them differently. Nothing operational leaks either way —
|
|
62
|
-
the service is gated by
|
|
62
|
+
the service is gated by its address plus credentials.
|
|
63
63
|
|
|
64
64
|
---
|
|
65
65
|
|
|
@@ -154,31 +154,159 @@ Three invocation modes, one flag each:
|
|
|
154
154
|
|
|
155
155
|
| Mode | Flag | Entry |
|
|
156
156
|
|---|---|---|
|
|
157
|
-
| **npx** | *(default)* | `npx -y -p @basein/runner@0.
|
|
158
|
-
| **global** | `--global` | `<node> <prefix>/node_modules/@basein/runner/dist/bin/bir-proxy.js …` |
|
|
157
|
+
| **npx** | *(default)* | `npx -y -p @basein/runner@0.2.9 bir-proxy …` |
|
|
158
|
+
| **global** | `--global` | `<node> <prefix>/lib/node_modules/@basein/runner/dist/bin/bir-proxy.js …` (Windows: `<prefix>/node_modules/@basein/runner/…`) |
|
|
159
159
|
| **local** | `--local` | `<node> <this checkout>/dist/bin/bir-proxy.js …` |
|
|
160
160
|
|
|
161
|
+
`<node>` is the absolute path of the Node that ran the install, symlinks
|
|
162
|
+
resolved. Under the served line (§5.1) that is the machine's own Node when it
|
|
163
|
+
qualifies (20 or newer, not under a version manager, npm beside it), else the
|
|
164
|
+
private Node in `~/.baseinstrunner/node/v<V>/`; `<prefix>` is
|
|
165
|
+
`~/.baseinstrunner/npm` either way. By hand it is whatever `npm i -g` used.
|
|
166
|
+
npm's global layout differs by platform:
|
|
167
|
+
the package is at `<prefix>/lib/node_modules/@basein/runner` on macOS and Linux
|
|
168
|
+
and at `<prefix>/node_modules/@basein/runner` on Windows.
|
|
169
|
+
|
|
161
170
|
**The npx spec is pinned.** It was not, and that was the one real defect in the
|
|
162
171
|
original packaging story: two runners provisioned a week apart could run
|
|
163
172
|
different proxy versions against the same service, with nothing in a recording to
|
|
164
173
|
say which.
|
|
165
174
|
|
|
166
|
-
**`--global` is what a fleet wants**, and it is what
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
175
|
+
**`--global` is what a fleet wants**, and it is what `bir setup` uses: from an
|
|
176
|
+
installed package it runs `bir install --global --replay` in-process (from a
|
|
177
|
+
checkout, `--local`, since `--global` refuses one). It removes the registry from the hot
|
|
178
|
+
path entirely: no resolution before the MCP server can start, no dependence on
|
|
179
|
+
the MCP host seeing the same `PATH` as the shell that installed things — which
|
|
180
|
+
GUI-launched hosts on Windows do not — and it works with no network at all.
|
|
171
181
|
|
|
172
182
|
It also survives upgrades. The path
|
|
173
|
-
`<prefix>/node_modules/@basein/runner/dist/bin/bir-proxy.js` contains no
|
|
174
|
-
so `npm i -g @basein/runner
|
|
175
|
-
changing the path. **A pinned-npx install, by contrast, must re-run
|
|
176
|
-
on every upgrade** to move the pin.
|
|
183
|
+
`<prefix>/lib/node_modules/@basein/runner/dist/bin/bir-proxy.js` contains no
|
|
184
|
+
version, so `npm i -g @basein/runner@<version>` changes what that path points at
|
|
185
|
+
without changing the path. **A pinned-npx install, by contrast, must re-run
|
|
186
|
+
`bir install` on every upgrade** to move the pin.
|
|
187
|
+
|
|
188
|
+
A flag-less `bir install` keeps the invocation the project was installed with
|
|
189
|
+
(`invocation` in its record, §4.3): a `bir install --replay` after `bir setup`
|
|
190
|
+
keeps the absolute paths and the command SessionStart hook instead of quietly
|
|
191
|
+
turning every entry into a pinned-npx one.
|
|
192
|
+
|
|
193
|
+
**An entry whose runner moved is re-wrapped.** `bir install` compares each
|
|
194
|
+
wrapped entry — its Node path, its proxy path, or its pinned npx version — with
|
|
195
|
+
the package it is running from, and regenerates any that disagree from the
|
|
196
|
+
stashed original: `~ fleet — re-wrapping: its runner moved (was …)`. The
|
|
197
|
+
scenario entry gets the same check. That is what lets `bir setup` be the upgrade
|
|
198
|
+
path even when the package changed prefix (from the machine's npm prefix to the
|
|
199
|
+
private one) or the Node moved (a version manager, a reinstall), instead of
|
|
200
|
+
printing "already wrapped" about an entry the host would keep spawning from a
|
|
201
|
+
path that is gone.
|
|
177
202
|
|
|
178
203
|
`--global` refuses to run from a checkout: it asserts a path that outlives the
|
|
179
204
|
shell, and a checkout's `dist/` moves with the checkout. Use `--local` when you
|
|
180
205
|
mean the checkout on purpose.
|
|
181
206
|
|
|
207
|
+
### 4.1 Hooks go to `.claude/settings.local.json`
|
|
208
|
+
|
|
209
|
+
Claude Code's hooks are written to `<project>/.claude/settings.local.json`, not
|
|
210
|
+
to the `.claude/settings.json` a team commits. They carry a bearer token and an
|
|
211
|
+
absolute path to this machine's Node; neither belongs in a teammate's clone.
|
|
212
|
+
Inside a git repository `bir` adds the line `.claude/settings.local.json` to
|
|
213
|
+
`.git/info/exclude` — the person's own, untracked list, never a `.gitignore` the
|
|
214
|
+
team would see in a diff — and says so. A worktree's `gitdir:` pointer is
|
|
215
|
+
followed. Outside a repository, or when the exclude file cannot be written, that
|
|
216
|
+
is a note, not a failed install.
|
|
217
|
+
|
|
218
|
+
An install that finds `bir` hooks in a legacy `.claude/settings.json` removes
|
|
219
|
+
them from there on the way (`- hooks removed from … (they live in
|
|
220
|
+
settings.local.json now)`). `bir uninstall` cleans both files and takes the
|
|
221
|
+
`.claude/settings.local.json` line out of `.git/info/exclude` again. It restores
|
|
222
|
+
a file byte-for-byte only when nothing else edited it since the install; a file
|
|
223
|
+
it created — usually `settings.local.json` itself — stays behind, empty of our
|
|
224
|
+
entries.
|
|
225
|
+
|
|
226
|
+
With `--replay`, `bir install` does two more things: the `bir` scenario server
|
|
227
|
+
goes into `.mcp.json` (project scope — never `~/.claude.json`, whose per-project
|
|
228
|
+
map Claude Code keys with forward slashes, so an entry written from a Windows
|
|
229
|
+
cwd lands under a key it never reads), and `bir` is merged into
|
|
230
|
+
`enabledMcpjsonServers` in `settings.local.json`, so Claude Code never shows its
|
|
231
|
+
approval dialog for the scenario server. A "No" there leaves a direct replay
|
|
232
|
+
with nowhere to deliver its results, and nothing to say so.
|
|
233
|
+
`bir uninstall --replay` removes only the scenario server; the proxies, the
|
|
234
|
+
hooks and the pre-approval stay as they are.
|
|
235
|
+
|
|
236
|
+
### 4.2 SessionStart is a command hook
|
|
237
|
+
|
|
238
|
+
For `--global` and `--local` installs the SessionStart hook is a command hook in
|
|
239
|
+
Claude Code's exec form — `command` spawned directly with `args` as its argument
|
|
240
|
+
vector, no shell in between, so a path with a space needs no quoting and it runs
|
|
241
|
+
the same under Git Bash, PowerShell and cmd.exe:
|
|
242
|
+
|
|
243
|
+
```json
|
|
244
|
+
{ "type": "command", "command": "<node>", "args": ["<dist>/bin/bir-hooks.js", "ensure"], "timeout": 30 }
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
`bir-hooks ensure` reads the SessionStart payload from stdin, starts the
|
|
248
|
+
recorder for this directory in the background when none answers `/health`, and
|
|
249
|
+
relays the payload to it. It *replaces* the HTTP SessionStart hook rather than
|
|
250
|
+
sitting beside it: Claude Code runs every hook of an event in parallel, and an
|
|
251
|
+
HTTP hook racing the daemon it depends on would fail on every first session of
|
|
252
|
+
the day. It prints nothing on stdout — which Claude Code adds to the model's
|
|
253
|
+
context — unless the control server answered with something, and it always exits
|
|
254
|
+
0: a recorder that would not start is one line on stderr and a session that
|
|
255
|
+
records Tier 2, exactly what happened before the hook existed.
|
|
256
|
+
|
|
257
|
+
Every other event (`UserPromptSubmit`, `PreToolUse`, `PostToolUse`, `Stop`,
|
|
258
|
+
`SessionEnd`, …) stays an HTTP hook aimed at the control server, with the
|
|
259
|
+
project's bearer token in its `Authorization` header.
|
|
260
|
+
|
|
261
|
+
A pinned-npx install keeps the HTTP SessionStart hook: resolving a package
|
|
262
|
+
inside a hook is too slow to start a recorder from, so those installs need a
|
|
263
|
+
recorder somebody started (`bir up`).
|
|
264
|
+
|
|
265
|
+
`bir doctor` checks that the hook's `command` still exists and, when it does
|
|
266
|
+
not, reports:
|
|
267
|
+
|
|
268
|
+
```
|
|
269
|
+
the SessionStart hook in <file> points at a Node that is gone (<command>) — run `bir setup` here again
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
That is the failure a version manager's per-shell directory produces, and it is
|
|
273
|
+
why the bootstrap script installs a private Node rather than pin one that lives
|
|
274
|
+
under `fnm_multishells` or `.nvm` (§5.1).
|
|
275
|
+
|
|
276
|
+
### 4.3 One port and one token per project
|
|
277
|
+
|
|
278
|
+
`~/.baseinstrunner/installed.json` keeps a record per project directory:
|
|
279
|
+
|
|
280
|
+
```
|
|
281
|
+
projects[<normalised cwd>] = { port, token, replay, invocation }
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
**Port.** The first project on a machine gets 53411; the next gets the lowest
|
|
285
|
+
port above it that no other project holds *and* that is free right now — it is
|
|
286
|
+
bound once to check, because 53411 sits inside the ranges Hyper-V and WSL
|
|
287
|
+
reserve on Windows, and a recorder told to use an excluded port exits while every
|
|
288
|
+
session records Tier 2. Once recorded the port is kept; the hooks name it.
|
|
289
|
+
`bir setup --port <n>` (or `bir install --port <n>`) overrides. A *background*
|
|
290
|
+
recorder — the one the hook, `bir up` or `bir setup` starts — for a project with
|
|
291
|
+
a recorded port (or with `BIR_CONTROL_PORT` set) does not fall back to an
|
|
292
|
+
ephemeral one: it exits 1 with `control.port_busy` on its log, whose `fix` reads
|
|
293
|
+
"port N is held by another program or excluded by the system — run `bir setup
|
|
294
|
+
--port <another>` in this directory", and the hook turns that into one stderr
|
|
295
|
+
line, rather than let the project's hooks post to whatever else is listening
|
|
296
|
+
there. `bir-hooks` run by hand in a terminal still falls back to an ephemeral
|
|
297
|
+
port, and says so on the same log line. Two projects used to share 53411 and one
|
|
298
|
+
project's steps landed in the other's run; this is the fix.
|
|
299
|
+
|
|
300
|
+
**Invocation.** `global`, `local` or `npx` — what the entries were generated
|
|
301
|
+
with, so a later flag-less `bir install` keeps the same shape (§4).
|
|
302
|
+
|
|
303
|
+
**Token.** Generated once per project, so a token in one project's settings
|
|
304
|
+
file opens that project's recorder and no other. A record written by 0.2.8 has
|
|
305
|
+
none and falls back to the machine-wide `token` in the same file.
|
|
306
|
+
|
|
307
|
+
**Replay.** The switches `bir replay on|off|allow …` and
|
|
308
|
+
`bir setup --allow-servers` store, read by the recorder at start (§5.1).
|
|
309
|
+
|
|
182
310
|
---
|
|
183
311
|
|
|
184
312
|
## 5. Provisioning a runner
|
|
@@ -188,72 +316,194 @@ runs":
|
|
|
188
316
|
|
|
189
317
|
| Scope | How often | What it does |
|
|
190
318
|
|---|---|---|
|
|
191
|
-
| **machine** | once | Node ≥ 20
|
|
192
|
-
| **account** | once per user |
|
|
193
|
-
| **project** | once per repo | `bir install --global` rewrites
|
|
194
|
-
| **session** | while recording | `bir-hooks`
|
|
319
|
+
| **machine** | once | Node ≥ 20 and the package. The served line uses the machine's Node when it qualifies and otherwise puts a private one under `~/.baseinstrunner/node/`; the package goes under `~/.baseinstrunner/npm/`, and `bir` / `bir-hooks` launchers in `~/.baseinstrunner/bin/`, first on the user PATH |
|
|
320
|
+
| **account** | once per user | the sign-in → `~/.baseinstrunner/credentials.json` (0600); the service address → `~/.baseinstrunner/config.json`. `bir setup` stores both; so does `bir login` |
|
|
321
|
+
| **project** | once per repo | `bir setup` in that directory: `bir install --global --replay` (`--local` from a checkout) rewrites the MCP entries, wires the hooks into `.claude/settings.local.json`, records the port, token and invocation |
|
|
322
|
+
| **session** | while recording | the recorder — `bir-hooks` as a background process — started by the SessionStart hook, discovered by `sha256(normalised cwd)` |
|
|
195
323
|
|
|
196
|
-
That cwd keying is why
|
|
324
|
+
That cwd keying is why the recorder must run in the *same directory* the agent
|
|
197
325
|
session runs in. A control server started elsewhere is invisible, and the run
|
|
198
|
-
silently degrades to Tier 2.
|
|
326
|
+
silently degrades to Tier 2. It is also why the hook is what starts it: the hook
|
|
327
|
+
already runs in the right directory, with the right environment, at exactly the
|
|
328
|
+
right moment.
|
|
199
329
|
|
|
200
330
|
### 5.1 One command
|
|
201
331
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
and project scope together:
|
|
332
|
+
The console's *Set up the runner* page shows one line, with a fresh one-time
|
|
333
|
+
setup token filled in, to paste into a terminal opened in the project folder:
|
|
205
334
|
|
|
206
335
|
```powershell
|
|
207
|
-
|
|
336
|
+
$env:BIR_SETUP_TOKEN="<token>"; irm https://api.bi2202.com/install.ps1 | iex
|
|
208
337
|
```
|
|
209
338
|
|
|
210
|
-
```
|
|
211
|
-
|
|
339
|
+
```cmd
|
|
340
|
+
powershell -NoProfile -Command "$env:BIR_SETUP_TOKEN='<token>'; irm https://api.bi2202.com/install.ps1 | iex"
|
|
212
341
|
```
|
|
213
342
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
(`-Tarball` / `BIR_TARBALL`) instead of the registry.
|
|
343
|
+
```bash
|
|
344
|
+
curl -fsSL https://api.bi2202.com/install.sh | BIR_SETUP_TOKEN="<token>" sh
|
|
345
|
+
```
|
|
218
346
|
|
|
219
|
-
|
|
220
|
-
|
|
347
|
+
The first is Windows PowerShell, the second the same line from cmd.exe (a
|
|
348
|
+
terminal opened from Start is often cmd), the third macOS and Linux. Without the
|
|
349
|
+
variable the line still installs everything; `bir setup` then opens the browser
|
|
350
|
+
for the device flow — even under the scripts, where its output is a pipe — and
|
|
351
|
+
over SSH prints the link instead (§7).
|
|
352
|
+
|
|
353
|
+
The scripts are [`scripts/install.ps1`](../scripts/install.ps1) and
|
|
354
|
+
[`scripts/install.sh`](../scripts/install.sh); they replace
|
|
355
|
+
`install-runner.ps1` and `install-runner.sh`, and they ship in the tarball. The
|
|
356
|
+
API serves copies at `/install.ps1` and `/install.sh` with two placeholders
|
|
357
|
+
filled in: `__BIR_AUTH_URL__` (the service's `PUBLIC_API_URL`, else the origin
|
|
358
|
+
the request arrived on) and `__BIR_VERSION__` (`RUNNER_VERSION`, default
|
|
359
|
+
`latest`). Run straight from the repository or the tarball the placeholders are
|
|
360
|
+
unreplaced, which counts as unset: `BIR_AUTH_URL` must then be in the
|
|
361
|
+
environment, and the version is `latest`.
|
|
362
|
+
|
|
363
|
+
What they do, in order, the same on every platform:
|
|
364
|
+
|
|
365
|
+
0. **Stop before any download** if the current directory is the home folder or
|
|
366
|
+
a drive root: "This is your home folder. Open a terminal in the project you
|
|
367
|
+
start Claude Code in (cd there) and paste the line again." Nothing is
|
|
368
|
+
installed, nobody is signed in and the token is unused, so the fix is
|
|
369
|
+
exactly that — `cd` to the project and paste the *same* line
|
|
370
|
+
(`BIR_PROJECT=<dir>` names the project instead; the script `cd`s there
|
|
371
|
+
first). Move `BIR_SETUP_TOKEN` out of the environment into a local variable,
|
|
372
|
+
so npm, the Claude Code installer and the `claude` the person starts next
|
|
373
|
+
never see it.
|
|
374
|
+
1. **Node.** A Node ≥ 20 already on PATH is used by its real binary — unless
|
|
375
|
+
that binary is one the hook must not pin: under a version manager's
|
|
376
|
+
per-shell or per-version directory (nvm, fnm, Volta, asdf, mise and nodenv
|
|
377
|
+
on every platform; Scoop on Windows) or, on macOS and Linux, a Homebrew Node
|
|
378
|
+
(`*/Cellar/*` — a `brew upgrade` moves the path the hook pinned) or a snap
|
|
379
|
+
one. Both scripts also insist on npm's `npm-cli.js` beside the binary
|
|
380
|
+
(`lib/node_modules/npm/bin/` on macOS and Linux, `node_modules\npm\bin\` on
|
|
381
|
+
Windows). Otherwise a private Node is installed instead: pinned version,
|
|
382
|
+
digest embedded in the script and checked, unpacked to
|
|
383
|
+
`~/.baseinstrunner/node/v<V>/`, never put on PATH. The SessionStart hook
|
|
384
|
+
pins an absolute node path, and a path that changes with the shell is a
|
|
385
|
+
hook that fails every session. `BIR_NODE_ARCHIVE=<file>` uses a downloaded
|
|
386
|
+
archive instead of nodejs.org.
|
|
387
|
+
2. **The package.** `@basein/runner@<version>` into the private prefix
|
|
388
|
+
`~/.baseinstrunner/npm`, always — never the machine's npm prefix, which is
|
|
389
|
+
root-owned under a `.pkg` or apt Node and per-version under nvm, Volta, Scoop
|
|
390
|
+
or asdf, where a global install vanishes on the next switch.
|
|
391
|
+
`BIR_TARBALL=<file.tgz>` instead of the registry. Launchers go into
|
|
392
|
+
`~/.baseinstrunner/bin`, which is put first on the user PATH; in PowerShell
|
|
393
|
+
`bir` then resolves to `bir.cmd`, so there is no execution-policy change,
|
|
394
|
+
ever. The PowerShell window the line was pasted into gets `bir` in place —
|
|
395
|
+
the script's last two lines are "bir works in every new terminal (and in
|
|
396
|
+
this window when it is PowerShell); `bir doctor` checks the setup" and the
|
|
397
|
+
cmd hint below — but the cmd variant
|
|
398
|
+
runs the script in a child `powershell.exe`, so a cmd window sees `bir` only
|
|
399
|
+
once it is a new window, or after the `set PATH=…` line the script prints
|
|
400
|
+
for it. On macOS and Linux the script prints the
|
|
401
|
+
`export PATH="$HOME/.baseinstrunner/bin:$PATH"` line for the terminal it ran
|
|
402
|
+
in (with `~/.local/bin` added when it just installed Claude Code); every new
|
|
403
|
+
terminal has it from the profile.
|
|
404
|
+
3. **Claude Code**, through its official installer, when `claude` is not on
|
|
405
|
+
PATH. `BIR_NO_CLAUDE=1` skips it. It needs a Claude subscription and asks to
|
|
406
|
+
sign in the first time it starts.
|
|
407
|
+
4. **`bir setup --auth-url <url> --project <cwd>`**, with the token in that
|
|
408
|
+
child's environment only. It signs in, stores the service address, runs
|
|
409
|
+
`bir install --global --replay`, starts the recorder in the background, and
|
|
410
|
+
prints a summary that names the account, the wrapped servers (`(no MCP
|
|
411
|
+
servers here)` and "steer mode only" mean none in *any* scope — `.mcp.json`,
|
|
412
|
+
and the user and local scopes of `~/.claude.json`), the recorder's pid and
|
|
413
|
+
log file, and the one thing left to do: `claude`.
|
|
414
|
+
|
|
415
|
+
Both scripts are idempotent, neither prompts (under `curl | sh` stdin is the
|
|
416
|
+
script itself), and `HTTPS_PROXY`, `HTTP_PROXY`, `NO_PROXY` and
|
|
417
|
+
`NODE_EXTRA_CA_CERTS` pass through — to the scripts' own downloads and to every
|
|
418
|
+
node and npm they run. The one fetch they cannot reach is the `irm` that fetches
|
|
419
|
+
the script itself: Windows PowerShell 5.1's `irm` uses the system (WinINET)
|
|
420
|
+
proxy, not `HTTPS_PROXY`. `bir setup` exits 1 when the recorder reports it has
|
|
421
|
+
no session to record with, so a provisioning script can gate on the exit code.
|
|
422
|
+
|
|
423
|
+
The scripts never reach `bir setup` from the home folder (step 0). `bir setup`
|
|
424
|
+
run *by hand* there, with the runner already installed, is different: it keeps
|
|
425
|
+
the sign-in, wires nothing, prints the two commands left — `cd <project>` and
|
|
426
|
+
`bir setup` — and exits 0. The second needs no token; the session is cached.
|
|
427
|
+
That is also how a second project on the same machine is added.
|
|
428
|
+
|
|
429
|
+
Fleet switches on `bir setup`: `--allow-servers a,b` stores the servers replay
|
|
430
|
+
may call unattended for this project; `bir replay allow|on|off|status` changes
|
|
431
|
+
it later. Both live in the project record (§4.3), so a recorder the hook starts
|
|
432
|
+
— with Claude Code's environment, not the operator's terminal — keeps them;
|
|
433
|
+
`BIR_REPLAY`, `BIR_REPLAY_ALLOW_SERVERS` and `BIR_MIN_STEER_SIMILARITY` in the
|
|
434
|
+
recorder's environment still override, and `bir doctor` says which source is in
|
|
435
|
+
effect. `--no-replay` records without the scenario server; `--no-daemon` wires
|
|
436
|
+
everything and starts nothing; `--no-browser` prints the sign-in link without
|
|
437
|
+
opening one.
|
|
438
|
+
|
|
439
|
+
Two things the scripts deliberately do not do: supervise the recorder (the hook
|
|
440
|
+
does, §5.2) and mint the token (the console does, §7).
|
|
221
441
|
|
|
222
442
|
The PowerShell script is **pure ASCII** on purpose. Windows PowerShell 5.1
|
|
223
443
|
decodes a BOM-less `.ps1` using the machine's ANSI codepage, and PowerShell
|
|
224
444
|
accepts curly quotes as string delimiters — so a UTF-8 em-dash in a comment can
|
|
225
445
|
decode into a quote and swallow the rest of the file. It parsed fine on one
|
|
226
|
-
machine and failed on another before this was fixed. Keep it ASCII.
|
|
446
|
+
machine and failed on another before this was fixed. Keep it ASCII. It also
|
|
447
|
+
never calls `exit` — under `iex` that closes the person's window — and it sets
|
|
448
|
+
strict mode and `$ErrorActionPreference` inside a function so neither leaks into
|
|
449
|
+
the caller's session.
|
|
450
|
+
|
|
451
|
+
### 5.2 Keeping the recorder alive
|
|
452
|
+
|
|
453
|
+
Not needed. The SessionStart hook is `bir-hooks ensure` (§4.2): every Claude
|
|
454
|
+
Code session in the project starts the recorder when there is none, in the
|
|
455
|
+
background, so a recorder that died or was never started is running by the first
|
|
456
|
+
prompt. Nothing has to stay open. `bir up` starts it by hand, `bir up --restart`
|
|
457
|
+
replaces it, `bir down` stops it — it asks first (`POST /control/stop`, which
|
|
458
|
+
finishes the run being written), signals only a pid that answered `/health` with
|
|
459
|
+
the token, and says "forced" when it had to — and `bir status` and `bir doctor`
|
|
460
|
+
show its pid and log. Two Claude Code windows opened together take a lock, so
|
|
461
|
+
only one spawns.
|
|
462
|
+
|
|
463
|
+
On POSIX the daemon is a detached spawn with its stdio ignored. On Windows it is
|
|
464
|
+
launched through `Start-Process -WindowStyle Hidden` in a short-lived
|
|
465
|
+
`powershell.exe`, because a node started under a PowerShell layer carries an
|
|
466
|
+
extra copy of its stdout pipe and a daemon that inherited it would hold that pipe
|
|
467
|
+
open for days.
|
|
468
|
+
|
|
469
|
+
Its audit lines go to `~/.baseinstrunner/logs/<project-key>.log`: the daemon
|
|
470
|
+
opens the file itself, appends, and rolls it to `.1` when it starts and the
|
|
471
|
+
file is past 8 MB. `bir status` names the file. The old shape still works —
|
|
472
|
+
`bir-hooks` in a terminal writes them to that terminal — and is what the hook
|
|
473
|
+
does for you.
|
|
227
474
|
|
|
228
|
-
|
|
475
|
+
```bash
|
|
476
|
+
grep version.skew ~/.baseinstrunner/logs/*.log
|
|
477
|
+
grep run.lossy ~/.baseinstrunner/logs/*.log
|
|
478
|
+
```
|
|
229
479
|
|
|
230
|
-
|
|
231
|
-
|
|
480
|
+
What the hook does not give you is restart-on-crash mid-session, which the
|
|
481
|
+
proxies already survive (they fall to Tier 2 and say so). A service manager is
|
|
482
|
+
optional. If you want one — a machine that records SDK sessions with nobody
|
|
483
|
+
starting Claude Code, or a recorder up before the first session — have it run
|
|
484
|
+
`bir up` in the project directory at logon, and nothing else: `bir up` returns
|
|
485
|
+
once the recorder answers and leaves it running, so a supervisor must not expect
|
|
486
|
+
a long-lived process. The three recipes below are recipes — nothing the package
|
|
487
|
+
ships or its tests cover.
|
|
232
488
|
|
|
233
489
|
**Windows** — a Scheduled Task at logon, with *Start in* set to the project
|
|
234
|
-
directory
|
|
490
|
+
directory (`bir.cmd` is the launcher the served line writes; a by-hand install
|
|
491
|
+
has `bir` wherever npm put it):
|
|
235
492
|
|
|
236
493
|
```powershell
|
|
237
|
-
$action = New-ScheduledTaskAction -Execute
|
|
494
|
+
$action = New-ScheduledTaskAction -Execute "$env:USERPROFILE\.baseinstrunner\bin\bir.cmd" -Argument 'up' -WorkingDirectory 'C:\work\repo'
|
|
238
495
|
$trigger = New-ScheduledTaskTrigger -AtLogOn
|
|
239
|
-
Register-ScheduledTask -TaskName 'bir
|
|
496
|
+
Register-ScheduledTask -TaskName 'bir up (repo)' -Action $action -Trigger $trigger
|
|
240
497
|
```
|
|
241
498
|
|
|
242
|
-
**macOS** — a launchd agent at `~/Library/LaunchAgents/com.basein.
|
|
243
|
-
with `WorkingDirectory` set to the
|
|
244
|
-
then `launchctl load` it.
|
|
245
|
-
|
|
246
|
-
**Linux** — a systemd user unit with `WorkingDirectory=`, plus
|
|
247
|
-
`loginctl enable-linger "$USER"` so it survives logout.
|
|
499
|
+
**macOS** — a launchd agent at `~/Library/LaunchAgents/com.basein.up.plist`
|
|
500
|
+
running `$HOME/.baseinstrunner/bin/bir up` with `WorkingDirectory` set to the
|
|
501
|
+
project and `RunAtLoad` (not `KeepAlive`), then `launchctl load` it.
|
|
248
502
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
```bash
|
|
254
|
-
grep version.skew ~/.baseinstrunner/audit.log
|
|
255
|
-
grep run.lossy ~/.baseinstrunner/audit.log
|
|
256
|
-
```
|
|
503
|
+
**Linux** — a systemd user unit, `Type=oneshot` with `RemainAfterExit=yes` (so
|
|
504
|
+
the unit does not stop and take the recorder down with it), `ExecStart=%h/.baseinstrunner/bin/bir up`,
|
|
505
|
+
`WorkingDirectory=` the project, plus `loginctl enable-linger "$USER"` so it
|
|
506
|
+
runs without a login session.
|
|
257
507
|
|
|
258
508
|
### 5.3 Verify
|
|
259
509
|
|
|
@@ -263,25 +513,54 @@ bir doctor --json # exits non-zero if it is not working right now
|
|
|
263
513
|
|
|
264
514
|
`bir doctor` asks the control server which proxies actually registered; it never
|
|
265
515
|
reads the config file back. That makes it the right gate for a provisioning
|
|
266
|
-
script. `bir status` only describes intent.
|
|
516
|
+
script. `bir status` only describes intent. `bir doctor` also says when there is
|
|
517
|
+
no recorder ("no control server for this directory — recording will be Tier 2
|
|
518
|
+
(MCP only); `bir up` starts one, and the SessionStart hook starts one with each
|
|
519
|
+
session"), when the recorder has nowhere to record to ("the recorder is running
|
|
520
|
+
but has nowhere to send steps — run `bir setup` (or set BIR_AUTH_URL and
|
|
521
|
+
`bir login`), then `bir up --restart`"), and when the SessionStart hook names a
|
|
522
|
+
Node that is gone (§4.2).
|
|
267
523
|
|
|
268
524
|
---
|
|
269
525
|
|
|
270
526
|
## 6. Machines with no registry
|
|
271
527
|
|
|
272
528
|
Zero dependencies makes offline install trivial — no `npm ci --offline`, no
|
|
273
|
-
vendored `node_modules
|
|
529
|
+
vendored `node_modules`. The install script ships in the tarball, takes a local
|
|
530
|
+
tarball instead of the registry and a local Node archive instead of nodejs.org,
|
|
531
|
+
and can skip the Claude Code installer:
|
|
274
532
|
|
|
275
533
|
```bash
|
|
276
534
|
# on a connected machine
|
|
277
|
-
npm pack @basein/runner
|
|
535
|
+
npm pack @basein/runner@<version> # or download the Release asset
|
|
536
|
+
tar -xzf basein-runner-<version>.tgz package/scripts
|
|
537
|
+
# plus the Node archive for the runner's platform from nodejs.org/dist, if the
|
|
538
|
+
# runner has no Node ≥ 20 (the script names the pinned version, 24.21.0)
|
|
278
539
|
|
|
279
540
|
# on the runner
|
|
280
|
-
|
|
541
|
+
BIR_AUTH_URL=https://api.example.com \
|
|
542
|
+
BIR_TARBALL=./basein-runner-<version>.tgz \
|
|
543
|
+
BIR_NODE_ARCHIVE=./node-v24.21.0-linux-x64.tar.gz \
|
|
544
|
+
BIR_NO_CLAUDE=1 sh ./package/scripts/install.sh
|
|
281
545
|
```
|
|
282
546
|
|
|
283
|
-
|
|
284
|
-
|
|
547
|
+
```powershell
|
|
548
|
+
$env:BIR_AUTH_URL='https://api.example.com'; $env:BIR_TARBALL='.\basein-runner-<version>.tgz'; $env:BIR_NODE_ARCHIVE='.\node-v24.21.0-win-x64.zip'; $env:BIR_NO_CLAUDE=1; Get-Content -Raw .\package\scripts\install.ps1 | iex
|
|
549
|
+
```
|
|
550
|
+
|
|
551
|
+
`<version>` is whatever the registry holds (`npm view @basein/runner version`).
|
|
552
|
+
The PowerShell line pipes the file through `iex`, as the served line does: a
|
|
553
|
+
`.ps1` invoked by path is subject to the execution policy, and a default client
|
|
554
|
+
(Restricted) refuses it with "running scripts is disabled". The script is
|
|
555
|
+
written to be run that way — everything inside one function, no `exit`.
|
|
556
|
+
|
|
557
|
+
`BIR_AUTH_URL` is required here because the raw script's placeholder is
|
|
558
|
+
unreplaced (§5.1). Claude Code has to come from somewhere too; `BIR_NO_CLAUDE=1`
|
|
559
|
+
only stops the script from fetching it. The service itself must be reachable —
|
|
560
|
+
"no registry" is not "no network" — or the sign-in has nothing to talk to.
|
|
561
|
+
|
|
562
|
+
`bir setup` passes `--global`, so the resulting configs contain absolute paths
|
|
563
|
+
and never reach for a registry. Nothing further is needed.
|
|
285
564
|
|
|
286
565
|
Note that a private repository's Release assets require a GitHub token to fetch,
|
|
287
566
|
so the tarball has to be carried to the runner by whatever means you already use.
|
|
@@ -292,30 +571,80 @@ so the tarball has to be carried to the runner by whatever means you already use
|
|
|
292
571
|
|
|
293
572
|
`bir login` signs in through the browser — the OAuth 2.0 device grant (RFC 8628,
|
|
294
573
|
[loginWeb.md](loginWeb.md)) — and caches
|
|
295
|
-
`{accessToken, refreshToken, accessExpiresAt, user}` at
|
|
296
|
-
`~/.baseinstrunner/credentials.json`, mode 0600.
|
|
574
|
+
`{accessToken, refreshToken, accessExpiresAt, user, authUrl}` at
|
|
575
|
+
`~/.baseinstrunner/credentials.json`, mode 0600. `bir setup` does the same
|
|
576
|
+
sign-in and stores the service address beside it in
|
|
577
|
+
`~/.baseinstrunner/config.json`, so no process afterwards needs `BIR_AUTH_URL`
|
|
578
|
+
(it still overrides when set).
|
|
579
|
+
|
|
580
|
+
**The setup token.** The served line carries `BIR_SETUP_TOKEN`: a one-time token
|
|
581
|
+
minted by the console's *Set up the runner* page — ten-minute TTL, single use,
|
|
582
|
+
hashed at rest, and minting a new one retires the person's earlier unused ones.
|
|
583
|
+
It travels as an environment-variable assignment rather than an argument so that
|
|
584
|
+
it is never on a command line, where a process listing would show it; the
|
|
585
|
+
variable's *name* is what keeps the pasted line out of PSReadLine's on-disk
|
|
586
|
+
history, which drops lines containing "token" (a `--token` argument would be
|
|
587
|
+
dropped just the same). The script moves it out of the environment as its first
|
|
588
|
+
statement and hands it to `bir setup` through that child's environment only;
|
|
589
|
+
`bir setup` deletes it again the moment it has read it, so the recorder it
|
|
590
|
+
starts never holds it. By hand: `bir setup --token <t>` or
|
|
591
|
+
`bir login --token <t>`. It signs in one person on one machine with no browser
|
|
592
|
+
step; it is not a fleet credential.
|
|
593
|
+
|
|
594
|
+
What `bir setup` says about a token that is not fresh:
|
|
595
|
+
|
|
596
|
+
- pasted a second time after a successful paste — "the setup token was already
|
|
597
|
+
used; keeping the sign-in from before (<email>)", and it goes on; that is the
|
|
598
|
+
upgrade path (§8) and the retry after a failure past the sign-in;
|
|
599
|
+
- used or expired, with no cached sign-in to keep — "this setup token has
|
|
600
|
+
expired or was already used — make a new one on the console's Set up the
|
|
601
|
+
runner page and paste it within ten minutes";
|
|
602
|
+
- not a token at all — "that is not a valid setup token — copy the whole token
|
|
603
|
+
from the console's Set up the runner page".
|
|
604
|
+
|
|
605
|
+
"sign-in failed: …" is reserved for the unexpected, and "not signed in — run
|
|
606
|
+
`bir setup` again (or `bir login`)." for a sign-in that ended with no session.
|
|
607
|
+
|
|
608
|
+
**Credentials are bound to the service that issued them.** `credentials.json`
|
|
609
|
+
carries `authUrl`, and `authenticate()` refuses to use or refresh a session
|
|
610
|
+
against another origin — `auth.url_mismatch` on the audit log, and the runner
|
|
611
|
+
records nothing rather than send a session where it was not minted. A file from
|
|
612
|
+
before the binding is trusted as before. So a copied `credentials.json` works
|
|
613
|
+
only where `config.json` (or `BIR_AUTH_URL`) names the same service, and a stale
|
|
614
|
+
`BIR_AUTH_URL` in a profile file now means "not recording" — `bir setup` warns
|
|
615
|
+
when the environment names a different address than the one being set up, or
|
|
616
|
+
than the stored one (a bare `bir setup` in a second project included), and says
|
|
617
|
+
to remove the variable.
|
|
297
618
|
|
|
298
619
|
**For a machine with a person attached, headless is no longer a problem.** The
|
|
299
620
|
runner prints a link and a short code and waits; the link opens on *any* device,
|
|
300
621
|
so a box with no browser, no display, or only an SSH session is signed in from a
|
|
301
622
|
phone. Nothing is typed into the terminal, so nothing lands in a shell history or
|
|
302
|
-
a CI transcript. `bir login --no-browser`
|
|
303
|
-
the right flag in a provisioning script (
|
|
623
|
+
a CI transcript. `bir login --no-browser` (or `bir setup --no-browser`) skips
|
|
624
|
+
even trying to open one, which is the right flag in a provisioning script (over
|
|
625
|
+
SSH it is the default; a pipe on stdout is not — under the install scripts
|
|
626
|
+
`bir setup` still opens the browser, because somebody just pasted the line). The
|
|
627
|
+
setup token removes even the waiting.
|
|
304
628
|
|
|
305
629
|
For machines with **no person attached**, in order of preference:
|
|
306
630
|
|
|
307
631
|
1. **A service account, signed in once.** Approve the device code for a dedicated
|
|
308
|
-
account, then copy `credentials.json` to each machine at 0600
|
|
309
|
-
|
|
310
|
-
long-lived secret, so
|
|
311
|
-
|
|
312
|
-
|
|
632
|
+
account, then copy `credentials.json` to each machine at 0600, together with
|
|
633
|
+
a `config.json` naming the same service. The rotating refresh token keeps it
|
|
634
|
+
alive and nothing ever prompts. It is still a shared long-lived secret, so
|
|
635
|
+
never a person's account.
|
|
636
|
+
2. **No service address at all** for runners that should record nothing: no
|
|
637
|
+
address in `~/.baseinstrunner/config.json` and no `BIR_AUTH_URL`. The
|
|
638
|
+
recorder then starts with a null recorder (`recorder.disabled` on its log)
|
|
639
|
+
and everything else still works; `bir up` and `bir doctor` exit 1 to say so.
|
|
640
|
+
`BIR_AUTH_DISABLE=1` is *not* that: it fakes a session and records with a
|
|
641
|
+
bogus token — a local-dev and CI switch, not a fleet one.
|
|
313
642
|
3. **Ask the service for a non-interactive grant** — a long-lived runner token,
|
|
314
643
|
created in the console and read from an env var. This remains the right answer
|
|
315
|
-
for a large fleet and is still a service-side change; the device grant
|
|
316
|
-
it less urgent, not unnecessary.
|
|
644
|
+
for a large fleet and is still a service-side change; the device grant and the
|
|
645
|
+
setup token make it less urgent, not unnecessary.
|
|
317
646
|
|
|
318
|
-
`bir logout`
|
|
647
|
+
`bir logout` revokes the session server-side before clearing the file, so
|
|
319
648
|
decommissioning a runner ends its access rather than merely tidying the disk.
|
|
320
649
|
|
|
321
650
|
`BIR_HOME` relocates all of this if state belongs somewhere other than
|
|
@@ -325,45 +654,52 @@ decommissioning a runner ends its access rather than merely tidying the disk.
|
|
|
325
654
|
|
|
326
655
|
## 8. Updating a runner
|
|
327
656
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
657
|
+
Paste the line again — the same line, used token and all. It installs the
|
|
658
|
+
version the service serves (`RUNNER_VERSION`, default `latest`), keeps the
|
|
659
|
+
sign-in it already has ("the setup token was already used; keeping the sign-in
|
|
660
|
+
from before (<email>)"), and runs `bir setup`, which re-wraps whatever moved and
|
|
661
|
+
restarts the recorder. For a machine installed by hand: the new package, then
|
|
662
|
+
`bir setup` in each recorded project.
|
|
334
663
|
|
|
335
664
|
```bash
|
|
336
|
-
|
|
665
|
+
npm i -g @basein/runner@<version> && cd ~/work/repo && bir setup
|
|
337
666
|
```
|
|
338
667
|
|
|
339
|
-
|
|
668
|
+
Two rules used to follow, and neither is a step now:
|
|
340
669
|
|
|
341
|
-
- **
|
|
342
|
-
step people
|
|
343
|
-
|
|
344
|
-
is
|
|
345
|
-
|
|
670
|
+
- **Restarting the recorder.** A running process is still the old code, and
|
|
671
|
+
this was the step people forgot. `bir setup`, `bir up` and the SessionStart
|
|
672
|
+
hook all restart a running recorder whose version is not the package's —
|
|
673
|
+
provided no session is mid-run. A recorder still serving an open Claude Code
|
|
674
|
+
session is left alone; `bir setup` says so ("recorder 0.2.8 is still serving
|
|
675
|
+
an open Claude Code session; it restarts to 0.2.9 with the next session") and
|
|
676
|
+
the next session's hook does it. `bir up --restart` forces it now.
|
|
677
|
+
- **Re-running `bir install`.** `bir setup` does. A `--global` entry whose path
|
|
678
|
+
did not change needs nothing (§4); one whose runner moved — a new prefix, a
|
|
679
|
+
new Node — is re-wrapped from the stashed original; a pinned-npx entry always
|
|
680
|
+
has its pin moved.
|
|
346
681
|
|
|
347
|
-
Rolling back
|
|
682
|
+
Rolling back: install the older version by hand and run `bir setup`.
|
|
348
683
|
|
|
349
684
|
### The half-upgraded machine
|
|
350
685
|
|
|
351
686
|
This is the update failure that looks like success: the package is new, but a
|
|
352
|
-
proxy is still the old code because a config points elsewhere or
|
|
353
|
-
|
|
354
|
-
ship.
|
|
687
|
+
proxy is still the old code because a config points elsewhere, or the recorder
|
|
688
|
+
is a process that outlived the upgrade. Everything keeps working, and quietly
|
|
689
|
+
runs code you did not ship.
|
|
355
690
|
|
|
356
|
-
|
|
357
|
-
|
|
691
|
+
The restart-on-skew above closes the common case. For the rest, a proxy reports
|
|
692
|
+
its version when it registers, the control server compares it with its own, and
|
|
693
|
+
a mismatch is one line on the audit log:
|
|
358
694
|
|
|
359
695
|
```
|
|
360
696
|
[bir] … version.skew server=fleet proxy=0.0.9 hooks=0.1.0 fix="restart bir-hooks after upgrading, …"
|
|
361
697
|
```
|
|
362
698
|
|
|
363
|
-
`bir doctor --json` carries the same facts structurally — `
|
|
364
|
-
|
|
365
|
-
script can assert agreement rather than grep a log. A proxy too old
|
|
366
|
-
version is not treated as skewed: silence, not a false alarm.
|
|
699
|
+
`bir doctor --json` carries the same facts structurally — under `health`,
|
|
700
|
+
`hooksVersion` and a `version` on each entry of `registeredProxies` — so a
|
|
701
|
+
provisioning script can assert agreement rather than grep a log. A proxy too old
|
|
702
|
+
to report a version is not treated as skewed: silence, not a false alarm.
|
|
367
703
|
|
|
368
704
|
---
|
|
369
705
|
|
|
@@ -372,10 +708,11 @@ version is not treated as skewed: silence, not a false alarm.
|
|
|
372
708
|
CI does this on three platforms. To do it by hand on a clean machine:
|
|
373
709
|
|
|
374
710
|
```bash
|
|
375
|
-
npm run build && npm test #
|
|
376
|
-
npm run test:smoke # real processes: hooks + proxy + scenario over stdio
|
|
711
|
+
npm run build && npm test # ~400 tests (npm runs pretest first)
|
|
712
|
+
npm run test:smoke # real processes: hooks + proxy + scenario over stdio;
|
|
713
|
+
# needs the build and the pretest above
|
|
377
714
|
npm pack && npm i -g ./basein-runner-*.tgz
|
|
378
|
-
cd /tmp/scratch && bir install --global && bir
|
|
715
|
+
cd /tmp/scratch && bir setup --auth-url <api> --token <t> # or BIR_AUTH_URL=<api> bir login, then bir install --global && bir up
|
|
379
716
|
bir doctor --json # must exit 0
|
|
380
717
|
bir uninstall # must restore the config byte-for-byte
|
|
381
718
|
```
|
|
@@ -385,7 +722,12 @@ crosses process boundaries, which is exactly where a bad `files` list, a missing
|
|
|
385
722
|
shebang, or a broken bin mapping shows up.
|
|
386
723
|
|
|
387
724
|
Sandbox a manual run with `BIR_HOME` and `BIR_CLAUDE_JSON` so it cannot touch
|
|
388
|
-
your real Claude Code configuration.
|
|
725
|
+
your real Claude Code configuration. A fresh `BIR_HOME` has no sign-in, which is
|
|
726
|
+
why the recipe signs in: a bare `bir install --global && bir up` there starts a
|
|
727
|
+
recorder with nothing to record to, `bir up` ends with "it has no session to
|
|
728
|
+
record with" and exit 1, and `bir doctor --json` exits 1 for the same reason.
|
|
729
|
+
`bir uninstall` restores byte-for-byte only what nothing else edited since the
|
|
730
|
+
install; a file it created stays behind, empty of our entries.
|
|
389
731
|
|
|
390
732
|
---
|
|
391
733
|
|
|
@@ -395,18 +737,22 @@ your real Claude Code configuration.
|
|
|
395
737
|
|---|---|---|
|
|
396
738
|
| 1 | Name settled; `PACKAGE_NAME` aligned | done |
|
|
397
739
|
| 2 | `repository` + `publishConfig`; provenance deferred (private repo) | done |
|
|
398
|
-
| 3 | Tarball trimmed — no dead sourcemaps, no scratchpad | done |
|
|
399
|
-
| 4 | npx spec pinned; `--global` added | done |
|
|
740
|
+
| 3 | Tarball trimmed — no dead sourcemaps, no scratchpad; `scripts/` ships | done |
|
|
741
|
+
| 4 | npx spec pinned; `--global` added; moved runners re-wrapped | done |
|
|
400
742
|
| 5 | CI and release workflows, three platforms | done |
|
|
401
|
-
| 6 | `scripts/install
|
|
402
|
-
| 7 | Version-skew detection on registration | done |
|
|
743
|
+
| 6 | `scripts/install.{ps1,sh}`, served by the API at `/install.ps1` and `/install.sh` (replace `install-runner.*`) | done |
|
|
744
|
+
| 7 | Version-skew detection on registration; recorder restarts itself between sessions | done |
|
|
403
745
|
| 8 | UTF-8 BOM tolerated in MCP configs | done |
|
|
404
746
|
| 9 | npm org `basein`, `NPM_TOKEN` secret | done |
|
|
405
|
-
| 10 |
|
|
406
|
-
| 11 |
|
|
407
|
-
| 12 |
|
|
408
|
-
|
|
409
|
-
|
|
747
|
+
| 10 | `bir setup`: sign-in, wrap, replay, recorder in one command | done |
|
|
748
|
+
| 11 | Background recorder started by the SessionStart hook (`bir-hooks ensure`); `bir up` / `bir down`; log per project | done |
|
|
749
|
+
| 12 | Hooks in `.claude/settings.local.json` + `.git/info/exclude`; one port and token per project | done |
|
|
750
|
+
| 13 | Setup token (`BIR_SETUP_TOKEN`) for the first sign-in; credentials bound to their service; address in `config.json` | done |
|
|
751
|
+
| 14 | Tag `v0.2.9` to publish | ready |
|
|
752
|
+
| 15 | Non-interactive auth grant for fleets | **needs the service** |
|
|
753
|
+
| 16 | Migrate to trusted publishing before Jan 2027 (§3.3) | scheduled |
|
|
754
|
+
|
|
755
|
+
Item 15 is the only one that needs somebody else.
|
|
410
756
|
|
|
411
757
|
---
|
|
412
758
|
|