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

@labelbox/horizon-cli

v0.0.1

Published

Command-line client for Horizon

Readme

@labelbox/horizon-cli

horizon — the Horizon CLI. It mirrors the TypeScript SDK (@labelbox/horizon-sdk) exactly: where the SDK is horizon.synthesizers.create(...), the CLI is horizon synthesizers create …. Dots become spaces; you get --help at every level. horizon is the only executable name.

horizon --help                     # list nouns (synthesizers, synthesizer-runs)
horizon synthesizers --help        # list verbs (create, get, list, …)
horizon synthesizers create --help # list flags

How it works (fully live, zero per-operation code)

The CLI ships no baked API reference and has no @labelbox/horizon-sdk dependency. On each run it revalidates the manifest from GET /v1/cli/manifest when the server selected by --base-url is reachable (production by default, or staging, or localhost). If the server is unavailable, it falls back only to a locally cached manifest that was previously validated. From that manifest it builds its entire command tree, --help, request/response shapes, and docs browse surfaces (src/manifest.ts). Dispatch is generic (src/dispatch.ts): each request is built straight from the manifest operation's HTTP method + path template + params + body — there is no hand-written command per operation and no baked client.

The result: adding or changing a backend endpoint needs zero CLI release — the live CLI reflects it as soon as the backend deploys. The CLI is re-released only when its own engine code changes. When the server is reachable, the manifest is revalidated on every run via a conditional fetch (ETag / If-None-Match) and cached per base-url under ~/.cache/horizon/. When the server is unreachable, the CLI may use the last locally validated copy so commands remain available offline. Validated fresh HTTP 200 responses use atomic same-directory rename and therefore retain last-network-writer behavior. Replacement preserves an existing file's POSIX mode bits, but deliberately publishes a new inode and does not preserve its ACLs or extended attributes. Publication requires parent-directory write/search permission; if unavailable, the CLI keeps using the validated in-memory result and never falls back to a partial direct write.

Docs browse surfaces

Beyond the executable horizon <noun> <verb> operations, the manifest carries the docs, exposed as one consistent positional shape — horizon <group> [<id>] (bare lists, an id shows that one):

horizon resources [<id>]     # Reference — resource hubs: object shape, operations, recipes
horizon recipes [<id>]       # How-to — multi-step user-goal guides (--format cli|ts|curl)
horizon explain [<concept>]  # Explanation — concept pages
horizon tutorials [<id>]     # Tutorials — getting-started docs

Install / run

The package is not published yet, so no npm install command is valid today. The manual Release / CLI workflow builds and externally consumes an immutable 0.0.1 bootstrap archive, but executable release authority refuses npm and GitHub mutation until a Labelbox npm owner reserves the package and registers its Trusted Publisher.

When activated, the CLI remains standalone and pulls in no @labelbox/horizon-sdk. It is versioned independently of the SDK, remains below 1.0.0, and may include breaking changes.

From a monorepo checkout, run the bin directly without installing:

yarn workspace @labelbox/horizon-cli build
node ./packages/horizon-cli/dist/bin.js --help

Auth

Set LABELBOX_API_KEY in the environment without printing it or putting it on a command line. Override the host with --base-url <url> (defaults to the public Horizon API gateway, see DEFAULT_BASE_URL in src/manifest.ts).

Flags

  • Path / query params are individual flags: --environment-id, --limit, … Required path and query params must be passed as flags. A query param whose type is an array or object (e.g. --models, --enrichment-filters) takes a JSON value (--models '["a","b"]'); its help text is tagged [pass as JSON].
  • Operation headers declared by the API are individual flags too. Required concurrency and replay controls therefore appear as --if-match and --idempotency-key and are forwarded under their declared HTTP names.
  • Request body: scalar top-level fields are individual flags (--name, --system-prompt, …); pass the full body — including complex fields like contextInputs / targetFields — with --from-json <file> or --data <json> (mutually exclusive — pass only one). Scalar flags override values from --from-json/--data. A required scalar body field can be supplied by either its flag or the JSON body.
  • Multipart request body: binary fields are local-path flags such as --file ./skill.zip; scalar form fields remain ordinary flags. The CLI reads the file only in its terminal process and lets the HTTP runtime set the multipart boundary. Filesystem-less embedded callers expose JSON operations only.
  • Output: prints every non-binary result as the JSON HTTP envelope { data, status, headers } by default. A bodyless 204 or 304 is encoded as data: null because JSON has no undefined. --quiet deliberately discards that HTTP metadata and prints only the resulting resource's id; an operation may instead declare one scalar response field for quiet output (for example, the handoff-access command's ephemeral URL). Results without either value print a blank line in quiet mode. Binary downloads are written to stdout byte-for-byte with no encoding or trailing newline, so redirect them to a file.

The embedded dispatcher exposes JSON/text operations and returns each success as { data, status, headers }. headers contains the response headers declared for that status in OpenAPI (including ETag, Location, and Idempotency-Replayed). Bodyless data is undefined. Operations with a binary success representation are excluded from embedded and MCP callers; use the terminal CLI, which streams their bytes directly to stdout.

horizon environments get --environment-id 784e2386-e297-4f9d-a886-838422383b65
horizon synthesizers get --synthesizer-job-id b45c081d-3069-4e32-98d4-aa5ec3d442c6

The command surface is the live server

There is nothing to regenerate or commit for the CLI — the command surface is revalidated against the target server's GET /v1/cli/manifest on every run when the server is reachable, with a validated local-cache fallback when it is offline. A backend change therefore flows through automatically after deploy with no CLI step. The manifest itself is assembled by yarn generate cli:manifest (included in yarn generate prerequisites) from the spec-derived reference files and embedded into the backend; to add or change a command, change the backend @SdkRoute — not this package.

The engine (manifest fetch + cache + validation, generic dispatch, flag mapping, help formatting, the request-body / returns shape trees, and the docs browse renderers) is verified by src/*.test.ts, which build the program from a fixture manifest in-memory and assert the realized flags + --help + browse output — no committed golden snapshot to maintain.