@zitadel/cli 0.1.0-alpha.0 → 0.1.0-alpha.2

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 (35) hide show
  1. package/README.md +158 -24
  2. package/SKILLS.md +40 -8
  3. package/dist/commands/apply.mjs +4 -3
  4. package/dist/commands/apply.mjs.map +1 -1
  5. package/dist/commands/doctor.mjs +150 -16
  6. package/dist/commands/doctor.mjs.map +1 -1
  7. package/dist/commands/eject.mjs +4 -3
  8. package/dist/commands/eject.mjs.map +1 -1
  9. package/dist/commands/logs.mjs +50 -0
  10. package/dist/commands/logs.mjs.map +1 -0
  11. package/dist/commands/plan.mjs +4 -3
  12. package/dist/commands/plan.mjs.map +1 -1
  13. package/dist/commands/reset.mjs +62 -0
  14. package/dist/commands/reset.mjs.map +1 -0
  15. package/dist/commands/setup.mjs +189 -23
  16. package/dist/commands/setup.mjs.map +1 -1
  17. package/dist/commands/start.mjs +128 -0
  18. package/dist/commands/start.mjs.map +1 -0
  19. package/dist/commands/status.mjs +74 -27
  20. package/dist/commands/status.mjs.map +1 -1
  21. package/dist/commands/stop.mjs +43 -0
  22. package/dist/commands/stop.mjs.map +1 -0
  23. package/dist/docker-DrOBycgG.mjs +210 -0
  24. package/dist/docker-DrOBycgG.mjs.map +1 -0
  25. package/dist/{project-C3pSfbao.mjs → oclif-CqkWBQ7i.mjs} +300 -119
  26. package/dist/oclif-CqkWBQ7i.mjs.map +1 -0
  27. package/dist/{orca-COsUnVoz.mjs → orca-S1tVnngd.mjs} +41 -12
  28. package/dist/{orca-COsUnVoz.mjs.map → orca-S1tVnngd.mjs.map} +1 -1
  29. package/dist/project-B6YfSaZw.mjs +87 -0
  30. package/dist/project-B6YfSaZw.mjs.map +1 -0
  31. package/dist/{sync-Cuyh-X1J.mjs → sync-DN4oNLVH.mjs} +4 -4
  32. package/dist/{sync-Cuyh-X1J.mjs.map → sync-DN4oNLVH.mjs.map} +1 -1
  33. package/oclif.manifest.json +357 -3
  34. package/package.json +3 -3
  35. package/dist/project-C3pSfbao.mjs.map +0 -1
package/README.md CHANGED
@@ -3,7 +3,8 @@
3
3
  Scaffolds Zitadel auth (login, register, profile, middleware) into a Next.js app.
4
4
 
5
5
  ```sh
6
- npx @zitadel/cli@latest setup --framework next --server <your-zitadel-server>
6
+ npx @zitadel/cli@alpha start
7
+ npx @zitadel/cli@alpha setup --framework next --server local
7
8
  ```
8
9
 
