patchcord 0.5.1 → 0.5.3

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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "patchcord",
3
3
  "description": "Cross-machine agent messaging with push delivery. Messages from other agents arrive as native channel notifications.",
4
- "version": "0.5.1",
4
+ "version": "0.5.3",
5
5
  "author": {
6
6
  "name": "ppravdin"
7
7
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "patchcord",
3
- "version": "0.5.1",
3
+ "version": "0.5.3",
4
4
  "description": "Cross-machine agent messaging for Claude Code and Codex",
5
5
  "author": "ppravdin",
6
6
  "license": "MIT",
@@ -17,6 +17,17 @@ const JWT_REFRESH_SAFETY_MARGIN_SEC = 120;
17
17
  const HEARTBEAT_INTERVAL_MS = 25_000;
18
18
  const RECONNECT_BACKOFF_MS = [1000, 2000, 4000, 8000, 15_000, 30_000];
19
19
 
20
+ // Guarantee a terminal stderr line on any unhandled failure so the agent
21
+ // reading Monitor's output file always sees WHY the process died.
22
+ process.on("uncaughtException", (err) => {
23
+ process.stderr.write(`subscribe: fatal: uncaught: ${err?.stack || err?.message || err}\n`);
24
+ process.exit(1);
25
+ });
26
+ process.on("unhandledRejection", (err) => {
27
+ process.stderr.write(`subscribe: fatal: unhandled rejection: ${err?.stack || err?.message || err}\n`);
28
+ process.exit(1);
29
+ });
30
+
20
31
  function die(msg, code = 1) {
21
32
  process.stderr.write(msg + "\n");
22
33
  process.exit(code);
@@ -68,13 +68,19 @@ then the script is at
68
68
  description: "patchcord realtime listener (<agent>@<ns>)",
69
69
  persistent: true,
70
70
  timeout_ms: 3600000,
71
- command: "exec node \"<absolute-path-to-subscribe.mjs>\" 2>&1 | grep --line-buffered -E '^PATCHCORD:|^subscribe: (fatal|ws error|token|already|connected|reconnecting|cwd|agent)'"
71
+ command: "exec node \"<absolute-path-to-subscribe.mjs>\""
72
72
  )
73
73
  ```
74
- The `grep` filter is intentional it surfaces the signal lines
75
- (`PATCHCORD:` arrivals, connect/disconnect, errors) and drops the
76
- noise. The filter catches every terminal/state-change event, so the
77
- Monitor won't silently miss a crash.
74
+ No `2>&1`, no grep filter. By construction the script only writes
75
+ `PATCHCORD: ...` lines to stdout. Everything else — `connected`,
76
+ `token refreshed`, startup diagnostics, errors goes to stderr,
77
+ which Monitor captures into its output file but does NOT fire as
78
+ notifications. So there's nothing to filter and no way to get the
79
+ filter wrong.
80
+
81
+ Crash detection is handled automatically by Monitor itself: when the
82
+ process exits, Monitor emits a built-in "stream ended" task
83
+ notification with the output file path and exit code.
78
84
 
79
85
  6. **Tell the user one short line:**
80
86
  "Patchcord listener active — I'll pick up new messages as they arrive."
@@ -97,18 +103,45 @@ There is no `/patchcord:unsubscribe` command. Tell the user either:
97
103
  - Run `kill $(cat /tmp/patchcord_subscribe_<namespace>_<agent>.pid)` in
98
104
  a terminal.
99
105
 
100
- # If it fails to start
106
+ # If the Monitor stream ends — STRICT PROTOCOL
107
+
108
+ The stream-end task notification includes the path to Monitor's output
109
+ file. Do exactly this, in this order:
110
+
111
+ 1. Read that output file using the Read tool.
112
+ 2. Look at the last ~15 lines for a line matching one of the known
113
+ failure strings below. There will always be at least one terminal
114
+ error line — the script's error handlers guarantee it.
115
+ 3. Report the specific cause to the user in one short sentence.
116
+ 4. STOP.
117
+
118
+ **Forbidden on failure — do not do any of these:**
119
+ - Do NOT run `pgrep`, `ps`, `kill`, `pkill`, `killall`, or any command
120
+ that targets PIDs or process names.
121
+ - Do NOT modify, delete, or write to the pidfile yourself. The script
122
+ manages it; it's already cleaned up by the time Monitor emits the
123
+ stream-end event.
124
+ - Do NOT spawn another Monitor or another `node subscribe.mjs`. One
125
+ failure means something is wrong with the config or environment;
126
+ respawning will not fix it and will make things worse.
127
+ - Do NOT search for orphaned processes or try to "clean up" state.
101
128
 
102
- Stderr shows the exact cause. Report it to the user verbatim and stop
103
- — do not loop or retry:
129
+ Concrete failure strings you may see and what they mean:
104
130
 
105
131
  - `no .mcp.json in <cwd>` — session is not in a patchcord project dir.
106
- - `token rejected` bearer in `.mcp.json` is bad; regenerate from the
107
- dashboard.
108
- - `server not configured for realtime` — server hasn't had
109
- `SUPABASE_JWT_SECRET` / `SUPABASE_ANON_KEY` set. Self-hosted without
110
- Supabase does not support this feature yet.
111
- - `namespace not owned` — the token's namespace lost its owner row;
112
- regenerate from the dashboard.
113
- - `already running (pid N)` pidfile guard tripped. Another subscribe
114
- is active. Report and stop.
132
+ Tell the user which directory to `cd` into.
133
+ - `ticket: token rejected (HTTP 401|403)` — bearer in `.mcp.json` is
134
+ bad; regenerate from the dashboard.
135
+ - `ticket: server not configured for realtime` the patchcord server
136
+ hasn't had `SUPABASE_JWT_SECRET` / `SUPABASE_ANON_KEY` set. This is
137
+ a cloud-only feature.
138
+ - `ticket: namespace not owned — regenerate your token` — the token's
139
+ namespace lost its owner row; regenerate from the dashboard.
140
+ - `already running (pid N)` (exit code 2) — pidfile guard tripped,
141
+ another listener is active. Report and stop. Do NOT kill the other
142
+ listener to make room.
143
+ - `subscribe: fatal: ...` — unhandled error. Show the user the line
144
+ verbatim, stop.
145
+
146
+ If the process exited cleanly (exit 0) with no error line, the user
147
+ closed the session or killed the process. Nothing to do.