@hirohsu/user-web-feedback 2.8.11 → 2.8.13
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/cli.cjs +61 -17
- package/dist/index.cjs +61 -17
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -92987,6 +92987,9 @@ var WebServer = class {
|
|
|
92987
92987
|
sseTransports = /* @__PURE__ */ new Map();
|
|
92988
92988
|
sseTransportsList = [];
|
|
92989
92989
|
pendingDeliveryCache = /* @__PURE__ */ new Map();
|
|
92990
|
+
streamableHttpTransports = /* @__PURE__ */ new Map();
|
|
92991
|
+
streamableHttpSseActive = /* @__PURE__ */ new Map();
|
|
92992
|
+
activeSessionPromises = /* @__PURE__ */ new Map();
|
|
92990
92993
|
dbInitialized = false;
|
|
92991
92994
|
selfProbeService;
|
|
92992
92995
|
/**
|
|
@@ -94986,6 +94989,17 @@ var WebServer = class {
|
|
|
94986
94989
|
/**
|
|
94987
94990
|
* 處理回饋提交
|
|
94988
94991
|
*/
|
|
94992
|
+
async waitForActiveConnection(maxRounds = 5, intervalMs = 5e3) {
|
|
94993
|
+
for (let round = 1; round <= maxRounds; round++) {
|
|
94994
|
+
const hasStreamableSSE = [...this.streamableHttpSseActive.values()].some((v) => v);
|
|
94995
|
+
const hasSSETransport = this.sseTransportsList.length > 0;
|
|
94996
|
+
if (hasStreamableSSE || hasSSETransport) return true;
|
|
94997
|
+
logger.warn(`[\u9023\u7DDA\u7B49\u5F85] \u7B2C ${round}/${maxRounds} \u8F2A\uFF1A\u76EE\u524D\u7121\u6D3B\u8E8D MCP Client \u9023\u7DDA\uFF0C${intervalMs / 1e3}s \u5F8C\u91CD\u8A66...`);
|
|
94998
|
+
await new Promise((r) => setTimeout(r, intervalMs));
|
|
94999
|
+
}
|
|
95000
|
+
logger.error(`[\u9023\u7DDA\u7B49\u5F85] \u5DF2\u5617\u8A66 ${maxRounds} \u8F2A\u4ECD\u7121\u6D3B\u8E8D\u9023\u7DDA\uFF0C\u5C07\u76F4\u63A5\u9001\u51FA\uFF08\u5FEB\u53D6\u6A5F\u5236\u4F5C\u70BA\u4FDD\u8B77\uFF09`);
|
|
95001
|
+
return false;
|
|
95002
|
+
}
|
|
94989
95003
|
async handleFeedbackSubmission(socket, feedbackData) {
|
|
94990
95004
|
const session = this.sessionStorage.getSession(feedbackData.sessionId);
|
|
94991
95005
|
if (!session) {
|
|
@@ -95044,6 +95058,10 @@ var WebServer = class {
|
|
|
95044
95058
|
const projectId = session.projectId;
|
|
95045
95059
|
setTimeout(() => this.pendingDeliveryCache.delete(projectId), 6e4);
|
|
95046
95060
|
}
|
|
95061
|
+
const hasConnection = await this.waitForActiveConnection();
|
|
95062
|
+
if (hasConnection) {
|
|
95063
|
+
logger.info("[\u9023\u7DDA\u76E3\u63A7] \u9023\u7DDA\u78BA\u8A8D\uFF0C\u6B63\u5728\u9001\u51FA\u56DE\u8986\u81F3 MCP Client...");
|
|
95064
|
+
}
|
|
95047
95065
|
session.resolve(session.feedback);
|
|
95048
95066
|
const sessionIdToDelete = feedbackData.sessionId;
|
|
95049
95067
|
setTimeout(() => this.sessionStorage.deleteSession(sessionIdToDelete), 5e3);
|
|
@@ -95131,8 +95149,11 @@ var WebServer = class {
|
|
|
95131
95149
|
async collectFeedback(workSummary, timeoutSeconds, projectName, projectPath) {
|
|
95132
95150
|
const sessionId = this.generateSessionId();
|
|
95133
95151
|
const project = projectName ? projectManager.getOrCreateProject(projectName, projectPath) : projectManager.getDefaultProject();
|
|
95134
|
-
|
|
95135
|
-
|
|
95152
|
+
const existingPromise = this.activeSessionPromises.get(project.id);
|
|
95153
|
+
if (existingPromise) {
|
|
95154
|
+
logger.warn(`[\u91CD\u8907\u8ACB\u6C42] \u5C08\u6848 "${project.name}" \u5DF2\u6709\u9032\u884C\u4E2D\u7684\u56DE\u994B\u6703\u8A71\uFF0C\u91CD\u7528\u73FE\u6709\u8ACB\u6C42\uFF08\u9632\u6B62\u91CD\u8907\u5F48\u7A97\uFF09`);
|
|
95155
|
+
return existingPromise;
|
|
95156
|
+
}
|
|
95136
95157
|
const pendingEntry = this.pendingDeliveryCache.get(project.id);
|
|
95137
95158
|
if (pendingEntry && pendingEntry.expiresAt > Date.now()) {
|
|
95138
95159
|
const prevSession = this.sessionStorage.getSession(pendingEntry.result.sessionId);
|
|
@@ -95142,7 +95163,9 @@ var WebServer = class {
|
|
|
95142
95163
|
return pendingEntry.result;
|
|
95143
95164
|
}
|
|
95144
95165
|
}
|
|
95145
|
-
|
|
95166
|
+
logger.info(`\u5EFA\u7ACB\u56DE\u994B\u6703\u8A71: ${sessionId}, \u903E\u6642: ${timeoutSeconds}\u79D2, \u5C08\u6848: ${project.name} (${project.id})`);
|
|
95167
|
+
const feedbackUrl = this.generateFeedbackUrl(sessionId);
|
|
95168
|
+
const sessionPromise = new Promise((resolve2, reject) => {
|
|
95146
95169
|
const session = {
|
|
95147
95170
|
workSummary,
|
|
95148
95171
|
feedback: [],
|
|
@@ -95172,6 +95195,11 @@ var WebServer = class {
|
|
|
95172
95195
|
});
|
|
95173
95196
|
}
|
|
95174
95197
|
});
|
|
95198
|
+
this.activeSessionPromises.set(project.id, sessionPromise);
|
|
95199
|
+
void sessionPromise.finally(() => {
|
|
95200
|
+
this.activeSessionPromises.delete(project.id);
|
|
95201
|
+
});
|
|
95202
|
+
return sessionPromise;
|
|
95175
95203
|
}
|
|
95176
95204
|
emitDashboardSessionCreated(projectId, sessionId, projectName, workSummary) {
|
|
95177
95205
|
if (this.io) {
|
|
@@ -95439,7 +95467,6 @@ var WebServer = class {
|
|
|
95439
95467
|
*/
|
|
95440
95468
|
async setupStreamableHTTPEndpoints() {
|
|
95441
95469
|
const { StreamableHTTPServerTransport: StreamableHTTPServerTransport2 } = await Promise.resolve().then(() => (init_streamableHttp2(), streamableHttp_exports2));
|
|
95442
|
-
const streamableTransports = /* @__PURE__ */ new Map();
|
|
95443
95470
|
this.app.post("/mcp", import_express.default.json(), async (req, res) => {
|
|
95444
95471
|
logger.debug("\u6536\u5230 Streamable HTTP \u8ACB\u6C42:", JSON.stringify(req.body));
|
|
95445
95472
|
if (!this.mcpServerRef) {
|
|
@@ -95450,7 +95477,7 @@ var WebServer = class {
|
|
|
95450
95477
|
req.socket?.setKeepAlive(true, 3e4);
|
|
95451
95478
|
req.on("close", () => {
|
|
95452
95479
|
if (!res.writableEnded) {
|
|
95453
|
-
logger.warn("[\
|
|
95480
|
+
logger.warn("[\u9023\u7DDA\u76E3\u63A7] MCP Client HTTP \u9023\u7DDA\u5728\u5DE5\u5177\u57F7\u884C\u671F\u9593\u63D0\u524D\u95DC\u9589\uFF0C\u56DE\u8986\u53EF\u80FD\u672A\u9001\u9054");
|
|
95454
95481
|
}
|
|
95455
95482
|
});
|
|
95456
95483
|
const sessionId = req.headers["mcp-session-id"];
|
|
@@ -95462,23 +95489,30 @@ var WebServer = class {
|
|
|
95462
95489
|
});
|
|
95463
95490
|
transport.onclose = () => {
|
|
95464
95491
|
const sid = transport.sessionId;
|
|
95465
|
-
if (sid)
|
|
95466
|
-
|
|
95492
|
+
if (sid) {
|
|
95493
|
+
this.streamableHttpTransports.delete(sid);
|
|
95494
|
+
this.streamableHttpSseActive.delete(sid);
|
|
95495
|
+
logger.info(`[\u9023\u7DDA\u76E3\u63A7] MCP Client \u5DF2\u65B7\u7DDA (sessionId=${sid})\uFF0C\u5269\u9918\u6D3B\u8E8D\u9023\u7DDA: ${this.streamableHttpTransports.size}`);
|
|
95496
|
+
}
|
|
95467
95497
|
};
|
|
95468
95498
|
try {
|
|
95469
95499
|
await this.mcpServerRef.getMcpServerInstance().connect(transport);
|
|
95470
|
-
|
|
95500
|
+
const clientIp = req.headers["x-forwarded-for"] || req.socket?.remoteAddress || "unknown";
|
|
95501
|
+
logger.info(`[\u9023\u7DDA\u76E3\u63A7] \u2705 MCP Client \u5DF2\u9023\u7DDA \u2014 IP: ${clientIp}`);
|
|
95471
95502
|
await transport.handleRequest(req, res, req.body);
|
|
95472
95503
|
const sid = transport.sessionId;
|
|
95473
|
-
if (sid)
|
|
95504
|
+
if (sid) {
|
|
95505
|
+
this.streamableHttpTransports.set(sid, transport);
|
|
95506
|
+
logger.info(`[\u9023\u7DDA\u76E3\u63A7] MCP Session \u5DF2\u5EFA\u7ACB (sessionId=${sid})\uFF0C\u76EE\u524D\u6D3B\u8E8D\u9023\u7DDA: ${this.streamableHttpTransports.size}`);
|
|
95507
|
+
}
|
|
95474
95508
|
} catch (error2) {
|
|
95475
95509
|
logger.error("\u8655\u7406 Streamable HTTP \u521D\u59CB\u5316\u8ACB\u6C42\u5931\u6557:", error2);
|
|
95476
95510
|
if (!res.headersSent) {
|
|
95477
95511
|
res.status(500).json({ error: "Failed to initialize MCP session" });
|
|
95478
95512
|
}
|
|
95479
95513
|
}
|
|
95480
|
-
} else if (sessionId &&
|
|
95481
|
-
const transport =
|
|
95514
|
+
} else if (sessionId && this.streamableHttpTransports.has(sessionId)) {
|
|
95515
|
+
const transport = this.streamableHttpTransports.get(sessionId);
|
|
95482
95516
|
try {
|
|
95483
95517
|
await transport.handleRequest(req, res, req.body);
|
|
95484
95518
|
} catch (error2) {
|
|
@@ -95493,24 +95527,29 @@ var WebServer = class {
|
|
|
95493
95527
|
});
|
|
95494
95528
|
this.app.delete("/mcp", async (req, res) => {
|
|
95495
95529
|
const sessionId = req.headers["mcp-session-id"];
|
|
95496
|
-
if (sessionId &&
|
|
95497
|
-
const transport =
|
|
95530
|
+
if (sessionId && this.streamableHttpTransports.has(sessionId)) {
|
|
95531
|
+
const transport = this.streamableHttpTransports.get(sessionId);
|
|
95498
95532
|
try {
|
|
95499
95533
|
await transport.handleRequest(req, res, req.body);
|
|
95500
95534
|
} catch {
|
|
95501
95535
|
}
|
|
95502
|
-
|
|
95536
|
+
this.streamableHttpTransports.delete(sessionId);
|
|
95537
|
+
this.streamableHttpSseActive.delete(sessionId);
|
|
95538
|
+
logger.info(`[\u9023\u7DDA\u76E3\u63A7] MCP Session \u5DF2\u522A\u9664 (sessionId=${sessionId})`);
|
|
95503
95539
|
} else {
|
|
95504
95540
|
res.status(404).json({ error: "Session not found" });
|
|
95505
95541
|
}
|
|
95506
95542
|
});
|
|
95507
95543
|
this.app.get("/mcp", async (req, res) => {
|
|
95508
95544
|
const sessionId = req.query["sessionId"];
|
|
95509
|
-
if (!sessionId || !
|
|
95545
|
+
if (!sessionId || !this.streamableHttpTransports.has(sessionId)) {
|
|
95510
95546
|
res.status(400).json({ error: "Invalid session" });
|
|
95511
95547
|
return;
|
|
95512
95548
|
}
|
|
95513
|
-
|
|
95549
|
+
this.streamableHttpSseActive.set(sessionId, true);
|
|
95550
|
+
const activeSseCount = [...this.streamableHttpSseActive.values()].filter(Boolean).length;
|
|
95551
|
+
logger.info(`[\u9023\u7DDA\u76E3\u63A7] \u{1F4E1} GET SSE \u4EA4\u4ED8\u901A\u9053\u5DF2\u958B\u555F (sessionId=${sessionId})\uFF0C\u6D3B\u8E8D SSE \u6578: ${activeSseCount}`);
|
|
95552
|
+
const transport = this.streamableHttpTransports.get(sessionId);
|
|
95514
95553
|
const heartbeat = setInterval(() => {
|
|
95515
95554
|
try {
|
|
95516
95555
|
if (!res.writableEnded) res.write(": ping\n\n");
|
|
@@ -95519,7 +95558,12 @@ var WebServer = class {
|
|
|
95519
95558
|
clearInterval(heartbeat);
|
|
95520
95559
|
}
|
|
95521
95560
|
}, 15e3);
|
|
95522
|
-
req.on("close", () =>
|
|
95561
|
+
req.on("close", () => {
|
|
95562
|
+
clearInterval(heartbeat);
|
|
95563
|
+
this.streamableHttpSseActive.set(sessionId, false);
|
|
95564
|
+
const remaining = [...this.streamableHttpSseActive.values()].filter(Boolean).length;
|
|
95565
|
+
logger.warn(`[\u9023\u7DDA\u76E3\u63A7] \u26A0\uFE0F GET SSE \u4EA4\u4ED8\u901A\u9053\u5DF2\u95DC\u9589 (sessionId=${sessionId})\uFF0C\u5269\u9918\u6D3B\u8E8D SSE \u6578: ${remaining}`);
|
|
95566
|
+
});
|
|
95523
95567
|
try {
|
|
95524
95568
|
await transport.handleRequest(req, res);
|
|
95525
95569
|
} catch (error2) {
|
package/dist/index.cjs
CHANGED
|
@@ -90002,6 +90002,9 @@ var WebServer = class {
|
|
|
90002
90002
|
sseTransports = /* @__PURE__ */ new Map();
|
|
90003
90003
|
sseTransportsList = [];
|
|
90004
90004
|
pendingDeliveryCache = /* @__PURE__ */ new Map();
|
|
90005
|
+
streamableHttpTransports = /* @__PURE__ */ new Map();
|
|
90006
|
+
streamableHttpSseActive = /* @__PURE__ */ new Map();
|
|
90007
|
+
activeSessionPromises = /* @__PURE__ */ new Map();
|
|
90005
90008
|
dbInitialized = false;
|
|
90006
90009
|
selfProbeService;
|
|
90007
90010
|
/**
|
|
@@ -92001,6 +92004,17 @@ var WebServer = class {
|
|
|
92001
92004
|
/**
|
|
92002
92005
|
* 處理回饋提交
|
|
92003
92006
|
*/
|
|
92007
|
+
async waitForActiveConnection(maxRounds = 5, intervalMs = 5e3) {
|
|
92008
|
+
for (let round = 1; round <= maxRounds; round++) {
|
|
92009
|
+
const hasStreamableSSE = [...this.streamableHttpSseActive.values()].some((v) => v);
|
|
92010
|
+
const hasSSETransport = this.sseTransportsList.length > 0;
|
|
92011
|
+
if (hasStreamableSSE || hasSSETransport) return true;
|
|
92012
|
+
logger.warn(`[\u9023\u7DDA\u7B49\u5F85] \u7B2C ${round}/${maxRounds} \u8F2A\uFF1A\u76EE\u524D\u7121\u6D3B\u8E8D MCP Client \u9023\u7DDA\uFF0C${intervalMs / 1e3}s \u5F8C\u91CD\u8A66...`);
|
|
92013
|
+
await new Promise((r) => setTimeout(r, intervalMs));
|
|
92014
|
+
}
|
|
92015
|
+
logger.error(`[\u9023\u7DDA\u7B49\u5F85] \u5DF2\u5617\u8A66 ${maxRounds} \u8F2A\u4ECD\u7121\u6D3B\u8E8D\u9023\u7DDA\uFF0C\u5C07\u76F4\u63A5\u9001\u51FA\uFF08\u5FEB\u53D6\u6A5F\u5236\u4F5C\u70BA\u4FDD\u8B77\uFF09`);
|
|
92016
|
+
return false;
|
|
92017
|
+
}
|
|
92004
92018
|
async handleFeedbackSubmission(socket, feedbackData) {
|
|
92005
92019
|
const session = this.sessionStorage.getSession(feedbackData.sessionId);
|
|
92006
92020
|
if (!session) {
|
|
@@ -92059,6 +92073,10 @@ var WebServer = class {
|
|
|
92059
92073
|
const projectId = session.projectId;
|
|
92060
92074
|
setTimeout(() => this.pendingDeliveryCache.delete(projectId), 6e4);
|
|
92061
92075
|
}
|
|
92076
|
+
const hasConnection = await this.waitForActiveConnection();
|
|
92077
|
+
if (hasConnection) {
|
|
92078
|
+
logger.info("[\u9023\u7DDA\u76E3\u63A7] \u9023\u7DDA\u78BA\u8A8D\uFF0C\u6B63\u5728\u9001\u51FA\u56DE\u8986\u81F3 MCP Client...");
|
|
92079
|
+
}
|
|
92062
92080
|
session.resolve(session.feedback);
|
|
92063
92081
|
const sessionIdToDelete = feedbackData.sessionId;
|
|
92064
92082
|
setTimeout(() => this.sessionStorage.deleteSession(sessionIdToDelete), 5e3);
|
|
@@ -92146,8 +92164,11 @@ var WebServer = class {
|
|
|
92146
92164
|
async collectFeedback(workSummary, timeoutSeconds, projectName, projectPath) {
|
|
92147
92165
|
const sessionId = this.generateSessionId();
|
|
92148
92166
|
const project = projectName ? projectManager.getOrCreateProject(projectName, projectPath) : projectManager.getDefaultProject();
|
|
92149
|
-
|
|
92150
|
-
|
|
92167
|
+
const existingPromise = this.activeSessionPromises.get(project.id);
|
|
92168
|
+
if (existingPromise) {
|
|
92169
|
+
logger.warn(`[\u91CD\u8907\u8ACB\u6C42] \u5C08\u6848 "${project.name}" \u5DF2\u6709\u9032\u884C\u4E2D\u7684\u56DE\u994B\u6703\u8A71\uFF0C\u91CD\u7528\u73FE\u6709\u8ACB\u6C42\uFF08\u9632\u6B62\u91CD\u8907\u5F48\u7A97\uFF09`);
|
|
92170
|
+
return existingPromise;
|
|
92171
|
+
}
|
|
92151
92172
|
const pendingEntry = this.pendingDeliveryCache.get(project.id);
|
|
92152
92173
|
if (pendingEntry && pendingEntry.expiresAt > Date.now()) {
|
|
92153
92174
|
const prevSession = this.sessionStorage.getSession(pendingEntry.result.sessionId);
|
|
@@ -92157,7 +92178,9 @@ var WebServer = class {
|
|
|
92157
92178
|
return pendingEntry.result;
|
|
92158
92179
|
}
|
|
92159
92180
|
}
|
|
92160
|
-
|
|
92181
|
+
logger.info(`\u5EFA\u7ACB\u56DE\u994B\u6703\u8A71: ${sessionId}, \u903E\u6642: ${timeoutSeconds}\u79D2, \u5C08\u6848: ${project.name} (${project.id})`);
|
|
92182
|
+
const feedbackUrl = this.generateFeedbackUrl(sessionId);
|
|
92183
|
+
const sessionPromise = new Promise((resolve, reject) => {
|
|
92161
92184
|
const session = {
|
|
92162
92185
|
workSummary,
|
|
92163
92186
|
feedback: [],
|
|
@@ -92187,6 +92210,11 @@ var WebServer = class {
|
|
|
92187
92210
|
});
|
|
92188
92211
|
}
|
|
92189
92212
|
});
|
|
92213
|
+
this.activeSessionPromises.set(project.id, sessionPromise);
|
|
92214
|
+
void sessionPromise.finally(() => {
|
|
92215
|
+
this.activeSessionPromises.delete(project.id);
|
|
92216
|
+
});
|
|
92217
|
+
return sessionPromise;
|
|
92190
92218
|
}
|
|
92191
92219
|
emitDashboardSessionCreated(projectId, sessionId, projectName, workSummary) {
|
|
92192
92220
|
if (this.io) {
|
|
@@ -92454,7 +92482,6 @@ var WebServer = class {
|
|
|
92454
92482
|
*/
|
|
92455
92483
|
async setupStreamableHTTPEndpoints() {
|
|
92456
92484
|
const { StreamableHTTPServerTransport: StreamableHTTPServerTransport2 } = await Promise.resolve().then(() => (init_streamableHttp2(), streamableHttp_exports2));
|
|
92457
|
-
const streamableTransports = /* @__PURE__ */ new Map();
|
|
92458
92485
|
this.app.post("/mcp", import_express.default.json(), async (req, res) => {
|
|
92459
92486
|
logger.debug("\u6536\u5230 Streamable HTTP \u8ACB\u6C42:", JSON.stringify(req.body));
|
|
92460
92487
|
if (!this.mcpServerRef) {
|
|
@@ -92465,7 +92492,7 @@ var WebServer = class {
|
|
|
92465
92492
|
req.socket?.setKeepAlive(true, 3e4);
|
|
92466
92493
|
req.on("close", () => {
|
|
92467
92494
|
if (!res.writableEnded) {
|
|
92468
|
-
logger.warn("[\
|
|
92495
|
+
logger.warn("[\u9023\u7DDA\u76E3\u63A7] MCP Client HTTP \u9023\u7DDA\u5728\u5DE5\u5177\u57F7\u884C\u671F\u9593\u63D0\u524D\u95DC\u9589\uFF0C\u56DE\u8986\u53EF\u80FD\u672A\u9001\u9054");
|
|
92469
92496
|
}
|
|
92470
92497
|
});
|
|
92471
92498
|
const sessionId = req.headers["mcp-session-id"];
|
|
@@ -92477,23 +92504,30 @@ var WebServer = class {
|
|
|
92477
92504
|
});
|
|
92478
92505
|
transport.onclose = () => {
|
|
92479
92506
|
const sid = transport.sessionId;
|
|
92480
|
-
if (sid)
|
|
92481
|
-
|
|
92507
|
+
if (sid) {
|
|
92508
|
+
this.streamableHttpTransports.delete(sid);
|
|
92509
|
+
this.streamableHttpSseActive.delete(sid);
|
|
92510
|
+
logger.info(`[\u9023\u7DDA\u76E3\u63A7] MCP Client \u5DF2\u65B7\u7DDA (sessionId=${sid})\uFF0C\u5269\u9918\u6D3B\u8E8D\u9023\u7DDA: ${this.streamableHttpTransports.size}`);
|
|
92511
|
+
}
|
|
92482
92512
|
};
|
|
92483
92513
|
try {
|
|
92484
92514
|
await this.mcpServerRef.getMcpServerInstance().connect(transport);
|
|
92485
|
-
|
|
92515
|
+
const clientIp = req.headers["x-forwarded-for"] || req.socket?.remoteAddress || "unknown";
|
|
92516
|
+
logger.info(`[\u9023\u7DDA\u76E3\u63A7] \u2705 MCP Client \u5DF2\u9023\u7DDA \u2014 IP: ${clientIp}`);
|
|
92486
92517
|
await transport.handleRequest(req, res, req.body);
|
|
92487
92518
|
const sid = transport.sessionId;
|
|
92488
|
-
if (sid)
|
|
92519
|
+
if (sid) {
|
|
92520
|
+
this.streamableHttpTransports.set(sid, transport);
|
|
92521
|
+
logger.info(`[\u9023\u7DDA\u76E3\u63A7] MCP Session \u5DF2\u5EFA\u7ACB (sessionId=${sid})\uFF0C\u76EE\u524D\u6D3B\u8E8D\u9023\u7DDA: ${this.streamableHttpTransports.size}`);
|
|
92522
|
+
}
|
|
92489
92523
|
} catch (error2) {
|
|
92490
92524
|
logger.error("\u8655\u7406 Streamable HTTP \u521D\u59CB\u5316\u8ACB\u6C42\u5931\u6557:", error2);
|
|
92491
92525
|
if (!res.headersSent) {
|
|
92492
92526
|
res.status(500).json({ error: "Failed to initialize MCP session" });
|
|
92493
92527
|
}
|
|
92494
92528
|
}
|
|
92495
|
-
} else if (sessionId &&
|
|
92496
|
-
const transport =
|
|
92529
|
+
} else if (sessionId && this.streamableHttpTransports.has(sessionId)) {
|
|
92530
|
+
const transport = this.streamableHttpTransports.get(sessionId);
|
|
92497
92531
|
try {
|
|
92498
92532
|
await transport.handleRequest(req, res, req.body);
|
|
92499
92533
|
} catch (error2) {
|
|
@@ -92508,24 +92542,29 @@ var WebServer = class {
|
|
|
92508
92542
|
});
|
|
92509
92543
|
this.app.delete("/mcp", async (req, res) => {
|
|
92510
92544
|
const sessionId = req.headers["mcp-session-id"];
|
|
92511
|
-
if (sessionId &&
|
|
92512
|
-
const transport =
|
|
92545
|
+
if (sessionId && this.streamableHttpTransports.has(sessionId)) {
|
|
92546
|
+
const transport = this.streamableHttpTransports.get(sessionId);
|
|
92513
92547
|
try {
|
|
92514
92548
|
await transport.handleRequest(req, res, req.body);
|
|
92515
92549
|
} catch {
|
|
92516
92550
|
}
|
|
92517
|
-
|
|
92551
|
+
this.streamableHttpTransports.delete(sessionId);
|
|
92552
|
+
this.streamableHttpSseActive.delete(sessionId);
|
|
92553
|
+
logger.info(`[\u9023\u7DDA\u76E3\u63A7] MCP Session \u5DF2\u522A\u9664 (sessionId=${sessionId})`);
|
|
92518
92554
|
} else {
|
|
92519
92555
|
res.status(404).json({ error: "Session not found" });
|
|
92520
92556
|
}
|
|
92521
92557
|
});
|
|
92522
92558
|
this.app.get("/mcp", async (req, res) => {
|
|
92523
92559
|
const sessionId = req.query["sessionId"];
|
|
92524
|
-
if (!sessionId || !
|
|
92560
|
+
if (!sessionId || !this.streamableHttpTransports.has(sessionId)) {
|
|
92525
92561
|
res.status(400).json({ error: "Invalid session" });
|
|
92526
92562
|
return;
|
|
92527
92563
|
}
|
|
92528
|
-
|
|
92564
|
+
this.streamableHttpSseActive.set(sessionId, true);
|
|
92565
|
+
const activeSseCount = [...this.streamableHttpSseActive.values()].filter(Boolean).length;
|
|
92566
|
+
logger.info(`[\u9023\u7DDA\u76E3\u63A7] \u{1F4E1} GET SSE \u4EA4\u4ED8\u901A\u9053\u5DF2\u958B\u555F (sessionId=${sessionId})\uFF0C\u6D3B\u8E8D SSE \u6578: ${activeSseCount}`);
|
|
92567
|
+
const transport = this.streamableHttpTransports.get(sessionId);
|
|
92529
92568
|
const heartbeat = setInterval(() => {
|
|
92530
92569
|
try {
|
|
92531
92570
|
if (!res.writableEnded) res.write(": ping\n\n");
|
|
@@ -92534,7 +92573,12 @@ var WebServer = class {
|
|
|
92534
92573
|
clearInterval(heartbeat);
|
|
92535
92574
|
}
|
|
92536
92575
|
}, 15e3);
|
|
92537
|
-
req.on("close", () =>
|
|
92576
|
+
req.on("close", () => {
|
|
92577
|
+
clearInterval(heartbeat);
|
|
92578
|
+
this.streamableHttpSseActive.set(sessionId, false);
|
|
92579
|
+
const remaining = [...this.streamableHttpSseActive.values()].filter(Boolean).length;
|
|
92580
|
+
logger.warn(`[\u9023\u7DDA\u76E3\u63A7] \u26A0\uFE0F GET SSE \u4EA4\u4ED8\u901A\u9053\u5DF2\u95DC\u9589 (sessionId=${sessionId})\uFF0C\u5269\u9918\u6D3B\u8E8D SSE \u6578: ${remaining}`);
|
|
92581
|
+
});
|
|
92538
92582
|
try {
|
|
92539
92583
|
await transport.handleRequest(req, res);
|
|
92540
92584
|
} catch (error2) {
|