@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.
Files changed (40) hide show
  1. package/README.md +86 -22
  2. package/dist/auth/client.d.ts +40 -1
  3. package/dist/auth/client.js +77 -9
  4. package/dist/bin/bir-hooks.d.ts +18 -3
  5. package/dist/bin/bir-hooks.js +124 -38
  6. package/dist/bin/bir-scenario.d.ts +18 -2
  7. package/dist/bin/bir-scenario.js +374 -4
  8. package/dist/bin/bir.d.ts +12 -0
  9. package/dist/bin/bir.js +501 -81
  10. package/dist/bin/investigate.js +1 -1
  11. package/dist/bin/scenario-edit.d.ts +173 -0
  12. package/dist/bin/scenario-edit.js +771 -0
  13. package/dist/bin/setup.d.ts +72 -0
  14. package/dist/bin/setup.js +286 -0
  15. package/dist/config/adapters/claude-code.d.ts +90 -4
  16. package/dist/config/adapters/claude-code.js +164 -16
  17. package/dist/config/generate.d.ts +114 -1
  18. package/dist/config/generate.js +106 -3
  19. package/dist/control/client.d.ts +5 -0
  20. package/dist/control/client.js +8 -0
  21. package/dist/control/daemon.d.ts +116 -0
  22. package/dist/control/daemon.js +339 -0
  23. package/dist/control/discovery.d.ts +26 -0
  24. package/dist/control/discovery.js +41 -9
  25. package/dist/control/ensure-hook.d.ts +39 -0
  26. package/dist/control/ensure-hook.js +98 -0
  27. package/dist/control/paths.d.ts +14 -0
  28. package/dist/control/paths.js +20 -0
  29. package/dist/control/server.d.ts +28 -0
  30. package/dist/control/server.js +15 -2
  31. package/dist/proxy/session.d.ts +8 -1
  32. package/dist/proxy/session.js +28 -6
  33. package/docs/calculatedReplay.md +51 -0
  34. package/docs/calculatedReplayGuide.md +471 -74
  35. package/docs/installRun.md +457 -111
  36. package/docs/loginWeb.md +1 -1
  37. package/docs/quickstart.md +195 -158
  38. package/package.json +2 -1
  39. package/scripts/install.ps1 +669 -0
  40. package/scripts/install.sh +586 -0
package/README.md CHANGED
@@ -22,40 +22,70 @@ hooks ──built-ins, prompt, stop──▶ bir-hooks (control server) ──
22
22
 
23
23
  ## Install
24
24
 
25
- On a runner machine, one command does machine, account and project scope:
25
+ One line, pasted into a terminal **opened in the project you start Claude Code
26
+ in**. The console's *Set up the runner* page shows it with a one-time setup
27
+ token filled in, so there is no sign-in step either:
26
28
 
27
29
  ```powershell
28
- .\scripts\install-runner.ps1 C:\work\repo -AuthUrl https://your-basein-service # Windows
30
+ $env:BIR_SETUP_TOKEN="<token>"; irm https://api.bi2202.com/install.ps1 | iex # Windows (PowerShell)
29
31
  ```
30
32
 
31
33
  ```bash
32
- BIR_AUTH_URL=https://your-basein-service ./scripts/install-runner.sh ~/work/repo # macOS/Linux
34
+ curl -fsSL https://api.bi2202.com/install.sh | BIR_SETUP_TOKEN="<token>" sh # macOS / Linux
33
35
  ```
34
36
 
35
- Step by step, for one machine: [docs/quickstart.md](docs/quickstart.md).
36
- For a fleet — packaging, releases, offline installs, updates:
37
- [docs/installRun.md](docs/installRun.md).
37
+ That installs Node if the machine has none (a private copy, never on PATH),
38
+ installs this package, installs Claude Code if it is missing, and runs
39
+ `bir setup`: it signs in, stores the service address, wraps the project's MCP
40
+ servers, wires Claude Code's hooks, adds the scenario server, and starts the
41
+ recorder in the background. It ends with the one thing left to do: `claude`.
42
+ Without a token the same line signs in through the browser. Re-running it is
43
+ safe, and is also how you upgrade. To record another project: `cd` there and
44
+ run `bir setup` (no token needed; the sign-in is kept).
45
+
46
+ The scripts are [scripts/install.ps1](scripts/install.ps1) and
47
+ [scripts/install.sh](scripts/install.sh); the API serves them with its own
48
+ address filled in. Step by step, and what each line does:
49
+ [docs/quickstart.md](docs/quickstart.md). For a fleet — packaging, releases,
50
+ offline installs, updates: [docs/installRun.md](docs/installRun.md).
38
51
 
