@lunora/cli 1.0.0-alpha.41 → 1.0.0-alpha.411

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (136) hide show
  1. package/LICENSE.md +33 -0
  2. package/README.md +3 -1
  3. package/dist/bin.mjs +2 -10
  4. package/dist/index.d.mts +1045 -374
  5. package/dist/index.d.ts +1045 -374
  6. package/dist/index.mjs +1 -19
  7. package/dist/packem_chunks/dispatch.mjs +1 -0
  8. package/dist/packem_chunks/handler.mjs +1 -160
  9. package/dist/packem_chunks/handler10.mjs +1 -16
  10. package/dist/packem_chunks/handler11.mjs +1 -22
  11. package/dist/packem_chunks/handler12.mjs +1 -192
  12. package/dist/packem_chunks/handler13.mjs +3 -131
  13. package/dist/packem_chunks/handler14.mjs +1 -65
  14. package/dist/packem_chunks/handler15.mjs +1 -58
  15. package/dist/packem_chunks/handler16.mjs +1 -80
  16. package/dist/packem_chunks/handler17.mjs +7 -43
  17. package/dist/packem_chunks/handler18.mjs +1 -105
  18. package/dist/packem_chunks/handler19.mjs +1 -170
  19. package/dist/packem_chunks/handler2.mjs +1 -114
  20. package/dist/packem_chunks/handler20.mjs +3 -94
  21. package/dist/packem_chunks/handler21.mjs +3 -94
  22. package/dist/packem_chunks/handler22.mjs +1 -0
  23. package/dist/packem_chunks/handler23.mjs +2 -0
  24. package/dist/packem_chunks/handler24.mjs +99 -0
  25. package/dist/packem_chunks/handler25.mjs +2 -0
  26. package/dist/packem_chunks/handler26.mjs +9 -0
  27. package/dist/packem_chunks/handler27.mjs +1 -0
  28. package/dist/packem_chunks/handler3.mjs +1 -204
  29. package/dist/packem_chunks/handler4.mjs +3 -33
  30. package/dist/packem_chunks/handler5.mjs +1 -49
  31. package/dist/packem_chunks/handler6.mjs +1 -91
  32. package/dist/packem_chunks/handler7.mjs +1 -42
  33. package/dist/packem_chunks/handler8.mjs +2 -174
  34. package/dist/packem_chunks/handler9.mjs +1 -315
  35. package/dist/packem_chunks/runCodegenCommand.mjs +4 -52
  36. package/dist/packem_chunks/runDeployCommand.mjs +23 -592
  37. package/dist/packem_chunks/runDevCommand.mjs +2 -0
  38. package/dist/packem_chunks/runInitCommand.mjs +165 -1432
  39. package/dist/packem_chunks/runResetCommand.mjs +1 -41
  40. package/dist/packem_chunks/runRpcCommand.mjs +1 -68
  41. package/dist/packem_shared/COMMANDS-BpfWKiiZ.mjs +1 -0
  42. package/dist/packem_shared/DEFAULT_IMPORT_BATCH_SIZE-CyIsI3S1.mjs +1 -0
  43. package/dist/packem_shared/EXIT_CODE-D_XCixv8.mjs +1 -0
  44. package/dist/packem_shared/admin-token-VdUnvnKW.mjs +1 -0
  45. package/dist/packem_shared/admin-url-DJB8P6Jh.mjs +1 -0
  46. package/dist/packem_shared/api-spec-lYyq4vL6.mjs +1 -0
  47. package/dist/packem_shared/binding-manifest-file-CQ1eQYy1.mjs +2 -0
  48. package/dist/packem_shared/buildRegistryIndex-Dc8D8AR6.mjs +1 -0
  49. package/dist/packem_shared/cap-error-body-YBKO32BF.mjs +1 -0
  50. package/dist/packem_shared/catalog-CahzmDLV.mjs +1 -0
  51. package/dist/packem_shared/cli-CPcfZdt0.mjs +2 -0
  52. package/dist/packem_shared/cli-manifest-DoQ_3bzs.mjs +1 -0
  53. package/dist/packem_shared/cloudflare-credentials-BVmZnDZk.mjs +1 -0
  54. package/dist/packem_shared/codegen-error-AmH54ofi.mjs +3 -0
  55. package/dist/packem_shared/command-CB4JM-Jl.mjs +1 -0
  56. package/dist/packem_shared/commands-DvSfLvyt.mjs +13 -0
  57. package/dist/packem_shared/createLogger-4TujevyB.mjs +2 -0
  58. package/dist/packem_shared/createRecordingSpawner-Ch7zuQQE.mjs +1 -0
  59. package/dist/packem_shared/deploy-target-resolve-BTCW1Iv4.mjs +1 -0
  60. package/dist/packem_shared/diffSnapshots-htqNZT8Q.mjs +7 -0
  61. package/dist/packem_shared/docker-AqLAkOyY.mjs +1 -0
  62. package/dist/packem_shared/free-port-DjbFhaaT.mjs +1 -0
  63. package/dist/packem_shared/health-probe-Bw9JFfIy.mjs +1 -0
  64. package/dist/packem_shared/import-BRM1hs7j.mjs +12 -0
  65. package/dist/packem_shared/index-oHZBGBwB.mjs +1 -0
  66. package/dist/packem_shared/insertSchemaExtension-DuiV6cba.mjs +8 -0
  67. package/dist/packem_shared/lint-ignore-report-DKZagpqk.mjs +2 -0
  68. package/dist/packem_shared/mcp-config-file-CcDTeXIq.mjs +2 -0
  69. package/dist/packem_shared/open-url-EnKy--w-.mjs +1 -0
  70. package/dist/packem_shared/output-format-DWe_tkTR.mjs +2 -0
  71. package/dist/packem_shared/parseManifest-CwPTKdtS.mjs +1 -0
  72. package/dist/packem_shared/path-containment-CgxYZggb.mjs +1 -0
  73. package/dist/packem_shared/plan-DFY3CqHD.mjs +5 -0
  74. package/dist/packem_shared/planDevCommand-BDoJUgQ4.mjs +1 -0
  75. package/dist/packem_shared/platform-diagnostics-Cn2g6Jh-.mjs +4 -0
  76. package/dist/packem_shared/prompt-cancelled-BvsNxg_Q.mjs +1 -0
  77. package/dist/packem_shared/render-lunora-error-B6893vbU.mjs +2 -0
  78. package/dist/packem_shared/resolve-Bdc9KEUv.mjs +5 -0
  79. package/dist/packem_shared/resolve-target-C_ZloTvd.mjs +1 -0
  80. package/dist/packem_shared/runAddCommand-BChdJQ5L.mjs +1 -0
  81. package/dist/packem_shared/runExportCommand-slAq5BCF.mjs +5 -0
  82. package/dist/packem_shared/runMigrateGenerateCommand-BwemPIOz.mjs +11 -0
  83. package/dist/packem_shared/schemaIrToSnapshot-BHuNOfgs.mjs +1 -0
  84. package/dist/packem_shared/secret-key-8DPOH98z.mjs +1 -0
  85. package/dist/packem_shared/shared-D-zCOmgY.mjs +1 -0
  86. package/dist/packem_shared/storage-DNzYhXR-.mjs +1 -0
  87. package/dist/packem_shared/tui-prompts-B3YwUhGw.mjs +4 -0
  88. package/dist/packem_shared/variables-CPYyJtsL.mjs +1 -0
  89. package/dist/packem_shared/wrangler-name-Dsk5K1f-.mjs +1 -0
  90. package/dist/packem_shared/wrangler-secrets-CTQPpalP.mjs +1 -0
  91. package/package.json +39 -19
  92. package/skills/README.md +36 -17
  93. package/skills/lunora/SKILL.md +158 -65
  94. package/skills/lunora-cloudflare-audit/SKILL.md +163 -0
  95. package/skills/lunora-create-package/SKILL.md +97 -84
  96. package/skills/lunora-deploy/SKILL.md +175 -105
  97. package/skills/lunora-functions/SKILL.md +363 -56
  98. package/skills/lunora-migration-helper/SKILL.md +162 -137
  99. package/skills/lunora-performance-audit/SKILL.md +137 -95
  100. package/skills/lunora-quickstart/SKILL.md +137 -136
  101. package/skills/lunora-realtime/SKILL.md +217 -97
  102. package/skills/lunora-setup-auth/SKILL.md +162 -102
  103. package/skills/lunora-setup-hyperdrive/SKILL.md +130 -109
  104. package/skills/lunora-setup-hyperdrive-global/SKILL.md +93 -90
  105. package/skills/lunora-setup-mail/SKILL.md +85 -113
  106. package/skills/lunora-setup-scheduler/SKILL.md +132 -96
  107. package/skills/lunora-setup-storage/SKILL.md +189 -95
  108. package/dist/packem_chunks/planDevCommand.mjs +0 -541
  109. package/dist/packem_chunks/runMigrateGenerateCommand.mjs +0 -397
  110. package/dist/packem_shared/COMMANDS-B0ftFD_3.mjs +0 -948
  111. package/dist/packem_shared/DEFAULT_IMPORT_BATCH_SIZE-Ck-2bU08.mjs +0 -244
  112. package/dist/packem_shared/admin-url-4UzT-CI4.mjs +0 -19
  113. package/dist/packem_shared/api-spec-CtA6ilu4.mjs +0 -13
  114. package/dist/packem_shared/buildRegistryIndex-BcYe607_.mjs +0 -38
  115. package/dist/packem_shared/codegen-error-DJG-ghs_.mjs +0 -31
  116. package/dist/packem_shared/command-D3lB_4Az.mjs +0 -19
  117. package/dist/packem_shared/commands-B-gR09Z_.mjs +0 -845
  118. package/dist/packem_shared/createLogger-B40gPzQo.mjs +0 -78
  119. package/dist/packem_shared/createRecordingSpawner-DxI3mebw.mjs +0 -43
  120. package/dist/packem_shared/detect-package-manager-DYp7n3mJ.mjs +0 -61
  121. package/dist/packem_shared/diffSnapshots-BeDvvNiF.mjs +0 -161
  122. package/dist/packem_shared/docker-hMQ97KSQ.mjs +0 -21
  123. package/dist/packem_shared/insertSchemaExtension-DAqbfr9Z.mjs +0 -64
  124. package/dist/packem_shared/open-url-Dfq6fAyT.mjs +0 -41
  125. package/dist/packem_shared/output-format-wUvAN6AL.mjs +0 -17
  126. package/dist/packem_shared/parseArgs-YXFuKdEk.mjs +0 -56
  127. package/dist/packem_shared/parseManifest--vZf2FY1.mjs +0 -94
  128. package/dist/packem_shared/prompt-cancelled-APzX1Im-.mjs +0 -9
  129. package/dist/packem_shared/resolve-target-qbsJ_5sF.mjs +0 -16
  130. package/dist/packem_shared/runAddCommand-bnY6-HKb.mjs +0 -4
  131. package/dist/packem_shared/schema-drift-gate-BtBt0as0.mjs +0 -79
  132. package/dist/packem_shared/schemaIrToSnapshot-DdsljJT-.mjs +0 -43
  133. package/dist/packem_shared/storage-BIsph-Vk.mjs +0 -84
  134. package/dist/packem_shared/tui-prompts-BjEN8XgP.mjs +0 -658
  135. package/dist/packem_shared/wrangler-name-cy4yhm9j.mjs +0 -12
  136. package/dist/packem_shared/wrangler-secrets-P2_ZUR-k.mjs +0 -47
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@lunora/cli",
3
- "version": "1.0.0-alpha.41",
4
- "description": "The Lunora CLI: init, dev, deploy, codegen, run, reset, and migrate commands",
3
+ "version": "1.0.0-alpha.411",
4
+ "description": "The Lunora CLI: init, dev, deploy, codegen, migrate, seed, doctor, insights, logs, registry, and the rest of the project commands",
5
5
  "keywords": [
6
6
  "agent-skills",
7
7
  "cli",
@@ -51,27 +51,47 @@
51
51
  "access": "public"
52
52
  },
