@barnum/barnum 0.0.0-main-ccc32185 → 0.0.0-main-d44f0a8e

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.
Binary file
Binary file
Binary file
Binary file
Binary file
package/cli.cjs CHANGED
@@ -1,56 +1,33 @@
1
1
  #!/usr/bin/env node
2
- 'use strict';
2
+ "use strict";
3
3
 
4
- var path = require('path');
5
- var spawn = require('child_process').spawn;
6
- var chmodSync = require('fs').chmodSync;
4
+ const { execFileSync } = require("child_process");
5
+ const path = require("path");
6
+ const os = require("os");
7
+ const fs = require("fs");
7
8
 
8
- var bin;
9
+ const platform = os.platform();
10
+ const arch = os.arch();
9
11
 
10
- if (process.platform === 'darwin' && process.arch === 'x64') {
11
- bin = path.join(__dirname, 'artifacts', 'macos-x64', 'barnum');
12
- } else if (process.platform === 'darwin' && process.arch === 'arm64') {
13
- bin = path.join(__dirname, 'artifacts', 'macos-arm64', 'barnum');
14
- } else if (process.platform === 'linux' && process.arch === 'x64') {
15
- bin = path.join(__dirname, 'artifacts', 'linux-x64', 'barnum');
16
- } else if (process.platform === 'linux' && process.arch === 'arm64') {
17
- bin = path.join(__dirname, 'artifacts', 'linux-arm64', 'barnum');
18
- } else if (process.platform === 'win32' && process.arch === 'x64') {
19
- bin = path.join(__dirname, 'artifacts', 'win-x64', 'barnum.exe');
20
- } else {
21
- throw new Error(
22
- `Platform "${process.platform} (${process.arch})" not supported.`
23
- );
24
- }
12
+ let binaryName = "barnum";
13
+ let artifactDir;
25
14
 
26
- // --executor and --run-handler-path are internal. Error if the user passed them directly.
27
- var userArgs = process.argv.slice(2);
28
- var internalFlags = ['--executor', '--run-handler-path'];
29
- for (var flag of internalFlags) {
30
- if (userArgs.includes(flag)) {
31
- console.error('Error: ' + flag + ' is an internal flag and cannot be passed directly.');
32
- process.exit(1);
33
- }
34
- }
15
+ if (platform === "darwin" && arch === "arm64") artifactDir = "macos-arm64";
16
+ else if (platform === "darwin") artifactDir = "macos-x64";
17
+ else if (platform === "linux" && arch === "arm64") artifactDir = "linux-arm64";
18
+ else if (platform === "linux") artifactDir = "linux-x64";
19
+ else if (platform === "win32") { artifactDir = "win-x64"; binaryName = "barnum.exe"; }
20
+ else { console.error(`Unsupported platform: ${platform}-${arch}`); process.exit(1); }
35
21
 
36
- var executorPath = path.resolve(__dirname, 'actions', 'executor.ts');
37
- var runHandlerPath = path.resolve(__dirname, 'actions', 'run-handler.ts');
22
+ const binaryPath = path.join(__dirname, "artifacts", artifactDir, binaryName);
38
23
 
39
- function resolveExecutorCommand() {
40
- if (typeof Bun !== 'undefined') {
41
- // Bun runs .ts natively
42
- return process.execPath + ' ' + executorPath;
43
- }
44
- // Node: use tsx
45
- var tsxPath = require.resolve('tsx/cli');
46
- return 'node ' + tsxPath + ' ' + executorPath;
24
+ if (!fs.existsSync(binaryPath)) {
25
+ console.error(`Binary not found: ${binaryPath}`);
26
+ process.exit(1);
47
27
  }
48
28
 
49
- var executor = resolveExecutorCommand();
50
- var args = userArgs.concat('--executor', executor, '--run-handler-path', runHandlerPath);
51
-
52
29
  try {
53
- chmodSync(bin, 0o755);
54
- } catch {}
55
-
56
- spawn(bin, args, { stdio: 'inherit' }).on('exit', process.exit);
30
+ execFileSync(binaryPath, process.argv.slice(2), { stdio: "inherit" });
31
+ } catch (e) {
32
+ process.exit(e.status || 1);
33
+ }
package/index.js ADDED
@@ -0,0 +1,2 @@
1
+ // @barnum/barnum - workflow engine
2
+ // Not yet implemented.
package/package.json CHANGED
@@ -1,19 +1,17 @@
1
1
  {
2
2
  "name": "@barnum/barnum",
3
- "version": "0.0.0-main-ccc32185",
3
+ "version": "0.0.0-main-d44f0a8e",
4
+ "description": "Barnum workflow engine",
4
5
  "type": "module",
5
- "description": "Barnum CLI - workflow engine for agents.",
6
- "main": "index.ts",
7
- "types": "index.ts",
8
- "exports": {
9
- ".": "./index.ts",
10
- "./schema": "./barnum-config-schema.zod.ts",
11
- "./cli-schema": "./barnum-cli-schema.zod.ts",
12
- "./binary": "./index.cjs"
13
- },
6
+ "main": "index.js",
14
7
  "bin": {
15
8
  "barnum": "cli.cjs"
16
9
  },
10
+ "scripts": {
11
+ "test": "vitest run",
12
+ "typecheck": "tsc --noEmit",
13
+ "lint": "oxlint src/"
14
+ },
17
15
  "author": "Robert Balicki",
18
16
  "license": "MIT",
19
17
  "repository": {
@@ -24,35 +22,21 @@
24
22
  "access": "public",
25
23
  "registry": "https://registry.npmjs.org"
26
24
  },
27
- "keywords": [
28
- "barnum",
29
- "ai",
30
- "agents",
31
- "automation",
32
- "cli"
33
- ],
34
25
  "files": [
35
- "index.ts",
36
- "index.cjs",
26
+ "index.js",
37
27
  "cli.cjs",
38
- "artifacts/**/*",
39
- "barnum-config-schema.json",
40
- "barnum-config-schema.zod.ts",
41
- "barnum-cli-schema.zod.ts",
42
- "run.ts",
43
- "types.ts",
44
- "actions/**/*.ts"
28
+ "artifacts/**/*"
45
29
  ],
46
- "scripts": {
47
- "typecheck": "tsc --noEmit"
48
- },
49
- "dependencies": {
50
- "zod": "^3.0.0",
51
- "zod-to-json-schema": "^3.25.1"
52
- },
30
+ "sideEffects": false,
53
31
  "devDependencies": {
32
+ "@oxlint/binding-darwin-arm64": "^1.57.0",
54
33
  "@types/node": "^25.5.0",
55
- "typescript": "^5.9.3"
34
+ "oxlint": "^1.57.0",
35
+ "prettier": "^3.8.1",
36
+ "typescript": "^5.7.0",
37
+ "vitest": "^3.0.0"
56
38
  },
57
- "sideEffects": false
39
+ "dependencies": {
40
+ "zod": "^4.3.6"
41
+ }
58
42
  }
