libretto 0.5.3-experimental.5 → 0.5.3

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 (126) hide show
  1. package/README.md +114 -37
  2. package/README.template.md +160 -0
  3. package/dist/cli/cli.js +22 -97
  4. package/dist/cli/commands/browser.js +86 -59
  5. package/dist/cli/commands/deploy.js +148 -0
  6. package/dist/cli/commands/execution.js +218 -96
  7. package/dist/cli/commands/init.js +34 -29
  8. package/dist/cli/commands/logs.js +4 -5
  9. package/dist/cli/commands/shared.js +30 -29
  10. package/dist/cli/commands/snapshot.js +26 -39
  11. package/dist/cli/core/ai-config.js +21 -4
  12. package/dist/cli/core/api-snapshot-analyzer.js +15 -5
  13. package/dist/cli/core/browser.js +207 -37
  14. package/dist/cli/core/context.js +4 -1
  15. package/dist/cli/core/deploy-artifact.js +687 -0
  16. package/dist/cli/core/session-telemetry.js +434 -174
  17. package/dist/cli/core/session.js +21 -8
  18. package/dist/cli/core/snapshot-analyzer.js +14 -31
  19. package/dist/cli/core/snapshot-api-config.js +2 -6
  20. package/dist/cli/core/telemetry.js +20 -4
  21. package/dist/cli/framework/simple-cli.js +144 -43
  22. package/dist/cli/router.js +16 -21
  23. package/dist/cli/workers/run-integration-runtime.js +25 -45
  24. package/dist/cli/workers/run-integration-worker-protocol.js +3 -2
  25. package/dist/cli/workers/run-integration-worker.js +1 -4
  26. package/dist/index.d.ts +1 -2
  27. package/dist/index.js +13 -10
  28. package/dist/runtime/download/download.js +5 -1
  29. package/dist/runtime/extract/extract.js +11 -2
  30. package/dist/runtime/network/network.js +8 -1
  31. package/dist/runtime/recovery/agent.js +6 -2
  32. package/dist/runtime/recovery/errors.js +3 -1
  33. package/dist/runtime/recovery/recovery.js +3 -1
  34. package/dist/shared/condense-dom/condense-dom.js +17 -69
  35. package/dist/shared/config/config.d.ts +1 -9
  36. package/dist/shared/config/config.js +0 -18
  37. package/dist/shared/config/index.d.ts +2 -1
  38. package/dist/shared/config/index.js +0 -10
  39. package/dist/shared/debug/pause.js +9 -3
  40. package/dist/shared/dom-semantics.d.ts +8 -0
  41. package/dist/shared/dom-semantics.js +69 -0
  42. package/dist/shared/instrumentation/instrument.js +101 -5
  43. package/dist/shared/llm/ai-sdk-adapter.js +3 -1
  44. package/dist/shared/llm/client.js +3 -1
  45. package/dist/shared/logger/index.js +4 -1
  46. package/dist/shared/run/api.js +3 -1
  47. package/dist/shared/run/browser.js +47 -3
  48. package/dist/shared/state/session-state.d.ts +2 -1
  49. package/dist/shared/state/session-state.js +5 -2
  50. package/dist/shared/visualization/ghost-cursor.js +36 -14
  51. package/dist/shared/visualization/highlight.js +9 -6
  52. package/dist/shared/workflow/workflow.d.ts +18 -10
  53. package/dist/shared/workflow/workflow.js +50 -5
  54. package/package.json +14 -6
  55. package/scripts/generate-changelog.ts +132 -0
  56. package/scripts/postinstall.mjs +4 -3
  57. package/scripts/skills-libretto.mjs +2 -88
  58. package/scripts/summarize-evals.mjs +32 -10
  59. package/skills/libretto/SKILL.md +132 -62
  60. package/skills/libretto/references/action-logs.md +101 -0
  61. package/skills/libretto/references/auth-profiles.md +1 -2
  62. package/skills/libretto/references/code-generation-rules.md +176 -0
  63. package/skills/libretto/references/configuration-file-reference.md +53 -0
  64. package/skills/libretto/references/pages-and-page-targeting.md +1 -1
  65. package/skills/libretto/references/site-security-review.md +143 -0
  66. package/src/cli/cli.ts +23 -110
  67. package/src/cli/commands/browser.ts +94 -70
  68. package/src/cli/commands/deploy.ts +198 -0
  69. package/src/cli/commands/execution.ts +251 -111
  70. package/src/cli/commands/init.ts +37 -33
  71. package/src/cli/commands/logs.ts +7 -7
  72. package/src/cli/commands/shared.ts +36 -37
  73. package/src/cli/commands/snapshot.ts +44 -59
  74. package/src/cli/core/ai-config.ts +24 -4
  75. package/src/cli/core/api-snapshot-analyzer.ts +17 -6
  76. package/src/cli/core/browser.ts +260 -49
  77. package/src/cli/core/context.ts +7 -2
  78. package/src/cli/core/deploy-artifact.ts +938 -0
  79. package/src/cli/core/session-telemetry.ts +449 -197
  80. package/src/cli/core/session.ts +21 -7
  81. package/src/cli/core/snapshot-analyzer.ts +26 -46
  82. package/src/cli/core/snapshot-api-config.ts +170 -175
  83. package/src/cli/core/telemetry.ts +39 -4
  84. package/src/cli/framework/simple-cli.ts +281 -98
  85. package/src/cli/router.ts +15 -21
  86. package/src/cli/workers/run-integration-runtime.ts +35 -57
  87. package/src/cli/workers/run-integration-worker-protocol.ts +2 -1
  88. package/src/cli/workers/run-integration-worker.ts +1 -4
  89. package/src/index.ts +77 -67
  90. package/src/runtime/download/download.ts +62 -58
  91. package/src/runtime/download/index.ts +5 -5
  92. package/src/runtime/extract/extract.ts +71 -61
  93. package/src/runtime/network/index.ts +3 -3
  94. package/src/runtime/network/network.ts +99 -93
  95. package/src/runtime/recovery/agent.ts +217 -212
  96. package/src/runtime/recovery/errors.ts +107 -104
  97. package/src/runtime/recovery/index.ts +3 -3
  98. package/src/runtime/recovery/recovery.ts +38 -35
  99. package/src/shared/condense-dom/condense-dom.ts +27 -82
  100. package/src/shared/config/config.ts +0 -19
  101. package/src/shared/config/index.ts +0 -5
  102. package/src/shared/debug/pause.ts +57 -51
  103. package/src/shared/dom-semantics.ts +68 -0
  104. package/src/shared/instrumentation/errors.ts +64 -62
  105. package/src/shared/instrumentation/index.ts +5 -5
  106. package/src/shared/instrumentation/instrument.ts +339 -209
  107. package/src/shared/llm/ai-sdk-adapter.ts +58 -55
  108. package/src/shared/llm/client.ts +181 -174
  109. package/src/shared/llm/types.ts +39 -39
  110. package/src/shared/logger/index.ts +11 -4
  111. package/src/shared/logger/logger.ts +312 -306
  112. package/src/shared/logger/sinks.ts +118 -114
  113. package/src/shared/paths/paths.ts +50 -49
  114. package/src/shared/paths/repo-root.ts +17 -17
  115. package/src/shared/run/api.ts +5 -1
  116. package/src/shared/run/browser.ts +65 -3
  117. package/src/shared/state/index.ts +9 -9
  118. package/src/shared/state/session-state.ts +46 -43
  119. package/src/shared/visualization/ghost-cursor.ts +180 -149
  120. package/src/shared/visualization/highlight.ts +89 -86
  121. package/src/shared/visualization/index.ts +13 -13
  122. package/src/shared/workflow/workflow.ts +107 -30
  123. package/scripts/check-skills-sync.mjs +0 -23
  124. package/scripts/prepare-release.sh +0 -97
  125. package/skills/libretto/references/reverse-engineering-network-requests.md +0 -75
  126. package/skills/libretto/references/user-action-log.md +0 -31
