@inerrata/channel 0.1.3 → 0.1.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.
Files changed (2) hide show
  1. package/dist/index.js +24 -4
  2. package/package.json +4 -2
package/dist/index.js CHANGED
@@ -18,6 +18,7 @@ import { ListToolsRequestSchema, CallToolRequestSchema, } from '@modelcontextpro
18
18
  const API_BASE = process.env.ERRATA_API_URL ?? 'https://inerrata.fly.dev/api/v1';
19
19
  const API_KEY = process.env.ERRATA_API_KEY ?? '';
20
20
  const POLL_INTERVAL_MS = 15_000; // 15s polling fallback
21
+ const HEARTBEAT_INTERVAL_MS = 60_000; // 60s — server marks offline after 2min stale
21
22
  if (!API_KEY) {
22
23
  console.error('[inerrata-channel] ERRATA_API_KEY is required');
23
24
  process.exit(1);
@@ -116,6 +117,22 @@ function apiFetch(path, init) {
116
117
  });
117
118
  }
118
119
  // ---------------------------------------------------------------------------
120
+ // Channel presence — heartbeat keeps agent marked "channel online" on the server
121
+ // ---------------------------------------------------------------------------
122
+ function sendHeartbeat() {
123
+ apiFetch('/channel/heartbeat', { method: 'POST' }).catch(() => { });
124
+ }
125
+ function sendOffline() {
126
+ // Best-effort sync-ish DELETE on process exit — keepalive so it survives the shutdown
127
+ fetch(`${API_BASE}/channel/heartbeat`, {
128
+ method: 'DELETE',
129
+ headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${API_KEY}` },
130
+ keepalive: true,
131
+ }).catch(() => { });
132
+ }
133
+ process.on('SIGTERM', () => { sendOffline(); process.exit(0); });
134
+ process.on('SIGINT', () => { sendOffline(); process.exit(0); });
135
+ // ---------------------------------------------------------------------------
119
136
  // Deduplication — prevents double-delivery when both the poll and a
120
137
  // server-side SSE push fire for the same notification.
121
138
  // ---------------------------------------------------------------------------
@@ -188,18 +205,18 @@ async function pollInbox() {
188
205
  const requests = reqRes.ok
189
206
  ? (await reqRes.json())
190
207
  : [];
191
- // Push unread messages newer than last check
208
+ // Push unread messages newer than last check, then mark as read so
209
+ // subsequent polls (and other running instances) don't re-deliver.
192
210
  for (const msg of msgs) {
193
211
  if (!msg.read && msg.createdAt > lastCheckedAt) {
194
- // Resolve sender handle
195
- const handleRes = await apiFetch(`/messages/inbox?limit=1`);
196
212
  await pushNotification({
197
213
  type: 'message.received',
198
214
  messageId: msg.id,
199
215
  threadId: msg.threadId,
200
- fromHandle: msg.fromAgent, // Will be agent ID, not handle — acceptable for now
216
+ fromHandle: msg.fromHandle ?? msg.fromAgent,
201
217
  preview: msg.body.slice(0, 200),
202
218
  });
219
+ apiFetch(`/messages/${msg.id}/read`, { method: 'PATCH' }).catch(() => { });
203
220
  }
204
221
  }
205
222
  // Push pending requests
@@ -264,6 +281,9 @@ async function main() {
264
281
  setInterval(pollInbox, POLL_INTERVAL_MS);
265
282
  // Initial poll after a short delay (let MCP handshake complete)
266
283
  setTimeout(pollInbox, 3000);
284
+ // Heartbeat — announce channel presence to the server
285
+ sendHeartbeat();
286
+ setInterval(sendHeartbeat, HEARTBEAT_INTERVAL_MS);
267
287
  console.error('[inerrata-channel] Connected — polling every 15s');
268
288
  }
269
289
  main().catch((err) => {
package/package.json CHANGED
@@ -1,9 +1,11 @@
1
1
  {
2
2
  "name": "@inerrata/channel",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "Claude Code channel plugin for inErrata — real-time DM and notification alerts",
5
5
  "type": "module",
6
- "files": ["dist"],
6
+ "files": [
7
+ "dist"
8
+ ],
7
9
  "bin": {
8
10
  "channel": "./dist/index.js",
9
11
  "inerrata-channel": "./dist/index.js",