UNPKG

prisma

Version:

The Prisma CLI: one binary for the ORM, Composer, and the Prisma Developer Platform.

15,165 lines • 552 kB
#!/usr/bin/env node
import { createRequire } from "node:module";
import process$1 from "node:process";
import { SERVICE_TOKEN_ENV_VAR, claimedExpiresAt, claimedIdentity, createCli, credentialWorkspaceId, credentialWorkspaceMismatchError, credentialsRequiredError, defineCommand, defineCommandFamily, defineConfigSection, defineSessionCommand, detectCI, emptyServiceTokenError, flag, loadConfig, noSessionForWorkspaceError, positional, readActiveAccessToken, realpathOr, resolveSectionOverChain, telemetryCommandGroup } from "@prisma/cli-engine";
import { createComposerFamily } from "@prisma/composer-cli/family";
import { ormCommandFamily } from "@prisma/orm-toolchain/cli";
import { CliStructuredError, notOk, ok } from "@prisma/cli-engine/protocol";
import { AuthError, createManagementApiClient, createManagementApiSdk } from "@prisma/management-api-sdk";
import os from "node:os";
import path from "node:path";
import { randomBytes, randomUUID } from "node:crypto";
import fs, { mkdir, readFile, readdir, rename, rm, rmdir, stat, unlink, writeFile } from "node:fs/promises";
import { mkdir as mkdir$1, readFile as readFile$1, writeFile as writeFile$1 } from "fs/promises";
import path$1 from "path";
import events from "node:events";
import http from "node:http";
import readline from "node:readline/promises";
import open from "open";
import { fstatSync, readFileSync, statSync } from "node:fs";
import { PnpmTool, YarnTool } from "@manypkg/tools";
import { Result, TaggedError, UnhandledException, matchError } from "better-result";
import { execFile, fork, spawn } from "node:child_process";
import { promisify } from "node:util";
import { ApiError, CancelledError, ComputeClient, streamLogs } from "@prisma/compute-sdk";
import { parse } from "dotenv";
import { fileURLToPath } from "node:url";
import { execa } from "execa";
import { Writable } from "node:stream";
import { pipeline } from "node:stream/promises";
import spawn$1 from "cross-spawn";
//#region \0rolldown/runtime.js
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
var __copyProps = (to, from, except, desc) => {
	if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
		key = keys[i];
		if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
			get: ((k) => from[k]).bind(null, key),
			enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
		});
	}
	return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
	value: mod,
	enumerable: true
}) : target, mod));
var __require = /* @__PURE__ */ createRequire(import.meta.url);
//#endregion
//#region ../cli/src/cli-name.ts
/**
* The CLI's user-facing identity, in one place: the binary on PATH is
* `prisma`, published by the `prisma` package, and every user-facing
* command string and notice consumes this constant rather than
* restating the name. The `@prisma/cli` package installs the same shell
* under the name `prisma-cli`; what a user is told to type is the
* unified binary's name.
*/
const CLI_NAME = "prisma";
/** The unified CLI's docs section (also the update-check fallback
*  instruction URL). */
const CLI_DOCS_URL = "https://www.prisma.io/docs/cli";
/**
* Base URL for structured-error documentation links. The engine composes
* each diagnostic's docsUrl as base + code; every code is documented at
* this page (registry: docs/reference/error-reference.md).
*/
const DOCS_ERRORS_BASE_URL = "https://www.prisma.io/docs/cli/error-reference/";
//#endregion
//#region ../cli/src/auth/client.ts
const CLIENT_ID = "cmm3lndn701oo0uefvxzo0ivw";
const AUTH_FILE_ENV_VAR = "PRISMA_COMPUTE_AUTH_FILE";
/**
* The redirect the OAuth client is registered with. `performLogin`
* replaces it with its own ephemeral callback server's port; the
* refreshing client never reads it.
*/
const DEFAULT_REDIRECT_URI = "http://localhost/auth/callback";
function getApiBaseUrl(env = process.env) {
	return env.PRISMA_MANAGEMENT_API_URL?.trim() || "https://api.prisma.io";
}
function getAuthBaseUrl(env = process.env) {
	return env.PRISMA_AUTH_BASE_URL?.trim() || "https://auth.prisma.io";
}
function getAuthFilePath(env = process.env) {
	const configured = env[AUTH_FILE_ENV_VAR];
	if (configured?.trim()) return path.resolve(configured);
	return defaultAuthFilePath(env);
}
function defaultAuthFilePath(env = process.env) {
	if (process.platform === "darwin") return path.join(os.homedir(), "Library", "Application Support", "prisma", "auth.json");
	if (process.platform === "win32") {
		const appData = env.APPDATA ?? path.join(os.homedir(), "AppData", "Roaming");
		return path.join(appData, "prisma", "auth.json");
	}
	const xdgConfigHome = env.XDG_CONFIG_HOME ?? path.join(os.homedir(), ".config");
	return path.join(xdgConfigHome, "prisma", "auth.json");
}
//#endregion
//#region ../../node_modules/.pnpm/xdg-app-paths@8.3.0/node_modules/xdg-app-paths/dist/cjs/lib/XDGAppPaths.js
var require_XDGAppPaths = /* @__PURE__ */ __commonJSMin(((exports) => {
	exports.__esModule = true;
	exports.Adapt = void 0;
	function isBoolean(t) {
		return typeOf(t) === "boolean";
	}
	function isObject(t) {
		return typeOf(t) === "object";
	}
	function isString(t) {
		return typeOf(t) === "string";
	}
	function typeOf(t) {
		return typeof t;
	}
	function Adapt(adapter_) {
		var meta = adapter_.meta, path = adapter_.path, xdg = adapter_.xdg;
		return { XDGAppPaths: new (function() {
			function XDGAppPaths_(options_) {
				if (options_ === void 0) options_ = {};
				var _a, _b, _c;
				function XDGAppPaths(options) {
					if (options === void 0) options = {};
					return new XDGAppPaths_(options);
				}
				var options = isObject(options_) ? options_ : { name: options_ };
				var suffix = (_a = options.suffix) !== null && _a !== void 0 ? _a : "";
				var isolated_ = (_b = options.isolated) !== null && _b !== void 0 ? _b : true;
				var namePriorityList = [
					options.name,
					meta.pkgMainFilename(),
					meta.mainFilename()
				];
				var name = path.parse(((_c = namePriorityList.find(function(e) {
					return isString(e);
				})) !== null && _c !== void 0 ? _c : "$eval") + suffix).name;
				XDGAppPaths.$name = function $name() {
					return name;
				};
				XDGAppPaths.$isolated = function $isolated() {
					return isolated_;
				};
				function isIsolated(dirOptions) {
					var _a;
					dirOptions = dirOptions !== null && dirOptions !== void 0 ? dirOptions : { isolated: isolated_ };
					return isBoolean(dirOptions) ? dirOptions : (_a = dirOptions.isolated) !== null && _a !== void 0 ? _a : isolated_;
				}
				function finalPathSegment(dirOptions) {
					return isIsolated(dirOptions) ? name : "";
				}
				XDGAppPaths.cache = function cache(dirOptions) {
					return path.join(xdg.cache(), finalPathSegment(dirOptions));
				};
				XDGAppPaths.config = function config(dirOptions) {
					return path.join(xdg.config(), finalPathSegment(dirOptions));
				};
				XDGAppPaths.data = function data(dirOptions) {
					return path.join(xdg.data(), finalPathSegment(dirOptions));
				};
				XDGAppPaths.runtime = function runtime(dirOptions) {
					return xdg.runtime() ? path.join(xdg.runtime(), finalPathSegment(dirOptions)) : void 0;
				};
				XDGAppPaths.state = function state(dirOptions) {
					return path.join(xdg.state(), finalPathSegment(dirOptions));
				};
				XDGAppPaths.configDirs = function configDirs(dirOptions) {
					return xdg.configDirs().map(function(s) {
						return path.join(s, finalPathSegment(dirOptions));
					});
				};
				XDGAppPaths.dataDirs = function dataDirs(dirOptions) {
					return xdg.dataDirs().map(function(s) {
						return path.join(s, finalPathSegment(dirOptions));
					});
				};
				return XDGAppPaths;
			}
			return XDGAppPaths_;
		}())() };
	}
	exports.Adapt = Adapt;
}));
//#endregion
//#region ../../node_modules/.pnpm/xdg-portable@10.6.0/node_modules/xdg-portable/dist/cjs/lib/XDG.js
var require_XDG = /* @__PURE__ */ __commonJSMin(((exports) => {
	var __spreadArray = exports && exports.__spreadArray || function(to, from) {
		for (var i = 0, il = from.length, j = to.length; i < il; i++, j++) to[j] = from[i];
		return to;
	};
	exports.__esModule = true;
	exports.Adapt = void 0;
	function Adapt(adapter_) {
		var env = adapter_.env, osPaths = adapter_.osPaths, path = adapter_.path;
		var isMacOS = /^darwin$/i.test(adapter_.process.platform);
		var isWinOS = /^win/i.test(adapter_.process.platform);
		function baseDir() {
			return osPaths.home() || osPaths.temp();
		}
		function valOrPath(val, pathSegments) {
			return val || path.join.apply(path, pathSegments);
		}
		var linux = function() {
			var cache = function() {
				return valOrPath(env.get("XDG_CACHE_HOME"), [baseDir(), ".cache"]);
			};
			var config = function() {
				return valOrPath(env.get("XDG_CONFIG_HOME"), [baseDir(), ".config"]);
			};
			var data = function() {
				return valOrPath(env.get("XDG_DATA_HOME"), [
					baseDir(),
					".local",
					"share"
				]);
			};
			var runtime = function() {
				return env.get("XDG_RUNTIME_DIR") || void 0;
			};
			var state = function() {
				return valOrPath(env.get("XDG_STATE_HOME"), [
					baseDir(),
					".local",
					"state"
				]);
			};
			return {
				cache,
				config,
				data,
				runtime,
				state
			};
		};
		var macos = function() {
			var cache = function() {
				return valOrPath(env.get("XDG_CACHE_HOME"), [
					baseDir(),
					"Library",
					"Caches"
				]);
			};
			var config = function() {
				return valOrPath(env.get("XDG_CONFIG_HOME"), [
					baseDir(),
					"Library",
					"Preferences"
				]);
			};
			var data = function() {
				return valOrPath(env.get("XDG_DATA_HOME"), [
					baseDir(),
					"Library",
					"Application Support"
				]);
			};
			var runtime = function() {
				return env.get("XDG_RUNTIME_DIR") || void 0;
			};
			var state = function() {
				return valOrPath(env.get("XDG_STATE_HOME"), [
					baseDir(),
					"Library",
					"State"
				]);
			};
			return {
				cache,
				config,
				data,
				runtime,
				state
			};
		};
		var windows = function() {
			function appData() {
				return valOrPath(env.get("APPDATA"), [
					baseDir(),
					"AppData",
					"Roaming"
				]);
			}
			function localAppData() {
				return valOrPath(env.get("LOCALAPPDATA"), [
					baseDir(),
					"AppData",
					"Local"
				]);
			}
			var cache = function() {
				return valOrPath(env.get("XDG_CACHE_HOME"), [localAppData(), "xdg.cache"]);
			};
			var config = function() {
				return valOrPath(env.get("XDG_CONFIG_HOME"), [appData(), "xdg.config"]);
			};
			var data = function() {
				return valOrPath(env.get("XDG_DATA_HOME"), [appData(), "xdg.data"]);
			};
			var runtime = function() {
				return env.get("XDG_RUNTIME_DIR") || void 0;
			};
			var state = function() {
				return valOrPath(env.get("XDG_STATE_HOME"), [localAppData(), "xdg.state"]);
			};
			return {
				cache,
				config,
				data,
				runtime,
				state
			};
		};
		return { XDG: new (function() {
			function XDG_() {
				function XDG() {
					return new XDG_();
				}
				var extension = isMacOS ? macos() : isWinOS ? windows() : linux();
				XDG.cache = extension.cache;
				XDG.config = extension.config;
				XDG.data = extension.data;
				XDG.runtime = extension.runtime;
				XDG.state = extension.state;
				XDG.configDirs = function configDirs() {
					var pathList = env.get("XDG_CONFIG_DIRS");
					return __spreadArray([extension.config()], pathList ? pathList.split(path.delimiter) : []);
				};
				XDG.dataDirs = function dataDirs() {
					var pathList = env.get("XDG_DATA_DIRS");
					return __spreadArray([extension.data()], pathList ? pathList.split(path.delimiter) : []);
				};
				return XDG;
			}
			return XDG_;
		}())() };
	}
	exports.Adapt = Adapt;
}));
//#endregion
//#region ../../node_modules/.pnpm/os-paths@7.4.0/node_modules/os-paths/dist/cjs/lib/OSPaths.js
var require_OSPaths = /* @__PURE__ */ __commonJSMin(((exports) => {
	var __spreadArray = exports && exports.__spreadArray || function(to, from) {
		for (var i = 0, il = from.length, j = to.length; i < il; i++, j++) to[j] = from[i];
		return to;
	};
	exports.__esModule = true;
	exports.Adapt = void 0;
	function isEmpty(s) {
		return !s;
	}
	function Adapt(adapter_) {
		var env = adapter_.env, os = adapter_.os, path = adapter_.path;
		var isWinOS = /^win/i.test(adapter_.process.platform);
		function normalizePath(path_) {
			return path_ ? adapter_.path.normalize(adapter_.path.join(path_, ".")) : void 0;
		}
		function home() {
			var posix = function() {
				return normalizePath((typeof os.homedir === "function" ? os.homedir() : void 0) || env.get("HOME"));
			};
			var windows = function() {
				return normalizePath([
					typeof os.homedir === "function" ? os.homedir() : void 0,
					env.get("USERPROFILE"),
					env.get("HOME"),
					env.get("HOMEDRIVE") || env.get("HOMEPATH") ? path.join(env.get("HOMEDRIVE") || "", env.get("HOMEPATH") || "") : void 0
				].find(function(v) {
					return !isEmpty(v);
				}));
			};
			return isWinOS ? windows() : posix();
		}
		function temp() {
			function joinPathToBase(base, segments) {
				return base ? path.join.apply(path, __spreadArray([base], segments)) : void 0;
			}
			function posix() {
				return normalizePath([
					typeof os.tmpdir === "function" ? os.tmpdir() : void 0,
					env.get("TMPDIR"),
					env.get("TEMP"),
					env.get("TMP")
				].find(function(v) {
					return !isEmpty(v);
				})) || "/tmp";
			}
			function windows() {
				var fallback = "C:\\Temp";
				var v = [
					typeof os.tmpdir === "function" ? os.tmpdir : function() {},
					function() {
						return env.get("TEMP");
					},
					function() {
						return env.get("TMP");
					},
					function() {
						return joinPathToBase(env.get("LOCALAPPDATA"), ["Temp"]);
					},
					function() {
						return joinPathToBase(home(), [
							"AppData",
							"Local",
							"Temp"
						]);
					},
					function() {
						return joinPathToBase(env.get("ALLUSERSPROFILE"), ["Temp"]);
					},
					function() {
						return joinPathToBase(env.get("SystemRoot"), ["Temp"]);
					},
					function() {
						return joinPathToBase(env.get("windir"), ["Temp"]);
					},
					function() {
						return joinPathToBase(env.get("SystemDrive"), ["\\", "Temp"]);
					}
				].find(function(v) {
					return v && !isEmpty(v());
				});
				return v && normalizePath(v()) || fallback;
			}
			return isWinOS ? windows() : posix();
		}
		return { OSPaths: new (function() {
			function OSPaths_() {
				function OSPaths() {
					return new OSPaths_();
				}
				OSPaths.home = home;
				OSPaths.temp = temp;
				return OSPaths;
			}
			return OSPaths_;
		}())() };
	}
	exports.Adapt = Adapt;
}));
//#endregion
//#region ../../node_modules/.pnpm/os-paths@7.4.0/node_modules/os-paths/dist/cjs/platform-adapters/node.js
var require_node$2 = /* @__PURE__ */ __commonJSMin(((exports) => {
	var __createBinding = exports && exports.__createBinding || (Object.create ? (function(o, m, k, k2) {
		if (k2 === void 0) k2 = k;
		Object.defineProperty(o, k2, {
			enumerable: true,
			get: function() {
				return m[k];
			}
		});
	}) : (function(o, m, k, k2) {
		if (k2 === void 0) k2 = k;
		o[k2] = m[k];
	}));
	var __setModuleDefault = exports && exports.__setModuleDefault || (Object.create ? (function(o, v) {
		Object.defineProperty(o, "default", {
			enumerable: true,
			value: v
		});
	}) : function(o, v) {
		o["default"] = v;
	});
	var __importStar = exports && exports.__importStar || function(mod) {
		if (mod && mod.__esModule) return mod;
		var result = {};
		if (mod != null) {
			for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
		}
		__setModuleDefault(result, mod);
		return result;
	};
	exports.__esModule = true;
	exports.adapter = void 0;
	exports.adapter = {
		atImportPermissions: { env: true },
		env: { get: function(s) {
			return process.env[s];
		} },
		os: __importStar(__require("os")),
		path: __importStar(__require("path")),
		process
	};
}));
//#endregion
//#region ../../node_modules/.pnpm/os-paths@7.4.0/node_modules/os-paths/dist/cjs/mod.cjs.js
var require_mod_cjs$2 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
	var OSPaths_js_1 = require_OSPaths();
	var node_js_1 = require_node$2();
	module.exports = OSPaths_js_1.Adapt(node_js_1.adapter).OSPaths;
}));
//#endregion
//#region ../../node_modules/.pnpm/xdg-portable@10.6.0/node_modules/xdg-portable/dist/cjs/platform-adapters/node.js
var require_node$1 = /* @__PURE__ */ __commonJSMin(((exports) => {
	var __createBinding = exports && exports.__createBinding || (Object.create ? (function(o, m, k, k2) {
		if (k2 === void 0) k2 = k;
		Object.defineProperty(o, k2, {
			enumerable: true,
			get: function() {
				return m[k];
			}
		});
	}) : (function(o, m, k, k2) {
		if (k2 === void 0) k2 = k;
		o[k2] = m[k];
	}));
	var __setModuleDefault = exports && exports.__setModuleDefault || (Object.create ? (function(o, v) {
		Object.defineProperty(o, "default", {
			enumerable: true,
			value: v
		});
	}) : function(o, v) {
		o["default"] = v;
	});
	var __importStar = exports && exports.__importStar || function(mod) {
		if (mod && mod.__esModule) return mod;
		var result = {};
		if (mod != null) {
			for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
		}
		__setModuleDefault(result, mod);
		return result;
	};
	var __importDefault = exports && exports.__importDefault || function(mod) {
		return mod && mod.__esModule ? mod : { "default": mod };
	};
	exports.__esModule = true;
	exports.adapter = void 0;
	var path$3 = __importStar(__require("path"));
	exports.adapter = {
		atImportPermissions: { env: true },
		env: { get: function(s) {
			return process.env[s];
		} },
		osPaths: __importDefault(require_mod_cjs$2())["default"],
		path: path$3,
		process
	};
}));
//#endregion
//#region ../../node_modules/.pnpm/xdg-portable@10.6.0/node_modules/xdg-portable/dist/cjs/mod.cjs.js
var require_mod_cjs$1 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
	var XDG_js_1 = require_XDG();
	var node_js_1 = require_node$1();
	module.exports = XDG_js_1.Adapt(node_js_1.adapter).XDG;
}));
//#endregion
//#region ../../node_modules/.pnpm/xdg-app-paths@8.3.0/node_modules/xdg-app-paths/dist/cjs/platform-adapters/node.js
var require_node = /* @__PURE__ */ __commonJSMin(((exports) => {
	var __createBinding = exports && exports.__createBinding || (Object.create ? (function(o, m, k, k2) {
		if (k2 === void 0) k2 = k;
		Object.defineProperty(o, k2, {
			enumerable: true,
			get: function() {
				return m[k];
			}
		});
	}) : (function(o, m, k, k2) {
		if (k2 === void 0) k2 = k;
		o[k2] = m[k];
	}));
	var __setModuleDefault = exports && exports.__setModuleDefault || (Object.create ? (function(o, v) {
		Object.defineProperty(o, "default", {
			enumerable: true,
			value: v
		});
	}) : function(o, v) {
		o["default"] = v;
	});
	var __importStar = exports && exports.__importStar || function(mod) {
		if (mod && mod.__esModule) return mod;
		var result = {};
		if (mod != null) {
			for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
		}
		__setModuleDefault(result, mod);
		return result;
	};
	var __importDefault = exports && exports.__importDefault || function(mod) {
		return mod && mod.__esModule ? mod : { "default": mod };
	};
	exports.__esModule = true;
	exports.adapter = void 0;
	var path$2 = __importStar(__require("path"));
	var xdg_portable_1 = __importDefault(require_mod_cjs$1());
	exports.adapter = {
		atImportPermissions: {
			env: true,
			read: true
		},
		meta: {
			mainFilename: function() {
				var requireMainFilename = (typeof __require !== "undefined" && __require !== null && __require.main ? __require.main : { filename: void 0 }).filename;
				return (requireMainFilename !== process.execArgv[0] ? requireMainFilename : void 0) || (typeof process._eval === "undefined" ? process.argv[1] : void 0);
			},
			pkgMainFilename: function() {
				return process.pkg ? process.execPath : void 0;
			}
		},
		path: path$2,
		process,
		xdg: xdg_portable_1["default"]
	};
}));
var mod_esm_default = (/* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exports, module) => {
	var XDGAppPaths_js_1 = require_XDGAppPaths();
	var node_js_1 = require_node();
	module.exports = XDGAppPaths_js_1.Adapt(node_js_1.adapter).XDGAppPaths;
})))())).default;
//#endregion
//#region ../../node_modules/.pnpm/@prisma+credentials-store@7.8.0/node_modules/@prisma/credentials-store/dist/index.mjs
var CredentialsStore = class {
	loadedCredentials;
	authFilePath;
	/**
	* @param authFilePathOverride - Override for the path to the auth file. Can also be set via the PRISMA_PLATFORM_AUTH_FILE environment variable. Useful for testing.
	*/
	constructor(authFilePathOverride) {
		this.authFilePath = process.env.PRISMA_PLATFORM_AUTH_FILE || authFilePathOverride || path$1.join(mod_esm_default({ name: "prisma-platform" }).config(), "auth.json");
	}
	async reloadCredentialsFromDisk() {
		try {
			const content = await readFile$1(this.authFilePath, "utf-8");
			const data = JSON.parse(content);
			this.loadedCredentials = data.tokens || [];
		} catch (error) {
			this.loadedCredentials = [];
		}
	}
	async storeCredentials(credentials) {
		await this.reloadCredentialsFromDisk();
		const updatedCredentials = [...(this.loadedCredentials || []).filter((c) => c.workspaceId !== credentials.workspaceId), credentials];
		this.loadedCredentials = updatedCredentials;
		await this.writeCredentialsToDisk(updatedCredentials);
	}
	async deleteCredentials(workspaceId) {
		await this.reloadCredentialsFromDisk();
		const updatedCredentials = (this.loadedCredentials || []).filter((c) => c.workspaceId !== workspaceId);
		this.loadedCredentials = updatedCredentials;
		await this.writeCredentialsToDisk(updatedCredentials);
	}
	async getCredentials() {
		if (this.loadedCredentials === void 0) await this.reloadCredentialsFromDisk();
		return this.loadedCredentials || [];
	}
	async getCredentialsForWorkspace(workspaceId) {
		return (await this.getCredentials()).filter((c) => c.workspaceId === workspaceId)[0];
	}
	async writeCredentialsToDisk(credentials) {
		const data = { tokens: credentials };
		await mkdir$1(path$1.dirname(this.authFilePath), { recursive: true });
		await writeFile$1(this.authFilePath, JSON.stringify(data, null, 2));
	}
};
//#endregion
//#region ../cli/src/lib/workspace-id.ts
/**
* A workspace reaches the CLI under two ids for the same workspace: a
* credential's `workspace_id` claim carries the bare id, while the
* management API and anything derived from it carry the same id behind
* a `wksp_` prefix. Comparing the two forms directly silently matches
* nothing, so every comparison between workspace ids of different
* origin goes through here.
*/
const WORKSPACE_ID_PREFIX = "wksp_";
function stripWorkspacePrefix(value) {
	return value.startsWith(WORKSPACE_ID_PREFIX) ? value.slice(5) : value;
}
function sameWorkspaceId(left, right) {
	return stripWorkspacePrefix(left) === stripWorkspacePrefix(right);
}
//#endregion
//#region ../cli/src/auth/token-storage.ts
const REFRESH_LOCK_RETRY_MS$1 = 100;
const REFRESH_LOCK_STALE_MS$1 = 3e4;
const REFRESH_LOCK_WAIT_TIMEOUT_MS$1 = 25e3;
const EMPTY_AUTH_CONTEXT = {
	activeWorkspaceId: null,
	workspaces: {}
};
const UNKNOWN_WORKSPACE_NAME = "Unknown workspace";
function getAuthContextFilePath(authFilePath) {
	const extension = path.extname(authFilePath);
	if (!extension) return `${authFilePath}.context.json`;
	return `${authFilePath.slice(0, -extension.length)}.context${extension}`;
}
function findLatestValidTokens(allCredentials) {
	for (let i = allCredentials.length - 1; i >= 0; i -= 1) {
		const credential = allCredentials[i];
		if (!credential) continue;
		if (typeof credential.workspaceId !== "string" || credential.workspaceId.length === 0 || typeof credential.token !== "string" || credential.token.length === 0 || typeof credential.refreshToken !== "string" || credential.refreshToken.length === 0) continue;
		return {
			workspaceId: credential.workspaceId,
			accessToken: credential.token,
			refreshToken: credential.refreshToken
		};
	}
	return null;
}
function storedCredentialToTokens(credential) {
	if (!credential) return null;
	if (typeof credential.workspaceId !== "string" || credential.workspaceId.length === 0 || typeof credential.token !== "string" || credential.token.length === 0 || typeof credential.refreshToken !== "string" || credential.refreshToken.length === 0) return null;
	return {
		workspaceId: credential.workspaceId,
		accessToken: credential.token,
		refreshToken: credential.refreshToken
	};
}
function findTokensForWorkspace(allCredentials, workspaceId) {
	return storedCredentialToTokens(allCredentials.find((credential) => credential?.workspaceId === workspaceId)) ?? null;
}
function tokensEqual(a, b) {
	return a?.workspaceId === b?.workspaceId && a?.accessToken === b?.accessToken && a?.refreshToken === b?.refreshToken;
}
function sleep$2(ms, signal) {
	signal?.throwIfAborted();
	return new Promise((resolve, reject) => {
		const onAbort = () => {
			clearTimeout(timeout);
			reject(signal?.reason);
		};
		const timeout = setTimeout(() => {
			signal?.removeEventListener("abort", onAbort);
			resolve();
		}, ms);
		signal?.addEventListener("abort", onAbort, { once: true });
	});
}
var FileTokenStorage = class {
	credentialsStore;
	authFilePath;
	contextFilePath;
	lockFilePath;
	constructor(env = process.env, signal, options = {}) {
		this.signal = signal;
		this.options = options;
		const authFilePath = getAuthFilePath(env);
		this.authFilePath = authFilePath;
		this.contextFilePath = getAuthContextFilePath(authFilePath);
		this.credentialsStore = new CredentialsStore(authFilePath);
		this.lockFilePath = `${authFilePath}.lock`;
	}
	async getTokens() {
		this.signal?.throwIfAborted();
		try {
			const credentials = await this.readCredentialsFromDisk();
			if (this.options.pinnedWorkspaceId) return findTokensForWorkspace(credentials, this.options.pinnedWorkspaceId);
			const context = await this.readAuthContext();
			if (context.state.activeWorkspaceId) return findTokensForWorkspace(credentials, context.state.activeWorkspaceId);
			const latest = findLatestValidTokens(credentials);
			if (!latest) return null;
			if (!context.exists) {
				await this.setActiveWorkspaceId(latest.workspaceId);
				return latest;
			}
			return null;
		} catch (_error) {
			if (this.signal?.aborted) throw this.signal.reason;
			return null;
		}
	}
	async setTokens(tokens) {
		if (this.options.lockSetTokens === false) {
			await this.setTokensUnlocked(tokens);
			return;
		}
		await this.withRefreshLock(() => this.setTokensUnlocked(tokens));
	}
	async setTokensUnlocked(tokens) {
		this.signal?.throwIfAborted();
		await this.credentialsStore.storeCredentials({
			workspaceId: tokens.workspaceId,
			token: tokens.accessToken,
			refreshToken: tokens.refreshToken
		});
		this.signal?.throwIfAborted();
		await this.rememberWorkspaceUnlocked(tokens.workspaceId, {
			id: tokens.workspaceId,
			name: tokens.workspaceId
		});
		await this.maybeActivateWorkspaceId(tokens.workspaceId);
	}
	async clearTokens() {
		await this.withRefreshLock(() => this.clearTokensUnlocked());
	}
	async clearTokensUnlocked() {
		this.signal?.throwIfAborted();
		await fs.mkdir(path.dirname(this.authFilePath), { recursive: true });
		this.signal?.throwIfAborted();
		await fs.writeFile(this.authFilePath, `${JSON.stringify({ tokens: [] }, null, 2)}\n`, {
			encoding: "utf8",
			signal: this.signal
		});
		await this.clearAuthContext();
		await this.credentialsStore.reloadCredentialsFromDisk();
		this.signal?.throwIfAborted();
	}
	async clearTokensIfCurrent(tokens) {
		this.signal?.throwIfAborted();
		if (!tokensEqual(await this.getTokens(), tokens)) return;
		await this.credentialsStore.deleteCredentials(tokens.workspaceId);
		await this.removeRememberedWorkspace(tokens.workspaceId, { preserveActivePointer: true });
		this.signal?.throwIfAborted();
	}
	async listWorkspaces() {
		this.signal?.throwIfAborted();
		const credentials = await this.readCredentialsFromDisk();
		const context = await this.ensureMigratedAuthContext(credentials);
		return this.workspacesFromState(credentials, context);
	}
	workspacesFromState(credentials, context) {
		return credentials.map((credential) => storedCredentialToTokens(credential)).filter((tokens) => tokens !== null).map((tokens) => {
			const cached = context.state.workspaces[tokens.workspaceId];
			const id = typeof cached?.id === "string" && cached.id.trim().length > 0 ? cached.id.trim() : tokens.workspaceId;
			const name = typeof cached?.name === "string" && cached.name.trim().length > 0 ? workspaceDisplayName(cached.name.trim(), tokens.workspaceId) : UNKNOWN_WORKSPACE_NAME;
			const lastSeenAt = typeof cached?.lastSeenAt === "string" && cached.lastSeenAt.trim().length > 0 ? cached.lastSeenAt.trim() : null;
			return {
				id,
				name,
				credentialWorkspaceId: tokens.workspaceId,
				active: context.state.activeWorkspaceId === tokens.workspaceId,
				lastSeenAt
			};
		});
	}
	async listWorkspaceTokens() {
		this.signal?.throwIfAborted();
		return (await this.readCredentialsFromDisk()).map((credential) => storedCredentialToTokens(credential)).filter((tokens) => tokens !== null);
	}
	async useWorkspace(workspaceRef) {
		return this.withRefreshLock(() => this.useWorkspaceUnlocked(workspaceRef));
	}
	/**
	* Resolve a workspace ref (id, credential workspace id, or cached name)
	* against the locally stored sessions without changing the active
	* workspace. Read-only counterpart of useWorkspace: it reads the auth
	* context as-is and never runs the legacy-state migration, so it writes
	* nothing.
	*/
	async resolveWorkspace(workspaceRef) {
		const ref = workspaceRef.trim();
		if (!ref) throw new WorkspaceSelectionError("missing");
		this.signal?.throwIfAborted();
		const credentials = await this.readCredentialsFromDisk();
		const context = await this.readAuthContext();
		const matches = this.workspacesFromState(credentials, context).filter((workspace) => workspaceMatchesRef(workspace, ref));
		if (matches.length === 0) throw new WorkspaceSelectionError("not-found", ref);
		if (matches.length > 1) throw new WorkspaceSelectionError("ambiguous", ref, matches);
		return matches[0];
	}
	async useWorkspaceUnlocked(workspaceRef) {
		const ref = workspaceRef.trim();
		if (!ref) throw new WorkspaceSelectionError("missing");
		const workspaces = await this.listWorkspaces();
		const context = await this.readAuthContext();
		const previous = workspaces.find((workspace) => workspace.credentialWorkspaceId === context.state.activeWorkspaceId) ?? null;
		const matches = workspaces.filter((workspace) => workspaceMatchesRef(workspace, ref));
		if (matches.length === 0) throw new WorkspaceSelectionError("not-found", ref);
		if (matches.length > 1) throw new WorkspaceSelectionError("ambiguous", ref, matches);
		const selected = matches[0];
		await this.setActiveWorkspaceId(selected.credentialWorkspaceId);
		return {
			previous,
			selected
		};
	}
	async logoutWorkspace(workspaceRef) {
		return this.withRefreshLock(() => this.logoutWorkspaceUnlocked(workspaceRef));
	}
	async logoutWorkspaceUnlocked(workspaceRef) {
		const ref = workspaceRef.trim();
		if (!ref) throw new WorkspaceSelectionError("missing");
		const workspaces = await this.listWorkspaces();
		const context = await this.readAuthContext();
		const matches = workspaces.filter((workspace) => workspaceMatchesRef(workspace, ref));
		if (matches.length === 0) throw new WorkspaceSelectionError("not-found", ref);
		if (matches.length > 1) throw new WorkspaceSelectionError("ambiguous", ref, matches);
		const workspace = matches[0];
		const wasActive = context.state.activeWorkspaceId === workspace.credentialWorkspaceId;
		await this.credentialsStore.deleteCredentials(workspace.credentialWorkspaceId);
		this.signal?.throwIfAborted();
		const remainingWorkspaces = workspaces.filter((candidate) => candidate.credentialWorkspaceId !== workspace.credentialWorkspaceId);
		if (remainingWorkspaces.length === 0) {
			await this.clearAuthContext();
			return {
				workspace,
				wasActive,
				activeWorkspace: null
			};
		}
		delete context.state.workspaces[workspace.credentialWorkspaceId];
		if (wasActive) context.state.activeWorkspaceId = null;
		await this.writeAuthContext(context.state);
		return {
			workspace,
			wasActive,
			activeWorkspace: context.state.activeWorkspaceId === null ? null : remainingWorkspaces.find((candidate) => candidate.credentialWorkspaceId === context.state.activeWorkspaceId) ?? null
		};
	}
	async rememberWorkspace(credentialWorkspaceId, workspace) {
		await this.withRefreshLock(() => this.rememberWorkspaceUnlocked(credentialWorkspaceId, workspace));
	}
	async rememberWorkspaceUnlocked(credentialWorkspaceId, workspace) {
		const context = await this.readAuthContext();
		context.state.workspaces[credentialWorkspaceId] = {
			id: workspace.id,
			name: workspace.name,
			lastSeenAt: (/* @__PURE__ */ new Date()).toISOString()
		};
		await this.writeAuthContext(context.state);
	}
	async withRefreshLock(fn) {
		const lockId = await this.acquireRefreshLock();
		try {
			return await fn();
		} finally {
			await this.releaseRefreshLock(lockId);
		}
	}
	async acquireRefreshLock() {
		const lockId = randomUUID();
		const startedAt = Date.now();
		const retryMs = this.options.lockRetryMs ?? REFRESH_LOCK_RETRY_MS$1;
		const waitTimeoutMs = this.options.lockWaitTimeoutMs ?? REFRESH_LOCK_WAIT_TIMEOUT_MS$1;
		this.signal?.throwIfAborted();
		await fs.mkdir(path.dirname(this.lockFilePath), { recursive: true });
		while (true) {
			this.signal?.throwIfAborted();
			if (await this.tryCreateRefreshLock(lockId)) return lockId;
			if (await this.releaseStaleRefreshLock()) continue;
			this.throwIfRefreshLockWaitTimedOut(startedAt, waitTimeoutMs);
			await sleep$2(retryMs, this.signal);
		}
	}
	async tryCreateRefreshLock(lockId) {
		let lockFileCreated = false;
		try {
			const handle = await fs.open(this.lockFilePath, "wx");
			lockFileCreated = true;
			try {
				this.signal?.throwIfAborted();
				await handle.writeFile(lockId, { encoding: "utf8" });
				this.signal?.throwIfAborted();
			} finally {
				await handle.close();
			}
			return true;
		} catch (error) {
			if (lockFileCreated) await fs.unlink(this.lockFilePath).catch(() => void 0);
			if (error.code === "EEXIST") return false;
			throw error;
		}
	}
	async releaseStaleRefreshLock() {
		const staleLockId = await this.getStaleRefreshLockId();
		if (!staleLockId) return false;
		await this.releaseRefreshLock(staleLockId);
		return true;
	}
	throwIfRefreshLockWaitTimedOut(startedAt, waitTimeoutMs) {
		if (Date.now() - startedAt < waitTimeoutMs) return;
		throw new RefreshLockTimeoutError(this.lockFilePath, waitTimeoutMs);
	}
	async getStaleRefreshLockId() {
		this.signal?.throwIfAborted();
		const lockId = await fs.readFile(this.lockFilePath, {
			encoding: "utf8",
			signal: this.signal
		}).catch((error) => {
			if (this.signal?.aborted) throw error;
			return null;
		});
		if (lockId === null) return null;
		this.signal?.throwIfAborted();
		const stats = await fs.stat(this.lockFilePath).catch(() => null);
		this.signal?.throwIfAborted();
		if (!stats) return null;
		const staleMs = this.options.lockStaleMs ?? REFRESH_LOCK_STALE_MS$1;
		return Date.now() - stats.mtimeMs > staleMs ? lockId : null;
	}
	async releaseRefreshLock(lockId) {
		if (await fs.readFile(this.lockFilePath, { encoding: "utf8" }).catch(() => null) !== lockId) return;
		await fs.unlink(this.lockFilePath).catch(() => {});
	}
	async readCredentialsFromDisk() {
		this.signal?.throwIfAborted();
		await this.credentialsStore.reloadCredentialsFromDisk();
		this.signal?.throwIfAborted();
		return await this.credentialsStore.getCredentials();
	}
	async ensureMigratedAuthContext(credentials) {
		const context = await this.readAuthContext();
		if (context.state.activeWorkspaceId || context.exists) return context;
		const latest = findLatestValidTokens(credentials);
		if (!latest) return context;
		context.state.activeWorkspaceId = latest.workspaceId;
		context.state.workspaces[latest.workspaceId] ??= {
			id: latest.workspaceId,
			name: latest.workspaceId,
			lastSeenAt: (/* @__PURE__ */ new Date()).toISOString()
		};
		await this.writeAuthContext(context.state);
		return {
			exists: true,
			state: context.state
		};
	}
	async setActiveWorkspaceId(workspaceId) {
		const context = await this.readAuthContext();
		context.state.activeWorkspaceId = workspaceId;
		context.state.workspaces[workspaceId] ??= {
			id: workspaceId,
			name: workspaceId,
			lastSeenAt: (/* @__PURE__ */ new Date()).toISOString()
		};
		await this.writeAuthContext(context.state);
	}
	async maybeActivateWorkspaceId(workspaceId) {
		if (this.options.pinnedWorkspaceId) return;
		const context = await this.readAuthContext();
		if (this.options.activateOnSetTokens === false && context.exists && context.state.activeWorkspaceId && context.state.activeWorkspaceId !== workspaceId) return;
		context.state.activeWorkspaceId = workspaceId;
		context.state.workspaces[workspaceId] ??= {
			id: workspaceId,
			name: workspaceId,
			lastSeenAt: (/* @__PURE__ */ new Date()).toISOString()
		};
		await this.writeAuthContext(context.state);
	}
	async removeRememberedWorkspace(workspaceId, options) {
		const context = await this.readAuthContext();
		delete context.state.workspaces[workspaceId];
		if (!options.preserveActivePointer && context.state.activeWorkspaceId === workspaceId) context.state.activeWorkspaceId = null;
		await this.writeAuthContext(context.state);
	}
	async readAuthContext() {
		this.signal?.throwIfAborted();
		const raw = await fs.readFile(this.contextFilePath, {
			encoding: "utf8",
			signal: this.signal
		}).catch((error) => {
			if (this.signal?.aborted) throw error;
			if (error.code === "ENOENT") return null;
			throw error;
		});
		if (raw === null) return {
			exists: false,
			state: structuredClone(EMPTY_AUTH_CONTEXT)
		};
		try {
			const parsed = JSON.parse(raw);
			return {
				exists: true,
				state: {
					activeWorkspaceId: typeof parsed.activeWorkspaceId === "string" && parsed.activeWorkspaceId.trim().length > 0 ? parsed.activeWorkspaceId.trim() : null,
					workspaces: parsed.workspaces && typeof parsed.workspaces === "object" && !Array.isArray(parsed.workspaces) ? parsed.workspaces : {}
				}
			};
		} catch {
			return {
				exists: false,
				state: structuredClone(EMPTY_AUTH_CONTEXT)
			};
		}
	}
	async writeAuthContext(state) {
		this.signal?.throwIfAborted();
		await fs.mkdir(path.dirname(this.contextFilePath), { recursive: true });
		this.signal?.throwIfAborted();
		await fs.writeFile(this.contextFilePath, `${JSON.stringify(state, null, 2)}\n`, {
			encoding: "utf8",
			signal: this.signal
		});
		this.signal?.throwIfAborted();
	}
	async clearAuthContext() {
		await fs.unlink(this.contextFilePath).catch((error) => {
			if (error.code === "ENOENT") return;
			throw error;
		});
	}
};
var WorkspaceSelectionError = class extends Error {
	constructor(reason, workspaceRef, matches = []) {
		super(reason);
		this.reason = reason;
		this.workspaceRef = workspaceRef;
		this.matches = matches;
		this.name = "WorkspaceSelectionError";
	}
};
var RefreshLockTimeoutError = class extends Error {
	constructor(lockFilePath, waitTimeoutMs) {
		super(`Timed out waiting ${waitTimeoutMs}ms for auth refresh lock at ${lockFilePath}`);
		this.name = "RefreshLockTimeoutError";
	}
};
function workspaceMatchesRef(workspace, ref) {
	return sameWorkspaceId(workspace.credentialWorkspaceId, ref) || sameWorkspaceId(workspace.id, ref) || workspace.name.toLowerCase() === ref.toLowerCase();
}
function workspaceDisplayName(name, credentialWorkspaceId) {
	return name === credentialWorkspaceId ? UNKNOWN_WORKSPACE_NAME : name;
}
//#endregion
//#region ../cli/src/auth/login.ts
var AuthError$1 = class extends Error {
	constructor(message) {
		super(message);
		this.name = "AuthError";
	}
};
async function login(options = {}) {
	const hostname = options.hostname ?? "localhost";
	const port = options.port ?? 0;
	const input = options.input ?? process.stdin;
	const output = options.output ?? process.stderr;
	const interactive = input.isTTY === true;
	const server = http.createServer();
	server.listen({
		host: hostname,
		port
	});
	const pasteAbort = new AbortController();
	try {
		const state = new LoginState({
			hostname,
			port: (await events.once(server, "listening").then(() => server.address())).port,
			tokenStorage: options.tokenStorage,
			clientId: options.clientId,
			apiBaseUrl: options.apiBaseUrl,
			authBaseUrl: options.authBaseUrl,
			openUrl: options.openUrl,
			onVerificationUrl: options.onVerificationUrl,
			env: options.env,
			signal: options.signal,
			output
		});
		let completed = false;
		let completion;
		const completeOnce = (url) => {
			if (!completion) completion = state.handleCallback(url).then(() => {
				completed = true;
			}, (error) => {
				completion = void 0;
				throw error;
			});
			return completion;
		};
		const httpResult = new Promise((resolve, reject) => {
			const onAbort = () => {
				reject(options.signal?.reason);
			};
			options.signal?.addEventListener("abort", onAbort, { once: true });
			const settle = (callback) => {
				options.signal?.removeEventListener("abort", onAbort);
				callback();
			};
			server.on("request", async (req, res) => {
				const url = new URL(`http://${state.host}${req.url}`);
				if (url.pathname !== "/auth/callback") {
					res.statusCode = 404;
					res.end("Not found");
					return;
				}
				if (completed) {
					const workspaceName = await state.resolveWorkspaceName();
					res.setHeader("Content-Type", "text/html; charset=utf-8");
					res.end(renderSuccessPage(workspaceName, options.uiContext));
					return;
				}
				try {
					await completeOnce(url);
					const workspaceName = await state.resolveWorkspaceName();
					res.setHeader("Content-Type", "text/html; charset=utf-8");
					res.end(renderSuccessPage(workspaceName, options.uiContext));
					settle(resolve);
				} catch (error) {
					res.statusCode = 400;
					res.setHeader("Content-Type", "text/plain; charset=utf-8");
					res.end(options.uiContext === "prisma-plugin" ? "Sign-in couldn’t be completed. Return to your ChatGPT conversation to try again." : "Sign-in could not be completed. Return to your terminal.");
					settle(() => reject(error));
					return;
				}
			});
		});
		options.signal?.throwIfAborted();
		const callbackResult = interactive ? Promise.race([httpResult, consumePastedCallback({
			input,
			output,
			signal: pasteAbort.signal,
			complete: completeOnce
		})]) : httpResult;
		await Promise.all([state.openLoginPage(interactive), callbackResult]);
	} finally {
		pasteAbort.abort();
		if (server.listening) await new Promise((resolve) => server.close(() => resolve()));
	}
}
async function consumePastedCallback(options) {
	if (!options.input.isTTY) return;
	const rl = readline.createInterface({
		input: options.input,
		output: options.output
	});
	try {
		for (;;) {
			const url = await readPastedCallbackUrl(rl, options);
			if (url === null) return;
			if (url === void 0) continue;
			if (await tryCompletePastedCallback(url, options)) return;
		}
	} finally {
		rl.close();
	}
}
async function readPastedCallbackUrl(rl, options) {
	let answer;
	try {
		answer = await rl.question("Paste the callback URL here: ", { signal: options.signal });
	} catch (error) {
		if (error?.name === "AbortError") return null;
		throw error;
	}
	const trimmed = answer.trim().replace(/^["']|["']$/g, "");
	try {
		if (!trimmed) throw new Error("empty input");
		return new URL(trimmed);
	} catch {
		options.output.write("That didn't look like a URL. Paste the full localhost callback URL and try again.\n");
		return;
	}
}
async function tryCompletePastedCallback(url, options) {
	try {
		await options.complete(url);
		return true;
	} catch (error) {
		if (error instanceof CliStructuredError) throw error;
		const message = error instanceof Error ? error.message : String(error);
		options.output.write(`Sign-in didn't complete (${message}). Paste the callback URL to try again.\n`);
		return false;
	}
}
var LoginState = class {
	latestVerifier;
	latestState;
	sdk;
	openUrl;
	tokenStorage;
	output;
	constructor(options) {
		this.options = options;
		this.tokenStorage = options.tokenStorage ?? new FileTokenStorage(options.env, options.signal, { activateOnSetTokens: true });
		this.sdk = createManagementApiSdk({
			clientId: options.clientId ?? "cmm3lndn701oo0uefvxzo0ivw",
			redirectUri: `http://${options.hostname}:${options.port}/auth/callback`,
			tokenStorage: this.tokenStorage,
			apiBaseUrl: options.apiBaseUrl ?? getApiBaseUrl(options.env),
			authBaseUrl: options.authBaseUrl
		});
		this.openUrl = options.openUrl ?? open;
		this.output = options.output;
	}
	async openLoginPage(interactive) {
		this.options.signal?.throwIfAborted();
		const { url, state, verifier } = await this.sdk.getLoginUrl({
			scope: "workspace:admin offline_access",
			additionalParams: {
				utm_source: "prisma-cli",
				utm_medium: "command-login",
				utm_campaign: "prisma-cli"
			}
		});
		this.latestState = state;
		this.latestVerifier = verifier;
		try {
			this.options.onVerificationUrl?.(url);
		} catch {}
		this.options.signal?.throwIfAborted();
		if (interactive) this.printLoginInstructions(url);
		try {
			await this.openUrl(url);
		} catch (error) {
			if (!interactive) throw error;
		}
		this.options.signal?.throwIfAborted();
	}
	printLoginInstructions(url) {
		const output = this.output;
		if (!output) return;
		output.write(`\nOpen this URL to sign in: ${url}\n\nIf the browser opens on another machine, finish sign-in there. When it\nredirects to localhost, copy the full localhost URL from the address bar\nand paste it here.\n\n`);
	}
	async handleCallback(url) {
		if (url.pathname !== "/auth/callback") throw new AuthError$1("Not a callback URL");
		const params = url.searchParams;
		const error = params.get("error");
		if (error === "access_denied") throw new CliStructuredError("AUTH.LOGIN_DENIED", "Sign-in was not authorized.", { nextActions: [{
			kind: "run-command",
			label: "Sign in again if you want to grant access",
			command: `${CLI_NAME} auth login`
		}] });
		if (error) {
			const desc = params.get("error_description");
			throw new AuthError$1(desc ? `${error}: ${desc}` : error);
		}
		if (!this.latestVerifier) throw new AuthError$1("No verifier found");
		if (!this.latestState) throw new AuthError$1("No state found");
		try {
			await this.sdk.handleCallback({
				callbackUrl: url,
				verifier: this.latestVerifier,
				expectedState: this.latestState
			});
		} catch (error) {
			if (error instanceof AuthError) throw new AuthError$1(error.message);
			throw new AuthError$1(error instanceof Error ? error.message : "Unknown error during login");
		}
	}
	async resolveWorkspaceName() {
		try {
			const tokens = await this.tokenStorage.getTokens();
			if (!tokens?.workspaceId) return null;
			const { data } = await this.sdk.client.GET("/v1/workspaces/{id}", {
				params: { path: { id: tokens.workspaceId } },
				signal: this.options.signal
			});
			const name = data?.data?.name;
			return typeof name === "string" && name.trim().length > 0 ? name.trim() : null;
		} catch {
			this.options.signal?.throwIfAborted();
			return null;
		}
	}
	get host() {
		return `${this.options.hostname}:${this.options.port}`;
	}
};
function renderSuccessPage(workspaceName, uiContext) {
	let body = workspaceName ? `Your terminal is now connected to your ${escapeHtml(workspaceName)} workspace. Head back to your terminal to continue.` : "Your terminal is now connected to your Prisma workspace. Head back to your terminal to continue.";
	if (uiContext === "prisma-plugin") body = "You’re connected to Prisma. Return to your ChatGPT conversation.";
	return `<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Prisma Developer Platform</title>
  <style>
    :root {
      color-scheme: light dark;
      --background: #ffffff;
      --foreground: #1f2430;
      --muted: #4f5665;
      --mark-color: #050812;
      --surface: #f6f7fb;
      --border: #e4e7ee;
      --success: #15803d;
    }

    @media (prefers-color-scheme: dark) {
      :root {
        --background: #050812;
        --foreground: #f6f7fb;
        --muted: #c5cad6;
        --mark-color: #ffffff;
        --surface: #0d1322;
        --border: #232a3d;
        --success: #4ade80;
      }
    }

    * {
      box-sizing: border-box;
    }

    body {
      min-height: 100vh;
      margin: 0;
      background: var(--background);
      color: var(--foreground);
      font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
      display: grid;
      grid-template-rows: 128px 1fr;
    }

    .mark {
      align-self: end;
      justify-self: center;
      width: 36px;
      height: 36px;
      color: var(--mark-color);
    }

    .mark path {
      fill: currentColor !important;
    }

    main {
      align-self: center;
      justify-self: center;
      width: min(520px, calc(100vw - 48px));
      margin-top: -128px;
      text-align: center;
    }

    h1 {
      margin: 0 0 12px;
      font-size: 26px;
      line-height: 1.2;
      font-weight: 700;
      letter-spacing: 0;
    }

    p {
      margin: 0 auto;
      max-width: 480px;
      color: var(--muted);
      font-size: 15px;
      line-height: 1.55;
      letter-spacing: 0;
    }

    .skills {
      margin-top: 40px;
      padding-top: 28px;
      border-top: 1px solid var(--border);
      text-align: left;
    }

    .skills-lead {
      display: flex;
      align-items: center;
      gap: 8px;
      margin: 0 0 12px;
      font-size: 15px;
      color: var(--foreground);
    }

    .skills-lead svg {
      flex: none;
      color: var(--muted);
    }

    .command {
      display: flex;
      align-items: center;
      gap: 8px;
      padding: 10px 8px 10px 16px;
      background: var(--surface);
      border: 1px solid var(--border);
      border-radius: 8px;
    }

    .command code {
      flex: 1;
      overflow-x: auto;
      font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
      font-size: 13.5px;
      white-space: nowrap;
    }

    .command .prompt {
      color: var(--muted);
      user-select: none;
    }

    .copy {
      flex: none;
      display: grid;
      place-items: center;
      padding: 6px;
      border: 0;
      border-radius: 6px;
      background: transparent;
      color: var(--muted);
      cursor: pointer;
    }

    .copy:hover {
      background: var(--border);
      color: var(--foreground);
    }

    .copy .icon-check,
    .copy.copied .icon-copy {
      display: none;
    }

    .copy.copied .icon-check {
      display: block;
    }

    .copy.copied,
    .copy.copied:hover {
      color: var(--success);
    }

    .visually-hidden {
      position: absolute;
      width: 1px;
      height: 1px;
      margin: -1px;
      padding: 0;
      overflow: hidden;
      clip: rect(0 0 0 0);
      white-space: nowrap;
      border: 0;
    }
  </style>
</head>
<body>
  <svg class="mark" width="36" height="36" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"><path d="M25.21,24.21,12.739,27.928a.525.525,0,0,1-.667-.606L16.528,5.811a.43.43,0,0,1,.809-.094l8.249,17.661A.6.6,0,0,1,25.21,24.21Zm2.139-.878L17.8,2.883h0A1.531,1.531,0,0,0,16.491,2a1.513,1.513,0,0,0-1.4.729L4.736,19.648a1.592,1.592,0,0,0,.018,1.7l5.064,7.909a1.628,1.628,0,0,0,1.83.678l14.7-4.383a1.6,1.6,0,0,0,1-2.218Z" style="fill:#0c344b;fill-rule:evenodd"/></svg>
  <main>
    <h1>You're all set.</h1>
    <p>${body}</p>
    ${uiContext === "prisma-plugin" ? "" : `<section class="skills">
      <div class="skills-lead">
        <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9.937 15.5A2 2 0 0 0 8.5 14.063l-6.135-1.582a.5.5 0 0 1 0-.962L8.5 9.936A2 2 0 0 0 9.937 8.5l1.582-6.135a.5.5 0 0 1 .963 0L14.063 8.5A2 2 0 0 0 15.5 9.937l6.135 1.581a.5.5 0 0 1 0 .964L15.5 14.063a2 2 0 0 0-1.437 1.437l-1.582 6.135a.5.5 0 0 1-.963 0z"/><path d="M20 3v4"/><path d="M22 5h-4"/></svg>
        Using an AI coding agent? Add the Prisma skills:
      </div>
      <div class="command">
        <code><span class="prompt">$ </span><span class="command-text">npx skills add prisma/skills</span></code>
        <button class="copy" type="button" aria-label="Copy command to clipboard">
          <svg class="icon-copy" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect width="14" height="14" x="8" y="8" rx="2" ry="2"/><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/></svg>
          <svg class="icon-check" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="20 6 9 17 4 12"/></svg>
        </button>
      </div>
      <span class="visually-hidden skills-status" role="status"></span>
    </section>`}
  </main>
  ${uiContext === "prisma-plugin" ? "" : `<script>
    (() => {
      const command = "npx skills add prisma/skills";
      const button = document.querySelector(".copy");
      const status = document.querySelector(".skills-status");
      let timer;
      button.addEventListener("click", async () => {
        try {
          await navigator.clipboard.writeText(command);
        } catch {
          // Clipboard access denied: select the command so Cmd/Ctrl+C works.
          const range = document.createRange();
          range.selectNodeContents(document.querySelector(".command-text"));
          const selection = window.getSelection();
          selection.removeAllRanges();
          selection.addRange(range);
          status.textContent =
            "Copying failed. The command is selected; press Ctrl+C or Cmd+C to copy it.";
          return;
        }
        button.classList.add("copied");
        status.textContent = "Command copied to clipboard.";
        clearTimeout(timer);
        timer = setTimeout(() => {
          button.classList.remove("copied");
          status.textContent = "";
        }, 2000);
      });
    })();
  <\/script>`}
</body>
</html>`;
}
function escapeHtml(value) {
	return value.replace(/[&<>"']/g, (char) => {
		switch (char) {
			case "&": return "&amp;";
			case "<": return "&lt;";
			case ">": return "&gt;";
			case "\"": return "&quot;";
			case "'": return "&#39;";
			default: return char;
		}
	});
}
//#endregion
//#region ../cli/src/auth/operations.ts
/** Holds the tokens the SDK writes at callback time and nothing else:
*  minting and custody stay separate, so a login never writes through
*  the credential manager. */
var ThrowawayTokenStorage = class {
	tokens = null;
	async getTokens() {
		return this.tokens;
	}
	async setTokens(tokens) {
		this.tokens = tokens;
	}
	async clearTokens() {
		this.tokens = null;
	}
};
/** Runs the browser consent flow and RETURNS the minted credential.
*  Storing it is the caller's job. */
async function performLogin(env, signal, options) {
	const tokenStorage = new ThrowawayTokenStorage();
	await login({
		tokenStorage,
		env,
		signal,
		onVerificationUrl: options?.onVerificationUrl,
		uiContext: options?.uiContext
	});
	const tokens = tokenStorage.tokens;
	if (!tokens) throw new AuthError$1("Sign-in finished without producing a credential.");
	return {
		token: tokens.accessToken,
		refreshToken: tokens.refreshToken,
		expiresAt: claimedExpiresAt(tokens.accessToken)
	};
}
//#endregion
//#region ../cli/src/auth/service-token.ts
/**
* The env-supplied service token, trimmed — or undefined when the var
* is not set. A blank or whitespace value is never "not set" and never
* an override: it raises the single blank-token error, identically
* everywhere the environment credential would be consulted.
*/
function environmentServiceToken(env) {
	const raw = env[SERVICE_TOKEN_ENV_VAR];
	if (raw === void 0) return void 0;
	if (raw.trim().length === 0) throw emptyServiceTokenError({ envVar: SERVICE_TOKEN_ENV_VAR });
	return raw.trim();
}
/** Whether the environment credential is the one this process
*  authenticates as. It does not change stored state (design §11.7) —
*  this is a display fact. Blank raises. */
function environmentCredentialInForce(env) {
	return environmentServiceToken(env) !== void 0;
}
const PRISMA_CLI_PACKAGE_SPEC = `@prisma/cli@next`;
const DEFAULT_PRISMA_CLI_PACKAGE_RUNNER = ["npx", "-y"];
const PRISMA_CLI_BINARY = CLI_NAME;
function formatPrismaCliCommand(args, options = {}) {
	return [...getPrismaCliCommandPrefix(options), ...args].join(" ");
}
function getPrismaCliCommandPrefix({ invocation = "package", packageRunner = DEFAULT_PRISMA_CLI_PACKAGE_RUNNER }) {
	if (invocation === "binary") return [PRISMA_CLI_BINARY];
	return [...packageRunner, PRISMA_CLI_PACKAGE_SPEC];
}
//#endregion
//#region ../cli/src/lib/agent/package-manager.ts
const LOCKFILE_PACKAGE_MANAGERS = [
	{
		packageManager: "bun",
		fileNames: ["bun.lock", "bun.lockb"]
	},
	{
		packageManager: "pnpm",
		fileNames: ["pnpm-lock.yaml", "pnpm-workspace.yaml"]
	},
	{
		packageManager: "yarn",
		fileNames: ["yarn.lock"]
	},
	{
		packageManager: "npm",
		fileNames: ["package-lock.json", "npm-shrinkwrap.json"]
	}
];
async function resolvePackageRunner(options) {
	options.signal.throwIfAborted();
	const packageManager = detectPackageManagerSync(options.cwd, options.signal) ?? "npm";
	options.signal.throwIfAborted();
	return packageRunnerForPackageManager(packageManager);
}
function detectPackageManagerSync(cwd, signal) {
	let directory = path.resolve(cwd);
	while (true) {
		signal?.throwIfAborted();
		const packageJsonManager = readPackageJsonPackageManager(directory);
		if (packageJsonManager) return packageJsonManager;
		const lockfileManager = readLockfilePackageManager(directory, signal);
		if (lockfileManager) return lockfileManager;
		const parent = path.dirname(directory);
		if (parent === directory) return null;
		directory = parent;
	}
}
function readPackageJsonPackageManager(directory) {
	const packageJsonPath = path.join(directory, "package.json");
	let content;
	try {
		content = readFileSync(packageJsonPath, "utf8");
	} catch (error) {
		if (isMissingFileError(error)) return null;
		throw error;
	}
	try {
		return parsePackageManager(JSON.parse(content).packageManager);
	} catch {
		return null;
	}
}
function readLockfilePackageManager(directory, signal) {
	for (const candidate of LOCKFILE_PACKAGE_MANAGERS) for (const fileName of candidate.fileNames) {
		signal?.throwIfAborted();
		if (fileExists(path.join(directory, fileName))) return candidate.packageManager;
	}
	return null;
}
function fileExists(filePath) {
	try {
		return statSync(filePath).isFile();
	} catch (error) {
		if (isMissingFileError(error)) return false;
		throw error;
	}
}
function parsePackageManager(value) {
	if (typeof value !== "string") return null;
	const normalized = value.trim().toLowerCase();
	if (normalized === "bun" || normalized.startsWith("bun@")) return "bun";
	if (normalized === "pnpm" || normalized.startsWith("pnpm@")) return "pnpm";
	if (normalized === "yarn" || normalized.startsWith("yarn@")) return "yarn";
	if (normalized === "npm" || normalized.startsWith("npm@")) return "npm";
	return null;
}
/** The command that installs what package.json already declares —
*  init writes the version into the manifest, so a plain install is
*  right for every manager. */
function resolveInstallCommandSync(cwd) {
	return installCommandForPackageManager(detectPackageManagerSync(cwd) ?? "npm");
}
function installCommandForPackageManager(packageManager) {
	switch (packageManager) {
		case "bun": return "bun install";
		case "pnpm": return "pnpm install";
		case "yarn": return "yarn install";
		case "npm": return "npm install";
	}
}
function packageRunnerForPackageManager(packageManager) {
	switch (packageManager) {
		case "bun": return ["bunx"];
		case "pnpm": return ["pnpm", "dlx"];
		case "yarn": return ["yarn", "dlx"];
		case "npm": return ["npx", "-y"];
	}
}
function isMissingFileError(error) {
	const code = error.code;
	return code === "ENOENT" || code === "ENOTDIR";
}
//#endregion
//#region ../cli/src/lib/agent/cli-command.ts
async function resolvePrismaCliPackageCommandFormatter(options) {
	return createPrismaCliPackageCommandFormatter(await resolvePackageRunner(options));
}
async function resolvePrismaCliPackageCommand(options) {
	return (await resolvePrismaCliPackageCommandFormatter(options))(options.args);
}
function createPrismaCliPackageCommandFormatter(packageRunner) {
	return (args) => formatPrismaCliCommand(args, { packageRunner });
}
//#endregion
//#region ../cli/src/lib/semver-order.ts
function parseVersion(version) {
	const match = VERSION_PATTERN.exec(version);
	if (!match) return null;
	return {
		major: Number(match[1]),
		minor: Number(match[2]),
		patch: Number(match[3]),
		prerelease: match[4]?.split(".") ?? []
	};
}
const VERSION_PATTERN = /^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?$/;
const NUMERIC_PART = /^\d+$/;
/** Negative when left is older, positive when newer, 0 when equal, and
*  null when either side is not a version this understands. */
function compareVersionStrings(left, right) {
	const parsedLeft = parseVersion(left);
	const parsedRight = parseVersion(right);
	if (!parsedLeft || !parsedRight) return null;
	return compareVersions(parsedLeft, parsedRight);
}
function compareVersions(left, right) {
	for (const key of [
		"major",
		"minor",
		"patch"
	]) {
		const diff = left[key] - right[key];
		if (diff !== 0) return diff;
	}
	return comparePrerelease(left.prerelease, right.prerelease);
}
function comparePrerelease(left, right) {
	if (left.length === 0 && right.length === 0) return 0;
	if (left.length === 0) return 1;
	if (right.length === 0) return -1;
	const count = Math.max(left.length, right.length);
	for (let index = 0; index < count; index += 1) {
		const leftPart = left[index];
		const rightPart = right[index];
		if (leftPart === void 0) return -1;
		if (rightPart === void 0) return 1;
		const diff = comparePrereleasePart(leftPart, rightPart);
		if (diff !== 0) return diff;
	}
	return 0;
}
function comparePrereleasePart(left, right) {
	const leftNumber = NUMERIC_PART.test(left) ? Number(left) : null;
	const rightNumber = NUMERIC_PART.test(right) ? Number(right) : null;
	if (leftNumber !== null && rightNumber !== null) return leftNumber - rightNumber;
	if (leftNumber !== null) return -1;
	if (rightNumber !== null) return 1;
	return left.localeCompare(right);
}
//#endregion
//#region ../cli/src/lib/skills/allowlist.ts
/**
* SECURITY INVARIANT — read before changing this list.
*
* Skill content only ever comes from the packages named here. The sync
* command never scans node_modules, or any other directory, looking for
* skills to install, and no discovery mode may be added: a skill is
* instructions an agent will follow, so installing one from an
* arbitrary transitive dependency hands that dependency's author
* influence over the user's agent. Resolving these names keeps the
* trust boundary identical to the code's — if you run
* `@prisma/orm-postgres`, you already trust its author.
*
* Adding a package here is a deliberate decision about a package Prisma
* publishes. This is permanent.
*/
const SKILL_SOURCE_PACKAGES = [
	"@prisma/orm-postgres",
	"@prisma/orm-sqlite",
	"@prisma/orm-mongo",
	"@prisma/composer",
	"prisma"
];
/** The directory inside a source package's tarball that holds its skill
*  trees, one directory per skill. */
const PACKAGE_SKILLS_DIR = "skills";
/**
* The agent harnesses this CLI can install skills for, each mapped to
* the project-relative directory that harness reads its skills from.
* There is no harness detection anywhere: which of these a project uses
* comes from `skills: { agents: [...] }` in prisma.config.ts, and every
* one of them when the config says nothing.
*/
const AGENT_SKILL_DIRS = {
	claude: ".claude/skills",
	cursor: ".cursor/skills",
	agents: ".agents/skills",
	devin: ".devin/skills"
};
const KNOWN_AGENTS = Object.keys(AGENT_SKILL_DIRS);
/** Without a config, sync writes every known agent's directory, so a
*  harness adopted later finds the skills already there. */
const DEFAULT_AGENTS = KNOWN_AGENTS;
function isKnownAgent(name) {
	return name in AGENT_SKILL_DIRS;
}
function agentSkillDirs(agents) {
	return agents.map((agent) => AGENT_SKILL_DIRS[agent]);
}
function isSkillSourcePackage(name) {
	return SKILL_SOURCE_PACKAGES.includes(name);
}
//#endregion
//#region ../cli/src/lib/skills/unquote.ts
const QUOTED = /^(["'])(.*)\1$/;
/** Strips one layer of matching single or double quotes. */
function unquote(value) {
	return QUOTED.exec(value)?.[2] ?? value;
}
//#endregion
//#region ../cli/src/lib/skills/frontmatter.ts
const LINE_BREAK = /\r?\n/;
const INDENTED = /^[ \t]/;
const EMPTY_STAMP = {
	library: null,
	libraryVersion: null
};
const METADATA_KEY = "metadata";
const STAMP_KEYS = new Map([["library", "library"], ["library_version", "libraryVersion"]]);
/**
* The `library` and `library_version` entries of a SKILL.md's
* `metadata` map. The Agent Skills spec defines no custom top-level
* frontmatter keys — extensions live under `metadata`, a map of strings
* — so the stamp is read there and nowhere else. A file without
* frontmatter, without a `metadata` map, or without those entries
* reports nulls rather than failing.
*
* Contract: this reads only the stamp this CLI writes. A file it
* cannot read classifies as unmanaged, which sync refuses to touch, so
* a parse bug can never delete user files.
*/
function parseSkillStamp(source) {
	const lines = source.split(LINE_BREAK);
	if (lines[0]?.trim() !== "---") return EMPTY_STAMP;
	const stamp = {
		library: null,
		libraryVersion: null
	};
	let inMetadata = false;
	for (const line of lines.slice(1)) {
		if (line.trim() === "---") break;
		if (line.trim() === "") continue;
		if (!INDENTED.test(line)) {
			inMetadata = keyOf(line) === METADATA_KEY;
			continue;
		}
		if (!inMetadata) continue;
		const field = STAMP_KEYS.get(keyOf(line) ?? "");
		if (field) stamp[field] = valueAfterKey(line);
	}
	return stamp;
}
async function readSkillStamp(path) {
	try {
		return parseSkillStamp(await readFile(path, "utf8"));
	} catch {
		return null;
	}
}
function keyOf(line) {
	const separator = line.indexOf(":");
	return separator === -1 ? null : line.slice(0, separator).trim();
}
function valueAfterKey(line) {
	const separator = line.indexOf(":");
	return unquote(line.slice(separator + 1).trim());
}
//#endregion
//#region ../cli/src/lib/skills/opt-out.ts
/**
* The project's persisted answer to the staleness check, written by
* `skills sync --disable` and read by the check on every command. It
* sits at the project root beside the CLI's other local state, so the
* opt-out follows the project rather than one machine's environment.
*/
const SKILLS_STATE_FILE = path.join(".prisma", "skills.json");
function skillsStatePath(projectRoot) {
	return path.join(projectRoot, SKILLS_STATE_FILE);
}
async function readSkillsCheckDisabled(projectRoot) {
	try {
		return JSON.parse(await readFile(skillsStatePath(projectRoot), "utf8")).check === false;
	} catch {
		return false;
	}
}
async function writeSkillsCheckDisabled(projectRoot, disabled) {
	const target = skillsStatePath(projectRoot);
	await mkdir(path.dirname(target), { recursive: true });
	await writeFile(target, `${JSON.stringify({ check: !disabled }, null, 2)}\n`, "utf8");
}
//#endregion
//#region ../cli/src/lib/skills/resolve.ts
/**
* Standard module resolution of one named package from one directory.
* Under Yarn PnP this goes through the PnP resolver and answers a path
* inside a zip, which the patched filesystem reads like any other.
*/
async function resolvePackage(fromDir, packageName) {
	const dir = resolvePackageDir(fromDir, packageName);
	if (dir === null) return null;
	const version = await readPackageVersion(path.join(dir, "package.json"));
	return version === null ? null : {
		name: packageName,
		version,
		dir,
		resolvedFrom: fromDir
	};
}
function resolvePackageDir(fromDir, packageName) {
	const requireFrom = createRequire(path.join(fromDir, "package.json"));
	try {
		return path.dirname(requireFrom.resolve(`${packageName}/package.json`));
	} catch {}
	try {
		return packageRootOf(requireFrom.resolve(packageName), packageName);
	} catch {
		return null;
	}
}
/** Walks up from a resolved entry point to the directory named by the
*  package specifier — the last `node_modules/<name>` segment on the
*  path, or the first ancestor holding a package.json with that name. */
function packageRootOf(entry, packageName) {
	const marker = `${path.sep}node_modules${path.sep}${packageName.split("/").join(path.sep)}`;
	const at = entry.lastIndexOf(marker);
	if (at !== -1) return entry.slice(0, at + marker.length);
	let dir = path.dirname(entry);
	for (;;) {
		if (path.basename(dir) === path.basename(packageName)) return dir;
		const parent = path.dirname(dir);
		if (parent === dir) return null;
		dir = parent;
	}
}
async function readPackageVersion(manifestPath) {
	try {
		const manifest = JSON.parse(await readFile(manifestPath, "utf8"));
		return typeof manifest.version === "string" ? manifest.version : null;
	} catch {
		return null;
	}
}
//#endregion
//#region ../cli/src/lib/skills/workspace-members.ts
/**
* The workspace member directories declared by the root's workspace
* config, expanded from its globs by @manypkg/tools. Enumeration reads
* the declared globs from plain files (pnpm-workspace.yaml or
* package.json) and its glob expansion ignores node_modules, so a
* package is resolvable from a member directory only because the user
* declared that member — never because something was found by scanning.
*
* PnpmTool covers pnpm; YarnTool reads the package.json `workspaces`
* field in both its array and `{ packages }` forms, which is how npm,
* Yarn (Plug'n'Play included — the globs live in plain files), and bun
* all declare their members. Only directories holding a package.json
* come back.
*/
async function workspaceMemberDirs(root) {
	const dirs = /* @__PURE__ */ new Set();
	if (await hasPnpmWorkspace(root)) for (const dir of await memberDirsVia(PnpmTool, root)) dirs.add(dir);
	if (await hasPackageJsonWorkspaces(root)) for (const dir of await memberDirsVia(YarnTool, root)) dirs.add(dir);
	dirs.delete(path.resolve(root));
	return [...dirs].sort();
}
async function memberDirsVia(tool, root) {
	try {
		const { packages } = await tool.getPackages(root);
		return packages.map((pkg) => path.resolve(pkg.dir));
	} catch {
		return [];
	}
}
async function hasPnpmWorkspace(root) {
	try {
		await readFile(path.join(root, "pnpm-workspace.yaml"), "utf8");
		return true;
	} catch {
		return false;
	}
}
async function hasPackageJsonWorkspaces(root) {
	try {
		return JSON.parse(await readFile(path.join(root, "package.json"), "utf8")).workspaces !== void 0;
	} catch {
		return false;
	}
}
//#endregion
//#region ../cli/src/lib/skills/status.ts
/** Reads every skill the installed source packages ship and the state
*  of each copy in the configured agents' directories, plus the copies
*  the next sync will remove. */
async function readSkillsStatus(cwd, options) {
	const projectRoot = path.resolve(cwd);
	const agents = options?.agents ?? DEFAULT_AGENTS;
	const dirs = agentSkillDirs(agents);
	const unconfiguredDirs = agentSkillDirs(KNOWN_AGENTS.filter((agent) => !agents.includes(agent)));
	const checkDisabled = options?.checkDisabled ?? await readSkillsCheckDisabled(projectRoot);
	const packages = await findInstalledSourcePackages(projectRoot);
	const sources = await collectSkillSources(packages);
	const skills = [];
	for (const source of sources.values()) skills.push(await readSkillStatus(projectRoot, dirs, source));
	skills.sort((left, right) => left.skill.localeCompare(right.skill));
	return {
		projectRoot,
		checkDisabled,
		packages,
		skills,
		orphans: options?.orphans === false ? [] : await findOrphanedSkills(projectRoot, dirs, new Set(sources.keys()), unconfiguredDirs),
		upToDate: skills.every((skill) => skill.upToDate)
	};
}
/**
* The allowlisted packages installed in this project, resolved by name
* from the project root and from each declared workspace member. Never
* a directory scan.
*/
async function findInstalledSourcePackages(projectRoot) {
	const searchDirs = [projectRoot, ...await workspaceMemberDirs(projectRoot)];
	const found = [];
	for (const name of SKILL_SOURCE_PACKAGES) {
		const resolutions = [];
		for (const dir of searchDirs) {
			const resolved = await resolvePackage(dir, name);
			if (resolved !== null) resolutions.push(resolved);
		}
		if (resolutions.length === 0) continue;
		const highest = resolutions.reduce((best, candidate) => (compareVersionStrings(candidate.version, best.version) ?? 0) > 0 ? candidate : best);
		const versions = [...new Set(resolutions.map((one) => one.version))].sort();
		found.push({
			name,
			version: highest.version,
			dir: highest.dir,
			conflictingVersions: versions.length > 1 ? versions : []
		});
	}
	return found;
}
/** Every skill tree the installed source packages ship, keyed by skill
*  name. When two packages ship the same skill, the higher version
*  wins — the public packages version in lockstep, so this only
*  arbitrates a half-finished upgrade. */
async function collectSkillSources(packages) {
	const sources = /* @__PURE__ */ new Map();
	for (const installed of packages) {
		const skillsDir = path.join(installed.dir, PACKAGE_SKILLS_DIR);
		for (const skill of await skillDirectories(skillsDir)) {
			const existing = sources.get(skill);
			if (existing !== void 0 && (compareVersionStrings(installed.version, existing.version) ?? 0) <= 0) continue;
			sources.set(skill, {
				skill,
				library: installed.name,
				version: installed.version,
				dir: path.join(skillsDir, skill)
			});
		}
	}
	return sources;
}
async function readSkillStatus(projectRoot, dirs, source) {
	const targets = [];
	for (const dir of dirs) {
		const skillFile = path.join(projectRoot, dir, source.skill, "SKILL.md");
		const stamp = await readSkillStamp(skillFile);
		targets.push({
			dir,
			syncedVersion: stamp?.libraryVersion ?? null,
			state: await targetState(skillFile, stamp, source.version)
		});
	}
	return {
		skill: source.skill,
		library: source.library,
		version: source.version,
		sourceDir: source.dir,
		targets,
		upToDate: targets.every((target) => target.state === "synced" || target.state === "unmanaged")
	};
}
async function targetState(skillFile, stamp, sourceVersion) {
	if (stamp === null) return await missingFromDisk(skillFile) ? "absent" : "unmanaged";
	if (stamp.library === null || !isSkillSourcePackage(stamp.library)) return "unmanaged";
	return stamp.libraryVersion === sourceVersion ? "synced" : "stale";
}
async function missingFromDisk(target) {
	try {
		await stat(target);
		return false;
	} catch (error) {
		return error.code === "ENOENT";
	}
}
/**
* Copies in the harness directories that this CLI installed — their
* SKILL.md names an allowlisted package as its `library` — and that
* nothing wants any more: in a configured directory, one no installed
* package still provides; in the directory of an agent the config no
* longer names, every one. A skill from anywhere else is someone
* else's file and is never touched.
*/
async function findOrphanedSkills(projectRoot, dirs, provided, unconfiguredDirs = []) {
	const orphans = /* @__PURE__ */ new Map();
	const scan = [...dirs.map((dir) => ({
		dir,
		wanted: provided
	})), ...unconfiguredDirs.map((dir) => ({
		dir,
		wanted: /* @__PURE__ */ new Set()
	}))];
	for (const { dir, wanted } of scan) {
		const harnessDir = path.join(projectRoot, dir);
		for (const skill of await skillDirectories(harnessDir)) {
			if (wanted.has(skill)) continue;
			const stamp = await readSkillStamp(path.join(harnessDir, skill, "SKILL.md"));
			if (stamp?.library === null || stamp === null) continue;
			if (!isSkillSourcePackage(stamp.library)) continue;
			const entry = orphans.get(skill) ?? {
				library: stamp.library,
				dirs: []
			};
			entry.dirs.push(dir);
			orphans.set(skill, entry);
		}
	}
	return [...orphans.entries()].map(([skill, entry]) => ({
		skill,
		library: entry.library,
		dirs: entry.dirs
	}));
}
/** The subdirectories of `dir` that hold a SKILL.md. */
async function skillDirectories(dir) {
	let entries;
	try {
		entries = (await readdir(dir, { withFileTypes: true })).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
	} catch {
		return [];
	}
	const skills = [];
	for (const name of entries.sort()) if (await isFile(path.join(dir, name, "SKILL.md"))) skills.push(name);
	return skills;
}
async function isFile(target) {
	try {
		return (await stat(target)).isFile();
	} catch {
		return false;
	}
}
//#endregion
//#region ../cli/src/lib/version.ts
const requireFromHere = createRequire(import.meta.url);
/** The bundled entry sits one directory below the package root
*  (`dist/cli.js`); this source file sits two below (`src/lib/`). Both
*  are tried, nearest first, so the same code serves either. */
const PACKAGE_JSON_CANDIDATES = ["../package.json", "../../package.json"];
function readPackageMetadata() {
	for (const candidate of PACKAGE_JSON_CANDIDATES) try {
		const metadata = requireFromHere(candidate);
		if (metadata.version) return metadata;
	} catch {}
	return {};
}
function getCliVersion() {
	const pkg = readPackageMetadata();
	if (!pkg.version) throw new Error("CLI version metadata is missing from the installed package: the bundled package.json could not be read or did not contain a version field. Reinstall the CLI from the npm registry, or check your install path is intact.");
	return pkg.version;
}
function getCliName() {
	return CLI_NAME;
}
//#endregion
//#region ../cli/src/commands/skills/config.ts
const DEFAULT = {
	check: true,
	agents: DEFAULT_AGENTS,
	agentsConfigured: false
};
const SKILLS_CONFIG_SECTION_NAME = "skills";
function invalidSection(value) {
	return {
		code: "SKILLS.CONFIG_INVALID",
		severity: "error",
		summary: `The 'skills' config section must be an object, and is ${describe(value)}.`,
		nextActions: [{
			kind: "user-choice",
			label: "Write skills: { check: false } to silence the skills check."
		}]
	};
}
function invalidCheck(value) {
	return {
		code: "SKILLS.CONFIG_INVALID",
		severity: "error",
		summary: `skills.check must be true or false, and is ${describe(value)}.`,
		nextActions: [{
			kind: "user-choice",
			label: "Set skills.check to true or false, or remove it."
		}]
	};
}
function invalidAgents(value) {
	return {
		code: "SKILLS.CONFIG_INVALID",
		severity: "error",
		summary: `skills.agents must be an array of agent names, and is ${describe(value)}.`,
		nextActions: [{
			kind: "user-choice",
			label: `List the agents to install skills for (${KNOWN_AGENTS.join(", ")}), or remove skills.agents to install for all of them.`
		}]
	};
}
function unknownAgent(name) {
	return {
		code: "SKILLS.CONFIG_INVALID",
		severity: "error",
		summary: `skills.agents names '${name}', which this CLI does not know. The known agents are ${KNOWN_AGENTS.join(", ")}.`,
		nextActions: [{
			kind: "user-choice",
			label: `Remove '${name}' from skills.agents, or update the CLI if a newer version knows it.`
		}]
	};
}
function describe(value) {
	return value === null ? "null" : typeof value;
}
function validateAgents(raw) {
	if (raw === void 0) return {
		ok: true,
		agents: DEFAULT_AGENTS
	};
	if (!Array.isArray(raw)) return {
		ok: false,
		diagnostic: invalidAgents(raw)
	};
	const agents = [];
	for (const entry of raw) {
		if (typeof entry !== "string" || !isKnownAgent(entry)) return {
			ok: false,
			diagnostic: typeof entry === "string" ? unknownAgent(entry) : invalidAgents(entry)
		};
		if (!agents.includes(entry)) agents.push(entry);
	}
	return {
		ok: true,
		agents
	};
}
/** The disk loader bound the way the bin's Runtime binds it, for
*  callers with no Runtime in scope (the post-login tip). */
function projectConfigLoader(cwd) {
	return (configPath) => loadConfig(cwd, configPath, getCliVersion());
}
/**
* The project's skills settings, resolved per key over the discovered
* config chain exactly as the skills commands resolve them, so callers
* that run outside a command handler (the staleness check, the
* post-login tip) agree with the commands on the governing config from
* any directory. `load` is Runtime.loadConfig wherever a Runtime is in
* scope, so a host-supplied loader governs these reads too. Chain
* discovery is stat-only until a file exists, so a project without a
* config never pays a TypeScript transpile.
*
* Null deliberately collapses "no config" and "config broken or
* invalid": both callers fall back to the default agent set, so a
* broken config never silences the check. The commands that consume
* the config surface the error themselves.
*/
async function readProjectSkillsConfig(load, configPath) {
	const loaded = await load(configPath);
	if (loaded.diagnostics.some((entry) => entry.diagnostic.severity === "error") || loaded.files.length === 0) return null;
	const resolved = resolveSectionOverChain(skillsConfigSection, loaded.files);
	if (!resolved.ok) return null;
	const section = skillsConfigSection.validate(resolved.value, resolved.provenance);
	return section.ok ? section.value : null;
}
const skillsConfigSection = defineConfigSection({
	name: SKILLS_CONFIG_SECTION_NAME,
	validate: (raw) => {
		if (raw === void 0) return {
			ok: true,
			value: DEFAULT,
			diagnostics: []
		};
		if (typeof raw !== "object" || raw === null || Array.isArray(raw)) return {
			ok: false,
			diagnostics: [invalidSection(raw)]
		};
		let check;
		let rawAgents;
		try {
			check = raw.check;
			rawAgents = raw.agents;
		} catch {
			return {
				ok: false,
				diagnostics: [invalidSection(raw)]
			};
		}
		if (check !== void 0 && typeof check !== "boolean") return {
			ok: false,
			diagnostics: [invalidCheck(check)]
		};
		const agents = validateAgents(rawAgents);
		if (!agents.ok) return {
			ok: false,
			diagnostics: [agents.diagnostic]
		};
		return {
			ok: true,
			value: {
				check: check ?? true,
				agents: agents.agents,
				agentsConfigured: rawAgents !== void 0
			},
			diagnostics: []
		};
	}
});
//#endregion
//#region ../cli/src/commands/auth/agent-setup-tip.ts
/**
* The post-login skills tip. Login is the moment a developer sets a
* project up, so it points at `skills sync` when the project's synced
* agent skills do not match its installed Prisma packages. Silent in
* CI, in a directory with no skill-bearing Prisma packages, when the
* copies are current, and when the check is opted out.
*/
const SKILLS_SYNC_ARGS = ["skills", "sync"];
async function resolveAgentSetupTipCommand(ctx) {
	if (ctx.env.CI) return null;
	try {
		const config = await readProjectSkillsConfig(projectConfigLoader(ctx.cwd), ctx.configPath);
		if (config !== null && !config.check) return null;
		const status = await readSkillsStatus(ctx.cwd, {
			orphans: false,
			agents: config?.agents
		});
		if (status.packages.length === 0 || status.upToDate || status.checkDisabled) return null;
		return await resolvePrismaCliPackageCommand({
			cwd: ctx.cwd,
			signal: ctx.signal,
			args: SKILLS_SYNC_ARGS
		});
	} catch {
		return null;
	}
}
//#endregion
//#region ../cli/src/commands/auth/credential-card.ts
const ENVIRONMENT_CREDENTIAL_NOTICE = `${SERVICE_TOKEN_ENV_VAR} supplies the credential in force; unset it to use your stored workspace sessions.`;
const UNVERIFIED_CREDENTIAL_NOTICE = "Could not reach Prisma to confirm this sign-in. Showing what the local credential says.";
/** The card rows for the active credential, or the signed-out row when
*  there is none. A credential nothing names — an environment token
*  whose claims carry no workspace — has no workspace row at all. */
function credentialFieldRows(spec) {
	const credential = spec.credential;
	if (credential === null) return [{
		label: "status",
		value: "signed out"
	}];
	const rows = [{
		label: "status",
		value: "signed in"
	}];
	if (spec.identity?.email !== void 0) rows.push({
		label: "user",
		value: spec.identity.email
	});
	if (credential.workspaceId !== void 0) rows.push({
		label: "workspace",
		value: credential.workspaceName ?? credential.workspaceId
	});
	if (credential.origin.source === "environment") rows.push({
		label: "environment variable",
		value: SERVICE_TOKEN_ENV_VAR
	});
	return rows;
}
//#endregion
//#region ../cli/src/auth/legacy-state.ts
const LEGACY_PLACEHOLDER_NAME = "Unknown workspace";
/**
* The sessions re-serialized in the legacy store's record shape. The
* 3.x CLI reads `tokens` from auth.json (`data.tokens || []`, silently
* empty for any other shape), so a write that dropped the key made
* every session invisible to `@prisma/cli@latest` on the same machine
* the moment this CLI first mutated the file (#204). Sessions without
* a refresh token still mirror; the legacy reader skips them, exactly
* as it skips its own unrefreshable records.
*/
function legacyTokensMirror(sessions) {
	return sessions.map((session) => ({
		workspaceId: session.workspaceId,
		token: session.token,
		...session.refreshToken === void 0 ? {} : { refreshToken: session.refreshToken }
	}));
}
/**
* Keeps auth.context.json's `activeWorkspaceId` — the pointer the 3.x
* CLI selects its session with — in step with `currentWorkspaceId`.
* The rest of the context file (the remembered-workspace name map) is
* preserved verbatim; only the pointer moves.
*/
async function syncLegacyContext(authFilePath, currentWorkspaceId) {
	const contextFilePath = getAuthContextFilePath(authFilePath);
	const context = await readLegacyContext(contextFilePath);
	if (context.exists && context.activeWorkspaceId === currentWorkspaceId) return;
	if (!context.exists && currentWorkspaceId === null) return;
	const raw = await fs.readFile(contextFilePath, "utf8").catch(() => null);
	let workspaces = {};
	if (raw !== null) try {
		const parsed = JSON.parse(raw);
		if (typeof parsed.workspaces === "object" && parsed.workspaces !== null && !Array.isArray(parsed.workspaces)) workspaces = parsed.workspaces;
	} catch {}
	const tempPath = `${contextFilePath}.${randomUUID()}.tmp`;
	const payload = `${JSON.stringify({
		activeWorkspaceId: currentWorkspaceId,
		workspaces
	}, null, 2)}\n`;
	try {
		await fs.writeFile(tempPath, payload, "utf8");
		await fs.rename(tempPath, contextFilePath);
	} catch (error) {
		await fs.unlink(tempPath).catch(() => {});
		throw error;
	}
}
/**
* The legacy store read as sessions. Pure: adoption never writes, and
* the legacy files stay untouched until a mutation materializes the
* adopted set in the new format.
*/
async function adoptLegacyState(parsedAuthFile, authFilePath) {
	const entries = parsedAuthFile.tokens;
	if (!Array.isArray(entries)) return {
		version: 1,
		sessions: [],
		currentWorkspaceId: null
	};
	const context = await readLegacyContext(getAuthContextFilePath(authFilePath));
	const adopted = /* @__PURE__ */ new Map();
	for (const entry of entries) {
		const session = adoptLegacyEntry(entry, context);
		if (session) adopted.set(session.workspaceId, session);
	}
	const sessions = [...adopted.values()];
	return {
		version: 1,
		sessions,
		currentWorkspaceId: adoptedCurrent(sessions, context)
	};
}
function adoptLegacyEntry(entry, context) {
	if (typeof entry !== "object" || entry === null) return void 0;
	const { token, refreshToken } = entry;
	if (typeof token !== "string" || token.length === 0) return void 0;
	const workspaceId = credentialWorkspaceId(token);
	if (workspaceId === void 0) return void 0;
	const name = adoptedName(context.names[workspaceId], workspaceId);
	const expiresAt = claimedExpiresAt(token);
	return {
		workspaceId,
		...name === void 0 ? {} : { name },
		token,
		...typeof refreshToken === "string" && refreshToken.length > 0 ? { refreshToken } : {},
		...expiresAt === void 0 ? {} : { expiresAt: expiresAt.toISOString() }
	};
}
/** Legacy placeholders do not adopt: a name equal to "Unknown
*  workspace" or to the workspace id adopts as no name at all. */
function adoptedName(name, workspaceId) {
	const trimmed = name?.trim();
	if (!trimmed) return void 0;
	if (trimmed === LEGACY_PLACEHOLDER_NAME) return void 0;
	if (trimmed === workspaceId) return void 0;
	return trimmed;
}
function adoptedCurrent(sessions, context) {
	if (context.exists) {
		const pointed = context.activeWorkspaceId;
		return pointed !== null && sessions.some((session) => session.workspaceId === pointed) ? pointed : null;
	}
	return sessions.length === 1 ? sessions[0].workspaceId : null;
}
async function readLegacyContext(contextFilePath) {
	const absent = {
		exists: false,
		activeWorkspaceId: null,
		names: {}
	};
	const raw = await fs.readFile(contextFilePath, "utf8").catch(() => null);
	if (raw === null) return absent;
	let parsed;
	try {
		parsed = JSON.parse(raw);
	} catch {
		return absent;
	}
	if (typeof parsed !== "object" || parsed === null) return absent;
	const { activeWorkspaceId, workspaces } = parsed;
	const names = {};
	if (typeof workspaces === "object" && workspaces !== null && !Array.isArray(workspaces)) for (const [workspaceId, value] of Object.entries(workspaces)) {
		const name = value?.name;
		if (typeof name === "string") names[workspaceId] = name;
	}
	return {
		exists: true,
		activeWorkspaceId: typeof activeWorkspaceId === "string" && activeWorkspaceId.trim() ? activeWorkspaceId.trim() : null,
		names
	};
}
//#endregion
//#region ../cli/src/auth/state-file.ts
const STATE_FILE_ENV_VAR = "PRISMA_AUTH_FILE";
const DEPRECATED_STATE_FILE_ENV_VAR = "PRISMA_COMPUTE_AUTH_FILE";
const FILE_MODE = 384;
const LOCK_STALE_MS = 5e3;
const LOCK_RETRY_MS = 10;
const LOCK_WAIT_TIMEOUT_MS = 1e4;
const REFRESH_LOCK_STALE_MS = 3e4;
const REFRESH_LOCK_RETRY_MS = 100;
const REFRESH_LOCK_WAIT_TIMEOUT_MS = 3e4;
const STATE_LOCK_TIMINGS = {
	staleMs: LOCK_STALE_MS,
	retryMs: LOCK_RETRY_MS,
	waitTimeoutMs: LOCK_WAIT_TIMEOUT_MS
};
const REFRESH_LOCK_TIMINGS = {
	staleMs: REFRESH_LOCK_STALE_MS,
	retryMs: REFRESH_LOCK_RETRY_MS,
	waitTimeoutMs: REFRESH_LOCK_WAIT_TIMEOUT_MS
};
const EMPTY_STATE = {
	version: 1,
	sessions: [],
	currentWorkspaceId: null
};
function makeDebugLog(env, write = (text) => {
	process.stderr.write(text);
}) {
	if (env.PRISMA_DEBUG !== "1") return () => {};
	return (message) => {
		write(`prisma auth: ${message}\n`);
	};
}
function resolveStateFilePath(env) {
	const configured = env[STATE_FILE_ENV_VAR];
	if (configured?.trim()) return {
		filePath: path.resolve(configured),
		fromDeprecatedEnvVar: false
	};
	const deprecated = env[DEPRECATED_STATE_FILE_ENV_VAR];
	if (deprecated?.trim()) return {
		filePath: path.resolve(deprecated),
		fromDeprecatedEnvVar: true
	};
	return {
		filePath: defaultAuthFilePath(env),
		fromDeprecatedEnvVar: false
	};
}
function credentialsUnreadableError(filePath, cause) {
	return new CliStructuredError("CLI.CREDENTIALS_UNREADABLE", "Your stored credentials could not be read.", {
		why: `The credentials file at ${filePath} exists but could not be read.`,
		nextActions: [{
			kind: "user-choice",
			label: "Check the file's permissions, then run the command again."
		}],
		cause
	});
}
/**
* The stored state: the new format as written, the legacy store adopted
* (§7 — a pure read that writes nothing), or empty. Reads take no lock:
* writes rename a complete file into place.
*/
async function readCredentialState(filePath) {
	let raw;
	try {
		raw = await fs.readFile(filePath, "utf8");
	} catch (error) {
		if (error.code === "ENOENT") return EMPTY_STATE;
		throw credentialsUnreadableError(filePath, error);
	}
	let parsed;
	try {
		parsed = JSON.parse(raw);
	} catch {
		return EMPTY_STATE;
	}
	if (typeof parsed !== "object" || parsed === null) return EMPTY_STATE;
	const shape = parsed;
	if (Array.isArray(shape.sessions)) return {
		version: typeof shape.version === "number" ? shape.version : 1,
		sessions: shape.sessions.filter(isStoredSession).map(normalizeSession),
		currentWorkspaceId: typeof shape.currentWorkspaceId === "string" && shape.currentWorkspaceId.length > 0 ? shape.currentWorkspaceId : null
	};
	return adoptLegacyState(parsed, filePath);
}
function isStoredSession(value) {
	if (typeof value !== "object" || value === null) return false;
	const candidate = value;
	return typeof candidate.workspaceId === "string" && candidate.workspaceId.length > 0 && typeof candidate.token === "string" && candidate.token.length > 0;
}
function normalizeSession(session) {
	const user = normalizeStoredSessionUser(session.user);
	return {
		workspaceId: session.workspaceId,
		...typeof session.name === "string" && session.name.length > 0 ? { name: session.name } : {},
		...user === void 0 ? {} : { user },
		token: session.token,
		...typeof session.refreshToken === "string" && session.refreshToken.length > 0 ? { refreshToken: session.refreshToken } : {},
		...typeof session.expiresAt === "string" && session.expiresAt.length > 0 ? { expiresAt: session.expiresAt } : {}
	};
}
function normalizeStoredSessionUser(value) {
	if (typeof value !== "object" || value === null || Array.isArray(value)) return;
	const candidate = value;
	const id = normalizedString(candidate.id);
	const email = normalizedString(candidate.email);
	const name = normalizedString(candidate.name);
	if (id === void 0 && email === void 0 && name === void 0) return;
	return {
		...id === void 0 ? {} : { id },
		...email === void 0 ? {} : { email },
		...name === void 0 ? {} : { name }
	};
}
function normalizedString(value) {
	if (typeof value !== "string") return void 0;
	const trimmed = value.trim();
	return trimmed.length === 0 ? void 0 : trimmed;
}
/** Temp file in the same directory, fsync, rename, mode 0600 — a reader
*  only ever sees a complete state. The written file also carries the
*  legacy `tokens` mirror and the auth.context.json pointer stays in
*  step, so the 3.x CLI sharing this store keeps seeing the sessions
*  (#204). Our own reader branches on `sessions` before it ever looks
*  at `tokens`, so the mirror is invisible to this CLI. */
async function writeCredentialState(filePath, state) {
	await fs.mkdir(path.dirname(filePath), { recursive: true });
	const tempPath = `${filePath}.${randomUUID()}.tmp`;
	const payload = {
		...state,
		tokens: legacyTokensMirror(state.sessions)
	};
	try {
		const handle = await fs.open(tempPath, "wx", FILE_MODE);
		try {
			await handle.writeFile(`${JSON.stringify(payload, null, 2)}\n`, "utf8");
			await handle.sync();
		} finally {
			await handle.close();
		}
		await fs.rename(tempPath, filePath);
	} catch (error) {
		await fs.unlink(tempPath).catch(() => {});
		throw error;
	}
	await fs.chmod(filePath, FILE_MODE).catch(() => {});
	await syncLegacyContext(filePath, state.currentWorkspaceId);
}
var StateLockTimeoutError = class extends CliStructuredError {
	constructor(lockPath, waitTimeoutMs) {
		super("CLI.CREDENTIALS_LOCKED", "Another prisma process is still updating your credentials.", {
			why: `The credentials lock at ${lockPath} was held for longer than ${waitTimeoutMs}ms.`,
			nextActions: [{
				kind: "user-choice",
				label: "Wait for the other command to finish, then try again."
			}]
		});
	}
};
/**
* The short advisory lock every mutation takes: acquire, re-read, apply
* one slice, write, release. Its only job is lost-update prevention
* between processes. No network I/O ever runs under it, so holds are
* milliseconds and a crashed holder's lock is simply taken over after a
* small fixed staleness threshold.
*/
async function withStateLock(filePath, debug, run) {
	return withFileLock(`${filePath}.lock`, debug, STATE_LOCK_TIMINGS, run);
}
/**
* The cross-process lock the delegated refresh holds for its whole
* read → exchange → write sequence, so two processes never spend the
* same refresh token. Distinct from the state lock: it IS held across
* network I/O, so its staleness and wait budgets are larger, and it
* uses its own lock path so short mutations are not queued behind it.
*/
async function withRefreshFileLock(filePath, debug, run) {
	return withFileLock(`${filePath}.refresh-lock`, debug, REFRESH_LOCK_TIMINGS, run);
}
async function withFileLock(lockPath, debug, timings, run) {
	const lockId = await acquireStateLock(lockPath, debug, timings);
	debug(`lock acquired ${lockPath}`);
	try {
		return await run();
	} finally {
		await releaseStateLock(lockPath, lockId);
		debug(`lock released ${lockPath}`);
	}
}
async function acquireStateLock(lockPath, debug, timings) {
	const lockId = randomUUID();
	const startedAt = Date.now();
	await fs.mkdir(path.dirname(lockPath), { recursive: true });
	while (true) {
		if (await tryCreateStateLock(lockPath, lockId)) return lockId;
		const tookOver = await takeOverStaleStateLock(lockPath, debug, timings);
		if (Date.now() - startedAt >= timings.waitTimeoutMs) throw new StateLockTimeoutError(lockPath, timings.waitTimeoutMs);
		if (!tookOver) await new Promise((resolve) => setTimeout(resolve, timings.retryMs));
	}
}
async function tryCreateStateLock(lockPath, lockId) {
	let handle;
	try {
		handle = await fs.open(lockPath, "wx", FILE_MODE);
	} catch (error) {
		if (error.code === "EEXIST") return false;
		throw error;
	}
	try {
		await handle.writeFile(lockId, "utf8");
	} finally {
		await handle.close();
	}
	return true;
}
/**
* Clear a crashed holder's lock. Removing it by RENAME is what makes
* two waiting processes safe: only one of them can rename a given
* path, so only one clears the corpse. Unlinking instead lets the
* second process delete the FIRST one's freshly created lock — both
* then run their read-modify-write at once and one update is lost,
* which is the very thing the lock exists to prevent.
*/
async function takeOverStaleStateLock(lockPath, debug, timings) {
	const stale = await fs.stat(lockPath).catch(() => null);
	if (stale === null) return true;
	if (Date.now() - stale.mtimeMs <= timings.staleMs) return false;
	const takenPath = `${lockPath}.${randomUUID()}.stale`;
	try {
		await fs.rename(lockPath, takenPath);
	} catch {
		return false;
	}
	const taken = await fs.stat(takenPath).catch(() => null);
	if (taken !== null && taken.mtimeMs !== stale.mtimeMs) {
		await fs.link(takenPath, lockPath).catch(() => {});
		await fs.unlink(takenPath).catch(() => {});
		return false;
	}
	await fs.unlink(takenPath).catch(() => {});
	debug(`lock taken over from a crashed holder ${lockPath}`);
	return true;
}
async function releaseStateLock(lockPath, lockId) {
	if (await fs.readFile(lockPath, "utf8").catch(() => null) !== lockId) return;
	await fs.unlink(lockPath).catch(() => {});
}
//#endregion
//#region ../cli/src/auth/credential-manager.ts
/** The SDK's Tokens requires a workspace id, so an environment
*  credential whose claims name no workspace is given this instead. It
*  never leaves the manager, and it is never the empty string. */
const NO_WORKSPACE_CLAIMED = "(no workspace)";
/** Session display metadata is a CLI concern, not part of the shared engine
*  contract. FileCredentialManager provides it; other managers degrade to the
*  standard local session shape without inventing an account identity. */
async function sessionsForDisplay(manager) {
	if (isAccountAwareCredentialManager(manager)) return manager.enrichSessions();
	return manager.sessions();
}
function sessionIdentity(session) {
	return isAccountSession(session) ? session.identity : void 0;
}
function isAccountSession(session) {
	return "identity" in session;
}
function isAccountAwareCredentialManager(manager) {
	return "enrichSessions" in manager && typeof manager.enrichSessions === "function";
}
/**
* The memory-backed storage, for a credential with no home record: a
* free function closing over one local variable. It is never given the
* state file's path, so no method of it — clearTokens included — can
* reach the stored sessions, and an environment credential whose
* workspace matches a stored session cannot delete that session.
*/
function memoryBackedStorage(credential, withRefreshLock) {
	let tokens = {
		workspaceId: credentialWorkspaceId(credential.token) ?? NO_WORKSPACE_CLAIMED,
		accessToken: credential.token,
		refreshToken: credential.refreshToken,
		expiresAt: claimedExpiresAt(credential.token) ?? credential.expiresAt
	};
	return {
		getTokens: async () => tokens,
		setTokens: async (rotated, expiresAt) => {
			tokens = {
				...rotated,
				expiresAt: claimedExpiresAt(rotated.accessToken) ?? expiresAt ?? tokens?.expiresAt
			};
		},
		clearTokens: async () => {
			tokens = null;
		},
		withRefreshLock
	};
}
/**
* The credential manager over one state file. Sessions are keyed by
* workspace id; which credential this process acts as is decided once;
* every mutation takes a short file lock, re-reads, applies its slice,
* and writes atomically. Reads never write and take no lock.
*/
var FileCredentialManager = class {
	#env;
	#filePath;
	#debug;
	#fetchSessionMetadata;
	#refreshCredential;
	#actingAs = { kind: "unresolved" };
	/** Built for the credential the process acts as. Every mutation that
	*  changes that discards it, so a command that mutates and then
	*  reaches for ctx.api cannot be handed storage for the credential it
	*  used to be acting as. */
	#activeStorage;
	#refreshLock = Promise.resolve();
	constructor(options) {
		this.#env = options.env;
		this.#filePath = resolveStateFilePath(options.env).filePath;
		this.#debug = makeDebugLog(options.env, options.debugWrite);
		this.#fetchSessionMetadata = options.fetchSessionMetadata;
		this.#refreshCredential = options.refreshCredential;
		this.#debug(`state file ${this.#filePath}`);
	}
	get stateFilePath() {
		return this.#filePath;
	}
	async activeCredential() {
		const actingAs = await this.#resolveActingAs();
		if (actingAs.kind === "environment") return environmentCredential(this.#requireEnvironmentToken());
		const state = await readCredentialState(this.#filePath);
		if (actingAs.kind === "none") {
			if (state.sessions.length > 0) throw credentialsRequiredError("sessions-held-none-selected");
			return null;
		}
		const record = state.sessions.find((session) => session.workspaceId === actingAs.workspaceId);
		if (record === void 0) throw credentialsRequiredError("session-ended");
		return storedCredential(record);
	}
	async sessions() {
		return storedSessions(await readCredentialState(this.#filePath));
	}
	async enrichSessions() {
		if (this.#fetchSessionMetadata === void 0) return this.sessions();
		const state = await readCredentialState(this.#filePath);
		const now = Date.now();
		const candidates = state.sessions.filter((session) => lacksFetchableMetadata(session, now));
		if (candidates.length === 0) return storedSessions(state);
		const fetched = await Promise.all(candidates.map(async (session) => ({
			workspaceId: session.workspaceId,
			token: session.token,
			metadata: await this.#lookUpSessionMetadata(session)
		})));
		if (fetched.every((result) => result.metadata === void 0)) return this.sessions();
		const byWorkspaceId = new Map(fetched.map((result) => [result.workspaceId, result]));
		return this.#mutate((current) => {
			let changed = false;
			const sessions = current.sessions.map((session) => {
				const fetchedSession = byWorkspaceId.get(session.workspaceId);
				if (fetchedSession === void 0 || fetchedSession.token !== session.token) return session;
				const name = session.name ?? fetchedSession.metadata?.workspaceName;
				const user = session.user ?? fetchedSession.metadata?.user;
				if (name === session.name && user === session.user) return session;
				changed = true;
				return {
					...session,
					name,
					user
				};
			});
			if (!changed) return { result: storedSessions(current) };
			const next = {
				...current,
				sessions
			};
			return {
				state: next,
				result: storedSessions(next)
			};
		});
	}
	async createSession(credential, workspaceId) {
		const environmentInForce = this.#environmentToken() !== void 0;
		const claimed = credentialWorkspaceId(credential.token);
		if (claimed !== void 0 && claimed !== workspaceId) throw credentialWorkspaceMismatchError(workspaceId);
		await this.#mutate((state) => {
			const existing = state.sessions.find((session) => session.workspaceId === workspaceId);
			const record = {
				workspaceId,
				...existing?.name === void 0 ? {} : { name: existing.name },
				token: credential.token,
				...credential.refreshToken === void 0 ? {} : { refreshToken: credential.refreshToken },
				...expiresAtSlice(credential.token, credential.expiresAt)
			};
			return {
				state: {
					...state,
					sessions: [...state.sessions.filter((session) => session.workspaceId !== workspaceId), record],
					currentWorkspaceId: workspaceId
				},
				result: void 0
			};
		});
		if (!environmentInForce) this.#actAs({
			kind: "session",
			workspaceId
		});
		const { workspaceName: name, user } = await this.#lookUpSessionMetadata(credential) ?? {};
		return this.#mutate((state) => {
			const record = state.sessions.find((session) => session.workspaceId === workspaceId);
			if (record === void 0) throw credentialsRequiredError("session-ended");
			if (record.token !== credential.token || name === void 0 && user === void 0) return { result: toSession(record) };
			const enriched = {
				...record,
				...name === void 0 ? {} : { name },
				...user === void 0 ? {} : { user }
			};
			return {
				state: {
					...state,
					sessions: state.sessions.map((session) => session.workspaceId === workspaceId ? enriched : session)
				},
				result: toSession(enriched)
			};
		});
	}
	async selectSession(workspaceId) {
		const environmentInForce = this.#environmentToken() !== void 0;
		const selected = await this.#mutate((state) => {
			const record = requireRecord(state, workspaceId);
			return {
				state: {
					...state,
					currentWorkspaceId: workspaceId
				},
				result: toSession(record)
			};
		});
		if (!environmentInForce) this.#actAs({
			kind: "session",
			workspaceId
		});
		return selected;
	}
	/** Idempotent (§11.8): a workspace with no session is already in the
	*  state this asks for, so the slice writes nothing and succeeds. */
	async endSession(workspaceId) {
		this.#refuseBlankEnvironmentToken();
		await this.#mutate((state) => state.sessions.some((session) => session.workspaceId === workspaceId) ? {
			state: withoutRecord(state, workspaceId),
			result: void 0
		} : { result: void 0 });
		if (this.#actingAs.kind === "session" && this.#actingAs.workspaceId === workspaceId) this.#actAs({ kind: "none" });
	}
	async endAllSessions() {
		const environmentInForce = this.#environmentToken() !== void 0;
		await this.#mutate((state) => state.sessions.length === 0 && state.currentWorkspaceId === null ? { result: void 0 } : {
			state: EMPTY_STATE,
			result: void 0
		});
		await this.#reapLegacyContextFile();
		await this.#reapOrphanedWrites();
		if (!environmentInForce) this.#actAs({ kind: "none" });
	}
	async activeCredentialStorage() {
		this.#activeStorage ??= this.#buildActiveStorage();
		return this.#activeStorage;
	}
	/** The delegated path's read: the active credential's access token,
	*  fresh on every call, never the refresh token. Null when there is
	*  no active credential to read — storage exists only once
	*  activeCredential() has returned non-null. */
	async activeAccessToken(options) {
		if (await this.activeCredential() === null) return null;
		return readActiveAccessToken(await this.activeCredentialStorage(), this.#refreshCredential, options);
	}
	/** §11.2: which storage is chosen once, when the acting-as decision
	*  resolves. Each
	*  has exactly one source of truth — the file, or process memory. */
	#buildActiveStorage() {
		const actingAs = this.#actingAs;
		if (actingAs.kind === "environment") return memoryBackedStorage({
			token: this.#requireEnvironmentToken(),
			refreshToken: void 0,
			expiresAt: void 0
		}, (fn) => this.#withRefreshLock(fn));
		if (actingAs.kind === "session") return this.#fileBackedStorage(actingAs.workspaceId);
		throw new Error("@prisma/cli: activeCredentialStorage() is only valid once activeCredential() has returned non-null");
	}
	/**
	* The file-backed storage, for a credential with a home record.
	* getTokens re-reads the file on EVERY call with no memory layer in
	* front: that is what lets this process see a pair another process
	* already rotated to, skip the exchange, and retry.
	*/
	#fileBackedStorage(workspaceId) {
		return {
			getTokens: async () => {
				const record = (await readCredentialState(this.#filePath)).sessions.find((session) => session.workspaceId === workspaceId);
				if (record === void 0) return null;
				return {
					workspaceId,
					accessToken: record.token,
					...record.refreshToken === void 0 ? {} : { refreshToken: record.refreshToken },
					...record.expiresAt === void 0 ? {} : { expiresAt: new Date(record.expiresAt) }
				};
			},
			setTokens: async (tokens, expiresAt) => {
				this.#debug(`rotation write for session ${workspaceId}`);
				const claimed = credentialWorkspaceId(tokens.accessToken);
				if (claimed !== void 0 && claimed !== workspaceId) throw credentialWorkspaceMismatchError(workspaceId);
				await this.#mutate((state) => {
					const record = state.sessions.find((session) => session.workspaceId === workspaceId);
					if (record === void 0) throw credentialsRequiredError("session-ended");
					const rotated = {
						workspaceId: record.workspaceId,
						...record.name === void 0 ? {} : { name: record.name },
						user: record.user,
						token: tokens.accessToken,
						...tokens.refreshToken === void 0 ? {} : { refreshToken: tokens.refreshToken },
						...expiresAtSlice(tokens.accessToken, expiresAt ?? (record.expiresAt === void 0 ? void 0 : new Date(record.expiresAt)))
					};
					return {
						state: {
							...state,
							sessions: state.sessions.map((session) => session.workspaceId === workspaceId ? rotated : session)
						},
						result: void 0
					};
				});
			},
			clearTokens: async () => {
				this.#debug(`clearing session ${workspaceId}`);
				await this.#mutate((state) => ({
					state: withoutRecord(state, workspaceId),
					result: void 0
				}));
			},
			clearTokensIfCurrent: async (tokens) => {
				this.#debug(`clearing session ${workspaceId} if its pair still matches`);
				await this.#mutate((state) => {
					const record = state.sessions.find((session) => session.workspaceId === workspaceId);
					if (!(record !== void 0 && tokens.workspaceId === workspaceId && tokens.accessToken === record.token && tokens.refreshToken === record.refreshToken)) return { result: void 0 };
					return {
						state: withoutRecord(state, workspaceId),
						result: void 0
					};
				});
			},
			withRefreshLock: (fn) => this.#withRefreshLock(() => withRefreshFileLock(this.#filePath, this.#debug, fn))
		};
	}
	#withRefreshLock(fn) {
		const run = this.#refreshLock.then(fn, fn);
		this.#refreshLock = run.then(() => void 0, () => void 0);
		return run;
	}
	async #resolveActingAs() {
		const decided = this.#actingAs;
		if (decided.kind !== "unresolved") return decided;
		if (this.#environmentToken() !== void 0) {
			this.#debug("acting as the environment credential");
			this.#actingAs = { kind: "environment" };
			return { kind: "environment" };
		}
		const selected = resolvedMarker(await readCredentialState(this.#filePath));
		this.#debug(`acting as session ${selected ?? "(none)"}`);
		const resolved = selected === null ? { kind: "none" } : {
			kind: "session",
			workspaceId: selected
		};
		this.#actingAs = resolved;
		return resolved;
	}
	/** Changes which credential the process acts as after a mutation,
	*  discarding storage built for the previous one. */
	#actAs(next) {
		this.#actingAs = next;
		this.#activeStorage = void 0;
	}
	#environmentToken() {
		return environmentServiceToken(this.#env);
	}
	#requireEnvironmentToken() {
		const token = this.#environmentToken();
		if (token === void 0) throw credentialsRequiredError();
		return token;
	}
	/** A blank PRISMA_SERVICE_TOKEN is an error state everywhere the
	*  environment credential would be consulted, including the two
	*  mutations that do not otherwise read it. Reading is what raises;
	*  the value is deliberately unused. */
	#refuseBlankEnvironmentToken() {
		this.#environmentToken();
	}
	/** endAllSessions clears everything, including the legacy context
	*  sidecar, which survives a store that was already empty. */
	async #reapLegacyContextFile() {
		await fs.unlink(getAuthContextFilePath(this.#filePath)).catch(() => {});
	}
	/** A write that died between creating its temp file and renaming it
	*  leaves a full copy of the state, tokens and all. Someone running
	*  `auth logout` to revoke local access must not be left holding a
	*  working refresh token in an orphan. Writes take the lock and last
	*  milliseconds, so anything still here is one. */
	async #reapOrphanedWrites() {
		const directory = path.dirname(this.#filePath);
		const prefix = `${path.basename(this.#filePath)}.`;
		const entries = await fs.readdir(directory).catch(() => []);
		await Promise.all(entries.filter((entry) => entry.startsWith(prefix) && entry.endsWith(".tmp")).map((entry) => fs.unlink(path.join(directory, entry)).catch(() => {})));
	}
	async #lookUpSessionMetadata(credential) {
		if (this.#fetchSessionMetadata === void 0) return void 0;
		try {
			const metadata = await this.#fetchSessionMetadata(credential);
			const workspaceName = metadata?.workspaceName?.trim() || void 0;
			const user = normalizeStoredSessionUser(metadata?.user);
			if (workspaceName === void 0 && user === void 0) return void 0;
			return {
				workspaceName,
				user
			};
		} catch {
			return;
		}
	}
	/** One mutation: the short lock, a fresh read, one slice, one atomic
	*  write. A slice that returns no state writes nothing. */
	async #mutate(apply) {
		return withStateLock(this.#filePath, this.#debug, async () => {
			const applied = apply(await readCredentialState(this.#filePath));
			if (applied.state !== void 0) await writeCredentialState(this.#filePath, applied.state);
			return applied.result;
		});
	}
};
function requireRecord(state, workspaceId) {
	const record = state.sessions.find((session) => session.workspaceId === workspaceId);
	if (record === void 0) throw noSessionForWorkspaceError(workspaceId);
	return record;
}
function withoutRecord(state, workspaceId) {
	return {
		...state,
		sessions: state.sessions.filter((session) => session.workspaceId !== workspaceId),
		currentWorkspaceId: state.currentWorkspaceId === workspaceId ? null : state.currentWorkspaceId
	};
}
function expiresAtSlice(token, fallback) {
	const expiresAt = claimedExpiresAt(token) ?? fallback;
	return expiresAt === void 0 ? {} : { expiresAt: expiresAt.toISOString() };
}
/** The selection the manager will admit to: one that names a stored
*  session, or none. A dangling selection never escapes. */
function resolvedMarker(state) {
	const marked = state.currentWorkspaceId;
	if (marked !== null && state.sessions.some((session) => session.workspaceId === marked)) return marked;
	return null;
}
/** Whether a lookup with this session's token could add metadata. An
*  expired token is rejected, and a workspace-only token has no user. */
function lacksFetchableMetadata(session, now) {
	if (session.expiresAt !== void 0 && Date.parse(session.expiresAt) <= now) return false;
	if (session.name === void 0) return true;
	return session.user === void 0 && claimedIdentity(session.token)?.userId !== void 0;
}
function storedSessions(state) {
	return {
		sessions: state.sessions.map((record) => toSession(record)),
		selectedWorkspaceId: resolvedMarker(state) ?? void 0
	};
}
function toSession(record) {
	return {
		workspaceId: record.workspaceId,
		workspaceName: record.name,
		identity: storedIdentity(record),
		expiresAt: record.expiresAt === void 0 ? void 0 : new Date(record.expiresAt)
	};
}
function storedCredential(record) {
	return {
		workspaceId: record.workspaceId,
		workspaceName: record.name,
		expiresAt: record.expiresAt === void 0 ? void 0 : new Date(record.expiresAt),
		identity: storedIdentity(record),
		origin: { source: "stored" }
	};
}
function storedIdentity(record) {
	const user = record.user;
	return user === void 0 ? claimedIdentity(record.token) : {
		userId: user.id,
		email: user.email,
		name: user.name
	};
}
/** An environment token whose claims name no workspace reports no
*  workspace id — never the empty string. */
function environmentCredential(token) {
	return {
		workspaceId: credentialWorkspaceId(token),
		workspaceName: void 0,
		expiresAt: claimedExpiresAt(token),
		identity: claimedIdentity(token),
		origin: { source: "environment" }
	};
}
//#endregion
//#region ../cli/src/commands/auth/session-ref.ts
/**
* Command-side resolution of a user-typed workspace reference against
* the sessions the credential manager holds. The manager resolves no
* user input: the commands match the ref and pass the matched session's
* workspace id. This is also where a workspace the user never had is
* caught, which is why removal being idempotent still leaves a mistyped
* ref with a useful error.
*/
/** The safe identity fields a command may expose for a stored session. */
function sessionUser(session) {
	const identity = sessionIdentity(session);
	if (identity === void 0) return null;
	return {
		id: identity.userId ?? null,
		email: identity.email ?? null,
		name: identity.name ?? null
	};
}
/** The shortest useful human identity for a workspace session. */
function sessionUserLabel(session) {
	const identity = sessionIdentity(session);
	return identity?.email ?? identity?.name ?? identity?.userId;
}
/** Workspace first, account second: suitable for interactive choices. */
function sessionChoiceLabel(session) {
	const user = sessionUserLabel(session);
	return user === void 0 ? sessionLabel(session) : `${sessionLabel(session)} — ${user}`;
}
/** Exact workspace id first, then case-insensitive workspace name. */
function resolveSessionRef(sessions, ref) {
	const wanted = ref.trim();
	const byId = sessions.find((session) => session.workspaceId === wanted);
	if (byId !== void 0) return {
		kind: "matched",
		session: byId
	};
	const byName = sessions.filter((session) => session.workspaceName !== void 0 && session.workspaceName.toLowerCase() === wanted.toLowerCase());
	if (byName.length === 1) return {
		kind: "matched",
		session: byName[0]
	};
	if (byName.length > 1) return {
		kind: "ambiguous",
		matches: byName
	};
	return { kind: "no-match" };
}
function ambiguousSessionRefError(ref, matches) {
	return new CliStructuredError("AUTH.WORKSPACE_AMBIGUOUS", `More than one workspace session is named '${ref}'.`, {
		why: `Matching sessions: ${matches.map((match) => {
			const user = sessionUserLabel(match);
			return user === void 0 ? match.workspaceId : `${match.workspaceId} (${user})`;
		}).join(", ")}.`,
		meta: {
			workspaceIds: matches.map((match) => match.workspaceId),
			sessions: matches.map((match) => ({
				workspaceId: match.workspaceId,
				user: sessionUser(match)
			}))
		},
		nextActions: [{
			kind: "run-command",
			label: "List your workspace sessions and pass a workspace id",
			command: `${CLI_NAME} auth workspace list`
		}]
	});
}
/**
* Resolves the ref or throws the structured error for its failure —
* the ruled "no session for X" error for a ref that matches nothing.
*/
function requireSession(sessions, ref) {
	const resolution = resolveSessionRef(sessions, ref);
	if (resolution.kind === "ambiguous") throw ambiguousSessionRefError(ref, resolution.matches);
	if (resolution.kind === "no-match") throw noSessionForWorkspaceError(ref);
	return resolution.session;
}
/** How a session is named to users: its workspace name, or its id
*  when no name was ever fetched. */
function sessionLabel(session) {
	return session.workspaceName ?? session.workspaceId;
}
//#endregion
//#region ../cli/src/commands/auth/login.ts
const TITLE$14 = "Starting an authenticated CLI session.";
const LOGIN_STEP = "Sign in via your browser";
/** The minted credential names no workspace, so no session can be
*  keyed by one. */
function loginWorkspaceUnknownError() {
	return new CliStructuredError("AUTH.LOGIN_WORKSPACE_UNKNOWN", "Sign-in produced a credential that names no workspace.", {
		why: "A workspace session is keyed by the credential's workspace_id claim, and this credential carries none.",
		nextActions: [{
			kind: "run-command",
			label: "Sign in again and pick a workspace in the browser",
			command: `${CLI_NAME} auth login`
		}]
	});
}
function nextActionsFor$1(agentSetupTipCommand) {
	return [
		{
			kind: "run-command",
			label: "Show the signed-in identity",
			command: `${CLI_NAME} auth whoami`
		},
		{
			kind: "run-command",
			label: "List projects",
			command: `${CLI_NAME} project list`
		},
		...agentSetupTipCommand === null ? [] : [{
			kind: "run-command",
			label: "Install Prisma skills for this project",
			command: agentSetupTipCommand
		}]
	];
}
function presentationsFor$2(spec, result) {
	const user = sessionUserLabel(spec.session);
	const rows = [
		{
			label: "status",
			value: "signed in"
		},
		...user === void 0 ? [] : [{
			label: "user",
			value: user
		}],
		{
			label: "workspace",
			value: sessionLabel(spec.session)
		}
	];
	return {
		json: () => result,
		human: () => [
			{
				kind: "summary",
				status: "info",
				text: TITLE$14
			},
			{
				kind: "fields",
				rows
			},
			...spec.environmentCredentialInForce ? [{
				kind: "summary",
				status: "info",
				text: ENVIRONMENT_CREDENTIAL_NOTICE
			}] : [],
			...spec.agentSetupTipCommand === null ? [] : [{
				kind: "summary",
				status: "info",
				text: `Install Prisma skills for this project with ${spec.agentSetupTipCommand}.`
			}]
		],
		stdout: () => rows.map((row) => `${row.label}: ${row.value}`),
		next: () => nextActionsFor$1(spec.agentSetupTipCommand)
	};
}
const authLoginCommand = defineCommand({
	managesCredentials: true,
	args: { flags: { uiContext: flag.enum({
		values: ["prisma-plugin"],
		brief: "Show browser completion guidance for the Prisma plugin in ChatGPT (default: terminal guidance)"
	}) } },
	help: {
		summary: "Log in to your Prisma platform account",
		description: "Opens a browser sign-in and stores a session for one workspace, the account-level container that holds your Projects. Run it again to add a session for another workspace; 'auth workspace use' switches between stored sessions. In CI or other non-interactive environments, skip login and set PRISMA_SERVICE_TOKEN instead.",
		examples: ["auth login"]
	},
	handler: async (args, ctx) => {
		const environmentSession = environmentCredentialInForce(ctx.env);
		ctx.report({
			kind: "step-started",
			step: LOGIN_STEP
		});
		let session;
		try {
			const credential = await performLogin(ctx.env, ctx.signal, {
				uiContext: args.flags.uiContext,
				onVerificationUrl: (url) => ctx.report({
					kind: "endpoint",
					name: "verification",
					url
				})
			});
			const workspaceId = credentialWorkspaceId(credential.token);
			if (workspaceId === void 0) throw loginWorkspaceUnknownError();
			session = await ctx.credentialManager.createSession(credential, workspaceId);
		} catch (error) {
			ctx.report({
				kind: "step-finished",
				step: LOGIN_STEP,
				outcome: "failed"
			});
			throw error;
		}
		ctx.report({
			kind: "step-finished",
			step: LOGIN_STEP,
			outcome: "ok"
		});
		const agentSetupTipCommand = await resolveAgentSetupTipCommand(ctx);
		const result = {
			workspace: {
				id: session.workspaceId,
				name: session.workspaceName ?? null
			},
			user: sessionUser(session),
			environmentCredentialInForce: environmentSession
		};
		return ok(ctx.present({ data: result }, presentationsFor$2({
			session,
			environmentCredentialInForce: environmentSession,
			agentSetupTipCommand
		}, result)));
	}
});
//#endregion
//#region ../cli/src/commands/auth/logout.ts
const SIGN_IN$1 = {
	kind: "run-command",
	label: "Sign in",
	command: `${CLI_NAME} auth login`
};
function presentationsFor$1(result, environmentInForce) {
	const summary = result.endedCount === 0 ? "No workspace sessions to end." : `Ended ${result.endedCount} workspace ${result.endedCount === 1 ? "session" : "sessions"}.`;
	const rows = [{
		label: "ended",
		value: String(result.endedCount)
	}];
	return {
		json: () => result,
		human: () => [
			{
				kind: "summary",
				status: "info",
				text: "Clearing your stored workspace sessions."
			},
			{
				kind: "fields",
				rows
			},
			{
				kind: "summary",
				status: "ok",
				text: summary
			},
			...environmentInForce ? [{
				kind: "summary",
				status: "info",
				text: ENVIRONMENT_CREDENTIAL_NOTICE
			}] : []
		],
		stdout: () => rows.map((row) => `${row.label}: ${row.value}`),
		next: () => [SIGN_IN$1]
	};
}
const authLogoutCommand = defineCommand({
	managesCredentials: true,
	help: {
		summary: "Clear stored authentication credentials",
		description: "Ends every workspace session stored on this machine. To end just one and keep the others, use 'auth workspace logout' instead. A credential supplied via PRISMA_SERVICE_TOKEN is not stored here and stays in force.",
		examples: ["auth logout"]
	},
	handler: async (_args, ctx) => {
		const stored = await ctx.credentialManager.sessions();
		await ctx.credentialManager.endAllSessions();
		const result = {
			endedCount: stored.sessions.length,
			workspaceIds: stored.sessions.map((session) => session.workspaceId)
		};
		return ok(ctx.present({ data: result }, presentationsFor$1(result, environmentCredentialInForce(ctx.env))));
	}
});
//#endregion
//#region ../cli/src/commands/auth/whoami.ts
const TITLE$13 = "Showing the active authenticated identity.";
const SIGN_IN = {
	kind: "run-command",
	label: "Sign in",
	command: `${CLI_NAME} auth login`
};
/** whoami answers from the credential's own claims, so the lookup is
*  worth a moment and no more. ctx.signal only fires on Ctrl-C, and
*  nothing else bounds a request: a host that accepts the connection
*  and never answers would otherwise hold the command for minutes. */
const ENRICHMENT_TIMEOUT_MS = 3e3;
/** Best-effort online enrichment: whoami works offline, so a transient
*  failure leaves the identity as the credential's own claims said.
*  CLI.CREDENTIALS_REQUIRED means signed out; AUTH.SERVICE_TOKEN_REJECTED
*  is rethrown because signing in cannot fix an environment token. */
async function fetchedIdentity(api, signal) {
	const bounded = AbortSignal.any([signal, AbortSignal.timeout(ENRICHMENT_TIMEOUT_MS)]);
	try {
		const { data } = await api.GET("/v1/me", { signal: bounded });
		if (data === void 0) return { kind: "inconclusive" };
		const user = data.data?.user;
		return {
			kind: "confirmed",
			identity: user ? {
				userId: user.id ?? void 0,
				email: user.email ?? void 0,
				name: user.name ?? void 0
			} : void 0
		};
	} catch (cause) {
		signal.throwIfAborted();
		if (CliStructuredError.is(cause)) {
			if (cause.code === "CLI.CREDENTIALS_REQUIRED") return { kind: "signed-out" };
			if (cause.code === "AUTH.SERVICE_TOKEN_REJECTED") throw cause;
		}
		return { kind: "inconclusive" };
	}
}
/**
* `/v1/me` wins field by field where it disagrees with the claims, and
* the claims are the offline fallback — but only while both describe
* the same person. The two are read at different moments, so another
* process replacing the session in between can leave the claims
* describing one user and the lookup another; filling a gap in one from
* the other would then invent a person who does not exist. When the two
* name different users, the lookup is taken whole.
*/
function mergedIdentity(claimed, fetched) {
	if (fetched === void 0) return claimed ?? null;
	if (claimed === void 0) return fetched;
	if (!(fetched.userId === void 0 || claimed.userId === void 0 || fetched.userId === claimed.userId)) return fetched;
	return {
		userId: fetched.userId ?? claimed.userId,
		email: fetched.email ?? claimed.email,
		name: fetched.name ?? claimed.name
	};
}
function presentationsFor(spec, result) {
	const rows = credentialFieldRows(spec);
	const fromEnvironment = spec.credential?.origin.source === "environment";
	return {
		json: () => result,
		human: () => [
			{
				kind: "summary",
				status: "info",
				text: TITLE$13
			},
			{
				kind: "fields",
				rows
			},
			...fromEnvironment ? [{
				kind: "summary",
				status: "info",
				text: ENVIRONMENT_CREDENTIAL_NOTICE
			}] : [],
			...result.authenticated && !result.verified ? [{
				kind: "summary",
				status: "info",
				text: UNVERIFIED_CREDENTIAL_NOTICE
			}] : []
		],
		stdout: () => rows.map((row) => `${row.label}: ${row.value}`),
		next: () => spec.credential === null ? [SIGN_IN] : []
	};
}
const authWhoamiCommand = defineCommand({
	help: {
		summary: "Show who is signed in and which workspace commands target",
		description: "Shows which identity the CLI is acting as and which workspace its commands will target. Run it to check you are in the right workspace before creating or deleting resources, or to see whether a PRISMA_SERVICE_TOKEN credential is overriding your stored sessions.",
		examples: ["auth whoami", "auth whoami --json"]
	},
	handler: async (_args, ctx) => {
		const active = await ctx.activeCredential();
		const lookup = active === null ? void 0 : await fetchedIdentity(ctx.api, ctx.signal);
		const credential = lookup?.kind === "signed-out" ? null : active;
		const identity = credential === null ? null : mergedIdentity(credential.identity, lookup?.kind === "confirmed" ? lookup.identity : void 0);
		const result = {
			authenticated: credential !== null,
			verified: credential !== null && lookup?.kind === "confirmed",
			workspace: credential === null || credential.workspaceId === void 0 ? null : {
				id: credential.workspaceId,
				name: credential.workspaceName ?? null
			},
			user: identity === null ? null : {
				id: identity.userId ?? null,
				email: identity.email ?? null,
				name: identity.name ?? null
			},
			source: credential?.origin.source ?? null,
			expiresAt: credential?.expiresAt?.toISOString() ?? null
		};
		return ok(ctx.present({ data: result }, presentationsFor({
			credential,
			identity
		}, result)));
	}
});
//#endregion
//#region ../cli/src/commands/auth/workspace-list.ts
/** The `auth workspace list` command. */
const LOGIN_NEXT_ACTION = {
	kind: "run-command",
	label: "Authorize a workspace",
	command: `${CLI_NAME} auth login`
};
function serializeWorkspaceList(result) {
	return {
		context: {
			scope: "local-sessions",
			environmentCredentialInForce: result.environmentCredentialInForce,
			currentWorkspaceId: result.selectedWorkspaceId ?? null
		},
		items: result.sessions.map((session) => ({
			workspaceId: session.workspaceId,
			workspaceName: session.workspaceName ?? null,
			user: sessionUser(session),
			current: session.workspaceId === result.selectedWorkspaceId,
			expiresAt: session.expiresAt?.toISOString() ?? null
		})),
		count: result.sessions.length
	};
}
function listPresentations$9(result) {
	const columns = [
		"workspace",
		"user",
		"id",
		"status"
	];
	const rows = result.sessions.map((session) => [
		sessionLabel(session),
		sessionUserLabel(session) ?? "",
		session.workspaceId,
		session.workspaceId === result.selectedWorkspaceId ? "current" : ""
	]);
	return {
		human: () => [
			{
				kind: "summary",
				status: "info",
				text: "Listing your workspace sessions on this machine."
			},
			...result.environmentCredentialInForce ? [{
				kind: "summary",
				status: "info",
				text: ENVIRONMENT_CREDENTIAL_NOTICE
			}] : [],
			...result.sessions.length === 0 ? [{
				kind: "summary",
				status: "info",
				text: "No workspace sessions found."
			}] : [{
				kind: "table",
				columns,
				rows
			}]
		],
		stdout: () => result.sessions.map((session) => [
			sessionLabel(session),
			session.workspaceId,
			session.workspaceId === result.selectedWorkspaceId ? "current" : ""
		].join("  ").trimEnd()),
		json: () => serializeWorkspaceList(result),
		next: () => result.sessions.length === 0 ? [LOGIN_NEXT_ACTION] : []
	};
}
const authWorkspaceListCommand = defineCommand({
	managesCredentials: true,
	help: {
		summary: "List your workspace sessions",
		description: "Each 'auth login' stores one session per workspace. This lists the sessions on this machine and marks the current one, which every workspace-scoped command targets unless a PRISMA_SERVICE_TOKEN credential is set; that credential overrides stored sessions.",
		examples: ["auth workspace list", "auth workspace list --json"]
	},
	handler: async (_args, ctx) => {
		const stored = await sessionsForDisplay(ctx.credentialManager);
		const result = {
			sessions: stored.sessions,
			selectedWorkspaceId: stored.selectedWorkspaceId,
			environmentCredentialInForce: environmentCredentialInForce(ctx.env)
		};
		return ok(ctx.present({ data: result }, listPresentations$9(result)));
	}
});
//#endregion
//#region ../cli/src/commands/auth/workspace-logout.ts
/** The `auth workspace logout` command: ends one workspace session. */
function logoutPresentations(spec, result) {
	const rows = [{
		label: "workspace",
		value: spec.label
	}, ...spec.user === void 0 ? [] : [{
		label: "user",
		value: spec.user
	}]];
	return {
		json: () => result,
		human: () => [
			{
				kind: "summary",
				status: "info",
				text: "Ending a workspace session."
			},
			{
				kind: "fields",
				rows
			},
			{
				kind: "summary",
				status: "ok",
				text: spec.wasSelected ? "Ended the current workspace session; no replacement was selected." : "Ended the workspace session."
			},
			...spec.environmentCredentialInForce ? [{
				kind: "summary",
				status: "info",
				text: ENVIRONMENT_CREDENTIAL_NOTICE
			}] : []
		],
		stdout: () => rows.map((row) => `${row.label}: ${row.value}`),
		next: () => [{
			kind: "run-command",
			label: "List your workspace sessions",
			command: `${CLI_NAME} auth workspace list`
		}, ...spec.wasSelected ? [{
			kind: "run-command",
			label: "Make another session current",
			command: `${CLI_NAME} auth workspace use <id>`
		}] : []]
	};
}
const authWorkspaceLogoutCommand = defineCommand({
	managesCredentials: true,
	args: { positionals: { workspace: positional.string({
		brief: "Workspace id or name",
		placeholder: "id-or-name"
	}) } },
	help: {
		summary: "End one workspace session",
		description: "Removes the stored session for one workspace and leaves the others in place. To clear every session on this machine, use 'auth logout'.",
		examples: ["auth workspace logout my-workspace"]
	},
	handler: async (args, ctx) => {
		const stored = await ctx.credentialManager.sessions();
		const session = requireSession(stored.sessions, args.positionals.workspace);
		const wasSelected = session.workspaceId === stored.selectedWorkspaceId;
		await ctx.credentialManager.endSession(session.workspaceId);
		const result = {
			workspace: {
				id: session.workspaceId,
				name: session.workspaceName ?? null
			},
			user: sessionUser(session),
			wasSelected
		};
		return ok(ctx.present({ data: result }, logoutPresentations({
			label: sessionLabel(session),
			user: sessionUserLabel(session),
			wasSelected,
			environmentCredentialInForce: environmentCredentialInForce(ctx.env)
		}, result)));
	}
});
//#endregion
//#region ../cli/src/commands/auth/workspace-use.ts
/** The `auth workspace use` command: it SELECTS among the sessions you
*  have — it never creates one, and never opens a browser. */
function noWorkspaceSessionsError() {
	return new CliStructuredError("AUTH.NO_WORKSPACE_SESSIONS", "You have no workspace sessions to select from.", { nextActions: [{
		kind: "run-command",
		label: "Sign in and pick a workspace in the browser",
		command: `${CLI_NAME} auth login`
	}] });
}
function usePresentations(spec, result) {
	const user = sessionUserLabel(spec.session);
	const rows = [
		...spec.previous === void 0 ? [] : [{
			label: "previous",
			value: sessionLabel(spec.previous)
		}],
		{
			label: "workspace",
			value: sessionLabel(spec.session)
		},
		...user === void 0 ? [] : [{
			label: "user",
			value: user
		}]
	];
	return {
		json: () => result,
		human: () => [
			{
				kind: "summary",
				status: "info",
				text: "Switching the current workspace session."
			},
			{
				kind: "fields",
				rows
			},
			{
				kind: "summary",
				status: "ok",
				text: "Current workspace session updated."
			},
			...spec.environmentCredentialInForce ? [{
				kind: "summary",
				status: "info",
				text: ENVIRONMENT_CREDENTIAL_NOTICE
			}] : []
		],
		stdout: () => rows.map((row) => `${row.label}: ${row.value}`),
		next: () => [{
			kind: "run-command",
			label: "Show the signed-in identity",
			command: `${CLI_NAME} auth whoami`
		}, {
			kind: "run-command",
			label: "List projects",
			command: `${CLI_NAME} project list`
		}]
	};
}
const authWorkspaceUseCommand = defineCommand({
	managesCredentials: true,
	args: { positionals: { workspace: positional.optionalString({
		brief: "Workspace id or name",
		placeholder: "id-or-name"
	}) } },
	help: {
		summary: "Make a workspace session current: commands target that workspace",
		description: "Switches which workspace subsequent commands target. This changes local CLI state only; nothing is created or modified on the platform. Run without an argument to pick from your stored sessions. A PRISMA_SERVICE_TOKEN credential, when set, overrides stored sessions: commands keep targeting its workspace regardless of the selection here.",
		examples: ["auth workspace use", "auth workspace use my-workspace"]
	},
	handler: async (args, ctx) => {
		const ref = args.positionals.workspace?.trim();
		const stored = ref ? await sessionsForRef(ctx.credentialManager, ref) : await sessionsForDisplay(ctx.credentialManager);
		if (stored.sessions.length === 0) throw noWorkspaceSessionsError();
		const chosen = ref ? requireSession(stored.sessions, ref) : await promptForSession(stored, ctx.prompt.select);
		const previous = stored.sessions.find((session) => session.workspaceId === stored.selectedWorkspaceId);
		const session = await ctx.credentialManager.selectSession(chosen.workspaceId);
		const result = {
			workspace: {
				id: session.workspaceId,
				name: session.workspaceName ?? null
			},
			user: sessionUser(session),
			previousWorkspaceId: previous?.workspaceId ?? null
		};
		return ok(ctx.present({ data: result }, usePresentations({
			session,
			previous,
			environmentCredentialInForce: environmentCredentialInForce(ctx.env)
		}, result)));
	}
});
/** An explicit ref is a local switch: it resolves against stored state and
*  waits for no lookup. Only a ref that matches nothing looks metadata up,
*  because a session saved before names were stored may be the one meant. */
async function sessionsForRef(manager, ref) {
	const stored = await manager.sessions();
	if (resolveSessionRef(stored.sessions, ref).kind !== "no-match") return stored;
	return sessionsForDisplay(manager);
}
async function promptForSession(stored, select) {
	if (stored.sessions.length === 1) return stored.sessions[0];
	const workspaceId = await select("Select a workspace", stored.sessions.map((session) => ({
		value: session.workspaceId,
		label: `${sessionChoiceLabel(session)} (${session.workspaceId})${session.workspaceId === stored.selectedWorkspaceId ? " current" : ""}`
	})));
	return requireSession(stored.sessions, workspaceId);
}
//#endregion
//#region ../cli/src/controllers/branch.ts
function sortBranches(branches) {
	return branches.slice().sort((left, right) => {
		const leftRank = branchOrder(left);
		const rightRank = branchOrder(right);
		if (leftRank !== rightRank) return leftRank - rightRank;
		return left.name.localeCompare(right.name);
	});
}
function branchOrder(branch) {
	return branch.role === "production" ? 0 : 1;
}
async function listBranches$1(client, projectId, signal) {
	const collected = [];
	let cursor;
	while (true) {
		const query = {};
		if (cursor !== void 0) query.cursor = cursor;
		const { data, error, response } = await client.GET("/v1/projects/{projectId}/branches", {
			params: {
				path: { projectId },
				query
			},
			signal
		});
		if (error || !data) throw branchApiError("Failed to list branches", response, error);
		collected.push(...data.data);
		if (!data.pagination.hasMore || !data.pagination.nextCursor) break;
		cursor = data.pagination.nextCursor;
	}
	return collected;
}
function toBranchSummary(branch) {
	return {
		id: branch.id,
		name: branch.gitName,
		role: branch.role,
		envMap: branch.role
	};
}
function branchApiError(summary, response, error) {
	const status = response?.status ?? 0;
	const apiCode = error?.error?.code;
	return new CliStructuredError("BRANCH.API_ERROR", summary, {
		why: error?.error?.message ?? `The Management API returned status ${status || "unknown"}.`,
		...status || apiCode !== void 0 ? { meta: {
			...status ? { status } : {},
			...apiCode !== void 0 ? { apiCode } : {}
		} } : {},
		nextActions: [{
			kind: "user-choice",
			label: error?.error?.hint ?? "Re-run with --log-level verbose for the underlying API response details."
		}]
	});
}
//#endregion
//#region ../cli/src/lib/project/prisma-dir.ts
/**
* Walks up from cwd to the nearest directory containing a `.prisma/`
* directory and returns that directory, or null when no ancestor has
* one. Pure filesystem check — no config file is read or evaluated.
* Nearest wins by design: a nested directory deliberately linked to a
* different project beats the repo root.
*/
async function findNearestPrismaDir(cwd) {
	let dir = path.resolve(cwd);
	for (;;) {
		if (await isDirectory(path.join(dir, ".prisma"))) return dir;
		const parent = path.dirname(dir);
		if (parent === dir) return null;
		dir = parent;
	}
}
async function isDirectory(candidate) {
	try {
		return (await stat(candidate)).isDirectory();
	} catch {
		return false;
	}
}
//#endregion
//#region ../cli/src/lib/project/local-pin.ts
const LOCAL_RESOLUTION_PIN_RELATIVE_PATH = ".prisma/local.json";
var LocalResolutionPinInvalidJsonError = class extends TaggedError("LocalResolutionPinInvalidJsonError")() {
	constructor(cause) {
		super({
			message: `${LOCAL_RESOLUTION_PIN_RELATIVE_PATH} contains invalid JSON.`,
			cause,
			pinPath: LOCAL_RESOLUTION_PIN_RELATIVE_PATH
		});
	}
};
var LocalResolutionPinInvalidShapeError = class extends TaggedError("LocalResolutionPinInvalidShapeError")() {
	constructor() {
		super({
			message: `${LOCAL_RESOLUTION_PIN_RELATIVE_PATH} must contain workspaceId and projectId string fields only.`,
			pinPath: LOCAL_RESOLUTION_PIN_RELATIVE_PATH
		});
	}
};
var LocalResolutionPinReadAbortedError = class extends TaggedError("LocalResolutionPinReadAbortedError")() {
	constructor(cause) {
		super({
			message: `Reading ${LOCAL_RESOLUTION_PIN_RELATIVE_PATH} was aborted.`,
			cause,
			pinPath: LOCAL_RESOLUTION_PIN_RELATIVE_PATH
		});
	}
};
var LocalResolutionPinSerializationError = class extends TaggedError("LocalResolutionPinSerializationError")() {
	constructor(cause) {
		super({
			message: `Could not serialize ${LOCAL_RESOLUTION_PIN_RELATIVE_PATH}.`,
			cause,
			pinPath: LOCAL_RESOLUTION_PIN_RELATIVE_PATH
		});
	}
};
var LocalResolutionPinWriteAbortedError = class extends TaggedError("LocalResolutionPinWriteAbortedError")() {
	constructor(cause) {
		super({
			message: `Writing ${LOCAL_RESOLUTION_PIN_RELATIVE_PATH} was aborted.`,
			cause,
			pinPath: LOCAL_RESOLUTION_PIN_RELATIVE_PATH
		});
	}
};
var LocalResolutionPinWriteFailedError = class extends TaggedError("LocalResolutionPinWriteFailedError")() {
	constructor(operation, cause) {
		super({
			message: `Could not write ${LOCAL_RESOLUTION_PIN_RELATIVE_PATH}.`,
			cause,
			operation,
			pinPath: LOCAL_RESOLUTION_PIN_RELATIVE_PATH
		});
	}
};
var LocalResolutionPinGitignoreUpdateAbortedError = class extends TaggedError("LocalResolutionPinGitignoreUpdateAbortedError")() {
	constructor(cause) {
		super({
			message: "Updating .gitignore for the local Project binding was aborted.",
			cause,
			gitignorePath: ".gitignore"
		});
	}
};
var LocalResolutionPinGitignoreUpdateFailedError = class extends TaggedError("LocalResolutionPinGitignoreUpdateFailedError")() {
	constructor(operation, cause) {
		super({
			message: "Could not update .gitignore for the local Project binding.",
			cause,
			operation,
			gitignorePath: ".gitignore"
		});
	}
};
/** Reads the pin at the nearest ancestor with a `.prisma/` directory;
*  without one anywhere up the tree, reads at cwd and finds nothing.
*  Writes never walk — only discovery does. */
async function readLocalResolutionPin(cwd, signal) {
	return Result.gen(async function* () {
		yield* ensureLocalResolutionPinReadNotAborted(signal);
		const directory = await findNearestPrismaDir(cwd) ?? cwd;
		const file = yield* Result.await(readLocalResolutionPinFile(directory, signal));
		if (file.kind === "missing") return Result.ok({ kind: "missing" });
		const parsed = yield* parseLocalResolutionPin(file.raw);
		if (!isLocalResolutionPin(parsed)) return Result.err(new LocalResolutionPinInvalidShapeError());
		return Result.ok({
			kind: "present",
			pin: parsed,
			directory
		});
	});
}
function ensureLocalResolutionPinReadNotAborted(signal) {
	return Result.try({
		try: () => signal?.throwIfAborted(),
		catch: (cause) => new LocalResolutionPinReadAbortedError(cause)
	});
}
async function readLocalResolutionPinFile(cwd, signal) {
	const readResult = await Result.tryPromise({
		try: () => readFile(path.join(cwd, LOCAL_RESOLUTION_PIN_RELATIVE_PATH), {
			encoding: "utf8",
			signal
		}),
		catch: (cause) => signal?.aborted ? new LocalResolutionPinReadAbortedError(cause) : new UnhandledException({ cause })
	});
	if (readResult.isErr()) {
		if (readResult.error instanceof UnhandledException && readResult.error.cause.code === "ENOENT") return Result.ok({ kind: "missing" });
		return Result.err(readResult.error);
	}
	return Result.ok({
		kind: "present",
		raw: readResult.value
	});
}
function parseLocalResolutionPin(raw) {
	return Result.try({
		try: () => JSON.parse(raw),
		catch: (cause) => cause instanceof SyntaxError ? new LocalResolutionPinInvalidJsonError(cause) : new UnhandledException({ cause })
	});
}
async function writeLocalResolutionPin(cwd, pin, signal) {
	return Result.gen(async function* () {
		const prismaDir = path.join(cwd, ".prisma");
		yield* ensureLocalResolutionPinWriteNotAborted(signal);
		yield* Result.await(writeLocalResolutionPinBoundary(() => mkdir(prismaDir, { recursive: true }), "create-directory", signal));
		const pinPath = path.join(cwd, LOCAL_RESOLUTION_PIN_RELATIVE_PATH);
		const tmpPath = path.join(prismaDir, `local.${process.pid}.${Date.now()}.tmp`);
		const serialized = yield* serializeLocalResolutionPin(pin);
		yield* Result.await(writeLocalResolutionPinBoundary(() => writeFile(tmpPath, serialized, {
			encoding: "utf8",
			signal
		}), "write-temp-file", signal));
		yield* ensureLocalResolutionPinWriteNotAborted(signal);
		yield* Result.await(writeLocalResolutionPinBoundary(() => rename(tmpPath, pinPath), "rename-temp-file", signal));
		return Result.ok(void 0);
	});
}
async function ensureLocalResolutionPinGitignore(cwd, signal) {
	const gitignorePath = path.join(cwd, ".gitignore");
	let existing = null;
	const notAborted = ensureLocalResolutionPinGitignoreUpdateNotAborted(signal);
	if (notAborted.isErr()) return Result.err(notAborted.error);
	const existingResult = await Result.tryPromise({
		try: () => readFile(gitignorePath, {
			encoding: "utf8",
			signal
		}),
		catch: (cause) => signal?.aborted ? new LocalResolutionPinGitignoreUpdateAbortedError(cause) : new LocalResolutionPinGitignoreUpdateFailedError("read", cause)
	});
	if (existingResult.isErr()) if (existingResult.error instanceof LocalResolutionPinGitignoreUpdateFailedError && existingResult.error.cause.code === "ENOENT") existing = null;
	else return Result.err(existingResult.error);
	else existing = existingResult.value;
	if (existing === null) return writeLocalResolutionPinGitignore(gitignorePath, ".prisma/\n", signal);
	if (existing.split(/\r?\n/).map((line) => line.trim()).some((line) => line === ".prisma/" || line === ".prisma/local.json")) return Result.ok(void 0);
	return writeLocalResolutionPinGitignore(gitignorePath, existing.endsWith("\n") ? `${existing}.prisma/\n` : `${existing}\n.prisma/\n`, signal);
}
function ensureLocalResolutionPinWriteNotAborted(signal) {
	return Result.try({
		try: () => signal?.throwIfAborted(),
		catch: (cause) => new LocalResolutionPinWriteAbortedError(cause)
	});
}
function serializeLocalResolutionPin(pin) {
	return Result.try({
		try: () => `${JSON.stringify(pin, null, 2)}\n`,
		catch: (cause) => new LocalResolutionPinSerializationError(cause)
	});
}
function writeLocalResolutionPinBoundary(run, operation, signal) {
	return Result.tryPromise({
		try: async () => {
			await run();
		},
		catch: (cause) => signal?.aborted ? new LocalResolutionPinWriteAbortedError(cause) : new LocalResolutionPinWriteFailedError(operation, cause)
	});
}
function ensureLocalResolutionPinGitignoreUpdateNotAborted(signal) {
	return Result.try({
		try: () => signal?.throwIfAborted(),
		catch: (cause) => new LocalResolutionPinGitignoreUpdateAbortedError(cause)
	});
}
function writeLocalResolutionPinGitignore(gitignorePath, contents, signal) {
	return Result.tryPromise({
		try: () => writeFile(gitignorePath, contents, {
			encoding: "utf8",
			signal
		}),
		catch: (cause) => signal?.aborted ? new LocalResolutionPinGitignoreUpdateAbortedError(cause) : new LocalResolutionPinGitignoreUpdateFailedError("write", cause)
	});
}
function isLocalResolutionPin(value) {
	if (!value || typeof value !== "object") return false;
	const keys = Object.keys(value);
	if (keys.length !== 2 || !keys.includes("workspaceId") || !keys.includes("projectId")) return false;
	const candidate = value;
	return typeof candidate.workspaceId === "string" && candidate.workspaceId.trim().length > 0 && typeof candidate.projectId === "string" && candidate.projectId.trim().length > 0;
}
//#endregion
//#region ../cli/src/lib/project/provider.ts
function createManagementProjectProvider(client) {
	return {
		async renameProject(options) {
			const result = await client.PATCH("/v1/projects/{id}", {
				params: { path: { id: options.projectId } },
				body: { name: options.name },
				signal: options.signal
			});
			const status = result.response?.status ?? 0;
			if (status === 400 || status === 422) throw projectRenameFailedError(options.name, result.error);
			if (result.error || !result.data) throw projectApiError("Failed to rename project", result.response, result.error);
			const project = result.data.data;
			return {
				id: project.id,
				name: project.name,
				...project.url ? { url: project.url } : {}
			};
		},
		async removeProject(options) {
			const result = await client.DELETE("/v1/projects/{id}", {
				params: { path: { id: options.projectId } },
				signal: options.signal
			});
			if (result.response?.status === 400) throw projectDeleteBlockedError(options.projectId, result.error);
			if (result.error) throw projectApiError("Failed to delete project", result.response, result.error);
		},
		async transferProject(options) {
			const result = await client.POST("/v1/projects/{id}/transfer", {
				params: { path: { id: options.projectId } },
				body: { recipientAccessToken: options.recipientAccessToken },
				signal: options.signal
			});
			if (result.response?.status === 400) throw projectTransferRejectedError(options.projectId, result.error);
			if (result.error) throw projectApiError("Failed to transfer project", result.response, result.error);
		}
	};
}
function userChoice$6(label) {
	return {
		kind: "user-choice",
		label
	};
}
function projectRenameFailedError(name, error) {
	return new CliStructuredError("PROJECT.RENAME_FAILED", "Project rename failed", {
		why: error?.error?.message ?? `The platform rejected the name "${name}".`,
		nextActions: [userChoice$6(error?.error?.hint ?? "Pass a different project name and retry the rename.")]
	});
}
function projectDeleteBlockedError(projectId, error) {
	const deleteServicesCommand = formatPrismaCliCommand([
		"service",
		"delete",
		"--service",
		"<name>"
	]);
	return new CliStructuredError("PROJECT.DELETE_BLOCKED", "Project cannot be deleted yet", {
		why: error?.error?.message ?? `Project "${projectId}" still has active deployments.`,
		nextActions: [userChoice$6("Delete the project's services first, then retry the deletion."), {
			kind: "run-command",
			label: deleteServicesCommand,
			command: deleteServicesCommand
		}]
	});
}
function projectTransferRejectedError(projectId, error) {
	return new CliStructuredError("PROJECT.TRANSFER_REJECTED", "Project transfer was rejected", {
		why: error?.error?.message ?? `The platform rejected the transfer of project "${projectId}", for example because the recipient token is invalid or expired.`,
		nextActions: [userChoice$6("Check the recipient workspace session or token and retry the transfer.")]
	});
}
function projectApiError(summary, response, error) {
	const status = response?.status ?? 0;
	const apiCode = error?.error?.code;
	return new CliStructuredError("PROJECT.API_ERROR", summary, {
		why: error?.error?.message ?? `The Management API returned status ${status || "unknown"}.`,
		...apiCode !== void 0 || status ? { meta: {
			...status ? { status } : {},
			...apiCode !== void 0 ? { apiCode } : {}
		} } : {},
		nextActions: [userChoice$6(error?.error?.hint ?? "Re-run with --log-level verbose for the underlying API response details.")]
	});
}
//#endregion
//#region ../cli/src/command-arguments.ts
function formatCommandArgument(value) {
	return /^[A-Za-z0-9._/-]+$/.test(value) && !value.startsWith("-") ? value : `'${value.replace(/'/g, "'\\''")}'`;
}
//#endregion
//#region ../cli/src/lib/project/resolution.ts
var ProjectNotFoundError = class extends TaggedError("ProjectNotFoundError")() {
	constructor(projectRef, workspace) {
		super({
			message: `Project "${projectRef}" was not found in workspace "${workspace.name}".`,
			projectRef,
			workspace
		});
	}
};
var ProjectAmbiguousError = class extends TaggedError("ProjectAmbiguousError")() {
	constructor(projectRef, matches) {
		super({
			message: projectRef ? `Multiple projects matched "${projectRef}".` : "Multiple projects matched the current directory context.",
			projectRef,
			matches
		});
	}
};
var LocalStateStaleError = class extends TaggedError("LocalStateStaleError")() {
	constructor() {
		super({
			message: `The target recorded in ${LOCAL_RESOLUTION_PIN_RELATIVE_PATH} is no longer available in the selected workspace.`,
			pinPath: LOCAL_RESOLUTION_PIN_RELATIVE_PATH
		});
	}
};
var LocalProjectWorkspaceMismatchError = class extends TaggedError("LocalProjectWorkspaceMismatchError")() {
	constructor(options) {
		super({
			message: `${LOCAL_RESOLUTION_PIN_RELATIVE_PATH} links this directory to project ${options.pinnedProjectId} in workspace ${options.pinnedWorkspaceId}, but the active workspace is "${options.activeWorkspace.name}" (${options.activeWorkspace.id}).`,
			pinnedWorkspaceId: options.pinnedWorkspaceId,
			pinnedProjectId: options.pinnedProjectId,
			activeWorkspace: options.activeWorkspace
		});
	}
};
var ProjectSetupRequiredError = class extends TaggedError("ProjectSetupRequiredError")() {
	constructor(options) {
		const commandLabel = options.commandName ? `prisma ${options.commandName}` : "this command";
		super({
			message: `This directory is not linked to a Prisma Project, and ${commandLabel} will not choose one from package or directory names.`,
			commandName: options.commandName,
			suggestion: options.suggestion
		});
	}
};
async function resolveProjectTarget(options) {
	return Result.gen(async function* () {
		const localPin = yield* Result.await(readImplicitLocalPin(options));
		const projects = await options.listProjects();
		const target = yield* Result.await(resolveBoundProjectTarget(options, projects, { localPin }));
		if (target) return Result.ok(target);
		return Result.err(await projectSetupRequiredError({
			cwd: options.context.runtime.cwd,
			projects,
			commandName: options.commandName,
			signal: options.context.runtime.signal
		}));
	});
}
async function inspectProjectBinding(options) {
	return Result.gen(async function* () {
		const localPin = yield* Result.await(readImplicitLocalPin(options));
		const projects = await options.listProjects();
		const target = yield* Result.await(resolveBoundProjectTarget(options, projects, { localPin }));
		if (target) return Result.ok(target);
		return Result.ok({
			workspace: options.workspace,
			project: null,
			localBinding: { status: "not-linked" },
			resolution: { projectSource: "unbound" },
			...await buildProjectSetupSuggestion({
				cwd: options.context.runtime.cwd,
				projects,
				commandName: options.commandName ?? "project show",
				signal: options.context.runtime.signal
			})
		});
	});
}
function runCommand$5(command, reason) {
	return {
		kind: "run-command",
		label: command,
		command,
		...reason === void 0 ? {} : { reason }
	};
}
function userChoice$5(label) {
	return {
		kind: "user-choice",
		label
	};
}
function projectNotFoundError$1(projectRef, workspace) {
	return projectResolutionErrorToStructured(new ProjectNotFoundError(projectRef, workspace));
}
function projectNotFoundStructuredError(projectRef, workspace) {
	return new CliStructuredError("PROJECT.NOT_FOUND", "Project not found", {
		why: `The project "${projectRef}" does not exist in workspace "${workspace.name}" or is not accessible.`,
		nextActions: [userChoice$5("Pass a project id or name from prisma project list."), runCommand$5("prisma project list")]
	});
}
function projectAmbiguousError(projectRef, matches) {
	return projectResolutionErrorToStructured(new ProjectAmbiguousError(projectRef, matches));
}
function projectAmbiguousStructuredError(projectRef, matches) {
	const firstMatch = matches[0];
	const nextActions = [userChoice$5("Pass --project <id-or-name> to choose the project explicitly."), runCommand$5("prisma project list")];
	if (firstMatch) nextActions.push(runCommand$5(`prisma project link ${firstMatch.id}`));
	return new CliStructuredError("PROJECT.AMBIGUOUS", "Project resolution is ambiguous", {
		why: projectRef ? `Multiple projects matched "${projectRef}".` : "Multiple projects matched the current directory context.",
		meta: { matches: matches.map((project) => ({
			id: project.id,
			name: project.name
		})) },
		nextActions
	});
}
function localStateStaleStructuredError() {
	return new CliStructuredError("PROJECT.LOCAL_STATE_STALE", "Local project binding is stale", {
		why: `The target recorded in ${LOCAL_RESOLUTION_PIN_RELATIVE_PATH} is no longer available in the selected workspace.`,
		meta: { pinPath: LOCAL_RESOLUTION_PIN_RELATIVE_PATH },
		nextActions: [
			userChoice$5(`Delete ${LOCAL_RESOLUTION_PIN_RELATIVE_PATH}, then choose a Project explicitly.`),
			runCommand$5("prisma project list"),
			runCommand$5("prisma project link <id-or-name>")
		]
	});
}
function localProjectWorkspaceMismatchStructuredError(options) {
	return new CliStructuredError("PROJECT.LOCAL_WORKSPACE_MISMATCH", "Project link uses another workspace", {
		why: `${LOCAL_RESOLUTION_PIN_RELATIVE_PATH} links this directory to project ${options.pinnedProjectId} in workspace ${options.pinnedWorkspaceId}, but your current CLI session is workspace "${options.activeWorkspace.name}" (${options.activeWorkspace.id}).`,
		meta: {
			pinPath: LOCAL_RESOLUTION_PIN_RELATIVE_PATH,
			pinnedWorkspaceId: options.pinnedWorkspaceId,
			pinnedProjectId: options.pinnedProjectId,
			activeWorkspaceId: options.activeWorkspace.id,
			activeWorkspaceName: options.activeWorkspace.name
		},
		nextActions: [
			userChoice$5("Switch to the linked workspace, or relink this directory to a project in the current workspace."),
			runCommand$5(`prisma auth workspace use ${options.pinnedWorkspaceId}`),
			runCommand$5("prisma project list"),
			runCommand$5("prisma project link <id-or-name>")
		]
	});
}
/**
* Converts expected project-resolution variants to the structured errors
* a command boundary raises — the codes here are the registered PROJECT.*
* codes, assigned at origin.
* `LocalResolutionPinReadAbortedError` and `UnhandledException` intentionally
* propagate as exceptions; callers such as `resolveProjectShowInRealMode`
* throw this helper's result, so passthrough variants should keep bubbling.
*/
function projectResolutionErrorToStructured(error) {
	return matchError(error, {
		ProjectNotFoundError: (error) => projectNotFoundStructuredError(error.projectRef, error.workspace),
		ProjectAmbiguousError: (error) => projectAmbiguousStructuredError(error.projectRef, error.matches),
		ProjectSetupRequiredError: (error) => projectSetupRequiredStructuredError(error),
		LocalStateStaleError: () => localStateStaleStructuredError(),
		LocalProjectWorkspaceMismatchError: (error) => localProjectWorkspaceMismatchStructuredError({
			pinnedWorkspaceId: error.pinnedWorkspaceId,
			pinnedProjectId: error.pinnedProjectId,
			activeWorkspace: error.activeWorkspace
		}),
		LocalResolutionPinReadAbortedError: (error) => {
			throw error;
		},
		UnhandledException: (error) => {
			throw error;
		}
	});
}
async function buildProjectSetupSuggestion(options) {
	const suggestedName = await inferTargetName(options.cwd, options.signal);
	const candidates = sortProjects(options.projects.filter((project) => projectMatchesSuggestedName(project, suggestedName.name))).map(toProjectSummary$1);
	return {
		suggestedProjectName: suggestedName.name,
		suggestedProjectNameSource: suggestedName.source,
		candidates,
		recoveryCommands: buildProjectRecoveryCommands(options.commandName)
	};
}
async function projectSetupRequiredError(options) {
	const suggestion = await buildProjectSetupSuggestion(options);
	return new ProjectSetupRequiredError({
		commandName: options.commandName,
		suggestion
	});
}
function projectSetupRequiredStructuredError(error) {
	const suggestion = error.suggestion;
	return new CliStructuredError("PROJECT.SETUP_REQUIRED", "Choose a Project before running this command", {
		why: error.message,
		meta: { ...suggestion },
		nextActions: buildProjectSetupNextActions({
			commandName: error.commandName,
			suggestedProjectName: suggestion.suggestedProjectName
		})
	});
}
function buildProjectSetupNextActions(options = {}) {
	const recoveryCommands = buildProjectRecoveryCommands(options.commandName);
	const linkCommand = recoveryCommands[0] ?? "prisma project link <id-or-name>";
	const retryCommand = options.retryCommand ?? recoveryCommands[1];
	const actions = [{
		kind: "user-choice",
		label: "Ask the user whether to link an existing Project or create a new one",
		commands: [
			"prisma project list",
			linkCommand,
			...retryCommand ? [retryCommand] : []
		],
		reason: options.reason ?? "This directory is not linked to a Prisma Project. Package and directory names are suggestions only, not a safe Project selection."
	}, {
		kind: "run-command",
		label: "Link the chosen Project",
		command: linkCommand,
		reason: "Linking writes the durable local Project binding for this directory."
	}];
	const createCommand = options.createCommand ?? (options.suggestedProjectName ? `prisma project create ${formatCommandArgument(options.suggestedProjectName)}` : void 0);
	if (createCommand) actions.push({
		kind: "run-command",
		label: "Create and link a new Project",
		command: createCommand,
		reason: "Use this when the user wants a new Prisma Project instead of an existing one."
	});
	if (options.commandName) actions.push({
		kind: "run-command",
		label: "Retry with an explicit Project",
		command: retryCommand ?? `prisma ${options.commandName} --project <id-or-name>`
	});
	return actions;
}
async function readPackageName(cwd, signal) {
	signal?.throwIfAborted();
	try {
		const raw = await readFile(path.join(cwd, "package.json"), {
			encoding: "utf8",
			signal
		});
		const parsed = JSON.parse(raw);
		if (!parsed || typeof parsed !== "object") return null;
		const packageName = "name" in parsed ? parsed.name : null;
		return typeof packageName === "string" && packageName.trim().length > 0 ? packageName.trim() : null;
	} catch (error) {
		if (error.code === "ENOENT") return null;
		if (error instanceof SyntaxError) return null;
		throw error;
	}
}
async function inferTargetName(cwd, signal) {
	const packageName = await readPackageName(cwd, signal);
	if (packageName && isValidInferredTargetName(packageName)) return {
		name: packageName,
		source: "package-name"
	};
	return {
		name: path.basename(cwd),
		source: "directory-name"
	};
}
const INFERRED_TARGET_NAME = /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/;
function isValidInferredTargetName(value) {
	return INFERRED_TARGET_NAME.test(value);
}
function sortProjects(projects) {
	return projects.slice().sort((left, right) => left.name.localeCompare(right.name) || left.id.localeCompare(right.id));
}
function resolveExplicitProject(projectRef, projects, workspace) {
	const byId = projects.filter((project) => project.id === projectRef);
	const matches = byId.length > 0 ? byId : projects.filter((project) => project.name === projectRef);
	if (matches.length === 1) return Result.ok(matches[0]);
	if (matches.length > 1) return Result.err(new ProjectAmbiguousError(projectRef, matches));
	return Result.err(new ProjectNotFoundError(projectRef, workspace));
}
function projectMatchesSuggestedName(project, suggestedName) {
	return project.id === suggestedName || project.name === suggestedName || project.slug === suggestedName;
}
async function resolveDurablePlatformMapping() {
	return null;
}
async function resolveBoundProjectTarget(options, projects, settings) {
	if (options.explicitProject) {
		const projectResult = resolveExplicitProject(options.explicitProject, projects, options.workspace);
		if (projectResult.isErr()) return Result.err(projectResult.error);
		return Result.ok(resolvedTarget(options.workspace, projectResult.value, "explicit", {
			targetName: options.explicitProject,
			targetNameSource: "explicit"
		}));
	}
	const localPin = settings.localPin;
	if (!localPin) return Result.ok(null);
	if (localPin.kind === "present") {
		if (!sameWorkspaceId(localPin.pin.workspaceId, options.workspace.id)) return Result.err(new LocalProjectWorkspaceMismatchError({
			pinnedWorkspaceId: localPin.pin.workspaceId,
			pinnedProjectId: localPin.pin.projectId,
			activeWorkspace: options.workspace
		}));
		const project = projects.find((candidate) => candidate.id === localPin.pin.projectId);
		if (!project) return Result.err(new LocalStateStaleError());
		return Result.ok(resolvedTarget(options.workspace, project, "local-pin", {
			targetName: project.name,
			targetNameSource: "local-pin"
		}));
	}
	const platformMapping = await resolveDurablePlatformMapping();
	if (platformMapping && sameWorkspaceId(platformMapping.workspace.id, options.workspace.id)) return Result.ok(resolvedTarget(options.workspace, platformMapping, "platform-mapping", {
		targetName: platformMapping.name,
		targetNameSource: "platform-mapping"
	}));
	return Result.ok(null);
}
async function readImplicitLocalPin(options) {
	if (options.explicitProject) return Result.ok(null);
	const localPinResult = await readLocalResolutionPin(options.context.runtime.cwd, options.context.runtime.signal);
	if (localPinResult.isErr()) return Result.err(localPinReadErrorToProjectError(localPinResult.error));
	const localPin = localPinResult.value;
	if (localPin.kind === "present" && !sameWorkspaceId(localPin.pin.workspaceId, options.workspace.id)) return Result.err(new LocalProjectWorkspaceMismatchError({
		pinnedWorkspaceId: localPin.pin.workspaceId,
		pinnedProjectId: localPin.pin.projectId,
		activeWorkspace: options.workspace
	}));
	return Result.ok(localPin);
}
function localPinReadErrorToProjectError(error) {
	return matchError(error, {
		LocalResolutionPinInvalidJsonError: () => new LocalStateStaleError(),
		LocalResolutionPinInvalidShapeError: () => new LocalStateStaleError(),
		LocalResolutionPinReadAbortedError: (error) => error,
		UnhandledException: (error) => error
	});
}
function resolvedTarget(workspace, project, projectSource, resolutionDetails) {
	return {
		workspace,
		project: toProjectSummary$1(project),
		resolution: {
			projectSource,
			...resolutionDetails
		}
	};
}
function buildProjectRecoveryCommands(commandName) {
	const commands = ["prisma project link <id-or-name>"];
	if (commandName) commands.push(`prisma ${commandName} --project <id-or-name>`);
	return commands;
}
function toProjectSummary$1(project) {
	return {
		id: project.id,
		name: project.name,
		...project.url ? { url: project.url } : {},
		...project.defaultRegion != null ? { defaultRegion: project.defaultRegion } : {}
	};
}
//#endregion
//#region ../cli/src/controllers/project.ts
const GITHUB_INSTALL_POLL_INTERVAL_MS = 2e3;
const GITHUB_INSTALL_POLL_TIMEOUT_MS = 12e4;
function runCommand$4(command) {
	return {
		kind: "run-command",
		label: command,
		command
	};
}
function userChoice$4(label) {
	return {
		kind: "user-choice",
		label
	};
}
/** A URL is not a command: putting one in `command` tells a consumer to
*  execute it. */
function openUrl(url) {
	return {
		kind: "open-url",
		label: url,
		url
	};
}
async function readProjectListLocalBinding(cwd, projects, signal) {
	const pinResult = await readLocalResolutionPin(cwd, signal);
	if (pinResult.isErr()) return localPinReadErrorToInvalidLocalBinding(pinResult.error);
	const pin = pinResult.value;
	if (pin.kind === "present") return projects.some((project) => project.id === pin.pin.projectId) ? { status: "linked" } : { status: "invalid" };
	return { status: "not-linked" };
}
function localPinReadErrorToInvalidLocalBinding(error) {
	return matchError(error, {
		LocalResolutionPinInvalidJsonError: () => ({ status: "invalid" }),
		LocalResolutionPinInvalidShapeError: () => ({ status: "invalid" }),
		LocalResolutionPinReadAbortedError: (error) => {
			throw error;
		},
		UnhandledException: (error) => {
			throw error;
		}
	});
}
function transferRecipientRequiredError(formatCommand) {
	return new CliStructuredError("PROJECT.TRANSFER_RECIPIENT_REQUIRED", "Transfer recipient required", {
		why: "Project transfer needs the receiving workspace.",
		nextActions: [
			userChoice$4("Pass --to-workspace <id-or-name> for a locally authenticated workspace, or --recipient-token <token> for a cross-account transfer."),
			runCommand$4(formatCommand([
				"auth",
				"workspace",
				"list"
			])),
			runCommand$4(formatCommand([
				"project",
				"transfer",
				"<project>",
				"--to-workspace",
				"<id-or-name>",
				"--confirm",
				"<project-id>"
			]))
		]
	});
}
function transferRecipientUnavailableError(formatCommand) {
	return new CliStructuredError("PROJECT.TRANSFER_RECIPIENT_UNAVAILABLE", "Local workspace sessions are unavailable", {
		why: `--to-workspace resolves locally stored OAuth sessions, but ${SERVICE_TOKEN_ENV_VAR} is set and service-token mode does not read them.`,
		nextActions: [userChoice$4("Pass --recipient-token <token> with an access token for the receiving workspace, or unset the service token."), runCommand$4(formatCommand([
			"project",
			"transfer",
			"<project>",
			"--recipient-token",
			"<token>",
			"--confirm",
			"<project-id>"
		]))]
	});
}
async function cleanupLocalPinForProject(context, projectId, hooks) {
	const pinResult = await readLocalResolutionPin(context.runtime.cwd, context.runtime.signal);
	if (pinResult.isErr()) return false;
	const pin = pinResult.value;
	if (pin.kind !== "present" || pin.pin.projectId !== projectId) return false;
	try {
		await unlink(path.join(pin.directory, LOCAL_RESOLUTION_PIN_RELATIVE_PATH));
		return true;
	} catch {
		hooks.onError(`The local pin ${LOCAL_RESOLUTION_PIN_RELATIVE_PATH} points at the deleted project but could not be deleted.`);
		return false;
	}
}
async function rewriteOrClearLocalPinForProject(context, projectId, recipientWorkspaceId, hooks) {
	const pinResult = await readLocalResolutionPin(context.runtime.cwd, context.runtime.signal);
	if (pinResult.isErr()) return "none";
	const pin = pinResult.value;
	if (pin.kind !== "present" || pin.pin.projectId !== projectId) return "none";
	if (recipientWorkspaceId) {
		if ((await writeLocalResolutionPin(pin.directory, {
			workspaceId: recipientWorkspaceId,
			projectId
		}, context.runtime.signal)).isOk()) return "rewritten";
		hooks.onError(`The local pin ${LOCAL_RESOLUTION_PIN_RELATIVE_PATH} points at the transferred project but could not be rewritten.`);
		return "none";
	}
	try {
		await unlink(path.join(pin.directory, LOCAL_RESOLUTION_PIN_RELATIVE_PATH));
		return "cleared";
	} catch {
		hooks.onError(`The local pin ${LOCAL_RESOLUTION_PIN_RELATIVE_PATH} points at the transferred project but could not be cleared.`);
		return "none";
	}
}
/** The projects the API returns for the active credential, sorted.
*  Takes no workspace: the credential names one, and the API answers
*  within it. */
async function listRealWorkspaceProjects(client, signal) {
	const { data, error, response } = await client.GET("/v1/projects", { signal });
	if (error || !data) throw projectApiError("Failed to list projects", response, error);
	return sortProjects((data.data ?? []).map((project) => ({
		id: project.id,
		name: project.name,
		..."url" in project && typeof project.url === "string" ? { url: project.url } : {},
		..."defaultRegion" in project ? { defaultRegion: project.defaultRegion } : {},
		slug: "slug" in project && typeof project.slug === "string" ? project.slug : null,
		workspace: {
			id: project.workspace.id,
			name: project.workspace.name
		}
	})));
}
async function findRepositoryInInstallations(api, installations, repository, signal) {
	let inspectableInstallationCount = 0;
	for (const installation of installations) {
		if (installation.provider !== "github" || installation.suspended) continue;
		const matchedRepository = await findRepositoryInInstallationIfAvailable(api, installation.id, repository, signal);
		if (matchedRepository === "unavailable") continue;
		inspectableInstallationCount += 1;
		if (matchedRepository) return {
			match: {
				installation,
				repository: matchedRepository
			},
			inspectableInstallationCount
		};
	}
	return {
		match: null,
		inspectableInstallationCount
	};
}
function readPositiveIntegerEnv(value, fallback) {
	if (value === void 0) return fallback;
	const parsed = Number(value);
	return Number.isInteger(parsed) && parsed > 0 ? parsed : fallback;
}
async function listScmInstallations(api, workspaceId, signal) {
	const installations = [];
	let cursor;
	const seenCursors = /* @__PURE__ */ new Set();
	do {
		const { data, error, response } = await api.GET("/v1/scm-installations", {
			params: { query: {
				workspaceId,
				limit: 100,
				...cursor ? { cursor } : {}
			} },
			signal
		});
		if (error || !data) throw repoConnectionApiError("Failed to inspect GitHub App installations", response, error);
		installations.push(...data.data);
		cursor = readNextPaginationCursor(data.pagination, seenCursors, "Failed to inspect GitHub App installations", response);
	} while (cursor);
	return installations;
}
async function findRepositoryInInstallation(api, installationId, repository, signal) {
	const expectedFullName = repository.fullName.toLowerCase();
	let cursor;
	const seenCursors = /* @__PURE__ */ new Set();
	do {
		const { data, error, response } = await api.GET("/v1/scm-installations/{installationId}/repositories", {
			params: {
				path: { installationId },
				query: {
					limit: 100,
					...cursor ? { cursor } : {}
				}
			},
			signal
		});
		if (error || !data) throw repoConnectionApiError("Failed to inspect GitHub repositories", response, error);
		const matchedRepository = data.data.find((candidate) => candidate.fullName.toLowerCase() === expectedFullName);
		if (matchedRepository) return matchedRepository;
		cursor = readNextPaginationCursor(data.pagination, seenCursors, "Failed to inspect GitHub repositories", response);
	} while (cursor);
	return null;
}
function readNextPaginationCursor(pagination, seenCursors, summary, response) {
	const nextCursor = pagination.hasMore && pagination.nextCursor ? pagination.nextCursor : void 0;
	if (!nextCursor) return;
	if (seenCursors.has(nextCursor)) throw repoConnectionApiError(summary, response, { error: { message: "Pagination cursor did not advance." } });
	seenCursors.add(nextCursor);
	return nextCursor;
}
async function findRepositoryInInstallationIfAvailable(api, installationId, repository, signal) {
	try {
		return await findRepositoryInInstallation(api, installationId, repository, signal);
	} catch (error) {
		if (signal.aborted) throw error;
		if (isUnavailableScmInstallationError(error)) return "unavailable";
		throw error;
	}
}
function isUnavailableScmInstallationError(error) {
	if (!CliStructuredError.is(error) || error.code !== "GIT.REPO_CONNECTION_FAILED") return false;
	return error.meta?.status === 404 || error.meta?.status === 422;
}
async function createGitHubInstallIntent(api, workspaceId, signal) {
	const { data, error, response } = await api.POST("/v1/scm-installations/install-intents", {
		body: {
			provider: "github",
			workspaceId
		},
		signal
	});
	if (error || !data) throw repoConnectionApiError("Failed to create GitHub App installation link", response, error);
	return data.data.installUrl;
}
async function readFirstSourceRepository(api, projectId, signal) {
	const { data, error, response } = await api.GET("/v1/source-repositories", {
		params: { query: {
			projectId,
			limit: 1
		} },
		signal
	});
	if (error || !data) throw repoConnectionApiError("Failed to inspect GitHub repository connection", response, error);
	return data.data[0] ?? null;
}
function toRepositoryConnection(record) {
	const [owner = "", name = ""] = record.repoFullName.split("/");
	return {
		id: record.id,
		provider: "github",
		repoId: record.repoId,
		repository: {
			owner,
			name,
			fullName: record.repoFullName,
			url: `https://github.com/${record.repoFullName}`
		},
		defaultBranch: record.defaultBranch,
		isPrivate: record.isPrivate,
		status: record.status,
		installation: {
			id: record.installationId,
			status: "connected"
		},
		automation: {
			branches: record.status === "active",
			pullRequests: false,
			comments: false
		},
		connectedAt: record.createdAt,
		updatedAt: record.updatedAt
	};
}
function unsupportedRepositoryProviderError() {
	return new CliStructuredError("GIT.REPO_PROVIDER_UNSUPPORTED", "Repository provider is not supported", {
		why: "Repository connection supports GitHub repository URLs only.",
		nextActions: [userChoice$4("Pass a GitHub repository URL such as git@github.com:prisma/prisma-cli.git."), runCommand$4("prisma git connect git@github.com:owner/repo.git")]
	});
}
function repoNotConnectedError() {
	return new CliStructuredError("GIT.REPO_NOT_CONNECTED", "No GitHub repository connected", {
		why: "The resolved project does not have an active GitHub repository connection.",
		nextActions: [userChoice$4("Run prisma git connect before disconnecting."), runCommand$4("prisma git connect")]
	});
}
function repoInstallationRequiredError(repository, installUrl) {
	return new CliStructuredError("GIT.REPO_INSTALLATION_REQUIRED", "GitHub App installation required", {
		why: `The selected workspace does not have a GitHub App installation that can be used to link ${repository.fullName}.`,
		meta: {
			repository: repository.fullName,
			installUrl
		},
		nextActions: [
			userChoice$4("Finish installing the GitHub App in the browser, then rerun prisma git connect."),
			openUrl(installUrl),
			runCommand$4(`prisma git connect ${repository.url}`)
		]
	});
}
function repoNotAccessibleError(repository, installUrl) {
	return new CliStructuredError("GIT.REPO_NOT_ACCESSIBLE", "GitHub repository is not accessible", {
		why: `The GitHub App installations connected to this workspace do not expose ${repository.fullName}.`,
		meta: {
			repository: repository.fullName,
			installUrl
		},
		nextActions: [
			userChoice$4("Open the GitHub App installation URL, grant access to this repository, then rerun prisma git connect."),
			openUrl(installUrl),
			runCommand$4(`prisma git connect ${repository.url}`)
		]
	});
}
function repoAlreadyConnectedError(repositoryFullName) {
	return new CliStructuredError("GIT.REPO_ALREADY_CONNECTED", "Project already has a GitHub repository connected", {
		why: `The resolved project is already connected to ${repositoryFullName}.`,
		meta: { repository: repositoryFullName },
		nextActions: [userChoice$4("Disconnect the existing repository before connecting a different one."), runCommand$4("prisma git disconnect")]
	});
}
function repositoryFullNamesMatch(left, right) {
	return left.toLowerCase() === right.toLowerCase();
}
function repoConnectionApiError(summary, response, error) {
	const status = response?.status ?? 0;
	const apiCode = error?.error?.code;
	const apiMessage = error?.error?.message;
	const apiHint = error?.error?.hint;
	const unauthorized = status === 401 || status === 403;
	return new CliStructuredError("GIT.REPO_CONNECTION_FAILED", summary, {
		why: apiMessage ?? (unauthorized ? `The Management API rejected the request as unauthorized (HTTP ${status}).` : `The Management API returned status ${status || "unknown"}.`),
		meta: {
			status,
			...apiCode ? { apiCode } : {}
		},
		nextActions: [userChoice$4(apiHint ?? repoConnectionFixForStatus(status)), runCommand$4(unauthorized ? "prisma auth login" : "prisma project show")]
	});
}
function repoConnectionFixForStatus(status) {
	if (status === 401 || status === 403) return "Sign in again with prisma auth login, then rerun the command.";
	if (status === 404) return "Install the GitHub App for this workspace, then rerun prisma git connect.";
	if (status === 409) return "This project or repository is already linked. Disconnect the old link first, then try again.";
	if (status === 422) return "Make sure the GitHub App installation has access to this repository.";
	return "Re-run with --log-level verbose for the underlying API response details.";
}
//#endregion
//#region ../cli/src/lib/project/setup.ts
function isValidProjectSetupName(projectName) {
	return projectName.trim().length > 0;
}
function resolveProjectForSetup(projectRef, projects, workspace) {
	const matches = projects.filter((project) => project.id === projectRef || project.name === projectRef);
	if (matches.length > 1) throw projectAmbiguousError(projectRef, matches);
	const match = matches[0];
	if (match !== void 0) return match;
	throw projectNotFoundError$1(projectRef, workspace);
}
function projectDirectoryBindingErrorToStructured(error) {
	return matchError(error, {
		LocalResolutionPinSerializationError: (error) => {
			throw error;
		},
		LocalResolutionPinWriteAbortedError: (error) => {
			throw error;
		},
		LocalResolutionPinWriteFailedError: (error) => localStateWriteFailedError(error, {
			why: `The CLI could not write ${LOCAL_RESOLUTION_PIN_RELATIVE_PATH}.`,
			meta: {
				pinPath: error.pinPath,
				operation: error.operation
			}
		}),
		LocalResolutionPinGitignoreUpdateAbortedError: (error) => {
			throw error;
		},
		LocalResolutionPinGitignoreUpdateFailedError: (error) => localStateWriteFailedError(error, {
			why: "The CLI could not update .gitignore to keep local Project binding state out of git.",
			meta: {
				gitignorePath: error.gitignorePath,
				operation: error.operation
			}
		})
	});
}
function localStateWriteFailedError(error, options) {
	return new CliStructuredError("PROJECT.LOCAL_STATE_WRITE_FAILED", "Could not save local Project binding", {
		why: options.why,
		meta: options.meta,
		cause: error.cause,
		nextActions: [{
			kind: "user-choice",
			label: "Check that this directory is writable and that .prisma/local.json and .gitignore are not blocked by directories or permissions, then retry."
		}, {
			kind: "run-command",
			label: "prisma project link <id-or-name>",
			command: "prisma project link <id-or-name>"
		}]
	});
}
function toProjectSummary(project) {
	return {
		id: project.id,
		name: project.name,
		...project.url ? { url: project.url } : {},
		...project.defaultRegion != null ? { defaultRegion: project.defaultRegion } : {}
	};
}
function projectSetupNameRequiredError(command) {
	const example = `prisma ${command} my-app`;
	return new CliStructuredError("PROJECT.USAGE_ERROR", "Project create requires a name", {
		why: "The project name must be a non-empty value.",
		nextActions: [{
			kind: "user-choice",
			label: "Pass a Project name explicitly."
		}, {
			kind: "run-command",
			label: example,
			command: example
		}]
	});
}
function projectCreateFailedError(error, projectName, workspace, options) {
	const status = extractHttpStatus(error);
	const permissionRejection = status === 401 || status === 403;
	const message = error instanceof Error ? error.message : String(error);
	const nextActions = [{
		kind: "user-choice",
		label: permissionRejection ? options.permissionFix : options.fallbackFix
	}, ...options.nextSteps.map((step) => ({
		kind: "run-command",
		label: step,
		command: step
	}))];
	return new CliStructuredError("PROJECT.CREATE_FAILED", `Could not create Project "${projectName}"`, {
		why: permissionRejection ? `The platform rejected the Project create in workspace "${workspace.name}" (HTTP ${status}).` : message,
		cause: error,
		nextActions
	});
}
const HTTP_STATUS_IN_MESSAGE = /\(HTTP (\d{3})\)/;
function extractHttpStatus(error) {
	if (!error || typeof error !== "object") return null;
	const candidate = error;
	if (typeof candidate.statusCode === "number") return candidate.statusCode;
	if (typeof candidate.status === "number") return candidate.status;
	if (typeof candidate.message === "string") {
		const match = HTTP_STATUS_IN_MESSAGE.exec(candidate.message);
		if (match) return Number.parseInt(match[1], 10);
	}
	return null;
}
//#endregion
//#region ../cli/src/commands/project/context.ts
/**
* Glue between the engine command context and the project controllers.
* The resolution and env-file operations take a controller
* `CommandContext` but read only `runtime.cwd`, `runtime.env`
* and `runtime.signal`, so the CLI hands them exactly that.
*/
/**
* The controllers' `CommandContext` is typed to the three fields the
* CLI supplies today, but a controller edit could start reading a
* fourth. Left alone that surfaces as
* `Cannot read properties of undefined`, worst case inside
* `project transfer` after the project has already moved. Refusing the
* read here names the missing field at the moment it is read instead.
* Probes pass through rather than throwing: symbols are how the language
* inspects an object, and `then` is their string-keyed equivalent — the
* runtime reads it on anything it resolves through a promise. Throwing
* on a probe would be the very failure this trap exists to remove.
*/
const PROBE_KEYS = new Set(["then"]);
function refuseUnknownReads(fields, prefix) {
	return new Proxy(fields, { get(target, key) {
		if (typeof key !== "string" || key in target || PROBE_KEYS.has(key)) return Reflect.get(target, key);
		throw new Error(`the operation-context adapter provides only runtime.cwd, runtime.env and runtime.signal; ${prefix}${key} was read`);
	} });
}
function operationContext(ctx) {
	return refuseUnknownReads({ runtime: refuseUnknownReads({
		cwd: ctx.cwd,
		env: ctx.env,
		signal: ctx.signal
	}, "runtime.") }, "");
}
function listWorkspaceProjects$1(ctx) {
	return listRealWorkspaceProjects(ctx.api, ctx.signal);
}
/** Explicit `--project`, else the `.prisma/local.json` pin, else the
*  setup-required error. An absent `commandName` makes that error read
*  "this command" and drops its retry step — `branch list`'s legacy
*  behavior. */
async function resolvePinnedProject(ctx, workspace, explicitProject, commandName) {
	const target = await resolveProjectTarget({
		context: operationContext(ctx),
		workspace,
		explicitProject,
		listProjects: () => listWorkspaceProjects$1(ctx),
		commandName
	});
	if (target.isErr()) throw projectResolutionErrorToStructured(target.error);
	return target.value;
}
/** Writes `.prisma/local.json` for this directory and keeps it out of
*  git, then reports what `project create` / `project link` did. */
async function bindDirectoryToProject(ctx, workspace, project, action) {
	const written = await writeLocalResolutionPin(ctx.cwd, {
		workspaceId: workspace.id,
		projectId: project.id
	}, ctx.signal);
	if (written.isErr()) throw projectDirectoryBindingErrorToStructured(written.error);
	const ignored = await ensureLocalResolutionPinGitignore(ctx.cwd, ctx.signal);
	if (ignored.isErr()) throw projectDirectoryBindingErrorToStructured(ignored.error);
	return {
		workspace,
		project,
		directory: `./${path.basename(ctx.cwd)}`,
		localPin: {
			path: LOCAL_RESOLUTION_PIN_RELATIVE_PATH,
			written: true
		},
		action
	};
}
//#endregion
//#region ../cli/src/commands/resources-shared/workspace.ts
function workspaceRequiredError$1() {
	return new CliStructuredError("AUTH.USAGE_ERROR", "Workspace required", {
		why: "This command needs an active workspace, but the authenticated session does not have one.",
		nextActions: [{
			kind: "user-choice",
			label: `Run ${CLI_NAME} auth login and choose a workspace.`
		}, {
			kind: "run-command",
			label: `${CLI_NAME} auth login`,
			command: `${CLI_NAME} auth login`
		}]
	});
}
async function resolveActiveWorkspace(ctx) {
	const credential = await ctx.activeCredential();
	if (credential?.workspaceId === void 0) throw workspaceRequiredError$1();
	return {
		id: credential.workspaceId,
		name: credential.workspaceName ?? credential.workspaceId
	};
}
//#endregion
//#region ../cli/src/commands/branch/list.ts
/** The `branch list` command. */
const TITLE$12 = "Listing branches for the resolved project.";
function listPresentations$8(result) {
	const rows = result.branches.map((branch) => [
		branch.name,
		branch.role,
		branch.envMap
	]);
	return {
		json: () => result,
		next: () => [],
		human: () => [
			{
				kind: "summary",
				status: "info",
				text: TITLE$12
			},
			{
				kind: "fields",
				rows: [{
					label: "project",
					value: result.projectName
				}]
			},
			...rows.length === 0 ? [{
				kind: "summary",
				status: "info",
				text: "No branches found."
			}] : [{
				kind: "table",
				columns: [
					"Name",
					"Role",
					"Env map"
				],
				rows
			}]
		],
		stdout: () => rows.map((row) => row.join("	"))
	};
}
const branchListCommand = defineCommand({
	help: {
		summary: "List a project's Platform branches",
		description: "A Branch maps to a Git branch of the connected repository. Each one is an isolated environment with its own services, databases, buckets, and environment variables: the production branch serves live traffic, every other branch is a preview.",
		examples: ["branch list", "branch list --project my-app"]
	},
	args: { flags: { project: flag.string({
		brief: "Project id or name (default: the project this directory is linked to)",
		placeholder: "id-or-name"
	}) } },
	needs: { credentials: true },
	handler: async (args, ctx) => {
		const target = await resolvePinnedProject(ctx, await resolveActiveWorkspace(ctx), args.flags.project, "branch list");
		const branches = await listBranches$1(ctx.api, target.project.id, ctx.signal);
		const result = {
			projectId: target.project.id,
			projectName: target.project.name,
			branches: sortBranches(branches.map(toBranchSummary))
		};
		return ok(ctx.present({ data: result }, listPresentations$8(result)));
	}
});
//#endregion
//#region ../cli/src/lib/bucket/provider.ts
function createManagementBucketProvider(client) {
	return {
		async listBuckets(options) {
			const buckets = [];
			let cursor;
			while (true) {
				const result = await client.GET("/v1/buckets", {
					params: { query: {
						projectId: options.projectId,
						branchGitName: options.branchName,
						cursor
					} },
					signal: options.signal
				});
				if (result.error || !result.data) throw bucketApiError("Failed to list buckets", result.response, result.error);
				const data = result.data;
				buckets.push(...data.data);
				if (!data.pagination.hasMore || !data.pagination.nextCursor) break;
				cursor = data.pagination.nextCursor;
			}
			return buckets.map(normalizeBucket);
		},
		async createBucket(options) {
			const result = await client.POST("/v1/buckets", {
				body: {
					projectId: options.projectId,
					...options.name ? { name: options.name } : {},
					...options.branchGitName ? { branchGitName: options.branchGitName } : {}
				},
				signal: options.signal
			});
			if (result.error || !result.data) throw bucketApiError("Failed to create bucket", result.response, result.error);
			const data = result.data;
			return normalizeBucket(data.data);
		},
		async deleteBucket(bucketId, options) {
			const result = await client.DELETE("/v1/buckets/{bucketId}", {
				params: { path: { bucketId } },
				signal: options?.signal
			});
			if (result.error) throw bucketApiError("Failed to delete bucket", result.response, result.error);
		},
		async listKeys(bucketId, options) {
			const keys = [];
			let cursor;
			while (true) {
				const result = await client.GET("/v1/buckets/{bucketId}/keys", {
					params: {
						path: { bucketId },
						query: { cursor }
					},
					signal: options?.signal
				});
				if (result.error || !result.data) throw bucketApiError("Failed to list bucket keys", result.response, result.error);
				const data = result.data;
				keys.push(...data.data);
				if (!data.pagination.hasMore || !data.pagination.nextCursor) break;
				cursor = data.pagination.nextCursor;
			}
			return keys.map(normalizeKey);
		},
		async createKey(options) {
			const result = await client.POST("/v1/buckets/{bucketId}/keys", {
				params: { path: { bucketId: options.bucketId } },
				body: {
					role: options.role,
					...options.name ? { name: options.name } : {}
				},
				signal: options.signal
			});
			if (result.error || !result.data) throw bucketApiError("Failed to create bucket key", result.response, result.error);
			const raw = result.data.data;
			const secretAccessKey = raw.secretAccessKey;
			const accessKeyId = raw.accessKeyId;
			const endpoint = raw.endpoint;
			const bucketName = raw.bucketName;
			if (!secretAccessKey || !accessKeyId || !endpoint || !bucketName) throw bucketKeySecretMissingError(options.bucketId);
			return {
				key: normalizeKey(raw),
				secretAccessKey,
				accessKeyId,
				endpoint,
				bucketName
			};
		},
		async deleteKey(bucketId, keyId, options) {
			const result = await client.DELETE("/v1/buckets/{bucketId}/keys/{keyId}", {
				params: { path: {
					bucketId,
					keyId
				} },
				signal: options?.signal
			});
			if (result.error) throw bucketApiError("Failed to delete bucket key", result.response, result.error);
		}
	};
}
function normalizeBucket(raw) {
	return {
		id: raw.id,
		name: raw.name,
		status: raw.status,
		branchId: raw.branchId,
		createdAt: raw.createdAt
	};
}
function normalizeKey(raw) {
	return {
		id: raw.id,
		name: raw.name,
		role: raw.role,
		valueHint: raw.valueHint,
		createdAt: raw.createdAt
	};
}
const VERBOSE_LOG_FIX$1 = "Re-run with --log-level verbose for the underlying API response details.";
function userChoice$3(label) {
	return {
		kind: "user-choice",
		label
	};
}
function runCommand$3(command) {
	return {
		kind: "run-command",
		label: command,
		command
	};
}
function bucketKeySecretMissingError(bucketId) {
	return new CliStructuredError("BUCKET.KEY_SECRET_MISSING", "Created bucket key did not return credentials", {
		why: "Bucket key credentials are one-time-view secrets, but the Management API did not include them in this create response.",
		nextActions: [userChoice$3("Create another bucket key and store the returned credentials immediately."), runCommand$3(`${CLI_NAME} bucket key create ${bucketId}`)]
	});
}
/** A 401 or 403 is the API refusing the caller, not a bucket problem. */
function isRejectedCaller$1(status) {
	return status === 401 || status === 403;
}
function apiErrorWhy$1(status, message) {
	if (!isRejectedCaller$1(status)) return message ?? `The Management API returned status ${status || "unknown"}.`;
	const rejection = `The Management API rejected the request as ${status === 401 ? "unauthorized" : "forbidden"}.`;
	return message ? `${rejection} ${message}` : rejection;
}
function apiErrorMeta$1(status, apiCode) {
	if (!status && apiCode === void 0) return;
	return {
		...status ? { status } : {},
		...apiCode === void 0 ? {} : { apiCode }
	};
}
function apiErrorActions$1(status, hint) {
	if (!isRejectedCaller$1(status)) return [userChoice$3(hint ?? VERBOSE_LOG_FIX$1)];
	return [userChoice$3(hint ?? `Sign in again with prisma auth login, then retry the command.`), runCommand$3(`${CLI_NAME} auth login`)];
}
/**
* Every bucket Management API failure lands on the one registered code.
* The response's own error code is data, not an identity: it travels in
* `meta.apiCode` beside `meta.status` so a consumer can still branch on
* it without the CLI minting a code it never registered.
*/
function bucketApiError(summary, response, error) {
	const status = response?.status ?? 0;
	const meta = apiErrorMeta$1(status, error?.error?.code);
	return new CliStructuredError("BUCKET.API_ERROR", summary, {
		why: apiErrorWhy$1(status, error?.error?.message),
		...meta === void 0 ? {} : { meta },
		nextActions: apiErrorActions$1(status, error?.error?.hint)
	});
}
//#endregion
//#region ../cli/src/commands/bucket/context.ts
/** Workspace, project and provider for the `bucket *` commands. */
/** Where a caller who is missing a bucket id finds one. */
const LIST_BUCKETS_COMMAND = `${CLI_NAME} bucket list`;
const projectFlag$3 = flag.string({
	brief: "Project id or name (default: the project this directory is linked to)",
	placeholder: "id-or-name"
});
const branchFlag$2 = flag.string({
	brief: "Branch git name; use to target one branch's environment",
	placeholder: "git-name"
});
const bucketPositional = positional.string({
	brief: "Bucket id",
	placeholder: "bucket-id"
});
/** The legacy `requireBucketContext`: `bucket list` and `bucket create`
*  address a project, so they need the workspace and the resolved
*  project before the provider. */
async function resolveBucketContext(ctx, flags, commandName) {
	const target = await resolvePinnedProject(ctx, await resolveActiveWorkspace(ctx), flags.project, commandName);
	return {
		provider: createManagementBucketProvider(ctx.api),
		projectId: target.project.id,
		projectName: target.project.name
	};
}
/** The legacy `requireBucketProviderOnly`: `bucket delete` and every
*  `bucket key` command address a bucket id directly, with no workspace
*  requirement and no project resolution. */
function resolveBucketProviderOnly(ctx) {
	return createManagementBucketProvider(ctx.api);
}
//#endregion
//#region ../cli/src/commands/bucket/presentation.ts
/** Legacy `formatBucketTarget`. */
function bucketTargetLabel(projectName, branchId) {
	return branchId ? `${projectName} / ${branchId}` : projectName;
}
function bucketRows(buckets) {
	return buckets.map((bucket) => [
		bucket.name,
		bucket.id,
		bucket.status,
		bucket.branchId ?? "unscoped",
		bucket.createdAt
	]);
}
/** The stdout rows: an unscoped bucket has an empty branch field, not
*  the word a reader wants to see there. */
function bucketStdoutRows(buckets) {
	return buckets.map((bucket) => [
		bucket.name,
		bucket.id,
		bucket.status,
		bucket.branchId ?? "",
		bucket.createdAt
	]);
}
function bucketKeyRows(keys) {
	return keys.map((key) => [
		key.name,
		key.id,
		key.role,
		key.valueHint,
		key.createdAt
	]);
}
//#endregion
//#region ../cli/src/commands/bucket/create.ts
/** The `bucket create` command. */
const bucketCreateCommand = defineCommand({
	args: { flags: {
		name: flag.string({
			brief: "Bucket display name (auto-generated if omitted)",
			placeholder: "name"
		}),
		project: projectFlag$3,
		branch: branchFlag$2
	} },
	help: {
		summary: "Create an object-store bucket",
		description: "Creates blob storage in a Branch of the project. A bucket holds no credentials of its own: mint them with 'bucket key create', which prints them once.",
		examples: [
			"bucket create",
			"bucket create --name my-store",
			"bucket create --branch preview --json"
		]
	},
	needs: { credentials: true },
	handler: async (args, ctx) => {
		const { provider, projectId, projectName } = await resolveBucketContext(ctx, args.flags, "bucket create");
		const bucket = await provider.createBucket({
			projectId,
			name: args.flags.name?.trim() || void 0,
			branchGitName: args.flags.branch,
			signal: ctx.signal
		});
		const result = {
			projectId,
			projectName,
			bucket
		};
		return ok(ctx.present({ data: result }, {
			human: () => [{
				kind: "summary",
				status: "ok",
				text: `Created bucket "${bucket.name}" in ${bucketTargetLabel(projectName, bucket.branchId)}.`
			}],
			stdout: () => [],
			json: () => result,
			next: () => []
		}));
	}
});
//#endregion
//#region ../cli/src/commands/bucket/delete.ts
/** The `bucket delete` command. */
const CONSENT_QUESTION$6 = "Deleting this bucket permanently removes all objects and access keys.";
function deletePresentations$5(result) {
	return {
		stdout: () => [],
		json: () => result,
		next: () => [],
		human: () => [
			{
				kind: "summary",
				status: "ok",
				text: "Deleting object-store bucket."
			},
			{
				kind: "fields",
				rows: [{
					label: "bucket",
					value: result.bucket.id
				}]
			},
			{
				kind: "list",
				items: ["Bucket and all its access keys were removed."]
			}
		]
	};
}
const bucketDeleteCommand = defineCommand({
	args: { positionals: { bucketId: bucketPositional } },
	help: {
		summary: "Delete a bucket and all its access keys",
		description: "Deletion is permanent: the stored objects are destroyed and every access key stops working. The exact bucket id is the consent token; pass it with --confirm to run non-interactively.",
		examples: ["bucket delete bkt_123 --confirm bkt_123"]
	},
	needs: { credentials: true },
	handler: async (args, ctx) => {
		const bucketId = args.positionals.bucketId.trim();
		if (!bucketId) throw new CliStructuredError("BUCKET.USAGE_ERROR", "Bucket id required", {
			why: "Bucket deletion needs a bucket id.",
			nextActions: [{
				kind: "user-choice",
				label: "Pass the bucket id to delete."
			}, {
				kind: "run-command",
				label: LIST_BUCKETS_COMMAND,
				command: LIST_BUCKETS_COMMAND
			}]
		});
		await ctx.prompt.consent(CONSENT_QUESTION$6, { token: bucketId });
		await resolveBucketProviderOnly(ctx).deleteBucket(bucketId, { signal: ctx.signal });
		const result = { bucket: { id: bucketId } };
		return ok(ctx.present({ data: result }, deletePresentations$5(result)));
	}
});
//#endregion
//#region ../cli/src/commands/bucket/key-create.ts
/** The `bucket key create` command. */
/** Legacy `resolveKeyRole`: anything that is not exactly `read` — the
*  omitted flag included — is `read_write`. */
function resolveKeyRole(role) {
	return role === "read" ? "read" : "read_write";
}
function createPresentations$1(result) {
	return {
		json: () => result,
		next: () => [],
		human: () => [
			{
				kind: "summary",
				status: "ok",
				text: `Created key "${result.key.name}" for bucket "${result.bucketName}".`
			},
			{
				kind: "list",
				items: ["The credentials below are shown once — copy them now.", "Set these environment variables to use this bucket:"]
			},
			{
				kind: "fields",
				rows: [
					{
						label: "S3_ENDPOINT",
						value: result.endpoint
					},
					{
						label: "S3_ACCESS_KEY_ID",
						value: result.accessKeyId
					},
					{
						label: "S3_SECRET_ACCESS_KEY",
						value: result.secretAccessKey
					},
					{
						label: "S3_BUCKET",
						value: result.bucketName
					}
				]
			}
		],
		stdout: () => [
			`S3_ENDPOINT=${result.endpoint}`,
			`S3_ACCESS_KEY_ID=${result.accessKeyId}`,
			`S3_SECRET_ACCESS_KEY=${result.secretAccessKey}`,
			`S3_BUCKET=${result.bucketName}`
		]
	};
}
const bucketKeyCreateCommand = defineCommand({
	args: {
		positionals: { bucketId: bucketPositional },
		flags: {
			role: flag.enum({
				brief: "Access role; read_write unless set, use read for consumers that only download",
				values: ["read", "read_write"]
			}),
			name: flag.string({
				brief: "Key display name; use it to record which consumer holds the key (auto-generated if omitted)",
				placeholder: "name"
			})
		}
	},
	help: {
		summary: "Create a bucket access key and print its one-time credentials",
		description: "Mints S3-compatible credentials for one bucket, shown exactly once; store them now, nothing prints them again. Give each consumer its own key so one can be revoked without breaking the others, and grant read-only consumers --role read.",
		examples: [
			"bucket key create bkt_123",
			"bucket key create bkt_123 --role read",
			"bucket key create bkt_123 --name ci-key --role read_write"
		]
	},
	needs: { credentials: true },
	handler: async (args, ctx) => {
		const bucketId = args.positionals.bucketId.trim();
		if (!bucketId) throw new CliStructuredError("BUCKET.USAGE_ERROR", "Bucket id required", {
			why: "Bucket key creation needs a bucket id.",
			nextActions: [{
				kind: "user-choice",
				label: "Pass the bucket id."
			}, {
				kind: "run-command",
				label: LIST_BUCKETS_COMMAND,
				command: LIST_BUCKETS_COMMAND
			}]
		});
		const result = {
			bucketId,
			...await resolveBucketProviderOnly(ctx).createKey({
				bucketId,
				name: args.flags.name?.trim() || void 0,
				role: resolveKeyRole(args.flags.role),
				signal: ctx.signal
			})
		};
		return ok(ctx.present({ data: result }, createPresentations$1(result)));
	}
});
//#endregion
//#region ../cli/src/commands/bucket/key-delete.ts
/** The `bucket key delete` command. */
function deletePresentations$4(result) {
	return {
		stdout: () => [],
		json: () => result,
		next: () => [],
		human: () => [
			{
				kind: "summary",
				status: "ok",
				text: "Deleting bucket access key."
			},
			{
				kind: "fields",
				rows: [{
					label: "key",
					value: result.key.id
				}]
			},
			{
				kind: "list",
				items: ["The access key was revoked and removed."]
			}
		]
	};
}
const bucketKeyDeleteCommand = defineCommand({
	args: { positionals: {
		bucketId: bucketPositional,
		keyId: positional.string({
			brief: "Key id",
			placeholder: "key-id"
		})
	} },
	help: {
		summary: "Revoke and delete a bucket access key",
		description: "The key's credentials stop working immediately; anything still using them loses access to the bucket. The bucket and its other keys are untouched.",
		examples: ["bucket key delete bkt_123 bkey_456"]
	},
	needs: { credentials: true },
	handler: async (args, ctx) => {
		const bucketId = args.positionals.bucketId.trim();
		const keyId = args.positionals.keyId.trim();
		if (!bucketId || !keyId) {
			const listKeysCommand = `${CLI_NAME} bucket key list <bucketId>`;
			throw new CliStructuredError("BUCKET.USAGE_ERROR", "Bucket id and key id required", {
				why: "Bucket key deletion needs both a bucket id and a key id.",
				nextActions: [{
					kind: "user-choice",
					label: "Pass the bucket id and key id."
				}, {
					kind: "run-command",
					label: listKeysCommand,
					command: listKeysCommand
				}]
			});
		}
		await resolveBucketProviderOnly(ctx).deleteKey(bucketId, keyId, { signal: ctx.signal });
		const result = { key: { id: keyId } };
		return ok(ctx.present({ data: result }, deletePresentations$4(result)));
	}
});
//#endregion
//#region ../cli/src/output/patterns.ts
function serializeList(input) {
	return {
		context: input.context,
		items: input.items.map((item) => ({
			name: item.label,
			id: item.id,
			status: item.status
		})),
		count: input.items.length
	};
}
//#endregion
//#region ../cli/src/presenters/bucket.ts
function serializeBucketList(result) {
	return {
		context: {
			project: result.projectName,
			...result.branchName ? { branch: result.branchName } : {}
		},
		items: result.buckets.map((bucket) => ({
			name: bucket.name,
			id: bucket.id,
			status: bucket.status
		})),
		count: result.buckets.length,
		projectId: result.projectId,
		branchName: result.branchName,
		buckets: result.buckets
	};
}
function serializeBucketKeyList(result) {
	return {
		...serializeList({
			context: { bucket: result.bucketId },
			items: result.keys.map((key) => ({
				noun: "key",
				label: key.name,
				id: key.id,
				status: null
			}))
		}),
		bucketId: result.bucketId,
		keys: result.keys
	};
}
//#endregion
//#region ../cli/src/commands/bucket/key-list.ts
/** The `bucket key list` command. */
const TITLE$11 = "Listing access keys for bucket.";
function listPresentations$7(result) {
	const rows = bucketKeyRows(result.keys);
	return {
		next: () => [],
		human: () => [
			{
				kind: "summary",
				status: "info",
				text: TITLE$11
			},
			{
				kind: "fields",
				rows: [{
					label: "bucket",
					value: result.bucketId
				}]
			},
			...rows.length === 0 ? [{
				kind: "summary",
				status: "info",
				text: "No keys found."
			}] : [{
				kind: "table",
				columns: [
					"Name",
					"Id",
					"Role",
					"Hint",
					"Created"
				],
				rows
			}]
		],
		stdout: () => rows.map((row) => row.join("	")),
		json: () => serializeBucketKeyList(result)
	};
}
const bucketKeyListCommand = defineCommand({
	args: { positionals: { bucketId: bucketPositional } },
	help: {
		summary: "List access keys for a bucket",
		description: "Shows each key's id, name, and role. Key secrets print once, at 'bucket key create'; listing never reveals them.",
		examples: ["bucket key list bkt_123", "bucket key list bkt_123 --json"]
	},
	needs: { credentials: true },
	handler: async (args, ctx) => {
		const bucketId = args.positionals.bucketId.trim();
		if (!bucketId) throw new CliStructuredError("BUCKET.USAGE_ERROR", "Bucket id required", {
			why: "Bucket key listing needs a bucket id.",
			nextActions: [{
				kind: "user-choice",
				label: "Pass the bucket id."
			}, {
				kind: "run-command",
				label: LIST_BUCKETS_COMMAND,
				command: LIST_BUCKETS_COMMAND
			}]
		});
		const result = {
			bucketId,
			keys: await resolveBucketProviderOnly(ctx).listKeys(bucketId, { signal: ctx.signal })
		};
		return ok(ctx.present({ data: result }, listPresentations$7(result)));
	}
});
//#endregion
//#region ../cli/src/commands/bucket/list.ts
/** The `bucket list` command. */
const TITLE$10 = "Listing object-store buckets for the resolved project.";
function listPresentations$6(result) {
	const rows = bucketRows(result.buckets);
	const stdoutRows = bucketStdoutRows(result.buckets);
	return {
		next: () => [],
		human: () => [
			{
				kind: "summary",
				status: "info",
				text: TITLE$10
			},
			{
				kind: "fields",
				rows: [{
					label: "project",
					value: result.projectName
				}, ...result.branchName ? [{
					label: "branch",
					value: result.branchName
				}] : []]
			},
			...rows.length === 0 ? [{
				kind: "summary",
				status: "info",
				text: "No buckets found."
			}] : [{
				kind: "table",
				columns: [
					"Name",
					"Id",
					"Status",
					"Branch",
					"Created"
				],
				rows
			}]
		],
		stdout: () => stdoutRows.map((row) => row.join("	")),
		json: () => serializeBucketList(result)
	};
}
const bucketListCommand = defineCommand({
	args: { flags: {
		project: projectFlag$3,
		branch: branchFlag$2
	} },
	help: {
		summary: "List a project's object-store buckets",
		description: "A bucket is blob storage for files and uploads. Buckets are branch-bound: each belongs to one Branch, the isolated environment for one Git branch.",
		examples: [
			"bucket list",
			"bucket list --branch preview",
			"bucket list --json"
		]
	},
	needs: { credentials: true },
	handler: async (args, ctx) => {
		const { provider, projectId, projectName } = await resolveBucketContext(ctx, args.flags, "bucket list");
		const buckets = await provider.listBuckets({
			projectId,
			branchName: args.flags.branch,
			signal: ctx.signal
		});
		const result = {
			projectId,
			projectName,
			branchName: args.flags.branch ?? null,
			buckets
		};
		return ok(ctx.present({ data: result }, listPresentations$6(result)));
	}
});
//#endregion
//#region ../cli/src/lib/feedback.ts
const FEEDBACK_TIMEOUT_MS = 3e3;
const TIMEOUT_DETAIL = `The feedback service did not answer within ${FEEDBACK_TIMEOUT_MS / 1e3} seconds.`;
function isTimeout(error) {
	return error instanceof Error && error.name === "TimeoutError";
}
function unreachableDetail(error) {
	if (isTimeout(error)) return TIMEOUT_DETAIL;
	return `The feedback service could not be reached${error instanceof Error && error.cause instanceof Error ? ` (${error.cause.message})` : ""}.`;
}
function unreadableBodyDetail(error) {
	return isTimeout(error) ? TIMEOUT_DETAIL : "The feedback service response could not be read.";
}
//#endregion
//#region ../cli/src/commands/feedback.ts
const DEFAULT_FEEDBACK_ENDPOINT = "https://hiieirp2pwqnjvq9axzyg6d0.fra.prisma.build/feedback";
const MAX_MESSAGE_LENGTH = 4e3;
const MAX_EMAIL_LENGTH = 320;
const EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
function sendFailedError(detail) {
	return new CliStructuredError("FEEDBACK.SEND_FAILED", "Feedback could not be delivered", {
		why: detail,
		nextActions: [{
			kind: "user-choice",
			label: "Check your network and rerun."
		}]
	});
}
function messageRequiredError() {
	return new CliStructuredError("FEEDBACK.MESSAGE_REQUIRED", "Feedback message required", {
		why: "The message argument is empty.",
		nextActions: [{
			kind: "user-choice",
			label: "Pass a non-empty message."
		}, {
			kind: "run-command",
			label: "Send feedback",
			command: `${CLI_NAME} feedback "the deploy flow is great"`
		}]
	});
}
function messageTooLongError(length) {
	return new CliStructuredError("FEEDBACK.MESSAGE_TOO_LONG", "Feedback message too long", {
		why: `The message is ${length} characters; the limit is ${MAX_MESSAGE_LENGTH}.`,
		nextActions: [{
			kind: "user-choice",
			label: "Shorten the message."
		}]
	});
}
function emailInvalidError(value) {
	return new CliStructuredError("FEEDBACK.EMAIL_INVALID", "Invalid email", {
		why: `"${value}" is not a valid email address of at most ${MAX_EMAIL_LENGTH} characters.`,
		nextActions: [{
			kind: "user-choice",
			label: "Pass a valid address with --email, or drop the flag to stay anonymous."
		}, {
			kind: "run-command",
			label: "Send feedback with a contact address",
			command: `${CLI_NAME} feedback "please add X" --email you@example.com`
		}]
	});
}
function feedbackPresentations(result) {
	return {
		stdout: () => [],
		json: () => result,
		next: () => [],
		human: () => [{
			kind: "summary",
			status: "ok",
			text: "Feedback sent. Thank you!"
		}, {
			kind: "fields",
			rows: [
				...result.id ? [{
					label: "id",
					value: result.id
				}] : [],
				{
					label: "sent as",
					value: result.email ?? "anonymous"
				},
				{
					label: "included",
					value: `CLI ${result.context.cliVersion}, ${result.context.runtime.name} ${result.context.runtime.version}, ${result.context.platform} ${result.context.arch}`
				}
			]
		}]
	};
}
async function readServiceError(response, signal) {
	let payload;
	try {
		payload = await response.json();
	} catch (error) {
		if (signal.aborted) throw error;
		return "";
	}
	return typeof payload?.error?.message === "string" ? ` (${payload.error.message})` : "";
}
async function sendFeedback(endpoint, body, signal) {
	try {
		return await fetch(endpoint, {
			method: "POST",
			headers: {
				"content-type": "application/json",
				"user-agent": `${CLI_NAME}/${getCliVersion()}`
			},
			body: JSON.stringify(body),
			signal: AbortSignal.any([signal, AbortSignal.timeout(FEEDBACK_TIMEOUT_MS)])
		});
	} catch (error) {
		if (signal.aborted) throw error;
		throw sendFailedError(unreachableDetail(error));
	}
}
async function readSubmissionId(response, signal) {
	let payload;
	try {
		payload = await response.json();
	} catch (error) {
		if (signal.aborted) throw error;
		if (!(error instanceof SyntaxError)) throw sendFailedError(unreadableBodyDetail(error));
		payload = null;
	}
	return typeof payload?.id === "string" ? payload.id : null;
}
async function postFeedback(endpoint, body, signal) {
	const response = await sendFeedback(endpoint, body, signal);
	if (!response.ok) throw sendFailedError(`The feedback service responded with HTTP ${response.status}${await readServiceError(response, signal)}.`);
	return readSubmissionId(response, signal);
}
const feedbackCommand = defineCommand({
	help: {
		summary: "Send feedback to the Prisma CLI team",
		description: "Anonymous unless --email is passed. Every submission includes the CLI version, node version, and OS platform/arch, and nothing else.",
		examples: ["feedback \"the deploy flow is great\"", "feedback \"please add X\" --email you@example.com"]
	},
	args: {
		flags: { email: flag.string({
			brief: "Contact email if you want a reply; feedback is anonymous without it",
			placeholder: "address"
		}) },
		positionals: { message: positional.string({
			brief: "Feedback text (up to 4000 characters)",
			placeholder: "message"
		}) }
	},
	handler: async (args, ctx) => {
		const message = args.positionals.message.trim();
		if (!message) throw messageRequiredError();
		if (message.length > MAX_MESSAGE_LENGTH) throw messageTooLongError(message.length);
		const email = args.flags.email?.trim();
		if (email !== void 0 && (email.length > MAX_EMAIL_LENGTH || !EMAIL_PATTERN.test(email))) throw emailInvalidError(args.flags.email ?? "");
		const context = {
			cliVersion: getCliVersion(),
			runtime: { ...ctx.host.runtime },
			platform: ctx.host.platform,
			arch: ctx.host.arch
		};
		const result = {
			id: await postFeedback(ctx.env.PRISMA_CLI_FEEDBACK_URL || DEFAULT_FEEDBACK_ENDPOINT, {
				message,
				...email ? { email } : {},
				meta: {
					cliVersion: context.cliVersion,
					nodeVersion: context.runtime.version,
					platform: context.platform,
					arch: context.arch
				}
			}, ctx.signal),
			email: email ?? null,
			context
		};
		return ok(ctx.present({ data: result }, feedbackPresentations(result)));
	}
});
//#endregion
//#region ../cli/src/adapters/git.ts
const execFileAsync = promisify(execFile);
async function readGitOriginRemote(cwd, signal) {
	try {
		const { stdout } = await execFileAsync("git", [
			"config",
			"--get",
			"remote.origin.url"
		], {
			cwd,
			timeout: 5e3,
			signal
		});
		const remote = stdout.trim();
		return remote.length > 0 ? remote : null;
	} catch (error) {
		if (signal?.aborted || isAbortError(error)) throw error;
		return null;
	}
}
function isAbortError(error) {
	return error instanceof Error && error.name === "AbortError";
}
function parseGitHubRepositoryUrl(value) {
	const input = value.trim();
	const shorthand = input.match(/^git@github\.com:([^/\s]+)\/([^/\s]+?)(?:\.git)?$/);
	if (shorthand) return toGitHubRepositoryReference(shorthand[1], shorthand[2]);
	let parsed;
	try {
		parsed = new URL(input);
	} catch {
		return null;
	}
	if (parsed.hostname !== "github.com") return null;
	if (parsed.protocol !== "https:" && parsed.protocol !== "http:" && parsed.protocol !== "ssh:") return null;
	const parts = parsed.pathname.split("/").filter(Boolean);
	if (parts.length !== 2) return null;
	const [owner, rawName] = parts;
	return toGitHubRepositoryReference(owner, rawName.endsWith(".git") ? rawName.slice(0, -4) : rawName);
}
function toGitHubRepositoryReference(owner, name) {
	if (!owner || !name || owner.includes("/") || name.includes("/")) return null;
	return {
		provider: "github",
		owner,
		name,
		fullName: `${owner}/${name}`,
		url: `https://github.com/${owner}/${name}`
	};
}
//#endregion
//#region ../cli/src/presenters/project.ts
function serializeProjectList(result) {
	return {
		...serializeList({
			context: { workspace: result.workspace.name },
			items: result.projects.map((project) => ({
				noun: "project",
				label: project.name,
				id: project.id,
				status: null
			}))
		}),
		localBinding: result.localBinding ?? null
	};
}
function serializeProjectSetup(result) {
	return result;
}
function formatGitConnectionDetail(status) {
	switch (status) {
		case "active": return "GitHub branch automation is active for this project.";
		case "pending": return "GitHub branch automation is pending GitHub App installation.";
		case "archived": return "GitHub branch automation has been archived for this project.";
		default: return "GitHub repository is connected, but branch automation is not active.";
	}
}
//#endregion
//#region ../cli/src/commands/git/context.ts
/** Workspace, project and the source-repository client for the
*  `git *` commands. */
const projectFlag$2 = flag.string({
	brief: "Project id or name (default: the project this directory is linked to)",
	placeholder: "id-or-name"
});
async function resolveGitContext(ctx, explicitProject, commandName) {
	const target = await resolvePinnedProject(ctx, await resolveActiveWorkspace(ctx), explicitProject, commandName);
	return {
		api: ctx.api,
		target
	};
}
//#endregion
//#region ../cli/src/commands/git/errors.ts
/**
* The install wait's two terminal outcomes: the workspace has an
* inspectable installation that simply does not expose the repository,
* or it has none at all.
*/
function installWaitFailedError(repository, installUrl, inspectableInstallationCount) {
	return inspectableInstallationCount > 0 ? repoNotAccessibleError(repository, installUrl) : repoInstallationRequiredError(repository, installUrl);
}
//#endregion
//#region ../cli/src/commands/git/connect.ts
/** The `git connect` command. */
/** Where the deploy workflow's inputs and the minimal file are documented. */
const WORKFLOW_DOCS_URL = "https://github.com/prisma/cloud-deploy-action#readme";
/** The platform never builds on push; the repository's own Actions workflow
*  does, so a connection without one deploys nothing. Console import writes
*  the file through a setup pull request; this command does not. */
const WORKFLOW_DETAIL = "Deploys run from the repository's GitHub Actions workflow (prisma/cloud-deploy-action), which must exist on each pushed branch; the platform does not build on push.";
const WORKFLOW_NEXT_STEP = "If the repository has no .github/workflows/prisma-deploy.yml yet, add one running prisma/cloud-deploy-action@v1 with `id-token: write`; a push of any branch that lacks the file deploys nothing.";
/** The legacy wait line, printed once before the poll loop. */
const WAIT_MESSAGE = "Waiting for GitHub App installation or repository access approval...";
/**
* The legacy `resolveInstalledRepository`: find the repository in the
* workspace's GitHub App installations, and when it is not there yet,
* send the user to an install intent and wait for them to finish. The
* engine owns the announcement, the browser and the polling clock; this
* only supplies the address, the cadence and the question being polled.
*/
async function resolveInstalledRepository(ctx, api, workspaceId, repository) {
	const inspect = async (signal) => findRepositoryInInstallations(api, await listScmInstallations(api, workspaceId, signal), repository, signal);
	const first = await inspect(ctx.signal);
	if (first.match) return first.match;
	const installUrl = await createGitHubInstallIntent(api, workspaceId, ctx.signal);
	let match = null;
	let inspectableInstallationCount = 0;
	try {
		await ctx.prompt.browserWait({
			url: installUrl,
			message: WAIT_MESSAGE,
			timeout: readPositiveIntegerEnv(ctx.env.PRISMA_CLI_GITHUB_INSTALL_TIMEOUT_MS, GITHUB_INSTALL_POLL_TIMEOUT_MS),
			interval: readPositiveIntegerEnv(ctx.env.PRISMA_CLI_GITHUB_INSTALL_POLL_INTERVAL_MS, GITHUB_INSTALL_POLL_INTERVAL_MS),
			poll: async (signal) => {
				const lookup = await inspect(signal);
				match = lookup.match;
				inspectableInstallationCount = lookup.inspectableInstallationCount;
				return lookup.match !== null;
			}
		});
	} catch (error) {
		if (CliStructuredError.is(error) && error.code === "CLI.BROWSER_WAIT_TIMEOUT") throw installWaitFailedError(repository, installUrl, inspectableInstallationCount);
		throw error;
	}
	if (match === null) throw installWaitFailedError(repository, installUrl, inspectableInstallationCount);
	return match;
}
/** The connect result in every mode: the connection card, plus the
*  workflow step the platform will not do for you. */
function connectPresentations(result) {
	const connection = result.repositoryConnection;
	return {
		stdout: () => [],
		json: () => result,
		next: () => [{
			kind: "edit-file",
			label: WORKFLOW_NEXT_STEP
		}, {
			kind: "open-url",
			label: "Workflow reference (prisma/cloud-deploy-action)",
			url: WORKFLOW_DOCS_URL
		}],
		human: () => [
			{
				kind: "summary",
				status: "ok",
				text: "Connecting Git to the resolved project."
			},
			{
				kind: "fields",
				rows: [
					{
						label: "project",
						value: result.project.name
					},
					{
						label: "workspace",
						value: result.workspace.name
					},
					{
						label: "repository",
						value: connection.repository.fullName
					},
					{
						label: "status",
						value: connection.status
					}
				]
			},
			{
				kind: "list",
				items: [formatGitConnectionDetail(connection.status), WORKFLOW_DETAIL]
			}
		]
	};
}
const gitConnectCommand = defineCommand({
	args: {
		positionals: { gitUrl: positional.optionalString({
			brief: "GitHub repository URL (default: this repository's origin)",
			placeholder: "git-url"
		}) },
		flags: { project: projectFlag$2 }
	},
	help: {
		summary: "Connect a project to a GitHub repository so its deploy workflow deploys every push",
		description: "Links the repository to the project. Once connected, the repository's prisma/cloud-deploy-action workflow authenticates each run through GitHub's OIDC token and deploys every pushed branch to a matching Platform Branch, an isolated environment with its own services, databases, and buckets. The platform does not build on push itself: a repository without the workflow must add .github/workflows/prisma-deploy.yml (importing in the Console opens a pull request that adds it). If the Prisma GitHub App does not cover the repository yet, the command opens the install page and waits. Run without a URL to use this repository's origin remote.",
		examples: [
			"git connect",
			"git connect git@github.com:prisma/prisma-cli.git",
			"git connect --project proj_123"
		]
	},
	needs: { credentials: true },
	handler: async (args, ctx) => {
		const { api, target } = await resolveGitContext(ctx, args.flags.project, "git connect");
		const remoteUrl = args.positionals.gitUrl ?? await readGitOriginRemote(ctx.cwd, ctx.signal);
		if (!remoteUrl) {
			const example = `${CLI_NAME} git connect git@github.com:prisma/prisma-cli.git`;
			throw new CliStructuredError("GIT.USAGE_ERROR", "Repository connection requires a GitHub repository URL", {
				why: "No git-url was provided and the local repo does not have an origin remote.",
				nextActions: [{
					kind: "user-choice",
					label: `Pass a GitHub repository URL, or add a GitHub origin remote and rerun ${CLI_NAME} git connect.`
				}, {
					kind: "run-command",
					label: example,
					command: example
				}]
			});
		}
		const repository = parseGitHubRepositoryUrl(remoteUrl);
		if (!repository) throw unsupportedRepositoryProviderError();
		const existing = await readFirstSourceRepository(api, target.project.id, ctx.signal);
		if (existing) {
			const existingConnection = toRepositoryConnection(existing);
			if (!repositoryFullNamesMatch(existingConnection.repository.fullName, repository.fullName)) throw repoAlreadyConnectedError(existingConnection.repository.fullName);
			const idempotent = {
				...target,
				repositoryConnection: existingConnection
			};
			return ok(ctx.present({ data: idempotent }, connectPresentations(idempotent)));
		}
		const installed = await resolveInstalledRepository(ctx, api, target.workspace.id, repository);
		const { data, error, response } = await api.POST("/v1/source-repositories", {
			body: {
				projectId: target.project.id,
				provider: "github",
				providerRepositoryId: installed.repository.id,
				installationId: installed.installation.id
			},
			signal: ctx.signal
		});
		if (error || !data) throw repoConnectionApiError("Failed to connect GitHub repository", response, error);
		const result = {
			...target,
			repositoryConnection: toRepositoryConnection(data.data)
		};
		return ok(ctx.present({ data: result }, connectPresentations(result)));
	}
});
//#endregion
//#region ../cli/src/commands/git/disconnect.ts
/** The `git disconnect` command. */
function disconnectPresentations(result) {
	return {
		stdout: () => [],
		json: () => result,
		next: () => [],
		human: () => [
			{
				kind: "summary",
				status: "ok",
				text: "Disconnecting Git from the resolved project."
			},
			{
				kind: "fields",
				rows: [
					{
						label: "project",
						value: result.project.name
					},
					{
						label: "workspace",
						value: result.workspace.name
					},
					{
						label: "repository",
						value: result.repositoryConnection.repository.fullName
					}
				]
			},
			{
				kind: "list",
				items: ["GitHub branch automation is no longer active for this project."]
			}
		]
	};
}
const gitDisconnectCommand = defineCommand({
	args: { flags: { project: projectFlag$2 } },
	help: {
		summary: "Disconnect the GitHub repository: pushes stop deploying",
		description: "Stops deploy-on-push by removing the repository connection. Nothing already deployed changes: existing branches, services, and databases stay as they are.",
		examples: ["git disconnect", "git disconnect --project proj_123"]
	},
	needs: { credentials: true },
	handler: async (args, ctx) => {
		const { api, target } = await resolveGitContext(ctx, args.flags.project, "git disconnect");
		const existing = await readFirstSourceRepository(api, target.project.id, ctx.signal);
		if (!existing) throw repoNotConnectedError();
		const { error, response } = await api.DELETE("/v1/source-repositories/{id}", {
			params: { path: { id: existing.id } },
			signal: ctx.signal
		});
		if (error) throw repoConnectionApiError("Failed to disconnect GitHub repository", response, error);
		const result = {
			...target,
			repositoryConnection: toRepositoryConnection(existing)
		};
		return ok(ctx.present({ data: result }, disconnectPresentations(result)));
	}
});
//#endregion
//#region ../cli/src/lib/skills/sync.ts
/**
* Brings the harness skill directories in line with the installed
* source packages and the configured agents: copies each skill tree
* whose stamp does not match the package it came from, and removes
* copies whose source package is gone or whose agent the config no
* longer names. A target directory that exists but is not this CLI's
* copy is refused, never replaced. Doing nothing is the normal outcome
* and is not an error.
*/
async function syncSkills(status) {
	const synced = [];
	const refused = [];
	for (const skill of status.skills) {
		const dirs = skill.targets.filter((target) => target.state === "stale" || target.state === "absent").map((target) => target.dir);
		const refusedDirs = skill.targets.filter((target) => target.state === "unmanaged").map((target) => target.dir);
		if (refusedDirs.length > 0) refused.push({
			skill: skill.skill,
			dirs: refusedDirs
		});
		for (const target of skill.targets) if (target.state === "synced") await removeOldCliGitignore(path.join(status.projectRoot, target.dir, skill.skill, ".gitignore"));
		if (dirs.length === 0) continue;
		for (const dir of dirs) await replaceTree(skill.sourceDir, path.join(status.projectRoot, dir, skill.skill));
		synced.push({
			skill: skill.skill,
			library: skill.library,
			version: skill.version,
			dirs
		});
	}
	const pruned = [];
	for (const orphan of status.orphans) {
		for (const dir of orphan.dirs) {
			await rm(path.join(status.projectRoot, dir, orphan.skill), {
				recursive: true,
				force: true
			});
			await removeEmptyHarnessDirs(status.projectRoot, dir);
		}
		pruned.push({
			skill: orphan.skill,
			library: orphan.library,
			dirs: orphan.dirs
		});
	}
	return {
		projectRoot: status.projectRoot,
		packages: status.packages,
		skills: status.skills.map((skill) => skill.skill),
		synced,
		pruned,
		refused,
		checkDisabled: status.checkDisabled
	};
}
/**
* A harness directory this CLI emptied by removing its last copy is
* removed too, and so is its parent (`.claude/skills`, then `.claude`),
* so opting an agent out leaves nothing behind. `rmdir` refuses a
* directory holding anything else, which is exactly the user's file
* that must stay.
*/
async function removeEmptyHarnessDirs(projectRoot, dir) {
	for (let current = dir; current !== "." && current !== ""; current = path.dirname(current)) try {
		await rmdir(path.join(projectRoot, current));
	} catch {
		return;
	}
}
const OLD_CLI_GITIGNORE = /^\*\r?\n?$/;
async function removeOldCliGitignore(file) {
	let content;
	try {
		content = await readFile(file, "utf8");
	} catch {
		return;
	}
	if (OLD_CLI_GITIGNORE.test(content)) await rm(file, { force: true });
}
/**
* Copies a skill tree over whatever is at the destination, so a skill
* that lost a reference file between versions does not keep the stale
* one. Files are read and written rather than handed to `fs.cp`,
* because under Yarn PnP the source lives inside a zip and only the
* patched read path can see it.
*/
async function replaceTree(source, destination) {
	await rm(destination, {
		recursive: true,
		force: true
	});
	await copyTree(source, destination);
}
async function copyTree(source, destination) {
	await mkdir(destination, { recursive: true });
	for (const entry of await readdir(source, { withFileTypes: true })) {
		const from = path.join(source, entry.name);
		const to = path.join(destination, entry.name);
		if (entry.isDirectory()) {
			await copyTree(from, to);
			continue;
		}
		if (entry.isFile() || entry.isSymbolicLink()) await writeFile(to, await readFile(from));
	}
}
//#endregion
//#region ../cli/src/commands/skills/presentation.ts
function projectFields(projectRoot, checkDisabled) {
	return {
		kind: "fields",
		rows: [{
			label: "project",
			value: projectRoot
		}, {
			label: "check",
			value: checkDisabled ? "disabled" : "enabled"
		}]
	};
}
/** Decision B: "up to date" may not over-claim — when directories were
*  refused, the summary says so in the same line, for sync and list
*  alike. */
function unmanagedClause(count) {
	if (count === 0) return "";
	return count === 1 ? "; 1 directory is not managed by this CLI" : `; ${count} directories are not managed by this CLI`;
}
function syncSummary(result) {
	if (result.pruned.length === 0) {
		if (result.agents.length === 0) return "No agents are configured to sync skills for.";
		if (result.packages.length === 0) return "No Prisma packages with agent skills are installed.";
		if (result.skills.length === 0) return "No Prisma dependencies in your project ship agent skills to sync.";
	}
	const refusedDirs = result.refused.reduce((count, skill) => count + skill.dirs.length, 0);
	if (result.synced.length === 0 && result.pruned.length === 0) return `Agent skills are up to date${unmanagedClause(refusedDirs)}.`;
	const removed = `${result.pruned.length} skill${result.pruned.length === 1 ? "" : "s"}`;
	if (result.synced.length === 0 && result.pruned.length > 0) return `Removed ${removed}${unmanagedClause(refusedDirs)}.`;
	const synced = `${result.synced.length} skill${result.synced.length === 1 ? "" : "s"}`;
	return `${result.pruned.length === 0 ? `Synced ${synced}` : `Synced ${synced} and removed ${result.pruned.length}`}${unmanagedClause(refusedDirs)}.`;
}
function syncPresentations(result) {
	const syncedRows = result.synced.map((skill) => [
		skill.skill,
		skill.library,
		skill.version,
		skill.dirs.join(", ")
	]);
	const prunedRows = result.pruned.map((skill) => [skill.skill, skill.dirs.join(", ")]);
	const refusedRows = result.refused.map((skill) => [skill.skill, skill.dirs.join(", ")]);
	return {
		json: () => result,
		next: () => [],
		human: () => [
			{
				kind: "summary",
				status: result.synced.length > 0 ? "ok" : "info",
				text: syncSummary(result)
			},
			projectFields(result.projectRoot, result.checkDisabled),
			...syncedRows.length === 0 ? [] : [{
				kind: "table",
				columns: [
					"Skill",
					"Package",
					"Version",
					"Installed into"
				],
				rows: syncedRows
			}],
			...prunedRows.length === 0 ? [] : [{
				kind: "table",
				columns: ["Removed skill", "Removed from"],
				rows: prunedRows
			}],
			...refusedRows.length === 0 ? [] : [{
				kind: "table",
				columns: ["Unmanaged skill", "Left untouched in"],
				rows: refusedRows
			}]
		],
		stdout: () => syncedRows.map((row) => row.join("	"))
	};
}
function listSummary(result) {
	if (result.agents.length === 0) return "No agents are configured to sync skills for.";
	if (result.packages.length === 0) return "No Prisma packages with agent skills are installed.";
	if (result.skills.length === 0) return "No Prisma dependencies in your project ship agent skills.";
	if (!result.upToDate) return "Agent skills are out of date.";
	const unmanaged = result.skills.flatMap((skill) => skill.targets).filter((target) => target.state === "unmanaged").length;
	return `Agent skills are up to date${unmanagedClause(unmanaged)}.`;
}
function listPresentations$5(result) {
	const rows = result.skills.flatMap((skill) => skill.targets.map((target) => [
		skill.skill,
		skill.library,
		skill.version,
		target.dir,
		target.syncedVersion ?? "-",
		target.state
	]));
	return {
		json: () => result,
		next: () => [],
		human: () => [
			{
				kind: "summary",
				status: "info",
				text: listSummary(result)
			},
			projectFields(result.projectRoot, result.checkDisabled),
			...rows.length === 0 ? [] : [{
				kind: "table",
				columns: [
					"Skill",
					"Package",
					"Installed",
					"Directory",
					"Synced",
					"State"
				],
				rows
			}]
		],
		stdout: () => rows.map((row) => row.join("	"))
	};
}
//#endregion
//#region ../cli/src/commands/skills/sync.ts
function packageReports(packages) {
	return packages.map((installed) => ({
		package: installed.name,
		version: installed.version,
		conflictingVersions: installed.conflictingVersions
	}));
}
/** Workspace members that pin different versions of the same
*  skill-bearing package: the highest wins, and the user hears about
*  it, because the losing members get a skill describing a version
*  they did not install. */
function versionConflictDiagnostics(packages) {
	return packages.filter((installed) => installed.conflictingVersions.length > 1).map((installed) => ({
		code: "SKILLS.VERSION_CONFLICT",
		severity: "warn",
		summary: `Workspace members install different versions of ${installed.name} (${installed.conflictingVersions.join(", ")}); the skills for ${installed.version} were installed.`,
		nextActions: [{
			kind: "user-choice",
			label: `Pin one version of ${installed.name} across the workspace.`
		}]
	}));
}
/** Target directories that already hold a skill this CLI does not
*  manage: sync leaves them alone, and the user hears why the packaged
*  skill was not installed there. */
function unmanagedDirectoryDiagnostics(refused) {
	return refused.flatMap((entry) => entry.dirs.map((dir) => ({
		code: "SKILLS.UNMANAGED_DIRECTORY",
		severity: "warn",
		summary: `${dir}/${entry.skill} is not managed by this CLI, so it was left untouched.`,
		nextActions: [{
			kind: "user-choice",
			label: `Move or remove ${dir}/${entry.skill}, then rerun skills sync to install the packaged skill.`
		}]
	})));
}
function bothSwitchesError() {
	return new CliStructuredError("CLI.INVALID_ARGUMENTS", "--disable and --enable ask for opposite things, so only one may be given.", { nextActions: [{
		kind: "user-choice",
		label: "Run with --disable to silence the skills check, or --enable to restore it."
	}] });
}
const skillsSyncCommand = defineCommand({
	help: {
		summary: "Copy the AI-agent instruction files (skills) from installed Prisma packages into this project",
		description: "Agent skills are instruction files that teach AI coding agents (Claude Code, Cursor, and others) how to use the installed Prisma packages. They ship inside the packages themselves, so they always describe the version in use. Sync copies them into the skill directories of the agents that skills.agents in prisma.config.ts names (every known agent when unset), and removes copies whose package is gone or whose agent is no longer named there — agents: [] removes them all. It does nothing, and exits 0, when everything is already current.",
		examples: ["skills sync", "skills sync --disable"]
	},
	needs: { config: skillsConfigSection },
	args: { flags: {
		disable: flag.boolean({ brief: "Stop other commands reporting out-of-date skills in this project" }),
		enable: flag.boolean({ brief: "Undo --disable for this project" })
	} },
	handler: async (args, ctx) => {
		if (args.flags.disable && args.flags.enable) return notOk(bothSwitchesError());
		const outcome = await syncSkills(await readSkillsStatus(ctx.cwd, { agents: ctx.config.agents }));
		let optedOut = outcome.checkDisabled;
		if (args.flags.disable || args.flags.enable) {
			optedOut = args.flags.disable;
			await writeSkillsCheckDisabled(outcome.projectRoot, optedOut);
		}
		const checkDisabled = optedOut || !ctx.config.check;
		const result = {
			projectRoot: outcome.projectRoot,
			agents: ctx.config.agents,
			packages: packageReports(outcome.packages),
			skills: outcome.skills,
			synced: outcome.synced,
			pruned: outcome.pruned,
			refused: outcome.refused,
			checkDisabled
		};
		return ok(ctx.present({
			data: result,
			diagnostics: [...versionConflictDiagnostics(outcome.packages), ...unmanagedDirectoryDiagnostics(outcome.refused)]
		}, syncPresentations(result)));
	}
});
//#endregion
//#region ../cli/src/commands/init.ts
const POSTINSTALL_SCRIPT = "prisma skills sync || exit 0";
function summary(status, text) {
	return {
		kind: "summary",
		status,
		text
	};
}
const APPEND_ADVICE = {
	kind: "user-choice",
	label: `Append "${POSTINSTALL_SCRIPT}" to your postinstall script yourself to resync the skills on every install.`
};
function addDependencyAdvice(version) {
	return {
		kind: "user-choice",
		label: `Add "prisma": "${version}" to devDependencies yourself, then run your package manager's install.`
	};
}
function noPackageJsonDiagnostic(version) {
	return {
		code: "INIT.NO_PACKAGE_JSON",
		severity: "warn",
		summary: "There is no package.json in this directory, so the postinstall hook and the prisma dev dependency were not added.",
		nextActions: [{
			kind: "user-choice",
			label: `Run ${CLI_NAME} init from the directory that holds your package.json.`
		}, addDependencyAdvice(version)]
	};
}
function unreadablePackageJsonDiagnostic(version) {
	return {
		code: "INIT.PACKAGE_JSON_UNREADABLE",
		severity: "warn",
		summary: "package.json could not be parsed, so the postinstall hook and the prisma dev dependency were not added.",
		nextActions: [APPEND_ADVICE, addDependencyAdvice(version)]
	};
}
function unwritablePackageJsonDiagnostic(hookNeeded, dependencyNeeded, version) {
	return {
		code: "INIT.PACKAGE_JSON_UNWRITABLE",
		severity: "warn",
		summary: "package.json could not be written, so init left it unchanged.",
		nextActions: [...hookNeeded ? [APPEND_ADVICE] : [], ...dependencyNeeded ? [addDependencyAdvice(version)] : []]
	};
}
function scriptsNotAnObjectDiagnostic(dependencyNeeded, version) {
	return {
		code: "INIT.SCRIPTS_NOT_AN_OBJECT",
		severity: "warn",
		summary: "The scripts field in package.json is not an object, so init left it alone.",
		nextActions: [APPEND_ADVICE, ...dependencyNeeded ? [addDependencyAdvice(version)] : []]
	};
}
function foreignPostinstallDiagnostic(dependencyNeeded, version) {
	return {
		code: "INIT.POSTINSTALL_KEPT",
		severity: "warn",
		summary: "package.json already has a postinstall script, so init left it alone.",
		nextActions: [APPEND_ADVICE, ...dependencyNeeded ? [addDependencyAdvice(version)] : []]
	};
}
function devDependenciesNotAnObjectDiagnostic(version) {
	return {
		code: "INIT.DEV_DEPENDENCIES_NOT_AN_OBJECT",
		severity: "warn",
		summary: "The devDependencies field in package.json is not an object, so init did not add the prisma dev dependency.",
		nextActions: [{
			kind: "user-choice",
			label: `Fix the devDependencies field so it is an object, then add "prisma": "${version}" yourself and run your package manager's install.`
		}]
	};
}
function configSnippet(agents) {
	return `skills: { agents: [${agents.map((agent) => `"${agent}"`).join(", ")}] }`;
}
/** The same never-touch discipline as the postinstall step's
*  foreign-script rule: a prisma.config.ts the user already has is
*  theirs, and init only says what to add. */
function configKeptDiagnostic(agents) {
	return {
		code: "INIT.CONFIG_KEPT",
		severity: "warn",
		summary: "prisma.config.ts already exists, so init left it alone instead of writing the skills section.",
		nextActions: [{
			kind: "user-choice",
			label: `Add ${configSnippet(agents)} to the object passed to definePrismaConfig in prisma.config.ts.`
		}]
	};
}
function configUnwritableDiagnostic(agents) {
	return {
		code: "INIT.CONFIG_UNWRITABLE",
		severity: "warn",
		summary: "prisma.config.ts could not be written, so init skipped it.",
		nextActions: [{
			kind: "user-choice",
			label: `Create a prisma.config.ts whose definePrismaConfig call carries ${configSnippet(agents)}.`
		}]
	};
}
function skillsSyncFailedDiagnostic(cause) {
	return {
		code: "INIT.SKILLS_SYNC_FAILED",
		severity: "warn",
		summary: `The agent skills could not be synced: ${cause instanceof Error ? cause.message : String(cause)}`,
		nextActions: [{
			kind: "run-command",
			label: "Retry the sync on its own",
			command: `${CLI_NAME} skills sync`
		}]
	};
}
const FIRST_INDENT = /\n([ \t]+)"/;
/** The indentation the file already uses, so the rewrite matches it. */
function detectIndent(source) {
	return FIRST_INDENT.exec(source)?.[1] ?? "  ";
}
const BOM = "";
function isPlainObject(value) {
	return typeof value === "object" && value !== null && !Array.isArray(value);
}
function parseManifestObject(source) {
	try {
		const parsed = JSON.parse(source);
		return isPlainObject(parsed) ? parsed : null;
	} catch {
		return null;
	}
}
function renderManifest(manifest, source, bom, crlf) {
	let rewritten = JSON.stringify(manifest, null, detectIndent(source));
	if (crlf) rewritten = rewritten.replaceAll("\n", "\r\n");
	const eol = crlf ? "\r\n" : "\n";
	return `${bom}${rewritten}${source.endsWith("\n") ? eol : ""}`;
}
const DEPENDENCY_FIELDS = [
	"dependencies",
	"devDependencies",
	"optionalDependencies",
	"peerDependencies"
];
/** A declaration in any field, at any version or range, counts — init
*  never second-guesses a version the user chose. */
function declaresPrisma(manifest) {
	return DEPENDENCY_FIELDS.some((field) => {
		const value = manifest[field];
		return isPlainObject(value) && Object.hasOwn(value, "prisma");
	});
}
async function addPostinstallHook(cwd) {
	const manifestPath = path.join(cwd, "package.json");
	const version = getCliVersion();
	let raw;
	try {
		raw = await readFile(manifestPath, "utf8");
	} catch {
		return {
			report: {
				outcome: "skipped",
				script: null,
				dependency: "skipped"
			},
			lines: [summary("warn", "No package.json here; postinstall hook skipped.")],
			diagnostics: [noPackageJsonDiagnostic(version)]
		};
	}
	const bom = raw.startsWith(BOM) ? BOM : "";
	const source = bom === "" ? raw : raw.slice(1);
	const crlf = source.includes("\r\n");
	const manifest = parseManifestObject(source);
	if (manifest === null) return {
		report: {
			outcome: "skipped",
			script: null,
			dependency: "skipped"
		},
		lines: [summary("warn", "package.json could not be parsed; postinstall hook skipped.")],
		diagnostics: [unreadablePackageJsonDiagnostic(version)]
	};
	const dependencyDeclared = declaresPrisma(manifest);
	const keptDependency = dependencyDeclared ? "declared" : "skipped";
	if (manifest.scripts !== void 0 && !isPlainObject(manifest.scripts)) return {
		report: {
			outcome: "kept",
			script: null,
			dependency: keptDependency
		},
		lines: [summary("warn", "The scripts field in package.json is not an object; left untouched.")],
		diagnostics: [scriptsNotAnObjectDiagnostic(!dependencyDeclared, version)]
	};
	const scripts = manifest.scripts ?? {};
	const existing = scripts.postinstall;
	if (existing !== void 0 && existing !== "prisma skills sync || exit 0") return {
		report: {
			outcome: "kept",
			script: typeof existing === "string" ? existing : null,
			dependency: keptDependency
		},
		lines: [summary("warn", "package.json has its own postinstall script; left untouched.")],
		diagnostics: [foreignPostinstallDiagnostic(!dependencyDeclared, version)]
	};
	return writeHookAndDependency({
		cwd,
		manifestPath,
		manifest,
		scripts,
		source,
		bom,
		crlf,
		hookNeeded: existing === void 0,
		dependencyNeeded: !dependencyDeclared
	});
}
async function writeHookAndDependency(edit) {
	const { manifest, hookNeeded, dependencyNeeded } = edit;
	const version = getCliVersion();
	const dependencyBlocked = dependencyNeeded && manifest.devDependencies !== void 0 && !isPlainObject(manifest.devDependencies);
	const block = dependencyBlock(dependencyBlocked, version);
	const dependencyEdit = dependencyNeeded && !dependencyBlocked;
	if (!hookNeeded && !dependencyEdit) return {
		report: {
			outcome: "exists",
			script: POSTINSTALL_SCRIPT,
			dependency: block.kept
		},
		lines: [ALREADY_HOOKED, ...block.lines],
		diagnostics: block.diagnostics
	};
	if (hookNeeded) manifest.scripts = {
		...edit.scripts,
		postinstall: POSTINSTALL_SCRIPT
	};
	if (dependencyEdit) manifest.devDependencies = {
		...isPlainObject(manifest.devDependencies) ? manifest.devDependencies : {},
		prisma: version
	};
	try {
		await writeFile(edit.manifestPath, renderManifest(manifest, edit.source, edit.bom, edit.crlf), "utf8");
	} catch {
		return {
			report: {
				outcome: hookNeeded ? "skipped" : "exists",
				script: hookNeeded ? null : POSTINSTALL_SCRIPT,
				dependency: dependencyEdit ? "skipped" : block.kept
			},
			lines: [summary("warn", "package.json could not be written; left unchanged."), ...block.lines],
			diagnostics: [unwritablePackageJsonDiagnostic(hookNeeded, dependencyEdit, version), ...block.diagnostics]
		};
	}
	return manifestEditedStep(edit, dependencyEdit, block, version);
}
const ALREADY_HOOKED = {
	kind: "summary",
	status: "info",
	text: "The postinstall hook is already in package.json."
};
/** What a blocked (non-object) devDependencies field contributes to
*  every outcome: its warning line, its diagnostic, and the outcome the
*  untouched dependency reports. */
function dependencyBlock(blocked, version) {
	if (!blocked) return {
		lines: [],
		diagnostics: [],
		kept: "declared"
	};
	return {
		lines: [summary("warn", "The devDependencies field in package.json is not an object; prisma was not added.")],
		diagnostics: [devDependenciesNotAnObjectDiagnostic(version)],
		kept: "skipped"
	};
}
/** Detection walks ancestor package.json and lockfiles, and runs after
*  the manifest edit already landed — a directory it cannot read must
*  not fail the init, and guessing npm would name the wrong manager, so
*  the fallback advice names none. The same guard the post-login tip
*  puts around this walk. */
function installAddedDependencyAction(cwd) {
	try {
		return {
			kind: "run-command",
			label: "Install the added prisma dev dependency",
			command: resolveInstallCommandSync(cwd)
		};
	} catch {
		return {
			kind: "user-choice",
			label: "Run your package manager's install to fetch the added prisma dev dependency."
		};
	}
}
function manifestEditedStep(edit, dependencyEdit, block, version) {
	const { hookNeeded } = edit;
	return {
		report: {
			outcome: hookNeeded ? "added" : "exists",
			script: POSTINSTALL_SCRIPT,
			dependency: dependencyEdit ? "added" : block.kept
		},
		lines: [
			hookNeeded ? summary("ok", `Added "postinstall": "${POSTINSTALL_SCRIPT}" to package.json.`) : ALREADY_HOOKED,
			...dependencyEdit ? [summary("ok", `Added "prisma": "${version}" to devDependencies in package.json.`)] : [],
			...block.lines
		],
		next: dependencyEdit ? [installAddedDependencyAction(edit.cwd)] : [],
		diagnostics: block.diagnostics
	};
}
/** What the scaffold contains: the effective agents list, spelled the
*  way a user would write it by hand. */
function renderConfigScaffold(agents) {
	return [
		"import { definePrismaConfig } from \"prisma/config\";",
		"",
		"export default definePrismaConfig({",
		"  skills: {",
		`    agents: [${agents.map((agent) => `"${agent}"`).join(", ")}],`,
		"  },",
		"});",
		""
	].join("\n");
}
async function scaffoldConfigStep(cwd, agents, agentsConfigured) {
	const configPath = path.join(cwd, "prisma.config.ts");
	let existing = null;
	try {
		existing = await readFile(configPath, "utf8");
	} catch {
		existing = null;
	}
	if (existing !== null) return {
		report: {
			outcome: "exists",
			agents: null
		},
		lines: [agentsConfigured ? summary("info", "prisma.config.ts already configures skills.agents.") : summary("warn", "prisma.config.ts already exists; left untouched.")],
		diagnostics: agentsConfigured ? [] : [configKeptDiagnostic(agents)]
	};
	try {
		await writeFile(configPath, renderConfigScaffold(agents), {
			encoding: "utf8",
			flag: "wx"
		});
	} catch {
		return {
			report: {
				outcome: "skipped",
				agents: null
			},
			lines: [summary("warn", "prisma.config.ts could not be written; skipped.")],
			diagnostics: [configUnwritableDiagnostic(agents)]
		};
	}
	return {
		report: {
			outcome: "created",
			agents
		},
		lines: [summary("ok", `Created prisma.config.ts with ${configSnippet(agents)}.`)],
		diagnostics: []
	};
}
function skillsOutcome(result) {
	if (result.synced.length > 0 || result.pruned.length > 0) return "synced";
	if (result.agents.length === 0) return "no-agents";
	if (result.packages.length === 0) return "no-packages";
	if (result.skills.length === 0) return "no-skills";
	return "up-to-date";
}
async function syncSkillsStep(cwd, agents, checkEnabledByConfig) {
	try {
		const outcome = await syncSkills(await readSkillsStatus(cwd, { agents }));
		const result = {
			projectRoot: outcome.projectRoot,
			agents,
			packages: packageReports(outcome.packages),
			skills: outcome.skills,
			synced: outcome.synced,
			pruned: outcome.pruned,
			refused: outcome.refused,
			checkDisabled: outcome.checkDisabled || !checkEnabledByConfig
		};
		return {
			report: {
				outcome: skillsOutcome(result),
				sync: result
			},
			lines: null,
			diagnostics: [...versionConflictDiagnostics(outcome.packages), ...unmanagedDirectoryDiagnostics(outcome.refused)]
		};
	} catch (cause) {
		return {
			report: {
				outcome: "failed",
				sync: null
			},
			lines: [summary("warn", "The agent skills could not be synced.")],
			diagnostics: [skillsSyncFailedDiagnostic(cause)]
		};
	}
}
/** Whether some file on the resolved chain sits in a strict ancestor
*  directory of cwd. A config in cwd itself does not count, and neither
*  does one reached sideways (--config to a file elsewhere) — the spec
*  defers only to an ancestor config. The chain is the one the needs
*  check loaded before the handler ran, so init's own scaffold never
*  counts either. Realpath'd on both sides so symlinked layouts compare
*  like with like. */
function governedByAncestorConfig(files, cwd) {
	const here = realpathOr(cwd);
	return files.some((file) => {
		const relative = path.relative(realpathOr(path.dirname(file.path)), here);
		return relative !== "" && !relative.startsWith("..") && !path.isAbsolute(relative);
	});
}
const ANCESTOR_SKIPPED_POSTINSTALL = {
	report: {
		outcome: "skipped",
		reason: "governing-config",
		script: null,
		dependency: "skipped"
	},
	lines: [summary("info", "Skipped the postinstall hook and the prisma dev dependency: another prisma.config.ts already governs this directory, so both belong at the repository root. Pass --postinstall to add them here anyway.")],
	diagnostics: []
};
const ANCESTOR_SKIPPED_SKILLS = {
	report: {
		outcome: "skipped",
		reason: "governing-config",
		sync: null
	},
	lines: [summary("info", "Skipped the skills sync: another prisma.config.ts already governs this directory, so the skills belong at the repository root. Pass --skills with your agents to sync them here anyway.")],
	diagnostics: []
};
const SKIPPED_POSTINSTALL = {
	report: {
		outcome: "skipped",
		script: null,
		dependency: "skipped"
	},
	lines: [summary("info", "Skipped the postinstall hook (--no-postinstall).")],
	diagnostics: []
};
const SKIPPED_SKILLS = {
	report: {
		outcome: "skipped",
		sync: null
	},
	lines: [summary("info", "Skipped the skills sync (--skills=none).")],
	diagnostics: []
};
const SKIP_SENTINEL = "none";
function invalidSkillsFlagError(problem) {
	return new CliStructuredError("CLI.INVALID_ARGUMENTS", problem, { nextActions: [{
		kind: "user-choice",
		label: `Pass --skills a comma-separated list of agents (${KNOWN_AGENTS.join(", ")}), or --skills=${SKIP_SENTINEL} to record that no agent skills are wanted.`
	}] });
}
/** `--skills`: absent defers to the config's agents (every known agent
*  when there is no config); `none` records the choice — the scaffold
*  gets `agents: []` and the sync is skipped; otherwise a
*  comma-separated list of agent names. */
function parseSkillsFlag(raw, configured) {
	if (raw === void 0) return {
		kind: "agents",
		agents: configured
	};
	const names = raw.split(",").map((name) => name.trim()).filter((name) => name !== "");
	if (names.includes(SKIP_SENTINEL)) return names.length === 1 ? { kind: "skip" } : {
		kind: "invalid",
		error: invalidSkillsFlagError(`--skills=${SKIP_SENTINEL} records that no agent skills are wanted, so it cannot be combined with agent names.`)
	};
	const agents = [];
	for (const name of names) {
		if (!isKnownAgent(name)) return {
			kind: "invalid",
			error: invalidSkillsFlagError(`--skills names '${name}', which this CLI does not know. The known agents are ${KNOWN_AGENTS.join(", ")}.`)
		};
		if (!agents.includes(name)) agents.push(name);
	}
	if (agents.length === 0) return {
		kind: "invalid",
		error: invalidSkillsFlagError("--skills was given no agent names.")
	};
	return {
		kind: "agents",
		agents
	};
}
async function postinstallStep(flag, deferToAncestor, cwd) {
	if (flag === false) return SKIPPED_POSTINSTALL;
	if (deferToAncestor && flag === void 0) return ANCESTOR_SKIPPED_POSTINSTALL;
	return addPostinstallHook(cwd);
}
function initPresentations(result, postinstall, config, skills) {
	return {
		json: () => result,
		next: () => postinstall.next ?? [],
		stdout: () => [],
		human: (ui) => {
			const skillsBlocks = result.skills.sync === null ? [] : syncPresentations(result.skills.sync).human(ui);
			return [
				...postinstall.lines ?? [],
				...config.lines ?? [],
				...skills.lines ?? skillsBlocks
			];
		}
	};
}
const initCommand = defineCommand({
	help: {
		summary: "Prepare this repository for Prisma: config file, dev dependency, AI-agent skills",
		description: "Runs locally and calls no platform API. Adds a postinstall script to package.json that keeps the Prisma agent skills in sync on every install, adds prisma to devDependencies at this CLI's exact version when no dependency field declares it, scaffolds a prisma.config.ts recording which agents to install skills for, then syncs the skills once now. Everything lands in the current directory; a prisma.config.ts or postinstall script that already exists is never edited. Rerunning is safe: each step reports what is already done. In a directory another prisma.config.ts already governs, init writes only the prisma.config.ts scaffold — the postinstall hook, the prisma dev dependency, and the skills sync belong at the repository root and are skipped unless --postinstall or --skills asks for them here.",
		examples: [
			"init",
			"init --skills=claude,cursor",
			"init --skills=none",
			"init --no-postinstall"
		]
	},
	needs: { config: skillsConfigSection },
	args: { flags: {
		postinstall: flag.optionalBoolean({ brief: "Add the skills-sync postinstall hook (--no-postinstall skips)" }),
		skills: flag.string({
			brief: `Agents to install skills for (comma-separated: ${KNOWN_AGENTS.join(", ")}); '${SKIP_SENTINEL}' records that no agent skills are wanted`,
			placeholder: "agents"
		})
	} },
	handler: async (args, ctx) => {
		const skillsFlag = parseSkillsFlag(args.flags.skills, ctx.config.agents);
		if (skillsFlag.kind === "invalid") return notOk(skillsFlag.error);
		const deferToAncestor = governedByAncestorConfig(ctx.configFiles, ctx.cwd);
		const postinstall = await postinstallStep(args.flags.postinstall, deferToAncestor, ctx.cwd);
		const config = await scaffoldConfigStep(ctx.cwd, skillsFlag.kind === "skip" ? [] : skillsFlag.agents, ctx.config.agentsConfigured);
		let skills;
		if (skillsFlag.kind === "skip") skills = SKIPPED_SKILLS;
		else if (deferToAncestor && args.flags.skills === void 0) skills = ANCESTOR_SKIPPED_SKILLS;
		else skills = await syncSkillsStep(ctx.cwd, skillsFlag.agents, ctx.config.check);
		const result = {
			postinstall: postinstall.report,
			config: config.report,
			skills: skills.report
		};
		return ok(ctx.present({
			data: result,
			diagnostics: [
				...postinstall.diagnostics,
				...config.diagnostics,
				...skills.diagnostics
			]
		}, initPresentations(result, postinstall, config, skills)));
	}
});
//#endregion
//#region ../cli/src/controllers/database.ts
const USAGE_DATE_ONLY_PATTERN = /^\d{4}-\d{2}-\d{2}$/;
const USAGE_DATETIME_PATTERN = /^\d{4}-\d{2}-\d{2}T/;
const LIST_DATABASES_COMMAND = `${CLI_NAME} postgres list`;
/** The corrected `postgres usage` form both period errors point at. */
const USAGE_PERIOD_EXAMPLE_COMMAND = `${CLI_NAME} postgres usage <database> --from 2026-06-01 --to 2026-06-30`;
function userChoice$2(label) {
	return {
		kind: "user-choice",
		label
	};
}
function runCommand$2(command) {
	return {
		kind: "run-command",
		label: command,
		command
	};
}
function parseUsageDate(value, flagName, dayBoundary) {
	if (value === void 0) return;
	const trimmed = value.trim();
	if (USAGE_DATE_ONLY_PATTERN.test(trimmed) && isValidCalendarDate(trimmed)) return dayBoundary === "start" ? `${trimmed}T00:00:00.000Z` : `${trimmed}T23:59:59.999Z`;
	if (USAGE_DATETIME_PATTERN.test(trimmed) && !Number.isNaN(Date.parse(trimmed)) && isValidCalendarDate(trimmed.slice(0, 10))) return trimmed;
	throw new CliStructuredError("POSTGRES.USAGE_ERROR", "Invalid usage period", {
		why: `${flagName} must be an ISO date such as 2026-06-01 or an ISO datetime such as 2026-06-01T12:00:00Z.`,
		nextActions: [userChoice$2(`Pass an ISO date or datetime to ${flagName}.`), runCommand$2(USAGE_PERIOD_EXAMPLE_COMMAND)]
	});
}
function isValidCalendarDate(datePart) {
	const timestamp = Date.parse(`${datePart}T00:00:00.000Z`);
	return !Number.isNaN(timestamp) && new Date(timestamp).toISOString().startsWith(datePart);
}
function parseBackupLimit(value) {
	if (value === void 0) return;
	const limit = Number(value.trim());
	if (!Number.isInteger(limit) || limit < 1 || limit > 100) throw new CliStructuredError("POSTGRES.USAGE_ERROR", "Invalid backup limit", {
		why: "--limit must be an integer between 1 and 100.",
		nextActions: [userChoice$2("Pass a --limit between 1 and 100."), runCommand$2(`${CLI_NAME} postgres backup list <database> --limit 50`)]
	});
	return limit;
}
async function resolveDatabase(provider, target, databaseRef, branchName, signal) {
	const ref = databaseRef.trim();
	if (!ref) throw new CliStructuredError("POSTGRES.USAGE_ERROR", "Database id or name required", {
		why: "This command needs a database id or name.",
		nextActions: [userChoice$2("Pass a database id or name."), runCommand$2(LIST_DATABASES_COMMAND)]
	});
	const databases = await provider.listDatabases({
		projectId: target.project.id,
		branchName,
		signal
	});
	const byId = databases.filter((database) => database.id === ref);
	const matches = byId.length > 0 ? byId : databases.filter((database) => database.name === ref);
	if (matches.length === 0) throw databaseNotFoundError(ref, target.project.name, branchName);
	if (matches.length > 1) throw databaseAmbiguousError(ref, matches, branchName);
	const selected = matches[0];
	const shown = await provider.showDatabase(selected.id, {
		projectId: target.project.id,
		signal
	});
	if (shown === null) throw databaseRemovedDuringResolutionError(selected, target.project.name);
	return ensureProjectId(shown, target.project.id);
}
function databaseRemovedDuringResolutionError(database, projectName) {
	return new CliStructuredError("POSTGRES.NOT_FOUND", "Database not found", {
		why: `"${database.name}" (${database.id}) was listed for project "${projectName}", but reading it returned 404. It was most likely removed while this command was running.`,
		nextActions: [userChoice$2("Re-run the command, or list the project's databases to see what is there now."), runCommand$2(LIST_DATABASES_COMMAND)]
	});
}
function ensureProjectId(database, projectId) {
	return database.projectId ? database : {
		...database,
		projectId
	};
}
function sortDatabases(databases) {
	return databases.slice().sort((left, right) => {
		const branchOrder = (left.branchName ?? "").localeCompare(right.branchName ?? "");
		if (branchOrder !== 0) return branchOrder;
		const nameOrder = left.name.localeCompare(right.name);
		return nameOrder !== 0 ? nameOrder : left.id.localeCompare(right.id);
	});
}
function defaultConnectionName() {
	return `cli-${(/* @__PURE__ */ new Date()).toISOString().replace(/[-:.TZ]/g, "").slice(0, 17)}-${randomBytes(2).toString("hex")}`;
}
function databaseNotFoundError(databaseRef, projectName, branchName) {
	return new CliStructuredError("POSTGRES.NOT_FOUND", "Database not found", {
		why: `No database matched "${databaseRef}"${projectName ? ` in project "${projectName}"${branchName ? ` on branch "${branchName}"` : ""}` : ""}.`,
		nextActions: [userChoice$2(`Pass a database id or name from ${LIST_DATABASES_COMMAND}.`), runCommand$2(LIST_DATABASES_COMMAND)]
	});
}
function databaseAmbiguousError(databaseRef, matches, branchName) {
	return new CliStructuredError("POSTGRES.AMBIGUOUS", "Database resolution is ambiguous", {
		why: branchName ? `Multiple databases matched "${databaseRef}" on branch "${branchName}".` : `Multiple databases matched "${databaseRef}".`,
		meta: { matches: matches.map((database) => ({
			id: database.id,
			name: database.name,
			branchName: database.branchName
		})) },
		nextActions: [userChoice$2("Pass the database id, or pass --branch <git-name> to narrow the match."), runCommand$2(LIST_DATABASES_COMMAND)]
	});
}
//#endregion
//#region ../cli/src/presenters/database.ts
function serializeDatabaseList(result) {
	return {
		...serializeList({
			context: {
				project: result.projectName,
				...result.branchName ? { branch: result.branchName } : {}
			},
			items: result.databases.map((database) => ({
				noun: "database",
				label: database.name,
				id: database.id,
				status: database.isDefault ? "default" : null
			}))
		}),
		projectId: result.projectId,
		branchName: result.branchName,
		databases: result.databases
	};
}
function serializeDatabaseConnectionList(result) {
	return {
		...serializeList({
			context: {
				project: result.projectName,
				database: result.database.name
			},
			items: result.connections.map((connection) => ({
				noun: "connection",
				label: connection.name,
				id: connection.id,
				status: null
			}))
		}),
		projectId: result.projectId,
		database: result.database,
		connections: result.connections
	};
}
function serializeDatabaseBackupList(result) {
	return {
		...serializeList({
			context: {
				project: result.projectName,
				database: result.database.name
			},
			items: result.backups.map((backup) => ({
				noun: "backup",
				label: backup.id,
				id: backup.id,
				status: null
			}))
		}),
		projectId: result.projectId,
		database: result.database,
		backups: result.backups,
		retentionDays: result.retentionDays,
		hasMore: result.hasMore
	};
}
//#endregion
//#region ../cli/src/lib/database/provider.ts
const SUBSCRIPTION_LOOKUP_TIMEOUT_MS = 3e3;
const VERBOSE_LOG_FIX = "Re-run with --log-level verbose for the underlying API response details.";
function userChoice$1(label) {
	return {
		kind: "user-choice",
		label
	};
}
function runCommand$1(command) {
	return {
		kind: "run-command",
		label: command,
		command
	};
}
function createManagementDatabaseProvider(client, options) {
	const toDatabaseApiError = (summary, response, error, signal) => databaseApiError({
		client,
		workspaceId: options?.workspaceId,
		summary,
		response,
		error,
		signal
	});
	return {
		async listDatabases(options) {
			const databases = [];
			let cursor;
			while (true) {
				const result = await client.GET("/v1/databases", {
					params: { query: {
						projectId: options.projectId,
						branchGitName: options.branchName,
						cursor
					} },
					signal: options.signal
				});
				if (result.error || !result.data) throw await toDatabaseApiError("Failed to list databases", result.response, result.error, options.signal);
				databases.push(...result.data.data);
				if (!result.data.pagination.hasMore || !result.data.pagination.nextCursor) break;
				cursor = result.data.pagination.nextCursor;
			}
			return databases.map((database) => normalizeDatabase(database, options.projectId));
		},
		async showDatabase(databaseId, options) {
			const result = await client.GET("/v1/databases/{databaseId}", {
				params: { path: { databaseId } },
				signal: options?.signal
			});
			if (result.response?.status === 404 && !isPlanLimitApiError(result.error)) return null;
			if (result.error || !result.data) throw await toDatabaseApiError("Failed to show database", result.response, result.error, options?.signal);
			const database = result.data.data;
			return normalizeDatabase(database, requireDatabaseProjectId(database, options?.projectId));
		},
		async createDatabase(options) {
			const result = await client.POST("/v1/databases", {
				body: {
					projectId: options.projectId,
					name: options.name,
					source: { type: "empty" },
					...options.branchName ? { branchGitName: options.branchName } : {},
					...options.region ? { region: options.region } : {}
				},
				signal: options.signal
			});
			if (result.error || !result.data) throw await toDatabaseApiError("Failed to create database", result.response, result.error, options.signal);
			return normalizeCreatedDatabase(result.data.data, options.projectId);
		},
		async removeDatabase(databaseId, options) {
			const result = await client.DELETE("/v1/databases/{databaseId}", {
				params: { path: { databaseId } },
				signal: options?.signal
			});
			if (result.error) throw await toDatabaseApiError("Failed to delete database", result.response, result.error, options?.signal);
		},
		async listConnections(databaseId, options) {
			const result = await client.GET("/v1/databases/{databaseId}/connections", {
				params: { path: { databaseId } },
				signal: options?.signal
			});
			if (result.error || !result.data) throw await toDatabaseApiError("Failed to list database connections", result.response, result.error, options?.signal);
			return result.data.data.map((connection) => normalizeConnection(connection, databaseId));
		},
		async createConnection(options) {
			const result = await client.POST("/v1/databases/{databaseId}/connections", {
				params: { path: { databaseId: options.databaseId } },
				body: { name: options.name },
				signal: options.signal
			});
			if (result.error || !result.data) throw await toDatabaseApiError("Failed to create database connection", result.response, result.error, options.signal);
			return normalizeCreatedConnection(result.data.data, options.databaseId);
		},
		async removeConnection(connectionId, options) {
			const result = await client.DELETE("/v1/connections/{id}", {
				params: { path: { id: connectionId } },
				signal: options?.signal
			});
			if (result.error) throw await toDatabaseApiError("Failed to delete database connection", result.response, result.error, options?.signal);
		},
		async getUsage(databaseId, options) {
			const result = await client.GET("/v1/databases/{databaseId}/usage", {
				params: {
					path: { databaseId },
					query: {
						...options?.from ? { startDate: options.from } : {},
						...options?.to ? { endDate: options.to } : {}
					}
				},
				signal: options?.signal
			});
			if (result.error || !result.data) throw await toDatabaseApiError("Failed to fetch database usage", result.response, result.error, options?.signal);
			return normalizeUsage(result.data);
		},
		async listBackups(databaseId, options) {
			const result = await client.GET("/v1/databases/{databaseId}/backups", {
				params: {
					path: { databaseId },
					query: { ...options?.limit !== void 0 ? { limit: options.limit } : {} }
				},
				signal: options?.signal
			});
			if (result.response?.status === 422 && !isPlanLimitApiError(result.error)) throw backupsUnsupportedError(databaseId, result.error);
			if (result.error || !result.data) throw await toDatabaseApiError("Failed to list database backups", result.response, result.error, options?.signal);
			return normalizeBackupList(result.data);
		},
		async restoreDatabase(options) {
			const result = await client.POST("/v1/databases/{targetDatabaseId}/restore", {
				params: { path: { targetDatabaseId: options.targetDatabaseId } },
				body: { source: {
					type: "backup",
					databaseId: options.sourceDatabaseId,
					backupId: options.backupId
				} },
				signal: options.signal
			});
			if (result.response?.status === 409 && !isPlanLimitApiError(result.error)) throw restoreConflictError(options.targetDatabaseId, result.error);
			if (result.response?.status === 404 && !isPlanLimitApiError(result.error)) throw restoreBackupNotFoundError(options, result.error);
			if (result.error || !result.data) throw await toDatabaseApiError("Failed to restore database", result.response, result.error, options.signal);
			return normalizeDatabase(result.data.data, options.projectId);
		},
		async rotateConnection(connectionId, options) {
			const result = await client.POST("/v1/connections/{id}/rotate", {
				params: { path: { id: connectionId } },
				signal: options?.signal
			});
			if (result.error || !result.data) throw await toDatabaseApiError("Failed to rotate database connection", result.response, result.error, options?.signal);
			return normalizeRotatedConnection(result.data.data);
		}
	};
}
function normalizeDatabase(database, fallbackProjectId) {
	return {
		id: database.id,
		name: database.name,
		projectId: database.projectId ?? fallbackProjectId,
		branchId: database.branchId ?? database.branch?.id ?? null,
		branchName: database.branchGitName ?? database.branchName ?? database.branch?.gitName ?? database.branch?.name ?? null,
		region: normalizeRegion(database),
		status: database.status ?? null,
		isDefault: database.isDefault ?? null,
		createdAt: database.createdAt ?? null
	};
}
function normalizeConnection(connection, fallbackDatabaseId) {
	return {
		id: connection.id,
		name: connection.name ?? connection.id,
		databaseId: connection.databaseId ?? fallbackDatabaseId,
		createdAt: connection.createdAt ?? null
	};
}
function normalizeCreatedDatabase(database, fallbackProjectId) {
	const rawConnection = database.connections?.[0];
	if (!rawConnection) throw new CliStructuredError("POSTGRES.CONNECTION_MISSING", "Created database did not return a connection string", {
		why: "The Management API created the database but did not include the one-time connection payload.",
		nextActions: [userChoice$1(`Create a connection explicitly with ${CLI_NAME} postgres connection create <database>.`), runCommand$1(`${CLI_NAME} postgres connection create ${database.id}`)]
	});
	return {
		database: normalizeDatabase(database, fallbackProjectId),
		...normalizeCreatedConnection(rawConnection, database.id)
	};
}
function normalizeCreatedConnection(connection, fallbackDatabaseId) {
	const connectionString = extractConnectionString(connection);
	if (!connectionString) throw new CliStructuredError("POSTGRES.CONNECTION_STRING_MISSING", "Created connection did not return a connection string", {
		why: "Database connection strings are one-time-view secrets, but the Management API did not include one in this create response.",
		nextActions: [userChoice$1("Create another database connection and store the returned URL immediately."), runCommand$1(`${CLI_NAME} postgres connection create ${fallbackDatabaseId}`)]
	});
	return {
		connection: normalizeConnection(connection, fallbackDatabaseId),
		connectionString
	};
}
function normalizeRegion(database) {
	if (typeof database.region === "string") return database.region;
	return database.region?.id ?? database.regionId ?? null;
}
function requireDatabaseProjectId(database, fallbackProjectId) {
	const projectId = database.projectId ?? fallbackProjectId;
	if (projectId) return projectId;
	throw new CliStructuredError("POSTGRES.API_ERROR", "Database response did not include a project id", {
		why: "The Management API returned database metadata without project context.",
		nextActions: [userChoice$1(VERBOSE_LOG_FIX)]
	});
}
function extractConnectionString(connection) {
	return connection.endpoints?.pooled?.connectionString ?? connection.connectionString ?? connection.endpoints?.direct?.connectionString ?? connection.endpoints?.accelerate?.connectionString ?? null;
}
/** Absence is carried, not filled in. A `0` here reached the card, the
*  stdout lane and the json record as though the API had measured it, and
*  "you used nothing" is the answer a user acts on. The unit is the same
*  problem an order of magnitude worse: `GiB` was a guess, and a value the
*  API denominates in bytes would print against it unchanged. */
function normalizeUsageMetric(metric) {
	return {
		used: metric?.used ?? null,
		unit: metric?.unit ?? null
	};
}
function normalizeUsage(usage) {
	return {
		period: {
			start: usage.period?.start ?? null,
			end: usage.period?.end ?? null
		},
		metrics: {
			operations: normalizeUsageMetric(usage.metrics?.operations),
			storage: normalizeUsageMetric(usage.metrics?.storage)
		},
		generatedAt: usage.generatedAt ?? null
	};
}
function normalizeBackupList(body) {
	return {
		backups: (body.data ?? []).map((backup) => ({
			id: backup.id,
			backupType: backup.backupType ?? "",
			status: backup.status ?? "",
			size: backup.size ?? null,
			createdAt: backup.createdAt ?? ""
		})),
		retentionDays: body.meta?.backupRetentionDays ?? null,
		hasMore: body.pagination?.hasMore ?? false
	};
}
function normalizeRotatedConnection(connection) {
	const connectionString = extractConnectionString(connection);
	if (!connectionString) throw new CliStructuredError("POSTGRES.CONNECTION_STRING_MISSING", "Rotated connection did not return a connection string", {
		why: "Rotated connection strings are one-time-view secrets, but the Management API did not include one in this rotate response.",
		nextActions: [userChoice$1("Re-run the rotation, or create a replacement connection and store the returned URL immediately.")]
	});
	const database = connection.database?.id && connection.database?.name ? {
		id: connection.database.id,
		name: connection.database.name
	} : null;
	return {
		connection: normalizeConnection(connection, connection.database?.id ?? connection.databaseId ?? ""),
		database,
		connectionString
	};
}
function backupsUnsupportedError(databaseId, error) {
	return new CliStructuredError("POSTGRES.BACKUPS_UNSUPPORTED", "Backups are not available for this database", {
		why: error?.error?.message ?? `The platform does not manage backups for database "${databaseId}", for example because it is a remote/BYO database.`,
		nextActions: [userChoice$1("Use your own backup tooling for externally managed databases.")]
	});
}
function restoreBackupNotFoundError(options, error) {
	const listCommand = `${CLI_NAME} postgres backup list ${options.sourceDatabaseId}`;
	return new CliStructuredError("POSTGRES.BACKUP_NOT_FOUND", "Database backup not found", {
		why: error?.error?.message ?? `No backup matched "${options.backupId}" for database "${options.sourceDatabaseId}".`,
		nextActions: [userChoice$1(`Pass a backup id from ${listCommand}.`), runCommand$1(listCommand)]
	});
}
function restoreConflictError(targetDatabaseId, error) {
	return new CliStructuredError("POSTGRES.RESTORE_CONFLICT", "Database cannot be restored right now", {
		why: error?.error?.message ?? `Database "${targetDatabaseId}" is provisioning or already recovering.`,
		nextActions: [userChoice$1("Wait for the database to become ready, then retry the restore."), runCommand$1(`${CLI_NAME} postgres show ${targetDatabaseId}`)]
	});
}
/** A 401 or 403 is the API refusing the caller, not a database problem. */
function isRejectedCaller(status) {
	return status === 401 || status === 403;
}
function apiErrorWhy(status, message) {
	if (!isRejectedCaller(status)) return message ?? `The Management API returned status ${status || "unknown"}.`;
	const rejection = `The Management API rejected the request as ${status === 401 ? "unauthorized" : "forbidden"}.`;
	return message ? `${rejection} ${message}` : rejection;
}
function apiErrorMeta(status, apiCode) {
	if (!status && apiCode === void 0) return;
	return {
		...status ? { status } : {},
		...apiCode === void 0 ? {} : { apiCode }
	};
}
function apiErrorActions(status, hint) {
	if (!isRejectedCaller(status)) return [userChoice$1(hint ?? VERBOSE_LOG_FIX)];
	return [userChoice$1(hint ?? `Sign in again with prisma auth login, then retry the command.`), runCommand$1(`${CLI_NAME} auth login`)];
}
/**
* Every database Management API failure that is not a plan limit lands
* on the one registered code. The response's own error code is data, not
* an identity: it travels in `meta.apiCode` beside `meta.status` so a
* consumer can still branch on it without the CLI minting a code it
* never registered.
*/
async function databaseApiError(options) {
	if (isPlanLimitApiError(options.error)) return planLimitReachedError(options);
	const status = options.response?.status ?? 0;
	const meta = apiErrorMeta(status, options.error?.error?.code);
	return new CliStructuredError("POSTGRES.API_ERROR", options.summary, {
		why: apiErrorWhy(status, options.error?.error?.message),
		...meta === void 0 ? {} : { meta },
		nextActions: apiErrorActions(status, options.error?.error?.hint)
	});
}
async function planLimitReachedError(options) {
	const subscription = options.workspaceId ? await readWorkspaceSubscription(options.client, options.workspaceId, options.signal) : null;
	const planName = subscription?.planName || null;
	const usageBlocked = subscription?.usageBlocked ?? null;
	const upgradeUrl = subscription?.upgradeUrl || null;
	return new CliStructuredError("POSTGRES.PLAN_LIMIT_REACHED", "Workspace plan limit reached", {
		why: "Database operations are blocked because this workspace has used the operations included in its plan. This is a workspace plan limit, not a Prisma outage.",
		meta: {
			workspaceId: options.workspaceId ?? null,
			blockedFeature: null,
			planName,
			usageBlocked,
			upgradeUrl
		},
		nextActions: [{
			kind: "user-choice",
			label: "Upgrade the workspace plan",
			reason: upgradeUrl ? `Upgrade at ${upgradeUrl}${planName ? ` (current plan: ${planName})` : ""}.` : "Open Prisma Console and upgrade the affected workspace plan."
		}]
	});
}
function isPlanLimitApiError(error) {
	return error?.error?.code === "planLimitReached";
}
async function readWorkspaceSubscription(client, workspaceId, signal) {
	signal?.throwIfAborted();
	const timeoutController = new AbortController();
	const timeout = setTimeout(() => timeoutController.abort(), SUBSCRIPTION_LOOKUP_TIMEOUT_MS);
	const requestSignal = signal ? AbortSignal.any([signal, timeoutController.signal]) : timeoutController.signal;
	try {
		const result = await client.GET("/v1/workspaces/{id}/subscription", {
			params: { path: { id: workspaceId } },
			signal: requestSignal
		});
		signal?.throwIfAborted();
		if (result.error) return null;
		return result.data?.data ?? null;
	} catch {
		signal?.throwIfAborted();
		return null;
	} finally {
		clearTimeout(timeout);
	}
}
//#endregion
//#region ../cli/src/commands/postgres/context.ts
/** Workspace, project and provider for the `postgres *` commands. */
const projectFlag$1 = flag.string({
	brief: "Project id or name (default: the project this directory is linked to)",
	placeholder: "id-or-name"
});
const branchFlag$1 = flag.string({
	brief: "Branch git name; use to target one branch's environment",
	placeholder: "git-name"
});
const databasePositional = positional.string({
	brief: "Database id or name",
	placeholder: "database"
});
async function resolvePostgresContext(ctx, flags, commandName) {
	const workspace = await resolveActiveWorkspace(ctx);
	const target = await resolvePinnedProject(ctx, workspace, flags.project, commandName);
	return {
		provider: createManagementDatabaseProvider(ctx.api, { workspaceId: workspace.id }),
		target,
		projectId: target.project.id,
		projectName: target.project.name
	};
}
/** `connection rotate` and `connection delete` address a connection
*  directly: no workspace requirement and no project resolution, so
*  the workspace is only a plan-limit lookup hint. */
async function resolvePostgresProviderOnly(ctx) {
	const workspaceId = (await ctx.activeCredential())?.workspaceId;
	return createManagementDatabaseProvider(ctx.api, { ...workspaceId === void 0 ? {} : { workspaceId } });
}
//#endregion
//#region ../cli/src/commands/postgres/presentation.ts
/** Legacy `formatDatabaseTarget`. */
function postgresTargetLabel(projectName, branchName) {
	return branchName ? `${projectName} / ${branchName}` : projectName;
}
/** The human status cell: the database's own status, or the word for a
*  status the API did not report. Whether the database is the project's
*  default is a different fact and never stands in for this one — a
*  reader could not tell that substitution from a real status, and a
*  stopped database would have read as healthy. */
function formatStatus(database) {
	return database.status ?? "unknown";
}
/** The reader's branch cell. "unscoped" is a claim about the database —
*  that it belongs to no branch — so only an absent `branchId` may
*  produce it. The API does not always send a branch name beside the id
*  it does send, and reporting that database as unscoped was wrong. */
function branchLabel(database) {
	return database.branchName ?? database.branchId ?? "unscoped";
}
/** The stdout status cell. The Option A channel ruling makes stdout the
*  machine-usable payload, so it carries the raw status and nothing
*  else: an absent status is an empty field, and `isDefault` is a
*  different fact that does not belong in this one. */
function statusValue(database) {
	return database.status ?? "";
}
/** The human usage cell: the measurement with the unit the API gave it.
*  A metric the API did not report reads "unknown" rather than `0`, and a
*  measurement with no unit prints alone rather than against a unit the
*  CLI picked. */
function formatUsageMetric(metric) {
	if (metric.used === null) return "unknown";
	return metric.unit ? `${metric.used} ${metric.unit}` : String(metric.used);
}
/** The stdout usage cell: the number the API measured, or an empty field
*  when it measured none. */
function usageMetricValue(metric) {
	return metric.used === null ? "" : String(metric.used);
}
/** The human size cell. `formatBackupSize` is for reading; stdout gets
*  the byte count through `backupStdoutRows`, because "2.0 KiB" will
*  not parse back to 2048. */
function formatBackupSize(size) {
	if (size === null) return "unknown";
	if (size < 1024) return `${size} B`;
	if (size < 1024 * 1024) return `${(size / 1024).toFixed(1)} KiB`;
	if (size < 1024 * 1024 * 1024) return `${(size / (1024 * 1024)).toFixed(1)} MiB`;
	return `${(size / (1024 * 1024 * 1024)).toFixed(1)} GiB`;
}
function backupRows(backups) {
	return backups.map((backup) => [
		backup.id,
		backup.backupType || "unknown",
		backup.status || "unknown",
		formatBackupSize(backup.size),
		backup.createdAt || "unknown"
	]);
}
function backupStdoutRows(backups) {
	return backups.map((backup) => [
		backup.id,
		backup.backupType,
		backup.status,
		backup.size === null ? "" : String(backup.size),
		backup.createdAt || ""
	]);
}
/** The one-time-secret card. The URL prints bare: this card is the only
*  place an interactive user ever sees it. */
function secretBlocks(headline, connectionString) {
	return [
		{
			kind: "summary",
			status: "ok",
			text: headline
		},
		{
			kind: "list",
			items: ["The connection URL below is shown once, so save it now."]
		},
		{
			kind: "fields",
			rows: [{
				label: "connection URL",
				value: connectionString
			}]
		}
	];
}
//#endregion
//#region ../cli/src/commands/postgres/backup-list.ts
/** The `postgres backup list` command. */
const TITLE$9 = "Listing platform-created database backups.";
function backupListPresentations(result) {
	const rows = backupRows(result.backups);
	const stdoutRows = backupStdoutRows(result.backups);
	return {
		next: () => [],
		human: () => [
			{
				kind: "summary",
				status: "info",
				text: TITLE$9
			},
			{
				kind: "fields",
				rows: [{
					label: "database",
					value: result.database.name
				}, ...result.retentionDays !== null ? [{
					label: "retention",
					value: `${result.retentionDays} days`
				}] : []]
			},
			...rows.length === 0 ? [{
				kind: "summary",
				status: "info",
				text: "No backups found."
			}] : [{
				kind: "table",
				columns: [
					"Id",
					"Type",
					"Status",
					"Size",
					"Created"
				],
				rows
			}],
			...result.hasMore ? [{
				kind: "list",
				items: ["More backups exist; raise --limit to see them."]
			}] : []
		],
		stdout: () => stdoutRows.map((row) => row.join("	")),
		json: () => serializeDatabaseBackupList(result)
	};
}
const postgresBackupListCommand = defineCommand({
	args: {
		positionals: { database: databasePositional },
		flags: {
			limit: flag.string({
				brief: "Maximum number of backups to return",
				placeholder: "n"
			}),
			project: projectFlag$1,
			branch: branchFlag$1
		}
	},
	help: {
		summary: "List backups for a database",
		description: "Backups are created automatically by the platform. Use the listed backup ids with 'postgres backup restore'.",
		examples: ["postgres backup list db_123", "postgres backup list acme-production --limit 50"]
	},
	needs: { credentials: true },
	handler: async (args, ctx) => {
		const limit = parseBackupLimit(args.flags.limit);
		const { provider, target, projectId, projectName } = await resolvePostgresContext(ctx, args.flags, "postgres backup list");
		const database = await resolveDatabase(provider, target, args.positionals.database, args.flags.branch, ctx.signal);
		const backups = await provider.listBackups(database.id, {
			limit,
			signal: ctx.signal
		});
		const result = {
			projectId,
			projectName,
			database,
			backups: backups.backups,
			retentionDays: backups.retentionDays,
			hasMore: backups.hasMore
		};
		return ok(ctx.present({ data: result }, backupListPresentations(result)));
	}
});
//#endregion
//#region ../cli/src/commands/postgres/backup-restore.ts
/** The `postgres backup restore` command. */
const CONSENT_QUESTION$5 = "Restoring immediately and irreversibly overwrites all data in the target database, so it requires the exact target database id.";
function restorePresentations(result, sourceDatabaseId, targetDatabaseId) {
	const rows = [
		{
			label: "project",
			value: result.projectName
		},
		{
			label: "database",
			value: result.database.name
		},
		{
			label: "id",
			value: result.database.id
		},
		{
			label: "backup",
			value: result.source.backupId
		},
		...sourceDatabaseId ? [{
			label: "source",
			value: sourceDatabaseId
		}] : []
	];
	return {
		stdout: () => [],
		json: () => result,
		human: () => [
			{
				kind: "summary",
				status: "ok",
				text: "Restoring database from backup."
			},
			{
				kind: "fields",
				rows
			},
			{
				kind: "list",
				items: [`The restore is running; the database status is "${result.database.status ?? "recovering"}" until it completes.`, "Connections and credentials are preserved."]
			}
		],
		next: () => [{
			kind: "run-command",
			label: `${CLI_NAME} postgres show ${targetDatabaseId}`,
			command: `${CLI_NAME} postgres show ${targetDatabaseId}`
		}]
	};
}
const postgresBackupRestoreCommand = defineCommand({
	args: {
		positionals: { database: positional.string({
			brief: "Target database id or name",
			placeholder: "database"
		}) },
		flags: {
			backup: flag.string({
				brief: "Backup to restore from, listed by 'postgres backup list'",
				placeholder: "backup-id"
			}),
			sourceDatabase: flag.string({
				brief: "Database the backup belongs to; use when restoring another database's backup into the target",
				placeholder: "database"
			}),
			project: projectFlag$1,
			branch: branchFlag$1
		}
	},
	help: {
		summary: "Restore a database from a backup after exact id confirmation",
		description: "Replaces the target database's current state with the backup's contents; anything written since that backup is lost, so the exact target database id is the consent token. To restore another database's backup into this one, name where the backup came from with --source-database.",
		examples: ["postgres backup restore db_123 --backup bkp_456 --confirm db_123"]
	},
	needs: { credentials: true },
	handler: async (args, ctx) => {
		const backupId = args.flags.backup?.trim();
		if (!backupId) {
			const listCommand = `${CLI_NAME} postgres backup list <database>`;
			throw new CliStructuredError("POSTGRES.USAGE_ERROR", "Backup id required", {
				why: "Database restore needs the backup to restore from.",
				nextActions: [{
					kind: "user-choice",
					label: `Pass --backup <backup-id> from ${listCommand}.`
				}, {
					kind: "run-command",
					label: listCommand,
					command: listCommand
				}]
			});
		}
		const { provider, target, projectId, projectName } = await resolvePostgresContext(ctx, args.flags, "postgres backup restore");
		const database = await resolveDatabase(provider, target, args.positionals.database, args.flags.branch, ctx.signal);
		const sourceDatabase = args.flags.sourceDatabase ? await resolveDatabase(provider, target, args.flags.sourceDatabase, args.flags.branch, ctx.signal) : database;
		await ctx.prompt.consent(CONSENT_QUESTION$5, { token: database.id });
		const result = {
			projectId,
			projectName,
			database: await provider.restoreDatabase({
				targetDatabaseId: database.id,
				sourceDatabaseId: sourceDatabase.id,
				backupId,
				projectId,
				signal: ctx.signal
			}),
			source: {
				databaseId: sourceDatabase.id,
				backupId
			}
		};
		return ok(ctx.present({ data: result }, restorePresentations(result, sourceDatabase.id === database.id ? null : sourceDatabase.id, database.id)));
	}
});
//#endregion
//#region ../cli/src/commands/postgres/connection-create.ts
/** The `postgres connection create` command. */
const postgresConnectionCreateCommand = defineCommand({
	args: {
		positionals: { database: databasePositional },
		flags: {
			name: flag.string({
				brief: "Connection name; use it to record which consumer holds the credential",
				placeholder: "name"
			}),
			project: projectFlag$1,
			branch: branchFlag$1
		}
	},
	help: {
		summary: "Create an additional connection URL for a database; printed exactly once",
		description: "Mints an additional credential for an existing database, shown exactly once. Give each consumer (an app, CI, a teammate) its own named connection, so its access can later be rotated or revoked without touching the others.",
		examples: ["postgres connection create db_123", "postgres connection create db_123 --name readonly"]
	},
	needs: { credentials: true },
	handler: async (args, ctx) => {
		const { provider, target, projectId, projectName } = await resolvePostgresContext(ctx, args.flags, "postgres connection create");
		const database = await resolveDatabase(provider, target, args.positionals.database, args.flags.branch, ctx.signal);
		const created = await provider.createConnection({
			databaseId: database.id,
			name: args.flags.name?.trim() || defaultConnectionName(),
			signal: ctx.signal
		});
		const result = {
			projectId,
			projectName,
			database,
			connection: created.connection,
			connectionString: created.connectionString
		};
		return ok(ctx.present({ data: result }, {
			human: () => secretBlocks(`Added a connection to "${database.name}" in ${postgresTargetLabel(projectName, database.branchName)}.`, result.connectionString),
			stdout: () => [result.connectionString],
			json: () => result,
			next: () => []
		}));
	}
});
//#endregion
//#region ../cli/src/commands/postgres/connection-delete.ts
/** The `postgres connection delete` command. */
const CONSENT_QUESTION$4 = "Deleting this database connection is destructive and requires the exact id.";
const postgresConnectionDeleteCommand = defineCommand({
	args: { positionals: { connection: positional.string({
		brief: "Connection id",
		placeholder: "connection-id"
	}) } },
	help: {
		summary: "Delete a database connection after exact id confirmation",
		description: "Revokes the credential: anything still using its URL loses access to the database. The database itself and its other connections are untouched. The exact connection id is the consent token.",
		examples: ["postgres connection delete conn_123 --confirm conn_123"]
	},
	needs: { credentials: true },
	handler: async (args, ctx) => {
		const connectionId = args.positionals.connection.trim();
		if (!connectionId) {
			const example = `${CLI_NAME} postgres connection delete <connection-id> --confirm <connection-id>`;
			throw new CliStructuredError("POSTGRES.USAGE_ERROR", "Connection id required", {
				why: "Database connection deletion needs a connection id.",
				nextActions: [{
					kind: "user-choice",
					label: "Pass the connection id to delete."
				}, {
					kind: "run-command",
					label: example,
					command: example
				}]
			});
		}
		await ctx.prompt.consent(CONSENT_QUESTION$4, { token: connectionId });
		await (await resolvePostgresProviderOnly(ctx)).removeConnection(connectionId, { signal: ctx.signal });
		const result = { connection: { id: connectionId } };
		return ok(ctx.present({ data: result }, {
			human: () => [
				{
					kind: "summary",
					status: "ok",
					text: "Deleting database connection."
				},
				{
					kind: "fields",
					rows: [{
						label: "connection",
						value: connectionId
					}]
				},
				{
					kind: "list",
					items: ["The connection metadata was deleted. Existing one-time secrets were not shown."]
				}
			],
			stdout: () => [],
			json: () => ({ connection: result.connection }),
			next: () => []
		}));
	}
});
//#endregion
//#region ../cli/src/commands/postgres/connection-list.ts
/** The `postgres connection list` command. */
const TITLE$8 = "Listing database connection metadata.";
function connectionRows(result) {
	return result.connections.map((connection) => [
		connection.name,
		connection.id,
		connection.createdAt ?? "unknown"
	]);
}
/** The stdout rows: an absent creation time is an empty field. */
function connectionStdoutRows(result) {
	return result.connections.map((connection) => [
		connection.name,
		connection.id,
		connection.createdAt ?? ""
	]);
}
function listPresentations$4(result) {
	const rows = connectionRows(result);
	const stdoutRows = connectionStdoutRows(result);
	return {
		next: () => [],
		human: () => [
			{
				kind: "summary",
				status: "info",
				text: TITLE$8
			},
			{
				kind: "fields",
				rows: [{
					label: "database",
					value: result.database.name
				}]
			},
			...rows.length === 0 ? [{
				kind: "summary",
				status: "info",
				text: "No database connections found."
			}] : [{
				kind: "table",
				columns: [
					"Name",
					"Id",
					"Created"
				],
				rows
			}]
		],
		stdout: () => stdoutRows.map((row) => row.join("	")),
		json: () => serializeDatabaseConnectionList(result)
	};
}
const postgresConnectionListCommand = defineCommand({
	args: {
		positionals: { database: databasePositional },
		flags: {
			project: projectFlag$1,
			branch: branchFlag$1
		}
	},
	help: {
		summary: "List database connection metadata without secret values",
		description: "Each connection is an independent credential for the same database, so one consumer's access can be rotated or revoked without breaking the others. Listing shows metadata only; connection URLs print once, at create or rotate, and never again.",
		examples: ["postgres connection list db_123", "postgres connection list acme-preview --branch preview --json"]
	},
	needs: { credentials: true },
	handler: async (args, ctx) => {
		const { provider, target, projectId, projectName } = await resolvePostgresContext(ctx, args.flags, "postgres connection list");
		const database = await resolveDatabase(provider, target, args.positionals.database, args.flags.branch, ctx.signal);
		const result = {
			projectId,
			projectName,
			database,
			connections: await provider.listConnections(database.id, { signal: ctx.signal })
		};
		return ok(ctx.present({ data: result }, listPresentations$4(result)));
	}
});
//#endregion
//#region ../cli/src/commands/postgres/connection-rotate.ts
/** The `postgres connection rotate` command. */
const CONSENT_QUESTION$3 = "Rotating revokes the previous credentials and breaks clients still using them, so it requires the exact connection id.";
const postgresConnectionRotateCommand = defineCommand({
	args: { positionals: { connection: positional.string({
		brief: "Connection id",
		placeholder: "connection-id"
	}) } },
	help: {
		summary: "Rotate connection credentials and print the new one-time connection URL",
		description: "Replaces the connection's credentials: the old URL stops working and the new one prints exactly once. Use it after a suspected leak or on a rotation schedule. The exact connection id is the consent token.",
		examples: ["postgres connection rotate conn_123 --confirm conn_123"]
	},
	needs: { credentials: true },
	handler: async (args, ctx) => {
		const connectionId = args.positionals.connection.trim();
		if (!connectionId) {
			const example = `${CLI_NAME} postgres connection rotate <connection-id> --confirm <connection-id>`;
			throw new CliStructuredError("POSTGRES.USAGE_ERROR", "Connection id required", {
				why: "Database connection rotation needs a connection id.",
				nextActions: [{
					kind: "user-choice",
					label: "Pass the connection id to rotate."
				}, {
					kind: "run-command",
					label: example,
					command: example
				}]
			});
		}
		await ctx.prompt.consent(CONSENT_QUESTION$3, { token: connectionId });
		const rotated = await (await resolvePostgresProviderOnly(ctx)).rotateConnection(connectionId, { signal: ctx.signal });
		const result = {
			connection: rotated.connection,
			database: rotated.database,
			connectionString: rotated.connectionString
		};
		const subject = result.database ? `"${result.database.name}"` : `connection ${result.connection.id}`;
		return ok(ctx.present({ data: result }, {
			human: () => secretBlocks(`Rotated credentials for ${subject}. The previous credentials no longer work.`, result.connectionString),
			stdout: () => [result.connectionString],
			json: () => result,
			next: () => []
		}));
	}
});
//#endregion
//#region ../cli/src/commands/postgres/create.ts
/** The `postgres create` command. */
const postgresCreateCommand = defineCommand({
	args: {
		positionals: { name: positional.string({
			brief: "Database name",
			placeholder: "name"
		}) },
		flags: {
			region: flag.string({
				brief: "Prisma Postgres region id; set it when the data must live near a location",
				placeholder: "region"
			}),
			project: projectFlag$1,
			branch: branchFlag$1
		}
	},
	help: {
		summary: "Create a Prisma Postgres database and print its one-time connection URL",
		description: "Creates a database in a Branch of the project and prints its connection URL exactly once; store it now, nothing prints it again. Mint further URLs for other consumers with 'postgres connection create'.",
		examples: ["postgres create my-db", "postgres create my-db --branch feature/foo --region eu-central-1"]
	},
	needs: { credentials: true },
	handler: async (args, ctx) => {
		const name = args.positionals.name.trim();
		if (!name) {
			const example = `${CLI_NAME} postgres create <name>`;
			throw new CliStructuredError("POSTGRES.USAGE_ERROR", "Database name required", {
				why: "Database create needs a non-empty name.",
				nextActions: [{
					kind: "user-choice",
					label: "Pass a database name."
				}, {
					kind: "run-command",
					label: example,
					command: example
				}]
			});
		}
		const { provider, projectId, projectName } = await resolvePostgresContext(ctx, args.flags, "postgres create");
		const created = await provider.createDatabase({
			projectId,
			name,
			branchName: args.flags.branch,
			region: args.flags.region,
			signal: ctx.signal
		});
		const result = {
			projectId,
			projectName,
			database: ensureProjectId(created.database, projectId),
			connection: created.connection,
			connectionString: created.connectionString
		};
		return ok(ctx.present({ data: result }, {
			human: () => secretBlocks(`Created database "${result.database.name}" in ${postgresTargetLabel(projectName, result.database.branchName)}.`, result.connectionString),
			stdout: () => [result.connectionString],
			json: () => result,
			next: () => []
		}));
	}
});
//#endregion
//#region ../cli/src/commands/postgres/delete.ts
/** The `postgres delete` command. */
const CONSENT_QUESTION$2 = "Deleting this database is destructive and requires the exact id.";
function deletePresentations$3(result) {
	return {
		stdout: () => [],
		json: () => result,
		next: () => [],
		human: () => [
			{
				kind: "summary",
				status: "ok",
				text: "Deleting database."
			},
			{
				kind: "fields",
				rows: [
					{
						label: "project",
						value: result.projectName
					},
					{
						label: "database",
						value: result.database.name
					},
					{
						label: "id",
						value: result.database.id
					}
				]
			},
			{
				kind: "list",
				items: ["Database and its connection metadata were deleted."]
			}
		]
	};
}
const postgresDeleteCommand = defineCommand({
	args: {
		positionals: { database: databasePositional },
		flags: {
			project: projectFlag$1,
			branch: branchFlag$1
		}
	},
	help: {
		summary: "Delete a database after exact id confirmation",
		description: "Deletion is permanent and destroys the data. The exact database id is the consent token; pass it with --confirm to run non-interactively.",
		examples: ["postgres delete db_123 --confirm db_123"]
	},
	needs: { credentials: true },
	handler: async (args, ctx) => {
		const { provider, target, projectId, projectName } = await resolvePostgresContext(ctx, args.flags, "postgres delete");
		const database = await resolveDatabase(provider, target, args.positionals.database, args.flags.branch, ctx.signal);
		await ctx.prompt.consent(CONSENT_QUESTION$2, { token: database.id });
		await provider.removeDatabase(database.id, { signal: ctx.signal });
		const result = {
			projectId,
			projectName,
			database
		};
		return ok(ctx.present({ data: result }, deletePresentations$3(result)));
	}
});
//#endregion
//#region ../cli/src/commands/postgres/list.ts
/** The `postgres list` command. */
const TITLE$7 = "Listing databases for the resolved project.";
function databaseRows(result) {
	return result.databases.map((database) => [
		database.name,
		branchLabel(database),
		database.region ?? "unknown",
		formatStatus(database),
		database.id
	]);
}
/** The stdout rows carry the values, not the reader's placeholders:
*  an absent branch, region or status is an empty field. */
function databaseStdoutRows(result) {
	return result.databases.map((database) => [
		database.name,
		database.branchName ?? "",
		database.region ?? "",
		statusValue(database),
		database.id
	]);
}
function listPresentations$3(result) {
	const rows = databaseRows(result);
	const stdoutRows = databaseStdoutRows(result);
	return {
		next: () => [],
		human: () => [
			{
				kind: "summary",
				status: "info",
				text: TITLE$7
			},
			{
				kind: "fields",
				rows: [{
					label: "project",
					value: result.projectName
				}, ...result.branchName ? [{
					label: "branch",
					value: result.branchName
				}] : []]
			},
			...rows.length === 0 ? [{
				kind: "summary",
				status: "info",
				text: "No databases found."
			}] : [{
				kind: "table",
				columns: [
					"Name",
					"Branch",
					"Region",
					"Status",
					"Id"
				],
				rows
			}]
		],
		stdout: () => stdoutRows.map((row) => row.join("	")),
		json: () => serializeDatabaseList(result)
	};
}
const postgresListCommand = defineCommand({
	args: { flags: {
		project: projectFlag$1,
		branch: branchFlag$1
	} },
	help: {
		summary: "List a project's Prisma Postgres databases",
		description: "Databases are branch-bound: each belongs to one Branch, the isolated environment for one Git branch. Without --branch, this lists databases across every branch of the project.",
		examples: [
			"postgres list",
			"postgres list --branch feature/foo",
			"postgres list --json"
		]
	},
	needs: { credentials: true },
	handler: async (args, ctx) => {
		const { provider, projectId, projectName } = await resolvePostgresContext(ctx, args.flags, "postgres list");
		const databases = sortDatabases(await provider.listDatabases({
			projectId,
			branchName: args.flags.branch,
			signal: ctx.signal
		}));
		const result = {
			projectId,
			projectName,
			branchName: args.flags.branch ?? null,
			databases
		};
		return ok(ctx.present({ data: result }, listPresentations$3(result)));
	}
});
//#endregion
//#region ../cli/src/commands/postgres/show.ts
/** The `postgres show` command. */
const TITLE$6 = "Showing database metadata.";
function fieldRows$2(result) {
	return [
		{
			label: "project",
			value: result.projectName
		},
		{
			label: "database",
			value: result.database.name
		},
		{
			label: "id",
			value: result.database.id
		},
		{
			label: "branch",
			value: branchLabel(result.database)
		},
		{
			label: "region",
			value: result.database.region ?? "unknown"
		},
		{
			label: "status",
			value: formatStatus(result.database)
		},
		{
			label: "connections",
			value: String(result.connections.length)
		}
	];
}
/** The stdout mirror of the field rows: same labels, raw values. An
*  absent branch, region or status is an empty field rather than the
*  word the card shows a reader. */
function stdoutFieldRows$2(result) {
	return [
		{
			label: "project",
			value: result.projectName
		},
		{
			label: "database",
			value: result.database.name
		},
		{
			label: "id",
			value: result.database.id
		},
		{
			label: "branch",
			value: result.database.branchName ?? ""
		},
		{
			label: "region",
			value: result.database.region ?? ""
		},
		{
			label: "status",
			value: statusValue(result.database)
		},
		{
			label: "connections",
			value: String(result.connections.length)
		}
	];
}
function showPresentations$2(result) {
	const rows = fieldRows$2(result);
	return {
		json: () => result,
		next: () => [],
		human: () => [{
			kind: "summary",
			status: "info",
			text: TITLE$6
		}, {
			kind: "fields",
			rows
		}],
		stdout: () => stdoutFieldRows$2(result).map((row) => `${row.label}: ${row.value}`)
	};
}
const postgresShowCommand = defineCommand({
	args: {
		positionals: { database: databasePositional },
		flags: {
			project: projectFlag$1,
			branch: branchFlag$1
		}
	},
	help: {
		summary: "Show database metadata without secret values",
		description: "Shows a database's name, branch, region, and status. Connection URLs are secrets printed once, by 'postgres create' or 'postgres connection rotate'; this command never reveals them.",
		examples: ["postgres show db_123", "postgres show acme-preview --branch preview --json"]
	},
	needs: { credentials: true },
	handler: async (args, ctx) => {
		const { provider, target, projectId, projectName } = await resolvePostgresContext(ctx, args.flags, "postgres show");
		const database = await resolveDatabase(provider, target, args.positionals.database, args.flags.branch, ctx.signal);
		const result = {
			projectId,
			projectName,
			database,
			connections: await provider.listConnections(database.id, { signal: ctx.signal })
		};
		return ok(ctx.present({ data: result }, showPresentations$2(result)));
	}
});
//#endregion
//#region ../cli/src/commands/postgres/usage.ts
/** The `postgres usage` command. */
const TITLE$5 = "Showing database usage metrics.";
function fieldRows$1(result) {
	return [
		{
			label: "project",
			value: result.projectName
		},
		{
			label: "database",
			value: result.database.name
		},
		{
			label: "id",
			value: result.database.id
		},
		{
			label: "period",
			value: `${result.period.start || "unknown"} to ${result.period.end || "unknown"}`
		},
		{
			label: "operations",
			value: formatUsageMetric(result.metrics.operations)
		},
		{
			label: "storage",
			value: formatUsageMetric(result.metrics.storage)
		},
		{
			label: "generated",
			value: result.generatedAt || "unknown"
		}
	];
}
/** The stdout mirror of the field rows. The reader's card carries the
*  unit beside each metric and the word "unknown" for an absent value;
*  stdout carries the number and an empty field, because that is what a
*  program can consume. The units and the period bounds are both in the
*  `--json` record. */
function stdoutFieldRows$1(result) {
	return [
		{
			label: "project",
			value: result.projectName
		},
		{
			label: "database",
			value: result.database.name
		},
		{
			label: "id",
			value: result.database.id
		},
		{
			label: "period start",
			value: result.period.start || ""
		},
		{
			label: "period end",
			value: result.period.end || ""
		},
		{
			label: "operations",
			value: usageMetricValue(result.metrics.operations)
		},
		{
			label: "storage",
			value: usageMetricValue(result.metrics.storage)
		},
		{
			label: "generated",
			value: result.generatedAt || ""
		}
	];
}
function usagePresentations(result) {
	const rows = fieldRows$1(result);
	return {
		json: () => result,
		next: () => [],
		human: () => [{
			kind: "summary",
			status: "info",
			text: TITLE$5
		}, {
			kind: "fields",
			rows
		}],
		stdout: () => stdoutFieldRows$1(result).map((row) => `${row.label}: ${row.value}`)
	};
}
const postgresUsageCommand = defineCommand({
	args: {
		positionals: { database: databasePositional },
		flags: {
			from: flag.string({
				brief: "Start of the usage period",
				placeholder: "iso-date"
			}),
			to: flag.string({
				brief: "End of the usage period",
				placeholder: "iso-date"
			}),
			project: projectFlag$1,
			branch: branchFlag$1
		}
	},
	help: {
		summary: "Show usage metrics for a database",
		examples: ["postgres usage db_123", "postgres usage acme-production --from 2026-06-01 --to 2026-06-30"]
	},
	needs: { credentials: true },
	handler: async (args, ctx) => {
		const from = parseUsageDate(args.flags.from, "--from", "start");
		const to = parseUsageDate(args.flags.to, "--to", "end");
		if (from && to && Date.parse(from) > Date.parse(to)) throw new CliStructuredError("POSTGRES.USAGE_ERROR", "Invalid usage period", {
			why: "--from must not be later than --to.",
			nextActions: [{
				kind: "user-choice",
				label: "Pass a --from date that is on or before the --to date."
			}, {
				kind: "run-command",
				label: USAGE_PERIOD_EXAMPLE_COMMAND,
				command: USAGE_PERIOD_EXAMPLE_COMMAND
			}]
		});
		const { provider, target, projectId, projectName } = await resolvePostgresContext(ctx, args.flags, "postgres usage");
		const database = await resolveDatabase(provider, target, args.positionals.database, args.flags.branch, ctx.signal);
		const usage = await provider.getUsage(database.id, {
			from,
			to,
			signal: ctx.signal
		});
		const result = {
			projectId,
			projectName,
			database,
			period: usage.period,
			metrics: usage.metrics,
			generatedAt: usage.generatedAt
		};
		return ok(ctx.present({ data: result }, usagePresentations(result)));
	}
});
//#endregion
//#region ../cli/src/lib/app/branch-database-api.ts
async function createBranchDatabase(client, options) {
	const result = await client.POST("/v1/databases", {
		body: {
			projectId: options.projectId,
			branchId: options.branchId,
			name: options.branchName,
			source: { type: "empty" }
		},
		signal: options.signal
	});
	if (result.error || !result.data) throw apiCallError$2(`Failed to create database for branch "${options.branchName}"`, result.response, result.error);
	return normalizeBranchDatabaseRecord(result.data.data);
}
async function listEnvironmentVariables(client, options) {
	const variables = [];
	let cursor;
	while (true) {
		const result = await client.GET("/v1/environment-variables", {
			params: { query: {
				projectId: options.projectId,
				class: options.className,
				key: options.key,
				branchId: options.branchId,
				cursor
			} },
			signal: options.signal
		});
		if (result.error || !result.data) throw apiCallError$2("Failed to list environment variables", result.response, result.error);
		variables.push(...result.data.data);
		if (!result.data.pagination.hasMore || !result.data.pagination.nextCursor) break;
		cursor = result.data.pagination.nextCursor;
	}
	return variables.map((variable) => normalizeEnvironmentVariable(variable));
}
async function createEnvironmentVariable(client, options) {
	const result = await client.POST("/v1/environment-variables", {
		body: {
			projectId: options.projectId,
			class: options.className,
			key: options.key,
			value: options.value,
			...options.branchId ? { branchId: options.branchId } : {}
		},
		signal: options.signal
	});
	if (result.error || !result.data) throw apiCallError$2(`Failed to add ${options.key}`, result.response, result.error);
	return normalizeEnvironmentVariable(result.data.data);
}
async function deleteBranchDatabase(client, options) {
	const result = await client.DELETE("/v1/databases/{databaseId}", {
		params: { path: { databaseId: options.databaseId } },
		signal: options.signal
	});
	if (result.error) throw apiCallError$2("Failed to delete branch database", result.response, result.error);
}
async function updateEnvironmentVariable(client, options) {
	const result = await client.PATCH("/v1/environment-variables/{envVarId}", {
		params: { path: { envVarId: options.envVarId } },
		body: { value: options.value },
		signal: options.signal
	});
	if (result.error || !result.data) throw apiCallError$2("Failed to update environment variable", result.response, result.error);
	return normalizeEnvironmentVariable(result.data.data);
}
async function deleteEnvironmentVariable(client, options) {
	const result = await client.DELETE("/v1/environment-variables/{envVarId}", {
		params: { path: { envVarId: options.envVarId } },
		signal: options.signal
	});
	if (result.error) throw apiCallError$2("Failed to delete environment variable", result.response, result.error);
}
function normalizeEnvironmentVariable(variable) {
	return {
		id: variable.id,
		key: variable.key,
		branchId: variable.branchId,
		className: variable.class,
		isManagedBySystem: variable.isManagedBySystem
	};
}
function normalizeBranchDatabaseRecord(database) {
	const connection = database.connections?.[0];
	const databaseUrl = connection?.endpoints?.pooled?.connectionString;
	const directUrl = connection?.endpoints?.direct?.connectionString ?? null;
	if (!databaseUrl) throw new Error("Created database did not return a pooled connection string.");
	return {
		id: database.id,
		name: database.name,
		branchId: database.branchId,
		databaseUrl,
		directUrl
	};
}
function apiCallError$2(summary, response, error) {
	if (response.status === 404) return /* @__PURE__ */ new Error("Resource Not Found");
	const message = error.error?.message ?? `Management API returned HTTP ${response.status}.`;
	const hint = error.error?.hint ? ` ${error.error.hint}` : "";
	return /* @__PURE__ */ new Error(`${summary}: ${message}${hint}`);
}
//#endregion
//#region ../cli/src/lib/app/env-vars.ts
function envVarNames(envVars) {
	if (!envVars) return [];
	return Object.entries(envVars).filter(([, value]) => value !== null).map(([name]) => name).sort((left, right) => left.localeCompare(right));
}
//#endregion
//#region ../cli/src/lib/app/app-provider.ts
var DomainApiError = class extends Error {
	status;
	code;
	hint;
	constructor(options) {
		super(`${options.summary}: ${options.message}${options.hint ? ` ${options.hint}` : ""}`);
		this.name = "DomainApiError";
		this.status = options.status;
		this.code = options.code ?? null;
		this.hint = options.hint ?? null;
	}
};
function createAppProvider(client, options) {
	const sdk = new ComputeClient(client);
	return {
		async createProject(options) {
			const projectResult = await sdk.createProject({
				name: options.name,
				region: options.region,
				signal: options.signal
			});
			if (projectResult.isErr()) throw new Error(projectResult.error.message);
			return {
				id: projectResult.value.id,
				name: projectResult.value.name,
				defaultRegion: projectResult.value.defaultRegion
			};
		},
		async listApps(projectId, options) {
			return listComputeServices(client, {
				projectId,
				branchGitName: options?.branchName,
				signal: options?.signal
			});
		},
		async createApp(options) {
			return createComputeService(client, {
				projectId: options.projectId,
				branchName: options.branchName,
				displayName: options.name,
				...options.region !== void 0 ? { region: options.region } : {},
				...options.signal !== void 0 ? { signal: options.signal } : {}
			});
		},
		async resolveBranch(projectId, options) {
			const branch = await resolveOrCreateBranch$1(client, {
				projectId,
				gitName: options.branchName,
				signal: options.signal
			});
			return {
				id: branch.id,
				name: branch.gitName,
				role: branch.role
			};
		},
		async createBranchDatabase(options) {
			return createBranchDatabase(client, options);
		},
		async deleteBranchDatabase(options) {
			return deleteBranchDatabase(client, options);
		},
		async listEnvironmentVariables(options) {
			return listEnvironmentVariables(client, options);
		},
		async createEnvironmentVariable(options) {
			return createEnvironmentVariable(client, options);
		},
		async updateEnvironmentVariable(options) {
			return updateEnvironmentVariable(client, options);
		},
		async deleteEnvironmentVariable(options) {
			return deleteEnvironmentVariable(client, options);
		},
		async removeApp(appId, options) {
			const appResult = await sdk.showApp({
				appId,
				signal: options?.signal
			});
			if (appResult.isErr()) throw new Error(appResult.error.message);
			const destroyResult = await sdk.destroyApp({
				appId,
				keepApp: false,
				timeoutSeconds: 120,
				pollIntervalMs: 2e3,
				signal: options?.signal,
				progress: options?.progress
			});
			if (destroyResult.isErr()) throw new Error(destroyResult.error.message);
			return {
				id: appResult.value.id,
				name: appResult.value.name
			};
		},
		async listDomains(appId, options) {
			return listComputeServiceDomains(client, appId, options?.signal);
		},
		async addDomain(options) {
			const result = await client.POST("/v1/apps/{appId}/domains", {
				params: { path: { appId: options.appId } },
				body: { hostname: options.hostname },
				signal: options.signal
			});
			if (result.error || !result.data) {
				if (result.response.status === 409) {
					const existing = (await listComputeServiceDomains(client, options.appId, options.signal)).find((domain) => sameHostname(domain.hostname, options.hostname));
					if (existing) return {
						domain: existing,
						existing: true
					};
				}
				throw domainApiCallError("Failed to add custom domain", result.response, result.error);
			}
			return {
				domain: normalizeDomainRecord(result.data.data),
				existing: false
			};
		},
		async showDomain(domainId, options) {
			const result = await client.GET("/v1/domains/{domainId}", {
				params: { path: { domainId } },
				signal: options?.signal
			});
			if (result.error || !result.data) throw domainApiCallError("Failed to show custom domain", result.response, result.error);
			return normalizeDomainRecord(result.data.data);
		},
		async removeDomain(domainId, options) {
			const result = await client.DELETE("/v1/domains/{domainId}", {
				params: { path: { domainId } },
				signal: options?.signal
			});
			if (result.error) throw domainApiCallError("Failed to delete custom domain", result.response, result.error);
		},
		async retryDomain(domainId, options) {
			const result = await client.POST("/v1/domains/{domainId}/retry", {
				params: { path: { domainId } },
				signal: options?.signal
			});
			if (result.error || !result.data) throw domainApiCallError("Failed to retry custom domain", result.response, result.error);
			return normalizeDomainRecord(result.data.data);
		},
		async promoteDeployment(options) {
			const promoteResult = await sdk.promote({
				appId: options.appId,
				deploymentId: options.deploymentId,
				timeoutSeconds: 120,
				pollIntervalMs: 2e3,
				signal: options.signal,
				progress: options.progress
			});
			if (promoteResult.isErr()) throw new Error(promoteResult.error.message);
		},
		async startDeployment(options) {
			const result = await sdk.startDeployment({
				deploymentId: options.deploymentId,
				signal: options.signal
			});
			if (result.isErr()) throw new Error(result.error.message);
		},
		async stopDeployment(options) {
			const result = await sdk.stopDeployment({
				deploymentId: options.deploymentId,
				signal: options.signal
			});
			if (result.isErr()) throw new Error(result.error.message);
		},
		async deleteDeployment(options) {
			const result = await sdk.deleteDeployment({
				deploymentId: options.deploymentId,
				signal: options.signal
			});
			if (result.isErr()) throw new Error(result.error.message);
		},
		async readDeployment(options) {
			const result = await sdk.showDeployment({
				deploymentId: options.deploymentId,
				signal: options.signal
			});
			if (result.isErr()) throw new Error(result.error.message);
			return {
				id: result.value.id,
				status: result.value.status,
				createdAt: result.value.createdAt,
				url: toAbsoluteUrl(result.value.previewDomain ?? null),
				live: null
			};
		},
		async updateAppEnv(options) {
			const updateResult = await sdk.updateEnv({
				appId: options.appId,
				envVars: options.envVars,
				timeoutSeconds: 120,
				pollIntervalMs: 2e3,
				signal: options.signal,
				progress: options.progress
			});
			if (updateResult.isErr()) throw new Error(updateResult.error.message);
			const promoteResult = await sdk.promote({
				appId: options.appId,
				deploymentId: updateResult.value.deploymentId,
				timeoutSeconds: 120,
				pollIntervalMs: 2e3,
				signal: options.signal,
				progress: options.promoteProgress
			});
			if (promoteResult.isErr()) throw new Error(promoteResult.error.message);
			const [serviceResult, versionResult] = await Promise.all([sdk.showApp({
				appId: options.appId,
				signal: options.signal
			}), sdk.showDeployment({
				deploymentId: updateResult.value.deploymentId,
				signal: options.signal
			})]);
			if (serviceResult.isErr()) throw new Error(serviceResult.error.message);
			if (versionResult.isErr()) throw new Error(versionResult.error.message);
			return {
				projectId: updateResult.value.projectId,
				app: {
					id: serviceResult.value.id,
					name: serviceResult.value.name,
					region: serviceResult.value.region ?? null,
					liveDeploymentId: serviceResult.value.latestDeploymentId ?? null,
					liveUrl: toAbsoluteUrl(serviceResult.value.appEndpointDomain ?? null)
				},
				deployment: {
					id: versionResult.value.id,
					status: versionResult.value.status,
					createdAt: versionResult.value.createdAt,
					url: toAbsoluteUrl(serviceResult.value.appEndpointDomain ?? versionResult.value.previewDomain ?? null),
					live: true
				},
				variables: envVarNames(versionResult.value.envVars)
			};
		},
		async listAppEnvNames(options) {
			const [serviceResult, versionResult] = await Promise.all([sdk.showApp({
				appId: options.appId,
				signal: options.signal
			}), sdk.showDeployment({
				deploymentId: options.deploymentId,
				signal: options.signal
			})]);
			if (serviceResult.isErr()) throw new Error(serviceResult.error.message);
			if (versionResult.isErr()) throw new Error(versionResult.error.message);
			return {
				projectId: serviceResult.value.projectId,
				app: {
					id: serviceResult.value.id,
					name: serviceResult.value.name,
					region: serviceResult.value.region ?? null,
					liveDeploymentId: serviceResult.value.latestDeploymentId ?? null,
					liveUrl: toAbsoluteUrl(serviceResult.value.appEndpointDomain ?? null)
				},
				deployment: {
					id: versionResult.value.id,
					status: versionResult.value.status,
					createdAt: versionResult.value.createdAt,
					url: toAbsoluteUrl(versionResult.value.previewDomain ?? null),
					live: serviceResult.value.latestDeploymentId === versionResult.value.id
				},
				variables: envVarNames(versionResult.value.envVars)
			};
		},
		async listDeployments(appId, options) {
			const [appResult, versionsResult] = await Promise.all([sdk.showApp({
				appId,
				signal: options?.signal
			}), sdk.listDeployments({
				appId,
				signal: options?.signal
			})]);
			if (appResult.isErr()) throw new Error(appResult.error.message);
			if (versionsResult.isErr()) throw new Error(versionsResult.error.message);
			return {
				app: {
					id: appResult.value.id,
					name: appResult.value.name,
					region: appResult.value.region ?? null,
					liveDeploymentId: appResult.value.latestDeploymentId ?? null,
					liveUrl: toAbsoluteUrl(appResult.value.appEndpointDomain ?? null)
				},
				deployments: versionsResult.value.slice().sort((left, right) => {
					const byDate = right.createdAt.localeCompare(left.createdAt);
					return byDate !== 0 ? byDate : right.id.localeCompare(left.id);
				}).map((deployment) => ({
					id: deployment.id,
					status: deployment.status,
					createdAt: deployment.createdAt,
					url: toAbsoluteUrl(deployment.previewDomain ?? null),
					live: null
				}))
			};
		},
		async showDeployment(deploymentId, options) {
			const deploymentResult = await sdk.showDeployment({
				deploymentId,
				signal: options?.signal
			});
			if (deploymentResult.isErr()) {
				if (ApiError.is(deploymentResult.error) && deploymentResult.error.statusCode === 404) return null;
				throw new Error(deploymentResult.error.message);
			}
			const app = await owningService(sdk, deploymentResult.value.serviceId, options?.signal);
			const promotedUrl = app !== null && app.liveDeploymentId === deploymentResult.value.id ? app.liveUrl : null;
			return {
				app,
				deployment: {
					id: deploymentResult.value.id,
					status: deploymentResult.value.status,
					createdAt: deploymentResult.value.createdAt,
					url: promotedUrl ?? toAbsoluteUrl(deploymentResult.value.previewDomain ?? null),
					live: null
				}
			};
		},
		async streamDeploymentLogs(streamOptions) {
			if (!options?.baseUrl || !options.getToken) throw new Error("Log streaming requires an authenticated API base URL and token.");
			const result = await streamLogs({
				baseUrl: options.baseUrl,
				token: await options.getToken(),
				deploymentId: streamOptions.deploymentId,
				signal: streamOptions.signal
			}, streamOptions.onRecord);
			if (result.isErr()) {
				if (CancelledError.is(result.error)) return;
				throw result.error;
			}
		}
	};
}
async function listBranches(client, options) {
	const result = await client.GET("/v1/projects/{projectId}/branches", {
		params: {
			path: { projectId: options.projectId },
			query: { gitName: options.gitName }
		},
		signal: options.signal
	});
	if (result.error || !result.data) throw apiCallError$1("Failed to list branches", result.response, result.error);
	return result.data.data.map((branch) => ({
		id: branch.id,
		gitName: branch.gitName,
		isDefault: branch.isDefault,
		role: branch.role
	}));
}
async function resolveOrCreateBranch$1(client, options) {
	const existing = (await listBranches(client, options))[0];
	if (existing) return existing;
	const result = await client.POST("/v1/projects/{projectId}/branches", {
		params: { path: { projectId: options.projectId } },
		body: { gitName: options.gitName },
		signal: options.signal
	});
	if (result.error || !result.data) {
		if (result.response.status === 409) {
			const raced = (await listBranches(client, options))[0];
			if (raced) return raced;
		}
		throw apiCallError$1(`Failed to create branch "${options.gitName}"`, result.response, result.error);
	}
	const branch = result.data.data;
	return {
		id: branch.id,
		gitName: branch.gitName,
		isDefault: branch.isDefault,
		role: branch.role
	};
}
async function listComputeServices(client, options) {
	const services = [];
	let cursor;
	while (true) {
		const result = await client.GET("/v1/apps", {
			params: { query: {
				projectId: options.projectId,
				branchGitName: options.branchGitName,
				cursor
			} },
			signal: options.signal
		});
		if (result.error || !result.data) throw apiCallError$1("Failed to list apps", result.response, result.error);
		services.push(...result.data.data);
		if (!result.data.pagination.hasMore || !result.data.pagination.nextCursor) break;
		cursor = result.data.pagination.nextCursor;
	}
	return services.map(toAppRecord);
}
async function owningService(sdk, serviceId, signal) {
	const result = await sdk.showApp({
		appId: serviceId,
		signal
	});
	if (result.isErr()) {
		if (ApiError.is(result.error) && result.error.statusCode === 404) return null;
		throw new Error(result.error.message);
	}
	return {
		id: result.value.id,
		name: result.value.name,
		region: result.value.region ?? null,
		liveDeploymentId: result.value.latestDeploymentId ?? null,
		liveUrl: toAbsoluteUrl(result.value.appEndpointDomain ?? null)
	};
}
function toAppRecord(service) {
	return {
		id: service.id,
		name: service.name,
		region: service.region.id ?? null,
		branchId: service.branchId,
		liveDeploymentId: service.latestDeploymentId ?? null,
		liveUrl: toAbsoluteUrl(service.appEndpointDomain ?? null)
	};
}
/**
* Creates a service on a branch, resolving (or creating) the branch
* first because the create body needs its id. A name already taken on
* the branch comes back as the existing service rather than an error:
* the API answers 409 and the caller asked for a service by that name,
* which already exists.
*/
async function createComputeService(client, options) {
	const branch = await resolveOrCreateBranch$1(client, {
		projectId: options.projectId,
		gitName: options.branchName,
		signal: options.signal
	});
	const result = await client.POST("/v1/apps", {
		body: {
			projectId: options.projectId,
			branchId: branch.id,
			displayName: options.displayName,
			...options.region ? { regionId: options.region } : {}
		},
		signal: options.signal
	});
	if (result.error || !result.data) {
		if (result.response.status === 409) {
			const matched = (await listComputeServices(client, {
				projectId: options.projectId,
				branchGitName: options.branchName,
				signal: options.signal
			})).find((service) => service.name === options.displayName);
			if (matched) return {
				service: matched,
				existing: true
			};
		}
		throw apiCallError$1(`Failed to create app "${options.displayName}"`, result.response, result.error);
	}
	return {
		service: toAppRecord(result.data.data),
		existing: false
	};
}
async function listComputeServiceDomains(client, appId, signal) {
	const result = await client.GET("/v1/apps/{appId}/domains", {
		params: { path: { appId } },
		signal
	});
	if (result.error || !result.data) throw domainApiCallError("Failed to list custom domains", result.response, result.error);
	return result.data.data.map((domain) => normalizeDomainRecord(domain));
}
function normalizeDomainRecord(domain) {
	return {
		id: domain.id,
		type: domain.type,
		url: domain.url,
		hostname: domain.hostname,
		appId: domain.appId,
		status: domain.status,
		foundryStatus: domain.foundryStatus,
		failureReason: domain.failureReason,
		failureCategory: domain.failureCategory,
		certExpiresAt: domain.certExpiresAt,
		createdAt: domain.createdAt,
		updatedAt: domain.updatedAt,
		dnsRecords: normalizeDomainDnsRecords(domain.dnsRecords)
	};
}
function normalizeDomainDnsRecords(records) {
	if (!Array.isArray(records)) return [];
	return records.map((record) => {
		if (typeof record.type !== "string" || typeof record.name !== "string" || typeof record.value !== "string") return null;
		return {
			type: record.type,
			name: record.name,
			value: record.value,
			ttl: typeof record.ttl === "number" ? record.ttl : null
		};
	}).filter((record) => Boolean(record));
}
function sameHostname(left, right) {
	return normalizeHostnameForComparison(left) === normalizeHostnameForComparison(right);
}
function normalizeHostnameForComparison(hostname) {
	return hostname.trim().replace(/\.$/, "").toLowerCase();
}
function apiCallError$1(summary, response, error) {
	if (response.status === 404) return /* @__PURE__ */ new Error("Resource Not Found");
	const message = error.error?.message ?? `Management API returned HTTP ${response.status}.`;
	const hint = error.error?.hint ? ` ${error.error.hint}` : "";
	return /* @__PURE__ */ new Error(`${summary}: ${message}${hint}`);
}
function domainApiCallError(summary, response, error) {
	return new DomainApiError({
		summary,
		status: response.status,
		code: error.error?.code ?? null,
		message: error.error?.message ?? `Management API returned HTTP ${response.status}.`,
		hint: error.error?.hint ?? null
	});
}
function toAbsoluteUrl(url) {
	if (!url) return null;
	return url.startsWith("https://") || url.startsWith("http://") ? url : `https://${url}`;
}
//#endregion
//#region ../cli/src/commands/project/presentation.ts
/** Deploys come from pushing a connected repository, so the step after
*  creating or linking a Project is connecting one. */
const CONNECT_REPO_NEXT_ACTION = {
	kind: "run-command",
	label: `${CLI_NAME} git connect`,
	command: `${CLI_NAME} git connect`
};
/** The legacy local-pin warnings of `project delete` / `project
*  transfer`: the operation succeeded, so they are warn diagnostics
*  under the pinned local-state code, never errors. */
function localPinDiagnostics(warnings) {
	return warnings.map((warning) => ({
		code: "PROJECT.LOCAL_STATE_WRITE_FAILED",
		severity: "warn",
		summary: warning,
		nextActions: []
	}));
}
function setupPresentations(result) {
	return {
		stdout: () => [],
		human: () => [
			...result.action === "created" ? [{
				kind: "summary",
				status: "ok",
				text: `Created Project "${result.project.name}"`
			}] : [],
			{
				kind: "summary",
				status: "ok",
				text: `Linked "${result.directory}" to Project "${result.project.name}"`
			},
			{
				kind: "summary",
				status: "info",
				text: `Saved ${result.localPin.path}`
			}
		],
		json: () => serializeProjectSetup(result),
		next: () => [CONNECT_REPO_NEXT_ACTION]
	};
}
//#endregion
//#region ../cli/src/commands/project/create.ts
/** The `project create` command. */
const projectCreateCommand = defineCommand({
	args: {
		positionals: { name: positional.string({
			brief: "Project name",
			placeholder: "name"
		}) },
		flags: { region: flag.string({
			brief: "Prisma Compute region for the project's infrastructure; set it when data or users must stay near a location",
			placeholder: "region"
		}) }
	},
	help: {
		summary: "Create a Project and link this directory: commands run here target it",
		description: "Creates a Project in your workspace and links the current directory to it, so later commands resolve it without --project. A Project groups one product or codebase; its Branches are isolated environments, each with its own services, databases, and buckets. This is usually the first command after 'auth login' in a new repository.",
		examples: ["project create my-app", "project create my-app --json"]
	},
	needs: { credentials: true },
	handler: async (args, ctx) => {
		const workspace = await resolveActiveWorkspace(ctx);
		if (!isValidProjectSetupName(args.positionals.name)) throw projectSetupNameRequiredError("project create");
		const name = args.positionals.name.trim();
		const created = await createAppProvider(ctx.api).createProject({
			name,
			region: args.flags.region,
			signal: ctx.signal
		}).catch((error) => {
			/** A cancelled run is cancelled, not a failed creation. The
			*  provider flattens the underlying AbortError into a plain
			*  Error, which the engine would settle as a bug, so hand it
			*  back its own abort reason and let it settle the run as
			*  cancelled. */
			if (ctx.signal.aborted) throw ctx.signal.reason;
			throw projectCreateFailedError(error, name, workspace, {
				nextSteps: ["prisma project list", "prisma project link <id-or-name>"],
				permissionFix: "Grant the token permission to create Projects in this workspace, or link an existing Project.",
				fallbackFix: "Retry the command, or choose an existing Project with prisma project link <id-or-name>."
			});
		});
		const result = await bindDirectoryToProject(ctx, workspace, {
			id: created.id,
			name: created.name,
			...created.defaultRegion != null ? { defaultRegion: created.defaultRegion } : {}
		}, "created");
		return ok(ctx.present({ data: result }, setupPresentations(result)));
	}
});
//#endregion
//#region ../cli/src/commands/project/delete.ts
/** The `project delete` command. */
const CONSENT_QUESTION$1 = "Deleting a project is permanent, destroys its databases, and stops its apps, so it requires the exact project id.";
function deletePresentations$2(result) {
	return {
		stdout: () => [],
		json: () => result,
		next: () => [],
		human: () => [
			{
				kind: "summary",
				status: "ok",
				text: "Deleting project."
			},
			{
				kind: "fields",
				rows: [
					{
						label: "workspace",
						value: result.workspace.name
					},
					{
						label: "project",
						value: result.project.name
					},
					{
						label: "id",
						value: result.project.id
					}
				]
			},
			{
				kind: "list",
				items: ["The project, its databases, and its apps were deleted.", ...result.localPin.cleared ? ["This directory's local project binding was cleared."] : []]
			}
		]
	};
}
const projectDeleteCommand = defineCommand({
	args: { positionals: { project: positional.string({
		brief: "Project id or name",
		placeholder: "id-or-name"
	}) } },
	help: {
		summary: "Delete a Project permanently after exact id confirmation",
		description: "Deletion is permanent: it destroys the project's databases and stops its deployed services. Because of that, the command asks for the exact project id as a consent token; pass it with --confirm to run non-interactively.",
		examples: ["project delete proj_123 --confirm proj_123"]
	},
	needs: { credentials: true },
	handler: async (args, ctx) => {
		const workspace = await resolveActiveWorkspace(ctx);
		const projects = await listWorkspaceProjects$1(ctx);
		const project = toProjectSummary(resolveProjectForSetup(args.positionals.project.trim(), projects, workspace));
		await ctx.prompt.consent(CONSENT_QUESTION$1, { token: project.id });
		await createManagementProjectProvider(ctx.api).removeProject({
			projectId: project.id,
			signal: ctx.signal
		});
		const warnings = [];
		const result = {
			workspace,
			project,
			localPin: { cleared: await cleanupLocalPinForProject(operationContext(ctx), project.id, { onError: (message) => warnings.push(message) }) }
		};
		const diagnostics = localPinDiagnostics(warnings);
		return ok(ctx.present({
			data: result,
			diagnostics
		}, deletePresentations$2(result)));
	}
});
//#endregion
//#region ../cli/src/lib/app/env-errors.ts
/**
* The structured errors the `project env` code paths raise, with the
* registered PROJECT.* codes assigned at origin. This is the lowest
* layer the env commands, controllers and parsers share, so the
* parsers can raise without depending on the controllers.
*/
function userChoice(label) {
	return {
		kind: "user-choice",
		label
	};
}
function runCommand(command, reason) {
	return {
		kind: "run-command",
		label: command,
		command,
		...reason === void 0 ? {} : { reason }
	};
}
function envUsageError(summary, why, fix, commands = []) {
	return new CliStructuredError("PROJECT.USAGE_ERROR", summary, {
		why,
		nextActions: [userChoice(fix), ...commands.map((step) => runCommand(step))]
	});
}
//#endregion
//#region ../cli/src/lib/app/env-config.ts
const VALID_ROLES = new Set(["production", "preview"]);
function positionalHint(command) {
	if (command === "add" || command === "update") return "KEY=value ";
	if (command === "delete") return "KEY ";
	return "";
}
function resolveEnvScope(flags, options) {
	if (flags.roleName && flags.branchName) throw envUsageError(`prisma project env ${options.command} accepts either --role or --branch`, "--role targets a project-level config map; --branch targets a preview branch override.", "Pass exactly one scope flag.", [`prisma project env ${options.command} ${positionalHint(options.command)}--role preview`, `prisma project env ${options.command} ${positionalHint(options.command)}--branch feature/foo`]);
	if (flags.roleName) {
		if (!VALID_ROLES.has(flags.roleName)) throw envUsageError(`Unknown role "${flags.roleName}"`, "--role accepts production or preview.", "Pass --role production or --role preview.", [`prisma project env ${options.command} --role production`, `prisma project env ${options.command} --role preview`]);
		return {
			kind: "role",
			role: flags.roleName
		};
	}
	if (flags.branchName) return {
		kind: "branch",
		branchName: flags.branchName
	};
	if (options.requireExplicit) {
		const positional = positionalHint(options.command);
		throw envUsageError(`prisma project env ${options.command} requires --role or --branch`, "Writing without an explicit scope is rejected so the command never silently targets production.", "Pass --role production, --role preview, or --branch <git-name>.", [
			`prisma project env ${options.command} ${positional}--role production`,
			`prisma project env ${options.command} ${positional}--role preview`,
			`prisma project env ${options.command} ${positional}--branch feature/foo`
		]);
	}
	return null;
}
function parseKeyValuePositional(raw, command, env = process.env) {
	if (!raw) throw envUsageError(`prisma project env ${command} requires KEY=VALUE`, "No KEY=VALUE positional argument was supplied.", "Pass the variable as KEY=VALUE, e.g. STRIPE_KEY=sk_test_xxx.", [`prisma project env ${command} STRIPE_KEY=sk_test_xxx --role production`]);
	const separatorIndex = raw.indexOf("=");
	if (separatorIndex === -1) {
		if (KEY_SHAPE.test(raw)) {
			validateKey(raw, command);
			const value = env[raw];
			if (typeof value === "string" && value.length > 0) return {
				key: raw,
				value
			};
			throw envUsageError(`Value for "${raw}" was not provided`, `No KEY=VALUE assignment was supplied, and ${raw} is not set in the current environment.`, "Pass KEY=VALUE or export the variable before running the command.", [`prisma project env ${command} ${raw}=value --role production`, `${raw}=value prisma project env ${command} ${raw} --role production`]);
		}
		throw envUsageError(`KEY=VALUE argument is missing the = separator`, `"${raw}" does not contain an = character.`, "Pass the variable as KEY=VALUE, e.g. STRIPE_KEY=sk_test_xxx.", [`prisma project env ${command} STRIPE_KEY=sk_test_xxx --role production`]);
	}
	const key = raw.slice(0, separatorIndex);
	const value = raw.slice(separatorIndex + 1);
	validateKey(key, command);
	if (value.length === 0) throw envUsageError(`KEY=VALUE argument has an empty value`, `"${raw}" has an empty value after the = separator.`, `Pass a non-empty value, or use prisma project env delete to delete a variable.`, [`prisma project env ${command} ${key}=value --role production`]);
	return {
		key,
		value
	};
}
const KEY_SHAPE = /^[A-Z_][A-Z0-9_]*$/;
function validateKey(key, command) {
	if (key.length === 0) throw envUsageError(`Variable key cannot be empty`, "An empty key was passed.", "Pass an env-var key, e.g. STRIPE_KEY.", [`prisma project env ${command} STRIPE_KEY=value --role production`]);
	if (key.length > 256) throw envUsageError(`Variable key "${key}" exceeds the 256-character limit`, "Env-var keys are capped at 256 characters by the platform.", "Use a shorter key.");
	if (!KEY_SHAPE.test(key)) throw envUsageError(`Variable key "${key}" must match the POSIX env-var shape`, "Keys must start with an uppercase letter or underscore and contain only uppercase letters, digits, and underscores.", "Rename the key to match [A-Z_][A-Z0-9_]*.", [`prisma project env ${command} STRIPE_KEY=value --role production`]);
}
function formatScopeLabel(scope) {
	if (scope.kind === "role") return scope.role;
	return `branch:${scope.branchName}`;
}
//#endregion
//#region ../cli/src/lib/app/env-file.ts
const ASSIGNMENT_KEY_PATTERN = /^\s*(?:export\s+)?([^#=\s]+)\s*=/;
async function readEnvFileAssignments(cwd, filePath, command) {
	const resolvedPath = path.resolve(cwd, filePath);
	let contents;
	try {
		contents = await readFile(resolvedPath, "utf8");
	} catch (error) {
		throw envUsageError(`Failed to read env file "${filePath}"`, error instanceof Error ? error.message : "The file could not be read.", "Pass a readable dotenv file path.", [`prisma project env ${command} --file .env --role preview`]);
	}
	return parseEnvFileContents(contents, filePath, command);
}
function parseEnvFileContents(contents, filePath, command) {
	const parsedKeys = extractParsedKeys(contents);
	if (parsedKeys.length === 0) throw envUsageError(`No environment variables found in "${filePath}"`, "The file does not contain any KEY=VALUE assignments.", "Pass a dotenv file with at least one non-empty variable.");
	const seen = /* @__PURE__ */ new Map();
	for (const entry of parsedKeys) {
		validateEnvFileKey(entry.key, entry.line, filePath, command);
		const firstLine = seen.get(entry.key);
		if (firstLine !== void 0) throw envUsageError(`Duplicate environment variable "${entry.key}" in "${filePath}"`, `Lines ${firstLine} and ${entry.line} both define ${entry.key}.`, "Keep one assignment for each key before importing the file.");
		seen.set(entry.key, entry.line);
	}
	const parsedValues = parse(contents);
	return parsedKeys.map(({ key }) => {
		const value = parsedValues[key];
		if (typeof value !== "string" || value.length === 0) {
			const line = seen.get(key);
			throw envUsageError(`Environment variable "${key}" in "${filePath}" has an empty value`, line === void 0 ? `${key} has an empty value.` : `Line ${line} defines ${key} with an empty value.`, "Pass a non-empty value, or omit the key from the file.");
		}
		return {
			key,
			value
		};
	});
}
function extractParsedKeys(contents) {
	const keys = [];
	let multilineQuote = null;
	const lines = contents.split(/\n/);
	for (const [index, line] of lines.entries()) {
		const lineNumber = index + 1;
		if (multilineQuote !== null) {
			if (hasClosingQuote(line, multilineQuote, 0)) multilineQuote = null;
			continue;
		}
		const match = ASSIGNMENT_KEY_PATTERN.exec(line);
		if (!match) continue;
		const key = match[1];
		keys.push({
			key,
			line: lineNumber
		});
		const valueStart = line.slice(match[0].length).trimStart();
		const openingQuote = valueStart[0];
		if ((openingQuote === "\"" || openingQuote === "'" || openingQuote === "`") && !hasClosingQuote(valueStart, openingQuote, 1)) multilineQuote = openingQuote;
	}
	return keys;
}
function validateEnvFileKey(key, line, filePath, command) {
	try {
		validateKey(key, command);
	} catch (error) {
		const reason = error instanceof Error && error.message.length > 0 ? error.message : "Invalid environment variable key.";
		throw envUsageError(`Invalid environment variable "${key}" in "${filePath}"`, `Line ${line}: ${reason}`, "Use a valid env-var key and retry the import.");
	}
}
function hasClosingQuote(value, quote, startIndex) {
	for (let index = startIndex; index < value.length; index += 1) if (value[index] === quote && !isEscaped(value, index)) return true;
	return false;
}
function isEscaped(value, index) {
	let backslashes = 0;
	for (let cursor = index - 1; cursor >= 0 && value[cursor] === "\\"; cursor -= 1) backslashes += 1;
	return backslashes % 2 === 1;
}
//#endregion
//#region ../cli/src/controllers/app-env-api.ts
async function findVariableByNaturalKey(client, projectId, key, resolved, signal) {
	const { data, error, response } = await client.GET("/v1/environment-variables", {
		params: { query: {
			projectId,
			class: resolved.apiTarget.class,
			key,
			...resolved.apiTarget.branchId !== null ? { branchId: resolved.apiTarget.branchId } : {}
		} },
		signal
	});
	if (error || !data) throw apiCallError(`Failed to look up ${key}`, response, error);
	return data.data.filter((row) => rowMatchesExactScope(row, resolved))[0] ?? null;
}
function toMetadata(row, requestedScope) {
	const rowScope = row.branchId === null ? {
		kind: "role",
		role: row.class
	} : requestedScope;
	return {
		id: row.id,
		key: row.key,
		scope: rowScope,
		source: formatDescriptorLabel(rowScope),
		isManagedBySystem: row.isManagedBySystem,
		updatedAt: row.updatedAt
	};
}
function rowMatchesExactScope(row, resolved) {
	return row.class === resolved.apiTarget.class && row.branchId === resolved.apiTarget.branchId;
}
function apiCallError(summary, response, error) {
	const status = response?.status ?? 0;
	const apiCode = error?.error?.code;
	const apiMessage = error?.error?.message;
	const apiHint = error?.error?.hint;
	if (status === 401 || status === 403) return new CliStructuredError("PROJECT.ENV_API_ERROR", summary, {
		why: "The Management API rejected the request as unauthorized or forbidden.",
		meta: { status },
		nextActions: [runCommand("prisma auth login")]
	});
	return new CliStructuredError("PROJECT.ENV_API_ERROR", summary, {
		why: apiMessage ?? `The Management API returned status ${status || "unknown"}.`,
		...status || apiCode !== void 0 ? { meta: {
			...status ? { status } : {},
			...apiCode !== void 0 ? { apiCode } : {}
		} } : {},
		nextActions: [userChoice(apiHint ?? "Re-run with --log-level verbose for the underlying API response details.")]
	});
}
function formatDescriptorLabel(scope) {
	if (scope.kind === "role") return scope.role ?? "unknown";
	if (scope.kind === "overview") return "overview";
	return `branch:${scope.branchName ?? scope.branchId ?? "unknown"}`;
}
//#endregion
//#region ../cli/src/controllers/app-env.ts
function resolveEnvWriteSource(rawAssignment, filePath, command) {
	if (filePath !== void 0 && rawAssignment !== void 0) throw envUsageError(`prisma project env ${command} accepts either KEY=VALUE or --file`, "The command received both a positional assignment and a dotenv file path.", "Pass one input source.", [`prisma project env ${command} KEY=value --role preview`, `prisma project env ${command} --file .env --role preview`]);
	if (filePath !== void 0) {
		if (filePath.length === 0) throw envUsageError(`prisma project env ${command} --file requires a path`, "The --file flag was passed without a file path.", "Pass a readable dotenv file path.", [`prisma project env ${command} --file .env --role preview`]);
		return {
			kind: "file",
			filePath
		};
	}
	if (rawAssignment === void 0) throw envUsageError(`prisma project env ${command} requires KEY=VALUE or --file`, "No environment variable input was supplied.", "Pass a single KEY=VALUE assignment or a dotenv file path.", [`prisma project env ${command} KEY=value --role preview`, `prisma project env ${command} --file .env --role preview`]);
	return {
		kind: "single",
		rawAssignment
	};
}
async function resolveEnvWriteInput(context, source, command) {
	if (source.kind === "file") return {
		kind: "file",
		filePath: source.filePath,
		assignments: await readEnvFileAssignments(context.runtime.cwd, source.filePath, command)
	};
	return {
		kind: "single",
		...parseKeyValuePositional(source.rawAssignment, command, context.runtime.env)
	};
}
async function resolveScopeToApi(client, projectId, scope, options) {
	if (scope.kind === "role") return {
		scope,
		descriptor: {
			kind: "role",
			role: scope.role
		},
		apiTarget: {
			class: scope.role,
			branchId: null
		}
	};
	const branch = options.createBranchIfMissing ? await resolveOrCreateBranch(client, projectId, scope.branchName, options.signal) : await resolveExistingBranch(client, projectId, scope.branchName, options.signal);
	if (branch.role === "production") throw new CliStructuredError("PROJECT.ENV_BRANCH_SCOPE_IS_PRODUCTION", `Branch "${scope.branchName}" is the production branch`, {
		why: "Production variables are project-level only; branch overrides apply to preview branches.",
		nextActions: [userChoice("Use --role production for the production branch."), runCommand("prisma project env list --role production")]
	});
	return {
		scope,
		descriptor: {
			kind: "branch",
			branchName: branch.gitName,
			branchId: branch.id
		},
		apiTarget: {
			class: "preview",
			branchId: branch.id
		}
	};
}
/** No explicit scope lists the overview of every scope. Nothing is
*  inferred from ambient context: what runs is what was named. */
async function resolveListScopeToApi(client, projectId, explicit, options) {
	if (explicit) {
		const resolved = await resolveScopeToApi(client, projectId, explicit, {
			createBranchIfMissing: false,
			signal: options.signal
		});
		return {
			kind: "scoped",
			descriptor: resolved.descriptor,
			target: targetFromExplicitScope(resolved.descriptor),
			apiTarget: resolved.apiTarget,
			addScope: resolved.scope
		};
	}
	return {
		kind: "overview",
		descriptor: { kind: "overview" },
		target: {
			source: "overview",
			envMap: "overview"
		},
		addScope: {
			kind: "role",
			role: "preview"
		}
	};
}
function targetFromExplicitScope(scope) {
	if (scope.kind === "branch") return {
		source: "explicit",
		branchName: scope.branchName,
		branchId: scope.branchId,
		branchRole: "preview",
		branchExists: true,
		envMap: "preview"
	};
	if (scope.kind === "role") return {
		source: "explicit",
		envMap: scope.role
	};
	return {
		source: "overview",
		envMap: "overview"
	};
}
function formatScopeFlag$1(scope) {
	if (scope.kind === "role") return `--role ${scope.role}`;
	return `--branch ${scope.branchName}`;
}
async function listBranchesByName(client, projectId, branchName, signal) {
	const { data, error, response } = await client.GET("/v1/projects/{projectId}/branches", {
		params: {
			path: { projectId },
			query: { gitName: branchName }
		},
		signal
	});
	if (error || !data) throw apiCallError(`Failed to resolve branch "${branchName}"`, response, error);
	return data.data;
}
async function resolveExistingBranch(client, projectId, branchName, signal) {
	const branch = (await listBranchesByName(client, projectId, branchName, signal))[0];
	if (!branch) throw new CliStructuredError("PROJECT.ENV_BRANCH_NOT_FOUND", `Branch "${branchName}" not found`, {
		why: "Branch update, list, and delete commands only target existing preview branches.",
		nextActions: [userChoice("Create the branch by deploying it, or use `project env add --branch` to create its first override."), runCommand(`prisma project env add KEY=value --branch ${branchName}`)]
	});
	return branch;
}
async function resolveOrCreateBranch(client, projectId, branchName, signal) {
	const existing = (await listBranchesByName(client, projectId, branchName, signal))[0];
	if (existing) return existing;
	if (!await projectHasDefaultBranch(client, projectId, signal)) throw new CliStructuredError("PROJECT.ENV_BRANCH_CREATE_REQUIRES_DEFAULT_BRANCH", `Cannot create branch "${branchName}" from project env`, {
		why: "Creating the first branch would make it the project default, but branch overrides are preview-only.",
		nextActions: [userChoice("Create or deploy the default branch first, then add the branch override."), runCommand("prisma git connect <repository-url>")]
	});
	const { data, error, response } = await client.POST("/v1/projects/{projectId}/branches", {
		params: { path: { projectId } },
		body: {
			gitName: branchName,
			isDefault: false
		},
		signal
	});
	if (error || !data) {
		if (response?.status === 409) {
			const raced = (await listBranchesByName(client, projectId, branchName, signal))[0];
			if (raced) return raced;
		}
		throw apiCallError(`Failed to create branch "${branchName}"`, response, error);
	}
	return data.data;
}
async function projectHasDefaultBranch(client, projectId, signal) {
	let cursor;
	while (true) {
		const query = {};
		if (cursor !== void 0) query.cursor = cursor;
		const result = await client.GET("/v1/projects/{projectId}/branches", {
			params: {
				path: { projectId },
				query
			},
			signal
		});
		if (result.error || !result.data) throw apiCallError("Failed to check project default branch", result.response, result.error);
		if (result.data.data.some((branch) => branch.isDefault)) return true;
		if (!result.data.pagination.hasMore || !result.data.pagination.nextCursor) return false;
		cursor = result.data.pagination.nextCursor;
	}
}
async function listVariables(client, projectId, resolved, signal) {
	return materializeEffectiveRows(await collectEnvironmentVariables(client, projectId, signal, {
		className: resolved.apiTarget.class,
		filter: (row) => rowMatchesScope(row, resolved)
	}), resolved);
}
async function listOverviewVariables(client, projectId, signal) {
	return (await collectEnvironmentVariables(client, projectId, signal, { filter: (row) => row.branchId === null && (row.class === "production" || row.class === "preview") })).sort((left, right) => {
		const roleOrder = roleSortOrder(left.class) - roleSortOrder(right.class);
		return roleOrder !== 0 ? roleOrder : left.key.localeCompare(right.key);
	});
}
async function collectEnvironmentVariables(client, projectId, signal, options) {
	const collected = [];
	let cursor;
	while (true) {
		const query = { projectId };
		if (options.className !== void 0) query.class = options.className;
		if (cursor !== void 0) query.cursor = cursor;
		const result = await client.GET("/v1/environment-variables", {
			params: { query },
			signal
		});
		if (result.error || !result.data) throw apiCallError(`Failed to list environment variables`, result.response, result.error);
		const page = result.data.data.filter(options.filter);
		collected.push(...page);
		if (!result.data.pagination.hasMore || !result.data.pagination.nextCursor) break;
		cursor = result.data.pagination.nextCursor;
	}
	return collected;
}
function roleSortOrder(role) {
	return role === "production" ? 0 : 1;
}
function rowMatchesScope(row, resolved) {
	if (row.class !== resolved.apiTarget.class) return false;
	if (resolved.apiTarget.branchId === null) return row.branchId === null;
	return row.branchId === null || row.branchId === resolved.apiTarget.branchId;
}
function materializeEffectiveRows(rows, resolved) {
	if (resolved.apiTarget.branchId === null) return rows;
	const byKey = /* @__PURE__ */ new Map();
	for (const row of rows) if (row.branchId === null && !byKey.has(row.key)) byKey.set(row.key, row);
	for (const row of rows) if (row.branchId === resolved.apiTarget.branchId) byKey.set(row.key, row);
	return [...byKey.values()].sort((left, right) => left.key.localeCompare(right.key));
}
//#endregion
//#region ../cli/src/controllers/app-env-file.ts
async function runEnvAddFile(context, client, projectId, resolved, filePath, assignments, verboseContext) {
	const existing = await findVariablesByNaturalKey(client, projectId, assignments.map((assignment) => assignment.key), resolved, context.runtime.signal);
	const existingKeys = assignments.map((assignment) => assignment.key).filter((key) => existing.has(key));
	if (existingKeys.length > 0) throw new CliStructuredError("PROJECT.ENV_VARIABLE_ALREADY_EXISTS", `${existingKeys.length} environment variable(s) already exist in ${formatScopeLabel(resolved.scope)}`, {
		why: `Existing keys: ${formatKeyList(existingKeys)}.`,
		meta: { keys: existingKeys },
		nextActions: [userChoice("Split the input file by key state: update existing keys and add new keys separately."), ...splitFileActions(filePath, resolved.scope, {
			existingKeys,
			first: "update-existing"
		})]
	});
	const warnings = await missingPreviewDefaultWarnings(client, projectId, resolved.scope, assignments.map((assignment) => assignment.key), context.runtime.signal);
	const variables = [];
	for (const assignment of assignments) try {
		const { data, error, response } = await client.POST("/v1/environment-variables", {
			body: {
				projectId,
				class: resolved.apiTarget.class,
				...resolved.apiTarget.branchId !== null ? { branchId: resolved.apiTarget.branchId } : {},
				key: assignment.key,
				value: assignment.value
			},
			signal: context.runtime.signal
		});
		if (error || !data) throw apiCallError(`Failed to add ${assignment.key}`, response, error);
		variables.push(toMetadata(data.data, resolved.descriptor));
	} catch (error) {
		throw envFileApplyFailedError("add", filePath, resolved.scope, assignment.key, variables, error);
	}
	return {
		command: "project.env.add",
		result: {
			projectId,
			verboseContext,
			scope: resolved.descriptor,
			variables,
			file: {
				path: filePath,
				count: variables.length
			}
		},
		warnings
	};
}
async function runEnvUpdateFile(context, client, projectId, resolved, filePath, assignments, verboseContext) {
	const existing = await findVariablesByNaturalKey(client, projectId, assignments.map((assignment) => assignment.key), resolved, context.runtime.signal);
	const missingKeys = assignments.map((assignment) => assignment.key).filter((key) => !existing.has(key));
	if (missingKeys.length > 0) throw new CliStructuredError("PROJECT.ENV_VARIABLE_NOT_FOUND", `${missingKeys.length} environment variable(s) not found in ${formatScopeLabel(resolved.scope)}`, {
		why: `Missing keys: ${formatKeyList(missingKeys)}.`,
		meta: { keys: missingKeys },
		nextActions: [userChoice("Split the input file by key state: add missing keys and update existing keys separately."), ...splitFileActions(filePath, resolved.scope, {
			missingKeys,
			first: "add-missing"
		})]
	});
	const variables = [];
	for (const assignment of assignments) {
		const existingVariable = existing.get(assignment.key);
		if (!existingVariable) continue;
		try {
			const { data, error, response } = await client.PATCH("/v1/environment-variables/{envVarId}", {
				params: { path: { envVarId: existingVariable.id } },
				body: { value: assignment.value },
				signal: context.runtime.signal
			});
			if (error || !data) throw apiCallError(`Failed to update value for ${assignment.key}`, response, error);
			variables.push(toMetadata(data.data, resolved.descriptor));
		} catch (error) {
			throw envFileApplyFailedError("update", filePath, resolved.scope, assignment.key, variables, error);
		}
	}
	return {
		command: "project.env.update",
		result: {
			projectId,
			verboseContext,
			scope: resolved.descriptor,
			variables,
			file: {
				path: filePath,
				count: variables.length
			}
		},
		warnings: []
	};
}
async function findVariablesByNaturalKey(client, projectId, keys, resolved, signal) {
	const found = /* @__PURE__ */ new Map();
	for (const key of keys) {
		const row = await findVariableByNaturalKey(client, projectId, key, resolved, signal);
		if (row) found.set(key, row);
	}
	return found;
}
async function missingPreviewDefaultWarnings(client, projectId, scope, keys, signal) {
	if (scope.kind !== "branch") return [];
	const previewScope = {
		scope: {
			kind: "role",
			role: "preview"
		},
		descriptor: {
			kind: "role",
			role: "preview"
		},
		apiTarget: {
			class: "preview",
			branchId: null
		}
	};
	const missing = [];
	for (const key of keys) if (!await findVariableByNaturalKey(client, projectId, key, previewScope, signal)) missing.push(key);
	if (missing.length === 0) return [];
	if (missing.length === 1) return [`Variable "${missing[0]}" does not exist in preview. It will only exist on ${formatScopeLabel(scope)}.`];
	return [`Variables ${formatKeyList(missing)} do not exist in preview. They will only exist on ${formatScopeLabel(scope)}.`];
}
function envFileApplyFailedError(command, filePath, scope, failedKey, writtenVariables, error) {
	const writtenKeys = writtenVariables.map((variable) => variable.key);
	const cause = error instanceof Error ? error.message : "Unknown error.";
	return new CliStructuredError("PROJECT.ENV_FILE_APPLY_FAILED", `Failed to ${command} "${failedKey}" from "${filePath}"`, {
		why: writtenKeys.length === 0 ? `No variables were written before ${failedKey} failed. Cause: ${cause}` : `Written keys before failure: ${formatKeyList(writtenKeys)}. Cause: ${cause}`,
		meta: {
			file: filePath,
			failedKey,
			writtenKeys
		},
		cause: error,
		nextActions: [
			userChoice("Inspect the target scope, then retry the remaining keys once the API issue is resolved."),
			runCommand(`prisma project env list ${formatScopeFlag(scope)}`),
			runCommand(retryStepForApplyFailure(command, filePath, scope, writtenKeys))
		]
	});
}
function retryStepForApplyFailure(command, filePath, scope, writtenKeys) {
	if (command === "update") return `prisma project env update --file ${filePath} ${formatScopeFlag(scope)}`;
	if (writtenKeys.length === 0) return `prisma project env add --file ${filePath} ${formatScopeFlag(scope)}`;
	return `prisma project env add --file <remaining.env> ${formatScopeFlag(scope)}`;
}
/** Each command carries the key list it applies to as its reason. */
function splitFileActions(filePath, scope, options) {
	const scopeFlag = formatScopeFlag(scope);
	const existingFile = `${filePath}.existing`;
	const newFile = `${filePath}.new`;
	if (options.first === "update-existing") return [runCommand(`prisma project env update --file ${existingFile} ${scopeFlag}`, `existing keys: ${formatKeyList(options.existingKeys)}`), runCommand(`prisma project env add --file ${newFile} ${scopeFlag}`, "new keys only")];
	return [runCommand(`prisma project env add --file ${newFile} ${scopeFlag}`, `missing keys: ${formatKeyList(options.missingKeys)}`), runCommand(`prisma project env update --file ${existingFile} ${scopeFlag}`, "existing keys only")];
}
function formatKeyList(keys) {
	return keys.map((key) => `"${key}"`).join(", ");
}
function formatScopeFlag(scope) {
	if (scope.kind === "role") return `--role ${scope.role}`;
	return `--branch ${scope.branchName}`;
}
//#endregion
//#region ../cli/src/presenters/verbose-context.ts
function stripVerboseContext(result) {
	const { verboseContext: _verboseContext, ...serialized } = result;
	return serialized;
}
//#endregion
//#region ../cli/src/presenters/app-env.ts
function scopeLabel(scope) {
	if (scope.kind === "role") return scope.role ?? "unknown";
	if (scope.kind === "overview") return "overview";
	return `branch:${scope.branchName ?? scope.branchId ?? "unknown"}`;
}
function listTargetLabel(result) {
	const target = result.target;
	if (target.source === "overview") return "overview";
	if (target.branchName) {
		const suffix = target.branchExists === false ? " (not created yet)" : "";
		return `branch:${target.branchName} -> ${target.envMap}${suffix}`;
	}
	return scopeLabel(result.scope);
}
function serializeEnvList(result) {
	const serializable = stripVerboseContext(result);
	return {
		projectId: serializable.projectId,
		scope: serializable.scope,
		target: serializable.target,
		...serializeList({
			context: { target: listTargetLabel(serializable) },
			items: serializable.variables.map((variable) => ({
				noun: "variable",
				label: `${variable.key} (${variable.source})`,
				id: variable.id,
				status: variable.isManagedBySystem ? "default" : null
			}))
		}),
		variables: serializable.variables
	};
}
//#endregion
//#region ../cli/src/commands/project/env-shared.ts
/** Flags, scope resolution and presentation shared by the
*  `project env *` commands. */
const roleFlag = flag.enum({
	brief: "Project-wide scope: production, or preview (shared by every preview branch)",
	values: ["production", "preview"]
});
const projectFlag = flag.string({
	brief: "Project id or name (default: the project this directory is linked to)",
	placeholder: "id-or-name"
});
const branchFlag = flag.string({
	brief: "Scope to one preview branch's override; use for values only that branch needs",
	placeholder: "git-name"
});
const fileFlag = flag.string({
	brief: "Read KEY=VALUE assignments from a dotenv file; use to import many variables at once",
	placeholder: "path"
});
function requireEnvScope(flags, command) {
	const scope = resolveEnvScope({
		roleName: flags.role,
		branchName: flags.branch
	}, {
		requireExplicit: true,
		command
	});
	if (!scope) throw envUsageError(`prisma project env ${command} requires --role or --branch`, "Writing without an explicit scope is rejected.", "Pass --role production, --role preview, or --branch <git-name>.", [`prisma project env ${command} KEY=value --role production`]);
	return scope;
}
/** Workspace, pinned project and the API scope every env write needs. */
async function resolveEnvTarget(ctx, flags, scope, commandName, createBranchIfMissing) {
	const workspace = await resolveActiveWorkspace(ctx);
	const target = await resolvePinnedProject(ctx, workspace, flags.project, commandName);
	const resolved = await resolveScopeToApi(ctx.api, target.project.id, scope, {
		createBranchIfMissing,
		signal: ctx.signal
	});
	return {
		projectId: target.project.id,
		verboseContext: {
			workspace,
			project: target.project,
			resolution: target.resolution
		},
		resolved
	};
}
/** The human table's rows: the first cell glues the key to where the
*  value comes from, which is what a reader wants to see. */
function variableRows(variables) {
	return variables.map((variable) => [
		`${variable.key} (${variable.source})`,
		variable.id,
		variable.isManagedBySystem ? "default" : ""
	]);
}
/** The stdout lane's rows: the bare key, because a consumer piping this
*  should not have to split on `" ("` to recover it (conventions §8 —
*  the stdout lane carries data, not decoration). The source is in the
*  `--json` record. */
function variableStdoutRows(variables) {
	return variables.map((variable) => [
		variable.key,
		variable.id,
		variable.isManagedBySystem ? "default" : ""
	]);
}
function variableFieldRows(projectId, scope, variable) {
	return [
		{
			label: "project",
			value: projectId
		},
		{
			label: "scope",
			value: scopeLabel(scope)
		},
		{
			label: "key",
			value: variable.key
		},
		{
			label: "id",
			value: variable.id
		},
		{
			label: "last updated",
			value: variable.updatedAt
		}
	];
}
function fileWritePresentations(input, result) {
	const rows = variableRows(input.variables);
	return {
		stdout: () => [],
		json: () => result,
		next: () => [],
		human: () => [
			{
				kind: "summary",
				status: "info",
				text: input.title
			},
			{
				kind: "fields",
				rows: [{
					label: "target",
					value: `${scopeLabel(input.scope)} from ${input.filePath}`
				}]
			},
			...rows.length === 0 ? [{
				kind: "list",
				items: [input.emptyMessage]
			}] : [{
				kind: "table",
				columns: [
					"variable",
					"id",
					"status"
				],
				rows
			}]
		]
	};
}
/** The legacy "the key has no preview default" warnings, which the engine
*  envelope carries as warn diagnostics. */
function previewDefaultDiagnostics(warnings) {
	return warnings.map((warning) => ({
		code: "PROJECT.ENV_PREVIEW_DEFAULT_MISSING",
		severity: "warn",
		summary: warning,
		nextActions: []
	}));
}
//#endregion
//#region ../cli/src/commands/project/env-add.ts
/** The `project env add` command. */
const TITLE$4 = "Setting a new environment variable.";
function singlePresentations$1(result) {
	return {
		stdout: () => [],
		json: () => result,
		next: () => [],
		human: () => [{
			kind: "summary",
			status: "info",
			text: TITLE$4
		}, {
			kind: "fields",
			rows: variableFieldRows(result.projectId, result.scope, result.variable)
		}]
	};
}
const projectEnvAddCommand = defineCommand({
	args: {
		positionals: { assignment: positional.optionalString({
			brief: "Variable assignment as KEY=VALUE or KEY from the current environment",
			placeholder: "assignment"
		}) },
		flags: {
			file: fileFlag,
			role: roleFlag,
			branch: branchFlag,
			project: projectFlag
		}
	},
	help: {
		summary: "Create an environment variable in one scope: production, preview, or one branch",
		description: "Variables reach a service's environment when it deploys. Writes always name their scope so production is never targeted by accident: --role production, --role preview (shared by every preview branch), or --branch for one branch's override. The value comes from KEY=VALUE, from the current shell environment when only KEY is given, or from a dotenv file with --file.",
		examples: [
			"project env add STRIPE_KEY=sk_test_xxx --role production",
			"project env add STRIPE_KEY=sk_test_xxx --role preview",
			"project env add --file .env --role preview",
			"project env add DATABASE_URL=postgresql://branch --branch feature/foo",
			"project env add --file .env.local --branch feature/foo",
			"API_URL=https://api.example prisma project env add API_URL --project proj_123 --role preview"
		]
	},
	needs: { credentials: true },
	handler: async (args, ctx) => {
		const source = resolveEnvWriteSource(args.positionals.assignment, args.flags.file, "add");
		const scope = requireEnvScope(args.flags, "add");
		const input = await resolveEnvWriteInput(operationContext(ctx), source, "add");
		const { projectId, verboseContext, resolved } = await resolveEnvTarget(ctx, args.flags, scope, "project env add", true);
		if (input.kind === "file") {
			const written = await runEnvAddFile(operationContext(ctx), ctx.api, projectId, resolved, input.filePath, input.assignments, verboseContext);
			const result = {
				projectId,
				scope: resolved.descriptor,
				variables: written.result.variables,
				file: written.result.file
			};
			return ok(ctx.present({
				data: result,
				diagnostics: previewDefaultDiagnostics(written.warnings)
			}, fileWritePresentations({
				title: "Setting new environment variables from file.",
				emptyMessage: "No environment variables imported.",
				scope: result.scope,
				filePath: result.file.path,
				variables: result.variables
			}, result)));
		}
		if (await findVariableByNaturalKey(ctx.api, projectId, input.key, resolved, ctx.signal)) throw new CliStructuredError("PROJECT.ENV_VARIABLE_ALREADY_EXISTS", `Variable "${input.key}" already exists in ${formatScopeLabel(scope)}`, {
			why: "A variable with this key already exists in the targeted scope.",
			nextActions: [userChoice("Use `prisma project env update` to change an existing variable's value."), runCommand(`prisma project env update ${input.key}=<new-value> ${formatScopeFlag$1(scope)}`)]
		});
		const warnings = scope.kind === "branch" && !await findVariableByNaturalKey(ctx.api, projectId, input.key, {
			descriptor: {
				kind: "role",
				role: "preview"
			},
			apiTarget: {
				class: "preview",
				branchId: null
			}
		}, ctx.signal) ? [`Variable "${input.key}" does not exist in preview. It will only exist on ${formatScopeLabel(scope)}.`] : [];
		const { data, error, response } = await ctx.api.POST("/v1/environment-variables", {
			body: {
				projectId,
				class: resolved.apiTarget.class,
				...resolved.apiTarget.branchId !== null ? { branchId: resolved.apiTarget.branchId } : {},
				key: input.key,
				value: input.value
			},
			signal: ctx.signal
		});
		if (error || !data) throw apiCallError(`Failed to add ${input.key}`, response, error);
		const result = {
			projectId,
			scope: resolved.descriptor,
			variable: toMetadata(data.data, resolved.descriptor)
		};
		return ok(ctx.present({
			data: result,
			diagnostics: previewDefaultDiagnostics(warnings)
		}, singlePresentations$1(result)));
	}
});
//#endregion
//#region ../cli/src/commands/project/env-delete.ts
/** The `project env delete` command. */
const TITLE$3 = "Deleting the environment variable from the scope.";
function deletePresentations$1(result) {
	return {
		stdout: () => [],
		json: () => result,
		next: () => [],
		human: () => [{
			kind: "summary",
			status: "info",
			text: TITLE$3
		}, {
			kind: "fields",
			rows: [
				{
					label: "project",
					value: result.projectId
				},
				{
					label: "scope",
					value: scopeLabel(result.scope)
				},
				{
					label: "key",
					value: result.key
				}
			]
		}]
	};
}
const projectEnvDeleteCommand = defineCommand({
	args: {
		positionals: { key: positional.string({
			brief: "Variable key to delete",
			placeholder: "key"
		}) },
		flags: {
			role: roleFlag,
			branch: branchFlag,
			project: projectFlag
		}
	},
	help: {
		summary: "Delete an environment variable from one scope: production, preview, or one branch",
		description: "Removes the variable from the named scope only; the same key in other scopes is untouched. Services lose the value on their next deploy.",
		examples: [
			"project env delete STRIPE_KEY --role production",
			"project env delete STRIPE_KEY --role preview",
			"project env delete DATABASE_URL --branch feature/foo"
		]
	},
	needs: { credentials: true },
	handler: async (args, ctx) => {
		const key = args.positionals.key;
		const scope = requireEnvScope(args.flags, "delete");
		const { projectId, resolved } = await resolveEnvTarget(ctx, args.flags, scope, "project env delete", false);
		const existing = await findVariableByNaturalKey(ctx.api, projectId, key, resolved, ctx.signal);
		if (!existing) throw new CliStructuredError("PROJECT.ENV_VARIABLE_NOT_FOUND", `Variable "${key}" not found in ${formatScopeLabel(scope)}`, {
			why: "No variable with this key exists in the targeted scope, so there is nothing to delete.",
			nextActions: [userChoice("Run prisma project env list with the same scope to see the available variables."), runCommand(`prisma project env list ${formatScopeFlag$1(scope)}`)]
		});
		const { error, response } = await ctx.api.DELETE("/v1/environment-variables/{envVarId}", {
			params: { path: { envVarId: existing.id } },
			signal: ctx.signal
		});
		if (error) throw apiCallError(`Failed to delete ${key}`, response, error);
		const result = {
			projectId,
			scope: resolved.descriptor,
			key
		};
		return ok(ctx.present({ data: result }, deletePresentations$1(result)));
	}
});
//#endregion
//#region ../cli/src/commands/project/env-list.ts
/** The `project env list` command. */
const TITLE$2 = "Listing environment variables for the selected scope.";
function listPresentations$2(result, addScopeFlag) {
	const rows = variableRows(result.variables);
	const stdoutRows = variableStdoutRows(result.variables);
	return {
		human: () => [
			{
				kind: "summary",
				status: "info",
				text: TITLE$2
			},
			{
				kind: "fields",
				rows: [{
					label: "target",
					value: listTargetLabel(result)
				}]
			},
			...rows.length === 0 ? [{
				kind: "summary",
				status: "info",
				text: "No environment variables defined in this scope."
			}] : [{
				kind: "table",
				columns: [
					"variable",
					"id",
					"status"
				],
				rows
			}]
		],
		stdout: () => stdoutRows.map((row) => row.join("	")),
		json: () => serializeEnvList(result),
		next: () => result.variables.length === 0 ? [{
			kind: "run-command",
			label: `${CLI_NAME} project env add KEY=value ${addScopeFlag}`,
			command: `${CLI_NAME} project env add KEY=value ${addScopeFlag}`
		}] : []
	};
}
const projectEnvListCommand = defineCommand({
	args: { flags: {
		role: roleFlag,
		branch: flag.string({
			brief: "Show what one preview branch resolves: the preview scope plus its overrides",
			placeholder: "git-name"
		}),
		project: projectFlag
	} },
	help: {
		summary: "List environment variables in a scope: names and ids, never values",
		description: "Values are write-only through the CLI: listing shows names, ids, and status, never values. Without a scope flag it shows an overview of every scope; --role narrows to the production or preview scope, and --branch shows what one preview branch resolves.",
		examples: [
			"project env list",
			"project env list --role production",
			"project env list --role preview",
			"project env list --branch feature/foo"
		]
	},
	needs: { credentials: true },
	handler: async (args, ctx) => {
		const explicit = resolveEnvScope({
			roleName: args.flags.role,
			branchName: args.flags.branch
		}, {
			requireExplicit: false,
			command: "list"
		});
		const projectId = (await resolvePinnedProject(ctx, await resolveActiveWorkspace(ctx), args.flags.project, "project env list")).project.id;
		const resolved = await resolveListScopeToApi(ctx.api, projectId, explicit ?? void 0, { signal: ctx.signal });
		const rows = resolved.kind === "scoped" ? await listVariables(ctx.api, projectId, {
			scope: resolved.addScope,
			descriptor: resolved.descriptor,
			apiTarget: resolved.apiTarget
		}, ctx.signal) : await listOverviewVariables(ctx.api, projectId, ctx.signal);
		const result = {
			projectId,
			scope: resolved.descriptor,
			target: resolved.target,
			variables: rows.map((row) => toMetadata(row, resolved.descriptor))
		};
		return ok(ctx.present({ data: result }, listPresentations$2(result, formatScopeFlag$1(resolved.addScope))));
	}
});
//#endregion
//#region ../cli/src/commands/project/env-update.ts
/** The `project env update` command. */
const TITLE$1 = "Replacing the environment variable's value.";
function singlePresentations(result) {
	return {
		stdout: () => [],
		json: () => result,
		next: () => [],
		human: () => [{
			kind: "summary",
			status: "info",
			text: TITLE$1
		}, {
			kind: "fields",
			rows: variableFieldRows(result.projectId, result.scope, result.variable)
		}]
	};
}
const projectEnvUpdateCommand = defineCommand({
	args: {
		positionals: { assignment: positional.optionalString({
			brief: "Variable assignment as KEY=VALUE or KEY from the current environment",
			placeholder: "assignment"
		}) },
		flags: {
			file: fileFlag,
			role: roleFlag,
			branch: branchFlag,
			project: projectFlag
		}
	},
	help: {
		summary: "Replace an existing environment variable's value",
		description: "Overwrites one variable's value in one scope; the variable must already exist there (use 'project env add' to create it). With --file, overwrites every variable the dotenv file lists. New values reach services on their next deploy.",
		examples: [
			"project env update STRIPE_KEY=sk_new_xxx --role production",
			"project env update STRIPE_KEY=sk_new_xxx --role preview",
			"project env update --file .env --role production",
			"project env update DATABASE_URL=postgresql://branch --branch feature/foo"
		]
	},
	needs: { credentials: true },
	handler: async (args, ctx) => {
		const source = resolveEnvWriteSource(args.positionals.assignment, args.flags.file, "update");
		const scope = requireEnvScope(args.flags, "update");
		const input = await resolveEnvWriteInput(operationContext(ctx), source, "update");
		const { projectId, verboseContext, resolved } = await resolveEnvTarget(ctx, args.flags, scope, "project env update", false);
		if (input.kind === "file") {
			const written = await runEnvUpdateFile(operationContext(ctx), ctx.api, projectId, resolved, input.filePath, input.assignments, verboseContext);
			const result = {
				projectId,
				scope: resolved.descriptor,
				variables: written.result.variables,
				file: written.result.file
			};
			return ok(ctx.present({ data: result }, fileWritePresentations({
				title: "Replacing environment variable values from file.",
				emptyMessage: "No environment variables updated.",
				scope: result.scope,
				filePath: result.file.path,
				variables: result.variables
			}, result)));
		}
		const existing = await findVariableByNaturalKey(ctx.api, projectId, input.key, resolved, ctx.signal);
		if (!existing) throw new CliStructuredError("PROJECT.ENV_VARIABLE_NOT_FOUND", `Variable "${input.key}" not found in ${formatScopeLabel(scope)}`, {
			why: "No variable with this key exists in the targeted scope.",
			nextActions: [userChoice("Use `prisma project env add` to create a new variable."), runCommand(`prisma project env add ${input.key}=<value> ${formatScopeFlag$1(scope)}`)]
		});
		const { data, error, response } = await ctx.api.PATCH("/v1/environment-variables/{envVarId}", {
			params: { path: { envVarId: existing.id } },
			body: { value: input.value },
			signal: ctx.signal
		});
		if (error || !data) throw apiCallError(`Failed to update value for ${input.key}`, response, error);
		const result = {
			projectId,
			scope: resolved.descriptor,
			variable: toMetadata(data.data, resolved.descriptor)
		};
		return ok(ctx.present({ data: result }, singlePresentations(result)));
	}
});
//#endregion
//#region ../cli/src/commands/project/link.ts
/** The `project link` command. */
const CREATE_CHOICE = "__create__";
const CANCEL_CHOICE = "__cancel__";
function setupCanceledError() {
	return new CliStructuredError("PROJECT.USAGE_ERROR", "Project setup canceled", {
		why: "Project link needs a Project before it can continue.",
		nextActions: [
			{
				kind: "user-choice",
				label: "Choose an existing Project or create a new one, then rerun project link."
			},
			{
				kind: "run-command",
				label: "prisma project link <id-or-name>",
				command: "prisma project link <id-or-name>"
			},
			{
				kind: "run-command",
				label: "prisma project create <name>",
				command: "prisma project create <name>"
			}
		]
	});
}
function choiceOptions(projects) {
	const sorted = sortProjects(projects);
	const duplicated = new Set(sorted.map((project) => project.name).filter((name, index, names) => names.indexOf(name) !== index));
	return [
		{
			value: CREATE_CHOICE,
			label: "+ Create a new Project"
		},
		...sorted.map((project) => ({
			value: project.id,
			label: duplicated.has(project.name) ? `${project.name} (${project.id})` : project.name
		})),
		{
			value: CANCEL_CHOICE,
			label: "Cancel"
		}
	];
}
async function createProjectForLink(ctx, workspace, projectName) {
	const created = await createAppProvider(ctx.api).createProject({
		name: projectName,
		signal: ctx.signal
	}).catch((error) => {
		/** A cancelled run is cancelled, not a failed creation. The
		*  provider flattens the underlying AbortError into a plain
		*  Error, which the engine would settle as a bug, so hand it
		*  back its own abort reason and let it settle the run as
		*  cancelled. */
		if (ctx.signal.aborted) throw ctx.signal.reason;
		throw projectCreateFailedError(error, projectName, workspace, {
			nextSteps: [
				"prisma project list",
				"prisma project link <id-or-name>",
				`prisma project create ${formatCommandArgument(projectName)}`
			],
			permissionFix: "Grant the token permission to create Projects in this workspace, or link an existing Project.",
			fallbackFix: "Retry the command, or choose an existing Project with prisma project link <id-or-name>."
		});
	});
	return {
		id: created.id,
		name: created.name
	};
}
async function pickProject(ctx, workspace, projects) {
	const choice = await ctx.prompt.select("Which Project should this directory use?", choiceOptions(projects));
	if (choice === CANCEL_CHOICE) throw setupCanceledError();
	if (choice === CREATE_CHOICE) {
		const suggested = await inferTargetName(ctx.cwd, ctx.signal);
		const name = await ctx.prompt.text("Project name", {
			placeholder: suggested.name,
			default: suggested.name
		});
		if (!isValidProjectSetupName(name)) throw projectSetupNameRequiredError("project link");
		return await bindDirectoryToProject(ctx, workspace, await createProjectForLink(ctx, workspace, name.trim()), "created");
	}
	const project = projects.find((candidate) => candidate.id === choice);
	if (!project) throw setupCanceledError();
	return await bindDirectoryToProject(ctx, workspace, toProjectSummary(project), "linked");
}
/** The link itself, without the command around it: resolve the named
*  Project or pick one, then bind `ctx.cwd` to it. */
async function linkDirectoryToProject(ctx, projectRef) {
	const workspace = await resolveActiveWorkspace(ctx);
	const projects = await listWorkspaceProjects$1(ctx);
	const ref = projectRef?.trim();
	return ref ? await bindDirectoryToProject(ctx, workspace, toProjectSummary(resolveProjectForSetup(ref, projects, workspace)), "linked") : await pickProject(ctx, workspace, projects);
}
const projectLinkCommand = defineCommand({
	args: { positionals: { project: positional.optionalString({
		brief: "Project id or name",
		placeholder: "id-or-name"
	}) } },
	help: {
		summary: "Link this directory to a Project: commands run here target it by default",
		description: "Records locally that this directory belongs to an existing Project, so later commands resolve it without --project. Linking itself changes local state only. Run without an argument to pick from your workspace's projects; that picker also offers creating a new Project, which does create one on the platform.",
		examples: [
			"project link",
			"project link proj_123",
			"project link \"Acme Dashboard\" --json"
		]
	},
	needs: { credentials: true },
	handler: async (args, ctx) => {
		const result = await linkDirectoryToProject(ctx, args.positionals.project);
		return ok(ctx.present({ data: result }, setupPresentations(result)));
	}
});
//#endregion
//#region ../cli/src/commands/project/list.ts
/** The `project list` command. */
const TITLE = "Listing projects for the authenticated workspace.";
/** An absent region stays empty; the table renderer draws the dim
*  placeholder dash. */
function projectRows(result) {
	return result.projects.map((project) => [
		project.name,
		project.id,
		project.defaultRegion ?? ""
	]);
}
/** The stdout rows: a project with no default region has an empty
*  region field, not the word the human table shows. */
function projectStdoutRows(result) {
	return result.projects.map((project) => [
		project.name,
		project.id,
		project.defaultRegion ?? ""
	]);
}
function nextActionsFor(result) {
	if (result.localBinding?.status === "linked") return [];
	return buildProjectSetupNextActions({
		createCommand: `${CLI_NAME} project create <name>`,
		reason: result.localBinding?.status === "invalid" ? "This directory has an invalid local Project binding. Ask the user which Prisma Project to link before running Project-scoped commands." : "This directory is not linked to a Prisma Project. Project list shows available Projects, but none is selected for this directory."
	});
}
function listPresentations$1(result) {
	const rows = projectRows(result);
	const stdoutRows = projectStdoutRows(result);
	return {
		human: () => [
			{
				kind: "summary",
				status: "info",
				text: TITLE
			},
			{
				kind: "fields",
				rows: [{
					label: "workspace",
					value: result.workspace.name
				}]
			},
			...rows.length === 0 ? [{
				kind: "summary",
				status: "info",
				text: "No projects found."
			}] : [{
				kind: "table",
				columns: [
					"name",
					"id",
					"region"
				],
				rows
			}]
		],
		stdout: () => stdoutRows.map((row) => row.join("	")),
		json: () => serializeProjectList(result),
		next: () => nextActionsFor(result)
	};
}
const projectListCommand = defineCommand({
	help: {
		summary: "List all projects in your workspace",
		description: "A Project groups one product or codebase. It is the child of a workspace and the parent of Branches: isolated environments that hold your services, databases, and buckets.",
		examples: ["project list", "project list --json"]
	},
	needs: { credentials: true },
	handler: async (_args, ctx) => {
		const workspace = await resolveActiveWorkspace(ctx);
		const projects = sortProjects(await listWorkspaceProjects$1(ctx));
		const localBinding = await readProjectListLocalBinding(ctx.cwd, projects, ctx.signal);
		const result = {
			workspace,
			projects: projects.map(toProjectSummary),
			localBinding
		};
		return ok(ctx.present({ data: result }, listPresentations$1(result)));
	}
});
//#endregion
//#region ../cli/src/commands/project/rename.ts
/** The `project rename` command. */
function renamePresentations(result) {
	return {
		stdout: () => [],
		json: () => result,
		next: () => [],
		human: () => [
			{
				kind: "summary",
				status: "ok",
				text: "Renaming project."
			},
			{
				kind: "fields",
				rows: [
					{
						label: "workspace",
						value: result.workspace.name
					},
					{
						label: "project",
						value: result.previousName
					},
					{
						label: "id",
						value: result.project.id
					}
				]
			},
			{
				kind: "list",
				items: [`The project is now named "${result.project.name}". Directory bindings pin the project id, so they stay valid.`]
			}
		]
	};
}
const projectRenameCommand = defineCommand({
	args: {
		positionals: { name: positional.string({
			brief: "New project name",
			placeholder: "name"
		}) },
		flags: { project: flag.string({
			brief: "Project id or name (default: the project this directory is linked to)",
			placeholder: "id-or-name"
		}) }
	},
	help: {
		summary: "Rename a Project",
		description: "Changes the display name of the linked project, or of the one named by --project. The project id stays the same, so existing links and automation keep working.",
		examples: ["project rename \"Acme Dashboard v2\"", "project rename billing-api --project proj_123"]
	},
	needs: { credentials: true },
	handler: async (args, ctx) => {
		const workspace = await resolveActiveWorkspace(ctx);
		const name = args.positionals.name.trim();
		if (!isValidProjectSetupName(name)) throw projectSetupNameRequiredError("project rename");
		const target = await resolvePinnedProject(ctx, workspace, args.flags.project, "project rename");
		const result = {
			workspace,
			project: await createManagementProjectProvider(ctx.api).renameProject({
				projectId: target.project.id,
				name,
				signal: ctx.signal
			}),
			previousName: target.project.name
		};
		return ok(ctx.present({ data: result }, renamePresentations(result)));
	}
});
//#endregion
//#region ../cli/src/lib/fs/home-path.ts
/**
* Shortens a path under the user's home directory to `~/...` for display,
* posix-style on every platform. Falls back to the Windows home variables
* when `HOME` is unset (native cmd/PowerShell sessions).
*/
function shortenHomePath(value, env) {
	const resolved = path.resolve(value);
	const home = resolveHomeDirectory(env);
	if (home && (resolved === home || resolved.startsWith(`${home}${path.sep}`))) {
		const relative = path.relative(home, resolved).split(path.sep).join("/");
		return relative ? `~/${relative}` : "~";
	}
	return resolved;
}
function resolveHomeDirectory(env) {
	if (env.HOME) return path.resolve(env.HOME);
	if (env.USERPROFILE) return path.resolve(env.USERPROFILE);
	if (env.HOMEDRIVE && env.HOMEPATH) return path.resolve(`${env.HOMEDRIVE}${env.HOMEPATH}`);
	return null;
}
//#endregion
//#region ../cli/src/commands/project/show.ts
/** The `project show` command. */
function fieldRows(result, cwd, env) {
	if (result.project === null) return [{
		label: "workspace",
		value: result.workspace.name
	}, {
		label: "project",
		value: "Not linked"
	}];
	return [
		{
			label: "local repo",
			value: shortenHomePath(cwd, env)
		},
		{
			label: "platform",
			value: `${result.workspace.name} / ${result.project.name}`
		},
		...result.project.url ? [{
			label: "url",
			value: result.project.url
		}] : [],
		...result.project.defaultRegion ? [{
			label: "region",
			value: result.project.defaultRegion
		}] : []
	];
}
/** The stdout mirror. Three human affordances stay on the human side:
*  the home directory shortened to `~`, the workspace and project glued
*  into one "platform" line, and the words "Not linked" standing in for
*  an absent project. stdout gets the raw path and one fact per line,
*  under the labels this same command already uses when the directory
*  is not linked. */
function stdoutFieldRows(result, cwd) {
	return [
		{
			label: "local repo",
			value: cwd
		},
		{
			label: "workspace",
			value: result.workspace.name
		},
		{
			label: "project",
			value: result.project?.name ?? ""
		},
		...result.project?.url ? [{
			label: "url",
			value: result.project.url
		}] : [],
		...result.project?.defaultRegion ? [{
			label: "region",
			value: result.project.defaultRegion
		}] : []
	];
}
function showPresentations$1(result, cwd, env) {
	const rows = fieldRows(result, cwd, env);
	return {
		json: () => result,
		human: () => [result.project === null ? {
			kind: "summary",
			status: "warn",
			text: "This directory is not linked to a Prisma Project."
		} : {
			kind: "summary",
			status: "info",
			text: result.resolution.projectSource === "explicit" ? "Showing the named project (this directory's own link, if any, is unchanged)." : "This directory is linked to the following platform project."
		}, {
			kind: "fields",
			rows
		}],
		stdout: () => stdoutFieldRows(result, cwd).map((row) => `${row.label}: ${row.value}`),
		next: () => result.project === null ? buildProjectSetupNextActions({
			commandName: "project show",
			retryCommand: "prisma project show <id-or-name>",
			suggestedProjectName: result.suggestedProjectName,
			reason: "This directory is not linked to a Prisma Project. Package and directory names can suggest setup defaults, but they do not select a Project."
		}) : []
	};
}
const projectShowCommand = defineCommand({
	args: { positionals: { project: positional.optionalString({
		brief: "Project id or name (default: the project this directory is linked to)",
		placeholder: "id-or-name"
	}) } },
	help: {
		summary: "Show which Project this directory is linked to",
		description: "Reports which Project this directory is linked to, or that it is not linked, with next steps. Pass an id or name to inspect any project without changing the local link.",
		examples: ["project show", "project show proj_123 --json"]
	},
	needs: { credentials: true },
	handler: async (args, ctx) => {
		const workspace = await resolveActiveWorkspace(ctx);
		const inspected = await inspectProjectBinding({
			context: operationContext(ctx),
			workspace,
			explicitProject: args.positionals.project,
			listProjects: () => listWorkspaceProjects$1(ctx),
			commandName: "project show"
		});
		if (inspected.isErr()) throw projectResolutionErrorToStructured(inspected.error);
		const result = inspected.value;
		return ok(ctx.present({ data: result }, showPresentations$1(result, ctx.cwd, ctx.env)));
	}
});
//#endregion
//#region ../cli/src/auth/errors.ts
function actions(fix, commands) {
	return [{
		kind: "user-choice",
		label: fix
	}, ...commands.map((command) => ({
		kind: "run-command",
		label: command,
		command
	}))];
}
function workspaceNotAuthenticatedError(workspaceRef) {
	return new CliStructuredError("AUTH.WORKSPACE_NOT_AUTHENTICATED", "Workspace is not authenticated", {
		why: `No stored OAuth session matched "${workspaceRef}".`,
		meta: { workspaceRef },
		nextActions: actions(`Run ${CLI_NAME} auth login and authorize that workspace, then switch to it.`, [`${CLI_NAME} auth workspace list`, `${CLI_NAME} auth login`])
	});
}
function workspaceAmbiguousError(workspaceRef, matches) {
	return new CliStructuredError("AUTH.WORKSPACE_AMBIGUOUS", "Workspace name is ambiguous", {
		why: `Multiple authenticated workspaces matched "${workspaceRef}".`,
		meta: {
			workspaceRef,
			matches
		},
		nextActions: actions(`Run ${CLI_NAME} auth workspace list and switch by workspace id.`, [`${CLI_NAME} auth workspace list`])
	});
}
//#endregion
//#region ../cli/src/auth/recipient.ts
var RecipientSessionInvalidError = class extends Error {
	constructor(workspaceRef) {
		super(`The stored session for workspace "${workspaceRef}" could not be validated.`);
		this.workspaceRef = workspaceRef;
		this.name = "RecipientSessionInvalidError";
	}
};
/**
* Resolve a locally stored OAuth workspace session and return a validated
* access token for it, refreshing through the SDK when the stored token has
* expired. The active workspace pointer is never touched.
*
* Throws WorkspaceSelectionError when the ref does not match exactly one
* stored session, and RecipientSessionInvalidError when the session cannot
* be validated or refreshed.
*/
async function resolveRecipientWorkspaceSession(workspaceRef, env = process.env, signal) {
	const workspace = await new FileTokenStorage(env, signal).resolveWorkspace(workspaceRef);
	const pinnedStorage = new FileTokenStorage(env, signal, {
		activateOnSetTokens: false,
		lockSetTokens: false,
		pinnedWorkspaceId: workspace.credentialWorkspaceId
	});
	if ((await createManagementApiSdk({
		clientId: "cmm3lndn701oo0uefvxzo0ivw",
		redirectUri: "http://localhost:0/auth/callback",
		tokenStorage: pinnedStorage,
		apiBaseUrl: getApiBaseUrl(env)
	}).client.GET("/v1/workspaces", { signal })).error) throw new RecipientSessionInvalidError(workspaceRef);
	const tokens = await pinnedStorage.getTokens();
	if (!tokens) throw new RecipientSessionInvalidError(workspaceRef);
	return {
		workspace,
		accessToken: tokens.accessToken
	};
}
//#endregion
//#region ../cli/src/commands/project/transfer.ts
/** The `project transfer` command. */
const CONSENT_QUESTION = "Transferring moves the project to another workspace and this workspace loses access, so it requires the exact project id.";
/** This CLI's command strings are `${CLI_NAME} …`; the legacy package-runner
*  formatter does not port. */
const formatCommand = (args) => [CLI_NAME, ...args].join(" ");
function recipientSourceError(workspaceRef, error) {
	if (error instanceof WorkspaceSelectionError) {
		if (error.reason === "ambiguous") throw workspaceAmbiguousError(error.workspaceRef ?? workspaceRef, error.matches.map((match) => ({
			id: match.id,
			name: match.name,
			credentialWorkspaceId: match.credentialWorkspaceId
		})));
		throw workspaceNotAuthenticatedError(error.workspaceRef ?? workspaceRef);
	}
	if (error instanceof RecipientSessionInvalidError) throw workspaceNotAuthenticatedError(error.workspaceRef);
	throw error;
}
async function resolveRecipient(ctx, options) {
	const recipientToken = options.recipientToken?.trim();
	if (recipientToken) return {
		accessToken: recipientToken,
		workspaceId: null,
		workspaceName: null,
		source: "recipient-token"
	};
	/** The handler rejects a run carrying neither recipient flag before
	*  it reaches here, so a blank `--to-workspace` means the recipient
	*  token was set and returned above. */
	const workspaceRef = options.toWorkspace?.trim() ?? "";
	if (ctx.env[SERVICE_TOKEN_ENV_VAR] !== void 0) throw transferRecipientUnavailableError(formatCommand);
	try {
		const session = await resolveRecipientWorkspaceSession(workspaceRef, ctx.env, ctx.signal);
		return {
			accessToken: session.accessToken,
			workspaceId: session.workspace.id,
			workspaceName: session.workspace.name,
			source: "workspace-session"
		};
	} catch (error) {
		recipientSourceError(workspaceRef, error);
	}
}
function transferPresentations(result, toWorkspace) {
	return {
		stdout: () => [],
		json: () => result,
		human: () => [
			{
				kind: "summary",
				status: "ok",
				text: "Transferring project."
			},
			{
				kind: "fields",
				rows: [
					{
						label: "workspace",
						value: result.workspace.name
					},
					{
						label: "project",
						value: result.project.name
					},
					{
						label: "id",
						value: result.project.id
					},
					{
						label: "recipient",
						value: result.recipient.workspaceName ?? result.recipient.workspaceId ?? "workspace of the provided recipient token"
					}
				]
			},
			{
				kind: "list",
				items: [
					"The project now belongs to the recipient workspace; this workspace no longer has access.",
					...result.localPin.action === "rewritten" ? ["This directory's local project binding now points at the recipient workspace."] : [],
					...result.localPin.action === "cleared" ? ["This directory's local project binding was cleared."] : []
				]
			}
		],
		next: () => toWorkspace ? [{
			kind: "run-command",
			label: `${CLI_NAME} auth workspace use ${formatCommandArgument(toWorkspace)}`,
			command: `${CLI_NAME} auth workspace use ${formatCommandArgument(toWorkspace)}`
		}] : []
	};
}
const projectTransferCommand = defineCommand({
	args: {
		positionals: { project: positional.string({
			brief: "Project id or name",
			placeholder: "id-or-name"
		}) },
		flags: {
			toWorkspace: flag.string({
				brief: "Receiving workspace, when it is one of your own stored sessions",
				placeholder: "id-or-name"
			}),
			recipientToken: flag.string({
				brief: "Access token for the receiving workspace, when you are not a member of it",
				placeholder: "token"
			})
		}
	},
	help: {
		summary: "Transfer a Project to another workspace after exact id confirmation",
		description: "Moves a project, with everything in it, out of the current workspace. Name the receiving workspace with --to-workspace when you are logged in to it too, or pass --recipient-token when someone else owns it. The exact project id is the consent token.",
		examples: ["project transfer proj_123 --to-workspace \"Prisma Labs\" --confirm proj_123", "project transfer proj_123 --recipient-token <token> --confirm proj_123"]
	},
	needs: { credentials: true },
	handler: async (args, ctx) => {
		const workspace = await resolveActiveWorkspace(ctx);
		const toWorkspace = args.flags.toWorkspace?.trim() || void 0;
		const recipientToken = args.flags.recipientToken?.trim() || void 0;
		if (toWorkspace && recipientToken) {
			const retry = formatCommand([
				"project",
				"transfer",
				"<project>",
				"--to-workspace",
				"<id-or-name>",
				"--confirm",
				"<project-id>"
			]);
			throw new CliStructuredError("PROJECT.USAGE_ERROR", "Choose one transfer recipient source", {
				why: "--to-workspace and --recipient-token are mutually exclusive.",
				nextActions: [{
					kind: "user-choice",
					label: "Pass either --to-workspace <id-or-name> or --recipient-token <token>."
				}, {
					kind: "run-command",
					label: retry,
					command: retry
				}]
			});
		}
		if (!toWorkspace && !recipientToken) throw transferRecipientRequiredError(formatCommand);
		const projects = await listWorkspaceProjects$1(ctx);
		const project = toProjectSummary(resolveProjectForSetup(args.positionals.project.trim(), projects, workspace));
		await ctx.prompt.consent(CONSENT_QUESTION, { token: project.id });
		const recipient = await resolveRecipient(ctx, {
			toWorkspace,
			recipientToken
		});
		await createManagementProjectProvider(ctx.api).transferProject({
			projectId: project.id,
			recipientAccessToken: recipient.accessToken,
			signal: ctx.signal
		});
		const warnings = [];
		const action = await rewriteOrClearLocalPinForProject(operationContext(ctx), project.id, recipient.workspaceId, { onError: (message) => warnings.push(message) });
		const result = {
			workspace,
			project,
			recipient: {
				workspaceId: recipient.workspaceId,
				workspaceName: recipient.workspaceName,
				source: recipient.source
			},
			localPin: { action }
		};
		const diagnostics = localPinDiagnostics(warnings);
		return ok(ctx.present({
			data: result,
			diagnostics
		}, transferPresentations(result, toWorkspace)));
	}
});
//#endregion
//#region ../cli/src/lib/app/domain-guidance.ts
function formatDomainFailureFix(domain) {
	if (domain.status !== "failed") return null;
	const dnsRecord = domain.dnsRecords[0];
	if (domain.failureCategory === "dns") {
		if (dnsRecord) return `Add ${dnsRecord.type} ${dnsRecord.name} -> ${dnsRecord.value}, then run prisma service domain retry ${domain.hostname}.`;
		return `DNS verification failed, but the platform did not return a DNS record. Run prisma service domain show ${domain.hostname} later, then retry when the DNS target is available.`;
	}
	if (domain.failureCategory === "acme") return `Retry TLS issuance with prisma service domain retry ${domain.hostname}. Contact support if it fails again.`;
	if (domain.failureCategory === "storage") return `Retry provisioning with prisma service domain retry ${domain.hostname}. Contact support if it fails again.`;
	return `Run prisma service domain retry ${domain.hostname}. Contact support if it fails again.`;
}
//#endregion
//#region ../cli/src/commands/service/errors.ts
function runCommandAction(label, command) {
	return {
		kind: "run-command",
		label,
		command: `${CLI_NAME} ${command}`
	};
}
function adviceAction(label) {
	return {
		kind: "user-choice",
		label
	};
}
const CNAME_HINT = /\bcname(?:s)?\s+to\b/;
const PRISMA_BUILD_HOST = /\b((?:[a-z0-9-]+\.)+prisma\.build)\b/i;
/**
* Consent declined interactively. The engine settles this code as a
* user cancellation (exit 3).
*/
function userCancelledError(summary) {
	return new CliStructuredError("CLI.PROMPT_CANCELLED", summary);
}
function workspaceRequiredError() {
	return new CliStructuredError("SERVICE.WORKSPACE_REQUIRED", "No workspace is selected for the current session", {
		why: "Platform commands need an authenticated session with an accessible workspace.",
		nextActions: [runCommandAction("Sign in", "auth login")]
	});
}
function serviceSelectionInvalidError(serviceName, projectId) {
	return new CliStructuredError("SERVICE.SELECTION_INVALID", "The requested service does not exist in the resolved project", {
		why: `The service "${serviceName}" could not be found in resolved project "${projectId}".`,
		nextActions: [adviceAction("Pass the id or name of an existing service."), runCommandAction("List services", "service list")]
	});
}
function serviceNameRequiredError() {
	return new CliStructuredError("SERVICE.NAME_REQUIRED", "Service create requires a name", {
		why: "The name positional was empty or only whitespace.",
		nextActions: [adviceAction("Pass a name, as in service create my-service."), runCommandAction("List services", "service list")]
	});
}
function projectNotFoundError(projectId) {
	return new CliStructuredError("PROJECT.NOT_FOUND", "Project not found", {
		why: `The resolved project "${projectId}" does not exist in the authenticated workspace or is no longer accessible.`,
		nextActions: [runCommandAction("Inspect the directory binding", "project show"), runCommandAction("Link a project", "project link <id-or-name>")]
	});
}
function deployFailedError(summary, cause, nextActions) {
	return new CliStructuredError("SERVICE.DEPLOY_FAILED", summary, {
		why: cause instanceof Error ? cause.message : String(cause),
		nextActions,
		cause
	});
}
function noVersionsError(summary, why, serviceName) {
	return new CliStructuredError("SERVICE.NO_VERSIONS", summary, {
		why,
		nextActions: [runCommandAction("Inspect the service", `service show ${serviceName}`)]
	});
}
function versionNotFoundError(deploymentId) {
	return new CliStructuredError("SERVICE.VERSION_NOT_FOUND", `Version "${deploymentId}" not found`, {
		why: "The requested service version does not exist or is no longer available.",
		nextActions: [runCommandAction("Choose an available version id", "service version list <service>")]
	});
}
/** `--tail` and `--from-start` ask for opposite ends of the log, so a
*  run naming both has no answer to give. Refused before any work. */
function logsRangeConflictError() {
	return new CliStructuredError("SERVICE.LOGS_RANGE_CONFLICT", "Choose one end of the log to read from", {
		why: "--tail and --from-start are mutually exclusive: one reads the last lines, the other reads from the beginning.",
		nextActions: [adviceAction("Pass --tail <n> for the last n lines, or --from-start for the whole log.")]
	});
}
/** The version exists but names no owning service, so there is
*  nothing to report or act on it as. */
function versionDetachedError(deploymentId) {
	return new CliStructuredError("SERVICE.VERSION_DETACHED", `Version "${deploymentId}" has no owning service`, {
		why: "The Management API returned the version without a service, so there is nothing to report or act on it as.",
		nextActions: [runCommandAction("Show the version", `service version show ${deploymentId}`)]
	});
}
function versionNotFoundForServiceError(deploymentId, serviceName) {
	return new CliStructuredError("SERVICE.VERSION_NOT_FOUND", `Version "${deploymentId}" not found for service "${serviceName}"`, {
		why: "The requested version does not belong to the resolved service or is no longer available.",
		nextActions: [runCommandAction("Choose an available version id", `service version list ${serviceName}`)]
	});
}
/** Every command that acts on an existing service needs its target
*  named explicitly; nothing is inferred, remembered, or prompted for. */
function serviceTargetRequiredError(commandName) {
	return new CliStructuredError("SERVICE.TARGET_REQUIRED", `Command "${commandName}" requires a service`, {
		why: "Service commands act only on an explicitly named service, and this run named none.",
		nextActions: [adviceAction("Pass the service id or name as the first argument."), runCommandAction("List services", "service list")]
	});
}
function noPreviousVersionError(serviceName) {
	return new CliStructuredError("SERVICE.NO_PREVIOUS_VERSION", "No previous version available for rollback", {
		why: "The service does not have an earlier version to switch back to.",
		nextActions: [adviceAction("Deploy a second version first, or pass --to <version-id> for a specific earlier version."), runCommandAction("List versions", `service version list ${serviceName}`)]
	});
}
/** Rolling back without `--to` needs the live deployment: the default
*  target is defined relative to it. */
function liveVersionUnknownError(serviceName) {
	return new CliStructuredError("SERVICE.LIVE_VERSION_UNKNOWN", "Cannot determine which version is currently live", {
		why: "The service record does not name a live version, so the version to roll back to cannot be chosen without guessing what production is serving.",
		nextActions: [runCommandAction("Roll back to a named version", `service version rollback ${serviceName} --to <version>`), runCommandAction("List versions", `service version list ${serviceName}`)]
	});
}
function deleteFailedError(summary, cause, serviceName) {
	return new CliStructuredError("SERVICE.DELETE_FAILED", summary, {
		why: cause instanceof Error ? cause.message : String(cause),
		nextActions: [runCommandAction("Inspect the service", `service show ${serviceName}`), runCommandAction("List versions", `service version list ${serviceName}`)],
		cause
	});
}
/** A blank `--branch` names no branch and must never fall through to
*  the branch the command targets when the flag is omitted. */
function branchValueEmptyError() {
	return new CliStructuredError("SERVICE.BRANCH_INVALID", "The --branch value cannot be empty", {
		why: "The command scopes its work to the given branch; an empty --branch names none, and omitting the flag targets the default branch instead.",
		nextActions: [adviceAction("Pass a non-empty branch name, or omit --branch to target the default branch.")]
	});
}
function liveUrlUnavailableError(serviceName) {
	return new CliStructuredError("SERVICE.FEATURE_UNAVAILABLE", "Live URL is not available for this service", {
		why: "Versions exist, but the provider does not expose a stable live service URL for this service yet.",
		nextActions: [runCommandAction("Inspect the service state", `service show ${serviceName}`)]
	});
}
function branchNotDeployableError(branchName) {
	return new CliStructuredError("SERVICE.BRANCH_NOT_DEPLOYABLE", "Custom domains require the production branch", {
		why: `Custom domains on preview branch "${branchName}" are not supported in Public Beta.`,
		nextActions: [adviceAction("Use --branch production, or attach the domain after promoting/deploying to the production branch."), runCommandAction("Add on production", "service domain add <hostname> --service <name> --branch production")]
	});
}
function domainHostnameInvalidError(hostname, why) {
	return new CliStructuredError("SERVICE.DOMAIN_HOSTNAME_INVALID", `Invalid custom domain "${hostname}"`, {
		why: why ?? "Custom domains must be valid hostnames without protocol, path, wildcard, or port.",
		nextActions: [adviceAction("Pass a hostname like shop.acme.com."), runCommandAction("Add a domain", "service domain add shop.acme.com --service <name>")]
	});
}
function domainNotFoundError(hostname) {
	return new CliStructuredError("SERVICE.DOMAIN_NOT_FOUND", `Custom domain "${hostname}" not found`, {
		why: "The hostname is not attached to the service.",
		nextActions: [adviceAction("Check the hostname and the service, or add the domain first."), runCommandAction("Add the domain", `service domain add ${hostname} --service <name>`)]
	});
}
function formatDomainFailureWhy(domain) {
	if (!domain.failureReason) return "The platform reported a terminal failed state for this custom domain.";
	if (!domain.failureCategory) return domain.failureReason;
	return `${domain.failureCategory}: ${domain.failureReason}`;
}
function domainVerificationFailedError(hostname, domain) {
	const why = formatDomainFailureWhy(domain);
	const guidance = formatDomainFailureFix(domain);
	return new CliStructuredError("SERVICE.DOMAIN_VERIFICATION_FAILED", `Custom domain "${hostname}" failed verification`, {
		why,
		nextActions: [
			...guidance ? [adviceAction(guidance)] : [],
			runCommandAction("Show the domain", `service domain show ${hostname} --service <name>`),
			runCommandAction("Retry verification", `service domain retry ${hostname} --service <name>`)
		]
	});
}
function domainVerificationTimeoutError(hostname, lastStatus) {
	return new CliStructuredError("SERVICE.DOMAIN_VERIFICATION_TIMEOUT", `Timed out waiting for "${hostname}" to become active`, {
		why: `The domain is still "${lastStatus}".`,
		nextActions: [runCommandAction("Show the domain", `service domain show ${hostname} --service <name>`), adviceAction("Retry wait with a longer --timeout.")]
	});
}
function timeoutInvalidError(value) {
	return new CliStructuredError("SERVICE.TIMEOUT_INVALID", `Invalid timeout "${value}"`, {
		why: "Timeout must be a duration such as 0, 30s, 15m, or 1h.",
		nextActions: [runCommandAction("Wait with a valid timeout", "service domain wait shop.acme.com --service <name> --timeout 15m")]
	});
}
function debugMeta(error) {
	if (error instanceof DomainApiError) return {
		status: error.status,
		apiCode: error.code,
		hint: error.hint
	};
	return {};
}
function domainCommandError(command, error, hostname) {
	if (error instanceof DomainApiError) {
		const known = domainApiFailure(command, error, hostname);
		if (known) return known;
	}
	return new CliStructuredError("SERVICE.DEPLOY_FAILED", `Custom domain ${command} failed`, {
		why: error instanceof Error ? error.message : String(error),
		meta: debugMeta(error),
		nextActions: [runCommandAction("Show the domain", `service domain show ${hostname} --service <name>`)],
		cause: error
	});
}
function domainApiFailure(command, error, hostname) {
	if (command === "add") return domainAddFailure(error, hostname);
	if (error.status === 404) return domainNotFoundError(hostname);
	if (command === "retry" && error.status === 409) return domainRetryNotEligibleError(hostname, error);
	return null;
}
function domainAddFailure(error, hostname) {
	if ((error.status === 400 || error.status === 422) && isDomainDnsError(error)) return domainDnsNotConfiguredError(hostname, error);
	if (error.status === 400) return domainHostnameRejectedError(hostname, error);
	if (error.status === 429 || isDomainQuotaError(error)) return domainQuotaExceededError(error);
	if (error.status === 409) return domainAlreadyRegisteredError(hostname, error);
	if (error.status === 422) return domainRequiresDeploymentError(hostname, error);
	return null;
}
function domainHostnameRejectedError(hostname, error) {
	return new CliStructuredError("SERVICE.DOMAIN_HOSTNAME_INVALID", `Invalid custom domain "${hostname}"`, {
		why: error.message,
		meta: debugMeta(error),
		nextActions: [adviceAction("Pass a valid hostname like shop.acme.com and make sure DNS can be verified."), runCommandAction("Add a domain", "service domain add shop.acme.com --service <name>")]
	});
}
function domainQuotaExceededError(error) {
	return new CliStructuredError("SERVICE.DOMAIN_QUOTA_EXCEEDED", "Custom domain quota exceeded", {
		why: error.message,
		meta: debugMeta(error),
		nextActions: [adviceAction("Delete an existing custom domain before adding another one."), runCommandAction("Delete a domain", "service domain delete <hostname> --service <name>")]
	});
}
function domainAlreadyRegisteredError(hostname, error) {
	return new CliStructuredError("SERVICE.DOMAIN_ALREADY_REGISTERED", `Custom domain "${hostname}" is already registered`, {
		why: error.hint ?? error.message,
		meta: debugMeta(error),
		nextActions: [adviceAction("Select the service that owns this hostname and delete it there, or contact Prisma support if you cannot access it.")]
	});
}
function domainRequiresDeploymentError(hostname, error) {
	return new CliStructuredError("SERVICE.NO_VERSIONS", "Custom domain requires a live production version", {
		why: "The selected production service does not have a promoted version that can receive a custom domain.",
		meta: debugMeta(error),
		nextActions: [adviceAction("Promote a version on the service's production branch, then add the domain again."), runCommandAction("Add the domain", `service domain add ${hostname} --service <name>`)]
	});
}
function domainRetryNotEligibleError(hostname, error) {
	return new CliStructuredError("SERVICE.DOMAIN_RETRY_NOT_ELIGIBLE", `Custom domain "${hostname}" is not eligible for retry`, {
		why: error.message,
		meta: debugMeta(error),
		nextActions: [adviceAction("Wait for the current verification or TLS step to finish, then rerun retry if the domain fails."), runCommandAction("Show the domain", `service domain show ${hostname} --service <name>`)]
	});
}
function isDomainQuotaError(error) {
	if (error.status !== 409) return false;
	const text = `${error.message} ${error.hint ?? ""}`.toLowerCase();
	return text.includes("quota") || text.includes("maximum") || text.includes("limit");
}
function isDomainDnsError(error) {
	const text = `${error.message} ${error.hint ?? ""}`.toLowerCase();
	return text.includes("dns is not configured") || text.includes("dns verification failed") || text.includes("no cname") || text.includes("cname record") || text.includes("no a/aaaa") || CNAME_HINT.test(text);
}
function domainDnsNotConfiguredError(hostname, error) {
	const target = extractDomainDnsTarget(error);
	const record = target ? `CNAME ${hostname} -> ${target}` : null;
	return new CliStructuredError("SERVICE.DOMAIN_DNS_NOT_CONFIGURED", `DNS is not configured for "${hostname}"`, {
		why: error.hint ?? error.message,
		meta: {
			...debugMeta(error),
			...record ? { dnsRecord: record } : {}
		},
		nextActions: record ? [adviceAction(`Add ${record} at your DNS provider, then rerun the domain command.`), runCommandAction("Add the domain", `service domain add ${hostname} --service <name>`)] : [adviceAction("The platform did not return the required DNS target. Re-run with --log-level verbose for the underlying API response details.")]
	});
}
function extractDomainDnsTarget(error) {
	const text = `${error.hint ?? ""} ${error.message}`;
	return PRISMA_BUILD_HOST.exec(text)?.[1]?.toLowerCase() ?? null;
}
//#endregion
//#region ../cli/src/commands/service/presentation.ts
function fields(rows) {
	return {
		kind: "fields",
		rows
	};
}
/** The heading a command that only reports opens with. */
function title(text) {
	return {
		kind: "summary",
		status: "info",
		text
	};
}
/** The line a command that changed something ends on: what it did, in
*  the past tense, marked as a success. */
function completed(text) {
	return {
		kind: "summary",
		status: "ok",
		text
	};
}
function formatRecentDeployments(deployments) {
	if (deployments.length === 0) return "none";
	return deployments.map((deployment) => `${deployment.id} (${deployment.status}${deployment.live ? ", live" : ""})`).join(", ");
}
function domainTargetRows(target) {
	return [
		{
			label: "workspace",
			value: target.workspace.name
		},
		{
			label: "project",
			value: target.project.name
		},
		{
			label: "branch",
			value: target.branch.name
		},
		{
			label: "service",
			value: target.service.name
		}
	];
}
function domainDnsRows(domain) {
	return domain.dnsRecords.map((record) => ({
		label: `dns ${record.type}`,
		value: `${record.name} -> ${record.value}${record.ttl === null ? "" : ` (ttl ${record.ttl})`}`
	}));
}
function domainFailureRows(domain) {
	if (!domain.failureReason) return [];
	return [{
		label: "failure",
		value: domain.failureCategory ? `${domain.failureCategory}: ${domain.failureReason}` : domain.failureReason
	}];
}
function listPresentations(result) {
	return {
		json: () => result,
		human: () => [
			title("Listing services for the selected project."),
			fields([{
				label: "project",
				value: result.projectName
			}, {
				label: "branch",
				value: result.branch
			}]),
			...result.services.length === 0 ? [{
				kind: "summary",
				status: "info",
				text: "No services found."
			}] : [{
				kind: "table",
				columns: [
					"name",
					"id",
					"region",
					"live url"
				],
				rows: result.services.map((service) => [
					service.name,
					service.id,
					service.region ?? "",
					service.liveUrl ?? "not deployed"
				])
			}]
		],
		/** The machine rows leave an absent region and an undeployed service
		*  empty, rather than repeating the words the human table shows. */
		stdout: () => result.services.map((service) => [
			service.name,
			service.id,
			service.region ?? "",
			service.liveUrl ?? ""
		].join("	")),
		next: () => {
			const first = result.services[0];
			return first ? [runCommandAction("Show a service", `service show ${first.name}`)] : [adviceAction("Create a service with service create <name>, choosing the name.")];
		}
	};
}
function createPresentations(result) {
	return {
		stdout: () => [],
		json: () => result,
		human: () => [result.existing ? title(`${result.service.name} already exists on ${result.branch}; showing it.`) : completed(`Created ${result.service.name} on ${result.branch}.`), fields([
			{
				label: "project",
				value: result.projectId
			},
			{
				label: "branch",
				value: result.branch
			},
			{
				label: "service",
				value: result.service.name
			},
			{
				label: "id",
				value: result.service.id
			},
			{
				label: "region",
				value: result.service.region ?? ""
			},
			{
				label: "live url",
				value: result.service.liveUrl ?? "not deployed"
			}
		])],
		next: () => [runCommandAction("Deploy to the service", "deploy"), runCommandAction("Show the service", `service show ${result.service.name}`)]
	};
}
function showPresentations(result) {
	const next = [];
	if (result.liveUrl) next.push(runCommandAction("Open the live URL", `service open ${result.service.name}`));
	const inspectable = result.liveVersion ?? result.recentVersions[0];
	if (inspectable) next.push(runCommandAction("Show the version", `service version show ${inspectable.id}`));
	return {
		stdout: () => [],
		json: () => result,
		human: () => [title(`Showing the state of service ${result.service.name}.`), fields([
			{
				label: "project",
				value: result.projectId
			},
			{
				label: "service",
				value: result.service.name
			},
			{
				label: "live version",
				value: result.liveVersion?.id ?? ""
			},
			{
				label: "live url",
				value: result.liveUrl ?? "unavailable"
			},
			{
				label: "recent versions",
				value: formatRecentDeployments(result.recentVersions)
			}
		])],
		next: () => next
	};
}
function versionListPresentations(result) {
	return {
		stdout: () => [],
		json: () => result,
		human: () => [
			title(`Listing versions of service ${result.service.name}.`),
			fields([{
				label: "project",
				value: result.projectId
			}, {
				label: "service",
				value: result.service.name
			}]),
			result.versions.length === 0 ? {
				kind: "summary",
				status: "info",
				text: "No versions found."
			} : {
				kind: "table",
				columns: [
					"version",
					"status",
					"created",
					"live"
				],
				rows: result.versions.map((deployment) => [
					deployment.id,
					deployment.status,
					deployment.createdAt,
					deployment.live ? "yes" : ""
				])
			}
		],
		next: () => {
			const newest = result.versions[0];
			return newest ? [runCommandAction("Show the newest version", `service version show ${newest.id}`)] : [];
		}
	};
}
function versionShowPresentations(result) {
	return {
		stdout: () => [],
		json: () => result,
		next: () => [],
		human: () => [title("Showing service version details."), fields([
			...result.service ? [{
				label: "service",
				value: result.service.name
			}] : [],
			{
				label: "version",
				value: result.version.id
			},
			{
				label: "status",
				value: result.version.status
			},
			...result.version.url ? [{
				label: "url",
				value: result.version.url
			}] : [],
			...result.version.live === null ? [] : [{
				label: "live",
				value: result.version.live ? "yes" : "no"
			}],
			{
				label: "created",
				value: result.version.createdAt
			}
		])]
	};
}
function openPresentations(result, liveDeploymentId) {
	return {
		json: () => result,
		human: () => [result.opened ? completed(`Opened the live URL for service ${result.service.name}.`) : title(`Resolved the live URL for service ${result.service.name}.`), fields([
			{
				label: "project",
				value: result.projectId
			},
			{
				label: "service",
				value: result.service.name
			},
			{
				label: "url",
				value: result.url
			},
			{
				label: "opened",
				value: result.opened ? "yes" : "no"
			}
		])],
		stdout: () => [result.url],
		next: () => [runCommandAction("Inspect the service", `service show ${result.service.name}`), runCommandAction("Show the live version", `service version show ${liveDeploymentId}`)]
	};
}
function versionNextActions(deploymentId, serviceName) {
	return [runCommandAction("List versions", `service version list ${serviceName}`), runCommandAction("Show the version", `service version show ${deploymentId}`)];
}
function promotePresentations(result, alreadyLive) {
	return {
		stdout: () => [],
		json: () => result,
		human: () => [completed(alreadyLive ? `${result.version.id} was already live for ${result.service.name}.` : `Promoted ${result.version.id} to production.`), fields([
			{
				label: "service",
				value: result.service.name
			},
			{
				label: "version",
				value: result.version.id
			},
			{
				label: "status",
				value: result.version.status
			},
			...result.version.url ? [{
				label: "url",
				value: result.version.url
			}] : []
		])],
		next: () => versionNextActions(result.version.id, result.service.name)
	};
}
function rollbackPresentations(result, alreadyLive) {
	return {
		stdout: () => [],
		json: () => result,
		human: () => [completed(alreadyLive ? `${result.version.id} was already live for ${result.service.name}.` : `Rolled ${result.service.name} back to ${result.version.id}.`), fields([
			{
				label: "project",
				value: result.projectId
			},
			{
				label: "service",
				value: result.service.name
			},
			{
				label: "version",
				value: result.version.id
			},
			{
				label: "status",
				value: result.version.status
			},
			{
				label: "previous live version",
				value: result.previousLiveVersionId ?? "unknown"
			},
			...result.version.url ? [{
				label: "url",
				value: result.version.url
			}] : []
		])],
		next: () => versionNextActions(result.version.id, result.service.name)
	};
}
function versionStartPresentations(result) {
	return {
		stdout: () => [],
		json: () => result,
		human: () => [completed(result.alreadyInState ? `${result.version.id} was already running.` : `Started ${result.version.id}.`), fields([
			{
				label: "service",
				value: result.service.name
			},
			{
				label: "version",
				value: result.version.id
			},
			{
				label: "status",
				value: result.version.status
			},
			...result.version.url ? [{
				label: "url",
				value: result.version.url
			}] : []
		])],
		next: () => versionNextActions(result.version.id, result.service.name)
	};
}
function versionStopPresentations(result) {
	return {
		stdout: () => [],
		json: () => result,
		human: () => [completed(result.alreadyInState ? `${result.version.id} was already stopped.` : `Stopped ${result.version.id}.`), fields([
			{
				label: "service",
				value: result.service.name
			},
			{
				label: "version",
				value: result.version.id
			},
			{
				label: "status",
				value: result.version.status
			}
		])],
		next: () => versionNextActions(result.version.id, result.service.name)
	};
}
function versionDeletePresentations(result) {
	return {
		stdout: () => [],
		json: () => result,
		human: () => [completed(`Deleted ${result.versionId} from ${result.service.name}.`), fields([
			{
				label: "service",
				value: result.service.name
			},
			{
				label: "version",
				value: result.versionId
			},
			{
				label: "deleted",
				value: "yes"
			}
		])],
		next: () => [runCommandAction("List versions", `service version list ${result.service.name}`)]
	};
}
function deletePresentations(result) {
	return {
		stdout: () => [],
		json: () => result,
		human: () => [completed(`Deleted ${result.service.name} and every version it owned.`), fields([
			{
				label: "project",
				value: result.projectId
			},
			{
				label: "service",
				value: result.service.name
			},
			{
				label: "deleted",
				value: "yes"
			}
		])],
		next: () => [runCommandAction("List remaining services", "service list")]
	};
}
function domainAddPresentations(result) {
	return {
		stdout: () => [],
		json: () => result,
		human: () => [result.existing ? title(`Showing the existing custom domain on ${result.service.name}.`) : completed(`Added ${result.domain.hostname} to ${result.service.name}.`), fields([
			...domainTargetRows(result),
			{
				label: "hostname",
				value: result.domain.hostname
			},
			{
				label: "status",
				value: result.domain.status
			},
			...domainDnsRows(result.domain)
		])],
		next: () => [runCommandAction("Wait for activation", `service domain wait ${result.domain.hostname}`), runCommandAction("Show the domain", `service domain show ${result.domain.hostname}`)]
	};
}
function domainShowPresentations(result) {
	return {
		stdout: () => [],
		json: () => result,
		human: () => [title("Showing custom domain status."), fields([
			...domainTargetRows(result),
			{
				label: "hostname",
				value: result.domain.hostname
			},
			{
				label: "status",
				value: result.domain.status
			},
			...domainFailureRows(result.domain),
			{
				label: "cert expires",
				value: result.domain.certExpiresAt ?? "not yet issued"
			},
			{
				label: "created",
				value: result.domain.createdAt
			},
			...domainDnsRows(result.domain)
		])],
		next: () => {
			if (result.domain.status === "active") return [];
			if (result.domain.status === "failed") return [runCommandAction("Retry verification", `service domain retry ${result.domain.hostname}`)];
			return [runCommandAction("Wait for activation", `service domain wait ${result.domain.hostname}`)];
		}
	};
}
function domainDeletePresentations(result) {
	return {
		stdout: () => [],
		json: () => result,
		next: () => [],
		human: () => [completed(`Deleted ${result.hostname} from ${result.service.name}.`), fields([
			...domainTargetRows(result),
			{
				label: "hostname",
				value: result.hostname
			},
			{
				label: "deleted",
				value: "yes"
			}
		])]
	};
}
function domainRetryPresentations(result) {
	return {
		stdout: () => [],
		json: () => result,
		human: () => [completed(`Retried verification for ${result.domain.hostname}.`), fields([
			...domainTargetRows(result),
			{
				label: "hostname",
				value: result.domain.hostname
			},
			{
				label: "status",
				value: result.domain.status
			},
			...domainFailureRows(result.domain),
			...domainDnsRows(result.domain)
		])],
		next: () => [runCommandAction("Wait for activation", `service domain wait ${result.domain.hostname}`)]
	};
}
function domainWaitPresentations(result) {
	return {
		stdout: () => [],
		json: () => result,
		next: () => [],
		human: () => [completed(`${result.hostname} is live at ${result.liveUrl}`), fields([
			...domainTargetRows(result),
			{
				label: "hostname",
				value: result.hostname
			},
			{
				label: "status",
				value: result.status
			}
		])]
	};
}
//#endregion
//#region ../cli/src/lib/app/read-branch.ts
/**
* Resolves the branch an app management command should read from, without ever
* creating one. Returns the branch whose `gitName` matches `branchName`, else
* the project's default branch, else null when the project has no branches.
*/
async function resolveReadBranch(client, options) {
	const branches = [];
	let cursor;
	do {
		const result = await client.GET("/v1/projects/{projectId}/branches", {
			params: {
				path: { projectId: options.projectId },
				query: { cursor }
			},
			signal: options.signal
		});
		if (result.error || !result.data) throw new Error(`Failed to list branches for project ${options.projectId}: ${JSON.stringify(result.error)}`);
		branches.push(...result.data.data);
		cursor = result.data.pagination.hasMore ? result.data.pagination.nextCursor ?? void 0 : void 0;
	} while (cursor);
	const chosen = branches.find((branch) => branch.gitName === options.branchName) ?? branches.find((branch) => branch.isDefault) ?? null;
	return chosen ? {
		id: chosen.id,
		name: chosen.gitName,
		kind: chosen.role
	} : null;
}
//#endregion
//#region ../cli/src/commands/service/target.ts
/** A hostname's optional root dot, and one DNS label. */
const TRAILING_DOT = /\.$/;
const DNS_LABEL = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
/** The workspace the run is acting as, from the credential the engine
*  is authenticating with. A workspace with no name shows its id
*  instead: the id is the only other identifier the user can act on,
*  and every display of this name needs a non-empty string. A
*  credential that names no workspace at all — an environment token
*  whose claims carry none — cannot scope these commands, so it is the
*  same failure as having no credential. */
async function requireWorkspace(ctx) {
	const credential = await ctx.activeCredential();
	if (!credential?.workspaceId) throw workspaceRequiredError();
	return {
		id: credential.workspaceId,
		name: credential.workspaceName ?? credential.workspaceId
	};
}
/** What project resolution reads: where the command was invoked, and
*  the run's abort signal. */
function resolutionContext(ctx) {
	return { runtime: {
		cwd: ctx.cwd,
		signal: ctx.signal
	} };
}
function toBranchKind(name) {
	return name === "production" || name === "main" ? "production" : "preview";
}
/**
* The same listing `controllers/project.ts#listRealWorkspaceProjects`
* performs, on ctx.api — duplicated here so this CLI does not drag
* the legacy controller import graph (child-process git adapters).
*
* No workspace filter, and no workspace parameter that could invite one
* back: the credential is issued for one workspace and the API answers
* within it. The filter this function used to carry compared the
* credential's bare workspace id against the API's `wksp_`-prefixed one
* and so discarded every project, every time — which made every service
* command report the pinned project as missing. #144 removed it from
* the legacy listing this mirrors; the copy here was written from the
* version that still had it.
*
* A refused request is raised, not read as an empty workspace. Without
* that, a 401, 403 or 500 becomes "no projects", the caller finds the
* pinned project missing, and the user is told their local binding is
* stale — sent to re-link a project that was never the problem. That is
* the same wrong recovery path the missing filter produced.
*/
async function listWorkspaceProjects(ctx) {
	const { data, error, response } = await ctx.api.GET("/v1/projects", { signal: ctx.signal });
	if (error || !data) throw projectApiError("Failed to list projects", response, error);
	return sortProjects((data.data ?? []).map((project) => ({
		id: project.id,
		name: project.name,
		..."url" in project && typeof project.url === "string" ? { url: project.url } : {},
		..."defaultRegion" in project ? { defaultRegion: project.defaultRegion } : {},
		slug: "slug" in project && typeof project.slug === "string" ? project.slug : null,
		workspace: {
			id: project.workspace.id,
			name: project.workspace.name
		}
	})));
}
/** A blank `--branch` names no branch and must never fall through to
*  the default-branch behavior of omitting the flag. */
function requireBranchFlagValue(branchName) {
	if (branchName !== void 0 && branchName.trim() === "") throw branchValueEmptyError();
}
async function resolveServiceProjectContext(ctx, explicitProject, options) {
	requireBranchFlagValue(options.branchName);
	const workspace = await requireWorkspace(ctx);
	const projects = await listWorkspaceProjects(ctx);
	const resolvedResult = await resolveProjectTarget({
		context: resolutionContext(ctx),
		workspace,
		...explicitProject !== void 0 ? { explicitProject } : {},
		listProjects: () => Promise.resolve(projects),
		commandName: options.commandName
	});
	if (resolvedResult.isErr()) throw projectResolutionErrorToStructured(resolvedResult.error);
	const resolved = resolvedResult.value;
	const requested = options.branchName ? {
		name: options.branchName,
		explicit: true
	} : {
		name: "main",
		explicit: false
	};
	const remoteBranch = requested.explicit ? null : await resolveReadBranch(ctx.api, {
		projectId: resolved.project.id,
		branchName: requested.name,
		signal: ctx.signal
	});
	return {
		workspace: resolved.workspace,
		project: resolved.project,
		resolution: resolved.resolution,
		branch: remoteBranch ?? {
			id: null,
			name: requested.name,
			kind: toBranchKind(requested.name)
		}
	};
}
function serviceProvider(ctx) {
	return createAppProvider(ctx.api);
}
function sortServices(services) {
	return services.slice().sort((left, right) => left.name.localeCompare(right.name) || left.id.localeCompare(right.id));
}
function isMissingProjectError(error) {
	return error instanceof Error && error.message === "Resource Not Found";
}
async function listServices(ctx, provider, projectId, branchName) {
	return provider.listApps(projectId, {
		...branchName !== void 0 ? { branchName } : {},
		signal: ctx.signal
	}).then(sortServices).catch((error) => {
		if (isMissingProjectError(error)) throw projectNotFoundError(projectId);
		throw deployFailedError("Failed to list services", error, [runCommandAction("Inspect the project", "project show")]);
	});
}
/** The service argument, required: service commands never infer,
*  remember, or prompt for a target. */
function requireServiceArgument(serviceRef, commandName) {
	if (!serviceRef) throw serviceTargetRequiredError(commandName);
	return serviceRef;
}
/** Matches the service argument against the branch's services: the
*  stable platform id is primary, the name is the fallback. An id
*  match always wins, so a service named like another service's id
*  cannot shadow it. */
function matchRequestedService(serviceRef, services, projectId) {
	const matched = services.find((service) => service.id === serviceRef) ?? services.find((service) => service.name === serviceRef);
	if (!matched) throw serviceSelectionInvalidError(serviceRef, projectId);
	return matched;
}
/** Resolve a service version by its globally-unique id. The id alone names
*  the subject — no service, project, or branch parameter is consulted,
*  the same way `service version show` resolves it. */
async function resolveVersionSubject(ctx, deploymentId) {
	const provider = serviceProvider(ctx);
	const shown = await provider.showDeployment(deploymentId, { signal: ctx.signal }).catch((error) => {
		throw deployFailedError("Failed to show version", error, []);
	});
	if (!shown) throw versionNotFoundError(deploymentId);
	if (!shown.app) throw versionDetachedError(deploymentId);
	return {
		provider,
		service: shown.app,
		version: shown.deployment
	};
}
/** The live deployment is the one the service record names as its latest
*  deployment. Nothing else decides it — local CLI state never does. */
function resolveCurrentLiveVersionId(service, deployments) {
	if (service.liveDeploymentId && deployments.some((deployment) => deployment.id === service.liveDeploymentId)) return service.liveDeploymentId;
	return null;
}
function applyLiveVersionHint(deployments, currentLiveDeploymentId) {
	if (!currentLiveDeploymentId) return deployments.map((deployment) => ({
		...deployment,
		live: deployment.live ?? null
	}));
	return deployments.map((deployment) => ({
		...deployment,
		live: deployment.id === currentLiveDeploymentId
	}));
}
function sortVersionsNewestFirst(deployments) {
	return deployments.slice().sort((left, right) => right.createdAt.localeCompare(left.createdAt) || right.id.localeCompare(left.id));
}
function toServiceSummary(service) {
	return {
		id: service.id,
		name: service.name
	};
}
/** A service record as the listing and create presenters report it. A
*  service that names no live deployment has no URL to show: the
*  endpoint domain it already carries does not resolve until the first
*  promote. */
function toServiceListEntry(service) {
	return {
		id: service.id,
		name: service.name,
		region: service.region,
		liveVersionId: service.liveDeploymentId,
		liveUrl: service.liveDeploymentId ? service.liveUrl : null
	};
}
function toServiceDomainSummary(domain) {
	return {
		id: domain.id,
		type: domain.type,
		url: domain.url,
		hostname: domain.hostname,
		serviceId: domain.appId,
		status: domain.status,
		foundryStatus: domain.foundryStatus,
		failureReason: domain.failureReason,
		failureCategory: domain.failureCategory,
		certExpiresAt: domain.certExpiresAt,
		createdAt: domain.createdAt,
		updatedAt: domain.updatedAt,
		dnsRecords: domain.dnsRecords.map((record) => ({
			type: record.type,
			name: record.name,
			value: record.value,
			ttl: record.ttl
		}))
	};
}
function normalizeDomainHostname(hostname) {
	const normalized = hostname.trim().replace(TRAILING_DOT, "").toLowerCase();
	if (!isValidDomainHostname(normalized)) throw domainHostnameInvalidError(hostname);
	return normalized;
}
function isValidDomainHostname(hostname) {
	if (hostname.length < 1 || hostname.length > 253) return false;
	if (hostname.includes("://") || hostname.includes("/") || hostname.includes(":") || hostname.startsWith("*.")) return false;
	const labels = hostname.split(".");
	if (labels.length < 2) return false;
	return labels.every((label) => DNS_LABEL.test(label));
}
function sameDomainHostname(left, right) {
	return left.trim().replace(TRAILING_DOT, "").toLowerCase() === right.trim().replace(TRAILING_DOT, "").toLowerCase();
}
async function resolveDomainByHostname(provider, serviceId, hostname, command, signal) {
	const matched = (await provider.listDomains(serviceId, { signal }).catch((error) => {
		throw domainCommandError(command, error, hostname);
	})).find((domain) => sameDomainHostname(domain.hostname, hostname));
	if (matched) return matched;
	throw domainNotFoundError(hostname);
}
/** Project + branch resolution, before the service match. */
async function resolveServiceProjectState(ctx, options) {
	const provider = serviceProvider(ctx);
	const target = await resolveServiceProjectContext(ctx, options.projectRef, {
		commandName: options.commandName,
		...options.branchName !== void 0 ? { branchName: options.branchName } : {}
	});
	return {
		provider,
		target,
		projectId: target.project.id
	};
}
/** The shared read flow for every command that acts on an existing
*  service: project + branch resolution, service listing, and the
*  parameter-only service match. */
async function resolveServiceReadState(ctx, options) {
	const requested = requireServiceArgument(options.serviceName, options.commandName);
	const state = await resolveServiceProjectState(ctx, options);
	const service = matchRequestedService(requested, await listServices(ctx, state.provider, state.projectId, state.target.branch.name), state.projectId);
	return {
		...state,
		service
	};
}
async function resolveServiceDomainTarget(ctx, options) {
	requireBranchFlagValue(options.branchName);
	const branchName = options.branchName?.trim() ?? "production";
	if (toBranchKind(branchName) !== "production") throw branchNotDeployableError(branchName);
	const requested = requireServiceArgument(options.serviceName, options.commandName);
	const provider = serviceProvider(ctx);
	const target = await resolveServiceProjectContext(ctx, options.projectRef, {
		commandName: options.commandName,
		branchName
	});
	const projectId = target.project.id;
	const service = matchRequestedService(requested, await listServices(ctx, provider, projectId, target.branch.name), projectId);
	return {
		provider,
		service,
		resultTarget: {
			workspace: target.workspace,
			project: target.project,
			branch: {
				name: target.branch.name,
				kind: target.branch.kind
			},
			service: toServiceSummary(service)
		}
	};
}
//#endregion
//#region ../cli/src/commands/service/create.ts
const serviceCreateCommand = defineCommand({
	help: {
		summary: "Create a service in a project",
		description: "Registers a service on a Branch ahead of its first deploy. Deploying through a connected repository or 'deploy' creates services automatically, so reach for this only when a service must exist before anything has been deployed to it.",
		examples: ["service create my-service", "service create my-service --region us-east-1 --branch main"]
	},
	args: {
		positionals: { name: positional.string({
			brief: "Service name",
			placeholder: "name"
		}) },
		flags: {
			project: flag.string({
				brief: "Project id or name (default: the project this directory is linked to)",
				placeholder: "id-or-name"
			}),
			region: flag.string({
				brief: "Prisma Compute region id; set it when the service must run near a location",
				placeholder: "region"
			}),
			branch: flag.string({
				brief: "Branch to create the service on (default: the default branch)",
				placeholder: "branch"
			})
		}
	},
	needs: { credentials: true },
	handler: async (args, ctx) => {
		const name = args.positionals.name.trim();
		if (!name) throw serviceNameRequiredError();
		const target = await resolveServiceProjectContext(ctx, args.flags.project, {
			commandName: "service create",
			...args.flags.branch !== void 0 ? { branchName: args.flags.branch } : {}
		});
		const created = await serviceProvider(ctx).createApp({
			projectId: target.project.id,
			branchName: target.branch.name,
			name,
			...args.flags.region !== void 0 ? { region: args.flags.region } : {},
			signal: ctx.signal
		}).catch((error) => {
			throw deployFailedError("Failed to create service", error, [runCommandAction("List services", "service list")]);
		});
		const result = {
			projectId: target.project.id,
			branch: target.branch.name,
			service: toServiceListEntry(created.service),
			existing: created.existing
		};
		return ok(ctx.present({ data: result }, createPresentations(result)));
	}
});
//#endregion
//#region ../cli/src/commands/service/release.ts
function requireVersionForService(deployments, deploymentId, serviceName) {
	const deployment = deployments.find((candidate) => candidate.id === deploymentId);
	if (!deployment) throw versionNotFoundForServiceError(deploymentId, serviceName);
	return deployment;
}
/** The rollback default: the newest deployment that is not the live
*  one. With nothing naming the live deployment, every deployment
*  qualifies and the newest one — most likely the one already live — is
*  what a caller would get, so this refuses instead of guessing. */
function resolveRollbackTarget(deployments, currentLiveDeploymentId, serviceName) {
	if (deployments.length === 0) throw noPreviousVersionError(serviceName);
	if (currentLiveDeploymentId === null) throw liveVersionUnknownError(serviceName);
	const previousDeployment = deployments.find((deployment) => deployment.id !== currentLiveDeploymentId);
	if (!previousDeployment) throw noPreviousVersionError(serviceName);
	return previousDeployment;
}
/**
* Maps the compute SDK's promote callbacks onto engine events: one
* `status` event per reported transition of the target deployment, an
* `endpoint` event for the promoted URL, and a warning message when the
* SDK reports a promotion failure before rejecting.
*/
function promoteProgressReporter(ctx, deploymentId) {
	let previousStatus = null;
	const status = (next) => {
		if (previousStatus === next) return;
		ctx.report({
			kind: "status",
			subject: deploymentId,
			status: next,
			...previousStatus === null ? {} : { from: previousStatus }
		});
		previousStatus = next;
	};
	return {
		onDeploymentStarting: () => status("starting"),
		onDeploymentStartRequested: () => status("start-requested"),
		onStatusChange: (next) => status(next),
		onDeploymentRunning: () => status("running"),
		onPromoteStart: () => status("promoting"),
		onPromoted: (appEndpointDomain) => {
			status("promoted");
			if (appEndpointDomain) ctx.report({
				kind: "endpoint",
				name: "live",
				url: `https://${appEndpointDomain}`
			});
		},
		onPromoteFailed: (error) => {
			ctx.report({
				kind: "message",
				severity: "warn",
				text: `Promotion failed${error?.message ? `: ${error.message}` : "."}`
			});
		}
	};
}
/**
* Maps the compute SDK's app-teardown callbacks onto engine events: the
* SDK polls each deployment down internally, so the deployment stop and
* delete phases become `progress` counts and the terminal delete becomes
* a `status` event for the service.
*/
function destroyProgressReporter(ctx, serviceName) {
	let stopping = 0;
	let stopped = 0;
	let deleting = 0;
	let deleted = 0;
	return {
		onStoppingDeployments: (deploymentIds) => {
			stopping = deploymentIds.length;
			ctx.report({
				kind: "progress",
				step: "stop-versions",
				completed: 0,
				total: stopping
			});
		},
		onDeploymentStopped: () => {
			stopped += 1;
			ctx.report({
				kind: "progress",
				step: "stop-versions",
				completed: stopped,
				total: stopping
			});
		},
		onDeletingDeployments: (deploymentIds) => {
			deleting = deploymentIds.length;
			ctx.report({
				kind: "progress",
				step: "delete-versions",
				completed: 0,
				total: deleting
			});
		},
		onDeploymentDeleted: () => {
			deleted += 1;
			ctx.report({
				kind: "progress",
				step: "delete-versions",
				completed: deleted,
				total: deleting
			});
		},
		onAppDeleted: () => {
			ctx.report({
				kind: "status",
				subject: serviceName,
				status: "deleted",
				from: "deleting"
			});
		}
	};
}
//#endregion
//#region ../cli/src/commands/service/delete.ts
const serviceDeleteCommand = defineCommand({
	help: {
		summary: "Delete a service from one branch",
		description: "Removes the service from the targeted Branch only: its versions stop serving and its URL goes away. The same service on other branches is untouched. Pass the confirmation token with --confirm to run non-interactively.",
		examples: ["service delete my-service", "service delete my-service --confirm my-service"]
	},
	args: {
		flags: {
			project: flag.string({
				brief: "Project id or name",
				placeholder: "id-or-name"
			}),
			branch: flag.string({
				brief: "Branch the deletion is scoped to",
				placeholder: "name"
			})
		},
		positionals: { service: positional.optionalString({
			brief: "Service id or name",
			placeholder: "service"
		}) }
	},
	needs: { credentials: true },
	handler: async (args, ctx) => {
		const state = await resolveServiceReadState(ctx, {
			serviceName: args.positionals.service,
			projectRef: args.flags.project,
			branchName: args.flags.branch,
			commandName: "service delete"
		});
		if (!await ctx.prompt.consent(`Delete Service "${state.service.name}" and every version it owns?`, { token: state.service.name })) throw userCancelledError("Service deletion canceled");
		ctx.report({
			kind: "step-started",
			step: "delete"
		});
		ctx.report({
			kind: "status",
			subject: state.service.name,
			status: "deleting"
		});
		let deletedService;
		try {
			deletedService = await state.provider.removeApp(state.service.id, {
				signal: ctx.signal,
				progress: destroyProgressReporter(ctx, state.service.name)
			});
		} catch (error) {
			ctx.report({
				kind: "step-finished",
				step: "delete",
				outcome: "failed"
			});
			throw deleteFailedError("Failed to delete service", error, state.service.name);
		}
		ctx.report({
			kind: "step-finished",
			step: "delete",
			outcome: "ok"
		});
		const result = {
			projectId: state.projectId,
			service: toServiceSummary(deletedService),
			deleted: true
		};
		return ok(ctx.present({ data: result }, deletePresentations(result)));
	}
});
//#endregion
//#region ../cli/src/commands/service/domain-shared.ts
/** The shared argument surface of every `service domain` command. */
function domainTargetArgs() {
	return {
		flags: {
			service: flag.string({
				brief: "Service id or name the domain belongs to",
				placeholder: "name"
			}),
			project: flag.string({
				brief: "Project id or name (default: the project this directory is linked to)",
				placeholder: "id-or-name"
			}),
			branch: flag.string({
				brief: "Branch name",
				placeholder: "name"
			})
		},
		positionals: { hostname: positional.string({
			brief: "Custom domain hostname",
			placeholder: "hostname"
		}) }
	};
}
//#endregion
//#region ../cli/src/commands/service/domain-add.ts
const serviceDomainAddCommand = defineCommand({
	help: {
		summary: "Register a custom domain on the service's production branch",
		description: "Points a hostname you own at the service instead of its generated URL. After registering, create the DNS record the platform asks for, then 'service domain wait' blocks until verification and TLS provisioning finish.",
		examples: ["service domain add shop.acme.com --service my-service"]
	},
	args: domainTargetArgs(),
	needs: { credentials: true },
	handler: async (args, ctx) => {
		const hostname = normalizeDomainHostname(args.positionals.hostname);
		const target = await resolveServiceDomainTarget(ctx, {
			serviceName: args.flags.service,
			projectRef: args.flags.project,
			branchName: args.flags.branch,
			commandName: `service domain add ${hostname}`
		});
		const added = await target.provider.addDomain({
			appId: target.service.id,
			hostname,
			signal: ctx.signal
		}).catch((error) => {
			throw domainCommandError("add", error, hostname);
		});
		const result = {
			...target.resultTarget,
			domain: toServiceDomainSummary(added.domain),
			existing: added.existing
		};
		return ok(ctx.present({ data: result }, domainAddPresentations(result)));
	}
});
//#endregion
//#region ../cli/src/commands/service/domain-delete.ts
const serviceDomainDeleteCommand = defineCommand({
	help: {
		summary: "Delete a custom domain from the service",
		description: "The hostname stops routing to the service immediately; the service itself and its generated URL keep working. Pass the hostname with --confirm to run non-interactively.",
		examples: ["service domain delete shop.acme.com --service my-service", "service domain delete shop.acme.com --service my-service --confirm shop.acme.com"]
	},
	args: domainTargetArgs(),
	needs: { credentials: true },
	handler: async (args, ctx) => {
		const hostname = normalizeDomainHostname(args.positionals.hostname);
		const target = await resolveServiceDomainTarget(ctx, {
			serviceName: args.flags.service,
			projectRef: args.flags.project,
			branchName: args.flags.branch,
			commandName: `service domain delete ${hostname}`
		});
		const domain = await resolveDomainByHostname(target.provider, target.service.id, hostname, "delete", ctx.signal);
		if (!await ctx.prompt.consent(`Delete ${hostname} from Service "${target.resultTarget.service.name}"?`, { token: hostname })) throw userCancelledError("Custom domain deletion canceled");
		await target.provider.removeDomain(domain.id, { signal: ctx.signal }).catch((error) => {
			throw domainCommandError("delete", error, hostname);
		});
		const result = {
			...target.resultTarget,
			hostname,
			deleted: true
		};
		return ok(ctx.present({ data: result }, domainDeletePresentations(result)));
	}
});
//#endregion
//#region ../cli/src/commands/service/domain-retry.ts
const serviceDomainRetryCommand = defineCommand({
	help: {
		summary: "Retry custom domain DNS verification and TLS provisioning",
		description: "Run it after fixing what made the domain fail, typically a missing or wrong DNS record reported by 'service domain show'.",
		examples: ["service domain retry shop.acme.com --service my-service"]
	},
	args: domainTargetArgs(),
	needs: { credentials: true },
	handler: async (args, ctx) => {
		const hostname = normalizeDomainHostname(args.positionals.hostname);
		const target = await resolveServiceDomainTarget(ctx, {
			serviceName: args.flags.service,
			projectRef: args.flags.project,
			branchName: args.flags.branch,
			commandName: `service domain retry ${hostname}`
		});
		const domain = await resolveDomainByHostname(target.provider, target.service.id, hostname, "retry", ctx.signal);
		const retried = await target.provider.retryDomain(domain.id, { signal: ctx.signal }).catch((error) => {
			throw domainCommandError("retry", error, hostname);
		});
		const result = {
			...target.resultTarget,
			domain: toServiceDomainSummary(retried)
		};
		return ok(ctx.present({ data: result }, domainRetryPresentations(result)));
	}
});
//#endregion
//#region ../cli/src/commands/service/domain-show.ts
const serviceDomainShowCommand = defineCommand({
	help: {
		summary: "Show custom domain status and certificate details",
		description: "Reports where the domain is in its lifecycle: DNS verification, TLS certificate issuance, active, or failed with the fix to apply. Check here first when a domain is not serving.",
		examples: ["service domain show shop.acme.com --service my-service"]
	},
	args: domainTargetArgs(),
	needs: { credentials: true },
	handler: async (args, ctx) => {
		const hostname = normalizeDomainHostname(args.positionals.hostname);
		const target = await resolveServiceDomainTarget(ctx, {
			serviceName: args.flags.service,
			projectRef: args.flags.project,
			branchName: args.flags.branch,
			commandName: `service domain show ${hostname}`
		});
		const domain = await resolveDomainByHostname(target.provider, target.service.id, hostname, "show", ctx.signal);
		const detail = await target.provider.showDomain(domain.id, { signal: ctx.signal }).catch((error) => {
			throw domainCommandError("show", error, hostname);
		});
		const result = {
			...target.resultTarget,
			domain: toServiceDomainSummary(detail)
		};
		return ok(ctx.present({ data: result }, domainShowPresentations(result)));
	}
});
//#endregion
//#region ../cli/src/commands/service/domain-wait.ts
const DEFAULT_TIMEOUT_MS = 900 * 1e3;
const DEFAULT_POLL_INTERVAL_MS$1 = 5e3;
const UNIT_MULTIPLIER_MS = {
	ms: 1,
	s: 1e3,
	m: 6e4,
	h: 36e5
};
function parseWaitTimeout(value) {
	if (!value) return DEFAULT_TIMEOUT_MS;
	const trimmed = value.trim().toLowerCase();
	if (trimmed === "0") return 0;
	const match = DURATION.exec(trimmed);
	if (!match) throw timeoutInvalidError(value);
	return Number.parseInt(match[1], 10) * (UNIT_MULTIPLIER_MS[match[2]] ?? 1);
}
function pollIntervalMs$1(ctx) {
	const raw = ctx.env.PRISMA_CLI_DOMAIN_WAIT_POLL_MS;
	if (!raw) return DEFAULT_POLL_INTERVAL_MS$1;
	const parsed = Number.parseInt(raw, 10);
	return Number.isInteger(parsed) && parsed > 0 ? parsed : DEFAULT_POLL_INTERVAL_MS$1;
}
async function sleep$1(milliseconds, signal) {
	if (milliseconds <= 0) return;
	signal.throwIfAborted();
	await new Promise((resolve, reject) => {
		const onAbort = () => {
			clearTimeout(timeout);
			reject(signal.reason);
		};
		const timeout = setTimeout(() => {
			signal.removeEventListener("abort", onAbort);
			resolve();
		}, milliseconds);
		signal.addEventListener("abort", onAbort, { once: true });
	});
}
const DURATION = /^(\d+)(ms|s|m|h)$/;
const serviceDomainWaitCommand = defineCommand({
	help: {
		summary: "Wait until a custom domain is active or failed",
		description: "Blocks while DNS verification and TLS provisioning run, then exits with the outcome; useful in scripts and CI after 'service domain add'.",
		examples: ["service domain wait shop.acme.com --service my-service", "service domain wait shop.acme.com --service my-service --timeout 30m"]
	},
	args: {
		flags: {
			...domainTargetArgs().flags,
			timeout: flag.string({
				brief: "Maximum time to wait",
				placeholder: "duration",
				default: "15m"
			})
		},
		positionals: domainTargetArgs().positionals
	},
	needs: { credentials: true },
	handler: async (args, ctx) => {
		const hostname = normalizeDomainHostname(args.positionals.hostname);
		const timeoutMs = parseWaitTimeout(args.flags.timeout);
		const target = await resolveServiceDomainTarget(ctx, {
			serviceName: args.flags.service,
			projectRef: args.flags.project,
			branchName: args.flags.branch,
			commandName: `service domain wait ${hostname}`
		});
		const domain = await resolveDomainByHostname(target.provider, target.service.id, hostname, "wait", ctx.signal);
		const start = Date.now();
		const deadline = start + timeoutMs;
		const interval = pollIntervalMs$1(ctx);
		let previousStatus = null;
		let current = domain;
		for (;;) {
			if (previousStatus !== current.status) ctx.report({
				kind: "status",
				subject: hostname,
				status: current.status,
				...previousStatus === null ? {} : { from: previousStatus },
				data: {
					domainId: current.id,
					elapsedMs: Date.now() - start
				}
			});
			previousStatus = current.status;
			if (current.status === "active") {
				const result = {
					...target.resultTarget,
					hostname,
					status: current.status,
					liveUrl: `https://${hostname}`
				};
				return ok(ctx.present({ data: result }, domainWaitPresentations(result)));
			}
			if (current.status === "failed") throw domainVerificationFailedError(hostname, current);
			if (timeoutMs === 0 || Date.now() >= deadline) throw domainVerificationTimeoutError(hostname, current.status);
			await sleep$1(Math.min(interval, Math.max(deadline - Date.now(), 0)), ctx.signal);
			current = await target.provider.showDomain(current.id, { signal: ctx.signal }).catch((error) => {
				throw domainCommandError("wait", error, hostname);
			});
		}
	}
});
//#endregion
//#region ../cli/src/commands/service/list.ts
const serviceListCommand = defineCommand({
	help: {
		summary: "List the services in a project",
		description: "A service is one HTTP application (a frontend or a backend) deployed on a Branch of the project. Each deploy produces a service version; at most one version serves traffic at a time.",
		examples: ["service list", "service list --project my-app --json"]
	},
	args: { flags: {
		project: flag.string({
			brief: "Project id or name (default: the project this directory is linked to)",
			placeholder: "id-or-name"
		}),
		branch: flag.string({
			brief: "Branch the services live on (default: the default branch)",
			placeholder: "branch"
		})
	} },
	needs: { credentials: true },
	handler: async (args, ctx) => {
		const target = await resolveServiceProjectContext(ctx, args.flags.project, {
			commandName: "service list",
			...args.flags.branch !== void 0 ? { branchName: args.flags.branch } : {}
		});
		const services = await listServices(ctx, serviceProvider(ctx), target.project.id, target.branch.name);
		const result = {
			projectId: target.project.id,
			projectName: target.project.name,
			branch: target.branch.name,
			services: services.map(toServiceListEntry)
		};
		return ok(ctx.present({ data: result }, listPresentations(result)));
	}
});
//#endregion
//#region ../cli/src/lib/ndjson.ts
/**
* Reads a newline-delimited JSON body line by line.
*
* Shared by every command that reads an NDJSON log page, so the stream
* handling below is written and tested once. The two subtleties are the
* reason: a chunk boundary can fall inside a line, and a body can end
* without a trailing newline, so the last record arrives only if the
* leftover buffer is flushed at `done`.
*/
async function forEachNdjsonRecord(body, onRecord) {
	const reader = body.getReader();
	const decoder = new TextDecoder();
	let buffer = "";
	try {
		for (;;) {
			const { done, value } = await reader.read();
			if (value) buffer += decoder.decode(value, { stream: true });
			let newlineIndex = buffer.indexOf("\n");
			while (newlineIndex !== -1) {
				const line = buffer.slice(0, newlineIndex).trim();
				buffer = buffer.slice(newlineIndex + 1);
				if (line) onRecord(JSON.parse(line));
				newlineIndex = buffer.indexOf("\n");
			}
			if (done) {
				const tail = buffer.trim();
				if (tail) onRecord(JSON.parse(tail));
				return;
			}
		}
	} finally {
		await reader.cancel().catch(() => void 0);
		reader.releaseLock();
	}
}
//#endregion
//#region ../cli/src/commands/service/logs.ts
const TRAILING_NEWLINE = /\n$/;
/** The endpoint's own default page size, restated so `--tail` and the
*  unflagged run send the same shape of request. */
const DEFAULT_TAIL = 100;
/** Contract: poll every 2s in --follow. Overridable so a test drives the
*  loop without waiting, the way `service domain wait` does. */
const DEFAULT_POLL_INTERVAL_MS = 2e3;
function pollIntervalMs(ctx) {
	const raw = ctx.env.PRISMA_CLI_SERVICE_LOGS_POLL_MS;
	if (!raw) return DEFAULT_POLL_INTERVAL_MS;
	const parsed = Number.parseInt(raw, 10);
	return Number.isInteger(parsed) && parsed >= 0 ? parsed : DEFAULT_POLL_INTERVAL_MS;
}
async function sleep(milliseconds, signal) {
	if (milliseconds <= 0) {
		signal.throwIfAborted();
		return;
	}
	signal.throwIfAborted();
	await new Promise((resolve, reject) => {
		const onAbort = () => {
			clearTimeout(timeout);
			reject(signal.reason);
		};
		const timeout = setTimeout(() => {
			signal.removeEventListener("abort", onAbort);
			resolve();
		}, milliseconds);
		signal.addEventListener("abort", onAbort, { once: true });
	});
}
function logsFailedError(deploymentId, status) {
	return new CliStructuredError("SERVICE.LOGS_FAILED", `Failed to read logs for version ${deploymentId}`, {
		why: `The Management API returned HTTP ${status}.`,
		meta: { status },
		nextActions: [adviceAction("Retry the command, or rerun with --log-level verbose for more detail."), runCommandAction("Show the version", `service version show ${deploymentId}`)]
	});
}
/**
* The body ended mid-page, without the terminal record that closes one.
* Distinct from SERVICE.LOGS_NO_CURSOR, which is a page that closed
* properly and said there is nothing to resume from: this one is an
* incomplete read, and the lines already printed are not the whole page.
*/
function logsIncompleteError(deploymentId) {
	return new CliStructuredError("SERVICE.LOGS_INCOMPLETE", `Incomplete log page for version ${deploymentId}`, {
		why: "The response ended without the record that closes a page, so the lines shown may be only part of it.",
		nextActions: [adviceAction("Rerun the command to read the page again.")]
	});
}
/** An error terminal record is the platform reporting that the log read
*  itself failed, so it settles the run rather than printing. */
function logStreamFailedError(deploymentId, record) {
	return new CliStructuredError("SERVICE.LOGS_FAILED", `Log stream failed for version ${deploymentId}`, {
		why: record.message,
		meta: {
			code: record.code,
			retryable: record.retryable,
			...record.cursor === null ? {} : { cursor: record.cursor }
		},
		nextActions: [runCommandAction("Show the version", `service version show ${deploymentId}`)]
	});
}
function listDeployments(ctx, provider, service) {
	return provider.listDeployments(service.id, { signal: ctx.signal }).catch((error) => {
		throw deployFailedError("Failed to list service versions", error, [runCommandAction("List versions", `service version list ${service.name}`)]);
	});
}
/** `--version-id <id>` with a service target: the id must belong to
*  the resolved service. */
async function resolveVersionInService(ctx, state, deploymentId) {
	const deploymentsResult = await listDeployments(ctx, state.provider, state.service);
	const deployment = requireVersionForService(deploymentsResult.deployments, deploymentId, state.service.name);
	return {
		service: deploymentsResult.app,
		version: deployment
	};
}
/** No `--version-id`: read whatever is live for the resolved service. */
async function resolveLiveVersion(ctx, state) {
	const deploymentsResult = await listDeployments(ctx, state.provider, state.service);
	const currentLiveDeploymentId = resolveCurrentLiveVersionId(deploymentsResult.app, deploymentsResult.deployments);
	const deployments = applyLiveVersionHint(deploymentsResult.deployments, currentLiveDeploymentId);
	const deployment = currentLiveDeploymentId ? deployments.find((candidate) => candidate.id === currentLiveDeploymentId) ?? null : null;
	if (!deployment) throw noVersionsError("No versions available to read logs from", `The service "${deploymentsResult.app.name}" does not have a live version.`, deploymentsResult.app.name);
	return {
		service: deploymentsResult.app,
		version: deployment
	};
}
/**
* Reads one page and reports its log records. Returns the terminal
* record that closed it — the caller decides whether that ends the run
* or starts the next page.
*
* Every page ends with a terminal record, so a body that stops without
* one was truncated. The lines that did arrive have already been
* reported, but the run must not settle as though it had read the whole
* page: the user would have a partial log and no way to tell.
*/
async function readPage(ctx, deploymentId, query) {
	const { data, response } = await ctx.api.GET("/v1/deployments/{deploymentId}/logs", {
		params: {
			path: { deploymentId },
			query
		},
		parseAs: "stream",
		signal: ctx.signal
	});
	const body = data;
	if (!response.ok || !body) {
		await body?.cancel().catch(() => void 0);
		throw response.status === 404 ? versionNotFoundError(deploymentId) : logsFailedError(deploymentId, response.status);
	}
	let terminal = null;
	await forEachNdjsonRecord(body, (record) => {
		if (record.type === "terminal") {
			terminal = record;
			return;
		}
		ctx.report({
			kind: "output",
			source: "logs",
			channel: "data",
			line: record.text.replace(TRAILING_NEWLINE, ""),
			data: {
				byteStart: record.byteStart,
				byteEnd: record.byteEnd
			}
		});
	});
	if (terminal === null) throw logsIncompleteError(deploymentId);
	return terminal;
}
/**
* Following needs somewhere to resume from. Without a cursor the next
* request would carry no range at all, the endpoint would apply its
* default tail, and the same lines would print again every interval —
* silent duplication the user cannot act on. So the run stops and says
* why. It settles as an error rather than a clean end because `--follow`
* has no successful ending: it runs until interrupted (130) or fails,
* and an exit 0 here would be a novel outcome meaning "gave up".
*/
function requireResumeCursor(deploymentId, cursor) {
	if (cursor === null) throw new CliStructuredError("SERVICE.LOGS_NO_CURSOR", `Cannot follow logs for version ${deploymentId}`, {
		why: "The log page ended without a resume cursor, so there is no point to continue reading from.",
		nextActions: [adviceAction("Rerun without --follow to read the page, or retry if the version is still starting.")]
	});
	return cursor;
}
/**
* `--follow`: wait the poll interval, read the next page from the cursor
* the last one ended on, repeat until the user interrupts. Never
* returns — the run ends by abort (the engine settles 130) or by throw.
*/
async function followPages(ctx, deploymentId, startCursor) {
	const interval = pollIntervalMs(ctx);
	let cursor = requireResumeCursor(deploymentId, startCursor);
	let retriedAfterError = false;
	for (;;) {
		await sleep(interval, ctx.signal);
		const next = await readPage(ctx, deploymentId, { cursor });
		if (next.kind === "error") {
			if (!next.retryable || retriedAfterError) throw logStreamFailedError(deploymentId, next);
			retriedAfterError = true;
			continue;
		}
		retriedAfterError = false;
		cursor = requireResumeCursor(deploymentId, next.cursor);
	}
}
/**
* A globally-unique version id is a complete target on its own, so
* `--version-id` with no service argument resolves it directly, the
* way `service version show` does — no project resolution at all. A
* service argument scopes the lookup to that service.
*/
async function resolveLogsTarget(ctx, options) {
	const explicitVersionId = options.versionId;
	const serviceRequested = options.service !== void 0;
	if (explicitVersionId !== void 0 && !serviceRequested) {
		const subject = await resolveVersionSubject(ctx, explicitVersionId);
		return {
			projectId: null,
			target: {
				service: subject.service,
				version: subject.version
			}
		};
	}
	const readState = await resolveServiceReadState(ctx, {
		...options.service !== void 0 ? { serviceName: options.service } : {},
		...options.project !== void 0 ? { projectRef: options.project } : {},
		...options.branch !== void 0 ? { branchName: options.branch } : {},
		commandName: "service logs"
	});
	return {
		projectId: readState.projectId,
		target: explicitVersionId !== void 0 ? await resolveVersionInService(ctx, readState, explicitVersionId) : await resolveLiveVersion(ctx, readState)
	};
}
const serviceLogsCommand = defineSessionCommand({
	help: {
		summary: "Read logs for a version of the service",
		description: "Reads the live version's logs by default; point --version-id at any other version, such as a failed build being debugged. --follow keeps streaming new lines, and --tail or --from-start choose where reading starts.",
		examples: [
			"service logs my-service",
			"service logs my-service --tail 500",
			"service logs my-service --follow",
			"service logs --version-id cpv_123 --from-start"
		]
	},
	args: {
		positionals: { service: positional.optionalString({
			brief: "Service id or name",
			placeholder: "service"
		}) },
		flags: {
			project: flag.string({
				brief: "Project id or name",
				placeholder: "id-or-name"
			}),
			branch: flag.string({
				brief: "Branch the service lives on (default: the default branch)",
				placeholder: "name"
			}),
			versionId: flag.string({
				brief: "Service version id to read; use for a failed or stopped version (default: the live version)",
				placeholder: "id"
			}),
			tail: flag.number({
				brief: `Read the last N lines (default ${DEFAULT_TAIL})`,
				placeholder: "n"
			}),
			fromStart: flag.boolean({ brief: "Read from the beginning instead of the last lines" }),
			follow: flag.boolean({ brief: "Keep polling for new lines until interrupted" })
		}
	},
	needs: { credentials: true },
	handler: async (args, ctx) => {
		if (args.flags.fromStart && args.flags.tail !== void 0) throw logsRangeConflictError();
		const { projectId, target } = await resolveLogsTarget(ctx, {
			service: args.positionals.service,
			...args.flags
		});
		const versionId = target.version.id;
		for (const line of [
			...projectId === null ? [] : [`project: ${projectId}`],
			`service: ${target.service.name}`,
			`version: ${versionId}`
		]) ctx.report({
			kind: "output",
			source: "logs",
			channel: "diagnostic",
			line
		});
		const terminal = await readPage(ctx, versionId, args.flags.fromStart ? { from_start: "true" } : { tail: args.flags.tail ?? DEFAULT_TAIL });
		if (terminal.kind === "error") throw logStreamFailedError(versionId, terminal);
		if (!args.flags.follow) return ok(void 0);
		return followPages(ctx, versionId, terminal.cursor);
	}
});
//#endregion
//#region ../cli/src/commands/service/open.ts
const serviceOpenCommand = defineCommand({
	help: {
		summary: "Open the service's live URL",
		examples: ["service open my-service", "service open my-service --branch feature-x"]
	},
	args: {
		flags: {
			project: flag.string({
				brief: "Project id or name",
				placeholder: "id-or-name"
			}),
			branch: flag.string({
				brief: "Branch the service lives on (default: the default branch)",
				placeholder: "name"
			})
		},
		positionals: { service: positional.optionalString({
			brief: "Service id or name",
			placeholder: "service"
		}) }
	},
	needs: { credentials: true },
	handler: async (args, ctx) => {
		const state = await resolveServiceReadState(ctx, {
			serviceName: args.positionals.service,
			projectRef: args.flags.project,
			branchName: args.flags.branch,
			commandName: "service open"
		});
		const deploymentsResult = await state.provider.listDeployments(state.service.id, { signal: ctx.signal }).catch((error) => {
			throw deployFailedError("Failed to resolve service URL", error, [runCommandAction("Inspect the service", `service show ${state.service.name}`)]);
		});
		const currentLiveDeploymentId = resolveCurrentLiveVersionId(deploymentsResult.app, deploymentsResult.deployments);
		const deployments = sortVersionsNewestFirst(applyLiveVersionHint(deploymentsResult.deployments, currentLiveDeploymentId));
		const liveDeployment = currentLiveDeploymentId ? deployments.find((deployment) => deployment.id === currentLiveDeploymentId) ?? null : null;
		if (!liveDeployment) throw noVersionsError("No versions available to open", `The service "${deploymentsResult.app.name}" does not have any versions yet.`, deploymentsResult.app.name);
		if (!deploymentsResult.app.liveUrl) throw liveUrlUnavailableError(deploymentsResult.app.name);
		const url = deploymentsResult.app.liveUrl;
		const { opened } = await ctx.openUrl({
			url,
			message: "Live URL"
		});
		const result = {
			projectId: state.projectId,
			service: toServiceSummary(deploymentsResult.app),
			url,
			opened
		};
		return ok(ctx.present({ data: result }, openPresentations(result, liveDeployment.id)));
	}
});
//#endregion
//#region ../cli/src/commands/service/show.ts
const serviceShowCommand = defineCommand({
	help: {
		summary: "Show a service and the version currently serving traffic",
		description: "Shows the version serving traffic and the service's live URL, when the service has a live version; a service that has never deployed has none yet. Inspect other versions with 'service version list' and 'service version show'.",
		examples: ["service show my-service", "service show my-service --branch feature-x"]
	},
	args: {
		flags: {
			project: flag.string({
				brief: "Project id or name",
				placeholder: "id-or-name"
			}),
			branch: flag.string({
				brief: "Branch the service lives on (default: the default branch)",
				placeholder: "name"
			})
		},
		positionals: { service: positional.optionalString({
			brief: "Service id or name",
			placeholder: "service"
		}) }
	},
	needs: { credentials: true },
	handler: async (args, ctx) => {
		const state = await resolveServiceReadState(ctx, {
			serviceName: args.positionals.service,
			projectRef: args.flags.project,
			branchName: args.flags.branch,
			commandName: "service show"
		});
		const deploymentsResult = await state.provider.listDeployments(state.service.id, { signal: ctx.signal }).catch((error) => {
			throw deployFailedError("Failed to inspect service", error, [runCommandAction("List versions", `service version list ${state.service.name}`)]);
		});
		const currentLiveDeploymentId = resolveCurrentLiveVersionId(deploymentsResult.app, deploymentsResult.deployments);
		const deployments = sortVersionsNewestFirst(applyLiveVersionHint(deploymentsResult.deployments, currentLiveDeploymentId));
		const liveVersion = currentLiveDeploymentId ? deployments.find((deployment) => deployment.id === currentLiveDeploymentId) ?? null : null;
		const result = {
			projectId: state.projectId,
			service: toServiceSummary(deploymentsResult.app),
			liveVersion,
			liveUrl: liveVersion ? deploymentsResult.app.liveUrl : null,
			recentVersions: deployments.slice(0, 5)
		};
		return ok(ctx.present({ data: result }, showPresentations(result)));
	}
});
//#endregion
//#region ../cli/src/commands/service/version-delete.ts
const serviceVersionDeleteCommand = defineCommand({
	help: {
		summary: "Delete a service version and the artifact it holds",
		description: "Permanently removes the version and its built artifact, so it can no longer be started or rolled back to. Pass the exact version id with --confirm to run non-interactively.",
		examples: ["service version delete cpv_123", "service version delete cpv_123 --confirm cpv_123"]
	},
	args: { positionals: { version: positional.string({
		brief: "Version id to delete",
		placeholder: "version"
	}) } },
	needs: { credentials: true },
	handler: async (args, ctx) => {
		const { provider, service, version } = await resolveVersionSubject(ctx, args.positionals.version);
		if (!await ctx.prompt.consent(`Delete version "${version.id}" from Service "${service.name}"?`, { token: version.id })) throw userCancelledError("Deployment deletion canceled");
		ctx.report({
			kind: "step-started",
			step: "delete"
		});
		try {
			await provider.deleteDeployment({
				deploymentId: version.id,
				signal: ctx.signal
			});
		} catch (error) {
			ctx.report({
				kind: "step-finished",
				step: "delete",
				outcome: "failed"
			});
			throw deployFailedError("Failed to delete version", error, [runCommandAction("List versions", `service version list ${service.name}`)]);
		}
		ctx.report({
			kind: "step-finished",
			step: "delete",
			outcome: "ok"
		});
		const result = {
			service: toServiceSummary(service),
			versionId: version.id,
			deleted: true
		};
		return ok(ctx.present({ data: result }, versionDeletePresentations(result)));
	}
});
//#endregion
//#region ../cli/src/commands/service/version-list.ts
const serviceVersionListCommand = defineCommand({
	help: {
		summary: "List versions of the service",
		description: "Every deploy produces an immutable service version; at most one serves traffic at a time. Use the listed ids with 'service version' promote, rollback, start, stop, and delete.",
		examples: ["service version list my-service", "service version list my-service --branch feature-x"]
	},
	args: {
		flags: {
			project: flag.string({
				brief: "Project id or name",
				placeholder: "id-or-name"
			}),
			branch: flag.string({
				brief: "Branch the service lives on (default: the default branch)",
				placeholder: "name"
			})
		},
		positionals: { service: positional.optionalString({
			brief: "Service id or name",
			placeholder: "service"
		}) }
	},
	needs: { credentials: true },
	handler: async (args, ctx) => {
		const state = await resolveServiceReadState(ctx, {
			serviceName: args.positionals.service,
			projectRef: args.flags.project,
			branchName: args.flags.branch,
			commandName: "service version list"
		});
		const deploymentsResult = await state.provider.listDeployments(state.service.id, { signal: ctx.signal }).catch((error) => {
			throw deployFailedError("Failed to list service versions", error, []);
		});
		const currentLiveDeploymentId = resolveCurrentLiveVersionId(deploymentsResult.app, deploymentsResult.deployments);
		const deployments = sortVersionsNewestFirst(applyLiveVersionHint(deploymentsResult.deployments, currentLiveDeploymentId));
		const result = {
			projectId: state.projectId,
			service: toServiceSummary(deploymentsResult.app),
			versions: deployments
		};
		return ok(ctx.present({ data: result }, versionListPresentations(result)));
	}
});
//#endregion
//#region ../cli/src/commands/service/version-promote.ts
const serviceVersionPromoteCommand = defineCommand({
	help: {
		summary: "Promote a service version to production by rebuilding with production env vars",
		description: "The normal release path: take a version that proved itself on a preview branch, rebuild its source with production environment variables, and switch production traffic to the result. To undo a release, use 'service version rollback', which needs no rebuild.",
		examples: ["service version promote cpv_123"]
	},
	args: { positionals: { version: positional.string({
		brief: "Version id to promote",
		placeholder: "version"
	}) } },
	needs: { credentials: true },
	handler: async (args, ctx) => {
		const { provider, service, version } = await resolveVersionSubject(ctx, args.positionals.version);
		const alreadyLive = service.liveDeploymentId === version.id;
		if (!alreadyLive) {
			ctx.report({
				kind: "step-started",
				step: "promote"
			});
			try {
				await provider.promoteDeployment({
					appId: service.id,
					deploymentId: version.id,
					signal: ctx.signal,
					progress: promoteProgressReporter(ctx, version.id)
				});
			} catch (error) {
				ctx.report({
					kind: "step-finished",
					step: "promote",
					outcome: "failed"
				});
				throw deployFailedError("Failed to promote version", error, [runCommandAction("List versions", `service version list ${service.name}`)]);
			}
			ctx.report({
				kind: "step-finished",
				step: "promote",
				outcome: "ok"
			});
		}
		const result = {
			service: toServiceSummary(service),
			version: {
				...version,
				status: "running",
				live: true
			}
		};
		const diagnostics = alreadyLive ? [{
			code: "SERVICE.VERSION_ALREADY_LIVE",
			severity: "warn",
			summary: "The selected version is already live for this service.",
			nextActions: []
		}] : [];
		return ok(ctx.present({
			data: result,
			diagnostics
		}, promotePresentations(result, alreadyLive)));
	}
});
//#endregion
//#region ../cli/src/commands/service/version-rollback.ts
const serviceVersionRollbackCommand = defineCommand({
	help: {
		summary: "Roll back production to a previous service version",
		description: "Returns production traffic to an already-built version without rebuilding, which makes it the fastest way out of a bad release. Defaults to the version before the live one; name an exact target with --to.",
		examples: [
			"service version rollback my-service",
			"service version rollback my-service --to cpv_123",
			"service version rollback my-service --to cpv_123 --confirm cpv_123"
		]
	},
	args: {
		flags: {
			project: flag.string({
				brief: "Project id or name",
				placeholder: "id-or-name"
			}),
			branch: flag.string({
				brief: "Branch the service lives on (default: the default branch)",
				placeholder: "name"
			}),
			to: flag.string({
				brief: "Version id to roll back to; use when the last good version is further back (default: the version before the live one)",
				placeholder: "version"
			})
		},
		positionals: { service: positional.optionalString({
			brief: "Service id or name",
			placeholder: "service"
		}) }
	},
	needs: { credentials: true },
	handler: async (args, ctx) => {
		const state = await resolveServiceReadState(ctx, {
			serviceName: args.positionals.service,
			projectRef: args.flags.project,
			branchName: args.flags.branch,
			commandName: "service version rollback"
		});
		const deploymentsResult = await state.provider.listDeployments(state.service.id, { signal: ctx.signal }).catch((error) => {
			throw deployFailedError("Failed to list service versions", error, [runCommandAction("List versions", `service version list ${state.service.name}`)]);
		});
		const currentLiveDeploymentId = resolveCurrentLiveVersionId(deploymentsResult.app, deploymentsResult.deployments);
		const targetVersion = args.flags.to ? requireVersionForService(deploymentsResult.deployments, args.flags.to, state.service.name) : resolveRollbackTarget(deploymentsResult.deployments, currentLiveDeploymentId, state.service.name);
		if (!await ctx.prompt.consent(`Roll back Service "${state.service.name}" to version ${targetVersion.id} and make it live?`, { token: targetVersion.id })) throw userCancelledError("Service rollback canceled");
		const alreadyLive = currentLiveDeploymentId === targetVersion.id;
		if (!alreadyLive) {
			ctx.report({
				kind: "step-started",
				step: "rollback"
			});
			try {
				await state.provider.promoteDeployment({
					appId: state.service.id,
					deploymentId: targetVersion.id,
					signal: ctx.signal,
					progress: promoteProgressReporter(ctx, targetVersion.id)
				});
			} catch (error) {
				ctx.report({
					kind: "step-finished",
					step: "rollback",
					outcome: "failed"
				});
				throw deployFailedError("Failed to roll back version", error, [runCommandAction("List versions", `service version list ${state.service.name}`)]);
			}
			ctx.report({
				kind: "step-finished",
				step: "rollback",
				outcome: "ok"
			});
		}
		const result = {
			projectId: state.projectId,
			service: toServiceSummary(deploymentsResult.app),
			version: {
				...targetVersion,
				status: "running",
				live: true
			},
			previousLiveVersionId: currentLiveDeploymentId
		};
		const diagnostics = alreadyLive ? [{
			code: "SERVICE.VERSION_ALREADY_LIVE",
			severity: "warn",
			summary: "The selected version is already live for this service.",
			nextActions: []
		}] : [];
		return ok(ctx.present({
			data: result,
			diagnostics
		}, rollbackPresentations(result, alreadyLive)));
	}
});
//#endregion
//#region ../cli/src/commands/service/version-show.ts
const serviceVersionShowCommand = defineCommand({
	help: {
		summary: "Show a service version in detail",
		description: "Version ids are globally unique, so the id alone is the complete target: no --project or --branch scope is needed.",
		examples: ["service version show cpv_123"]
	},
	args: { positionals: { version: positional.string({
		brief: "Version id",
		placeholder: "version"
	}) } },
	needs: { credentials: true },
	handler: async (args, ctx) => {
		const versionId = args.positionals.version;
		const shown = await serviceProvider(ctx).showDeployment(versionId, { signal: ctx.signal }).catch((error) => {
			throw deployFailedError("Failed to show version", error, [runCommandAction("List versions", "service version list <service>")]);
		});
		if (!shown) throw versionNotFoundError(versionId);
		const result = {
			service: shown.app ? toServiceSummary(shown.app) : null,
			version: {
				...shown.deployment,
				live: shown.app ? shown.app.liveDeploymentId === shown.deployment.id : null
			}
		};
		return ok(ctx.present({ data: result }, versionShowPresentations(result)));
	}
});
//#endregion
//#region ../cli/src/commands/service/version-run-state.ts
/**
* `start` and `stop` are the same command with the direction reversed,
* so the verb decides every value that differs between them. Spelling
* that out here keeps the two from drifting apart, which is the risk
* with a body this long duplicated.
*/
const VERBS = {
	start: {
		/** The status the API reports once the version is up. */
		settledStatus: "running",
		diagnosticCode: "SERVICE.VERSION_ALREADY_RUNNING",
		diagnosticSummary: "The selected version is already running.",
		failureSummary: "Failed to start version",
		presentations: versionStartPresentations
	},
	stop: {
		settledStatus: "stopped",
		diagnosticCode: "SERVICE.VERSION_ALREADY_STOPPED",
		diagnosticSummary: "The selected version is already stopped.",
		failureSummary: "Failed to stop version",
		presentations: versionStopPresentations
	}
};
async function changeVersionRunState(ctx, versionId, verb) {
	const spec = VERBS[verb];
	const { provider, service, version } = await resolveVersionSubject(ctx, versionId);
	const alreadyInState = version.status === spec.settledStatus;
	let observed = version;
	if (!alreadyInState) {
		ctx.report({
			kind: "step-started",
			step: verb
		});
		try {
			await (verb === "start" ? provider.startDeployment({
				deploymentId: version.id,
				signal: ctx.signal
			}) : provider.stopDeployment({
				deploymentId: version.id,
				signal: ctx.signal
			}));
			observed = await provider.readDeployment({
				deploymentId: version.id,
				signal: ctx.signal
			});
		} catch (error) {
			ctx.report({
				kind: "step-finished",
				step: verb,
				outcome: "failed"
			});
			throw deployFailedError(spec.failureSummary, error, [runCommandAction("Show the version", `service version show ${version.id}`)]);
		}
		ctx.report({
			kind: "step-finished",
			step: verb,
			outcome: "ok"
		});
	}
	const result = {
		service: toServiceSummary(service),
		version: observed,
		alreadyInState
	};
	return {
		result,
		diagnostics: alreadyInState ? [{
			code: spec.diagnosticCode,
			severity: "warn",
			summary: spec.diagnosticSummary,
			nextActions: []
		}] : [],
		presentations: spec.presentations(result)
	};
}
//#endregion
//#region ../cli/src/commands/service/version-start.ts
const serviceVersionStartCommand = defineCommand({
	help: {
		summary: "Start a stopped service version",
		examples: ["service version start cpv_123"]
	},
	args: { positionals: { version: positional.string({
		brief: "Version id to start",
		placeholder: "version"
	}) } },
	needs: { credentials: true },
	handler: async (args, ctx) => {
		const { result, diagnostics, presentations } = await changeVersionRunState(ctx, args.positionals.version, "start");
		return ok(ctx.present({
			data: result,
			diagnostics
		}, presentations));
	}
});
//#endregion
//#region ../cli/src/commands/service/version-stop.ts
const serviceVersionStopCommand = defineCommand({
	help: {
		summary: "Stop a running service version",
		examples: ["service version stop cpv_123"]
	},
	args: { positionals: { version: positional.string({
		brief: "Version id to stop",
		placeholder: "version"
	}) } },
	needs: { credentials: true },
	handler: async (args, ctx) => {
		const { result, diagnostics, presentations } = await changeVersionRunState(ctx, args.positionals.version, "stop");
		return ok(ctx.present({
			data: result,
			diagnostics
		}, presentations));
	}
});
//#endregion
//#region ../cli/src/commands/skills/family.ts
/** Skill delivery is product-agnostic — the same two commands serve the
*  ORM's skills and Composer's — so it is its own family rather than
*  part of either product's. */
const skillsCommandFamily = defineCommandFamily({
	configSection: skillsConfigSection,
	docsBaseUrl: DOCS_ERRORS_BASE_URL,
	commands: {
		sync: skillsSyncCommand,
		list: defineCommand({
			help: {
				summary: "Show which Prisma agent skills are installed in this project",
				description: "Shows each skill, the package and version it came from, and whether the synced copies are current. Run 'skills sync' to bring stale copies up to date.",
				examples: ["skills list", "skills list --json"]
			},
			needs: { config: skillsConfigSection },
			handler: async (_args, ctx) => {
				const status = await readSkillsStatus(ctx.cwd, { agents: ctx.config.agents });
				const result = {
					projectRoot: status.projectRoot,
					agents: ctx.config.agents,
					packages: packageReports(status.packages),
					skills: status.skills.map((skill) => ({
						skill: skill.skill,
						library: skill.library,
						version: skill.version,
						upToDate: skill.upToDate,
						targets: skill.targets.map((target) => ({
							dir: target.dir,
							syncedVersion: target.syncedVersion,
							state: target.state
						}))
					})),
					orphaned: status.orphans.map((orphan) => ({
						skill: orphan.skill,
						library: orphan.library,
						dirs: orphan.dirs
					})),
					checkDisabled: status.checkDisabled || !ctx.config.check,
					upToDate: status.upToDate
				};
				return ok(ctx.present({
					data: result,
					diagnostics: versionConflictDiagnostics(status.packages)
				}, listPresentations$5(result)));
			}
		})
	}
});
//#endregion
//#region ../cli/src/cli.ts
const platformCommandFamily = defineCommandFamily({
	docsBaseUrl: DOCS_ERRORS_BASE_URL,
	commands: {
		login: authLoginCommand,
		logout: authLogoutCommand,
		whoami: authWhoamiCommand,
		workspaceList: authWorkspaceListCommand,
		workspaceUse: authWorkspaceUseCommand,
		workspaceLogout: authWorkspaceLogoutCommand,
		projectList: projectListCommand,
		projectShow: projectShowCommand,
		projectCreate: projectCreateCommand,
		projectLink: projectLinkCommand,
		projectRename: projectRenameCommand,
		projectDelete: projectDeleteCommand,
		projectTransfer: projectTransferCommand,
		projectEnvAdd: projectEnvAddCommand,
		projectEnvUpdate: projectEnvUpdateCommand,
		projectEnvList: projectEnvListCommand,
		projectEnvDelete: projectEnvDeleteCommand,
		postgresList: postgresListCommand,
		postgresShow: postgresShowCommand,
		postgresCreate: postgresCreateCommand,
		postgresUsage: postgresUsageCommand,
		postgresBackupRestore: postgresBackupRestoreCommand,
		postgresDelete: postgresDeleteCommand,
		postgresBackupList: postgresBackupListCommand,
		postgresConnectionList: postgresConnectionListCommand,
		postgresConnectionCreate: postgresConnectionCreateCommand,
		postgresConnectionRotate: postgresConnectionRotateCommand,
		postgresConnectionDelete: postgresConnectionDeleteCommand,
		bucketList: bucketListCommand,
		bucketCreate: bucketCreateCommand,
		bucketDelete: bucketDeleteCommand,
		bucketKeyList: bucketKeyListCommand,
		bucketKeyCreate: bucketKeyCreateCommand,
		bucketKeyDelete: bucketKeyDeleteCommand,
		branchList: branchListCommand,
		gitConnect: gitConnectCommand,
		gitDisconnect: gitDisconnectCommand,
		serviceList: serviceListCommand,
		serviceLogs: serviceLogsCommand,
		serviceCreate: serviceCreateCommand,
		serviceShow: serviceShowCommand,
		serviceOpen: serviceOpenCommand,
		serviceVersionList: serviceVersionListCommand,
		serviceVersionShow: serviceVersionShowCommand,
		serviceVersionPromote: serviceVersionPromoteCommand,
		serviceVersionRollback: serviceVersionRollbackCommand,
		serviceVersionStart: serviceVersionStartCommand,
		serviceVersionStop: serviceVersionStopCommand,
		serviceVersionDelete: serviceVersionDeleteCommand,
		serviceDelete: serviceDeleteCommand,
		serviceDomainAdd: serviceDomainAddCommand,
		serviceDomainShow: serviceDomainShowCommand,
		serviceDomainDelete: serviceDomainDeleteCommand,
		serviceDomainRetry: serviceDomainRetryCommand,
		serviceDomainWait: serviceDomainWaitCommand
	}
});
/**
* Composer's commands, contributed by composer's own package and run by
* this process, mounted as shipped. Only the command definitions and
* their handler entry functions load here; the alchemy and effect
* constellation stays behind composer's dynamic executor imports, so
* mounting costs an unrelated command nothing.
*/
const composerCommandFamily = createComposerFamily();
/**
* The ORM commands, contributed by orm-toolchain's own package, mounted
* as shipped: the family keys are the mount paths, so the shell adds
* nothing. The family object carries its `orm` config section, its docs
* base and its redirect table. Unlike composer's, this family's entry
* module imports esbuild and arktype statically, so every invocation of
* this bin pays that import; fixing that is orm-toolchain's move.
*/
const ormCommandFamily$1 = ormCommandFamily;
/** The engine ships the three telemetry commands and the group help
*  text that belongs to them; both halves are spread in below. */
const telemetry = telemetryCommandGroup({ docsUrl: CLI_DOCS_URL });
const cliGroups = {
	auth: {
		brief: "Manage authentication for the Prisma Platform. Sign in and out, inspect identity, switch workspaces",
		description: "Signing in opens a browser flow and stores a session for one workspace: the account-level container that holds your Projects, members, and billing. Commands act on the current session's workspace; log in once per workspace and switch with the 'workspace' subcommands. A PRISMA_SERVICE_TOKEN environment credential overrides stored sessions, which is the way to authenticate CI."
	},
	project: {
		brief: "Manage Prisma Platform projects. CRUD, link a directory, transfer ownership, manage environment variables",
		description: "A Project groups one product or codebase. It is the child of a workspace and the parent of Branches: isolated environments, one per Git branch, each holding its own services, databases, and buckets. 'Linking' connects a local directory to a Project: the link is stored locally, and every project-scoped command run in that directory targets the linked project unless --project names another. 'create' links automatically; 'link' points a directory at an existing Project; 'show' reports the link.",
		workflow: [
			{
				run: "project create my-app",
				brief: "Create a Project, link this directory"
			},
			{
				run: "git connect",
				brief: "Connect GitHub; branches carrying the deploy workflow deploy on push"
			},
			{
				run: "project env add KEY=value --role preview",
				brief: "Set env vars services get at deploy"
			}
		]
	},
	"project env": {
		brief: "Manage a project's environment variables. Add, update, list, and delete values per scope",
		description: "Variables live in scopes: production, preview (shared by every preview branch), or a single branch's override. Values reach services when they deploy, and are write-only afterwards: list shows metadata, never values."
	},
	postgres: {
		brief: "Manage Prisma Postgres databases. CRUD, usage metrics, backups, and connection credentials",
		description: "Databases are branch-bound: each belongs to a Branch, the isolated environment for one Git branch of a project, so commands take --branch to target one. Address a database by its id (db_...) or name. Connection URLs are secrets that print exactly once, at create or rotate; nothing shows them again. 'backup' restores platform-taken backups, and 'connection' manages per-consumer credentials.",
		workflow: [
			{
				run: "postgres create app-db",
				brief: "Create a database; its URL prints once"
			},
			{
				run: "postgres connection create app-db --name ci",
				brief: "Mint one credential per consumer"
			},
			{
				run: "postgres connection rotate conn_123",
				brief: "Replace a leaked credential"
			}
		]
	},
	"postgres backup": {
		brief: "Inspect and restore database backups. The platform takes them automatically",
		description: "The platform takes backups automatically; there is no backup-create command. Restore replaces a database's current state with a backup's contents after exact id confirmation."
	},
	"postgres connection": {
		brief: "Manage database connection credentials. Create, rotate, and revoke per-consumer connection URLs",
		description: "A connection is one independent credential (a connection URL) for one database. Give each consumer (an app, CI, a teammate) its own, so one can be rotated or revoked without breaking the others. URLs print once, at create or rotate; list shows metadata only."
	},
	bucket: {
		brief: "Manage S3-compatible object-store buckets for a project. CRUD operations and access keys",
		description: "A bucket is blob storage for files and uploads, bound to one Branch of a project and reachable through the standard S3 API. Access goes through S3-compatible keys minted per consumer with 'bucket key create', which prints credentials (endpoint, key id, secret) exactly once.",
		workflow: [{
			run: "bucket create --name uploads",
			brief: "Create a bucket in the branch"
		}, {
			run: "bucket key create bkt_123",
			brief: "Mint S3 credentials, shown once"
		}]
	},
	"bucket key": {
		brief: "Manage a bucket's access keys. Create, list, and revoke per-consumer S3 credentials",
		description: "A key is one consumer's S3-compatible credentials for one bucket, with role read or read_write. Secrets print once at create; delete revokes access immediately."
	},
	branch: {
		brief: "View Platform branches: the isolated environment behind each Git branch",
		description: "A Branch maps to a Git branch of the connected repository. Each is an isolated environment with its own services, databases, buckets, and environment variables: the production branch serves live traffic, every other branch is a preview."
	},
	git: {
		brief: "Manage the GitHub connection behind deploy-on-push. Connect or disconnect a repository",
		description: "Connecting a GitHub repository turns on deploy-on-push: the repository's prisma/cloud-deploy-action workflow authenticates through the connection and deploys every pushed branch to a matching Platform Branch. The platform does not build on push; the workflow does. Disconnecting stops push deploys without touching anything already deployed."
	},
	service: {
		brief: "Manage deployed services. Logs, versions, promote and rollback releases, custom domains",
		description: "A service is one HTTP application (a frontend or a backend) deployed on a Branch. Every deploy produces an immutable service version; at most one serves traffic at a time. The 'version' subcommands move which one that is: promote releases a preview build into production, rollback returns production to a previous version without rebuilding. 'logs' reads and streams output, and 'domain' attaches hostnames you own.",
		workflow: [
			{
				run: "service logs my-api --follow",
				brief: "Stream the live version's logs"
			},
			{
				run: "service version promote cpv_123",
				brief: "Release a preview build to production"
			},
			{
				run: "service version rollback my-api",
				brief: "Put production back on the previous version"
			}
		]
	},
	"service domain": {
		brief: "Manage custom domains for a service. Register hostnames, drive DNS and TLS verification, inspect status",
		description: "Custom domains point hostnames you own at a service's production branch. After add, create the DNS record the platform reports; wait and retry drive DNS verification and TLS provisioning to done.",
		workflow: [
			{
				run: "service domain add shop.acme.com --service my-api",
				brief: "Register the hostname"
			},
			{
				run: "service domain wait shop.acme.com --service my-api",
				brief: "Block until active or failed"
			},
			{
				run: "service domain show shop.acme.com --service my-api",
				brief: "Inspect status and certificate"
			}
		]
	},
	"service version": {
		brief: "Manage a service's deploy versions. List, inspect, promote, roll back, start, stop, delete",
		description: "Every deploy produces an immutable version; at most one serves traffic at a time on each branch. List and show inspect them. Promote and rollback choose which version serves traffic; start, stop, and delete drive one version's lifecycle."
	},
	"auth workspace": {
		brief: "Manage stored workspace sessions. List them, switch the current one, end one",
		description: "One session is stored per workspace you log in to. List them, switch the current one, or end one without touching the others."
	},
	contract: {
		brief: "Author your data contract: the PSL source of your data model. Emit, infer, format, print",
		description: "A contract is the declarative description of your application's data model, authored in PSL (Prisma Schema Language). Migrations are planned from it, and live databases are verified and signed against it. Emit generates its artifacts, infer derives a contract from an existing database, format normalizes the source, and print writes the contract your config loads as PSL."
	},
	db: {
		brief: "Run contract operations against a live database. Verify, sign, update, migrate",
		description: "These commands run against a live database, addressed with --db <url>. Verify checks the database against the contract (the PSL description of your data model), sign marks it as matching, and update and migrate advance its schema."
	},
	migration: {
		brief: "Manage on-disk migrations derived from contract changes. Plan, inspect, check, track history",
		description: "A migration is an on-disk package describing one schema change, derived from edits to your contract (the PSL description of your data model). Plan writes one; the rest inspect, check integrity, and track what has run where. Apply them with 'db migrate'."
	},
	"migration ref": {
		brief: "Manage refs: named pointers to contracts. Set, list, delete",
		description: "A ref is a named pointer to a contract, letting commands target a contract by a stable name. Set, list, and delete refs here."
	},
	orm: { brief: "Initialize a Prisma ORM project" },
	skills: {
		brief: "Manage Prisma skills for AI coding agents. Sync and list the instruction files",
		description: "Agent skills are instruction files that teach AI coding agents (Claude Code, Cursor, and others) how to use the installed Prisma packages. They ship inside the packages; sync copies them into the directories the agent harnesses read."
	},
	...telemetry.groups
};
const mountedCommands = {
	"auth login": authLoginCommand,
	"auth logout": authLogoutCommand,
	"auth whoami": authWhoamiCommand,
	"auth workspace list": authWorkspaceListCommand,
	"auth workspace use": authWorkspaceUseCommand,
	"auth workspace logout": authWorkspaceLogoutCommand,
	"project list": projectListCommand,
	"project show": projectShowCommand,
	"project create": projectCreateCommand,
	"project link": projectLinkCommand,
	"project rename": projectRenameCommand,
	"project delete": projectDeleteCommand,
	"project transfer": projectTransferCommand,
	"project env add": projectEnvAddCommand,
	"project env update": projectEnvUpdateCommand,
	"project env list": projectEnvListCommand,
	"project env delete": projectEnvDeleteCommand,
	"postgres list": postgresListCommand,
	"postgres show": postgresShowCommand,
	"postgres create": postgresCreateCommand,
	"postgres usage": postgresUsageCommand,
	"postgres delete": postgresDeleteCommand,
	"postgres backup list": postgresBackupListCommand,
	"postgres backup restore": postgresBackupRestoreCommand,
	"postgres connection list": postgresConnectionListCommand,
	"postgres connection create": postgresConnectionCreateCommand,
	"postgres connection rotate": postgresConnectionRotateCommand,
	"postgres connection delete": postgresConnectionDeleteCommand,
	"bucket list": bucketListCommand,
	"bucket create": bucketCreateCommand,
	"bucket delete": bucketDeleteCommand,
	"bucket key list": bucketKeyListCommand,
	"bucket key create": bucketKeyCreateCommand,
	"bucket key delete": bucketKeyDeleteCommand,
	"branch list": branchListCommand,
	"git connect": gitConnectCommand,
	"git disconnect": gitDisconnectCommand,
	"service list": serviceListCommand,
	"service logs": serviceLogsCommand,
	"service create": serviceCreateCommand,
	"service show": serviceShowCommand,
	"service open": serviceOpenCommand,
	"service version list": serviceVersionListCommand,
	"service version show": serviceVersionShowCommand,
	"service version promote": serviceVersionPromoteCommand,
	"service version rollback": serviceVersionRollbackCommand,
	"service version start": serviceVersionStartCommand,
	"service version stop": serviceVersionStopCommand,
	"service version delete": serviceVersionDeleteCommand,
	"service delete": serviceDeleteCommand,
	"service domain add": serviceDomainAddCommand,
	"service domain show": serviceDomainShowCommand,
	"service domain delete": serviceDomainDeleteCommand,
	"service domain retry": serviceDomainRetryCommand,
	"service domain wait": serviceDomainWaitCommand,
	deploy: composerCommandFamily.commands.deploy,
	dev: composerCommandFamily.commands.dev,
	"contract emit": ormCommandFamily$1.commands["contract emit"],
	"contract infer": ormCommandFamily$1.commands["contract infer"],
	"db init": ormCommandFamily$1.commands["db init"],
	"db schema": ormCommandFamily$1.commands["db schema"],
	"db sign": ormCommandFamily$1.commands["db sign"],
	"db update": ormCommandFamily$1.commands["db update"],
	"db verify": ormCommandFamily$1.commands["db verify"],
	"db migrate": ormCommandFamily$1.commands["db migrate"],
	"contract format": ormCommandFamily$1.commands["contract format"],
	"contract print": ormCommandFamily$1.commands["contract print"],
	"orm init": ormCommandFamily$1.commands["orm init"],
	lsp: ormCommandFamily$1.commands.lsp,
	"migration check": ormCommandFamily$1.commands["migration check"],
	"migration graph": ormCommandFamily$1.commands["migration graph"],
	"migration list": ormCommandFamily$1.commands["migration list"],
	"migration log": ormCommandFamily$1.commands["migration log"],
	"migration new": ormCommandFamily$1.commands["migration new"],
	"migration plan": ormCommandFamily$1.commands["migration plan"],
	"migration show": ormCommandFamily$1.commands["migration show"],
	"migration status": ormCommandFamily$1.commands["migration status"],
	"migration ref delete": ormCommandFamily$1.commands["migration ref delete"],
	"migration ref list": ormCommandFamily$1.commands["migration ref list"],
	"migration ref set": ormCommandFamily$1.commands["migration ref set"],
	init: initCommand,
	"skills sync": skillsCommandFamily.commands.sync,
	"skills list": skillsCommandFamily.commands.list,
	feedback: feedbackCommand,
	...telemetry.commands
};
function buildCli() {
	return createCli({
		name: CLI_NAME,
		version: getCliVersion(),
		commandFamilies: [
			platformCommandFamily,
			composerCommandFamily,
			ormCommandFamily$1,
			skillsCommandFamily
		],
		groups: cliGroups,
		commands: mountedCommands,
		help: {
			tagline: "The Prisma Developer Platform, from your terminal",
			description: "Deploy your app with isolated infrastructure for every branch: a Project groups one product, and each of its Branches maps to a Git branch with its own services, databases, and buckets. The production branch serves live traffic; every other branch is a preview.",
			workflow: [
				{
					run: "auth login",
					brief: "Sign in to your Prisma workspace"
				},
				{
					run: "project create my-app",
					brief: "Create a Project, link this directory"
				},
				{
					run: "git connect",
					brief: "Connect GitHub; branches carrying the deploy workflow deploy on push"
				},
				{
					run: "deploy",
					brief: "Or build and deploy straight from this machine"
				}
			],
			examples: [
				"auth login",
				"project list",
				"deploy"
			],
			docsUrl: CLI_DOCS_URL
		},
		telemetry: { docsUrl: CLI_DOCS_URL }
	});
}
//#endregion
//#region ../cli-telemetry/dist/index.js
/**
* Fork the detached sender and hand it one payload over IPC. Returns
* synchronously — the child runs in the background and never blocks the
* parent. Every failure mode is swallowed; the parent's stdout/stderr is
* untouched in normal operation, the only escape valve being
* `PRISMA_DEBUG=1` which routes diagnostics to stderr.
*/
function runTelemetry(inputs) {
	try {
		const child = fork(inputs.senderPath, [], {
			detached: true,
			stdio: [
				"pipe",
				"ignore",
				"ignore",
				"ipc"
			]
		});
		child.on("error", () => {});
		child.send(inputs.payload, (err) => {
			if (err !== null && process.env.PRISMA_DEBUG === "1") process.stderr.write(`[cli-telemetry] parent send error: ${String(err)}\n`);
		});
		child.disconnect();
		child.unref();
		return { spawned: true };
	} catch (err) {
		if (process.env.PRISMA_DEBUG === "1") process.stderr.write(`[cli-telemetry] parent fork failed: ${String(err)}\n`);
		return {
			spawned: false,
			reason: "fork-failed"
		};
	}
}
//#endregion
//#region ../cli/src/auth/refresh.ts
const TRAILING_SLASH = /\/$/;
const CREDENTIAL_REFRESH_TIMEOUT_MS = 1e4;
/** The dumb HTTP adapter behind the engine's delegated-credential policy. */
function makeCredentialRefresher(authBaseUrl) {
	const endpoint = `${authBaseUrl.replace(TRAILING_SLASH, "")}/token`;
	return async ({ refreshToken, signal }) => {
		signal.throwIfAborted();
		const refreshSignal = AbortSignal.any([signal, AbortSignal.timeout(CREDENTIAL_REFRESH_TIMEOUT_MS)]);
		const response = await fetch(endpoint, {
			method: "POST",
			headers: { "content-type": "application/x-www-form-urlencoded" },
			body: new URLSearchParams({
				grant_type: "refresh_token",
				refresh_token: refreshToken,
				client_id: CLIENT_ID
			}),
			signal: refreshSignal
		});
		const body = await readBody(response);
		if (response.status >= 400 && response.status < 500 && body?.error === "invalid_grant") return { kind: "invalid" };
		if (!response.ok || typeof body?.access_token !== "string" || typeof body.refresh_token !== "string" || typeof body.expires_in !== "number" || !Number.isFinite(body.expires_in) || body.expires_in < 0) throw new Error(`OAuth token refresh failed (status ${String(response.status)})`);
		return {
			kind: "success",
			accessToken: body.access_token,
			refreshToken: body.refresh_token,
			expiresAt: new Date(Date.now() + body.expires_in * 1e3)
		};
	};
}
async function readBody(response) {
	try {
		const body = await response.json();
		return typeof body === "object" && body !== null ? body : null;
	} catch {
		return null;
	}
}
//#endregion
//#region ../cli/src/auth/session-metadata.ts
const METADATA_LOOKUP_TIMEOUT_MS = 3e3;
function fetchSessionMetadata(apiBaseUrl) {
	return async (credential) => {
		const { data } = await createManagementApiClient({
			baseUrl: apiBaseUrl,
			token: credential.token
		}).GET("/v1/me", { signal: AbortSignal.timeout(METADATA_LOOKUP_TIMEOUT_MS) });
		if (!data) return void 0;
		const { user, workspace } = data.data;
		return {
			workspaceName: workspace?.name ?? void 0,
			user: user ? {
				id: user.id,
				email: user.email,
				...user.name ? { name: user.name } : {}
			} : void 0
		};
	};
}
//#endregion
//#region ../cli/src/package-manager-runner.ts
/** How much of a manager's stderr the failure carries, per the seam's
*  contract. */
const STDERR_TAIL_BYTES = 64 * 1024;
/** A missing executable or a signal kill leaves the child with no exit
*  code of its own; the run still failed, and the engine reports it. */
const NO_EXIT_CODE = 1;
const LF = 10;
const CR = 13;
const LINE_BREAKS = [LF, CR];
/**
* The engine redacts a URL by its scheme, and the bound cuts at an
* arbitrary byte: a cut inside `https://` leaves `user:secret@host`,
* which no pattern recognises. So the tail starts after the first line
* break in what was kept. A window with no line break at all is one
* truncated line whose start cannot be trusted, and is dropped.
*/
function fromLineStart(window) {
	const breaks = LINE_BREAKS.map((byte) => window.indexOf(byte)).filter((at) => at !== -1);
	if (breaks.length === 0) return Buffer.alloc(0);
	const at = Math.min(...breaks);
	const span = window[at] === CR && window[at + 1] === LF ? 2 : 1;
	return window.subarray(at + span);
}
/** Keeps the last `limit` bytes by dropping whole chunks off the front,
*  so a manager writing megabytes in small pieces does not recopy the
*  window once per piece. */
function boundedTail(limit) {
	const chunks = [];
	let written = 0;
	let kept = 0;
	return {
		push(chunk) {
			chunks.push(chunk);
			written += chunk.length;
			kept += chunk.length;
			let first = chunks[0];
			while (first !== void 0 && kept - first.length >= limit) {
				chunks.shift();
				kept -= first.length;
				first = chunks[0];
			}
		},
		/** Every byte the child wrote, whether or not it was kept. */
		get bytes() {
			return written;
		},
		text() {
			const all = Buffer.concat(chunks);
			if (written <= limit) return all.toString("utf8");
			return fromLineStart(all.subarray(all.length - limit)).toString("utf8");
		}
	};
}
/** Decodes across chunk boundaries, so a multi-byte character split by
*  the pipe is not delivered as two replacement characters. Returns the
*  final decode: a child that died part-way through a character leaves
*  bytes the decoder is still holding, and without it they are lost. */
function forward(source, emit, keep) {
	const decoder = new TextDecoder();
	const emitDecoded = (text) => {
		if (text !== "") emit(text);
	};
	source.on("data", (chunk) => {
		keep?.(chunk);
		emitDecoded(decoder.decode(chunk, { stream: true }));
	});
	return () => {
		emitDecoded(decoder.decode());
	};
}
/**
* Spawns the package manager the engine composed. Its output streams
* out as the child writes it; its stderr also comes back bounded for
* the caller's failure predicate. Every failure — a non-zero exit, an
* executable that is not installed, an abort — resolves.
*/
const runPackageManager = async ({ file, args, cwd, signal, onOutput }) => {
	const tail = boundedTail(STDERR_TAIL_BYTES);
	const subprocess = execa(file, [...args], {
		cwd,
		cancelSignal: signal,
		stdin: "ignore",
		buffer: false,
		reject: false
	});
	const flushStdout = forward(subprocess.stdout, (text) => onOutput("data", text));
	const flushStderr = forward(subprocess.stderr, (text) => onOutput("diagnostic", text), tail.push);
	const result = await subprocess;
	flushStdout();
	flushStderr();
	return {
		exitCode: result.exitCode ?? NO_EXIT_CODE,
		stderr: tail.bytes === 0 ? result.shortMessage ?? "" : tail.text()
	};
};
//#endregion
//#region ../cli/src/spawn.ts
/** How long after the child exits the relay keeps reading its pipes. A
*  grandchild that inherited them can hold EOF back forever; settlement
*  must not wait on it, so the pipes are destroyed after this grace. */
const POST_EXIT_DRAIN_GRACE_MS = 5e3;
/**
* The engine's spawn seam, adapted through cross-spawn. Human mode
* inherits stdio; structured mode pipes both child output streams to
* diagnostics. Neither mode detaches or opens a new console, so the child
* stays in this process's group (POSIX) or console (Windows).
*
* The child's own status settles the run: `ended` resolves from the
* process `exit` event, waits for the diagnostic relay only up to the
* drain grace, and never rejects for a relay failure — rejection is
* reserved for a child that could not be launched at all.
*/
function makeSpawnChild(diagnostics, options) {
	const drainGraceMs = options?.drainGraceMs ?? POST_EXIT_DRAIN_GRACE_MS;
	return (request) => {
		const structured = request.output === "diagnostic";
		const child = spawn$1(request.command, [...request.args], {
			cwd: request.cwd,
			env: request.env,
			stdio: structured ? [
				"inherit",
				"pipe",
				"pipe"
			] : "inherit"
		});
		const processEnded = new Promise((resolve, reject) => {
			child.on("error", reject);
			child.on("exit", (exitCode, signal) => {
				resolve({
					exitCode,
					signal
				});
			});
		});
		if (!structured) return {
			ended: processEnded,
			kill: (signal) => {
				child.kill(signal);
			}
		};
		const forwarding = forwardStructuredOutput(child.stdout, child.stderr, diagnostics);
		return {
			ended: processEnded.then(async (result) => {
				const drainDeadline = setTimeout(() => {
					child.stdout?.destroy();
					child.stderr?.destroy();
				}, drainGraceMs);
				await forwarding;
				clearTimeout(drainDeadline);
				return result;
			}),
			kill: (signal) => {
				child.kill(signal);
			}
		};
	};
}
/** Best-effort relay: a forwarding failure never rejects, so the child's
*  real status still settles the run when the diagnostic sink dies. */
function forwardStructuredOutput(stdout, stderr, diagnostics) {
	const sources = [stdout, stderr].filter((source) => source !== null);
	return Promise.all(sources.map((source) => forwardOutput(source, diagnostics))).then(() => void 0, () => void 0);
}
/** Decode each child stream continuously and stop reading while the
*  diagnostic destination applies backpressure. A destination that
*  errors or closes instead of draining fails the relay rather than
*  stalling it. */
function forwardOutput(source, diagnostics) {
	let pendingDone;
	let pendingDrain;
	let failure;
	const fail = (cause) => {
		failure ??= cause;
		const done = pendingDone;
		pendingDone = void 0;
		done?.(cause);
	};
	const onSinkError = (cause) => {
		fail(toError(cause));
	};
	const onSinkClose = () => {
		fail(/* @__PURE__ */ new Error("the diagnostic stream closed during child output"));
	};
	diagnostics.once?.("error", onSinkError);
	diagnostics.once?.("close", onSinkClose);
	const destination = new Writable({
		decodeStrings: false,
		write: (text, _encoding, done) => {
			if (failure !== void 0) {
				done(failure);
				return;
			}
			try {
				if (diagnostics.write(text) === false && diagnostics.once !== void 0) {
					pendingDone = done;
					const onDrain = () => {
						pendingDrain = void 0;
						if (pendingDone !== done) return;
						pendingDone = void 0;
						done();
					};
					pendingDrain = onDrain;
					diagnostics.once("drain", onDrain);
				} else done();
			} catch (cause) {
				done(toError(cause));
			}
		}
	});
	return pipeline(source.setEncoding("utf8"), destination).finally(() => {
		diagnostics.off?.("error", onSinkError);
		diagnostics.off?.("close", onSinkClose);
		if (pendingDrain !== void 0) diagnostics.off?.("drain", pendingDrain);
	});
}
function toError(cause) {
	return cause instanceof Error ? cause : new Error(String(cause));
}
//#endregion
//#region ../cli/src/runtime.ts
/** Dumb wiring: forwards process signals to the engine's subscribers.
*  The signal policy (first aborts, second force-exits) is the engine's. */
function makeOnSignal(proc) {
	return (cb) => {
		const onInt = () => cb("SIGINT");
		const onTerm = () => cb("SIGTERM");
		proc.on("SIGINT", onInt);
		proc.on("SIGTERM", onTerm);
		return () => {
			proc.off("SIGINT", onInt);
			proc.off("SIGTERM", onTerm);
		};
	};
}
/** Bun and Deno both announce themselves in process.versions; nothing
*  else does, so an absent marker means Node. */
function describeHost(proc) {
	const name = ["bun", "deno"].find((candidate) => proc.versions[candidate]) ?? "node";
	return {
		runtime: {
			name,
			version: proc.versions[name] ?? proc.version
		},
		platform: proc.platform,
		arch: proc.arch
	};
}
/**
* Path to the compiled sender entry. In the workspace (dev runs and the
* monorepo dist) the package specifier resolves to
* `packages/cli-telemetry/dist/sender.js`; in the published cli the
* telemetry package is bundled away, so the fallback resolves the copy
* tsdown emits next to the CLI entry (`dist/sender.js`).
*/
function resolveSenderPath() {
	try {
		return fileURLToPath(import.meta.resolve("@repo/cli-telemetry/sender"));
	} catch {
		return fileURLToPath(new URL("./sender.js", import.meta.url));
	}
}
/** PRISMA_COMPUTE_AUTH_FILE still names the credentials file, but
*  PRISMA_AUTH_FILE is the supported name. Warned once per process. */
function warnOnDeprecatedStateFileEnvVar(proc) {
	if (!resolveStateFilePath(proc.env).fromDeprecatedEnvVar) return;
	proc.stderr.write(`${DEPRECATED_STATE_FILE_ENV_VAR} is deprecated; use ${STATE_FILE_ENV_VAR} instead.\n`);
}
/** Whether fd 1 and fd 2 are the same open device. Distinguishes one
*  terminal (mirror suppressed) from a harness that allocated separate
*  PTYs for the two streams (mirror kept). Undefined when the fds
*  cannot be inspected — the engine then assumes one terminal. */
function outputStreamsShareDevice() {
	try {
		const out = fstatSync(1);
		const err = fstatSync(2);
		return out.dev === err.dev && out.ino === err.ino && out.rdev === err.rdev;
	} catch {
		return;
	}
}
/** One chain discovery and transpile per config path per process: a
*  command's needs check and the skills staleness check both load the
*  config, and the process is single-shot, so nothing invalidates. The
*  promise is cached, not the value, so concurrent calls coalesce. */
function memoizedConfigLoader(proc) {
	const loads = /* @__PURE__ */ new Map();
	return (configPath) => {
		let load = loads.get(configPath);
		if (load === void 0) {
			load = loadConfig(proc.cwd(), configPath, getCliVersion());
			loads.set(configPath, load);
		}
		return load;
	};
}
/**
* The engine returns the iterator when it has finished prompting. Node's
* stdin iterator cannot honour that while it is awaiting a keystroke,
* and a terminal being read keeps the process alive, so the adapter
* unrefs stdin itself and settles at once. The underlying return is
* left to complete whenever it can, and the next run refs stdin again
* before it reads.
*/
function makeStdin(proc) {
	return {
		setRawMode: proc.stdin.isTTY === true && proc.stdin.setRawMode !== void 0 ? (enabled) => {
			proc.stdin.setRawMode?.(enabled);
		} : void 0,
		[Symbol.asyncIterator]: () => {
			proc.stdin.ref?.();
			const chunks = proc.stdin[Symbol.asyncIterator]();
			return {
				next: () => chunks.next(),
				return: async () => {
					proc.stdin.unref?.();
					chunks.return?.()?.catch(() => {});
					return {
						done: true,
						value: void 0
					};
				}
			};
		}
	};
}
async function assembleRuntime(proc) {
	const stdin = makeStdin(proc);
	warnOnDeprecatedStateFileEnvVar(proc);
	const apiBaseUrl = getApiBaseUrl(proc.env);
	const authBaseUrl = getAuthBaseUrl(proc.env);
	return {
		stdout: { write: (text) => {
			proc.stdout.write(text);
		} },
		stderr: {
			write: (text) => {
				proc.stderr.write(text);
			},
			get columns() {
				return proc.stderr.columns;
			}
		},
		stdin,
		cwd: proc.cwd(),
		env: proc.env,
		isTty: {
			stdin: proc.stdin.isTTY === true,
			stdout: proc.stdout.isTTY === true,
			stderr: proc.stderr.isTTY === true
		},
		outputStreamsShareDevice: outputStreamsShareDevice(),
		exit: (code) => proc.exit(code),
		onSignal: makeOnSignal(proc),
		loadConfig: memoizedConfigLoader(proc),
		credentialManager: new FileCredentialManager({
			env: proc.env,
			fetchSessionMetadata: fetchSessionMetadata(apiBaseUrl),
			refreshCredential: makeCredentialRefresher(authBaseUrl)
		}),
		managementApiClientConfig: {
			clientId: CLIENT_ID,
			redirectUri: DEFAULT_REDIRECT_URI,
			apiBaseUrl,
			authBaseUrl
		},
		spawn: makeSpawnChild(proc.stderr),
		/** The engine has already decided and composed; the bin only forks
		*  the detached sender and hands the payload over. Every failure is
		*  swallowed inside runTelemetry. */
		spawnTelemetry: (payload) => {
			runTelemetry({
				payload,
				senderPath: resolveSenderPath()
			});
		},
		openUrl: async (url) => {
			await open(url);
		},
		managementApi: { baseUrl: apiBaseUrl },
		runPackageManager,
		host: describeHost(proc)
	};
}
async function maybeWriteSkillsStaleNotice(runtime) {
	if (isSuppressedByInvocation(runtime)) return;
	try {
		if (await readSkillsCheckDisabled(runtime.cwd)) return;
		const status = await readSkillsStatus(runtime.cwd, {
			orphans: false,
			checkDisabled: false
		});
		if (status.upToDate) return;
		const config = await readProjectSkillsConfig(runtime.loadConfig, configPathFromArgv(runtime.argv));
		if (config !== null && !config.check) return;
		const agents = config?.agents ?? DEFAULT_AGENTS;
		if (agents.length === 0) return;
		const dirs = agentSkillDirs(agents);
		if (isFormat(flagTokens(runtime.argv), "markdown")) {
			const notice = renderStaleNoticeMarkdown(status, dirs);
			if (notice !== null) runtime.stdout.write(notice);
			return;
		}
		const notice = renderStaleNotice(status, dirs);
		if (notice !== null) runtime.stderr.write(notice);
	} catch {
		return;
	}
}
/** The first skill with a stale or never-synced copy in one of the
*  configured directories — what the check names in its one line. */
function firstOutdatedSkillIn(status, dirs) {
	return status.skills.find((skill) => skill.targets.some((target) => dirs.includes(target.dir) && (target.state === "stale" || target.state === "absent"))) ?? null;
}
/** The mismatch in words, without the stream's framing around it. */
function staleNoticeSentence(status, dirs) {
	const outdated = firstOutdatedSkillIn(status, dirs);
	if (outdated === null) return null;
	const synced = outdated.targets.find((target) => dirs.includes(target.dir) && target.state === "stale")?.syncedVersion;
	return `Prisma agent skills are out of date (installed ${outdated.library} ${outdated.version}, synced ${synced ?? "none"})`;
}
function renderStaleNotice(status, dirs) {
	const sentence = staleNoticeSentence(status, dirs);
	return sentence === null ? null : `${sentence}. Run: ${getCliName()} skills sync\n`;
}
/** The same notice as a trailing section of the Markdown document, in
*  the heading-then-bullet shape the engine renders `### Diagnostics`
*  in. The leading blank line separates it from the command's output. */
function renderStaleNoticeMarkdown(status, dirs) {
	const sentence = staleNoticeSentence(status, dirs);
	return sentence === null ? null : `\n### Notice\n${sentence}.\n- Sync agent skills: \`${getCliName()} skills sync\`\n`;
}
/** The shared flags that take a separate value, so the word after them
*  is that value rather than the command being invoked. */
const FLAGS_TAKING_A_VALUE = new Set([
	"--format",
	"--log-level",
	"--config",
	"--confirm"
]);
/** The first word of the invocation — the group, or the command when it
*  is mounted top-level — skipping the shared flags that may precede it. */
function invokedGroup(argv) {
	for (let index = 0; index < argv.length; index += 1) {
		const token = argv[index];
		if (!token.startsWith("-")) return token;
		if (FLAGS_TAKING_A_VALUE.has(token)) index += 1;
	}
}
/** Tokens before a bare `--`; everything after it is positional data,
*  never a flag. */
function flagTokens(argv) {
	const end = argv.indexOf("--");
	return end === -1 ? argv : argv.slice(0, end);
}
/** The off switches that cost nothing to read. */
function isSuppressedByInvocation(runtime) {
	const env = runtime.env;
	if (env["PRISMA_SKILLS_CHECK"] === "0") return true;
	if (detectCI(env)) return true;
	const argv = flagTokens(runtime.argv);
	const group = invokedGroup(argv);
	if (group === "skills" || group === "init") return true;
	if (argv.includes("--json") || argv.includes("--quiet") || argv.includes("-q")) return true;
	if (argv.includes("--version")) return true;
	return isFormat(argv, "json");
}
function isFormat(tokens, format) {
	return tokens.some((token, index) => token === `--format=${format}` || token === "--format" && tokens[index + 1] === format);
}
/** The file an explicit --config names, so the check anchors the same
*  chain the command did. Discovery otherwise anchors at cwd. */
function configPathFromArgv(argv) {
	const tokens = flagTokens(argv);
	for (let index = 0; index < tokens.length; index += 1) {
		const token = tokens[index];
		if (token === "--config") return tokens[index + 1];
		if (token.startsWith("--config=")) return token.slice(9);
	}
}
//#endregion
//#region ../cli/src/update-check.ts
const UPDATE_CHECK_FILE_NAME = "update-check.json";
const FALLBACK_INSTALL_DOCS_URL = CLI_DOCS_URL;
const NOTIFICATION_INTERVAL_MS = 1440 * 60 * 1e3;
const REGISTRY_URL = "https://registry.npmjs.org/@prisma%2fcli";
const REGISTRY_TIMEOUT_MS = 3e3;
var UpdateCheckStore = class {
	filePath;
	constructor(cacheDir) {
		this.filePath = path.join(cacheDir, UPDATE_CHECK_FILE_NAME);
	}
	async read() {
		try {
			return JSON.parse(await readFile(this.filePath, "utf8"));
		} catch (error) {
			if (isUnreadableCacheError(error)) return null;
			throw error;
		}
	}
	async write(state) {
		const dir = path.dirname(this.filePath);
		const tempPath = path.join(dir, `${UPDATE_CHECK_FILE_NAME}.${process.pid}.${randomUUID()}.tmp`);
		await mkdir(dir, { recursive: true });
		await writeFile(tempPath, `${JSON.stringify(state, null, 2)}\n`, "utf8");
		await rename(tempPath, this.filePath);
	}
};
async function maybeWriteCachedUpdateNotification(runtime) {
	if (!canRunUpdateCheck(runtime)) return;
	try {
		const cacheDir = resolveUpdateCheckCacheDir(runtime);
		const store = new UpdateCheckStore(cacheDir);
		const state = await store.read();
		const latestVersion = state?.latestVersion;
		if (latestVersion && isInstalledVersionStale(getCliVersion(), latestVersion) && shouldNotify(state)) {
			runtime.stderr.write(renderUpdateNotification(latestVersion, selectUpdateInstruction(runtime.env)));
			await store.write({
				...state,
				packageName: "@prisma/cli",
				installedVersion: getCliVersion(),
				notifiedAt: (/* @__PURE__ */ new Date()).toISOString()
			});
		}
		await scheduleRemoteDiscovery(runtime, store, state, cacheDir);
	} catch {
		return;
	}
}
async function runUpdateDiscovery(options) {
	try {
		const latestVersion = await fetchLatestVersion(options.registryUrl ?? REGISTRY_URL, options.fetchImpl ?? fetch);
		if (!latestVersion) return;
		const store = new UpdateCheckStore(options.cacheDir);
		const previousState = await store.read();
		await store.write({
			...previousState,
			packageName: "@prisma/cli",
			installedVersion: options.installedVersion,
			latestVersion,
			checkedAt: (options.now ?? /* @__PURE__ */ new Date()).toISOString()
		});
	} catch {
		return;
	}
}
function isUnreadableCacheError(error) {
	const code = error.code;
	return code === "ENOENT" || code === "EACCES" || code === "EPERM" || error instanceof SyntaxError;
}
async function runUpdateDiscoveryWorker(env = process.env) {
	const cacheDir = env.PRISMA_CLI_UPDATE_CHECK_DIR;
	const installedVersion = env.PRISMA_CLI_UPDATE_CHECK_INSTALLED_VERSION;
	if (!cacheDir || !installedVersion) return;
	await runUpdateDiscovery({
		cacheDir,
		installedVersion,
		registryUrl: env.PRISMA_CLI_UPDATE_CHECK_REGISTRY_URL
	});
}
function canRunUpdateCheck(runtime) {
	if (runtime.env.NO_UPDATE_NOTIFIER !== void 0) return false;
	if (isTestRuntime(runtime.env) && runtime.env.PRISMA_CLI_TEST_ENABLE_UPDATE_CHECK !== "1") return false;
	if (runtime.env.CI || runtime.env.GITHUB_ACTIONS) return false;
	if (!runtime.stderr.isTTY) return false;
	if (runtime.argv.includes("--json") || runtime.argv.includes("--quiet") || runtime.argv.includes("-q")) return false;
	if (runtime.argv.includes("--version")) return false;
	return true;
}
function shouldNotify(state) {
	return !state.notifiedAt || isAtLeastIntervalAgo(state.notifiedAt);
}
async function scheduleRemoteDiscovery(runtime, store, state, cacheDir) {
	if (state?.checkedAt && !isAtLeastIntervalAgo(state.checkedAt)) return;
	const checkedAt = (/* @__PURE__ */ new Date()).toISOString();
	await store.write({
		...state,
		packageName: "@prisma/cli",
		installedVersion: getCliVersion(),
		checkedAt
	});
	if (isTestRuntime(runtime.env)) return;
	const entrypoint = process.argv[1];
	if (!entrypoint) return;
	spawn(process.execPath, [entrypoint], {
		detached: true,
		stdio: "ignore",
		env: {
			...process.env,
			PRISMA_CLI_RUN_UPDATE_CHECK_WORKER: "1",
			PRISMA_CLI_UPDATE_CHECK_DIR: cacheDir,
			PRISMA_CLI_UPDATE_CHECK_INSTALLED_VERSION: getCliVersion(),
			PRISMA_CLI_UPDATE_CHECK_REGISTRY_URL: runtime.env.PRISMA_CLI_UPDATE_CHECK_REGISTRY_URL ?? REGISTRY_URL
		}
	}).unref();
}
function selectUpdateInstruction(env, processArgv = process.argv) {
	const entrypoint = (processArgv[1] ?? "").replace(/\\/g, "/").toLowerCase();
	const userAgent = env.npm_config_user_agent?.toLowerCase() ?? "";
	if (isEphemeralInvocation(entrypoint, env.npm_lifecycle_event?.toLowerCase() ?? "")) return docsInstruction();
	if (entrypoint.includes("/node_modules/.bin/")) {
		if (userAgent.startsWith("pnpm")) return commandInstruction("pnpm add -D @prisma/cli@latest");
		if (userAgent.startsWith("bun")) return commandInstruction("bun add -d @prisma/cli@latest");
		if (userAgent.startsWith("npm")) return commandInstruction("npm install --save-dev @prisma/cli@latest");
	}
	if (env.npm_config_global === "true" || isLikelyGlobalNpmEntrypoint(entrypoint)) return commandInstruction("npm install --global @prisma/cli@latest");
	return docsInstruction();
}
function renderUpdateNotification(latestVersion, instruction) {
	return [
		`Update available: ${getCliName()} ${getCliVersion()} -> ${latestVersion}`,
		renderUpdateInstruction(instruction),
		""
	].join("\n");
}
function renderUpdateInstruction(instruction) {
	if (instruction.type === "command") return `Run ${instruction.value} to update.`;
	return `See ${instruction.value} for update instructions.`;
}
function isEphemeralInvocation(entrypoint, lifecycle) {
	return lifecycle === "npx" || lifecycle === "pnpx" || entrypoint.includes("/_npx/") || entrypoint.includes("/.bun/");
}
function isLikelyGlobalNpmEntrypoint(entrypoint) {
	return /\/npm\/prisma-cli(\.cmd|\.exe)?$/.test(entrypoint) || /\/npm-global\/bin\/prisma-cli$/.test(entrypoint);
}
function commandInstruction(value) {
	return {
		type: "command",
		value
	};
}
function docsInstruction() {
	return {
		type: "docs",
		value: FALLBACK_INSTALL_DOCS_URL
	};
}
function resolveUpdateCheckCacheDir(runtime) {
	const configured = runtime.env.PRISMA_CLI_UPDATE_CHECK_DIR;
	if (configured?.trim()) return path.resolve(configured);
	if (process.platform === "darwin") return path.join(os.homedir(), "Library", "Caches", "prisma-cli");
	if (process.platform === "win32") {
		const localAppData = runtime.env.LOCALAPPDATA ?? path.join(os.homedir(), "AppData", "Local");
		return path.join(localAppData, "prisma-cli", "cache");
	}
	const xdgCacheHome = runtime.env.XDG_CACHE_HOME ?? path.join(os.homedir(), ".cache");
	return path.join(xdgCacheHome, "prisma-cli");
}
function isTestRuntime(env) {
	return env.VITEST !== void 0 || env.NODE_ENV === "test";
}
function isAtLeastIntervalAgo(value) {
	const timestamp = Date.parse(value);
	return Number.isNaN(timestamp) || Date.now() - timestamp >= NOTIFICATION_INTERVAL_MS;
}
function isInstalledVersionStale(installedVersion, latestVersion) {
	const order = compareVersionStrings(installedVersion, latestVersion);
	return order !== null && order < 0;
}
async function fetchLatestVersion(registryUrl, fetchImpl) {
	const controller = new AbortController();
	const timeout = setTimeout(() => controller.abort(), REGISTRY_TIMEOUT_MS);
	try {
		const response = await fetchImpl(registryUrl, {
			signal: controller.signal,
			headers: { accept: "application/json" }
		});
		if (!response.ok) return null;
		const latest = (await response.json())["dist-tags"]?.latest;
		return typeof latest === "string" ? latest : null;
	} finally {
		clearTimeout(timeout);
	}
}
//#endregion
//#region ../cli/src/main.ts
/** The bin body: build, run, return the exit code. Signal policy lives
*  in the engine; the bin only forwards signals and provides
*  process.exit. A construction error prints one line to stderr and
*  exits 1. Telemetry is the engine's: it decides and composes at
*  command start, and the runtime's spawnTelemetry seam forks the
*  sender. */
async function main(proc, buildCliForRun = buildCli) {
	let cli;
	try {
		cli = buildCliForRun();
	} catch (cause) {
		proc.stderr.write(`${cause instanceof Error ? cause.message : String(cause)}\n`);
		return 1;
	}
	await maybeWriteCachedUpdateNotification({
		env: proc.env,
		argv: proc.argv.slice(2),
		stderr: proc.stderr
	});
	const runtime = await assembleRuntime(proc);
	const exitCode = await cli.run(proc.argv.slice(2), runtime);
	await maybeWriteSkillsStaleNotice({
		env: proc.env,
		argv: proc.argv.slice(2),
		cwd: proc.cwd(),
		stdout: proc.stdout,
		stderr: proc.stderr,
		loadConfig: runtime.loadConfig
	});
	return exitCode;
}
//#endregion
//#region ../cli/src/bin.ts
if (process$1.env.PRISMA_CLI_RUN_UPDATE_CHECK_WORKER === "1") {
	await runUpdateDiscoveryWorker();
	process$1.exitCode = 0;
} else process$1.exitCode = await main(process$1);
//#endregion
export {};