9
10
  > **Beta.** This is the **next-generation Zitadel**, a ground-up rewrite of the platform. It is distinct from the established Zitadel at [github.com/zitadel/zitadel](https://github.com/zitadel/zitadel). APIs and CLI flags will change.
@@ -11,21 +12,41 @@ npx @zitadel/cli@latest setup --framework next --server <your-zitadel-server>
11
12
  ## Requirements
12
13
 
13
14
  - Node 20+
15
+ - Docker for the managed local Zitadel runtime
14
16
  - A Next.js project created with `create-next-app`
15
- - A running next-generation Zitadel server to point at:
16
- - **Zitadel Cloud** — coming soon
17
- - **Self-hosted** — grab a binary from [github.com/zitadel/nextgen/releases](https://github.com/zitadel/nextgen/releases) and run it locally
18
17
 
19
18
  ## Quickstart
20
19
 
21
20
  ```sh
22
21
  npx create-next-app@latest my-app
23
22
  cd my-app
24
- npx @zitadel/cli@latest setup --framework next --server http://localhost:8080
23
+ npx @zitadel/cli@alpha doctor
24
+ npx @zitadel/cli@alpha start
25
+ npx @zitadel/cli@alpha setup --framework next --server local
25
26
  npm run dev
26
27
  ```
27
28
 
28
- `setup` creates a project on the Zitadel server, scaffolds `app/login`, `app/register`, `app/profile`, and `middleware.ts`, and writes `.env.local` and `.zitadel/`. The project's default user schema and login flow are provisioned server-side at creation time, so the CLI does not scaffold or upload them. Open `http://localhost:3000/login` to see the login page.
29
+ `start` runs a Docker-backed local Zitadel server and stores runtime data
30
+ under `.zitadel/local/`. Alpha CLI versions use the matching
31
+ `ghcr.io/zitadel/nextgen:<cli-version>` image by default; local/dev builds fall
32
+ back to `ghcr.io/zitadel/nextgen:latest`. Override with `--image` or
33
+ `ZITADEL_LOCAL_IMAGE` for advanced debugging.
34
+ `setup --server local` creates a project on that local server, scaffolds
35
+ `app/login`, `app/register`, and `middleware.ts`, writes `.env.local` and
36
+ `.zitadel/`, and installs dependencies with the detected package manager. Pass
37
+ `--skip-install` to install them yourself. The project's default user schema
38
+ and login flow are provisioned server-side at creation time, so the CLI does
39
+ not scaffold or upload them. Open `http://localhost:3000/login` to see the
40
+ login page.
41
+
42
+ For a reproducible tester report, use the exact alpha train from the GitHub
43
+ Release:
44
+
45
+ ```sh
46
+ npx @zitadel/cli@0.1.0-alpha.N doctor
47
+ npx @zitadel/cli@0.1.0-alpha.N start
48
+ npx @zitadel/cli@0.1.0-alpha.N setup --framework next --server local
49
+ ```
29
50
 
30
51
  The default project flow supports password registration/login, passkey
31
52
  registration/login, and optional passkey setup after password registration.
@@ -34,9 +55,10 @@ passkey can sign in with either credential.
34
55
 
35
56
  ## Other commands
36
57
 
37
- - `zitadel doctor` — verify the generated files and local state
38
- - `zitadel status` — summarise the local project
58
+ - `zitadel doctor` — verify the local Docker runtime and generated project files
59
+ - `zitadel status` — summarise the local Docker runtime and project
39
60
  - `zitadel eject` — remove what setup wrote (alias: `zitadel uninstall`)
61
+ - `zitadel start|stop|logs|reset` — manage the local Docker runtime
40
62
 
41
63
  ## Reference
42
64
 
@@ -44,17 +66,22 @@ passkey can sign in with either credential.
44
66
  <summary>Full command reference</summary>
45
67
 
46
68
  <!-- commands -->
47
- * [`zitadel autocomplete [SHELL]`](#zitadel-autocomplete-shell)
48
- * [`zitadel commands`](#zitadel-commands)
49
- * [`zitadel doctor`](#zitadel-doctor)
50
- * [`zitadel eject`](#zitadel-eject)
51
- * [`zitadel help [COMMAND]`](#zitadel-help-command)
52
- * [`zitadel search`](#zitadel-search)
53
- * [`zitadel setup`](#zitadel-setup)
54
- * [`zitadel status`](#zitadel-status)
55
- * [`zitadel uninstall`](#zitadel-uninstall)
56
- * [`zitadel version`](#zitadel-version)
57
- * [`zitadel which`](#zitadel-which)
69
+
70
+ - [`zitadel autocomplete [SHELL]`](#zitadel-autocomplete-shell)
71
+ - [`zitadel commands`](#zitadel-commands)
72
+ - [`zitadel doctor`](#zitadel-doctor)
73
+ - [`zitadel eject`](#zitadel-eject)
74
+ - [`zitadel help [COMMAND]`](#zitadel-help-command)
75
+ - [`zitadel logs`](#zitadel-logs)
76
+ - [`zitadel reset`](#zitadel-reset)
77
+ - [`zitadel search`](#zitadel-search)
78
+ - [`zitadel setup`](#zitadel-setup)
79
+ - [`zitadel start`](#zitadel-start)
80
+ - [`zitadel status`](#zitadel-status)
81
+ - [`zitadel stop`](#zitadel-stop)
82
+ - [`zitadel uninstall`](#zitadel-uninstall)
83
+ - [`zitadel version`](#zitadel-version)
84
+ - [`zitadel which`](#zitadel-which)
58
85
 
59
86
  ## `zitadel autocomplete [SHELL]`
60
87
 
@@ -118,11 +145,12 @@ _See code: [@oclif/plugin-commands](https://github.com/oclif/plugin-commands/blo
118
145
 
119
146
  ## `zitadel doctor`
120
147
 
121
- Verify generated files and local state.
148
+ Verify local runtime and project state.
122
149
 
123
150
  ```
124
151
  USAGE
125
152
  $ zitadel doctor [--json] [-c <value>] [-s <value>] [-n] [-f] [--dry-run] [--verbose] [--debug] [--fix]
153
+ [--image <value>] [--port <value>]
126
154
 
127
155
  FLAGS
128
156
  -c, --cwd=<value> Project directory to operate on.
@@ -132,13 +160,15 @@ FLAGS
132
160
  --debug Debug logging.
133
161
  --dry-run Preview without mutating files or the platform.
134
162
  --fix Re-apply missing managed files.
163
+ --image=<value> Container image to check.
164
+ --port=<value> [default: 8080] Local HTTP port.
135
165
  --verbose Verbose logging.
136
166
 
137
167
  GLOBAL FLAGS
138
168
  --json Format output as json.
139
169
 
140
170
  DESCRIPTION
141
- Verify generated files and local state.
171
+ Verify local runtime and project state.
142
172
  ```
143
173
 
144
174
  ## `zitadel eject`
@@ -188,6 +218,57 @@ DESCRIPTION
188
218
 
189
219
  _See code: [@oclif/plugin-help](https://github.com/oclif/plugin-help/blob/6.2.49/src/commands/help.ts)_
190
220
 
221
+ ## `zitadel logs`
222
+
223
+ Show local Zitadel server logs.
224
+
225
+ ```
226
+ USAGE
227
+ $ zitadel logs [--json] [-c <value>] [-s <value>] [-n] [-f] [--dry-run] [--verbose] [--debug] [--follow]
228
+ [--tail <value>]
229
+
230
+ FLAGS
231
+ -c, --cwd=<value> Project directory to operate on.
232
+ -f, --force Overwrite protected files on conflict.
233
+ -n, --non-interactive Disable prompts. Required when scripting or running as an agent.
234
+ -s, --server=<value> Override the resolved server URL.
235
+ --debug Debug logging.
236
+ --dry-run Preview without mutating files or the platform.
237
+ --follow Follow logs.
238
+ --tail=<value> [default: 200] Number of lines to show.
239
+ --verbose Verbose logging.
240
+
241
+ GLOBAL FLAGS
242
+ --json Format output as json.
243
+
244
+ DESCRIPTION
245
+ Show local Zitadel server logs.
246
+ ```
247
+
248
+ ## `zitadel reset`
249
+
250
+ Delete the local Zitadel server runtime and data.
251
+
252
+ ```
253
+ USAGE
254
+ $ zitadel reset [--json] [-c <value>] [-s <value>] [-n] [-f] [--dry-run] [--verbose] [--debug]
255
+
256
+ FLAGS
257
+ -c, --cwd=<value> Project directory to operate on.
258
+ -f, --force Overwrite protected files on conflict.
259
+ -n, --non-interactive Disable prompts. Required when scripting or running as an agent.
260
+ -s, --server=<value> Override the resolved server URL.
261
+ --debug Debug logging.
262
+ --dry-run Preview without mutating files or the platform.
263
+ --verbose Verbose logging.
264
+
265
+ GLOBAL FLAGS
266
+ --json Format output as json.
267
+
268
+ DESCRIPTION
269
+ Delete the local Zitadel server runtime and data.
270
+ ```
271
+
191
272
  ## `zitadel search`
192
273
 
193
274
  Search for a command.
@@ -211,7 +292,7 @@ Create a Zitadel project and scaffold local auth.
211
292
  ```
212
293
  USAGE
213
294
  $ zitadel setup [--json] [-c <value>] [-s <value>] [-n] [-f] [--dry-run] [--verbose] [--debug]
214
- [--framework next] [--renderer react|web-component]
295
+ [--framework next] [--renderer react|web-component] [--skip-install]
215
296
 
216
297
  FLAGS
217
298
  -c, --cwd=<value> Project directory to operate on.
@@ -224,6 +305,7 @@ FLAGS
224
305
  <options: next>
225
306
  --renderer=<option> Renderer (default: react).
226
307
  <options: react|web-component>
308
+ --skip-install Do not install dependencies after setup updates package.json.
227
309
  --verbose Verbose logging.
228
310
 
229
311
  GLOBAL FLAGS
@@ -236,9 +318,36 @@ EXAMPLES
236
318
  $ zitadel setup --framework next
237
319
  ```
238
320
 
321
+ ## `zitadel start`
322
+
323
+ Start a local Zitadel server.
324
+
325
+ ```
326
+ USAGE
327
+ $ zitadel start [--json] [-c <value>] [-s <value>] [-n] [-f] [--dry-run] [--verbose] [--debug] [--image
328
+ <value>] [--port <value>]
329
+
330
+ FLAGS
331
+ -c, --cwd=<value> Project directory to operate on.
332
+ -f, --force Overwrite protected files on conflict.
333
+ -n, --non-interactive Disable prompts. Required when scripting or running as an agent.
334
+ -s, --server=<value> Override the resolved server URL.
335
+ --debug Debug logging.
336
+ --dry-run Preview without mutating files or the platform.
337
+ --image=<value> Container image to run.
338
+ --port=<value> [default: 8080] Local HTTP port.
339
+ --verbose Verbose logging.
340
+
341
+ GLOBAL FLAGS
342
+ --json Format output as json.
343
+
344
+ DESCRIPTION
345
+ Start a local Zitadel server.
346
+ ```
347
+
239
348
  ## `zitadel status`
240
349
 
241
- Summarize the local project state.
350
+ Summarize the local Zitadel server and project state.
242
351
 
243
352
  ```
244
353
  USAGE
@@ -257,7 +366,31 @@ GLOBAL FLAGS
257
366
  --json Format output as json.
258
367
 
259
368
  DESCRIPTION
260
- Summarize the local project state.
369
+ Summarize the local Zitadel server and project state.
370
+ ```
371
+
372
+ ## `zitadel stop`
373
+
374
+ Stop the local Zitadel server.
375
+
376
+ ```
377
+ USAGE
378
+ $ zitadel stop [--json] [-c <value>] [-s <value>] [-n] [-f] [--dry-run] [--verbose] [--debug]
379
+
380
+ FLAGS
381
+ -c, --cwd=<value> Project directory to operate on.
382
+ -f, --force Overwrite protected files on conflict.
383
+ -n, --non-interactive Disable prompts. Required when scripting or running as an agent.
384
+ -s, --server=<value> Override the resolved server URL.
385
+ --debug Debug logging.
386
+ --dry-run Preview without mutating files or the platform.
387
+ --verbose Verbose logging.
388
+
389
+ GLOBAL FLAGS
390
+ --json Format output as json.
391
+
392
+ DESCRIPTION
393
+ Stop the local Zitadel server.
261
394
  ```
262
395
 
263
396
  ## `zitadel uninstall`
@@ -340,6 +473,7 @@ EXAMPLES
340
473
  ```
341
474
 
342
475
  _See code: [@oclif/plugin-which](https://github.com/oclif/plugin-which/blob/3.2.55/src/commands/which.ts)_
476
+
343
477
  <!-- commandsstop -->
344
478
 
345
479
  </details>
package/SKILLS.md CHANGED
@@ -24,7 +24,7 @@ parse the result rather than scraping human output.
24
24
  metadata) or run `zitadel <command> --help` for the full per-command flag list.
25
25
 
26
26
  ```sh
27
- npx @zitadel/cli@latest <command> --non-interactive --json
27
+ npx @zitadel/cli@alpha <command> --non-interactive --json
28
28
  ```
29
29
 
30
30
  ## Reading the envelope
@@ -38,6 +38,8 @@ Each invocation prints one JSON object:
38
38
  - On failure: `code` (e.g. `E_VALIDATION`, `E_NETWORK`, `E_CONFLICT`) and
39
39
  `message`.
40
40
  - `next_commands`: the suggested follow-ups. Prefer these over free-text hints.
41
+ - `E_LOCAL_SERVER_NOT_RUNNING`: start the local Docker runtime with
42
+ `npx @zitadel/cli@alpha start`, then retry with `--server local`.
41
43
 
42
44
  Exit codes mirror the error class (3 = validation, 4 = network, 5 = conflict,
43
45
  1 = auth, 2 = not-implemented). An unknown command is handled by the CLI's help
@@ -51,21 +53,51 @@ layer, not the envelope.
51
53
  scaffolds nor uploads them. Flags: `--framework`, `--renderer`.
52
54
  - `plan` — validate config and preview the sync diff without mutating anything.
53
55
  - `apply` — validate and upload repo config to the platform.
54
- - `doctor` — verify generated files and local state; `--fix` re-applies missing
55
- managed files.
56
- - `status` summarize the local project state.
56
+ - `doctor` — verify generated app files and local state once `zitadel.json`
57
+ exists. Local Docker runtime prerequisites are advisory warnings unless an
58
+ existing managed runtime is unhealthy; `--fix` re-applies missing managed
59
+ files.
60
+ - `status` — summarize the local Docker runtime and project state.
57
61
  - `eject` (alias `uninstall`) — remove managed files and local Zitadel state;
58
62
  requires `--force` when non-interactive.
63
+ - `start` — start the managed local Zitadel container and persist runtime
64
+ metadata under `.zitadel/local/runtime.json`.
65
+ - `stop` — stop/remove the managed container while preserving
66
+ `.zitadel/local/nextgen-data`.
67
+ - `logs` — print managed container logs; `--follow` streams in human mode.
68
+ - `reset` — stop/remove the managed container and delete local runtime data;
69
+ requires `--force` when non-interactive.
70
+
71
+ Alpha releases are lockstep trains. `npx @zitadel/cli@alpha start` runs the
72
+ latest tested alpha server image, and
73
+ `npx @zitadel/cli@0.1.0-alpha.N start` runs
74
+ `ghcr.io/zitadel/nextgen:0.1.0-alpha.N`. `zitadel start --image <ref>` remains
75
+ the explicit image override for debugging.
59
76
 
60
77
  ## Golden path
61
78
 
62
79
  ```sh
63
- npx @zitadel/cli@latest setup --framework next --non-interactive --json
64
- npx @zitadel/cli@latest doctor --non-interactive --json
65
- npx @zitadel/cli@latest plan --non-interactive --json
66
- npx @zitadel/cli@latest apply --non-interactive --json
80
+ npx @zitadel/cli@alpha doctor --non-interactive --json
81
+ npx @zitadel/cli@alpha start --non-interactive --json
82
+ npx @zitadel/cli@alpha setup --framework next --server local --non-interactive --json
83
+ npx @zitadel/cli@alpha doctor --non-interactive --json
84
+ npx @zitadel/cli@alpha plan --non-interactive --json
85
+ npx @zitadel/cli@alpha apply --non-interactive --json
86
+ ```
87
+
88
+ Exact alpha train invocation:
89
+
90
+ ```sh
91
+ npx @zitadel/cli@0.1.0-alpha.N doctor --non-interactive --json
92
+ npx @zitadel/cli@0.1.0-alpha.N start --non-interactive --json
93
+ npx @zitadel/cli@0.1.0-alpha.N setup --framework next --server local --non-interactive --json
67
94
  ```
68
95
 
69
96
  Repo config is authoritative: edit `zitadel.json` or files under `.zitadel/`,
70
97
  then re-run `plan` and `apply`. Managed files carry a marker comment; `eject`
71
98
  removes only files that still carry it, preserving anything the user replaced.
99
+ For app-local development, `--server local` resolves through
100
+ `.zitadel/local/runtime.json` and requires a healthy `npx @zitadel/cli@alpha start`
101
+ runtime. `setup` installs dependencies with the detected package manager by
102
+ default; pass `--skip-install` when the agent or host workflow will install
103
+ dependencies separately.
@@ -1,8 +1,9 @@
1
- import { o as readZitadelSecret, s as BaseCommand } from "../project-C3pSfbao.mjs";
2
- import { a as makeSyncers, i as runSyncLoop, n as summarizePlan, o as environmentSchema, r as buildSyncPlan, t as renderPlan } from "../sync-Cuyh-X1J.mjs";
1
+ import { t as BaseCommand } from "../oclif-CqkWBQ7i.mjs";
2
+ import { o as readZitadelSecret } from "../project-B6YfSaZw.mjs";
3
+ import { a as makeSyncers, i as runSyncLoop, n as summarizePlan, o as environmentSchema, r as buildSyncPlan, t as renderPlan } from "../sync-DN4oNLVH.mjs";
3
4
  import { Flags } from "@oclif/core";
4
- import { consola as consola$1 } from "consola";
5
5
  import { createZitadelClient } from "@zitadel/api/client";
6
+ import { consola as consola$1 } from "consola";
6
7
  //#region src/commands/apply.ts
7
8
  /**
8
9
  * `zitadel apply` — validate and upload repo config to the platform.
@@ -1 +1 @@
1
- {"version":3,"file":"apply.mjs","names":[],"sources":["../../src/commands/apply.ts"],"sourcesContent":["import { Flags } from \"@oclif/core\";\nimport { consola } from \"consola\";\n\nimport { createZitadelClient } from \"@zitadel/api/client\";\n\nimport { BaseCommand, type JsonEnvelope } from \"../lib/oclif\";\nimport { environmentSchema } from \"../lib/environment\";\nimport { buildSyncPlan, makeSyncers, renderPlan, runSyncLoop, summarizePlan } from \"../lib/sync\";\nimport { readZitadelSecret } from \"../lib/project\";\n\n/**\n * `zitadel apply` — validate and upload repo config to the platform.\n *\n * Runs the sync loop to convergence, or (with `--dry-run`) previews the diff\n * without mutating. All validation — structural shape and `${VAR}` / `*_env`\n * reference presence — happens inside the sync engine ({@link buildSyncPlan}),\n * so an invalid or under-configured file fails with `E_VALIDATION` before any\n * platform call.\n */\nexport default class Apply extends BaseCommand {\n static override description = \"Validate and upload repo config to the platform.\";\n // Temporarily hidden while we collapse the dev workflow around `setup`'s\n // auto-apply. The logic stays wired up so re-exposing this command is a\n // one-line flip when we settle on the surface area.\n static override hidden = true;\n static override flags = {\n environment: Flags.string({\n char: \"e\",\n description: \"Target environment (default: development).\",\n options: [...environmentSchema.options],\n }),\n };\n\n async run(): Promise<JsonEnvelope> {\n const { flags } = await this.parse(Apply);\n await this.toMeta(flags);\n const { cwd, source, env, dryRun, isTTY } = this.meta;\n\n const secret = await readZitadelSecret(cwd);\n consola.info(`Project ${secret.project_id}`);\n consola.info(`Server ${source}`);\n const client = createZitadelClient({\n baseUrl: source,\n token: secret.project_secret,\n });\n const syncers = makeSyncers({ client, projectId: secret.project_id, env });\n\n if (!dryRun) {\n consola.start(\"Syncing schemas and flows to Zitadel\");\n await runSyncLoop(cwd, syncers);\n consola.success(\"Sync complete\");\n return this.emit({ status: \"ok\", data: { synced: true } });\n }\n\n consola.start(\"Building plan (dry run)\");\n const plan = await buildSyncPlan(cwd, syncers, true);\n const summary = summarizePlan(plan);\n consola.success(\n `Plan: ${summary.creates} create${summary.creates === 1 ? \"\" : \"s\"}, ` +\n `${summary.updates} update${summary.updates === 1 ? \"\" : \"s\"}, ` +\n `${summary.deletes} delete${summary.deletes === 1 ? \"\" : \"s\"}`,\n );\n return this.emit({\n status: \"ok\",\n data: summary,\n pretty: renderPlan(plan, isTTY),\n });\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;AAmBA,IAAqB,QAArB,MAAqB,cAAc,YAAY;CAC7C,OAAgB,cAAc;CAI9B,OAAgB,SAAS;CACzB,OAAgB,QAAQ,EACtB,aAAa,MAAM,OAAO;EACxB,MAAM;EACN,aAAa;EACb,SAAS,CAAC,GAAG,kBAAkB,QAAQ;EACxC,CAAC,EACH;CAED,MAAM,MAA6B;EACjC,MAAM,EAAE,UAAU,MAAM,KAAK,MAAM,MAAM;AACzC,QAAM,KAAK,OAAO,MAAM;EACxB,MAAM,EAAE,KAAK,QAAQ,KAAK,QAAQ,UAAU,KAAK;EAEjD,MAAM,SAAS,MAAM,kBAAkB,IAAI;AAC3C,YAAQ,KAAK,aAAa,OAAO,aAAa;AAC9C,YAAQ,KAAK,aAAa,SAAS;EAKnC,MAAM,UAAU,YAAY;GAAE,QAJf,oBAAoB;IACjC,SAAS;IACT,OAAO,OAAO;IACf,CACmC;GAAE,WAAW,OAAO;GAAY;GAAK,CAAC;AAE1E,MAAI,CAAC,QAAQ;AACX,aAAQ,MAAM,uCAAuC;AACrD,SAAM,YAAY,KAAK,QAAQ;AAC/B,aAAQ,QAAQ,gBAAgB;AAChC,UAAO,KAAK,KAAK;IAAE,QAAQ;IAAM,MAAM,EAAE,QAAQ,MAAM;IAAE,CAAC;;AAG5D,YAAQ,MAAM,0BAA0B;EACxC,MAAM,OAAO,MAAM,cAAc,KAAK,SAAS,KAAK;EACpD,MAAM,UAAU,cAAc,KAAK;AACnC,YAAQ,QACN,SAAS,QAAQ,QAAQ,SAAS,QAAQ,YAAY,IAAI,KAAK,IAAI,IAC9D,QAAQ,QAAQ,SAAS,QAAQ,YAAY,IAAI,KAAK,IAAI,IAC1D,QAAQ,QAAQ,SAAS,QAAQ,YAAY,IAAI,KAAK,MAC5D;AACD,SAAO,KAAK,KAAK;GACf,QAAQ;GACR,MAAM;GACN,QAAQ,WAAW,MAAM,MAAM;GAChC,CAAC"}
1
+ {"version":3,"file":"apply.mjs","names":[],"sources":["../../src/commands/apply.ts"],"sourcesContent":["import { Flags } from \"@oclif/core\";\nimport { consola } from \"consola\";\n\nimport { createZitadelClient } from \"@zitadel/api/client\";\n\nimport { BaseCommand, type JsonEnvelope } from \"../lib/oclif\";\nimport { environmentSchema } from \"../lib/environment\";\nimport { buildSyncPlan, makeSyncers, renderPlan, runSyncLoop, summarizePlan } from \"../lib/sync\";\nimport { readZitadelSecret } from \"../lib/project\";\n\n/**\n * `zitadel apply` — validate and upload repo config to the platform.\n *\n * Runs the sync loop to convergence, or (with `--dry-run`) previews the diff\n * without mutating. All validation — structural shape and `${VAR}` / `*_env`\n * reference presence — happens inside the sync engine ({@link buildSyncPlan}),\n * so an invalid or under-configured file fails with `E_VALIDATION` before any\n * platform call.\n */\nexport default class Apply extends BaseCommand {\n static override description = \"Validate and upload repo config to the platform.\";\n // Temporarily hidden while we collapse the dev workflow around `setup`'s\n // auto-apply. The logic stays wired up so re-exposing this command is a\n // one-line flip when we settle on the surface area.\n static override hidden = true;\n static override flags = {\n environment: Flags.string({\n char: \"e\",\n description: \"Target environment (default: development).\",\n options: [...environmentSchema.options],\n }),\n };\n\n async run(): Promise<JsonEnvelope> {\n const { flags } = await this.parse(Apply);\n await this.toMeta(flags);\n const { cwd, source, env, dryRun, isTTY } = this.meta;\n\n const secret = await readZitadelSecret(cwd);\n consola.info(`Project ${secret.project_id}`);\n consola.info(`Server ${source}`);\n const client = createZitadelClient({\n baseUrl: source,\n token: secret.project_secret,\n });\n const syncers = makeSyncers({ client, projectId: secret.project_id, env });\n\n if (!dryRun) {\n consola.start(\"Syncing schemas and flows to Zitadel\");\n await runSyncLoop(cwd, syncers);\n consola.success(\"Sync complete\");\n return this.emit({ status: \"ok\", data: { synced: true } });\n }\n\n consola.start(\"Building plan (dry run)\");\n const plan = await buildSyncPlan(cwd, syncers, true);\n const summary = summarizePlan(plan);\n consola.success(\n `Plan: ${summary.creates} create${summary.creates === 1 ? \"\" : \"s\"}, ` +\n `${summary.updates} update${summary.updates === 1 ? \"\" : \"s\"}, ` +\n `${summary.deletes} delete${summary.deletes === 1 ? \"\" : \"s\"}`,\n );\n return this.emit({\n status: \"ok\",\n data: summary,\n pretty: renderPlan(plan, isTTY),\n });\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAmBA,IAAqB,QAArB,MAAqB,cAAc,YAAY;CAC7C,OAAgB,cAAc;CAI9B,OAAgB,SAAS;CACzB,OAAgB,QAAQ,EACtB,aAAa,MAAM,OAAO;EACxB,MAAM;EACN,aAAa;EACb,SAAS,CAAC,GAAG,kBAAkB,QAAQ;EACxC,CAAC,EACH;CAED,MAAM,MAA6B;EACjC,MAAM,EAAE,UAAU,MAAM,KAAK,MAAM,MAAM;AACzC,QAAM,KAAK,OAAO,MAAM;EACxB,MAAM,EAAE,KAAK,QAAQ,KAAK,QAAQ,UAAU,KAAK;EAEjD,MAAM,SAAS,MAAM,kBAAkB,IAAI;AAC3C,YAAQ,KAAK,aAAa,OAAO,aAAa;AAC9C,YAAQ,KAAK,aAAa,SAAS;EAKnC,MAAM,UAAU,YAAY;GAAE,QAJf,oBAAoB;IACjC,SAAS;IACT,OAAO,OAAO;IACf,CACmC;GAAE,WAAW,OAAO;GAAY;GAAK,CAAC;AAE1E,MAAI,CAAC,QAAQ;AACX,aAAQ,MAAM,uCAAuC;AACrD,SAAM,YAAY,KAAK,QAAQ;AAC/B,aAAQ,QAAQ,gBAAgB;AAChC,UAAO,KAAK,KAAK;IAAE,QAAQ;IAAM,MAAM,EAAE,QAAQ,MAAM;IAAE,CAAC;;AAG5D,YAAQ,MAAM,0BAA0B;EACxC,MAAM,OAAO,MAAM,cAAc,KAAK,SAAS,KAAK;EACpD,MAAM,UAAU,cAAc,KAAK;AACnC,YAAQ,QACN,SAAS,QAAQ,QAAQ,SAAS,QAAQ,YAAY,IAAI,KAAK,IAAI,IAC9D,QAAQ,QAAQ,SAAS,QAAQ,YAAY,IAAI,KAAK,IAAI,IAC1D,QAAQ,QAAQ,SAAS,QAAQ,YAAY,IAAI,KAAK,MAC5D;AACD,SAAO,KAAK,KAAK;GACf,QAAQ;GACR,MAAM;GACN,QAAQ,WAAW,MAAM,MAAM;GAChC,CAAC"}
@@ -1,9 +1,11 @@
1
- import { a as readZitadelConfig, i as readRendererId, o as readZitadelSecret, p as ZitadelError, r as readDevelopmentIssuer, s as BaseCommand, u as isObject } from "../project-C3pSfbao.mjs";
2
- import { r as issuerFromPort, t as createOrca } from "../orca-COsUnVoz.mjs";
1
+ import { C as isObject, E as ZitadelError, a as DEFAULT_LOCAL_SERVER_PORT, c as checkLocalServerHealth, f as isPortAvailable, g as readRuntimeMetadata, h as localServerUrl, l as defaultLocalServerImageForCliVersion, m as localRuntimePaths, s as assertWritableDirectory, t as BaseCommand, x as publicCliCommand } from "../oclif-CqkWBQ7i.mjs";
2
+ import { r as issuerFromPort, t as createOrca } from "../orca-S1tVnngd.mjs";
3
+ import { a as readZitadelConfig, i as readRendererId, o as readZitadelSecret, r as readDevelopmentIssuer, t as hasZitadelConfig } from "../project-B6YfSaZw.mjs";
4
+ import { o as imageAvailable, r as dockerAvailable } from "../docker-DrOBycgG.mjs";
3
5
  import { Flags } from "@oclif/core";
4
6
  import consola from "consola";
5
- import { chmod, readFile, stat, writeFile } from "node:fs/promises";
6
7
  import { join } from "node:path";
8
+ import { chmod, readFile, stat, writeFile } from "node:fs/promises";
7
9
  import "@zitadel/api/generated/endpoints/zitadelNextGen.zod";
8
10
  //#region src/commands/doctor/checks/types.ts
9
11
  /**
@@ -148,7 +150,7 @@ var FrameworkCheck = class extends AbstractSanityCheck {
148
150
  * SDK package via `patcher.repair`. The user schema and flow definition are
149
151
  * server-owned and no longer scaffolded locally, so nothing here reads them.
150
152
  */
151
- async function loadPatchContext(cwd, orca) {
153
+ async function loadPatchContext(cwd, orca, cliVersion) {
152
154
  const config = await readZitadelConfig(cwd);
153
155
  const secret = await readZitadelSecret(cwd);
154
156
  const framework = await orca.detect(cwd);
@@ -157,6 +159,7 @@ async function loadPatchContext(cwd, orca) {
157
159
  rendererId: readRendererId(config),
158
160
  issuer: await resolveIssuer(cwd, config, framework),
159
161
  server: typeof config.server === "string" ? config.server : "",
162
+ cliVersion,
160
163
  project: {
161
164
  id: secret.project_id,
162
165
  projectSecret: secret.project_secret,
@@ -206,7 +209,7 @@ var DependencyCheck = class extends AbstractSanityCheck {
206
209
  * so going through `repair` is the only sanctioned path.
207
210
  */
208
211
  async fix(ctx) {
209
- const patchCtx = await loadPatchContext(ctx.cwd, ctx.orca);
212
+ const patchCtx = await loadPatchContext(ctx.cwd, ctx.orca, ctx.cliVersion);
210
213
  await ctx.orca.patcherFor(patchCtx.framework.id).repair(patchCtx, {
211
214
  cwd: ctx.cwd,
212
215
  dryRun: ctx.dryRun,
@@ -243,6 +246,13 @@ const SANITY_CHECKS = [
243
246
  ];
244
247
  //#endregion
245
248
  //#region src/commands/doctor/index.ts
249
+ const LOCAL_RUNTIME_CHECK_NAMES = new Set([
250
+ "docker-cli",
251
+ "image",
252
+ "state-dir",
253
+ "port",
254
+ "runtime"
255
+ ]);
246
256
  /**
247
257
  * `zitadel doctor` — verify generated files and local state.
248
258
  *
@@ -257,18 +267,33 @@ const SANITY_CHECKS = [
257
267
  * whatever remains broken.
258
268
  */
259
269
  var Doctor = class Doctor extends BaseCommand {
260
- static description = "Verify generated files and local state.";
261
- static flags = { fix: Flags.boolean({ description: "Re-apply missing managed files." }) };
270
+ static description = "Verify local runtime and project state.";
271
+ static flags = {
272
+ fix: Flags.boolean({ description: "Re-apply missing managed files." }),
273
+ image: Flags.string({ description: "Container image to check." }),
274
+ port: Flags.integer({
275
+ description: "Local HTTP port.",
276
+ default: DEFAULT_LOCAL_SERVER_PORT
277
+ })
278
+ };
262
279
  async run() {
263
280
  const { flags } = await this.parse(Doctor);
264
- await this.toMeta(flags);
281
+ const port = flags.port ?? 8080;
282
+ await this.toMeta(flags, {
283
+ resolveServer: false,
284
+ source: localServerUrl(port)
285
+ });
265
286
  const { cwd, dryRun } = this.meta;
287
+ const image = flags.image ?? this.meta.env.ZITADEL_LOCAL_IMAGE ?? defaultLocalServerImageForCliVersion(this.meta.cliVersion);
288
+ const runtimeChecks = await runLocalRuntimeChecks(cwd, image, port);
289
+ const hasConfig = await hasZitadelConfig(cwd);
266
290
  const ctx = {
267
291
  cwd,
268
292
  orca: createOrca(),
293
+ cliVersion: this.meta.cliVersion,
269
294
  dryRun
270
295
  };
271
- if (flags.fix) {
296
+ if (hasConfig && flags.fix) {
272
297
  const before = await Promise.all(SANITY_CHECKS.map((check) => check.run(ctx)));
273
298
  for (const [index, check] of SANITY_CHECKS.entries()) {
274
299
  if (before[index]?.status !== "fail") continue;
@@ -279,23 +304,132 @@ var Doctor = class Doctor extends BaseCommand {
279
304
  }
280
305
  }
281
306
  }
282
- const checks = await Promise.all(SANITY_CHECKS.map((check) => check.run(ctx)));
307
+ const projectChecks = hasConfig ? await Promise.all(SANITY_CHECKS.map((check) => check.run(ctx))) : [];
308
+ const checks = [...runtimeChecks, ...projectChecks];
283
309
  const failed = checks.filter((check) => check.status === "fail");
310
+ const warnings = checks.filter((check) => check.status === "warn");
284
311
  const data = {
285
- title: failed.length === 0 ? "Zitadel doctor passed." : "Zitadel doctor found issues.",
312
+ title: failed.length > 0 ? "Zitadel doctor found issues." : warnings.length > 0 ? "Zitadel doctor passed with warnings." : "Zitadel doctor passed.",
286
313
  ok: failed.length === 0,
314
+ image,
315
+ port,
316
+ project: { lifecycle: hasConfig ? "configured" : "not-configured" },
287
317
  checks
288
318
  };
289
- if (failed.length > 0) throw new ZitadelError("E_VALIDATION", "Zitadel doctor found issues", {
290
- hint: "Run `npx @zitadel/cli@latest doctor --fix` to re-apply missing managed files.",
291
- details: data
292
- });
319
+ if (failed.length > 0) {
320
+ const advice = failureAdvice(failed, image, port, this.meta.cliVersion);
321
+ throw new ZitadelError("E_VALIDATION", "Zitadel doctor found issues", {
322
+ hint: advice.hint,
323
+ nextCommands: advice.nextCommands,
324
+ details: data
325
+ });
326
+ }
293
327
  return this.emit({
294
328
  status: "ok",
295
- data
329
+ data,
330
+ warnings: warnings.map((warning) => `${warning.name}: ${warning.message}`)
296
331
  });
297
332
  }
298
333
  };
334
+ function failureAdvice(failed, image, port, cliVersion) {
335
+ const failedNames = new Set(failed.map((check) => check.name));
336
+ if (failedNames.has("docker-cli")) return {
337
+ hint: "Docker is required for `zitadel start`, but the Docker daemon is not reachable. Install or start Docker, then rerun `zitadel doctor`.",
338
+ nextCommands: ["docker version", publicCliCommand("doctor", cliVersion)]
339
+ };
340
+ if (failedNames.has("image")) return {
341
+ hint: "The local Zitadel image is not available. Check Docker registry access, build it locally, or pass --image / ZITADEL_LOCAL_IMAGE.",
342
+ nextCommands: [`docker pull ${image}`, publicCliCommand("doctor", cliVersion)]
343
+ };
344
+ if (failedNames.has("state-dir")) return {
345
+ hint: "The local Zitadel state directory is not writable. Fix directory permissions, then rerun `zitadel doctor`.",
346
+ nextCommands: [publicCliCommand("doctor", cliVersion)]
347
+ };
348
+ if (failedNames.has("port")) {
349
+ const fallbackPort = port === 8080 ? port + 1 : DEFAULT_LOCAL_SERVER_PORT;
350
+ return {
351
+ hint: `Port ${String(port)} is already in use. Stop the process using it, or choose another port for local Zitadel.`,
352
+ nextCommands: [publicCliCommand(`doctor --port ${String(fallbackPort)}`, cliVersion)]
353
+ };
354
+ }
355
+ if (failedNames.has("runtime")) return {
356
+ hint: "Existing local runtime metadata was found, but the local Zitadel server is not healthy. Start it again or reset stale local data.",
357
+ nextCommands: [publicCliCommand("start", cliVersion), publicCliCommand("reset --force", cliVersion)]
358
+ };
359
+ if (failed.some((check) => !LOCAL_RUNTIME_CHECK_NAMES.has(check.name))) return {
360
+ hint: `Run \`${publicCliCommand("doctor --fix", cliVersion)}\` to re-apply missing managed files.`,
361
+ nextCommands: [publicCliCommand("doctor --fix", cliVersion)]
362
+ };
363
+ return {
364
+ hint: "Fix the reported checks, then rerun `zitadel doctor`.",
365
+ nextCommands: [publicCliCommand("doctor", cliVersion)]
366
+ };
367
+ }
368
+ async function runLocalRuntimeChecks(cwd, image, port) {
369
+ const runtime = await readRuntimeMetadata(cwd);
370
+ const docker = await check("docker-cli", "Docker is reachable", async () => {
371
+ let result;
372
+ try {
373
+ result = await dockerAvailable();
374
+ } catch (error) {
375
+ throw new Error(dockerUnavailableMessage(error), { cause: error });
376
+ }
377
+ if (result.status !== 0) throw new Error(dockerUnavailableMessage(result.stderr || "docker version failed"));
378
+ return `Docker engine ${result.stdout.trim() || "available"}`;
379
+ }, "warn");
380
+ return [
381
+ docker,
382
+ docker.status === "pass" ? await check("image", `Image ${image} is available`, async () => {
383
+ try {
384
+ return await imageAvailable(image) === "local" ? `Image ${image} is available locally` : `Image ${image} is available from the registry`;
385
+ } catch (error) {
386
+ throw new Error(imageUnavailableMessage(image, error), { cause: error });
387
+ }
388
+ }, "warn") : {
389
+ name: "image",
390
+ status: "warn",
391
+ message: "Skipped image check because Docker is not reachable."
392
+ },
393
+ await check("state-dir", "Local state directory is writable", async () => {
394
+ const paths = localRuntimePaths(cwd);
395
+ await assertWritableDirectory(paths.dataDir);
396
+ return `${paths.dataDir} is writable`;
397
+ }, "warn"),
398
+ await check("port", `Port ${String(port)} is available`, async () => {
399
+ if (runtime && await checkLocalServerHealth(runtime.server_url)) return `${runtime.server_url} is already healthy`;
400
+ if (!await isPortAvailable(port)) throw new Error(`Port ${String(port)} is already in use`);
401
+ return `Port ${String(port)} is available`;
402
+ }, "warn"),
403
+ await check("runtime", "Existing local runtime is healthy", async () => {
404
+ if (!runtime) return "No existing runtime metadata";
405
+ if (!await checkLocalServerHealth(runtime.server_url)) throw new Error(`${runtime.server_url} did not respond to /healthz`);
406
+ return `${runtime.server_url} is healthy`;
407
+ })
408
+ ];
409
+ }
410
+ async function check(name, fallback, run, failureStatus = "fail") {
411
+ try {
412
+ return {
413
+ name,
414
+ status: "pass",
415
+ message: await run()
416
+ };
417
+ } catch (error) {
418
+ return {
419
+ name,
420
+ status: failureStatus,
421
+ message: error instanceof Error ? error.message : fallback
422
+ };
423
+ }
424
+ }
425
+ function dockerUnavailableMessage(error) {
426
+ const detail = error instanceof Error ? error.message : String(error);
427
+ return `Docker is not reachable${detail.trim() ? ` (${detail.trim()})` : ""}; \`zitadel start\` needs Docker, but cloud setup can continue.`;
428
+ }
429
+ function imageUnavailableMessage(image, error) {
430
+ const detail = error instanceof Error ? error.message : String(error);
431
+ return `Image ${image} is not available to Docker${detail.trim() ? ` (${detail.trim()})` : ""}; \`zitadel start\` may need a pull or a different image.`;
432
+ }
299
433
  //#endregion
300
434
  export { Doctor as default };
301
435