@withone/cli 1.25.0 → 1.26.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.
@@ -914,7 +914,7 @@ async function executeSubflowStep(step, context, api, permissions, allowedAction
914
914
  if (flowStack.includes(resolvedKey)) {
915
915
  throw new Error(`Circular flow detected: ${[...flowStack, resolvedKey].join(" \u2192 ")}`);
916
916
  }
917
- const { loadFlowWithMeta: loadFlowWithMeta2 } = await import("./flow-runner-MXRNWG4Y.js");
917
+ const { loadFlowWithMeta: loadFlowWithMeta2 } = await import("./flow-runner-3DCROTPW.js");
918
918
  const { flow: subFlow, rootDir: subRootDir } = loadFlowWithMeta2(resolvedKey);
919
919
  const subContext = await executeFlow(
920
920
  subFlow,
@@ -1027,6 +1027,12 @@ async function executeBashStep(step, context, options) {
1027
1027
  response: { stdout: stdout.trim(), stderr: stderr.trim(), exitCode: 0 }
1028
1028
  };
1029
1029
  }
1030
+ function describe(value) {
1031
+ if (value === null) return "null";
1032
+ if (Array.isArray(value)) return `array (${JSON.stringify(value)})`;
1033
+ if (typeof value === "object") return `object (${JSON.stringify(value)})`;
1034
+ return `${typeof value} (${JSON.stringify(value)})`;
1035
+ }
1030
1036
  function isMissing(value) {
1031
1037
  if (value === void 0 || value === null) return true;
1032
1038
  if (typeof value === "string" && value.length === 0) return true;
@@ -1218,18 +1224,68 @@ async function executeSteps(steps, context, api, permissions, allowedActionIds,
1218
1224
  return results;
1219
1225
  }
1220
1226
  async function executeFlow(flow, inputs, api, permissions, allowedActionIds, options = {}, resumeState, flowStack = []) {
1221
- for (const [name, decl] of Object.entries(flow.inputs)) {
1222
- if (decl.required !== false && inputs[name] === void 0 && decl.default === void 0) {
1223
- throw new Error(`Missing required input: "${name}" \u2014 ${decl.description || ""}`);
1224
- }
1225
- }
1226
1227
  const resolvedInputs = {};
1227
1228
  for (const [name, decl] of Object.entries(flow.inputs)) {
1228
- if (inputs[name] !== void 0) {
1229
- resolvedInputs[name] = inputs[name];
1230
- } else if (decl.default !== void 0) {
1231
- resolvedInputs[name] = decl.default;
1229
+ const provided = inputs[name];
1230
+ const isMissing2 = provided === void 0 || provided === null;
1231
+ if (isMissing2) {
1232
+ if (decl.required !== false && decl.default === void 0) {
1233
+ throw new Error(`Missing required input: "${name}"${decl.description ? ` \u2014 ${decl.description}` : ""}`);
1234
+ }
1235
+ if (decl.default !== void 0) {
1236
+ resolvedInputs[name] = decl.default;
1237
+ }
1238
+ continue;
1239
+ }
1240
+ let value = provided;
1241
+ switch (decl.type) {
1242
+ case "string":
1243
+ if (typeof value !== "string") value = String(value);
1244
+ break;
1245
+ case "number":
1246
+ if (typeof value === "string" && value.trim() !== "" && !Number.isNaN(Number(value))) {
1247
+ value = Number(value);
1248
+ }
1249
+ if (typeof value !== "number" || Number.isNaN(value)) {
1250
+ throw new Error(`Input "${name}" must be a number, got ${describe(provided)}`);
1251
+ }
1252
+ break;
1253
+ case "boolean":
1254
+ if (value === "true" || value === "1" || value === 1) value = true;
1255
+ else if (value === "false" || value === "0" || value === 0) value = false;
1256
+ if (typeof value !== "boolean") {
1257
+ throw new Error(`Input "${name}" must be a boolean, got ${describe(provided)}`);
1258
+ }
1259
+ break;
1260
+ case "array":
1261
+ if (typeof value === "string") {
1262
+ try {
1263
+ value = JSON.parse(value);
1264
+ } catch {
1265
+ }
1266
+ }
1267
+ if (!Array.isArray(value)) {
1268
+ throw new Error(`Input "${name}" must be an array, got ${describe(provided)}`);
1269
+ }
1270
+ break;
1271
+ case "object":
1272
+ if (typeof value === "string") {
1273
+ try {
1274
+ value = JSON.parse(value);
1275
+ } catch {
1276
+ }
1277
+ }
1278
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
1279
+ throw new Error(`Input "${name}" must be an object, got ${describe(provided)}`);
1280
+ }
1281
+ break;
1282
+ }
1283
+ if (Array.isArray(decl.enum) && decl.enum.length > 0) {
1284
+ if (!decl.enum.some((allowed) => allowed === value)) {
1285
+ throw new Error(`Input "${name}" must be one of ${JSON.stringify(decl.enum)}, got ${JSON.stringify(value)}`);
1286
+ }
1232
1287
  }
1288
+ resolvedInputs[name] = value;
1233
1289
  }
1234
1290
  const context = resumeState?.context || {
1235
1291
  input: resolvedInputs,
@@ -1237,6 +1293,7 @@ async function executeFlow(flow, inputs, api, permissions, allowedActionIds, opt
1237
1293
  steps: {},
1238
1294
  loop: {}
1239
1295
  };
1296
+ context.input = resolvedInputs;
1240
1297
  const completedStepIds = resumeState ? new Set(resumeState.completedSteps) : void 0;
1241
1298
  if (options.dryRun && !options.mock) {
1242
1299
  options.onEvent?.({
@@ -1301,7 +1358,8 @@ var FLOW_SCHEMA = {
1301
1358
  required: { type: "boolean", required: false, description: "Whether this input must be provided" },
1302
1359
  default: { type: "unknown", required: false, description: "Default value if not provided" },
1303
1360
  description: { type: "string", required: false, description: "Human-readable description" },
1304
- connection: { type: "object", required: false, description: 'Connection metadata: { platform: "gmail" } \u2014 enables auto-resolution' }
1361
+ connection: { type: "object", required: false, description: 'Connection metadata: { platform: "gmail" } \u2014 enables auto-resolution' },
1362
+ enum: { type: "array", required: false, description: "Allowed values. Resolved input must equal one of these (post-coercion)." }
1305
1363
  },
1306
1364
  stepCommonFields: {
1307
1365
  id: { type: "string", required: true, description: "Unique step identifier (used in selectors)" },
@@ -11,7 +11,7 @@ import {
11
11
  saveFlow,
12
12
  summarizeFlowInputs,
13
13
  walkSteps
14
- } from "./chunk-ONGJ2QKB.js";
14
+ } from "./chunk-ZOIXA7MV.js";
15
15
  export {
16
16
  FlowRunner,
17
17
  collectStepTypes,
package/dist/index.js CHANGED
@@ -16,7 +16,7 @@ import {
16
16
  loadFlowWithMeta,
17
17
  resolveFlowPath,
18
18
  saveFlow
19
- } from "./chunk-ONGJ2QKB.js";
19
+ } from "./chunk-ZOIXA7MV.js";
20
20
 
21
21
  // src/index.ts
22
22
  import { createRequire as createRequire2 } from "module";
@@ -2281,6 +2281,11 @@ function validateFlowSchema(flow2) {
2281
2281
  if (!d.type || !FLOW_SCHEMA.validInputTypes.includes(d.type)) {
2282
2282
  errors.push({ path: `${prefix}.type`, message: `Input type must be one of: ${FLOW_SCHEMA.validInputTypes.join(", ")}` });
2283
2283
  }
2284
+ if (d.enum !== void 0) {
2285
+ if (!Array.isArray(d.enum) || d.enum.length === 0) {
2286
+ errors.push({ path: `${prefix}.enum`, message: '"enum" must be a non-empty array of allowed values' });
2287
+ }
2288
+ }
2284
2289
  if (d.connection !== void 0) {
2285
2290
  if (!d.connection || typeof d.connection !== "object") {
2286
2291
  errors.push({ path: `${prefix}.connection`, message: "Connection metadata must be an object" });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@withone/cli",
3
- "version": "1.25.0",
3
+ "version": "1.26.0",
4
4
  "description": "CLI for managing One",
5
5
  "type": "module",
6
6
  "files": [
@@ -186,9 +186,29 @@ one --agent flow execute <key> -i connectionKey=xxx -i param=value
186
186
  | `default` | Default value if not provided |
187
187
  | `description` | Human-readable description |
188
188
  | `connection` | `{ "platform": "gmail" }` — enables auto-resolution |
189
+ | `enum` | Array of allowed values; rejected if input doesn't match (post-coercion) |
189
190
 
190
191
  Connection inputs with a `connection` field auto-resolve if the user has exactly one connection for that platform.
191
192
 
193
+ **Validation, coercion, and enums.** At flow start the engine validates every declared input:
194
+
195
+ 1. **Required check** — `required: true` (the default) inputs without a value or `default` cause `Missing required input: "X"`.
196
+ 2. **Type coercion** — narrow, bidirectional fixes only:
197
+ - `number`: numeric strings (`"5"`) become `5`. Non-numeric strings throw.
198
+ - `boolean`: `"true"`/`"1"`/`1` → `true`; `"false"`/`"0"`/`0` → `false`. Anything else throws.
199
+ - `array` / `object`: JSON strings are parsed. Non-JSON throws.
200
+ - `string`: anything else is `String(value)`-coerced.
201
+ 3. **Enum check** — if `enum` is set, the (coerced) value must be `===` one of the allowed entries. Errors quote both the allowed list and the actual value.
202
+
203
+ This eliminates the per-flow `if (!$.input.x) throw ...` boilerplate. Errors look like:
204
+
205
+ ```
206
+ Input "tier" must be a number, got string ("lots")
207
+ Input "stage" must be one of ["pre_seed","seed","series_a"], got "ipo"
208
+ ```
209
+
210
+ The same checks run when a step calls a sub-flow, so type/enum guarantees hold across the call boundary.
211
+
192
212
  ## Selector Syntax
193
213
 
194
214
  | Pattern | Resolves To |