@gecho-ai/gecho-bridge 1.1.1 → 1.1.2
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/index.js +4 -4
- package/mcp-client.js +60 -15
- package/package.json +1 -1
- package/server.js +47 -2
package/index.js
CHANGED
|
@@ -148,9 +148,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
148
148
|
const timeoutId = setTimeout(() => {
|
|
149
149
|
if (pendingRequests.has(requestId)) {
|
|
150
150
|
pendingRequests.delete(requestId);
|
|
151
|
-
resolve({ error: "抓取超时 (
|
|
151
|
+
resolve({ error: "抓取超时 (300s),请检查浏览器是否已停止滚动" });
|
|
152
152
|
}
|
|
153
|
-
},
|
|
153
|
+
}, 300000);
|
|
154
154
|
|
|
155
155
|
pendingRequests.set(requestId, { resolve, reject, timeoutId });
|
|
156
156
|
|
|
@@ -243,8 +243,8 @@ async function run() {
|
|
|
243
243
|
const result = await new Promise((resolve) => {
|
|
244
244
|
const timeoutId = setTimeout(() => {
|
|
245
245
|
pendingRequests.delete(requestId);
|
|
246
|
-
resolve({ error: "抓取超时 (
|
|
247
|
-
},
|
|
246
|
+
resolve({ error: "抓取超时 (300s)" });
|
|
247
|
+
}, 300000);
|
|
248
248
|
|
|
249
249
|
pendingRequests.set(requestId, { resolve, timeoutId });
|
|
250
250
|
|
package/mcp-client.js
CHANGED
|
@@ -19,7 +19,10 @@ const http = require("http");
|
|
|
19
19
|
const { spawn } = require("child_process");
|
|
20
20
|
const path = require("path");
|
|
21
21
|
|
|
22
|
-
const
|
|
22
|
+
const SERVICE_BASE_URL = "http://127.0.0.1:18793";
|
|
23
|
+
const HTTP_SERVICE_URL = `${SERVICE_BASE_URL}/search`;
|
|
24
|
+
const PING_URL = `${SERVICE_BASE_URL}/ping`;
|
|
25
|
+
const SHUTDOWN_URL = `${SERVICE_BASE_URL}/shutdown`;
|
|
23
26
|
const SERVICE_PATH = path.join(__dirname, "server.js");
|
|
24
27
|
|
|
25
28
|
const server = new Server(
|
|
@@ -32,7 +35,7 @@ const server = new Server(
|
|
|
32
35
|
*/
|
|
33
36
|
function checkServiceAlive() {
|
|
34
37
|
return new Promise((resolve) => {
|
|
35
|
-
const req = http.get(
|
|
38
|
+
const req = http.get(PING_URL, (res) => {
|
|
36
39
|
resolve(res.statusCode === 200);
|
|
37
40
|
});
|
|
38
41
|
req.on("error", () => resolve(false));
|
|
@@ -41,31 +44,65 @@ function checkServiceAlive() {
|
|
|
41
44
|
}
|
|
42
45
|
|
|
43
46
|
/**
|
|
44
|
-
*
|
|
47
|
+
* 请求旧服务优雅退出
|
|
45
48
|
*/
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
function requestShutdown() {
|
|
50
|
+
return new Promise((resolve) => {
|
|
51
|
+
const req = http.request(SHUTDOWN_URL, { method: "POST" }, (res) => {
|
|
52
|
+
resolve(res.statusCode >= 200 && res.statusCode < 300);
|
|
53
|
+
});
|
|
54
|
+
req.on("error", () => resolve(false));
|
|
55
|
+
req.setTimeout(1000, () => {
|
|
56
|
+
req.destroy();
|
|
57
|
+
resolve(false);
|
|
58
|
+
});
|
|
59
|
+
req.end();
|
|
60
|
+
});
|
|
61
|
+
}
|
|
51
62
|
|
|
63
|
+
function startServiceDetached() {
|
|
52
64
|
const child = spawn("node", [SERVICE_PATH], {
|
|
53
65
|
detached: true,
|
|
54
66
|
stdio: "ignore" // 静默启动,不占用当前终端
|
|
55
67
|
});
|
|
56
|
-
|
|
57
68
|
child.unref(); // 让子进程独立运行,父进程退出时不影响它
|
|
69
|
+
}
|
|
58
70
|
|
|
59
|
-
|
|
60
|
-
let retries =
|
|
71
|
+
async function waitForServiceDown() {
|
|
72
|
+
let retries = 10;
|
|
61
73
|
while (retries > 0) {
|
|
62
|
-
await new Promise(r => setTimeout(r,
|
|
74
|
+
await new Promise((r) => setTimeout(r, 300));
|
|
75
|
+
if (!(await checkServiceAlive())) {
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
retries--;
|
|
79
|
+
}
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async function waitForServiceUp() {
|
|
84
|
+
let retries = 8;
|
|
85
|
+
while (retries > 0) {
|
|
86
|
+
await new Promise((r) => setTimeout(r, 800));
|
|
63
87
|
if (await checkServiceAlive()) {
|
|
64
|
-
return;
|
|
88
|
+
return true;
|
|
65
89
|
}
|
|
66
90
|
retries--;
|
|
67
91
|
}
|
|
68
|
-
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* 启动即接管:每次启动 MCP 时先尝试关闭旧服务,再拉起当前服务
|
|
97
|
+
*/
|
|
98
|
+
async function ensureServiceRunning() {
|
|
99
|
+
await requestShutdown();
|
|
100
|
+
await waitForServiceDown();
|
|
101
|
+
startServiceDetached();
|
|
102
|
+
const ready = await waitForServiceUp();
|
|
103
|
+
if (!ready) {
|
|
104
|
+
throw new Error("Failed to start Service Layer.");
|
|
105
|
+
}
|
|
69
106
|
}
|
|
70
107
|
|
|
71
108
|
// 1. 定义工具
|
|
@@ -138,7 +175,15 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
138
175
|
return { content: [{ type: "text", text: `❌ 错误: ${serviceResponse.error}` }], isError: true };
|
|
139
176
|
}
|
|
140
177
|
|
|
141
|
-
const result = serviceResponse.data
|
|
178
|
+
const result = serviceResponse.data;
|
|
179
|
+
if (typeof result === 'object' && result !== null && result.error) {
|
|
180
|
+
return { content: [{ type: "text", text: `❌ 抓取错误: ${result.error}` }], isError: true };
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (!Array.isArray(result)) {
|
|
184
|
+
return { content: [{ type: "text", text: `❌ 异常: 服务端未返回数组格式的数据` }], isError: true };
|
|
185
|
+
}
|
|
186
|
+
|
|
142
187
|
const savePath = serviceResponse.savePath || "";
|
|
143
188
|
const saveLine = savePath
|
|
144
189
|
? `📂 完整结果已保存到: ${savePath}\n\n`
|
package/package.json
CHANGED
package/server.js
CHANGED
|
@@ -19,6 +19,7 @@ const HTTP_PORT = 18793;
|
|
|
19
19
|
let extensionSocket = null;
|
|
20
20
|
const pendingRequests = new Map();
|
|
21
21
|
let requestIdCounter = 1;
|
|
22
|
+
let shuttingDown = false;
|
|
22
23
|
|
|
23
24
|
function decodeBase64Utf8(value) {
|
|
24
25
|
try {
|
|
@@ -64,6 +65,36 @@ function toSafeFileName(name) {
|
|
|
64
65
|
return reserved.has(upper) ? `${candidate}_` : candidate;
|
|
65
66
|
}
|
|
66
67
|
|
|
68
|
+
function gracefulShutdown(reason) {
|
|
69
|
+
if (shuttingDown) return;
|
|
70
|
+
shuttingDown = true;
|
|
71
|
+
console.log(`🛑 Service shutting down: ${reason}`);
|
|
72
|
+
|
|
73
|
+
for (const [_requestId, pending] of pendingRequests) {
|
|
74
|
+
clearTimeout(pending.timeoutId);
|
|
75
|
+
pending.resolve({ error: "Service is shutting down" });
|
|
76
|
+
}
|
|
77
|
+
pendingRequests.clear();
|
|
78
|
+
|
|
79
|
+
try {
|
|
80
|
+
if (extensionSocket && extensionSocket.readyState === 1) {
|
|
81
|
+
extensionSocket.close(1001, "service_shutdown");
|
|
82
|
+
}
|
|
83
|
+
} catch (_e) {}
|
|
84
|
+
|
|
85
|
+
try {
|
|
86
|
+
wss.close(() => {
|
|
87
|
+
server.close(() => process.exit(0));
|
|
88
|
+
});
|
|
89
|
+
} catch (_e) {
|
|
90
|
+
try {
|
|
91
|
+
server.close(() => process.exit(0));
|
|
92
|
+
} catch (__e) {
|
|
93
|
+
process.exit(0);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
67
98
|
// --- WebSocket Server (与插件通信) ---
|
|
68
99
|
const wss = new WebSocketServer({ port: WS_PORT, host: "127.0.0.1" });
|
|
69
100
|
|
|
@@ -112,7 +143,18 @@ const server = http.createServer(async (req, res) => {
|
|
|
112
143
|
return res.end(JSON.stringify({ status: "ok" }));
|
|
113
144
|
}
|
|
114
145
|
|
|
146
|
+
if (req.method === "POST" && req.url === "/shutdown") {
|
|
147
|
+
res.end(JSON.stringify({ status: "ok", message: "shutdown accepted" }));
|
|
148
|
+
setTimeout(() => gracefulShutdown("remote_shutdown"), 20).unref?.();
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
|
|
115
152
|
if (req.method === "POST" && req.url === "/search") {
|
|
153
|
+
if (shuttingDown) {
|
|
154
|
+
res.statusCode = 503;
|
|
155
|
+
return res.end(JSON.stringify({ error: "Service is shutting down" }));
|
|
156
|
+
}
|
|
157
|
+
|
|
116
158
|
let body = "";
|
|
117
159
|
req.on("data", chunk => { body += chunk; });
|
|
118
160
|
req.on("end", async () => {
|
|
@@ -135,8 +177,8 @@ const server = http.createServer(async (req, res) => {
|
|
|
135
177
|
const result = await new Promise((resolve) => {
|
|
136
178
|
const timeoutId = setTimeout(() => {
|
|
137
179
|
pendingRequests.delete(requestId);
|
|
138
|
-
resolve({ error: "Scraping timeout (
|
|
139
|
-
},
|
|
180
|
+
resolve({ error: "Scraping timeout (300s)" });
|
|
181
|
+
}, 300000);
|
|
140
182
|
|
|
141
183
|
pendingRequests.set(requestId, { resolve, timeoutId });
|
|
142
184
|
|
|
@@ -180,3 +222,6 @@ server.listen(HTTP_PORT, "127.0.0.1", () => {
|
|
|
180
222
|
console.log(` - WebSocket (Extension): ws://127.0.0.1:${WS_PORT}`);
|
|
181
223
|
console.log(` - HTTP API (Client): http://127.0.0.1:${HTTP_PORT}`);
|
|
182
224
|
});
|
|
225
|
+
|
|
226
|
+
process.on("SIGTERM", () => gracefulShutdown("sigterm"));
|
|
227
|
+
process.on("SIGINT", () => gracefulShutdown("sigint"));
|