@@ -2,17 +2,21 @@ import { z } from "zod";
2
2
  import {
3
3
  runClose as runCloseWithLogger,
4
4
  runCloseAll as runCloseAllWithLogger,
5
+ runConnect as runConnectWithLogger,
5
6
  runOpen,
6
7
  runPages,
7
8
  runSave
8
9
  } from "../core/browser.js";
9
- import { withSessionLogger } from "../core/context.js";
10
- import { assertSessionAvailableForStart } from "../core/session.js";
10
+ import { createLoggerForSession, withSessionLogger } from "../core/context.js";
11
+ import {
12
+ assertSessionAvailableForStart,
13
+ validateSessionName
14
+ } from "../core/session.js";
11
15
  import { SimpleCLI } from "../framework/simple-cli.js";
12
16
  import {
13
- loadSessionStateMiddleware,
14
- resolveSessionMiddleware,
15
- sessionOption
17
+ sessionOption,
18
+ withAutoSession,
19
+ withRequiredSession
16
20
  } from "./shared.js";
17
21
  function parseViewportArg(viewportArg) {
18
22
  if (!viewportArg) return void 0;
@@ -52,16 +56,32 @@ const openInput = SimpleCLI.input({
52
56
  (input) => !(input.headed && input.headless),
53
57
  "Cannot pass both --headed and --headless."
54
58
  );
55
- function createOpenCommand(logger) {
56
- return SimpleCLI.command({
57
- description: "Launch browser and open URL (headed by default)"
58
- }).input(openInput).use(resolveSessionMiddleware).handle(async ({ input, ctx }) => {
59
- assertSessionAvailableForStart(ctx.session, logger);
60
- const headed = input.headed || !input.headless;
61
- const viewport = parseViewportArg(input.viewport);
62
- await runOpen(input.url, headed, ctx.session, logger, { viewport });
63
- });
64
- }
59
+ const openCommand = SimpleCLI.command({
60
+ description: "Launch browser and open URL (headed by default)"
61
+ }).input(openInput).use(withAutoSession()).handle(async ({ input, ctx }) => {
62
+ assertSessionAvailableForStart(ctx.session, ctx.logger);
63
+ const headed = input.headed || !input.headless;
64
+ const viewport = parseViewportArg(input.viewport);
65
+ await runOpen(input.url, headed, ctx.session, ctx.logger, { viewport });
66
+ });
67
+ const connectInput = SimpleCLI.input({
68
+ positionals: [
69
+ SimpleCLI.positional("cdpUrl", z.string().optional(), {
70
+ help: "CDP endpoint URL (e.g. http://127.0.0.1:9222)"
71
+ })
72
+ ],
73
+ named: {
74
+ session: sessionOption()
75
+ }
76
+ }).refine(
77
+ (input) => Boolean(input.cdpUrl),
78
+ `Usage: libretto connect <cdp-url> --session <name>`
79
+ );
80
+ const connectCommand = SimpleCLI.command({
81
+ description: "Connect to an existing Chrome DevTools Protocol (CDP) endpoint"
82
+ }).input(connectInput).use(withAutoSession()).handle(async ({ input, ctx }) => {
83
+ await runConnectWithLogger(input.cdpUrl, ctx.session, ctx.logger);
84
+ });
65
85
  const saveInput = SimpleCLI.input({
66
86
  positionals: [
67
87
  SimpleCLI.positional("urlOrDomain", z.string().optional(), {
@@ -73,72 +93,79 @@ const saveInput = SimpleCLI.input({
73
93
  }
74
94
  }).refine(
75
95
  (input) => Boolean(input.urlOrDomain),
76
- `Usage: libretto save <url|domain> [--session <name>]`
96
+ `Usage: libretto save <url|domain> --session <name>`
77
97
  );
78
- function createSaveCommand(logger) {
79
- return SimpleCLI.command({
80
- description: "Save current browser session"
81
- }).input(saveInput).use(resolveSessionMiddleware).use(loadSessionStateMiddleware).handle(async ({ input, ctx }) => {
82
- await runSave(input.urlOrDomain, ctx.session, logger);
83
- });
84
- }
98
+ const saveCommand = SimpleCLI.command({
99
+ description: "Save current browser session"
100
+ }).input(saveInput).use(withRequiredSession()).handle(async ({ input, ctx }) => {
101
+ await runSave(input.urlOrDomain, ctx.session, ctx.logger);
102
+ });
85
103
  const pagesInput = SimpleCLI.input({
86
104
  positionals: [],
87
105
  named: {
88
106
  session: sessionOption()
89
107
  }
90
108
  });
91
- function createPagesCommand(logger) {
92
- return SimpleCLI.command({
93
- description: "List open pages in the session"
94
- }).input(pagesInput).use(resolveSessionMiddleware).use(loadSessionStateMiddleware).handle(async ({ ctx }) => {
95
- await runPages(ctx.session, logger);
96
- });
97
- }
109
+ const pagesCommand = SimpleCLI.command({
110
+ description: "List open pages in the session"
111
+ }).input(pagesInput).use(withRequiredSession()).handle(async ({ ctx }) => {
112
+ await runPages(ctx.session, ctx.logger);
113
+ });
98
114
  const closeInput = SimpleCLI.input({
99
115
  positionals: [],
100
116
  named: {
101
117
  session: sessionOption(),
102
- all: SimpleCLI.flag({ help: "Close all tracked sessions in this workspace" }),
103
- force: SimpleCLI.flag({ help: "Force kill sessions that ignore SIGTERM (requires --all)" })
118
+ all: SimpleCLI.flag({
119
+ help: "Close all tracked sessions in this workspace"
120
+ }),
121
+ force: SimpleCLI.flag({
122
+ help: "Force kill sessions that ignore SIGTERM (requires --all)"
123
+ })
124
+ }
125
+ }).refine(
126
+ (input) => input.all || input.session,
127
+ `Usage: libretto close --session <name>
128
+ Usage: libretto close --all [--force]`
129
+ );
130
+ const closeCommand = SimpleCLI.command({
131
+ description: "Close the browser"
132
+ }).input(closeInput).handle(async ({ input }) => {
133
+ if (input.force && !input.all) {
134
+ throw new Error(`Usage: libretto close --all [--force]`);
104
135
  }
136
+ if (input.all) {
137
+ const logger2 = createLoggerForSession("cli");
138
+ await runCloseAllWithLogger(logger2, { force: input.force });
139
+ return;
140
+ }
141
+ validateSessionName(input.session);
142
+ const logger = createLoggerForSession(input.session);
143
+ await runCloseWithLogger(input.session, logger);
105
144
  });
106
- function createCloseCommand(logger) {
107
- return SimpleCLI.command({
108
- description: "Close the browser"
109
- }).input(closeInput).use(resolveSessionMiddleware).handle(async ({ input, ctx }) => {
110
- if (input.force && !input.all) {
111
- throw new Error(`Usage: libretto close --all [--force]`);
112
- }
113
- if (input.all) {
114
- await runCloseAllWithLogger(logger, { force: input.force });
115
- return;
116
- }
117
- await runCloseWithLogger(ctx.session, logger);
118
- });
119
- }
120
- function createBrowserCommands(logger) {
121
- return {
122
- open: createOpenCommand(logger),
123
- save: createSaveCommand(logger),
124
- pages: createPagesCommand(logger),
125
- close: createCloseCommand(logger)
126
- };
127
- }
145
+ const browserCommands = {
146
+ open: openCommand,
147
+ connect: connectCommand,
148
+ save: saveCommand,
149
+ pages: pagesCommand,
150
+ close: closeCommand
151
+ };
128
152
  async function runClose(session) {
129
153
  await withSessionLogger(session, async (logger) => {
130
154
  await runCloseWithLogger(session, logger);
131
155
  });
132
156
  }
133
157
  export {
158
+ browserCommands,
159
+ closeCommand,
134
160
  closeInput,
135
- createBrowserCommands,
136
- createCloseCommand,
137
- createOpenCommand,
138
- createPagesCommand,
139
- createSaveCommand,
161
+ connectCommand,
162
+ connectInput,
163
+ openCommand,
140
164
  openInput,
165
+ pagesCommand,
141
166
  pagesInput,
167
+ parseViewportArg,
142
168
  runClose,
169
+ saveCommand,
143
170
  saveInput
144
171
  };
@@ -0,0 +1,148 @@
1
+ import { randomBytes } from "node:crypto";
2
+ import { z } from "zod";
3
+ import { buildHostedDeployTarball } from "../core/deploy-artifact.js";
4
+ import { SimpleCLI } from "../framework/simple-cli.js";
5
+ function generateDeploymentName() {
6
+ return `deploy-${Date.now().toString(36)}-${randomBytes(4).toString("hex")}`;
7
+ }
8
+ function getConfig() {
9
+ const apiUrl = process.env.LIBRETTO_API_URL;
10
+ const apiKey = process.env.LIBRETTO_API_KEY;
11
+ if (!apiUrl) {
12
+ throw new Error(
13
+ "LIBRETTO_API_URL environment variable is required."
14
+ );
15
+ }
16
+ if (!apiKey) {
17
+ throw new Error(
18
+ "LIBRETTO_API_KEY environment variable is required."
19
+ );
20
+ }
21
+ return { apiUrl: apiUrl.replace(/\/$/, ""), apiKey };
22
+ }
23
+ async function postJson(apiUrl, apiKey, path, input = {}) {
24
+ return fetch(`${apiUrl}${path}`, {
25
+ method: "POST",
26
+ headers: {
27
+ "x-api-key": apiKey,
28
+ "Content-Type": "application/json"
29
+ },
30
+ body: JSON.stringify({ json: input })
31
+ });
32
+ }
33
+ async function pollDeployment(apiUrl, apiKey, deploymentId, pollIntervalMs, maxWaitMs) {
34
+ const start = Date.now();
35
+ let status = "building";
36
+ let deployment;
37
+ while (status === "building" && Date.now() - start < maxWaitMs) {
38
+ await new Promise((r) => setTimeout(r, pollIntervalMs));
39
+ const res = await postJson(apiUrl, apiKey, "/v1/deployments/get", {
40
+ id: deploymentId
41
+ });
42
+ const body = await res.json();
43
+ if (res.status !== 200) {
44
+ throw new Error(
45
+ `Failed to get deployment status (${res.status}): ${JSON.stringify(body)}`
46
+ );
47
+ }
48
+ status = body.json.status;
49
+ deployment = body.json;
50
+ process.stdout.write(".");
51
+ }
52
+ console.log();
53
+ if (!deployment) {
54
+ throw new Error("Deployment timed out before receiving a status update.");
55
+ }
56
+ return deployment;
57
+ }
58
+ const deployInput = SimpleCLI.input({
59
+ positionals: [
60
+ SimpleCLI.positional("sourceDir", z.string().default("."), {
61
+ help: "Path to source directory (default: current directory)"
62
+ })
63
+ ],
64
+ named: {
65
+ description: SimpleCLI.option(z.string().optional(), {
66
+ help: "Deployment description"
67
+ }),
68
+ entryPoint: SimpleCLI.option(z.string().optional(), {
69
+ name: "entry-point",
70
+ help: "Entry point file (default: index.ts)"
71
+ }),
72
+ external: SimpleCLI.option(
73
+ z.string().optional().transform(
74
+ (value) => value?.split(",").map((entry) => entry.trim()).filter((entry) => entry.length > 0) ?? []
75
+ ),
76
+ {
77
+ help: "Comma-separated packages to keep out of the bundle and install into the deployed package"
78
+ }
79
+ )
80
+ }
81
+ });
82
+ const deployCommand = SimpleCLI.command({
83
+ description: "Deploy workflows to the hosted platform",
84
+ experimental: true
85
+ }).input(deployInput).handle(async ({ input }) => {
86
+ const { apiUrl, apiKey } = getConfig();
87
+ const deploymentName = generateDeploymentName();
88
+ console.log("Bundling hosted deployment artifact...");
89
+ const { entryPoint, source } = await buildHostedDeployTarball({
90
+ additionalExternals: input.external,
91
+ deploymentName,
92
+ entryPoint: input.entryPoint,
93
+ sourceDir: input.sourceDir
94
+ });
95
+ const createPayload = {
96
+ source,
97
+ entry_point: entryPoint
98
+ };
99
+ if (input.description) createPayload.description = input.description;
100
+ console.log("Uploading deployment...");
101
+ const res = await postJson(
102
+ apiUrl,
103
+ apiKey,
104
+ "/v1/deployments/create",
105
+ createPayload
106
+ );
107
+ const body = await res.json();
108
+ if (res.status !== 200) {
109
+ throw new Error(
110
+ `Failed to create deployment (${res.status}): ${JSON.stringify(body)}`
111
+ );
112
+ }
113
+ const { deployment_id, status } = body.json;
114
+ console.log(`Deployment created: ${deployment_id}`);
115
+ console.log(`Status: ${status}`);
116
+ if (status === "building") {
117
+ process.stdout.write("Waiting for build");
118
+ const deployment = await pollDeployment(
119
+ apiUrl,
120
+ apiKey,
121
+ deployment_id,
122
+ 1e4,
123
+ 5 * 60 * 1e3
124
+ );
125
+ if (deployment.status === "failed") {
126
+ throw new Error(
127
+ `Build failed: ${deployment.build_error ?? "unknown error"}`
128
+ );
129
+ }
130
+ if (deployment.status === "ready") {
131
+ console.log(`Build complete.`);
132
+ if (deployment.workflows?.length) {
133
+ console.log(
134
+ `Workflows: ${deployment.workflows.join(", ")}`
135
+ );
136
+ }
137
+ } else {
138
+ console.log(
139
+ `Build still in progress (timed out waiting). Check status with deployment ID: ${deployment_id}`
140
+ );
141
+ }
142
+ }
143
+ return deployment_id;
144
+ });
145
+ export {
146
+ deployCommand,
147
+ deployInput
148
+ };