@manuelfedele/postino 0.3.0 → 0.3.1

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/dist/valkey.js CHANGED
@@ -5,12 +5,16 @@ export const valkey = new Redis(config.valkeyUrl, {
5
5
  lazyConnect: true,
6
6
  maxRetriesPerRequest: 3,
7
7
  });
8
- valkey.on("error", () => { }); // Handled in connect()
8
+ valkey.on("error", (err) => {
9
+ process.stderr.write(`postino valkey: ${err.message}\n`);
10
+ });
9
11
  export const valkeySub = new Redis(config.valkeyUrl, {
10
12
  lazyConnect: true,
11
13
  maxRetriesPerRequest: 3,
12
14
  });
13
- valkeySub.on("error", () => { }); // Handled in connect()
15
+ valkeySub.on("error", (err) => {
16
+ process.stderr.write(`postino valkeySub: ${err.message}\n`);
17
+ });
14
18
  const prefix = config.keyPrefix;
15
19
  export const keys = {
16
20
  inbox: (agent) => `${prefix}inbox:${agent}`,
@@ -57,31 +61,41 @@ export async function registerAgent(name) {
57
61
  }
58
62
  }, HEARTBEAT_INTERVAL);
59
63
  }
64
+ async function removeAgentIfEmpty(name) {
65
+ const inboxLen = await valkey.llen(keys.inbox(name));
66
+ if (inboxLen > 0)
67
+ return false;
68
+ const pipe = valkey.pipeline();
69
+ pipe.srem(keys.agents(), name);
70
+ pipe.del(keys.broadcastCursor(name));
71
+ await pipe.exec();
72
+ return true;
73
+ }
60
74
  export async function deregisterAgent(name) {
61
75
  if (heartbeatTimer)
62
76
  clearInterval(heartbeatTimer);
63
77
  await valkey.del(keys.agentInfo(name));
64
- // Auto-cleanup: remove from agents set if inbox is empty
65
- const inboxLen = await valkey.llen(keys.inbox(name));
66
- if (inboxLen === 0) {
67
- await valkey.srem(keys.agents(), name);
68
- await valkey.del(keys.broadcastCursor(name));
69
- }
78
+ await removeAgentIfEmpty(name);
70
79
  await publishEvent("agent_offline", { agent: name });
71
80
  }
72
81
  export async function cleanupStaleAgents() {
73
82
  const allAgents = await valkey.smembers(keys.agents());
74
- const removed = [];
83
+ if (allAgents.length === 0)
84
+ return [];
85
+ // Batch check online status for all agents
86
+ const pipe = valkey.pipeline();
75
87
  for (const name of allAgents) {
76
- const online = await valkey.exists(keys.agentInfo(name));
88
+ pipe.exists(keys.agentInfo(name));
89
+ }
90
+ const results = await pipe.exec();
91
+ const removed = [];
92
+ for (let i = 0; i < allAgents.length; i++) {
93
+ const online = results?.[i]?.[1];
77
94
  if (online)
78
95
  continue;
79
- const inboxLen = await valkey.llen(keys.inbox(name));
80
- if (inboxLen === 0) {
81
- await valkey.srem(keys.agents(), name);
82
- await valkey.del(keys.broadcastCursor(name));
83
- removed.push(name);
84
- }
96
+ const wasRemoved = await removeAgentIfEmpty(allAgents[i]);
97
+ if (wasRemoved)
98
+ removed.push(allAgents[i]);
85
99
  }
86
100
  return removed;
87
101
  }
package/dist/web/api.js CHANGED
@@ -8,7 +8,7 @@ import { loadConfig } from "../types.js";
8
8
  const __dirname = dirname(fileURLToPath(import.meta.url));
9
9
  function readVersion() {
10
10
  try {
11
- const pkgPath = join(__dirname, "..", "package.json");
11
+ const pkgPath = join(__dirname, "..", "..", "package.json");
12
12
  const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
13
13
  return pkg.version || "0.0.0";
14
14
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@manuelfedele/postino",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "Inter-agent messaging and broadcast system for Claude Code",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",