@1presence/bridge 0.28.0 → 0.29.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
@@ -15,6 +15,11 @@ npx @1presence/bridge
15
15
 
16
16
  On first run, a browser window will open to sign in to your 1Presence account. Your credentials are cached in `~/.1presence/auth.json` so subsequent runs start immediately.
17
17
 
18
+ ### Flags
19
+
20
+ - `--debug` (`-d`) — print a clean per-turn transcript: the user prompt, the assistant's text, and every tool call's input and output. This mirrors what an admin sees in the chat's debug view. Use this when you want to follow the conversation and inspect tool calls.
21
+ - `--verbose` (`-v`) — log tool inputs/outputs plus the full system prompt and setup paths on every turn. Use this when debugging the prompt itself; for following messages and tool calls without the prompt dump, prefer `--debug`.
22
+
18
23
  Once connected, your 1Presence app on any device automatically routes to your local Claude Code session. When you stop the bridge, 1Presence falls back to platform mode.
19
24
 
20
25
  ## How it works
package/dist/claude.js CHANGED
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.setVerbose = setVerbose;
4
+ exports.setDebug = setDebug;
4
5
  exports.spawnClaude = spawnClaude;
5
6
  exports.killAll = killAll;
6
7
  const child_process_1 = require("child_process");
@@ -36,9 +37,17 @@ const config_1 = require("./config");
36
37
  // Track whether we've already announced the model this process — printing it
37
38
  // per-spawn is noisy; once on startup is what the user actually wants to see.
38
39
  let modelAnnounced = false;
39
- // Verbose flag — when set via --verbose, log full tool inputs and outputs.
40
+ // Verbose flag — when set via --verbose, log full tool inputs and outputs
41
+ // PLUS the entire system prompt. Great for prompt debugging, noisy for
42
+ // message debugging (the prompt dump buries the conversation).
40
43
  let verbose = false;
41
44
  function setVerbose(v) { verbose = v; }
