@1presence/bridge 0.75.0 → 0.77.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 CHANGED
@@ -53,9 +53,11 @@ Your OAuth tokens and vault data stay server-side — nothing sensitive is store
53
53
 
54
54
  ## Model
55
55
 
56
- On first run the bridge asks which Claude model you want it to use. Pick "Use Claude Code default" to defer to your local Claude Code default, or choose a specific model id to pin it (e.g. `claude-opus-5`, `claude-opus-4-8`, `claude-sonnet-5`, `claude-haiku-4-5`). The default selection (auto-chosen if the prompt times out) is "Use Claude Code default" — it tracks whatever model Claude Code serves for your plan. The choice is saved to `~/.1presence/config.json` and reused on every subsequent run.
56
+ **Every start** the bridge asks which Claude model to use — the choice is held in memory for that run only and nothing is written to disk, so restarting always asks again. Pick "Use Claude Code default" to defer to your local Claude Code default, or pin one of the latest models per family: `claude-opus-5`, `claude-fable-5`, `claude-sonnet-5`, `claude-haiku-4-5`. If the prompt times out it auto-selects "Use Claude Code default", which tracks whatever model Claude Code serves for your plan.
57
57
 
58
- The first reply each session prints the model and credential source so you can confirm what's running. To change your pick later, edit or delete `~/.1presence/config.json` — the bridge will prompt again on the next start.
58
+ Superseded versions (`claude-opus-4-8`, `claude-sonnet-4-6`, …) are deliberately not listed. If you need something other than the latest, use "Use Claude Code default" and set the model in Claude Code itself.
59
+
60
+ Every reply prints the model, the account it ran for, and your context/cost usage, so you can always see what is running — and if the model that answered isn't the one you pinned, the bridge says so explicitly. Note that only **one bridge per account** serves your turns: start a second one and the older process exits, because otherwise your turns could be answered by a terminal you aren't watching, on the model *it* pinned.
59
61
 
60
62
  ## Signing out
61
63
 
package/dist/config.js CHANGED
@@ -1,71 +1,63 @@
1
1
  import { emitKeypressEvents } from 'readline';
2
- import { spawn } from 'child_process';
2
+ import { query } from '@anthropic-ai/claude-agent-sdk';
3
3
  let selectedModel = null;
