@zitadel/cli 0.1.0-alpha.5 → 0.1.0-alpha.9
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 +15 -12
- package/SKILLS.md +54 -20
- package/dist/commands/apply.mjs +3 -3
- package/dist/commands/doctor.mjs +160 -26
- package/dist/commands/doctor.mjs.map +1 -1
- package/dist/commands/eject.mjs +3 -3
- package/dist/commands/logs.mjs +13 -5
- package/dist/commands/logs.mjs.map +1 -1
- package/dist/commands/plan.mjs +3 -3
- package/dist/commands/reset.mjs +24 -7
- package/dist/commands/reset.mjs.map +1 -1
- package/dist/commands/setup.mjs +18 -78
- package/dist/commands/setup.mjs.map +1 -1
- package/dist/commands/start.mjs +171 -11
- package/dist/commands/start.mjs.map +1 -1
- package/dist/commands/status.mjs +26 -7
- package/dist/commands/status.mjs.map +1 -1
- package/dist/commands/stop.mjs +69 -7
- package/dist/commands/stop.mjs.map +1 -1
- 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/{oclif-2t97lHfY.mjs → oclif-B7lBzh3R.mjs} +51 -26
- package/dist/oclif-B7lBzh3R.mjs.map +1 -0
- package/dist/{orca-CYqJP4ZJ.mjs → orca-U142Wrau.mjs} +155 -114
- package/dist/orca-U142Wrau.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-IzPVR0Pr.mjs → project-Cd0L3PtM.mjs} +2 -2
- package/dist/{project-IzPVR0Pr.mjs.map → project-Cd0L3PtM.mjs.map} +1 -1
- package/dist/{sync-Df9S8Pio.mjs → sync-BojoQm2P.mjs} +2 -2
- package/dist/{sync-Df9S8Pio.mjs.map → sync-BojoQm2P.mjs.map} +1 -1
- package/oclif.manifest.json +29 -1
- package/package.json +8 -42
- package/dist/docker--EAWr_WY.mjs +0 -210
- package/dist/docker--EAWr_WY.mjs.map +0 -1
- package/dist/oclif-2t97lHfY.mjs.map +0 -1
- package/dist/orca-CYqJP4ZJ.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -15,8 +15,8 @@ automation when reproducibility matters.
|
|
|
15
15
|
|
|
16
16
|
## Requirements
|
|
17
17
|
|
|
18
|
-
- Node
|
|
19
|
-
- Docker
|
|
18
|
+
- Node 24+
|
|
19
|
+
- Docker only when using the optional Docker runtime backend
|
|
20
20
|
- A Next.js project, or an empty directory where setup can scaffold one
|
|
21
21
|
|
|
22
22
|
## Quickstart
|
|
@@ -30,11 +30,10 @@ npx @zitadel/cli@alpha setup --server local
|
|
|
30
30
|
npm run dev
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
-
`start` runs
|
|
34
|
-
under `.zitadel/local/`.
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
`ZITADEL_LOCAL_IMAGE` for advanced debugging.
|
|
33
|
+
`start` runs the `@zitadel/server` npm binary by default and stores runtime data
|
|
34
|
+
under `.zitadel/local/`. Remote-server setup can use `--server <url>` without
|
|
35
|
+
starting a local runtime. Use `--runtime docker`, `--image`, or
|
|
36
|
+
`ZITADEL_LOCAL_IMAGE` for advanced Docker backend debugging.
|
|
38
37
|
`setup --server local` creates a project on that local server, asks which
|
|
39
38
|
framework to scaffold when the directory is fresh, writes the Next.js app into
|
|
40
39
|
the current directory, scaffolds `app/login`, `app/register`, `app/profile`, and
|
|
@@ -63,12 +62,12 @@ passkey can sign in with either credential.
|
|
|
63
62
|
|
|
64
63
|
## Other commands
|
|
65
64
|
|
|
66
|
-
- `zitadel doctor` — verify the local
|
|
67
|
-
- `zitadel status` — summarise the local
|
|
65
|
+
- `zitadel doctor` — verify the local runtime and generated project files
|
|
66
|
+
- `zitadel status` — summarise the local runtime and project
|
|
68
67
|
- `zitadel plan` — validate config and preview sync changes without mutation
|
|
69
68
|
- `zitadel apply` — validate and upload repo config to Zitadel
|
|
70
69
|
- `zitadel eject` — remove what setup wrote (alias: `zitadel uninstall`)
|
|
71
|
-
- `zitadel start|stop|logs|reset` — manage the local
|
|
70
|
+
- `zitadel start|stop|logs|reset` — manage the local runtime
|
|
72
71
|
|
|
73
72
|
## Reference
|
|
74
73
|
|
|
@@ -188,7 +187,7 @@ Verify local runtime and project state.
|
|
|
188
187
|
```
|
|
189
188
|
USAGE
|
|
190
189
|
$ zitadel doctor [--json] [-c <value>] [-s <value>] [-n] [-f] [--dry-run] [--verbose] [--debug] [--fix]
|
|
191
|
-
[--image <value>] [--port <value>]
|
|
190
|
+
[--image <value>] [--port <value>] [--runtime binary|docker]
|
|
192
191
|
|
|
193
192
|
FLAGS
|
|
194
193
|
-c, --cwd=<value> Project directory to operate on.
|
|
@@ -200,6 +199,8 @@ FLAGS
|
|
|
200
199
|
--fix Re-apply missing managed files.
|
|
201
200
|
--image=<value> Container image to check.
|
|
202
201
|
--port=<value> [default: 8080] Local HTTP port.
|
|
202
|
+
--runtime=<option> Local runtime backend.
|
|
203
|
+
<options: binary|docker>
|
|
203
204
|
--verbose Verbose logging.
|
|
204
205
|
|
|
205
206
|
GLOBAL FLAGS
|
|
@@ -394,7 +395,7 @@ Start a local Zitadel server.
|
|
|
394
395
|
```
|
|
395
396
|
USAGE
|
|
396
397
|
$ zitadel start [--json] [-c <value>] [-s <value>] [-n] [-f] [--dry-run] [--verbose] [--debug] [--image
|
|
397
|
-
<value>] [--port <value>]
|
|
398
|
+
<value>] [--port <value>] [--runtime binary|docker]
|
|
398
399
|
|
|
399
400
|
FLAGS
|
|
400
401
|
-c, --cwd=<value> Project directory to operate on.
|
|
@@ -405,6 +406,8 @@ FLAGS
|
|
|
405
406
|
--dry-run Preview without mutating files or the platform.
|
|
406
407
|
--image=<value> Container image to run.
|
|
407
408
|
--port=<value> [default: 8080] Local HTTP port.
|
|
409
|
+
--runtime=<option> Local runtime backend.
|
|
410
|
+
<options: binary|docker>
|
|
408
411
|
--verbose Verbose logging.
|
|
409
412
|
|
|
410
413
|
GLOBAL FLAGS
|
package/SKILLS.md
CHANGED
|
@@ -42,8 +42,11 @@ Each invocation prints one JSON object:
|
|
|
42
42
|
- On failure: `code` (e.g. `E_VALIDATION`, `E_NETWORK`, `E_CONFLICT`) and
|
|
43
43
|
`message`.
|
|
44
44
|
- `next_commands`: the suggested follow-ups. Prefer these over free-text hints.
|
|
45
|
-
- `E_LOCAL_SERVER_NOT_RUNNING`: start the local
|
|
45
|
+
- `E_LOCAL_SERVER_NOT_RUNNING`: start the local runtime with
|
|
46
46
|
`npx @zitadel/cli@alpha start`, then retry with `--server local`.
|
|
47
|
+
- `E_PORT_IN_USE`: the requested local runtime port already has a listener.
|
|
48
|
+
Stop that process, run `npx @zitadel/cli@alpha stop --all` for host-wide
|
|
49
|
+
CLI-managed local runtimes, or choose another `start --port`.
|
|
47
50
|
|
|
48
51
|
Exit codes mirror the error class (3 = validation, 4 = network, 5 = conflict,
|
|
49
52
|
1 = auth, 2 = not-implemented). An unknown command is handled by the CLI's help
|
|
@@ -65,25 +68,26 @@ layer, not the envelope.
|
|
|
65
68
|
- `plan` — validate config and preview the sync diff without mutating anything.
|
|
66
69
|
- `apply` — validate and upload repo config to the platform.
|
|
67
70
|
- `doctor` — verify generated app files and local state once `zitadel.json`
|
|
68
|
-
exists.
|
|
69
|
-
|
|
70
|
-
files.
|
|
71
|
-
- `status` — summarize the local
|
|
71
|
+
exists. The default local runtime is the `@zitadel/server` npm binary;
|
|
72
|
+
Docker checks apply only when using `--runtime docker` or `--image`.
|
|
73
|
+
`--fix` re-applies missing managed files.
|
|
74
|
+
- `status` — summarize the local runtime and project state.
|
|
72
75
|
- `eject` (alias `uninstall`) — remove managed files and local Zitadel state;
|
|
73
76
|
requires `--force` when non-interactive.
|
|
74
|
-
- `start` — start the managed local Zitadel
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
- `
|
|
79
|
-
-
|
|
77
|
+
- `start` — start the managed local Zitadel server and persist runtime metadata
|
|
78
|
+
under `.zitadel/local/runtime.json`. Use `--runtime docker` or `--image` for
|
|
79
|
+
the Docker backend.
|
|
80
|
+
- `stop` — stop the managed runtime while preserving
|
|
81
|
+
`.zitadel/local/nextgen-data`. Use `stop --all` to sweep all discovered
|
|
82
|
+
host-wide CLI-managed local runtime processes, including healthy runtimes
|
|
83
|
+
from other local projects; it does not kill arbitrary `/healthz` listeners.
|
|
84
|
+
- `logs` — print managed runtime logs; `--follow` streams in human mode.
|
|
85
|
+
- `reset` — stop/remove the managed runtime and delete local runtime data;
|
|
80
86
|
requires `--force` when non-interactive.
|
|
81
87
|
|
|
82
|
-
Alpha releases are
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
`ghcr.io/zitadel/nextgen:0.1.0-alpha.N`. `zitadel start --image <ref>` remains
|
|
86
|
-
the explicit image override for debugging.
|
|
88
|
+
Alpha releases are fixed product package trains. `npx @zitadel/cli@alpha start`
|
|
89
|
+
uses the matching `@zitadel/server` package by default. `zitadel start --runtime
|
|
90
|
+
docker --image <ref>` remains the explicit image override for debugging.
|
|
87
91
|
|
|
88
92
|
## Golden path
|
|
89
93
|
|
|
@@ -107,10 +111,40 @@ npx @zitadel/cli@0.1.0-alpha.N setup --framework next --server local --non-inter
|
|
|
107
111
|
After `setup`, follow `data.next_commands` to start the app. Prove the generated
|
|
108
112
|
auth flow in a visible browser by registering a unique user, logging out, logging
|
|
109
113
|
back in with the same email/password, and ending on the signed-in profile page.
|
|
110
|
-
Do not treat a rendered login or registration form as completion.
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
+
Do not treat a rendered login or registration form as completion.
|
|
115
|
+
|
|
116
|
+
### Driving the login UI
|
|
117
|
+
|
|
118
|
+
`<zitadel-login>` and `<zitadel-logout>` are Lit elements with open shadow
|
|
119
|
+
roots. The stable automation hooks live inside nested shadow roots, so a flat
|
|
120
|
+
`document.querySelector('[data-testid="zitadel-field-email-input"]')` will not
|
|
121
|
+
find them. Browser drivers with shadow-DOM-aware locators, such as Playwright,
|
|
122
|
+
can target the hooks directly. Generic DOM-eval drivers should pierce shadow
|
|
123
|
+
roots recursively:
|
|
124
|
+
|
|
125
|
+
```js
|
|
126
|
+
function deepQuery(sel, root = document) {
|
|
127
|
+
const hit = root.querySelector(sel);
|
|
128
|
+
if (hit) return hit;
|
|
129
|
+
for (const el of root.querySelectorAll("*")) {
|
|
130
|
+
if (el.shadowRoot) {
|
|
131
|
+
const result = deepQuery(sel, el.shadowRoot);
|
|
132
|
+
if (result) return result;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Use `zitadel-field-email-input`, `zitadel-field-password-input`, and
|
|
140
|
+
`zitadel-action-submit-button` for sign-in and registration. For sign-out, open
|
|
141
|
+
the user menu button if needed, then pierce to `.signout-btn`; Playwright-style
|
|
142
|
+
locators may use `zitadel-logout .signout-btn`. The canonical component hook
|
|
143
|
+
list lives in `packages/components/README.md`.
|
|
144
|
+
|
|
145
|
+
The checked-in automated regression path is `moon run workspace:journey`, which
|
|
146
|
+
exercises fresh-app setup plus registration, logout, and login across the
|
|
147
|
+
supported frameworks.
|
|
114
148
|
|
|
115
149
|
Repo config is authoritative: edit `zitadel.json` or files under `.zitadel/`,
|
|
116
150
|
then re-run `plan` and `apply`. Managed files carry a marker comment; `eject`
|
package/dist/commands/apply.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { t as BaseCommand } from "../oclif-
|
|
2
|
-
import { o as readZitadelSecret } from "../project-
|
|
3
|
-
import { a as makeSyncers, i as runSyncLoop, n as summarizePlan, o as environmentSchema, r as buildSyncPlan, t as renderPlan } from "../sync-
|
|
1
|
+
import { t as BaseCommand } from "../oclif-B7lBzh3R.mjs";
|
|
2
|
+
import { o as readZitadelSecret } from "../project-Cd0L3PtM.mjs";
|
|
3
|
+
import { a as makeSyncers, i as runSyncLoop, n as summarizePlan, o as environmentSchema, r as buildSyncPlan, t as renderPlan } from "../sync-BojoQm2P.mjs";
|
|
4
4
|
import { Flags } from "@oclif/core";
|
|
5
5
|
import { createZitadelClient } from "@zitadel/api/client";
|
|
6
6
|
import { consola as consola$1 } from "consola";
|
package/dist/commands/doctor.mjs
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { a as readZitadelConfig, i as readRendererId, o as readZitadelSecret, r as readDevelopmentIssuer, t as hasZitadelConfig } from "../project-
|
|
4
|
-
import {
|
|
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-B7lBzh3R.mjs";
|
|
2
|
+
import { i as issuerFromPort, t as createOrca } from "../orca-U142Wrau.mjs";
|
|
3
|
+
import { a as readZitadelConfig, i as readRendererId, o as readZitadelSecret, r as readDevelopmentIssuer, t as hasZitadelConfig } from "../project-Cd0L3PtM.mjs";
|
|
4
|
+
import { n as listenersForPort } from "../ports-B09RjuHx.mjs";
|
|
5
|
+
import { d as assertServerPackageAvailable, o as imageAvailable, r as dockerAvailable } from "../docker-CnGQK3ZK.mjs";
|
|
6
|
+
import { n as dockerUnavailableMessage, t as dockerRuntimeGuidance } from "../docker-guidance-ypN3IM3o.mjs";
|
|
7
|
+
import { t as discoverManagedRuntimeProcesses } from "../processes-Cw8TO1SY.mjs";
|
|
5
8
|
import { Flags } from "@oclif/core";
|
|
6
9
|
import consola from "consola";
|
|
7
10
|
import { join } from "node:path";
|
|
@@ -247,6 +250,7 @@ const SANITY_CHECKS = [
|
|
|
247
250
|
//#endregion
|
|
248
251
|
//#region src/commands/doctor/index.ts
|
|
249
252
|
const LOCAL_RUNTIME_CHECK_NAMES = new Set([
|
|
253
|
+
"server-binary",
|
|
250
254
|
"docker-cli",
|
|
251
255
|
"image",
|
|
252
256
|
"state-dir",
|
|
@@ -274,6 +278,10 @@ var Doctor = class Doctor extends BaseCommand {
|
|
|
274
278
|
port: Flags.integer({
|
|
275
279
|
description: "Local HTTP port.",
|
|
276
280
|
default: DEFAULT_LOCAL_SERVER_PORT
|
|
281
|
+
}),
|
|
282
|
+
runtime: Flags.string({
|
|
283
|
+
description: "Local runtime backend.",
|
|
284
|
+
options: ["binary", "docker"]
|
|
277
285
|
})
|
|
278
286
|
};
|
|
279
287
|
async run() {
|
|
@@ -284,8 +292,15 @@ var Doctor = class Doctor extends BaseCommand {
|
|
|
284
292
|
source: localServerUrl(port)
|
|
285
293
|
});
|
|
286
294
|
const { cwd, dryRun } = this.meta;
|
|
295
|
+
const existingRuntime = await readRuntimeMetadata(cwd);
|
|
296
|
+
const runtimeBackend = resolveRuntimeBackend({
|
|
297
|
+
runtime: flags.runtime,
|
|
298
|
+
image: flags.image,
|
|
299
|
+
envImage: this.meta.env.ZITADEL_LOCAL_IMAGE,
|
|
300
|
+
existingRuntime
|
|
301
|
+
});
|
|
287
302
|
const image = flags.image ?? this.meta.env.ZITADEL_LOCAL_IMAGE ?? defaultLocalServerImageForCliVersion(this.meta.cliVersion);
|
|
288
|
-
const runtimeChecks = await runLocalRuntimeChecks(cwd, image, port);
|
|
303
|
+
const runtimeChecks = await runLocalRuntimeChecks(cwd, runtimeBackend, image, port);
|
|
289
304
|
const hasConfig = await hasZitadelConfig(cwd);
|
|
290
305
|
const ctx = {
|
|
291
306
|
cwd,
|
|
@@ -308,17 +323,23 @@ var Doctor = class Doctor extends BaseCommand {
|
|
|
308
323
|
const checks = [...runtimeChecks, ...projectChecks];
|
|
309
324
|
const failed = checks.filter((check) => check.status === "fail");
|
|
310
325
|
const warnings = checks.filter((check) => check.status === "warn");
|
|
326
|
+
const warningAdvice = advisoryForWarnings(warnings, this.meta.cliVersion);
|
|
311
327
|
const data = {
|
|
312
328
|
title: failed.length > 0 ? "Zitadel doctor found issues." : warnings.length > 0 ? "Zitadel doctor passed with warnings." : "Zitadel doctor passed.",
|
|
313
329
|
ok: failed.length === 0,
|
|
314
|
-
|
|
330
|
+
runtime: runtimeBackend,
|
|
331
|
+
...runtimeBackend === "docker" ? { image } : {},
|
|
315
332
|
port,
|
|
316
333
|
project: { lifecycle: hasConfig ? "configured" : "not-configured" },
|
|
317
|
-
checks
|
|
334
|
+
checks,
|
|
335
|
+
...warningAdvice ? {
|
|
336
|
+
next_actions: warningAdvice.nextActions,
|
|
337
|
+
next_commands: warningAdvice.nextCommands
|
|
338
|
+
} : {}
|
|
318
339
|
};
|
|
319
340
|
if (failed.length > 0) {
|
|
320
341
|
const advice = failureAdvice(failed, image, port, this.meta.cliVersion);
|
|
321
|
-
throw new ZitadelError("E_VALIDATION", "Zitadel doctor found issues", {
|
|
342
|
+
throw new ZitadelError(failed.some((check) => check.name === "port") ? "E_PORT_IN_USE" : "E_VALIDATION", "Zitadel doctor found issues", {
|
|
322
343
|
hint: advice.hint,
|
|
323
344
|
nextCommands: advice.nextCommands,
|
|
324
345
|
details: data
|
|
@@ -333,10 +354,17 @@ var Doctor = class Doctor extends BaseCommand {
|
|
|
333
354
|
};
|
|
334
355
|
function failureAdvice(failed, image, port, cliVersion) {
|
|
335
356
|
const failedNames = new Set(failed.map((check) => check.name));
|
|
336
|
-
if (failedNames.has("
|
|
337
|
-
hint: "
|
|
338
|
-
nextCommands: [
|
|
357
|
+
if (failedNames.has("server-binary")) return {
|
|
358
|
+
hint: "The Zitadel server npm package is not available. Reinstall the CLI package, then retry.",
|
|
359
|
+
nextCommands: [publicCliCommand("doctor", cliVersion)]
|
|
339
360
|
};
|
|
361
|
+
if (failedNames.has("docker-cli")) {
|
|
362
|
+
const advice = dockerRuntimeGuidance("doctor", cliVersion);
|
|
363
|
+
return {
|
|
364
|
+
hint: advice.hint,
|
|
365
|
+
nextCommands: advice.nextCommands
|
|
366
|
+
};
|
|
367
|
+
}
|
|
340
368
|
if (failedNames.has("image")) return {
|
|
341
369
|
hint: "The local Zitadel image is not available. Check Docker registry access, build it locally, or pass --image / ZITADEL_LOCAL_IMAGE.",
|
|
342
370
|
nextCommands: [`docker pull ${image}`, publicCliCommand("doctor", cliVersion)]
|
|
@@ -365,8 +393,39 @@ function failureAdvice(failed, image, port, cliVersion) {
|
|
|
365
393
|
nextCommands: [publicCliCommand("doctor", cliVersion)]
|
|
366
394
|
};
|
|
367
395
|
}
|
|
368
|
-
|
|
396
|
+
function advisoryForWarnings(warnings, cliVersion) {
|
|
397
|
+
const nextActions = [];
|
|
398
|
+
const nextCommands = [];
|
|
399
|
+
if (warnings.some((check) => check.name === "docker-cli")) {
|
|
400
|
+
const advice = dockerRuntimeGuidance("doctor", cliVersion);
|
|
401
|
+
nextActions.push(...advice.nextActions);
|
|
402
|
+
nextCommands.push(...advice.nextCommands);
|
|
403
|
+
}
|
|
404
|
+
if (hasManagedRuntimeProcesses(warnings.find((check) => check.name === "managed-runtime-processes"))) {
|
|
405
|
+
nextActions.push("Review other host-wide CLI-managed local Zitadel runtimes before starting a new one.");
|
|
406
|
+
nextCommands.push(publicCliCommand("stop --all", cliVersion));
|
|
407
|
+
}
|
|
408
|
+
if (nextActions.length === 0 && nextCommands.length === 0) return;
|
|
409
|
+
return {
|
|
410
|
+
nextActions: unique(nextActions),
|
|
411
|
+
nextCommands: unique(nextCommands)
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
async function runLocalRuntimeChecks(cwd, runtimeBackend, image, port) {
|
|
369
415
|
const runtime = await readRuntimeMetadata(cwd);
|
|
416
|
+
const managedRuntimeCheck = await checkManagedRuntimeProcesses(runtime);
|
|
417
|
+
if (runtimeBackend === "binary") return [
|
|
418
|
+
await check("server-binary", "Server npm package is available", async () => {
|
|
419
|
+
return `@zitadel/server ${await assertServerPackageAvailable()} is available`;
|
|
420
|
+
}),
|
|
421
|
+
await check("state-dir", "Local state directory is writable", async () => {
|
|
422
|
+
const probe = await assertLocalStateWritable(cwd);
|
|
423
|
+
return probe.checkedPath === probe.targetPath ? `${probe.targetPath} is writable` : `${probe.targetPath} can be created (${probe.checkedPath} is writable)`;
|
|
424
|
+
}, "warn"),
|
|
425
|
+
await check("port", `Port ${String(port)} is available`, () => checkPortAvailability(runtime, port)),
|
|
426
|
+
await checkRuntime(runtime, runtimeBackend),
|
|
427
|
+
managedRuntimeCheck
|
|
428
|
+
];
|
|
370
429
|
const docker = await check("docker-cli", "Docker is reachable", async () => {
|
|
371
430
|
let result;
|
|
372
431
|
try {
|
|
@@ -394,18 +453,87 @@ async function runLocalRuntimeChecks(cwd, image, port) {
|
|
|
394
453
|
const probe = await assertLocalStateWritable(cwd);
|
|
395
454
|
return probe.checkedPath === probe.targetPath ? `${probe.targetPath} is writable` : `${probe.targetPath} can be created (${probe.checkedPath} is writable)`;
|
|
396
455
|
}, "warn"),
|
|
397
|
-
await check("port", `Port ${String(port)} is available`,
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
return `Port ${String(port)} is available`;
|
|
401
|
-
}, "warn"),
|
|
402
|
-
await check("runtime", "Existing local runtime is healthy", async () => {
|
|
403
|
-
if (!runtime) return "No existing runtime metadata";
|
|
404
|
-
if (!await checkLocalServerHealth(runtime.server_url)) throw new Error(`${runtime.server_url} did not respond to /healthz`);
|
|
405
|
-
return `${runtime.server_url} is healthy`;
|
|
406
|
-
})
|
|
456
|
+
await check("port", `Port ${String(port)} is available`, () => checkPortAvailability(runtime, port)),
|
|
457
|
+
await checkRuntime(runtime, runtimeBackend),
|
|
458
|
+
managedRuntimeCheck
|
|
407
459
|
];
|
|
408
460
|
}
|
|
461
|
+
async function checkPortAvailability(runtime, port) {
|
|
462
|
+
if (runtime?.port === port && await checkLocalServerHealth(runtime.server_url)) return `${runtime.server_url} is already healthy`;
|
|
463
|
+
const listeners = await listenersForPort(port);
|
|
464
|
+
if (listeners.length > 0) throw new PortInUseCheckError(port, localServerUrl(port), listeners);
|
|
465
|
+
return `Port ${String(port)} is available`;
|
|
466
|
+
}
|
|
467
|
+
var PortInUseCheckError = class extends Error {
|
|
468
|
+
constructor(port, serverUrl, listeners) {
|
|
469
|
+
super(`Port ${String(port)} is already in use by ${formatListeners(listeners)}`);
|
|
470
|
+
this.port = port;
|
|
471
|
+
this.serverUrl = serverUrl;
|
|
472
|
+
this.listeners = listeners;
|
|
473
|
+
}
|
|
474
|
+
};
|
|
475
|
+
async function checkManagedRuntimeProcesses(runtime) {
|
|
476
|
+
const discovery = await discoverManagedRuntimeProcesses();
|
|
477
|
+
if (!discovery.supported) return {
|
|
478
|
+
name: "managed-runtime-processes",
|
|
479
|
+
status: "warn",
|
|
480
|
+
message: "Managed local runtime process discovery is unavailable.",
|
|
481
|
+
details: {
|
|
482
|
+
supported: false,
|
|
483
|
+
error: discovery.error
|
|
484
|
+
}
|
|
485
|
+
};
|
|
486
|
+
const processes = additionalManagedRuntimeProcesses(discovery.processes, runtime);
|
|
487
|
+
if (processes.length === 0) return {
|
|
488
|
+
name: "managed-runtime-processes",
|
|
489
|
+
status: "pass",
|
|
490
|
+
message: "No additional host-wide managed local runtime processes found.",
|
|
491
|
+
details: {
|
|
492
|
+
supported: true,
|
|
493
|
+
scope: "host",
|
|
494
|
+
processes: []
|
|
495
|
+
}
|
|
496
|
+
};
|
|
497
|
+
return {
|
|
498
|
+
name: "managed-runtime-processes",
|
|
499
|
+
status: "warn",
|
|
500
|
+
message: `${String(processes.length)} other host-wide managed local runtime process${processes.length === 1 ? "" : "es"} found.`,
|
|
501
|
+
details: {
|
|
502
|
+
supported: true,
|
|
503
|
+
scope: "host",
|
|
504
|
+
processes
|
|
505
|
+
}
|
|
506
|
+
};
|
|
507
|
+
}
|
|
508
|
+
function additionalManagedRuntimeProcesses(processes, runtime) {
|
|
509
|
+
if (runtime?.backend !== "binary") return processes;
|
|
510
|
+
return processes.filter((processInfo) => processInfo.pid !== runtime.pid && processInfo.ppid !== runtime.pid);
|
|
511
|
+
}
|
|
512
|
+
async function checkRuntime(runtime, runtimeBackend) {
|
|
513
|
+
return check("runtime", "Existing local runtime is healthy", async () => {
|
|
514
|
+
if (!runtime) return "No existing runtime metadata";
|
|
515
|
+
if (runtime.backend !== runtimeBackend) throw new Error(`Existing local runtime uses ${runtime.backend}; run start --runtime ${runtimeBackend} to switch backends.`);
|
|
516
|
+
if (!await checkLocalServerHealth(runtime.server_url)) throw new Error(`${runtime.server_url} did not respond to /healthz`);
|
|
517
|
+
return `${runtime.server_url} is healthy`;
|
|
518
|
+
});
|
|
519
|
+
}
|
|
520
|
+
function formatListeners(listeners) {
|
|
521
|
+
return listeners.map((listener) => [listener.command ?? "unknown", listener.pid ? `pid ${String(listener.pid)}` : void 0].filter(Boolean).join(" ")).join(", ");
|
|
522
|
+
}
|
|
523
|
+
function hasManagedRuntimeProcesses(check) {
|
|
524
|
+
if (!check || check.status !== "warn") return false;
|
|
525
|
+
const details = check.details;
|
|
526
|
+
return typeof details === "object" && details !== null && "supported" in details && details.supported === true && Array.isArray(details.processes) && (details.processes?.length ?? 0) > 0;
|
|
527
|
+
}
|
|
528
|
+
function unique(values) {
|
|
529
|
+
return [...new Set(values)];
|
|
530
|
+
}
|
|
531
|
+
function resolveRuntimeBackend(input) {
|
|
532
|
+
if (input.runtime === "binary" || input.runtime === "docker") return input.runtime;
|
|
533
|
+
if (input.existingRuntime) return input.existingRuntime.backend;
|
|
534
|
+
if (input.image || input.envImage) return "docker";
|
|
535
|
+
return "binary";
|
|
536
|
+
}
|
|
409
537
|
async function check(name, fallback, run, failureStatus = "fail") {
|
|
410
538
|
try {
|
|
411
539
|
return {
|
|
@@ -414,6 +542,16 @@ async function check(name, fallback, run, failureStatus = "fail") {
|
|
|
414
542
|
message: await run()
|
|
415
543
|
};
|
|
416
544
|
} catch (error) {
|
|
545
|
+
if (error instanceof PortInUseCheckError) return {
|
|
546
|
+
name,
|
|
547
|
+
status: failureStatus,
|
|
548
|
+
message: error.message,
|
|
549
|
+
details: {
|
|
550
|
+
port: error.port,
|
|
551
|
+
server_url: error.serverUrl,
|
|
552
|
+
listeners: error.listeners
|
|
553
|
+
}
|
|
554
|
+
};
|
|
417
555
|
return {
|
|
418
556
|
name,
|
|
419
557
|
status: failureStatus,
|
|
@@ -421,10 +559,6 @@ async function check(name, fallback, run, failureStatus = "fail") {
|
|
|
421
559
|
};
|
|
422
560
|
}
|
|
423
561
|
}
|
|
424
|
-
function dockerUnavailableMessage(error) {
|
|
425
|
-
const detail = error instanceof Error ? error.message : String(error);
|
|
426
|
-
return `Docker is not reachable${detail.trim() ? ` (${detail.trim()})` : ""}; \`zitadel start\` needs Docker, but cloud setup can continue.`;
|
|
427
|
-
}
|
|
428
562
|
function imageUnavailableMessage(image, error) {
|
|
429
563
|
const detail = error instanceof Error ? error.message : String(error);
|
|
430
564
|
return `Image ${image} is not available to Docker${detail.trim() ? ` (${detail.trim()})` : ""}; \`zitadel start\` may need a pull or a different image.`;
|