@zitadel/cli 0.1.0-alpha.0 → 0.1.0-alpha.2
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 +158 -24
- package/SKILLS.md +40 -8
- package/dist/commands/apply.mjs +4 -3
- package/dist/commands/apply.mjs.map +1 -1
- package/dist/commands/doctor.mjs +150 -16
- package/dist/commands/doctor.mjs.map +1 -1
- package/dist/commands/eject.mjs +4 -3
- package/dist/commands/eject.mjs.map +1 -1
- package/dist/commands/logs.mjs +50 -0
- package/dist/commands/logs.mjs.map +1 -0
- package/dist/commands/plan.mjs +4 -3
- package/dist/commands/plan.mjs.map +1 -1
- package/dist/commands/reset.mjs +62 -0
- package/dist/commands/reset.mjs.map +1 -0
- package/dist/commands/setup.mjs +189 -23
- package/dist/commands/setup.mjs.map +1 -1
- package/dist/commands/start.mjs +128 -0
- package/dist/commands/start.mjs.map +1 -0
- package/dist/commands/status.mjs +74 -27
- package/dist/commands/status.mjs.map +1 -1
- package/dist/commands/stop.mjs +43 -0
- package/dist/commands/stop.mjs.map +1 -0
- package/dist/docker-DrOBycgG.mjs +210 -0
- package/dist/docker-DrOBycgG.mjs.map +1 -0
- package/dist/{project-C3pSfbao.mjs → oclif-CqkWBQ7i.mjs} +300 -119
- package/dist/oclif-CqkWBQ7i.mjs.map +1 -0
- package/dist/{orca-COsUnVoz.mjs → orca-S1tVnngd.mjs} +41 -12
- package/dist/{orca-COsUnVoz.mjs.map → orca-S1tVnngd.mjs.map} +1 -1
- package/dist/project-B6YfSaZw.mjs +87 -0
- package/dist/project-B6YfSaZw.mjs.map +1 -0
- package/dist/{sync-Cuyh-X1J.mjs → sync-DN4oNLVH.mjs} +4 -4
- package/dist/{sync-Cuyh-X1J.mjs.map → sync-DN4oNLVH.mjs.map} +1 -1
- package/oclif.manifest.json +357 -3
- package/package.json +3 -3
- package/dist/project-C3pSfbao.mjs.map +0 -1
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { E as ZitadelError, a as DEFAULT_LOCAL_SERVER_PORT, b as writeRuntimeMetadata, c as checkLocalServerHealth, d as ensureLocalState, h as localServerUrl, l as defaultLocalServerImageForCliVersion, p as localContainerName, t as BaseCommand, u as ensureContainerIdentity, x as publicCliCommand } from "../oclif-CqkWBQ7i.mjs";
|
|
2
|
+
import { c as metadataFromStart, i as ensureImage, l as startContainer, n as currentUser, s as inspectContainer, u as stopAndRemoveContainer } from "../docker-DrOBycgG.mjs";
|
|
3
|
+
import { Flags } from "@oclif/core";
|
|
4
|
+
import { setTimeout } from "node:timers/promises";
|
|
5
|
+
//#region src/commands/start.ts
|
|
6
|
+
const START_TIMEOUT_MS = 9e4;
|
|
7
|
+
var Start = class Start extends BaseCommand {
|
|
8
|
+
static description = "Start a local Zitadel server.";
|
|
9
|
+
static flags = {
|
|
10
|
+
image: Flags.string({ description: "Container image to run." }),
|
|
11
|
+
port: Flags.integer({
|
|
12
|
+
description: "Local HTTP port.",
|
|
13
|
+
default: DEFAULT_LOCAL_SERVER_PORT
|
|
14
|
+
})
|
|
15
|
+
};
|
|
16
|
+
async run() {
|
|
17
|
+
const { flags } = await this.parse(Start);
|
|
18
|
+
const port = flags.port ?? 8080;
|
|
19
|
+
const serverUrl = localServerUrl(port);
|
|
20
|
+
await this.toMeta(flags, {
|
|
21
|
+
resolveServer: false,
|
|
22
|
+
source: serverUrl
|
|
23
|
+
});
|
|
24
|
+
validatePort(port);
|
|
25
|
+
const image = flags.image ?? this.meta.env.ZITADEL_LOCAL_IMAGE ?? defaultLocalServerImageForCliVersion(this.meta.cliVersion);
|
|
26
|
+
const containerName = localContainerName(this.meta.cwd);
|
|
27
|
+
if (this.meta.dryRun) return this.emit({
|
|
28
|
+
status: "ok",
|
|
29
|
+
data: {
|
|
30
|
+
title: "Local Zitadel server start plan.",
|
|
31
|
+
runtime: {
|
|
32
|
+
container_name: containerName,
|
|
33
|
+
image,
|
|
34
|
+
port
|
|
35
|
+
},
|
|
36
|
+
urls: {
|
|
37
|
+
api: serverUrl,
|
|
38
|
+
console: `${serverUrl}/ui/console/`,
|
|
39
|
+
login: `${serverUrl}/ui/login/`
|
|
40
|
+
},
|
|
41
|
+
next_commands: [publicCliCommand("start", this.meta.cliVersion)]
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
const paths = await ensureLocalState(this.meta.cwd);
|
|
45
|
+
const existing = await inspectContainer(containerName);
|
|
46
|
+
if (existing.exists && existing.running && existing.image === image && await checkLocalServerHealth(serverUrl)) {
|
|
47
|
+
const metadata = metadataFromStart({
|
|
48
|
+
cwdDataDir: paths.dataDir,
|
|
49
|
+
cliVersion: this.meta.cliVersion,
|
|
50
|
+
containerName,
|
|
51
|
+
containerId: existing.id ?? containerName,
|
|
52
|
+
image,
|
|
53
|
+
port,
|
|
54
|
+
serverUrl
|
|
55
|
+
});
|
|
56
|
+
await writeRuntimeMetadata(this.meta.cwd, metadata);
|
|
57
|
+
return this.emit({
|
|
58
|
+
status: "ok",
|
|
59
|
+
data: readyData(metadata, true, this.meta.cliVersion)
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
if (existing.exists) await stopAndRemoveContainer(containerName);
|
|
63
|
+
await ensureImage(image);
|
|
64
|
+
const containerId = await startContainer({
|
|
65
|
+
containerName,
|
|
66
|
+
image,
|
|
67
|
+
port,
|
|
68
|
+
dataDir: paths.dataDir,
|
|
69
|
+
identity: await ensureContainerIdentity(this.meta.cwd, currentUser())
|
|
70
|
+
});
|
|
71
|
+
await waitForHealth(serverUrl, containerName, this.meta.cliVersion);
|
|
72
|
+
const metadata = metadataFromStart({
|
|
73
|
+
cwdDataDir: paths.dataDir,
|
|
74
|
+
cliVersion: this.meta.cliVersion,
|
|
75
|
+
containerName,
|
|
76
|
+
containerId,
|
|
77
|
+
image,
|
|
78
|
+
port,
|
|
79
|
+
serverUrl
|
|
80
|
+
});
|
|
81
|
+
await writeRuntimeMetadata(this.meta.cwd, metadata);
|
|
82
|
+
return this.emit({
|
|
83
|
+
status: "ok",
|
|
84
|
+
data: readyData(metadata, false, this.meta.cliVersion)
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
function readyData(metadata, alreadyRunning, cliVersion) {
|
|
89
|
+
return {
|
|
90
|
+
title: alreadyRunning ? "Local Zitadel server is already running." : "Local Zitadel server is ready.",
|
|
91
|
+
runtime: {
|
|
92
|
+
container_name: metadata.container_name,
|
|
93
|
+
container_id: metadata.container_id,
|
|
94
|
+
image: metadata.image,
|
|
95
|
+
port: metadata.port,
|
|
96
|
+
data_dir: metadata.data_dir
|
|
97
|
+
},
|
|
98
|
+
urls: {
|
|
99
|
+
api: metadata.server_url,
|
|
100
|
+
console: `${metadata.server_url}/ui/console/`,
|
|
101
|
+
login: `${metadata.server_url}/ui/login/`
|
|
102
|
+
},
|
|
103
|
+
next_actions: ["From your app directory, run setup; the CLI will detect the framework or ask when needed.", "Setup installs dependencies when needed; then start your app dev server."],
|
|
104
|
+
next_commands: [publicCliCommand("setup --server local", cliVersion)]
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
async function waitForHealth(serverUrl, containerName, cliVersion) {
|
|
108
|
+
const started = Date.now();
|
|
109
|
+
while (Date.now() - started < START_TIMEOUT_MS) {
|
|
110
|
+
if (await checkLocalServerHealth(serverUrl, 1e3)) return;
|
|
111
|
+
await setTimeout(1e3);
|
|
112
|
+
}
|
|
113
|
+
throw new ZitadelError("E_NETWORK", "Local Zitadel server did not become healthy", {
|
|
114
|
+
hint: "Inspect the container logs, then reset the local runtime if needed.",
|
|
115
|
+
nextCommands: [publicCliCommand("logs", cliVersion), publicCliCommand("reset --force", cliVersion)],
|
|
116
|
+
details: {
|
|
117
|
+
container_name: containerName,
|
|
118
|
+
server_url: serverUrl
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
function validatePort(port) {
|
|
123
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535) throw new ZitadelError("E_VALIDATION", `Invalid port ${String(port)}`, { hint: "Use a TCP port between 1 and 65535." });
|
|
124
|
+
}
|
|
125
|
+
//#endregion
|
|
126
|
+
export { Start as default };
|
|
127
|
+
|
|
128
|
+
//# sourceMappingURL=start.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"start.mjs","names":["sleep"],"sources":["../../src/commands/start.ts"],"sourcesContent":["import { setTimeout as sleep } from \"node:timers/promises\";\n\nimport { Flags } from \"@oclif/core\";\n\nimport { ZitadelError } from \"../lib/errors\";\nimport {\n currentUser,\n ensureImage,\n inspectContainer,\n metadataFromStart,\n startContainer,\n stopAndRemoveContainer,\n} from \"../lib/local-server/docker\";\nimport {\n DEFAULT_LOCAL_SERVER_PORT,\n checkLocalServerHealth,\n defaultLocalServerImageForCliVersion,\n ensureContainerIdentity,\n ensureLocalState,\n localContainerName,\n localServerUrl,\n writeRuntimeMetadata,\n} from \"../lib/local-server/runtime\";\nimport { BaseCommand, type JsonEnvelope } from \"../lib/oclif\";\nimport { publicCliCommand } from \"../lib/public-cli\";\n\nconst START_TIMEOUT_MS = 90_000;\n\nexport default class Start extends BaseCommand {\n static override description = \"Start a local Zitadel server.\";\n static override flags = {\n image: Flags.string({ description: \"Container image to run.\" }),\n port: Flags.integer({ description: \"Local HTTP port.\", default: DEFAULT_LOCAL_SERVER_PORT }),\n };\n\n async run(): Promise<JsonEnvelope> {\n const { flags } = await this.parse(Start);\n const port = flags.port ?? DEFAULT_LOCAL_SERVER_PORT;\n const serverUrl = localServerUrl(port);\n await this.toMeta(flags, { resolveServer: false, source: serverUrl });\n\n validatePort(port);\n const image =\n flags.image ??\n this.meta.env.ZITADEL_LOCAL_IMAGE ??\n defaultLocalServerImageForCliVersion(this.meta.cliVersion);\n const containerName = localContainerName(this.meta.cwd);\n if (this.meta.dryRun) {\n return this.emit({\n status: \"ok\",\n data: {\n title: \"Local Zitadel server start plan.\",\n runtime: {\n container_name: containerName,\n image,\n port,\n },\n urls: {\n api: serverUrl,\n console: `${serverUrl}/ui/console/`,\n login: `${serverUrl}/ui/login/`,\n },\n next_commands: [publicCliCommand(\"start\", this.meta.cliVersion)],\n },\n });\n }\n\n const paths = await ensureLocalState(this.meta.cwd);\n\n const existing = await inspectContainer(containerName);\n if (\n existing.exists &&\n existing.running &&\n existing.image === image &&\n (await checkLocalServerHealth(serverUrl))\n ) {\n const metadata = metadataFromStart({\n cwdDataDir: paths.dataDir,\n cliVersion: this.meta.cliVersion,\n containerName,\n containerId: existing.id ?? containerName,\n image,\n port,\n serverUrl,\n });\n await writeRuntimeMetadata(this.meta.cwd, metadata);\n return this.emit({\n status: \"ok\",\n data: readyData(metadata, true, this.meta.cliVersion),\n });\n }\n\n if (existing.exists) {\n await stopAndRemoveContainer(containerName);\n }\n\n await ensureImage(image);\n const containerId = await startContainer({\n containerName,\n image,\n port,\n dataDir: paths.dataDir,\n identity: await ensureContainerIdentity(this.meta.cwd, currentUser()),\n });\n await waitForHealth(serverUrl, containerName, this.meta.cliVersion);\n\n const metadata = metadataFromStart({\n cwdDataDir: paths.dataDir,\n cliVersion: this.meta.cliVersion,\n containerName,\n containerId,\n image,\n port,\n serverUrl,\n });\n await writeRuntimeMetadata(this.meta.cwd, metadata);\n return this.emit({\n status: \"ok\",\n data: readyData(metadata, false, this.meta.cliVersion),\n });\n }\n}\n\nfunction readyData(\n metadata: ReturnType<typeof metadataFromStart>,\n alreadyRunning: boolean,\n cliVersion: string,\n) {\n return {\n title: alreadyRunning\n ? \"Local Zitadel server is already running.\"\n : \"Local Zitadel server is ready.\",\n runtime: {\n container_name: metadata.container_name,\n container_id: metadata.container_id,\n image: metadata.image,\n port: metadata.port,\n data_dir: metadata.data_dir,\n },\n urls: {\n api: metadata.server_url,\n console: `${metadata.server_url}/ui/console/`,\n login: `${metadata.server_url}/ui/login/`,\n },\n next_actions: [\n \"From your app directory, run setup; the CLI will detect the framework or ask when needed.\",\n \"Setup installs dependencies when needed; then start your app dev server.\",\n ],\n next_commands: [publicCliCommand(\"setup --server local\", cliVersion)],\n };\n}\n\nasync function waitForHealth(\n serverUrl: string,\n containerName: string,\n cliVersion: string,\n): Promise<void> {\n const started = Date.now();\n while (Date.now() - started < START_TIMEOUT_MS) {\n if (await checkLocalServerHealth(serverUrl, 1000)) {\n return;\n }\n await sleep(1000);\n }\n throw new ZitadelError(\"E_NETWORK\", \"Local Zitadel server did not become healthy\", {\n hint: \"Inspect the container logs, then reset the local runtime if needed.\",\n nextCommands: [\n publicCliCommand(\"logs\", cliVersion),\n publicCliCommand(\"reset --force\", cliVersion),\n ],\n details: { container_name: containerName, server_url: serverUrl },\n });\n}\n\nfunction validatePort(port: number): void {\n if (!Number.isInteger(port) || port < 1 || port > 65_535) {\n throw new ZitadelError(\"E_VALIDATION\", `Invalid port ${String(port)}`, {\n hint: \"Use a TCP port between 1 and 65535.\",\n });\n }\n}\n"],"mappings":"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"}
|
package/dist/commands/status.mjs
CHANGED
|
@@ -1,46 +1,93 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { c as checkLocalServerHealth, g as readRuntimeMetadata, p as localContainerName, t as BaseCommand, x as publicCliCommand, y as runtimeSummary } from "../oclif-CqkWBQ7i.mjs";
|
|
2
|
+
import { a as readZitadelConfig, n as hasZitadelSecret, o as readZitadelSecret, r as readDevelopmentIssuer, t as hasZitadelConfig } from "../project-B6YfSaZw.mjs";
|
|
3
|
+
import { s as inspectContainer } from "../docker-DrOBycgG.mjs";
|
|
2
4
|
//#region src/commands/status.ts
|
|
3
5
|
/**
|
|
4
|
-
* `zitadel status` — summarize the local project state.
|
|
6
|
+
* `zitadel status` — summarize the local server and project state.
|
|
5
7
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* run on partial or broken setups.
|
|
8
|
+
* This is intentionally informational: a missing `zitadel.json`, stopped
|
|
9
|
+
* local server, or orphaned config should be visible without making `status`
|
|
10
|
+
* itself fail.
|
|
10
11
|
*/
|
|
11
12
|
var Status = class Status extends BaseCommand {
|
|
12
|
-
static description = "Summarize the local project state.";
|
|
13
|
+
static description = "Summarize the local Zitadel server and project state.";
|
|
13
14
|
async run() {
|
|
14
15
|
const { flags } = await this.parse(Status);
|
|
15
|
-
await
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
status: "skipped",
|
|
20
|
-
reason: "orphaned-config",
|
|
21
|
-
data: {
|
|
22
|
-
project_id: typeof config.project === "string" ? config.project : void 0,
|
|
23
|
-
lifecycle: "orphaned-config",
|
|
24
|
-
message: "zitadel.json exists but .zitadel/secret is missing."
|
|
25
|
-
},
|
|
26
|
-
nextCommands: ["zitadel setup --force", "zitadel doctor --fix"]
|
|
16
|
+
const runtime = await readRuntimeMetadata(flags.cwd ? String(flags.cwd) : process.cwd());
|
|
17
|
+
if (flags.server === "local") await this.toMeta(flags, {
|
|
18
|
+
resolveServer: false,
|
|
19
|
+
source: runtime?.server_url ?? "http://localhost:8080"
|
|
27
20
|
});
|
|
28
|
-
|
|
21
|
+
else await this.toMeta(flags);
|
|
22
|
+
const { cwd } = this.meta;
|
|
23
|
+
const containerName = runtime?.container_name ?? localContainerName(cwd);
|
|
24
|
+
let docker;
|
|
25
|
+
let dockerError;
|
|
26
|
+
try {
|
|
27
|
+
docker = await inspectContainer(containerName);
|
|
28
|
+
} catch (error) {
|
|
29
|
+
dockerError = error instanceof Error ? error.message : String(error);
|
|
30
|
+
}
|
|
31
|
+
const healthy = runtime ? await checkLocalServerHealth(runtime.server_url) : false;
|
|
32
|
+
const serverLifecycle = runtime ? docker?.running && healthy ? "running" : docker?.exists ? "unhealthy" : "stopped" : "missing";
|
|
33
|
+
const project = await projectStatus(cwd);
|
|
34
|
+
const nextCommands = nextCommandsFor(serverLifecycle, project.lifecycle, this.meta.cliVersion);
|
|
35
|
+
const nextActions = nextActionsFor(project.lifecycle);
|
|
29
36
|
return this.emit({
|
|
30
37
|
status: "ok",
|
|
31
38
|
data: {
|
|
32
|
-
title: "Zitadel
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
39
|
+
title: "Zitadel status.",
|
|
40
|
+
server: {
|
|
41
|
+
lifecycle: serverLifecycle,
|
|
42
|
+
runtime: runtimeSummary(runtime),
|
|
43
|
+
docker: {
|
|
44
|
+
available: dockerError === void 0,
|
|
45
|
+
error: dockerError,
|
|
46
|
+
container_exists: docker?.exists ?? false,
|
|
47
|
+
container_running: docker?.running ?? false
|
|
48
|
+
},
|
|
49
|
+
health: {
|
|
50
|
+
healthy,
|
|
51
|
+
url: runtime?.server_url
|
|
52
|
+
}
|
|
36
53
|
},
|
|
37
|
-
|
|
38
|
-
next_actions:
|
|
39
|
-
next_commands:
|
|
54
|
+
project,
|
|
55
|
+
next_actions: nextActions,
|
|
56
|
+
next_commands: nextCommands
|
|
40
57
|
}
|
|
41
58
|
});
|
|
42
59
|
}
|
|
43
60
|
};
|
|
61
|
+
async function projectStatus(cwd) {
|
|
62
|
+
if (!await hasZitadelConfig(cwd)) return {
|
|
63
|
+
lifecycle: "not-configured",
|
|
64
|
+
message: "zitadel.json has not been created yet."
|
|
65
|
+
};
|
|
66
|
+
const config = await readZitadelConfig(cwd);
|
|
67
|
+
if (!await hasZitadelSecret(cwd)) return {
|
|
68
|
+
lifecycle: "orphaned-config",
|
|
69
|
+
project_id: typeof config.project === "string" ? config.project : void 0,
|
|
70
|
+
message: "zitadel.json exists but .zitadel/secret is missing."
|
|
71
|
+
};
|
|
72
|
+
const secret = await readZitadelSecret(cwd);
|
|
73
|
+
return {
|
|
74
|
+
lifecycle: "configured",
|
|
75
|
+
project_id: String(config.project ?? secret.project_id ?? ""),
|
|
76
|
+
issuer: readDevelopmentIssuer(config)
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
function nextActionsFor(projectLifecycle) {
|
|
80
|
+
if (projectLifecycle === "not-configured") return ["From your app directory, run setup; the CLI will detect the framework or ask in an empty directory."];
|
|
81
|
+
return [];
|
|
82
|
+
}
|
|
83
|
+
function nextCommandsFor(serverLifecycle, projectLifecycle, cliVersion) {
|
|
84
|
+
const commands = [];
|
|
85
|
+
if (serverLifecycle !== "running") commands.push(publicCliCommand("start", cliVersion));
|
|
86
|
+
if (projectLifecycle === "not-configured") commands.push(publicCliCommand("setup --server local", cliVersion));
|
|
87
|
+
else if (projectLifecycle === "orphaned-config") commands.push(publicCliCommand("setup --force", cliVersion), publicCliCommand("doctor --fix", cliVersion));
|
|
88
|
+
else commands.push(publicCliCommand("doctor", cliVersion), publicCliCommand("apply", cliVersion));
|
|
89
|
+
return commands;
|
|
90
|
+
}
|
|
44
91
|
//#endregion
|
|
45
92
|
export { Status as default };
|
|
46
93
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"status.mjs","names":[],"sources":["../../src/commands/status.ts"],"sourcesContent":["import { BaseCommand, type JsonEnvelope } from \"../lib/oclif\";\nimport {\n hasZitadelSecret,\n readDevelopmentIssuer,\n readZitadelConfig,\n readZitadelSecret,\n} from \"../lib/project\";\n\n/**\n * `zitadel status` — summarize the local project state.\n *\n *
|
|
1
|
+
{"version":3,"file":"status.mjs","names":[],"sources":["../../src/commands/status.ts"],"sourcesContent":["import { inspectContainer } from \"../lib/local-server/docker\";\nimport {\n DEFAULT_LOCAL_SERVER_URL,\n checkLocalServerHealth,\n localContainerName,\n readRuntimeMetadata,\n runtimeSummary,\n} from \"../lib/local-server/runtime\";\nimport { BaseCommand, type JsonEnvelope } from \"../lib/oclif\";\nimport {\n hasZitadelConfig,\n hasZitadelSecret,\n readDevelopmentIssuer,\n readZitadelConfig,\n readZitadelSecret,\n} from \"../lib/project\";\nimport { publicCliCommand } from \"../lib/public-cli\";\n\n/**\n * `zitadel status` — summarize the local server and project state.\n *\n * This is intentionally informational: a missing `zitadel.json`, stopped\n * local server, or orphaned config should be visible without making `status`\n * itself fail.\n */\nexport default class Status extends BaseCommand {\n static override description = \"Summarize the local Zitadel server and project state.\";\n\n async run(): Promise<JsonEnvelope> {\n const { flags } = await this.parse(Status);\n const runtime = await readRuntimeMetadata(flags.cwd ? String(flags.cwd) : process.cwd());\n if (flags.server === \"local\") {\n await this.toMeta(flags, {\n resolveServer: false,\n source: runtime?.server_url ?? DEFAULT_LOCAL_SERVER_URL,\n });\n } else {\n await this.toMeta(flags);\n }\n const { cwd } = this.meta;\n\n const containerName = runtime?.container_name ?? localContainerName(cwd);\n let docker: Awaited<ReturnType<typeof inspectContainer>> | undefined;\n let dockerError: string | undefined;\n try {\n docker = await inspectContainer(containerName);\n } catch (error) {\n dockerError = error instanceof Error ? error.message : String(error);\n }\n const healthy = runtime ? await checkLocalServerHealth(runtime.server_url) : false;\n const serverLifecycle = runtime\n ? docker?.running && healthy\n ? \"running\"\n : docker?.exists\n ? \"unhealthy\"\n : \"stopped\"\n : \"missing\";\n\n const project = await projectStatus(cwd);\n const nextCommands = nextCommandsFor(serverLifecycle, project.lifecycle, this.meta.cliVersion);\n const nextActions = nextActionsFor(project.lifecycle);\n\n return this.emit({\n status: \"ok\",\n data: {\n title: \"Zitadel status.\",\n server: {\n lifecycle: serverLifecycle,\n runtime: runtimeSummary(runtime),\n docker: {\n available: dockerError === undefined,\n error: dockerError,\n container_exists: docker?.exists ?? false,\n container_running: docker?.running ?? false,\n },\n health: {\n healthy,\n url: runtime?.server_url,\n },\n },\n project,\n next_actions: nextActions,\n next_commands: nextCommands,\n },\n });\n }\n}\n\ntype ProjectStatus =\n | {\n lifecycle: \"not-configured\";\n message: string;\n }\n | {\n lifecycle: \"orphaned-config\";\n project_id?: string;\n message: string;\n }\n | {\n lifecycle: \"configured\";\n project_id: string;\n issuer?: string;\n };\n\nasync function projectStatus(cwd: string): Promise<ProjectStatus> {\n if (!(await hasZitadelConfig(cwd))) {\n return {\n lifecycle: \"not-configured\",\n message: \"zitadel.json has not been created yet.\",\n };\n }\n\n const config = await readZitadelConfig(cwd);\n if (!(await hasZitadelSecret(cwd))) {\n return {\n lifecycle: \"orphaned-config\",\n project_id: typeof config.project === \"string\" ? config.project : undefined,\n message: \"zitadel.json exists but .zitadel/secret is missing.\",\n };\n }\n\n const secret = await readZitadelSecret(cwd);\n return {\n lifecycle: \"configured\",\n project_id: String(config.project ?? secret.project_id ?? \"\"),\n issuer: readDevelopmentIssuer(config),\n };\n}\n\nfunction nextActionsFor(projectLifecycle: ProjectStatus[\"lifecycle\"]): string[] {\n if (projectLifecycle === \"not-configured\") {\n return [\n \"From your app directory, run setup; the CLI will detect the framework or ask in an empty directory.\",\n ];\n }\n return [];\n}\n\nfunction nextCommandsFor(\n serverLifecycle: string,\n projectLifecycle: ProjectStatus[\"lifecycle\"],\n cliVersion: string,\n): string[] {\n const commands: string[] = [];\n if (serverLifecycle !== \"running\") {\n commands.push(publicCliCommand(\"start\", cliVersion));\n }\n if (projectLifecycle === \"not-configured\") {\n commands.push(publicCliCommand(\"setup --server local\", cliVersion));\n } else if (projectLifecycle === \"orphaned-config\") {\n commands.push(\n publicCliCommand(\"setup --force\", cliVersion),\n publicCliCommand(\"doctor --fix\", cliVersion),\n );\n } else {\n commands.push(publicCliCommand(\"doctor\", cliVersion), publicCliCommand(\"apply\", cliVersion));\n }\n return commands;\n}\n"],"mappings":";;;;;;;;;;;AAyBA,IAAqB,SAArB,MAAqB,eAAe,YAAY;CAC9C,OAAgB,cAAc;CAE9B,MAAM,MAA6B;EACjC,MAAM,EAAE,UAAU,MAAM,KAAK,MAAM,OAAO;EAC1C,MAAM,UAAU,MAAM,oBAAoB,MAAM,MAAM,OAAO,MAAM,IAAI,GAAG,QAAQ,KAAK,CAAC;AACxF,MAAI,MAAM,WAAW,QACnB,OAAM,KAAK,OAAO,OAAO;GACvB,eAAe;GACf,QAAQ,SAAS,cAAA;GAClB,CAAC;MAEF,OAAM,KAAK,OAAO,MAAM;EAE1B,MAAM,EAAE,QAAQ,KAAK;EAErB,MAAM,gBAAgB,SAAS,kBAAkB,mBAAmB,IAAI;EACxE,IAAI;EACJ,IAAI;AACJ,MAAI;AACF,YAAS,MAAM,iBAAiB,cAAc;WACvC,OAAO;AACd,iBAAc,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;;EAEtE,MAAM,UAAU,UAAU,MAAM,uBAAuB,QAAQ,WAAW,GAAG;EAC7E,MAAM,kBAAkB,UACpB,QAAQ,WAAW,UACjB,YACA,QAAQ,SACN,cACA,YACJ;EAEJ,MAAM,UAAU,MAAM,cAAc,IAAI;EACxC,MAAM,eAAe,gBAAgB,iBAAiB,QAAQ,WAAW,KAAK,KAAK,WAAW;EAC9F,MAAM,cAAc,eAAe,QAAQ,UAAU;AAErD,SAAO,KAAK,KAAK;GACf,QAAQ;GACR,MAAM;IACJ,OAAO;IACP,QAAQ;KACN,WAAW;KACX,SAAS,eAAe,QAAQ;KAChC,QAAQ;MACN,WAAW,gBAAgB,KAAA;MAC3B,OAAO;MACP,kBAAkB,QAAQ,UAAU;MACpC,mBAAmB,QAAQ,WAAW;MACvC;KACD,QAAQ;MACN;MACA,KAAK,SAAS;MACf;KACF;IACD;IACA,cAAc;IACd,eAAe;IAChB;GACF,CAAC;;;AAoBN,eAAe,cAAc,KAAqC;AAChE,KAAI,CAAE,MAAM,iBAAiB,IAAI,CAC/B,QAAO;EACL,WAAW;EACX,SAAS;EACV;CAGH,MAAM,SAAS,MAAM,kBAAkB,IAAI;AAC3C,KAAI,CAAE,MAAM,iBAAiB,IAAI,CAC/B,QAAO;EACL,WAAW;EACX,YAAY,OAAO,OAAO,YAAY,WAAW,OAAO,UAAU,KAAA;EAClE,SAAS;EACV;CAGH,MAAM,SAAS,MAAM,kBAAkB,IAAI;AAC3C,QAAO;EACL,WAAW;EACX,YAAY,OAAO,OAAO,WAAW,OAAO,cAAc,GAAG;EAC7D,QAAQ,sBAAsB,OAAO;EACtC;;AAGH,SAAS,eAAe,kBAAwD;AAC9E,KAAI,qBAAqB,iBACvB,QAAO,CACL,sGACD;AAEH,QAAO,EAAE;;AAGX,SAAS,gBACP,iBACA,kBACA,YACU;CACV,MAAM,WAAqB,EAAE;AAC7B,KAAI,oBAAoB,UACtB,UAAS,KAAK,iBAAiB,SAAS,WAAW,CAAC;AAEtD,KAAI,qBAAqB,iBACvB,UAAS,KAAK,iBAAiB,wBAAwB,WAAW,CAAC;UAC1D,qBAAqB,kBAC9B,UAAS,KACP,iBAAiB,iBAAiB,WAAW,EAC7C,iBAAiB,gBAAgB,WAAW,CAC7C;KAED,UAAS,KAAK,iBAAiB,UAAU,WAAW,EAAE,iBAAiB,SAAS,WAAW,CAAC;AAE9F,QAAO"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { g as readRuntimeMetadata, p as localContainerName, t as BaseCommand, x as publicCliCommand } from "../oclif-CqkWBQ7i.mjs";
|
|
2
|
+
import { u as stopAndRemoveContainer } from "../docker-DrOBycgG.mjs";
|
|
3
|
+
//#region src/commands/stop.ts
|
|
4
|
+
var Stop = class Stop extends BaseCommand {
|
|
5
|
+
static description = "Stop the local Zitadel server.";
|
|
6
|
+
async run() {
|
|
7
|
+
const { flags } = await this.parse(Stop);
|
|
8
|
+
const runtime = await readRuntimeMetadata(flags.cwd ? String(flags.cwd) : process.cwd());
|
|
9
|
+
await this.toMeta(flags, {
|
|
10
|
+
resolveServer: false,
|
|
11
|
+
source: runtime?.server_url ?? "http://localhost:8080"
|
|
12
|
+
});
|
|
13
|
+
const containerName = runtime?.container_name ?? localContainerName(this.meta.cwd);
|
|
14
|
+
if (this.meta.dryRun) return this.emit({
|
|
15
|
+
status: "ok",
|
|
16
|
+
data: {
|
|
17
|
+
title: "Local Zitadel server stop plan.",
|
|
18
|
+
runtime: {
|
|
19
|
+
container_name: containerName,
|
|
20
|
+
data_preserved: true,
|
|
21
|
+
data_dir: runtime?.data_dir
|
|
22
|
+
},
|
|
23
|
+
next_commands: [publicCliCommand("stop", this.meta.cliVersion)]
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
await stopAndRemoveContainer(containerName);
|
|
27
|
+
return this.emit({
|
|
28
|
+
status: "ok",
|
|
29
|
+
data: {
|
|
30
|
+
title: "Local Zitadel server stopped.",
|
|
31
|
+
runtime: {
|
|
32
|
+
container_name: containerName,
|
|
33
|
+
data_preserved: true,
|
|
34
|
+
data_dir: runtime?.data_dir
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
//#endregion
|
|
41
|
+
export { Stop as default };
|
|
42
|
+
|
|
43
|
+
//# sourceMappingURL=stop.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stop.mjs","names":[],"sources":["../../src/commands/stop.ts"],"sourcesContent":["import { stopAndRemoveContainer } from \"../lib/local-server/docker\";\nimport {\n DEFAULT_LOCAL_SERVER_URL,\n localContainerName,\n readRuntimeMetadata,\n} from \"../lib/local-server/runtime\";\nimport { BaseCommand, type JsonEnvelope } from \"../lib/oclif\";\nimport { publicCliCommand } from \"../lib/public-cli\";\n\nexport default class Stop extends BaseCommand {\n static override description = \"Stop the local Zitadel server.\";\n\n async run(): Promise<JsonEnvelope> {\n const { flags } = await this.parse(Stop);\n const runtime = await readRuntimeMetadata(flags.cwd ? String(flags.cwd) : process.cwd());\n await this.toMeta(flags, {\n resolveServer: false,\n source: runtime?.server_url ?? DEFAULT_LOCAL_SERVER_URL,\n });\n\n const containerName = runtime?.container_name ?? localContainerName(this.meta.cwd);\n if (this.meta.dryRun) {\n return this.emit({\n status: \"ok\",\n data: {\n title: \"Local Zitadel server stop plan.\",\n runtime: {\n container_name: containerName,\n data_preserved: true,\n data_dir: runtime?.data_dir,\n },\n next_commands: [publicCliCommand(\"stop\", this.meta.cliVersion)],\n },\n });\n }\n\n await stopAndRemoveContainer(containerName);\n\n return this.emit({\n status: \"ok\",\n data: {\n title: \"Local Zitadel server stopped.\",\n runtime: {\n container_name: containerName,\n data_preserved: true,\n data_dir: runtime?.data_dir,\n },\n },\n });\n }\n}\n"],"mappings":";;;AASA,IAAqB,OAArB,MAAqB,aAAa,YAAY;CAC5C,OAAgB,cAAc;CAE9B,MAAM,MAA6B;EACjC,MAAM,EAAE,UAAU,MAAM,KAAK,MAAM,KAAK;EACxC,MAAM,UAAU,MAAM,oBAAoB,MAAM,MAAM,OAAO,MAAM,IAAI,GAAG,QAAQ,KAAK,CAAC;AACxF,QAAM,KAAK,OAAO,OAAO;GACvB,eAAe;GACf,QAAQ,SAAS,cAAA;GAClB,CAAC;EAEF,MAAM,gBAAgB,SAAS,kBAAkB,mBAAmB,KAAK,KAAK,IAAI;AAClF,MAAI,KAAK,KAAK,OACZ,QAAO,KAAK,KAAK;GACf,QAAQ;GACR,MAAM;IACJ,OAAO;IACP,SAAS;KACP,gBAAgB;KAChB,gBAAgB;KAChB,UAAU,SAAS;KACpB;IACD,eAAe,CAAC,iBAAiB,QAAQ,KAAK,KAAK,WAAW,CAAC;IAChE;GACF,CAAC;AAGJ,QAAM,uBAAuB,cAAc;AAE3C,SAAO,KAAK,KAAK;GACf,QAAQ;GACR,MAAM;IACJ,OAAO;IACP,SAAS;KACP,gBAAgB;KAChB,gBAAgB;KAChB,UAAU,SAAS;KACpB;IACF;GACF,CAAC"}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import { E as ZitadelError, i as CONTAINER_HTTP_PORT, r as CONTAINER_DATA_DIR } from "./oclif-CqkWBQ7i.mjs";
|
|
2
|
+
import { spawn } from "node:child_process";
|
|
3
|
+
//#region src/lib/local-server/docker.ts
|
|
4
|
+
function dockerRunArgs(spec) {
|
|
5
|
+
const args = [
|
|
6
|
+
"run",
|
|
7
|
+
"--detach",
|
|
8
|
+
"--name",
|
|
9
|
+
spec.containerName,
|
|
10
|
+
"--publish",
|
|
11
|
+
`127.0.0.1:${spec.port}:${CONTAINER_HTTP_PORT}`,
|
|
12
|
+
"--volume",
|
|
13
|
+
`${spec.dataDir}:${CONTAINER_DATA_DIR}`,
|
|
14
|
+
"--env",
|
|
15
|
+
`NEXTGEN_SERVER_ADDRESS=:${CONTAINER_HTTP_PORT}`,
|
|
16
|
+
"--env",
|
|
17
|
+
`NEXTGEN_SERVER_DATA_DIR=${CONTAINER_DATA_DIR}`
|
|
18
|
+
];
|
|
19
|
+
if (spec.identity) args.push("--volume", `${spec.identity.passwdFile}:/etc/passwd:ro`, "--volume", `${spec.identity.groupFile}:/etc/group:ro`, "--user", `${spec.identity.uid}:${spec.identity.gid}`);
|
|
20
|
+
args.push(spec.image);
|
|
21
|
+
return args;
|
|
22
|
+
}
|
|
23
|
+
async function runDocker(args) {
|
|
24
|
+
return new Promise((resolve, reject) => {
|
|
25
|
+
const child = spawn("docker", args, {
|
|
26
|
+
stdio: [
|
|
27
|
+
"ignore",
|
|
28
|
+
"pipe",
|
|
29
|
+
"pipe"
|
|
30
|
+
],
|
|
31
|
+
env: process.env
|
|
32
|
+
});
|
|
33
|
+
let stdout = "";
|
|
34
|
+
let stderr = "";
|
|
35
|
+
child.stdout?.setEncoding("utf8");
|
|
36
|
+
child.stderr?.setEncoding("utf8");
|
|
37
|
+
child.stdout?.on("data", (chunk) => {
|
|
38
|
+
stdout += chunk;
|
|
39
|
+
});
|
|
40
|
+
child.stderr?.on("data", (chunk) => {
|
|
41
|
+
stderr += chunk;
|
|
42
|
+
});
|
|
43
|
+
child.on("error", reject);
|
|
44
|
+
child.on("close", (code) => {
|
|
45
|
+
resolve({
|
|
46
|
+
status: code ?? 1,
|
|
47
|
+
stdout,
|
|
48
|
+
stderr
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
async function streamDocker(args) {
|
|
54
|
+
await new Promise((resolve, reject) => {
|
|
55
|
+
const child = spawn("docker", args, {
|
|
56
|
+
stdio: "inherit",
|
|
57
|
+
env: process.env
|
|
58
|
+
});
|
|
59
|
+
child.on("error", reject);
|
|
60
|
+
child.on("close", (code) => {
|
|
61
|
+
if (code === 0) resolve();
|
|
62
|
+
else reject(/* @__PURE__ */ new Error(`docker ${args.join(" ")} exited with status ${code ?? 1}`));
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
async function dockerAvailable() {
|
|
67
|
+
return runDocker([
|
|
68
|
+
"version",
|
|
69
|
+
"--format",
|
|
70
|
+
"{{.Server.Version}}"
|
|
71
|
+
]);
|
|
72
|
+
}
|
|
73
|
+
async function pullImage(image) {
|
|
74
|
+
await requireDocker([
|
|
75
|
+
"pull",
|
|
76
|
+
"--quiet",
|
|
77
|
+
image
|
|
78
|
+
], `Pull Docker image ${image}`);
|
|
79
|
+
}
|
|
80
|
+
async function imageExists(image) {
|
|
81
|
+
let result;
|
|
82
|
+
const args = [
|
|
83
|
+
"image",
|
|
84
|
+
"inspect",
|
|
85
|
+
image
|
|
86
|
+
];
|
|
87
|
+
try {
|
|
88
|
+
result = await runDocker(args);
|
|
89
|
+
} catch (error) {
|
|
90
|
+
throw dockerError(`Inspect Docker image ${image}`, args, error);
|
|
91
|
+
}
|
|
92
|
+
return result.status === 0;
|
|
93
|
+
}
|
|
94
|
+
async function imageAvailable(image) {
|
|
95
|
+
if (await imageExists(image)) return "local";
|
|
96
|
+
const args = [
|
|
97
|
+
"manifest",
|
|
98
|
+
"inspect",
|
|
99
|
+
image
|
|
100
|
+
];
|
|
101
|
+
let result;
|
|
102
|
+
try {
|
|
103
|
+
result = await runDocker(args);
|
|
104
|
+
} catch (error) {
|
|
105
|
+
throw dockerError(`Inspect Docker image ${image}`, args, error);
|
|
106
|
+
}
|
|
107
|
+
if (result.status === 0) return "remote";
|
|
108
|
+
throw dockerError(`Inspect Docker image ${image}`, args, result);
|
|
109
|
+
}
|
|
110
|
+
async function ensureImage(image) {
|
|
111
|
+
if (await imageExists(image)) return "local";
|
|
112
|
+
await pullImage(image);
|
|
113
|
+
return "pulled";
|
|
114
|
+
}
|
|
115
|
+
async function inspectContainer(containerName) {
|
|
116
|
+
const result = await runDocker([
|
|
117
|
+
"inspect",
|
|
118
|
+
"--format",
|
|
119
|
+
"{{.Id}} {{.State.Running}} {{.Config.Image}}",
|
|
120
|
+
containerName
|
|
121
|
+
]);
|
|
122
|
+
if (result.status !== 0) return {
|
|
123
|
+
exists: false,
|
|
124
|
+
running: false
|
|
125
|
+
};
|
|
126
|
+
const [id, running, ...imageParts] = result.stdout.trim().split(/\s+/);
|
|
127
|
+
return {
|
|
128
|
+
exists: true,
|
|
129
|
+
running: running === "true",
|
|
130
|
+
id,
|
|
131
|
+
image: imageParts.join(" ")
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
async function startContainer(spec) {
|
|
135
|
+
return (await requireDocker(dockerRunArgs(spec), "Start local Zitadel container")).stdout.trim();
|
|
136
|
+
}
|
|
137
|
+
async function stopAndRemoveContainer(containerName) {
|
|
138
|
+
const inspect = await inspectContainer(containerName);
|
|
139
|
+
if (!inspect.exists) return;
|
|
140
|
+
if (inspect.running) await requireDocker(["stop", containerName], `Stop ${containerName}`);
|
|
141
|
+
await requireDocker(["rm", containerName], `Remove ${containerName}`);
|
|
142
|
+
}
|
|
143
|
+
async function containerLogs(containerName, tail) {
|
|
144
|
+
return (await requireDocker([
|
|
145
|
+
"logs",
|
|
146
|
+
"--tail",
|
|
147
|
+
String(tail),
|
|
148
|
+
containerName
|
|
149
|
+
], "Read logs")).stdout;
|
|
150
|
+
}
|
|
151
|
+
async function followContainerLogs(containerName, tail) {
|
|
152
|
+
await streamDocker([
|
|
153
|
+
"logs",
|
|
154
|
+
"--tail",
|
|
155
|
+
String(tail),
|
|
156
|
+
"--follow",
|
|
157
|
+
containerName
|
|
158
|
+
]);
|
|
159
|
+
}
|
|
160
|
+
function currentUser() {
|
|
161
|
+
const getuid = process.getuid;
|
|
162
|
+
const getgid = process.getgid;
|
|
163
|
+
if (typeof getuid !== "function") return {};
|
|
164
|
+
const uid = getuid();
|
|
165
|
+
return {
|
|
166
|
+
uid,
|
|
167
|
+
gid: typeof getgid === "function" ? getgid() : uid
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
function metadataFromStart(input) {
|
|
171
|
+
return {
|
|
172
|
+
schema_version: 1,
|
|
173
|
+
container_name: input.containerName,
|
|
174
|
+
container_id: input.containerId,
|
|
175
|
+
image: input.image,
|
|
176
|
+
port: input.port,
|
|
177
|
+
server_url: input.serverUrl,
|
|
178
|
+
data_dir: input.cwdDataDir,
|
|
179
|
+
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
180
|
+
cli_version: input.cliVersion
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
async function requireDocker(args, action) {
|
|
184
|
+
let result;
|
|
185
|
+
try {
|
|
186
|
+
result = await runDocker(args);
|
|
187
|
+
} catch (error) {
|
|
188
|
+
throw dockerError(action, args, error);
|
|
189
|
+
}
|
|
190
|
+
if (result.status !== 0) throw dockerError(action, args, result);
|
|
191
|
+
return result;
|
|
192
|
+
}
|
|
193
|
+
function dockerError(action, args, cause) {
|
|
194
|
+
const details = cause && typeof cause === "object" && "stderr" in cause ? {
|
|
195
|
+
command: ["docker", ...args],
|
|
196
|
+
stderr: String(cause.stderr)
|
|
197
|
+
} : {
|
|
198
|
+
command: ["docker", ...args],
|
|
199
|
+
cause
|
|
200
|
+
};
|
|
201
|
+
return new ZitadelError("E_VALIDATION", `${action} failed`, {
|
|
202
|
+
hint: "Check that Docker is installed, running, and reachable from this shell.",
|
|
203
|
+
nextCommands: ["zitadel doctor"],
|
|
204
|
+
details
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
//#endregion
|
|
208
|
+
export { followContainerLogs as a, metadataFromStart as c, ensureImage as i, startContainer as l, currentUser as n, imageAvailable as o, dockerAvailable as r, inspectContainer as s, containerLogs as t, stopAndRemoveContainer as u };
|
|
209
|
+
|
|
210
|
+
//# sourceMappingURL=docker-DrOBycgG.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docker-DrOBycgG.mjs","names":[],"sources":["../src/lib/local-server/docker.ts"],"sourcesContent":["import { spawn } from \"node:child_process\";\n\nimport { ZitadelError } from \"../errors\";\nimport {\n CONTAINER_DATA_DIR,\n CONTAINER_HTTP_PORT,\n type ContainerIdentity,\n type RuntimeMetadata,\n} from \"./runtime\";\n\nexport type DockerResult = {\n status: number;\n stdout: string;\n stderr: string;\n};\n\nexport type DockerRunSpec = {\n containerName: string;\n image: string;\n port: number;\n dataDir: string;\n identity?: ContainerIdentity;\n};\n\nexport function dockerRunArgs(spec: DockerRunSpec): string[] {\n const args = [\n \"run\",\n \"--detach\",\n \"--name\",\n spec.containerName,\n \"--publish\",\n `127.0.0.1:${spec.port}:${CONTAINER_HTTP_PORT}`,\n \"--volume\",\n `${spec.dataDir}:${CONTAINER_DATA_DIR}`,\n \"--env\",\n `NEXTGEN_SERVER_ADDRESS=:${CONTAINER_HTTP_PORT}`,\n \"--env\",\n `NEXTGEN_SERVER_DATA_DIR=${CONTAINER_DATA_DIR}`,\n ];\n\n if (spec.identity) {\n args.push(\n \"--volume\",\n `${spec.identity.passwdFile}:/etc/passwd:ro`,\n \"--volume\",\n `${spec.identity.groupFile}:/etc/group:ro`,\n \"--user\",\n `${spec.identity.uid}:${spec.identity.gid}`,\n );\n }\n\n args.push(spec.image);\n return args;\n}\n\nexport async function runDocker(args: string[]): Promise<DockerResult> {\n return new Promise((resolve, reject) => {\n const child = spawn(\"docker\", args, {\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n env: process.env,\n });\n let stdout = \"\";\n let stderr = \"\";\n child.stdout?.setEncoding(\"utf8\");\n child.stderr?.setEncoding(\"utf8\");\n child.stdout?.on(\"data\", (chunk: string) => {\n stdout += chunk;\n });\n child.stderr?.on(\"data\", (chunk: string) => {\n stderr += chunk;\n });\n child.on(\"error\", reject);\n child.on(\"close\", (code) => {\n resolve({ status: code ?? 1, stdout, stderr });\n });\n });\n}\n\nexport async function streamDocker(args: string[]): Promise<void> {\n await new Promise<void>((resolve, reject) => {\n const child = spawn(\"docker\", args, {\n stdio: \"inherit\",\n env: process.env,\n });\n child.on(\"error\", reject);\n child.on(\"close\", (code) => {\n if (code === 0) {\n resolve();\n } else {\n reject(new Error(`docker ${args.join(\" \")} exited with status ${code ?? 1}`));\n }\n });\n });\n}\n\nexport async function dockerAvailable(): Promise<DockerResult> {\n return runDocker([\"version\", \"--format\", \"{{.Server.Version}}\"]);\n}\n\nexport async function pullImage(image: string): Promise<void> {\n await requireDocker([\"pull\", \"--quiet\", image], `Pull Docker image ${image}`);\n}\n\nexport async function imageExists(image: string): Promise<boolean> {\n let result: DockerResult;\n const args = [\"image\", \"inspect\", image];\n try {\n result = await runDocker(args);\n } catch (error) {\n throw dockerError(`Inspect Docker image ${image}`, args, error);\n }\n return result.status === 0;\n}\n\nexport async function imageAvailable(image: string): Promise<\"local\" | \"remote\"> {\n if (await imageExists(image)) {\n return \"local\";\n }\n\n const args = [\"manifest\", \"inspect\", image];\n let result: DockerResult;\n try {\n result = await runDocker(args);\n } catch (error) {\n throw dockerError(`Inspect Docker image ${image}`, args, error);\n }\n if (result.status === 0) {\n return \"remote\";\n }\n throw dockerError(`Inspect Docker image ${image}`, args, result);\n}\n\nexport async function ensureImage(image: string): Promise<\"local\" | \"pulled\"> {\n if (await imageExists(image)) {\n return \"local\";\n }\n await pullImage(image);\n return \"pulled\";\n}\n\nexport async function inspectContainer(containerName: string): Promise<{\n exists: boolean;\n running: boolean;\n id?: string;\n image?: string;\n}> {\n const result = await runDocker([\n \"inspect\",\n \"--format\",\n \"{{.Id}} {{.State.Running}} {{.Config.Image}}\",\n containerName,\n ]);\n if (result.status !== 0) {\n return { exists: false, running: false };\n }\n const [id, running, ...imageParts] = result.stdout.trim().split(/\\s+/);\n return { exists: true, running: running === \"true\", id, image: imageParts.join(\" \") };\n}\n\nexport async function startContainer(spec: DockerRunSpec): Promise<string> {\n const args = dockerRunArgs(spec);\n const result = await requireDocker(args, \"Start local Zitadel container\");\n return result.stdout.trim();\n}\n\nexport async function stopAndRemoveContainer(containerName: string): Promise<void> {\n const inspect = await inspectContainer(containerName);\n if (!inspect.exists) {\n return;\n }\n if (inspect.running) {\n await requireDocker([\"stop\", containerName], `Stop ${containerName}`);\n }\n await requireDocker([\"rm\", containerName], `Remove ${containerName}`);\n}\n\nexport async function containerLogs(containerName: string, tail: number): Promise<string> {\n const result = await requireDocker([\"logs\", \"--tail\", String(tail), containerName], \"Read logs\");\n return result.stdout;\n}\n\nexport async function followContainerLogs(containerName: string, tail: number): Promise<void> {\n await streamDocker([\"logs\", \"--tail\", String(tail), \"--follow\", containerName]);\n}\n\nexport function currentUser(): { uid?: number; gid?: number } {\n const getuid = process.getuid;\n const getgid = process.getgid;\n if (typeof getuid !== \"function\") {\n return {};\n }\n const uid = getuid();\n const gid = typeof getgid === \"function\" ? getgid() : uid;\n return { uid, gid };\n}\n\nexport function metadataFromStart(input: {\n cwdDataDir: string;\n cliVersion: string;\n containerName: string;\n containerId: string;\n image: string;\n port: number;\n serverUrl: string;\n}): RuntimeMetadata {\n return {\n schema_version: 1,\n container_name: input.containerName,\n container_id: input.containerId,\n image: input.image,\n port: input.port,\n server_url: input.serverUrl,\n data_dir: input.cwdDataDir,\n created_at: new Date().toISOString(),\n cli_version: input.cliVersion,\n };\n}\n\nexport async function requireDocker(args: string[], action: string): Promise<DockerResult> {\n let result: DockerResult;\n try {\n result = await runDocker(args);\n } catch (error) {\n throw dockerError(action, args, error);\n }\n if (result.status !== 0) {\n throw dockerError(action, args, result);\n }\n return result;\n}\n\nfunction dockerError(action: string, args: string[], cause: unknown): ZitadelError {\n const details =\n cause && typeof cause === \"object\" && \"stderr\" in cause\n ? { command: [\"docker\", ...args], stderr: String((cause as { stderr?: unknown }).stderr) }\n : { command: [\"docker\", ...args], cause };\n return new ZitadelError(\"E_VALIDATION\", `${action} failed`, {\n hint: \"Check that Docker is installed, running, and reachable from this shell.\",\n nextCommands: [\"zitadel doctor\"],\n details,\n });\n}\n"],"mappings":"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"}
|