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

deepline

v0.1.36

Published

Deepline SDK + CLI — B2B data enrichment powered by durable cloud execution

Readme

Deepline V2 SDK + CLI

TypeScript SDK and CLI for Deepline plays, tools, and enrichment.

Dev workflow

Prerequisites

bun run dev          # starts Next.js + Convex + Temporal + worker

Wait for health: curl http://localhost:3000/api/v2/health

Auth (one-time)

./cli-env sdk-worktree deepline auth register

Opens browser for approval. Stores SDK CLI auth in ~/.local/deepline/<host-slug>/.env.

Check status: ./cli-env sdk-worktree deepline auth status --json

Making changes

Edit any file in sdk/src/ and re-run. No build step needed — deepline-dev.ts runs source directly via Bun.

# Edit sdk/src/cli/commands/play.ts
# Then immediately:
./cli-env sdk-worktree deepline plays run --file my-play.play.ts --watch

The root package.json has an alias, bun run deepline, that runs the same source CLI without selecting a host. Use ./cli-env sdk-worktree deepline ... for local app/worktree development so the binary and DEEPLINE_HOST_URL move together.

When you need to build

Only two cases require npm run sdk:build:

  1. npx --package=./sdk -- deepline — the path used by integration tests
  2. Testing the published artifact — verifying dist/cli/index.mjs works
cd sdk && bun run build    # or: npm run sdk:build from root

Env files

| File | Set by | Contains | | ------------------------------------ | ------------------- | ----------------------------------------- | | ~/.local/deepline/<host-slug>/.env | SDK auth register | DEEPLINE_HOST_URL, DEEPLINE_API_KEY | | .env.deepline | Developer/agent | DEEPLINE_HOST_URL, DEEPLINE_API_KEY |

The SDK CLI env contract is only DEEPLINE_HOST_URL and DEEPLINE_API_KEY. Do not route the SDK CLI through .env, .env.local, or .env.worktree.

Config resolution order:

host: explicit SDK option -> DEEPLINE_HOST_URL -> nearest .env.deepline -> saved production auth -> https://code.deepline.com
key:  explicit SDK option -> DEEPLINE_API_KEY -> matching nearest .env.deepline -> saved auth for the resolved host

CLI commands

./cli-env sdk-worktree deepline health
./cli-env sdk-worktree deepline auth register
./cli-env sdk-worktree deepline auth status
./cli-env sdk-worktree deepline tools list
./cli-env sdk-worktree deepline tools describe test_rate_limit
./cli-env sdk-worktree deepline tools execute test_rate_limit --input '{"key":"stripe.com"}'
./cli-env sdk-worktree deepline plays run my-play.play.ts --input '{}' --watch
./cli-env sdk-worktree deepline plays run company-lookup --input '{}' --watch
./cli-env sdk-worktree deepline plays get company-lookup --json
./cli-env sdk-worktree deepline plays versions company-lookup --json
./cli-env sdk-worktree deepline runs list --play company-lookup --status failed --json
./cli-env sdk-worktree deepline runs get run_123 --json
./cli-env sdk-worktree deepline runs tail run_123 --json
./cli-env sdk-worktree deepline runs stop run_123 --reason "no longer needed" --json
./cli-env sdk-worktree deepline plays publish my-play.play.ts --latest --json
./cli-env sdk-worktree deepline update

tools describe and tools execute are canonical. tools get and tools run remain compatibility aliases. Unknown plays run flags such as --limit 5 are passed into the play input object. CSV file inputs should be passed through --input, matching the play contract, for example --input '{"file":"leads.csv"}' for ctx.csv(input.file).

Public play entrypoints

These are the public ways to run plays. They intentionally map to the external surfaces we exercise in CI:

  1. CLI
    • Repo-local Bun/source CLI: bun sdk/bin/deepline-dev.ts play run ...
    • Repo script alias: bun run deepline -- play run ...
    • Installed CLI: deepline play run ...
  2. Programmatic SDK convenience client
    • client.runPlay(...)
    • client.runs.get(runId)
    • client.runs.list({ play: name })
    • client.runs.tail(runId)
    • client.runs.stop(runId, { reason })
  3. High-level SDK handles
    • ctx.play(name).run(...)
    • ctx.play(name).runSync(...)
    • ctx.runPlay(name, input)
  4. Raw HTTP API
    • POST /api/v2/plays/run
    • GET /api/v2/plays/run/:workflowId
  5. In-play composition
    • ctx.runPlay(...)

Internal / advanced only:

  • registerPlayArtifact()
  • stagePlayFiles()
  • startPlayRun()
  • getPlayStatus()
  • getPlayTailStatus()
  • listPlayRuns()
  • stopPlay()

Those low-level primitives are still available for callers that explicitly need to own the package-submit-poll flow, but they are not the primary public SDK surface.

SDK usage (programmatic)

import { Deepline, definePlay } from 'deepline';

const ctx = await Deepline.connect();

// Tools
const tools = await ctx.tools.list();
const result = await ctx.tools.execute({
  tool: 'test_company_search',
  input: {
    domain: 'stripe.com',
  },
});
const company = result.value;

// Named play (already live)
const job = await ctx.play('my-play').run({ domain: 'example.com' });
const status = await job.status();
const output = await job.get(); // polls until complete

// Define + run a play
const myPlay = definePlay('my-play', async (ctx, input: { domain: string }) => {
  const company = await ctx.tools.execute({
    id: 'company_search',
    tool: 'test_company_search',
    input: { domain: input.domain },
    description: 'Look up company details by domain.',
  });
  return { company: company.result };
});

const detail = await myPlay.get();
const result = await myPlay.runSync({ domain: 'example.com' });

Testing

