npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@chaos-scheduler/sdk

v1.1.0

Published

TypeScript SDK for the Chaos Scheduler REST API (/api/v1) — register environments/workflows, run/enqueue on demand, poll runs, and verify webhook signatures.

Readme

@chaos-scheduler/sdk

Typed TypeScript client for the Chaos Scheduler REST API (/api/v1), plus webhook signature helpers. It is the foundation for the @chaos-scheduler/mcp-server and for any external system that registers workflows, triggers runs on demand, polls results, or receives result webhooks.

  • Zero runtime dependencies (uses the global fetch and node:crypto).
  • Ships ESM + CJS + .d.ts.
  • Wire types are hand-derived from the Rust backend — see Source of truth.

Install

npm install @chaos-scheduler/sdk

Requires Node.js ≥ 18 (for global fetch). Pass a fetch implementation in the options for older runtimes.

Quick start

import { ChaosSchedulerClient } from "@chaos-scheduler/sdk";

const client = new ChaosSchedulerClient({
  baseUrl: "http://127.0.0.1:9618", // the scheduler's loopback API
  apiKey: process.env.CHAOS_SCHEDULER_API_KEY, // "<id>.<secret>"
});

// 1) Register an externally-managed workflow (scope: write)
const wf = await client.registerWorkflow({
  name: "Nightly digest",
  script_path: "scripts/digest.py",
  cron_schedule: "0 6 * * *",
  environment: "production",
});

// 2) Enqueue a run, safely retryable via an idempotency key (scope: write)
const outcome = await client.enqueueWorkflow(wf.id, {
  idempotencyKey: crypto.randomUUID(),
});

// 3) Poll the run to completion (read)
if (!("workflow_id" in outcome)) {
  // duplicate replay — nothing new started
} else if (outcome.run_id) {
  const run = await client.waitForRun(outcome.run_id);
  console.log(run.status, run.exit_code);
}

Authentication

The API authenticates with scoped API keys minted in the Scheduler UI (Integrations screen) or via Tauri commands. A token is "<id>.<secret>" and is sent as Authorization: Bearer <id>.<secret>. Scopes:

| Scope | Grants | | ------- | -------------------------------------------- | | read | list/get environments, workflows, runs | | write | create/register/delete, run/enqueue/dispatch | | admin | superuser (implies read + write) |

write and admin keys are local-code-execution credentials: a holder can register or run workflows that execute commands on the scheduler host. Store them in a secret manager, rotate/revoke aggressively, and avoid putting them in logs, prompts, or issue trackers.

Protected environments default to prod,production. Backend write paths refuse to create, edit, delete, or execute workflows in those environments unless the scheduler process is started with CHAOS_SCHEDULER_ALLOW_PROTECTED_WRITES=1. Override the protected-name list with CHAOS_SCHEDULER_PROTECTED_ENVIRONMENTS=prod,production,....

getHealth() and getVersion() need no key.

Note: API-key creation/listing is not exposed over REST; keys are managed inside the desktop app. The SDK therefore has no key-management methods.

Idempotency

runWorkflow, enqueueWorkflow, dispatchWorkflow, and rerunWorkflow accept an idempotencyKey. Reusing a key returns the original result as { status: "duplicate", run_id, queued_run_id }. Queued dispatches replay with queued_run_id; admitted dispatches replay with run_id. Use the isDuplicateDispatch guard:

import { isDuplicateDispatch } from "@chaos-scheduler/sdk";

const res = await client.enqueueWorkflow(id, { idempotencyKey: key });
if (isDuplicateDispatch(res)) {
  // replay: res.run_id or res.queued_run_id points at the first request
} else {
  // fresh dispatch: res.status is admitted/queued/skipped, res.run_id is new
}

Inbound webhook trigger (signed)

POST /api/v1/workflows/{id}/dispatch accepts a raw body forwarded to the workflow's webhook trigger. If an inbound secret is configured, sign the canonical payload (not raw-body HMAC):

METHOD\nPATH\nTIMESTAMP\nSHA256_HEX(body) → hex(HMAC_SHA256(secret, canonical))

The client sets X-Chaos-Timestamp, X-Chaos-Event-Id, and X-Chaos-Signature when you pass signatureSecret:

await client.dispatchWorkflow(id, {
  payload: JSON.stringify({ event: "push", ref: "main" }),
  signatureSecret: process.env.INBOUND_SECRET,
  // optional: timestamp, eventId for pinned replays
});

Helpers: computeInboundDispatchSignature, inboundDispatchHeaders, verifyInboundDispatchSignature. Vectors: packages/test-fixtures/webhook-vectors.v1.json.

Verifying outbound result webhooks

The scheduler's webhook action POSTs the run result to your endpoint with:

  • X-Chaos-Event: run.succeeded | run.failed
  • X-Chaos-Signature: sha256=<hex HMAC-SHA256 of the raw body>

