@zitadel/cli 0.1.0-alpha.4 → 0.1.0-alpha.5

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.
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 app.
3
+ Scaffolds Zitadel auth (login, register, profile, middleware) into a Next.js, React, Vue, Angular, or Nuxt app.
4
4
 
5
5
  ```sh
6
6
  npx @zitadel/cli@alpha start
@@ -37,13 +37,15 @@ back to `ghcr.io/zitadel/nextgen:latest`. Override with `--image` or
37
37
  `ZITADEL_LOCAL_IMAGE` for advanced debugging.
38
38
  `setup --server local` creates a project on that local server, asks which
39
39
  framework to scaffold when the directory is fresh, writes the Next.js app into
40
- the current directory, scaffolds `app/login`, `app/register`, and `proxy.ts`
41
- for Next 16+ or `middleware.ts` for older Next versions, writes `.env.local`
42
- and `.zitadel/`, and installs dependencies with the detected package manager.
43
- Pass `--skip-install` to install them yourself. The project's default user
44
- schema and login flow are provisioned server-side at creation time, so the CLI
45
- does not scaffold or upload them. Open `http://localhost:3000/login` to see the
46
- login page.
40
+ the current directory, scaffolds `app/login`, `app/register`, `app/profile`, and
41
+ `proxy.ts` for Next 16+ or `middleware.ts` for older Next versions. Fresh
42
+ scaffolds also replace the starter `app/page.tsx` with links to sign in, create
43
+ an account, and profile. Setup writes `.env.local` and `.zitadel/`, and installs
44
+ dependencies with the detected package manager. Pass `--skip-install` to install
45
+ them yourself. The project's default user schema and login flow are provisioned
46
+ server-side at creation time, so the CLI does not scaffold or upload them. Open
47
+ the dev server URL printed by your framework, register a user, log out, log back
48
+ in, and end on the signed-in profile page.
47
49
 
48
50
  For a reproducible tester report, use the exact alpha train from the GitHub
49
51
  Release:
@@ -74,24 +76,23 @@ passkey can sign in with either credential.
74
76
  <summary>Full command reference</summary>
75
77
 
76
78
  <!-- commands -->
