UNPKG

@lunora/cli

Version:

The Lunora CLI: init, dev, deploy, codegen, migrate, seed, doctor, insights, logs, registry, and the rest of the project commands

177 lines (151 loc) • 10.7 kB
--- name: lunora description: Routes general Lunora requests to the right Lunora skill and gives the shared mental model (codegen loop, generated `api`/`internal` references, review commands, add-on capabilities, the `@lunora/mcp` server). Use when the user asks which Lunora skill to use, gives an underspecified task in a project with a `lunora/` directory or `lunorash`/`@lunora/*` dependencies, asks to review or audit Lunora code, asks about an add-on without its own skill (queues, workflows, agents, AI, flags, payments, notifications), or wants to expose a deployment to an AI agent over MCP. --- # Lunora Lunora is a type-safe, real-time backend on Cloudflare Workers + Durable Objects. Functions are chainable `query` / `mutation` / `action` builders; state lives in one `ShardDO` per app by default (SQLite, OCC, hibernated WebSocket subscriptions). `.shardBy(key)` partitions a table across many DOs and `.global()` replicates it to D1 (or Postgres/MySQL over Hyperdrive) for cross-region reads. Codegen produces the types the client and server share. If a project-level `AGENTS.md` / `CLAUDE.md` exists, read it first; it overrides these defaults. If a more specific Lunora skill matches, switch to it. ## The feedback loop ```bash lunora codegen # regenerate lunora/_generated/ from lunora/schema.ts + function files lunora verify # wrangler config + codegen dry-run + tsc --noEmit; writes nothing ``` `lunora codegen` writes `lunora/_generated/` (`api.ts`, `internal.ts`, `server.ts`, `dataModel.ts`, `shard.ts`, `app.ts`, `openapi.ts`, …; it prints the exact list) and reports schema advisories and platform diagnostics. It does not run `tsc`, so use `lunora verify` as the check that the code you wrote is done. Its `tsc --noEmit` step runs only when the project has a `tsconfig.json` (otherwise it warns and skips) and is turned off by `--no-typecheck`. `lunora dev` re-runs codegen on save, and `lunora deploy` runs it too. Scaffolded projects gitignore `lunora/_generated/`; never hand-edit it. ## Route to the right skill - New project, or adding Lunora to an existing app: `lunora-quickstart` - Writing or reviewing schema + functions (the core authoring rules): `lunora-functions` - Wiring live data into a client (hooks, optimistic updates), or choosing between a live query, stream, live topic, whisper, room and socket (including pushing a server event to the browser): `lunora-realtime` - Authentication (email/password, OAuth, magic link, OTP): `lunora-setup-auth` - Transactional email: `lunora-setup-mail` - R2 file storage (signed upload/download): `lunora-setup-storage` - Deferred work (`ctx.scheduler`) and cron jobs: `lunora-setup-scheduler` - Querying an existing Postgres/MySQL database from an action (`ctx.sql`, non-reactive): `lunora-setup-hyperdrive` - Postgres/MySQL as a reactive `.global()` backend, or migrating a D1 `.global()` dataset onto it: `lunora-setup-hyperdrive-global` - Building a reusable capability (registry item or `@lunora/*` package): `lunora-create-package` - Schema/data migrations: `lunora-migration-helper` - Deploying (wrangler, bindings, secrets, the drift gate): `lunora-deploy` - Slow queries, scans, write conflicts: `lunora-performance-audit` - Launch readiness, surprise Cloudflare bills, public routes reaching paid products: `lunora-cloudflare-audit` ### Reviewing existing `lunora/` code There is no review skill because two commands do the mechanical part. Run them before reading code by hand: ```bash lunora verify # does it build and typecheck lunora advisor # static + runtime lint set, scored per procedure ``` `lunora advisor` covers RLS coverage, ownership and identity checks, fail-open guards, input validators, unindexed reads, non-deterministic calls in queries, SQL interpolation and leaked secrets. It prints each finding's lint id and category, so read its output rather than guessing rule names. `--entry <file>#<export>` inspects one procedure; `--min-score` / `--baseline` gate CI. Once it is clean, a by-hand pass against `lunora-functions` is worth the tokens. ## Core mental model - Functions live in `lunora/**/*.ts` and import their builders from the generated server module: `query` (reactive read), `mutation` (transactional write), `action` (side effects, `fetch`, no direct db). `internalQuery` / `internalMutation` / `internalAction` are not callable from clients. - Schema lives in `lunora/schema.ts` (`defineSchema` + `defineTable`, validators from `v.*`), imported from `lunorash/server` or `@lunora/server`. - Function references mirror the folder tree: `lunora/billing/invoices.ts` exporting `create` is `api.billing.invoices.create`. Public references come from `_generated/api`, internal ones from `_generated/internal` (kept separate so client bundles never carry internal names). A reference is a plain `{ __lunoraRef: "billing_invoices:create" }` object; there is no `anyApi` proxy. - Reads go through indexes: declare `.index("by_x", ["x"])` and query with `ctx.db.query("t").withIndex("by_x", (q) => q.eq("x", value))`, not `.filter(...)`. - Clients subscribe over WebSocket; `useQuery` re-renders when a mutation changes the queried rows. Subscriptions run under the socket's verified identity, so `rls()` and `ctx.auth` apply to live updates too. ## Capabilities without a dedicated skill Most add-ons install as a registry item: `lunora add <feature>` (or `lunora registry add <item>`) scaffolds the `lunora/` glue, wrangler bindings and `.dev.vars` entries, then prints post-install steps. Browse with `lunora registry list`, preview with `lunora registry view <item>`. Read the installed item's README and the package's `docs/` for the API. | Goal | Install / package | | --------------------------------------------- | ---------------------------------------------------------- | | Background jobs on Cloudflare Queues | `lunora add queue` → `@lunora/queue` (`ctx.queues`) | | Durable multi-step workflows | `lunora add workflow` → `@lunora/workflow` (`ctx.runStep`) | | Durable AI agents (tool loops, HITL, memory) | `@lunora/agent` (`defineAgent`) | | Workers AI / RAG | `lunora add ai` → `@lunora/ai` (`ctx.ai`, `defineRag`) | | Feature flags (OpenFeature) | `lunora add flags` → `@lunora/flags` (`ctx.flags`) | | Payments (Stripe / Polar) | `lunora add payment` → `@lunora/payment` | | Agent payments (x402) | `@lunora/x402` | | Push / in-app / webhook notifications | `@lunora/notify` (`ctx.notify`, `ctx.push`) | | Rate limiting | `lunora add ratelimit` → `@lunora/ratelimit` | | Headless browser (action-only) | `lunora add browser` → `@lunora/browser` (`ctx.browser`) | | Cloudflare Containers | `@lunora/container` (`defineContainer`, `ctx.containers`) | | Presence / who's-here | `lunora add presence` | | Cloudflare Access (Zero Trust) identity | `lunora add cloudflare-access` | | Backup / restore | `lunora add backup` | | Testing (in-memory harness, agent doubles) | `@lunora/testing` (`lunoraTest`) | | Deterministic seed data | `@lunora/seed` + `lunora seed` | | Local-first replica / offline mirror | `@lunora/replica` | | Exposing the deployment to AI agents over MCP | `@lunora/mcp` (below) | Inside the Lunora monorepo, `vis generate lunora-<kind>` scaffolds code: `query`, `mutation`, `action`, `http-route`, `table`, `cron`, `container`, `workflow`, `queue`, `step`, `agent`, `package`, plus the no-name singletons `flags`, `auth-do` and `collections` (`vis generate --list` for the set). Pass names as `--name=value`: vis parses `--name foo` as `--name=true` plus a stray positional. ## Exposing a deployment over MCP Two entry points: - `lunora mcp install` wires an editor (Claude Code, Cursor, …) to the hosted docs server plus this project's dev server; `lunora mcp serve` is the stdio server those entries spawn (`--allow-writes`, `--token` opt into more). - The `lunora-mcp` binary from `@lunora/mcp` fronts a deployed Worker. It needs `LUNORA_URL` and `LUNORA_ADMIN_TOKEN`. Every tool reads admin-gated routes, so the token cannot be scoped down; safety comes from the gates below. Always advertised: `lunora_list_functions`, `lunora_list_tables` (`.global()` tables with row counts), `lunora_get_function_schema`, `lunora_run_query` (read-only), and `lunora_explain_error`, which explains an error `code` or message from the catalog compiled into `@lunora/errors` and needs no deployment. Use it before guessing what a Lunora error means. Everything else sits behind an env gate that both hides the tool and refuses it at dispatch. All default to off; enable one only when the answer to "may a model see this?" is yes. - `LUNORA_MCP_ALLOW_WRITES` → `lunora_run_mutation`, `lunora_run_action`. - `LUNORA_MCP_ALLOW_OBSERVABILITY` → the five `lunora_get_*` tools (logs, issues, advisories, query insights, migration status). Read-only, but they return production log lines and error messages, which then reach the model provider. - `LUNORA_MCP_ALLOW_DATA_READS` → `lunora_find_related`. It returns raw rows read through the admin writer, with RLS and column masks bypassed. It is kept separate from observability so enabling logs never also hands over rows. - `LUNORA_MCP_ALLOW_AGENTS` (with `LUNORA_MCP_AGENTS="name:description;…"`) → the `agent_<name>` tools and `lunora_agent_status`. Writes are a two-step handshake. The first `lunora_run_mutation` / `lunora_run_action` call does not execute; it returns `status: "action_required"` with `proposedAction`, an `actionDigest`, and an `expiresAt` ten minutes out. The client shows that to a human, then repeats the call with identical `functionPath`, `args` and `shardKey` plus `confirmed: true` and the digest. Any edit needs a fresh proposal, and an expired digest is refused. The digest proves the write that runs is the one proposed; it does not prove a human saw it, so enabling writes means trusting the client to ask.