@1presence/bridge 0.73.0 → 0.75.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/accumulator.js +0 -17
- package/dist/auth.js +1 -55
- package/dist/claude.js +23 -290
- package/dist/claudeAuth.js +1 -38
- package/dist/config.js +1 -46
- package/dist/index.js +20 -223
- package/dist/outbox.js +1 -16
- package/dist/sessionPath.js +1 -18
- package/dist/timer.js +0 -4
- package/dist/update.js +0 -3
- package/package.json +1 -1
package/dist/claudeAuth.js
CHANGED
|
@@ -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 {
|
|
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 {
|
|
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
|
}
|