esque-bridge 0.6.11 → 0.6.12

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 (2) hide show
  1. package/index.js +41 -1
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -92,6 +92,12 @@ const BIN_OVERRIDE = argv.bin || null;
92
92
  const LT_SUBDOMAIN = argv.subdomain || process.env.LT_SUBDOMAIN || undefined;
93
93
  const SESSIONS_FILE = path.join(os.homedir(), '.esque-bridge-sessions.json');
94
94
  const SECRET_FILE = path.join(os.homedir(), '.esque-bridge-secret');
95
+ // Where to announce the current tunnel URL so the phone can rediscover it
96
+ // after a restart rotates the cloudflared URL. Best-effort — QR pairing works
97
+ // without it. Override for self-hosters via --backend / ESQUE_BACKEND_URL.
98
+ const BACKEND_URL = String(
99
+ argv.backend || process.env.ESQUE_BACKEND_URL || 'https://esque-backend-production.up.railway.app',
100
+ ).replace(/\/+$/, '');
95
101
 
96
102
  // Pairing secret. Persisted across restarts so a routine bridge restart
97
103
  // doesn't silently invalidate the phone's pairing (the #1 real-world
@@ -1477,21 +1483,55 @@ async function main() {
1477
1483
  // after a restart (the app asks GET /preview for the fresh URL).
1478
1484
  if (loadPreviewCmd()) revivePreview();
1479
1485
 
1486
+ // Announce this tunnel URL to the backend, keyed by the persistent secret,
1487
+ // so the phone can rediscover it after a restart rotates the URL — then
1488
+ // refresh on a heartbeat so the mapping never expires while we're alive.
1489
+ // Entirely best-effort: a down/unreachable backend never affects pairing or
1490
+ // prompts (the QR already carries this URL for the current session).
1491
+ const announce = async () => {
1492
+ try {
1493
+ await fetch(`${BACKEND_URL}/v1/bridge/announce`, {
1494
+ method: 'POST',
1495
+ headers: { 'content-type': 'application/json' },
1496
+ body: JSON.stringify({ secret: PAIRING_SECRET, url: tunnel.url, agent: AGENT_TYPE }),
1497
+ signal: AbortSignal.timeout(8000),
1498
+ });
1499
+ } catch {
1500
+ /* best-effort rendezvous */
1501
+ }
1502
+ };
1503
+ announce();
1504
+ const announceTimer = setInterval(announce, 45_000);
1505
+ announceTimer.unref?.();
1506
+
1480
1507
  let shuttingDown = false;
1481
1508
  const shutdown = (signal) => {
1482
1509
  shuttingDown = true;
1483
1510
  console.log(`\n Received ${signal} — closing tunnel…`);
1511
+ clearInterval(announceTimer);
1484
1512
  // The agent runs detached in its own process group, so the terminal's
1485
1513
  // Ctrl-C does NOT reach it — stop it explicitly or it keeps editing
1486
1514
  // files with no bridge attached.
1487
1515
  try { activeAgentKill && activeAgentKill('SIGTERM'); } catch { /* already gone */ }
1488
1516
  killPreview();
1517
+ // Best-effort deregister so the phone learns we're down right away rather
1518
+ // than after the rendezvous TTL. Fire-and-forget — don't delay exit on it.
1519
+ try {
1520
+ fetch(`${BACKEND_URL}/v1/bridge/forget`, {
1521
+ method: 'POST',
1522
+ headers: { 'content-type': 'application/json' },
1523
+ body: JSON.stringify({ secret: PAIRING_SECRET }),
1524
+ signal: AbortSignal.timeout(2000),
1525
+ }).catch(() => undefined);
1526
+ } catch { /* ignore */ }
1489
1527
  try {
1490
1528
  tunnel.close();
1491
1529
  } catch {
1492
1530
  /* tunnel may already be closed */
1493
1531
  }
1494
- process.exit(0);
1532
+ // Give the forget() a brief moment to flush, then exit regardless.
1533
+ setTimeout(() => process.exit(0), 250);
1534
+ return;
1495
1535
  };
1496
1536
  process.on('SIGINT', () => shutdown('SIGINT'));
1497
1537
  process.on('SIGTERM', () => shutdown('SIGTERM'));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "esque-bridge",
3
- "version": "0.6.11",
3
+ "version": "0.6.12",
4
4
  "description": "Desktop-side receiver for the Esque Agent mobile app. Pairs your phone with a local coding-agent CLI (Claude Code, Codex, Aider, or any custom command) via a tunnel + QR code, so prompts run through your subscription instead of per-token API billing.",
5
5
  "bin": {
6
6
  "esque-bridge": "index.js"