@zitadel/cli 1.0.0-alpha.20 → 1.0.0-alpha.21

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.
@@ -278,6 +278,7 @@ var Claim = class Claim extends BaseCommand {
278
278
  timeoutSeconds: flags.timeout,
279
279
  now: Date.now()
280
280
  });
281
+ if (isLoopbackUrl(this.meta.source) && !isLoopbackUrl(challenge.claim_url)) consola.warn(`The server at ${this.meta.source} advertised a claim page on ${new URL(challenge.claim_url).origin}. If you started that server yourself, set NEXTGEN_SERVER_PUBLIC_BASE to its reachable origin (e.g. http://localhost:8080).`);
281
282
  consola.box({
282
283
  title: "Finish in your browser",
283
284
  message: `${challenge.claim_url}\n\nSign in there to attach this project to your team.`,
@@ -396,6 +397,18 @@ function expiredError(message) {
396
397
  nextCommands: ["zitadel claim"]
397
398
  });
398
399
  }
400
+ /**
401
+ * Loopback check on the URL's hostname: `localhost`, the whole `127.0.0.0/8`
402
+ * block, or `[::1]` (how WHATWG URLs spell IPv6 loopback).
403
+ */
404
+ function isLoopbackUrl(value) {
405
+ try {
406
+ const hostname = new URL(value).hostname;
407
+ return hostname === "localhost" || hostname === "[::1]" || /^127(\.\d{1,3}){3}$/.test(hostname);
408
+ } catch {
409
+ return false;
410
+ }
411
+ }
399
412
  //#endregion
400
413
  export { claimDeadline, Claim as default };
401
414
 
