@1presence/bridge 0.72.0 → 0.74.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,12 +1,5 @@
1
1
  import { spawn } from 'node:child_process';
2
2
  import { createInterface } from 'node:readline';
3
- /**
4
- * Parse the stdout of `claude auth status --json`. Extracts the first JSON object
5
- * (defensive against any banner/log noise around it). Returns null when there is
6
- * no parseable object or no boolean `loggedIn` field — i.e. an output shape we
7
- * don't recognise, which the caller treats as "unknown, don't block".
8
- * Exported for unit testing.
9
- */
10
3
  export function parseAuthStatus(stdout) {
11
4
  const start = stdout.indexOf('{');
12
5
  const end = stdout.lastIndexOf('}');
@@ -28,14 +21,6 @@ export function parseAuthStatus(stdout) {
28
21
  return null;
29
22
  }
30
23
  }
31
- /**
32
- * Ask the local `claude` CLI whether it's signed in via `claude auth status
33
- * --json`. Zero-cost — no model turn runs. Resolves the parsed status, or null
34
- * when we can't determine it (CLI not installed, too old for `auth status`,
35
- * errored, or timed out). null means "unknown": a probe we can't trust must
36
- * never gate startup — the first turn's error path still guides the user if auth
37
- * is genuinely broken.
38
- */
39
24
  export function probeClaudeAuth(timeoutMs = 8000) {
40
25
  return new Promise((resolve) => {
41
26
  let settled = false;
@@ -54,21 +39,13 @@ export function probeClaudeAuth(timeoutMs = 8000) {
54
39
  const timer = setTimeout(() => { try {
55
40
  proc.kill('SIGKILL');
56
41
  }
57
- catch { /* already gone */ } finish(null); }, timeoutMs);
42
+ catch { } finish(null); }, timeoutMs);
58
43
  let out = '';
59
44
  proc.stdout?.on('data', (c) => { out += c.toString('utf-8'); });
60
45
  proc.on('error', () => { clearTimeout(timer); finish(null); });
61
46
  proc.on('close', () => { clearTimeout(timer); finish(parseAuthStatus(out)); });
62
47
  });
63
48
  }
64
- /**
65
- * Launch the local Claude Code subscription sign-in (`claude auth login
66
- * --claudeai`) with inherited stdio, so the user completes the browser / paste
67
- * flow right here. Resolves true when the child exits (any exit code — the caller
68
- * re-probes to learn the outcome), or false if it can't be spawned at all (CLI
69
- * missing / too old for `auth login`), in which case the poll below still carries
70
- * a sign-in the user does in another terminal.
71
- */
72
49
  export function launchClaudeLogin() {
73
50
  return new Promise((resolve) => {
74
51
  let proc;
@@ -84,16 +61,6 @@ export function launchClaudeLogin() {
84
61
  });
85
62
  }
86
63
  const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
87
- /**
88
- * Poll `claude auth status` until it reports signed-in, or the timeout elapses.
89
- * This is also what catches a sign-in the user completes in ANOTHER terminal
90
- * window: on macOS the credential lives in the Keychain, which no process can
91
- * watch, so polling is the only cross-platform detector. The Agent SDK re-reads
92
- * credentials on every query(), so once this returns true the very next turn
93
- * works with no bridge restart. `onWaiting` fires once, the first time we find
94
- * the user still isn't signed in (so the caller can print a waiting line without
95
- * spamming it). Returns true if signed in, false on timeout.
96
- */
97
64
  export async function waitForClaudeLogin(opts = {}) {
98
65
  const intervalMs = opts.intervalMs ?? 3000;
99
66
  const timeoutMs = opts.timeoutMs ?? 5 * 60 * 1000;
@@ -112,10 +79,6 @@ export async function waitForClaudeLogin(opts = {}) {
112
79
  await sleep(intervalMs);
113
80
  }
114
81
  }
115
- /**
116
- * Minimal Y/n prompt. Returns the boolean answer; empty input returns `def`.
117
- * Caller is responsible for only invoking this when stdin is a TTY.
118
- */
119
82
  export function promptYesNo(question, def) {
120
83
  return new Promise((resolve) => {
121
84
  const rl = createInterface({ input: process.stdin, output: process.stdout });
package/dist/config.js CHANGED
@@ -1,19 +1,6 @@
1
1
  import { emitKeypressEvents } from 'readline';
2
2
  import { spawn } from 'child_process';
3
- // ─── In-memory model choice ───────────────────────────────────────────────────
4
- //
5
- // The bridge prompts for a model on every interactive startup. The choice is
6
- // kept in memory for the life of the process — nothing is written to disk.
7
- // In a non-TTY environment the prompt is skipped and Claude Code's own default
8
- // is used.
9
3
  let selectedModel = null;
10
- // ─── Default-model probe ──────────────────────────────────────────────────────
11
- /**
12
- * Asks the local `claude` CLI which model it would pick by default, by reading
13
- * the `model` field of the `system/init` stream-json event and killing the
14
- * process before any model call completes. Returns null on timeout or if the
15
- * CLI isn't installed — we never want this auxiliary lookup to block startup.
16
- */
17
4
  function detectClaudeDefaultModel() {
18
5
  return new Promise((resolve) => {
19
6
  let settled = false;
@@ -64,34 +51,12 @@ function detectClaudeDefaultModel() {
64
51
  return;
65
52
  }
66
53
  }
67
- catch { /* ignore non-JSON lines */ }
54
+ catch { }
68
55
  }
69
56
  });
70
- // Send one empty user message so claude proceeds past startup and emits
71
- // the init event. We kill before any assistant turn runs, so no tokens
72
- // are billed.
73
57
  proc.stdin?.end('{"type":"user","message":{"role":"user","content":""}}\n');
74
58
  });
75
59
  }
