@withone/cli 1.47.6 → 1.47.7

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.
@@ -1289,7 +1289,7 @@ async function executeSubflowStep(step, context, api, permissions, allowedAction
1289
1289
  if (flowStack.includes(resolvedKey)) {
1290
1290
  throw new Error(`Circular flow detected: ${[...flowStack, resolvedKey].join(" \u2192 ")}`);
1291
1291
  }
1292
- const { loadFlowWithMeta: loadFlowWithMeta2 } = await import("./flow-runner-KL2ATVBD.js");
1292
+ const { loadFlowWithMeta: loadFlowWithMeta2 } = await import("./flow-runner-7VRNSKWU.js");
1293
1293
  const { flow: subFlow, rootDir: subRootDir } = loadFlowWithMeta2(resolvedKey);
1294
1294
  const subContext = await executeFlow(
1295
1295
  subFlow,
@@ -1503,16 +1503,17 @@ async function executeSingleStep(step, context, api, permissions, allowedActionI
1503
1503
  }
1504
1504
  const startTime = Date.now();
1505
1505
  let lastError;
1506
- const maxAttempts = step.onError?.strategy === "retry" && step.onError.retries ? step.onError.retries + 1 : 1;
1506
+ const onError = step.onError ?? context._defaultOnError;
1507
+ const maxAttempts = onError?.strategy === "retry" && onError.retries ? onError.retries + 1 : 1;
1507
1508
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
1508
1509
  try {
1509
1510
  if (attempt > 1) {
1510
- const delay = computeRetryDelay(step.onError, attempt);
1511
+ const delay = computeRetryDelay(onError, attempt);
1511
1512
  options.onEvent?.({
1512
1513
  event: "step:retry",
1513
1514
  stepId: step.id,
1514
1515
  attempt,
1515
- maxRetries: step.onError.retries,
1516
+ maxRetries: onError.retries,
1516
1517
  delayMs: delay
1517
1518
  });
1518
1519
  await sleep2(delay);
@@ -1599,8 +1600,8 @@ async function executeSingleStep(step, context, api, permissions, allowedActionI
1599
1600
  if (attempt === maxAttempts) {
1600
1601
  break;
1601
1602
  }
1602
- if (step.onError?.strategy === "retry" && (step.onError.retryOn || step.onError.failFastOn)) {
1603
- const decision = shouldRetryError(lastError, step.onError);
1603
+ if (onError?.strategy === "retry" && (onError.retryOn || onError.failFastOn)) {
1604
+ const decision = shouldRetryError(lastError, onError);
1604
1605
  if (!decision.retry) {
1605
1606
  options.onEvent?.({
1606
1607
  event: "step:retry-skip",
@@ -1614,7 +1615,7 @@ async function executeSingleStep(step, context, api, permissions, allowedActionI
1614
1615
  }
1615
1616
  }
1616
1617
  const errorMessage = lastError?.message || "Unknown error";
1617
- const strategy = step.onError?.strategy || "fail";
1618
+ const strategy = onError?.strategy || "fail";
1618
1619
  const retriesUsed = Math.max(0, maxAttempts - 1);
1619
1620
  const isTimeout = lastError instanceof StepTimeoutError;
1620
1621
  const errorCode = lastError?.errorCode;
@@ -1629,7 +1630,7 @@ async function executeSingleStep(step, context, api, permissions, allowedActionI
1629
1630
  context.steps[step.id] = result;
1630
1631
  return result;
1631
1632
  }
1632
- if (strategy === "fallback" && step.onError?.fallbackStepId) {
1633
+ if (strategy === "fallback" && onError?.fallbackStepId) {
1633
1634
  const result = {
1634
1635
  status: isTimeout ? "timeout" : "failed",
1635
1636
  error: errorMessage,
@@ -1750,6 +1751,7 @@ async function executeFlow(flow, inputs, api, permissions, allowedActionIds, opt
1750
1751
  loop: {}
1751
1752
  };
1752
1753
  context.input = resolvedInputs;
1754
+ context._defaultOnError = flow.defaultOnError;
1753
1755
  const completedStepIds = resumeState ? new Set(resumeState.completedSteps) : void 0;
1754
1756
  if (options.dryRun && !options.mock) {
1755
1757
  options.onEvent?.({
@@ -1807,7 +1809,8 @@ var FLOW_SCHEMA = {
1807
1809
  description: { type: "string", required: false, description: "What this flow does" },
1808
1810
  version: { type: "string", required: false, description: "Semver or arbitrary version string" },
1809
1811
  inputs: { type: "object", required: true, description: "Input declarations (Record<string, InputDeclaration>)" },
1810
- steps: { type: "array", required: true, description: "Ordered array of steps", stepsArray: true }
1812
+ steps: { type: "array", required: true, description: "Ordered array of steps", stepsArray: true },
1813
+ defaultOnError: { type: "object", required: false, description: 'Default error strategy inherited by every step without its own `onError` (e.g. { "strategy": "continue" }). A step opts out with its own `onError`.' }
1811
1814
  },
1812
1815
  inputFields: {
1813
1816
  type: { type: "string", required: true, description: "Data type: string, number, boolean, object, array", enum: ["string", "number", "boolean", "object", "array"] },
@@ -12,7 +12,7 @@ import {
12
12
  stripStepsAlias,
13
13
  summarizeFlowInputs,
14
14
  walkSteps
15
- } from "./chunk-SFGH6VAJ.js";
15
+ } from "./chunk-KWGN3RJR.js";
16
16
  import "./chunk-44CV5IMX.js";
17
17
  import "./chunk-K6MWE2ZH.js";
18
18
  export {
package/dist/index.js CHANGED
@@ -30,7 +30,7 @@ import {
30
30
  searchCachePath,
31
31
  validateActionInput,
32
32
  writeCache
33
- } from "./chunk-SFGH6VAJ.js";
33
+ } from "./chunk-KWGN3RJR.js";
34
34
  import {
35
35
  memSqlCommand
36
36
  } from "./chunk-QV3Y5N5G.js";
@@ -3101,6 +3101,16 @@ function validateFlowSchema(flow2) {
3101
3101
  if (f.version !== void 0 && typeof f.version !== "string") {
3102
3102
  errors.push({ path: "version", message: '"version" must be a string' });
3103
3103
  }
3104
+ if (f.defaultOnError !== void 0) {
3105
+ if (!f.defaultOnError || typeof f.defaultOnError !== "object" || Array.isArray(f.defaultOnError)) {
3106
+ errors.push({ path: "defaultOnError", message: '"defaultOnError" must be an object (e.g. { "strategy": "continue" })' });
3107
+ } else {
3108
+ const oe = f.defaultOnError;
3109
+ if (!FLOW_SCHEMA.errorStrategies.includes(oe.strategy)) {
3110
+ errors.push({ path: "defaultOnError.strategy", message: `Error strategy must be one of: ${FLOW_SCHEMA.errorStrategies.join(", ")}` });
3111
+ }
3112
+ }
3113
+ }
3104
3114
  if (!f.inputs || typeof f.inputs !== "object" || Array.isArray(f.inputs)) {
3105
3115
  errors.push({ path: "inputs", message: 'Flow must have an "inputs" object' });
3106
3116
  } else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@withone/cli",
3
- "version": "1.47.6",
3
+ "version": "1.47.7",
4
4
  "description": "CLI for managing One",
5
5
  "type": "module",
6
6
  "files": [
@@ -566,6 +566,22 @@ the outputSchema declaration.
566
566
 
567
567
  Strategies: `fail` (default), `continue`, `retry`, `fallback`.
568
568
 
569
+ **Flow-level default (cli#93).** Set `defaultOnError` at the top of the flow and every step without its own `onError` inherits it — handy when most steps should `continue` (e.g. rendering/formatting pipelines) and you don't want to repeat it N times. A step opts out by declaring its own `onError`:
570
+
571
+ ```json
572
+ {
573
+ "key": "render-report",
574
+ "defaultOnError": { "strategy": "continue" },
575
+ "steps": [
576
+ { "id": "critical", "onError": { "strategy": "fail" }, ... }, // stays fatal
577
+ { "id": "chart", ... }, // inherits continue
578
+ { "id": "thumbnail", ... } // inherits continue
579
+ ]
580
+ }
581
+ ```
582
+
583
+ Scoped per-flow: a sub-flow uses its own `defaultOnError`, not the parent's.
584
+
569
585
  **Retry backoff.** By default each retry waits exactly `retryDelayMs`. For rate-limited APIs add `"backoff": "exponential"` (or `"exponential-jitter"`) and an optional `"maxDelayMs"` cap (defaults to 30000):
570
586
 
571
587
  ```json