@vizor-vr/cli
v0.1.0
Published
Command-line interface for Vizor VR — upload source video, list content, and check transcode status.
Readme
@vizor-vr/cli
Command-line interface for Vizor VR — upload source video, list your content, and check transcode status from a terminal or a CI job.
Zero runtime dependencies. Node.js 20+.
npm install -g @vizor-vr/cli
vizor --helpOr without installing:
npx @vizor-vr/cli listCommands
| Command | What it does |
| ------------------- | ---------------------------------------------------------------- |
| vizor login | Read a session token from stdin, verify it, and store it |
| vizor logout | Delete the stored credential |
| vizor list | List the organization's content items |
| vizor status <id> | Show a media asset's transcode status |
| vizor upload <f> | Upload a source video (multipart above 256 MiB, with part retry) |
Global options
| Option | Meaning |
| ----------------- | ---------------------------------------------------------- |
| --api-url <url> | API origin. Default https://api.vizor-vr.com |
| --json | Machine-readable JSON on stdout instead of a table |
| -h, --help | Usage |
| -v, --version | CLI version |
--api-url also accepts a self-hosted or local origin, so the same binary drives
a development stack and production.
Environment
| Variable | Meaning |
| ---------------- | ------------------------------------------------------------------ |
| VIZOR_TOKEN | Session token. Takes precedence over the stored credential |
| VIZOR_API_URL | API origin. Overridden by --api-url |
Exit codes
| Code | Meaning |
| ---- | ---------------------------------------------------- |
| 0 | Success |
| 1 | Runtime error (API error, unreadable file, transcode failed) |
| 2 | Usage error (unknown command, missing argument) |
| 3 | Not authenticated |
vizor status exits 1 when the asset's status is failed, so
vizor status "$id" || alert works in a pipeline. A still-transcoding asset is a
normal, successful read.
Examples
# Log in. The token is read from stdin so it never lands in shell history.
printf '%s' "$VIZOR_TOKEN" | vizor login
# Human-readable listing.
vizor list --limit 5 --published published
# Scriptable listing.
vizor list --json | jq -r '.data[].id'
# Upload and poll.
vizor upload ./dive.mp4
vizor status <media-id> --json | jq -r .status
# Point at a self-hosted stack.
vizor list --api-url https://api.vizor.internalAuthentication — read this first
Vizor's API has no long-lived, non-browser credential today. This is a real gap, not a CLI limitation, and it shapes everything below.
- API keys (
x-api-key) exist, but their scopes aredelivery:readandanalytics:ingestonly. There is no key-authenticated content or media surface — nocontent:read, nocontent:write. An API key therefore cannot drivelist,status, orupload. - Every route this CLI uses (
/api/v1/content,/api/v1/media/*) is session-authenticated:Authorization: Bearer <session token>, with the write routes additionally requiring theeditorrole. - Session tokens are issued to the browser and are short-lived (on the order
of a minute in production).
vizor loginstores whatever token you give it, but a stored production token goes stale quickly and the next command returns401.
What that means in practice:
- Against a self-hosted or development stack the CLI is fully usable end to end — including large multipart uploads.
- Against production it works for the lifetime of the token you paste. Short
commands (
list,status, a smallupload) succeed; a long multipart upload can fail partway through when the token expires between signing batches. The error says so and tells you to log in again. Parts are idempotent, so re-running the upload is safe.
The CLI does not paper over this with a fake credential flow. Closing the gap
needs an API change — content:read / content:write scopes plus
key-authenticated content and media routes — which is tracked separately; the
scope constants in apps/api/src/lib/api-key-scopes.ts already reserve those two
names. When those land, vizor login gains an API-key mode and nothing else in
this package has to change.
Getting a token
Sign in to the Vizor dashboard, then copy the session token your browser sends in
the Authorization header on any dashboard API call (DevTools → Network). Pipe it
in:
pbpaste | vizor login # macOS
vizor login < token.txt # anywherevizor login verifies the token against the API before storing it, so a bad or
expired token fails immediately instead of leaving a dud credential on disk.
Where the credential is stored
| Platform | Path |
| ------------- | ------------------------------------------ |
| Windows | %APPDATA%\vizor\config.json |
| macOS / Linux | $XDG_CONFIG_HOME/vizor/config.json, else ~/.config/vizor/config.json |
The file is written with owner-only permissions (0600) on platforms that honour
POSIX modes; on Windows it inherits the per-user %APPDATA% ACL. The token is
never printed, never logged, and never included in an error message. Remove it
with vizor logout.
If you would rather not persist anything — CI, for example — skip login entirely
and pass VIZOR_TOKEN in the environment.
Uploads
vizor upload drives the same three-step flow the dashboard uses:
POST /api/v1/media/upload-urlreserves the asset row and picks the strategy.- Sources above 256 MiB go multipart: part URLs are signed 25 at a time and
each 128 MiB part is
PUTdirectly to object storage with up to 3 attempts. Smaller sources take a single presignedPUT. POST /api/v1/media/:id/completeassembles the parts, re-verifies the real byte size server-side, and queues transcoding.
Progress goes to stderr, so --json keeps stdout parseable.
--content-id <id> attaches the upload to an existing content item. The CLI does
not create content items; use the dashboard or the REST API for that.
Security
See the monorepo security policy for how to report vulnerabilities ([email protected]).
Reporting a bug in this package? Please make sure any pasted output has no credential in it — the CLI does not print tokens, but shell transcripts sometimes do.
License
MIT — see LICENSE.
