@relayflows/sdk 2.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/dist/authored-flow-error.d.ts +9 -0
- package/dist/authored-flow-error.d.ts.map +1 -0
- package/dist/authored-flow-error.js +13 -0
- package/dist/authored-flow-error.js.map +1 -0
- package/dist/authored-flow-executor.d.ts +28 -0
- package/dist/authored-flow-executor.d.ts.map +1 -0
- package/dist/authored-flow-executor.js +203 -0
- package/dist/authored-flow-executor.js.map +1 -0
- package/dist/authored-flow-lifecycle.d.ts +61 -0
- package/dist/authored-flow-lifecycle.d.ts.map +1 -0
- package/dist/authored-flow-lifecycle.js +284 -0
- package/dist/authored-flow-lifecycle.js.map +1 -0
- package/dist/authored-flow-loader.d.ts +7 -0
- package/dist/authored-flow-loader.d.ts.map +1 -0
- package/dist/authored-flow-loader.js +39 -0
- package/dist/authored-flow-loader.js.map +1 -0
- package/dist/authored-flow-operation.d.ts +43 -0
- package/dist/authored-flow-operation.d.ts.map +1 -0
- package/dist/authored-flow-operation.js +176 -0
- package/dist/authored-flow-operation.js.map +1 -0
- package/dist/authored-flow.d.ts +12 -0
- package/dist/authored-flow.d.ts.map +1 -0
- package/dist/authored-flow.js +13 -0
- package/dist/authored-flow.js.map +1 -0
- package/dist/authored-promise-graph.d.ts +67 -0
- package/dist/authored-promise-graph.d.ts.map +1 -0
- package/dist/authored-promise-graph.js +206 -0
- package/dist/authored-promise-graph.js.map +1 -0
- package/dist/backlog-picker.d.ts +55 -0
- package/dist/backlog-picker.d.ts.map +1 -0
- package/dist/backlog-picker.js +117 -0
- package/dist/backlog-picker.js.map +1 -0
- package/dist/canonical.d.ts +18 -0
- package/dist/canonical.d.ts.map +1 -0
- package/dist/canonical.js +55 -0
- package/dist/canonical.js.map +1 -0
- package/dist/cli/check.d.ts +30 -0
- package/dist/cli/check.d.ts.map +1 -0
- package/dist/cli/check.js +324 -0
- package/dist/cli/check.js.map +1 -0
- package/dist/cli/direct-run.d.ts +3 -0
- package/dist/cli/direct-run.d.ts.map +1 -0
- package/dist/cli/direct-run.js +69 -0
- package/dist/cli/direct-run.js.map +1 -0
- package/dist/cli/hn-monitor.d.ts +107 -0
- package/dist/cli/hn-monitor.d.ts.map +1 -0
- package/dist/cli/hn-monitor.js +220 -0
- package/dist/cli/hn-monitor.js.map +1 -0
- package/dist/cli/interruptible-sleep.d.ts +13 -0
- package/dist/cli/interruptible-sleep.d.ts.map +1 -0
- package/dist/cli/interruptible-sleep.js +31 -0
- package/dist/cli/interruptible-sleep.js.map +1 -0
- package/dist/cli/run.d.ts +54 -0
- package/dist/cli/run.d.ts.map +1 -0
- package/dist/cli/run.js +316 -0
- package/dist/cli/run.js.map +1 -0
- package/dist/cli/tick-runner.d.ts +151 -0
- package/dist/cli/tick-runner.d.ts.map +1 -0
- package/dist/cli/tick-runner.js +258 -0
- package/dist/cli/tick-runner.js.map +1 -0
- package/dist/cli-adapter.d.ts +29 -0
- package/dist/cli-adapter.d.ts.map +1 -0
- package/dist/cli-adapter.js +92 -0
- package/dist/cli-adapter.js.map +1 -0
- package/dist/cli-executable.d.ts +3 -0
- package/dist/cli-executable.d.ts.map +1 -0
- package/dist/cli-executable.js +10 -0
- package/dist/cli-executable.js.map +1 -0
- package/dist/cli.d.ts +9 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +348 -0
- package/dist/cli.js.map +1 -0
- package/dist/compile.d.ts +42 -0
- package/dist/compile.d.ts.map +1 -0
- package/dist/compile.js +515 -0
- package/dist/compile.js.map +1 -0
- package/dist/demo-hn-monitor.d.ts +2 -0
- package/dist/demo-hn-monitor.d.ts.map +1 -0
- package/dist/demo-hn-monitor.js +118 -0
- package/dist/demo-hn-monitor.js.map +1 -0
- package/dist/dir-watcher-poller.d.ts +73 -0
- package/dist/dir-watcher-poller.d.ts.map +1 -0
- package/dist/dir-watcher-poller.js +80 -0
- package/dist/dir-watcher-poller.js.map +1 -0
- package/dist/direct-input.d.ts +10 -0
- package/dist/direct-input.d.ts.map +1 -0
- package/dist/direct-input.js +61 -0
- package/dist/direct-input.js.map +1 -0
- package/dist/failure-kinds.d.ts +25 -0
- package/dist/failure-kinds.d.ts.map +1 -0
- package/dist/failure-kinds.js +64 -0
- package/dist/failure-kinds.js.map +1 -0
- package/dist/gate-contract.d.ts +24 -0
- package/dist/gate-contract.d.ts.map +1 -0
- package/dist/gate-contract.js +50 -0
- package/dist/gate-contract.js.map +1 -0
- package/dist/hn-poller.d.ts +50 -0
- package/dist/hn-poller.d.ts.map +1 -0
- package/dist/hn-poller.js +79 -0
- package/dist/hn-poller.js.map +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +30 -0
- package/dist/index.js.map +1 -0
- package/dist/journal-client.d.ts +122 -0
- package/dist/journal-client.d.ts.map +1 -0
- package/dist/journal-client.js +277 -0
- package/dist/journal-client.js.map +1 -0
- package/dist/json-schema-bound.d.ts +7 -0
- package/dist/json-schema-bound.d.ts.map +1 -0
- package/dist/json-schema-bound.js +353 -0
- package/dist/json-schema-bound.js.map +1 -0
- package/dist/json-schema.d.ts +5 -0
- package/dist/json-schema.d.ts.map +1 -0
- package/dist/json-schema.js +72 -0
- package/dist/json-schema.js.map +1 -0
- package/dist/json-value.d.ts +6 -0
- package/dist/json-value.d.ts.map +1 -0
- package/dist/json-value.js +94 -0
- package/dist/json-value.js.map +1 -0
- package/dist/model-name.d.ts +7 -0
- package/dist/model-name.d.ts.map +1 -0
- package/dist/model-name.js +21 -0
- package/dist/model-name.js.map +1 -0
- package/dist/output-schema.d.ts +8 -0
- package/dist/output-schema.d.ts.map +1 -0
- package/dist/output-schema.js +28 -0
- package/dist/output-schema.js.map +1 -0
- package/dist/preflight.d.ts +80 -0
- package/dist/preflight.d.ts.map +1 -0
- package/dist/preflight.js +361 -0
- package/dist/preflight.js.map +1 -0
- package/dist/protocol.d.ts +359 -0
- package/dist/protocol.d.ts.map +1 -0
- package/dist/protocol.js +15 -0
- package/dist/protocol.js.map +1 -0
- package/dist/spec.d.ts +294 -0
- package/dist/spec.d.ts.map +1 -0
- package/dist/spec.js +13 -0
- package/dist/spec.js.map +1 -0
- package/dist/step-dependencies.d.ts +2 -0
- package/dist/step-dependencies.d.ts.map +1 -0
- package/dist/step-dependencies.js +88 -0
- package/dist/step-dependencies.js.map +1 -0
- package/dist/step-fields.d.ts +17 -0
- package/dist/step-fields.d.ts.map +1 -0
- package/dist/step-fields.js +32 -0
- package/dist/step-fields.js.map +1 -0
- package/dist/tick-source.d.ts +205 -0
- package/dist/tick-source.d.ts.map +1 -0
- package/dist/tick-source.js +234 -0
- package/dist/tick-source.js.map +1 -0
- package/dist/unknown-keys.d.ts +7 -0
- package/dist/unknown-keys.d.ts.map +1 -0
- package/dist/unknown-keys.js +47 -0
- package/dist/unknown-keys.js.map +1 -0
- package/dist/validate.d.ts +7 -0
- package/dist/validate.d.ts.map +1 -0
- package/dist/validate.js +458 -0
- package/dist/validate.js.map +1 -0
- package/dist/work-package-consumer.d.ts +33 -0
- package/dist/work-package-consumer.d.ts.map +1 -0
- package/dist/work-package-consumer.js +44 -0
- package/dist/work-package-consumer.js.map +1 -0
- package/dist/work-package-validator.d.ts +15 -0
- package/dist/work-package-validator.d.ts.map +1 -0
- package/dist/work-package-validator.js +72 -0
- package/dist/work-package-validator.js.map +1 -0
- package/dist/worker-cli.d.ts +16 -0
- package/dist/worker-cli.d.ts.map +1 -0
- package/dist/worker-cli.js +77 -0
- package/dist/worker-cli.js.map +1 -0
- package/dist/worker.d.ts +57 -0
- package/dist/worker.d.ts.map +1 -0
- package/dist/worker.js +129 -0
- package/dist/worker.js.map +1 -0
- package/dist/wrapper-runtime.d.ts +10 -0
- package/dist/wrapper-runtime.d.ts.map +1 -0
- package/dist/wrapper-runtime.js +83 -0
- package/dist/wrapper-runtime.js.map +1 -0
- package/dist/wrapper-session.d.ts +16 -0
- package/dist/wrapper-session.d.ts.map +1 -0
- package/dist/wrapper-session.js +244 -0
- package/dist/wrapper-session.js.map +1 -0
- package/package.json +50 -0
- package/src/authored-flow-error.ts +31 -0
- package/src/authored-flow-executor.ts +365 -0
- package/src/authored-flow-lifecycle.ts +344 -0
- package/src/authored-flow-loader.ts +44 -0
- package/src/authored-flow-operation.ts +252 -0
- package/src/authored-flow.ts +21 -0
- package/src/authored-promise-graph.ts +196 -0
- package/src/backlog-picker.ts +153 -0
- package/src/canonical.ts +57 -0
- package/src/cli/check.ts +391 -0
- package/src/cli/direct-run.ts +86 -0
- package/src/cli/hn-monitor.ts +286 -0
- package/src/cli/interruptible-sleep.ts +27 -0
- package/src/cli/run.ts +439 -0
- package/src/cli/tick-runner.ts +378 -0
- package/src/cli-adapter.ts +115 -0
- package/src/cli-executable.ts +12 -0
- package/src/cli.ts +364 -0
- package/src/compile.ts +573 -0
- package/src/demo-hn-monitor.ts +132 -0
- package/src/dir-watcher-poller.ts +122 -0
- package/src/direct-input.ts +69 -0
- package/src/failure-kinds.ts +77 -0
- package/src/gate-contract.ts +72 -0
- package/src/hn-poller.ts +104 -0
- package/src/index.ts +195 -0
- package/src/journal-client.ts +379 -0
- package/src/json-schema-bound.ts +357 -0
- package/src/json-schema.ts +65 -0
- package/src/json-value.ts +110 -0
- package/src/model-name.ts +20 -0
- package/src/output-schema.ts +33 -0
- package/src/preflight.ts +482 -0
- package/src/protocol.ts +364 -0
- package/src/spec.ts +343 -0
- package/src/step-dependencies.ts +100 -0
- package/src/step-fields.ts +36 -0
- package/src/tick-source.ts +334 -0
- package/src/unknown-keys.ts +52 -0
- package/src/validate.ts +503 -0
- package/src/work-package-consumer.ts +73 -0
- package/src/work-package-validator.ts +90 -0
- package/src/worker-cli.ts +110 -0
- package/src/worker.ts +150 -0
- package/src/wrapper-runtime.ts +93 -0
- package/src/wrapper-session.ts +287 -0
package/src/cli.ts
ADDED
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { realpathSync } from 'node:fs';
|
|
4
|
+
import { pathToFileURL } from 'node:url';
|
|
5
|
+
import {
|
|
6
|
+
checkFlow,
|
|
7
|
+
inputFailureReport,
|
|
8
|
+
type CheckReport,
|
|
9
|
+
} from './cli/check.js';
|
|
10
|
+
import {
|
|
11
|
+
resumeFlow,
|
|
12
|
+
runFlow,
|
|
13
|
+
type RunExecution,
|
|
14
|
+
type RunReport,
|
|
15
|
+
} from './cli/run.js';
|
|
16
|
+
import { runDirectFlow } from './cli/direct-run.js';
|
|
17
|
+
import { isAuthoredFlowPath } from './direct-input.js';
|
|
18
|
+
import { runHnMonitor } from './cli/hn-monitor.js';
|
|
19
|
+
import { runTickRunner } from './cli/tick-runner.js';
|
|
20
|
+
|
|
21
|
+
export type { CheckInputDiagnostic, CheckReport } from './cli/check.js';
|
|
22
|
+
|
|
23
|
+
export interface CliIo {
|
|
24
|
+
stdout(line: string): void;
|
|
25
|
+
stderr(line: string): void;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
type CliExitCode = 0 | 1 | 2 | 3;
|
|
29
|
+
type ParsedArgs =
|
|
30
|
+
| { command: 'check'; json: boolean; value: string }
|
|
31
|
+
| { command: 'run'; dataDir: string; input: string | undefined; json: boolean; value: string }
|
|
32
|
+
| { command: 'resume'; dataDir: string; json: boolean; value: string }
|
|
33
|
+
| { command: 'hn-monitor'; sub: 'start'; dataDir: string; specPath: string; pollIntervalMs: number | undefined }
|
|
34
|
+
| { command: 'tick'; sub: 'start'; dataDir: string; specPath: string; scheduleId: string;
|
|
35
|
+
intervalMs: number; epochMs: number | undefined; maxCatchUp: number | undefined;
|
|
36
|
+
pollIntervalMs: number | undefined };
|
|
37
|
+
|
|
38
|
+
const DEFAULT_DATA_DIR = '.relayflowd';
|
|
39
|
+
const USAGE = [
|
|
40
|
+
'Usage:',
|
|
41
|
+
'flows check [--json] <flow.yaml|spec.json>',
|
|
42
|
+
'flows run [--json] [--data-dir <dir>] <flow.yaml|spec.json>',
|
|
43
|
+
'flows run [--json] [--data-dir <dir>] <flow.ts> --input <inline-json-or-file>',
|
|
44
|
+
'flows tick start --schedule-id <id> --interval-ms <ms> [--epoch-ms <ms>] [--max-catch-up <n>] [--poll-interval-ms <ms>] [--data-dir <dir>] <spec.json>',
|
|
45
|
+
'flows resume [--json] [--data-dir <dir>] <run-id>',
|
|
46
|
+
'flows hn-monitor start [--data-dir <dir>] [--poll-interval-ms <n>] <spec.json>',
|
|
47
|
+
].join(' ');
|
|
48
|
+
|
|
49
|
+
const PROCESS_IO: CliIo = {
|
|
50
|
+
stdout: (line) => process.stdout.write(`${line}\n`),
|
|
51
|
+
stderr: (line) => process.stderr.write(`${line}\n`),
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export async function runCli(
|
|
55
|
+
args: readonly string[],
|
|
56
|
+
io: CliIo = PROCESS_IO,
|
|
57
|
+
): Promise<CliExitCode> {
|
|
58
|
+
const parsed = parseArgs(args);
|
|
59
|
+
if (parsed === undefined) {
|
|
60
|
+
const report = inputFailureReport({ kind: 'invalid_invocation', message: USAGE });
|
|
61
|
+
emitCheckReport(report, args.includes('--json'), io);
|
|
62
|
+
return 2;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (parsed.command === 'check') {
|
|
66
|
+
const checked = checkFlow(parsed.value);
|
|
67
|
+
emitCheckReport(checked.report, parsed.json, io);
|
|
68
|
+
return checked.report.ok ? 0 : 2;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (parsed.command === 'hn-monitor') {
|
|
72
|
+
const controller = new AbortController();
|
|
73
|
+
const onSignal = (): void => controller.abort();
|
|
74
|
+
process.once('SIGINT', onSignal);
|
|
75
|
+
process.once('SIGTERM', onSignal);
|
|
76
|
+
try {
|
|
77
|
+
return await runHnMonitor({
|
|
78
|
+
dataDir: parsed.dataDir,
|
|
79
|
+
specPath: parsed.specPath,
|
|
80
|
+
pollIntervalMs: parsed.pollIntervalMs,
|
|
81
|
+
signal: controller.signal,
|
|
82
|
+
}, io);
|
|
83
|
+
} finally {
|
|
84
|
+
process.off('SIGINT', onSignal);
|
|
85
|
+
process.off('SIGTERM', onSignal);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (parsed.command === 'tick') {
|
|
90
|
+
const controller = new AbortController();
|
|
91
|
+
const onSignal = (): void => controller.abort();
|
|
92
|
+
process.once('SIGINT', onSignal);
|
|
93
|
+
process.once('SIGTERM', onSignal);
|
|
94
|
+
try {
|
|
95
|
+
return await runTickRunner({
|
|
96
|
+
dataDir: parsed.dataDir,
|
|
97
|
+
specPath: parsed.specPath,
|
|
98
|
+
schedule: {
|
|
99
|
+
scheduleId: parsed.scheduleId,
|
|
100
|
+
intervalMs: parsed.intervalMs,
|
|
101
|
+
...(parsed.epochMs === undefined ? {} : { epochMs: parsed.epochMs }),
|
|
102
|
+
...(parsed.maxCatchUp === undefined ? {} : { maxCatchUp: parsed.maxCatchUp }),
|
|
103
|
+
},
|
|
104
|
+
pollIntervalMs: parsed.pollIntervalMs,
|
|
105
|
+
signal: controller.signal,
|
|
106
|
+
}, io) as CliExitCode;
|
|
107
|
+
} finally {
|
|
108
|
+
process.off('SIGINT', onSignal);
|
|
109
|
+
process.off('SIGTERM', onSignal);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const execution = parsed.command === 'run'
|
|
114
|
+
? isAuthoredFlowPath(parsed.value)
|
|
115
|
+
? await runDirectFlow(
|
|
116
|
+
parsed.value,
|
|
117
|
+
parsed.input,
|
|
118
|
+
parsed.dataDir,
|
|
119
|
+
{ onWait: (progress) => emitWait(progress, io) },
|
|
120
|
+
)
|
|
121
|
+
: await runFlow(parsed.value, parsed.dataDir, { onWait: (progress) => emitWait(progress, io) })
|
|
122
|
+
: await resumeFlow(parsed.value, parsed.dataDir, { onWait: (progress) => emitWait(progress, io) });
|
|
123
|
+
emitRunReport(execution, parsed.json, io);
|
|
124
|
+
return execution.exitCode;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function emitWait(
|
|
128
|
+
progress: { runId: string; stepId: string; stepType: string; leaseDeadlineMs: number },
|
|
129
|
+
io: CliIo,
|
|
130
|
+
): void {
|
|
131
|
+
io.stderr(
|
|
132
|
+
`WAITING [worker_lease] Run "${progress.runId}" step "${progress.stepId}" (${progress.stepType}) `
|
|
133
|
+
+ `is running under a worker lease until ${progress.leaseDeadlineMs}.`,
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function parseArgs(args: readonly string[]): ParsedArgs | undefined {
|
|
138
|
+
const command = args[0];
|
|
139
|
+
if (command === 'hn-monitor') return parseHnMonitorArgs(args.slice(1));
|
|
140
|
+
if (command === 'tick') return parseTickArgs(args.slice(1));
|
|
141
|
+
if (command !== 'check' && command !== 'run' && command !== 'resume') return undefined;
|
|
142
|
+
|
|
143
|
+
let json = false;
|
|
144
|
+
let dataDir = DEFAULT_DATA_DIR;
|
|
145
|
+
let sawDataDir = false;
|
|
146
|
+
let input: string | undefined;
|
|
147
|
+
let sawInput = false;
|
|
148
|
+
const positionals: string[] = [];
|
|
149
|
+
for (let index = 1; index < args.length; index += 1) {
|
|
150
|
+
const argument = args[index]!;
|
|
151
|
+
if (argument === '--json') {
|
|
152
|
+
if (json) return undefined;
|
|
153
|
+
json = true;
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
if (argument === '--data-dir') {
|
|
157
|
+
const value = args[index + 1];
|
|
158
|
+
if (command === 'check' || sawDataDir || value === undefined || value.startsWith('-')) return undefined;
|
|
159
|
+
dataDir = value;
|
|
160
|
+
sawDataDir = true;
|
|
161
|
+
index += 1;
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
if (argument === '--input') {
|
|
165
|
+
const value = args[index + 1];
|
|
166
|
+
if (command !== 'run' || sawInput || value === undefined || value.startsWith('--')) return undefined;
|
|
167
|
+
input = value;
|
|
168
|
+
sawInput = true;
|
|
169
|
+
index += 1;
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
if (argument.startsWith('-')) return undefined;
|
|
173
|
+
positionals.push(argument);
|
|
174
|
+
}
|
|
175
|
+
if (positionals.length !== 1) return undefined;
|
|
176
|
+
|
|
177
|
+
if (command === 'run' && input !== undefined && !isAuthoredFlowPath(positionals[0]!)) return undefined;
|
|
178
|
+
return command === 'check'
|
|
179
|
+
? { command, json, value: positionals[0]! }
|
|
180
|
+
: command === 'run'
|
|
181
|
+
? { command, dataDir, input, json, value: positionals[0]! }
|
|
182
|
+
: { command, dataDir, json, value: positionals[0]! };
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function parseHnMonitorArgs(rest: readonly string[]): ParsedArgs | undefined {
|
|
186
|
+
const sub = rest[0];
|
|
187
|
+
if (sub !== 'start') return undefined;
|
|
188
|
+
|
|
189
|
+
let dataDir = DEFAULT_DATA_DIR;
|
|
190
|
+
let sawDataDir = false;
|
|
191
|
+
let pollIntervalMs: number | undefined;
|
|
192
|
+
const positionals: string[] = [];
|
|
193
|
+
for (let index = 1; index < rest.length; index += 1) {
|
|
194
|
+
const argument = rest[index]!;
|
|
195
|
+
if (argument === '--data-dir') {
|
|
196
|
+
const value = rest[index + 1];
|
|
197
|
+
if (sawDataDir || value === undefined || value.startsWith('-')) return undefined;
|
|
198
|
+
dataDir = value;
|
|
199
|
+
sawDataDir = true;
|
|
200
|
+
index += 1;
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
if (argument === '--poll-interval-ms') {
|
|
204
|
+
const value = rest[index + 1];
|
|
205
|
+
if (pollIntervalMs !== undefined || value === undefined || value.startsWith('-')) return undefined;
|
|
206
|
+
const parsed = Number.parseInt(value, 10);
|
|
207
|
+
if (!Number.isFinite(parsed) || parsed <= 0) return undefined;
|
|
208
|
+
pollIntervalMs = parsed;
|
|
209
|
+
index += 1;
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
212
|
+
if (argument.startsWith('-')) return undefined;
|
|
213
|
+
positionals.push(argument);
|
|
214
|
+
}
|
|
215
|
+
if (positionals.length !== 1) return undefined;
|
|
216
|
+
return { command: 'hn-monitor', sub: 'start', dataDir, specPath: positionals[0]!, pollIntervalMs };
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* `flows tick start`. Numeric flags are parsed here but their BOUNDS are not
|
|
221
|
+
* re-derived: `runTickRunner` calls `assertTickScheduleValid`, the same
|
|
222
|
+
* function `emitDueTicks` uses, so the CLI's refusal and the emit path's
|
|
223
|
+
* refusal cannot drift. This parser only rejects shapes it cannot turn into a
|
|
224
|
+
* number at all.
|
|
225
|
+
*/
|
|
226
|
+
function parseTickArgs(rest: readonly string[]): ParsedArgs | undefined {
|
|
227
|
+
const sub = rest[0];
|
|
228
|
+
if (sub !== 'start') return undefined;
|
|
229
|
+
|
|
230
|
+
let dataDir = DEFAULT_DATA_DIR;
|
|
231
|
+
let sawDataDir = false;
|
|
232
|
+
let scheduleId: string | undefined;
|
|
233
|
+
let intervalMs: number | undefined;
|
|
234
|
+
let epochMs: number | undefined;
|
|
235
|
+
let maxCatchUp: number | undefined;
|
|
236
|
+
let pollIntervalMs: number | undefined;
|
|
237
|
+
const positionals: string[] = [];
|
|
238
|
+
|
|
239
|
+
// Number, not parseInt: parseInt('1.5') is 1, so a fractional --interval-ms
|
|
240
|
+
// would silently become a 1ms schedule instead of being refused. Requiring
|
|
241
|
+
// an exact integer round-trip rejects '1.5', '1e3', '0x10' and ' 1' rather
|
|
242
|
+
// than coercing them into something the operator did not write.
|
|
243
|
+
const takeNumber = (value: string | undefined): number | undefined => {
|
|
244
|
+
if (value === undefined || value.startsWith('-')) return undefined;
|
|
245
|
+
const parsed = Number(value);
|
|
246
|
+
if (!Number.isInteger(parsed) || String(parsed) !== value) return undefined;
|
|
247
|
+
return parsed;
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
for (let index = 1; index < rest.length; index += 1) {
|
|
251
|
+
const argument = rest[index]!;
|
|
252
|
+
if (argument === '--data-dir') {
|
|
253
|
+
const value = rest[index + 1];
|
|
254
|
+
if (sawDataDir || value === undefined || value.startsWith('-')) return undefined;
|
|
255
|
+
dataDir = value; sawDataDir = true; index += 1; continue;
|
|
256
|
+
}
|
|
257
|
+
if (argument === '--schedule-id') {
|
|
258
|
+
const value = rest[index + 1];
|
|
259
|
+
if (scheduleId !== undefined || value === undefined || value.startsWith('-')) return undefined;
|
|
260
|
+
scheduleId = value; index += 1; continue;
|
|
261
|
+
}
|
|
262
|
+
if (argument === '--interval-ms') {
|
|
263
|
+
if (intervalMs !== undefined) return undefined;
|
|
264
|
+
const value = takeNumber(rest[index + 1]);
|
|
265
|
+
if (value === undefined) return undefined;
|
|
266
|
+
intervalMs = value; index += 1; continue;
|
|
267
|
+
}
|
|
268
|
+
if (argument === '--epoch-ms') {
|
|
269
|
+
if (epochMs !== undefined) return undefined;
|
|
270
|
+
const value = takeNumber(rest[index + 1]);
|
|
271
|
+
if (value === undefined) return undefined;
|
|
272
|
+
epochMs = value; index += 1; continue;
|
|
273
|
+
}
|
|
274
|
+
if (argument === '--max-catch-up') {
|
|
275
|
+
if (maxCatchUp !== undefined) return undefined;
|
|
276
|
+
const value = takeNumber(rest[index + 1]);
|
|
277
|
+
if (value === undefined) return undefined;
|
|
278
|
+
maxCatchUp = value; index += 1; continue;
|
|
279
|
+
}
|
|
280
|
+
if (argument === '--poll-interval-ms') {
|
|
281
|
+
if (pollIntervalMs !== undefined) return undefined;
|
|
282
|
+
const value = takeNumber(rest[index + 1]);
|
|
283
|
+
if (value === undefined) return undefined;
|
|
284
|
+
pollIntervalMs = value; index += 1; continue;
|
|
285
|
+
}
|
|
286
|
+
if (argument.startsWith('-')) return undefined;
|
|
287
|
+
positionals.push(argument);
|
|
288
|
+
}
|
|
289
|
+
if (positionals.length !== 1 || scheduleId === undefined || intervalMs === undefined) return undefined;
|
|
290
|
+
return {
|
|
291
|
+
command: 'tick', sub: 'start', dataDir, specPath: positionals[0]!,
|
|
292
|
+
scheduleId, intervalMs, epochMs, maxCatchUp, pollIntervalMs,
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function emitCheckReport(report: CheckReport, json: boolean, io: CliIo): void {
|
|
297
|
+
emitDiagnostics(report.diagnostics, io);
|
|
298
|
+
if (json) {
|
|
299
|
+
io.stdout(JSON.stringify(report));
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
for (const gate of report.gates) {
|
|
303
|
+
// A gate that accepts every output is legal, but it must not read like a
|
|
304
|
+
// gate that judges something.
|
|
305
|
+
const vacuous = gate.acceptsAnyOutput === true ? ' [json_schema accepts any output]' : '';
|
|
306
|
+
io.stdout(`GATE step "${gate.stepId}" ${gate.checks.join('+')} from data (kernel, journal-replayable)${vacuous}`);
|
|
307
|
+
}
|
|
308
|
+
for (const resolution of report.resolutions) {
|
|
309
|
+
const config = resolution.source === 'project' && report.projectConfigPath !== undefined
|
|
310
|
+
? ` (${report.projectConfigPath})`
|
|
311
|
+
: '';
|
|
312
|
+
const model = resolution.model === undefined ? '' : ` model "${resolution.model}"`;
|
|
313
|
+
io.stdout(`RESOLVED step "${resolution.stepId}" cli "${resolution.cli}"${model} from ${resolution.source}${config}`);
|
|
314
|
+
}
|
|
315
|
+
if (report.ok) io.stdout(`CHECK PASSED ${report.path ?? ''}`.trimEnd());
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function emitRunReport(execution: RunExecution, json: boolean, io: CliIo): void {
|
|
319
|
+
const { report } = execution;
|
|
320
|
+
emitDiagnostics(report.diagnostics, io);
|
|
321
|
+
if (json) {
|
|
322
|
+
io.stdout(JSON.stringify(report));
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
if (report.runId === undefined) return;
|
|
326
|
+
const completed = report.completedSteps === undefined ? '' : ` (${report.completedSteps} steps)`;
|
|
327
|
+
const reason = report.completionReason === undefined
|
|
328
|
+
? ''
|
|
329
|
+
: ` completionReason: ${report.completionReason}`;
|
|
330
|
+
io.stdout(`RUN ${report.runId} ${report.status ?? 'unknown'}${completed}${reason}`);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
function emitDiagnostics(
|
|
334
|
+
diagnostics: CheckReport['diagnostics'] | RunReport['diagnostics'],
|
|
335
|
+
io: CliIo,
|
|
336
|
+
): void {
|
|
337
|
+
for (const diagnostic of diagnostics) {
|
|
338
|
+
io.stderr(`${diagnosticLabel(diagnostic.severity)} [${diagnostic.kind}] ${diagnostic.message}`);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
function diagnosticLabel(severity: string): string {
|
|
343
|
+
switch (severity) {
|
|
344
|
+
case 'warning': return 'WARNING';
|
|
345
|
+
case 'failure': return 'FAILED';
|
|
346
|
+
case 'parked': return 'PARKED';
|
|
347
|
+
default: return 'REFUSED';
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
function isDirectInvocation(entryPath: string | undefined): boolean {
|
|
352
|
+
if (entryPath === undefined) return false;
|
|
353
|
+
try {
|
|
354
|
+
return pathToFileURL(realpathSync(entryPath)).href === import.meta.url;
|
|
355
|
+
} catch {
|
|
356
|
+
return false;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
if (isDirectInvocation(process.argv[1])) {
|
|
361
|
+
void runCli(process.argv.slice(2)).then((exitCode) => {
|
|
362
|
+
process.exitCode = exitCode;
|
|
363
|
+
});
|
|
364
|
+
}
|