rogerthat 1.24.4 → 1.24.5

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.
@@ -299,6 +299,30 @@ function formatLine(args, msg, savedPaths) {
299
299
  }
300
300
  return JSON.stringify(msg);
301
301
  }
302
+ /** Append a raw status line to the inbox file (if --inbox is set). Used for the
303
+ * relay's own health signals — connect / reconnect notices — so an agent's
304
+ * Monitor (a `tail -F` of the inbox) sees a confirmation that the listener is
305
+ * actually attached, instead of having to inspect `ps`/`ss` to tell a healthy
306
+ * relay from a crashed one. Written even under --quiet: quiet silences stdout,
307
+ * not the inbox's own heartbeat. The `⟲` marker keeps it greppable/skippable.
308
+ * Best-effort — a failed append must never take the relay down. */
309
+ function writeInboxStatus(args, text) {
310
+ if (!args.inbox)
311
+ return;
312
+ // Only in text mode. JSONL consumers parse each line (or the whole file) as
313
+ // JSON, so a bare `⟲ …` line would break them — they get message objects only.
314
+ if (args.format !== "text")
315
+ return;
316
+ try {
317
+ const dir = dirname(args.inbox);
318
+ if (dir && !existsSync(dir))
319
+ mkdirSync(dir, { recursive: true });
320
+ appendFileSync(args.inbox, `⟲ [listen-here] ${text}\n`, { mode: 0o600 });
321
+ }
322
+ catch {
323
+ /* never let a health-line write kill the relay */
324
+ }
325
+ }
302
326
  async function dispatch(args, msg) {
303
327
  // --min-priority filter: drop messages below the threshold entirely (no
304
328
  // inbox write, no hook spawn, no stdout). Missing priority counts as
@@ -404,6 +428,11 @@ async function runOneConnection(args, sinceCursor, abortSignal) {
404
428
  res.resume();
405
429
  return { lastId: sinceCursor, reason: "ended", statusError: status };
406
430
  }
431
+ // SSE handshake accepted → the relay is genuinely attached and will receive
432
+ // phone messages from here on. Announce it in the inbox so the agent's Monitor
433
+ // confirms "listening" without a manual selftest, and the operator's phone
434
+ // sees a peer come online. (See writeInboxStatus.)
435
+ writeInboxStatus(args, `● connected — listening on ${args.channel} as session ${(args.session ?? "").slice(0, 8)}…`);
407
436
  let lastId = sinceCursor;
408
437
  // If the operator aborts mid-stream, destroy() the response to unblock the
409
438
  // for-await on it.
@@ -547,12 +576,35 @@ export async function runListenHere(argv) {
547
576
  return 0;
548
577
  if (result.statusError !== undefined) {
549
578
  const status = result.statusError;
550
- // 4xx (except 408/429) are permanent bail rather than spin.
579
+ // 4xx (except 408/429) usually mean our SESSION is gone, not that our
580
+ // creds are bad: a server restart wipes in-memory sessions AND tombstones,
581
+ // so the stream returns 400 not_joined (or 410) for the session we held.
582
+ // Our join creds are durable, so the robust move is to mint a FRESH
583
+ // session and resume — a restart should be transparent, not fatal. Only
584
+ // bail if we have no identity to re-join with, or the re-join itself fails
585
+ // (that's a genuinely bad token / identity_key).
551
586
  if (status >= 400 && status < 500 && status !== 408 && status !== 429) {
552
- console.error(`[listen-here] server returned ${status} auth or session invalid; exiting`);
553
- return 1;
587
+ // Re-join needs the identity_key (joinForSession auto-joins with it). If
588
+ // the listener was started with an explicit --session and no
589
+ // --identity-key, we have nothing to re-join with → bail as before.
590
+ if (!args.identityKey) {
591
+ console.error(`[listen-here] server returned ${status} and no --identity-key to re-join with; exiting`);
592
+ return 1;
593
+ }
594
+ try {
595
+ args.session = await joinForSession(args);
596
+ if (!args.quiet) {
597
+ console.error(`[listen-here] re-joined after ${status} as session ${args.session.slice(0, 8)}… — resuming`);
598
+ }
599
+ }
600
+ catch (err) {
601
+ console.error(`[listen-here] re-join after ${status} failed (${err.message}); exiting`);
602
+ return 1;
603
+ }
604
+ }
605
+ else {
606
+ console.error(`[listen-here] server returned ${status} — will retry`);
554
607
  }
555
- console.error(`[listen-here] server returned ${status} — will retry`);
556
608
  }
557
609
  // Connection closed cleanly or with retryable error. Backoff then reconnect.
558
610
  if (ac.signal.aborted)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rogerthat",
3
- "version": "1.24.4",
3
+ "version": "1.24.5",
4
4
  "mcpName": "io.github.opcastil11/rogerthat",
5
5
  "description": "Real-time chat for AI agents. A walkie-talkie hub that lets two or more agents — Claude Code, Cursor, Cline, Claude Desktop, Codex — on different machines send messages to each other over MCP or plain REST. Hosted at rogerthat.chat or self-hosted with `npx rogerthat`.",
6
6
  "keywords": [