@zitadel/cli 0.1.0-alpha.15 → 0.1.0-alpha.16

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 (43) hide show
  1. package/README.md +2 -2
  2. package/SKILLS.md +15 -6
  3. package/dist/commands/apply.mjs +8 -5
  4. package/dist/commands/apply.mjs.map +1 -1
  5. package/dist/commands/doctor.mjs +7 -7
  6. package/dist/commands/eject.mjs +17 -5
  7. package/dist/commands/eject.mjs.map +1 -1
  8. package/dist/commands/logs.mjs +2 -2
  9. package/dist/commands/plan.mjs +8 -5
  10. package/dist/commands/plan.mjs.map +1 -1
  11. package/dist/commands/reset.mjs +2 -2
  12. package/dist/commands/schemas/list.mjs +3 -3
  13. package/dist/commands/setup.mjs +128 -34
  14. package/dist/commands/setup.mjs.map +1 -1
  15. package/dist/commands/start.mjs +4 -4
  16. package/dist/commands/status.mjs +40 -9
  17. package/dist/commands/status.mjs.map +1 -1
  18. package/dist/commands/stop.mjs +3 -3
  19. package/dist/{docker-D7BSD5C9.mjs → docker-BbX0yOpg.mjs} +2 -2
  20. package/dist/{docker-D7BSD5C9.mjs.map → docker-BbX0yOpg.mjs.map} +1 -1
  21. package/dist/{docker-guidance-mcTT3_0E.mjs → docker-guidance-3UZESTfb.mjs} +2 -2
  22. package/dist/{docker-guidance-mcTT3_0E.mjs.map → docker-guidance-3UZESTfb.mjs.map} +1 -1
  23. package/dist/{environment-BQF7LeCz.mjs → environment-ABj1IWZt.mjs} +1 -1
  24. package/dist/{environment-BQF7LeCz.mjs.map → environment-ABj1IWZt.mjs.map} +1 -1
  25. package/dist/journey-guidance-E_3_UwGt.mjs +29 -0
  26. package/dist/journey-guidance-E_3_UwGt.mjs.map +1 -0
  27. package/dist/{oclif-Bm-FkF6z.mjs → oclif-SjHLB_XK.mjs} +17 -35
  28. package/dist/oclif-SjHLB_XK.mjs.map +1 -0
  29. package/dist/{orca-CpXj_XsA.mjs → orca-_TmrJQMw.mjs} +209 -163
  30. package/dist/orca-_TmrJQMw.mjs.map +1 -0
  31. package/dist/{ports-CxBKS1ga.mjs → ports-XujDtA47.mjs} +1 -1
  32. package/dist/{ports-CxBKS1ga.mjs.map → ports-XujDtA47.mjs.map} +1 -1
  33. package/dist/{processes-BVqYsxT8.mjs → processes-p2PPbKGG.mjs} +1 -1
  34. package/dist/{processes-BVqYsxT8.mjs.map → processes-p2PPbKGG.mjs.map} +1 -1
  35. package/dist/{project-Dk7V0xka.mjs → project-DXc7q4wN.mjs} +2 -2
  36. package/dist/{project-Dk7V0xka.mjs.map → project-DXc7q4wN.mjs.map} +1 -1
  37. package/dist/{sync-CYuHVQT5.mjs → sync-Bu_iLIbb.mjs} +148 -30
  38. package/dist/sync-Bu_iLIbb.mjs.map +1 -0
  39. package/oclif.manifest.json +12 -1
  40. package/package.json +5 -5
  41. package/dist/oclif-Bm-FkF6z.mjs.map +0 -1
  42. package/dist/orca-CpXj_XsA.mjs.map +0 -1
  43. package/dist/sync-CYuHVQT5.mjs.map +0 -1
package/README.md CHANGED
@@ -38,8 +38,8 @@ starting a local runtime. Use `--runtime docker`, `--image`, or
38
38
  framework to scaffold when the directory is fresh, writes the Next.js app into
39
39
  the current directory, scaffolds `app/login`, `app/register`, `app/profile`, and
40
40
  `proxy.ts` for Next 16+ or `middleware.ts` for older Next versions. Fresh
41
- scaffolds also replace the starter `app/page.tsx` with links to sign in, create
42
- an account, and profile. Setup writes `.env.local` and `.zitadel/`, and installs
41
+ scaffolds also replace the starter `app/page.tsx` with a redirect to `/login`.
42
+ Setup writes `.env.local` and `.zitadel/`, and installs
43
43
  dependencies with the detected package manager. Pass `--skip-install` to install
44
44
  them yourself. The project's default user schema and login flow are provisioned
45
45
  from versioned local defaults; setup writes editable copies into
package/SKILLS.md CHANGED
@@ -43,11 +43,16 @@ Each invocation prints one JSON object:
43
43
  - `cli_version`, `command`, `source`: always present.
44
44
  - On success: `data` with the command-specific payload.
45
45
  - On a no-op: `reason` (e.g. `no-framework-detected`, `orphaned-config`).
46
- - On failure: `code` (e.g. `E_VALIDATION`, `E_NETWORK`, `E_CONFLICT`) and
47
- `message`.
46
+ - On failure: `code` (e.g. `E_VALIDATION`, `E_NETWORK`, `E_NOT_FOUND`,
47
+ `E_CONFLICT`) and `message`.
48
48
  - `next_commands`: the suggested follow-ups. Prefer these over free-text hints.
49
49
  - `E_LOCAL_SERVER_NOT_RUNNING`: start the local runtime with
50
50
  `npx @zitadel/cli@alpha start`, then retry with `--server local`.
51
+ - `E_NOT_FOUND`: an HTTP 404 from the target server. With the platform's
52
+ error envelope it names a missing resource (e.g. an unknown schema id);
53
+ without one the endpoint itself is missing — the `--server` value likely
54
+ points at something that is not a Zitadel platform API. Follow
55
+ `next_commands` (usually `start` + retry with `--server local`).
51
56
  - `E_PORT_IN_USE`: the requested local runtime port already has a listener.
52
57
  Stop that process, run `npx @zitadel/cli@alpha stop --all` for host-wide
53
58
  CLI-managed local runtimes, or choose another `start --port`.
@@ -57,9 +62,9 @@ UIs display both streams together, but the machine contract is one parseable
57
62
  JSON object on stdout; installer, audit, and package-manager progress belongs
58
63
  on stderr.
59
64
 
60
- Exit codes mirror the error class (3 = validation, 4 = network, 5 = conflict,
61
- 1 = auth, 2 = not-implemented). An unknown command is handled by the CLI's help
62
- layer, not the envelope.
65
+ Exit codes mirror the error class (3 = validation, 4 = network or not-found,
66
+ 5 = conflict, 1 = auth, 2 = not-implemented). An unknown command is handled by
67
+ the CLI's help layer, not the envelope.
63
68
 
64
69
  ## Commands
65
70
 
@@ -76,7 +81,11 @@ layer, not the envelope.
76
81
  framework and recorded in `zitadel.json` branding, but only Next varies its
77
82
  generated templates by it), `--dev-port` (dev-server port, also the issuer
78
83
  origin registered with Zitadel — use distinct ports to run several scaffolded
79
- apps side by side), `--skip-install`.
84
+ apps side by side), `--preset password-first|passkey-first` (the sign-in
85
+ experience the scaffold starts from: `password-first` is the default —
86
+ email + password with passkey optional during registration; `passkey-first`
87
+ enters login on a one-tap passkey step with an email + password fallback;
88
+ recorded in `zitadel.json`), `--skip-install`.
80
89
  - `plan` — validate config and preview the sync diff without mutating anything.
81
90
  - `apply` — validate and upload repo config to the platform.
