windmill-cli 1.770.0 → 1.771.1

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 +290 -55
  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.1",
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,7 +17236,7 @@ __export(exports_services_gen, {
17234
17236
  publishHubScript: () => publishHubScript,
17235
17237
  publishHubResources: () => publishHubResources,
17236
17238
  publishHubResourceType: () => publishHubResourceType,
17237
- publishHubRawAppEmbed: () => publishHubRawAppEmbed,
17239
+ publishHubRawAppRecording: () => publishHubRawAppRecording,
17238
17240
  publishHubRawApp: () => publishHubRawApp,
17239
17241
  publishHubProjectLogo: () => publishHubProjectLogo,
17240
17242
  publishHubPipelineRecording: () => publishHubPipelineRecording,
@@ -17310,6 +17312,7 @@ __export(exports_services_gen, {
17310
17312
  listScriptPaths: () => listScriptPaths,
17311
17313
  listSchedulesWithJobs: () => listSchedulesWithJobs,
17312
17314
  listSchedules: () => listSchedules,
17315
+ listRunnables: () => listRunnables,
17313
17316
  listResourceTypeNames: () => listResourceTypeNames,
17314
17317
  listResourceType: () => listResourceType,
17315
17318
  listResourceNames: () => listResourceNames,
@@ -17438,6 +17441,7 @@ __export(exports_services_gen, {
17438
17441
  getWorkspaceDefaultApp: () => getWorkspaceDefaultApp,
17439
17442
  getWorkspaceAsSuperAdmin: () => getWorkspaceAsSuperAdmin,
17440
17443
  getWebsocketTrigger: () => getWebsocketTrigger,
17444
+ getWacApprovalUrls: () => getWacApprovalUrls,
17441
17445
  getVolumeStorage: () => getVolumeStorage,
17442
17446
  getVariableValue: () => getVariableValue,
17443
17447
  getVariable: () => getVariable,
@@ -21329,6 +21333,26 @@ var backendVersion = () => {
21329
21333
  workspace: data3.workspace
21330
21334
  }
21331
21335
  });
21336
+ }, listRunnables = (data3) => {
21337
+ return request(OpenAPI, {
21338
+ method: "GET",
21339
+ url: "/w/{workspace}/runnables/list",
21340
+ path: {
21341
+ workspace: data3.workspace
21342
+ },
21343
+ query: {
21344
+ order_by: data3.orderBy,
21345
+ order_desc: data3.orderDesc,
21346
+ kinds: data3.kinds,
21347
+ show_archived: data3.showArchived,
21348
+ include_without_main: data3.includeWithoutMain,
21349
+ path_start: data3.pathStart,
21350
+ label: data3.label,
21351
+ search: data3.search,
21352
+ per_page: data3.perPage,
21353
+ cursor: data3.cursor
21354
+ }
21355
+ });
21332
21356
  }, listFlows = (data3) => {
21333
21357
  return request(OpenAPI, {
21334
21358
  method: "GET",
@@ -22375,6 +22399,7 @@ var backendVersion = () => {
22375
22399
  page: data3.page,
22376
22400
  per_page: data3.perPage,
22377
22401
  is_skipped: data3.isSkipped,
22402
+ resolved: data3.resolved,
22378
22403
  is_flow_step: data3.isFlowStep,
22379
22404
  has_null_parent: data3.hasNullParent,
22380
22405
  success: data3.success,
@@ -22492,6 +22517,7 @@ var backendVersion = () => {
22492
22517
  page: data3.page,
22493
22518
  per_page: data3.perPage,
22494
22519
  is_skipped: data3.isSkipped,
22520
+ resolved: data3.resolved,
22495
22521
  is_flow_step: data3.isFlowStep,
22496
22522
  has_null_parent: data3.hasNullParent,
22497
22523
  is_not_schedule: data3.isNotSchedule
@@ -22586,6 +22612,7 @@ var backendVersion = () => {
22586
22612
  per_page: data3.perPage,
22587
22613
  trigger_kind: data3.triggerKind,
22588
22614
  is_skipped: data3.isSkipped,
22615
+ resolved: data3.resolved,
22589
22616
  is_flow_step: data3.isFlowStep,
22590
22617
  has_null_parent: data3.hasNullParent,
22591
22618
  success: data3.success,
@@ -22818,6 +22845,26 @@ var backendVersion = () => {
22818
22845
  id: data3.id
22819
22846
  }
22820
22847
  });
22848
+ }, resolveCompletedJobs = (data3) => {
22849
+ return request(OpenAPI, {
22850
+ method: "POST",
22851
+ url: "/w/{workspace}/jobs/completed/resolve",
22852
+ path: {
22853
+ workspace: data3.workspace
22854
+ },
22855
+ body: data3.requestBody,
22856
+ mediaType: "application/json"
22857
+ });
22858
+ }, unresolveCompletedJobs = (data3) => {
22859
+ return request(OpenAPI, {
22860
+ method: "POST",
22861
+ url: "/w/{workspace}/jobs/completed/unresolve",
22862
+ path: {
22863
+ workspace: data3.workspace
22864
+ },
22865
+ body: data3.requestBody,
22866
+ mediaType: "application/json"
22867
+ });
22821
22868
  }, cancelQueuedJob = (data3) => {
22822
22869
  return request(OpenAPI, {
22823
22870
  method: "POST",
@@ -22896,6 +22943,19 @@ var backendVersion = () => {
22896
22943
  flow_level: data3.flowLevel
22897
22944
  }
22898
22945
  });
22946
+ }, getWacApprovalUrls = (data3) => {
22947
+ return request(OpenAPI, {
22948
+ method: "GET",
22949
+ url: "/w/{workspace}/jobs/wac_approval_urls/{id}/{step_key}",
22950
+ path: {
22951
+ workspace: data3.workspace,
22952
+ id: data3.id,
22953
+ step_key: data3.stepKey
22954
+ },
22955
+ query: {
22956
+ approver: data3.approver
22957
+ }
22958
+ });
22899
22959
  }, getSlackApprovalPayload = (data3) => {
22900
22960
  return request(OpenAPI, {
22901
22961
  method: "GET",
@@ -24819,6 +24879,8 @@ var backendVersion = () => {
24819
24879
  workspace: data3.workspace
24820
24880
  },
24821
24881
  query: {
24882
+ page: data3.page,
24883
+ per_page: data3.perPage,
24822
24884
  only_member_of: data3.onlyMemberOf
24823
24885
  }
24824
24886
  });
@@ -25687,6 +25749,7 @@ var backendVersion = () => {
25687
25749
  per_page: data3.perPage,
25688
25750
  trigger_kind: data3.triggerKind,
25689
25751
  is_skipped: data3.isSkipped,
25752
+ resolved: data3.resolved,
25690
25753
  is_flow_step: data3.isFlowStep,
25691
25754
  has_null_parent: data3.hasNullParent,
25692
25755
  success: data3.success,
@@ -25989,10 +26052,10 @@ var backendVersion = () => {
25989
26052
  body: data3.requestBody,
25990
26053
  mediaType: "application/json"
25991
26054
  });
25992
- }, publishHubRawAppEmbed = (data3) => {
26055
+ }, publishHubRawAppRecording = (data3) => {
25993
26056
  return request(OpenAPI, {
25994
26057
  method: "POST",
25995
- url: "/w/{workspace}/hub/raw_apps/{id}/embed",
26058
+ url: "/w/{workspace}/hub/raw_apps/{id}/recording",
25996
26059
  path: {
25997
26060
  workspace: data3.workspace,
25998
26061
  id: data3.id
@@ -26202,6 +26265,7 @@ __export(exports_gen, {
26202
26265
  updateApp: () => updateApp,
26203
26266
  updateAmqpTrigger: () => updateAmqpTrigger,
26204
26267
  unstar: () => unstar,
26268
+ unresolveCompletedJobs: () => unresolveCompletedJobs,
26205
26269
  unassignGhesInstallation: () => unassignGhesInstallation,
26206
26270
  unarchiveWorkspace: () => unarchiveWorkspace,
26207
26271
  toggleWorkspaceErrorHandlerForScript: () => toggleWorkspaceErrorHandlerForScript,
@@ -26308,6 +26372,7 @@ __export(exports_gen, {
26308
26372
  restartWorkerGroup: () => restartWorkerGroup,
26309
26373
  restartFlowAtStep: () => restartFlowAtStep,
26310
26374
  resolveNpmPackageVersion: () => resolveNpmPackageVersion,
26375
+ resolveCompletedJobs: () => resolveCompletedJobs,
26311
26376
  resetPassword: () => resetPassword,
26312
26377
  resetKafkaOffsets: () => resetKafkaOffsets,
26313
26378
  resetDiffTally: () => resetDiffTally,
@@ -26333,7 +26398,7 @@ __export(exports_gen, {
26333
26398
  publishHubScript: () => publishHubScript,
26334
26399
  publishHubResources: () => publishHubResources,
26335
26400
  publishHubResourceType: () => publishHubResourceType,
26336
- publishHubRawAppEmbed: () => publishHubRawAppEmbed,
26401
+ publishHubRawAppRecording: () => publishHubRawAppRecording,
26337
26402
  publishHubRawApp: () => publishHubRawApp,
26338
26403
  publishHubProjectLogo: () => publishHubProjectLogo,
26339
26404
  publishHubPipelineRecording: () => publishHubPipelineRecording,
@@ -26409,6 +26474,7 @@ __export(exports_gen, {
26409
26474
  listScriptPaths: () => listScriptPaths,
26410
26475
  listSchedulesWithJobs: () => listSchedulesWithJobs,
26411
26476
  listSchedules: () => listSchedules,
26477
+ listRunnables: () => listRunnables,
26412
26478
  listResourceTypeNames: () => listResourceTypeNames,
26413
26479
  listResourceType: () => listResourceType,
26414
26480
  listResourceNames: () => listResourceNames,
@@ -26537,6 +26603,7 @@ __export(exports_gen, {
26537
26603
  getWorkspaceDefaultApp: () => getWorkspaceDefaultApp,
26538
26604
  getWorkspaceAsSuperAdmin: () => getWorkspaceAsSuperAdmin,
26539
26605
  getWebsocketTrigger: () => getWebsocketTrigger,
26606
+ getWacApprovalUrls: () => getWacApprovalUrls,
26540
26607
  getVolumeStorage: () => getVolumeStorage,
26541
26608
  getVariableValue: () => getVariableValue,
26542
26609
  getVariable: () => getVariable,
@@ -27066,7 +27133,7 @@ var init_auth = __esm(async () => {
27066
27133
  });
27067
27134
 
27068
27135
  // src/core/constants.ts
27069
- var WM_FORK_PREFIX = "wm-fork", VERSION = "1.770.0";
27136
+ var WM_FORK_PREFIX = "wm-fork", VERSION = "1.771.1";
27070
27137
 
27071
27138
  // src/utils/git.ts
27072
27139
  var exports_git = {};
@@ -82088,15 +82155,43 @@ workflow<T>(fn: (...args: any[]) => Promise<T>): void
82088
82155
  /**
82089
82156
  * Suspend the workflow and wait for an external approval.
82090
82157
  *
82091
- * Use \`getResumeUrls()\` (wrapped in \`step()\`) to obtain resume/cancel/approvalPage
82092
- * URLs before calling this function.
82158
+ * Pass \`key\` to name the step, then \`getApprovalUrls(key)\` yields the URLs that
82159
+ * resume exactly this approval — route them through your own channel. Without a
82160
+ * key the steps are named \`approval\`, \`approval_2\`, ...
82161
+ *
82162
+ * @example
82163
+ * const urls = await step("urls", () => getApprovalUrls("manager"));
82164
+ * await step("notify", () => sendEmail(urls.resume, urls.cancel));
82165
+ * const { value, approver } = await waitForApproval({ key: "manager", timeout: 3600 });
82166
+ */
82167
+ waitForApproval(options?: { timeout?: number; form?: object; selfApproval?: boolean; key?: string; }): PromiseLike<{ value: any; approver: string; approved: boolean }>
82168
+
82169
+ /**
82170
+ * Resume/cancel/approval-page URLs bound to one \`waitForApproval\` step.
82171
+ *
82172
+ * Unlike \`getResumeUrls()\`, which signs a random nonce, these address the very
82173
+ * \`resume_job\` record the step's built-in approval buttons use, so they are
82174
+ * stable across replays and safe to embed in a custom notification.
82175
+ *
82176
+ * \`stepKey\` must match the \`key\` given to \`waitForApproval\`. Keys must be unique
82177
+ * within a workflow; reusing one throws rather than silently renaming it. The URL
82178
+ * only resumes while that step is awaiting approval; used at any other moment it is
82179
+ * rejected rather than banking a row a different approval would consume. Send it
82180
+ * ahead of time — approvers just cannot act before the workflow reaches the step.
82181
+ *
82182
+ * \`resume\` and \`cancel\` are step-bound; \`approvalPage\` is not — it opens the job's
82183
+ * approval page, which acts on whichever approval is pending when it is used.
82093
82184
  *
82094
82185
  * @example
82095
- * const urls = await step("urls", () => getResumeUrls());
82096
- * await step("notify", () => sendEmail(urls.approvalPage));
82097
- * const { value, approver } = await waitForApproval({ timeout: 3600 });
82186
+ * const urls = await step("urls", () => getApprovalUrls("manager"));
82187
+ * await step("notify", () => sendEmail(urls.resume, urls.cancel));
82188
+ * await waitForApproval({ key: "manager" });
82098
82189
  */
82099
- waitForApproval(options?: { timeout?: number; form?: object; selfApproval?: boolean; }): PromiseLike<{ value: any; approver: string; approved: boolean }>
82190
+ async getApprovalUrls(stepKey: string = "approval", approver?: string): Promise<{
82191
+ approvalPage: string;
82192
+ resume: string;
82193
+ cancel: string;
82194
+ }>
82100
82195
 
82101
82196
  /**
82102
82197
  * Process items in parallel with optional concurrency control.
@@ -82852,15 +82947,43 @@ workflow<T>(fn: (...args: any[]) => Promise<T>): void
82852
82947
  /**
82853
82948
  * Suspend the workflow and wait for an external approval.
82854
82949
  *
82855
- * Use \`getResumeUrls()\` (wrapped in \`step()\`) to obtain resume/cancel/approvalPage
82856
- * URLs before calling this function.
82950
+ * Pass \`key\` to name the step, then \`getApprovalUrls(key)\` yields the URLs that
82951
+ * resume exactly this approval — route them through your own channel. Without a
82952
+ * key the steps are named \`approval\`, \`approval_2\`, ...
82857
82953
  *
82858
82954
  * @example
82859
- * const urls = await step("urls", () => getResumeUrls());
82860
- * await step("notify", () => sendEmail(urls.approvalPage));
82861
- * const { value, approver } = await waitForApproval({ timeout: 3600 });
82955
+ * const urls = await step("urls", () => getApprovalUrls("manager"));
82956
+ * await step("notify", () => sendEmail(urls.resume, urls.cancel));
82957
+ * const { value, approver } = await waitForApproval({ key: "manager", timeout: 3600 });
82862
82958
  */
82863
- waitForApproval(options?: { timeout?: number; form?: object; selfApproval?: boolean; }): PromiseLike<{ value: any; approver: string; approved: boolean }>
82959
+ waitForApproval(options?: { timeout?: number; form?: object; selfApproval?: boolean; key?: string; }): PromiseLike<{ value: any; approver: string; approved: boolean }>
82960
+
82961
+ /**
82962
+ * Resume/cancel/approval-page URLs bound to one \`waitForApproval\` step.
82963
+ *
82964
+ * Unlike \`getResumeUrls()\`, which signs a random nonce, these address the very
82965
+ * \`resume_job\` record the step's built-in approval buttons use, so they are
82966
+ * stable across replays and safe to embed in a custom notification.
82967
+ *
82968
+ * \`stepKey\` must match the \`key\` given to \`waitForApproval\`. Keys must be unique
82969
+ * within a workflow; reusing one throws rather than silently renaming it. The URL
82970
+ * only resumes while that step is awaiting approval; used at any other moment it is
82971
+ * rejected rather than banking a row a different approval would consume. Send it
82972
+ * ahead of time — approvers just cannot act before the workflow reaches the step.
82973
+ *
82974
+ * \`resume\` and \`cancel\` are step-bound; \`approvalPage\` is not — it opens the job's
82975
+ * approval page, which acts on whichever approval is pending when it is used.
82976
+ *
82977
+ * @example
82978
+ * const urls = await step("urls", () => getApprovalUrls("manager"));
82979
+ * await step("notify", () => sendEmail(urls.resume, urls.cancel));
82980
+ * await waitForApproval({ key: "manager" });
82981
+ */
82982
+ async getApprovalUrls(stepKey: string = "approval", approver?: string): Promise<{
82983
+ approvalPage: string;
82984
+ resume: string;
82985
+ cancel: string;
82986
+ }>
82864
82987
 
82865
82988
  /**
82866
82989
  * Process items in parallel with optional concurrency control.
@@ -83710,15 +83833,43 @@ workflow<T>(fn: (...args: any[]) => Promise<T>): void
83710
83833
  /**
83711
83834
  * Suspend the workflow and wait for an external approval.
83712
83835
  *
83713
- * Use \`getResumeUrls()\` (wrapped in \`step()\`) to obtain resume/cancel/approvalPage
83714
- * URLs before calling this function.
83836
+ * Pass \`key\` to name the step, then \`getApprovalUrls(key)\` yields the URLs that
83837
+ * resume exactly this approval — route them through your own channel. Without a
83838
+ * key the steps are named \`approval\`, \`approval_2\`, ...
83839
+ *
83840
+ * @example
83841
+ * const urls = await step("urls", () => getApprovalUrls("manager"));
83842
+ * await step("notify", () => sendEmail(urls.resume, urls.cancel));
83843
+ * const { value, approver } = await waitForApproval({ key: "manager", timeout: 3600 });
83844
+ */
83845
+ waitForApproval(options?: { timeout?: number; form?: object; selfApproval?: boolean; key?: string; }): PromiseLike<{ value: any; approver: string; approved: boolean }>
83846
+
83847
+ /**
83848
+ * Resume/cancel/approval-page URLs bound to one \`waitForApproval\` step.
83849
+ *
83850
+ * Unlike \`getResumeUrls()\`, which signs a random nonce, these address the very
83851
+ * \`resume_job\` record the step's built-in approval buttons use, so they are
83852
+ * stable across replays and safe to embed in a custom notification.
83853
+ *
83854
+ * \`stepKey\` must match the \`key\` given to \`waitForApproval\`. Keys must be unique
83855
+ * within a workflow; reusing one throws rather than silently renaming it. The URL
83856
+ * only resumes while that step is awaiting approval; used at any other moment it is
83857
+ * rejected rather than banking a row a different approval would consume. Send it
83858
+ * ahead of time — approvers just cannot act before the workflow reaches the step.
83859
+ *
83860
+ * \`resume\` and \`cancel\` are step-bound; \`approvalPage\` is not — it opens the job's
83861
+ * approval page, which acts on whichever approval is pending when it is used.
83715
83862
  *
83716
83863
  * @example
83717
- * const urls = await step("urls", () => getResumeUrls());
83718
- * await step("notify", () => sendEmail(urls.approvalPage));
83719
- * const { value, approver } = await waitForApproval({ timeout: 3600 });
83864
+ * const urls = await step("urls", () => getApprovalUrls("manager"));
83865
+ * await step("notify", () => sendEmail(urls.resume, urls.cancel));
83866
+ * await waitForApproval({ key: "manager" });
83720
83867
  */
83721
- waitForApproval(options?: { timeout?: number; form?: object; selfApproval?: boolean; }): PromiseLike<{ value: any; approver: string; approved: boolean }>
83868
+ async getApprovalUrls(stepKey: string = "approval", approver?: string): Promise<{
83869
+ approvalPage: string;
83870
+ resume: string;
83871
+ cancel: string;
83872
+ }>
83722
83873
 
83723
83874
  /**
83724
83875
  * Process items in parallel with optional concurrency control.
@@ -85304,6 +85455,17 @@ def get_shared_state(path: str = 'state.json') -> None
85304
85455
  # Dictionary with approvalPage, resume, and cancel URLs
85305
85456
  def get_resume_urls(approver: str = None, flow_level: bool = None) -> dict
85306
85457
 
85458
+ # Get the resume URLs bound to one \`\`wait_for_approval\`\` step of this workflow.
85459
+ #
85460
+ # Args:
85461
+ # step_key: Checkpoint key of the approval step, as passed to
85462
+ # \`\`wait_for_approval(key=...)\`\`
85463
+ # approver: Optional approver name
85464
+ #
85465
+ # Returns:
85466
+ # Dictionary with approvalPage, resume, and cancel URLs
85467
+ def get_approval_urls(step_key: str = 'approval', approver: str = None) -> dict
85468
+
85307
85469
  # Sends an interactive approval request via Slack, allowing optional customization of the message, approver, and form fields.
85308
85470
  #
85309
85471
  # **[Enterprise Edition Only]** To include form fields in the Slack approval request, use the "Advanced -> Suspend -> Form" functionality.
@@ -85580,8 +85742,9 @@ async def sleep(seconds: int)
85580
85742
 
85581
85743
  # Suspend the workflow and wait for an external approval.
85582
85744
  #
85583
- # Use \`\`get_resume_urls()\`\` (wrapped in \`\`step()\`\`) to obtain
85584
- # resume/cancel/approval URLs before calling this function.
85745
+ # Pass \`\`key\`\` to name the step, then \`\`get_approval_urls(key)\`\` yields the URLs
85746
+ # that resume exactly this approval route them through your own channel.
85747
+ # Without a key the steps are named \`\`approval\`\`, \`\`approval_2\`\`, ...
85585
85748
  #
85586
85749
  # Returns a dict with \`\`value\`\` (form data), \`\`approver\`\`, and \`\`approved\`\`.
85587
85750
  #
@@ -85589,13 +85752,14 @@ async def sleep(seconds: int)
85589
85752
  # timeout: Approval timeout in seconds (default 1800).
85590
85753
  # form: Optional form schema for the approval page.
85591
85754
  # self_approval: Whether the user who triggered the flow can approve it (default True).
85755
+ # key: Optional checkpoint key naming this approval step.
85592
85756
  #
85593
85757
  # Example::
85594
85758
  #
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
85759
+ # urls = await step("urls", lambda: get_approval_urls("manager"))
85760
+ # await step("notify", lambda: send_email(urls["resume"], urls["cancel"]))
85761
+ # result = await wait_for_approval(key="manager", timeout=3600)
85762
+ async def wait_for_approval(timeout: int = 1800, form: dict | None = None, self_approval: bool = True, key: str | None = None) -> dict
85599
85763
 
85600
85764
  # Process items in parallel with optional concurrency control.
85601
85765
  #
@@ -86672,6 +86836,18 @@ const user = await backend.get_user({ user_id: '123' });
86672
86836
 
86673
86837
  The frontend cannot reach datatables, workspace items, or external services on its own — it goes through \`backend.<key>(args)\` for everything server-side.
86674
86838
 
86839
+ ### Keeping data out of recorded demos
86840
+
86841
+ 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:
86842
+
86843
+ \`\`\`tsx
86844
+ <label data-wm-no-record>
86845
+ Customer SSN <input value={ssn} onChange={onSsn} />
86846
+ </label>
86847
+ \`\`\`
86848
+
86849
+ 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.
86850
+
86675
86851
  ## Backend runnables
86676
86852
 
86677
86853
  Each runnable has a unique key (used to call it from the frontend) and one of four types:
@@ -86774,6 +86950,7 @@ def main(user_id: str):
86774
86950
  3. **Keep runnables focused** — one function per runnable; small surface area.
86775
86951
  4. **Use descriptive keys** — \`get_user\`, not \`a\`.
86776
86952
  5. **Always whitelist tables** — adding a runnable that queries a new table requires the table to be in \`data.tables\` first.
86953
+ 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
86954
  `,
86778
86955
  triggers: `---
86779
86956
  name: triggers
@@ -87227,7 +87404,7 @@ import {
87227
87404
  step,
87228
87405
  sleep,
87229
87406
  waitForApproval,
87230
- getResumeUrls,
87407
+ getApprovalUrls,
87231
87408
  parallel,
87232
87409
  workflow,
87233
87410
  } from "windmill-client";
@@ -87245,7 +87422,7 @@ export const main = workflow(async (x: string) => {
87245
87422
  Python:
87246
87423
 
87247
87424
  \`\`\`python
87248
- from wmill import task, task_script, task_flow, step, sleep, wait_for_approval, get_resume_urls, parallel, workflow
87425
+ from wmill import task, task_script, task_flow, step, sleep, wait_for_approval, get_approval_urls, parallel, workflow
87249
87426
 
87250
87427
  @task()
87251
87428
  async def process(x: str) -> str:
@@ -87329,12 +87506,13 @@ output = await pipeline(input=data)
87329
87506
  Use \`step()\` for lightweight inline values that must not change during replay:
87330
87507
 
87331
87508
  \`\`\`typescript
87332
- const urls = await step("get_urls", () => getResumeUrls());
87333
87509
  const startedAt = await step("started_at", () => new Date().toISOString());
87334
87510
  \`\`\`
87335
87511
 
87336
87512
  \`\`\`python
87337
- urls = await step("get_urls", lambda: get_resume_urls())
87513
+ from datetime import datetime
87514
+
87515
+ started_at = await step("started_at", lambda: datetime.now().isoformat())
87338
87516
  \`\`\`
87339
87517
 
87340
87518
  Use stable, descriptive step names. Do not generate step names dynamically.
@@ -87359,20 +87537,28 @@ Only parallelize independent steps. Do not read the result of a task before it i
87359
87537
 
87360
87538
  ## Approvals
87361
87539
 
87362
- Generate resume URLs inside \`step()\` before sending them:
87540
+ Name the approval step and generate its URLs inside \`step()\` before sending them.
87541
+ \`getApprovalUrls\` / \`get_approval_urls\` returns the URLs bound to that step, the same
87542
+ ones its built-in approve/reject buttons use:
87363
87543
 
87364
87544
  \`\`\`typescript
87365
- const urls = await step("get_urls", () => getResumeUrls());
87366
- await step("notify", () => sendApprovalEmail(urls.approvalPage));
87367
- const approval = await waitForApproval({ timeout: 3600 });
87545
+ const urls = await step("urls", () => getApprovalUrls("manager"));
87546
+ await step("notify", () => sendApprovalEmail(urls.resume, urls.cancel));
87547
+ const approval = await waitForApproval({ key: "manager", timeout: 3600 });
87368
87548
  \`\`\`
87369
87549
 
87370
87550
  \`\`\`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)
87551
+ urls = await step("urls", lambda: get_approval_urls("manager"))
87552
+ await step("notify", lambda: send_approval_email(urls["resume"], urls["cancel"]))
87553
+ approval = await wait_for_approval(key="manager", timeout=3600)
87374
87554
  \`\`\`
87375
87555
 
87556
+ With several approvals in one workflow, give each its own key so each notification
87557
+ resumes its own step. Keys must be unique — reusing one raises an error rather than
87558
+ silently renaming the step. A minted URL only resumes while its own step is awaiting
87559
+ approval; used at any other moment it is rejected rather than resuming the wrong one. \`getResumeUrls()\` / \`get_resume_urls()\` still works but signs a
87560
+ random nonce, so its URLs are not tied to any particular approval step.
87561
+
87376
87562
  \`selfApproval: false\` and \`self_approval=False\` are Enterprise-only approval behavior. Do not use them unless the user asks for that behavior.
87377
87563
 
87378
87564
  ## Error Handling
@@ -87386,7 +87572,7 @@ TypeScript: avoid broad \`try/catch\` around WAC SDK calls. The SDK uses an inte
87386
87572
 
87387
87573
  ## TypeScript Workflow-as-Code API (windmill-client)
87388
87574
 
87389
- Import: \`import { workflow, task, taskScript, taskFlow, step, sleep, waitForApproval, getResumeUrls, parallel } from "windmill-client"\`
87575
+ Import: \`import { workflow, task, taskScript, taskFlow, step, sleep, waitForApproval, getApprovalUrls, getResumeUrls, parallel } from "windmill-client"\`
87390
87576
 
87391
87577
  \`\`\`typescript
87392
87578
  export interface TaskOptions {
@@ -87456,15 +87642,39 @@ export async function sleep(seconds: number): Promise<void>
87456
87642
  /**
87457
87643
  * Suspend the workflow and wait for an external approval.
87458
87644
  *
87459
- * Use \`getResumeUrls()\` (wrapped in \`step()\`) to obtain resume/cancel/approvalPage
87460
- * URLs before calling this function.
87645
+ * Pass \`key\` to name the step, then \`getApprovalUrls(key)\` yields the URLs that
87646
+ * resume exactly this approval — route them through your own channel. Without a
87647
+ * key the steps are named \`approval\`, \`approval_2\`, ...
87648
+ *
87649
+ * @example
87650
+ * const urls = await step("urls", () => getApprovalUrls("manager"));
87651
+ * await step("notify", () => sendEmail(urls.resume, urls.cancel));
87652
+ * const { value, approver } = await waitForApproval({ key: "manager", timeout: 3600 });
87653
+ */
87654
+ export function waitForApproval(options?: { timeout?: number; form?: object; selfApproval?: boolean; key?: string; }): PromiseLike<{ value: any; approver: string; approved: boolean }>
87655
+
87656
+ /**
87657
+ * Resume/cancel/approval-page URLs bound to one \`waitForApproval\` step.
87658
+ *
87659
+ * Unlike \`getResumeUrls()\`, which signs a random nonce, these address the very
87660
+ * \`resume_job\` record the step's built-in approval buttons use, so they are
87661
+ * stable across replays and safe to embed in a custom notification.
87662
+ *
87663
+ * \`stepKey\` must match the \`key\` given to \`waitForApproval\`. Keys must be unique
87664
+ * within a workflow; reusing one throws rather than silently renaming it. The URL
87665
+ * only resumes while that step is awaiting approval; used at any other moment it is
87666
+ * rejected rather than banking a row a different approval would consume. Send it
87667
+ * ahead of time — approvers just cannot act before the workflow reaches the step.
87668
+ *
87669
+ * \`resume\` and \`cancel\` are step-bound; \`approvalPage\` is not — it opens the job's
87670
+ * approval page, which acts on whichever approval is pending when it is used.
87461
87671
  *
87462
87672
  * @example
87463
- * const urls = await step("urls", () => getResumeUrls());
87464
- * await step("notify", () => sendEmail(urls.approvalPage));
87465
- * const { value, approver } = await waitForApproval({ timeout: 3600 });
87673
+ * const urls = await step("urls", () => getApprovalUrls("manager"));
87674
+ * await step("notify", () => sendEmail(urls.resume, urls.cancel));
87675
+ * await waitForApproval({ key: "manager" });
87466
87676
  */
87467
- export function waitForApproval(options?: { timeout?: number; form?: object; selfApproval?: boolean; }): PromiseLike<{ value: any; approver: string; approved: boolean }>
87677
+ export async function getApprovalUrls(stepKey: string = "approval", approver?: string): Promise<{ approvalPage: string; resume: string; cancel: string; }>
87468
87678
 
87469
87679
  /**
87470
87680
  * Process items in parallel with optional concurrency control.
@@ -87482,7 +87692,7 @@ export async function parallel<T, R>(items: T[], fn: (item: T) => PromiseLike<R>
87482
87692
 
87483
87693
  ## Python Workflow-as-Code API (wmill)
87484
87694
 
87485
- Import: \`from wmill import workflow, task, task_script, task_flow, step, sleep, wait_for_approval, get_resume_urls, parallel, TaskError\`
87695
+ Import: \`from wmill import workflow, task, task_script, task_flow, step, sleep, wait_for_approval, get_approval_urls, get_resume_urls, parallel, TaskError\`
87486
87696
 
87487
87697
  \`\`\`python
87488
87698
  # Raised when a WAC task step failed.
@@ -87570,8 +87780,9 @@ async def sleep(seconds: int)
87570
87780
 
87571
87781
  # Suspend the workflow and wait for an external approval.
87572
87782
  #
87573
- # Use \`\`get_resume_urls()\`\` (wrapped in \`\`step()\`\`) to obtain
87574
- # resume/cancel/approval URLs before calling this function.
87783
+ # Pass \`\`key\`\` to name the step, then \`\`get_approval_urls(key)\`\` yields the URLs
87784
+ # that resume exactly this approval route them through your own channel.
87785
+ # Without a key the steps are named \`\`approval\`\`, \`\`approval_2\`\`, ...
87575
87786
  #
87576
87787
  # Returns a dict with \`\`value\`\` (form data), \`\`approver\`\`, and \`\`approved\`\`.
87577
87788
  #
@@ -87579,13 +87790,37 @@ async def sleep(seconds: int)
87579
87790
  # timeout: Approval timeout in seconds (default 1800).
87580
87791
  # form: Optional form schema for the approval page.
87581
87792
  # self_approval: Whether the user who triggered the flow can approve it (default True).
87793
+ # key: Optional checkpoint key naming this approval step.
87582
87794
  #
87583
87795
  # Example::
87584
87796
  #
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
87797
+ # urls = await step("urls", lambda: get_approval_urls("manager"))
87798
+ # await step("notify", lambda: send_email(urls["resume"], urls["cancel"]))
87799
+ # result = await wait_for_approval(key="manager", timeout=3600)
87800
+ async def wait_for_approval(timeout: int = 1800, form: dict | None = None, self_approval: bool = True, key: str | None = None) -> dict
87801
+
87802
+ # Get the resume/cancel/approval-page URLs bound to one \`\`wait_for_approval\`\` step.
87803
+ #
87804
+ # Unlike :func:\`get_resume_urls\`, which signs a random nonce, these address the
87805
+ # very \`\`resume_job\`\` record the step's built-in approval buttons use, so they
87806
+ # are stable across replays and safe to embed in a custom notification.
87807
+ #
87808
+ # Args:
87809
+ # step_key: Checkpoint key of the approval step, as passed to
87810
+ # \`\`wait_for_approval(key=...)\`\`. Keys must be unique within a workflow;
87811
+ # reusing one raises rather than silently renaming it. The URL only
87812
+ # resumes while that step is awaiting approval; used at any other moment
87813
+ # it is rejected rather than banking a row a different approval would
87814
+ # consume. Send it ahead of time — approvers just cannot act before the
87815
+ # workflow reaches the step.
87816
+ # \`\`resume\`\` and \`\`cancel\`\` are step-bound; \`\`approvalPage\`\` is not — it
87817
+ # opens the job's approval page, which acts on whichever approval is
87818
+ # pending when it is used.
87819
+ # approver: Optional approver name
87820
+ #
87821
+ # Returns:
87822
+ # Dictionary with approvalPage, resume, and cancel URLs
87823
+ def get_approval_urls(step_key: str = 'approval', approver: str = None) -> dict
87589
87824
 
87590
87825
  # Process items in parallel with optional concurrency control.
87591
87826
  #
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "windmill-cli",
3
- "version": "1.770.0",
3
+ "version": "1.771.1",
4
4
  "description": "CLI for Windmill",
5
5
  "license": "Apache 2.0",
6
6
  "type": "module",