libretto 0.6.31 → 0.6.33

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.
Files changed (51) hide show
  1. package/README.md +1 -1
  2. package/README.template.md +1 -1
  3. package/dist/cli/commands/auth.js +119 -268
  4. package/dist/cli/commands/browser.js +1 -0
  5. package/dist/cli/commands/cloud-credentials.js +5 -17
  6. package/dist/cli/commands/cloud-jobs.js +125 -0
  7. package/dist/cli/commands/cloud-schedules.js +128 -0
  8. package/dist/cli/commands/cloud-settings.js +75 -0
  9. package/dist/cli/commands/cloud-sharing.js +13 -27
  10. package/dist/cli/commands/deploy.js +7 -16
  11. package/dist/cli/commands/execution.js +3 -2
  12. package/dist/cli/commands/profiles.js +8 -21
  13. package/dist/cli/commands/shared.js +17 -0
  14. package/dist/cli/core/browser.js +2 -1
  15. package/dist/cli/core/daemon/daemon.js +2 -1
  16. package/dist/cli/core/daemon/ipc.js +23 -16
  17. package/dist/cli/core/deploy-artifact.js +41 -16
  18. package/dist/cli/core/providers/kernel.js +3 -2
  19. package/dist/cli/core/providers/libretto-cloud.js +2 -1
  20. package/dist/cli/core/telemetry.js +14 -2
  21. package/dist/cli/router.js +6 -0
  22. package/dist/index.d.ts +1 -1
  23. package/dist/shared/workflow/workflow.d.ts +18 -0
  24. package/dist/shared/workflow/workflow.js +9 -0
  25. package/package.json +1 -1
  26. package/skills/libretto/SKILL.md +17 -2
  27. package/skills/libretto/references/website-authentication.md +18 -2
  28. package/skills/libretto-readonly/SKILL.md +1 -1
  29. package/src/cli/commands/auth.ts +169 -382
  30. package/src/cli/commands/browser.ts +1 -0
  31. package/src/cli/commands/cloud-credentials.ts +6 -18
  32. package/src/cli/commands/cloud-jobs.ts +157 -0
  33. package/src/cli/commands/cloud-schedules.ts +164 -0
  34. package/src/cli/commands/cloud-settings.ts +101 -0
  35. package/src/cli/commands/cloud-sharing.ts +20 -28
  36. package/src/cli/commands/deploy.ts +8 -19
  37. package/src/cli/commands/execution.ts +2 -1
  38. package/src/cli/commands/profiles.ts +10 -22
  39. package/src/cli/commands/shared.ts +29 -0
  40. package/src/cli/core/browser.ts +2 -0
  41. package/src/cli/core/daemon/config.ts +1 -0
  42. package/src/cli/core/daemon/daemon.ts +1 -0
  43. package/src/cli/core/daemon/ipc.ts +27 -18
  44. package/src/cli/core/deploy-artifact.ts +63 -14
  45. package/src/cli/core/providers/kernel.ts +3 -2
  46. package/src/cli/core/providers/libretto-cloud.ts +1 -0
  47. package/src/cli/core/providers/types.ts +1 -0
  48. package/src/cli/core/telemetry.ts +15 -1
  49. package/src/cli/router.ts +6 -0
  50. package/src/index.ts +1 -0
  51. package/src/shared/workflow/workflow.ts +22 -0
@@ -1,7 +1,8 @@
1
1
  import { z } from "zod";
2
2
  import { SimpleCLI } from "affordance";
