orchestrating 0.1.36 → 0.1.37

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/bin/orch +20 -0
  2. package/package.json +1 -1
package/bin/orch CHANGED
@@ -448,10 +448,12 @@ async function handleDaemon(projectsDir) {
448
448
 
449
449
  const PING_INTERVAL_MS = 30_000;
450
450
  const PONG_TIMEOUT_MS = 10_000;
451
+ const HEARTBEAT_TIMEOUT_MS = 65_000; // expect server heartbeat every 30s, allow 2 missed + margin
451
452
  let ws = null;
452
453
  let pingTimer = null;
453
454
  let pongTimer = null;
454
455
  let reconnectTimer = null;
456
+ let heartbeatTimer = null;
455
457
 
456
458
  // Scan project directories
457
459
  const homeDir = os.homedir();
@@ -478,6 +480,14 @@ async function handleDaemon(projectsDir) {
478
480
  const recentRequests = new Set();
479
481
  const recentSpawns = new Map(); // "command:cwd" -> timestamp
480
482
 
483
+ function resetHeartbeatTimer(sock) {
484
+ if (heartbeatTimer) clearTimeout(heartbeatTimer);
485
+ heartbeatTimer = setTimeout(() => {
486
+ process.stderr.write(`${RED}${PREFIX} No server heartbeat — reconnecting${RESET}\n`);
487
+ if (sock && sock.readyState !== WebSocket.CLOSED) sock.terminate();
488
+ }, HEARTBEAT_TIMEOUT_MS);
489
+ }
490
+
481
491
  function scheduleReconnect(delaySec) {
482
492
  if (reconnecting) return;
483
493
  reconnecting = true;
@@ -530,6 +540,9 @@ async function handleDaemon(projectsDir) {
530
540
  }, PONG_TIMEOUT_MS);
531
541
  }
532
542
  }, PING_INTERVAL_MS);
543
+
544
+ // Start expecting server heartbeats (more reliable than WS pings through proxies)
545
+ resetHeartbeatTimer(sock);
533
546
  });
534
547
 
535
548
  sock.on("pong", () => {
@@ -540,6 +553,11 @@ async function handleDaemon(projectsDir) {
540
553
  let msg;
541
554
  try { msg = JSON.parse(raw.toString()); } catch { return; }
542
555
 
556
+ if (msg.type === "heartbeat") {
557
+ resetHeartbeatTimer(sock);
558
+ return;
559
+ }
560
+
543
561
  if (msg.type === "error") {
544
562
  process.stderr.write(`${RED}${PREFIX} Server: ${msg.error}${RESET}\n`);
545
563
  if (/unauthorized|auth|token/i.test(msg.error || "")) {
@@ -647,6 +665,7 @@ async function handleDaemon(projectsDir) {
647
665
  if (ws === sock) ws = null;
648
666
  if (pingTimer) { clearInterval(pingTimer); pingTimer = null; }
649
667
  if (pongTimer) { clearTimeout(pongTimer); pongTimer = null; }
668
+ if (heartbeatTimer) { clearTimeout(heartbeatTimer); heartbeatTimer = null; }
650
669
  process.stderr.write(`${DIM}${PREFIX} Disconnected — reconnecting in 2s${RESET}\n`);
651
670
  scheduleReconnect(2);
652
671
  });
@@ -663,6 +682,7 @@ async function handleDaemon(projectsDir) {
663
682
  process.stderr.write(`\n${DIM}${PREFIX} Shutting down${RESET}\n`);
664
683
  if (pingTimer) clearInterval(pingTimer);
665
684
  if (pongTimer) clearTimeout(pongTimer);
685
+ if (heartbeatTimer) clearTimeout(heartbeatTimer);
666
686
  if (reconnectTimer) clearTimeout(reconnectTimer);
667
687
  if (ws) ws.close();
668
688
  process.exit(0);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "orchestrating",
3
- "version": "0.1.36",
3
+ "version": "0.1.37",
4
4
  "description": "Stream terminal sessions to the orchestrat.ing dashboard",
5
5
  "type": "module",
6
6
  "bin": {