Skip to content

A worker in Node

Terminal window
oren init worker my-task --template node
import { task } from '@oren-sh/sdk';
type Inputs = { defaultChange: 'major' | 'minor' | 'patch' | 'none' };
type Outputs = { change: string; commitCount: number };
await task<Inputs, Outputs>(async ({ inputs, path, log }) => {
const source = path('source');
log('analysing', source);
return { change: inputs.defaultChange, commitCount: 3 };
});

Whatever the function returns becomes the task’s output. Throwing aborts the step with exit code 1 — the CLI handles the rest.

inputs inputs validated against the contract, with defaults applied
execution pipeline, step, task, mounted dependencies
path(name) the path where a dependency was mounted
log(...) writes to stderr

TaskError has its message shown without a stack trace — it is for whoever wrote the pipeline, not for whoever wrote the worker.

import { task, TaskError } from '@oren-sh/sdk';
await task(async ({ path }) => {
const source = path('source');
if (!existsSync(join(source, '.git'))) {
throw new TaskError(`${source} does not contain a git repository`);
}
// ...
});

A worker in shell lands at about 34 MB; the same one in Node, 283 MB — the runtime. In exchange you get types on inputs and outputs, structured errors and logic testable outside the container.

Both satisfy the same contract and ask for exactly the same dependencies, so the choice is about maintenance, not about privilege.

The protocol is reading one file and writing another. A worker in Go, Python or Rust works the same, with nothing from Node involved. This package exists so you do not reimplement path lookup and error handling in every worker.