libretto 0.6.13 → 0.6.15
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 +43 -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 +7 -4
- 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/kernel.js +3 -3
- 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 +46 -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 +7 -4
- 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/kernel.ts +4 -3
- 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({
|
|
@@ -217,9 +215,25 @@ const loginCommand = SimpleCLI.command({
|
|
|
217
215
|
}
|
|
218
216
|
}
|
|
219
217
|
});
|
|
218
|
+
const forgotPasswordCommand = SimpleCLI.command({
|
|
219
|
+
description: "Send a password reset email"
|
|
220
|
+
}).input(SimpleCLI.input({ positionals: [], named: {} })).handle(async () => {
|
|
221
|
+
const apiUrl = resolveHostedApiUrl();
|
|
222
|
+
const email = await prompt("Email:");
|
|
223
|
+
const result = await orpcCall({
|
|
224
|
+
apiUrl,
|
|
225
|
+
path: "/v1/auth/requestPasswordReset",
|
|
226
|
+
input: { email },
|
|
227
|
+
unauthenticated: true
|
|
228
|
+
});
|
|
229
|
+
if (result.status === "not_found") {
|
|
230
|
+
console.log(`No Libretto account exists for ${email}.`);
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
console.log(`Password reset link sent to ${email}.`);
|
|
234
|
+
});
|
|
220
235
|
const logoutCommand = SimpleCLI.command({
|
|
221
|
-
description: "Clear local libretto credentials"
|
|
222
|
-
experimental: true
|
|
236
|
+
description: "Clear local libretto credentials"
|
|
223
237
|
}).handle(async () => {
|
|
224
238
|
const state = await readAuthState();
|
|
225
239
|
if (state?.session?.cookie) {
|
|
@@ -236,8 +250,7 @@ const logoutCommand = SimpleCLI.command({
|
|
|
236
250
|
console.log("Logged out.");
|
|
237
251
|
});
|
|
238
252
|
const inviteCommand = SimpleCLI.command({
|
|
239
|
-
description: "Invite a teammate to your active organization"
|
|
240
|
-
experimental: true
|
|
253
|
+
description: "Invite a teammate to your active organization"
|
|
241
254
|
}).input(
|
|
242
255
|
SimpleCLI.input({
|
|
243
256
|
positionals: [
|
|
@@ -287,12 +300,11 @@ const inviteCommand = SimpleCLI.command({
|
|
|
287
300
|
console.log();
|
|
288
301
|
console.log("Tell them to run:");
|
|
289
302
|
console.log(
|
|
290
|
-
` libretto
|
|
303
|
+
` libretto cloud auth accept-invite ${orgSlug} ${data.id}`
|
|
291
304
|
);
|
|
292
305
|
});
|
|
293
306
|
const acceptInviteCommand = SimpleCLI.command({
|
|
294
|
-
description: "Accept an organization invitation"
|
|
295
|
-
experimental: true
|
|
307
|
+
description: "Accept an organization invitation"
|
|
296
308
|
}).input(
|
|
297
309
|
SimpleCLI.input({
|
|
298
310
|
positionals: [
|
|
@@ -313,7 +325,7 @@ const acceptInviteCommand = SimpleCLI.command({
|
|
|
313
325
|
})
|
|
314
326
|
).handle(async ({ input }) => {
|
|
315
327
|
const stored = await readAuthState();
|
|
316
|
-
const apiUrl =
|
|
328
|
+
const apiUrl = resolveHostedApiUrl();
|
|
317
329
|
const credential = pickCredential(stored);
|
|
318
330
|
const expectedTenantSlug = input.tenantSlug;
|
|
319
331
|
if (credential.source !== "none") {
|
|
@@ -328,7 +340,7 @@ const acceptInviteCommand = SimpleCLI.command({
|
|
|
328
340
|
[
|
|
329
341
|
"You're already a member of an organization.",
|
|
330
342
|
"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)."
|
|
343
|
+
"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
344
|
].join("\n")
|
|
333
345
|
);
|
|
334
346
|
}
|
|
@@ -381,12 +393,11 @@ const acceptInviteCommand = SimpleCLI.command({
|
|
|
381
393
|
console.log();
|
|
382
394
|
console.log("Email verified. You're logged in and a member of the organization.");
|
|
383
395
|
console.log("To generate an API key, run:");
|
|
384
|
-
console.log(" libretto
|
|
396
|
+
console.log(" libretto cloud auth api-key issue --label <label>");
|
|
385
397
|
console.log("Then add LIBRETTO_API_KEY=<key> to your project's .env file.");
|
|
386
398
|
});
|
|
387
399
|
const apiKeyIssueCommand = SimpleCLI.command({
|
|
388
|
-
description: "Issue a new API key for the active organization"
|
|
389
|
-
experimental: true
|
|
400
|
+
description: "Issue a new API key for the active organization"
|
|
390
401
|
}).input(
|
|
391
402
|
SimpleCLI.input({
|
|
392
403
|
positionals: [],
|
|
@@ -416,8 +427,7 @@ const apiKeyIssueCommand = SimpleCLI.command({
|
|
|
416
427
|
);
|
|
417
428
|
});
|
|
418
429
|
const apiKeyListCommand = SimpleCLI.command({
|
|
419
|
-
description: "List API keys for the active organization"
|
|
420
|
-
experimental: true
|
|
430
|
+
description: "List API keys for the active organization"
|
|
421
431
|
}).handle(async () => {
|
|
422
432
|
const stored = await readAuthState();
|
|
423
433
|
const apiUrl = resolveApiUrl(stored);
|
|
@@ -444,13 +454,12 @@ const apiKeyListCommand = SimpleCLI.command({
|
|
|
444
454
|
}
|
|
445
455
|
});
|
|
446
456
|
const apiKeyRevokeCommand = SimpleCLI.command({
|
|
447
|
-
description: "Revoke an API key by id"
|
|
448
|
-
experimental: true
|
|
457
|
+
description: "Revoke an API key by id"
|
|
449
458
|
}).input(
|
|
450
459
|
SimpleCLI.input({
|
|
451
460
|
positionals: [
|
|
452
461
|
SimpleCLI.positional("id", z.string().min(1), {
|
|
453
|
-
help: "API key id (from `auth api-key list`)."
|
|
462
|
+
help: "API key id (from `libretto cloud auth api-key list`)."
|
|
454
463
|
})
|
|
455
464
|
],
|
|
456
465
|
named: {}
|
|
@@ -470,24 +479,23 @@ const apiKeyRevokeCommand = SimpleCLI.command({
|
|
|
470
479
|
});
|
|
471
480
|
console.log(`API key ${input.id} revoked.`);
|
|
472
481
|
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>`."
|
|
482
|
+
"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
483
|
);
|
|
475
484
|
});
|
|
476
485
|
const whoamiCommand = SimpleCLI.command({
|
|
477
|
-
description: "Print the active session and credential source"
|
|
478
|
-
experimental: true
|
|
486
|
+
description: "Print the active session and credential source"
|
|
479
487
|
}).handle(async () => {
|
|
480
488
|
const stored = await readAuthState();
|
|
481
489
|
const credential = pickCredential(stored);
|
|
482
490
|
const envKey = process.env.LIBRETTO_API_KEY?.trim();
|
|
483
491
|
if (credential.source === "none") {
|
|
484
492
|
console.log(
|
|
485
|
-
"Not authenticated. Run `libretto
|
|
493
|
+
"Not authenticated. Run `libretto cloud auth signup`, `libretto cloud auth login`, or set LIBRETTO_API_KEY in your env."
|
|
486
494
|
);
|
|
487
495
|
return;
|
|
488
496
|
}
|
|
489
497
|
console.log(`Auth source: ${credential.source}`);
|
|
490
|
-
console.log(`API URL: ${
|
|
498
|
+
console.log(`API URL: ${resolveHostedApiUrl()}`);
|
|
491
499
|
console.log(
|
|
492
500
|
`LIBRETTO_API_KEY: ${envKey ? `set in env (${envKey.slice(0, 6)}\u2026)` : "not set in env"}`
|
|
493
501
|
);
|
|
@@ -507,6 +515,7 @@ const authCommands = SimpleCLI.group({
|
|
|
507
515
|
routes: {
|
|
508
516
|
signup: signupCommand,
|
|
509
517
|
login: loginCommand,
|
|
518
|
+
"forgot-password": forgotPasswordCommand,
|
|
510
519
|
logout: logoutCommand,
|
|
511
520
|
invite: inviteCommand,
|
|
512
521
|
"accept-invite": acceptInviteCommand,
|
|
@@ -527,6 +536,7 @@ export {
|
|
|
527
536
|
apiKeyListCommand,
|
|
528
537
|
apiKeyRevokeCommand,
|
|
529
538
|
authCommands,
|
|
539
|
+
forgotPasswordCommand,
|
|
530
540
|
inviteCommand,
|
|
531
541
|
loginCommand,
|
|
532
542
|
logoutCommand,
|
|
@@ -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(
|
|
@@ -598,7 +601,7 @@ const readonlyExecCommand = SimpleCLI.command({
|
|
|
598
601
|
mode: "readonly-exec"
|
|
599
602
|
});
|
|
600
603
|
});
|
|
601
|
-
const runUsage = `Usage: ${librettoCommand("run <integrationFile> [--params <json> | --params-file <path>] [--tsconfig <path>] [--headed|--headless] [--read-only|--write-access] [--no-visualize] [--stay-open-on-success] [--viewport WxH]")}`;
|
|
604
|
+
const runUsage = `Usage: ${librettoCommand("run <integrationFile> [--params <json> | --params-file <path>] [--tsconfig <path>] [--headed|--headless] [--read-only|--write-access] [--no-visualize] [--stay-open-on-success] [--viewport WxH] [--provider <provider>]")}`;
|
|
602
605
|
const runInput = SimpleCLI.input({
|
|
603
606
|
positionals: [
|
|
604
607
|
SimpleCLI.positional("integrationFile", z.string().optional(), {
|
|
@@ -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) {
|