windmill-cli 1.770.0 → 1.771.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.
Files changed (2) hide show
  1. package/esm/main.js +302 -51
  2. package/package.json +1 -1
package/esm/main.js CHANGED
@@ -16784,7 +16784,7 @@ var init_OpenAPI = __esm(() => {
16784
16784
  PASSWORD: undefined,
16785
16785
  TOKEN: getEnv3("WM_TOKEN"),
16786
16786
  USERNAME: undefined,
16787
- VERSION: "1.770.0",
16787
+ VERSION: "1.771.0",
16788
16788
  WITH_CREDENTIALS: true,
16789
16789
  interceptors: {
16790
16790
  request: new Interceptors,
@@ -17103,6 +17103,7 @@ __export(exports_services_gen, {
17103
17103
  updateApp: () => updateApp,
17104
17104
  updateAmqpTrigger: () => updateAmqpTrigger,
17105
17105
  unstar: () => unstar,
17106
+ unresolveCompletedJobs: () => unresolveCompletedJobs,
17106
17107
  unassignGhesInstallation: () => unassignGhesInstallation,
17107
17108
  unarchiveWorkspace: () => unarchiveWorkspace,
17108
17109
  toggleWorkspaceErrorHandlerForScript: () => toggleWorkspaceErrorHandlerForScript,
@@ -17209,6 +17210,7 @@ __export(exports_services_gen, {
17209
17210
  restartWorkerGroup: () => restartWorkerGroup,
17210
17211
  restartFlowAtStep: () => restartFlowAtStep,
17211
17212
  resolveNpmPackageVersion: () => resolveNpmPackageVersion,
17213
+ resolveCompletedJobs: () => resolveCompletedJobs,
17212
17214
  resetPassword: () => resetPassword,
17213
17215
  resetKafkaOffsets: () => resetKafkaOffsets,
17214
17216
  resetDiffTally: () => resetDiffTally,
@@ -17234,6 +17236,7 @@ __export(exports_services_gen, {
17234
17236
  publishHubScript: () => publishHubScript,
17235
17237
  publishHubResources: () => publishHubResources,
17236
17238
  publishHubResourceType: () => publishHubResourceType,
17239
+ publishHubRawAppRecording: () => publishHubRawAppRecording,
17237
17240
  publishHubRawAppEmbed: () => publishHubRawAppEmbed,
17238
17241
  publishHubRawApp: () => publishHubRawApp,
17239
17242
  publishHubProjectLogo: () => publishHubProjectLogo,
@@ -17310,6 +17313,7 @@ __export(exports_services_gen, {
17310
17313
  listScriptPaths: () => listScriptPaths,
17311
17314
  listSchedulesWithJobs: () => listSchedulesWithJobs,
17312
17315
  listSchedules: () => listSchedules,
17316
+ listRunnables: () => listRunnables,
17313
17317
  listResourceTypeNames: () => listResourceTypeNames,
17314
17318
  listResourceType: () => listResourceType,
17315
17319
  listResourceNames: () => listResourceNames,
@@ -17438,6 +17442,7 @@ __export(exports_services_gen, {
17438
17442
  getWorkspaceDefaultApp: () => getWorkspaceDefaultApp,
17439
17443
  getWorkspaceAsSuperAdmin: () => getWorkspaceAsSuperAdmin,
17440
17444
  getWebsocketTrigger: () => getWebsocketTrigger,
17445
+ getWacApprovalUrls: () => getWacApprovalUrls,
17441
17446
  getVolumeStorage: () => getVolumeStorage,
17442
17447
  getVariableValue: () => getVariableValue,
17443
17448
  getVariable: () => getVariable,
@@ -21329,6 +21334,26 @@ var backendVersion = () => {
21329
21334
  workspace: data3.workspace
21330
21335
  }
21331
21336
  });
21337
+ }, listRunnables = (data3) => {
21338
+ return request(OpenAPI, {
21339
+ method: "GET",
21340
+ url: "/w/{workspace}/runnables/list",
21341
+ path: {
21342
+ workspace: data3.workspace
21343
+ },
21344
+ query: {
21345
+ order_by: data3.orderBy,
21346
+ order_desc: data3.orderDesc,
21347
+ kinds: data3.kinds,
21348
+ show_archived: data3.showArchived,
21349
+ include_without_main: data3.includeWithoutMain,
21350
+ path_start: data3.pathStart,
21351
+ label: data3.label,
21352
+ search: data3.search,
21353
+ per_page: data3.perPage,
21354
+ cursor: data3.cursor
21355
+ }
21356
+ });
21332
21357
  }, listFlows = (data3) => {
21333
21358
  return request(OpenAPI, {
21334
21359
  method: "GET",
@@ -22375,6 +22400,7 @@ var backendVersion = () => {
22375
22400
  page: data3.page,
22376
22401
  per_page: data3.perPage,
22377
22402
  is_skipped: data3.isSkipped,
22403
+ resolved: data3.resolved,
22378
22404
  is_flow_step: data3.isFlowStep,
22379
22405
  has_null_parent: data3.hasNullParent,
22380
22406
  success: data3.success,
@@ -22492,6 +22518,7 @@ var backendVersion = () => {
22492
22518
  page: data3.page,
22493
22519
  per_page: data3.perPage,
22494
22520
  is_skipped: data3.isSkipped,
22521
+ resolved: data3.resolved,
22495
22522
  is_flow_step: data3.isFlowStep,
22496
22523
  has_null_parent: data3.hasNullParent,
22497
22524
  is_not_schedule: data3.isNotSchedule
@@ -22586,6 +22613,7 @@ var backendVersion = () => {
22586
22613
  per_page: data3.perPage,
22587
22614
  trigger_kind: data3.triggerKind,
22588
22615
  is_skipped: data3.isSkipped,
22616
+ resolved: data3.resolved,
22589
22617
  is_flow_step: data3.isFlowStep,
22590
22618
  has_null_parent: data3.hasNullParent,
22591
22619
  success: data3.success,
@@ -22818,6 +22846,26 @@ var backendVersion = () => {
22818
22846
  id: data3.id
22819
22847
  }
22820
22848
  });
22849
+ }, resolveCompletedJobs = (data3) => {
22850
+ return request(OpenAPI, {
22851
+ method: "POST",
22852
+ url: "/w/{workspace}/jobs/completed/resolve",
22853
+ path: {
22854
+ workspace: data3.workspace
22855
+ },
22856
+ body: data3.requestBody,
22857
+ mediaType: "application/json"
22858
+ });
22859
+ }, unresolveCompletedJobs = (data3) => {
22860
+ return request(OpenAPI, {
22861
+ method: "POST",
22862
+ url: "/w/{workspace}/jobs/completed/unresolve",
22863
+ path: {
22864
+ workspace: data3.workspace
22865
+ },
22866
+ body: data3.requestBody,
22867
+ mediaType: "application/json"
22868
+ });
22821
22869
  }, cancelQueuedJob = (data3) => {
22822
22870
  return request(OpenAPI, {
22823
22871
  method: "POST",
@@ -22896,6 +22944,19 @@ var backendVersion = () => {
22896
22944
  flow_level: data3.flowLevel
22897
22945
  }
22898
22946
  });
22947
+ }, getWacApprovalUrls = (data3) => {
22948
+ return request(OpenAPI, {
22949
+ method: "GET",
22950
+ url: "/w/{workspace}/jobs/wac_approval_urls/{id}/{step_key}",
22951
+ path: {
22952
+ workspace: data3.workspace,
22953
+ id: data3.id,
22954
+ step_key: data3.stepKey
22955
+ },
22956
+ query: {
22957
+ approver: data3.approver
22958
+ }
22959
+ });
22899
22960
  }, getSlackApprovalPayload = (data3) => {
22900
22961
  return request(OpenAPI, {
22901
22962
  method: "GET",
@@ -24819,6 +24880,8 @@ var backendVersion = () => {
24819
24880
  workspace: data3.workspace
24820
24881
  },
24821
24882
  query: {
24883
+ page: data3.page,
24884
+ per_page: data3.perPage,
24822
24885
  only_member_of: data3.onlyMemberOf
24823
24886
  }
24824
24887
  });
@@ -25687,6 +25750,7 @@ var backendVersion = () => {
25687
25750
  per_page: data3.perPage,
25688
25751
  trigger_kind: data3.triggerKind,
25689
25752
  is_skipped: data3.isSkipped,
25753
+ resolved: data3.resolved,
25690
25754
  is_flow_step: data3.isFlowStep,
25691
25755
  has_null_parent: data3.hasNullParent,
25692
25756
  success: data3.success,
@@ -26003,6 +26067,20 @@ var backendVersion = () => {
26003
26067
  body: data3.requestBody,
26004
26068
  mediaType: "application/json"
26005
26069
  });
26070
+ }, publishHubRawAppRecording = (data3) => {
26071
+ return request(OpenAPI, {
26072
+ method: "POST",
26073
+ url: "/w/{workspace}/hub/raw_apps/{id}/recording",
26074
+ path: {
26075
+ workspace: data3.workspace,
26076
+ id: data3.id
26077
+ },
26078
+ query: {
26079
+ folder: data3.folder
26080
+ },
26081
+ body: data3.requestBody,
26082
+ mediaType: "application/json"
26083
+ });
26006
26084
  }, publishHubScriptRecording = (data3) => {
26007
26085
  return request(OpenAPI, {
26008
26086
  method: "POST",
@@ -26202,6 +26280,7 @@ __export(exports_gen, {
26202
26280
  updateApp: () => updateApp,
26203
26281
  updateAmqpTrigger: () => updateAmqpTrigger,
26204
26282
  unstar: () => unstar,
26283
+ unresolveCompletedJobs: () => unresolveCompletedJobs,
26205
26284
  unassignGhesInstallation: () => unassignGhesInstallation,
26206
26285
  unarchiveWorkspace: () => unarchiveWorkspace,
26207
26286
  toggleWorkspaceErrorHandlerForScript: () => toggleWorkspaceErrorHandlerForScript,
@@ -26308,6 +26387,7 @@ __export(exports_gen, {
26308
26387
  restartWorkerGroup: () => restartWorkerGroup,
26309
26388
  restartFlowAtStep: () => restartFlowAtStep,
26310
26389
  resolveNpmPackageVersion: () => resolveNpmPackageVersion,
26390
+ resolveCompletedJobs: () => resolveCompletedJobs,
26311
26391
  resetPassword: () => resetPassword,
26312
26392
  resetKafkaOffsets: () => resetKafkaOffsets,
26313
26393
  resetDiffTally: () => resetDiffTally,
@@ -26333,6 +26413,7 @@ __export(exports_gen, {
26333
26413
  publishHubScript: () => publishHubScript,
26334
26414
  publishHubResources: () => publishHubResources,
26335
26415
  publishHubResourceType: () => publishHubResourceType,
26416
+ publishHubRawAppRecording: () => publishHubRawAppRecording,
26336
26417
  publishHubRawAppEmbed: () => publishHubRawAppEmbed,
26337
26418
  publishHubRawApp: () => publishHubRawApp,
26338
26419
  publishHubProjectLogo: () => publishHubProjectLogo,
@@ -26409,6 +26490,7 @@ __export(exports_gen, {
26409
26490
  listScriptPaths: () => listScriptPaths,
26410
26491
  listSchedulesWithJobs: () => listSchedulesWithJobs,
26411
26492
  listSchedules: () => listSchedules,
26493
+ listRunnables: () => listRunnables,
26412
26494
  listResourceTypeNames: () => listResourceTypeNames,
26413
26495
  listResourceType: () => listResourceType,
26414
26496
  listResourceNames: () => listResourceNames,
@@ -26537,6 +26619,7 @@ __export(exports_gen, {
26537
26619
  getWorkspaceDefaultApp: () => getWorkspaceDefaultApp,
26538
26620
  getWorkspaceAsSuperAdmin: () => getWorkspaceAsSuperAdmin,
26539
26621
  getWebsocketTrigger: () => getWebsocketTrigger,
26622
+ getWacApprovalUrls: () => getWacApprovalUrls,
26540
26623
  getVolumeStorage: () => getVolumeStorage,
26541
26624
  getVariableValue: () => getVariableValue,
26542
26625
  getVariable: () => getVariable,
@@ -27066,7 +27149,7 @@ var init_auth = __esm(async () => {
27066
27149
  });
27067
27150
 
27068
27151
  // src/core/constants.ts
27069
- var WM_FORK_PREFIX = "wm-fork", VERSION = "1.770.0";
27152
+ var WM_FORK_PREFIX = "wm-fork", VERSION = "1.771.0";
27070
27153
 
27071
27154
  // src/utils/git.ts
27072
27155
  var exports_git = {};
@@ -82088,15 +82171,43 @@ workflow<T>(fn: (...args: any[]) => Promise<T>): void
82088
82171
  /**
82089
82172
  * Suspend the workflow and wait for an external approval.
82090
82173
  *
82091
- * Use \`getResumeUrls()\` (wrapped in \`step()\`) to obtain resume/cancel/approvalPage
82092
- * URLs before calling this function.
82174
+ * Pass \`key\` to name the step, then \`getApprovalUrls(key)\` yields the URLs that
82175
+ * resume exactly this approval — route them through your own channel. Without a
82176
+ * key the steps are named \`approval\`, \`approval_2\`, ...
82177
+ *
82178
+ * @example
82179
+ * const urls = await step("urls", () => getApprovalUrls("manager"));
82180
+ * await step("notify", () => sendEmail(urls.resume, urls.cancel));
82181
+ * const { value, approver } = await waitForApproval({ key: "manager", timeout: 3600 });
82182
+ */
82183
+ waitForApproval(options?: { timeout?: number; form?: object; selfApproval?: boolean; key?: string; }): PromiseLike<{ value: any; approver: string; approved: boolean }>
82184
+
82185
+ /**
82186
+ * Resume/cancel/approval-page URLs bound to one \`waitForApproval\` step.
82187
+ *
82188
+ * Unlike \`getResumeUrls()\`, which signs a random nonce, these address the very
82189
+ * \`resume_job\` record the step's built-in approval buttons use, so they are
82190
+ * stable across replays and safe to embed in a custom notification.
82191
+ *
82192
+ * \`stepKey\` must match the \`key\` given to \`waitForApproval\`. Keys must be unique
82193
+ * within a workflow; reusing one throws rather than silently renaming it. The URL
82194
+ * only resumes while that step is awaiting approval; used at any other moment it is
82195
+ * rejected rather than banking a row a different approval would consume. Send it
82196
+ * ahead of time — approvers just cannot act before the workflow reaches the step.
82197
+ *
82198
+ * \`resume\` and \`cancel\` are step-bound; \`approvalPage\` is not — it opens the job's
82199
+ * approval page, which acts on whichever approval is pending when it is used.
82093
82200
  *
82094
82201
  * @example
82095
- * const urls = await step("urls", () => getResumeUrls());
82096
- * await step("notify", () => sendEmail(urls.approvalPage));
82097
- * const { value, approver } = await waitForApproval({ timeout: 3600 });
82202
+ * const urls = await step("urls", () => getApprovalUrls("manager"));
82203
+ * await step("notify", () => sendEmail(urls.resume, urls.cancel));
82204
+ * await waitForApproval({ key: "manager" });
82098
82205
  */
82099
- waitForApproval(options?: { timeout?: number; form?: object; selfApproval?: boolean; }): PromiseLike<{ value: any; approver: string; approved: boolean }>
82206
+ async getApprovalUrls(stepKey: string = "approval", approver?: string): Promise<{
82207
+ approvalPage: string;
82208
+ resume: string;
82209
+ cancel: string;
82210
+ }>
82100
82211
 
82101
82212
  /**
82102
82213
  * Process items in parallel with optional concurrency control.
@@ -82852,15 +82963,43 @@ workflow<T>(fn: (...args: any[]) => Promise<T>): void
82852
82963
  /**
82853
82964
  * Suspend the workflow and wait for an external approval.
82854
82965
  *
82855
- * Use \`getResumeUrls()\` (wrapped in \`step()\`) to obtain resume/cancel/approvalPage
82856
- * URLs before calling this function.
82966
+ * Pass \`key\` to name the step, then \`getApprovalUrls(key)\` yields the URLs that
82967
+ * resume exactly this approval — route them through your own channel. Without a
82968
+ * key the steps are named \`approval\`, \`approval_2\`, ...
82857
82969
  *
82858
82970
  * @example
82859
- * const urls = await step("urls", () => getResumeUrls());
82860
- * await step("notify", () => sendEmail(urls.approvalPage));
82861
- * const { value, approver } = await waitForApproval({ timeout: 3600 });
82971
+ * const urls = await step("urls", () => getApprovalUrls("manager"));
82972
+ * await step("notify", () => sendEmail(urls.resume, urls.cancel));
82973
+ * const { value, approver } = await waitForApproval({ key: "manager", timeout: 3600 });
82862
82974
  */
82863
- waitForApproval(options?: { timeout?: number; form?: object; selfApproval?: boolean; }): PromiseLike<{ value: any; approver: string; approved: boolean }>
82975
+ waitForApproval(options?: { timeout?: number; form?: object; selfApproval?: boolean; key?: string; }): PromiseLike<{ value: any; approver: string; approved: boolean }>
82976
+
82977
+ /**
82978
+ * Resume/cancel/approval-page URLs bound to one \`waitForApproval\` step.
82979
+ *
82980
+ * Unlike \`getResumeUrls()\`, which signs a random nonce, these address the very
82981
+ * \`resume_job\` record the step's built-in approval buttons use, so they are
82982
+ * stable across replays and safe to embed in a custom notification.
82983
+ *
82984
+ * \`stepKey\` must match the \`key\` given to \`waitForApproval\`. Keys must be unique
82985
+ * within a workflow; reusing one throws rather than silently renaming it. The URL
82986
+ * only resumes while that step is awaiting approval; used at any other moment it is
82987
+ * rejected rather than banking a row a different approval would consume. Send it
82988
+ * ahead of time — approvers just cannot act before the workflow reaches the step.
82989
+ *
82990
+ * \`resume\` and \`cancel\` are step-bound; \`approvalPage\` is not — it opens the job's
82991
+ * approval page, which acts on whichever approval is pending when it is used.
82992
+ *
82993
+ * @example
82994
+ * const urls = await step("urls", () => getApprovalUrls("manager"));
82995
+ * await step("notify", () => sendEmail(urls.resume, urls.cancel));
82996
+ * await waitForApproval({ key: "manager" });
82997
+ */
82998
+ async getApprovalUrls(stepKey: string = "approval", approver?: string): Promise<{
82999
+ approvalPage: string;
83000
+ resume: string;
83001
+ cancel: string;
83002
+ }>
82864
83003
 
82865
83004
  /**
82866
83005
  * Process items in parallel with optional concurrency control.
@@ -83710,15 +83849,43 @@ workflow<T>(fn: (...args: any[]) => Promise<T>): void
83710
83849
  /**
83711
83850
  * Suspend the workflow and wait for an external approval.
83712
83851
  *
83713
- * Use \`getResumeUrls()\` (wrapped in \`step()\`) to obtain resume/cancel/approvalPage
83714
- * URLs before calling this function.
83852
+ * Pass \`key\` to name the step, then \`getApprovalUrls(key)\` yields the URLs that
83853
+ * resume exactly this approval — route them through your own channel. Without a
83854
+ * key the steps are named \`approval\`, \`approval_2\`, ...
83715
83855
  *
83716
83856
  * @example
83717
- * const urls = await step("urls", () => getResumeUrls());
83718
- * await step("notify", () => sendEmail(urls.approvalPage));
83719
- * const { value, approver } = await waitForApproval({ timeout: 3600 });
83857
+ * const urls = await step("urls", () => getApprovalUrls("manager"));
83858
+ * await step("notify", () => sendEmail(urls.resume, urls.cancel));
83859
+ * const { value, approver } = await waitForApproval({ key: "manager", timeout: 3600 });
83720
83860
  */
83721
- waitForApproval(options?: { timeout?: number; form?: object; selfApproval?: boolean; }): PromiseLike<{ value: any; approver: string; approved: boolean }>
83861
+ waitForApproval(options?: { timeout?: number; form?: object; selfApproval?: boolean; key?: string; }): PromiseLike<{ value: any; approver: string; approved: boolean }>
83862
+
83863
+ /**
83864
+ * Resume/cancel/approval-page URLs bound to one \`waitForApproval\` step.
83865
+ *
83866
+ * Unlike \`getResumeUrls()\`, which signs a random nonce, these address the very
83867
+ * \`resume_job\` record the step's built-in approval buttons use, so they are
83868
+ * stable across replays and safe to embed in a custom notification.
83869
+ *
83870
+ * \`stepKey\` must match the \`key\` given to \`waitForApproval\`. Keys must be unique
83871
+ * within a workflow; reusing one throws rather than silently renaming it. The URL
83872
+ * only resumes while that step is awaiting approval; used at any other moment it is
83873
+ * rejected rather than banking a row a different approval would consume. Send it
83874
+ * ahead of time — approvers just cannot act before the workflow reaches the step.
83875
+ *
83876
+ * \`resume\` and \`cancel\` are step-bound; \`approvalPage\` is not — it opens the job's
83877
+ * approval page, which acts on whichever approval is pending when it is used.
83878
+ *
83879
+ * @example
83880
+ * const urls = await step("urls", () => getApprovalUrls("manager"));
83881
+ * await step("notify", () => sendEmail(urls.resume, urls.cancel));
83882
+ * await waitForApproval({ key: "manager" });
83883
+ */
83884
+ async getApprovalUrls(stepKey: string = "approval", approver?: string): Promise<{
83885
+ approvalPage: string;
83886
+ resume: string;
83887
+ cancel: string;
83888
+ }>
83722
83889
 
83723
83890
  /**
83724
83891
  * Process items in parallel with optional concurrency control.
@@ -85304,6 +85471,17 @@ def get_shared_state(path: str = 'state.json') -> None
85304
85471
  # Dictionary with approvalPage, resume, and cancel URLs
85305
85472
  def get_resume_urls(approver: str = None, flow_level: bool = None) -> dict
85306
85473
 
85474
+ # Get the resume URLs bound to one \`\`wait_for_approval\`\` step of this workflow.
85475
+ #
85476
+ # Args:
85477
+ # step_key: Checkpoint key of the approval step, as passed to
85478
+ # \`\`wait_for_approval(key=...)\`\`
85479
+ # approver: Optional approver name
85480
+ #
85481
+ # Returns:
85482
+ # Dictionary with approvalPage, resume, and cancel URLs
85483
+ def get_approval_urls(step_key: str = 'approval', approver: str = None) -> dict
85484
+
85307
85485
  # Sends an interactive approval request via Slack, allowing optional customization of the message, approver, and form fields.
85308
85486
  #
85309
85487
  # **[Enterprise Edition Only]** To include form fields in the Slack approval request, use the "Advanced -> Suspend -> Form" functionality.
@@ -85580,8 +85758,9 @@ async def sleep(seconds: int)
85580
85758
 
85581
85759
  # Suspend the workflow and wait for an external approval.
85582
85760
  #
85583
- # Use \`\`get_resume_urls()\`\` (wrapped in \`\`step()\`\`) to obtain
85584
- # resume/cancel/approval URLs before calling this function.
85761
+ # Pass \`\`key\`\` to name the step, then \`\`get_approval_urls(key)\`\` yields the URLs
85762
+ # that resume exactly this approval route them through your own channel.
85763
+ # Without a key the steps are named \`\`approval\`\`, \`\`approval_2\`\`, ...
85585
85764
  #
85586
85765
  # Returns a dict with \`\`value\`\` (form data), \`\`approver\`\`, and \`\`approved\`\`.
85587
85766
  #
@@ -85589,13 +85768,14 @@ async def sleep(seconds: int)
85589
85768
  # timeout: Approval timeout in seconds (default 1800).
85590
85769
  # form: Optional form schema for the approval page.
85591
85770
  # self_approval: Whether the user who triggered the flow can approve it (default True).
85771
+ # key: Optional checkpoint key naming this approval step.
85592
85772
  #
85593
85773
  # Example::
85594
85774
  #
85595
- # urls = await step("urls", lambda: get_resume_urls())
85596
- # await step("notify", lambda: send_email(urls["approvalPage"]))
85597
- # result = await wait_for_approval(timeout=3600)
85598
- async def wait_for_approval(timeout: int = 1800, form: dict | None = None, self_approval: bool = True) -> dict
85775
+ # urls = await step("urls", lambda: get_approval_urls("manager"))
85776
+ # await step("notify", lambda: send_email(urls["resume"], urls["cancel"]))
85777
+ # result = await wait_for_approval(key="manager", timeout=3600)
85778
+ async def wait_for_approval(timeout: int = 1800, form: dict | None = None, self_approval: bool = True, key: str | None = None) -> dict
85599
85779
 
85600
85780
  # Process items in parallel with optional concurrency control.
85601
85781
  #
@@ -86672,6 +86852,18 @@ const user = await backend.get_user({ user_id: '123' });
86672
86852
 
86673
86853
  The frontend cannot reach datatables, workspace items, or external services on its own — it goes through \`backend.<key>(args)\` for everything server-side.
86674
86854
 
86855
+ ### Keeping data out of recorded demos
86856
+
86857
+ An app can be demoed by recording a session: every interaction becomes a step carrying a snapshot of the page, replayed publicly or on the Hub. Password inputs are masked automatically. Mark anything else that must not appear with \`data-wm-no-record\` — the whole marked subtree is dropped from every snapshot, along with its values and the step's own metadata:
86858
+
86859
+ \`\`\`tsx
86860
+ <label data-wm-no-record>
86861
+ Customer SSN <input value={ssn} onChange={onSsn} />
86862
+ </label>
86863
+ \`\`\`
86864
+
86865
+ Apply it to customer data, internal notes and anything else a viewer of the demo should not see. It costs nothing when the app is never recorded.
86866
+
86675
86867
  ## Backend runnables
86676
86868
 
86677
86869
  Each runnable has a unique key (used to call it from the frontend) and one of four types:
@@ -86774,6 +86966,7 @@ def main(user_id: str):
86774
86966
  3. **Keep runnables focused** — one function per runnable; small surface area.
86775
86967
  4. **Use descriptive keys** — \`get_user\`, not \`a\`.
86776
86968
  5. **Always whitelist tables** — adding a runnable that queries a new table requires the table to be in \`data.tables\` first.
86969
+ 6. **Mark sensitive UI with \`data-wm-no-record\`** — it is what keeps that data out of a recorded demo; passwords are handled for you.
86777
86970
  `,
86778
86971
  triggers: `---
86779
86972
  name: triggers
@@ -87227,7 +87420,7 @@ import {
87227
87420
  step,
87228
87421
  sleep,
87229
87422
  waitForApproval,
87230
- getResumeUrls,
87423
+ getApprovalUrls,
87231
87424
  parallel,
87232
87425
  workflow,
87233
87426
  } from "windmill-client";
@@ -87245,7 +87438,7 @@ export const main = workflow(async (x: string) => {
87245
87438
  Python:
87246
87439
 
87247
87440
  \`\`\`python
87248
- from wmill import task, task_script, task_flow, step, sleep, wait_for_approval, get_resume_urls, parallel, workflow
87441
+ from wmill import task, task_script, task_flow, step, sleep, wait_for_approval, get_approval_urls, parallel, workflow
87249
87442
 
87250
87443
  @task()
87251
87444
  async def process(x: str) -> str:
@@ -87329,12 +87522,13 @@ output = await pipeline(input=data)
87329
87522
  Use \`step()\` for lightweight inline values that must not change during replay:
87330
87523
 
87331
87524
  \`\`\`typescript
87332
- const urls = await step("get_urls", () => getResumeUrls());
87333
87525
  const startedAt = await step("started_at", () => new Date().toISOString());
87334
87526
  \`\`\`
87335
87527
 
87336
87528
  \`\`\`python
87337
- urls = await step("get_urls", lambda: get_resume_urls())
87529
+ from datetime import datetime
87530
+
87531
+ started_at = await step("started_at", lambda: datetime.now().isoformat())
87338
87532
  \`\`\`
87339
87533
 
87340
87534
  Use stable, descriptive step names. Do not generate step names dynamically.
@@ -87359,20 +87553,28 @@ Only parallelize independent steps. Do not read the result of a task before it i
87359
87553
 
87360
87554
  ## Approvals
87361
87555
 
87362
- Generate resume URLs inside \`step()\` before sending them:
87556
+ Name the approval step and generate its URLs inside \`step()\` before sending them.
87557
+ \`getApprovalUrls\` / \`get_approval_urls\` returns the URLs bound to that step, the same
87558
+ ones its built-in approve/reject buttons use:
87363
87559
 
87364
87560
  \`\`\`typescript
87365
- const urls = await step("get_urls", () => getResumeUrls());
87366
- await step("notify", () => sendApprovalEmail(urls.approvalPage));
87367
- const approval = await waitForApproval({ timeout: 3600 });
87561
+ const urls = await step("urls", () => getApprovalUrls("manager"));
87562
+ await step("notify", () => sendApprovalEmail(urls.resume, urls.cancel));
87563
+ const approval = await waitForApproval({ key: "manager", timeout: 3600 });
87368
87564
  \`\`\`
87369
87565
 
87370
87566
  \`\`\`python
87371
- urls = await step("get_urls", lambda: get_resume_urls())
87372
- await step("notify", lambda: send_approval_email(urls["approvalPage"]))
87373
- approval = await wait_for_approval(timeout=3600)
87567
+ urls = await step("urls", lambda: get_approval_urls("manager"))
87568
+ await step("notify", lambda: send_approval_email(urls["resume"], urls["cancel"]))
87569
+ approval = await wait_for_approval(key="manager", timeout=3600)
87374
87570
  \`\`\`
87375
87571
 
87572
+ With several approvals in one workflow, give each its own key so each notification
87573
+ resumes its own step. Keys must be unique — reusing one raises an error rather than
87574
+ silently renaming the step. A minted URL only resumes while its own step is awaiting
87575
+ approval; used at any other moment it is rejected rather than resuming the wrong one. \`getResumeUrls()\` / \`get_resume_urls()\` still works but signs a
87576
+ random nonce, so its URLs are not tied to any particular approval step.
87577
+
87376
87578
  \`selfApproval: false\` and \`self_approval=False\` are Enterprise-only approval behavior. Do not use them unless the user asks for that behavior.
87377
87579
 
87378
87580
  ## Error Handling
@@ -87386,7 +87588,7 @@ TypeScript: avoid broad \`try/catch\` around WAC SDK calls. The SDK uses an inte
87386
87588
 
87387
87589
  ## TypeScript Workflow-as-Code API (windmill-client)
87388
87590
 
87389
- Import: \`import { workflow, task, taskScript, taskFlow, step, sleep, waitForApproval, getResumeUrls, parallel } from "windmill-client"\`
87591
+ Import: \`import { workflow, task, taskScript, taskFlow, step, sleep, waitForApproval, getApprovalUrls, getResumeUrls, parallel } from "windmill-client"\`
87390
87592
 
87391
87593
  \`\`\`typescript
87392
87594
  export interface TaskOptions {
@@ -87456,15 +87658,39 @@ export async function sleep(seconds: number): Promise<void>
87456
87658
  /**
87457
87659
  * Suspend the workflow and wait for an external approval.
87458
87660
  *
87459
- * Use \`getResumeUrls()\` (wrapped in \`step()\`) to obtain resume/cancel/approvalPage
87460
- * URLs before calling this function.
87661
+ * Pass \`key\` to name the step, then \`getApprovalUrls(key)\` yields the URLs that
87662
+ * resume exactly this approval — route them through your own channel. Without a
87663
+ * key the steps are named \`approval\`, \`approval_2\`, ...
87461
87664
  *
87462
87665
  * @example
87463
- * const urls = await step("urls", () => getResumeUrls());
87464
- * await step("notify", () => sendEmail(urls.approvalPage));
87465
- * const { value, approver } = await waitForApproval({ timeout: 3600 });
87666
+ * const urls = await step("urls", () => getApprovalUrls("manager"));
87667
+ * await step("notify", () => sendEmail(urls.resume, urls.cancel));
87668
+ * const { value, approver } = await waitForApproval({ key: "manager", timeout: 3600 });
87466
87669
  */
87467
- export function waitForApproval(options?: { timeout?: number; form?: object; selfApproval?: boolean; }): PromiseLike<{ value: any; approver: string; approved: boolean }>
87670
+ export function waitForApproval(options?: { timeout?: number; form?: object; selfApproval?: boolean; key?: string; }): PromiseLike<{ value: any; approver: string; approved: boolean }>
87671
+
87672
+ /**
87673
+ * Resume/cancel/approval-page URLs bound to one \`waitForApproval\` step.
87674
+ *
87675
+ * Unlike \`getResumeUrls()\`, which signs a random nonce, these address the very
87676
+ * \`resume_job\` record the step's built-in approval buttons use, so they are
87677
+ * stable across replays and safe to embed in a custom notification.
87678
+ *
87679
+ * \`stepKey\` must match the \`key\` given to \`waitForApproval\`. Keys must be unique
87680
+ * within a workflow; reusing one throws rather than silently renaming it. The URL
87681
+ * only resumes while that step is awaiting approval; used at any other moment it is
87682
+ * rejected rather than banking a row a different approval would consume. Send it
87683
+ * ahead of time — approvers just cannot act before the workflow reaches the step.
87684
+ *
87685
+ * \`resume\` and \`cancel\` are step-bound; \`approvalPage\` is not — it opens the job's
87686
+ * approval page, which acts on whichever approval is pending when it is used.
87687
+ *
87688
+ * @example
87689
+ * const urls = await step("urls", () => getApprovalUrls("manager"));
87690
+ * await step("notify", () => sendEmail(urls.resume, urls.cancel));
87691
+ * await waitForApproval({ key: "manager" });
87692
+ */
87693
+ export async function getApprovalUrls(stepKey: string = "approval", approver?: string): Promise<{ approvalPage: string; resume: string; cancel: string; }>
87468
87694
 
87469
87695
  /**
87470
87696
  * Process items in parallel with optional concurrency control.
@@ -87482,7 +87708,7 @@ export async function parallel<T, R>(items: T[], fn: (item: T) => PromiseLike<R>
87482
87708
 
87483
87709
  ## Python Workflow-as-Code API (wmill)
87484
87710
 
87485
- Import: \`from wmill import workflow, task, task_script, task_flow, step, sleep, wait_for_approval, get_resume_urls, parallel, TaskError\`
87711
+ Import: \`from wmill import workflow, task, task_script, task_flow, step, sleep, wait_for_approval, get_approval_urls, get_resume_urls, parallel, TaskError\`
87486
87712
 
87487
87713
  \`\`\`python
87488
87714
  # Raised when a WAC task step failed.
@@ -87570,8 +87796,9 @@ async def sleep(seconds: int)
87570
87796
 
87571
87797
  # Suspend the workflow and wait for an external approval.
87572
87798
  #
87573
- # Use \`\`get_resume_urls()\`\` (wrapped in \`\`step()\`\`) to obtain
87574
- # resume/cancel/approval URLs before calling this function.
87799
+ # Pass \`\`key\`\` to name the step, then \`\`get_approval_urls(key)\`\` yields the URLs
87800
+ # that resume exactly this approval route them through your own channel.
87801
+ # Without a key the steps are named \`\`approval\`\`, \`\`approval_2\`\`, ...
87575
87802
  #
87576
87803
  # Returns a dict with \`\`value\`\` (form data), \`\`approver\`\`, and \`\`approved\`\`.
87577
87804
  #
@@ -87579,13 +87806,37 @@ async def sleep(seconds: int)
87579
87806
  # timeout: Approval timeout in seconds (default 1800).
87580
87807
  # form: Optional form schema for the approval page.
87581
87808
  # self_approval: Whether the user who triggered the flow can approve it (default True).
87809
+ # key: Optional checkpoint key naming this approval step.
87582
87810
  #
87583
87811
  # Example::
87584
87812
  #
87585
- # urls = await step("urls", lambda: get_resume_urls())
87586
- # await step("notify", lambda: send_email(urls["approvalPage"]))
87587
- # result = await wait_for_approval(timeout=3600)
87588
- async def wait_for_approval(timeout: int = 1800, form: dict | None = None, self_approval: bool = True) -> dict
87813
+ # urls = await step("urls", lambda: get_approval_urls("manager"))
87814
+ # await step("notify", lambda: send_email(urls["resume"], urls["cancel"]))
87815
+ # result = await wait_for_approval(key="manager", timeout=3600)
87816
+ async def wait_for_approval(timeout: int = 1800, form: dict | None = None, self_approval: bool = True, key: str | None = None) -> dict
87817
+
87818
+ # Get the resume/cancel/approval-page URLs bound to one \`\`wait_for_approval\`\` step.
87819
+ #
87820
+ # Unlike :func:\`get_resume_urls\`, which signs a random nonce, these address the
87821
+ # very \`\`resume_job\`\` record the step's built-in approval buttons use, so they
87822
+ # are stable across replays and safe to embed in a custom notification.
87823
+ #
87824
+ # Args:
87825
+ # step_key: Checkpoint key of the approval step, as passed to
87826
+ # \`\`wait_for_approval(key=...)\`\`. Keys must be unique within a workflow;
87827
+ # reusing one raises rather than silently renaming it. The URL only
87828
+ # resumes while that step is awaiting approval; used at any other moment
87829
+ # it is rejected rather than banking a row a different approval would
87830
+ # consume. Send it ahead of time — approvers just cannot act before the
87831
+ # workflow reaches the step.
87832
+ # \`\`resume\`\` and \`\`cancel\`\` are step-bound; \`\`approvalPage\`\` is not — it
87833
+ # opens the job's approval page, which acts on whichever approval is
87834
+ # pending when it is used.
87835
+ # approver: Optional approver name
87836
+ #
87837
+ # Returns:
87838
+ # Dictionary with approvalPage, resume, and cancel URLs
87839
+ def get_approval_urls(step_key: str = 'approval', approver: str = None) -> dict
87589
87840
 
87590
87841
  # Process items in parallel with optional concurrency control.
87591
87842
  #
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "windmill-cli",
3
- "version": "1.770.0",
3
+ "version": "1.771.0",
4
4
  "description": "CLI for Windmill",
5
5
  "license": "Apache 2.0",
6
6
  "type": "module",