@@ -1 +1 @@
1
- {"version":3,"file":"claim.mjs","names":["nodeSpawn","sleep"],"sources":["../../src/lib/browser.ts","../../src/commands/claim.ts"],"sourcesContent":["import { spawn as nodeSpawn } from \"node:child_process\";\nimport { access, readFile } from \"node:fs/promises\";\nimport { constants } from \"node:fs\";\nimport { delimiter, join } from \"node:path\";\n\n/**\n * Why a browser launch did not happen. Never an error condition: every reason\n * resolves to the same caller behaviour (show the link and let the user open\n * it themselves), so this exists for narration and telemetry, not control flow.\n *\n * - `headless` — a graphical session was not detected (no `DISPLAY`, no\n * `WAYLAND_DISPLAY`, or a plain SSH session). Spawning would either fail\n * noisily or open a browser on a machine nobody is looking at.\n * - `no-opener` — no known opener binary is on `PATH`. Common on minimal\n * containers and BSDs without `xdg-utils`.\n * - `disabled` — `BROWSER=none`, the convention honoured by webbrowser(3) and\n * most dev servers for \"never open anything\".\n * - `spawn-failed` — the opener exists but the process could not be started.\n */\nexport type OpenFailure = \"headless\" | \"no-opener\" | \"disabled\" | \"spawn-failed\";\n\n/** Outcome of {@link openInBrowser}. `opened` is best effort: see the note there. */\nexport type OpenResult = { opened: boolean; reason?: OpenFailure };\n\n/**\n * Injectable seams so the platform matrix is unit-testable without touching\n * the real machine: every environment probe this module makes goes through\n * one of these.\n */\nexport type BrowserDeps = {\n platform: NodeJS.Platform;\n env: NodeJS.ProcessEnv;\n /**\n * Resolves an executable name against `PATH`, or `undefined` if absent.\n * The default implementation reads `PATH` from {@link BrowserDeps.env}, not\n * from `process.env`, so overriding `env` alone is enough to redirect\n * executable resolution.\n */\n lookupPath: (command: string) => Promise<string | undefined>;\n /** Reads `/proc/version` for WSL detection; `undefined` when unreadable. */\n readProcVersion: () => Promise<string | undefined>;\n spawn: (command: string, args: string[]) => void;\n};\n\n/**\n * Openers tried in order on Linux and the BSDs. `xdg-open` is the standard and\n * covers any desktop that ships `xdg-utils`; the rest are the fallbacks that\n * exist on systems that don't (GNOME-only, Debian's alternatives system, KDE).\n * `gio` takes a subcommand, so it carries its own argv shape.\n */\nconst UNIX_OPENERS: ReadonlyArray<{ command: string; args: (url: string) => string[] }> = [\n { command: \"xdg-open\", args: (url) => [url] },\n { command: \"gio\", args: (url) => [\"open\", url] },\n { command: \"x-www-browser\", args: (url) => [url] },\n { command: \"sensible-browser\", args: (url) => [url] },\n { command: \"gnome-open\", args: (url) => [url] },\n { command: \"kde-open\", args: (url) => [url] },\n];\n\n/**\n * Opens `url` in the user's default browser, best effort.\n *\n * Best effort is the contract, not a caveat: this never throws and never\n * reports a failure the caller must handle. There is no portable way to know a\n * browser actually rendered the page — the opener returns as soon as it has\n * handed the URL off — so `opened: true` means only \"an opener was started\".\n * Callers must print the URL regardless and treat the launch as a convenience\n * on top of it, which is what makes headless machines, bare containers, and\n * SSH sessions work without a special case.\n */\nexport async function openInBrowser(\n url: string,\n overrides: Partial<BrowserDeps> = {},\n): Promise<OpenResult> {\n // Resolve `env` first so the default `lookupPath` closes over the *effective*\n // environment: overriding `env.PATH` alone must redirect executable\n // resolution, without the caller also having to replace `lookupPath`.\n const env = overrides.env ?? process.env;\n const deps: BrowserDeps = { ...defaultDeps(env), ...overrides };\n\n const browser = deps.env.BROWSER?.trim();\n if (browser === \"none\") {\n return { opened: false, reason: \"disabled\" };\n }\n if (browser) {\n return launch(deps, browser, [url]);\n }\n\n if (deps.platform === \"darwin\") {\n return launch(deps, \"open\", [url]);\n }\n if (deps.platform === \"win32\") {\n // `start` is a cmd builtin, not an executable, and its first quoted\n // argument is taken as the window title — hence the empty \"\" before the\n // URL, without which a quoted URL would be swallowed as the title.\n return launch(deps, \"cmd\", [\"/c\", \"start\", \"\", url]);\n }\n\n if (await isWsl(deps)) {\n // Inside WSL the browser lives on the Windows side. `wslview` (wslu) is\n // the purpose-built bridge; PowerShell is the fallback every WSL install\n // has.\n const wslview = await deps.lookupPath(\"wslview\");\n if (wslview) {\n return launch(deps, \"wslview\", [url]);\n }\n return launch(deps, \"powershell.exe\", [\"-NoProfile\", \"-Command\", \"Start-Process\", url]);\n }\n\n if (!hasGraphicalSession(deps.env)) {\n return { opened: false, reason: \"headless\" };\n }\n\n for (const opener of UNIX_OPENERS) {\n if (await deps.lookupPath(opener.command)) {\n return launch(deps, opener.command, opener.args(url));\n }\n }\n return { opened: false, reason: \"no-opener\" };\n}\n\n/**\n * Whether a graphical session is plausibly present and worth opening into.\n *\n * X11 and Wayland both advertise themselves through an env var. SSH is\n * excluded even when `DISPLAY` is set, because X11 forwarding sets it: the\n * browser would launch on the remote host and, at best, tunnel a window back\n * over a link nobody asked to render one on.\n */\nfunction hasGraphicalSession(env: NodeJS.ProcessEnv): boolean {\n if (env.SSH_CONNECTION || env.SSH_TTY || env.SSH_CLIENT) {\n return false;\n }\n return Boolean(env.DISPLAY || env.WAYLAND_DISPLAY);\n}\n\n/**\n * WSL advertises itself through `WSL_DISTRO_NAME`/`WSLENV`, but neither is set\n * under every shell, so fall back to the kernel release string, which carries\n * `microsoft` on all WSL kernels.\n */\nasync function isWsl(deps: BrowserDeps): Promise<boolean> {\n if (deps.platform !== \"linux\") {\n return false;\n }\n if (deps.env.WSL_DISTRO_NAME || deps.env.WSLENV) {\n return true;\n }\n const version = await deps.readProcVersion();\n return version !== undefined && version.toLowerCase().includes(\"microsoft\");\n}\n\n/**\n * Starts the opener fully detached: the CLI keeps polling for the claim while\n * the browser lives on, and must not hold the event loop open waiting for a\n * process it does not own. Output is discarded because openers write\n * diagnostics to stderr that would interleave with the CLI's own narration.\n */\nfunction launch(deps: BrowserDeps, command: string, args: string[]): OpenResult {\n try {\n deps.spawn(command, args);\n return { opened: true };\n } catch {\n return { opened: false, reason: \"spawn-failed\" };\n }\n}\n\nfunction defaultDeps(env: NodeJS.ProcessEnv): BrowserDeps {\n return {\n platform: process.platform,\n env,\n lookupPath: (command) => lookupPath(command, env.PATH ?? \"\"),\n readProcVersion: async () => {\n try {\n return await readFile(\"/proc/version\", \"utf8\");\n } catch {\n return undefined;\n }\n },\n spawn: (command, args) => {\n const child = nodeSpawn(command, args, { stdio: \"ignore\", detached: true });\n // A spawn error surfaces asynchronously; swallow it so an opener that\n // vanishes between the PATH probe and the spawn cannot crash the CLI\n // with an unhandled 'error' event.\n child.on(\"error\", () => undefined);\n child.unref();\n },\n };\n}\n\n/**\n * Minimal `which`: the first entry of `pathEnv` holding an executable\n * `command`. Probing the filesystem directly avoids shelling out (and avoids\n * depending on a `which`/`where` binary being present on the very systems that\n * are missing `xdg-open`). `pathEnv` is passed in rather than read from\n * `process.env` so it follows whatever environment the caller injected.\n */\nasync function lookupPath(command: string, pathEnv: string): Promise<string | undefined> {\n for (const dir of pathEnv.split(delimiter).filter(Boolean)) {\n const candidate = join(dir, command);\n try {\n await access(candidate, constants.X_OK);\n return candidate;\n } catch {\n continue;\n }\n }\n return undefined;\n}\n","import { setTimeout as sleep } from \"node:timers/promises\";\n\nimport { Flags } from \"@oclif/core\";\nimport { createZitadelClient } from \"@zitadel/api/client\";\nimport { ApiError } from \"@zitadel/api/runtime/fetch\";\nimport consola from \"consola\";\n\nimport { openInBrowser } from \"../lib/browser\";\nimport { isAttached } from \"../lib/claim-state\";\nimport { ZitadelError } from \"../lib/errors\";\nimport { isObject } from \"../lib/json\";\nimport { BaseCommand, type JsonEnvelope } from \"../lib/oclif\";\nimport { readZitadelSecret, writeZitadelSecret, type ZitadelSecret } from \"../lib/project\";\n\n/**\n * Poll cadence while a human completes the browser step. Starts responsive so\n * a claim finished in a few seconds is reported almost immediately, then eases\n * off so a link left open for the full ten minutes costs a few hundred\n * requests rather than a few thousand.\n */\nconst INITIAL_POLL_MS = 1000;\nconst MAX_POLL_MS = 5000;\nconst POLL_BACKOFF_FACTOR = 1.5;\n\n/**\n * Backstop for the poll deadline, used only when the server's own `expires_at`\n * cannot be parsed. The challenge TTL is the platform's to decide (ADR 046) and\n * the response carries it, so this is never the normal path — but without it a\n * malformed or misrouted response would leave the loop with no deadline at all\n * and the CLI polling forever. Matches the TTL the ADR documents.\n */\nconst FALLBACK_TTL_MS = 10 * 60 * 1000;\n\n/**\n * When to stop polling: the earlier of the server's own expiry and any\n * `--timeout` the caller set.\n *\n * Trusts `expires_at` rather than assuming the TTL, because the contract\n * documents it as the authority and the CLI should not go stale if the platform\n * ever retunes it. An unparseable value falls back to {@link FALLBACK_TTL_MS}\n * rather than to no deadline: a malformed or misrouted response must not leave\n * the loop running forever. Exported so that fallback is testable without\n * waiting out a real TTL.\n */\nexport function claimDeadline(input: {\n expiresAt: string;\n timeoutSeconds?: number;\n now: number;\n}): number {\n const parsed = Date.parse(input.expiresAt);\n const serverDeadline = Number.isNaN(parsed) ? input.now + FALLBACK_TTL_MS : parsed;\n if (input.timeoutSeconds === undefined) {\n return serverDeadline;\n }\n return Math.min(serverDeadline, input.now + input.timeoutSeconds * 1000);\n}\n\n/**\n * `zitadel claim` — attach this project to a team.\n *\n * Shaped like the device-authorization grant (ADR 046): the CLI mints a\n * challenge with the project secret, hands a URL to a browser, and polls until\n * the human finishes. The project secret never leaves the machine and the\n * browser only ever sees the challenge id.\n *\n * The command blocks for as long as the link is valid. That is the point: the\n * developer is looking at the terminal, finishes in the browser, and comes\n * back to a terminal that already knows. Nothing about the project changes —\n * the issuer, users, and applications keep working exactly as before, so this\n * is purely additive.\n */\nexport default class Claim extends BaseCommand {\n static override description =\n \"Attach this project to a team so it becomes permanent. Opens a browser to finish signing in.\";\n\n static override examples = [\n \"<%= config.bin %> <%= command.id %>\",\n \"<%= config.bin %> <%= command.id %> --no-open\",\n \"<%= config.bin %> <%= command.id %> --timeout 120\",\n ];\n\n static override flags = {\n \"no-open\": Flags.boolean({\n description: \"Print the link instead of opening a browser.\",\n }),\n timeout: Flags.integer({\n min: 1,\n description:\n \"Seconds to wait for the browser step. Defaults to the link's own expiry (10 minutes).\",\n }),\n };\n\n async run(): Promise<JsonEnvelope> {\n const { flags } = await this.parse(Claim);\n await this.toMeta(flags);\n const { cwd, dryRun, nonInteractive } = this.meta;\n\n const secret = await readZitadelSecret(cwd);\n // The local record is authoritative enough to skip the round trip: the\n // platform enforces first-claim-wins anyway, so re-asking could only ever\n // return the same answer at the cost of a request.\n if (isAttached(secret)) {\n return this.alreadyClaimed({\n project_id: secret.project_id,\n team_id: secret.team_id,\n claimed_at: secret.claimed_at,\n });\n }\n\n // `--dry-run` promises to mutate neither files nor the platform, so it has\n // to stop before `initClaim`: minting a challenge is a platform write, and\n // a developer who then finished the browser step would really claim the\n // project while this run deliberately skipped recording it — the local file\n // and the platform would disagree, which is worse than not previewing at\n // all. There is nothing further to preview here anyway: the outcome of a\n // claim is decided in a browser, not by anything the CLI could compute.\n if (dryRun) {\n this.recordTelemetry({ claim_outcome: \"dry_run\" });\n return this.emit({\n status: \"skipped\",\n reason: \"dry-run\",\n data: {\n title: \"Zitadel claim was not started.\",\n project_id: secret.project_id,\n would: \"Open a browser to attach this project to a team, then record the team in .zitadel/secret.\",\n },\n nextCommands: [\"zitadel claim\"],\n });\n }\n\n const client = createZitadelClient({\n baseUrl: this.meta.source,\n token: secret.project_secret,\n });\n\n let challenge;\n try {\n challenge = await client.initClaim(secret.project_id);\n } catch (error) {\n const claimed = alreadyClaimedDetails(error);\n if (claimed) {\n return this.alreadyClaimed({ project_id: secret.project_id, ...claimed });\n }\n throw error;\n }\n\n const deadline = claimDeadline({\n expiresAt: challenge.expires_at,\n timeoutSeconds: flags.timeout,\n now: Date.now(),\n });\n\n // Always show the link first, before attempting anything: it is the whole\n // instruction on its own, so a launch that never happens (headless box,\n // no `xdg-open`, `--no-open`, an agent) needs no separate path.\n consola.box({\n title: \"Finish in your browser\",\n message: `${challenge.claim_url}\\n\\nSign in there to attach this project to your team.`,\n style: { padding: 1, borderStyle: \"rounded\", borderColor: \"cyan\" },\n });\n\n const skipLaunch = flags[\"no-open\"] || nonInteractive;\n const opened = skipLaunch ? false : (await openInBrowser(challenge.claim_url)).opened;\n if (!opened) {\n consola.info(\"Open the link above to continue.\");\n }\n consola.start(\"Waiting for the browser step to finish\");\n\n const completed = await this.poll(client, secret.project_id, challenge.challenge_id, deadline);\n\n const next: ZitadelSecret = {\n ...secret,\n claimed_at: completed.claimed_at,\n team_id: completed.team_id,\n };\n // Unconditional: `--dry-run` never reaches here (it returns above), so a\n // claim that got this far really happened on the platform and the local\n // record must follow it.\n await writeZitadelSecret(cwd, next);\n consola.success(`Project attached to team ${completed.team_id}`);\n\n this.recordTelemetry({ claim_outcome: \"completed\", browser_opened: opened });\n return this.emit({\n status: \"ok\",\n data: {\n title: \"Zitadel project attached to a team.\",\n project_id: secret.project_id,\n team_id: completed.team_id,\n claimed_at: completed.claimed_at,\n dashboard_url: completed.dashboard_url,\n next_actions: [`Manage the project at ${completed.dashboard_url}.`],\n },\n });\n }\n\n /**\n * Polls `claim/status` until the browser leg lands or the link dies.\n *\n * The two failure modes read the same to a user (the link no longer works,\n * start another one), so they share an error shape and differ only in\n * wording — a `410` means the platform expired it, a passed deadline means\n * we stopped waiting first.\n */\n private async poll(\n client: ReturnType<typeof createZitadelClient>,\n projectId: string,\n challengeId: string,\n deadline: number,\n ): Promise<{ team_id: string; claimed_at: string; dashboard_url: string }> {\n let interval = INITIAL_POLL_MS;\n let polls = 0;\n\n while (Date.now() < deadline) {\n polls += 1;\n try {\n const status = await client.getClaimStatus(projectId, { challenge_id: challengeId });\n if (status.status === \"completed\") {\n this.recordTelemetry({ poll_count: polls });\n return status;\n }\n } catch (error) {\n if (error instanceof ApiError && error.status === 410) {\n this.recordTelemetry({ claim_outcome: \"expired\", poll_count: polls });\n throw expiredError(\"The link expired before the browser step finished.\");\n }\n if (error instanceof ApiError && error.status === 429) {\n // Polled too eagerly. Back off to the ceiling immediately rather\n // than easing towards it, so a rate limit is never compounded.\n interval = MAX_POLL_MS;\n } else {\n this.recordTelemetry({ poll_count: polls });\n throw error;\n }\n }\n\n // Never sleep past the deadline: the last wait should end the loop, not\n // overshoot it by up to a full interval.\n const remaining = deadline - Date.now();\n if (remaining <= 0) {\n break;\n }\n await sleep(Math.min(interval, remaining));\n interval = Math.min(interval * POLL_BACKOFF_FACTOR, MAX_POLL_MS);\n }\n\n this.recordTelemetry({ claim_outcome: \"timeout\", poll_count: polls });\n throw expiredError(\"Stopped waiting for the browser step to finish.\");\n }\n\n /**\n * The idempotent outcome: the project already belongs to a team, whether we\n * learned that locally or from a `409`. Reported as a skip, not an error, so\n * re-running the command (or an agent retrying it) is a clean no-op.\n */\n private alreadyClaimed(data: {\n project_id: string;\n team_id: string;\n claimed_at?: string;\n dashboard_url?: string;\n }): JsonEnvelope {\n this.recordTelemetry({ claim_outcome: \"already_claimed\" });\n consola.info(`This project already belongs to team ${data.team_id}.`);\n return this.emit({\n status: \"skipped\",\n reason: \"already-claimed\",\n data: { title: \"Zitadel project already belongs to a team.\", ...data },\n });\n }\n}\n\n/**\n * Extracts the owning team from a `409 proj.already_claimed` envelope. Reads\n * the body defensively rather than casting to the generated type: this is the\n * one place a malformed error body would otherwise turn a clean skip into a\n * crash.\n */\nfunction alreadyClaimedDetails(\n error: unknown,\n): { team_id: string; dashboard_url?: string } | undefined {\n if (!(error instanceof ApiError) || error.status !== 409 || !isObject(error.body)) {\n return undefined;\n }\n const details = error.body.details;\n if (!isObject(details) || typeof details.team_id !== \"string\") {\n return undefined;\n }\n return {\n team_id: details.team_id,\n dashboard_url: typeof details.dashboard_url === \"string\" ? details.dashboard_url : undefined,\n };\n}\n\nfunction expiredError(message: string): ZitadelError {\n return new ZitadelError(\"E_VALIDATION\", message, {\n hint: \"Links are valid for 10 minutes. Start a new one.\",\n nextCommands: [\"zitadel claim\"],\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"claim.mjs","names":["nodeSpawn","sleep"],"sources":["../../src/lib/browser.ts","../../src/commands/claim.ts"],"sourcesContent":["import { spawn as nodeSpawn } from \"node:child_process\";\nimport { access, readFile } from \"node:fs/promises\";\nimport { constants } from \"node:fs\";\nimport { delimiter, join } from \"node:path\";\n\n/**\n * Why a browser launch did not happen. Never an error condition: every reason\n * resolves to the same caller behaviour (show the link and let the user open\n * it themselves), so this exists for narration and telemetry, not control flow.\n *\n * - `headless` — a graphical session was not detected (no `DISPLAY`, no\n * `WAYLAND_DISPLAY`, or a plain SSH session). Spawning would either fail\n * noisily or open a browser on a machine nobody is looking at.\n * - `no-opener` — no known opener binary is on `PATH`. Common on minimal\n * containers and BSDs without `xdg-utils`.\n * - `disabled` — `BROWSER=none`, the convention honoured by webbrowser(3) and\n * most dev servers for \"never open anything\".\n * - `spawn-failed` — the opener exists but the process could not be started.\n */\nexport type OpenFailure = \"headless\" | \"no-opener\" | \"disabled\" | \"spawn-failed\";\n\n/** Outcome of {@link openInBrowser}. `opened` is best effort: see the note there. */\nexport type OpenResult = { opened: boolean; reason?: OpenFailure };\n\n/**\n * Injectable seams so the platform matrix is unit-testable without touching\n * the real machine: every environment probe this module makes goes through\n * one of these.\n */\nexport type BrowserDeps = {\n platform: NodeJS.Platform;\n env: NodeJS.ProcessEnv;\n /**\n * Resolves an executable name against `PATH`, or `undefined` if absent.\n * The default implementation reads `PATH` from {@link BrowserDeps.env}, not\n * from `process.env`, so overriding `env` alone is enough to redirect\n * executable resolution.\n */\n lookupPath: (command: string) => Promise<string | undefined>;\n /** Reads `/proc/version` for WSL detection; `undefined` when unreadable. */\n readProcVersion: () => Promise<string | undefined>;\n spawn: (command: string, args: string[]) => void;\n};\n\n/**\n * Openers tried in order on Linux and the BSDs. `xdg-open` is the standard and\n * covers any desktop that ships `xdg-utils`; the rest are the fallbacks that\n * exist on systems that don't (GNOME-only, Debian's alternatives system, KDE).\n * `gio` takes a subcommand, so it carries its own argv shape.\n */\nconst UNIX_OPENERS: ReadonlyArray<{ command: string; args: (url: string) => string[] }> = [\n { command: \"xdg-open\", args: (url) => [url] },\n { command: \"gio\", args: (url) => [\"open\", url] },\n { command: \"x-www-browser\", args: (url) => [url] },\n { command: \"sensible-browser\", args: (url) => [url] },\n { command: \"gnome-open\", args: (url) => [url] },\n { command: \"kde-open\", args: (url) => [url] },\n];\n\n/**\n * Opens `url` in the user's default browser, best effort.\n *\n * Best effort is the contract, not a caveat: this never throws and never\n * reports a failure the caller must handle. There is no portable way to know a\n * browser actually rendered the page — the opener returns as soon as it has\n * handed the URL off — so `opened: true` means only \"an opener was started\".\n * Callers must print the URL regardless and treat the launch as a convenience\n * on top of it, which is what makes headless machines, bare containers, and\n * SSH sessions work without a special case.\n */\nexport async function openInBrowser(\n url: string,\n overrides: Partial<BrowserDeps> = {},\n): Promise<OpenResult> {\n // Resolve `env` first so the default `lookupPath` closes over the *effective*\n // environment: overriding `env.PATH` alone must redirect executable\n // resolution, without the caller also having to replace `lookupPath`.\n const env = overrides.env ?? process.env;\n const deps: BrowserDeps = { ...defaultDeps(env), ...overrides };\n\n const browser = deps.env.BROWSER?.trim();\n if (browser === \"none\") {\n return { opened: false, reason: \"disabled\" };\n }\n if (browser) {\n return launch(deps, browser, [url]);\n }\n\n if (deps.platform === \"darwin\") {\n return launch(deps, \"open\", [url]);\n }\n if (deps.platform === \"win32\") {\n // `start` is a cmd builtin, not an executable, and its first quoted\n // argument is taken as the window title — hence the empty \"\" before the\n // URL, without which a quoted URL would be swallowed as the title.\n return launch(deps, \"cmd\", [\"/c\", \"start\", \"\", url]);\n }\n\n if (await isWsl(deps)) {\n // Inside WSL the browser lives on the Windows side. `wslview` (wslu) is\n // the purpose-built bridge; PowerShell is the fallback every WSL install\n // has.\n const wslview = await deps.lookupPath(\"wslview\");\n if (wslview) {\n return launch(deps, \"wslview\", [url]);\n }\n return launch(deps, \"powershell.exe\", [\"-NoProfile\", \"-Command\", \"Start-Process\", url]);\n }\n\n if (!hasGraphicalSession(deps.env)) {\n return { opened: false, reason: \"headless\" };\n }\n\n for (const opener of UNIX_OPENERS) {\n if (await deps.lookupPath(opener.command)) {\n return launch(deps, opener.command, opener.args(url));\n }\n }\n return { opened: false, reason: \"no-opener\" };\n}\n\n/**\n * Whether a graphical session is plausibly present and worth opening into.\n *\n * X11 and Wayland both advertise themselves through an env var. SSH is\n * excluded even when `DISPLAY` is set, because X11 forwarding sets it: the\n * browser would launch on the remote host and, at best, tunnel a window back\n * over a link nobody asked to render one on.\n */\nfunction hasGraphicalSession(env: NodeJS.ProcessEnv): boolean {\n if (env.SSH_CONNECTION || env.SSH_TTY || env.SSH_CLIENT) {\n return false;\n }\n return Boolean(env.DISPLAY || env.WAYLAND_DISPLAY);\n}\n\n/**\n * WSL advertises itself through `WSL_DISTRO_NAME`/`WSLENV`, but neither is set\n * under every shell, so fall back to the kernel release string, which carries\n * `microsoft` on all WSL kernels.\n */\nasync function isWsl(deps: BrowserDeps): Promise<boolean> {\n if (deps.platform !== \"linux\") {\n return false;\n }\n if (deps.env.WSL_DISTRO_NAME || deps.env.WSLENV) {\n return true;\n }\n const version = await deps.readProcVersion();\n return version !== undefined && version.toLowerCase().includes(\"microsoft\");\n}\n\n/**\n * Starts the opener fully detached: the CLI keeps polling for the claim while\n * the browser lives on, and must not hold the event loop open waiting for a\n * process it does not own. Output is discarded because openers write\n * diagnostics to stderr that would interleave with the CLI's own narration.\n */\nfunction launch(deps: BrowserDeps, command: string, args: string[]): OpenResult {\n try {\n deps.spawn(command, args);\n return { opened: true };\n } catch {\n return { opened: false, reason: \"spawn-failed\" };\n }\n}\n\nfunction defaultDeps(env: NodeJS.ProcessEnv): BrowserDeps {\n return {\n platform: process.platform,\n env,\n lookupPath: (command) => lookupPath(command, env.PATH ?? \"\"),\n readProcVersion: async () => {\n try {\n return await readFile(\"/proc/version\", \"utf8\");\n } catch {\n return undefined;\n }\n },\n spawn: (command, args) => {\n const child = nodeSpawn(command, args, { stdio: \"ignore\", detached: true });\n // A spawn error surfaces asynchronously; swallow it so an opener that\n // vanishes between the PATH probe and the spawn cannot crash the CLI\n // with an unhandled 'error' event.\n child.on(\"error\", () => undefined);\n child.unref();\n },\n };\n}\n\n/**\n * Minimal `which`: the first entry of `pathEnv` holding an executable\n * `command`. Probing the filesystem directly avoids shelling out (and avoids\n * depending on a `which`/`where` binary being present on the very systems that\n * are missing `xdg-open`). `pathEnv` is passed in rather than read from\n * `process.env` so it follows whatever environment the caller injected.\n */\nasync function lookupPath(command: string, pathEnv: string): Promise<string | undefined> {\n for (const dir of pathEnv.split(delimiter).filter(Boolean)) {\n const candidate = join(dir, command);\n try {\n await access(candidate, constants.X_OK);\n return candidate;\n } catch {\n continue;\n }\n }\n return undefined;\n}\n","import { setTimeout as sleep } from \"node:timers/promises\";\n\nimport { Flags } from \"@oclif/core\";\nimport { createZitadelClient } from \"@zitadel/api/client\";\nimport { ApiError } from \"@zitadel/api/runtime/fetch\";\nimport consola from \"consola\";\n\nimport { openInBrowser } from \"../lib/browser\";\nimport { isAttached } from \"../lib/claim-state\";\nimport { ZitadelError } from \"../lib/errors\";\nimport { isObject } from \"../lib/json\";\nimport { BaseCommand, type JsonEnvelope } from \"../lib/oclif\";\nimport { readZitadelSecret, writeZitadelSecret, type ZitadelSecret } from \"../lib/project\";\n\n/**\n * Poll cadence while a human completes the browser step. Starts responsive so\n * a claim finished in a few seconds is reported almost immediately, then eases\n * off so a link left open for the full ten minutes costs a few hundred\n * requests rather than a few thousand.\n */\nconst INITIAL_POLL_MS = 1000;\nconst MAX_POLL_MS = 5000;\nconst POLL_BACKOFF_FACTOR = 1.5;\n\n/**\n * Backstop for the poll deadline, used only when the server's own `expires_at`\n * cannot be parsed. The challenge TTL is the platform's to decide (ADR 046) and\n * the response carries it, so this is never the normal path — but without it a\n * malformed or misrouted response would leave the loop with no deadline at all\n * and the CLI polling forever. Matches the TTL the ADR documents.\n */\nconst FALLBACK_TTL_MS = 10 * 60 * 1000;\n\n/**\n * When to stop polling: the earlier of the server's own expiry and any\n * `--timeout` the caller set.\n *\n * Trusts `expires_at` rather than assuming the TTL, because the contract\n * documents it as the authority and the CLI should not go stale if the platform\n * ever retunes it. An unparseable value falls back to {@link FALLBACK_TTL_MS}\n * rather than to no deadline: a malformed or misrouted response must not leave\n * the loop running forever. Exported so that fallback is testable without\n * waiting out a real TTL.\n */\nexport function claimDeadline(input: {\n expiresAt: string;\n timeoutSeconds?: number;\n now: number;\n}): number {\n const parsed = Date.parse(input.expiresAt);\n const serverDeadline = Number.isNaN(parsed) ? input.now + FALLBACK_TTL_MS : parsed;\n if (input.timeoutSeconds === undefined) {\n return serverDeadline;\n }\n return Math.min(serverDeadline, input.now + input.timeoutSeconds * 1000);\n}\n\n/**\n * `zitadel claim` — attach this project to a team.\n *\n * Shaped like the device-authorization grant (ADR 046): the CLI mints a\n * challenge with the project secret, hands a URL to a browser, and polls until\n * the human finishes. The project secret never leaves the machine and the\n * browser only ever sees the challenge id.\n *\n * The command blocks for as long as the link is valid. That is the point: the\n * developer is looking at the terminal, finishes in the browser, and comes\n * back to a terminal that already knows. Nothing about the project changes —\n * the issuer, users, and applications keep working exactly as before, so this\n * is purely additive.\n */\nexport default class Claim extends BaseCommand {\n static override description =\n \"Attach this project to a team so it becomes permanent. Opens a browser to finish signing in.\";\n\n static override examples = [\n \"<%= config.bin %> <%= command.id %>\",\n \"<%= config.bin %> <%= command.id %> --no-open\",\n \"<%= config.bin %> <%= command.id %> --timeout 120\",\n ];\n\n static override flags = {\n \"no-open\": Flags.boolean({\n description: \"Print the link instead of opening a browser.\",\n }),\n timeout: Flags.integer({\n min: 1,\n description:\n \"Seconds to wait for the browser step. Defaults to the link's own expiry (10 minutes).\",\n }),\n };\n\n async run(): Promise<JsonEnvelope> {\n const { flags } = await this.parse(Claim);\n await this.toMeta(flags);\n const { cwd, dryRun, nonInteractive } = this.meta;\n\n const secret = await readZitadelSecret(cwd);\n // The local record is authoritative enough to skip the round trip: the\n // platform enforces first-claim-wins anyway, so re-asking could only ever\n // return the same answer at the cost of a request.\n if (isAttached(secret)) {\n return this.alreadyClaimed({\n project_id: secret.project_id,\n team_id: secret.team_id,\n claimed_at: secret.claimed_at,\n });\n }\n\n // `--dry-run` promises to mutate neither files nor the platform, so it has\n // to stop before `initClaim`: minting a challenge is a platform write, and\n // a developer who then finished the browser step would really claim the\n // project while this run deliberately skipped recording it — the local file\n // and the platform would disagree, which is worse than not previewing at\n // all. There is nothing further to preview here anyway: the outcome of a\n // claim is decided in a browser, not by anything the CLI could compute.\n if (dryRun) {\n this.recordTelemetry({ claim_outcome: \"dry_run\" });\n return this.emit({\n status: \"skipped\",\n reason: \"dry-run\",\n data: {\n title: \"Zitadel claim was not started.\",\n project_id: secret.project_id,\n would: \"Open a browser to attach this project to a team, then record the team in .zitadel/secret.\",\n },\n nextCommands: [\"zitadel claim\"],\n });\n }\n\n const client = createZitadelClient({\n baseUrl: this.meta.source,\n token: secret.project_secret,\n });\n\n let challenge;\n try {\n challenge = await client.initClaim(secret.project_id);\n } catch (error) {\n const claimed = alreadyClaimedDetails(error);\n if (claimed) {\n return this.alreadyClaimed({ project_id: secret.project_id, ...claimed });\n }\n throw error;\n }\n\n const deadline = claimDeadline({\n expiresAt: challenge.expires_at,\n timeoutSeconds: flags.timeout,\n now: Date.now(),\n });\n\n // A local server left on the default public base advertises a claim page\n // on a remote origin — the exact confusion this warning names. Cloud\n // servers legitimately use a console origin different from the API one,\n // so only a loopback API paired with a non-loopback claim page warns.\n if (isLoopbackUrl(this.meta.source) && !isLoopbackUrl(challenge.claim_url)) {\n consola.warn(\n `The server at ${this.meta.source} advertised a claim page on ${new URL(challenge.claim_url).origin}. ` +\n \"If you started that server yourself, set NEXTGEN_SERVER_PUBLIC_BASE to its reachable origin (e.g. http://localhost:8080).\",\n );\n }\n\n // Always show the link first, before attempting anything: it is the whole\n // instruction on its own, so a launch that never happens (headless box,\n // no `xdg-open`, `--no-open`, an agent) needs no separate path.\n consola.box({\n title: \"Finish in your browser\",\n message: `${challenge.claim_url}\\n\\nSign in there to attach this project to your team.`,\n style: { padding: 1, borderStyle: \"rounded\", borderColor: \"cyan\" },\n });\n\n const skipLaunch = flags[\"no-open\"] || nonInteractive;\n const opened = skipLaunch ? false : (await openInBrowser(challenge.claim_url)).opened;\n if (!opened) {\n consola.info(\"Open the link above to continue.\");\n }\n consola.start(\"Waiting for the browser step to finish\");\n\n const completed = await this.poll(client, secret.project_id, challenge.challenge_id, deadline);\n\n const next: ZitadelSecret = {\n ...secret,\n claimed_at: completed.claimed_at,\n team_id: completed.team_id,\n };\n // Unconditional: `--dry-run` never reaches here (it returns above), so a\n // claim that got this far really happened on the platform and the local\n // record must follow it.\n await writeZitadelSecret(cwd, next);\n consola.success(`Project attached to team ${completed.team_id}`);\n\n this.recordTelemetry({ claim_outcome: \"completed\", browser_opened: opened });\n return this.emit({\n status: \"ok\",\n data: {\n title: \"Zitadel project attached to a team.\",\n project_id: secret.project_id,\n team_id: completed.team_id,\n claimed_at: completed.claimed_at,\n dashboard_url: completed.dashboard_url,\n next_actions: [`Manage the project at ${completed.dashboard_url}.`],\n },\n });\n }\n\n /**\n * Polls `claim/status` until the browser leg lands or the link dies.\n *\n * The two failure modes read the same to a user (the link no longer works,\n * start another one), so they share an error shape and differ only in\n * wording — a `410` means the platform expired it, a passed deadline means\n * we stopped waiting first.\n */\n private async poll(\n client: ReturnType<typeof createZitadelClient>,\n projectId: string,\n challengeId: string,\n deadline: number,\n ): Promise<{ team_id: string; claimed_at: string; dashboard_url: string }> {\n let interval = INITIAL_POLL_MS;\n let polls = 0;\n\n while (Date.now() < deadline) {\n polls += 1;\n try {\n const status = await client.getClaimStatus(projectId, { challenge_id: challengeId });\n if (status.status === \"completed\") {\n this.recordTelemetry({ poll_count: polls });\n return status;\n }\n } catch (error) {\n if (error instanceof ApiError && error.status === 410) {\n this.recordTelemetry({ claim_outcome: \"expired\", poll_count: polls });\n throw expiredError(\"The link expired before the browser step finished.\");\n }\n if (error instanceof ApiError && error.status === 429) {\n // Polled too eagerly. Back off to the ceiling immediately rather\n // than easing towards it, so a rate limit is never compounded.\n interval = MAX_POLL_MS;\n } else {\n this.recordTelemetry({ poll_count: polls });\n throw error;\n }\n }\n\n // Never sleep past the deadline: the last wait should end the loop, not\n // overshoot it by up to a full interval.\n const remaining = deadline - Date.now();\n if (remaining <= 0) {\n break;\n }\n await sleep(Math.min(interval, remaining));\n interval = Math.min(interval * POLL_BACKOFF_FACTOR, MAX_POLL_MS);\n }\n\n this.recordTelemetry({ claim_outcome: \"timeout\", poll_count: polls });\n throw expiredError(\"Stopped waiting for the browser step to finish.\");\n }\n\n /**\n * The idempotent outcome: the project already belongs to a team, whether we\n * learned that locally or from a `409`. Reported as a skip, not an error, so\n * re-running the command (or an agent retrying it) is a clean no-op.\n */\n private alreadyClaimed(data: {\n project_id: string;\n team_id: string;\n claimed_at?: string;\n dashboard_url?: string;\n }): JsonEnvelope {\n this.recordTelemetry({ claim_outcome: \"already_claimed\" });\n consola.info(`This project already belongs to team ${data.team_id}.`);\n return this.emit({\n status: \"skipped\",\n reason: \"already-claimed\",\n data: { title: \"Zitadel project already belongs to a team.\", ...data },\n });\n }\n}\n\n/**\n * Extracts the owning team from a `409 proj.already_claimed` envelope. Reads\n * the body defensively rather than casting to the generated type: this is the\n * one place a malformed error body would otherwise turn a clean skip into a\n * crash.\n */\nfunction alreadyClaimedDetails(\n error: unknown,\n): { team_id: string; dashboard_url?: string } | undefined {\n if (!(error instanceof ApiError) || error.status !== 409 || !isObject(error.body)) {\n return undefined;\n }\n const details = error.body.details;\n if (!isObject(details) || typeof details.team_id !== \"string\") {\n return undefined;\n }\n return {\n team_id: details.team_id,\n dashboard_url: typeof details.dashboard_url === \"string\" ? details.dashboard_url : undefined,\n };\n}\n\nfunction expiredError(message: string): ZitadelError {\n return new ZitadelError(\"E_VALIDATION\", message, {\n hint: \"Links are valid for 10 minutes. Start a new one.\",\n nextCommands: [\"zitadel claim\"],\n });\n}\n\n/**\n * Loopback check on the URL's hostname: `localhost`, the whole `127.0.0.0/8`\n * block, or `[::1]` (how WHATWG URLs spell IPv6 loopback).\n */\nfunction isLoopbackUrl(value: string): boolean {\n try {\n const hostname = new URL(value).hostname;\n return hostname === \"localhost\" || hostname === \"[::1]\" || /^127(\\.\\d{1,3}){3}$/.test(hostname);\n } catch {\n return false;\n }\n}\n"],"mappings":"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"}
@@ -1,10 +1,10 @@
1
1
  import { E as ZitadelError, S as isObject, a as DEFAULT_LOCAL_SERVER_PORT, c as checkLocalServerHealth, h as readRuntimeMetadata, j as publicCliCommand, l as defaultLocalServerImageForCliVersion, m as localServerUrl, s as assertLocalStateWritable, t as BaseCommand } from "../oclif-CanO3zdt.mjs";
