@jaw.id/cli 0.0.7 → 0.1.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.
Files changed (62) hide show
  1. package/README.md +24 -24
  2. package/dist/base-command.js +28 -8
  3. package/dist/base-command.js.map +1 -1
  4. package/dist/commands/config/set.js +28 -38
  5. package/dist/commands/config/set.js.map +1 -1
  6. package/dist/commands/config/show.js +29 -12
  7. package/dist/commands/config/show.js.map +1 -1
  8. package/dist/commands/config/write.js +288 -0
  9. package/dist/commands/config/write.js.map +1 -0
  10. package/dist/commands/disconnect.js +42 -54
  11. package/dist/commands/disconnect.js.map +1 -1
  12. package/dist/commands/mcp/index.js +62 -102
  13. package/dist/commands/mcp/index.js.map +1 -1
  14. package/dist/commands/rpc/call.js +218 -92
  15. package/dist/commands/rpc/call.js.map +1 -1
  16. package/dist/commands/session/revoke.js +727 -0
  17. package/dist/commands/session/revoke.js.map +1 -0
  18. package/dist/commands/session/setup.js +960 -0
  19. package/dist/commands/session/setup.js.map +1 -0
  20. package/dist/commands/session/status.js +206 -0
  21. package/dist/commands/session/status.js.map +1 -0
  22. package/dist/commands/version.js +28 -8
  23. package/dist/commands/version.js.map +1 -1
  24. package/dist/index.js +42 -67
  25. package/dist/index.js.map +1 -1
  26. package/dist/lib/bridge-singleton.js +47 -61
  27. package/dist/lib/bridge-singleton.js.map +1 -1
  28. package/dist/lib/config.js +17 -13
  29. package/dist/lib/config.js.map +1 -1
  30. package/dist/lib/crypto.js +3 -15
  31. package/dist/lib/crypto.js.map +1 -1
  32. package/dist/lib/keystore.js +64 -0
  33. package/dist/lib/keystore.js.map +1 -0
  34. package/dist/lib/output.js +1 -16
  35. package/dist/lib/output.js.map +1 -1
  36. package/dist/lib/paths.js +3 -1
  37. package/dist/lib/paths.js.map +1 -1
  38. package/dist/lib/session-bridge.js +168 -0
  39. package/dist/lib/session-bridge.js.map +1 -0
  40. package/dist/lib/session-config.js +52 -0
  41. package/dist/lib/session-config.js.map +1 -0
  42. package/dist/lib/validation.js +62 -27
  43. package/dist/lib/validation.js.map +1 -1
  44. package/dist/lib/ws-bridge.js +15 -46
  45. package/dist/lib/ws-bridge.js.map +1 -1
  46. package/dist/mcp/handlers/config.js +28 -29
  47. package/dist/mcp/handlers/config.js.map +1 -1
  48. package/dist/mcp/handlers/daemon.js +41 -51
  49. package/dist/mcp/handlers/daemon.js.map +1 -1
  50. package/dist/mcp/handlers/resources.js +1 -3
  51. package/dist/mcp/handlers/resources.js.map +1 -1
  52. package/dist/mcp/handlers/rpc.js +63 -79
  53. package/dist/mcp/handlers/rpc.js.map +1 -1
  54. package/dist/mcp/helpers.js.map +1 -1
  55. package/dist/mcp/server.js +61 -99
  56. package/dist/mcp/server.js.map +1 -1
  57. package/dist/mcp/tools.js +2 -4
  58. package/dist/mcp/tools.js.map +1 -1
  59. package/oclif.manifest.json +304 -4
  60. package/package.json +19 -1
  61. package/dist/lib/session-store.js +0 -80
  62. package/dist/lib/session-store.js.map +0 -1
package/dist/index.js CHANGED
@@ -11,7 +11,9 @@ var PATHS = {
11
11
  root: JAW_DIR,
12
12
  config: path.join(JAW_DIR, "config.json"),
13
13
  session: path.join(JAW_DIR, "session.json"),
14
- relay: path.join(JAW_DIR, "relay.json")
14
+ relay: path.join(JAW_DIR, "relay.json"),
15
+ keystore: path.join(JAW_DIR, "keystore.json"),
16
+ sessionConfig: path.join(JAW_DIR, "session-config.json")
15
17
  };
16
18
 
17
19
  // src/lib/validation.ts
@@ -42,13 +44,23 @@ function ensureDir(dir) {
42
44
  fs2.mkdirSync(dir, { recursive: true, mode: 448 });
43
45
  fs2.chmodSync(dir, 448);
44
46
  }