82
91
  - `schemas list` — inspect the revision history of a user-schema, filtered by
@@ -1,7 +1,7 @@
1
- import { t as BaseCommand } from "../oclif-Bm-FkF6z.mjs";
2
- import { o as readZitadelSecret } from "../project-Dk7V0xka.mjs";
3
- import { a as runSyncLoop, c as makeSyncers, n as summarizePlan, r as buildSyncPlan, t as renderPlan } from "../sync-CYuHVQT5.mjs";
4
- import { t as environmentSchema } from "../environment-BQF7LeCz.mjs";
1
+ import { t as BaseCommand } from "../oclif-SjHLB_XK.mjs";
2
+ import { o as readZitadelSecret } from "../project-DXc7q4wN.mjs";
3
+ import { i as buildSyncPlan, l as makeSyncers, n as renderPlan, o as runSyncLoop, r as summarizePlan, t as collectPlanWarnings } from "../sync-Bu_iLIbb.mjs";
4
+ import { t as environmentSchema } from "../environment-ABj1IWZt.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";
@@ -62,7 +62,10 @@ var Apply = class Apply extends BaseCommand {
62
62
  consola$1.success(`Plan: ${summary.creates} create${summary.creates === 1 ? "" : "s"}, ${summary.updates} update${summary.updates === 1 ? "" : "s"}, ${summary.revisions} new revision${summary.revisions === 1 ? "" : "s"}, ${summary.deletes} delete${summary.deletes === 1 ? "" : "s"}`);
63
63
  return this.emit({
64
64
  status: "ok",
65
- data: summary,
65
+ data: {
66
+ ...summary,
67
+ warnings: collectPlanWarnings(plan)
68
+ },
66
69
  pretty: renderPlan(plan, isTTY)
67
70
  });
68
71
  }
