libretto 0.5.0 → 0.5.2

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 (122) hide show
  1. package/README.md +109 -35
  2. package/dist/cli/cli.js +22 -97
  3. package/dist/cli/commands/browser.js +86 -59
  4. package/dist/cli/commands/execution.js +199 -86
  5. package/dist/cli/commands/init.js +34 -29
  6. package/dist/cli/commands/logs.js +4 -5
  7. package/dist/cli/commands/shared.js +30 -29
  8. package/dist/cli/commands/snapshot.js +26 -39
  9. package/dist/cli/core/ai-config.js +21 -4
  10. package/dist/cli/core/api-snapshot-analyzer.js +15 -5
  11. package/dist/cli/core/browser.js +207 -37
  12. package/dist/cli/core/context.js +4 -1
  13. package/dist/cli/core/session-telemetry.js +434 -174
  14. package/dist/cli/core/session.js +21 -8
  15. package/dist/cli/core/snapshot-analyzer.js +14 -31
  16. package/dist/cli/core/snapshot-api-config.js +2 -6
  17. package/dist/cli/core/telemetry.js +20 -4
  18. package/dist/cli/framework/simple-cli.js +45 -25
  19. package/dist/cli/router.js +14 -21
  20. package/dist/cli/workers/run-integration-runtime.js +24 -5
  21. package/dist/cli/workers/run-integration-worker-protocol.js +3 -1
  22. package/dist/cli/workers/run-integration-worker.js +1 -4
  23. package/dist/index.d.ts +1 -2
  24. package/dist/index.js +7 -10
  25. package/dist/runtime/download/download.js +5 -1
  26. package/dist/runtime/extract/extract.js +11 -2
  27. package/dist/runtime/network/network.js +8 -1
  28. package/dist/runtime/recovery/agent.js +6 -2
  29. package/dist/runtime/recovery/errors.js +3 -1
  30. package/dist/runtime/recovery/recovery.js +3 -1
  31. package/dist/shared/condense-dom/condense-dom.js +17 -69
  32. package/dist/shared/config/config.d.ts +1 -9
  33. package/dist/shared/config/config.js +0 -18
  34. package/dist/shared/config/index.d.ts +2 -1
  35. package/dist/shared/config/index.js +0 -10
  36. package/dist/shared/debug/pause.js +9 -3
  37. package/dist/shared/dom-semantics.d.ts +8 -0
  38. package/dist/shared/dom-semantics.js +69 -0
  39. package/dist/shared/instrumentation/instrument.js +101 -5
  40. package/dist/shared/llm/ai-sdk-adapter.js +3 -1
  41. package/dist/shared/llm/client.js +3 -1
  42. package/dist/shared/logger/index.js +4 -1
  43. package/dist/shared/run/api.js +3 -1
  44. package/dist/shared/run/browser.js +47 -3
  45. package/dist/shared/state/session-state.d.ts +2 -1
  46. package/dist/shared/state/session-state.js +5 -2
  47. package/dist/shared/visualization/ghost-cursor.js +36 -14
  48. package/dist/shared/visualization/highlight.js +9 -6
  49. package/dist/shared/workflow/workflow.d.ts +4 -5
  50. package/dist/shared/workflow/workflow.js +3 -5
  51. package/package.json +6 -2
  52. package/scripts/check-skills-sync.mjs +25 -0
  53. package/scripts/compare-eval-summary.mjs +47 -0
  54. package/scripts/postinstall.mjs +15 -15
  55. package/scripts/prepare-release.sh +97 -0
  56. package/scripts/skills-libretto.mjs +103 -0
  57. package/scripts/summarize-evals.mjs +135 -0
  58. package/scripts/sync-skills.mjs +12 -0
  59. package/skills/libretto/SKILL.md +132 -54
  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 +210 -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/execution.ts +233 -102
  69. package/src/cli/commands/init.ts +37 -33
  70. package/src/cli/commands/logs.ts +7 -7
  71. package/src/cli/commands/shared.ts +36 -37
  72. package/src/cli/commands/snapshot.ts +44 -59
  73. package/src/cli/core/ai-config.ts +24 -4
  74. package/src/cli/core/api-snapshot-analyzer.ts +17 -6
  75. package/src/cli/core/browser.ts +260 -49
  76. package/src/cli/core/context.ts +7 -2
  77. package/src/cli/core/session-telemetry.ts +449 -197
  78. package/src/cli/core/session.ts +21 -7
  79. package/src/cli/core/snapshot-analyzer.ts +26 -46
  80. package/src/cli/core/snapshot-api-config.ts +170 -175
  81. package/src/cli/core/telemetry.ts +39 -4
  82. package/src/cli/framework/simple-cli.ts +144 -77
  83. package/src/cli/router.ts +13 -21
  84. package/src/cli/workers/run-integration-runtime.ts +36 -9
  85. package/src/cli/workers/run-integration-worker-protocol.ts +2 -0
  86. package/src/cli/workers/run-integration-worker.ts +1 -4
  87. package/src/index.ts +73 -66
  88. package/src/runtime/download/download.ts +62 -58
  89. package/src/runtime/download/index.ts +5 -5
  90. package/src/runtime/extract/extract.ts +71 -61
  91. package/src/runtime/network/index.ts +3 -3
  92. package/src/runtime/network/network.ts +99 -93
  93. package/src/runtime/recovery/agent.ts +217 -212
  94. package/src/runtime/recovery/errors.ts +107 -104
  95. package/src/runtime/recovery/index.ts +3 -3
  96. package/src/runtime/recovery/recovery.ts +38 -35
  97. package/src/shared/condense-dom/condense-dom.ts +27 -82
  98. package/src/shared/config/config.ts +0 -19
  99. package/src/shared/config/index.ts +0 -5
  100. package/src/shared/debug/pause.ts +57 -51
  101. package/src/shared/dom-semantics.ts +68 -0
  102. package/src/shared/instrumentation/errors.ts +64 -62
  103. package/src/shared/instrumentation/index.ts +5 -5
  104. package/src/shared/instrumentation/instrument.ts +339 -209
  105. package/src/shared/llm/ai-sdk-adapter.ts +58 -55
  106. package/src/shared/llm/client.ts +181 -174
  107. package/src/shared/llm/types.ts +39 -39
  108. package/src/shared/logger/index.ts +11 -4
  109. package/src/shared/logger/logger.ts +312 -306
  110. package/src/shared/logger/sinks.ts +118 -114
  111. package/src/shared/paths/paths.ts +50 -49
  112. package/src/shared/paths/repo-root.ts +17 -17
  113. package/src/shared/run/api.ts +5 -1
  114. package/src/shared/run/browser.ts +65 -3
  115. package/src/shared/state/index.ts +9 -9
  116. package/src/shared/state/session-state.ts +46 -43
  117. package/src/shared/visualization/ghost-cursor.ts +180 -149
  118. package/src/shared/visualization/highlight.ts +89 -86
  119. package/src/shared/visualization/index.ts +13 -13
  120. package/src/shared/workflow/workflow.ts +19 -25
  121. package/skills/libretto/references/reverse-engineering-network-requests.md +0 -39
  122. package/skills/libretto/references/user-action-log.md +0 -31