39
52
  By hand, or when developing BaseInstRunner itself:
40
53
 
41
54
  ```bash
42
55
  npm i -g @basein/runner # or: npm install && npm run build, in a checkout
43
56
  cd the-project-you-record-from
44
- bir install --global # --local from a checkout; plain `bir install` writes a pinned npx entry
57
+ bir setup --auth-url https://api.bi2202.com # everything below, in one go
45
58
  ```
46
59
 
47
- `bir install` rewrites your MCP entries in place, keeping every key (so
48
- `mcp__<server>__<tool>` names never change), and wires Claude Code's hooks. It
49
- stashes the original text of every file first — `bir uninstall` puts it back
50
- byte-for-byte.
60
+ or the pieces `bir setup` is made of:
51
61
 
52
62
  ```bash
53
- export BIR_AUTH_URL=https://your-basein-service # required to record anything
54
- bir login # once; approve it in the browser
55
- bir-hooks # leave running; Ctrl-C to stop
56
- claude # your session, now recorded
63
+ bir login # once; approve it in the browser (or --token <t> from the console)
64
+ bir install --global --replay # --local from a checkout; plain `bir install` writes a pinned npx entry
65
+ bir up # the recorder, in the background; the SessionStart hook does this too
66
+ claude # your session, now recorded
57
67
  ```
58
68
 
69
+ `bir install` rewrites your MCP entries in place, keeping every key (so
70
+ `mcp__<server>__<tool>` names never change), and wires Claude Code's hooks into
71
+ `.claude/settings.local.json` — your file, not the one a team commits; in a git
72
+ repository it is added to `.git/info/exclude`. It stashes the original text of
73
+ every file first — `bir uninstall` puts a file back byte-for-byte when nothing
74
+ else has edited it since, and repairs it entry by entry when something has. A
75
+ project remembers whether it was installed with `--global`, `--local` or the
76
+ pinned-npx default, so a later `bir install` with no flag keeps that shape.
77
+
78
+ **Nothing has to stay open.** The SessionStart hook is `bir-hooks ensure`: it
79
+ starts the recorder for the project when none is running, in the background,
80
+ with its audit log at `~/.baseinstrunner/logs/<project-key>.log`. `bir up`
81
+ starts it by hand, `bir down` stops it, `bir status` shows it. Running
82
+ `bir-hooks` in a terminal still works and is what the hook does for you.
83
+
84
+ The service address is stored by `bir setup` and `bir login` in
85
+ `~/.baseinstrunner/config.json`; `BIR_AUTH_URL` in the environment overrides
86
+ it. A cached session is bound to the service that issued it and is never sent
87
+ anywhere else.
88
+
59
89
  `bir login` prints a link and a short code and waits for you to approve it in
60
90
  the browser — the OAuth device grant, the same shape `gh auth login` uses. It
61
91
  never handles a password, so it works for accounts that only sign in with
@@ -117,7 +147,9 @@ between the two is not guaranteed.
117
147
  section.
118
148
  - **Change what the model does.** Tool descriptions are never edited. The
119
149
  model-visible changes are schema relaxation, below, and — only with
120
- `--replay` — one first-party tool.
150
+ `--replay` — the first-party `bir` server's tools: `run_scenario`, and three
151
+ that only read a scenario. The three that change one appear only after
152
+ `bir scenario editing on` in that project.
121
153
 
122
154
  ---
123
155
 
@@ -134,10 +166,18 @@ record, calculate, check, enable, read the audit lines — is
134
166
  [docs/calculatedReplayGuide.md](docs/calculatedReplayGuide.md).
135
167
 
