@deepnoodle/mobius
v0.0.21
Published
TypeScript SDK for Mobius workflow orchestration
Maintainers
Readme
@deepnoodle/mobius
TypeScript SDK for Mobius — a work coordination platform for mixed teams of humans, systems, and AI agents.
This package contains the runtime client and worker used to claim and execute tasks against the Mobius API, plus high-level helpers for starting, observing, and controlling workflow runs. Types are generated from the canonical OpenAPI spec and round-tripped against the same cross-language contract fixtures as the Go and Python SDKs.
Install
npm install @deepnoodle/mobius
# or
pnpm add @deepnoodle/mobiusRequires Node.js 18+.
Quick start
Worker
import { Client, Worker } from "@deepnoodle/mobius";
const client = new Client({ apiKey: process.env.MOBIUS_API_KEY! });
const worker = new Worker(client, {
name: "email-sender",
version: "1.0.0",
queues: ["emails"],
// Hold up to 5 jobs in flight from one process. Surfaces as a
// single row on the workers page with a saturation bar.
concurrency: 5,
});
worker.register("send_email", async (params, signal) => {
// send email...
return { sent: true };
});
await worker.run();The worker_instance_id is auto-detected from the runtime platform
(Cloud Run revision, Kubernetes pod, Fly machine, Railway replica,
Render instance) and falls back to a per-boot UUID. Set
workerInstanceId explicitly only for stable singleton workers — two
live processes using the same override in the same project will
collide and the second will throw WorkerInstanceConflictError.
For independent presence rows (one row per worker) — e.g. graceful
draining or in-flight isolation — use WorkerPool with count and
optionally workerInstanceIdPrefix instead.
Low-level client
import { Client } from "@deepnoodle/mobius";
const client = new Client({ apiKey: process.env.MOBIUS_API_KEY! });
const claim = await client.claimJob({
worker_instance_id: "my-worker-1",
worker_session_token: crypto.randomUUID(),
concurrency_limit: 1,
queues: ["default"],
wait_seconds: 20,
});Run control
const run = await client.startRun(
{
name: "demo",
steps: [],
},
{
external_id: "customer-run-123",
metadata: { org_id: "org_123" },
},
);
const terminal = await client.waitRun(run.id);
console.log(terminal.status, terminal.result_b64, terminal.error_message);Webhooks
import {
parseSignedWebhookRequest,
} from "@deepnoodle/mobius";
const signed = await parseSignedWebhookRequest(
request,
process.env.MOBIUS_WEBHOOK_SECRET!,
);
console.log(signed.event.type, signed.event.data);Saved workflows
const result = await client.ensureWorkflow(
{ name: "customer-onboarding", steps: [] },
{ handle: "customer-onboarding" },
);
console.log(result.created, result.updated, result.definition.id);Rate limiting
The client retries 429 Too Many Requests and 503 Service Unavailable
responses automatically, respecting the server's Retry-After header and
falling back to exponential backoff (1s, 2s, 4s, capped at 60s). Non-
idempotent POST / PATCH requests are retried only when they carry an
Idempotency-Key header; otherwise a 429 surfaces as RateLimitError
immediately.
import { Client, RateLimitError } from "@deepnoodle/mobius";
const client = new Client({
apiKey: process.env.MOBIUS_API_KEY!,
retry: 3, // default; set to 0 to disable retries entirely
});
try {
await client.claimJob({
worker_instance_id: "w1",
worker_session_token: crypto.randomUUID(),
concurrency_limit: 1,
queues: ["default"],
});
} catch (err) {
if (err instanceof RateLimitError) {
console.warn(
`rate limited on ${err.scope} scope; retry after ${err.retryAfter}s`,
);
}
}The full policy is documented in
docs/retries.md.
Documentation
License
Apache-2.0
