ghost-bridge 1.1.0 → 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 +41 -9
- package/dist/cli.js +0 -0
- package/dist/server.js +191 -144
- package/extension/background.js +328 -167
- package/extension/bg-control.js +52 -0
- package/extension/bg-dom.js +411 -44
- package/extension/bg-runtime.js +17 -0
- package/extension/manifest.json +2 -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,7 @@ 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 |
|
|
98
100
|
| `eval_script` | Execute JavaScript, wait for returned promises, and cap arbitrary output |
|
|
99
101
|
| `page_request` | Send an authenticated page-context request and wait for the response in one call |
|
|
100
102
|
| `bind_tab` | Bind a Chrome tab as a named target such as `cases` or `app` |
|
|
@@ -115,12 +117,12 @@ Typical prompts:
|
|
|
115
117
|
|
|
116
118
|
Recommended flow:
|
|
117
119
|
|
|
118
|
-
1.
|
|
119
|
-
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
|
|
120
123
|
Default is optimized for transfer with JPEG; switch to `png` for pixel-level checks
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
5. Put consecutive fills/clicks into one `dispatch_action.actions` call and request `snapshotAfter` when the next page state is needed
|
|
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
|
|
124
126
|
|
|
125
127
|
Round-trip-efficient examples:
|
|
126
128
|
|
|
@@ -128,14 +130,42 @@ Round-trip-efficient examples:
|
|
|
128
130
|
{
|
|
129
131
|
"target": "app",
|
|
130
132
|
"actions": [
|
|
131
|
-
{
|
|
132
|
-
|
|
133
|
-
|
|
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
|
+
}
|
|
134
153
|
],
|
|
135
154
|
"snapshotAfter": true
|
|
136
155
|
}
|
|
137
156
|
```
|
|
138
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
|
+
|
|
139
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:
|
|
140
170
|
|
|
141
171
|
```javascript
|
|
@@ -150,6 +180,8 @@ Notes:
|
|
|
150
180
|
|
|
151
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"`.
|
|
152
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.
|
|
153
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.
|
|
154
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
|
|
155
187
|
|
package/dist/cli.js
CHANGED
|
File without changes
|
package/dist/server.js
CHANGED
|
@@ -28536,6 +28536,178 @@ 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";
|
|
@@ -29130,50 +29302,6 @@ async function askChrome(command, params = {}, options = {}) {
|
|
|
29130
29302
|
});
|
|
29131
29303
|
});
|
|
29132
29304
|
}
|
|
29133
|
-
function jsonText(data) {
|
|
29134
|
-
return typeof data === "string" ? data : JSON.stringify(data);
|
|
29135
|
-
}
|
|
29136
|
-
function compact(value) {
|
|
29137
|
-
if (Array.isArray(value)) {
|
|
29138
|
-
const cleaned = value.map(compact).filter((v) => v !== void 0);
|
|
29139
|
-
return cleaned.length ? cleaned : void 0;
|
|
29140
|
-
}
|
|
29141
|
-
if (value && typeof value === "object") {
|
|
29142
|
-
const cleaned = {};
|
|
29143
|
-
for (const [k, v] of Object.entries(value)) {
|
|
29144
|
-
const c = compact(v);
|
|
29145
|
-
if (c !== void 0) cleaned[k] = c;
|
|
29146
|
-
}
|
|
29147
|
-
return Object.keys(cleaned).length ? cleaned : void 0;
|
|
29148
|
-
}
|
|
29149
|
-
if (value === null || value === "") return void 0;
|
|
29150
|
-
return value;
|
|
29151
|
-
}
|
|
29152
|
-
function out(data) {
|
|
29153
|
-
return jsonText(compact(data));
|
|
29154
|
-
}
|
|
29155
|
-
function clampNumber(value, fallback, min, max) {
|
|
29156
|
-
const number3 = Number(value);
|
|
29157
|
-
if (!Number.isFinite(number3)) return fallback;
|
|
29158
|
-
return Math.min(max, Math.max(min, Math.round(number3)));
|
|
29159
|
-
}
|
|
29160
|
-
function boundedOut(data, maxLength = DEFAULT_EVAL_OUTPUT_LENGTH) {
|
|
29161
|
-
const text = out(data) ?? "undefined";
|
|
29162
|
-
const limit = clampNumber(maxLength, DEFAULT_EVAL_OUTPUT_LENGTH, 200, MAX_EVAL_OUTPUT_LENGTH);
|
|
29163
|
-
if (text.length <= limit) return text;
|
|
29164
|
-
const markerBudget = 100;
|
|
29165
|
-
const headLength = Math.max(100, Math.floor((limit - markerBudget) * 0.8));
|
|
29166
|
-
const tailLength = Math.max(50, limit - markerBudget - headLength);
|
|
29167
|
-
const omitted = Math.max(0, text.length - headLength - tailLength);
|
|
29168
|
-
return out({
|
|
29169
|
-
truncated: true,
|
|
29170
|
-
originalLength: text.length,
|
|
29171
|
-
content: `${text.slice(0, headLength)}
|
|
29172
|
-
... [\u5DF2\u7701\u7565 ${omitted} \u4E2A\u5B57\u7B26] ...
|
|
29173
|
-
${text.slice(-tailLength)}`,
|
|
29174
|
-
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"
|
|
29175
|
-
});
|
|
29176
|
-
}
|
|
29177
29305
|
function truncateUrl(url2, maxLen = 200) {
|
|
29178
29306
|
if (typeof url2 !== "string" || url2.length <= maxLen) return url2;
|
|
29179
29307
|
return url2.slice(0, maxLen) + "\u2026";
|
|
@@ -29568,7 +29696,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
29568
29696
|
},
|
|
29569
29697
|
{
|
|
29570
29698
|
name: "get_interactive_snapshot",
|
|
29571
|
-
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",
|
|
29572
29700
|
inputSchema: {
|
|
29573
29701
|
type: "object",
|
|
29574
29702
|
properties: {
|
|
@@ -29588,83 +29716,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
29588
29716
|
}
|
|
29589
29717
|
}
|
|
29590
29718
|
},
|
|
29591
|
-
|
|
29592
|
-
name: "dispatch_action",
|
|
29593
|
-
description: "\u6267\u884C\u5355\u4E2A\u6216\u4E00\u6279\u4EA4\u4E92\u52A8\u4F5C\u3002\u53EF\u7528\u5FEB\u7167 ref \u6216 CSS selector \u5B9A\u4F4D\uFF1B\u4F18\u5148\u7528 actions \u6279\u91CF\u5B8C\u6210\u8FDE\u7EED\u586B\u5199/\u70B9\u51FB\uFF0CsnapshotAfter:true \u53EF\u5728\u540C\u4E00\u6B21\u8C03\u7528\u8FD4\u56DE\u64CD\u4F5C\u540E\u5143\u7D20\uFF0C\u51CF\u5C11\u6A21\u578B\u5F80\u8FD4\u3002",
|
|
29594
|
-
inputSchema: {
|
|
29595
|
-
type: "object",
|
|
29596
|
-
properties: {
|
|
29597
|
-
target: TARGET_ARG,
|
|
29598
|
-
ref: {
|
|
29599
|
-
type: "string",
|
|
29600
|
-
description: "\u5143\u7D20 ref\uFF0C\u5982 'e1'\uFF08\u6765\u81EA get_interactive_snapshot\uFF09"
|
|
29601
|
-
},
|
|
29602
|
-
selector: {
|
|
29603
|
-
type: "string",
|
|
29604
|
-
description: "CSS \u9009\u62E9\u5668\uFF1B\u5DF2\u77E5\u9009\u62E9\u5668\u65F6\u53EF\u66FF\u4EE3 ref\uFF0C\u7701\u53BB\u524D\u7F6E\u5FEB\u7167"
|
|
29605
|
-
},
|
|
29606
|
-
action: {
|
|
29607
|
-
type: "string",
|
|
29608
|
-
enum: ["click", "fill", "press", "scroll", "select", "hover", "focus"]
|
|
29609
|
-
},
|
|
29610
|
-
value: {
|
|
29611
|
-
type: "string",
|
|
29612
|
-
description: "fill \u7684\u6587\u672C / select \u7684 option value"
|
|
29613
|
-
},
|
|
29614
|
-
key: {
|
|
29615
|
-
type: "string",
|
|
29616
|
-
description: "press \u6309\u952E\uFF0C\u9ED8\u8BA4 'Enter'"
|
|
29617
|
-
},
|
|
29618
|
-
deltaX: {
|
|
29619
|
-
type: "number",
|
|
29620
|
-
description: "\u6C34\u5E73\u6EDA\u52A8\u91CF\uFF0C\u9ED8\u8BA4 0"
|
|
29621
|
-
},
|
|
29622
|
-
deltaY: {
|
|
29623
|
-
type: "number",
|
|
29624
|
-
description: "\u5782\u76F4\u6EDA\u52A8\u91CF\uFF0C\u9ED8\u8BA4 300\uFF08\u6B63\u6570\u5411\u4E0B\uFF09"
|
|
29625
|
-
},
|
|
29626
|
-
waitMs: {
|
|
29627
|
-
type: "number",
|
|
29628
|
-
description: "\u64CD\u4F5C\u540E\u7B49\u5F85 ms\uFF0C\u9ED8\u8BA4 500\uFF0C\u6700\u5927 3000"
|
|
29629
|
-
},
|
|
29630
|
-
actions: {
|
|
29631
|
-
type: "array",
|
|
29632
|
-
maxItems: 20,
|
|
29633
|
-
description: "\u987A\u5E8F\u6267\u884C\u7684\u52A8\u4F5C\uFF0C\u6700\u591A 20 \u4E2A\uFF1B\u6BCF\u9879\u652F\u6301 ref/selector\u3001action\u3001value/key\u3001deltaX/deltaY\u3001waitMs",
|
|
29634
|
-
items: {
|
|
29635
|
-
type: "object",
|
|
29636
|
-
properties: {
|
|
29637
|
-
ref: { type: "string" },
|
|
29638
|
-
selector: { type: "string" },
|
|
29639
|
-
action: { type: "string", enum: ["click", "fill", "press", "scroll", "select", "hover", "focus"] },
|
|
29640
|
-
value: { type: "string" },
|
|
29641
|
-
key: { type: "string" },
|
|
29642
|
-
deltaX: { type: "number" },
|
|
29643
|
-
deltaY: { type: "number" },
|
|
29644
|
-
waitMs: { type: "number" }
|
|
29645
|
-
},
|
|
29646
|
-
required: ["action"]
|
|
29647
|
-
}
|
|
29648
|
-
},
|
|
29649
|
-
stopOnError: {
|
|
29650
|
-
type: "boolean",
|
|
29651
|
-
description: "\u6279\u91CF\u52A8\u4F5C\u5931\u8D25\u65F6\u7ACB\u5373\u505C\u6B62\uFF0C\u9ED8\u8BA4 true"
|
|
29652
|
-
},
|
|
29653
|
-
snapshotAfter: {
|
|
29654
|
-
type: "boolean",
|
|
29655
|
-
description: "\u5728\u6700\u540E\u4E00\u4E2A\u52A8\u4F5C\u540E\u8FD4\u56DE\u65B0\u7684\u4EA4\u4E92\u5FEB\u7167\uFF0C\u9ED8\u8BA4 false"
|
|
29656
|
-
},
|
|
29657
|
-
snapshotSelector: {
|
|
29658
|
-
type: "string",
|
|
29659
|
-
description: "\u9650\u5236\u64CD\u4F5C\u540E\u5FEB\u7167\u8303\u56F4"
|
|
29660
|
-
},
|
|
29661
|
-
snapshotMaxElements: {
|
|
29662
|
-
type: "number",
|
|
29663
|
-
description: "\u64CD\u4F5C\u540E\u5FEB\u7167\u5143\u7D20\u4E0A\u9650\uFF0C\u9ED8\u8BA4 20"
|
|
29664
|
-
}
|
|
29665
|
-
}
|
|
29666
|
-
}
|
|
29667
|
-
}
|
|
29719
|
+
DISPATCH_ACTION_TOOL
|
|
29668
29720
|
]
|
|
29669
29721
|
}));
|
|
29670
29722
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
@@ -29704,7 +29756,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
29704
29756
|
summary,
|
|
29705
29757
|
page,
|
|
29706
29758
|
interactive,
|
|
29707
|
-
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"
|
|
29708
29760
|
} : {
|
|
29709
29761
|
summary,
|
|
29710
29762
|
interactive: elements ? { viewport: interactive?.viewport, elements } : void 0,
|
|
@@ -29924,6 +29976,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
29924
29976
|
}
|
|
29925
29977
|
if (name === "get_page_content") {
|
|
29926
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);
|
|
29927
29981
|
const validModes = ["text", "html", "structured"];
|
|
29928
29982
|
if (mode && !validModes.includes(mode)) {
|
|
29929
29983
|
return {
|
|
@@ -29933,22 +29987,14 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
29933
29987
|
}]
|
|
29934
29988
|
};
|
|
29935
29989
|
}
|
|
29936
|
-
|
|
29937
|
-
|
|
29938
|
-
|
|
29939
|
-
|
|
29940
|
-
|
|
29941
|
-
|
|
29942
|
-
|
|
29943
|
-
|
|
29944
|
-
if (typeof res2?.content === "string") {
|
|
29945
|
-
res2.content = res2.content.slice(offset, offset + maxLength);
|
|
29946
|
-
res2.offset = offset;
|
|
29947
|
-
res2.hasMore = (res2.contentLength || 0) > offset + maxLength;
|
|
29948
|
-
}
|
|
29949
|
-
return { content: [{ type: "text", text: out(res2) }] };
|
|
29950
|
-
}
|
|
29951
|
-
const res = await askChrome("getPageContent", { target, mode, selector, maxLength, includeMetadata });
|
|
29990
|
+
const res = await askChrome("getPageContent", {
|
|
29991
|
+
target,
|
|
29992
|
+
mode,
|
|
29993
|
+
selector,
|
|
29994
|
+
maxLength: safeMaxLength,
|
|
29995
|
+
offset: safeOffset,
|
|
29996
|
+
includeMetadata
|
|
29997
|
+
});
|
|
29952
29998
|
if (typeof res?.metadata?.url === "string" && res.metadata.url.length > 200) {
|
|
29953
29999
|
res.metadata.url = truncateUrl(res.metadata.url);
|
|
29954
30000
|
res.metadata.urlTruncated = true;
|
|
@@ -29961,11 +30007,12 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
29961
30007
|
return { content: [{ type: "text", text: out(res) }] };
|
|
29962
30008
|
}
|
|
29963
30009
|
if (name === "dispatch_action") {
|
|
29964
|
-
const
|
|
29965
|
-
|
|
29966
|
-
|
|
29967
|
-
|
|
29968
|
-
|
|
30010
|
+
const budget = calculateDispatchBudget(args);
|
|
30011
|
+
const res = await askChrome(
|
|
30012
|
+
"dispatchAction",
|
|
30013
|
+
{ ...args, timeoutMs: budget.executionTimeoutMs },
|
|
30014
|
+
{ timeoutMs: budget.serverTimeoutMs }
|
|
30015
|
+
);
|
|
29969
30016
|
return { content: [{ type: "text", text: out(res) }] };
|
|
29970
30017
|
}
|
|
29971
30018
|
return { content: [{ type: "text", text: `\u672A\u77E5\u5DE5\u5177\uFF1A${name}` }] };
|