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.
Files changed (101) hide show
  1. package/README.md +23 -10
  2. package/README.template.md +23 -10
  3. package/dist/cli/cli.js +10 -0
  4. package/dist/cli/commands/ai.js +77 -2
  5. package/dist/cli/commands/browser.js +71 -6
  6. package/dist/cli/commands/execution.js +101 -44
  7. package/dist/cli/commands/setup.js +376 -0
  8. package/dist/cli/commands/snapshot.js +2 -2
  9. package/dist/cli/commands/status.js +62 -0
  10. package/dist/cli/core/{snapshot-api-config.js → ai-model.js} +81 -7
  11. package/dist/cli/core/api-snapshot-analyzer.js +7 -5
  12. package/dist/cli/core/browser.js +81 -42
  13. package/dist/cli/core/{ai-config.js → config.js} +13 -79
  14. package/dist/cli/core/context.js +1 -25
  15. package/dist/cli/core/deploy-artifact.js +121 -61
  16. package/dist/cli/core/readonly-exec.js +231 -0
  17. package/dist/{shared/llm/client.js → cli/core/resolve-model.js} +4 -68
  18. package/dist/cli/core/session.js +44 -0
  19. package/dist/cli/core/skill-version.js +73 -0
  20. package/dist/cli/core/telemetry.js +1 -54
  21. package/dist/cli/index.js +1 -7
  22. package/dist/cli/router.js +4 -4
  23. package/dist/cli/workers/run-integration-runtime.js +29 -25
  24. package/dist/cli/workers/run-integration-worker-protocol.js +3 -2
  25. package/dist/index.d.ts +2 -4
  26. package/dist/index.js +2 -2
  27. package/dist/runtime/extract/extract.d.ts +2 -2
  28. package/dist/runtime/extract/extract.js +4 -2
  29. package/dist/runtime/extract/index.d.ts +1 -1
  30. package/dist/runtime/recovery/agent.d.ts +2 -3
  31. package/dist/runtime/recovery/agent.js +5 -3
  32. package/dist/runtime/recovery/errors.d.ts +2 -3
  33. package/dist/runtime/recovery/errors.js +4 -2
  34. package/dist/runtime/recovery/index.d.ts +1 -2
  35. package/dist/runtime/recovery/recovery.d.ts +2 -3
  36. package/dist/runtime/recovery/recovery.js +3 -3
  37. package/dist/shared/debug/pause.js +4 -21
  38. package/dist/shared/run/api.d.ts +2 -0
  39. package/dist/shared/run/browser.d.ts +4 -1
  40. package/dist/shared/run/browser.js +5 -3
  41. package/dist/shared/state/index.d.ts +1 -1
  42. package/dist/shared/state/index.js +2 -0
  43. package/dist/shared/state/session-state.d.ts +10 -1
  44. package/dist/shared/state/session-state.js +3 -0
  45. package/dist/shared/workflow/workflow.d.ts +2 -3
  46. package/dist/shared/workflow/workflow.js +16 -9
  47. package/package.json +3 -4
  48. package/scripts/postinstall.mjs +13 -11
  49. package/scripts/skills-libretto.mjs +14 -4
  50. package/skills/AGENTS.md +11 -0
  51. package/skills/libretto/SKILL.md +30 -9
  52. package/skills/libretto/references/auth-profiles.md +1 -1
  53. package/skills/libretto/references/code-generation-rules.md +6 -6
  54. package/skills/libretto/references/configuration-file-reference.md +11 -6
  55. package/skills/libretto-readonly/SKILL.md +95 -0
  56. package/src/cli/cli.ts +10 -0
  57. package/src/cli/commands/ai.ts +111 -1
  58. package/src/cli/commands/browser.ts +81 -7
  59. package/src/cli/commands/execution.ts +128 -61
  60. package/src/cli/commands/setup.ts +499 -0
  61. package/src/cli/commands/snapshot.ts +2 -2
  62. package/src/cli/commands/status.ts +77 -0
  63. package/src/cli/core/{snapshot-api-config.ts → ai-model.ts} +154 -14
  64. package/src/cli/core/api-snapshot-analyzer.ts +7 -5
  65. package/src/cli/core/browser.ts +107 -45
  66. package/src/cli/core/{ai-config.ts → config.ts} +13 -108
  67. package/src/cli/core/context.ts +1 -45
  68. package/src/cli/core/deploy-artifact.ts +141 -71
  69. package/src/cli/core/readonly-exec.ts +284 -0
  70. package/src/{shared/llm/client.ts → cli/core/resolve-model.ts} +3 -85
  71. package/src/cli/core/session.ts +62 -2
  72. package/src/cli/core/skill-version.ts +93 -0
  73. package/src/cli/core/telemetry.ts +0 -52
  74. package/src/cli/index.ts +0 -6
  75. package/src/cli/router.ts +4 -4
  76. package/src/cli/workers/run-integration-runtime.ts +36 -31
  77. package/src/cli/workers/run-integration-worker-protocol.ts +2 -1
  78. package/src/index.ts +1 -7
  79. package/src/runtime/extract/extract.ts +6 -5
  80. package/src/runtime/recovery/agent.ts +5 -4
  81. package/src/runtime/recovery/errors.ts +4 -3
  82. package/src/runtime/recovery/recovery.ts +4 -4
  83. package/src/shared/debug/pause.ts +4 -23
  84. package/src/shared/run/browser.ts +5 -1
  85. package/src/shared/state/index.ts +2 -0
  86. package/src/shared/state/session-state.ts +3 -0
  87. package/src/shared/workflow/workflow.ts +24 -15
  88. package/dist/cli/commands/init.js +0 -286
  89. package/dist/cli/commands/logs.js +0 -117
  90. package/dist/shared/llm/ai-sdk-adapter.d.ts +0 -22
  91. package/dist/shared/llm/ai-sdk-adapter.js +0 -49
  92. package/dist/shared/llm/client.d.ts +0 -13
  93. package/dist/shared/llm/index.d.ts +0 -5
  94. package/dist/shared/llm/index.js +0 -6
  95. package/dist/shared/llm/types.d.ts +0 -67
  96. package/dist/shared/llm/types.js +0 -0
  97. package/src/cli/commands/init.ts +0 -331
  98. package/src/cli/commands/logs.ts +0 -128
  99. package/src/shared/llm/ai-sdk-adapter.ts +0 -81
  100. package/src/shared/llm/index.ts +0 -3
  101. 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
  };
