libretto 0.2.7 → 0.3.0
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/LICENSE +21 -0
- package/package.json +21 -15
- package/skill/SKILL.md +20 -18
- package/skill/code-generation-rules.md +3 -3
- package/skill/integration-approach-selection.md +3 -3
- package/dist/cli/cli.js +0 -209
- package/dist/cli/commands/ai.js +0 -21
- package/dist/cli/commands/browser.js +0 -82
- package/dist/cli/commands/execution.js +0 -461
- package/dist/cli/commands/init.js +0 -95
- package/dist/cli/commands/logs.js +0 -93
- package/dist/cli/commands/snapshot.js +0 -106
- package/dist/cli/core/ai-config.js +0 -149
- package/dist/cli/core/browser.js +0 -648
- package/dist/cli/core/context.js +0 -118
- package/dist/cli/core/pause-signals.js +0 -29
- package/dist/cli/core/session-telemetry.js +0 -491
- package/dist/cli/core/session.js +0 -183
- package/dist/cli/core/snapshot-analyzer.js +0 -492
- package/dist/cli/core/telemetry.js +0 -362
- package/dist/cli/index.js +0 -13
- package/dist/cli/workers/run-integration-runtime.js +0 -227
- package/dist/cli/workers/run-integration-worker-protocol.js +0 -12
- package/dist/cli/workers/run-integration-worker.js +0 -66
- package/dist/index.cjs +0 -116
- package/dist/index.d.cts +0 -21
- package/dist/index.d.ts +0 -21
- package/dist/index.js +0 -97
- package/dist/runtime/download/download.cjs +0 -70
- package/dist/runtime/download/download.d.cts +0 -35
- package/dist/runtime/download/download.d.ts +0 -35
- package/dist/runtime/download/download.js +0 -45
- package/dist/runtime/download/index.cjs +0 -30
- package/dist/runtime/download/index.d.cts +0 -3
- package/dist/runtime/download/index.d.ts +0 -3
- package/dist/runtime/download/index.js +0 -8
- package/dist/runtime/extract/extract.cjs +0 -88
- package/dist/runtime/extract/extract.d.cts +0 -23
- package/dist/runtime/extract/extract.d.ts +0 -23
- package/dist/runtime/extract/extract.js +0 -64
- package/dist/runtime/extract/index.cjs +0 -28
- package/dist/runtime/extract/index.d.cts +0 -5
- package/dist/runtime/extract/index.d.ts +0 -5
- package/dist/runtime/extract/index.js +0 -4
- package/dist/runtime/network/index.cjs +0 -28
- package/dist/runtime/network/index.d.cts +0 -4
- package/dist/runtime/network/index.d.ts +0 -4
- package/dist/runtime/network/index.js +0 -6
- package/dist/runtime/network/network.cjs +0 -91
- package/dist/runtime/network/network.d.cts +0 -28
- package/dist/runtime/network/network.d.ts +0 -28
- package/dist/runtime/network/network.js +0 -67
- package/dist/runtime/recovery/agent.cjs +0 -223
- package/dist/runtime/recovery/agent.d.cts +0 -13
- package/dist/runtime/recovery/agent.d.ts +0 -13
- package/dist/runtime/recovery/agent.js +0 -199
- package/dist/runtime/recovery/errors.cjs +0 -124
- package/dist/runtime/recovery/errors.d.cts +0 -31
- package/dist/runtime/recovery/errors.d.ts +0 -31
- package/dist/runtime/recovery/errors.js +0 -100
- package/dist/runtime/recovery/index.cjs +0 -34
- package/dist/runtime/recovery/index.d.cts +0 -7
- package/dist/runtime/recovery/index.d.ts +0 -7
- package/dist/runtime/recovery/index.js +0 -10
- package/dist/runtime/recovery/recovery.cjs +0 -55
- package/dist/runtime/recovery/recovery.d.cts +0 -12
- package/dist/runtime/recovery/recovery.d.ts +0 -12
- package/dist/runtime/recovery/recovery.js +0 -31
- package/dist/shared/config/config.cjs +0 -44
- package/dist/shared/config/config.d.cts +0 -10
- package/dist/shared/config/config.d.ts +0 -10
- package/dist/shared/config/config.js +0 -18
- package/dist/shared/config/index.cjs +0 -32
- package/dist/shared/config/index.d.cts +0 -1
- package/dist/shared/config/index.d.ts +0 -1
- package/dist/shared/config/index.js +0 -10
- package/dist/shared/debug/index.cjs +0 -30
- package/dist/shared/debug/index.d.cts +0 -1
- package/dist/shared/debug/index.d.ts +0 -1
- package/dist/shared/debug/index.js +0 -5
- package/dist/shared/debug/pause.cjs +0 -90
- package/dist/shared/debug/pause.d.cts +0 -16
- package/dist/shared/debug/pause.d.ts +0 -16
- package/dist/shared/debug/pause.js +0 -55
- package/dist/shared/instrumentation/errors.cjs +0 -81
- package/dist/shared/instrumentation/errors.d.cts +0 -12
- package/dist/shared/instrumentation/errors.d.ts +0 -12
- package/dist/shared/instrumentation/errors.js +0 -57
- package/dist/shared/instrumentation/index.cjs +0 -35
- package/dist/shared/instrumentation/index.d.cts +0 -6
- package/dist/shared/instrumentation/index.d.ts +0 -6
- package/dist/shared/instrumentation/index.js +0 -12
- package/dist/shared/instrumentation/instrument.cjs +0 -206
- package/dist/shared/instrumentation/instrument.d.cts +0 -32
- package/dist/shared/instrumentation/instrument.d.ts +0 -32
- package/dist/shared/instrumentation/instrument.js +0 -190
- package/dist/shared/llm/ai-sdk-adapter.cjs +0 -67
- package/dist/shared/llm/ai-sdk-adapter.d.cts +0 -22
- package/dist/shared/llm/ai-sdk-adapter.d.ts +0 -22
- package/dist/shared/llm/ai-sdk-adapter.js +0 -43
- package/dist/shared/llm/client.cjs +0 -139
- package/dist/shared/llm/client.d.cts +0 -6
- package/dist/shared/llm/client.d.ts +0 -6
- package/dist/shared/llm/client.js +0 -115
- package/dist/shared/llm/index.cjs +0 -31
- package/dist/shared/llm/index.d.cts +0 -5
- package/dist/shared/llm/index.d.ts +0 -5
- package/dist/shared/llm/index.js +0 -6
- package/dist/shared/llm/types.cjs +0 -16
- package/dist/shared/llm/types.d.cts +0 -66
- package/dist/shared/llm/types.d.ts +0 -66
- package/dist/shared/llm/types.js +0 -0
- package/dist/shared/logger/index.cjs +0 -37
- package/dist/shared/logger/index.d.cts +0 -2
- package/dist/shared/logger/index.d.ts +0 -2
- package/dist/shared/logger/index.js +0 -13
- package/dist/shared/logger/logger.cjs +0 -232
- package/dist/shared/logger/logger.d.cts +0 -86
- package/dist/shared/logger/logger.d.ts +0 -86
- package/dist/shared/logger/logger.js +0 -207
- package/dist/shared/logger/sinks.cjs +0 -160
- package/dist/shared/logger/sinks.d.cts +0 -9
- package/dist/shared/logger/sinks.d.ts +0 -9
- package/dist/shared/logger/sinks.js +0 -124
- package/dist/shared/paths/paths.cjs +0 -104
- package/dist/shared/paths/paths.d.cts +0 -10
- package/dist/shared/paths/paths.d.ts +0 -10
- package/dist/shared/paths/paths.js +0 -73
- package/dist/shared/run/api.cjs +0 -28
- package/dist/shared/run/api.d.cts +0 -2
- package/dist/shared/run/api.d.ts +0 -2
- package/dist/shared/run/api.js +0 -4
- package/dist/shared/run/browser.cjs +0 -98
- package/dist/shared/run/browser.d.cts +0 -22
- package/dist/shared/run/browser.d.ts +0 -22
- package/dist/shared/run/browser.js +0 -74
- package/dist/shared/state/index.cjs +0 -38
- package/dist/shared/state/index.d.cts +0 -2
- package/dist/shared/state/index.d.ts +0 -2
- package/dist/shared/state/index.js +0 -16
- package/dist/shared/state/session-state.cjs +0 -85
- package/dist/shared/state/session-state.d.cts +0 -34
- package/dist/shared/state/session-state.d.ts +0 -34
- package/dist/shared/state/session-state.js +0 -56
- package/dist/shared/visualization/ghost-cursor.cjs +0 -174
- package/dist/shared/visualization/ghost-cursor.d.cts +0 -37
- package/dist/shared/visualization/ghost-cursor.d.ts +0 -37
- package/dist/shared/visualization/ghost-cursor.js +0 -145
- package/dist/shared/visualization/highlight.cjs +0 -134
- package/dist/shared/visualization/highlight.d.cts +0 -22
- package/dist/shared/visualization/highlight.d.ts +0 -22
- package/dist/shared/visualization/highlight.js +0 -108
- package/dist/shared/visualization/index.cjs +0 -45
- package/dist/shared/visualization/index.d.cts +0 -3
- package/dist/shared/visualization/index.d.ts +0 -3
- package/dist/shared/visualization/index.js +0 -24
- package/dist/shared/workflow/workflow.cjs +0 -47
- package/dist/shared/workflow/workflow.d.cts +0 -21
- package/dist/shared/workflow/workflow.d.ts +0 -21
- package/dist/shared/workflow/workflow.js +0 -21
|
@@ -1,362 +0,0 @@
|
|
|
1
|
-
import { appendFileSync, existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
-
import {
|
|
3
|
-
getSessionActionsLogPath,
|
|
4
|
-
getSessionNetworkLogPath
|
|
5
|
-
} from "./context.js";
|
|
6
|
-
import { assertSessionStateExistsOrThrow } from "./session.js";
|
|
7
|
-
function readNetworkLog(session, opts = {}) {
|
|
8
|
-
assertSessionStateExistsOrThrow(session);
|
|
9
|
-
const logPath = getSessionNetworkLogPath(session);
|
|
10
|
-
if (!existsSync(logPath)) return [];
|
|
11
|
-
const lines = readFileSync(logPath, "utf-8").trim().split("\n").filter(Boolean);
|
|
12
|
-
let entries = lines.map(
|
|
13
|
-
(line) => JSON.parse(line)
|
|
14
|
-
);
|
|
15
|
-
if (opts.method) {
|
|
16
|
-
const m = opts.method.toUpperCase();
|
|
17
|
-
entries = entries.filter((e) => e.method === m);
|
|
18
|
-
}
|
|
19
|
-
if (opts.filter) {
|
|
20
|
-
const re = new RegExp(opts.filter, "i");
|
|
21
|
-
entries = entries.filter((e) => re.test(e.url));
|
|
22
|
-
}
|
|
23
|
-
if (opts.pageId) {
|
|
24
|
-
entries = entries.filter((e) => e.pageId === opts.pageId);
|
|
25
|
-
}
|
|
26
|
-
const last = opts.last ?? 20;
|
|
27
|
-
if (entries.length > last) {
|
|
28
|
-
entries = entries.slice(-last);
|
|
29
|
-
}
|
|
30
|
-
return entries;
|
|
31
|
-
}
|
|
32
|
-
function formatNetworkEntry(e) {
|
|
33
|
-
const time = e.ts.replace(/.*T/, "").replace(/\.\d+Z$/, "");
|
|
34
|
-
const duration = e.durationMs != null ? `${e.durationMs}ms` : "?ms";
|
|
35
|
-
const size = e.size != null ? `${e.size}B` : "";
|
|
36
|
-
const parts = [
|
|
37
|
-
`[${time}]`,
|
|
38
|
-
`${e.status}`,
|
|
39
|
-
`${e.method.padEnd(6)}`,
|
|
40
|
-
e.url,
|
|
41
|
-
duration,
|
|
42
|
-
size
|
|
43
|
-
].filter(Boolean);
|
|
44
|
-
let line = parts.join(" ");
|
|
45
|
-
if (e.postData) {
|
|
46
|
-
line += `
|
|
47
|
-
body: ${e.postData.substring(0, 120)}${e.postData.length > 120 ? "..." : ""}`;
|
|
48
|
-
}
|
|
49
|
-
return line;
|
|
50
|
-
}
|
|
51
|
-
function clearNetworkLog(session) {
|
|
52
|
-
assertSessionStateExistsOrThrow(session);
|
|
53
|
-
const logPath = getSessionNetworkLogPath(session);
|
|
54
|
-
writeFileSync(logPath, "");
|
|
55
|
-
}
|
|
56
|
-
function parentLogAction(session, entry) {
|
|
57
|
-
try {
|
|
58
|
-
const record = { ts: (/* @__PURE__ */ new Date()).toISOString(), ...entry };
|
|
59
|
-
appendFileSync(getSessionActionsLogPath(session), JSON.stringify(record) + "\n");
|
|
60
|
-
} catch {
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
function readActionLog(session, opts = {}) {
|
|
64
|
-
assertSessionStateExistsOrThrow(session);
|
|
65
|
-
const logPath = getSessionActionsLogPath(session);
|
|
66
|
-
if (!existsSync(logPath)) return [];
|
|
67
|
-
const lines = readFileSync(logPath, "utf-8").trim().split("\n").filter(Boolean);
|
|
68
|
-
let entries = lines.map(
|
|
69
|
-
(line) => JSON.parse(line)
|
|
70
|
-
);
|
|
71
|
-
if (opts.action) {
|
|
72
|
-
const a = opts.action.toLowerCase();
|
|
73
|
-
entries = entries.filter((e) => e.action === a);
|
|
74
|
-
}
|
|
75
|
-
if (opts.source) {
|
|
76
|
-
const s = opts.source.toLowerCase();
|
|
77
|
-
entries = entries.filter((e) => e.source === s);
|
|
78
|
-
}
|
|
79
|
-
if (opts.filter) {
|
|
80
|
-
const re = new RegExp(opts.filter, "i");
|
|
81
|
-
entries = entries.filter(
|
|
82
|
-
(e) => re.test(e.action) || re.test(e.selector || "") || re.test(e.value || "") || re.test(e.url || "")
|
|
83
|
-
);
|
|
84
|
-
}
|
|
85
|
-
if (opts.pageId) {
|
|
86
|
-
entries = entries.filter((e) => e.pageId === opts.pageId);
|
|
87
|
-
}
|
|
88
|
-
const last = opts.last ?? 20;
|
|
89
|
-
if (entries.length > last) {
|
|
90
|
-
entries = entries.slice(-last);
|
|
91
|
-
}
|
|
92
|
-
return entries;
|
|
93
|
-
}
|
|
94
|
-
function formatActionEntry(e) {
|
|
95
|
-
const time = e.ts.replace(/.*T/, "").replace(/\.\d+Z$/, "");
|
|
96
|
-
const src = e.source.toUpperCase().padEnd(5);
|
|
97
|
-
const parts = [`[${time}]`, `[${src}]`, e.action];
|
|
98
|
-
if (e.selector) parts.push(e.selector);
|
|
99
|
-
if (e.value) parts.push(`"${e.value}"`);
|
|
100
|
-
if (e.url) parts.push(e.url);
|
|
101
|
-
if (e.duration != null) parts.push(`${e.duration}ms`);
|
|
102
|
-
if (!e.success) parts.push(`ERROR: ${e.error || "unknown"}`);
|
|
103
|
-
return parts.join(" ");
|
|
104
|
-
}
|
|
105
|
-
function clearActionLog(session) {
|
|
106
|
-
assertSessionStateExistsOrThrow(session);
|
|
107
|
-
const logPath = getSessionActionsLogPath(session);
|
|
108
|
-
writeFileSync(logPath, "");
|
|
109
|
-
}
|
|
110
|
-
const LOCATOR_ACTION_METHODS = [
|
|
111
|
-
"click",
|
|
112
|
-
"dblclick",
|
|
113
|
-
"fill",
|
|
114
|
-
"type",
|
|
115
|
-
"press",
|
|
116
|
-
"check",
|
|
117
|
-
"uncheck",
|
|
118
|
-
"selectOption",
|
|
119
|
-
"hover",
|
|
120
|
-
"focus",
|
|
121
|
-
"scrollIntoViewIfNeeded",
|
|
122
|
-
"waitFor",
|
|
123
|
-
"innerHTML",
|
|
124
|
-
"innerText",
|
|
125
|
-
"textContent",
|
|
126
|
-
"inputValue",
|
|
127
|
-
"isChecked",
|
|
128
|
-
"isDisabled",
|
|
129
|
-
"isEditable",
|
|
130
|
-
"isEnabled",
|
|
131
|
-
"isHidden",
|
|
132
|
-
"isVisible",
|
|
133
|
-
"count",
|
|
134
|
-
"boundingBox",
|
|
135
|
-
"screenshot",
|
|
136
|
-
"evaluate",
|
|
137
|
-
"evaluateAll",
|
|
138
|
-
"evaluateHandle",
|
|
139
|
-
"getAttribute",
|
|
140
|
-
"dispatchEvent",
|
|
141
|
-
"setInputFiles",
|
|
142
|
-
"selectText",
|
|
143
|
-
"dragTo",
|
|
144
|
-
"highlight",
|
|
145
|
-
"tap"
|
|
146
|
-
];
|
|
147
|
-
const LOCATOR_RETURNING_METHODS = [
|
|
148
|
-
"first",
|
|
149
|
-
"last",
|
|
150
|
-
"locator",
|
|
151
|
-
"getByRole",
|
|
152
|
-
"getByText",
|
|
153
|
-
"getByLabel",
|
|
154
|
-
"getByPlaceholder",
|
|
155
|
-
"getByAltText",
|
|
156
|
-
"getByTitle",
|
|
157
|
-
"getByTestId",
|
|
158
|
-
"filter",
|
|
159
|
-
"and",
|
|
160
|
-
"or"
|
|
161
|
-
];
|
|
162
|
-
function formatHint(method, args) {
|
|
163
|
-
const formatted = args.map((a) => JSON.stringify(a)).join(", ");
|
|
164
|
-
return `${method}(${formatted})`;
|
|
165
|
-
}
|
|
166
|
-
function wrapLocator(locator, hint, session, page, pageId, onActivity) {
|
|
167
|
-
if (locator.__librettoActionLogged) return locator;
|
|
168
|
-
locator.__librettoActionLogged = true;
|
|
169
|
-
for (const actMethod of LOCATOR_ACTION_METHODS) {
|
|
170
|
-
if (typeof locator[actMethod] !== "function") continue;
|
|
171
|
-
const origAct = locator[actMethod].bind(locator);
|
|
172
|
-
locator[actMethod] = async (...actArgs) => {
|
|
173
|
-
const start = Date.now();
|
|
174
|
-
try {
|
|
175
|
-
await page.evaluate(() => {
|
|
176
|
-
window.__btApiActionInProgress = true;
|
|
177
|
-
});
|
|
178
|
-
} catch {
|
|
179
|
-
}
|
|
180
|
-
try {
|
|
181
|
-
const result = await origAct(...actArgs);
|
|
182
|
-
parentLogAction(session, {
|
|
183
|
-
pageId,
|
|
184
|
-
action: actMethod,
|
|
185
|
-
source: "agent",
|
|
186
|
-
selector: hint,
|
|
187
|
-
value: actArgs[0] !== void 0 ? String(actArgs[0]).slice(0, 100) : void 0,
|
|
188
|
-
duration: Date.now() - start,
|
|
189
|
-
success: true
|
|
190
|
-
});
|
|
191
|
-
onActivity?.();
|
|
192
|
-
return result;
|
|
193
|
-
} catch (err) {
|
|
194
|
-
parentLogAction(session, {
|
|
195
|
-
pageId,
|
|
196
|
-
action: actMethod,
|
|
197
|
-
source: "agent",
|
|
198
|
-
selector: hint,
|
|
199
|
-
duration: Date.now() - start,
|
|
200
|
-
success: false,
|
|
201
|
-
error: err.message
|
|
202
|
-
});
|
|
203
|
-
onActivity?.();
|
|
204
|
-
throw err;
|
|
205
|
-
} finally {
|
|
206
|
-
try {
|
|
207
|
-
await page.evaluate(() => {
|
|
208
|
-
window.__btApiActionInProgress = false;
|
|
209
|
-
});
|
|
210
|
-
} catch {
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
};
|
|
214
|
-
}
|
|
215
|
-
for (const method of LOCATOR_RETURNING_METHODS) {
|
|
216
|
-
if (typeof locator[method] !== "function") continue;
|
|
217
|
-
const origMethod = locator[method].bind(locator);
|
|
218
|
-
locator[method] = (...args) => {
|
|
219
|
-
const child = origMethod(...args);
|
|
220
|
-
const childHint = args.length > 0 ? `${hint}.${formatHint(method, args)}` : `${hint}.${method}()`;
|
|
221
|
-
return wrapLocator(child, childHint, session, page, pageId, onActivity);
|
|
222
|
-
};
|
|
223
|
-
}
|
|
224
|
-
if (typeof locator.nth === "function") {
|
|
225
|
-
const origNth = locator.nth.bind(locator);
|
|
226
|
-
locator.nth = (index) => {
|
|
227
|
-
const child = origNth(index);
|
|
228
|
-
const childHint = `${hint}.nth(${index})`;
|
|
229
|
-
return wrapLocator(child, childHint, session, page, pageId, onActivity);
|
|
230
|
-
};
|
|
231
|
-
}
|
|
232
|
-
if (typeof locator.all === "function") {
|
|
233
|
-
const origAll = locator.all.bind(locator);
|
|
234
|
-
locator.all = async () => {
|
|
235
|
-
const items = await origAll();
|
|
236
|
-
return items.map((item, i) => {
|
|
237
|
-
const childHint = `${hint}.all()[${i}]`;
|
|
238
|
-
return wrapLocator(item, childHint, session, page, pageId, onActivity);
|
|
239
|
-
});
|
|
240
|
-
};
|
|
241
|
-
}
|
|
242
|
-
return locator;
|
|
243
|
-
}
|
|
244
|
-
function wrapPageForActionLogging(page, session, pageId, onActivity) {
|
|
245
|
-
const PAGE_ACTIONS = [
|
|
246
|
-
"click",
|
|
247
|
-
"dblclick",
|
|
248
|
-
"fill",
|
|
249
|
-
"type",
|
|
250
|
-
"press",
|
|
251
|
-
"check",
|
|
252
|
-
"uncheck",
|
|
253
|
-
"selectOption",
|
|
254
|
-
"hover",
|
|
255
|
-
"focus"
|
|
256
|
-
];
|
|
257
|
-
const NAV_ACTIONS = ["goto", "reload", "goBack", "goForward"];
|
|
258
|
-
for (const method of PAGE_ACTIONS) {
|
|
259
|
-
const orig = page[method].bind(page);
|
|
260
|
-
page[method] = async (...args) => {
|
|
261
|
-
const start = Date.now();
|
|
262
|
-
try {
|
|
263
|
-
await page.evaluate(() => {
|
|
264
|
-
window.__btApiActionInProgress = true;
|
|
265
|
-
});
|
|
266
|
-
} catch {
|
|
267
|
-
}
|
|
268
|
-
try {
|
|
269
|
-
const result = await orig(...args);
|
|
270
|
-
parentLogAction(session, {
|
|
271
|
-
pageId,
|
|
272
|
-
action: method,
|
|
273
|
-
source: "agent",
|
|
274
|
-
selector: typeof args[0] === "string" ? args[0] : void 0,
|
|
275
|
-
value: args[1] !== void 0 ? String(args[1]).slice(0, 100) : void 0,
|
|
276
|
-
duration: Date.now() - start,
|
|
277
|
-
success: true
|
|
278
|
-
});
|
|
279
|
-
onActivity?.();
|
|
280
|
-
return result;
|
|
281
|
-
} catch (err) {
|
|
282
|
-
parentLogAction(session, {
|
|
283
|
-
pageId,
|
|
284
|
-
action: method,
|
|
285
|
-
source: "agent",
|
|
286
|
-
selector: typeof args[0] === "string" ? args[0] : void 0,
|
|
287
|
-
duration: Date.now() - start,
|
|
288
|
-
success: false,
|
|
289
|
-
error: err.message
|
|
290
|
-
});
|
|
291
|
-
onActivity?.();
|
|
292
|
-
throw err;
|
|
293
|
-
} finally {
|
|
294
|
-
try {
|
|
295
|
-
await page.evaluate(() => {
|
|
296
|
-
window.__btApiActionInProgress = false;
|
|
297
|
-
});
|
|
298
|
-
} catch {
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
};
|
|
302
|
-
}
|
|
303
|
-
for (const method of NAV_ACTIONS) {
|
|
304
|
-
const orig = page[method].bind(page);
|
|
305
|
-
page[method] = async (...args) => {
|
|
306
|
-
const start = Date.now();
|
|
307
|
-
try {
|
|
308
|
-
const result = await orig(...args);
|
|
309
|
-
parentLogAction(session, {
|
|
310
|
-
pageId,
|
|
311
|
-
action: method,
|
|
312
|
-
source: "agent",
|
|
313
|
-
url: typeof args[0] === "string" ? args[0] : page.url(),
|
|
314
|
-
duration: Date.now() - start,
|
|
315
|
-
success: true
|
|
316
|
-
});
|
|
317
|
-
onActivity?.();
|
|
318
|
-
return result;
|
|
319
|
-
} catch (err) {
|
|
320
|
-
parentLogAction(session, {
|
|
321
|
-
pageId,
|
|
322
|
-
action: method,
|
|
323
|
-
source: "agent",
|
|
324
|
-
url: typeof args[0] === "string" ? args[0] : void 0,
|
|
325
|
-
duration: Date.now() - start,
|
|
326
|
-
success: false,
|
|
327
|
-
error: err.message
|
|
328
|
-
});
|
|
329
|
-
onActivity?.();
|
|
330
|
-
throw err;
|
|
331
|
-
}
|
|
332
|
-
};
|
|
333
|
-
}
|
|
334
|
-
const LOCATOR_FACTORIES = [
|
|
335
|
-
"locator",
|
|
336
|
-
"getByRole",
|
|
337
|
-
"getByText",
|
|
338
|
-
"getByLabel",
|
|
339
|
-
"getByPlaceholder",
|
|
340
|
-
"getByAltText",
|
|
341
|
-
"getByTitle",
|
|
342
|
-
"getByTestId"
|
|
343
|
-
];
|
|
344
|
-
for (const factory of LOCATOR_FACTORIES) {
|
|
345
|
-
const orig = page[factory].bind(page);
|
|
346
|
-
page[factory] = (...factoryArgs) => {
|
|
347
|
-
const locator = orig(...factoryArgs);
|
|
348
|
-
const hint = formatHint(factory, factoryArgs);
|
|
349
|
-
return wrapLocator(locator, hint, session, page, pageId, onActivity);
|
|
350
|
-
};
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
export {
|
|
354
|
-
clearActionLog,
|
|
355
|
-
clearNetworkLog,
|
|
356
|
-
formatActionEntry,
|
|
357
|
-
formatNetworkEntry,
|
|
358
|
-
parentLogAction,
|
|
359
|
-
readActionLog,
|
|
360
|
-
readNetworkLog,
|
|
361
|
-
wrapPageForActionLogging
|
|
362
|
-
};
|
package/dist/cli/index.js
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { runLibrettoCLI } from "./cli.js";
|
|
2
|
-
import {
|
|
3
|
-
maybeConfigureLLMClientFactoryFromEnv,
|
|
4
|
-
setLLMClientFactory
|
|
5
|
-
} from "./core/context.js";
|
|
6
|
-
import { runClose } from "./commands/browser.js";
|
|
7
|
-
maybeConfigureLLMClientFactoryFromEnv();
|
|
8
|
-
void runLibrettoCLI();
|
|
9
|
-
export {
|
|
10
|
-
runClose,
|
|
11
|
-
runLibrettoCLI,
|
|
12
|
-
setLLMClientFactory
|
|
13
|
-
};
|
|
@@ -1,227 +0,0 @@
|
|
|
1
|
-
import { appendFileSync, existsSync, readFileSync } from "node:fs";
|
|
2
|
-
import { writeFile } from "node:fs/promises";
|
|
3
|
-
import { cwd } from "node:process";
|
|
4
|
-
import { isAbsolute, resolve } from "node:path";
|
|
5
|
-
import { pathToFileURL } from "node:url";
|
|
6
|
-
import {
|
|
7
|
-
launchBrowser
|
|
8
|
-
} from "../../index.js";
|
|
9
|
-
import { setSessionForPause } from "../../shared/debug/pause.js";
|
|
10
|
-
import { parseSessionStateContent } from "../../shared/state/index.js";
|
|
11
|
-
import { getProfilePath, normalizeDomain } from "../core/browser.js";
|
|
12
|
-
import {
|
|
13
|
-
getSessionActionsLogPath,
|
|
14
|
-
getSessionNetworkLogPath,
|
|
15
|
-
getSessionStatePath
|
|
16
|
-
} from "../core/context.js";
|
|
17
|
-
import { getPauseSignalPaths, removeSignalIfExists } from "../core/pause-signals.js";
|
|
18
|
-
import { installSessionTelemetry } from "../core/session-telemetry.js";
|
|
19
|
-
const LIBRETTO_WORKFLOW_BRAND = /* @__PURE__ */ Symbol.for("libretto.workflow");
|
|
20
|
-
const FAILURE_HOLD_POLL_INTERVAL_MS = 250;
|
|
21
|
-
function mirrorStdoutToFile(filePath) {
|
|
22
|
-
const stdout = process.stdout;
|
|
23
|
-
const originalWrite = stdout.write.bind(stdout);
|
|
24
|
-
stdout.write = ((chunk, ...args) => {
|
|
25
|
-
try {
|
|
26
|
-
const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk), "utf8");
|
|
27
|
-
appendFileSync(filePath, buffer);
|
|
28
|
-
} catch {
|
|
29
|
-
}
|
|
30
|
-
return originalWrite(chunk, ...args);
|
|
31
|
-
});
|
|
32
|
-
return () => {
|
|
33
|
-
stdout.write = originalWrite;
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
function readSessionStatePid(session) {
|
|
37
|
-
const statePath = getSessionStatePath(session);
|
|
38
|
-
if (!existsSync(statePath)) return null;
|
|
39
|
-
try {
|
|
40
|
-
return parseSessionStateContent(readFileSync(statePath, "utf8"), statePath).pid;
|
|
41
|
-
} catch {
|
|
42
|
-
return null;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
async function waitForFailureSessionRelease(args) {
|
|
46
|
-
const { session, expectedPid, logger } = args;
|
|
47
|
-
logger.info("run-failure-session-hold", { session, expectedPid });
|
|
48
|
-
while (true) {
|
|
49
|
-
const currentPid = readSessionStatePid(session);
|
|
50
|
-
if (currentPid !== expectedPid) {
|
|
51
|
-
logger.info("run-failure-session-released", {
|
|
52
|
-
session,
|
|
53
|
-
expectedPid,
|
|
54
|
-
currentPid
|
|
55
|
-
});
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
await new Promise(
|
|
59
|
-
(resolveWait) => setTimeout(resolveWait, FAILURE_HOLD_POLL_INTERVAL_MS)
|
|
60
|
-
);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
function isLoadedLibrettoWorkflow(value) {
|
|
64
|
-
if (!value || typeof value !== "object") return false;
|
|
65
|
-
const candidate = value;
|
|
66
|
-
return candidate[LIBRETTO_WORKFLOW_BRAND] === true && typeof candidate.run === "function" && !!candidate.metadata && typeof candidate.metadata === "object";
|
|
67
|
-
}
|
|
68
|
-
function resolveLocalAuthProfilePath(domain) {
|
|
69
|
-
return getProfilePath(normalizeDomain(domain));
|
|
70
|
-
}
|
|
71
|
-
function getMissingLocalAuthProfileError(args) {
|
|
72
|
-
const normalizedDomain = normalizeDomain(args.domain);
|
|
73
|
-
return [
|
|
74
|
-
`Local auth profile not found for domain "${normalizedDomain}".`,
|
|
75
|
-
`Expected profile file: ${args.profilePath}`,
|
|
76
|
-
"To create it:",
|
|
77
|
-
` 1. libretto-cli open https://${normalizedDomain} --headed --session ${args.session}`,
|
|
78
|
-
" 2. Log in manually in the browser window.",
|
|
79
|
-
` 3. libretto-cli save ${normalizedDomain} --session ${args.session}`
|
|
80
|
-
].join("\n");
|
|
81
|
-
}
|
|
82
|
-
function getAbsoluteIntegrationPath(integrationPath) {
|
|
83
|
-
const absolutePath = isAbsolute(integrationPath) ? integrationPath : resolve(cwd(), integrationPath);
|
|
84
|
-
if (!existsSync(absolutePath)) {
|
|
85
|
-
throw new Error(`Integration file does not exist: ${absolutePath}`);
|
|
86
|
-
}
|
|
87
|
-
return absolutePath;
|
|
88
|
-
}
|
|
89
|
-
async function loadWorkflowExport(absolutePath, exportName) {
|
|
90
|
-
let loadedModule;
|
|
91
|
-
try {
|
|
92
|
-
loadedModule = await import(pathToFileURL(absolutePath).href);
|
|
93
|
-
} catch (error) {
|
|
94
|
-
throw new Error(
|
|
95
|
-
`Failed to import integration module at ${absolutePath}: ${error instanceof Error ? error.message : String(error)}`
|
|
96
|
-
);
|
|
97
|
-
}
|
|
98
|
-
const targetExport = loadedModule[exportName];
|
|
99
|
-
if (!targetExport) {
|
|
100
|
-
const availableExports = Object.keys(loadedModule);
|
|
101
|
-
const detail = availableExports.length > 0 ? ` Available exports: ${availableExports.join(", ")}` : " The module has no exports.";
|
|
102
|
-
throw new Error(
|
|
103
|
-
`Export "${exportName}" was not found in ${absolutePath}.${detail}`
|
|
104
|
-
);
|
|
105
|
-
}
|
|
106
|
-
if (!isLoadedLibrettoWorkflow(targetExport)) {
|
|
107
|
-
throw new Error(
|
|
108
|
-
[
|
|
109
|
-
`Export "${exportName}" in ${absolutePath} is not a valid Libretto workflow.`,
|
|
110
|
-
"",
|
|
111
|
-
'A workflow must be created using the workflow() function from "libretto":',
|
|
112
|
-
"",
|
|
113
|
-
' import { workflow } from "libretto";',
|
|
114
|
-
"",
|
|
115
|
-
` export const ${exportName} = workflow<InputType, OutputType>(`,
|
|
116
|
-
" {},",
|
|
117
|
-
" async (ctx, input) => {",
|
|
118
|
-
" // ctx.page \u2014 Playwright Page instance",
|
|
119
|
-
" // ctx.logger \u2014 MinimalLogger",
|
|
120
|
-
" // ctx.services \u2014 injected dependencies (generic, default {})",
|
|
121
|
-
" // input \u2014 JSON-serializable input matching InputType",
|
|
122
|
-
" return output; // must match OutputType",
|
|
123
|
-
" },",
|
|
124
|
-
" );"
|
|
125
|
-
].join("\n")
|
|
126
|
-
);
|
|
127
|
-
}
|
|
128
|
-
return targetExport;
|
|
129
|
-
}
|
|
130
|
-
async function runIntegrationInternal(args, options) {
|
|
131
|
-
const { logger } = options;
|
|
132
|
-
const absolutePath = getAbsoluteIntegrationPath(args.integrationPath);
|
|
133
|
-
const workflow = await loadWorkflowExport(absolutePath, args.exportName);
|
|
134
|
-
const signalPaths = getPauseSignalPaths(args.session);
|
|
135
|
-
await removeSignalIfExists(signalPaths.pausedSignalPath);
|
|
136
|
-
await removeSignalIfExists(signalPaths.resumeSignalPath);
|
|
137
|
-
await removeSignalIfExists(signalPaths.completedSignalPath);
|
|
138
|
-
await removeSignalIfExists(signalPaths.failedSignalPath);
|
|
139
|
-
await removeSignalIfExists(signalPaths.outputSignalPath);
|
|
140
|
-
const restoreStdout = mirrorStdoutToFile(signalPaths.outputSignalPath);
|
|
141
|
-
console.log(
|
|
142
|
-
`Running integration "${args.exportName}" from ${absolutePath} (${args.headless ? "headless" : "headed"})...`
|
|
143
|
-
);
|
|
144
|
-
const integrationLogger = logger.withScope("integration-run", {
|
|
145
|
-
integrationPath: absolutePath,
|
|
146
|
-
integrationExport: args.exportName,
|
|
147
|
-
session: args.session
|
|
148
|
-
});
|
|
149
|
-
const authProfileDomain = args.authProfileDomain;
|
|
150
|
-
const storageStatePath = authProfileDomain ? resolveLocalAuthProfilePath(authProfileDomain) : void 0;
|
|
151
|
-
if (authProfileDomain && storageStatePath && !existsSync(storageStatePath)) {
|
|
152
|
-
throw new Error(
|
|
153
|
-
getMissingLocalAuthProfileError({
|
|
154
|
-
domain: authProfileDomain,
|
|
155
|
-
profilePath: storageStatePath,
|
|
156
|
-
session: args.session
|
|
157
|
-
})
|
|
158
|
-
);
|
|
159
|
-
}
|
|
160
|
-
const browserSession = await launchBrowser({
|
|
161
|
-
sessionName: args.session,
|
|
162
|
-
headless: args.headless,
|
|
163
|
-
storageStatePath
|
|
164
|
-
});
|
|
165
|
-
const actionsLogPath = getSessionActionsLogPath(args.session);
|
|
166
|
-
const networkLogPath = getSessionNetworkLogPath(args.session);
|
|
167
|
-
await installSessionTelemetry({
|
|
168
|
-
context: browserSession.context,
|
|
169
|
-
initialPage: browserSession.page,
|
|
170
|
-
includeUserDomActions: true,
|
|
171
|
-
logAction: (entry) => {
|
|
172
|
-
appendFileSync(actionsLogPath, JSON.stringify(entry) + "\n");
|
|
173
|
-
},
|
|
174
|
-
logNetwork: (entry) => {
|
|
175
|
-
appendFileSync(networkLogPath, JSON.stringify(entry) + "\n");
|
|
176
|
-
}
|
|
177
|
-
});
|
|
178
|
-
setSessionForPause(args.session);
|
|
179
|
-
const workflowContext = {
|
|
180
|
-
logger: integrationLogger,
|
|
181
|
-
page: browserSession.page,
|
|
182
|
-
services: {}
|
|
183
|
-
};
|
|
184
|
-
try {
|
|
185
|
-
try {
|
|
186
|
-
await workflow.run(workflowContext, args.params ?? {});
|
|
187
|
-
} catch (error) {
|
|
188
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
189
|
-
await writeFile(
|
|
190
|
-
signalPaths.failedSignalPath,
|
|
191
|
-
JSON.stringify(
|
|
192
|
-
{
|
|
193
|
-
failedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
194
|
-
message: errorMessage
|
|
195
|
-
},
|
|
196
|
-
null,
|
|
197
|
-
2
|
|
198
|
-
),
|
|
199
|
-
"utf8"
|
|
200
|
-
);
|
|
201
|
-
await waitForFailureSessionRelease({
|
|
202
|
-
session: args.session,
|
|
203
|
-
expectedPid: process.pid,
|
|
204
|
-
logger
|
|
205
|
-
});
|
|
206
|
-
return { status: "failed-held" };
|
|
207
|
-
}
|
|
208
|
-
await writeFile(
|
|
209
|
-
signalPaths.completedSignalPath,
|
|
210
|
-
JSON.stringify({ completedAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2),
|
|
211
|
-
"utf8"
|
|
212
|
-
);
|
|
213
|
-
console.log("Integration completed.");
|
|
214
|
-
return { status: "completed" };
|
|
215
|
-
} finally {
|
|
216
|
-
restoreStdout();
|
|
217
|
-
await browserSession.close();
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
async function runIntegrationFromFileInWorker(args, logger) {
|
|
221
|
-
return await runIntegrationInternal(args, {
|
|
222
|
-
logger
|
|
223
|
-
});
|
|
224
|
-
}
|
|
225
|
-
export {
|
|
226
|
-
runIntegrationFromFileInWorker
|
|
227
|
-
};
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
const RunIntegrationWorkerRequestSchema = z.object({
|
|
3
|
-
integrationPath: z.string().min(1),
|
|
4
|
-
exportName: z.string().min(1),
|
|
5
|
-
session: z.string().min(1),
|
|
6
|
-
params: z.unknown(),
|
|
7
|
-
headless: z.boolean(),
|
|
8
|
-
authProfileDomain: z.string().optional()
|
|
9
|
-
});
|
|
10
|
-
export {
|
|
11
|
-
RunIntegrationWorkerRequestSchema
|
|
12
|
-
};
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import { writeFile } from "node:fs/promises";
|
|
2
|
-
import { ZodError } from "zod";
|
|
3
|
-
import {
|
|
4
|
-
RunIntegrationWorkerRequestSchema
|
|
5
|
-
} from "./run-integration-worker-protocol.js";
|
|
6
|
-
import { runIntegrationFromFileInWorker } from "./run-integration-runtime.js";
|
|
7
|
-
import {
|
|
8
|
-
ensureLibrettoSetup,
|
|
9
|
-
withSessionLogger
|
|
10
|
-
} from "../core/context.js";
|
|
11
|
-
import { getPauseSignalPaths } from "../core/pause-signals.js";
|
|
12
|
-
function parseWorkerRequest(argv) {
|
|
13
|
-
const rawPayload = argv[2];
|
|
14
|
-
if (!rawPayload) {
|
|
15
|
-
throw new Error("Missing worker payload argument.");
|
|
16
|
-
}
|
|
17
|
-
let parsed;
|
|
18
|
-
try {
|
|
19
|
-
parsed = JSON.parse(rawPayload);
|
|
20
|
-
} catch (error) {
|
|
21
|
-
throw new Error(
|
|
22
|
-
`Invalid worker payload JSON: ${error instanceof Error ? error.message : String(error)}`
|
|
23
|
-
);
|
|
24
|
-
}
|
|
25
|
-
try {
|
|
26
|
-
return RunIntegrationWorkerRequestSchema.parse(parsed);
|
|
27
|
-
} catch (error) {
|
|
28
|
-
if (error instanceof ZodError) {
|
|
29
|
-
const details = error.issues.map((issue) => `${issue.path.join(".") || "root"}: ${issue.message}`).join("; ");
|
|
30
|
-
throw new Error(`Worker payload is invalid: ${details}`);
|
|
31
|
-
}
|
|
32
|
-
throw error;
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
async function main() {
|
|
36
|
-
let request = null;
|
|
37
|
-
let exitCode = 0;
|
|
38
|
-
try {
|
|
39
|
-
request = parseWorkerRequest(process.argv);
|
|
40
|
-
const workerRequest = request;
|
|
41
|
-
ensureLibrettoSetup();
|
|
42
|
-
await withSessionLogger(workerRequest.session, async (logger) => {
|
|
43
|
-
await runIntegrationFromFileInWorker(workerRequest, logger);
|
|
44
|
-
});
|
|
45
|
-
} catch (error) {
|
|
46
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
47
|
-
if (request) {
|
|
48
|
-
const { failedSignalPath } = getPauseSignalPaths(request.session);
|
|
49
|
-
await writeFile(
|
|
50
|
-
failedSignalPath,
|
|
51
|
-
JSON.stringify(
|
|
52
|
-
{
|
|
53
|
-
failedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
54
|
-
message
|
|
55
|
-
},
|
|
56
|
-
null,
|
|
57
|
-
2
|
|
58
|
-
),
|
|
59
|
-
"utf8"
|
|
60
|
-
);
|
|
61
|
-
}
|
|
62
|
-
exitCode = 1;
|
|
63
|
-
}
|
|
64
|
-
process.exit(exitCode);
|
|
65
|
-
}
|
|
66
|
-
void main();
|