@1presence/bridge 0.40.0 → 0.42.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/dist/config.js CHANGED
@@ -1,9 +1,5 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ensureModelChoice = ensureModelChoice;
4
- exports.getBridgeModel = getBridgeModel;
5
- const readline_1 = require("readline");
6
- const child_process_1 = require("child_process");
1
+ import { emitKeypressEvents } from 'readline';
2
+ import { spawn } from 'child_process';
7
3
  // ─── In-memory model choice ───────────────────────────────────────────────────
8
4
  //
9
5
  // The bridge prompts for a model on every interactive startup. The choice is
@@ -27,7 +23,7 @@ function detectClaudeDefaultModel() {
27
23
  } };
28
24
  let proc;
29
25
  try {
30
- proc = (0, child_process_1.spawn)('claude', [
26
+ proc = spawn('claude', [
31
27
  '-p',
32
28
  '--input-format', 'stream-json',
33
29
  '--output-format', 'stream-json',
@@ -111,7 +107,7 @@ function promptForModel(defaultModel) {
111
107
  }
112
108
  };
113
109
  render();
114
- (0, readline_1.emitKeypressEvents)(process.stdin);
110
+ emitKeypressEvents(process.stdin);
115
111
  const wasRaw = process.stdin.isRaw;
116
112
  if (process.stdin.isTTY)
117
113
  process.stdin.setRawMode(true);
@@ -172,7 +168,7 @@ function promptForModel(defaultModel) {
172
168
  * in memory only — every startup re-prompts. In a non-TTY environment the
173
169
  * prompt is skipped and Claude Code's own default is used.
174
170
  */
175
- async function ensureModelChoice() {
171
+ export async function ensureModelChoice() {
176
172
  if (!process.stdin.isTTY) {
177
173
  selectedModel = null;
178
174
  return;
@@ -187,6 +183,6 @@ async function ensureModelChoice() {
187
183
  }
188
184
  }
189
185
  /** Returns the model id chosen for this session, or null to defer to Claude Code's own default. */
190
- function getBridgeModel() {
186
+ export function getBridgeModel() {
191
187
  return selectedModel;
192
188
  }
package/dist/index.js CHANGED
@@ -1,29 +1,29 @@
1
1
  #!/usr/bin/env node
2
- "use strict";
3
- var __importDefault = (this && this.__importDefault) || function (mod) {
4
- return (mod && mod.__esModule) ? mod : { "default": mod };
5
- };
6
- Object.defineProperty(exports, "__esModule", { value: true });
7
- const ws_1 = __importDefault(require("ws"));
8
- const fs_1 = require("fs");
9
- const os_1 = require("os");
10
- const path_1 = require("path");
11
- const auth_1 = require("./auth");
12
- const claude_1 = require("./claude");
13
- const config_1 = require("./config");
14
- const update_1 = require("./update");
15
- const accumulator_1 = require("./accumulator");
16
- const outbox_1 = require("./outbox");
17
- const timer_1 = require("./timer");
18
- const package_json_1 = require("../package.json");
2
+ import WebSocket from 'ws';
3
+ import { writeFileSync, chmodSync, existsSync, statSync, readdirSync } from 'fs';
4
+ import { tmpdir } from 'os';
5
+ import { join, dirname } from 'path';
6
+ import { fileURLToPath } from 'url';
7
+ import { createRequire } from 'module';
8
+ import { getValidAuth, ensureFreshToken, isTokenValid, AuthCancelledError } from './auth.js';
9
+ import { spawnClaude, killAll, cancelConversation, setVerbose, setDebug, paint, SECTION_COLORS } from './claude.js';
10
+ import { ensureModelChoice } from './config.js';
11
+ import { checkAndUpdate } from './update.js';
12
+ import { makeBridgeAccumulator, postSaveTurn } from './accumulator.js';
13
+ import { writeSpool, deleteSpool, listSpool } from './outbox.js';
14
+ import { startTurnTimer, stopTurnTimer, formatElapsed } from './timer.js';
15
+ // ESM has no __dirname; derive it. JSON version is read via createRequire to
16
+ // avoid version-sensitive import assertions on a published CLI bin.
17
+ const __dirname = dirname(fileURLToPath(import.meta.url));
18
+ const { version } = createRequire(import.meta.url)('../package.json');
19
19
  // Published tarballs don't ship src/, so this fires only when running the
20
20
  // dist build from a live workspace checkout. Catches the trap where editing
21
21
  // src/ without re-running tsc leaves you executing stale dist code — banner
22
22
  // version matches package.json but behavior doesn't match the source.
23
23
  if (__dirname.endsWith('dist')) {
24
- const srcDir = (0, path_1.join)(__dirname, '..', 'src');
25
- if ((0, fs_1.existsSync)(srcDir)) {
26
- const newest = (dir) => Math.max(...(0, fs_1.readdirSync)(dir).map(f => (0, fs_1.statSync)((0, path_1.join)(dir, f)).mtimeMs));
24
+ const srcDir = join(__dirname, '..', 'src');
25
+ if (existsSync(srcDir)) {
26
+ const newest = (dir) => Math.max(...readdirSync(dir).map(f => statSync(join(dir, f)).mtimeMs));
27
27
  if (newest(srcDir) > newest(__dirname)) {
28
28
  console.error('Bridge dist is stale (src/ has been edited since last build). Run: npm run build');
29
29
  process.exit(1);
@@ -157,7 +157,7 @@ async function fetchSystemPrompt(token, agentSlug) {
157
157
  }
158
158
  // ─── Setup files ──────────────────────────────────────────────────────────────
159
159
  function tmpFile(name) {
160
- return (0, path_1.join)((0, os_1.tmpdir)(), name);
160
+ return join(tmpdir(), name);
161
161
  }
162
162
  // Fetch the system prompt and write it to /tmp/agent-${uid}.md. The hosted
163
163
  // runtime rebuilds buildSystemBlocks() per turn (dynamic context: vault state,
@@ -169,9 +169,9 @@ async function writeSystemPrompt(auth, agentSlug) {
169
169
  const systemPrompt = await fetchSystemPrompt(token, agentSlug);
170
170
  writeRestricted(tmpFile(`agent-${uid}.md`), systemPrompt);
171
171
  if (VERBOSE) {
172
- console.log((0, claude_1.paint)(claude_1.SECTION_COLORS.system, '\n[bridge:verbose] ─── system prompt ───────────────────────'));
173
- console.log((0, claude_1.paint)(claude_1.SECTION_COLORS.system, systemPrompt));
174
- console.log((0, claude_1.paint)(claude_1.SECTION_COLORS.system, '[bridge:verbose] ─── end system prompt ───────────────────\n'));
172
+ console.log(paint(SECTION_COLORS.system, '\n[bridge:verbose] ─── system prompt ───────────────────────'));
173
+ console.log(paint(SECTION_COLORS.system, systemPrompt));
174
+ console.log(paint(SECTION_COLORS.system, '[bridge:verbose] ─── end system prompt ───────────────────\n'));
175
175
  }
176
176
  }
177
177
  function writeMcpConfig(auth) {
@@ -195,8 +195,8 @@ async function writeSetupFiles(auth, agentSlug) {
195
195
  // state. writeFileSync's mode only takes effect on file creation — chmodSync
196
196
  // covers the overwrite case so a legacy 0644 file gets tightened on next run.
197
197
  function writeRestricted(path, data) {
198
- (0, fs_1.writeFileSync)(path, data, { mode: 0o600 });
199
- (0, fs_1.chmodSync)(path, 0o600);
198
+ writeFileSync(path, data, { mode: 0o600 });
199
+ chmodSync(path, 0o600);
200
200
  }
201
201
  // ─── Helpers ──────────────────────────────────────────────────────────────────
202
202
  const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
@@ -208,7 +208,7 @@ async function handleMessage(conversationId, text, sessionId, history, auth, vau
208
208
  // Refresh JWT if <10 min remaining before spawning Claude
209
209
  let activeAuth = auth;
210
210
  try {
211
- const freshAuth = await (0, auth_1.ensureFreshToken)(auth);
211
+ const freshAuth = await ensureFreshToken(auth);
212
212
  if (freshAuth.token !== auth.token) {
213
213
  currentAuth = freshAuth;
214
214
  activeAuth = freshAuth;
@@ -218,11 +218,11 @@ async function handleMessage(conversationId, text, sessionId, history, auth, vau
218
218
  catch (err) {
219
219
  // If the cached token still has time, proceed — refresh was preemptive.
220
220
  // If it's already invalid, MCP calls will 401 mid-turn — fail fast instead.
221
- if (!(0, auth_1.isTokenValid)(auth.token)) {
221
+ if (!isTokenValid(auth.token)) {
222
222
  const message = 'Authentication expired and refresh failed — please restart the bridge to sign in again.';
223
- (0, timer_1.stopTurnTimer)();
223
+ stopTurnTimer();
224
224
  console.error(`[bridge] ${message} (${err.message})`);
225
- if (currentWs?.readyState === ws_1.default.OPEN) {
225
+ if (currentWs?.readyState === WebSocket.OPEN) {
226
226
  currentWs.send(JSON.stringify({ type: 'error', conversationId, message }));
227
227
  }
228
228
  return;
@@ -241,15 +241,15 @@ async function handleMessage(conversationId, text, sessionId, history, auth, vau
241
241
  }
242
242
  catch (err) {
243
243
  const message = `System prompt refresh failed: ${err.message}`;
244
- (0, timer_1.stopTurnTimer)();
244
+ stopTurnTimer();
245
245
  console.error(`[${new Date().toLocaleTimeString()}] ✗ ${message}`);
246
- if (currentWs?.readyState === ws_1.default.OPEN) {
246
+ if (currentWs?.readyState === WebSocket.OPEN) {
247
247
  currentWs.send(JSON.stringify({ type: 'error', conversationId, message }));
248
248
  }
249
249
  return;
250
250
  }
251
251
  let responding = false;
252
- const accumulator = (0, accumulator_1.makeBridgeAccumulator)();
252
+ const accumulator = makeBridgeAccumulator();
253
253
  const startedAt = Date.now();
254
254
  const turnSessionId = sessionId ?? conversationId; // gateway always supplies one; defensive fallback
255
255
  // The CLI's `--session-id` is treated as a "claim this new session ID"
@@ -283,14 +283,14 @@ async function handleMessage(conversationId, text, sessionId, history, auth, vau
283
283
  // ack is recoverable by drain-on-startup. The gateway dedupes on
284
284
  // conversationId, so a replay is idempotent.
285
285
  try {
286
- (0, outbox_1.writeSpool)(record);
286
+ writeSpool(record);
287
287
  }
288
288
  catch (err) {
289
289
  console.warn(`[bridge] spool write failed: ${err.message}`);
290
290
  }
291
- const result = await (0, accumulator_1.postSaveTurn)(GATEWAY_HTTP, activeAuth.token, record);
291
+ const result = await postSaveTurn(GATEWAY_HTTP, activeAuth.token, record);
292
292
  if (result.ok) {
293
- (0, outbox_1.deleteSpool)(record.conversationId);
293
+ deleteSpool(record.conversationId);
294
294
  }
295
295
  else {
296
296
  // Leave the spool file in place — next startup or next successful
@@ -298,7 +298,7 @@ async function handleMessage(conversationId, text, sessionId, history, auth, vau
298
298
  console.warn(`[bridge] save-turn POST failed (${result.status}): ${result.error ?? 'unknown'} — kept on disk for retry`);
299
299
  }
300
300
  }
301
- (0, claude_1.spawnClaude)({
301
+ spawnClaude({
302
302
  conversationId,
303
303
  presenceSessionId: claudePinnedSessionId,
304
304
  text,
@@ -313,7 +313,7 @@ async function handleMessage(conversationId, text, sessionId, history, auth, vau
313
313
  responding = true;
314
314
  console.log(`[${new Date().toLocaleTimeString()}] ◐ responding…`);
315
315
  }
316
- if (currentWs?.readyState === ws_1.default.OPEN) {
316
+ if (currentWs?.readyState === WebSocket.OPEN) {
317
317
  currentWs.send(JSON.stringify({ type: 'stream', conversationId, event }));
318
318
  }
319
319
  },
@@ -321,13 +321,13 @@ async function handleMessage(conversationId, text, sessionId, history, auth, vau
321
321
  // Ephemeral, non-persisted thread notice (admin-only Local Mode). Relayed
322
322
  // by the gateway to the PWA SSE stream as a `notice` AgentEvent; it does
323
323
  // NOT go through the turn accumulator, so it never lands in history.
324
- if (currentWs?.readyState === ws_1.default.OPEN) {
324
+ if (currentWs?.readyState === WebSocket.OPEN) {
325
325
  currentWs.send(JSON.stringify({ type: 'notice', conversationId, message }));
326
326
  }
327
327
  },
328
328
  onDone: (messageCount, costUsd, usage, model, contextTokens) => {
329
- const elapsed = (0, timer_1.stopTurnTimer)();
330
- const parts = [(0, timer_1.formatElapsed)(elapsed)];
329
+ const elapsed = stopTurnTimer();
330
+ const parts = [formatElapsed(elapsed)];
331
331
  if (usage)
332
332
  parts.push(`in:${usage.input_tokens} out:${usage.output_tokens}`);
333
333
  const costStr = costUsd === 0 ? '$0.0000 (plan usage)' : `$${costUsd.toFixed(4)}`;
@@ -341,9 +341,9 @@ async function handleMessage(conversationId, text, sessionId, history, auth, vau
341
341
  sessionCostUsd += costUsd;
342
342
  const ctxPct = Math.max(0, Math.min(100, Math.round((contextTokens / contextWindowFor(model)) * 100)));
343
343
  const costSeg = sessionCostUsd > 0 ? `$${sessionCostUsd.toFixed(2)} session` : 'plan usage';
344
- console.log((0, claude_1.paint)('90', ` 🤖 ${friendlyModelName(model)} · 🧠 ${ctxPct}% · 💰 ${costSeg}`));
344
+ console.log(paint('90', ` 🤖 ${friendlyModelName(model)} · 🧠 ${ctxPct}% · 💰 ${costSeg}`));
345
345
  const mapped = toBridgeUsage(usage);
346
- if (currentWs?.readyState === ws_1.default.OPEN) {
346
+ if (currentWs?.readyState === WebSocket.OPEN) {
347
347
  currentWs.send(JSON.stringify({
348
348
  type: 'done',
349
349
  conversationId,
@@ -357,10 +357,10 @@ async function handleMessage(conversationId, text, sessionId, history, auth, vau
357
357
  void finalizeAndPost(buildSpoolRecord(mapped, model));
358
358
  },
359
359
  onError: (message, usage, model) => {
360
- const elapsed = (0, timer_1.stopTurnTimer)();
361
- console.error(`[${new Date().toLocaleTimeString()}] ✗ ${message} (${(0, timer_1.formatElapsed)(elapsed)})`);
360
+ const elapsed = stopTurnTimer();
361
+ console.error(`[${new Date().toLocaleTimeString()}] ✗ ${message} (${formatElapsed(elapsed)})`);
362
362
  const mapped = toBridgeUsage(usage);
363
- if (currentWs?.readyState === ws_1.default.OPEN) {
363
+ if (currentWs?.readyState === WebSocket.OPEN) {
364
364
  currentWs.send(JSON.stringify({
365
365
  type: 'error',
366
366
  conversationId,
@@ -391,14 +391,14 @@ function toBridgeUsage(usage) {
391
391
  // dedupes on conversationId — if it already saved via the WS path, the
392
392
  // reply is finalized=false and we still delete the spool.
393
393
  async function drainOutbox(auth) {
394
- const records = (0, outbox_1.listSpool)();
394
+ const records = listSpool();
395
395
  if (records.length === 0)
396
396
  return;
397
397
  console.log(`[bridge] draining ${records.length} pending save record${records.length === 1 ? '' : 's'}…`);
398
398
  for (const record of records) {
399
- const result = await (0, accumulator_1.postSaveTurn)(GATEWAY_HTTP, auth.token, record);
399
+ const result = await postSaveTurn(GATEWAY_HTTP, auth.token, record);
400
400
  if (result.ok) {
401
- (0, outbox_1.deleteSpool)(record.conversationId);
401
+ deleteSpool(record.conversationId);
402
402
  }
403
403
  else {
404
404
  console.warn(`[bridge] drain POST failed (${result.status}): ${result.error ?? 'unknown'} — leaving on disk`);
@@ -411,14 +411,14 @@ async function drainOutbox(auth) {
411
411
  const PING_INTERVAL_MS = 30_000;
412
412
  const PONG_TIMEOUT_MS = 10_000;
413
413
  function connect(auth, retryDelay = 1000) {
414
- const ws = new ws_1.default(GATEWAY_WS, {
414
+ const ws = new WebSocket(GATEWAY_WS, {
415
415
  headers: { Authorization: `Bearer ${auth.token}` },
416
416
  });
417
417
  let pingTimer = null;
418
418
  let pongTimer = null;
419
419
  function startPing() {
420
420
  pingTimer = setInterval(() => {
421
- if (ws.readyState !== ws_1.default.OPEN)
421
+ if (ws.readyState !== WebSocket.OPEN)
422
422
  return;
423
423
  ws.send(JSON.stringify({ type: 'ping', ts: Date.now() }));
424
424
  pongTimer = setTimeout(() => {
@@ -473,7 +473,7 @@ function connect(auth, retryDelay = 1000) {
473
473
  // (PWA→gateway connection dropped). Kill the local Claude Code process for
474
474
  // this conversation so it stops generating instead of running to the end.
475
475
  if (msg.type === 'cancel' && msg.conversationId) {
476
- const cancelled = (0, claude_1.cancelConversation)(msg.conversationId);
476
+ const cancelled = cancelConversation(msg.conversationId);
477
477
  if (cancelled)
478
478
  console.log(`[bridge] ✕ stopped conversation ${msg.conversationId}`);
479
479
  return;
@@ -484,9 +484,9 @@ function connect(auth, retryDelay = 1000) {
484
484
  const ts = new Date().toLocaleTimeString();
485
485
  const hist = Array.isArray(history) ? history : [];
486
486
  console.log(`[${ts}] ▶ ${text}${hist.length ? ` (history: ${hist.length} turn${hist.length === 1 ? '' : 's'})` : ''}`);
487
- (0, timer_1.startTurnTimer)();
487
+ startTurnTimer();
488
488
  handleMessage(conversationId, text, sessionId ?? null, hist, auth, vaultFileOpen, clientCapabilities, syncedFolders, agentSlug).catch((err) => {
489
- (0, timer_1.stopTurnTimer)();
489
+ stopTurnTimer();
490
490
  console.error(`[bridge] handleMessage error: ${err.message}`);
491
491
  });
492
492
  });
@@ -529,24 +529,24 @@ function connect(auth, retryDelay = 1000) {
529
529
  }
530
530
  // ─── Main ─────────────────────────────────────────────────────────────────────
531
531
  async function main() {
532
- console.log(`1Presence Bridge v${package_json_1.version}\n`);
532
+ console.log(`1Presence Bridge v${version}\n`);
533
533
  if (VERBOSE) {
534
- (0, claude_1.setVerbose)(true);
534
+ setVerbose(true);
535
535
  console.log('[bridge:verbose] verbose logging enabled — system prompts (magenta), user prompts (blue), assistant text (green), tool inputs (cyan), and tool outputs (yellow) will be printed, colour-coded by kind.\n');
536
536
  }
537
537
  if (DEBUG) {
538
- (0, claude_1.setDebug)(true);
538
+ setDebug(true);
539
539
  console.log('[bridge:debug] debug transcript enabled — user prompts, assistant text, tool inputs, and tool outputs will be printed (system prompt omitted; use --verbose for that).\n');
540
540
  }
541
- if (await (0, update_1.checkAndUpdate)())
541
+ if (await checkAndUpdate())
542
542
  return;
543
543
  // Auth
544
- const auth = await (0, auth_1.getValidAuth)(GATEWAY_HTTP, PWA_URL);
544
+ const auth = await getValidAuth(GATEWAY_HTTP, PWA_URL);
545
545
  currentAuth = auth;
546
546
  // One-time interactive model choice (only prompts on first run; saved to
547
547
  // ~/.1presence/config.json). In a non-TTY environment this is a no-op and
548
548
  // Claude Code's own default is used.
549
- await (0, config_1.ensureModelChoice)();
549
+ await ensureModelChoice();
550
550
  // Write system prompt + MCP config. If this fails the bridge is dead in the
551
551
  // water — surface the underlying error rather than letting it bubble up as
552
552
  // a generic "Fatal:" with no context.
@@ -566,7 +566,7 @@ async function main() {
566
566
  // Graceful shutdown
567
567
  const shutdown = () => {
568
568
  console.log('\nShutting down…');
569
- (0, claude_1.killAll)();
569
+ killAll();
570
570
  process.exit(0);
571
571
  };
572
572
  process.on('SIGINT', shutdown);
@@ -588,7 +588,7 @@ async function main() {
588
588
  });
589
589
  }
590
590
  main().catch((err) => {
591
- if (err instanceof auth_1.AuthCancelledError) {
591
+ if (err instanceof AuthCancelledError) {
592
592
  console.error(`\n${err.message}`);
593
593
  console.error('Run `npx @1presence/bridge` again when you are ready to sign in.');
594
594
  process.exit(0);
package/dist/outbox.js CHANGED
@@ -1,11 +1,6 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.writeSpool = writeSpool;
4
- exports.deleteSpool = deleteSpool;
5
- exports.listSpool = listSpool;
6
- const fs_1 = require("fs");
7
- const os_1 = require("os");
8
- const path_1 = require("path");
1
+ import { mkdirSync, writeFileSync, readdirSync, readFileSync, unlinkSync } from 'fs';
2
+ import { homedir } from 'os';
3
+ import { join } from 'path';
9
4
  // ─── On-disk turn spool ───────────────────────────────────────────────────────
10
5
  //
11
6
  // Each in-flight bridge turn writes a record to ~/.1presence/outbox/. The file
@@ -20,31 +15,31 @@ const path_1 = require("path");
20
15
  //
21
16
  // Payload mode 0600 — the file contains the user's assistant transcript and
22
17
  // tool inputs. Tightened on every write to handle legacy world-readable files.
23
- const OUTBOX_DIR = (0, path_1.join)((0, os_1.homedir)(), '.1presence', 'outbox');
18
+ const OUTBOX_DIR = join(homedir(), '.1presence', 'outbox');
24
19
  function ensureDir() {
25
- (0, fs_1.mkdirSync)(OUTBOX_DIR, { recursive: true });
20
+ mkdirSync(OUTBOX_DIR, { recursive: true });
26
21
  }
27
22
  function pathFor(conversationId) {
28
- return (0, path_1.join)(OUTBOX_DIR, `${conversationId}.json`);
23
+ return join(OUTBOX_DIR, `${conversationId}.json`);
29
24
  }
30
- function writeSpool(record) {
25
+ export function writeSpool(record) {
31
26
  ensureDir();
32
- (0, fs_1.writeFileSync)(pathFor(record.conversationId), JSON.stringify(record), { mode: 0o600 });
27
+ writeFileSync(pathFor(record.conversationId), JSON.stringify(record), { mode: 0o600 });
33
28
  }
34
- function deleteSpool(conversationId) {
29
+ export function deleteSpool(conversationId) {
35
30
  try {
36
- (0, fs_1.unlinkSync)(pathFor(conversationId));
31
+ unlinkSync(pathFor(conversationId));
37
32
  }
38
33
  catch { /* already gone — fine */ }
39
34
  }
40
- function listSpool() {
35
+ export function listSpool() {
41
36
  ensureDir();
42
37
  const out = [];
43
- for (const file of (0, fs_1.readdirSync)(OUTBOX_DIR)) {
38
+ for (const file of readdirSync(OUTBOX_DIR)) {
44
39
  if (!file.endsWith('.json'))
45
40
  continue;
46
41
  try {
47
- const raw = (0, fs_1.readFileSync)((0, path_1.join)(OUTBOX_DIR, file), 'utf-8');
42
+ const raw = readFileSync(join(OUTBOX_DIR, file), 'utf-8');
48
43
  out.push(JSON.parse(raw));
49
44
  }
50
45
  catch {
package/dist/timer.js CHANGED
@@ -1,12 +1,7 @@
1
- "use strict";
2
1
  // Live elapsed-time indicator for the active turn. Writes `\r\x1b[K⏱ Xs`
3
2
  // once per second; wraps console.log/error/warn so that any other output
4
3
  // clears the timer line before printing, then redraws the timer on the new
5
4
  // bottom line. Idempotent — stop is safe to call multiple times.
6
- Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.formatElapsed = formatElapsed;
8
- exports.startTurnTimer = startTurnTimer;
9
- exports.stopTurnTimer = stopTurnTimer;
10
5
  let intervalId = null;
11
6
  let startedAt = 0;
12
7
  let originalLog = null;
@@ -19,14 +14,14 @@ function draw() {
19
14
  const elapsedSec = Math.floor((Date.now() - startedAt) / 1000);
20
15
  process.stdout.write(`\r\x1b[K⏱ ${formatElapsed(elapsedSec)}`);
21
16
  }
22
- function formatElapsed(seconds) {
17
+ export function formatElapsed(seconds) {
23
18
  if (seconds < 60)
24
19
  return `${seconds}s`;
25
20
  const m = Math.floor(seconds / 60);
26
21
  const s = seconds % 60;
27
22
  return `${m}m ${s.toString().padStart(2, '0')}s`;
28
23
  }
29
- function startTurnTimer() {
24
+ export function startTurnTimer() {
30
25
  if (intervalId !== null)
31
26
  return;
32
27
  startedAt = Date.now();
@@ -39,7 +34,7 @@ function startTurnTimer() {
39
34
  draw();
40
35
  intervalId = setInterval(draw, 1000);
41
36
  }
42
- function stopTurnTimer() {
37
+ export function stopTurnTimer() {
43
38
  if (intervalId === null)
44
39
  return 0;
45
40
  clearInterval(intervalId);
package/dist/update.js CHANGED
@@ -1,8 +1,8 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.checkAndUpdate = checkAndUpdate;
4
- const child_process_1 = require("child_process");
5
- const package_json_1 = require("../package.json");
1
+ import { spawn } from 'child_process';
2
+ import { createRequire } from 'module';
3
+ // ESM JSON imports need version-sensitive import assertions; createRequire reads
4
+ // the manifest synchronously on every Node 18+ without that fragility.
5
+ const { version } = createRequire(import.meta.url)('../package.json');
6
6
  function isNewer(a, b) {
7
7
  const pa = a.split('.').map(Number);
8
8
  const pb = b.split('.').map(Number);
@@ -14,7 +14,7 @@ function isNewer(a, b) {
14
14
  }
15
15
  return false;
16
16
  }
17
- async function checkAndUpdate() {
17
+ export async function checkAndUpdate() {
18
18
  try {
19
19
  const res = await fetch('https://registry.npmjs.org/@1presence/bridge/latest', {
20
20
  signal: AbortSignal.timeout(3000),
@@ -23,10 +23,10 @@ async function checkAndUpdate() {
23
23
  return false;
24
24
  const data = await res.json();
25
25
  const latest = data.version;
26
- if (!isNewer(latest, package_json_1.version))
26
+ if (!isNewer(latest, version))
27
27
  return false;
28
28
  console.log(`Updating to v${latest}…\n`);
29
- const child = (0, child_process_1.spawn)('npx', ['--yes', `@1presence/bridge@${latest}`], {
29
+ const child = spawn('npx', ['--yes', `@1presence/bridge@${latest}`], {
30
30
  stdio: 'inherit',
31
31
  env: process.env,
32
32
  });
package/package.json CHANGED
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "name": "@1presence/bridge",
3
- "version": "0.40.0",
3
+ "version": "0.42.0",
4
4
  "description": "Run 1Presence on your Mac and use your Claude.ai Pro subscription from any device",
5
+ "type": "module",
5
6
  "bin": {
6
7
  "1presence-bridge": "dist/index.js"
7
8
  },
@@ -18,6 +19,7 @@
18
19
  "start": "node dist/index.js"
19
20
  },
20
21
  "dependencies": {
22
+ "@anthropic-ai/claude-agent-sdk": "^0.3.153",
21
23
  "ws": "^8.20.0"
22
24
  },
23
25
  "devDependencies": {