3
- import { orpcCall, resolveApiUrl } from "../core/auth-fetch.js";
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
- .handle(async () => {
38
- const { apiUrl, credential } = requireApiKeyCredential();
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
- .handle(async ({ input }) => {
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
  {},
@@ -529,6 +529,7 @@ export async function runOpen(
529
529
  export async function runOpenWithProvider(
530
530
  rawUrl: string,
531
531
  providerName: string,
532
+ headless: boolean,
532
533
  session: string,
533
534
  logger: LoggerApi,
534
535
  accessMode: SessionAccessMode,
@@ -557,6 +558,7 @@ export async function runOpenWithProvider(
557
558
  browser: {
558
559
  kind: "provider",
559
560
  providerName,
561
+ headless,
560
562
  initialUrl: url,
561
563
  },
562
564
  },
@@ -39,6 +39,7 @@ export type DaemonBrowserConnectConfig = {
39
39
  export type DaemonBrowserProviderConfig = {
40
40
  kind: "provider";
41
41
  providerName: string;
42
+ headless?: boolean;
42
43
  initialUrl?: string;
43
44
  authProfileName?: string;
44
45
  authProfilePersist?: boolean;
@@ -479,6 +479,7 @@ class BrowserDaemon {
479
479
  providerSession = await provider.createSession({
480
480
  authProfileName: config.authProfileName,
481
481
  authProfilePersist: config.authProfilePersist,
482
+ headless: config.headless,
482
483
  });
483
484
  const browser = await chromium.connectOverCDP(
484
485
  providerSession.cdpEndpoint,
@@ -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 { createRequire } from "node:module";
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 require = createRequire(import.meta.url);
250
- const tsxCliPath = require.resolve("tsx/cli");
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
- process.execPath,
255
- [
256
- tsxCliPath,
257
- ...(config.workflow?.tsconfigPath
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!;
@@ -22,6 +22,10 @@ import {
22
22
  LIBRETTO_WORKFLOW_BRAND,
23
23
  } from "../../shared/workflow/workflow.js";
24
24
  import { normalizeCredentialNames } from "../../shared/workflow/credentials.js";
25
+ import {
26
+ ViewportConfigSchema,
27
+ type ViewportConfig,
28
+ } from "./config.js";
25
29
 
26
30
  type PackageManifest = {
27
31
  name?: string;
@@ -57,6 +61,9 @@ export type WorkflowDeployMetadata = {
57
61
  credentialNames: string[];
58
62
  authProfileName?: string;
59
63
  authProfileRefresh?: boolean;
64
+ startUrl?: string;
65
+ gpu?: boolean;
66
+ viewport?: ViewportConfig;
60
67
  sourceFile?: string;
61
68
  sourceFiles?: string[];
62
69
  };
@@ -825,6 +832,7 @@ function createDiscoveryLibrettoModule(
825
832
  name,
826
833
  ...extractDiscoveryCredentialMetadata(definitionOrHandler),
827
834
  ...extractDiscoveryAuthProfileMetadata(definitionOrHandler),
835
+ ...extractDiscoveryLaunchMetadata(definitionOrHandler),
828
836
  });
829
837
  return {
830
838
  [LIBRETTO_WORKFLOW_BRAND]: true,
@@ -849,6 +857,46 @@ function createDiscoveryLibrettoModule(
849
857
  });
850
858
  }
851
859
 
860
+ function extractDiscoveryLaunchMetadata(
861
+ definitionOrHandler: unknown,
862
+ ): Omit<
863
+ WorkflowDeployMetadata,
864
+ "name" | "credentialNames" | "authProfileName" | "authProfileRefresh"
865
+ > {
866
+ if (!definitionOrHandler || typeof definitionOrHandler !== "object") {
867
+ return {};
868
+ }
869
+
870
+ const record = definitionOrHandler as {
871
+ startUrl?: unknown;
872
+ gpu?: unknown;
873
+ viewport?: unknown;
874
+ };
875
+ const metadata: Omit<
876
+ WorkflowDeployMetadata,
877
+ "name" | "credentialNames" | "authProfileName" | "authProfileRefresh"
878
+ > = {};
879
+
880
+ if (typeof record.startUrl === "string") {
881
+ metadata.startUrl = record.startUrl;
882
+ }
883
+ if (typeof record.gpu === "boolean") {
884
+ metadata.gpu = record.gpu;
885
+ }
886
+ if (
887
+ record.viewport &&
888
+ typeof record.viewport === "object" &&
889
+ !Array.isArray(record.viewport)
890
+ ) {
891
+ const viewport = ViewportConfigSchema.safeParse(record.viewport);
892
+ if (viewport.success) {
893
+ metadata.viewport = viewport.data;
894
+ }
895
+ }
896
+
897
+ return metadata;
898
+ }
899
+
852
900
  function extractDiscoveryCredentialMetadata(
853
901
  definitionOrHandler: unknown,
854
902
  ): Pick<WorkflowDeployMetadata, "credentialNames"> {
@@ -982,6 +1030,9 @@ function createBootstrapSource(args: {
982
1030
  credentialNames: workflow.credentialNames,
983
1031
  authProfileName: workflow.authProfileName,
984
1032
  authProfileRefresh: workflow.authProfileRefresh,
1033
+ startUrl: workflow.startUrl,
1034
+ gpu: workflow.gpu,
1035
+ viewport: workflow.viewport,
985
1036
  })});`,
986
1037
  )
987
1038
  .join("\n");
@@ -1069,23 +1120,21 @@ function createWorkflowProxy(workflowName, metadata) {
1069
1120
  return await target.run(ctx, input);
1070
1121
  };
1071
1122
 
1072
- if (!metadata?.authProfileName) {
1073
- return workflow(workflowName, {
1074
- credentials: Array.isArray(metadata?.credentialNames)
1075
- ? metadata.credentialNames
1076
- : [],
1077
- handler,
1078
- });
1079
- }
1080
-
1081
1123
  return workflow(workflowName, {
1082
- credentials: Array.isArray(metadata.credentialNames)
1124
+ credentials: Array.isArray(metadata?.credentialNames)
1083
1125
  ? metadata.credentialNames
1084
1126
  : [],
1085
- authProfile: {
1086
- name: metadata.authProfileName,
1087
- ...(typeof metadata.authProfileRefresh === "boolean" ? { refresh: metadata.authProfileRefresh } : {}),
1088
- },
1127
+ ...(metadata?.authProfileName
1128
+ ? {
1129
+ authProfile: {
1130
+ name: metadata.authProfileName,
1131
+ ...(typeof metadata.authProfileRefresh === "boolean" ? { refresh: metadata.authProfileRefresh } : {}),
1132
+ },
1133
+ }
1134
+ : {}),
1135
+ ...(typeof metadata?.startUrl === "string" ? { startUrl: metadata.startUrl } : {}),
1136
+ ...(typeof metadata?.gpu === "boolean" ? { gpu: metadata.gpu } : {}),
1137
+ ...(metadata?.viewport ? { viewport: metadata.viewport } : {}),
1089
1138
  handler,
1090
1139
  });
1091
1140
  }
@@ -99,7 +99,8 @@ export function createKernelProvider(
99
99
  >();
100
100
 
101
101
  return {
102
- async createSession() {
102
+ async createSession(sessionOptions) {
103
+ const sessionHeadless = sessionOptions?.headless ?? headless;
103
104
  const json = await kernelFetchJson<KernelBrowserResponse>(
104
105
  endpoint,
105
106
  apiKey,
@@ -107,7 +108,7 @@ export function createKernelProvider(
107
108
  {
108
109
  method: "POST",
109
110
  body: JSON.stringify({
110
- headless,
111
+ headless: sessionHeadless,
111
112
  stealth,
112
113
  timeout_seconds: timeoutSeconds,
113
114
  }),
@@ -39,6 +39,7 @@ export function createLibrettoCloudProvider(): ProviderApi {
39
39
  timeout_seconds: browserSessionTimeoutSeconds,
40
40
  profile_name: options?.authProfileName,
41
41
  profile_persist: options?.authProfilePersist,
42
+ headless: options?.headless,
42
43
  },
43
44
  }),
44
45
  });
@@ -21,6 +21,7 @@ export type ProviderApi = {
21
21
  createSession(options?: {
22
22
  authProfileName?: string;
23
23
  authProfilePersist?: boolean;
24
+ headless?: boolean;
24
25
  }): Promise<ProviderSession>;
25
26
  closeSession(sessionId: string): Promise<ProviderCloseResult>;
26
27
  };
@@ -6,6 +6,7 @@ import { basename, dirname, join } from "node:path";
6
6
  import { fileURLToPath } from "node:url";
7
7
  import type { SimpleCLICommandMeta, SimpleCLIMiddleware } from "affordance";
8
8
  import { resolveHostedApiUrl } from "./auth-fetch.js";
9
+ import { readAuthState } from "./auth-storage.js";
9
10
 
10
11
  const TELEMETRY_FILE_NAME = "telemetry.json";
11
12
  const TELEMETRY_ENDPOINT_PATH = "/v1/telemetry/recordCliEvent";
@@ -25,6 +26,7 @@ type CliTelemetryPayload = {
25
26
  error: boolean;
26
27
  packageVersion: string;
27
28
  buildChannel: BuildChannel;
29
+ cloudUserId?: string;
28
30
  };
29
31
 
30
32
  type PackageJson = {
@@ -109,7 +111,7 @@ function writeTelemetryNotice(): void {
109
111
  if (!process.stderr.isTTY) return;
110
112
  process.stderr.write(
111
113
  [
112
- "Libretto collects anonymous CLI telemetry: install id, timestamp, command event, error status, package version, and build channel only.",
114
+ "Libretto collects CLI telemetry: install id, timestamp, command event, error status, package version, build channel, and signed-in cloud user id only.",
113
115
  "Set LIBRETTO_TELEMETRY_DISABLED=1 or DO_NOT_TRACK=1 to disable it, or set enabled:false in ~/.libretto/telemetry.json.",
114
116
  ].join(" ") + "\n",
115
117
  );
@@ -130,6 +132,7 @@ async function recordCliTelemetryEvent(
130
132
  if (isTelemetryDisabled()) return;
131
133
  const installId = await readOrCreateInstallId();
132
134
  if (!installId) return;
135
+ const cloudUserId = await readConfiguredCloudUserId();
133
136
 
134
137
  await sendWithTimeout({
135
138
  installId,
@@ -138,9 +141,20 @@ async function recordCliTelemetryEvent(
138
141
  error,
139
142
  packageVersion,
140
143
  buildChannel,
144
+ ...(cloudUserId ? { cloudUserId } : {}),
141
145
  });
142
146
  }
143
147
 
148
+ async function readConfiguredCloudUserId(): Promise<string | null> {
149
+ try {
150
+ const state = await readAuthState();
151
+ const cloudUserId = state?.session?.userId;
152
+ return cloudUserId && cloudUserId.length > 0 ? cloudUserId : null;
153
+ } catch {
154
+ return null;
155
+ }
156
+ }
157
+
144
158
  async function sendWithTimeout(payload: CliTelemetryPayload): Promise<void> {
145
159
  const controller = new AbortController();
146
160
  const timeout = setTimeout(() => controller.abort(), TELEMETRY_TIMEOUT_MS);
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
  },
package/src/index.ts CHANGED
@@ -128,6 +128,7 @@ export {
128
128
  workflow,
129
129
  type ExportedLibrettoWorkflow,
130
130
  type LibrettoWorkflowContext,
131
+ type LibrettoWorkflowDefinition,
131
132
  type LibrettoWorkflowHandler,
132
133
  type LibrettoWorkflowOptions,
133
134
  type WorkflowInputValidator,
@@ -4,6 +4,7 @@ import {
4
4
  createRecoveryPage,
5
5
  type RecoveryAction,
6
6
  } from "../../runtime/recovery/page-fallbacks.js";
7
+ import type { ViewportConfig } from "../../cli/core/config.js";
7
8
  import { normalizeProfileName } from "./auth-profile-name.js";
8
9
  import {
9
10
  mergeCredentialsIntoInput,
@@ -37,6 +38,9 @@ export type LibrettoWorkflowDefinition<
37
38
  output?: OutputSchema;
38
39
  credentials?: readonly string[];
39
40
  authProfile?: LibrettoWorkflowAuthProfile;
41
+ startUrl?: string;
42
+ gpu?: boolean;
43
+ viewport?: ViewportConfig;
40
44
  recoveryAction?: RecoveryAction;
41
45
  };
42
46
 
@@ -155,6 +159,9 @@ export class LibrettoWorkflow<
155
159
  public readonly credentialNames: readonly string[];
156
160
  public readonly authProfileName?: string;
157
161
  public readonly authProfileRefresh?: boolean;
162
+ public readonly startUrl?: string;
163
+ public readonly gpu?: boolean;
164
+ public readonly viewport?: ViewportConfig;
158
165
  public readonly recoveryAction?: RecoveryAction;
159
166
  private readonly handler: LibrettoWorkflowHandler<
160
167
  z.infer<InputSchema>,
@@ -170,6 +177,9 @@ export class LibrettoWorkflow<
170
177
  credentialNames?: readonly string[];
171
178
  authProfileName?: string;
172
179
  authProfileRefresh?: boolean;
180
+ startUrl?: string;
181
+ gpu?: boolean;
182
+ viewport?: ViewportConfig;
173
183
  recoveryAction?: RecoveryAction;
174
184
  }
175
185
  | undefined,
@@ -184,6 +194,9 @@ export class LibrettoWorkflow<
184
194
  this.credentialNames = options?.credentialNames ?? [];
185
195
  this.authProfileName = options?.authProfileName;
186
196
  this.authProfileRefresh = options?.authProfileRefresh;
197
+ this.startUrl = options?.startUrl;
198
+ this.gpu = options?.gpu;
199
+ this.viewport = options?.viewport;
187
200
  this.recoveryAction = options?.recoveryAction;
188
201
  this.handler = handler;
189
202
  }
@@ -218,6 +231,9 @@ export type ExportedLibrettoWorkflow = {
218
231
  readonly credentialNames: readonly string[];
219
232
  readonly authProfileName?: string;
220
233
  readonly authProfileRefresh?: boolean;
234
+ readonly startUrl?: string;
235
+ readonly gpu?: boolean;
236
+ readonly viewport?: ViewportConfig;
221
237
  readonly recoveryAction?: RecoveryAction;
222
238
  run: (ctx: LibrettoWorkflowContext, input: unknown) => Promise<unknown>;
223
239
  };
@@ -322,6 +338,9 @@ function getWorkflowConstructorOptions<
322
338
  credentialNames: readonly string[];
323
339
  authProfileName?: string;
324
340
  authProfileRefresh?: boolean;
341
+ startUrl?: string;
342
+ gpu?: boolean;
343
+ viewport?: ViewportConfig;
325
344
  recoveryAction?: RecoveryAction;
326
345
  } {
327
346
  const authProfile = normalizeWorkflowAuthProfile(options.authProfile);
@@ -331,6 +350,9 @@ function getWorkflowConstructorOptions<
331
350
  credentialNames: normalizeCredentialNames(options.credentials),
332
351
  authProfileName: authProfile?.name,
333
352
  authProfileRefresh: authProfile?.refresh,
353
+ startUrl: options.startUrl,
354
+ gpu: options.gpu,
355
+ viewport: options.viewport,
334
356
  recoveryAction: options.recoveryAction,
335
357
  };
336
358
  }