@andrew-chen-wang/camoufox-mcp 0.0.1
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/config.d.ts +41 -0
- package/index.d.ts +22 -0
- package/index.js +1 -0
- package/lib/browserContextFactory.js +178 -0
- package/lib/config.js +204 -0
- package/lib/connection.js +84 -0
- package/lib/context.js +361 -0
- package/lib/fileUtils.js +32 -0
- package/lib/index.js +66 -0
- package/lib/javascript.js +49 -0
- package/lib/manualPromise.js +111 -0
- package/lib/mcpHttpServer.js +90 -0
- package/lib/package.js +20 -0
- package/lib/pageSnapshot.js +44 -0
- package/lib/resources/resource.js +16 -0
- package/lib/server.js +48 -0
- package/lib/tab.js +107 -0
- package/lib/tools/common.js +68 -0
- package/lib/tools/console.js +77 -0
- package/lib/tools/devtools.js +189 -0
- package/lib/tools/dialogs.js +52 -0
- package/lib/tools/files.js +51 -0
- package/lib/tools/forms.js +67 -0
- package/lib/tools/install.js +57 -0
- package/lib/tools/keyboard.js +158 -0
- package/lib/tools/mouse.js +119 -0
- package/lib/tools/navigate.js +117 -0
- package/lib/tools/network.js +197 -0
- package/lib/tools/pdf.js +49 -0
- package/lib/tools/screenshot.js +80 -0
- package/lib/tools/snapshot.js +307 -0
- package/lib/tools/storage.js +344 -0
- package/lib/tools/tabs.js +172 -0
- package/lib/tools/testing.js +60 -0
- package/lib/tools/tool.js +18 -0
- package/lib/tools/utils.js +87 -0
- package/lib/tools/vision.js +189 -0
- package/lib/tools/wait.js +59 -0
- package/lib/tools.js +73 -0
- package/package.json +48 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Microsoft Corporation.
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
import { z } from 'zod';
|
|
17
|
+
import { defineTool } from './tool.js';
|
|
18
|
+
const handleDialog = captureSnapshot => defineTool({
|
|
19
|
+
capability: 'core',
|
|
20
|
+
schema: {
|
|
21
|
+
name: 'browser_handle_dialog',
|
|
22
|
+
title: 'Handle a dialog',
|
|
23
|
+
description: 'Handle a dialog',
|
|
24
|
+
inputSchema: z.object({
|
|
25
|
+
accept: z.boolean().describe('Whether to accept the dialog.'),
|
|
26
|
+
promptText: z.string().optional().describe('The text of the prompt in case of a prompt dialog.'),
|
|
27
|
+
}),
|
|
28
|
+
type: 'destructive',
|
|
29
|
+
},
|
|
30
|
+
handle: async (context, params) => {
|
|
31
|
+
const dialogState = context.modalStates().find(state => state.type === 'dialog');
|
|
32
|
+
if (!dialogState)
|
|
33
|
+
throw new Error('No dialog visible');
|
|
34
|
+
if (params.accept)
|
|
35
|
+
await dialogState.dialog.accept(params.promptText);
|
|
36
|
+
else
|
|
37
|
+
await dialogState.dialog.dismiss();
|
|
38
|
+
context.clearModalState(dialogState);
|
|
39
|
+
const code = [
|
|
40
|
+
`// <internal code to handle "${dialogState.dialog.type()}" dialog>`,
|
|
41
|
+
];
|
|
42
|
+
return {
|
|
43
|
+
code,
|
|
44
|
+
captureSnapshot,
|
|
45
|
+
waitForNetwork: false,
|
|
46
|
+
};
|
|
47
|
+
},
|
|
48
|
+
clearsModalState: 'dialog',
|
|
49
|
+
});
|
|
50
|
+
export default (captureSnapshot) => [
|
|
51
|
+
handleDialog(captureSnapshot),
|
|
52
|
+
];
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Microsoft Corporation.
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
import { z } from 'zod';
|
|
17
|
+
import { defineTool } from './tool.js';
|
|
18
|
+
const uploadFile = captureSnapshot => defineTool({
|
|
19
|
+
capability: 'files',
|
|
20
|
+
schema: {
|
|
21
|
+
name: 'browser_file_upload',
|
|
22
|
+
title: 'Upload files',
|
|
23
|
+
description: 'Upload one or multiple files',
|
|
24
|
+
inputSchema: z.object({
|
|
25
|
+
paths: z.array(z.string()).describe('The absolute paths to the files to upload. Can be a single file or multiple files.'),
|
|
26
|
+
}),
|
|
27
|
+
type: 'destructive',
|
|
28
|
+
},
|
|
29
|
+
handle: async (context, params) => {
|
|
30
|
+
const modalState = context.modalStates().find(state => state.type === 'fileChooser');
|
|
31
|
+
if (!modalState)
|
|
32
|
+
throw new Error('No file chooser visible');
|
|
33
|
+
const code = [
|
|
34
|
+
`// <internal code to chose files ${params.paths.join(', ')}`,
|
|
35
|
+
];
|
|
36
|
+
const action = async () => {
|
|
37
|
+
await modalState.fileChooser.setFiles(params.paths);
|
|
38
|
+
context.clearModalState(modalState);
|
|
39
|
+
};
|
|
40
|
+
return {
|
|
41
|
+
code,
|
|
42
|
+
action,
|
|
43
|
+
captureSnapshot,
|
|
44
|
+
waitForNetwork: true,
|
|
45
|
+
};
|
|
46
|
+
},
|
|
47
|
+
clearsModalState: 'fileChooser',
|
|
48
|
+
});
|
|
49
|
+
export default (captureSnapshot) => [
|
|
50
|
+
uploadFile(captureSnapshot),
|
|
51
|
+
];
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Microsoft Corporation.
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
import { z } from 'zod';
|
|
17
|
+
import * as javascript from '../javascript.js';
|
|
18
|
+
import { defineTool } from './tool.js';
|
|
19
|
+
import { generateLocator } from './utils.js';
|
|
20
|
+
const fillForm = defineTool({
|
|
21
|
+
capability: 'core',
|
|
22
|
+
schema: {
|
|
23
|
+
name: 'browser_fill_form',
|
|
24
|
+
title: 'Fill form',
|
|
25
|
+
description: 'Fill multiple form fields',
|
|
26
|
+
inputSchema: z.object({
|
|
27
|
+
fields: z.array(z.object({
|
|
28
|
+
name: z.string().describe('Human-readable field name'),
|
|
29
|
+
type: z.enum(['textbox', 'checkbox', 'radio', 'combobox', 'slider']).describe('Type of the field'),
|
|
30
|
+
ref: z.string().describe('Exact target field reference from the page snapshot'),
|
|
31
|
+
value: z.string().describe('Value to fill in the field'),
|
|
32
|
+
})),
|
|
33
|
+
}),
|
|
34
|
+
type: 'destructive',
|
|
35
|
+
},
|
|
36
|
+
handle: async (context, params) => {
|
|
37
|
+
const snapshot = context.currentTabOrDie().snapshotOrDie();
|
|
38
|
+
const code = [];
|
|
39
|
+
const steps = [];
|
|
40
|
+
for (const field of params.fields) {
|
|
41
|
+
const locator = snapshot.refLocator({ element: field.name, ref: field.ref });
|
|
42
|
+
const locatorString = await generateLocator(locator);
|
|
43
|
+
if (field.type === 'textbox' || field.type === 'slider') {
|
|
44
|
+
code.push(`await page.${locatorString}.fill(${javascript.quote(field.value)});`);
|
|
45
|
+
steps.push(() => locator.fill(field.value));
|
|
46
|
+
}
|
|
47
|
+
else if (field.type === 'checkbox' || field.type === 'radio') {
|
|
48
|
+
const shouldCheck = field.value === 'true';
|
|
49
|
+
code.push(`await page.${locatorString}.${shouldCheck ? 'check' : 'uncheck'}();`);
|
|
50
|
+
steps.push(() => shouldCheck ? locator.check() : locator.uncheck());
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
code.push(`await page.${locatorString}.selectOption({ label: ${javascript.quote(field.value)} });`);
|
|
54
|
+
steps.push(() => locator.selectOption({ label: field.value }).then(() => { }));
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
code,
|
|
59
|
+
action: () => steps.reduce((promise, step) => promise.then(step), Promise.resolve()),
|
|
60
|
+
captureSnapshot: true,
|
|
61
|
+
waitForNetwork: true,
|
|
62
|
+
};
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
export default [
|
|
66
|
+
fillForm,
|
|
67
|
+
];
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Microsoft Corporation.
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
import { fork } from 'child_process';
|
|
17
|
+
import path from 'path';
|
|
18
|
+
import { z } from 'zod';
|
|
19
|
+
import { defineTool } from './tool.js';
|
|
20
|
+
import { fileURLToPath } from 'node:url';
|
|
21
|
+
const install = defineTool({
|
|
22
|
+
capability: 'install',
|
|
23
|
+
schema: {
|
|
24
|
+
name: 'browser_install',
|
|
25
|
+
title: 'Install the browser specified in the config',
|
|
26
|
+
description: 'Install the browser specified in the config. Call this if you get an error about the browser not being installed.',
|
|
27
|
+
inputSchema: z.object({}),
|
|
28
|
+
type: 'destructive',
|
|
29
|
+
},
|
|
30
|
+
handle: async (context) => {
|
|
31
|
+
const channel = context.config.browser?.launchOptions?.channel ?? context.config.browser?.browserName ?? 'chrome';
|
|
32
|
+
const cliUrl = import.meta.resolve('camoufox-js/package.json');
|
|
33
|
+
const cliPath = path.join(fileURLToPath(cliUrl), '..', 'dist', '__main__.js');
|
|
34
|
+
const child = fork(cliPath, ['fetch'], {
|
|
35
|
+
stdio: 'pipe',
|
|
36
|
+
});
|
|
37
|
+
const output = [];
|
|
38
|
+
child.stdout?.on('data', data => output.push(data.toString()));
|
|
39
|
+
child.stderr?.on('data', data => output.push(data.toString()));
|
|
40
|
+
await new Promise((resolve, reject) => {
|
|
41
|
+
child.on('close', code => {
|
|
42
|
+
if (code === 0)
|
|
43
|
+
resolve();
|
|
44
|
+
else
|
|
45
|
+
reject(new Error(`Failed to install browser: ${output.join('')}`));
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
return {
|
|
49
|
+
code: [`// Camoufox browser assets installed for ${channel}`],
|
|
50
|
+
captureSnapshot: false,
|
|
51
|
+
waitForNetwork: false,
|
|
52
|
+
};
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
export default [
|
|
56
|
+
install,
|
|
57
|
+
];
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Microsoft Corporation.
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
import { z } from 'zod';
|
|
17
|
+
import { defineTool } from './tool.js';
|
|
18
|
+
const pressKey = captureSnapshot => defineTool({
|
|
19
|
+
capability: 'core',
|
|
20
|
+
schema: {
|
|
21
|
+
name: 'browser_press_key',
|
|
22
|
+
title: 'Press a key',
|
|
23
|
+
description: 'Press a key on the keyboard',
|
|
24
|
+
inputSchema: z.object({
|
|
25
|
+
key: z.string().describe('Name of the key to press or a character to generate, such as `ArrowLeft` or `a`'),
|
|
26
|
+
}),
|
|
27
|
+
type: 'destructive',
|
|
28
|
+
},
|
|
29
|
+
handle: async (context, params) => {
|
|
30
|
+
const tab = context.currentTabOrDie();
|
|
31
|
+
const code = [
|
|
32
|
+
`// Press ${params.key}`,
|
|
33
|
+
`await page.keyboard.press('${params.key}');`,
|
|
34
|
+
];
|
|
35
|
+
const action = () => tab.page.keyboard.press(params.key);
|
|
36
|
+
return {
|
|
37
|
+
code,
|
|
38
|
+
action,
|
|
39
|
+
captureSnapshot,
|
|
40
|
+
waitForNetwork: true
|
|
41
|
+
};
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
const keyDown = captureSnapshot => defineTool({
|
|
45
|
+
capability: 'core',
|
|
46
|
+
schema: {
|
|
47
|
+
name: 'browser_key_down',
|
|
48
|
+
title: 'Key down',
|
|
49
|
+
description: 'Press and hold a key on the keyboard',
|
|
50
|
+
inputSchema: z.object({
|
|
51
|
+
key: z.string().describe('Name of the key to press down'),
|
|
52
|
+
}),
|
|
53
|
+
type: 'destructive',
|
|
54
|
+
},
|
|
55
|
+
handle: async (context, params) => {
|
|
56
|
+
const tab = context.currentTabOrDie();
|
|
57
|
+
return {
|
|
58
|
+
code: [
|
|
59
|
+
`// Press and hold ${params.key}`,
|
|
60
|
+
`await page.keyboard.down('${params.key}');`,
|
|
61
|
+
],
|
|
62
|
+
action: () => tab.page.keyboard.down(params.key),
|
|
63
|
+
captureSnapshot,
|
|
64
|
+
waitForNetwork: false,
|
|
65
|
+
};
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
const keyUp = captureSnapshot => defineTool({
|
|
69
|
+
capability: 'core',
|
|
70
|
+
schema: {
|
|
71
|
+
name: 'browser_key_up',
|
|
72
|
+
title: 'Key up',
|
|
73
|
+
description: 'Release a previously held key on the keyboard',
|
|
74
|
+
inputSchema: z.object({
|
|
75
|
+
key: z.string().describe('Name of the key to release'),
|
|
76
|
+
}),
|
|
77
|
+
type: 'destructive',
|
|
78
|
+
},
|
|
79
|
+
handle: async (context, params) => {
|
|
80
|
+
const tab = context.currentTabOrDie();
|
|
81
|
+
return {
|
|
82
|
+
code: [
|
|
83
|
+
`// Release ${params.key}`,
|
|
84
|
+
`await page.keyboard.up('${params.key}');`,
|
|
85
|
+
],
|
|
86
|
+
action: () => tab.page.keyboard.up(params.key),
|
|
87
|
+
captureSnapshot,
|
|
88
|
+
waitForNetwork: false,
|
|
89
|
+
};
|
|
90
|
+
},
|
|
91
|
+
});
|
|
92
|
+
const keyDownCompat = captureSnapshot => defineTool({
|
|
93
|
+
capability: 'core',
|
|
94
|
+
schema: {
|
|
95
|
+
name: 'browser_keydown',
|
|
96
|
+
title: 'Key down',
|
|
97
|
+
description: 'Press and hold a key on the keyboard',
|
|
98
|
+
inputSchema: z.object({
|
|
99
|
+
key: z.string().describe('Name of the key to press down'),
|
|
100
|
+
}),
|
|
101
|
+
type: 'destructive',
|
|
102
|
+
},
|
|
103
|
+
handle: async (context, params) => keyDown(captureSnapshot).handle(context, params),
|
|
104
|
+
});
|
|
105
|
+
const keyUpCompat = captureSnapshot => defineTool({
|
|
106
|
+
capability: 'core',
|
|
107
|
+
schema: {
|
|
108
|
+
name: 'browser_keyup',
|
|
109
|
+
title: 'Key up',
|
|
110
|
+
description: 'Release a previously held key on the keyboard',
|
|
111
|
+
inputSchema: z.object({
|
|
112
|
+
key: z.string().describe('Name of the key to release'),
|
|
113
|
+
}),
|
|
114
|
+
type: 'destructive',
|
|
115
|
+
},
|
|
116
|
+
handle: async (context, params) => keyUp(captureSnapshot).handle(context, params),
|
|
117
|
+
});
|
|
118
|
+
const pressSequentially = captureSnapshot => defineTool({
|
|
119
|
+
capability: 'core',
|
|
120
|
+
schema: {
|
|
121
|
+
name: 'browser_press_sequentially',
|
|
122
|
+
title: 'Type text sequentially',
|
|
123
|
+
description: 'Type text into the currently focused element one character at a time',
|
|
124
|
+
inputSchema: z.object({
|
|
125
|
+
text: z.string(),
|
|
126
|
+
submit: z.boolean().optional(),
|
|
127
|
+
}),
|
|
128
|
+
type: 'destructive',
|
|
129
|
+
},
|
|
130
|
+
handle: async (context, params) => {
|
|
131
|
+
const tab = context.currentTabOrDie();
|
|
132
|
+
const code = [
|
|
133
|
+
`// Press "${params.text}" sequentially into the focused element`,
|
|
134
|
+
`await page.keyboard.type(${JSON.stringify(params.text)});`,
|
|
135
|
+
];
|
|
136
|
+
const action = async () => {
|
|
137
|
+
await tab.page.keyboard.type(params.text);
|
|
138
|
+
if (params.submit)
|
|
139
|
+
await tab.page.keyboard.press('Enter');
|
|
140
|
+
};
|
|
141
|
+
if (params.submit)
|
|
142
|
+
code.push(`await page.keyboard.press('Enter');`);
|
|
143
|
+
return {
|
|
144
|
+
code,
|
|
145
|
+
action,
|
|
146
|
+
captureSnapshot,
|
|
147
|
+
waitForNetwork: true,
|
|
148
|
+
};
|
|
149
|
+
},
|
|
150
|
+
});
|
|
151
|
+
export default (captureSnapshot) => [
|
|
152
|
+
pressKey(captureSnapshot),
|
|
153
|
+
keyDown(captureSnapshot),
|
|
154
|
+
keyDownCompat(captureSnapshot),
|
|
155
|
+
keyUp(captureSnapshot),
|
|
156
|
+
keyUpCompat(captureSnapshot),
|
|
157
|
+
pressSequentially(captureSnapshot),
|
|
158
|
+
];
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Microsoft Corporation.
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
import { z } from 'zod';
|
|
17
|
+
import { defineTool } from './tool.js';
|
|
18
|
+
const moveMouse = captureSnapshot => defineTool({
|
|
19
|
+
capability: 'core',
|
|
20
|
+
schema: {
|
|
21
|
+
name: 'browser_mouse_move',
|
|
22
|
+
title: 'Move mouse',
|
|
23
|
+
description: 'Move the mouse to page coordinates',
|
|
24
|
+
inputSchema: z.object({
|
|
25
|
+
x: z.number(),
|
|
26
|
+
y: z.number(),
|
|
27
|
+
steps: z.number().optional(),
|
|
28
|
+
}),
|
|
29
|
+
type: 'destructive',
|
|
30
|
+
},
|
|
31
|
+
handle: async (context, params) => {
|
|
32
|
+
const page = context.currentTabOrDie().page;
|
|
33
|
+
return {
|
|
34
|
+
code: [
|
|
35
|
+
`// Move mouse to (${params.x}, ${params.y})`,
|
|
36
|
+
`await page.mouse.move(${params.x}, ${params.y}${params.steps !== undefined ? `, { steps: ${params.steps} }` : ''});`,
|
|
37
|
+
],
|
|
38
|
+
action: () => page.mouse.move(params.x, params.y, params.steps !== undefined ? { steps: params.steps } : undefined),
|
|
39
|
+
captureSnapshot,
|
|
40
|
+
waitForNetwork: false,
|
|
41
|
+
};
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
const moveMouseCompat = captureSnapshot => defineTool({
|
|
45
|
+
capability: 'core',
|
|
46
|
+
schema: {
|
|
47
|
+
name: 'browser_mouse_move_xy',
|
|
48
|
+
title: 'Move mouse',
|
|
49
|
+
description: 'Move the mouse to page coordinates',
|
|
50
|
+
inputSchema: z.object({
|
|
51
|
+
x: z.number(),
|
|
52
|
+
y: z.number(),
|
|
53
|
+
}),
|
|
54
|
+
type: 'destructive',
|
|
55
|
+
},
|
|
56
|
+
handle: async (context, params) => moveMouse(captureSnapshot).handle(context, params),
|
|
57
|
+
});
|
|
58
|
+
function defineMouseButtonTool(name, title, actionName) {
|
|
59
|
+
return defineTool({
|
|
60
|
+
capability: 'core',
|
|
61
|
+
schema: {
|
|
62
|
+
name,
|
|
63
|
+
title,
|
|
64
|
+
description: `${title} at the current mouse location`,
|
|
65
|
+
inputSchema: z.object({
|
|
66
|
+
button: z.enum(['left', 'middle', 'right']).optional(),
|
|
67
|
+
clickCount: z.number().optional(),
|
|
68
|
+
}),
|
|
69
|
+
type: 'destructive',
|
|
70
|
+
},
|
|
71
|
+
handle: async (context, params) => {
|
|
72
|
+
const page = context.currentTabOrDie().page;
|
|
73
|
+
const options = { button: params.button, clickCount: params.clickCount };
|
|
74
|
+
return {
|
|
75
|
+
code: [
|
|
76
|
+
`// Mouse ${actionName}`,
|
|
77
|
+
`await page.mouse.${actionName}(${Object.values(options).some(v => v !== undefined) ? `{ button: '${params.button ?? 'left'}'${params.clickCount !== undefined ? `, clickCount: ${params.clickCount}` : ''} }` : ''});`,
|
|
78
|
+
],
|
|
79
|
+
action: () => page.mouse[actionName](options),
|
|
80
|
+
captureSnapshot: true,
|
|
81
|
+
waitForNetwork: false,
|
|
82
|
+
};
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
const mouseDown = defineMouseButtonTool('browser_mouse_down', 'Mouse down', 'down');
|
|
87
|
+
const mouseUp = defineMouseButtonTool('browser_mouse_up', 'Mouse up', 'up');
|
|
88
|
+
const mouseWheel = captureSnapshot => defineTool({
|
|
89
|
+
capability: 'core',
|
|
90
|
+
schema: {
|
|
91
|
+
name: 'browser_mouse_wheel',
|
|
92
|
+
title: 'Mouse wheel',
|
|
93
|
+
description: 'Scroll using the mouse wheel',
|
|
94
|
+
inputSchema: z.object({
|
|
95
|
+
deltaX: z.number(),
|
|
96
|
+
deltaY: z.number(),
|
|
97
|
+
}),
|
|
98
|
+
type: 'destructive',
|
|
99
|
+
},
|
|
100
|
+
handle: async (context, params) => {
|
|
101
|
+
const page = context.currentTabOrDie().page;
|
|
102
|
+
return {
|
|
103
|
+
code: [
|
|
104
|
+
`// Mouse wheel by (${params.deltaX}, ${params.deltaY})`,
|
|
105
|
+
`await page.mouse.wheel(${params.deltaX}, ${params.deltaY});`,
|
|
106
|
+
],
|
|
107
|
+
action: () => page.mouse.wheel(params.deltaX, params.deltaY),
|
|
108
|
+
captureSnapshot,
|
|
109
|
+
waitForNetwork: false,
|
|
110
|
+
};
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
export default (captureSnapshot) => [
|
|
114
|
+
moveMouse(captureSnapshot),
|
|
115
|
+
moveMouseCompat(captureSnapshot),
|
|
116
|
+
mouseDown,
|
|
117
|
+
mouseUp,
|
|
118
|
+
mouseWheel(captureSnapshot),
|
|
119
|
+
];
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Microsoft Corporation.
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
import { z } from 'zod';
|
|
17
|
+
import { defineTool } from './tool.js';
|
|
18
|
+
const navigate = captureSnapshot => defineTool({
|
|
19
|
+
capability: 'core',
|
|
20
|
+
schema: {
|
|
21
|
+
name: 'browser_navigate',
|
|
22
|
+
title: 'Navigate to a URL',
|
|
23
|
+
description: 'Navigate to a URL',
|
|
24
|
+
inputSchema: z.object({
|
|
25
|
+
url: z.string().describe('The URL to navigate to'),
|
|
26
|
+
}),
|
|
27
|
+
type: 'destructive',
|
|
28
|
+
},
|
|
29
|
+
handle: async (context, params) => {
|
|
30
|
+
const tab = await context.ensureTab();
|
|
31
|
+
await tab.navigate(params.url);
|
|
32
|
+
const code = [
|
|
33
|
+
`// Navigate to ${params.url}`,
|
|
34
|
+
`await page.goto('${params.url}');`,
|
|
35
|
+
];
|
|
36
|
+
return {
|
|
37
|
+
code,
|
|
38
|
+
captureSnapshot,
|
|
39
|
+
waitForNetwork: false,
|
|
40
|
+
};
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
const goBack = captureSnapshot => defineTool({
|
|
44
|
+
capability: 'history',
|
|
45
|
+
schema: {
|
|
46
|
+
name: 'browser_navigate_back',
|
|
47
|
+
title: 'Go back',
|
|
48
|
+
description: 'Go back to the previous page',
|
|
49
|
+
inputSchema: z.object({}),
|
|
50
|
+
type: 'readOnly',
|
|
51
|
+
},
|
|
52
|
+
handle: async (context) => {
|
|
53
|
+
const tab = await context.ensureTab();
|
|
54
|
+
await tab.page.goBack();
|
|
55
|
+
const code = [
|
|
56
|
+
`// Navigate back`,
|
|
57
|
+
`await page.goBack();`,
|
|
58
|
+
];
|
|
59
|
+
return {
|
|
60
|
+
code,
|
|
61
|
+
captureSnapshot,
|
|
62
|
+
waitForNetwork: false,
|
|
63
|
+
};
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
const goForward = captureSnapshot => defineTool({
|
|
67
|
+
capability: 'history',
|
|
68
|
+
schema: {
|
|
69
|
+
name: 'browser_navigate_forward',
|
|
70
|
+
title: 'Go forward',
|
|
71
|
+
description: 'Go forward to the next page',
|
|
72
|
+
inputSchema: z.object({}),
|
|
73
|
+
type: 'readOnly',
|
|
74
|
+
},
|
|
75
|
+
handle: async (context) => {
|
|
76
|
+
const tab = context.currentTabOrDie();
|
|
77
|
+
await tab.page.goForward();
|
|
78
|
+
const code = [
|
|
79
|
+
`// Navigate forward`,
|
|
80
|
+
`await page.goForward();`,
|
|
81
|
+
];
|
|
82
|
+
return {
|
|
83
|
+
code,
|
|
84
|
+
captureSnapshot,
|
|
85
|
+
waitForNetwork: false,
|
|
86
|
+
};
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
const reload = captureSnapshot => defineTool({
|
|
90
|
+
capability: 'history',
|
|
91
|
+
schema: {
|
|
92
|
+
name: 'browser_reload',
|
|
93
|
+
title: 'Reload page',
|
|
94
|
+
description: 'Reload the current page',
|
|
95
|
+
inputSchema: z.object({}),
|
|
96
|
+
type: 'readOnly',
|
|
97
|
+
},
|
|
98
|
+
handle: async (context) => {
|
|
99
|
+
const tab = context.currentTabOrDie();
|
|
100
|
+
const code = [
|
|
101
|
+
`// Reload the page`,
|
|
102
|
+
`await page.reload();`,
|
|
103
|
+
];
|
|
104
|
+
return {
|
|
105
|
+
code,
|
|
106
|
+
action: () => tab.page.reload({ waitUntil: 'domcontentloaded' }).then(() => { }),
|
|
107
|
+
captureSnapshot,
|
|
108
|
+
waitForNetwork: true,
|
|
109
|
+
};
|
|
110
|
+
},
|
|
111
|
+
});
|
|
112
|
+
export default (captureSnapshot) => [
|
|
113
|
+
navigate(captureSnapshot),
|
|
114
|
+
goBack(captureSnapshot),
|
|
115
|
+
goForward(captureSnapshot),
|
|
116
|
+
reload(captureSnapshot),
|
|
117
|
+
];
|