@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-
|
|
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
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
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)" },
|
package/dist/index.js
CHANGED
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
loadFlowWithMeta,
|
|
17
17
|
resolveFlowPath,
|
|
18
18
|
saveFlow
|
|
19
|
-
} from "./chunk-
|
|
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
|
@@ -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 |
|