libretto 0.4.4 → 0.5.1
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 +106 -36
- package/dist/cli/cli.js +39 -113
- package/dist/cli/commands/ai.js +1 -1
- package/dist/cli/commands/browser.js +87 -60
- package/dist/cli/commands/execution.js +201 -88
- package/dist/cli/commands/init.js +30 -8
- package/dist/cli/commands/logs.js +5 -6
- package/dist/cli/commands/shared.js +30 -29
- package/dist/cli/commands/snapshot.js +26 -39
- package/dist/cli/core/ai-config.js +9 -2
- package/dist/cli/core/api-snapshot-analyzer.js +15 -5
- package/dist/cli/core/browser.js +141 -33
- package/dist/cli/core/context.js +7 -18
- package/dist/cli/core/session-telemetry.js +5 -2
- package/dist/cli/core/session.js +23 -10
- package/dist/cli/core/snapshot-analyzer.js +16 -33
- package/dist/cli/core/snapshot-api-config.js +2 -6
- package/dist/cli/core/telemetry.js +10 -2
- package/dist/cli/framework/simple-cli.js +45 -25
- package/dist/cli/router.js +14 -21
- package/dist/cli/workers/run-integration-runtime.js +26 -7
- package/dist/cli/workers/run-integration-worker-protocol.js +3 -1
- package/dist/cli/workers/run-integration-worker.js +1 -4
- package/dist/index.d.ts +1 -2
- package/dist/index.js +7 -10
- package/dist/runtime/download/download.js +5 -1
- package/dist/runtime/extract/extract.js +11 -2
- package/dist/runtime/network/network.js +8 -1
- package/dist/runtime/recovery/agent.js +6 -2
- package/dist/runtime/recovery/errors.js +3 -1
- package/dist/runtime/recovery/recovery.js +3 -1
- package/dist/shared/condense-dom/condense-dom.js +6 -13
- package/dist/shared/config/config.d.ts +1 -9
- package/dist/shared/config/config.js +0 -18
- package/dist/shared/config/index.d.ts +2 -1
- package/dist/shared/config/index.js +0 -10
- package/dist/shared/debug/pause.js +9 -3
- package/dist/shared/instrumentation/instrument.js +101 -5
- package/dist/shared/llm/ai-sdk-adapter.js +3 -1
- package/dist/shared/llm/client.js +3 -1
- package/dist/shared/logger/index.js +4 -1
- package/dist/shared/paths/paths.js +2 -1
- package/dist/shared/paths/repo-root.d.ts +3 -0
- package/dist/shared/paths/repo-root.js +24 -0
- package/dist/shared/run/api.js +3 -1
- package/dist/shared/run/browser.js +7 -2
- package/dist/shared/state/session-state.d.ts +2 -1
- package/dist/shared/state/session-state.js +5 -2
- package/dist/shared/visualization/ghost-cursor.js +19 -10
- package/dist/shared/visualization/highlight.js +9 -6
- package/dist/shared/workflow/workflow.d.ts +4 -5
- package/dist/shared/workflow/workflow.js +3 -5
- package/package.json +11 -8
- package/scripts/check-skills-sync.mjs +25 -0
- package/scripts/compare-eval-summary.mjs +47 -0
- package/scripts/postinstall.mjs +26 -17
- package/scripts/prepare-release.sh +97 -0
- package/scripts/skills-libretto.mjs +103 -0
- package/scripts/summarize-evals.mjs +135 -0
- package/scripts/sync-skills.mjs +12 -0
- package/skills/libretto/SKILL.md +130 -377
- package/skills/libretto/references/auth-profiles.md +30 -0
- package/skills/libretto/{code-generation-rules.md → references/code-generation-rules.md} +27 -42
- package/skills/libretto/references/configuration-file-reference.md +53 -0
- package/skills/libretto/references/pages-and-page-targeting.md +29 -0
- package/skills/libretto/references/site-security-review.md +143 -0
- package/src/cli/cli.ts +86 -0
- package/src/cli/commands/ai.ts +35 -0
- package/src/cli/commands/browser.ts +189 -0
- package/src/cli/commands/execution.ts +822 -0
- package/src/cli/commands/init.ts +350 -0
- package/src/cli/commands/logs.ts +128 -0
- package/src/cli/commands/shared.ts +69 -0
- package/src/cli/commands/snapshot.ts +312 -0
- package/src/cli/core/ai-config.ts +264 -0
- package/src/cli/core/api-snapshot-analyzer.ts +108 -0
- package/src/cli/core/browser.ts +976 -0
- package/src/cli/core/context.ts +127 -0
- package/src/cli/core/pause-signals.ts +35 -0
- package/src/cli/core/session-telemetry.ts +564 -0
- package/src/cli/core/session.ts +223 -0
- package/src/cli/core/snapshot-analyzer.ts +855 -0
- package/src/cli/core/snapshot-api-config.ts +231 -0
- package/src/cli/core/telemetry.ts +459 -0
- package/src/cli/framework/simple-cli.ts +1340 -0
- package/src/cli/index.ts +13 -0
- package/src/cli/router.ts +20 -0
- package/src/cli/workers/run-integration-runtime.ts +338 -0
- package/src/cli/workers/run-integration-worker-protocol.ts +16 -0
- package/src/cli/workers/run-integration-worker.ts +72 -0
- package/src/index.ts +127 -0
- package/src/runtime/download/download.ts +104 -0
- package/src/runtime/download/index.ts +7 -0
- package/src/runtime/extract/extract.ts +102 -0
- package/src/runtime/extract/index.ts +1 -0
- package/src/runtime/network/index.ts +5 -0
- package/src/runtime/network/network.ts +119 -0
- package/{dist/runtime/recovery/agent.cjs → src/runtime/recovery/agent.ts} +114 -76
- package/src/runtime/recovery/errors.ts +155 -0
- package/src/runtime/recovery/index.ts +7 -0
- package/src/runtime/recovery/recovery.ts +53 -0
- package/{dist/shared/condense-dom/condense-dom.cjs → src/shared/condense-dom/condense-dom.ts} +249 -124
- package/src/shared/config/config.ts +3 -0
- package/src/shared/config/index.ts +0 -0
- package/src/shared/debug/index.ts +1 -0
- package/src/shared/debug/pause.ts +91 -0
- package/src/shared/instrumentation/errors.ts +84 -0
- package/src/shared/instrumentation/index.ts +9 -0
- package/src/shared/instrumentation/instrument.ts +406 -0
- package/src/shared/llm/ai-sdk-adapter.ts +81 -0
- package/{dist/shared/llm/client.cjs → src/shared/llm/client.ts} +86 -80
- package/src/shared/llm/index.ts +3 -0
- package/src/shared/llm/types.ts +63 -0
- package/src/shared/logger/index.ts +13 -0
- package/src/shared/logger/logger.ts +358 -0
- package/src/shared/logger/sinks.ts +148 -0
- package/src/shared/paths/paths.ts +110 -0
- package/src/shared/paths/repo-root.ts +27 -0
- package/src/shared/run/api.ts +6 -0
- package/src/shared/run/browser.ts +107 -0
- package/src/shared/state/index.ts +11 -0
- package/src/shared/state/session-state.ts +77 -0
- package/src/shared/visualization/ghost-cursor.ts +213 -0
- package/src/shared/visualization/highlight.ts +149 -0
- package/src/shared/visualization/index.ts +18 -0
- package/src/shared/workflow/workflow.ts +36 -0
- package/dist/index.cjs +0 -144
- package/dist/index.d.cts +0 -21
- package/dist/runtime/download/download.cjs +0 -70
- package/dist/runtime/download/download.d.cts +0 -35
- package/dist/runtime/download/index.cjs +0 -30
- package/dist/runtime/download/index.d.cts +0 -3
- package/dist/runtime/extract/extract.cjs +0 -88
- package/dist/runtime/extract/extract.d.cts +0 -23
- package/dist/runtime/extract/index.cjs +0 -28
- package/dist/runtime/extract/index.d.cts +0 -5
- package/dist/runtime/network/index.cjs +0 -28
- package/dist/runtime/network/index.d.cts +0 -4
- package/dist/runtime/network/network.cjs +0 -91
- package/dist/runtime/network/network.d.cts +0 -28
- package/dist/runtime/recovery/agent.d.cts +0 -13
- package/dist/runtime/recovery/errors.cjs +0 -124
- package/dist/runtime/recovery/errors.d.cts +0 -31
- package/dist/runtime/recovery/index.cjs +0 -34
- package/dist/runtime/recovery/index.d.cts +0 -7
- package/dist/runtime/recovery/recovery.cjs +0 -55
- package/dist/runtime/recovery/recovery.d.cts +0 -12
- package/dist/shared/condense-dom/condense-dom.d.cts +0 -34
- package/dist/shared/config/config.cjs +0 -44
- package/dist/shared/config/config.d.cts +0 -10
- package/dist/shared/config/index.cjs +0 -32
- package/dist/shared/config/index.d.cts +0 -1
- package/dist/shared/debug/index.cjs +0 -28
- package/dist/shared/debug/index.d.cts +0 -1
- package/dist/shared/debug/pause.cjs +0 -86
- package/dist/shared/debug/pause.d.cts +0 -12
- package/dist/shared/instrumentation/errors.cjs +0 -81
- package/dist/shared/instrumentation/errors.d.cts +0 -12
- package/dist/shared/instrumentation/index.cjs +0 -35
- package/dist/shared/instrumentation/index.d.cts +0 -6
- package/dist/shared/instrumentation/instrument.cjs +0 -206
- package/dist/shared/instrumentation/instrument.d.cts +0 -32
- package/dist/shared/llm/ai-sdk-adapter.cjs +0 -71
- package/dist/shared/llm/ai-sdk-adapter.d.cts +0 -22
- package/dist/shared/llm/client.d.cts +0 -13
- package/dist/shared/llm/index.cjs +0 -31
- package/dist/shared/llm/index.d.cts +0 -5
- package/dist/shared/llm/types.cjs +0 -16
- package/dist/shared/llm/types.d.cts +0 -67
- package/dist/shared/logger/index.cjs +0 -37
- package/dist/shared/logger/index.d.cts +0 -2
- package/dist/shared/logger/logger.cjs +0 -232
- package/dist/shared/logger/logger.d.cts +0 -86
- package/dist/shared/logger/sinks.cjs +0 -160
- package/dist/shared/logger/sinks.d.cts +0 -9
- package/dist/shared/paths/paths.cjs +0 -104
- package/dist/shared/paths/paths.d.cts +0 -10
- package/dist/shared/run/api.cjs +0 -28
- package/dist/shared/run/api.d.cts +0 -2
- package/dist/shared/run/browser.cjs +0 -98
- package/dist/shared/run/browser.d.cts +0 -22
- package/dist/shared/state/index.cjs +0 -38
- package/dist/shared/state/index.d.cts +0 -2
- package/dist/shared/state/session-state.cjs +0 -92
- package/dist/shared/state/session-state.d.cts +0 -40
- package/dist/shared/visualization/ghost-cursor.cjs +0 -174
- package/dist/shared/visualization/ghost-cursor.d.cts +0 -37
- package/dist/shared/visualization/highlight.cjs +0 -134
- package/dist/shared/visualization/highlight.d.cts +0 -22
- package/dist/shared/visualization/index.cjs +0 -45
- package/dist/shared/visualization/index.d.cts +0 -3
- package/dist/shared/workflow/workflow.cjs +0 -47
- package/dist/shared/workflow/workflow.d.cts +0 -21
- package/skills/libretto/integration-approach-selection.md +0 -174
|
@@ -1,9 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
existsSync,
|
|
3
|
-
mkdtempSync,
|
|
4
|
-
readFileSync,
|
|
5
|
-
rmSync
|
|
6
|
-
} from "node:fs";
|
|
1
|
+
import { existsSync, mkdtempSync, readFileSync, rmSync } from "node:fs";
|
|
7
2
|
import { extname, isAbsolute, join, resolve } from "node:path";
|
|
8
3
|
import { spawn } from "node:child_process";
|
|
9
4
|
import { tmpdir } from "node:os";
|
|
@@ -60,7 +55,12 @@ Screenshot file path: ${pngPath}
|
|
|
60
55
|
Use the screenshot alongside the HTML snapshot context above.`;
|
|
61
56
|
}
|
|
62
57
|
async runAnalyzer(args, logger, stdinText) {
|
|
63
|
-
const result = await runExternalCommand(
|
|
58
|
+
const result = await runExternalCommand(
|
|
59
|
+
this.command,
|
|
60
|
+
args,
|
|
61
|
+
logger,
|
|
62
|
+
stdinText
|
|
63
|
+
);
|
|
64
64
|
if (result.exitCode !== 0) {
|
|
65
65
|
throw new Error(
|
|
66
66
|
`Analyzer command failed (${[this.command, ...args].join(" ")}).
|
|
@@ -76,7 +76,7 @@ ${stripAnsi(result.stderr).trim() || stripAnsi(result.stdout).trim() || "No erro
|
|
|
76
76
|
}
|
|
77
77
|
class CodexUserCodingAgent extends UserCodingAgent {
|
|
78
78
|
async analyzeSnapshot(prompt, pngPath, logger) {
|
|
79
|
-
const tempDir = mkdtempSync(join(tmpdir(), "libretto-
|
|
79
|
+
const tempDir = mkdtempSync(join(tmpdir(), "libretto-analyzer-"));
|
|
80
80
|
const outputPath = join(
|
|
81
81
|
tempDir,
|
|
82
82
|
`snapshot-analyzer-${Date.now()}-${Math.random().toString(36).slice(2)}.json`
|
|
@@ -171,7 +171,7 @@ async function runExternalCommand(command, args, logger, stdinText) {
|
|
|
171
171
|
if (error.code === "ENOENT") {
|
|
172
172
|
reject(
|
|
173
173
|
new Error(
|
|
174
|
-
`Command not found: ${command}. Configure AI with 'libretto
|
|
174
|
+
`Command not found: ${command}. Configure AI with 'libretto ai configure'.`
|
|
175
175
|
)
|
|
176
176
|
);
|
|
177
177
|
return;
|
|
@@ -535,7 +535,9 @@ function buildInlinePromptSelection(args, fullHtmlContent, condensedHtmlContent,
|
|
|
535
535
|
fullDomChars: fullHtmlContent.length,
|
|
536
536
|
fullDomEstimatedTokens: estimateTokensFromChars(fullHtmlContent.length),
|
|
537
537
|
condensedDomChars: condensedHtmlContent.length,
|
|
538
|
-
condensedDomEstimatedTokens: estimateTokensFromChars(
|
|
538
|
+
condensedDomEstimatedTokens: estimateTokensFromChars(
|
|
539
|
+
condensedHtmlContent.length
|
|
540
|
+
),
|
|
539
541
|
configuredModel: model
|
|
540
542
|
};
|
|
541
543
|
const buildCandidate = (domSource, htmlContent, selectionReason, truncated) => {
|
|
@@ -607,7 +609,10 @@ function buildInlinePromptSelection(args, fullHtmlContent, condensedHtmlContent,
|
|
|
607
609
|
2e3,
|
|
608
610
|
budget.promptBudgetTokens - estimateTokensFromChars(basePrompt.length)
|
|
609
611
|
);
|
|
610
|
-
const truncatedHtml = truncateText(
|
|
612
|
+
const truncatedHtml = truncateText(
|
|
613
|
+
condensedHtmlContent,
|
|
614
|
+
availableHtmlTokens * 4
|
|
615
|
+
);
|
|
611
616
|
return buildCandidate(
|
|
612
617
|
"condensed",
|
|
613
618
|
truncatedHtml.text,
|
|
@@ -615,27 +620,6 @@ function buildInlinePromptSelection(args, fullHtmlContent, condensedHtmlContent,
|
|
|
615
620
|
truncatedHtml.truncated
|
|
616
621
|
);
|
|
617
622
|
}
|
|
618
|
-
function formatInterpretationOutput(parsed, header = "Interpretation:") {
|
|
619
|
-
const outputLines = [];
|
|
620
|
-
outputLines.push(header);
|
|
621
|
-
outputLines.push(`Answer: ${parsed.answer}`);
|
|
622
|
-
outputLines.push("");
|
|
623
|
-
if (parsed.selectors.length === 0) {
|
|
624
|
-
outputLines.push("Selectors: none found.");
|
|
625
|
-
} else {
|
|
626
|
-
outputLines.push("Selectors:");
|
|
627
|
-
parsed.selectors.forEach((selector, index) => {
|
|
628
|
-
outputLines.push(` ${index + 1}. ${selector.label}`);
|
|
629
|
-
outputLines.push(` selector: ${selector.selector}`);
|
|
630
|
-
outputLines.push(` rationale: ${selector.rationale}`);
|
|
631
|
-
});
|
|
632
|
-
}
|
|
633
|
-
if (parsed.notes && parsed.notes.trim()) {
|
|
634
|
-
outputLines.push("");
|
|
635
|
-
outputLines.push(`Notes: ${parsed.notes.trim()}`);
|
|
636
|
-
}
|
|
637
|
-
return outputLines.join("\n");
|
|
638
|
-
}
|
|
639
623
|
async function runInterpret(args, logger) {
|
|
640
624
|
logger.info("interpret-start", {
|
|
641
625
|
objective: args.objective,
|
|
@@ -676,7 +660,6 @@ export {
|
|
|
676
660
|
InterpretResultSchema,
|
|
677
661
|
buildInlinePromptSelection,
|
|
678
662
|
canAnalyzeSnapshots,
|
|
679
|
-
formatInterpretationOutput,
|
|
680
663
|
getMimeType,
|
|
681
664
|
readFileAsBase64,
|
|
682
665
|
runInterpret
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "node:fs";
|
|
2
2
|
import { dirname, join, resolve } from "node:path";
|
|
3
|
-
import {
|
|
4
|
-
readAiConfig
|
|
5
|
-
} from "./ai-config.js";
|
|
3
|
+
import { readAiConfig } from "./ai-config.js";
|
|
6
4
|
import { LIBRETTO_CONFIG_PATH, REPO_ROOT } from "./context.js";
|
|
7
5
|
import {
|
|
8
6
|
hasProviderCredentials,
|
|
@@ -154,9 +152,7 @@ function resolveSnapshotApiModel(config = readAiConfig()) {
|
|
|
154
152
|
function resolveSnapshotApiModelOrThrow(config = readAiConfig()) {
|
|
155
153
|
const selection = resolveSnapshotApiModel(config);
|
|
156
154
|
if (!selection) {
|
|
157
|
-
throw new SnapshotApiUnavailableError(
|
|
158
|
-
noSnapshotApiConfiguredMessage()
|
|
159
|
-
);
|
|
155
|
+
throw new SnapshotApiUnavailableError(noSnapshotApiConfiguredMessage());
|
|
160
156
|
}
|
|
161
157
|
if (!hasProviderCredentials(selection.provider)) {
|
|
162
158
|
throw new SnapshotApiUnavailableError(
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
appendFileSync,
|
|
3
|
+
existsSync,
|
|
4
|
+
readFileSync,
|
|
5
|
+
writeFileSync
|
|
6
|
+
} from "node:fs";
|
|
2
7
|
import {
|
|
3
8
|
getSessionActionsLogPath,
|
|
4
9
|
getSessionNetworkLogPath
|
|
@@ -56,7 +61,10 @@ function clearNetworkLog(session) {
|
|
|
56
61
|
function parentLogAction(session, entry) {
|
|
57
62
|
try {
|
|
58
63
|
const record = { ts: (/* @__PURE__ */ new Date()).toISOString(), ...entry };
|
|
59
|
-
appendFileSync(
|
|
64
|
+
appendFileSync(
|
|
65
|
+
getSessionActionsLogPath(session),
|
|
66
|
+
JSON.stringify(record) + "\n"
|
|
67
|
+
);
|
|
60
68
|
} catch {
|
|
61
69
|
}
|
|
62
70
|
}
|
|
@@ -214,7 +214,9 @@ class SimpleCLIApp {
|
|
|
214
214
|
if (isHelpFlag(argsBeforePassthrough[0])) {
|
|
215
215
|
return [];
|
|
216
216
|
}
|
|
217
|
-
const helpFlagIndex = argsBeforePassthrough.findIndex(
|
|
217
|
+
const helpFlagIndex = argsBeforePassthrough.findIndex(
|
|
218
|
+
(arg) => isHelpFlag(arg)
|
|
219
|
+
);
|
|
218
220
|
if (helpFlagIndex >= 0) {
|
|
219
221
|
return argsBeforePassthrough.slice(0, helpFlagIndex);
|
|
220
222
|
}
|
|
@@ -229,7 +231,10 @@ class SimpleCLIApp {
|
|
|
229
231
|
}
|
|
230
232
|
throw new Error(`Unknown command: ${args.join(" ")}`);
|
|
231
233
|
}
|
|
232
|
-
const rawInput = this.parseCommandInput(
|
|
234
|
+
const rawInput = this.parseCommandInput(
|
|
235
|
+
command2,
|
|
236
|
+
args.slice(command2.path.length)
|
|
237
|
+
);
|
|
233
238
|
return {
|
|
234
239
|
routeKey: command2.routeKey,
|
|
235
240
|
rawInput
|
|
@@ -239,7 +244,9 @@ class SimpleCLIApp {
|
|
|
239
244
|
const inputDefinition = command2.input?.getDefinition();
|
|
240
245
|
if (!inputDefinition) {
|
|
241
246
|
if (args.length > 0) {
|
|
242
|
-
throw new Error(
|
|
247
|
+
throw new Error(
|
|
248
|
+
`Unexpected arguments for ${this.name} ${command2.path.join(" ")}.`
|
|
249
|
+
);
|
|
243
250
|
}
|
|
244
251
|
return {
|
|
245
252
|
positionals: [],
|
|
@@ -256,7 +263,9 @@ class SimpleCLIApp {
|
|
|
256
263
|
const arg = args[index];
|
|
257
264
|
if (arg === "--") {
|
|
258
265
|
if (!passthroughEntry) {
|
|
259
|
-
throw new Error(
|
|
266
|
+
throw new Error(
|
|
267
|
+
`Unexpected "--" for ${this.name} ${command2.path.join(" ")}.`
|
|
268
|
+
);
|
|
260
269
|
}
|
|
261
270
|
named["--"] = args.slice(index + 1);
|
|
262
271
|
break;
|
|
@@ -305,7 +314,11 @@ class SimpleCLIApp {
|
|
|
305
314
|
}
|
|
306
315
|
positionals.push(arg);
|
|
307
316
|
}
|
|
308
|
-
validateParsedPositionals(
|
|
317
|
+
validateParsedPositionals(
|
|
318
|
+
command2,
|
|
319
|
+
inputDefinition.positionals,
|
|
320
|
+
positionals
|
|
321
|
+
);
|
|
309
322
|
validateRequiredNamedArgs(inputDefinition.named, named);
|
|
310
323
|
return {
|
|
311
324
|
positionals,
|
|
@@ -506,10 +519,7 @@ class SimpleCLIApp {
|
|
|
506
519
|
function splitNamedArg(arg) {
|
|
507
520
|
const separatorIndex = arg.indexOf("=");
|
|
508
521
|
if (separatorIndex < 0) return [arg, void 0];
|
|
509
|
-
return [
|
|
510
|
-
arg.slice(0, separatorIndex),
|
|
511
|
-
arg.slice(separatorIndex + 1)
|
|
512
|
-
];
|
|
522
|
+
return [arg.slice(0, separatorIndex), arg.slice(separatorIndex + 1)];
|
|
513
523
|
}
|
|
514
524
|
function readNamedArgValue(args, index, rawName, displayName, spec, inlineValue, namedSpecs) {
|
|
515
525
|
if (spec.kind === "flag") {
|
|
@@ -549,22 +559,29 @@ function buildNamedArgLookup(namedDefinition) {
|
|
|
549
559
|
return lookup;
|
|
550
560
|
}
|
|
551
561
|
function validateParsedPositionals(command2, definitions, positionals) {
|
|
552
|
-
const variadicDefinition = definitions.find(
|
|
562
|
+
const variadicDefinition = definitions.find(
|
|
563
|
+
(definition) => definition.variadic
|
|
564
|
+
);
|
|
553
565
|
if (!variadicDefinition && positionals.length > definitions.length) {
|
|
554
566
|
throw new Error(`Unexpected arguments for ${command2.path.join(" ")}.`);
|
|
555
567
|
}
|
|
556
568
|
definitions.forEach((definition, index) => {
|
|
557
569
|
const value = definition.variadic ? positionals.slice(index) : positionals[index];
|
|
558
|
-
if (value !== void 0 && (!Array.isArray(value) || value.length > 0))
|
|
570
|
+
if (value !== void 0 && (!Array.isArray(value) || value.length > 0))
|
|
571
|
+
return;
|
|
559
572
|
if (schemaAcceptsUndefined(definition.schema)) return;
|
|
560
573
|
throw new Error(`Missing required argument <${definition.key}>.`);
|
|
561
574
|
});
|
|
562
575
|
}
|
|
563
576
|
function validateInputDefinition(definition) {
|
|
564
|
-
const variadicIndex = definition.positionals.findIndex(
|
|
577
|
+
const variadicIndex = definition.positionals.findIndex(
|
|
578
|
+
(positional2) => positional2.variadic
|
|
579
|
+
);
|
|
565
580
|
if (variadicIndex < 0) return;
|
|
566
581
|
if (variadicIndex !== definition.positionals.length - 1) {
|
|
567
|
-
throw new Error(
|
|
582
|
+
throw new Error(
|
|
583
|
+
"Variadic positional arguments must be the last positional."
|
|
584
|
+
);
|
|
568
585
|
}
|
|
569
586
|
}
|
|
570
587
|
function validateRequiredNamedArgs(definitions, named) {
|
|
@@ -596,11 +613,10 @@ function resolveRouteTree(routes, parentPath = [], parentMiddlewares = []) {
|
|
|
596
613
|
kind: "group",
|
|
597
614
|
path: groupPath
|
|
598
615
|
});
|
|
599
|
-
const nested = resolveRouteTree(
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
);
|
|
616
|
+
const nested = resolveRouteTree(routeValue.routes, groupPath, [
|
|
617
|
+
...parentMiddlewares,
|
|
618
|
+
...routeValue.middlewares
|
|
619
|
+
]);
|
|
604
620
|
resolved.commands.push(...nested.commands);
|
|
605
621
|
resolved.groups.push(...nested.groups);
|
|
606
622
|
resolved.routeEntries.push(...nested.routeEntries);
|
|
@@ -608,7 +624,9 @@ function resolveRouteTree(routes, parentPath = [], parentMiddlewares = []) {
|
|
|
608
624
|
}
|
|
609
625
|
const command2 = routeValue.getDefinition();
|
|
610
626
|
if (!command2.handler) {
|
|
611
|
-
throw new Error(
|
|
627
|
+
throw new Error(
|
|
628
|
+
`Command "${[...parentPath, token].join(" ")}" is missing a handler.`
|
|
629
|
+
);
|
|
612
630
|
}
|
|
613
631
|
const path = [...parentPath, token];
|
|
614
632
|
resolved.commands.push({
|
|
@@ -616,7 +634,10 @@ function resolveRouteTree(routes, parentPath = [], parentMiddlewares = []) {
|
|
|
616
634
|
path,
|
|
617
635
|
description: command2.config.description,
|
|
618
636
|
input: command2.input,
|
|
619
|
-
middlewares: mergeInheritedMiddlewares(
|
|
637
|
+
middlewares: mergeInheritedMiddlewares(
|
|
638
|
+
parentMiddlewares,
|
|
639
|
+
command2.middlewares
|
|
640
|
+
),
|
|
620
641
|
handler: command2.handler
|
|
621
642
|
});
|
|
622
643
|
resolved.routeEntries.push({
|
|
@@ -630,7 +651,9 @@ function mergeInheritedMiddlewares(parentMiddlewares, commandMiddlewares) {
|
|
|
630
651
|
if (parentMiddlewares.length === 0) {
|
|
631
652
|
return [...commandMiddlewares];
|
|
632
653
|
}
|
|
633
|
-
if (commandMiddlewares.length >= parentMiddlewares.length && parentMiddlewares.every(
|
|
654
|
+
if (commandMiddlewares.length >= parentMiddlewares.length && parentMiddlewares.every(
|
|
655
|
+
(middleware, index) => commandMiddlewares[index] === middleware
|
|
656
|
+
)) {
|
|
634
657
|
return [...commandMiddlewares];
|
|
635
658
|
}
|
|
636
659
|
return [...parentMiddlewares, ...commandMiddlewares];
|
|
@@ -653,10 +676,7 @@ function buildInputNormalizer(definition) {
|
|
|
653
676
|
spec.name ? toCamelCase(spec.name) : "",
|
|
654
677
|
...(spec.aliases ?? []).flatMap((alias) => {
|
|
655
678
|
const normalizedAlias = normalizeNamedArgToken(alias);
|
|
656
|
-
return [
|
|
657
|
-
normalizedAlias,
|
|
658
|
-
toCamelCase(normalizedAlias)
|
|
659
|
-
];
|
|
679
|
+
return [normalizedAlias, toCamelCase(normalizedAlias)];
|
|
660
680
|
}),
|
|
661
681
|
toKebabCase(key),
|
|
662
682
|
key
|
package/dist/cli/router.js
CHANGED
|
@@ -1,29 +1,22 @@
|
|
|
1
1
|
import { aiCommands } from "./commands/ai.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { browserCommands } from "./commands/browser.js";
|
|
3
|
+
import { executionCommands } from "./commands/execution.js";
|
|
4
4
|
import { initCommand } from "./commands/init.js";
|
|
5
5
|
import { logCommands } from "./commands/logs.js";
|
|
6
|
-
import {
|
|
7
|
-
import { createSnapshotCommand } from "./commands/snapshot.js";
|
|
6
|
+
import { snapshotCommand } from "./commands/snapshot.js";
|
|
8
7
|
import { SimpleCLI } from "./framework/simple-cli.js";
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
function createCLIApp(logger) {
|
|
20
|
-
return SimpleCLI.define("libretto-cli", buildCLIRoutes(logger), {
|
|
21
|
-
globalNamed: {
|
|
22
|
-
session: sessionOption()
|
|
23
|
-
}
|
|
24
|
-
});
|
|
8
|
+
const cliRoutes = {
|
|
9
|
+
...browserCommands,
|
|
10
|
+
...executionCommands,
|
|
11
|
+
...logCommands,
|
|
12
|
+
ai: aiCommands,
|
|
13
|
+
init: initCommand,
|
|
14
|
+
snapshot: snapshotCommand
|
|
15
|
+
};
|
|
16
|
+
function createCLIApp() {
|
|
17
|
+
return SimpleCLI.define("libretto", cliRoutes);
|
|
25
18
|
}
|
|
26
19
|
export {
|
|
27
|
-
|
|
20
|
+
cliRoutes,
|
|
28
21
|
createCLIApp
|
|
29
22
|
};
|
|
@@ -4,6 +4,7 @@ import { cwd } from "node:process";
|
|
|
4
4
|
import { isAbsolute, resolve } from "node:path";
|
|
5
5
|
import { pathToFileURL } from "node:url";
|
|
6
6
|
import {
|
|
7
|
+
instrumentContext,
|
|
7
8
|
launchBrowser
|
|
8
9
|
} from "../../index.js";
|
|
9
10
|
import { parseSessionStateContent } from "../../shared/state/index.js";
|
|
@@ -13,7 +14,10 @@ import {
|
|
|
13
14
|
getSessionNetworkLogPath,
|
|
14
15
|
getSessionStatePath
|
|
15
16
|
} from "../core/context.js";
|
|
16
|
-
import {
|
|
17
|
+
import {
|
|
18
|
+
getPauseSignalPaths,
|
|
19
|
+
removeSignalIfExists
|
|
20
|
+
} from "../core/pause-signals.js";
|
|
17
21
|
import { installSessionTelemetry } from "../core/session-telemetry.js";
|
|
18
22
|
const LIBRETTO_WORKFLOW_BRAND = /* @__PURE__ */ Symbol.for("libretto.workflow");
|
|
19
23
|
const FAILURE_HOLD_POLL_INTERVAL_MS = 250;
|
|
@@ -40,7 +44,7 @@ function readSessionStatePid(session) {
|
|
|
40
44
|
const statePath = getSessionStatePath(session);
|
|
41
45
|
if (!existsSync(statePath)) return null;
|
|
42
46
|
try {
|
|
43
|
-
return parseSessionStateContent(readFileSync(statePath, "utf8"), statePath).pid;
|
|
47
|
+
return parseSessionStateContent(readFileSync(statePath, "utf8"), statePath).pid ?? null;
|
|
44
48
|
} catch {
|
|
45
49
|
return null;
|
|
46
50
|
}
|
|
@@ -66,7 +70,7 @@ async function waitForFailureSessionRelease(args) {
|
|
|
66
70
|
function isLoadedLibrettoWorkflow(value) {
|
|
67
71
|
if (!value || typeof value !== "object") return false;
|
|
68
72
|
const candidate = value;
|
|
69
|
-
return candidate[LIBRETTO_WORKFLOW_BRAND] === true && typeof candidate.run === "function"
|
|
73
|
+
return candidate[LIBRETTO_WORKFLOW_BRAND] === true && typeof candidate.run === "function";
|
|
70
74
|
}
|
|
71
75
|
function resolveLocalAuthProfilePath(domain) {
|
|
72
76
|
return getProfilePath(normalizeDomain(domain));
|
|
@@ -77,9 +81,9 @@ function getMissingLocalAuthProfileError(args) {
|
|
|
77
81
|
`Local auth profile not found for domain "${normalizedDomain}".`,
|
|
78
82
|
`Expected profile file: ${args.profilePath}`,
|
|
79
83
|
"To create it:",
|
|
80
|
-
` 1. libretto
|
|
84
|
+
` 1. libretto open https://${normalizedDomain} --headed --session ${args.session}`,
|
|
81
85
|
" 2. Log in manually in the browser window.",
|
|
82
|
-
` 3. libretto
|
|
86
|
+
` 3. libretto save ${normalizedDomain} --session ${args.session}`
|
|
83
87
|
].join("\n");
|
|
84
88
|
}
|
|
85
89
|
function getAbsoluteIntegrationPath(integrationPath) {
|
|
@@ -119,8 +123,8 @@ ${TSCONFIG_HINT}` : "";
|
|
|
119
123
|
' import { workflow } from "libretto";',
|
|
120
124
|
"",
|
|
121
125
|
` export const ${exportName} = workflow<InputType, OutputType>(`,
|
|
122
|
-
" {},",
|
|
123
126
|
" async (ctx, input) => {",
|
|
127
|
+
" // ctx.session \u2014 libretto session name",
|
|
124
128
|
" // ctx.page \u2014 Playwright Page instance",
|
|
125
129
|
" // ctx.logger \u2014 MinimalLogger",
|
|
126
130
|
" // ctx.services \u2014 injected dependencies (generic, default {})",
|
|
@@ -133,6 +137,12 @@ ${TSCONFIG_HINT}` : "";
|
|
|
133
137
|
}
|
|
134
138
|
return targetExport;
|
|
135
139
|
}
|
|
140
|
+
async function installHeadedWorkflowVisualization(args) {
|
|
141
|
+
await (args.instrument ?? instrumentContext)(args.context, {
|
|
142
|
+
visualize: true,
|
|
143
|
+
logger: args.logger
|
|
144
|
+
});
|
|
145
|
+
}
|
|
136
146
|
async function runIntegrationInternal(args, options) {
|
|
137
147
|
const { logger } = options;
|
|
138
148
|
const absolutePath = getAbsoluteIntegrationPath(args.integrationPath);
|
|
@@ -165,8 +175,15 @@ async function runIntegrationInternal(args, options) {
|
|
|
165
175
|
const browserSession = await launchBrowser({
|
|
166
176
|
sessionName: args.session,
|
|
167
177
|
headless: args.headless,
|
|
168
|
-
storageStatePath
|
|
178
|
+
storageStatePath,
|
|
179
|
+
viewport: args.viewport
|
|
169
180
|
});
|
|
181
|
+
if (!args.headless && args.visualize !== false) {
|
|
182
|
+
await installHeadedWorkflowVisualization({
|
|
183
|
+
context: browserSession.context,
|
|
184
|
+
logger: integrationLogger
|
|
185
|
+
});
|
|
186
|
+
}
|
|
170
187
|
const actionsLogPath = getSessionActionsLogPath(args.session);
|
|
171
188
|
const networkLogPath = getSessionNetworkLogPath(args.session);
|
|
172
189
|
await installSessionTelemetry({
|
|
@@ -181,6 +198,7 @@ async function runIntegrationInternal(args, options) {
|
|
|
181
198
|
}
|
|
182
199
|
});
|
|
183
200
|
const workflowContext = {
|
|
201
|
+
session: args.session,
|
|
184
202
|
logger: integrationLogger,
|
|
185
203
|
page: browserSession.page,
|
|
186
204
|
services: {}
|
|
@@ -228,5 +246,6 @@ async function runIntegrationFromFileInWorker(args, logger) {
|
|
|
228
246
|
});
|
|
229
247
|
}
|
|
230
248
|
export {
|
|
249
|
+
installHeadedWorkflowVisualization,
|
|
231
250
|
runIntegrationFromFileInWorker
|
|
232
251
|
};
|
|
@@ -5,7 +5,9 @@ const RunIntegrationWorkerRequestSchema = z.object({
|
|
|
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
|
+
authProfileDomain: z.string().optional(),
|
|
10
|
+
viewport: z.object({ width: z.number(), height: z.number() }).optional()
|
|
9
11
|
});
|
|
10
12
|
export {
|
|
11
13
|
RunIntegrationWorkerRequestSchema
|
|
@@ -4,10 +4,7 @@ import {
|
|
|
4
4
|
RunIntegrationWorkerRequestSchema
|
|
5
5
|
} from "./run-integration-worker-protocol.js";
|
|
6
6
|
import { runIntegrationFromFileInWorker } from "./run-integration-runtime.js";
|
|
7
|
-
import {
|
|
8
|
-
ensureLibrettoSetup,
|
|
9
|
-
withSessionLogger
|
|
10
|
-
} from "../core/context.js";
|
|
7
|
+
import { ensureLibrettoSetup, withSessionLogger } from "../core/context.js";
|
|
11
8
|
import { getPauseSignalPaths } from "../core/pause-signals.js";
|
|
12
9
|
function parseWorkerRequest(argv) {
|
|
13
10
|
const rawPayload = argv[2];
|
package/dist/index.d.ts
CHANGED
|
@@ -10,12 +10,11 @@ export { ExtractOptions, extractFromPage } from './runtime/extract/extract.js';
|
|
|
10
10
|
export { PageRequestOptions, RequestConfig, pageRequest } from './runtime/network/network.js';
|
|
11
11
|
export { DownloadResult, DownloadViaClickOptions, SaveDownloadOptions, downloadAndSave, downloadViaClick } from './runtime/download/download.js';
|
|
12
12
|
export { pause } from './shared/debug/pause.js';
|
|
13
|
-
export { isDebugMode, isDryRun, shouldPauseBeforeMutation } from './shared/config/config.js';
|
|
14
13
|
export { InstrumentationOptions, InstrumentedPage, installInstrumentation, instrumentContext, instrumentPage } from './shared/instrumentation/instrument.js';
|
|
15
14
|
export { GhostCursorOptions, ensureGhostCursor, ghostClick, hideGhostCursor, moveGhostCursor } from './shared/visualization/ghost-cursor.js';
|
|
16
15
|
export { HighlightOptions, clearHighlights, ensureHighlightLayer, showHighlight } from './shared/visualization/highlight.js';
|
|
17
16
|
export { BrowserSession, LaunchBrowserArgs, launchBrowser } from './shared/run/browser.js';
|
|
18
|
-
export { LIBRETTO_WORKFLOW_BRAND, LibrettoWorkflow, LibrettoWorkflowContext, LibrettoWorkflowHandler,
|
|
17
|
+
export { LIBRETTO_WORKFLOW_BRAND, LibrettoWorkflow, LibrettoWorkflowContext, LibrettoWorkflowHandler, workflow } from './shared/workflow/workflow.js';
|
|
19
18
|
import 'zod';
|
|
20
19
|
import 'ai';
|
|
21
20
|
import 'playwright';
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { resolve } from "node:path";
|
|
2
2
|
import { pathToFileURL } from "node:url";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
Logger,
|
|
5
|
+
defaultLogger
|
|
6
|
+
} from "./shared/logger/logger.js";
|
|
4
7
|
import {
|
|
5
8
|
createFileLogSink,
|
|
6
9
|
prettyConsoleSink,
|
|
@@ -20,7 +23,9 @@ import { attemptWithRecovery } from "./runtime/recovery/recovery.js";
|
|
|
20
23
|
import {
|
|
21
24
|
detectSubmissionError
|
|
22
25
|
} from "./runtime/recovery/errors.js";
|
|
23
|
-
import {
|
|
26
|
+
import {
|
|
27
|
+
extractFromPage
|
|
28
|
+
} from "./runtime/extract/extract.js";
|
|
24
29
|
import {
|
|
25
30
|
pageRequest
|
|
26
31
|
} from "./runtime/network/network.js";
|
|
@@ -29,11 +34,6 @@ import {
|
|
|
29
34
|
downloadAndSave
|
|
30
35
|
} from "./runtime/download/download.js";
|
|
31
36
|
import { pause } from "./shared/debug/pause.js";
|
|
32
|
-
import {
|
|
33
|
-
isDebugMode,
|
|
34
|
-
isDryRun,
|
|
35
|
-
shouldPauseBeforeMutation
|
|
36
|
-
} from "./shared/config/config.js";
|
|
37
37
|
import {
|
|
38
38
|
instrumentPage,
|
|
39
39
|
installInstrumentation,
|
|
@@ -97,8 +97,6 @@ export {
|
|
|
97
97
|
installInstrumentation,
|
|
98
98
|
instrumentContext,
|
|
99
99
|
instrumentPage,
|
|
100
|
-
isDebugMode,
|
|
101
|
-
isDryRun,
|
|
102
100
|
jsonlConsoleSink,
|
|
103
101
|
launchBrowser,
|
|
104
102
|
moveGhostCursor,
|
|
@@ -108,7 +106,6 @@ export {
|
|
|
108
106
|
pause,
|
|
109
107
|
prettyConsoleSink,
|
|
110
108
|
serializeSessionState,
|
|
111
|
-
shouldPauseBeforeMutation,
|
|
112
109
|
showHighlight,
|
|
113
110
|
workflow
|
|
114
111
|
};
|
|
@@ -29,7 +29,11 @@ async function downloadViaClick(page, selector, options) {
|
|
|
29
29
|
}
|
|
30
30
|
async function downloadAndSave(page, selector, options) {
|
|
31
31
|
const { savePath, ...downloadOpts } = options ?? {};
|
|
32
|
-
const { buffer, filename } = await downloadViaClick(
|
|
32
|
+
const { buffer, filename } = await downloadViaClick(
|
|
33
|
+
page,
|
|
34
|
+
selector,
|
|
35
|
+
downloadOpts
|
|
36
|
+
);
|
|
33
37
|
const dest = resolve(savePath ?? filename);
|
|
34
38
|
await writeFile(dest, buffer);
|
|
35
39
|
options?.logger?.info("download:saved", {
|
|
@@ -1,6 +1,15 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
defaultLogger
|
|
3
|
+
} from "../../shared/logger/logger.js";
|
|
2
4
|
async function extractFromPage(options) {
|
|
3
|
-
const {
|
|
5
|
+
const {
|
|
6
|
+
page,
|
|
7
|
+
instruction,
|
|
8
|
+
schema,
|
|
9
|
+
selector,
|
|
10
|
+
logger = defaultLogger,
|
|
11
|
+
llmClient
|
|
12
|
+
} = options;
|
|
4
13
|
let screenshot;
|
|
5
14
|
let domContent;
|
|
6
15
|
if (selector) {
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
async function pageRequest(page, config, options) {
|
|
2
|
-
const {
|
|
2
|
+
const {
|
|
3
|
+
url,
|
|
4
|
+
method = "GET",
|
|
5
|
+
headers = {},
|
|
6
|
+
body,
|
|
7
|
+
bodyType = "json",
|
|
8
|
+
responseType = "json"
|
|
9
|
+
} = config;
|
|
3
10
|
const { logger, schema } = options ?? {};
|
|
4
11
|
const startTime = Date.now();
|
|
5
12
|
const fetchHeaders = { ...headers };
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
defaultLogger
|
|
3
|
+
} from "../../shared/logger/logger.js";
|
|
2
4
|
function delay(ms) {
|
|
3
5
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
4
6
|
}
|
|
@@ -86,7 +88,9 @@ async function executeBrowserAction(page, action, logger = defaultLogger) {
|
|
|
86
88
|
if (point) await page.mouse.move(point.x, point.y);
|
|
87
89
|
}
|
|
88
90
|
await page.mouse.up();
|
|
89
|
-
logger.info(
|
|
91
|
+
logger.info(
|
|
92
|
+
`Dragged from (${start.x}, ${start.y}) to (${end.x}, ${end.y})`
|
|
93
|
+
);
|
|
90
94
|
}
|
|
91
95
|
break;
|
|
92
96
|
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
defaultLogger
|
|
3
|
+
} from "../../shared/logger/logger.js";
|
|
2
4
|
import { z } from "zod";
|
|
3
5
|
const detectSubmissionErrorSchema = z.object({
|
|
4
6
|
hasError: z.boolean().describe("Whether an error is visible on the page"),
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
defaultLogger
|
|
3
|
+
} from "../../shared/logger/logger.js";
|
|
2
4
|
import { executeRecoveryAgent } from "./agent.js";
|
|
3
5
|
async function attemptWithRecovery(page, fn, logger, llmClient) {
|
|
4
6
|
const log = logger ?? defaultLogger;
|
|
@@ -28,12 +28,7 @@ const STATE_ATTRS = /* @__PURE__ */ new Set([
|
|
|
28
28
|
"open",
|
|
29
29
|
"multiple"
|
|
30
30
|
]);
|
|
31
|
-
const BOOLEAN_ATTRS = /* @__PURE__ */ new Set([
|
|
32
|
-
...STATE_ATTRS,
|
|
33
|
-
"async",
|
|
34
|
-
"defer",
|
|
35
|
-
"nomodule"
|
|
36
|
-
]);
|
|
31
|
+
const BOOLEAN_ATTRS = /* @__PURE__ */ new Set([...STATE_ATTRS, "async", "defer", "nomodule"]);
|
|
37
32
|
const EMPTY_VALUE_DROP_ATTRS = /* @__PURE__ */ new Set([
|
|
38
33
|
"alt",
|
|
39
34
|
"autocomplete",
|
|
@@ -170,12 +165,8 @@ function condenseDom(html) {
|
|
|
170
165
|
}
|
|
171
166
|
const hasAriaLabel = /aria-label\s*=/i.test(attrs);
|
|
172
167
|
if (!hasAriaLabel) {
|
|
173
|
-
const titleMatch = inner.match(
|
|
174
|
-
|
|
175
|
-
);
|
|
176
|
-
const descMatch = inner.match(
|
|
177
|
-
/<desc[^>]*>([^<]+)<\/desc>/i
|
|
178
|
-
);
|
|
168
|
+
const titleMatch = inner.match(/<title[^>]*>([^<]+)<\/title>/i);
|
|
169
|
+
const descMatch = inner.match(/<desc[^>]*>([^<]+)<\/desc>/i);
|
|
179
170
|
const labelText = titleMatch?.[1]?.trim() || descMatch?.[1]?.trim();
|
|
180
171
|
if (labelText) {
|
|
181
172
|
keepAttrs.push(
|
|
@@ -404,7 +395,9 @@ function shouldKeepCustomDataAttribute(tagName, attrName, value, interactive) {
|
|
|
404
395
|
function looksMeaningfulToken(value) {
|
|
405
396
|
if (!/^[a-z][a-z0-9-]{1,40}$/i.test(value)) return false;
|
|
406
397
|
if (!/[a-z]{3}/i.test(value)) return false;
|
|
407
|
-
if (/(track|metric|telemetry|analytics|component|display|loaded|token|dps|color|screen|strict|rehydr|fetch)/i.test(
|
|
398
|
+
if (/(track|metric|telemetry|analytics|component|display|loaded|token|dps|color|screen|strict|rehydr|fetch)/i.test(
|
|
399
|
+
value
|
|
400
|
+
)) {
|
|
408
401
|
return false;
|
|
409
402
|
}
|
|
410
403
|
return true;
|