libretto 0.5.4 → 0.5.6
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 +71 -6
- package/dist/cli/commands/execution.js +101 -44
- package/dist/cli/commands/setup.js +376 -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 +81 -42
- package/dist/cli/core/{ai-config.js → config.js} +13 -79
- package/dist/cli/core/context.js +1 -25
- package/dist/cli/core/deploy-artifact.js +121 -61
- 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 +44 -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 +29 -25
- package/dist/cli/workers/run-integration-worker-protocol.js +3 -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 +4 -1
- package/dist/shared/run/browser.js +5 -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 +10 -1
- package/dist/shared/state/session-state.js +3 -0
- package/dist/shared/workflow/workflow.d.ts +2 -3
- package/dist/shared/workflow/workflow.js +16 -9
- package/package.json +3 -4
- 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 +6 -6
- 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 +81 -7
- package/src/cli/commands/execution.ts +128 -61
- package/src/cli/commands/setup.ts +499 -0
- package/src/cli/commands/snapshot.ts +2 -2
- package/src/cli/commands/status.ts +77 -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 +107 -45
- package/src/cli/core/{ai-config.ts → config.ts} +13 -108
- package/src/cli/core/context.ts +1 -45
- package/src/cli/core/deploy-artifact.ts +141 -71
- 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 +62 -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 +36 -31
- package/src/cli/workers/run-integration-worker-protocol.ts +2 -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 +5 -1
- package/src/shared/state/index.ts +2 -0
- package/src/shared/state/session-state.ts +3 -0
- package/src/shared/workflow/workflow.ts +24 -15
- 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/dist/shared/llm/types.js +0 -0
- 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
|
@@ -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,13 +4,17 @@ 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
|
|
11
11
|
} from "../../index.js";
|
|
12
12
|
import { parseSessionStateContent } from "../../shared/state/index.js";
|
|
13
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
getProfilePath,
|
|
15
|
+
normalizeDomain,
|
|
16
|
+
normalizeUrl
|
|
17
|
+
} from "../core/browser.js";
|
|
14
18
|
import {
|
|
15
19
|
getSessionActionsLogPath,
|
|
16
20
|
getSessionNetworkLogPath,
|
|
@@ -68,18 +72,14 @@ async function waitForFailureSessionRelease(args) {
|
|
|
68
72
|
);
|
|
69
73
|
}
|
|
70
74
|
}
|
|
71
|
-
function resolveLocalAuthProfilePath(domain) {
|
|
72
|
-
return getProfilePath(normalizeDomain(domain));
|
|
73
|
-
}
|
|
74
75
|
function getMissingLocalAuthProfileError(args) {
|
|
75
|
-
const normalizedDomain = normalizeDomain(args.domain);
|
|
76
76
|
return [
|
|
77
|
-
`Local auth profile not found for domain "${normalizedDomain}".`,
|
|
77
|
+
`Local auth profile not found for domain "${args.normalizedDomain}".`,
|
|
78
78
|
`Expected profile file: ${args.profilePath}`,
|
|
79
79
|
"To create it:",
|
|
80
|
-
` 1. libretto open https://${normalizedDomain} --headed --session ${args.session}`,
|
|
80
|
+
` 1. libretto open https://${args.normalizedDomain} --headed --session ${args.session}`,
|
|
81
81
|
" 2. Log in manually in the browser window.",
|
|
82
|
-
` 3. libretto save ${normalizedDomain} --session ${args.session}`
|
|
82
|
+
` 3. libretto save ${args.normalizedDomain} --session ${args.session}`
|
|
83
83
|
].join("\n");
|
|
84
84
|
}
|
|
85
85
|
function getAbsoluteIntegrationPath(integrationPath) {
|
|
@@ -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,19 +134,20 @@ 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;
|
|
141
|
-
const
|
|
142
|
-
|
|
145
|
+
const normalizedAuthProfileDomain = authProfileDomain ? normalizeDomain(normalizeUrl(authProfileDomain)) : void 0;
|
|
146
|
+
const storageStatePath = normalizedAuthProfileDomain ? getProfilePath(normalizedAuthProfileDomain) : void 0;
|
|
147
|
+
if (normalizedAuthProfileDomain && storageStatePath && !existsSync(storageStatePath)) {
|
|
143
148
|
throw new Error(
|
|
144
149
|
getMissingLocalAuthProfileError({
|
|
145
|
-
|
|
150
|
+
normalizedDomain: normalizedAuthProfileDomain,
|
|
146
151
|
profilePath: storageStatePath,
|
|
147
152
|
session: args.session
|
|
148
153
|
})
|
|
@@ -152,7 +157,8 @@ async function runIntegrationInternal(args, options) {
|
|
|
152
157
|
sessionName: args.session,
|
|
153
158
|
headless: args.headless,
|
|
154
159
|
storageStatePath,
|
|
155
|
-
viewport: args.viewport
|
|
160
|
+
viewport: args.viewport,
|
|
161
|
+
accessMode: args.accessMode
|
|
156
162
|
});
|
|
157
163
|
if (!args.headless && args.visualize !== false) {
|
|
158
164
|
await installHeadedWorkflowVisualization({
|
|
@@ -175,7 +181,6 @@ async function runIntegrationInternal(args, options) {
|
|
|
175
181
|
});
|
|
176
182
|
const workflowContext = {
|
|
177
183
|
session: args.session,
|
|
178
|
-
logger: integrationLogger,
|
|
179
184
|
page: browserSession.page
|
|
180
185
|
};
|
|
181
186
|
try {
|
|
@@ -208,7 +213,6 @@ async function runIntegrationInternal(args, options) {
|
|
|
208
213
|
JSON.stringify({ completedAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2),
|
|
209
214
|
"utf8"
|
|
210
215
|
);
|
|
211
|
-
console.log("Integration completed.");
|
|
212
216
|
return { status: "completed" };
|
|
213
217
|
} finally {
|
|
214
218
|
restoreStdout();
|
|
@@ -1,13 +1,14 @@
|
|
|
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")
|
|
11
12
|
});
|
|
12
13
|
export {
|
|
13
14
|
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 };
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
2
|
defaultLogger
|
|
3
3
|
} from "../../shared/logger/logger.js";
|
|
4
|
+
import { generateObject } from "ai";
|
|
4
5
|
import { z } from "zod";
|
|
5
6
|
const detectSubmissionErrorSchema = z.object({
|
|
6
7
|
hasError: z.boolean().describe("Whether an error is visible on the page"),
|
|
7
8
|
matchedKnownErrorId: z.string().nullable().describe("The ID of the matched known error, or null if no match"),
|
|
8
9
|
errorMessage: z.string().nullable().describe("The error message visible on screen, or null if no error")
|
|
9
10
|
});
|
|
10
|
-
async function detectSubmissionError(page, error, logContext,
|
|
11
|
+
async function detectSubmissionError(page, error, logContext, model, knownErrors = [], logger) {
|
|
11
12
|
const log = logger ?? defaultLogger;
|
|
12
13
|
let screenshot;
|
|
13
14
|
let domSnapshot;
|
|
@@ -58,7 +59,8 @@ IMPORTANT:
|
|
|
58
59
|
${domSnapshot ? `<dom_snapshot>
|
|
59
60
|
${domSnapshot}
|
|
60
61
|
</dom_snapshot>` : ""}`;
|
|
61
|
-
const result = await
|
|
62
|
+
const { object: result } = await generateObject({
|
|
63
|
+
model,
|
|
62
64
|
schema: detectSubmissionErrorSchema,
|
|
63
65
|
messages: [
|
|
64
66
|
{
|
|
@@ -3,5 +3,4 @@ export { attemptWithRecovery } from './recovery.js';
|
|
|
3
3
|
export { DetectedSubmissionError, KnownSubmissionError, detectSubmissionError } from './errors.js';
|
|
4
4
|
import 'playwright';
|
|
5
5
|
import '../../shared/logger/logger.js';
|
|
6
|
-
import '
|
|
7
|
-
import 'zod';
|
|
6
|
+
import 'ai';
|
|
@@ -1,12 +1,11 @@
|
|
|
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
|
* Attempts to execute a function, and if it fails, runs popup recovery
|
|
8
7
|
* (if an LLM client is provided) and retries the function once.
|
|
9
8
|
*/
|
|
10
|
-
declare function attemptWithRecovery<T>(page: Page, fn: () => Promise<T>, logger?: MinimalLogger,
|
|
9
|
+
declare function attemptWithRecovery<T>(page: Page, fn: () => Promise<T>, logger?: MinimalLogger, model?: LanguageModel): Promise<T>;
|
|
11
10
|
|
|
12
11
|
export { attemptWithRecovery };
|
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
defaultLogger
|
|
3
3
|
} from "../../shared/logger/logger.js";
|
|
4
4
|
import { executeRecoveryAgent } from "./agent.js";
|
|
5
|
-
async function attemptWithRecovery(page, fn, logger,
|
|
5
|
+
async function attemptWithRecovery(page, fn, logger, model) {
|
|
6
6
|
const log = logger ?? defaultLogger;
|
|
7
7
|
try {
|
|
8
8
|
return await fn();
|
|
@@ -13,7 +13,7 @@ async function attemptWithRecovery(page, fn, logger, llmClient) {
|
|
|
13
13
|
});
|
|
14
14
|
throw error;
|
|
15
15
|
}
|
|
16
|
-
if (!
|
|
16
|
+
if (!model) {
|
|
17
17
|
throw error;
|
|
18
18
|
}
|
|
19
19
|
log.info("Action failed, attempting popup recovery", {
|
|
@@ -23,7 +23,7 @@ async function attemptWithRecovery(page, fn, logger, llmClient) {
|
|
|
23
23
|
page,
|
|
24
24
|
"Look at the page to see if there is a popup blocking the screen. If so, close the popup.",
|
|
25
25
|
log,
|
|
26
|
-
|
|
26
|
+
model
|
|
27
27
|
);
|
|
28
28
|
return await fn();
|
|
29
29
|
}
|
|
@@ -5,31 +5,14 @@ import {
|
|
|
5
5
|
getPauseSignalPaths,
|
|
6
6
|
removeSignalIfExists
|
|
7
7
|
} from "../../cli/core/pause-signals.js";
|
|
8
|
-
import {
|
|
9
|
-
listSessionsWithStateFile,
|
|
10
|
-
readSessionState
|
|
11
|
-
} from "../../cli/core/session.js";
|
|
12
|
-
function isPidRunning(pid) {
|
|
13
|
-
try {
|
|
14
|
-
process.kill(pid, 0);
|
|
15
|
-
return true;
|
|
16
|
-
} catch {
|
|
17
|
-
return false;
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
function getRunningSessions() {
|
|
21
|
-
return listSessionsWithStateFile().filter((candidate) => {
|
|
22
|
-
const state = readSessionState(candidate);
|
|
23
|
-
return state !== null && state.pid != null && isPidRunning(state.pid);
|
|
24
|
-
});
|
|
25
|
-
}
|
|
8
|
+
import { listRunningSessions } from "../../cli/core/session.js";
|
|
26
9
|
function throwMissingSessionError() {
|
|
27
|
-
const runningSessions =
|
|
10
|
+
const runningSessions = listRunningSessions();
|
|
28
11
|
const lines = ["pause(session) requires a non-empty session ID."];
|
|
29
12
|
if (runningSessions.length > 0) {
|
|
30
13
|
lines.push("", "Running sessions:");
|
|
31
|
-
for (const
|
|
32
|
-
lines.push(` ${
|
|
14
|
+
for (const s of runningSessions) {
|
|
15
|
+
lines.push(` ${s.session}`);
|
|
33
16
|
}
|
|
34
17
|
}
|
|
35
18
|
throw new Error(lines.join("\n"));
|
package/dist/shared/run/api.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { Browser, BrowserContext, Page } from 'playwright';
|
|
2
|
+
import { SessionAccessMode } from '../state/session-state.js';
|
|
3
|
+
import 'zod';
|
|
2
4
|
|
|
3
5
|
type LaunchBrowserArgs = {
|
|
4
6
|
sessionName: string;
|
|
@@ -8,6 +10,7 @@ type LaunchBrowserArgs = {
|
|
|
8
10
|
height: number;
|
|
9
11
|
};
|
|
10
12
|
storageStatePath?: string;
|
|
13
|
+
accessMode?: SessionAccessMode;
|
|
11
14
|
};
|
|
12
15
|
type BrowserSession = {
|
|
13
16
|
browser: Browser;
|
|
@@ -17,6 +20,6 @@ type BrowserSession = {
|
|
|
17
20
|
metadataPath: string;
|
|
18
21
|
close: () => Promise<void>;
|
|
19
22
|
};
|
|
20
|
-
declare function launchBrowser({ sessionName, headless, viewport, storageStatePath, }: LaunchBrowserArgs): Promise<BrowserSession>;
|
|
23
|
+
declare function launchBrowser({ sessionName, headless, viewport, storageStatePath, accessMode, }: LaunchBrowserArgs): Promise<BrowserSession>;
|
|
21
24
|
|
|
22
25
|
export { type BrowserSession, type LaunchBrowserArgs, launchBrowser };
|