@web-auto/camo 0.1.18 → 0.1.20

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 (91) hide show
  1. package/README.md +18 -19
  2. package/bin/browser-service.mjs +11 -0
  3. package/package.json +7 -2
  4. package/scripts/install.mjs +3 -3
  5. package/src/cli.mjs +8 -5
  6. package/src/commands/attach.mjs +141 -0
  7. package/src/commands/browser.mjs +5 -16
  8. package/src/commands/mouse.mjs +2 -12
  9. package/src/container/runtime-core/operations/index.mjs +6 -15
  10. package/src/container/subscription-registry.mjs +6 -6
  11. package/src/core/actions.mjs +0 -12
  12. package/src/core/index.mjs +0 -1
  13. package/src/lifecycle/lock.mjs +7 -3
  14. package/src/services/browser-service/index.js +651 -0
  15. package/src/services/browser-service/index.js.map +1 -0
  16. package/src/services/browser-service/internal/BrowserSession.input.test.js +322 -0
  17. package/src/services/browser-service/internal/BrowserSession.input.test.js.map +1 -0
  18. package/src/services/browser-service/internal/BrowserSession.js +304 -0
  19. package/src/services/browser-service/internal/BrowserSession.js.map +1 -0
  20. package/src/services/browser-service/internal/ElementRegistry.js +61 -0
  21. package/src/services/browser-service/internal/ElementRegistry.js.map +1 -0
  22. package/src/services/browser-service/internal/ProfileLock.js +85 -0
  23. package/src/services/browser-service/internal/ProfileLock.js.map +1 -0
  24. package/src/services/browser-service/internal/SessionManager.js +184 -0
  25. package/src/services/browser-service/internal/SessionManager.js.map +1 -0
  26. package/src/services/browser-service/internal/SessionManager.test.js +40 -0
  27. package/src/services/browser-service/internal/SessionManager.test.js.map +1 -0
  28. package/src/services/browser-service/internal/browser-session/cookies.js +145 -0
  29. package/src/services/browser-service/internal/browser-session/cookies.js.map +1 -0
  30. package/src/services/browser-service/internal/browser-session/input-ops.js +127 -0
  31. package/src/services/browser-service/internal/browser-session/input-ops.js.map +1 -0
  32. package/src/services/browser-service/internal/browser-session/input-pipeline.js +133 -0
  33. package/src/services/browser-service/internal/browser-session/input-pipeline.js.map +1 -0
  34. package/src/services/browser-service/internal/browser-session/logging.js +46 -0
  35. package/src/services/browser-service/internal/browser-session/navigation.js +39 -0
  36. package/src/services/browser-service/internal/browser-session/navigation.js.map +1 -0
  37. package/src/services/browser-service/internal/browser-session/page-hooks.js +443 -0
  38. package/src/services/browser-service/internal/browser-session/page-hooks.js.map +1 -0
  39. package/src/services/browser-service/internal/browser-session/page-management.js +212 -0
  40. package/src/services/browser-service/internal/browser-session/page-management.js.map +1 -0
  41. package/src/services/browser-service/internal/browser-session/recording.js +199 -0
  42. package/src/services/browser-service/internal/browser-session/recording.js.map +1 -0
  43. package/src/services/browser-service/internal/browser-session/runtime-events.js +62 -0
  44. package/src/services/browser-service/internal/browser-session/runtime-events.js.map +1 -0
  45. package/src/services/browser-service/internal/browser-session/session-core.js +85 -0
  46. package/src/services/browser-service/internal/browser-session/session-core.js.map +1 -0
  47. package/src/services/browser-service/internal/browser-session/session-state.js +39 -0
  48. package/src/services/browser-service/internal/browser-session/session-state.js.map +1 -0
  49. package/src/services/browser-service/internal/browser-session/types.js +15 -0
  50. package/src/services/browser-service/internal/browser-session/types.js.map +1 -0
  51. package/src/services/browser-service/internal/browser-session/utils.js +69 -0
  52. package/src/services/browser-service/internal/browser-session/utils.js.map +1 -0
  53. package/src/services/browser-service/internal/browser-session/viewport-manager.js +47 -0
  54. package/src/services/browser-service/internal/browser-session/viewport-manager.js.map +1 -0
  55. package/src/services/browser-service/internal/browser-session/viewport.js +216 -0
  56. package/src/services/browser-service/internal/browser-session/viewport.js.map +1 -0
  57. package/src/services/browser-service/internal/container-matcher.js +852 -0
  58. package/src/services/browser-service/internal/container-matcher.js.map +1 -0
  59. package/src/services/browser-service/internal/container-registry.js +182 -0
  60. package/src/services/browser-service/internal/engine-manager.js +259 -0
  61. package/src/services/browser-service/internal/engine-manager.js.map +1 -0
  62. package/src/services/browser-service/internal/fingerprint.js +203 -0
  63. package/src/services/browser-service/internal/fingerprint.js.map +1 -0
  64. package/src/services/browser-service/internal/heartbeat.js +137 -0
  65. package/src/services/browser-service/internal/logging.js +46 -0
  66. package/src/services/browser-service/internal/page-runtime/runtime.js +1317 -0
  67. package/src/services/browser-service/internal/pageRuntime.js +29 -0
  68. package/src/services/browser-service/internal/pageRuntime.js.map +1 -0
  69. package/src/services/browser-service/internal/runtimeInjector.js +31 -0
  70. package/src/services/browser-service/internal/runtimeInjector.js.map +1 -0
  71. package/src/services/browser-service/internal/service-process-logger.js +140 -0
  72. package/src/services/browser-service/internal/state-bus.js +46 -0
  73. package/src/services/browser-service/internal/state-bus.js.map +1 -0
  74. package/src/services/browser-service/internal/storage-paths.js +42 -0
  75. package/src/services/browser-service/internal/storage-paths.js.map +1 -0
  76. package/src/services/browser-service/internal/ws-server.js +1194 -0
  77. package/src/services/browser-service/internal/ws-server.js.map +1 -0
  78. package/src/services/browser-service/internal/ws-server.test.js +59 -0
  79. package/src/services/browser-service/internal/ws-server.test.js.map +1 -0
  80. package/src/services/browser-service/server.mjs +6 -0
  81. package/src/services/controller/cli-bridge.js +93 -0
  82. package/src/services/controller/container-index.js +50 -0
  83. package/src/services/controller/container-storage.js +36 -0
  84. package/src/services/controller/controller-actions.js +207 -0
  85. package/src/services/controller/controller.js +1138 -0
  86. package/src/services/controller/selectors.js +54 -0
  87. package/src/services/controller/transport.js +118 -0
  88. package/src/utils/browser-service.mjs +100 -125
  89. package/src/utils/config.mjs +22 -21
  90. package/src/utils/help.mjs +11 -9
  91. package/src/utils/ws-client.mjs +30 -0