136
168
  ```bash
137
- bir install --replay # adds the `bir` MCP server; raises the prompt-hook timeout
138
- bir-hooks # replay is on by default; BIR_REPLAY=0 bir-hooks turns it off
169
+ bir install --replay # adds the `bir` MCP server, pre-approved in .claude/settings.local.json
170
+ bir up # or let the SessionStart hook start the recorder; replay is on by default
171
+ bir replay allow fleet # the servers replay may call unattended — stored for this project
172
+ bir replay off # and back on with `bir replay on`; `bir replay status` shows it
139
173
  ```
140
174
 
175
+ `bir setup` does the first two. The switches are kept per project in
176
+ `~/.baseinstrunner/installed.json`, so a recorder the hook starts — with Claude
177
+ Code's environment, not your terminal's — keeps them; `BIR_REPLAY`,
178
+ `BIR_REPLAY_ALLOW_SERVERS` and `BIR_MIN_STEER_SIMILARITY` in the environment
179
+ still override them, and `bir doctor` says which source is in effect.
180
+
141
181
  Two switches, not one, because of what replay is: **a scenario is a pre-approved
142
182
  list of tool calls with computed arguments.** A steered step is answered
143
183
  `permissionDecision: "allow"`, so a pinned `Bash` or `Edit` runs without the
@@ -154,6 +194,25 @@ sequence runs through connections the proxies already hold — **zero model toke
154
194
  scenarios steer the live session instead, pinning each step's arguments. Either
155
195
  way the turn reports what it cost, so the savings ledger measures something real.
156
196
 