4
4
  function detectClaudeDefaultModel() {
5
5
  return new Promise((resolve) => {
6
6
  let settled = false;
7
- const finish = (v) => { if (!settled) {
7
+ const abort = new AbortController();
8
+ const finish = (v) => {
9
+ if (settled)
10
+ return;
8
11
  settled = true;
12
+ clearTimeout(timer);
13
+ try {
14
+ abort.abort();
15
+ }
16
+ catch { }
9
17
  resolve(v);
10
- } };
11
- let proc;
12
- try {
13
- proc = spawn('claude', [
14
- '-p',
15
- '--input-format', 'stream-json',
16
- '--output-format', 'stream-json',
17
- '--verbose',
18
- '--tools', '',
19
- '--setting-sources', '',
20
- ], { stdio: ['pipe', 'pipe', 'ignore'] });
21
- }
22
- catch {
23
- finish(null);
24
- return;
25
- }
26
- const timer = setTimeout(() => { try {
27
- proc.kill('SIGKILL');
28
- }
29
- catch { } ; finish(null); }, 5000);
30
- proc.on('error', () => { clearTimeout(timer); finish(null); });
31
- proc.on('close', () => { clearTimeout(timer); finish(null); });
32
- let buf = '';
33
- proc.stdout?.on('data', (chunk) => {
34
- buf += chunk.toString('utf-8');
35
- const lines = buf.split('\n');
36
- buf = lines.pop() ?? '';
37
- for (const line of lines) {
38
- const trimmed = line.trim();
39
- if (!trimmed)
40
- continue;
41
- try {
42
- const ev = JSON.parse(trimmed);
43
- if (ev['type'] === 'system' && ev['subtype'] === 'init') {
44
- const model = ev['model'];
45
- clearTimeout(timer);
46
- try {
47
- proc.kill('SIGKILL');
48
- }
49
- catch { }
50
- finish(typeof model === 'string' ? model : null);
18
+ };
19
+ const timer = setTimeout(() => finish(null), 5000);
20
+ const { ANTHROPIC_API_KEY: _stripped, ...safeEnv } = process.env;
21
+ void (async () => {
22
+ try {
23
+ const stream = query({
24
+ prompt: (async function* () {
25
+ yield {
26
+ type: 'user',
27
+ message: { role: 'user', content: '' },
28
+ parent_tool_use_id: null,
29
+ };
30
+ })(),
31
+ options: {
32
+ systemPrompt: '',
33
+ settingSources: [],
34
+ tools: [],
35
+ permissionMode: 'default',
36
+ env: safeEnv,
37
+ abortController: abort,
38
+ },
39
+ });
40
+ for await (const msg of stream) {
41
+ const ev = msg;
42
+ if (ev.type === 'system' && ev.subtype === 'init') {
43
+ finish(typeof ev.model === 'string' ? ev.model : null);
51
44
  return;
52
45
  }
53
46
  }
54
- catch { }
47
+ finish(null);
48
+ }
49
+ catch {
50
+ finish(null);
55
51
  }
56
- });
57
- proc.stdin?.end('{"type":"user","message":{"role":"user","content":""}}\n');
52
+ })();
58
53
  });
59
54
  }
60
55
  const MODEL_OPTIONS = [
61
56
  { num: 1, model: null, label: 'Use Claude Code default' },
62
57
  { num: 2, model: 'claude-opus-5', label: 'claude-opus-5' },
63
- { num: 3, model: 'claude-opus-4-8', label: 'claude-opus-4-8' },
64
- { num: 4, model: 'claude-opus-4-7', label: 'claude-opus-4-7' },
65
- { num: 5, model: 'claude-fable-5', label: 'claude-fable-5' },
66
- { num: 6, model: 'claude-sonnet-5', label: 'claude-sonnet-5' },
67
- { num: 7, model: 'claude-sonnet-4-6', label: 'claude-sonnet-4-6' },
68
- { num: 8, model: 'claude-haiku-4-5', label: 'claude-haiku-4-5' },
58
+ { num: 3, model: 'claude-fable-5', label: 'claude-fable-5' },
59
+ { num: 4, model: 'claude-sonnet-5', label: 'claude-sonnet-5' },
60
+ { num: 5, model: 'claude-haiku-4-5', label: 'claude-haiku-4-5' },
69
61
  ];
70
62
  const PROMPT_TIMEOUT_MS = 10_000;
71
63
  const DEFAULT_OPTION_NUM = 1;
package/dist/index.js CHANGED
@@ -10,7 +10,7 @@ import { getValidAuth, ensureFreshToken, forceRefreshToken, isTokenValid, AuthCa
10
10
  import { spawnClaude, killAll, cancelConversation, setVerbose, setDebug, paint, SECTION_COLORS, getCliVersion, getRateLimitWindow } from './claude.js';
11
11
  import { ensureModelChoice } from './config.js';
12
12
  import { probeClaudeAuth, launchClaudeLogin, waitForClaudeLogin, promptYesNo } from './claudeAuth.js';
13
- import { checkAndUpdate } from './update.js';
13
+ import { checkAndUpdate, warnIfRuntimeStale } from './update.js';
14
14
  import { makeBridgeAccumulator, postSaveTurn } from './accumulator.js';
15
15
  import { writeSpool, deleteSpool, listSpool } from './outbox.js';
16
16
  import { startTurnTimer, stopTurnTimer, formatElapsed } from './timer.js';
@@ -673,6 +673,7 @@ async function main() {
673
673
  return;
674
674
  const auth = await getValidAuth(GATEWAY_HTTP, PWA_URL);
675
675
  currentAuth = auth;
676
+ await warnIfRuntimeStale();
676
677
  await ensureModelChoice();
677
678
  await ensureClaudeCodeLogin();
678
679
  process.stdout.write('Setting up…');
package/dist/update.js CHANGED
@@ -1,5 +1,7 @@
1
1
  import { spawn } from 'child_process';
2
2
  import { createRequire } from 'module';
3
+ import { readFileSync } from 'fs';
4
+ import { dirname, join } from 'path';
3
5
  const { version } = createRequire(import.meta.url)('../package.json');
4
6
  function isNewer(a, b) {
5
7
  const pa = a.split('.').map(Number);
@@ -35,3 +37,50 @@ export async function checkAndUpdate() {
35
37
  return false;
36
38
  }
37
39
  }
40
+ function bundledSdkVersion() {
41
+ try {
42
+ const req = createRequire(import.meta.url);
43
+ let dir = dirname(req.resolve('@anthropic-ai/claude-agent-sdk'));
44
+ for (let i = 0; i < 5; i++) {
45
+ try {
46
+ const pkg = JSON.parse(readFileSync(join(dir, 'package.json'), 'utf-8'));
47
+ if (pkg.name === '@anthropic-ai/claude-agent-sdk' && typeof pkg.version === 'string')
48
+ return pkg.version;
49
+ }
50
+ catch { }
51
+ const parent = dirname(dir);
52
+ if (parent === dir)
53
+ break;
54
+ dir = parent;
55
+ }
56
+ return null;
57
+ }
58
+ catch {
59
+ return null;
60
+ }
61
+ }
62
+ export async function warnIfRuntimeStale() {
63
+ const installed = bundledSdkVersion();
64
+ if (!installed)
65
+ return null;
66
+ try {
67
+ const res = await fetch('https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk/latest', {
68
+ signal: AbortSignal.timeout(3000),
69
+ });
70
+ if (!res.ok)
71
+ return null;
72
+ const { version: latest } = await res.json();
73
+ if (!latest || !isNewer(latest, installed))
74
+ return null;
75
+ console.log(`\n⚠ The Claude Code runtime bundled with this bridge is out of date (${installed} → ${latest} available).`);
76
+ console.log(' It runs your turns and it decides what "Use Claude Code default" resolves to, so that');
77
+ console.log(' default may be an older model than your account can serve. Pin a model in the next');
78
+ console.log(' prompt to be certain. The runtime ships with the bridge — updating your local `claude`');
79
+ console.log(' will NOT change it. Restart the bridge to pick up a newer release; if this warning');
80
+ console.log(' persists, the newest published bridge itself needs updating — please report it.');
81
+ return { installed, latest };
82
+ }
83
+ catch {
84
+ return null;
85
+ }
86
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@1presence/bridge",
3
- "version": "0.75.0",
3
+ "version": "0.77.0",
4
4
  "description": "Run 1Presence on your Mac and use your Claude.ai Pro subscription from any device",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -21,7 +21,7 @@
21
21
  "start": "node dist/index.js"
22
22
  },
23
23
  "dependencies": {
24
- "@anthropic-ai/claude-agent-sdk": "^0.3.153",
24
+ "@anthropic-ai/claude-agent-sdk": "^0.3.234",
25
25
  "ws": "^8.20.0",
26
26
  "zod": "^4.0.0"
27
27
  },