2
2
  import { n as claimCommand, r as claimState, t as claimAction } from "../claim-state-DlfD6Res.mjs";
3
- import { s as issuerFromPort, t as createOrca } from "../orca-BzQIQqzt.mjs";
3
+ import { s as issuerFromPort, t as createOrca } from "../orca-66vAxeIe.mjs";
4
4
  import { a as readProjectServer, c as readZitadelConfig, i as readPreset, l as readZitadelSecret, o as readRendererId, r as readDevelopmentIssuer, s as readUseCase, t as hasZitadelConfig } from "../project-CHhot85s.mjs";
5
5
  import { i as updateScaffold, t as SCHEMAS_DIR } from "../user-schema-DTuOsdKE.mjs";
6
6
  import { c as writeScaffoldManifest, n as detectPackageManager, o as hashScaffoldFile, s as readScaffoldManifest, t as addExactCommandFor } from "../package-manager-DFdLcDx1.mjs";
7
- import { d as assertServerPackageAvailable, o as imageAvailable, r as dockerAvailable } from "../docker-DcRGTOJa.mjs";
7
+ import { d as assertServerPackageAvailable, o as imageAvailable, r as dockerAvailable } from "../docker-Ev1Ggt9l.mjs";
8
8
  import { n as dockerRuntimeGuidance, r as dockerUnavailableMessage, t as listenersForPort } from "../ports-BM20XIZb.mjs";
