craftdriver 1.7.0 → 1.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/CHANGELOG.md +23 -11
- package/README.md +13 -4
- package/dist/cli/agentSession.d.ts +62 -0
- package/dist/cli/agentSession.d.ts.map +1 -0
- package/dist/cli/agentSession.js +100 -0
- package/dist/cli/agentSession.js.map +1 -0
- package/dist/cli/artifactPaths.d.ts +42 -0
- package/dist/cli/artifactPaths.d.ts.map +1 -0
- package/dist/cli/artifactPaths.js +144 -0
- package/dist/cli/artifactPaths.js.map +1 -0
- package/dist/cli/bounds.d.ts +88 -0
- package/dist/cli/bounds.d.ts.map +1 -0
- package/dist/cli/bounds.js +128 -0
- package/dist/cli/bounds.js.map +1 -0
- package/dist/cli/client.d.ts +3 -0
- package/dist/cli/client.d.ts.map +1 -1
- package/dist/cli/client.js +24 -8
- package/dist/cli/client.js.map +1 -1
- package/dist/cli/daemon.d.ts +3 -0
- package/dist/cli/daemon.d.ts.map +1 -1
- package/dist/cli/daemon.js +152 -39
- package/dist/cli/daemon.js.map +1 -1
- package/dist/cli/defaults.d.ts +23 -3
- package/dist/cli/defaults.d.ts.map +1 -1
- package/dist/cli/defaults.js +50 -5
- package/dist/cli/defaults.js.map +1 -1
- package/dist/cli/dispatcher.d.ts +54 -11
- package/dist/cli/dispatcher.d.ts.map +1 -1
- package/dist/cli/dispatcher.js +851 -54
- package/dist/cli/dispatcher.js.map +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +305 -56
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/init.d.ts +14 -14
- package/dist/cli/init.d.ts.map +1 -1
- package/dist/cli/init.js +328 -180
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/journal.d.ts +153 -0
- package/dist/cli/journal.d.ts.map +1 -0
- package/dist/cli/journal.js +252 -0
- package/dist/cli/journal.js.map +1 -0
- package/dist/cli/lineReader.d.ts +33 -0
- package/dist/cli/lineReader.d.ts.map +1 -0
- package/dist/cli/lineReader.js +66 -0
- package/dist/cli/lineReader.js.map +1 -0
- package/dist/cli/locatorCandidates.d.ts +44 -0
- package/dist/cli/locatorCandidates.d.ts.map +1 -0
- package/dist/cli/locatorCandidates.js +190 -0
- package/dist/cli/locatorCandidates.js.map +1 -0
- package/dist/cli/mcp/artifacts.d.ts +39 -1
- package/dist/cli/mcp/artifacts.d.ts.map +1 -1
- package/dist/cli/mcp/artifacts.js +104 -9
- package/dist/cli/mcp/artifacts.js.map +1 -1
- package/dist/cli/mcp/bounds.d.ts +65 -0
- package/dist/cli/mcp/bounds.d.ts.map +1 -0
- package/dist/cli/mcp/bounds.js +124 -0
- package/dist/cli/mcp/bounds.js.map +1 -0
- package/dist/cli/mcp/params.d.ts +38 -0
- package/dist/cli/mcp/params.d.ts.map +1 -0
- package/dist/cli/mcp/params.js +174 -0
- package/dist/cli/mcp/params.js.map +1 -0
- package/dist/cli/mcp/server.d.ts +94 -16
- package/dist/cli/mcp/server.d.ts.map +1 -1
- package/dist/cli/mcp/server.js +304 -150
- package/dist/cli/mcp/server.js.map +1 -1
- package/dist/cli/mcp/tools.d.ts +62 -17
- package/dist/cli/mcp/tools.d.ts.map +1 -1
- package/dist/cli/mcp/tools.js +373 -158
- package/dist/cli/mcp/tools.js.map +1 -1
- package/dist/cli/pageSemantics.d.ts +42 -0
- package/dist/cli/pageSemantics.d.ts.map +1 -0
- package/dist/cli/pageSemantics.js +194 -0
- package/dist/cli/pageSemantics.js.map +1 -0
- package/dist/cli/parseArgs.d.ts +6 -18
- package/dist/cli/parseArgs.d.ts.map +1 -1
- package/dist/cli/parseArgs.js +689 -45
- package/dist/cli/parseArgs.js.map +1 -1
- package/dist/cli/protocol.d.ts +8 -0
- package/dist/cli/protocol.d.ts.map +1 -1
- package/dist/cli/sessionRegistry.d.ts +42 -0
- package/dist/cli/sessionRegistry.d.ts.map +1 -0
- package/dist/cli/sessionRegistry.js +99 -0
- package/dist/cli/sessionRegistry.js.map +1 -0
- package/dist/cli/snapshot.d.ts +100 -18
- package/dist/cli/snapshot.d.ts.map +1 -1
- package/dist/cli/snapshot.js +248 -102
- package/dist/cli/snapshot.js.map +1 -1
- package/dist/cli/stateStore.d.ts +50 -0
- package/dist/cli/stateStore.d.ts.map +1 -0
- package/dist/cli/stateStore.js +122 -0
- package/dist/cli/stateStore.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/bidi/index.d.ts +1 -1
- package/dist/lib/bidi/index.d.ts.map +1 -1
- package/dist/lib/bidi/index.js +2 -2
- package/dist/lib/bidi/index.js.map +1 -1
- package/dist/lib/bidi/logs.d.ts +3 -0
- package/dist/lib/bidi/logs.d.ts.map +1 -1
- package/dist/lib/bidi/logs.js +7 -0
- package/dist/lib/bidi/logs.js.map +1 -1
- package/dist/lib/bidi/network.d.ts +14 -1
- package/dist/lib/bidi/network.d.ts.map +1 -1
- package/dist/lib/bidi/network.js +45 -7
- package/dist/lib/bidi/network.js.map +1 -1
- package/dist/lib/bidi/storage.d.ts +30 -6
- package/dist/lib/bidi/storage.d.ts.map +1 -1
- package/dist/lib/bidi/storage.js +190 -45
- package/dist/lib/bidi/storage.js.map +1 -1
- package/dist/lib/bidi/types.d.ts +7 -2
- package/dist/lib/bidi/types.d.ts.map +1 -1
- package/dist/lib/browser.d.ts +6 -3
- package/dist/lib/browser.d.ts.map +1 -1
- package/dist/lib/browser.js +140 -30
- package/dist/lib/browser.js.map +1 -1
- package/dist/lib/browserContext.d.ts +54 -18
- package/dist/lib/browserContext.d.ts.map +1 -1
- package/dist/lib/browserContext.js +295 -92
- package/dist/lib/browserContext.js.map +1 -1
- package/dist/lib/by.d.ts +3 -3
- package/dist/lib/by.d.ts.map +1 -1
- package/dist/lib/by.js +7 -3
- package/dist/lib/by.js.map +1 -1
- package/dist/lib/driver.d.ts +7 -0
- package/dist/lib/driver.d.ts.map +1 -1
- package/dist/lib/driver.js +12 -0
- package/dist/lib/driver.js.map +1 -1
- package/dist/lib/errors.d.ts +8 -0
- package/dist/lib/errors.d.ts.map +1 -1
- package/dist/lib/errors.js +8 -0
- package/dist/lib/errors.js.map +1 -1
- package/dist/lib/initScript.d.ts +9 -0
- package/dist/lib/initScript.d.ts.map +1 -0
- package/dist/lib/initScript.js +14 -0
- package/dist/lib/initScript.js.map +1 -0
- package/dist/lib/page.d.ts +22 -0
- package/dist/lib/page.d.ts.map +1 -1
- package/dist/lib/page.js +48 -0
- package/dist/lib/page.js.map +1 -1
- package/dist/lib/secureFile.d.ts +8 -0
- package/dist/lib/secureFile.d.ts.map +1 -0
- package/dist/lib/secureFile.js +67 -0
- package/dist/lib/secureFile.js.map +1 -0
- package/dist/lib/sessionStateValidation.d.ts +35 -0
- package/dist/lib/sessionStateValidation.d.ts.map +1 -0
- package/dist/lib/sessionStateValidation.js +223 -0
- package/dist/lib/sessionStateValidation.js.map +1 -0
- package/dist/lib/tracing.d.ts +2 -1
- package/dist/lib/tracing.d.ts.map +1 -1
- package/dist/lib/tracing.js +12 -1
- package/dist/lib/tracing.js.map +1 -1
- package/docs/agents.md +56 -61
- package/docs/api-reference.md +2 -1
- package/docs/browser-api.md +24 -4
- package/docs/browser-context.md +18 -12
- package/docs/cli.md +346 -26
- package/docs/error-codes.md +5 -4
- package/docs/getting-started.md +6 -1
- package/docs/index.md +1 -1
- package/docs/mcp.md +129 -62
- package/docs/public/examples/agent-actions.html +48 -0
- package/docs/public/examples/agent-debug.html +86 -0
- package/docs/public/examples/agent-late-mutation.html +18 -0
- package/docs/public/examples/agent-ref-shift.html +33 -0
- package/docs/recipes/login-once-reuse-session.md +32 -4
- package/docs/session-management.md +64 -3
- package/docs/why-craftdriver.md +1 -1
- package/package.json +3 -1
- package/skills/craftdriver/SKILL.md +59 -75
- package/skills/craftdriver/agents/openai.yaml +4 -0
- package/skills/craftdriver/cheatsheet.md +18 -4
- package/skills/craftdriver/cli.md +194 -68
- package/skills/craftdriver/patterns.md +14 -8
- package/skills/craftdriver/workflow.md +149 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../../src/cli/mcp/tools.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../../src/cli/mcp/tools.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,KAAK,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAClF,OAAO,EAA+B,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AAE3E;;;;;;GAMG;AACH,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,wDAAwD;IACxD,YAAY,EAAE,OAAO,CAAC;IACtB,yDAAyD;IACzD,eAAe,EAAE,OAAO,CAAC;IACzB,0EAA0E;IAC1E,cAAc,EAAE,OAAO,CAAC;IACxB,kEAAkE;IAClE,aAAa,EAAE,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,UAAU,CAAC;IACnB,WAAW,EAAE,eAAe,CAAC;IAC7B,gDAAgD;IAChD,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,CAAC;CAC/F;AAoCD,eAAO,MAAM,KAAK,EAAE,OAAO,EA4a1B,CAAC;AAIF,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAEzD;AAED,+EAA+E;AAC/E,wBAAgB,cAAc,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAErE;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEtF;AAED,gFAAgF;AAChF,wBAAsB,OAAO,CAC3B,OAAO,EAAE,kBAAkB,EAC3B,IAAI,EAAE,OAAO,EACb,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,OAAO,CAAC,OAAO,CAAC,CAElB;AAED;;;;GAIG;AACH,wBAAsB,eAAe,CACnC,OAAO,EAAE,kBAAkB,EAC3B,IAAI,EAAE,OAAO,EACb,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,OAAO,CAAC,mBAAmB,CAAC,CAE9B"}
|
package/dist/cli/mcp/tools.js
CHANGED
|
@@ -1,94 +1,201 @@
|
|
|
1
|
-
import {
|
|
2
|
-
const
|
|
1
|
+
import { toInputSchema, validateArgs } from './params.js';
|
|
2
|
+
const SELECTOR = {
|
|
3
3
|
type: 'string',
|
|
4
|
+
required: true,
|
|
4
5
|
description: "CSS by default. Switch kind with a prefix: 'role=button[name=Submit]', " +
|
|
5
6
|
"'text=Sign In', 'text*=Sign', 'label=Email', 'placeholder=...', " +
|
|
6
|
-
"'testid=...', 'xpath=...', 'id=...', 'name=...'.",
|
|
7
|
+
"'testid=...', 'xpath=...', 'id=...', 'name=...', or 'ref=eN' from a snapshot.",
|
|
7
8
|
};
|
|
9
|
+
const OPTIONAL_SELECTOR = { ...SELECTOR, required: false };
|
|
10
|
+
const TIMEOUT = { type: 'number', min: 0, max: 300_000, description: 'Milliseconds.' };
|
|
11
|
+
/** A page action that changes what the user sees. */
|
|
12
|
+
function mutating(title, openWorld = true) {
|
|
13
|
+
return {
|
|
14
|
+
title,
|
|
15
|
+
readOnlyHint: false,
|
|
16
|
+
destructiveHint: false,
|
|
17
|
+
idempotentHint: false,
|
|
18
|
+
openWorldHint: openWorld,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
/** An inspection that changes nothing. */
|
|
22
|
+
function readOnly(title, openWorld = true) {
|
|
23
|
+
return {
|
|
24
|
+
title,
|
|
25
|
+
readOnlyHint: true,
|
|
26
|
+
destructiveHint: false,
|
|
27
|
+
idempotentHint: true,
|
|
28
|
+
openWorldHint: openWorld,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
8
31
|
export const TOOLS = [
|
|
32
|
+
// ---- navigation and core actions ---------------------------------------
|
|
9
33
|
{
|
|
10
34
|
name: 'browser_navigate',
|
|
11
35
|
description: 'Navigate the active page to a URL. Waits for load.',
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
properties: { url: { type: 'string' } },
|
|
15
|
-
required: ['url'],
|
|
16
|
-
},
|
|
17
|
-
mutating: true,
|
|
36
|
+
params: { url: { type: 'string', required: true, maxLength: 2048 } },
|
|
37
|
+
annotations: mutating('Navigate'),
|
|
18
38
|
toDispatch: (a) => ({ cmd: 'go', args: { url: a.url } }),
|
|
19
39
|
},
|
|
20
40
|
{
|
|
21
41
|
name: 'browser_click',
|
|
22
42
|
description: 'Click an element. Auto-waits for visible+enabled. Returns NO_MATCH immediately ' +
|
|
23
|
-
'when the selector matches zero elements at t=0.',
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
43
|
+
'when the selector matches zero elements at t=0. Set double for a double-click.',
|
|
44
|
+
params: {
|
|
45
|
+
selector: SELECTOR,
|
|
46
|
+
double: { type: 'boolean', description: 'Double-click instead of single.' },
|
|
47
|
+
timeout_ms: TIMEOUT,
|
|
28
48
|
},
|
|
29
|
-
|
|
30
|
-
toDispatch: (a) => ({
|
|
49
|
+
annotations: mutating('Click'),
|
|
50
|
+
toDispatch: (a) => ({
|
|
51
|
+
cmd: a.double === true ? 'dblclick' : 'click',
|
|
52
|
+
args: { selector: a.selector, timeout: a.timeout_ms },
|
|
53
|
+
}),
|
|
31
54
|
},
|
|
32
55
|
{
|
|
33
56
|
name: 'browser_fill',
|
|
34
|
-
description: 'Fill an input/textarea
|
|
35
|
-
'For checkboxes
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
value: { type: 'string' },
|
|
41
|
-
timeout_ms: { type: 'number' },
|
|
42
|
-
},
|
|
43
|
-
required: ['selector', 'value'],
|
|
57
|
+
description: 'Fill an input/textarea. Clears first, then enters the value with real key events. ' +
|
|
58
|
+
'For checkboxes use browser_element with action=check.',
|
|
59
|
+
params: {
|
|
60
|
+
selector: SELECTOR,
|
|
61
|
+
value: { type: 'string', required: true },
|
|
62
|
+
timeout_ms: TIMEOUT,
|
|
44
63
|
},
|
|
45
|
-
|
|
64
|
+
annotations: mutating('Fill field'),
|
|
46
65
|
toDispatch: (a) => ({
|
|
47
66
|
cmd: 'fill',
|
|
48
67
|
args: { selector: a.selector, value: a.value, timeout: a.timeout_ms },
|
|
49
68
|
}),
|
|
50
69
|
},
|
|
70
|
+
{
|
|
71
|
+
name: 'browser_type',
|
|
72
|
+
description: 'Type text into whatever currently holds focus. No selector — use browser_fill to ' +
|
|
73
|
+
'target a field, or browser_element with action=focus first to append.',
|
|
74
|
+
params: { text: { type: 'string', required: true } },
|
|
75
|
+
annotations: mutating('Type into focus'),
|
|
76
|
+
toDispatch: (a) => ({ cmd: 'type', args: { text: a.text } }),
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
name: 'browser_element',
|
|
80
|
+
description: 'Act on one element: dblclick, focus, scroll, clear, check, uncheck, or select. ' +
|
|
81
|
+
'check/uncheck are idempotent and read the state back. select matches an <option> ' +
|
|
82
|
+
'by its value attribute, not its label.',
|
|
83
|
+
params: {
|
|
84
|
+
action: {
|
|
85
|
+
type: 'string',
|
|
86
|
+
required: true,
|
|
87
|
+
enum: ['dblclick', 'focus', 'scroll', 'clear', 'check', 'uncheck', 'select'],
|
|
88
|
+
},
|
|
89
|
+
selector: SELECTOR,
|
|
90
|
+
value: { type: 'string', description: 'Option value; required for action=select.' },
|
|
91
|
+
timeout_ms: TIMEOUT,
|
|
92
|
+
},
|
|
93
|
+
annotations: mutating('Element action'),
|
|
94
|
+
toDispatch: (a) => ({
|
|
95
|
+
cmd: a.action,
|
|
96
|
+
args: {
|
|
97
|
+
selector: a.selector,
|
|
98
|
+
...(a.value !== undefined ? { value: a.value } : {}),
|
|
99
|
+
timeout: a.timeout_ms,
|
|
100
|
+
},
|
|
101
|
+
}),
|
|
102
|
+
},
|
|
51
103
|
{
|
|
52
104
|
name: 'browser_press',
|
|
53
|
-
description: 'Press a
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
type: 'object',
|
|
57
|
-
properties: { key: { type: 'string' }, selector: STRING_SELECTOR },
|
|
58
|
-
required: ['key'],
|
|
59
|
-
},
|
|
60
|
-
mutating: true,
|
|
105
|
+
description: 'Press a key (e.g. "Enter", "Tab", "Control+A"). Optional selector focuses first.',
|
|
106
|
+
params: { key: { type: 'string', required: true, maxLength: 64 }, selector: OPTIONAL_SELECTOR },
|
|
107
|
+
annotations: mutating('Press key'),
|
|
61
108
|
toDispatch: (a) => ({
|
|
62
109
|
cmd: 'press',
|
|
63
110
|
args: { key: a.key, ...(a.selector ? { selector: a.selector } : {}) },
|
|
64
111
|
}),
|
|
65
112
|
},
|
|
113
|
+
{
|
|
114
|
+
name: 'browser_key',
|
|
115
|
+
description: 'Low-level keyboard control: hold a key down, release it, or press it. Use for ' +
|
|
116
|
+
'modifier combinations that browser_press cannot express.',
|
|
117
|
+
params: {
|
|
118
|
+
action: { type: 'string', required: true, enum: ['press', 'down', 'up'] },
|
|
119
|
+
key: { type: 'string', required: true, maxLength: 64 },
|
|
120
|
+
selector: OPTIONAL_SELECTOR,
|
|
121
|
+
},
|
|
122
|
+
annotations: mutating('Keyboard'),
|
|
123
|
+
toDispatch: (a) => ({
|
|
124
|
+
cmd: 'key',
|
|
125
|
+
args: { action: a.action, key: a.key, ...(a.selector ? { selector: a.selector } : {}) },
|
|
126
|
+
}),
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
name: 'browser_mouse',
|
|
130
|
+
description: 'Move, click, press, release, or wheel the mouse, by element or by coordinate. ' +
|
|
131
|
+
'Use for drag, hover-reveal, and canvas interactions.',
|
|
132
|
+
params: {
|
|
133
|
+
action: { type: 'string', required: true, enum: ['move', 'click', 'down', 'up', 'wheel'] },
|
|
134
|
+
selector: OPTIONAL_SELECTOR,
|
|
135
|
+
x: { type: 'number' },
|
|
136
|
+
y: { type: 'number' },
|
|
137
|
+
button: { type: 'string', enum: ['left', 'middle', 'right'] },
|
|
138
|
+
delta_x: { type: 'number' },
|
|
139
|
+
delta_y: { type: 'number' },
|
|
140
|
+
},
|
|
141
|
+
annotations: mutating('Mouse'),
|
|
142
|
+
toDispatch: (a) => ({
|
|
143
|
+
cmd: 'mouse',
|
|
144
|
+
args: {
|
|
145
|
+
action: a.action,
|
|
146
|
+
...(a.selector ? { selector: a.selector } : {}),
|
|
147
|
+
x: a.x,
|
|
148
|
+
y: a.y,
|
|
149
|
+
button: a.button,
|
|
150
|
+
deltaX: a.delta_x,
|
|
151
|
+
deltaY: a.delta_y,
|
|
152
|
+
},
|
|
153
|
+
}),
|
|
154
|
+
},
|
|
66
155
|
{
|
|
67
156
|
name: 'browser_hover',
|
|
68
157
|
description: 'Hover over an element.',
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
properties: { selector: STRING_SELECTOR, timeout_ms: { type: 'number' } },
|
|
72
|
-
required: ['selector'],
|
|
73
|
-
},
|
|
74
|
-
mutating: true,
|
|
158
|
+
params: { selector: SELECTOR, timeout_ms: TIMEOUT },
|
|
159
|
+
annotations: mutating('Hover'),
|
|
75
160
|
toDispatch: (a) => ({ cmd: 'hover', args: { selector: a.selector, timeout: a.timeout_ms } }),
|
|
76
161
|
},
|
|
162
|
+
{
|
|
163
|
+
name: 'browser_upload',
|
|
164
|
+
description: 'Set files on a file input. Paths must already exist and are never echoed back. ' +
|
|
165
|
+
'Bounded in count.',
|
|
166
|
+
params: {
|
|
167
|
+
selector: SELECTOR,
|
|
168
|
+
files: { type: 'string[]', required: true, maxItems: 10, maxLength: 4096 },
|
|
169
|
+
},
|
|
170
|
+
annotations: { ...mutating('Upload files'), openWorldHint: true },
|
|
171
|
+
toDispatch: (a) => ({ cmd: 'upload', args: { selector: a.selector, files: a.files } }),
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
name: 'browser_dialog',
|
|
175
|
+
description: 'Inspect, accept, or dismiss a native dialog (alert/confirm/prompt). An open dialog ' +
|
|
176
|
+
'blocks page script, so handle it before anything else.',
|
|
177
|
+
params: {
|
|
178
|
+
action: { type: 'string', required: true, enum: ['inspect', 'accept', 'dismiss'] },
|
|
179
|
+
text: { type: 'string', description: 'Prompt answer; used with accept.' },
|
|
180
|
+
},
|
|
181
|
+
annotations: mutating('Dialog'),
|
|
182
|
+
toDispatch: (a) => ({
|
|
183
|
+
cmd: 'dialog',
|
|
184
|
+
args: { action: a.action, ...(a.text !== undefined ? { text: a.text } : {}) },
|
|
185
|
+
}),
|
|
186
|
+
},
|
|
187
|
+
// ---- inspection ---------------------------------------------------------
|
|
77
188
|
{
|
|
78
189
|
name: 'browser_find',
|
|
79
|
-
description: 'Locate elements without acting on them. Returns up to `limit` matches with ' +
|
|
80
|
-
'
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
limit: { type: 'number' },
|
|
87
|
-
offset: { type: 'number' },
|
|
88
|
-
},
|
|
89
|
-
required: ['selector'],
|
|
190
|
+
description: 'Locate elements without acting on them. Returns up to `limit` matches with tag, ' +
|
|
191
|
+
'text snippet, and visibility. Use `all` to enumerate.',
|
|
192
|
+
params: {
|
|
193
|
+
selector: SELECTOR,
|
|
194
|
+
all: { type: 'boolean' },
|
|
195
|
+
limit: { type: 'number', min: 1, max: 500, integer: true },
|
|
196
|
+
offset: { type: 'number', min: 0, integer: true },
|
|
90
197
|
},
|
|
91
|
-
|
|
198
|
+
annotations: readOnly('Find elements'),
|
|
92
199
|
toDispatch: (a) => ({
|
|
93
200
|
cmd: 'find',
|
|
94
201
|
args: { selector: a.selector, all: a.all, limit: a.limit, offset: a.offset },
|
|
@@ -96,55 +203,27 @@ export const TOOLS = [
|
|
|
96
203
|
},
|
|
97
204
|
{
|
|
98
205
|
name: 'browser_exists',
|
|
99
|
-
description: 'Zero-wait probe: returns { exists, count } immediately. Call this BEFORE ' +
|
|
100
|
-
'
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
type: 'object',
|
|
104
|
-
properties: { selector: STRING_SELECTOR },
|
|
105
|
-
required: ['selector'],
|
|
106
|
-
},
|
|
107
|
-
mutating: false,
|
|
206
|
+
description: 'Zero-wait probe: returns { exists, count } immediately. Call this BEFORE clicking or ' +
|
|
207
|
+
'waiting when guessing a selector — one roundtrip instead of the full timeout.',
|
|
208
|
+
params: { selector: SELECTOR },
|
|
209
|
+
annotations: readOnly('Probe selector'),
|
|
108
210
|
toDispatch: (a) => ({ cmd: 'exists', args: { selector: a.selector } }),
|
|
109
211
|
},
|
|
110
|
-
{
|
|
111
|
-
name: 'browser_wait',
|
|
112
|
-
description: 'Wait for a condition: selector to become visible/hidden/attached/detached, ' +
|
|
113
|
-
'or a load state (load|domcontentloaded|networkidle).',
|
|
114
|
-
inputSchema: {
|
|
115
|
-
type: 'object',
|
|
116
|
-
properties: {
|
|
117
|
-
target: { type: 'string' },
|
|
118
|
-
kind: { type: 'string', enum: ['selector', 'load'] },
|
|
119
|
-
state: { type: 'string' },
|
|
120
|
-
timeout_ms: { type: 'number' },
|
|
121
|
-
},
|
|
122
|
-
required: ['target'],
|
|
123
|
-
},
|
|
124
|
-
mutating: false,
|
|
125
|
-
toDispatch: (a) => ({
|
|
126
|
-
cmd: 'wait',
|
|
127
|
-
args: { target: a.target, kind: a.kind ?? 'selector', state: a.state, timeout: a.timeout_ms },
|
|
128
|
-
}),
|
|
129
|
-
},
|
|
130
212
|
{
|
|
131
213
|
name: 'browser_read',
|
|
132
|
-
description: 'Read text/attr/value/is-state from an element, or page body text when no ' +
|
|
133
|
-
'
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
enum: ['visible', 'enabled', 'checked'],
|
|
143
|
-
description: 'state when kind=is',
|
|
144
|
-
},
|
|
214
|
+
description: 'Read text/attr/value/is-state from an element, or page body text when no selector ' +
|
|
215
|
+
'is given. `kind` defaults to "text".',
|
|
216
|
+
params: {
|
|
217
|
+
selector: OPTIONAL_SELECTOR,
|
|
218
|
+
kind: { type: 'string', enum: ['text', 'attr', 'value', 'is'] },
|
|
219
|
+
name: { type: 'string', description: 'Attribute name when kind=attr.' },
|
|
220
|
+
what: {
|
|
221
|
+
type: 'string',
|
|
222
|
+
enum: ['visible', 'enabled', 'checked'],
|
|
223
|
+
description: 'State when kind=is.',
|
|
145
224
|
},
|
|
146
225
|
},
|
|
147
|
-
|
|
226
|
+
annotations: readOnly('Read element'),
|
|
148
227
|
toDispatch: (a) => {
|
|
149
228
|
const kind = a.kind ?? 'text';
|
|
150
229
|
if (kind === 'attr')
|
|
@@ -157,88 +236,203 @@ export const TOOLS = [
|
|
|
157
236
|
},
|
|
158
237
|
},
|
|
159
238
|
{
|
|
160
|
-
name: '
|
|
161
|
-
description: '
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
239
|
+
name: 'browser_wait',
|
|
240
|
+
description: 'Wait for a selector to become visible/hidden/attached/detached, or for a load state ' +
|
|
241
|
+
'(load|domcontentloaded|networkidle).',
|
|
242
|
+
params: {
|
|
243
|
+
target: { type: 'string', required: true },
|
|
244
|
+
kind: { type: 'string', enum: ['selector', 'load'] },
|
|
245
|
+
state: { type: 'string', maxLength: 32 },
|
|
246
|
+
timeout_ms: TIMEOUT,
|
|
247
|
+
},
|
|
248
|
+
annotations: readOnly('Wait for condition'),
|
|
249
|
+
toDispatch: (a) => ({
|
|
250
|
+
cmd: 'wait',
|
|
251
|
+
args: { target: a.target, kind: a.kind ?? 'selector', state: a.state, timeout: a.timeout_ms },
|
|
252
|
+
}),
|
|
165
253
|
},
|
|
166
254
|
{
|
|
167
255
|
name: 'browser_snapshot',
|
|
168
|
-
description: 'Sanitized accessibility-tree summary of the active page: one line ' +
|
|
169
|
-
'
|
|
170
|
-
'
|
|
171
|
-
'
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
inputSchema: { type: 'object', properties: {} },
|
|
175
|
-
mutating: false,
|
|
256
|
+
description: 'Sanitized accessibility-tree summary of the active page: one line per visible ' +
|
|
257
|
+
'interactive element with role, accessible name, and a stable ref (e1, e2, …). Use ' +
|
|
258
|
+
'`ref=eN` as a selector for later calls. A ref binds to one element and fails ' +
|
|
259
|
+
'STALE_REF rather than drifting; it is exploration state and never belongs in a test.',
|
|
260
|
+
params: {},
|
|
261
|
+
annotations: readOnly('Page snapshot'),
|
|
176
262
|
toDispatch: () => ({ cmd: 'snapshot', args: {} }),
|
|
177
263
|
},
|
|
264
|
+
{
|
|
265
|
+
name: 'browser_locators',
|
|
266
|
+
description: 'Turn an element into durable selectors for a committed test, ordered by resilience ' +
|
|
267
|
+
'(role+name, label, test id, unique text, minimal CSS) and each re-checked against ' +
|
|
268
|
+
'the live page. Use this to convert a snapshot ref into something a test can keep. ' +
|
|
269
|
+
'No candidate ever contains a ref.',
|
|
270
|
+
params: {
|
|
271
|
+
selector: SELECTOR,
|
|
272
|
+
limit: { type: 'number', min: 1, max: 20, integer: true },
|
|
273
|
+
},
|
|
274
|
+
annotations: readOnly('Durable selectors'),
|
|
275
|
+
toDispatch: (a) => ({ cmd: 'locators', args: { selector: a.selector, limit: a.limit } }),
|
|
276
|
+
},
|
|
178
277
|
{
|
|
179
278
|
name: 'browser_screenshot',
|
|
180
|
-
description: 'Capture a PNG of the active page or
|
|
181
|
-
'
|
|
182
|
-
'
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
type: 'string',
|
|
191
|
-
description: 'Absolute path to write the PNG to. If omitted, an artifact ' +
|
|
192
|
-
'path is auto-allocated.',
|
|
193
|
-
},
|
|
194
|
-
},
|
|
279
|
+
description: 'Capture a PNG of the active page or one element. Written to disk under the ' +
|
|
280
|
+
'per-session artifact directory; the response carries the path and byte count, not ' +
|
|
281
|
+
'image tokens.',
|
|
282
|
+
// No destination parameter: the description promises the per-session
|
|
283
|
+
// artifact directory, and a caller-supplied path made that promise false
|
|
284
|
+
// — it could write a PNG anywhere the process could reach. The server
|
|
285
|
+
// allocates the path instead.
|
|
286
|
+
params: {
|
|
287
|
+
selector: OPTIONAL_SELECTOR,
|
|
288
|
+
full_page: { type: 'boolean' },
|
|
195
289
|
},
|
|
196
|
-
|
|
197
|
-
returnsImage: true,
|
|
290
|
+
annotations: { ...readOnly('Screenshot'), idempotentHint: false },
|
|
198
291
|
toDispatch: (a) => ({
|
|
199
292
|
cmd: 'screenshot',
|
|
200
293
|
args: {
|
|
201
294
|
...(a.selector ? { selector: a.selector } : {}),
|
|
202
295
|
fullPage: a.full_page,
|
|
203
|
-
...(a.path ? { path: a.path } : {}),
|
|
204
296
|
},
|
|
205
297
|
}),
|
|
206
298
|
},
|
|
207
299
|
{
|
|
208
|
-
name: '
|
|
209
|
-
description: '
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
300
|
+
name: 'browser_status',
|
|
301
|
+
description: 'Report whether a browser is up and which URL is active.',
|
|
302
|
+
params: {},
|
|
303
|
+
annotations: readOnly('Status', false),
|
|
304
|
+
toDispatch: () => ({ cmd: 'status', args: {} }),
|
|
305
|
+
},
|
|
306
|
+
// ---- pages, evidence, and artifacts -------------------------------------
|
|
307
|
+
{
|
|
308
|
+
name: 'browser_page',
|
|
309
|
+
description: 'List, open, select, or close a tab. A tab the application opens is listed but never ' +
|
|
310
|
+
'selected implicitly. Switching or closing clears refs, so snapshot again afterwards.',
|
|
311
|
+
params: {
|
|
312
|
+
action: { type: 'string', required: true, enum: ['list', 'open', 'select', 'close'] },
|
|
313
|
+
target: { type: 'string', maxLength: 128, description: 'Index or page id.' },
|
|
314
|
+
url: { type: 'string', maxLength: 2048, description: 'Used with action=open.' },
|
|
315
|
+
},
|
|
316
|
+
annotations: mutating('Tabs'),
|
|
317
|
+
toDispatch: (a) => ({
|
|
318
|
+
cmd: 'page',
|
|
319
|
+
args: {
|
|
320
|
+
action: a.action,
|
|
321
|
+
...(a.target !== undefined ? { target: a.target } : {}),
|
|
322
|
+
...(a.url !== undefined ? { url: a.url } : {}),
|
|
323
|
+
},
|
|
324
|
+
}),
|
|
325
|
+
},
|
|
326
|
+
{
|
|
327
|
+
name: 'browser_logs',
|
|
328
|
+
description: 'Console and network history for this session, captured from launch — so an error ' +
|
|
329
|
+
'thrown during the first navigation is still answerable. Every result carries a ' +
|
|
330
|
+
'cursor; pass it back as `since` for only what is new. kind=error covers both ' +
|
|
331
|
+
'uncaught exceptions and console.error. Network rows are summaries: no bodies, ' +
|
|
332
|
+
'cookies or headers.',
|
|
333
|
+
params: {
|
|
334
|
+
action: { type: 'string', enum: ['list', 'wait', 'clear'] },
|
|
335
|
+
kind: {
|
|
336
|
+
type: 'string',
|
|
337
|
+
maxLength: 64,
|
|
338
|
+
description: 'Comma-separated: console, error, request, response.',
|
|
217
339
|
},
|
|
218
|
-
|
|
340
|
+
level: { type: 'string', maxLength: 32 },
|
|
341
|
+
contains: { type: 'string', maxLength: 512 },
|
|
342
|
+
since: { type: 'number', min: 0, integer: true },
|
|
343
|
+
limit: { type: 'number', min: 1, max: 500, integer: true },
|
|
344
|
+
timeout_ms: TIMEOUT,
|
|
219
345
|
},
|
|
220
|
-
|
|
221
|
-
toDispatch: (a) =>
|
|
222
|
-
|
|
223
|
-
|
|
346
|
+
annotations: readOnly('Console and network'),
|
|
347
|
+
toDispatch: (a) => ({
|
|
348
|
+
cmd: 'logs',
|
|
349
|
+
args: {
|
|
350
|
+
action: a.action ?? 'list',
|
|
351
|
+
kind: a.kind,
|
|
352
|
+
level: a.level,
|
|
353
|
+
contains: a.contains,
|
|
354
|
+
since: a.since,
|
|
355
|
+
limit: a.limit,
|
|
356
|
+
timeout: a.timeout_ms,
|
|
357
|
+
},
|
|
358
|
+
}),
|
|
224
359
|
},
|
|
225
360
|
{
|
|
226
|
-
name: '
|
|
227
|
-
description: '
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
361
|
+
name: 'browser_mock',
|
|
362
|
+
description: 'Serve a fixed response for matching requests, or block them, so an error path can be ' +
|
|
363
|
+
'driven without changing the application. Validated before installation and bounded ' +
|
|
364
|
+
'in count. Clear them when done — they outlive the call that added them.',
|
|
365
|
+
params: {
|
|
366
|
+
action: { type: 'string', enum: ['add', 'block', 'list', 'remove', 'clear'] },
|
|
367
|
+
pattern: { type: 'string', maxLength: 500, description: 'URL pattern, e.g. **/api/*.' },
|
|
368
|
+
status: { type: 'number', min: 100, max: 599, integer: true },
|
|
369
|
+
body: { type: 'string', maxLength: 64 * 1024 },
|
|
370
|
+
content_type: { type: 'string', maxLength: 128 },
|
|
371
|
+
id: { type: 'string', maxLength: 128, description: 'Used with action=remove.' },
|
|
372
|
+
},
|
|
373
|
+
annotations: mutating('Network mocks'),
|
|
374
|
+
toDispatch: (a) => ({
|
|
375
|
+
cmd: 'mock',
|
|
376
|
+
args: {
|
|
377
|
+
action: a.action ?? 'list',
|
|
378
|
+
pattern: a.pattern,
|
|
379
|
+
status: a.status,
|
|
380
|
+
body: a.body,
|
|
381
|
+
contentType: a.content_type,
|
|
382
|
+
id: a.id,
|
|
383
|
+
},
|
|
384
|
+
}),
|
|
231
385
|
},
|
|
386
|
+
{
|
|
387
|
+
name: 'browser_state',
|
|
388
|
+
description: 'Save or restore cookies and local storage, so a login is captured once instead of ' +
|
|
389
|
+
'replayed. Navigate to the site BEFORE loading: local storage only restores onto its ' +
|
|
390
|
+
'own origin, and loading onto a blank page would silently drop it. Names are bare ' +
|
|
391
|
+
'(no paths); values are never printed.',
|
|
392
|
+
params: {
|
|
393
|
+
action: { type: 'string', enum: ['save', 'load', 'list'] },
|
|
394
|
+
name: { type: 'string', maxLength: 64 },
|
|
395
|
+
session_storage: { type: 'boolean', description: 'Include sessionStorage on save.' },
|
|
396
|
+
},
|
|
397
|
+
annotations: { ...mutating('Login state'), destructiveHint: true },
|
|
398
|
+
toDispatch: (a) => ({
|
|
399
|
+
cmd: 'state',
|
|
400
|
+
args: {
|
|
401
|
+
action: a.action ?? 'list',
|
|
402
|
+
...(a.name !== undefined ? { name: a.name } : {}),
|
|
403
|
+
sessionStorage: a.session_storage,
|
|
404
|
+
},
|
|
405
|
+
}),
|
|
406
|
+
},
|
|
407
|
+
{
|
|
408
|
+
name: 'browser_trace',
|
|
409
|
+
description: 'Record actions, console, network and screenshots to a file for a failure a snapshot ' +
|
|
410
|
+
'cannot explain. One trace at a time; output lands in an owned directory. Stop with ' +
|
|
411
|
+
'zip for an archive playable at player.vibium.dev.',
|
|
412
|
+
params: {
|
|
413
|
+
action: { type: 'string', enum: ['start', 'stop', 'status'] },
|
|
414
|
+
name: { type: 'string', maxLength: 64, description: 'Bare name; used with start.' },
|
|
415
|
+
zip: { type: 'boolean', description: 'Also write an archive; used with stop.' },
|
|
416
|
+
no_screenshots: { type: 'boolean', description: 'Skip screenshots; used with start.' },
|
|
417
|
+
},
|
|
418
|
+
annotations: mutating('Tracing'),
|
|
419
|
+
toDispatch: (a) => ({
|
|
420
|
+
cmd: 'trace',
|
|
421
|
+
args: {
|
|
422
|
+
action: a.action ?? 'status',
|
|
423
|
+
...(a.name !== undefined ? { name: a.name } : {}),
|
|
424
|
+
zip: a.zip,
|
|
425
|
+
noScreenshots: a.no_screenshots,
|
|
426
|
+
},
|
|
427
|
+
}),
|
|
428
|
+
},
|
|
429
|
+
// ---- escape hatch -------------------------------------------------------
|
|
232
430
|
{
|
|
233
431
|
name: 'browser_advanced_eval',
|
|
234
|
-
description: 'Evaluate arbitrary JavaScript in the page. LAST RESORT — prefer ' +
|
|
235
|
-
'
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
properties: { js: { type: 'string' } },
|
|
239
|
-
required: ['js'],
|
|
240
|
-
},
|
|
241
|
-
mutating: true,
|
|
432
|
+
description: 'Evaluate arbitrary JavaScript in the page. LAST RESORT — prefer the targeted tools. ' +
|
|
433
|
+
'Costs tokens and bypasses auto-wait.',
|
|
434
|
+
params: { js: { type: 'string', required: true, maxLength: 32 * 1024 } },
|
|
435
|
+
annotations: { ...mutating('Evaluate JavaScript'), destructiveHint: true },
|
|
242
436
|
toDispatch: (a) => ({ cmd: 'eval', args: { js: a.js } }),
|
|
243
437
|
},
|
|
244
438
|
];
|
|
@@ -246,9 +440,30 @@ const BY_NAME = new Map(TOOLS.map((t) => [t.name, t]));
|
|
|
246
440
|
export function getTool(name) {
|
|
247
441
|
return BY_NAME.get(name);
|
|
248
442
|
}
|
|
249
|
-
/**
|
|
250
|
-
export
|
|
251
|
-
|
|
252
|
-
|
|
443
|
+
/** The advertised schema for a tool, derived from its parameter descriptor. */
|
|
444
|
+
export function inputSchemaFor(tool) {
|
|
445
|
+
return toInputSchema(tool.params);
|
|
446
|
+
}
|
|
447
|
+
/**
|
|
448
|
+
* Validate arguments against the tool's descriptor.
|
|
449
|
+
*
|
|
450
|
+
* Separate from dispatch so the server can reject before touching the session
|
|
451
|
+
* queue: an invalid call should not wait behind a slow browser action to be
|
|
452
|
+
* told it was malformed.
|
|
453
|
+
*/
|
|
454
|
+
export function validateToolArgs(tool, args) {
|
|
455
|
+
return validateArgs(tool.name, tool.params, args);
|
|
456
|
+
}
|
|
457
|
+
/** Map a tool to a dispatcher command and run it through the shared session. */
|
|
458
|
+
export async function runTool(session, tool, args) {
|
|
459
|
+
return session.run(tool.toDispatch(args));
|
|
460
|
+
}
|
|
461
|
+
/**
|
|
462
|
+
* As {@link runTool}, but keeps the post-action snapshot the session captured
|
|
463
|
+
* in the same operation. The MCP adapter renders that as the second content
|
|
464
|
+
* block; it does not compute or own it.
|
|
465
|
+
*/
|
|
466
|
+
export async function runToolDetailed(session, tool, args) {
|
|
467
|
+
return session.runDetailed(tool.toDispatch(args));
|
|
253
468
|
}
|
|
254
469
|
//# sourceMappingURL=tools.js.map
|