77
-
78
- - [`zitadel apply`](#zitadel-apply)
79
- - [`zitadel autocomplete [SHELL]`](#zitadel-autocomplete-shell)
80
- - [`zitadel commands`](#zitadel-commands)
81
- - [`zitadel doctor`](#zitadel-doctor)
82
- - [`zitadel eject`](#zitadel-eject)
83
- - [`zitadel help [COMMAND]`](#zitadel-help-command)
84
- - [`zitadel logs`](#zitadel-logs)
85
- - [`zitadel plan`](#zitadel-plan)
86
- - [`zitadel reset`](#zitadel-reset)
87
- - [`zitadel search`](#zitadel-search)
88
- - [`zitadel setup`](#zitadel-setup)
89
- - [`zitadel start`](#zitadel-start)
90
- - [`zitadel status`](#zitadel-status)
91
- - [`zitadel stop`](#zitadel-stop)
92
- - [`zitadel uninstall`](#zitadel-uninstall)
93
- - [`zitadel version`](#zitadel-version)
94
- - [`zitadel which`](#zitadel-which)
79
+ * [`zitadel apply`](#zitadel-apply)
80
+ * [`zitadel autocomplete [SHELL]`](#zitadel-autocomplete-shell)
81
+ * [`zitadel commands`](#zitadel-commands)
82
+ * [`zitadel doctor`](#zitadel-doctor)
83
+ * [`zitadel eject`](#zitadel-eject)
84
+ * [`zitadel help [COMMAND]`](#zitadel-help-command)
85
+ * [`zitadel logs`](#zitadel-logs)
86
+ * [`zitadel plan`](#zitadel-plan)
87
+ * [`zitadel reset`](#zitadel-reset)
88
+ * [`zitadel search`](#zitadel-search)
89
+ * [`zitadel setup`](#zitadel-setup)
90
+ * [`zitadel start`](#zitadel-start)
91
+ * [`zitadel status`](#zitadel-status)
92
+ * [`zitadel stop`](#zitadel-stop)
93
+ * [`zitadel uninstall`](#zitadel-uninstall)
94
+ * [`zitadel version`](#zitadel-version)
95
+ * [`zitadel which`](#zitadel-which)
95
96
 
96
97
  ## `zitadel apply`
97
98
 
@@ -99,19 +100,19 @@ Validate and upload repo config to the platform.
99
100
 
100
101
  ```
101
102
  USAGE
102
- $ zitadel apply [--json] [-c <value>] [-s <value>] [-n] [-f] [--dry-run] [--verbose] [--debug]
103
- [-e development|preview|production]
103
+ $ zitadel apply [--json] [-c <value>] [-s <value>] [-n] [-f] [--dry-run] [--verbose] [--debug] [-e
104
+ development|preview|production]
104
105
 
105
106
  FLAGS
106
- -c, --cwd=<value> Project directory to operate on.
107
- -e, --environment=<option> Target environment (default: development).
108
- <options: development|preview|production>
109
- -f, --force Overwrite protected files on conflict.
110
- -n, --non-interactive Disable prompts. Required when scripting or running as an agent.
111
- -s, --server=<value> Override the resolved server URL.
112
- --debug Debug logging.
113
- --dry-run Preview without mutating files or the platform.
114
- --verbose Verbose logging.
107
+ -c, --cwd=<value> Project directory to operate on.
108
+ -e, --environment=<option> Target environment (default: development).
109
+ <options: development|preview|production>
110
+ -f, --force Overwrite protected files on conflict.
111
+ -n, --non-interactive Disable prompts. Required when scripting or running as an agent.
112
+ -s, --server=<value> Override the resolved server URL.
113
+ --debug Debug logging.
114
+ --dry-run Preview without mutating files or the platform.
115
+ --verbose Verbose logging.
115
116
 
116
117
  GLOBAL FLAGS
117
118
  --json Format output as json.
@@ -288,19 +289,19 @@ Validate config without mutation and preview the sync diff.
288
289
 
289
290
  ```
290
291
  USAGE
291
- $ zitadel plan [--json] [-c <value>] [-s <value>] [-n] [-f] [--dry-run] [--verbose] [--debug]
292
- [-e development|preview|production]
292
+ $ zitadel plan [--json] [-c <value>] [-s <value>] [-n] [-f] [--dry-run] [--verbose] [--debug] [-e
293
+ development|preview|production]
293
294
 
294
295
  FLAGS
295
- -c, --cwd=<value> Project directory to operate on.
296
- -e, --environment=<option> Target environment (default: development).
297
- <options: development|preview|production>
298
- -f, --force Overwrite protected files on conflict.
299
- -n, --non-interactive Disable prompts. Required when scripting or running as an agent.
300
- -s, --server=<value> Override the resolved server URL.
301
- --debug Debug logging.
302
- --dry-run Preview without mutating files or the platform.
303
- --verbose Verbose logging.
296
+ -c, --cwd=<value> Project directory to operate on.
297
+ -e, --environment=<option> Target environment (default: development).
298
+ <options: development|preview|production>
299
+ -f, --force Overwrite protected files on conflict.
300
+ -n, --non-interactive Disable prompts. Required when scripting or running as an agent.
301
+ -s, --server=<value> Override the resolved server URL.
302
+ --debug Debug logging.
303
+ --dry-run Preview without mutating files or the platform.
304
+ --verbose Verbose logging.
304
305
 
305
306
  GLOBAL FLAGS
306
307
  --json Format output as json.
@@ -356,7 +357,7 @@ Create a Zitadel project and scaffold local auth.
356
357
  ```
357
358
  USAGE
358
359
  $ zitadel setup [--json] [-c <value>] [-s <value>] [-n] [-f] [--dry-run] [--verbose] [--debug]
359
- [--framework next] [--renderer react|web-component] [--skip-install]
360
+ [--framework next|nuxt|react|vue|angular] [--renderer react|web-component] [--dev-port <value>] [--skip-install]
360
361
 
361
362
  FLAGS
362
363
  -c, --cwd=<value> Project directory to operate on.
@@ -364,9 +365,11 @@ FLAGS
364
365
  -n, --non-interactive Disable prompts. Required when scripting or running as an agent.
365
366
  -s, --server=<value> Override the resolved server URL.
366
367
  --debug Debug logging.
368
+ --dev-port=<value> Dev-server port; also the issuer origin registered with Zitadel. Defaults to the detected
369
+ port. Use distinct ports to run several scaffolded apps side by side.
367
370
  --dry-run Preview without mutating files or the platform.
368
371
  --framework=<option> Framework to target.
369
- <options: next>
372
+ <options: next|nuxt|react|vue|angular>
370
373
  --renderer=<option> Renderer (default: react).
371
374
  <options: react|web-component>
372
375
  --skip-install Do not install dependencies after setup updates package.json.
@@ -380,6 +383,8 @@ DESCRIPTION
380
383
 
381
384
  EXAMPLES
382
385
  $ zitadel setup --framework next
386
+
387
+ $ zitadel setup --framework react --dev-port 3000
383
388
  ```
384
389
 
385
390
  ## `zitadel start`
@@ -537,7 +542,6 @@ EXAMPLES
537
542
  ```
538
543
 
539
544
  _See code: [@oclif/plugin-which](https://github.com/oclif/plugin-which/blob/3.2.55/src/commands/which.ts)_
540
-
541
545
  <!-- commandsstop -->
542
546
 
543
547
  </details>
package/SKILLS.md CHANGED
@@ -4,8 +4,8 @@ description: >-
4
4
  Set up and manage Zitadel authentication in a local project with the
5
5
  agent-friendly `zitadel` CLI. Use when the user wants to add login,
6
6
  registration, or session handling, create a Zitadel project, scaffold auth
7
- routes for a Next.js App Router app, or plan and apply Zitadel config changes
8
- from repo state.
7
+ for a Next.js, React, Vue, Angular, or Nuxt app, or plan and apply Zitadel
8
+ config changes from repo state.
9
9
  ---
10
10
 
11
11
  # Zitadel CLI
@@ -56,7 +56,12 @@ layer, not the envelope.
56
56
  and login flow are provisioned server-side at creation, so setup neither
57
57
  scaffolds nor uploads them. Agents must pass `--framework` when scaffolding
58
58
  into a fresh directory; interactive humans can omit it and choose from the
59
- prompt. Flags: `--framework`, `--renderer`.
59
+ prompt. Flags: `--framework next|react|vue|angular|nuxt`, `--renderer
60
+ react|web-component` (selects the Next.js auth-page renderer; accepted for any
61
+ framework and recorded in `zitadel.json` branding, but only Next varies its
62
+ generated templates by it), `--dev-port` (dev-server port, also the issuer
63
+ origin registered with Zitadel — use distinct ports to run several scaffolded
64
+ apps side by side), `--skip-install`.
60
65
  - `plan` — validate config and preview the sync diff without mutating anything.
61
66
  - `apply` — validate and upload repo config to the platform.
62
67
  - `doctor` — verify generated app files and local state once `zitadel.json`
@@ -99,6 +104,14 @@ npx @zitadel/cli@0.1.0-alpha.N start --non-interactive --json
99
104
  npx @zitadel/cli@0.1.0-alpha.N setup --framework next --server local --non-interactive --json
100
105
  ```
101
106
 
107
+ After `setup`, follow `data.next_commands` to start the app. Prove the generated
108
+ auth flow in a visible browser by registering a unique user, logging out, logging
109
+ back in with the same email/password, and ending on the signed-in profile page.
110
+ Do not treat a rendered login or registration form as completion. If browser DOM
111
+ snapshots are sparse, target the component hooks exposed by the generated UI:
112
+ `zitadel-field-email-input`, `zitadel-field-password-input`, and
113
+ `zitadel-action-submit-button`.
114
+
102
115
  Repo config is authoritative: edit `zitadel.json` or files under `.zitadel/`,
103
116
  then re-run `plan` and `apply`. Managed files carry a marker comment; `eject`
104
117
  removes only files that still carry it, preserving anything the user replaced.
@@ -1,6 +1,6 @@
1
- import { t as BaseCommand } from "../oclif-B3Qhw0cj.mjs";
2
- import { o as readZitadelSecret } from "../project-kWWyS7fS.mjs";
3
- import { a as makeSyncers, i as runSyncLoop, n as summarizePlan, o as environmentSchema, r as buildSyncPlan, t as renderPlan } from "../sync-CHXZqYR7.mjs";
1
+ import { t as BaseCommand } from "../oclif-2t97lHfY.mjs";
2
+ import { o as readZitadelSecret } from "../project-IzPVR0Pr.mjs";
3
+ import { a as makeSyncers, i as runSyncLoop, n as summarizePlan, o as environmentSchema, r as buildSyncPlan, t as renderPlan } from "../sync-Df9S8Pio.mjs";
4
4
  import { Flags } from "@oclif/core";
5
5
  import { createZitadelClient } from "@zitadel/api/client";
6
6
  import { consola as consola$1 } from "consola";
@@ -1,7 +1,7 @@
1
- import { C as isObject, E as ZitadelError, a as DEFAULT_LOCAL_SERVER_PORT, b as publicCliCommand, c as checkLocalServerHealth, f as isPortAvailable, h as readRuntimeMetadata, l as defaultLocalServerImageForCliVersion, m as localServerUrl, s as assertLocalStateWritable, t as BaseCommand } from "../oclif-B3Qhw0cj.mjs";
2
- import { r as issuerFromPort, t as createOrca } from "../orca-BGM8VCgQ.mjs";
3
- import { a as readZitadelConfig, i as readRendererId, o as readZitadelSecret, r as readDevelopmentIssuer, t as hasZitadelConfig } from "../project-kWWyS7fS.mjs";
4
- import { o as imageAvailable, r as dockerAvailable } from "../docker-B4zvLujy.mjs";
1
+ import { D as ZitadelError, a as DEFAULT_LOCAL_SERVER_PORT, c as checkLocalServerHealth, f as isPortAvailable, h as readRuntimeMetadata, l as defaultLocalServerImageForCliVersion, m as localServerUrl, s as assertLocalStateWritable, t as BaseCommand, w as isObject, x as publicCliCommand } from "../oclif-2t97lHfY.mjs";
2
+ import { r as issuerFromPort, t as createOrca } from "../orca-CYqJP4ZJ.mjs";
3
+ import { a as readZitadelConfig, i as readRendererId, o as readZitadelSecret, r as readDevelopmentIssuer, t as hasZitadelConfig } from "../project-IzPVR0Pr.mjs";
4
+ import { o as imageAvailable, r as dockerAvailable } from "../docker--EAWr_WY.mjs";
5
5
  import { Flags } from "@oclif/core";
6
6
  import consola from "consola";
7
7
  import { join } from "node:path";
@@ -1,6 +1,6 @@
1
- import { E as ZitadelError, t as BaseCommand } from "../oclif-B3Qhw0cj.mjs";
2
- import { t as createOrca } from "../orca-BGM8VCgQ.mjs";
3
- import { a as readZitadelConfig, i as readRendererId } from "../project-kWWyS7fS.mjs";
1
+ import { D as ZitadelError, t as BaseCommand } from "../oclif-2t97lHfY.mjs";
2
+ import { t as createOrca } from "../orca-CYqJP4ZJ.mjs";
3
+ import { a as readZitadelConfig, i as readRendererId } from "../project-IzPVR0Pr.mjs";
4
4
  import { join } from "node:path";
5
5
  import { readFile, rename, rm, stat } from "node:fs/promises";
6
6
  //#region src/commands/eject.ts
@@ -16,7 +16,8 @@ async function resolveEjectActions(cwd) {
16
16
  rootConfigFiles: ["zitadel.json"],
17
17
  directories: [".zitadel"],
18
18
  envBackups: [".env.local"],
19
- dependencies: []
19
+ dependencies: [],
20
+ configEdits: []
20
21
  };
21
22
  const orca = createOrca();
22
23
  const framework = await orca.tryDetect(cwd);
@@ -114,7 +115,12 @@ var Eject = class Eject extends BaseCommand {
114
115
  });
115
116
  removed.push(rel);
116
117
  }
117
- if (removed.length === 0 && backedUp.length === 0) return this.emit({
118
+ const manualSteps = actions.configEdits.map((rel) => {
119
+ if (rel === "package.json" || rel.endsWith("/package.json")) return `Remove the "dev" script setup added to ${rel}`;
120
+ if (rel === "angular.json" || rel.endsWith("/angular.json")) return `Remove the Zitadel proxyConfig (and dev-server port) from the serve target in ${rel}`;
121
+ return `Remove the Zitadel configuration block from ${rel}`;
122
+ });
123
+ if (removed.length === 0 && backedUp.length === 0 && manualSteps.length === 0) return this.emit({
118
124
  status: "skipped",
119
125
  reason: "nothing-to-eject",
120
126
  data: { cwd }
@@ -127,7 +133,8 @@ var Eject = class Eject extends BaseCommand {
127
133
  files_removed: removed,
128
134
  files_preserved: preserved,
129
135
  backed_up: backedUp,
130
- next_commands: nextCommands
136
+ next_commands: nextCommands,
137
+ manual_steps: manualSteps
131
138
  }
132
139
  });
133
140
  }
@@ -1 +1 @@
1
- {"version":3,"file":"eject.mjs","names":[],"sources":["../../src/commands/eject.ts"],"sourcesContent":["import { readFile, rename, rm, stat } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nimport { BaseCommand, type JsonEnvelope } from \"../lib/oclif\";\nimport { ZitadelError } from \"../lib/errors\";\nimport { createOrca } from \"../lib/orca\";\nimport type { EjectActions } from \"../lib/orca/patchers/types\";\nimport { MANAGED_MARKER } from \"../lib/paths\";\nimport { readRendererId, readZitadelConfig } from \"../lib/project\";\n\n/**\n * Asks the framework patcher which artifacts it owns. Falls back to the\n * framework-agnostic set (`zitadel.json`, `.zitadel/`, `.env.local`) when the\n * framework or its patcher cannot be resolved, so an orphaned/partial project\n * can still be cleaned up.\n */\nasync function resolveEjectActions(cwd: string): Promise<EjectActions> {\n const fallback: EjectActions = {\n markedFiles: [],\n rootConfigFiles: [\"zitadel.json\"],\n directories: [\".zitadel\"],\n envBackups: [\".env.local\"],\n dependencies: [],\n };\n const orca = createOrca();\n const framework = await orca.tryDetect(cwd);\n if (!framework) {\n return fallback;\n }\n try {\n const config = await readZitadelConfig(cwd).catch(() => ({}) as Record<string, unknown>);\n return orca.patcherFor(framework.id).artifacts({\n framework,\n rendererId: readRendererId(config),\n });\n } catch {\n return fallback;\n }\n}\n\n/**\n * Builds the `next_commands` envelope field — manual follow-ups `eject` can't\n * safely run itself: deleting the `.env.local.ejected-*` backups it created\n * (only when some were made), and uninstalling the SDK packages the patcher\n * added (the CLI never modifies the user's `package.json` + lockfile +\n * `node_modules` directly; it just suggests the command).\n */\nfunction assembleNextCommands(\n backedUp: ReadonlyArray<unknown>,\n dependencies: ReadonlyArray<string>,\n): ReadonlyArray<string> {\n const commands: string[] = [];\n if (backedUp.length > 0) {\n commands.push(\"rm -f .env.local.ejected-*\");\n }\n for (const dep of dependencies) {\n commands.push(`npm uninstall ${dep}`);\n }\n return commands;\n}\n\nasync function pathExists(path: string): Promise<boolean> {\n try {\n await stat(path);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * `zitadel eject` — remove managed files and local Zitadel state.\n *\n * Removes Zitadel-managed files from the project, leaving the remote project\n * untouched. The set of files comes from the framework patcher's\n * {@link import(\"../lib/orca/patchers/types\").Patcher.artifacts}, so the patcher\n * is the single source of truth for what its integration owns.\n *\n * Marked code files are removed only when they still carry the managed marker\n * (user-replaced files are preserved); `zitadel.json` is removed; `.env.local`\n * is renamed to a timestamped backup; and `.zitadel/` is removed wholesale.\n * `--dry-run` reports without touching the filesystem; non-interactive runs\n * require `--force`.\n */\nexport default class Eject extends BaseCommand {\n static override description = \"Remove managed files and local Zitadel state.\";\n static override aliases = [\"uninstall\"];\n\n async run(): Promise<JsonEnvelope> {\n const { flags } = await this.parse(Eject);\n await this.toMeta(flags);\n const { cwd, force, nonInteractive, dryRun } = this.meta;\n\n if (!force && nonInteractive) {\n throw new ZitadelError(\"E_VALIDATION\", \"Eject requires --force in non-interactive mode\", {\n hint: \"Re-run with --force to confirm deletion of managed files.\",\n });\n }\n\n const actions = await resolveEjectActions(cwd);\n const removed: string[] = [];\n const preserved: string[] = [];\n const backedUp: string[] = [];\n\n for (const rel of actions.markedFiles) {\n const abs = join(cwd, rel);\n if (!(await pathExists(abs))) {\n continue;\n }\n const contents = await readFile(abs, \"utf8\").catch(() => \"\");\n if (!contents.includes(MANAGED_MARKER)) {\n preserved.push(rel);\n continue;\n }\n if (!dryRun) {\n await rm(abs, { force: true });\n }\n removed.push(rel);\n }\n\n for (const rel of actions.rootConfigFiles) {\n const abs = join(cwd, rel);\n if (!(await pathExists(abs))) {\n continue;\n }\n if (!dryRun) {\n await rm(abs, { force: true });\n }\n removed.push(rel);\n }\n\n for (const rel of actions.envBackups) {\n const abs = join(cwd, rel);\n if (!(await pathExists(abs))) {\n continue;\n }\n if (dryRun) {\n backedUp.push(`${rel} -> ${rel}.ejected-<timestamp>`);\n continue;\n }\n const stamp = new Date().toISOString().replace(/[:.]/g, \"-\");\n const backup = `${abs}.ejected-${stamp}`;\n await rename(abs, backup);\n backedUp.push(`${rel} -> ${backup.slice(cwd.length + 1)}`);\n }\n\n for (const rel of actions.directories) {\n const abs = join(cwd, rel);\n if (!(await pathExists(abs))) {\n continue;\n }\n if (!dryRun) {\n await rm(abs, { recursive: true, force: true });\n }\n removed.push(rel);\n }\n\n if (removed.length === 0 && backedUp.length === 0) {\n return this.emit({ status: \"skipped\", reason: \"nothing-to-eject\", data: { cwd } });\n }\n\n const nextCommands = assembleNextCommands(backedUp, actions.dependencies);\n\n return this.emit({\n status: \"ok\",\n data: {\n title: \"Zitadel ejected. Remote project is untouched.\",\n files_removed: removed,\n files_preserved: preserved,\n backed_up: backedUp,\n next_commands: nextCommands,\n },\n });\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"eject.mjs","names":[],"sources":["../../src/commands/eject.ts"],"sourcesContent":["import { readFile, rename, rm, stat } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nimport { BaseCommand, type JsonEnvelope } from \"../lib/oclif\";\nimport { ZitadelError } from \"../lib/errors\";\nimport { createOrca } from \"../lib/orca\";\nimport type { EjectActions } from \"../lib/orca/patchers/types\";\nimport { MANAGED_MARKER } from \"../lib/paths\";\nimport { readRendererId, readZitadelConfig } from \"../lib/project\";\n\n/**\n * Asks the framework patcher which artifacts it owns. Falls back to the\n * framework-agnostic set (`zitadel.json`, `.zitadel/`, `.env.local`) when the\n * framework or its patcher cannot be resolved, so an orphaned/partial project\n * can still be cleaned up.\n */\nasync function resolveEjectActions(cwd: string): Promise<EjectActions> {\n const fallback: EjectActions = {\n markedFiles: [],\n rootConfigFiles: [\"zitadel.json\"],\n directories: [\".zitadel\"],\n envBackups: [\".env.local\"],\n dependencies: [],\n configEdits: [],\n };\n const orca = createOrca();\n const framework = await orca.tryDetect(cwd);\n if (!framework) {\n return fallback;\n }\n try {\n const config = await readZitadelConfig(cwd).catch(() => ({}) as Record<string, unknown>);\n return orca.patcherFor(framework.id).artifacts({\n framework,\n rendererId: readRendererId(config),\n });\n } catch {\n return fallback;\n }\n}\n\n/**\n * Builds the `next_commands` envelope field — manual follow-ups `eject` can't\n * safely run itself: deleting the `.env.local.ejected-*` backups it created\n * (only when some were made), and uninstalling the SDK packages the patcher\n * added (the CLI never modifies the user's `package.json` + lockfile +\n * `node_modules` directly; it just suggests the command).\n */\nfunction assembleNextCommands(\n backedUp: ReadonlyArray<unknown>,\n dependencies: ReadonlyArray<string>,\n): ReadonlyArray<string> {\n const commands: string[] = [];\n if (backedUp.length > 0) {\n commands.push(\"rm -f .env.local.ejected-*\");\n }\n for (const dep of dependencies) {\n commands.push(`npm uninstall ${dep}`);\n }\n return commands;\n}\n\nasync function pathExists(path: string): Promise<boolean> {\n try {\n await stat(path);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * `zitadel eject` — remove managed files and local Zitadel state.\n *\n * Removes Zitadel-managed files from the project, leaving the remote project\n * untouched. The set of files comes from the framework patcher's\n * {@link import(\"../lib/orca/patchers/types\").Patcher.artifacts}, so the patcher\n * is the single source of truth for what its integration owns.\n *\n * Marked code files are removed only when they still carry the managed marker\n * (user-replaced files are preserved); `zitadel.json` is removed; `.env.local`\n * is renamed to a timestamped backup; and `.zitadel/` is removed wholesale.\n * `--dry-run` reports without touching the filesystem; non-interactive runs\n * require `--force`.\n */\nexport default class Eject extends BaseCommand {\n static override description = \"Remove managed files and local Zitadel state.\";\n static override aliases = [\"uninstall\"];\n\n async run(): Promise<JsonEnvelope> {\n const { flags } = await this.parse(Eject);\n await this.toMeta(flags);\n const { cwd, force, nonInteractive, dryRun } = this.meta;\n\n if (!force && nonInteractive) {\n throw new ZitadelError(\"E_VALIDATION\", \"Eject requires --force in non-interactive mode\", {\n hint: \"Re-run with --force to confirm deletion of managed files.\",\n });\n }\n\n const actions = await resolveEjectActions(cwd);\n const removed: string[] = [];\n const preserved: string[] = [];\n const backedUp: string[] = [];\n\n for (const rel of actions.markedFiles) {\n const abs = join(cwd, rel);\n if (!(await pathExists(abs))) {\n continue;\n }\n const contents = await readFile(abs, \"utf8\").catch(() => \"\");\n if (!contents.includes(MANAGED_MARKER)) {\n preserved.push(rel);\n continue;\n }\n if (!dryRun) {\n await rm(abs, { force: true });\n }\n removed.push(rel);\n }\n\n for (const rel of actions.rootConfigFiles) {\n const abs = join(cwd, rel);\n if (!(await pathExists(abs))) {\n continue;\n }\n if (!dryRun) {\n await rm(abs, { force: true });\n }\n removed.push(rel);\n }\n\n for (const rel of actions.envBackups) {\n const abs = join(cwd, rel);\n if (!(await pathExists(abs))) {\n continue;\n }\n if (dryRun) {\n backedUp.push(`${rel} -> ${rel}.ejected-<timestamp>`);\n continue;\n }\n const stamp = new Date().toISOString().replace(/[:.]/g, \"-\");\n const backup = `${abs}.ejected-${stamp}`;\n await rename(abs, backup);\n backedUp.push(`${rel} -> ${backup.slice(cwd.length + 1)}`);\n }\n\n for (const rel of actions.directories) {\n const abs = join(cwd, rel);\n if (!(await pathExists(abs))) {\n continue;\n }\n if (!dryRun) {\n await rm(abs, { recursive: true, force: true });\n }\n removed.push(rel);\n }\n\n // In-place config merges (vite.config.ts / angular.json / nuxt.config.ts)\n // can't be auto-reverted, so surface them as manual cleanup steps. The\n // Angular patcher also edits package.json (a `dev` script, not a config\n // block), so word that one accurately.\n const manualSteps = actions.configEdits.map((rel) => {\n if (rel === \"package.json\" || rel.endsWith(\"/package.json\")) {\n return `Remove the \"dev\" script setup added to ${rel}`;\n }\n if (rel === \"angular.json\" || rel.endsWith(\"/angular.json\")) {\n return `Remove the Zitadel proxyConfig (and dev-server port) from the serve target in ${rel}`;\n }\n return `Remove the Zitadel configuration block from ${rel}`;\n });\n\n if (removed.length === 0 && backedUp.length === 0 && manualSteps.length === 0) {\n return this.emit({ status: \"skipped\", reason: \"nothing-to-eject\", data: { cwd } });\n }\n\n const nextCommands = assembleNextCommands(backedUp, actions.dependencies);\n\n return this.emit({\n status: \"ok\",\n data: {\n title: \"Zitadel ejected. Remote project is untouched.\",\n files_removed: removed,\n files_preserved: preserved,\n backed_up: backedUp,\n next_commands: nextCommands,\n manual_steps: manualSteps,\n },\n });\n }\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
- import { E as ZitadelError, S as resolveCwd, b as publicCliCommand, h as readRuntimeMetadata, t as BaseCommand } from "../oclif-B3Qhw0cj.mjs";
2
- import { a as followContainerLogs, t as containerLogs } from "../docker-B4zvLujy.mjs";
1
+ import { C as resolveCwd, D as ZitadelError, h as readRuntimeMetadata, t as BaseCommand, x as publicCliCommand } from "../oclif-2t97lHfY.mjs";
2
+ import { a as followContainerLogs, t as containerLogs } from "../docker--EAWr_WY.mjs";
3
3
  import { Flags } from "@oclif/core";
4
4
  //#region src/commands/logs.ts
5
5
  var Logs = class Logs extends BaseCommand {
@@ -1,6 +1,6 @@
1
- import { t as BaseCommand } from "../oclif-B3Qhw0cj.mjs";
2
- import { o as readZitadelSecret } from "../project-kWWyS7fS.mjs";
3
- import { a as makeSyncers, n as summarizePlan, o as environmentSchema, r as buildSyncPlan, t as renderPlan } from "../sync-CHXZqYR7.mjs";
1
+ import { t as BaseCommand } from "../oclif-2t97lHfY.mjs";
2
+ import { o as readZitadelSecret } from "../project-IzPVR0Pr.mjs";
3
+ import { a as makeSyncers, n as summarizePlan, o as environmentSchema, r as buildSyncPlan, t as renderPlan } from "../sync-Df9S8Pio.mjs";
4
4
  import { Flags } from "@oclif/core";
5
5
  import { createZitadelClient } from "@zitadel/api/client";
6
6
  import { consola as consola$1 } from "consola";
@@ -1,5 +1,5 @@
1
- import { E as ZitadelError, S as resolveCwd, _ as removeRuntimeMetadata, b as publicCliCommand, g as removeLocalData, h as readRuntimeMetadata, p as localContainerName, t as BaseCommand } from "../oclif-B3Qhw0cj.mjs";
2
- import { u as stopAndRemoveContainer } from "../docker-B4zvLujy.mjs";
1
+ import { C as resolveCwd, D as ZitadelError, _ as removeRuntimeMetadata, g as removeLocalData, h as readRuntimeMetadata, p as localContainerName, t as BaseCommand, x as publicCliCommand } from "../oclif-2t97lHfY.mjs";
2
+ import { u as stopAndRemoveContainer } from "../docker--EAWr_WY.mjs";
3
3
  import { cancel, confirm, isCancel } from "@clack/prompts";
4
4
  //#region src/commands/reset.ts
5
5
  var Reset = class Reset extends BaseCommand {
@@ -1,6 +1,6 @@
1
- import { D as toZitadelError, E as ZitadelError, b as publicCliCommand, n as DEFAULT_SERVER, t as BaseCommand } from "../oclif-B3Qhw0cj.mjs";
2
- import { n as RENDERER_IDS, r as issuerFromPort, t as createOrca } from "../orca-BGM8VCgQ.mjs";
3
- import { n as hasZitadelSecret, t as hasZitadelConfig } from "../project-kWWyS7fS.mjs";
1
+ import { D as ZitadelError, O as toZitadelError, n as DEFAULT_SERVER, t as BaseCommand, x as publicCliCommand } from "../oclif-2t97lHfY.mjs";
2
+ import { n as RENDERER_IDS, r as issuerFromPort, t as createOrca } from "../orca-CYqJP4ZJ.mjs";
3
+ import { n as hasZitadelSecret, t as hasZitadelConfig } from "../project-IzPVR0Pr.mjs";
4
4
  import { cancel, confirm, intro, isCancel, outro, select, spinner, text } from "@clack/prompts";
5
5
  import { Flags } from "@oclif/core";
6
6
  import { createZitadelClient } from "@zitadel/api/client";
@@ -141,11 +141,16 @@ async function installDependenciesForSetup(input) {
141
141
  });
142
142
  }
143
143
  function outcome(input) {
144
- const startAction = `Start your project: ${input.devCommand} (then open ${input.issuer}/login)`;
144
+ const startAction = `Start your project: ${input.devCommand} (then open ${input.issuer})`;
145
+ const verifyAction = "Verify auth in the browser: register a user, log out, log in again with the same user, and confirm /profile shows Signed in.";
145
146
  return {
146
147
  install: input.install,
147
148
  devCommand: input.devCommand,
148
- nextActions: input.includeInstallCommand ? [`Install dependencies: ${input.install.command}`, startAction] : [startAction],
149
+ nextActions: input.includeInstallCommand ? [
150
+ `Install dependencies: ${input.install.command}`,
151
+ startAction,
152
+ verifyAction
153
+ ] : [startAction, verifyAction],
149
154
  nextCommands: input.includeInstallCommand ? [input.install.command, input.devCommand] : [input.devCommand]
150
155
  };
151
156
  }
@@ -188,7 +193,8 @@ function bail(value) {
188
193
  * becomes the issuer URL (`http://localhost:<port>`) via `issuerFromPort`.
189
194
  */
190
195
  var DevPortPrompt = class {
191
- async ask(answers, _ctx) {
196
+ async ask(answers, ctx) {
197
+ if (ctx.devPortFromFlag) return answers;
192
198
  const value = await text({
193
199
  message: "Dev server port",
194
200
  placeholder: String(answers.devPort),
@@ -587,7 +593,7 @@ const FRAMEWORK_OPTIONS = createOrca().availableFrameworks().map((framework) =>
587
593
  */
588
594
  var Setup = class Setup extends BaseCommand {
589
595
  static description = "Create a Zitadel project and scaffold local auth.";
590
- static examples = ["<%= config.bin %> setup --framework next"];
596
+ static examples = ["<%= config.bin %> setup --framework next", "<%= config.bin %> setup --framework react --dev-port 3000"];
591
597
  static flags = {
592
598
  framework: Flags.string({
593
599
  description: "Framework to target.",
@@ -597,11 +603,16 @@ var Setup = class Setup extends BaseCommand {
597
603
  description: "Renderer (default: react).",
598
604
  options: [...RENDERER_IDS]
599
605
  }),
606
+ "dev-port": Flags.integer({ description: "Dev-server port; also the issuer origin registered with Zitadel. Defaults to the detected port. Use distinct ports to run several scaffolded apps side by side." }),
600
607
  "skip-install": Flags.boolean({ description: "Do not install dependencies after setup updates package.json." })
601
608
  };
602
609
  async run() {
603
610
  const { flags } = await this.parse(Setup);
604
- await this.toMeta(flags);
611
+ try {
612
+ await this.toMeta(flags);
613
+ } catch (error) {
614
+ throw localSetupHint(error, flags.framework, this.config.version);
615
+ }
605
616
  const { cwd, nonInteractive, dryRun, force } = this.meta;
606
617
  if (await hasZitadelConfig(cwd)) return this.emit({
607
618
  status: "skipped",
@@ -623,6 +634,15 @@ var Setup = class Setup extends BaseCommand {
623
634
  consola$1.success(`Scaffolded ${framework.id} skeleton`);
624
635
  } else throw error;
625
636
  }
637
+ if (flags["dev-port"] !== void 0) {
638
+ const devPort = flags["dev-port"];
639
+ if (!Number.isInteger(devPort) || devPort < 1 || devPort > 65535) throw new ZitadelError("E_VALIDATION", `--dev-port must be an integer in 1..65535, got ${devPort}`);
640
+ framework = {
641
+ ...framework,
642
+ devPort,
643
+ url: issuerFromPort(devPort)
644
+ };
645
+ }
626
646
  let answers = {
627
647
  server: this.meta.source,
628
648
  devPort: framework.devPort
@@ -631,15 +651,21 @@ var Setup = class Setup extends BaseCommand {
631
651
  intro("Zitadel setup");
632
652
  const promptCtx = {
633
653
  framework,
634
- serverFlag: this.meta.serverFlag
654
+ serverFlag: this.meta.serverFlag,
655
+ devPortFromFlag: flags["dev-port"] !== void 0
635
656
  };
636
657
  for (const prompt of SETUP_PROMPTS) answers = await prompt.ask(answers, promptCtx);
637
658
  outro("Configuration captured");
638
659
  }
639
660
  const issuer = issuerFromPort(answers.devPort);
661
+ framework = {
662
+ ...framework,
663
+ devPort: answers.devPort,
664
+ url: issuer
665
+ };
640
666
  consola$1.start(`Creating project on ${answers.server}${dryRun ? " (dry run)" : ""}`);
641
667
  const unauthClient = createZitadelClient({ baseUrl: answers.server });
642
- const project = dryRun ? dryRunProject() : await createProjectWithLocalHint(unauthClient, answers.server, this.meta.cliVersion);
668
+ const project = dryRun ? dryRunProject(issuer) : await createProjectWithLocalHint(unauthClient, answers.server, this.meta.cliVersion, issuer, framework.id);
643
669
  consola$1.success(`Created project ${project.id}`);
644
670
  const ctx = {
645
671
  framework,
@@ -647,7 +673,8 @@ var Setup = class Setup extends BaseCommand {
647
673
  project,
648
674
  issuer,
649
675
  server: answers.server,
650
- cliVersion: this.meta.cliVersion
676
+ cliVersion: this.meta.cliVersion,
677
+ scaffoldedFramework
651
678
  };
652
679
  consola$1.start(`Patching project files${dryRun ? " (dry run)" : ""}`);
653
680
  const result = await orca.patcherFor(framework.id).patch(ctx, {
@@ -726,23 +753,23 @@ async function resolveScaffoldFramework(framework, nonInteractive, orca) {
726
753
  return new PickFrameworkPrompt().ask(orca.availableFrameworks());
727
754
  }
728
755
  /** A deterministic stand-in project for `--dry-run`, so no remote call is made. */
729
- function dryRunProject() {
756
+ function dryRunProject(issuer) {
730
757
  return {
731
758
  id: "dry-run-0000",
732
759
  projectSecret: "sk_proj_dry_run_full",
733
760
  previewSecret: "sk_proj_dry_run_preview",
734
- previewOrigins: [],
761
+ previewOrigins: [issuer],
735
762
  createdAt: "2026-04-21T14:03:11.000Z"
736
763
  };
737
764
  }
738
- async function createProjectWithLocalHint(client, server, cliVersion) {
765
+ async function createProjectWithLocalHint(client, server, cliVersion, issuer, framework) {
739
766
  try {
740
- return await client.createProject({ previewOrigins: [] });
767
+ return await client.createProject({ previewOrigins: [issuer] });
741
768
  } catch (error) {
742
769
  const normalized = toZitadelError(error);
743
770
  throw new ZitadelError(normalized.code, normalized.message, {
744
- hint: `${normalized.hint ? `${normalized.hint} ` : ""}If you meant to use a local Zitadel server, run npx @zitadel/cli@alpha start and retry setup with --server local.`,
745
- nextCommands: [publicCliCommand("start", cliVersion), publicCliCommand("setup --server local", cliVersion)],
771
+ hint: `${normalized.hint ? `${normalized.hint} ` : ""}If you meant to use a local Zitadel server, start it first and retry setup with --framework ${framework} --server local.`,
772
+ nextCommands: [publicCliCommand("start", cliVersion), publicCliCommand(`setup --framework ${framework} --server local`, cliVersion)],
746
773
  details: {
747
774
  server,
748
775
  original: normalized.details
@@ -750,6 +777,16 @@ async function createProjectWithLocalHint(client, server, cliVersion) {
750
777
  });
751
778
  }
752
779
  }
780
+ function localSetupHint(error, framework, cliVersion) {
781
+ const normalized = toZitadelError(error);
782
+ if (normalized.code !== "E_LOCAL_SERVER_NOT_RUNNING") return error;
783
+ const setupCommand = framework ? `setup --framework ${framework} --server local` : "setup --server local";
784
+ return new ZitadelError(normalized.code, normalized.message, {
785
+ hint: `${normalized.hint ? `${normalized.hint} ` : ""}Start local Zitadel first, then rerun setup. After setup succeeds, follow its next_commands to start the app and verify registration, logout, and login in the browser.`,
786
+ nextCommands: [publicCliCommand("start", cliVersion), publicCliCommand(setupCommand, cliVersion)],
787
+ details: normalized.details
788
+ });
789
+ }
753
790
  /** Renders an absolute path relative to `cwd` for human-readable output. */
754
791
  function relativeDisplay(cwd, path) {
755
792
  return path.startsWith(cwd) ? path.slice(cwd.length + 1) : path;
@@ -801,6 +838,7 @@ const SENTENCE_BY_PATH = {
801
838
  ".env.example": { subject: "the .env example template" },
802
839
  ".env.local": { subject: "the local development environment variables" },
803
840
  ".zitadel/state.json": { subject: "the empty sync state file" },
841
+ "app/page.tsx": { subject: "the auth home page" },
804
842
  "app/login/page.tsx": { subject: "the login page" },
805
843
  "app/register/page.tsx": { subject: "the registration page" },
806
844
  "app/profile/page.tsx": { subject: "the profile page" },
@@ -829,6 +867,7 @@ function buildSummary(opts) {
829
867
  secondary: path(fileNameOf(packageJsonHit))
830
868
  });
831
869
  for (const [label, suffix] of [
870
+ ["Home page", "app/page.tsx"],
832
871
  ["Login page", "app/login/page.tsx"],
833
872
  ["Register page", "app/register/page.tsx"],
834
873
  ["Profile page", "app/profile/page.tsx"],