import { spinner } from "@clack/prompts";
import { spawn } from "node:child_process";

//#region \0rolldown/runtime.js
var __defProp = Object.defineProperty;
var __exportAll = (all, no_symbols) => {
	let target = {};
	for (var name in all) {
		__defProp(target, name, {
			get: all[name],
			enumerable: true
		});
	}
	if (!no_symbols) {
		__defProp(target, Symbol.toStringTag, { value: "Module" });
	}
	return target;
};

//#endregion
//#region src/local/embed-config.ts
const DEFAULTS = {
	cliName: "cdkl",
	binaryName: "cdkl",
	productName: "cdk-local",
	resourceNamePrefix: "cdkl",
	awsBindMountPath: "/cdk-local-aws",
	envPrefix: "CDKL",
	sigV4StrictByDefault: false,
	sigV4OptFlag: "--strict-sigv4"
};
let current = DEFAULTS;
/**
* Resolve and install the active embed config. Called once per command
* factory with the host's overrides (or `undefined` for native cdkl
* behavior). Idempotent: re-calling with the same overrides is a no-op,
* which is why all four factories may safely set the same config.
*/
function setEmbedConfig(config) {
	current = {
		cliName: config?.cliName ?? DEFAULTS.cliName,
		binaryName: config?.binaryName ?? DEFAULTS.binaryName,
		productName: config?.productName ?? DEFAULTS.productName,
		resourceNamePrefix: config?.resourceNamePrefix ?? DEFAULTS.resourceNamePrefix,
		awsBindMountPath: config?.awsBindMountPath ?? DEFAULTS.awsBindMountPath,
		envPrefix: config?.envPrefix ?? DEFAULTS.envPrefix,
		sigV4StrictByDefault: config?.sigV4StrictByDefault ?? DEFAULTS.sigV4StrictByDefault,
		sigV4OptFlag: config?.sigV4OptFlag ?? DEFAULTS.sigV4OptFlag
	};
}
/** The active resolved embed config. */
function getEmbedConfig() {
	return current;
}
/** Restore cdk-local defaults. Primarily a test-isolation helper. */
function resetEmbedConfig() {
	current = DEFAULTS;
}