53
53
  "dependencies": {
54
- "@bomb.sh/tab": "0.0.17",
55
- "@lunora/codegen": "1.0.0-alpha.17",
56
- "@lunora/config": "1.0.0-alpha.29",
57
- "@lunora/container": "1.0.0-alpha.5",
58
- "@lunora/d1": "1.0.0-alpha.11",
59
- "@lunora/seed": "1.0.0-alpha.7",
60
- "@visulima/cerebro": "3.0.0-alpha.32",
61
- "@visulima/error": "6.0.0-alpha.34",
62
- "@visulima/fs": "5.0.0-alpha.32",
63
- "@visulima/pail": "4.0.0-alpha.22",
64
- "@visulima/path": "3.0.0-alpha.13",
65
- "@visulima/spinner": "1.0.0-alpha.4",
66
- "@visulima/tui": "1.0.0-alpha.26",
67
- "cfonts": "^3.3.1",
68
- "giget": "3.3.0",
54
+ "@bomb.sh/tab": "0.0.22",
55
+ "@lunora/advisor": "1.0.0-alpha.244",
56
+ "@lunora/bindings": "1.0.0-alpha.128",
57
+ "@lunora/codegen": "1.0.0-alpha.337",
58
+ "@lunora/config": "1.0.0-alpha.387",
59
+ "@lunora/container": "1.0.0-alpha.88",
60
+ "@lunora/d1": "1.0.0-alpha.196",
61
+ "@lunora/errors": ">=1.0.0-alpha.51 <2.0.0-0",
62
+ "@lunora/mcp": "1.0.0-alpha.250",
63
+ "@lunora/runtime": "1.0.0-alpha.226",
64
+ "@lunora/seed": "1.0.0-alpha.244",
65
+ "@visulima/cerebro": "3.1.2",
66
+ "@visulima/error": "6.1.0",
67
+ "@visulima/fs": "6.0.29",
68
+ "@visulima/pail": "5.0.0",
69
+ "@visulima/path": "4.0.0",
70
+ "@visulima/spinner": "1.0.1",
71
+ "@visulima/tui": "4.0.29",
72
+ "@visulima/tui-kit": "1.0.32",
73
+ "adm-zip": "0.6.1",
74
+ "csv-parse": "7.0.3",
75
+ "giget": "3.3.1",
69
76
  "jsonc-parser": "^3.3.1",
70
- "magic-string": "^0.30.21",
71
- "react": "^19.2.7",
77
+ "magic-string": "^1.4.2",
78
+ "react": "^19.3.0",
72
79
  "react-reconciler": "^0.33.0",
80
+ "smol-toml": "1.9.0",
73
81
  "ts-morph": "^28.0.0"
74
82
  },