# Unit tests (no server needed)
bun run vitest run tests/lib/plays tests/api/plays-get-route.test.ts

# SDK + CLI integration (needs running dev server)
bash scripts/test-sdk-play-cli.sh

# V2 plays e2e — all free test tools, real Temporal (needs running dev server)
bun tests/v2-plays/runner.ts --top 10
bun tests/v2-plays/runner.ts --shard 1/4
bun tests/v2-plays/runner.ts --file tests/v2-plays/plays/07-waterfall-basic.play.ts

Repro: rate limit handling (end-to-end)

Full sequence from zero to hitting + surviving rate limits via v2 plays.

1. Install + auth

# Production
bash <(curl -sS https://code.deepline.com/api/v2/sdk/install)

# Local dev (no install needed)
bun run dev   # start server
bun sdk/bin/deepline-dev.ts auth register

2. Write a play that hammers the rate-limited test tool

cat > rate-limit-repro.play.ts << 'TS'
import { definePlay } from 'deepline';

// test_rate_limit has a 5 req/s shared window — this play fires 20 calls
// to force 429s and prove the runtime retries + backs off correctly.
export default definePlay('rate-limit-repro', async (ctx) => {
  const items = Array.from({ length: 20 }, (_, i) => ({
    key: `probe-${i}`,
    row_number: i + 1,
  }));

  const results = await ctx.map('rate_limit_probes', items)
  .step("result", (row, ctx) =>
      ctx.tools.execute({
        id: 'rate_limit_probe',
        tool: 'test_rate_limit',
        input: {
        key: 'redis-shared-scenario',
        row_number: row.row_number,
      },
        description: 'Exercise the shared rate-limit retry path.',
      }))
  .run();

  return { total: results.length, results };
});
TS

3. Run it

# Dev
bun sdk/bin/deepline-dev.ts play run --file rate-limit-repro.play.ts --watch

# Production
deepline play run --file rate-limit-repro.play.ts --watch

4. Fallback variant (multi-step with rate limits)

Use steps() and when() to express row-level fallback sequences. Skipped conditional steps yield null.

cat > rate-limit-waterfall.play.ts << 'TS'
import { definePlay, steps, when } from 'deepline';

export default definePlay('rate-limit-waterfall', async (ctx) => {
  const leads = Array.from({ length: 24 }, (_, i) => ({
    lead_id: `lead-${String(i + 1).padStart(3, '0')}`,
    row_number: i + 1,
  }));

  const probeFallback = steps<{ lead_id: string; row_number: number }>()
    .step("primary_probe", (row, ctx) =>
      ctx.tool({
        id: 'rate_limit_probe',
        tool: 'test_rate_limit',
        input: {
        key: 'redis-shared-scenario',
        lead_id: row.lead_id,
        row_number: row.row_number,
      },
        description: 'Exercise primary rate-limit behavior.',
      }))
    .step("backup_probe", when(
      (row) => !row.primary_probe,
      (row, ctx) =>
        ctx.tool({
          id: 'rate_limit_probe_backup',
          tool: 'test_rate_limit',
          input: {
          key: 'redis-shared-scenario',
          lead_id: row.lead_id,
          row_number: row.row_number,
        },
          description: 'Exercise fallback rate-limit behavior.',
        }),
    ))
    .return((row) => row.primary_probe ?? row.backup_probe ?? null);

  // Fan out over leads — each gets a row-level fallback sequence
  const results = await ctx.map('leads', leads)
    .step("probe", probeFallback)
    .run();

  return results;
});
TS
bun sdk/bin/deepline-dev.ts play run --file rate-limit-waterfall.play.ts --watch

What to expect

  • test_rate_limit has a Redis-backed rate limit: 5 req/s, 3 max concurrency.
  • The SDK HTTP client auto-retries 429s with exponential backoff (1s → 2s → 4s, capped at 30s, up to 4 attempts).
  • The play rate limit scheduler (PlayRateLimitScheduler) paces tool calls using queue_hint metadata from rate limit definitions.
  • All 20/24 rows should complete — no terminal rate limit errors in the output.
  • The waterfall variant proves that short-circuiting (skipping later steps when earlier steps match) works correctly under rate pressure.

V1 equivalent

The v1 repro scripts use deepline enrich (Python CLI) with --with JSON specs. The v2 plays replace this with typed TypeScript — same backend rate limit infrastructure, better DX.

# V1 (still works)
scripts/repro-rate-limit-enrich.sh redis_429
scripts/repro-rate-limit-enrich.sh redis_429_waterfall

Transpiling native plays

Convert V1 native PlayDocument definitions to V2 definePlay() files:

bun scripts/transpile-native-plays.ts                     # all -> src/lib/plays/generated/v2/
bun scripts/transpile-native-plays.ts --play test_play     # single play
bun scripts/transpile-native-plays.ts --stdout             # print to stdout

File structure

sdk/
  bin/deepline-dev.ts           # Dev entry (bun, no build, points at localhost)
  src/
    index.ts                    # Public exports
    config.ts                   # Config + env resolution
    http.ts                     # Fetch client with retries
    client.ts                   # DeeplineClient (tools, plays API)
    play.ts                     # definePlay, Deepline.connect, job handles
    types.ts                    # TypeScript interfaces
    errors.ts                   # DeeplineError, AuthError, RateLimitError
    cli/
      index.ts                  # CLI router
      commands/
        auth.ts                 # auth register/status
        play.ts                 # play run/get/tail/runs/publish
        tools.ts                # tools list/get/call
        enrich.ts               # CSV enrichment (delegates to Python CLI)
    plays/
      bundle-play-file.ts       # esbuild bundler for .play.ts files
      local-file-discovery.ts   # ctx.csv() file resolution + packaging
  dist/                         # Build output (gitignored)