All integrations

Node SDK

Node workers, Next.js build tasks, Deno/Bun scripts.

LiveSDKchirp login

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. 1

    Install + authenticate

    Same one-time browser pairing as the Python SDK. CI uses CHIRP_API_KEY env var.

    shell
    npm install chirp
    npx chirp login
  2. 2

    Wrap an async function with `track()`

    Higher-order pattern: track(opts, fn) returns a wrapped version of fn that owns the activity lifecycle. Resolves green, rejects red.

    deploy.ts
    import { 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. 3

    Use `ChirpAgent` callback form for finer control

    When you need to start/end the activity manually or branch the lifecycle.

    typescript
    import { 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. 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).

    typescript
    import 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 used schema: '@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_KEY is 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. Use chirp.diagnose() to see why no key was found.
Edge runtime (Vercel Edge / Cloudflare Workers) can't import `chirp`.
Use chirp/edge instead - same API, but uses Web Crypto and Web fetch instead of Node primitives. Tree-shakes to ~3KB.