@@ -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 { initCommand } from "./commands/init.js";
6
- import { logCommands } from "./commands/logs.js";
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
- init: initCommand,
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
- getWorkflowFromModuleExports,
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 { getProfilePath, normalizeDomain } from "../core/browser.js";
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 loadWorkflowByName(absolutePath, workflowName) {
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 workflow = getWorkflowFromModuleExports(loadedModule, workflowName);
105
- if (workflow) {
106
- return workflow;
104
+ const defaultWorkflow = getDefaultWorkflowFromModuleExports(loadedModule);
105
+ if (defaultWorkflow) {
106
+ return defaultWorkflow;
107
107
  }
108
- const availableWorkflows = getWorkflowsFromModuleExports(loadedModule).map(
108
+ const availableWorkflowNames = getWorkflowsFromModuleExports(loadedModule).map(
109
109
  (candidate) => candidate.name
110
110
  );
111
- const detail = availableWorkflows.length > 0 ? ` Available workflows: ${availableWorkflows.join(", ")}` : ' No workflows found in this file. Export a workflow() instance from "libretto" directly or via `export const workflows = { ... }`.';
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
- `Workflow "${workflowName}" not found in ${absolutePath}.${detail}`
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 loadWorkflowByName(absolutePath, args.workflowName);
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 "${args.workflowName}" from ${absolutePath} (${args.headless ? "headless" : "headed"})...`
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: args.workflowName,
141
+ workflowName: workflow.name,
138
142
  session: args.session
139
143
  });
140
144
  const authProfileDomain = args.authProfileDomain;
141
- const storageStatePath = authProfileDomain ? resolveLocalAuthProfilePath(authProfileDomain) : void 0;
142
- if (authProfileDomain && storageStatePath && !existsSync(storageStatePath)) {
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
- domain: authProfileDomain,
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 { LLMClient } from '../../shared/llm/types.js';
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
- llmClient: LLMClient;
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
- llmClient
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 llmClient.generateObjectFromMessages({
53
+ const { object: result } = await generateObject({
54
+ model,
53
55
  schema,
54
56
  messages: [
55
57
  {
@@ -2,4 +2,4 @@ export { ExtractOptions, extractFromPage } from './extract.js';
2
2
  import 'playwright';
3
3
  import 'zod';
4
4
  import '../../shared/logger/logger.js';
5
- import '../../shared/llm/types.js';
5
+ import 'ai';
@@ -1,13 +1,12 @@
1
1
  import { Page } from 'playwright';
2
2
  import { MinimalLogger } from '../../shared/logger/logger.js';
3
- import { LLMClient } from '../../shared/llm/types.js';
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, llmClient?: LLMClient): Promise<void>;
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, llmClient) {
141
- if (!llmClient) {
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 llmClient.generateObjectFromMessages({
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 { LLMClient } from '../../shared/llm/types.js';
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, llmClient: LLMClient, knownErrors?: KnownSubmissionError[], logger?: MinimalLogger): Promise<DetectedSubmissionError>;
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, llmClient, knownErrors = [], logger) {
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 llmClient.generateObjectFromMessages({
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 '../../shared/llm/types.js';
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 { LLMClient } from '../../shared/llm/types.js';
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, llmClient?: LLMClient): Promise<T>;
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, llmClient) {
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 (!llmClient) {
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
- llmClient
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 = getRunningSessions();
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 runningSession of runningSessions) {
32
- lines.push(` ${runningSession}`);
14
+ for (const s of runningSessions) {
15
+ lines.push(` ${s.session}`);
33
16
  }
34
17
  }
35
18
  throw new Error(lines.join("\n"));
@@ -1,2 +1,4 @@
1
1
  export { BrowserSession, LaunchBrowserArgs, launchBrowser } from './browser.js';
2
2
  import 'playwright';
3
+ import '../state/session-state.js';
4
+ import 'zod';
@@ -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 };