ghost-bridge 0.7.0 → 0.8.0

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/server.js CHANGED
@@ -28523,6 +28523,7 @@ import net from "net";
28523
28523
  import fs2 from "fs";
28524
28524
  import os from "os";
28525
28525
  import path2 from "path";
28526
+ import { fileURLToPath as fileURLToPath2 } from "url";
28526
28527
 
28527
28528
  // lib/version.js
28528
28529
  import fs from "fs";
@@ -28541,6 +28542,7 @@ var WS_TOKEN = process.env.GHOST_BRIDGE_TOKEN || DEFAULT_WS_TOKEN;
28541
28542
  var RESPONSE_TIMEOUT = 8e3;
28542
28543
  var PORT_INFO_FILE = path2.join(os.tmpdir(), "ghost-bridge-port.json");
28543
28544
  var SERVER_STARTED_AT = (/* @__PURE__ */ new Date()).toISOString();
28545
+ var SERVER_ENTRY_PATH = fileURLToPath2(import.meta.url);
28544
28546
  var chromeConnection = null;
28545
28547
  var activeConnection = null;
28546
28548
  var actualPort = BASE_PORT;
@@ -28550,6 +28552,30 @@ var mcpClients = /* @__PURE__ */ new Set();
28550
28552
  function log(msg) {
28551
28553
  console.error(`[ghost-bridge] ${msg}`);
28552
28554
  }
