anhedral 0.10.0 → 0.11.0

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 (120) hide show
  1. package/README.md +78 -39
  2. package/assets/{anhedral-cli-init-technical.svg → anhedral-stack-technical.svg} +6 -6
  3. package/assets/{anhedral-cli-init.svg → anhedral-stack.svg} +7 -7
  4. package/assets/security/audit-deps.mjs +6 -1
  5. package/dist/bin.js +271 -154
  6. package/dist/bounded-file.d.ts +1 -0
  7. package/dist/bounded-file.js +1 -0
  8. package/dist/capabilities.d.ts +4 -4
  9. package/dist/capabilities.js +1 -1
  10. package/dist/capability-contract.d.ts +47 -0
  11. package/dist/capability-contract.js +136 -0
  12. package/dist/cli-arguments.d.ts +16 -0
  13. package/dist/cli-arguments.js +115 -0
  14. package/dist/cli-output.d.ts +8 -0
  15. package/dist/cli-output.js +211 -0
  16. package/dist/cloudflare.d.ts +7 -2
  17. package/dist/cloudflare.js +166 -36
  18. package/dist/command-schema.d.ts +21 -0
  19. package/dist/command-schema.js +374 -0
  20. package/dist/delivery.d.ts +2 -2
  21. package/dist/delivery.js +21 -4
  22. package/dist/dependencies.d.ts +32 -0
  23. package/dist/dependencies.js +47 -1
  24. package/dist/index.d.ts +23 -1
  25. package/dist/index.js +15 -1
  26. package/dist/inspector-model.d.ts +115 -0
  27. package/dist/inspector-model.js +1 -0
  28. package/dist/lifecycle-model.d.ts +15 -0
  29. package/dist/lifecycle-model.js +20 -0
  30. package/dist/lifecycle.d.ts +12 -0
  31. package/dist/lifecycle.js +92 -0
  32. package/dist/mobile-delivery.d.ts +100 -0
  33. package/dist/mobile-delivery.js +319 -0
  34. package/dist/platforms/desktop.js +4 -5
  35. package/dist/platforms/extension.js +9 -73
  36. package/dist/platforms/mobile.js +14 -52
  37. package/dist/project-inspector.d.ts +4 -0
  38. package/dist/project-inspector.js +646 -0
  39. package/dist/project-state.d.ts +17 -0
  40. package/dist/project-state.js +239 -11
  41. package/dist/provider-auth.d.ts +41 -0
  42. package/dist/provider-auth.js +210 -0
  43. package/dist/providers/adapters.d.ts +17 -0
  44. package/dist/providers/adapters.js +269 -0
  45. package/dist/providers/api.d.ts +28 -0
  46. package/dist/providers/api.js +252 -0
  47. package/dist/providers/config.d.ts +15 -0
  48. package/dist/providers/config.js +184 -0
  49. package/dist/providers/definitions.d.ts +318 -0
  50. package/dist/providers/definitions.js +127 -0
  51. package/dist/providers/provision.d.ts +24 -0
  52. package/dist/providers/provision.js +76 -0
  53. package/dist/providers/receipts.d.ts +20 -0
  54. package/dist/providers/receipts.js +66 -0
  55. package/dist/providers/recovery-verification.d.ts +7 -0
  56. package/dist/providers/recovery-verification.js +112 -0
  57. package/dist/providers/recovery.d.ts +30 -0
  58. package/dist/providers/recovery.js +34 -0
  59. package/dist/providers/runtime.d.ts +10 -0
  60. package/dist/providers/runtime.js +231 -0
  61. package/dist/providers/schema.d.ts +28 -0
  62. package/dist/providers/schema.js +49 -0
  63. package/dist/providers/state.d.ts +56 -0
  64. package/dist/providers/state.js +329 -0
  65. package/dist/provision.d.ts +1 -1
  66. package/dist/provision.js +61 -3
  67. package/dist/readiness.js +11 -3
  68. package/dist/registry/add.d.ts +6 -0
  69. package/dist/registry/add.js +35 -4
  70. package/dist/registry/commands.d.ts +3 -2
  71. package/dist/registry/commands.js +53 -14
  72. package/dist/registry/options.js +3 -2
  73. package/dist/registry/preview.d.ts +11 -0
  74. package/dist/registry/preview.js +91 -0
  75. package/dist/registry/receipt.js +1 -0
  76. package/dist/registry/resolve.js +19 -13
  77. package/dist/registry/types.d.ts +1 -0
  78. package/dist/registry/usage.d.ts +10 -0
  79. package/dist/registry/usage.js +22 -37
  80. package/dist/render.d.ts +2 -0
  81. package/dist/render.js +10 -0
  82. package/dist/scaffold/api.js +20 -3
  83. package/dist/scaffold/auth.js +68 -28
  84. package/dist/scaffold/bootstrap.d.ts +2 -0
  85. package/dist/scaffold/bootstrap.js +9 -0
  86. package/dist/scaffold/database.js +12 -6
  87. package/dist/scaffold/documentation.js +11 -8
  88. package/dist/scaffold/environment.d.ts +2 -0
  89. package/dist/scaffold/environment.js +18 -1
  90. package/dist/scaffold/files.d.ts +1 -0
  91. package/dist/scaffold/files.js +15 -4
  92. package/dist/scaffold/services.js +37 -13
  93. package/dist/scaffold/stack-documentation.d.ts +3 -0
  94. package/dist/scaffold/stack-documentation.js +52 -0
  95. package/dist/scaffold/styling.d.ts +3 -0
  96. package/dist/scaffold/styling.js +84 -0
  97. package/dist/scaffold/web.js +31 -11
  98. package/dist/scaffold/workspace.js +49 -22
  99. package/dist/ship.d.ts +320 -0
  100. package/dist/ship.js +211 -0
  101. package/dist/stack-registry.d.ts +2 -2
  102. package/dist/stack-registry.js +44 -11
  103. package/dist/standard.d.ts +2 -1
  104. package/dist/standard.js +13 -12
  105. package/dist/vercel.d.ts +3 -3
  106. package/dist/vercel.js +27 -1
  107. package/dist/version.d.ts +1 -1
  108. package/dist/version.js +1 -1
  109. package/dist/vinext.js +9 -18
  110. package/dist/workspace-graph.js +3 -2
  111. package/docs/application-stack-standard.md +51 -80
  112. package/docs/cli-design.md +67 -0
  113. package/docs/delivery-workflow.md +29 -15
  114. package/docs/plugin-ui.md +61 -0
  115. package/docs/provider-configuration.md +117 -0
  116. package/package.json +16 -12
  117. package/dist/managed-hosting.d.ts +0 -49
  118. package/dist/managed-hosting.js +0 -373
  119. package/docs/managed-hosting-architecture.md +0 -87
  120. package/docs/sponsorships.md +0 -25
package/README.md CHANGED
@@ -1,12 +1,14 @@
1
- # Anhedral Init
1
+ # Anhedral
2
2
 
