@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,586 @@
1
+ export function createInitialState() {
2
+ return {
3
+ ws: null,
4
+ metadata: { deviceWidth: 1280, deviceHeight: 720, pageScaleFactor: 1, format: 'jpeg' },
5
+ userActivityTimeout: null,
6
+ pendingBinary: false,
7
+ modifiers: 0,
8
+ clickCount: 0,
9
+ clickTimer: null,
10
+ isComposing: false,
11
+ lastInputValue: '',
12
+ fixedSize: false,
13
+ };
14
+ }
15
+ export function buildWebSocketUrl(config) {
16
+ const wsParam = config.instanceId
17
+ ? 'instanceId=' + config.instanceId
18
+ : 'session=' + config.session;
19
+ return config.wsProtocol + '//' + config.hostname + ':' + config.port + '?' + wsParam;
20
+ }
21
+ export function parseConfigFromLocation() {
22
+ const wsProtocol = typeof location !== 'undefined' && location.protocol === 'https:' ? 'wss:' : 'ws:';
23
+ const defaultPort = 5005;
24
+ const port = (typeof location !== 'undefined' && parseInt(location.port, 10)) || defaultPort;
25
+ let instanceId = null;
26
+ let session = 'default';
27
+ if (typeof URLSearchParams !== 'undefined' && typeof location !== 'undefined') {
28
+ const urlParams = new URLSearchParams(location.search);
29
+ instanceId = urlParams.get('instanceId');
30
+ session = urlParams.get('session') || 'default';
31
+ }
32
+ return {
33
+ wsProtocol,
34
+ hostname: typeof location !== 'undefined' ? location.hostname : 'localhost',
35
+ port,
36
+ instanceId,
37
+ session,
38
+ };
39
+ }
40
+ export function safeSend(ws, data) {
41
+ if (ws && ws.readyState === WebSocket.OPEN) {
42
+ ws.send(data);
43
+ }
44
+ }
45
+ export function sendUserActivity(state, qualityBadge, ws) {
46
+ safeSend(ws, JSON.stringify({ type: 'user_activity' }));
47
+ if (state.userActivityTimeout !== null) {
48
+ clearTimeout(state.userActivityTimeout);
49
+ }
50
+ state.userActivityTimeout = setTimeout(() => {
51
+ qualityBadge.textContent = 'static';
52
+ }, 2000);
53
+ qualityBadge.textContent = 'interacting';
54
+ }
55
+ export function screenToPage(screenX, screenY, screenWidth, screenHeight, deviceWidth, deviceHeight) {
56
+ const scaleX = deviceWidth / screenWidth;
57
+ const scaleY = deviceHeight / screenHeight;
58
+ return {
59
+ x: Math.round(screenX * scaleX),
60
+ y: Math.round(screenY * scaleY),
61
+ };
62
+ }
63
+ export function updateModifiers(e) {
64
+ let modifiers = 0;
65
+ if (e.altKey)
66
+ modifiers |= 1;
67
+ if (e.ctrlKey)
68
+ modifiers |= 2;
69
+ if (e.metaKey)
70
+ modifiers |= 4;
71
+ if (e.shiftKey)
72
+ modifiers |= 8;
73
+ return modifiers;
74
+ }
75
+ export function shouldSendText(key, ctrlKey, metaKey, altKey) {
76
+ return key.length === 1 && !ctrlKey && !metaKey && !altKey;
77
+ }
78
+ export function buildViewerScript() {
79
+ return `
80
+ const wsProtocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
81
+ const defaultPort = 5005;
82
+ const port = parseInt(location.port, 10) || defaultPort;
83
+ const urlParams = new URLSearchParams(location.search);
84
+ const instanceId = urlParams.get('instanceId');
85
+ const session = urlParams.get('session') || 'default';
86
+ const wsParam = instanceId ? 'instanceId=' + instanceId : 'session=' + session;
87
+ const wsUrl = wsProtocol + '//' + location.hostname + ':' + port + '?' + wsParam;
88
+
89
+ // Background management
90
+ let shouldReconnect = true;
91
+ let reconnectTimer = null;
92
+ let backgroundTimer = null;
93
+ const BACKGROUND_TIMEOUT = 60000; // 60 seconds
94
+
95
+ const screen = document.getElementById('screen');
96
+ const statusDot = document.getElementById('statusDot');
97
+ const statusText = document.getElementById('statusText');
98
+ const urlDisplay = document.getElementById('urlDisplay');
99
+ const qualityBadge = document.getElementById('qualityBadge');
100
+ const tabsContainer = document.getElementById('tabs');
101
+ const connecting = document.getElementById('connecting');
102
+
103
+ const hiddenInput = document.createElement('input');
104
+ hiddenInput.type = 'text';
105
+ hiddenInput.style.cssText = 'position:fixed;right:8px;bottom:8px;opacity:0.01;width:1px;height:1px;border:none;outline:none;padding:0;margin:0;';
106
+ hiddenInput.id = 'hidden-input';
107
+ hiddenInput.setAttribute('autocomplete', 'off');
108
+ hiddenInput.setAttribute('autocorrect', 'off');
109
+ hiddenInput.setAttribute('autocapitalize', 'off');
110
+ hiddenInput.setAttribute('spellcheck', 'false');
111
+ document.body.appendChild(hiddenInput);
112
+
113
+ let ws = null;
114
+ let metadata = { deviceWidth: 1280, deviceHeight: 720, pageScaleFactor: 1, format: 'jpeg' };
115
+ let userActivityTimeout = null;
116
+ let pendingBinary = false;
117
+ let modifiers = 0;
118
+ let clickCount = 0;
119
+ let clickTimer = null;
120
+ let isComposing = false;
121
+ let lastInputValue = '';
122
+ let fixedSize = false;
123
+ let isRecording = false;
124
+
125
+ function connect() {
126
+ ws = new WebSocket(wsUrl);
127
+ ws.binaryType = 'arraybuffer';
128
+
129
+ ws.onopen = () => {
130
+ statusDot.classList.add('connected');
131
+ statusText.textContent = 'Connected';
132
+ connecting.style.display = 'none';
133
+ reconnectTimer = null;
134
+ };
135
+
136
+ ws.onclose = () => {
137
+ statusDot.classList.remove('connected');
138
+ statusText.textContent = 'Disconnected';
139
+ connecting.style.display = 'flex';
140
+
141
+ // Only reconnect if we should (page is visible)
142
+ if (shouldReconnect) {
143
+ reconnectTimer = setTimeout(connect, 2000);
144
+ }
145
+ };
146
+
147
+ ws.onerror = () => {
148
+ statusText.textContent = 'Connection error';
149
+ };
150
+
151
+ ws.onmessage = (event) => {
152
+ if (event.data instanceof ArrayBuffer) {
153
+ handleBinary(event.data);
154
+ return;
155
+ }
156
+
157
+ const msg = JSON.parse(event.data);
158
+
159
+ // Handle recorder responses
160
+ if (msg.id && msg.id.startsWith('recorder-')) {
161
+ if (msg.id.startsWith('recorder-start-') && msg.success) {
162
+ isRecording = true;
163
+ recordBtn.classList.add('recording');
164
+ recordText.textContent = 'Stop';
165
+ } else if (msg.id.startsWith('recorder-stop-') && msg.success) {
166
+ isRecording = false;
167
+ recordBtn.classList.remove('recording');
168
+ recordText.textContent = 'Record';
169
+
170
+ // Download YAML
171
+ if (msg.data && msg.data.yaml) {
172
+ const blob = new Blob([msg.data.yaml], { type: 'text/yaml' });
173
+ const url = URL.createObjectURL(blob);
174
+ const a = document.createElement('a');
175
+ a.href = url;
176
+ a.download = 'session-' + Date.now() + '.yaml';
177
+ a.click();
178
+ URL.revokeObjectURL(url);
179
+ alert('Recording stopped. ' + msg.data.steps + ' steps recorded.');
180
+ }
181
+ }
182
+ return;
183
+ }
184
+
185
+ switch (msg.type) {
186
+ case 'frame':
187
+ pendingBinary = true;
188
+ metadata = msg.metadata;
189
+ if (msg.format) metadata.format = msg.format;
190
+ if (msg.state) {
191
+ qualityBadge.textContent = msg.state;
192
+ }
193
+ break;
194
+
195
+ case 'status':
196
+ if (msg.connected === false) {
197
+ statusDot.classList.remove('connected');
198
+ statusText.textContent = 'Instance not found';
199
+ const p = connecting.querySelector('p');
200
+ if (p) p.textContent = 'The browser instance has been closed or not found.';
201
+ connecting.style.display = 'flex';
202
+ } else {
203
+ if (msg.viewportWidth) {
204
+ metadata.deviceWidth = msg.viewportWidth;
205
+ metadata.deviceHeight = msg.viewportHeight;
206
+ }
207
+ }
208
+ break;
209
+
210
+ case 'navigation':
211
+ urlDisplay.value = msg.data.url;
212
+ document.title = msg.data.title + ' - Agent Browser Viewer';
213
+ break;
214
+
215
+ case 'tab_created':
216
+ addTab(msg.data.index, msg.data.url, msg.data.title, false);
217
+ break;
218
+
219
+ case 'tab_closed':
220
+ removeTab(msg.data.index);
221
+ break;
222
+
223
+ case 'tab_switched':
224
+ setActiveTab(msg.data.toIndex);
225
+ break;
226
+ }
227
+ };
228
+ }
229
+
230
+ function handleBinary(data) {
231
+ if (!pendingBinary) return;
232
+ pendingBinary = false;
233
+
234
+ const blob = new Blob([data], {
235
+ type: metadata.format === 'webp' ? 'image/webp' : 'image/jpeg'
236
+ });
237
+ const url = URL.createObjectURL(blob);
238
+
239
+ const cleanup = () => {
240
+ URL.revokeObjectURL(url);
241
+ connecting.style.display = 'none';
242
+ screen.style.display = 'block';
243
+ };
244
+
245
+ screen.onload = cleanup;
246
+ screen.onerror = cleanup;
247
+ screen.src = url;
248
+
249
+ if (!fixedSize) {
250
+ screen.style.width = metadata.deviceWidth + 'px';
251
+ screen.style.height = metadata.deviceHeight + 'px';
252
+ fixedSize = true;
253
+ }
254
+ }
255
+
256
+ function safeSend(data) {
257
+ if (ws && ws.readyState === WebSocket.OPEN) {
258
+ ws.send(data);
259
+ }
260
+ }
261
+
262
+ function sendUserActivity() {
263
+ safeSend(JSON.stringify({ type: 'user_activity' }));
264
+
265
+ clearTimeout(userActivityTimeout);
266
+ userActivityTimeout = setTimeout(() => {
267
+ qualityBadge.textContent = 'static';
268
+ }, 2000);
269
+
270
+ qualityBadge.textContent = 'interacting';
271
+ }
272
+
273
+ function screenToPage(screenX, screenY) {
274
+ const rect = screen.getBoundingClientRect();
275
+ const scaleX = metadata.deviceWidth / rect.width;
276
+ const scaleY = metadata.deviceHeight / rect.height;
277
+
278
+ return {
279
+ x: Math.round((screenX - rect.left) * scaleX),
280
+ y: Math.round((screenY - rect.top) * scaleY)
281
+ };
282
+ }
283
+
284
+ function updateModifiers(e) {
285
+ modifiers = 0;
286
+ if (e.altKey) modifiers |= 1;
287
+ if (e.ctrlKey) modifiers |= 2;
288
+ if (e.metaKey) modifiers |= 4;
289
+ if (e.shiftKey) modifiers |= 8;
290
+ }
291
+
292
+ function focusHiddenInput() {
293
+ hiddenInput.focus();
294
+ hiddenInput.select();
295
+ }
296
+
297
+ screen.addEventListener('dragstart', (e) => e.preventDefault());
298
+
299
+ screen.addEventListener('click', () => {
300
+ focusHiddenInput();
301
+ });
302
+
303
+ screen.addEventListener('mousemove', (e) => {
304
+ sendUserActivity();
305
+ const pos = screenToPage(e.clientX, e.clientY);
306
+ safeSend(JSON.stringify({
307
+ type: 'input_mouse',
308
+ eventType: 'mouseMoved',
309
+ x: pos.x,
310
+ y: pos.y
311
+ }));
312
+ });
313
+
314
+ screen.addEventListener('mousedown', (e) => {
315
+ sendUserActivity();
316
+ updateModifiers(e);
317
+
318
+ if (clickTimer) {
319
+ clickCount++;
320
+ } else {
321
+ clickCount = 1;
322
+ clickTimer = setTimeout(() => {
323
+ clickCount = 0;
324
+ clickTimer = null;
325
+ }, 300);
326
+ }
327
+
328
+ const pos = screenToPage(e.clientX, e.clientY);
329
+ const buttonMap = { 0: 'left', 1: 'middle', 2: 'right' };
330
+ safeSend(JSON.stringify({
331
+ type: 'input_mouse',
332
+ eventType: 'mousePressed',
333
+ x: pos.x,
334
+ y: pos.y,
335
+ button: buttonMap[e.button] || 'left',
336
+ clickCount: clickCount,
337
+ modifiers: modifiers
338
+ }));
339
+ });
340
+
341
+ screen.addEventListener('mouseup', (e) => {
342
+ updateModifiers(e);
343
+ const pos = screenToPage(e.clientX, e.clientY);
344
+ const buttonMap = { 0: 'left', 1: 'middle', 2: 'right' };
345
+ safeSend(JSON.stringify({
346
+ type: 'input_mouse',
347
+ eventType: 'mouseReleased',
348
+ x: pos.x,
349
+ y: pos.y,
350
+ button: buttonMap[e.button] || 'left',
351
+ clickCount: clickCount,
352
+ modifiers: modifiers
353
+ }));
354
+ });
355
+
356
+ screen.addEventListener('wheel', (e) => {
357
+ sendUserActivity();
358
+ updateModifiers(e);
359
+ const pos = screenToPage(e.clientX, e.clientY);
360
+ safeSend(JSON.stringify({
361
+ type: 'input_mouse',
362
+ eventType: 'mouseWheel',
363
+ x: pos.x,
364
+ y: pos.y,
365
+ deltaX: e.deltaX,
366
+ deltaY: e.deltaY,
367
+ modifiers: modifiers
368
+ }));
369
+ e.preventDefault();
370
+ });
371
+
372
+ hiddenInput.addEventListener('compositionstart', () => {
373
+ isComposing = true;
374
+ lastInputValue = hiddenInput.value;
375
+ console.log('[Viewer] compositionstart, lastInputValue:', lastInputValue);
376
+ });
377
+
378
+ hiddenInput.addEventListener('compositionend', (e) => {
379
+ isComposing = false;
380
+ const newText = hiddenInput.value.slice(lastInputValue.length);
381
+ console.log('[Viewer] compositionend, newText:', newText, 'hiddenInput.value:', hiddenInput.value);
382
+ if (newText) {
383
+ sendUserActivity();
384
+ safeSend(JSON.stringify({
385
+ type: 'keyboard_insert_text',
386
+ text: newText
387
+ }));
388
+ }
389
+ lastInputValue = '';
390
+ hiddenInput.value = '';
391
+ });
392
+
393
+ hiddenInput.addEventListener('input', (e) => {
394
+ console.log('[Viewer] input event, isComposing:', isComposing, 'hiddenInput.value:', hiddenInput.value);
395
+ if (isComposing) return;
396
+
397
+ const newValue = hiddenInput.value;
398
+ if (newValue.length > 0) {
399
+ sendUserActivity();
400
+ safeSend(JSON.stringify({
401
+ type: 'keyboard_insert_text',
402
+ text: newValue
403
+ }));
404
+ }
405
+ hiddenInput.value = '';
406
+ lastInputValue = '';
407
+ });
408
+
409
+ hiddenInput.addEventListener('paste', (e) => {
410
+ e.preventDefault();
411
+ e.stopPropagation();
412
+
413
+ const text = e.clipboardData.getData('text');
414
+ console.log('[Viewer] paste event, text:', text);
415
+ if (text) {
416
+ sendUserActivity();
417
+ safeSend(JSON.stringify({
418
+ type: 'keyboard_insert_text',
419
+ text: text
420
+ }));
421
+ }
422
+ });
423
+
424
+ document.addEventListener('keydown', (e) => {
425
+ console.log('[Viewer] keydown, key:', e.key, 'target:', e.target === hiddenInput ? 'hiddenInput' : 'other', 'metaKey:', e.metaKey, 'ctrlKey:', e.ctrlKey);
426
+ if (e.target === hiddenInput) {
427
+ if (e.key === 'Tab') {
428
+ e.preventDefault();
429
+ }
430
+
431
+ if (e.key === 'Backspace' || e.key === 'Delete') {
432
+ e.preventDefault();
433
+ sendUserActivity();
434
+ safeSend(JSON.stringify({
435
+ type: 'keyboard_down',
436
+ key: e.key
437
+ }));
438
+ return;
439
+ }
440
+
441
+ if (e.key.length === 1 && !e.ctrlKey && !e.metaKey && !e.altKey && !isComposing) {
442
+ console.log('[Viewer] single char in hiddenInput, waiting for input event');
443
+ return;
444
+ }
445
+ }
446
+
447
+ if (isComposing) return;
448
+
449
+ sendUserActivity();
450
+
451
+ safeSend(JSON.stringify({
452
+ type: 'keyboard_down',
453
+ key: e.key
454
+ }));
455
+
456
+ if (e.key === 'Tab' || (e.key === 'Backspace' && !e.target.matches('input, textarea'))) {
457
+ e.preventDefault();
458
+ }
459
+ });
460
+
461
+ document.addEventListener('keyup', (e) => {
462
+ console.log('[Viewer] keyup, key:', e.key);
463
+ if (isComposing && e.target === hiddenInput) return;
464
+
465
+ safeSend(JSON.stringify({
466
+ type: 'keyboard_up',
467
+ key: e.key
468
+ }));
469
+ });
470
+
471
+ screen.addEventListener('touchstart', (e) => {
472
+ sendUserActivity();
473
+ focusHiddenInput();
474
+ const touchPoints = Array.from(e.touches).map(t => {
475
+ const pos = screenToPage(t.clientX, t.clientY);
476
+ return { x: pos.x, y: pos.y, id: t.identifier };
477
+ });
478
+ safeSend(JSON.stringify({
479
+ type: 'input_touch',
480
+ eventType: 'touchStart',
481
+ touchPoints: touchPoints
482
+ }));
483
+ e.preventDefault();
484
+ }, { passive: false });
485
+
486
+ screen.addEventListener('touchmove', (e) => {
487
+ const touchPoints = Array.from(e.touches).map(t => {
488
+ const pos = screenToPage(t.clientX, t.clientY);
489
+ return { x: pos.x, y: pos.y, id: t.identifier };
490
+ });
491
+ safeSend(JSON.stringify({
492
+ type: 'input_touch',
493
+ eventType: 'touchMove',
494
+ touchPoints: touchPoints
495
+ }));
496
+ e.preventDefault();
497
+ }, { passive: false });
498
+
499
+ screen.addEventListener('touchend', (e) => {
500
+ const touchPoints = Array.from(e.changedTouches).map(t => {
501
+ const pos = screenToPage(t.clientX, t.clientY);
502
+ return { x: pos.x, y: pos.y, id: t.identifier };
503
+ });
504
+ safeSend(JSON.stringify({
505
+ type: 'input_touch',
506
+ eventType: 'touchEnd',
507
+ touchPoints: touchPoints
508
+ }));
509
+ e.preventDefault();
510
+ }, { passive: false });
511
+
512
+ function addTab(index, url, title, active) {
513
+ let tab = document.getElementById('tab-' + index);
514
+ if (!tab) {
515
+ tab = document.createElement('button');
516
+ tab.id = 'tab-' + index;
517
+ tab.className = 'tab';
518
+ tab.onclick = () => {
519
+ safeSend(JSON.stringify({ id: 'tab-' + Date.now(), action: 'tab_switch', index }));
520
+ };
521
+ tabsContainer.appendChild(tab);
522
+ }
523
+ tab.textContent = title || url || 'New Tab';
524
+ tab.title = url;
525
+ tab.classList.toggle('active', active);
526
+ }
527
+
528
+ function removeTab(index) {
529
+ const tab = document.getElementById('tab-' + index);
530
+ if (tab) tab.remove();
531
+ }
532
+
533
+ function setActiveTab(index) {
534
+ document.querySelectorAll('.tab').forEach((t, i) => {
535
+ t.classList.toggle('active', t.id === 'tab-' + index);
536
+ });
537
+ }
538
+
539
+ // Recorder functionality
540
+ const recordBtn = document.getElementById('recordBtn');
541
+ const recordText = document.getElementById('recordText');
542
+
543
+ recordBtn.onclick = () => {
544
+ if (isRecording) {
545
+ // Stop recording
546
+ safeSend(JSON.stringify({ id: 'recorder-stop-' + Date.now(), action: 'recorder_stop' }));
547
+ } else {
548
+ // Start recording
549
+ safeSend(JSON.stringify({ id: 'recorder-start-' + Date.now(), action: 'recorder_start' }));
550
+ }
551
+ };
552
+
553
+ // Page visibility management
554
+ document.addEventListener('visibilitychange', () => {
555
+ if (document.hidden) {
556
+ // Page going to background: stop reconnect attempts
557
+ shouldReconnect = false;
558
+
559
+ // Start background timer - disconnect after timeout
560
+ backgroundTimer = setTimeout(() => {
561
+ if (ws && ws.readyState === WebSocket.OPEN) {
562
+ ws.close(1000, 'Page in background');
563
+ }
564
+ }, BACKGROUND_TIMEOUT);
565
+ } else {
566
+ // Page coming back to foreground
567
+ shouldReconnect = true;
568
+
569
+ // Clear background timer if page is visible again
570
+ if (backgroundTimer) {
571
+ clearTimeout(backgroundTimer);
572
+ backgroundTimer = null;
573
+ }
574
+
575
+ // Reconnect immediately if disconnected
576
+ if (!ws || ws.readyState === WebSocket.CLOSED) {
577
+ connect();
578
+ }
579
+ }
580
+ });
581
+
582
+ focusHiddenInput();
583
+ connect();
584
+ `;
585
+ }
586
+ //# sourceMappingURL=viewer-script.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"viewer-script.js","sourceRoot":"","sources":["../src/viewer-script.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,86 @@
1
+ {
2
+ "name": "@dyyz1993/agent-browser",
3
+ "version": "0.9.2",
4
+ "description": "Headless browser automation CLI for AI agents",
5
+ "type": "module",
6
+ "main": "dist/daemon.js",
7
+ "files": [
8
+ "dist",
9
+ "bin",
10
+ "scripts",
11
+ "skills"
12
+ ],
13
+ "bin": {
14
+ "agent-browser": "./dist/cli.js"
15
+ },
16
+ "scripts": {
17
+ "prepare": "husky",
18
+ "version:sync": "node scripts/sync-version.js",
19
+ "version": "npm run version:sync && git add cli/Cargo.toml",
20
+ "build": "tsc && node scripts/copy-recorder.js",
21
+ "build:native": "npm run version:sync && cargo build --release --manifest-path cli/Cargo.toml && node scripts/copy-native.js",
22
+ "build:linux": "npm run version:sync && docker compose -f docker/docker-compose.yml run --rm build-linux",
23
+ "build:macos": "npm run version:sync && (cargo build --release --manifest-path cli/Cargo.toml --target aarch64-apple-darwin & cargo build --release --manifest-path cli/Cargo.toml --target x86_64-apple-darwin & wait) && cp cli/target/aarch64-apple-darwin/release/agent-browser bin/agent-browser-darwin-arm64 && cp cli/target/x86_64-apple-darwin/release/agent-browser bin/agent-browser-darwin-x64",
24
+ "build:windows": "npm run version:sync && docker compose -f docker/docker-compose.yml run --rm build-windows",
25
+ "build:all-platforms": "npm run version:sync && (npm run build:linux & npm run build:windows & wait) && npm run build:macos",
26
+ "build:docker": "docker build -t agent-browser-builder -f docker/Dockerfile.build .",
27
+ "release": "npm run version:sync && npm run build && npm run build:all-platforms && npm publish",
28
+ "start": "node dist/daemon.js",
29
+ "dev": "tsx src/daemon.ts",
30
+ "typecheck": "tsc --noEmit",
31
+ "format": "prettier --write 'src/**/*.ts'",
32
+ "format:check": "prettier --check 'src/**/*.ts'",
33
+ "test": "vitest run",
34
+ "test:watch": "vitest",
35
+ "postinstall": "node scripts/postinstall.js",
36
+ "changeset": "changeset",
37
+ "ci:version": "changeset version && pnpm run version:sync && pnpm install --no-frozen-lockfile",
38
+ "ci:publish": "pnpm run version:sync && pnpm run build && changeset publish"
39
+ },
40
+ "keywords": [
41
+ "browser",
42
+ "automation",
43
+ "headless",
44
+ "playwright",
45
+ "cli",
46
+ "agent"
47
+ ],
48
+ "license": "Apache-2.0",
49
+ "repository": {
50
+ "type": "git",
51
+ "url": "git+https://github.com/xuyingzhou/agent-browser.git"
52
+ },
53
+ "bugs": {
54
+ "url": "https://github.com/xuyingzhou/agent-browser/issues"
55
+ },
56
+ "homepage": "https://github.com/xuyingzhou/agent-browser#readme",
57
+ "dependencies": {
58
+ "node-simctl": "^7.4.0",
59
+ "playwright-core": "^1.57.0",
60
+ "sharp": "^0.34.5",
61
+ "webdriverio": "^9.15.0",
62
+ "ws": "^8.19.0",
63
+ "zod": "^3.22.4"
64
+ },
65
+ "devDependencies": {
66
+ "@changesets/cli": "^2.29.8",
67
+ "@types/node": "^20.10.0",
68
+ "@types/ws": "^8.18.1",
69
+ "happy-dom": "^20.7.0",
70
+ "husky": "^9.1.7",
71
+ "lint-staged": "^15.2.11",
72
+ "playwright": "^1.57.0",
73
+ "prettier": "^3.7.4",
74
+ "tsx": "^4.6.0",
75
+ "typescript": "^5.3.0",
76
+ "vitest": "^4.0.16"
77
+ },
78
+ "lint-staged": {
79
+ "src/**/*.ts": "prettier --write"
80
+ },
81
+ "directories": {
82
+ "doc": "docs",
83
+ "test": "test"
84
+ },
85
+ "author": "xuyingzhou"
86
+ }