//#endregion
//#region src/utils/logger.ts
/**
* ANSI color codes
*
* Kept internal — `ConsoleLogger.formatMessage` references these for the
* verbose/compact mode level prefixes.
*/
const colors = {
	reset: "\x1B[0m",
	bright: "\x1B[1m",
	dim: "\x1B[2m",
	red: "\x1B[31m",
	green: "\x1B[32m",
	yellow: "\x1B[33m",
	blue: "\x1B[34m",
	cyan: "\x1B[36m",
	gray: "\x1B[90m"
};
function formatTimestamp() {
	return (/* @__PURE__ */ new Date()).toISOString();
}
/**
* Console logger implementation
*
* Supports two output modes:
* - verbose (debug level): timestamps, module prefixes, all details
* - compact (info level): clean output without timestamps or prefixes
*/
var ConsoleLogger = class {
	level;
	useColors;
	constructor(level = "info", useColors = true) {
		this.level = level;
		this.useColors = useColors;
	}
	shouldLog(level) {
		const levels = [
			"debug",
			"info",
			"warn",
			"error"
		];
		const currentLevelIndex = levels.indexOf(this.level);
		return levels.indexOf(level) >= currentLevelIndex;
	}
	formatMessage(level, message, ...args) {
		const formattedArgs = args.length > 0 ? " " + args.map((a) => JSON.stringify(a)).join(" ") : "";
		if (this.level === "debug") {
			const timestamp = formatTimestamp();
			const levelStr = level.toUpperCase().padEnd(5);
			if (this.useColors) {
				const levelColor = {
					debug: colors.gray,
					info: colors.blue,
					warn: colors.yellow,
					error: colors.red
				}[level];
				return `${colors.dim}${timestamp}${colors.reset} ${levelColor}${levelStr}${colors.reset} ${message}${formattedArgs}`;
			}
			return `${timestamp} ${levelStr} ${message}${formattedArgs}`;
		}
		if (this.useColors) {
			if (level === "error") return `${colors.red}${message}${formattedArgs}${colors.reset}`;
			if (level === "warn") return `${colors.yellow}${message}${formattedArgs}${colors.reset}`;
			return `${message}${formattedArgs}`;
		}
		return `${message}${formattedArgs}`;
	}
	emit(level, formatted) {
		if (level === "error") console.error(formatted);
		else if (level === "warn") console.warn(formatted);
		else if (level === "info") console.info(formatted);
		else console.debug(formatted);
	}
	debug(message, ...args) {
		if (this.shouldLog("debug")) this.emit("debug", this.formatMessage("debug", message, ...args));
	}
	info(message, ...args) {
		if (this.shouldLog("info")) this.emit("info", this.formatMessage("info", message, ...args));
	}
	warn(message, ...args) {
		if (this.shouldLog("warn")) this.emit("warn", this.formatMessage("warn", message, ...args));
	}
	error(message, ...args) {
		if (this.shouldLog("error")) this.emit("error", this.formatMessage("error", message, ...args));
	}
	setLevel(level) {
		this.level = level;
	}
	getLevel() {
		return this.level;
	}
	child(prefix) {
		return new ChildLogger(prefix, this.useColors);
	}
};
/**
* Child logger that always syncs level from global logger
*/
var ChildLogger = class extends ConsoleLogger {
	prefix;
	constructor(prefix, useColors) {
		super("info", useColors);
		this.prefix = prefix;
	}
	syncLevel() {
		if (globalLogger) this.setLevel(globalLogger.getLevel());
	}
	debug(message, ...args) {
		this.syncLevel();
		super.debug(`[${this.prefix}] ${message}`, ...args);
	}
	info(message, ...args) {
		this.syncLevel();
		const msg = this.getLevel() === "debug" ? `[${this.prefix}] ${message}` : message;
		super.info(msg, ...args);
	}
	warn(message, ...args) {
		this.syncLevel();
		const msg = this.getLevel() === "debug" ? `[${this.prefix}] ${message}` : message;
		super.warn(msg, ...args);
	}
	error(message, ...args) {
		this.syncLevel();
		const msg = this.getLevel() === "debug" ? `[${this.prefix}] ${message}` : message;
		super.error(msg, ...args);
	}
};
/**
* Resolve the initial log level from the `CDKL_LOG_LEVEL` env var, falling
* back to `'info'`. This is primarily an internal contract: `cdkl studio`
* spawns its single-shot `cdkl invoke` child with `CDKL_LOG_LEVEL=warn` so
* cdk-local's OWN synth / orchestration progress (toolkit "Successfully
* synthesized to ...", asset-bundling lines, info-level status) is silenced
* in the child — leaving the studio LOGS panel showing only the Lambda
* container's runtime logs (which stream straight from `docker logs` and are
* unaffected by this level) plus the response. `--verbose` still overrides to
* `debug` at the command layer. An invalid value is ignored.
*/
function resolveConfiguredLogLevel() {
	const env = process.env["CDKL_LOG_LEVEL"];
	if (env === "debug" || env === "info" || env === "warn" || env === "error") return env;
	return "info";
}
let globalLogger = null;
function getLogger() {
	if (!globalLogger) globalLogger = new ConsoleLogger(resolveConfiguredLogLevel());
	return globalLogger;
}

