@zitadel/cli 0.1.0-alpha.8 → 1.0.0-alpha.20

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 (66) hide show
  1. package/README.md +250 -62
  2. package/SKILLS.md +227 -38
  3. package/dist/branding-cygSBPkV.mjs +79 -0
  4. package/dist/branding-cygSBPkV.mjs.map +1 -0
  5. package/dist/claim-state-DlfD6Res.mjs +66 -0
  6. package/dist/claim-state-DlfD6Res.mjs.map +1 -0
  7. package/dist/commands/apply.mjs +27 -8
  8. package/dist/commands/apply.mjs.map +1 -1
  9. package/dist/commands/branding/eject.mjs +104 -0
  10. package/dist/commands/branding/eject.mjs.map +1 -0
  11. package/dist/commands/claim.mjs +402 -0
  12. package/dist/commands/claim.mjs.map +1 -0
  13. package/dist/commands/doctor.mjs +687 -55
  14. package/dist/commands/doctor.mjs.map +1 -1
  15. package/dist/commands/eject.mjs +18 -6
  16. package/dist/commands/eject.mjs.map +1 -1
  17. package/dist/commands/logs.mjs +2 -2
  18. package/dist/commands/plan.mjs +19 -6
  19. package/dist/commands/plan.mjs.map +1 -1
  20. package/dist/commands/reset.mjs +12 -4
  21. package/dist/commands/reset.mjs.map +1 -1
  22. package/dist/commands/schemas/list.mjs +146 -0
  23. package/dist/commands/schemas/list.mjs.map +1 -0
  24. package/dist/commands/setup.mjs +642 -287
  25. package/dist/commands/setup.mjs.map +1 -1
  26. package/dist/commands/start.mjs +73 -6
  27. package/dist/commands/start.mjs.map +1 -1
  28. package/dist/commands/status.mjs +52 -13
  29. package/dist/commands/status.mjs.map +1 -1
  30. package/dist/commands/stop.mjs +59 -6
  31. package/dist/commands/stop.mjs.map +1 -1
  32. package/dist/designs-Ckz18Dpo.mjs +38 -0
  33. package/dist/designs-Ckz18Dpo.mjs.map +1 -0
  34. package/dist/{docker-BA78SdC2.mjs → docker-DcRGTOJa.mjs} +61 -11
  35. package/dist/docker-DcRGTOJa.mjs.map +1 -0
  36. package/dist/environment-rjRVkJjW.mjs +17 -0
  37. package/dist/environment-rjRVkJjW.mjs.map +1 -0
  38. package/dist/journey-guidance-BGrOX_gT.mjs +40 -0
  39. package/dist/journey-guidance-BGrOX_gT.mjs.map +1 -0
  40. package/dist/oclif-CanO3zdt.mjs +1769 -0
  41. package/dist/oclif-CanO3zdt.mjs.map +1 -0
  42. package/dist/{orca-CfKDQRop.mjs → orca-BzQIQqzt.mjs} +1663 -303
  43. package/dist/orca-BzQIQqzt.mjs.map +1 -0
  44. package/dist/package-manager-DFdLcDx1.mjs +194 -0
  45. package/dist/package-manager-DFdLcDx1.mjs.map +1 -0
  46. package/dist/ports-BM20XIZb.mjs +116 -0
  47. package/dist/ports-BM20XIZb.mjs.map +1 -0
  48. package/dist/processes-DKHPkU8O.mjs +120 -0
  49. package/dist/processes-DKHPkU8O.mjs.map +1 -0
  50. package/dist/{project-CKAHtHML.mjs → project-CHhot85s.mjs} +57 -4
  51. package/dist/project-CHhot85s.mjs.map +1 -0
  52. package/dist/sync-BBlACKzT.mjs +1633 -0
  53. package/dist/sync-BBlACKzT.mjs.map +1 -0
  54. package/dist/user-schema-DTuOsdKE.mjs +91 -0
  55. package/dist/user-schema-DTuOsdKE.mjs.map +1 -0
  56. package/oclif.manifest.json +412 -5
  57. package/package.json +10 -5
  58. package/dist/docker-BA78SdC2.mjs.map +0 -1
  59. package/dist/docker-guidance-BvfpmsDj.mjs +0 -21
  60. package/dist/docker-guidance-BvfpmsDj.mjs.map +0 -1
  61. package/dist/oclif-VkCTGIEk.mjs +0 -818
  62. package/dist/oclif-VkCTGIEk.mjs.map +0 -1
  63. package/dist/orca-CfKDQRop.mjs.map +0 -1
  64. package/dist/project-CKAHtHML.mjs.map +0 -1
  65. package/dist/sync-B5lqgQO3.mjs +0 -733
  66. package/dist/sync-B5lqgQO3.mjs.map +0 -1
