libretto 0.6.30 → 0.6.32
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/cloud-credentials.js +5 -17
- package/dist/cli/commands/cloud-jobs.js +118 -0
- package/dist/cli/commands/cloud-schedules.js +128 -0
- package/dist/cli/commands/cloud-settings.js +75 -0
- package/dist/cli/commands/cloud-sharing.js +13 -27
- package/dist/cli/commands/deploy.js +7 -16
- package/dist/cli/commands/profiles.js +8 -21
- package/dist/cli/commands/shared.js +17 -0
- package/dist/cli/core/daemon/ipc.js +23 -16
- package/dist/cli/core/deploy-artifact.js +5 -2
- package/dist/cli/router.js +6 -0
- package/package.json +1 -1
- package/skills/libretto/SKILL.md +3 -1
- package/skills/libretto-readonly/SKILL.md +3 -2
- package/src/cli/commands/cloud-credentials.ts +6 -18
- package/src/cli/commands/cloud-jobs.ts +149 -0
- package/src/cli/commands/cloud-schedules.ts +164 -0
- package/src/cli/commands/cloud-settings.ts +101 -0
- package/src/cli/commands/cloud-sharing.ts +20 -28
- package/src/cli/commands/deploy.ts +8 -19
- package/src/cli/commands/profiles.ts +10 -22
- package/src/cli/commands/shared.ts +29 -0
- package/src/cli/core/daemon/ipc.ts +27 -18
- package/src/cli/core/deploy-artifact.ts +7 -2
- package/src/cli/router.ts +6 -0
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
import { randomBytes } from "node:crypto";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
-
import {
|
|
4
|
-
orpcCall,
|
|
5
|
-
resolveApiUrl,
|
|
6
|
-
} from "../core/auth-fetch.js";
|
|
3
|
+
import { orpcCall } from "../core/auth-fetch.js";
|
|
7
4
|
import {
|
|
8
5
|
buildHostedDeployTarball,
|
|
9
6
|
type WorkflowDeployMetadata,
|
|
10
7
|
} from "../core/deploy-artifact.js";
|
|
11
8
|
import { readAuthState } from "../core/auth-storage.js";
|
|
12
9
|
import { SimpleCLI } from "affordance";
|
|
10
|
+
import { withCloudApiKey } from "./shared.js";
|
|
13
11
|
|
|
14
12
|
type DeploymentStatus = "building" | "ready" | "failed";
|
|
15
13
|
|
|
@@ -53,19 +51,6 @@ function deployApiKeyRequiredMessage(hasStoredSession: boolean): string {
|
|
|
53
51
|
].join("\n");
|
|
54
52
|
}
|
|
55
53
|
|
|
56
|
-
async function requireDeployApiKey() {
|
|
57
|
-
const apiKey = process.env.LIBRETTO_API_KEY?.trim();
|
|
58
|
-
|
|
59
|
-
if (!apiKey) {
|
|
60
|
-
throw new Error(deployApiKeyRequiredMessage(await hasStoredCloudSession()));
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
return {
|
|
64
|
-
apiUrl: resolveApiUrl(null),
|
|
65
|
-
credential: { source: "env-api-key" as const, apiKey },
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
|
|
69
54
|
async function hasStoredCloudSession(): Promise<boolean> {
|
|
70
55
|
try {
|
|
71
56
|
return Boolean((await readAuthState())?.session);
|
|
@@ -188,8 +173,12 @@ export const deployCommand = SimpleCLI.command({
|
|
|
188
173
|
description: "Deploy workflows to the hosted platform",
|
|
189
174
|
})
|
|
190
175
|
.input(deployInput)
|
|
191
|
-
.
|
|
192
|
-
|
|
176
|
+
.use(withCloudApiKey(
|
|
177
|
+
"deploy to Libretto Cloud",
|
|
178
|
+
async () => deployApiKeyRequiredMessage(await hasStoredCloudSession()),
|
|
179
|
+
))
|
|
180
|
+
.handle(async ({ input, ctx }) => {
|
|
181
|
+
const { apiUrl, credential } = ctx;
|
|
193
182
|
const deploymentName = generateDeploymentName();
|
|
194
183
|
|
|
195
184
|
// Hosted deploy uploads a generated artifact with a deploy entrypoint and
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { SimpleCLI } from "affordance";
|
|
3
|
-
import { orpcCall
|
|
3
|
+
import { orpcCall } from "../core/auth-fetch.js";
|
|
4
4
|
import { normalizeProfileName } from "../core/profiles.js";
|
|
5
|
+
import { withCloudApiKey } from "./shared.js";
|
|
5
6
|
|
|
6
7
|
type ListProfilesResponse = {
|
|
7
8
|
profiles: Array<{
|
|
@@ -17,30 +18,17 @@ type DeleteProfileResponse = {
|
|
|
17
18
|
deleted_count: number;
|
|
18
19
|
};
|
|
19
20
|
|
|
20
|
-
function requireApiKeyCredential() {
|
|
21
|
-
const apiKey = process.env.LIBRETTO_API_KEY?.trim();
|
|
22
|
-
if (!apiKey) {
|
|
23
|
-
throw new Error(
|
|
24
|
-
"LIBRETTO_API_KEY is required to manage Libretto Cloud profiles. Issue one with `libretto cloud auth api-key issue --label <label>`.",
|
|
25
|
-
);
|
|
26
|
-
}
|
|
27
|
-
return {
|
|
28
|
-
apiUrl: resolveApiUrl(null),
|
|
29
|
-
credential: { source: "env-api-key" as const, apiKey },
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
|
|
33
21
|
export const listProfilesCommand = SimpleCLI.command({
|
|
34
22
|
description: "List Libretto Cloud auth profiles",
|
|
35
23
|
})
|
|
36
24
|
.input(SimpleCLI.input({ positionals: [], named: {} }))
|
|
37
|
-
.
|
|
38
|
-
|
|
25
|
+
.use(withCloudApiKey("manage Libretto Cloud profiles"))
|
|
26
|
+
.handle(async ({ ctx }) => {
|
|
39
27
|
const response = await orpcCall<ListProfilesResponse>({
|
|
40
|
-
apiUrl,
|
|
28
|
+
apiUrl: ctx.apiUrl,
|
|
41
29
|
path: "/v1/browserProfiles/list",
|
|
42
30
|
input: {},
|
|
43
|
-
credential,
|
|
31
|
+
credential: ctx.credential,
|
|
44
32
|
});
|
|
45
33
|
if (response.profiles.length === 0) {
|
|
46
34
|
console.log("No cloud profiles found.");
|
|
@@ -65,14 +53,14 @@ export const deleteProfileCommand = SimpleCLI.command({
|
|
|
65
53
|
],
|
|
66
54
|
named: {},
|
|
67
55
|
}))
|
|
68
|
-
.
|
|
56
|
+
.use(withCloudApiKey("manage Libretto Cloud profiles"))
|
|
57
|
+
.handle(async ({ input, ctx }) => {
|
|
69
58
|
const profileName = normalizeProfileName(input.profileName);
|
|
70
|
-
const { apiUrl, credential } = requireApiKeyCredential();
|
|
71
59
|
const response = await orpcCall<DeleteProfileResponse>({
|
|
72
|
-
apiUrl,
|
|
60
|
+
apiUrl: ctx.apiUrl,
|
|
73
61
|
path: "/v1/browserProfiles/delete",
|
|
74
62
|
input: { name: profileName },
|
|
75
|
-
credential,
|
|
63
|
+
credential: ctx.credential,
|
|
76
64
|
});
|
|
77
65
|
if (!response.success || response.deleted_count === 0) {
|
|
78
66
|
console.log(`No cloud profile found for ${profileName}.`);
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
type SessionState,
|
|
10
10
|
validateSessionName,
|
|
11
11
|
} from "../core/session.js";
|
|
12
|
+
import { resolveApiUrl } from "../core/auth-fetch.js";
|
|
12
13
|
import {
|
|
13
14
|
SimpleCLI,
|
|
14
15
|
type SimpleCLIContext,
|
|
@@ -40,6 +41,11 @@ export type ExperimentsContext = {
|
|
|
40
41
|
experiments: Experiments;
|
|
41
42
|
};
|
|
42
43
|
|
|
44
|
+
export type CloudApiKeyContext = {
|
|
45
|
+
apiUrl: string;
|
|
46
|
+
credential: { source: "env-api-key"; apiKey: string };
|
|
47
|
+
};
|
|
48
|
+
|
|
43
49
|
export function withExperiments<
|
|
44
50
|
TContext extends SimpleCLIContext,
|
|
45
51
|
>(): SimpleCLIMiddleware<unknown, TContext, TContext & ExperimentsContext> {
|
|
@@ -49,6 +55,29 @@ export function withExperiments<
|
|
|
49
55
|
});
|
|
50
56
|
}
|
|
51
57
|
|
|
58
|
+
export function withCloudApiKey<
|
|
59
|
+
TContext extends SimpleCLIContext,
|
|
60
|
+
>(
|
|
61
|
+
action: string,
|
|
62
|
+
formatMissingMessage?: () => string | Promise<string>,
|
|
63
|
+
): SimpleCLIMiddleware<unknown, TContext, TContext & CloudApiKeyContext> {
|
|
64
|
+
return async ({ ctx }) => {
|
|
65
|
+
const apiKey = process.env.LIBRETTO_API_KEY?.trim();
|
|
66
|
+
if (!apiKey) {
|
|
67
|
+
throw new Error(
|
|
68
|
+
formatMissingMessage
|
|
69
|
+
? await formatMissingMessage()
|
|
70
|
+
: `LIBRETTO_API_KEY is required to ${action}. Issue one with \`libretto cloud auth api-key issue --label <label>\`.`,
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
...ctx,
|
|
75
|
+
apiUrl: resolveApiUrl(null),
|
|
76
|
+
credential: { source: "env-api-key", apiKey },
|
|
77
|
+
};
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
52
81
|
export function withRequiredSession(): SimpleCLIMiddleware<
|
|
53
82
|
{ session?: string },
|
|
54
83
|
{},
|
|
@@ -2,7 +2,7 @@ import { createHash } from "node:crypto";
|
|
|
2
2
|
import type { ChildProcess } from "node:child_process";
|
|
3
3
|
import { spawn } from "node:child_process";
|
|
4
4
|
import { openSync, closeSync } from "node:fs";
|
|
5
|
-
import
|
|
5
|
+
import * as moduleBuiltin from "node:module";
|
|
6
6
|
import { homedir, userInfo } from "node:os";
|
|
7
7
|
import { fileURLToPath } from "node:url";
|
|
8
8
|
import { createIpcPeer, type IpcPeer } from "../../../shared/ipc/ipc.js";
|
|
@@ -246,25 +246,34 @@ export class DaemonClient {
|
|
|
246
246
|
const daemonEntryPath = fileURLToPath(
|
|
247
247
|
new URL("./daemon.js", import.meta.url),
|
|
248
248
|
);
|
|
249
|
-
const
|
|
250
|
-
const
|
|
249
|
+
const childArgs = [daemonEntryPath, JSON.stringify(config)];
|
|
250
|
+
const childEnv: NodeJS.ProcessEnv = { ...process.env };
|
|
251
|
+
|
|
252
|
+
if (config.workflow) {
|
|
253
|
+
const tsxPreflightPath = fileURLToPath(
|
|
254
|
+
import.meta.resolve("tsx/preflight"),
|
|
255
|
+
);
|
|
256
|
+
const tsxLoaderFlag =
|
|
257
|
+
typeof moduleBuiltin.register === "function" ? "--import" : "--loader";
|
|
258
|
+
|
|
259
|
+
childArgs.unshift(
|
|
260
|
+
"--require",
|
|
261
|
+
tsxPreflightPath,
|
|
262
|
+
tsxLoaderFlag,
|
|
263
|
+
import.meta.resolve("tsx"),
|
|
264
|
+
);
|
|
265
|
+
|
|
266
|
+
if (config.workflow.tsconfigPath) {
|
|
267
|
+
childEnv.TSX_TSCONFIG_PATH = config.workflow.tsconfigPath;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
251
270
|
|
|
252
271
|
const childStderrFd = openSync(logPath, "a");
|
|
253
|
-
const child = spawn(
|
|
254
|
-
|
|
255
|
-
[
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
? ["--tsconfig", config.workflow.tsconfigPath]
|
|
259
|
-
: []),
|
|
260
|
-
daemonEntryPath,
|
|
261
|
-
JSON.stringify(config),
|
|
262
|
-
],
|
|
263
|
-
{
|
|
264
|
-
detached: true,
|
|
265
|
-
stdio: ["ignore", "ignore", childStderrFd, "ipc"],
|
|
266
|
-
},
|
|
267
|
-
);
|
|
272
|
+
const child = spawn(process.execPath, childArgs, {
|
|
273
|
+
detached: true,
|
|
274
|
+
stdio: ["ignore", "ignore", childStderrFd, "ipc"],
|
|
275
|
+
env: childEnv,
|
|
276
|
+
});
|
|
268
277
|
closeSync(childStderrFd);
|
|
269
278
|
|
|
270
279
|
const pid = child.pid!;
|
|
@@ -692,6 +692,10 @@ function toPortableRelativePath(args: {
|
|
|
692
692
|
return relPath;
|
|
693
693
|
}
|
|
694
694
|
|
|
695
|
+
function isShareableSourceRelPath(relPath: string): boolean {
|
|
696
|
+
return !relPath.split("/").includes("node_modules");
|
|
697
|
+
}
|
|
698
|
+
|
|
695
699
|
function writeShareableSourceFiles(args: {
|
|
696
700
|
absSourceDir: string;
|
|
697
701
|
absSourcePaths: readonly string[];
|
|
@@ -702,7 +706,7 @@ function writeShareableSourceFiles(args: {
|
|
|
702
706
|
absPath,
|
|
703
707
|
absSourceDir: args.absSourceDir,
|
|
704
708
|
}),
|
|
705
|
-
))].sort();
|
|
709
|
+
))].filter(isShareableSourceRelPath).sort();
|
|
706
710
|
|
|
707
711
|
for (const relPath of relPaths) {
|
|
708
712
|
const targetPath = join(args.outputDir, ".libretto-share", "source", relPath);
|
|
@@ -1209,7 +1213,8 @@ async function writeBundledDeployEntrypoint(args: {
|
|
|
1209
1213
|
relPath !== "" &&
|
|
1210
1214
|
!relPath.startsWith("../") &&
|
|
1211
1215
|
relPath !== ".." &&
|
|
1212
|
-
!isAbsolute(relPath)
|
|
1216
|
+
!isAbsolute(relPath) &&
|
|
1217
|
+
isShareableSourceRelPath(relPath.replaceAll("\\", "/"))
|
|
1213
1218
|
);
|
|
1214
1219
|
});
|
|
1215
1220
|
return { shareableSourceFiles, workflows };
|
package/src/cli/router.ts
CHANGED
|
@@ -2,6 +2,9 @@ import { authCommands } from "./commands/auth.js";
|
|
|
2
2
|
import { billingCommands } from "./commands/billing.js";
|
|
3
3
|
import { browserCommands } from "./commands/browser.js";
|
|
4
4
|
import { cloudCredentialCommands } from "./commands/cloud-credentials.js";
|
|
5
|
+
import { cloudJobCommands } from "./commands/cloud-jobs.js";
|
|
6
|
+
import { cloudScheduleCommands } from "./commands/cloud-schedules.js";
|
|
7
|
+
import { settingsCommands } from "./commands/cloud-settings.js";
|
|
5
8
|
import { codeSharingCommands, shareWorkflowCommand } from "./commands/cloud-sharing.js";
|
|
6
9
|
import { deployCommand } from "./commands/deploy.js";
|
|
7
10
|
import { executionCommands } from "./commands/execution.js";
|
|
@@ -25,7 +28,10 @@ export const cliRoutes = {
|
|
|
25
28
|
auth: authCommands,
|
|
26
29
|
billing: billingCommands,
|
|
27
30
|
credentials: cloudCredentialCommands,
|
|
31
|
+
jobs: cloudJobCommands,
|
|
28
32
|
profiles: profileCommands,
|
|
33
|
+
schedules: cloudScheduleCommands,
|
|
34
|
+
settings: settingsCommands,
|
|
29
35
|
share: shareWorkflowCommand,
|
|
30
36
|
sharing: codeSharingCommands,
|
|
31
37
|
},
|