76
- // The fixed menu — keep the option count small so the timeout default is easy
77
- // to glance at. Option 1's `model: null` means "let Claude Code pick" (no
78
- // `--model` flag passed to the subprocess). This is the DYNAMIC default: it
79
- // tracks whatever model Claude Code actually serves for the user's plan (the
80
- // probe above surfaces it as a live suffix — on most accounts today that's the
81
- // 1M-context Opus) and self-heals if a pinned id is ever unservable. The
82
- // remaining rows pin explicit ids for users who want a specific model.
83
- // `num` must stay contiguous 1..N in array order — the jump-key handler maps a
84
- // typed digit `n` to `idx = n - 1`.
85
- //
86
- // NB there is no way to *enumerate* the models available on subscription auth:
87
- // the `/v1/models` API needs an API key (the bridge strips ANTHROPIC_API_KEY —
88
- // see claude.ts) and the CLI exposes only the single default via the init event.
89
- // So this list is hand-maintained — add/remove ids here as the roster changes.
90
- // (We dropped the standalone `claude-opus-4-8[1m]` row: it duplicated option 1,
91
- // whose Claude Code default already resolves to the 1M Opus on subscription, and
92
- // pinning `[1m]` risked a hard failure where the null default degrades gracefully.
93
- // An earlier every-turn `400 / "unknown"` was once blamed on `[1m]` but was
94
- // actually a tool-schema bug that hit every model — see vault/Bugs.md.)
95
60
  const MODEL_OPTIONS = [
96
61
  { num: 1, model: null, label: 'Use Claude Code default' },
97
62
  { num: 2, model: 'claude-opus-5', label: 'claude-opus-5' },
@@ -103,10 +68,6 @@ const MODEL_OPTIONS = [
103
68
  { num: 8, model: 'claude-haiku-4-5', label: 'claude-haiku-4-5' },
104
69
  ];
105
70
  const PROMPT_TIMEOUT_MS = 10_000;
106
- // Default to option 1 ("Use Claude Code default") — the dynamic choice that
107
- // follows whatever Claude Code serves for the user's plan (currently the 1M
108
- // Opus on most accounts) and degrades gracefully rather than pinning a possibly
109
- // unservable id. Users can arrow to a pinned model if they want one.
110
71
  const DEFAULT_OPTION_NUM = 1;
111
72
  function promptForModel(defaultModel) {
112
73
  return new Promise((resolve) => {
@@ -187,11 +148,6 @@ function promptForModel(defaultModel) {
187
148
  }, PROMPT_TIMEOUT_MS);
188
149
  });
189
150
  }
190
- /**
191
- * Asks the user which model to use for this bridge session. The choice lives
192
- * in memory only — every startup re-prompts. In a non-TTY environment the
193
- * prompt is skipped and Claude Code's own default is used.
194
- */
195
151
  export async function ensureModelChoice() {
196
152
  if (!process.stdin.isTTY) {
197
153
  selectedModel = null;
@@ -206,7 +162,6 @@ export async function ensureModelChoice() {
206
162
  console.log(`\nUsing your Claude Code default${defaultModel ? ` (${defaultModel})` : ''}.\n`);
207
163
  }
208
164
  }
209
- /** Returns the model id chosen for this session, or null to defer to Claude Code's own default. */
210
165
  export function getBridgeModel() {
211
166
  return selectedModel;
212
167
  }