83
+ "peerDependencies": {
84
+ "mysql2": "*",
85
+ "pg": "*"
86
+ },
87
+ "peerDependenciesMeta": {
88
+ "mysql2": {
89
+ "optional": true
90
+ },
91
+ "pg": {
92
+ "optional": true
93
+ }
94
+ },
75
95
  "engines": {
76
96
  "node": "^22.15.0 || >=24.11.0"
77
97
  }
package/skills/README.md CHANGED
@@ -9,21 +9,40 @@ framework correctly. They ship inside `@lunora/cli` and are discovered by the
9
9
  Each skill is a `SKILL.md` with YAML frontmatter (`name`, `description`) in its
10
10
  own directory:
11
11
 
12
- | Skill | Use for |
13
- | -------------------------- | --------------------------------------------------------------------------- |
14
- | `lunora` | Router — start here, then switch to the matching skill below. |
15
- | `lunora-quickstart` | `lunora init` / adding Lunora to an app + first round-trip. |
16
- | `lunora-functions` | Core authoring rules — schema, validators, query/mutation/action, `ctx.db`. |
17
- | `lunora-realtime` | Client reactivity — live hooks, optimistic updates, `@lunora/db`. |
18
- | `lunora-setup-auth` | Authentication via `lunora registry add auth` (+ providers). |
19
- | `lunora-setup-mail` | Transactional email via `lunora registry add mail` — `sendEmail` actions. |
20
- | `lunora-setup-storage` | R2 file storage via `lunora registry add storage` — signed upload/download. |
21
- | `lunora-setup-scheduler` | Deferred (`ctx.scheduler`) + cron jobs (`lunora registry add crons`). |
22
- | `lunora-create-package` | Building a reusable registry item or `@lunora/*` package. |
23
- | `lunora-migration-helper` | Schema/data migrations, `.global()` D1 flow, the drift gate. |
24
- | `lunora-deploy` | Deploying to Cloudflare — wrangler bindings, secrets, the gate. |
25
- | `lunora-performance-audit` | Scans, indexes, OCC write conflicts, sharding/`.global()`. |
12
+ | Skill | Use for |
13
+ | -------------------------------- | ---------------------------------------------------------------------------- |
14
+ | `lunora` | Router — start here, then switch to the matching skill below. |
15
+ | `lunora-quickstart` | `lunora init` / adding Lunora to an app + first round-trip. |
16
+ | `lunora-functions` | Core authoring rules — schema, validators, query/mutation/action, `ctx.db`. |
17
+ | `lunora-realtime` | Client reactivity — live hooks, optimistic updates, `@lunora/db`. |
18
+ | `lunora-setup-auth` | Authentication via `lunora registry add auth` (+ providers). |
19
+ | `lunora-setup-mail` | Transactional email via `lunora registry add mail` — `sendEmail` actions. |
20
+ | `lunora-setup-storage` | R2 file storage via `lunora registry add storage` — signed upload/download. |
21
+ | `lunora-setup-scheduler` | Deferred (`ctx.scheduler`) + cron jobs (`lunora registry add crons`). |
22
+ | `lunora-setup-hyperdrive` | Existing Postgres/MySQL from an action — `ctx.sql`, non-reactive. |
23
+ | `lunora-setup-hyperdrive-global` | Postgres/MySQL as a reactive `.global()` backend; D1 → Hyperdrive migration. |
24
+ | `lunora-create-package` | Building a reusable registry item or `@lunora/*` package. |
25
+ | `lunora-migration-helper` | Schema/data migrations, `.global()` DDL flow, the drift gate. |
26
+ | `lunora-deploy` | Deploying to Cloudflare — wrangler bindings, secrets, the gate. |
27
+ | `lunora-performance-audit` | `lunora insights`, scans, indexes, OCC conflicts, sharding/`.global()`. |
28
+ | `lunora-cloudflare-audit` | Launch/bill audit: doctor + advisor signal, fan-out, cron, public-path cost. |
26
29
 
