orchestrating 0.1.25 → 0.1.26

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 +40 -22
  2. package/package.json +1 -1
package/bin/orch CHANGED
@@ -221,6 +221,18 @@ async function handleDaemon() {
221
221
  process.stderr.write(`${GREEN}${PREFIX} Listening for remote sessions as "${hostname}"${RESET}\n`);
222
222
  process.stderr.write(`${DIM}${PREFIX} Tip: Use 'nohup orch daemon &' to run in background${RESET}\n`);
223
223
 
224
+ let reconnecting = false;
225
+
226
+ function scheduleReconnect(delaySec) {
227
+ if (reconnecting) return;
228
+ reconnecting = true;
229
+ if (reconnectTimer) clearTimeout(reconnectTimer);
230
+ reconnectTimer = setTimeout(() => {
231
+ reconnecting = false;
232
+ connect();
233
+ }, delaySec * 1000);
234
+ }
235
+
224
236
  async function connect() {
225
237
  // Refresh token if needed before connecting
226
238
  if (isTokenExpired()) {
@@ -230,15 +242,20 @@ async function handleDaemon() {
230
242
  process.stderr.write(`${DIM}${PREFIX} Token refreshed${RESET}\n`);
231
243
  } else {
232
244
  process.stderr.write(`${RED}${PREFIX} Token refresh failed — retrying in 10s${RESET}\n`);
233
- reconnectTimer = setTimeout(connect, 10_000);
245
+ scheduleReconnect(10);
234
246
  return;
235
247
  }
236
248
  }
237
249
 
238
- ws = new WebSocket(serverUrl);
250
+ if (ws) {
251
+ ws.removeAllListeners();
252
+ ws.close();
253
+ }
254
+ const sock = new WebSocket(serverUrl);
255
+ ws = sock;
239
256
 
240
- ws.on("open", () => {
241
- ws.send(JSON.stringify({
257
+ sock.on("open", () => {
258
+ sock.send(JSON.stringify({
242
259
  type: "register_daemon",
243
260
  token,
244
261
  hostname,
@@ -250,30 +267,31 @@ async function handleDaemon() {
250
267
  if (pingTimer) clearInterval(pingTimer);
251
268
  if (pongTimer) clearTimeout(pongTimer);
252
269
  pingTimer = setInterval(() => {
253
- if (ws && ws.readyState === WebSocket.OPEN) {
254
- ws.ping();
270
+ if (sock.readyState === WebSocket.OPEN) {
271
+ sock.ping();
255
272
  pongTimer = setTimeout(() => {
256
- if (ws) ws.terminate();
273
+ sock.terminate();
257
274
  }, PONG_TIMEOUT_MS);
258
275
  }
259
276
  }, PING_INTERVAL_MS);
260
277
  });
261
278
 
262
- ws.on("pong", () => {
279
+ sock.on("pong", () => {
263
280
  if (pongTimer) { clearTimeout(pongTimer); pongTimer = null; }
264
281
  });
265
282
 
266
- ws.on("message", (raw) => {
283
+ sock.on("message", (raw) => {
267
284
  let msg;
268
285
  try { msg = JSON.parse(raw.toString()); } catch { return; }
269
286
 
270
287
  if (msg.type === "error") {
271
288
  process.stderr.write(`${RED}${PREFIX} Server: ${msg.error}${RESET}\n`);
272
289
  if (/unauthorized|auth|token/i.test(msg.error || "")) {
290
+ // Don't reconnect from here — let the close handler do it after refreshing
273
291
  refreshAuthToken().then((refreshed) => {
274
292
  if (refreshed) {
275
293
  token = refreshed;
276
- connect();
294
+ process.stderr.write(`${DIM}${PREFIX} Token refreshed — will reconnect${RESET}\n`);
277
295
  } else {
278
296
  process.stderr.write(`${RED}${PREFIX} Auth failed. Run 'orch login'.${RESET}\n`);
279
297
  process.exit(1);
@@ -284,15 +302,15 @@ async function handleDaemon() {
284
302
  }
285
303
 
286
304
  if (msg.type === "start_session") {
287
- const { requestId, command, args, cwd, label, yolo } = msg;
288
- const cmdStr = [command, ...args].join(" ");
305
+ const { requestId, command: cmd, args: cmdArgs, cwd, label: lbl, yolo } = msg;
306
+ const cmdStr = [cmd, ...cmdArgs].join(" ");
289
307
  process.stderr.write(`${GREEN}${PREFIX} Spawning: ${cmdStr}${RESET}\n`);
290
308
 
291
309
  try {
292
310
  const childArgs = [];
293
- if (label) childArgs.push("-l", label);
311
+ if (lbl) childArgs.push("-l", lbl);
294
312
  if (yolo) childArgs.push("-y");
295
- childArgs.push(command, ...args);
313
+ childArgs.push(cmd, ...cmdArgs);
296
314
 
297
315
  const child = spawn(process.execPath, [orchPath, ...childArgs], {
298
316
  stdio: "ignore",
@@ -302,8 +320,8 @@ async function handleDaemon() {
302
320
  });
303
321
  child.unref();
304
322
 
305
- if (ws && ws.readyState === WebSocket.OPEN) {
306
- ws.send(JSON.stringify({
323
+ if (sock.readyState === WebSocket.OPEN) {
324
+ sock.send(JSON.stringify({
307
325
  type: "session_started",
308
326
  requestId,
309
327
  success: true,
@@ -311,8 +329,8 @@ async function handleDaemon() {
311
329
  }
312
330
  } catch (err) {
313
331
  process.stderr.write(`${RED}${PREFIX} Spawn failed: ${err.message}${RESET}\n`);
314
- if (ws && ws.readyState === WebSocket.OPEN) {
315
- ws.send(JSON.stringify({
332
+ if (sock.readyState === WebSocket.OPEN) {
333
+ sock.send(JSON.stringify({
316
334
  type: "session_started",
317
335
  requestId,
318
336
  success: false,
@@ -323,15 +341,15 @@ async function handleDaemon() {
323
341
  }
324
342
  });
325
343
 
326
- ws.on("close", () => {
327
- ws = null;
344
+ sock.on("close", () => {
345
+ if (ws === sock) ws = null;
328
346
  if (pingTimer) { clearInterval(pingTimer); pingTimer = null; }
329
347
  if (pongTimer) { clearTimeout(pongTimer); pongTimer = null; }
330
348
  process.stderr.write(`${DIM}${PREFIX} Disconnected — reconnecting in 2s${RESET}\n`);
331
- reconnectTimer = setTimeout(connect, 2000);
349
+ scheduleReconnect(2);
332
350
  });
333
351
 
334
- ws.on("error", () => {
352
+ sock.on("error", () => {
335
353
  // Will trigger close
336
354
  });
337
355
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "orchestrating",
3
- "version": "0.1.25",
3
+ "version": "0.1.26",
4
4
  "description": "Stream terminal sessions to the orchestrat.ing dashboard",
5
5
  "type": "module",
6
6
  "bin": {