libretto 0.6.11 → 0.6.12
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 +4 -0
- package/README.template.md +4 -0
- package/dist/cli/cli.js +4 -3
- package/dist/cli/commands/ai.js +3 -2
- package/dist/cli/commands/browser.js +17 -17
- package/dist/cli/commands/execution.js +254 -234
- package/dist/cli/commands/experiments.js +100 -0
- package/dist/cli/commands/setup.js +20 -34
- package/dist/cli/commands/shared.js +10 -0
- package/dist/cli/commands/snapshot.js +81 -9
- package/dist/cli/commands/status.js +5 -4
- package/dist/cli/core/ai-model.js +6 -3
- package/dist/cli/core/browser.js +300 -121
- package/dist/cli/core/config.js +4 -2
- package/dist/cli/core/context.js +4 -0
- package/dist/cli/core/daemon/config.js +0 -6
- package/dist/cli/core/daemon/daemon.js +535 -89
- package/dist/cli/core/daemon/ipc.js +170 -129
- package/dist/cli/core/daemon/snapshot.js +72 -6
- package/dist/cli/core/experiments.js +66 -0
- package/dist/cli/core/session.js +5 -4
- package/dist/cli/core/skill-version.js +2 -1
- package/dist/cli/core/snapshot-analyzer.js +4 -3
- package/dist/cli/core/workflow-runner/runner.js +147 -0
- package/dist/cli/core/workflow-runtime.js +60 -0
- package/dist/cli/router.js +4 -1
- package/dist/shared/debug/pause-handler.d.ts +9 -0
- package/dist/shared/debug/pause-handler.js +15 -0
- package/dist/shared/debug/pause.d.ts +1 -2
- package/dist/shared/debug/pause.js +13 -36
- package/dist/shared/ipc/child-process-transport.d.ts +7 -0
- package/dist/shared/ipc/child-process-transport.js +60 -0
- package/dist/shared/ipc/child-process-transport.spec.d.ts +2 -0
- package/dist/shared/ipc/child-process-transport.spec.js +68 -0
- package/dist/shared/ipc/ipc.d.ts +46 -0
- package/dist/shared/ipc/ipc.js +165 -0
- package/dist/shared/ipc/ipc.spec.d.ts +2 -0
- package/dist/shared/ipc/ipc.spec.js +114 -0
- package/dist/shared/ipc/socket-transport.d.ts +9 -0
- package/dist/shared/ipc/socket-transport.js +143 -0
- package/dist/shared/ipc/socket-transport.spec.d.ts +2 -0
- package/dist/shared/ipc/socket-transport.spec.js +117 -0
- package/dist/shared/package-manager.d.ts +7 -0
- package/dist/shared/package-manager.js +60 -0
- package/dist/shared/paths/paths.d.ts +1 -8
- package/dist/shared/paths/paths.js +1 -49
- package/dist/shared/snapshot/capture-snapshot.d.ts +9 -0
- package/dist/shared/snapshot/capture-snapshot.js +463 -0
- package/dist/shared/snapshot/diff-snapshots.d.ts +72 -0
- package/dist/shared/snapshot/diff-snapshots.js +358 -0
- package/dist/shared/snapshot/render-snapshot.d.ts +39 -0
- package/dist/shared/snapshot/render-snapshot.js +651 -0
- package/dist/shared/snapshot/snapshot.spec.d.ts +2 -0
- package/dist/shared/snapshot/snapshot.spec.js +333 -0
- package/dist/shared/snapshot/types.d.ts +40 -0
- package/dist/shared/snapshot/types.js +0 -0
- package/dist/shared/snapshot/wait-for-page-stable.d.ts +17 -0
- package/dist/shared/snapshot/wait-for-page-stable.js +281 -0
- package/dist/shared/state/session-state.d.ts +1 -0
- package/dist/shared/state/session-state.js +1 -0
- package/docs/experiments.md +67 -0
- package/package.json +4 -2
- package/skills/libretto/SKILL.md +3 -1
- package/skills/libretto-readonly/SKILL.md +1 -1
- package/src/cli/AGENTS.md +7 -0
- package/src/cli/cli.ts +4 -3
- package/src/cli/commands/ai.ts +3 -2
- package/src/cli/commands/browser.ts +13 -11
- package/src/cli/commands/execution.ts +303 -271
- package/src/cli/commands/experiments.ts +120 -0
- package/src/cli/commands/setup.ts +18 -36
- package/src/cli/commands/shared.ts +20 -0
- package/src/cli/commands/snapshot.ts +99 -11
- package/src/cli/commands/status.ts +5 -4
- package/src/cli/core/ai-model.ts +6 -3
- package/src/cli/core/browser.ts +369 -147
- package/src/cli/core/config.ts +3 -1
- package/src/cli/core/context.ts +4 -0
- package/src/cli/core/daemon/config.ts +35 -19
- package/src/cli/core/daemon/daemon.ts +686 -106
- package/src/cli/core/daemon/ipc.ts +330 -214
- package/src/cli/core/daemon/snapshot.ts +106 -8
- package/src/cli/core/experiments.ts +85 -0
- package/src/cli/core/session.ts +5 -4
- package/src/cli/core/skill-version.ts +2 -1
- package/src/cli/core/snapshot-analyzer.ts +4 -3
- package/src/cli/core/workflow-runner/runner.ts +237 -0
- package/src/cli/core/workflow-runtime.ts +85 -0
- package/src/cli/router.ts +4 -1
- package/src/shared/debug/pause-handler.ts +20 -0
- package/src/shared/debug/pause.ts +14 -48
- package/src/shared/ipc/AGENTS.md +24 -0
- package/src/shared/ipc/child-process-transport.spec.ts +86 -0
- package/src/shared/ipc/child-process-transport.ts +96 -0
- package/src/shared/ipc/ipc.spec.ts +161 -0
- package/src/shared/ipc/ipc.ts +288 -0
- package/src/shared/ipc/socket-transport.spec.ts +141 -0
- package/src/shared/ipc/socket-transport.ts +189 -0
- package/src/shared/package-manager.ts +76 -0
- package/src/shared/paths/paths.ts +0 -72
- package/src/shared/snapshot/capture-snapshot.ts +615 -0
- package/src/shared/snapshot/diff-snapshots.ts +579 -0
- package/src/shared/snapshot/render-snapshot.ts +962 -0
- package/src/shared/snapshot/snapshot.spec.ts +388 -0
- package/src/shared/snapshot/types.ts +43 -0
- package/src/shared/snapshot/wait-for-page-stable.ts +425 -0
- package/src/shared/state/session-state.ts +1 -0
- package/dist/cli/core/daemon/index.js +0 -16
- package/dist/cli/core/daemon/spawn.js +0 -90
- package/dist/cli/core/pause-signals.js +0 -29
- package/dist/cli/workers/run-integration-runtime.js +0 -235
- package/dist/cli/workers/run-integration-worker-protocol.js +0 -17
- package/dist/cli/workers/run-integration-worker.js +0 -64
- package/src/cli/core/daemon/index.ts +0 -24
- package/src/cli/core/daemon/spawn.ts +0 -171
- package/src/cli/core/pause-signals.ts +0 -35
- package/src/cli/workers/run-integration-runtime.ts +0 -326
- package/src/cli/workers/run-integration-worker-protocol.ts +0 -19
- package/src/cli/workers/run-integration-worker.ts +0 -72
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { librettoCommand } from "../../shared/package-manager.js";
|
|
3
|
+
import {
|
|
4
|
+
EXPERIMENTS,
|
|
5
|
+
isExperimentName,
|
|
6
|
+
resolveExperiments,
|
|
7
|
+
setExperimentEnabled
|
|
8
|
+
} from "../core/experiments.js";
|
|
9
|
+
import { SimpleCLI } from "../framework/simple-cli.js";
|
|
10
|
+
const experimentNames = Object.keys(EXPERIMENTS);
|
|
11
|
+
const experimentsUsage = [
|
|
12
|
+
"Usage:",
|
|
13
|
+
` ${librettoCommand("experiments")}`,
|
|
14
|
+
` ${librettoCommand("experiments describe <experiment>")}`,
|
|
15
|
+
` ${librettoCommand("experiments enable <experiment>")}`,
|
|
16
|
+
` ${librettoCommand("experiments disable <experiment>")}`
|
|
17
|
+
].join("\n");
|
|
18
|
+
const experimentsInput = SimpleCLI.input({
|
|
19
|
+
positionals: [
|
|
20
|
+
SimpleCLI.positional("action", z.string().optional(), {
|
|
21
|
+
help: "Action to apply"
|
|
22
|
+
}),
|
|
23
|
+
SimpleCLI.positional("experiment", z.string().optional(), {
|
|
24
|
+
help: "Experiment name"
|
|
25
|
+
})
|
|
26
|
+
],
|
|
27
|
+
named: {}
|
|
28
|
+
});
|
|
29
|
+
function formatAvailableExperiments() {
|
|
30
|
+
return [
|
|
31
|
+
"Available experiments:",
|
|
32
|
+
...experimentNames.map((name) => ` ${name}`)
|
|
33
|
+
].join("\n");
|
|
34
|
+
}
|
|
35
|
+
function experimentUsageError(message) {
|
|
36
|
+
return new Error(
|
|
37
|
+
[message, "", experimentsUsage, "", formatAvailableExperiments()].join(
|
|
38
|
+
"\n"
|
|
39
|
+
)
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
function printExperiments(experiments) {
|
|
43
|
+
console.log("Libretto experiments:");
|
|
44
|
+
for (const name of experimentNames) {
|
|
45
|
+
const metadata = EXPERIMENTS[name];
|
|
46
|
+
console.log(
|
|
47
|
+
`- ${name}: ${experiments[name] ? "enabled" : "disabled"} \u2014 ${metadata.title}`
|
|
48
|
+
);
|
|
49
|
+
console.log(` ${metadata.oneSentenceDescription}`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function printExperimentDescription(name, experiments) {
|
|
53
|
+
const metadata = EXPERIMENTS[name];
|
|
54
|
+
console.log(`${metadata.title} (${name})`);
|
|
55
|
+
console.log(`Status: ${experiments[name] ? "enabled" : "disabled"}`);
|
|
56
|
+
console.log("");
|
|
57
|
+
if (experiments[name]) {
|
|
58
|
+
console.log(
|
|
59
|
+
"Since this experiment is enabled, Libretto\u2019s expected usage deviates from the skill. Use these instructions where they differ:"
|
|
60
|
+
);
|
|
61
|
+
console.log("");
|
|
62
|
+
}
|
|
63
|
+
console.log(metadata.docs ?? metadata.oneSentenceDescription);
|
|
64
|
+
}
|
|
65
|
+
const experimentsCommand = SimpleCLI.command({
|
|
66
|
+
description: "List or update Libretto experiment flags"
|
|
67
|
+
}).input(experimentsInput).handle(async ({ input }) => {
|
|
68
|
+
if (!input.action) {
|
|
69
|
+
printExperiments(resolveExperiments());
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
if (input.action !== "describe" && input.action !== "enable" && input.action !== "disable") {
|
|
73
|
+
throw experimentUsageError(`Unknown experiments action "${input.action}".`);
|
|
74
|
+
}
|
|
75
|
+
if (!input.experiment) {
|
|
76
|
+
throw experimentUsageError(
|
|
77
|
+
`Missing experiment name for ${input.action}.`
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
if (!isExperimentName(input.experiment)) {
|
|
81
|
+
throw experimentUsageError(`Unknown experiment "${input.experiment}".`);
|
|
82
|
+
}
|
|
83
|
+
if (input.action === "describe") {
|
|
84
|
+
printExperimentDescription(input.experiment, resolveExperiments());
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
const experiments = setExperimentEnabled(
|
|
88
|
+
input.experiment,
|
|
89
|
+
input.action === "enable"
|
|
90
|
+
);
|
|
91
|
+
console.log(`Experiment "${input.experiment}" ${input.action}d.`);
|
|
92
|
+
if (input.action === "enable") {
|
|
93
|
+
console.log("");
|
|
94
|
+
printExperimentDescription(input.experiment, experiments);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
export {
|
|
98
|
+
experimentsCommand,
|
|
99
|
+
experimentsInput
|
|
100
|
+
};
|
|
@@ -1,10 +1,5 @@
|
|
|
1
1
|
import { createInterface } from "node:readline";
|
|
2
|
-
import {
|
|
3
|
-
cpSync,
|
|
4
|
-
existsSync,
|
|
5
|
-
readdirSync,
|
|
6
|
-
rmSync
|
|
7
|
-
} from "node:fs";
|
|
2
|
+
import { cpSync, existsSync, readdirSync, rmSync } from "node:fs";
|
|
8
3
|
import { spawnSync } from "node:child_process";
|
|
9
4
|
import { basename, dirname, join } from "node:path";
|
|
10
5
|
import { fileURLToPath } from "node:url";
|
|
@@ -18,6 +13,11 @@ import {
|
|
|
18
13
|
DEFAULT_SNAPSHOT_MODELS,
|
|
19
14
|
resolveAiSetupStatus
|
|
20
15
|
} from "../core/ai-model.js";
|
|
16
|
+
import {
|
|
17
|
+
detectProjectPackageManager,
|
|
18
|
+
installCommand,
|
|
19
|
+
librettoCommand
|
|
20
|
+
} from "../../shared/package-manager.js";
|
|
21
21
|
import { SimpleCLI } from "../framework/simple-cli.js";
|
|
22
22
|
const PROVIDER_SDK_PACKAGES = {
|
|
23
23
|
openai: "@ai-sdk/openai",
|
|
@@ -26,24 +26,6 @@ const PROVIDER_SDK_PACKAGES = {
|
|
|
26
26
|
vertex: "@ai-sdk/google-vertex",
|
|
27
27
|
openrouter: "@ai-sdk/openai"
|
|
28
28
|
};
|
|
29
|
-
function detectPackageManager() {
|
|
30
|
-
if (existsSync(join(REPO_ROOT, "pnpm-lock.yaml"))) return "pnpm";
|
|
31
|
-
if (existsSync(join(REPO_ROOT, "yarn.lock"))) return "yarn";
|
|
32
|
-
if (existsSync(join(REPO_ROOT, "bun.lockb"))) return "bun";
|
|
33
|
-
return "npm";
|
|
34
|
-
}
|
|
35
|
-
function installCommand(pkgManager) {
|
|
36
|
-
switch (pkgManager) {
|
|
37
|
-
case "yarn":
|
|
38
|
-
return "yarn add";
|
|
39
|
-
case "bun":
|
|
40
|
-
return "bun add";
|
|
41
|
-
case "pnpm":
|
|
42
|
-
return "pnpm add";
|
|
43
|
-
default:
|
|
44
|
-
return "npm install";
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
29
|
function isSdkInstalled(sdkPackage) {
|
|
48
30
|
try {
|
|
49
31
|
const result = spawnSync("node", ["-e", `require.resolve("${sdkPackage}")`], {
|
|
@@ -58,7 +40,7 @@ function isSdkInstalled(sdkPackage) {
|
|
|
58
40
|
function installSdkIfNeeded(provider) {
|
|
59
41
|
const sdkPackage = PROVIDER_SDK_PACKAGES[provider];
|
|
60
42
|
if (isSdkInstalled(sdkPackage)) return;
|
|
61
|
-
const pkgManager =
|
|
43
|
+
const pkgManager = detectProjectPackageManager();
|
|
62
44
|
const cmd = installCommand(pkgManager);
|
|
63
45
|
console.log(`
|
|
64
46
|
Installing ${sdkPackage}...`);
|
|
@@ -144,7 +126,7 @@ function printHealthySummary(status) {
|
|
|
144
126
|
console.log(`\u2713 Using ${providerLabel(status.provider)} (${status.model}).`);
|
|
145
127
|
}
|
|
146
128
|
console.log(
|
|
147
|
-
|
|
129
|
+
`To change: ${librettoCommand("ai configure openai | anthropic | gemini | vertex | openrouter")}`
|
|
148
130
|
);
|
|
149
131
|
}
|
|
150
132
|
function printInvalidAiConfigWarning(status) {
|
|
@@ -189,13 +171,15 @@ function printSnapshotApiStatus() {
|
|
|
189
171
|
console.log();
|
|
190
172
|
console.log(formatMissingCredentialsMessage(plan));
|
|
191
173
|
console.log(
|
|
192
|
-
` To fix: add ${plan.envVar} to .env, or run
|
|
174
|
+
` To fix: add ${plan.envVar} to .env, or run \`${librettoCommand("setup")}\` interactively to repair.`
|
|
193
175
|
);
|
|
194
176
|
return false;
|
|
195
177
|
}
|
|
196
178
|
if (plan.kind === "repair-invalid-config") {
|
|
197
179
|
printInvalidAiConfigWarning(status);
|
|
198
|
-
console.log(
|
|
180
|
+
console.log(
|
|
181
|
+
` Run \`${librettoCommand("setup")}\` interactively to reconfigure.`
|
|
182
|
+
);
|
|
199
183
|
return false;
|
|
200
184
|
}
|
|
201
185
|
console.log();
|
|
@@ -208,10 +192,10 @@ function printSnapshotApiStatus() {
|
|
|
208
192
|
" GOOGLE_CLOUD_PROJECT=... # plus application default credentials for Vertex"
|
|
209
193
|
);
|
|
210
194
|
console.log(
|
|
211
|
-
|
|
195
|
+
` Or run \`${librettoCommand("ai configure openai | anthropic | gemini | vertex | openrouter")}\` to set a specific model.`
|
|
212
196
|
);
|
|
213
197
|
console.log(
|
|
214
|
-
|
|
198
|
+
` Run \`${librettoCommand("setup")}\` interactively to set up credentials.`
|
|
215
199
|
);
|
|
216
200
|
return false;
|
|
217
201
|
}
|
|
@@ -246,14 +230,15 @@ Add ${selected.envVar} to your .env file:`);
|
|
|
246
230
|
}
|
|
247
231
|
function printSkipMessage() {
|
|
248
232
|
console.log(
|
|
249
|
-
|
|
233
|
+
`
|
|
234
|
+
Skipped. You can set up API credentials later by rerunning \`${librettoCommand("setup")}\`.`
|
|
250
235
|
);
|
|
251
236
|
console.log("Or add credentials directly to your .env file:");
|
|
252
237
|
console.log(" OPENAI_API_KEY=...");
|
|
253
238
|
console.log(" ANTHROPIC_API_KEY=...");
|
|
254
239
|
console.log(" GEMINI_API_KEY=...");
|
|
255
240
|
console.log(
|
|
256
|
-
|
|
241
|
+
` Or run \`${librettoCommand("ai configure openai | anthropic | gemini | vertex | openrouter")}\` to set a specific model.`
|
|
257
242
|
);
|
|
258
243
|
}
|
|
259
244
|
async function runInteractiveApiSetup() {
|
|
@@ -341,7 +326,7 @@ function copySkills() {
|
|
|
341
326
|
"\n\u26A0\uFE0F No .agents/ or .claude/ directory found. Libretto skills were not installed."
|
|
342
327
|
);
|
|
343
328
|
console.log(
|
|
344
|
-
|
|
329
|
+
` Create one of these directories in your repo root and rerun \`${librettoCommand("setup")}\` to install skills:`
|
|
345
330
|
);
|
|
346
331
|
console.log(` mkdir ${join(REPO_ROOT, ".claude")}`);
|
|
347
332
|
return;
|
|
@@ -395,7 +380,8 @@ const setupCommand = SimpleCLI.command({
|
|
|
395
380
|
const ready = printSnapshotApiStatus();
|
|
396
381
|
if (!ready) {
|
|
397
382
|
console.log(
|
|
398
|
-
|
|
383
|
+
`
|
|
384
|
+
If you're an agent, request the user to run \`${librettoCommand("setup")}\`.`
|
|
399
385
|
);
|
|
400
386
|
}
|
|
401
387
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { resolveExperiments } from "../core/experiments.js";
|
|
2
3
|
import { createLoggerForSession } from "../core/context.js";
|
|
3
4
|
import {
|
|
4
5
|
generateSessionName,
|
|
@@ -17,6 +18,14 @@ function pageOption(help = "Target a specific page id") {
|
|
|
17
18
|
function integerOption(help) {
|
|
18
19
|
return SimpleCLI.option(z.coerce.number().int().optional(), { help });
|
|
19
20
|
}
|
|
21
|
+
function withExperiments() {
|
|
22
|
+
return async ({
|
|
23
|
+
ctx
|
|
24
|
+
}) => ({
|
|
25
|
+
...ctx,
|
|
26
|
+
experiments: resolveExperiments()
|
|
27
|
+
});
|
|
28
|
+
}
|
|
20
29
|
function withRequiredSession() {
|
|
21
30
|
return async ({ input, ctx }) => {
|
|
22
31
|
if (!input.session) {
|
|
@@ -47,5 +56,6 @@ export {
|
|
|
47
56
|
pageOption,
|
|
48
57
|
sessionOption,
|
|
49
58
|
withAutoSession,
|
|
59
|
+
withExperiments,
|
|
50
60
|
withRequiredSession
|
|
51
61
|
};
|
|
@@ -3,11 +3,18 @@ import { z } from "zod";
|
|
|
3
3
|
import { condenseDom } from "../../shared/condense-dom/condense-dom.js";
|
|
4
4
|
import { readSessionState } from "../core/session.js";
|
|
5
5
|
import { SimpleCLI } from "../framework/simple-cli.js";
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
pageOption,
|
|
8
|
+
sessionOption,
|
|
9
|
+
withExperiments,
|
|
10
|
+
withRequiredSession
|
|
11
|
+
} from "./shared.js";
|
|
7
12
|
import { runApiInterpret } from "../core/api-snapshot-analyzer.js";
|
|
8
13
|
import { readSnapshotModel } from "../core/config.js";
|
|
9
14
|
import { resolveSnapshotApiModelOrThrow } from "../core/ai-model.js";
|
|
10
|
-
import { DaemonClient } from "../core/daemon/
|
|
15
|
+
import { DaemonClient } from "../core/daemon/ipc.js";
|
|
16
|
+
import { librettoCommand } from "../../shared/package-manager.js";
|
|
17
|
+
import { renderSnapshot } from "../../shared/snapshot/render-snapshot.js";
|
|
11
18
|
const FALLBACK_SNAPSHOT_VIEWPORT = { width: 1280, height: 800 };
|
|
12
19
|
function isZeroViewport(value) {
|
|
13
20
|
return typeof value === "number" && value <= 0;
|
|
@@ -65,8 +72,17 @@ async function forceSnapshotViewport(page, viewport, logger, session, pageId, re
|
|
|
65
72
|
}
|
|
66
73
|
async function captureSnapshot(session, logger, daemonSocketPath, pageId) {
|
|
67
74
|
logger.info("snapshot-via-daemon", { session, pageId });
|
|
68
|
-
const client =
|
|
69
|
-
|
|
75
|
+
const client = await DaemonClient.connect(daemonSocketPath);
|
|
76
|
+
let snapshotResult;
|
|
77
|
+
try {
|
|
78
|
+
snapshotResult = await client.snapshot({ pageId });
|
|
79
|
+
} finally {
|
|
80
|
+
client.destroy();
|
|
81
|
+
}
|
|
82
|
+
if (!("htmlPath" in snapshotResult)) {
|
|
83
|
+
throw new Error("Daemon returned a compact snapshot for a legacy request.");
|
|
84
|
+
}
|
|
85
|
+
const { pngPath, htmlPath, snapshotRunId, pageUrl, title } = snapshotResult;
|
|
70
86
|
const htmlContent = readFileSync(htmlPath, "utf8");
|
|
71
87
|
const condenseResult = condenseDom(htmlContent);
|
|
72
88
|
const condensedHtmlPath = htmlPath.replace(/\.html$/, ".condensed.html");
|
|
@@ -88,6 +104,12 @@ async function captureSnapshot(session, logger, daemonSocketPath, pageId) {
|
|
|
88
104
|
return { pngPath, htmlPath, condensedHtmlPath, baseName: snapshotRunId };
|
|
89
105
|
}
|
|
90
106
|
async function runSnapshot(session, logger, pageId, objective, context) {
|
|
107
|
+
if (objective === void 0) {
|
|
108
|
+
throw new Error("Missing required option --objective.");
|
|
109
|
+
}
|
|
110
|
+
if (context === void 0) {
|
|
111
|
+
throw new Error("Missing required option --context.");
|
|
112
|
+
}
|
|
91
113
|
const normalizedObjective = objective.trim();
|
|
92
114
|
const normalizedContext = context.trim();
|
|
93
115
|
const snapshotModel = readSnapshotModel();
|
|
@@ -95,7 +117,7 @@ async function runSnapshot(session, logger, pageId, objective, context) {
|
|
|
95
117
|
const state = readSessionState(session, logger);
|
|
96
118
|
if (!state?.daemonSocketPath) {
|
|
97
119
|
throw new Error(
|
|
98
|
-
`Session "${session}" has no daemon socket. The browser daemon may have crashed. Close and reopen the session:
|
|
120
|
+
`Session "${session}" has no daemon socket. The browser daemon may have crashed. Close and reopen the session: ${librettoCommand(`close --session ${session}`)}`
|
|
99
121
|
);
|
|
100
122
|
}
|
|
101
123
|
const { pngPath, htmlPath, condensedHtmlPath } = await captureSnapshot(session, logger, state.daemonSocketPath, pageId);
|
|
@@ -113,18 +135,68 @@ async function runSnapshot(session, logger, pageId, objective, context) {
|
|
|
113
135
|
};
|
|
114
136
|
await runApiInterpret(interpretArgs, logger, snapshotModel);
|
|
115
137
|
}
|
|
138
|
+
async function runCompactSnapshot(args) {
|
|
139
|
+
if (!args.daemonSocketPath) {
|
|
140
|
+
throw new Error(
|
|
141
|
+
`Session "${args.session}" has no daemon socket. The browser daemon may have crashed. Close and reopen the session: ${librettoCommand(`close --session ${args.session}`)}`
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
args.logger.info("compact-snapshot-via-daemon", {
|
|
145
|
+
session: args.session,
|
|
146
|
+
pageId: args.pageId,
|
|
147
|
+
ref: args.ref
|
|
148
|
+
});
|
|
149
|
+
const client = await DaemonClient.connect(args.daemonSocketPath);
|
|
150
|
+
let result;
|
|
151
|
+
try {
|
|
152
|
+
result = await client.snapshot({
|
|
153
|
+
mode: "compact",
|
|
154
|
+
pageId: args.pageId,
|
|
155
|
+
useCachedSnapshot: args.ref !== void 0
|
|
156
|
+
});
|
|
157
|
+
} finally {
|
|
158
|
+
client.destroy();
|
|
159
|
+
}
|
|
160
|
+
if (!("mode" in result) || result.mode !== "compact") {
|
|
161
|
+
throw new Error("Daemon returned a legacy snapshot for a compact request.");
|
|
162
|
+
}
|
|
163
|
+
console.log(`Screenshot at ${result.pngPath}`);
|
|
164
|
+
console.log(renderSnapshot(result.snapshot, args.ref));
|
|
165
|
+
console.log(
|
|
166
|
+
`Hint: Use ${librettoCommand(`snapshot <ref> --session ${args.session}`)} to inspect a subtree.`
|
|
167
|
+
);
|
|
168
|
+
}
|
|
116
169
|
const snapshotInput = SimpleCLI.input({
|
|
117
|
-
positionals: [
|
|
170
|
+
positionals: [
|
|
171
|
+
SimpleCLI.positional("ref", z.string().optional(), {
|
|
172
|
+
help: "Optional element ref to scope output to that subtree (for example, l16 or e16)"
|
|
173
|
+
})
|
|
174
|
+
],
|
|
118
175
|
named: {
|
|
119
176
|
session: sessionOption(),
|
|
120
177
|
page: pageOption(),
|
|
121
|
-
objective: SimpleCLI.option(z.string()),
|
|
122
|
-
context: SimpleCLI.option(z.string())
|
|
178
|
+
objective: SimpleCLI.option(z.string().optional()),
|
|
179
|
+
context: SimpleCLI.option(z.string().optional())
|
|
123
180
|
}
|
|
124
181
|
});
|
|
125
182
|
const snapshotCommand = SimpleCLI.command({
|
|
126
183
|
description: "Capture PNG + HTML and analyze with --objective and --context"
|
|
127
|
-
}).input(snapshotInput).use(withRequiredSession()).handle(async ({ input, ctx }) => {
|
|
184
|
+
}).input(snapshotInput).use(withRequiredSession()).use(withExperiments()).handle(async ({ input, ctx }) => {
|
|
185
|
+
if (ctx.experiments["compact-snapshot-format"]) {
|
|
186
|
+
await runCompactSnapshot({
|
|
187
|
+
session: ctx.session,
|
|
188
|
+
daemonSocketPath: ctx.sessionState.daemonSocketPath,
|
|
189
|
+
logger: ctx.logger,
|
|
190
|
+
pageId: input.page,
|
|
191
|
+
ref: input.ref
|
|
192
|
+
});
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
if (input.ref) {
|
|
196
|
+
throw new Error(
|
|
197
|
+
`Snapshot refs require the compact-snapshot-format experiment. Enable it with ${librettoCommand("experiments enable compact-snapshot-format")}.`
|
|
198
|
+
);
|
|
199
|
+
}
|
|
128
200
|
await runSnapshot(
|
|
129
201
|
ctx.session,
|
|
130
202
|
ctx.logger,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { LIBRETTO_CONFIG_PATH } from "../core/context.js";
|
|
2
2
|
import { resolveAiSetupStatus } from "../core/ai-model.js";
|
|
3
|
+
import { librettoCommand } from "../../shared/package-manager.js";
|
|
3
4
|
import { listRunningSessions } from "../core/session.js";
|
|
4
5
|
import { SimpleCLI } from "../framework/simple-cli.js";
|
|
5
6
|
function printAiStatus(status) {
|
|
@@ -13,26 +14,26 @@ function printAiStatus(status) {
|
|
|
13
14
|
console.log(` Source: ${status.source}`);
|
|
14
15
|
}
|
|
15
16
|
console.log(
|
|
16
|
-
|
|
17
|
+
` To change: ${librettoCommand("ai configure openai | anthropic | gemini | vertex | openrouter")}`
|
|
17
18
|
);
|
|
18
19
|
break;
|
|
19
20
|
case "configured-missing-credentials":
|
|
20
21
|
console.log(
|
|
21
22
|
` \u2717 ${status.provider} is configured (model: ${status.model}), but credentials are missing.`
|
|
22
23
|
);
|
|
23
|
-
console.log(
|
|
24
|
+
console.log(` Run \`${librettoCommand("setup")}\` to repair.`);
|
|
24
25
|
break;
|
|
25
26
|
case "invalid-config":
|
|
26
27
|
console.log(" \u2717 Config is invalid:");
|
|
27
28
|
for (const line of status.message.split("\n")) {
|
|
28
29
|
console.log(` ${line}`);
|
|
29
30
|
}
|
|
30
|
-
console.log(
|
|
31
|
+
console.log(` Run \`${librettoCommand("setup")}\` to reconfigure.`);
|
|
31
32
|
break;
|
|
32
33
|
case "unconfigured":
|
|
33
34
|
console.log(" \u2717 No AI model configured.");
|
|
34
35
|
console.log(
|
|
35
|
-
|
|
36
|
+
` Run \`${librettoCommand("setup")}\` or \`${librettoCommand("ai configure")}\` to set up.`
|
|
36
37
|
);
|
|
37
38
|
break;
|
|
38
39
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { readSnapshotModel } from "./config.js";
|
|
2
2
|
import { LIBRETTO_CONFIG_PATH } from "./context.js";
|
|
3
|
+
import { librettoCommand } from "../../shared/package-manager.js";
|
|
3
4
|
import {
|
|
4
5
|
hasProviderCredentials,
|
|
5
6
|
parseModel
|
|
@@ -52,7 +53,9 @@ function providerSetupSentence(provider) {
|
|
|
52
53
|
}
|
|
53
54
|
}
|
|
54
55
|
function defaultModelCommandLine() {
|
|
55
|
-
return
|
|
56
|
+
return librettoCommand(
|
|
57
|
+
"ai configure openai | anthropic | gemini | vertex | openrouter"
|
|
58
|
+
);
|
|
56
59
|
}
|
|
57
60
|
function providerMissingCredentialSummary(provider) {
|
|
58
61
|
switch (provider) {
|
|
@@ -72,7 +75,7 @@ function noSnapshotApiConfiguredMessage() {
|
|
|
72
75
|
return [
|
|
73
76
|
"Failed to analyze snapshot because no snapshot analyzer is configured.",
|
|
74
77
|
`Add OPENAI_API_KEY, ANTHROPIC_API_KEY, GEMINI_API_KEY or GOOGLE_GENERATIVE_AI_API_KEY, GOOGLE_CLOUD_PROJECT, or OPENROUTER_API_KEY to .env or as a shell environment variable, or choose a default model with \`${defaultModelCommandLine()}\`.`,
|
|
75
|
-
|
|
78
|
+
`For more info, run \`${librettoCommand("setup")}\`.`
|
|
76
79
|
].join(" ");
|
|
77
80
|
}
|
|
78
81
|
function missingProviderSnapshotMessage(selection) {
|
|
@@ -80,7 +83,7 @@ function missingProviderSnapshotMessage(selection) {
|
|
|
80
83
|
return [
|
|
81
84
|
`Failed to analyze snapshot because ${selection.provider} is configured${configuredSource}, but ${providerMissingCredentialSummary(selection.provider)}.`,
|
|
82
85
|
providerSetupSentence(selection.provider),
|
|
83
|
-
|
|
86
|
+
`For more info, run \`${librettoCommand("setup")}\`.`
|
|
84
87
|
].join(" ");
|
|
85
88
|
}
|
|
86
89
|
function inferAutoSnapshotModel() {
|