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
@@ -1,16 +1,19 @@
1
1
  import type { Page } from "playwright";
2
2
  import type z from "zod";
3
- import { type MinimalLogger, defaultLogger } from "../../shared/logger/logger.js";
3
+ import {
4
+ type MinimalLogger,
5
+ defaultLogger,
6
+ } from "../../shared/logger/logger.js";
4
7
  import type { LLMClient } from "../../shared/llm/types.js";
5
8
 
6
9
  export type ExtractOptions<T extends z.ZodType> = {
7
- page: Page;
8
- instruction: string;
9
- schema: T;
10
- llmClient: LLMClient;
11
- logger?: MinimalLogger;
12
- /** Optional CSS selector to scope extraction to a specific element. */
13
- selector?: string;
10
+ page: Page;
11
+ instruction: string;
12
+ schema: T;
13
+ llmClient: LLMClient;
14
+ logger?: MinimalLogger;
15
+ /** Optional CSS selector to scope extraction to a specific element. */
16
+ selector?: string;
14
17
  };
15
18
 
16
19
  /**
@@ -20,48 +23,55 @@ export type ExtractOptions<T extends z.ZodType> = {
20
23
  * matching the provided Zod schema.
21
24
  */
22
25
  export async function extractFromPage<T extends z.ZodType>(
23
- options: ExtractOptions<T>,
26
+ options: ExtractOptions<T>,
24
27
  ): Promise<z.infer<T>> {
25
- const { page, instruction, schema, selector, logger = defaultLogger, llmClient } = options;
28
+ const {
29
+ page,
30
+ instruction,
31
+ schema,
32
+ selector,
33
+ logger = defaultLogger,
34
+ llmClient,
35
+ } = options;
26
36
 
27
- let screenshot: string;
28
- let domContent: string | undefined;
37
+ let screenshot: string;
38
+ let domContent: string | undefined;
29
39
 
30
- if (selector) {
31
- const element = page.locator(selector);
32
- await element.waitFor({ state: "visible", timeout: 10_000 });
40
+ if (selector) {
41
+ const element = page.locator(selector);
42
+ await element.waitFor({ state: "visible", timeout: 10_000 });
33
43
 
34
- const screenshotBuffer = await element.screenshot();
35
- screenshot = screenshotBuffer.toString("base64");
44
+ const screenshotBuffer = await element.screenshot();
45
+ screenshot = screenshotBuffer.toString("base64");
36
46
 
37
- try {
38
- domContent = await element.innerHTML();
39
- if (domContent.length > 30000) {
40
- domContent = domContent.slice(0, 30000) + "\n... [truncated]";
41
- }
42
- } catch {
43
- domContent = undefined;
44
- }
45
- } else {
46
- const cdpClient = await page.context().newCDPSession(page);
47
- await cdpClient.send("Page.enable");
48
- const { data } = await cdpClient.send("Page.captureScreenshot", {
49
- format: "png",
50
- });
51
- screenshot = data;
47
+ try {
48
+ domContent = await element.innerHTML();
49
+ if (domContent.length > 30000) {
50
+ domContent = domContent.slice(0, 30000) + "\n... [truncated]";
51
+ }
52
+ } catch {
53
+ domContent = undefined;
54
+ }
55
+ } else {
56
+ const cdpClient = await page.context().newCDPSession(page);
57
+ await cdpClient.send("Page.enable");
58
+ const { data } = await cdpClient.send("Page.captureScreenshot", {
59
+ format: "png",
60
+ });
61
+ screenshot = data;
52
62
 
53
- try {
54
- const htmlContent = await page.content();
55
- domContent =
56
- htmlContent.length > 50000
57
- ? htmlContent.slice(0, 50000) + "\n... [truncated]"
58
- : htmlContent;
59
- } catch {
60
- domContent = undefined;
61
- }
62
- }
63
+ try {
64
+ const htmlContent = await page.content();
65
+ domContent =
66
+ htmlContent.length > 50000
67
+ ? htmlContent.slice(0, 50000) + "\n... [truncated]"
68
+ : htmlContent;
69
+ } catch {
70
+ domContent = undefined;
71
+ }
72
+ }
63
73
 
64
- const prompt = `You are analyzing a screenshot${selector ? " of a specific element" : ""} from a web page to extract structured data.
74
+ const prompt = `You are analyzing a screenshot${selector ? " of a specific element" : ""} from a web page to extract structured data.
65
75
 
66
76
  Instruction: ${instruction}
67
77
 
@@ -69,24 +79,24 @@ ${domContent ? `Here is the HTML content for additional context:\n<html>\n${domC
69
79
 
70
80
  Extract the requested information from the screenshot and return it in the specified format. Be precise and only extract what is visible.`;
71
81
 
72
- const result = await llmClient.generateObjectFromMessages({
73
- schema,
74
- messages: [
75
- {
76
- role: "user",
77
- content: [
78
- { type: "text", text: prompt },
79
- { type: "image", image: `data:image/png;base64,${screenshot}` },
80
- ],
81
- },
82
- ],
83
- temperature: 0,
84
- });
82
+ const result = await llmClient.generateObjectFromMessages({
83
+ schema,
84
+ messages: [
85
+ {
86
+ role: "user",
87
+ content: [
88
+ { type: "text", text: prompt },
89
+ { type: "image", image: `data:image/png;base64,${screenshot}` },
90
+ ],
91
+ },
92
+ ],
93
+ temperature: 0,
94
+ });
85
95
 
86
- logger.info("extractFromPage completed", {
87
- selector,
88
- instruction: instruction.slice(0, 100),
89
- });
96
+ logger.info("extractFromPage completed", {
97
+ selector,
98
+ instruction: instruction.slice(0, 100),
99
+ });
90
100
 
91
- return result;
101
+ return result;
92
102
  }
@@ -1,5 +1,5 @@
1
1
  export {
2
- pageRequest,
3
- type RequestConfig,
4
- type PageRequestOptions,
2
+ pageRequest,
3
+ type RequestConfig,
4
+ type PageRequestOptions,
5
5
  } from "./network.js";
@@ -3,25 +3,25 @@ import type z from "zod";
3
3
  import type { MinimalLogger } from "../../shared/logger/logger.js";
4
4
 
5
5
  export type RequestConfig = {
6
- url: string;
7
- method?: "GET" | "POST" | "PUT" | "DELETE" | "PATCH";
8
- headers?: Record<string, string>;
9
- body?: Record<string, any> | string;
10
- /** How to serialize the body. Defaults to "json". */
11
- bodyType?: "json" | "form";
12
- /** How to parse the response. Defaults to "json". */
13
- responseType?: "json" | "text" | "xml";
6
+ url: string;
7
+ method?: "GET" | "POST" | "PUT" | "DELETE" | "PATCH";
8
+ headers?: Record<string, string>;
9
+ body?: Record<string, any> | string;
10
+ /** How to serialize the body. Defaults to "json". */
11
+ bodyType?: "json" | "form";
12
+ /** How to parse the response. Defaults to "json". */
13
+ responseType?: "json" | "text" | "xml";
14
14
  };
15
15
 
16
16
  export type PageRequestOptions<T extends z.ZodType | undefined = undefined> = {
17
- logger?: MinimalLogger;
18
- /** Optional Zod schema to validate the response body. */
19
- schema?: T;
17
+ logger?: MinimalLogger;
18
+ /** Optional Zod schema to validate the response body. */
19
+ schema?: T;
20
20
  };
21
21
 
22
22
  type PageRequestResult<T extends z.ZodType | undefined> = T extends z.ZodType
23
- ? z.infer<T>
24
- : any;
23
+ ? z.infer<T>
24
+ : any;
25
25
 
26
26
  /**
27
27
  * Executes a fetch() call inside the browser context via page.evaluate().
@@ -29,85 +29,91 @@ type PageRequestResult<T extends z.ZodType | undefined> = T extends z.ZodType
29
29
  * validation, and logging.
30
30
  */
31
31
  export async function pageRequest<T extends z.ZodType | undefined = undefined>(
32
- page: Page,
33
- config: RequestConfig,
34
- options?: PageRequestOptions<T>,
32
+ page: Page,
33
+ config: RequestConfig,
34
+ options?: PageRequestOptions<T>,
35
35
  ): Promise<PageRequestResult<T>> {
36
- const { url, method = "GET", headers = {}, body, bodyType = "json", responseType = "json" } = config;
37
- const { logger, schema } = options ?? {};
38
-
39
- const startTime = Date.now();
40
-
41
- // Build fetch options to pass into page.evaluate
42
- const fetchHeaders: Record<string, string> = { ...headers };
43
- let fetchBody: string | undefined;
44
-
45
- if (body !== undefined) {
46
- if (bodyType === "form") {
47
- fetchHeaders["Content-Type"] = "application/x-www-form-urlencoded";
48
- if (typeof body === "string") {
49
- fetchBody = body;
50
- } else {
51
- fetchBody = new URLSearchParams(
52
- Object.entries(body).map(([k, v]) => [k, String(v)]),
53
- ).toString();
54
- }
55
- } else {
56
- fetchHeaders["Content-Type"] = "application/json";
57
- fetchBody = typeof body === "string" ? body : JSON.stringify(body);
58
- }
59
- }
60
-
61
- const result = await page.evaluate(
62
- async ({ url, method, headers, body, responseType }) => {
63
- const res = await fetch(url, {
64
- method,
65
- headers,
66
- body: body ?? undefined,
67
- });
68
-
69
- const status = res.status;
70
- const ok = res.ok;
71
- let data: any;
72
-
73
- if (responseType === "json") {
74
- data = await res.json();
75
- } else {
76
- data = await res.text();
77
- }
78
-
79
- return { status, ok, data };
80
- },
81
- { url, method, headers: fetchHeaders, body: fetchBody, responseType },
82
- );
83
-
84
- const duration = Date.now() - startTime;
85
-
86
- if (!result.ok) {
87
- logger?.warn("network:request:error", {
88
- method,
89
- url,
90
- status: result.status,
91
- duration,
92
- body: typeof result.data === "string"
93
- ? result.data.slice(0, 500)
94
- : undefined,
95
- });
96
- throw new Error(
97
- `pageRequest failed: ${method} ${url} returned ${result.status}`,
98
- );
99
- }
100
-
101
- logger?.info("network:request", {
102
- method,
103
- url,
104
- status: result.status,
105
- duration,
106
- });
107
-
108
- if (schema) {
109
- return schema.parse(result.data) as PageRequestResult<T>;
110
- }
111
-
112
- return result.data as PageRequestResult<T>;
36
+ const {
37
+ url,
38
+ method = "GET",
39
+ headers = {},
40
+ body,
41
+ bodyType = "json",
42
+ responseType = "json",
43
+ } = config;
44
+ const { logger, schema } = options ?? {};
45
+
46
+ const startTime = Date.now();
47
+
48
+ // Build fetch options to pass into page.evaluate
49
+ const fetchHeaders: Record<string, string> = { ...headers };
50
+ let fetchBody: string | undefined;
51
+
52
+ if (body !== undefined) {
53
+ if (bodyType === "form") {
54
+ fetchHeaders["Content-Type"] = "application/x-www-form-urlencoded";
55
+ if (typeof body === "string") {
56
+ fetchBody = body;
57
+ } else {
58
+ fetchBody = new URLSearchParams(
59
+ Object.entries(body).map(([k, v]) => [k, String(v)]),
60
+ ).toString();
61
+ }
62
+ } else {
63
+ fetchHeaders["Content-Type"] = "application/json";
64
+ fetchBody = typeof body === "string" ? body : JSON.stringify(body);
65
+ }
66
+ }
67
+
68
+ const result = await page.evaluate(
69
+ async ({ url, method, headers, body, responseType }) => {
70
+ const res = await fetch(url, {
71
+ method,
72
+ headers,
73
+ body: body ?? undefined,
74
+ });
75
+
76
+ const status = res.status;
77
+ const ok = res.ok;
78
+ let data: any;
79
+
80
+ if (responseType === "json") {
81
+ data = await res.json();
82
+ } else {
83
+ data = await res.text();
84
+ }
85
+
86
+ return { status, ok, data };
87
+ },
88
+ { url, method, headers: fetchHeaders, body: fetchBody, responseType },
89
+ );
90
+
91
+ const duration = Date.now() - startTime;
92
+
93
+ if (!result.ok) {
94
+ logger?.warn("network:request:error", {
95
+ method,
96
+ url,
97
+ status: result.status,
98
+ duration,
99
+ body:
100
+ typeof result.data === "string" ? result.data.slice(0, 500) : undefined,
101
+ });
102
+ throw new Error(
103
+ `pageRequest failed: ${method} ${url} returned ${result.status}`,
104
+ );
105
+ }
106
+
107
+ logger?.info("network:request", {
108
+ method,
109
+ url,
110
+ status: result.status,
111
+ duration,
112
+ });
113
+
114
+ if (schema) {
115
+ return schema.parse(result.data) as PageRequestResult<T>;
116
+ }
117
+
118
+ return result.data as PageRequestResult<T>;
113
119
  }