@@ -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 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({\n client,\n projectId: secret.project_id,\n env,\n });\n\n if (!dryRun) {\n consola.start(\"Syncing schemas and flows to Zitadel\");\n const { filesUpdated } = await runSyncLoop(cwd, syncers);\n consola.success(\"Sync complete\");\n return this.emit({ status: \"ok\", data: { synced: true, files_updated: filesUpdated } });\n }\n\n consola.start(\"Building plan (dry run)\");\n const plan = await buildSyncPlan(cwd, syncers, true);\n const summary = summarizePlan(plan);\n this.recordTelemetry({\n creates: summary.creates,\n updates: summary.updates,\n revisions: summary.revisions,\n deletes: summary.deletes,\n total: summary.total,\n });\n consola.success(\n `Plan: ${summary.creates} create${summary.creates === 1 ? \"\" : \"s\"}, ` +\n `${summary.updates} update${summary.updates === 1 ? \"\" : \"s\"}, ` +\n `${summary.revisions} new revision${summary.revisions === 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;CAC9B,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;GAC1B,QALa,oBAAoB;IACjC,SAAS;IACT,OAAO,OAAO;IACf,CAEO;GACN,WAAW,OAAO;GAClB;GACD,CAAC;AAEF,MAAI,CAAC,QAAQ;AACX,aAAQ,MAAM,uCAAuC;GACrD,MAAM,EAAE,iBAAiB,MAAM,YAAY,KAAK,QAAQ;AACxD,aAAQ,QAAQ,gBAAgB;AAChC,UAAO,KAAK,KAAK;IAAE,QAAQ;IAAM,MAAM;KAAE,QAAQ;KAAM,eAAe;KAAc;IAAE,CAAC;;AAGzF,YAAQ,MAAM,0BAA0B;EACxC,MAAM,OAAO,MAAM,cAAc,KAAK,SAAS,KAAK;EACpD,MAAM,UAAU,cAAc,KAAK;AACnC,OAAK,gBAAgB;GACnB,SAAS,QAAQ;GACjB,SAAS,QAAQ;GACjB,WAAW,QAAQ;GACnB,SAAS,QAAQ;GACjB,OAAO,QAAQ;GAChB,CAAC;AACF,YAAQ,QACN,SAAS,QAAQ,QAAQ,SAAS,QAAQ,YAAY,IAAI,KAAK,IAAI,IAC9D,QAAQ,QAAQ,SAAS,QAAQ,YAAY,IAAI,KAAK,IAAI,IAC1D,QAAQ,UAAU,eAAe,QAAQ,cAAc,IAAI,KAAK,IAAI,IACpE,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 {\n buildSyncPlan,\n collectPlanWarnings,\n makeSyncers,\n renderPlan,\n runSyncLoop,\n summarizePlan,\n} 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 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({\n client,\n projectId: secret.project_id,\n env,\n });\n\n if (!dryRun) {\n consola.start(\"Syncing schemas and flows to Zitadel\");\n const { filesUpdated } = await runSyncLoop(cwd, syncers);\n consola.success(\"Sync complete\");\n return this.emit({ status: \"ok\", data: { synced: true, files_updated: filesUpdated } });\n }\n\n consola.start(\"Building plan (dry run)\");\n const plan = await buildSyncPlan(cwd, syncers, true);\n const summary = summarizePlan(plan);\n this.recordTelemetry({\n creates: summary.creates,\n updates: summary.updates,\n revisions: summary.revisions,\n deletes: summary.deletes,\n total: summary.total,\n });\n consola.success(\n `Plan: ${summary.creates} create${summary.creates === 1 ? \"\" : \"s\"}, ` +\n `${summary.updates} update${summary.updates === 1 ? \"\" : \"s\"}, ` +\n `${summary.revisions} new revision${summary.revisions === 1 ? \"\" : \"s\"}, ` +\n `${summary.deletes} delete${summary.deletes === 1 ? \"\" : \"s\"}`,\n );\n return this.emit({\n status: \"ok\",\n data: { ...summary, warnings: collectPlanWarnings(plan) },\n pretty: renderPlan(plan, isTTY),\n });\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AA0BA,IAAqB,QAArB,MAAqB,cAAc,YAAY;CAC7C,OAAgB,cAAc;CAC9B,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;GAC1B,QALa,oBAAoB;IACjC,SAAS;IACT,OAAO,OAAO;IACf,CAEO;GACN,WAAW,OAAO;GAClB;GACD,CAAC;AAEF,MAAI,CAAC,QAAQ;AACX,aAAQ,MAAM,uCAAuC;GACrD,MAAM,EAAE,iBAAiB,MAAM,YAAY,KAAK,QAAQ;AACxD,aAAQ,QAAQ,gBAAgB;AAChC,UAAO,KAAK,KAAK;IAAE,QAAQ;IAAM,MAAM;KAAE,QAAQ;KAAM,eAAe;KAAc;IAAE,CAAC;;AAGzF,YAAQ,MAAM,0BAA0B;EACxC,MAAM,OAAO,MAAM,cAAc,KAAK,SAAS,KAAK;EACpD,MAAM,UAAU,cAAc,KAAK;AACnC,OAAK,gBAAgB;GACnB,SAAS,QAAQ;GACjB,SAAS,QAAQ;GACjB,WAAW,QAAQ;GACnB,SAAS,QAAQ;GACjB,OAAO,QAAQ;GAChB,CAAC;AACF,YAAQ,QACN,SAAS,QAAQ,QAAQ,SAAS,QAAQ,YAAY,IAAI,KAAK,IAAI,IAC9D,QAAQ,QAAQ,SAAS,QAAQ,YAAY,IAAI,KAAK,IAAI,IAC1D,QAAQ,UAAU,eAAe,QAAQ,cAAc,IAAI,KAAK,IAAI,IACpE,QAAQ,QAAQ,SAAS,QAAQ,YAAY,IAAI,KAAK,MAC5D;AACD,SAAO,KAAK,KAAK;GACf,QAAQ;GACR,MAAM;IAAE,GAAG;IAAS,UAAU,oBAAoB,KAAK;IAAE;GACzD,QAAQ,WAAW,MAAM,MAAM;GAChC,CAAC"}
@@ -1,11 +1,11 @@
1
- import { C as isObject, E as ZitadelError, a as DEFAULT_LOCAL_SERVER_PORT, b as publicCliCommand, c as checkLocalServerHealth, l as defaultLocalServerImageForCliVersion, m as readRuntimeMetadata, p as localServerUrl, s as assertLocalStateWritable, t as BaseCommand } from "../oclif-Bm-FkF6z.mjs";
2
- import { i as issuerFromPort, t as createOrca } from "../orca-CpXj_XsA.mjs";
3
- import { a as readZitadelConfig, i as readRendererId, o as readZitadelSecret, r as readDevelopmentIssuer, t as hasZitadelConfig } from "../project-Dk7V0xka.mjs";
1
+ import { C as isObject, E as ZitadelError, a as DEFAULT_LOCAL_SERVER_PORT, b as publicCliCommand, c as checkLocalServerHealth, l as defaultLocalServerImageForCliVersion, m as readRuntimeMetadata, p as localServerUrl, s as assertLocalStateWritable, t as BaseCommand } from "../oclif-SjHLB_XK.mjs";
2
+ import { o as issuerFromPort, t as createOrca } from "../orca-_TmrJQMw.mjs";
3
+ import { a as readZitadelConfig, i as readRendererId, o as readZitadelSecret, r as readDevelopmentIssuer, t as hasZitadelConfig } from "../project-DXc7q4wN.mjs";
4
4
  import { t as SCHEMAS_DIR } from "../user-schema-DDz5-lX5.mjs";
5
- import { n as listenersForPort } from "../ports-CxBKS1ga.mjs";
6
- import { d as assertServerPackageAvailable, o as imageAvailable, r as dockerAvailable } from "../docker-D7BSD5C9.mjs";
7
- import { n as dockerUnavailableMessage, t as dockerRuntimeGuidance } from "../docker-guidance-mcTT3_0E.mjs";
8
- import { t as discoverManagedRuntimeProcesses } from "../processes-BVqYsxT8.mjs";
5
+ import { n as listenersForPort } from "../ports-XujDtA47.mjs";
6
+ import { d as assertServerPackageAvailable, o as imageAvailable, r as dockerAvailable } from "../docker-BbX0yOpg.mjs";
7
+ import { n as dockerUnavailableMessage, t as dockerRuntimeGuidance } from "../docker-guidance-3UZESTfb.mjs";
8
+ import { t as discoverManagedRuntimeProcesses } from "../processes-p2PPbKGG.mjs";
9
9
  import { chmod, readFile, readdir, stat, writeFile } from "node:fs/promises";
10
10
  import { join } from "node:path";
11
11
  import { Flags } from "@oclif/core";
@@ -1,7 +1,7 @@
1
- import { E as ZitadelError, t as BaseCommand } from "../oclif-Bm-FkF6z.mjs";
2
- import { t as createOrca } from "../orca-CpXj_XsA.mjs";
3
- import { a as readZitadelConfig, i as readRendererId } from "../project-Dk7V0xka.mjs";
4
- import { readFile, rename, rm, stat } from "node:fs/promises";
1
+ import { E as ZitadelError, t as BaseCommand } from "../oclif-SjHLB_XK.mjs";
2
+ import { a as removeGuidanceSection, i as AGENTS_HEADER, t as createOrca } from "../orca-_TmrJQMw.mjs";
3
+ import { a as readZitadelConfig, i as readRendererId } from "../project-DXc7q4wN.mjs";
4
+ import { readFile, rename, rm, stat, writeFile } from "node:fs/promises";
5
5
  import { join } from "node:path";
6
6
  //#region src/commands/eject.ts
7
7
  /**
@@ -17,7 +17,8 @@ async function resolveEjectActions(cwd) {
17
17
  directories: [".zitadel"],
18
18
  envBackups: [".env.local"],
19
19
  dependencies: [],
20
- configEdits: []
20
+ configEdits: [],
21
+ guidanceFiles: ["AGENTS.md", "README.md"]
21
22
  };
22
23
  const orca = createOrca();
23
24
  const framework = await orca.tryDetect(cwd);
@@ -115,6 +116,17 @@ var Eject = class Eject extends BaseCommand {
115
116
  });
116
117
  removed.push(rel);
117
118
  }
119
+ for (const rel of actions.guidanceFiles) {
120
+ const abs = join(cwd, rel);
121
+ if (!await pathExists(abs)) continue;
122
+ const contents = await readFile(abs, "utf8").catch(() => "");
123
+ const stripped = removeGuidanceSection(contents);
124
+ if (stripped === contents) continue;
125
+ const husk = stripped.trim() === "" || stripped.trim() === AGENTS_HEADER.trim();
126
+ if (!dryRun) if (husk) await rm(abs, { force: true });
127
+ else await writeFile(abs, stripped);
128
+ removed.push(husk ? rel : `${rel} (managed section)`);
129
+ }
118
130
  const manualSteps = actions.configEdits.map((rel) => {
119
131
  if (rel === "package.json" || rel.endsWith("/package.json")) return `Remove the "dev" script setup added to ${rel}`;
120
132
  if (rel === "angular.json" || rel.endsWith("/angular.json")) return `Remove the Zitadel proxyConfig (and dev-server port) from the serve target in ${rel}`;
@@ -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 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
+ {"version":3,"file":"eject.mjs","names":[],"sources":["../../src/commands/eject.ts"],"sourcesContent":["import { readFile, rename, rm, stat, writeFile } 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 { AGENTS_HEADER, removeGuidanceSection } from \"../lib/orca/patchers/rule/guidance\";\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 guidanceFiles: [\"AGENTS.md\", \"README.md\"],\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 // Guidance sections live inside user-owned docs (README.md / AGENTS.md):\n // strip only the marker-fenced section so the stale golden path doesn't\n // outlive the files it points at. The whole file goes only when nothing\n // but the scaffold-created header (or whitespace) would remain — i.e. we\n // created it and nobody added anything since.\n for (const rel of actions.guidanceFiles) {\n const abs = join(cwd, rel);\n if (!(await pathExists(abs))) {\n continue;\n }\n const contents = await readFile(abs, \"utf8\").catch(() => \"\");\n const stripped = removeGuidanceSection(contents);\n if (stripped === contents) {\n continue;\n }\n const husk = stripped.trim() === \"\" || stripped.trim() === AGENTS_HEADER.trim();\n if (!dryRun) {\n if (husk) {\n await rm(abs, { force: true });\n } else {\n await writeFile(abs, stripped);\n }\n }\n removed.push(husk ? rel : `${rel} (managed section)`);\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":";;;;;;;;;;;;AAiBA,eAAe,oBAAoB,KAAoC;CACrE,MAAM,WAAyB;EAC7B,aAAa,EAAE;EACf,iBAAiB,CAAC,eAAe;EACjC,aAAa,CAAC,WAAW;EACzB,YAAY,CAAC,aAAa;EAC1B,cAAc,EAAE;EAChB,aAAa,EAAE;EACf,eAAe,CAAC,aAAa,YAAY;EAC1C;CACD,MAAM,OAAO,YAAY;CACzB,MAAM,YAAY,MAAM,KAAK,UAAU,IAAI;AAC3C,KAAI,CAAC,UACH,QAAO;AAET,KAAI;EACF,MAAM,SAAS,MAAM,kBAAkB,IAAI,CAAC,aAAa,EAAE,EAA6B;AACxF,SAAO,KAAK,WAAW,UAAU,GAAG,CAAC,UAAU;GAC7C;GACA,YAAY,eAAe,OAAO;GACnC,CAAC;SACI;AACN,SAAO;;;;;;;;;;AAWX,SAAS,qBACP,UACA,cACuB;CACvB,MAAM,WAAqB,EAAE;AAC7B,KAAI,SAAS,SAAS,EACpB,UAAS,KAAK,6BAA6B;AAE7C,MAAK,MAAM,OAAO,aAChB,UAAS,KAAK,iBAAiB,MAAM;AAEvC,QAAO;;AAGT,eAAe,WAAW,MAAgC;AACxD,KAAI;AACF,QAAM,KAAK,KAAK;AAChB,SAAO;SACD;AACN,SAAO;;;;;;;;;;;;;;;;;AAkBX,IAAqB,QAArB,MAAqB,cAAc,YAAY;CAC7C,OAAgB,cAAc;CAC9B,OAAgB,UAAU,CAAC,YAAY;CAEvC,MAAM,MAA6B;EACjC,MAAM,EAAE,UAAU,MAAM,KAAK,MAAM,MAAM;AACzC,QAAM,KAAK,OAAO,MAAM;EACxB,MAAM,EAAE,KAAK,OAAO,gBAAgB,WAAW,KAAK;AAEpD,MAAI,CAAC,SAAS,eACZ,OAAM,IAAI,aAAa,gBAAgB,kDAAkD,EACvF,MAAM,6DACP,CAAC;EAGJ,MAAM,UAAU,MAAM,oBAAoB,IAAI;EAC9C,MAAM,UAAoB,EAAE;EAC5B,MAAM,YAAsB,EAAE;EAC9B,MAAM,WAAqB,EAAE;AAE7B,OAAK,MAAM,OAAO,QAAQ,aAAa;GACrC,MAAM,MAAM,KAAK,KAAK,IAAI;AAC1B,OAAI,CAAE,MAAM,WAAW,IAAI,CACzB;AAGF,OAAI,EAAC,MADkB,SAAS,KAAK,OAAO,CAAC,YAAY,GAAG,EAC9C,SAAA,kCAAwB,EAAE;AACtC,cAAU,KAAK,IAAI;AACnB;;AAEF,OAAI,CAAC,OACH,OAAM,GAAG,KAAK,EAAE,OAAO,MAAM,CAAC;AAEhC,WAAQ,KAAK,IAAI;;AAGnB,OAAK,MAAM,OAAO,QAAQ,iBAAiB;GACzC,MAAM,MAAM,KAAK,KAAK,IAAI;AAC1B,OAAI,CAAE,MAAM,WAAW,IAAI,CACzB;AAEF,OAAI,CAAC,OACH,OAAM,GAAG,KAAK,EAAE,OAAO,MAAM,CAAC;AAEhC,WAAQ,KAAK,IAAI;;AAGnB,OAAK,MAAM,OAAO,QAAQ,YAAY;GACpC,MAAM,MAAM,KAAK,KAAK,IAAI;AAC1B,OAAI,CAAE,MAAM,WAAW,IAAI,CACzB;AAEF,OAAI,QAAQ;AACV,aAAS,KAAK,GAAG,IAAI,MAAM,IAAI,sBAAsB;AACrD;;GAGF,MAAM,SAAS,GAAG,IAAI,4BADR,IAAI,MAAM,EAAC,aAAa,CAAC,QAAQ,SAAS,IAClB;AACtC,SAAM,OAAO,KAAK,OAAO;AACzB,YAAS,KAAK,GAAG,IAAI,MAAM,OAAO,MAAM,IAAI,SAAS,EAAE,GAAG;;AAG5D,OAAK,MAAM,OAAO,QAAQ,aAAa;GACrC,MAAM,MAAM,KAAK,KAAK,IAAI;AAC1B,OAAI,CAAE,MAAM,WAAW,IAAI,CACzB;AAEF,OAAI,CAAC,OACH,OAAM,GAAG,KAAK;IAAE,WAAW;IAAM,OAAO;IAAM,CAAC;AAEjD,WAAQ,KAAK,IAAI;;AAQnB,OAAK,MAAM,OAAO,QAAQ,eAAe;GACvC,MAAM,MAAM,KAAK,KAAK,IAAI;AAC1B,OAAI,CAAE,MAAM,WAAW,IAAI,CACzB;GAEF,MAAM,WAAW,MAAM,SAAS,KAAK,OAAO,CAAC,YAAY,GAAG;GAC5D,MAAM,WAAW,sBAAsB,SAAS;AAChD,OAAI,aAAa,SACf;GAEF,MAAM,OAAO,SAAS,MAAM,KAAK,MAAM,SAAS,MAAM,KAAK,cAAc,MAAM;AAC/E,OAAI,CAAC,OACH,KAAI,KACF,OAAM,GAAG,KAAK,EAAE,OAAO,MAAM,CAAC;OAE9B,OAAM,UAAU,KAAK,SAAS;AAGlC,WAAQ,KAAK,OAAO,MAAM,GAAG,IAAI,oBAAoB;;EAOvD,MAAM,cAAc,QAAQ,YAAY,KAAK,QAAQ;AACnD,OAAI,QAAQ,kBAAkB,IAAI,SAAS,gBAAgB,CACzD,QAAO,0CAA0C;AAEnD,OAAI,QAAQ,kBAAkB,IAAI,SAAS,gBAAgB,CACzD,QAAO,iFAAiF;AAE1F,UAAO,+CAA+C;IACtD;AAEF,MAAI,QAAQ,WAAW,KAAK,SAAS,WAAW,KAAK,YAAY,WAAW,EAC1E,QAAO,KAAK,KAAK;GAAE,QAAQ;GAAW,QAAQ;GAAoB,MAAM,EAAE,KAAK;GAAE,CAAC;EAGpF,MAAM,eAAe,qBAAqB,UAAU,QAAQ,aAAa;AAEzE,SAAO,KAAK,KAAK;GACf,QAAQ;GACR,MAAM;IACJ,OAAO;IACP,eAAe;IACf,iBAAiB;IACjB,WAAW;IACX,eAAe;IACf,cAAc;IACf;GACF,CAAC"}
@@ -1,5 +1,5 @@
1
- import { E as ZitadelError, S as resolveCwd, b as publicCliCommand, m as readRuntimeMetadata, t as BaseCommand } from "../oclif-Bm-FkF6z.mjs";
2
- import { a as followContainerLogs, f as binaryLogs, p as followBinaryLogs, t as containerLogs } from "../docker-D7BSD5C9.mjs";
1
+ import { E as ZitadelError, S as resolveCwd, b as publicCliCommand, m as readRuntimeMetadata, t as BaseCommand } from "../oclif-SjHLB_XK.mjs";
2
+ import { a as followContainerLogs, f as binaryLogs, p as followBinaryLogs, t as containerLogs } from "../docker-BbX0yOpg.mjs";
3
3
  import { Flags } from "@oclif/core";
4
4
  //#region src/commands/logs.ts
5
5
  var Logs = class Logs extends BaseCommand {
@@ -1,7 +1,7 @@
1
- import { t as BaseCommand } from "../oclif-Bm-FkF6z.mjs";
2
- import { o as readZitadelSecret } from "../project-Dk7V0xka.mjs";
3
- import { c as makeSyncers, n as summarizePlan, r as buildSyncPlan, t as renderPlan } from "../sync-CYuHVQT5.mjs";
4
- import { t as environmentSchema } from "../environment-BQF7LeCz.mjs";
1
+ import { t as BaseCommand } from "../oclif-SjHLB_XK.mjs";
2
+ import { o as readZitadelSecret } from "../project-DXc7q4wN.mjs";
3
+ import { i as buildSyncPlan, l as makeSyncers, n as renderPlan, r as summarizePlan, t as collectPlanWarnings } from "../sync-Bu_iLIbb.mjs";
4
+ import { t as environmentSchema } from "../environment-ABj1IWZt.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";
@@ -49,7 +49,10 @@ var Plan = class Plan extends BaseCommand {
49
49
  consola$1.success(`Plan: ${summary.creates} create${summary.creates === 1 ? "" : "s"}, ${summary.updates} update${summary.updates === 1 ? "" : "s"}, ${summary.revisions} new revision${summary.revisions === 1 ? "" : "s"}, ${summary.deletes} delete${summary.deletes === 1 ? "" : "s"}, ${plan.length - summary.total} unchanged`);
50
50
  return this.emit({
51
51
  status: "ok",
52
- data: summary,
52
+ data: {
53
+ ...summary,
54
+ warnings: collectPlanWarnings(plan)
55
+ },
53
56
  pretty: renderPlan(plan, isTTY)
54
57
  });
55
58
  }
@@ -1 +1 @@
1
- {"version":3,"file":"plan.mjs","names":[],"sources":["../../src/commands/plan.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, summarizePlan } from \"../lib/sync\";\nimport { readZitadelSecret } from \"../lib/project\";\n\n/**\n * `zitadel plan` — validate config and preview the sync diff without mutating.\n *\n * The read-only counterpart of `apply`: it builds and renders the diff instead\n * of running the sync loop. All validation (structural shape and env-ref\n * presence) happens in the sync engine, so an invalid file fails the same way\n * `apply` would.\n */\nexport default class Plan extends BaseCommand {\n static override description = \"Validate config without mutation and preview the sync diff.\";\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(Plan);\n await this.toMeta(flags);\n const { cwd, source, env, 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({\n client,\n projectId: secret.project_id,\n env,\n });\n\n consola.start(\"Building plan\");\n const plan = await buildSyncPlan(cwd, syncers, true);\n const summary = summarizePlan(plan);\n this.recordTelemetry({\n creates: summary.creates,\n updates: summary.updates,\n revisions: summary.revisions,\n deletes: summary.deletes,\n total: summary.total,\n });\n consola.success(\n `Plan: ${summary.creates} create${summary.creates === 1 ? \"\" : \"s\"}, ` +\n `${summary.updates} update${summary.updates === 1 ? \"\" : \"s\"}, ` +\n `${summary.revisions} new revision${summary.revisions === 1 ? \"\" : \"s\"}, ` +\n `${summary.deletes} delete${summary.deletes === 1 ? \"\" : \"s\"}, ` +\n `${plan.length - summary.total} unchanged`,\n );\n return this.emit({\n status: \"ok\",\n data: summary,\n pretty: renderPlan(plan, isTTY),\n });\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAkBA,IAAqB,OAArB,MAAqB,aAAa,YAAY;CAC5C,OAAgB,cAAc;CAC9B,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,KAAK;AACxC,QAAM,KAAK,OAAO,MAAM;EACxB,MAAM,EAAE,KAAK,QAAQ,KAAK,UAAU,KAAK;EAEzC,MAAM,SAAS,MAAM,kBAAkB,IAAI;AAC3C,YAAQ,KAAK,aAAa,OAAO,aAAa;AAC9C,YAAQ,KAAK,aAAa,SAAS;EAKnC,MAAM,UAAU,YAAY;GAC1B,QALa,oBAAoB;IACjC,SAAS;IACT,OAAO,OAAO;IACf,CAEO;GACN,WAAW,OAAO;GAClB;GACD,CAAC;AAEF,YAAQ,MAAM,gBAAgB;EAC9B,MAAM,OAAO,MAAM,cAAc,KAAK,SAAS,KAAK;EACpD,MAAM,UAAU,cAAc,KAAK;AACnC,OAAK,gBAAgB;GACnB,SAAS,QAAQ;GACjB,SAAS,QAAQ;GACjB,WAAW,QAAQ;GACnB,SAAS,QAAQ;GACjB,OAAO,QAAQ;GAChB,CAAC;AACF,YAAQ,QACN,SAAS,QAAQ,QAAQ,SAAS,QAAQ,YAAY,IAAI,KAAK,IAAI,IAC9D,QAAQ,QAAQ,SAAS,QAAQ,YAAY,IAAI,KAAK,IAAI,IAC1D,QAAQ,UAAU,eAAe,QAAQ,cAAc,IAAI,KAAK,IAAI,IACpE,QAAQ,QAAQ,SAAS,QAAQ,YAAY,IAAI,KAAK,IAAI,IAC1D,KAAK,SAAS,QAAQ,MAAM,YAClC;AACD,SAAO,KAAK,KAAK;GACf,QAAQ;GACR,MAAM;GACN,QAAQ,WAAW,MAAM,MAAM;GAChC,CAAC"}
1
+ {"version":3,"file":"plan.mjs","names":[],"sources":["../../src/commands/plan.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 {\n buildSyncPlan,\n collectPlanWarnings,\n makeSyncers,\n renderPlan,\n summarizePlan,\n} from \"../lib/sync\";\nimport { readZitadelSecret } from \"../lib/project\";\n\n/**\n * `zitadel plan` — validate config and preview the sync diff without mutating.\n *\n * The read-only counterpart of `apply`: it builds and renders the diff instead\n * of running the sync loop. All validation (structural shape and env-ref\n * presence) happens in the sync engine, so an invalid file fails the same way\n * `apply` would.\n */\nexport default class Plan extends BaseCommand {\n static override description = \"Validate config without mutation and preview the sync diff.\";\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(Plan);\n await this.toMeta(flags);\n const { cwd, source, env, 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({\n client,\n projectId: secret.project_id,\n env,\n });\n\n consola.start(\"Building plan\");\n const plan = await buildSyncPlan(cwd, syncers, true);\n const summary = summarizePlan(plan);\n this.recordTelemetry({\n creates: summary.creates,\n updates: summary.updates,\n revisions: summary.revisions,\n deletes: summary.deletes,\n total: summary.total,\n });\n consola.success(\n `Plan: ${summary.creates} create${summary.creates === 1 ? \"\" : \"s\"}, ` +\n `${summary.updates} update${summary.updates === 1 ? \"\" : \"s\"}, ` +\n `${summary.revisions} new revision${summary.revisions === 1 ? \"\" : \"s\"}, ` +\n `${summary.deletes} delete${summary.deletes === 1 ? \"\" : \"s\"}, ` +\n `${plan.length - summary.total} unchanged`,\n );\n return this.emit({\n status: \"ok\",\n data: { ...summary, warnings: collectPlanWarnings(plan) },\n pretty: renderPlan(plan, isTTY),\n });\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAwBA,IAAqB,OAArB,MAAqB,aAAa,YAAY;CAC5C,OAAgB,cAAc;CAC9B,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,KAAK;AACxC,QAAM,KAAK,OAAO,MAAM;EACxB,MAAM,EAAE,KAAK,QAAQ,KAAK,UAAU,KAAK;EAEzC,MAAM,SAAS,MAAM,kBAAkB,IAAI;AAC3C,YAAQ,KAAK,aAAa,OAAO,aAAa;AAC9C,YAAQ,KAAK,aAAa,SAAS;EAKnC,MAAM,UAAU,YAAY;GAC1B,QALa,oBAAoB;IACjC,SAAS;IACT,OAAO,OAAO;IACf,CAEO;GACN,WAAW,OAAO;GAClB;GACD,CAAC;AAEF,YAAQ,MAAM,gBAAgB;EAC9B,MAAM,OAAO,MAAM,cAAc,KAAK,SAAS,KAAK;EACpD,MAAM,UAAU,cAAc,KAAK;AACnC,OAAK,gBAAgB;GACnB,SAAS,QAAQ;GACjB,SAAS,QAAQ;GACjB,WAAW,QAAQ;GACnB,SAAS,QAAQ;GACjB,OAAO,QAAQ;GAChB,CAAC;AACF,YAAQ,QACN,SAAS,QAAQ,QAAQ,SAAS,QAAQ,YAAY,IAAI,KAAK,IAAI,IAC9D,QAAQ,QAAQ,SAAS,QAAQ,YAAY,IAAI,KAAK,IAAI,IAC1D,QAAQ,UAAU,eAAe,QAAQ,cAAc,IAAI,KAAK,IAAI,IACpE,QAAQ,QAAQ,SAAS,QAAQ,YAAY,IAAI,KAAK,IAAI,IAC1D,KAAK,SAAS,QAAQ,MAAM,YAClC;AACD,SAAO,KAAK,KAAK;GACf,QAAQ;GACR,MAAM;IAAE,GAAG;IAAS,UAAU,oBAAoB,KAAK;IAAE;GACzD,QAAQ,WAAW,MAAM,MAAM;GAChC,CAAC"}
@@ -1,5 +1,5 @@
1
- import { E as ZitadelError, S as resolveCwd, b as publicCliCommand, f as localContainerName, g as removeRuntimeMetadata, h as removeLocalData, m as readRuntimeMetadata, t as BaseCommand } from "../oclif-Bm-FkF6z.mjs";
2
- import { g as stopBinaryRuntime, u as stopAndRemoveContainer } from "../docker-D7BSD5C9.mjs";
1
+ import { E as ZitadelError, S as resolveCwd, b as publicCliCommand, f as localContainerName, g as removeRuntimeMetadata, h as removeLocalData, m as readRuntimeMetadata, t as BaseCommand } from "../oclif-SjHLB_XK.mjs";
2
+ import { g as stopBinaryRuntime, u as stopAndRemoveContainer } from "../docker-BbX0yOpg.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 { t as BaseCommand } from "../../oclif-Bm-FkF6z.mjs";
2
- import { o as readZitadelSecret } from "../../project-Dk7V0xka.mjs";
3
- import { t as environmentSchema } from "../../environment-BQF7LeCz.mjs";
1
+ import { t as BaseCommand } from "../../oclif-SjHLB_XK.mjs";
2
+ import { o as readZitadelSecret } from "../../project-DXc7q4wN.mjs";
3
+ import { t as environmentSchema } from "../../environment-ABj1IWZt.mjs";
4
4
  import { cancel, isCancel, select, spinner } from "@clack/prompts";
5
5
  import { Flags } from "@oclif/core";
6
6
  import { createZitadelClient } from "@zitadel/api/client";
@@ -1,17 +1,18 @@
1
- import { D as toZitadelError, E as ZitadelError, T as stableStringify, b as publicCliCommand, n as DEFAULT_SERVER, t as BaseCommand } from "../oclif-Bm-FkF6z.mjs";
2
- import { i as issuerFromPort, n as inspectScaffoldTarget, r as RENDERER_IDS, t as createOrca } from "../orca-CpXj_XsA.mjs";
3
- import { n as hasZitadelSecret, t as hasZitadelConfig } from "../project-Dk7V0xka.mjs";
4
- import { i as hashForState, l as FLOWS_DIR, o as writeBackResource, s as updateState } from "../sync-CYuHVQT5.mjs";
1
+ import { D as toZitadelError, E as ZitadelError, T as stableStringify, b as publicCliCommand, n as DEFAULT_SERVER, t as BaseCommand } from "../oclif-SjHLB_XK.mjs";
2
+ import { n as inspectScaffoldTarget, o as issuerFromPort, r as RENDERER_IDS, t as createOrca } from "../orca-_TmrJQMw.mjs";
3
+ import { n as hasZitadelSecret, t as hasZitadelConfig } from "../project-DXc7q4wN.mjs";
4
+ import { a as hashForState, c as updateState, s as writeBackResource, u as FLOWS_DIR } from "../sync-Bu_iLIbb.mjs";
5
5
  import { t as SCHEMAS_DIR } from "../user-schema-DDz5-lX5.mjs";
6
- import { t as listListeningPorts } from "../ports-CxBKS1ga.mjs";
6
+ import { n as verifyLoginAction, t as customizeAndPublishActions } from "../journey-guidance-E_3_UwGt.mjs";
7
+ import { t as listListeningPorts } from "../ports-XujDtA47.mjs";
7
8
  import { mkdir, readFile, rm, stat, writeFile } from "node:fs/promises";
8
9
  import { basename, dirname, join } from "node:path";
9
10
  import { cancel, confirm, intro, isCancel, outro, select, spinner, text } from "@clack/prompts";
10
11
  import { Flags } from "@oclif/core";
11
12
  import { createZitadelClient } from "@zitadel/api/client";
13
+ import { DEFAULT_FLOW_CONFIG_PATH, DEFAULT_FLOW_SCHEMA_URI, DEFAULT_SCHEMA_CONFIG_PATH, DEFAULT_SETUP_PRESET, SETUP_PRESETS, flowsReadmeContent, getDefaultHumanUserSchema, getDefaultLoginFlow, schemasReadmeContent } from "@zitadel/config/defaults";
12
14
  import { consola as consola$1 } from "consola";
13
15
  import { spawn } from "node:child_process";
14
- import { DEFAULT_FLOW_CONFIG_PATH, DEFAULT_FLOW_SCHEMA_URI, DEFAULT_SCHEMA_CONFIG_PATH, flowsReadmeContent, getDefaultHumanUserSchema, getDefaultLoginFlow, schemasReadmeContent } from "@zitadel/config/defaults";
15
16
  import { normalizeFlowBody, normalizeSchemaBody } from "@zitadel/config/normalize";
16
17
  import pc from "picocolors";
17
18
  //#region src/lib/setup-resources.ts
@@ -30,7 +31,8 @@ async function materializeSetupResources(opts) {
30
31
  await mkdir(join(opts.cwd, FLOWS_DIR), { recursive: true });
31
32
  await mkdir(join(opts.cwd, SCHEMAS_DIR), { recursive: true });
32
33
  const filesWritten = [];
33
- const { $id: _templateId, ...schemaBody } = getDefaultHumanUserSchema();
34
+ const preset = opts.preset ?? DEFAULT_SETUP_PRESET;
35
+ const { $id: _templateId, ...schemaBody } = getDefaultHumanUserSchema({ preset });
34
36
  if (await writeResourceFile(opts.cwd, DEFAULT_SCHEMA_CONFIG_PATH, schemaBody, opts.force)) filesWritten.push(join(opts.cwd, DEFAULT_SCHEMA_CONFIG_PATH));
35
37
  const schemaId = requiredString((await opts.client.createSchema(schemaBody, { project_id: opts.projectId })).id, "created schema id");
36
38
  let schemaHash = hashForState({ normalize: normalizeSchemaBody }, schemaBody);
@@ -44,7 +46,10 @@ async function materializeSetupResources(opts) {
44
46
  id: schemaId,
45
47
  hash: schemaHash
46
48
  });
47
- const flowBody = getDefaultLoginFlow({ userSchemaUrl: schemaId });
49
+ const flowBody = getDefaultLoginFlow({
50
+ userSchemaUrl: schemaId,
51
+ preset
52
+ });
48
53
  if (await writeResourceFile(opts.cwd, DEFAULT_FLOW_CONFIG_PATH, flowBody, opts.force)) filesWritten.push(join(opts.cwd, DEFAULT_FLOW_CONFIG_PATH));
49
54
  const flow = await opts.client.createFlowDefinition({
50
55
  project_id: opts.projectId,
@@ -238,17 +243,18 @@ async function installDependenciesForSetup(input) {
238
243
  }
239
244
  function outcome(input) {
240
245
  const planCommand = publicCliCommand("plan", input.cliVersion);
241
- const applyCommand = publicCliCommand("apply", input.cliVersion);
242
- const afterInstall = [
243
- `Start your project: ${input.devCommand} (then open ${input.issuer})`,
244
- "Verify auth in the browser: register a user, log out, log in again with the same user, and confirm /profile shows Signed in.",
245
- "Customize auth: edit the user schema (.zitadel/schemas/) and login flow (.zitadel/flows/) — each folder has a README explaining what you can change.",
246
- `Preview and publish config changes: ${planCommand} then ${applyCommand}`
246
+ const statusCommand = publicCliCommand("status", input.cliVersion);
247
+ const verifyActions = [
248
+ ...input.includeInstallCommand ? [`Install dependencies: ${input.install.command}`] : [],
249
+ `Start your project: ${input.devCommand} (then open ${input.issuer}/login)`,
250
+ verifyLoginAction()
247
251
  ];
252
+ const breadcrumb = `Once login works: ${statusCommand} shows your next steps; customizing is covered in your README's Zitadel section.`;
248
253
  return {
249
254
  install: input.install,
250
255
  devCommand: input.devCommand,
251
- nextActions: input.includeInstallCommand ? [`Install dependencies: ${input.install.command}`, ...afterInstall] : afterInstall,
256
+ boxActions: [...verifyActions, breadcrumb],
257
+ nextActions: [...verifyActions, ...customizeAndPublishActions(input.cliVersion)],
252
258
  nextCommands: input.includeInstallCommand ? [
253
259
  input.install.command,
254
260
  input.devCommand,
@@ -468,6 +474,36 @@ async function discoverLocalOidc() {
468
474
  }
469
475
  }
470
476
  //#endregion
477
+ //#region src/commands/setup/prompts/sign-in-preset.ts
478
+ /**
479
+ * "How should users sign in?" — picks the schema+flow preset the scaffold
480
+ * starts from (#448: app-type/preset selection before any `.zitadel/` file
481
+ * is written). `--preset` is authoritative; non-interactive runs keep the
482
+ * password-first default seeded by the command.
483
+ */
484
+ var SignInPresetPrompt = class {
485
+ async ask(answers, ctx) {
486
+ if (ctx.presetFromFlag) return answers;
487
+ const labels = {
488
+ "password-first": "Password first — email + password, passkey optional during registration",
489
+ "passkey-first": "Passkey first — one-tap passkey up front, email + password fallback"
490
+ };
491
+ const value = await select({
492
+ message: "How should users sign in?",
493
+ initialValue: answers.preset,
494
+ options: SETUP_PRESETS.map((preset) => ({
495
+ value: preset,
496
+ label: labels[preset]
497
+ }))
498
+ });
499
+ bail(value);
500
+ return {
501
+ ...answers,
502
+ preset: value
503
+ };
504
+ }
505
+ };
506
+ //#endregion
471
507
  //#region src/commands/setup/prompts/pick-framework.ts
472
508
  /**
473
509
  * "Choose a framework to scaffold" — the only prompt outside the main wizard.
@@ -506,7 +542,8 @@ var PickFrameworkPrompt = class {
506
542
  const SETUP_PROMPTS = [
507
543
  new FrameworkConfirmPrompt(),
508
544
  new ServerPrompt(),
509
- new DevPortPrompt()
545
+ new DevPortPrompt(),
546
+ new SignInPresetPrompt()
510
547
  ];
511
548
  //#endregion
512
549
  //#region src/commands/setup/summary.ts
@@ -633,14 +670,18 @@ var Setup = class Setup extends BaseCommand {
633
670
  options: [...RENDERER_IDS]
634
671
  }),
635
672
  "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." }),
636
- "skip-install": Flags.boolean({ description: "Do not install dependencies after setup updates package.json." })
673
+ "skip-install": Flags.boolean({ description: "Do not install dependencies after setup updates package.json." }),
674
+ preset: Flags.string({
675
+ description: "Sign-in preset for the scaffolded schema and login flow (default: password-first).",
676
+ options: [...SETUP_PRESETS]
677
+ })
637
678
  };
638
679
  async run() {
639
680
  const { flags } = await this.parse(Setup);
640
681
  try {
641
682
  await this.toMeta(flags);
642
683
  } catch (error) {
643
- throw localSetupHint(error, flags.framework, this.config.version);
684
+ throw localSetupHint(error, retryOptionsFromFlags(flags), this.config.version);
644
685
  }
645
686
  const { cwd, nonInteractive, dryRun, force } = this.meta;
646
687
  if (await hasZitadelConfig(cwd)) return this.emit({
@@ -671,6 +712,7 @@ var Setup = class Setup extends BaseCommand {
671
712
  scaffolded_skeleton: scaffoldedFramework,
672
713
  skip_install: Boolean(flags["skip-install"]),
673
714
  dev_port_explicit: flags["dev-port"] !== void 0,
715
+ preset: flags.preset ?? DEFAULT_SETUP_PRESET,
674
716
  step: "framework_resolved"
675
717
  });
676
718
  if (flags["dev-port"] !== void 0) {
@@ -684,18 +726,21 @@ var Setup = class Setup extends BaseCommand {
684
726
  }
685
727
  let answers = {
686
728
  server: this.meta.source,
687
- devPort: framework.devPort
729
+ devPort: framework.devPort,
730
+ preset: flags.preset ?? DEFAULT_SETUP_PRESET
688
731
  };
689
732
  if (!nonInteractive && !dryRun) {
690
733
  intro("Zitadel setup");
691
734
  const promptCtx = {
692
735
  framework,
693
736
  serverFlag: this.meta.serverFlag,
694
- devPortFromFlag: flags["dev-port"] !== void 0
737
+ devPortFromFlag: flags["dev-port"] !== void 0,
738
+ presetFromFlag: flags.preset !== void 0
695
739
  };
696
740
  for (const prompt of SETUP_PROMPTS) answers = await prompt.ask(answers, promptCtx);
697
741
  outro("Configuration captured");
698
742
  }
743
+ this.recordTelemetry({ preset: answers.preset });
699
744
  const issuer = issuerFromPort(answers.devPort);
700
745
  framework = {
701
746
  ...framework,
@@ -704,7 +749,13 @@ var Setup = class Setup extends BaseCommand {
704
749
  };
705
750
  consola$1.start(`Creating project on ${answers.server}${dryRun ? " (dry run)" : ""}`);
706
751
  const unauthClient = createZitadelClient({ baseUrl: answers.server });
707
- const project = dryRun ? dryRunProject(issuer) : await createProjectWithLocalHint(unauthClient, answers.server, this.meta.cliVersion, issuer, framework.id);
752
+ const projectName = defaultProjectName(cwd, framework.id);
753
+ const project = dryRun ? dryRunProject(issuer) : await createProjectWithLocalHint(unauthClient, answers.server, this.meta.cliVersion, projectName, issuer, {
754
+ ...retryOptionsFromFlags(flags),
755
+ framework: framework.id,
756
+ preset: answers.preset,
757
+ devPort: answers.devPort
758
+ });
708
759
  consola$1.success(`Created project ${project.id}`);
709
760
  this.recordTelemetry({ step: "project_created" });
710
761
  const ctx = {
@@ -714,7 +765,8 @@ var Setup = class Setup extends BaseCommand {
714
765
  issuer,
715
766
  server: answers.server,
716
767
  cliVersion: this.meta.cliVersion,
717
- scaffoldedFramework
768
+ scaffoldedFramework,
769
+ preset: answers.preset
718
770
  };
719
771
  consola$1.start(`Patching project files${dryRun ? " (dry run)" : ""}`);
720
772
  const result = await orca.patcherFor(framework.id).patch(ctx, {
@@ -736,7 +788,8 @@ var Setup = class Setup extends BaseCommand {
736
788
  token: project.projectSecret
737
789
  }),
738
790
  projectId: project.id,
739
- force
791
+ force,
792
+ preset: answers.preset
740
793
  });
741
794
  } catch (error) {
742
795
  await rm(join(cwd, "zitadel.json"), { force: true });
@@ -788,7 +841,7 @@ var Setup = class Setup extends BaseCommand {
788
841
  message: [
789
842
  renderSummary(sections),
790
843
  "",
791
- installOutcome.nextActions.join("\n")
844
+ installOutcome.boxActions.join("\n")
792
845
  ].join("\n"),
793
846
  style: {
794
847
  padding: 1,
@@ -847,17 +900,49 @@ function dryRunProject(issuer) {
847
900
  createdAt: "2026-04-21T14:03:11.000Z"
848
901
  };
849
902
  }
850
- async function createProjectWithLocalHint(client, server, cliVersion, issuer, framework) {
903
+ /**
904
+ * Reconstructs the flag list of the current invocation for retry guidance,
905
+ * ending in `--server local`. Flags whose resolved value equals the default
906
+ * are omitted — the retry reproduces the same outcome without them.
907
+ */
908
+ function setupRetryFlags(opts) {
909
+ const parts = [];
910
+ if (opts.framework) parts.push(`--framework ${opts.framework}`);
911
+ if (opts.preset && opts.preset !== DEFAULT_SETUP_PRESET) parts.push(`--preset ${opts.preset}`);
912
+ if (opts.renderer && opts.renderer !== "react") parts.push(`--renderer ${opts.renderer}`);
913
+ if (opts.devPort !== void 0) parts.push(`--dev-port ${opts.devPort}`);
914
+ if (opts.nonInteractive) parts.push("--non-interactive");
915
+ parts.push("--server local");
916
+ return parts.join(" ");
917
+ }
918
+ /**
919
+ * Retry options straight from parsed flags, for failures before the wizard
920
+ * resolves answers. `--non-interactive` is echoed only when explicitly passed
921
+ * — TTY/JSON-inferred non-interactivity re-infers itself on the retry.
922
+ */
923
+ function retryOptionsFromFlags(flags) {
924
+ return {
925
+ framework: flags.framework,
926
+ preset: flags.preset,
927
+ renderer: flags.renderer,
928
+ devPort: flags["dev-port"],
929
+ nonInteractive: Boolean(flags["non-interactive"])
930
+ };
931
+ }
932
+ async function createProjectWithLocalHint(client, server, cliVersion, projectName, issuer, retry) {
851
933
  try {
852
- return await client.createProject({
934
+ const payload = {
935
+ name: projectName,
853
936
  previewOrigins: [issuer],
854
937
  seedDefaults: false
855
- });
938
+ };
939
+ return await client.createProject(payload);
856
940
  } catch (error) {
857
941
  const normalized = toZitadelError(error);
942
+ const retryFlags = setupRetryFlags(retry);
858
943
  throw new ZitadelError(normalized.code, normalized.message, {
859
- 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.`,
860
- nextCommands: [publicCliCommand("start", cliVersion), publicCliCommand(`setup --framework ${framework} --server local`, cliVersion)],
944
+ hint: `${normalized.hint ? `${normalized.hint} ` : ""}If you meant to use a local Zitadel server, start it first and retry setup with ${retryFlags}.`,
945
+ nextCommands: [publicCliCommand("start", cliVersion), publicCliCommand(`setup ${retryFlags}`, cliVersion)],
861
946
  details: {
862
947
  server,
863
948
  original: normalized.details
@@ -865,10 +950,14 @@ async function createProjectWithLocalHint(client, server, cliVersion, issuer, fr
865
950
  });
866
951
  }
867
952
  }
868
- function localSetupHint(error, framework, cliVersion) {
953
+ function defaultProjectName(cwd, framework) {
954
+ const fromDirectory = basename(cwd).trim();
955
+ return fromDirectory.length > 0 ? fromDirectory : `zitadel-${framework}-app`;
956
+ }
957
+ function localSetupHint(error, retry, cliVersion) {
869
958
  const normalized = toZitadelError(error);
870
959
  if (normalized.code !== "E_LOCAL_SERVER_NOT_RUNNING") return error;
871
- const setupCommand = framework ? `setup --framework ${framework} --server local` : "setup --server local";
960
+ const setupCommand = `setup ${setupRetryFlags(retry)}`;
872
961
  return new ZitadelError(normalized.code, normalized.message, {
873
962
  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.`,
874
963
  nextCommands: [publicCliCommand("start", cliVersion), publicCliCommand(setupCommand, cliVersion)],
@@ -907,7 +996,7 @@ function pickWrittenFile(written, suffix) {
907
996
  * something happened.
908
997
  */
909
998
  function describeWrittenFile(relPath, dryRun) {
910
- if (relPath === ".zitadel" || relPath === ".zitadel/flows" || relPath === ".zitadel/schemas") return null;
999
+ if (relPath === ".zitadel" || relPath === ".zitadel/flows" || relPath === ".zitadel/schemas" || relPath === ".zitadel/meta") return null;
911
1000
  const verb = dryRun ? "Would write" : "Wrote";
912
1001
  const sentence = SENTENCE_BY_PATH[relPath];
913
1002
  if (sentence) return `${verb} ${sentence.subject} (${path(relPath)})`;
@@ -930,7 +1019,12 @@ const SENTENCE_BY_PATH = {
930
1019
  ".zitadel/flows/README.md": { subject: "the flows folder README" },
931
1020
  ".zitadel/schemas/default-human-user.json": { subject: "the editable default human user schema" },
932
1021
  ".zitadel/schemas/README.md": { subject: "the schemas folder README" },
933
- "app/page.tsx": { subject: "the auth home page" },
1022
+ ".zitadel/meta/flow-definition.json": { subject: "the flow dialect spec (editor $schema)" },
1023
+ ".zitadel/meta/user-schema.json": { subject: "the user-schema dialect spec" },
1024
+ ".zitadel/meta/user-property.json": { subject: "the user-property dialect spec" },
1025
+ "AGENTS.md": { subject: "the agent guidance (golden journey + config dialect)" },
1026
+ "README.md": { subject: "the README's Zitadel section" },
1027
+ "app/page.tsx": { subject: "the home page redirect" },
934
1028
  "app/login/page.tsx": { subject: "the login page" },
935
1029
  "app/register/page.tsx": { subject: "the registration page" },
936
1030
  "app/profile/page.tsx": { subject: "the profile page" },
@@ -959,7 +1053,7 @@ function buildSummary(opts) {
959
1053
  secondary: path(fileNameOf(packageJsonHit))
960
1054
  });
961
1055
  for (const [label, suffix] of [
962
- ["Home page", "app/page.tsx"],
1056
+ ["Home redirect", "app/page.tsx"],
963
1057
  ["Login page", "app/login/page.tsx"],
964
1058
  ["Register page", "app/register/page.tsx"],
965
1059
  ["Profile page", "app/profile/page.tsx"],