27
- These are mirrored into `.agents/skills/` and `.claude/skills/` (via symlinks)
28
- so agents working inside this repo pick them up directly. The source of truth
29
- lives here so the published `@lunora/cli` tarball carries them.
30
+ These are mirrored (via symlinks) into `.agents/skills/` and `.claude/skills/`,
31
+ so agents working inside this repo pick them up directly, and into
32
+ `plugins/lunora/skills/`, the payload of the Claude Code / Codex plugin. The
33
+ source of truth lives here so the published `@lunora/cli` tarball carries them —
34
+ `lunora rules install` copies this directory into a consumer project.
35
+
36
+ **Adding a skill?** Create it here, then mirror it through all three hops:
37
+
38
+ ```bash
39
+ ln -s ../../packages/cli/skills/<name> .agents/skills/<name>
40
+ ln -s ../../.agents/skills/<name> .claude/skills/<name>
41
+ ln -s ../../../packages/cli/skills/<name> plugins/lunora/skills/<name>
42
+ ```
43
+
44
+ `scripts/check-skill-mirrors.js` (run on every `pnpm install`) fails if a mirror
45
+ is missing, dangling, or has become a real directory — a copied directory keeps
46
+ resolving while it silently drifts from the source, so it is treated as an
47
+ error rather than a warning. Forget a hop and it prints the exact `ln -s` for
48
+ the one you missed, so `pnpm install` is the shortest path to the right command.
@@ -1,83 +1,176 @@
1
1
  ---
2
2
  name: lunora
3
- description: Routes general Lunora requests to the right project skill. Use when the user
4
- asks which Lunora skill to use, or gives an underspecified task for a Lunora
5
- app (a type-safe, real-time backend on Cloudflare Workers + Durable Objects
6
- with a Vite-first DX).
3
+ description: Routes general Lunora requests to the right Lunora skill and gives the
4
+ shared mental model (codegen loop, generated `api`/`internal` references,
5
+ review commands, add-on capabilities, the `@lunora/mcp` server). Use when the
6
+ user asks which Lunora skill to use, gives an underspecified task in a project
7
+ with a `lunora/` directory or `lunorash`/`@lunora/*` dependencies, asks to
8
+ review or audit Lunora code, asks about an add-on without its own skill
9
+ (queues, workflows, agents, AI, flags, payments, notifications), or wants to
10
+ expose a deployment to an AI agent over MCP.
7
11
  ---
8
12
 
9
13
  # Lunora
10
14
 
11
- Use this as the routing skill for Lunora work in this repo.
15
+ Lunora is a type-safe, real-time backend on Cloudflare Workers + Durable
16
+ Objects. Functions are chainable `query` / `mutation` / `action` builders; state
17
+ lives in one `ShardDO` per app by default (SQLite, OCC, hibernated WebSocket
18
+ subscriptions). `.shardBy(key)` partitions a table across many DOs and
19
+ `.global()` replicates it to D1 (or Postgres/MySQL over Hyperdrive) for
20
+ cross-region reads. Codegen produces the types the client and server share.
12
21
 
13
- Lunora exposes a Convex-style functional API (`defineSchema`, `query`,
14
- `mutation`, `action`) on top of Cloudflare Workers and Durable Objects. State
15
- lives in a per-app `ShardDO` (SQLite, OCC, hibernated WebSocket subscriptions)
16
- by default; `.shardBy(key)` partitions it across many DOs and `.global()`
17
- replicates a table to D1 for low-latency cross-region reads. A Vite plugin
18
- drives codegen and end-to-end type sync.
22
+ If a project-level `AGENTS.md` / `CLAUDE.md` exists, read it first; it overrides
23
+ these defaults. If a more specific Lunora skill matches, switch to it.
19
24
 
20
- If a more specific Lunora skill clearly matches the request, use that instead.
21
-
22
- ## Start Here
23
-
24
- Before writing or changing any `lunora/` code, make sure the generated types are
25
- current — they are the contract the client and server share.
25
+ ## The feedback loop
26
26
 
27
27
  ```bash
28
- lunora codegen
28
+ lunora codegen # regenerate lunora/_generated/ from lunora/schema.ts + function files
29
+ lunora verify # wrangler config + codegen dry-run + tsc --noEmit; writes nothing
29
30
  ```
30
31
 
