ghost-bridge 1.0.2 → 1.2.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 +64 -5
- package/dist/cli.js +0 -0
- package/dist/server.js +260 -89
- package/extension/background.js +453 -147
- package/extension/bg-control.js +52 -0
- package/extension/bg-dom.js +422 -38
- package/extension/bg-runtime.js +17 -0
- package/extension/manifest.json +3 -2
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -16,6 +16,8 @@ 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
|
+
- Locate elements by role/name, label, placeholder, text, test ID, or CSS across open Shadow DOM and same-origin iframes
|
|
20
|
+
- Run `locate → act → wait → snapshot` as one browser call instead of model-driven polling
|
|
19
21
|
- Bind multiple Chrome tabs as named targets and operate them independently
|
|
20
22
|
- Share one Chrome transport across multiple MCP clients
|
|
21
23
|
|
|
@@ -94,7 +96,9 @@ Typical prompts:
|
|
|
94
96
|
| `capture_screenshot` | Visual inspection and UI debugging |
|
|
95
97
|
| `get_page_content` | Text, HTML, and structured DOM extraction |
|
|
96
98
|
| `get_interactive_snapshot` | Find clickable and editable elements |
|
|
97
|
-
| `dispatch_action` |
|
|
99
|
+
| `dispatch_action` | Locate, act, wait, and verify in one call; supports semantic locators and batches |
|
|
100
|
+
| `eval_script` | Execute JavaScript, wait for returned promises, and cap arbitrary output |
|
|
101
|
+
| `page_request` | Send an authenticated page-context request and wait for the response in one call |
|
|
98
102
|
| `bind_tab` | Bind a Chrome tab as a named target such as `cases` or `app` |
|
|
99
103
|
| `unbind_tab` | Remove a named target binding |
|
|
100
104
|
| `list_targets` | Show named targets and their per-tab session status |
|
|
@@ -113,16 +117,71 @@ Typical prompts:
|
|
|
113
117
|
|
|
114
118
|
Recommended flow:
|
|
115
119
|
|
|
116
|
-
1.
|
|
117
|
-
2. Use `
|
|
120
|
+
1. When the target is describable, call `dispatch_action` directly with a semantic `locator`
|
|
121
|
+
2. Use `inspect_page` when the page is unfamiliar or a locator is ambiguous; its compact response includes actionable refs
|
|
122
|
+
3. Use `capture_screenshot` for visual issues
|
|
118
123
|
Default is optimized for transfer with JPEG; switch to `png` for pixel-level checks
|
|
119
|
-
|
|
120
|
-
|
|
124
|
+
4. Use `get_page_content` for DOM or text extraction
|
|
125
|
+
5. Put consecutive fills/clicks into one `dispatch_action.actions` call; add `waitFor` to the action that changes state and `snapshotAfter` when the resulting UI is needed
|
|
126
|
+
|
|
127
|
+
Round-trip-efficient examples:
|
|
128
|
+
|
|
129
|
+
```json
|
|
130
|
+
{
|
|
131
|
+
"target": "app",
|
|
132
|
+
"actions": [
|
|
133
|
+
{
|
|
134
|
+
"locator": { "role": "textbox", "label": "Email" },
|
|
135
|
+
"action": "fill",
|
|
136
|
+
"value": "user@example.com"
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
"locator": { "role": "textbox", "label": "Password" },
|
|
140
|
+
"action": "fill",
|
|
141
|
+
"value": "secret"
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
"locator": { "role": "button", "name": "Sign in" },
|
|
145
|
+
"action": "click",
|
|
146
|
+
"waitFor": {
|
|
147
|
+
"type": "element",
|
|
148
|
+
"locator": { "text": "Signed in" },
|
|
149
|
+
"state": "visible",
|
|
150
|
+
"timeoutMs": 10000
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
],
|
|
154
|
+
"snapshotAfter": true
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Locator fields are `css`, `testId`, `role` + `name`, `label`, `placeholder`, `text`, `match`, and zero-based `nth`. Matching is exact by default. Ghost Bridge refuses ambiguous action targets and returns compact candidates instead of silently choosing the first element.
|
|
159
|
+
|
|
160
|
+
`waitFor` supports:
|
|
161
|
+
|
|
162
|
+
- `element`: `visible`, `hidden`, `attached`, `detached`, or `enabled`
|
|
163
|
+
- `url`: `contains` or `equals`
|
|
164
|
+
- `networkIdle`: optional `idleMs`
|
|
165
|
+
- `expression`: a truthy JavaScript expression, including a returned Promise
|
|
166
|
+
|
|
167
|
+
Polling happens inside the extension at a short interval, so it does not create repeated model/tool turns. Each condition defaults to 10 seconds and is capped at 30 seconds. The whole batch has a hard deadline of at most 60 seconds; once reached, remaining actions are not executed. Fixed `waitMs` remains for compatibility but defaults to zero.
|
|
168
|
+
|
|
169
|
+
For API calls that need the page's login state, prefer `page_request`. If custom asynchronous JavaScript is still needed, return the promise from `eval_script` instead of storing a result on `window` and polling it in another tool call:
|
|
170
|
+
|
|
171
|
+
```javascript
|
|
172
|
+
(async () => {
|
|
173
|
+
const response = await fetch('/api/items', { credentials: 'include' })
|
|
174
|
+
const data = await response.json()
|
|
175
|
+
return data.items.map(({ id, name }) => ({ id, name }))
|
|
176
|
+
})()
|
|
177
|
+
```
|
|
121
178
|
|
|
122
179
|
Notes:
|
|
123
180
|
|
|
124
181
|
- 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
182
|
- 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.
|
|
183
|
+
- Semantic locators traverse open Shadow DOM and readable same-origin iframes. Cross-origin iframe DOM is not accessible and is skipped explicitly.
|
|
184
|
+
- `get_page_content` reports iframe counters and uses contiguous `offset`/`maxLength` slices, so pagination does not duplicate or skip the hidden middle of a head/tail truncation.
|
|
126
185
|
- 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.
|
|
127
186
|
- `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
|
|
128
187
|
|
package/dist/cli.js
CHANGED
|
File without changes
|
package/dist/server.js
CHANGED
|
@@ -28536,11 +28536,185 @@ var packageJsonPath = path.resolve(__dirname, "../package.json");
|
|
|
28536
28536
|
var packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
|
|
28537
28537
|
var GHOST_BRIDGE_VERSION = packageJson.version;
|
|
28538
28538
|
|
|
28539
|
+
// src/bridge-utils.js
|
|
28540
|
+
var DEFAULT_DISPATCH_TIMEOUT_MS = 3e4;
|
|
28541
|
+
var MAX_DISPATCH_TIMEOUT_MS = 6e4;
|
|
28542
|
+
function jsonText(data) {
|
|
28543
|
+
return typeof data === "string" ? data : JSON.stringify(data);
|
|
28544
|
+
}
|
|
28545
|
+
function compact(value) {
|
|
28546
|
+
if (Array.isArray(value)) {
|
|
28547
|
+
const cleaned = value.map(compact).filter((item) => item !== void 0);
|
|
28548
|
+
return cleaned.length ? cleaned : void 0;
|
|
28549
|
+
}
|
|
28550
|
+
if (value && typeof value === "object") {
|
|
28551
|
+
const cleaned = {};
|
|
28552
|
+
for (const [key, item] of Object.entries(value)) {
|
|
28553
|
+
const compacted = compact(item);
|
|
28554
|
+
if (compacted !== void 0) cleaned[key] = compacted;
|
|
28555
|
+
}
|
|
28556
|
+
return Object.keys(cleaned).length ? cleaned : void 0;
|
|
28557
|
+
}
|
|
28558
|
+
if (value === null || value === "") return void 0;
|
|
28559
|
+
return value;
|
|
28560
|
+
}
|
|
28561
|
+
function out(data) {
|
|
28562
|
+
return jsonText(compact(data));
|
|
28563
|
+
}
|
|
28564
|
+
function clampNumber(value, fallback, min, max) {
|
|
28565
|
+
const number3 = Number(value);
|
|
28566
|
+
if (!Number.isFinite(number3)) return fallback;
|
|
28567
|
+
return Math.min(max, Math.max(min, Math.round(number3)));
|
|
28568
|
+
}
|
|
28569
|
+
function buildTruncatedOutput(text, retainedChars) {
|
|
28570
|
+
const headLength = Math.ceil(retainedChars * 0.8);
|
|
28571
|
+
const tailLength = Math.max(0, retainedChars - headLength);
|
|
28572
|
+
const omitted = Math.max(0, text.length - headLength - tailLength);
|
|
28573
|
+
return out({
|
|
28574
|
+
truncated: true,
|
|
28575
|
+
originalLength: text.length,
|
|
28576
|
+
content: `${text.slice(0, headLength)}
|
|
28577
|
+
... [\u5DF2\u7701\u7565 ${omitted} \u4E2A\u5B57\u7B26] ...
|
|
28578
|
+
${tailLength ? text.slice(-tailLength) : ""}`,
|
|
28579
|
+
hint: "\u9700\u8981\u66F4\u591A\u7ED3\u679C\u65F6\u8BF7\u6536\u7A84\u8FD4\u56DE\u5B57\u6BB5\uFF1B\u4E0D\u8981\u901A\u8FC7\u8FDE\u7EED\u8F6E\u8BE2\u5206\u7247\u83B7\u53D6\u5927\u5BF9\u8C61"
|
|
28580
|
+
});
|
|
28581
|
+
}
|
|
28582
|
+
function boundedOut(data, maxLength, { defaultLength = 8e3, maxAllowed = 5e4 } = {}) {
|
|
28583
|
+
const text = out(data) ?? "undefined";
|
|
28584
|
+
const limit = clampNumber(maxLength, defaultLength, 200, maxAllowed);
|
|
28585
|
+
if (text.length <= limit) return text;
|
|
28586
|
+
let low = 0;
|
|
28587
|
+
let high = text.length;
|
|
28588
|
+
let best = buildTruncatedOutput(text, 0);
|
|
28589
|
+
if (best.length > limit) {
|
|
28590
|
+
return JSON.stringify({ truncated: true });
|
|
28591
|
+
}
|
|
28592
|
+
while (low <= high) {
|
|
28593
|
+
const middle = Math.floor((low + high) / 2);
|
|
28594
|
+
const candidate = buildTruncatedOutput(text, middle);
|
|
28595
|
+
if (candidate.length <= limit) {
|
|
28596
|
+
best = candidate;
|
|
28597
|
+
low = middle + 1;
|
|
28598
|
+
} else {
|
|
28599
|
+
high = middle - 1;
|
|
28600
|
+
}
|
|
28601
|
+
}
|
|
28602
|
+
return best;
|
|
28603
|
+
}
|
|
28604
|
+
function calculateDispatchBudget(args = {}) {
|
|
28605
|
+
const steps = Array.isArray(args.actions) ? args.actions : [args];
|
|
28606
|
+
if (!steps.length || steps.length > 20) throw new Error("actions \u6570\u91CF\u5FC5\u987B\u5728 1-20 \u4E4B\u95F4");
|
|
28607
|
+
const totalLegacyWaitMs = steps.reduce(
|
|
28608
|
+
(sum, step) => sum + clampNumber(step.waitMs, 0, 0, 3e3),
|
|
28609
|
+
0
|
|
28610
|
+
);
|
|
28611
|
+
const totalConditionWaitMs = steps.reduce(
|
|
28612
|
+
(sum, step) => sum + (step.waitFor ? clampNumber(step.waitFor.timeoutMs, 1e4, 100, 3e4) : 0),
|
|
28613
|
+
0
|
|
28614
|
+
);
|
|
28615
|
+
const estimatedActionMs = steps.length * 750;
|
|
28616
|
+
const snapshotMs = args.snapshotAfter ? 2e3 : 0;
|
|
28617
|
+
const estimatedExecutionMs = 2e3 + estimatedActionMs + totalLegacyWaitMs + totalConditionWaitMs + snapshotMs;
|
|
28618
|
+
const explicitTimeout = args.timeoutMs === void 0 ? null : clampNumber(args.timeoutMs, DEFAULT_DISPATCH_TIMEOUT_MS, 1e3, MAX_DISPATCH_TIMEOUT_MS);
|
|
28619
|
+
if (explicitTimeout === null && estimatedExecutionMs > MAX_DISPATCH_TIMEOUT_MS) {
|
|
28620
|
+
throw new Error(
|
|
28621
|
+
`\u6279\u5904\u7406\u6700\u574F\u6267\u884C\u9884\u7B97 ${estimatedExecutionMs}ms \u8D85\u8FC7 ${MAX_DISPATCH_TIMEOUT_MS}ms\uFF1B\u8BF7\u51CF\u5C11\u52A8\u4F5C/\u7B49\u5F85\u6570\u91CF\u3001\u7F29\u77ED waitFor.timeoutMs\uFF0C\u6216\u663E\u5F0F\u8BBE\u7F6E\u8F83\u5C0F\u7684\u6574\u4F53 timeoutMs \u8BA9\u6279\u5904\u7406\u6309\u622A\u6B62\u65F6\u95F4\u505C\u6B62`
|
|
28622
|
+
);
|
|
28623
|
+
}
|
|
28624
|
+
const executionTimeoutMs = explicitTimeout ?? Math.max(5e3, Math.min(MAX_DISPATCH_TIMEOUT_MS, estimatedExecutionMs));
|
|
28625
|
+
return {
|
|
28626
|
+
steps,
|
|
28627
|
+
estimatedExecutionMs,
|
|
28628
|
+
executionTimeoutMs,
|
|
28629
|
+
serverTimeoutMs: executionTimeoutMs + 5e3
|
|
28630
|
+
};
|
|
28631
|
+
}
|
|
28632
|
+
|
|
28633
|
+
// src/tool-schemas.js
|
|
28634
|
+
var LOCATOR_REF = { $ref: "#/$defs/locator" };
|
|
28635
|
+
var WAIT_FOR_REF = { $ref: "#/$defs/wait" };
|
|
28636
|
+
var LOCATOR_DEFINITION = {
|
|
28637
|
+
type: "object",
|
|
28638
|
+
description: "\u591A\u5B57\u6BB5\u540C\u65F6\u6EE1\u8DB3\uFF1B\u9ED8\u8BA4 exact\uFF0C\u6B67\u4E49\u65F6\u7528 nth \u6216\u6536\u7A84\u6761\u4EF6\u3002",
|
|
28639
|
+
properties: {
|
|
28640
|
+
css: { type: "string", description: "CSS \u9009\u62E9\u5668" },
|
|
28641
|
+
testId: { type: "string", description: "data-testid" },
|
|
28642
|
+
role: { type: "string", description: "ARIA/\u9690\u5F0F role" },
|
|
28643
|
+
name: { type: "string", description: "\u53EF\u8BBF\u95EE\u540D\u79F0" },
|
|
28644
|
+
label: { type: "string", description: "\u8868\u5355 label/aria-label" },
|
|
28645
|
+
placeholder: { type: "string" },
|
|
28646
|
+
text: { type: "string", description: "\u5143\u7D20\u6587\u672C" },
|
|
28647
|
+
nth: { type: "integer", minimum: 0, description: "\u4ECE 0 \u5F00\u59CB" },
|
|
28648
|
+
match: { enum: ["exact", "contains"], description: "\u9ED8\u8BA4 exact" }
|
|
28649
|
+
}
|
|
28650
|
+
};
|
|
28651
|
+
var WAIT_FOR_DEFINITION = {
|
|
28652
|
+
type: "object",
|
|
28653
|
+
description: "\u52A8\u4F5C\u540E\u5728\u6269\u5C55\u5185\u90E8\u7B49\u5F85\uFF0C\u4E0D\u589E\u52A0\u6A21\u578B\u5F80\u8FD4\u3002",
|
|
28654
|
+
properties: {
|
|
28655
|
+
type: { enum: ["element", "url", "networkIdle", "expression"] },
|
|
28656
|
+
locator: { ...LOCATOR_REF, description: "element \u6761\u4EF6\u7684\u5B9A\u4F4D\u5668\uFF1B\u7701\u7565\u65F6\u590D\u7528\u52A8\u4F5C locator" },
|
|
28657
|
+
state: { enum: ["visible", "hidden", "attached", "detached", "enabled"], description: "element \u9ED8\u8BA4 visible" },
|
|
28658
|
+
contains: { type: "string", description: "URL \u5305\u542B" },
|
|
28659
|
+
equals: { type: "string", description: "URL \u7B49\u4E8E" },
|
|
28660
|
+
idleMs: { type: "number", minimum: 100, maximum: 1e4, description: "networkIdle \u7A7A\u95F2\u65F6\u95F4" },
|
|
28661
|
+
expression: { type: "string", description: "JS \u771F\u503C\u8868\u8FBE\u5F0F\uFF0C\u53EF\u8FD4\u56DE Promise" },
|
|
28662
|
+
timeoutMs: { type: "number", minimum: 100, maximum: 3e4, description: "\u9ED8\u8BA4 10000" }
|
|
28663
|
+
},
|
|
28664
|
+
required: ["type"]
|
|
28665
|
+
};
|
|
28666
|
+
var ACTION_PROPERTIES = {
|
|
28667
|
+
ref: { type: "string" },
|
|
28668
|
+
selector: { type: "string" },
|
|
28669
|
+
locator: LOCATOR_REF,
|
|
28670
|
+
action: { enum: ["click", "fill", "press", "scroll", "select", "hover", "focus"] },
|
|
28671
|
+
value: { type: "string" },
|
|
28672
|
+
key: { type: "string" },
|
|
28673
|
+
deltaX: { type: "number" },
|
|
28674
|
+
deltaY: { type: "number" },
|
|
28675
|
+
waitMs: { type: "number", minimum: 0, maximum: 3e3 },
|
|
28676
|
+
waitFor: WAIT_FOR_REF
|
|
28677
|
+
};
|
|
28678
|
+
var ACTION_DEFINITION = {
|
|
28679
|
+
type: "object",
|
|
28680
|
+
properties: ACTION_PROPERTIES,
|
|
28681
|
+
required: ["action"]
|
|
28682
|
+
};
|
|
28683
|
+
var DISPATCH_ACTION_TOOL = {
|
|
28684
|
+
name: "dispatch_action",
|
|
28685
|
+
description: "\u5B9A\u4F4D\u2192\u64CD\u4F5C\u2192\u7B49\u5F85\u2192\u5FEB\u7167\u4E00\u6B21\u5B8C\u6210\u3002\u5355\u52A8\u4F20 ref/selector/locator+action\uFF0C\u6279\u91CF\u4F20 actions\uFF1Bfill/select \u7528 value\uFF0Cpress \u7528 key\u3002",
|
|
28686
|
+
inputSchema: {
|
|
28687
|
+
type: "object",
|
|
28688
|
+
$defs: {
|
|
28689
|
+
locator: LOCATOR_DEFINITION,
|
|
28690
|
+
wait: WAIT_FOR_DEFINITION,
|
|
28691
|
+
action: ACTION_DEFINITION
|
|
28692
|
+
},
|
|
28693
|
+
properties: {
|
|
28694
|
+
target: { type: "string" },
|
|
28695
|
+
...ACTION_PROPERTIES,
|
|
28696
|
+
timeoutMs: { type: "number", minimum: 1e3, maximum: 6e4 },
|
|
28697
|
+
actions: {
|
|
28698
|
+
type: "array",
|
|
28699
|
+
minItems: 1,
|
|
28700
|
+
maxItems: 20,
|
|
28701
|
+
items: { $ref: "#/$defs/action" }
|
|
28702
|
+
},
|
|
28703
|
+
stopOnError: { type: "boolean" },
|
|
28704
|
+
snapshotAfter: { type: "boolean" },
|
|
28705
|
+
snapshotSelector: { type: "string" },
|
|
28706
|
+
snapshotMaxElements: { type: "number" }
|
|
28707
|
+
}
|
|
28708
|
+
}
|
|
28709
|
+
};
|
|
28710
|
+
|
|
28539
28711
|
// src/server.js
|
|
28540
28712
|
var BASE_PORT = Number(process.env.GHOST_BRIDGE_PORT || 33333);
|
|
28541
28713
|
var DEFAULT_WS_TOKEN = "ghost-bridge-local";
|
|
28542
28714
|
var WS_TOKEN = process.env.GHOST_BRIDGE_TOKEN || DEFAULT_WS_TOKEN;
|
|
28543
28715
|
var RESPONSE_TIMEOUT = 8e3;
|
|
28716
|
+
var DEFAULT_EVAL_OUTPUT_LENGTH = 8e3;
|
|
28717
|
+
var MAX_EVAL_OUTPUT_LENGTH = 5e4;
|
|
28544
28718
|
var PORT_INFO_FILE = process.env.GHOST_BRIDGE_PORT_INFO || path2.join(os.tmpdir(), "ghost-bridge-port.json");
|
|
28545
28719
|
var SERVER_STARTED_AT = (/* @__PURE__ */ new Date()).toISOString();
|
|
28546
28720
|
var SERVER_ENTRY_PATH = fileURLToPath2(import.meta.url);
|
|
@@ -29128,27 +29302,9 @@ async function askChrome(command, params = {}, options = {}) {
|
|
|
29128
29302
|
});
|
|
29129
29303
|
});
|
|
29130
29304
|
}
|
|
29131
|
-
function
|
|
29132
|
-
|
|
29133
|
-
|
|
29134
|
-
function compact(value) {
|
|
29135
|
-
if (Array.isArray(value)) {
|
|
29136
|
-
const cleaned = value.map(compact).filter((v) => v !== void 0);
|
|
29137
|
-
return cleaned.length ? cleaned : void 0;
|
|
29138
|
-
}
|
|
29139
|
-
if (value && typeof value === "object") {
|
|
29140
|
-
const cleaned = {};
|
|
29141
|
-
for (const [k, v] of Object.entries(value)) {
|
|
29142
|
-
const c = compact(v);
|
|
29143
|
-
if (c !== void 0) cleaned[k] = c;
|
|
29144
|
-
}
|
|
29145
|
-
return Object.keys(cleaned).length ? cleaned : void 0;
|
|
29146
|
-
}
|
|
29147
|
-
if (value === null || value === "") return void 0;
|
|
29148
|
-
return value;
|
|
29149
|
-
}
|
|
29150
|
-
function out(data) {
|
|
29151
|
-
return jsonText(compact(data));
|
|
29305
|
+
function truncateUrl(url2, maxLen = 200) {
|
|
29306
|
+
if (typeof url2 !== "string" || url2.length <= maxLen) return url2;
|
|
29307
|
+
return url2.slice(0, maxLen) + "\u2026";
|
|
29152
29308
|
}
|
|
29153
29309
|
function shrinkListTabs(res, { fullUrl } = {}) {
|
|
29154
29310
|
if (!res || !Array.isArray(res.tabs)) return res;
|
|
@@ -29230,7 +29386,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
29230
29386
|
tools: [
|
|
29231
29387
|
{
|
|
29232
29388
|
name: "inspect_page",
|
|
29233
|
-
description: "\u9875\u9762\u5206\u6790\u5165\u53E3\uFF1A\u8FD4\u56DE\u5143\u6570\u636E\u3001\u7ED3\u6784\u8BA1\u6570\
|
|
29389
|
+
description: "\u9875\u9762\u5206\u6790\u5165\u53E3\uFF1A\u4E00\u6B21\u8FD4\u56DE\u5143\u6570\u636E\u3001\u7ED3\u6784\u8BA1\u6570\u548C\u5C11\u91CF\u53EF\u76F4\u63A5\u64CD\u4F5C\u7684\u5143\u7D20 ref\uFF0C\u901A\u5E38\u65E0\u9700\u518D\u8C03\u7528 get_interactive_snapshot\u3002detail:true \u8FD4\u56DE\u5B8C\u6574\u7ED3\u6784\u3002",
|
|
29234
29390
|
inputSchema: {
|
|
29235
29391
|
type: "object",
|
|
29236
29392
|
properties: {
|
|
@@ -29245,7 +29401,11 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
29245
29401
|
},
|
|
29246
29402
|
maxElements: {
|
|
29247
29403
|
type: "number",
|
|
29248
|
-
description: "
|
|
29404
|
+
description: "\u8FD4\u56DE\u53EF\u4EA4\u4E92\u5143\u7D20\u4E0A\u9650\uFF0C\u9ED8\u8BA4 20"
|
|
29405
|
+
},
|
|
29406
|
+
includeElements: {
|
|
29407
|
+
type: "boolean",
|
|
29408
|
+
description: "\u7D27\u51D1\u6A21\u5F0F\u662F\u5426\u9644\u5E26\u53EF\u64CD\u4F5C\u5143\u7D20\uFF0C\u9ED8\u8BA4 true"
|
|
29249
29409
|
},
|
|
29250
29410
|
detail: {
|
|
29251
29411
|
type: "boolean",
|
|
@@ -29395,13 +29555,37 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
29395
29555
|
},
|
|
29396
29556
|
{
|
|
29397
29557
|
name: "eval_script",
|
|
29398
|
-
description: "\u5728\u76EE\u6807\u9875\u6267\u884C\
|
|
29558
|
+
description: "\u5728\u76EE\u6807\u9875\u6267\u884C JS\u3002\u9AD8\u6210\u672C\u5DE5\u5177\uFF1A\u540C\u4E00\u76EE\u6807\u7684\u8BFB\u53D6/\u64CD\u4F5C\u5E94\u5408\u5E76\u8FDB\u4E00\u6B21 code\uFF1B\u5F02\u6B65\u4EE3\u7801\u76F4\u63A5\u8FD4\u56DE Promise\uFF0C\u672C\u5DE5\u5177\u9ED8\u8BA4\u7B49\u5F85\u5B8C\u6210\uFF0C\u7981\u6B62\u5199 window \u4E34\u65F6\u53D8\u91CF\u540E\u518D\u6B21\u8F6E\u8BE2\u3002\u7ED3\u679C\u9ED8\u8BA4\u6700\u591A 8000 \u5B57\u7B26\u3002",
|
|
29399
29559
|
inputSchema: {
|
|
29400
29560
|
type: "object",
|
|
29401
|
-
properties: {
|
|
29561
|
+
properties: {
|
|
29562
|
+
target: TARGET_ARG,
|
|
29563
|
+
code: { type: "string", description: "JS \u8868\u8FBE\u5F0F\uFF1B\u5F02\u6B65\u793A\u4F8B\uFF1A(async()=>await fetch(...).then(r=>r.json()))()" },
|
|
29564
|
+
awaitPromise: { type: "boolean", description: "\u7B49\u5F85 Promise \u5B8C\u6210\uFF0C\u9ED8\u8BA4 true" },
|
|
29565
|
+
timeoutMs: { type: "number", description: "Promise \u7B49\u5F85\u4E0A\u9650\uFF0C\u9ED8\u8BA4 10000\uFF0C\u6700\u5927 30000" },
|
|
29566
|
+
maxOutputLength: { type: "number", description: "\u5E8F\u5217\u5316\u7ED3\u679C\u5B57\u7B26\u4E0A\u9650\uFF0C\u9ED8\u8BA4 8000\uFF0C\u6700\u5927 50000" }
|
|
29567
|
+
},
|
|
29402
29568
|
required: ["code"]
|
|
29403
29569
|
}
|
|
29404
29570
|
},
|
|
29571
|
+
{
|
|
29572
|
+
name: "page_request",
|
|
29573
|
+
description: "\u4F7F\u7528\u5F53\u524D\u9875\u9762\u767B\u5F55\u6001\u53D1\u8D77 fetch \u5E76\u7B49\u5F85\u54CD\u5E94\uFF0C\u4E00\u6B21\u8FD4\u56DE\u7ED3\u679C\uFF1B\u9875\u9762\u63A5\u53E3\u8C03\u7528\u4F18\u5148\u7528\u5B83\uFF0C\u907F\u514D eval_script \u53D1\u8D77\u8BF7\u6C42\u540E\u518D\u8F6E\u8BE2\u3002",
|
|
29574
|
+
inputSchema: {
|
|
29575
|
+
type: "object",
|
|
29576
|
+
properties: {
|
|
29577
|
+
target: TARGET_ARG,
|
|
29578
|
+
url: { type: "string", description: "\u76F8\u5BF9\u6216\u7EDD\u5BF9 HTTP(S) URL" },
|
|
29579
|
+
method: { type: "string", description: "\u9ED8\u8BA4 GET" },
|
|
29580
|
+
headers: { type: "object", description: "\u8BF7\u6C42\u5934" },
|
|
29581
|
+
body: { description: "\u5B57\u7B26\u4E32\u6216 JSON \u5BF9\u8C61\uFF1B\u5BF9\u8C61\u4F1A\u81EA\u52A8 JSON.stringify" },
|
|
29582
|
+
responseType: { type: "string", enum: ["auto", "json", "text"], description: "\u9ED8\u8BA4 auto" },
|
|
29583
|
+
timeoutMs: { type: "number", description: "\u9ED8\u8BA4 10000\uFF0C\u6700\u5927 30000" },
|
|
29584
|
+
maxOutputLength: { type: "number", description: "\u54CD\u5E94\u5185\u5BB9\u5B57\u7B26\u4E0A\u9650\uFF0C\u9ED8\u8BA4 8000\uFF0C\u6700\u5927 50000" }
|
|
29585
|
+
},
|
|
29586
|
+
required: ["url"]
|
|
29587
|
+
}
|
|
29588
|
+
},
|
|
29405
29589
|
{
|
|
29406
29590
|
name: "list_network_requests",
|
|
29407
29591
|
description: "\u5217\u51FA\u6355\u83B7\u7684\u7F51\u7EDC\u8BF7\u6C42\uFF0C\u652F\u6301 URL/\u65B9\u6CD5/\u72B6\u6001/\u7C7B\u578B\u8FC7\u6EE4\uFF1B\u8D85\u957F URL \u81EA\u52A8\u6458\u8981\u3002",
|
|
@@ -29481,7 +29665,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
29481
29665
|
},
|
|
29482
29666
|
{
|
|
29483
29667
|
name: "get_page_content",
|
|
29484
|
-
description: "\u63D0\u53D6\u9875\u9762\u6587\u672C/HTML/\u7ED3\u6784\u5316\u6570\u636E\uFF0C\u6BD4\u622A\u56FE\u8F7B\u91CF\u3002\u9ED8\u8BA4\u4E0A\u9650 8000 \u5B57\u7B26\uFF1B\u4E0D\u591F\u65F6\u8C03\u5927 maxLength \u6216\u7528 offset \u7FFB\u9875\u3001selector \u6536\u7A84\u3002\u4E0D\u53CD\u6620 CSS\
|
|
29668
|
+
description: "\u63D0\u53D6\u9875\u9762\u6587\u672C/HTML/\u7ED3\u6784\u5316\u6570\u636E\uFF0C\u6BD4\u622A\u56FE\u8F7B\u91CF\u3002text \u6A21\u5F0F\u9012\u5F52\u6536\u96C6\u540C\u6E90 iframe \u6587\u672C\uFF08\u9002\u5408\u6587\u6863\u7C7B\u9875\u9762\uFF09\u3002\u9ED8\u8BA4\u4E0A\u9650 8000 \u5B57\u7B26\uFF1B\u4E0D\u591F\u65F6\u8C03\u5927 maxLength \u6216\u7528 offset \u7FFB\u9875\u3001selector \u6536\u7A84\u3002\u4E0D\u53CD\u6620 CSS\u3002",
|
|
29485
29669
|
inputSchema: {
|
|
29486
29670
|
type: "object",
|
|
29487
29671
|
properties: {
|
|
@@ -29512,7 +29696,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
29512
29696
|
},
|
|
29513
29697
|
{
|
|
29514
29698
|
name: "get_interactive_snapshot",
|
|
29515
|
-
description: "\u626B\u63CF\u53EF\u89C1\u53EF\u4EA4\u4E92\u5143\u7D20\
|
|
29699
|
+
description: "\u5F53\u65E0\u6CD5\u76F4\u63A5\u5199\u51FA\u8BED\u4E49 locator \u65F6\uFF0C\u626B\u63CF\u53EF\u89C1\u53EF\u4EA4\u4E92\u5143\u7D20\u5E76\u8FD4\u56DE ref\u3002\u9ED8\u8BA4 30 \u4E2A\uFF0C\u9700\u8981\u66F4\u591A\u4F20 maxElements \u6216\u7528 selector \u6536\u7A84\u3002\u652F\u6301 Shadow DOM\u3002",
|
|
29516
29700
|
inputSchema: {
|
|
29517
29701
|
type: "object",
|
|
29518
29702
|
properties: {
|
|
@@ -29532,45 +29716,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
29532
29716
|
}
|
|
29533
29717
|
}
|
|
29534
29718
|
},
|
|
29535
|
-
|
|
29536
|
-
name: "dispatch_action",
|
|
29537
|
-
description: "\u5BF9 get_interactive_snapshot \u8FD4\u56DE\u7684 ref \u6267\u884C\u64CD\u4F5C\uFF1Aclick/fill/press/scroll/select/hover/focus\u3002\u26A0\uFE0F \u5FC5\u987B\u5148\u5FEB\u7167\u62FF ref\uFF1B\u64CD\u4F5C\u540E\u5EFA\u8BAE\u590D\u67E5\u9875\u9762\u72B6\u6001\u3002",
|
|
29538
|
-
inputSchema: {
|
|
29539
|
-
type: "object",
|
|
29540
|
-
properties: {
|
|
29541
|
-
target: TARGET_ARG,
|
|
29542
|
-
ref: {
|
|
29543
|
-
type: "string",
|
|
29544
|
-
description: "\u5143\u7D20 ref\uFF0C\u5982 'e1'\uFF08\u6765\u81EA get_interactive_snapshot\uFF09"
|
|
29545
|
-
},
|
|
29546
|
-
action: {
|
|
29547
|
-
type: "string",
|
|
29548
|
-
enum: ["click", "fill", "press", "scroll", "select", "hover", "focus"]
|
|
29549
|
-
},
|
|
29550
|
-
value: {
|
|
29551
|
-
type: "string",
|
|
29552
|
-
description: "fill \u7684\u6587\u672C / select \u7684 option value"
|
|
29553
|
-
},
|
|
29554
|
-
key: {
|
|
29555
|
-
type: "string",
|
|
29556
|
-
description: "press \u6309\u952E\uFF0C\u9ED8\u8BA4 'Enter'"
|
|
29557
|
-
},
|
|
29558
|
-
deltaX: {
|
|
29559
|
-
type: "number",
|
|
29560
|
-
description: "\u6C34\u5E73\u6EDA\u52A8\u91CF\uFF0C\u9ED8\u8BA4 0"
|
|
29561
|
-
},
|
|
29562
|
-
deltaY: {
|
|
29563
|
-
type: "number",
|
|
29564
|
-
description: "\u5782\u76F4\u6EDA\u52A8\u91CF\uFF0C\u9ED8\u8BA4 300\uFF08\u6B63\u6570\u5411\u4E0B\uFF09"
|
|
29565
|
-
},
|
|
29566
|
-
waitMs: {
|
|
29567
|
-
type: "number",
|
|
29568
|
-
description: "\u64CD\u4F5C\u540E\u7B49\u5F85 ms\uFF0C\u9ED8\u8BA4 500\uFF0C\u6700\u5927 3000"
|
|
29569
|
-
}
|
|
29570
|
-
},
|
|
29571
|
-
required: ["ref", "action"]
|
|
29572
|
-
}
|
|
29573
|
-
}
|
|
29719
|
+
DISPATCH_ACTION_TOOL
|
|
29574
29720
|
]
|
|
29575
29721
|
}));
|
|
29576
29722
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
@@ -29578,7 +29724,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
29578
29724
|
const args = request.params.arguments || {};
|
|
29579
29725
|
try {
|
|
29580
29726
|
if (name === "inspect_page") {
|
|
29581
|
-
const { target, selector, includeInteractive = true, maxElements =
|
|
29727
|
+
const { target, selector, includeInteractive = true, maxElements = 20, includeElements = true, detail = false } = args;
|
|
29582
29728
|
const snapshot = await askChrome("inspectPageSnapshot", {
|
|
29583
29729
|
target,
|
|
29584
29730
|
selector,
|
|
@@ -29593,13 +29739,14 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
29593
29739
|
const interactiveCount = Array.isArray(interactive?.elements) ? interactive.elements.length : Array.isArray(interactive) ? interactive.length : void 0;
|
|
29594
29740
|
const summary = {
|
|
29595
29741
|
title: page?.metadata?.title,
|
|
29596
|
-
url: page?.metadata?.url,
|
|
29742
|
+
url: truncateUrl(page?.metadata?.url),
|
|
29597
29743
|
description: page?.metadata?.description,
|
|
29598
29744
|
links,
|
|
29599
29745
|
buttons,
|
|
29600
29746
|
forms,
|
|
29601
29747
|
interactiveCount
|
|
29602
29748
|
};
|
|
29749
|
+
const elements = includeElements && Array.isArray(interactive?.elements) ? interactive.elements : void 0;
|
|
29603
29750
|
return {
|
|
29604
29751
|
content: [
|
|
29605
29752
|
{
|
|
@@ -29609,10 +29756,11 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
29609
29756
|
summary,
|
|
29610
29757
|
page,
|
|
29611
29758
|
interactive,
|
|
29612
|
-
nextStepHint: "\u89C6\u89C9\u7528 capture_screenshot\uFF1B\u70B9\u51FB/\u8F93\u5165\u7528
|
|
29759
|
+
nextStepHint: "\u89C6\u89C9\u7528 capture_screenshot\uFF1B\u70B9\u51FB/\u8F93\u5165\u4F18\u5148\u7528 dispatch_action \u8BED\u4E49 locator\uFF0C\u5F53\u524D refs \u4E5F\u53EF\u76F4\u63A5\u4F7F\u7528\uFF1B\u8BF7\u6C42/\u6027\u80FD\u7528 list_network_requests / perf_metrics\u3002"
|
|
29613
29760
|
} : {
|
|
29614
29761
|
summary,
|
|
29615
|
-
|
|
29762
|
+
interactive: elements ? { viewport: interactive?.viewport, elements } : void 0,
|
|
29763
|
+
nextStepHint: "\u53EF\u76F4\u63A5\u7528 interactive.elements \u7684 ref \u8C03 dispatch_action\uFF1B\u8FDE\u7EED\u52A8\u4F5C\u653E\u8FDB actions\uFF0C\u64CD\u4F5C\u540E\u9700\u7EE7\u7EED\u5B9A\u4F4D\u65F6\u4F20 snapshotAfter:true\u3002"
|
|
29616
29764
|
}
|
|
29617
29765
|
)
|
|
29618
29766
|
}
|
|
@@ -29758,8 +29906,29 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
29758
29906
|
return { content: [{ type: "text", text: out(res) }] };
|
|
29759
29907
|
}
|
|
29760
29908
|
if (name === "eval_script") {
|
|
29761
|
-
const
|
|
29762
|
-
|
|
29909
|
+
const timeoutMs = clampNumber(args.timeoutMs, 1e4, 100, 3e4);
|
|
29910
|
+
const res = await askChrome("eval", {
|
|
29911
|
+
target: args.target,
|
|
29912
|
+
code: args.code,
|
|
29913
|
+
awaitPromise: args.awaitPromise !== false,
|
|
29914
|
+
timeoutMs
|
|
29915
|
+
}, { timeoutMs: timeoutMs + 3e3 });
|
|
29916
|
+
return { content: [{ type: "text", text: boundedOut(res, args.maxOutputLength) }] };
|
|
29917
|
+
}
|
|
29918
|
+
if (name === "page_request") {
|
|
29919
|
+
const timeoutMs = clampNumber(args.timeoutMs, 1e4, 100, 3e4);
|
|
29920
|
+
const maxOutputLength = clampNumber(args.maxOutputLength, DEFAULT_EVAL_OUTPUT_LENGTH, 200, MAX_EVAL_OUTPUT_LENGTH);
|
|
29921
|
+
const res = await askChrome("pageRequest", {
|
|
29922
|
+
target: args.target,
|
|
29923
|
+
url: args.url,
|
|
29924
|
+
method: args.method,
|
|
29925
|
+
headers: args.headers,
|
|
29926
|
+
body: args.body,
|
|
29927
|
+
responseType: args.responseType,
|
|
29928
|
+
timeoutMs,
|
|
29929
|
+
maxOutputLength
|
|
29930
|
+
}, { timeoutMs: timeoutMs + 3e3 });
|
|
29931
|
+
return { content: [{ type: "text", text: boundedOut(res, maxOutputLength) }] };
|
|
29763
29932
|
}
|
|
29764
29933
|
if (name === "list_network_requests") {
|
|
29765
29934
|
const { target, filter, method, status, resourceType, limit, priorityMode = "debug" } = args;
|
|
@@ -29807,6 +29976,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
29807
29976
|
}
|
|
29808
29977
|
if (name === "get_page_content") {
|
|
29809
29978
|
const { target, mode = "text", selector, maxLength = 8e3, offset = 0, includeMetadata = true } = args;
|
|
29979
|
+
const safeMaxLength = clampNumber(maxLength, 8e3, 1, 5e4);
|
|
29980
|
+
const safeOffset = clampNumber(offset, 0, 0, 1e8);
|
|
29810
29981
|
const validModes = ["text", "html", "structured"];
|
|
29811
29982
|
if (mode && !validModes.includes(mode)) {
|
|
29812
29983
|
return {
|
|
@@ -29816,22 +29987,18 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
29816
29987
|
}]
|
|
29817
29988
|
};
|
|
29818
29989
|
}
|
|
29819
|
-
|
|
29820
|
-
|
|
29821
|
-
|
|
29822
|
-
|
|
29823
|
-
|
|
29824
|
-
|
|
29825
|
-
|
|
29826
|
-
|
|
29827
|
-
|
|
29828
|
-
|
|
29829
|
-
|
|
29830
|
-
res2.hasMore = (res2.contentLength || 0) > offset + maxLength;
|
|
29831
|
-
}
|
|
29832
|
-
return { content: [{ type: "text", text: out(res2) }] };
|
|
29990
|
+
const res = await askChrome("getPageContent", {
|
|
29991
|
+
target,
|
|
29992
|
+
mode,
|
|
29993
|
+
selector,
|
|
29994
|
+
maxLength: safeMaxLength,
|
|
29995
|
+
offset: safeOffset,
|
|
29996
|
+
includeMetadata
|
|
29997
|
+
});
|
|
29998
|
+
if (typeof res?.metadata?.url === "string" && res.metadata.url.length > 200) {
|
|
29999
|
+
res.metadata.url = truncateUrl(res.metadata.url);
|
|
30000
|
+
res.metadata.urlTruncated = true;
|
|
29833
30001
|
}
|
|
29834
|
-
const res = await askChrome("getPageContent", { target, mode, selector, maxLength, includeMetadata });
|
|
29835
30002
|
return { content: [{ type: "text", text: out(res) }] };
|
|
29836
30003
|
}
|
|
29837
30004
|
if (name === "get_interactive_snapshot") {
|
|
@@ -29840,8 +30007,12 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
29840
30007
|
return { content: [{ type: "text", text: out(res) }] };
|
|
29841
30008
|
}
|
|
29842
30009
|
if (name === "dispatch_action") {
|
|
29843
|
-
const
|
|
29844
|
-
const res = await askChrome(
|
|
30010
|
+
const budget = calculateDispatchBudget(args);
|
|
30011
|
+
const res = await askChrome(
|
|
30012
|
+
"dispatchAction",
|
|
30013
|
+
{ ...args, timeoutMs: budget.executionTimeoutMs },
|
|
30014
|
+
{ timeoutMs: budget.serverTimeoutMs }
|
|
30015
|
+
);
|
|
29845
30016
|
return { content: [{ type: "text", text: out(res) }] };
|
|
29846
30017
|
}
|
|
29847
30018
|
return { content: [{ type: "text", text: `\u672A\u77E5\u5DE5\u5177\uFF1A${name}` }] };
|