package/README.md DELETED
@@ -1,19 +0,0 @@
1
- # @barnum/barnum
2
-
3
- Barnum CLI - workflow engine for agents.
4
-
5
- ## Installation
6
-
7
- ```bash
8
- npm install -g @barnum/barnum
9
- ```
10
-
11
- ## Usage
12
-
13
- ```bash
14
- barnum --help
15
- ```
16
-
17
- ## About
18
-
19
- Barnum is a task automation tool that orchestrates AI agents to execute multi-step workflows defined in configuration files.
@@ -1,12 +0,0 @@
1
- const [handlerPath, exportName = "default"] = process.argv.slice(2);
2
-
3
- const chunks: Buffer[] = [];
4
- for await (const chunk of process.stdin) chunks.push(chunk);
5
- const envelope = JSON.parse(Buffer.concat(chunks).toString());
6
-
7
- const mod = await import(handlerPath);
8
- const handler = mod[exportName];
9
-
10
- const results = await handler.handle(envelope);
11
-
12
- process.stdout.write(JSON.stringify(results));
@@ -1,68 +0,0 @@
1
- import { z } from "zod";
2
-
3
- const SchemaType = z.union([
4
- z.literal("zod").describe("Zod TypeScript schema."),
5
- z.literal("json").describe("JSON Schema."),
6
- ]).describe("Output format for `barnum config schema`.");
7
-
8
- const ConfigCommand = z.discriminatedUnion("kind", [
9
- z.object({
10
- config: z.string().describe("Config (JSON string or path to file)"),
11
- kind: z.literal("Docs"),
12
- }).describe("Generate markdown documentation from config"),
13
- z.object({
14
- config: z.string().describe("Config (JSON string or path to file)"),
15
- kind: z.literal("Validate"),
16
- }).describe("Validate a config file"),
17
- z.object({
18
- config: z.string().describe("Config (JSON string or path to file)"),
19
- kind: z.literal("Graph"),
20
- }).describe("Generate DOT visualization of config (for `GraphViz`)"),
21
- z.object({
22
- kind: z.literal("Schema"),
23
- schemaType: SchemaType.describe("Output format: zod (default) or json"),
24
- }).describe("Print the config schema (Zod by default, JSON with --type json)"),
25
- ]).describe("Subcommands for `barnum config`.");
26
-
27
- const Command = z.discriminatedUnion("kind", [
28
- z.object({
29
- config: z.string().nullable().optional().describe("Config (JSON string or path to file). Required unless `--resume-from` is used."),
30
- entrypointValue: z.string().nullable().optional().describe("Initial value for the entrypoint step (JSON string or path to file). Only valid when config has an `entrypoint`. Defaults to `{}` if not provided."),
31
- executor: z.string().describe("Executor command for TypeScript handlers, injected by cli.cjs and run.ts."),
32
- initialState: z.string().nullable().optional().describe("Initial tasks (JSON string or path to file). Required if config has no `entrypoint`. Cannot be used with `--entrypoint-value`."),
33
- kind: z.literal("Run"),
34
- logFile: z.string().nullable().optional().describe("Log file path (logs emitted in addition to stderr)"),
35
- resumeFrom: z.string().nullable().optional().describe("Resume from a previous state log file. Incompatible with `--config`, `--initial-state`, and `--entrypoint-value`."),
36
- runHandlerPath: z.string().describe("Path to run-handler.ts, injected by cli.cjs and run.ts."),
37
- stateLog: z.string().nullable().optional().describe("State log file path (NDJSON file for persistence/resume)"),
38
- wake: z.string().nullable().optional().describe("Wake script to call before starting"),
39
- }).describe("Run the task queue"),
40
- z.object({
41
- command: ConfigCommand.describe("Config subcommand to run."),
42
- kind: z.literal("Config"),
43
- }).describe("Config file operations (docs, validate, graph, schema)"),
44
- z.object({
45
- json: z.boolean().describe("Output as JSON (for programmatic access)"),
46
- kind: z.literal("Version"),
47
- }).describe("Print version information"),
48
- ]).describe("Barnum subcommands.");
49
-
50
- const LogLevel = z.union([
51
- z.literal("off").describe("No logging"),
52
- z.literal("error").describe("Error messages only"),
53
- z.literal("warn").describe("Warnings and errors"),
54
- z.literal("info").describe("Informational messages (default)"),
55
- z.literal("debug").describe("Debug messages (includes task return values)"),
56
- z.literal("trace").describe("Trace messages (very verbose)"),
57
- ]).describe("Log level for barnum output.");
58
-
59
- export const cliSchema = z.object({
60
- command: Command.describe("Subcommand to run."),
61
- logLevel: LogLevel.describe("Log level (debug shows task return values)"),
62
- }).describe("Top-level CLI arguments for barnum.");
63
-
64
- export type Cli = z.infer<typeof cliSchema>;
65
- export type SchemaType = z.infer<typeof SchemaType>;
66
- export type ConfigCommand = z.infer<typeof ConfigCommand>;
67
- export type Command = z.infer<typeof Command>;
68
- export type LogLevel = z.infer<typeof LogLevel>;
@@ -1,271 +0,0 @@
1
- {
2
- "$schema": "http://json-schema.org/draft-07/schema#",
3
- "title": "Config",
4
- "description": "Top-level Barnum configuration.\n\nDefines a workflow as a directed graph of steps. Each step processes tasks and can spawn follow-up tasks on other steps.",
5
- "type": "object",
6
- "required": [
7
- "steps"
8
- ],
9
- "properties": {
10
- "entrypoint": {
11
- "description": "Name of the step that starts the workflow. When set, the CLI accepts `--entrypoint-value` to provide the initial task value (defaults to `{}`). When omitted, `--initial-state` must provide explicit `[{\"kind\": \"StepName\", \"value\": ...}]` tasks.",
12
- "default": null,
13
- "type": [
14
- "string",
15
- "null"
16
- ]
17
- },
18
- "options": {
19
- "description": "Global runtime options (timeout, retries, concurrency). Individual steps can override these via their own `options` field.",
20
- "default": {
21
- "maxConcurrency": null,
22
- "maxRetries": 0,
23
- "retryOnInvalidResponse": true,
24
- "retryOnTimeout": true,
25
- "timeout": null
26
- },
27
- "allOf": [
28
- {
29
- "$ref": "#/definitions/Options"
30
- }
31
- ]
32
- },
33
- "steps": {
34
- "description": "The steps that make up this workflow. Each step defines how to process a task and which steps it can spawn follow-up tasks on.",
35
- "type": "array",
36
- "items": {
37
- "$ref": "#/definitions/Step"
38
- }
39
- }
40
- },
41
- "additionalProperties": false,
42
- "definitions": {
43
- "ActionKind": {
44
- "description": "How a step processes tasks.",
45
- "oneOf": [
46
- {
47
- "description": "Run a shell command.",
48
- "type": "object",
49
- "required": [
50
- "kind",
51
- "script"
52
- ],
53
- "properties": {
54
- "kind": {
55
- "type": "string",
56
- "enum": [
57
- "Bash"
58
- ]
59
- },
60
- "script": {
61
- "description": "Shell script to execute.\n\n**Input (stdin):** JSON object: `{\"kind\": \"<step name>\", \"value\": <payload>}`. Use `jq '.value'` to extract the payload, or `jq -r '.value.fieldName'` for a specific field.\n\n**Output (stdout):** JSON array of follow-up tasks to spawn: `[{\"kind\": \"NextStep\", \"value\": {...}}, ...]`. Each `kind` must be a step name listed in this step's `next` array. Return `[]` to spawn no follow-ups.",
62
- "type": "string"
63
- }
64
- }
65
- },
66
- {
67
- "description": "Run a TypeScript handler.",
68
- "type": "object",
69
- "required": [
70
- "kind",
71
- "path"
72
- ],
73
- "properties": {
74
- "exportedAs": {
75
- "description": "Named export to invoke from the handler module.",
76
- "default": "default",
77
- "type": "string"
78
- },
79
- "kind": {
80
- "type": "string",
81
- "enum": [
82
- "TypeScript"
83
- ]
84
- },
85
- "path": {
86
- "description": "Path to the handler file (absolute — JS layer resolves before passing to Rust).",
87
- "type": "string"
88
- },
89
- "stepConfig": {
90
- "description": "Step configuration passed through to the handler. Rust stores this as-is and includes it in the envelope.",
91
- "default": null
92
- },
93
- "valueSchema": {
94
- "description": "JSON Schema for this step's input value. Produced by JS from Zod. Used to validate transition values targeting this step.",
95
- "default": null
96
- }
97
- }
98
- }
99
- ]
100
- },
101
- "FinallyHook": {
102
- "description": "Finally hook. Runs after a task and all its descendants complete.\n\nIn JSON: `{\"kind\": \"Bash\", \"script\": \"./finally-hook.sh\"}`\n\n**stdin:** JSON object: `{\"kind\": \"<step name>\", \"value\": <payload>}`. **stdout:** JSON array of follow-up tasks: `[{\"kind\": \"StepName\", \"value\": {...}}, ...]`. Return `[]` for no follow-ups.",
103
- "oneOf": [
104
- {
105
- "description": "Run a shell command as the finally hook.",
106
- "type": "object",
107
- "required": [
108
- "kind",
109
- "script"
110
- ],
111
- "properties": {
112
- "kind": {
113
- "type": "string",
114
- "enum": [
115
- "Bash"
116
- ]
117
- },
118
- "script": {
119
- "description": "Shell script to execute.",
120
- "type": "string"
121
- }
122
- }
123
- }
124
- ]
125
- },
126
- "Options": {
127
- "description": "Global runtime options for task execution. All fields have sensible defaults.",
128
- "type": "object",
129
- "properties": {
130
- "maxConcurrency": {
131
- "description": "Maximum concurrent tasks (None = unlimited).",
132
- "default": null,
133
- "type": [
134
- "integer",
135
- "null"
136
- ],
137
- "format": "uint",
138
- "minimum": 0.0
139
- },
140
- "maxRetries": {
141
- "description": "Maximum retries per task (default: 0).",
142
- "default": 0,
143
- "type": "integer",
144
- "format": "uint32",
145
- "minimum": 0.0
146
- },
147
- "retryOnInvalidResponse": {
148
- "description": "Whether to retry when agent returns invalid response (default: true).",
149
- "default": true,
150
- "type": "boolean"
151
- },
152
- "retryOnTimeout": {
153
- "description": "Whether to retry when agent times out (default: true).",
154
- "default": true,
155
- "type": "boolean"
156
- },
157
- "timeout": {
158
- "description": "Timeout in seconds for each task (None = no timeout).",
159
- "default": null,
160
- "type": [
161
- "integer",
162
- "null"
163
- ],
164
- "format": "uint64",
165
- "minimum": 0.0
166
- }
167
- },
168
- "additionalProperties": false
169
- },
170
- "Step": {
171
- "description": "A named step in the workflow. Steps are the nodes of the task graph.\n\nThe `finally` hook runs after the task **and all of its descendant tasks** complete.",
172
- "type": "object",
173
- "required": [
174
- "action",
175
- "name"
176
- ],
177
- "properties": {
178
- "action": {
179
- "description": "How this step processes tasks.",
180
- "allOf": [
181
- {
182
- "$ref": "#/definitions/ActionKind"
183
- }
184
- ]
185
- },
186
- "finally": {
187
- "description": "Shell script that runs after this task **and all tasks it spawned (recursively)** have completed.\n\n**stdin:** JSON object: `{\"kind\": \"<step name>\", \"value\": <payload>}`. Same envelope format as command action scripts.\n\n**stdout:** A JSON array of follow-up tasks to spawn: `[{\"kind\": \"StepName\", \"value\": {...}}, ...]`. Each `kind` must be a valid step name. Return `[]` to spawn no follow-ups.\n\nUse this for cleanup, aggregation, or spawning a final summarization step after an entire subtree of work completes.",
188
- "default": null,
189
- "anyOf": [
190
- {
191
- "$ref": "#/definitions/FinallyHook"
192
- },
193
- {
194
- "type": "null"
195
- }
196
- ]
197
- },
198
- "name": {
199
- "description": "Unique name for this step (e.g., `\"Analyze\"`, `\"Implement\"`, `\"Review\"`). This is the string used as `kind` when creating tasks: `{\"kind\": \"ThisStepName\", \"value\": {...}}`.",
200
- "type": "string"
201
- },
202
- "next": {
203
- "description": "Step names this step is allowed to spawn follow-up tasks on. Each string must match the `name` of another step in this config. An empty array means this is a terminal step (no follow-ups).",
204
- "default": [],
205
- "type": "array",
206
- "items": {
207
- "type": "string"
208
- }
209
- },
210
- "options": {
211
- "description": "Per-step options that override the global `options`. Only the fields you set here take effect; everything else falls through to the global defaults.",
212
- "default": {
213
- "maxRetries": null,
214
- "retryOnInvalidResponse": null,
215
- "retryOnTimeout": null,
216
- "timeout": null
217
- },
218
- "allOf": [
219
- {
220
- "$ref": "#/definitions/StepOptions"
221
- }
222
- ]
223
- }
224
- },
225
- "additionalProperties": false
226
- },
227
- "StepOptions": {
228
- "description": "Per-step option overrides. Only set the fields you want to override; omitted fields inherit from the global `options`.",
229
- "type": "object",
230
- "properties": {
231
- "maxRetries": {
232
- "description": "Maximum retries for tasks on this step (overrides global `max_retries`).",
233
- "default": null,
234
- "type": [
235
- "integer",
236
- "null"
237
- ],
238
- "format": "uint32",
239
- "minimum": 0.0
240
- },
241
- "retryOnInvalidResponse": {
242
- "description": "Whether to retry when an agent returns an invalid response on this step (overrides global `retry_on_invalid_response`).",
243
- "default": null,
244
- "type": [
245
- "boolean",
246
- "null"
247
- ]
248
- },
249
- "retryOnTimeout": {
250
- "description": "Whether to retry when an agent times out on this step (overrides global `retry_on_timeout`).",
251
- "default": null,
252
- "type": [
253
- "boolean",
254
- "null"
255
- ]
256
- },
257
- "timeout": {
258
- "description": "Timeout in seconds for tasks on this step (overrides global `timeout`).",
259
- "default": null,
260
- "type": [
261
- "integer",
262
- "null"
263
- ],
264
- "format": "uint64",
265
- "minimum": 0.0
266
- }
267
- },
268
- "additionalProperties": false
269
- }
270
- }
271
- }
@@ -1,62 +0,0 @@
1
- import { z } from "zod";
2
-
3
- const ActionKind = z.discriminatedUnion("kind", [
4
- z.object({
5
- kind: z.literal("Bash"),
6
- script: z.string().describe("Shell script to execute.\n\n**Input (stdin):** JSON object: `{\"kind\": \"<step name>\", \"value\": <payload>}`. Use `jq '.value'` to extract the payload, or `jq -r '.value.fieldName'` for a specific field.\n\n**Output (stdout):** JSON array of follow-up tasks to spawn: `[{\"kind\": \"NextStep\", \"value\": {...}}, ...]`. Each `kind` must be a step name listed in this step's `next` array. Return `[]` to spawn no follow-ups."),
7
- }).describe("Run a shell command."),
8
- z.object({
9
- exportedAs: z.string().optional().default("default").describe("Named export to invoke from the handler module."),
10
- kind: z.literal("TypeScript"),
11
- path: z.string().describe("Path to the handler file (absolute — JS layer resolves before passing to Rust)."),
12
- stepConfig: z.any().optional().default(null).describe("Step configuration passed through to the handler. Rust stores this as-is and includes it in the envelope."),
13
- valueSchema: z.any().optional().default(null).describe("JSON Schema for this step's input value. Produced by JS from Zod. Used to validate transition values targeting this step."),
14
- }).describe("Run a TypeScript handler."),
15
- ]).describe("How a step processes tasks.");
16
-
17
- const FinallyHook = z.discriminatedUnion("kind", [
18
- z.object({
19
- kind: z.literal("Bash"),
20
- script: z.string().describe("Shell script to execute."),
21
- }).describe("Run a shell command as the finally hook."),
22
- ]).describe("Finally hook. Runs after a task and all its descendants complete.\n\nIn JSON: `{\"kind\": \"Bash\", \"script\": \"./finally-hook.sh\"}`\n\n**stdin:** JSON object: `{\"kind\": \"<step name>\", \"value\": <payload>}`. **stdout:** JSON array of follow-up tasks: `[{\"kind\": \"StepName\", \"value\": {...}}, ...]`. Return `[]` for no follow-ups.");
23
-
24
- const Options = z.object({
25
- maxConcurrency: z.number().int().nonnegative().nullable().optional().default(null).describe("Maximum concurrent tasks (None = unlimited)."),
26
- maxRetries: z.number().int().nonnegative().optional().default(0).describe("Maximum retries per task (default: 0)."),
27
- retryOnInvalidResponse: z.boolean().optional().default(true).describe("Whether to retry when agent returns invalid response (default: true)."),
28
- retryOnTimeout: z.boolean().optional().default(true).describe("Whether to retry when agent times out (default: true)."),
29
- timeout: z.number().int().nonnegative().nullable().optional().default(null).describe("Timeout in seconds for each task (None = no timeout)."),
30
- }).strict().describe("Global runtime options for task execution. All fields have sensible defaults.");
31
-
32
- const StepOptions = z.object({
33
- maxRetries: z.number().int().nonnegative().nullable().optional().default(null).describe("Maximum retries for tasks on this step (overrides global `max_retries`)."),
34
- retryOnInvalidResponse: z.boolean().nullable().optional().default(null).describe("Whether to retry when an agent returns an invalid response on this step (overrides global `retry_on_invalid_response`)."),
35
- retryOnTimeout: z.boolean().nullable().optional().default(null).describe("Whether to retry when an agent times out on this step (overrides global `retry_on_timeout`)."),
36
- timeout: z.number().int().nonnegative().nullable().optional().default(null).describe("Timeout in seconds for tasks on this step (overrides global `timeout`)."),
37
- }).strict().describe("Per-step option overrides. Only set the fields you want to override; omitted fields inherit from the global `options`.");
38
-
39
- const Step = z.object({
40
- action: ActionKind.describe("How this step processes tasks."),
41
- finally: FinallyHook.nullable().optional().default(null).describe("Shell script that runs after this task **and all tasks it spawned (recursively)** have completed.\n\n**stdin:** JSON object: `{\"kind\": \"<step name>\", \"value\": <payload>}`. Same envelope format as command action scripts.\n\n**stdout:** A JSON array of follow-up tasks to spawn: `[{\"kind\": \"StepName\", \"value\": {...}}, ...]`. Each `kind` must be a valid step name. Return `[]` to spawn no follow-ups.\n\nUse this for cleanup, aggregation, or spawning a final summarization step after an entire subtree of work completes."),
42
- name: z.string().describe("Unique name for this step (e.g., `\"Analyze\"`, `\"Implement\"`, `\"Review\"`). This is the string used as `kind` when creating tasks: `{\"kind\": \"ThisStepName\", \"value\": {...}}`."),
43
- next: z.array(z.string()).optional().default([]).describe("Step names this step is allowed to spawn follow-up tasks on. Each string must match the `name` of another step in this config. An empty array means this is a terminal step (no follow-ups)."),
44
- options: StepOptions.optional().default({"maxRetries": null, "retryOnInvalidResponse": null, "retryOnTimeout": null, "timeout": null}).describe("Per-step options that override the global `options`. Only the fields you set here take effect; everything else falls through to the global defaults."),
45
- }).strict().describe("A named step in the workflow. Steps are the nodes of the task graph.\n\nThe `finally` hook runs after the task **and all of its descendant tasks** complete.");
46
-
47
- export const configSchema = z.object({
48
- entrypoint: z.string().nullable().optional().default(null).describe("Name of the step that starts the workflow. When set, the CLI accepts `--entrypoint-value` to provide the initial task value (defaults to `{}`). When omitted, `--initial-state` must provide explicit `[{\"kind\": \"StepName\", \"value\": ...}]` tasks."),
49
- options: Options.optional().default({"maxConcurrency": null, "maxRetries": 0, "retryOnInvalidResponse": true, "retryOnTimeout": true, "timeout": null}).describe("Global runtime options (timeout, retries, concurrency). Individual steps can override these via their own `options` field."),
50
- steps: z.array(Step).describe("The steps that make up this workflow. Each step defines how to process a task and which steps it can spawn follow-up tasks on."),
51
- }).strict().describe("Top-level Barnum configuration.\n\nDefines a workflow as a directed graph of steps. Each step processes tasks and can spawn follow-up tasks on other steps.");
52
-
53
- export type Config = z.infer<typeof configSchema>;
54
- export type ActionKind = z.infer<typeof ActionKind>;
55
- export type FinallyHook = z.infer<typeof FinallyHook>;
56
- export type Options = z.infer<typeof Options>;
57
- export type StepOptions = z.infer<typeof StepOptions>;
58
- export type Step = z.infer<typeof Step>;
59
-
60
- export function defineConfig(config: z.input<typeof configSchema>): Config {
61
- return configSchema.parse(config);
62
- }
package/index.cjs DELETED
@@ -1,23 +0,0 @@
1
- 'use strict';
2
-
3
- const path = require('path');
4
-
5
- let binary;
6
-
7
- if (process.platform === 'darwin' && process.arch === 'x64') {
8
- binary = path.join(__dirname, 'artifacts', 'macos-x64', 'barnum');
9
- } else if (process.platform === 'darwin' && process.arch === 'arm64') {
10
- binary = path.join(__dirname, 'artifacts', 'macos-arm64', 'barnum');
11
- } else if (process.platform === 'linux' && process.arch === 'x64') {
12
- binary = path.join(__dirname, 'artifacts', 'linux-x64', 'barnum');
13
- } else if (process.platform === 'linux' && process.arch === 'arm64') {
14
- binary = path.join(__dirname, 'artifacts', 'linux-arm64', 'barnum');
15
- } else if (process.platform === 'win32' && process.arch === 'x64') {
16
- binary = path.join(__dirname, 'artifacts', 'win-x64', 'barnum.exe');
17
- } else {
18
- throw new Error(
19
- `Platform "${process.platform} (${process.arch})" not supported.`
20
- );
21
- }
22
-
23
- module.exports = binary;
package/index.ts DELETED
@@ -1,9 +0,0 @@
1
- export * from "./barnum-config-schema.zod.js";
2
- export * from "./barnum-cli-schema.zod.js";
3
- export { BarnumConfig, type RunOptions, type ConfigInput, type StepInput } from "./run.js";
4
- export { Handler, createHandler, isHandler } from "./types.js";
5
- export type {
6
- HandlerDefinition,
7
- HandlerContext,
8
- FollowUpTask,
9
- } from "./types.js";
package/run.ts DELETED
@@ -1,180 +0,0 @@
1
- import { spawn, type ChildProcess } from "node:child_process";
2
- import { chmodSync } from "node:fs";
3
- import { createRequire } from "node:module";
4
- import { dirname, resolve } from "node:path";
5
- import { fileURLToPath } from "node:url";
6
- import { configSchema } from "./barnum-config-schema.zod.js";
7
- import { Handler, isHandler } from "./types.js";
8
- import { z } from "zod";
9
- import { zodToJsonSchema } from "zod-to-json-schema";
10
-
11
- const __dirname = dirname(fileURLToPath(import.meta.url));
12
- const require = createRequire(import.meta.url);
13
- const binaryPath: string = process.env.BARNUM ?? require("./index.cjs");
14
-
15
- function resolveExecutor(): string {
16
- // @ts-expect-error Bun global
17
- if (typeof Bun !== "undefined") {
18
- return process.execPath;
19
- }
20
- // Resolve tsx from the calling script's node_modules
21
- const callerRequire = createRequire(process.argv[1] || import.meta.url);
22
- const tsxPath = callerRequire.resolve("tsx/cli");
23
- return `node ${tsxPath}`;
24
- }
25
-
26
- const runHandlerPath = resolve(__dirname, "actions", "run-handler.ts");
27
-
28
- function spawnBarnum(args: string[], cwd?: string): ChildProcess {
29
- try {
30
- chmodSync(binaryPath, 0o755);
31
- } catch {}
32
- return spawn(binaryPath, args, { stdio: "inherit", cwd });
33
- }
34
-
35
- export interface RunOptions {
36
- entrypointValue?: unknown;
37
- resumeFrom?: string;
38
- logLevel?: string;
39
- logFile?: string;
40
- stateLog?: string;
41
- wake?: string;
42
- cwd?: string;
43
- }
44
-
45
- // ==================== Zod subset validation ====================
46
-
47
- const UNSUPPORTED_ZOD_TYPES = new Set([
48
- "ZodEffects", // .transform(), .refine(), .superRefine(), .preprocess()
49
- "ZodPipeline", // .pipe()
50
- "ZodBranded", // .brand()
51
- ]);
52
-
53
- function assertSerializableZod(schema: z.ZodType, stepName: string): void {
54
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
55
- const def = (schema as any)._def;
56
- if (!def) return;
57
-
58
- const typeName: string | undefined = def.typeName;
59
-
60
- if (typeName && UNSUPPORTED_ZOD_TYPES.has(typeName)) {
61
- throw new Error(
62
- `Step "${stepName}": Zod schema uses unsupported type "${typeName}". ` +
63
- `Only JSON-Schema-representable types are allowed. ` +
64
- `Remove .transform(), .refine(), .preprocess(), .pipe(), or .brand().`,
65
- );
66
- }
67
-
68
- // Recurse into compound types
69
- if (def.innerType) assertSerializableZod(def.innerType, stepName);
70
- if (def.schema) assertSerializableZod(def.schema, stepName);
71
- if (def.left) assertSerializableZod(def.left, stepName);
72
- if (def.right) assertSerializableZod(def.right, stepName);
73
-
74
- // z.object() — check each value
75
- if (def.shape) {
76
- const shape = typeof def.shape === "function" ? def.shape() : def.shape;
77
- for (const value of Object.values(shape)) {
78
- assertSerializableZod(value as z.ZodType, stepName);
79
- }
80
- }
81
-
82
- // z.array(), z.set()
83
- if (def.type) assertSerializableZod(def.type, stepName);
84
-
85
- // z.union(), z.discriminatedUnion()
86
- if (def.options) {
87
- for (const option of def.options) {
88
- assertSerializableZod(option as z.ZodType, stepName);
89
- }
90
- }
91
-
92
- // z.tuple()
93
- if (def.items) {
94
- for (const item of def.items) {
95
- assertSerializableZod(item as z.ZodType, stepName);
96
- }
97
- }
98
-
99
- // z.record()
100
- if (def.keyType) assertSerializableZod(def.keyType, stepName);
101
- if (def.valueType) assertSerializableZod(def.valueType, stepName);
102
- }
103
-
104
- // ==================== Config input types ====================
105
-
106
- type ZodConfigInput = z.input<typeof configSchema>;
107
- type ZodStepInput = ZodConfigInput["steps"][number];
108
-
109
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
110
- export type StepInput = Omit<ZodStepInput, "action"> & {
111
- action: { kind: "Bash"; script: string } | Handler<any, any>;
112
- stepConfig?: unknown;
113
- };
114
-
115
- export type ConfigInput = Omit<ZodConfigInput, "steps"> & {
116
- steps: StepInput[];
117
- };
118
-
119
- function resolveHandlers(config: ConfigInput): ZodConfigInput {
120
- return {
121
- ...config,
122
- steps: config.steps.map((step) => {
123
- const { stepConfig, action, ...stepRest } = step;
124
-
125
- if (!isHandler(action)) {
126
- return { ...stepRest, action } as ZodStepInput;
127
- }
128
-
129
- const def = action.__definition;
130
- const parsedStepConfig = def.stepConfigValidator.parse(stepConfig ?? null);
131
-
132
- const valueValidator = def.getStepValueValidator(parsedStepConfig);
133
- assertSerializableZod(valueValidator, step.name);
134
- const valueSchema = zodToJsonSchema(valueValidator, {
135
- target: "jsonSchema7",
136
- });
137
-
138
- return {
139
- ...stepRest,
140
- action: {
141
- kind: "TypeScript" as const,
142
- path: action.__filePath,
143
- exportedAs: "default",
144
- stepConfig: parsedStepConfig,
145
- valueSchema,
146
- },
147
- };
148
- }),
149
- };
150
- }
151
-
152
- // ==================== BarnumConfig ====================
153
-
154
- export class BarnumConfig {
155
- private readonly config: z.output<typeof configSchema>;
156
-
157
- private constructor(config: z.output<typeof configSchema>) {
158
- this.config = config;
159
- }
160
-
161
- static fromConfig(config: ConfigInput): BarnumConfig {
162
- const resolved = resolveHandlers(config);
163
- return new BarnumConfig(configSchema.parse(resolved));
164
- }
165
-
166
- run(opts?: RunOptions): ChildProcess {
167
- const args = opts?.resumeFrom
168
- ? ["run", "--resume-from", opts.resumeFrom]
169
- : ["run", "--config", JSON.stringify(this.config)];
170
- if (opts?.entrypointValue != null)
171
- args.push("--entrypoint-value", JSON.stringify(opts.entrypointValue));
172
- if (opts?.logLevel) args.push("--log-level", opts.logLevel);
173
- if (opts?.logFile) args.push("--log-file", opts.logFile);
174
- if (opts?.stateLog) args.push("--state-log", opts.stateLog);
175
- if (opts?.wake) args.push("--wake", opts.wake);
176
- args.push("--executor", resolveExecutor());
177
- args.push("--run-handler-path", runHandlerPath);
178
- return spawnBarnum(args, opts?.cwd);
179
- }
180
- }
package/types.ts DELETED
@@ -1,82 +0,0 @@
1
- import type { z } from "zod";
2
- import { fileURLToPath } from "node:url";
3
-
4
- export interface HandlerDefinition<C = unknown, V = unknown> {
5
- stepConfigValidator: z.ZodType<C>;
6
- getStepValueValidator: (stepConfig: C) => z.ZodType<V>;
7
- handle: (context: HandlerContext<C, V>) => Promise<FollowUpTask[]>;
8
- }
9
-
10
- export interface HandlerContext<C, V> {
11
- stepConfig: C;
12
- value: V;
13
- config: unknown;
14
- stepName: string;
15
- }
16
-
17
- export interface FollowUpTask {
18
- kind: string;
19
- value: unknown;
20
- }
21
-
22
- // ==================== Opaque Handler ====================
23
-
24
- const HANDLER_BRAND = Symbol.for("barnum:handler");
25
-
26
- export class Handler<C = unknown, V = unknown> {
27
- readonly [HANDLER_BRAND] = true as const;
28
- /** @internal */ readonly __filePath: string;
29
- /** @internal */ readonly __definition: HandlerDefinition<C, V>;
30
-
31
- /** @internal */
32
- constructor(definition: HandlerDefinition<C, V>, filePath: string) {
33
- this.__definition = definition;
34
- this.__filePath = filePath;
35
- }
36
-
37
- handle(context: HandlerContext<C, V>): Promise<FollowUpTask[]> {
38
- return this.__definition.handle(context);
39
- }
40
- }
41
-
42
- export function isHandler(x: unknown): x is Handler {
43
- return typeof x === "object" && x !== null && HANDLER_BRAND in x;
44
- }
45
-
46
- function getCallerFilePath(): string {
47
- const original = Error.prepareStackTrace;
48
- let callerFile: string | undefined;
49
-
50
- Error.prepareStackTrace = (_err, stack) => {
51
- // Frame 0: getCallerFilePath
52
- // Frame 1: createHandler
53
- // Frame 2: the file that called createHandler
54
- const frame = stack[2];
55
- callerFile = frame?.getFileName() ?? undefined;
56
- return "";
57
- };
58
-
59
- const err = new Error();
60
- void err.stack;
61
- Error.prepareStackTrace = original;
62
-
63
- if (!callerFile) {
64
- throw new Error(
65
- "createHandler: could not determine caller file path from stack trace. " +
66
- "Pass the path explicitly: createHandler(definition, { path: import.meta.filename })",
67
- );
68
- }
69
-
70
- if (callerFile.startsWith("file://")) {
71
- return fileURLToPath(callerFile);
72
- }
73
- return callerFile;
74
- }
75
-
76
- export function createHandler<C, V>(
77
- definition: HandlerDefinition<C, V>,
78
- opts?: { path?: string },
79
- ): Handler<C, V> {
80
- const filePath = opts?.path ?? getCallerFilePath();
81
- return new Handler(definition, filePath);
82
- }