@zitadel/cli 0.1.0-alpha.18 → 0.1.0-alpha.19

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 (56) hide show
  1. package/README.md +81 -33
  2. package/SKILLS.md +74 -8
  3. package/dist/{branding-BXj028-W.mjs → branding-cygSBPkV.mjs} +2 -2
  4. package/dist/{branding-BXj028-W.mjs.map → branding-cygSBPkV.mjs.map} +1 -1
  5. package/dist/{claim-state-BTGInAaN.mjs → claim-state-DlfD6Res.mjs} +2 -2
  6. package/dist/{claim-state-BTGInAaN.mjs.map → claim-state-DlfD6Res.mjs.map} +1 -1
  7. package/dist/commands/apply.mjs +4 -4
  8. package/dist/commands/branding/eject.mjs +11 -32
  9. package/dist/commands/branding/eject.mjs.map +1 -1
  10. package/dist/commands/claim.mjs +3 -3
  11. package/dist/commands/doctor.mjs +41 -18
  12. package/dist/commands/doctor.mjs.map +1 -1
  13. package/dist/commands/eject.mjs +4 -4
  14. package/dist/commands/eject.mjs.map +1 -1
  15. package/dist/commands/logs.mjs +2 -2
  16. package/dist/commands/plan.mjs +4 -4
  17. package/dist/commands/reset.mjs +2 -2
  18. package/dist/commands/schemas/list.mjs +6 -6
  19. package/dist/commands/schemas/list.mjs.map +1 -1
  20. package/dist/commands/setup.mjs +101 -143
  21. package/dist/commands/setup.mjs.map +1 -1
  22. package/dist/commands/start.mjs +3 -3
  23. package/dist/commands/status.mjs +5 -5
  24. package/dist/commands/stop.mjs +3 -3
  25. package/dist/designs-Ckz18Dpo.mjs +38 -0
  26. package/dist/designs-Ckz18Dpo.mjs.map +1 -0
  27. package/dist/{docker-Ciz9N6uI.mjs → docker-DcRGTOJa.mjs} +4 -3
  28. package/dist/docker-DcRGTOJa.mjs.map +1 -0
  29. package/dist/{environment-CuHKxtIO.mjs → environment-rjRVkJjW.mjs} +1 -1
  30. package/dist/{environment-CuHKxtIO.mjs.map → environment-rjRVkJjW.mjs.map} +1 -1
  31. package/dist/{journey-guidance-CtQUfWar.mjs → journey-guidance-BGrOX_gT.mjs} +2 -2
  32. package/dist/{journey-guidance-CtQUfWar.mjs.map → journey-guidance-BGrOX_gT.mjs.map} +1 -1
  33. package/dist/{oclif-CL6-M13m.mjs → oclif-CanO3zdt.mjs} +48 -2
  34. package/dist/oclif-CanO3zdt.mjs.map +1 -0
  35. package/dist/{orca-BWB4zOdw.mjs → orca-Cu0iW1mv.mjs} +167 -26
  36. package/dist/orca-Cu0iW1mv.mjs.map +1 -0
  37. package/dist/package-manager-DFdLcDx1.mjs +194 -0
  38. package/dist/package-manager-DFdLcDx1.mjs.map +1 -0
  39. package/dist/{ports-Cu2Y7aXQ.mjs → ports-BM20XIZb.mjs} +2 -2
  40. package/dist/{ports-Cu2Y7aXQ.mjs.map → ports-BM20XIZb.mjs.map} +1 -1
  41. package/dist/{processes-Bn2WngOi.mjs → processes-DKHPkU8O.mjs} +1 -1
  42. package/dist/{processes-Bn2WngOi.mjs.map → processes-DKHPkU8O.mjs.map} +1 -1
  43. package/dist/{project-yejJSfdJ.mjs → project-CHhot85s.mjs} +2 -2
  44. package/dist/{project-yejJSfdJ.mjs.map → project-CHhot85s.mjs.map} +1 -1
  45. package/dist/{sync-Cx2_4rXW.mjs → sync-Bd6vASgg.mjs} +456 -31
  46. package/dist/sync-Bd6vASgg.mjs.map +1 -0
  47. package/dist/{user-schema-W1OsXdpc.mjs → user-schema-DTuOsdKE.mjs} +1 -1
  48. package/dist/{user-schema-W1OsXdpc.mjs.map → user-schema-DTuOsdKE.mjs.map} +1 -1
  49. package/oclif.manifest.json +2 -2
  50. package/package.json +6 -5
  51. package/dist/docker-Ciz9N6uI.mjs.map +0 -1
  52. package/dist/oclif-CL6-M13m.mjs.map +0 -1
  53. package/dist/orca-BWB4zOdw.mjs.map +0 -1
  54. package/dist/scaffold-manifest-CKNFLX9T.mjs +0 -81
  55. package/dist/scaffold-manifest-CKNFLX9T.mjs.map +0 -1
  56. package/dist/sync-Cx2_4rXW.mjs.map +0 -1
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @zitadel/cli
2
2
 
3
- Scaffolds Zitadel auth (login, register, profile, middleware) into a Next.js, React, Vue, Angular, or Nuxt app.
3
+ Scaffolds Zitadel auth (login, register, profile, middleware/proxy) into a Next.js, Nuxt, React, Vue, Angular, Solid, Svelte, or Qwik app.
4
4
 
5
5
  ```sh
6
6
  npx @zitadel/cli@alpha start
@@ -15,9 +15,10 @@ automation when reproducibility matters.
15
15
 
16
16
  ## Requirements
17
17
 
18
- - Node 24+
18
+ - Node from the supported range in `package.json#engines` (currently ≥ 24)
19
19
  - Docker only when using the optional Docker runtime backend
20
- - A Next.js project, or an empty directory where setup can scaffold one
20
+ - An app in one of the eight supported frameworks, or an empty directory
21
+ where setup can scaffold one
21
22
 
22
23
  ## Quickstart
23
24
 
