@workflow-manager/runner 0.1.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 +196 -0
- package/dist/engine.d.ts +2 -0
- package/dist/engine.js +217 -0
- package/dist/events.d.ts +7 -0
- package/dist/events.js +21 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +413 -0
- package/dist/mockExecutor.d.ts +2 -0
- package/dist/mockExecutor.js +110 -0
- package/dist/opencodeExecutor.d.ts +3 -0
- package/dist/opencodeExecutor.js +174 -0
- package/dist/parser.d.ts +5 -0
- package/dist/parser.js +101 -0
- package/dist/remote/api.d.ts +99 -0
- package/dist/remote/api.js +81 -0
- package/dist/remote/commands.d.ts +8 -0
- package/dist/remote/commands.js +183 -0
- package/dist/remote/config.d.ts +8 -0
- package/dist/remote/config.js +39 -0
- package/dist/remote/telemetry.d.ts +12 -0
- package/dist/remote/telemetry.js +69 -0
- package/dist/remote/types.d.ts +7 -0
- package/dist/remote/types.js +1 -0
- package/dist/types.d.ts +122 -0
- package/dist/types.js +1 -0
- package/man/wfm.1 +96 -0
- package/package.json +65 -0
- package/skills/README.md +63 -0
- package/skills/workflow-manager-cli/README.md +11 -0
- package/skills/workflow-manager-cli/SKILL.md +111 -0
package/README.md
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
# workflow-manager
|
|
2
|
+
|
|
3
|
+
CLI runner for in-memory and Markdown/JSON workflow orchestration.
|
|
4
|
+
|
|
5
|
+
## What it does
|
|
6
|
+
|
|
7
|
+
- Parses workflow definitions from Markdown frontmatter or JSON
|
|
8
|
+
- Validates structure, dependencies, adapters, and validation modes
|
|
9
|
+
- Executes workflow steps with deterministic run state transitions
|
|
10
|
+
- Emits a full event timeline and JSON run result
|
|
11
|
+
- Records authenticated CLI run telemetry for success, failure, and workflow effectiveness
|
|
12
|
+
- Publishes and pulls shared workflows from the remote registry
|
|
13
|
+
|
|
14
|
+
## Architecture
|
|
15
|
+
|
|
16
|
+
- `src/index.ts`: CLI commands (`questions`, `scaffold`, `validate`, `run`)
|
|
17
|
+
- `src/parser.ts`: parsing + validation
|
|
18
|
+
- `src/engine.ts`: execution loop, confirmations, retries, rollback/restart
|
|
19
|
+
- `src/mockExecutor.ts`: mock step executor for simulation
|
|
20
|
+
- `src/events.ts`: event sequencing/logging
|
|
21
|
+
- `src/types.ts`: contracts and status enums
|
|
22
|
+
- `apps/remote-registry/`: React + Vite remote registry app
|
|
23
|
+
- `supabase/`: migrations, local stack config, and Edge Functions
|
|
24
|
+
- `doc/`: VitePress documentation site
|
|
25
|
+
- `skills/`: agent skills shipped inside the main npm package for TanStack Intent and compatible loaders
|
|
26
|
+
|
|
27
|
+
## Quick start
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
bun install
|
|
31
|
+
bun run build
|
|
32
|
+
bun link
|
|
33
|
+
|
|
34
|
+
wfm scaffold ./example-workflow.md
|
|
35
|
+
wfm validate ./example-workflow.md
|
|
36
|
+
wfm run ./example-workflow.md --auto-confirm-all
|
|
37
|
+
|
|
38
|
+
# JSON workflow support
|
|
39
|
+
wfm scaffold ./example-workflow.json --format json
|
|
40
|
+
wfm validate ./example-workflow.json
|
|
41
|
+
wfm run ./example-workflow.json --auto-confirm-all
|
|
42
|
+
|
|
43
|
+
# Remote registry
|
|
44
|
+
wfm auth login --token <token>
|
|
45
|
+
wfm search bunny
|
|
46
|
+
wfm publish ./example-workflow.json --visibility public --tag storytelling,example
|
|
47
|
+
wfm pull alice/remote-bunny --output ./remote-bunny.json
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Build
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
bun run build
|
|
54
|
+
bun test
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Build a standalone Bun binary:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
bun run build:bin
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Build all release binaries locally:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
bun run build:bin:all
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Testing
|
|
70
|
+
|
|
71
|
+
Run unit tests:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
bun run test:unit
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Run the story workflow e2e tests (JSON and Markdown fixtures):
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
bun run test:e2e
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
The e2e suite also runs an `opencode` adapter variant for both JSON and Markdown workflows and asserts adapter routing in run events.
|
|
84
|
+
|
|
85
|
+
Run real OpenCode adapter e2e (requires `opencode` CLI installed):
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
bun run test:e2e:real
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
The real adapter test is opt-in and triggered by `WORKFLOW_MANAGER_REAL_OPENCODE=1`.
|
|
92
|
+
|
|
93
|
+
Run full test suite:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
bun test
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Documentation site:
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
bun run docs:dev
|
|
103
|
+
bun run docs:build
|
|
104
|
+
bun run docs:preview
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Docs are now treated as a manual Netlify release flow. Build `doc/.vitepress/dist` locally and deploy it manually when needed.
|
|
108
|
+
|
|
109
|
+
Remote registry app:
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
bun run remote-registry:dev
|
|
113
|
+
bun run remote-registry:build
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
`workflow-manager-ui` is the Netlify auto-release target. The root `netlify.toml` now builds `apps/remote-registry/` so connected Netlify Git deploys can produce preview deploys for PRs and production deploys from merges to `main`.
|
|
117
|
+
|
|
118
|
+
Manual help:
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
wfm man
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Remote registry commands:
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
wfm auth whoami
|
|
128
|
+
wfm auth logout
|
|
129
|
+
wfm remote info alice/remote-bunny
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Agent skills
|
|
133
|
+
|
|
134
|
+
The published `@workflow-manager/runner` npm package now ships the CLI runner and the bundled agent skills together.
|
|
135
|
+
|
|
136
|
+
- bundled skill: `skills/workflow-manager-cli/SKILL.md`
|
|
137
|
+
- discovery keyword: `tanstack-intent`
|
|
138
|
+
- install flow: install `@workflow-manager/runner`, then run `npx @tanstack/intent@latest list` and `npx @tanstack/intent@latest install`
|
|
139
|
+
|
|
140
|
+
Example usage:
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
npm install @workflow-manager/runner
|
|
144
|
+
npx @tanstack/intent@latest list
|
|
145
|
+
npx @tanstack/intent@latest install
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
See `skills/README.md` for the packaged skill layout, TanStack Intent integration, and release checks.
|
|
149
|
+
|
|
150
|
+
The deployed registry dashboard also supports browser-based token creation, workflow publishing, and creator analytics.
|
|
151
|
+
|
|
152
|
+
Current dashboard capabilities include:
|
|
153
|
+
|
|
154
|
+
- creator workflow analytics
|
|
155
|
+
- analytics refresh and trend views
|
|
156
|
+
- authenticated CLI run telemetry insights
|
|
157
|
+
- token list and revoke controls
|
|
158
|
+
- browser-based workflow publishing for JSON and Markdown sources
|
|
159
|
+
- workflow metadata and version management
|
|
160
|
+
|
|
161
|
+
## Contribution
|
|
162
|
+
|
|
163
|
+
- Keep workflow contracts backward-compatible when possible (`src/types.ts`)
|
|
164
|
+
- Update docs under `doc/` when changing schema or runtime behavior
|
|
165
|
+
- Add or update tests in `tests/` when touching parser or engine logic
|
|
166
|
+
|
|
167
|
+
Netlify auto-release is configured for the UI in `netlify.toml`:
|
|
168
|
+
|
|
169
|
+
- base directory: `apps/remote-registry`
|
|
170
|
+
- build command: `bun run build`
|
|
171
|
+
- publish directory: `dist`
|
|
172
|
+
- PRs: Deploy Previews
|
|
173
|
+
- `main`: Production deploys for `workflow-manager-ui`
|
|
174
|
+
|
|
175
|
+
The docs site is no longer the auto-release target and should be deployed manually.
|
|
176
|
+
|
|
177
|
+
## Release
|
|
178
|
+
|
|
179
|
+
- Push a semantic tag like `v0.2.0` to trigger the GitHub Actions release workflow.
|
|
180
|
+
- Publish the root npm package when you want the CLI runner and `skills/` bundle to ship together.
|
|
181
|
+
- The workflow runs tests and build, then compiles binaries for:
|
|
182
|
+
- macOS arm64: `wfm-macos-arm64`
|
|
183
|
+
- Linux x64: `wfm-linux-x64`
|
|
184
|
+
- Windows x64: `wfm-windows-x64.exe`
|
|
185
|
+
- Assets are attached to the GitHub Release for that tag.
|
|
186
|
+
|
|
187
|
+
## Documentation
|
|
188
|
+
|
|
189
|
+
VitePress docs are in `doc/` and focus on:
|
|
190
|
+
|
|
191
|
+
- how the workflow manager works
|
|
192
|
+
- runtime architecture
|
|
193
|
+
- workflow schema
|
|
194
|
+
- practical workflow examples and implementation patterns
|
|
195
|
+
- CLI manual help usage
|
|
196
|
+
- remote registry architecture, agents, and milestones
|
package/dist/engine.d.ts
ADDED
package/dist/engine.js
ADDED
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { EventLog } from "./events.js";
|
|
3
|
+
import { executeMockStep } from "./mockExecutor.js";
|
|
4
|
+
import { executeOpencodeStep, shouldUseRealOpencode } from "./opencodeExecutor.js";
|
|
5
|
+
function nodeType(step) {
|
|
6
|
+
if (step.kind === "approval")
|
|
7
|
+
return "HUMAN";
|
|
8
|
+
if (step.kind === "system")
|
|
9
|
+
return "SYSTEM";
|
|
10
|
+
return "AGENT";
|
|
11
|
+
}
|
|
12
|
+
function stepObjective(step, workflowObjective) {
|
|
13
|
+
return step.objective ?? `${workflowObjective} :: ${step.key}`;
|
|
14
|
+
}
|
|
15
|
+
function requiresValidation(step) {
|
|
16
|
+
if (step.approvalSpec?.validation?.required)
|
|
17
|
+
return step.approvalSpec.validation.mode ?? "human";
|
|
18
|
+
if (step.validation?.required)
|
|
19
|
+
return step.validation.mode ?? "human";
|
|
20
|
+
if (step.kind === "approval")
|
|
21
|
+
return step.approvalSpec?.validation?.mode ?? "human";
|
|
22
|
+
return step.validation?.mode ?? "none";
|
|
23
|
+
}
|
|
24
|
+
function canConfirm(step, options, output) {
|
|
25
|
+
const mode = requiresValidation(step);
|
|
26
|
+
if (mode === "none" && output.execution_status !== "YIELD_EXTERNAL")
|
|
27
|
+
return { ok: true };
|
|
28
|
+
if (options.autoConfirmAll)
|
|
29
|
+
return { ok: true };
|
|
30
|
+
const list = new Set(options.confirmations ?? []);
|
|
31
|
+
const modeToken = `${step.key}:${mode}`;
|
|
32
|
+
if (list.has(step.key) || list.has(modeToken))
|
|
33
|
+
return { ok: true };
|
|
34
|
+
const autoConfirm = step.validation?.autoConfirm ?? step.approvalSpec?.validation?.autoConfirm ?? false;
|
|
35
|
+
if (autoConfirm)
|
|
36
|
+
return { ok: true };
|
|
37
|
+
return { ok: false, reason: `Missing confirmation for ${step.key} (${mode})` };
|
|
38
|
+
}
|
|
39
|
+
function executeStep(step, input, attempt) {
|
|
40
|
+
const adapterKey = step.taskSpec?.adapterKey ?? "mock";
|
|
41
|
+
if (adapterKey === "opencode" && shouldUseRealOpencode(step)) {
|
|
42
|
+
return executeOpencodeStep(step, input, attempt);
|
|
43
|
+
}
|
|
44
|
+
return executeMockStep(step, input, attempt);
|
|
45
|
+
}
|
|
46
|
+
export function runWorkflow(definition, options) {
|
|
47
|
+
const runId = randomUUID();
|
|
48
|
+
const actor = options?.actor ?? "cli";
|
|
49
|
+
const primaryObjective = options?.objective ?? definition.title;
|
|
50
|
+
const workflowObjectives = definition.objectives ?? [];
|
|
51
|
+
const globalState = { ...(options?.input ?? {}) };
|
|
52
|
+
const eventLog = new EventLog();
|
|
53
|
+
let runStatus = "queued";
|
|
54
|
+
const stepRuns = new Map();
|
|
55
|
+
for (const step of definition.steps) {
|
|
56
|
+
stepRuns.set(step.key, {
|
|
57
|
+
stepKey: step.key,
|
|
58
|
+
status: "pending",
|
|
59
|
+
attempt: 0,
|
|
60
|
+
confirmed: false,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
eventLog.push(runId, "run.created", { workflowKey: definition.key }, undefined, actor);
|
|
64
|
+
runStatus = "running";
|
|
65
|
+
eventLog.push(runId, "run.started", { objective: primaryObjective, objectives: workflowObjectives }, undefined, actor);
|
|
66
|
+
let index = 0;
|
|
67
|
+
let guard = 0;
|
|
68
|
+
const maxSteps = Math.max(definition.steps.length * 30, 30);
|
|
69
|
+
while (index < definition.steps.length) {
|
|
70
|
+
guard += 1;
|
|
71
|
+
if (guard > maxSteps) {
|
|
72
|
+
runStatus = "failed";
|
|
73
|
+
eventLog.push(runId, "run.failed", { reason: "Execution guard exceeded" });
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
const step = definition.steps[index];
|
|
77
|
+
const stepRun = stepRuns.get(step.key);
|
|
78
|
+
if (!stepRun)
|
|
79
|
+
throw new Error(`Missing step run for ${step.key}`);
|
|
80
|
+
const dependencies = step.dependsOn ?? [];
|
|
81
|
+
const depsComplete = dependencies.every((depKey) => stepRuns.get(depKey)?.status === "succeeded");
|
|
82
|
+
if (!depsComplete) {
|
|
83
|
+
runStatus = "failed";
|
|
84
|
+
eventLog.push(runId, "run.failed", { reason: `Dependencies not satisfied for ${step.key}` }, step.key);
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
stepRun.status = "runnable";
|
|
88
|
+
eventLog.push(runId, "step.runnable", { stepKey: step.key }, step.key);
|
|
89
|
+
stepRun.status = "running";
|
|
90
|
+
stepRun.attempt += 1;
|
|
91
|
+
eventLog.push(runId, "step.claimed", { attempt: stepRun.attempt }, step.key);
|
|
92
|
+
eventLog.push(runId, "step.execution_started", { attempt: stepRun.attempt }, step.key);
|
|
93
|
+
const previousOutput = {};
|
|
94
|
+
for (const dep of dependencies) {
|
|
95
|
+
previousOutput[dep] = stepRuns.get(dep)?.output ?? null;
|
|
96
|
+
}
|
|
97
|
+
const inputEnvelope = {
|
|
98
|
+
global_context: {
|
|
99
|
+
workflow_id: runId,
|
|
100
|
+
primary_objective: primaryObjective,
|
|
101
|
+
workflow_objectives: workflowObjectives,
|
|
102
|
+
global_state: globalState,
|
|
103
|
+
},
|
|
104
|
+
step_context: {
|
|
105
|
+
step_id: step.key,
|
|
106
|
+
step_objective: stepObjective(step, primaryObjective),
|
|
107
|
+
previous_output: previousOutput,
|
|
108
|
+
assigned_node_type: nodeType(step),
|
|
109
|
+
},
|
|
110
|
+
priming_configuration: {
|
|
111
|
+
required_skills: step.taskSpec?.init?.skills ?? [],
|
|
112
|
+
mcp_endpoints: step.taskSpec?.init?.mcps ?? [],
|
|
113
|
+
system_prompts: step.taskSpec?.init?.systemPrompts ?? [],
|
|
114
|
+
context: step.taskSpec?.init?.context,
|
|
115
|
+
adapter: step.taskSpec?.adapterKey ?? "mock",
|
|
116
|
+
model: step.taskSpec?.init?.model,
|
|
117
|
+
},
|
|
118
|
+
};
|
|
119
|
+
const output = executeStep(step, inputEnvelope, stepRun.attempt);
|
|
120
|
+
eventLog.push(runId, "step.execution_finished", {
|
|
121
|
+
status: output.execution_status,
|
|
122
|
+
action: output.qa_routing.action,
|
|
123
|
+
adapter: step.taskSpec?.adapterKey ?? "mock",
|
|
124
|
+
init: {
|
|
125
|
+
skills: step.taskSpec?.init?.skills ?? [],
|
|
126
|
+
mcps: step.taskSpec?.init?.mcps ?? [],
|
|
127
|
+
context: step.taskSpec?.init?.context ?? {},
|
|
128
|
+
},
|
|
129
|
+
}, step.key);
|
|
130
|
+
const confirmation = canConfirm(step, options ?? {}, output);
|
|
131
|
+
if (!confirmation.ok) {
|
|
132
|
+
stepRun.status = "waiting_for_approval";
|
|
133
|
+
runStatus = "waiting_for_approval";
|
|
134
|
+
eventLog.push(runId, "step.waiting_for_approval", { reason: confirmation.reason, validation: requiresValidation(step) }, step.key);
|
|
135
|
+
eventLog.push(runId, "run.waiting_for_approval", { reason: "confirmation required" }, step.key, actor);
|
|
136
|
+
break;
|
|
137
|
+
}
|
|
138
|
+
stepRun.confirmed = true;
|
|
139
|
+
eventLog.push(runId, "step.confirmed", { by: actor, validation: requiresValidation(step) }, step.key, actor);
|
|
140
|
+
if (output.execution_status === "YIELD_EXTERNAL") {
|
|
141
|
+
stepRun.status = "waiting_for_approval";
|
|
142
|
+
runStatus = "waiting_for_approval";
|
|
143
|
+
eventLog.push(runId, "step.waiting_for_approval", { reason: "external intervention" }, step.key);
|
|
144
|
+
eventLog.push(runId, "approval.resolved", { decision: "approved" }, step.key, actor);
|
|
145
|
+
stepRun.status = "succeeded";
|
|
146
|
+
runStatus = "running";
|
|
147
|
+
stepRun.output = output.mutated_payload;
|
|
148
|
+
globalState[step.key] = output.mutated_payload;
|
|
149
|
+
index += 1;
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
if (output.execution_status === "FAILED") {
|
|
153
|
+
stepRun.status = "failed";
|
|
154
|
+
runStatus = "failed";
|
|
155
|
+
eventLog.push(runId, "run.failed", { stepKey: step.key, reason: "step failed" }, step.key);
|
|
156
|
+
break;
|
|
157
|
+
}
|
|
158
|
+
if (output.execution_status === "QA_REJECTED") {
|
|
159
|
+
const retryMax = step.retryPolicy?.maxAttempts ?? definition.defaultRetryPolicy?.maxAttempts ?? 1;
|
|
160
|
+
if (output.qa_routing.action === "RETRY_CURRENT") {
|
|
161
|
+
if (stepRun.attempt < retryMax) {
|
|
162
|
+
stepRun.status = "pending";
|
|
163
|
+
eventLog.push(runId, "step.retried", { stepKey: step.key, attempt: stepRun.attempt + 1 }, step.key);
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
stepRun.status = "failed";
|
|
167
|
+
runStatus = "failed";
|
|
168
|
+
eventLog.push(runId, "run.failed", { stepKey: step.key, reason: "max retry exceeded" }, step.key);
|
|
169
|
+
break;
|
|
170
|
+
}
|
|
171
|
+
if (output.qa_routing.action === "ROLLBACK_PREVIOUS") {
|
|
172
|
+
if (index === 0) {
|
|
173
|
+
stepRun.status = "failed";
|
|
174
|
+
runStatus = "failed";
|
|
175
|
+
eventLog.push(runId, "run.failed", { stepKey: step.key, reason: "cannot rollback before first step" }, step.key);
|
|
176
|
+
break;
|
|
177
|
+
}
|
|
178
|
+
const prevStep = definition.steps[index - 1];
|
|
179
|
+
const prevRun = stepRuns.get(prevStep.key);
|
|
180
|
+
prevRun.status = "pending";
|
|
181
|
+
prevRun.confirmed = false;
|
|
182
|
+
eventLog.push(runId, "step.retried", { stepKey: prevStep.key, via: step.key }, prevStep.key);
|
|
183
|
+
stepRun.status = "pending";
|
|
184
|
+
stepRun.confirmed = false;
|
|
185
|
+
index -= 1;
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
if (output.qa_routing.action === "RESTART_ALL") {
|
|
189
|
+
for (const s of definition.steps) {
|
|
190
|
+
const sr = stepRuns.get(s.key);
|
|
191
|
+
sr.status = "pending";
|
|
192
|
+
sr.attempt = 0;
|
|
193
|
+
sr.confirmed = false;
|
|
194
|
+
delete sr.output;
|
|
195
|
+
}
|
|
196
|
+
eventLog.push(runId, "step.retried", { mode: "restart_all", triggeredBy: step.key }, step.key);
|
|
197
|
+
index = 0;
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
stepRun.status = "succeeded";
|
|
202
|
+
stepRun.output = output.mutated_payload;
|
|
203
|
+
globalState[step.key] = output.mutated_payload;
|
|
204
|
+
index += 1;
|
|
205
|
+
}
|
|
206
|
+
if (runStatus === "running") {
|
|
207
|
+
runStatus = "succeeded";
|
|
208
|
+
eventLog.push(runId, "run.completed", { steps: definition.steps.length }, undefined, actor);
|
|
209
|
+
}
|
|
210
|
+
return {
|
|
211
|
+
runId,
|
|
212
|
+
status: runStatus,
|
|
213
|
+
outputs: globalState,
|
|
214
|
+
stepRuns: definition.steps.map((s) => stepRuns.get(s.key)),
|
|
215
|
+
events: eventLog.all(),
|
|
216
|
+
};
|
|
217
|
+
}
|
package/dist/events.d.ts
ADDED
package/dist/events.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
export class EventLog {
|
|
3
|
+
sequence = 0;
|
|
4
|
+
events = [];
|
|
5
|
+
push(runId, type, payload = {}, stepRunId, actor = "system") {
|
|
6
|
+
this.sequence += 1;
|
|
7
|
+
this.events.push({
|
|
8
|
+
id: randomUUID(),
|
|
9
|
+
runId,
|
|
10
|
+
stepRunId,
|
|
11
|
+
type,
|
|
12
|
+
sequenceNumber: this.sequence,
|
|
13
|
+
occurredAt: new Date().toISOString(),
|
|
14
|
+
actor,
|
|
15
|
+
payload,
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
all() {
|
|
19
|
+
return [...this.events];
|
|
20
|
+
}
|
|
21
|
+
}
|
package/dist/index.d.ts
ADDED