Skip to content

Node SDK

modelreins-worker (Node) is the official JavaScript SDK and CLI for running silicon workers against ModelReins. It ships providers for Claude, Aider, Codex, Ollama, LM Studio, Nocturne, 1min.ai, and Playwright (headless-browser automation), plus a full killswitch + rate-limit classification surface.

Published on npm: npmjs.com/package/modelreins-worker

Terminal window
# Run without installing (simplest)
npx modelreins-worker
# Or install globally
npm install -g modelreins-worker
# Or as a project dep (to use the Worker class programmatically)
npm install modelreins-worker

Requires Node ≥ 18.

Terminal window
# Env-based
MODELREINS_URL=https://app.modelreins.com \
MODELREINS_TOKEN=<your-worker-token> \
npx modelreins-worker
# Or flag-based
npx modelreins-worker \
--server https://app.modelreins.com \
--token <your-worker-token> \
--name my-worker \
--provider claude
Terminal window
modelreins-worker config set server https://app.modelreins.com
modelreins-worker config set token <your-worker-token>
modelreins-worker config set provider playwright
modelreins-worker config list # dumps config (secrets masked)
modelreins-worker config path # prints config file location
modelreins-worker config reset # nukes config
Terminal window
modelreins-worker --setup

Walks through server URL, token (pasted from the one-time-view URL), worker name, provider pick. Writes to the config file. Any flag on the command line overrides config.

Pick a provider with --provider <name> or MODELREINS_WORKER_TYPE=<name>. Each provider is a thin shim that translates a ModelReins job into the provider’s execution shape.

ProviderWhat it runsUse when
claudeclaude CLI (Anthropic)You have a Claude Code install and want the worker to dispatch jobs through it
aideraider CLICode editing + git-aware flows
codexOpenAI codex CLIOpenAI-hosted coding agent
ollama-httpOllama HTTP APILocal GGUF inference on any machine running Ollama
lmstudioLM Studio’s OpenAI-compatible APILocal inference via LM Studio
nocturneNocturne endpointSelf-hosted Mediagato inference cluster
onemin1min.ai APIPaid managed API
httpGeneric HTTP endpointAny OpenAI-compatible server
playwrightHeadless Chromium via PlaywrightBrowser-automation workers (scraping, form-filling, UI actions)
spawnArbitrary shell commandCustom executor — see executor option

Uses playwright to spawn a headless Chromium for each job. The job prompt is delivered as JSON containing the browser instructions; the output is whatever the page produced (screenshot URL, extracted text, form submission result).

Terminal window
# Ensure playwright + chromium are installed on the worker machine:
npm install playwright
npx playwright install chromium
# Launch the worker with the playwright provider:
modelreins-worker --provider playwright --name pw-worker-01

Artifacts the worker produces (screenshots, downloaded files) can be streamed back to the fleet via the artifact tunnel; use upload_artifact() from the Python SDK from inside a custom executor, or post to /artifacts/upload directly. Node-side upload_artifact is on the roadmap.

const { Worker, ERROR_TYPES, classifyError } = require('modelreins-worker');
const w = new Worker({
url: 'https://app.modelreins.com',
token: process.env.MODELREINS_TOKEN,
name: 'my-worker',
type: 'claude', // provider
model: 'opus',
tags: 'code,review',
networks: 'lan',
pollMs: 5000,
workDir: '/tmp/work',
// Custom executor — bypasses provider selection entirely
executor: async (job, ctx) => {
await ctx.output(`starting job ${job.id}\n`);
// ... do the work ...
await ctx.output('done\n');
return 0;
},
});
w.on('ready', ({ name }) => console.log(`${name} ready`));
w.on('job:start', ({ id, prompt }) => console.log(`start ${id}`));
w.on('job:done', ({ id, exitCode }) => console.log(`done ${id} (${exitCode})`));
w.on('job:error', ({ id, error, errorType, retryable }) => console.error(`err ${id}: ${error}`));
w.on('killswitch', ({ reason }) => console.warn(`killswitch fired: ${reason}`));
w.on('error', ({ type, error }) => console.error(`${type}: ${error}`));
await w.start(); // begin polling
// ... later ...
await w.stop(); // graceful shutdown
EventPayloadFires when
starting{ name, url }Before first heartbeat
connected{ name }First heartbeat acked
ready{ name }Provider initialized + polling
stopped{ name }After .stop() completes
job:start{ id, prompt }Job claimed
job:done{ id, exitCode }Handler returned (exit 0)
job:error{ id, error, errorType, retryable }Handler threw; errorType is one of ERROR_TYPES
output{ jobId, content, stream }Stdout/stderr line from the job
killswitch{ reason }Server-side killswitch activated (see below)
error{ type, error }Any non-job error (heartbeat, config, output flush)
context{ policy, size, context }Context loaded for a job via /context
config:applied{ key, value }Hot-reloaded config took effect

The worker polls GET /killswitch every ~60 seconds (once per 12 inbox polls). When the server returns { active: true }, the worker emits a killswitch event, stops cleanly, and exits. Use this for fleet-wide halts during incidents or budget cut-offs.

Listen for it if you want to do local cleanup before exit:

w.on('killswitch', ({ reason }) => {
console.warn(`Killswitch fired: ${reason}`);
// ... close local files, notify ops, etc. ...
});

The server endpoint is tenant-scoped, so a killswitch in tenant A doesn’t halt tenant B’s workers.

Worker errors get classified by classifyError(error, exitCode, stderr) into one of:

ERROR_TYPES constantRetryableTriggered by
SPAWN_FAILEDnoENOENT, “not found”, “permission denied”
AUTH_FAILEDno”not logged in”, “unauthorized”, 401, 403
RATE_LIMITEDyes”rate limit”, “429”, “too many”, “hit your limit”, “usage limit”, “limit reached”, “resets “
TIMEOUTyes”timeout”, “etimedout”
NETWORKyes”econnrefused”, “enotfound”, “dns”
OOMnoexit code 137
EXIT_ERRORnoany non-zero exit
UNKNOWNnofallback

The RATE_LIMITED classifier is intentionally fuzzy — the phrases were drawn from real provider responses across Claude, Codex, 1min.ai, Ollama, etc. If your provider phrases a rate-limit message differently, file an issue with the exact stderr line and it’ll get added.

Retryable errors emit job:error with retryable: true; ModelReins’ scheduler uses that to put the job back into retry status rather than failed.

const { classifyError } = require('modelreins-worker');
const cls = classifyError({ message: 'HTTP 429 Too Many Requests' });
// { type: 'rate_limited', message: 'Rate limited', retryable: true }

All WorkerOptions have matching env-var defaults:

OptionEnv var
urlMODELREINS_URL
tokenMODELREINS_TOKEN / MODELREINS_API_KEY
nameMODELREINS_WORKER
typeMODELREINS_WORKER_TYPE
modelMODELREINS_WORKER_MODEL
tagsMODELREINS_WORKER_TAGS
networksMODELREINS_WORKER_NETWORKS
pollMsMODELREINS_POLL_MS
workDirMODELREINS_WORKDIR
commandMODELREINS_COMMAND
promptArgMODELREINS_PROMPT_ARG
extraArgsMODELREINS_EXTRA_ARGS
outputFormatMODELREINS_OUTPUT_FORMAT

Precedence: constructor option → env var → config file → default.

Terminal window
DEBUG=modelreins:* npx modelreins-worker
  • Python SDK — the sister SDK with upload_artifact() + submit_review()
  • Providers — full provider catalog and provider-specific guides
  • Jobs — job lifecycle, context policies, retry states
  • What’s new — release history