@@ -31,14 +32,20 @@ npm run dev
31
32
  ```
32
33
 
33
34
  `start` runs the `@zitadel/server` npm binary by default and stores runtime data
34
- under `.zitadel/local/`. Remote-server setup can use `--server <url>` without
35
- starting a local runtime. Use `--runtime docker`, `--image`, or
36
- `ZITADEL_LOCAL_IMAGE` for advanced Docker backend debugging.
35
+ under `.zitadel/local/` (SQLite by default). Remote-server setup can use
36
+ `--server <url>` without starting a local runtime. Use `--runtime docker`,
37
+ `--image`, or `ZITADEL_LOCAL_IMAGE` for advanced Docker backend debugging.
37
38
  `setup --server local` creates a project on that local server, asks which
38
- framework to scaffold when the directory is fresh, writes the Next.js app into
39
- the current directory, scaffolds `app/login`, `app/register`, `app/profile`, and
40
- `proxy.ts` for Next 16+ or `middleware.ts` for older Next versions. Fresh
41
- scaffolds also replace the starter `app/page.tsx` with a redirect to `/login`.
39
+ framework to scaffold when the directory is fresh (and which login design to
40
+ use), writes the app into the current directory, and scaffolds the framework's
41
+ idiomatic auth routes plus the proxy layer — for Next.js that means
42
+ `app/login`, `app/register`, `app/profile`, and `proxy.ts` for Next 16+ or
43
+ `middleware.ts` for older versions; other frameworks get their equivalents
44
+ from the same patcher system. In a pre-existing app, setup derives the
45
+ embedding posture from the app instead of assuming a fresh skeleton: the
46
+ scaffolded pages take the `variant="widget"` posture inside your app's own
47
+ shell, recorded in the scaffold manifest and verified by `doctor`. Fresh
48
+ scaffolds also replace the starter home page with a redirect to `/login`.
42
49
  Setup writes `.env.local` and `.zitadel/`, and installs
43
50
  dependencies with the detected package manager. Pass `--skip-install` to install
44
51
  them yourself. The project's default user schema and login flow are provisioned
@@ -80,13 +87,23 @@ and agent UIs may display stderr package-manager progress together with stdout.
80
87
 
81
88
  ## Other commands
82
89
 
90
+ - `zitadel claim` — attach the project to your team (opens the claim page,
91
+ polls for completion; team attachment then shows in `setup`, `status`, and
92
+ `doctor`)
83
93
  - `zitadel doctor` — verify the local runtime and generated project files
94
+ (including scaffold drift and dependency-version alignment)
84
95
  - `zitadel status` — summarise the local runtime and project
85
96
  - `zitadel plan` — validate config and preview sync changes without mutation
86
97
  - `zitadel apply` — validate and upload repo config to Zitadel
98
+ - `zitadel branding eject` — scaffold an editable login template from a design
99
+ - `zitadel schemas list` — list the project's user schemas
87
100
  - `zitadel eject` — remove what setup wrote (alias: `zitadel uninstall`)
88
101
  - `zitadel start|stop|logs|reset` — manage the local runtime
89
102
 
103
+ The full agent-facing contract (JSON envelope, posture rules, claim flow,
104
+ doctor repair) is [`SKILLS.md`](https://github.com/zitadel/nextgen/blob/main/apps/cli/SKILLS.md),
105
+ which ships in this package.
106
+
90
107
  ## Reference
91
108
 
92
109
  <details>
@@ -484,29 +501,60 @@ USAGE
484
501
  centered|split|split-right|hero|minimal]
485
502
 
486
503
  FLAGS
487
- -c, --cwd=<value> Project directory to operate on.
488
- -f, --force Overwrite protected files on conflict.
489
- -n, --non-interactive Disable prompts. Required when scripting or running as an agent.
490
- -s, --server=<value> Override the resolved server URL.
491
- --debug Debug logging.
492
- --design=<option> Login design to eject into .zitadel/branding/ and publish as branding revision 1. When
493
- omitted, the login uses the built-in template; run the `branding eject` command later to
494
- customize.
495
- <options: centered|split|split-right|hero|minimal>
496
- --dev-port=<value> Dev-server port; also the issuer origin registered with Zitadel. Defaults to the detected
497
- port. Use distinct ports to run several scaffolded apps side by side.
498
- --dry-run Preview without mutating files or the platform.
499
- --framework=<option> Framework to target.
500
- <options: next|nuxt|react|vue|solid|svelte|qwik|angular>
501
- --preset=<option> Sign-in preset for the scaffolded schema and login flow (default: password-first).
502
- <options: password-first|passkey-first>
503
- --renderer=<option> Renderer (default: react). Not yet available: web-component.
504
- <options: react>
505
- --skip-install Do not install dependencies after setup updates package.json.
506
- --[no-]telemetry Send anonymous usage analytics. Disable with --no-telemetry.
507
- --use-case=<option> Use case for the scaffolded schema fields: who signs in to the app (default: minimal).
508
- <options: minimal|consumer|business>
509
- --verbose Verbose logging.
504
+ -c, --cwd=<value>
505
+ Project directory to operate on.
506
+
507
+ -f, --force
508
+ Overwrite protected files on conflict.
509
+
510
+ -n, --non-interactive
511
+ Disable prompts. Required when scripting or running as an agent.
512
+
513
+ -s, --server=<value>
514
+ Override the resolved server URL.
515
+
516
+ --debug
517
+ Debug logging.
518
+
519
+ --design=<option>
520
+ Login design to eject into .zitadel/branding/ and publish as branding revision 1. Skips the wizard's design
521
+ question. When omitted in non-interactive runs, the login uses the built-in template; run the `branding eject`
522
+ command later to customize. Split-family designs (split, split-right, hero) collapse their brand pane by container
523
+ width: narrow containers — including widget-posture embeds at card width render the compact brand mark instead
524
+ (logo_url, else hero_url, from .zitadel/branding/branding.json; hero falls back to editable text).
525
+ <options: centered|split|split-right|hero|minimal>
526
+
527
+ --dev-port=<value>
528
+ Dev-server port; also the issuer origin registered with Zitadel. Defaults to the detected port. Use distinct ports
529
+ to run several scaffolded apps side by side.
530
+
531
+ --dry-run
532
+ Preview without mutating files or the platform.
533
+
534
+ --framework=<option>
535
+ Framework to target.
536
+ <options: next|nuxt|react|vue|solid|svelte|qwik|angular>
537
+
538
+ --preset=<option>
539
+ Sign-in preset for the scaffolded schema and login flow (default: password-first).
540
+ <options: password-first|passkey-first>
541
+
542
+ --renderer=<option>
543
+ Renderer (default: react). Not yet available: web-component.
544
+ <options: react>
545
+
546
+ --skip-install
547
+ Do not install dependencies after setup updates package.json.
548
+
549
+ --[no-]telemetry
550
+ Send anonymous usage analytics. Disable with --no-telemetry.
551
+
552
+ --use-case=<option>
553
+ Use case for the scaffolded schema fields: who signs in to the app (default: minimal).
554
+ <options: minimal|consumer|business>
555
+
556
+ --verbose
557
+ Verbose logging.
510
558
 
511
559
  GLOBAL FLAGS
512
560
  --json Format output as json.
package/SKILLS.md CHANGED
@@ -99,7 +99,14 @@ the CLI's help layer, not the envelope.
99
99
  templates by it; the planned `web-component` renderer is not yet available
100
100
  and is rejected if passed), `--dev-port` (dev-server port, also the issuer
101
101
  origin registered with Zitadel — use distinct ports to run several scaffolded
102
- apps side by side), `--preset password-first|passkey-first` (the sign-in
102
+ apps side by side. The app must actually serve this port or the flow API
103
+ rejects its origin on the first submit, so setup makes the port explicit in
104
+ the app's own dev-server config: `server.port` + `strictPort` for Vite
105
+ frameworks, `serve.options.port` for Angular, and — because `next dev` and
106
+ `nuxt dev` take a port only from the command line — `--port` in the
107
+ `package.json` `dev` script for Next and Nuxt. On a pre-existing app that
108
+ means setup edits the `dev` script when it does not already name that port;
109
+ a script already on it is left untouched), `--preset password-first|passkey-first` (the sign-in
103
110
  experience the scaffold starts from: `password-first` is the default —
104
111
  email + password with passkey optional during registration; `passkey-first`
105
112
  enters login on a one-tap passkey step with an email + password fallback;
@@ -114,8 +121,51 @@ the CLI's help layer, not the envelope.
114
121
  the interactive wizard asks this as its final question with the built-in
115
122
  template preselected — omit the flag in non-interactive runs to keep the
116
123
  built-in template and no branding files), `--skip-install`.
124
+ On Next and Nuxt, the scaffolded auth/profile pages derive their embedding
125
+ posture from the app: a fresh scaffold (setup created the skeleton) pins
126
+ `variant="page"` full-page chrome, while a pre-existing app embeds
127
+ `variant="widget"` cards with `theme="auto"` in a layout-neutral wrapper.
128
+ `theme="auto"` follows the OS `prefers-color-scheme`, not the host app's
129
+ own theme — edit the generated page to set `theme="light"` or
130
+ `theme="dark"` when the app pins its scheme. Other frameworks always
131
+ scaffold the page posture. The chosen posture is recorded in the scaffold
132
+ manifest, `doctor --fix` restores managed pages in the recorded posture,
133
+ and editing the generated page is the supported way to change presentation
134
+ — there is no config knob.
135
+ Widget-posture embedding levers: host-page CSS sets `--zl-*` design-token
136
+ custom properties on the element to bridge the app's look through the
137
+ widget's shadow DOM (fonts `--zl-font-family-heading`/`-sans`, radii
138
+ `--zl-radius-*`, primary CTA `--zl-primary`/`--zl-primary-foreground`,
139
+ link color `--zl-color-text-link`); the `suppress-header` attribute
140
+ (wrapper prop `suppressHeader`) visually hides the widget's own heading
141
+ block when the page already carries one, keeping it in the accessibility
142
+ tree. Split-family designs collapse their brand pane by container width —
143
+ at card width they show the compact brand mark (`logo_url`, else
144
+ `hero_url`, from `.zitadel/branding/branding.json`; `hero` falls back to
145
+ editable text), and setup warns when a widget-posture app picks `split`
146
+ or `split-right`.
117
147
  - `plan` — validate config and preview the sync diff without mutating anything.
118
148
  - `apply` — validate and upload repo config to the platform.
149
+ - `plan` and `apply --dry-run` also emit `data.warnings`: non-blocking
150
+ findings as `{path, rule, message}`, the same text the human plan prints as
151
+ `# warning:` lines and `apply` prints through stderr. They never fail a run.
152
+ Two families exist today: flow-definition rules (`warn/…`, mirrored from the
153
+ server's validator) and branding asset reachability. `warn/asset-unreachable`
154
+ and `warn/asset-content-type` come from a bounded HEAD probe of
155
+ `logo_url` / `hero_url` — a URL that is well-formed but dead passes every
156
+ gate and then renders as a 0×0 image with nothing in the console, so the
157
+ probe is the only place it can be caught. It is advisory by design: the
158
+ machine planning is not necessarily the machine that renders the login page.
159
+ The probe only contacts public HTTPS destinations and re-checks every
160
+ redirect; loopback/private/internal targets stay inconclusive instead of
161
+ turning repo config into a network request from the planning host.
162
+ Set `ZITADEL_SKIP_ASSET_PROBE` to turn it off (offline, air-gapped CI, or a
163
+ CDN that only resolves from production) and `ZITADEL_ASSET_PROBE_TIMEOUT_MS`
164
+ to retune the per-URL budget (default 2500).
165
+ - In the human-readable plan, a multi-line field (branding's inlined
166
+ `liquid_template`) renders as `(<n> lines, sha256:…)` when it is created or
167
+ unchanged, and as a changed-line diff when it moved — not as one escaped
168
+ line. Read the file itself for full content.
119
169
  - `schemas list` — inspect the revision history of a user-schema, filtered by
120
170
  `--object-type` (e.g. `human-user`). Non-interactive/`--json` prints one row
121
171
  per revision (newest first); interactive adds a picker that fetches and
@@ -129,7 +179,11 @@ the CLI's help layer, not the envelope.
129
179
  managed config wirings (Vite/Nuxt proxy merges, Angular's `angular.json`
130
180
  proxy and auth routes) through the patchers' idempotent transforms — a
131
181
  detached or missing wiring config fails, an unverifiable one warns, and
132
- `--fix` re-applies it. Boundary migrations converge: a pristine leftover
182
+ `--fix` re-applies it. The Next/Nuxt `dev` script is verified the same way,
183
+ against the port recorded as the development issuer rather than the port the
184
+ script names today: a script moved off that port reports as an unapplied
185
+ config edit (a warning — a `dev` script is not the only way to choose a
186
+ port), and `--fix` restores the registered one. Boundary migrations converge: a pristine leftover
133
187
  `middleware.ts` from a Next 15→16 upgrade is swapped for `proxy.ts`, while
134
188
  an edited one is reported as a conflict instead of creating both (Next
135
189
  rejects the pair). The default local
@@ -138,7 +192,14 @@ the CLI's help layer, not the envelope.
138
192
  files and never replaces an existing scaffolded app file; additive repairs
139
193
  (missing `.gitignore` entries, `.env.example` keys) still append to their
140
194
  targets, and the SDK dependency is re-added only when absent — an existing
141
- version pin is never rewritten.
195
+ version pin is never rewritten. The `dependency-version` check warns when
196
+ an exactly-pinned `@zitadel/*` dependency does not match the CLI's own
197
+ version (the packages release as one train, and a floating
198
+ `npx @zitadel/cli@alpha` can run ahead of the app's pins); ranges,
199
+ dist-tags, and `file:`/`workspace:` specifiers express a deliberate choice
200
+ and are not compared. The repair — an exact-pin install command for the
201
+ project's detected package manager — is emitted in `data.next_commands`
202
+ and quoted in the warning message.
142
203
  - `claim` — attach the project to a team so it becomes permanent. Mints a
143
204
  short-lived link, opens it in a browser, and blocks until the developer
144
205
  finishes signing in there, then records `claimed_at` and `team_id` in
@@ -168,8 +229,13 @@ the CLI's help layer, not the envelope.
168
229
  - `eject` (alias `uninstall`) — remove managed files and local Zitadel state;
169
230
  requires `--force` when non-interactive.
170
231
  - `start` — start the managed local Zitadel server and persist runtime metadata
171
- under `.zitadel/local/runtime.json`. Use `--runtime docker` or `--image` for
172
- the Docker backend.
232
+ under `.zitadel/local/runtime.json`. The binary runtime defaults to SQLite
233
+ under `.zitadel/local/nextgen-data/`. Runtime metadata reports the published
234
+ server package version; the repository contributor wrapper reports the
235
+ `dev+<short-commit>` source build it launched. That label names the revision
236
+ the binary was built from, which after a Moon cache hit can be an earlier
237
+ commit whose server sources are byte-identical. Use `--runtime docker` or
238
+ `--image` for the Docker backend.
173
239
  - `stop` — stop the managed runtime while preserving
174
240
  `.zitadel/local/nextgen-data`. Use `stop --all` to sweep all discovered
175
241
  host-wide CLI-managed local runtime processes, including healthy runtimes
@@ -263,9 +329,9 @@ validator (`E_VALIDATION` lists rule ids such as `no-script-tag` and
263
329
  must be absolute `https://`. Keep exactly one descriptor in
264
330
  `.zitadel/branding/` — extra `*.json` files there fail the scan.
265
331
  Server-provisioned defaults remain a fallback for non-CLI project
266
- creation, but CLI-created projects are authored from local files first. Flow create, read, list, and
267
- update are available, while the server enforces lifecycle rules such as
268
- draft-only edits. Managed files carry a marker comment; `eject` removes only
332
+ creation, but CLI-created projects are authored from local files first. Flow create, read, list,
333
+ update, and delete are available, while the server enforces lifecycle rules
334
+ such as draft-only edits. Managed files carry a marker comment; `eject` removes only
269
335
  files that still carry it, preserving anything the user replaced. For app-local
270
336
  development, `--server local` resolves through `.zitadel/local/runtime.json` and
271
337
  requires a healthy
@@ -1,4 +1,4 @@
1
- import { E as ZitadelError } from "./oclif-CL6-M13m.mjs";
1
+ import { E as ZitadelError } from "./oclif-CanO3zdt.mjs";
2
2
  import { isAbsolute, join, relative, resolve } from "node:path";
3
3
  import { readFileSync } from "node:fs";
4
4
  //#region src/lib/branding/index.ts
@@ -76,4 +76,4 @@ function toLocalBrandingBody(canonicalWire, localData) {
76
76
  //#endregion
77
77
  export { toLocalBrandingBody as a, toBrandingWireBody as i, readDescriptorTemplate as n, resolveTemplatePath as r, BRANDING_DIR as t };
78
78
 
79
- //# sourceMappingURL=branding-BXj028-W.mjs.map
79
+ //# sourceMappingURL=branding-cygSBPkV.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"branding-BXj028-W.mjs","names":[],"sources":["../src/lib/branding/index.ts"],"sourcesContent":["import { readFileSync } from \"node:fs\";\nimport { isAbsolute, join, relative, resolve } from \"node:path\";\n\nimport { ZitadelError } from \"../errors\";\n\n/**\n * Relative directory (from the project root) where local branding files live:\n * the `branding.json` descriptor plus the sibling `.liquid` template it\n * references. Owned here so commands, syncers, and tests share one source of\n * truth; only `*.json` files are sync-discovered, the template rides along\n * via `liquid_template_file`.\n */\nexport const BRANDING_DIR = \".zitadel/branding\";\n\ntype BrandingDescriptor = {\n liquid_template?: string;\n liquid_template_file?: string;\n [key: string]: unknown;\n};\n\n/**\n * Resolves a descriptor's `liquid_template_file` reference to an absolute\n * path. References are relative to the descriptor's directory (all\n * descriptors live flat in {@link BRANDING_DIR}) and must stay inside the\n * project — a reference escaping `cwd` is an error because apply would later\n * write server state back to that path.\n */\nexport function resolveTemplatePath(cwd: string, ref: string): string {\n const absolute = isAbsolute(ref) ? ref : resolve(join(cwd, BRANDING_DIR), ref);\n const rel = relative(cwd, absolute);\n if (rel.startsWith(\"..\") || isAbsolute(rel)) {\n throw new ZitadelError(\n \"E_VALIDATION\",\n `liquid_template_file ${JSON.stringify(ref)} points outside the project`,\n { hint: `Keep the template next to its descriptor in ${BRANDING_DIR}/.` },\n );\n }\n return absolute;\n}\n\n/**\n * Returns the template string a descriptor carries — the inline\n * `liquid_template`, or the content of `liquid_template_file`. Returns\n * undefined when the descriptor has neither (a legal descriptor that only\n * sets layout/asset URLs).\n */\nexport function readDescriptorTemplate(cwd: string, data: object): string | undefined {\n const descriptor = data as BrandingDescriptor;\n if (typeof descriptor.liquid_template === \"string\") {\n return descriptor.liquid_template;\n }\n if (typeof descriptor.liquid_template_file !== \"string\") {\n return undefined;\n }\n const path = resolveTemplatePath(cwd, descriptor.liquid_template_file);\n try {\n return readFileSync(path, \"utf8\");\n } catch (error) {\n throw new ZitadelError(\n \"E_VALIDATION\",\n `liquid_template_file ${JSON.stringify(descriptor.liquid_template_file)} cannot be read`,\n {\n hint: \"Create the template file or fix the reference; the `branding eject` command scaffolds a starting point.\",\n details: { cause: error instanceof Error ? error.message : String(error) },\n },\n );\n }\n}\n\n/**\n * Converts a local descriptor to the wire body of `POST /branding`: strips\n * the editor `$schema` affordance and replaces `liquid_template_file` with\n * the inlined template content. Non-throwing on a missing template file (the\n * field is simply left out) — `validate` reports that case with a hint\n * before any planning happens.\n */\nexport function toBrandingWireBody(cwd: string, data: object): object {\n const { $schema, liquid_template_file, ...rest } = data as BrandingDescriptor & {\n $schema?: string;\n };\n void $schema;\n const out: Record<string, unknown> = { ...rest };\n if (typeof liquid_template_file === \"string\" && out.liquid_template === undefined) {\n try {\n out.liquid_template = readFileSync(resolveTemplatePath(cwd, liquid_template_file), \"utf8\");\n } catch {\n // Reported by validate(); keep normalize/hashing total.\n }\n }\n return out;\n}\n\n/**\n * Converts the server's canonical wire body back to the local descriptor\n * form: when the local descriptor references a template file, the template\n * string moves back out of the JSON into that reference. The caller writes\n * the template content to the referenced file separately.\n */\nexport function toLocalBrandingBody(canonicalWire: object, localData: object): object {\n const local = localData as BrandingDescriptor;\n if (typeof local.liquid_template_file !== \"string\") {\n return canonicalWire;\n }\n const { liquid_template, ...rest } = canonicalWire as BrandingDescriptor;\n void liquid_template;\n return { ...rest, liquid_template_file: local.liquid_template_file };\n}\n"],"mappings":";;;;;;;;;;;AAYA,MAAa,eAAe;;;;;;;;AAe5B,SAAgB,oBAAoB,KAAa,KAAqB;CACpE,MAAM,WAAW,WAAW,IAAI,GAAG,MAAM,QAAQ,KAAK,KAAK,aAAa,EAAE,IAAI;CAC9E,MAAM,MAAM,SAAS,KAAK,SAAS;AACnC,KAAI,IAAI,WAAW,KAAK,IAAI,WAAW,IAAI,CACzC,OAAM,IAAI,aACR,gBACA,wBAAwB,KAAK,UAAU,IAAI,CAAC,8BAC5C,EAAE,MAAM,+CAA+C,aAAa,KAAK,CAC1E;AAEH,QAAO;;;;;;;;AAST,SAAgB,uBAAuB,KAAa,MAAkC;CACpF,MAAM,aAAa;AACnB,KAAI,OAAO,WAAW,oBAAoB,SACxC,QAAO,WAAW;AAEpB,KAAI,OAAO,WAAW,yBAAyB,SAC7C;CAEF,MAAM,OAAO,oBAAoB,KAAK,WAAW,qBAAqB;AACtE,KAAI;AACF,SAAO,aAAa,MAAM,OAAO;UAC1B,OAAO;AACd,QAAM,IAAI,aACR,gBACA,wBAAwB,KAAK,UAAU,WAAW,qBAAqB,CAAC,kBACxE;GACE,MAAM;GACN,SAAS,EAAE,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,EAAE;GAC3E,CACF;;;;;;;;;;AAWL,SAAgB,mBAAmB,KAAa,MAAsB;CACpE,MAAM,EAAE,SAAS,sBAAsB,GAAG,SAAS;CAInD,MAAM,MAA+B,EAAE,GAAG,MAAM;AAChD,KAAI,OAAO,yBAAyB,YAAY,IAAI,oBAAoB,KAAA,EACtE,KAAI;AACF,MAAI,kBAAkB,aAAa,oBAAoB,KAAK,qBAAqB,EAAE,OAAO;SACpF;AAIV,QAAO;;;;;;;;AAST,SAAgB,oBAAoB,eAAuB,WAA2B;CACpF,MAAM,QAAQ;AACd,KAAI,OAAO,MAAM,yBAAyB,SACxC,QAAO;CAET,MAAM,EAAE,iBAAiB,GAAG,SAAS;AAErC,QAAO;EAAE,GAAG;EAAM,sBAAsB,MAAM;EAAsB"}
1
+ {"version":3,"file":"branding-cygSBPkV.mjs","names":[],"sources":["../src/lib/branding/index.ts"],"sourcesContent":["import { readFileSync } from \"node:fs\";\nimport { isAbsolute, join, relative, resolve } from \"node:path\";\n\nimport { ZitadelError } from \"../errors\";\n\n/**\n * Relative directory (from the project root) where local branding files live:\n * the `branding.json` descriptor plus the sibling `.liquid` template it\n * references. Owned here so commands, syncers, and tests share one source of\n * truth; only `*.json` files are sync-discovered, the template rides along\n * via `liquid_template_file`.\n */\nexport const BRANDING_DIR = \".zitadel/branding\";\n\ntype BrandingDescriptor = {\n liquid_template?: string;\n liquid_template_file?: string;\n [key: string]: unknown;\n};\n\n/**\n * Resolves a descriptor's `liquid_template_file` reference to an absolute\n * path. References are relative to the descriptor's directory (all\n * descriptors live flat in {@link BRANDING_DIR}) and must stay inside the\n * project — a reference escaping `cwd` is an error because apply would later\n * write server state back to that path.\n */\nexport function resolveTemplatePath(cwd: string, ref: string): string {\n const absolute = isAbsolute(ref) ? ref : resolve(join(cwd, BRANDING_DIR), ref);\n const rel = relative(cwd, absolute);\n if (rel.startsWith(\"..\") || isAbsolute(rel)) {\n throw new ZitadelError(\n \"E_VALIDATION\",\n `liquid_template_file ${JSON.stringify(ref)} points outside the project`,\n { hint: `Keep the template next to its descriptor in ${BRANDING_DIR}/.` },\n );\n }\n return absolute;\n}\n\n/**\n * Returns the template string a descriptor carries — the inline\n * `liquid_template`, or the content of `liquid_template_file`. Returns\n * undefined when the descriptor has neither (a legal descriptor that only\n * sets layout/asset URLs).\n */\nexport function readDescriptorTemplate(cwd: string, data: object): string | undefined {\n const descriptor = data as BrandingDescriptor;\n if (typeof descriptor.liquid_template === \"string\") {\n return descriptor.liquid_template;\n }\n if (typeof descriptor.liquid_template_file !== \"string\") {\n return undefined;\n }\n const path = resolveTemplatePath(cwd, descriptor.liquid_template_file);\n try {\n return readFileSync(path, \"utf8\");\n } catch (error) {\n throw new ZitadelError(\n \"E_VALIDATION\",\n `liquid_template_file ${JSON.stringify(descriptor.liquid_template_file)} cannot be read`,\n {\n hint: \"Create the template file or fix the reference; the `branding eject` command scaffolds a starting point.\",\n details: { cause: error instanceof Error ? error.message : String(error) },\n },\n );\n }\n}\n\n/**\n * Converts a local descriptor to the wire body of `POST /branding`: strips\n * the editor `$schema` affordance and replaces `liquid_template_file` with\n * the inlined template content. Non-throwing on a missing template file (the\n * field is simply left out) — `validate` reports that case with a hint\n * before any planning happens.\n */\nexport function toBrandingWireBody(cwd: string, data: object): object {\n const { $schema, liquid_template_file, ...rest } = data as BrandingDescriptor & {\n $schema?: string;\n };\n void $schema;\n const out: Record<string, unknown> = { ...rest };\n if (typeof liquid_template_file === \"string\" && out.liquid_template === undefined) {\n try {\n out.liquid_template = readFileSync(resolveTemplatePath(cwd, liquid_template_file), \"utf8\");\n } catch {\n // Reported by validate(); keep normalize/hashing total.\n }\n }\n return out;\n}\n\n/**\n * Converts the server's canonical wire body back to the local descriptor\n * form: when the local descriptor references a template file, the template\n * string moves back out of the JSON into that reference. The caller writes\n * the template content to the referenced file separately.\n */\nexport function toLocalBrandingBody(canonicalWire: object, localData: object): object {\n const local = localData as BrandingDescriptor;\n if (typeof local.liquid_template_file !== \"string\") {\n return canonicalWire;\n }\n const { liquid_template, ...rest } = canonicalWire as BrandingDescriptor;\n void liquid_template;\n return { ...rest, liquid_template_file: local.liquid_template_file };\n}\n"],"mappings":";;;;;;;;;;;AAYA,MAAa,eAAe;;;;;;;;AAe5B,SAAgB,oBAAoB,KAAa,KAAqB;CACpE,MAAM,WAAW,WAAW,IAAI,GAAG,MAAM,QAAQ,KAAK,KAAK,aAAa,EAAE,IAAI;CAC9E,MAAM,MAAM,SAAS,KAAK,SAAS;AACnC,KAAI,IAAI,WAAW,KAAK,IAAI,WAAW,IAAI,CACzC,OAAM,IAAI,aACR,gBACA,wBAAwB,KAAK,UAAU,IAAI,CAAC,8BAC5C,EAAE,MAAM,+CAA+C,aAAa,KAAK,CAC1E;AAEH,QAAO;;;;;;;;AAST,SAAgB,uBAAuB,KAAa,MAAkC;CACpF,MAAM,aAAa;AACnB,KAAI,OAAO,WAAW,oBAAoB,SACxC,QAAO,WAAW;AAEpB,KAAI,OAAO,WAAW,yBAAyB,SAC7C;CAEF,MAAM,OAAO,oBAAoB,KAAK,WAAW,qBAAqB;AACtE,KAAI;AACF,SAAO,aAAa,MAAM,OAAO;UAC1B,OAAO;AACd,QAAM,IAAI,aACR,gBACA,wBAAwB,KAAK,UAAU,WAAW,qBAAqB,CAAC,kBACxE;GACE,MAAM;GACN,SAAS,EAAE,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,EAAE;GAC3E,CACF;;;;;;;;;;AAWL,SAAgB,mBAAmB,KAAa,MAAsB;CACpE,MAAM,EAAE,SAAS,sBAAsB,GAAG,SAAS;CAInD,MAAM,MAA+B,EAAE,GAAG,MAAM;AAChD,KAAI,OAAO,yBAAyB,YAAY,IAAI,oBAAoB,KAAA,EACtE,KAAI;AACF,MAAI,kBAAkB,aAAa,oBAAoB,KAAK,qBAAqB,EAAE,OAAO;SACpF;AAIV,QAAO;;;;;;;;AAST,SAAgB,oBAAoB,eAAuB,WAA2B;CACpF,MAAM,QAAQ;AACd,KAAI,OAAO,MAAM,yBAAyB,SACxC,QAAO;CAET,MAAM,EAAE,iBAAiB,GAAG,SAAS;AAErC,QAAO;EAAE,GAAG;EAAM,sBAAsB,MAAM;EAAsB"}
@@ -1,4 +1,4 @@
1
- import { A as serverKind, k as publicCliCommand } from "./oclif-CL6-M13m.mjs";
1
+ import { M as serverKind, j as publicCliCommand } from "./oclif-CanO3zdt.mjs";
2
2
  //#region src/lib/claim-state.ts
3
3
  /**
4
4
  * Whether the local record says a team owns this project.
@@ -63,4 +63,4 @@ function claimCommand(cliVersion) {
63
63
  //#endregion
64
64
  export { isAttached as i, claimCommand as n, claimState as r, claimAction as t };
65
65
 
66
- //# sourceMappingURL=claim-state-BTGInAaN.mjs.map
66
+ //# sourceMappingURL=claim-state-DlfD6Res.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"claim-state-BTGInAaN.mjs","names":[],"sources":["../src/lib/claim-state.ts"],"sourcesContent":["import { serverKind } from \"./oclif/server-kind\";\nimport type { ZitadelSecret } from \"./project\";\nimport { publicCliCommand } from \"./public-cli\";\n\n/**\n * Whether this project is attached to a team, as the CLI can tell locally.\n *\n * `not-applicable` is its own variant rather than a boolean flag on the caller\n * because \"there is nothing to attach here\" and \"nothing is attached yet\" want\n * opposite output: the first prints nothing at all, the second nudges.\n */\nexport type ClaimState =\n | { kind: \"attached\"; team_id: string; claimed_at: string }\n | { kind: \"detached\" }\n | { kind: \"not-applicable\" };\n\n/**\n * Whether the local record says a team owns this project.\n *\n * Both fields or neither: a half-written record is not an attachment, and\n * treating it as one would hand callers a `team_id` with no idea when it\n * landed. Shared with `zitadel claim`, which asks the same question before it\n * decides to skip, so the two cannot drift as the record grows fields.\n *\n * Deliberately separate from {@link claimState}: the command must answer this\n * on any server, while the nudges only ask it on the cloud.\n */\nexport function isAttached(\n secret: Pick<ZitadelSecret, \"claimed_at\" | \"team_id\">,\n): secret is { claimed_at: string; team_id: string } {\n return Boolean(secret.claimed_at && secret.team_id);\n}\n\n/**\n * Decides the attachment state from local state alone.\n *\n * **Why no network call.** The contract exposes no ambient claim-state read:\n * `getClaimStatus` answers for one live challenge and rejects a secret that did\n * not mint it, and claim attributes were deliberately kept off\n * `GET /projects/{id}` because they belong to the grant, not the project. So\n * `.zitadel/secret` is the only source, which is also why `status` and `doctor`\n * stay fast and work offline. `zitadel claim` reaches the same conclusion the\n * same way before it decides to skip.\n *\n * **Why cloud only.** Claiming attaches a project to a team on the platform.\n * A local or self-hosted server has no such platform, and `--server local` is\n * the documented dev loop, so an ungated nudge would advertise an impossible\n * action for the entire life of a local project.\n *\n * The local record can be stale in one direction: a project claimed from\n * another machine, or a `.zitadel/secret` restored from a backup, reads as\n * `detached` here. That is tolerable because everything downstream is\n * advisory, and `zitadel claim` itself turns the resulting `409` into a clean\n * skip rather than an error.\n */\nexport function claimState(input: {\n secret: Pick<ZitadelSecret, \"claimed_at\" | \"team_id\">;\n server: string;\n}): ClaimState {\n if (serverKind.value(input.server) !== \"cloud\") {\n return { kind: \"not-applicable\" };\n }\n if (isAttached(input.secret)) {\n return {\n kind: \"attached\",\n team_id: input.secret.team_id,\n claimed_at: input.secret.claimed_at,\n };\n }\n return { kind: \"detached\" };\n}\n\n/**\n * The nudge for `next_actions`, quoting the command the way `journey-guidance`\n * does.\n *\n * Deliberately says nothing about deletion: unattached projects are framed as\n * temporary, but the epic's 14-day lifetime is not enforced anywhere yet, so\n * promising it would be a lie the product cannot keep.\n */\nexport function claimAction(cliVersion: string): string {\n return (\n \"This project is temporary until you attach it to a team: run \" +\n `${publicCliCommand(\"claim\", cliVersion)} to make it permanent. ` +\n \"Nothing about the project changes, so users, passkeys, and the issuer keep working.\"\n );\n}\n\n/** The command for `next_commands`. */\nexport function claimCommand(cliVersion: string): string {\n return publicCliCommand(\"claim\", cliVersion);\n}\n"],"mappings":";;;;;;;;;;;;;AA2BA,SAAgB,WACd,QACmD;AACnD,QAAO,QAAQ,OAAO,cAAc,OAAO,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;AAyBrD,SAAgB,WAAW,OAGZ;AACb,KAAI,WAAW,MAAM,MAAM,OAAO,KAAK,QACrC,QAAO,EAAE,MAAM,kBAAkB;AAEnC,KAAI,WAAW,MAAM,OAAO,CAC1B,QAAO;EACL,MAAM;EACN,SAAS,MAAM,OAAO;EACtB,YAAY,MAAM,OAAO;EAC1B;AAEH,QAAO,EAAE,MAAM,YAAY;;;;;;;;;;AAW7B,SAAgB,YAAY,YAA4B;AACtD,QACE,gEACG,iBAAiB,SAAS,WAAW,CAAC;;;AAM7C,SAAgB,aAAa,YAA4B;AACvD,QAAO,iBAAiB,SAAS,WAAW"}
1
+ {"version":3,"file":"claim-state-DlfD6Res.mjs","names":[],"sources":["../src/lib/claim-state.ts"],"sourcesContent":["import { serverKind } from \"./oclif/server-kind\";\nimport type { ZitadelSecret } from \"./project\";\nimport { publicCliCommand } from \"./public-cli\";\n\n/**\n * Whether this project is attached to a team, as the CLI can tell locally.\n *\n * `not-applicable` is its own variant rather than a boolean flag on the caller\n * because \"there is nothing to attach here\" and \"nothing is attached yet\" want\n * opposite output: the first prints nothing at all, the second nudges.\n */\nexport type ClaimState =\n | { kind: \"attached\"; team_id: string; claimed_at: string }\n | { kind: \"detached\" }\n | { kind: \"not-applicable\" };\n\n/**\n * Whether the local record says a team owns this project.\n *\n * Both fields or neither: a half-written record is not an attachment, and\n * treating it as one would hand callers a `team_id` with no idea when it\n * landed. Shared with `zitadel claim`, which asks the same question before it\n * decides to skip, so the two cannot drift as the record grows fields.\n *\n * Deliberately separate from {@link claimState}: the command must answer this\n * on any server, while the nudges only ask it on the cloud.\n */\nexport function isAttached(\n secret: Pick<ZitadelSecret, \"claimed_at\" | \"team_id\">,\n): secret is { claimed_at: string; team_id: string } {\n return Boolean(secret.claimed_at && secret.team_id);\n}\n\n/**\n * Decides the attachment state from local state alone.\n *\n * **Why no network call.** The contract exposes no ambient claim-state read:\n * `getClaimStatus` answers for one live challenge and rejects a secret that did\n * not mint it, and claim attributes were deliberately kept off\n * `GET /projects/{id}` because they belong to the grant, not the project. So\n * `.zitadel/secret` is the only source, which is also why `status` and `doctor`\n * stay fast and work offline. `zitadel claim` reaches the same conclusion the\n * same way before it decides to skip.\n *\n * **Why cloud only.** Claiming attaches a project to a team on the platform.\n * A local or self-hosted server has no such platform, and `--server local` is\n * the documented dev loop, so an ungated nudge would advertise an impossible\n * action for the entire life of a local project.\n *\n * The local record can be stale in one direction: a project claimed from\n * another machine, or a `.zitadel/secret` restored from a backup, reads as\n * `detached` here. That is tolerable because everything downstream is\n * advisory, and `zitadel claim` itself turns the resulting `409` into a clean\n * skip rather than an error.\n */\nexport function claimState(input: {\n secret: Pick<ZitadelSecret, \"claimed_at\" | \"team_id\">;\n server: string;\n}): ClaimState {\n if (serverKind.value(input.server) !== \"cloud\") {\n return { kind: \"not-applicable\" };\n }\n if (isAttached(input.secret)) {\n return {\n kind: \"attached\",\n team_id: input.secret.team_id,\n claimed_at: input.secret.claimed_at,\n };\n }\n return { kind: \"detached\" };\n}\n\n/**\n * The nudge for `next_actions`, quoting the command the way `journey-guidance`\n * does.\n *\n * Deliberately says nothing about deletion: unattached projects are framed as\n * temporary, but the epic's 14-day lifetime is not enforced anywhere yet, so\n * promising it would be a lie the product cannot keep.\n */\nexport function claimAction(cliVersion: string): string {\n return (\n \"This project is temporary until you attach it to a team: run \" +\n `${publicCliCommand(\"claim\", cliVersion)} to make it permanent. ` +\n \"Nothing about the project changes, so users, passkeys, and the issuer keep working.\"\n );\n}\n\n/** The command for `next_commands`. */\nexport function claimCommand(cliVersion: string): string {\n return publicCliCommand(\"claim\", cliVersion);\n}\n"],"mappings":";;;;;;;;;;;;;AA2BA,SAAgB,WACd,QACmD;AACnD,QAAO,QAAQ,OAAO,cAAc,OAAO,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;AAyBrD,SAAgB,WAAW,OAGZ;AACb,KAAI,WAAW,MAAM,MAAM,OAAO,KAAK,QACrC,QAAO,EAAE,MAAM,kBAAkB;AAEnC,KAAI,WAAW,MAAM,OAAO,CAC1B,QAAO;EACL,MAAM;EACN,SAAS,MAAM,OAAO;EACtB,YAAY,MAAM,OAAO;EAC1B;AAEH,QAAO,EAAE,MAAM,YAAY;;;;;;;;;;AAW7B,SAAgB,YAAY,YAA4B;AACtD,QACE,gEACG,iBAAiB,SAAS,WAAW,CAAC;;;AAM7C,SAAgB,aAAa,YAA4B;AACvD,QAAO,iBAAiB,SAAS,WAAW"}
@@ -1,7 +1,7 @@
1
- import { k as publicCliCommand, t as BaseCommand } from "../oclif-CL6-M13m.mjs";
2
- import { l as readZitadelSecret } from "../project-yejJSfdJ.mjs";
3
- import { a as buildSyncPlan, i as summarizePlan, l as makeSyncers, n as enumeratePlanResources, r as renderPlan, s as runSyncLoop, t as collectPlanWarnings } from "../sync-Cx2_4rXW.mjs";
4
- import { t as environmentSchema } from "../environment-CuHKxtIO.mjs";
1
+ import { j as publicCliCommand, t as BaseCommand } from "../oclif-CanO3zdt.mjs";
2
+ import { l as readZitadelSecret } from "../project-CHhot85s.mjs";
3
+ import { a as buildSyncPlan, i as summarizePlan, l as makeSyncers, n as enumeratePlanResources, r as renderPlan, s as runSyncLoop, t as collectPlanWarnings } from "../sync-Bd6vASgg.mjs";
4
+ import { t as environmentSchema } from "../environment-rjRVkJjW.mjs";
5
5
  import { Flags } from "@oclif/core";
6
6
  import { createZitadelClient } from "@zitadel/api/client";
7
7
  import { consola as consola$1 } from "consola";
@@ -1,6 +1,7 @@
1
- import { E as ZitadelError, T as stableStringify, k as publicCliCommand, t as BaseCommand } from "../../oclif-CL6-M13m.mjs";
2
- import { t as hasZitadelConfig } from "../../project-yejJSfdJ.mjs";
3
- import { t as BRANDING_DIR } from "../../branding-BXj028-W.mjs";
1
+ import { t as BRANDING_DESIGN_INFO } from "../../designs-Ckz18Dpo.mjs";
2
+ import { E as ZitadelError, O as normalizePublicCliJson, T as stableStringify, j as publicCliCommand, k as normalizePublicCliProse, t as BaseCommand } from "../../oclif-CanO3zdt.mjs";
3
+ import { t as hasZitadelConfig } from "../../project-CHhot85s.mjs";
4
+ import { t as BRANDING_DIR } from "../../branding-cygSBPkV.mjs";
4
5
  import { mkdir, writeFile } from "node:fs/promises";
5
6
  import { dirname, join } from "node:path";
6
7
  import { cancel, isCancel, select } from "@clack/prompts";
@@ -57,9 +58,9 @@ var BrandingEject = class BrandingEject extends BaseCommand {
57
58
  ...branding
58
59
  })}\n`, force);
59
60
  await write(DEFAULT_BRANDING_TEMPLATE_PATH, template, force);
60
- await write(join(BRANDING_DIR, "README.md"), brandingReadmeContent(), false).catch(swallowConflict);
61
+ await write(join(BRANDING_DIR, "README.md"), normalizePublicCliProse(brandingReadmeContent(), this.meta.cliVersion), false).catch(swallowConflict);
61
62
  const brandingMeta = metaSchemaFiles().find((f) => f.name === "branding.json");
62
- if (brandingMeta) await write(join(META_SCHEMA_DIR, brandingMeta.name), `${stableStringify(brandingMeta.body)}\n`, false).catch(swallowConflict);
63
+ if (brandingMeta) await write(join(META_SCHEMA_DIR, brandingMeta.name), `${stableStringify(normalizePublicCliJson(brandingMeta.body, this.meta.cliVersion))}\n`, false).catch(swallowConflict);
63
64
  for (const file of filesWritten) consola$1.info(`Wrote ${file}`);
64
65
  consola$1.success(`Ejected the ${design} design into ${BRANDING_DIR}/`);
65
66
  return this.emit({
@@ -77,33 +78,11 @@ var BrandingEject = class BrandingEject extends BaseCommand {
77
78
  const choice = await select({
78
79
  message: "Which design do you want to start from?",
79
80
  initialValue: DEFAULT_BRANDING_DESIGN,
80
- options: [
81
- {
82
- value: "centered",
83
- label: "Centered",
84
- hint: "the bundled default, card centred on page"
85
- },
86
- {
87
- value: "split",
88
- label: "Split",
89
- hint: "brand panel left, form right"
90
- },
91
- {
92
- value: "split-right",
93
- label: "Split (reversed)",
94
- hint: "form left, brand panel right"
95
- },
96
- {
97
- value: "hero",
98
- label: "Hero",
99
- hint: "landing-style brand pane left, form right"
100
- },
101
- {
102
- value: "minimal",
103
- label: "Minimal",
104
- hint: "no card chrome, fields straight on the page"
105
- }
106
- ]
81
+ options: BRANDING_DESIGNS.map((design) => ({
82
+ value: design,
83
+ label: BRANDING_DESIGN_INFO[design].label,
84
+ hint: BRANDING_DESIGN_INFO[design].hint
85
+ }))
107
86
  });
108
87
  if (isCancel(choice)) {
109
88
  cancel("Eject cancelled.");
@@ -1 +1 @@
1
- {"version":3,"file":"eject.mjs","names":[],"sources":["../../../src/commands/branding/eject.ts"],"sourcesContent":["import { mkdir, writeFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\n\nimport { cancel, isCancel, select } from \"@clack/prompts\";\nimport { Flags } from \"@oclif/core\";\nimport { consola } from \"consola\";\n\nimport {\n BRANDING_DESIGNS,\n DEFAULT_BRANDING_CONFIG_PATH,\n DEFAULT_BRANDING_DESIGN,\n DEFAULT_BRANDING_TEMPLATE_PATH,\n brandingReadmeContent,\n getDefaultBrandingConfig,\n} from \"@zitadel/config/defaults\";\nimport { BRANDING_FILE_SCHEMA_REF, META_SCHEMA_DIR, metaSchemaFiles } from \"@zitadel/config/meta-schemas\";\n\nimport { BRANDING_DIR } from \"../../lib/branding\";\nimport { ZitadelError } from \"../../lib/errors\";\nimport { stableStringify } from \"../../lib/json\";\nimport { BaseCommand, type JsonEnvelope } from \"../../lib/oclif\";\nimport { hasZitadelConfig } from \"../../lib/project\";\nimport { publicCliCommand } from \"../../lib/public-cli\";\n\n/**\n * The `branding eject` command — take ownership of the login template.\n *\n * Scaffolds `.zitadel/branding/` from a shipped design: the `branding.json`\n * descriptor, the `login.liquid` template it references, a README, and the\n * branding dialect meta-schema (for projects set up before it existed).\n * Purely local — nothing is uploaded; the first `zitadel apply` publishes\n * revision 1. Ejecting the template is the last rung of the branding\n * override ladder (docs/design/branding/override-ladder.md); unrelated to\n * `zitadel eject`, which removes the Zitadel integration.\n */\nexport default class BrandingEject extends BaseCommand {\n static override description =\n \"Take ownership of the login template: scaffold .zitadel/branding/ from a shipped design.\";\n static override flags = {\n design: Flags.string({\n description: `Design to start from (default: ${DEFAULT_BRANDING_DESIGN}).`,\n options: [...BRANDING_DESIGNS],\n }),\n };\n\n async run(): Promise<JsonEnvelope> {\n const { flags } = await this.parse(BrandingEject);\n await this.toMeta(flags);\n const { cwd, force, nonInteractive } = this.meta;\n\n if (!(await hasZitadelConfig(cwd))) {\n throw new ZitadelError(\"E_VALIDATION\", \"This directory is not a Zitadel project\", {\n hint: \"Run setup first; branding files live under .zitadel/.\",\n nextCommands: [publicCliCommand(\"setup\", this.meta.cliVersion)],\n });\n }\n\n const design = flags.design ?? (await this.promptDesign(nonInteractive));\n this.recordTelemetry({ design, force });\n const { branding, template } = getDefaultBrandingConfig(design);\n\n const filesWritten: string[] = [];\n const write = async (relPath: string, content: string, overwrite: boolean): Promise<void> => {\n const dest = join(cwd, relPath);\n await mkdir(dirname(dest), { recursive: true });\n try {\n await writeFile(dest, content, overwrite ? undefined : { flag: \"wx\" });\n filesWritten.push(relPath);\n } catch (error) {\n if (isErrno(error, \"EEXIST\")) {\n throw new ZitadelError(\"E_CONFLICT\", `${relPath} already exists`, {\n hint: \"Rerun with --force to replace it, or edit the existing file directly.\",\n });\n }\n throw error;\n }\n };\n\n await write(\n DEFAULT_BRANDING_CONFIG_PATH,\n `${stableStringify({ $schema: BRANDING_FILE_SCHEMA_REF, ...branding })}\\n`,\n force,\n );\n await write(DEFAULT_BRANDING_TEMPLATE_PATH, template, force);\n\n // Never overwrite a hand-edited README; setups from before the branding\n // dialect also miss the meta-schema, so materialize it when absent.\n await write(join(BRANDING_DIR, \"README.md\"), brandingReadmeContent(), false).catch(\n swallowConflict,\n );\n const brandingMeta = metaSchemaFiles().find((f) => f.name === \"branding.json\");\n if (brandingMeta) {\n await write(\n join(META_SCHEMA_DIR, brandingMeta.name),\n `${stableStringify(brandingMeta.body)}\\n`,\n false,\n ).catch(swallowConflict);\n }\n\n for (const file of filesWritten) {\n consola.info(`Wrote ${file}`);\n }\n consola.success(`Ejected the ${design} design into ${BRANDING_DIR}/`);\n\n return this.emit({\n status: \"ok\",\n data: {\n design,\n files_written: filesWritten,\n next_actions: [\n `Edit ${DEFAULT_BRANDING_TEMPLATE_PATH} — the login UI is yours now.`,\n \"Plan validates the template and shows the pending revision; apply publishes it.\",\n ],\n next_commands: [\n publicCliCommand(\"plan\", this.meta.cliVersion),\n publicCliCommand(\"apply\", this.meta.cliVersion),\n ],\n },\n });\n }\n\n private async promptDesign(nonInteractive: boolean): Promise<string> {\n if (nonInteractive) {\n return DEFAULT_BRANDING_DESIGN;\n }\n const choice = await select({\n message: \"Which design do you want to start from?\",\n initialValue: DEFAULT_BRANDING_DESIGN as string,\n options: [\n { value: \"centered\", label: \"Centered\", hint: \"the bundled default, card centred on page\" },\n { value: \"split\", label: \"Split\", hint: \"brand panel left, form right\" },\n { value: \"split-right\", label: \"Split (reversed)\", hint: \"form left, brand panel right\" },\n { value: \"hero\", label: \"Hero\", hint: \"landing-style brand pane left, form right\" },\n { value: \"minimal\", label: \"Minimal\", hint: \"no card chrome, fields straight on the page\" },\n ],\n });\n if (isCancel(choice)) {\n cancel(\"Eject cancelled.\");\n throw new ZitadelError(\"E_VALIDATION\", \"Eject cancelled by user\");\n }\n return choice;\n }\n}\n\nfunction swallowConflict(error: unknown): void {\n if (error instanceof ZitadelError && error.code === \"E_CONFLICT\") {\n return;\n }\n throw error;\n}\n\nfunction isErrno(error: unknown, code: string): boolean {\n return (\n typeof error === \"object\" &&\n error !== null &&\n \"code\" in error &&\n (error as NodeJS.ErrnoException).code === code\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"eject.mjs","names":[],"sources":["../../../src/commands/branding/eject.ts"],"sourcesContent":["import { mkdir, writeFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\n\nimport { cancel, isCancel, select } from \"@clack/prompts\";\nimport { Flags } from \"@oclif/core\";\nimport { consola } from \"consola\";\n\nimport {\n BRANDING_DESIGNS,\n DEFAULT_BRANDING_CONFIG_PATH,\n DEFAULT_BRANDING_DESIGN,\n DEFAULT_BRANDING_TEMPLATE_PATH,\n brandingReadmeContent,\n getDefaultBrandingConfig,\n} from \"@zitadel/config/defaults\";\nimport { BRANDING_FILE_SCHEMA_REF, META_SCHEMA_DIR, metaSchemaFiles } from \"@zitadel/config/meta-schemas\";\n\nimport { BRANDING_DIR } from \"../../lib/branding\";\nimport { BRANDING_DESIGN_INFO } from \"../../lib/branding/designs\";\nimport { ZitadelError } from \"../../lib/errors\";\nimport { stableStringify } from \"../../lib/json\";\nimport { BaseCommand, type JsonEnvelope } from \"../../lib/oclif\";\nimport { hasZitadelConfig } from \"../../lib/project\";\nimport {\n normalizePublicCliJson,\n normalizePublicCliProse,\n publicCliCommand,\n} from \"../../lib/public-cli\";\n\n/**\n * The `branding eject` command — take ownership of the login template.\n *\n * Scaffolds `.zitadel/branding/` from a shipped design: the `branding.json`\n * descriptor, the `login.liquid` template it references, a README, and the\n * branding dialect meta-schema (for projects set up before it existed).\n * Purely local — nothing is uploaded; the first `zitadel apply` publishes\n * revision 1. Ejecting the template is the last rung of the branding\n * override ladder (docs/design/branding/override-ladder.md); unrelated to\n * `zitadel eject`, which removes the Zitadel integration.\n */\nexport default class BrandingEject extends BaseCommand {\n static override description =\n \"Take ownership of the login template: scaffold .zitadel/branding/ from a shipped design.\";\n static override flags = {\n design: Flags.string({\n description: `Design to start from (default: ${DEFAULT_BRANDING_DESIGN}).`,\n options: [...BRANDING_DESIGNS],\n }),\n };\n\n async run(): Promise<JsonEnvelope> {\n const { flags } = await this.parse(BrandingEject);\n await this.toMeta(flags);\n const { cwd, force, nonInteractive } = this.meta;\n\n if (!(await hasZitadelConfig(cwd))) {\n throw new ZitadelError(\"E_VALIDATION\", \"This directory is not a Zitadel project\", {\n hint: \"Run setup first; branding files live under .zitadel/.\",\n nextCommands: [publicCliCommand(\"setup\", this.meta.cliVersion)],\n });\n }\n\n const design = flags.design ?? (await this.promptDesign(nonInteractive));\n this.recordTelemetry({ design, force });\n const { branding, template } = getDefaultBrandingConfig(design);\n\n const filesWritten: string[] = [];\n const write = async (relPath: string, content: string, overwrite: boolean): Promise<void> => {\n const dest = join(cwd, relPath);\n await mkdir(dirname(dest), { recursive: true });\n try {\n await writeFile(dest, content, overwrite ? undefined : { flag: \"wx\" });\n filesWritten.push(relPath);\n } catch (error) {\n if (isErrno(error, \"EEXIST\")) {\n throw new ZitadelError(\"E_CONFLICT\", `${relPath} already exists`, {\n hint: \"Rerun with --force to replace it, or edit the existing file directly.\",\n });\n }\n throw error;\n }\n };\n\n await write(\n DEFAULT_BRANDING_CONFIG_PATH,\n `${stableStringify({ $schema: BRANDING_FILE_SCHEMA_REF, ...branding })}\\n`,\n force,\n );\n await write(DEFAULT_BRANDING_TEMPLATE_PATH, template, force);\n\n // Never overwrite a hand-edited README; setups from before the branding\n // dialect also miss the meta-schema, so materialize it when absent.\n await write(\n join(BRANDING_DIR, \"README.md\"),\n normalizePublicCliProse(brandingReadmeContent(), this.meta.cliVersion),\n false,\n ).catch(swallowConflict);\n const brandingMeta = metaSchemaFiles().find((f) => f.name === \"branding.json\");\n if (brandingMeta) {\n await write(\n join(META_SCHEMA_DIR, brandingMeta.name),\n `${stableStringify(normalizePublicCliJson(brandingMeta.body, this.meta.cliVersion))}\\n`,\n false,\n ).catch(swallowConflict);\n }\n\n for (const file of filesWritten) {\n consola.info(`Wrote ${file}`);\n }\n consola.success(`Ejected the ${design} design into ${BRANDING_DIR}/`);\n\n return this.emit({\n status: \"ok\",\n data: {\n design,\n files_written: filesWritten,\n next_actions: [\n `Edit ${DEFAULT_BRANDING_TEMPLATE_PATH} — the login UI is yours now.`,\n \"Plan validates the template and shows the pending revision; apply publishes it.\",\n ],\n next_commands: [\n publicCliCommand(\"plan\", this.meta.cliVersion),\n publicCliCommand(\"apply\", this.meta.cliVersion),\n ],\n },\n });\n }\n\n private async promptDesign(nonInteractive: boolean): Promise<string> {\n if (nonInteractive) {\n return DEFAULT_BRANDING_DESIGN;\n }\n const choice = await select({\n message: \"Which design do you want to start from?\",\n initialValue: DEFAULT_BRANDING_DESIGN as string,\n options: BRANDING_DESIGNS.map((design) => ({\n value: design as string,\n label: BRANDING_DESIGN_INFO[design].label,\n hint: BRANDING_DESIGN_INFO[design].hint,\n })),\n });\n if (isCancel(choice)) {\n cancel(\"Eject cancelled.\");\n throw new ZitadelError(\"E_VALIDATION\", \"Eject cancelled by user\");\n }\n return choice;\n }\n}\n\nfunction swallowConflict(error: unknown): void {\n if (error instanceof ZitadelError && error.code === \"E_CONFLICT\") {\n return;\n }\n throw error;\n}\n\nfunction isErrno(error: unknown, code: string): boolean {\n return (\n typeof error === \"object\" &&\n error !== null &&\n \"code\" in error &&\n (error as NodeJS.ErrnoException).code === code\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAwCA,IAAqB,gBAArB,MAAqB,sBAAsB,YAAY;CACrD,OAAgB,cACd;CACF,OAAgB,QAAQ,EACtB,QAAQ,MAAM,OAAO;EACnB,aAAa,kCAAkC,wBAAwB;EACvE,SAAS,CAAC,GAAG,iBAAiB;EAC/B,CAAC,EACH;CAED,MAAM,MAA6B;EACjC,MAAM,EAAE,UAAU,MAAM,KAAK,MAAM,cAAc;AACjD,QAAM,KAAK,OAAO,MAAM;EACxB,MAAM,EAAE,KAAK,OAAO,mBAAmB,KAAK;AAE5C,MAAI,CAAE,MAAM,iBAAiB,IAAI,CAC/B,OAAM,IAAI,aAAa,gBAAgB,2CAA2C;GAChF,MAAM;GACN,cAAc,CAAC,iBAAiB,SAAS,KAAK,KAAK,WAAW,CAAC;GAChE,CAAC;EAGJ,MAAM,SAAS,MAAM,UAAW,MAAM,KAAK,aAAa,eAAe;AACvE,OAAK,gBAAgB;GAAE;GAAQ;GAAO,CAAC;EACvC,MAAM,EAAE,UAAU,aAAa,yBAAyB,OAAO;EAE/D,MAAM,eAAyB,EAAE;EACjC,MAAM,QAAQ,OAAO,SAAiB,SAAiB,cAAsC;GAC3F,MAAM,OAAO,KAAK,KAAK,QAAQ;AAC/B,SAAM,MAAM,QAAQ,KAAK,EAAE,EAAE,WAAW,MAAM,CAAC;AAC/C,OAAI;AACF,UAAM,UAAU,MAAM,SAAS,YAAY,KAAA,IAAY,EAAE,MAAM,MAAM,CAAC;AACtE,iBAAa,KAAK,QAAQ;YACnB,OAAO;AACd,QAAI,QAAQ,OAAO,SAAS,CAC1B,OAAM,IAAI,aAAa,cAAc,GAAG,QAAQ,kBAAkB,EAChE,MAAM,yEACP,CAAC;AAEJ,UAAM;;;AAIV,QAAM,MACJ,8BACA,GAAG,gBAAgB;GAAE,SAAS;GAA0B,GAAG;GAAU,CAAC,CAAC,KACvE,MACD;AACD,QAAM,MAAM,gCAAgC,UAAU,MAAM;AAI5D,QAAM,MACJ,KAAK,cAAc,YAAY,EAC/B,wBAAwB,uBAAuB,EAAE,KAAK,KAAK,WAAW,EACtE,MACD,CAAC,MAAM,gBAAgB;EACxB,MAAM,eAAe,iBAAiB,CAAC,MAAM,MAAM,EAAE,SAAS,gBAAgB;AAC9E,MAAI,aACF,OAAM,MACJ,KAAK,iBAAiB,aAAa,KAAK,EACxC,GAAG,gBAAgB,uBAAuB,aAAa,MAAM,KAAK,KAAK,WAAW,CAAC,CAAC,KACpF,MACD,CAAC,MAAM,gBAAgB;AAG1B,OAAK,MAAM,QAAQ,aACjB,WAAQ,KAAK,SAAS,OAAO;AAE/B,YAAQ,QAAQ,eAAe,OAAO,eAAe,aAAa,GAAG;AAErE,SAAO,KAAK,KAAK;GACf,QAAQ;GACR,MAAM;IACJ;IACA,eAAe;IACf,cAAc,CACZ,QAAQ,+BAA+B,gCACvC,kFACD;IACD,eAAe,CACb,iBAAiB,QAAQ,KAAK,KAAK,WAAW,EAC9C,iBAAiB,SAAS,KAAK,KAAK,WAAW,CAChD;IACF;GACF,CAAC;;CAGJ,MAAc,aAAa,gBAA0C;AACnE,MAAI,eACF,QAAO;EAET,MAAM,SAAS,MAAM,OAAO;GAC1B,SAAS;GACT,cAAc;GACd,SAAS,iBAAiB,KAAK,YAAY;IACzC,OAAO;IACP,OAAO,qBAAqB,QAAQ;IACpC,MAAM,qBAAqB,QAAQ;IACpC,EAAE;GACJ,CAAC;AACF,MAAI,SAAS,OAAO,EAAE;AACpB,UAAO,mBAAmB;AAC1B,SAAM,IAAI,aAAa,gBAAgB,0BAA0B;;AAEnE,SAAO;;;AAIX,SAAS,gBAAgB,OAAsB;AAC7C,KAAI,iBAAiB,gBAAgB,MAAM,SAAS,aAClD;AAEF,OAAM;;AAGR,SAAS,QAAQ,OAAgB,MAAuB;AACtD,QACE,OAAO,UAAU,YACjB,UAAU,QACV,UAAU,SACT,MAAgC,SAAS"}
@@ -1,6 +1,6 @@
1
- import { E as ZitadelError, S as isObject, t as BaseCommand } from "../oclif-CL6-M13m.mjs";
2
- import { i as isAttached } from "../claim-state-BTGInAaN.mjs";
3
- import { l as readZitadelSecret, u as writeZitadelSecret } from "../project-yejJSfdJ.mjs";
1
+ import { E as ZitadelError, S as isObject, t as BaseCommand } from "../oclif-CanO3zdt.mjs";
2
+ import { i as isAttached } from "../claim-state-DlfD6Res.mjs";
3
+ import { l as readZitadelSecret, u as writeZitadelSecret } from "../project-CHhot85s.mjs";
4
4
  import { access, readFile } from "node:fs/promises";
5
5
  import { delimiter, join } from "node:path";
6
6
  import { Flags } from "@oclif/core";
@@ -1,12 +1,12 @@
1
- import { E as ZitadelError, S as isObject, a as DEFAULT_LOCAL_SERVER_PORT, c as checkLocalServerHealth, h as readRuntimeMetadata, k as publicCliCommand, l as defaultLocalServerImageForCliVersion, m as localServerUrl, s as assertLocalStateWritable, t as BaseCommand } from "../oclif-CL6-M13m.mjs";
2
- import { n as claimCommand, r as claimState, t as claimAction } from "../claim-state-BTGInAaN.mjs";
3
- import { s as issuerFromPort, t as createOrca } from "../orca-BWB4zOdw.mjs";
4
- import { a as readProjectServer, c as readZitadelConfig, i as readPreset, l as readZitadelSecret, o as readRendererId, r as readDevelopmentIssuer, s as readUseCase, t as hasZitadelConfig } from "../project-yejJSfdJ.mjs";
5
- import { i as updateScaffold, t as SCHEMAS_DIR } from "../user-schema-W1OsXdpc.mjs";
6
- import { n as readScaffoldManifest, r as writeScaffoldManifest, t as hashScaffoldFile } from "../scaffold-manifest-CKNFLX9T.mjs";
7
- import { d as assertServerPackageAvailable, o as imageAvailable, r as dockerAvailable } from "../docker-Ciz9N6uI.mjs";
8
- import { n as dockerRuntimeGuidance, r as dockerUnavailableMessage, t as listenersForPort } from "../ports-Cu2Y7aXQ.mjs";
9
- import { t as discoverManagedRuntimeProcesses } from "../processes-Bn2WngOi.mjs";
1
+ import { E as ZitadelError, S as isObject, a as DEFAULT_LOCAL_SERVER_PORT, c as checkLocalServerHealth, h as readRuntimeMetadata, j as publicCliCommand, l as defaultLocalServerImageForCliVersion, m as localServerUrl, s as assertLocalStateWritable, t as BaseCommand } from "../oclif-CanO3zdt.mjs";
2
+ import { n as claimCommand, r as claimState, t as claimAction } from "../claim-state-DlfD6Res.mjs";
3
+ import { s as issuerFromPort, t as createOrca } from "../orca-Cu0iW1mv.mjs";
4
+ import { a as readProjectServer, c as readZitadelConfig, i as readPreset, l as readZitadelSecret, o as readRendererId, r as readDevelopmentIssuer, s as readUseCase, t as hasZitadelConfig } from "../project-CHhot85s.mjs";
5
+ import { i as updateScaffold, t as SCHEMAS_DIR } from "../user-schema-DTuOsdKE.mjs";
6
+ import { c as writeScaffoldManifest, n as detectPackageManager, o as hashScaffoldFile, s as readScaffoldManifest, t as addExactCommandFor } from "../package-manager-DFdLcDx1.mjs";
7
+ import { d as assertServerPackageAvailable, o as imageAvailable, r as dockerAvailable } from "../docker-DcRGTOJa.mjs";
8
+ import { n as dockerRuntimeGuidance, r as dockerUnavailableMessage, t as listenersForPort } from "../ports-BM20XIZb.mjs";
9
+ import { t as discoverManagedRuntimeProcesses } from "../processes-DKHPkU8O.mjs";
10
10
  import { chmod, readFile, readdir, rm, stat, writeFile } from "node:fs/promises";
11
11
  import { join } from "node:path";
12
12
  import { Flags } from "@oclif/core";
@@ -280,16 +280,19 @@ var DependencyVersionCheck = class {
280
280
  }).filter(([name]) => name.startsWith("@zitadel/")).filter(([, version]) => semver.valid(version.trim()) !== null);
281
281
  const mismatched = exact.filter(([, version]) => !semver.eq(version.trim(), ctx.cliVersion));
282
282
  if (mismatched.length > 0) {
283
- const remedy = mismatched.map(([name]) => `${name}@${ctx.cliVersion}`).join(" ");
283
+ const remedy = addExactCommandFor(await detectPackageManager(ctx.cwd), mismatched.map(([name]) => `${name}@${ctx.cliVersion}`)).display;
284
284
  return {
285
285
  ...base,
286
286
  status: "warn",
287
- message: `${mismatched.map(([name, version]) => `${name}@${version}`).join(", ")} does not match this CLI's version (${ctx.cliVersion}); scaffolded files and guidance target the CLI's train — update with e.g. \`npm install ${remedy}\``,
288
- details: { mismatched: mismatched.map(([name, version]) => ({
289
- name,
290
- declared: version,
291
- expected: ctx.cliVersion
292
- })) }
287
+ message: `${mismatched.map(([name, version]) => `${name}@${version}`).join(", ")} does not match this CLI's version (${ctx.cliVersion}); scaffolded files and guidance target the CLI's train — update with \`${remedy}\``,
288
+ details: {
289
+ mismatched: mismatched.map(([name, version]) => ({
290
+ name,
291
+ declared: version,
292
+ expected: ctx.cliVersion
293
+ })),
294
+ remedy_command: remedy
295
+ }
293
296
  };
