Node SDK
Node workers, Next.js build tasks, Deno/Bun scripts.
The Node SDK mirrors the Python ergonomics - same `track` higher-order function, same `ChirpAgent` callback form, same end-state semantics. Zero runtime dependencies; ships TypeScript types out of the box. Works in Node ≥ 18, Bun, Deno (via npm:chirp), and edge runtimes that expose `fetch`.
An additional `chirp/express` submodule ships an error-middleware variant for Express/Fastify routes - wraps the request handler so the activity ends red on a 5xx and green on 2xx.
Prerequisites
- Node ≥ 18 (or Bun/Deno).
- Chirp installed on your phone, signed in.
Setup
- 1
Install + authenticate
Same one-time browser pairing as the Python SDK. CI uses
CHIRP_API_KEYenv var.shellnpm install chirp npx chirp login - 2
Wrap an async function with `track()`
Higher-order pattern:
track(opts, fn)returns a wrapped version offnthat owns the activity lifecycle. Resolves green, rejects red.deploy.tsimport { track, update } from 'chirp'; const deploy = track( { name: 'deploy', schema: '@deploy' }, async (env: string) => { await build(); update({ stage: 'pushing', progress: 0.6 }); await push(env); update({ stage: 'verifying', progress: 0.9 }); await smoke(env); }, ); await deploy('prod'); - 3
Use `ChirpAgent` callback form for finer control
When you need to start/end the activity manually or branch the lifecycle.
typescriptimport { ChirpAgent } from 'chirp'; const agent = new ChirpAgent({ name: 'sync', schema: '@progress' }); await agent.start(); try { await agent.update({ progress: 0.5, metaLeft: 'halfway' }); await doWork(); await agent.end({ status: 'done' }); } catch (err) { await agent.end({ status: 'failed', message: String(err) }); throw err; } - 4
Express middleware variant
Mount Chirp as request middleware. Each request becomes its own activity; HTTP status drives the final state. Great for cron-triggered HTTP endpoints (Vercel/Cloudflare Cron, AWS EventBridge → API Gateway).
typescriptimport express from 'express'; import { chirpMiddleware } from 'chirp/express'; const app = express(); app.post('/cron/nightly-sync', chirpMiddleware({ name: 'nightly sync', schema: '@progress' }), async (req, res) => { await runSync(); res.json({ ok: true }); }, );
What you’ll see
Same lifecycle as Python: starts on entry, updates as you call `update()`, ends green/red on resolution/rejection. Multiple concurrent calls each spawn their own activity instance.
Troubleshooting
- TypeScript complains about `update` arguments.
- The
update()shape is typed against the schema you passed at start time. If you usedschema: '@deploy', only the @deploy bindings (stage,progress,repo, etc.) typecheck. Cast or pick a different schema if you need custom keys. - Activity doesn't appear in production but works locally.
- Check
CHIRP_API_KEYis set in your production env. The SDK falls back to silent failure when no credentials are available - your code keeps running, but Chirp isn't told. Usechirp.diagnose()to see why no key was found. - Edge runtime (Vercel Edge / Cloudflare Workers) can't import `chirp`.
- Use
chirp/edgeinstead - same API, but uses Web Crypto and Web fetch instead of Node primitives. Tree-shakes to ~3KB.