libretto 0.5.6 → 0.6.0
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/dist/cli/commands/browser.js +31 -6
- package/dist/cli/commands/execution.js +54 -15
- package/dist/cli/commands/setup.js +78 -64
- package/dist/cli/commands/status.js +1 -1
- package/dist/cli/core/browser.js +163 -10
- package/dist/cli/core/config.js +1 -0
- package/dist/cli/core/providers/browserbase.js +53 -0
- package/dist/cli/core/providers/index.js +48 -0
- package/dist/cli/core/providers/kernel.js +46 -0
- package/dist/cli/core/providers/libretto-cloud.js +58 -0
- package/dist/cli/core/providers/types.js +0 -0
- package/dist/cli/core/session.js +9 -0
- package/dist/cli/workers/run-integration-runtime.js +3 -1
- package/dist/cli/workers/run-integration-worker-protocol.js +3 -1
- package/dist/shared/run/browser.d.ts +6 -1
- package/dist/shared/run/browser.js +39 -1
- package/dist/shared/state/session-state.d.ts +11 -1
- package/dist/shared/state/session-state.js +9 -2
- package/package.json +1 -1
- package/src/cli/commands/browser.ts +35 -7
- package/src/cli/commands/execution.ts +54 -14
- package/src/cli/commands/setup.ts +81 -64
- package/src/cli/commands/status.ts +3 -1
- package/src/cli/core/browser.ts +197 -9
- package/src/cli/core/config.ts +1 -0
- package/src/cli/core/providers/browserbase.ts +57 -0
- package/src/cli/core/providers/index.ts +62 -0
- package/src/cli/core/providers/kernel.ts +49 -0
- package/src/cli/core/providers/libretto-cloud.ts +61 -0
- package/src/cli/core/providers/types.ts +9 -0
- package/src/cli/core/session.ts +13 -0
- package/src/cli/workers/run-integration-runtime.ts +2 -0
- package/src/cli/workers/run-integration-worker-protocol.ts +2 -0
- package/src/shared/run/browser.ts +45 -0
- package/src/shared/state/session-state.ts +7 -0
|
@@ -5,9 +5,14 @@ import {
|
|
|
5
5
|
runCloseAll as runCloseAllWithLogger,
|
|
6
6
|
runConnect as runConnectWithLogger,
|
|
7
7
|
runOpen,
|
|
8
|
+
runOpenWithProvider,
|
|
8
9
|
runPages,
|
|
9
10
|
runSave,
|
|
10
11
|
} from "../core/browser.js";
|
|
12
|
+
import {
|
|
13
|
+
resolveProviderName,
|
|
14
|
+
getCloudProviderApi,
|
|
15
|
+
} from "../core/providers/index.js";
|
|
11
16
|
import { readLibrettoConfig } from "../core/config.js";
|
|
12
17
|
import { createLoggerForSession, withSessionLogger } from "../core/context.js";
|
|
13
18
|
import {
|
|
@@ -47,7 +52,10 @@ export function parseViewportArg(
|
|
|
47
52
|
return { width, height };
|
|
48
53
|
}
|
|
49
54
|
|
|
50
|
-
function resolveRequestedSessionMode(
|
|
55
|
+
function resolveRequestedSessionMode(
|
|
56
|
+
readOnly: boolean | undefined,
|
|
57
|
+
writeAccess: boolean | undefined,
|
|
58
|
+
): SessionAccessMode {
|
|
51
59
|
if (readOnly) return "read-only";
|
|
52
60
|
if (writeAccess) return "write-access";
|
|
53
61
|
const config = readLibrettoConfig();
|
|
@@ -75,6 +83,10 @@ export const openInput = SimpleCLI.input({
|
|
|
75
83
|
viewport: SimpleCLI.option(z.string().optional(), {
|
|
76
84
|
help: "Viewport size as WIDTHxHEIGHT (e.g. 1920x1080)",
|
|
77
85
|
}),
|
|
86
|
+
provider: SimpleCLI.option(z.string().optional(), {
|
|
87
|
+
help: "Browser provider (local, kernel, browserbase)",
|
|
88
|
+
aliases: ["-p"],
|
|
89
|
+
}),
|
|
78
90
|
},
|
|
79
91
|
})
|
|
80
92
|
.refine(
|
|
@@ -98,12 +110,28 @@ export const openCommand = SimpleCLI.command({
|
|
|
98
110
|
.handle(async ({ input, ctx }) => {
|
|
99
111
|
warnIfInstalledSkillOutOfDate();
|
|
100
112
|
assertSessionAvailableForStart(ctx.session, ctx.logger);
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
viewport
|
|
105
|
-
|
|
106
|
-
|
|
113
|
+
const providerName = resolveProviderName(input.provider);
|
|
114
|
+
if (providerName === "local") {
|
|
115
|
+
const headed = input.headed || !input.headless;
|
|
116
|
+
const viewport = parseViewportArg(input.viewport);
|
|
117
|
+
await runOpen(input.url!, headed, ctx.session, ctx.logger, {
|
|
118
|
+
viewport,
|
|
119
|
+
accessMode: resolveRequestedSessionMode(
|
|
120
|
+
input.readOnly,
|
|
121
|
+
input.writeAccess,
|
|
122
|
+
),
|
|
123
|
+
});
|
|
124
|
+
} else {
|
|
125
|
+
const provider = getCloudProviderApi(providerName);
|
|
126
|
+
await runOpenWithProvider(
|
|
127
|
+
input.url!,
|
|
128
|
+
providerName,
|
|
129
|
+
provider,
|
|
130
|
+
ctx.session,
|
|
131
|
+
ctx.logger,
|
|
132
|
+
resolveRequestedSessionMode(input.readOnly, input.writeAccess),
|
|
133
|
+
);
|
|
134
|
+
}
|
|
107
135
|
});
|
|
108
136
|
|
|
109
137
|
export const connectInput = SimpleCLI.input({
|
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
wrapPageForActionLogging,
|
|
28
28
|
} from "../core/telemetry.js";
|
|
29
29
|
import { readLibrettoConfig } from "../core/config.js";
|
|
30
|
+
import { resolveProviderName, getCloudProviderApi } from "../core/providers/index.js";
|
|
30
31
|
import { createReadonlyExecHelpers } from "../core/readonly-exec.js";
|
|
31
32
|
import type { RunIntegrationWorkerRequest } from "../workers/run-integration-worker-protocol.js";
|
|
32
33
|
import { SimpleCLI } from "../framework/simple-cli.js";
|
|
@@ -630,6 +631,8 @@ async function runIntegrationFromFile(
|
|
|
630
631
|
authProfileDomain: args.authProfileDomain,
|
|
631
632
|
viewport: args.viewport,
|
|
632
633
|
accessMode: args.accessMode,
|
|
634
|
+
cdpEndpoint: args.cdpEndpoint,
|
|
635
|
+
provider: args.provider,
|
|
633
636
|
} satisfies RunIntegrationWorkerRequest);
|
|
634
637
|
const worker = spawn(
|
|
635
638
|
process.execPath,
|
|
@@ -803,6 +806,10 @@ export const runInput = SimpleCLI.input({
|
|
|
803
806
|
viewport: SimpleCLI.option(z.string().optional(), {
|
|
804
807
|
help: "Viewport size as WIDTHxHEIGHT (e.g. 1920x1080)",
|
|
805
808
|
}),
|
|
809
|
+
provider: SimpleCLI.option(z.string().optional(), {
|
|
810
|
+
help: "Browser provider (local, kernel, browserbase)",
|
|
811
|
+
aliases: ["-p"],
|
|
812
|
+
}),
|
|
806
813
|
},
|
|
807
814
|
})
|
|
808
815
|
.refine(
|
|
@@ -865,20 +872,53 @@ export const runCommand = SimpleCLI.command({
|
|
|
865
872
|
ctx.logger,
|
|
866
873
|
);
|
|
867
874
|
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
875
|
+
const providerName = resolveProviderName(input.provider);
|
|
876
|
+
let cdpEndpoint: string | undefined;
|
|
877
|
+
let providerInfo: { name: string; sessionId: string } | undefined;
|
|
878
|
+
let provider: ReturnType<typeof getCloudProviderApi> | undefined;
|
|
879
|
+
if (providerName !== "local") {
|
|
880
|
+
provider = getCloudProviderApi(providerName);
|
|
881
|
+
console.log(
|
|
882
|
+
`Creating ${providerName} browser session (session: ${ctx.session})...`,
|
|
883
|
+
);
|
|
884
|
+
const providerSession = await provider.createSession();
|
|
885
|
+
console.log(`Connecting to ${providerName} browser...`);
|
|
886
|
+
cdpEndpoint = providerSession.cdpEndpoint;
|
|
887
|
+
providerInfo = {
|
|
888
|
+
name: providerName,
|
|
889
|
+
sessionId: providerSession.sessionId,
|
|
890
|
+
};
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
try {
|
|
894
|
+
await runIntegrationFromFile(
|
|
895
|
+
{
|
|
896
|
+
integrationPath: input.integrationFile!,
|
|
897
|
+
session: ctx.session,
|
|
898
|
+
params,
|
|
899
|
+
tsconfigPath: input.tsconfig,
|
|
900
|
+
headless: cdpEndpoint ? true : (headlessMode ?? false),
|
|
901
|
+
visualize,
|
|
902
|
+
authProfileDomain: input.authProfile,
|
|
903
|
+
viewport,
|
|
904
|
+
accessMode: input.readOnly ? "read-only" : input.writeAccess ? "write-access" : (readLibrettoConfig().sessionMode ?? "write-access"),
|
|
905
|
+
cdpEndpoint,
|
|
906
|
+
provider: providerInfo,
|
|
907
|
+
},
|
|
908
|
+
ctx.logger,
|
|
909
|
+
);
|
|
910
|
+
} finally {
|
|
911
|
+
if (provider && providerInfo) {
|
|
912
|
+
try {
|
|
913
|
+
await provider.closeSession(providerInfo.sessionId);
|
|
914
|
+
} catch (cleanupErr) {
|
|
915
|
+
console.error(
|
|
916
|
+
`Failed to clean up ${providerInfo.name} session ${providerInfo.sessionId}:`,
|
|
917
|
+
cleanupErr instanceof Error ? cleanupErr.message : cleanupErr,
|
|
918
|
+
);
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
}
|
|
882
922
|
});
|
|
883
923
|
|
|
884
924
|
export const resumeInput = SimpleCLI.input({
|
|
@@ -77,6 +77,18 @@ function promptUser(
|
|
|
77
77
|
});
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
+
/** Map provider to a human-readable label for status messages. */
|
|
81
|
+
function providerLabel(provider: Provider): string {
|
|
82
|
+
const choice = PROVIDER_CHOICES.find((c) => c.provider === provider);
|
|
83
|
+
return choice?.label ?? provider;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** Extract the env var name from source like "env:GOOGLE_CLOUD_PROJECT". */
|
|
87
|
+
function sourceEnvVar(source: string): string | null {
|
|
88
|
+
if (source.startsWith("env:")) return source.slice(4);
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
|
|
80
92
|
/**
|
|
81
93
|
* If the workspace has usable credentials but no pinned model in config,
|
|
82
94
|
* write the resolved default model to `.libretto/config.json`.
|
|
@@ -92,18 +104,24 @@ function ensurePinnedDefaultModel(
|
|
|
92
104
|
}
|
|
93
105
|
|
|
94
106
|
function printHealthySummary(status: AiSetupStatus & { kind: "ready" }): void {
|
|
95
|
-
|
|
96
|
-
|
|
107
|
+
const envVar = sourceEnvVar(status.source);
|
|
108
|
+
if (envVar) {
|
|
109
|
+
console.log(
|
|
110
|
+
`✓ Detected ${envVar}. Using ${providerLabel(status.provider)}.`,
|
|
111
|
+
);
|
|
112
|
+
} else {
|
|
113
|
+
console.log(`✓ Using ${providerLabel(status.provider)} (${status.model}).`);
|
|
114
|
+
}
|
|
97
115
|
console.log(
|
|
98
|
-
"
|
|
116
|
+
"To change: npx libretto ai configure openai | anthropic | gemini | vertex",
|
|
99
117
|
);
|
|
100
118
|
}
|
|
101
119
|
|
|
102
120
|
function printInvalidAiConfigWarning(status: AiSetupStatus): void {
|
|
103
121
|
if (status.kind !== "invalid-config") return;
|
|
104
|
-
console.log("
|
|
122
|
+
console.log("! Existing AI config is invalid:");
|
|
105
123
|
for (const line of status.message.split("\n")) {
|
|
106
|
-
console.log(`
|
|
124
|
+
console.log(` ${line}`);
|
|
107
125
|
}
|
|
108
126
|
}
|
|
109
127
|
|
|
@@ -152,56 +170,54 @@ export function buildRepairPlan(status: AiSetupStatus): RepairPlan {
|
|
|
152
170
|
export function formatMissingCredentialsMessage(
|
|
153
171
|
plan: RepairPlan & { kind: "repair-missing-credentials" },
|
|
154
172
|
): string {
|
|
155
|
-
return
|
|
156
|
-
` ✗ ${plan.provider} is configured (model: ${plan.model}), but ${plan.envVar} is not set.`,
|
|
157
|
-
].join("\n");
|
|
173
|
+
return `✗ ${plan.provider} is configured (model: ${plan.model}), but ${plan.envVar} is not set.`;
|
|
158
174
|
}
|
|
159
175
|
|
|
160
176
|
function printSnapshotApiStatus(): boolean {
|
|
161
177
|
const status = resolveAiSetupStatus();
|
|
162
|
-
const envPath = join(REPO_ROOT, ".env");
|
|
163
178
|
|
|
164
|
-
console.log("\nSnapshot analysis:");
|
|
165
179
|
console.log(
|
|
166
|
-
"
|
|
180
|
+
"\nLibretto uses a sub-agent to analyze DOM snapshots. The model is determined by environment variables.",
|
|
167
181
|
);
|
|
168
|
-
console.log(` Credentials are loaded from process env and ${envPath}.`);
|
|
169
182
|
|
|
170
183
|
if (status.kind === "ready") {
|
|
171
|
-
|
|
172
|
-
printHealthySummary(
|
|
184
|
+
console.log();
|
|
185
|
+
printHealthySummary(status);
|
|
186
|
+
ensurePinnedDefaultModel(status);
|
|
173
187
|
return true;
|
|
174
188
|
}
|
|
175
189
|
|
|
176
190
|
// Provider-specific missing-credentials message
|
|
177
191
|
const plan = buildRepairPlan(status);
|
|
178
192
|
if (plan.kind === "repair-missing-credentials") {
|
|
193
|
+
console.log();
|
|
179
194
|
console.log(formatMissingCredentialsMessage(plan));
|
|
180
195
|
console.log(
|
|
181
|
-
`
|
|
196
|
+
` To fix: add ${plan.envVar} to .env, or run \`npx libretto setup\` interactively to repair.`,
|
|
182
197
|
);
|
|
183
198
|
return false;
|
|
184
199
|
}
|
|
185
200
|
|
|
186
201
|
if (plan.kind === "repair-invalid-config") {
|
|
187
202
|
printInvalidAiConfigWarning(status);
|
|
188
|
-
console.log("
|
|
203
|
+
console.log(" Run `npx libretto setup` interactively to reconfigure.");
|
|
189
204
|
return false;
|
|
190
205
|
}
|
|
191
206
|
|
|
192
|
-
console.log(
|
|
193
|
-
console.log("
|
|
194
|
-
console.log("
|
|
195
|
-
console.log("
|
|
196
|
-
console.log("
|
|
207
|
+
console.log();
|
|
208
|
+
console.log("✗ No snapshot API credentials detected.");
|
|
209
|
+
console.log(" Add one provider to .env:");
|
|
210
|
+
console.log(" OPENAI_API_KEY=...");
|
|
211
|
+
console.log(" ANTHROPIC_API_KEY=...");
|
|
212
|
+
console.log(" GEMINI_API_KEY=... # or GOOGLE_GENERATIVE_AI_API_KEY");
|
|
197
213
|
console.log(
|
|
198
|
-
"
|
|
214
|
+
" GOOGLE_CLOUD_PROJECT=... # plus application default credentials for Vertex",
|
|
199
215
|
);
|
|
200
216
|
console.log(
|
|
201
|
-
"
|
|
217
|
+
" Or run `npx libretto ai configure openai | anthropic | gemini | vertex` to set a specific model.",
|
|
202
218
|
);
|
|
203
219
|
console.log(
|
|
204
|
-
"
|
|
220
|
+
" Run `npx libretto setup` interactively to set up credentials.",
|
|
205
221
|
);
|
|
206
222
|
return false;
|
|
207
223
|
}
|
|
@@ -222,11 +238,11 @@ function writeEnvVar(envVar: string, value: string, envPath: string): void {
|
|
|
222
238
|
() => envLine,
|
|
223
239
|
);
|
|
224
240
|
writeFileSync(envPath, updated);
|
|
225
|
-
console.log(`\n
|
|
241
|
+
console.log(`\n✓ Updated ${envVar} in ${envPath}`);
|
|
226
242
|
} else {
|
|
227
243
|
const separator = envContent && !envContent.endsWith("\n") ? "\n" : "";
|
|
228
244
|
appendFileSync(envPath, `${separator}${envLine}\n`);
|
|
229
|
-
console.log(`\n
|
|
245
|
+
console.log(`\n✓ Added ${envVar} to ${envPath}`);
|
|
230
246
|
}
|
|
231
247
|
|
|
232
248
|
process.env[envVar] = value;
|
|
@@ -244,13 +260,13 @@ async function promptForCredential(
|
|
|
244
260
|
envPath: string,
|
|
245
261
|
modelOverride?: string,
|
|
246
262
|
): Promise<boolean> {
|
|
247
|
-
console.log(`\n
|
|
248
|
-
console.log(
|
|
263
|
+
console.log(`\n${choice.label} selected.`);
|
|
264
|
+
console.log(`${choice.envHint}\n`);
|
|
249
265
|
|
|
250
|
-
const apiKeyValue = await promptUser(rl, `
|
|
266
|
+
const apiKeyValue = await promptUser(rl, `Enter your ${choice.envVar}: `);
|
|
251
267
|
|
|
252
268
|
if (!apiKeyValue) {
|
|
253
|
-
console.log("\
|
|
269
|
+
console.log("\nNo value entered. Skipping API key setup.");
|
|
254
270
|
return false;
|
|
255
271
|
}
|
|
256
272
|
|
|
@@ -259,7 +275,10 @@ async function promptForCredential(
|
|
|
259
275
|
|
|
260
276
|
const model = modelOverride ?? DEFAULT_SNAPSHOT_MODELS[choice.provider];
|
|
261
277
|
writeAiConfig(model);
|
|
262
|
-
console.log(
|
|
278
|
+
console.log(`✓ Snapshot API ready: ${model}`);
|
|
279
|
+
console.log(
|
|
280
|
+
"To change: npx libretto ai configure openai | anthropic | gemini | vertex",
|
|
281
|
+
);
|
|
263
282
|
return true;
|
|
264
283
|
}
|
|
265
284
|
|
|
@@ -272,14 +291,14 @@ async function promptProviderSelection(
|
|
|
272
291
|
envPath: string,
|
|
273
292
|
): Promise<boolean> {
|
|
274
293
|
console.log(
|
|
275
|
-
"
|
|
294
|
+
"Which model provider would you like to use for snapshot analysis?\n",
|
|
276
295
|
);
|
|
277
296
|
for (const choice of PROVIDER_CHOICES) {
|
|
278
|
-
console.log(`
|
|
297
|
+
console.log(` ${choice.key}) ${choice.label}`);
|
|
279
298
|
}
|
|
280
|
-
console.log("
|
|
299
|
+
console.log(" s) Skip for now\n");
|
|
281
300
|
|
|
282
|
-
const answer = await promptUser(rl, "
|
|
301
|
+
const answer = await promptUser(rl, "Choice: ");
|
|
283
302
|
|
|
284
303
|
if (answer.toLowerCase() === "s" || !answer) {
|
|
285
304
|
printSkipMessage();
|
|
@@ -288,7 +307,7 @@ async function promptProviderSelection(
|
|
|
288
307
|
|
|
289
308
|
const selected = PROVIDER_CHOICES.find((choice) => choice.key === answer);
|
|
290
309
|
if (!selected) {
|
|
291
|
-
console.log(`\
|
|
310
|
+
console.log(`\nUnknown choice "${answer}". Skipping API setup.`);
|
|
292
311
|
return false;
|
|
293
312
|
}
|
|
294
313
|
|
|
@@ -297,14 +316,14 @@ async function promptProviderSelection(
|
|
|
297
316
|
|
|
298
317
|
function printSkipMessage(): void {
|
|
299
318
|
console.log(
|
|
300
|
-
"\
|
|
319
|
+
"\nSkipped. You can set up API credentials later by rerunning `npx libretto setup`.",
|
|
301
320
|
);
|
|
302
|
-
console.log("
|
|
303
|
-
console.log("
|
|
304
|
-
console.log("
|
|
305
|
-
console.log("
|
|
321
|
+
console.log("Or add credentials directly to your .env file:");
|
|
322
|
+
console.log(" OPENAI_API_KEY=...");
|
|
323
|
+
console.log(" ANTHROPIC_API_KEY=...");
|
|
324
|
+
console.log(" GEMINI_API_KEY=...");
|
|
306
325
|
console.log(
|
|
307
|
-
"
|
|
326
|
+
" Or run `npx libretto ai configure openai | anthropic | gemini | vertex` to set a specific model.",
|
|
308
327
|
);
|
|
309
328
|
}
|
|
310
329
|
|
|
@@ -312,13 +331,14 @@ async function runInteractiveApiSetup(): Promise<void> {
|
|
|
312
331
|
const status = resolveAiSetupStatus();
|
|
313
332
|
const envPath = join(REPO_ROOT, ".env");
|
|
314
333
|
|
|
315
|
-
console.log(
|
|
316
|
-
|
|
317
|
-
|
|
334
|
+
console.log(
|
|
335
|
+
"\nLibretto uses a sub-agent to analyze DOM snapshots. The model is determined by environment variables.",
|
|
336
|
+
);
|
|
318
337
|
|
|
319
338
|
if (status.kind === "ready") {
|
|
320
|
-
|
|
321
|
-
printHealthySummary(
|
|
339
|
+
console.log();
|
|
340
|
+
printHealthySummary(status);
|
|
341
|
+
ensurePinnedDefaultModel(status);
|
|
322
342
|
return;
|
|
323
343
|
}
|
|
324
344
|
|
|
@@ -334,12 +354,12 @@ async function runInteractiveApiSetup(): Promise<void> {
|
|
|
334
354
|
if (plan.kind === "repair-missing-credentials") {
|
|
335
355
|
console.log(formatMissingCredentialsMessage(plan));
|
|
336
356
|
console.log("");
|
|
337
|
-
console.log("
|
|
338
|
-
console.log(`
|
|
339
|
-
console.log("
|
|
340
|
-
console.log("
|
|
357
|
+
console.log("How would you like to fix this?\n");
|
|
358
|
+
console.log(` 1) Enter ${plan.envVar}`);
|
|
359
|
+
console.log(" 2) Switch to a different provider");
|
|
360
|
+
console.log(" s) Skip for now\n");
|
|
341
361
|
|
|
342
|
-
const answer = await promptUser(rl, "
|
|
362
|
+
const answer = await promptUser(rl, "Choice: ");
|
|
343
363
|
|
|
344
364
|
if (answer === "1") {
|
|
345
365
|
const matchingChoice = PROVIDER_CHOICES.find(
|
|
@@ -365,14 +385,14 @@ async function runInteractiveApiSetup(): Promise<void> {
|
|
|
365
385
|
if (plan.kind === "repair-invalid-config") {
|
|
366
386
|
printInvalidAiConfigWarning(status);
|
|
367
387
|
console.log(
|
|
368
|
-
"\
|
|
388
|
+
"\nWould you like to reconfigure with a fresh provider selection?\n",
|
|
369
389
|
);
|
|
370
390
|
await promptProviderSelection(rl, envPath);
|
|
371
391
|
return;
|
|
372
392
|
}
|
|
373
393
|
|
|
374
394
|
// ── Unconfigured: standard first-run flow ──
|
|
375
|
-
console.log("
|
|
395
|
+
console.log("✗ No snapshot API credentials detected.\n");
|
|
376
396
|
await promptProviderSelection(rl, envPath);
|
|
377
397
|
} finally {
|
|
378
398
|
rl.close();
|
|
@@ -380,16 +400,16 @@ async function runInteractiveApiSetup(): Promise<void> {
|
|
|
380
400
|
}
|
|
381
401
|
|
|
382
402
|
function installBrowsers(): void {
|
|
383
|
-
console.log("
|
|
403
|
+
console.log("Installing Playwright Chromium...");
|
|
384
404
|
const result = spawnSync("npx", ["playwright", "install", "chromium"], {
|
|
385
405
|
stdio: "inherit",
|
|
386
406
|
shell: true,
|
|
387
407
|
});
|
|
388
408
|
if (result.status === 0) {
|
|
389
|
-
console.log("
|
|
409
|
+
console.log("✓ Playwright Chromium installed");
|
|
390
410
|
} else {
|
|
391
411
|
console.error(
|
|
392
|
-
"
|
|
412
|
+
"✗ Failed to install Playwright Chromium. Run manually: npx playwright install chromium",
|
|
393
413
|
);
|
|
394
414
|
}
|
|
395
415
|
}
|
|
@@ -421,9 +441,6 @@ function copySkills(): void {
|
|
|
421
441
|
const agentDirs = detectAgentDirs(REPO_ROOT);
|
|
422
442
|
|
|
423
443
|
if (agentDirs.length === 0) {
|
|
424
|
-
console.log(
|
|
425
|
-
"\nSkills: No .agents/ or .claude/ directory found in repo root — skipping.",
|
|
426
|
-
);
|
|
427
444
|
return;
|
|
428
445
|
}
|
|
429
446
|
|
|
@@ -431,7 +448,7 @@ function copySkills(): void {
|
|
|
431
448
|
try {
|
|
432
449
|
skillsRoot = getPackageSkillsRoot();
|
|
433
450
|
} catch (e) {
|
|
434
|
-
console.error(
|
|
451
|
+
console.error(`✗ ${e instanceof Error ? e.message : String(e)}`);
|
|
435
452
|
return;
|
|
436
453
|
}
|
|
437
454
|
|
|
@@ -452,7 +469,7 @@ function copySkills(): void {
|
|
|
452
469
|
cpSync(sourceDir, skillDest, { recursive: true });
|
|
453
470
|
const fileCount = readdirSync(skillDest).length;
|
|
454
471
|
console.log(
|
|
455
|
-
|
|
472
|
+
`✓ Copied ${fileCount} skill files to ${agentName}/skills/${skillName}/`,
|
|
456
473
|
);
|
|
457
474
|
}
|
|
458
475
|
}
|
|
@@ -473,13 +490,12 @@ export const setupCommand = SimpleCLI.command({
|
|
|
473
490
|
})
|
|
474
491
|
.input(setupInput)
|
|
475
492
|
.handle(async ({ input }) => {
|
|
476
|
-
console.log("Setting up libretto...\n");
|
|
477
493
|
ensureLibrettoSetup();
|
|
478
494
|
|
|
479
495
|
if (!input.skipBrowsers) {
|
|
480
496
|
installBrowsers();
|
|
481
497
|
} else {
|
|
482
|
-
console.log("
|
|
498
|
+
console.log("Skipping browser installation (--skip-browsers)");
|
|
483
499
|
}
|
|
484
500
|
|
|
485
501
|
copySkills();
|
|
@@ -495,5 +511,6 @@ export const setupCommand = SimpleCLI.command({
|
|
|
495
511
|
}
|
|
496
512
|
}
|
|
497
513
|
|
|
514
|
+
console.log(`\nConfig set up at ${LIBRETTO_CONFIG_PATH}`);
|
|
498
515
|
console.log("\n✓ libretto setup complete");
|
|
499
516
|
});
|
|
@@ -57,7 +57,9 @@ function printOpenSessions(sessions: SessionState[]): void {
|
|
|
57
57
|
|
|
58
58
|
for (const session of sessions) {
|
|
59
59
|
const statusLabel = session.status ? ` [${session.status}]` : "";
|
|
60
|
-
const endpoint =
|
|
60
|
+
const endpoint = session.provider
|
|
61
|
+
? `${session.provider.name} (${session.cdpEndpoint})`
|
|
62
|
+
: `http://127.0.0.1:${session.port}`;
|
|
61
63
|
console.log(` ${session.session}${statusLabel} — ${endpoint}`);
|
|
62
64
|
}
|
|
63
65
|
}
|