3
- [![CI](https://github.com/anhedral/anhedral-init/actions/workflows/ci.yml/badge.svg)](https://github.com/anhedral/anhedral-init/actions/workflows/ci.yml)
3
+ [![CI](https://github.com/anhedral/anhedral-cli/actions/workflows/ci.yml/badge.svg)](https://github.com/anhedral/anhedral-cli/actions/workflows/ci.yml)
4
4
  [![npm](https://img.shields.io/npm/v/anhedral)](https://www.npmjs.com/package/anhedral)
5
5
  [![License: Apache-2.0](https://img.shields.io/badge/license-Apache--2.0-blue)](LICENSE)
6
6
 
7
+ Anhedral scaffolds source code, guides authentication, then provisions, configures, and deploys the supported stack automatically wherever provider APIs allow. Application features, design, custom integrations and business workflows remain development work; Anhedral offers development and ongoing maintenance services for businesses that want that work delivered for them.
8
+
7
9
  1. **Describe the app** — Anhedral clarifies its purpose, users and essential features.
8
10
  2. **Choose the architecture** — Explain the stack decisions; default web apps use Vinext on Cloudflare Workers.
9
- 3. **Set up ownership** — Choose customer-account or managed hosting; set up only the accounts that profile requires.
11
+ 3. **Set up ownership** — Use your own source repository and provider accounts; configure only the selected services.
10
12
  4. **Build the app** — Implement the requested features, data, authentication and integrations.
11
13
  5. **Check and ship** — Run checks, deploy, open the real URL and verify the live user flows.
12
14
  6. **Maintain it** — Keep architecture, deployment history and verification visible; update or roll back as needed.
@@ -17,50 +19,84 @@ Anhedral initializes a Vinext, React, TypeScript, Tailwind, shadcn/ui and Lucide
17
19
 
18
20
  ## Try it
19
21
 
20
- Install the plugin and select **Try now**. Use the CLI version pinned by its bundled registry. A local rewrite or ZIP does not update an installed plugin; unpublished development versions require their matching local CLI and skill.
22
+ Use `anhedral help <command>` for focused options and examples. Human output shows concise outcomes and next steps; `--json` preserves the complete result for scripts. `--cwd` / `-c` selects a project, and `--quiet` / `-q` suppresses human output while retaining errors and JSON. [CLI behavior and design](docs/cli-design.md) explains the comparison with shadcn, GitHub CLI and uv, and the source preview contract.
23
+
24
+ Use the CLI independently, or install the optional Codex plugin:
25
+
26
+ ```sh
27
+ codex plugin marketplace add anhedral/anhedral-cli
28
+ codex plugin add anhedral@anhedral
29
+ ```
30
+
31
+ Select **Try now** in Codex. The plugin uses the CLI version pinned by its bundled registry. A local rebuild does not update an installed plugin; development versions require their matching local CLI and plugin.
21
32
 
22
33
  Starter prompt:
23
34
 
24
- > Create a new app with [@Anhedral](plugin://anhedral@anhedral). Start by asking me what it should do.
35
+ > Create a new project using [@Anhedral](plugin://anhedral@anhedral). Start by explaining the stack and asking me what it should be about.
25
36
 
26
37
  Codex asks about the app, explains the stack, implements the product, configures selected services, and delivers into your authorized accounts. The CLI commands below are also available independently.
27
38
 
28
- Use Node.js 22.13+ and pnpm 10.34.5 for generated workspaces. Generate on Linux, macOS, or WSL.
39
+ Use Node.js 22.13+ on the 22.x line or Node.js 24+, with pnpm 10.34.5, for generated workspaces. Generate on Linux, macOS, or WSL.
29
40
 
30
41
  ```sh
31
42
  # Inspect a plan first: no project files or cloud resources are created
32
- pnpm dlx anhedral@latest new my-app --dry-run --json
43
+ pnpm dlx anhedral@latest init my-app --dry-run --json
33
44
 
34
45
  # Generate the default Vinext/Workers workspace
35
- pnpm dlx anhedral@latest new my-app
46
+ pnpm dlx anhedral@latest init my-app
36
47
  cd my-app
37
48
  pnpm dev
38
49
  ```
39
50
 
40
51
  The first command downloads the CLI through pnpm; the CLI's dry run performs no writes or network calls. Generation needs network access for the official shadcn bootstrap. The default selects Vinext on Cloudflare; add capabilities only when needed. Follow the generated README for provider setup, Worker builds, and preview deployment.
41
52
 
42
- **If Anhedral is useful to you, [star the repository](https://github.com/anhedral/anhedral-init) to help other developers discover it.**
53
+ **If Anhedral is useful to you, [star the repository](https://github.com/anhedral/anhedral-cli) to help other developers discover it.**
43
54
 
44
- ## Sponsors
55
+ ## Initialize, authenticate and deliver
45
56
 
46
- ### Help keep Anhedral Init free and open source
57
+ Install the CLI with `npm install --global anhedral`, or run these commands through `pnpm dlx anhedral@latest`.
58
+
59
+ ```sh
60
+ anhedral init my-app --hono --d1 --better-auth --r2
61
+ anhedral auth my-app
62
+ anhedral auth my-app --status --json
63
+ anhedral ship my-app --dry-run --json
64
+ anhedral ship my-app --json
65
+ ```
47
66
 
48
- Anhedral Init is free and open source. Sponsorship helps fund ongoing development, maintenance, and documentation so more developers can build and deliver applications they own.
67
+ `init` accepts a destination directory or initializes the current directory. Authentication requirements derive from the selected stack; Cloudflare requirements are deduplicated. Interactive authentication can open the supported Cloudflare, Vercel and Expo login flows. API-backed checks use credentials supplied through the environment; they do not read or print application secret files. Missing access and insufficient permissions remain explicit. A verified session or API endpoint does not establish every provisioning permission, the intended account connection or configured application secrets. Use the supported `connect` workflow to select hosting account, project and audience before delivery.
49
68
 
50
- **[♡ Become a sponsor](docs/sponsorships.md)**
69
+ `init` writes a nonsecret `anhedral.providers.json` for the selected integrations. Complete its account/resource choices before shipping. Adapters provision and configure Neon, Clerk, Resend, Stripe webhooks, RevenueCat apps, Sentry, PostHog and OpenAI project/service-account resources. Management access, provider eligibility and owner prerequisites remain explicit. See [provider configuration](docs/provider-configuration.md) for supported operations, credentials, runtime configuration, recovery and verification limits.
70
+
71
+ Cloudflare `ship` runs checks before reconciling D1, R2, KV, Queue and private Basin stream resources. If provisioning changes tracked binding IDs, it stops before deployment: review and commit the configuration, then repeat `ship`. It never commits source automatically. Runtime credentials accompany the checked code deployment; Vercel receives the selected web runtime's production variables. In mixed projects default `ship` runs Cloudflare services before the Vercel web target; use `--provider cloudflare` or `--provider vercel` for an explicit target. Partial delivery is reported without an automatic cross-provider rollback. The operation journal records diagnostics; retries recheck prerequisites and use resource receipts rather than skipping recorded phases. Pending provider setup stops deployment. Authentication alone does not establish completed setup.
72
+
73
+ For an Expo project with an explicit owner/slug in `app.json` and the required signing configuration:
74
+
75
+ ```sh
76
+ anhedral ship my-app --platform android --profile preview --json
77
+ anhedral ship my-app --platform ios --profile simulator --json
78
+ ```
79
+
80
+ EAS project creation/linking stops for review and a source commit. Queued builds return their build-page link; repeat the same command to inspect the existing build without submitting another. Completed builds return their ID, artifact type and download URL after checking project and source identity. Android preview produces an APK; production normally produces an AAB. The simulator profile produces an iOS Simulator artifact, not a TestFlight upload. Production iOS builds require Apple signing credentials. No store submission is performed. Build success does not verify the app's feature behavior.
81
+
82
+ Private provisioning receipts and credentials stay in ignored `.anhedral/` state. Inspect them with `anhedral integrations status my-app --json`; its output is redacted and read-only. `integrations recover` verifies an explicitly selected pending resource and writes only its local completion receipt. Legacy entries without descriptors, changed configuration, and missing one-time credentials remain blocked. See [provider configuration](docs/provider-configuration.md) before attempting recovery or moving machines.
83
+
84
+ For Better Auth, configure the primary server's ignored private runtime file with a generated secret of at least 32 characters and its HTTPS public origin; POSIX file permissions must be `0600`. Older Wrangler configs must remove same-named plain auth variables before secrets can be deployed. Review and apply the generated database migrations; resource provisioning does not establish application authorization or completed features.
51
85
 
52
86
  ## Choose your stack
53
87
 
54
- The bundled stack registry drives composition of every supported stack part. Run `anhedral registry list` or `anhedral registry view better-auth --json` to inspect items, destinations, dependencies and selection constraints. `new --dry-run --json` includes the resolved dependency graph. Scaffolding installs recipes in dependency order into `apps/*` and `packages/*`, validates local workspace references and cycles, and writes `anhedral.registry.json` with recipe versions and the resulting workspace graph. The same catalog is bundled with the plugin at build time.
88
+ The bundled stack registry drives composition of every supported stack part. Run `anhedral inspect --json` for read-only stack/setup/delivery evidence. Run `anhedral registry capabilities --json` for the implemented scope, local evidence, and owner prerequisites. Live provider acceptance remains separate; Electron/WXT distribution and plugin live readers are not automated, and Expo delivery returns EAS artifacts without store submission. Run `anhedral registry list` or `anhedral registry view better-auth --json` to inspect items, destinations, dependencies and selection constraints. `init --dry-run --json` includes the resolved dependency graph. Scaffolding installs recipes in dependency order into `apps/*` and `packages/*`, validates local workspace references and cycles, and writes `anhedral.registry.json` with recipe versions and the resulting workspace graph. The same catalog is bundled with the plugin at build time. Generated `docs/stack.md` lists the current resolved options, targets, environment names, required setup and verification; additions refresh it without rewriting the application README.
55
89
 
56
90
  Registry items are trusted recipes shipped in the CLI release: foundations, apps, services, shared packages and runtime integrations. They follow shadcn's source ownership and dependency composition practices; this catalog is an Anhedral schema, not a shadcn registry payload. Runtime-aware recipes coordinate bindings, manifests and auth routes across workspaces. Each registry entry owns its recipe, provider requirements, environment variables, bindings and composition rules. Resolved plans name the exact generated destinations, including runtime-dependent packages. Recipe versions follow the CLI release. The official shadcn bootstrap is pinned to 4.21.4; commit the generated source and lockfile to preserve the application artifact. Incremental `add` supports only the bundled catalog maintained and tested by Anhedral. External registries and customer-authored registry payloads are unsupported. Additions refuse to overwrite edited or untracked source; runtime relocation requires a deliberate migration.
57
91
 
58
92
  Use additions after initialization:
59
93
 
60
94
  ```sh
61
- anhedral add my-app resend stripe --dry-run --json
62
- anhedral add my-app resend stripe
95
+ anhedral add resend stripe --cwd my-app --dry-run --json
96
+ anhedral add resend stripe --cwd my-app --diff
97
+ anhedral add resend stripe --cwd my-app
63
98
  anhedral registry list --json
99
+ anhedral registry capabilities --json
64
100
  anhedral registry view resend --json
65
101
  ```
66
102
 
@@ -69,15 +105,15 @@ Review the dry-run file list and conflicts, then run the same addition without `
69
105
  Application recipes start from the official **shadcn pnpm/Turborepo monorepo**. The default creates **Vinext + React + TypeScript + Tailwind + shadcn/ui + Lucide**, targeting **Cloudflare Workers**. Select additional apps and services only when needed.
70
106
 
71
107
  ```sh
72
- pnpm dlx anhedral@latest new my-app
73
- pnpm dlx anhedral@latest new mobile-app --expo
74
- pnpm dlx anhedral@latest new product --next --expo --hono --neon --clerk --r2
108
+ pnpm dlx anhedral@latest init my-app
109
+ pnpm dlx anhedral@latest init mobile-app --expo
110
+ pnpm dlx anhedral@latest init product --next --expo --hono --neon --clerk --r2
75
111
  pnpm dlx anhedral@latest init --wxt
76
112
  ```
77
113
 
78
- To use a local checkout, run `pnpm build`, then `node dist/bin.js new <directory>`.
114
+ To use a local checkout, run `pnpm build`, then `node dist/bin.js init <directory>`.
79
115
 
80
- ![Anhedral CLI init stack](assets/anhedral-cli-init.svg)
116
+ ![Anhedral stack](assets/anhedral-stack.svg)
81
117
 
82
118
  Applications: `vinext`, `next`, `expo`, `electron`, `wxt`, `hono`.
83
119
 
@@ -99,18 +135,18 @@ Provider packages and services are **starters**, not finished product features.
99
135
 
100
136
  Anhedral's stack must make unexpected spending and runaway work difficult to trigger, easy to detect, and possible to stop. Choosing Vercel, Cloudflare, or another provider does not establish those protections. Treat the following as delivery requirements for each selected service; **the initializer does not automatically configure or verify all of them**.
101
137
 
102
- | Common mistake | Basic instruction |
103
- | --- | --- |
104
- | Assuming a free tier or budget alert caps spending | Check what each service charges for. Set available spending limits and alerts, allow for enforcement delays, and identify costs without a hard cap. |
105
- | Creating infinite loops in jobs, queues, schedules or alarms | Define a stopping condition, deadline and maximum amount of work. Keep job processing separate from enqueueing, and prevent overlapping scheduled runs. |
106
- | Retrying forever or processing the same job twice | Limit retries and concurrency, add backoff, and make repeated processing safe. Move failed jobs to a dead-letter queue for investigation before replay. |
107
- | Scanning an entire database or writing repeatedly on every request | Use indexed lookups, bounded pagination and batches. Measure storage operations per user action and remove unnecessary work. |
108
- | Letting public traffic trigger unlimited paid operations | Authorize expensive actions. Set user, tenant and total usage quotas for email, AI, uploads and background jobs; rate-limit public entry points. |
109
- | Assuming caching or DDoS protection eliminates traffic costs | Check request, bandwidth and image-processing charges. Optimize media, restrict transformations and protect direct access to origins. |
110
- | Collecting unlimited logs, analytics or session recordings | Collect only necessary events, sample high-volume data, and limit retention and ingestion. Keep test traffic separate. |
111
- | Testing with unrestricted production credentials | Separate environments and credentials. Mock paid services in routine tests and bound live test traffic. Review generated code and test failure paths before release. |
112
- | Discovering runaway usage only when the bill arrives | Monitor usage spikes and retries, assign an alert owner, and test an emergency stop for both web requests and background work. |
113
- | Assuming code rollback restores data | Keep backups and test restoration. Document how to stop work, recover data and resume safely. |
138
+ | Common mistake | Basic instruction |
139
+ | ------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
140
+ | Assuming a free tier or budget alert caps spending | Check what each service charges for. Set available spending limits and alerts, allow for enforcement delays, and identify costs without a hard cap. |
141
+ | Creating infinite loops in jobs, queues, schedules or alarms | Define a stopping condition, deadline and maximum amount of work. Keep job processing separate from enqueueing, and prevent overlapping scheduled runs. |
142
+ | Retrying forever or processing the same job twice | Limit retries and concurrency, add backoff, and make repeated processing safe. Move failed jobs to a dead-letter queue for investigation before replay. |
143
+ | Scanning an entire database or writing repeatedly on every request | Use indexed lookups, bounded pagination and batches. Measure storage operations per user action and remove unnecessary work. |
144
+ | Letting public traffic trigger unlimited paid operations | Authorize expensive actions. Set user, tenant and total usage quotas for email, AI, uploads and background jobs; rate-limit public entry points. |
145
+ | Assuming caching or DDoS protection eliminates traffic costs | Check request, bandwidth and image-processing charges. Optimize media, restrict transformations and protect direct access to origins. |
146
+ | Collecting unlimited logs, analytics or session recordings | Collect only necessary events, sample high-volume data, and limit retention and ingestion. Keep test traffic separate. |
147
+ | Testing with unrestricted production credentials | Separate environments and credentials. Mock paid services in routine tests and bound live test traffic. Review generated code and test failure paths before release. |
148
+ | Discovering runaway usage only when the bill arrives | Monitor usage spikes and retries, assign an alert owner, and test an emergency stop for both web requests and background work. |
149
+ | Assuming code rollback restores data | Keep backups and test restoration. Document how to stop work, recover data and resume safely. |
114
150
 
115
151
  Before delivery, record the budget and limits in the app's `docs/architecture.md`, and the tested controls, alert owner and recovery steps in `docs/verification.md`. Verify behavior under traffic spikes, duplicate jobs and service failures. Resolve exposure beyond the customer's authorized budget before enabling the workload.
116
152
 
@@ -131,11 +167,10 @@ anhedral verify my-app --json
131
167
  anhedral manage my-app --json
132
168
  ```
133
169
 
134
- Anhedral-managed hosting is under implementation. Its intended profile uses Workers for Platforms, Anhedral sign-in, an app registry, durable source/build versions and private routing; customer Cloudflare/GitHub/Vercel accounts should not be prerequisites. GitHub connection/export is optional for this managed profile. The local foundation is not yet a deployed service, a verified managed URL or a promise of free hosting. The [managed-hosting contract](docs/managed-hosting-architecture.md) defines required accounts, paid activation, source/version storage and the planned agent CLI. Use the supported customer-account path when that is the agreed destination; do not silently substitute it for unavailable managed hosting.
135
170
 
136
171
  For explicit Next.js/Vercel, connect the intended customer scope and private audience with `--git`, run checks before an authorized source push, then `adopt --json` and `verify --json` after the actual production deployment. Use `ship` only when selecting CLI delivery rather than deploying the same Git change twice.
137
172
 
138
- Account discovery selects the sole accessible account; use `--account <id>` for multiple accounts. D1, private R2, KV and Queues are reconciled idempotently. Selected external providers use their actual tools. Private hosting requires Cloudflare Zero Trust and scoped Access permissions; protection covers the whole Worker before product deployment. Whole-Worker Access currently excludes WebSockets, which need a deliberate alternative access workflow.
173
+ Account discovery selects the sole accessible account; use `--account <id>` for multiple accounts. D1, private R2, KV and Queues are reconciled idempotently. Selected API integrations use the bounded adapters documented in [provider configuration](docs/provider-configuration.md). Private hosting requires Cloudflare Zero Trust and scoped Access permissions; protection covers the whole Worker before product deployment. Whole-Worker Access currently excludes WebSockets, which need a deliberate alternative access workflow.
139
174
 
140
175
  Shipping requires a clean app source commit, runs checks, captures actual URLs and versions, and confirms 100% provider traffic. Failures preserve partial-operation evidence. `status --refresh` verifies current state. `rollback --release <id> --data-compatible` restores recorded code versions after schema/resource compatibility review; it does not restore data.
141
176
 
@@ -143,7 +178,7 @@ For a Git-triggered production release, run checks before pushing, then `adopt -
143
178
 
144
179
  ## Inspect setup and compatibility
145
180
 
146
- The CLI supports `new`, `init`, `add`, `registry`, `doctor`, `repository`, `register`, `connect`, `provision`, `ship`, `adopt`, `recover`, `status`, `manage`, `verify`, `rollback`, `--help`, and `--version`. `anhedral doctor <directory> --json` checks registry provenance, generated targets, workspace dependencies and local setup requirements without writes. Add `--providers` for read-only tool/account readiness; production product behavior remains unverified. Use `anhedral --help` for options. Its JSON plan includes versioned setup requirements, and generated projects include `anhedral.setup.json`. Requirements do not imply that accounts or resources are configured. The CLI and plugin catalogs derive from `src/stack-registry.ts`; `pnpm build` refreshes the plugin's bundled registry and stack standard.
181
+ The CLI supports `init`, `auth`, `new`, `add`, `registry`, `doctor`, `repository`, `register`, `connect`, `provision`, `ship`, `adopt`, `recover`, `status`, `manage`, `verify`, `rollback`, `integrations`, `--help`, and `--version`. `anhedral doctor <directory> --json` checks registry provenance, generated targets, workspace dependencies and local setup requirements without writes. Add `--providers` for read-only tool/account readiness; production product behavior remains unverified. Use `anhedral --help` for options. Its JSON plan includes versioned setup requirements, and generated projects include `anhedral.setup.json`. Requirements do not imply that accounts or resources are configured. The CLI and plugin catalogs derive from `src/stack-registry.ts`; `pnpm build` refreshes the plugin's bundled registry and stack standard.
147
182
 
148
183
  Requires Node.js 20.19+ in the 20 series or 22.12+ and pnpm 10.34.5. Pin the CLI version and commit the generated lockfile. The shadcn bootstrap version is pinned in `src/dependencies.ts`; review its upgrades and generated output before use.
149
184
 
@@ -153,26 +188,30 @@ Initialize from Linux, macOS, or WSL. Native Windows initialization fails before
153
188
 
154
189
  The workflow follows the [Anhedral Application Stack & Delivery Standard](docs/application-stack-standard.md).
155
190
 
156
- Install the plugin with `codex plugin marketplace add anhedral/anhedral-init`, then `codex plugin add anhedral@anhedral`. Releases include a self-contained skill plugin ZIP matching the CLI version. A public ChatGPT listing requires separate submission.
191
+ Install the plugin with `codex plugin marketplace add anhedral/anhedral-cli`, then `codex plugin add anhedral@anhedral`. The marketplace pins an immutable plugin source tag containing the same self-contained runtime as the release ZIP. Released plugin ZIPs match the CLI version. The headless-first plugin includes an optional MCP-backed project inspector with Overview, Stack, Setup and Delivery sections; see [plugin UI development](docs/plugin-ui.md) for preview, packaging and host integration. A public ChatGPT listing requires separate submission.
157
192
 
158
193
  Ask: **“Build a team request dashboard with Anhedral. Include sign-in, persistent requests, ownership, status updates, and filters. Implement and verify the complete app.”**
159
194
 
160
195
  The plugin guides Codex through requirement inference, stack selection, CLI planning and generation, implementation, and verification. It records decisions in the app's `docs/architecture.md` and evidence in `docs/verification.md`. The CLI maintains deployment records and reads provider status. Provider access uses the tools and CLIs available in the session.
161
196
 
162
- Anhedral carries one request through to a shipped product with editable source in your accounts. Initialization generates a starter; delivery also requires implementation, provider access and live verification.
197
+ The plugin guides development toward an application with editable source in your accounts. Initialization generates a starter; delivery also requires feature implementation, provider access and live verification. A successful scaffold or build is not evidence of a working product.
163
198
 
164
199
  ## Contributing and security
165
200
 
166
- Anhedral is maintainer-led, with a small contribution surface. Focused bug fixes, tests, and documentation corrections are welcome; discuss larger changes before implementation. See [contributing](https://github.com/anhedral/anhedral-init/blob/main/.github/CONTRIBUTING.md), the [code of conduct](https://github.com/anhedral/anhedral-init/blob/main/.github/CODE_OF_CONDUCT.md), and [security reporting](https://github.com/anhedral/anhedral-init/blob/main/.github/SECURITY.md). Use [issues](https://github.com/anhedral/anhedral-init/issues) for reproducible CLI problems and [private reporting](https://github.com/anhedral/anhedral-init/security/advisories/new) for vulnerabilities.
201
+ Anhedral is maintainer-led, with a small contribution surface. Focused bug fixes, tests, and documentation corrections are welcome; discuss larger changes before implementation. See [contributing](https://github.com/anhedral/anhedral-cli/blob/main/.github/CONTRIBUTING.md), the [code of conduct](https://github.com/anhedral/anhedral-cli/blob/main/.github/CODE_OF_CONDUCT.md), and [security reporting](https://github.com/anhedral/anhedral-cli/blob/main/.github/SECURITY.md). Use [issues](https://github.com/anhedral/anhedral-cli/issues) for reproducible CLI problems and [private reporting](https://github.com/anhedral/anhedral-cli/security/advisories/new) for vulnerabilities.
167
202
 
168
203
  ## Development
169
204
 
205
+ Match the CLI, plugin and registry versions when developing or reviewing changes. Unpublished changes require the locally built CLI and matching plugin rather than the public npm release. Local tests and packaged-artifact checks do not establish live provider acceptance or native plugin installation.
206
+
170
207
  ```sh
171
208
  pnpm install --frozen-lockfile
172
209
  pnpm test:all
173
210
  pnpm test:integration # Generate and validate fresh application workspaces
174
211
  ```
175
212
 
213
+ The project inspector lives in `plugin-app/` with its own dependency lockfile and Node 22.13+/24 toolchain. Run `pnpm --dir plugin-app install --frozen-lockfile`, then `pnpm plugin:check`.
214
+
176
215
  Implementation lives in `src/`, plugin instructions in `plugins/anhedral/`, and validation in `scripts/` and `tests/`. `dist/` and `.artifacts/release/` are generated and ignored.
177
216
 
178
217
  Anhedral is open source under the [Apache License 2.0](LICENSE). Generated applications remain ordinary project source that their developers can customize and license for their products. Preserve applicable license notices for upstream code and dependencies.
@@ -154,7 +154,7 @@
154
154
  <text x="1234" y="1004" fill="#f7f5f0" font-family="Inter, Helvetica Neue, Arial, sans-serif" font-size="19" font-weight="500" text-anchor="middle" letter-spacing="0">Hono + OpenAPI</text>
155
155
  <text x="1234" y="1030" fill="#c4c7ca" font-family="Inter, Helvetica Neue, Arial, sans-serif" font-size="15" font-weight="400" text-anchor="middle" letter-spacing="0">optional shared API</text>
156
156
  <text x="1234" y="1066" fill="#c4c7ca" font-family="Inter, Helvetica Neue, Arial, sans-serif" font-size="13" font-weight="400" text-anchor="middle" letter-spacing="0">Web-only: route handlers</text>
157
- <text x="1234" y="1094" fill="#c4c7ca" font-family="Inter, Helvetica Neue, Arial, sans-serif" font-size="13" font-weight="400" text-anchor="middle" letter-spacing="0">Containers when required</text>
157
+ <text x="1234" y="1094" fill="#c4c7ca" font-family="Inter, Helvetica Neue, Arial, sans-serif" font-size="13" font-weight="400" text-anchor="middle" letter-spacing="0">Selected Worker bindings</text>
158
158
  <rect x="1435" y="354" width="430" height="80" rx="12" fill="url(#card-fill)" stroke="#73777b" stroke-width="1.15"/>
159
159
  <rect x="1451" y="366" width="56" height="56" rx="11" fill="#07121d" stroke="#34424e" stroke-width="1.15"/>
160
160
  <g transform="translate(1456 382.5) scale(0.9583333333333334)"><path d="M24 0V24l-9.365-8.045V24H0V0ZM2.942 21.087h8.751V9.563l9.365 8.204V2.919L2.942 2.914Z" fill="#35d6a8"/></g>
@@ -168,7 +168,7 @@
168
168
  <g transform="translate(1455 458) scale(2)"><path d="M21.678.521c-1.032-.92-2.28-.55-3.513.544a8.71 8.71 0 0 0-.547.535c-2.109 2.237-4.066 6.38-4.674 9.544.237.48.422 1.093.544 1.561a13.044 13.044 0 0 1 .164.703s-.019-.071-.096-.296l-.05-.146a1.689 1.689 0 0 0-.033-.08c-.138-.32-.518-.995-.686-1.289-.143.423-.27.818-.376 1.176.484.884.778 2.4.778 2.4s-.025-.099-.147-.442c-.107-.303-.644-1.244-.772-1.464-.217.804-.304 1.346-.226 1.478.152.256.296.698.422 1.186.286 1.1.485 2.44.485 2.44l.017.224a22.41 22.41 0 0 0 .056 2.748c.095 1.146.273 2.13.5 2.657l.155-.084c-.334-1.038-.47-2.399-.41-3.967.09-2.398.642-5.29 1.661-8.304 1.723-4.55 4.113-8.201 6.3-9.945-1.993 1.8-4.692 7.63-5.5 9.788-.904 2.416-1.545 4.684-1.931 6.857.666-2.037 2.821-2.912 2.821-2.912s1.057-1.304 2.292-3.166c-.74.169-1.955.458-2.362.629-.6.251-.762.337-.762.337s1.945-1.184 3.613-1.72C21.695 7.9 24.195 2.767 21.678.521m-18.573.543A1.842 1.842 0 0 0 1.27 2.9v16.608a1.84 1.84 0 0 0 1.835 1.834h9.418a22.953 22.953 0 0 1-.052-2.707c-.006-.062-.011-.141-.016-.2a27.01 27.01 0 0 0-.473-2.378c-.121-.47-.275-.898-.369-1.057-.116-.197-.098-.31-.097-.432 0-.12.015-.245.037-.386a9.98 9.98 0 0 1 .234-1.045l.217-.028c-.017-.035-.014-.065-.031-.097l-.041-.381a32.8 32.8 0 0 1 .382-1.194l.2-.019c-.008-.016-.01-.038-.018-.053l-.043-.316c.63-3.28 2.587-7.443 4.8-9.791.066-.069.133-.128.198-.194Z" fill="#51dce9"/></g>
169
169
  <path d="M 1519 456 L 1519 508" fill="none" stroke="#34424e" stroke-width="1" stroke-linecap="round"/>
170
170
  <text x="1539" y="473" fill="#f7f5f0" font-family="Inter, Helvetica Neue, Arial, sans-serif" font-size="18" font-weight="500" text-anchor="start" letter-spacing="0">D1 + Local Data</text>
171
- <text x="1539" y="497" fill="#c4c7ca" font-family="Inter, Helvetica Neue, Arial, sans-serif" font-size="14" font-weight="400" text-anchor="start" letter-spacing="0">D1 + Drizzle · or local SQLite</text>
171
+ <text x="1539" y="497" fill="#c4c7ca" font-family="Inter, Helvetica Neue, Arial, sans-serif" font-size="14" font-weight="400" text-anchor="start" letter-spacing="0">D1 + Drizzle · local adapter interface</text>
172
172
  <text x="1539" y="517" fill="#c4c7ca" font-family="Inter, Helvetica Neue, Arial, sans-serif" font-size="12" font-weight="400" text-anchor="start" letter-spacing="0">Choose persistence per product</text>
173
173
  <rect x="1435" y="530" width="430" height="80" rx="12" fill="url(#card-fill)" stroke="#73777b" stroke-width="1.15"/>
174
174
  <rect x="1451" y="542" width="56" height="56" rx="11" fill="#07121d" stroke="#34424e" stroke-width="1.15"/>
@@ -230,11 +230,11 @@
230
230
  <text x="1539" y="1221" fill="#c4c7ca" font-family="Inter, Helvetica Neue, Arial, sans-serif" font-size="12" font-weight="400" text-anchor="start" letter-spacing="0">Workers AI when advantageous</text>
231
231
  <rect x="1435" y="1234" width="430" height="80" rx="12" fill="url(#card-fill)" stroke="#73777b" stroke-width="1.15"/>
232
232
  <rect x="1451" y="1246" width="56" height="56" rx="11" fill="#07121d" stroke="#34424e" stroke-width="1.15"/>
233
- <g transform="translate(1455 1250) scale(2)"><path d="M16.5088 16.8447c.1475-.5068.0908-.9707-.1553-1.3154-.2246-.3164-.6045-.499-1.0615-.5205l-8.6592-.1123a.1559.1559 0 0 1-.1333-.0713c-.0283-.042-.0351-.0986-.021-.1553.0278-.084.1123-.1484.2036-.1562l8.7359-.1123c1.0351-.0489 2.1601-.8868 2.5537-1.9136l.499-1.3013c.0215-.0561.0293-.1128.0147-.168-.5625-2.5463-2.835-4.4453-5.5499-4.4453-2.5039 0-4.6284 1.6177-5.3876 3.8614-.4927-.3658-1.1187-.5625-1.794-.499-1.2026.119-2.1665 1.083-2.2861 2.2856-.0283.31-.0069.6128.0635.894C1.5683 13.171 0 14.7754 0 16.752c0 .1748.0142.3515.0352.5273.0141.083.0844.1475.1689.1475h15.9814c.0909 0 .1758-.0645.2032-.1553l.12-.4268zm2.7568-5.5634c-.0771 0-.1611 0-.2383.0112-.0566 0-.1054.0415-.127.0976l-.3378 1.1744c-.1475.5068-.0918.9707.1543 1.3164.2256.3164.6055.498 1.0625.5195l1.8437.1133c.0557 0 .1055.0263.1329.0703.0283.043.0351.1074.0214.1562-.0283.084-.1132.1485-.204.1553l-1.921.1123c-1.041.0488-2.1582.8867-2.5527 1.914l-.1406.3585c-.0283.0713.0215.1416.0986.1416h6.5977c.0771 0 .1474-.0489.169-.126.1122-.4082.1757-.837.1757-1.2803 0-2.6025-2.125-4.727-4.7344-4.727" fill="#f48120"/></g>
233
+ <g transform="translate(1455 1250) scale(2)"><path d="M14.679 0c4.648 0 7.413 2.765 7.413 6.434s-2.765 6.434-7.413 6.434H12.33L24 24h-8.245l-8.88-8.44c-.636-.588-.93-1.273-.93-1.86 0-.831.587-1.565 1.713-1.883l4.574-1.224c1.737-.465 2.936-1.81 2.936-3.572 0-2.153-1.761-3.4-3.939-3.4H0V0z" fill="#f48120"/></g>
234
234
  <path d="M 1519 1248 L 1519 1300" fill="none" stroke="#34424e" stroke-width="1" stroke-linecap="round"/>
235
- <text x="1539" y="1265" fill="#f7f5f0" font-family="Inter, Helvetica Neue, Arial, sans-serif" font-size="18" font-weight="500" text-anchor="start" letter-spacing="0">Domain + Mail</text>
236
- <text x="1539" y="1289" fill="#c4c7ca" font-family="Inter, Helvetica Neue, Arial, sans-serif" font-size="14" font-weight="400" text-anchor="start" letter-spacing="0">GoDaddy broker · Cloudflare nameservers</text>
237
- <text x="1539" y="1309" fill="#c4c7ca" font-family="Inter, Helvetica Neue, Arial, sans-serif" font-size="12" font-weight="400" text-anchor="start" letter-spacing="0">Cloudflare Routing + Sending / Resend alternative</text>
235
+ <text x="1539" y="1265" fill="#f7f5f0" font-family="Inter, Helvetica Neue, Arial, sans-serif" font-size="18" font-weight="500" text-anchor="start" letter-spacing="0">Resend Email</text>
236
+ <text x="1539" y="1289" fill="#c4c7ca" font-family="Inter, Helvetica Neue, Arial, sans-serif" font-size="14" font-weight="400" text-anchor="start" letter-spacing="0">Transactional email client</text>
237
+ <text x="1539" y="1309" fill="#c4c7ca" font-family="Inter, Helvetica Neue, Arial, sans-serif" font-size="12" font-weight="400" text-anchor="start" letter-spacing="0">Configure API key and verified sender</text>
238
238
  <rect x="1435" y="1322" width="430" height="80" rx="12" fill="url(#card-fill)" stroke="#73777b" stroke-width="1.15"/>
239
239
  <rect x="1451" y="1334" width="56" height="56" rx="11" fill="#07121d" stroke="#34424e" stroke-width="1.15"/>
240
240
  <g transform="translate(1455 1338) scale(2)"><path d="M16.5088 16.8447c.1475-.5068.0908-.9707-.1553-1.3154-.2246-.3164-.6045-.499-1.0615-.5205l-8.6592-.1123a.1559.1559 0 0 1-.1333-.0713c-.0283-.042-.0351-.0986-.021-.1553.0278-.084.1123-.1484.2036-.1562l8.7359-.1123c1.0351-.0489 2.1601-.8868 2.5537-1.9136l.499-1.3013c.0215-.0561.0293-.1128.0147-.168-.5625-2.5463-2.835-4.4453-5.5499-4.4453-2.5039 0-4.6284 1.6177-5.3876 3.8614-.4927-.3658-1.1187-.5625-1.794-.499-1.2026.119-2.1665 1.083-2.2861 2.2856-.0283.31-.0069.6128.0635.894C1.5683 13.171 0 14.7754 0 16.752c0 .1748.0142.3515.0352.5273.0141.083.0844.1475.1689.1475h15.9814c.0909 0 .1758-.0645.2032-.1553l.12-.4268zm2.7568-5.5634c-.0771 0-.1611 0-.2383.0112-.0566 0-.1054.0415-.127.0976l-.3378 1.1744c-.1475.5068-.0918.9707.1543 1.3164.2256.3164.6055.498 1.0625.5195l1.8437.1133c.0557 0 .1055.0263.1329.0703.0283.043.0351.1074.0214.1562-.0283.084-.1132.1485-.204.1553l-1.921.1123c-1.041.0488-2.1582.8867-2.5527 1.914l-.1406.3585c-.0283.0713.0215.1416.0986.1416h6.5977c.0771 0 .1474-.0489.169-.126.1122-.4082.1757-.837.1757-1.2803 0-2.6025-2.125-4.727-4.7344-4.727" fill="#f48120"/></g>
@@ -1,5 +1,5 @@
1
1
  <svg xmlns="http://www.w3.org/2000/svg" width="1920" height="1660" viewBox="0 0 1920 1660" role="img" aria-labelledby="title description">
2
- <title id="title">Anhedral Init Stack</title>
2
+ <title id="title">Anhedral Stack</title>
3
3
  <desc id="description">Client-owned shadcn, pnpm and Turborepo monorepo. Vinext on Cloudflare Workers is the web default; native Next.js on Vercel and OpenNext on Workers are optional; Hono and OpenAPI provide optional shared APIs. Select direct Neon HTTP with Drizzle or D1, authentication, private R2, KV, realtime, jobs, workflows, AI, email, billing, observability and analytics only when required. Dashed yellow lines show publication; white lines show application runtime and service bindings.</desc>
4
4
  <defs>
5
5
  <linearGradient id="card-fill" x1="0" y1="0" x2="1" y2="1">
@@ -154,7 +154,7 @@
154
154
  <text x="1234" y="1004" fill="#f7f5f0" font-family="Inter, Helvetica Neue, Arial, sans-serif" font-size="19" font-weight="500" text-anchor="middle" letter-spacing="0">Hono + OpenAPI</text>
155
155
  <text x="1234" y="1030" fill="#c4c7ca" font-family="Inter, Helvetica Neue, Arial, sans-serif" font-size="15" font-weight="400" text-anchor="middle" letter-spacing="0">optional shared API</text>
156
156
  <text x="1234" y="1066" fill="#c4c7ca" font-family="Inter, Helvetica Neue, Arial, sans-serif" font-size="13" font-weight="400" text-anchor="middle" letter-spacing="0">Web-only: route handlers</text>
157
- <text x="1234" y="1094" fill="#c4c7ca" font-family="Inter, Helvetica Neue, Arial, sans-serif" font-size="13" font-weight="400" text-anchor="middle" letter-spacing="0">Containers when required</text>
157
+ <text x="1234" y="1094" fill="#c4c7ca" font-family="Inter, Helvetica Neue, Arial, sans-serif" font-size="13" font-weight="400" text-anchor="middle" letter-spacing="0">Selected Worker bindings</text>
158
158
  <rect x="1435" y="354" width="430" height="80" rx="12" fill="url(#card-fill)" stroke="#73777b" stroke-width="1.15"/>
159
159
  <rect x="1451" y="366" width="56" height="56" rx="11" fill="#07121d" stroke="#34424e" stroke-width="1.15"/>
160
160
  <g transform="translate(1456 382.5) scale(0.9583333333333334)"><path d="M24 0V24l-9.365-8.045V24H0V0ZM2.942 21.087h8.751V9.563l9.365 8.204V2.919L2.942 2.914Z" fill="#35d6a8"/></g>
@@ -168,7 +168,7 @@
168
168
  <g transform="translate(1455 458) scale(2)"><path d="M21.678.521c-1.032-.92-2.28-.55-3.513.544a8.71 8.71 0 0 0-.547.535c-2.109 2.237-4.066 6.38-4.674 9.544.237.48.422 1.093.544 1.561a13.044 13.044 0 0 1 .164.703s-.019-.071-.096-.296l-.05-.146a1.689 1.689 0 0 0-.033-.08c-.138-.32-.518-.995-.686-1.289-.143.423-.27.818-.376 1.176.484.884.778 2.4.778 2.4s-.025-.099-.147-.442c-.107-.303-.644-1.244-.772-1.464-.217.804-.304 1.346-.226 1.478.152.256.296.698.422 1.186.286 1.1.485 2.44.485 2.44l.017.224a22.41 22.41 0 0 0 .056 2.748c.095 1.146.273 2.13.5 2.657l.155-.084c-.334-1.038-.47-2.399-.41-3.967.09-2.398.642-5.29 1.661-8.304 1.723-4.55 4.113-8.201 6.3-9.945-1.993 1.8-4.692 7.63-5.5 9.788-.904 2.416-1.545 4.684-1.931 6.857.666-2.037 2.821-2.912 2.821-2.912s1.057-1.304 2.292-3.166c-.74.169-1.955.458-2.362.629-.6.251-.762.337-.762.337s1.945-1.184 3.613-1.72C21.695 7.9 24.195 2.767 21.678.521m-18.573.543A1.842 1.842 0 0 0 1.27 2.9v16.608a1.84 1.84 0 0 0 1.835 1.834h9.418a22.953 22.953 0 0 1-.052-2.707c-.006-.062-.011-.141-.016-.2a27.01 27.01 0 0 0-.473-2.378c-.121-.47-.275-.898-.369-1.057-.116-.197-.098-.31-.097-.432 0-.12.015-.245.037-.386a9.98 9.98 0 0 1 .234-1.045l.217-.028c-.017-.035-.014-.065-.031-.097l-.041-.381a32.8 32.8 0 0 1 .382-1.194l.2-.019c-.008-.016-.01-.038-.018-.053l-.043-.316c.63-3.28 2.587-7.443 4.8-9.791.066-.069.133-.128.198-.194Z" fill="#51dce9"/></g>
169
169
  <path d="M 1519 456 L 1519 508" fill="none" stroke="#34424e" stroke-width="1" stroke-linecap="round"/>
170
170
  <text x="1539" y="473" fill="#f7f5f0" font-family="Inter, Helvetica Neue, Arial, sans-serif" font-size="18" font-weight="500" text-anchor="start" letter-spacing="0">D1 + Local Data</text>
171
- <text x="1539" y="497" fill="#c4c7ca" font-family="Inter, Helvetica Neue, Arial, sans-serif" font-size="14" font-weight="400" text-anchor="start" letter-spacing="0">D1 + Drizzle · or local SQLite</text>
171
+ <text x="1539" y="497" fill="#c4c7ca" font-family="Inter, Helvetica Neue, Arial, sans-serif" font-size="14" font-weight="400" text-anchor="start" letter-spacing="0">D1 + Drizzle · local adapter interface</text>
172
172
  <text x="1539" y="517" fill="#c4c7ca" font-family="Inter, Helvetica Neue, Arial, sans-serif" font-size="12" font-weight="400" text-anchor="start" letter-spacing="0">Choose persistence per product</text>
173
173
  <rect x="1435" y="530" width="430" height="80" rx="12" fill="url(#card-fill)" stroke="#73777b" stroke-width="1.15"/>
174
174
  <rect x="1451" y="542" width="56" height="56" rx="11" fill="#07121d" stroke="#34424e" stroke-width="1.15"/>
@@ -230,11 +230,11 @@
230
230
  <text x="1539" y="1221" fill="#c4c7ca" font-family="Inter, Helvetica Neue, Arial, sans-serif" font-size="12" font-weight="400" text-anchor="start" letter-spacing="0">Workers AI when advantageous</text>
231
231
  <rect x="1435" y="1234" width="430" height="80" rx="12" fill="url(#card-fill)" stroke="#73777b" stroke-width="1.15"/>
232
232
  <rect x="1451" y="1246" width="56" height="56" rx="11" fill="#07121d" stroke="#34424e" stroke-width="1.15"/>
233
- <g transform="translate(1455 1250) scale(2)"><path d="M16.5088 16.8447c.1475-.5068.0908-.9707-.1553-1.3154-.2246-.3164-.6045-.499-1.0615-.5205l-8.6592-.1123a.1559.1559 0 0 1-.1333-.0713c-.0283-.042-.0351-.0986-.021-.1553.0278-.084.1123-.1484.2036-.1562l8.7359-.1123c1.0351-.0489 2.1601-.8868 2.5537-1.9136l.499-1.3013c.0215-.0561.0293-.1128.0147-.168-.5625-2.5463-2.835-4.4453-5.5499-4.4453-2.5039 0-4.6284 1.6177-5.3876 3.8614-.4927-.3658-1.1187-.5625-1.794-.499-1.2026.119-2.1665 1.083-2.2861 2.2856-.0283.31-.0069.6128.0635.894C1.5683 13.171 0 14.7754 0 16.752c0 .1748.0142.3515.0352.5273.0141.083.0844.1475.1689.1475h15.9814c.0909 0 .1758-.0645.2032-.1553l.12-.4268zm2.7568-5.5634c-.0771 0-.1611 0-.2383.0112-.0566 0-.1054.0415-.127.0976l-.3378 1.1744c-.1475.5068-.0918.9707.1543 1.3164.2256.3164.6055.498 1.0625.5195l1.8437.1133c.0557 0 .1055.0263.1329.0703.0283.043.0351.1074.0214.1562-.0283.084-.1132.1485-.204.1553l-1.921.1123c-1.041.0488-2.1582.8867-2.5527 1.914l-.1406.3585c-.0283.0713.0215.1416.0986.1416h6.5977c.0771 0 .1474-.0489.169-.126.1122-.4082.1757-.837.1757-1.2803 0-2.6025-2.125-4.727-4.7344-4.727" fill="#f48120"/></g>
233
+ <g transform="translate(1455 1250) scale(2)"><path d="M14.679 0c4.648 0 7.413 2.765 7.413 6.434s-2.765 6.434-7.413 6.434H12.33L24 24h-8.245l-8.88-8.44c-.636-.588-.93-1.273-.93-1.86 0-.831.587-1.565 1.713-1.883l4.574-1.224c1.737-.465 2.936-1.81 2.936-3.572 0-2.153-1.761-3.4-3.939-3.4H0V0z" fill="#f48120"/></g>
234
234
  <path d="M 1519 1248 L 1519 1300" fill="none" stroke="#34424e" stroke-width="1" stroke-linecap="round"/>
235
- <text x="1539" y="1265" fill="#f7f5f0" font-family="Inter, Helvetica Neue, Arial, sans-serif" font-size="18" font-weight="500" text-anchor="start" letter-spacing="0">Domain + Mail</text>
236
- <text x="1539" y="1289" fill="#c4c7ca" font-family="Inter, Helvetica Neue, Arial, sans-serif" font-size="14" font-weight="400" text-anchor="start" letter-spacing="0">GoDaddy broker · Cloudflare nameservers</text>
237
- <text x="1539" y="1309" fill="#c4c7ca" font-family="Inter, Helvetica Neue, Arial, sans-serif" font-size="12" font-weight="400" text-anchor="start" letter-spacing="0">Cloudflare Routing + Sending / Resend alternative</text>
235
+ <text x="1539" y="1265" fill="#f7f5f0" font-family="Inter, Helvetica Neue, Arial, sans-serif" font-size="18" font-weight="500" text-anchor="start" letter-spacing="0">Resend Email</text>
236
+ <text x="1539" y="1289" fill="#c4c7ca" font-family="Inter, Helvetica Neue, Arial, sans-serif" font-size="14" font-weight="400" text-anchor="start" letter-spacing="0">Transactional email client</text>
237
+ <text x="1539" y="1309" fill="#c4c7ca" font-family="Inter, Helvetica Neue, Arial, sans-serif" font-size="12" font-weight="400" text-anchor="start" letter-spacing="0">Configure API key and verified sender</text>
238
238
  <rect x="1435" y="1322" width="430" height="80" rx="12" fill="url(#card-fill)" stroke="#73777b" stroke-width="1.15"/>
239
239
  <rect x="1451" y="1334" width="56" height="56" rx="11" fill="#07121d" stroke="#34424e" stroke-width="1.15"/>
240
240
  <g transform="translate(1455 1338) scale(2)"><path d="M16.5088 16.8447c.1475-.5068.0908-.9707-.1553-1.3154-.2246-.3164-.6045-.499-1.0615-.5205l-8.6592-.1123a.1559.1559 0 0 1-.1333-.0713c-.0283-.042-.0351-.0986-.021-.1553.0278-.084.1123-.1484.2036-.1562l8.7359-.1123c1.0351-.0489 2.1601-.8868 2.5537-1.9136l.499-1.3013c.0215-.0561.0293-.1128.0147-.168-.5625-2.5463-2.835-4.4453-5.5499-4.4453-2.5039 0-4.6284 1.6177-5.3876 3.8614-.4927-.3658-1.1187-.5625-1.794-.499-1.2026.119-2.1665 1.083-2.2861 2.2856-.0283.31-.0069.6128.0635.894C1.5683 13.171 0 14.7754 0 16.752c0 .1748.0142.3515.0352.5273.0141.083.0844.1475.1689.1475h15.9814c.0909 0 .1758-.0645.2032-.1553l.12-.4268zm2.7568-5.5634c-.0771 0-.1611 0-.2383.0112-.0566 0-.1054.0415-.127.0976l-.3378 1.1744c-.1475.5068-.0918.9707.1543 1.3164.2256.3164.6055.498 1.0625.5195l1.8437.1133c.0557 0 .1055.0263.1329.0703.0283.043.0351.1074.0214.1562-.0283.084-.1132.1485-.204.1553l-1.921.1123c-1.041.0488-2.1582.8867-2.5527 1.914l-.1406.3585c-.0283.0713.0215.1416.0986.1416h6.5977c.0771 0 .1474-.0489.169-.126.1122-.4082.1757-.837.1757-1.2803 0-2.6025-2.125-4.727-4.7344-4.727" fill="#f48120"/></g>
@@ -115,11 +115,16 @@ function validateAdvisory(advisory) {
115
115
  ensure(Array.isArray(advisory.findings), 'Malformed pnpm advisory');
116
116
  ensure(advisory.findings.length > 0, 'Malformed pnpm advisory');
117
117
  advisory.findings.forEach(validateFinding);
118
+ const versions = advisory.findings.map((finding) => finding.version);
119
+ ensure(new Set(versions).size === versions.length, 'Duplicate pnpm advisory versions');
118
120
  }
119
121
 
120
122
  function validateCount(advisories, severity, count) {
121
123
  ensure(Number.isInteger(count) && count >= 0, 'Malformed pnpm vulnerability counts');
122
- const details = advisories.filter((advisory) => advisory.severity === severity).length;
124
+ // The pnpm 10 registry endpoint counts affected versions, not unique advisory IDs.
125
+ const details = advisories
126
+ .filter((advisory) => advisory.severity === severity)
127
+ .reduce((total, advisory) => total + advisory.findings.length, 0);
123
128
  ensure(count === details, `Missing ${severity} advisory details: count ${count}, details ${details}`);
124
129
  }
125
130