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,120 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { librettoCommand } from "../../shared/package-manager.js";
|
|
3
|
+
import {
|
|
4
|
+
EXPERIMENTS,
|
|
5
|
+
isExperimentName,
|
|
6
|
+
resolveExperiments,
|
|
7
|
+
setExperimentEnabled,
|
|
8
|
+
type ExperimentName,
|
|
9
|
+
type Experiments,
|
|
10
|
+
} from "../core/experiments.js";
|
|
11
|
+
import { SimpleCLI } from "../framework/simple-cli.js";
|
|
12
|
+
|
|
13
|
+
const experimentNames = Object.keys(EXPERIMENTS) as ExperimentName[];
|
|
14
|
+
|
|
15
|
+
const experimentsUsage = [
|
|
16
|
+
"Usage:",
|
|
17
|
+
` ${librettoCommand("experiments")}`,
|
|
18
|
+
` ${librettoCommand("experiments describe <experiment>")}`,
|
|
19
|
+
` ${librettoCommand("experiments enable <experiment>")}`,
|
|
20
|
+
` ${librettoCommand("experiments disable <experiment>")}`,
|
|
21
|
+
].join("\n");
|
|
22
|
+
|
|
23
|
+
export const experimentsInput = SimpleCLI.input({
|
|
24
|
+
positionals: [
|
|
25
|
+
SimpleCLI.positional("action", z.string().optional(), {
|
|
26
|
+
help: "Action to apply",
|
|
27
|
+
}),
|
|
28
|
+
SimpleCLI.positional("experiment", z.string().optional(), {
|
|
29
|
+
help: "Experiment name",
|
|
30
|
+
}),
|
|
31
|
+
],
|
|
32
|
+
named: {},
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
function formatAvailableExperiments(): string {
|
|
36
|
+
return [
|
|
37
|
+
"Available experiments:",
|
|
38
|
+
...experimentNames.map((name) => ` ${name}`),
|
|
39
|
+
].join("\n");
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function experimentUsageError(message: string): Error {
|
|
43
|
+
return new Error(
|
|
44
|
+
[message, "", experimentsUsage, "", formatAvailableExperiments()].join(
|
|
45
|
+
"\n",
|
|
46
|
+
),
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function printExperiments(experiments: Experiments): void {
|
|
51
|
+
console.log("Libretto experiments:");
|
|
52
|
+
for (const name of experimentNames) {
|
|
53
|
+
const metadata = EXPERIMENTS[name];
|
|
54
|
+
console.log(
|
|
55
|
+
`- ${name}: ${experiments[name] ? "enabled" : "disabled"} — ${metadata.title}`,
|
|
56
|
+
);
|
|
57
|
+
console.log(` ${metadata.oneSentenceDescription}`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function printExperimentDescription(
|
|
62
|
+
name: ExperimentName,
|
|
63
|
+
experiments: Experiments,
|
|
64
|
+
): void {
|
|
65
|
+
const metadata = EXPERIMENTS[name];
|
|
66
|
+
console.log(`${metadata.title} (${name})`);
|
|
67
|
+
console.log(`Status: ${experiments[name] ? "enabled" : "disabled"}`);
|
|
68
|
+
console.log("");
|
|
69
|
+
if (experiments[name]) {
|
|
70
|
+
console.log(
|
|
71
|
+
"Since this experiment is enabled, Libretto’s expected usage deviates from the skill. Use these instructions where they differ:",
|
|
72
|
+
);
|
|
73
|
+
console.log("");
|
|
74
|
+
}
|
|
75
|
+
console.log(metadata.docs ?? metadata.oneSentenceDescription);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export const experimentsCommand = SimpleCLI.command({
|
|
79
|
+
description: "List or update Libretto experiment flags",
|
|
80
|
+
})
|
|
81
|
+
.input(experimentsInput)
|
|
82
|
+
.handle(async ({ input }) => {
|
|
83
|
+
if (!input.action) {
|
|
84
|
+
printExperiments(resolveExperiments());
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (
|
|
89
|
+
input.action !== "describe" &&
|
|
90
|
+
input.action !== "enable" &&
|
|
91
|
+
input.action !== "disable"
|
|
92
|
+
) {
|
|
93
|
+
throw experimentUsageError(`Unknown experiments action "${input.action}".`);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (!input.experiment) {
|
|
97
|
+
throw experimentUsageError(
|
|
98
|
+
`Missing experiment name for ${input.action}.`,
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (!isExperimentName(input.experiment)) {
|
|
103
|
+
throw experimentUsageError(`Unknown experiment "${input.experiment}".`);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (input.action === "describe") {
|
|
107
|
+
printExperimentDescription(input.experiment, resolveExperiments());
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const experiments = setExperimentEnabled(
|
|
112
|
+
input.experiment,
|
|
113
|
+
input.action === "enable",
|
|
114
|
+
);
|
|
115
|
+
console.log(`Experiment "${input.experiment}" ${input.action}d.`);
|
|
116
|
+
if (input.action === "enable") {
|
|
117
|
+
console.log("");
|
|
118
|
+
printExperimentDescription(input.experiment, experiments);
|
|
119
|
+
}
|
|
120
|
+
});
|
|
@@ -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";
|
|
@@ -19,6 +14,11 @@ import {
|
|
|
19
14
|
DEFAULT_SNAPSHOT_MODELS,
|
|
20
15
|
resolveAiSetupStatus,
|
|
21
16
|
} from "../core/ai-model.js";
|
|
17
|
+
import {
|
|
18
|
+
detectProjectPackageManager,
|
|
19
|
+
installCommand,
|
|
20
|
+
librettoCommand,
|
|
21
|
+
} from "../../shared/package-manager.js";
|
|
22
22
|
import type { Provider } from "../core/resolve-model.js";
|
|
23
23
|
import { SimpleCLI } from "../framework/simple-cli.js";
|
|
24
24
|
|
|
@@ -30,26 +30,6 @@ const PROVIDER_SDK_PACKAGES: Record<Provider, string> = {
|
|
|
30
30
|
openrouter: "@ai-sdk/openai",
|
|
31
31
|
};
|
|
32
32
|
|
|
33
|
-
function detectPackageManager(): string {
|
|
34
|
-
if (existsSync(join(REPO_ROOT, "pnpm-lock.yaml"))) return "pnpm";
|
|
35
|
-
if (existsSync(join(REPO_ROOT, "yarn.lock"))) return "yarn";
|
|
36
|
-
if (existsSync(join(REPO_ROOT, "bun.lockb"))) return "bun";
|
|
37
|
-
return "npm";
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function installCommand(pkgManager: string): string {
|
|
41
|
-
switch (pkgManager) {
|
|
42
|
-
case "yarn":
|
|
43
|
-
return "yarn add";
|
|
44
|
-
case "bun":
|
|
45
|
-
return "bun add";
|
|
46
|
-
case "pnpm":
|
|
47
|
-
return "pnpm add";
|
|
48
|
-
default:
|
|
49
|
-
return "npm install";
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
33
|
function isSdkInstalled(sdkPackage: string): boolean {
|
|
54
34
|
try {
|
|
55
35
|
const result = spawnSync("node", ["-e", `require.resolve("${sdkPackage}")`], {
|
|
@@ -66,7 +46,7 @@ function installSdkIfNeeded(provider: Provider): void {
|
|
|
66
46
|
const sdkPackage = PROVIDER_SDK_PACKAGES[provider];
|
|
67
47
|
if (isSdkInstalled(sdkPackage)) return;
|
|
68
48
|
|
|
69
|
-
const pkgManager =
|
|
49
|
+
const pkgManager = detectProjectPackageManager();
|
|
70
50
|
const cmd = installCommand(pkgManager);
|
|
71
51
|
console.log(`\nInstalling ${sdkPackage}...`);
|
|
72
52
|
const result = spawnSync(cmd, [sdkPackage], {
|
|
@@ -177,7 +157,7 @@ function printHealthySummary(status: AiSetupStatus & { kind: "ready" }): void {
|
|
|
177
157
|
console.log(`✓ Using ${providerLabel(status.provider)} (${status.model}).`);
|
|
178
158
|
}
|
|
179
159
|
console.log(
|
|
180
|
-
|
|
160
|
+
`To change: ${librettoCommand("ai configure openai | anthropic | gemini | vertex | openrouter")}`,
|
|
181
161
|
);
|
|
182
162
|
}
|
|
183
163
|
|
|
@@ -254,14 +234,16 @@ function printSnapshotApiStatus(): boolean {
|
|
|
254
234
|
console.log();
|
|
255
235
|
console.log(formatMissingCredentialsMessage(plan));
|
|
256
236
|
console.log(
|
|
257
|
-
` To fix: add ${plan.envVar} to .env, or run
|
|
237
|
+
` To fix: add ${plan.envVar} to .env, or run \`${librettoCommand("setup")}\` interactively to repair.`,
|
|
258
238
|
);
|
|
259
239
|
return false;
|
|
260
240
|
}
|
|
261
241
|
|
|
262
242
|
if (plan.kind === "repair-invalid-config") {
|
|
263
243
|
printInvalidAiConfigWarning(status);
|
|
264
|
-
console.log(
|
|
244
|
+
console.log(
|
|
245
|
+
` Run \`${librettoCommand("setup")}\` interactively to reconfigure.`,
|
|
246
|
+
);
|
|
265
247
|
return false;
|
|
266
248
|
}
|
|
267
249
|
|
|
@@ -275,10 +257,10 @@ function printSnapshotApiStatus(): boolean {
|
|
|
275
257
|
" GOOGLE_CLOUD_PROJECT=... # plus application default credentials for Vertex",
|
|
276
258
|
);
|
|
277
259
|
console.log(
|
|
278
|
-
|
|
260
|
+
` Or run \`${librettoCommand("ai configure openai | anthropic | gemini | vertex | openrouter")}\` to set a specific model.`,
|
|
279
261
|
);
|
|
280
262
|
console.log(
|
|
281
|
-
|
|
263
|
+
` Run \`${librettoCommand("setup")}\` interactively to set up credentials.`,
|
|
282
264
|
);
|
|
283
265
|
return false;
|
|
284
266
|
}
|
|
@@ -324,14 +306,14 @@ async function promptProviderSelection(
|
|
|
324
306
|
|
|
325
307
|
function printSkipMessage(): void {
|
|
326
308
|
console.log(
|
|
327
|
-
|
|
309
|
+
`\nSkipped. You can set up API credentials later by rerunning \`${librettoCommand("setup")}\`.`,
|
|
328
310
|
);
|
|
329
311
|
console.log("Or add credentials directly to your .env file:");
|
|
330
312
|
console.log(" OPENAI_API_KEY=...");
|
|
331
313
|
console.log(" ANTHROPIC_API_KEY=...");
|
|
332
314
|
console.log(" GEMINI_API_KEY=...");
|
|
333
315
|
console.log(
|
|
334
|
-
|
|
316
|
+
` Or run \`${librettoCommand("ai configure openai | anthropic | gemini | vertex | openrouter")}\` to set a specific model.`,
|
|
335
317
|
);
|
|
336
318
|
}
|
|
337
319
|
|
|
@@ -442,7 +424,7 @@ function copySkills(): void {
|
|
|
442
424
|
"\n⚠️ No .agents/ or .claude/ directory found. Libretto skills were not installed.",
|
|
443
425
|
);
|
|
444
426
|
console.log(
|
|
445
|
-
|
|
427
|
+
` Create one of these directories in your repo root and rerun \`${librettoCommand("setup")}\` to install skills:`,
|
|
446
428
|
);
|
|
447
429
|
console.log(` mkdir ${join(REPO_ROOT, ".claude")}`);
|
|
448
430
|
return;
|
|
@@ -510,7 +492,7 @@ export const setupCommand = SimpleCLI.command({
|
|
|
510
492
|
const ready = printSnapshotApiStatus();
|
|
511
493
|
if (!ready) {
|
|
512
494
|
console.log(
|
|
513
|
-
|
|
495
|
+
`\nIf you're an agent, request the user to run \`${librettoCommand("setup")}\`.`,
|
|
514
496
|
);
|
|
515
497
|
}
|
|
516
498
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import type { LoggerApi } from "../../shared/logger/index.js";
|
|
3
|
+
import type { Experiments } from "../core/experiments.js";
|
|
4
|
+
import { resolveExperiments } from "../core/experiments.js";
|
|
3
5
|
import { createLoggerForSession } from "../core/context.js";
|
|
4
6
|
import {
|
|
5
7
|
generateSessionName,
|
|
@@ -9,6 +11,8 @@ import {
|
|
|
9
11
|
} from "../core/session.js";
|
|
10
12
|
import {
|
|
11
13
|
SimpleCLI,
|
|
14
|
+
type SimpleCLIMiddlewareArgs,
|
|
15
|
+
type SimpleCLIContext,
|
|
12
16
|
type SimpleCLIMiddleware,
|
|
13
17
|
} from "../framework/simple-cli.js";
|
|
14
18
|
|
|
@@ -33,6 +37,22 @@ export type SessionStateContext = SessionContext & {
|
|
|
33
37
|
sessionState: SessionState;
|
|
34
38
|
};
|
|
35
39
|
|
|
40
|
+
export type ExperimentsContext = {
|
|
41
|
+
experiments: Experiments;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export function withExperiments() {
|
|
45
|
+
return async <TInput, TContext extends SimpleCLIContext>({
|
|
46
|
+
ctx,
|
|
47
|
+
}: SimpleCLIMiddlewareArgs<
|
|
48
|
+
TInput,
|
|
49
|
+
TContext
|
|
50
|
+
>): Promise<TContext & ExperimentsContext> => ({
|
|
51
|
+
...ctx,
|
|
52
|
+
experiments: resolveExperiments(),
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
36
56
|
export function withRequiredSession(): SimpleCLIMiddleware<
|
|
37
57
|
{ session?: string },
|
|
38
58
|
{},
|
|
@@ -8,11 +8,18 @@ import {
|
|
|
8
8
|
type ScreenshotPair,
|
|
9
9
|
} from "../core/snapshot-analyzer.js";
|
|
10
10
|
import { SimpleCLI } from "../framework/simple-cli.js";
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
pageOption,
|
|
13
|
+
sessionOption,
|
|
14
|
+
withExperiments,
|
|
15
|
+
withRequiredSession,
|
|
16
|
+
} from "./shared.js";
|
|
12
17
|
import { runApiInterpret } from "../core/api-snapshot-analyzer.js";
|
|
13
18
|
import { readSnapshotModel } from "../core/config.js";
|
|
14
19
|
import { resolveSnapshotApiModelOrThrow } from "../core/ai-model.js";
|
|
15
|
-
import { DaemonClient } from "../core/daemon/
|
|
20
|
+
import { DaemonClient } from "../core/daemon/ipc.js";
|
|
21
|
+
import { librettoCommand } from "../../shared/package-manager.js";
|
|
22
|
+
import { renderSnapshot } from "../../shared/snapshot/render-snapshot.js";
|
|
16
23
|
|
|
17
24
|
export const FALLBACK_SNAPSHOT_VIEWPORT = { width: 1280, height: 800 } as const;
|
|
18
25
|
|
|
@@ -116,9 +123,17 @@ async function captureSnapshot(
|
|
|
116
123
|
pageId?: string,
|
|
117
124
|
): Promise<ScreenshotPair> {
|
|
118
125
|
logger.info("snapshot-via-daemon", { session, pageId });
|
|
119
|
-
const client =
|
|
120
|
-
|
|
121
|
-
|
|
126
|
+
const client = await DaemonClient.connect(daemonSocketPath);
|
|
127
|
+
let snapshotResult: Awaited<ReturnType<DaemonClient["snapshot"]>>;
|
|
128
|
+
try {
|
|
129
|
+
snapshotResult = await client.snapshot({ pageId });
|
|
130
|
+
} finally {
|
|
131
|
+
client.destroy();
|
|
132
|
+
}
|
|
133
|
+
if (!("htmlPath" in snapshotResult)) {
|
|
134
|
+
throw new Error("Daemon returned a compact snapshot for a legacy request.");
|
|
135
|
+
}
|
|
136
|
+
const { pngPath, htmlPath, snapshotRunId, pageUrl, title } = snapshotResult;
|
|
122
137
|
|
|
123
138
|
// condenseDom runs in the CLI process, not the daemon.
|
|
124
139
|
const htmlContent = readFileSync(htmlPath, "utf8");
|
|
@@ -148,9 +163,16 @@ async function runSnapshot(
|
|
|
148
163
|
session: string,
|
|
149
164
|
logger: LoggerApi,
|
|
150
165
|
pageId: string | undefined,
|
|
151
|
-
objective: string,
|
|
152
|
-
context: string,
|
|
166
|
+
objective: string | undefined,
|
|
167
|
+
context: string | undefined,
|
|
153
168
|
): Promise<void> {
|
|
169
|
+
if (objective === undefined) {
|
|
170
|
+
throw new Error("Missing required option --objective.");
|
|
171
|
+
}
|
|
172
|
+
if (context === undefined) {
|
|
173
|
+
throw new Error("Missing required option --context.");
|
|
174
|
+
}
|
|
175
|
+
|
|
154
176
|
const normalizedObjective = objective.trim();
|
|
155
177
|
const normalizedContext = context.trim();
|
|
156
178
|
|
|
@@ -161,7 +183,7 @@ async function runSnapshot(
|
|
|
161
183
|
if (!state?.daemonSocketPath) {
|
|
162
184
|
throw new Error(
|
|
163
185
|
`Session "${session}" has no daemon socket. The browser daemon may have crashed. ` +
|
|
164
|
-
`Close and reopen the session:
|
|
186
|
+
`Close and reopen the session: ${librettoCommand(`close --session ${session}`)}`,
|
|
165
187
|
);
|
|
166
188
|
}
|
|
167
189
|
|
|
@@ -189,13 +211,61 @@ async function runSnapshot(
|
|
|
189
211
|
await runApiInterpret(interpretArgs, logger, snapshotModel);
|
|
190
212
|
}
|
|
191
213
|
|
|
214
|
+
async function runCompactSnapshot(
|
|
215
|
+
args: {
|
|
216
|
+
session: string;
|
|
217
|
+
daemonSocketPath?: string;
|
|
218
|
+
logger: LoggerApi;
|
|
219
|
+
pageId?: string;
|
|
220
|
+
ref?: string;
|
|
221
|
+
},
|
|
222
|
+
): Promise<void> {
|
|
223
|
+
if (!args.daemonSocketPath) {
|
|
224
|
+
throw new Error(
|
|
225
|
+
`Session "${args.session}" has no daemon socket. The browser daemon may have crashed. ` +
|
|
226
|
+
`Close and reopen the session: ${librettoCommand(`close --session ${args.session}`)}`,
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
args.logger.info("compact-snapshot-via-daemon", {
|
|
231
|
+
session: args.session,
|
|
232
|
+
pageId: args.pageId,
|
|
233
|
+
ref: args.ref,
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
const client = await DaemonClient.connect(args.daemonSocketPath);
|
|
237
|
+
let result: Awaited<ReturnType<DaemonClient["snapshot"]>>;
|
|
238
|
+
try {
|
|
239
|
+
result = await client.snapshot({
|
|
240
|
+
mode: "compact",
|
|
241
|
+
pageId: args.pageId,
|
|
242
|
+
useCachedSnapshot: args.ref !== undefined,
|
|
243
|
+
});
|
|
244
|
+
} finally {
|
|
245
|
+
client.destroy();
|
|
246
|
+
}
|
|
247
|
+
if (!("mode" in result) || result.mode !== "compact") {
|
|
248
|
+
throw new Error("Daemon returned a legacy snapshot for a compact request.");
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
console.log(`Screenshot at ${result.pngPath}`);
|
|
252
|
+
console.log(renderSnapshot(result.snapshot, args.ref));
|
|
253
|
+
console.log(
|
|
254
|
+
`Hint: Use ${librettoCommand(`snapshot <ref> --session ${args.session}`)} to inspect a subtree.`,
|
|
255
|
+
);
|
|
256
|
+
}
|
|
257
|
+
|
|
192
258
|
export const snapshotInput = SimpleCLI.input({
|
|
193
|
-
positionals: [
|
|
259
|
+
positionals: [
|
|
260
|
+
SimpleCLI.positional("ref", z.string().optional(), {
|
|
261
|
+
help: "Optional element ref to scope output to that subtree (for example, l16 or e16)",
|
|
262
|
+
}),
|
|
263
|
+
],
|
|
194
264
|
named: {
|
|
195
265
|
session: sessionOption(),
|
|
196
266
|
page: pageOption(),
|
|
197
|
-
objective: SimpleCLI.option(z.string()),
|
|
198
|
-
context: SimpleCLI.option(z.string()),
|
|
267
|
+
objective: SimpleCLI.option(z.string().optional()),
|
|
268
|
+
context: SimpleCLI.option(z.string().optional()),
|
|
199
269
|
},
|
|
200
270
|
});
|
|
201
271
|
|
|
@@ -204,7 +274,25 @@ export const snapshotCommand = SimpleCLI.command({
|
|
|
204
274
|
})
|
|
205
275
|
.input(snapshotInput)
|
|
206
276
|
.use(withRequiredSession())
|
|
277
|
+
.use(withExperiments())
|
|
207
278
|
.handle(async ({ input, ctx }) => {
|
|
279
|
+
if (ctx.experiments["compact-snapshot-format"]) {
|
|
280
|
+
await runCompactSnapshot({
|
|
281
|
+
session: ctx.session,
|
|
282
|
+
daemonSocketPath: ctx.sessionState.daemonSocketPath,
|
|
283
|
+
logger: ctx.logger,
|
|
284
|
+
pageId: input.page,
|
|
285
|
+
ref: input.ref,
|
|
286
|
+
});
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
if (input.ref) {
|
|
291
|
+
throw new Error(
|
|
292
|
+
`Snapshot refs require the compact-snapshot-format experiment. Enable it with ${librettoCommand("experiments enable compact-snapshot-format")}.`,
|
|
293
|
+
);
|
|
294
|
+
}
|
|
295
|
+
|
|
208
296
|
await runSnapshot(
|
|
209
297
|
ctx.session,
|
|
210
298
|
ctx.logger,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { LIBRETTO_CONFIG_PATH } from "../core/context.js";
|
|
2
2
|
import { type AiSetupStatus, resolveAiSetupStatus } from "../core/ai-model.js";
|
|
3
|
+
import { librettoCommand } from "../../shared/package-manager.js";
|
|
3
4
|
import { listRunningSessions, type SessionState } from "../core/session.js";
|
|
4
5
|
import { SimpleCLI } from "../framework/simple-cli.js";
|
|
5
6
|
|
|
@@ -17,7 +18,7 @@ function printAiStatus(status: AiSetupStatus): void {
|
|
|
17
18
|
console.log(` Source: ${status.source}`);
|
|
18
19
|
}
|
|
19
20
|
console.log(
|
|
20
|
-
|
|
21
|
+
` To change: ${librettoCommand("ai configure openai | anthropic | gemini | vertex | openrouter")}`,
|
|
21
22
|
);
|
|
22
23
|
break;
|
|
23
24
|
|
|
@@ -25,7 +26,7 @@ function printAiStatus(status: AiSetupStatus): void {
|
|
|
25
26
|
console.log(
|
|
26
27
|
` ✗ ${status.provider} is configured (model: ${status.model}), but credentials are missing.`,
|
|
27
28
|
);
|
|
28
|
-
console.log(
|
|
29
|
+
console.log(` Run \`${librettoCommand("setup")}\` to repair.`);
|
|
29
30
|
break;
|
|
30
31
|
|
|
31
32
|
case "invalid-config":
|
|
@@ -33,13 +34,13 @@ function printAiStatus(status: AiSetupStatus): void {
|
|
|
33
34
|
for (const line of status.message.split("\n")) {
|
|
34
35
|
console.log(` ${line}`);
|
|
35
36
|
}
|
|
36
|
-
console.log(
|
|
37
|
+
console.log(` Run \`${librettoCommand("setup")}\` to reconfigure.`);
|
|
37
38
|
break;
|
|
38
39
|
|
|
39
40
|
case "unconfigured":
|
|
40
41
|
console.log(" ✗ No AI model configured.");
|
|
41
42
|
console.log(
|
|
42
|
-
|
|
43
|
+
` Run \`${librettoCommand("setup")}\` or \`${librettoCommand("ai configure")}\` to set up.`,
|
|
43
44
|
);
|
|
44
45
|
break;
|
|
45
46
|
}
|
package/src/cli/core/ai-model.ts
CHANGED
|
@@ -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,
|
|
@@ -80,7 +81,9 @@ function providerSetupSentence(provider: Provider): string {
|
|
|
80
81
|
}
|
|
81
82
|
|
|
82
83
|
function defaultModelCommandLine(): string {
|
|
83
|
-
return
|
|
84
|
+
return librettoCommand(
|
|
85
|
+
"ai configure openai | anthropic | gemini | vertex | openrouter",
|
|
86
|
+
);
|
|
84
87
|
}
|
|
85
88
|
|
|
86
89
|
function providerMissingCredentialSummary(provider: Provider): string {
|
|
@@ -102,7 +105,7 @@ function noSnapshotApiConfiguredMessage(): string {
|
|
|
102
105
|
return [
|
|
103
106
|
"Failed to analyze snapshot because no snapshot analyzer is configured.",
|
|
104
107
|
`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()}\`.`,
|
|
105
|
-
|
|
108
|
+
`For more info, run \`${librettoCommand("setup")}\`.`,
|
|
106
109
|
].join(" ");
|
|
107
110
|
}
|
|
108
111
|
|
|
@@ -116,7 +119,7 @@ function missingProviderSnapshotMessage(
|
|
|
116
119
|
return [
|
|
117
120
|
`Failed to analyze snapshot because ${selection.provider} is configured${configuredSource}, but ${providerMissingCredentialSummary(selection.provider)}.`,
|
|
118
121
|
providerSetupSentence(selection.provider),
|
|
119
|
-
|
|
122
|
+
`For more info, run \`${librettoCommand("setup")}\`.`,
|
|
120
123
|
].join(" ");
|
|
121
124
|
}
|
|
122
125
|
|