@@ -0,0 +1,68 @@
1
+ export const TEST_ATTRIBUTE_NAMES = [
2
+ "data-testid",
3
+ "data-test",
4
+ "data-qa",
5
+ "data-cy",
6
+ ] as const;
7
+
8
+ export const TRUSTED_ATTRIBUTE_NAMES = [
9
+ "id",
10
+ "name",
11
+ "for",
12
+ "tabindex",
13
+ "contenteditable",
14
+ "role",
15
+ "title",
16
+ "alt",
17
+ "type",
18
+ "value",
19
+ "placeholder",
20
+ "autocomplete",
21
+ "href",
22
+ "action",
23
+ "method",
24
+ "src",
25
+ ] as const;
26
+
27
+ export const INTERACTIVE_TAG_NAMES = [
28
+ "a",
29
+ "button",
30
+ "input",
31
+ "select",
32
+ "textarea",
33
+ "form",
34
+ "details",
35
+ "dialog",
36
+ "label",
37
+ ] as const;
38
+
39
+ export const INTERACTIVE_ROLE_NAMES = [
40
+ "button",
41
+ "link",
42
+ "tab",
43
+ "menuitem",
44
+ "checkbox",
45
+ "radio",
46
+ "switch",
47
+ "slider",
48
+ "combobox",
49
+ ] as const;
50
+
51
+ export function filterSemanticClasses(value: string): string {
52
+ const classes = value.split(/\s+/).filter(Boolean);
53
+ const kept = classes.filter((cls) => !isObfuscatedClass(cls));
54
+ return kept.join(" ");
55
+ }
56
+
57
+ export function isObfuscatedClass(cls: string): boolean {
58
+ if (cls.length > 80) return true;
59
+ if (/^_?[0-9a-f]{6,}$/i.test(cls)) return true;
60
+ if (/^[a-z]+_[0-9a-f]{4,}$/i.test(cls)) return true;
61
+ if (/^[a-z]{1,2}[0-9]{2,}$/i.test(cls)) return true;
62
+
63
+ const digits = (cls.match(/[0-9]/g) || []).length;
64
+ const letters = (cls.match(/[a-zA-Z]/g) || []).length;
65
+ if (cls.length >= 6 && digits >= letters * 0.5 && digits >= 2) return true;
66
+
67
+ return false;
68
+ }
@@ -8,75 +8,77 @@ import type { Page, Locator } from "playwright";
8
8
  * Best-effort: if any probe fails, we skip that check silently.