31
- This regenerates `lunora/_generated/` (`api.ts`, `server.ts`, `dataModel.ts`,
32
- `shard.ts`, `openapi.ts`, …) from `lunora/schema.ts` and your function files. The
33
- output typechecks your schema and functions, so it doubles as the agent's main
34
- feedback loop after each edit. Commit `lunora/_generated/` — it is part of the
35
- source tree, not a build artifact to gitignore.
36
-
37
- If a project-level `AGENTS.md` / `CLAUDE.md` exists, read it first — it overrides
38
- these defaults.
32
+ `lunora codegen` writes `lunora/_generated/` (`api.ts`, `internal.ts`,
33
+ `server.ts`, `dataModel.ts`, `shard.ts`, `app.ts`, `openapi.ts`, …; it prints
34
+ the exact list) and reports schema advisories and platform diagnostics. It does
35
+ not run `tsc`, so use `lunora verify` as the check that the code you wrote is
36
+ done. Its `tsc --noEmit` step runs only when the project has a `tsconfig.json`
37
+ (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.
38
+ Scaffolded projects gitignore `lunora/_generated/`; never hand-edit it.
39
39
 
40
- ## Route to the Right Skill
41
-
42
- After codegen is green, use the most specific Lunora skill for the task:
40
+ ## Route to the right skill
43
41
 
44
42
  - New project, or adding Lunora to an existing app: `lunora-quickstart`
45
43
  - Writing or reviewing schema + functions (the core authoring rules):
46
44
  `lunora-functions`
47
- - Wiring live data into a client (hooks, optimistic updates): `lunora-realtime`
48
- - Authentication setup (email/password, OAuth, magic link, OTP):
49
- `lunora-setup-auth`
50
- - Wiring a prebuilt capability (mail, file storage, scheduled jobs, rate
51
- limiting, vectors, AI, containers, payments, MCP): install it with
52
- `lunora registry add <item>` (see `lunora registry list`). Capabilities with a
53
- dedicated skill: `lunora-setup-mail` (mail), `lunora-setup-storage` (R2 file
54
- storage), `lunora-setup-scheduler` (deferred `ctx.scheduler` + cron jobs). For
55
- the rest, read the item's README after installing.
56
- - Building a reusable capability — a registry item or an `@lunora/*` package:
45
+ - Wiring live data into a client (hooks, optimistic updates), or choosing
46
+ between a live query, stream, live topic, whisper, room and socket (including
47
+ pushing a server event to the browser): `lunora-realtime`
48
+ - Authentication (email/password, OAuth, magic link, OTP): `lunora-setup-auth`
49
+ - Transactional email: `lunora-setup-mail`
50
+ - R2 file storage (signed upload/download): `lunora-setup-storage`
51
+ - Deferred work (`ctx.scheduler`) and cron jobs: `lunora-setup-scheduler`
52
+ - Querying an existing Postgres/MySQL database from an action (`ctx.sql`,
53
+ non-reactive): `lunora-setup-hyperdrive`
54
+ - Postgres/MySQL as a reactive `.global()` backend, or migrating a D1
55
+ `.global()` dataset onto it: `lunora-setup-hyperdrive-global`
56
+ - Building a reusable capability (registry item or `@lunora/*` package):
57
57
  `lunora-create-package`
58
- - Planning or running a schema/data migration: `lunora-migration-helper`
59
- - Deploying to Cloudflare (wrangler, bindings, secrets, the drift gate):
60
- `lunora-deploy`
61
- - Investigating performance, scan, or write-conflict issues:
62
- `lunora-performance-audit`
63
-
64
- If one of those clearly matches the user's goal, switch to it instead of staying
65
- in this skill.
66
-
67
- ## Core Mental Model
68
-
69
- - **Functions** live in `lunora/*.ts` and are one of `query` (reactive read),
70
- `mutation` (transactional write), or `action` (side effects / `fetch` / no
71
- direct db). `internalQuery` / `internalMutation` / `internalAction` are the
72
- non-public variants.
73
- - **Schema** lives in `lunora/schema.ts` via `defineSchema` + `defineTable`,
74
- with validators from `v.*` (re-exported by `@lunora/server`).
75
- - **Reads go through indexes.** Prefer `ctx.db.query("t").withIndex(...)` over
76
- `.filter(...)`; declare the index with `.index("by_x", ["x"])`.
77
- - **Clients** subscribe over WebSocket. `useQuery`/`useMutation` (React, Vue,
78
- Solid, Svelte) re-render the moment a mutation changes the queried rows.
79
-
80
- ## When Not to Use
81
-
82
- - The user has already named a more specific Lunora workflow.
83
- - Another Lunora skill obviously fits the request better.
58
+ - Schema/data migrations: `lunora-migration-helper`
59
+ - Deploying (wrangler, bindings, secrets, the drift gate): `lunora-deploy`
60
+ - Slow queries, scans, write conflicts: `lunora-performance-audit`
61
+ - Launch readiness, surprise Cloudflare bills, public routes reaching paid products: `lunora-cloudflare-audit`
62
+
63
+ ### Reviewing existing `lunora/` code
64
+
65
+ There is no review skill because two commands do the mechanical part. Run them
66
+ before reading code by hand:
67
+
68
+ ```bash
69
+ lunora verify # does it build and typecheck
70
+ lunora advisor # static + runtime lint set, scored per procedure
71
+ ```
72
+
73
+ `lunora advisor` covers RLS coverage, ownership and identity checks, fail-open
74
+ guards, input validators, unindexed reads, non-deterministic calls in queries,
75
+ SQL interpolation and leaked secrets. It prints each finding's lint id and
76
+ category, so read its output rather than guessing rule names. `--entry
77
+ <file>#<export>` inspects one procedure; `--min-score` / `--baseline` gate CI.
78
+ Once it is clean, a by-hand pass against `lunora-functions` is worth the tokens.
79
+
80
+ ## Core mental model
81
+
82
+ - Functions live in `lunora/**/*.ts` and import their builders from the
83
+ generated server module: `query` (reactive read), `mutation` (transactional
84
+ write), `action` (side effects, `fetch`, no direct db). `internalQuery` /
85
+ `internalMutation` / `internalAction` are not callable from clients.
86
+ - Schema lives in `lunora/schema.ts` (`defineSchema` + `defineTable`, validators
87
+ from `v.*`), imported from `lunorash/server` or `@lunora/server`.
88
+ - Function references mirror the folder tree: `lunora/billing/invoices.ts`
89
+ exporting `create` is `api.billing.invoices.create`. Public references come
90
+ from `_generated/api`, internal ones from `_generated/internal` (kept separate
91
+ so client bundles never carry internal names). A reference is a plain
92
+ `{ __lunoraRef: "billing_invoices:create" }` object; there is no `anyApi`
93
+ proxy.
94
+ - Reads go through indexes: declare `.index("by_x", ["x"])` and query with
95
+ `ctx.db.query("t").withIndex("by_x", (q) => q.eq("x", value))`, not
96
+ `.filter(...)`.
97
+ - Clients subscribe over WebSocket; `useQuery` re-renders when a mutation
98
+ changes the queried rows. Subscriptions run under the socket's verified
99
+ identity, so `rls()` and `ctx.auth` apply to live updates too.
100
+
101
+ ## Capabilities without a dedicated skill
102
+
103
+ Most add-ons install as a registry item: `lunora add <feature>` (or
104
+ `lunora registry add <item>`) scaffolds the `lunora/` glue, wrangler bindings
105
+ and `.dev.vars` entries, then prints post-install steps. Browse with
106
+ `lunora registry list`, preview with `lunora registry view <item>`. Read the
107
+ installed item's README and the package's `docs/` for the API.
108
+
109
+ | Goal | Install / package |
110
+ | --------------------------------------------- | ---------------------------------------------------------- |
111
+ | Background jobs on Cloudflare Queues | `lunora add queue` → `@lunora/queue` (`ctx.queues`) |
112
+ | Durable multi-step workflows | `lunora add workflow` → `@lunora/workflow` (`ctx.runStep`) |
113
+ | Durable AI agents (tool loops, HITL, memory) | `@lunora/agent` (`defineAgent`) |
114
+ | Workers AI / RAG | `lunora add ai` → `@lunora/ai` (`ctx.ai`, `defineRag`) |
115
+ | Feature flags (OpenFeature) | `lunora add flags` → `@lunora/flags` (`ctx.flags`) |
116
+ | Payments (Stripe / Polar) | `lunora add payment` → `@lunora/payment` |
117
+ | Agent payments (x402) | `@lunora/x402` |
118
+ | Push / in-app / webhook notifications | `@lunora/notify` (`ctx.notify`, `ctx.push`) |
119
+ | Rate limiting | `lunora add ratelimit` → `@lunora/ratelimit` |
120
+ | Headless browser (action-only) | `lunora add browser` → `@lunora/browser` (`ctx.browser`) |
121
+ | Cloudflare Containers | `@lunora/container` (`defineContainer`, `ctx.containers`) |
122
+ | Presence / who's-here | `lunora add presence` |
123
+ | Cloudflare Access (Zero Trust) identity | `lunora add cloudflare-access` |
124
+ | Backup / restore | `lunora add backup` |
125
+ | Testing (in-memory harness, agent doubles) | `@lunora/testing` (`lunoraTest`) |
126
+ | Deterministic seed data | `@lunora/seed` + `lunora seed` |
127
+ | Local-first replica / offline mirror | `@lunora/replica` |
128
+ | Exposing the deployment to AI agents over MCP | `@lunora/mcp` (below) |
129
+
130
+ Inside the Lunora monorepo, `vis generate lunora-<kind>` scaffolds code:
131
+ `query`, `mutation`, `action`, `http-route`, `table`, `cron`, `container`,
132
+ `workflow`, `queue`, `step`, `agent`, `package`, plus the no-name singletons
133
+ `flags`, `auth-do` and `collections` (`vis generate --list` for the set). Pass
134
+ names as `--name=value`: vis parses `--name foo` as `--name=true` plus a stray
135
+ positional.
136
+
137
+ ## Exposing a deployment over MCP
138
+
139
+ Two entry points:
140
+
141
+ - `lunora mcp install` wires an editor (Claude Code, Cursor, …) to the hosted
142
+ docs server plus this project's dev server; `lunora mcp serve` is the stdio
143
+ server those entries spawn (`--allow-writes`, `--token` opt into more).
144
+ - The `lunora-mcp` binary from `@lunora/mcp` fronts a deployed Worker. It needs
145
+ `LUNORA_URL` and `LUNORA_ADMIN_TOKEN`. Every tool reads admin-gated routes, so
146
+ the token cannot be scoped down; safety comes from the gates below.
147
+
148
+ Always advertised: `lunora_list_functions`, `lunora_list_tables` (`.global()`
149
+ tables with row counts), `lunora_get_function_schema`, `lunora_run_query`
150
+ (read-only), and `lunora_explain_error`, which explains an error `code` or
151
+ message from the catalog compiled into `@lunora/errors` and needs no
152
+ deployment. Use it before guessing what a Lunora error means.
153
+
154
+ Everything else sits behind an env gate that both hides the tool and refuses it
155
+ at dispatch. All default to off; enable one only when the answer to "may a model
156
+ see this?" is yes.
157
+
158
+ - `LUNORA_MCP_ALLOW_WRITES` → `lunora_run_mutation`, `lunora_run_action`.
159
+ - `LUNORA_MCP_ALLOW_OBSERVABILITY` → the five `lunora_get_*` tools (logs,
160
+ issues, advisories, query insights, migration status). Read-only, but they
161
+ return production log lines and error messages, which then reach the model
162
+ provider.
163
+ - `LUNORA_MCP_ALLOW_DATA_READS` → `lunora_find_related`. It returns raw rows
164
+ read through the admin writer, with RLS and column masks bypassed. It is kept
165
+ separate from observability so enabling logs never also hands over rows.
166
+ - `LUNORA_MCP_ALLOW_AGENTS` (with `LUNORA_MCP_AGENTS="name:description;…"`) →
167
+ the `agent_<name>` tools and `lunora_agent_status`.
168
+
169
+ Writes are a two-step handshake. The first `lunora_run_mutation` /
170
+ `lunora_run_action` call does not execute; it returns `status:
171
+ "action_required"` with `proposedAction`, an `actionDigest`, and an `expiresAt`
172
+ ten minutes out. The client shows that to a human, then repeats the call with
173
+ identical `functionPath`, `args` and `shardKey` plus `confirmed: true` and the
174
+ digest. Any edit needs a fresh proposal, and an expired digest is refused. The
175
+ digest proves the write that runs is the one proposed; it does not prove a human
176
+ saw it, so enabling writes means trusting the client to ask.
@@ -0,0 +1,163 @@
1
+ ---
2
+ name: lunora-cloudflare-audit
3
+ description: Audits a Lunora app for Cloudflare cost and reliability risk before launch or after a surprise bill. Chains the read-only `lunora doctor`, `lunora advisor` and `lunora insights` signals, then traces what they cannot see — retry and fan-out loops, cron cadence, public routes that reach a metered product (Workers AI, Browser, Containers, Vectorize, R2, D1), preview environments with paid bindings, and missing usage alerts (`lunora cloudflare alerts`). Reports each finding with the billing meter it moves, the smallest fix, and how to verify. Use when the user asks to "audit before launch", "what could blow up our Cloudflare bill", "why did the bill spike", "is this safe to put on the public internet", or "review our cron / queue / AI / workflow cost". For a slow query or OCC conflict use `lunora-performance-audit`; for a failing deploy use `lunora-deploy`.
4
+ ---
5
+
6
+ # Lunora Cloudflare Audit
7
+
8
+ Diagnose from evidence: name the mechanism that spends money or breaks, point at
9
+ the line, and prescribe the smallest fix. Lunora already ships three scanners,
10
+ so this skill is mostly the order to run them in and the judgment they lack.
11
+
12
+ Recurring work (crons, queue consumers, workflows) has no public entry point, so
13
+ check it before concluding costs are modest. If nothing metered is reachable
14
+ from outside, traffic is modest and nothing recurs, say so and stop. Do not
15
+ manufacture findings to fill a report.
16
+
17
+ ## Ground rules
18
+
19
+ - **Evidence first.** Every finding needs a file and line (or command output)
20
+ and a concrete Cloudflare mechanism. "Could be expensive" is not a finding.
21
+ - **Prices and limits come from current docs, never memory.** Name the meter
22
+ (DO requests, DO duration, SQLite rows read, D1 rows written, R2 Class A ops,
23
+ Workers AI neurons, Browser duration, Vectorize queried dimensions, …) and
24
+ fetch the current Cloudflare pricing/limits page before quoting a unit, an
25
+ allowance or a cap. If you could not fetch it, say the claim is unverified.
26
+ - **Do not infer account state from the repo.** Alerts, WAF, cache rules, Access
27
+ and billing live in the dashboard. Without an export or screenshot, list that
28
+ scope as _not inspected_.
29
+ - **Confirmed vs suspected.** A scanner hit is a lead. Confirm it against the
30
+ code path before it becomes a finding.
31
+ - **Read-only.** Never run `lunora doctor --fix`, `deploy`, or `cloudflare
32
+ alerts setup` from an audit. Show the change and get explicit approval first.
33
+
34
+ ## Workflow
35
+
36
+ 1. **Scope.** Which wrangler environments (read each `env.<name>` block in
37
+ `wrangler.jsonc` or `wrangler.json`, the two files Lunora reads; the scan
38
+ commands below take no `--env`), and which paths
39
+ are hot: public queries/mutations, HTTP actions, crons, queue consumers,
40
+ workflows.
41
+ 2. **Preflight signal** (read-only; never pass `--fix`):
42
+
43
+ ```bash
44
+ lunora doctor --format json # wrangler bindings, paid products, sampling, DO class budget
45
+ lunora advisor --format json --no-write # static lints, scored per procedure
46
+ ```
47
+
48
+ `lunora doctor` reports `paid-product-bound` (a metered binding is declared:
49
+ Workers AI, Browser, Images), `observability-full-sampling`,
50
+ `r2-lifecycle-unset`, `cpu-limit-missing` and `do-class-budget-*`.
51
+ `lunora advisor` lint ids are listed below.
52
+
53
+ 3. **Runtime signal, only if the user points you at a worker that has served
54
+ traffic.** `lunora insights` (local by default). Production needs `--prod` and
55
+ the user's admin token, plus `--url …` when the checkout has no production
56
+ link; do not go looking for a token.
57
+ 4. **Account signal.** `lunora cloudflare alerts` (status) reads last month's
58
+ usage and which products have a usage alert. Needs Cloudflare credentials;
59
+ ask first. Budget alerts have no API, so they are always _not inspected_.
60
+ 5. **Trace the gaps** in the next section by reading code.
61
+ 6. **Report** in the format at the end.
62
+
63
+ ## What each signal already covers
64
+
65
+ Check this table before reading code by hand. Only the last column is yours.
66
+
67
+ | Risk | Already flagged by | Still read by hand |
68
+ | -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
69
+ | Full-table reads re-sent to every subscriber | `unbounded_collect`, `unbounded_collect_on_open_range`, `filter_without_index`, `count_without_index`, `filter_after_index_scan` | Whether the table will actually grow; `.global()` D1 cost |
70
+ | Runaway loops and re-arming schedules | `unbounded_loop`, `self_reschedule_without_progress`, `dispatch_cycle`, `queue_dispatch_cycle` | Loops spanning a Queue → Worker → DO hop the graph cannot see |
71
+ | Sweeps that re-read the same rows | `unbounded_collect_on_open_range` | — |
72
+ | Cron cadence | `cron_every_minute` (sees `cronJobs()` registrations only) | Every-minute schedules the lint cannot see: `createWorker({ crons })` handlers and `backupCron` in the worker entry; what any tick does: rows read, calls made per fire |
73
+ | Queue retries losing messages | `queue_without_dlq` | Retry delay and `maxRetries` against the downstream's own rate limit |
74
+ | Hot shard / wide fan-out | `privileged_fanout_from_public_procedure`; `hot_shard` and `fan_out_breadth` are runtime lints, shown in the Studio Advisors tab against a live worker, not by `lunora advisor` | Shard traffic on a live worker (Studio Advisors); singleton shard keys (`"global"`, an env var) on `.shardBy()` |
75
+ | Unbounded AI use | `ai_unbounded_generation_public`, `ai_run_without_logging` | Output and usage bounds on every raw `ctx.ai.run()` (`ai_raw_run_escape_hatch` only checks where the model id comes from, so a fixed-model call escapes it); a `ctx.ai` call inside a retry or `Promise.all` over user-sized input |
76
+ | Browser / Containers left running | `browser_user_url_without_allowlist`, `container_long_sleep_after`, `container_oversized_instance` (the container lints read configuration, not lifecycle) | A browser session opened without a close path in an `action`; named container starts that are never stopped or destroyed once their work finishes, within the configured idle window |
77
+ | Public mutations with no throttle | `public_mutation_without_ratelimit`, `ratelimit_default_memory_store`, `ratelimit_key_spoofable_or_global` | Public _reads_ that reach a metered product |
78
+ | Upload size and type | `storage_upload_without_max_size`, `storage_upload_without_content_type_allowlist` | Egress and lifecycle on the bucket (`r2-lifecycle-unset`) |
79
+ | Secrets in the repo | `hardcoded_secret`, `plaintext_secret_in_wrangler_vars` | — |
80
+ | Log volume | `observability-full-sampling` (doctor) | Retention and plan |
81
+ | Handled by construction | Codegen reconciles bindings, DO migrations and the compatibility date; `ShardDO` uses the WebSocket Hibernation API | Nothing, unless `wrangler.jsonc` was hand-edited |
82
+
83
+ ## Gaps to trace by hand
84
+
85
+ For each, find the call site, then count the billed units per user action.
86
+
87
+ - **Fan-out without a cap.** `Promise.all(items.map(...))` or a loop that calls
88
+ `ctx.db`, `ctx.runMutation`, `ctx.scheduler.runAfter`, a queue send
89
+ (`ctx.queues.<name>.send` / `sendBatch`) or `ctx.ai` once per element of user-sized input. Cap the batch or hand the list
90
+ to a Queue/workpool with a concurrency limit.
91
+ - **Retry storms.** A retry (hand-rolled, queue `maxRetries`, workflow step
92
+ retry) around a call that is itself billed or rate-limited, with no backoff or
93
+ circuit breaker. A workflow step that retries a paid `ctx.ai` call bills each
94
+ attempt.
95
+ - **Webhooks with side effects and no dedupe.** An `httpAction` for a provider
96
+ webhook that writes or sends mail without keying on the event id. Providers
97
+ redeliver. (Payment webhooks have their own lints; this is every other
98
+ provider.)
99
+ - **Preview / staging with paid bindings.** An `env.<name>` block that binds
100
+ Workers AI, Browser, Containers or the production R2 bucket and is reachable
101
+ without auth.
102
+ - **Enumeration on hot paths.** Storage enumeration through
103
+ `ctx.db.system.query("_storage")` or a Vectorize query (`ctx.vectors.query`)
104
+ inside a `query` that every subscriber re-runs. (`ctx.storage.list` is
105
+ action-only.)
106
+ - **Third-party origins.** An action that fetches a public serverless URL on
107
+ every request instead of caching, queueing, or using a service binding.
108
+
109
+ ## Public-path cost gate
110
+
111
+ Run this whenever an anonymous caller (an unauthenticated `query`, a public
112
+ `httpAction`, a crawlable GET) can reach a metered product. The caller does not
113
+ have to be a crawler: any script can repeat the request.
114
+
115
+ 1. List the public entry points (`lunora advisor` flags `http_action_missing_auth_guard`;
116
+ read `lunora/http.ts` and the public builders). Also read the custom routes
117
+ registered on the app in the worker entry (`defineApp()`): they run outside
118
+ the HTTP-action router, so no lint covers their use of paid `env` bindings.
119
+ 2. For each, trace arbitrary, missing and malformed inputs. Where is the first
120
+ rejection, and does it happen **before** the metered call?
121
+ 3. State the per-hit unit (one DO request? N rows read? one AI inference? one
122
+ browser session?) and the exposure: `anonymous requests × units per request`.
123
+ Only count a cache if it sits in front of the work and its misses, expiry and
124
+ concurrent fills are also bounded.
125
+ 4. Verdict: **BLOCK** when evidence shows repeated anonymous requests run
126
+ corpus-scale or high-unit work before validation; **CONDITIONAL** when the
127
+ shape may exist but a query plan, measurement or config is missing (never
128
+ assume safe); **PASS** only when the expensive path is prevented before it
129
+ executes.
130
+
131
+ A rate limit or alert that fires _after_ the metered work started is defense in
132
+ depth, not a fix. The fix is to reject early, bound the work, or move it behind
133
+ auth.
134
+
135
+ ## Report
136
+
137
+ Lead with scope, then one card per finding, most severe first.
138
+
139
+ ```markdown
140
+ ## Lunora Cloudflare audit
141
+
142
+ Scope inspected: <paths, env, commands run>
143
+ Scope not inspected: <dashboard / alerts / billing / prod>
144
+ Docs refreshed: <URLs fetched, or "none — claims marked unverified">
145
+
146
+ ### <critical|high|medium|low> — <title>
147
+
148
+ - Evidence: <file:line or command output; lint id / doctor code if one fired>
149
+ - Mechanism: <the billed meter or failure, in Cloudflare terms>
150
+ - Fix: <smallest change; the better primitive if the primitive is wrong>
151
+ - Cost / trade-off: <meter moved, effort, downside>
152
+ - Verify: <command or check that proves it>
153
+ - Confidence: <high|medium|low>
154
+ ```
155
+
156
+ Severity: **critical** = exposed secret, public data exposure or an unbounded
157
+ billed loop; **high** = anonymous traffic reaches high-unit work, or a retry
158
+ path bills each attempt; **medium** = a cost or reliability gap that grows with
159
+ usage; **low** = hygiene. If nothing is supported, write `No confirmed findings.`
160
+ and list what was not inspected.
161
+
162
+ End with the proposed next steps, and offer `lunora cloudflare alerts setup
163
+ --email <address> --dry-run` if no usage alert exists. Do not run it unasked.