45
+ // Debug flag — when set via --debug, render a clean, sectioned transcript of
46
+ // the live turn: user prompt, assistant text, every tool input, every tool
47
+ // result. This is the bridge equivalent of the chat's admin debug view. It
48
+ // deliberately does NOT print the system prompt — that's what --verbose is for.
49
+ let debug = false;
50
+ function setDebug(v) { debug = v; }
42
51
  function formatPayload(value) {
43
52
  try {
44
53
  return JSON.stringify(value, null, 2);
@@ -47,6 +56,26 @@ function formatPayload(value) {
47
56
  return String(value);
48
57
  }
49
58
  }
59
+ // ─── Debug transcript rendering ─────────────────────────────────────────────
60
+ //
61
+ // A clean, scannable block per event — coloured header rule + body. Matches
62
+ // the shape of the chat's admin debug bubbles (user / assistant / tool input /
63
+ // tool result) so what you see locally mirrors what an admin sees in the app.
64
+ const USE_COLOR = process.stderr.isTTY === true && !process.env['NO_COLOR'];
65
+ function paint(code, s) {
66
+ return USE_COLOR ? `\x1b[${code}m${s}\x1b[0m` : s;
67
+ }
68
+ // ANSI colour codes per section, mirroring the admin debug palette.
69
+ const DEBUG_COLORS = {
70
+ user: '34', // blue
71
+ assistant: '32', // green
72
+ input: '36', // cyan
73
+ result: '33', // yellow
74
+ };
75
+ function debugBlock(label, colorCode, body) {
76
+ const rule = `── ${label} `.padEnd(64, '─');
77
+ process.stderr.write(`\n${paint(colorCode, rule)}\n${body.trimEnd()}\n`);
78
+ }
50
79
  // ─── Active processes ─────────────────────────────────────────────────────────
51
80
  const active = new Map();
52
81
  // ─── Spawn ────────────────────────────────────────────────────────────────────
@@ -61,6 +90,16 @@ function spawnClaude(params) {
61
90
  process.stderr.write(`[bridge:verbose] mcp config: ${mcpConfigPath}\n`);
62
91
  process.stderr.write(`[bridge:verbose] history turns: ${history.length}\n`);
63
92
  }
93
+ // Debug transcript: lead with the user prompt for this turn (the clean
94
+ // message, before the gateway's ephemeral-context prefix), plus a hint at
95
+ // how much prior context is being replayed.
96
+ if (debug) {
97
+ const histNote = history.length ? ` (replaying ${history.length} prior turn${history.length === 1 ? '' : 's'})` : '';
98
+ debugBlock(`user${histNote}`, DEBUG_COLORS.user, text);
99
+ }
100
+ // tool_use_id → tool name, so a tool_result block (which only carries the id)
101
+ // can be labelled with the tool it answers in the debug transcript.
102
+ const toolNames = new Map();
64
103
  // If a prior process is still running for this conversation (user sent a
65
104
  // follow-up before the previous turn finished), supersede it. The latest
66
105
  // user intent wins; the orphan would otherwise keep streaming events.
@@ -230,16 +269,25 @@ function spawnClaude(params) {
230
269
  let wroteText = false;
231
270
  for (const block of content) {
232
271
  if (block['type'] === 'tool_use') {
233
- if (wroteText) {
234
- process.stderr.write('\n');
235
- wroteText = false;
236
- }
237
272
  const toolName = block['name'];
238
- const prefix = toolName.startsWith('mcp__') ? '[mcp]' : '[tool]';
239
- process.stderr.write(`[bridge] ${prefix} ${toolName}\n`);
240
- if (verbose) {
241
- const input = block['input'];
242
- process.stderr.write(`[bridge:verbose] ─── input ${toolName} ───\n${formatPayload(input)}\n[bridge:verbose] ─── end input ───\n`);
273
+ const toolId = block['id'];
274
+ if (toolId)
275
+ toolNames.set(toolId, toolName);
276
+ if (debug) {
277
+ // Clean transcript: a single coloured block with the full input.
278
+ debugBlock(`tool → ${toolName}`, DEBUG_COLORS.input, formatPayload(block['input']));
279
+ }
280
+ else {
281
+ if (wroteText) {
282
+ process.stderr.write('\n');
283
+ wroteText = false;
284
+ }
285
+ const prefix = toolName.startsWith('mcp__') ? '[mcp]' : '[tool]';
286
+ process.stderr.write(`[bridge] ${prefix} ${toolName}\n`);
287
+ if (verbose) {
288
+ const input = block['input'];
289
+ process.stderr.write(`[bridge:verbose] ─── input ${toolName} ───\n${formatPayload(input)}\n[bridge:verbose] ─── end input ───\n`);
290
+ }
243
291
  }
244
292
  // Defense-in-depth: CLI flags (--tools "", --allowedTools, --strict-mcp-config,
245
293
  // --setting-sources "") are supposed to make this unreachable. If we see a
@@ -269,8 +317,14 @@ function spawnClaude(params) {
269
317
  else if (block['type'] === 'text') {
270
318
  const text = block['text'];
271
319
  if (text) {
272
- process.stderr.write(text.replace(/\n+/g, ' '));
273
- wroteText = true;
320
+ if (debug) {
321
+ // Full text, newlines intact — the readable transcript.
322
+ debugBlock('assistant', DEBUG_COLORS.assistant, text);
323
+ }
324
+ else {
325
+ process.stderr.write(text.replace(/\n+/g, ' '));
326
+ wroteText = true;
327
+ }
274
328
  }
275
329
  }
276
330
  }
@@ -279,7 +333,7 @@ function spawnClaude(params) {
279
333
  }
280
334
  }
281
335
  // Tool results stream back as `user` events with tool_result blocks.
282
- if (verbose && type === 'user') {
336
+ if ((verbose || debug) && type === 'user') {
283
337
  const msg = event['message'];
284
338
  const content = msg?.['content'];
285
339
  if (Array.isArray(content)) {
@@ -287,7 +341,14 @@ function spawnClaude(params) {
287
341
  if (block['type'] === 'tool_result') {
288
342
  const id = block['tool_use_id'] ?? '';
289
343
  const out = block['content'];
290
- process.stderr.write(`[bridge:verbose] ─── output ${id} ───\n${formatPayload(out)}\n[bridge:verbose] ─── end output ───\n`);
344
+ if (debug) {
345
+ const name = toolNames.get(id) ?? id ?? 'result';
346
+ const errFlag = block['is_error'] ? ' [error]' : '';
347
+ debugBlock(`result ← ${name}${errFlag}`, DEBUG_COLORS.result, formatPayload(out));
348
+ }
349
+ else {
350
+ process.stderr.write(`[bridge:verbose] ─── output ${id} ───\n${formatPayload(out)}\n[bridge:verbose] ─── end output ───\n`);
351
+ }
291
352
  }
292
353
  }
293
354
  }
package/dist/index.js CHANGED
@@ -31,6 +31,10 @@ if (__dirname.endsWith('dist')) {
31
31
  }
32
32
  // ─── CLI args ─────────────────────────────────────────────────────────────────
33
33
  const VERBOSE = process.argv.includes('--verbose') || process.argv.includes('-v');
34
+ // --debug renders a clean per-turn transcript (user prompt, assistant text,
35
+ // tool inputs, tool outputs) — the bridge equivalent of the chat's admin
36
+ // debug view. Unlike --verbose it does NOT dump the system prompt.
37
+ const DEBUG = process.argv.includes('--debug') || process.argv.includes('-d');
34
38
  // ─── Config ───────────────────────────────────────────────────────────────────
35
39
  const GATEWAY_URL = process.env.BRIDGE_GATEWAY_URL ?? 'https://api.1presence.com';
36
40
  const GATEWAY_WS = GATEWAY_URL.replace(/^https?:/, 'wss:').replace(/\/$/, '') + '/bridge';
@@ -424,6 +428,10 @@ async function main() {
424
428
  (0, claude_1.setVerbose)(true);
425
429
  console.log('[bridge:verbose] verbose logging enabled — system prompts, tool inputs, and tool outputs will be printed.\n');
426
430
  }
431
+ if (DEBUG) {
432
+ (0, claude_1.setDebug)(true);
433
+ 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');
434
+ }
427
435
  if (await (0, update_1.checkAndUpdate)())
428
436
  return;
429
437
  // Auth
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@1presence/bridge",
3
- "version": "0.28.0",
3
+ "version": "0.29.0",
4
4
  "description": "Run 1Presence on your Mac and use your Claude.ai Pro subscription from any device",
5
5
  "bin": {
6
6
  "1presence-bridge": "dist/index.js"