@withone/cli 2.12.0 → 2.14.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.
- package/README.md +8 -2
- package/dist/{chunk-EITBMRUB.js → chunk-2OIJGF77.js} +1 -1
- package/dist/{chunk-6IRGRC3G.js → chunk-4H5L3SBY.js} +2 -2
- package/dist/{chunk-J7JWBH45.js → chunk-B5YDLVY5.js} +2 -2
- package/dist/{chunk-7ZBRP3BJ.js → chunk-IM3OA7CU.js} +305 -82
- package/dist/{chunk-WZTPSXHP.js → chunk-LEJKEGB6.js} +2 -2
- package/dist/{chunk-DGQOAFXJ.js → chunk-TSMVFVQ3.js} +2 -2
- package/dist/{chunk-EWHFGWB3.js → chunk-XUAUDMQE.js} +39 -0
- package/dist/{embedding-7Q22D23K.js → embedding-YIXO45HW.js} +2 -2
- package/dist/{flow-runner-6QTY3U2C.js → flow-runner-SRO6YL62.js} +2 -2
- package/dist/index.js +124 -42
- package/dist/{migrate-KKNH3745.js → migrate-RJ7F2LPT.js} +5 -5
- package/dist/{runtime-WB4J6RQK.js → runtime-2OVXKNJS.js} +3 -3
- package/dist/{schema-SDZZ7XZT.js → schema-FIIT33OD.js} +4 -4
- package/dist/sql-KRQUWOPT.js +13 -0
- package/package.json +1 -1
- package/skills/one/references/flows.md +14 -13
- package/dist/sql-BNSA2GZM.js +0 -13
|
@@ -29,7 +29,7 @@ import {
|
|
|
29
29
|
writeAnalyticsQueue,
|
|
30
30
|
writeUsageLog,
|
|
31
31
|
writeUsageState
|
|
32
|
-
} from "./chunk-
|
|
32
|
+
} from "./chunk-XUAUDMQE.js";
|
|
33
33
|
|
|
34
34
|
// src/lib/flow-runner.ts
|
|
35
35
|
import fs5 from "fs";
|
|
@@ -40,7 +40,7 @@ import crypto from "crypto";
|
|
|
40
40
|
import fs4 from "fs";
|
|
41
41
|
import os2 from "os";
|
|
42
42
|
import path5 from "path";
|
|
43
|
-
import { exec, spawn } from "child_process";
|
|
43
|
+
import { exec, spawn as spawn2 } from "child_process";
|
|
44
44
|
import { promisify } from "util";
|
|
45
45
|
|
|
46
46
|
// src/lib/install-context.ts
|
|
@@ -1506,8 +1506,8 @@ var FLOW_SCHEMA = {
|
|
|
1506
1506
|
configKey: "code",
|
|
1507
1507
|
description: "JS code \u2014 inline source or an external .mjs module under the flow's lib/ folder",
|
|
1508
1508
|
fields: {
|
|
1509
|
-
source: { type: "string", required: false, description: 'Inline JS function body (flow context as $, supports await). Mutually exclusive with "module".' },
|
|
1510
|
-
module: { type: "string", required: false, description: 'Relative path to a .mjs file under the flow folder (e.g. "lib/normalize.mjs"). Reads $ from stdin as JSON, writes result to stdout as JSON. Mutually exclusive with "source".' }
|
|
1509
|
+
source: { type: "string", required: false, description: 'Inline JS function body (flow context as $, supports await). Runs in the sandbox: no files, programs, network or timers. Mutually exclusive with "module".' },
|
|
1510
|
+
module: { type: "string", required: false, description: 'Relative path to a .mjs file under the flow folder (e.g. "lib/normalize.mjs"). Reads $ from stdin as JSON, writes result to stdout as JSON. Mutually exclusive with "source". Runs with full Node access and requires --allow-code.' }
|
|
1511
1511
|
},
|
|
1512
1512
|
example: {
|
|
1513
1513
|
id: "processData",
|
|
@@ -1735,7 +1735,7 @@ function generateFlowGuide() {
|
|
|
1735
1735
|
Workflows live in \`.one/flows/\` (relative to your current working directory \u2014 the CLI does NOT walk up parent directories or fall back to a global location) and chain actions across platforms. Two layouts are supported:
|
|
1736
1736
|
|
|
1737
1737
|
- **Folder layout (REQUIRED for new flows)** \u2014 \`.one/flows/<key>/flow.json\`, with an optional \`lib/\` subfolder for JavaScript modules. This is like a skill: the folder groups the JSON spec with any JavaScript modules it needs, so the whole flow is shareable. **Always create new flows in this layout.**
|
|
1738
|
-
- **Single-file layout (DEPRECATED)** \u2014 \`.one/flows/<key>.flow.json\`. Still loads and runs for backward compatibility, but is deprecated. Do not create new flows in this layout. When editing an existing single-file flow, migrate it to the folder layout: move \`<key>.flow.json\` to \`<key>/flow.json
|
|
1738
|
+
- **Single-file layout (DEPRECATED)** \u2014 \`.one/flows/<key>.flow.json\`. Still loads and runs for backward compatibility, but is deprecated. Do not create new flows in this layout. When editing an existing single-file flow, migrate it to the folder layout: move \`<key>.flow.json\` to \`<key>/flow.json\`. Move a \`code.source\` block into a \`<key>/lib/*.mjs\` module only if it needs files, network or timers: modules run with full Node access and make the flow require \`--allow-code\`.
|
|
1739
1739
|
|
|
1740
1740
|
**Subdirectory groups** \u2014 Flows can be organized into subdirectories: \`.one/flows/<group>/<key>/flow.json\`. For example:
|
|
1741
1741
|
\`\`\`
|
|
@@ -1755,16 +1755,17 @@ When resolving a flow by key, the CLI checks the folder layout first, then the d
|
|
|
1755
1755
|
**Agents: always inspect a flow before running it.** Nothing about a flow's runtime requirements is guessable from its name. Before \`flow execute\`, do one of these:
|
|
1756
1756
|
|
|
1757
1757
|
1. Run \`one --agent flow list\` \u2014 the JSON output includes \`requiresBash\`, \`usesCodeModules\`, \`inputs\` (with \`autoResolvable\` flags), \`stepTypes\`, and the flow's \`description\`. This is the fastest path.
|
|
1758
|
-
2. Read the flow's \`description\` field directly from the JSON. Flow authors are required (see "Author conventions" below) to state any \`--allow-bash\` requirement and any non-auto-resolving inputs in the description.
|
|
1758
|
+
2. Read the flow's \`description\` field directly from the JSON. Flow authors are required (see "Author conventions" below) to state any \`--allow-bash\` or \`--allow-code\` requirement and any non-auto-resolving inputs in the description.
|
|
1759
1759
|
3. Run \`one --agent flow execute <key> --dry-run\` to see the resolved inputs and step plan without side effects.
|
|
1760
1760
|
|
|
1761
|
-
If you skip this step you will hit errors like *"Workflow X contains bash steps. Re-run with --allow-bash."* \u2014 the CLI now pre-flights and fails fast, so you won't waste a long run, but the error is still avoidable by reading first.
|
|
1761
|
+
If you skip this step you will hit errors like *"Workflow X contains bash steps. Re-run with --allow-bash."* (or the same for \`code.module\` steps and \`--allow-code\`) \u2014 the CLI now pre-flights and fails fast, so you won't waste a long run, but the error is still avoidable by reading first.
|
|
1762
1762
|
|
|
1763
1763
|
## Author conventions \u2014 WRITE flows that are safe to execute blind
|
|
1764
1764
|
|
|
1765
1765
|
When you create a flow, its \`description\` field is the contract with future executors (human or agent). It MUST state:
|
|
1766
1766
|
|
|
1767
1767
|
- **\`--allow-bash\` if any step is type \`bash\`.** Example: *"Fetches recent Gmail threads and summarizes them with Claude Haiku. Requires \`--allow-bash\`."*
|
|
1768
|
+
- **\`--allow-code\` if any code step uses \`code.module\`.** Modules run with full Node access, so they are gated like bash.
|
|
1768
1769
|
- **Every input that does NOT have a \`connection\` hint.** Connection inputs auto-resolve when exactly one matching connection exists; everything else must be passed via \`-i name=value\` and the description must name it.
|
|
1769
1770
|
- **Any files/directories the flow writes to** so operators know what will be modified on disk.
|
|
1770
1771
|
|
|
@@ -1782,6 +1783,7 @@ one --agent flow execute <key> -i name=value # Execute (bare
|
|
|
1782
1783
|
one --agent flow execute <group/key> -i name=value # Execute (namespaced key)
|
|
1783
1784
|
one --agent flow execute <key> --dry-run --mock # Test with mock data
|
|
1784
1785
|
one --agent flow execute <key> --allow-bash # Enable bash steps
|
|
1786
|
+
one --agent flow execute <key> --allow-code # Enable code.module steps (full Node)
|
|
1785
1787
|
one --agent flow runs [flowKey] # List past runs
|
|
1786
1788
|
one --agent flow resume <runId> # Resume failed run
|
|
1787
1789
|
one --agent flow scaffold [template] # Generate a starter template
|
|
@@ -1828,7 +1830,7 @@ If you're editing an existing \`.one/flows/<key>.flow.json\`, migrate it \u2014
|
|
|
1828
1830
|
|
|
1829
1831
|
1. \`mkdir -p .one/flows/<key>/lib\`
|
|
1830
1832
|
2. Move the file: \`mv .one/flows/<key>.flow.json .one/flows/<key>/flow.json\`
|
|
1831
|
-
3.
|
|
1833
|
+
3. Leave inline \`code.source\` in place unless it needs files, network or timers; it runs in the sandbox and needs no flag. If it does need them, extract it into \`lib/<step-id>.mjs\` (see translation pattern below) and swap the step config to \`{ "module": "lib/<step-id>.mjs" }\`. That gives it full Node access and makes the flow require \`--allow-code\`.
|
|
1832
1834
|
4. Validate: \`one --agent flow validate <key>\`.
|
|
1833
1835
|
5. Run it and confirm behavior is unchanged.
|
|
1834
1836
|
|
|
@@ -2196,13 +2198,272 @@ Set timeout to at least 180000ms (3 min). Run Claude-heavy flows sequentially, n
|
|
|
2196
2198
|
|
|
2197
2199
|
- Connection keys are **inputs**, not hardcoded
|
|
2198
2200
|
- Action IDs in examples are placeholders \u2014 always use \`actions find\`
|
|
2199
|
-
-
|
|
2200
|
-
-
|
|
2201
|
+
- Transforms, conditions, \`if\`/\`unless\` and inline \`code.source\` run in a sandbox (separate process, no files, programs, network or environment of its own). \`code.source\` allows \`require('crypto' | 'buffer' | 'url' | 'path')\` \u2014 \`fs\`, \`http\`, \`child_process\` are blocked. \`$\` is a copy: return a value, don't mutate \`$\`
|
|
2202
|
+
- When a step needs files, network or timers, use \`code.module\` to point at a \`.mjs\` file in the flow's \`lib/\` folder \u2014 runs as a child \`node\` process with full Node APIs, reads \`$\` from stdin, writes JSON to stdout. Requires \`--allow-code\`
|
|
2201
2203
|
- Bash steps require \`--allow-bash\` flag
|
|
2202
2204
|
- State is persisted after every step \u2014 resume picks up where it left off`);
|
|
2203
2205
|
return sections.join("\n");
|
|
2204
2206
|
}
|
|
2205
2207
|
|
|
2208
|
+
// src/lib/flow-sandbox.ts
|
|
2209
|
+
import { spawn } from "child_process";
|
|
2210
|
+
var syncTimeoutMs = 3e4;
|
|
2211
|
+
var callTimeoutMs = 6e4;
|
|
2212
|
+
var SANDBOX_NODE_FLAGS = ["--permission", "--disallow-code-generation-from-strings"];
|
|
2213
|
+
function sandboxEnv() {
|
|
2214
|
+
if (process.platform === "win32" && process.env.SystemRoot) return { SystemRoot: process.env.SystemRoot };
|
|
2215
|
+
return {};
|
|
2216
|
+
}
|
|
2217
|
+
var BOOTSTRAP = String.raw`
|
|
2218
|
+
'use strict';
|
|
2219
|
+
const vm = require('node:vm');
|
|
2220
|
+
const proc = process;
|
|
2221
|
+
const MODULES = {
|
|
2222
|
+
buffer: require('node:buffer'),
|
|
2223
|
+
crypto: require('node:crypto'),
|
|
2224
|
+
url: require('node:url'),
|
|
2225
|
+
path: require('node:path'),
|
|
2226
|
+
};
|
|
2227
|
+
const BLOCKED = new Set(['fs', 'http', 'https', 'net', 'child_process', 'process', 'os',
|
|
2228
|
+
'cluster', 'dgram', 'tls', 'vm', 'worker_threads']);
|
|
2229
|
+
const sandboxRequire = async (name) => {
|
|
2230
|
+
const clean = String(name).replace(/^node:/, '');
|
|
2231
|
+
if (BLOCKED.has(clean)) throw new Error('Module "' + name + '" is blocked in code steps');
|
|
2232
|
+
if (!Object.prototype.hasOwnProperty.call(MODULES, clean)) {
|
|
2233
|
+
throw new Error('Module "' + name + '" not available. Allowed: ' + Object.keys(MODULES).join(', '));
|
|
2234
|
+
}
|
|
2235
|
+
return MODULES[clean];
|
|
2236
|
+
};
|
|
2237
|
+
const SYNC_TIMEOUT = Number(proc.argv[proc.argv.length - 1]) || 30000;
|
|
2238
|
+
const send = proc.send.bind(proc);
|
|
2239
|
+
|
|
2240
|
+
for (const k of ['process', 'require', 'module', 'exports', '__filename', '__dirname',
|
|
2241
|
+
'fetch', 'WebSocket', 'EventSource', 'Request', 'Response', 'Headers', 'FormData',
|
|
2242
|
+
'setTimeout', 'setInterval', 'setImmediate', 'clearTimeout', 'clearInterval',
|
|
2243
|
+
'clearImmediate', 'navigator', 'global']) {
|
|
2244
|
+
try { delete globalThis[k]; } catch (_) { /* non-configurable: leave it */ }
|
|
2245
|
+
}
|
|
2246
|
+
|
|
2247
|
+
const describe = (err) => {
|
|
2248
|
+
if (err && typeof err === 'object') {
|
|
2249
|
+
return { name: String(err.name || 'Error'), message: String(err.message), stack: String(err.stack || '') };
|
|
2250
|
+
}
|
|
2251
|
+
return { name: 'Error', message: String(err), stack: '' };
|
|
2252
|
+
};
|
|
2253
|
+
|
|
2254
|
+
const reply = (id, value) => {
|
|
2255
|
+
try {
|
|
2256
|
+
send({ id, ok: true, value });
|
|
2257
|
+
} catch (err) {
|
|
2258
|
+
send({ id, ok: false, error: { name: 'Error', stack: '', message:
|
|
2259
|
+
'returned a value that cannot be copied out of the sandbox (functions, symbols and class instances with private state cannot be passed between steps): ' + (err && err.message) } });
|
|
2260
|
+
}
|
|
2261
|
+
};
|
|
2262
|
+
|
|
2263
|
+
// The sandbox's copy of $. The host sends only the parts that changed since
|
|
2264
|
+
// the last call (see syncFor in flow-sandbox.ts), so a loop over a large
|
|
2265
|
+
// step output doesn't copy that output again on every iteration.
|
|
2266
|
+
let store = { input: {}, env: {}, steps: {}, loop: {} };
|
|
2267
|
+
const applySync = (sync) => {
|
|
2268
|
+
if (sync.reset) store = { input: {}, env: {}, steps: {}, loop: {} };
|
|
2269
|
+
if ('input' in sync) store.input = sync.input;
|
|
2270
|
+
if ('env' in sync) store.env = sync.env;
|
|
2271
|
+
if ('loop' in sync) store.loop = sync.loop;
|
|
2272
|
+
if (sync.steps) for (const k of Object.keys(sync.steps)) store.steps[k] = sync.steps[k];
|
|
2273
|
+
if (sync.drop) for (const k of sync.drop) delete store.steps[k];
|
|
2274
|
+
};
|
|
2275
|
+
|
|
2276
|
+
proc.on('message', async (msg) => {
|
|
2277
|
+
const { id, kind, code, sync, filename } = msg;
|
|
2278
|
+
try {
|
|
2279
|
+
applySync(sync);
|
|
2280
|
+
globalThis.__one_ctx = store;
|
|
2281
|
+
globalThis.__one_require = sandboxRequire;
|
|
2282
|
+
let value;
|
|
2283
|
+
if (kind === 'expr') {
|
|
2284
|
+
const script = new vm.Script('(function ($) { return (' + code + '\n) })(__one_ctx)', { filename: filename || 'expression' });
|
|
2285
|
+
value = script.runInThisContext({ timeout: SYNC_TIMEOUT });
|
|
2286
|
+
} else {
|
|
2287
|
+
// Same wrapper shape as new AsyncFunction('$', 'require', body), so
|
|
2288
|
+
// stack line numbers are offset by exactly 2, as the engine expects.
|
|
2289
|
+
const script = new vm.Script('(async function anonymous($,require\n) {\n' + code + '\n})(__one_ctx, __one_require)', { filename });
|
|
2290
|
+
value = await script.runInThisContext({ timeout: SYNC_TIMEOUT });
|
|
2291
|
+
}
|
|
2292
|
+
reply(id, value);
|
|
2293
|
+
} catch (err) {
|
|
2294
|
+
send({ id, ok: false, error: describe(err) });
|
|
2295
|
+
} finally {
|
|
2296
|
+
delete globalThis.__one_ctx;
|
|
2297
|
+
delete globalThis.__one_require;
|
|
2298
|
+
}
|
|
2299
|
+
});
|
|
2300
|
+
send({ ready: true });
|
|
2301
|
+
`;
|
|
2302
|
+
var child = null;
|
|
2303
|
+
var ready = null;
|
|
2304
|
+
var nextId = 1;
|
|
2305
|
+
var pending2 = /* @__PURE__ */ new Map();
|
|
2306
|
+
var ERROR_CTORS = {
|
|
2307
|
+
TypeError,
|
|
2308
|
+
SyntaxError,
|
|
2309
|
+
ReferenceError,
|
|
2310
|
+
RangeError,
|
|
2311
|
+
EvalError,
|
|
2312
|
+
URIError
|
|
2313
|
+
};
|
|
2314
|
+
function rebuildError(e) {
|
|
2315
|
+
const Ctor = ERROR_CTORS[e.name] ?? Error;
|
|
2316
|
+
const err = new Ctor(e.message);
|
|
2317
|
+
if (!ERROR_CTORS[e.name] && e.name && e.name !== "Error") err.name = e.name;
|
|
2318
|
+
if (e.stack) err.stack = e.stack;
|
|
2319
|
+
return err;
|
|
2320
|
+
}
|
|
2321
|
+
function setRef(on) {
|
|
2322
|
+
if (!child) return;
|
|
2323
|
+
const channel = child.channel;
|
|
2324
|
+
if (on) {
|
|
2325
|
+
child.ref();
|
|
2326
|
+
channel?.ref();
|
|
2327
|
+
} else {
|
|
2328
|
+
child.unref();
|
|
2329
|
+
channel?.unref();
|
|
2330
|
+
}
|
|
2331
|
+
}
|
|
2332
|
+
function failAll(proc, err) {
|
|
2333
|
+
for (const [id, p] of pending2) {
|
|
2334
|
+
if (p.proc !== proc) continue;
|
|
2335
|
+
clearTimeout(p.timer);
|
|
2336
|
+
pending2.delete(id);
|
|
2337
|
+
p.reject(err);
|
|
2338
|
+
}
|
|
2339
|
+
}
|
|
2340
|
+
function start() {
|
|
2341
|
+
if (ready) return ready;
|
|
2342
|
+
const proc = spawn(
|
|
2343
|
+
process.execPath,
|
|
2344
|
+
[...SANDBOX_NODE_FLAGS, "-e", BOOTSTRAP, "--", String(syncTimeoutMs)],
|
|
2345
|
+
{
|
|
2346
|
+
stdio: ["ignore", "inherit", "inherit", "ipc"],
|
|
2347
|
+
serialization: "advanced",
|
|
2348
|
+
env: sandboxEnv(),
|
|
2349
|
+
windowsHide: true
|
|
2350
|
+
}
|
|
2351
|
+
);
|
|
2352
|
+
child = proc;
|
|
2353
|
+
ready = new Promise((resolve2, reject) => {
|
|
2354
|
+
const onReady = (msg) => {
|
|
2355
|
+
if (msg && typeof msg === "object" && msg.ready) {
|
|
2356
|
+
proc.off("message", onReady);
|
|
2357
|
+
resolve2();
|
|
2358
|
+
}
|
|
2359
|
+
};
|
|
2360
|
+
proc.on("message", onReady);
|
|
2361
|
+
proc.once("error", (err) => reject(new Error(`Could not start the flow code sandbox: ${err.message}`)));
|
|
2362
|
+
proc.once("exit", (code, signal) => {
|
|
2363
|
+
reject(new Error(`Flow code sandbox exited before it was ready (code ${code}, signal ${signal})`));
|
|
2364
|
+
});
|
|
2365
|
+
});
|
|
2366
|
+
proc.on("message", (msg) => {
|
|
2367
|
+
if (typeof msg?.id !== "number") return;
|
|
2368
|
+
const p = pending2.get(msg.id);
|
|
2369
|
+
if (!p) return;
|
|
2370
|
+
clearTimeout(p.timer);
|
|
2371
|
+
pending2.delete(msg.id);
|
|
2372
|
+
if (pending2.size === 0) setRef(false);
|
|
2373
|
+
if (msg.ok) p.resolve(msg.value);
|
|
2374
|
+
else p.reject(rebuildError(msg.error ?? { name: "Error", message: "unknown sandbox error", stack: "" }));
|
|
2375
|
+
});
|
|
2376
|
+
proc.on("exit", (code, signal) => {
|
|
2377
|
+
if (child === proc) {
|
|
2378
|
+
child = null;
|
|
2379
|
+
ready = null;
|
|
2380
|
+
}
|
|
2381
|
+
failAll(proc, new Error(`Flow code sandbox stopped (code ${code}, signal ${signal})`));
|
|
2382
|
+
});
|
|
2383
|
+
proc.on("error", (err) => {
|
|
2384
|
+
if (child === proc) {
|
|
2385
|
+
child = null;
|
|
2386
|
+
ready = null;
|
|
2387
|
+
}
|
|
2388
|
+
failAll(proc, err);
|
|
2389
|
+
});
|
|
2390
|
+
return ready;
|
|
2391
|
+
}
|
|
2392
|
+
var synced = null;
|
|
2393
|
+
function syncFor(proc, context) {
|
|
2394
|
+
const prev = synced && synced.proc === proc && synced.context === context && synced.stepsObj === context.steps ? synced : null;
|
|
2395
|
+
const sync = {};
|
|
2396
|
+
if (!prev) sync.reset = true;
|
|
2397
|
+
if (!prev || prev.input !== context.input) sync.input = context.input;
|
|
2398
|
+
if (!prev || prev.env !== context.env) sync.env = context.env;
|
|
2399
|
+
if (!prev || prev.loop !== context.loop) sync.loop = context.loop;
|
|
2400
|
+
const stepUpdates = {};
|
|
2401
|
+
let anyStep = false;
|
|
2402
|
+
const nextSteps = /* @__PURE__ */ new Map();
|
|
2403
|
+
for (const [k, v] of Object.entries(context.steps)) {
|
|
2404
|
+
nextSteps.set(k, v);
|
|
2405
|
+
if (!prev || prev.steps.get(k) !== v) {
|
|
2406
|
+
stepUpdates[k] = v;
|
|
2407
|
+
anyStep = true;
|
|
2408
|
+
}
|
|
2409
|
+
}
|
|
2410
|
+
if (anyStep) sync.steps = stepUpdates;
|
|
2411
|
+
if (prev) {
|
|
2412
|
+
const drop = [...prev.steps.keys()].filter((k) => !nextSteps.has(k));
|
|
2413
|
+
if (drop.length) sync.drop = drop;
|
|
2414
|
+
}
|
|
2415
|
+
const commit = () => {
|
|
2416
|
+
synced = {
|
|
2417
|
+
proc,
|
|
2418
|
+
context,
|
|
2419
|
+
stepsObj: context.steps,
|
|
2420
|
+
input: context.input,
|
|
2421
|
+
env: context.env,
|
|
2422
|
+
loop: context.loop,
|
|
2423
|
+
steps: nextSteps
|
|
2424
|
+
};
|
|
2425
|
+
};
|
|
2426
|
+
return { sync, commit };
|
|
2427
|
+
}
|
|
2428
|
+
async function call(kind, code, context, filename) {
|
|
2429
|
+
await start();
|
|
2430
|
+
const proc = child;
|
|
2431
|
+
const id = nextId++;
|
|
2432
|
+
return new Promise((resolve2, reject) => {
|
|
2433
|
+
const timer = setTimeout(() => {
|
|
2434
|
+
pending2.delete(id);
|
|
2435
|
+
reject(new Error(`timed out after ${callTimeoutMs / 1e3}s`));
|
|
2436
|
+
if (child === proc) {
|
|
2437
|
+
child = null;
|
|
2438
|
+
ready = null;
|
|
2439
|
+
}
|
|
2440
|
+
proc.kill();
|
|
2441
|
+
}, callTimeoutMs);
|
|
2442
|
+
timer.unref();
|
|
2443
|
+
pending2.set(id, { resolve: resolve2, reject, timer, proc });
|
|
2444
|
+
setRef(true);
|
|
2445
|
+
const { sync, commit } = syncFor(proc, context);
|
|
2446
|
+
try {
|
|
2447
|
+
proc.send({ id, kind, code, sync, filename });
|
|
2448
|
+
commit();
|
|
2449
|
+
} catch (err) {
|
|
2450
|
+
synced = null;
|
|
2451
|
+
clearTimeout(timer);
|
|
2452
|
+
pending2.delete(id);
|
|
2453
|
+
if (pending2.size === 0) setRef(false);
|
|
2454
|
+
reject(new Error(
|
|
2455
|
+
`the flow context contains a value that cannot be passed to the sandbox: ${err.message}`
|
|
2456
|
+
));
|
|
2457
|
+
}
|
|
2458
|
+
});
|
|
2459
|
+
}
|
|
2460
|
+
function sandboxEvaluate(expr, context) {
|
|
2461
|
+
return call("expr", expr, context, "expression");
|
|
2462
|
+
}
|
|
2463
|
+
function sandboxRunCode(source, context, filename) {
|
|
2464
|
+
return call("code", source, context, filename);
|
|
2465
|
+
}
|
|
2466
|
+
|
|
2206
2467
|
// src/lib/flow-engine.ts
|
|
2207
2468
|
var execAsync = promisify(exec);
|
|
2208
2469
|
function sleep2(ms) {
|
|
@@ -2456,13 +2717,12 @@ function resolveValue(value, context) {
|
|
|
2456
2717
|
}
|
|
2457
2718
|
return value;
|
|
2458
2719
|
}
|
|
2459
|
-
function evaluateExpression(expr, context) {
|
|
2460
|
-
|
|
2461
|
-
return fn(context);
|
|
2720
|
+
async function evaluateExpression(expr, context) {
|
|
2721
|
+
return sandboxEvaluate(expr, context);
|
|
2462
2722
|
}
|
|
2463
|
-
function evaluateCondition(expr, context) {
|
|
2723
|
+
async function evaluateCondition(expr, context) {
|
|
2464
2724
|
try {
|
|
2465
|
-
return Boolean(evaluateExpression(expr, context));
|
|
2725
|
+
return Boolean(await evaluateExpression(expr, context));
|
|
2466
2726
|
} catch (err) {
|
|
2467
2727
|
if (err instanceof TypeError) return false;
|
|
2468
2728
|
throw err;
|
|
@@ -2538,13 +2798,13 @@ function stepInterpolationSource(step) {
|
|
|
2538
2798
|
return {};
|
|
2539
2799
|
}
|
|
2540
2800
|
}
|
|
2541
|
-
function dryResolveStep(step, context) {
|
|
2801
|
+
async function dryResolveStep(step, context) {
|
|
2542
2802
|
const base = { stepId: step.id, name: step.name, type: step.type };
|
|
2543
2803
|
const expr = step.type === "transform" ? step.transform?.expression : step.type === "condition" ? step.condition?.expression : step.type === "while" ? step.while?.condition : void 0;
|
|
2544
2804
|
if (step.type === "transform" || step.type === "condition" || step.type === "while") {
|
|
2545
2805
|
if (!expr) return { ...base, references: [] };
|
|
2546
2806
|
try {
|
|
2547
|
-
return { ...base, references: [], resolved: evaluateExpression(expr, context) };
|
|
2807
|
+
return { ...base, references: [], resolved: await evaluateExpression(expr, context) };
|
|
2548
2808
|
} catch (err) {
|
|
2549
2809
|
if (err instanceof TypeError) {
|
|
2550
2810
|
const deps = expressionDeferredOn(expr, context);
|
|
@@ -2577,57 +2837,23 @@ function dryResolveStep(step, context) {
|
|
|
2577
2837
|
}
|
|
2578
2838
|
return { ...base, references, resolved };
|
|
2579
2839
|
}
|
|
2580
|
-
function dryResolveAllSteps(steps, context) {
|
|
2840
|
+
async function dryResolveAllSteps(steps, context) {
|
|
2581
2841
|
const out = [];
|
|
2582
|
-
const visit = (list) => {
|
|
2842
|
+
const visit = async (list) => {
|
|
2583
2843
|
for (const step of list) {
|
|
2584
|
-
out.push(dryResolveStep(step, context));
|
|
2844
|
+
out.push(await dryResolveStep(step, context));
|
|
2585
2845
|
const config = step;
|
|
2586
2846
|
for (const { configKey, fieldName } of getNestedStepsKeys()) {
|
|
2587
2847
|
const block = config[configKey];
|
|
2588
2848
|
if (block && Array.isArray(block[fieldName])) {
|
|
2589
|
-
visit(block[fieldName]);
|
|
2849
|
+
await visit(block[fieldName]);
|
|
2590
2850
|
}
|
|
2591
2851
|
}
|
|
2592
2852
|
}
|
|
2593
2853
|
};
|
|
2594
|
-
visit(steps);
|
|
2854
|
+
await visit(steps);
|
|
2595
2855
|
return out;
|
|
2596
2856
|
}
|
|
2597
|
-
var ALLOWED_MODULES = {
|
|
2598
|
-
buffer: () => import("buffer"),
|
|
2599
|
-
crypto: () => import("crypto"),
|
|
2600
|
-
url: () => import("url"),
|
|
2601
|
-
path: () => import("path")
|
|
2602
|
-
};
|
|
2603
|
-
var BLOCKED_MODULES = /* @__PURE__ */ new Set([
|
|
2604
|
-
"fs",
|
|
2605
|
-
"http",
|
|
2606
|
-
"https",
|
|
2607
|
-
"net",
|
|
2608
|
-
"child_process",
|
|
2609
|
-
"process",
|
|
2610
|
-
"os",
|
|
2611
|
-
"cluster",
|
|
2612
|
-
"dgram",
|
|
2613
|
-
"tls",
|
|
2614
|
-
"vm",
|
|
2615
|
-
"worker_threads"
|
|
2616
|
-
]);
|
|
2617
|
-
function createSandboxedRequire() {
|
|
2618
|
-
const cache = {};
|
|
2619
|
-
return async (moduleName) => {
|
|
2620
|
-
const clean = moduleName.replace(/^node:/, "");
|
|
2621
|
-
if (BLOCKED_MODULES.has(clean)) {
|
|
2622
|
-
throw new Error(`Module "${moduleName}" is blocked in code steps`);
|
|
2623
|
-
}
|
|
2624
|
-
if (!ALLOWED_MODULES[clean]) {
|
|
2625
|
-
throw new Error(`Module "${moduleName}" not available. Allowed: ${Object.keys(ALLOWED_MODULES).join(", ")}`);
|
|
2626
|
-
}
|
|
2627
|
-
if (!cache[clean]) cache[clean] = await ALLOWED_MODULES[clean]();
|
|
2628
|
-
return cache[clean];
|
|
2629
|
-
};
|
|
2630
|
-
}
|
|
2631
2857
|
function stripCodeFences(text) {
|
|
2632
2858
|
const trimmed = text.trim();
|
|
2633
2859
|
const match = trimmed.match(/^```(?:\w*)\s*\n([\s\S]*?)\n\s*```\s*$/);
|
|
@@ -2637,9 +2863,9 @@ function stripToJson(text) {
|
|
|
2637
2863
|
const s = stripCodeFences(text);
|
|
2638
2864
|
const candidates = [s.indexOf("{"), s.indexOf("[")].filter((i) => i >= 0);
|
|
2639
2865
|
if (candidates.length === 0) return s;
|
|
2640
|
-
const
|
|
2866
|
+
const start2 = Math.min(...candidates);
|
|
2641
2867
|
const end = Math.max(s.lastIndexOf("}"), s.lastIndexOf("]"));
|
|
2642
|
-
return end >
|
|
2868
|
+
return end > start2 ? s.slice(start2, end + 1) : s;
|
|
2643
2869
|
}
|
|
2644
2870
|
function unwrapClaudeEnvelope(stdout, stepId) {
|
|
2645
2871
|
const trimmed = stdout.trim();
|
|
@@ -2727,8 +2953,8 @@ async function executeActionStep(step, context, api, permissions, allowedActionI
|
|
|
2727
2953
|
output: result.responseData
|
|
2728
2954
|
};
|
|
2729
2955
|
}
|
|
2730
|
-
function executeTransformStep(step, context) {
|
|
2731
|
-
const output = evaluateExpression(step.transform.expression, context);
|
|
2956
|
+
async function executeTransformStep(step, context) {
|
|
2957
|
+
const output = await evaluateExpression(step.transform.expression, context);
|
|
2732
2958
|
return { status: "success", output, response: output };
|
|
2733
2959
|
}
|
|
2734
2960
|
async function executeCodeStep(step, context, options) {
|
|
@@ -2740,15 +2966,9 @@ async function executeCodeStep(step, context, options) {
|
|
|
2740
2966
|
if (typeof config.source !== "string") {
|
|
2741
2967
|
throw new Error(`Code step "${step.id}" must define either "source" or "module"`);
|
|
2742
2968
|
}
|
|
2743
|
-
const AsyncFunction = Object.getPrototypeOf(async function() {
|
|
2744
|
-
}).constructor;
|
|
2745
|
-
const sandboxedRequire = createSandboxedRequire();
|
|
2746
2969
|
const sourceURL = `code:${step.id}`;
|
|
2747
|
-
const taggedSource = `${config.source}
|
|
2748
|
-
//# sourceURL=${sourceURL}`;
|
|
2749
|
-
const fn = new AsyncFunction("$", "require", taggedSource);
|
|
2750
2970
|
try {
|
|
2751
|
-
const output = await
|
|
2971
|
+
const output = await sandboxRunCode(config.source, context, sourceURL);
|
|
2752
2972
|
return { status: "success", output, response: output };
|
|
2753
2973
|
} catch (err) {
|
|
2754
2974
|
throw rewriteCodeStepError(err, step.id, config.source, sourceURL);
|
|
@@ -2780,6 +3000,9 @@ function rewriteCodeStepError(err, stepId, source, sourceURL) {
|
|
|
2780
3000
|
return err;
|
|
2781
3001
|
}
|
|
2782
3002
|
async function executeCodeModule(stepId, modulePath, context, options) {
|
|
3003
|
+
if (!options.allowCode) {
|
|
3004
|
+
throw new Error(`Code step "${stepId}" runs module "${modulePath}" with full Node access and requires the --allow-code flag`);
|
|
3005
|
+
}
|
|
2783
3006
|
const rootDir = options.rootDir;
|
|
2784
3007
|
if (!rootDir) {
|
|
2785
3008
|
throw new Error(`Code step "${stepId}" uses module "${modulePath}" but no flow rootDir is available. Flows that use code modules must be loaded via loadFlowWithMeta.`);
|
|
@@ -2799,16 +3022,16 @@ async function executeCodeModule(stepId, modulePath, context, options) {
|
|
|
2799
3022
|
void _omitEnv;
|
|
2800
3023
|
const stdinPayload = JSON.stringify(safeContext);
|
|
2801
3024
|
return await new Promise((resolve2, reject) => {
|
|
2802
|
-
const
|
|
3025
|
+
const child2 = spawn2(process.execPath, [absPath], {
|
|
2803
3026
|
cwd: rootDir,
|
|
2804
3027
|
stdio: ["pipe", "pipe", "pipe"]
|
|
2805
3028
|
});
|
|
2806
3029
|
const stdoutChunks = [];
|
|
2807
3030
|
const stderrChunks = [];
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
3031
|
+
child2.stdout.on("data", (c) => stdoutChunks.push(c));
|
|
3032
|
+
child2.stderr.on("data", (c) => stderrChunks.push(c));
|
|
3033
|
+
child2.on("error", (err) => reject(err));
|
|
3034
|
+
child2.on("close", (code) => {
|
|
2812
3035
|
const stdout = Buffer.concat(stdoutChunks).toString("utf-8");
|
|
2813
3036
|
const stderr = Buffer.concat(stderrChunks).toString("utf-8");
|
|
2814
3037
|
if (code !== 0) {
|
|
@@ -2826,13 +3049,13 @@ async function executeCodeModule(stepId, modulePath, context, options) {
|
|
|
2826
3049
|
reject(new Error(`Code module "${modulePath}" did not print valid JSON to stdout: ${err.message}`));
|
|
2827
3050
|
}
|
|
2828
3051
|
});
|
|
2829
|
-
|
|
2830
|
-
|
|
3052
|
+
child2.stdin.write(stdinPayload);
|
|
3053
|
+
child2.stdin.end();
|
|
2831
3054
|
});
|
|
2832
3055
|
}
|
|
2833
3056
|
async function executeConditionStep(step, context, api, permissions, allowedActionIds, options, flowStack) {
|
|
2834
3057
|
const condition = step.condition;
|
|
2835
|
-
const result = evaluateCondition(condition.expression, context);
|
|
3058
|
+
const result = await evaluateCondition(condition.expression, context);
|
|
2836
3059
|
const branch = result ? condition.then : condition.else || [];
|
|
2837
3060
|
const branchResults = await executeSteps(branch, context, api, permissions, allowedActionIds, options, void 0, flowStack);
|
|
2838
3061
|
return {
|
|
@@ -2975,7 +3198,7 @@ async function executeWhileStep(step, context, api, permissions, allowedActionId
|
|
|
2975
3198
|
};
|
|
2976
3199
|
for (let iteration = 0; iteration < maxIterations; iteration++) {
|
|
2977
3200
|
if (iteration > 0) {
|
|
2978
|
-
const conditionResult = evaluateCondition(config.condition, context);
|
|
3201
|
+
const conditionResult = await evaluateCondition(config.condition, context);
|
|
2979
3202
|
if (!conditionResult) break;
|
|
2980
3203
|
}
|
|
2981
3204
|
await executeSteps(config.steps, context, api, permissions, allowedActionIds, options, void 0, flowStack);
|
|
@@ -3000,7 +3223,7 @@ async function executeSubflowStep(step, context, api, permissions, allowedAction
|
|
|
3000
3223
|
if (flowStack.includes(resolvedKey)) {
|
|
3001
3224
|
throw new Error(`Circular flow detected: ${[...flowStack, resolvedKey].join(" \u2192 ")}`);
|
|
3002
3225
|
}
|
|
3003
|
-
const { loadFlowWithMeta: loadFlowWithMeta2 } = await import("./flow-runner-
|
|
3226
|
+
const { loadFlowWithMeta: loadFlowWithMeta2 } = await import("./flow-runner-SRO6YL62.js");
|
|
3004
3227
|
const { flow: subFlow, rootDir: subRootDir } = loadFlowWithMeta2(resolvedKey);
|
|
3005
3228
|
const subContext = await executeFlow(
|
|
3006
3229
|
subFlow,
|
|
@@ -3197,7 +3420,7 @@ function checkRequires(step, context) {
|
|
|
3197
3420
|
}
|
|
3198
3421
|
async function executeSingleStep(step, context, api, permissions, allowedActionIds, options, flowStack = []) {
|
|
3199
3422
|
if (step.if) {
|
|
3200
|
-
const condResult = evaluateCondition(step.if, context);
|
|
3423
|
+
const condResult = await evaluateCondition(step.if, context);
|
|
3201
3424
|
if (!condResult) {
|
|
3202
3425
|
const result = { status: "skipped" };
|
|
3203
3426
|
context.steps[step.id] = result;
|
|
@@ -3205,7 +3428,7 @@ async function executeSingleStep(step, context, api, permissions, allowedActionI
|
|
|
3205
3428
|
}
|
|
3206
3429
|
}
|
|
3207
3430
|
if (step.unless) {
|
|
3208
|
-
const condResult = evaluateCondition(step.unless, context);
|
|
3431
|
+
const condResult = await evaluateCondition(step.unless, context);
|
|
3209
3432
|
if (condResult) {
|
|
3210
3433
|
const result = { status: "skipped" };
|
|
3211
3434
|
context.steps[step.id] = result;
|
|
@@ -3269,7 +3492,7 @@ async function executeSingleStep(step, context, api, permissions, allowedActionI
|
|
|
3269
3492
|
case "action":
|
|
3270
3493
|
return await executeActionStep(step, context, api, permissions, allowedActionIds, options);
|
|
3271
3494
|
case "transform":
|
|
3272
|
-
return executeTransformStep(step, context);
|
|
3495
|
+
return await executeTransformStep(step, context);
|
|
3273
3496
|
case "code":
|
|
3274
3497
|
return await executeCodeStep(step, context, options);
|
|
3275
3498
|
case "condition":
|
|
@@ -3366,7 +3589,7 @@ async function executeSteps(steps, context, api, permissions, allowedActionIds,
|
|
|
3366
3589
|
if (isDryResolve && options.stopAfter === step.id) {
|
|
3367
3590
|
options.onEvent?.({
|
|
3368
3591
|
event: "step:dry-resolve",
|
|
3369
|
-
...dryResolveStep(step, context),
|
|
3592
|
+
...await dryResolveStep(step, context),
|
|
3370
3593
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
3371
3594
|
});
|
|
3372
3595
|
throw new FlowStopSignal(step.id);
|
|
@@ -3483,7 +3706,7 @@ async function executeFlow(flow, inputs, api, permissions, allowedActionIds, opt
|
|
|
3483
3706
|
event: "flow:dry-run",
|
|
3484
3707
|
flowKey: flow.key,
|
|
3485
3708
|
resolvedInputs,
|
|
3486
|
-
steps: dryResolveAllSteps(flow.steps, context),
|
|
3709
|
+
steps: await dryResolveAllSteps(flow.steps, context),
|
|
3487
3710
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
3488
3711
|
});
|
|
3489
3712
|
return context;
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
import {
|
|
5
5
|
okJson,
|
|
6
6
|
requireMemoryInit
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-B5YDLVY5.js";
|
|
8
8
|
import {
|
|
9
9
|
isAgentMode,
|
|
10
10
|
note
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
import {
|
|
13
13
|
getBackend,
|
|
14
14
|
upsertRecord
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-4H5L3SBY.js";
|
|
16
16
|
|
|
17
17
|
// src/commands/mem/migrate.ts
|
|
18
18
|
import fs4 from "fs";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
requireMemoryInit
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-B5YDLVY5.js";
|
|
4
4
|
import {
|
|
5
5
|
error,
|
|
6
6
|
isAgentMode,
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
} from "./chunk-Q6PWTATS.js";
|
|
9
9
|
import {
|
|
10
10
|
getBackend
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-4H5L3SBY.js";
|
|
12
12
|
|
|
13
13
|
// src/commands/mem/sql.ts
|
|
14
14
|
async function memSqlCommand(sql) {
|