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

@elah/cli

v0.1.1

Published

Headless command-line runtime for the Elah video engine — split, trim, and export projects using @elah/core

Readme

@elah/cli

Headless command-line runtime for the Elah video engine. A thin consumer of @elah/core's public APIs — no rendering or timeline logic lives here.

npm node license


Install

npm install -g @elah/cli

Or without installing, via npx @elah/cli <command>. Requires Node >= 18.17. export/build --export/serve additionally require a system Google Chrome (or --browser <path> / ELAH_BROWSER) — see Serve mode and Docker for headless environments.


Commands

elah split  --project <in.json> --clip <clipId> --at <frame|timecode> [--out <out.json>]
elah trim   --project <in.json> --clip <clipId> [--start <frame|timecode>] [--duration <frames|timecode>] [--out <out.json>]
elah export --project <in.json> --out <file.mp4> [--codec avc|vp9|vp8] [--height <N>]
elah build  --spec <spec.json> [--out <project.json>] [--export <file.mp4>]
elah serve  [--port <n>] [--host <addr>] [--concurrency <n>] [--media-root <dir>]

split, trim and build run in plain Node against the engine. export launches the system Google Chrome headlessly (or --browser <path> / ELAH_BROWSER) and runs core's real exportVideo pipeline in it, so CLI output is identical to Editor output by construction. Exit codes: 0 success, 1 validation/runtime failure, 2 usage error. Diagnostics go to stderr; split/trim print the resulting project JSON to stdout unless --out is given.


The build spec — the AI-generation contract

elah build consumes a seconds-based spec, probes each media asset's real duration, and constructs the project through TimelineEngine, so overlaps, track caps and source bounds are all validated with precise, path-addressed errors (clips[2].duration must be …) that a generating model can self-correct from.

{
  "fps": 30,
  "stage": { "width": 1920, "height": 1080 },
  "assets": {
    "footage": "./media/clip.mp4",
    "music": "./media/song.mp3",
    "logo": "./media/logo.png"
  },
  "clips": [
    { "track": "video", "asset": "footage", "start": 0, "duration": 8, "sourceStart": 2, "volume": 0.8 },
    { "track": "text",  "text": "Hello", "start": 0.5, "duration": 4,
      "fontSize": 96, "color": "#ffffff", "fontFamily": "Arial", "fontWeight": "bold",
      "align": "center", "x": 0.5, "y": 0.15 },
    { "track": "image", "asset": "logo", "start": 1, "duration": 6, "x": 0.9, "y": 0.1, "scale": 0.3, "opacity": 0.8 },
    { "track": "audio", "asset": "music", "start": 0, "duration": 8, "volume": 0.5 }
  ]
}

Rules:

  • Times are seconds (floats fine), converted to integer frames at fps (default 30; rounding to nearest frame). stage defaults to 1920×1080.
  • assets maps names to paths relative to the spec file, or http(s) URLs. Duplicate keys follow JSON semantics (last one wins) — avoid them.
  • video/audio clips: duration defaults to the media's remainder after sourceStart; exceeding it is an error (with a one-frame rounding tolerance, which clamps). The real media length is recorded as the clip's source bounds so later trim/split behave correctly.
  • text clips need text + duration; image clips need duration.
  • x/y are the normalized (0..1) stage position of the clip's center; scale is relative to native size.
  • Overlaps: video clips must not overlap (single video track, engine-enforced); overlapping text/image/audio clips are automatically placed on additional tracks. Note: a clip bumped to a later track renders beneath the one it overlaps — list the clip you want on top first.
  • Unknown or misspelled fields are rejected by name.

Then: elah build --spec spec.json --export final.mp4 (add --out project.json to keep the editable project; export options like --height pass through).


Library API

@elah/cli is also importable — no shelling out, no stderr parsing:

import { build, exportProject } from '@elah/cli'

const { project } = await build({ spec: mySpecObject, baseDir: '/path/to/assets' })

const { bytes } = await exportProject(
  { project, outPath: 'out.mp4' },
  { onProgress: ({ frame, totalFrames }) => console.log(`${frame}/${totalFrames}`) }
)

For repeated renders, createRenderSession() keeps a browser warm across calls instead of launching Chrome per export — this is what elah serve uses internally:

import { createRenderSession } from '@elah/cli'

const session = createRenderSession()
await session.warmup()
const mp4 = await session.render(project, '/path/to/assets')
// ... more session.render() calls reuse the same browser ...
await session.close()

Errors throw CliError (aliased ElahError) with a .message that is already the human-readable, path-addressed text (clips[2].duration must be …).


Serve mode

elah serve runs a long-lived HTTP render server with a warm browser, so each request only pays for a new browser tab instead of a fresh Chrome process:

elah serve --port 8080 --concurrency 2 --media-root ./assets

| Route | Behavior | |---|---| | GET /healthz | 200 { status, browser: "connected" \| "disconnected" } | | POST /render | body = a build spec JSON document; blocks until the render finishes and returns the MP4 bytes. 422 on spec/asset validation errors, 503 + Retry-After when at capacity (--concurrency), 500 on render failure. |

curl -X POST --data-binary @spec.json http://127.0.0.1:8080/render -o out.mp4

There is no job queue — this is a synchronous, retry-on-503 contract. See docs/deploy-render-server.md for the full contract, security notes, and deployment options.


Docker

A Dockerfile that installs branded Chrome + fonts and runs elah serve is at Dockerfile (build from the repo root — see docs/deploy-render-server.md).


Validation tooling

  • ELAH_E2E=1 npm test --workspace=packages/cli additionally runs a browser end-to-end test (exports the no-media fixture through headless Chrome and probes the MP4).
  • node packages/cli/scripts/parity-compare.mjs <editor.mp4> <cli.mp4> compares an editor UI export against a CLI export of the same project: stream params, decoded-video hashes (expected bit-identical), and the audio null-test residual (−91 dB = digital silence; a small residual is a systematic editor-page vs harness-page environment delta in the AAC encode stage — both pipelines are individually deterministic).

Full documentation lands with the release phase; see elah --help.


Package layers

@elah/core      — engine, playback, resolver, stores, media, export (framework-agnostic)
@elah/timeline  — React timeline UI components and hooks
@elah/editor    — EditorProvider, Preview, AssetPanel + re-exports everything above
@elah/cli       — headless split/trim/build/export/serve on top of @elah/core (this package)

Use @elah/cli for automation, AI-generation pipelines, and server-side rendering. Use @elah/editor when you need an interactive, in-browser editing UI.


Links