echoclaw-relay-agent 0.17.2 → 0.18.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 (2) hide show
  1. package/dist/cli.js +56 -17
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -255,6 +255,30 @@ async function runSetup(code, relay, bridgePort) {
255
255
  }
256
256
  }
257
257
  catch { /* no service manager on this platform — fine */ }
258
+ // Kill any remaining relay-agent processes not managed by service manager.
259
+ // Use narrow match: only our own binary name, exclude our own PID.
260
+ try {
261
+ const { execSync } = await import('child_process');
262
+ const myPid = process.pid.toString();
263
+ try {
264
+ execSync(`pgrep -f "echoclaw-relay-agent.*--gateway" | grep -v "^${myPid}$" | xargs kill 2>/dev/null`, { stdio: 'ignore' });
265
+ }
266
+ catch { /* no matching processes — fine */ }
267
+ // Poll until processes are gone (max 5s)
268
+ const deadline = Date.now() + 5000;
269
+ while (Date.now() < deadline) {
270
+ try {
271
+ const out = execSync(`pgrep -f "echoclaw-relay-agent.*--gateway" | grep -v "^${myPid}$"`, { stdio: 'pipe' }).toString().trim();
272
+ if (!out)
273
+ break;
274
+ }
275
+ catch {
276
+ break; /* pgrep returns non-zero = no matches */
277
+ }
278
+ await new Promise(r => setTimeout(r, 300));
279
+ }
280
+ }
281
+ catch { /* pgrep not available — fine */ }
258
282
  const sid = existingSession?.relaySessionId?.slice?.(0, 8) ?? 'unknown';
259
283
  console.log(` ${DIM}Clearing previous session (${sid}...)${RESET}`);
260
284
  try {
@@ -645,32 +669,35 @@ async function runDaemon(relay, code, gateway, bridgePort) {
645
669
  agent.on('connected', () => {
646
670
  updater.startPeriodicCheck();
647
671
  });
648
- // Retry logic — transient failures (network, server restart) shouldn't kill the service
649
- const MAX_RETRIES = 5;
650
- const RETRY_DELAY = 3000;
651
- let lastErr;
652
- for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
672
+ // V2: Never-give-up retry logic — daemon must stay alive forever.
673
+ // Only exits on: explicit UNPAIR, SIGINT/SIGTERM, or no saved session + no code.
674
+ const BASE_DELAY = 3000;
675
+ const MAX_DELAY = 60000;
676
+ let attempt = 0;
677
+ while (!stopping) {
653
678
  try {
679
+ attempt++;
654
680
  await agent.start(code);
655
- return; // successstay alive
681
+ // Connectedreset attempt counter
682
+ attempt = 0;
683
+ // agent.start() resolved means connection dropped, loop to reconnect
656
684
  }
657
685
  catch (err) {
658
- lastErr = err;
659
686
  const msg = err instanceof Error ? err.message : String(err);
660
- console.error(` [start-failed] attempt=${attempt}/${MAX_RETRIES} error=${msg}`);
661
- // Permanent errors don't retry
662
- if (msg.includes('No saved session found') ||
663
- msg.includes('DEVICE_TOKEN_MISMATCH') ||
664
- msg.includes('SESSION_NOT_FOUND')) {
687
+ console.error(` [start-failed] attempt=${attempt} error=${msg}`);
688
+ // Truly permanent: no session AND no code to pair with
689
+ if (msg.includes('No saved session found') && !code) {
690
+ console.error(` [fatal] No session and no pairing code. Run: echoclaw-relay setup <CODE>`);
665
691
  break;
666
692
  }
667
- if (attempt < MAX_RETRIES) {
668
- console.log(` [retry] waiting ${RETRY_DELAY / 1000}s...`);
669
- await new Promise(r => setTimeout(r, RETRY_DELAY));
670
- }
693
+ // SESSION_NOT_FOUND is NOT permanent in V2 — identity reconnect will handle it
694
+ // Just retry with backoff
695
+ // Exponential backoff: 3s, 6s, 12s, 24s, 48s, 60s (capped)
696
+ const delay = Math.min(BASE_DELAY * Math.pow(2, attempt - 1), MAX_DELAY);
697
+ console.log(` [retry] waiting ${(delay / 1000).toFixed(0)}s...`);
698
+ await new Promise(r => setTimeout(r, delay));
671
699
  }
672
700
  }
673
- throw lastErr;
674
701
  }
675
702
  // ── Main ─────────────────────────────────────────────────────
676
703
  async function main() {
@@ -710,4 +737,16 @@ async function main() {
710
737
  process.exit(1);
711
738
  }
712
739
  }
740
+ // V2: Global error handlers — log and exit, let supervisor (launchd/systemd) restart.
741
+ // Staying alive after uncaughtException risks corrupted state (half-open sockets,
742
+ // partial writes). Clean crash + restart is safer than zombie process.
743
+ process.on('uncaughtException', (err) => {
744
+ console.error(` [FATAL] uncaughtException: ${err.message}`);
745
+ console.error(err.stack || '');
746
+ process.exit(1);
747
+ });
748
+ process.on('unhandledRejection', (reason) => {
749
+ console.error(` [FATAL] unhandledRejection: ${reason}`);
750
+ process.exit(1);
751
+ });
713
752
  main();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "echoclaw-relay-agent",
3
- "version": "0.17.2",
3
+ "version": "0.18.0",
4
4
  "description": "EchoClaw Relay Connection — E2E encrypted relay transport, pairing, and session management",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",