libretto 0.5.5 → 0.6.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/README.md +23 -10
- package/README.template.md +23 -10
- package/dist/cli/cli.js +10 -0
- package/dist/cli/commands/ai.js +77 -2
- package/dist/cli/commands/browser.js +98 -8
- package/dist/cli/commands/execution.js +152 -56
- package/dist/cli/commands/setup.js +390 -0
- package/dist/cli/commands/snapshot.js +2 -2
- package/dist/cli/commands/status.js +62 -0
- package/dist/cli/core/{snapshot-api-config.js → ai-model.js} +81 -7
- package/dist/cli/core/api-snapshot-analyzer.js +7 -5
- package/dist/cli/core/browser.js +202 -36
- package/dist/cli/core/{ai-config.js → config.js} +14 -79
- package/dist/cli/core/context.js +1 -25
- package/dist/cli/core/deploy-artifact.js +121 -61
- package/dist/cli/core/providers/browserbase.js +53 -0
- package/dist/cli/core/providers/index.js +48 -0
- package/dist/cli/core/providers/kernel.js +46 -0
- package/dist/cli/core/providers/libretto-cloud.js +58 -0
- package/dist/cli/core/readonly-exec.js +231 -0
- package/dist/{shared/llm/client.js → cli/core/resolve-model.js} +4 -68
- package/dist/cli/core/session.js +53 -0
- package/dist/cli/core/skill-version.js +73 -0
- package/dist/cli/core/telemetry.js +1 -54
- package/dist/cli/index.js +1 -7
- package/dist/cli/router.js +4 -4
- package/dist/cli/workers/run-integration-runtime.js +19 -13
- package/dist/cli/workers/run-integration-worker-protocol.js +5 -2
- package/dist/index.d.ts +2 -4
- package/dist/index.js +2 -2
- package/dist/runtime/extract/extract.d.ts +2 -2
- package/dist/runtime/extract/extract.js +4 -2
- package/dist/runtime/extract/index.d.ts +1 -1
- package/dist/runtime/recovery/agent.d.ts +2 -3
- package/dist/runtime/recovery/agent.js +5 -3
- package/dist/runtime/recovery/errors.d.ts +2 -3
- package/dist/runtime/recovery/errors.js +4 -2
- package/dist/runtime/recovery/index.d.ts +1 -2
- package/dist/runtime/recovery/recovery.d.ts +2 -3
- package/dist/runtime/recovery/recovery.js +3 -3
- package/dist/shared/debug/pause.js +4 -21
- package/dist/shared/run/api.d.ts +2 -0
- package/dist/shared/run/browser.d.ts +9 -1
- package/dist/shared/run/browser.js +43 -3
- package/dist/shared/state/index.d.ts +1 -1
- package/dist/shared/state/index.js +2 -0
- package/dist/shared/state/session-state.d.ts +20 -1
- package/dist/shared/state/session-state.js +12 -2
- package/dist/shared/workflow/workflow.d.ts +2 -1
- package/dist/shared/workflow/workflow.js +16 -9
- package/package.json +17 -16
- package/scripts/postinstall.mjs +13 -11
- package/scripts/skills-libretto.mjs +14 -4
- package/skills/AGENTS.md +11 -0
- package/skills/libretto/SKILL.md +30 -9
- package/skills/libretto/references/auth-profiles.md +1 -1
- package/skills/libretto/references/code-generation-rules.md +3 -3
- package/skills/libretto/references/configuration-file-reference.md +11 -6
- package/skills/libretto-readonly/SKILL.md +95 -0
- package/src/cli/cli.ts +10 -0
- package/src/cli/commands/ai.ts +111 -1
- package/src/cli/commands/browser.ts +111 -9
- package/src/cli/commands/execution.ts +181 -74
- package/src/cli/commands/setup.ts +516 -0
- package/src/cli/commands/snapshot.ts +2 -2
- package/src/cli/commands/status.ts +79 -0
- package/src/cli/core/{snapshot-api-config.ts → ai-model.ts} +154 -14
- package/src/cli/core/api-snapshot-analyzer.ts +7 -5
- package/src/cli/core/browser.ts +242 -35
- package/src/cli/core/{ai-config.ts → config.ts} +14 -108
- package/src/cli/core/context.ts +1 -45
- package/src/cli/core/deploy-artifact.ts +141 -71
- package/src/cli/core/providers/browserbase.ts +57 -0
- package/src/cli/core/providers/index.ts +62 -0
- package/src/cli/core/providers/kernel.ts +49 -0
- package/src/cli/core/providers/libretto-cloud.ts +61 -0
- package/src/cli/core/providers/types.ts +9 -0
- package/src/cli/core/readonly-exec.ts +284 -0
- package/src/{shared/llm/client.ts → cli/core/resolve-model.ts} +3 -85
- package/src/cli/core/session.ts +75 -2
- package/src/cli/core/skill-version.ts +93 -0
- package/src/cli/core/telemetry.ts +0 -52
- package/src/cli/index.ts +0 -6
- package/src/cli/router.ts +4 -4
- package/src/cli/workers/run-integration-runtime.ts +18 -16
- package/src/cli/workers/run-integration-worker-protocol.ts +4 -1
- package/src/index.ts +1 -7
- package/src/runtime/extract/extract.ts +6 -5
- package/src/runtime/recovery/agent.ts +5 -4
- package/src/runtime/recovery/errors.ts +4 -3
- package/src/runtime/recovery/recovery.ts +4 -4
- package/src/shared/debug/pause.ts +4 -23
- package/src/shared/run/browser.ts +50 -1
- package/src/shared/state/index.ts +2 -0
- package/src/shared/state/session-state.ts +10 -0
- package/src/shared/workflow/workflow.ts +24 -13
- package/dist/cli/commands/init.js +0 -286
- package/dist/cli/commands/logs.js +0 -117
- package/dist/shared/llm/ai-sdk-adapter.d.ts +0 -22
- package/dist/shared/llm/ai-sdk-adapter.js +0 -49
- package/dist/shared/llm/client.d.ts +0 -13
- package/dist/shared/llm/index.d.ts +0 -5
- package/dist/shared/llm/index.js +0 -6
- package/dist/shared/llm/types.d.ts +0 -67
- package/src/cli/commands/init.ts +0 -331
- package/src/cli/commands/logs.ts +0 -128
- package/src/shared/llm/ai-sdk-adapter.ts +0 -81
- package/src/shared/llm/index.ts +0 -3
- package/src/shared/llm/types.ts +0 -63
- /package/dist/{shared/llm → cli/core/providers}/types.js +0 -0
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { generateObject } from "ai";
|
|
2
1
|
const GEMINI_API_KEY_ENV_VARS = [
|
|
3
2
|
"GEMINI_API_KEY",
|
|
4
3
|
"GOOGLE_GENERATIVE_AI_API_KEY"
|
|
@@ -108,76 +107,13 @@ async function getProviderModel(provider, modelId) {
|
|
|
108
107
|
}
|
|
109
108
|
}
|
|
110
109
|
}
|
|
111
|
-
function
|
|
112
|
-
return parts.map((part) => {
|
|
113
|
-
if (part.type === "text") {
|
|
114
|
-
return { type: "text", text: part.text };
|
|
115
|
-
}
|
|
116
|
-
return {
|
|
117
|
-
type: "image",
|
|
118
|
-
image: part.image,
|
|
119
|
-
...part.mediaType ? { mediaType: part.mediaType } : {}
|
|
120
|
-
};
|
|
121
|
-
});
|
|
122
|
-
}
|
|
123
|
-
function convertAssistantContentParts(parts) {
|
|
124
|
-
return parts.filter(
|
|
125
|
-
(part) => part.type === "text"
|
|
126
|
-
).map((part) => ({ type: "text", text: part.text }));
|
|
127
|
-
}
|
|
128
|
-
function convertMessages(messages) {
|
|
129
|
-
return messages.map((msg) => {
|
|
130
|
-
if (msg.role === "user") {
|
|
131
|
-
if (typeof msg.content === "string") {
|
|
132
|
-
return { role: "user", content: msg.content };
|
|
133
|
-
}
|
|
134
|
-
return {
|
|
135
|
-
role: "user",
|
|
136
|
-
content: convertUserContentParts(msg.content)
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
if (typeof msg.content === "string") {
|
|
140
|
-
return { role: "assistant", content: msg.content };
|
|
141
|
-
}
|
|
142
|
-
return {
|
|
143
|
-
role: "assistant",
|
|
144
|
-
content: convertAssistantContentParts(msg.content)
|
|
145
|
-
};
|
|
146
|
-
});
|
|
147
|
-
}
|
|
148
|
-
function createLLMClient(model) {
|
|
110
|
+
async function resolveModel(model) {
|
|
149
111
|
const { provider, modelId } = parseModel(model);
|
|
150
|
-
|
|
151
|
-
const getModel = () => {
|
|
152
|
-
modelPromise ??= getProviderModel(provider, modelId);
|
|
153
|
-
return modelPromise;
|
|
154
|
-
};
|
|
155
|
-
return {
|
|
156
|
-
async generateObject(opts) {
|
|
157
|
-
const aiModel = await getModel();
|
|
158
|
-
const result = await generateObject({
|
|
159
|
-
model: aiModel,
|
|
160
|
-
prompt: opts.prompt,
|
|
161
|
-
schema: opts.schema,
|
|
162
|
-
temperature: opts.temperature ?? 0
|
|
163
|
-
});
|
|
164
|
-
return result.object;
|
|
165
|
-
},
|
|
166
|
-
async generateObjectFromMessages(opts) {
|
|
167
|
-
const aiModel = await getModel();
|
|
168
|
-
const result = await generateObject({
|
|
169
|
-
model: aiModel,
|
|
170
|
-
messages: convertMessages(opts.messages),
|
|
171
|
-
schema: opts.schema,
|
|
172
|
-
temperature: opts.temperature ?? 0
|
|
173
|
-
});
|
|
174
|
-
return result.object;
|
|
175
|
-
}
|
|
176
|
-
};
|
|
112
|
+
return getProviderModel(provider, modelId);
|
|
177
113
|
}
|
|
178
114
|
export {
|
|
179
|
-
createLLMClient,
|
|
180
115
|
hasProviderCredentials,
|
|
181
116
|
missingProviderCredentialsMessage,
|
|
182
|
-
parseModel
|
|
117
|
+
parseModel,
|
|
118
|
+
resolveModel
|
|
183
119
|
};
|
package/dist/cli/core/session.js
CHANGED
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
LIBRETTO_SESSIONS_DIR
|
|
14
14
|
} from "./context.js";
|
|
15
15
|
import {
|
|
16
|
+
SessionAccessModeSchema,
|
|
16
17
|
SESSION_STATE_VERSION,
|
|
17
18
|
parseSessionStateContent,
|
|
18
19
|
serializeSessionState
|
|
@@ -28,6 +29,9 @@ function generateSessionName() {
|
|
|
28
29
|
}
|
|
29
30
|
return `ses-${id}`;
|
|
30
31
|
}
|
|
32
|
+
function resolveSessionAccessMode(state) {
|
|
33
|
+
return SessionAccessModeSchema.parse(state?.mode);
|
|
34
|
+
}
|
|
31
35
|
function logFileForSession(session) {
|
|
32
36
|
validateSessionName(session);
|
|
33
37
|
const dir = getSessionDir(session);
|
|
@@ -85,6 +89,21 @@ function listSessionsWithStateFile() {
|
|
|
85
89
|
function listActiveSessions() {
|
|
86
90
|
return listSessionsWithStateFile();
|
|
87
91
|
}
|
|
92
|
+
function listRunningSessions() {
|
|
93
|
+
const sessions = listSessionsWithStateFile();
|
|
94
|
+
const running = [];
|
|
95
|
+
for (const name of sessions) {
|
|
96
|
+
const state = readSessionState(name);
|
|
97
|
+
if (!state) continue;
|
|
98
|
+
if (state.provider) {
|
|
99
|
+
running.push(state);
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
if (state.pid == null || !isPidRunning(state.pid)) continue;
|
|
103
|
+
running.push(state);
|
|
104
|
+
}
|
|
105
|
+
return running;
|
|
106
|
+
}
|
|
88
107
|
function throwSessionNotFoundError(session) {
|
|
89
108
|
const active = listActiveSessions();
|
|
90
109
|
const lines = [`No session "${session}" found.`];
|
|
@@ -132,10 +151,34 @@ function writeSessionState(state, logger) {
|
|
|
132
151
|
logger?.info("session-state-write", {
|
|
133
152
|
session: state.session,
|
|
134
153
|
stateFile,
|
|
154
|
+
mode: state.mode,
|
|
135
155
|
port: state.port,
|
|
136
156
|
pid: state.pid
|
|
137
157
|
});
|
|
138
158
|
}
|
|
159
|
+
function setSessionMode(session, mode, logger) {
|
|
160
|
+
const state = readSessionStateOrThrow(session);
|
|
161
|
+
const normalizedMode = SessionAccessModeSchema.parse(mode);
|
|
162
|
+
if (state.mode === normalizedMode) {
|
|
163
|
+
return state;
|
|
164
|
+
}
|
|
165
|
+
const nextState = {
|
|
166
|
+
...state,
|
|
167
|
+
mode: normalizedMode
|
|
168
|
+
};
|
|
169
|
+
writeSessionState(nextState, logger);
|
|
170
|
+
return nextState;
|
|
171
|
+
}
|
|
172
|
+
function assertSessionAllowsCommand(state, commandName, allowedModes) {
|
|
173
|
+
const mode = resolveSessionAccessMode(state);
|
|
174
|
+
if (allowedModes.includes(mode)) {
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
const supportedModes = [...allowedModes].join(", ");
|
|
178
|
+
throw new Error(
|
|
179
|
+
`Command "${commandName}" is blocked for session "${state.session}" because it is in ${mode} mode. Allowed modes for this command: ${supportedModes}. Run \`libretto session-mode write-access --session ${state.session}\` to unlock the session.`
|
|
180
|
+
);
|
|
181
|
+
}
|
|
139
182
|
function clearSessionState(session, logger) {
|
|
140
183
|
const stateFile = getStateFilePath(session);
|
|
141
184
|
if (!existsSync(stateFile)) {
|
|
@@ -168,6 +211,11 @@ function setSessionStatus(session, status, logger) {
|
|
|
168
211
|
function assertSessionAvailableForStart(session, logger) {
|
|
169
212
|
const existingState = readSessionState(session, logger);
|
|
170
213
|
if (!existingState) return;
|
|
214
|
+
if (existingState.provider && existingState.cdpEndpoint) {
|
|
215
|
+
throw new Error(
|
|
216
|
+
`Session "${session}" is already open via ${existingState.provider.name} provider. Close it first with: libretto close --session ${session}`
|
|
217
|
+
);
|
|
218
|
+
}
|
|
171
219
|
if (existingState.pid == null || !isPidRunning(existingState.pid)) {
|
|
172
220
|
setSessionStatus(session, "exited", logger);
|
|
173
221
|
return;
|
|
@@ -181,15 +229,20 @@ export {
|
|
|
181
229
|
SESSION_BROWSER_AGENT,
|
|
182
230
|
SESSION_DEV_SERVER,
|
|
183
231
|
SESSION_STATE_VERSION,
|
|
232
|
+
assertSessionAllowsCommand,
|
|
184
233
|
assertSessionAvailableForStart,
|
|
185
234
|
assertSessionStateExistsOrThrow,
|
|
186
235
|
clearSessionState,
|
|
187
236
|
generateSessionName,
|
|
188
237
|
getStateFilePath,
|
|
238
|
+
isPidRunning,
|
|
239
|
+
listRunningSessions,
|
|
189
240
|
listSessionsWithStateFile,
|
|
190
241
|
logFileForSession,
|
|
191
242
|
readSessionState,
|
|
192
243
|
readSessionStateOrThrow,
|
|
244
|
+
resolveSessionAccessMode,
|
|
245
|
+
setSessionMode,
|
|
193
246
|
setSessionStatus,
|
|
194
247
|
validateSessionName,
|
|
195
248
|
writeSessionState
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { REPO_ROOT } from "./context.js";
|
|
5
|
+
const INSTALLED_SKILL_PATHS = [
|
|
6
|
+
[".agents", "skills", "libretto", "SKILL.md"],
|
|
7
|
+
[".claude", "skills", "libretto", "SKILL.md"]
|
|
8
|
+
];
|
|
9
|
+
let cachedCliVersion = null;
|
|
10
|
+
function readCurrentCliVersion() {
|
|
11
|
+
if (cachedCliVersion) {
|
|
12
|
+
return cachedCliVersion;
|
|
13
|
+
}
|
|
14
|
+
const packageJsonPath = fileURLToPath(
|
|
15
|
+
new URL("../../../package.json", import.meta.url)
|
|
16
|
+
);
|
|
17
|
+
const manifest = JSON.parse(
|
|
18
|
+
readFileSync(packageJsonPath, "utf8")
|
|
19
|
+
);
|
|
20
|
+
if (!manifest.version) {
|
|
21
|
+
throw new Error(
|
|
22
|
+
`Unable to determine current libretto version from ${packageJsonPath}.`
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
cachedCliVersion = manifest.version;
|
|
26
|
+
return cachedCliVersion;
|
|
27
|
+
}
|
|
28
|
+
function readInstalledSkillVersion(skillPath) {
|
|
29
|
+
if (!existsSync(skillPath)) {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
const contents = readFileSync(skillPath, "utf8");
|
|
33
|
+
const frontmatterMatch = contents.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
34
|
+
if (!frontmatterMatch) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
const metadataBlock = frontmatterMatch[1].match(
|
|
38
|
+
/^metadata:\s*\r?\n((?:[ \t]+.*(?:\r?\n|$))*)/m
|
|
39
|
+
)?.[1];
|
|
40
|
+
if (!metadataBlock) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
const versionMatch = metadataBlock.match(
|
|
44
|
+
/^[ \t]+version:\s*["']?([^"'\r\n]+)["']?\s*$/m
|
|
45
|
+
);
|
|
46
|
+
return versionMatch?.[1]?.trim() ?? null;
|
|
47
|
+
}
|
|
48
|
+
function findInstalledSkillVersionMismatch() {
|
|
49
|
+
const cliVersion = readCurrentCliVersion();
|
|
50
|
+
for (const relativePathParts of INSTALLED_SKILL_PATHS) {
|
|
51
|
+
const skillPath = join(REPO_ROOT, ...relativePathParts);
|
|
52
|
+
const installedVersion = readInstalledSkillVersion(skillPath);
|
|
53
|
+
if (installedVersion && installedVersion !== cliVersion) {
|
|
54
|
+
return { installedVersion, cliVersion };
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
function warnIfInstalledSkillOutOfDate() {
|
|
60
|
+
try {
|
|
61
|
+
const mismatch = findInstalledSkillVersionMismatch();
|
|
62
|
+
if (!mismatch) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
console.error(
|
|
66
|
+
`Warning: Your agent skill (${mismatch.installedVersion}) is out of date with your Libretto CLI (${mismatch.cliVersion}). Please run \`npx libretto setup\` to update your skills to the correct version.`
|
|
67
|
+
);
|
|
68
|
+
} catch {
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
export {
|
|
72
|
+
warnIfInstalledSkillOutOfDate
|
|
73
|
+
};
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
appendFileSync,
|
|
3
3
|
existsSync,
|
|
4
|
-
readFileSync
|
|
5
|
-
writeFileSync
|
|
4
|
+
readFileSync
|
|
6
5
|
} from "node:fs";
|
|
7
6
|
import {
|
|
8
7
|
getSessionActionsLogPath,
|
|
@@ -34,30 +33,6 @@ function readNetworkLog(session, opts = {}) {
|
|
|
34
33
|
}
|
|
35
34
|
return entries;
|
|
36
35
|
}
|
|
37
|
-
function formatNetworkEntry(e) {
|
|
38
|
-
const time = e.ts.replace(/.*T/, "").replace(/\.\d+Z$/, "");
|
|
39
|
-
const duration = e.durationMs != null ? `${e.durationMs}ms` : "?ms";
|
|
40
|
-
const size = e.size != null ? `${e.size}B` : "";
|
|
41
|
-
const parts = [
|
|
42
|
-
`[${time}]`,
|
|
43
|
-
`${e.status}`,
|
|
44
|
-
`${e.method.padEnd(6)}`,
|
|
45
|
-
e.url,
|
|
46
|
-
duration,
|
|
47
|
-
size
|
|
48
|
-
].filter(Boolean);
|
|
49
|
-
let line = parts.join(" ");
|
|
50
|
-
if (e.postData) {
|
|
51
|
-
line += `
|
|
52
|
-
body: ${e.postData.substring(0, 120)}${e.postData.length > 120 ? "..." : ""}`;
|
|
53
|
-
}
|
|
54
|
-
return line;
|
|
55
|
-
}
|
|
56
|
-
function clearNetworkLog(session) {
|
|
57
|
-
assertSessionStateExistsOrThrow(session);
|
|
58
|
-
const logPath = getSessionNetworkLogPath(session);
|
|
59
|
-
writeFileSync(logPath, "");
|
|
60
|
-
}
|
|
61
36
|
function parentLogAction(session, entry) {
|
|
62
37
|
try {
|
|
63
38
|
const record = { ts: (/* @__PURE__ */ new Date()).toISOString(), ...entry };
|
|
@@ -99,30 +74,6 @@ function readActionLog(session, opts = {}) {
|
|
|
99
74
|
}
|
|
100
75
|
return entries;
|
|
101
76
|
}
|
|
102
|
-
function formatActionEntry(e) {
|
|
103
|
-
const time = e.ts.replace(/.*T/, "").replace(/\.\d+Z$/, "");
|
|
104
|
-
const src = e.source.toUpperCase().padEnd(5);
|
|
105
|
-
const displaySelector = e.bestSemanticSelector || e.selector;
|
|
106
|
-
const parts = [`[${time}]`, `[${src}]`, e.action];
|
|
107
|
-
if (displaySelector) parts.push(displaySelector);
|
|
108
|
-
if (e.targetSelector && e.targetSelector !== displaySelector) {
|
|
109
|
-
parts.push(`target=${e.targetSelector}`);
|
|
110
|
-
}
|
|
111
|
-
if (e.nearbyText) parts.push(`text="${e.nearbyText}"`);
|
|
112
|
-
if (e.coordinates) {
|
|
113
|
-
parts.push(`@(${e.coordinates.x},${e.coordinates.y})`);
|
|
114
|
-
}
|
|
115
|
-
if (e.value) parts.push(`"${e.value}"`);
|
|
116
|
-
if (e.url) parts.push(e.url);
|
|
117
|
-
if (e.duration != null) parts.push(`${e.duration}ms`);
|
|
118
|
-
if (!e.success) parts.push(`ERROR: ${e.error || "unknown"}`);
|
|
119
|
-
return parts.join(" ");
|
|
120
|
-
}
|
|
121
|
-
function clearActionLog(session) {
|
|
122
|
-
assertSessionStateExistsOrThrow(session);
|
|
123
|
-
const logPath = getSessionActionsLogPath(session);
|
|
124
|
-
writeFileSync(logPath, "");
|
|
125
|
-
}
|
|
126
77
|
const LOCATOR_ACTION_METHODS = [
|
|
127
78
|
"click",
|
|
128
79
|
"dblclick",
|
|
@@ -367,10 +318,6 @@ function wrapPageForActionLogging(page, session, pageId, onActivity) {
|
|
|
367
318
|
}
|
|
368
319
|
}
|
|
369
320
|
export {
|
|
370
|
-
clearActionLog,
|
|
371
|
-
clearNetworkLog,
|
|
372
|
-
formatActionEntry,
|
|
373
|
-
formatNetworkEntry,
|
|
374
321
|
parentLogAction,
|
|
375
322
|
readActionLog,
|
|
376
323
|
readNetworkLog,
|
package/dist/cli/index.js
CHANGED
|
@@ -1,14 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { runLibrettoCLI } from "./cli.js";
|
|
3
|
-
import {
|
|
4
|
-
maybeConfigureLLMClientFactoryFromEnv,
|
|
5
|
-
setLLMClientFactory
|
|
6
|
-
} from "./core/context.js";
|
|
7
3
|
import { runClose } from "./commands/browser.js";
|
|
8
|
-
maybeConfigureLLMClientFactoryFromEnv();
|
|
9
4
|
void runLibrettoCLI();
|
|
10
5
|
export {
|
|
11
6
|
runClose,
|
|
12
|
-
runLibrettoCLI
|
|
13
|
-
setLLMClientFactory
|
|
7
|
+
runLibrettoCLI
|
|
14
8
|
};
|
package/dist/cli/router.js
CHANGED
|
@@ -2,17 +2,17 @@ import { aiCommands } from "./commands/ai.js";
|
|
|
2
2
|
import { browserCommands } from "./commands/browser.js";
|
|
3
3
|
import { deployCommand } from "./commands/deploy.js";
|
|
4
4
|
import { executionCommands } from "./commands/execution.js";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
5
|
+
import { setupCommand } from "./commands/setup.js";
|
|
6
|
+
import { statusCommand } from "./commands/status.js";
|
|
7
7
|
import { snapshotCommand } from "./commands/snapshot.js";
|
|
8
8
|
import { SimpleCLI } from "./framework/simple-cli.js";
|
|
9
9
|
const cliRoutes = {
|
|
10
10
|
...browserCommands,
|
|
11
11
|
deploy: deployCommand,
|
|
12
12
|
...executionCommands,
|
|
13
|
-
...logCommands,
|
|
14
13
|
ai: aiCommands,
|
|
15
|
-
|
|
14
|
+
setup: setupCommand,
|
|
15
|
+
status: statusCommand,
|
|
16
16
|
snapshot: snapshotCommand
|
|
17
17
|
};
|
|
18
18
|
function createCLIApp() {
|
|
@@ -4,7 +4,7 @@ import { cwd } from "node:process";
|
|
|
4
4
|
import { isAbsolute, resolve } from "node:path";
|
|
5
5
|
import { pathToFileURL } from "node:url";
|
|
6
6
|
import {
|
|
7
|
-
|
|
7
|
+
getDefaultWorkflowFromModuleExports,
|
|
8
8
|
getWorkflowsFromModuleExports,
|
|
9
9
|
instrumentContext,
|
|
10
10
|
launchBrowser
|
|
@@ -89,7 +89,7 @@ function getAbsoluteIntegrationPath(integrationPath) {
|
|
|
89
89
|
}
|
|
90
90
|
return absolutePath;
|
|
91
91
|
}
|
|
92
|
-
async function
|
|
92
|
+
async function loadDefaultWorkflow(absolutePath) {
|
|
93
93
|
let loadedModule;
|
|
94
94
|
try {
|
|
95
95
|
loadedModule = await import(pathToFileURL(absolutePath).href);
|
|
@@ -101,16 +101,20 @@ ${TSCONFIG_HINT}` : "";
|
|
|
101
101
|
`Failed to import integration module at ${absolutePath}: ${message}${compileHint}`
|
|
102
102
|
);
|
|
103
103
|
}
|
|
104
|
-
const
|
|
105
|
-
if (
|
|
106
|
-
return
|
|
104
|
+
const defaultWorkflow = getDefaultWorkflowFromModuleExports(loadedModule);
|
|
105
|
+
if (defaultWorkflow) {
|
|
106
|
+
return defaultWorkflow;
|
|
107
107
|
}
|
|
108
|
-
const
|
|
108
|
+
const availableWorkflowNames = getWorkflowsFromModuleExports(loadedModule).map(
|
|
109
109
|
(candidate) => candidate.name
|
|
110
110
|
);
|
|
111
|
-
|
|
111
|
+
if (availableWorkflowNames.length === 0) {
|
|
112
|
+
throw new Error(
|
|
113
|
+
`No default-exported workflow found in ${absolutePath}. Export the workflow with \`export default workflow("name", handler)\`.`
|
|
114
|
+
);
|
|
115
|
+
}
|
|
112
116
|
throw new Error(
|
|
113
|
-
`
|
|
117
|
+
`No default-exported workflow found in ${absolutePath}. libretto run only uses the file's default export. Available named workflows: ${availableWorkflowNames.join(", ")}`
|
|
114
118
|
);
|
|
115
119
|
}
|
|
116
120
|
async function installHeadedWorkflowVisualization(args) {
|
|
@@ -122,7 +126,7 @@ async function installHeadedWorkflowVisualization(args) {
|
|
|
122
126
|
async function runIntegrationInternal(args, options) {
|
|
123
127
|
const { logger } = options;
|
|
124
128
|
const absolutePath = getAbsoluteIntegrationPath(args.integrationPath);
|
|
125
|
-
const workflow = await
|
|
129
|
+
const workflow = await loadDefaultWorkflow(absolutePath);
|
|
126
130
|
const signalPaths = getPauseSignalPaths(args.session);
|
|
127
131
|
await removeSignalIfExists(signalPaths.pausedSignalPath);
|
|
128
132
|
await removeSignalIfExists(signalPaths.resumeSignalPath);
|
|
@@ -130,11 +134,11 @@ async function runIntegrationInternal(args, options) {
|
|
|
130
134
|
await removeSignalIfExists(signalPaths.failedSignalPath);
|
|
131
135
|
const restoreStdout = mirrorStdoutToFile(signalPaths.outputSignalPath);
|
|
132
136
|
console.log(
|
|
133
|
-
`Running workflow "${
|
|
137
|
+
`Running workflow "${workflow.name}" from ${absolutePath} (${args.headless ? "headless" : "headed"})...`
|
|
134
138
|
);
|
|
135
139
|
const integrationLogger = logger.withScope("integration-run", {
|
|
136
140
|
integrationPath: absolutePath,
|
|
137
|
-
workflowName:
|
|
141
|
+
workflowName: workflow.name,
|
|
138
142
|
session: args.session
|
|
139
143
|
});
|
|
140
144
|
const authProfileDomain = args.authProfileDomain;
|
|
@@ -153,7 +157,10 @@ async function runIntegrationInternal(args, options) {
|
|
|
153
157
|
sessionName: args.session,
|
|
154
158
|
headless: args.headless,
|
|
155
159
|
storageStatePath,
|
|
156
|
-
viewport: args.viewport
|
|
160
|
+
viewport: args.viewport,
|
|
161
|
+
accessMode: args.accessMode,
|
|
162
|
+
cdpEndpoint: args.cdpEndpoint,
|
|
163
|
+
provider: args.provider
|
|
157
164
|
});
|
|
158
165
|
if (!args.headless && args.visualize !== false) {
|
|
159
166
|
await installHeadedWorkflowVisualization({
|
|
@@ -208,7 +215,6 @@ async function runIntegrationInternal(args, options) {
|
|
|
208
215
|
JSON.stringify({ completedAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2),
|
|
209
216
|
"utf8"
|
|
210
217
|
);
|
|
211
|
-
console.log("Integration completed.");
|
|
212
218
|
return { status: "completed" };
|
|
213
219
|
} finally {
|
|
214
220
|
restoreStdout();
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { SessionAccessModeSchema } from "../../shared/state/index.js";
|
|
2
3
|
const RunIntegrationWorkerRequestSchema = z.object({
|
|
3
4
|
integrationPath: z.string().min(1),
|
|
4
|
-
workflowName: z.string().min(1),
|
|
5
5
|
session: z.string().min(1),
|
|
6
6
|
params: z.unknown(),
|
|
7
7
|
headless: z.boolean(),
|
|
8
8
|
visualize: z.boolean().default(true),
|
|
9
9
|
authProfileDomain: z.string().optional(),
|
|
10
|
-
viewport: z.object({ width: z.number(), height: z.number() }).optional()
|
|
10
|
+
viewport: z.object({ width: z.number(), height: z.number() }).optional(),
|
|
11
|
+
accessMode: SessionAccessModeSchema.default("write-access"),
|
|
12
|
+
cdpEndpoint: z.string().optional(),
|
|
13
|
+
provider: z.object({ name: z.string(), sessionId: z.string() }).optional()
|
|
11
14
|
});
|
|
12
15
|
export {
|
|
13
16
|
RunIntegrationWorkerRequestSchema
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
export { LogOptions, Logger, LoggerApi, LoggerSink, MinimalLogger, defaultLogger } from './shared/logger/logger.js';
|
|
2
2
|
export { createFileLogSink, jsonlConsoleSink, prettyConsoleSink } from './shared/logger/sinks.js';
|
|
3
|
-
export { LLMClient, Message, MessageContentPart } from './shared/llm/types.js';
|
|
4
|
-
export { createLLMClientFromModel } from './shared/llm/ai-sdk-adapter.js';
|
|
5
3
|
export { SESSION_STATE_VERSION, SessionState, SessionStateFile, SessionStateFileSchema, SessionStatus, SessionStatusSchema, parseSessionStateContent, parseSessionStateData, serializeSessionState } from './shared/state/session-state.js';
|
|
6
4
|
export { executeRecoveryAgent } from './runtime/recovery/agent.js';
|
|
7
5
|
export { attemptWithRecovery } from './runtime/recovery/recovery.js';
|
|
@@ -14,7 +12,7 @@ export { InstrumentationOptions, InstrumentedPage, installInstrumentation, instr
|
|
|
14
12
|
export { GhostCursorOptions, ensureGhostCursor, ghostClick, hideGhostCursor, moveGhostCursor } from './shared/visualization/ghost-cursor.js';
|
|
15
13
|
export { HighlightOptions, clearHighlights, ensureHighlightLayer, showHighlight } from './shared/visualization/highlight.js';
|
|
16
14
|
export { BrowserSession, LaunchBrowserArgs, launchBrowser } from './shared/run/browser.js';
|
|
17
|
-
export { ExportedLibrettoWorkflow, LIBRETTO_WORKFLOW_BRAND, LibrettoWorkflow, LibrettoWorkflowContext, LibrettoWorkflowHandler, getWorkflowFromModuleExports, getWorkflowsFromModuleExports, isLibrettoWorkflow, workflow } from './shared/workflow/workflow.js';
|
|
15
|
+
export { ExportedLibrettoWorkflow, LIBRETTO_WORKFLOW_BRAND, LibrettoWorkflow, LibrettoWorkflowContext, LibrettoWorkflowHandler, getDefaultWorkflowFromModuleExports, getWorkflowFromModuleExports, getWorkflowsFromModuleExports, isLibrettoWorkflow, workflow } from './shared/workflow/workflow.js';
|
|
18
16
|
import 'zod';
|
|
19
|
-
import 'ai';
|
|
20
17
|
import 'playwright';
|
|
18
|
+
import 'ai';
|
package/dist/index.js
CHANGED
|
@@ -9,7 +9,6 @@ import {
|
|
|
9
9
|
prettyConsoleSink,
|
|
10
10
|
jsonlConsoleSink
|
|
11
11
|
} from "./shared/logger/sinks.js";
|
|
12
|
-
import { createLLMClientFromModel } from "./shared/llm/ai-sdk-adapter.js";
|
|
13
12
|
import {
|
|
14
13
|
SESSION_STATE_VERSION,
|
|
15
14
|
SessionStatusSchema,
|
|
@@ -54,6 +53,7 @@ import {
|
|
|
54
53
|
launchBrowser
|
|
55
54
|
} from "./shared/run/api.js";
|
|
56
55
|
import {
|
|
56
|
+
getDefaultWorkflowFromModuleExports,
|
|
57
57
|
getWorkflowFromModuleExports,
|
|
58
58
|
getWorkflowsFromModuleExports,
|
|
59
59
|
isLibrettoWorkflow,
|
|
@@ -86,7 +86,6 @@ export {
|
|
|
86
86
|
attemptWithRecovery,
|
|
87
87
|
clearHighlights,
|
|
88
88
|
createFileLogSink,
|
|
89
|
-
createLLMClientFromModel,
|
|
90
89
|
defaultLogger,
|
|
91
90
|
detectSubmissionError,
|
|
92
91
|
downloadAndSave,
|
|
@@ -95,6 +94,7 @@ export {
|
|
|
95
94
|
ensureHighlightLayer,
|
|
96
95
|
executeRecoveryAgent,
|
|
97
96
|
extractFromPage,
|
|
97
|
+
getDefaultWorkflowFromModuleExports,
|
|
98
98
|
getWorkflowFromModuleExports,
|
|
99
99
|
getWorkflowsFromModuleExports,
|
|
100
100
|
ghostClick,
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { Page } from 'playwright';
|
|
2
2
|
import z from 'zod';
|
|
3
3
|
import { MinimalLogger } from '../../shared/logger/logger.js';
|
|
4
|
-
import {
|
|
4
|
+
import { LanguageModel } from 'ai';
|
|
5
5
|
|
|
6
6
|
type ExtractOptions<T extends z.ZodType> = {
|
|
7
7
|
page: Page;
|
|
8
8
|
instruction: string;
|
|
9
9
|
schema: T;
|
|
10
|
-
|
|
10
|
+
model: LanguageModel;
|
|
11
11
|
logger?: MinimalLogger;
|
|
12
12
|
/** Optional CSS selector to scope extraction to a specific element. */
|
|
13
13
|
selector?: string;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
defaultLogger
|
|
3
3
|
} from "../../shared/logger/logger.js";
|
|
4
|
+
import { generateObject } from "ai";
|
|
4
5
|
async function extractFromPage(options) {
|
|
5
6
|
const {
|
|
6
7
|
page,
|
|
@@ -8,7 +9,7 @@ async function extractFromPage(options) {
|
|
|
8
9
|
schema,
|
|
9
10
|
selector,
|
|
10
11
|
logger = defaultLogger,
|
|
11
|
-
|
|
12
|
+
model
|
|
12
13
|
} = options;
|
|
13
14
|
let screenshot;
|
|
14
15
|
let domContent;
|
|
@@ -49,7 +50,8 @@ ${domContent}
|
|
|
49
50
|
</html>` : ""}
|
|
50
51
|
|
|
51
52
|
Extract the requested information from the screenshot and return it in the specified format. Be precise and only extract what is visible.`;
|
|
52
|
-
const result = await
|
|
53
|
+
const { object: result } = await generateObject({
|
|
54
|
+
model,
|
|
53
55
|
schema,
|
|
54
56
|
messages: [
|
|
55
57
|
{
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import { Page } from 'playwright';
|
|
2
2
|
import { MinimalLogger } from '../../shared/logger/logger.js';
|
|
3
|
-
import {
|
|
4
|
-
import 'zod';
|
|
3
|
+
import { LanguageModel } from 'ai';
|
|
5
4
|
|
|
6
5
|
/**
|
|
7
6
|
* Executes a vision-based recovery agent to recover from browser automation failures.
|
|
8
7
|
* Takes a screenshot, sends it to the LLM with the instruction, and executes
|
|
9
8
|
* the LLM's suggested browser actions.
|
|
10
9
|
*/
|
|
11
|
-
declare function executeRecoveryAgent(page: Page, instruction: string, logger?: MinimalLogger,
|
|
10
|
+
declare function executeRecoveryAgent(page: Page, instruction: string, logger?: MinimalLogger, model?: LanguageModel): Promise<void>;
|
|
12
11
|
|
|
13
12
|
export { executeRecoveryAgent };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
defaultLogger
|
|
3
3
|
} from "../../shared/logger/logger.js";
|
|
4
|
+
import { generateObject } from "ai";
|
|
4
5
|
function delay(ms) {
|
|
5
6
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
6
7
|
}
|
|
@@ -137,8 +138,8 @@ const recoveryActionSchema = z.object({
|
|
|
137
138
|
})
|
|
138
139
|
])
|
|
139
140
|
});
|
|
140
|
-
async function executeRecoveryAgent(page, instruction, logger,
|
|
141
|
-
if (!
|
|
141
|
+
async function executeRecoveryAgent(page, instruction, logger, model) {
|
|
142
|
+
if (!model) {
|
|
142
143
|
return;
|
|
143
144
|
}
|
|
144
145
|
const log = logger ?? defaultLogger;
|
|
@@ -158,7 +159,8 @@ async function executeRecoveryAgent(page, instruction, logger, llmClient) {
|
|
|
158
159
|
}
|
|
159
160
|
const maxSteps = 3;
|
|
160
161
|
for (let step = 1; step <= maxSteps; step++) {
|
|
161
|
-
const result = await
|
|
162
|
+
const { object: result } = await generateObject({
|
|
163
|
+
model,
|
|
162
164
|
schema: recoveryActionSchema,
|
|
163
165
|
messages: [
|
|
164
166
|
{
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { Page } from 'playwright';
|
|
2
2
|
import { MinimalLogger } from '../../shared/logger/logger.js';
|
|
3
|
-
import {
|
|
4
|
-
import 'zod';
|
|
3
|
+
import { LanguageModel } from 'ai';
|
|
5
4
|
|
|
6
5
|
/**
|
|
7
6
|
* Known error type for classifying submission errors.
|
|
@@ -26,6 +25,6 @@ type DetectedSubmissionError = {
|
|
|
26
25
|
* @returns DetectedSubmissionError if a known error is matched
|
|
27
26
|
* @throws The original error if no known error matches
|
|
28
27
|
*/
|
|
29
|
-
declare function detectSubmissionError(page: Page, error: unknown, logContext: string,
|
|
28
|
+
declare function detectSubmissionError(page: Page, error: unknown, logContext: string, model: LanguageModel, knownErrors?: KnownSubmissionError[], logger?: MinimalLogger): Promise<DetectedSubmissionError>;
|
|
30
29
|
|
|
31
30
|
export { type DetectedSubmissionError, type KnownSubmissionError, detectSubmissionError };
|