@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.
Files changed (187) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +907 -0
  3. package/bin/agent-browser-darwin-arm64 +0 -0
  4. package/bin/agent-browser.js +120 -0
  5. package/dist/__tests__/e2e/utils/test-helpers.d.ts +5 -0
  6. package/dist/__tests__/e2e/utils/test-helpers.d.ts.map +1 -0
  7. package/dist/__tests__/e2e/utils/test-helpers.js +22 -0
  8. package/dist/__tests__/e2e/utils/test-helpers.js.map +1 -0
  9. package/dist/__tests__/test-iframe.d.ts +2 -0
  10. package/dist/__tests__/test-iframe.d.ts.map +1 -0
  11. package/dist/__tests__/test-iframe.js +52 -0
  12. package/dist/__tests__/test-iframe.js.map +1 -0
  13. package/dist/__tests__/utils/parseCli.d.ts +20 -0
  14. package/dist/__tests__/utils/parseCli.d.ts.map +1 -0
  15. package/dist/__tests__/utils/parseCli.js +1086 -0
  16. package/dist/__tests__/utils/parseCli.js.map +1 -0
  17. package/dist/actions.d.ts +50 -0
  18. package/dist/actions.d.ts.map +1 -0
  19. package/dist/actions.js +2164 -0
  20. package/dist/actions.js.map +1 -0
  21. package/dist/browser.d.ts +556 -0
  22. package/dist/browser.d.ts.map +1 -0
  23. package/dist/browser.js +2599 -0
  24. package/dist/browser.js.map +1 -0
  25. package/dist/cli/commands.d.ts +8 -0
  26. package/dist/cli/commands.d.ts.map +1 -0
  27. package/dist/cli/commands.js +1038 -0
  28. package/dist/cli/commands.js.map +1 -0
  29. package/dist/cli/connection.d.ts +50 -0
  30. package/dist/cli/connection.d.ts.map +1 -0
  31. package/dist/cli/connection.js +595 -0
  32. package/dist/cli/connection.js.map +1 -0
  33. package/dist/cli/flags.d.ts +36 -0
  34. package/dist/cli/flags.d.ts.map +1 -0
  35. package/dist/cli/flags.js +206 -0
  36. package/dist/cli/flags.js.map +1 -0
  37. package/dist/cli/help.d.ts +4 -0
  38. package/dist/cli/help.d.ts.map +1 -0
  39. package/dist/cli/help.js +1024 -0
  40. package/dist/cli/help.js.map +1 -0
  41. package/dist/cli/output.d.ts +14 -0
  42. package/dist/cli/output.d.ts.map +1 -0
  43. package/dist/cli/output.js +456 -0
  44. package/dist/cli/output.js.map +1 -0
  45. package/dist/cli-new.d.ts +3 -0
  46. package/dist/cli-new.d.ts.map +1 -0
  47. package/dist/cli-new.js +308 -0
  48. package/dist/cli-new.js.map +1 -0
  49. package/dist/cli-old.d.ts +3 -0
  50. package/dist/cli-old.d.ts.map +1 -0
  51. package/dist/cli-old.js +1101 -0
  52. package/dist/cli-old.js.map +1 -0
  53. package/dist/cli.d.ts +3 -0
  54. package/dist/cli.d.ts.map +1 -0
  55. package/dist/cli.js +403 -0
  56. package/dist/cli.js.map +1 -0
  57. package/dist/content-detection.d.ts +18 -0
  58. package/dist/content-detection.d.ts.map +1 -0
  59. package/dist/content-detection.js +68 -0
  60. package/dist/content-detection.js.map +1 -0
  61. package/dist/daemon.d.ts +55 -0
  62. package/dist/daemon.d.ts.map +1 -0
  63. package/dist/daemon.js +426 -0
  64. package/dist/daemon.js.map +1 -0
  65. package/dist/diff.d.ts +42 -0
  66. package/dist/diff.d.ts.map +1 -0
  67. package/dist/diff.js +166 -0
  68. package/dist/diff.js.map +1 -0
  69. package/dist/human-mouse.d.ts +31 -0
  70. package/dist/human-mouse.d.ts.map +1 -0
  71. package/dist/human-mouse.js +184 -0
  72. package/dist/human-mouse.js.map +1 -0
  73. package/dist/ios-actions.d.ts +11 -0
  74. package/dist/ios-actions.d.ts.map +1 -0
  75. package/dist/ios-actions.js +228 -0
  76. package/dist/ios-actions.js.map +1 -0
  77. package/dist/ios-manager.d.ts +266 -0
  78. package/dist/ios-manager.d.ts.map +1 -0
  79. package/dist/ios-manager.js +1076 -0
  80. package/dist/ios-manager.js.map +1 -0
  81. package/dist/message-bridge.d.ts +10 -0
  82. package/dist/message-bridge.d.ts.map +1 -0
  83. package/dist/message-bridge.js +60 -0
  84. package/dist/message-bridge.js.map +1 -0
  85. package/dist/protocol.d.ts +26 -0
  86. package/dist/protocol.d.ts.map +1 -0
  87. package/dist/protocol.js +912 -0
  88. package/dist/protocol.js.map +1 -0
  89. package/dist/recorder/binding.d.ts +24 -0
  90. package/dist/recorder/binding.d.ts.map +1 -0
  91. package/dist/recorder/binding.js +215 -0
  92. package/dist/recorder/binding.js.map +1 -0
  93. package/dist/recorder/index.d.ts +4 -0
  94. package/dist/recorder/index.d.ts.map +1 -0
  95. package/dist/recorder/index.js +4 -0
  96. package/dist/recorder/index.js.map +1 -0
  97. package/dist/recorder/inject.js +1913 -0
  98. package/dist/recorder/recorder.d.ts +19 -0
  99. package/dist/recorder/recorder.d.ts.map +1 -0
  100. package/dist/recorder/recorder.js +101 -0
  101. package/dist/recorder/recorder.js.map +1 -0
  102. package/dist/recorder/store.d.ts +22 -0
  103. package/dist/recorder/store.d.ts.map +1 -0
  104. package/dist/recorder/store.js +150 -0
  105. package/dist/recorder/store.js.map +1 -0
  106. package/dist/recorder/types.d.ts +73 -0
  107. package/dist/recorder/types.d.ts.map +1 -0
  108. package/dist/recorder/types.js +5 -0
  109. package/dist/recorder/types.js.map +1 -0
  110. package/dist/snapshot.d.ts +81 -0
  111. package/dist/snapshot.d.ts.map +1 -0
  112. package/dist/snapshot.js +1348 -0
  113. package/dist/snapshot.js.map +1 -0
  114. package/dist/stream-server-standalone.d.ts +38 -0
  115. package/dist/stream-server-standalone.d.ts.map +1 -0
  116. package/dist/stream-server-standalone.js +494 -0
  117. package/dist/stream-server-standalone.js.map +1 -0
  118. package/dist/stream-server.d.ts +214 -0
  119. package/dist/stream-server.d.ts.map +1 -0
  120. package/dist/stream-server.js +811 -0
  121. package/dist/stream-server.js.map +1 -0
  122. package/dist/types.d.ts +914 -0
  123. package/dist/types.d.ts.map +1 -0
  124. package/dist/types.js +4 -0
  125. package/dist/types.js.map +1 -0
  126. package/dist/viewer-html.d.ts +2 -0
  127. package/dist/viewer-html.d.ts.map +1 -0
  128. package/dist/viewer-html.js +185 -0
  129. package/dist/viewer-html.js.map +1 -0
  130. package/dist/viewer-script.d.ts +47 -0
  131. package/dist/viewer-script.d.ts.map +1 -0
  132. package/dist/viewer-script.js +586 -0
  133. package/dist/viewer-script.js.map +1 -0
  134. package/package.json +86 -0
  135. package/scripts/build-all-platforms.sh +68 -0
  136. package/scripts/check-version-sync.js +39 -0
  137. package/scripts/check_goods_container.js +35 -0
  138. package/scripts/check_page_content.js +36 -0
  139. package/scripts/click_applause_rate.js +30 -0
  140. package/scripts/copy-native.js +36 -0
  141. package/scripts/copy-recorder.js +21 -0
  142. package/scripts/e2e-test-recorder.ts +584 -0
  143. package/scripts/explore_jd_page.js +31 -0
  144. package/scripts/extract_all_jd_data.js +80 -0
  145. package/scripts/extract_jd_product_detail.js +62 -0
  146. package/scripts/extract_jd_products_correct_links.js +78 -0
  147. package/scripts/extract_jd_products_final.js +80 -0
  148. package/scripts/extract_jd_reviews.js +48 -0
  149. package/scripts/extract_jd_seafood_final.js +78 -0
  150. package/scripts/extract_multiple_products.js +77 -0
  151. package/scripts/extract_products_no_scroll.js +68 -0
  152. package/scripts/extract_products_simple.js +68 -0
  153. package/scripts/find_applause_rate.js +26 -0
  154. package/scripts/find_jd_links.js +28 -0
  155. package/scripts/find_main_content.js +20 -0
  156. package/scripts/find_product_cards.js +38 -0
  157. package/scripts/find_root_content.js +26 -0
  158. package/scripts/find_unique_products.js +55 -0
  159. package/scripts/get_jd_product_detail.js +16 -0
  160. package/scripts/get_jd_products.js +23 -0
  161. package/scripts/get_jd_seafood_products.js +44 -0
  162. package/scripts/get_product_details_from_images.js +54 -0
  163. package/scripts/postinstall.js +235 -0
  164. package/scripts/scroll_and_get_products.js +47 -0
  165. package/scripts/scroll_deep_and_find.js +45 -0
  166. package/scripts/sync-version.js +69 -0
  167. package/scripts/verify-baidu-enter.ts +116 -0
  168. package/skills/agent-browser/SKILL.md +310 -0
  169. package/skills/agent-browser/references/authentication.md +198 -0
  170. package/skills/agent-browser/references/commands.md +471 -0
  171. package/skills/agent-browser/references/data-extraction.md +377 -0
  172. package/skills/agent-browser/references/proxy-support.md +188 -0
  173. package/skills/agent-browser/references/session-management.md +197 -0
  174. package/skills/agent-browser/references/snapshot-refs.md +379 -0
  175. package/skills/agent-browser/references/video-recording.md +173 -0
  176. package/skills/agent-browser/templates/api-interception.sh +53 -0
  177. package/skills/agent-browser/templates/authenticated-session.sh +97 -0
  178. package/skills/agent-browser/templates/capture-workflow.sh +69 -0
  179. package/skills/agent-browser/templates/data-extraction.sh +210 -0
  180. package/skills/agent-browser/templates/form-automation.sh +62 -0
  181. package/skills/skill-creator/LICENSE.txt +202 -0
  182. package/skills/skill-creator/SKILL.md +356 -0
  183. package/skills/skill-creator/references/output-patterns.md +82 -0
  184. package/skills/skill-creator/references/workflows.md +28 -0
  185. package/skills/skill-creator/scripts/init_skill.py +303 -0
  186. package/skills/skill-creator/scripts/package_skill.py +113 -0
  187. package/skills/skill-creator/scripts/quick_validate.py +95 -0