9
9
  import { t as discoverManagedRuntimeProcesses } from "../processes-DKHPkU8O.mjs";
10
10
  import { chmod, readFile, readdir, rm, stat, writeFile } from "node:fs/promises";
@@ -1,5 +1,5 @@
1
1
  import { E as ZitadelError, t as BaseCommand } from "../oclif-CanO3zdt.mjs";
2
- import { a as AGENTS_HEADER, o as removeGuidanceSection, t as createOrca } from "../orca-BzQIQqzt.mjs";
2
+ import { a as AGENTS_HEADER, o as removeGuidanceSection, t as createOrca } from "../orca-66vAxeIe.mjs";
3
3
  import { c as readZitadelConfig, o as readRendererId } from "../project-CHhot85s.mjs";
4
4
  import { readFile, rename, rm, stat, writeFile } from "node:fs/promises";
5
5
  import { join } from "node:path";
@@ -1,5 +1,5 @@
1
1
  import { E as ZitadelError, h as readRuntimeMetadata, j as publicCliCommand, t as BaseCommand, x as resolveCwd } from "../oclif-CanO3zdt.mjs";
2
- import { a as followContainerLogs, f as binaryLogs, p as followBinaryLogs, t as containerLogs } from "../docker-DcRGTOJa.mjs";
2
+ import { a as followContainerLogs, f as binaryLogs, p as followBinaryLogs, t as containerLogs } from "../docker-Ev1Ggt9l.mjs";
3
3
  import { Flags } from "@oclif/core";