Verify it over the raw request body (never a re-serialized object):

import { verifyWebhookSignature } from "@chaos-scheduler/sdk";
import express from "express";

const app = express();
app.post(
  "/chaos-webhook",
  express.raw({ type: "application/json" }),
  (req, res) => {
    const ok = verifyWebhookSignature(
      req.body, // Buffer of raw bytes
      req.header("x-chaos-signature"),
      process.env.WEBHOOK_SECRET!,
    );
    if (!ok) return res.status(401).end();
    const result = JSON.parse(req.body.toString("utf8"));
    // handle result…
    res.status(200).end();
  },
);

The signature scheme (hex(HMAC_SHA256(secret, raw_body))) is verified in the SDK's test suite against a cross-implementation vector shared with the backend.

Case sensitivity: verifyWebhookSignature accepts uppercase hex digests, but the scheduler's inbound webhook verifier (api.rs::verify_inbound_webhook) compares signatures case-sensitively. Emit lowercase sha256=<hex> when calling the scheduler API.

API surface

Client methods (all return typed models):

| Method | Endpoint | Scope | | ---------------------------------------- | ------------------------------------------- | ----- | | getHealth() | GET /api/v1/health | — | | getVersion() | GET /api/v1/version | — | | listEnvironments() | GET /api/v1/environments | read | | createEnvironment(input) | POST /api/v1/environments | write | | listWorkflows() | GET /api/v1/workflows | read | | getWorkflow(id) | GET /api/v1/workflows/{id} | read | | registerWorkflow(input) | POST /api/v1/workflows | write | | deleteWorkflow(id) | DELETE /api/v1/workflows/{id} | write | | setWorkflowSpec(id, spec) | POST /api/v1/workflows/{id}/spec | write | | runWorkflow(id, opts) | POST /api/v1/workflows/{id}/run | write | | enqueueWorkflow(id, opts) | POST /api/v1/workflows/{id}/enqueue | write | | dispatchWorkflow(id, opts) | POST /api/v1/workflows/{id}/dispatch | write | | listRuns(id) | GET /api/v1/workflows/{id}/runs | read | | getRun(id) | GET /api/v1/runs/{id} | read | | getRunLogs(id) | GET /api/v1/runs/{id}/logs | read | | getRunTasks(id) | GET /api/v1/runs/{id}/tasks | read | | getRunMetrics(id) | GET /api/v1/runs/{id}/metrics | read | | listQueues() | GET /api/v1/queues | read | | listQueuedRuns() | GET /api/v1/queued-runs | read | | updateWorkflow(id, input) | PATCH /api/v1/workflows/{id} | write | | rerunWorkflow(id, opts) | POST /api/v1/workflows/{id}/rerun | write | | listEmailProfiles() | GET /api/v1/email-profiles | read | | createEmailProfile(input) | POST /api/v1/email-profiles | write | | updateEmailProfile(id, in) | PATCH /api/v1/email-profiles/{id} | write | | deleteEmailProfile(id) | DELETE /api/v1/email-profiles/{id} | write | | setWorkflowEmailProfile(id, profileId) | POST /api/v1/workflows/{id}/email-profile | write | | waitForRun(runId, opts) | polls GET /api/v1/runs/{id} | read |

Deprecated: runWorkflow (POST /api/v1/workflows/{id}/run) is an alias of enqueueWorkflow. Manual runs are admission-controlled — /run does not execute immediately or bypass the queue, it shares the same admission path as /enqueue — so prefer enqueueWorkflow. runWorkflow keeps working unchanged.

Email-profile smtp_password values are masked (••••••••, exported as MASKED_SECRET) on read; echo the mask back on update to preserve the stored secret, or send a new value to replace it.

waitForRun throws if the run does not reach a terminal status before timeoutMs (default 300000) elapses; catch the error to distinguish a slow run from a failed one.

Webhook helpers: computeWebhookSignature, webhookSignatureHeader, verifyWebhookSignature. Errors: ChaosApiError (.status, .isAuthError, .isRateLimited, .isNotFound).

verifyWebhookSignature compares the hex digest case-insensitively (and tolerates a missing sha256= prefix). The backend emits lowercase hex with the prefix, so this only relaxes acceptance and never rejects a valid backend-signed request.

Source of truth

There is no generated OpenAPI document yet. The wire types in src/types.ts are hand-derived from the Rust backend and must be kept in sync with:

  • src-tauri/src/db.rsWorkflow, Run, Environment
  • src-tauri/src/workflow_spec.rsWorkflowSpec and friends
  • src-tauri/src/actions.rsActionSpec, HMAC sign_payload
  • src-tauri/src/scheduler.rsDispatchOutcome
  • src-tauri/src/api.rs — routes, request bodies, response envelopes

Development

npm install
npm run build      # tsup → dist (ESM + CJS + d.ts)
npm test           # vitest
npm run typecheck  # tsc --noEmit

License

MIT