197
+ **Fixing a step.** A calculated step is code, and code can be wrong.
198
+ `bir investigate` names such a step; `bir scenario edit` fixes it, and the
199
+ service checks the change against the recording before it saves it. **A change
200
+ is saved only if it reproduces the recording — or on purpose, with
201
+ `--force --note "why"`.** Only the owner can change a scenario. Claude can do it
202
+ too: in its terminal, or with the `bir` server's tools once
203
+ `bir scenario editing on` was run in the project. Built 2026-09-25, not yet
204
+ released: it needs the next `@basein/runner` version and a service deploy. Step
205
+ by step: [the guide's §9.2](docs/calculatedReplayGuide.md).
206
+
207
+ ```bash
208
+ bir scenario show scn_… --step 3 # read one step
209
+ bir scenario check scn_… --step 3 --input-logic step3.js # try a change; nothing is saved
210
+ bir scenario edit scn_… --step 3 --input-logic step3.js --note "why" # the same check, then save
211
+ bir scenario edits scn_… # the history
212
+ bir scenario undo scn_… # undo the newest edit
213
+ bir scenario editing on # let Claude's `bir` tools change scenarios here
214
+ ```
215
+
157
216
  ---
158
217
 
159
218
  ## Two things to know before you turn it on
@@ -184,7 +243,8 @@ instead.
184
243
 
185
244
  | Variable | Effect |
186
245
  |---|---|
187
- | `BIR_AUTH_URL` | BaseIn auth-service — the **API** address (normally `https://api.<your-domain>`), not the docs or app website. **Unset means nothing is recorded** — everything else still works. |
246
+ | `BIR_AUTH_URL` | BaseIn auth-service — the **API** address (normally `https://api.<your-domain>`), not the docs or app website. Overrides the address `bir setup` / `bir login` stored in `~/.baseinstrunner/config.json`. **Neither set means nothing is recorded** — everything else still works. |
247
+ | `BIR_SETUP_TOKEN` | A one-time setup token from the console, read by `bir setup` when `--token` is not given; the install scripts pass it this way. Never inherited by the recorder. |
188
248
  | `BIR_AUTH_DISABLE=1` | Skip authentication (local dev / CI). |
189
249
  | `BIR_CONTROL_PORT` | Preferred control-server port (default 53411). |
190
250
  | `BIR_CONTROL_URL` / `BIR_CONTROL_TOKEN` | Point a proxy at a control server directly, bypassing discovery. Injected into generated config; the only reliable channel for an SDK session. |
@@ -200,7 +260,7 @@ what to do when they register, so there is one switch and no second copy of it:
200
260
  | Variable | Effect |
201
261
  |---|---|
202
262
  | `BIR_REPLAY=0` | Disable replay. It is on by default; nothing below matters while this is `0`. |
203
- | `BIR_REPLAY_ALLOW_SERVERS` | Server keys eligible for **direct** execution. Unset means every wrapped server; setting it is the recommendation. |
263
+ | `BIR_REPLAY_ALLOW_SERVERS` | Server keys eligible for **direct** execution. Unset means the list `bir replay allow` stored for the project, else every wrapped server; narrowing it is the recommendation. |
204
264
  | `BIR_MIN_STEER_SIMILARITY` | Minimum match similarity to replay (default `0.92`, above the service's own `0.9` detection threshold). |
205
265
  | `ANTHROPIC_API_KEY` | **Optional.** Working out what a new request is about — which fleet, which file, which date — is done for you by the service on its own key, as long as you are signed in. Set this only to keep that reading on your machine, on your key. Signed out *and* unset, a scenario whose values change between requests is declined rather than replayed on stale ones. |
206
266
  | `BIR_DERIVE_MODEL` | Derivation model, when you set a key of your own (default `claude-haiku-4-5-20251001`). |
@@ -211,7 +271,10 @@ what to do when they register, so there is one switch and no second copy of it:
211
271
  ### The audit log
212
272
 
213
273
  Every decision is one `key=value` line on **stderr** — never stdout, which is the
214
- host's JSON-RPC stream. There is no file sink to configure; persistence is the
274
+ host's JSON-RPC stream. A recorder started in the background (by `bir up`, `bir
275
+ setup` or the SessionStart hook) writes them to
276
+ `~/.baseinstrunner/logs/<project-key>.log` — `bir status` names the file. One
277
+ started in a terminal writes them to that terminal, and persistence is the
215
278
  shell's job:
216
279
 
217
280
  ```bash
@@ -227,6 +290,7 @@ The governing rule: **a host session never fails because of BaseInstRunner.**
227
290
 
228
291
  | Failure | Behaviour |
229
292
  |---|---|
293
+ | No recorder running at session start | the SessionStart hook starts one in the background; a session is never blocked or failed by it |
230
294
  | Control server unreachable at startup | buffer ≤ 5 s, then Tier 2; logged once |
231
295
  | Control server dies mid-session | the proxy switches to Tier 2 for the rest; run flagged lossy |
232
296
  | Recorder HTTP fails | send dropped and logged; never retried inline |
@@ -242,7 +306,7 @@ The governing rule: **a host session never fails because of BaseInstRunner.**
242
306
 
243
307
  ```bash
244
308
  npm run build # src → dist
245
- npm test # 233 tests: transcript identity, passthrough, correlation, config, replay
309
+ npm test # ~400 tests: transcript identity, passthrough, correlation, config, replay, setup
246
310
  npm run typecheck
247
311
  npm run test:smoke # end-to-end replay with real processes (needs build + pretest first)
248
312
  ```
@@ -32,7 +32,10 @@
32
32
  * server the host is waiting on. Callers get `undefined` and decide.
33
33
  *
34
34
  * Config:
35
- * BIR_AUTH_URL base URL of the BaseIn auth-service (required).
35
+ * BIR_AUTH_URL base URL of the BaseIn auth-service. When unset, the
36
+ * address `bir setup` / `bir login` stored in
37
+ * ~/.baseinstrunner/config.json is used — see
38
+ * {@link resolveAuthUrl}.
36
39
  * BIR_AUTH_DISABLE "1" to skip auth entirely (local dev / CI).
37
40
  */
38
41
  export interface AuthUser {
@@ -46,6 +49,15 @@ export interface AuthSession {
46
49
  /** Epoch milliseconds at which the access token expires. */
47
50
  accessExpiresAt: number;
48
51
  user: AuthUser;
52
+ /**
53
+ * The service that issued this session. `authenticate()` refuses to hand the
54
+ * tokens to any other origin: a stale `BIR_AUTH_URL` in some profile file, or
55
+ * one set by a repository's settings, must degrade to not-recording rather
56
+ * than send a live refresh token and every recording somewhere else. Absent
57
+ * on a file written before this field existed; those are trusted as before
58
+ * and re-bound by the next sign-in.
59
+ */
60
+ authUrl?: string;
49
61
  }
50
62
  export interface AuthenticateOptions {
51
63
  /** Base URL of the auth service. Defaults to BIR_AUTH_URL. */
@@ -73,6 +85,33 @@ export declare function loadCredentials(): AuthSession | undefined;
73
85
  export declare function saveCredentials(session: AuthSession): void;
74
86
  /** Remove the cached session (`bir logout`). No-op if absent. */
75
87
  export declare function clearCredentials(): void;
88
+ /** The persisted settings in ~/.baseinstrunner/config.json. */
89
+ export interface StoredConfig {
90
+ /** The service address 'bir setup' or 'bir login' last signed in against. */
91
+ authUrl?: string;
92
+ }
93
+ /** Read the stored settings; an absent or unreadable file is an empty one. */
94
+ export declare function loadStoredConfig(): StoredConfig;
95
+ /** Merge 'patch' into the stored settings and write them back, mode 0600. */
96
+ export declare function saveStoredConfig(patch: StoredConfig): StoredConfig;
97
+ /**
98
+ * Remember the service address, so no later process needs BIR_AUTH_URL.
99
+ *
100
+ * Called by 'bir setup' and 'bir login' — the two moments a person has just
101
+ * proved the address is right by signing in to it. A wrong address is never
102
+ * stored, because a session was never issued for it.
103
+ */
104
+ export declare function rememberAuthUrl(authUrl: string): void;
105
+ /**
106
+ * The service address this process should talk to, or "" when there is none.
107
+ *
108
+ * Three sources, in order: an explicit option, BIR_AUTH_URL, and the address
109
+ * stored by 'bir setup' / 'bir login'. The third is what makes the install a
110
+ * single step: a recorder started by a Claude Code hook inherits whatever
111
+ * environment the host had, which on a GUI-launched host is not the shell the
112
+ * person set things up in. The stored address is read the same way from every
113
+ * process, so they cannot disagree about where the recording goes.
114
+ */
76
115
  export declare function resolveAuthUrl(opts?: AuthenticateOptions): string;
77
116
  /**
78
117
  * Follow a redirect on the base URL *once*, so a bearer token is never sent to
@@ -32,14 +32,17 @@
32
32
  * server the host is waiting on. Callers get `undefined` and decide.
33
33
  *
34
34
  * Config:
35
- * BIR_AUTH_URL base URL of the BaseIn auth-service (required).
35
+ * BIR_AUTH_URL base URL of the BaseIn auth-service. When unset, the
36
+ * address `bir setup` / `bir login` stored in
37
+ * ~/.baseinstrunner/config.json is used — see
38
+ * {@link resolveAuthUrl}.
36
39
  * BIR_AUTH_DISABLE "1" to skip auth entirely (local dev / CI).
37
40
  */
38
41
  import { spawn } from "node:child_process";
39
42
  import { createInterface } from "node:readline";
40
43
  import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
41
44
  import { hostname } from "node:os";
42
- import { configDir } from "../control/paths.js";
45
+ import { configDir, configPath, ensureDir } from "../control/paths.js";
43
46
  import { join } from "node:path";
44
47
  import { logLine, errText } from "../util/log.js";
45
48
  import { packageVersion } from "../util/version.js";
@@ -103,16 +106,70 @@ export function clearCredentials() {
103
106
  function isAccessValid(session) {
104
107
  return session.accessExpiresAt - EXPIRY_SKEW_MS > Date.now();
105
108
  }
106
- function toSession(res) {
109
+ function toSession(res, authUrl) {
107
110
  return {
108
111
  accessToken: res.accessToken,
109
112
  refreshToken: res.refreshToken,
110
113
  accessExpiresAt: Date.now() + res.expiresIn * 1000,
111
114
  user: res.user,
115
+ ...(authUrl ? { authUrl: authUrl.replace(/\/+$/, "") } : {}),
112
116
  };
113
117
  }
118
+ /** True when a cached session may be used against `authUrl`. */
119
+ function issuedFor(session, authUrl) {
120
+ if (!session.authUrl)
121
+ return true; // pre-binding file: trusted as before
122
+ return session.authUrl === authUrl.replace(/\/+$/, "");
123
+ }
124
+ /** Read the stored settings; an absent or unreadable file is an empty one. */
125
+ export function loadStoredConfig() {
126
+ try {
127
+ if (!existsSync(configPath()))
128
+ return {};
129
+ const parsed = JSON.parse(readFileSync(configPath(), "utf8"));
130
+ return parsed && typeof parsed === "object" ? parsed : {};
131
+ }
132
+ catch {
133
+ return {};
134
+ }
135
+ }
136
+ /** Merge 'patch' into the stored settings and write them back, mode 0600. */
137
+ export function saveStoredConfig(patch) {
138
+ const next = { ...loadStoredConfig(), ...patch };
139
+ ensureDir(configDir());
140
+ writeFileSync(configPath(), JSON.stringify(next, null, 2) + "\n", { mode: 0o600 });
141
+ return next;
142
+ }
143
+ /**
144
+ * Remember the service address, so no later process needs BIR_AUTH_URL.
145
+ *
146
+ * Called by 'bir setup' and 'bir login' — the two moments a person has just
147
+ * proved the address is right by signing in to it. A wrong address is never
148
+ * stored, because a session was never issued for it.
149
+ */
150
+ export function rememberAuthUrl(authUrl) {
151
+ const url = authUrl.replace(/\/+$/, "");
152
+ if (!url)
153
+ return;
154
+ if (loadStoredConfig().authUrl === url)
155
+ return;
156
+ saveStoredConfig({ authUrl: url });
157
+ }
158
+ /**
159
+ * The service address this process should talk to, or "" when there is none.
160
+ *
161
+ * Three sources, in order: an explicit option, BIR_AUTH_URL, and the address
162
+ * stored by 'bir setup' / 'bir login'. The third is what makes the install a
163
+ * single step: a recorder started by a Claude Code hook inherits whatever
164
+ * environment the host had, which on a GUI-launched host is not the shell the
165
+ * person set things up in. The stored address is read the same way from every
166
+ * process, so they cannot disagree about where the recording goes.
167
+ */
114
168
  export function resolveAuthUrl(opts) {
115
- return (opts?.authUrl ?? process.env.BIR_AUTH_URL ?? "").replace(/\/+$/, "");
169
+ const explicit = opts?.authUrl ?? process.env.BIR_AUTH_URL;
170
+ if (explicit !== undefined && explicit !== "")
171
+ return explicit.replace(/\/+$/, "");
172
+ return (loadStoredConfig().authUrl ?? "").replace(/\/+$/, "");
116
173
  }
117
174
  /**
118
175
  * Follow a redirect on the base URL *once*, so a bearer token is never sent to
@@ -304,7 +361,7 @@ async function postJson(url, body) {
304
361
  /** Exchange a refresh token for a new session. Undefined on failure. */
305
362
  async function tryRefresh(authUrl, refreshToken) {
306
363
  try {
307
- return toSession(await postJson(`${authUrl}/auth/refresh`, { refreshToken }));
364
+ return toSession(await postJson(`${authUrl}/auth/refresh`, { refreshToken }), authUrl);
308
365
  }
309
366
  catch {
310
367
  return undefined;
@@ -326,11 +383,22 @@ export async function authenticate(opts) {
326
383
  const authUrl = resolveAuthUrl(opts);
327
384
  if (!authUrl) {
328
385
  logLine("auth.unconfigured", {
329
- why: "BIR_AUTH_URL is not set — nothing will be recorded",
386
+ why: "no service address — run `bir setup`, or set BIR_AUTH_URL; nothing will be recorded",
330
387
  });
331
388
  return undefined;
332
389
  }
333
390
  const cached = loadCredentials();
391
+ if (cached && !issuedFor(cached, authUrl)) {
392
+ // The tokens on disk belong to another service. Using them here would
393
+ // hand a live session to whatever this address is; refreshing them here
394
+ // would hand it the refresh token too. Neither is ever the right call.
395
+ logLine("auth.url_mismatch", {
396
+ cached: cached.authUrl,
397
+ requested: authUrl,
398
+ why: "the cached session was issued by another service — run `bir setup` (or `bir login`) for this one",
399
+ });
400
+ return undefined;
401
+ }
334
402
  if (cached && isAccessValid(cached))
335
403
  return cached;
336
404
  if (cached?.refreshToken) {
@@ -513,7 +581,7 @@ export async function deviceLogin(authUrl, opts = {}) {
513
581
  sleep: opts.sleep,
514
582
  signal: opts.signal,
515
583
  });
516
- const session = toSession(res);
584
+ const session = toSession(res, authUrl);
517
585
  saveCredentials(session);
518
586
  return session;
519
587
  }
@@ -536,7 +604,7 @@ export async function tokenLogin(authUrl, token, opts = {}) {
536
604
  if (res.status === 404)
537
605
  throw new DeviceFlowUnsupported();
538
606
  if (res.ok) {
539
- const session = toSession((await res.json()));
607
+ const session = toSession((await res.json()), authUrl);
540
608
  saveCredentials(session);
541
609
  logLine("auth.token_login", { url: authUrl });
542
610
  return session;
@@ -604,7 +672,7 @@ export async function legacyPasswordLogin(authUrl) {
604
672
  return undefined;
605
673
  }
606
674
  try {
607
- const session = toSession(await postJson(`${authUrl}/auth/login`, { email, password }));
675
+ const session = toSession(await postJson(`${authUrl}/auth/login`, { email, password }), authUrl);
608
676
  saveCredentials(session);
609
677
  return session;
610
678
  }
@@ -2,7 +2,11 @@
2
2
  /**
3
3
  * bir-hooks — the Claude Code hook receiver and the control server (Tier 1).
4
4
  *
5
- * Run it in the project you will launch `claude` from, in its own terminal:
5
+ * Normally nobody runs it: `bir install` makes it the SessionStart hook
6
+ * (`bir-hooks ensure`), which starts it in the background for the project when no
7
+ * recorder is running there, and `bir up` / `bir down` start and stop that
8
+ * background process by hand. Its audit lines then go to
9
+ * ~/.baseinstrunner/logs/<project-key>.log. It can still be run in a terminal:
6
10
  *
7
11
  * bir-hooks # start; Ctrl-C to stop
8
12
  * bir-hooks 2>&1 | tee -a ~/.baseinstrunner/audit.log
@@ -13,7 +17,8 @@
13
17
  * 3. owns run identity, the monotonic `stepIndex`, and the recorder chain (D5).
14
18
  *
15
19
  * Environment:
16
- * BIR_AUTH_URL BaseIn auth-service (required to record anything)
20
+ * BIR_AUTH_URL BaseIn auth-service. Unset, the address `bir setup`
21
+ * stored in ~/.baseinstrunner/config.json is used
17
22
  * BIR_CONTROL_PORT preferred port (default 53411; falls back if busy)
18
23
  * BIR_CORRELATION_DECISION `allow` (default) or `ask` — see below
19
24
  * BIR_NO_CORRELATION=1 never inject a call id; join on fingerprints
@@ -60,5 +65,15 @@
60
65
  * hosts that ignore `updatedInput` on a non-allow decision then fall back to
61
66
  * fingerprint matching, which is lossy under identical concurrent calls.
62
67
  */
63
- export {};
68
+ /**
69
+ * Assemble the replay configuration (docs/calculatedReplay.md §5.3 of the guide).
70
+ *
71
+ * The steer threshold defaults **above** the service's own detection threshold
72
+ * (0.9). Detection decides "do not record this again", which is cheap — the worst
73
+ * case is a lost recording. Steering decides "do not think about this again",
74
+ * which is a much stronger claim, and a 0.90-similar prompt usually covers only
75
+ * part of the live task.
76
+ */
77
+ /** Where the three replay switches came from, for `/health` and `bir doctor`. */
78
+ export type ReplaySource = "env" | "sidecar" | "default";
64
79
  //# sourceMappingURL=bir-hooks.d.ts.map