ghost-bridge 0.8.0 → 0.9.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/README.md +15 -1
- package/dist/server.js +158 -38
- package/extension/background.js +529 -216
- package/extension/manifest.json +3 -2
- package/extension/offscreen.js +149 -10
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -16,6 +16,7 @@ Most browser-capable AI tools start a separate browser. Ghost Bridge connects AI
|
|
|
16
16
|
- Inspect page structure, text, screenshots, errors, and network traffic
|
|
17
17
|
- Search and extract script sources, even in production bundles
|
|
18
18
|
- Click, type, scroll, and submit forms on the current page
|
|
19
|
+
- Bind multiple Chrome tabs as named targets and operate them independently
|
|
19
20
|
- Share one Chrome transport across multiple MCP clients
|
|
20
21
|
|
|
21
22
|
## Quick Start
|
|
@@ -94,6 +95,9 @@ Typical prompts:
|
|
|
94
95
|
| `get_page_content` | Text, HTML, and structured DOM extraction |
|
|
95
96
|
| `get_interactive_snapshot` | Find clickable and editable elements |
|
|
96
97
|
| `dispatch_action` | Click, fill, press, scroll, hover, select |
|
|
98
|
+
| `bind_tab` | Bind a Chrome tab as a named target such as `cases` or `app` |
|
|
99
|
+
| `unbind_tab` | Remove a named target binding |
|
|
100
|
+
| `list_targets` | Show named targets and their per-tab session status |
|
|
97
101
|
| `pin_current_tab` | Keep Ghost Bridge attached to the current tab while you browse elsewhere |
|
|
98
102
|
| `pin_tab` | Pin a target tab by tab ID, URL fragment, or title fragment |
|
|
99
103
|
| `unpin_tab` | Return to following the focused tab |
|
|
@@ -117,9 +121,19 @@ Recommended flow:
|
|
|
117
121
|
|
|
118
122
|
Notes:
|
|
119
123
|
|
|
124
|
+
- Use `bind_tab` when a workflow spans multiple pages. For example, bind a checklist page as `cases` and a business page as `app`, then call tools with `target: "cases"` or `target: "app"`.
|
|
125
|
+
- All browser tools accept an optional `target` parameter. When named targets are bound, `dispatch_action` requires `target` so refs from one page are not accidentally used on another page.
|
|
120
126
|
- Use `pin_current_tab` when you are debugging a page and need to switch to other tabs without changing the AI target. Use `unpin_tab` to restore the original follow-focused-tab behavior.
|
|
121
127
|
- `list_network_requests` and `get_network_detail` automatically summarize `data:` URLs and very long URLs so inline images or oversized query strings do not overwhelm model context
|
|
122
128
|
|
|
129
|
+
Multi-page example:
|
|
130
|
+
|
|
131
|
+
```text
|
|
132
|
+
Bind the current checklist tab as cases.
|
|
133
|
+
Bind the tab whose title contains "Orders" as app.
|
|
134
|
+
Read the next case from target cases, operate target app, then mark the case passed or failed back on target cases.
|
|
135
|
+
```
|
|
136
|
+
|
|
123
137
|
## Configuration
|
|
124
138
|
|
|
125
139
|
| Setting | Default | Notes |
|
|
@@ -135,7 +149,7 @@ flowchart LR
|
|
|
135
149
|
A["AI Client<br/>Claude / Codex / Cursor"]
|
|
136
150
|
B["Ghost Bridge MCP Server<br/>server.js"]
|
|
137
151
|
C["Chrome Extension<br/>background.js"]
|
|
138
|
-
D["Browser
|
|
152
|
+
D["Browser Tabs<br/>Target Sessions"]
|
|
139
153
|
|
|
140
154
|
A <-->|"stdio"| B
|
|
141
155
|
B <-->|"WebSocket"| C
|
package/dist/server.js
CHANGED
|
@@ -28549,6 +28549,8 @@ var actualPort = BASE_PORT;
|
|
|
28549
28549
|
var isMainInstance = false;
|
|
28550
28550
|
var pendingRequests = /* @__PURE__ */ new Map();
|
|
28551
28551
|
var mcpClients = /* @__PURE__ */ new Set();
|
|
28552
|
+
var mcpClientSeq = 0;
|
|
28553
|
+
var LOCAL_CLIENT_ID = "local";
|
|
28552
28554
|
function log(msg) {
|
|
28553
28555
|
console.error(`[ghost-bridge] ${msg}`);
|
|
28554
28556
|
}
|
|
@@ -28561,7 +28563,8 @@ function buildIdentityPayload() {
|
|
|
28561
28563
|
port: actualPort,
|
|
28562
28564
|
version: GHOST_BRIDGE_VERSION,
|
|
28563
28565
|
serverPath: SERVER_ENTRY_PATH,
|
|
28564
|
-
startedAt: SERVER_STARTED_AT
|
|
28566
|
+
startedAt: SERVER_STARTED_AT,
|
|
28567
|
+
capabilities: ["heartbeat"]
|
|
28565
28568
|
};
|
|
28566
28569
|
}
|
|
28567
28570
|
function getServiceMismatch(probe) {
|
|
@@ -28769,8 +28772,14 @@ async function initWebSocketService() {
|
|
|
28769
28772
|
return wss2;
|
|
28770
28773
|
}
|
|
28771
28774
|
var wss = await initWebSocketService();
|
|
28775
|
+
var wsPingInterval = null;
|
|
28776
|
+
var WS_PING_INTERVAL_MS = 3e4;
|
|
28772
28777
|
if (wss) {
|
|
28773
28778
|
wss.on("connection", (ws, req) => {
|
|
28779
|
+
ws.isAlive = true;
|
|
28780
|
+
ws.on("pong", () => {
|
|
28781
|
+
ws.isAlive = true;
|
|
28782
|
+
});
|
|
28774
28783
|
const url2 = new URL(req.url || "/", "http://localhost");
|
|
28775
28784
|
const token = url2.searchParams.get("token") || "";
|
|
28776
28785
|
const role = url2.searchParams.get("role") || "";
|
|
@@ -28786,7 +28795,9 @@ if (wss) {
|
|
|
28786
28795
|
}
|
|
28787
28796
|
log(`\u8FDE\u63A5\u9A8C\u8BC1\u901A\u8FC7 (token: ${token})`);
|
|
28788
28797
|
if (role === "mcp-client") {
|
|
28789
|
-
|
|
28798
|
+
const clientId = `c${++mcpClientSeq}`;
|
|
28799
|
+
ws.ghostClientId = clientId;
|
|
28800
|
+
log(`\u{1F4E1} MCP \u5BA2\u6237\u7AEF\u5DF2\u8FDE\u63A5 (${clientId})`);
|
|
28790
28801
|
mcpClients.add(ws);
|
|
28791
28802
|
ws.send(JSON.stringify(buildIdentityPayload()));
|
|
28792
28803
|
ws.on("message", (data) => {
|
|
@@ -28814,15 +28825,26 @@ if (wss) {
|
|
|
28814
28825
|
return;
|
|
28815
28826
|
}
|
|
28816
28827
|
if (msg.id) {
|
|
28817
|
-
|
|
28828
|
+
const wireId = `${clientId}:${msg.id}`;
|
|
28829
|
+
pendingRequests.set(wireId, { source: ws, originalId: msg.id });
|
|
28830
|
+
chromeConnection.send(JSON.stringify({
|
|
28831
|
+
id: wireId,
|
|
28832
|
+
command: msg.command,
|
|
28833
|
+
params: msg.params,
|
|
28834
|
+
clientId,
|
|
28835
|
+
...WS_TOKEN ? { token: WS_TOKEN } : {}
|
|
28836
|
+
}));
|
|
28837
|
+
} else {
|
|
28838
|
+
chromeConnection.send(data);
|
|
28818
28839
|
}
|
|
28819
|
-
chromeConnection.send(data);
|
|
28820
28840
|
} catch {
|
|
28821
28841
|
}
|
|
28822
28842
|
});
|
|
28823
28843
|
ws.on("close", () => {
|
|
28824
|
-
log(
|
|
28844
|
+
log(`\u{1F4E1} MCP \u5BA2\u6237\u7AEF\u5DF2\u65AD\u5F00 (${ws.ghostClientId})`);
|
|
28825
28845
|
mcpClients.delete(ws);
|
|
28846
|
+
askChrome("_clientDisconnected", { clientId: ws.ghostClientId }, { timeoutMs: 3e3 }).catch(() => {
|
|
28847
|
+
});
|
|
28826
28848
|
});
|
|
28827
28849
|
} else {
|
|
28828
28850
|
if (chromeConnection && chromeConnection !== ws && chromeConnection.readyState === import_websocket.default.OPEN) {
|
|
@@ -28840,11 +28862,17 @@ if (wss) {
|
|
|
28840
28862
|
ws.on("message", (data) => {
|
|
28841
28863
|
try {
|
|
28842
28864
|
const msg = JSON.parse(data.toString());
|
|
28865
|
+
if (msg.type === "heartbeat") {
|
|
28866
|
+
ws.send(JSON.stringify({ type: "heartbeat_ack", ts: msg.ts || Date.now() }));
|
|
28867
|
+
return;
|
|
28868
|
+
}
|
|
28843
28869
|
if (msg.id && pendingRequests.has(msg.id)) {
|
|
28844
28870
|
const pending = pendingRequests.get(msg.id);
|
|
28845
|
-
if (pending.source
|
|
28871
|
+
if (pending.source) {
|
|
28846
28872
|
pendingRequests.delete(msg.id);
|
|
28847
|
-
pending.source.
|
|
28873
|
+
if (pending.source.readyState === import_websocket.default.OPEN) {
|
|
28874
|
+
pending.source.send(JSON.stringify({ ...msg, id: pending.originalId }));
|
|
28875
|
+
}
|
|
28848
28876
|
return;
|
|
28849
28877
|
}
|
|
28850
28878
|
}
|
|
@@ -28852,14 +28880,38 @@ if (wss) {
|
|
|
28852
28880
|
}
|
|
28853
28881
|
handleIncoming(data);
|
|
28854
28882
|
});
|
|
28855
|
-
ws.on("close", () => {
|
|
28856
|
-
|
|
28883
|
+
ws.on("close", (code, reason) => {
|
|
28884
|
+
const reasonText = reason ? reason.toString() : "";
|
|
28885
|
+
log(`\u{1F310} Chrome \u8FDE\u63A5\u5DF2\u5173\u95ED (code=${code}${reasonText ? ` reason="${reasonText}"` : ""})`);
|
|
28886
|
+
if (chromeConnection !== ws) {
|
|
28887
|
+
log("\u5FFD\u7565\u65E7 Chrome \u8FDE\u63A5\u7684\u5173\u95ED\u4E8B\u4EF6");
|
|
28888
|
+
return;
|
|
28889
|
+
}
|
|
28857
28890
|
chromeConnection = null;
|
|
28858
|
-
activeConnection
|
|
28891
|
+
if (activeConnection === ws) {
|
|
28892
|
+
activeConnection = null;
|
|
28893
|
+
}
|
|
28859
28894
|
failAllPending("Chrome \u8FDE\u63A5\u65AD\u5F00");
|
|
28860
28895
|
});
|
|
28896
|
+
ws.on("error", (err) => {
|
|
28897
|
+
log(`\u{1F310} Chrome \u8FDE\u63A5\u9519\u8BEF: ${err.message}`);
|
|
28898
|
+
});
|
|
28861
28899
|
}
|
|
28862
28900
|
});
|
|
28901
|
+
wsPingInterval = setInterval(() => {
|
|
28902
|
+
for (const client of wss.clients) {
|
|
28903
|
+
if (client.isAlive === false) {
|
|
28904
|
+
log("\u{1F310} \u5FC3\u8DF3\u8D85\u65F6\uFF0C\u4E3B\u52A8\u65AD\u5F00\u6B7B\u8FDE\u63A5");
|
|
28905
|
+
client.terminate();
|
|
28906
|
+
continue;
|
|
28907
|
+
}
|
|
28908
|
+
client.isAlive = false;
|
|
28909
|
+
try {
|
|
28910
|
+
client.ping();
|
|
28911
|
+
} catch {
|
|
28912
|
+
}
|
|
28913
|
+
}
|
|
28914
|
+
}, WS_PING_INTERVAL_MS);
|
|
28863
28915
|
} else {
|
|
28864
28916
|
log(`\u{1F4E1} \u4F5C\u4E3A\u5BA2\u6237\u7AEF\u8FDE\u63A5\u5230\u4E3B\u5B9E\u4F8B (\u7AEF\u53E3 ${actualPort})...`);
|
|
28865
28917
|
connectToMainInstance();
|
|
@@ -28970,7 +29022,7 @@ async function askMainInstance(command, params = {}) {
|
|
|
28970
29022
|
async function askChrome(command, params = {}, options = {}) {
|
|
28971
29023
|
if (!activeConnection) throw new Error("Chrome \u672A\u8FDE\u63A5\uFF0C\u8BF7\u786E\u8BA4\u6D4F\u89C8\u5668\u5F00\u542F\u4E14\u6269\u5C55\u5DF2\u542F\u7528");
|
|
28972
29024
|
const id = crypto.randomUUID();
|
|
28973
|
-
const payload = { id, command, params };
|
|
29025
|
+
const payload = { id, command, params, clientId: LOCAL_CLIENT_ID };
|
|
28974
29026
|
if (WS_TOKEN) payload.token = WS_TOKEN;
|
|
28975
29027
|
const timeoutMs = options.timeoutMs || RESPONSE_TIMEOUT;
|
|
28976
29028
|
return new Promise((resolve, reject) => {
|
|
@@ -28991,6 +29043,10 @@ async function askChrome(command, params = {}, options = {}) {
|
|
|
28991
29043
|
function jsonText(data) {
|
|
28992
29044
|
return typeof data === "string" ? data : JSON.stringify(data);
|
|
28993
29045
|
}
|
|
29046
|
+
var TARGET_ARG = {
|
|
29047
|
+
type: "string",
|
|
29048
|
+
description: "\u547D\u540D\u6D4F\u89C8\u5668\u76EE\u6807\uFF0C\u5982 cases/app\u3002\u5148\u7528 bind_tab \u7ED1\u5B9A\uFF1B\u4E0D\u6307\u5B9A\u5219\u4F7F\u7528\u9ED8\u8BA4 focused/pinned \u76EE\u6807\u3002\u7ED1\u5B9A\u4E0E pin \u5747\u6309\u4F1A\u8BDD\u9694\u79BB"
|
|
29049
|
+
};
|
|
28994
29050
|
function buildSnippet(source, line, column, { beautifyEnabled = true, contextLines = 20 } = {}) {
|
|
28995
29051
|
const result = {};
|
|
28996
29052
|
if (!source) {
|
|
@@ -29038,6 +29094,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
29038
29094
|
inputSchema: {
|
|
29039
29095
|
type: "object",
|
|
29040
29096
|
properties: {
|
|
29097
|
+
target: TARGET_ARG,
|
|
29041
29098
|
selector: {
|
|
29042
29099
|
type: "string",
|
|
29043
29100
|
description: "CSS \u9009\u62E9\u5668\uFF0C\u9650\u5B9A\u5206\u6790\u8303\u56F4\u3002\u4E0D\u6307\u5B9A\u5219\u5206\u6790\u6574\u4E2A\u9875\u9762"
|
|
@@ -29063,6 +29120,36 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
29063
29120
|
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
29121
|
inputSchema: { type: "object", properties: {} }
|
|
29065
29122
|
},
|
|
29123
|
+
{
|
|
29124
|
+
name: "bind_tab",
|
|
29125
|
+
description: "\u6309 tabId\u3001URL \u7247\u6BB5\u6216\u6807\u9898\u7247\u6BB5\u628A Chrome \u6807\u7B7E\u9875\u7ED1\u5B9A\u4E3A\u547D\u540D target\uFF0C\u4F8B\u5982 cases \u6216 app\u3002target \u547D\u540D\u7A7A\u95F4\u6309 MCP \u4F1A\u8BDD\u9694\u79BB\uFF1A\u591A\u4E2A Agent / \u540C\u4E00 Agent \u7684\u591A\u4E2A\u4F1A\u8BDD\u5404\u81EA\u7ED1\u5B9A\u7684 target\uFF08\u542B\u540C\u540D\uFF09\u4E92\u4E0D\u5E72\u6270\u3002",
|
|
29126
|
+
inputSchema: {
|
|
29127
|
+
type: "object",
|
|
29128
|
+
properties: {
|
|
29129
|
+
name: { type: "string", description: "target \u540D\u79F0\uFF0C\u5982 cases/app\u3002\u53EA\u80FD\u5305\u542B\u5B57\u6BCD\u3001\u6570\u5B57\u3001\u4E0B\u5212\u7EBF\u548C\u8FDE\u5B57\u7B26" },
|
|
29130
|
+
tabId: { type: "number", description: "Chrome \u6807\u7B7E\u9875 ID\uFF0C\u4F18\u5148\u4F7F\u7528" },
|
|
29131
|
+
urlContains: { type: "string", description: "URL \u7247\u6BB5\u3002\u672A\u63D0\u4F9B tabId \u65F6\u6309\u5B83\u5339\u914D" },
|
|
29132
|
+
titleContains: { type: "string", description: "\u6807\u9898\u7247\u6BB5\u3002\u53EF\u4E0E urlContains \u540C\u65F6\u4F7F\u7528" }
|
|
29133
|
+
},
|
|
29134
|
+
required: ["name"]
|
|
29135
|
+
}
|
|
29136
|
+
},
|
|
29137
|
+
{
|
|
29138
|
+
name: "unbind_tab",
|
|
29139
|
+
description: "\u89E3\u7ED1\u4E00\u4E2A\u547D\u540D target\u3002\u82E5\u8BE5 tab \u6CA1\u6709\u5176\u4ED6 target \u5F15\u7528\uFF0C\u4F1A\u91CA\u653E\u5BF9\u5E94 debugger session\u3002",
|
|
29140
|
+
inputSchema: {
|
|
29141
|
+
type: "object",
|
|
29142
|
+
properties: {
|
|
29143
|
+
name: { type: "string", description: "\u8981\u89E3\u7ED1\u7684 target \u540D\u79F0" }
|
|
29144
|
+
},
|
|
29145
|
+
required: ["name"]
|
|
29146
|
+
}
|
|
29147
|
+
},
|
|
29148
|
+
{
|
|
29149
|
+
name: "list_targets",
|
|
29150
|
+
description: "\u67E5\u770B\u5F53\u524D\u5DF2\u7ED1\u5B9A\u7684\u547D\u540D targets\uFF0C\u4EE5\u53CA\u9ED8\u8BA4 focused/pinned \u76EE\u6807\u72B6\u6001\u3002",
|
|
29151
|
+
inputSchema: { type: "object", properties: {} }
|
|
29152
|
+
},
|
|
29066
29153
|
{
|
|
29067
29154
|
name: "get_target_tab",
|
|
29068
29155
|
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",
|
|
@@ -29075,7 +29162,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
29075
29162
|
},
|
|
29076
29163
|
{
|
|
29077
29164
|
name: "pin_tab",
|
|
29078
|
-
description: "\u6309 tabId\u3001URL \u7247\u6BB5\u6216\u6807\u9898\u7247\u6BB5\u9501\u5B9A Chrome \u6807\u7B7E\u9875\u3002",
|
|
29165
|
+
description: "\u6309 tabId\u3001URL \u7247\u6BB5\u6216\u6807\u9898\u7247\u6BB5\u9501\u5B9A Chrome \u6807\u7B7E\u9875\u3002pin \u72B6\u6001\u6309 MCP \u4F1A\u8BDD\u9694\u79BB\uFF1A\u591A\u4E2A Agent / \u591A\u4E2A\u4F1A\u8BDD\u5404\u81EA pin \u7684\u9875\u9762\u4E92\u4E0D\u8986\u76D6\u3002",
|
|
29079
29166
|
inputSchema: {
|
|
29080
29167
|
type: "object",
|
|
29081
29168
|
properties: {
|
|
@@ -29096,6 +29183,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
29096
29183
|
inputSchema: {
|
|
29097
29184
|
type: "object",
|
|
29098
29185
|
properties: {
|
|
29186
|
+
target: TARGET_ARG,
|
|
29099
29187
|
severity: {
|
|
29100
29188
|
type: "string",
|
|
29101
29189
|
enum: ["error", "warn", "info", "all"],
|
|
@@ -29114,6 +29202,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
29114
29202
|
inputSchema: {
|
|
29115
29203
|
type: "object",
|
|
29116
29204
|
properties: {
|
|
29205
|
+
target: TARGET_ARG,
|
|
29117
29206
|
scriptUrlContains: { type: "string" },
|
|
29118
29207
|
line: { type: "number" },
|
|
29119
29208
|
column: { type: "number" },
|
|
@@ -29128,6 +29217,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
29128
29217
|
inputSchema: {
|
|
29129
29218
|
type: "object",
|
|
29130
29219
|
properties: {
|
|
29220
|
+
target: TARGET_ARG,
|
|
29131
29221
|
durationMs: { type: "number", description: "\u9ED8\u8BA4 1500ms" }
|
|
29132
29222
|
}
|
|
29133
29223
|
}
|
|
@@ -29138,6 +29228,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
29138
29228
|
inputSchema: {
|
|
29139
29229
|
type: "object",
|
|
29140
29230
|
properties: {
|
|
29231
|
+
target: TARGET_ARG,
|
|
29141
29232
|
query: { type: "string" },
|
|
29142
29233
|
scriptUrlContains: { type: "string" },
|
|
29143
29234
|
maxMatches: { type: "number" }
|
|
@@ -29148,14 +29239,14 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
29148
29239
|
{
|
|
29149
29240
|
name: "symbolic_hints",
|
|
29150
29241
|
description: "\u6536\u96C6\u9875\u9762\u7684\u8D44\u6E90\u3001\u5168\u5C40\u7B26\u53F7\u4E0E UA/URL \u7EBF\u7D22\uFF0C\u5E2E\u52A9\u63A8\u65AD\u7248\u672C\u4E0E\u6A21\u5757\u5F52\u5C5E",
|
|
29151
|
-
inputSchema: { type: "object", properties: {} }
|
|
29242
|
+
inputSchema: { type: "object", properties: { target: TARGET_ARG } }
|
|
29152
29243
|
},
|
|
29153
29244
|
{
|
|
29154
29245
|
name: "eval_script",
|
|
29155
29246
|
description: "\u5728\u76EE\u6807\u6807\u7B7E\u9875\u6267\u884C\u53EA\u8BFB JS \u8868\u8FBE\u5F0F\uFF08\u8C28\u614E\u4F7F\u7528\uFF09",
|
|
29156
29247
|
inputSchema: {
|
|
29157
29248
|
type: "object",
|
|
29158
|
-
properties: { code: { type: "string" } },
|
|
29249
|
+
properties: { target: TARGET_ARG, code: { type: "string" } },
|
|
29159
29250
|
required: ["code"]
|
|
29160
29251
|
}
|
|
29161
29252
|
},
|
|
@@ -29165,6 +29256,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
29165
29256
|
inputSchema: {
|
|
29166
29257
|
type: "object",
|
|
29167
29258
|
properties: {
|
|
29259
|
+
target: TARGET_ARG,
|
|
29168
29260
|
filter: { type: "string", description: "URL \u5173\u952E\u8BCD\u8FC7\u6EE4" },
|
|
29169
29261
|
method: { type: "string", description: "\u8BF7\u6C42\u65B9\u6CD5\uFF1AGET/POST/PUT/DELETE \u7B49" },
|
|
29170
29262
|
status: { type: "string", description: "\u72B6\u6001\uFF1Asuccess/error/failed/pending" },
|
|
@@ -29184,6 +29276,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
29184
29276
|
inputSchema: {
|
|
29185
29277
|
type: "object",
|
|
29186
29278
|
properties: {
|
|
29279
|
+
target: TARGET_ARG,
|
|
29187
29280
|
requestId: { type: "string", description: "\u8BF7\u6C42 ID\uFF08\u4ECE list_network_requests \u83B7\u53D6\uFF09" },
|
|
29188
29281
|
includeBody: { type: "boolean", description: "\u662F\u5426\u5305\u542B\u54CD\u5E94\u4F53\uFF0C\u9ED8\u8BA4 false" }
|
|
29189
29282
|
},
|
|
@@ -29193,7 +29286,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
29193
29286
|
{
|
|
29194
29287
|
name: "clear_network_requests",
|
|
29195
29288
|
description: "\u6E05\u7A7A\u5DF2\u6355\u83B7\u7684\u7F51\u7EDC\u8BF7\u6C42\u8BB0\u5F55",
|
|
29196
|
-
inputSchema: { type: "object", properties: {} }
|
|
29289
|
+
inputSchema: { type: "object", properties: { target: TARGET_ARG } }
|
|
29197
29290
|
},
|
|
29198
29291
|
{
|
|
29199
29292
|
name: "perf_metrics",
|
|
@@ -29201,6 +29294,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
29201
29294
|
inputSchema: {
|
|
29202
29295
|
type: "object",
|
|
29203
29296
|
properties: {
|
|
29297
|
+
target: TARGET_ARG,
|
|
29204
29298
|
includeTimings: {
|
|
29205
29299
|
type: "boolean",
|
|
29206
29300
|
description: "\u662F\u5426\u5305\u542B Navigation Timing \u548C Web Vitals\uFF0C\u9ED8\u8BA4 true"
|
|
@@ -29218,6 +29312,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
29218
29312
|
inputSchema: {
|
|
29219
29313
|
type: "object",
|
|
29220
29314
|
properties: {
|
|
29315
|
+
target: TARGET_ARG,
|
|
29221
29316
|
format: {
|
|
29222
29317
|
type: "string",
|
|
29223
29318
|
enum: ["png", "jpeg"],
|
|
@@ -29250,6 +29345,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
29250
29345
|
inputSchema: {
|
|
29251
29346
|
type: "object",
|
|
29252
29347
|
properties: {
|
|
29348
|
+
target: TARGET_ARG,
|
|
29253
29349
|
mode: {
|
|
29254
29350
|
type: "string",
|
|
29255
29351
|
enum: ["text", "html", "structured"],
|
|
@@ -29276,6 +29372,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
29276
29372
|
inputSchema: {
|
|
29277
29373
|
type: "object",
|
|
29278
29374
|
properties: {
|
|
29375
|
+
target: TARGET_ARG,
|
|
29279
29376
|
selector: {
|
|
29280
29377
|
type: "string",
|
|
29281
29378
|
description: "CSS \u9009\u62E9\u5668\uFF0C\u9650\u5B9A\u626B\u63CF\u8303\u56F4\u3002\u4E0D\u6307\u5B9A\u5219\u626B\u63CF\u6574\u4E2A\u9875\u9762"
|
|
@@ -29297,6 +29394,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
29297
29394
|
inputSchema: {
|
|
29298
29395
|
type: "object",
|
|
29299
29396
|
properties: {
|
|
29397
|
+
target: TARGET_ARG,
|
|
29300
29398
|
ref: {
|
|
29301
29399
|
type: "string",
|
|
29302
29400
|
description: "\u76EE\u6807\u5143\u7D20\u7684 ref \u6807\u8BC6\uFF0C\u5982 'e1'\u3001'e5'\uFF08\u4ECE get_interactive_snapshot \u83B7\u53D6\uFF09"
|
|
@@ -29337,8 +29435,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
29337
29435
|
const args = request.params.arguments || {};
|
|
29338
29436
|
try {
|
|
29339
29437
|
if (name === "inspect_page") {
|
|
29340
|
-
const { selector, includeInteractive = true, maxElements = 30 } = args;
|
|
29438
|
+
const { target, selector, includeInteractive = true, maxElements = 30 } = args;
|
|
29341
29439
|
const snapshot = await askChrome("inspectPageSnapshot", {
|
|
29440
|
+
target,
|
|
29342
29441
|
selector,
|
|
29343
29442
|
includeInteractive,
|
|
29344
29443
|
maxElements
|
|
@@ -29421,6 +29520,20 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
29421
29520
|
const res = await askChrome("listTabs");
|
|
29422
29521
|
return { content: [{ type: "text", text: jsonText(res) }] };
|
|
29423
29522
|
}
|
|
29523
|
+
if (name === "bind_tab") {
|
|
29524
|
+
const { name: targetName, tabId, urlContains, titleContains } = args;
|
|
29525
|
+
const res = await askChrome("bindTab", { name: targetName, tabId, urlContains, titleContains }, { timeoutMs: 1e4 });
|
|
29526
|
+
return { content: [{ type: "text", text: jsonText(res) }] };
|
|
29527
|
+
}
|
|
29528
|
+
if (name === "unbind_tab") {
|
|
29529
|
+
const { name: targetName } = args;
|
|
29530
|
+
const res = await askChrome("unbindTab", { name: targetName }, { timeoutMs: 1e4 });
|
|
29531
|
+
return { content: [{ type: "text", text: jsonText(res) }] };
|
|
29532
|
+
}
|
|
29533
|
+
if (name === "list_targets") {
|
|
29534
|
+
const res = await askChrome("listTargets");
|
|
29535
|
+
return { content: [{ type: "text", text: jsonText(res) }] };
|
|
29536
|
+
}
|
|
29424
29537
|
if (name === "get_target_tab") {
|
|
29425
29538
|
const res = await askChrome("getTargetTab");
|
|
29426
29539
|
return { content: [{ type: "text", text: jsonText(res) }] };
|
|
@@ -29439,12 +29552,13 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
29439
29552
|
return { content: [{ type: "text", text: jsonText(res) }] };
|
|
29440
29553
|
}
|
|
29441
29554
|
if (name === "get_last_error") {
|
|
29442
|
-
const { severity = "error", limit = 20 } = args;
|
|
29443
|
-
const data = await askChrome("getLastError", { severity, limit });
|
|
29555
|
+
const { target, severity = "error", limit = 20 } = args;
|
|
29556
|
+
const data = await askChrome("getLastError", { target, severity, limit });
|
|
29444
29557
|
return { content: [{ type: "text", text: jsonText(data) }] };
|
|
29445
29558
|
}
|
|
29446
29559
|
if (name === "get_script_source") {
|
|
29447
29560
|
const {
|
|
29561
|
+
target,
|
|
29448
29562
|
scriptUrlContains,
|
|
29449
29563
|
line,
|
|
29450
29564
|
column,
|
|
@@ -29453,6 +29567,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
29453
29567
|
} = args;
|
|
29454
29568
|
const res = await askChrome("getScriptSource", {
|
|
29455
29569
|
scriptUrlContains,
|
|
29570
|
+
target,
|
|
29456
29571
|
line,
|
|
29457
29572
|
column
|
|
29458
29573
|
});
|
|
@@ -29479,45 +29594,46 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
29479
29594
|
};
|
|
29480
29595
|
}
|
|
29481
29596
|
if (name === "coverage_snapshot") {
|
|
29597
|
+
const { target } = args;
|
|
29482
29598
|
const durationMs = args.durationMs || 1500;
|
|
29483
|
-
const res = await askChrome("coverageSnapshot", { durationMs }, { timeoutMs: durationMs + 4e3 });
|
|
29599
|
+
const res = await askChrome("coverageSnapshot", { target, durationMs }, { timeoutMs: durationMs + 4e3 });
|
|
29484
29600
|
return { content: [{ type: "text", text: jsonText(res) }] };
|
|
29485
29601
|
}
|
|
29486
29602
|
if (name === "find_by_string") {
|
|
29487
|
-
const { query, scriptUrlContains, maxMatches = 5 } = args;
|
|
29488
|
-
const res = await askChrome("findByString", { query, scriptUrlContains, maxMatches });
|
|
29603
|
+
const { target, query, scriptUrlContains, maxMatches = 5 } = args;
|
|
29604
|
+
const res = await askChrome("findByString", { target, query, scriptUrlContains, maxMatches });
|
|
29489
29605
|
return { content: [{ type: "text", text: jsonText(res) }] };
|
|
29490
29606
|
}
|
|
29491
29607
|
if (name === "symbolic_hints") {
|
|
29492
|
-
const res = await askChrome("symbolicHints");
|
|
29608
|
+
const res = await askChrome("symbolicHints", { target: args.target });
|
|
29493
29609
|
return { content: [{ type: "text", text: jsonText(res) }] };
|
|
29494
29610
|
}
|
|
29495
29611
|
if (name === "eval_script") {
|
|
29496
|
-
const res = await askChrome("eval", { code: args.code });
|
|
29612
|
+
const res = await askChrome("eval", { target: args.target, code: args.code });
|
|
29497
29613
|
return { content: [{ type: "text", text: jsonText(res) }] };
|
|
29498
29614
|
}
|
|
29499
29615
|
if (name === "list_network_requests") {
|
|
29500
|
-
const { filter, method, status, resourceType, limit, priorityMode = "debug" } = args;
|
|
29501
|
-
const res = await askChrome("listNetworkRequests", { filter, method, status, resourceType, limit, priorityMode });
|
|
29616
|
+
const { target, filter, method, status, resourceType, limit, priorityMode = "debug" } = args;
|
|
29617
|
+
const res = await askChrome("listNetworkRequests", { target, filter, method, status, resourceType, limit, priorityMode });
|
|
29502
29618
|
return { content: [{ type: "text", text: jsonText(res) }] };
|
|
29503
29619
|
}
|
|
29504
29620
|
if (name === "get_network_detail") {
|
|
29505
|
-
const { requestId, includeBody } = args;
|
|
29506
|
-
const res = await askChrome("getNetworkDetail", { requestId, includeBody });
|
|
29621
|
+
const { target, requestId, includeBody } = args;
|
|
29622
|
+
const res = await askChrome("getNetworkDetail", { target, requestId, includeBody });
|
|
29507
29623
|
return { content: [{ type: "text", text: jsonText(res) }] };
|
|
29508
29624
|
}
|
|
29509
29625
|
if (name === "clear_network_requests") {
|
|
29510
|
-
const res = await askChrome("clearNetworkRequests");
|
|
29626
|
+
const res = await askChrome("clearNetworkRequests", { target: args.target });
|
|
29511
29627
|
return { content: [{ type: "text", text: jsonText(res) }] };
|
|
29512
29628
|
}
|
|
29513
29629
|
if (name === "perf_metrics") {
|
|
29514
|
-
const { includeTimings, includeResources } = args;
|
|
29515
|
-
const res = await askChrome("perfMetrics", { includeTimings, includeResources });
|
|
29630
|
+
const { target, includeTimings, includeResources } = args;
|
|
29631
|
+
const res = await askChrome("perfMetrics", { target, includeTimings, includeResources });
|
|
29516
29632
|
return { content: [{ type: "text", text: jsonText(res) }] };
|
|
29517
29633
|
}
|
|
29518
29634
|
if (name === "capture_screenshot") {
|
|
29519
|
-
const { format, quality, fullPage, clip } = args;
|
|
29520
|
-
const res = await askChrome("captureScreenshot", { format, quality, fullPage, clip }, { timeoutMs: 15e3 });
|
|
29635
|
+
const { target, format, quality, fullPage, clip } = args;
|
|
29636
|
+
const res = await askChrome("captureScreenshot", { target, format, quality, fullPage, clip }, { timeoutMs: 15e3 });
|
|
29521
29637
|
const contents = [];
|
|
29522
29638
|
if (res.imageData) {
|
|
29523
29639
|
contents.push({
|
|
@@ -29541,7 +29657,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
29541
29657
|
return { content: contents };
|
|
29542
29658
|
}
|
|
29543
29659
|
if (name === "get_page_content") {
|
|
29544
|
-
const { mode = "text", selector, maxLength = 5e4, includeMetadata = true } = args;
|
|
29660
|
+
const { target, mode = "text", selector, maxLength = 5e4, includeMetadata = true } = args;
|
|
29545
29661
|
const validModes = ["text", "html", "structured"];
|
|
29546
29662
|
if (mode && !validModes.includes(mode)) {
|
|
29547
29663
|
return {
|
|
@@ -29551,17 +29667,17 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
29551
29667
|
}]
|
|
29552
29668
|
};
|
|
29553
29669
|
}
|
|
29554
|
-
const res = await askChrome("getPageContent", { mode, selector, maxLength, includeMetadata });
|
|
29670
|
+
const res = await askChrome("getPageContent", { target, mode, selector, maxLength, includeMetadata });
|
|
29555
29671
|
return { content: [{ type: "text", text: jsonText(res) }] };
|
|
29556
29672
|
}
|
|
29557
29673
|
if (name === "get_interactive_snapshot") {
|
|
29558
|
-
const { selector, includeText, maxElements } = args;
|
|
29559
|
-
const res = await askChrome("getInteractiveSnapshot", { selector, includeText, maxElements });
|
|
29674
|
+
const { target, selector, includeText, maxElements } = args;
|
|
29675
|
+
const res = await askChrome("getInteractiveSnapshot", { target, selector, includeText, maxElements });
|
|
29560
29676
|
return { content: [{ type: "text", text: jsonText(res) }] };
|
|
29561
29677
|
}
|
|
29562
29678
|
if (name === "dispatch_action") {
|
|
29563
|
-
const { ref, action, value, key, deltaX, deltaY, waitMs } = args;
|
|
29564
|
-
const res = await askChrome("dispatchAction", { ref, action, value, key, deltaX, deltaY, waitMs }, { timeoutMs: 1e4 });
|
|
29679
|
+
const { target, ref, action, value, key, deltaX, deltaY, waitMs } = args;
|
|
29680
|
+
const res = await askChrome("dispatchAction", { target, ref, action, value, key, deltaX, deltaY, waitMs }, { timeoutMs: 1e4 });
|
|
29565
29681
|
return { content: [{ type: "text", text: jsonText(res) }] };
|
|
29566
29682
|
}
|
|
29567
29683
|
return { content: [{ type: "text", text: `\u672A\u77E5\u5DE5\u5177\uFF1A${name}` }] };
|
|
@@ -29612,6 +29728,10 @@ function cleanup() {
|
|
|
29612
29728
|
} catch (e) {
|
|
29613
29729
|
log(`\u6E05\u7406\u7AEF\u53E3\u4FE1\u606F\u6587\u4EF6\u5931\u8D25: ${e.message}`);
|
|
29614
29730
|
}
|
|
29731
|
+
if (wsPingInterval) {
|
|
29732
|
+
clearInterval(wsPingInterval);
|
|
29733
|
+
wsPingInterval = null;
|
|
29734
|
+
}
|
|
29615
29735
|
if (wss) {
|
|
29616
29736
|
wss.close(() => {
|
|
29617
29737
|
log("\u{1F50C} WebSocket \u670D\u52A1\u5668\u5DF2\u5173\u95ED");
|