@dyyz1993/agent-browser 0.11.5 → 0.12.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/bin/agent-browser-darwin-arm64 +0 -0
- package/dist/__tests__/utils/parseCli.d.ts.map +1 -1
- package/dist/__tests__/utils/parseCli.js +97 -2
- package/dist/__tests__/utils/parseCli.js.map +1 -1
- package/dist/actions.d.ts.map +1 -1
- package/dist/actions.js +117 -81
- package/dist/actions.js.map +1 -1
- package/dist/browser.d.ts +1 -0
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +24 -29
- package/dist/browser.js.map +1 -1
- package/dist/cli/commands.d.ts.map +1 -1
- package/dist/cli/commands.js +102 -3
- package/dist/cli/commands.js.map +1 -1
- package/dist/cli/connection.d.ts.map +1 -1
- package/dist/cli/connection.js +12 -29
- package/dist/cli/connection.js.map +1 -1
- package/dist/cli/help.d.ts.map +1 -1
- package/dist/cli/help.js +35 -25
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/output.d.ts.map +1 -1
- package/dist/cli/output.js +3 -0
- package/dist/cli/output.js.map +1 -1
- package/dist/cli.js +117 -3
- package/dist/cli.js.map +1 -1
- package/dist/daemon.d.ts +18 -2
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +46 -32
- package/dist/daemon.js.map +1 -1
- package/dist/message-bridge.d.ts.map +1 -1
- package/dist/message-bridge.js +4 -1
- package/dist/message-bridge.js.map +1 -1
- package/dist/rc-config.d.ts +42 -0
- package/dist/rc-config.d.ts.map +1 -0
- package/dist/rc-config.js +170 -0
- package/dist/rc-config.js.map +1 -0
- package/dist/recorder/inject.js +30 -24
- package/package.json +1 -1
- 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/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/generate-skill.cjs +303 -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/verify-form.sh +67 -0
- package/scripts/verify-login.sh +65 -0
- package/scripts/verify-recording.sh +80 -0
- package/scripts/verify-upload.sh +41 -0
- package/skills/agent-browser/SKILL.md +135 -370
- package/bin/agent-browser-linux-x64 +0 -0
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { execSync } = require('child_process');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
|
|
7
|
+
const SKILL_DIR = path.join(__dirname, '..', 'skills', 'agent-browser');
|
|
8
|
+
const REFS_DIR = path.join(SKILL_DIR, 'references');
|
|
9
|
+
|
|
10
|
+
function run(cmd) {
|
|
11
|
+
try {
|
|
12
|
+
return execSync(cmd, { encoding: 'utf-8', timeout: 10000 }).trim();
|
|
13
|
+
} catch {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function getSections(helpText) {
|
|
19
|
+
if (!helpText) return {};
|
|
20
|
+
const sections = {};
|
|
21
|
+
let current = null;
|
|
22
|
+
for (const line of helpText.split('\n')) {
|
|
23
|
+
const headerMatch = line.match(/^([A-Z][A-Za-z &/\-]+):\s*$/);
|
|
24
|
+
if (headerMatch) {
|
|
25
|
+
current = headerMatch[1];
|
|
26
|
+
sections[current] = [];
|
|
27
|
+
} else if (current) {
|
|
28
|
+
sections[current].push(line);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return sections;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function extractCommands(sections) {
|
|
35
|
+
const cmds = [];
|
|
36
|
+
for (const [name, lines] of Object.entries(sections)) {
|
|
37
|
+
for (const line of lines) {
|
|
38
|
+
const m = line.match(/^\s{2}(\S+)\s+(.*)/);
|
|
39
|
+
if (m) cmds.push({ group: name, name: m[1], desc: m[2].trim() });
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return cmds;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
console.log('Scanning agent-browser CLI...');
|
|
46
|
+
|
|
47
|
+
const helpText = run('agent-browser --help 2>&1');
|
|
48
|
+
if (!helpText) {
|
|
49
|
+
console.error('ERROR: agent-browser not found or failed.');
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const sections = getSections(helpText);
|
|
54
|
+
const commands = extractCommands(sections);
|
|
55
|
+
|
|
56
|
+
console.log(`Found ${Object.keys(sections).length} sections, ${commands.length} commands`);
|
|
57
|
+
|
|
58
|
+
const existingRefs = fs.readdirSync(REFS_DIR)
|
|
59
|
+
.filter(f => f.endsWith('.md'))
|
|
60
|
+
.map(f => f.replace('.md', ''));
|
|
61
|
+
|
|
62
|
+
console.log(`Existing reference docs: ${existingRefs.join(', ')}`);
|
|
63
|
+
|
|
64
|
+
const subSkills = [
|
|
65
|
+
{
|
|
66
|
+
title: 'Page Navigation & Interaction',
|
|
67
|
+
match: c => ['Core Commands', 'Navigation'].includes(c.group) &&
|
|
68
|
+
!['screenshot', 'pdf', 'snapshot', 'eval', 'connect', 'close'].includes(c.name),
|
|
69
|
+
summary: 'open, click, fill, type, press, hover, drag, scroll, check/uncheck, select',
|
|
70
|
+
ref: null,
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
title: 'Snapshot & Element Inspection',
|
|
74
|
+
match: c => c.name === 'snapshot' || c.group === 'Get Info' || c.group === 'Check State',
|
|
75
|
+
summary: 'snapshot -i (refs), get text/url/title/box/styles, is visible/enabled/checked',
|
|
76
|
+
ref: 'snapshot-refs',
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
title: 'Finding Elements',
|
|
80
|
+
match: c => c.group === 'Find Elements',
|
|
81
|
+
summary: 'find by role, text, label, placeholder, testid, nth',
|
|
82
|
+
ref: 'commands',
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
title: 'Data Extraction',
|
|
86
|
+
match: c => c.name === 'eval',
|
|
87
|
+
summary: 'JS evaluation, DOM scraping, API interception, infinite scroll',
|
|
88
|
+
ref: 'data-extraction',
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
title: 'Network Control',
|
|
92
|
+
match: c => c.group === 'Network',
|
|
93
|
+
summary: 'request monitoring, API mocking, URL blocking',
|
|
94
|
+
ref: 'network-monitoring',
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
title: 'Session & State',
|
|
98
|
+
match: c => ['Sessions', 'Storage', 'Tabs'].includes(c.group) ||
|
|
99
|
+
['connect', 'close'].includes(c.name),
|
|
100
|
+
summary: 'named sessions, state save/load, cookies, tabs, parallel sessions',
|
|
101
|
+
ref: 'session-management',
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
title: 'Authentication',
|
|
105
|
+
match: () => false,
|
|
106
|
+
summary: 'login flows, OAuth, 2FA, state reuse, cookie persistence',
|
|
107
|
+
ref: 'authentication',
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
title: 'Recording & Replay',
|
|
111
|
+
match: c => c.group === 'Debug' && (c.name.startsWith('record') || c.name.startsWith('recorder')),
|
|
112
|
+
summary: 'step recorder (YAML), video recording (WebM), trace',
|
|
113
|
+
ref: 'recorder',
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
title: 'Visual Remote Control (Viewer)',
|
|
117
|
+
match: c => c.group === 'Remote' && c.name === 'viewer',
|
|
118
|
+
summary: 'real-time frame streaming, element crop mode, WebSocket viewer',
|
|
119
|
+
ref: 'viewer-mode',
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
title: 'Mobile Remote Control',
|
|
123
|
+
match: () => false,
|
|
124
|
+
summary: 'touchpad gestures, input panel, IME/CJK, DeviceMode auto-switch',
|
|
125
|
+
ref: 'mobile-viewer',
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
title: 'iOS Simulator (Appium)',
|
|
129
|
+
match: () => false,
|
|
130
|
+
summary: 'native iOS automation via Xcode + Appium',
|
|
131
|
+
ref: null,
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
title: 'Cloud Browser Providers',
|
|
135
|
+
match: () => false,
|
|
136
|
+
summary: 'browserbase, kernel, browseruse',
|
|
137
|
+
ref: null,
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
title: 'Proxy & Network Config',
|
|
141
|
+
match: c => c.name === 'install',
|
|
142
|
+
summary: 'HTTP/SOCKS5 proxy, geo-testing, rotating proxies',
|
|
143
|
+
ref: 'proxy-support',
|
|
144
|
+
},
|
|
145
|
+
];
|
|
146
|
+
|
|
147
|
+
for (const sk of subSkills) {
|
|
148
|
+
const seen = new Set();
|
|
149
|
+
sk.matched = commands.filter(sk.match).filter(c => {
|
|
150
|
+
if (seen.has(c.name)) return false;
|
|
151
|
+
seen.add(c.name);
|
|
152
|
+
return true;
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const envSection = sections['Environment'] || [];
|
|
157
|
+
const envVars = envSection
|
|
158
|
+
.map(l => l.match(/^\s+(\S+)\s+(.*)/))
|
|
159
|
+
.filter(Boolean)
|
|
160
|
+
.map(m => ({ name: m[1], desc: m[2].trim() }));
|
|
161
|
+
|
|
162
|
+
const optSection = sections['Options'] || [];
|
|
163
|
+
const opts = optSection
|
|
164
|
+
.map(l => {
|
|
165
|
+
const m = l.match(/^\s+(-{1,2}[\w-]+(?:,\s*-{1,2}[\w-]+)?)\s+<([^>]+)>\s+(.*)/);
|
|
166
|
+
if (m) return { flag: `${m[1]} <${m[2]}>`, desc: m[3].trim() };
|
|
167
|
+
const m2 = l.match(/^\s+(-{1,2}[\w-]+(?:,\s*-{1,2}[\w-]+)?)\s+(.*)/);
|
|
168
|
+
if (m2) return { flag: m2[1], desc: m2[2].trim() };
|
|
169
|
+
return null;
|
|
170
|
+
})
|
|
171
|
+
.filter(Boolean);
|
|
172
|
+
|
|
173
|
+
console.log(`Parsed ${envVars.length} env vars, ${opts.length} flags`);
|
|
174
|
+
|
|
175
|
+
const refTableRows = [
|
|
176
|
+
{ file: 'commands.md', label: 'Complete Command Reference', desc: 'All commands with options and examples' },
|
|
177
|
+
{ file: 'snapshot-refs.md', label: 'Snapshot & Refs', desc: 'Ref lifecycle, invalidation rules, shell scripts' },
|
|
178
|
+
{ file: 'data-extraction.md', label: 'Data Extraction', desc: 'DOM scraping, JS eval, API interception, infinite scroll' },
|
|
179
|
+
{ file: 'session-management.md', label: 'Session & State', desc: 'Parallel sessions, state persistence, concurrent scraping' },
|
|
180
|
+
{ file: 'authentication.md', label: 'Authentication', desc: 'Login flows, OAuth, 2FA, state reuse' },
|
|
181
|
+
{ file: 'network-monitoring.md', label: 'Network Control', desc: 'Request monitoring, API mocking, URL blocking' },
|
|
182
|
+
{ file: 'recorder.md', label: 'Recording & Replay', desc: 'Step recorder, video recording, trace' },
|
|
183
|
+
{ file: 'proxy-support.md', label: 'Proxy Config', desc: 'HTTP/SOCKS5 proxy, geo-testing, rotating proxies' },
|
|
184
|
+
{ file: 'viewer-mode.md', label: 'Viewer / Streaming', desc: 'Frame streaming, element crop, architecture' },
|
|
185
|
+
{ file: 'mobile-viewer.md', label: 'Mobile Remote Control', desc: 'Touchpad, input panel, IME/CJK, DeviceMode' },
|
|
186
|
+
{ file: 'video-recording.md', label: 'Video Recording', desc: 'WebM video capture for debugging' },
|
|
187
|
+
];
|
|
188
|
+
|
|
189
|
+
const output = [];
|
|
190
|
+
const w = (s = '') => output.push(s);
|
|
191
|
+
|
|
192
|
+
w('---');
|
|
193
|
+
w('name: agent-browser');
|
|
194
|
+
w('description: Browser automation CLI for AI agents. Use when the user needs to interact with websites, including navigating pages, filling forms, clicking buttons, taking screenshots, extracting data, testing web apps, viewer/streaming mode, mobile remote control, or automating any browser task. Triggers include requests to "open a website", "fill out a form", "click a button", "take a screenshot", "scrape data from a page", "test this web app", "login to a site", "automate browser actions", "view remote browser", "mobile browsing", or any task requiring programmatic web interaction.');
|
|
195
|
+
w('allowed-tools: Bash(agent-browser:*)');
|
|
196
|
+
w('---');
|
|
197
|
+
w();
|
|
198
|
+
w('# Browser Automation with agent-browser');
|
|
199
|
+
w();
|
|
200
|
+
w('Fast CLI for browser automation. Works headlessly by default, supports named sessions, proxy, and remote streaming.');
|
|
201
|
+
w();
|
|
202
|
+
w('## Browser Setup (macOS)');
|
|
203
|
+
w();
|
|
204
|
+
w('Set the browser path to avoid Playwright downloading Chromium:');
|
|
205
|
+
w();
|
|
206
|
+
w('```bash');
|
|
207
|
+
w('export AGENT_BROWSER_EXECUTABLE_PATH=/Applications/Chromium.app/Contents/MacOS/Chromium');
|
|
208
|
+
w('```');
|
|
209
|
+
w();
|
|
210
|
+
w('Or per-command: `agent-browser --executable-path /Applications/Chromium.app/Contents/MacOS/Chromium open <url>`');
|
|
211
|
+
w();
|
|
212
|
+
w('Verify: `agent-browser config`');
|
|
213
|
+
w();
|
|
214
|
+
w('## Quick Start');
|
|
215
|
+
w();
|
|
216
|
+
w('```bash');
|
|
217
|
+
w('agent-browser open https://example.com');
|
|
218
|
+
w('agent-browser snapshot -i # Get refs: @e1, @e2, ...');
|
|
219
|
+
w('agent-browser fill @e1 "user@example.com" # Interact via refs');
|
|
220
|
+
w('agent-browser click @e2');
|
|
221
|
+
w('agent-browser snapshot -i # Re-snapshot after page change');
|
|
222
|
+
w('```');
|
|
223
|
+
w();
|
|
224
|
+
w('## Discovering Commands');
|
|
225
|
+
w();
|
|
226
|
+
w('```bash');
|
|
227
|
+
w('agent-browser --help # All commands & options');
|
|
228
|
+
w('agent-browser snapshot --help # Command-specific help');
|
|
229
|
+
w('agent-browser config # Current config & env vars');
|
|
230
|
+
w('```');
|
|
231
|
+
w();
|
|
232
|
+
w('The CLI is self-documenting. When unsure about a command, run `--help` first.');
|
|
233
|
+
w();
|
|
234
|
+
w('## Capabilities');
|
|
235
|
+
w();
|
|
236
|
+
w('| Area | Key Commands | Deep Dive |');
|
|
237
|
+
w('|------|-------------|-----------|');
|
|
238
|
+
|
|
239
|
+
for (const sk of subSkills) {
|
|
240
|
+
const cmdList = sk.matched.length > 0
|
|
241
|
+
? sk.matched.slice(0, 5).map(c => `\`${c.name}\``).join(', ')
|
|
242
|
+
: sk.summary.split(',').slice(0, 3).map(s => `\`${s.trim()}\``).join(', ');
|
|
243
|
+
const refLink = sk.ref
|
|
244
|
+
? `[${sk.ref}](references/${sk.ref}.md)`
|
|
245
|
+
: sk.title;
|
|
246
|
+
w(`| ${sk.title} | ${cmdList} | ${refLink} |`);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
w();
|
|
250
|
+
w('### Core Workflow Pattern');
|
|
251
|
+
w();
|
|
252
|
+
w('1. `open <url>` → navigate');
|
|
253
|
+
w('2. `snapshot -i` → get element refs (`@e1`, `@e2`, ...)');
|
|
254
|
+
w('3. `fill` / `click` / `select` → interact using refs');
|
|
255
|
+
w('4. Re-`snapshot` after any page change (refs are invalidated)');
|
|
256
|
+
w();
|
|
257
|
+
w('### Refs');
|
|
258
|
+
w();
|
|
259
|
+
w('Refs (`@e1`, `@e2`) are **short-lived** — invalidated by any page change. Always re-snapshot after navigation or DOM mutations. See [snapshot-refs.md](references/snapshot-refs.md).');
|
|
260
|
+
w();
|
|
261
|
+
w('### Iframes');
|
|
262
|
+
w();
|
|
263
|
+
w('```bash');
|
|
264
|
+
w('agent-browser snapshot --in-frame "#my-iframe" # Single iframe');
|
|
265
|
+
w('agent-browser click @e1 --in-frame "#outer/inner" # Nested');
|
|
266
|
+
w('```');
|
|
267
|
+
w();
|
|
268
|
+
w('### Semantic Locators (No Refs Needed)');
|
|
269
|
+
w();
|
|
270
|
+
w('```bash');
|
|
271
|
+
w('agent-browser find text "Sign In" click');
|
|
272
|
+
w('agent-browser find label "Email" fill "user@test.com"');
|
|
273
|
+
w('agent-browser find role button click --name "Submit"');
|
|
274
|
+
w('```');
|
|
275
|
+
w();
|
|
276
|
+
w('## Key Flags');
|
|
277
|
+
w();
|
|
278
|
+
for (const opt of opts) {
|
|
279
|
+
w(`- \`${opt.flag}\` — ${opt.desc}`);
|
|
280
|
+
}
|
|
281
|
+
w();
|
|
282
|
+
w('## Environment Variables');
|
|
283
|
+
w();
|
|
284
|
+
for (const ev of envVars) {
|
|
285
|
+
w(`- \`${ev.name}\` — ${ev.desc}`);
|
|
286
|
+
}
|
|
287
|
+
w();
|
|
288
|
+
w('## Reference Docs');
|
|
289
|
+
w();
|
|
290
|
+
w('| Doc | Content |');
|
|
291
|
+
w('|-----|---------|');
|
|
292
|
+
for (const r of refTableRows) {
|
|
293
|
+
w(`| [${r.label}](references/${r.file}) | ${r.desc} |`);
|
|
294
|
+
}
|
|
295
|
+
w();
|
|
296
|
+
|
|
297
|
+
const outPath = path.join(SKILL_DIR, 'SKILL.md');
|
|
298
|
+
fs.writeFileSync(outPath, output.join('\n'));
|
|
299
|
+
console.log(`\nGenerated: ${outPath} (${output.length} lines)`);
|
|
300
|
+
console.log('\nSummary:');
|
|
301
|
+
for (const sk of subSkills) {
|
|
302
|
+
console.log(` ${sk.title}: ${sk.matched.length} commands -> ${sk.ref || '(inline)'}`);
|
|
303
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
(() => {
|
|
2
|
+
const reviews = {};
|
|
3
|
+
|
|
4
|
+
const goodEl = document.querySelector('.filter-items a[data-tab="good"]');
|
|
5
|
+
const neutralEl = document.querySelector('.filter-items a[data-tab="neutral"]');
|
|
6
|
+
const badEl = document.querySelector('.filter-items a[data-tab="bad"]');
|
|
7
|
+
|
|
8
|
+
reviews.good = goodEl?.textContent?.trim() || '';
|
|
9
|
+
reviews.neutral = neutralEl?.textContent?.trim() || '';
|
|
10
|
+
reviews.bad = badEl?.textContent?.trim() || '';
|
|
11
|
+
|
|
12
|
+
const regionEl = document.querySelector('.address-item');
|
|
13
|
+
const region = regionEl?.textContent?.trim() || '';
|
|
14
|
+
|
|
15
|
+
return { reviews, region };
|
|
16
|
+
})()
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
(() => {
|
|
2
|
+
const items = document.querySelectorAll('.gl-item');
|
|
3
|
+
const products = [];
|
|
4
|
+
|
|
5
|
+
items.forEach((item, index) => {
|
|
6
|
+
const titleEl = item.querySelector('.p-name a');
|
|
7
|
+
const priceEl = item.querySelector('.p-price');
|
|
8
|
+
const shopEl = item.querySelector('.p-shop a');
|
|
9
|
+
const salesEl = item.querySelector('.p-commit');
|
|
10
|
+
const linkEl = item.querySelector('.p-name a');
|
|
11
|
+
|
|
12
|
+
products.push({
|
|
13
|
+
index: index,
|
|
14
|
+
title: titleEl?.textContent?.trim() || '',
|
|
15
|
+
price: priceEl?.textContent?.trim() || '',
|
|
16
|
+
shop: shopEl?.textContent?.trim() || '',
|
|
17
|
+
sales: salesEl?.textContent?.trim() || '',
|
|
18
|
+
link: linkEl?.href || ''
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
return products.slice(0, 10);
|
|
23
|
+
})()
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
(async () => {
|
|
2
|
+
const result = {
|
|
3
|
+
products: []
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
window.scrollTo(0, 1000);
|
|
7
|
+
await new Promise(resolve => setTimeout(resolve, 3000));
|
|
8
|
+
|
|
9
|
+
window.scrollTo(0, 3000);
|
|
10
|
+
await new Promise(resolve => setTimeout(resolve, 3000));
|
|
11
|
+
|
|
12
|
+
const productCards = document.querySelectorAll('[data-point-id], .youLikeItem, [class*="item"]');
|
|
13
|
+
|
|
14
|
+
productCards.forEach((card, index) => {
|
|
15
|
+
if (index >= 15) return;
|
|
16
|
+
|
|
17
|
+
const product = {
|
|
18
|
+
index,
|
|
19
|
+
className: card.className,
|
|
20
|
+
title: '',
|
|
21
|
+
price: '',
|
|
22
|
+
shop: '',
|
|
23
|
+
link: ''
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const titleEl = card.querySelector('[class*="title"], [class*="name"], h3, h4, h5');
|
|
27
|
+
const priceEl = card.querySelector('[class*="price"], .price');
|
|
28
|
+
const shopEl = card.querySelector('[class*="shop"], [class*="store"], [class*="brand"]');
|
|
29
|
+
const linkEl = card.querySelector('a[href*="jd"]');
|
|
30
|
+
const imgEl = card.querySelector('img');
|
|
31
|
+
|
|
32
|
+
if (titleEl) product.title = titleEl.textContent?.trim() || '';
|
|
33
|
+
if (priceEl) product.price = priceEl.textContent?.trim() || '';
|
|
34
|
+
if (shopEl) product.shop = shopEl.textContent?.trim() || '';
|
|
35
|
+
if (linkEl) product.link = linkEl.href || '';
|
|
36
|
+
if (imgEl && imgEl.src) product.image = imgEl.src.substring(0, 100);
|
|
37
|
+
|
|
38
|
+
if (product.title || product.price || product.link) {
|
|
39
|
+
result.products.push(product);
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
return result;
|
|
44
|
+
})()
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
(async () => {
|
|
2
|
+
const result = {
|
|
3
|
+
products: []
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
const wrappers = document.querySelectorAll('._wrapper_8g5fc_1');
|
|
7
|
+
|
|
8
|
+
wrappers.forEach((wrapper, index) => {
|
|
9
|
+
if (index >= 10) return;
|
|
10
|
+
|
|
11
|
+
const product = {
|
|
12
|
+
index,
|
|
13
|
+
className: wrapper.className
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const img = wrapper.querySelector('img');
|
|
17
|
+
if (img) {
|
|
18
|
+
product.img = img.getAttribute('data-src')?.substring(0, 100) || img.src?.substring(0, 100);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const parentLink = wrapper.closest('a');
|
|
22
|
+
if (parentLink) {
|
|
23
|
+
product.link = parentLink.href;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const allText = wrapper.textContent?.trim() || '';
|
|
27
|
+
product.text = allText.substring(0, 150);
|
|
28
|
+
|
|
29
|
+
const priceElements = wrapper.querySelectorAll('*');
|
|
30
|
+
for (const el of priceElements) {
|
|
31
|
+
const text = el.textContent?.trim() || '';
|
|
32
|
+
if (text.match(/[¥¥]\s*\d+/)) {
|
|
33
|
+
product.price = text;
|
|
34
|
+
break;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const greatGrandParent = wrapper.parentElement?.parentElement?.parentElement;
|
|
39
|
+
if (greatGrandParent) {
|
|
40
|
+
product.greatGrandParentClass = greatGrandParent.className?.substring(0, 100);
|
|
41
|
+
const greatGrandParentText = greatGrandParent.textContent?.trim() || '';
|
|
42
|
+
product.greatGrandParentText = greatGrandParentText.substring(0, 200);
|
|
43
|
+
|
|
44
|
+
const greatGrandParentLink = greatGrandParent.querySelector('a[href*="jd"]');
|
|
45
|
+
if (greatGrandParentLink) {
|
|
46
|
+
product.link = greatGrandParentLink.href;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
result.products.push(product);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
return result;
|
|
54
|
+
})()
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# 场景2: 表单填写验证脚本
|
|
3
|
+
# 从 form-test.yaml 生成的脚本
|
|
4
|
+
|
|
5
|
+
set -e # 遇到错误立即退出
|
|
6
|
+
|
|
7
|
+
# 启用人工模拟模式
|
|
8
|
+
export AGENT_BROWSER_HUMAN=bezier
|
|
9
|
+
SESSION="form-verify-$(date +%s)"
|
|
10
|
+
|
|
11
|
+
# 测试页面路径
|
|
12
|
+
TEST_PAGE="file:///Users/xuyingzhou/Project/temporary/agent-browser/test-pages/form-test.html"
|
|
13
|
+
|
|
14
|
+
echo "=== 开始执行表单填写脚本 ==="
|
|
15
|
+
echo "Session: $SESSION"
|
|
16
|
+
echo "AGENT_BROWSER_HUMAN: $AGENT_BROWSER_HUMAN"
|
|
17
|
+
|
|
18
|
+
# 打开测试页面
|
|
19
|
+
echo "1. 打开表单测试页面..."
|
|
20
|
+
agent-browser --session $SESSION --allow-file-access --headed open "$TEST_PAGE"
|
|
21
|
+
agent-browser --session $SESSION wait --load networkidle
|
|
22
|
+
|
|
23
|
+
# 填写表单
|
|
24
|
+
echo "2. 填写表单..."
|
|
25
|
+
agent-browser --session $SESSION fill "#name" "测试用户"
|
|
26
|
+
agent-browser --session $SESSION fill "#email" "test@example.com"
|
|
27
|
+
agent-browser --session $SESSION fill "#password" "password123"
|
|
28
|
+
agent-browser --session $SESSION select "#country" "cn"
|
|
29
|
+
agent-browser --session $SESSION check "#agree"
|
|
30
|
+
|
|
31
|
+
# 提交表单
|
|
32
|
+
echo "3. 提交表单..."
|
|
33
|
+
agent-browser --session $SESSION click "#submitBtn"
|
|
34
|
+
|
|
35
|
+
# 等待结果显示
|
|
36
|
+
agent-browser --session $SESSION wait 500
|
|
37
|
+
|
|
38
|
+
# 验证结果
|
|
39
|
+
echo "4. 验证结果..."
|
|
40
|
+
URL=$(agent-browser --session $SESSION get url)
|
|
41
|
+
echo "当前URL: $URL"
|
|
42
|
+
|
|
43
|
+
# 检查URL是否正确
|
|
44
|
+
if [[ "$URL" == *"form-test.html"* ]]; then
|
|
45
|
+
echo "✓ URL验证成功"
|
|
46
|
+
else
|
|
47
|
+
echo "✗ URL验证失败"
|
|
48
|
+
agent-browser --session $SESSION close
|
|
49
|
+
exit 1
|
|
50
|
+
fi
|
|
51
|
+
|
|
52
|
+
# 检查表单数据是否显示
|
|
53
|
+
RESULT=$(agent-browser --session $SESSION get text "#result")
|
|
54
|
+
if [[ "$RESULT" == *"测试用户"* ]]; then
|
|
55
|
+
echo "✓ 表单数据验证成功"
|
|
56
|
+
else
|
|
57
|
+
echo "✗ 表单数据验证失败"
|
|
58
|
+
agent-browser --session $SESSION close
|
|
59
|
+
exit 1
|
|
60
|
+
fi
|
|
61
|
+
|
|
62
|
+
# 清理
|
|
63
|
+
echo "5. 关闭浏览器..."
|
|
64
|
+
agent-browser --session $SESSION close
|
|
65
|
+
|
|
66
|
+
echo "=== 脚本执行成功 ==="
|
|
67
|
+
exit 0
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# 场景8: 登录功能验证脚本(含验证码识别)
|
|
3
|
+
|
|
4
|
+
set -e
|
|
5
|
+
export AGENT_BROWSER_HUMAN=bezier
|
|
6
|
+
SESSION="login-verify-$(date +%s)"
|
|
7
|
+
TEST_PAGE="file:///Users/xuyingzhou/Project/temporary/agent-browser/test-pages/login-test.html"
|
|
8
|
+
|
|
9
|
+
echo "=== 开始执行登录验证脚本 ==="
|
|
10
|
+
echo "Session: $SESSION"
|
|
11
|
+
|
|
12
|
+
# 打开测试页面
|
|
13
|
+
echo "1. 打开登录测试页面..."
|
|
14
|
+
agent-browser --session $SESSION --allow-file-access --headed open "$TEST_PAGE"
|
|
15
|
+
agent-browser --session $SESSION wait --load networkidle
|
|
16
|
+
|
|
17
|
+
# 检查未登录标识
|
|
18
|
+
echo "2. 检查未登录标识..."
|
|
19
|
+
UNLOGGED=$(agent-browser --session $SESSION get text "#loggedOutIndicator")
|
|
20
|
+
if [[ "$UNLOGGED" == *"尚未登录"* ]]; then
|
|
21
|
+
echo "✓ 检测到未登录标识"
|
|
22
|
+
else
|
|
23
|
+
echo "✗ 未检测到未登录标识"
|
|
24
|
+
fi
|
|
25
|
+
|
|
26
|
+
# 获取验证码并立即填写(使用链式命令减少延迟)
|
|
27
|
+
echo "3. 获取验证码并填写..."
|
|
28
|
+
agent-browser --session $SESSION eval --stdin <<'EOF'
|
|
29
|
+
const captcha = document.getElementById("captchaCode").textContent;
|
|
30
|
+
document.getElementById("username").value = "testuser";
|
|
31
|
+
document.getElementById("password").value = "password123";
|
|
32
|
+
document.getElementById("captcha").value = captcha;
|
|
33
|
+
"验证码: " + captcha;
|
|
34
|
+
EOF
|
|
35
|
+
|
|
36
|
+
echo "4. 提交登录..."
|
|
37
|
+
agent-browser --session $SESSION click "#loginBtn"
|
|
38
|
+
agent-browser --session $SESSION wait 500
|
|
39
|
+
|
|
40
|
+
# 验证登录结果
|
|
41
|
+
echo "6. 验证登录结果..."
|
|
42
|
+
STATUS=$(agent-browser --session $SESSION get text "#loginStatus")
|
|
43
|
+
if [[ "$STATUS" == *"登录成功"* ]]; then
|
|
44
|
+
echo "✓ 登录成功"
|
|
45
|
+
else
|
|
46
|
+
echo "✗ 登录失败: $STATUS"
|
|
47
|
+
agent-browser --session $SESSION close
|
|
48
|
+
exit 1
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
# 检查用户信息显示
|
|
52
|
+
echo "7. 检查用户信息..."
|
|
53
|
+
USERINFO=$(agent-browser --session $SESSION get text "#userInfo")
|
|
54
|
+
if [[ "$USERINFO" == *"testuser"* ]]; then
|
|
55
|
+
echo "✓ 用户信息显示正确"
|
|
56
|
+
else
|
|
57
|
+
echo "✗ 用户信息显示错误"
|
|
58
|
+
fi
|
|
59
|
+
|
|
60
|
+
# 清理
|
|
61
|
+
echo "8. 关闭浏览器..."
|
|
62
|
+
agent-browser --session $SESSION close
|
|
63
|
+
|
|
64
|
+
echo "=== 脚本执行成功 ==="
|
|
65
|
+
exit 0
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# 从 complex-scene-5.yaml 生成的验证脚本
|
|
3
|
+
# 用于验证录制转脚本的可靠性
|
|
4
|
+
|
|
5
|
+
set -e # 遇到错误立即退出
|
|
6
|
+
|
|
7
|
+
export AGENT_BROWSER_HUMAN=bezier
|
|
8
|
+
SESSION="verify-$(date +%s)"
|
|
9
|
+
|
|
10
|
+
# 测试页面路径
|
|
11
|
+
TEST_PAGE="file:///Users/xuyingzhou/Project/temporary/agent-browser/test-pages/complex-scene-5.html"
|
|
12
|
+
|
|
13
|
+
echo "=== 开始执行脚本 ==="
|
|
14
|
+
echo "Session: $SESSION"
|
|
15
|
+
|
|
16
|
+
# 打开测试页面
|
|
17
|
+
echo "1. 打开测试页面..."
|
|
18
|
+
agent-browser --session $SESSION --allow-file-access --headed open "$TEST_PAGE"
|
|
19
|
+
agent-browser --session $SESSION wait --load networkidle
|
|
20
|
+
|
|
21
|
+
# 执行表单填写
|
|
22
|
+
echo "2. 填写表单..."
|
|
23
|
+
agent-browser --session $SESSION fill "#username" "测试用户001"
|
|
24
|
+
agent-browser --session $SESSION fill "#email" "test@example.com"
|
|
25
|
+
agent-browser --session $SESSION fill "#comments" "这是一个自动化测试备注信息"
|
|
26
|
+
|
|
27
|
+
# 点击按钮
|
|
28
|
+
echo "3. 点击按钮..."
|
|
29
|
+
agent-browser --session $SESSION click "#btn-primary"
|
|
30
|
+
agent-browser --session $SESSION click "#btn-secondary"
|
|
31
|
+
agent-browser --session $SESSION click "#btn-success"
|
|
32
|
+
agent-browser --session $SESSION click "#btn-danger"
|
|
33
|
+
|
|
34
|
+
# 下拉选择
|
|
35
|
+
echo "4. 选择下拉选项..."
|
|
36
|
+
agent-browser --session $SESSION select "#country" "cn"
|
|
37
|
+
agent-browser --session $SESSION select "#city" "北京"
|
|
38
|
+
|
|
39
|
+
# 复选框
|
|
40
|
+
echo "5. 勾选复选框..."
|
|
41
|
+
agent-browser --session $SESSION check "#chk1"
|
|
42
|
+
agent-browser --session $SESSION check "#chk2"
|
|
43
|
+
agent-browser --session $SESSION check "#chk4"
|
|
44
|
+
|
|
45
|
+
# 单选框
|
|
46
|
+
echo "6. 选择单选项..."
|
|
47
|
+
agent-browser --session $SESSION click "#rd1"
|
|
48
|
+
|
|
49
|
+
# 计数器
|
|
50
|
+
echo "7. 点击计数器..."
|
|
51
|
+
agent-browser --session $SESSION click "#counter-increase"
|
|
52
|
+
|
|
53
|
+
# 开关
|
|
54
|
+
echo "8. 切换开关..."
|
|
55
|
+
agent-browser --session $SESSION click "#dark-mode-toggle"
|
|
56
|
+
|
|
57
|
+
# 收集数据
|
|
58
|
+
echo "9. 点击收集数据..."
|
|
59
|
+
agent-browser --session $SESSION click "#collect-data"
|
|
60
|
+
|
|
61
|
+
# 验证结果
|
|
62
|
+
echo "10. 验证结果..."
|
|
63
|
+
URL=$(agent-browser --session $SESSION get url)
|
|
64
|
+
echo "当前URL: $URL"
|
|
65
|
+
|
|
66
|
+
# 检查URL是否正确
|
|
67
|
+
if [[ "$URL" == *"complex-scene-5.html"* ]]; then
|
|
68
|
+
echo "✓ URL验证成功"
|
|
69
|
+
else
|
|
70
|
+
echo "✗ URL验证失败"
|
|
71
|
+
agent-browser --session $SESSION close
|
|
72
|
+
exit 1
|
|
73
|
+
fi
|
|
74
|
+
|
|
75
|
+
# 清理
|
|
76
|
+
echo "11. 关闭浏览器..."
|
|
77
|
+
agent-browser --session $SESSION close
|
|
78
|
+
|
|
79
|
+
echo "=== 脚本执行成功 ==="
|
|
80
|
+
exit 0
|