9
9
  */
10
10
  export async function enrichTimeoutError(
11
- err: any,
12
- locator: Locator,
13
- page: Page,
11
+ err: any,
12
+ locator: Locator,
13
+ page: Page,
14
14
  ): Promise<void> {
15
- const reasons: string[] = [];
15
+ const reasons: string[] = [];
16
16
 
17
- try {
18
- const visible = await locator.isVisible().catch(() => null);
19
- if (visible === false) {
20
- reasons.push("Element is not visible");
21
- }
17
+ try {
18
+ const visible = await locator.isVisible().catch(() => null);
19
+ if (visible === false) {
20
+ reasons.push("Element is not visible");
21
+ }
22
22
 
23
- // isInViewport is available in modern Playwright but may not exist in older versions
24
- if (typeof (locator as any).isInViewport === "function") {
25
- const inViewport = await (locator as any).isInViewport().catch(() => null);
26
- if (inViewport === false) {
27
- reasons.push("Element is outside of the viewport");
28
- }
29
- }
23
+ // isInViewport is available in modern Playwright but may not exist in older versions
24
+ if (typeof (locator as any).isInViewport === "function") {
25
+ const inViewport = await (locator as any)
26
+ .isInViewport()
27
+ .catch(() => null);
28
+ if (inViewport === false) {
29
+ reasons.push("Element is outside of the viewport");
30
+ }
31
+ }
30
32
 
31
- const enabled = await locator.isEnabled().catch(() => null);
32
- if (enabled === false) {
33
- reasons.push("Element is not enabled (disabled)");
34
- }
33
+ const enabled = await locator.isEnabled().catch(() => null);
34
+ if (enabled === false) {
35
+ reasons.push("Element is not enabled (disabled)");
36
+ }
35
37
 
36
- // If the element appears visible and in viewport, check for intercepting elements
37
- if (reasons.length === 0) {
38
- const box = await locator.boundingBox().catch(() => null);
39
- if (box) {
40
- const centerX = box.x + box.width / 2;
41
- const centerY = box.y + box.height / 2;
38
+ // If the element appears visible and in viewport, check for intercepting elements
39
+ if (reasons.length === 0) {
40
+ const box = await locator.boundingBox().catch(() => null);
41
+ if (box) {
42
+ const centerX = box.x + box.width / 2;
43
+ const centerY = box.y + box.height / 2;
42
44
 
43
- const interceptInfo = await page
44
- .evaluate(
45
- ({ x, y }) => {
46
- const els = document.elementsFromPoint(x, y);
47
- if (!els || els.length < 2) return null;
48
- const topEl = els[0];
49
- if (!topEl) return null;
45
+ const interceptInfo = await page
46
+ .evaluate(
47
+ ({ x, y }) => {
48
+ const els = document.elementsFromPoint(x, y);
49
+ if (!els || els.length < 2) return null;
50
+ const topEl = els[0];
51
+ if (!topEl) return null;
50
52
 
51
- // Build a brief preview of the intercepting element
52
- const tag = topEl.tagName.toLowerCase();
53
- const id = topEl.id ? `#${topEl.id}` : "";
54
- const cls = topEl.className
55
- ? `.${String(topEl.className).split(/\s+/).slice(0, 2).join(".")}`
56
- : "";
57
- const text = (topEl.textContent || "").trim().slice(0, 40);
58
- return {
59
- tag,
60
- preview: `<${tag}${id}${cls}>${text ? ` "${text}"` : ""}`,
61
- };
62
- },
63
- { x: centerX, y: centerY },
64
- )
65
- .catch(() => null);
53
+ // Build a brief preview of the intercepting element
54
+ const tag = topEl.tagName.toLowerCase();
55
+ const id = topEl.id ? `#${topEl.id}` : "";
56
+ const cls = topEl.className
57
+ ? `.${String(topEl.className).split(/\s+/).slice(0, 2).join(".")}`
58
+ : "";
59
+ const text = (topEl.textContent || "").trim().slice(0, 40);
60
+ return {
61
+ tag,
62
+ preview: `<${tag}${id}${cls}>${text ? ` "${text}"` : ""}`,
63
+ };
64
+ },
65
+ { x: centerX, y: centerY },
66
+ )
67
+ .catch(() => null);
66
68
 
67
- if (interceptInfo) {
68
- reasons.push(
69
- `Element may be intercepted by ${interceptInfo.preview}`,
70
- );
71
- }
72
- }
73
- }
74
- } catch {
75
- // All enrichment is best-effort
76
- }
69
+ if (interceptInfo) {
70
+ reasons.push(
71
+ `Element may be intercepted by ${interceptInfo.preview}`,
72
+ );
73
+ }
74
+ }
75
+ }
76
+ } catch {
77
+ // All enrichment is best-effort
78
+ }
77
79
 
78
- if (reasons.length > 0) {
79
- const enrichment = `\n[libretto diagnostics] ${reasons.join("; ")}`;
80
- err.message = (err.message || "") + enrichment;
81
- }
80
+ if (reasons.length > 0) {
81
+ const enrichment = `\n[libretto diagnostics] ${reasons.join("; ")}`;
82
+ err.message = (err.message || "") + enrichment;
83
+ }
82
84
  }
@@ -1,9 +1,9 @@
1
1
  export {
2
- instrumentPage,
3
- installInstrumentation,
4
- instrumentContext,
5
- type InstrumentationOptions,
6
- type InstrumentedPage,
2
+ instrumentPage,
3
+ installInstrumentation,
4
+ instrumentContext,
5
+ type InstrumentationOptions,
6
+ type InstrumentedPage,
7
7
  } from "./instrument.js";
8
8
 
9
9
  export { enrichTimeoutError } from "./errors.js";