47
+ function migrateConfig(config) {
48
+ if (config.paymasterUrl && !config.paymasters) {
49
+ const chainId = config.defaultChain ?? 1;
50
+ config.paymasters = { [chainId]: { url: config.paymasterUrl } };
51
+ delete config.paymasterUrl;
52
+ saveConfig(config);
53
+ }
54
+ return config;
55
+ }
45
56
  function loadConfig() {
46
57
  if (!fs2.existsSync(PATHS.config)) {
47
58
  return {};
48
59
  }
49
60
  const raw = fs2.readFileSync(PATHS.config, "utf-8");
50
61
  try {
51
- return JSON.parse(raw);
62
+ const config = JSON.parse(raw);
63
+ return migrateConfig(config);
52
64
  } catch {
53
65
  throw new Error(
54
66
  `Config file at ${PATHS.config} is not valid JSON. Run \`jaw config set apiKey=<key>\` to reset it.`
@@ -134,18 +146,14 @@ var BaseCommand = class extends Command {
134
146
  resolveApiKey(flags) {
135
147
  const apiKey = flags["api-key"] ?? loadConfig().apiKey;
136
148
  if (!apiKey) {
137
- this.error(
138
- "API key required. Set via --api-key, JAW_API_KEY env, or `jaw config set apiKey <key>`"
139
- );
149
+ this.error("API key required. Set via --api-key, JAW_API_KEY env, or `jaw config set apiKey <key>`");
140
150
  }
141
151
  return apiKey;
142
152
  }
143
153
  resolveChainId(flags) {
144
154
  const chainId = flags.chain ?? loadConfig().defaultChain;
145
155
  if (!chainId) {
146
- this.error(
147
- "Chain ID required. Set via --chain, JAW_CHAIN_ID env, or `jaw config set defaultChain <id>`"
148
- );
156
+ this.error("Chain ID required. Set via --chain, JAW_CHAIN_ID env, or `jaw config set defaultChain <id>`");
149
157
  }
150
158
  return chainId;
151
159
  }
@@ -158,11 +166,7 @@ var BaseCommand = class extends Command {
158
166
  // src/lib/crypto.ts
159
167
  var subtle = globalThis.crypto.subtle;
160
168
  async function generateKeyPair() {
161
- return subtle.generateKey(
162
- { name: "ECDH", namedCurve: "P-256" },
163
- true,
164
- ["deriveKey"]
165
- );
169
+ return subtle.generateKey({ name: "ECDH", namedCurve: "P-256" }, true, ["deriveKey"]);
166
170
  }
167
171
  async function deriveSharedSecret(privateKey, peerPublicKey) {
168
172
  return subtle.deriveKey(
@@ -176,11 +180,7 @@ async function deriveSharedSecret(privateKey, peerPublicKey) {
176
180
  async function encryptMessage(sharedSecret, payload) {
177
181
  const iv = globalThis.crypto.getRandomValues(new Uint8Array(12));
178
182
  const plaintext = new TextEncoder().encode(JSON.stringify(payload));
179
- const cipherBuf = await subtle.encrypt(
180
- { name: "AES-GCM", iv },
181
- sharedSecret,
182
- plaintext
183
- );
183
+ const cipherBuf = await subtle.encrypt({ name: "AES-GCM", iv }, sharedSecret, plaintext);
184
184
  return {
185
185
  iv: bufferToBase64(iv),
186
186
  ciphertext: bufferToBase64(new Uint8Array(cipherBuf))
@@ -189,11 +189,7 @@ async function encryptMessage(sharedSecret, payload) {
189
189
  async function decryptMessage(sharedSecret, envelope) {
190
190
  const iv = Buffer.from(envelope.iv, "base64");
191
191
  const ciphertext = Buffer.from(envelope.ciphertext, "base64");
192
- const plainBuf = await subtle.decrypt(
193
- { name: "AES-GCM", iv },
194
- sharedSecret,
195
- ciphertext
196
- );
192
+ const plainBuf = await subtle.decrypt({ name: "AES-GCM", iv }, sharedSecret, ciphertext);
197
193
  return JSON.parse(new TextDecoder().decode(plainBuf));
198
194
  }
199
195
  async function exportKeyToHex(type, key) {
@@ -285,11 +281,7 @@ var WSBridge = class {
285
281
  let expectingKeyExchange = !this.peerPublicKeyHex;
286
282
  const timer = setTimeout(() => {
287
283
  ws.close();
288
- reject(
289
- new Error(
290
- "Browser did not connect in time.\nRun `jaw disconnect` then try again."
291
- )
292
- );
284
+ reject(new Error("Browser did not connect in time.\nRun `jaw disconnect` then try again."));
293
285
  }, 3e4);
294
286
  const sendEncryptedInit = async () => {
295
287
  if (!this.sharedSecret) return;
@@ -312,10 +304,7 @@ var WSBridge = class {
312
304
  if (!msg) return;
313
305
  if (msg.type === "encrypted" && this.sharedSecret) {
314
306
  try {
315
- const inner = await decryptMessage(
316
- this.sharedSecret,
317
- msg
318
- );
307
+ const inner = await decryptMessage(this.sharedSecret, msg);
319
308
  if (inner.type === "ready") {
320
309
  clearTimeout(readyTimer);
321
310
  ws.off("message", onMsg);
@@ -353,6 +342,10 @@ var WSBridge = class {
353
342
  expectingKeyExchange = true;
354
343
  onBrowserNeeded().catch(() => {
355
344
  });
345
+ } else if (!onBrowserNeeded) {
346
+ clearTimeout(timer);
347
+ ws.close();
348
+ reject(new Error("Browser not connected \u2014 relay session is stale."));
356
349
  }
357
350
  } else if (msg.type === "browser_connected") {
358
351
  expectingKeyExchange = true;
@@ -402,9 +395,7 @@ var WSBridge = class {
402
395
  return new Promise((resolve, reject) => {
403
396
  const timer = setTimeout(() => {
404
397
  reject(
405
- new Error(
406
- `Request timed out after ${this.timeout / 1e3}s. Did you complete the action in the browser?`
407
- )
398
+ new Error(`Request timed out after ${this.timeout / 1e3}s. Did you complete the action in the browser?`)
408
399
  );
409
400
  this.close();
410
401
  }, this.timeout);
@@ -412,10 +403,7 @@ var WSBridge = class {
412
403
  const msg = safeParse(data);
413
404
  if (!msg || msg.type !== "encrypted" || !this.sharedSecret) return;
414
405
  try {
415
- const inner = await decryptMessage(
416
- this.sharedSecret,
417
- msg
418
- );
406
+ const inner = await decryptMessage(this.sharedSecret, msg);
419
407
  if (inner.type === "rpc_response" && inner.id === id) {
420
408
  clearTimeout(timer);
421
409
  ws.off("message", onMessage);
@@ -423,11 +411,7 @@ var WSBridge = class {
423
411
  resolve(inner.data);
424
412
  } else {
425
413
  const err = inner.error;
426
- reject(
427
- new Error(
428
- err ? `[${err.code}] ${err.message}` : "Request failed"
429
- )
430
- );
414
+ reject(new Error(err ? `[${err.code}] ${err.message}` : "Request failed"));
431
415
  }
432
416
  }
433
417
  } catch {
@@ -437,9 +421,6 @@ var WSBridge = class {
437
421
  this.sendRaw(ws, serialized);
438
422
  });
439
423
  }
440
- isOpen() {
441
- return this.ws?.readyState === WebSocket.OPEN;
442
- }
443
424
  async shutdown() {
444
425
  this.disposed = true;
445
426
  if (this.ws?.readyState === WebSocket.OPEN && this.sharedSecret) {
@@ -447,10 +428,7 @@ var WSBridge = class {
447
428
  const envelope = await encryptMessage(this.sharedSecret, {
448
429
  type: "shutdown"
449
430
  });
450
- this.sendRaw(
451
- this.ws,
452
- JSON.stringify({ type: "encrypted", ...envelope })
453
- );
431
+ this.sendRaw(this.ws, JSON.stringify({ type: "encrypted", ...envelope }));
454
432
  } catch {
455
433
  }
456
434
  }
@@ -528,10 +506,8 @@ var WSBridge = class {
528
506
  this.reconnectAttempts++;
529
507
  setTimeout(() => {
530
508
  if (this.disposed) return;
531
- this.connectInternal(this.onBrowserNeeded, this.onPeerKeyChanged).catch(
532
- () => {
533
- }
534
- );
509
+ this.connectInternal(this.onBrowserNeeded, this.onPeerKeyChanged).catch(() => {
510
+ });
535
511
  }, delay);
536
512
  }
537
513
  /** Send a raw string over the WebSocket, enforcing message size limits. */
@@ -541,10 +517,7 @@ var WSBridge = class {
541
517
  async deriveSecret() {
542
518
  if (!this.peerPublicKeyHex) return;
543
519
  const privateKey = await importKeyFromHex("private", this.privateKeyHex);
544
- const peerPublicKey = await importKeyFromHex(
545
- "public",
546
- this.peerPublicKeyHex
547
- );
520
+ const peerPublicKey = await importKeyFromHex("public", this.peerPublicKeyHex);
548
521
  this.sharedSecret = await deriveSharedSecret(privateKey, peerPublicKey);
549
522
  }
550
523
  };
@@ -604,17 +577,19 @@ async function getBridge(options) {
604
577
  throw new Error(`Untrusted relayUrl: ${relayUrl}. Must be wss://*.jaw.id or ws://localhost.`);
605
578
  }
606
579
  let relaySession = loadRelaySession();
607
- if (relaySession && relaySession.relayUrl === relayUrl) {
580
+ if (relaySession && relaySession.relayUrl === relayUrl && relaySession.peerPublicKey) {
608
581
  try {
609
- return await connectBridge(relaySession, options, chainId, keysUrl, relayUrl);
582
+ return await connectBridge(relaySession, options, chainId, keysUrl, relayUrl, false);
610
583
  } catch {
611
584
  deleteRelaySession();
612
585
  relaySession = null;
613
586
  }
587
+ } else if (relaySession) {
588
+ deleteRelaySession();
614
589
  }
615
590
  const session = await createNewSession(relayUrl);
616
591
  saveRelaySession(session);
617
- return await connectBridge(session, options, chainId, keysUrl, relayUrl);
592
+ return await connectBridge(session, options, chainId, keysUrl, relayUrl, true);
618
593
  }
619
594
  async function createNewSession(relayUrl) {
620
595
  const kp = await generateKeyPair();
@@ -629,7 +604,7 @@ async function createNewSession(relayUrl) {
629
604
  startedAt: (/* @__PURE__ */ new Date()).toISOString()
630
605
  };
631
606
  }
632
- async function connectBridge(relaySession, options, chainId, keysUrl, relayUrl) {
607
+ async function connectBridge(relaySession, options, chainId, keysUrl, relayUrl, openBrowser) {
633
608
  const config = loadConfig();
634
609
  const bridge = new WSBridge({
635
610
  relayUrl,
@@ -639,19 +614,19 @@ async function connectBridge(relaySession, options, chainId, keysUrl, relayUrl)
639
614
  apiKey: options.apiKey,
640
615
  chainId,
641
616
  ens: options.ens ?? config.ens,
642
- paymasterUrl: options.paymasterUrl ?? config.paymasterUrl
617
+ paymasterUrl: options.paymasterUrl ?? config.paymasters?.[chainId]?.url
643
618
  },
644
619
  privateKeyHex: relaySession.privateKey,
645
620
  publicKeyHex: relaySession.publicKey,
646
621
  peerPublicKeyHex: relaySession.peerPublicKey
647
622
  });
648
623
  await bridge.connect(
649
- // onBrowserNeeded
650
- async () => {
624
+ // onBrowserNeeded — only open a browser for new sessions
625
+ openBrowser ? async () => {
651
626
  const bridgeUrl = buildBridgeUrl(keysUrl, relaySession.session, relayUrl, relaySession.publicKey);
652
627
  const { default: open } = await import('open');
653
628
  await open(bridgeUrl);
654
- },
629
+ } : void 0,
655
630
  // onPeerKeyChanged
656
631
  (newPeerKey) => {
657
632
  relaySession.peerPublicKey = newPeerKey;
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/lib/paths.ts","../src/lib/validation.ts","../src/lib/config.ts","../src/lib/output.ts","../src/base-command.ts","../src/lib/crypto.ts","../src/lib/ws-bridge.ts","../src/lib/bridge-singleton.ts"],"names":["fs","crypto2"],"mappings":"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","file":"index.js","sourcesContent":["import * as path from \"node:path\";\nimport * as os from \"node:os\";\n\nconst JAW_DIR = path.join(os.homedir(), \".jaw\");\n\nexport const PATHS = {\n root: JAW_DIR,\n config: path.join(JAW_DIR, \"config.json\"),\n session: path.join(JAW_DIR, \"session.json\"),\n relay: path.join(JAW_DIR, \"relay.json\"),\n} as const;\n","export function isValidAddress(value: string): boolean {\n return /^0x[0-9a-fA-F]{40}$/.test(value);\n}\n\nexport function isValidHex(value: string): boolean {\n return /^0x[0-9a-fA-F]*$/.test(value);\n}\n\nexport function isValidChainId(value: string | number): boolean {\n const num = typeof value === \"string\" ? parseInt(value, 10) : value;\n return Number.isInteger(num) && num > 0;\n}\n\nexport function parseChainId(value: string): number {\n const num = parseInt(value, 10);\n if (!isValidChainId(num)) {\n throw new Error(`Invalid chain ID: ${value}`);\n }\n return num;\n}\n\nexport function assertAddress(value: string, label = \"address\"): `0x${string}` {\n if (!isValidAddress(value)) {\n throw new Error(`Invalid ${label}: ${value}`);\n }\n return value as `0x${string}`;\n}\n\nexport function parseWei(raw: string, label = \"value\"): bigint {\n try {\n return BigInt(raw);\n } catch {\n throw new Error(`Invalid ${label}: \"${raw}\" is not a valid wei amount`);\n }\n}\n\nexport function isValidKeysUrl(url: string): boolean {\n try {\n const parsed = new URL(url);\n const isTrustedHost =\n parsed.hostname.endsWith(\".jaw.id\") ||\n parsed.hostname === \"jaw.id\" ||\n parsed.hostname === \"localhost\" ||\n parsed.hostname === \"127.0.0.1\";\n const isSecure =\n parsed.protocol === \"https:\" ||\n parsed.hostname === \"localhost\" ||\n parsed.hostname === \"127.0.0.1\";\n return isTrustedHost && isSecure;\n } catch {\n return false;\n }\n}\n\nexport function isValidRelayUrl(url: string): boolean {\n try {\n const parsed = new URL(url);\n const isTrustedHost =\n parsed.hostname.endsWith(\".jaw.id\") ||\n parsed.hostname === \"jaw.id\" ||\n parsed.hostname === \"localhost\" ||\n parsed.hostname === \"127.0.0.1\";\n const isSecure =\n parsed.protocol === \"wss:\" ||\n parsed.hostname === \"localhost\" ||\n parsed.hostname === \"127.0.0.1\";\n const isWebSocket =\n parsed.protocol === \"wss:\" || parsed.protocol === \"ws:\";\n return isTrustedHost && isSecure && isWebSocket;\n } catch {\n return false;\n }\n}\n","import * as fs from \"node:fs\";\nimport { PATHS } from \"./paths.js\";\nimport type { JawConfig } from \"./types.js\";\nimport { isValidKeysUrl, isValidRelayUrl } from \"./validation.js\";\n\nexport function ensureDir(dir: string): void {\n fs.mkdirSync(dir, { recursive: true, mode: 0o700 });\n fs.chmodSync(dir, 0o700);\n}\n\nexport function loadConfig(): JawConfig {\n if (!fs.existsSync(PATHS.config)) {\n return {};\n }\n const raw = fs.readFileSync(PATHS.config, \"utf-8\");\n try {\n return JSON.parse(raw) as JawConfig;\n } catch {\n throw new Error(\n `Config file at ${PATHS.config} is not valid JSON. Run \\`jaw config set apiKey=<key>\\` to reset it.`,\n );\n }\n}\n\nexport function saveConfig(config: JawConfig): void {\n ensureDir(PATHS.root);\n fs.writeFileSync(PATHS.config, JSON.stringify(config, null, 2) + \"\\n\", {\n encoding: \"utf-8\",\n mode: 0o600,\n });\n}\n\nexport function redactConfig(config: JawConfig): Record<string, unknown> {\n return {\n ...config,\n apiKey: config.apiKey ? `${config.apiKey.slice(0, 8)}...` : undefined,\n };\n}\n\nexport function getConfigValue(\n key: keyof JawConfig,\n): string | number | undefined {\n const config = loadConfig();\n return config[key];\n}\n\nexport function setConfigValue(\n key: keyof JawConfig,\n value: string | number,\n): void {\n if (\n key === \"keysUrl\" &&\n typeof value === \"string\" &&\n !isValidKeysUrl(value)\n ) {\n throw new Error(\n `Untrusted keysUrl: ${value}. Must be a *.jaw.id domain (HTTPS) or localhost.`,\n );\n }\n if (\n key === \"relayUrl\" &&\n typeof value === \"string\" &&\n !isValidRelayUrl(value)\n ) {\n throw new Error(\n `Untrusted relayUrl: ${value}. Must be wss://*.jaw.id or ws://localhost.`,\n );\n }\n const config = loadConfig();\n const updated = { ...config, [key]: value };\n saveConfig(updated);\n}\n","import type { OutputFormat } from './types.js';\n\nexport function formatOutput(data: unknown, format: OutputFormat): string {\n if (format === 'json') {\n return JSON.stringify(data, replaceBigInt, 2);\n }\n return formatHuman(data);\n}\n\nfunction replaceBigInt(_key: string, value: unknown): unknown {\n if (typeof value === 'bigint') {\n return value.toString();\n }\n return value;\n}\n\nfunction formatHuman(data: unknown, indent = 0): string {\n if (data === null || data === undefined) {\n return 'null';\n }\n\n if (typeof data === 'string' || typeof data === 'number' || typeof data === 'boolean' || typeof data === 'bigint') {\n return String(data);\n }\n\n if (Array.isArray(data)) {\n if (data.length === 0) return '(empty)';\n return data.map((item, i) => `${i + 1}. ${formatHuman(item, indent + 2)}`).join('\\n');\n }\n\n if (typeof data === 'object') {\n const entries = Object.entries(data as Record<string, unknown>);\n if (entries.length === 0) return '(empty)';\n const pad = ' '.repeat(indent);\n const maxKeyLen = Math.max(...entries.map(([k]) => k.length));\n return entries\n .map(([key, val]) => {\n const paddedKey = key.padEnd(maxKeyLen);\n const valStr = typeof val === 'object' && val !== null\n ? '\\n' + formatHuman(val, indent + 2)\n : String(val);\n return `${pad}${paddedKey} ${valStr}`;\n })\n .join('\\n');\n }\n\n return String(data);\n}\n\nexport function printTable(rows: Record<string, unknown>[]): string {\n if (rows.length === 0) return '(no results)';\n\n const keys = Object.keys(rows[0]);\n const widths = keys.map(k =>\n Math.max(k.length, ...rows.map(r => String(r[k] ?? '').length))\n );\n\n const header = keys.map((k, i) => k.padEnd(widths[i])).join(' ');\n const separator = widths.map(w => '-'.repeat(w)).join(' ');\n const body = rows.map(r =>\n keys.map((k, i) => String(r[k] ?? '').padEnd(widths[i])).join(' ')\n ).join('\\n');\n\n return `${header}\\n${separator}\\n${body}`;\n}\n","import { Command, Flags } from \"@oclif/core\";\nimport type { OutputFormat } from \"./lib/types.js\";\nimport { loadConfig } from \"./lib/config.js\";\nimport { formatOutput } from \"./lib/output.js\";\n\nexport abstract class BaseCommand extends Command {\n static override baseFlags = {\n output: Flags.string({\n char: \"o\",\n description: \"Output format\",\n options: [\"json\", \"human\"],\n default: \"human\",\n env: \"JAW_OUTPUT\",\n }),\n chain: Flags.integer({\n char: \"c\",\n description: \"Chain ID\",\n env: \"JAW_CHAIN_ID\",\n }),\n \"api-key\": Flags.string({\n description: \"JAW API key\",\n env: \"JAW_API_KEY\",\n }),\n yes: Flags.boolean({\n char: \"y\",\n description: \"Skip confirmations (for AI agents)\",\n default: false,\n }),\n quiet: Flags.boolean({\n char: \"q\",\n description: \"Suppress non-essential output\",\n default: false,\n }),\n };\n\n protected resolveApiKey(flags: { \"api-key\"?: string }): string {\n const apiKey = flags[\"api-key\"] ?? loadConfig().apiKey;\n if (!apiKey) {\n this.error(\n \"API key required. Set via --api-key, JAW_API_KEY env, or `jaw config set apiKey <key>`\",\n );\n }\n return apiKey;\n }\n\n protected resolveChainId(flags: { chain?: number }): number {\n const chainId = flags.chain ?? loadConfig().defaultChain;\n if (!chainId) {\n this.error(\n \"Chain ID required. Set via --chain, JAW_CHAIN_ID env, or `jaw config set defaultChain <id>`\",\n );\n }\n return chainId;\n }\n\n protected outputResult(data: unknown, format: OutputFormat): void {\n const output = formatOutput(data, format);\n this.log(output);\n }\n}\n","/**\n * E2E encryption primitives for CLI ↔ browser communication.\n *\n * Uses ECDH P-256 for key exchange and AES-256-GCM for message encryption.\n * Mirrors the same crypto operations in @jaw.id/core but uses only\n * Node.js built-in crypto.subtle — no external dependencies.\n */\n\nimport type { webcrypto } from \"node:crypto\";\n\ntype CKey = webcrypto.CryptoKey;\ntype CKeyPair = webcrypto.CryptoKeyPair;\n\nconst subtle = globalThis.crypto.subtle;\n\n// ── Key generation ───────────────────────────────────────────────\n\nexport async function generateKeyPair(): Promise<CKeyPair> {\n return subtle.generateKey(\n { name: \"ECDH\", namedCurve: \"P-256\" },\n true,\n [\"deriveKey\"],\n ) as Promise<CKeyPair>;\n}\n\nexport async function deriveSharedSecret(\n privateKey: CKey,\n peerPublicKey: CKey,\n): Promise<CKey> {\n return subtle.deriveKey(\n { name: \"ECDH\", public: peerPublicKey },\n privateKey,\n { name: \"AES-GCM\", length: 256 },\n false,\n [\"encrypt\", \"decrypt\"],\n );\n}\n\n// ── Encrypt / Decrypt ────────────────────────────────────────────\n\nexport interface EncryptedEnvelope {\n iv: string; // base64\n ciphertext: string; // base64\n}\n\nexport async function encryptMessage(\n sharedSecret: CKey,\n payload: Record<string, unknown>,\n): Promise<EncryptedEnvelope> {\n const iv = globalThis.crypto.getRandomValues(new Uint8Array(12));\n const plaintext = new TextEncoder().encode(JSON.stringify(payload));\n const cipherBuf = await subtle.encrypt(\n { name: \"AES-GCM\", iv },\n sharedSecret,\n plaintext,\n );\n return {\n iv: bufferToBase64(iv),\n ciphertext: bufferToBase64(new Uint8Array(cipherBuf)),\n };\n}\n\nexport async function decryptMessage(\n sharedSecret: CKey,\n envelope: EncryptedEnvelope,\n): Promise<Record<string, unknown>> {\n const iv = Buffer.from(envelope.iv, \"base64\");\n const ciphertext = Buffer.from(envelope.ciphertext, \"base64\");\n const plainBuf = await subtle.decrypt(\n { name: \"AES-GCM\", iv },\n sharedSecret,\n ciphertext,\n );\n return JSON.parse(new TextDecoder().decode(plainBuf));\n}\n\n// ── Key import / export (hex) ────────────────────────────────────\n\nexport async function exportKeyToHex(\n type: \"private\" | \"public\",\n key: CKey,\n): Promise<string> {\n const format = type === \"private\" ? \"pkcs8\" : \"spki\";\n const buf = await subtle.exportKey(format, key);\n return bytesToHex(new Uint8Array(buf));\n}\n\nexport async function importKeyFromHex(\n type: \"private\" | \"public\",\n hex: string,\n): Promise<CKey> {\n const format = type === \"private\" ? \"pkcs8\" : \"spki\";\n return subtle.importKey(\n format,\n Buffer.from(hexToBytes(hex)),\n { name: \"ECDH\", namedCurve: \"P-256\" },\n true,\n type === \"private\" ? [\"deriveKey\"] : [],\n );\n}\n\n// ── Encoding helpers ─────────────────────────────────────────────\n\nfunction bytesToHex(bytes: Uint8Array): string {\n return Array.from(bytes)\n .map((b) => b.toString(16).padStart(2, \"0\"))\n .join(\"\");\n}\n\nfunction hexToBytes(hex: string): Uint8Array {\n if (hex.length % 2 !== 0) throw new Error(\"Invalid hex: odd length\");\n const bytes = new Uint8Array(hex.length / 2);\n for (let i = 0; i < hex.length; i += 2) {\n bytes[i / 2] = parseInt(hex.substring(i, i + 2), 16);\n }\n return bytes;\n}\n\nfunction bufferToBase64(buf: Uint8Array): string {\n return Buffer.from(buf).toString(\"base64\");\n}\n","/**\n * WSBridge — CLI-side relay client\n *\n * Connects to the cloud relay (wss://relay.jaw.id) instead of a local daemon.\n * All messages (except key_exchange) are E2E encrypted via ECDH + AES-256-GCM.\n */\n\nimport * as crypto from \"node:crypto\";\nimport type { webcrypto } from \"node:crypto\";\nimport WebSocket from \"ws\";\nimport {\n deriveSharedSecret,\n encryptMessage,\n decryptMessage,\n importKeyFromHex,\n type EncryptedEnvelope,\n} from \"./crypto.js\";\n\ntype CKey = webcrypto.CryptoKey;\n\nexport interface WSBridgeConfig {\n apiKey: string;\n chainId: number;\n ens?: string;\n paymasterUrl?: string;\n}\n\nexport interface WSBridgeOptions {\n relayUrl: string;\n session: string;\n timeout?: number;\n config: WSBridgeConfig;\n /** CLI's ECDH private key (hex). Loaded from relay.json for existing sessions. */\n privateKeyHex: string;\n /** CLI's ECDH public key (hex). Sent to browser for key derivation. */\n publicKeyHex: string;\n /** Browser's ECDH public key (hex). Null if new session (key exchange needed). */\n peerPublicKeyHex: string | null;\n}\n\nconst DEFAULT_TIMEOUT_MS = 120_000;\n\n/**\n * Maximum outbound message size (5 MB).\n *\n * wallet_sendCalls with large batches (50+ calls, complex calldata) can reach\n * hundreds of KB. Base64 encoding of the AES-GCM ciphertext adds ~33% overhead.\n * 5 MB is generous enough for any realistic batch while still preventing\n * accidental memory issues.\n */\nconst MAX_MESSAGE_BYTES = 5 * 1024 * 1024;\n\n/** Minimum time between browser reopen attempts (ms). */\nconst BROWSER_REOPEN_COOLDOWN_MS = 5_000;\n\n/** Maximum reconnection attempts before giving up. */\nconst MAX_RECONNECT_ATTEMPTS = 3;\n\n/** Base delay for exponential backoff (ms). */\nconst RECONNECT_BASE_DELAY_MS = 1_000;\n\nexport class WSBridge {\n private readonly relayUrl: string;\n private readonly session: string;\n private readonly timeout: number;\n private readonly config: WSBridgeConfig;\n private readonly privateKeyHex: string;\n readonly publicKeyHex: string;\n private peerPublicKeyHex: string | null;\n private sharedSecret: CKey | null = null;\n private ws: WebSocket | null = null;\n private disposed = false;\n\n // Auto-reopen browser state\n private onBrowserNeeded: (() => Promise<void>) | undefined;\n private onPeerKeyChanged: ((key: string) => void) | undefined;\n private lastBrowserOpenTime = 0;\n\n // Reconnection state\n private reconnectAttempts = 0;\n\n /** Updated after key exchange — caller should persist this. */\n get peerPublicKey(): string | null {\n return this.peerPublicKeyHex;\n }\n\n constructor(options: WSBridgeOptions) {\n this.relayUrl = options.relayUrl;\n this.session = options.session;\n this.timeout = options.timeout ?? DEFAULT_TIMEOUT_MS;\n this.config = options.config;\n this.privateKeyHex = options.privateKeyHex;\n this.publicKeyHex = options.publicKeyHex;\n this.peerPublicKeyHex = options.peerPublicKeyHex;\n }\n\n /**\n * Connect to the relay and wait for the browser to be ready.\n *\n * @param onBrowserNeeded — called when the relay reports no browser connected.\n * @param onPeerKeyChanged — called when a key_exchange updates the peer key.\n */\n async connect(\n onBrowserNeeded?: () => Promise<void>,\n onPeerKeyChanged?: (newPeerPublicKeyHex: string) => void,\n ): Promise<void> {\n // Store callbacks for auto-reopen on browser disconnect\n this.onBrowserNeeded = onBrowserNeeded;\n this.onPeerKeyChanged = onPeerKeyChanged;\n\n // Pre-derive shared secret if we already have the peer key\n if (this.peerPublicKeyHex) {\n await this.deriveSecret();\n }\n\n return this.connectInternal(onBrowserNeeded, onPeerKeyChanged);\n }\n\n private async connectInternal(\n onBrowserNeeded?: () => Promise<void>,\n onPeerKeyChanged?: (newPeerPublicKeyHex: string) => void,\n ): Promise<void> {\n return new Promise((resolve, reject) => {\n const url = `${this.relayUrl}?session=${encodeURIComponent(this.session)}&role=cli`;\n const ws = new WebSocket(url);\n\n let browserOpened = false;\n let resolved = false;\n let expectingKeyExchange = !this.peerPublicKeyHex;\n\n const timer = setTimeout(() => {\n ws.close();\n reject(\n new Error(\n \"Browser did not connect in time.\\n\" +\n \"Run `jaw disconnect` then try again.\",\n ),\n );\n }, 30_000);\n\n const sendEncryptedInit = async () => {\n if (!this.sharedSecret) return;\n const envelope = await encryptMessage(this.sharedSecret, {\n type: \"init\",\n apiKey: this.config.apiKey,\n chainId: this.config.chainId,\n ens: this.config.ens,\n paymasterUrl: this.config.paymasterUrl,\n });\n this.sendRaw(ws, JSON.stringify({ type: \"encrypted\", ...envelope }));\n };\n\n const waitForReady = () => {\n const readyTimer = setTimeout(() => {\n ws.close();\n reject(new Error(\"Browser SDK did not become ready in time.\"));\n }, 15_000);\n\n const onMsg = async (data: WebSocket.Data) => {\n const msg = safeParse(data);\n if (!msg) return;\n\n if (msg.type === \"encrypted\" && this.sharedSecret) {\n try {\n const inner = await decryptMessage(\n this.sharedSecret,\n msg as unknown as EncryptedEnvelope,\n );\n if (inner.type === \"ready\") {\n clearTimeout(readyTimer);\n ws.off(\"message\", onMsg);\n this.reconnectAttempts = 0; // Reset on successful connect\n resolve();\n }\n } catch {\n // Not a valid encrypted message for us, ignore\n }\n }\n };\n ws.on(\"message\", onMsg);\n };\n\n const onBrowserReady = async () => {\n if (resolved) return;\n resolved = true;\n clearTimeout(timer);\n // Set up listener BEFORE sending init to avoid missing the ready response\n waitForReady();\n await sendEncryptedInit();\n };\n\n ws.on(\"open\", () => {\n this.ws = ws;\n });\n\n ws.on(\"message\", async (data) => {\n const msg = safeParse(data);\n if (!msg) return;\n\n if (msg.type === \"status\") {\n if (msg.browserConnected) {\n if (this.sharedSecret) {\n // Already have shared secret — skip key exchange\n await onBrowserReady();\n } else {\n // Browser is connected but we don't have its key yet.\n expectingKeyExchange = true;\n }\n } else if (!browserOpened && onBrowserNeeded) {\n browserOpened = true;\n expectingKeyExchange = true;\n onBrowserNeeded().catch(() => {\n /* best effort */\n });\n }\n } else if (msg.type === \"browser_connected\") {\n expectingKeyExchange = true;\n // Wait for key_exchange from browser\n } else if (msg.type === \"browser_disconnected\") {\n // Browser tab closed — attempt to reopen after cooldown\n this.handleBrowserDisconnect();\n } else if (msg.type === \"key_exchange\" && expectingKeyExchange) {\n expectingKeyExchange = false;\n const peerKey = msg.publicKey as string;\n this.peerPublicKeyHex = peerKey;\n await this.deriveSecret();\n onPeerKeyChanged?.(peerKey);\n await onBrowserReady();\n }\n });\n\n ws.on(\"error\", (err) => {\n clearTimeout(timer);\n reject(err);\n });\n\n ws.on(\"close\", () => {\n clearTimeout(timer);\n if (!this.disposed) {\n this.handleRelayDisconnect();\n }\n });\n });\n }\n\n /**\n * Send an encrypted RPC request through the relay to the browser SDK.\n */\n async request(method: string, params?: unknown): Promise<unknown> {\n const ws = this.ws;\n if (!ws || ws.readyState !== WebSocket.OPEN) {\n throw new Error(\"Not connected to relay\");\n }\n if (!this.sharedSecret) {\n throw new Error(\"No shared secret — key exchange not completed\");\n }\n\n const id = crypto.randomUUID();\n\n const envelope = await encryptMessage(this.sharedSecret, {\n type: \"rpc_request\",\n id,\n method,\n params,\n });\n\n const serialized = JSON.stringify({ type: \"encrypted\", ...envelope });\n assertMessageSize(serialized, method);\n\n return new Promise<unknown>((resolve, reject) => {\n const timer = setTimeout(() => {\n reject(\n new Error(\n `Request timed out after ${this.timeout / 1000}s. ` +\n \"Did you complete the action in the browser?\",\n ),\n );\n this.close();\n }, this.timeout);\n\n const onMessage = async (data: WebSocket.Data) => {\n const msg = safeParse(data);\n if (!msg || msg.type !== \"encrypted\" || !this.sharedSecret) return;\n\n try {\n const inner = await decryptMessage(\n this.sharedSecret,\n msg as unknown as EncryptedEnvelope,\n );\n if (inner.type === \"rpc_response\" && inner.id === id) {\n clearTimeout(timer);\n ws.off(\"message\", onMessage);\n\n if (inner.success) {\n resolve(inner.data);\n } else {\n const err = inner.error as\n | { code: number; message: string }\n | undefined;\n reject(\n new Error(\n err ? `[${err.code}] ${err.message}` : \"Request failed\",\n ),\n );\n }\n }\n } catch {\n // Decryption failed — not our message or tampered, ignore\n }\n };\n\n ws.on(\"message\", onMessage);\n this.sendRaw(ws, serialized);\n });\n }\n\n isOpen(): boolean {\n return this.ws?.readyState === WebSocket.OPEN;\n }\n\n async shutdown(): Promise<void> {\n this.disposed = true;\n if (this.ws?.readyState === WebSocket.OPEN && this.sharedSecret) {\n try {\n const envelope = await encryptMessage(this.sharedSecret, {\n type: \"shutdown\",\n });\n this.sendRaw(\n this.ws,\n JSON.stringify({ type: \"encrypted\", ...envelope }),\n );\n } catch {\n // Best effort\n }\n }\n this.close();\n }\n\n /**\n * Connect to relay and send shutdown directly — no init/ready handshake.\n * Used by `jaw disconnect` when we just need to tell the browser to close.\n */\n async connectAndShutdown(): Promise<void> {\n if (!this.peerPublicKeyHex) {\n // No peer key means browser never connected — nothing to shut down\n return;\n }\n\n this.disposed = true;\n await this.deriveSecret();\n\n return new Promise<void>((resolve) => {\n const url = `${this.relayUrl}?session=${encodeURIComponent(this.session)}&role=cli`;\n const ws = new WebSocket(url);\n\n const timer = setTimeout(() => {\n try {\n ws.close();\n } catch {\n /* ignore */\n }\n resolve();\n }, 3000);\n\n ws.on(\"open\", async () => {\n this.ws = ws;\n try {\n await this.shutdown();\n } catch {\n // Best effort\n }\n clearTimeout(timer);\n resolve();\n });\n\n ws.on(\"error\", () => {\n clearTimeout(timer);\n resolve();\n });\n });\n }\n\n close(): void {\n this.disposed = true;\n if (this.ws) {\n try {\n this.ws.close();\n } catch {\n // ignore\n }\n this.ws = null;\n }\n }\n\n /**\n * Auto-reopen browser when browser_disconnected is received from relay.\n * Respects a cooldown to prevent rapid re-opening.\n */\n private handleBrowserDisconnect(): void {\n if (this.disposed || !this.onBrowserNeeded) return;\n\n const now = Date.now();\n if (now - this.lastBrowserOpenTime < BROWSER_REOPEN_COOLDOWN_MS) {\n return; // Too soon — skip this reopen\n }\n\n this.lastBrowserOpenTime = now;\n\n // Reset shared secret — the new browser tab will do a fresh key exchange\n this.sharedSecret = null;\n this.peerPublicKeyHex = null;\n\n this.onBrowserNeeded().catch(() => {\n // Best effort — if browser open fails, the next request will error\n });\n }\n\n /**\n * Attempt to reconnect to the relay with exponential backoff\n * when the WebSocket connection drops unexpectedly.\n */\n private handleRelayDisconnect(): void {\n if (this.disposed) return;\n if (this.reconnectAttempts >= MAX_RECONNECT_ATTEMPTS) return;\n\n const delay = RECONNECT_BASE_DELAY_MS * Math.pow(2, this.reconnectAttempts);\n this.reconnectAttempts++;\n\n setTimeout(() => {\n if (this.disposed) return;\n this.connectInternal(this.onBrowserNeeded, this.onPeerKeyChanged).catch(\n () => {\n // Reconnection failed — will retry if attempts remain\n },\n );\n }, delay);\n }\n\n /** Send a raw string over the WebSocket, enforcing message size limits. */\n private sendRaw(ws: WebSocket, data: string): void {\n ws.send(data);\n }\n\n private async deriveSecret(): Promise<void> {\n if (!this.peerPublicKeyHex) return;\n const privateKey = await importKeyFromHex(\"private\", this.privateKeyHex);\n const peerPublicKey = await importKeyFromHex(\n \"public\",\n this.peerPublicKeyHex,\n );\n this.sharedSecret = await deriveSharedSecret(privateKey, peerPublicKey);\n }\n}\n\n/**\n * Validate message size before sending to the relay.\n * Throws with a descriptive error if the message is too large.\n */\nfunction assertMessageSize(serialized: string, method: string): void {\n const byteLength = Buffer.byteLength(serialized, \"utf-8\");\n if (byteLength > MAX_MESSAGE_BYTES) {\n const sizeMB = (byteLength / (1024 * 1024)).toFixed(2);\n throw new Error(\n `Message for ${method} is too large (${sizeMB} MB, limit ${MAX_MESSAGE_BYTES / (1024 * 1024)} MB). ` +\n \"Try reducing the number of calls in your batch.\",\n );\n }\n}\n\nfunction safeParse(data: WebSocket.Data): Record<string, unknown> | null {\n try {\n return JSON.parse(data.toString()) as Record<string, unknown>;\n } catch {\n return null;\n }\n}\n","/**\n * Bridge lifecycle management — relay edition.\n *\n * - Generates a session token + ECDH keypair for new sessions\n * - Opens the browser to keys.jaw.id/cli-bridge\n * - Saves relay state to ~/.jaw/relay.json\n * - Creates a WSBridge client connected to the relay\n */\n\nimport * as fs from \"node:fs\";\nimport * as crypto from \"node:crypto\";\nimport { PATHS } from \"./paths.js\";\nimport { ensureDir, loadConfig } from \"./config.js\";\nimport { WSBridge } from \"./ws-bridge.js\";\nimport { isValidKeysUrl, isValidRelayUrl } from \"./validation.js\";\nimport { generateKeyPair, exportKeyToHex } from \"./crypto.js\";\n\nconst DEFAULT_KEYS_URL = \"https://keys.jaw.id\";\nconst DEFAULT_RELAY_URL = \"wss://relay.jaw.id\";\n\ninterface RelaySession {\n session: string;\n relayUrl: string;\n privateKey: string;\n publicKey: string;\n peerPublicKey: string | null;\n startedAt: string;\n}\n\nexport interface BridgeOptions {\n keysUrl?: string;\n relayUrl?: string;\n apiKey: string;\n chainId?: number;\n ens?: string;\n paymasterUrl?: string;\n timeout?: number;\n}\n\nfunction loadRelaySession(): RelaySession | null {\n try {\n if (!fs.existsSync(PATHS.relay)) return null;\n const raw = fs.readFileSync(PATHS.relay, \"utf-8\");\n const parsed = JSON.parse(raw) as RelaySession;\n // Basic validation\n if (!parsed.session || !parsed.relayUrl || !parsed.privateKey || !parsed.publicKey) {\n return null;\n }\n return parsed;\n } catch {\n return null;\n }\n}\n\nfunction saveRelaySession(info: RelaySession): void {\n ensureDir(PATHS.root);\n fs.writeFileSync(PATHS.relay, JSON.stringify(info, null, 2) + \"\\n\", {\n encoding: \"utf-8\",\n mode: 0o600,\n });\n}\n\nfunction deleteRelaySession(): void {\n try {\n if (fs.existsSync(PATHS.relay)) fs.unlinkSync(PATHS.relay);\n } catch {\n // ignore\n }\n}\n\n/**\n * Get or create a relay bridge connection.\n */\nexport async function getBridge(options: BridgeOptions): Promise<WSBridge> {\n const config = loadConfig();\n const keysUrl = options.keysUrl ?? config.keysUrl ?? DEFAULT_KEYS_URL;\n const relayUrl = options.relayUrl ?? config.relayUrl ?? DEFAULT_RELAY_URL;\n const chainId = options.chainId ?? config.defaultChain ?? 1;\n if (!isValidKeysUrl(keysUrl)) {\n throw new Error(`Untrusted keysUrl: ${keysUrl}. Must be a *.jaw.id domain (HTTPS) or localhost.`);\n }\n if (!isValidRelayUrl(relayUrl)) {\n throw new Error(`Untrusted relayUrl: ${relayUrl}. Must be wss://*.jaw.id or ws://localhost.`);\n }\n\n // Try existing session first\n let relaySession = loadRelaySession();\n if (relaySession && relaySession.relayUrl === relayUrl) {\n try {\n return await connectBridge(relaySession, options, chainId, keysUrl, relayUrl);\n } catch {\n // Connection failed — stale session or relay restarted.\n // Delete and fall through to create a new one.\n deleteRelaySession();\n relaySession = null;\n }\n }\n\n // New session\n const session = await createNewSession(relayUrl);\n saveRelaySession(session);\n return await connectBridge(session, options, chainId, keysUrl, relayUrl);\n}\n\nasync function createNewSession(relayUrl: string): Promise<RelaySession> {\n const kp = await generateKeyPair();\n const privateKey = await exportKeyToHex(\"private\", kp.privateKey);\n const publicKey = await exportKeyToHex(\"public\", kp.publicKey);\n\n return {\n session: crypto.randomUUID(),\n relayUrl,\n privateKey,\n publicKey,\n peerPublicKey: null,\n startedAt: new Date().toISOString(),\n };\n}\n\nasync function connectBridge(\n relaySession: RelaySession,\n options: BridgeOptions,\n chainId: number,\n keysUrl: string,\n relayUrl: string,\n): Promise<WSBridge> {\n const config = loadConfig();\n const bridge = new WSBridge({\n relayUrl,\n session: relaySession.session,\n timeout: options.timeout,\n config: {\n apiKey: options.apiKey,\n chainId,\n ens: options.ens ?? config.ens,\n paymasterUrl: options.paymasterUrl ?? config.paymasterUrl,\n },\n privateKeyHex: relaySession.privateKey,\n publicKeyHex: relaySession.publicKey,\n peerPublicKeyHex: relaySession.peerPublicKey,\n });\n\n await bridge.connect(\n // onBrowserNeeded\n async () => {\n const bridgeUrl = buildBridgeUrl(keysUrl, relaySession.session, relayUrl, relaySession.publicKey);\n const { default: open } = await import(\"open\");\n await open(bridgeUrl);\n },\n // onPeerKeyChanged\n (newPeerKey) => {\n relaySession.peerPublicKey = newPeerKey;\n saveRelaySession(relaySession);\n },\n );\n\n return bridge;\n}\n\nfunction buildBridgeUrl(\n keysUrl: string,\n session: string,\n relayUrl: string,\n cliPublicKeyHex: string,\n): string {\n const url = new URL(\"/cli-bridge\", keysUrl);\n url.searchParams.set(\"session\", session);\n url.searchParams.set(\"relay\", relayUrl);\n // CLI public key in fragment — not sent to server\n url.hash = `pk=${cliPublicKeyHex}`;\n return url.toString();\n}\n\n/**\n * Shutdown: close the relay session and clean up.\n */\nexport async function shutdownDaemon(): Promise<void> {\n const session = loadRelaySession();\n if (!session) return;\n\n // Send shutdown directly — no init/ready handshake needed\n try {\n const bridge = new WSBridge({\n relayUrl: session.relayUrl,\n session: session.session,\n timeout: 5000,\n config: { apiKey: \"\", chainId: 1 },\n privateKeyHex: session.privateKey,\n publicKeyHex: session.publicKey,\n peerPublicKeyHex: session.peerPublicKey,\n });\n await bridge.connectAndShutdown();\n } catch {\n // Best effort — relay or browser may already be gone\n }\n\n deleteRelaySession();\n\n // Legacy cleanup: kill old daemon process and remove files\n const legacyBridge = PATHS.root + \"/bridge.json\";\n const legacyLog = PATHS.root + \"/daemon.log\";\n const legacyLock = PATHS.root + \"/daemon.lock\";\n try {\n if (fs.existsSync(legacyBridge)) {\n const info = JSON.parse(fs.readFileSync(legacyBridge, \"utf-8\"));\n if (info.pid && Number.isInteger(info.pid) && info.pid > 0) {\n try { process.kill(info.pid, \"SIGTERM\"); } catch { /* already dead */ }\n }\n }\n } catch { /* ignore */ }\n for (const f of [legacyBridge, legacyLog, legacyLock]) {\n try { if (fs.existsSync(f)) fs.unlinkSync(f); } catch { /* ignore */ }\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/lib/paths.ts","../src/lib/validation.ts","../src/lib/config.ts","../src/lib/output.ts","../src/base-command.ts","../src/lib/crypto.ts","../src/lib/ws-bridge.ts","../src/lib/bridge-singleton.ts"],"names":["fs","crypto2"],"mappings":"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","file":"index.js","sourcesContent":["import * as path from 'node:path';\nimport * as os from 'node:os';\n\nconst JAW_DIR = path.join(os.homedir(), '.jaw');\n\nexport const PATHS = {\n root: JAW_DIR,\n config: path.join(JAW_DIR, 'config.json'),\n session: path.join(JAW_DIR, 'session.json'),\n relay: path.join(JAW_DIR, 'relay.json'),\n keystore: path.join(JAW_DIR, 'keystore.json'),\n sessionConfig: path.join(JAW_DIR, 'session-config.json'),\n} as const;\n","import type { PermissionsConfig } from './types.js';\n\nconst ADDRESS_RE = /^0x[0-9a-fA-F]{40}$/;\nconst SELECTOR_RE = /^0x[0-9a-fA-F]{8}$/;\nconst HEX_RE = /^0x[0-9a-fA-F]+$/;\nconst VALID_SPEND_UNITS = new Set(['minute', 'hour', 'day', 'week', 'month', 'year', 'forever']);\n\nexport function parsePermissionsConfig(raw: unknown): PermissionsConfig {\n const errors: string[] = [];\n\n if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) {\n throw new Error('Invalid permissions:\\n - Must be an object');\n }\n\n const obj = raw as Record<string, unknown>;\n const calls = obj.calls as unknown[] | undefined;\n const spends = obj.spends as unknown[] | undefined;\n\n if (!calls && !spends) {\n throw new Error('Invalid permissions:\\n - Must include at least \"calls\" or \"spends\"');\n }\n\n if (calls !== undefined) {\n if (!Array.isArray(calls) || calls.length === 0) {\n errors.push('calls: Must be a non-empty array');\n } else {\n for (let i = 0; i < calls.length; i++) {\n const c = calls[i] as Record<string, unknown>;\n if (!c || typeof c !== 'object') {\n errors.push(`calls.${i}: Must be an object`);\n continue;\n }\n if (typeof c.target !== 'string' || !ADDRESS_RE.test(c.target)) {\n errors.push(`calls.${i}.target: Must be a valid 0x address (40 hex chars)`);\n }\n if (c.selector !== undefined && (typeof c.selector !== 'string' || !SELECTOR_RE.test(c.selector))) {\n errors.push(`calls.${i}.selector: Must be a 4-byte hex selector (e.g. 0xa9059cbb)`);\n }\n }\n }\n }\n\n if (spends !== undefined) {\n if (!Array.isArray(spends) || spends.length === 0) {\n errors.push('spends: Must be a non-empty array');\n } else {\n for (let i = 0; i < spends.length; i++) {\n const s = spends[i] as Record<string, unknown>;\n if (!s || typeof s !== 'object') {\n errors.push(`spends.${i}: Must be an object`);\n continue;\n }\n if (typeof s.token !== 'string' || !ADDRESS_RE.test(s.token)) {\n errors.push(`spends.${i}.token: Must be a valid 0x address (40 hex chars)`);\n }\n if (typeof s.allowance !== 'string' || !HEX_RE.test(s.allowance)) {\n errors.push(`spends.${i}.allowance: Must be a non-empty 0x hex value`);\n }\n if (typeof s.unit !== 'string' || !VALID_SPEND_UNITS.has(s.unit)) {\n errors.push(`spends.${i}.unit: Must be one of: ${[...VALID_SPEND_UNITS].join(', ')}`);\n }\n if (s.multiplier !== undefined && (!Number.isInteger(s.multiplier) || (s.multiplier as number) < 1)) {\n errors.push(`spends.${i}.multiplier: Must be a positive integer`);\n }\n }\n }\n }\n\n if (errors.length > 0) {\n throw new Error(`Invalid permissions:\\n${errors.map((e) => ` - ${e}`).join('\\n')}`);\n }\n\n return raw as PermissionsConfig;\n}\n\nexport function isValidKeysUrl(url: string): boolean {\n try {\n const parsed = new URL(url);\n const isTrustedHost =\n parsed.hostname.endsWith('.jaw.id') ||\n parsed.hostname === 'jaw.id' ||\n parsed.hostname === 'localhost' ||\n parsed.hostname === '127.0.0.1';\n const isSecure = parsed.protocol === 'https:' || parsed.hostname === 'localhost' || parsed.hostname === '127.0.0.1';\n return isTrustedHost && isSecure;\n } catch {\n return false;\n }\n}\n\nexport function isValidRelayUrl(url: string): boolean {\n try {\n const parsed = new URL(url);\n const isTrustedHost =\n parsed.hostname.endsWith('.jaw.id') ||\n parsed.hostname === 'jaw.id' ||\n parsed.hostname === 'localhost' ||\n parsed.hostname === '127.0.0.1';\n const isSecure = parsed.protocol === 'wss:' || parsed.hostname === 'localhost' || parsed.hostname === '127.0.0.1';\n const isWebSocket = parsed.protocol === 'wss:' || parsed.protocol === 'ws:';\n return isTrustedHost && isSecure && isWebSocket;\n } catch {\n return false;\n }\n}\n","import * as fs from 'node:fs';\nimport { PATHS } from './paths.js';\nimport type { JawConfig } from './types.js';\nimport { isValidKeysUrl, isValidRelayUrl } from './validation.js';\n\nexport function ensureDir(dir: string): void {\n fs.mkdirSync(dir, { recursive: true, mode: 0o700 });\n fs.chmodSync(dir, 0o700);\n}\n\nfunction migrateConfig(config: JawConfig): JawConfig {\n if (config.paymasterUrl && !config.paymasters) {\n const chainId = config.defaultChain ?? 1;\n config.paymasters = { [chainId]: { url: config.paymasterUrl } };\n delete config.paymasterUrl;\n saveConfig(config);\n }\n return config;\n}\n\nexport function loadConfig(): JawConfig {\n if (!fs.existsSync(PATHS.config)) {\n return {};\n }\n const raw = fs.readFileSync(PATHS.config, 'utf-8');\n try {\n const config = JSON.parse(raw) as JawConfig;\n return migrateConfig(config);\n } catch {\n throw new Error(\n `Config file at ${PATHS.config} is not valid JSON. Run \\`jaw config set apiKey=<key>\\` to reset it.`\n );\n }\n}\n\nexport function saveConfig(config: JawConfig): void {\n ensureDir(PATHS.root);\n fs.writeFileSync(PATHS.config, JSON.stringify(config, null, 2) + '\\n', {\n encoding: 'utf-8',\n mode: 0o600,\n });\n}\n\nexport function redactConfig(config: JawConfig): Record<string, unknown> {\n return {\n ...config,\n apiKey: config.apiKey ? `${config.apiKey.slice(0, 8)}...` : undefined,\n };\n}\n\nexport function setConfigValue(key: keyof JawConfig, value: string | number): void {\n if (key === 'keysUrl' && typeof value === 'string' && !isValidKeysUrl(value)) {\n throw new Error(`Untrusted keysUrl: ${value}. Must be a *.jaw.id domain (HTTPS) or localhost.`);\n }\n if (key === 'relayUrl' && typeof value === 'string' && !isValidRelayUrl(value)) {\n throw new Error(`Untrusted relayUrl: ${value}. Must be wss://*.jaw.id or ws://localhost.`);\n }\n const config = loadConfig();\n const updated = { ...config, [key]: value };\n saveConfig(updated);\n}\n","import type { OutputFormat } from './types.js';\n\nexport function formatOutput(data: unknown, format: OutputFormat): string {\n if (format === 'json') {\n return JSON.stringify(data, replaceBigInt, 2);\n }\n return formatHuman(data);\n}\n\nfunction replaceBigInt(_key: string, value: unknown): unknown {\n if (typeof value === 'bigint') {\n return value.toString();\n }\n return value;\n}\n\nfunction formatHuman(data: unknown, indent = 0): string {\n if (data === null || data === undefined) {\n return 'null';\n }\n\n if (typeof data === 'string' || typeof data === 'number' || typeof data === 'boolean' || typeof data === 'bigint') {\n return String(data);\n }\n\n if (Array.isArray(data)) {\n if (data.length === 0) return '(empty)';\n return data.map((item, i) => `${i + 1}. ${formatHuman(item, indent + 2)}`).join('\\n');\n }\n\n if (typeof data === 'object') {\n const entries = Object.entries(data as Record<string, unknown>);\n if (entries.length === 0) return '(empty)';\n const pad = ' '.repeat(indent);\n const maxKeyLen = Math.max(...entries.map(([k]) => k.length));\n return entries\n .map(([key, val]) => {\n const paddedKey = key.padEnd(maxKeyLen);\n const valStr = typeof val === 'object' && val !== null ? '\\n' + formatHuman(val, indent + 2) : String(val);\n return `${pad}${paddedKey} ${valStr}`;\n })\n .join('\\n');\n }\n\n return String(data);\n}\n","import { Command, Flags } from '@oclif/core';\nimport type { OutputFormat } from './lib/types.js';\nimport { loadConfig } from './lib/config.js';\nimport { formatOutput } from './lib/output.js';\n\nexport abstract class BaseCommand extends Command {\n static override baseFlags = {\n output: Flags.string({\n char: 'o',\n description: 'Output format',\n options: ['json', 'human'],\n default: 'human',\n env: 'JAW_OUTPUT',\n }),\n chain: Flags.integer({\n char: 'c',\n description: 'Chain ID',\n env: 'JAW_CHAIN_ID',\n }),\n 'api-key': Flags.string({\n description: 'JAW API key',\n env: 'JAW_API_KEY',\n }),\n yes: Flags.boolean({\n char: 'y',\n description: 'Skip confirmations (for AI agents)',\n default: false,\n }),\n quiet: Flags.boolean({\n char: 'q',\n description: 'Suppress non-essential output',\n default: false,\n }),\n };\n\n protected resolveApiKey(flags: { 'api-key'?: string }): string {\n const apiKey = flags['api-key'] ?? loadConfig().apiKey;\n if (!apiKey) {\n this.error('API key required. Set via --api-key, JAW_API_KEY env, or `jaw config set apiKey <key>`');\n }\n return apiKey;\n }\n\n protected resolveChainId(flags: { chain?: number }): number {\n const chainId = flags.chain ?? loadConfig().defaultChain;\n if (!chainId) {\n this.error('Chain ID required. Set via --chain, JAW_CHAIN_ID env, or `jaw config set defaultChain <id>`');\n }\n return chainId;\n }\n\n protected outputResult(data: unknown, format: OutputFormat): void {\n const output = formatOutput(data, format);\n this.log(output);\n }\n}\n","/**\n * E2E encryption primitives for CLI ↔ browser communication.\n *\n * Uses ECDH P-256 for key exchange and AES-256-GCM for message encryption.\n * Mirrors the same crypto operations in @jaw.id/core but uses only\n * Node.js built-in crypto.subtle — no external dependencies.\n */\n\nimport type { webcrypto } from 'node:crypto';\n\ntype CKey = webcrypto.CryptoKey;\ntype CKeyPair = webcrypto.CryptoKeyPair;\n\nconst subtle = globalThis.crypto.subtle;\n\n// ── Key generation ───────────────────────────────────────────────\n\nexport async function generateKeyPair(): Promise<CKeyPair> {\n return subtle.generateKey({ name: 'ECDH', namedCurve: 'P-256' }, true, ['deriveKey']) as Promise<CKeyPair>;\n}\n\nexport async function deriveSharedSecret(privateKey: CKey, peerPublicKey: CKey): Promise<CKey> {\n return subtle.deriveKey(\n { name: 'ECDH', public: peerPublicKey },\n privateKey,\n { name: 'AES-GCM', length: 256 },\n false,\n ['encrypt', 'decrypt']\n );\n}\n\n// ── Encrypt / Decrypt ────────────────────────────────────────────\n\nexport interface EncryptedEnvelope {\n iv: string; // base64\n ciphertext: string; // base64\n}\n\nexport async function encryptMessage(sharedSecret: CKey, payload: Record<string, unknown>): Promise<EncryptedEnvelope> {\n const iv = globalThis.crypto.getRandomValues(new Uint8Array(12));\n const plaintext = new TextEncoder().encode(JSON.stringify(payload));\n const cipherBuf = await subtle.encrypt({ name: 'AES-GCM', iv }, sharedSecret, plaintext);\n return {\n iv: bufferToBase64(iv),\n ciphertext: bufferToBase64(new Uint8Array(cipherBuf)),\n };\n}\n\nexport async function decryptMessage(\n sharedSecret: CKey,\n envelope: EncryptedEnvelope\n): Promise<Record<string, unknown>> {\n const iv = Buffer.from(envelope.iv, 'base64');\n const ciphertext = Buffer.from(envelope.ciphertext, 'base64');\n const plainBuf = await subtle.decrypt({ name: 'AES-GCM', iv }, sharedSecret, ciphertext);\n return JSON.parse(new TextDecoder().decode(plainBuf));\n}\n\n// ── Key import / export (hex) ────────────────────────────────────\n\nexport async function exportKeyToHex(type: 'private' | 'public', key: CKey): Promise<string> {\n const format = type === 'private' ? 'pkcs8' : 'spki';\n const buf = await subtle.exportKey(format, key);\n return bytesToHex(new Uint8Array(buf));\n}\n\nexport async function importKeyFromHex(type: 'private' | 'public', hex: string): Promise<CKey> {\n const format = type === 'private' ? 'pkcs8' : 'spki';\n return subtle.importKey(\n format,\n Buffer.from(hexToBytes(hex)),\n { name: 'ECDH', namedCurve: 'P-256' },\n true,\n type === 'private' ? ['deriveKey'] : []\n );\n}\n\n// ── Encoding helpers ─────────────────────────────────────────────\n\nfunction bytesToHex(bytes: Uint8Array): string {\n return Array.from(bytes)\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('');\n}\n\nfunction hexToBytes(hex: string): Uint8Array {\n if (hex.length % 2 !== 0) throw new Error('Invalid hex: odd length');\n const bytes = new Uint8Array(hex.length / 2);\n for (let i = 0; i < hex.length; i += 2) {\n bytes[i / 2] = parseInt(hex.substring(i, i + 2), 16);\n }\n return bytes;\n}\n\nfunction bufferToBase64(buf: Uint8Array): string {\n return Buffer.from(buf).toString('base64');\n}\n","/**\n * WSBridge — CLI-side relay client\n *\n * Connects to the cloud relay (wss://relay.jaw.id) instead of a local daemon.\n * All messages (except key_exchange) are E2E encrypted via ECDH + AES-256-GCM.\n */\n\nimport * as crypto from 'node:crypto';\nimport type { webcrypto } from 'node:crypto';\nimport WebSocket from 'ws';\nimport {\n deriveSharedSecret,\n encryptMessage,\n decryptMessage,\n importKeyFromHex,\n type EncryptedEnvelope,\n} from './crypto.js';\n\ntype CKey = webcrypto.CryptoKey;\n\nexport interface WSBridgeConfig {\n apiKey: string;\n chainId: number;\n ens?: string;\n paymasterUrl?: string;\n}\n\nexport interface WSBridgeOptions {\n relayUrl: string;\n session: string;\n timeout?: number;\n config: WSBridgeConfig;\n /** CLI's ECDH private key (hex). Loaded from relay.json for existing sessions. */\n privateKeyHex: string;\n /** CLI's ECDH public key (hex). Sent to browser for key derivation. */\n publicKeyHex: string;\n /** Browser's ECDH public key (hex). Null if new session (key exchange needed). */\n peerPublicKeyHex: string | null;\n}\n\nconst DEFAULT_TIMEOUT_MS = 120_000;\n\n/**\n * Maximum outbound message size (5 MB).\n *\n * wallet_sendCalls with large batches (50+ calls, complex calldata) can reach\n * hundreds of KB. Base64 encoding of the AES-GCM ciphertext adds ~33% overhead.\n * 5 MB is generous enough for any realistic batch while still preventing\n * accidental memory issues.\n */\nconst MAX_MESSAGE_BYTES = 5 * 1024 * 1024;\n\n/** Minimum time between browser reopen attempts (ms). */\nconst BROWSER_REOPEN_COOLDOWN_MS = 5_000;\n\n/** Maximum reconnection attempts before giving up. */\nconst MAX_RECONNECT_ATTEMPTS = 3;\n\n/** Base delay for exponential backoff (ms). */\nconst RECONNECT_BASE_DELAY_MS = 1_000;\n\nexport class WSBridge {\n private readonly relayUrl: string;\n private readonly session: string;\n private readonly timeout: number;\n private readonly config: WSBridgeConfig;\n private readonly privateKeyHex: string;\n readonly publicKeyHex: string;\n private peerPublicKeyHex: string | null;\n private sharedSecret: CKey | null = null;\n private ws: WebSocket | null = null;\n private disposed = false;\n\n // Auto-reopen browser state\n private onBrowserNeeded: (() => Promise<void>) | undefined;\n private onPeerKeyChanged: ((key: string) => void) | undefined;\n private lastBrowserOpenTime = 0;\n\n // Reconnection state\n private reconnectAttempts = 0;\n\n /** Updated after key exchange — caller should persist this. */\n get peerPublicKey(): string | null {\n return this.peerPublicKeyHex;\n }\n\n constructor(options: WSBridgeOptions) {\n this.relayUrl = options.relayUrl;\n this.session = options.session;\n this.timeout = options.timeout ?? DEFAULT_TIMEOUT_MS;\n this.config = options.config;\n this.privateKeyHex = options.privateKeyHex;\n this.publicKeyHex = options.publicKeyHex;\n this.peerPublicKeyHex = options.peerPublicKeyHex;\n }\n\n /**\n * Connect to the relay and wait for the browser to be ready.\n *\n * @param onBrowserNeeded — called when the relay reports no browser connected.\n * @param onPeerKeyChanged — called when a key_exchange updates the peer key.\n */\n async connect(\n onBrowserNeeded?: () => Promise<void>,\n onPeerKeyChanged?: (newPeerPublicKeyHex: string) => void\n ): Promise<void> {\n // Store callbacks for auto-reopen on browser disconnect\n this.onBrowserNeeded = onBrowserNeeded;\n this.onPeerKeyChanged = onPeerKeyChanged;\n\n // Pre-derive shared secret if we already have the peer key\n if (this.peerPublicKeyHex) {\n await this.deriveSecret();\n }\n\n return this.connectInternal(onBrowserNeeded, onPeerKeyChanged);\n }\n\n private async connectInternal(\n onBrowserNeeded?: () => Promise<void>,\n onPeerKeyChanged?: (newPeerPublicKeyHex: string) => void\n ): Promise<void> {\n return new Promise((resolve, reject) => {\n const url = `${this.relayUrl}?session=${encodeURIComponent(this.session)}&role=cli`;\n const ws = new WebSocket(url);\n\n let browserOpened = false;\n let resolved = false;\n let expectingKeyExchange = !this.peerPublicKeyHex;\n\n const timer = setTimeout(() => {\n ws.close();\n reject(new Error('Browser did not connect in time.\\n' + 'Run `jaw disconnect` then try again.'));\n }, 30_000);\n\n const sendEncryptedInit = async () => {\n if (!this.sharedSecret) return;\n const envelope = await encryptMessage(this.sharedSecret, {\n type: 'init',\n apiKey: this.config.apiKey,\n chainId: this.config.chainId,\n ens: this.config.ens,\n paymasterUrl: this.config.paymasterUrl,\n });\n this.sendRaw(ws, JSON.stringify({ type: 'encrypted', ...envelope }));\n };\n\n const waitForReady = () => {\n const readyTimer = setTimeout(() => {\n ws.close();\n reject(new Error('Browser SDK did not become ready in time.'));\n }, 15_000);\n\n const onMsg = async (data: WebSocket.Data) => {\n const msg = safeParse(data);\n if (!msg) return;\n\n if (msg.type === 'encrypted' && this.sharedSecret) {\n try {\n const inner = await decryptMessage(this.sharedSecret, msg as unknown as EncryptedEnvelope);\n if (inner.type === 'ready') {\n clearTimeout(readyTimer);\n ws.off('message', onMsg);\n this.reconnectAttempts = 0; // Reset on successful connect\n resolve();\n }\n } catch {\n // Not a valid encrypted message for us, ignore\n }\n }\n };\n ws.on('message', onMsg);\n };\n\n const onBrowserReady = async () => {\n if (resolved) return;\n resolved = true;\n clearTimeout(timer);\n // Set up listener BEFORE sending init to avoid missing the ready response\n waitForReady();\n await sendEncryptedInit();\n };\n\n ws.on('open', () => {\n this.ws = ws;\n });\n\n ws.on('message', async (data) => {\n const msg = safeParse(data);\n if (!msg) return;\n\n if (msg.type === 'status') {\n if (msg.browserConnected) {\n if (this.sharedSecret) {\n // Already have shared secret — skip key exchange\n await onBrowserReady();\n } else {\n // Browser is connected but we don't have its key yet.\n expectingKeyExchange = true;\n }\n } else if (!browserOpened && onBrowserNeeded) {\n browserOpened = true;\n expectingKeyExchange = true;\n onBrowserNeeded().catch(() => {\n /* best effort */\n });\n } else if (!onBrowserNeeded) {\n // Reuse path (bridge-singleton.ts called connectBridge with\n // openBrowser=false): the relay reports no browser connected and\n // we have no callback to open one. Bail immediately so the\n // caller can fall through to a fresh session, instead of waiting\n // out the 30s connect-timer.\n clearTimeout(timer);\n ws.close();\n reject(new Error('Browser not connected — relay session is stale.'));\n }\n } else if (msg.type === 'browser_connected') {\n expectingKeyExchange = true;\n // Wait for key_exchange from browser\n } else if (msg.type === 'browser_disconnected') {\n // Browser tab closed — attempt to reopen after cooldown\n this.handleBrowserDisconnect();\n } else if (msg.type === 'key_exchange' && expectingKeyExchange) {\n expectingKeyExchange = false;\n const peerKey = msg.publicKey as string;\n this.peerPublicKeyHex = peerKey;\n await this.deriveSecret();\n onPeerKeyChanged?.(peerKey);\n await onBrowserReady();\n }\n });\n\n ws.on('error', (err) => {\n clearTimeout(timer);\n reject(err);\n });\n\n ws.on('close', () => {\n clearTimeout(timer);\n if (!this.disposed) {\n this.handleRelayDisconnect();\n }\n });\n });\n }\n\n /**\n * Send an encrypted RPC request through the relay to the browser SDK.\n */\n async request(method: string, params?: unknown): Promise<unknown> {\n const ws = this.ws;\n if (!ws || ws.readyState !== WebSocket.OPEN) {\n throw new Error('Not connected to relay');\n }\n if (!this.sharedSecret) {\n throw new Error('No shared secret — key exchange not completed');\n }\n\n const id = crypto.randomUUID();\n\n const envelope = await encryptMessage(this.sharedSecret, {\n type: 'rpc_request',\n id,\n method,\n params,\n });\n\n const serialized = JSON.stringify({ type: 'encrypted', ...envelope });\n assertMessageSize(serialized, method);\n\n return new Promise<unknown>((resolve, reject) => {\n const timer = setTimeout(() => {\n reject(\n new Error(`Request timed out after ${this.timeout / 1000}s. ` + 'Did you complete the action in the browser?')\n );\n this.close();\n }, this.timeout);\n\n const onMessage = async (data: WebSocket.Data) => {\n const msg = safeParse(data);\n if (!msg || msg.type !== 'encrypted' || !this.sharedSecret) return;\n\n try {\n const inner = await decryptMessage(this.sharedSecret, msg as unknown as EncryptedEnvelope);\n if (inner.type === 'rpc_response' && inner.id === id) {\n clearTimeout(timer);\n ws.off('message', onMessage);\n\n if (inner.success) {\n resolve(inner.data);\n } else {\n const err = inner.error as { code: number; message: string } | undefined;\n reject(new Error(err ? `[${err.code}] ${err.message}` : 'Request failed'));\n }\n }\n } catch {\n // Decryption failed — not our message or tampered, ignore\n }\n };\n\n ws.on('message', onMessage);\n this.sendRaw(ws, serialized);\n });\n }\n\n async shutdown(): Promise<void> {\n this.disposed = true;\n if (this.ws?.readyState === WebSocket.OPEN && this.sharedSecret) {\n try {\n const envelope = await encryptMessage(this.sharedSecret, {\n type: 'shutdown',\n });\n this.sendRaw(this.ws, JSON.stringify({ type: 'encrypted', ...envelope }));\n } catch {\n // Best effort\n }\n }\n this.close();\n }\n\n /**\n * Connect to relay and send shutdown directly — no init/ready handshake.\n * Used by `jaw disconnect` when we just need to tell the browser to close.\n */\n async connectAndShutdown(): Promise<void> {\n if (!this.peerPublicKeyHex) {\n // No peer key means browser never connected — nothing to shut down\n return;\n }\n\n this.disposed = true;\n await this.deriveSecret();\n\n return new Promise<void>((resolve) => {\n const url = `${this.relayUrl}?session=${encodeURIComponent(this.session)}&role=cli`;\n const ws = new WebSocket(url);\n\n const timer = setTimeout(() => {\n try {\n ws.close();\n } catch {\n /* ignore */\n }\n resolve();\n }, 3000);\n\n ws.on('open', async () => {\n this.ws = ws;\n try {\n await this.shutdown();\n } catch {\n // Best effort\n }\n clearTimeout(timer);\n resolve();\n });\n\n ws.on('error', () => {\n clearTimeout(timer);\n resolve();\n });\n });\n }\n\n close(): void {\n this.disposed = true;\n if (this.ws) {\n try {\n this.ws.close();\n } catch {\n // ignore\n }\n this.ws = null;\n }\n }\n\n /**\n * Auto-reopen browser when browser_disconnected is received from relay.\n * Respects a cooldown to prevent rapid re-opening.\n */\n private handleBrowserDisconnect(): void {\n if (this.disposed || !this.onBrowserNeeded) return;\n\n const now = Date.now();\n if (now - this.lastBrowserOpenTime < BROWSER_REOPEN_COOLDOWN_MS) {\n return; // Too soon — skip this reopen\n }\n\n this.lastBrowserOpenTime = now;\n\n // Reset shared secret — the new browser tab will do a fresh key exchange\n this.sharedSecret = null;\n this.peerPublicKeyHex = null;\n\n this.onBrowserNeeded().catch(() => {\n // Best effort — if browser open fails, the next request will error\n });\n }\n\n /**\n * Attempt to reconnect to the relay with exponential backoff\n * when the WebSocket connection drops unexpectedly.\n */\n private handleRelayDisconnect(): void {\n if (this.disposed) return;\n if (this.reconnectAttempts >= MAX_RECONNECT_ATTEMPTS) return;\n\n const delay = RECONNECT_BASE_DELAY_MS * Math.pow(2, this.reconnectAttempts);\n this.reconnectAttempts++;\n\n setTimeout(() => {\n if (this.disposed) return;\n this.connectInternal(this.onBrowserNeeded, this.onPeerKeyChanged).catch(() => {\n // Reconnection failed — will retry if attempts remain\n });\n }, delay);\n }\n\n /** Send a raw string over the WebSocket, enforcing message size limits. */\n private sendRaw(ws: WebSocket, data: string): void {\n ws.send(data);\n }\n\n private async deriveSecret(): Promise<void> {\n if (!this.peerPublicKeyHex) return;\n const privateKey = await importKeyFromHex('private', this.privateKeyHex);\n const peerPublicKey = await importKeyFromHex('public', this.peerPublicKeyHex);\n this.sharedSecret = await deriveSharedSecret(privateKey, peerPublicKey);\n }\n}\n\n/**\n * Validate message size before sending to the relay.\n * Throws with a descriptive error if the message is too large.\n */\nfunction assertMessageSize(serialized: string, method: string): void {\n const byteLength = Buffer.byteLength(serialized, 'utf-8');\n if (byteLength > MAX_MESSAGE_BYTES) {\n const sizeMB = (byteLength / (1024 * 1024)).toFixed(2);\n throw new Error(\n `Message for ${method} is too large (${sizeMB} MB, limit ${MAX_MESSAGE_BYTES / (1024 * 1024)} MB). ` +\n 'Try reducing the number of calls in your batch.'\n );\n }\n}\n\nfunction safeParse(data: WebSocket.Data): Record<string, unknown> | null {\n try {\n return JSON.parse(data.toString()) as Record<string, unknown>;\n } catch {\n return null;\n }\n}\n","/**\n * Bridge lifecycle management — relay edition.\n *\n * - Generates a session token + ECDH keypair for new sessions\n * - Opens the browser to keys.jaw.id/cli-bridge\n * - Saves relay state to ~/.jaw/relay.json\n * - Creates a WSBridge client connected to the relay\n */\n\nimport * as fs from 'node:fs';\nimport * as crypto from 'node:crypto';\nimport { PATHS } from './paths.js';\nimport { ensureDir, loadConfig } from './config.js';\nimport { WSBridge } from './ws-bridge.js';\nimport { isValidKeysUrl, isValidRelayUrl } from './validation.js';\nimport { generateKeyPair, exportKeyToHex } from './crypto.js';\n\nconst DEFAULT_KEYS_URL = 'https://keys.jaw.id';\nconst DEFAULT_RELAY_URL = 'wss://relay.jaw.id';\n\ninterface RelaySession {\n session: string;\n relayUrl: string;\n privateKey: string;\n publicKey: string;\n peerPublicKey: string | null;\n startedAt: string;\n}\n\nexport interface BridgeOptions {\n keysUrl?: string;\n relayUrl?: string;\n apiKey: string;\n chainId?: number;\n ens?: string;\n paymasterUrl?: string;\n timeout?: number;\n}\n\nfunction loadRelaySession(): RelaySession | null {\n try {\n if (!fs.existsSync(PATHS.relay)) return null;\n const raw = fs.readFileSync(PATHS.relay, 'utf-8');\n const parsed = JSON.parse(raw) as RelaySession;\n // Basic validation\n if (!parsed.session || !parsed.relayUrl || !parsed.privateKey || !parsed.publicKey) {\n return null;\n }\n return parsed;\n } catch {\n return null;\n }\n}\n\nfunction saveRelaySession(info: RelaySession): void {\n ensureDir(PATHS.root);\n fs.writeFileSync(PATHS.relay, JSON.stringify(info, null, 2) + '\\n', {\n encoding: 'utf-8',\n mode: 0o600,\n });\n}\n\nfunction deleteRelaySession(): void {\n try {\n if (fs.existsSync(PATHS.relay)) fs.unlinkSync(PATHS.relay);\n } catch {\n // ignore\n }\n}\n\n/**\n * Get or create a relay bridge connection.\n */\nexport async function getBridge(options: BridgeOptions): Promise<WSBridge> {\n const config = loadConfig();\n const keysUrl = options.keysUrl ?? config.keysUrl ?? DEFAULT_KEYS_URL;\n const relayUrl = options.relayUrl ?? config.relayUrl ?? DEFAULT_RELAY_URL;\n const chainId = options.chainId ?? config.defaultChain ?? 1;\n if (!isValidKeysUrl(keysUrl)) {\n throw new Error(`Untrusted keysUrl: ${keysUrl}. Must be a *.jaw.id domain (HTTPS) or localhost.`);\n }\n if (!isValidRelayUrl(relayUrl)) {\n throw new Error(`Untrusted relayUrl: ${relayUrl}. Must be wss://*.jaw.id or ws://localhost.`);\n }\n\n // Try existing session first — but never open a browser for a stale session.\n // If the browser is already connected, reuse it. If not, fall through to a new session.\n let relaySession = loadRelaySession();\n if (relaySession && relaySession.relayUrl === relayUrl && relaySession.peerPublicKey) {\n try {\n return await connectBridge(relaySession, options, chainId, keysUrl, relayUrl, false);\n } catch {\n // Connection failed — stale session or relay restarted.\n // Delete and fall through to create a new one.\n deleteRelaySession();\n relaySession = null;\n }\n } else if (relaySession) {\n // Incomplete session (no peer key) — discard\n deleteRelaySession();\n }\n\n // New session — this is the only path that opens a browser\n const session = await createNewSession(relayUrl);\n saveRelaySession(session);\n return await connectBridge(session, options, chainId, keysUrl, relayUrl, true);\n}\n\nasync function createNewSession(relayUrl: string): Promise<RelaySession> {\n const kp = await generateKeyPair();\n const privateKey = await exportKeyToHex('private', kp.privateKey);\n const publicKey = await exportKeyToHex('public', kp.publicKey);\n\n return {\n session: crypto.randomUUID(),\n relayUrl,\n privateKey,\n publicKey,\n peerPublicKey: null,\n startedAt: new Date().toISOString(),\n };\n}\n\nasync function connectBridge(\n relaySession: RelaySession,\n options: BridgeOptions,\n chainId: number,\n keysUrl: string,\n relayUrl: string,\n openBrowser: boolean\n): Promise<WSBridge> {\n const config = loadConfig();\n const bridge = new WSBridge({\n relayUrl,\n session: relaySession.session,\n timeout: options.timeout,\n config: {\n apiKey: options.apiKey,\n chainId,\n ens: options.ens ?? config.ens,\n paymasterUrl: options.paymasterUrl ?? config.paymasters?.[chainId]?.url,\n },\n privateKeyHex: relaySession.privateKey,\n publicKeyHex: relaySession.publicKey,\n peerPublicKeyHex: relaySession.peerPublicKey,\n });\n\n await bridge.connect(\n // onBrowserNeeded — only open a browser for new sessions\n openBrowser\n ? async () => {\n const bridgeUrl = buildBridgeUrl(keysUrl, relaySession.session, relayUrl, relaySession.publicKey);\n const { default: open } = await import('open');\n await open(bridgeUrl);\n }\n : undefined,\n // onPeerKeyChanged\n (newPeerKey) => {\n relaySession.peerPublicKey = newPeerKey;\n saveRelaySession(relaySession);\n }\n );\n\n return bridge;\n}\n\nfunction buildBridgeUrl(keysUrl: string, session: string, relayUrl: string, cliPublicKeyHex: string): string {\n const url = new URL('/cli-bridge', keysUrl);\n url.searchParams.set('session', session);\n url.searchParams.set('relay', relayUrl);\n // CLI public key in fragment — not sent to server\n url.hash = `pk=${cliPublicKeyHex}`;\n return url.toString();\n}\n\n/**\n * Shutdown: close the relay session and clean up.\n */\nexport async function shutdownDaemon(): Promise<void> {\n const session = loadRelaySession();\n if (!session) return;\n\n // Send shutdown directly — no init/ready handshake needed\n try {\n const bridge = new WSBridge({\n relayUrl: session.relayUrl,\n session: session.session,\n timeout: 5000,\n config: { apiKey: '', chainId: 1 },\n privateKeyHex: session.privateKey,\n publicKeyHex: session.publicKey,\n peerPublicKeyHex: session.peerPublicKey,\n });\n await bridge.connectAndShutdown();\n } catch {\n // Best effort — relay or browser may already be gone\n }\n\n deleteRelaySession();\n\n // Legacy cleanup: kill old daemon process and remove files\n const legacyBridge = PATHS.root + '/bridge.json';\n const legacyLog = PATHS.root + '/daemon.log';\n const legacyLock = PATHS.root + '/daemon.lock';\n try {\n if (fs.existsSync(legacyBridge)) {\n const info = JSON.parse(fs.readFileSync(legacyBridge, 'utf-8'));\n if (info.pid && Number.isInteger(info.pid) && info.pid > 0) {\n try {\n process.kill(info.pid, 'SIGTERM');\n } catch {\n /* already dead */\n }\n }\n }\n } catch {\n /* ignore */\n }\n for (const f of [legacyBridge, legacyLog, legacyLock]) {\n try {\n if (fs.existsSync(f)) fs.unlinkSync(f);\n } catch {\n /* ignore */\n }\n }\n}\n"]}