//#endregion
//#region src/utils/docker-cmd.ts
var docker_cmd_exports = /* @__PURE__ */ __exportAll({
	formatDockerLoginError: () => formatDockerLoginError,
	getDockerCmd: () => getDockerCmd,
	runDockerForeground: () => runDockerForeground,
	runDockerStreaming: () => runDockerStreaming,
	spawnForeground: () => spawnForeground,
	spawnStreaming: () => spawnStreaming
});
/**
* Shared helpers for invoking the docker-compatible CLI binary across cdk-local.
*
* Two parity decisions with `aws-cdk-cli`'s `cdk-assets-lib`:
*   1. `CDK_DOCKER` env var swaps the binary so podman / finch users can
*      run cdk-local without code changes (`CDK_DOCKER=podman cdkl invoke`).
*   2. `runDockerStreaming` uses streaming spawn rather than `execFile`'s
*      buffered `maxBuffer` ceiling. BuildKit's progress output can run to
*      tens of MB on multi-stage builds with `# syntax=docker/dockerfile:1`
*      frontend downloads + heredoc / `RUN --mount=...` features; the 50 MB
*      `execFile` ceiling cdk-local used to set silently killed those builds
*      with `ERR_CHILD_PROCESS_STDIO_MAXBUFFER`.
*
* Output handling: stdout/stderr are collected in memory unconditionally so
* `runDockerStreaming` can return them to the caller for error wrapping.
* When the logger is at debug level (i.e. the user passed `--verbose`),
* the chunks are ALSO mirrored to `process.stdout` / `process.stderr` so
* the user sees live build progress.
*/
/**
* Return the docker-compatible CLI binary to invoke. Matches CDK CLI:
* `CDK_DOCKER` env var overrides the default `docker` so users on
* podman / finch / nerdctl can swap without changing cdk-local code.
*/
function getDockerCmd() {
	const override = process.env["CDK_DOCKER"];
	return override && override.length > 0 ? override : "docker";
}
/**
* Spawn a docker-compatible CLI binary (resolved via `getDockerCmd`) with
* streaming I/O. Collects stdout/stderr in memory and resolves with both
* on exit code 0; rejects with a `SpawnError` carrying both streams on any
* non-zero exit so the caller can wrap with its own error class without
* losing the upstream output.
*
* No `maxBuffer` ceiling: BuildKit progress output frequently exceeds the
* `child_process.execFile` default of 1 MB (cdk-local previously bumped to 50 MB
* but BuildKit + frontend pulls can still exceed that on first-time builds).
*/
async function runDockerStreaming(args, options = {}) {
	return spawnStreaming(getDockerCmd(), args, options);
}
/**
* Generic streaming spawn — used by `runDockerStreaming` AND by the
* `executable` source mode in `docker-build.ts` (which runs an arbitrary
* user-supplied build command, not docker).
*/
async function spawnStreaming(cmd, args, options = {}) {
	const streamLive = options.streamLive ?? getLogger().getLevel() === "debug";
	const env = options.env ? mergeEnv(options.env) : void 0;
	const spin = startProgressSpinner(options.progressLabel, streamLive);
	return new Promise((resolve, reject) => {
		let child;
		try {
			child = spawn(cmd, args, {
				cwd: options.cwd,
				env,
				stdio: [
					options.input ? "pipe" : "ignore",
					"pipe",
					"pipe"
				]
			});
		} catch (err) {
			stopProgressSpinner(spin, options.progressLabel);
			reject(err);
			return;
		}
		const stdoutChunks = [];
		const stderrChunks = [];
		child.stdout.on("data", (chunk) => {
			stdoutChunks.push(chunk);
			if (streamLive) process.stdout.write(chunk);
		});
		child.stderr.on("data", (chunk) => {
			stderrChunks.push(chunk);
			if (streamLive) process.stderr.write(chunk);
		});
		child.once("error", (err) => {
			stopProgressSpinner(spin, options.progressLabel);
			if (err.code === "ENOENT") {
				const usingOverride = process.env["CDK_DOCKER"] === cmd && cmd !== "docker";
				reject(/* @__PURE__ */ new Error(usingOverride ? `Failed to find and execute '${cmd}' (resolved via CDK_DOCKER). Install '${cmd}' or unset CDK_DOCKER to fall back to 'docker'.` : `Failed to find and execute '${cmd}'. Install Docker (or set the 'CDK_DOCKER' environment variable to a compatible binary such as podman / finch).`));
			} else reject(err);
		});
		child.once("close", (code) => {
			const stdout = Buffer.concat(stdoutChunks).toString("utf-8");
			const stderr = Buffer.concat(stderrChunks).toString("utf-8");
			if (code === 0) {
				stopProgressSpinner(spin, options.progressLabel);
				resolve({
					stdout,
					stderr
				});
			} else {
				stopProgressSpinner(spin, options.progressLabel);
				const message = stderr.trim() || stdout.trim() || `${cmd} ${args[0] ?? ""} exited with code ${code}`;
				const err = new Error(message);
				err.stderr = stderr;
				err.stdout = stdout;
				err.exitCode = code;
				reject(err);
			}
		});
		if (options.input !== void 0) {
			child.stdin.on("error", () => {});
			child.stdin.write(options.input);
			child.stdin.end();
		}
	});
}
/**
* Start an interactive clack spinner for the spawn, but only when the
* current shell would actually render it (TTY) and the caller isn't
* already streaming live output. Returns `undefined` when either
* precondition fails — `stopProgressSpinner` then is a no-op.
*
* Test seam: the integration with `@clack/prompts` is mocked in
* `tests/unit/utils/docker-cmd-progress-spinner.test.ts`.
*/
function startProgressSpinner(label, streamLive) {
	if (label === void 0 || streamLive || process.stdout.isTTY !== true) return void 0;
	const spin = spinner();
	spin.start(label);
	return spin;
}
function stopProgressSpinner(spin, label) {
	if (spin === void 0) return;
	spin.stop(label ?? "");
}
/**
* Spawn a docker-compatible CLI binary (resolved via `getDockerCmd`) attached
* to the parent process's stdio so the user sees live output (`docker pull`
* layer progress, `docker login` interactive prompts that should never fire
* with `--password-stdin` but still safe to inherit, etc.). Resolves on exit
* code 0; rejects with a plain `Error` carrying the exit code on any non-zero
* exit, so the caller can wrap with its own error class.
*
* Differs from {@link runDockerStreaming} in two ways:
*   1. `stdio: 'inherit'` — output is NOT captured, so terminal control codes
*      (color, progress bar overwrites) flow through unchanged. This is the
*      load-bearing reason for the split: `docker pull`'s progress bars only
*      animate properly when stdout is a real TTY connected to the parent.
*   2. No `input` / `streamLive` options — inherit-mode has nothing to
*      capture and nothing to mirror.
*
* Used by the `--verbose`-mode `docker pull` plumbing in `docker-runner.ts`
* and `ecr-puller.ts` (visible layer progress). Non-verbose pulls go through
* {@link runDockerStreaming} so stderr can be folded into the error message.
*/
async function runDockerForeground(args, options = {}) {
	return spawnForeground(getDockerCmd(), args, options);
}
/**
* Foreground (stdio-inherit) spawn — the inherit-mode counterpart to
* {@link spawnStreaming}. Used by {@link runDockerForeground} for docker-CLI
* subprocesses.
*
* The ENOENT branch crafts a docker-specific install hint ("Install Docker
* (or set CDK_DOCKER ...)"), so non-docker callers reusing this helper
* would see a misleading error on missing-binary failures. Keep the binary
* docker-shaped, or update the ENOENT message before adding a non-docker
* call site.
*/
async function spawnForeground(cmd, args, options = {}) {
	const env = options.env ? mergeEnv(options.env) : void 0;
	return new Promise((resolve, reject) => {
		const child = spawn(cmd, args, {
			cwd: options.cwd,
			env,
			stdio: "inherit"
		});
		child.once("error", (err) => {
			if (err.code === "ENOENT") {
				const usingOverride = process.env["CDK_DOCKER"] === cmd && cmd !== "docker";
				reject(/* @__PURE__ */ new Error(usingOverride ? `Failed to find and execute '${cmd}' (resolved via CDK_DOCKER). Install '${cmd}' or unset CDK_DOCKER to fall back to 'docker'.` : `Failed to find and execute '${cmd}'. Install Docker (or set the 'CDK_DOCKER' environment variable to a compatible binary such as podman / finch).`));
			} else reject(/* @__PURE__ */ new Error(`${cmd} failed: ${err.message}`));
		});
		child.once("close", (code) => {
			if (code === 0) resolve();
			else reject(/* @__PURE__ */ new Error(`${cmd} exited with code ${code}`));
		});
	});
}
/**
* Format the stderr from a failed `docker login` so the surfaced cdk-local
* error gives the user an actionable workaround when the underlying
* failure is a credential-helper persistence bug (which has nothing to
* do with cdk-local, AWS, or IAM perms — the docker CLI itself fails to
* save the auth token to the platform's credential store). The most
* common shape is `osxkeychain` on macOS rejecting an overwrite for
* an existing entry, but `wincred` (Windows), `pass` (Linux), and
* `secretservice` (Linux) hit the same class of `Error saving
* credentials` failure, so the rewritten message stays platform-
* agnostic — `docker logout <endpoint>` is the correct recovery on
* every backend.
*
* Detected docker / docker-credential-* output patterns:
*   - `error storing credentials - err: exit status 1, out: \`The
*     specified item already exists in the keychain.\`` (osxkeychain)
*   - `Error saving credentials: ...` (any backend)
*
* Non-matching failures (genuine IAM / network / endpoint problems)
* pass through with just the stderr trimmed — the original message
* stays load-bearing for diagnosis.
*/
function formatDockerLoginError(stderr, endpoint) {
	const trimmed = stderr.trim();
	if (trimmed.includes("already exists in the keychain") || trimmed.includes("Error saving credentials")) return `docker's credential helper (osxkeychain on macOS / wincred on Windows / pass / secretservice on Linux) failed to persist the ECR auth token. The "already exists in the keychain" / "Error saving credentials" output is a known docker-credential-helpers issue — unrelated to ${getEmbedConfig().productName}, AWS credentials, or IAM perms. Quick fix: run \`docker logout ${endpoint}\` to clear the stale entry, then retry the ${getEmbedConfig().productName} command. Permanent fix: edit ~/.docker/config.json and remove (or empty) the platform-specific "credsStore" entry (e.g. "osxkeychain" → "" or "desktop" on macOS Docker Desktop). Original docker stderr: ${trimmed}`;
	return trimmed;
}
function mergeEnv(overrides) {
	const merged = { ...process.env };
	for (const [k, v] of Object.entries(overrides)) if (v === void 0) delete merged[k];
	else merged[k] = v;
	return merged;
}

//#endregion
export { runDockerStreaming as a, resolveConfiguredLogLevel as c, setEmbedConfig as d, runDockerForeground as i, getEmbedConfig as l, formatDockerLoginError as n, spawnStreaming as o, getDockerCmd as r, getLogger as s, docker_cmd_exports as t, resetEmbedConfig as u };
//# sourceMappingURL=docker-cmd-GcI_cqY5.js.map