@tailor-platform/sdk 1.67.0 → 1.68.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.
@@ -39,6 +39,7 @@ import * as inflection from "inflection";
39
39
  import { pathToString } from "@bufbuild/protobuf/reflect";
40
40
  import { createValidator } from "@bufbuild/protovalidate";
41
41
  import { setTimeout as setTimeout$1 } from "timers/promises";
42
+ import { setTimeout as setTimeout$2 } from "node:timers/promises";
42
43
  import { spawn } from "node:child_process";
43
44
  import { watch } from "chokidar";
44
45
  import * as madgeModule from "madge";
@@ -4891,7 +4892,7 @@ async function readConfigId(configPath) {
4891
4892
  async function assertConfigIdInCI(configPath) {
4892
4893
  const result = await readConfigId(configPath);
4893
4894
  if (result === null) return;
4894
- if (!result.id) throw new Error("tailor.config.ts is missing an 'id'. CI does not auto-generate one (each run would be treated as a separate app and break resource ownership). Run 'tailor-sdk setup github' or 'tailor-sdk apply' locally and commit the injected id.");
4895
+ if (!result.id) throw new Error("tailor.config.ts is missing an 'id'. CI does not auto-generate one (each run would be treated as a separate app and break resource ownership). Run 'tailor-sdk setup' or 'tailor-sdk apply' locally and commit the injected id.");
4895
4896
  if (!uuidRegex.test(result.id)) throw new Error(`'id' in ${configPath} must be a UUID. To use this config for a separate app, delete it.`);
4896
4897
  }
4897
4898
  /**
@@ -8700,7 +8701,7 @@ const DEFAULT_POLL_INTERVAL = 1e3;
8700
8701
  * @returns {Promise<ExecutionWaitResult>} Execution result
8701
8702
  * @throws {Error} If execution is not found
8702
8703
  */
8703
- async function waitForExecution$1(client, workspaceId, executionId, pollInterval = DEFAULT_POLL_INTERVAL) {
8704
+ async function waitForExecution(client, workspaceId, executionId, pollInterval = DEFAULT_POLL_INTERVAL) {
8704
8705
  while (true) {
8705
8706
  const { execution } = await client.getFunctionExecution({
8706
8707
  workspaceId,
@@ -8735,7 +8736,7 @@ async function executeScript(options) {
8735
8736
  invoker
8736
8737
  });
8737
8738
  const executionId = response.executionId;
8738
- const result = await waitForExecution$1(client, workspaceId, executionId, pollInterval);
8739
+ const result = await waitForExecution(client, workspaceId, executionId, pollInterval);
8739
8740
  if (result.status === FunctionExecution_Status.SUCCESS) return {
8740
8741
  success: true,
8741
8742
  logs: result.logs,
@@ -11178,7 +11179,42 @@ function colorizeExecutorJobStatus(status) {
11178
11179
  * @returns True if status is terminal
11179
11180
  */
11180
11181
  function isExecutorJobTerminalStatus(status) {
11181
- return status === ExecutorJobStatus.SUCCESS || status === ExecutorJobStatus.FAILED || status === ExecutorJobStatus.CANCELED;
11182
+ return isExecutorJobSuccessStatus(status) || isExecutorJobFailureStatus(status);
11183
+ }
11184
+ /**
11185
+ * Check if executor job status is successful.
11186
+ * @param status - Executor job status enum value
11187
+ * @returns True if status is success
11188
+ */
11189
+ function isExecutorJobSuccessStatus(status) {
11190
+ return status === ExecutorJobStatus.SUCCESS;
11191
+ }
11192
+ /**
11193
+ * Check if executor job status is a terminal failure.
11194
+ * @param status - Executor job status enum value
11195
+ * @returns True if status is failure
11196
+ */
11197
+ function isExecutorJobFailureStatus(status) {
11198
+ return status === ExecutorJobStatus.FAILED || status === ExecutorJobStatus.CANCELED;
11199
+ }
11200
+ /**
11201
+ * Check if executor job status can still progress.
11202
+ * @param status - Executor job status enum value
11203
+ * @returns True if status is transient
11204
+ */
11205
+ function isExecutorJobTransientStatus(status) {
11206
+ return status === ExecutorJobStatus.UNSPECIFIED || status === ExecutorJobStatus.PENDING || status === ExecutorJobStatus.RUNNING;
11207
+ }
11208
+ /**
11209
+ * Classify executor job status for waiter decisions.
11210
+ * @param status - Executor job status enum value
11211
+ * @returns Classified executor job status
11212
+ */
11213
+ function classifyExecutorJobStatus(status) {
11214
+ if (isExecutorJobSuccessStatus(status)) return "success";
11215
+ if (isExecutorJobTerminalStatus(status)) return "failure";
11216
+ if (isExecutorJobTransientStatus(status)) return "transient";
11217
+ return "transient";
11182
11218
  }
11183
11219
  /**
11184
11220
  * Parse executor job status string to enum.
@@ -11570,36 +11606,158 @@ function functionExecutionStatusToString(status) {
11570
11606
  }
11571
11607
  }
11572
11608
 
11609
+ //#endregion
11610
+ //#region src/cli/shared/wait-error.ts
11611
+ /**
11612
+ * Format a caught error into a string for diagnostics.
11613
+ * @param error - Error to format
11614
+ * @returns Error message string
11615
+ */
11616
+ function formatWaitError(error) {
11617
+ return error instanceof Error ? error.message : String(error);
11618
+ }
11619
+ /**
11620
+ * Check whether a polling error is retryable (transient platform errors).
11621
+ * @param error - Error to check
11622
+ * @returns True if the error should be retried
11623
+ */
11624
+ function isRetryableWaitError(error) {
11625
+ if (!(error instanceof ConnectError)) return false;
11626
+ return error.code === Code.Aborted || error.code === Code.ResourceExhausted || error.code === Code.Unavailable;
11627
+ }
11628
+
11573
11629
  //#endregion
11574
11630
  //#region src/cli/commands/workflow/args.ts
11631
+ const workflowWaitUntilArg = z.enum([
11632
+ "success",
11633
+ "suspended",
11634
+ "terminal"
11635
+ ]);
11575
11636
  const nameArgs = { name: arg(z.string(), {
11576
11637
  positional: true,
11577
11638
  description: "Workflow name"
11578
11639
  }) };
11579
- const waitArgs = {
11580
- wait: arg(z.boolean().default(false), {
11581
- alias: "W",
11582
- description: "Wait for execution to complete"
11583
- }),
11640
+ const workflowWaitControlArgs = {
11584
11641
  interval: arg(durationArg.default("3s"), {
11585
11642
  alias: "i",
11586
- description: "Polling interval when using --wait (e.g., '3s', '500ms', '1m')"
11643
+ description: "Polling interval when waiting (e.g., '3s', '500ms', '1m')"
11644
+ }),
11645
+ timeout: arg(durationArg.default("10m"), {
11646
+ alias: "t",
11647
+ description: "Maximum time to wait (e.g., '30s', '10m')"
11648
+ }),
11649
+ until: arg(workflowWaitUntilArg.default("terminal"), {
11650
+ alias: "u",
11651
+ description: "Wait target (success, suspended, terminal)"
11587
11652
  }),
11588
11653
  logs: arg(z.boolean().default(false), {
11589
11654
  alias: "l",
11590
- description: "Display job execution logs after completion (requires --wait)"
11655
+ description: "Display job execution logs after completion"
11591
11656
  })
11592
11657
  };
11658
+ const waitArgs = {
11659
+ wait: arg(z.boolean().default(false), {
11660
+ alias: "W",
11661
+ description: "Wait for execution to complete"
11662
+ }),
11663
+ ...workflowWaitControlArgs
11664
+ };
11593
11665
 
11594
11666
  //#endregion
11595
11667
  //#region src/cli/commands/workflow/status.ts
11596
11668
  /**
11669
+ * Check if workflow execution status is successful.
11670
+ * @param status - Workflow execution status enum value
11671
+ * @returns True if status is success
11672
+ */
11673
+ function isWorkflowExecutionSuccessStatus(status) {
11674
+ return status === WorkflowExecution_Status.SUCCESS;
11675
+ }
11676
+ /**
11677
+ * Check if workflow job execution status is suspended or waiting.
11678
+ * @param status - Workflow job execution status enum value
11679
+ * @returns True if status represents a wait point
11680
+ */
11681
+ function isWorkflowJobExecutionSuspendedStatus(status) {
11682
+ return status === WorkflowJobExecution_Status.SUSPEND || status === WorkflowJobExecution_Status.WAITING;
11683
+ }
11684
+ /**
11685
+ * Check if workflow execution status is suspended or waiting.
11686
+ * @param status - Workflow execution status enum value
11687
+ * @returns True if status represents a suspended execution
11688
+ */
11689
+ function isWorkflowExecutionSuspendedStatus(status) {
11690
+ return status === WorkflowExecution_Status.PENDING_RESUME || status === WorkflowExecution_Status.WAITING;
11691
+ }
11692
+ /**
11693
+ * Check if workflow execution status is a terminal failure.
11694
+ * @param status - Workflow execution status enum value
11695
+ * @returns True if status represents failure
11696
+ */
11697
+ function isWorkflowExecutionFailureStatus(status) {
11698
+ return status === WorkflowExecution_Status.FAILED;
11699
+ }
11700
+ /**
11701
+ * Check if workflow execution status can still progress without user action.
11702
+ * @param status - Workflow execution status enum value
11703
+ * @returns True if status is transient
11704
+ */
11705
+ function isWorkflowExecutionTransientStatus(status) {
11706
+ return status === WorkflowExecution_Status.UNSPECIFIED || status === WorkflowExecution_Status.PENDING || status === WorkflowExecution_Status.RUNNING || status === WorkflowExecution_Status.PENDING_RETRY;
11707
+ }
11708
+ /**
11597
11709
  * Check if workflow execution status is terminal.
11598
11710
  * @param status - Workflow execution status enum value
11599
11711
  * @returns True if status is terminal
11600
11712
  */
11601
11713
  function isWorkflowExecutionTerminalStatus(status) {
11602
- return status === WorkflowExecution_Status.SUCCESS || status === WorkflowExecution_Status.FAILED || status === WorkflowExecution_Status.PENDING_RESUME;
11714
+ return isWorkflowExecutionSuccessStatus(status) || isWorkflowExecutionFailureStatus(status) || isWorkflowExecutionSuspendedStatus(status);
11715
+ }
11716
+ /**
11717
+ * Classify workflow execution status for waiter decisions.
11718
+ * @param execution - Workflow execution to classify
11719
+ * @returns Classified workflow execution status
11720
+ */
11721
+ function classifyWorkflowExecutionStatus(execution) {
11722
+ if (isWorkflowExecutionTerminalStatus(execution.status)) {
11723
+ if (isWorkflowExecutionSuccessStatus(execution.status)) return {
11724
+ statusClass: "success",
11725
+ status: execution.status
11726
+ };
11727
+ if (isWorkflowExecutionFailureStatus(execution.status)) return {
11728
+ statusClass: "failure",
11729
+ status: execution.status
11730
+ };
11731
+ return {
11732
+ statusClass: "suspended",
11733
+ status: execution.status
11734
+ };
11735
+ }
11736
+ if (execution.jobExecutions.some((job) => isWorkflowJobExecutionSuspendedStatus(job.status))) return {
11737
+ statusClass: "suspended",
11738
+ status: execution.status
11739
+ };
11740
+ if (isWorkflowExecutionTransientStatus(execution.status)) return {
11741
+ statusClass: "transient",
11742
+ status: execution.status
11743
+ };
11744
+ return {
11745
+ statusClass: "transient",
11746
+ status: execution.status
11747
+ };
11748
+ }
11749
+ /**
11750
+ * Check if a classified workflow execution has reached the requested waiter target.
11751
+ * @param classification - Workflow execution status classification
11752
+ * @param until - Requested wait target
11753
+ * @returns True if the wait target is reached
11754
+ */
11755
+ function hasReachedWorkflowWaitTarget(classification, until) {
11756
+ switch (until) {
11757
+ case "success": return classification.statusClass === "success";
11758
+ case "suspended": return classification.statusClass === "suspended";
11759
+ case "terminal": return classification.statusClass === "success" || classification.statusClass === "failure" || classification.statusClass === "suspended";
11760
+ }
11603
11761
  }
11604
11762
 
11605
11763
  //#endregion
@@ -11616,6 +11774,8 @@ function workflowExecutionStatusToString(status) {
11616
11774
  case WorkflowExecution_Status.RUNNING: return "RUNNING";
11617
11775
  case WorkflowExecution_Status.SUCCESS: return "SUCCESS";
11618
11776
  case WorkflowExecution_Status.FAILED: return "FAILED";
11777
+ case WorkflowExecution_Status.PENDING_RETRY: return "PENDING_RETRY";
11778
+ case WorkflowExecution_Status.WAITING: return "WAITING";
11619
11779
  default: return "UNSPECIFIED";
11620
11780
  }
11621
11781
  }
@@ -11630,6 +11790,7 @@ function workflowJobExecutionStatusToString(status) {
11630
11790
  case WorkflowJobExecution_Status.SUSPEND: return "SUSPEND";
11631
11791
  case WorkflowJobExecution_Status.SUCCESS: return "SUCCESS";
11632
11792
  case WorkflowJobExecution_Status.FAILED: return "FAILED";
11793
+ case WorkflowJobExecution_Status.WAITING: return "WAITING";
11633
11794
  default: return "UNSPECIFIED";
11634
11795
  }
11635
11796
  }
@@ -11695,24 +11856,200 @@ function toWorkflowExecutionInfo(execution) {
11695
11856
  }
11696
11857
 
11697
11858
  //#endregion
11698
- //#region src/cli/commands/workflow/executions.ts
11699
- function sleep$1(ms) {
11700
- return new Promise((resolve) => setTimeout(resolve, ms));
11701
- }
11702
- function formatTime$2(date) {
11859
+ //#region src/cli/commands/workflow/waiter.ts
11860
+ const DEFAULT_WORKFLOW_WAIT_INTERVAL_MS = 3e3;
11861
+ function formatTime$1(date) {
11703
11862
  return date.toLocaleTimeString("en-US", { hour12: false });
11704
11863
  }
11705
- function colorizeStatus$1(status) {
11864
+ function colorizeStatus(status) {
11706
11865
  const statusText = WorkflowExecution_Status[status];
11707
11866
  switch (status) {
11708
- case WorkflowExecution_Status.PENDING: return styles.dim(statusText);
11709
- case WorkflowExecution_Status.PENDING_RESUME: return styles.warning(statusText);
11867
+ case WorkflowExecution_Status.PENDING:
11868
+ case WorkflowExecution_Status.UNSPECIFIED: return styles.dim(statusText);
11869
+ case WorkflowExecution_Status.PENDING_RESUME:
11870
+ case WorkflowExecution_Status.PENDING_RETRY:
11871
+ case WorkflowExecution_Status.WAITING: return styles.warning(statusText);
11710
11872
  case WorkflowExecution_Status.RUNNING: return styles.info(statusText);
11711
11873
  case WorkflowExecution_Status.SUCCESS: return styles.success(statusText);
11712
11874
  case WorkflowExecution_Status.FAILED: return styles.error(statusText);
11713
11875
  default: return statusText;
11714
11876
  }
11715
11877
  }
11878
+ function getActiveJobs(execution) {
11879
+ return execution.jobExecutions.filter((job) => job.status === WorkflowJobExecution_Status.RUNNING || job.status === WorkflowJobExecution_Status.SUSPEND || job.status === WorkflowJobExecution_Status.WAITING).map((job) => job.stackedJobName).join(", ");
11880
+ }
11881
+ function createWorkflowWaitResult(options) {
11882
+ const elapsedMs = Date.now() - options.startedAt;
11883
+ if (options.execution) {
11884
+ const classification = classifyWorkflowExecutionStatus(options.execution);
11885
+ return {
11886
+ ...toWorkflowExecutionInfo(options.execution),
11887
+ statusClass: classification.statusClass,
11888
+ elapsedMs,
11889
+ attempts: options.attempts,
11890
+ timedOut: options.timedOut,
11891
+ lastError: options.lastError
11892
+ };
11893
+ }
11894
+ return {
11895
+ id: options.executionId,
11896
+ workflowName: "",
11897
+ status: "UNKNOWN",
11898
+ statusClass: "unknown",
11899
+ jobExecutions: 0,
11900
+ startedAt: null,
11901
+ finishedAt: null,
11902
+ elapsedMs,
11903
+ attempts: options.attempts,
11904
+ timedOut: options.timedOut,
11905
+ lastError: options.lastError
11906
+ };
11907
+ }
11908
+ /**
11909
+ * Wait for a workflow execution to reach the requested state.
11910
+ * @param options - Workflow waiter options
11911
+ * @returns Final or timed-out workflow wait result
11912
+ */
11913
+ async function waitForWorkflowExecution(options) {
11914
+ const interval = options.interval ?? 3e3;
11915
+ const until = options.until ?? "terminal";
11916
+ const startedAt = Date.now();
11917
+ const sp = options.showProgress ? spinner({ indent: 2 }).start("Waiting for workflow to complete...") : null;
11918
+ let attempts = 0;
11919
+ let lastExecution;
11920
+ let lastError = null;
11921
+ let lastStatus;
11922
+ let lastActiveJobs;
11923
+ try {
11924
+ while (true) {
11925
+ const elapsedMs = Date.now() - startedAt;
11926
+ const remainingMs = options.timeout === void 0 ? void 0 : options.timeout - elapsedMs;
11927
+ if (remainingMs !== void 0 && remainingMs <= 0) {
11928
+ sp?.fail("Workflow wait timed out.");
11929
+ return createWorkflowWaitResult({
11930
+ executionId: options.executionId,
11931
+ execution: lastExecution,
11932
+ startedAt,
11933
+ attempts,
11934
+ timedOut: true,
11935
+ lastError
11936
+ });
11937
+ }
11938
+ try {
11939
+ attempts += 1;
11940
+ const { execution } = await options.client.getWorkflowExecution({
11941
+ workspaceId: options.workspaceId,
11942
+ executionId: options.executionId
11943
+ });
11944
+ if (!execution) {
11945
+ sp?.fail(`Execution '${options.executionId}' not found.`);
11946
+ throw new Error(`Execution '${options.executionId}' not found.`);
11947
+ }
11948
+ lastExecution = execution;
11949
+ lastError = null;
11950
+ const classification = classifyWorkflowExecutionStatus(execution);
11951
+ const coloredStatus = colorizeStatus(execution.status);
11952
+ if (execution.status !== lastStatus) {
11953
+ if (options.showProgress) {
11954
+ sp?.stop();
11955
+ logger.info(`Status: ${coloredStatus}`, {
11956
+ mode: "stream",
11957
+ indent: 2
11958
+ });
11959
+ sp?.start("Waiting for workflow to complete...");
11960
+ }
11961
+ lastStatus = execution.status;
11962
+ }
11963
+ if (options.trackJobs) {
11964
+ const activeJobs = getActiveJobs(execution);
11965
+ if (activeJobs && activeJobs !== lastActiveJobs) {
11966
+ if (options.showProgress) {
11967
+ sp?.stop();
11968
+ logger.info(`Job | ${activeJobs}: ${coloredStatus}`, {
11969
+ mode: "stream",
11970
+ indent: 2
11971
+ });
11972
+ sp?.start("Waiting for workflow to complete...");
11973
+ }
11974
+ lastActiveJobs = activeJobs;
11975
+ }
11976
+ }
11977
+ if (sp) sp.text = `Waiting for workflow to complete... (${formatTime$1(/* @__PURE__ */ new Date())})`;
11978
+ if (hasReachedWorkflowWaitTarget(classification, until) || classification.statusClass === "failure" || until === "suspended" && classification.statusClass === "success") {
11979
+ if (execution.status === WorkflowExecution_Status.SUCCESS) sp?.succeed(`Completed: ${coloredStatus}`);
11980
+ else if (isWorkflowExecutionFailureStatus(execution.status)) sp?.fail(`Completed: ${coloredStatus}`);
11981
+ else if (isWorkflowExecutionSuspendedStatus(execution.status)) sp?.warn(`Completed: ${coloredStatus}`);
11982
+ else sp?.succeed(`Completed: ${coloredStatus}`);
11983
+ return createWorkflowWaitResult({
11984
+ executionId: options.executionId,
11985
+ execution,
11986
+ startedAt,
11987
+ attempts,
11988
+ timedOut: false,
11989
+ lastError
11990
+ });
11991
+ }
11992
+ } catch (error) {
11993
+ if (!isRetryableWaitError(error)) throw error;
11994
+ lastError = formatWaitError(error);
11995
+ if (options.showProgress) {
11996
+ if (sp) sp.text = `Retrying workflow status poll... (${formatTime$1(/* @__PURE__ */ new Date())})`;
11997
+ }
11998
+ }
11999
+ const nextElapsedMs = Date.now() - startedAt;
12000
+ const nextRemainingMs = options.timeout === void 0 ? void 0 : options.timeout - nextElapsedMs;
12001
+ if (nextRemainingMs !== void 0 && nextRemainingMs <= 0) {
12002
+ sp?.fail("Workflow wait timed out.");
12003
+ return createWorkflowWaitResult({
12004
+ executionId: options.executionId,
12005
+ execution: lastExecution,
12006
+ startedAt,
12007
+ attempts,
12008
+ timedOut: true,
12009
+ lastError
12010
+ });
12011
+ }
12012
+ await setTimeout$2(nextRemainingMs === void 0 ? interval : Math.min(interval, nextRemainingMs));
12013
+ }
12014
+ } finally {
12015
+ sp?.stop();
12016
+ }
12017
+ }
12018
+ /**
12019
+ * Wait for an existing workflow execution by ID.
12020
+ * @param options - Workflow execution wait options
12021
+ * @returns Workflow wait result
12022
+ */
12023
+ async function waitForWorkflowExecutionById(options) {
12024
+ return await waitForWorkflowExecution({
12025
+ client: await initOperatorClient(await loadAccessToken({ profile: options.profile })),
12026
+ workspaceId: await loadWorkspaceId({
12027
+ workspaceId: options.workspaceId,
12028
+ profile: options.profile
12029
+ }),
12030
+ executionId: options.executionId,
12031
+ interval: options.interval,
12032
+ timeout: options.timeout,
12033
+ until: options.until,
12034
+ showProgress: options.showProgress,
12035
+ trackJobs: options.trackJobs
12036
+ });
12037
+ }
12038
+ /**
12039
+ * Build a user-facing failure message for a workflow wait result.
12040
+ * @param result - Workflow wait result
12041
+ * @param until - Requested wait target
12042
+ * @returns Failure message, or undefined when the wait succeeded
12043
+ */
12044
+ function getWorkflowWaitFailureMessage(result, until) {
12045
+ if (result.timedOut) return `Timed out waiting for workflow execution '${result.id}' to reach ${until}. Last status: ${result.status}.`;
12046
+ if (result.status === "FAILED") return `Workflow execution '${result.id}' failed.`;
12047
+ if (until === "success" && result.statusClass !== "success") return `Workflow execution '${result.id}' reached ${result.status} before success.`;
12048
+ if (until === "suspended" && result.statusClass !== "suspended") return `Workflow execution '${result.id}' reached ${result.status} before suspension.`;
12049
+ }
12050
+
12051
+ //#endregion
12052
+ //#region src/cli/commands/workflow/executions.ts
11716
12053
  function parseStatus(status) {
11717
12054
  switch (status.toUpperCase()) {
11718
12055
  case "PENDING": return WorkflowExecution_Status.PENDING;
@@ -11720,7 +12057,10 @@ function parseStatus(status) {
11720
12057
  case "RUNNING": return WorkflowExecution_Status.RUNNING;
11721
12058
  case "SUCCESS": return WorkflowExecution_Status.SUCCESS;
11722
12059
  case "FAILED": return WorkflowExecution_Status.FAILED;
11723
- default: throw new Error(`Invalid status: ${status}. Valid values: PENDING, PENDING_RESUME, RUNNING, SUCCESS, FAILED`);
12060
+ case "PENDING_RETRY": return WorkflowExecution_Status.PENDING_RETRY;
12061
+ case "WAITING": return WorkflowExecution_Status.WAITING;
12062
+ case "UNSPECIFIED": return WorkflowExecution_Status.UNSPECIFIED;
12063
+ default: throw new Error(`Invalid status: ${status}. Valid values: UNSPECIFIED, PENDING, PENDING_RESUME, RUNNING, SUCCESS, FAILED, PENDING_RETRY, WAITING`);
11724
12064
  }
11725
12065
  }
11726
12066
  async function listWorkflowExecutions(options) {
@@ -11809,37 +12149,28 @@ async function getWorkflowExecution(options) {
11809
12149
  }
11810
12150
  async function waitForCompletion() {
11811
12151
  const interval = options.interval ?? 3e3;
11812
- while (true) {
11813
- const { execution } = await client.getWorkflowExecution({
11814
- workspaceId,
11815
- executionId: options.executionId
11816
- });
11817
- if (!execution) throw new Error(`Execution '${options.executionId}' not found.`);
11818
- if (isWorkflowExecutionTerminalStatus(execution.status)) return await fetchExecutionWithLogs(options.executionId, options.logs ?? false);
11819
- await sleep$1(interval);
11820
- }
12152
+ const waitResult = await waitForWorkflowExecution({
12153
+ client,
12154
+ workspaceId,
12155
+ executionId: options.executionId,
12156
+ interval,
12157
+ timeout: options.timeout,
12158
+ until: options.until ?? "terminal"
12159
+ });
12160
+ return {
12161
+ ...await fetchExecutionWithLogs(options.executionId, options.logs ?? false),
12162
+ statusClass: waitResult.statusClass,
12163
+ elapsedMs: waitResult.elapsedMs,
12164
+ attempts: waitResult.attempts,
12165
+ timedOut: waitResult.timedOut,
12166
+ lastError: waitResult.lastError
12167
+ };
11821
12168
  }
11822
12169
  return {
11823
12170
  execution: await fetchExecutionWithLogs(options.executionId, options.logs ?? false),
11824
12171
  wait: waitForCompletion
11825
12172
  };
11826
12173
  }
11827
- async function waitWithSpinner(waitFn, interval, json) {
11828
- const sp = !json ? spinner().start("Waiting for workflow to complete...") : null;
11829
- const updateInterval = setInterval(() => {
11830
- if (sp) sp.text = `Waiting for workflow to complete... (${formatTime$2(/* @__PURE__ */ new Date())})`;
11831
- }, interval);
11832
- try {
11833
- const result = await waitFn();
11834
- const coloredStatus = colorizeStatus$1(WorkflowExecution_Status[result.status]);
11835
- if (result.status === "SUCCESS") sp?.succeed(`Completed: ${coloredStatus}`);
11836
- else sp?.fail(`Completed: ${coloredStatus}`);
11837
- return result;
11838
- } finally {
11839
- clearInterval(updateInterval);
11840
- sp?.stop();
11841
- }
11842
- }
11843
12174
  /**
11844
12175
  * Print a workflow execution and its logs in a human-readable format.
11845
12176
  * @param execution - Workflow execution detail info
@@ -11901,19 +12232,55 @@ const executionsCommand = defineAppCommand({
11901
12232
  logs: arg(z.boolean().default(false), { description: "Display job execution logs (detail mode only)" })
11902
12233
  }).strict(),
11903
12234
  run: async (args) => {
12235
+ const jsonOutput = logger.jsonMode || args.json;
11904
12236
  if (args.executionId) {
11905
12237
  const interval = parseDuration(args.interval);
11906
- const { execution, wait } = await getWorkflowExecution({
12238
+ if (!jsonOutput) logger.info(`Execution ID: ${args.executionId}`, { mode: "stream" });
12239
+ if (args.wait) {
12240
+ const result = await waitForWorkflowExecutionById({
12241
+ executionId: args.executionId,
12242
+ workspaceId: args["workspace-id"],
12243
+ profile: args.profile,
12244
+ interval,
12245
+ timeout: parseDuration(args.timeout),
12246
+ until: args.until,
12247
+ showProgress: !jsonOutput,
12248
+ trackJobs: true
12249
+ });
12250
+ if (args.logs && !jsonOutput) {
12251
+ const { execution } = await getWorkflowExecution({
12252
+ executionId: args.executionId,
12253
+ workspaceId: args["workspace-id"],
12254
+ profile: args.profile,
12255
+ logs: true
12256
+ });
12257
+ printExecutionWithLogs(execution);
12258
+ } else if (args.logs) {
12259
+ const { execution } = await getWorkflowExecution({
12260
+ executionId: args.executionId,
12261
+ workspaceId: args["workspace-id"],
12262
+ profile: args.profile,
12263
+ logs: true
12264
+ });
12265
+ const output = {
12266
+ ...result,
12267
+ jobDetails: execution.jobDetails
12268
+ };
12269
+ logger.out(output);
12270
+ } else logger.out(result);
12271
+ const failureMessage = getWorkflowWaitFailureMessage(result, args.until);
12272
+ if (failureMessage) throw new Error(failureMessage);
12273
+ return;
12274
+ }
12275
+ const { execution } = await getWorkflowExecution({
11907
12276
  executionId: args.executionId,
11908
12277
  workspaceId: args["workspace-id"],
11909
12278
  profile: args.profile,
11910
12279
  interval,
11911
12280
  logs: args.logs
11912
12281
  });
11913
- if (!args.json) logger.info(`Execution ID: ${execution.id}`, { mode: "stream" });
11914
- const result = args.wait ? await waitWithSpinner(wait, interval, args.json) : execution;
11915
- if (args.logs && !args.json) printExecutionWithLogs(result);
11916
- else logger.out(result);
12282
+ if (args.logs && !jsonOutput) printExecutionWithLogs(execution);
12283
+ else logger.out(execution);
11917
12284
  } else {
11918
12285
  const executions = await listWorkflowExecutions({
11919
12286
  workspaceId: args["workspace-id"],
@@ -11978,90 +12345,6 @@ const getCommand$5 = defineAppCommand({
11978
12345
 
11979
12346
  //#endregion
11980
12347
  //#region src/cli/commands/workflow/start.ts
11981
- function sleep(ms) {
11982
- return new Promise((resolve) => setTimeout(resolve, ms));
11983
- }
11984
- function formatTime$1(date) {
11985
- return date.toLocaleTimeString("en-US", { hour12: false });
11986
- }
11987
- function colorizeStatus(status) {
11988
- const statusText = WorkflowExecution_Status[status];
11989
- switch (status) {
11990
- case WorkflowExecution_Status.PENDING: return styles.dim(statusText);
11991
- case WorkflowExecution_Status.PENDING_RESUME: return styles.warning(statusText);
11992
- case WorkflowExecution_Status.RUNNING: return styles.info(statusText);
11993
- case WorkflowExecution_Status.SUCCESS: return styles.success(statusText);
11994
- case WorkflowExecution_Status.FAILED: return styles.error(statusText);
11995
- default: return statusText;
11996
- }
11997
- }
11998
- /**
11999
- * Wait for a workflow execution to reach a terminal state, optionally showing progress.
12000
- * @param options - Wait options
12001
- * @returns Final workflow execution info
12002
- */
12003
- async function waitForExecution(options) {
12004
- const { client, workspaceId, executionId, interval, showProgress, trackJobs } = options;
12005
- let lastStatus;
12006
- let lastRunningJobs;
12007
- const sp = showProgress ? spinner({ indent: 2 }).start("Waiting for workflow to complete...") : null;
12008
- try {
12009
- while (true) {
12010
- const { execution } = await client.getWorkflowExecution({
12011
- workspaceId,
12012
- executionId
12013
- });
12014
- if (!execution) {
12015
- sp?.fail(`Execution '${executionId}' not found.`);
12016
- throw new Error(`Execution '${executionId}' not found.`);
12017
- }
12018
- const now = formatTime$1(/* @__PURE__ */ new Date());
12019
- const coloredStatus = colorizeStatus(execution.status);
12020
- if (execution.status !== lastStatus) {
12021
- if (showProgress) {
12022
- sp?.stop();
12023
- logger.info(`Status: ${coloredStatus}`, {
12024
- mode: "stream",
12025
- indent: 2
12026
- });
12027
- sp?.start(`Waiting for workflow to complete...`);
12028
- }
12029
- lastStatus = execution.status;
12030
- }
12031
- if (trackJobs && execution.status === WorkflowExecution_Status.RUNNING) {
12032
- const runningJobs = getRunningJobs(execution);
12033
- if (runningJobs && runningJobs !== lastRunningJobs) {
12034
- if (showProgress) {
12035
- sp?.stop();
12036
- logger.info(`Job | ${runningJobs}: ${coloredStatus}`, {
12037
- mode: "stream",
12038
- indent: 2
12039
- });
12040
- sp?.start(`Waiting for workflow to complete...`);
12041
- }
12042
- lastRunningJobs = runningJobs;
12043
- }
12044
- }
12045
- if (sp) sp.text = `Waiting for workflow to complete... (${now})`;
12046
- if (isTerminalStatus(execution.status)) {
12047
- if (execution.status === WorkflowExecution_Status.SUCCESS) sp?.succeed(`Completed: ${coloredStatus}`);
12048
- else if (execution.status === WorkflowExecution_Status.FAILED) sp?.fail(`Completed: ${coloredStatus}`);
12049
- else sp?.warn(`Completed: ${coloredStatus}`);
12050
- return toWorkflowExecutionInfo(execution);
12051
- }
12052
- await sleep(interval);
12053
- }
12054
- } catch (error) {
12055
- sp?.stop();
12056
- throw error;
12057
- }
12058
- }
12059
- function getRunningJobs(execution) {
12060
- return execution.jobExecutions.filter((job) => job.status === WorkflowJobExecution_Status.RUNNING).map((job) => job.stackedJobName).join(", ");
12061
- }
12062
- function isTerminalStatus(status) {
12063
- return status === WorkflowExecution_Status.SUCCESS || status === WorkflowExecution_Status.FAILED || status === WorkflowExecution_Status.PENDING_RESUME;
12064
- }
12065
12348
  async function startWorkflowCore(options) {
12066
12349
  const { client, workspaceId, workflowName } = options;
12067
12350
  try {
@@ -12076,11 +12359,13 @@ async function startWorkflowCore(options) {
12076
12359
  });
12077
12360
  return {
12078
12361
  executionId,
12079
- wait: (waitOptions) => waitForExecution({
12362
+ wait: (waitOptions) => waitForWorkflowExecution({
12080
12363
  client,
12081
12364
  workspaceId,
12082
12365
  executionId,
12083
12366
  interval: options.interval ?? 3e3,
12367
+ timeout: waitOptions?.timeout,
12368
+ until: waitOptions?.until,
12084
12369
  showProgress: waitOptions?.showProgress,
12085
12370
  trackJobs: true
12086
12371
  })
@@ -12164,7 +12449,11 @@ const startCommand = defineAppCommand({
12164
12449
  const jsonOutput = logger.jsonMode;
12165
12450
  logger.info(`Execution ID: ${executionId}`, { mode: "stream" });
12166
12451
  if (args.wait) {
12167
- const result = await wait({ showProgress: !jsonOutput });
12452
+ const result = await wait({
12453
+ showProgress: !jsonOutput,
12454
+ timeout: parseDuration(args.timeout),
12455
+ until: args.until
12456
+ });
12168
12457
  if (args.logs && !jsonOutput) {
12169
12458
  const { execution } = await getWorkflowExecution({
12170
12459
  executionId,
@@ -12173,7 +12462,20 @@ const startCommand = defineAppCommand({
12173
12462
  logs: true
12174
12463
  });
12175
12464
  printExecutionWithLogs(execution);
12465
+ } else if (args.logs) {
12466
+ const { execution } = await getWorkflowExecution({
12467
+ executionId,
12468
+ workspaceId: args["workspace-id"],
12469
+ profile: args.profile,
12470
+ logs: true
12471
+ });
12472
+ logger.out({
12473
+ ...result,
12474
+ jobDetails: execution.jobDetails
12475
+ });
12176
12476
  } else logger.out(result);
12477
+ const failureMessage = getWorkflowWaitFailureMessage(result, args.until);
12478
+ if (failureMessage) throw new Error(failureMessage);
12177
12479
  } else logger.out({ executionId });
12178
12480
  }
12179
12481
  });
@@ -12183,6 +12485,16 @@ const startCommand = defineAppCommand({
12183
12485
  function formatTime(date) {
12184
12486
  return date.toLocaleTimeString("en-US", { hour12: false });
12185
12487
  }
12488
+ function createUnknownExecutorJob(executorName, jobId) {
12489
+ return {
12490
+ id: jobId,
12491
+ executorName,
12492
+ status: "UNKNOWN",
12493
+ scheduledAt: "N/A",
12494
+ createdAt: "N/A",
12495
+ updatedAt: "N/A"
12496
+ };
12497
+ }
12186
12498
  async function listExecutorJobs(options) {
12187
12499
  const executorName = "executorName" in options ? options.executorName : options.executor.name;
12188
12500
  const client = await initOperatorClient(await loadAccessToken({ profile: options.profile }));
@@ -12266,7 +12578,27 @@ async function watchExecutorJob(options) {
12266
12578
  profile: options.profile
12267
12579
  });
12268
12580
  const interval = options.interval ?? 3e3;
12269
- const sp = spinner().start("Waiting for executor job to complete...");
12581
+ const timeout = options.timeout;
12582
+ const showProgress = options.showProgress ?? !logger.jsonMode;
12583
+ const startedAt = Date.now();
12584
+ const sp = showProgress ? spinner().start("Waiting for executor job to complete...") : null;
12585
+ let attempts = 0;
12586
+ let lastError = null;
12587
+ const remainingTimeout = () => {
12588
+ if (timeout === void 0) return;
12589
+ return timeout - (Date.now() - startedAt);
12590
+ };
12591
+ const withWaitMetadata = (result, timedOut) => ({
12592
+ ...result,
12593
+ elapsedMs: Date.now() - startedAt,
12594
+ attempts,
12595
+ timedOut,
12596
+ lastError
12597
+ });
12598
+ const timeoutResult = (targetType, job) => withWaitMetadata({
12599
+ job: job ? toExecutorJobInfo(job) : createUnknownExecutorJob(executorName, options.jobId),
12600
+ targetType
12601
+ }, true);
12270
12602
  try {
12271
12603
  const { executor } = await client.getExecutorExecutor({
12272
12604
  workspaceId,
@@ -12277,29 +12609,47 @@ async function watchExecutorJob(options) {
12277
12609
  const targetTypeStr = executorTargetTypeToString(targetType);
12278
12610
  let job;
12279
12611
  while (true) {
12280
- job = (await client.getExecutorJob({
12281
- workspaceId,
12282
- executorName,
12283
- jobId: options.jobId
12284
- })).job;
12285
- if (!job) throw new Error(`Job '${options.jobId}' not found.`);
12286
- if (isExecutorJobTerminalStatus(job.status)) break;
12287
- sp.text = `Waiting for executor job... (${formatTime(/* @__PURE__ */ new Date())})`;
12288
- await setTimeout$1(interval);
12612
+ const remainingMs = remainingTimeout();
12613
+ if (remainingMs !== void 0 && remainingMs <= 0) {
12614
+ sp?.fail("Executor job wait timed out.");
12615
+ return timeoutResult(targetTypeStr, job);
12616
+ }
12617
+ try {
12618
+ attempts += 1;
12619
+ job = (await client.getExecutorJob({
12620
+ workspaceId,
12621
+ executorName,
12622
+ jobId: options.jobId
12623
+ })).job;
12624
+ if (!job) throw new Error(`Job '${options.jobId}' not found.`);
12625
+ lastError = null;
12626
+ if (classifyExecutorJobStatus(job.status) !== "transient") break;
12627
+ } catch (error) {
12628
+ if (!isRetryableWaitError(error)) throw error;
12629
+ lastError = formatWaitError(error);
12630
+ if (sp) sp.text = `Retrying executor job poll... (${formatTime(/* @__PURE__ */ new Date())})`;
12631
+ }
12632
+ const nextRemainingMs = remainingTimeout();
12633
+ if (nextRemainingMs !== void 0 && nextRemainingMs <= 0) {
12634
+ sp?.fail("Executor job wait timed out.");
12635
+ return timeoutResult(targetTypeStr, job);
12636
+ }
12637
+ if (sp) sp.text = `Waiting for executor job... (${formatTime(/* @__PURE__ */ new Date())})`;
12638
+ await setTimeout$1(nextRemainingMs === void 0 ? interval : Math.min(interval, nextRemainingMs));
12289
12639
  }
12290
12640
  const jobInfo = toExecutorJobInfo(job);
12291
12641
  const coloredStatus = colorizeExecutorJobStatus(jobInfo.status);
12292
- if (job.status === ExecutorJobStatus.SUCCESS) sp.succeed(`Executor job completed: ${coloredStatus}`);
12293
- else sp.fail(`Executor job completed: ${coloredStatus}`);
12642
+ if (job.status === ExecutorJobStatus.SUCCESS) sp?.succeed(`Executor job completed: ${coloredStatus}`);
12643
+ else sp?.fail(`Executor job completed: ${coloredStatus}`);
12294
12644
  const attemptInfos = (await fetchAll(async (pageToken, maxPageSize) => {
12295
- const { attempts, nextPageToken } = await client.listExecutorJobAttempts({
12645
+ const { attempts: jobAttempts, nextPageToken } = await client.listExecutorJobAttempts({
12296
12646
  workspaceId,
12297
12647
  jobId: options.jobId,
12298
12648
  pageToken,
12299
12649
  pageSize: maxPageSize,
12300
12650
  pageDirection: PageDirection.DESC
12301
12651
  });
12302
- return [attempts, nextPageToken];
12652
+ return [jobAttempts, nextPageToken];
12303
12653
  })).map(toExecutorJobAttemptInfo);
12304
12654
  const jobDetail = {
12305
12655
  ...jobInfo,
@@ -12308,18 +12658,27 @@ async function watchExecutorJob(options) {
12308
12658
  const operationReference = attemptInfos[0]?.operationReference;
12309
12659
  if (operationReference) switch (targetType) {
12310
12660
  case ExecutorTargetType.WORKFLOW:
12311
- sp.stop();
12661
+ sp?.stop();
12312
12662
  try {
12313
- const executionResult = await waitForExecution({
12663
+ const workflowTimeout = remainingTimeout();
12664
+ if (workflowTimeout !== void 0 && workflowTimeout <= 0) return withWaitMetadata({
12665
+ job: jobDetail,
12666
+ targetType: targetTypeStr,
12667
+ workflowExecutionId: operationReference
12668
+ }, true);
12669
+ const executionResult = await waitForWorkflowExecution({
12314
12670
  client,
12315
12671
  workspaceId,
12316
12672
  executionId: operationReference,
12317
12673
  interval,
12318
- showProgress: true,
12674
+ timeout: workflowTimeout,
12675
+ showProgress,
12319
12676
  trackJobs: true
12320
12677
  });
12678
+ attempts += executionResult.attempts;
12679
+ lastError = executionResult.lastError;
12321
12680
  let workflowJobLogs;
12322
- if (options.logs) {
12681
+ if (options.logs) try {
12323
12682
  const { execution: execWithLogs } = await getWorkflowExecution({
12324
12683
  executionId: operationReference,
12325
12684
  workspaceId: options.workspaceId,
@@ -12331,67 +12690,109 @@ async function watchExecutorJob(options) {
12331
12690
  logs: job.logs,
12332
12691
  result: job.result
12333
12692
  }));
12693
+ } catch (error) {
12694
+ logger.warn(`Could not fetch workflow execution logs: ${error instanceof Error ? error.message : error}`);
12334
12695
  }
12335
- return {
12696
+ return withWaitMetadata({
12336
12697
  job: jobDetail,
12337
12698
  targetType: targetTypeStr,
12338
12699
  workflowExecutionId: operationReference,
12339
12700
  workflowStatus: executionResult.status,
12340
12701
  workflowJobLogs
12341
- };
12702
+ }, executionResult.timedOut);
12342
12703
  } catch (error) {
12343
12704
  logger.warn(`Could not track workflow execution: ${error instanceof Error ? error.message : error}`);
12344
- return {
12705
+ return withWaitMetadata({
12345
12706
  job: jobDetail,
12346
12707
  targetType: targetTypeStr,
12347
12708
  workflowExecutionId: operationReference
12348
- };
12709
+ }, false);
12349
12710
  }
12350
12711
  case ExecutorTargetType.FUNCTION:
12351
12712
  case ExecutorTargetType.JOB_FUNCTION:
12352
- sp.start(`Waiting for function execution ${operationReference}...`);
12713
+ sp?.start(`Waiting for function execution ${operationReference}...`);
12353
12714
  try {
12715
+ let functionStatus;
12354
12716
  while (true) {
12355
- const { execution } = await client.getFunctionExecution({
12356
- workspaceId,
12357
- executionId: operationReference
12358
- });
12359
- if (!execution) throw new Error(`Function execution '${operationReference}' not found.`);
12360
- if (isFunctionExecutionTerminalStatus(execution.status)) {
12361
- const statusStr = functionExecutionStatusToString(execution.status);
12362
- const coloredFnStatus = colorizeFunctionExecutionStatus(statusStr);
12363
- if (execution.status === FunctionExecution_Status.SUCCESS) sp.succeed(`Function execution completed: ${coloredFnStatus}`);
12364
- else sp.fail(`Function execution completed: ${coloredFnStatus}`);
12365
- return {
12717
+ const functionTimeout = remainingTimeout();
12718
+ if (functionTimeout !== void 0 && functionTimeout <= 0) {
12719
+ sp?.fail("Function execution wait timed out.");
12720
+ return withWaitMetadata({
12366
12721
  job: jobDetail,
12367
12722
  targetType: targetTypeStr,
12368
12723
  functionExecutionId: operationReference,
12369
- functionStatus: statusStr,
12370
- functionLogs: options.logs ? execution.logs || void 0 : void 0
12371
- };
12724
+ functionStatus
12725
+ }, true);
12372
12726
  }
12373
- sp.text = `Waiting for function execution... (${formatTime(/* @__PURE__ */ new Date())})`;
12374
- await setTimeout$1(interval);
12727
+ try {
12728
+ attempts += 1;
12729
+ const { execution } = await client.getFunctionExecution({
12730
+ workspaceId,
12731
+ executionId: operationReference
12732
+ });
12733
+ if (!execution) throw new Error(`Function execution '${operationReference}' not found.`);
12734
+ lastError = null;
12735
+ functionStatus = functionExecutionStatusToString(execution.status);
12736
+ if (isFunctionExecutionTerminalStatus(execution.status)) {
12737
+ const coloredFnStatus = colorizeFunctionExecutionStatus(functionStatus);
12738
+ if (execution.status === FunctionExecution_Status.SUCCESS) sp?.succeed(`Function execution completed: ${coloredFnStatus}`);
12739
+ else sp?.fail(`Function execution completed: ${coloredFnStatus}`);
12740
+ return withWaitMetadata({
12741
+ job: jobDetail,
12742
+ targetType: targetTypeStr,
12743
+ functionExecutionId: operationReference,
12744
+ functionStatus,
12745
+ functionLogs: options.logs ? execution.logs || void 0 : void 0
12746
+ }, false);
12747
+ }
12748
+ } catch (error) {
12749
+ if (!isRetryableWaitError(error)) throw error;
12750
+ lastError = formatWaitError(error);
12751
+ if (sp) sp.text = `Retrying function execution poll... (${formatTime(/* @__PURE__ */ new Date())})`;
12752
+ }
12753
+ const nextFunctionTimeout = remainingTimeout();
12754
+ if (nextFunctionTimeout !== void 0 && nextFunctionTimeout <= 0) {
12755
+ sp?.fail("Function execution wait timed out.");
12756
+ return withWaitMetadata({
12757
+ job: jobDetail,
12758
+ targetType: targetTypeStr,
12759
+ functionExecutionId: operationReference,
12760
+ functionStatus
12761
+ }, true);
12762
+ }
12763
+ if (sp) sp.text = `Waiting for function execution... (${formatTime(/* @__PURE__ */ new Date())})`;
12764
+ await setTimeout$1(nextFunctionTimeout === void 0 ? interval : Math.min(interval, nextFunctionTimeout));
12375
12765
  }
12376
12766
  } catch (error) {
12377
- sp.warn(`Could not track function execution: ${error instanceof Error ? error.message : error}`);
12378
- return {
12767
+ sp?.warn(`Could not track function execution: ${error instanceof Error ? error.message : error}`);
12768
+ return withWaitMetadata({
12379
12769
  job: jobDetail,
12380
12770
  targetType: targetTypeStr,
12381
12771
  functionExecutionId: operationReference
12382
- };
12772
+ }, false);
12383
12773
  }
12384
12774
  break;
12385
12775
  default: break;
12386
12776
  }
12387
- return {
12777
+ return withWaitMetadata({
12388
12778
  job: jobDetail,
12389
12779
  targetType: targetTypeStr
12390
- };
12780
+ }, false);
12391
12781
  } finally {
12392
- sp.stop();
12782
+ sp?.stop();
12393
12783
  }
12394
12784
  }
12785
+ /**
12786
+ * Build a user-facing failure message for an executor job wait result.
12787
+ * @param result - Executor job wait result
12788
+ * @returns Failure message, or undefined when the wait succeeded
12789
+ */
12790
+ function getExecutorWaitFailureMessage(result) {
12791
+ if (result.timedOut) return `Timed out waiting for executor job '${result.job.id}'. Last status: ${result.job.status}.`;
12792
+ if (result.job.status === "FAILED" || result.job.status === "CANCELED") return `Executor job '${result.job.id}' completed with status ${result.job.status}.`;
12793
+ if (result.workflowStatus === "FAILED") return `Workflow execution '${result.workflowExecutionId}' failed.`;
12794
+ if (result.functionStatus === "FAILED") return `Function execution '${result.functionExecutionId}' failed.`;
12795
+ }
12395
12796
  function printJobWithAttempts(job) {
12396
12797
  const summaryData = [
12397
12798
  ["id", job.id],
@@ -12473,6 +12874,10 @@ const jobsCommand = defineAppCommand({
12473
12874
  alias: "i",
12474
12875
  description: "Polling interval when using --wait (e.g., '3s', '500ms', '1m')"
12475
12876
  }),
12877
+ timeout: arg(durationArg.default("5m"), {
12878
+ alias: "t",
12879
+ description: "Maximum time to wait when using --wait (e.g., '30s', '5m')"
12880
+ }),
12476
12881
  ...pagedLogArgs,
12477
12882
  limit: arg(nonNegativeIntArg.default(50), { description: "Maximum number of jobs to list (0: unlimited, default: 50) (list mode only)" }),
12478
12883
  logs: arg(z.boolean().default(false), {
@@ -12481,6 +12886,7 @@ const jobsCommand = defineAppCommand({
12481
12886
  })
12482
12887
  }).strict(),
12483
12888
  run: async (args) => {
12889
+ const jsonOutput = logger.jsonMode || args.json;
12484
12890
  if (args.jobId) {
12485
12891
  if (args.wait) {
12486
12892
  const result = await watchExecutorJob({
@@ -12489,9 +12895,11 @@ const jobsCommand = defineAppCommand({
12489
12895
  workspaceId: args["workspace-id"],
12490
12896
  profile: args.profile,
12491
12897
  interval: parseDuration(args.interval),
12492
- logs: args.logs
12898
+ timeout: parseDuration(args.timeout),
12899
+ logs: args.logs,
12900
+ showProgress: !jsonOutput
12493
12901
  });
12494
- if (!args.json) {
12902
+ if (!jsonOutput) {
12495
12903
  logger.log(styles.bold(`Target Type: ${result.targetType}\n`));
12496
12904
  printJobWithAttempts(result.job);
12497
12905
  if (result.workflowExecutionId) {
@@ -12526,6 +12934,8 @@ const jobsCommand = defineAppCommand({
12526
12934
  }
12527
12935
  }
12528
12936
  } else logger.out(result);
12937
+ const failureMessage = getExecutorWaitFailureMessage(result);
12938
+ if (failureMessage) throw new Error(failureMessage);
12529
12939
  return;
12530
12940
  }
12531
12941
  const job = await getExecutorJob({
@@ -12535,7 +12945,7 @@ const jobsCommand = defineAppCommand({
12535
12945
  workspaceId: args["workspace-id"],
12536
12946
  profile: args.profile
12537
12947
  });
12538
- if (args.attempts && !args.json) printJobWithAttempts(job);
12948
+ if (args.attempts && !jsonOutput) printJobWithAttempts(job);
12539
12949
  else logger.out(job);
12540
12950
  } else {
12541
12951
  if (args.wait) logger.warn("--wait flag is ignored in list mode. Specify a job ID to wait.");
@@ -12722,12 +13132,17 @@ The \`--logs\` option displays logs from the downstream execution when available
12722
13132
  alias: "i",
12723
13133
  description: "Polling interval when using --wait (e.g., '3s', '500ms', '1m')"
12724
13134
  }),
13135
+ timeout: arg(durationArg.default("5m"), {
13136
+ alias: "t",
13137
+ description: "Maximum time to wait when using --wait (e.g., '30s', '5m')"
13138
+ }),
12725
13139
  logs: arg(z.boolean().default(false), {
12726
13140
  alias: "l",
12727
13141
  description: "Display function execution logs after completion (requires --wait)"
12728
13142
  })
12729
13143
  }).strict(),
12730
13144
  run: async (args) => {
13145
+ const jsonOutput = logger.jsonMode || args.json;
12731
13146
  await assertWritable({ profile: args.profile });
12732
13147
  const client = await initOperatorClient(await loadAccessToken({ profile: args.profile }));
12733
13148
  const workspaceId = await loadWorkspaceId({
@@ -12768,9 +13183,11 @@ The \`--logs\` option displays logs from the downstream execution when available
12768
13183
  workspaceId: args["workspace-id"],
12769
13184
  profile: args.profile,
12770
13185
  interval: parseDuration(args.interval),
12771
- logs: args.logs
13186
+ timeout: parseDuration(args.timeout),
13187
+ logs: args.logs,
13188
+ showProgress: !jsonOutput
12772
13189
  });
12773
- if (!args.json) {
13190
+ if (!jsonOutput) {
12774
13191
  logger.log(styles.bold(`\nTarget Type: ${watchResult.targetType}`));
12775
13192
  logger.log(`Job Status: ${watchResult.job.status}`);
12776
13193
  if (watchResult.workflowExecutionId) {
@@ -12805,6 +13222,8 @@ The \`--logs\` option displays logs from the downstream execution when available
12805
13222
  }
12806
13223
  }
12807
13224
  } else logger.out(watchResult);
13225
+ const failureMessage = getExecutorWaitFailureMessage(watchResult);
13226
+ if (failureMessage) throw new Error(failureMessage);
12808
13227
  }
12809
13228
  }
12810
13229
  });
@@ -15942,11 +16361,13 @@ async function resumeWorkflow(options) {
15942
16361
  });
15943
16362
  return {
15944
16363
  executionId,
15945
- wait: (waitOptions) => waitForExecution({
16364
+ wait: (waitOptions) => waitForWorkflowExecution({
15946
16365
  client,
15947
16366
  workspaceId,
15948
16367
  executionId,
15949
16368
  interval: options.interval ?? 3e3,
16369
+ timeout: waitOptions?.timeout,
16370
+ until: waitOptions?.until,
15950
16371
  showProgress: waitOptions?.showProgress
15951
16372
  })
15952
16373
  };
@@ -15970,16 +16391,21 @@ const resumeCommand = defineAppCommand({
15970
16391
  ...waitArgs
15971
16392
  }).strict(),
15972
16393
  run: async (args) => {
16394
+ const jsonOutput = logger.jsonMode || args.json;
15973
16395
  const { executionId, wait } = await resumeWorkflow({
15974
16396
  executionId: args.executionId,
15975
16397
  workspaceId: args["workspace-id"],
15976
16398
  profile: args.profile,
15977
16399
  interval: parseDuration(args.interval)
15978
16400
  });
15979
- if (!args.json) logger.info(`Execution ID: ${executionId}`, { mode: "stream" });
16401
+ if (!jsonOutput) logger.info(`Execution ID: ${executionId}`, { mode: "stream" });
15980
16402
  if (args.wait) {
15981
- const result = await wait({ showProgress: !args.json });
15982
- if (args.logs && !args.json) {
16403
+ const result = await wait({
16404
+ showProgress: !jsonOutput,
16405
+ timeout: parseDuration(args.timeout),
16406
+ until: args.until
16407
+ });
16408
+ if (args.logs && !jsonOutput) {
15983
16409
  const { execution } = await getWorkflowExecution({
15984
16410
  executionId,
15985
16411
  workspaceId: args["workspace-id"],
@@ -15987,11 +16413,112 @@ const resumeCommand = defineAppCommand({
15987
16413
  logs: true
15988
16414
  });
15989
16415
  printExecutionWithLogs(execution);
16416
+ } else if (args.logs) {
16417
+ const { execution } = await getWorkflowExecution({
16418
+ executionId,
16419
+ workspaceId: args["workspace-id"],
16420
+ profile: args.profile,
16421
+ logs: true
16422
+ });
16423
+ logger.out({
16424
+ ...result,
16425
+ jobDetails: execution.jobDetails
16426
+ });
15990
16427
  } else logger.out(result);
16428
+ const failureMessage = getWorkflowWaitFailureMessage(result, args.until);
16429
+ if (failureMessage) throw new Error(failureMessage);
15991
16430
  } else logger.out({ executionId });
15992
16431
  }
15993
16432
  });
15994
16433
 
16434
+ //#endregion
16435
+ //#region src/cli/commands/workflow/wait.ts
16436
+ /**
16437
+ * Wait for an existing workflow execution by ID.
16438
+ * @param options - Workflow wait options
16439
+ * @returns Workflow wait result
16440
+ */
16441
+ async function waitWorkflowExecution(options) {
16442
+ return await waitForWorkflowExecutionById({
16443
+ ...options,
16444
+ showProgress: options.showProgress ?? !logger.jsonMode,
16445
+ trackJobs: options.trackJobs ?? true
16446
+ });
16447
+ }
16448
+ /**
16449
+ * Attach workflow job logs to a wait result when requested.
16450
+ * @param result - Workflow wait result
16451
+ * @param options - Workflow wait options
16452
+ * @returns Workflow wait result with optional job details
16453
+ */
16454
+ async function addWorkflowLogsToWaitResult(result, options) {
16455
+ const { execution } = await getWorkflowExecution({
16456
+ executionId: options.executionId,
16457
+ workspaceId: options.workspaceId,
16458
+ profile: options.profile,
16459
+ logs: true
16460
+ });
16461
+ return {
16462
+ ...result,
16463
+ jobDetails: execution.jobDetails
16464
+ };
16465
+ }
16466
+ const waitCommand = defineAppCommand({
16467
+ name: "wait",
16468
+ description: "Wait for a workflow execution.",
16469
+ examples: [
16470
+ {
16471
+ cmd: "execution-id --until success --timeout 10m --json",
16472
+ desc: "Wait for workflow success"
16473
+ },
16474
+ {
16475
+ cmd: "execution-id --until suspended --timeout 6m --logs --json",
16476
+ desc: "Wait for a workflow wait point"
16477
+ },
16478
+ {
16479
+ cmd: "execution-id --until terminal",
16480
+ desc: "Wait for success, failure, or suspension"
16481
+ }
16482
+ ],
16483
+ args: z.object({
16484
+ ...workspaceArgs,
16485
+ "execution-id": arg(z.string(), {
16486
+ positional: true,
16487
+ description: "Execution ID"
16488
+ }),
16489
+ ...workflowWaitControlArgs
16490
+ }).strict(),
16491
+ run: async (args) => {
16492
+ const jsonOutput = logger.jsonMode || args.json;
16493
+ const result = await waitWorkflowExecution({
16494
+ executionId: args.executionId,
16495
+ workspaceId: args["workspace-id"],
16496
+ profile: args.profile,
16497
+ interval: parseDuration(args.interval),
16498
+ timeout: parseDuration(args.timeout),
16499
+ until: args.until,
16500
+ showProgress: !jsonOutput
16501
+ });
16502
+ const output = args.logs ? await addWorkflowLogsToWaitResult(result, {
16503
+ executionId: args.executionId,
16504
+ workspaceId: args["workspace-id"],
16505
+ profile: args.profile
16506
+ }) : result;
16507
+ if (!jsonOutput && output.jobDetails) printExecutionWithLogs({
16508
+ id: output.id,
16509
+ workflowName: output.workflowName,
16510
+ status: output.status,
16511
+ jobExecutions: output.jobExecutions,
16512
+ startedAt: output.startedAt,
16513
+ finishedAt: output.finishedAt,
16514
+ jobDetails: output.jobDetails
16515
+ });
16516
+ else logger.out(output);
16517
+ const failureMessage = getWorkflowWaitFailureMessage(result, args.until);
16518
+ if (failureMessage) throw new Error(failureMessage);
16519
+ }
16520
+ });
16521
+
15995
16522
  //#endregion
15996
16523
  //#region src/cli/commands/workspace/app/transform.ts
15997
16524
  const statusToString = (status) => {
@@ -17669,5 +18196,5 @@ function isDeno() {
17669
18196
  }
17670
18197
 
17671
18198
  //#endregion
17672
- export { listCommand$5 as $, INITIAL_SCHEMA_NUMBER as $t, truncate as A, commonArgs as An, startCommand as At, logBetaWarning as B, getExecutor as Bt, listCommand$2 as C, PluginManager as Cn, triggerExecutor as Ct, resumeWorkflow as D, apiCall as Dn, jobsCommand as Dt, resumeCommand as E, apiCommand as En, getExecutorJob as Et, writeDbTypesFile as F, pagedLogArgs as Fn, getWorkflowExecution as Ft, organizationTree as G, MIGRATION_LABEL_KEY as Gt, removeCommand$1 as H, executeScript as Ht, getConfiguredEditorCommand as I, paginationArgs as In, listWorkflowExecutions as It, listOrganizations as J, compareSnapshotWithRemote as Jt, treeCommand as K, handleOptionalToRequiredError as Kt, openInConfiguredEditor as L, toPageDirection as Ln, functionExecutionStatusToString as Lt, generate as M, confirmationArgs as Mn, getCommand$5 as Mt, generateCommand as N, deploymentArgs as Nn, getWorkflow as Nt, listCommand$3 as O, assertWritable as On, listExecutorJobs as Ot, generateMigrationScript as P, isVerbose as Pn, executionsCommand as Pt, updateFolder as Q, DIFF_FILE_NAME as Qt, show as R, workspaceArgs as Rn, formatKeyValueTable as Rt, listApps as S, sdkNameLabelKey as Sn, triggerCommand as St, healthCommand as T, prompt as Tn, listExecutors as Tt, updateCommand$1 as U, waitForExecution$1 as Ut, remove as V, deploy as Vt, updateOrganization as W, bundleMigrationScript as Wt, getOrganization as X, protoGqlPermission as Xt, getCommand$1 as Y, generateAllTypeManifestsFromSnapshot as Yt, updateCommand$2 as Z, DB_TYPES_FILE_NAME as Zt, getWorkspace as _, formatMigrationDiff as _n, listFunctionRegistries as _t, updateUser as a, createSnapshotFromLocalTypes as an, createCommand$1 as at, createCommand as b, ensureConfigId as bn, listWebhookExecutors as bt, listCommand as c, getMigrationFilePath as cn, listOAuth2Clients as ct, inviteUser as d, isValidMigrationNumber as dn, getMachineUserToken as dt, MIGRATE_FILE_NAME as en, listFolders as et, restoreCommand as f, loadDiff as fn, tokenCommand as ft, getCommand as g, formatDiffSummary as gn, listCommand$8 as gt, listWorkspaces as h, parseMigrationNumberArg as hn, generate$1 as ht, updateCommand as i, compareSnapshots as in, deleteFolder as it, truncateCommand as j, configArg as jn, startWorkflow as jt, listWorkflows as k, defineAppCommand as kn, watchExecutorJob as kt, listUsers as l, getMigrationFiles as ln, getCommand$3 as lt, listCommand$1 as m, formatMigrationNumber as mn, listMachineUsers as mt, query as n, assertValidMigrationFiles as nn, getFolder as nt, removeCommand as o, getLatestMigrationNumber as on, createFolder as ot, restoreWorkspace as p, reconstructSnapshotFromMigrations as pn, listCommand$7 as pt, listCommand$4 as q, parseMigrationLabelNumber as qt, queryCommand as r, compareLocalTypesWithSnapshot as rn, deleteCommand$1 as rt, removeUser as s, getMigrationDirPath as sn, listCommand$6 as st, isNativeTypeScriptRuntime as t, SCHEMA_FILE_NAME as tn, getCommand$2 as tt, inviteCommand as u, getNextMigrationNumber as un, getOAuth2Client as ut, deleteCommand as v, hasChanges as vn, getCommand$4 as vt, getAppHealth as w, generateUserTypes as wn, listCommand$9 as wt, createWorkspace as x, resourceTrn as xn, webhookCommand as xt, deleteWorkspace as y, getNamespacesWithMigrations as yn, getFunctionRegistry as yt, showCommand as z, getCommand$6 as zt };
17673
- //# sourceMappingURL=runtime-BU6KtCvk.mjs.map
18199
+ export { updateCommand$2 as $, protoGqlPermission as $t, listCommand$3 as A, apiCall as An, jobsCommand as At, show as B, toPageDirection as Bn, functionExecutionStatusToString as Bt, listCommand$2 as C, ensureConfigId as Cn, webhookCommand as Ct, waitWorkflowExecution as D, generateUserTypes as Dn, listExecutors as Dt, waitCommand as E, PluginManager as En, listCommand$9 as Et, generateCommand as F, confirmationArgs as Fn, getCommand$5 as Ft, updateCommand$1 as G, executeScript as Gt, logBetaWarning as H, getCommand$6 as Ht, generateMigrationScript as I, deploymentArgs as In, getWorkflow as It, treeCommand as J, MIGRATION_LABEL_KEY as Jt, updateOrganization as K, waitForExecution as Kt, writeDbTypesFile as L, isVerbose as Ln, executionsCommand as Lt, truncate as M, defineAppCommand as Mn, watchExecutorJob as Mt, truncateCommand as N, commonArgs as Nn, startCommand as Nt, resumeCommand as O, prompt as On, getExecutorJob as Ot, generate as P, configArg as Pn, startWorkflow as Pt, getOrganization as Q, generateAllTypeManifestsFromSnapshot as Qt, getConfiguredEditorCommand as R, pagedLogArgs as Rn, getWorkflowExecution as Rt, listApps as S, getNamespacesWithMigrations as Sn, listWebhookExecutors as St, healthCommand as T, sdkNameLabelKey as Tn, triggerExecutor as Tt, remove as U, getExecutor as Ut, showCommand as V, workspaceArgs as Vn, formatKeyValueTable as Vt, removeCommand$1 as W, deploy as Wt, listOrganizations as X, parseMigrationLabelNumber as Xt, listCommand$4 as Y, handleOptionalToRequiredError as Yt, getCommand$1 as Z, compareSnapshotWithRemote as Zt, getWorkspace as _, formatMigrationNumber as _n, generate$1 as _t, updateUser as a, assertValidMigrationFiles as an, deleteCommand$1 as at, createCommand as b, formatMigrationDiff as bn, getCommand$4 as bt, listCommand as c, createSnapshotFromLocalTypes as cn, createFolder as ct, inviteUser as d, getMigrationFilePath as dn, getCommand$3 as dt, DB_TYPES_FILE_NAME as en, updateFolder as et, restoreCommand as f, getMigrationFiles as fn, getOAuth2Client as ft, getCommand as g, reconstructSnapshotFromMigrations as gn, listMachineUsers as gt, listWorkspaces as h, loadDiff as hn, listCommand$7 as ht, updateCommand as i, SCHEMA_FILE_NAME as in, getFolder as it, listWorkflows as j, assertWritable as jn, listExecutorJobs as jt, resumeWorkflow as k, apiCommand as kn, getExecutorWaitFailureMessage as kt, listUsers as l, getLatestMigrationNumber as ln, listCommand$6 as lt, listCommand$1 as m, isValidMigrationNumber as mn, tokenCommand as mt, query as n, INITIAL_SCHEMA_NUMBER as nn, listFolders as nt, removeCommand as o, compareLocalTypesWithSnapshot as on, deleteFolder as ot, restoreWorkspace as p, getNextMigrationNumber as pn, getMachineUserToken as pt, organizationTree as q, bundleMigrationScript as qt, queryCommand as r, MIGRATE_FILE_NAME as rn, getCommand$2 as rt, removeUser as s, compareSnapshots as sn, createCommand$1 as st, isNativeTypeScriptRuntime as t, DIFF_FILE_NAME as tn, listCommand$5 as tt, inviteCommand as u, getMigrationDirPath as un, listOAuth2Clients as ut, deleteCommand as v, parseMigrationNumberArg as vn, listCommand$8 as vt, getAppHealth as w, resourceTrn as wn, triggerCommand as wt, createWorkspace as x, hasChanges as xn, getFunctionRegistry as xt, deleteWorkspace as y, formatDiffSummary as yn, listFunctionRegistries as yt, openInConfiguredEditor as z, paginationArgs as zn, listWorkflowExecutions as zt };
18200
+ //# sourceMappingURL=runtime-DxaBq6U8.mjs.map