patchcord 0.5.62 → 0.5.64

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "patchcord",
3
- "version": "0.5.62",
3
+ "version": "0.5.64",
4
4
  "description": "Cross-machine agent messaging for Claude Code and Codex",
5
5
  "author": "ppravdin",
6
6
  "license": "MIT",
@@ -221,6 +221,25 @@ async function run() {
221
221
  process.exit(0);
222
222
  });
223
223
 
224
+ // Exit when our stdout consumer (Monitor) dies. Node ignores SIGPIPE by
225
+ // default, so the EPIPE error surfaces on the stream instead. Without this
226
+ // the process orphans after the session ends and the pidfile guard blocks
227
+ // the next session from starting a fresh listener.
228
+ process.stdout.on("error", (err) => {
229
+ if (err.code === "EPIPE") {
230
+ cleanup();
231
+ process.exit(0);
232
+ }
233
+ });
234
+
235
+ // Keepalive: write a line every 30 s so EPIPE is detected promptly even
236
+ // when no messages arrive. The Monitor command pipes through
237
+ // `grep --line-buffered '^PATCHCORD:'` which drops these lines silently.
238
+ const _keepaliveTimer = setInterval(() => {
239
+ try { process.stdout.write("HEARTBEAT\n"); } catch (_) {}
240
+ }, 30_000);
241
+ process.on("exit", () => clearInterval(_keepaliveTimer));
242
+
224
243
  logErr(`subscribe: agent=${ticket.agent_id} namespaces=${ticket.namespace_ids.join(",")}`);
225
244
 
226
245
  let backoffIdx = 0;
@@ -432,8 +451,10 @@ function runOnce(ticket, baseUrl, token, refreshTicket) {
432
451
  return;
433
452
  }
434
453
  // Successful subscribe ack — log once for confirmation, then quiet.
454
+ // Replace Supabase's "Subscribed to PostgreSQL" with a user-facing label.
435
455
  if (status === "ok" && message) {
436
- logErr(`subscribe: system ok on ${frame.topic}: ${message}`);
456
+ const displayMsg = message.replace(/PostgreSQL/g, "Patchcord");
457
+ logErr(`subscribe: system ok on ${frame.topic}: ${displayMsg}`);
437
458
  }
438
459
  return;
439
460
  }
@@ -20,10 +20,12 @@ User invoked /patchcord:subscribe — do NOT substitute `wait_for_message()`. Sp
20
20
  description: "patchcord realtime listener",
21
21
  persistent: true,
22
22
  timeout_ms: 3600000,
23
- command: "exec patchcord subscribe"
23
+ command: "patchcord subscribe | grep --line-buffered '^PATCHCORD:'; exit ${PIPESTATUS[0]}"
24
24
  )
25
25
  ```
26
26
 
27
+ The grep filter drops internal `HEARTBEAT` keepalive lines (written every 30 s to detect a dead pipe) — only `PATCHCORD:` lines fire notifications. `${PIPESTATUS[0]}` preserves subscribe's exit code through the pipe.
28
+
27
29
  `subscribe.mjs` handles its own pidfile guard — if another listener is already active for this agent it exits with code 2 and stderr `already running (pid N)`. Monitor catches the stream-end event; read the output file and report.
28
30
 
29
31
  3. **Tell the user one line:** *"Patchcord listener active — I'll pick up new messages as they arrive."*
@@ -57,4 +59,4 @@ Report the cause in one sentence. STOP.
57
59
 
58
60
  **Forbidden on failure:** no `pgrep`/`ps`/`kill`/`pkill`/`killall`, no pidfile writes, no respawning. The script manages pidfile cleanup itself; respawning will not fix a config problem.
59
61
 
60
- Clean exit (code 0) with no error line = the user closed the session or killed the process. Nothing to do.
62
+ No matching error pattern = the listener exited cleanly (session ended, user killed it, or EPIPE detected). Nothing to do.