@@ -0,0 +1,26 @@
1
+ (() => {
2
+ const root = document.getElementById('root');
3
+
4
+ if (!root) {
5
+ return { error: 'root not found' };
6
+ }
7
+
8
+ const result = {
9
+ rootId: root.id,
10
+ childrenCount: root.children.length,
11
+ children: []
12
+ };
13
+
14
+ for (let i = 0; i < Math.min(root.children.length, 20); i++) {
15
+ const child = root.children[i];
16
+ result.children.push({
17
+ index: i,
18
+ tagName: child.tagName,
19
+ id: child.id,
20
+ className: child.className,
21
+ innerHTML: child.innerHTML.substring(0, 300)
22
+ });
23
+ }
24
+
25
+ return result;
26
+ })()
@@ -0,0 +1,55 @@
1
+ (async () => {
2
+ const result = {
3
+ products: []
4
+ };
5
+
6
+ const containers = document.querySelectorAll('._goodsContainer_1p2ae_1');
7
+
8
+ containers.forEach((container, index) => {
9
+ if (index >= 10) return;
10
+
11
+ const product = {
12
+ index,
13
+ className: container.className,
14
+ text: container.textContent?.trim().substring(0, 300)
15
+ };
16
+
17
+ const link = container.querySelector('a[href*="jd"]');
18
+ if (link) {
19
+ product.link = link.href;
20
+ }
21
+
22
+ const img = container.querySelector('img');
23
+ if (img) {
24
+ product.img = img.getAttribute('data-src')?.substring(0, 100) || img.src?.substring(0, 100);
25
+ }
26
+
27
+ const text = container.textContent || '';
28
+
29
+ const priceMatch = text.match(/[¥¥]\s*\d+\.?\d*/);
30
+ if (priceMatch) {
31
+ product.price = priceMatch[0];
32
+ }
33
+
34
+ const salesMatch = text.match(/已售[\d\+万]+/);
35
+ if (salesMatch) {
36
+ product.sales = salesMatch[0];
37
+ }
38
+
39
+ const shopMatch = text.match(/([^\s]+旗舰店|[^\s]+专营店)/);
40
+ if (shopMatch) {
41
+ product.shop = shopMatch[0];
42
+ }
43
+
44
+ const titleMatch = text.match(/([^\s]{5,50})/);
45
+ if (titleMatch && !titleMatch[0].includes('广告') && !titleMatch[0].includes('旗舰店')) {
46
+ product.title = titleMatch[0];
47
+ }
48
+
49
+ if (product.price || product.shop || product.sales) {
50
+ result.products.push(product);
51
+ }
52
+ });
53
+
54
+ return result;
55
+ })()
@@ -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,235 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Postinstall script for agent-browser
5
+ *
6
+ * Downloads the platform-specific native binary if not present.
7
+ * On global installs, patches npm's bin entry to use the native binary directly:
8
+ * - Windows: Overwrites .cmd/.ps1 shims
9
+ * - Mac/Linux: Replaces symlink to point to native binary
10
+ */
11
+
12
+ import { existsSync, mkdirSync, chmodSync, createWriteStream, unlinkSync, writeFileSync, symlinkSync, lstatSync } from 'fs';
13
+ import { dirname, join } from 'path';
14
+ import { fileURLToPath } from 'url';
15
+ import { platform, arch } from 'os';
16
+ import { get } from 'https';
17
+ import { execSync } from 'child_process';
18
+
19
+ const __dirname = dirname(fileURLToPath(import.meta.url));
20
+ const projectRoot = join(__dirname, '..');
21
+ const binDir = join(projectRoot, 'bin');
22
+
23
+ // Platform detection
24
+ const platformKey = `${platform()}-${arch()}`;
25
+ const ext = platform() === 'win32' ? '.exe' : '';
26
+ const binaryName = `agent-browser-${platformKey}${ext}`;
27
+ const binaryPath = join(binDir, binaryName);
28
+
29
+ // Package info
30
+ const packageJson = JSON.parse(
31
+ (await import('fs')).readFileSync(join(projectRoot, 'package.json'), 'utf8')
32
+ );
33
+ const version = packageJson.version;
34
+
35
+ // GitHub release URL
36
+ const GITHUB_REPO = 'vercel-labs/agent-browser';
37
+ const DOWNLOAD_URL = `https://github.com/${GITHUB_REPO}/releases/download/v${version}/${binaryName}`;
38
+
39
+ async function downloadFile(url, dest) {
40
+ return new Promise((resolve, reject) => {
41
+ const file = createWriteStream(dest);
42
+
43
+ const request = (url) => {
44
+ get(url, (response) => {
45
+ // Handle redirects
46
+ if (response.statusCode === 301 || response.statusCode === 302) {
47
+ request(response.headers.location);
48
+ return;
49
+ }
50
+
51
+ if (response.statusCode !== 200) {
52
+ reject(new Error(`Failed to download: HTTP ${response.statusCode}`));
53
+ return;
54
+ }
55
+
56
+ response.pipe(file);
57
+ file.on('finish', () => {
58
+ file.close();
59
+ resolve();
60
+ });
61
+ }).on('error', (err) => {
62
+ unlinkSync(dest);
63
+ reject(err);
64
+ });
65
+ };
66
+
67
+ request(url);
68
+ });
69
+ }
70
+
71
+ async function main() {
72
+ // Check if binary already exists
73
+ if (existsSync(binaryPath)) {
74
+ // Ensure binary is executable (npm doesn't preserve execute bit)
75
+ if (platform() !== 'win32') {
76
+ chmodSync(binaryPath, 0o755);
77
+ }
78
+ console.log(`✓ Native binary ready: ${binaryName}`);
79
+
80
+ // On global installs, fix npm's bin entry to use native binary directly
81
+ await fixGlobalInstallBin();
82
+
83
+ showPlaywrightReminder();
84
+ return;
85
+ }
86
+
87
+ // Ensure bin directory exists
88
+ if (!existsSync(binDir)) {
89
+ mkdirSync(binDir, { recursive: true });
90
+ }
91
+
92
+ console.log(`Downloading native binary for ${platformKey}...`);
93
+ console.log(`URL: ${DOWNLOAD_URL}`);
94
+
95
+ try {
96
+ await downloadFile(DOWNLOAD_URL, binaryPath);
97
+
98
+ // Make executable on Unix
99
+ if (platform() !== 'win32') {
100
+ chmodSync(binaryPath, 0o755);
101
+ }
102
+
103
+ console.log(`✓ Downloaded native binary: ${binaryName}`);
104
+ } catch (err) {
105
+ console.log(`⚠ Could not download native binary: ${err.message}`);
106
+ console.log(` The CLI will use Node.js fallback (slightly slower startup)`);
107
+ console.log('');
108
+ console.log('To build the native binary locally:');
109
+ console.log(' 1. Install Rust: https://rustup.rs');
110
+ console.log(' 2. Run: npm run build:native');
111
+ }
112
+
113
+ // On global installs, fix npm's bin entry to use native binary directly
114
+ // This avoids the /bin/sh error on Windows and provides zero-overhead execution
115
+ await fixGlobalInstallBin();
116
+
117
+ showPlaywrightReminder();
118
+ }
119
+
120
+ function showPlaywrightReminder() {
121
+ console.log('');
122
+ console.log('╔═══════════════════════════════════════════════════════════════════════════╗');
123
+ console.log('║ To download browser binaries, run: ║');
124
+ console.log('║ ║');
125
+ console.log('║ npx playwright install chromium ║');
126
+ console.log('║ ║');
127
+ console.log('║ On Linux, include system dependencies with: ║');
128
+ console.log('║ ║');
129
+ console.log('║ npx playwright install --with-deps chromium ║');
130
+ console.log('║ ║');
131
+ console.log('╚═══════════════════════════════════════════════════════════════════════════╝');
132
+ }
133
+
134
+ /**
135
+ * Fix npm's bin entry on global installs to use the native binary directly.
136
+ * This provides zero-overhead CLI execution for global installs.
137
+ */
138
+ async function fixGlobalInstallBin() {
139
+ if (platform() === 'win32') {
140
+ await fixWindowsShims();
141
+ } else {
142
+ await fixUnixSymlink();
143
+ }
144
+ }
145
+
146
+ /**
147
+ * Fix npm symlink on Mac/Linux global installs.
148
+ * Replace the symlink to the JS wrapper with a symlink to the native binary.
149
+ */
150
+ async function fixUnixSymlink() {
151
+ // Get npm's global bin directory (npm prefix -g + /bin)
152
+ let npmBinDir;
153
+ try {
154
+ const prefix = execSync('npm prefix -g', { encoding: 'utf8' }).trim();
155
+ npmBinDir = join(prefix, 'bin');
156
+ } catch {
157
+ return; // npm not available
158
+ }
159
+
160
+ const symlinkPath = join(npmBinDir, 'agent-browser');
161
+
162
+ // Check if symlink exists (indicates global install)
163
+ try {
164
+ const stat = lstatSync(symlinkPath);
165
+ if (!stat.isSymbolicLink()) {
166
+ return; // Not a symlink, don't touch it
167
+ }
168
+ } catch {
169
+ return; // Symlink doesn't exist, not a global install
170
+ }
171
+
172
+ // Replace symlink to point directly to native binary
173
+ try {
174
+ unlinkSync(symlinkPath);
175
+ symlinkSync(binaryPath, symlinkPath);
176
+ console.log('✓ Optimized: symlink points to native binary (zero overhead)');
177
+ } catch (err) {
178
+ // Permission error or other issue - not critical, JS wrapper still works
179
+ console.log(`⚠ Could not optimize symlink: ${err.message}`);
180
+ console.log(' CLI will work via Node.js wrapper (slightly slower startup)');
181
+ }
182
+ }
183
+
184
+ /**
185
+ * Fix npm-generated shims on Windows global installs.
186
+ * npm generates shims that try to run /bin/sh, which doesn't exist on Windows.
187
+ * We overwrite them to invoke the native .exe directly.
188
+ */
189
+ async function fixWindowsShims() {
190
+ // Check if this is a global install by looking for npm's global prefix
191
+ let npmBinDir;
192
+ try {
193
+ npmBinDir = execSync('npm prefix -g', { encoding: 'utf8' }).trim();
194
+ } catch {
195
+ return; // Not a global install or npm not available
196
+ }
197
+
198
+ // The shims are in the npm prefix directory (not prefix/bin on Windows)
199
+ const cmdShim = join(npmBinDir, 'agent-browser.cmd');
200
+ const ps1Shim = join(npmBinDir, 'agent-browser.ps1');
201
+
202
+ // Only fix if shims exist (indicates global install)
203
+ if (!existsSync(cmdShim)) {
204
+ return;
205
+ }
206
+
207
+ // Path to native binary relative to npm prefix
208
+ const relativeBinaryPath = 'node_modules\\agent-browser\\bin\\agent-browser-win32-x64.exe';
209
+
210
+ try {
211
+ // Overwrite .cmd shim
212
+ const cmdContent = `@ECHO off\r\n"%~dp0${relativeBinaryPath}" %*\r\n`;
213
+ writeFileSync(cmdShim, cmdContent);
214
+
215
+ // Overwrite .ps1 shim
216
+ const ps1Content = `#!/usr/bin/env pwsh
217
+ $basedir = Split-Path $MyInvocation.MyCommand.Definition -Parent
218
+ $exe = ""
219
+ if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
220
+ $exe = ".exe"
221
+ }
222
+ & "$basedir/${relativeBinaryPath.replace(/\\/g, '/')}" $args
223
+ exit $LASTEXITCODE
224
+ `;
225
+ writeFileSync(ps1Shim, ps1Content);
226
+
227
+ console.log('✓ Optimized: shims point to native binary (zero overhead)');
228
+ } catch (err) {
229
+ // Permission error or other issue - not critical, JS wrapper still works
230
+ console.log(`⚠ Could not optimize shims: ${err.message}`);
231
+ console.log(' CLI will work via Node.js wrapper (slightly slower startup)');
232
+ }
233
+ }
234
+
235
+ main().catch(console.error);
@@ -0,0 +1,47 @@
1
+ (async () => {
2
+ window.scrollTo(0, document.body.scrollHeight);
3
+ await new Promise(resolve => setTimeout(resolve, 2000));
4
+
5
+ const scrollContainers = document.querySelectorAll('._infiniteScroll_center_1twc0_1');
6
+ const result = {
7
+ scrollContainersCount: scrollContainers.length,
8
+ containers: []
9
+ };
10
+
11
+ scrollContainers.forEach((container, index) => {
12
+ result.containers.push({
13
+ index,
14
+ className: container.className,
15
+ id: container.id,
16
+ childrenCount: container.children.length,
17
+ hasContent: container.children.length > 0
18
+ });
19
+
20
+ if (container.children.length > 0 && index === 1) {
21
+ result.products = [];
22
+ for (let i = 0; i < Math.min(container.children.length, 10); i++) {
23
+ const product = container.children[i];
24
+ const productData = {
25
+ index: i,
26
+ className: product.className,
27
+ id: product.id,
28
+ innerHTML: product.innerHTML.substring(0, 500)
29
+ };
30
+
31
+ const titleEl = product.querySelector('[class*="title"], [class*="name"]');
32
+ const priceEl = product.querySelector('[class*="price"]');
33
+ const shopEl = product.querySelector('[class*="shop"], [class*="store"]');
34
+ const linkEl = product.querySelector('a[href*="jd"]');
35
+
36
+ if (titleEl) productData.title = titleEl.textContent?.substring(0, 100);
37
+ if (priceEl) productData.price = priceEl.textContent?.substring(0, 50);
38
+ if (shopEl) productData.shop = shopEl.textContent?.substring(0, 50);
39
+ if (linkEl) productData.link = linkEl.href.substring(0, 100);
40
+
41
+ result.products.push(productData);
42
+ }
43
+ }
44
+ });
45
+
46
+ return result;
47
+ })()
@@ -0,0 +1,45 @@
1
+ (async () => {
2
+ const result = {
3
+ scrollPositions: [],
4
+ products: []
5
+ };
6
+
7
+ for (let i = 0; i < 10; i++) {
8
+ const scrollY = window.scrollY;
9
+ const scrollHeight = document.documentElement.scrollHeight;
10
+
11
+ window.scrollTo(0, scrollY + 500);
12
+ result.scrollPositions.push({ step: i, scrollY: scrollY + 500 });
13
+
14
+ await new Promise(resolve => setTimeout(resolve, 1000));
15
+ }
16
+
17
+ await new Promise(resolve => setTimeout(resolve, 3000));
18
+
19
+ const allImages = document.querySelectorAll('img[data-src*="360buy"]');
20
+ result.imageCount = allImages.length;
21
+
22
+ allImages.forEach((img, index) => {
23
+ if (index >= 10) return;
24
+
25
+ const parent = img.closest('a') || img.parentElement;
26
+ if (parent) {
27
+ const product = {
28
+ index,
29
+ imgSrc: img.src || img.getAttribute('data-src')?.substring(0, 100),
30
+ parentTag: parent.tagName,
31
+ parentClass: parent.className?.substring(0, 100),
32
+ link: parent.href || ''
33
+ };
34
+
35
+ const text = parent.textContent?.trim();
36
+ if (text) {
37
+ product.text = text.substring(0, 100);
38
+ }
39
+
40
+ result.products.push(product);
41
+ }
42
+ });
43
+
44
+ return result;
45
+ })()
@@ -0,0 +1,69 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Syncs the version from package.json to all other config files.
5
+ * Run this script before building or releasing.
6
+ */
7
+
8
+ import { execSync } from "child_process";
9
+ import { readFileSync, writeFileSync } from "fs";
10
+ import { dirname, join } from "path";
11
+ import { fileURLToPath } from "url";
12
+
13
+ const __dirname = dirname(fileURLToPath(import.meta.url));
14
+ const rootDir = join(__dirname, "..");
15
+ const cliDir = join(rootDir, "cli");
16
+
17
+ // Read version from package.json (single source of truth)
18
+ const packageJson = JSON.parse(
19
+ readFileSync(join(rootDir, "package.json"), "utf-8")
20
+ );
21
+ const version = packageJson.version;
22
+
23
+ console.log(`Syncing version ${version} to all config files...`);
24
+
25
+ // Update Cargo.toml
26
+ const cargoTomlPath = join(cliDir, "Cargo.toml");
27
+ let cargoToml = readFileSync(cargoTomlPath, "utf-8");
28
+ const cargoVersionRegex = /^version\s*=\s*"[^"]*"/m;
29
+ const newCargoVersion = `version = "${version}"`;
30
+
31
+ let cargoTomlUpdated = false;
32
+ if (cargoVersionRegex.test(cargoToml)) {
33
+ const oldMatch = cargoToml.match(cargoVersionRegex)?.[0];
34
+ if (oldMatch !== newCargoVersion) {
35
+ cargoToml = cargoToml.replace(cargoVersionRegex, newCargoVersion);
36
+ writeFileSync(cargoTomlPath, cargoToml);
37
+ console.log(` Updated cli/Cargo.toml: ${oldMatch} -> ${newCargoVersion}`);
38
+ cargoTomlUpdated = true;
39
+ } else {
40
+ console.log(` cli/Cargo.toml already up to date`);
41
+ }
42
+ } else {
43
+ console.error(" Could not find version field in cli/Cargo.toml");
44
+ process.exit(1);
45
+ }
46
+
47
+ // Update Cargo.lock to match Cargo.toml
48
+ if (cargoTomlUpdated) {
49
+ try {
50
+ execSync("cargo update -p agent-browser --offline", {
51
+ cwd: cliDir,
52
+ stdio: "pipe",
53
+ });
54
+ console.log(` Updated cli/Cargo.lock`);
55
+ } catch {
56
+ // --offline may fail if package not in cache, try without it
57
+ try {
58
+ execSync("cargo update -p agent-browser", {
59
+ cwd: cliDir,
60
+ stdio: "pipe",
61
+ });
62
+ console.log(` Updated cli/Cargo.lock`);
63
+ } catch (e) {
64
+ console.error(` Warning: Could not update Cargo.lock: ${e.message}`);
65
+ }
66
+ }
67
+ }
68
+
69
+ console.log("Version sync complete.");