4
4
  //#region src/commands/logs.ts
5
5
  var Logs = class Logs extends BaseCommand {
@@ -1,5 +1,5 @@
1
1
  import { E as ZitadelError, _ as removeRuntimeMetadata, g as removeLocalData, h as readRuntimeMetadata, j as publicCliCommand, p as localContainerName, t as BaseCommand, x as resolveCwd } from "../oclif-CanO3zdt.mjs";
2
- import { g as stopBinaryRuntime, u as stopAndRemoveContainer } from "../docker-DcRGTOJa.mjs";
2
+ import { g as stopBinaryRuntime, u as stopAndRemoveContainer } from "../docker-Ev1Ggt9l.mjs";
3
3
  import { cancel, confirm, isCancel } from "@clack/prompts";
4
4
  //#region src/commands/reset.ts
5
5
  var Reset = class Reset extends BaseCommand {
@@ -2,7 +2,7 @@ import { n as brandingDesignLabel, t as BRANDING_DESIGN_INFO } from "../designs-
2
2
  import { D as toZitadelError, E as ZitadelError, T as stableStringify, j as publicCliCommand, k as normalizePublicCliProse, n as DEFAULT_SERVER, t as BaseCommand, u as detectHealthyLocalServer } from "../oclif-CanO3zdt.mjs";
3
3
  import { n as claimCommand, r as claimState, t as claimAction } from "../claim-state-DlfD6Res.mjs";
4
4
  import { n as customizeAndPublishActions, r as verifyLoginAction, t as brandingGuidanceAction } from "../journey-guidance-BGrOX_gT.mjs";
5
- import { i as RENDERER_IDS, n as inspectScaffoldTarget, r as AVAILABLE_RENDERER_IDS, s as issuerFromPort, t as createOrca } from "../orca-BzQIQqzt.mjs";
5
+ import { i as RENDERER_IDS, n as inspectScaffoldTarget, r as AVAILABLE_RENDERER_IDS, s as issuerFromPort, t as createOrca } from "../orca-66vAxeIe.mjs";
6
6
  import { n as hasZitadelSecret, t as hasZitadelConfig } from "../project-CHhot85s.mjs";
7
7
  import { a as updateState, t as SCHEMAS_DIR } from "../user-schema-DTuOsdKE.mjs";
8
8
  import { a as runPackageCommand, c as writeScaffoldManifest, i as installCommandFor, n as detectPackageManager, r as devCommandFor } from "../package-manager-DFdLcDx1.mjs";
@@ -1,5 +1,5 @@
1
1
  import { D as toZitadelError, E as ZitadelError, a as DEFAULT_LOCAL_SERVER_PORT, c as checkLocalServerHealth, d as ensureContainerIdentity, f as ensureLocalState, h as readRuntimeMetadata, j as publicCliCommand, l as defaultLocalServerImageForCliVersion, m as localServerUrl, p as localContainerName, t as BaseCommand, y as writeRuntimeMetadata } from "../oclif-CanO3zdt.mjs";
2
- import { c as metadataFromStart, f as binaryLogs, g as stopBinaryRuntime, h as startBinaryRuntime, i as ensureImage, l as startContainer, m as isProcessRunning, n as currentUser, r as dockerAvailable, s as inspectContainer, u as stopAndRemoveContainer } from "../docker-DcRGTOJa.mjs";
2
+ import { c as metadataFromStart, f as binaryLogs, g as stopBinaryRuntime, h as startBinaryRuntime, i as ensureImage, l as startContainer, m as isProcessRunning, n as currentUser, r as dockerAvailable, s as inspectContainer, u as stopAndRemoveContainer } from "../docker-Ev1Ggt9l.mjs";
3
3
  import { n as dockerRuntimeGuidance, r as dockerUnavailableMessage, t as listenersForPort } from "../ports-BM20XIZb.mjs";
4
4
  import { Flags } from "@oclif/core";
5
5
  import { setTimeout } from "node:timers/promises";
@@ -2,7 +2,7 @@ import { c as checkLocalServerHealth, h as readRuntimeMetadata, j as publicCliCo
2
2
  import { n as claimCommand, r as claimState, t as claimAction } from "../claim-state-DlfD6Res.mjs";
3
3
  import { n as customizeAndPublishActions, r as verifyLoginAction } from "../journey-guidance-BGrOX_gT.mjs";
4
4
  import { a as readProjectServer, c as readZitadelConfig, l as readZitadelSecret, n as hasZitadelSecret, r as readDevelopmentIssuer, t as hasZitadelConfig } from "../project-CHhot85s.mjs";
5
- import { m as isProcessRunning, s as inspectContainer } from "../docker-DcRGTOJa.mjs";
5
+ import { m as isProcessRunning, s as inspectContainer } from "../docker-Ev1Ggt9l.mjs";
6
6
  import { createZitadelClient } from "@zitadel/api/client";
7
7
  //#region src/commands/status.ts
8
8
  /**
@@ -1,5 +1,5 @@
1
1
  import { E as ZitadelError, h as readRuntimeMetadata, j as publicCliCommand, p as localContainerName, t as BaseCommand, x as resolveCwd } from "../oclif-CanO3zdt.mjs";
2
- import { g as stopBinaryRuntime, u as stopAndRemoveContainer } from "../docker-DcRGTOJa.mjs";
2
+ import { g as stopBinaryRuntime, u as stopAndRemoveContainer } from "../docker-Ev1Ggt9l.mjs";
3
3
  import { t as discoverManagedRuntimeProcesses } from "../processes-DKHPkU8O.mjs";
4
4
  import { Flags } from "@oclif/core";
5
5
  //#region src/commands/stop.ts
@@ -40,7 +40,8 @@ async function startBinaryRuntime(spec) {
40
40
  env: {
41
41
  ...process.env,
42
42
  NEXTGEN_SERVER_ADDRESS: `:${String(spec.port)}`,
43
- NEXTGEN_SERVER_DATA_DIR: spec.dataDir
43
+ NEXTGEN_SERVER_DATA_DIR: spec.dataDir,
44
+ NEXTGEN_SERVER_PUBLIC_BASE: spec.serverUrl
44
45
  },
45
46
  stdio: [
46
47
  "ignore",
@@ -236,7 +237,9 @@ function dockerRunArgs(spec) {
236
237
  "--env",
237
238
  `NEXTGEN_SERVER_ADDRESS=:${CONTAINER_HTTP_PORT}`,
238
239
  "--env",
239
- `NEXTGEN_SERVER_DATA_DIR=${CONTAINER_DATA_DIR}`
240
+ `NEXTGEN_SERVER_DATA_DIR=${CONTAINER_DATA_DIR}`,
241
+ "--env",
242
+ `NEXTGEN_SERVER_PUBLIC_BASE=http://localhost:${spec.port}`
240
243
  ];
241
244
  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}`);
242
245
  args.push(spec.image);
@@ -430,4 +433,4 @@ function dockerError(action, args, cause) {
430
433
  //#endregion
431
434
  export { followContainerLogs as a, metadataFromStart as c, assertServerPackageAvailable as d, binaryLogs as f, stopBinaryRuntime as g, startBinaryRuntime as h, ensureImage as i, startContainer as l, isProcessRunning as m, currentUser as n, imageAvailable as o, followBinaryLogs as p, dockerAvailable as r, inspectContainer as s, containerLogs as t, stopAndRemoveContainer as u };
432
435
 
433
- //# sourceMappingURL=docker-DcRGTOJa.mjs.map
436
+ //# sourceMappingURL=docker-Ev1Ggt9l.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"docker-Ev1Ggt9l.mjs","names":[],"sources":["../src/lib/local-server/binary.ts","../src/lib/local-server/docker.ts"],"sourcesContent":["import { spawn } from \"node:child_process\";\nimport { createReadStream } from \"node:fs\";\nimport { mkdir, open, readFile, stat } from \"node:fs/promises\";\nimport { createRequire } from \"node:module\";\nimport { dirname, join } from \"node:path\";\n\nimport { ZitadelError } from \"../errors\";\nimport {\n type BinaryRuntimeMetadata,\n type RuntimeMetadata,\n checkLocalServerHealth,\n} from \"./runtime\";\n\nexport const SERVER_NPM_PACKAGE = \"@zitadel/server\";\nconst START_COMMAND_ENV = \"ZITADEL_SERVER_BINARY\";\nconst START_COMMAND_VERSION_ENV = \"ZITADEL_SERVER_BINARY_VERSION\";\nconst STOP_TIMEOUT_MS = 10_000;\nconst STOP_KILL_TIMEOUT_MS = 2_000;\n\nconst require = createRequire(import.meta.url);\n\nexport type BinaryRunSpec = {\n cliVersion: string;\n dataDir: string;\n logPath: string;\n port: number;\n serverUrl: string;\n};\n\nexport type StopBinaryRuntimeResult = Readonly<{\n pid: number;\n signal?: NodeJS.Signals;\n status: \"failed\" | \"stale\" | \"stopped\";\n target: \"process\" | \"process-group\";\n}>;\n\nexport function resolveServerCommand(env: NodeJS.ProcessEnv = process.env): {\n command: string;\n args: string[];\n serverPackage: string;\n serverVersion: string;\n} {\n if (env[START_COMMAND_ENV]) {\n return {\n command: env[START_COMMAND_ENV],\n args: [],\n serverPackage: SERVER_NPM_PACKAGE,\n serverVersion: env[START_COMMAND_VERSION_ENV]?.trim() || \"override\",\n };\n }\n\n const manifestPath = resolveServerPackageManifest();\n const manifest = require(manifestPath) as { version?: unknown };\n return {\n command: process.execPath,\n args: [join(dirname(manifestPath), \"bin\", \"zitadel-server.js\")],\n serverPackage: SERVER_NPM_PACKAGE,\n serverVersion: typeof manifest.version === \"string\" ? manifest.version : \"unknown\",\n };\n}\n\nexport async function startBinaryRuntime(spec: BinaryRunSpec): Promise<BinaryRuntimeMetadata> {\n const command = resolveServerCommand();\n await mkdir(dirname(spec.logPath), { recursive: true, mode: 0o700 });\n const log = await open(spec.logPath, \"a\", 0o600);\n try {\n const child = spawn(command.command, command.args, {\n detached: true,\n env: {\n ...process.env,\n NEXTGEN_SERVER_ADDRESS: `:${String(spec.port)}`,\n NEXTGEN_SERVER_DATA_DIR: spec.dataDir,\n // Browser-facing URLs (claim, dashboard) must point at this local\n // server, not the cloud default the server config falls back to.\n NEXTGEN_SERVER_PUBLIC_BASE: spec.serverUrl,\n },\n stdio: [\"ignore\", log.fd, log.fd],\n });\n child.unref();\n if (!child.pid) {\n throw new Error(\"server process did not expose a pid\");\n }\n return {\n schema_version: 1,\n backend: \"binary\",\n pid: child.pid,\n command: [command.command, ...command.args].join(\" \"),\n log_path: spec.logPath,\n server_package: command.serverPackage,\n server_version: command.serverVersion,\n port: spec.port,\n server_url: spec.serverUrl,\n data_dir: spec.dataDir,\n created_at: new Date().toISOString(),\n cli_version: spec.cliVersion,\n };\n } finally {\n await log.close();\n }\n}\n\nexport async function stopBinaryRuntime(pid: number): Promise<StopBinaryRuntimeResult> {\n if (!isProcessRunning(pid)) {\n return { pid, status: \"stale\", target: \"process\" };\n }\n const term = signalRuntime(pid, \"SIGTERM\");\n if (!term.sent) {\n return { pid, status: \"stale\", target: term.target };\n }\n if (await waitForExit(pid, STOP_TIMEOUT_MS)) {\n return { pid, status: \"stopped\", target: term.target, signal: \"SIGTERM\" };\n }\n\n if (isProcessRunning(pid)) {\n const kill = signalRuntime(pid, \"SIGKILL\");\n if (kill.sent && (await waitForExit(pid, STOP_KILL_TIMEOUT_MS))) {\n return { pid, status: \"stopped\", target: kill.target, signal: \"SIGKILL\" };\n }\n }\n return { pid, status: \"failed\", target: term.target, signal: \"SIGKILL\" };\n}\n\nexport function isProcessRunning(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch (error) {\n return isErrno(error, \"EPERM\");\n }\n}\n\nexport async function binaryLogs(logPath: string, tail: number): Promise<string> {\n let content = \"\";\n try {\n content = await readFile(logPath, \"utf8\");\n } catch (error) {\n if (isErrno(error, \"ENOENT\")) {\n return \"\";\n }\n throw error;\n }\n const lines = content.split(/\\r?\\n/);\n const hasTrailingNewline = lines.at(-1) === \"\";\n const body = hasTrailingNewline ? lines.slice(0, -1) : lines;\n const selected = body.slice(-tail).join(\"\\n\");\n return selected.length === 0 ? \"\" : `${selected}${hasTrailingNewline ? \"\\n\" : \"\"}`;\n}\n\nexport async function followBinaryLogs(logPath: string, tail: number): Promise<void> {\n process.stdout.write(await binaryLogs(logPath, tail));\n let offset = await fileSize(logPath);\n await new Promise<void>((resolveFollow, reject) => {\n const interval = setInterval(() => {\n void (async () => {\n try {\n const size = await fileSize(logPath);\n if (size > offset) {\n const stream = createReadStream(logPath, { start: offset, end: size - 1 });\n stream.pipe(process.stdout, { end: false });\n offset = size;\n }\n } catch (error) {\n reject(error);\n }\n })();\n }, 1000);\n const stop = () => {\n clearInterval(interval);\n resolveFollow();\n };\n process.once(\"SIGINT\", stop);\n process.once(\"SIGTERM\", stop);\n });\n}\n\nexport async function assertServerPackageAvailable(): Promise<string> {\n try {\n const command = resolveServerCommand();\n return command.serverVersion;\n } catch (error) {\n throw new ZitadelError(\"E_VALIDATION\", \"Zitadel server npm package is not available\", {\n hint: \"Reinstall @zitadel/cli so npm can install @zitadel/server and its platform package.\",\n nextCommands: [\"npm install @zitadel/cli@alpha\"],\n details: { package: SERVER_NPM_PACKAGE, message: errorMessage(error) },\n });\n }\n}\n\nexport async function binaryRuntimeHealthy(runtime: RuntimeMetadata): Promise<boolean> {\n return (\n runtime.backend === \"binary\" &&\n isProcessRunning(runtime.pid) &&\n (await checkLocalServerHealth(runtime.server_url))\n );\n}\n\nfunction resolveServerPackageManifest(): string {\n try {\n return require.resolve(`${SERVER_NPM_PACKAGE}/package.json`);\n } catch (error) {\n throw new Error(`Missing ${SERVER_NPM_PACKAGE}. Reinstall @zitadel/cli.`, { cause: error });\n }\n}\n\nasync function fileSize(path: string): Promise<number> {\n try {\n return (await stat(path)).size;\n } catch (error) {\n if (isErrno(error, \"ENOENT\")) {\n return 0;\n }\n throw error;\n }\n}\n\nfunction isErrno(error: unknown, code: string): boolean {\n return (\n typeof error === \"object\" &&\n error !== null &&\n \"code\" in error &&\n (error as { code?: unknown }).code === code\n );\n}\n\nfunction signalProcess(pid: number, signal: NodeJS.Signals): boolean {\n try {\n process.kill(pid, signal);\n return true;\n } catch (error) {\n if (isErrno(error, \"ESRCH\")) {\n return false;\n }\n throw error;\n }\n}\n\nfunction signalRuntime(\n pid: number,\n signal: NodeJS.Signals,\n): { sent: boolean; target: \"process\" | \"process-group\" } {\n if (process.platform !== \"win32\") {\n try {\n process.kill(-pid, signal);\n return { sent: true, target: \"process-group\" };\n } catch (error) {\n if (!isErrno(error, \"ESRCH\")) {\n throw error;\n }\n }\n }\n return { sent: signalProcess(pid, signal), target: \"process\" };\n}\n\nasync function waitForExit(pid: number, timeoutMs: number): Promise<boolean> {\n const deadline = Date.now() + timeoutMs;\n while (Date.now() < deadline) {\n if (!isProcessRunning(pid)) {\n return true;\n }\n await delay(200);\n }\n return !isProcessRunning(pid);\n}\n\nfunction errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\nfunction delay(ms: number): Promise<void> {\n return new Promise((resolveDelay) => setTimeout(resolveDelay, ms));\n}\n","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 // Browser-facing URLs (claim, dashboard) must use the host-visible port\n // published above, not the cloud default the server config falls back to.\n \"--env\",\n `NEXTGEN_SERVER_PUBLIC_BASE=http://localhost:${spec.port}`,\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 backend: \"docker\",\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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"}
@@ -2435,8 +2435,9 @@ export default defineNuxtPlugin(() => {
2435
2435
  isAuthenticated: true,
2436
2436
  session: {
2437
2437
  userId: auth.session.userId,
2438
- email: auth.session.email,
2439
- name: auth.session.name,
2438
+ identifier: auth.session.identifier,
2439
+ identifierProperty: auth.session.identifierProperty,
2440
+ display: auth.session.display,
2440
2441
  },
2441
2442
  }
2442
2443
  : { isAuthenticated: false, session: null };
@@ -3913,4 +3914,4 @@ function isErrno(error, code) {
3913
3914
  //#endregion
3914
3915
  export { AGENTS_HEADER as a, RENDERER_IDS as i, inspectScaffoldTarget as n, removeGuidanceSection as o, AVAILABLE_RENDERER_IDS as r, issuerFromPort as s, createOrca as t };
3915
3916
 
3916
- //# sourceMappingURL=orca-BzQIQqzt.mjs.map
3917
+ //# sourceMappingURL=orca-66vAxeIe.mjs.map