@dyyz1993/agent-browser 0.9.2
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/LICENSE +202 -0
- package/README.md +907 -0
- package/bin/agent-browser-darwin-arm64 +0 -0
- package/bin/agent-browser.js +120 -0
- package/dist/__tests__/e2e/utils/test-helpers.d.ts +5 -0
- package/dist/__tests__/e2e/utils/test-helpers.d.ts.map +1 -0
- package/dist/__tests__/e2e/utils/test-helpers.js +22 -0
- package/dist/__tests__/e2e/utils/test-helpers.js.map +1 -0
- package/dist/__tests__/test-iframe.d.ts +2 -0
- package/dist/__tests__/test-iframe.d.ts.map +1 -0
- package/dist/__tests__/test-iframe.js +52 -0
- package/dist/__tests__/test-iframe.js.map +1 -0
- package/dist/__tests__/utils/parseCli.d.ts +20 -0
- package/dist/__tests__/utils/parseCli.d.ts.map +1 -0
- package/dist/__tests__/utils/parseCli.js +1086 -0
- package/dist/__tests__/utils/parseCli.js.map +1 -0
- package/dist/actions.d.ts +50 -0
- package/dist/actions.d.ts.map +1 -0
- package/dist/actions.js +2164 -0
- package/dist/actions.js.map +1 -0
- package/dist/browser.d.ts +556 -0
- package/dist/browser.d.ts.map +1 -0
- package/dist/browser.js +2599 -0
- package/dist/browser.js.map +1 -0
- package/dist/cli/commands.d.ts +8 -0
- package/dist/cli/commands.d.ts.map +1 -0
- package/dist/cli/commands.js +1038 -0
- package/dist/cli/commands.js.map +1 -0
- package/dist/cli/connection.d.ts +50 -0
- package/dist/cli/connection.d.ts.map +1 -0
- package/dist/cli/connection.js +595 -0
- package/dist/cli/connection.js.map +1 -0
- package/dist/cli/flags.d.ts +36 -0
- package/dist/cli/flags.d.ts.map +1 -0
- package/dist/cli/flags.js +206 -0
- package/dist/cli/flags.js.map +1 -0
- package/dist/cli/help.d.ts +4 -0
- package/dist/cli/help.d.ts.map +1 -0
- package/dist/cli/help.js +1024 -0
- package/dist/cli/help.js.map +1 -0
- package/dist/cli/output.d.ts +14 -0
- package/dist/cli/output.d.ts.map +1 -0
- package/dist/cli/output.js +456 -0
- package/dist/cli/output.js.map +1 -0
- package/dist/cli-new.d.ts +3 -0
- package/dist/cli-new.d.ts.map +1 -0
- package/dist/cli-new.js +308 -0
- package/dist/cli-new.js.map +1 -0
- package/dist/cli-old.d.ts +3 -0
- package/dist/cli-old.d.ts.map +1 -0
- package/dist/cli-old.js +1101 -0
- package/dist/cli-old.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +403 -0
- package/dist/cli.js.map +1 -0
- package/dist/content-detection.d.ts +18 -0
- package/dist/content-detection.d.ts.map +1 -0
- package/dist/content-detection.js +68 -0
- package/dist/content-detection.js.map +1 -0
- package/dist/daemon.d.ts +55 -0
- package/dist/daemon.d.ts.map +1 -0
- package/dist/daemon.js +426 -0
- package/dist/daemon.js.map +1 -0
- package/dist/diff.d.ts +42 -0
- package/dist/diff.d.ts.map +1 -0
- package/dist/diff.js +166 -0
- package/dist/diff.js.map +1 -0
- package/dist/human-mouse.d.ts +31 -0
- package/dist/human-mouse.d.ts.map +1 -0
- package/dist/human-mouse.js +184 -0
- package/dist/human-mouse.js.map +1 -0
- package/dist/ios-actions.d.ts +11 -0
- package/dist/ios-actions.d.ts.map +1 -0
- package/dist/ios-actions.js +228 -0
- package/dist/ios-actions.js.map +1 -0
- package/dist/ios-manager.d.ts +266 -0
- package/dist/ios-manager.d.ts.map +1 -0
- package/dist/ios-manager.js +1076 -0
- package/dist/ios-manager.js.map +1 -0
- package/dist/message-bridge.d.ts +10 -0
- package/dist/message-bridge.d.ts.map +1 -0
- package/dist/message-bridge.js +60 -0
- package/dist/message-bridge.js.map +1 -0
- package/dist/protocol.d.ts +26 -0
- package/dist/protocol.d.ts.map +1 -0
- package/dist/protocol.js +912 -0
- package/dist/protocol.js.map +1 -0
- package/dist/recorder/binding.d.ts +24 -0
- package/dist/recorder/binding.d.ts.map +1 -0
- package/dist/recorder/binding.js +215 -0
- package/dist/recorder/binding.js.map +1 -0
- package/dist/recorder/index.d.ts +4 -0
- package/dist/recorder/index.d.ts.map +1 -0
- package/dist/recorder/index.js +4 -0
- package/dist/recorder/index.js.map +1 -0
- package/dist/recorder/inject.js +1913 -0
- package/dist/recorder/recorder.d.ts +19 -0
- package/dist/recorder/recorder.d.ts.map +1 -0
- package/dist/recorder/recorder.js +101 -0
- package/dist/recorder/recorder.js.map +1 -0
- package/dist/recorder/store.d.ts +22 -0
- package/dist/recorder/store.d.ts.map +1 -0
- package/dist/recorder/store.js +150 -0
- package/dist/recorder/store.js.map +1 -0
- package/dist/recorder/types.d.ts +73 -0
- package/dist/recorder/types.d.ts.map +1 -0
- package/dist/recorder/types.js +5 -0
- package/dist/recorder/types.js.map +1 -0
- package/dist/snapshot.d.ts +81 -0
- package/dist/snapshot.d.ts.map +1 -0
- package/dist/snapshot.js +1348 -0
- package/dist/snapshot.js.map +1 -0
- package/dist/stream-server-standalone.d.ts +38 -0
- package/dist/stream-server-standalone.d.ts.map +1 -0
- package/dist/stream-server-standalone.js +494 -0
- package/dist/stream-server-standalone.js.map +1 -0
- package/dist/stream-server.d.ts +214 -0
- package/dist/stream-server.d.ts.map +1 -0
- package/dist/stream-server.js +811 -0
- package/dist/stream-server.js.map +1 -0
- package/dist/types.d.ts +914 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +4 -0
- package/dist/types.js.map +1 -0
- package/dist/viewer-html.d.ts +2 -0
- package/dist/viewer-html.d.ts.map +1 -0
- package/dist/viewer-html.js +185 -0
- package/dist/viewer-html.js.map +1 -0
- package/dist/viewer-script.d.ts +47 -0
- package/dist/viewer-script.d.ts.map +1 -0
- package/dist/viewer-script.js +586 -0
- package/dist/viewer-script.js.map +1 -0
- package/package.json +86 -0
- package/scripts/build-all-platforms.sh +68 -0
- package/scripts/check-version-sync.js +39 -0
- package/scripts/check_goods_container.js +35 -0
- package/scripts/check_page_content.js +36 -0
- package/scripts/click_applause_rate.js +30 -0
- package/scripts/copy-native.js +36 -0
- package/scripts/copy-recorder.js +21 -0
- package/scripts/e2e-test-recorder.ts +584 -0
- package/scripts/explore_jd_page.js +31 -0
- package/scripts/extract_all_jd_data.js +80 -0
- package/scripts/extract_jd_product_detail.js +62 -0
- package/scripts/extract_jd_products_correct_links.js +78 -0
- package/scripts/extract_jd_products_final.js +80 -0
- package/scripts/extract_jd_reviews.js +48 -0
- package/scripts/extract_jd_seafood_final.js +78 -0
- package/scripts/extract_multiple_products.js +77 -0
- package/scripts/extract_products_no_scroll.js +68 -0
- package/scripts/extract_products_simple.js +68 -0
- package/scripts/find_applause_rate.js +26 -0
- package/scripts/find_jd_links.js +28 -0
- package/scripts/find_main_content.js +20 -0
- package/scripts/find_product_cards.js +38 -0
- package/scripts/find_root_content.js +26 -0
- package/scripts/find_unique_products.js +55 -0
- package/scripts/get_jd_product_detail.js +16 -0
- package/scripts/get_jd_products.js +23 -0
- package/scripts/get_jd_seafood_products.js +44 -0
- package/scripts/get_product_details_from_images.js +54 -0
- package/scripts/postinstall.js +235 -0
- package/scripts/scroll_and_get_products.js +47 -0
- package/scripts/scroll_deep_and_find.js +45 -0
- package/scripts/sync-version.js +69 -0
- package/scripts/verify-baidu-enter.ts +116 -0
- package/skills/agent-browser/SKILL.md +310 -0
- package/skills/agent-browser/references/authentication.md +198 -0
- package/skills/agent-browser/references/commands.md +471 -0
- package/skills/agent-browser/references/data-extraction.md +377 -0
- package/skills/agent-browser/references/proxy-support.md +188 -0
- package/skills/agent-browser/references/session-management.md +197 -0
- package/skills/agent-browser/references/snapshot-refs.md +379 -0
- package/skills/agent-browser/references/video-recording.md +173 -0
- package/skills/agent-browser/templates/api-interception.sh +53 -0
- package/skills/agent-browser/templates/authenticated-session.sh +97 -0
- package/skills/agent-browser/templates/capture-workflow.sh +69 -0
- package/skills/agent-browser/templates/data-extraction.sh +210 -0
- package/skills/agent-browser/templates/form-automation.sh +62 -0
- package/skills/skill-creator/LICENSE.txt +202 -0
- package/skills/skill-creator/SKILL.md +356 -0
- package/skills/skill-creator/references/output-patterns.md +82 -0
- package/skills/skill-creator/references/workflows.md +28 -0
- package/skills/skill-creator/scripts/init_skill.py +303 -0
- package/skills/skill-creator/scripts/package_skill.py +113 -0
- package/skills/skill-creator/scripts/quick_validate.py +95 -0
package/dist/cli-old.js
ADDED
|
@@ -0,0 +1,1101 @@
|
|
|
1
|
+
#!/usr/bin/env npx tsx
|
|
2
|
+
import * as net from 'net';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
import * as url from 'url';
|
|
5
|
+
import { spawn } from 'child_process';
|
|
6
|
+
import { isDaemonRunning, getConnectionInfo } from './daemon.js';
|
|
7
|
+
function genId() {
|
|
8
|
+
return `n${Date.now() % 1000000}`;
|
|
9
|
+
}
|
|
10
|
+
class CliError extends Error {
|
|
11
|
+
usage;
|
|
12
|
+
constructor(message, usage) {
|
|
13
|
+
super(message);
|
|
14
|
+
this.usage = usage;
|
|
15
|
+
this.name = 'CliError';
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function error(message, usage) {
|
|
19
|
+
throw new CliError(message, usage);
|
|
20
|
+
}
|
|
21
|
+
function parseInFrame(args) {
|
|
22
|
+
let inFrame;
|
|
23
|
+
const remaining = [];
|
|
24
|
+
for (let i = 0; i < args.length; i++) {
|
|
25
|
+
if (args[i] === '--in-frame' || args[i] === '-f') {
|
|
26
|
+
inFrame = args[i + 1];
|
|
27
|
+
i++;
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
remaining.push(args[i]);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return { inFrame, remaining };
|
|
34
|
+
}
|
|
35
|
+
function parseCliArgs(args) {
|
|
36
|
+
if (args.length === 0) {
|
|
37
|
+
error('No command provided', 'agent-browser <command> [args...]');
|
|
38
|
+
}
|
|
39
|
+
const cmd = args[0];
|
|
40
|
+
const rest = args.slice(1);
|
|
41
|
+
const id = genId();
|
|
42
|
+
switch (cmd) {
|
|
43
|
+
case 'open':
|
|
44
|
+
case 'goto':
|
|
45
|
+
case 'navigate': {
|
|
46
|
+
const url = rest[0];
|
|
47
|
+
if (!url)
|
|
48
|
+
error('Missing URL', 'agent-browser open <url>');
|
|
49
|
+
const urlLower = url.toLowerCase();
|
|
50
|
+
const formattedUrl = urlLower.startsWith('http://') ||
|
|
51
|
+
urlLower.startsWith('https://') ||
|
|
52
|
+
urlLower.startsWith('about:') ||
|
|
53
|
+
urlLower.startsWith('data:') ||
|
|
54
|
+
urlLower.startsWith('file:')
|
|
55
|
+
? url
|
|
56
|
+
: `https://${url}`;
|
|
57
|
+
return { id, action: 'navigate', url: formattedUrl };
|
|
58
|
+
}
|
|
59
|
+
case 'back':
|
|
60
|
+
return { id, action: 'back' };
|
|
61
|
+
case 'forward':
|
|
62
|
+
return { id, action: 'forward' };
|
|
63
|
+
case 'reload':
|
|
64
|
+
return { id, action: 'reload' };
|
|
65
|
+
case 'click': {
|
|
66
|
+
const { inFrame, remaining } = parseInFrame(rest);
|
|
67
|
+
const selector = remaining[0];
|
|
68
|
+
if (!selector)
|
|
69
|
+
error('Missing selector', 'agent-browser click <selector> [--in-frame <path>]');
|
|
70
|
+
return { id, action: 'click', selector, inFrame };
|
|
71
|
+
}
|
|
72
|
+
case 'dblclick': {
|
|
73
|
+
const { inFrame, remaining } = parseInFrame(rest);
|
|
74
|
+
const selector = remaining[0];
|
|
75
|
+
if (!selector)
|
|
76
|
+
error('Missing selector', 'agent-browser dblclick <selector> [--in-frame <path>]');
|
|
77
|
+
return { id, action: 'dblclick', selector, inFrame };
|
|
78
|
+
}
|
|
79
|
+
case 'fill': {
|
|
80
|
+
const { inFrame, remaining } = parseInFrame(rest);
|
|
81
|
+
const selector = remaining[0];
|
|
82
|
+
const value = remaining.slice(1).join(' ');
|
|
83
|
+
if (!selector || !value)
|
|
84
|
+
error('Missing selector or value', 'agent-browser fill <selector> <text> [--in-frame <path>]');
|
|
85
|
+
return { id, action: 'fill', selector, value, inFrame };
|
|
86
|
+
}
|
|
87
|
+
case 'type': {
|
|
88
|
+
const { inFrame, remaining } = parseInFrame(rest);
|
|
89
|
+
const selector = remaining[0];
|
|
90
|
+
const text = remaining.slice(1).join(' ');
|
|
91
|
+
if (!selector || !text)
|
|
92
|
+
error('Missing selector or text', 'agent-browser type <selector> <text> [--in-frame <path>]');
|
|
93
|
+
return { id, action: 'type', selector, text, inFrame };
|
|
94
|
+
}
|
|
95
|
+
case 'hover': {
|
|
96
|
+
const { inFrame, remaining } = parseInFrame(rest);
|
|
97
|
+
const selector = remaining[0];
|
|
98
|
+
if (!selector)
|
|
99
|
+
error('Missing selector', 'agent-browser hover <selector> [--in-frame <path>]');
|
|
100
|
+
return { id, action: 'hover', selector, inFrame };
|
|
101
|
+
}
|
|
102
|
+
case 'focus': {
|
|
103
|
+
const { inFrame, remaining } = parseInFrame(rest);
|
|
104
|
+
const selector = remaining[0];
|
|
105
|
+
if (!selector)
|
|
106
|
+
error('Missing selector', 'agent-browser focus <selector> [--in-frame <path>]');
|
|
107
|
+
return { id, action: 'focus', selector, inFrame };
|
|
108
|
+
}
|
|
109
|
+
case 'check': {
|
|
110
|
+
const { inFrame, remaining } = parseInFrame(rest);
|
|
111
|
+
const selector = remaining[0];
|
|
112
|
+
if (!selector)
|
|
113
|
+
error('Missing selector', 'agent-browser check <selector> [--in-frame <path>]');
|
|
114
|
+
return { id, action: 'check', selector, inFrame };
|
|
115
|
+
}
|
|
116
|
+
case 'uncheck': {
|
|
117
|
+
const { inFrame, remaining } = parseInFrame(rest);
|
|
118
|
+
const selector = remaining[0];
|
|
119
|
+
if (!selector)
|
|
120
|
+
error('Missing selector', 'agent-browser uncheck <selector> [--in-frame <path>]');
|
|
121
|
+
return { id, action: 'uncheck', selector, inFrame };
|
|
122
|
+
}
|
|
123
|
+
case 'select': {
|
|
124
|
+
const { inFrame, remaining } = parseInFrame(rest);
|
|
125
|
+
const selector = remaining[0];
|
|
126
|
+
const values = remaining.slice(1);
|
|
127
|
+
if (!selector || values.length === 0)
|
|
128
|
+
error('Missing selector or values', 'agent-browser select <selector> <value...> [--in-frame <path>]');
|
|
129
|
+
return { id, action: 'select', selector, values: values.length === 1 ? values[0] : values, inFrame };
|
|
130
|
+
}
|
|
131
|
+
case 'drag': {
|
|
132
|
+
const source = rest[0];
|
|
133
|
+
const target = rest[1];
|
|
134
|
+
if (!source || !target)
|
|
135
|
+
error('Missing source or target', 'agent-browser drag <source> <target>');
|
|
136
|
+
return { id, action: 'drag', source, target };
|
|
137
|
+
}
|
|
138
|
+
case 'upload': {
|
|
139
|
+
const selector = rest[0];
|
|
140
|
+
const files = rest.slice(1);
|
|
141
|
+
if (!selector || files.length === 0)
|
|
142
|
+
error('Missing selector or files', 'agent-browser upload <selector> <files...>');
|
|
143
|
+
return { id, action: 'upload', selector, files };
|
|
144
|
+
}
|
|
145
|
+
case 'download': {
|
|
146
|
+
const selector = rest[0];
|
|
147
|
+
const path = rest[1];
|
|
148
|
+
if (!selector || !path)
|
|
149
|
+
error('Missing selector or path', 'agent-browser download <selector> <path>');
|
|
150
|
+
return { id, action: 'download', selector, path };
|
|
151
|
+
}
|
|
152
|
+
case 'press':
|
|
153
|
+
case 'key': {
|
|
154
|
+
const key = rest[0];
|
|
155
|
+
if (!key)
|
|
156
|
+
error('Missing key', 'agent-browser press <key>');
|
|
157
|
+
return { id, action: 'press', key };
|
|
158
|
+
}
|
|
159
|
+
case 'keydown': {
|
|
160
|
+
const key = rest[0];
|
|
161
|
+
if (!key)
|
|
162
|
+
error('Missing key', 'agent-browser keydown <key>');
|
|
163
|
+
return { id, action: 'keydown', key };
|
|
164
|
+
}
|
|
165
|
+
case 'keyup': {
|
|
166
|
+
const key = rest[0];
|
|
167
|
+
if (!key)
|
|
168
|
+
error('Missing key', 'agent-browser keyup <key>');
|
|
169
|
+
return { id, action: 'keyup', key };
|
|
170
|
+
}
|
|
171
|
+
case 'scroll': {
|
|
172
|
+
const direction = rest[0] || 'down';
|
|
173
|
+
const amount = rest[1] ? parseInt(rest[1], 10) : 300;
|
|
174
|
+
return { id, action: 'scroll', direction, amount };
|
|
175
|
+
}
|
|
176
|
+
case 'scrollintoview':
|
|
177
|
+
case 'scrollinto': {
|
|
178
|
+
const selector = rest[0];
|
|
179
|
+
if (!selector)
|
|
180
|
+
error('Missing selector', 'agent-browser scrollintoview <selector>');
|
|
181
|
+
return { id, action: 'scrollintoview', selector };
|
|
182
|
+
}
|
|
183
|
+
case 'wait': {
|
|
184
|
+
if (rest.includes('--url') || rest.includes('-u')) {
|
|
185
|
+
const urlIdx = rest.includes('--url') ? rest.indexOf('--url') : rest.indexOf('-u');
|
|
186
|
+
const url = rest[urlIdx + 1];
|
|
187
|
+
if (!url)
|
|
188
|
+
error('Missing URL pattern', 'agent-browser wait --url <pattern>');
|
|
189
|
+
return { id, action: 'waitforurl', url };
|
|
190
|
+
}
|
|
191
|
+
if (rest.includes('--load') || rest.includes('-l')) {
|
|
192
|
+
const loadIdx = rest.includes('--load') ? rest.indexOf('--load') : rest.indexOf('-l');
|
|
193
|
+
const state = rest[loadIdx + 1];
|
|
194
|
+
if (!state)
|
|
195
|
+
error('Missing load state', 'agent-browser wait --load <state>');
|
|
196
|
+
return { id, action: 'waitforloadstate', state };
|
|
197
|
+
}
|
|
198
|
+
if (rest.includes('--fn') || rest.includes('-f')) {
|
|
199
|
+
const fnIdx = rest.includes('--fn') ? rest.indexOf('--fn') : rest.indexOf('-f');
|
|
200
|
+
const expression = rest[fnIdx + 1];
|
|
201
|
+
if (!expression)
|
|
202
|
+
error('Missing expression', 'agent-browser wait --fn <expression>');
|
|
203
|
+
return { id, action: 'waitforfunction', expression };
|
|
204
|
+
}
|
|
205
|
+
if (rest.includes('--text') || rest.includes('-t')) {
|
|
206
|
+
const textIdx = rest.includes('--text') ? rest.indexOf('--text') : rest.indexOf('-t');
|
|
207
|
+
const text = rest[textIdx + 1];
|
|
208
|
+
if (!text)
|
|
209
|
+
error('Missing text', 'agent-browser wait --text <text>');
|
|
210
|
+
return { id, action: 'wait', selector: `text=${text}` };
|
|
211
|
+
}
|
|
212
|
+
if (rest.includes('--download') || rest.includes('-d')) {
|
|
213
|
+
const cmd = { id, action: 'waitfordownload' };
|
|
214
|
+
const dlIdx = rest.includes('--download') ? rest.indexOf('--download') : rest.indexOf('-d');
|
|
215
|
+
if (rest[dlIdx + 1] && !rest[dlIdx + 1].startsWith('--'))
|
|
216
|
+
cmd.path = rest[dlIdx + 1];
|
|
217
|
+
const timeoutIdx = rest.indexOf('--timeout');
|
|
218
|
+
if (timeoutIdx !== -1 && rest[timeoutIdx + 1])
|
|
219
|
+
cmd.timeout = parseInt(rest[timeoutIdx + 1], 10);
|
|
220
|
+
return cmd;
|
|
221
|
+
}
|
|
222
|
+
if (rest.includes('--request') || rest.includes('-r')) {
|
|
223
|
+
const reqIdx = rest.includes('--request') ? rest.indexOf('--request') : rest.indexOf('-r');
|
|
224
|
+
const url = rest[reqIdx + 1];
|
|
225
|
+
if (!url)
|
|
226
|
+
error('Missing URL pattern', 'agent-browser wait --request <pattern>');
|
|
227
|
+
const cmd = { id, action: 'responsebody', url };
|
|
228
|
+
const timeoutIdx = rest.indexOf('--timeout');
|
|
229
|
+
if (timeoutIdx !== -1 && rest[timeoutIdx + 1])
|
|
230
|
+
cmd.timeout = parseInt(rest[timeoutIdx + 1], 10);
|
|
231
|
+
return cmd;
|
|
232
|
+
}
|
|
233
|
+
if (rest[0]) {
|
|
234
|
+
const timeout = parseInt(rest[0], 10);
|
|
235
|
+
if (!isNaN(timeout))
|
|
236
|
+
return { id, action: 'wait', timeout };
|
|
237
|
+
return { id, action: 'wait', selector: rest[0] };
|
|
238
|
+
}
|
|
239
|
+
error('Missing arguments', 'agent-browser wait <selector|ms|--url|--load|--fn|--text|--download|--request>');
|
|
240
|
+
}
|
|
241
|
+
case 'screenshot': {
|
|
242
|
+
const fullPage = rest.includes('--full') || rest.includes('-f');
|
|
243
|
+
const filtered = rest.filter(r => r !== '--full' && r !== '-f');
|
|
244
|
+
let selector;
|
|
245
|
+
let path;
|
|
246
|
+
if (filtered.length === 2) {
|
|
247
|
+
selector = filtered[0];
|
|
248
|
+
path = filtered[1];
|
|
249
|
+
}
|
|
250
|
+
else if (filtered.length === 1) {
|
|
251
|
+
const arg = filtered[0];
|
|
252
|
+
const isPath = arg.includes('/') || arg.endsWith('.png') || arg.endsWith('.jpg') || arg.endsWith('.jpeg') || arg.endsWith('.webp');
|
|
253
|
+
if (isPath)
|
|
254
|
+
path = arg;
|
|
255
|
+
else
|
|
256
|
+
selector = arg;
|
|
257
|
+
}
|
|
258
|
+
return { id, action: 'screenshot', selector, path, fullPage: fullPage || undefined };
|
|
259
|
+
}
|
|
260
|
+
case 'pdf': {
|
|
261
|
+
const path = rest[0];
|
|
262
|
+
if (!path)
|
|
263
|
+
error('Missing path', 'agent-browser pdf <path>');
|
|
264
|
+
return { id, action: 'pdf', path };
|
|
265
|
+
}
|
|
266
|
+
case 'snapshot': {
|
|
267
|
+
const command = { id, action: 'snapshot' };
|
|
268
|
+
for (let i = 0; i < rest.length; i++) {
|
|
269
|
+
switch (rest[i]) {
|
|
270
|
+
case '-i':
|
|
271
|
+
case '--interactive':
|
|
272
|
+
command.interactive = true;
|
|
273
|
+
break;
|
|
274
|
+
case '-c':
|
|
275
|
+
case '--compact':
|
|
276
|
+
command.compact = true;
|
|
277
|
+
break;
|
|
278
|
+
case '-C':
|
|
279
|
+
case '--cursor':
|
|
280
|
+
command.cursor = true;
|
|
281
|
+
break;
|
|
282
|
+
case '-d':
|
|
283
|
+
case '--depth':
|
|
284
|
+
if (rest[i + 1]) {
|
|
285
|
+
command.maxDepth = parseInt(rest[i + 1], 10);
|
|
286
|
+
i++;
|
|
287
|
+
}
|
|
288
|
+
break;
|
|
289
|
+
case '-s':
|
|
290
|
+
case '--selector':
|
|
291
|
+
if (rest[i + 1]) {
|
|
292
|
+
command.selector = rest[i + 1];
|
|
293
|
+
i++;
|
|
294
|
+
}
|
|
295
|
+
break;
|
|
296
|
+
case '-f':
|
|
297
|
+
case '--in-frame':
|
|
298
|
+
if (rest[i + 1]) {
|
|
299
|
+
command.inFrame = rest[i + 1];
|
|
300
|
+
i++;
|
|
301
|
+
}
|
|
302
|
+
break;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
return command;
|
|
306
|
+
}
|
|
307
|
+
case 'eval': {
|
|
308
|
+
let script;
|
|
309
|
+
let file;
|
|
310
|
+
if (rest.includes('--file') || rest.includes('-f')) {
|
|
311
|
+
const fileIdx = rest.includes('--file') ? rest.indexOf('--file') : rest.indexOf('-f');
|
|
312
|
+
file = rest[fileIdx + 1];
|
|
313
|
+
if (!file)
|
|
314
|
+
error('Missing file path', 'agent-browser eval --file <path>');
|
|
315
|
+
}
|
|
316
|
+
else if (rest.includes('--stdin')) {
|
|
317
|
+
const fd = process.stdin.fd;
|
|
318
|
+
const buffer = Buffer.allocUnsafe(1024);
|
|
319
|
+
const chunks = [];
|
|
320
|
+
let bytesRead;
|
|
321
|
+
const fs = require('fs');
|
|
322
|
+
while ((bytesRead = fs.readSync(fd, buffer, 0, buffer.length)) > 0) {
|
|
323
|
+
chunks.push(Buffer.from(buffer.subarray(0, bytesRead)));
|
|
324
|
+
}
|
|
325
|
+
script = Buffer.concat(chunks).toString('utf8');
|
|
326
|
+
}
|
|
327
|
+
else if (rest.includes('-b') || rest.includes('--base64')) {
|
|
328
|
+
const bIdx = rest.includes('-b') ? rest.indexOf('-b') : rest.indexOf('--base64');
|
|
329
|
+
const encoded = rest[bIdx + 1];
|
|
330
|
+
if (!encoded)
|
|
331
|
+
error('Missing base64 script', 'agent-browser eval -b <base64-script>');
|
|
332
|
+
script = Buffer.from(encoded, 'base64').toString('utf8');
|
|
333
|
+
}
|
|
334
|
+
else {
|
|
335
|
+
script = rest.join(' ');
|
|
336
|
+
if (!script)
|
|
337
|
+
error('Missing script', 'agent-browser eval <script>');
|
|
338
|
+
}
|
|
339
|
+
return { id, action: 'evaluate', script, file };
|
|
340
|
+
}
|
|
341
|
+
case 'close':
|
|
342
|
+
case 'quit':
|
|
343
|
+
case 'exit':
|
|
344
|
+
return { id, action: 'close' };
|
|
345
|
+
case 'get': {
|
|
346
|
+
const subcmd = rest[0];
|
|
347
|
+
if (!subcmd)
|
|
348
|
+
error('Missing subcommand', 'agent-browser get <text|html|value|attr|url|title|count|box|styles> [args...]');
|
|
349
|
+
switch (subcmd) {
|
|
350
|
+
case 'text': {
|
|
351
|
+
const selector = rest[1];
|
|
352
|
+
if (!selector)
|
|
353
|
+
error('Missing selector', 'agent-browser get text <selector>');
|
|
354
|
+
return { id, action: 'gettext', selector };
|
|
355
|
+
}
|
|
356
|
+
case 'html': {
|
|
357
|
+
const selector = rest[1];
|
|
358
|
+
if (!selector)
|
|
359
|
+
error('Missing selector', 'agent-browser get html <selector>');
|
|
360
|
+
return { id, action: 'innerhtml', selector };
|
|
361
|
+
}
|
|
362
|
+
case 'value': {
|
|
363
|
+
const selector = rest[1];
|
|
364
|
+
if (!selector)
|
|
365
|
+
error('Missing selector', 'agent-browser get value <selector>');
|
|
366
|
+
return { id, action: 'inputvalue', selector };
|
|
367
|
+
}
|
|
368
|
+
case 'attr': {
|
|
369
|
+
const selector = rest[1];
|
|
370
|
+
const attribute = rest[2];
|
|
371
|
+
if (!selector || !attribute)
|
|
372
|
+
error('Missing selector or attribute', 'agent-browser get attr <selector> <attribute>');
|
|
373
|
+
return { id, action: 'getattribute', selector, attribute };
|
|
374
|
+
}
|
|
375
|
+
case 'url':
|
|
376
|
+
return { id, action: 'url' };
|
|
377
|
+
case 'title':
|
|
378
|
+
return { id, action: 'title' };
|
|
379
|
+
case 'count': {
|
|
380
|
+
const selector = rest[1];
|
|
381
|
+
if (!selector)
|
|
382
|
+
error('Missing selector', 'agent-browser get count <selector>');
|
|
383
|
+
return { id, action: 'count', selector };
|
|
384
|
+
}
|
|
385
|
+
case 'box': {
|
|
386
|
+
const selector = rest[1];
|
|
387
|
+
if (!selector)
|
|
388
|
+
error('Missing selector', 'agent-browser get box <selector>');
|
|
389
|
+
return { id, action: 'boundingbox', selector };
|
|
390
|
+
}
|
|
391
|
+
case 'styles': {
|
|
392
|
+
const selector = rest[1];
|
|
393
|
+
if (!selector)
|
|
394
|
+
error('Missing selector', 'agent-browser get styles <selector>');
|
|
395
|
+
return { id, action: 'styles', selector };
|
|
396
|
+
}
|
|
397
|
+
default:
|
|
398
|
+
error(`Unknown get subcommand: ${subcmd}`, 'agent-browser get <text|html|value|attr|url|title|count|box|styles> [args...]');
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
case 'is': {
|
|
402
|
+
const subcmd = rest[0];
|
|
403
|
+
if (!subcmd)
|
|
404
|
+
error('Missing subcommand', 'agent-browser is <visible|enabled|checked> <selector>');
|
|
405
|
+
const selector = rest[1];
|
|
406
|
+
if (!selector)
|
|
407
|
+
error('Missing selector', `agent-browser is ${subcmd} <selector>`);
|
|
408
|
+
switch (subcmd) {
|
|
409
|
+
case 'visible':
|
|
410
|
+
return { id, action: 'isvisible', selector };
|
|
411
|
+
case 'enabled':
|
|
412
|
+
return { id, action: 'isenabled', selector };
|
|
413
|
+
case 'checked':
|
|
414
|
+
return { id, action: 'ischecked', selector };
|
|
415
|
+
default:
|
|
416
|
+
error(`Unknown is subcommand: ${subcmd}`, 'agent-browser is <visible|enabled|checked> <selector>');
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
case 'find': {
|
|
420
|
+
const { inFrame, remaining: findRest } = parseInFrame(rest);
|
|
421
|
+
const locator = findRest[0];
|
|
422
|
+
if (!locator)
|
|
423
|
+
error('Missing locator type', 'agent-browser find <locator> <value> [action] [text] [--in-frame <path>]');
|
|
424
|
+
const nameIdx = findRest.indexOf('--name');
|
|
425
|
+
const name = nameIdx !== -1 ? findRest[nameIdx + 1] : undefined;
|
|
426
|
+
const exact = findRest.includes('--exact');
|
|
427
|
+
switch (locator) {
|
|
428
|
+
case 'role': {
|
|
429
|
+
const role = findRest[1];
|
|
430
|
+
if (!role)
|
|
431
|
+
error('Missing role', 'agent-browser find role <role> [action] [--name <name>] [--exact] [--in-frame <path>]');
|
|
432
|
+
const subaction = findRest[2] || 'click';
|
|
433
|
+
const value = findRest.slice(3).filter(a => !a.startsWith('--')).join(' ');
|
|
434
|
+
const cmd = { id, action: 'getbyrole', role, subaction, name, exact };
|
|
435
|
+
if (value)
|
|
436
|
+
cmd.value = value;
|
|
437
|
+
if (inFrame)
|
|
438
|
+
cmd.inFrame = inFrame;
|
|
439
|
+
return cmd;
|
|
440
|
+
}
|
|
441
|
+
case 'text': {
|
|
442
|
+
const text = findRest[1];
|
|
443
|
+
if (!text)
|
|
444
|
+
error('Missing text', 'agent-browser find text <text> [action] [--exact] [--in-frame <path>]');
|
|
445
|
+
const subaction = findRest[2] || 'click';
|
|
446
|
+
const cmd = { id, action: 'getbytext', text, subaction, exact };
|
|
447
|
+
if (inFrame)
|
|
448
|
+
cmd.inFrame = inFrame;
|
|
449
|
+
return cmd;
|
|
450
|
+
}
|
|
451
|
+
case 'label': {
|
|
452
|
+
const label = findRest[1];
|
|
453
|
+
if (!label)
|
|
454
|
+
error('Missing label', 'agent-browser find label <label> [action] [text] [--exact] [--in-frame <path>]');
|
|
455
|
+
const subaction = findRest[2] || 'click';
|
|
456
|
+
const value = findRest.slice(3).filter(a => !a.startsWith('--')).join(' ');
|
|
457
|
+
const cmd = { id, action: 'getbylabel', label, subaction, exact };
|
|
458
|
+
if (value)
|
|
459
|
+
cmd.value = value;
|
|
460
|
+
if (inFrame)
|
|
461
|
+
cmd.inFrame = inFrame;
|
|
462
|
+
return cmd;
|
|
463
|
+
}
|
|
464
|
+
case 'placeholder': {
|
|
465
|
+
const placeholder = findRest[1];
|
|
466
|
+
if (!placeholder)
|
|
467
|
+
error('Missing placeholder', 'agent-browser find placeholder <text> [action] [text] [--exact] [--in-frame <path>]');
|
|
468
|
+
const subaction = findRest[2] || 'click';
|
|
469
|
+
const value = findRest.slice(3).filter(a => !a.startsWith('--')).join(' ');
|
|
470
|
+
const cmd = { id, action: 'getbyplaceholder', placeholder, subaction, exact };
|
|
471
|
+
if (value)
|
|
472
|
+
cmd.value = value;
|
|
473
|
+
if (inFrame)
|
|
474
|
+
cmd.inFrame = inFrame;
|
|
475
|
+
return cmd;
|
|
476
|
+
}
|
|
477
|
+
case 'alt': {
|
|
478
|
+
const text = findRest[1];
|
|
479
|
+
if (!text)
|
|
480
|
+
error('Missing alt text', 'agent-browser find alt <text> [action] [--exact] [--in-frame <path>]');
|
|
481
|
+
const subaction = findRest[2] || 'click';
|
|
482
|
+
const cmd = { id, action: 'getbyalttext', text, subaction, exact };
|
|
483
|
+
if (inFrame)
|
|
484
|
+
cmd.inFrame = inFrame;
|
|
485
|
+
return cmd;
|
|
486
|
+
}
|
|
487
|
+
case 'title': {
|
|
488
|
+
const text = findRest[1];
|
|
489
|
+
if (!text)
|
|
490
|
+
error('Missing title text', 'agent-browser find title <text> [action] [--exact] [--in-frame <path>]');
|
|
491
|
+
const subaction = findRest[2] || 'click';
|
|
492
|
+
const cmd = { id, action: 'getbytitle', text, subaction, exact };
|
|
493
|
+
if (inFrame)
|
|
494
|
+
cmd.inFrame = inFrame;
|
|
495
|
+
return cmd;
|
|
496
|
+
}
|
|
497
|
+
case 'testid': {
|
|
498
|
+
const testId = findRest[1];
|
|
499
|
+
if (!testId)
|
|
500
|
+
error('Missing testid', 'agent-browser find testid <id> [action] [text] [--in-frame <path>]');
|
|
501
|
+
const subaction = findRest[2] || 'click';
|
|
502
|
+
const value = findRest.slice(3).join(' ');
|
|
503
|
+
const cmd = { id, action: 'getbytestid', testId, subaction };
|
|
504
|
+
if (value)
|
|
505
|
+
cmd.value = value;
|
|
506
|
+
if (inFrame)
|
|
507
|
+
cmd.inFrame = inFrame;
|
|
508
|
+
return cmd;
|
|
509
|
+
}
|
|
510
|
+
case 'first': {
|
|
511
|
+
const selector = findRest[1];
|
|
512
|
+
if (!selector)
|
|
513
|
+
error('Missing selector', 'agent-browser find first <selector> [action] [text] [--in-frame <path>]');
|
|
514
|
+
const subaction = findRest[2] || 'click';
|
|
515
|
+
const value = findRest.slice(3).join(' ');
|
|
516
|
+
const cmd = { id, action: 'nth', selector, index: 0, subaction };
|
|
517
|
+
if (value)
|
|
518
|
+
cmd.value = value;
|
|
519
|
+
if (inFrame)
|
|
520
|
+
cmd.inFrame = inFrame;
|
|
521
|
+
return cmd;
|
|
522
|
+
}
|
|
523
|
+
case 'last': {
|
|
524
|
+
const selector = findRest[1];
|
|
525
|
+
if (!selector)
|
|
526
|
+
error('Missing selector', 'agent-browser find last <selector> [action] [text] [--in-frame <path>]');
|
|
527
|
+
const subaction = findRest[2] || 'click';
|
|
528
|
+
const value = findRest.slice(3).join(' ');
|
|
529
|
+
const cmd = { id, action: 'nth', selector, index: -1, subaction };
|
|
530
|
+
if (value)
|
|
531
|
+
cmd.value = value;
|
|
532
|
+
if (inFrame)
|
|
533
|
+
cmd.inFrame = inFrame;
|
|
534
|
+
return cmd;
|
|
535
|
+
}
|
|
536
|
+
case 'nth': {
|
|
537
|
+
const idxStr = findRest[1];
|
|
538
|
+
if (!idxStr)
|
|
539
|
+
error('Missing index', 'agent-browser find nth <index> <selector> [action] [text] [--in-frame <path>]');
|
|
540
|
+
const idx = parseInt(idxStr, 10);
|
|
541
|
+
if (isNaN(idx))
|
|
542
|
+
error('Invalid index', 'agent-browser find nth <index> <selector> [action] [text] [--in-frame <path>]');
|
|
543
|
+
const selector = findRest[2];
|
|
544
|
+
if (!selector)
|
|
545
|
+
error('Missing selector', 'agent-browser find nth <index> <selector> [action] [text] [--in-frame <path>]');
|
|
546
|
+
const subaction = findRest[3] || 'click';
|
|
547
|
+
const value = findRest.slice(4).join(' ');
|
|
548
|
+
const cmd = { id, action: 'nth', selector, index: idx, subaction };
|
|
549
|
+
if (value)
|
|
550
|
+
cmd.value = value;
|
|
551
|
+
if (inFrame)
|
|
552
|
+
cmd.inFrame = inFrame;
|
|
553
|
+
return cmd;
|
|
554
|
+
}
|
|
555
|
+
default:
|
|
556
|
+
error(`Unknown find locator: ${locator}`, 'agent-browser find <role|text|label|placeholder|alt|title|testid|first|last|nth> ...');
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
case 'mouse': {
|
|
560
|
+
const subcmd = rest[0];
|
|
561
|
+
if (!subcmd)
|
|
562
|
+
error('Missing subcommand', 'agent-browser mouse <move|down|up|wheel> [args...]');
|
|
563
|
+
switch (subcmd) {
|
|
564
|
+
case 'move': {
|
|
565
|
+
const x = rest[1] ? parseInt(rest[1], 10) : NaN;
|
|
566
|
+
const y = rest[2] ? parseInt(rest[2], 10) : NaN;
|
|
567
|
+
if (isNaN(x) || isNaN(y))
|
|
568
|
+
error('Missing or invalid coordinates', 'agent-browser mouse move <x> <y>');
|
|
569
|
+
return { id, action: 'mousemove', x, y };
|
|
570
|
+
}
|
|
571
|
+
case 'down':
|
|
572
|
+
return { id, action: 'mousedown', button: rest[1] || 'left' };
|
|
573
|
+
case 'up':
|
|
574
|
+
return { id, action: 'mouseup', button: rest[1] || 'left' };
|
|
575
|
+
case 'wheel': {
|
|
576
|
+
const deltaY = rest[1] ? parseInt(rest[1], 10) : 100;
|
|
577
|
+
const deltaX = rest[2] ? parseInt(rest[2], 10) : 0;
|
|
578
|
+
return { id, action: 'wheel', deltaX, deltaY };
|
|
579
|
+
}
|
|
580
|
+
default:
|
|
581
|
+
error(`Unknown mouse subcommand: ${subcmd}`, 'agent-browser mouse <move|down|up|wheel> [args...]');
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
case 'set': {
|
|
585
|
+
const subcmd = rest[0];
|
|
586
|
+
if (!subcmd)
|
|
587
|
+
error('Missing subcommand', 'agent-browser set <viewport|device|geo|offline|headers|credentials|media> ...');
|
|
588
|
+
switch (subcmd) {
|
|
589
|
+
case 'viewport': {
|
|
590
|
+
const width = rest[1] ? parseInt(rest[1], 10) : NaN;
|
|
591
|
+
const height = rest[2] ? parseInt(rest[2], 10) : NaN;
|
|
592
|
+
if (isNaN(width) || isNaN(height))
|
|
593
|
+
error('Missing or invalid dimensions', 'agent-browser set viewport <width> <height>');
|
|
594
|
+
return { id, action: 'viewport', width, height };
|
|
595
|
+
}
|
|
596
|
+
case 'device': {
|
|
597
|
+
const device = rest[1];
|
|
598
|
+
if (!device)
|
|
599
|
+
error('Missing device name', 'agent-browser set device <name>');
|
|
600
|
+
return { id, action: 'device', device };
|
|
601
|
+
}
|
|
602
|
+
case 'geo':
|
|
603
|
+
case 'geolocation': {
|
|
604
|
+
const latitude = rest[1] ? parseFloat(rest[1]) : NaN;
|
|
605
|
+
const longitude = rest[2] ? parseFloat(rest[2]) : NaN;
|
|
606
|
+
if (isNaN(latitude) || isNaN(longitude))
|
|
607
|
+
error('Missing or invalid coordinates', 'agent-browser set geo <latitude> <longitude>');
|
|
608
|
+
return { id, action: 'geolocation', latitude, longitude };
|
|
609
|
+
}
|
|
610
|
+
case 'offline': {
|
|
611
|
+
const off = rest[1] !== 'off' && rest[1] !== 'false';
|
|
612
|
+
return { id, action: 'offline', offline: off };
|
|
613
|
+
}
|
|
614
|
+
case 'headers': {
|
|
615
|
+
const json = rest[1];
|
|
616
|
+
if (!json)
|
|
617
|
+
error('Missing headers JSON', 'agent-browser set headers <json>');
|
|
618
|
+
try {
|
|
619
|
+
const headers = JSON.parse(json);
|
|
620
|
+
return { id, action: 'headers', headers };
|
|
621
|
+
}
|
|
622
|
+
catch {
|
|
623
|
+
error('Invalid JSON', 'agent-browser set headers <json>');
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
case 'credentials':
|
|
627
|
+
case 'auth': {
|
|
628
|
+
const username = rest[1];
|
|
629
|
+
const password = rest[2];
|
|
630
|
+
if (!username || !password)
|
|
631
|
+
error('Missing credentials', 'agent-browser set credentials <username> <password>');
|
|
632
|
+
return { id, action: 'credentials', username, password };
|
|
633
|
+
}
|
|
634
|
+
case 'media': {
|
|
635
|
+
const color = rest.includes('dark') ? 'dark' : rest.includes('light') ? 'light' : 'no-preference';
|
|
636
|
+
const reduced = rest.includes('reduced-motion') ? 'reduce' : 'no-preference';
|
|
637
|
+
return { id, action: 'emulatemedia', colorScheme: color, reducedMotion: reduced };
|
|
638
|
+
}
|
|
639
|
+
default:
|
|
640
|
+
error(`Unknown set subcommand: ${subcmd}`, 'agent-browser set <viewport|device|geo|offline|headers|credentials|media> ...');
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
case 'network': {
|
|
644
|
+
const subcmd = rest[0];
|
|
645
|
+
if (!subcmd)
|
|
646
|
+
error('Missing subcommand', 'agent-browser network <route|unroute|requests> ...');
|
|
647
|
+
switch (subcmd) {
|
|
648
|
+
case 'route': {
|
|
649
|
+
const url = rest[1];
|
|
650
|
+
if (!url)
|
|
651
|
+
error('Missing URL pattern', 'agent-browser network route <url> [--abort] [--body <json>]');
|
|
652
|
+
const abort = rest.includes('--abort');
|
|
653
|
+
const bodyIdx = rest.indexOf('--body');
|
|
654
|
+
const body = bodyIdx !== -1 ? rest[bodyIdx + 1] : undefined;
|
|
655
|
+
return { id, action: 'route', url, abort, body };
|
|
656
|
+
}
|
|
657
|
+
case 'unroute':
|
|
658
|
+
return { id, action: 'unroute', url: rest[1] };
|
|
659
|
+
case 'requests': {
|
|
660
|
+
const clear = rest.includes('--clear');
|
|
661
|
+
const filterIdx = rest.indexOf('--filter');
|
|
662
|
+
const filter = filterIdx !== -1 ? rest[filterIdx + 1] : undefined;
|
|
663
|
+
return { id, action: 'requests', clear, filter };
|
|
664
|
+
}
|
|
665
|
+
default:
|
|
666
|
+
error(`Unknown network subcommand: ${subcmd}`, 'agent-browser network <route|unroute|requests> ...');
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
case 'storage': {
|
|
670
|
+
const type = rest[0];
|
|
671
|
+
if (!type || (type !== 'local' && type !== 'session'))
|
|
672
|
+
error('Missing storage type', 'agent-browser storage <local|session> [key] [value]');
|
|
673
|
+
const subcmd = rest[1];
|
|
674
|
+
if (!subcmd)
|
|
675
|
+
return { id, action: 'storage_get', type };
|
|
676
|
+
if (subcmd === 'set') {
|
|
677
|
+
const key = rest[2];
|
|
678
|
+
const value = rest[3];
|
|
679
|
+
if (!key || !value)
|
|
680
|
+
error('Missing key or value', 'agent-browser storage <local|session> set <key> <value>');
|
|
681
|
+
return { id, action: 'storage_set', type, key, value };
|
|
682
|
+
}
|
|
683
|
+
if (subcmd === 'clear')
|
|
684
|
+
return { id, action: 'storage_clear', type };
|
|
685
|
+
return { id, action: 'storage_get', type, key: subcmd };
|
|
686
|
+
}
|
|
687
|
+
case 'cookies': {
|
|
688
|
+
const subcmd = rest[0] || 'get';
|
|
689
|
+
switch (subcmd) {
|
|
690
|
+
case 'set': {
|
|
691
|
+
const name = rest[1];
|
|
692
|
+
const value = rest[2];
|
|
693
|
+
if (!name || !value)
|
|
694
|
+
error('Missing name or value', 'agent-browser cookies set <name> <value> [options]');
|
|
695
|
+
const cookie = { name, value };
|
|
696
|
+
for (let i = 3; i < rest.length; i++) {
|
|
697
|
+
switch (rest[i]) {
|
|
698
|
+
case '--url':
|
|
699
|
+
cookie.url = rest[++i];
|
|
700
|
+
break;
|
|
701
|
+
case '--domain':
|
|
702
|
+
cookie.domain = rest[++i];
|
|
703
|
+
break;
|
|
704
|
+
case '--path':
|
|
705
|
+
cookie.path = rest[++i];
|
|
706
|
+
break;
|
|
707
|
+
case '--httpOnly':
|
|
708
|
+
cookie.httpOnly = true;
|
|
709
|
+
break;
|
|
710
|
+
case '--secure':
|
|
711
|
+
cookie.secure = true;
|
|
712
|
+
break;
|
|
713
|
+
case '--sameSite':
|
|
714
|
+
cookie.sameSite = rest[++i];
|
|
715
|
+
break;
|
|
716
|
+
case '--expires':
|
|
717
|
+
cookie.expires = parseInt(rest[++i], 10);
|
|
718
|
+
break;
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
return { id, action: 'cookies_set', cookies: [cookie] };
|
|
722
|
+
}
|
|
723
|
+
case 'clear':
|
|
724
|
+
return { id, action: 'cookies_clear' };
|
|
725
|
+
default:
|
|
726
|
+
return { id, action: 'cookies_get', urls: subcmd !== 'get' ? [subcmd, ...rest.slice(1)] : undefined };
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
case 'tab': {
|
|
730
|
+
const subcmd = rest[0];
|
|
731
|
+
if (!subcmd || subcmd === 'list')
|
|
732
|
+
return { id, action: 'tab_list' };
|
|
733
|
+
if (subcmd === 'new') {
|
|
734
|
+
const cmd = { id, action: 'tab_new' };
|
|
735
|
+
if (rest[1])
|
|
736
|
+
cmd.url = rest[1];
|
|
737
|
+
return cmd;
|
|
738
|
+
}
|
|
739
|
+
if (subcmd === 'close') {
|
|
740
|
+
const cmd = { id, action: 'tab_close' };
|
|
741
|
+
if (rest[1])
|
|
742
|
+
cmd.index = parseInt(rest[1], 10);
|
|
743
|
+
return cmd;
|
|
744
|
+
}
|
|
745
|
+
const index = parseInt(subcmd, 10);
|
|
746
|
+
if (!isNaN(index))
|
|
747
|
+
return { id, action: 'tab_switch', index };
|
|
748
|
+
error('Unknown tab command', 'agent-browser tab <list|new|close|index>');
|
|
749
|
+
}
|
|
750
|
+
case 'window': {
|
|
751
|
+
if (rest[0] === 'new')
|
|
752
|
+
return { id, action: 'window_new' };
|
|
753
|
+
error('Unknown window command', 'agent-browser window new');
|
|
754
|
+
}
|
|
755
|
+
case 'dialog': {
|
|
756
|
+
const subcmd = rest[0];
|
|
757
|
+
if (!subcmd)
|
|
758
|
+
error('Missing subcommand', 'agent-browser dialog <accept|dismiss> [text]');
|
|
759
|
+
if (subcmd === 'accept') {
|
|
760
|
+
const cmd = { id, action: 'dialog', response: 'accept' };
|
|
761
|
+
if (rest[1])
|
|
762
|
+
cmd.promptText = rest[1];
|
|
763
|
+
return cmd;
|
|
764
|
+
}
|
|
765
|
+
if (subcmd === 'dismiss')
|
|
766
|
+
return { id, action: 'dialog', response: 'dismiss' };
|
|
767
|
+
error('Unknown dialog command', 'agent-browser dialog <accept|dismiss> [text]');
|
|
768
|
+
}
|
|
769
|
+
case 'trace': {
|
|
770
|
+
const subcmd = rest[0];
|
|
771
|
+
if (!subcmd)
|
|
772
|
+
error('Missing subcommand', 'agent-browser trace <start|stop> [path]');
|
|
773
|
+
if (subcmd === 'start')
|
|
774
|
+
return { id, action: 'trace_start' };
|
|
775
|
+
if (subcmd === 'stop') {
|
|
776
|
+
const path = rest[1];
|
|
777
|
+
if (!path)
|
|
778
|
+
error('Missing path', 'agent-browser trace stop <path>');
|
|
779
|
+
return { id, action: 'trace_stop', path };
|
|
780
|
+
}
|
|
781
|
+
error('Unknown trace command', 'agent-browser trace <start|stop> [path]');
|
|
782
|
+
}
|
|
783
|
+
case 'record': {
|
|
784
|
+
const subcmd = rest[0];
|
|
785
|
+
if (!subcmd)
|
|
786
|
+
error('Missing subcommand', 'agent-browser record <start|stop|restart> [path] [url]');
|
|
787
|
+
if (subcmd === 'start') {
|
|
788
|
+
const path = rest[1];
|
|
789
|
+
if (!path)
|
|
790
|
+
error('Missing path', 'agent-browser record start <output.webm> [url]');
|
|
791
|
+
const cmd = { id, action: 'recording_start', path };
|
|
792
|
+
if (rest[2])
|
|
793
|
+
cmd.url = rest[2].startsWith('http') ? rest[2] : `https://${rest[2]}`;
|
|
794
|
+
return cmd;
|
|
795
|
+
}
|
|
796
|
+
if (subcmd === 'stop')
|
|
797
|
+
return { id, action: 'recording_stop' };
|
|
798
|
+
if (subcmd === 'restart') {
|
|
799
|
+
const path = rest[1];
|
|
800
|
+
if (!path)
|
|
801
|
+
error('Missing path', 'agent-browser record restart <output.webm> [url]');
|
|
802
|
+
const cmd = { id, action: 'recording_restart', path };
|
|
803
|
+
if (rest[2])
|
|
804
|
+
cmd.url = rest[2].startsWith('http') ? rest[2] : `https://${rest[2]}`;
|
|
805
|
+
return cmd;
|
|
806
|
+
}
|
|
807
|
+
error('Unknown record command', 'agent-browser record <start|stop|restart> [path] [url]');
|
|
808
|
+
}
|
|
809
|
+
case 'console':
|
|
810
|
+
return { id, action: 'console', clear: rest.includes('--clear') };
|
|
811
|
+
case 'errors':
|
|
812
|
+
return { id, action: 'errors', clear: rest.includes('--clear') };
|
|
813
|
+
case 'highlight': {
|
|
814
|
+
const selector = rest[0];
|
|
815
|
+
if (!selector)
|
|
816
|
+
error('Missing selector', 'agent-browser highlight <selector>');
|
|
817
|
+
return { id, action: 'highlight', selector };
|
|
818
|
+
}
|
|
819
|
+
case 'state': {
|
|
820
|
+
const subcmd = rest[0];
|
|
821
|
+
if (!subcmd)
|
|
822
|
+
error('Missing subcommand', 'agent-browser state <save|load> <path>');
|
|
823
|
+
const path = rest[1];
|
|
824
|
+
if (!path)
|
|
825
|
+
error('Missing path', `agent-browser state ${subcmd} <path>`);
|
|
826
|
+
if (subcmd === 'save')
|
|
827
|
+
return { id, action: 'state_save', path };
|
|
828
|
+
if (subcmd === 'load')
|
|
829
|
+
return { id, action: 'state_load', path };
|
|
830
|
+
error('Unknown state command', 'agent-browser state <save|load> <path>');
|
|
831
|
+
}
|
|
832
|
+
case 'connect': {
|
|
833
|
+
const endpoint = rest[0];
|
|
834
|
+
if (!endpoint)
|
|
835
|
+
error('Missing endpoint', 'agent-browser connect <port|url>');
|
|
836
|
+
if (endpoint.startsWith('ws://') || endpoint.startsWith('wss://') || endpoint.startsWith('http://') || endpoint.startsWith('https://')) {
|
|
837
|
+
return { id, action: 'launch', cdpUrl: endpoint };
|
|
838
|
+
}
|
|
839
|
+
const port = parseInt(endpoint, 10);
|
|
840
|
+
if (isNaN(port) || port <= 0 || port > 65535)
|
|
841
|
+
error('Invalid port', 'agent-browser connect <port|url>');
|
|
842
|
+
return { id, action: 'launch', cdpPort: port };
|
|
843
|
+
}
|
|
844
|
+
case 'tap': {
|
|
845
|
+
const selector = rest[0];
|
|
846
|
+
if (!selector)
|
|
847
|
+
error('Missing selector', 'agent-browser tap <selector>');
|
|
848
|
+
return { id, action: 'tap', selector };
|
|
849
|
+
}
|
|
850
|
+
case 'swipe': {
|
|
851
|
+
const direction = rest[0];
|
|
852
|
+
if (!direction || !['up', 'down', 'left', 'right'].includes(direction))
|
|
853
|
+
error('Invalid direction', 'agent-browser swipe <up|down|left|right> [distance]');
|
|
854
|
+
const cmd = { id, action: 'swipe', direction };
|
|
855
|
+
if (rest[1])
|
|
856
|
+
cmd.distance = parseInt(rest[1], 10);
|
|
857
|
+
return cmd;
|
|
858
|
+
}
|
|
859
|
+
case 'device': {
|
|
860
|
+
const subcmd = rest[0];
|
|
861
|
+
if (!subcmd || subcmd === 'list')
|
|
862
|
+
return { id, action: 'device_list' };
|
|
863
|
+
error('Unknown device command', 'agent-browser device [list]');
|
|
864
|
+
}
|
|
865
|
+
default:
|
|
866
|
+
error(`Unknown command: ${cmd}`);
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
async function ensureDaemonRunning() {
|
|
870
|
+
if (isDaemonRunning())
|
|
871
|
+
return;
|
|
872
|
+
console.log('Starting agent-browser daemon...');
|
|
873
|
+
// Get daemon.js path
|
|
874
|
+
const currentDir = path.dirname(url.fileURLToPath(import.meta.url));
|
|
875
|
+
const daemonPath = path.join(currentDir, '../dist/daemon.js');
|
|
876
|
+
// Spawn independent daemon process
|
|
877
|
+
const daemon = spawn('node', [daemonPath], {
|
|
878
|
+
detached: true,
|
|
879
|
+
stdio: 'ignore',
|
|
880
|
+
env: {
|
|
881
|
+
...process.env,
|
|
882
|
+
AGENT_BROWSER_DAEMON: '1',
|
|
883
|
+
},
|
|
884
|
+
});
|
|
885
|
+
daemon.unref();
|
|
886
|
+
// Wait for daemon to be ready (up to 5 seconds)
|
|
887
|
+
for (let i = 0; i < 50; i++) {
|
|
888
|
+
await new Promise((r) => setTimeout(r, 100));
|
|
889
|
+
if (isDaemonRunning())
|
|
890
|
+
return;
|
|
891
|
+
}
|
|
892
|
+
throw new Error('Daemon failed to start');
|
|
893
|
+
}
|
|
894
|
+
function sendCommandToDaemon(command) {
|
|
895
|
+
return new Promise((resolve, reject) => {
|
|
896
|
+
const connectionInfo = getConnectionInfo();
|
|
897
|
+
let client;
|
|
898
|
+
if (connectionInfo.type === 'unix' && connectionInfo.path) {
|
|
899
|
+
client = net.createConnection({ path: connectionInfo.path });
|
|
900
|
+
}
|
|
901
|
+
else if (connectionInfo.type === 'tcp' && connectionInfo.port) {
|
|
902
|
+
client = net.createConnection({ port: connectionInfo.port, host: '127.0.0.1' });
|
|
903
|
+
}
|
|
904
|
+
else {
|
|
905
|
+
reject(new Error('Invalid connection info'));
|
|
906
|
+
return;
|
|
907
|
+
}
|
|
908
|
+
client.on('connect', () => {
|
|
909
|
+
client.write(JSON.stringify(command) + '\n');
|
|
910
|
+
});
|
|
911
|
+
let buffer = '';
|
|
912
|
+
client.on('data', (data) => {
|
|
913
|
+
buffer += data.toString();
|
|
914
|
+
if (buffer.includes('\n')) {
|
|
915
|
+
try {
|
|
916
|
+
const response = JSON.parse(buffer.trim());
|
|
917
|
+
client.end();
|
|
918
|
+
resolve(response);
|
|
919
|
+
}
|
|
920
|
+
catch {
|
|
921
|
+
client.end();
|
|
922
|
+
reject(new Error('Invalid response'));
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
});
|
|
926
|
+
client.on('error', (err) => {
|
|
927
|
+
reject(err);
|
|
928
|
+
});
|
|
929
|
+
client.on('timeout', () => {
|
|
930
|
+
client.end();
|
|
931
|
+
reject(new Error('Connection timeout'));
|
|
932
|
+
});
|
|
933
|
+
client.setTimeout(30000);
|
|
934
|
+
});
|
|
935
|
+
}
|
|
936
|
+
async function main() {
|
|
937
|
+
const args = process.argv.slice(2);
|
|
938
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
939
|
+
printHelp();
|
|
940
|
+
return;
|
|
941
|
+
}
|
|
942
|
+
if (args.includes('--version') || args.includes('-V')) {
|
|
943
|
+
console.log('agent-browser 0.9.2');
|
|
944
|
+
return;
|
|
945
|
+
}
|
|
946
|
+
try {
|
|
947
|
+
const command = parseCliArgs(process.argv.slice(2));
|
|
948
|
+
await ensureDaemonRunning();
|
|
949
|
+
const response = await sendCommandToDaemon(command);
|
|
950
|
+
if (response.success) {
|
|
951
|
+
if (response.data) {
|
|
952
|
+
if (typeof response.data === 'object' && response.data.snapshot) {
|
|
953
|
+
console.log(response.data.snapshot);
|
|
954
|
+
}
|
|
955
|
+
else {
|
|
956
|
+
console.log(JSON.stringify(response.data, null, 2));
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
else {
|
|
960
|
+
console.log('✓ Done');
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
else {
|
|
964
|
+
console.error(`✗ ${response.error || 'Unknown error'}`);
|
|
965
|
+
process.exit(1);
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
catch (error) {
|
|
969
|
+
if (error instanceof CliError) {
|
|
970
|
+
console.error(`✗ Error: ${error.message}`);
|
|
971
|
+
if (error.usage)
|
|
972
|
+
console.error(`Usage: ${error.usage}`);
|
|
973
|
+
}
|
|
974
|
+
else {
|
|
975
|
+
console.error(`✗ ${error instanceof Error ? error.message : String(error)}`);
|
|
976
|
+
}
|
|
977
|
+
process.exit(1);
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
main().catch((error) => {
|
|
981
|
+
console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
982
|
+
process.exit(1);
|
|
983
|
+
});
|
|
984
|
+
function printHelp() {
|
|
985
|
+
console.log(`agent-browser - fast browser automation CLI for AI agents
|
|
986
|
+
|
|
987
|
+
Usage: agent-browser <command> [args] [options]
|
|
988
|
+
|
|
989
|
+
Core Commands:
|
|
990
|
+
open <url> Navigate to URL
|
|
991
|
+
click <sel> Click element (supports --in-frame)
|
|
992
|
+
dblclick <sel> Double-click element (supports --in-frame)
|
|
993
|
+
type <sel> <text> Type into element (supports --in-frame)
|
|
994
|
+
fill <sel> <text> Clear and fill (supports --in-frame)
|
|
995
|
+
hover <sel> Hover element (supports --in-frame)
|
|
996
|
+
focus <sel> Focus element (supports --in-frame)
|
|
997
|
+
check <sel> Check checkbox (supports --in-frame)
|
|
998
|
+
uncheck <sel> Uncheck checkbox (supports --in-frame)
|
|
999
|
+
select <sel> <val...> Select dropdown option (supports --in-frame)
|
|
1000
|
+
press <key> Press key
|
|
1001
|
+
drag <src> <dst> Drag and drop
|
|
1002
|
+
upload <sel> <files...> Upload files
|
|
1003
|
+
download <sel> <path> Download file
|
|
1004
|
+
scroll <dir> [px] Scroll (up/down/left/right)
|
|
1005
|
+
scrollintoview <sel> Scroll element into view
|
|
1006
|
+
wait <sel|ms> Wait for element or time
|
|
1007
|
+
screenshot [path] Take screenshot (supports --full, -f)
|
|
1008
|
+
pdf <path> Save as PDF
|
|
1009
|
+
snapshot Accessibility tree
|
|
1010
|
+
eval <js> Run JavaScript (supports --file, --stdin, --base64)
|
|
1011
|
+
connect <port|url> Connect to browser via CDP
|
|
1012
|
+
close Close browser
|
|
1013
|
+
|
|
1014
|
+
Navigation:
|
|
1015
|
+
back Go back
|
|
1016
|
+
forward Go forward
|
|
1017
|
+
reload Reload page
|
|
1018
|
+
|
|
1019
|
+
Element Selection:
|
|
1020
|
+
--in-frame <path> Target element in iframe (e.g., --in-frame "frame1.frame2")
|
|
1021
|
+
|
|
1022
|
+
Get Info: agent-browser get <what> [selector]
|
|
1023
|
+
text, html, value, attr <name>, title, url, count, box, styles
|
|
1024
|
+
|
|
1025
|
+
Check State: agent-browser is <what> <selector>
|
|
1026
|
+
visible, enabled, checked
|
|
1027
|
+
|
|
1028
|
+
Find Elements: agent-browser find <locator> <value> [action] [text]
|
|
1029
|
+
role, text, label, placeholder, alt, title, testid, first, last, nth
|
|
1030
|
+
|
|
1031
|
+
Mouse: agent-browser mouse <action> [args]
|
|
1032
|
+
move <x> <y>, down [btn], up [btn], wheel <dy> [dx]
|
|
1033
|
+
|
|
1034
|
+
Browser Settings: agent-browser set <setting> [value]
|
|
1035
|
+
viewport <w> <h>, device <name>, geo <lat> <lng>
|
|
1036
|
+
offline [on|off], headers <json>, credentials <user> <pass>
|
|
1037
|
+
media [dark|light] [reduced-motion]
|
|
1038
|
+
|
|
1039
|
+
Network: agent-browser network <action>
|
|
1040
|
+
route <url> [--abort|--body <json>]
|
|
1041
|
+
unroute [url]
|
|
1042
|
+
requests [--clear] [--filter <pattern>]
|
|
1043
|
+
|
|
1044
|
+
Storage:
|
|
1045
|
+
cookies [get|set|clear] Manage cookies
|
|
1046
|
+
storage <local|session> Manage web storage
|
|
1047
|
+
|
|
1048
|
+
Tabs:
|
|
1049
|
+
tab [new|list|close|<n>] Manage tabs
|
|
1050
|
+
|
|
1051
|
+
Debug:
|
|
1052
|
+
trace start|stop [path] Record trace
|
|
1053
|
+
record start <path> [url] Start video recording
|
|
1054
|
+
record stop Stop and save video
|
|
1055
|
+
console [--clear] View console logs
|
|
1056
|
+
errors [--clear] View page errors
|
|
1057
|
+
highlight <sel> Highlight element
|
|
1058
|
+
|
|
1059
|
+
Sessions:
|
|
1060
|
+
session Show current session name
|
|
1061
|
+
session list List active sessions
|
|
1062
|
+
|
|
1063
|
+
Setup:
|
|
1064
|
+
install Install browser binaries
|
|
1065
|
+
install --with-deps Install with system dependencies (Linux)
|
|
1066
|
+
|
|
1067
|
+
Snapshot Options:
|
|
1068
|
+
-i, --interactive Only interactive elements
|
|
1069
|
+
-c, --compact Remove empty structural elements
|
|
1070
|
+
-d, --depth <n> Limit tree depth
|
|
1071
|
+
-s, --selector <sel> Scope to CSS selector
|
|
1072
|
+
-f, --in-frame <path> Target iframe
|
|
1073
|
+
|
|
1074
|
+
Options:
|
|
1075
|
+
--session <name> Isolated session
|
|
1076
|
+
--profile <path> Persistent browser profile
|
|
1077
|
+
--state <path> Load storage state from JSON
|
|
1078
|
+
--headers <json> HTTP headers
|
|
1079
|
+
--executable-path <path> Custom browser executable
|
|
1080
|
+
--extension <path> Load browser extensions
|
|
1081
|
+
--args <args> Browser launch args
|
|
1082
|
+
--user-agent <ua> Custom User-Agent
|
|
1083
|
+
--proxy <server> Proxy server URL
|
|
1084
|
+
--ignore-https-errors Ignore HTTPS certificate errors
|
|
1085
|
+
--headed Show browser window
|
|
1086
|
+
--cdp <port> Connect via CDP
|
|
1087
|
+
--json JSON output
|
|
1088
|
+
--version, -V Show version
|
|
1089
|
+
--help, -h Show this help
|
|
1090
|
+
|
|
1091
|
+
Examples:
|
|
1092
|
+
agent-browser open example.com
|
|
1093
|
+
agent-browser snapshot -i
|
|
1094
|
+
agent-browser click @e2 --in-frame "frame1"
|
|
1095
|
+
agent-browser fill @e3 "test@example.com"
|
|
1096
|
+
agent-browser find role button click --name Submit
|
|
1097
|
+
agent-browser eval --file script.js
|
|
1098
|
+
agent-browser screenshot --full
|
|
1099
|
+
`);
|
|
1100
|
+
}
|
|
1101
|
+
//# sourceMappingURL=cli-old.js.map
|