294
297
  }
295
298
  return {
@@ -300,8 +303,10 @@ var DependencyVersionCheck = class {
300
303
  }
301
304
  /**
302
305
  * No auto-fix: repair's dependency op is deliberately additive-only (it
303
- * never rewrites a version the user declared), so the warn message carries
304
- * the exact install command instead.
306
+ * never rewrites a version the user declared), so the repair ships as the
307
+ * package-manager-aware install command in `details.remedy_command` — the
308
+ * doctor command surfaces it in the envelope's `next_commands` — with the
309
+ * same command quoted in the warn message for human readers.
305
310
  */
306
311
  async fix(_ctx) {}
307
312
  };
@@ -935,6 +940,11 @@ function advisoryForWarnings(warnings, cliVersion) {
935
940
  nextActions.push("Review other host-wide CLI-managed local Zitadel runtimes before starting a new one.");
936
941
  nextCommands.push(publicCliCommand("stop --all", cliVersion));
937
942
  }
943
+ const dependencyRemedy = remedyCommandOf(warnings.find((check) => check.name === "dependency-version"));
944
+ if (dependencyRemedy !== void 0) {
945
+ nextActions.push("Align the exactly-pinned @zitadel/* dependencies with the CLI version; scaffolded files and guidance target the CLI's train.");
946
+ nextCommands.push(dependencyRemedy);
947
+ }
938
948
  if (nextActions.length === 0 && nextCommands.length === 0) return;
939
949
  return {
940
950
  nextActions: unique(nextActions),
@@ -1050,6 +1060,19 @@ async function checkRuntime(runtime, runtimeBackend) {
1050
1060
  function formatListeners(listeners) {
1051
1061
  return listeners.map((listener) => [listener.command ?? "unknown", listener.pid ? `pid ${String(listener.pid)}` : void 0].filter(Boolean).join(" ")).join(", ");
1052
1062
  }
1063
+ /**
1064
+ * A warning may carry its own repair as `details.remedy_command` (today: the
1065
+ * dependency-version check's package-manager-aware exact-pin install).
1066
+ * Surfacing that string keeps the structured advisory identical to the
1067
+ * command quoted in the warning's prose.
1068
+ */
1069
+ function remedyCommandOf(check) {
1070
+ if (check?.status !== "warn") return;
1071
+ const details = check.details;
1072
+ if (typeof details !== "object" || details === null) return;
1073
+ const remedy = details.remedy_command;
1074
+ return typeof remedy === "string" && remedy.length > 0 ? remedy : void 0;
1075
+ }
1053
1076
  function hasManagedRuntimeProcesses(check) {
1054
1077
  if (!check || check.status !== "warn") return false;
1055
1078
  const details = check.details;