libretto 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +128 -126
- package/dist/cli/cli.js +2 -0
- package/dist/cli/commands/browser.js +4 -1
- package/dist/cli/commands/execution.js +21 -6
- package/dist/cli/commands/logs.js +36 -8
- package/dist/cli/commands/snapshot.js +14 -7
- package/dist/cli/core/browser.js +89 -253
- package/dist/cli/core/session-telemetry.js +491 -0
- package/dist/cli/core/telemetry.js +18 -6
- package/dist/cli/workers/run-integration-runtime.js +19 -1
- package/dist/index.cjs +2 -6
- package/dist/index.d.cts +2 -5
- package/dist/index.d.ts +2 -5
- package/dist/index.js +2 -5
- package/dist/runtime/download/download.d.cts +2 -2
- package/dist/runtime/download/download.d.ts +2 -2
- package/dist/runtime/extract/extract.cjs +2 -1
- package/dist/runtime/extract/extract.d.cts +5 -5
- package/dist/runtime/extract/extract.d.ts +5 -5
- package/dist/runtime/extract/extract.js +2 -1
- package/dist/runtime/network/network.d.cts +6 -6
- package/dist/runtime/network/network.d.ts +6 -6
- package/dist/runtime/recovery/agent.cjs +12 -7
- package/dist/runtime/recovery/agent.d.cts +2 -2
- package/dist/runtime/recovery/agent.d.ts +2 -2
- package/dist/runtime/recovery/agent.js +12 -7
- package/dist/runtime/recovery/errors.cjs +8 -6
- package/dist/runtime/recovery/errors.d.cts +2 -2
- package/dist/runtime/recovery/errors.d.ts +2 -2
- package/dist/runtime/recovery/errors.js +8 -6
- package/dist/runtime/recovery/recovery.cjs +5 -3
- package/dist/runtime/recovery/recovery.d.cts +2 -2
- package/dist/runtime/recovery/recovery.d.ts +2 -2
- package/dist/runtime/recovery/recovery.js +5 -3
- package/dist/shared/instrumentation/instrument.d.cts +2 -2
- package/dist/shared/instrumentation/instrument.d.ts +2 -2
- package/dist/shared/llm/types.d.cts +5 -5
- package/dist/shared/llm/types.d.ts +5 -5
- package/dist/shared/logger/index.cjs +2 -0
- package/dist/shared/logger/index.d.cts +1 -1
- package/dist/shared/logger/index.d.ts +1 -1
- package/dist/shared/logger/index.js +2 -1
- package/dist/shared/logger/logger.cjs +15 -2
- package/dist/shared/logger/logger.d.cts +13 -1
- package/dist/shared/logger/logger.d.ts +13 -1
- package/dist/shared/logger/logger.js +13 -1
- package/dist/shared/state/session-state.d.cts +2 -2
- package/dist/shared/state/session-state.d.ts +2 -2
- package/package.json +15 -11
- package/scripts/postinstall.mjs +48 -0
- package/skill/SKILL.md +438 -0
- package/skill/code-generation-rules.md +190 -0
- package/skill/integration-approach-selection.md +174 -0
- package/dist/runtime/step/index.cjs +0 -31
- package/dist/runtime/step/index.d.cts +0 -7
- package/dist/runtime/step/index.d.ts +0 -7
- package/dist/runtime/step/index.js +0 -6
- package/dist/runtime/step/runner.cjs +0 -208
- package/dist/runtime/step/runner.d.cts +0 -16
- package/dist/runtime/step/runner.d.ts +0 -16
- package/dist/runtime/step/runner.js +0 -187
- package/dist/runtime/step/step.cjs +0 -67
- package/dist/runtime/step/step.d.cts +0 -23
- package/dist/runtime/step/step.d.ts +0 -23
- package/dist/runtime/step/step.js +0 -43
- package/dist/runtime/step/types.cjs +0 -16
- package/dist/runtime/step/types.d.cts +0 -72
- package/dist/runtime/step/types.d.ts +0 -72
- package/dist/runtime/step/types.js +0 -0
package/dist/cli/core/browser.js
CHANGED
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
readSessionState,
|
|
18
18
|
writeSessionState
|
|
19
19
|
} from "./session.js";
|
|
20
|
+
import { installSessionTelemetry } from "./session-telemetry.js";
|
|
20
21
|
async function pickFreePort() {
|
|
21
22
|
return await new Promise((resolve2, reject) => {
|
|
22
23
|
const server = createServer();
|
|
@@ -76,6 +77,10 @@ async function tryConnectToPort(port, logger, timeoutMs = 5e3) {
|
|
|
76
77
|
return null;
|
|
77
78
|
}
|
|
78
79
|
}
|
|
80
|
+
function isOperationalPage(page) {
|
|
81
|
+
const url = page.url();
|
|
82
|
+
return !url.startsWith("devtools://") && !url.startsWith("chrome-error://");
|
|
83
|
+
}
|
|
79
84
|
function disconnectBrowser(browser, logger, session) {
|
|
80
85
|
logger.info("cdp-disconnect", { session });
|
|
81
86
|
try {
|
|
@@ -84,7 +89,46 @@ function disconnectBrowser(browser, logger, session) {
|
|
|
84
89
|
logger.warn("cdp-disconnect-already-closed", { error: err });
|
|
85
90
|
}
|
|
86
91
|
}
|
|
87
|
-
|
|
92
|
+
function resolveOperationalPages(browser) {
|
|
93
|
+
return browser.contexts().flatMap((context) => context.pages()).filter(isOperationalPage);
|
|
94
|
+
}
|
|
95
|
+
async function resolvePageId(page) {
|
|
96
|
+
const cdpSession = await page.context().newCDPSession(page);
|
|
97
|
+
try {
|
|
98
|
+
const targetInfo = await cdpSession.send("Target.getTargetInfo");
|
|
99
|
+
const targetId = targetInfo?.targetInfo?.targetId;
|
|
100
|
+
if (typeof targetId !== "string" || targetId.length === 0) {
|
|
101
|
+
throw new Error(`Could not resolve target id for page at URL "${page.url()}".`);
|
|
102
|
+
}
|
|
103
|
+
return targetId;
|
|
104
|
+
} finally {
|
|
105
|
+
await cdpSession.detach();
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
async function resolvePageReferences(pages) {
|
|
109
|
+
const refs = await Promise.all(
|
|
110
|
+
pages.map(async (page) => {
|
|
111
|
+
const id = await resolvePageId(page);
|
|
112
|
+
return { id, page };
|
|
113
|
+
})
|
|
114
|
+
);
|
|
115
|
+
return refs;
|
|
116
|
+
}
|
|
117
|
+
async function listOpenPages(session, logger) {
|
|
118
|
+
const { browser, page: activePage } = await connect(session, logger);
|
|
119
|
+
try {
|
|
120
|
+
const pages = browser.contexts().flatMap((ctx) => ctx.pages()).filter(isOperationalPage);
|
|
121
|
+
const pageRefs = await resolvePageReferences(pages);
|
|
122
|
+
return pageRefs.map(({ id, page }) => ({
|
|
123
|
+
id,
|
|
124
|
+
url: page.url(),
|
|
125
|
+
active: page === activePage
|
|
126
|
+
}));
|
|
127
|
+
} finally {
|
|
128
|
+
disconnectBrowser(browser, logger, session);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
async function connect(session, logger, timeoutMs = 1e4, options) {
|
|
88
132
|
logger.info("connect", { session, timeoutMs });
|
|
89
133
|
const state = readSessionStateOrThrow(session);
|
|
90
134
|
const browser = await tryConnectToPort(state.port, logger, timeoutMs);
|
|
@@ -106,10 +150,7 @@ async function connect(session, logger, timeoutMs = 1e4) {
|
|
|
106
150
|
throw new Error("No browser context found.");
|
|
107
151
|
}
|
|
108
152
|
const allPages = contexts.flatMap((c) => c.pages());
|
|
109
|
-
const pages =
|
|
110
|
-
const url = p.url();
|
|
111
|
-
return !url.startsWith("devtools://") && !url.startsWith("chrome-error://");
|
|
112
|
-
});
|
|
153
|
+
const pages = resolveOperationalPages(browser);
|
|
113
154
|
logger.info("connect-pages", {
|
|
114
155
|
session,
|
|
115
156
|
totalPages: allPages.length,
|
|
@@ -123,7 +164,19 @@ async function connect(session, logger, timeoutMs = 1e4) {
|
|
|
123
164
|
});
|
|
124
165
|
throw new Error("No pages found.");
|
|
125
166
|
}
|
|
126
|
-
|
|
167
|
+
if (options?.requireSinglePage && !options.pageId && pages.length > 1) {
|
|
168
|
+
throw new Error(
|
|
169
|
+
`Multiple pages are open in session "${session}". Pass --page <id> to target a page (run "libretto-cli pages --session ${session}" to list ids).`
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
const pageRefs = await resolvePageReferences(pages);
|
|
173
|
+
const pageRef = options?.pageId ? pageRefs.find((ref) => ref.id === options.pageId) ?? null : pageRefs[pageRefs.length - 1];
|
|
174
|
+
if (!pageRef) {
|
|
175
|
+
throw new Error(
|
|
176
|
+
`Page "${options?.pageId}" was not found in session "${session}". Run "libretto-cli pages --session ${session}" to list ids.`
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
const page = pageRef.page;
|
|
127
180
|
const context = page.context();
|
|
128
181
|
page.on("close", () => {
|
|
129
182
|
logger.error("page-closed-during-command", {
|
|
@@ -145,7 +198,20 @@ async function connect(session, logger, timeoutMs = 1e4) {
|
|
|
145
198
|
});
|
|
146
199
|
});
|
|
147
200
|
logger.info("connect-success", { session, pageUrl: page.url() });
|
|
148
|
-
return { browser, context, page };
|
|
201
|
+
return { browser, context, page, pageId: pageRef.id };
|
|
202
|
+
}
|
|
203
|
+
async function runPages(session, logger) {
|
|
204
|
+
logger.info("pages-start", { session });
|
|
205
|
+
const pageSummaries = await listOpenPages(session, logger);
|
|
206
|
+
if (pageSummaries.length === 0) {
|
|
207
|
+
console.log("No pages found.");
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
console.log("Open pages:");
|
|
211
|
+
pageSummaries.forEach((pageSummary) => {
|
|
212
|
+
const activeSuffix = pageSummary.active ? " active=true" : "";
|
|
213
|
+
console.log(` id=${pageSummary.id} url=${pageSummary.url}${activeSuffix}`);
|
|
214
|
+
});
|
|
149
215
|
}
|
|
150
216
|
async function runOpen(rawUrl, headed, session, logger) {
|
|
151
217
|
const url = normalizeUrl(rawUrl);
|
|
@@ -181,43 +247,21 @@ async function runOpen(rawUrl, headed, session, logger) {
|
|
|
181
247
|
const launcherCode = `
|
|
182
248
|
import { chromium } from 'playwright';
|
|
183
249
|
import { appendFileSync, mkdirSync } from 'node:fs';
|
|
250
|
+
import { dirname } from 'node:path';
|
|
184
251
|
|
|
185
252
|
const LOG_FILE = '${escapedLogPath}';
|
|
186
253
|
const NETWORK_LOG = '${escapedNetworkLogPath}';
|
|
187
254
|
const ACTIONS_LOG = '${escapedActionsLogPath}';
|
|
188
|
-
mkdirSync(NETWORK_LOG
|
|
255
|
+
mkdirSync(dirname(NETWORK_LOG), { recursive: true });
|
|
189
256
|
|
|
190
|
-
|
|
191
|
-
async function logNetworkResponse(response) {
|
|
192
|
-
try {
|
|
193
|
-
const req = response.request();
|
|
194
|
-
const url = req.url();
|
|
195
|
-
if (STATIC_EXT_RE.test(url) || url.startsWith('chrome-extension://')) return;
|
|
196
|
-
let responseBody = null;
|
|
197
|
-
try {
|
|
198
|
-
const buf = await response.body();
|
|
199
|
-
responseBody = buf.toString('utf-8');
|
|
200
|
-
} catch {}
|
|
201
|
-
const entry = JSON.stringify({
|
|
202
|
-
ts: new Date().toISOString(),
|
|
203
|
-
method: req.method(),
|
|
204
|
-
url,
|
|
205
|
-
status: response.status(),
|
|
206
|
-
contentType: response.headers()['content-type'] || null,
|
|
207
|
-
postData: req.method() === 'POST' || req.method() === 'PUT' || req.method() === 'PATCH'
|
|
208
|
-
? (req.postData() || '').substring(0, 2000)
|
|
209
|
-
: undefined,
|
|
210
|
-
responseBody,
|
|
211
|
-
});
|
|
212
|
-
appendFileSync(NETWORK_LOG, entry + '\\n');
|
|
213
|
-
} catch {}
|
|
214
|
-
}
|
|
257
|
+
${installSessionTelemetry.toString()}
|
|
215
258
|
|
|
216
259
|
function logAction(entry) {
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
260
|
+
appendFileSync(ACTIONS_LOG, JSON.stringify(entry) + '\\n');
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function logNetwork(entry) {
|
|
264
|
+
appendFileSync(NETWORK_LOG, JSON.stringify(entry) + '\\n');
|
|
221
265
|
}
|
|
222
266
|
|
|
223
267
|
function childLog(level, event, data = {}) {
|
|
@@ -234,185 +278,6 @@ function childLog(level, event, data = {}) {
|
|
|
234
278
|
} catch {}
|
|
235
279
|
}
|
|
236
280
|
|
|
237
|
-
async function setupActionTracking(p) {
|
|
238
|
-
await p.exposeFunction('__btActionLog', (jsonStr) => {
|
|
239
|
-
try { logAction({ ...JSON.parse(jsonStr), source: 'user' }); } catch {}
|
|
240
|
-
});
|
|
241
|
-
|
|
242
|
-
await p.addInitScript(() => {
|
|
243
|
-
if (window.__btDomListenersInstalled) return;
|
|
244
|
-
window.__btDomListenersInstalled = true;
|
|
245
|
-
|
|
246
|
-
function identify(el) {
|
|
247
|
-
if (!el || !el.tagName) return '';
|
|
248
|
-
var tid = el.getAttribute('data-testid');
|
|
249
|
-
if (tid) return '[data-testid="' + tid + '"]';
|
|
250
|
-
var role = el.getAttribute('role') || '';
|
|
251
|
-
var id = el.id;
|
|
252
|
-
if (role && id) return role + '#' + id;
|
|
253
|
-
var name = el.getAttribute('aria-label') || (el.textContent || '').trim().slice(0, 30) || '';
|
|
254
|
-
if (role && name) return role + ' "' + name + '"';
|
|
255
|
-
var tag = el.tagName.toLowerCase();
|
|
256
|
-
var cls = el.className && typeof el.className === 'string' ? '.' + el.className.trim().split(/\\s+/).slice(0, 2).join('.') : '';
|
|
257
|
-
return tag + cls;
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
var clickTimer = null;
|
|
261
|
-
var pendingClick = null;
|
|
262
|
-
|
|
263
|
-
document.addEventListener('click', function(e) {
|
|
264
|
-
if (window.__btApiActionInProgress) return;
|
|
265
|
-
var target = e.target;
|
|
266
|
-
var sel = identify(target);
|
|
267
|
-
if (target.type === 'checkbox') {
|
|
268
|
-
if (typeof window.__btActionLog === 'function') {
|
|
269
|
-
window.__btActionLog(JSON.stringify({ action: target.checked ? 'check' : 'uncheck', selector: sel, success: true }));
|
|
270
|
-
}
|
|
271
|
-
return;
|
|
272
|
-
}
|
|
273
|
-
pendingClick = { selector: sel };
|
|
274
|
-
if (clickTimer) clearTimeout(clickTimer);
|
|
275
|
-
clickTimer = setTimeout(function() {
|
|
276
|
-
if (pendingClick && typeof window.__btActionLog === 'function') {
|
|
277
|
-
window.__btActionLog(JSON.stringify({ action: 'click', selector: pendingClick.selector, success: true }));
|
|
278
|
-
}
|
|
279
|
-
pendingClick = null;
|
|
280
|
-
clickTimer = null;
|
|
281
|
-
}, 200);
|
|
282
|
-
}, true);
|
|
283
|
-
|
|
284
|
-
document.addEventListener('dblclick', function(e) {
|
|
285
|
-
if (window.__btApiActionInProgress) return;
|
|
286
|
-
if (clickTimer) { clearTimeout(clickTimer); clickTimer = null; pendingClick = null; }
|
|
287
|
-
var sel = identify(e.target);
|
|
288
|
-
if (typeof window.__btActionLog === 'function') {
|
|
289
|
-
window.__btActionLog(JSON.stringify({ action: 'dblclick', selector: sel, success: true }));
|
|
290
|
-
}
|
|
291
|
-
}, true);
|
|
292
|
-
|
|
293
|
-
var inputTimers = new WeakMap();
|
|
294
|
-
document.addEventListener('input', function(e) {
|
|
295
|
-
if (window.__btApiActionInProgress) return;
|
|
296
|
-
var target = e.target;
|
|
297
|
-
var sel = identify(target);
|
|
298
|
-
if (target.tagName === 'SELECT') {
|
|
299
|
-
if (typeof window.__btActionLog === 'function') {
|
|
300
|
-
window.__btActionLog(JSON.stringify({ action: 'selectOption', selector: sel, value: target.value, success: true }));
|
|
301
|
-
}
|
|
302
|
-
return;
|
|
303
|
-
}
|
|
304
|
-
var existing = inputTimers.get(target);
|
|
305
|
-
if (existing) clearTimeout(existing);
|
|
306
|
-
inputTimers.set(target, setTimeout(function() {
|
|
307
|
-
inputTimers.delete(target);
|
|
308
|
-
if (typeof window.__btActionLog === 'function') {
|
|
309
|
-
window.__btActionLog(JSON.stringify({ action: 'fill', selector: sel, value: (target.value || '').slice(0, 100), success: true }));
|
|
310
|
-
}
|
|
311
|
-
}, 500));
|
|
312
|
-
}, true);
|
|
313
|
-
|
|
314
|
-
var SPECIAL_KEYS = ['Enter','Escape','Tab','Backspace','Delete','ArrowUp','ArrowDown','ArrowLeft','ArrowRight','Home','End','PageUp','PageDown','F1','F2','F3','F4','F5','F6','F7','F8','F9','F10','F11','F12'];
|
|
315
|
-
document.addEventListener('keydown', function(e) {
|
|
316
|
-
if (window.__btApiActionInProgress) return;
|
|
317
|
-
var isShortcut = e.ctrlKey || e.metaKey || e.altKey;
|
|
318
|
-
if (!isShortcut && SPECIAL_KEYS.indexOf(e.key) === -1) return;
|
|
319
|
-
var sel = identify(e.target);
|
|
320
|
-
var keyDesc = (e.ctrlKey ? 'Ctrl+' : '') + (e.metaKey ? 'Meta+' : '') + (e.altKey ? 'Alt+' : '') + (e.shiftKey ? 'Shift+' : '') + e.key;
|
|
321
|
-
if (typeof window.__btActionLog === 'function') {
|
|
322
|
-
window.__btActionLog(JSON.stringify({ action: 'press', selector: sel, value: keyDesc, success: true }));
|
|
323
|
-
}
|
|
324
|
-
}, true);
|
|
325
|
-
|
|
326
|
-
var scrollTimer = null;
|
|
327
|
-
document.addEventListener('scroll', function() {
|
|
328
|
-
if (window.__btApiActionInProgress) return;
|
|
329
|
-
if (scrollTimer) clearTimeout(scrollTimer);
|
|
330
|
-
scrollTimer = setTimeout(function() {
|
|
331
|
-
scrollTimer = null;
|
|
332
|
-
if (typeof window.__btActionLog === 'function') {
|
|
333
|
-
window.__btActionLog(JSON.stringify({ action: 'scroll', selector: 'document', value: 'y=' + window.scrollY, success: true }));
|
|
334
|
-
}
|
|
335
|
-
}, 300);
|
|
336
|
-
}, true);
|
|
337
|
-
});
|
|
338
|
-
|
|
339
|
-
var PAGE_ACTIONS = ['click', 'dblclick', 'fill', 'type', 'press', 'check', 'uncheck', 'selectOption', 'hover', 'focus'];
|
|
340
|
-
var NAV_ACTIONS = ['goto', 'reload', 'goBack', 'goForward'];
|
|
341
|
-
|
|
342
|
-
for (var m of PAGE_ACTIONS) {
|
|
343
|
-
(function(method) {
|
|
344
|
-
var orig = p[method].bind(p);
|
|
345
|
-
p[method] = async function() {
|
|
346
|
-
var args = Array.from(arguments);
|
|
347
|
-
var start = Date.now();
|
|
348
|
-
try { await p.evaluate(function() { window.__btApiActionInProgress = true; }); } catch {}
|
|
349
|
-
try {
|
|
350
|
-
var result = await orig.apply(null, args);
|
|
351
|
-
logAction({ action: method, source: 'agent', selector: typeof args[0] === 'string' ? args[0] : undefined, value: args[1] !== undefined ? String(args[1]).slice(0, 100) : undefined, duration: Date.now() - start, success: true });
|
|
352
|
-
return result;
|
|
353
|
-
} catch (err) {
|
|
354
|
-
logAction({ action: method, source: 'agent', selector: typeof args[0] === 'string' ? args[0] : undefined, duration: Date.now() - start, success: false, error: err.message });
|
|
355
|
-
throw err;
|
|
356
|
-
} finally {
|
|
357
|
-
try { await p.evaluate(function() { window.__btApiActionInProgress = false; }); } catch {}
|
|
358
|
-
}
|
|
359
|
-
};
|
|
360
|
-
})(m);
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
for (var m of NAV_ACTIONS) {
|
|
364
|
-
(function(method) {
|
|
365
|
-
var orig = p[method].bind(p);
|
|
366
|
-
p[method] = async function() {
|
|
367
|
-
var args = Array.from(arguments);
|
|
368
|
-
var start = Date.now();
|
|
369
|
-
try {
|
|
370
|
-
var result = await orig.apply(null, args);
|
|
371
|
-
logAction({ action: method, source: 'agent', url: typeof args[0] === 'string' ? args[0] : p.url(), duration: Date.now() - start, success: true });
|
|
372
|
-
return result;
|
|
373
|
-
} catch (err) {
|
|
374
|
-
logAction({ action: method, source: 'agent', url: typeof args[0] === 'string' ? args[0] : undefined, duration: Date.now() - start, success: false, error: err.message });
|
|
375
|
-
throw err;
|
|
376
|
-
}
|
|
377
|
-
};
|
|
378
|
-
})(m);
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
var LOCATOR_FACTORIES = ['locator', 'getByRole', 'getByText', 'getByLabel', 'getByPlaceholder', 'getByAltText', 'getByTitle', 'getByTestId'];
|
|
382
|
-
for (var f of LOCATOR_FACTORIES) {
|
|
383
|
-
(function(factory) {
|
|
384
|
-
var orig = p[factory].bind(p);
|
|
385
|
-
p[factory] = function() {
|
|
386
|
-
var args = Array.from(arguments);
|
|
387
|
-
var locator = orig.apply(null, args);
|
|
388
|
-
var hint = factory + '(' + args.map(function(a) { return typeof a === 'string' ? a : JSON.stringify(a); }).join(', ') + ')';
|
|
389
|
-
for (var am of PAGE_ACTIONS) {
|
|
390
|
-
(function(actMethod) {
|
|
391
|
-
if (typeof locator[actMethod] !== 'function') return;
|
|
392
|
-
var origAct = locator[actMethod].bind(locator);
|
|
393
|
-
locator[actMethod] = async function() {
|
|
394
|
-
var actArgs = Array.from(arguments);
|
|
395
|
-
var start = Date.now();
|
|
396
|
-
try { await p.evaluate(function() { window.__btApiActionInProgress = true; }); } catch {}
|
|
397
|
-
try {
|
|
398
|
-
var result = await origAct.apply(null, actArgs);
|
|
399
|
-
logAction({ action: actMethod, source: 'agent', selector: hint, value: actArgs[0] !== undefined ? String(actArgs[0]).slice(0, 100) : undefined, duration: Date.now() - start, success: true });
|
|
400
|
-
return result;
|
|
401
|
-
} catch (err) {
|
|
402
|
-
logAction({ action: actMethod, source: 'agent', selector: hint, duration: Date.now() - start, success: false, error: err.message });
|
|
403
|
-
throw err;
|
|
404
|
-
} finally {
|
|
405
|
-
try { await p.evaluate(function() { window.__btApiActionInProgress = false; }); } catch {}
|
|
406
|
-
}
|
|
407
|
-
};
|
|
408
|
-
})(am);
|
|
409
|
-
}
|
|
410
|
-
return locator;
|
|
411
|
-
};
|
|
412
|
-
})(f);
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
|
|
416
281
|
const browser = await chromium.launch({
|
|
417
282
|
headless: ${!headed},
|
|
418
283
|
args: ['--disable-blink-features=AutomationControlled', '--remote-debugging-port=${port}', '--remote-debugging-address=127.0.0.1', '--no-focus-on-check'],
|
|
@@ -432,43 +297,12 @@ const page = await context.newPage();
|
|
|
432
297
|
page.setDefaultTimeout(30000);
|
|
433
298
|
page.setDefaultNavigationTimeout(45000);
|
|
434
299
|
|
|
435
|
-
await
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
if (msg.type() === 'error' || msg.type() === 'warning') {
|
|
442
|
-
childLog(msg.type() === 'error' ? 'error' : 'warn', 'console-' + msg.type(), { text: msg.text(), url: page.url() });
|
|
443
|
-
}
|
|
444
|
-
});
|
|
445
|
-
page.on('framenavigated', (frame) => {
|
|
446
|
-
if (frame === page.mainFrame()) {
|
|
447
|
-
childLog('info', 'page-navigated', { url: frame.url() });
|
|
448
|
-
logAction({ action: 'navigate', source: 'agent', url: frame.url(), success: true });
|
|
449
|
-
}
|
|
450
|
-
});
|
|
451
|
-
page.on('requestfailed', (req) => {
|
|
452
|
-
const failure = req.failure();
|
|
453
|
-
childLog('warn', 'request-failed', { url: req.url(), method: req.method(), errorText: failure?.errorText });
|
|
454
|
-
});
|
|
455
|
-
page.on('response', logNetworkResponse);
|
|
456
|
-
page.on('popup', (popup) => logAction({ action: 'popup', source: 'agent', url: popup.url(), success: true }));
|
|
457
|
-
page.on('dialog', (dialog) => logAction({ action: 'dialog', source: 'agent', value: dialog.type() + ': ' + dialog.message().slice(0, 500), success: true }));
|
|
458
|
-
|
|
459
|
-
context.on('page', async (newPage) => {
|
|
460
|
-
childLog('info', 'new-page-created', { url: newPage.url() });
|
|
461
|
-
newPage.on('crash', () => childLog('error', 'page-crash', { url: newPage.url() }));
|
|
462
|
-
newPage.on('close', () => childLog('info', 'page-close', { url: newPage.url(), trace: new Error('page-close-trace').stack }));
|
|
463
|
-
newPage.on('response', logNetworkResponse);
|
|
464
|
-
newPage.on('popup', (popup) => logAction({ action: 'popup', source: 'agent', url: popup.url(), success: true }));
|
|
465
|
-
newPage.on('dialog', (dialog) => logAction({ action: 'dialog', source: 'agent', value: dialog.type() + ': ' + dialog.message().slice(0, 500), success: true }));
|
|
466
|
-
newPage.on('framenavigated', (frame) => {
|
|
467
|
-
if (frame === newPage.mainFrame()) logAction({ action: 'navigate', source: 'agent', url: frame.url(), success: true });
|
|
468
|
-
});
|
|
469
|
-
try { await setupActionTracking(newPage); } catch (err) {
|
|
470
|
-
childLog('warn', 'action-tracking-setup-failed', { url: newPage.url(), error: err.message });
|
|
471
|
-
}
|
|
300
|
+
await installSessionTelemetry({
|
|
301
|
+
context,
|
|
302
|
+
initialPage: page,
|
|
303
|
+
includeUserDomActions: true,
|
|
304
|
+
logAction,
|
|
305
|
+
logNetwork,
|
|
472
306
|
});
|
|
473
307
|
|
|
474
308
|
|
|
@@ -678,10 +512,12 @@ export {
|
|
|
678
512
|
getProfilePath,
|
|
679
513
|
getScreenshotBaseName,
|
|
680
514
|
hasProfile,
|
|
515
|
+
listOpenPages,
|
|
681
516
|
normalizeDomain,
|
|
682
517
|
normalizeUrl,
|
|
683
518
|
resolvePath,
|
|
684
519
|
runClose,
|
|
685
520
|
runOpen,
|
|
521
|
+
runPages,
|
|
686
522
|
runSave
|
|
687
523
|
};
|