yaml-flow 3.1.1 → 5.0.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 +81 -20
- package/board-live-cards-cli.js +37 -0
- package/browser/board-livegraph-runtime.js +1453 -0
- package/browser/board-livegraph-runtime.js.map +1 -0
- package/browser/card-compute.js +153 -433
- package/browser/live-cards.js +868 -115
- package/browser/live-cards.schema.json +90 -83
- package/dist/board-livegraph-runtime/index.cjs +1448 -0
- package/dist/board-livegraph-runtime/index.cjs.map +1 -0
- package/dist/board-livegraph-runtime/index.d.cts +101 -0
- package/dist/board-livegraph-runtime/index.d.ts +101 -0
- package/dist/board-livegraph-runtime/index.js +1441 -0
- package/dist/board-livegraph-runtime/index.js.map +1 -0
- package/dist/card-compute/index.cjs +266 -431
- package/dist/card-compute/index.cjs.map +1 -1
- package/dist/card-compute/index.d.cts +77 -49
- package/dist/card-compute/index.d.ts +77 -49
- package/dist/card-compute/index.js +263 -432
- package/dist/card-compute/index.js.map +1 -1
- package/dist/cli/board-live-cards-cli.cjs +2750 -0
- package/dist/cli/board-live-cards-cli.cjs.map +1 -0
- package/dist/cli/board-live-cards-cli.d.cts +205 -0
- package/dist/cli/board-live-cards-cli.d.ts +205 -0
- package/dist/cli/board-live-cards-cli.js +2702 -0
- package/dist/cli/board-live-cards-cli.js.map +1 -0
- package/dist/{constants-B2zqu10b.d.ts → constants-DuzE5n03.d.ts} +2 -2
- package/dist/{constants-DJZU1pwJ.d.cts → constants-ozjf1Ejw.d.cts} +2 -2
- package/dist/continuous-event-graph/index.cjs +258 -464
- package/dist/continuous-event-graph/index.cjs.map +1 -1
- package/dist/continuous-event-graph/index.d.cts +18 -358
- package/dist/continuous-event-graph/index.d.ts +18 -358
- package/dist/continuous-event-graph/index.js +255 -464
- package/dist/continuous-event-graph/index.js.map +1 -1
- package/dist/event-graph/index.cjs +4 -4
- package/dist/event-graph/index.cjs.map +1 -1
- package/dist/event-graph/index.d.cts +5 -5
- package/dist/event-graph/index.d.ts +5 -5
- package/dist/event-graph/index.js +4 -4
- package/dist/event-graph/index.js.map +1 -1
- package/dist/index.cjs +1684 -555
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +26 -7
- package/dist/index.d.ts +26 -7
- package/dist/index.js +1678 -555
- package/dist/index.js.map +1 -1
- package/dist/inference/index.cjs +138 -19
- package/dist/inference/index.cjs.map +1 -1
- package/dist/inference/index.d.cts +2 -2
- package/dist/inference/index.d.ts +2 -2
- package/dist/inference/index.js +138 -19
- package/dist/inference/index.js.map +1 -1
- package/dist/journal-DRfJiheM.d.cts +28 -0
- package/dist/journal-NLYuqege.d.ts +28 -0
- package/dist/live-cards-bridge-Or7fdEJV.d.ts +316 -0
- package/dist/live-cards-bridge-vGJ6tMzN.d.cts +316 -0
- package/dist/schedule-CMcZe5Ny.d.ts +21 -0
- package/dist/schedule-CiucyCan.d.cts +21 -0
- package/dist/step-machine/index.cjs +18 -1
- package/dist/step-machine/index.cjs.map +1 -1
- package/dist/step-machine/index.d.cts +2 -2
- package/dist/step-machine/index.d.ts +2 -2
- package/dist/step-machine/index.js +18 -1
- package/dist/step-machine/index.js.map +1 -1
- package/dist/stores/file.d.cts +1 -1
- package/dist/stores/file.d.ts +1 -1
- package/dist/stores/index.d.cts +1 -1
- package/dist/stores/index.d.ts +1 -1
- package/dist/stores/localStorage.d.cts +1 -1
- package/dist/stores/localStorage.d.ts +1 -1
- package/dist/stores/memory.d.cts +1 -1
- package/dist/stores/memory.d.ts +1 -1
- package/dist/{types-BwvgvlOO.d.cts → types-BzLD8bjb.d.cts} +1 -1
- package/dist/{types-ClRA8hzC.d.ts → types-C2eJ7DAV.d.ts} +1 -1
- package/dist/{types-DEj7OakX.d.cts → types-CMFSIjpc.d.cts} +39 -4
- package/dist/{types-DEj7OakX.d.ts → types-CMFSIjpc.d.ts} +39 -4
- package/dist/{types-FZ_eyErS.d.cts → types-ycun84cq.d.cts} +1 -0
- package/dist/{types-FZ_eyErS.d.ts → types-ycun84cq.d.ts} +1 -0
- package/dist/{validate-DEZ2Ymdb.d.ts → validate-DJQTQ6bP.d.ts} +1 -1
- package/dist/{validate-DqKTZg_o.d.cts → validate-ke92Cleg.d.cts} +1 -1
- package/examples/browser/boards/portfolio-tracker/cards/holdings-table.json +22 -0
- package/examples/browser/boards/portfolio-tracker/cards/portfolio-form.json +16 -0
- package/examples/browser/boards/portfolio-tracker/cards/portfolio-value.json +15 -0
- package/examples/browser/boards/portfolio-tracker/cards/price-fetch.json +15 -0
- package/examples/browser/boards/portfolio-tracker/fetch-prices.js +43 -0
- package/examples/browser/boards/portfolio-tracker/portfolio-tracker-task-executor.cjs +96 -0
- package/examples/browser/boards/portfolio-tracker/portfolio-tracker.bat +7 -0
- package/examples/browser/boards/portfolio-tracker/portfolio-tracker.js +217 -0
- package/examples/browser/livecards-browser/index.html +41 -0
- package/examples/browser/{index.html → step-machine-browser/index.html} +53 -53
- package/examples/cli/step-machine-cli/portfolio-tracker/cards/holdings-table.json +22 -0
- package/examples/cli/step-machine-cli/portfolio-tracker/cards/portfolio-form.json +43 -0
- package/examples/cli/step-machine-cli/portfolio-tracker/cards/portfolio-value.json +15 -0
- package/examples/cli/step-machine-cli/portfolio-tracker/cards/price-fetch.json +15 -0
- package/examples/cli/step-machine-cli/portfolio-tracker/fetch-prices.js +48 -0
- package/examples/cli/step-machine-cli/portfolio-tracker/handlers/_board-cli.js +58 -0
- package/examples/cli/step-machine-cli/portfolio-tracker/handlers/add-cards-cli.js +27 -0
- package/examples/cli/step-machine-cli/portfolio-tracker/handlers/init-board-cli.js +25 -0
- package/examples/cli/step-machine-cli/portfolio-tracker/handlers/reset-board-dir-cli.js +29 -0
- package/examples/cli/step-machine-cli/portfolio-tracker/handlers/retrigger-cli.js +27 -0
- package/examples/cli/step-machine-cli/portfolio-tracker/handlers/status-cli.js +25 -0
- package/examples/cli/step-machine-cli/portfolio-tracker/handlers/update-holdings-cli.js +37 -0
- package/examples/cli/step-machine-cli/portfolio-tracker/handlers/wait-completed-cli.js +53 -0
- package/examples/cli/step-machine-cli/portfolio-tracker/handlers/write-prices-cli.js +35 -0
- package/examples/cli/step-machine-cli/portfolio-tracker/portfolio-tracker.flow.yaml +227 -0
- package/examples/cli/step-machine-cli/portfolio-tracker/portfolio-tracker.input.json +38 -0
- package/examples/cli/step-machine-cli/portfolio-tracker/run-portfolio-tracker.bat +29 -0
- package/examples/cli/step-machine-demo/jsonata-init-board-cli.js +36 -0
- package/examples/cli/step-machine-demo/jsonata-init-board.flow.yaml +30 -0
- package/examples/cli/step-machine-demo/one-step-cli-only.flow.yaml +19 -0
- package/examples/cli/step-machine-demo/step-cli-echo-y.js +15 -0
- package/examples/cli/step-machine-demo/step2-double-cli.js +39 -0
- package/examples/cli/step-machine-demo/two-step-math-handlers.js +32 -0
- package/examples/cli/step-machine-demo/two-step-math.flow.yaml +31 -0
- package/examples/cli/step-machine-demo/two-step-mixed-handlers.js +24 -0
- package/examples/cli/step-machine-demo/two-step-mixed.flow.yaml +35 -0
- package/examples/example-board/board.yaml +23 -0
- package/examples/example-board/bootstrap_payload.json +1 -0
- package/examples/example-board/cards/card-chain-region-alert.json +39 -0
- package/examples/example-board/cards/card-chain-region-totals.json +26 -0
- package/examples/example-board/cards/card-chain-top-region.json +24 -0
- package/examples/example-board/cards/card-ex-actions.json +32 -0
- package/examples/example-board/cards/card-ex-chart.json +30 -0
- package/examples/example-board/cards/card-ex-filter.json +36 -0
- package/examples/example-board/cards/card-ex-filtered-by-preference.json +59 -0
- package/examples/example-board/cards/card-ex-form.json +91 -0
- package/examples/example-board/cards/card-ex-list.json +22 -0
- package/examples/example-board/cards/card-ex-markdown.json +17 -0
- package/examples/example-board/cards/card-ex-metric.json +19 -0
- package/examples/example-board/cards/card-ex-narrative.json +36 -0
- package/examples/example-board/cards/card-ex-source-http.json +28 -0
- package/examples/example-board/cards/card-ex-source.json +21 -0
- package/examples/example-board/cards/card-ex-status.json +35 -0
- package/examples/example-board/cards/card-ex-table.json +30 -0
- package/examples/example-board/cards/card-ex-todo.json +29 -0
- package/examples/example-board/demo-chat-handler.js +69 -0
- package/examples/example-board/demo-server.js +87 -0
- package/examples/example-board/demo-shell-browser.html +806 -0
- package/examples/example-board/demo-shell-with-server.html +280 -0
- package/examples/example-board/demo-shell.html +62 -0
- package/examples/example-board/demo-task-executor.js +255 -0
- package/examples/example-board/mock.db +15 -0
- package/examples/example-board/reusable-board-runtime-client.js +265 -0
- package/examples/example-board/reusable-runtime-artifacts-adapter.js +233 -0
- package/examples/example-board/reusable-server-runtime.js +1284 -0
- package/examples/index.html +799 -0
- package/examples/{batch → npm-libs/batch}/batch-step-machine.ts +1 -1
- package/examples/{continuous-event-graph → npm-libs/continuous-event-graph}/live-cards-board.ts +18 -18
- package/examples/{continuous-event-graph → npm-libs/continuous-event-graph}/live-portfolio-dashboard.ts +24 -24
- package/examples/{continuous-event-graph → npm-libs/continuous-event-graph}/portfolio-tracker.ts +1 -1
- package/examples/{continuous-event-graph → npm-libs/continuous-event-graph}/reactive-monitoring.ts +1 -1
- package/examples/{continuous-event-graph → npm-libs/continuous-event-graph}/reactive-pipeline.ts +1 -1
- package/examples/{continuous-event-graph → npm-libs/continuous-event-graph}/soc-incident-board.ts +1 -1
- package/examples/{continuous-event-graph → npm-libs/continuous-event-graph}/stock-dashboard.ts +1 -1
- package/examples/{event-graph → npm-libs/event-graph}/ci-cd-pipeline.ts +1 -1
- package/examples/{event-graph → npm-libs/event-graph}/executor-diamond.ts +1 -1
- package/examples/{event-graph → npm-libs/event-graph}/executor-pipeline.ts +1 -1
- package/examples/{event-graph → npm-libs/event-graph}/research-pipeline.ts +1 -1
- package/examples/{graph-of-graphs → npm-libs/graph-of-graphs}/multi-stage-etl.ts +1 -1
- package/examples/{graph-of-graphs → npm-libs/graph-of-graphs}/url-processing-pipeline.ts +1 -1
- package/examples/{inference → npm-libs/inference}/azure-deployment.ts +1 -1
- package/examples/{inference → npm-libs/inference}/copilot-cli.ts +1 -1
- package/examples/{inference → npm-libs/inference}/data-pipeline.ts +1 -1
- package/examples/{inference → npm-libs/inference}/pluggable-adapters.ts +1 -1
- package/examples/{node → npm-libs/node}/ai-conversation.ts +1 -1
- package/examples/{node → npm-libs/node}/simple-greeting.ts +2 -2
- package/examples/step-machine-cli/portfolio-tracker/cards/holdings-table.json +22 -0
- package/examples/step-machine-cli/portfolio-tracker/cards/portfolio-form.json +43 -0
- package/examples/step-machine-cli/portfolio-tracker/cards/portfolio-value.json +15 -0
- package/examples/step-machine-cli/portfolio-tracker/cards/price-fetch.json +15 -0
- package/examples/step-machine-cli/portfolio-tracker/fetch-prices.js +48 -0
- package/examples/step-machine-cli/portfolio-tracker/handlers/_board-cli.js +58 -0
- package/examples/step-machine-cli/portfolio-tracker/handlers/add-cards-cli.js +27 -0
- package/examples/step-machine-cli/portfolio-tracker/handlers/init-board-cli.js +25 -0
- package/examples/step-machine-cli/portfolio-tracker/handlers/reset-board-dir-cli.js +29 -0
- package/examples/step-machine-cli/portfolio-tracker/handlers/retrigger-cli.js +27 -0
- package/examples/step-machine-cli/portfolio-tracker/handlers/status-cli.js +25 -0
- package/examples/step-machine-cli/portfolio-tracker/handlers/update-holdings-cli.js +37 -0
- package/examples/step-machine-cli/portfolio-tracker/handlers/wait-completed-cli.js +53 -0
- package/examples/step-machine-cli/portfolio-tracker/handlers/write-prices-cli.js +35 -0
- package/examples/step-machine-cli/portfolio-tracker/portfolio-tracker-task-executor.cjs +96 -0
- package/examples/step-machine-cli/portfolio-tracker/portfolio-tracker.flow.yaml +227 -0
- package/examples/step-machine-cli/portfolio-tracker/portfolio-tracker.input.json +38 -0
- package/examples/step-machine-cli/portfolio-tracker/run-portfolio-tracker.bat +29 -0
- package/package.json +27 -2
- package/schema/board-status.schema.json +118 -0
- package/schema/card-runtime.schema.json +25 -0
- package/schema/flow.schema.json +5 -0
- package/schema/live-cards.schema.json +90 -83
- package/step-machine-cli.js +674 -0
- package/browser/ingest-board.js +0 -296
- package/examples/ingest.js +0 -733
- /package/examples/{flows → npm-libs/flows}/ai-conversation.yaml +0 -0
- /package/examples/{flows → npm-libs/flows}/order-processing.yaml +0 -0
- /package/examples/{flows → npm-libs/flows}/simple-greeting.yaml +0 -0
|
@@ -0,0 +1,674 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import * as fs from 'node:fs';
|
|
4
|
+
import * as path from 'node:path';
|
|
5
|
+
import { pathToFileURL } from 'node:url';
|
|
6
|
+
import { spawnSync } from 'node:child_process';
|
|
7
|
+
import jsonata from 'jsonata';
|
|
8
|
+
|
|
9
|
+
const { loadStepFlow, createStepMachine, MemoryStore, FileStore } = await import('./dist/index.js');
|
|
10
|
+
const PAUSE_FILE_NAME = '.pause';
|
|
11
|
+
|
|
12
|
+
async function main() {
|
|
13
|
+
const args = process.argv.slice(2);
|
|
14
|
+
const parsed = parseCliArgs(args);
|
|
15
|
+
|
|
16
|
+
if (parsed.help || args.length === 0) {
|
|
17
|
+
printUsage();
|
|
18
|
+
process.exit(args.length === 0 ? 1 : 0);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const {
|
|
22
|
+
flowArg,
|
|
23
|
+
handlersArg,
|
|
24
|
+
dataArg,
|
|
25
|
+
storeArg,
|
|
26
|
+
storeDirArg,
|
|
27
|
+
resumeRequested,
|
|
28
|
+
pauseRequested,
|
|
29
|
+
statusRequested,
|
|
30
|
+
} = parsed;
|
|
31
|
+
|
|
32
|
+
if ((pauseRequested || statusRequested) && (handlersArg || dataArg || resumeRequested || flowArg)) {
|
|
33
|
+
throw new Error('[step-machine-cli] --pause and --status are store-level operations. Do not provide flow, handlers, data, or --resume.');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (resumeRequested && dataArg) {
|
|
37
|
+
throw new Error('[step-machine-cli] --initial-data cannot be combined with --resume.');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const storeContext = createStoreContext(storeArg, storeDirArg);
|
|
41
|
+
|
|
42
|
+
if (statusRequested) {
|
|
43
|
+
await printStoreStatus(storeContext);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (pauseRequested) {
|
|
48
|
+
await requestPause(storeContext);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (!flowArg) {
|
|
53
|
+
throw new Error('[step-machine-cli] Flow path is required for run/resume operations.');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const flowPath = resolveInputPath(flowArg);
|
|
57
|
+
const flowDir = path.dirname(flowPath);
|
|
58
|
+
const handlersPath = handlersArg ? resolveInputPath(handlersArg) : undefined;
|
|
59
|
+
const initialData = parseInitialData(dataArg);
|
|
60
|
+
const { store } = storeContext;
|
|
61
|
+
|
|
62
|
+
const flow = await loadStepFlow(flowPath);
|
|
63
|
+
const inlineHandlers = handlersPath ? await loadHandlers(handlersPath) : {};
|
|
64
|
+
const handlers = buildStepHandlers(flow, inlineHandlers, flowDir, flow.handler_vars);
|
|
65
|
+
|
|
66
|
+
// Resume/start should ignore stale pause markers from previous runs.
|
|
67
|
+
clearPauseRequest(storeContext);
|
|
68
|
+
|
|
69
|
+
const abortController = new AbortController();
|
|
70
|
+
let pauseSignalSeen = false;
|
|
71
|
+
|
|
72
|
+
const machine = createStepMachine(flow, handlers, {
|
|
73
|
+
store,
|
|
74
|
+
signal: abortController.signal,
|
|
75
|
+
onStep: () => {
|
|
76
|
+
if (!pauseSignalSeen && hasPauseRequest(storeContext)) {
|
|
77
|
+
pauseSignalSeen = true;
|
|
78
|
+
abortController.abort();
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
let runIdToResume;
|
|
84
|
+
if (resumeRequested) {
|
|
85
|
+
runIdToResume = await resolveRunIdToResume(storeContext);
|
|
86
|
+
if (!runIdToResume) {
|
|
87
|
+
console.warn('[step-machine-cli] No paused run found in store directory.');
|
|
88
|
+
console.log(JSON.stringify({ status: 'noop', reason: 'no-paused-run' }, null, 2));
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
} else if (storeContext.storeType === 'file' && !initialData) {
|
|
92
|
+
runIdToResume = await resolveRunIdToResume(storeContext);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const result = runIdToResume
|
|
96
|
+
? await machine.resume(runIdToResume)
|
|
97
|
+
: await machine.run(initialData);
|
|
98
|
+
|
|
99
|
+
if (pauseSignalSeen && result.status === 'cancelled') {
|
|
100
|
+
const pausedState = await markRunPaused(store, result.runId);
|
|
101
|
+
clearPauseRequest(storeContext);
|
|
102
|
+
console.log(JSON.stringify({
|
|
103
|
+
runId: result.runId,
|
|
104
|
+
status: 'paused',
|
|
105
|
+
currentStep: pausedState?.currentStep,
|
|
106
|
+
pausedAt: pausedState?.pausedAt,
|
|
107
|
+
stepHistory: result.stepHistory,
|
|
108
|
+
data: result.data,
|
|
109
|
+
}, null, 2));
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (result.status !== 'completed') {
|
|
114
|
+
const reason = result.error?.message ?? result.intent ?? result.status;
|
|
115
|
+
console.error(`[step-machine-cli] Run failed: ${reason}`);
|
|
116
|
+
process.exit(1);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
console.log(JSON.stringify({
|
|
120
|
+
runId: result.runId,
|
|
121
|
+
status: result.status,
|
|
122
|
+
intent: result.intent,
|
|
123
|
+
finalStep: result.finalStep,
|
|
124
|
+
stepHistory: result.stepHistory,
|
|
125
|
+
data: result.data,
|
|
126
|
+
}, null, 2));
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function parseCliArgs(args) {
|
|
130
|
+
const valueFlags = new Set(['--handlers', '--initial-data', '--store', '--store-dir']);
|
|
131
|
+
const values = {};
|
|
132
|
+
const positionals = [];
|
|
133
|
+
let help = false;
|
|
134
|
+
let resumeRequested = false;
|
|
135
|
+
let pauseRequested = false;
|
|
136
|
+
let statusRequested = false;
|
|
137
|
+
|
|
138
|
+
for (let i = 0; i < args.length; i++) {
|
|
139
|
+
const arg = args[i];
|
|
140
|
+
if (arg === '-h' || arg === '--help') {
|
|
141
|
+
help = true;
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (arg === '--resume') {
|
|
146
|
+
resumeRequested = true;
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (arg === '--pause') {
|
|
151
|
+
pauseRequested = true;
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (arg === '--status') {
|
|
156
|
+
statusRequested = true;
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (valueFlags.has(arg)) {
|
|
161
|
+
const value = args[i + 1];
|
|
162
|
+
if (!value || value.startsWith('--')) {
|
|
163
|
+
throw new Error(`[step-machine-cli] Missing value for ${arg}.`);
|
|
164
|
+
}
|
|
165
|
+
values[arg] = value;
|
|
166
|
+
i++;
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (arg.startsWith('--')) {
|
|
171
|
+
throw new Error(`[step-machine-cli] Unknown flag: ${arg}`);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
positionals.push(arg);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if ([resumeRequested, pauseRequested, statusRequested].filter(Boolean).length > 1) {
|
|
178
|
+
throw new Error('[step-machine-cli] Use only one of --resume, --pause, or --status at a time.');
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return {
|
|
182
|
+
help,
|
|
183
|
+
flowArg: positionals[0],
|
|
184
|
+
handlersArg: values['--handlers'],
|
|
185
|
+
dataArg: values['--initial-data'],
|
|
186
|
+
storeArg: String(values['--store'] ?? 'memory').toLowerCase(),
|
|
187
|
+
storeDirArg: values['--store-dir'],
|
|
188
|
+
resumeRequested,
|
|
189
|
+
pauseRequested,
|
|
190
|
+
statusRequested,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function resolveInputPath(inputPath) {
|
|
195
|
+
return path.isAbsolute(inputPath)
|
|
196
|
+
? inputPath
|
|
197
|
+
: path.resolve(process.cwd(), inputPath);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function createStoreContext(storeType, storeDirArg) {
|
|
201
|
+
if (storeType !== 'memory' && storeType !== 'file') {
|
|
202
|
+
throw new Error(`[step-machine-cli] Invalid --store value "${storeType}". Expected "memory" or "file".`);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (storeType === 'memory') {
|
|
206
|
+
return {
|
|
207
|
+
storeType,
|
|
208
|
+
storeDir: undefined,
|
|
209
|
+
pauseFilePath: undefined,
|
|
210
|
+
store: new MemoryStore(),
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (!storeDirArg || storeDirArg.trim().length === 0) {
|
|
215
|
+
throw new Error('[step-machine-cli] --store file requires --store-dir <directory>.');
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const storeDir = resolveInputPath(storeDirArg);
|
|
219
|
+
return {
|
|
220
|
+
storeType,
|
|
221
|
+
storeDir,
|
|
222
|
+
pauseFilePath: path.join(storeDir, PAUSE_FILE_NAME),
|
|
223
|
+
store: new FileStore({ directory: storeDir }),
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
async function listRunStates(store) {
|
|
228
|
+
if (!store.listRuns) {
|
|
229
|
+
return [];
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const runIds = await store.listRuns();
|
|
233
|
+
const states = [];
|
|
234
|
+
for (const runId of runIds) {
|
|
235
|
+
const state = await store.loadRunState(runId);
|
|
236
|
+
if (state) {
|
|
237
|
+
states.push(state);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
states.sort((a, b) => (b.updatedAt ?? b.startedAt ?? 0) - (a.updatedAt ?? a.startedAt ?? 0));
|
|
242
|
+
return states;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function hasPauseRequest(storeContext) {
|
|
246
|
+
if (storeContext.storeType !== 'file' || !storeContext.pauseFilePath) {
|
|
247
|
+
return false;
|
|
248
|
+
}
|
|
249
|
+
return fs.existsSync(storeContext.pauseFilePath);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function clearPauseRequest(storeContext) {
|
|
253
|
+
if (!hasPauseRequest(storeContext)) {
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
fs.unlinkSync(storeContext.pauseFilePath);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
async function requestPause(storeContext) {
|
|
260
|
+
if (storeContext.storeType !== 'file' || !storeContext.pauseFilePath) {
|
|
261
|
+
throw new Error('[step-machine-cli] --pause requires --store file --store-dir <directory>.');
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const states = await listRunStates(storeContext.store);
|
|
265
|
+
if (states.length === 0) {
|
|
266
|
+
console.warn('[step-machine-cli] No runs found in store directory. Pause is a no-op.');
|
|
267
|
+
console.log(JSON.stringify({ status: 'noop', reason: 'no-runs' }, null, 2));
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const running = states.find((s) => s.status === 'running');
|
|
272
|
+
if (!running) {
|
|
273
|
+
console.warn('[step-machine-cli] No running run found. Pause is a no-op.');
|
|
274
|
+
console.log(JSON.stringify({ status: 'noop', reason: 'no-running-run' }, null, 2));
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
fs.mkdirSync(storeContext.storeDir, { recursive: true });
|
|
279
|
+
fs.writeFileSync(storeContext.pauseFilePath, JSON.stringify({ requestedAt: Date.now() }), 'utf-8');
|
|
280
|
+
console.log(JSON.stringify({ status: 'pause-requested', storeDir: storeContext.storeDir }, null, 2));
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
async function resolveRunIdToResume(storeContext) {
|
|
284
|
+
const states = await listRunStates(storeContext.store);
|
|
285
|
+
const pausedStates = states.filter((s) => s.status === 'paused');
|
|
286
|
+
if (pausedStates.length === 0) {
|
|
287
|
+
return undefined;
|
|
288
|
+
}
|
|
289
|
+
if (pausedStates.length > 1) {
|
|
290
|
+
console.warn('[step-machine-cli] Multiple paused runs found; resuming the most recently updated run.');
|
|
291
|
+
}
|
|
292
|
+
return pausedStates[0].runId;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
async function markRunPaused(store, runId) {
|
|
296
|
+
const state = await store.loadRunState(runId);
|
|
297
|
+
if (!state) {
|
|
298
|
+
return null;
|
|
299
|
+
}
|
|
300
|
+
const pausedState = {
|
|
301
|
+
...state,
|
|
302
|
+
status: 'paused',
|
|
303
|
+
pausedAt: Date.now(),
|
|
304
|
+
updatedAt: Date.now(),
|
|
305
|
+
};
|
|
306
|
+
await store.saveRunState(runId, pausedState);
|
|
307
|
+
return pausedState;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
async function printStoreStatus(storeContext) {
|
|
311
|
+
if (storeContext.storeType !== 'file') {
|
|
312
|
+
throw new Error('[step-machine-cli] --status requires --store file --store-dir <directory>.');
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
const states = await listRunStates(storeContext.store);
|
|
316
|
+
const summary = {
|
|
317
|
+
store: 'file',
|
|
318
|
+
storeDir: storeContext.storeDir,
|
|
319
|
+
pauseRequested: hasPauseRequest(storeContext),
|
|
320
|
+
totalRuns: states.length,
|
|
321
|
+
runs: states.map((s) => ({
|
|
322
|
+
runId: s.runId,
|
|
323
|
+
status: s.status,
|
|
324
|
+
currentStep: s.currentStep,
|
|
325
|
+
startedAt: s.startedAt,
|
|
326
|
+
updatedAt: s.updatedAt,
|
|
327
|
+
pausedAt: s.pausedAt,
|
|
328
|
+
})),
|
|
329
|
+
};
|
|
330
|
+
|
|
331
|
+
console.log(JSON.stringify(summary, null, 2));
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
function parseInitialData(dataArg) {
|
|
335
|
+
if (!dataArg) {
|
|
336
|
+
return undefined;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
try {
|
|
340
|
+
const parsed = JSON.parse(dataArg);
|
|
341
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
342
|
+
throw new Error('Initial data must be a JSON object.');
|
|
343
|
+
}
|
|
344
|
+
return parsed;
|
|
345
|
+
} catch (error) {
|
|
346
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
347
|
+
throw new Error(`[step-machine-cli] Invalid --initial-data value: ${msg}`);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
async function loadHandlers(handlersPath) {
|
|
352
|
+
const mod = await import(pathToFileURL(handlersPath).href);
|
|
353
|
+
const candidate = mod.default ?? mod.handlers ?? mod;
|
|
354
|
+
|
|
355
|
+
if (!candidate || typeof candidate !== 'object') {
|
|
356
|
+
throw new Error('[step-machine-cli] Handlers module must export an object map of stepName -> function.');
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
for (const [stepName, handler] of Object.entries(candidate)) {
|
|
360
|
+
if (typeof handler !== 'function') {
|
|
361
|
+
throw new Error(`[step-machine-cli] Handler for step "${stepName}" is not a function.`);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
return candidate;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
function buildStepHandlers(flow, inlineHandlers, flowDir, handlerVars) {
|
|
369
|
+
const handlers = {};
|
|
370
|
+
|
|
371
|
+
for (const [stepName, stepConfig] of Object.entries(flow.steps ?? {})) {
|
|
372
|
+
handlers[stepName] = resolveStepHandler(stepName, stepConfig, inlineHandlers, flowDir, handlerVars);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
return handlers;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
function resolveStepHandler(stepName, stepConfig, inlineHandlers, flowDir, handlerVars) {
|
|
379
|
+
const produces = Array.isArray(stepConfig?.produces_data) ? stepConfig.produces_data : undefined;
|
|
380
|
+
const spec = stepConfig?.handler;
|
|
381
|
+
|
|
382
|
+
if (isCliSpec(spec)) {
|
|
383
|
+
const base = createCliStepHandler(spec, flowDir, stepName, handlerVars);
|
|
384
|
+
return wrapWithOutputFiltering(base, produces);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
if (isInlineSpec(spec)) {
|
|
388
|
+
const inlineName = spec.inline;
|
|
389
|
+
const handler = inlineHandlers[inlineName];
|
|
390
|
+
if (typeof handler !== 'function') {
|
|
391
|
+
throw new Error(`[step-machine-cli] Inline handler "${inlineName}" for step "${stepName}" was not found in --handlers module.`);
|
|
392
|
+
}
|
|
393
|
+
return wrapWithOutputFiltering(handler, produces);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// Default behavior is explicit and predictable: no configured handler means passthrough.
|
|
397
|
+
return wrapWithOutputFiltering(createPassthroughHandler(), produces);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
function isCliSpec(spec) {
|
|
401
|
+
return !!spec && typeof spec === 'object' && typeof spec.cli === 'string' && spec.cli.trim().length > 0;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
function isInlineSpec(spec) {
|
|
405
|
+
return !!spec && typeof spec === 'object' && typeof spec.inline === 'string' && spec.inline.trim().length > 0;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
function createCliStepHandler(spec, flowDir, stepName, handlerVars) {
|
|
409
|
+
return async (input) => {
|
|
410
|
+
const stepInput = input && typeof input === 'object' && !Array.isArray(input)
|
|
411
|
+
? input
|
|
412
|
+
: {};
|
|
413
|
+
|
|
414
|
+
const resolvedHandlerVars = await evaluateTransforms(
|
|
415
|
+
handlerVars,
|
|
416
|
+
stepInput,
|
|
417
|
+
stepName,
|
|
418
|
+
'handler_vars',
|
|
419
|
+
);
|
|
420
|
+
|
|
421
|
+
const inputTransforms = await evaluateTransforms(
|
|
422
|
+
spec['input-transforms'],
|
|
423
|
+
{ ...stepInput, ...resolvedHandlerVars },
|
|
424
|
+
stepName,
|
|
425
|
+
'input-transforms',
|
|
426
|
+
);
|
|
427
|
+
|
|
428
|
+
const effectiveInput = { ...stepInput, ...resolvedHandlerVars, ...inputTransforms };
|
|
429
|
+
const command = applyCommandTemplate(spec.cli, effectiveInput, stepName);
|
|
430
|
+
|
|
431
|
+
const payload = JSON.stringify(effectiveInput);
|
|
432
|
+
const result = spawnSync(command, {
|
|
433
|
+
cwd: flowDir,
|
|
434
|
+
shell: true,
|
|
435
|
+
input: payload,
|
|
436
|
+
encoding: 'utf-8',
|
|
437
|
+
windowsHide: true,
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
if (result.error) {
|
|
441
|
+
return {
|
|
442
|
+
result: 'failure',
|
|
443
|
+
data: { error: `[step-machine-cli] step "${stepName}" failed to start: ${result.error.message}` },
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
const stdout = result.stdout ?? '';
|
|
448
|
+
const stderr = (result.stderr ?? '').trim();
|
|
449
|
+
const resultMode = String(spec['result-mode'] ?? 'json').toLowerCase();
|
|
450
|
+
|
|
451
|
+
if (result.status !== 0) {
|
|
452
|
+
return {
|
|
453
|
+
result: 'failure',
|
|
454
|
+
data: {
|
|
455
|
+
error: `[step-machine-cli] step "${stepName}" exited with status ${result.status}${stderr ? `: ${stderr}` : ''}`,
|
|
456
|
+
},
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
if (resultMode === 'exit-code') {
|
|
461
|
+
const outputTransforms = await evaluateTransforms(
|
|
462
|
+
spec['output-transforms'],
|
|
463
|
+
{
|
|
464
|
+
...effectiveInput,
|
|
465
|
+
result: 'success',
|
|
466
|
+
stdout,
|
|
467
|
+
stderr,
|
|
468
|
+
},
|
|
469
|
+
stepName,
|
|
470
|
+
'output-transforms',
|
|
471
|
+
);
|
|
472
|
+
|
|
473
|
+
return {
|
|
474
|
+
result: 'success',
|
|
475
|
+
data: outputTransforms,
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
try {
|
|
480
|
+
const parsed = parseJsonOutput(stdout);
|
|
481
|
+
const normalized = normalizeHandlerResult(parsed, stepName);
|
|
482
|
+
const outputTransforms = await evaluateTransforms(
|
|
483
|
+
spec['output-transforms'],
|
|
484
|
+
{
|
|
485
|
+
...effectiveInput,
|
|
486
|
+
result: normalized.result,
|
|
487
|
+
data: normalized.data,
|
|
488
|
+
},
|
|
489
|
+
stepName,
|
|
490
|
+
'output-transforms',
|
|
491
|
+
);
|
|
492
|
+
|
|
493
|
+
if (Object.keys(outputTransforms).length === 0) {
|
|
494
|
+
return normalized;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
return {
|
|
498
|
+
result: normalized.result,
|
|
499
|
+
data: outputTransforms,
|
|
500
|
+
};
|
|
501
|
+
} catch (error) {
|
|
502
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
503
|
+
return {
|
|
504
|
+
result: 'failure',
|
|
505
|
+
data: {
|
|
506
|
+
error: `[step-machine-cli] step "${stepName}" returned invalid JSON on stdout: ${msg}`,
|
|
507
|
+
},
|
|
508
|
+
};
|
|
509
|
+
}
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
async function evaluateTransforms(transformSpec, source, stepName, label) {
|
|
514
|
+
if (transformSpec === undefined || transformSpec === null) {
|
|
515
|
+
return {};
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
if (!transformSpec || typeof transformSpec !== 'object' || Array.isArray(transformSpec)) {
|
|
519
|
+
throw new Error(`[step-machine-cli] Step "${stepName}" ${label} must be an object map of key -> JSONata expression.`);
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
const result = {};
|
|
523
|
+
for (const [key, expression] of Object.entries(transformSpec)) {
|
|
524
|
+
if (typeof expression !== 'string') {
|
|
525
|
+
// Non-string values are treated as literals for convenience.
|
|
526
|
+
result[key] = expression;
|
|
527
|
+
continue;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
if (expression.trim().length === 0) {
|
|
531
|
+
throw new Error(`[step-machine-cli] Step "${stepName}" ${label}.${key} must be a non-empty string expression.`);
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
// handler_vars defaults to literal strings for ergonomics; use "=..." for JSONata expressions.
|
|
535
|
+
if (label === 'handler_vars' && !expression.startsWith('=')) {
|
|
536
|
+
result[key] = expression;
|
|
537
|
+
continue;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
const jsonataExpression = label === 'handler_vars' && expression.startsWith('=')
|
|
541
|
+
? expression.slice(1)
|
|
542
|
+
: expression;
|
|
543
|
+
|
|
544
|
+
// Convenience: direct key aliasing supports non-identifier keys like BOARD-DIR-NAME.
|
|
545
|
+
if (Object.prototype.hasOwnProperty.call(source, jsonataExpression)) {
|
|
546
|
+
result[key] = source[jsonataExpression];
|
|
547
|
+
continue;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
try {
|
|
551
|
+
const compiled = jsonata(jsonataExpression);
|
|
552
|
+
result[key] = await compiled.evaluate(source);
|
|
553
|
+
} catch (error) {
|
|
554
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
555
|
+
throw new Error(`[step-machine-cli] Step "${stepName}" ${label}.${key} failed: ${msg}`);
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
return result;
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
function applyCommandTemplate(command, source, stepName) {
|
|
563
|
+
if (typeof command !== 'string' || command.trim().length === 0) {
|
|
564
|
+
throw new Error(`[step-machine-cli] Step "${stepName}" handler.cli must be a non-empty command string.`);
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
return command.replace(/%%([A-Za-z0-9_-]+)%%/g, (full, key) => {
|
|
568
|
+
if (!Object.prototype.hasOwnProperty.call(source, key)) {
|
|
569
|
+
throw new Error(`[step-machine-cli] Step "${stepName}" command placeholder ${full} has no matching input or input-transform value.`);
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
const value = source[key];
|
|
573
|
+
if (value === undefined || value === null) {
|
|
574
|
+
throw new Error(`[step-machine-cli] Step "${stepName}" command placeholder ${full} resolved to empty value.`);
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
return String(value);
|
|
578
|
+
});
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
function createPassthroughHandler() {
|
|
582
|
+
return async (input) => {
|
|
583
|
+
const data = input && typeof input === 'object' && !Array.isArray(input)
|
|
584
|
+
? input
|
|
585
|
+
: {};
|
|
586
|
+
|
|
587
|
+
return {
|
|
588
|
+
result: 'success',
|
|
589
|
+
data,
|
|
590
|
+
};
|
|
591
|
+
};
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
function parseJsonOutput(stdout) {
|
|
595
|
+
const trimmed = stdout.trim();
|
|
596
|
+
if (!trimmed) {
|
|
597
|
+
throw new Error('empty stdout');
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
try {
|
|
601
|
+
return JSON.parse(trimmed);
|
|
602
|
+
} catch {
|
|
603
|
+
const lines = trimmed.split(/\r?\n/).filter(Boolean);
|
|
604
|
+
const last = lines[lines.length - 1];
|
|
605
|
+
return JSON.parse(last);
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
function wrapWithOutputFiltering(handler, produces) {
|
|
610
|
+
return async (input, context) => {
|
|
611
|
+
const raw = await handler(input, context);
|
|
612
|
+
const normalized = normalizeHandlerResult(raw, context?.stepName ?? 'unknown');
|
|
613
|
+
const filteredData = filterProducedData(normalized.data, produces);
|
|
614
|
+
return {
|
|
615
|
+
result: normalized.result,
|
|
616
|
+
data: filteredData,
|
|
617
|
+
};
|
|
618
|
+
};
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
function normalizeHandlerResult(raw, stepName) {
|
|
622
|
+
if (!raw || typeof raw !== 'object') {
|
|
623
|
+
throw new Error(`[step-machine-cli] Step "${stepName}" returned a non-object result.`);
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
const result = raw.result ?? raw.status;
|
|
627
|
+
if (typeof result !== 'string' || result.trim().length === 0) {
|
|
628
|
+
throw new Error(`[step-machine-cli] Step "${stepName}" result must include a non-empty "result" (or "status") string.`);
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
const data = raw.data && typeof raw.data === 'object' && !Array.isArray(raw.data)
|
|
632
|
+
? raw.data
|
|
633
|
+
: {};
|
|
634
|
+
|
|
635
|
+
const error = typeof raw.error === 'string' ? raw.error : undefined;
|
|
636
|
+
if (error && !('error' in data)) {
|
|
637
|
+
data.error = error;
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
return { result, data };
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
function filterProducedData(data, produces) {
|
|
644
|
+
if (!produces || produces.length === 0) {
|
|
645
|
+
return data;
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
const filtered = {};
|
|
649
|
+
for (const key of produces) {
|
|
650
|
+
if (Object.prototype.hasOwnProperty.call(data, key)) {
|
|
651
|
+
filtered[key] = data[key];
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
return filtered;
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
function printUsage() {
|
|
658
|
+
console.error('Usage: step-machine-cli <step-flow.yaml> [--handlers <step-handlers.js>] [--initial-data <json>] [--store <memory|file>] [--store-dir <directory>] [--resume]');
|
|
659
|
+
console.error(' step-machine-cli --store file --store-dir <directory> --pause');
|
|
660
|
+
console.error(' step-machine-cli --store file --store-dir <directory> --status');
|
|
661
|
+
console.error('');
|
|
662
|
+
console.error('Example:');
|
|
663
|
+
console.error(' step-machine-cli examples/step-machine-demo/two-step-mixed.flow.yaml --handlers examples/step-machine-demo/two-step-mixed-handlers.js --initial-data "{\"a\":3,\"b\":4}"');
|
|
664
|
+
console.error(' step-machine-cli ./flow.yaml --store file --store-dir ./.runs');
|
|
665
|
+
console.error(' step-machine-cli ./flow.yaml --store file --store-dir ./.runs --resume');
|
|
666
|
+
console.error(' step-machine-cli --store file --store-dir ./.runs --pause');
|
|
667
|
+
console.error(' step-machine-cli --store file --store-dir ./.runs --status');
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
main().catch((error) => {
|
|
671
|
+
const msg = error instanceof Error ? error.stack ?? error.message : String(error);
|
|
672
|
+
console.error(msg);
|
|
673
|
+
process.exit(1);
|
|
674
|
+
});
|