@elytracms/cli
v0.0.74
Published
The Elytra CLI — run a consuming repo's studio, frontend, and Convex backend under one command.
Readme
@elytracms/cli
The Elytra CLI — run an Elytra consuming repo's studio, delivery frontend, and Convex backend under one command.
Installed as a devDependency in projects scaffolded by
create-elytra; its dev/build
scripts call elytra.
Commands
elytra dev Start the studio + frontend (and Convex in connected mode)
together as one supervised process group. One Ctrl-C stops
everything; if any process exits, the rest are torn down.
elytra build Build the studio + frontend (and deploy Convex in connected
mode), in order — failing as a whole if any step fails.
Optional target (the per-app Vercel build commands):
elytra build studio Convex deploy coupled with the studio
build (functions + client in lockstep)
elytra build frontend frontend build only
elytra init First-run setup: provision Convex, generate the auth signing
keys, verify readiness. Idempotent — safe to re-run.
elytra doctor Diagnose the active deployment's readiness and print each fix.
Includes the expected deployment env vars, reported set/missing
by NAME only — values are never read into the report or printed.
When the publish revalidate pair is set, also sends a signed
no-op ping to the endpoint to prove the publish → live chain
end to end. Also reports the generated-previews artifacts
(manifest present, image files present, no strays — warnings
only; previews are optional). Read-only; exits non-zero if any
check fails.
elytra previews Render every canvas-eligible component's fixtures against your
theme and write committed insert-menu preview artifacts:
640×400 WebP images in studio/public/elytra-previews/ plus an
importable studio/elytra-previews.json manifest (per-entry
input hashes — re-running with unchanged inputs rewrites
nothing; removed components' images are pruned). Wire it with
`generatedPreviewsFromManifest` from
`@elytracms/studio/customization` (two lines — see the docs).
Rendering needs Playwright once (optional, on demand):
pnpm add -D playwright && pnpm exec playwright install chromium
Options:
--check verify the input hashes without rendering (no
Playwright needed); exits non-zero when previews
are missing/stale/orphaned. Wire into CI/builds.
--port <n> harness dev-server port (default: first free 5440+)
elytra help Show help
elytra version Show versionRun from anywhere inside an Elytra project (a directory with elytra.config.ts).
Mode-aware
The CLI reads the studio's .env (the same backend signal the studio itself
uses):
- Playground (
VITE_ELYTRA_MODE=playground) — runs the studio + frontend only; no Convex. - Connected (
VITE_CONVEX_URLset) — also runsconvex dev(or deploys on build) once a deployment is provisioned (CONVEX_DEPLOYMENTpresent).
It orchestrates and owns the process lifecycle; it does not reimplement Vite, Next, or Convex — each step shells out to the underlying tool and inherits its watch/HMR/build behaviour.
Deploying (Vercel)
Deploys are git-push-driven — there is no elytra deploy. A consuming repo
deploys as two Vercel projects on the same git repo, plus the Convex
deployment that the studio build pushes functions to:
| Vercel project | Root Directory | Build command |
| -------------- | -------------- | --------------------------------- |
| studio | studio | pnpm exec elytra build studio |
| frontend | frontend | pnpm exec elytra build frontend |
elytra build studio wraps Convex's recommended Vercel integration: it runs
convex deploy --cmd 'pnpm run build', which pushes your Convex functions and
then runs the studio build with the deployment URL injected into its env
(VITE_CONVEX_URL). The built studio always matches the functions it was
deployed with, and there is no hand-set URL to drift out of sync with the
deploy key's target. elytra build frontend runs the Next.js build only — the
frontend reads its Convex URL at runtime, not at build time.
Which env lives on which plane
Env lives on three planes. Keep them apart — most misconfiguration is a value set on the wrong plane:
- Local
.envfiles (studio/.env,frontend/.env) — local development only. Gitignored; they never reach Vercel or the Convex deployment. - Vercel project env — build- and runtime-env per Vercel project:
- Studio project:
CONVEX_DEPLOY_KEY(a production deploy key generated in the Convex dashboard — it selects and authorizes the deployment). Do not hand-setVITE_CONVEX_URL; the build injects it (above). Optional: the AI provider key (ANTHROPIC_API_KEY, or the Vercel AI Gateway) for the studio's AI route — a host/server env, neverVITE_-prefixed. - Frontend project:
ELYTRA_CONVEX_URL,ELYTRA_PROJECT_ID,REVALIDATE_SECRET,STUDIO_ORIGIN,DRAFT_PREVIEW_TOKEN— see the scaffold's CONNECT.md for what each pairs with.
- Studio project:
- Convex deployment env — set with
npx convex env set(fromstudio/); lives on the deployment itself, visible in the Convex dashboard but in no file: the auth signing keys (JWT_PRIVATE_KEY,JWKS— set vianpx @convex-dev/auth),SITE_URL, the instant-publish trioCANVAS_PROJECT_SCOPE/CANVAS_REVALIDATE_ENDPOINT/CANVAS_REVALIDATE_SECRET, andDRAFT_PREVIEW_TOKEN(each of the latter four must match its frontend-plane counterpart).elytra doctordiagnoses this plane's readiness.
Because plane 3 lives in no file, it is the easiest to overlook — elytra
doctor therefore enumerates the expected vars there (auth keys, SITE_URL,
the instant-publish trio, the draft-preview token, plus the AI provider key
on the studio-host plane when the AI route is present) and reports each one
set or missing, by name only: the doctor parses names out of npx convex
env list and discards every value on the spot — values are never stored,
printed, or logged. A missing required var (the auth keys) fails the run
with its exact fix line; a missing recommended var (SITE_URL, the AI key,
the draft-preview token) is a named warning that doesn't change the exit
code. The studio's boot-time "Deployment setup" screen lists the same
expected vars, so the CLI and the studio tell one story.
The instant-publish trio — CANVAS_PROJECT_SCOPE,
CANVAS_REVALIDATE_ENDPOINT, and CANVAS_REVALIDATE_SECRET — gets special
treatment, because the publish path silently no-ops while any of the
three is unset: CANVAS_PROJECT_SCOPE gates the write-chokepoint trigger
that schedules the revalidate webhook (checked FIRST, before the pair is
even consulted), and the pair then gates whether the scheduled webhook
actually delivers. Either way, publishes never update the live site until
a redeploy, with no error anywhere. All three missing is a warning carrying
exactly that consequence (a project without a delivery frontend may
legitimately leave them unset); any partial subset — one or two of the
three set — fails the run, because a half-configured trio has no
legitimate reading (documented together in the scaffold's CONNECT.md, set in
one shell block, so a partial state is far more likely an interrupted setup
than a deliberate choice). When the pair (CANVAS_REVALIDATE_ENDPOINT +
CANVAS_REVALIDATE_SECRET) IS set, the doctor goes one step further and
proves the chain end to end: it POSTs a signed no-op ping (an empty tag
list — the route revalidates nothing) to the endpoint. A 200
{"revalidated":[]} passes (endpoint reachable, and the frontend's
REVALIDATE_SECRET matches); a 401 fails as a secret mismatch between
Convex and the frontend host; a network error only warns (unreachable from
your machine is not the same as broken — Convex may still reach it). The
pair's values are read in-process for the ping only and never printed. The
ping proves only the pair's reachability/secret-match, not scheduling — a
missing CANVAS_PROJECT_SCOPE still fails its own row (and the run)
independently, even when the ping passes.
The draft-preview token — DRAFT_PREVIEW_TOKEN — gates the deployment's
draftSnapshot query and must equal the frontend host's own
DRAFT_PREVIEW_TOKEN (the same secret that gates the frontend's
preview-enable route); the doctor can only see the Convex-deployment side; it
has no pairWith escalation because its real partner lives on a plane this
doctor cannot read. Missing it is a warning: draft preview is closed
everywhere (401 on both sides) until it's set — a project that never uses
draft preview may legitimately leave it unset.
