@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 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: "抓取超时 (120s),请检查浏览器是否已停止滚动" });
151
+ resolve({ error: "抓取超时 (300s),请检查浏览器是否已停止滚动" });
152
152
  }
153
- }, 120000);
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: "抓取超时 (120s)" });
247
- }, 120000);
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 HTTP_SERVICE_URL = "http://127.0.0.1:18793/search";
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("http://127.0.0.1:18793/ping", (res) => {
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
- * 自动拉起 Service 层
47
+ * 请求旧服务优雅退出
45
48
  */
46
- async function ensureServiceRunning() {
47
- const alive = await checkServiceAlive();
48
- if (alive) {
49
- return;
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 = 5;
71
+ async function waitForServiceDown() {
72
+ let retries = 10;
61
73
  while (retries > 0) {
62
- await new Promise(r => setTimeout(r, 1000));
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
- throw new Error("Failed to start Service Layer after 5s.");
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gecho-ai/gecho-bridge",
3
- "version": "1.1.1",
3
+ "version": "1.1.2",
4
4
  "description": "MCP Bridge for TikTok Extension Relay - Connecting LLMs to TikTok via Chrome Extension",
5
5
  "main": "mcp-client.js",
6
6
  "bin": {
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 (120s)" });
139
- }, 120000);
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"));