@@ -1,818 +0,0 @@
1
- import { Command, Flags } from "@oclif/core";
2
- import consola from "consola";
3
- import { ApiError } from "@zitadel/api/runtime/fetch";
4
- import { stringify } from "safe-stable-stringify";
5
- import { dirname, join, resolve } from "node:path";
6
- import { access, mkdir, readFile, rm, stat, writeFile } from "node:fs/promises";
7
- import { createHash } from "node:crypto";
8
- import { constants } from "node:fs";
9
- import { createServer } from "node:net";
10
- //#region src/lib/errors.ts
11
- /**
12
- * Maps each {@link ZitadelErrorCode} to the process exit code the CLI
13
- * returns. The table is the single source of truth for exit semantics so
14
- * scripts and CI can branch on stable, documented numbers.
15
- */
16
- const EXIT_CODES = {
17
- E_ALREADY_INIT: 0,
18
- E_FRAMEWORK_NOT_DETECTED: 3,
19
- E_UNSUPPORTED_PROJECT_SHAPE: 3,
20
- E_NETWORK: 4,
21
- E_AUTH: 1,
22
- E_CONFLICT: 5,
23
- E_LOCAL_SERVER_NOT_RUNNING: 4,
24
- E_VALIDATION: 3,
25
- E_NOT_IMPLEMENTED: 2
26
- };
27
- /**
28
- * The CLI's single error type. Carries a {@link ZitadelErrorCode} so the
29
- * top-level handler can derive an exit code and structured output without
30
- * pattern-matching on messages. Throwing this anywhere guarantees the user
31
- * gets a categorised, hint-bearing failure instead of a raw stack trace.
32
- */
33
- var ZitadelError = class extends Error {
34
- code;
35
- hint;
36
- nextCommands;
37
- details;
38
- constructor(code, message, opts = {}) {
39
- super(message);
40
- this.name = "ZitadelError";
41
- this.code = code;
42
- this.hint = opts.hint;
43
- this.nextCommands = opts.nextCommands;
44
- this.details = opts.details;
45
- }
46
- get exitCode() {
47
- return EXIT_CODES[this.code] ?? 1;
48
- }
49
- };
50
- /**
51
- * Normalises any thrown value into a {@link ZitadelError}. Inspection is
52
- * ordered most-specific-first (already-normalised, then errno/filesystem,
53
- * network, Zod-like, generic `Error`, then a catch-all) so the most
54
- * actionable category and hint win. This is the boundary that lets the rest
55
- * of the CLI `throw` plain errors yet still produce consistent, categorised
56
- * output. The original error shape is preserved under `details` for
57
- * debugging without leaking it into the user-facing message.
58
- */
59
- function toZitadelError(error) {
60
- if (error instanceof ZitadelError) return error;
61
- if (error instanceof ApiError) return new ZitadelError(error.status === 401 || error.status === 403 ? "E_AUTH" : error.status >= 500 ? "E_NETWORK" : "E_VALIDATION", error.message, { details: {
62
- status: error.status,
63
- url: error.url,
64
- body: error.body
65
- } });
66
- if (isErrnoException(error)) {
67
- const details = { original: pickErrorShape(error) };
68
- if (error.code === "EACCES" || error.code === "EPERM") return new ZitadelError("E_AUTH", `Permission denied: ${error.message}`, {
69
- hint: "Check file permissions or run with the right user.",
70
- details
71
- });
72
- if (error.code === "EEXIST") return new ZitadelError("E_CONFLICT", error.message, {
73
- hint: "A file already exists. Use --force to overwrite or remove it first.",
74
- details
75
- });
76
- if (error.code === "ENOENT") return new ZitadelError("E_VALIDATION", error.message, {
77
- hint: "A required file or directory is missing.",
78
- details
79
- });
80
- }
81
- if (isNetworkError(error)) return new ZitadelError("E_NETWORK", errorMessage(error), {
82
- hint: "Check your connection, ZITADEL_API_BASE, or the configured server URL.",
83
- details: { original: pickErrorShape(error) }
84
- });
85
- if (isZodLikeError(error)) return new ZitadelError("E_VALIDATION", errorMessage(error), { details: { issues: error.issues } });
86
- if (error instanceof Error) return new ZitadelError("E_VALIDATION", error.message, { details: { original: pickErrorShape(error) } });
87
- return new ZitadelError("E_VALIDATION", "Unknown error", { details: error });
88
- }
89
- function isErrnoException(error) {
90
- return error instanceof Error && typeof error.code === "string";
91
- }
92
- function isNetworkError(error) {
93
- if (!(error instanceof Error)) return false;
94
- if (error.name === "TypeError" && /fetch failed|network|ECONNREFUSED|ENOTFOUND/i.test(error.message)) return true;
95
- const cause = error.cause;
96
- if (cause && typeof cause === "object" && "code" in cause) {
97
- const code = String(cause.code);
98
- return /^(ECONNREFUSED|ECONNRESET|ENOTFOUND|ETIMEDOUT|EAI_AGAIN|UND_ERR)/i.test(code);
99
- }
100
- return false;
101
- }
102
- function isZodLikeError(error) {
103
- return typeof error === "object" && error !== null && "issues" in error && Array.isArray(error.issues);
104
- }
105
- function errorMessage(error) {
106
- if (error instanceof Error) return error.message;
107
- if (typeof error === "string") return error;
108
- return String(error);
109
- }
110
- function pickErrorShape(error) {
111
- return {
112
- name: error.name,
113
- message: error.message,
114
- code: error.code
115
- };
116
- }
117
- //#endregion
118
- //#region src/lib/json.ts
119
- /**
120
- * Serialise a value to pretty-printed JSON with object keys sorted at every
121
- * depth. Determinism is the point: managed files written by the CLI must be
122
- * byte-stable across runs so diffs stay clean and content hashes don't churn
123
- * when only key ordering would otherwise differ. Delegates the deterministic
124
- * sort to `safe-stable-stringify`, matching `JSON.stringify(value, null, 2)`
125
- * formatting. The `?? "null"` only applies to `undefined`/function inputs,
126
- * which the CLI never serialises.
127
- */
128
- function stableStringify(value) {
129
- return stringify(value, null, 2) ?? "null";
130
- }
131
- /**
132
- * Parse `contents` as JSON and assert the root is a plain object (not an
133
- * array or scalar). The CLI's config and secret files are always objects, so
134
- * this guards callers from the `JSON.parse` return type of `any` and produces
135
- * a `path`-qualified error message pointing at the offending file.
136
- */
137
- function parseJsonObject(contents, path) {
138
- const value = JSON.parse(contents);
139
- if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error(`${path} must contain a JSON object`);
140
- return value;
141
- }
142
- /**
143
- * Narrows an unknown value to a plain (non-array, non-null) object. Shared by
144
- * the commands and the file-writer that walk parsed JSON, so the predicate
145
- * isn't reimplemented per call site.
146
- */
147
- function isObject(value) {
148
- return typeof value === "object" && value !== null && !Array.isArray(value);
149
- }
150
- //#endregion
151
- //#region src/lib/paths.ts
152
- /**
153
- * Resolve the working directory the CLI should operate against, defaulting to
154
- * the process CWD when no `--cwd` override is given. Always returns an
155
- * absolute path so downstream `join`/`readFile` calls are unaffected by later
156
- * `process.chdir` or relative-path ambiguity.
157
- */
158
- function resolveCwd(cwd) {
159
- return resolve(cwd ?? process.cwd());
160
- }
161
- /**
162
- * Sentinel comment stamped at the top of every file the CLI generates and
163
- * owns. Commands like `doctor` and `eject` look for this marker to decide
164
- * whether a file is safe to touch; the trailing `v1` lets the format evolve
165
- * without mistaking newer managed files for hand-edited ones.
166
- */
167
- const MANAGED_MARKER = "// zitadel-cli: managed-file v1";
168
- //#endregion
169
- //#region src/lib/public-cli.ts
170
- const CLI_PACKAGE_NAME = "@zitadel/cli";
171
- function npmDistTagForCliVersion(cliVersion) {
172
- return cliVersion.trim().replace(/^v/, "").match(/^\d+\.\d+\.\d+-([0-9A-Za-z][0-9A-Za-z-]*)/)?.[1] ?? "latest";
173
- }
174
- function npmSelectorForCliVersion(cliVersion) {
175
- const normalized = cliVersion.trim().replace(/^v/, "");
176
- if (/^\d+\.\d+\.\d+-alpha\.\d+$/.test(normalized)) return normalized;
177
- return npmDistTagForCliVersion(normalized);
178
- }
179
- function publicCliCommand(args, cliVersion) {
180
- const prefix = `npx ${CLI_PACKAGE_NAME}@${npmSelectorForCliVersion(cliVersion)}`;
181
- return args.length > 0 ? `${prefix} ${args}` : prefix;
182
- }
183
- function normalizePublicCliCommand(command, cliVersion) {
184
- if (command === "zitadel") return publicCliCommand("", cliVersion);
185
- if (command.startsWith("zitadel ")) return publicCliCommand(command.slice(8), cliVersion);
186
- return command;
187
- }
188
- function normalizePublicCliCommands(commands, cliVersion) {
189
- return commands?.map((command) => normalizePublicCliCommand(command, cliVersion));
190
- }
191
- //#endregion
192
- //#region src/lib/local-server/runtime.ts
193
- const LOCAL_SERVER_IMAGE_NAME = "ghcr.io/zitadel/nextgen";
194
- const DEFAULT_LOCAL_SERVER_IMAGE = `${LOCAL_SERVER_IMAGE_NAME}:latest`;
195
- const DEFAULT_LOCAL_SERVER_PORT = 8080;
196
- const DEFAULT_LOCAL_SERVER_URL = "http://localhost:8080";
197
- const LOCAL_RUNTIME_DIR = ".zitadel/local";
198
- const LOCAL_DATA_DIR = ".zitadel/local/nextgen-data";
199
- const LOCAL_RUNTIME_FILE = ".zitadel/local/runtime.json";
200
- const LOCAL_SERVER_LOG_FILE = ".zitadel/local/server.log";
201
- const LOCAL_CONTAINER_PASSWD_FILE = ".zitadel/local/container-passwd";
202
- const LOCAL_CONTAINER_GROUP_FILE = ".zitadel/local/container-group";
203
- const CONTAINER_DATA_DIR = "/var/lib/zitadel/nextgen-data";
204
- const CONTAINER_HTTP_PORT = 8080;
205
- function localRuntimePaths(cwd) {
206
- return {
207
- runtimeDir: join(cwd, LOCAL_RUNTIME_DIR),
208
- dataDir: join(cwd, LOCAL_DATA_DIR),
209
- runtimeFile: join(cwd, LOCAL_RUNTIME_FILE),
210
- logFile: join(cwd, LOCAL_SERVER_LOG_FILE),
211
- containerPasswdFile: join(cwd, LOCAL_CONTAINER_PASSWD_FILE),
212
- containerGroupFile: join(cwd, LOCAL_CONTAINER_GROUP_FILE)
213
- };
214
- }
215
- function localContainerName(cwd) {
216
- return `zitadel-server-${createHash("sha256").update(resolve(cwd)).digest("hex").slice(0, 12)}`;
217
- }
218
- function localServerUrl(port) {
219
- return `http://localhost:${port}`;
220
- }
221
- function defaultLocalServerImageForCliVersion(cliVersion) {
222
- const normalized = cliVersion.trim().replace(/^v/, "");
223
- if (/^\d+\.\d+\.\d+-alpha\.\d+$/.test(normalized)) return `${LOCAL_SERVER_IMAGE_NAME}:${normalized}`;
224
- return DEFAULT_LOCAL_SERVER_IMAGE;
225
- }
226
- async function ensureLocalState(cwd) {
227
- const paths = localRuntimePaths(cwd);
228
- await mkdir(paths.dataDir, {
229
- recursive: true,
230
- mode: 448
231
- });
232
- await appendGitignoreEntry(cwd, `${LOCAL_RUNTIME_DIR}/`);
233
- return paths;
234
- }
235
- async function assertLocalStateWritable(cwd) {
236
- const paths = localRuntimePaths(cwd);
237
- const checkedPath = await nearestExistingDirectory(paths.dataDir);
238
- await access(checkedPath, constants.W_OK);
239
- return {
240
- targetPath: paths.dataDir,
241
- checkedPath
242
- };
243
- }
244
- async function ensureContainerIdentity(cwd, user) {
245
- if (user.uid === void 0 || user.uid <= 0) return;
246
- const gid = user.gid ?? user.uid;
247
- const paths = localRuntimePaths(cwd);
248
- await mkdir(paths.runtimeDir, {
249
- recursive: true,
250
- mode: 448
251
- });
252
- await writeFile(paths.containerPasswdFile, [
253
- "root:x:0:0:root:/root:/bin/sh",
254
- "nonroot:x:65532:65532:nonroot:/nonexistent:/usr/sbin/nologin",
255
- `zitadel-local:x:${String(user.uid)}:${String(gid)}:Zitadel local user:/tmp:/usr/sbin/nologin`,
256
- ""
257
- ].join("\n"), { mode: 420 });
258
- await writeFile(paths.containerGroupFile, [
259
- "root:x:0:",
260
- "nonroot:x:65532:",
261
- `zitadel-local:x:${String(gid)}:`,
262
- ""
263
- ].join("\n"), { mode: 420 });
264
- return {
265
- uid: user.uid,
266
- gid,
267
- passwdFile: paths.containerPasswdFile,
268
- groupFile: paths.containerGroupFile
269
- };
270
- }
271
- async function readRuntimeMetadata(cwd) {
272
- const paths = localRuntimePaths(cwd);
273
- let raw;
274
- try {
275
- raw = await readFile(paths.runtimeFile, "utf8");
276
- } catch (error) {
277
- if (isErrno(error, "ENOENT")) return;
278
- throw error;
279
- }
280
- return normalizeRuntimeMetadata(parseJsonObject(raw, LOCAL_RUNTIME_FILE));
281
- }
282
- async function writeRuntimeMetadata(cwd, metadata) {
283
- const paths = localRuntimePaths(cwd);
284
- await mkdir(paths.runtimeDir, {
285
- recursive: true,
286
- mode: 448
287
- });
288
- await writeFile(paths.runtimeFile, `${JSON.stringify(metadata, null, 2)}\n`, { mode: 384 });
289
- }
290
- async function removeRuntimeMetadata(cwd) {
291
- await rm(localRuntimePaths(cwd).runtimeFile, { force: true });
292
- }
293
- async function removeLocalData(cwd) {
294
- await rm(localRuntimePaths(cwd).dataDir, {
295
- recursive: true,
296
- force: true
297
- });
298
- }
299
- async function checkLocalServerHealth(serverUrl, timeoutMs = 1500) {
300
- try {
301
- const healthUrl = new URL("/healthz", serverUrl);
302
- return (await fetch(healthUrl, { signal: AbortSignal.timeout(timeoutMs) })).ok;
303
- } catch {
304
- return false;
305
- }
306
- }
307
- async function isPortAvailable(port) {
308
- return new Promise((resolvePort) => {
309
- const server = createServer();
310
- server.once("error", () => resolvePort(false));
311
- server.once("listening", () => {
312
- server.close(() => resolvePort(true));
313
- });
314
- server.listen(port, "127.0.0.1");
315
- });
316
- }
317
- async function resolveLocalServer(cwd) {
318
- const runtime = await readRuntimeMetadata(cwd);
319
- if (runtime) {
320
- if (await checkLocalServerHealth(runtime.server_url)) return runtime.server_url;
321
- throw localServerNotRunning(runtime.server_url);
322
- }
323
- if (await checkLocalServerHealth("http://localhost:8080")) return DEFAULT_LOCAL_SERVER_URL;
324
- throw localServerNotRunning(DEFAULT_LOCAL_SERVER_URL);
325
- }
326
- function localServerNotRunning(serverUrl) {
327
- return new ZitadelError("E_LOCAL_SERVER_NOT_RUNNING", "Local Zitadel server is not running", {
328
- hint: `No healthy local server responded at ${serverUrl}.`,
329
- nextCommands: ["zitadel start"],
330
- details: { server_url: serverUrl }
331
- });
332
- }
333
- async function appendGitignoreEntry(cwd, entry) {
334
- const path = join(cwd, ".gitignore");
335
- let existing = "";
336
- try {
337
- existing = await readFile(path, "utf8");
338
- } catch (error) {
339
- if (!isErrno(error, "ENOENT")) throw error;
340
- }
341
- if (existing.split(/\r?\n/).map((line) => line.trim()).includes(entry)) return;
342
- const prefix = existing.length === 0 || existing.endsWith("\n") ? "" : "\n";
343
- await writeFile(path, `${existing}${prefix}${entry}\n`);
344
- }
345
- function normalizeRuntimeMetadata(input) {
346
- if (input.schema_version !== 1 || typeof input.port !== "number" || !isValidPort(input.port) || typeof input.server_url !== "string" || !isValidServerUrl(input.server_url, input.port) || typeof input.data_dir !== "string" || typeof input.created_at !== "string" || typeof input.cli_version !== "string") throw malformedRuntime(input);
347
- const backend = input.backend === void 0 ? "docker" : input.backend;
348
- const base = {
349
- schema_version: 1,
350
- port: input.port,
351
- server_url: input.server_url,
352
- data_dir: input.data_dir,
353
- created_at: input.created_at,
354
- cli_version: input.cli_version
355
- };
356
- if (backend === "binary") {
357
- if (typeof input.pid !== "number" || !Number.isInteger(input.pid) || input.pid <= 0 || typeof input.command !== "string" || typeof input.log_path !== "string" || typeof input.server_package !== "string" || typeof input.server_version !== "string") throw malformedRuntime(input);
358
- return {
359
- ...base,
360
- backend: "binary",
361
- pid: input.pid,
362
- command: input.command,
363
- log_path: input.log_path,
364
- server_package: input.server_package,
365
- server_version: input.server_version
366
- };
367
- }
368
- if (backend !== "docker" || typeof input.container_name !== "string" || typeof input.container_id !== "string" || typeof input.image !== "string") throw malformedRuntime(input);
369
- return {
370
- ...base,
371
- backend: "docker",
372
- container_name: input.container_name,
373
- container_id: input.container_id,
374
- image: input.image
375
- };
376
- }
377
- async function nearestExistingDirectory(path) {
378
- let current = path;
379
- while (true) try {
380
- if (!(await stat(current)).isDirectory()) throw new Error(`${current} exists but is not a directory`);
381
- return current;
382
- } catch (error) {
383
- if (!isErrno(error, "ENOENT")) throw error;
384
- const parent = dirname(current);
385
- if (parent === current) throw error;
386
- current = parent;
387
- }
388
- }
389
- function isErrno(error, code) {
390
- return typeof error === "object" && error !== null && "code" in error && error.code === code;
391
- }
392
- function runtimeSummary(metadata) {
393
- if (!metadata) return { configured: false };
394
- const base = {
395
- configured: true,
396
- backend: metadata.backend,
397
- port: metadata.port,
398
- server_url: metadata.server_url,
399
- data_dir: metadata.data_dir,
400
- created_at: metadata.created_at
401
- };
402
- if (metadata.backend === "binary") return {
403
- ...base,
404
- pid: metadata.pid,
405
- command: metadata.command,
406
- log_path: metadata.log_path,
407
- server_package: metadata.server_package,
408
- server_version: metadata.server_version
409
- };
410
- return {
411
- ...base,
412
- container_name: metadata.container_name,
413
- container_id: metadata.container_id,
414
- image: metadata.image
415
- };
416
- }
417
- function isValidPort(value) {
418
- return Number.isInteger(value) && value >= 1 && value <= 65535;
419
- }
420
- function isValidServerUrl(value, port) {
421
- try {
422
- const url = new URL(value);
423
- return (url.protocol === "http:" || url.protocol === "https:") && url.hostname.length > 0 && explicitUrlPort(value) === port;
424
- } catch {
425
- return false;
426
- }
427
- }
428
- function explicitUrlPort(value) {
429
- const match = value.match(/^[a-z][a-z\d+\-.]*:\/\/(?:\[[^\]]+\]|[^/?#:]+):(\d+)(?:[/?#]|$)/i);
430
- if (!match) return;
431
- const port = Number(match[1]);
432
- return isValidPort(port) ? port : void 0;
433
- }
434
- function malformedRuntime(input) {
435
- return new ZitadelError("E_VALIDATION", `${LOCAL_RUNTIME_FILE} is malformed`, {
436
- hint: "Run `zitadel reset --force`, then `zitadel start`.",
437
- nextCommands: ["zitadel reset --force", "zitadel start"],
438
- details: input
439
- });
440
- }
441
- //#endregion
442
- //#region src/lib/server.ts
443
- /**
444
- * Server URL used when nothing else resolves. Also surfaced in hints and
445
- * the interactive setup prompt as the suggested value, so it is exported
446
- * rather than kept private.
447
- */
448
- const DEFAULT_SERVER = "https://api.zitadel.cloud";
449
- /**
450
- * Resolves which server the CLI should target, applying a fixed
451
- * precedence: explicit `--server` flag, then `ZITADEL_API_BASE`, then the
452
- * selected environment block in `zitadel.json`, then the config's
453
- * top-level `server`, falling back to {@link DEFAULT_SERVER}. Every
454
- * candidate is validated to a normalised origin; an invalid URL throws a
455
- * `ZitadelError` rather than silently falling through.
456
- */
457
- async function resolveServer(input) {
458
- if (input.serverFlag) return validate(input.cwd, {
459
- value: input.serverFlag,
460
- origin: "flag"
461
- });
462
- const envValue = input.env.ZITADEL_API_BASE;
463
- if (envValue) return validate(input.cwd, {
464
- value: envValue,
465
- origin: "env"
466
- });
467
- const config = await readConfig(input.cwd);
468
- if (config) {
469
- const envBranch = readEnvServer(config, input.environment);
470
- if (envBranch) return validate(input.cwd, {
471
- value: envBranch,
472
- origin: "config-env"
473
- });
474
- if (typeof config.server === "string") return validate(input.cwd, {
475
- value: config.server,
476
- origin: "config-top"
477
- });
478
- }
479
- return {
480
- value: DEFAULT_SERVER,
481
- origin: "default"
482
- };
483
- }
484
- async function validate(cwd, resolved) {
485
- if (resolved.value === "local") return {
486
- value: await resolveLocalServer(cwd),
487
- origin: "local"
488
- };
489
- try {
490
- const url = new URL(resolved.value);
491
- if (url.protocol !== "https:" && url.protocol !== "http:") throw new ZitadelError("E_VALIDATION", `Server URL must use http(s): ${resolved.value}`, { hint: `Set "server" in zitadel.json to a URL like ${DEFAULT_SERVER}.` });
492
- return {
493
- value: url.origin,
494
- origin: resolved.origin
495
- };
496
- } catch (error) {
497
- if (error instanceof ZitadelError) throw error;
498
- throw new ZitadelError("E_VALIDATION", `Invalid server "${resolved.value}"`, {
499
- hint: `Use a URL like ${DEFAULT_SERVER}.`,
500
- details: { origin: resolved.origin }
501
- });
502
- }
503
- }
504
- async function readConfig(cwd) {
505
- try {
506
- return parseJsonObject(await readFile(join(cwd, "zitadel.json"), "utf8"), "zitadel.json");
507
- } catch (error) {
508
- if (typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT") return;
509
- throw error;
510
- }
511
- }
512
- function readEnvServer(config, environment) {
513
- if (!environment) return;
514
- const envs = config.environments;
515
- if (!isObject(envs)) return;
516
- const branch = envs[environment];
517
- if (!isObject(branch)) return;
518
- return typeof branch.server === "string" ? branch.server : void 0;
519
- }
520
- //#endregion
521
- //#region src/lib/oclif/base.ts
522
- /**
523
- * Base class for every oclif command. Owns the global flags, builds the
524
- * {@link GlobalOptions} context (including server `source` resolution) the
525
- * subclass's `run` reads via `this.meta`, and turns the {@link CommandResult}
526
- * it returns into the JSON envelope (oclif serialises it natively in `--json`
527
- * mode) or human-facing text. Errors are translated into the failure envelope
528
- * and the mapped process exit code. Subclasses stay thin: parse flags, call
529
- * {@link toMeta}, do their work, and `return this.emit(...)`. The agent
530
- * contract (ADR 004) is preserved — oclif only replaces parsing, dispatch,
531
- * help, and JSON emission.
532
- */
533
- var BaseCommand = class extends Command {
534
- /** Opt into oclif's native `--json` flag and JSON serialisation of the result. */
535
- static enableJsonFlag = true;
536
- /** Flags shared by every command, inherited via oclif `baseFlags`. */
537
- static baseFlags = {
538
- cwd: Flags.string({
539
- char: "c",
540
- description: "Project directory to operate on."
541
- }),
542
- server: Flags.string({
543
- char: "s",
544
- description: "Override the resolved server URL."
545
- }),
546
- "non-interactive": Flags.boolean({
547
- char: "n",
548
- description: "Disable prompts. Required when scripting or running as an agent."
549
- }),
550
- force: Flags.boolean({
551
- char: "f",
552
- description: "Overwrite protected files on conflict."
553
- }),
554
- "dry-run": Flags.boolean({ description: "Preview without mutating files or the platform." }),
555
- verbose: Flags.boolean({ description: "Verbose logging." }),
556
- debug: Flags.boolean({ description: "Debug logging." })
557
- };
558
- /** Resolved context for the current invocation; set by {@link toMeta}. */
559
- meta = this.fallbackMeta();
560
- /**
561
- * Builds {@link GlobalOptions} from parsed flags, resolving the server
562
- * `source` by the documented precedence and storing the result on
563
- * `this.meta` so the error handler can render a complete envelope.
564
- */
565
- async toMeta(flags, options = {}) {
566
- const cwd = resolveCwd(typeof flags.cwd === "string" ? flags.cwd : void 0);
567
- const serverFlag = typeof flags.server === "string" ? flags.server : void 0;
568
- const environment = typeof flags.environment === "string" ? flags.environment : "development";
569
- const source = options.resolveServer === false ? {
570
- value: options.source ?? "",
571
- origin: "default"
572
- } : await resolveServer({
573
- cwd,
574
- env: process.env,
575
- serverFlag,
576
- environment
577
- });
578
- const json = this.jsonEnabled();
579
- const isTTY = Boolean(process.stdout.isTTY && process.stdin.isTTY);
580
- const verbose = Boolean(flags.verbose);
581
- const debug = Boolean(flags.debug);
582
- consola.level = json ? -999 : debug ? 4 : 3;
583
- consola.options.formatOptions = {
584
- ...consola.options.formatOptions,
585
- date: false,
586
- colors: true,
587
- compact: true
588
- };
589
- this.meta = {
590
- cwd,
591
- nonInteractive: Boolean(flags["non-interactive"]) || !isTTY || json,
592
- dryRun: Boolean(flags["dry-run"]),
593
- force: Boolean(flags.force),
594
- command: this.id ?? "(default)",
595
- cliVersion: this.config.version,
596
- source: source.value,
597
- serverFlag,
598
- verbose,
599
- debug,
600
- env: process.env,
601
- isTTY
602
- };
603
- return this.meta;
604
- }
605
- /**
606
- * Final step of every command: in human mode it prints the rendered result
607
- * (oclif suppresses {@link Command.log} under `--json`); it returns the
608
- * envelope so oclif's `--json` path serialises it.
609
- */
610
- emit(result) {
611
- const normalized = normalizeCommandResult(result, this.meta);
612
- this.log(renderPretty(normalized, this.meta));
613
- return toEnvelope(normalized, this.meta);
614
- }
615
- /**
616
- * Renders any thrown error as the failure envelope and exits with its code.
617
- * A flag-parse error fires before {@link toMeta} runs, so the local `meta`
618
- * here refreshes `command` from the now-resolved command id to keep the
619
- * envelope's `command` field accurate.
620
- */
621
- async catch(error) {
622
- const meta = {
623
- ...this.meta,
624
- command: this.id ?? this.meta.command
625
- };
626
- const zitadelError = toZitadelError(error);
627
- if (this.jsonEnabled()) this.logJson(toErrorEnvelope(zitadelError, meta));
628
- else this.logToStderr(renderError(zitadelError, meta));
629
- return this.exit(zitadelError.exitCode);
630
- }
631
- /**
632
- * Context used before {@link toMeta} runs, so an error thrown during flag
633
- * parsing still renders a complete envelope. Version comes from oclif's
634
- * resolved {@link Command.config}.
635
- */
636
- fallbackMeta() {
637
- return {
638
- cwd: resolveCwd(void 0),
639
- nonInteractive: false,
640
- dryRun: false,
641
- force: false,
642
- command: "(default)",
643
- cliVersion: this.config.version,
644
- source: "",
645
- verbose: false,
646
- debug: false,
647
- env: process.env,
648
- isTTY: Boolean(process.stdout.isTTY && process.stdin.isTTY)
649
- };
650
- }
651
- };
652
- function normalizeCommandResult(result, meta) {
653
- if (result.status === "ok") return {
654
- ...result,
655
- data: normalizeDataNextCommands(result.data, meta)
656
- };
657
- return {
658
- ...result,
659
- data: normalizeDataNextCommands(result.data, meta),
660
- nextCommands: normalizePublicCliCommands(result.nextCommands, meta.cliVersion)
661
- };
662
- }
663
- function normalizeDataNextCommands(data, meta) {
664
- if (!isObject(data) || !Array.isArray(data.next_commands)) return data;
665
- return {
666
- ...data,
667
- next_commands: data.next_commands.map((command) => typeof command === "string" ? normalizePublicCliCommand(command, meta.cliVersion) : command)
668
- };
669
- }
670
- /** Wraps a {@link CommandResult} with the invocation metadata into the final envelope. */
671
- function toEnvelope(result, meta) {
672
- const base = {
673
- cli_version: meta.cliVersion,
674
- command: meta.command,
675
- source: meta.source
676
- };
677
- if (result.status === "ok") return {
678
- ...base,
679
- status: "ok",
680
- data: result.data,
681
- warnings: result.warnings ? [...result.warnings] : []
682
- };
683
- return {
684
- ...base,
685
- status: "skipped",
686
- reason: result.reason,
687
- data: result.data,
688
- next_commands: result.nextCommands ? [...result.nextCommands] : void 0
689
- };
690
- }
691
- /** Builds the failure envelope from a {@link ZitadelError} and the invocation metadata. */
692
- function toErrorEnvelope(error, meta) {
693
- return {
694
- status: "error",
695
- cli_version: meta.cliVersion,
696
- command: meta.command,
697
- source: meta.source,
698
- code: error.code,
699
- message: error.message,
700
- hint: error.hint,
701
- next_commands: normalizePublicCliCommands(error.nextCommands, meta.cliVersion),
702
- details: error.details
703
- };
704
- }
705
- /**
706
- * Renders a {@link CommandResult} as human-facing text for non-JSON mode. A
707
- * command may supply a bespoke `pretty` string (e.g. the `apply` plan diff);
708
- * otherwise success payloads are summarised by {@link formatData} and skips are
709
- * shown with their reason and follow-up commands.
710
- */
711
- function renderPretty(result, meta) {
712
- if (result.pretty !== void 0) return result.pretty;
713
- if (result.status === "ok") return formatData(result.data, result.warnings ? [...result.warnings] : [], meta);
714
- const lines = [`Skipped: ${result.reason}${suffixBlock(meta)}`];
715
- if (result.nextCommands && result.nextCommands.length > 0) {
716
- lines.push("Next:");
717
- for (const cmd of result.nextCommands) lines.push(` $ ${cmd}`);
718
- }
719
- return lines.join("\n");
720
- }
721
- /**
722
- * Renders a {@link ZitadelError} as a human-readable block for stderr: the
723
- * coded message, an optional hint, and any suggested next commands.
724
- */
725
- function renderError(error, meta) {
726
- const lines = [`Error ${error.code}: ${error.message}`];
727
- if (error.hint) lines.push(error.hint);
728
- const nextCommands = normalizePublicCliCommands(error.nextCommands, meta.cliVersion);
729
- if (nextCommands && nextCommands.length > 0) {
730
- lines.push("Next:");
731
- for (const cmd of nextCommands) lines.push(` $ ${cmd}`);
732
- }
733
- return lines.join("\n");
734
- }
735
- function formatData(data, warnings, opts) {
736
- if (typeof data === "string") {
737
- const suffix = sourceSuffix(opts);
738
- return suffix ? `${data}\n${suffix}` : data;
739
- }
740
- const lines = [];
741
- const titleLine = isObject(data) && typeof data.title === "string" ? String(data.title) : "Zitadel command completed.";
742
- lines.push(titleLine);
743
- const suffix = sourceSuffix(opts);
744
- if (suffix) lines.push(suffix);
745
- if (isObject(data)) {
746
- renderKnownSections(lines, data);
747
- if (Array.isArray(data.next_actions) && data.next_actions.length > 0) {
748
- lines.push("");
749
- lines.push("Next:");
750
- for (const action of data.next_actions) lines.push(` ${String(action)}`);
751
- }
752
- if (Array.isArray(data.next_commands) && data.next_commands.length > 0) {
753
- if (!Array.isArray(data.next_actions) || data.next_actions.length === 0) {
754
- lines.push("");
755
- lines.push("Next:");
756
- }
757
- for (const cmd of data.next_commands) lines.push(` $ ${String(cmd)}`);
758
- }
759
- }
760
- for (const warning of warnings.filter((warning) => !warningRenderedInChecks(data, warning))) lines.push(`Warning: ${warning}`);
761
- return lines.join("\n");
762
- }
763
- function warningRenderedInChecks(data, warning) {
764
- if (!isObject(data) || !Array.isArray(data.checks)) return false;
765
- return data.checks.some((check) => {
766
- if (!isObject(check) || check.status !== "warn") return false;
767
- return warning === `${String(check.name ?? "check")}: ${String(check.message ?? "")}`;
768
- });
769
- }
770
- function renderKnownSections(lines, data) {
771
- if (isObject(data.project)) {
772
- const project = data.project;
773
- const segments = [];
774
- if (typeof project.project_id === "string") segments.push(`project=${project.project_id}`);
775
- if (typeof project.lifecycle === "string") segments.push(`lifecycle=${project.lifecycle}`);
776
- if (typeof project.issuer === "string") segments.push(`issuer=${project.issuer}`);
777
- if (segments.length > 0) lines.push(`Project: ${segments.join(" ")}`);
778
- }
779
- if (typeof data.framework === "string") lines.push(`framework=${data.framework}`);
780
- if (Array.isArray(data.files_written) || Array.isArray(data.files_skipped)) {
781
- const written = Array.isArray(data.files_written) ? data.files_written.length : 0;
782
- const skippedCount = Array.isArray(data.files_skipped) ? data.files_skipped.length : 0;
783
- lines.push(`Files: ${written} written, ${skippedCount} unchanged`);
784
- }
785
- if (isObject(data.apply)) {
786
- const apply = data.apply;
787
- const bits = [];
788
- if (typeof apply.config_version === "number") bits.push(`v${apply.config_version}`);
789
- if (typeof apply.hash === "string") bits.push(`hash=${String(apply.hash).slice(0, 12)}`);
790
- if (typeof apply.environment === "string") bits.push(`env=${apply.environment}`);
791
- if (bits.length > 0) lines.push(`Apply: ${bits.join(" ")}`);
792
- }
793
- if (Array.isArray(data.checks) && data.checks.length > 0) {
794
- lines.push("Checks:");
795
- for (const check of data.checks) {
796
- if (!isObject(check)) continue;
797
- const status = check.status === "pass" ? "ok" : check.status === "warn" ? "warn" : "fail";
798
- lines.push(` [${status}] ${String(check.name ?? "check")}: ${String(check.message ?? "")}`);
799
- }
800
- }
801
- }
802
- function sourceSuffix(opts) {
803
- try {
804
- const url = new URL(opts.source);
805
- if (url.host === "api.zitadel.cloud") return "";
806
- return `(server: ${url.host})`;
807
- } catch {
808
- return "";
809
- }
810
- }
811
- function suffixBlock(opts) {
812
- const suffix = sourceSuffix(opts);
813
- return suffix ? ` ${suffix}` : "";
814
- }
815
- //#endregion
816
- export { resolveCwd as C, ZitadelError as D, stableStringify as E, toZitadelError as O, MANAGED_MARKER as S, parseJsonObject as T, removeRuntimeMetadata as _, DEFAULT_LOCAL_SERVER_PORT as a, npmDistTagForCliVersion as b, checkLocalServerHealth as c, ensureLocalState as d, isPortAvailable as f, removeLocalData as g, readRuntimeMetadata as h, CONTAINER_HTTP_PORT as i, defaultLocalServerImageForCliVersion as l, localServerUrl as m, DEFAULT_SERVER as n, DEFAULT_LOCAL_SERVER_URL as o, localContainerName as p, CONTAINER_DATA_DIR as r, assertLocalStateWritable as s, BaseCommand as t, ensureContainerIdentity as u, runtimeSummary as v, isObject as w, publicCliCommand as x, writeRuntimeMetadata as y };
817
-
818
- //# sourceMappingURL=oclif-VkCTGIEk.mjs.map