libretto 0.6.13 → 0.6.14
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/auth.js +24 -33
- package/dist/cli/commands/billing.js +3 -5
- package/dist/cli/commands/browser.js +3 -6
- package/dist/cli/commands/deploy.js +54 -45
- package/dist/cli/commands/execution.js +6 -3
- package/dist/cli/commands/experiments.js +1 -1
- package/dist/cli/commands/setup.js +1 -1
- package/dist/cli/commands/shared.js +1 -1
- package/dist/cli/commands/snapshot.js +1 -1
- package/dist/cli/commands/status.js +1 -1
- package/dist/cli/core/auth-fetch.js +11 -6
- package/dist/cli/core/browser.js +10 -5
- package/dist/cli/core/daemon/daemon.js +63 -10
- package/dist/cli/core/daemon/exec-repl.js +133 -0
- package/dist/cli/core/daemon/exec.js +6 -21
- package/dist/cli/core/daemon/ipc.js +47 -4
- package/dist/cli/core/daemon/ipc.spec.js +21 -0
- package/dist/cli/core/exec-compiler.js +8 -3
- package/dist/cli/core/providers/index.js +13 -4
- package/dist/cli/core/providers/libretto-cloud.js +178 -26
- package/dist/cli/router.js +9 -4
- package/dist/shared/ipc/socket-transport.d.ts +2 -1
- package/dist/shared/ipc/socket-transport.js +16 -5
- package/dist/shared/ipc/socket-transport.spec.js +5 -0
- package/package.json +2 -2
- package/skills/libretto/SKILL.md +33 -29
- package/skills/libretto/references/code-generation-rules.md +6 -0
- package/skills/libretto/references/configuration-file-reference.md +8 -0
- package/skills/libretto/references/site-security-review.md +6 -6
- package/skills/libretto-readonly/SKILL.md +1 -1
- package/src/cli/commands/auth.ts +24 -33
- package/src/cli/commands/billing.ts +3 -5
- package/src/cli/commands/browser.ts +5 -9
- package/src/cli/commands/deploy.ts +55 -49
- package/src/cli/commands/execution.ts +6 -3
- package/src/cli/commands/experiments.ts +1 -1
- package/src/cli/commands/setup.ts +1 -1
- package/src/cli/commands/shared.ts +1 -1
- package/src/cli/commands/snapshot.ts +1 -1
- package/src/cli/commands/status.ts +1 -1
- package/src/cli/core/auth-fetch.ts +9 -4
- package/src/cli/core/browser.ts +12 -5
- package/src/cli/core/daemon/daemon.ts +81 -9
- package/src/cli/core/daemon/exec-repl.ts +189 -0
- package/src/cli/core/daemon/exec.ts +8 -43
- package/src/cli/core/daemon/ipc.spec.ts +27 -0
- package/src/cli/core/daemon/ipc.ts +76 -7
- package/src/cli/core/exec-compiler.ts +8 -3
- package/src/cli/core/providers/index.ts +17 -4
- package/src/cli/core/providers/libretto-cloud.ts +224 -36
- package/src/cli/router.ts +9 -4
- package/src/shared/ipc/socket-transport.spec.ts +6 -0
- package/src/shared/ipc/socket-transport.ts +20 -5
- package/dist/cli/framework/simple-cli.js +0 -880
- package/src/cli/framework/simple-cli.ts +0 -1459
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { SimpleCLI } from "
|
|
2
|
+
import { SimpleCLI } from "affordance";
|
|
3
3
|
import {
|
|
4
4
|
ApiCallError,
|
|
5
5
|
betterAuthCall,
|
|
6
|
-
HOSTED_API_URL,
|
|
7
6
|
NOT_AUTHENTICATED_MESSAGE,
|
|
8
7
|
orpcCall,
|
|
9
8
|
pickCredential,
|
|
10
|
-
resolveApiUrl
|
|
9
|
+
resolveApiUrl,
|
|
10
|
+
resolveHostedApiUrl
|
|
11
11
|
} from "../core/auth-fetch.js";
|
|
12
12
|
import {
|
|
13
13
|
authStatePath,
|
|
@@ -88,10 +88,9 @@ async function issueApiKey(apiUrl, name, credential) {
|
|
|
88
88
|
return data;
|
|
89
89
|
}
|
|
90
90
|
const signupCommand = SimpleCLI.command({
|
|
91
|
-
description: "Create a new hosted-platform account and organization"
|
|
92
|
-
experimental: true
|
|
91
|
+
description: "Create a new hosted-platform account and organization"
|
|
93
92
|
}).input(SimpleCLI.input({ positionals: [], named: {} })).handle(async () => {
|
|
94
|
-
const apiUrl =
|
|
93
|
+
const apiUrl = resolveHostedApiUrl();
|
|
95
94
|
console.log("Sign up for libretto cloud");
|
|
96
95
|
console.log();
|
|
97
96
|
console.log("Heads up: a libretto user can only belong to one organization.");
|
|
@@ -103,7 +102,7 @@ const signupCommand = SimpleCLI.command({
|
|
|
103
102
|
const name = await prompt("Your name:");
|
|
104
103
|
if (name.toLowerCase() === "q" || name.length === 0) {
|
|
105
104
|
console.log(
|
|
106
|
-
"OK \u2014 ask an existing teammate to run `libretto
|
|
105
|
+
"OK \u2014 ask an existing teammate to run `libretto cloud auth invite <your-email>` and then run `libretto cloud auth accept-invite <slug> <invitation-id>` from this machine."
|
|
107
106
|
);
|
|
108
107
|
return;
|
|
109
108
|
}
|
|
@@ -162,14 +161,13 @@ const signupCommand = SimpleCLI.command({
|
|
|
162
161
|
console.log(`Session saved to ${authStatePath()}`);
|
|
163
162
|
console.log();
|
|
164
163
|
console.log("To generate an API key, run:");
|
|
165
|
-
console.log(" libretto
|
|
164
|
+
console.log(" libretto cloud auth api-key issue --label <label>");
|
|
166
165
|
console.log("Then add LIBRETTO_API_KEY=<key> to your project's .env file.");
|
|
167
166
|
});
|
|
168
167
|
const loginCommand = SimpleCLI.command({
|
|
169
|
-
description: "Sign in to an existing hosted-platform account"
|
|
170
|
-
experimental: true
|
|
168
|
+
description: "Sign in to an existing hosted-platform account"
|
|
171
169
|
}).input(SimpleCLI.input({ positionals: [], named: {} })).handle(async () => {
|
|
172
|
-
const apiUrl =
|
|
170
|
+
const apiUrl = resolveHostedApiUrl();
|
|
173
171
|
const email = await prompt("Email:");
|
|
174
172
|
const password = await promptPassword("Password:");
|
|
175
173
|
const { data, setCookie } = await betterAuthCall({
|
|
@@ -218,8 +216,7 @@ const loginCommand = SimpleCLI.command({
|
|
|
218
216
|
}
|
|
219
217
|
});
|
|
220
218
|
const logoutCommand = SimpleCLI.command({
|
|
221
|
-
description: "Clear local libretto credentials"
|
|
222
|
-
experimental: true
|
|
219
|
+
description: "Clear local libretto credentials"
|
|
223
220
|
}).handle(async () => {
|
|
224
221
|
const state = await readAuthState();
|
|
225
222
|
if (state?.session?.cookie) {
|
|
@@ -236,8 +233,7 @@ const logoutCommand = SimpleCLI.command({
|
|
|
236
233
|
console.log("Logged out.");
|
|
237
234
|
});
|
|
238
235
|
const inviteCommand = SimpleCLI.command({
|
|
239
|
-
description: "Invite a teammate to your active organization"
|
|
240
|
-
experimental: true
|
|
236
|
+
description: "Invite a teammate to your active organization"
|
|
241
237
|
}).input(
|
|
242
238
|
SimpleCLI.input({
|
|
243
239
|
positionals: [
|
|
@@ -287,12 +283,11 @@ const inviteCommand = SimpleCLI.command({
|
|
|
287
283
|
console.log();
|
|
288
284
|
console.log("Tell them to run:");
|
|
289
285
|
console.log(
|
|
290
|
-
` libretto
|
|
286
|
+
` libretto cloud auth accept-invite ${orgSlug} ${data.id}`
|
|
291
287
|
);
|
|
292
288
|
});
|
|
293
289
|
const acceptInviteCommand = SimpleCLI.command({
|
|
294
|
-
description: "Accept an organization invitation"
|
|
295
|
-
experimental: true
|
|
290
|
+
description: "Accept an organization invitation"
|
|
296
291
|
}).input(
|
|
297
292
|
SimpleCLI.input({
|
|
298
293
|
positionals: [
|
|
@@ -313,7 +308,7 @@ const acceptInviteCommand = SimpleCLI.command({
|
|
|
313
308
|
})
|
|
314
309
|
).handle(async ({ input }) => {
|
|
315
310
|
const stored = await readAuthState();
|
|
316
|
-
const apiUrl =
|
|
311
|
+
const apiUrl = resolveHostedApiUrl();
|
|
317
312
|
const credential = pickCredential(stored);
|
|
318
313
|
const expectedTenantSlug = input.tenantSlug;
|
|
319
314
|
if (credential.source !== "none") {
|
|
@@ -328,7 +323,7 @@ const acceptInviteCommand = SimpleCLI.command({
|
|
|
328
323
|
[
|
|
329
324
|
"You're already a member of an organization.",
|
|
330
325
|
"A libretto user can only belong to one organization at a time.",
|
|
331
|
-
"To accept this invite: log out, delete the existing account, and re-run `auth accept-invite` with a new account (or a fresh email)."
|
|
326
|
+
"To accept this invite: log out, delete the existing account, and re-run `libretto cloud auth accept-invite` with a new account (or a fresh email)."
|
|
332
327
|
].join("\n")
|
|
333
328
|
);
|
|
334
329
|
}
|
|
@@ -381,12 +376,11 @@ const acceptInviteCommand = SimpleCLI.command({
|
|
|
381
376
|
console.log();
|
|
382
377
|
console.log("Email verified. You're logged in and a member of the organization.");
|
|
383
378
|
console.log("To generate an API key, run:");
|
|
384
|
-
console.log(" libretto
|
|
379
|
+
console.log(" libretto cloud auth api-key issue --label <label>");
|
|
385
380
|
console.log("Then add LIBRETTO_API_KEY=<key> to your project's .env file.");
|
|
386
381
|
});
|
|
387
382
|
const apiKeyIssueCommand = SimpleCLI.command({
|
|
388
|
-
description: "Issue a new API key for the active organization"
|
|
389
|
-
experimental: true
|
|
383
|
+
description: "Issue a new API key for the active organization"
|
|
390
384
|
}).input(
|
|
391
385
|
SimpleCLI.input({
|
|
392
386
|
positionals: [],
|
|
@@ -416,8 +410,7 @@ const apiKeyIssueCommand = SimpleCLI.command({
|
|
|
416
410
|
);
|
|
417
411
|
});
|
|
418
412
|
const apiKeyListCommand = SimpleCLI.command({
|
|
419
|
-
description: "List API keys for the active organization"
|
|
420
|
-
experimental: true
|
|
413
|
+
description: "List API keys for the active organization"
|
|
421
414
|
}).handle(async () => {
|
|
422
415
|
const stored = await readAuthState();
|
|
423
416
|
const apiUrl = resolveApiUrl(stored);
|
|
@@ -444,13 +437,12 @@ const apiKeyListCommand = SimpleCLI.command({
|
|
|
444
437
|
}
|
|
445
438
|
});
|
|
446
439
|
const apiKeyRevokeCommand = SimpleCLI.command({
|
|
447
|
-
description: "Revoke an API key by id"
|
|
448
|
-
experimental: true
|
|
440
|
+
description: "Revoke an API key by id"
|
|
449
441
|
}).input(
|
|
450
442
|
SimpleCLI.input({
|
|
451
443
|
positionals: [
|
|
452
444
|
SimpleCLI.positional("id", z.string().min(1), {
|
|
453
|
-
help: "API key id (from `auth api-key list`)."
|
|
445
|
+
help: "API key id (from `libretto cloud auth api-key list`)."
|
|
454
446
|
})
|
|
455
447
|
],
|
|
456
448
|
named: {}
|
|
@@ -470,24 +462,23 @@ const apiKeyRevokeCommand = SimpleCLI.command({
|
|
|
470
462
|
});
|
|
471
463
|
console.log(`API key ${input.id} revoked.`);
|
|
472
464
|
console.log(
|
|
473
|
-
"If this key was in your .env, remove the LIBRETTO_API_KEY value and issue a new one with `auth api-key issue --label <label>`."
|
|
465
|
+
"If this key was in your .env, remove the LIBRETTO_API_KEY value and issue a new one with `libretto cloud auth api-key issue --label <label>`."
|
|
474
466
|
);
|
|
475
467
|
});
|
|
476
468
|
const whoamiCommand = SimpleCLI.command({
|
|
477
|
-
description: "Print the active session and credential source"
|
|
478
|
-
experimental: true
|
|
469
|
+
description: "Print the active session and credential source"
|
|
479
470
|
}).handle(async () => {
|
|
480
471
|
const stored = await readAuthState();
|
|
481
472
|
const credential = pickCredential(stored);
|
|
482
473
|
const envKey = process.env.LIBRETTO_API_KEY?.trim();
|
|
483
474
|
if (credential.source === "none") {
|
|
484
475
|
console.log(
|
|
485
|
-
"Not authenticated. Run `libretto
|
|
476
|
+
"Not authenticated. Run `libretto cloud auth signup`, `libretto cloud auth login`, or set LIBRETTO_API_KEY in your env."
|
|
486
477
|
);
|
|
487
478
|
return;
|
|
488
479
|
}
|
|
489
480
|
console.log(`Auth source: ${credential.source}`);
|
|
490
|
-
console.log(`API URL: ${
|
|
481
|
+
console.log(`API URL: ${resolveHostedApiUrl()}`);
|
|
491
482
|
console.log(
|
|
492
483
|
`LIBRETTO_API_KEY: ${envKey ? `set in env (${envKey.slice(0, 6)}\u2026)` : "not set in env"}`
|
|
493
484
|
);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { SimpleCLI } from "
|
|
1
|
+
import { SimpleCLI } from "affordance";
|
|
2
2
|
import {
|
|
3
3
|
NOT_AUTHENTICATED_MESSAGE,
|
|
4
4
|
orpcCall,
|
|
@@ -20,8 +20,7 @@ function formatLimit(limit) {
|
|
|
20
20
|
return limit === null ? "\u221E" : String(limit);
|
|
21
21
|
}
|
|
22
22
|
const billingPortalCommand = SimpleCLI.command({
|
|
23
|
-
description: "Open the libretto plans page (current plan + switch options)"
|
|
24
|
-
experimental: true
|
|
23
|
+
description: "Open the libretto plans page (current plan + switch options)"
|
|
25
24
|
}).handle(async () => {
|
|
26
25
|
const { apiUrl, credential } = await requireAuth();
|
|
27
26
|
const { url } = await orpcCall({
|
|
@@ -40,8 +39,7 @@ const billingPortalCommand = SimpleCLI.command({
|
|
|
40
39
|
);
|
|
41
40
|
});
|
|
42
41
|
const billingStatusCommand = SimpleCLI.command({
|
|
43
|
-
description: "Print the current plan, status, and browser-hour usage"
|
|
44
|
-
experimental: true
|
|
42
|
+
description: "Print the current plan, status, and browser-hour usage"
|
|
45
43
|
}).handle(async () => {
|
|
46
44
|
const { apiUrl, credential } = await requireAuth();
|
|
47
45
|
const sub = await orpcCall({
|
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
validateSessionName
|
|
20
20
|
} from "../core/session.js";
|
|
21
21
|
import { warnIfInstalledSkillOutOfDate } from "../core/skill-version.js";
|
|
22
|
-
import { SimpleCLI } from "
|
|
22
|
+
import { SimpleCLI } from "affordance";
|
|
23
23
|
import {
|
|
24
24
|
sessionOption,
|
|
25
25
|
withAutoSession,
|
|
@@ -51,8 +51,8 @@ function resolveRequestedSessionMode(readOnly, writeAccess) {
|
|
|
51
51
|
}
|
|
52
52
|
const openInput = SimpleCLI.input({
|
|
53
53
|
positionals: [
|
|
54
|
-
SimpleCLI.positional("url", z.string().
|
|
55
|
-
help: "URL to open"
|
|
54
|
+
SimpleCLI.positional("url", z.string().default("about:blank"), {
|
|
55
|
+
help: "URL to open (defaults to about:blank)"
|
|
56
56
|
})
|
|
57
57
|
],
|
|
58
58
|
named: {
|
|
@@ -80,9 +80,6 @@ const openInput = SimpleCLI.input({
|
|
|
80
80
|
})
|
|
81
81
|
}
|
|
82
82
|
}).refine(
|
|
83
|
-
(input) => Boolean(input.url),
|
|
84
|
-
`Usage: ${librettoCommand("open <url> [--headless] [--read-only|--write-access] [--auth-profile <domain>] [--viewport WxH] [--session <name>]")}`
|
|
85
|
-
).refine(
|
|
86
83
|
(input) => !(input.headed && input.headless),
|
|
87
84
|
"Cannot pass both --headed and --headless."
|
|
88
85
|
).refine(
|
|
@@ -1,31 +1,51 @@
|
|
|
1
1
|
import { randomBytes } from "node:crypto";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
orpcCall,
|
|
5
|
+
resolveApiUrl
|
|
6
|
+
} from "../core/auth-fetch.js";
|
|
4
7
|
import { buildHostedDeployTarball } from "../core/deploy-artifact.js";
|
|
5
|
-
import {
|
|
8
|
+
import { readAuthState } from "../core/auth-storage.js";
|
|
9
|
+
import { SimpleCLI } from "affordance";
|
|
6
10
|
function generateDeploymentName() {
|
|
7
11
|
return `deploy-${Date.now().toString(36)}-${randomBytes(4).toString("hex")}`;
|
|
8
12
|
}
|
|
9
|
-
function
|
|
10
|
-
|
|
13
|
+
function deployApiKeyRequiredMessage(hasStoredSession) {
|
|
14
|
+
if (hasStoredSession) {
|
|
15
|
+
return [
|
|
16
|
+
"LIBRETTO_API_KEY is required to deploy to Libretto Cloud.",
|
|
17
|
+
"You are logged in locally, but deploy endpoints require API-key auth.",
|
|
18
|
+
" \u2022 Generate a key: run `libretto cloud auth api-key issue --label <label>`.",
|
|
19
|
+
" \u2022 Add it to your project .env file: `LIBRETTO_API_KEY=<issued-key>`."
|
|
20
|
+
].join("\n");
|
|
21
|
+
}
|
|
22
|
+
return [
|
|
23
|
+
"LIBRETTO_API_KEY is required to deploy to Libretto Cloud.",
|
|
24
|
+
"No local cloud session was found.",
|
|
25
|
+
" \u2022 New account: run `libretto cloud auth signup`, then verify your email.",
|
|
26
|
+
" \u2022 Existing account: run `libretto cloud auth login`.",
|
|
27
|
+
" \u2022 Generate a key: run `libretto cloud auth api-key issue --label <label>`.",
|
|
28
|
+
" \u2022 Add it to your project .env file: `LIBRETTO_API_KEY=<issued-key>`."
|
|
29
|
+
].join("\n");
|
|
30
|
+
}
|
|
31
|
+
async function requireDeployApiKey() {
|
|
32
|
+
const apiKey = process.env.LIBRETTO_API_KEY?.trim();
|
|
11
33
|
if (!apiKey) {
|
|
12
|
-
throw new Error(
|
|
13
|
-
"LIBRETTO_API_KEY environment variable is required."
|
|
14
|
-
);
|
|
34
|
+
throw new Error(deployApiKeyRequiredMessage(await hasStoredCloudSession()));
|
|
15
35
|
}
|
|
16
|
-
return {
|
|
36
|
+
return {
|
|
37
|
+
apiUrl: resolveApiUrl(null),
|
|
38
|
+
credential: { source: "env-api-key", apiKey }
|
|
39
|
+
};
|
|
17
40
|
}
|
|
18
|
-
async function
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
},
|
|
25
|
-
body: JSON.stringify({ json: input })
|
|
26
|
-
});
|
|
41
|
+
async function hasStoredCloudSession() {
|
|
42
|
+
try {
|
|
43
|
+
return Boolean((await readAuthState())?.session);
|
|
44
|
+
} catch {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
27
47
|
}
|
|
28
|
-
async function pollDeployment(apiUrl,
|
|
48
|
+
async function pollDeployment(apiUrl, credential, deploymentId, pollIntervalMs, maxWaitMs) {
|
|
29
49
|
const start = Date.now();
|
|
30
50
|
const workflowWaitMs = 6e4;
|
|
31
51
|
let status = "building";
|
|
@@ -37,18 +57,14 @@ async function pollDeployment(apiUrl, apiKey, deploymentId, pollIntervalMs, maxW
|
|
|
37
57
|
if (status === "ready" && workflows?.length) break;
|
|
38
58
|
if (status === "ready" && readyAt && Date.now() - readyAt > workflowWaitMs) break;
|
|
39
59
|
await new Promise((r) => setTimeout(r, pollIntervalMs));
|
|
40
|
-
|
|
41
|
-
|
|
60
|
+
deployment = await orpcCall({
|
|
61
|
+
apiUrl,
|
|
62
|
+
path: "/v1/deployments/sync",
|
|
63
|
+
input: { id: deploymentId },
|
|
64
|
+
credential
|
|
42
65
|
});
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
throw new Error(
|
|
46
|
-
`Failed to sync deployment status (${res.status}): ${JSON.stringify(body)}`
|
|
47
|
-
);
|
|
48
|
-
}
|
|
49
|
-
status = body.json.status;
|
|
50
|
-
workflows = body.json.workflows;
|
|
51
|
-
deployment = body.json;
|
|
66
|
+
status = deployment.status;
|
|
67
|
+
workflows = deployment.workflows;
|
|
52
68
|
if (status === "ready" && readyAt === null) readyAt = Date.now();
|
|
53
69
|
process.stdout.write(".");
|
|
54
70
|
}
|
|
@@ -88,10 +104,9 @@ const deployInput = SimpleCLI.input({
|
|
|
88
104
|
}
|
|
89
105
|
});
|
|
90
106
|
const deployCommand = SimpleCLI.command({
|
|
91
|
-
description: "Deploy workflows to the hosted platform"
|
|
92
|
-
experimental: true
|
|
107
|
+
description: "Deploy workflows to the hosted platform"
|
|
93
108
|
}).input(deployInput).handle(async ({ input }) => {
|
|
94
|
-
const { apiUrl,
|
|
109
|
+
const { apiUrl, credential } = await requireDeployApiKey();
|
|
95
110
|
const deploymentName = generateDeploymentName();
|
|
96
111
|
console.log("Bundling hosted deployment artifact...");
|
|
97
112
|
const { entryPoint, source } = await buildHostedDeployTarball({
|
|
@@ -106,26 +121,20 @@ const deployCommand = SimpleCLI.command({
|
|
|
106
121
|
};
|
|
107
122
|
if (input.description) createPayload.description = input.description;
|
|
108
123
|
console.log("Uploading deployment...");
|
|
109
|
-
const
|
|
124
|
+
const body = await orpcCall({
|
|
110
125
|
apiUrl,
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
);
|
|
115
|
-
const
|
|
116
|
-
if (res.status !== 200) {
|
|
117
|
-
throw new Error(
|
|
118
|
-
`Failed to create deployment (${res.status}): ${JSON.stringify(body)}`
|
|
119
|
-
);
|
|
120
|
-
}
|
|
121
|
-
const { deployment_id, status } = body.json;
|
|
126
|
+
path: "/v1/deployments/create",
|
|
127
|
+
input: createPayload,
|
|
128
|
+
credential
|
|
129
|
+
});
|
|
130
|
+
const { deployment_id, status } = body;
|
|
122
131
|
console.log(`Deployment created: ${deployment_id}`);
|
|
123
132
|
console.log(`Status: ${status}`);
|
|
124
133
|
if (status === "building") {
|
|
125
134
|
process.stdout.write("Waiting for build");
|
|
126
135
|
const deployment = await pollDeployment(
|
|
127
136
|
apiUrl,
|
|
128
|
-
|
|
137
|
+
credential,
|
|
129
138
|
deployment_id,
|
|
130
139
|
1e4,
|
|
131
140
|
5 * 60 * 1e3
|
|
@@ -27,7 +27,10 @@ import { warnIfInstalledSkillOutOfDate } from "../core/skill-version.js";
|
|
|
27
27
|
import { readLibrettoConfig } from "../core/config.js";
|
|
28
28
|
import { librettoCommand } from "../../shared/package-manager.js";
|
|
29
29
|
import { renderSnapshotDiff } from "../../shared/snapshot/diff-snapshots.js";
|
|
30
|
-
import {
|
|
30
|
+
import {
|
|
31
|
+
getProviderStartupTimeoutMs,
|
|
32
|
+
resolveProviderName
|
|
33
|
+
} from "../core/providers/index.js";
|
|
31
34
|
import { getAbsoluteIntegrationPath } from "../core/workflow-runtime.js";
|
|
32
35
|
import {
|
|
33
36
|
compileExecFunction,
|
|
@@ -42,7 +45,7 @@ import {
|
|
|
42
45
|
readNetworkLog,
|
|
43
46
|
wrapPageForActionLogging
|
|
44
47
|
} from "../core/telemetry.js";
|
|
45
|
-
import { SimpleCLI } from "
|
|
48
|
+
import { SimpleCLI } from "affordance";
|
|
46
49
|
import {
|
|
47
50
|
pageOption,
|
|
48
51
|
sessionOption,
|
|
@@ -459,7 +462,7 @@ async function runIntegrationFromFile(args, logger) {
|
|
|
459
462
|
},
|
|
460
463
|
logger,
|
|
461
464
|
logPath: runLogPath,
|
|
462
|
-
startupTimeoutMs:
|
|
465
|
+
startupTimeoutMs: getProviderStartupTimeoutMs(args.providerName),
|
|
463
466
|
handlers
|
|
464
467
|
});
|
|
465
468
|
writeSessionState(
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
resolveExperiments,
|
|
7
7
|
setExperimentEnabled
|
|
8
8
|
} from "../core/experiments.js";
|
|
9
|
-
import { SimpleCLI } from "
|
|
9
|
+
import { SimpleCLI } from "affordance";
|
|
10
10
|
const experimentNames = Object.keys(EXPERIMENTS);
|
|
11
11
|
const experimentsUsage = [
|
|
12
12
|
"Usage:",
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
REPO_ROOT
|
|
9
9
|
} from "../core/context.js";
|
|
10
10
|
import { librettoCommand } from "../../shared/package-manager.js";
|
|
11
|
-
import { SimpleCLI } from "
|
|
11
|
+
import { SimpleCLI } from "affordance";
|
|
12
12
|
function installBrowsers() {
|
|
13
13
|
console.log("Installing Playwright Chromium...");
|
|
14
14
|
const result = spawnSync("npx", ["playwright", "install", "chromium"], {
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import { readAuthState, writeAuthState } from "./auth-storage.js";
|
|
2
|
-
const
|
|
2
|
+
const DEFAULT_HOSTED_API_URL = "https://api.libretto.sh";
|
|
3
|
+
function resolveHostedApiUrl() {
|
|
4
|
+
return process.env.LIBRETTO_API_URL?.trim() || DEFAULT_HOSTED_API_URL;
|
|
5
|
+
}
|
|
3
6
|
const NOT_AUTHENTICATED_MESSAGE = [
|
|
4
7
|
"Not authenticated.",
|
|
5
|
-
" \u2022
|
|
6
|
-
" \u2022
|
|
8
|
+
" \u2022 New account: run `libretto cloud auth signup`.",
|
|
9
|
+
" \u2022 Existing account: run `libretto cloud auth login`.",
|
|
10
|
+
" \u2022 Automation: set LIBRETTO_API_KEY in your env (issue one with `libretto cloud auth api-key issue --label <label>` after signing in)."
|
|
7
11
|
].join("\n");
|
|
8
12
|
function pickCredential(state) {
|
|
9
13
|
const envKey = process.env.LIBRETTO_API_KEY?.trim();
|
|
@@ -14,7 +18,7 @@ function pickCredential(state) {
|
|
|
14
18
|
return { source: "none" };
|
|
15
19
|
}
|
|
16
20
|
function resolveApiUrl(_state) {
|
|
17
|
-
return
|
|
21
|
+
return resolveHostedApiUrl();
|
|
18
22
|
}
|
|
19
23
|
async function authFetch(options) {
|
|
20
24
|
const headers = {
|
|
@@ -184,12 +188,13 @@ async function ensureAuthState(apiUrl) {
|
|
|
184
188
|
}
|
|
185
189
|
export {
|
|
186
190
|
ApiCallError,
|
|
187
|
-
|
|
191
|
+
DEFAULT_HOSTED_API_URL,
|
|
188
192
|
NOT_AUTHENTICATED_MESSAGE,
|
|
189
193
|
authFetch,
|
|
190
194
|
betterAuthCall,
|
|
191
195
|
ensureAuthState,
|
|
192
196
|
orpcCall,
|
|
193
197
|
pickCredential,
|
|
194
|
-
resolveApiUrl
|
|
198
|
+
resolveApiUrl,
|
|
199
|
+
resolveHostedApiUrl
|
|
195
200
|
};
|
package/dist/cli/core/browser.js
CHANGED
|
@@ -5,10 +5,14 @@ import { existsSync, readFileSync, unlinkSync } from "node:fs";
|
|
|
5
5
|
import { mkdir, writeFile } from "node:fs/promises";
|
|
6
6
|
import { dirname, join } from "node:path";
|
|
7
7
|
import { createServer } from "node:net";
|
|
8
|
+
import { isWindowsNamedPipePath } from "../../shared/ipc/socket-transport.js";
|
|
8
9
|
import { getSessionProviderClosePath, PROFILES_DIR } from "./context.js";
|
|
9
10
|
import { readLibrettoConfig } from "./config.js";
|
|
10
11
|
import { librettoCommand } from "../../shared/package-manager.js";
|
|
11
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
getCloudProviderApi,
|
|
14
|
+
getProviderStartupTimeoutMs
|
|
15
|
+
} from "./providers/index.js";
|
|
12
16
|
import {
|
|
13
17
|
assertSessionAvailableForStart,
|
|
14
18
|
clearSessionState,
|
|
@@ -64,14 +68,14 @@ function normalizeUrl(url) {
|
|
|
64
68
|
if (!parsedUrl) {
|
|
65
69
|
return new URL(`https://${url}`);
|
|
66
70
|
}
|
|
67
|
-
if (parsedUrl.protocol === "http:" || parsedUrl.protocol === "https:" || parsedUrl.protocol === "file:") {
|
|
71
|
+
if (parsedUrl.protocol === "http:" || parsedUrl.protocol === "https:" || parsedUrl.protocol === "file:" || parsedUrl.href === "about:blank") {
|
|
68
72
|
return parsedUrl;
|
|
69
73
|
}
|
|
70
74
|
if (isLikelyHostWithPort(parsedUrl, url)) {
|
|
71
75
|
return new URL(`https://${url}`);
|
|
72
76
|
}
|
|
73
77
|
throw new Error(
|
|
74
|
-
`Unsupported URL protocol: ${parsedUrl.protocol}. Use http://, https://, or
|
|
78
|
+
`Unsupported URL protocol: ${parsedUrl.protocol}. Use http://, https://, file://, or about:blank.`
|
|
75
79
|
);
|
|
76
80
|
}
|
|
77
81
|
function normalizeDomain(url) {
|
|
@@ -398,8 +402,8 @@ async function runOpenWithProvider(rawUrl, providerName, session, logger, access
|
|
|
398
402
|
},
|
|
399
403
|
logger,
|
|
400
404
|
logPath: runLogPath,
|
|
401
|
-
// Remote
|
|
402
|
-
startupTimeoutMs:
|
|
405
|
+
// Remote provider creation can wait for cloud capacity before CDP exists.
|
|
406
|
+
startupTimeoutMs: getProviderStartupTimeoutMs(providerName)
|
|
403
407
|
});
|
|
404
408
|
client.destroy();
|
|
405
409
|
if (!providerSession) {
|
|
@@ -727,6 +731,7 @@ function resolveClosableSessions(logger) {
|
|
|
727
731
|
}
|
|
728
732
|
function unlinkDaemonSocket(socketPath, logger, session) {
|
|
729
733
|
if (!socketPath) return;
|
|
734
|
+
if (isWindowsNamedPipePath(socketPath)) return;
|
|
730
735
|
try {
|
|
731
736
|
unlinkSync(socketPath);
|
|
732
737
|
} catch (err) {
|