@@ -0,0 +1,443 @@
1
+ import { ensurePageRuntime } from '../pageRuntime.js';
2
+ export function createPageHooksManager(deps) {
3
+ const bridgedPages = new WeakSet();
4
+ const recorderBridgePages = new WeakSet();
5
+ function setupPageHooks(page) {
6
+ const profileTag = `[session:${deps.profileId}]`;
7
+ const ensure = (reason) => {
8
+ ensurePageRuntime(page, true).catch((err) => {
9
+ console.warn(`${profileTag} ensure runtime failed (${reason})`, err?.message || err);
10
+ });
11
+ };
12
+ bindRuntimeBridge(page);
13
+ bindRecorderBridge(page);
14
+ page.on('domcontentloaded', () => {
15
+ ensure('domcontentloaded');
16
+ const recording = deps.getRecording();
17
+ if (recording.active) {
18
+ void installRecorderRuntime(page, 'domcontentloaded');
19
+ }
20
+ });
21
+ page.on('framenavigated', (frame) => {
22
+ if (frame === page.mainFrame()) {
23
+ ensure('framenavigated');
24
+ deps.recordPageVisit(page, 'framenavigated');
25
+ }
26
+ });
27
+ page.on('pageerror', (error) => {
28
+ console.warn(`${profileTag} pageerror`, error?.message || error);
29
+ });
30
+ page.on('console', (msg) => {
31
+ if (msg.type() === 'error') {
32
+ console.warn(`${profileTag} console.error`, msg.text());
33
+ }
34
+ });
35
+ ensure('initial');
36
+ const recording = deps.getRecording();
37
+ if (recording.active) {
38
+ void installRecorderRuntime(page, 'initial');
39
+ }
40
+ }
41
+ function bindRuntimeBridge(page) {
42
+ if (bridgedPages.has(page))
43
+ return;
44
+ bridgedPages.add(page);
45
+ page.exposeFunction('camo_dispatch', (evt) => {
46
+ deps.emitRuntimeEvent({
47
+ ...evt,
48
+ pageUrl: page.url(),
49
+ });
50
+ }).catch((err) => {
51
+ console.warn(`[session:${deps.profileId}] failed to expose camo_dispatch`, err?.message || err);
52
+ });
53
+ }
54
+ function bindRecorderBridge(page) {
55
+ if (recorderBridgePages.has(page))
56
+ return;
57
+ recorderBridgePages.add(page);
58
+ page.exposeFunction('camo_recorder_dispatch', (evt) => {
59
+ deps.handleRecorderEvent(page, evt);
60
+ }).catch((err) => {
61
+ console.warn(`[session:${deps.profileId}] failed to expose camo_recorder_dispatch`, err?.message || err);
62
+ });
63
+ }
64
+ async function installRecorderRuntime(page, reason) {
65
+ if (!page || page.isClosed())
66
+ return;
67
+ try {
68
+ await page.evaluate(buildRecorderBootstrapScript());
69
+ }
70
+ catch {
71
+ return;
72
+ }
73
+ const recording = deps.getRecording();
74
+ if (recording.active) {
75
+ await syncRecorderStateToPage(page).catch(() => { });
76
+ deps.recordPageVisit(page, reason);
77
+ }
78
+ }
79
+ async function syncRecorderStateToPage(page) {
80
+ if (!page || page.isClosed())
81
+ return;
82
+ const recording = deps.getRecording();
83
+ await page.evaluate((options) => {
84
+ const runtime = window.__camoRecorderV1__;
85
+ if (!runtime || typeof runtime.setOptions !== 'function')
86
+ return null;
87
+ return runtime.setOptions(options);
88
+ }, { enabled: recording.enabled, overlay: recording.overlay });
89
+ }
90
+ async function destroyRecorderRuntimeOnPage(page) {
91
+ if (!page || page.isClosed())
92
+ return;
93
+ await page.evaluate(() => {
94
+ const runtime = window.__camoRecorderV1__;
95
+ if (!runtime || typeof runtime.destroy !== 'function')
96
+ return null;
97
+ return runtime.destroy();
98
+ });
99
+ }
100
+ return {
101
+ setupPageHooks,
102
+ bindRuntimeBridge,
103
+ bindRecorderBridge,
104
+ installRecorderRuntime,
105
+ syncRecorderStateToPage,
106
+ destroyRecorderRuntimeOnPage,
107
+ };
108
+ }
109
+ export function buildRecorderBootstrapScript() {
110
+ return RECORDER_BOOTSTRAP_SCRIPT;
111
+ }
112
+ const RECORDER_BOOTSTRAP_SCRIPT = `(() => {
113
+ const KEY = '__camoRecorderV1__';
114
+ if (window[KEY]) return window[KEY].getState();
115
+
116
+ const state = {
117
+ enabled: false,
118
+ overlay: false,
119
+ destroyed: false,
120
+ scrollAt: 0,
121
+ wheelAt: 0,
122
+ };
123
+ const listeners = [];
124
+ const OVERLAY_ID = '__camo_recorder_toggle__';
125
+
126
+ const now = () => Date.now();
127
+ const safeText = (value, max = 160) => {
128
+ if (typeof value !== 'string') return '';
129
+ return value.replace(/\\s+/g, ' ').trim().slice(0, max);
130
+ };
131
+ const SENSITIVE_TEXT_RE = /(pass(word)?|pwd|secret|token|otp|one[\\s_-]?time|验证码|校验码|短信|sms|手机|phone|mail|邮箱|email)/i;
132
+ const toNumber = (value, fallback = 0) => {
133
+ const n = Number(value);
134
+ return Number.isFinite(n) ? n : fallback;
135
+ };
136
+ const getAttr = (el, name) => {
137
+ if (!(el instanceof Element)) return '';
138
+ const value = el.getAttribute?.(name);
139
+ return typeof value === 'string' ? value : '';
140
+ };
141
+ const hasSensitiveHint = (value) => SENSITIVE_TEXT_RE.test(String(value || ''));
142
+ const isSensitiveElement = (el) => {
143
+ if (!(el instanceof Element)) return false;
144
+ const tag = String(el.tagName || '').toLowerCase();
145
+ const type = String((el instanceof HTMLInputElement ? el.type : getAttr(el, 'type')) || '').toLowerCase();
146
+ if (tag === 'input' && ['password', 'email', 'tel'].includes(type)) return true;
147
+ const autocomplete = String(getAttr(el, 'autocomplete') || '').toLowerCase();
148
+ if (autocomplete.includes('one-time-code') || autocomplete.includes('password')) return true;
149
+ const hint = [
150
+ el.id || '',
151
+ getAttr(el, 'name'),
152
+ getAttr(el, 'aria-label'),
153
+ getAttr(el, 'placeholder'),
154
+ autocomplete,
155
+ String(el.className || ''),
156
+ ].join(' ');
157
+ return hasSensitiveHint(hint);
158
+ };
159
+
160
+ const isVisible = (el) => {
161
+ if (!(el instanceof Element)) return false;
162
+ const rect = el.getBoundingClientRect?.();
163
+ if (!rect || rect.width <= 0 || rect.height <= 0) return false;
164
+ try {
165
+ const style = window.getComputedStyle(el);
166
+ if (!style) return false;
167
+ if (style.display === 'none') return false;
168
+ if (style.visibility === 'hidden' || style.visibility === 'collapse') return false;
169
+ const opacity = Number.parseFloat(String(style.opacity || '1'));
170
+ if (Number.isFinite(opacity) && opacity <= 0.01) return false;
171
+ } catch {
172
+ return false;
173
+ }
174
+ return true;
175
+ };
176
+
177
+ const buildSelectorPath = (el) => {
178
+ if (!(el instanceof Element)) return null;
179
+ const parts = [];
180
+ let cursor = el;
181
+ let depth = 0;
182
+ while (cursor && depth < 8) {
183
+ const tag = String(cursor.tagName || '').toLowerCase();
184
+ if (!tag) break;
185
+ const id = cursor.id ? '#' + cursor.id : '';
186
+ const cls = Array.from(cursor.classList || []).slice(0, 2).join('.');
187
+ let piece = tag + id + (cls ? '.' + cls : '');
188
+ if (!id) {
189
+ const parent = cursor.parentElement;
190
+ if (parent) {
191
+ const siblings = Array.from(parent.children).filter((item) => item.tagName === cursor.tagName);
192
+ if (siblings.length > 1) {
193
+ const nth = siblings.indexOf(cursor) + 1;
194
+ piece += ':nth-of-type(' + nth + ')';
195
+ }
196
+ }
197
+ }
198
+ parts.unshift(piece);
199
+ cursor = cursor.parentElement;
200
+ depth += 1;
201
+ if (id) break;
202
+ }
203
+ return parts.join(' > ');
204
+ };
205
+
206
+ const resolveElement = (target) => {
207
+ if (target instanceof Element) return target;
208
+ if (target && target.scrollingElement instanceof Element) return target.scrollingElement;
209
+ if (document.activeElement instanceof Element) return document.activeElement;
210
+ if (document.scrollingElement instanceof Element) return document.scrollingElement;
211
+ return document.documentElement instanceof Element ? document.documentElement : null;
212
+ };
213
+
214
+ const buildElementPayload = (target) => {
215
+ const el = resolveElement(target);
216
+ if (!(el instanceof Element)) return null;
217
+ const rect = el.getBoundingClientRect?.();
218
+ const attrs = {};
219
+ ['name', 'type', 'role', 'placeholder', 'aria-label'].forEach((key) => {
220
+ const value = el.getAttribute?.(key);
221
+ if (value) attrs[key] = String(value).slice(0, 120);
222
+ });
223
+ const sensitive = isSensitiveElement(el);
224
+ let valueSnippet = null;
225
+ const value = el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement ? el.value : null;
226
+ if (typeof value === 'string' && value.length > 0) {
227
+ valueSnippet = sensitive ? '[REDACTED]' : value.slice(0, 120);
228
+ }
229
+ return {
230
+ tag: String(el.tagName || '').toLowerCase(),
231
+ id: el.id || null,
232
+ classes: Array.from(el.classList || []).slice(0, 6),
233
+ selectorPath: buildSelectorPath(el),
234
+ textSnippet: safeText(el.textContent || '', 120),
235
+ attrs,
236
+ valueSnippet,
237
+ sensitive,
238
+ visible: isVisible(el),
239
+ rect: rect
240
+ ? {
241
+ x: Math.round(toNumber(rect.x, 0)),
242
+ y: Math.round(toNumber(rect.y, 0)),
243
+ width: Math.round(toNumber(rect.width, 0)),
244
+ height: Math.round(toNumber(rect.height, 0)),
245
+ }
246
+ : null,
247
+ };
248
+ };
249
+
250
+ const emit = (type, payload = {}) => {
251
+ if (typeof window.camo_recorder_dispatch !== 'function') return;
252
+ try {
253
+ window.camo_recorder_dispatch({
254
+ ts: now(),
255
+ type,
256
+ payload,
257
+ href: String(window.location?.href || ''),
258
+ title: safeText(String(document?.title || ''), 200),
259
+ });
260
+ } catch {
261
+ // ignore bridge errors
262
+ }
263
+ };
264
+
265
+ const shouldRecord = (event) => {
266
+ if (state.destroyed || !state.enabled) return false;
267
+ if (!event) return false;
268
+ if (typeof event.isTrusted === 'boolean' && !event.isTrusted) return false;
269
+ return true;
270
+ };
271
+
272
+ const onClick = (event) => {
273
+ if (!shouldRecord(event)) return;
274
+ emit('interaction.click', {
275
+ button: Number(event.button || 0),
276
+ buttons: Number(event.buttons || 0),
277
+ element: buildElementPayload(event.target),
278
+ });
279
+ };
280
+
281
+ const onKeyDown = (event) => {
282
+ if (!shouldRecord(event)) return;
283
+ const element = buildElementPayload(event.target || document.activeElement);
284
+ const isPrintable = typeof event.key === 'string' && event.key.length === 1;
285
+ const redactKey = !!element?.sensitive || (isPrintable && !event.ctrlKey && !event.metaKey && !event.altKey);
286
+ emit('interaction.keydown', {
287
+ key: redactKey ? '[REDACTED]' : String(event.key || ''),
288
+ code: String(event.code || ''),
289
+ ctrlKey: !!event.ctrlKey,
290
+ metaKey: !!event.metaKey,
291
+ altKey: !!event.altKey,
292
+ shiftKey: !!event.shiftKey,
293
+ redacted: redactKey,
294
+ element,
295
+ });
296
+ };
297
+
298
+ const onInput = (event) => {
299
+ if (!shouldRecord(event)) return;
300
+ const element = buildElementPayload(event.target || document.activeElement);
301
+ const rawData = typeof event.data === 'string' ? event.data : '';
302
+ const redactData = !!element?.sensitive;
303
+ emit('interaction.input', {
304
+ inputType: String(event.inputType || ''),
305
+ data: redactData ? '[REDACTED]' : safeText(rawData, 80),
306
+ dataLength: rawData.length,
307
+ redacted: redactData,
308
+ element,
309
+ });
310
+ };
311
+
312
+ const onWheel = (event) => {
313
+ if (!shouldRecord(event)) return;
314
+ const ts = now();
315
+ if (ts - state.wheelAt < 120) return;
316
+ state.wheelAt = ts;
317
+ emit('interaction.wheel', {
318
+ deltaX: toNumber(event.deltaX, 0),
319
+ deltaY: toNumber(event.deltaY, 0),
320
+ deltaMode: Number(event.deltaMode || 0),
321
+ element: buildElementPayload(event.target),
322
+ });
323
+ };
324
+
325
+ const onScroll = (event) => {
326
+ if (!shouldRecord(event)) return;
327
+ const ts = now();
328
+ if (ts - state.scrollAt < 150) return;
329
+ state.scrollAt = ts;
330
+ const target = resolveElement(event.target || document.scrollingElement);
331
+ const scrollTop = target && typeof target.scrollTop === 'number'
332
+ ? target.scrollTop
333
+ : (window.scrollY || document.documentElement.scrollTop || 0);
334
+ const scrollLeft = target && typeof target.scrollLeft === 'number'
335
+ ? target.scrollLeft
336
+ : (window.scrollX || document.documentElement.scrollLeft || 0);
337
+ emit('interaction.scroll', {
338
+ scrollTop: Math.round(toNumber(scrollTop, 0)),
339
+ scrollLeft: Math.round(toNumber(scrollLeft, 0)),
340
+ element: buildElementPayload(target),
341
+ });
342
+ };
343
+
344
+ const addListener = (target, type, handler, options) => {
345
+ target.addEventListener(type, handler, options);
346
+ listeners.push(() => {
347
+ try {
348
+ target.removeEventListener(type, handler, options);
349
+ } catch {
350
+ // ignore
351
+ }
352
+ });
353
+ };
354
+
355
+ const getOverlayButton = () => document.getElementById(OVERLAY_ID);
356
+ const applyOverlay = () => {
357
+ const existing = getOverlayButton();
358
+ if (existing && !state.overlay) {
359
+ existing.remove();
360
+ return;
361
+ }
362
+ if (!state.overlay) return;
363
+ const btn = existing || document.createElement('button');
364
+ btn.id = OVERLAY_ID;
365
+ btn.type = 'button';
366
+ btn.textContent = state.enabled ? 'REC ON' : 'REC OFF';
367
+ Object.assign(btn.style, {
368
+ position: 'fixed',
369
+ right: '16px',
370
+ bottom: '16px',
371
+ zIndex: '2147483647',
372
+ border: '0',
373
+ borderRadius: '999px',
374
+ background: state.enabled ? '#d63636' : '#5b6575',
375
+ color: '#fff',
376
+ padding: '8px 12px',
377
+ fontSize: '12px',
378
+ fontFamily: 'monospace',
379
+ cursor: 'pointer',
380
+ boxShadow: '0 4px 14px rgba(0,0,0,0.25)',
381
+ });
382
+ if (!existing) {
383
+ btn.addEventListener('click', () => {
384
+ state.enabled = !state.enabled;
385
+ applyOverlay();
386
+ emit('recording.toggled', { enabled: state.enabled, source: 'overlay' });
387
+ });
388
+ (document.body || document.documentElement || document).appendChild(btn);
389
+ }
390
+ };
391
+
392
+ addListener(document, 'click', onClick, true);
393
+ addListener(document, 'keydown', onKeyDown, true);
394
+ addListener(document, 'input', onInput, true);
395
+ addListener(window, 'wheel', onWheel, { capture: true, passive: true });
396
+ addListener(window, 'scroll', onScroll, { capture: true, passive: true });
397
+
398
+ const api = {
399
+ setOptions(options = {}) {
400
+ if (typeof options.enabled === 'boolean') {
401
+ state.enabled = options.enabled;
402
+ }
403
+ if (typeof options.overlay === 'boolean') {
404
+ state.overlay = options.overlay;
405
+ }
406
+ applyOverlay();
407
+ return this.getState();
408
+ },
409
+ getState() {
410
+ return {
411
+ ok: true,
412
+ enabled: !!state.enabled,
413
+ overlay: !!state.overlay,
414
+ href: String(window.location?.href || ''),
415
+ };
416
+ },
417
+ destroy() {
418
+ state.destroyed = true;
419
+ while (listeners.length) {
420
+ const dispose = listeners.pop();
421
+ try {
422
+ dispose && dispose();
423
+ } catch {
424
+ // ignore
425
+ }
426
+ }
427
+ const existing = getOverlayButton();
428
+ if (existing) existing.remove();
429
+ try {
430
+ delete window[KEY];
431
+ } catch {
432
+ window[KEY] = undefined;
433
+ }
434
+ return { ok: true };
435
+ },
436
+ };
437
+
438
+ window[KEY] = api;
439
+ applyOverlay();
440
+ emit('recording.runtime_ready', { enabled: state.enabled, overlay: state.overlay });
441
+ return api.getState();
442
+ })();`;
443
+ //# sourceMappingURL=page-hooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page-hooks.js","sourceRoot":"","sources":["../../../../../../modules/camo-backend/src/internal/browser-session/page-hooks.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,212 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ import { ensurePageRuntime } from '../pageRuntime.js';
3
+ import { resolveNavigationWaitUntil, normalizeUrl } from './utils.js';
4
+ export class BrowserSessionPageManagement {
5
+ deps;
6
+ constructor(deps) {
7
+ this.deps = deps;
8
+ }
9
+ tryOsNewTabShortcut() {
10
+ if (this.deps.isHeadless())
11
+ return false;
12
+ if (process.platform === 'darwin') {
13
+ const res = spawnSync('osascript', ['-e', 'tell application "System Events" to keystroke "t" using command down'], { windowsHide: true });
14
+ return res.status === 0;
15
+ }
16
+ if (process.platform === 'win32') {
17
+ const script = 'Add-Type -AssemblyName System.Windows.Forms; $ws = New-Object -ComObject WScript.Shell; $ws.SendKeys("^t");';
18
+ const res = spawnSync('powershell', ['-NoProfile', '-Command', script], { windowsHide: true });
19
+ return res.status === 0;
20
+ }
21
+ return false;
22
+ }
23
+ async ensurePrimaryPage() {
24
+ const ctx = this.deps.ensureContext();
25
+ const existing = this.deps.getActivePage();
26
+ if (existing) {
27
+ try {
28
+ await this.deps.ensurePageViewport(existing);
29
+ }
30
+ catch {
31
+ /* ignore */
32
+ }
33
+ return existing;
34
+ }
35
+ const page = await ctx.newPage();
36
+ this.deps.setActivePage(page);
37
+ this.deps.setupPageHooks(page);
38
+ try {
39
+ await this.deps.ensurePageViewport(page);
40
+ }
41
+ catch {
42
+ /* ignore */
43
+ }
44
+ return page;
45
+ }
46
+ async ensurePage(url) {
47
+ let page = await this.ensurePrimaryPage();
48
+ if (url) {
49
+ const current = this.deps.getCurrentUrl() || page.url();
50
+ if (!current || normalizeUrl(current) !== normalizeUrl(url)) {
51
+ await page.goto(url, { waitUntil: resolveNavigationWaitUntil() });
52
+ await ensurePageRuntime(page);
53
+ this.deps.recordLastKnownUrl(url);
54
+ page = await this.ensurePrimaryPage();
55
+ }
56
+ }
57
+ return page;
58
+ }
59
+ listPages() {
60
+ const ctx = this.deps.ensureContext();
61
+ const pages = ctx.pages().filter((p) => !p.isClosed());
62
+ const active = this.deps.getActivePage();
63
+ return pages.map((p, index) => ({
64
+ index,
65
+ url: p.url(),
66
+ active: active === p,
67
+ }));
68
+ }
69
+ async newPage(url, options = {}) {
70
+ const ctx = this.deps.ensureContext();
71
+ const isMac = process.platform === 'darwin';
72
+ const shortcut = isMac ? 'Meta+t' : 'Control+t';
73
+ let page = null;
74
+ const opener = this.deps.getActivePage() || ctx.pages()[0];
75
+ if (!opener)
76
+ throw new Error('no_opener_page');
77
+ await opener.bringToFront().catch(() => null);
78
+ const before = ctx.pages().filter((p) => !p.isClosed()).length;
79
+ for (let attempt = 1; attempt <= 3; attempt += 1) {
80
+ const waitPage = ctx.waitForEvent('page', { timeout: 8000 }).catch(() => null);
81
+ await opener.keyboard.press(shortcut).catch(() => null);
82
+ page = await waitPage;
83
+ const pagesNow = ctx.pages().filter((p) => !p.isClosed());
84
+ const after = pagesNow.length;
85
+ if (page && after > before)
86
+ break;
87
+ if (!page && after > before) {
88
+ page = pagesNow[pagesNow.length - 1] || null;
89
+ break;
90
+ }
91
+ await new Promise((r) => setTimeout(r, 250));
92
+ }
93
+ let after = ctx.pages().filter((p) => !p.isClosed()).length;
94
+ if (!page || after <= before) {
95
+ const waitPage = ctx.waitForEvent('page', { timeout: 8000 }).catch(() => null);
96
+ const osShortcutOk = this.tryOsNewTabShortcut();
97
+ if (osShortcutOk) {
98
+ page = await waitPage;
99
+ }
100
+ const pagesNow = ctx.pages().filter((p) => !p.isClosed());
101
+ after = pagesNow.length;
102
+ if (!page && after > before) {
103
+ page = pagesNow[pagesNow.length - 1] || null;
104
+ }
105
+ }
106
+ if (!page || after <= before) {
107
+ if (!options?.strictShortcut) {
108
+ try {
109
+ page = await ctx.newPage();
110
+ await page.waitForLoadState('domcontentloaded', { timeout: 8000 }).catch(() => null);
111
+ }
112
+ catch {
113
+ // ignore fallback errors
114
+ }
115
+ after = ctx.pages().filter((p) => !p.isClosed()).length;
116
+ if (!page && after > before) {
117
+ const pagesNow = ctx.pages().filter((p) => !p.isClosed());
118
+ page = pagesNow[pagesNow.length - 1] || null;
119
+ }
120
+ }
121
+ }
122
+ if (!page || after <= before) {
123
+ throw new Error('new_tab_failed');
124
+ }
125
+ this.deps.setupPageHooks(page);
126
+ this.deps.setActivePage(page);
127
+ try {
128
+ await this.deps.ensurePageViewport(page);
129
+ }
130
+ catch {
131
+ /* ignore */
132
+ }
133
+ try {
134
+ await this.deps.maybeCenterPage(page, { width: 1920, height: 1080 });
135
+ }
136
+ catch {
137
+ /* ignore */
138
+ }
139
+ try {
140
+ await page.bringToFront();
141
+ }
142
+ catch {
143
+ /* ignore */
144
+ }
145
+ if (url) {
146
+ await page.goto(url, { waitUntil: resolveNavigationWaitUntil() });
147
+ await ensurePageRuntime(page);
148
+ this.deps.recordLastKnownUrl(url);
149
+ }
150
+ const pages = ctx.pages().filter((p) => !p.isClosed());
151
+ return { index: Math.max(0, pages.indexOf(page)), url: page.url() };
152
+ }
153
+ async switchPage(index) {
154
+ const ctx = this.deps.ensureContext();
155
+ const pages = ctx.pages().filter((p) => !p.isClosed());
156
+ const idx = Number(index);
157
+ if (!Number.isFinite(idx) || idx < 0 || idx >= pages.length) {
158
+ throw new Error(`invalid_page_index: ${index}`);
159
+ }
160
+ const page = pages[idx];
161
+ this.deps.setActivePage(page);
162
+ try {
163
+ await this.deps.ensurePageViewport(page);
164
+ }
165
+ catch {
166
+ /* ignore */
167
+ }
168
+ try {
169
+ await page.bringToFront();
170
+ }
171
+ catch {
172
+ /* ignore */
173
+ }
174
+ await ensurePageRuntime(page, true).catch(() => { });
175
+ this.deps.recordLastKnownUrl(page.url());
176
+ return { index: idx, url: page.url() };
177
+ }
178
+ async closePage(index) {
179
+ const ctx = this.deps.ensureContext();
180
+ const pages = ctx.pages().filter((p) => !p.isClosed());
181
+ if (pages.length === 0) {
182
+ return { closedIndex: -1, activeIndex: -1, total: 0 };
183
+ }
184
+ const active = this.deps.getActivePage();
185
+ const requested = typeof index === 'number' && Number.isFinite(index) ? index : null;
186
+ const closedIndex = requested !== null ? requested : Math.max(0, pages.findIndex((p) => p === active));
187
+ if (closedIndex < 0 || closedIndex >= pages.length) {
188
+ throw new Error(`invalid_page_index: ${index}`);
189
+ }
190
+ const page = pages[closedIndex];
191
+ await page.close().catch(() => { });
192
+ const remaining = ctx.pages().filter((p) => !p.isClosed());
193
+ const nextIndex = remaining.length === 0 ? -1 : Math.min(Math.max(0, closedIndex - 1), remaining.length - 1);
194
+ if (nextIndex >= 0) {
195
+ const nextPage = remaining[nextIndex];
196
+ this.deps.setActivePage(nextPage);
197
+ try {
198
+ await nextPage.bringToFront();
199
+ }
200
+ catch {
201
+ /* ignore */
202
+ }
203
+ await ensurePageRuntime(nextPage, true).catch(() => { });
204
+ this.deps.recordLastKnownUrl(nextPage.url());
205
+ }
206
+ else {
207
+ this.deps.setActivePage(undefined);
208
+ }
209
+ return { closedIndex, activeIndex: nextIndex, total: remaining.length };
210
+ }
211
+ }
212
+ //# sourceMappingURL=page-management.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page-management.js","sourceRoot":"","sources":["../../../../../../modules/camo-backend/src/internal/browser-session/page-management.ts"],"names":[],"mappings":"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"}