SDK
Node / generic
The stillrunningpackage monitors any async job, agent, or script. Wrap a function and StillRunning gets a ping on success or failure with the run's duration, plus optional cost, tokens, and model. Framework-agnostic, works with Claude Code scripts, LangChain, CrewAI, cron jobs, queue workers, anything.
On the Vercel AI SDK? Use stillrunning-vercel-ai-sdk instead, it auto-extracts tokens and cost for you.
Install
npm install stillrunning30-second quickstart
1. Create a workflow at stillrunning.ai/app/new and copy its token. 2. Set STILLRUNNING_TOKEN. 3. Wrap your work:
import { stillrunning } from 'stillrunning'
const sr = stillrunning() // reads STILLRUNNING_TOKEN
await sr.run(() => runNightlyImport())run times the function, pings success when it resolves, pings failwith the error message if it throws (then rethrows it untouched), and returns the function's result.
Attaching AI metrics
Pass meta, statically or derived from the result. Give model plus token counts and cost is estimated automatically from a built-in pricing table:
const answer = await sr.run(() => callMyAgent(prompt), {
meta: (result) => ({
tokensIn: result.usage.inputTokens,
tokensOut: result.usage.outputTokens,
model: result.model, // -> costUsd estimated automatically
toolCalls: result.steps.length,
}),
})Wrapping a function
wrap returns a monitored version with the same signature:
const syncCustomer = sr.wrap(rawSyncCustomer)
await syncCustomer('acme') // every call is monitoredHeartbeats and manual pings
For a cron job that just needs to say "I ran", or when the work can't be wrapped in one function:
await sr.heartbeat() // bare success ping
await sr.ping({ event: 'start', traceId }) // low-level: success | fail | start | logGrouping multi-step runs
Wrap several runs in withTrace so they share one traceId and stitch into a single outcome chain:
import { stillrunning, withTrace } from 'stillrunning'
const sr = stillrunning()
await withTrace(async () => {
await sr.run(() => stepOne())
await sr.run(() => stepTwo())
}) // both pings share one traceIdConfiguration
stillrunning({
token, // ping token; defaults to process.env.STILLRUNNING_TOKEN
baseUrl, // defaults to https://stillrunning.ai
awaitPing, // default true; false = fire-and-forget (lowest latency)
pingTimeoutMs, // default 3000
onError, // (err) => void — observe ping delivery failures
fetch, // custom fetch (testing / non-global-fetch runtimes)
})Monitoring never throws into your code: a failed ping routes to onError and is otherwise swallowed, bounded by pingTimeoutMs so a slow StillRunning never hangs your job.
Requirements
Node 18+ (or any runtime with fetch and AsyncLocalStorage).
You're set
Open your dashboard to watch runs land with duration, cost, and anomaly alerts.
Open dashboard