@chrysb/alphaclaw 0.9.0-beta.4 → 0.9.0-beta.6

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.
@@ -26,34 +26,28 @@ const resolveGatewayWsUrl = ({ openclawDir, gatewayPort }) => {
26
26
  return `${scheme}://127.0.0.1:${gatewayPort}`;
27
27
  };
28
28
 
29
- const sessions = new Map();
29
+ let activeSessionId = null;
30
30
  let activeTransport = null;
31
- const kSessionGraceMs = 15_000;
31
+ const kSseKeepAliveMs = 15_000;
32
32
 
33
- const closeSession = (sessionId) => {
34
- const t = sessions.get(sessionId);
35
- if (!t) return;
36
- sessions.delete(sessionId);
37
- if (activeTransport === t) activeTransport = null;
33
+ const closeActiveSession = () => {
34
+ if (!activeTransport) return;
35
+ const id = activeSessionId;
36
+ const t = activeTransport;
37
+ activeSessionId = null;
38
+ activeTransport = null;
38
39
  t.close().catch(() => {});
40
+ if (id) console.log(`[mcp] Closed session: ${id}`);
39
41
  };
40
42
 
41
- const closeAllSessions = () => {
42
- for (const [id] of sessions) closeSession(id);
43
- activeTransport = null;
44
- };
45
-
46
- const retireStaleSessions = (keepId) => {
47
- const staleIds = [...sessions.keys()].filter((id) => id !== keepId);
48
- if (staleIds.length === 0) return;
49
- setTimeout(() => {
50
- for (const id of staleIds) {
51
- if (sessions.has(id) && sessions.get(id) !== activeTransport) {
52
- console.log(`[mcp] Cleaning up stale session: ${id}`);
53
- closeSession(id);
54
- }
55
- }
56
- }, kSessionGraceMs);
43
+ const adoptSession = (sessionId, transport) => {
44
+ if (activeTransport && activeTransport !== transport) {
45
+ const prevId = activeSessionId;
46
+ activeTransport.close().catch(() => {});
47
+ console.log(`[mcp] Replaced session ${prevId} → ${sessionId}`);
48
+ }
49
+ activeSessionId = sessionId;
50
+ activeTransport = transport;
57
51
  };
58
52
 
59
53
  const registerMcpRoutes = ({
@@ -102,7 +96,7 @@ const registerMcpRoutes = ({
102
96
  });
103
97
 
104
98
  app.post("/api/mcp/stop", requireAuth, async (_req, res) => {
105
- closeAllSessions();
99
+ closeActiveSession();
106
100
  const result = stopMcpBridge();
107
101
  res.json(result);
108
102
  });
@@ -140,19 +134,24 @@ const registerMcpRoutes = ({
140
134
 
141
135
  if (req.method === "GET") {
142
136
  res.setHeader("X-Accel-Buffering", "no");
137
+ const keepAliveId = setInterval(() => {
138
+ if (res.headersSent && !res.writableEnded) {
139
+ res.write(": keepalive\n\n");
140
+ }
141
+ }, kSseKeepAliveMs);
142
+ res.on("close", () => clearInterval(keepAliveId));
143
143
  }
144
144
 
145
145
  const sessionId = req.headers["mcp-session-id"];
146
146
 
147
147
  // ── Existing session ───────────────────────────────────────
148
148
  if (sessionId) {
149
- const transport = sessions.get(sessionId);
150
- if (transport) {
149
+ if (sessionId === activeSessionId && activeTransport) {
151
150
  console.log(
152
- `[mcp] ${req.method} sessionId=${sessionId} → routed to transport (sessions=${sessions.size})`,
151
+ `[mcp] ${req.method} sessionId=${sessionId} → active session`,
153
152
  );
154
153
  try {
155
- await transport.handleRequest(req, res, req.body);
154
+ await activeTransport.handleRequest(req, res, req.body);
156
155
  } catch (err) {
157
156
  console.error(
158
157
  "[mcp] handleRequest error (existing session):",
@@ -164,7 +163,7 @@ const registerMcpRoutes = ({
164
163
  }
165
164
  } else {
166
165
  console.log(
167
- `[mcp] ${req.method} sessionId=${sessionId} → NOT FOUND (known=[${[...sessions.keys()].join(", ")}])`,
166
+ `[mcp] ${req.method} sessionId=${sessionId} → NOT FOUND (active=${activeSessionId || "none"})`,
168
167
  );
169
168
  res.status(404).json({
170
169
  jsonrpc: "2.0",
@@ -184,12 +183,8 @@ const registerMcpRoutes = ({
184
183
  sessionIdGenerator: () => randomUUID(),
185
184
  enableJsonResponse: true,
186
185
  onsessioninitialized: (newSessionId) => {
187
- sessions.set(newSessionId, transport);
188
- activeTransport = transport;
189
- retireStaleSessions(newSessionId);
190
- console.log(
191
- `[mcp] Session registered: ${newSessionId} (sessions=${sessions.size})`,
192
- );
186
+ adoptSession(newSessionId, transport);
187
+ console.log(`[mcp] Session adopted: ${newSessionId}`);
193
188
  },
194
189
  });
195
190
 
@@ -198,14 +193,11 @@ const registerMcpRoutes = ({
198
193
  };
199
194
 
200
195
  transport.onclose = () => {
201
- for (const [id, t] of sessions) {
202
- if (t === transport) {
203
- sessions.delete(id);
204
- break;
205
- }
196
+ if (activeTransport === transport) {
197
+ activeSessionId = null;
198
+ activeTransport = null;
206
199
  }
207
- if (activeTransport === transport) activeTransport = null;
208
- console.log(`[mcp] Transport closed (sessions=${sessions.size})`);
200
+ console.log(`[mcp] Transport closed`);
209
201
  };
210
202
 
211
203
  transport.onerror = (err) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chrysb/alphaclaw",
3
- "version": "0.9.0-beta.4",
3
+ "version": "0.9.0-beta.6",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },