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
|
@@ -0,0 +1,491 @@
|
|
|
1
|
+
async function installSessionTelemetry(options) {
|
|
2
|
+
const STATIC_EXT_RE = /\.(css|js|png|jpg|jpeg|gif|woff|woff2|ttf|ico|svg)(\?|$)/i;
|
|
3
|
+
const { context, initialPage, logAction, logNetwork } = options;
|
|
4
|
+
const includeUserDomActions = options.includeUserDomActions ?? false;
|
|
5
|
+
const pageIdCache = /* @__PURE__ */ new WeakMap();
|
|
6
|
+
const wrappedPages = /* @__PURE__ */ new WeakSet();
|
|
7
|
+
const exposedPages = /* @__PURE__ */ new WeakSet();
|
|
8
|
+
const resolvePageId = async (page) => {
|
|
9
|
+
if (pageIdCache.has(page)) return pageIdCache.get(page);
|
|
10
|
+
const cdpSession = await context.newCDPSession(page);
|
|
11
|
+
try {
|
|
12
|
+
const targetInfo = await cdpSession.send("Target.getTargetInfo");
|
|
13
|
+
const targetId = targetInfo?.targetInfo?.targetId;
|
|
14
|
+
if (typeof targetId !== "string" || targetId.length === 0) {
|
|
15
|
+
throw new Error(`Could not resolve target id for page at URL "${page.url()}".`);
|
|
16
|
+
}
|
|
17
|
+
pageIdCache.set(page, targetId);
|
|
18
|
+
return targetId;
|
|
19
|
+
} finally {
|
|
20
|
+
await cdpSession.detach();
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
const emitAction = (entry) => {
|
|
24
|
+
logAction({
|
|
25
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
26
|
+
...entry
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
const emitNetwork = (entry) => {
|
|
30
|
+
logNetwork({
|
|
31
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
32
|
+
...entry
|
|
33
|
+
});
|
|
34
|
+
};
|
|
35
|
+
const markApiActionInProgress = async (page, inProgress) => {
|
|
36
|
+
await page.evaluate((flag) => {
|
|
37
|
+
window.__btApiActionInProgress = flag;
|
|
38
|
+
}, inProgress);
|
|
39
|
+
};
|
|
40
|
+
const wrapLocator = (locator, page, pageId) => {
|
|
41
|
+
if (locator.__librettoActionLogged) return locator;
|
|
42
|
+
locator.__librettoActionLogged = true;
|
|
43
|
+
const locatorActionMethods = [
|
|
44
|
+
"click",
|
|
45
|
+
"dblclick",
|
|
46
|
+
"fill",
|
|
47
|
+
"type",
|
|
48
|
+
"press",
|
|
49
|
+
"check",
|
|
50
|
+
"uncheck",
|
|
51
|
+
"selectOption",
|
|
52
|
+
"hover",
|
|
53
|
+
"focus",
|
|
54
|
+
"scrollIntoViewIfNeeded",
|
|
55
|
+
"waitFor",
|
|
56
|
+
"innerHTML",
|
|
57
|
+
"innerText",
|
|
58
|
+
"textContent",
|
|
59
|
+
"inputValue",
|
|
60
|
+
"isChecked",
|
|
61
|
+
"isDisabled",
|
|
62
|
+
"isEditable",
|
|
63
|
+
"isEnabled",
|
|
64
|
+
"isHidden",
|
|
65
|
+
"isVisible",
|
|
66
|
+
"count",
|
|
67
|
+
"boundingBox",
|
|
68
|
+
"screenshot",
|
|
69
|
+
"evaluate",
|
|
70
|
+
"evaluateAll",
|
|
71
|
+
"evaluateHandle",
|
|
72
|
+
"getAttribute",
|
|
73
|
+
"dispatchEvent",
|
|
74
|
+
"setInputFiles",
|
|
75
|
+
"selectText",
|
|
76
|
+
"dragTo",
|
|
77
|
+
"highlight",
|
|
78
|
+
"tap"
|
|
79
|
+
];
|
|
80
|
+
const locatorReturningMethods = [
|
|
81
|
+
"first",
|
|
82
|
+
"last",
|
|
83
|
+
"locator",
|
|
84
|
+
"getByRole",
|
|
85
|
+
"getByText",
|
|
86
|
+
"getByLabel",
|
|
87
|
+
"getByPlaceholder",
|
|
88
|
+
"getByAltText",
|
|
89
|
+
"getByTitle",
|
|
90
|
+
"getByTestId",
|
|
91
|
+
"filter",
|
|
92
|
+
"and",
|
|
93
|
+
"or"
|
|
94
|
+
];
|
|
95
|
+
for (const actMethod of locatorActionMethods) {
|
|
96
|
+
if (typeof locator[actMethod] !== "function") continue;
|
|
97
|
+
const originalAction = locator[actMethod].bind(locator);
|
|
98
|
+
locator[actMethod] = async (...actionArgs) => {
|
|
99
|
+
const start = Date.now();
|
|
100
|
+
await markApiActionInProgress(page, true);
|
|
101
|
+
try {
|
|
102
|
+
const result = await originalAction(...actionArgs);
|
|
103
|
+
emitAction({
|
|
104
|
+
pageId,
|
|
105
|
+
action: actMethod,
|
|
106
|
+
source: "agent",
|
|
107
|
+
selector: "locator",
|
|
108
|
+
value: actionArgs[0] !== void 0 ? String(actionArgs[0]).slice(0, 100) : void 0,
|
|
109
|
+
duration: Date.now() - start,
|
|
110
|
+
success: true
|
|
111
|
+
});
|
|
112
|
+
return result;
|
|
113
|
+
} catch (error) {
|
|
114
|
+
emitAction({
|
|
115
|
+
pageId,
|
|
116
|
+
action: actMethod,
|
|
117
|
+
source: "agent",
|
|
118
|
+
selector: "locator",
|
|
119
|
+
duration: Date.now() - start,
|
|
120
|
+
success: false,
|
|
121
|
+
error: error?.message ?? String(error)
|
|
122
|
+
});
|
|
123
|
+
throw error;
|
|
124
|
+
} finally {
|
|
125
|
+
await markApiActionInProgress(page, false);
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
for (const method of locatorReturningMethods) {
|
|
130
|
+
if (typeof locator[method] !== "function") continue;
|
|
131
|
+
const originalMethod = locator[method].bind(locator);
|
|
132
|
+
locator[method] = (...args) => {
|
|
133
|
+
const child = originalMethod(...args);
|
|
134
|
+
return wrapLocator(child, page, pageId);
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
if (typeof locator.nth === "function") {
|
|
138
|
+
const originalNth = locator.nth.bind(locator);
|
|
139
|
+
locator.nth = (index) => {
|
|
140
|
+
const child = originalNth(index);
|
|
141
|
+
return wrapLocator(child, page, pageId);
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
if (typeof locator.all === "function") {
|
|
145
|
+
const originalAll = locator.all.bind(locator);
|
|
146
|
+
locator.all = async () => {
|
|
147
|
+
const items = await originalAll();
|
|
148
|
+
return items.map((item) => wrapLocator(item, page, pageId));
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
return locator;
|
|
152
|
+
};
|
|
153
|
+
const installUserDomTracking = async (page, pageId) => {
|
|
154
|
+
if (exposedPages.has(page)) return;
|
|
155
|
+
exposedPages.add(page);
|
|
156
|
+
await page.exposeFunction("__btActionLog", (jsonStr) => {
|
|
157
|
+
const parsed = JSON.parse(jsonStr);
|
|
158
|
+
emitAction({
|
|
159
|
+
pageId,
|
|
160
|
+
source: "user",
|
|
161
|
+
...parsed
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
await page.addInitScript(() => {
|
|
165
|
+
if (window.__btDomListenersInstalled) return;
|
|
166
|
+
window.__btDomListenersInstalled = true;
|
|
167
|
+
const identify = (el) => {
|
|
168
|
+
if (!el || !el.tagName) return "";
|
|
169
|
+
const testId = el.getAttribute("data-testid");
|
|
170
|
+
if (testId) return `[data-testid="${testId}"]`;
|
|
171
|
+
const role = el.getAttribute("role") || "";
|
|
172
|
+
const id = el.id;
|
|
173
|
+
if (role && id) return `${role}#${id}`;
|
|
174
|
+
const label = el.getAttribute("aria-label") || (el.textContent || "").trim().slice(0, 30) || "";
|
|
175
|
+
if (role && label) return `${role} "${label}"`;
|
|
176
|
+
const tag = el.tagName.toLowerCase();
|
|
177
|
+
const cls = el.className && typeof el.className === "string" ? "." + el.className.trim().split(/\s+/).slice(0, 2).join(".") : "";
|
|
178
|
+
return `${tag}${cls}`;
|
|
179
|
+
};
|
|
180
|
+
let clickTimer = null;
|
|
181
|
+
let pendingClick = null;
|
|
182
|
+
document.addEventListener(
|
|
183
|
+
"click",
|
|
184
|
+
(event) => {
|
|
185
|
+
if (window.__btApiActionInProgress) return;
|
|
186
|
+
const target = event.target;
|
|
187
|
+
const selector = identify(target);
|
|
188
|
+
if (target?.type === "checkbox") {
|
|
189
|
+
window.__btActionLog(
|
|
190
|
+
JSON.stringify({
|
|
191
|
+
action: target.checked ? "check" : "uncheck",
|
|
192
|
+
selector,
|
|
193
|
+
success: true
|
|
194
|
+
})
|
|
195
|
+
);
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
pendingClick = { selector };
|
|
199
|
+
if (clickTimer) clearTimeout(clickTimer);
|
|
200
|
+
clickTimer = setTimeout(() => {
|
|
201
|
+
if (pendingClick) {
|
|
202
|
+
window.__btActionLog(
|
|
203
|
+
JSON.stringify({
|
|
204
|
+
action: "click",
|
|
205
|
+
selector: pendingClick.selector,
|
|
206
|
+
success: true
|
|
207
|
+
})
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
pendingClick = null;
|
|
211
|
+
clickTimer = null;
|
|
212
|
+
}, 200);
|
|
213
|
+
},
|
|
214
|
+
true
|
|
215
|
+
);
|
|
216
|
+
document.addEventListener(
|
|
217
|
+
"dblclick",
|
|
218
|
+
(event) => {
|
|
219
|
+
if (window.__btApiActionInProgress) return;
|
|
220
|
+
if (clickTimer) {
|
|
221
|
+
clearTimeout(clickTimer);
|
|
222
|
+
clickTimer = null;
|
|
223
|
+
pendingClick = null;
|
|
224
|
+
}
|
|
225
|
+
const selector = identify(event.target);
|
|
226
|
+
window.__btActionLog(
|
|
227
|
+
JSON.stringify({ action: "dblclick", selector, success: true })
|
|
228
|
+
);
|
|
229
|
+
},
|
|
230
|
+
true
|
|
231
|
+
);
|
|
232
|
+
const inputTimers = /* @__PURE__ */ new WeakMap();
|
|
233
|
+
document.addEventListener(
|
|
234
|
+
"input",
|
|
235
|
+
(event) => {
|
|
236
|
+
if (window.__btApiActionInProgress) return;
|
|
237
|
+
const target = event.target;
|
|
238
|
+
const selector = identify(target);
|
|
239
|
+
if (target.tagName === "SELECT") {
|
|
240
|
+
window.__btActionLog(
|
|
241
|
+
JSON.stringify({
|
|
242
|
+
action: "selectOption",
|
|
243
|
+
selector,
|
|
244
|
+
value: target.value,
|
|
245
|
+
success: true
|
|
246
|
+
})
|
|
247
|
+
);
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
const existing = inputTimers.get(target);
|
|
251
|
+
if (existing) clearTimeout(existing);
|
|
252
|
+
inputTimers.set(
|
|
253
|
+
target,
|
|
254
|
+
setTimeout(() => {
|
|
255
|
+
inputTimers.delete(target);
|
|
256
|
+
window.__btActionLog(
|
|
257
|
+
JSON.stringify({
|
|
258
|
+
action: "fill",
|
|
259
|
+
selector,
|
|
260
|
+
value: (target.value || "").slice(0, 100),
|
|
261
|
+
success: true
|
|
262
|
+
})
|
|
263
|
+
);
|
|
264
|
+
}, 500)
|
|
265
|
+
);
|
|
266
|
+
},
|
|
267
|
+
true
|
|
268
|
+
);
|
|
269
|
+
const specialKeys = /* @__PURE__ */ new Set([
|
|
270
|
+
"Enter",
|
|
271
|
+
"Escape",
|
|
272
|
+
"Tab",
|
|
273
|
+
"Backspace",
|
|
274
|
+
"Delete",
|
|
275
|
+
"ArrowUp",
|
|
276
|
+
"ArrowDown",
|
|
277
|
+
"ArrowLeft",
|
|
278
|
+
"ArrowRight",
|
|
279
|
+
"Home",
|
|
280
|
+
"End",
|
|
281
|
+
"PageUp",
|
|
282
|
+
"PageDown",
|
|
283
|
+
"F1",
|
|
284
|
+
"F2",
|
|
285
|
+
"F3",
|
|
286
|
+
"F4",
|
|
287
|
+
"F5",
|
|
288
|
+
"F6",
|
|
289
|
+
"F7",
|
|
290
|
+
"F8",
|
|
291
|
+
"F9",
|
|
292
|
+
"F10",
|
|
293
|
+
"F11",
|
|
294
|
+
"F12"
|
|
295
|
+
]);
|
|
296
|
+
document.addEventListener(
|
|
297
|
+
"keydown",
|
|
298
|
+
(event) => {
|
|
299
|
+
if (window.__btApiActionInProgress) return;
|
|
300
|
+
const isShortcut = event.ctrlKey || event.metaKey || event.altKey;
|
|
301
|
+
if (!isShortcut && !specialKeys.has(event.key)) return;
|
|
302
|
+
const selector = identify(event.target);
|
|
303
|
+
const keyDesc = (event.ctrlKey ? "Ctrl+" : "") + (event.metaKey ? "Meta+" : "") + (event.altKey ? "Alt+" : "") + (event.shiftKey ? "Shift+" : "") + event.key;
|
|
304
|
+
window.__btActionLog(
|
|
305
|
+
JSON.stringify({
|
|
306
|
+
action: "press",
|
|
307
|
+
selector,
|
|
308
|
+
value: keyDesc,
|
|
309
|
+
success: true
|
|
310
|
+
})
|
|
311
|
+
);
|
|
312
|
+
},
|
|
313
|
+
true
|
|
314
|
+
);
|
|
315
|
+
let scrollTimer = null;
|
|
316
|
+
document.addEventListener(
|
|
317
|
+
"scroll",
|
|
318
|
+
() => {
|
|
319
|
+
if (window.__btApiActionInProgress) return;
|
|
320
|
+
if (scrollTimer) clearTimeout(scrollTimer);
|
|
321
|
+
scrollTimer = setTimeout(() => {
|
|
322
|
+
scrollTimer = null;
|
|
323
|
+
window.__btActionLog(
|
|
324
|
+
JSON.stringify({
|
|
325
|
+
action: "scroll",
|
|
326
|
+
selector: "document",
|
|
327
|
+
value: `y=${window.scrollY}`,
|
|
328
|
+
success: true
|
|
329
|
+
})
|
|
330
|
+
);
|
|
331
|
+
}, 300);
|
|
332
|
+
},
|
|
333
|
+
true
|
|
334
|
+
);
|
|
335
|
+
});
|
|
336
|
+
};
|
|
337
|
+
const wrapPageActions = (page, pageId) => {
|
|
338
|
+
if (wrappedPages.has(page)) return;
|
|
339
|
+
wrappedPages.add(page);
|
|
340
|
+
const pageActions = [
|
|
341
|
+
"click",
|
|
342
|
+
"dblclick",
|
|
343
|
+
"fill",
|
|
344
|
+
"type",
|
|
345
|
+
"press",
|
|
346
|
+
"check",
|
|
347
|
+
"uncheck",
|
|
348
|
+
"selectOption",
|
|
349
|
+
"hover",
|
|
350
|
+
"focus"
|
|
351
|
+
];
|
|
352
|
+
const navActions = ["goto", "reload", "goBack", "goForward"];
|
|
353
|
+
const locatorFactories = [
|
|
354
|
+
"locator",
|
|
355
|
+
"getByRole",
|
|
356
|
+
"getByText",
|
|
357
|
+
"getByLabel",
|
|
358
|
+
"getByPlaceholder",
|
|
359
|
+
"getByAltText",
|
|
360
|
+
"getByTitle",
|
|
361
|
+
"getByTestId"
|
|
362
|
+
];
|
|
363
|
+
for (const method of pageActions) {
|
|
364
|
+
const originalMethod = page[method].bind(page);
|
|
365
|
+
page[method] = async (...args) => {
|
|
366
|
+
const start = Date.now();
|
|
367
|
+
await markApiActionInProgress(page, true);
|
|
368
|
+
try {
|
|
369
|
+
const result = await originalMethod(...args);
|
|
370
|
+
emitAction({
|
|
371
|
+
pageId,
|
|
372
|
+
action: method,
|
|
373
|
+
source: "agent",
|
|
374
|
+
selector: typeof args[0] === "string" ? args[0] : void 0,
|
|
375
|
+
value: args[1] !== void 0 ? String(args[1]).slice(0, 100) : void 0,
|
|
376
|
+
duration: Date.now() - start,
|
|
377
|
+
success: true
|
|
378
|
+
});
|
|
379
|
+
return result;
|
|
380
|
+
} catch (error) {
|
|
381
|
+
emitAction({
|
|
382
|
+
pageId,
|
|
383
|
+
action: method,
|
|
384
|
+
source: "agent",
|
|
385
|
+
selector: typeof args[0] === "string" ? args[0] : void 0,
|
|
386
|
+
duration: Date.now() - start,
|
|
387
|
+
success: false,
|
|
388
|
+
error: error?.message ?? String(error)
|
|
389
|
+
});
|
|
390
|
+
throw error;
|
|
391
|
+
} finally {
|
|
392
|
+
await markApiActionInProgress(page, false);
|
|
393
|
+
}
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
for (const method of navActions) {
|
|
397
|
+
const originalMethod = page[method].bind(page);
|
|
398
|
+
page[method] = async (...args) => {
|
|
399
|
+
const start = Date.now();
|
|
400
|
+
try {
|
|
401
|
+
const result = await originalMethod(...args);
|
|
402
|
+
emitAction({
|
|
403
|
+
pageId,
|
|
404
|
+
action: method,
|
|
405
|
+
source: "agent",
|
|
406
|
+
url: typeof args[0] === "string" ? args[0] : page.url(),
|
|
407
|
+
duration: Date.now() - start,
|
|
408
|
+
success: true
|
|
409
|
+
});
|
|
410
|
+
return result;
|
|
411
|
+
} catch (error) {
|
|
412
|
+
emitAction({
|
|
413
|
+
pageId,
|
|
414
|
+
action: method,
|
|
415
|
+
source: "agent",
|
|
416
|
+
url: typeof args[0] === "string" ? args[0] : void 0,
|
|
417
|
+
duration: Date.now() - start,
|
|
418
|
+
success: false,
|
|
419
|
+
error: error?.message ?? String(error)
|
|
420
|
+
});
|
|
421
|
+
throw error;
|
|
422
|
+
}
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
for (const factory of locatorFactories) {
|
|
426
|
+
const originalFactory = page[factory].bind(page);
|
|
427
|
+
page[factory] = (...factoryArgs) => {
|
|
428
|
+
const locator = originalFactory(...factoryArgs);
|
|
429
|
+
return wrapLocator(locator, page, pageId);
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
};
|
|
433
|
+
const installForPage = async (page) => {
|
|
434
|
+
const pageId = await resolvePageId(page);
|
|
435
|
+
wrapPageActions(page, pageId);
|
|
436
|
+
if (includeUserDomActions) {
|
|
437
|
+
await installUserDomTracking(page, pageId);
|
|
438
|
+
}
|
|
439
|
+
page.on("response", async (response) => {
|
|
440
|
+
const request = response.request();
|
|
441
|
+
const url = request.url();
|
|
442
|
+
if (STATIC_EXT_RE.test(url) || url.startsWith("chrome-extension://")) return;
|
|
443
|
+
emitNetwork({
|
|
444
|
+
pageId,
|
|
445
|
+
method: request.method(),
|
|
446
|
+
url,
|
|
447
|
+
status: response.status(),
|
|
448
|
+
contentType: response.headers()["content-type"] ?? null,
|
|
449
|
+
postData: request.method() === "POST" || request.method() === "PUT" || request.method() === "PATCH" ? (request.postData() ?? "").substring(0, 2e3) : void 0,
|
|
450
|
+
responseBody: null,
|
|
451
|
+
size: null,
|
|
452
|
+
durationMs: null
|
|
453
|
+
});
|
|
454
|
+
});
|
|
455
|
+
page.on("framenavigated", (frame) => {
|
|
456
|
+
if (frame !== page.mainFrame()) return;
|
|
457
|
+
emitAction({
|
|
458
|
+
pageId,
|
|
459
|
+
action: "navigate",
|
|
460
|
+
source: "agent",
|
|
461
|
+
url: frame.url(),
|
|
462
|
+
success: true
|
|
463
|
+
});
|
|
464
|
+
});
|
|
465
|
+
page.on("popup", (popup) => {
|
|
466
|
+
emitAction({
|
|
467
|
+
pageId,
|
|
468
|
+
action: "popup",
|
|
469
|
+
source: "agent",
|
|
470
|
+
url: popup.url(),
|
|
471
|
+
success: true
|
|
472
|
+
});
|
|
473
|
+
});
|
|
474
|
+
page.on("dialog", (dialog) => {
|
|
475
|
+
emitAction({
|
|
476
|
+
pageId,
|
|
477
|
+
action: "dialog",
|
|
478
|
+
source: "agent",
|
|
479
|
+
value: `${dialog.type()}: ${dialog.message().slice(0, 500)}`,
|
|
480
|
+
success: true
|
|
481
|
+
});
|
|
482
|
+
});
|
|
483
|
+
};
|
|
484
|
+
await installForPage(initialPage);
|
|
485
|
+
context.on("page", (newPage) => {
|
|
486
|
+
void installForPage(newPage);
|
|
487
|
+
});
|
|
488
|
+
}
|
|
489
|
+
export {
|
|
490
|
+
installSessionTelemetry
|
|
491
|
+
};
|
|
@@ -20,6 +20,9 @@ function readNetworkLog(session, opts = {}) {
|
|
|
20
20
|
const re = new RegExp(opts.filter, "i");
|
|
21
21
|
entries = entries.filter((e) => re.test(e.url));
|
|
22
22
|
}
|
|
23
|
+
if (opts.pageId) {
|
|
24
|
+
entries = entries.filter((e) => e.pageId === opts.pageId);
|
|
25
|
+
}
|
|
23
26
|
const last = opts.last ?? 20;
|
|
24
27
|
if (entries.length > last) {
|
|
25
28
|
entries = entries.slice(-last);
|
|
@@ -79,6 +82,9 @@ function readActionLog(session, opts = {}) {
|
|
|
79
82
|
(e) => re.test(e.action) || re.test(e.selector || "") || re.test(e.value || "") || re.test(e.url || "")
|
|
80
83
|
);
|
|
81
84
|
}
|
|
85
|
+
if (opts.pageId) {
|
|
86
|
+
entries = entries.filter((e) => e.pageId === opts.pageId);
|
|
87
|
+
}
|
|
82
88
|
const last = opts.last ?? 20;
|
|
83
89
|
if (entries.length > last) {
|
|
84
90
|
entries = entries.slice(-last);
|
|
@@ -157,7 +163,7 @@ function formatHint(method, args) {
|
|
|
157
163
|
const formatted = args.map((a) => JSON.stringify(a)).join(", ");
|
|
158
164
|
return `${method}(${formatted})`;
|
|
159
165
|
}
|
|
160
|
-
function wrapLocator(locator, hint, session, page, onActivity) {
|
|
166
|
+
function wrapLocator(locator, hint, session, page, pageId, onActivity) {
|
|
161
167
|
if (locator.__librettoActionLogged) return locator;
|
|
162
168
|
locator.__librettoActionLogged = true;
|
|
163
169
|
for (const actMethod of LOCATOR_ACTION_METHODS) {
|
|
@@ -174,6 +180,7 @@ function wrapLocator(locator, hint, session, page, onActivity) {
|
|
|
174
180
|
try {
|
|
175
181
|
const result = await origAct(...actArgs);
|
|
176
182
|
parentLogAction(session, {
|
|
183
|
+
pageId,
|
|
177
184
|
action: actMethod,
|
|
178
185
|
source: "agent",
|
|
179
186
|
selector: hint,
|
|
@@ -185,6 +192,7 @@ function wrapLocator(locator, hint, session, page, onActivity) {
|
|
|
185
192
|
return result;
|
|
186
193
|
} catch (err) {
|
|
187
194
|
parentLogAction(session, {
|
|
195
|
+
pageId,
|
|
188
196
|
action: actMethod,
|
|
189
197
|
source: "agent",
|
|
190
198
|
selector: hint,
|
|
@@ -210,7 +218,7 @@ function wrapLocator(locator, hint, session, page, onActivity) {
|
|
|
210
218
|
locator[method] = (...args) => {
|
|
211
219
|
const child = origMethod(...args);
|
|
212
220
|
const childHint = args.length > 0 ? `${hint}.${formatHint(method, args)}` : `${hint}.${method}()`;
|
|
213
|
-
return wrapLocator(child, childHint, session, page, onActivity);
|
|
221
|
+
return wrapLocator(child, childHint, session, page, pageId, onActivity);
|
|
214
222
|
};
|
|
215
223
|
}
|
|
216
224
|
if (typeof locator.nth === "function") {
|
|
@@ -218,7 +226,7 @@ function wrapLocator(locator, hint, session, page, onActivity) {
|
|
|
218
226
|
locator.nth = (index) => {
|
|
219
227
|
const child = origNth(index);
|
|
220
228
|
const childHint = `${hint}.nth(${index})`;
|
|
221
|
-
return wrapLocator(child, childHint, session, page, onActivity);
|
|
229
|
+
return wrapLocator(child, childHint, session, page, pageId, onActivity);
|
|
222
230
|
};
|
|
223
231
|
}
|
|
224
232
|
if (typeof locator.all === "function") {
|
|
@@ -227,13 +235,13 @@ function wrapLocator(locator, hint, session, page, onActivity) {
|
|
|
227
235
|
const items = await origAll();
|
|
228
236
|
return items.map((item, i) => {
|
|
229
237
|
const childHint = `${hint}.all()[${i}]`;
|
|
230
|
-
return wrapLocator(item, childHint, session, page, onActivity);
|
|
238
|
+
return wrapLocator(item, childHint, session, page, pageId, onActivity);
|
|
231
239
|
});
|
|
232
240
|
};
|
|
233
241
|
}
|
|
234
242
|
return locator;
|
|
235
243
|
}
|
|
236
|
-
function wrapPageForActionLogging(page, session, onActivity) {
|
|
244
|
+
function wrapPageForActionLogging(page, session, pageId, onActivity) {
|
|
237
245
|
const PAGE_ACTIONS = [
|
|
238
246
|
"click",
|
|
239
247
|
"dblclick",
|
|
@@ -260,6 +268,7 @@ function wrapPageForActionLogging(page, session, onActivity) {
|
|
|
260
268
|
try {
|
|
261
269
|
const result = await orig(...args);
|
|
262
270
|
parentLogAction(session, {
|
|
271
|
+
pageId,
|
|
263
272
|
action: method,
|
|
264
273
|
source: "agent",
|
|
265
274
|
selector: typeof args[0] === "string" ? args[0] : void 0,
|
|
@@ -271,6 +280,7 @@ function wrapPageForActionLogging(page, session, onActivity) {
|
|
|
271
280
|
return result;
|
|
272
281
|
} catch (err) {
|
|
273
282
|
parentLogAction(session, {
|
|
283
|
+
pageId,
|
|
274
284
|
action: method,
|
|
275
285
|
source: "agent",
|
|
276
286
|
selector: typeof args[0] === "string" ? args[0] : void 0,
|
|
@@ -297,6 +307,7 @@ function wrapPageForActionLogging(page, session, onActivity) {
|
|
|
297
307
|
try {
|
|
298
308
|
const result = await orig(...args);
|
|
299
309
|
parentLogAction(session, {
|
|
310
|
+
pageId,
|
|
300
311
|
action: method,
|
|
301
312
|
source: "agent",
|
|
302
313
|
url: typeof args[0] === "string" ? args[0] : page.url(),
|
|
@@ -307,6 +318,7 @@ function wrapPageForActionLogging(page, session, onActivity) {
|
|
|
307
318
|
return result;
|
|
308
319
|
} catch (err) {
|
|
309
320
|
parentLogAction(session, {
|
|
321
|
+
pageId,
|
|
310
322
|
action: method,
|
|
311
323
|
source: "agent",
|
|
312
324
|
url: typeof args[0] === "string" ? args[0] : void 0,
|
|
@@ -334,7 +346,7 @@ function wrapPageForActionLogging(page, session, onActivity) {
|
|
|
334
346
|
page[factory] = (...factoryArgs) => {
|
|
335
347
|
const locator = orig(...factoryArgs);
|
|
336
348
|
const hint = formatHint(factory, factoryArgs);
|
|
337
|
-
return wrapLocator(locator, hint, session, page, onActivity);
|
|
349
|
+
return wrapLocator(locator, hint, session, page, pageId, onActivity);
|
|
338
350
|
};
|
|
339
351
|
}
|
|
340
352
|
}
|
|
@@ -7,8 +7,13 @@ import {
|
|
|
7
7
|
launchBrowser
|
|
8
8
|
} from "../../index.js";
|
|
9
9
|
import { getProfilePath, normalizeDomain } from "../core/browser.js";
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
getSessionActionsLogPath,
|
|
12
|
+
getSessionDir,
|
|
13
|
+
getSessionNetworkLogPath
|
|
14
|
+
} from "../core/context.js";
|
|
11
15
|
import { getPauseSignalPaths, removeSignalIfExists } from "../core/pause-signals.js";
|
|
16
|
+
import { installSessionTelemetry } from "../core/session-telemetry.js";
|
|
12
17
|
const LIBRETTO_WORKFLOW_BRAND = /* @__PURE__ */ Symbol.for("libretto.workflow");
|
|
13
18
|
const RESUME_POLL_INTERVAL_MS = 250;
|
|
14
19
|
function mirrorStdoutToFile(filePath) {
|
|
@@ -136,6 +141,19 @@ async function runIntegrationInternal(args, options) {
|
|
|
136
141
|
headless: args.headless,
|
|
137
142
|
storageStatePath
|
|
138
143
|
});
|
|
144
|
+
const actionsLogPath = getSessionActionsLogPath(args.session);
|
|
145
|
+
const networkLogPath = getSessionNetworkLogPath(args.session);
|
|
146
|
+
await installSessionTelemetry({
|
|
147
|
+
context: browserSession.context,
|
|
148
|
+
initialPage: browserSession.page,
|
|
149
|
+
includeUserDomActions: true,
|
|
150
|
+
logAction: (entry) => {
|
|
151
|
+
appendFileSync(actionsLogPath, JSON.stringify(entry) + "\n");
|
|
152
|
+
},
|
|
153
|
+
logNetwork: (entry) => {
|
|
154
|
+
appendFileSync(networkLogPath, JSON.stringify(entry) + "\n");
|
|
155
|
+
}
|
|
156
|
+
});
|
|
139
157
|
const workflowContext = {
|
|
140
158
|
logger: integrationLogger,
|
|
141
159
|
page: browserSession.page,
|
package/dist/index.cjs
CHANGED
|
@@ -29,8 +29,8 @@ __export(index_exports, {
|
|
|
29
29
|
attemptWithRecovery: () => import_recovery.attemptWithRecovery,
|
|
30
30
|
clearHighlights: () => import_highlight.clearHighlights,
|
|
31
31
|
createFileLogSink: () => import_sinks.createFileLogSink,
|
|
32
|
-
createRunner: () => import_runner.createRunner,
|
|
33
32
|
debugPause: () => import_pause.debugPause,
|
|
33
|
+
defaultLogger: () => import_logger.defaultLogger,
|
|
34
34
|
detectSubmissionError: () => import_errors.detectSubmissionError,
|
|
35
35
|
downloadAndSave: () => import_download.downloadAndSave,
|
|
36
36
|
downloadViaClick: () => import_download.downloadViaClick,
|
|
@@ -58,12 +58,9 @@ __export(index_exports, {
|
|
|
58
58
|
serializeSessionState: () => import_state.serializeSessionState,
|
|
59
59
|
shouldPauseBeforeMutation: () => import_config.shouldPauseBeforeMutation,
|
|
60
60
|
showHighlight: () => import_highlight.showHighlight,
|
|
61
|
-
step: () => import_step.step,
|
|
62
61
|
workflow: () => import_workflow.workflow
|
|
63
62
|
});
|
|
64
63
|
module.exports = __toCommonJS(index_exports);
|
|
65
|
-
var import_step = require("./runtime/step/step.js");
|
|
66
|
-
var import_runner = require("./runtime/step/runner.js");
|
|
67
64
|
var import_logger = require("./shared/logger/logger.js");
|
|
68
65
|
var import_sinks = require("./shared/logger/sinks.js");
|
|
69
66
|
var import_state = require("./shared/state/index.js");
|
|
@@ -93,8 +90,8 @@ var import_workflow = require("./shared/workflow/workflow.js");
|
|
|
93
90
|
attemptWithRecovery,
|
|
94
91
|
clearHighlights,
|
|
95
92
|
createFileLogSink,
|
|
96
|
-
createRunner,
|
|
97
93
|
debugPause,
|
|
94
|
+
defaultLogger,
|
|
98
95
|
detectSubmissionError,
|
|
99
96
|
downloadAndSave,
|
|
100
97
|
downloadViaClick,
|
|
@@ -122,6 +119,5 @@ var import_workflow = require("./shared/workflow/workflow.js");
|
|
|
122
119
|
serializeSessionState,
|
|
123
120
|
shouldPauseBeforeMutation,
|
|
124
121
|
showHighlight,
|
|
125
|
-
step,
|
|
126
122
|
workflow
|
|
127
123
|
});
|
package/dist/index.d.cts
CHANGED
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export { Runner, createRunner } from './runtime/step/runner.cjs';
|
|
3
|
-
export { DebugBundle, RecoveryHandler, RunnerConfig, Step, StepContext, StepHandler, StepHistoryEntry, StepOptions } from './runtime/step/types.cjs';
|
|
4
|
-
export { LogOptions, Logger, LoggerApi, LoggerSink } from './shared/logger/logger.cjs';
|
|
1
|
+
export { LogOptions, Logger, LoggerApi, LoggerSink, MinimalLogger, defaultLogger } from './shared/logger/logger.cjs';
|
|
5
2
|
export { createFileLogSink, jsonlConsoleSink, prettyConsoleSink } from './shared/logger/sinks.cjs';
|
|
6
3
|
export { LLMClient, Message, MessageContentPart } from './shared/llm/types.cjs';
|
|
7
4
|
export { SESSION_STATE_VERSION, SessionState, SessionStateFile, SessionStateFileSchema, SessionStatus, SessionStatusSchema, parseSessionStateContent, parseSessionStateData, serializeSessionState } from './shared/state/session-state.cjs';
|
|
@@ -18,5 +15,5 @@ export { GhostCursorOptions, ensureGhostCursor, ghostClick, hideGhostCursor, mov
|
|
|
18
15
|
export { HighlightOptions, clearHighlights, ensureHighlightLayer, showHighlight } from './shared/visualization/highlight.cjs';
|
|
19
16
|
export { BrowserSession, LaunchBrowserArgs, launchBrowser } from './shared/run/browser.cjs';
|
|
20
17
|
export { LIBRETTO_WORKFLOW_BRAND, LibrettoAuthProfile, LibrettoWorkflow, LibrettoWorkflowContext, LibrettoWorkflowHandler, LibrettoWorkflowMetadata, workflow } from './shared/workflow/workflow.cjs';
|
|
21
|
-
import 'playwright';
|
|
22
18
|
import 'zod';
|
|
19
|
+
import 'playwright';
|