@lunora/cli
Version:
The Lunora CLI: init, dev, deploy, codegen, run, reset, and migrate commands
4 lines (3 loc) • 36.9 kB
JavaScript
import{mkdirSync as f,readFileSync as u,lstatSync as v,writeFileSync as b}from"node:fs";import{resolveHint as k,isLunoraError as m,flattenHint as S,findSolutionByMessage as w}from"@lunora/errors";import{createCerebro as x}from"@visulima/cerebro";import O from"@visulima/cerebro/command/completion";import N from"@visulima/cerebro/command/version";import{p as l}from"./api-spec-BENwiyUa.mjs";import{createLogger as g}from"./createLogger--C3x1FNE.mjs";import{VisulimaError as R,renderError as A}from"@visulima/error";import{homedir as B}from"node:os";import{join as d}from"node:path";const D={argument:{description:"Feature or registry item: ai | auth | email | storage | crons | presence | queue | workflow | flags | backup | …",name:"feature",type:String},description:"Add a feature or registry item (ai, auth, email, storage, crons, …) to the current Lunora project",examples:[["lunora add auth","Add authentication (asks which provider)"],["lunora add auth --provider clerk","Add Clerk auth without prompting"],["lunora add email","Add transactional email (Cloudflare Email Workers + dev mail catcher)"],["lunora add storage","Add the R2 storage registry item (asks for the bucket name)"],["lunora add storage --bucket my-app-uploads","Add storage with a bucket name, no prompt"],["lunora add crons","Add the scheduled-jobs registry item"],["lunora add storage --ref alpha","Add an item from the alpha branch's registry"]],group:"Project",loader:()=>import("../packem_chunks/handler.mjs").then(e=>({default:e.execute})),name:"add",options:[{description:"auth: provider to use without prompting (auth | clerk | auth0)",name:"provider",type:String},{description:"auth: D1 database name to use without prompting (lowercase alphanumeric + hyphens)",name:"db",type:String},{description:"storage: R2 bucket name to use without prompting (lowercase alphanumeric + hyphens)",name:"bucket",type:String},{description:"email: verified destination address to use without prompting",name:"mail-to",type:String},{description:"Skip prompts (auth provider, DB name, bucket name, mail destination) and use the defaults",name:"yes",type:Boolean},{description:"Local registry root (offline; expects <name>/ subdirs)",name:"from",type:String},{description:"Override the remote registry source base (e.g. gh:owner/repo/registry)",name:"source",type:String},{description:"Fetch items from a git ref (branch, tag, or commit), e.g. --ref alpha. Overrides the version-derived default",name:"ref",type:String},{description:"Permit --source values outside gh:/github:/https://",name:"allow-unsafe-source",type:Boolean},{description:"Output format: pretty (default) or json",name:"format",type:String}]},L={description:"Run wrangler dry-run and report bundle size, top modules, and _generated files",examples:[["lunora analyze","Report the worker bundle size + heaviest modules"]],group:"Deploy",loader:()=>import("../packem_chunks/handler2.mjs").then(e=>({default:e.execute})),name:"analyze",options:[{description:"Emit a JSON report instead of human text",name:"json",type:Boolean}]},E={argument:{description:"create | list | restore <id|file> | pitr",name:"subcommand",type:String},description:"Managed snapshot backups (create | list | restore) plus native point-in-time recovery (pitr)",examples:[["lunora backup create","Snapshot every table to a backup file"],["lunora backup list","List recorded snapshots"],["lunora backup restore <id>","Restore a snapshot by id"],["lunora backup pitr --at 2026-06-01T00:00:00Z","Point-in-time recovery (≤30 days)"]],group:"Data",loader:()=>import("../packem_chunks/handler3.mjs").then(e=>({default:e.execute})),name:"backup",options:[{description:"Backup directory (default .lunora-backups)",name:"dir",type:String},{description:"Comma-separated table allowlist (create)",name:"tables",type:String},{description:"pitr: time to read/restore to (ISO or epoch-ms, ≤30 days)",name:"at",type:String},{description:"pitr --restore: explicit bookmark to restore to (wins over --at)",name:"bookmark",type:String},{description:"pitr: perform a restore instead of just reading the bookmark",name:"restore",type:Boolean},{description:"pitr --restore: restart the shard now so recovery applies immediately",name:"restart",type:Boolean},{description:"pitr: target shard key (default: root shard)",name:"shard",type:String},{description:"Target production — requires an explicit --url",name:"prod",type:Boolean},{description:"Confirm a production pitr --restore (required with --prod)",name:"yes",type:Boolean},{description:"Worker URL (default http://localhost:8787)",name:"url",type:String},{description:"Admin bearer token (prefer LUNORA_ADMIN_TOKEN; --token is visible to other local processes via the process table)",name:"token",type:String}]},C={description:"Codegen + validate + bundle the Worker to disk without deploying",examples:[["lunora build","Bundle to .lunora/build without deploying"],["lunora build --out-dir dist-worker","Bundle to a custom directory"]],group:"Deploy",loader:()=>import("../packem_chunks/handler4.mjs").then(e=>({default:e.execute})),name:"build",options:[{description:`Which API spec(s) to emit: ${l} (default openapi)`,name:"api-spec",type:String},{description:"Output format: pretty (default) or json",name:"format",type:String},{description:"Directory to write the bundled Worker to (default .lunora/build)",name:"out-dir",type:String}]},j={description:"Run codegen for lunora/ functions and schema",examples:[["lunora codegen","Generate lunora/_generated/ from your schema + functions"]],group:"Develop",loader:()=>import("../packem_chunks/runCodegenCommand.mjs").then(e=>({default:e.execute})),name:"codegen",options:[{description:`Which API spec(s) to emit: ${l} (default openapi)`,name:"api-spec",type:String},{description:"Output format: pretty (default) or json",name:"format",type:String}]},T={argument:{description:"<build|push|images|list|info|delete> [args…]",name:"args",type:String},description:"Build/push container images and manage container instances (wraps wrangler containers)",examples:[["lunora containers build ./containers/transcoder --tag transcoder:v1","Build a container image with the local Docker engine"],["lunora containers build ./containers/transcoder --tag transcoder:v1 --push","Build and push to the Cloudflare Registry in one step"],["lunora containers push transcoder:v1","Push a locally-tagged image to the Cloudflare Registry"],["lunora containers images list","List images in your Cloudflare Registry"],["lunora containers images delete transcoder:v1","Delete an image to free registry storage"]],group:"Deploy",loader:()=>import("../packem_chunks/handler5.mjs").then(e=>({default:e.execute})),name:"containers",options:[{description:"build: push the image to the Cloudflare Registry after building",name:"push",type:Boolean},{description:"build: name:tag for the image (forwarded to wrangler --tag)",name:"tag",type:String},{description:"Cloudflare environment name",name:"env",type:String}]},_={description:"Codegen, validate wrangler, then wrangler deploy",examples:[["lunora deploy","Deploy to Cloudflare"],["lunora deploy --env production","Deploy to a named environment"],["lunora deploy --dry-run","Validate + bundle without publishing"],["lunora deploy --migrate","Deploy, then run pending data migrations"]],group:"Deploy",loader:()=>import("../packem_chunks/runDeployCommand.mjs").then(e=>({default:e.execute})),name:"deploy",options:[{description:"Override the schema-drift gate (deploy even with breaking schema drift and no migration)",name:"allow-schema-drift",type:Boolean},{description:`Which API spec(s) to emit: ${l} (default openapi)`,name:"api-spec",type:String},{description:"Validate, bundle, and run pre-deploy gates without publishing (wrangler deploy --dry-run)",name:"dry-run",type:Boolean},{description:"Cloudflare environment name",name:"env",type:String},{description:"Output format: pretty (default) or json",name:"format",type:String},{description:"After a successful deploy, run pending data migrations against the live worker",name:"migrate",type:Boolean},{description:"Skip codegen + the schema-drift gate (assumes `lunora build`/`prepare` already ran in this CI run). Wrangler still bundles the worker.",name:"prebuilt",type:Boolean},{description:"Admin bearer token for --migrate (falls back to LUNORA_ADMIN_TOKEN)",name:"migrate-token",type:String},{description:"Worker URL for --migrate (REQUIRED with --migrate; the deploy target URL is not captured automatically)",name:"migrate-url",type:String},{description:"Confirm running the production data migration triggered by --migrate (required with --migrate)",name:"migrate-yes",type:Boolean},{description:"Upload a preview version (wrangler versions upload) instead of going live — prints a preview URL; doesn't shift production traffic",name:"preview",type:Boolean},{description:"Deploy to a temporary Cloudflare account when unauthenticated (wrangler deploy --temporary; live ~60min, then claim or it's deleted). Wrangler errors if you're already authenticated.",name:"temporary",type:Boolean},{description:"Re-bless the committed schema baseline (lunora/.lunora-schema.json) with the current shape",name:"update-schema-baseline",type:Boolean}]},I={argument:{description:"list | inspect <version-id> | rollback [version-id] | promote <version-id>",name:"subcommand",type:String},description:"List deployments and roll back / promote / inspect Worker versions",examples:[["lunora deployments list","Show the 10 most recent deployments"],["lunora deployments inspect <version-id>","View a specific Worker version"],["lunora deployments rollback --yes","Roll back to the previous version"],["lunora deployments promote <version-id> --yes","Send 100% of traffic to a version"]],group:"Deploy",loader:()=>import("../packem_chunks/handler6.mjs").then(e=>({default:e.execute})),name:"deployments",options:[{description:"Cloudflare environment name",name:"env",type:String},{description:"Display `list` output as JSON",name:"json",type:Boolean},{description:"Reason/description recorded with a rollback or promote",name:"message",type:String},{description:"Confirm a rollback or promote (required — these change live traffic)",name:"yes",type:Boolean}]},$={argument:{description:"Optional subcommand: stop (shut the running dev server down) | status (report it) | logs (print its captured output)",name:"args",type:String},description:"Run the dev stack: wrangler worker + studio + codegen watch",examples:[["lunora dev","Run the worker + studio + codegen watch"],["lunora dev --background","Run detached: blocks until ready, prints URL + PID, then returns"],["lunora dev stop","Stop the background/tracked dev server (idempotent)"],["lunora dev status","Report the running dev server (URL, PID, uptime)"],["lunora dev logs","Print the captured dev-server log (background runs)"],["lunora dev --json","Machine-readable JSON log lines (also LUNORA_LOG_JSON=1)"],["lunora dev --no-studio","Skip the embedded studio server"],["lunora dev --worker-port 8080","Use a custom wrangler dev port"],["lunora dev --remote","Proxy D1/KV/R2 to the deployed worker (also LUNORA_REMOTE=1)"]],group:"Develop",loader:()=>import("../packem_chunks/planDevCommand.mjs").then(e=>({default:e.execute})),name:"dev",options:[{description:`Which API spec(s) codegen emits: ${l} (default openapi)`,name:"api-spec",type:String},{description:"Studio server port (default 6173)",name:"port",type:Number},{description:"wrangler dev port (default 8787)",name:"worker-port",type:Number},{description:"Run the dev server as a managed background process (auto-enabled when an AI agent is detected; LUNORA_AGENT_MODE=0 disables)",name:"background",type:Boolean},{description:"Emit machine-readable JSON log lines (also LUNORA_LOG_JSON=1; auto-enabled for AI agents)",name:"json",type:Boolean},{description:"How many trailing lines `lunora dev logs` prints (default 100, 0 = all)",name:"lines",type:Number},{description:"Don't start the embedded studio server",name:"no-studio",type:Boolean},{description:"Don't watch + regenerate codegen",name:"no-codegen",type:Boolean},{description:"Proxy D1/KV/R2 bindings to the deployed worker (or set LUNORA_REMOTE=1)",name:"remote",type:Boolean}]},U={argument:{description:"Optional path under the docs site (e.g. addons/studio)",name:"section",type:String},description:"Open the Lunora docs in your browser (optional [section] path)",examples:[["lunora docs","Open the Lunora docs"],["lunora docs addons/studio","Open a specific docs section"]],group:"Project",loader:()=>import("../packem_chunks/handler7.mjs").then(e=>({default:e.execute})),name:"docs"},P={description:"Preflight the current Lunora project (wrangler bindings, placeholders, dev secrets)",examples:[["lunora doctor","Run the project preflight checks"]],group:"Project",loader:()=>import("../packem_chunks/handler8.mjs").then(e=>({default:e.execute})),name:"doctor",options:[]},W={argument:{description:"list | get <KEY> | set <KEY> <VALUE> | unset <KEY> | generate [KEY] | push | diff | doctor",name:"subcommand",type:String},description:"Manage .dev.vars and sync secrets via wrangler (list | get | set | unset | generate | push | diff | doctor)",examples:[["lunora env list","List .dev.vars keys"],["lunora env set API_KEY secret","Set a local variable"],["lunora env generate","Generate strong values for the project's secrets (print KEY=value)"],["lunora env generate AUTH_SECRET --set","Generate one secret and write it to .dev.vars"],["lunora env push --yes","Upload secrets to Cloudflare"],["lunora env diff","Compare local .dev.vars keys against Cloudflare"]],group:"Data",loader:()=>import("../packem_chunks/handler9.mjs").then(e=>({default:e.execute})),name:"env",options:[{description:"Target production for `push` (passes --env production to wrangler)",name:"prod",type:Boolean},{description:"For `generate` — write the generated secrets into .dev.vars instead of printing them",name:"set",type:Boolean},{description:"Push secrets to a temporary-account deployment when unauthenticated (wrangler secret put --temporary). Errors if you're already authenticated.",name:"temporary",type:Boolean},{description:"Required for `push` — confirms uploading secrets to Cloudflare",name:"yes",type:Boolean}]},M={argument:{description:"Optional path (alias for --out)",name:"path",type:String},description:"Stream NDJSON of every shard-local + global table from the worker",examples:[["lunora export --out backup.ndjson","Dump every table to an NDJSON file"],["lunora export --tables messages,users","Export only specific tables"]],group:"Data",loader:()=>import("../packem_chunks/handler10.mjs").then(e=>({default:e.execute})),name:"export",options:[{description:"Output file path (`-` for stdout, default)",name:"out",type:String},{description:"Comma-separated table allowlist",name:"tables",type:String},{description:"Target production — requires an explicit --url",name:"prod",type:Boolean},{description:"Worker URL (default http://localhost:8787)",name:"url",type:String},{description:"Admin bearer token (prefer LUNORA_ADMIN_TOKEN; --token is visible to other local processes via the process table)",name:"token",type:String}]},J={argument:{description:"Source NDJSON file",name:"file",type:String},description:"Bulk-insert rows from an NDJSON file via the worker's admin endpoint",examples:[["lunora import backup.ndjson","Bulk-insert rows from an NDJSON file"]],group:"Data",loader:()=>import("../packem_chunks/handler11.mjs").then(e=>({default:e.execute})),name:"import",options:[{description:"Wrap each bare doc as `{table:<name>,doc:...}`",name:"table",type:String},{description:"Rows per HTTP request (default 500)",name:"batch-size",type:Number},{description:"Target production — requires an explicit --url",name:"prod",type:Boolean},{description:"Confirm bulk-writing production (required with --prod)",name:"yes",type:Boolean},{description:"Worker URL (default http://localhost:8787)",name:"url",type:String},{description:"Admin bearer token (prefer LUNORA_ADMIN_TOKEN; --token is visible to other local processes via the process table)",name:"token",type:String}]},K={description:"Print resolved project config: @lunora/* versions, wrangler summary, schema overview",examples:[["lunora info","Print resolved project config"],["lunora info --json","Emit a JSON snapshot"]],group:"Project",loader:()=>import("../packem_chunks/handler12.mjs").then(e=>({default:e.execute})),name:"info",options:[{description:"Emit a JSON snapshot instead of human text",name:"json",type:Boolean}]},q={argument:{description:"Project name",name:"name",type:String},description:"Scaffold a new Lunora project",examples:[["lunora init my-app","Scaffold with the default (vite) template"],["lunora init my-app -t next","Scaffold a Next.js app"],["lunora init my-app -t tanstack-start-react","Scaffold a TanStack Start (React) app"],["lunora init my-app -t tanstack-start-solid","Scaffold a TanStack Start (Solid) app"],["lunora init my-app --ref alpha","Scaffold from the alpha branch's templates"],["lunora init --here","Add Lunora to the current project"],["lunora init my-app --ci github","Scaffold + add a GitHub Actions deploy pipeline"],["lunora init my-app --ci gitlab","Scaffold + add a GitLab CI deploy pipeline"]],group:"Project",loader:()=>import("../packem_chunks/runInitCommand.mjs").then(e=>({default:e.execute})),name:"init",options:[{alias:"t",description:"Bespoke template (standalone | astro | next | nuxt | sveltekit | tanstack-start-react | tanstack-start-solid). For an SPA use --vite react|vue|solid|svelte.",name:"template",type:String},{description:"Scaffold via the create-vite overlay for a framework (react | vue | solid | svelte | vanilla) — official create-vite base + Lunora layer",name:"vite",type:String},{description:"Local templates root to copy from (offline-friendly; expects <type>/ subdirs)",name:"from",type:String},{description:"Override the remote template source (e.g. gh:owner/repo/sub#ref)",name:"source",type:String},{description:"Fetch templates from a git ref (branch, tag, or commit), e.g. --ref alpha. Overrides the version-derived default",name:"ref",type:String},{description:"Permit --source values outside gh:/github:/https:// (e.g. local file://)",name:"allow-unsafe-source",type:Boolean},{description:"Add Lunora to the current project: detect the framework, patch the config, scaffold lunora/, print per-framework wiring steps",name:"here",type:Boolean},{alias:"i",description:"After scaffolding, offer to add auth + email (defaults on when stdin is a TTY)",name:"interactive",type:Boolean},{alias:"y",description:"Skip the auth/email offer; scaffold only",name:"yes",type:Boolean},{description:"Also scaffold a CI deploy pipeline: github (.github/workflows/deploy.yml) or gitlab (.gitlab-ci.yml)",name:"ci",type:String},{description:"Add features non-interactively after scaffolding (comma-separated): ai | auth | backup | browser | cloudflare-access | crons | email | flags | hyperdrive | payment | presence | queue | storage | workflow",name:"add",type:String},{description:"Walk through every step (prompts + output) without writing files, installing, or running git",name:"dry-run",type:Boolean}]},H={description:"Report write-conflict hot-spots, error rates, and latency outliers from a running Worker",examples:[["lunora insights","Report against the local dev worker"],["lunora insights --shard channel:demo","Scope the report to one shard"],["lunora insights --json","Emit the raw report as JSON"],["lunora insights --prod --url https://app.example.com --token $LUNORA_ADMIN_TOKEN","Report against production"]],group:"Develop",loader:()=>import("../packem_chunks/handler13.mjs").then(e=>({default:e.execute})),name:"insights",options:[{description:"Explicit shard key (defaults to the root shard)",name:"shard",type:String},{description:"Max rows per section (default 10)",name:"limit",type:String},{description:"Emit a JSON report instead of human text",name:"json",type:Boolean},{description:"Target production — requires an explicit --url",name:"prod",type:Boolean},{description:"Worker URL (default http://localhost:8787)",name:"url",type:String},{description:"Admin bearer token (or LUNORA_ADMIN_TOKEN)",name:"token",type:String}]},F={description:"Link this checkout to its deployed Worker (writes .lunora/project.json)",examples:[["lunora link --url https://app.acme.workers.dev","Link to a deployed Worker URL"],["lunora link --url https://app.acme.workers.dev --env production","Link a named environment"],["lunora link --remove","Remove the link"]],group:"Deploy",loader:()=>import("../packem_chunks/handler14.mjs").then(e=>({default:e.execute})),name:"link",options:[{description:"Cloudflare environment name to record alongside the link",name:"env",type:String},{description:"Worker name (defaults to the `name` in wrangler config)",name:"name",type:String},{description:"Remove the existing link (.lunora/project.json)",name:"remove",type:Boolean},{description:"Deployed Worker URL to link (e.g. https://app.acme.workers.dev)",name:"url",type:String}]},V={argument:{description:"Worker name (defaults to the name in wrangler config)",name:"worker",type:String},description:"Stream live logs from a deployed Worker, or read the durable log archive with --durable",group:"Deploy",loader:()=>import("../packem_chunks/handler21.mjs").then(e=>({default:e.execute})),name:"logs",options:[{description:"Cloudflare environment name",name:"env",type:String},{description:"Output format: pretty (default) or json",name:"format",type:String},{description:"Substring filter on log messages",name:"search",type:String},{description:"Filter by invocation status: ok, error, or canceled",name:"status",type:String},{description:"Tail a temporary-account deployment when unauthenticated (wrangler tail --temporary). Errors if you're already authenticated.",name:"temporary",type:Boolean},{description:"Read the durable log archive (pipelineLogSink → R2) via R2 SQL instead of tailing live",name:"durable",type:Boolean},{description:"durable: Iceberg table the Pipeline writes to (required with --durable)",name:"table",type:String},{description:"durable: Iceberg namespace (R2 Data Catalog database) the table lives in",name:"namespace",type:String},{description:"durable: lower time bound (epoch-millis or ISO 8601), inclusive",name:"since",type:String},{description:"durable: upper time bound (epoch-millis or ISO 8601), inclusive",name:"until",type:String},{description:"durable: exact severity filter (trace|debug|log|info|warn|error|fatal)",name:"level",type:String},{description:"durable: severity floor — this level and every more-severe one",name:"min-level",type:String},{description:"durable: keep function paths starting with this prefix (LIKE 'prefix%')",name:"function-prefix",type:String},{description:"durable: trace-id filter",name:"trace-id",type:String},{description:"durable: shard-key filter",name:"shard-key",type:String},{description:"durable: user-id filter",name:"user-id",type:String},{description:"durable: max rows (clamped to 1–10000; default 500)",name:"limit",type:String},{description:"durable: resume after a prior page — the ts from its nextCursor",name:"cursor",type:String},{description:"durable: emit one JSON object per line instead of a table",name:"ndjson",type:Boolean}]},G={argument:{description:"generate | create | up | down | status | d1-to-hyperdrive [name|id]",name:"subcommand",type:String},description:"Schema (generate), online data (create | up | down | status), and backend (d1-to-hyperdrive) migrations",examples:[["lunora migrate generate","Diff lunora/schema.ts and emit a SQL migration"],["lunora migrate create add_users_email","Scaffold a data migration"],["lunora migrate up backfill-names","Run a data migration across shards"],["lunora migrate status backfill-names","Report a migration's per-shard status"],["lunora migrate d1-to-hyperdrive --from-url https://old --to-url https://new","Copy .global() data from D1 to Hyperdrive"]],group:"Data",loader:()=>import("../packem_chunks/runMigrateGenerateCommand.mjs").then(e=>({default:e.execute})),name:"migrate",options:[{description:"Migration name slug (e.g. add_users_email)",name:"name",type:String},{description:"Target table for `create` (prompted for interactively when omitted)",name:"table",type:String},{description:"Preview a data migration without rewriting rows",name:"dry-run",type:Boolean},{description:"Rows per batch for a data migration",name:"batch-size",type:Number},{description:"Cap batches processed this run (maps to the runner's maxBatches)",name:"steps",type:Number},{description:"Target production — requires an explicit --url",name:"prod",type:Boolean},{description:"Worker URL (default http://localhost:8787)",name:"url",type:String},{description:"Admin bearer token (prefer LUNORA_ADMIN_TOKEN; --token is visible to other local processes via the process table)",name:"token",type:String},{description:"Required with --prod for up/down — confirms running against production",name:"yes",type:Boolean},{description:"d1-to-hyperdrive: source (D1) worker URL (defaults to --url)",name:"from-url",type:String},{description:"d1-to-hyperdrive: source admin token (defaults to --token / LUNORA_ADMIN_TOKEN)",name:"from-token",type:String},{description:"d1-to-hyperdrive: target (Hyperdrive) worker URL (defaults to --url)",name:"to-url",type:String},{description:"d1-to-hyperdrive: target admin token (defaults to --token / LUNORA_ADMIN_TOKEN)",name:"to-token",type:String},{description:"d1-to-hyperdrive: comma-separated .global() tables to move (default: all global tables)",name:"tables",type:String},{description:"d1-to-hyperdrive: keep the intermediate NDJSON dump at this path",name:"out",type:String}]},Y={description:"Run codegen + binding reconcile + wrangler validation (no Vite) — for CI",examples:[["lunora prepare","Codegen + binding reconcile + validate (CI, before deploy)"]],group:"Deploy",loader:()=>import("../packem_chunks/handler15.mjs").then(e=>({default:e.execute})),name:"prepare",options:[{description:"Override the schema-drift gate (proceed even with breaking schema drift and no migration)",name:"allow-schema-drift",type:Boolean},{description:`Which API spec(s) to emit: ${l} (default openapi)`,name:"api-spec",type:String},{description:"Re-bless the committed schema baseline (lunora/.lunora-schema.json) with the current shape",name:"update-schema-baseline",type:Boolean}]},z={argument:{description:"<add|list|view|build> [item names…]",name:"args",type:String},description:"Component registry: add/list/view items, or build the catalog",examples:[["lunora registry list","List available registry items"],["lunora registry add presence","Scaffold a registry item into lunora/"],["lunora registry build --check","Verify the committed catalog is current"]],group:"Project",loader:()=>import("../packem_chunks/handler16.mjs").then(e=>({default:e.execute})),name:"registry",options:[{description:"add: print the plan and stop without writing",name:"dry-run",type:Boolean},{description:"add: preview the file changes (content diff) and write nothing",name:"diff",type:Boolean},{description:"add: force-overwrite existing files (take the incoming copy)",name:"overwrite",type:Boolean},{description:"add: skip the package.json mutation confirmation prompt",name:"yes",type:Boolean},{description:"Local registry root (offline; expects <name>/ subdirs)",name:"from",type:String},{description:"Override the remote registry source base (e.g. gh:owner/repo/registry)",name:"source",type:String},{description:"Fetch items from a git ref (branch, tag, or commit), e.g. --ref alpha. Overrides the version-derived default",name:"ref",type:String},{description:"Permit --source values outside gh:/github:/https://",name:"allow-unsafe-source",type:Boolean},{description:"Emit JSON output (add plan / list)",name:"json",type:Boolean},{description:"build: output path for the catalog (default <root>/index.json)",name:"out",type:String},{description:"build: verify the index is current instead of rewriting it",name:"check",type:Boolean}]},Q={description:"Clear local Miniflare state (and .lunora-cache with --all)",examples:[["lunora reset","Clear local Miniflare state"],["lunora reset --all","Also remove .lunora-cache"]],group:"Develop",loader:()=>import("../packem_chunks/runResetCommand.mjs").then(e=>({default:e.execute})),name:"reset",options:[{description:"Also remove .lunora-cache",name:"all",type:Boolean},{description:"Skip the confirmation prompt (required when stdin is not a TTY)",name:"yes",type:Boolean}]},X={argument:{description:"install | check",name:"subcommand",type:String},description:"Install the Lunora agent skills (AI rules) into .agents/skills/, or check they're present",examples:[["lunora rules install","Copy the Lunora agent skills into .agents/skills/"],["lunora rules install --overwrite","Reinstall, replacing edited skill files"],["lunora rules check","Report which Lunora skills are installed"],["lunora rules check --strict","Exit non-zero when rules are missing (CI gate)"]],group:"Project",loader:()=>import("../packem_chunks/handler17.mjs").then(e=>({default:e.execute})),name:"rules",options:[{description:"install: overwrite skill files that already exist (default: skip them)",name:"overwrite",type:Boolean},{description:"check: exit non-zero when the rules are missing (for CI gating)",name:"strict",type:Boolean}]},Z={argument:{description:"Function path (e.g. messages:send)",name:"functionPath",type:String},description:"Send a single RPC to a running Lunora Worker",examples:[[`lunora run messages:send --args '{"text":"hi"}'`,"Call a function with JSON args"],["lunora run messages:list --shard channel:demo","Target a specific shard"]],group:"Develop",loader:()=>import("../packem_chunks/runRpcCommand.mjs").then(e=>({default:e.execute})),name:"run",options:[{description:"JSON-encoded args object",name:"args",type:String},{description:"Explicit shard key",name:"shard",type:String},{description:"Worker URL (default http://localhost:8787)",name:"url",type:String}]},ee={description:"Generate deterministic fake data from lunora/schema.ts and bulk-insert it via the worker's admin endpoint",examples:[["lunora seed","Seed every table with the default row count"],["lunora seed --table posts --count 50","Seed 50 posts; FK-parent tables are seeded automatically"],["lunora seed --reset","Wipe local .wrangler/state, then seed from scratch"],["lunora seed --seed 7 --dry-run","Print the NDJSON for seed 7 without inserting"]],group:"Data",loader:()=>import("../packem_chunks/handler18.mjs").then(e=>({default:e.execute})),name:"seed",options:[{description:"Rows per table (default 10)",name:"count",type:Number},{description:"Seed only this table; its FK-parent tables are seeded automatically",name:"table",type:String},{description:"Deterministic seed — same value yields identical rows (default 0)",name:"seed",type:Number},{description:"Print the generated NDJSON instead of inserting",name:"dry-run",type:Boolean},{description:"Wipe local .wrangler/state before seeding (local dev only)",name:"reset",type:Boolean},{description:"Rows per HTTP request (default 500)",name:"batch-size",type:Number},{description:"Target production — requires an explicit --url",name:"prod",type:Boolean},{description:"Worker URL (default http://localhost:8787)",name:"url",type:String},{description:"Admin bearer token (prefer LUNORA_ADMIN_TOKEN; --token is visible to other local processes via the process table)",name:"token",type:String},{description:"Skip the confirmation prompt when seeding a non-local/production target",name:"yes",type:Boolean}]},te={description:"Validate wrangler.jsonc + codegen dry-run + tsc --noEmit (no files written)",examples:[["lunora verify","Validate wrangler + codegen + tsc"],["lunora verify --no-typecheck","Skip the TypeScript type-check"],["lunora verify --health-url https://my-app.workers.dev","Also probe the deployment's /_lunora/health"]],group:"Deploy",loader:()=>import("../packem_chunks/handler19.mjs").then(e=>({default:e.execute})),name:"verify",options:[{description:"Treat breaking schema drift as a warning instead of a failure",name:"allow-schema-drift",type:Boolean},{description:`Which API spec(s) to emit: ${l} (default openapi)`,name:"api-spec",type:String},{description:"Output format: pretty (default) or json",name:"format",type:String},{description:"Probe this deployment's /_lunora/health endpoint (off by default; keeps verify offline-safe)",name:"health-url",type:String},{description:"Skip the TypeScript type-check step",name:"no-typecheck",type:Boolean}]},re={description:"Open the Lunora studio in your browser (local dev by default, --remote for production)",examples:[["lunora view","Open the studio for local dev"],["lunora view --remote","Open the deployed studio"]],group:"Project",loader:()=>import("../packem_chunks/handler20.mjs").then(e=>({default:e.execute})),name:"view",options:[{description:"Open the deployed worker URL instead of localhost",name:"remote",type:Boolean}]},oe={filterStacktrace:()=>!1,hideErrorCodeView:!0},ne=(e,t={})=>{const r=e instanceof Error?e.message:String(e),o=k(m(e)?{code:e.code,hint:e.hint,message:r}:r),n=new R({hint:o===void 0?void 0:S(o).split(`
`),message:t.reason===void 0?r:`${t.reason}: ${r}`,name:e instanceof Error&&e.name.length>0?e.name:"Error"});return n.stack="",A(n,oe)},ae=(e,t)=>{const r=Array.from({length:t.length+1},(o,n)=>n);for(let o=1;o<=e.length;o+=1){let n=r[0]??0;r[0]=o;for(let a=1;a<=t.length;a+=1){const i=r[a]??0,s=e[o-1]===t[a-1]?0:1;r[a]=Math.min((r[a-1]??0)+1,i+1,n+s),n=i}}return r[t.length]??0},ie=(e,t)=>{let r,o=Number.POSITIVE_INFINITY;for(const a of t){const i=ae(e,a);i<o&&(o=i,r=a)}const n=Math.max(2,Math.ceil(e.length/3));return r!==void 0&&o<=n?r:void 0},se="https://registry.npmjs.org/@lunora/cli/latest",le=1440*60*1e3,pe=1500,de="0.0.0",ce=/^v/u,c=e=>{const t=e.trim().replace(ce,"").split("-")[0]??"",[r,o,n]=t.split(".").map(a=>{const i=Number.parseInt(a,10);return Number.isFinite(i)?i:0});return[r??0,o??0,n??0]},ue=(e,t)=>{const r=c(e),o=c(t);for(let n=0;n<3;n+=1)if((r[n]??0)!==(o[n]??0))return(r[n]??0)>(o[n]??0)?1:-1;return 0},me=(e,t)=>ue(t,e)>0,ge=(e,t,r)=>t-e<r,he=(e,t)=>`Update available for @lunora/cli: ${e} → ${t} — run \`pnpm add -D @lunora/cli@latest\``,h=e=>d(e,"lunora-cli-update.json"),ye=e=>{const t=e.XDG_CACHE_HOME&&e.XDG_CACHE_HOME.length>0?e.XDG_CACHE_HOME:d(B(),".cache"),r=d(t,"lunora");try{f(r,{mode:448,recursive:!0})}catch{}return r},fe=e=>{try{const t=JSON.parse(u(h(e),"utf8"));if(t!==null&&typeof t=="object"){const{checkedAt:r,latest:o}=t;if(typeof o=="string"&&typeof r=="number")return{checkedAt:r,latest:o}}}catch{}},ve=(e,t)=>{try{const r=h(e);try{if(v(r).isSymbolicLink())return}catch{}b(r,`${JSON.stringify(t)}
`,"utf8")}catch{}},be=async e=>{try{const t=await e(se,{signal:AbortSignal.timeout(pe)});if(!t.ok)return;const r=await t.json(),o=r!==null&&typeof r=="object"?r.version:void 0;return typeof o=="string"?o:void 0}catch{return}},ke=(e,t,r)=>e===de||!r||t.CI!==void 0||t.LUNORA_NO_UPDATE_NOTIFIER!==void 0,Se=async e=>{const t=e.env??process.env,r=e.isTTY??process.stdout.isTTY;if(ke(e.current,t,r))return;const o=e.cacheDir??ye(t),n=(e.now??Date.now)(),a=e.ttlMs??le,i=fe(o);let s=i?.latest;if(i===void 0||!ge(i.checkedAt,n,a)){const p=await be(e.fetchImpl??globalThis.fetch);p!==void 0&&(s=p,ve(o,{checkedAt:n,latest:p}))}s!==void 0&&me(e.current,s)&&e.logger.warn(he(e.current,s))},we=["init","add","dev","codegen","build","deploy","containers","prepare","link","deployments","logs","run","insights","reset","migrate","export","import","seed","backup","verify","info","doctor","env","analyze","view","docs","registry","rules"],xe=()=>{try{const e=JSON.parse(u(new URL("../package.json",import.meta.url),"utf8")),t=e!==null&&typeof e=="object"?e.version:void 0;return typeof t=="string"&&t.length>0?t:"0.0.0"}catch{return"0.0.0"}},y=xe(),Oe=[q,D,$,j,C,_,T,Y,F,I,V,Z,H,Q,G,M,J,ee,E,te,K,P,W,L,re,U,z,X],Ne=e=>{const t={value:0},r=x("lunora",{argv:e.argv===void 0?void 0:[...e.argv],cwd:e.cwd,exit:o=>{t.value=typeof o=="number"?o:0},logger:e.logger,packageName:"@lunora/cli",packageVersion:y});for(const o of Oe)r.addCommand(o);return r.addCommand(N),r.addCommand(O),{cli:r,exitCode:t}},Re=/Command "(?<name>[^"]+)" not found/u,Ae=e=>{const t=g(),r=e instanceof Error?e.message:String(e),o=Re.exec(r);if(!o?.groups){m(e)||w(r)!==void 0?t.error(ne(e)):t.error(r);return}const n=o.groups.name??"",a=ie(n,we);t.error(`Unknown command "${n}".${a===void 0?"":` Did you mean "${a}"?`}`),t.info("Run `lunora --help` to list commands, or `lunora docs` to open the documentation.")},Ue=async(e={})=>{const{cli:t,exitCode:r}=Ne(e);try{await t.run({shouldExitProcess:!1})}catch(o){return Ae(o),1}return await Se({current:y,logger:g()}),r.value};export{we as COMMANDS,y as VERSION,Ue as runCli};