libretto 0.4.4 → 0.5.1
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 +106 -36
- package/dist/cli/cli.js +39 -113
- package/dist/cli/commands/ai.js +1 -1
- package/dist/cli/commands/browser.js +87 -60
- package/dist/cli/commands/execution.js +201 -88
- package/dist/cli/commands/init.js +30 -8
- package/dist/cli/commands/logs.js +5 -6
- package/dist/cli/commands/shared.js +30 -29
- package/dist/cli/commands/snapshot.js +26 -39
- package/dist/cli/core/ai-config.js +9 -2
- package/dist/cli/core/api-snapshot-analyzer.js +15 -5
- package/dist/cli/core/browser.js +141 -33
- package/dist/cli/core/context.js +7 -18
- package/dist/cli/core/session-telemetry.js +5 -2
- package/dist/cli/core/session.js +23 -10
- package/dist/cli/core/snapshot-analyzer.js +16 -33
- package/dist/cli/core/snapshot-api-config.js +2 -6
- package/dist/cli/core/telemetry.js +10 -2
- package/dist/cli/framework/simple-cli.js +45 -25
- package/dist/cli/router.js +14 -21
- package/dist/cli/workers/run-integration-runtime.js +26 -7
- package/dist/cli/workers/run-integration-worker-protocol.js +3 -1
- package/dist/cli/workers/run-integration-worker.js +1 -4
- package/dist/index.d.ts +1 -2
- package/dist/index.js +7 -10
- package/dist/runtime/download/download.js +5 -1
- package/dist/runtime/extract/extract.js +11 -2
- package/dist/runtime/network/network.js +8 -1
- package/dist/runtime/recovery/agent.js +6 -2
- package/dist/runtime/recovery/errors.js +3 -1
- package/dist/runtime/recovery/recovery.js +3 -1
- package/dist/shared/condense-dom/condense-dom.js +6 -13
- package/dist/shared/config/config.d.ts +1 -9
- package/dist/shared/config/config.js +0 -18
- package/dist/shared/config/index.d.ts +2 -1
- package/dist/shared/config/index.js +0 -10
- package/dist/shared/debug/pause.js +9 -3
- package/dist/shared/instrumentation/instrument.js +101 -5
- package/dist/shared/llm/ai-sdk-adapter.js +3 -1
- package/dist/shared/llm/client.js +3 -1
- package/dist/shared/logger/index.js +4 -1
- package/dist/shared/paths/paths.js +2 -1
- package/dist/shared/paths/repo-root.d.ts +3 -0
- package/dist/shared/paths/repo-root.js +24 -0
- package/dist/shared/run/api.js +3 -1
- package/dist/shared/run/browser.js +7 -2
- package/dist/shared/state/session-state.d.ts +2 -1
- package/dist/shared/state/session-state.js +5 -2
- package/dist/shared/visualization/ghost-cursor.js +19 -10
- package/dist/shared/visualization/highlight.js +9 -6
- package/dist/shared/workflow/workflow.d.ts +4 -5
- package/dist/shared/workflow/workflow.js +3 -5
- package/package.json +11 -8
- package/scripts/check-skills-sync.mjs +25 -0
- package/scripts/compare-eval-summary.mjs +47 -0
- package/scripts/postinstall.mjs +26 -17
- package/scripts/prepare-release.sh +97 -0
- package/scripts/skills-libretto.mjs +103 -0
- package/scripts/summarize-evals.mjs +135 -0
- package/scripts/sync-skills.mjs +12 -0
- package/skills/libretto/SKILL.md +130 -377
- package/skills/libretto/references/auth-profiles.md +30 -0
- package/skills/libretto/{code-generation-rules.md → references/code-generation-rules.md} +27 -42
- package/skills/libretto/references/configuration-file-reference.md +53 -0
- package/skills/libretto/references/pages-and-page-targeting.md +29 -0
- package/skills/libretto/references/site-security-review.md +143 -0
- package/src/cli/cli.ts +86 -0
- package/src/cli/commands/ai.ts +35 -0
- package/src/cli/commands/browser.ts +189 -0
- package/src/cli/commands/execution.ts +822 -0
- package/src/cli/commands/init.ts +350 -0
- package/src/cli/commands/logs.ts +128 -0
- package/src/cli/commands/shared.ts +69 -0
- package/src/cli/commands/snapshot.ts +312 -0
- package/src/cli/core/ai-config.ts +264 -0
- package/src/cli/core/api-snapshot-analyzer.ts +108 -0
- package/src/cli/core/browser.ts +976 -0
- package/src/cli/core/context.ts +127 -0
- package/src/cli/core/pause-signals.ts +35 -0
- package/src/cli/core/session-telemetry.ts +564 -0
- package/src/cli/core/session.ts +223 -0
- package/src/cli/core/snapshot-analyzer.ts +855 -0
- package/src/cli/core/snapshot-api-config.ts +231 -0
- package/src/cli/core/telemetry.ts +459 -0
- package/src/cli/framework/simple-cli.ts +1340 -0
- package/src/cli/index.ts +13 -0
- package/src/cli/router.ts +20 -0
- package/src/cli/workers/run-integration-runtime.ts +338 -0
- package/src/cli/workers/run-integration-worker-protocol.ts +16 -0
- package/src/cli/workers/run-integration-worker.ts +72 -0
- package/src/index.ts +127 -0
- package/src/runtime/download/download.ts +104 -0
- package/src/runtime/download/index.ts +7 -0
- package/src/runtime/extract/extract.ts +102 -0
- package/src/runtime/extract/index.ts +1 -0
- package/src/runtime/network/index.ts +5 -0
- package/src/runtime/network/network.ts +119 -0
- package/{dist/runtime/recovery/agent.cjs → src/runtime/recovery/agent.ts} +114 -76
- package/src/runtime/recovery/errors.ts +155 -0
- package/src/runtime/recovery/index.ts +7 -0
- package/src/runtime/recovery/recovery.ts +53 -0
- package/{dist/shared/condense-dom/condense-dom.cjs → src/shared/condense-dom/condense-dom.ts} +249 -124
- package/src/shared/config/config.ts +3 -0
- package/src/shared/config/index.ts +0 -0
- package/src/shared/debug/index.ts +1 -0
- package/src/shared/debug/pause.ts +91 -0
- package/src/shared/instrumentation/errors.ts +84 -0
- package/src/shared/instrumentation/index.ts +9 -0
- package/src/shared/instrumentation/instrument.ts +406 -0
- package/src/shared/llm/ai-sdk-adapter.ts +81 -0
- package/{dist/shared/llm/client.cjs → src/shared/llm/client.ts} +86 -80
- package/src/shared/llm/index.ts +3 -0
- package/src/shared/llm/types.ts +63 -0
- package/src/shared/logger/index.ts +13 -0
- package/src/shared/logger/logger.ts +358 -0
- package/src/shared/logger/sinks.ts +148 -0
- package/src/shared/paths/paths.ts +110 -0
- package/src/shared/paths/repo-root.ts +27 -0
- package/src/shared/run/api.ts +6 -0
- package/src/shared/run/browser.ts +107 -0
- package/src/shared/state/index.ts +11 -0
- package/src/shared/state/session-state.ts +77 -0
- package/src/shared/visualization/ghost-cursor.ts +213 -0
- package/src/shared/visualization/highlight.ts +149 -0
- package/src/shared/visualization/index.ts +18 -0
- package/src/shared/workflow/workflow.ts +36 -0
- package/dist/index.cjs +0 -144
- package/dist/index.d.cts +0 -21
- package/dist/runtime/download/download.cjs +0 -70
- package/dist/runtime/download/download.d.cts +0 -35
- package/dist/runtime/download/index.cjs +0 -30
- package/dist/runtime/download/index.d.cts +0 -3
- package/dist/runtime/extract/extract.cjs +0 -88
- package/dist/runtime/extract/extract.d.cts +0 -23
- package/dist/runtime/extract/index.cjs +0 -28
- package/dist/runtime/extract/index.d.cts +0 -5
- package/dist/runtime/network/index.cjs +0 -28
- package/dist/runtime/network/index.d.cts +0 -4
- package/dist/runtime/network/network.cjs +0 -91
- package/dist/runtime/network/network.d.cts +0 -28
- package/dist/runtime/recovery/agent.d.cts +0 -13
- package/dist/runtime/recovery/errors.cjs +0 -124
- package/dist/runtime/recovery/errors.d.cts +0 -31
- package/dist/runtime/recovery/index.cjs +0 -34
- package/dist/runtime/recovery/index.d.cts +0 -7
- package/dist/runtime/recovery/recovery.cjs +0 -55
- package/dist/runtime/recovery/recovery.d.cts +0 -12
- package/dist/shared/condense-dom/condense-dom.d.cts +0 -34
- package/dist/shared/config/config.cjs +0 -44
- package/dist/shared/config/config.d.cts +0 -10
- package/dist/shared/config/index.cjs +0 -32
- package/dist/shared/config/index.d.cts +0 -1
- package/dist/shared/debug/index.cjs +0 -28
- package/dist/shared/debug/index.d.cts +0 -1
- package/dist/shared/debug/pause.cjs +0 -86
- package/dist/shared/debug/pause.d.cts +0 -12
- package/dist/shared/instrumentation/errors.cjs +0 -81
- package/dist/shared/instrumentation/errors.d.cts +0 -12
- package/dist/shared/instrumentation/index.cjs +0 -35
- package/dist/shared/instrumentation/index.d.cts +0 -6
- package/dist/shared/instrumentation/instrument.cjs +0 -206
- package/dist/shared/instrumentation/instrument.d.cts +0 -32
- package/dist/shared/llm/ai-sdk-adapter.cjs +0 -71
- package/dist/shared/llm/ai-sdk-adapter.d.cts +0 -22
- package/dist/shared/llm/client.d.cts +0 -13
- package/dist/shared/llm/index.cjs +0 -31
- package/dist/shared/llm/index.d.cts +0 -5
- package/dist/shared/llm/types.cjs +0 -16
- package/dist/shared/llm/types.d.cts +0 -67
- package/dist/shared/logger/index.cjs +0 -37
- package/dist/shared/logger/index.d.cts +0 -2
- package/dist/shared/logger/logger.cjs +0 -232
- package/dist/shared/logger/logger.d.cts +0 -86
- package/dist/shared/logger/sinks.cjs +0 -160
- package/dist/shared/logger/sinks.d.cts +0 -9
- package/dist/shared/paths/paths.cjs +0 -104
- package/dist/shared/paths/paths.d.cts +0 -10
- package/dist/shared/run/api.cjs +0 -28
- package/dist/shared/run/api.d.cts +0 -2
- package/dist/shared/run/browser.cjs +0 -98
- package/dist/shared/run/browser.d.cts +0 -22
- package/dist/shared/state/index.cjs +0 -38
- package/dist/shared/state/index.d.cts +0 -2
- package/dist/shared/state/session-state.cjs +0 -92
- package/dist/shared/state/session-state.d.cts +0 -40
- package/dist/shared/visualization/ghost-cursor.cjs +0 -174
- package/dist/shared/visualization/ghost-cursor.d.cts +0 -37
- package/dist/shared/visualization/highlight.cjs +0 -134
- package/dist/shared/visualization/highlight.d.cts +0 -22
- package/dist/shared/visualization/index.cjs +0 -45
- package/dist/shared/visualization/index.d.cts +0 -3
- package/dist/shared/workflow/workflow.cjs +0 -47
- package/dist/shared/workflow/workflow.d.cts +0 -21
- package/skills/libretto/integration-approach-selection.md +0 -174
|
@@ -1,10 +1,2 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Runtime configuration for libretto.
|
|
3
|
-
*
|
|
4
|
-
* Values are derived from environment variables only.
|
|
5
|
-
*/
|
|
6
|
-
declare function isDebugMode(): boolean;
|
|
7
|
-
declare function isDryRun(): boolean;
|
|
8
|
-
declare function shouldPauseBeforeMutation(): boolean;
|
|
9
1
|
|
|
10
|
-
export {
|
|
2
|
+
export { }
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
function isDebugMode() {
|
|
2
|
-
return process.env.LIBRETTO_DEBUG === "true";
|
|
3
|
-
}
|
|
4
|
-
function isDryRun() {
|
|
5
|
-
const explicit = process.env.LIBRETTO_DRY_RUN;
|
|
6
|
-
if (explicit !== void 0) {
|
|
7
|
-
return explicit === "true";
|
|
8
|
-
}
|
|
9
|
-
return process.env.NODE_ENV === "development";
|
|
10
|
-
}
|
|
11
|
-
function shouldPauseBeforeMutation() {
|
|
12
|
-
return isDryRun() && isDebugMode();
|
|
13
|
-
}
|
|
14
|
-
export {
|
|
15
|
-
isDebugMode,
|
|
16
|
-
isDryRun,
|
|
17
|
-
shouldPauseBeforeMutation
|
|
18
|
-
};
|
|
@@ -1 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
|
|
2
|
+
export { }
|
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
2
|
import { mkdir, writeFile } from "node:fs/promises";
|
|
3
3
|
import { getSessionDir } from "../../cli/core/context.js";
|
|
4
|
-
import {
|
|
5
|
-
|
|
4
|
+
import {
|
|
5
|
+
getPauseSignalPaths,
|
|
6
|
+
removeSignalIfExists
|
|
7
|
+
} from "../../cli/core/pause-signals.js";
|
|
8
|
+
import {
|
|
9
|
+
listSessionsWithStateFile,
|
|
10
|
+
readSessionState
|
|
11
|
+
} from "../../cli/core/session.js";
|
|
6
12
|
function isPidRunning(pid) {
|
|
7
13
|
try {
|
|
8
14
|
process.kill(pid, 0);
|
|
@@ -14,7 +20,7 @@ function isPidRunning(pid) {
|
|
|
14
20
|
function getRunningSessions() {
|
|
15
21
|
return listSessionsWithStateFile().filter((candidate) => {
|
|
16
22
|
const state = readSessionState(candidate);
|
|
17
|
-
return state !== null && isPidRunning(state.pid);
|
|
23
|
+
return state !== null && state.pid != null && isPidRunning(state.pid);
|
|
18
24
|
});
|
|
19
25
|
}
|
|
20
26
|
function throwMissingSessionError() {
|
|
@@ -25,6 +25,7 @@ const LOCATOR_ACTIONS = [
|
|
|
25
25
|
];
|
|
26
26
|
const NAV_ACTIONS = ["goto", "reload", "goBack", "goForward"];
|
|
27
27
|
const POINTER_ACTIONS = /* @__PURE__ */ new Set(["click", "dblclick", "hover"]);
|
|
28
|
+
const instrumentedTargets = /* @__PURE__ */ new WeakSet();
|
|
28
29
|
const pageQueues = /* @__PURE__ */ new WeakMap();
|
|
29
30
|
function enqueue(page, fn) {
|
|
30
31
|
const prev = pageQueues.get(page) ?? Promise.resolve();
|
|
@@ -96,11 +97,93 @@ function wrapLocatorActions(locator, page, opts) {
|
|
|
96
97
|
};
|
|
97
98
|
}
|
|
98
99
|
}
|
|
100
|
+
const LOCATOR_FACTORY_METHODS = [
|
|
101
|
+
"locator",
|
|
102
|
+
"getByRole",
|
|
103
|
+
"getByText",
|
|
104
|
+
"getByLabel",
|
|
105
|
+
"getByPlaceholder",
|
|
106
|
+
"getByAltText",
|
|
107
|
+
"getByTitle",
|
|
108
|
+
"getByTestId",
|
|
109
|
+
"filter",
|
|
110
|
+
"and",
|
|
111
|
+
"or",
|
|
112
|
+
"first",
|
|
113
|
+
"last",
|
|
114
|
+
"nth"
|
|
115
|
+
];
|
|
116
|
+
const FRAME_LOCATOR_FACTORY_METHODS = [
|
|
117
|
+
"locator",
|
|
118
|
+
"getByRole",
|
|
119
|
+
"getByText",
|
|
120
|
+
"getByLabel",
|
|
121
|
+
"getByPlaceholder",
|
|
122
|
+
"getByAltText",
|
|
123
|
+
"getByTitle",
|
|
124
|
+
"getByTestId",
|
|
125
|
+
"owner",
|
|
126
|
+
"first",
|
|
127
|
+
"last",
|
|
128
|
+
"nth"
|
|
129
|
+
];
|
|
130
|
+
function instrumentLocator(locator, page, opts) {
|
|
131
|
+
const target = locator;
|
|
132
|
+
if (instrumentedTargets.has(target)) {
|
|
133
|
+
return locator;
|
|
134
|
+
}
|
|
135
|
+
instrumentedTargets.add(target);
|
|
136
|
+
wrapLocatorActions(locator, page, opts);
|
|
137
|
+
for (const method of LOCATOR_FACTORY_METHODS) {
|
|
138
|
+
if (typeof locator[method] !== "function") continue;
|
|
139
|
+
const orig = locator[method].bind(locator);
|
|
140
|
+
locator[method] = (...args) => {
|
|
141
|
+
const nextLocator = orig(...args);
|
|
142
|
+
return instrumentLocator(nextLocator, page, opts);
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
if (typeof locator.contentFrame === "function") {
|
|
146
|
+
const origContentFrame = locator.contentFrame.bind(locator);
|
|
147
|
+
locator.contentFrame = (...args) => {
|
|
148
|
+
const frameLocator = origContentFrame(...args);
|
|
149
|
+
return instrumentFrameLocator(frameLocator, page, opts);
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
return locator;
|
|
153
|
+
}
|
|
154
|
+
function instrumentFrameLocator(frameLocator, page, opts) {
|
|
155
|
+
const target = frameLocator;
|
|
156
|
+
if (instrumentedTargets.has(target)) {
|
|
157
|
+
return frameLocator;
|
|
158
|
+
}
|
|
159
|
+
instrumentedTargets.add(target);
|
|
160
|
+
for (const method of FRAME_LOCATOR_FACTORY_METHODS) {
|
|
161
|
+
if (typeof frameLocator[method] !== "function") continue;
|
|
162
|
+
const orig = frameLocator[method].bind(frameLocator);
|
|
163
|
+
frameLocator[method] = (...args) => {
|
|
164
|
+
const result = orig(...args);
|
|
165
|
+
if (method === "first" || method === "last" || method === "nth") {
|
|
166
|
+
return instrumentFrameLocator(result, page, opts);
|
|
167
|
+
}
|
|
168
|
+
return instrumentLocator(result, page, opts);
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
if (typeof frameLocator.frameLocator === "function") {
|
|
172
|
+
const origFrameLocator = frameLocator.frameLocator.bind(
|
|
173
|
+
frameLocator
|
|
174
|
+
);
|
|
175
|
+
frameLocator.frameLocator = (...args) => {
|
|
176
|
+
const nestedFrameLocator = origFrameLocator(...args);
|
|
177
|
+
return instrumentFrameLocator(nestedFrameLocator, page, opts);
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
return frameLocator;
|
|
181
|
+
}
|
|
99
182
|
function isTimeoutError(err) {
|
|
100
183
|
if (!err || typeof err.message !== "string") return false;
|
|
101
184
|
return err.message.includes("Timeout") || err.message.includes("timeout") || err.name === "TimeoutError";
|
|
102
185
|
}
|
|
103
|
-
const
|
|
186
|
+
const PAGE_LOCATOR_FACTORIES = [
|
|
104
187
|
"locator",
|
|
105
188
|
"getByRole",
|
|
106
189
|
"getByText",
|
|
@@ -110,6 +193,7 @@ const LOCATOR_FACTORIES = [
|
|
|
110
193
|
"getByTitle",
|
|
111
194
|
"getByTestId"
|
|
112
195
|
];
|
|
196
|
+
const PAGE_FRAME_LOCATOR_FACTORIES = ["frameLocator"];
|
|
113
197
|
async function installInstrumentation(page, options) {
|
|
114
198
|
if (page.__librettoInstrumented) return;
|
|
115
199
|
page.__librettoInstrumented = true;
|
|
@@ -129,7 +213,12 @@ async function installInstrumentation(page, options) {
|
|
|
129
213
|
try {
|
|
130
214
|
const loc = page.locator(args[0]);
|
|
131
215
|
const box = await loc.boundingBox();
|
|
132
|
-
await visualizeBeforeAction(
|
|
216
|
+
await visualizeBeforeAction(
|
|
217
|
+
page,
|
|
218
|
+
box,
|
|
219
|
+
method,
|
|
220
|
+
highlightBeforeActionMs
|
|
221
|
+
);
|
|
133
222
|
} catch {
|
|
134
223
|
}
|
|
135
224
|
});
|
|
@@ -161,13 +250,20 @@ async function installInstrumentation(page, options) {
|
|
|
161
250
|
return orig(...args);
|
|
162
251
|
};
|
|
163
252
|
}
|
|
164
|
-
for (const factory of
|
|
253
|
+
for (const factory of PAGE_LOCATOR_FACTORIES) {
|
|
165
254
|
if (typeof page[factory] !== "function") continue;
|
|
166
255
|
const origFactory = page[factory].bind(page);
|
|
167
256
|
page[factory] = (...factoryArgs) => {
|
|
168
257
|
const locator = origFactory(...factoryArgs);
|
|
169
|
-
|
|
170
|
-
|
|
258
|
+
return instrumentLocator(locator, page, mergedOpts);
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
for (const factory of PAGE_FRAME_LOCATOR_FACTORIES) {
|
|
262
|
+
if (typeof page[factory] !== "function") continue;
|
|
263
|
+
const origFactory = page[factory].bind(page);
|
|
264
|
+
page[factory] = (...factoryArgs) => {
|
|
265
|
+
const frameLocator = origFactory(...factoryArgs);
|
|
266
|
+
return instrumentFrameLocator(frameLocator, page, mergedOpts);
|
|
171
267
|
};
|
|
172
268
|
}
|
|
173
269
|
}
|
|
@@ -18,7 +18,9 @@ function createLLMClientFromModel(model) {
|
|
|
18
18
|
if (msg.role === "assistant") {
|
|
19
19
|
return {
|
|
20
20
|
role: "assistant",
|
|
21
|
-
content: msg.content.filter(
|
|
21
|
+
content: msg.content.filter(
|
|
22
|
+
(part) => part.type === "text"
|
|
23
|
+
).map((part) => ({ type: "text", text: part.text }))
|
|
22
24
|
};
|
|
23
25
|
}
|
|
24
26
|
return {
|
|
@@ -121,7 +121,9 @@ function convertUserContentParts(parts) {
|
|
|
121
121
|
});
|
|
122
122
|
}
|
|
123
123
|
function convertAssistantContentParts(parts) {
|
|
124
|
-
return parts.filter(
|
|
124
|
+
return parts.filter(
|
|
125
|
+
(part) => part.type === "text"
|
|
126
|
+
).map((part) => ({ type: "text", text: part.text }));
|
|
125
127
|
}
|
|
126
128
|
function convertMessages(messages) {
|
|
127
129
|
return messages.map((msg) => {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { mkdirSync } from "node:fs";
|
|
2
2
|
import { dirname, join } from "node:path";
|
|
3
|
+
import { resolveLibrettoRepoRoot } from "./repo-root.js";
|
|
3
4
|
const LIBRETTO_DIRNAME = ".libretto";
|
|
4
5
|
const LIBRETTO_SESSIONS_DIRNAME = "sessions";
|
|
5
6
|
const SESSION_STATE_FILENAME = "state.json";
|
|
@@ -8,7 +9,7 @@ const RUNNER_LOG_FILENAME = "logs.jsonl";
|
|
|
8
9
|
const PAUSED_SIGNAL_SUFFIX = "paused";
|
|
9
10
|
const RESUME_SIGNAL_SUFFIX = "resume";
|
|
10
11
|
function getLibrettoRoot(cwd = process.cwd()) {
|
|
11
|
-
return join(cwd, LIBRETTO_DIRNAME);
|
|
12
|
+
return join(resolveLibrettoRepoRoot(cwd), LIBRETTO_DIRNAME);
|
|
12
13
|
}
|
|
13
14
|
function getLibrettoSessionsDir(cwd = process.cwd()) {
|
|
14
15
|
return join(getLibrettoRoot(cwd), LIBRETTO_SESSIONS_DIRNAME);
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
const repoRootCache = /* @__PURE__ */ new Map();
|
|
4
|
+
function resolveLibrettoRepoRoot(cwd = process.cwd()) {
|
|
5
|
+
const override = process.env.LIBRETTO_REPO_ROOT?.trim();
|
|
6
|
+
if (override) {
|
|
7
|
+
return resolve(override);
|
|
8
|
+
}
|
|
9
|
+
const normalizedCwd = resolve(cwd);
|
|
10
|
+
const cached = repoRootCache.get(normalizedCwd);
|
|
11
|
+
if (cached) {
|
|
12
|
+
return cached;
|
|
13
|
+
}
|
|
14
|
+
const result = spawnSync("git", ["rev-parse", "--show-toplevel"], {
|
|
15
|
+
cwd: normalizedCwd,
|
|
16
|
+
encoding: "utf-8"
|
|
17
|
+
});
|
|
18
|
+
const repoRoot = result.status === 0 && result.stdout ? result.stdout.trim() : normalizedCwd;
|
|
19
|
+
repoRootCache.set(normalizedCwd, repoRoot);
|
|
20
|
+
return repoRoot;
|
|
21
|
+
}
|
|
22
|
+
export {
|
|
23
|
+
resolveLibrettoRepoRoot
|
|
24
|
+
};
|
package/dist/shared/run/api.js
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
chromium
|
|
3
|
+
} from "playwright";
|
|
2
4
|
import { createServer } from "node:net";
|
|
3
5
|
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
4
6
|
import { ensureLibrettoSessionStatePath } from "../paths/paths.js";
|
|
5
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
SESSION_STATE_VERSION,
|
|
9
|
+
SessionStateFileSchema
|
|
10
|
+
} from "../state/session-state.js";
|
|
6
11
|
async function pickFreePort() {
|
|
7
12
|
return await new Promise((resolve, reject) => {
|
|
8
13
|
const server = createServer();
|
|
@@ -15,7 +15,8 @@ declare const SessionViewportSchema: z.ZodObject<{
|
|
|
15
15
|
declare const SessionStateFileSchema: z.ZodObject<{
|
|
16
16
|
version: z.ZodLiteral<1>;
|
|
17
17
|
port: z.ZodNumber;
|
|
18
|
-
pid: z.ZodNumber
|
|
18
|
+
pid: z.ZodOptional<z.ZodNumber>;
|
|
19
|
+
cdpEndpoint: z.ZodOptional<z.ZodString>;
|
|
19
20
|
session: z.ZodString;
|
|
20
21
|
startedAt: z.ZodString;
|
|
21
22
|
status: z.ZodOptional<z.ZodEnum<{
|
|
@@ -14,7 +14,8 @@ const SessionViewportSchema = z.object({
|
|
|
14
14
|
const SessionStateFileSchema = z.object({
|
|
15
15
|
version: z.literal(SESSION_STATE_VERSION),
|
|
16
16
|
port: z.number().int().min(0).max(65535),
|
|
17
|
-
pid: z.number().int(),
|
|
17
|
+
pid: z.number().int().optional(),
|
|
18
|
+
cdpEndpoint: z.string().url().optional(),
|
|
18
19
|
session: z.string().min(1),
|
|
19
20
|
startedAt: z.string().datetime({ offset: true }),
|
|
20
21
|
status: SessionStatusSchema.optional(),
|
|
@@ -29,7 +30,9 @@ function formatIssues(error) {
|
|
|
29
30
|
function parseSessionStateData(rawState, source) {
|
|
30
31
|
const parsed = SessionStateFileSchema.safeParse(rawState);
|
|
31
32
|
if (!parsed.success) {
|
|
32
|
-
throw new Error(
|
|
33
|
+
throw new Error(
|
|
34
|
+
`Session state at ${source} is invalid: ${formatIssues(parsed.error)}`
|
|
35
|
+
);
|
|
33
36
|
}
|
|
34
37
|
const { version: _version, ...state } = parsed.data;
|
|
35
38
|
return state;
|
|
@@ -64,7 +64,13 @@ async function moveGhostCursor(page, target) {
|
|
|
64
64
|
el.style.transition = `transform ${duration}ms ${easing}`;
|
|
65
65
|
el.style.transform = `translate3d(${x}px, ${y}px, 0)`;
|
|
66
66
|
},
|
|
67
|
-
{
|
|
67
|
+
{
|
|
68
|
+
id: CURSOR_ID,
|
|
69
|
+
x: target.x,
|
|
70
|
+
y: target.y,
|
|
71
|
+
duration: durationMs,
|
|
72
|
+
easing: opts.easing
|
|
73
|
+
}
|
|
68
74
|
);
|
|
69
75
|
await page.waitForTimeout(durationMs);
|
|
70
76
|
} catch {
|
|
@@ -122,15 +128,18 @@ async function hideGhostCursor(page) {
|
|
|
122
128
|
}
|
|
123
129
|
async function getGhostCursorPosition(page) {
|
|
124
130
|
try {
|
|
125
|
-
return await page.evaluate(
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
131
|
+
return await page.evaluate(
|
|
132
|
+
({ id }) => {
|
|
133
|
+
const el = document.getElementById(id);
|
|
134
|
+
if (!el) return null;
|
|
135
|
+
const match = el.style.transform.match(
|
|
136
|
+
/translate3d\(\s*([\d.-]+)px\s*,\s*([\d.-]+)px/
|
|
137
|
+
);
|
|
138
|
+
if (!match) return null;
|
|
139
|
+
return { x: parseFloat(match[1]), y: parseFloat(match[2]) };
|
|
140
|
+
},
|
|
141
|
+
{ id: CURSOR_ID }
|
|
142
|
+
);
|
|
134
143
|
} catch {
|
|
135
144
|
return null;
|
|
136
145
|
}
|
|
@@ -92,12 +92,15 @@ async function showHighlight(page, params) {
|
|
|
92
92
|
}
|
|
93
93
|
async function clearHighlights(page) {
|
|
94
94
|
try {
|
|
95
|
-
await page.evaluate(
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
95
|
+
await page.evaluate(
|
|
96
|
+
({ layerId }) => {
|
|
97
|
+
const layer = document.getElementById(layerId);
|
|
98
|
+
if (!layer) return;
|
|
99
|
+
const rects = layer.querySelectorAll(".__libretto_highlight_rect__");
|
|
100
|
+
rects.forEach((r) => r.remove());
|
|
101
|
+
},
|
|
102
|
+
{ layerId: LAYER_ID }
|
|
103
|
+
);
|
|
101
104
|
} catch {
|
|
102
105
|
}
|
|
103
106
|
}
|
|
@@ -2,8 +2,8 @@ import { Page } from 'playwright';
|
|
|
2
2
|
import { MinimalLogger } from '../logger/logger.js';
|
|
3
3
|
|
|
4
4
|
declare const LIBRETTO_WORKFLOW_BRAND: unique symbol;
|
|
5
|
-
type LibrettoWorkflowMetadata = {};
|
|
6
5
|
type LibrettoWorkflowContext<S = {}> = {
|
|
6
|
+
session: string;
|
|
7
7
|
page: Page;
|
|
8
8
|
logger: MinimalLogger;
|
|
9
9
|
services: S;
|
|
@@ -11,11 +11,10 @@ type LibrettoWorkflowContext<S = {}> = {
|
|
|
11
11
|
type LibrettoWorkflowHandler<Input = unknown, Output = unknown, S = {}> = (ctx: LibrettoWorkflowContext<S>, input: Input) => Promise<Output>;
|
|
12
12
|
declare class LibrettoWorkflow<Input = unknown, Output = unknown, S = {}> {
|
|
13
13
|
readonly [LIBRETTO_WORKFLOW_BRAND] = true;
|
|
14
|
-
readonly metadata: LibrettoWorkflowMetadata;
|
|
15
14
|
private readonly handler;
|
|
16
|
-
constructor(
|
|
15
|
+
constructor(handler: LibrettoWorkflowHandler<Input, Output, S>);
|
|
17
16
|
run(ctx: LibrettoWorkflowContext<S>, input: Input): Promise<Output>;
|
|
18
17
|
}
|
|
19
|
-
declare function workflow<Input = unknown, Output = unknown, S = {}>(
|
|
18
|
+
declare function workflow<Input = unknown, Output = unknown, S = {}>(handler: LibrettoWorkflowHandler<Input, Output, S>): LibrettoWorkflow<Input, Output, S>;
|
|
20
19
|
|
|
21
|
-
export { LIBRETTO_WORKFLOW_BRAND, LibrettoWorkflow, type LibrettoWorkflowContext, type LibrettoWorkflowHandler,
|
|
20
|
+
export { LIBRETTO_WORKFLOW_BRAND, LibrettoWorkflow, type LibrettoWorkflowContext, type LibrettoWorkflowHandler, workflow };
|
|
@@ -1,18 +1,16 @@
|
|
|
1
1
|
const LIBRETTO_WORKFLOW_BRAND = /* @__PURE__ */ Symbol.for("libretto.workflow");
|
|
2
2
|
class LibrettoWorkflow {
|
|
3
3
|
[LIBRETTO_WORKFLOW_BRAND] = true;
|
|
4
|
-
metadata;
|
|
5
4
|
handler;
|
|
6
|
-
constructor(
|
|
7
|
-
this.metadata = metadata;
|
|
5
|
+
constructor(handler) {
|
|
8
6
|
this.handler = handler;
|
|
9
7
|
}
|
|
10
8
|
async run(ctx, input) {
|
|
11
9
|
return this.handler(ctx, input);
|
|
12
10
|
}
|
|
13
11
|
}
|
|
14
|
-
function workflow(
|
|
15
|
-
return new LibrettoWorkflow(
|
|
12
|
+
function workflow(handler) {
|
|
13
|
+
return new LibrettoWorkflow(handler);
|
|
16
14
|
}
|
|
17
15
|
export {
|
|
18
16
|
LIBRETTO_WORKFLOW_BRAND,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "libretto",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "AI-powered browser automation library and CLI built on Playwright",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -14,31 +14,33 @@
|
|
|
14
14
|
},
|
|
15
15
|
"files": [
|
|
16
16
|
"dist",
|
|
17
|
+
"src",
|
|
17
18
|
"scripts",
|
|
18
19
|
"skills/libretto"
|
|
19
20
|
],
|
|
21
|
+
"types": "./dist/index.d.ts",
|
|
20
22
|
"bin": {
|
|
21
|
-
"libretto": "./dist/cli/index.js"
|
|
22
|
-
"libretto-cli": "./dist/cli/index.js"
|
|
23
|
+
"libretto": "./dist/cli/index.js"
|
|
23
24
|
},
|
|
24
25
|
"exports": {
|
|
25
26
|
".": {
|
|
26
27
|
"types": "./dist/index.d.ts",
|
|
27
28
|
"import": "./dist/index.js",
|
|
28
|
-
"
|
|
29
|
+
"default": "./dist/index.js"
|
|
29
30
|
}
|
|
30
31
|
},
|
|
31
32
|
"scripts": {
|
|
32
33
|
"postinstall": "node scripts/postinstall.mjs",
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"build
|
|
34
|
+
"sync-skills": "node scripts/sync-skills.mjs",
|
|
35
|
+
"check:skills": "node scripts/check-skills-sync.mjs",
|
|
36
|
+
"build": "tsup --config tsup.config.ts",
|
|
36
37
|
"type-check": "tsc --noEmit",
|
|
37
38
|
"test": "pnpm run build && vitest run",
|
|
38
39
|
"eval": "pnpm run build && vitest run --config vitest.evals.config.ts",
|
|
39
40
|
"benchmark": "pnpm run build && tsx benchmarks/run.ts",
|
|
40
41
|
"test:watch": "vitest",
|
|
41
42
|
"cli": "node dist/index.js",
|
|
43
|
+
"prepare-release": "bash ./scripts/prepare-release.sh",
|
|
42
44
|
"prepack": "pnpm run build"
|
|
43
45
|
},
|
|
44
46
|
"peerDependencies": {
|
|
@@ -62,12 +64,13 @@
|
|
|
62
64
|
}
|
|
63
65
|
},
|
|
64
66
|
"devDependencies": {
|
|
65
|
-
"@anthropic-ai/claude-agent-sdk": "^0.2.75",
|
|
66
67
|
"@ai-sdk/anthropic": "^3.0.58",
|
|
67
68
|
"@ai-sdk/google": "^3.0.51",
|
|
68
69
|
"@ai-sdk/google-vertex": "^4.0.80",
|
|
69
70
|
"@ai-sdk/openai": "^3.0.41",
|
|
71
|
+
"@anthropic-ai/claude-agent-sdk": "^0.2.75",
|
|
70
72
|
"@types/node": "^25.5.0",
|
|
73
|
+
"glimpseui": "^0.5.1",
|
|
71
74
|
"openai": "^6.29.0",
|
|
72
75
|
"tsup": "^8.5.1",
|
|
73
76
|
"typescript": "^5.9.3",
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
|
|
6
|
+
import { compareSkillDirs, SKILL_DIRS } from "./skills-libretto.mjs";
|
|
7
|
+
|
|
8
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
const repoRoot = join(__dirname, "..");
|
|
10
|
+
const result = compareSkillDirs(repoRoot);
|
|
11
|
+
|
|
12
|
+
if (result.ok) {
|
|
13
|
+
console.log(
|
|
14
|
+
`libretto: verified identical skill mirrors across ${SKILL_DIRS.join(", ")}`,
|
|
15
|
+
);
|
|
16
|
+
process.exit(0);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
console.error("libretto: skill directories must be identical:");
|
|
20
|
+
for (const issue of result.issues) {
|
|
21
|
+
console.error(`- ${issue}`);
|
|
22
|
+
}
|
|
23
|
+
console.error("");
|
|
24
|
+
console.error("Run `pnpm i` to resync the mirrors in this repository.");
|
|
25
|
+
process.exit(1);
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
4
|
+
import { resolve } from "node:path";
|
|
5
|
+
|
|
6
|
+
function usage() {
|
|
7
|
+
console.error(
|
|
8
|
+
"Usage: node scripts/compare-eval-summary.mjs <baseline-summary.json> <current-summary.json> [threshold-percent]",
|
|
9
|
+
);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const [, , baselineArg, currentArg, thresholdArg] = process.argv;
|
|
13
|
+
|
|
14
|
+
if (!baselineArg || !currentArg) {
|
|
15
|
+
usage();
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const baseline = JSON.parse(readFileSync(resolve(baselineArg), "utf8"));
|
|
20
|
+
const current = JSON.parse(readFileSync(resolve(currentArg), "utf8"));
|
|
21
|
+
const threshold = thresholdArg ? Number(thresholdArg) : 5;
|
|
22
|
+
|
|
23
|
+
if (!Number.isFinite(threshold) || threshold < 0) {
|
|
24
|
+
console.error(`Invalid threshold percent: ${thresholdArg}`);
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const delta = Number((current.percent - baseline.percent).toFixed(2));
|
|
29
|
+
const withinThreshold = Math.abs(delta) <= threshold;
|
|
30
|
+
|
|
31
|
+
const lines = [
|
|
32
|
+
"# Eval Baseline Comparison",
|
|
33
|
+
"",
|
|
34
|
+
`- Baseline score: \`${baseline.percent}%\``,
|
|
35
|
+
`- Current score: \`${current.percent}%\``,
|
|
36
|
+
`- Delta: \`${delta > 0 ? "+" : ""}${delta}%\``,
|
|
37
|
+
`- Allowed range: \`+/-${threshold}%\``,
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
process.stdout.write(`${lines.join("\n")}\n`);
|
|
41
|
+
|
|
42
|
+
if (!withinThreshold) {
|
|
43
|
+
console.error(
|
|
44
|
+
`Eval score delta ${delta > 0 ? "+" : ""}${delta}% is outside the allowed +/-${threshold}% range.`,
|
|
45
|
+
);
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|