28555
+ function buildIdentityPayload() {
28556
+ return {
28557
+ type: "identity",
28558
+ service: "ghost-bridge",
28559
+ token: WS_TOKEN,
28560
+ pid: process.pid,
28561
+ port: actualPort,
28562
+ version: GHOST_BRIDGE_VERSION,
28563
+ serverPath: SERVER_ENTRY_PATH,
28564
+ startedAt: SERVER_STARTED_AT
28565
+ };
28566
+ }
28567
+ function getServiceMismatch(probe) {
28568
+ if (!probe || probe.service !== "ghost-bridge") return null;
28569
+ if (!probe.version) return "\u65E7\u670D\u52A1\u7F3A\u5C11\u7248\u672C\u5143\u6570\u636E";
28570
+ if (probe.version !== GHOST_BRIDGE_VERSION) {
28571
+ return `\u7248\u672C\u4E0D\u4E00\u81F4\uFF08\u65E7: ${probe.version}, \u65B0: ${GHOST_BRIDGE_VERSION}\uFF09`;
28572
+ }
28573
+ if (!probe.serverPath) return "\u65E7\u670D\u52A1\u7F3A\u5C11\u8DEF\u5F84\u5143\u6570\u636E";
28574
+ if (path2.resolve(probe.serverPath) !== path2.resolve(SERVER_ENTRY_PATH)) {
28575
+ return `\u8DEF\u5F84\u4E0D\u4E00\u81F4\uFF08\u65E7: ${probe.serverPath}, \u65B0: ${SERVER_ENTRY_PATH}\uFF09`;
28576
+ }
28577
+ return null;
28578
+ }
28553
28579
  function isProcessRunning(pid) {
28554
28580
  try {
28555
28581
  process.kill(pid, 0);
@@ -28628,11 +28654,11 @@ async function waitForPortAvailable(port, timeoutMs = 5e3) {
28628
28654
  }
28629
28655
  return isPortAvailable(port);
28630
28656
  }
28631
- async function stopExistingService(pid, port) {
28657
+ async function stopExistingService(pid, port, reason = "\u9700\u8981\u7531\u65B0\u5B9E\u4F8B\u63A5\u7BA1") {
28632
28658
  if (!pid || pid === process.pid) {
28633
28659
  return false;
28634
28660
  }
28635
- log(`\u68C0\u6D4B\u5230\u65E7\u5B9E\u4F8B token \u4E0D\u4E00\u81F4\uFF0C\u51C6\u5907\u505C\u6B62\u65E7\u5B9E\u4F8B (PID: ${pid})`);
28661
+ log(`\u68C0\u6D4B\u5230\u65E7\u5B9E\u4F8B\u9700\u8981\u66FF\u6362\uFF1A${reason}\uFF0C\u51C6\u5907\u505C\u6B62\u65E7\u5B9E\u4F8B (PID: ${pid})`);
28636
28662
  try {
28637
28663
  process.kill(pid, "SIGTERM");
28638
28664
  } catch (e) {
@@ -28686,13 +28712,18 @@ async function initWebSocketService() {
28686
28712
  const probe = await probeExistingService(existing.port);
28687
28713
  if (probe?.service === "ghost-bridge") {
28688
28714
  if (probe.token === WS_TOKEN) {
28689
- actualPort = existing.port;
28690
- isMainInstance = false;
28691
- log(`\u2705 \u590D\u7528\u73B0\u6709\u670D\u52A1\uFF0C\u7AEF\u53E3 ${actualPort}`);
28692
- return null;
28715
+ const mismatch = getServiceMismatch(probe);
28716
+ if (!mismatch) {
28717
+ actualPort = existing.port;
28718
+ isMainInstance = false;
28719
+ log(`\u2705 \u590D\u7528\u73B0\u6709\u670D\u52A1\uFF0C\u7AEF\u53E3 ${actualPort}`);
28720
+ return null;
28721
+ }
28722
+ await stopExistingService(Number(probe.pid) || existing.pid, existing.port, mismatch);
28723
+ } else {
28724
+ const oldPid = Number(probe.pid) || existing.pid;
28725
+ await stopExistingService(oldPid, existing.port, "token \u4E0D\u4E00\u81F4");
28693
28726
  }
28694
- const oldPid = Number(probe.pid) || existing.pid;
28695
- await stopExistingService(oldPid, existing.port);
28696
28727
  } else {
28697
28728
  log(`\u274C \u73B0\u6709\u670D\u52A1\u9A8C\u8BC1\u5931\u8D25\uFF0C\u542F\u52A8\u65B0\u670D\u52A1...`);
28698
28729
  try {
@@ -28705,12 +28736,17 @@ async function initWebSocketService() {
28705
28736
  const probe = await probeExistingService(BASE_PORT);
28706
28737
  if (probe?.service === "ghost-bridge") {
28707
28738
  if (probe.token === WS_TOKEN) {
28708
- actualPort = BASE_PORT;
28709
- isMainInstance = false;
28710
- log(`\u2705 \u590D\u7528\u56FA\u5B9A\u7AEF\u53E3\u4E0A\u7684\u73B0\u6709\u670D\u52A1\uFF0C\u7AEF\u53E3 ${actualPort}`);
28711
- return null;
28739
+ const mismatch = getServiceMismatch(probe);
28740
+ if (!mismatch) {
28741
+ actualPort = BASE_PORT;
28742
+ isMainInstance = false;
28743
+ log(`\u2705 \u590D\u7528\u56FA\u5B9A\u7AEF\u53E3\u4E0A\u7684\u73B0\u6709\u670D\u52A1\uFF0C\u7AEF\u53E3 ${actualPort}`);
28744
+ return null;
28745
+ }
28746
+ await stopExistingService(Number(probe.pid), BASE_PORT, mismatch);
28747
+ } else {
28748
+ await stopExistingService(Number(probe.pid), BASE_PORT, "token \u4E0D\u4E00\u81F4");
28712
28749
  }
28713
- await stopExistingService(Number(probe.pid), BASE_PORT);
28714
28750
  }
28715
28751
  if (!await isPortAvailable(BASE_PORT)) {
28716
28752
  throw new Error(`\u56FA\u5B9A\u7AEF\u53E3 ${BASE_PORT} \u5DF2\u88AB\u5176\u4ED6\u8FDB\u7A0B\u5360\u7528\uFF0C\u8BF7\u91CA\u653E\u8BE5\u7AEF\u53E3\u6216\u901A\u8FC7 GHOST_BRIDGE_PORT \u6307\u5B9A\u5176\u4ED6\u7AEF\u53E3`);
@@ -28724,6 +28760,8 @@ async function initWebSocketService() {
28724
28760
  port: actualPort,
28725
28761
  wsUrl: `ws://localhost:${actualPort}`,
28726
28762
  pid: process.pid,
28763
+ version: GHOST_BRIDGE_VERSION,
28764
+ serverPath: SERVER_ENTRY_PATH,
28727
28765
  startedAt: SERVER_STARTED_AT
28728
28766
  }, null, 2)
28729
28767
  );
@@ -28737,14 +28775,7 @@ if (wss) {
28737
28775
  const token = url2.searchParams.get("token") || "";
28738
28776
  const role = url2.searchParams.get("role") || "";
28739
28777
  if (role === "probe") {
28740
- ws.send(JSON.stringify({
28741
- type: "identity",
28742
- service: "ghost-bridge",
28743
- token: WS_TOKEN,
28744
- pid: process.pid,
28745
- port: actualPort,
28746
- startedAt: SERVER_STARTED_AT
28747
- }));
28778
+ ws.send(JSON.stringify(buildIdentityPayload()));
28748
28779
  ws.close(1e3, "Probe complete");
28749
28780
  return;
28750
28781
  }
@@ -28757,7 +28788,7 @@ if (wss) {
28757
28788
  if (role === "mcp-client") {
28758
28789
  log("\u{1F4E1} MCP \u5BA2\u6237\u7AEF\u5DF2\u8FDE\u63A5");
28759
28790
  mcpClients.add(ws);
28760
- ws.send(JSON.stringify({ type: "identity", service: "ghost-bridge", token: WS_TOKEN }));
28791
+ ws.send(JSON.stringify(buildIdentityPayload()));
28761
28792
  ws.on("message", (data) => {
28762
28793
  try {
28763
28794
  const msg = JSON.parse(data.toString());
@@ -28767,7 +28798,11 @@ if (wss) {
28767
28798
  result: {
28768
28799
  chromeConnected: !!chromeConnection,
28769
28800
  mcpClientsCount: mcpClients.size,
28770
- port: actualPort
28801
+ port: actualPort,
28802
+ version: GHOST_BRIDGE_VERSION,
28803
+ serverPath: SERVER_ENTRY_PATH,
28804
+ pid: process.pid,
28805
+ startedAt: SERVER_STARTED_AT
28771
28806
  }
28772
28807
  }));
28773
28808
  return;
@@ -28801,7 +28836,7 @@ if (wss) {
28801
28836
  log("\u{1F310} Chrome \u6269\u5C55\u5DF2\u8FDE\u63A5");
28802
28837
  chromeConnection = ws;
28803
28838
  activeConnection = ws;
28804
- ws.send(JSON.stringify({ type: "identity", service: "ghost-bridge", token: WS_TOKEN }));
28839
+ ws.send(JSON.stringify(buildIdentityPayload()));
28805
28840
  ws.on("message", (data) => {
28806
28841
  try {
28807
28842
  const msg = JSON.parse(data.toString());
@@ -28999,7 +29034,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
28999
29034
  tools: [
29000
29035
  {
29001
29036
  name: "inspect_page",
29002
- description: "\u3010\u9875\u9762\u5206\u6790\u5165\u53E3\u3011\u5F53\u7528\u6237\u8981\u6C42\u5206\u6790\u5F53\u524D\u9875\u9762/\u7F51\u7AD9/\u7F51\u9875\u3001\u7406\u89E3\u9875\u9762\u7ED3\u6784\u3001\u5FEB\u901F\u67E5\u770B\u5F53\u524D\u6807\u7B7E\u5185\u5BB9\u65F6\uFF0C\u4F18\u5148\u4F7F\u7528\u6B64\u5DE5\u5177\u3002\u65E0\u9700\u7528\u6237\u663E\u5F0F\u63D0\u5230 ghost-bridge\u3002\u9ED8\u8BA4\u8FD4\u56DE\u9875\u9762\u5143\u6570\u636E\u3001\u7ED3\u6784\u5316\u5185\u5BB9\u6458\u8981\u548C\u53EF\u4EA4\u4E92\u5143\u7D20\u6982\u89C8\uFF0C\u9002\u5408\u4F5C\u4E3A\u540E\u7EED\u622A\u56FE\u3001\u4EA4\u4E92\u3001\u7F51\u7EDC\u6392\u67E5\u524D\u7684\u7B2C\u4E00\u6B65\u3002",
29037
+ description: "\u9875\u9762\u5206\u6790\u5165\u53E3\u3002\u8FD4\u56DE\u9875\u9762\u5143\u6570\u636E\u3001\u7ED3\u6784\u5316\u6458\u8981\u548C\u53EF\u4EA4\u4E92\u5143\u7D20\u6982\u89C8\uFF0C\u9002\u5408\u5148\u5FEB\u901F\u4E86\u89E3\u5F53\u524D\u9875\u9762\u3002",
29003
29038
  inputSchema: {
29004
29039
  type: "object",
29005
29040
  properties: {
@@ -29023,9 +29058,41 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
29023
29058
  description: "\u83B7\u53D6 ghost-bridge \u670D\u52A1\u5668\u72B6\u6001\uFF0C\u5305\u62EC\u5F53\u524D WebSocket \u7AEF\u53E3\u3001\u8FDE\u63A5\u72B6\u6001\u7B49",
29024
29059
  inputSchema: { type: "object", properties: {} }
29025
29060
  },
29061
+ {
29062
+ name: "list_tabs",
29063
+ description: "\u5217\u51FA\u5F53\u524D Chrome \u6807\u7B7E\u9875\uFF0C\u5E76\u8FD4\u56DE Ghost Bridge \u5F53\u524D\u76EE\u6807\u6A21\u5F0F\u4E0E\u76EE\u6807\u6807\u7B7E\u9875\u3002",
29064
+ inputSchema: { type: "object", properties: {} }
29065
+ },
29066
+ {
29067
+ name: "get_target_tab",
29068
+ description: "\u67E5\u770B Ghost Bridge \u5F53\u524D\u64CD\u4F5C\u76EE\u6807\u3002\u76EE\u6807\u6A21\u5F0F\u4E3A focused \u65F6\u8DDF\u968F\u5F53\u524D\u805A\u7126\u6807\u7B7E\u9875\uFF1Bpinned \u65F6\u9501\u5B9A\u6307\u5B9A\u6807\u7B7E\u9875\u3002",
29069
+ inputSchema: { type: "object", properties: {} }
29070
+ },
29071
+ {
29072
+ name: "pin_current_tab",
29073
+ description: "\u9501\u5B9A\u5F53\u524D\u805A\u7126\u7684 Chrome \u6807\u7B7E\u9875\u3002\u9501\u5B9A\u540E\u7528\u6237\u5207\u6362\u5230\u5176\u4ED6\u9875\u9762\u4E5F\u4E0D\u4F1A\u6539\u53D8 Ghost Bridge \u7684\u64CD\u4F5C\u76EE\u6807\u3002",
29074
+ inputSchema: { type: "object", properties: {} }
29075
+ },
29076
+ {
29077
+ name: "pin_tab",
29078
+ description: "\u6309 tabId\u3001URL \u7247\u6BB5\u6216\u6807\u9898\u7247\u6BB5\u9501\u5B9A Chrome \u6807\u7B7E\u9875\u3002",
29079
+ inputSchema: {
29080
+ type: "object",
29081
+ properties: {
29082
+ tabId: { type: "number", description: "Chrome \u6807\u7B7E\u9875 ID\uFF0C\u4F18\u5148\u4F7F\u7528" },
29083
+ urlContains: { type: "string", description: "URL \u7247\u6BB5\u3002\u672A\u63D0\u4F9B tabId \u65F6\u6309\u5B83\u5339\u914D" },
29084
+ titleContains: { type: "string", description: "\u6807\u9898\u7247\u6BB5\u3002\u53EF\u4E0E urlContains \u540C\u65F6\u4F7F\u7528" }
29085
+ }
29086
+ }
29087
+ },
29088
+ {
29089
+ name: "unpin_tab",
29090
+ description: "\u89E3\u9664\u9501\u5B9A\uFF0C\u6062\u590D\u8DDF\u968F\u5F53\u524D\u805A\u7126\u6807\u7B7E\u9875\u3002",
29091
+ inputSchema: { type: "object", properties: {} }
29092
+ },
29026
29093
  {
29027
29094
  name: "get_last_error",
29028
- description: "\u83B7\u53D6\u5F53\u524D\u6807\u7B7E\u6700\u8FD1\u7684\u5F02\u5E38/\u62A5\u9519\u5806\u6808\u4E0E\u5143\u6570\u636E\uFF08\u65E0 sourcemap \u53CB\u597D\uFF09\u3002\u9ED8\u8BA4\u53EA\u8FD4\u56DE error \u7EA7\u522B\u7684\u6700\u8FD1 20 \u6761\u3002",
29095
+ description: "\u83B7\u53D6\u76EE\u6807\u6807\u7B7E\u9875\u6700\u8FD1\u7684\u63A7\u5236\u53F0\u3001\u5F02\u5E38\u548C\u7F51\u7EDC\u9519\u8BEF\u4E8B\u4EF6\u3002\u9ED8\u8BA4\u53EA\u8FD4\u56DE error\uFF1B\u5982\u9700\u67E5\u770B console.log / console.warn\uFF0C\u8BF7\u4F20 severity=info / warn / all\u3002",
29029
29096
  inputSchema: {
29030
29097
  type: "object",
29031
29098
  properties: {
@@ -29043,7 +29110,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
29043
29110
  },
29044
29111
  {
29045
29112
  name: "get_script_source",
29046
- description: "\u6293\u53D6\u76EE\u6807\u811A\u672C\u6E90\u7801\uFF08\u538B\u7F29\u7248\uFF09\uFF0C\u8FD4\u56DE\u5B9A\u4F4D\u7247\u6BB5\u4E0E\u53EF\u9009 beautify\uFF0C\u652F\u6301\u6309 URL \u7247\u6BB5\u7B5B\u9009",
29113
+ description: "\u6293\u53D6\u76EE\u6807\u6807\u7B7E\u9875\u7684\u811A\u672C\u6E90\u7801\u7247\u6BB5\uFF0C\u652F\u6301\u6309 URL \u7247\u6BB5\u7B5B\u9009\u548C\u53EF\u9009 beautify\u3002",
29047
29114
  inputSchema: {
29048
29115
  type: "object",
29049
29116
  properties: {
@@ -29067,7 +29134,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
29067
29134
  },
29068
29135
  {
29069
29136
  name: "find_by_string",
29070
- description: "\u5728\u5F53\u524D\u9875\u9762\u811A\u672C\u5185\u6309\u5B57\u7B26\u4E32\u641C\u7D22\uFF0C\u8FD4\u56DE\u5339\u914D\u7684\u4E0A\u4E0B\u6587\u7247\u6BB5\uFF08\u7528\u4E8E\u538B\u7F29\u4EE3\u7801\u5B9A\u4F4D\uFF09",
29137
+ description: "\u5728\u76EE\u6807\u6807\u7B7E\u9875\u811A\u672C\u5185\u6309\u5B57\u7B26\u4E32\u641C\u7D22\uFF0C\u8FD4\u56DE\u5339\u914D\u4E0A\u4E0B\u6587\u3002",
29071
29138
  inputSchema: {
29072
29139
  type: "object",
29073
29140
  properties: {
@@ -29085,7 +29152,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
29085
29152
  },
29086
29153
  {
29087
29154
  name: "eval_script",
29088
- description: "\u5728\u5F53\u524D\u9875\u9762\u6267\u884C\u53EA\u8BFB JS \u8868\u8FBE\u5F0F\uFF08\u8C28\u614E\u4F7F\u7528\uFF09",
29155
+ description: "\u5728\u76EE\u6807\u6807\u7B7E\u9875\u6267\u884C\u53EA\u8BFB JS \u8868\u8FBE\u5F0F\uFF08\u8C28\u614E\u4F7F\u7528\uFF09",
29089
29156
  inputSchema: {
29090
29157
  type: "object",
29091
29158
  properties: { code: { type: "string" } },
@@ -29094,7 +29161,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
29094
29161
  },
29095
29162
  {
29096
29163
  name: "list_network_requests",
29097
- description: "\u5217\u51FA\u6355\u83B7\u7684\u7F51\u7EDC\u8BF7\u6C42\uFF0C\u652F\u6301\u6309 URL\u3001\u65B9\u6CD5\u3001\u72B6\u6001\u3001\u7C7B\u578B\u8FC7\u6EE4\u3002\u9ED8\u8BA4\u6309\u6392\u969C\u4F18\u5148\u7EA7\u6392\u5E8F\uFF1A\u5931\u8D25\u8BF7\u6C42\u3001\u8FDB\u884C\u4E2D\u8BF7\u6C42\u3001XHR/Fetch \u4F1A\u4F18\u5148\u5C55\u793A\u3002\u4E3A\u907F\u514D\u4E0A\u4E0B\u6587\u81A8\u80C0\uFF0Cdata URL \u548C\u8D85\u957F URL \u4F1A\u81EA\u52A8\u6458\u8981\u5316\u3002",
29164
+ description: "\u5217\u51FA\u76EE\u6807\u6807\u7B7E\u9875\u6355\u83B7\u7684\u7F51\u7EDC\u8BF7\u6C42\uFF0C\u652F\u6301\u6309 URL\u3001\u65B9\u6CD5\u3001\u72B6\u6001\u548C\u7C7B\u578B\u8FC7\u6EE4\u3002\u9ED8\u8BA4\u6309\u6392\u969C\u4F18\u5148\u7EA7\u6392\u5E8F\uFF1Bdata URL \u548C\u8D85\u957F URL \u4F1A\u81EA\u52A8\u6458\u8981\u5316\u3002",
29098
29165
  inputSchema: {
29099
29166
  type: "object",
29100
29167
  properties: {
@@ -29113,7 +29180,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
29113
29180
  },
29114
29181
  {
29115
29182
  name: "get_network_detail",
29116
- description: "\u83B7\u53D6\u5355\u4E2A\u7F51\u7EDC\u8BF7\u6C42\u7684\u8BE6\u7EC6\u4FE1\u606F\uFF0C\u5305\u62EC\u8BF7\u6C42\u5934\u3001\u54CD\u5E94\u5934\uFF0C\u53EF\u9009\u83B7\u53D6\u54CD\u5E94\u4F53\u3002\u4E3A\u907F\u514D\u4E0A\u4E0B\u6587\u81A8\u80C0\uFF0Cdata URL \u548C\u8D85\u957F URL \u4F1A\u81EA\u52A8\u6458\u8981\u5316\u3002",
29183
+ description: "\u83B7\u53D6\u5355\u4E2A\u7F51\u7EDC\u8BF7\u6C42\u8BE6\u60C5\uFF0C\u5305\u62EC\u8BF7\u6C42\u5934\u3001\u54CD\u5E94\u5934\u548C\u53EF\u9009\u54CD\u5E94\u4F53\uFF1B\u8D85\u957F URL \u4F1A\u81EA\u52A8\u6458\u8981\u5316\u3002",
29117
29184
  inputSchema: {
29118
29185
  type: "object",
29119
29186
  properties: {
@@ -29130,7 +29197,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
29130
29197
  },
29131
29198
  {
29132
29199
  name: "perf_metrics",
29133
- description: "\u83B7\u53D6\u9875\u9762\u6027\u80FD\u6307\u6807\uFF1A\u5305\u542B\u5F15\u64CE\u7EA7\u6307\u6807\uFF08JS\u5806\u5185\u5B58\u3001DOM\u8282\u70B9\u6570\u3001Layout\u6B21\u6570\u3001\u811A\u672C\u6267\u884C\u65F6\u95F4\uFF09\u3001Web Vitals\uFF08FCP\u3001TTFB\u3001DOMContentLoaded\u3001Long Tasks\uFF09\u548C\u8D44\u6E90\u52A0\u8F7D\u6458\u8981\u3002\u7528\u4E8E\u8BCA\u65AD\u9875\u9762\u5361\u987F\u3001\u5185\u5B58\u5360\u7528\u8FC7\u9AD8\u3001\u52A0\u8F7D\u7F13\u6162\u7B49\u6027\u80FD\u95EE\u9898\u3002",
29200
+ description: "\u83B7\u53D6\u9875\u9762\u6027\u80FD\u6307\u6807\uFF0C\u5305\u62EC\u5F15\u64CE\u7EA7\u6307\u6807\u3001Web Vitals \u548C\u8D44\u6E90\u52A0\u8F7D\u6458\u8981\u3002",
29134
29201
  inputSchema: {
29135
29202
  type: "object",
29136
29203
  properties: {
@@ -29147,7 +29214,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
29147
29214
  },
29148
29215
  {
29149
29216
  name: "capture_screenshot",
29150
- description: "\u3010\u63A8\u8350\u7528\u4E8E\u89C6\u89C9\u5206\u6790\u3011\u622A\u53D6\u5F53\u524D\u9875\u9762\u7684\u622A\u56FE\uFF0C\u8FD4\u56DE base64 \u56FE\u7247\u3002\u9002\u7528\u4E8E\uFF1A1) \u67E5\u770B\u9875\u9762\u5B9E\u9645\u89C6\u89C9\u6548\u679C 2) \u6392\u67E5 UI/\u6837\u5F0F/\u5E03\u5C40/\u989C\u8272\u95EE\u9898 3) \u9A8C\u8BC1\u9875\u9762\u6E32\u67D3 4) \u5206\u6790\u5143\u7D20\u4F4D\u7F6E\u548C\u95F4\u8DDD 5) \u67E5\u770B\u56FE\u7247/\u56FE\u6807\u7B49\u89C6\u89C9\u5185\u5BB9\u3002\u5F53\u7528\u6237\u8BF4\u201C\u770B\u770B\u8FD9\u4E2A\u9875\u9762\u957F\u4EC0\u4E48\u6837\u201D\u201C\u5E2E\u6211\u5206\u6790\u754C\u9762/\u5E03\u5C40/\u6837\u5F0F\u201D\u65F6\uFF0C\u5E94\u4F18\u5148\u4F7F\u7528\u6B64\u5DE5\u5177\uFF0C\u65E0\u9700\u7528\u6237\u663E\u5F0F\u63D0\u5230 ghost-bridge\u3002\u5F53\u9700\u8981\u770B\u5230\u9875\u9762\u300C\u957F\u4EC0\u4E48\u6837\u300D\u65F6\u4F7F\u7528\u6B64\u5DE5\u5177\u3002\u9ED8\u8BA4\u4F18\u5148\u4F7F\u7528\u66F4\u7701\u4F20\u8F93\u7684 JPEG\uFF1A\u666E\u901A\u622A\u56FE\u9ED8\u8BA4 quality 80\uFF0C\u5B8C\u6574\u957F\u622A\u56FE\u9ED8\u8BA4 quality 70\u3002\u5F53\u9700\u8981\u68C0\u67E5\u6587\u5B57\u6E05\u6670\u5EA6\u30011px \u7EC6\u7EBF\u3001\u56FE\u6807\u8FB9\u7F18\u3001\u900F\u660E\u80CC\u666F\u6216\u50CF\u7D20\u7EA7\u7EC6\u8282\u65F6\uFF0C\u5E94\u4F18\u5148\u4F7F\u7528 PNG\u3002\u5982\u4EC5\u9700\u6587\u672C/\u94FE\u63A5\u7B49\u4FE1\u606F\uFF0C\u5EFA\u8BAE\u4F7F\u7528\u66F4\u5FEB\u7684 get_page_content\u3002",
29217
+ description: "\u622A\u53D6\u76EE\u6807\u6807\u7B7E\u9875\u622A\u56FE\uFF0C\u9002\u5408\u770B\u9875\u9762\u5B9E\u9645\u89C6\u89C9\u6548\u679C\u3001UI \u6837\u5F0F\u548C\u5E03\u5C40\u3002\u9ED8\u8BA4\u4F18\u5148\u4F7F\u7528 JPEG\uFF1B\u9700\u8981\u6587\u5B57\u3001\u7EC6\u7EBF\u6216\u900F\u660E\u80CC\u666F\u7EC6\u8282\u65F6\u6539\u7528 PNG\u3002",
29151
29218
  inputSchema: {
29152
29219
  type: "object",
29153
29220
  properties: {
@@ -29179,7 +29246,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
29179
29246
  },
29180
29247
  {
29181
29248
  name: "get_page_content",
29182
- description: "\u3010\u63A8\u8350\u7528\u4E8E\u5FEB\u901F\u83B7\u53D6\u9875\u9762\u5185\u5BB9\u3011\u63D0\u53D6\u5F53\u524D\u9875\u9762\u7684\u6587\u672C\u3001HTML \u6216\u7ED3\u6784\u5316\u6570\u636E\u3002\u6BD4 capture_screenshot \u66F4\u5FEB\u66F4\u8F7B\u91CF\uFF0C\u9002\u7528\u4E8E\uFF1A1) \u83B7\u53D6\u9875\u9762\u6587\u5B57\u5185\u5BB9 2) \u63D0\u53D6\u94FE\u63A5/\u6309\u94AE/\u8868\u5355\u7B49\u5143\u7D20 3) \u5206\u6790 DOM \u7ED3\u6784 4) \u83B7\u53D6\u9875\u9762\u5143\u6570\u636E\uFF08title/description\uFF09\u3002\u5F53\u7528\u6237\u8BF4\u201C\u5206\u6790\u8FD9\u4E2A\u9875\u9762/\u7F51\u7AD9\u201D\u201C\u770B\u770B\u9875\u9762\u91CC\u6709\u4EC0\u4E48\u5185\u5BB9\u201D\u4E14\u4E0D\u5F3A\u8C03\u89C6\u89C9\u6548\u679C\u65F6\uFF0C\u4F18\u5148\u4F7F\u7528\u6B64\u5DE5\u5177\uFF0C\u65E0\u9700\u7528\u6237\u663E\u5F0F\u63D0\u5230 ghost-bridge\u3002\u5F53\u9700\u8981\u6587\u672C\u4FE1\u606F\u800C\u975E\u89C6\u89C9\u6548\u679C\u65F6\uFF0C\u4F18\u5148\u4F7F\u7528\u6B64\u5DE5\u5177\u3002\u6CE8\u610F\uFF1A\u4E0D\u652F\u6301 iframe \u5185\u5BB9\uFF0C\u4E0D\u53CD\u6620 CSS \u6837\u5F0F\u3002",
29249
+ description: "\u63D0\u53D6\u76EE\u6807\u6807\u7B7E\u9875\u7684\u6587\u672C\u3001HTML \u6216\u7ED3\u6784\u5316\u6570\u636E\u3002\u6BD4\u622A\u56FE\u66F4\u8F7B\u91CF\uFF0C\u9002\u5408\u5148\u770B\u6587\u5B57\u3001DOM \u7ED3\u6784\u548C\u9875\u9762\u5143\u6570\u636E\uFF1B\u4E0D\u53CD\u6620 CSS\uFF0C\u4E5F\u4E0D\u542B iframe \u5185\u5BB9\u3002",
29183
29250
  inputSchema: {
29184
29251
  type: "object",
29185
29252
  properties: {
@@ -29205,7 +29272,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
29205
29272
  },
29206
29273
  {
29207
29274
  name: "get_interactive_snapshot",
29208
- description: "\u3010\u64CD\u4F5C\u9875\u9762\u524D\u5FC5\u987B\u5148\u8C03\u7528\u3011\u626B\u63CF\u5F53\u524D\u9875\u9762\u6240\u6709\u53EF\u89C1\u7684\u53EF\u4EA4\u4E92\u5143\u7D20\uFF08\u6309\u94AE/\u94FE\u63A5/\u8F93\u5165\u6846/\u4E0B\u62C9\u6846\u7B49\uFF09\uFF0C\u8FD4\u56DE\u5E26\u6709 ref \u77ED\u6807\u8BC6\uFF08\u5982 e1, e2, e3\uFF09\u7684\u7CBE\u7B80\u5217\u8868\uFF0C\u5305\u542B\u5143\u7D20\u7C7B\u578B\u3001\u6587\u672C\u548C\u4F4D\u7F6E\u3002Token \u6781\u7701\uFF08\u901A\u5E38 < 1000 tokens\uFF09\uFF0C\u4E13\u4E3A AI \u64CD\u4F5C\u9875\u9762\u800C\u8BBE\u8BA1\u3002\u5F53\u7528\u6237\u8981\u6C42\u70B9\u51FB\u3001\u8F93\u5165\u3001\u767B\u5F55\u3001\u63D0\u4EA4\u8868\u5355\u3001\u6253\u5F00\u83DC\u5355\u7B49\u64CD\u4F5C\u65F6\uFF0C\u5E94\u4E3B\u52A8\u4F7F\u7528\u6B64\u5DE5\u5177\u5F00\u59CB\u5B9A\u4F4D\u5143\u7D20\uFF0C\u65E0\u9700\u7528\u6237\u663E\u5F0F\u63D0\u5230 ghost-bridge\u3002\u83B7\u53D6\u540E\u53EF\u901A\u8FC7 dispatch_action \u5DE5\u5177\u4F7F\u7528 ref \u6807\u8BC6\u6765\u70B9\u51FB\u3001\u586B\u5199\u3001\u6309\u952E\u7B49\u3002\u652F\u6301 Shadow DOM \u7A7F\u900F\u3002\n\u26A0\uFE0F \u4EC5\u7528\u4E8E\u4EA4\u4E92\u64CD\u4F5C\u524D\u7684\u5143\u7D20\u5B9A\u4F4D\u3002\u5982\u9700\u6392\u67E5 UI/CSS \u5E03\u5C40\u95EE\u9898\uFF0C\u8BF7\u4F7F\u7528 capture_screenshot \u6216 get_page_content\u3002",
29275
+ description: "\u626B\u63CF\u5F53\u524D\u9875\u9762\u53EF\u89C1\u7684\u53EF\u4EA4\u4E92\u5143\u7D20\uFF0C\u8FD4\u56DE\u5E26 ref \u7684\u7CBE\u7B80\u5217\u8868\uFF0C\u4F9B\u540E\u7EED dispatch_action \u4F7F\u7528\u3002\u652F\u6301 Shadow DOM\uFF1B\u4EC5\u7528\u4E8E\u4EA4\u4E92\u5B9A\u4F4D\u3002",
29209
29276
  inputSchema: {
29210
29277
  type: "object",
29211
29278
  properties: {
@@ -29226,7 +29293,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
29226
29293
  },
29227
29294
  {
29228
29295
  name: "dispatch_action",
29229
- description: "\u3010\u64CD\u4F5C\u9875\u9762\u5143\u7D20\u3011\u5BF9 get_interactive_snapshot \u8FD4\u56DE\u7684\u5143\u7D20\u6267\u884C\u52A8\u4F5C\u3002\u901A\u8FC7 ref \u6807\u8BC6\uFF08\u5982 e1, e5\uFF09\u7CBE\u51C6\u5B9A\u4F4D\u5143\u7D20\uFF0C\u4F7F\u7528 CDP \u7269\u7406\u7EA7\u6A21\u62DF\u6267\u884C\u64CD\u4F5C\uFF0C\u517C\u5BB9\u6240\u6709\u524D\u7AEF\u6846\u67B6\uFF08React/Vue/Angular\uFF09\uFF0C\u6210\u529F\u7387\u6781\u9AD8\u3002\n\u5F53\u7528\u6237\u660E\u786E\u5E0C\u671B\u5728\u9875\u9762\u4E0A\u6267\u884C\u70B9\u51FB\u3001\u8F93\u5165\u3001\u56DE\u8F66\u3001\u6EDA\u52A8\u3001\u9009\u62E9\u7B49\u64CD\u4F5C\u65F6\uFF0C\u5E94\u7ED3\u5408 get_interactive_snapshot \u4E3B\u52A8\u4F7F\u7528\u6B64\u5DE5\u5177\uFF0C\u65E0\u9700\u7528\u6237\u663E\u5F0F\u63D0\u5230 ghost-bridge\u3002\n\u652F\u6301\u7684\u52A8\u4F5C\uFF1Aclick\uFF08\u70B9\u51FB\uFF09\u3001fill\uFF08\u586B\u5199\u8F93\u5165\u6846\uFF09\u3001press\uFF08\u6309\u952E\u5982 Enter\uFF09\u3001scroll\uFF08\u6EDA\u52A8\uFF09\u3001select\uFF08\u4E0B\u62C9\u9009\u62E9\uFF09\u3001hover\uFF08\u60AC\u505C\uFF09\u3001focus\uFF08\u805A\u7126\uFF09\u3002\n\u26A0\uFE0F \u4F7F\u7528\u524D\u5FC5\u987B\u5148\u8C03\u7528 get_interactive_snapshot \u83B7\u53D6\u5143\u7D20\u5217\u8868\u3002\u64CD\u4F5C\u540E\u5EFA\u8BAE\u7528 capture_screenshot \u6216\u518D\u6B21 get_interactive_snapshot \u9A8C\u8BC1\u7ED3\u679C\u3002",
29296
+ description: "\u5BF9 get_interactive_snapshot \u8FD4\u56DE\u7684\u5143\u7D20\u6267\u884C\u70B9\u51FB\u3001\u8F93\u5165\u3001\u6309\u952E\u3001\u6EDA\u52A8\u3001\u9009\u62E9\u3001\u60AC\u505C\u6216\u805A\u7126\u3002\u26A0\uFE0F \u4F7F\u7528\u524D\u5FC5\u987B\u5148\u8C03\u7528 get_interactive_snapshot \u83B7\u53D6 ref\uFF1B\u64CD\u4F5C\u540E\u5EFA\u8BAE\u518D\u9A8C\u8BC1\u9875\u9762\u72B6\u6001\u3002",
29230
29297
  inputSchema: {
29231
29298
  type: "object",
29232
29299
  properties: {
@@ -29305,13 +29372,19 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
29305
29372
  };
29306
29373
  }
29307
29374
  if (name === "get_server_info") {
29308
- let chromeOk, clientsCount;
29375
+ let chromeOk, clientsCount, mainStatus;
29309
29376
  if (isMainInstance) {
29310
29377
  chromeOk = !!chromeConnection;
29311
29378
  clientsCount = mcpClients.size;
29379
+ mainStatus = {
29380
+ pid: process.pid,
29381
+ version: GHOST_BRIDGE_VERSION,
29382
+ serverPath: SERVER_ENTRY_PATH,
29383
+ startedAt: SERVER_STARTED_AT
29384
+ };
29312
29385
  } else {
29313
29386
  try {
29314
- const mainStatus = await askMainInstance("_getMainStatus");
29387
+ mainStatus = await askMainInstance("_getMainStatus");
29315
29388
  chromeOk = mainStatus.chromeConnected;
29316
29389
  clientsCount = mainStatus.mcpClientsCount;
29317
29390
  } catch {
@@ -29330,6 +29403,11 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
29330
29403
  wsPort: actualPort,
29331
29404
  wsUrl: `ws://localhost:${actualPort}`,
29332
29405
  pid: process.pid,
29406
+ serverPath: SERVER_ENTRY_PATH,
29407
+ mainPid: mainStatus?.pid,
29408
+ mainVersion: mainStatus?.version,
29409
+ mainServerPath: mainStatus?.serverPath,
29410
+ mainStartedAt: mainStatus?.startedAt,
29333
29411
  chromeConnected: chromeOk,
29334
29412
  mcpClientsCount: clientsCount,
29335
29413
  portInfoFile: PORT_INFO_FILE,
@@ -29339,6 +29417,27 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
29339
29417
  ]
29340
29418
  };
29341
29419
  }
29420
+ if (name === "list_tabs") {
29421
+ const res = await askChrome("listTabs");
29422
+ return { content: [{ type: "text", text: jsonText(res) }] };
29423
+ }
29424
+ if (name === "get_target_tab") {
29425
+ const res = await askChrome("getTargetTab");
29426
+ return { content: [{ type: "text", text: jsonText(res) }] };
29427
+ }
29428
+ if (name === "pin_current_tab") {
29429
+ const res = await askChrome("pinCurrentTab", {}, { timeoutMs: 1e4 });
29430
+ return { content: [{ type: "text", text: jsonText(res) }] };
29431
+ }
29432
+ if (name === "pin_tab") {
29433
+ const { tabId, urlContains, titleContains } = args;
29434
+ const res = await askChrome("pinTab", { tabId, urlContains, titleContains }, { timeoutMs: 1e4 });
29435
+ return { content: [{ type: "text", text: jsonText(res) }] };
29436
+ }
29437
+ if (name === "unpin_tab") {
29438
+ const res = await askChrome("unpinTab", {}, { timeoutMs: 1e4 });
29439
+ return { content: [{ type: "text", text: jsonText(res) }] };
29440
+ }
29342
29441
  if (name === "get_last_error") {
29343
29442
  const { severity = "error", limit = 20 } = args;
29344
29443
  const data = await askChrome("getLastError", { severity, limit });