@zitadel/cli 0.1.0-alpha.0 → 0.1.0-alpha.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +227 -18
- package/SKILLS.md +104 -11
- package/dist/commands/apply.mjs +4 -4
- package/dist/commands/apply.mjs.map +1 -1
- package/dist/commands/doctor.mjs +291 -17
- package/dist/commands/doctor.mjs.map +1 -1
- package/dist/commands/eject.mjs +14 -6
- package/dist/commands/eject.mjs.map +1 -1
- package/dist/commands/logs.mjs +58 -0
- package/dist/commands/logs.mjs.map +1 -0
- package/dist/commands/plan.mjs +4 -4
- package/dist/commands/plan.mjs.map +1 -1
- package/dist/commands/reset.mjs +79 -0
- package/dist/commands/reset.mjs.map +1 -0
- package/dist/commands/setup.mjs +267 -105
- package/dist/commands/setup.mjs.map +1 -1
- package/dist/commands/start.mjs +288 -0
- package/dist/commands/start.mjs.map +1 -0
- package/dist/commands/status.mjs +92 -27
- package/dist/commands/status.mjs.map +1 -1
- package/dist/commands/stop.mjs +105 -0
- package/dist/commands/stop.mjs.map +1 -0
- package/dist/docker-CnGQK3ZK.mjs +432 -0
- package/dist/docker-CnGQK3ZK.mjs.map +1 -0
- package/dist/docker-guidance-ypN3IM3o.mjs +21 -0
- package/dist/docker-guidance-ypN3IM3o.mjs.map +1 -0
- package/dist/{project-C3pSfbao.mjs → oclif-B7lBzh3R.mjs} +339 -119
- package/dist/oclif-B7lBzh3R.mjs.map +1 -0
- package/dist/orca-BoTFU8SI.mjs +2581 -0
- package/dist/orca-BoTFU8SI.mjs.map +1 -0
- package/dist/ports-B09RjuHx.mjs +111 -0
- package/dist/ports-B09RjuHx.mjs.map +1 -0
- package/dist/processes-Cw8TO1SY.mjs +120 -0
- package/dist/processes-Cw8TO1SY.mjs.map +1 -0
- package/dist/project-Cd0L3PtM.mjs +87 -0
- package/dist/project-Cd0L3PtM.mjs.map +1 -0
- package/dist/{sync-Cuyh-X1J.mjs → sync-BojoQm2P.mjs} +4 -4
- package/dist/{sync-Cuyh-X1J.mjs.map → sync-BojoQm2P.mjs.map} +1 -1
- package/oclif.manifest.json +399 -7
- package/package.json +8 -41
- package/dist/orca-COsUnVoz.mjs +0 -1006
- package/dist/orca-COsUnVoz.mjs.map +0 -1
- package/dist/project-C3pSfbao.mjs.map +0 -1
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { Command, Flags } from "@oclif/core";
|
|
2
2
|
import consola from "consola";
|
|
3
|
-
import { readFile, stat } from "node:fs/promises";
|
|
4
|
-
import { join, resolve } from "node:path";
|
|
5
3
|
import { ApiError } from "@zitadel/api/runtime/fetch";
|
|
6
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";
|
|
7
9
|
//#region src/lib/errors.ts
|
|
8
10
|
/**
|
|
9
11
|
* Maps each {@link ZitadelErrorCode} to the process exit code the CLI
|
|
@@ -17,6 +19,8 @@ const EXIT_CODES = {
|
|
|
17
19
|
E_NETWORK: 4,
|
|
18
20
|
E_AUTH: 1,
|
|
19
21
|
E_CONFLICT: 5,
|
|
22
|
+
E_LOCAL_SERVER_NOT_RUNNING: 4,
|
|
23
|
+
E_PORT_IN_USE: 5,
|
|
20
24
|
E_VALIDATION: 3,
|
|
21
25
|
E_NOT_IMPLEMENTED: 2
|
|
22
26
|
};
|
|
@@ -144,6 +148,287 @@ function isObject(value) {
|
|
|
144
148
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
145
149
|
}
|
|
146
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 resolveLocalServer(cwd) {
|
|
308
|
+
const runtime = await readRuntimeMetadata(cwd);
|
|
309
|
+
if (runtime) {
|
|
310
|
+
if (await checkLocalServerHealth(runtime.server_url)) return runtime.server_url;
|
|
311
|
+
throw localServerNotRunning(runtime.server_url);
|
|
312
|
+
}
|
|
313
|
+
if (await checkLocalServerHealth("http://localhost:8080")) return DEFAULT_LOCAL_SERVER_URL;
|
|
314
|
+
throw localServerNotRunning(DEFAULT_LOCAL_SERVER_URL);
|
|
315
|
+
}
|
|
316
|
+
function localServerNotRunning(serverUrl) {
|
|
317
|
+
return new ZitadelError("E_LOCAL_SERVER_NOT_RUNNING", "Local Zitadel server is not running", {
|
|
318
|
+
hint: `No healthy local server responded at ${serverUrl}.`,
|
|
319
|
+
nextCommands: ["zitadel start"],
|
|
320
|
+
details: { server_url: serverUrl }
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
async function appendGitignoreEntry(cwd, entry) {
|
|
324
|
+
const path = join(cwd, ".gitignore");
|
|
325
|
+
let existing = "";
|
|
326
|
+
try {
|
|
327
|
+
existing = await readFile(path, "utf8");
|
|
328
|
+
} catch (error) {
|
|
329
|
+
if (!isErrno(error, "ENOENT")) throw error;
|
|
330
|
+
}
|
|
331
|
+
if (existing.split(/\r?\n/).map((line) => line.trim()).includes(entry)) return;
|
|
332
|
+
const prefix = existing.length === 0 || existing.endsWith("\n") ? "" : "\n";
|
|
333
|
+
await writeFile(path, `${existing}${prefix}${entry}\n`);
|
|
334
|
+
}
|
|
335
|
+
function normalizeRuntimeMetadata(input) {
|
|
336
|
+
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);
|
|
337
|
+
const backend = input.backend === void 0 ? "docker" : input.backend;
|
|
338
|
+
const base = {
|
|
339
|
+
schema_version: 1,
|
|
340
|
+
port: input.port,
|
|
341
|
+
server_url: input.server_url,
|
|
342
|
+
data_dir: input.data_dir,
|
|
343
|
+
created_at: input.created_at,
|
|
344
|
+
cli_version: input.cli_version
|
|
345
|
+
};
|
|
346
|
+
if (backend === "binary") {
|
|
347
|
+
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);
|
|
348
|
+
return {
|
|
349
|
+
...base,
|
|
350
|
+
backend: "binary",
|
|
351
|
+
pid: input.pid,
|
|
352
|
+
command: input.command,
|
|
353
|
+
log_path: input.log_path,
|
|
354
|
+
server_package: input.server_package,
|
|
355
|
+
server_version: input.server_version
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
if (backend !== "docker" || typeof input.container_name !== "string" || typeof input.container_id !== "string" || typeof input.image !== "string") throw malformedRuntime(input);
|
|
359
|
+
return {
|
|
360
|
+
...base,
|
|
361
|
+
backend: "docker",
|
|
362
|
+
container_name: input.container_name,
|
|
363
|
+
container_id: input.container_id,
|
|
364
|
+
image: input.image
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
async function nearestExistingDirectory(path) {
|
|
368
|
+
let current = path;
|
|
369
|
+
while (true) try {
|
|
370
|
+
if (!(await stat(current)).isDirectory()) throw new Error(`${current} exists but is not a directory`);
|
|
371
|
+
return current;
|
|
372
|
+
} catch (error) {
|
|
373
|
+
if (!isErrno(error, "ENOENT")) throw error;
|
|
374
|
+
const parent = dirname(current);
|
|
375
|
+
if (parent === current) throw error;
|
|
376
|
+
current = parent;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
function isErrno(error, code) {
|
|
380
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
381
|
+
}
|
|
382
|
+
function runtimeSummary(metadata) {
|
|
383
|
+
if (!metadata) return { configured: false };
|
|
384
|
+
const base = {
|
|
385
|
+
configured: true,
|
|
386
|
+
backend: metadata.backend,
|
|
387
|
+
port: metadata.port,
|
|
388
|
+
server_url: metadata.server_url,
|
|
389
|
+
data_dir: metadata.data_dir,
|
|
390
|
+
created_at: metadata.created_at
|
|
391
|
+
};
|
|
392
|
+
if (metadata.backend === "binary") return {
|
|
393
|
+
...base,
|
|
394
|
+
pid: metadata.pid,
|
|
395
|
+
command: metadata.command,
|
|
396
|
+
log_path: metadata.log_path,
|
|
397
|
+
server_package: metadata.server_package,
|
|
398
|
+
server_version: metadata.server_version
|
|
399
|
+
};
|
|
400
|
+
return {
|
|
401
|
+
...base,
|
|
402
|
+
container_name: metadata.container_name,
|
|
403
|
+
container_id: metadata.container_id,
|
|
404
|
+
image: metadata.image
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
function isValidPort(value) {
|
|
408
|
+
return Number.isInteger(value) && value >= 1 && value <= 65535;
|
|
409
|
+
}
|
|
410
|
+
function isValidServerUrl(value, port) {
|
|
411
|
+
try {
|
|
412
|
+
const url = new URL(value);
|
|
413
|
+
return (url.protocol === "http:" || url.protocol === "https:") && url.hostname.length > 0 && explicitUrlPort(value) === port;
|
|
414
|
+
} catch {
|
|
415
|
+
return false;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
function explicitUrlPort(value) {
|
|
419
|
+
const match = value.match(/^[a-z][a-z\d+\-.]*:\/\/(?:\[[^\]]+\]|[^/?#:]+):(\d+)(?:[/?#]|$)/i);
|
|
420
|
+
if (!match) return;
|
|
421
|
+
const port = Number(match[1]);
|
|
422
|
+
return isValidPort(port) ? port : void 0;
|
|
423
|
+
}
|
|
424
|
+
function malformedRuntime(input) {
|
|
425
|
+
return new ZitadelError("E_VALIDATION", `${LOCAL_RUNTIME_FILE} is malformed`, {
|
|
426
|
+
hint: "Run `zitadel reset --force`, then `zitadel start`.",
|
|
427
|
+
nextCommands: ["zitadel reset --force", "zitadel start"],
|
|
428
|
+
details: input
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
//#endregion
|
|
147
432
|
//#region src/lib/server.ts
|
|
148
433
|
/**
|
|
149
434
|
* Server URL used when nothing else resolves. Also surfaced in hints and
|
|
@@ -160,23 +445,23 @@ const DEFAULT_SERVER = "https://api.zitadel.cloud";
|
|
|
160
445
|
* `ZitadelError` rather than silently falling through.
|
|
161
446
|
*/
|
|
162
447
|
async function resolveServer(input) {
|
|
163
|
-
if (input.serverFlag) return validate({
|
|
448
|
+
if (input.serverFlag) return validate(input.cwd, {
|
|
164
449
|
value: input.serverFlag,
|
|
165
450
|
origin: "flag"
|
|
166
451
|
});
|
|
167
452
|
const envValue = input.env.ZITADEL_API_BASE;
|
|
168
|
-
if (envValue) return validate({
|
|
453
|
+
if (envValue) return validate(input.cwd, {
|
|
169
454
|
value: envValue,
|
|
170
455
|
origin: "env"
|
|
171
456
|
});
|
|
172
457
|
const config = await readConfig(input.cwd);
|
|
173
458
|
if (config) {
|
|
174
459
|
const envBranch = readEnvServer(config, input.environment);
|
|
175
|
-
if (envBranch) return validate({
|
|
460
|
+
if (envBranch) return validate(input.cwd, {
|
|
176
461
|
value: envBranch,
|
|
177
462
|
origin: "config-env"
|
|
178
463
|
});
|
|
179
|
-
if (typeof config.server === "string") return validate({
|
|
464
|
+
if (typeof config.server === "string") return validate(input.cwd, {
|
|
180
465
|
value: config.server,
|
|
181
466
|
origin: "config-top"
|
|
182
467
|
});
|
|
@@ -186,7 +471,11 @@ async function resolveServer(input) {
|
|
|
186
471
|
origin: "default"
|
|
187
472
|
};
|
|
188
473
|
}
|
|
189
|
-
function validate(resolved) {
|
|
474
|
+
async function validate(cwd, resolved) {
|
|
475
|
+
if (resolved.value === "local") return {
|
|
476
|
+
value: await resolveLocalServer(cwd),
|
|
477
|
+
origin: "local"
|
|
478
|
+
};
|
|
190
479
|
try {
|
|
191
480
|
const url = new URL(resolved.value);
|
|
192
481
|
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}.` });
|
|
@@ -219,24 +508,6 @@ function readEnvServer(config, environment) {
|
|
|
219
508
|
return typeof branch.server === "string" ? branch.server : void 0;
|
|
220
509
|
}
|
|
221
510
|
//#endregion
|
|
222
|
-
//#region src/lib/paths.ts
|
|
223
|
-
/**
|
|
224
|
-
* Resolve the working directory the CLI should operate against, defaulting to
|
|
225
|
-
* the process CWD when no `--cwd` override is given. Always returns an
|
|
226
|
-
* absolute path so downstream `join`/`readFile` calls are unaffected by later
|
|
227
|
-
* `process.chdir` or relative-path ambiguity.
|
|
228
|
-
*/
|
|
229
|
-
function resolveCwd(cwd) {
|
|
230
|
-
return resolve(cwd ?? process.cwd());
|
|
231
|
-
}
|
|
232
|
-
/**
|
|
233
|
-
* Sentinel comment stamped at the top of every file the CLI generates and
|
|
234
|
-
* owns. Commands like `doctor` and `eject` look for this marker to decide
|
|
235
|
-
* whether a file is safe to touch; the trailing `v1` lets the format evolve
|
|
236
|
-
* without mistaking newer managed files for hand-edited ones.
|
|
237
|
-
*/
|
|
238
|
-
const MANAGED_MARKER = "// zitadel-cli: managed-file v1";
|
|
239
|
-
//#endregion
|
|
240
511
|
//#region src/lib/oclif/base.ts
|
|
241
512
|
/**
|
|
242
513
|
* Base class for every oclif command. Owns the global flags, builds the
|
|
@@ -281,11 +552,14 @@ var BaseCommand = class extends Command {
|
|
|
281
552
|
* `source` by the documented precedence and storing the result on
|
|
282
553
|
* `this.meta` so the error handler can render a complete envelope.
|
|
283
554
|
*/
|
|
284
|
-
async toMeta(flags) {
|
|
555
|
+
async toMeta(flags, options = {}) {
|
|
285
556
|
const cwd = resolveCwd(typeof flags.cwd === "string" ? flags.cwd : void 0);
|
|
286
557
|
const serverFlag = typeof flags.server === "string" ? flags.server : void 0;
|
|
287
558
|
const environment = typeof flags.environment === "string" ? flags.environment : "development";
|
|
288
|
-
const source =
|
|
559
|
+
const source = options.resolveServer === false ? {
|
|
560
|
+
value: options.source ?? "",
|
|
561
|
+
origin: "default"
|
|
562
|
+
} : await resolveServer({
|
|
289
563
|
cwd,
|
|
290
564
|
env: process.env,
|
|
291
565
|
serverFlag,
|
|
@@ -324,8 +598,9 @@ var BaseCommand = class extends Command {
|
|
|
324
598
|
* envelope so oclif's `--json` path serialises it.
|
|
325
599
|
*/
|
|
326
600
|
emit(result) {
|
|
327
|
-
|
|
328
|
-
|
|
601
|
+
const normalized = normalizeCommandResult(result, this.meta);
|
|
602
|
+
this.log(renderPretty(normalized, this.meta));
|
|
603
|
+
return toEnvelope(normalized, this.meta);
|
|
329
604
|
}
|
|
330
605
|
/**
|
|
331
606
|
* Renders any thrown error as the failure envelope and exits with its code.
|
|
@@ -340,7 +615,7 @@ var BaseCommand = class extends Command {
|
|
|
340
615
|
};
|
|
341
616
|
const zitadelError = toZitadelError(error);
|
|
342
617
|
if (this.jsonEnabled()) this.logJson(toErrorEnvelope(zitadelError, meta));
|
|
343
|
-
else this.logToStderr(renderError(zitadelError));
|
|
618
|
+
else this.logToStderr(renderError(zitadelError, meta));
|
|
344
619
|
return this.exit(zitadelError.exitCode);
|
|
345
620
|
}
|
|
346
621
|
/**
|
|
@@ -364,6 +639,24 @@ var BaseCommand = class extends Command {
|
|
|
364
639
|
};
|
|
365
640
|
}
|
|
366
641
|
};
|
|
642
|
+
function normalizeCommandResult(result, meta) {
|
|
643
|
+
if (result.status === "ok") return {
|
|
644
|
+
...result,
|
|
645
|
+
data: normalizeDataNextCommands(result.data, meta)
|
|
646
|
+
};
|
|
647
|
+
return {
|
|
648
|
+
...result,
|
|
649
|
+
data: normalizeDataNextCommands(result.data, meta),
|
|
650
|
+
nextCommands: normalizePublicCliCommands(result.nextCommands, meta.cliVersion)
|
|
651
|
+
};
|
|
652
|
+
}
|
|
653
|
+
function normalizeDataNextCommands(data, meta) {
|
|
654
|
+
if (!isObject(data) || !Array.isArray(data.next_commands)) return data;
|
|
655
|
+
return {
|
|
656
|
+
...data,
|
|
657
|
+
next_commands: data.next_commands.map((command) => typeof command === "string" ? normalizePublicCliCommand(command, meta.cliVersion) : command)
|
|
658
|
+
};
|
|
659
|
+
}
|
|
367
660
|
/** Wraps a {@link CommandResult} with the invocation metadata into the final envelope. */
|
|
368
661
|
function toEnvelope(result, meta) {
|
|
369
662
|
const base = {
|
|
@@ -395,7 +688,7 @@ function toErrorEnvelope(error, meta) {
|
|
|
395
688
|
code: error.code,
|
|
396
689
|
message: error.message,
|
|
397
690
|
hint: error.hint,
|
|
398
|
-
next_commands: error.nextCommands,
|
|
691
|
+
next_commands: normalizePublicCliCommands(error.nextCommands, meta.cliVersion),
|
|
399
692
|
details: error.details
|
|
400
693
|
};
|
|
401
694
|
}
|
|
@@ -419,12 +712,13 @@ function renderPretty(result, meta) {
|
|
|
419
712
|
* Renders a {@link ZitadelError} as a human-readable block for stderr: the
|
|
420
713
|
* coded message, an optional hint, and any suggested next commands.
|
|
421
714
|
*/
|
|
422
|
-
function renderError(error) {
|
|
715
|
+
function renderError(error, meta) {
|
|
423
716
|
const lines = [`Error ${error.code}: ${error.message}`];
|
|
424
717
|
if (error.hint) lines.push(error.hint);
|
|
425
|
-
|
|
718
|
+
const nextCommands = normalizePublicCliCommands(error.nextCommands, meta.cliVersion);
|
|
719
|
+
if (nextCommands && nextCommands.length > 0) {
|
|
426
720
|
lines.push("Next:");
|
|
427
|
-
for (const cmd of
|
|
721
|
+
for (const cmd of nextCommands) lines.push(` $ ${cmd}`);
|
|
428
722
|
}
|
|
429
723
|
return lines.join("\n");
|
|
430
724
|
}
|
|
@@ -453,9 +747,16 @@ function formatData(data, warnings, opts) {
|
|
|
453
747
|
for (const cmd of data.next_commands) lines.push(` $ ${String(cmd)}`);
|
|
454
748
|
}
|
|
455
749
|
}
|
|
456
|
-
for (const warning of warnings) lines.push(`Warning: ${warning}`);
|
|
750
|
+
for (const warning of warnings.filter((warning) => !warningRenderedInChecks(data, warning))) lines.push(`Warning: ${warning}`);
|
|
457
751
|
return lines.join("\n");
|
|
458
752
|
}
|
|
753
|
+
function warningRenderedInChecks(data, warning) {
|
|
754
|
+
if (!isObject(data) || !Array.isArray(data.checks)) return false;
|
|
755
|
+
return data.checks.some((check) => {
|
|
756
|
+
if (!isObject(check) || check.status !== "warn") return false;
|
|
757
|
+
return warning === `${String(check.name ?? "check")}: ${String(check.message ?? "")}`;
|
|
758
|
+
});
|
|
759
|
+
}
|
|
459
760
|
function renderKnownSections(lines, data) {
|
|
460
761
|
if (isObject(data.project)) {
|
|
461
762
|
const project = data.project;
|
|
@@ -483,7 +784,7 @@ function renderKnownSections(lines, data) {
|
|
|
483
784
|
lines.push("Checks:");
|
|
484
785
|
for (const check of data.checks) {
|
|
485
786
|
if (!isObject(check)) continue;
|
|
486
|
-
const status = check.status === "pass" ? "ok" : "fail";
|
|
787
|
+
const status = check.status === "pass" ? "ok" : check.status === "warn" ? "warn" : "fail";
|
|
487
788
|
lines.push(` [${status}] ${String(check.name ?? "check")}: ${String(check.message ?? "")}`);
|
|
488
789
|
}
|
|
489
790
|
}
|
|
@@ -502,87 +803,6 @@ function suffixBlock(opts) {
|
|
|
502
803
|
return suffix ? ` ${suffix}` : "";
|
|
503
804
|
}
|
|
504
805
|
//#endregion
|
|
505
|
-
|
|
506
|
-
/**
|
|
507
|
-
* Reports whether `cwd` has already been initialized, i.e. a committed
|
|
508
|
-
* `zitadel.json` exists. Used to decide whether setup should run or skip.
|
|
509
|
-
*/
|
|
510
|
-
async function hasZitadelConfig(cwd) {
|
|
511
|
-
return exists(join(cwd, "zitadel.json"));
|
|
512
|
-
}
|
|
513
|
-
/**
|
|
514
|
-
* Reports whether local secret material (`.zitadel/secret`) is present. Gates
|
|
515
|
-
* commands that need credentials, and signals that secrets were already pulled.
|
|
516
|
-
*/
|
|
517
|
-
async function hasZitadelSecret(cwd) {
|
|
518
|
-
return exists(join(cwd, ".zitadel/secret"));
|
|
519
|
-
}
|
|
520
|
-
async function exists(path) {
|
|
521
|
-
try {
|
|
522
|
-
await stat(path);
|
|
523
|
-
return true;
|
|
524
|
-
} catch (error) {
|
|
525
|
-
if (isNotFound(error)) return false;
|
|
526
|
-
throw error;
|
|
527
|
-
}
|
|
528
|
-
}
|
|
529
|
-
/**
|
|
530
|
-
* Reads and parses `zitadel.json` into a plain object. Translates a missing
|
|
531
|
-
* file into an actionable `E_VALIDATION` error pointing at `zitadel setup`;
|
|
532
|
-
* other errors (e.g. malformed JSON) propagate unchanged.
|
|
533
|
-
*/
|
|
534
|
-
async function readZitadelConfig(cwd) {
|
|
535
|
-
try {
|
|
536
|
-
return parseJsonObject(await readFile(join(cwd, "zitadel.json"), "utf8"), "zitadel.json");
|
|
537
|
-
} catch (error) {
|
|
538
|
-
if (isNotFound(error)) throw new ZitadelError("E_VALIDATION", "zitadel.json was not found", {
|
|
539
|
-
hint: "Run `zitadel setup` first.",
|
|
540
|
-
nextCommands: ["zitadel setup"]
|
|
541
|
-
});
|
|
542
|
-
throw error;
|
|
543
|
-
}
|
|
544
|
-
}
|
|
545
|
-
/**
|
|
546
|
-
* Reads, parses, and structurally validates `.zitadel/secret`, returning it
|
|
547
|
-
* as a {@link ZitadelSecret}. A missing file becomes an actionable
|
|
548
|
-
* `E_VALIDATION` error pointing at `zitadel setup` / `zitadel doctor --fix`;
|
|
549
|
-
* a present-but-incomplete file throws so callers never proceed with partial
|
|
550
|
-
* credentials.
|
|
551
|
-
*/
|
|
552
|
-
async function readZitadelSecret(cwd) {
|
|
553
|
-
try {
|
|
554
|
-
const secret = parseJsonObject(await readFile(join(cwd, ".zitadel/secret"), "utf8"), ".zitadel/secret");
|
|
555
|
-
if (typeof secret.project_id !== "string" || typeof secret.project_secret !== "string" || typeof secret.preview_secret !== "string" || !Array.isArray(secret.preview_origins)) throw new Error(".zitadel/secret is missing required fields");
|
|
556
|
-
return secret;
|
|
557
|
-
} catch (error) {
|
|
558
|
-
if (isNotFound(error)) throw new ZitadelError("E_VALIDATION", ".zitadel/secret was not found", {
|
|
559
|
-
hint: "Run `zitadel setup` first, or restore the project secret with `zitadel doctor --fix`.",
|
|
560
|
-
nextCommands: ["zitadel setup", "zitadel doctor --fix"]
|
|
561
|
-
});
|
|
562
|
-
throw error;
|
|
563
|
-
}
|
|
564
|
-
}
|
|
565
|
-
/**
|
|
566
|
-
* Reads the configured renderer id from a parsed `zitadel.json`, normalising the
|
|
567
|
-
* legacy `default` alias to `react` and falling back to `react` when unset. The
|
|
568
|
-
* value is validated downstream by `getRenderer`, so callers need not re-check.
|
|
569
|
-
*/
|
|
570
|
-
function readRendererId(config) {
|
|
571
|
-
const branding = isObject(config.branding) ? config.branding : void 0;
|
|
572
|
-
const value = branding && typeof branding.renderer === "string" ? branding.renderer : "react";
|
|
573
|
-
return value === "default" ? "react" : value;
|
|
574
|
-
}
|
|
575
|
-
/** Reads `environments.development.issuer` from a parsed `zitadel.json`, if present. */
|
|
576
|
-
function readDevelopmentIssuer(config) {
|
|
577
|
-
if (isObject(config.environments) && isObject(config.environments.development)) {
|
|
578
|
-
const issuer = config.environments.development.issuer;
|
|
579
|
-
return typeof issuer === "string" ? issuer : void 0;
|
|
580
|
-
}
|
|
581
|
-
}
|
|
582
|
-
function isNotFound(error) {
|
|
583
|
-
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
584
|
-
}
|
|
585
|
-
//#endregion
|
|
586
|
-
export { readZitadelConfig as a, MANAGED_MARKER as c, parseJsonObject as d, stableStringify as f, readRendererId as i, DEFAULT_SERVER as l, hasZitadelSecret as n, readZitadelSecret as o, ZitadelError as p, readDevelopmentIssuer as r, BaseCommand as s, hasZitadelConfig as t, isObject as u };
|
|
806
|
+
export { isObject as C, toZitadelError as D, ZitadelError as E, resolveCwd as S, stableStringify as T, runtimeSummary as _, DEFAULT_LOCAL_SERVER_PORT as a, publicCliCommand as b, checkLocalServerHealth as c, ensureLocalState as d, localContainerName as f, removeRuntimeMetadata as g, removeLocalData as h, CONTAINER_HTTP_PORT as i, defaultLocalServerImageForCliVersion as l, readRuntimeMetadata as m, DEFAULT_SERVER as n, DEFAULT_LOCAL_SERVER_URL as o, localServerUrl as p, CONTAINER_DATA_DIR as r, assertLocalStateWritable as s, BaseCommand as t, ensureContainerIdentity as u, writeRuntimeMetadata as v, parseJsonObject as w, MANAGED_MARKER as x, npmDistTagForCliVersion as y };
|
|
587
807
|
|
|
588
|
-
//# sourceMappingURL=
|
|
808
|
+
//# sourceMappingURL=oclif-B7lBzh3R.mjs.map
|