@sleeperhit/cli
Command-line client for the Sleeper Hit Studio B2B Story API (/api/v1). It maps 1:1 onto the documented endpoints — create projects, attach sources, generate and approve a StoryPlan, run a job, watch it to completion, and open the live table-read theater.
The endpoints, auth, error envelope, and flow mirror docs/agents/ (README, quickstart, concepts, api-reference). Nothing here contradicts those docs — the CLI is a thin, scriptable wrapper around the same HTTP surface.
Requirements
- Node.js >= 20 (uses the built-in global
fetchandnode:crypto.randomUUID). - A Sleeper Hit Studio customer API key. Create one at https://sleeperhit.studio/dashboard/api.
- Recommended scopes for the full flow:
story:read,story:write,source:write,credits:read,artifact:read.
Install
From the monorepo root (the package lives at packages/cli and is picked up by the existing packages/* workspace glob):
pnpm install pnpm --filter @sleeperhit/cli build # compiles src -> dist
Global link (run sleeperhit anywhere)
cd packages/cli pnpm link --global # now `sleeperhit` is on your PATH sleeperhit --help
Run without building (dev)
# from packages/cli pnpm dev -- projects list # or directly with a TS-aware runner: pnpm exec tsx src/index.ts projects list
Configuration
| Env var | Required | Default | Purpose |
|---|---|---|---|
SLEEPERHIT_API_KEY | yes | — | Bearer key, sh_<prefix>_<secret>. Sent as Authorization: Bearer <key>. |
SLEEPERHIT_BASE_URL | no | https://sleeperhit.studio | Origin to target. /api/v1 is appended automatically. Point at https://stage.sleeperhit.studio or a local dev origin when testing. |
export SLEEPERHIT_API_KEY="sh_xxxxxxxxxxxxxxxxxx_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" # optional export SLEEPERHIT_BASE_URL="https://sleeperhit.studio"
The CLI auto-generates an Idempotency-Key (UUID) for every POST. Override it with --idempotency-key <key> when you want a stable key tied to your own job record for safe retries.
Errors are printed in the standard { error: { code, message, requestId } } form. On rate_limited the exact Retry-After is surfaced; jobs watch honors it automatically. The CLI exits non-zero on any error (1 API/runtime, 64 usage, 2 for a planned endpoint that is not deployed yet).
Commands
capabilities Live capability manifest
credits Studio Credit balance
projects create --name <name> [--description <text>] [--metadata <json>]
projects list [--limit N] [--cursor C]
projects get <projectId>
sources add <projectId> --type text|markdown|url|pdf
[--content <text> | --file <path>] (text / markdown)
[--uri <http(s) url>] (url / pdf)
[--label <text>] [--retention EXTRACTED_ONLY|STORE_ORIGINAL|METADATA_ONLY]
sources list <projectId> [--limit N] [--cursor C]
sources get <projectId> <sourceId>
plans create <projectId>
--target audience=...,objective=...,outcome=... (repeatable)
--artifact type=table_read,mode=podcast|documentary|drama[,narrationPolicy=auto|include|suppress][,durationSeconds=N] (repeatable)
[--source <sourceId> ...] [--title <text>] [--auto-approve]
[--json '<full payload>'] power-user escape hatch
plans get <planId>
plans approve <planId>
plans reject <planId> [--reason <text>]
jobs create <planId> [--project <projectId>] [--artifact type=...,mode=...]
jobs list [--project <id>] [--plan <id>] [--limit N] [--cursor C]
jobs get <jobId>
jobs watch <jobId> [--interval <ms>] Poll (~5s) until terminal; print theater/audio/video URLs on READY
jobs cancel <jobId>
artifacts get <artifactId>
artifacts render-video <artifactId> Opt-in MP4 render (separate Studio Credit charge)
music generate <artifactId> [--coverage <0..1|0..100>] Generate adaptive music in place
music update <artifactId> --scene <n> [--prompt <text>] [--summary <text>]
[--weight <0..2>] [--mute | --enable] Tune or mute one scene
music get <artifactId>
music watch <artifactId> [--interval <ms>]
refine <artifactId> <instruction...> Planned endpoint — wired forward-compatibly
finalize <artifactId> Planned endpoint — wired forward-compatibly
Run sleeperhit --help for the same reference at the terminal.
Planned (forward-compatible) commands
refine and finalize are documented in docs/agents/concepts.md as coming soon. They are wired against POST /api/v1/artifacts/{artifactId}/refine and POST /api/v1/artifacts/{artifactId}/finalize. Until those ship, the commands detect a 404/405/501 and print a clear "coming soon" note describing the current workaround (refine → create a new plan; finalize → approve + job + render-video) and exit with code 2.
End-to-end example
Copy-pasteable. Replace the captured IDs as you go (or pipe through jq).
export SLEEPERHIT_API_KEY="sh_..." # 1. Confirm the table read is available and you have credits sleeperhit capabilities | jq '.artifacts.table_read.availability' sleeperhit credits | jq '.credits.balance' # 2. Create a project PROJECT_ID=$(sleeperhit projects create --name "Q3 Launch Story" \ --description "Product launch table read" | jq -r '.project.id') # 3. Attach an inline text source (instant READY) SOURCE_ID=$(sleeperhit sources add "$PROJECT_ID" \ --type text --label press-release \ --content "Acme Corp launches its next-gen platform in Q3 2026, cutting onboarding time 60%." \ | jq -r '.source.id') # 4. Generate the plan and auto-approve it (skips the human hold) PLAN_ID=$(sleeperhit plans create "$PROJECT_ID" \ --target audience="enterprise buyers" \ --target objective="introduce the Q3 platform launch" \ --target outcome="book a demo within 7 days" \ --target tone="confident, specific" \ --artifact type=table_read,mode=documentary \ --source "$SOURCE_ID" \ --auto-approve | jq -r '.plan.id') # 5. Poll the plan until APPROVED (auto-approve usually lands here directly) sleeperhit plans get "$PLAN_ID" | jq '{status:.plan.status, total:.plan.quote.total}' # 6. Create the job (reserves credits) and watch it to completion JOB_ID=$(sleeperhit jobs create "$PLAN_ID" | jq -r '.job.id') sleeperhit jobs watch "$JOB_ID" # On READY, watch prints: # Theater: https://sleeperhit.studio/share/table-read/<token> # Theater (full): https://sleeperhit.studio/share/table-read/<token>?view=fullscreen # Audio (live): https://sleeperhit.studio/share/table-read-audio/<token> # Video render URL: /api/v1/artifacts/<artifactId>/render-video # 7. (Optional) opt into a durable MP4 ARTIFACT_ID=$(sleeperhit jobs get "$JOB_ID" \ | jq -r '.job.artifacts[] | select(.type=="table_read") | .id') sleeperhit artifacts render-video "$ARTIFACT_ID"
The shareToken baked into the theater/audio URLs authorizes them — not your API key — so you can hand those URLs directly to end users.
Use it in your own agent workflow
The CLI emits JSON on stdout and human progress on stderr, so it composes cleanly inside scripts or an agent's tool layer:
#!/usr/bin/env bash set -euo pipefail export SLEEPERHIT_API_KEY="${SLEEPERHIT_API_KEY:?set your key}" ARTICLE_URL="$1" PROJECT_ID=$(sleeperhit projects create --name "Agent run $(date +%s)" | jq -r '.project.id') # Async URL source — poll to READY before planning SOURCE_ID=$(sleeperhit sources add "$PROJECT_ID" --type url --uri "$ARTICLE_URL" | jq -r '.source.id') until [ "$(sleeperhit sources get "$PROJECT_ID" "$SOURCE_ID" | jq -r '.source.status')" = "READY" ]; do sleep 3 done PLAN_ID=$(sleeperhit plans create "$PROJECT_ID" \ --target audience="general" --target objective="summarize the article" --target outcome="share a 90s table read" \ --artifact type=table_read,mode=podcast,narrationPolicy=include,durationSeconds=90 \ --source "$SOURCE_ID" --auto-approve | jq -r '.plan.id') # wait for the async planner until [ "$(sleeperhit plans get "$PLAN_ID" | jq -r '.plan.status')" = "APPROVED" ]; do sleep 4; done JOB_ID=$(sleeperhit jobs create "$PLAN_ID" | jq -r '.job.id') sleeperhit jobs watch "$JOB_ID" # blocks, streams progress, prints share URLs on READY
For a power-user payload that exactly matches the API schema, bypass the flag helpers with --json:
sleeperhit plans create "$PROJECT_ID" --auto-approve --json '{ "title": "Launch narrative", "target": { "audience": "enterprise buyers", "objective": "introduce the launch", "outcome": "book a demo" }, "artifactRequests": [{ "type": "table_read", "mode": "drama", "narrationPolicy": "suppress", "durationSeconds": 120 }], "styleConstraints": { "voicePreference": "authoritative, not salesy" }, "sourceIds": ["'"$SOURCE_ID"'"] }'
See also
docs/agents/quickstart.md— the same flow as raw HTTP / Node.docs/agents/api-reference.md— every endpoint, field, and error code.docs/agents/concepts.md— the plan/job/artifact model and the planned refine/finalize operations.GET /api/v1/openapi.json— the live OpenAPI 3.1 spec.