@fluid-app/fluid-cli-mist 0.1.93 → 0.1.94

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["BASE","STATE_COLOR","BASE","PER_PAGE","MAX_PAGES","BASE","BASE","path","isRecord","defaultSleep","KIND_LABEL","ENV_ASSIGNMENT","POLL_MS","sleep"],"sources":["../src/api/client.ts","../src/api/id-resolver.ts","../src/api/mists.ts","../src/api/branch_environments.ts","../src/lib/hostable.ts","../src/lib/git.ts","../src/lib/slug-resolver.ts","../src/lib/staging-revision.ts","../src/lib/staging.ts","../src/lib/error-handler.ts","../src/lib/output.ts","../src/commands/list.ts","../src/api/integration_points.ts","../src/commands/show.ts","../src/lib/ora-reporter.ts","../src/lib/step-reporter.ts","../../../mist/core/src/lifecycle-urls.ts","../src/api/droplets.ts","../src/api/droplet_installations.ts","../src/api/drop_zones.ts","../src/api/mobile_widgets.ts","../src/api/env_vars.ts","../src/types.ts","../../../mist/core/src/droplet-lifecycle-repair.ts","../../../mist/core/src/lifecycle-types.ts","../../../mist/core/src/recovery-classification.ts","../../../mist/core/src/droplet-replacement.ts","../../../mist/core/src/operation-polling.ts","../src/api/droplet_replacements.ts","../src/lib/reinstall-plan-store.ts","../src/lib/droplet-repair.ts","../src/lib/droplet-egress.ts","../src/lib/surface-provision.ts","../src/lib/surface-flags.ts","../src/lib/surface-summary.ts","../src/commands/create.ts","../src/commands/attach.ts","../src/commands/attach-droplet.ts","../src/commands/update.ts","../src/commands/delete.ts","../src/commands/restore.ts","../src/api/git_credential.ts","../src/lib/env-placeholders.ts","../src/commands/clone.ts","../src/api/deployments.ts","../src/api/me.ts","../src/lib/rollback-subject.ts","../src/commands/push.ts","../src/commands/pull.ts","../src/commands/open.ts","../src/commands/deployments.ts","../src/api/logs.ts","../src/commands/logs.ts","../src/lib/encrypted-env.ts","../src/commands/env/list.ts","../../../mist/core/src/secret-generator.ts","../src/commands/env/set.ts","../src/commands/env/unset.ts","../src/commands/env/pull.ts","../src/commands/env/index.ts","../src/commands/droplet/egress-proxy.ts","../src/commands/droplet/reinstall.ts","../src/commands/droplet/repair.ts","../src/commands/droplet/index.ts","../src/commands/staging/status.ts","../src/lib/staging-request-store.ts","../src/commands/staging/wait.ts","../src/commands/staging/request.ts","../src/commands/staging/teardown.ts","../src/commands/staging/index.ts","../src/index.ts"],"sourcesContent":["/**\n * Generic fetch wrapper for the Mist Rails API.\n *\n * Spec: `features/mist/cli-spec.md` §4.\n * - Base URL: `https://api.fluid.app` (overridable via FLUID_API_BASE)\n * - Auth: `Authorization: Bearer <token>` from the standard fluid-cli token\n * store; the host CLI's PluginContext provides `getAuthToken()`.\n * - Errors: maps API responses to a typed `MistApiError` that the\n * error-handler turns into the spec's exit codes (1/2/3/4).\n */\n\nimport { getAuthToken } from \"@fluid-app/fluid-cli\";\n\nconst DEFAULT_BASE = \"https://api.fluid.app\";\n\nexport interface MistApiErrorBody {\n error?: {\n message?: string;\n details?: Record<string, unknown>;\n };\n /** Rails' legacy envelope names refused fields here, e.g.\n * `{ errors: { egress_proxy_requested: [\"must be a company admin\"] } }`. */\n errors?: Record<string, unknown>;\n status?: number;\n}\n\nexport class MistApiError extends Error {\n /** HTTP status, or 0 for network-level failures. */\n readonly status: number;\n readonly details: Record<string, unknown> | undefined;\n /** The response's top-level `errors` object (Rails' legacy envelope), when\n * it has one: the fields the API refused. */\n readonly fieldErrors: Record<string, unknown> | undefined;\n /** Best-guess exit code per the spec: 2 for 4xx, 3 for 5xx/vendor, 4 for net. */\n readonly exitCode: 2 | 3 | 4;\n\n constructor(\n message: string,\n status: number,\n details?: Record<string, unknown>,\n fieldErrors?: Record<string, unknown>,\n ) {\n super(message);\n this.name = \"MistApiError\";\n this.status = status;\n this.details = details;\n this.fieldErrors = fieldErrors;\n if (status === 0) this.exitCode = 4;\n else if (status >= 500) this.exitCode = 3;\n else this.exitCode = 2;\n }\n}\n\nfunction apiBase(): string {\n return process.env[\"FLUID_API_BASE\"] ?? DEFAULT_BASE;\n}\n\nexport interface MistClient {\n get<T>(path: string, query?: Record<string, unknown>): Promise<T>;\n /** `headers` carries request metadata such as an idempotency key. */\n post<T>(\n path: string,\n body?: unknown,\n headers?: Record<string, string>,\n ): Promise<T>;\n patch<T>(path: string, body?: unknown): Promise<T>;\n delete<T>(path: string, body?: unknown): Promise<T>;\n /** Raw `fetch` for streaming endpoints (logs polling). */\n request(path: string, init?: RequestInit): Promise<Response>;\n}\n\n/** Build a typed Mist API client. Throws if the user isn't logged in. */\nexport function createMistClient(): MistClient {\n const token = getAuthToken();\n if (!token) {\n throw new MistApiError(\n \"No active Fluid session — run `fluid login` first.\",\n 401,\n );\n }\n const base = apiBase();\n\n function buildUrl(path: string, query?: Record<string, unknown>): string {\n const url = new URL(path.startsWith(\"/\") ? path : `/${path}`, base);\n if (query) {\n for (const [k, v] of Object.entries(query)) {\n if (v == null) continue;\n url.searchParams.set(k, String(v));\n }\n }\n return url.toString();\n }\n\n async function call<T>(\n method: string,\n path: string,\n opts?: {\n body?: unknown;\n query?: Record<string, unknown>;\n headers?: Record<string, string>;\n },\n ): Promise<T> {\n const url = buildUrl(path, opts?.query);\n let response: Response;\n try {\n response = await fetch(url, {\n method,\n headers: {\n Authorization: `Bearer ${token}`,\n Accept: \"application/json\",\n ...(opts?.body !== undefined\n ? { \"Content-Type\": \"application/json\" }\n : {}),\n ...opts?.headers,\n },\n ...(opts?.body !== undefined\n ? { body: JSON.stringify(opts.body) }\n : {}),\n });\n } catch (err) {\n throw new MistApiError(\n `Network error: ${err instanceof Error ? err.message : String(err)}`,\n 0,\n );\n }\n\n // 204 No Content\n if (response.status === 204) return undefined as T;\n\n const text = await response.text();\n let parsed: unknown;\n try {\n parsed = text.length > 0 ? JSON.parse(text) : {};\n } catch {\n // Non-JSON body — treat as opaque error if non-2xx.\n if (!response.ok) {\n throw new MistApiError(\n `HTTP ${response.status}: ${text.slice(0, 200)}`,\n response.status,\n );\n }\n return undefined as T;\n }\n\n if (!response.ok) {\n const body = parsed as MistApiErrorBody;\n const message = body.error?.message ?? `HTTP ${response.status}`;\n const fieldErrors =\n body.errors && typeof body.errors === \"object\"\n ? body.errors\n : undefined;\n throw new MistApiError(\n message,\n response.status,\n body.error?.details,\n fieldErrors,\n );\n }\n return parsed as T;\n }\n\n return {\n get: (path, query) => call(\"GET\", path, { query }),\n post: (path, body, headers) => call(\"POST\", path, { body, headers }),\n patch: (path, body) => call(\"PATCH\", path, { body }),\n delete: (path, body) =>\n call(\"DELETE\", path, body !== undefined ? { body } : undefined),\n async request(path, init) {\n const url = buildUrl(path);\n return fetch(url, {\n ...init,\n headers: {\n Authorization: `Bearer ${token}`,\n Accept: \"application/json\",\n ...(init?.headers ?? {}),\n },\n });\n },\n };\n}\n","/**\n * Slug → UUID id resolver.\n *\n * Despite the spec describing `/api/v202604/mists/:id` as accepting the\n * 6-char slug, the Rails route only accepts the UUID `id`. Every command\n * the user invokes (`show`, `delete`, `restore`, `clone`, `push`, `env *`,\n * `deployments`, `logs`) takes a slug and would otherwise 404. We hide\n * the lookup here so the API surface (`showMist`, `deleteMist`, etc.)\n * keeps accepting `slug` strings — they internally call this resolver\n * once and use the returned UUID.\n *\n * The cache is process-local; one terminal invocation hits the list\n * endpoint at most once.\n */\n\nimport type { MistClient } from \"./client.js\";\nimport type { MistListResponse } from \"../types.js\";\n\n// We deliberately don't import listMists from ./mists here — that\n// would create a cycle (mists.ts imports resolveMistId from this\n// file). Instead, hit the same endpoint directly. Kept private to\n// this module since it's just the cycle-breaker.\nconst MISTS_BASE = \"/api/v202604/mists\";\nfunction listMistsForResolve(\n client: MistClient,\n params: { limit?: number } = {},\n): Promise<MistListResponse> {\n return client.get<MistListResponse>(\n MISTS_BASE,\n params as Record<string, unknown>,\n );\n}\n\nconst cache = new Map<string, string>();\n\nconst UUID_REGEX =\n /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n/** Returns true if the input already looks like a UUID we can use as-is. */\nfunction isUuid(s: string): boolean {\n return UUID_REGEX.test(s.trim());\n}\n\n/**\n * Resolve a user-supplied slug-or-id to the API's canonical UUID id.\n * No-op when the input already looks like a UUID.\n */\nexport async function resolveMistId(\n client: MistClient,\n slugOrId: string,\n): Promise<string> {\n const key = slugOrId.trim();\n if (!key) throw new Error(\"Empty mist identifier.\");\n if (isUuid(key)) return key;\n const hit = cache.get(key);\n if (hit) return hit;\n // The slug isn't a UUID — look it up via the list endpoint. limit=100\n // covers the vast majority of users; if the user has more than 100\n // mists and the one they want isn't on page 1, we'd need to paginate.\n // For now we keep it cheap and accept that pagination is a follow-up.\n const response = await listMistsForResolve(client, { limit: 100 });\n for (const m of response.mists ?? []) {\n if (m.slug) cache.set(m.slug, m.id);\n }\n const found = cache.get(key);\n if (!found) {\n throw new Error(\n `No mist found with slug \"${key}\". Try \\`fluid mist list\\` to see what's available.`,\n );\n }\n return found;\n}\n","/**\n * Mist CRUD over `/api/v202604/mists`. Mirrors the spec's endpoint table\n * one-for-one — no business logic.\n */\n\nimport type { MistClient } from \"./client.js\";\nimport type {\n MistCreateRequest,\n MistListResponse,\n MistShowResponse,\n MistUpdateRequest,\n} from \"../types.js\";\nimport { resolveMistId } from \"./id-resolver.js\";\n\nconst BASE = \"/api/v202604/mists\";\n\nexport interface ListMistsParams {\n state?: \"live\" | \"provisioning\" | \"failed\" | \"pending_destroy\" | \"archived\";\n kind?: \"next_app\";\n limit?: number;\n cursor?: string;\n}\n\nexport function listMists(\n client: MistClient,\n params: ListMistsParams = {},\n): Promise<MistListResponse> {\n return client.get<MistListResponse>(BASE, params as Record<string, unknown>);\n}\n\nexport async function showMist(\n client: MistClient,\n slug: string,\n): Promise<MistShowResponse> {\n const id = await resolveMistId(client, slug);\n return client.get<MistShowResponse>(`${BASE}/${id}`);\n}\n\nexport function createMist(\n client: MistClient,\n body: MistCreateRequest,\n): Promise<MistShowResponse> {\n return client.post<MistShowResponse>(BASE, body);\n}\n\nexport async function updateMist(\n client: MistClient,\n slug: string,\n body: MistUpdateRequest,\n): Promise<MistShowResponse> {\n const id = await resolveMistId(client, slug);\n return client.patch<MistShowResponse>(`${BASE}/${id}`, body);\n}\n\nexport async function deleteMist(\n client: MistClient,\n slug: string,\n): Promise<MistShowResponse> {\n const id = await resolveMistId(client, slug);\n return client.delete<MistShowResponse>(`${BASE}/${id}`);\n}\n\nexport async function restoreMist(\n client: MistClient,\n slug: string,\n): Promise<MistShowResponse> {\n const id = await resolveMistId(client, slug);\n return client.post<MistShowResponse>(`${BASE}/${id}/restore`);\n}\n","/**\n * Branch environments and branch operations for a Mist project.\n *\n * Server contracts (Rails, internal API v2026-04):\n * apps/rails/docs/openapi/internal/mist-branch-environments-v2026-04.yaml\n * apps/rails/docs/openapi/internal/mist-branch-operations-v2026-04.yaml\n *\n * Every response is checked at runtime before a caller sees it. A server\n * whose listing has no `capability` block predates branch environments and\n * reads as unsupported. A response that does not match the contract throws\n * `BranchContractError`; neither case is ever a reason to fall back to a\n * production publish.\n */\n\nimport { randomUUID } from \"node:crypto\";\nimport type { MistClient } from \"./client.js\";\nimport { resolveMistId } from \"./id-resolver.js\";\n\nexport const ENVIRONMENT_STATES = [\n \"provisioning\",\n \"ready\",\n \"error\",\n \"expired\",\n \"deleting\",\n \"deleted\",\n] as const;\nexport type EnvironmentState = (typeof ENVIRONMENT_STATES)[number];\n\nexport const OPERATION_KINDS = [\n \"ensure_staging\",\n \"create\",\n \"delete\",\n \"promote\",\n \"publish\",\n] as const;\nexport type OperationKind = (typeof OPERATION_KINDS)[number];\n\nexport const OPERATION_STATES = [\n \"queued\",\n \"running\",\n \"awaiting_content\",\n \"awaiting_resolution\",\n \"retryable_failure\",\n \"succeeded\",\n \"failed\",\n \"cancelled\",\n \"stale\",\n] as const;\nexport type OperationState = (typeof OPERATION_STATES)[number];\n\n/** States after which the server will not advance the operation again. */\nexport const TERMINAL_OPERATION_STATES: readonly OperationState[] = [\n \"succeeded\",\n \"failed\",\n \"cancelled\",\n \"stale\",\n];\n\nexport interface BranchEnvironment {\n id: string;\n slug: string;\n state: EnvironmentState;\n /** True for the reserved staging environment. */\n seeded: boolean;\n /** Stable address; null unless ready, and null for some older ready ones. */\n url: string | null;\n created_at: string;\n updated_at: string;\n}\n\nexport interface StagingCapability {\n /** Whether a new staging request would be admitted now. */\n available: boolean;\n /** Why it would be refused (e.g. `credentials_not_retired`), or null. */\n reason: string | null;\n /** The operation already working on staging, to follow instead. */\n active_operation_id: string | null;\n}\n\nexport type BranchCapability =\n | { supported: false }\n | { supported: true; version: number; staging: StagingCapability };\n\nexport interface BranchEnvironmentsListing {\n environments: BranchEnvironment[];\n capability: BranchCapability;\n}\n\nexport interface OperationProgress {\n database_isolated: boolean;\n branch_published: boolean;\n build_ready: boolean;\n}\n\nexport interface BranchOperation {\n id: string;\n kind: OperationKind;\n state: OperationState;\n source_revision: string;\n branch_id: string | null;\n progress: OperationProgress;\n /** Failure classification such as `build_failed`; never a description. */\n failure: { type: string } | null;\n created_at: string;\n updated_at: string;\n}\n\n/** A server response that does not match the branch environments contract. */\nexport class BranchContractError extends Error {\n constructor(message: string) {\n super(`Unexpected branch environments response: ${message}`);\n this.name = \"BranchContractError\";\n }\n}\n\nconst SOURCE_REVISION = /^[0-9a-f]{40}$/;\nconst OPERATION_KEY = /^[A-Za-z0-9._:-]{1,128}$/;\n\nfunction base(mistId: string): string {\n return `/api/v202604/mists/${mistId}`;\n}\n\n/** List the project's branch environments and what the server offers. */\nexport async function listBranchEnvironments(\n client: MistClient,\n slug: string,\n): Promise<BranchEnvironmentsListing> {\n const id = await resolveMistId(client, slug);\n const body = await client.get<unknown>(`${base(id)}/branch-environments`);\n return parseListing(body);\n}\n\n/**\n * Request the staging branch at `sourceRevision`. Reusing `operationKey`\n * with the same revision returns the same operation, so a caller persists\n * the key before calling and retries with it.\n */\nexport async function requestStaging(\n client: MistClient,\n slug: string,\n request: { sourceRevision: string; operationKey: string },\n): Promise<BranchOperation> {\n checkStagingRequest(request.sourceRevision, request.operationKey);\n const id = await resolveMistId(client, slug);\n const body = await client.post<unknown>(\n `${base(id)}/branch-environments/staging`,\n { source_revision: request.sourceRevision },\n { \"X-Operation-Key\": request.operationKey },\n );\n return parseOperationEnvelope(body);\n}\n\n/**\n * Ask the server to tear down one branch environment. The server only takes\n * it out of service and answers with it deleting (or already deleting or\n * deleted); a worker removes its resources shortly after. Repeating the\n * call is harmless.\n */\nexport async function removeBranchEnvironment(\n client: MistClient,\n slug: string,\n environmentId: string,\n): Promise<BranchEnvironment> {\n const id = await resolveMistId(client, slug);\n const body = await client.delete<unknown>(\n `${base(id)}/branch-environments/${encodeURIComponent(environmentId)}`,\n );\n return parseEnvironment(\n record(body, \"response\")[\"environment\"],\n \"environment\",\n );\n}\n\n/** Read one branch operation of the project. Never advances it. */\nexport async function fetchBranchOperation(\n client: MistClient,\n slug: string,\n operationId: string,\n): Promise<BranchOperation> {\n const id = await resolveMistId(client, slug);\n const body = await client.get<unknown>(\n `${base(id)}/branch-operations/${encodeURIComponent(operationId)}`,\n );\n return parseOperationEnvelope(body);\n}\n\n/**\n * Refuse a revision or operation key the server would reject, before\n * anything is sent or saved.\n *\n * Raises Error naming the malformed value.\n */\nexport function checkStagingRequest(\n sourceRevision: string,\n operationKey?: string,\n): void {\n if (!SOURCE_REVISION.test(sourceRevision)) {\n throw new Error(\"Source revision must be a full 40-character commit SHA\");\n }\n if (operationKey !== undefined && !isOperationKey(operationKey)) {\n throw new Error(\"Operation key must be 1-128 of A-Z a-z 0-9 . _ : -\");\n }\n}\n\n/** Whether the server would accept `key` as an operation key. */\nexport function isOperationKey(key: string): boolean {\n return OPERATION_KEY.test(key);\n}\n\n/** A fresh key for a new staging request. */\nexport function newOperationKey(): string {\n return `staging-${randomUUID()}`;\n}\n\nexport function isTerminal(operation: BranchOperation): boolean {\n return TERMINAL_OPERATION_STATES.includes(operation.state);\n}\n\nexport function parseListing(body: unknown): BranchEnvironmentsListing {\n const envelope = record(body, \"response\");\n const environments = array(envelope[\"environments\"], \"environments\").map(\n (item, index) => parseEnvironment(item, `environments[${index}]`),\n );\n return {\n environments,\n capability: parseCapability(envelope[\"capability\"]),\n };\n}\n\nfunction parseCapability(raw: unknown): BranchCapability {\n if (raw === undefined) return { supported: false };\n\n const capability = record(raw, \"capability\");\n const version = capability[\"version\"];\n if (\n typeof version !== \"number\" ||\n !Number.isInteger(version) ||\n version < 1\n ) {\n throw new BranchContractError(\n \"capability.version is not a positive integer\",\n );\n }\n const staging = record(capability[\"staging\"], \"capability.staging\");\n return {\n supported: true,\n version,\n staging: {\n available: boolean(staging[\"available\"], \"capability.staging.available\"),\n reason: nullableString(staging[\"reason\"], \"capability.staging.reason\"),\n active_operation_id: nullableString(\n staging[\"active_operation_id\"],\n \"capability.staging.active_operation_id\",\n ),\n },\n };\n}\n\nfunction parseEnvironment(raw: unknown, path: string): BranchEnvironment {\n const environment = record(raw, path);\n const url = nullableString(environment[\"url\"], `${path}.url`);\n if (url !== null && !url.startsWith(\"https://\")) {\n throw new BranchContractError(`${path}.url is not an https address`);\n }\n return {\n id: string(environment[\"id\"], `${path}.id`),\n slug: string(environment[\"slug\"], `${path}.slug`),\n state: member(environment[\"state\"], ENVIRONMENT_STATES, `${path}.state`),\n seeded: boolean(environment[\"seeded\"], `${path}.seeded`),\n url,\n created_at: string(environment[\"created_at\"], `${path}.created_at`),\n updated_at: string(environment[\"updated_at\"], `${path}.updated_at`),\n };\n}\n\nexport function parseOperationEnvelope(body: unknown): BranchOperation {\n return parseOperation(record(body, \"response\")[\"operation\"]);\n}\n\nfunction parseOperation(raw: unknown): BranchOperation {\n const operation = record(raw, \"operation\");\n const progress = record(operation[\"progress\"], \"operation.progress\");\n const sourceRevision = string(\n operation[\"source_revision\"],\n \"operation.source_revision\",\n );\n if (!SOURCE_REVISION.test(sourceRevision)) {\n throw new BranchContractError(\n \"operation.source_revision is not a commit SHA\",\n );\n }\n return {\n id: string(operation[\"id\"], \"operation.id\"),\n kind: member(operation[\"kind\"], OPERATION_KINDS, \"operation.kind\"),\n state: member(operation[\"state\"], OPERATION_STATES, \"operation.state\"),\n source_revision: sourceRevision,\n branch_id: nullableString(operation[\"branch_id\"], \"operation.branch_id\"),\n progress: {\n database_isolated: boolean(\n progress[\"database_isolated\"],\n \"operation.progress.database_isolated\",\n ),\n branch_published: boolean(\n progress[\"branch_published\"],\n \"operation.progress.branch_published\",\n ),\n build_ready: boolean(\n progress[\"build_ready\"],\n \"operation.progress.build_ready\",\n ),\n },\n failure: parseFailure(operation[\"failure\"]),\n created_at: string(operation[\"created_at\"], \"operation.created_at\"),\n updated_at: string(operation[\"updated_at\"], \"operation.updated_at\"),\n };\n}\n\nfunction parseFailure(raw: unknown): { type: string } | null {\n if (raw === null) return null;\n const failure = record(raw, \"operation.failure\");\n return { type: string(failure[\"type\"], \"operation.failure.type\") };\n}\n\nfunction record(raw: unknown, path: string): Record<string, unknown> {\n if (typeof raw !== \"object\" || raw === null || Array.isArray(raw)) {\n throw new BranchContractError(`${path} is not an object`);\n }\n return raw as Record<string, unknown>;\n}\n\nfunction array(raw: unknown, path: string): unknown[] {\n if (!Array.isArray(raw))\n throw new BranchContractError(`${path} is not a list`);\n return raw;\n}\n\nfunction string(raw: unknown, path: string): string {\n if (typeof raw !== \"string\") {\n throw new BranchContractError(`${path} is not a string`);\n }\n return raw;\n}\n\nfunction nullableString(raw: unknown, path: string): string | null {\n return raw === null ? null : string(raw, path);\n}\n\nfunction boolean(raw: unknown, path: string): boolean {\n if (typeof raw !== \"boolean\") {\n throw new BranchContractError(`${path} is not a boolean`);\n }\n return raw;\n}\n\nfunction member<T extends string>(\n raw: unknown,\n allowed: readonly T[],\n path: string,\n): T {\n if (\n typeof raw !== \"string\" ||\n !(allowed as readonly string[]).includes(raw)\n ) {\n throw new BranchContractError(`${path} has an unknown value`);\n }\n return raw as T;\n}\n","/**\n * Hostable reference parser. Accepts `<type>:<id>` shorthand and maps the\n * type half to the canonical Rails class name the API expects.\n *\n * Wire mapping (spec table):\n * droplet → Droplet::Application\n * mobile_widget → MobileWidget\n * drop_zone → DropZone\n *\n * The shorthand is forgiving (case-insensitive, accepts dashes or\n * underscores), but the output is exact so the Rails `included_in?`\n * check accepts it.\n */\n\nimport type { HostableType } from \"../types.js\";\n\nconst SHORTHAND: Record<string, HostableType> = {\n droplet: \"Droplet::Application\",\n droplets: \"Droplet::Application\",\n mobile_widget: \"MobileWidget\",\n \"mobile-widget\": \"MobileWidget\",\n mobilewidget: \"MobileWidget\",\n widget: \"MobileWidget\",\n drop_zone: \"DropZone\",\n \"drop-zone\": \"DropZone\",\n dropzone: \"DropZone\",\n};\n\n/** Friendly label for help text + error output. */\nexport const SHORTHAND_LIST = \"droplet, mobile_widget, drop_zone\";\n\nexport class HostableParseError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"HostableParseError\";\n }\n}\n\nexport interface HostablePair {\n hostable_type: HostableType;\n hostable_id: number;\n}\n\n/**\n * Parse a `<type>:<id>` shorthand, or accept already-canonical\n * `Droplet::Application:42` if the user prefers the Rails-style form.\n */\nexport function parseHostableRef(raw: string): HostablePair {\n const trimmed = raw.trim();\n if (!trimmed) {\n throw new HostableParseError(\n `Empty hostable reference. Use <type>:<id> where <type> is one of ${SHORTHAND_LIST}.`,\n );\n }\n // Split on the LAST colon so `Droplet::Application:42` still works.\n const lastColon = trimmed.lastIndexOf(\":\");\n if (lastColon <= 0 || lastColon === trimmed.length - 1) {\n throw new HostableParseError(\n `Invalid hostable reference \"${raw}\". Expected <type>:<id>, e.g. droplet:42.`,\n );\n }\n const typePart = trimmed.slice(0, lastColon).trim();\n const idPart = trimmed.slice(lastColon + 1).trim();\n const id = Number(idPart);\n if (!Number.isInteger(id) || id <= 0) {\n throw new HostableParseError(\n `Invalid hostable id \"${idPart}\". Must be a positive integer.`,\n );\n }\n const canonical = canonicalizeHostableType(typePart);\n return { hostable_type: canonical, hostable_id: id };\n}\n\n/**\n * Parse a `--hostable` ref down to a droplet uuid. Hostable is retired\n * (#19684/#19687) and droplets attach by their `drp_…` uuid (the numeric id\n * isn't exposed anywhere the CLI can read), so the ref carries a uuid:\n *\n * droplet:drp_abc123 → \"drp_abc123\"\n * drp_abc123 → \"drp_abc123\" (bare uuid, no prefix)\n *\n * A numeric ref (the old contract) is rejected with a pointer to the uuid.\n */\nexport function parseDropletRef(raw: string): string {\n const trimmed = raw.trim();\n if (!trimmed) {\n throw new HostableParseError(\n \"Empty droplet reference. Use `droplet:<uuid>` (e.g. droplet:drp_abc123).\",\n );\n }\n // Bare uuid (no `type:` prefix).\n if (/^drp_[A-Za-z0-9]+$/.test(trimmed)) return trimmed;\n\n const lastColon = trimmed.lastIndexOf(\":\");\n if (lastColon <= 0 || lastColon === trimmed.length - 1) {\n throw new HostableParseError(\n `Invalid droplet reference \"${raw}\". Expected droplet:<uuid>, e.g. droplet:drp_abc123.`,\n );\n }\n const typePart = trimmed.slice(0, lastColon).trim();\n const idPart = trimmed.slice(lastColon + 1).trim();\n if (canonicalizeHostableType(typePart) !== \"Droplet::Application\") {\n throw new HostableParseError(\n `Only droplets can be attached now that hostable is retired. Use \\`droplet:<uuid>\\` (got \"${raw}\").`,\n );\n }\n if (/^\\d+$/.test(idPart)) {\n throw new HostableParseError(\n `Droplets now attach by uuid, not numeric id. Use \\`droplet:<uuid>\\` (e.g. droplet:drp_abc123), not \"${raw}\".`,\n );\n }\n if (!/^drp_[A-Za-z0-9]+$/.test(idPart)) {\n throw new HostableParseError(\n `Invalid droplet uuid \"${idPart}\". Expected a drp_… uuid.`,\n );\n }\n return idPart;\n}\n\n/** Map an input string to the canonical Rails class name, throwing on miss. */\nexport function canonicalizeHostableType(typePart: string): HostableType {\n const exact: HostableType[] = [\n \"Droplet::Application\",\n \"MobileWidget\",\n \"DropZone\",\n ];\n if (exact.includes(typePart as HostableType)) return typePart as HostableType;\n const looked = SHORTHAND[typePart.toLowerCase().replace(/\\s+/g, \"\")];\n if (looked) return looked;\n throw new HostableParseError(\n `Unknown hostable type \"${typePart}\". Use one of: ${SHORTHAND_LIST}.`,\n );\n}\n","/**\n * Git wrapper — shells out to system `git` via execa. Spec §11.1: we\n * deliberately do NOT bundle git or use a pure-JS impl; users are expected\n * to have it installed.\n *\n * Security: token-bearing `remote_url` from POST /git_credential must never\n * be logged, persisted, *or* exposed in the process table. The token is\n * stripped from the URL and handed to git through a GIT_ASKPASS script via\n * env vars (MIST_GIT_USER / MIST_GIT_PASS) — so it never appears in argv\n * (visible to `ps aux` on macOS, `/proc/<pid>/cmdline` on Linux) and is\n * never written to `.git/config`.\n */\n\nimport {\n parseNameStatusZ,\n type ChangeEntry,\n type CommitMessage,\n} from \"@fluid-app/fluid-cli\";\nimport {\n chmodSync,\n existsSync,\n mkdirSync,\n readFileSync,\n rmSync,\n writeFileSync,\n} from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport { execa, type ExecaError } from \"execa\";\n\nexport class GitError extends Error {\n readonly exitCode: number;\n\n constructor(message: string, exitCode = 1) {\n super(message);\n this.name = \"GitError\";\n this.exitCode = exitCode;\n }\n}\n\n/**\n * Git author for Mist publish commits. Mist app repos are connected to the\n * we-commerce Vercel team via Vercel's GitHub integration, and Vercel\n * attributes a git-triggered deploy to the *commit author's* GitHub account.\n * If that author is a user's personal GitHub account (not a member of the\n * Vercel team), Vercel blocks the deployment (\"… does not have a Vercel\n * account linked to their GitHub account\"). Authoring every publish commit as\n * the Mist GitHub App bot — which the Vercel integration recognizes — keeps\n * deploys unblocked regardless of the user's local/global git identity. Human\n * attribution is preserved in the commit *message* (see push.ts), not the\n * author field.\n *\n * These MUST match the identity set on cloned repos in Mist Desktop\n * (apps/mist-desktop/src/main/ipc.ts) so both paths produce the same author.\n */\nconst MIST_GIT_AUTHOR_NAME = \"mist-we-commerce[bot]\";\nconst MIST_GIT_AUTHOR_EMAIL =\n \"293596226+mist-we-commerce[bot]@users.noreply.github.com\";\n\n/** execa throws ENOENT when the git binary itself is missing — the\n * single most common failure on non-developer machines. Translate it\n * into an actionable message instead of \"git <verb> failed (exit 1)\".\n * Returns null for every other failure so call sites keep their\n * specific messages. */\nfunction gitMissingError(err: unknown): GitError | null {\n const code = (err as NodeJS.ErrnoException | undefined)?.code;\n if (code !== \"ENOENT\") return null;\n return new GitError(\n \"Git isn't installed on this machine. In Mist Desktop, use the Install button in the yellow banner at the top of the app. From a terminal: macOS `xcode-select --install`, or download from https://git-scm.com/downloads.\",\n 127,\n );\n}\n\n/**\n * Split a token-bearing remote URL into the userless URL + creds. Used\n * to keep the token off git's argv: we pass `cleanUrl` to git and feed\n * `username` / `password` back through GIT_ASKPASS env vars.\n *\n * If the URL has no embedded credentials, returns empty strings — fine\n * for public repos where git won't even invoke askpass.\n */\nfunction splitRemoteCredentials(remoteUrl: string): {\n cleanUrl: string;\n username: string;\n password: string;\n} {\n const u = new URL(remoteUrl);\n const username = decodeURIComponent(u.username);\n const password = decodeURIComponent(u.password);\n u.username = \"\";\n u.password = \"\";\n return { cleanUrl: u.toString(), username, password };\n}\n\n/**\n * Write a stub GIT_ASKPASS shell script to a per-process tmpdir and\n * return its path. The script itself contains no secret — it just\n * echoes back whichever env var (MIST_GIT_USER / MIST_GIT_PASS)\n * matches git's prompt (\"Username for …\" / \"Password for …\"). The\n * credentials only live in the spawned git process's env for the\n * duration of the call.\n *\n * One POSIX script for all platforms: Git for Windows executes\n * GIT_ASKPASS through its bundled sh, so a .cmd askpass fails with\n * \"unable to read askpass response\" (git 2.52.0.windows.1) while a\n * plain sh script works.\n *\n * Idempotent: subsequent calls reuse the same script path.\n */\nlet askpassPath: string | null = null;\nfunction ensureAskpassScript(): string {\n if (askpassPath && existsSync(askpassPath)) return askpassPath;\n const dir = path.join(os.tmpdir(), `mist-cli-askpass-${process.pid}`);\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true, mode: 0o700 });\n const p = path.join(dir, \"askpass.sh\");\n writeFileSync(\n p,\n `#!/bin/sh\\ncase \"$1\" in\\n Username*) printf '%s' \"$MIST_GIT_USER\" ;;\\n *) printf '%s' \"$MIST_GIT_PASS\" ;;\\nesac\\n`,\n { mode: 0o700 },\n );\n chmodSync(p, 0o700);\n askpassPath = p;\n return askpassPath;\n}\n\n/**\n * Build the spawn env + cleaned URL for a credentialed git invocation.\n * Returns:\n * - `cleanUrl`: URL with userinfo stripped — safe to pass as argv.\n * - `env`: parent env plus GIT_ASKPASS + MIST_GIT_USER / MIST_GIT_PASS.\n * GIT_TERMINAL_PROMPT=0 prevents the TTY-prompt fallback if\n * the credentials are empty (we want auth to fail fast, not\n * hang the desktop waiting for interactive input).\n */\nfunction gitAuthSpawn(remoteUrl: string): {\n cleanUrl: string;\n env: NodeJS.ProcessEnv;\n} {\n const { cleanUrl, username, password } = splitRemoteCredentials(remoteUrl);\n return {\n cleanUrl,\n env: {\n ...process.env,\n GIT_ASKPASS: ensureAskpassScript(),\n GIT_TERMINAL_PROMPT: \"0\",\n MIST_GIT_USER: username,\n MIST_GIT_PASS: password,\n },\n };\n}\n\n/** Confirm git is installed. Cheap call once at startup. */\nexport async function ensureGitAvailable(): Promise<void> {\n try {\n await execa(\"git\", [\"--version\"]);\n } catch {\n throw new GitError(\n \"`git` is required but not found on your PATH. Install it from https://git-scm.com/downloads or via `brew install git`.\",\n );\n }\n}\n\n/**\n * Abort a transfer that has moved under 1 KB/s for a full minute. Without\n * this, git on a dropped or captive-portal Wi-Fi connection waits forever\n * and the caller (Fluid Mist's clone/sync) shows a spinner that never ends;\n * with it, the command fails and can be retried.\n */\nexport const GIT_STALL_GUARD_ARGS = [\n \"-c\",\n \"http.lowSpeedLimit=1000\",\n \"-c\",\n \"http.lowSpeedTime=60\",\n] as const;\n\n/**\n * `git clone <cleanUrl> <targetDir>`. The original remoteUrl carries a\n * token; gitAuthSpawn strips it from the URL and re-injects it via\n * GIT_ASKPASS so it never appears in argv. `credential.helper=` stops\n * git from caching what askpass returned to any system credential\n * store.\n */\nexport async function gitClone(\n remoteUrl: string,\n targetDir: string,\n): Promise<void> {\n const { cleanUrl, env } = gitAuthSpawn(remoteUrl);\n try {\n await execa(\n \"git\",\n [\n ...GIT_STALL_GUARD_ARGS,\n \"-c\",\n \"credential.helper=\",\n \"clone\",\n \"--quiet\",\n cleanUrl,\n targetDir,\n ],\n { env, stdio: [\"ignore\", \"inherit\", \"inherit\"] },\n );\n } catch (err) {\n const missing = gitMissingError(err);\n if (missing) throw missing;\n const e = err as ExecaError;\n throw new GitError(\n `git clone failed${e.exitCode != null ? ` (exit ${e.exitCode})` : \"\"}`,\n e.exitCode ?? 1,\n );\n }\n // Every commit made in this checkout should be bot-authored (Vercel\n // only deploys we-commerce team members). See ensureBotIdentity.\n await ensureBotIdentity(targetDir);\n}\n\n/** `git push origin <branch>` — credentials supplied via GIT_ASKPASS,\n * never in argv. The `.extraheader=` override clears any cached\n * Authorization header that a CI-style git config might inject. */\nexport async function gitPush(\n cwd: string,\n remoteUrl: string,\n branch = \"main\",\n): Promise<void> {\n const { cleanUrl, env } = gitAuthSpawn(remoteUrl);\n try {\n await execa(\n \"git\",\n [\n \"-c\",\n \"credential.helper=\",\n \"-c\",\n `http.https://github.com/.extraheader=`,\n \"push\",\n cleanUrl,\n `HEAD:${branch}`,\n ],\n { cwd, env, stdio: [\"ignore\", \"inherit\", \"inherit\"] },\n );\n } catch (err) {\n const missing = gitMissingError(err);\n if (missing) throw missing;\n const e = err as ExecaError;\n throw new GitError(\n `git push failed${e.exitCode != null ? ` (exit ${e.exitCode})` : \"\"}`,\n e.exitCode ?? 1,\n );\n }\n}\n\n/** `git pull origin <branch>`. Token supplied via GIT_ASKPASS, same as push. */\nexport async function gitPull(\n cwd: string,\n remoteUrl: string,\n branch = \"main\",\n): Promise<void> {\n const { cleanUrl, env } = gitAuthSpawn(remoteUrl);\n try {\n await execa(\n \"git\",\n [\n ...GIT_STALL_GUARD_ARGS,\n \"-c\",\n \"credential.helper=\",\n \"pull\",\n \"--ff-only\",\n cleanUrl,\n branch,\n ],\n { cwd, env, stdio: [\"ignore\", \"inherit\", \"inherit\"] },\n );\n } catch (err) {\n const missing = gitMissingError(err);\n if (missing) throw missing;\n const e = err as ExecaError;\n throw new GitError(\n `git pull failed${e.exitCode != null ? ` (exit ${e.exitCode})` : \"\"}`,\n e.exitCode ?? 1,\n );\n }\n}\n\n/** True when `relativePath` is ignored by the repo at `cwd`. */\nexport async function isGitIgnored(\n cwd: string,\n relativePath: string,\n): Promise<boolean> {\n try {\n await execa(\"git\", [\"check-ignore\", \"--quiet\", \"--\", relativePath], {\n cwd,\n });\n return true;\n } catch {\n return false;\n }\n}\n\n/** Read `git remote get-url origin` (or null if no origin). */\nexport async function getOriginUrl(cwd: string): Promise<string | null> {\n try {\n const { stdout } = await execa(\"git\", [\"remote\", \"get-url\", \"origin\"], {\n cwd,\n });\n return stdout.trim();\n } catch {\n return null;\n }\n}\n\n/** Set `origin` to the given URL. Used by `fluid mist remote restore`. */\nexport async function setOriginUrl(cwd: string, url: string): Promise<void> {\n try {\n await execa(\"git\", [\"remote\", \"set-url\", \"origin\", url], { cwd });\n } catch (err) {\n const missing = gitMissingError(err);\n if (missing) throw missing;\n const e = err as ExecaError;\n throw new GitError(`git remote set-url failed`, e.exitCode ?? 1);\n }\n}\n\n/**\n * Are there uncommitted changes (working tree or index) in this repo?\n * `git status --porcelain` is empty when everything is clean, non-empty\n * otherwise — so the count is just \"is the output non-trivial\".\n */\nexport async function hasUncommittedChanges(cwd: string): Promise<boolean> {\n try {\n const { stdout } = await execa(\"git\", [\"status\", \"--porcelain\"], { cwd });\n return stdout.trim().length > 0;\n } catch {\n return false;\n }\n}\n\n/**\n * Fetch the given branch from the token-bearing remote URL, writing\n * `FETCH_HEAD` so `git rev-list HEAD...FETCH_HEAD` is meaningful.\n *\n * Same security stance as gitPush — installation token comes through\n * GIT_ASKPASS, never argv or `.git/config`.\n */\nexport async function gitFetch(\n cwd: string,\n remoteUrl: string,\n branch = \"main\",\n): Promise<void> {\n const { cleanUrl, env } = gitAuthSpawn(remoteUrl);\n try {\n await execa(\n \"git\",\n [\n ...GIT_STALL_GUARD_ARGS,\n \"-c\",\n \"credential.helper=\",\n \"fetch\",\n \"--quiet\",\n cleanUrl,\n branch,\n ],\n { cwd, env },\n );\n } catch (err) {\n const missing = gitMissingError(err);\n if (missing) throw missing;\n const e = err as ExecaError;\n throw new GitError(\n `git fetch failed${e.exitCode != null ? ` (exit ${e.exitCode})` : \"\"}`,\n e.exitCode ?? 1,\n );\n }\n}\n\n/**\n * Count commits in local HEAD vs `FETCH_HEAD` (after `gitFetch`). Returns\n * `{ahead, behind}`: how many commits HEAD has that the remote doesn't,\n * and vice versa. A `0/0` result means \"in sync\".\n */\nexport async function gitAheadBehindVsFetchHead(\n cwd: string,\n): Promise<{ ahead: number; behind: number }> {\n try {\n const { stdout } = await execa(\n \"git\",\n [\"rev-list\", \"--left-right\", \"--count\", \"HEAD...FETCH_HEAD\"],\n { cwd },\n );\n const [a, b] = stdout.trim().split(/\\s+/);\n const ahead = Number.parseInt(a ?? \"0\", 10);\n const behind = Number.parseInt(b ?? \"0\", 10);\n return {\n ahead: Number.isFinite(ahead) ? ahead : 0,\n behind: Number.isFinite(behind) ? behind : 0,\n };\n } catch {\n // No FETCH_HEAD yet → treat as \"no remote info\". Caller decides.\n return { ahead: 0, behind: 0 };\n }\n}\n\n/** Fast-forward `HEAD` to `FETCH_HEAD`. Fails (non-zero exit) if not\n * fast-forwardable — caller must check ahead-count first. */\nexport async function gitMergeFastForward(cwd: string): Promise<void> {\n try {\n await execa(\"git\", [\"merge\", \"--ff-only\", \"FETCH_HEAD\"], { cwd });\n } catch (err) {\n const missing = gitMissingError(err);\n if (missing) throw missing;\n const e = err as ExecaError;\n throw new GitError(\n `git fast-forward merge failed${e.exitCode != null ? ` (exit ${e.exitCode})` : \"\"}`,\n e.exitCode ?? 1,\n );\n }\n}\n\n/**\n * Stash working-tree + untracked changes with a Mist-tagged message.\n * Returns the stash ref (`stash@{0}`) we created. Callers should pair\n * this with `gitStashPop` after the dangerous step is done.\n */\nexport async function gitStash(cwd: string, message: string): Promise<void> {\n try {\n await execa(\n \"git\",\n [\"stash\", \"push\", \"--include-untracked\", \"--message\", message],\n { cwd },\n );\n } catch (err) {\n const missing = gitMissingError(err);\n if (missing) throw missing;\n const e = err as ExecaError;\n throw new GitError(`git stash push failed`, e.exitCode ?? 1);\n }\n}\n\n/** Read the current HEAD sha, or null if there is no commit yet. */\nexport async function getHeadSha(cwd: string): Promise<string | null> {\n try {\n const { stdout } = await execa(\"git\", [\"rev-parse\", \"--verify\", \"HEAD\"], {\n cwd,\n });\n return stdout.trim() || null;\n } catch {\n return null;\n }\n}\n\n/** Shown when a teammate's pulled change collides with local edits. */\nexport const STASH_CONFLICT_MESSAGE =\n \"Your unpublished edits conflict with a teammate's change to the same files. \" +\n \"Nothing was published and your edits are back exactly as they were. \" +\n \"Merge the teammate's change first (ask Mist to, or run `git pull` in a terminal), then publish again.\";\n\n/**\n * Pop the most recent stash.\n *\n * Without `restoreHead`, a failed pop throws and leaves the tree as git\n * left it. With `restoreHead` (the HEAD the stash was taken on, captured\n * BEFORE the pull), a failed pop never leaves conflict markers behind: the\n * tree is hard-reset to `restoreHead`, any untracked files the failed pop\n * already wrote are removed (the stash still holds them), and the stash is\n * popped again onto the base it was taken from — which applies cleanly.\n * The pulled commits are dropped from HEAD (they stay on the remote), and\n * a GitError with {@link STASH_CONFLICT_MESSAGE} is thrown so the caller\n * aborts the publish instead of committing `<<<<<<<` markers.\n */\nexport async function gitStashPop(\n cwd: string,\n restoreHead?: string | null,\n): Promise<void> {\n try {\n await execa(\"git\", [\"stash\", \"pop\"], { cwd });\n return;\n } catch (err) {\n const missing = gitMissingError(err);\n if (missing) throw missing;\n if (!restoreHead) {\n const e = err as ExecaError;\n throw new GitError(\n `Couldn't restore your local changes after pulling — the file edits collided with a teammate's. Run \\`git stash list\\` and resolve manually.`,\n e.exitCode ?? 1,\n );\n }\n }\n await restoreStashOnto(cwd, restoreHead);\n throw new GitError(STASH_CONFLICT_MESSAGE, 1);\n}\n\nasync function restoreStashOnto(cwd: string, head: string): Promise<void> {\n try {\n await execa(\"git\", [\"reset\", \"--hard\", \"--quiet\", head], { cwd });\n // A conflicting pop restores the stash's untracked files before the\n // tracked merge fails; they'd block the second pop (\"already exists\").\n // They're still in the stash's third parent, so removing them is safe.\n const untracked = await execa(\n \"git\",\n [\"ls-tree\", \"-r\", \"-z\", \"--name-only\", \"stash@{0}^3\"],\n { cwd, reject: false },\n );\n if (untracked.exitCode === 0) {\n const root = (\n await execa(\"git\", [\"rev-parse\", \"--show-toplevel\"], { cwd })\n ).stdout.trim();\n for (const rel of untracked.stdout.split(\"\\0\").filter(Boolean)) {\n rmSync(path.join(root, rel), { force: true });\n }\n }\n await execa(\"git\", [\"stash\", \"pop\"], { cwd });\n } catch (err) {\n const missing = gitMissingError(err);\n if (missing) throw missing;\n throw new GitError(\n \"Your edits conflict with a teammate's change and couldn't be restored automatically. Nothing was published; your edits are saved in `git stash list`.\",\n 1,\n );\n }\n}\n\n/**\n * Refuse to commit a tree that still carries merge-conflict state:\n * unmerged index entries (`git ls-files -u`), or — checked after staging —\n * a staged file with a line starting `<<<<<<< `. Publishing either would\n * deploy a broken site.\n */\nexport async function assertNoUnmergedPaths(cwd: string): Promise<void> {\n const { stdout } = await execa(\"git\", [\"ls-files\", \"-u\", \"-z\"], {\n cwd,\n reject: false,\n });\n if (stdout.trim().length > 0) {\n throw new GitError(CONFLICT_REFUSAL_MESSAGE, 1);\n }\n}\n\nconst CONFLICT_REFUSAL_MESSAGE =\n \"Some files still have unresolved merge conflicts (lines starting `<<<<<<<`). \" +\n \"Nothing was published. Resolve the conflicts, then publish again.\";\n\nexport async function assertNoStagedConflictMarkers(\n cwd: string,\n): Promise<void> {\n const staged = await execa(\n \"git\",\n [\"diff\", \"--cached\", \"--name-only\", \"--diff-filter=ACMR\", \"-z\"],\n { cwd, reject: false },\n );\n const stagedPaths = new Set(staged.stdout.split(\"\\0\").filter(Boolean));\n if (stagedPaths.size === 0) return;\n // `git grep --cached` searches index content; -I skips binaries; exit 1\n // means no match. `:(top)` + --full-name make it repo-wide and\n // root-relative, matching `git diff`'s paths, even from a subdirectory.\n const hits = await execa(\n \"git\",\n [\n \"grep\",\n \"--cached\",\n \"-I\",\n \"-l\",\n \"-z\",\n \"--full-name\",\n \"-E\",\n \"^<<<<<<< \",\n \"--\",\n \":(top)\",\n ],\n { cwd, reject: false },\n );\n if (hits.exitCode !== 0) return;\n const conflicted = hits.stdout\n .split(\"\\0\")\n .filter((p) => p && stagedPaths.has(p));\n if (conflicted.length > 0) {\n throw new GitError(\n `${CONFLICT_REFUSAL_MESSAGE} Conflicted: ${conflicted.slice(0, 5).join(\", \")}`,\n 1,\n );\n }\n}\n\n/**\n * Persist the Mist bot as the repo's LOCAL git identity\n * (`git config --local user.{name,email}`).\n *\n * `gitAutoCommit` already forces the bot author on the publish commit\n * via `-c`/env, but that only covers commits WE make. Commits made any\n * other way against the same checkout — the Mist chat agent shelling out\n * `git commit` via run_cli, or the user in a terminal — inherit the\n * repo's configured identity, which is the person's personal GitHub\n * account. Vercel rejects a deploy whose commit author isn't a\n * we-commerce team member, so those personal-authored commits silently\n * block the deploy (exactly the www-* \"stuck project\" case).\n *\n * Setting the identity at the repo level makes EVERY commit path\n * bot-authored, and heals repos cloned before this was in place on their\n * next publish. Best-effort: a config failure must never abort a push —\n * the publish commit is still bot-forced by gitAutoCommit regardless.\n */\nexport async function ensureBotIdentity(cwd: string): Promise<void> {\n try {\n await execa(\n \"git\",\n [\"config\", \"--local\", \"user.name\", MIST_GIT_AUTHOR_NAME],\n { cwd },\n );\n await execa(\n \"git\",\n [\"config\", \"--local\", \"user.email\", MIST_GIT_AUTHOR_EMAIL],\n { cwd },\n );\n } catch {\n // Non-fatal — see doc comment.\n }\n}\n\n/**\n * Append Mist's local-only state to `.git/info/exclude` when needed:\n * `.mist` is the clone marker from spec §3, while `.mist-desktop/`\n * contains private chat history, attachments, screenshots, and other\n * machine-local Desktop state. Neither belongs in the project remote.\n *\n * Why local exclude and not `.gitignore`: `.gitignore` lives in the\n * template repo and would need a template release to reach existing\n * clones. `.git/info/exclude` is per-checkout, so writing it here (at\n * clone time) AND defensively at the top of every `gitAutoCommit` (at\n * push time) heals pre-existing clones on their very next push, with no\n * template change required.\n *\n * Idempotent and best-effort: never throws. A repo with an unusual\n * `.git` layout (missing `info/`, `.git` as a worktree pointer file,\n * etc.) just skips the exclude — `gitAutoCommit` also cleans both paths\n * from the index after staging.\n */\nexport function excludeMistMarkerFromGit(cwd: string): void {\n try {\n const gitDir = path.join(cwd, \".git\");\n if (!existsSync(gitDir)) return;\n const infoDir = path.join(gitDir, \"info\");\n if (!existsSync(infoDir)) mkdirSync(infoDir, { recursive: true });\n const excludePath = path.join(infoDir, \"exclude\");\n const existing = existsSync(excludePath)\n ? readFileSync(excludePath, \"utf8\")\n : \"\";\n const existingLines = new Set(\n existing\n .split(\"\\n\")\n .map((line) => line.trim())\n .filter(Boolean),\n );\n const missing = [\".mist\", \".mist-desktop/\"].filter(\n (entry) => !existingLines.has(entry),\n );\n if (missing.length === 0) return;\n const needsLeadingNewline = existing.length > 0 && !existing.endsWith(\"\\n\");\n writeFileSync(\n excludePath,\n `${existing}${needsLeadingNewline ? \"\\n\" : \"\"}${missing.join(\"\\n\")}\\n`,\n \"utf8\",\n );\n } catch {\n // Best-effort — see doc comment. gitAutoCommit's explicit index cleanup\n // still keeps local Mist state out if this fails silently.\n }\n}\n\n/**\n * `git add -A && git commit -m <message>` in `cwd`.\n *\n * Mist Desktop's \"Publish\" UX target is non-technical: the user expects\n * \"ship my changes\" to ship the changes they made, not to know about\n * staging or commit messages. This helper bundles working-tree edits\n * into one commit per publish so push has something to send.\n *\n * The commit author is ALWAYS forced to the Mist GitHub App bot identity\n * (MIST_GIT_AUTHOR_*), overriding whatever local/global git identity the\n * machine has. This is what keeps Vercel from blocking the deploy: a commit\n * authored by a user's personal GitHub account that isn't a we-commerce Vercel\n * team member gets rejected. Passing `-c user.name=… -c user.email=…` beats\n * even a `--local` config, so every publish is bot-authored regardless of how\n * the repo landed on disk (fresh clone, pre-existing clone, or terminal CLI).\n * The real person is still recorded in the commit *message* (see push.ts).\n *\n * Local Mist state handling: clone writes `.mist` and `.mist-desktop/` to\n * `.git/info/exclude`, but this heals a pre-existing clone or a repo whose\n * exclude write silently failed. Belt AND suspenders:\n * 1. Re-run the exclude write here (idempotent) — heals pre-existing\n * clones on their very next push. Anchored to the repository root\n * (`git rev-parse --show-toplevel`), not cwd, so a push run from a\n * repo subdirectory heals the real `.git/info/exclude`.\n * 2. After `git add -A`, run `git reset -- ':(top).mist' .mist`\n * (root-anchored + cwd-relative pathspecs; best-effort,\n * ignored if it fails or matches nothing) to unstage the marker in\n * case the exclude write above failed and `.mist` got swept in\n * anyway. We deliberately do NOT ask `git add` to exclude `.mist`\n * via a `:(exclude)` pathspec — when a path is *also* already\n * listed in `.git/info/exclude`, combining `-A` with an explicit\n * exclude pathspec on that same path makes git exit 1 with\n * \"paths are ignored by one of your .gitignore files\" (a real git\n * quirk, confirmed against git 2.41), which would turn the normal\n * case (exclude already written) into a hard failure. Add-then-\n * reset avoids that entirely and is a no-op when there's nothing\n * to unstage.\n * 3. Run `git rm --cached -r --ignore-unmatch .mist-desktop` from the\n * repository root. This keeps every local file on disk while staging\n * their removal if an older publish already tracked them. Ignore rules\n * alone cannot untrack an existing path.\n * Note: a `.mist` file already committed in an existing repo's history\n * is left alone — this only prevents *new* adds, it does not untrack\n * anything or rewrite history.\n */\n/**\n * The staged file set, as {@link ChangeEntry}s — the input a\n * {@link CommitMessage} resolver summarizes.\n *\n * `-z` (NUL-delimited) rather than the default: paths with spaces or\n * quotes are legal in git and common in themes and asset folders, and\n * the newline form quotes and escapes them, which would corrupt both the\n * path and the count.\n *\n * Best-effort — a summary is a nicety, never a reason to fail a publish.\n * An empty result makes the resolver fall back to its own constant.\n */\nasync function stagedChanges(cwd: string): Promise<ChangeEntry[]> {\n try {\n const { stdout } = await execa(\n \"git\",\n [\"diff\", \"--cached\", \"--name-status\", \"-z\"],\n { cwd },\n );\n return parseNameStatusZ(stdout);\n } catch {\n return [];\n }\n}\n\nexport async function gitAutoCommit(\n cwd: string,\n message: CommitMessage,\n): Promise<void> {\n // Persist bot identity so this AND any later terminal/agent commit on\n // the checkout is bot-authored (not just this one via -c/env below).\n await ensureBotIdentity(cwd);\n // Both `.mist` defenses must anchor to the REPOSITORY ROOT, not cwd:\n // a push run from a repo subdirectory (explicit slug from a nested\n // terminal, say) would otherwise look for `<cwd>/.git` — silently\n // skipping the exclude write — while `git add -A` still stages the\n // whole repository, letting the root marker into the commit. Resolve\n // the root first; fall back to cwd (the common root-invocation case)\n // when resolution fails — the `git add` below surfaces the real error.\n let repoRoot = cwd;\n try {\n const { stdout } = await execa(\"git\", [\"rev-parse\", \"--show-toplevel\"], {\n cwd,\n });\n if (stdout.trim().length > 0) repoRoot = stdout.trim();\n } catch {\n // Not a repo / git missing — handled by the add/commit path below.\n }\n // Defensive: heal clones made before writeMistFile started excluding\n // `.mist`, or one whose exclude write failed at clone time.\n excludeMistMarkerFromGit(repoRoot);\n // Never publish an unfinished merge: `git add -A` would mark unmerged\n // paths resolved with their `<<<<<<<` markers still inside.\n await assertNoUnmergedPaths(cwd);\n // Stage everything (including deletes).\n try {\n await execa(\"git\", [\"add\", \"-A\"], { cwd });\n } catch (err) {\n const missing = gitMissingError(err);\n if (missing) throw missing;\n const e = err as ExecaError;\n throw new GitError(`git add failed`, e.exitCode ?? 1);\n }\n // Second line of defense: unstage `.mist` if it somehow got staged\n // above (only possible if the exclude write failed). Two pathspecs:\n // `:(top).mist` resolves against the working-tree ROOT, so a push\n // run from a repo subdirectory still unstages the root marker that\n // `git add -A` can sweep in; plain `.mist` additionally covers a\n // project directory that is itself nested inside a larger repo.\n // Best-effort — `git reset` on paths with nothing staged, or that\n // don't exist, is already a no-op that exits 0, so failures here\n // are unexpected and non-fatal to publish.\n try {\n await execa(\"git\", [\"reset\", \"-q\", \"--\", \":(top).mist\", \".mist\"], { cwd });\n } catch {\n // Non-fatal — see doc comment.\n }\n // `.mist-desktop/` may contain private chats, user attachments, and large\n // screenshots. Remove it from the index even if an older publish already\n // tracked it; --cached deliberately preserves the local directory.\n try {\n await execa(\n \"git\",\n [\"rm\", \"-q\", \"-r\", \"--cached\", \"--ignore-unmatch\", \"--\", \".mist-desktop\"],\n { cwd: repoRoot },\n );\n } catch {\n // Non-fatal — the exclude file remains the primary defense for new paths.\n }\n await assertNoStagedConflictMarkers(cwd);\n // Resolve the subject only now: everything above decides what is\n // actually staged (add -A, then the `.mist` / `.mist-desktop`\n // unstaging), so a summary built any earlier would describe a file set\n // the commit doesn't carry.\n const subject =\n typeof message === \"string\"\n ? message\n : await message(await stagedChanges(cwd));\n\n // Commit as the Mist bot. `-c` overrides any configured user.name/user.email\n // (including `--local`); the GIT_{AUTHOR,COMMITTER}_* env vars additionally\n // override even `-c` (git reads them ahead of config), so we set both to seal\n // every precedence layer and make the author deterministic and Vercel-safe.\n const identityArgs = [\n \"-c\",\n `user.name=${MIST_GIT_AUTHOR_NAME}`,\n \"-c\",\n `user.email=${MIST_GIT_AUTHOR_EMAIL}`,\n ];\n const identityEnv: NodeJS.ProcessEnv = {\n GIT_AUTHOR_NAME: MIST_GIT_AUTHOR_NAME,\n GIT_AUTHOR_EMAIL: MIST_GIT_AUTHOR_EMAIL,\n GIT_COMMITTER_NAME: MIST_GIT_AUTHOR_NAME,\n GIT_COMMITTER_EMAIL: MIST_GIT_AUTHOR_EMAIL,\n };\n try {\n await execa(\"git\", [...identityArgs, \"commit\", \"-m\", subject], {\n cwd,\n env: { ...process.env, ...identityEnv },\n stdio: [\"ignore\", \"inherit\", \"inherit\"],\n });\n } catch (err) {\n const missing = gitMissingError(err);\n if (missing) throw missing;\n const e = err as ExecaError;\n // exit 1 with \"nothing to commit\" means there was nothing actually\n // staged (race or .gitignore). Treat as a no-op rather than an error.\n if (typeof e.stderr === \"string\" && /nothing to commit/i.test(e.stderr)) {\n return;\n }\n throw new GitError(\n `git commit failed${e.exitCode != null ? ` (exit ${e.exitCode})` : \"\"}`,\n e.exitCode ?? 1,\n );\n }\n}\n","/**\n * Slug resolution — spec §3 conventions:\n * 1. If a slug arg is passed, use it.\n * 2. Otherwise look for a `.mist` file in CWD (written by `fluid mist\n * clone`) and use the slug from it.\n * 3. Otherwise error: tell the user to pass a slug or cd into a cloned dir.\n */\n\nimport { existsSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { excludeMistMarkerFromGit } from \"./git.js\";\nimport type { Mist, MistFile } from \"../types.js\";\n\nconst MIST_FILE = \".mist\";\n\nexport class SlugResolutionError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"SlugResolutionError\";\n }\n}\n\nfunction readMistFile(dir: string): MistFile | null {\n const filePath = join(dir, MIST_FILE);\n if (!existsSync(filePath)) return null;\n try {\n const raw = readFileSync(filePath, \"utf8\");\n const parsed = JSON.parse(raw) as unknown;\n if (!parsed || typeof parsed !== \"object\") return null;\n const obj = parsed as Record<string, unknown>;\n if (typeof obj[\"slug\"] !== \"string\") return null;\n return {\n slug: obj[\"slug\"] as string,\n vendor_name:\n typeof obj[\"vendor_name\"] === \"string\"\n ? (obj[\"vendor_name\"] as string)\n : \"\",\n public_url:\n typeof obj[\"public_url\"] === \"string\"\n ? (obj[\"public_url\"] as string)\n : null,\n };\n } catch {\n return null;\n }\n}\n\n/** Returns the slug from arg or `.mist` file; throws if neither is present. */\nexport function resolveSlug(arg?: string, cwd: string = process.cwd()): string {\n if (arg && arg.trim().length > 0) return arg.trim();\n const file = readMistFile(cwd);\n if (file) return file.slug;\n throw new SlugResolutionError(\n \"No slug provided and no `.mist` file in this directory. Pass the slug as an argument or cd into a cloned mist. \" +\n \"(if you reverted a commit that removed .mist, pass the slug explicitly or re-run fluid mist clone)\",\n );\n}\n\n/**\n * Write a `.mist` file after `fluid mist clone`.\n *\n * Also excludes `.mist` via `.git/info/exclude` so it can never be swept\n * into a publish commit by `gitAutoCommit`'s `git add -A` — a commit\n * containing `.mist` that later gets `git revert`-ed deletes the marker\n * and locks every subsequent `fluid mist <cmd>` out of slug resolution\n * in that clone. Best-effort: if the exclude write fails (e.g. `.git`\n * isn't set up yet), `writeFileSync` below still runs — this is a\n * defense-in-depth step, not a precondition for the marker itself.\n */\nexport function writeMistFile(dir: string, mist: Mist): void {\n excludeMistMarkerFromGit(dir);\n const payload: MistFile = {\n slug: mist.slug,\n vendor_name: mist.vendor_name,\n public_url: mist.public_url,\n };\n writeFileSync(\n join(dir, MIST_FILE),\n JSON.stringify(payload, null, 2) + \"\\n\",\n \"utf8\",\n );\n}\n\nexport function readMistFileFrom(dir: string = process.cwd()): MistFile | null {\n return readMistFile(dir);\n}\n","/**\n * Pick the commit `fluid mist staging request` stages when no `--revision`\n * is given: the local checkout's HEAD, but only when nothing local would be\n * left out or lost.\n *\n * Staging can only serve a commit the mist's repository already has, and\n * this never pushes or switches branches to get one there; publishing to\n * main would ship the work to production. So the checkout must be clean\n * and its HEAD must already be on the remote's main branch.\n */\n\nimport { execa } from \"execa\";\nimport { gitFetch } from \"./git.js\";\n\n/** The local checkout can't be staged as it is. Nothing was changed. */\nexport class StagingPreflightError extends Error {\n constructor(message: string) {\n super(`${message} Nothing was requested.`);\n this.name = \"StagingPreflightError\";\n }\n}\n\n/**\n * The full SHA of the checkout's HEAD once it's clean and on the remote's\n * main branch. `remoteUrl` may carry a short-lived token; it only reaches\n * git through `gitFetch`, never argv or config.\n *\n * Raises StagingPreflightError when the directory is not a git checkout,\n * git can't report its status, it has uncommitted or untracked files, or\n * HEAD is not on the remote's main.\n */\nexport async function resolveLocalStagingRevision(\n cwd: string,\n remoteUrl: string,\n): Promise<string> {\n const inside = await git(cwd, [\"rev-parse\", \"--is-inside-work-tree\"]);\n if (inside !== \"true\") {\n throw new StagingPreflightError(\n \"This directory isn't a git checkout of the mist. Run it from the mist's folder or pass --revision.\",\n );\n }\n // Only git saying \"clean\" counts; a status that can't be read is not.\n // Untracked files are listed whatever status.showUntrackedFiles says.\n const status = await git(cwd, [\n \"status\",\n \"--porcelain\",\n \"--untracked-files=all\",\n ]);\n if (status === null) {\n throw new StagingPreflightError(\n \"Git couldn't report whether this checkout has uncommitted changes. Pass --revision to stage a specific commit.\",\n );\n }\n if (status.length > 0) {\n throw new StagingPreflightError(\n \"This checkout has uncommitted or untracked changes, and staging only serves commits already on the mist's repository. Commit and publish them, or pass --revision.\",\n );\n }\n const head = await git(cwd, [\"rev-parse\", \"HEAD\"]);\n if (!head || !/^[0-9a-f]{40}$/.test(head)) {\n throw new StagingPreflightError(\"This checkout has no commits yet.\");\n }\n\n await gitFetch(cwd, remoteUrl, \"main\");\n const onMain = await execa(\n \"git\",\n [\"merge-base\", \"--is-ancestor\", head, \"FETCH_HEAD\"],\n { cwd, reject: false },\n );\n if (onMain.exitCode !== 0) {\n throw new StagingPreflightError(\n `Commit ${head.slice(0, 7)} isn't on the mist's main branch yet, and staging only serves commits the repository already has.`,\n );\n }\n return head;\n}\n\nasync function git(cwd: string, args: string[]): Promise<string | null> {\n const run = await execa(\"git\", args, { cwd, reject: false });\n return run.exitCode === 0 ? run.stdout.trim() : null;\n}\n","/**\n * Staging workflow for `fluid mist staging`: read where staging stands,\n * request it, and follow the operation until the server finishes it.\n *\n * The server is authoritative. Nothing here falls back to a production\n * publish: an unsupported server or an unavailable staging stops with an\n * explanation instead.\n */\n\nimport type { MistClient } from \"../api/client.js\";\nimport {\n fetchBranchOperation,\n isTerminal,\n listBranchEnvironments,\n newOperationKey,\n requestStaging,\n type BranchEnvironment,\n type BranchOperation,\n type StagingCapability,\n} from \"../api/branch_environments.js\";\n\n/** A staging step the user can act on, as opposed to a server fault. */\nexport class StagingUnavailableError extends Error {\n readonly reason: string;\n readonly activeOperationId: string | null;\n\n constructor(\n reason: string,\n message: string,\n activeOperationId: string | null = null,\n ) {\n super(message);\n this.name = \"StagingUnavailableError\";\n this.reason = reason;\n this.activeOperationId = activeOperationId;\n }\n}\n\n/** The operation stopped without succeeding. */\nexport class StagingOperationFailedError extends Error {\n readonly operation: BranchOperation;\n\n constructor(operation: BranchOperation) {\n const cause = operation.failure ? ` (${operation.failure.type})` : \"\";\n super(`Staging ${operation.state}${cause}. Operation ${operation.id}.`);\n this.name = \"StagingOperationFailedError\";\n this.operation = operation;\n }\n}\n\n/** The local wait ran out; the server keeps working on the operation. */\nexport class StagingWaitTimeoutError extends Error {\n readonly operation: BranchOperation;\n\n constructor(operation: BranchOperation) {\n super(\n `Still ${operation.state} after the wait limit. The server keeps working on it; follow it with \\`fluid mist staging wait --operation ${operation.id}\\`.`,\n );\n this.name = \"StagingWaitTimeoutError\";\n this.operation = operation;\n }\n}\n\nconst UNSUPPORTED =\n \"This Fluid server doesn't support branch environments yet, so there is no staging to use. Nothing was published.\";\n\n/** Why staging can't be requested now, in words a user can act on. */\nexport function describeUnavailable(staging: StagingCapability): string {\n switch (staging.reason) {\n case \"feature_disabled\":\n return \"Branch environments aren't enabled for this company.\";\n case \"execution_unavailable\":\n return \"Staging isn't running on the server right now. Try again later.\";\n case \"hosting_unavailable\":\n return \"This mist isn't live, so it can't have a staging environment.\";\n case \"credentials_not_retired\":\n return \"This mist's production credentials still reach preview builds. A Fluid operator has to retire them before staging can be used.\";\n case \"operation_in_progress\":\n return `Staging is already being prepared (operation ${staging.active_operation_id ?? \"unknown\"}). Follow it with \\`fluid mist staging wait\\`.`;\n case \"environment_deleting\":\n return \"Staging is being torn down. Request it again once it's removed.\";\n case \"staging_exists\":\n return \"Staging already exists. It can't be requested again until it's torn down.\";\n case \"quota_exceeded\":\n return \"This company has reached its branch environment limit.\";\n default:\n return `Staging isn't available right now (${staging.reason ?? \"no reason given\"}).`;\n }\n}\n\nexport interface StagingStatus {\n capability: { version: number; staging: StagingCapability };\n environment: BranchEnvironment | null;\n activeOperation: BranchOperation | null;\n}\n\n/** Where staging stands: capability, the staging environment and any run. */\nexport async function readStagingStatus(\n client: MistClient,\n slug: string,\n): Promise<StagingStatus> {\n const listing = await listBranchEnvironments(client, slug);\n if (!listing.capability.supported) {\n throw new StagingUnavailableError(\"unsupported\", UNSUPPORTED);\n }\n const { staging } = listing.capability;\n const activeOperation = staging.active_operation_id\n ? await fetchBranchOperation(client, slug, staging.active_operation_id)\n : null;\n return {\n capability: {\n version: listing.capability.version,\n staging,\n },\n environment: listing.environments.find((env) => env.seeded) ?? null,\n activeOperation,\n };\n}\n\nexport interface StagingRequest {\n sourceRevision: string;\n /** Reuse to resume an interrupted request; a new one is made otherwise. */\n operationKey?: string;\n /** Told the key before the request is sent, so it can be retried. */\n onOperationKey?: (key: string) => void;\n}\n\n/**\n * Request staging at a revision. A key the caller passes is retried even\n * while the capability reports a run in progress, because that run may be\n * the one this key started. Throws `StagingOperationFailedError` when the\n * key returns an operation that already failed, was cancelled or went stale.\n */\nexport async function admitStaging(\n client: MistClient,\n slug: string,\n request: StagingRequest,\n): Promise<BranchOperation> {\n if (!request.operationKey) {\n const listing = await listBranchEnvironments(client, slug);\n if (!listing.capability.supported) {\n throw new StagingUnavailableError(\"unsupported\", UNSUPPORTED);\n }\n const { staging } = listing.capability;\n if (!staging.available) {\n throw new StagingUnavailableError(\n staging.reason ?? \"unavailable\",\n describeUnavailable(staging),\n staging.active_operation_id,\n );\n }\n }\n\n const operationKey = request.operationKey ?? newOperationKey();\n request.onOperationKey?.(operationKey);\n const operation = await requestStaging(client, slug, {\n sourceRevision: request.sourceRevision,\n operationKey,\n });\n // A resumed key can return an operation that already ended badly.\n if (isTerminal(operation) && operation.state !== \"succeeded\") {\n throw new StagingOperationFailedError(operation);\n }\n return operation;\n}\n\nexport interface WaitOptions {\n intervalMs: number;\n timeoutMs: number;\n /** Called whenever the state or progress changes. */\n onChange?: (operation: BranchOperation) => void;\n sleep?: (ms: number) => Promise<void>;\n now?: () => number;\n}\n\n/**\n * Poll the staging operation until it finishes. Resolves on success;\n * throws `StagingUnavailableError` for an operation that isn't a staging\n * request, `StagingOperationFailedError` on any other terminal state and\n * `StagingWaitTimeoutError` when the local wait runs out, which never\n * cancels the server operation.\n */\nexport async function waitForOperation(\n client: MistClient,\n slug: string,\n operationId: string,\n options: WaitOptions,\n): Promise<BranchOperation> {\n const sleep =\n options.sleep ??\n ((ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms)));\n const now = options.now ?? Date.now;\n const deadline = now() + options.timeoutMs;\n let lastSeen = \"\";\n\n for (;;) {\n const operation = await fetchBranchOperation(client, slug, operationId);\n if (operation.kind !== \"ensure_staging\") {\n throw new StagingUnavailableError(\n \"not_a_staging_operation\",\n `Operation ${operation.id} is a ${operation.kind} operation, not a staging request. Follow staging with \\`fluid mist staging wait\\`.`,\n );\n }\n const seen = JSON.stringify([\n operation.state,\n operation.progress,\n operation.failure,\n ]);\n if (seen !== lastSeen) {\n lastSeen = seen;\n options.onChange?.(operation);\n }\n if (isTerminal(operation)) {\n if (operation.state === \"succeeded\") return operation;\n throw new StagingOperationFailedError(operation);\n }\n if (now() >= deadline) throw new StagingWaitTimeoutError(operation);\n await sleep(options.intervalMs);\n }\n}\n\n/** One line describing an operation's progress, for stderr. */\nexport function describeProgress(operation: BranchOperation): string {\n const { progress } = operation;\n const steps = [\n `database ${progress.database_isolated ? \"isolated\" : \"pending\"}`,\n `branch ${progress.branch_published ? \"published\" : \"pending\"}`,\n `build ${progress.build_ready ? \"ready\" : \"pending\"}`,\n ].join(\", \");\n const failure = operation.failure\n ? `, last error ${operation.failure.type}`\n : \"\";\n return `${operation.state}: ${steps}${failure}`;\n}\n","/**\n * Centralized error handling — maps thrown errors to spec'd exit codes\n * and friendly messages. Imported by every command's `.action()` so each\n * file doesn't repeat the same try/catch.\n *\n * Exit codes (spec §3 \"Conventions\"):\n * 0 success | 1 user error | 2 API 4xx | 3 API 5xx / vendor | 4 network\n */\n\nimport chalk from \"chalk\";\nimport { BranchContractError } from \"../api/branch_environments.js\";\nimport { MistApiError } from \"../api/client.js\";\nimport { HostableParseError } from \"./hostable.js\";\nimport { SlugResolutionError } from \"./slug-resolver.js\";\nimport { StagingPreflightError } from \"./staging-revision.js\";\nimport {\n StagingOperationFailedError,\n StagingUnavailableError,\n StagingWaitTimeoutError,\n} from \"./staging.js\";\n\n/** Run an async action and translate failures into pretty output + exit. */\nexport async function runAction(fn: () => Promise<void> | void): Promise<void> {\n try {\n await fn();\n } catch (err) {\n handle(err);\n }\n}\n\nfunction handle(err: unknown): never {\n if (err instanceof SlugResolutionError) {\n console.error(chalk.red(err.message));\n process.exit(1);\n }\n if (err instanceof HostableParseError) {\n console.error(chalk.red(err.message));\n process.exit(1);\n }\n if (\n err instanceof StagingPreflightError ||\n err instanceof StagingUnavailableError ||\n err instanceof StagingWaitTimeoutError\n ) {\n console.error(chalk.yellow(err.message));\n process.exit(1);\n }\n if (\n err instanceof StagingOperationFailedError ||\n err instanceof BranchContractError\n ) {\n console.error(chalk.red(err.message));\n process.exit(3);\n }\n if (err instanceof MistApiError) {\n // Friendlier messages for the well-known cases from the spec.\n if (err.status === 403) {\n const action = err.details?.[\"permission\"];\n console.error(\n chalk.red(\n action\n ? `Your role doesn't include \\`mist.${String(action)}\\` — ask your Fluid admin to grant it.`\n : err.message,\n ),\n );\n } else if (err.status === 409) {\n const date = err.details?.[\"scheduled_destroy_at\"];\n console.error(\n chalk.yellow(\n typeof date === \"string\"\n ? `This mist is scheduled for destruction on ${date.slice(0, 10)}. Run \\`fluid mist restore <slug>\\` first.`\n : err.message,\n ),\n );\n } else if (err.status === 422) {\n // Hostable-related 422s come back with structured `details` per the\n // spec. Surface them as a one-line hint above the raw API message.\n const details = err.details ?? {};\n const idErrors = details[\"integratable_id\"] ?? details[\"hostable_id\"];\n const managedKey =\n typeof err.message === \"string\" &&\n /managed_by|managed by mist|FLUID_DROPLET|FLUID_WEBHOOK/i.test(\n err.message,\n );\n if (\n Array.isArray(idErrors) &&\n idErrors.some((e) => /already taken|already attached/i.test(String(e)))\n ) {\n console.error(\n chalk.yellow(\n \"That droplet is already attached to another mist. Detach it first or pick a different one.\",\n ),\n );\n } else if (managedKey) {\n console.error(\n chalk.yellow(\n \"That env var is Mist-managed (FLUID_DROPLET_UUID / FLUID_DROPLET_SECRET / FLUID_WEBHOOK_AUTH_TOKEN) — it's set automatically when a droplet is attached and can't be written directly.\",\n ),\n );\n } else if (\n typeof err.message === \"string\" &&\n /Droplet not found|integratable/i.test(err.message)\n ) {\n const id = details[\"integratable_id\"] ?? details[\"hostable_id\"];\n console.error(\n chalk.yellow(\n `Couldn't find droplet${id != null ? ` #${String(id)}` : \"\"} in this company.`,\n ),\n );\n } else {\n console.error(chalk.red(err.message));\n }\n } else if (err.status === 410) {\n console.error(\n chalk.red(\n \"Grace period expired — vendor teardown is in flight. Create a fresh mist with `fluid mist create`.\",\n ),\n );\n } else {\n console.error(chalk.red(err.message));\n }\n process.exit(err.exitCode);\n }\n // Unknown — print and exit 1 (user/programmer error)\n if (err instanceof Error) {\n console.error(chalk.red(err.message));\n } else {\n console.error(chalk.red(String(err)));\n }\n process.exit(1);\n}\n","/**\n * Output helpers — human-readable by default, `--json` emits raw API\n * payload. NO_COLOR respected via chalk's built-in detection.\n */\n\nimport chalk from \"chalk\";\nimport type { Mist } from \"../types.js\";\n\nexport function printJson(payload: unknown): void {\n process.stdout.write(JSON.stringify(payload, null, 2) + \"\\n\");\n}\n\nconst STATE_COLOR: Record<Mist[\"state\"], (s: string) => string> = {\n pending: chalk.gray,\n provisioning: chalk.yellow,\n live: chalk.green,\n failed: chalk.red,\n pending_destroy: chalk.magenta,\n archived: chalk.dim,\n};\n\nexport function colorState(state: Mist[\"state\"]): string {\n return STATE_COLOR[state](state);\n}\n\n/** Completion copy for `mist push --watch`. Deployment records expose an\n * ephemeral provider URL that may require provider SSO; only the Mist's\n * stable public URL is suitable to hand to a user. */\nexport function readyDeploymentMessage(\n publicUrl: string | null,\n slug: string,\n): string {\n return publicUrl\n ? `✔ Ready: ${chalk.cyan(publicUrl)}`\n : `✔ Deployment ready. The public URL is still provisioning; run \\`fluid mist show ${slug}\\` to check it.`;\n}\n\n/**\n * Bold display label for a mist, falling back to a dim \"(unnamed)\"\n * placeholder when both `name` and `vendorName` are missing. Never nests\n * `chalk.bold(chalk.dim(...))` — most terminals render that as plain bold,\n * losing the \"this is a placeholder\" muted intent.\n */\nexport function mistLabel(\n name: string | null | undefined,\n vendorName: string | null | undefined,\n): string {\n const raw = name ?? vendorName;\n return raw ? chalk.bold(raw) : chalk.dim(\"(unnamed)\");\n}\n\n/**\n * Pad columns to width — cheap manual table; avoids cli-table3 dep for v1.\n * Cells are defensively normalized: API records can legitimately have every\n * optional label field (`name`, `vendor_name`, `droplet.name`, ...) come back\n * null/absent at once (e.g. a `pending_destroy` mist), so callers sometimes\n * hand us `undefined`/`null` after chaining `??` — that must render as an\n * empty cell, not crash the whole table.\n */\nexport function printTable(rows: (string | null | undefined)[][]): void {\n if (rows.length === 0) return;\n const widths: number[] = [];\n for (const row of rows) {\n row.forEach((cell, i) => {\n const visible = stripAnsi(cell).length;\n widths[i] = Math.max(widths[i] ?? 0, visible);\n });\n }\n for (const row of rows) {\n const padded = row.map((cell, i) => {\n const w = widths[i] ?? 0;\n const text = cell ?? \"\";\n return text + \" \".repeat(Math.max(0, w - stripAnsi(text).length));\n });\n process.stdout.write(padded.join(\" \") + \"\\n\");\n }\n}\n\n// Chalk respects NO_COLOR, but our padding math needs the visible length —\n// strip ANSI escape sequences for measurement only. Tolerates null/undefined\n// since table cells are built from chained `??` fallbacks that can bottom out.\nfunction stripAnsi(s: string | null | undefined): string {\n // eslint-disable-next-line no-control-regex\n return (s ?? \"\").replace(/\\x1b\\[[0-9;]*m/g, \"\");\n}\n\nexport function relativeDate(iso: string | null): string {\n if (!iso) return \"—\";\n const diff = Date.now() - new Date(iso).getTime();\n const minutes = Math.floor(diff / 60_000);\n if (minutes < 1) return \"just now\";\n if (minutes < 60) return `${minutes}m ago`;\n const hours = Math.floor(minutes / 60);\n if (hours < 24) return `${hours}h ago`;\n const days = Math.floor(hours / 24);\n if (days < 30) return `${days}d ago`;\n return new Date(iso).toISOString().slice(0, 10);\n}\n\n/** Human-friendly absolute date for one-off prints (scheduled_destroy_at). */\nexport function formatAbsolute(iso: string): string {\n const d = new Date(iso);\n return d.toISOString().slice(0, 10);\n}\n","/** `fluid mist list` — GET /mists with optional filters. Spec §3. */\n\nimport chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport { createMistClient } from \"../api/client.js\";\nimport { listMists, type ListMistsParams } from \"../api/mists.js\";\nimport { runAction } from \"../lib/error-handler.js\";\nimport {\n colorState,\n printJson,\n printTable,\n relativeDate,\n} from \"../lib/output.js\";\n\ninterface Opts {\n state?: ListMistsParams[\"state\"];\n kind?: ListMistsParams[\"kind\"];\n limit?: string;\n json?: boolean;\n}\n\nexport const listCommand = new Command(\"list\")\n .description(\"List the mists you have access to\")\n .option(\n \"--state <state>\",\n \"Filter by state: live | provisioning | failed | pending_destroy | archived\",\n )\n .option(\"--kind <kind>\", \"Filter by kind (default: next_app)\")\n .option(\"--limit <n>\", \"Page size 1..100 (default 25)\")\n .option(\"--json\", \"Emit raw API JSON\")\n .action((opts: Opts) =>\n runAction(async () => {\n const client = createMistClient();\n const limit = opts.limit ? parseInt(opts.limit, 10) : undefined;\n const response = await listMists(client, {\n ...(opts.state ? { state: opts.state } : {}),\n ...(opts.kind ? { kind: opts.kind } : {}),\n ...(limit && Number.isFinite(limit) ? { limit } : {}),\n });\n if (opts.json) return printJson(response);\n const mists = response.mists ?? [];\n if (mists.length === 0) {\n console.log(chalk.dim(\"No mists found.\"));\n return;\n }\n const header = [\n chalk.bold(\"SLUG\"),\n chalk.bold(\"NAME\"),\n chalk.bold(\"STATE\"),\n chalk.bold(\"ATTACHED\"),\n chalk.bold(\"UPDATED\"),\n ];\n const rows = mists.map((m) => [\n m.slug,\n m.name ?? m.droplet?.name ?? m.vendor_name ?? chalk.dim(\"(unnamed)\"),\n colorState(m.state),\n m.hostable_type ?? chalk.dim(\"standalone\"),\n relativeDate(m.updated_at),\n ]);\n printTable([header, ...rows]);\n }),\n );\n","/**\n * Integration points — the attach mechanism that replaced hostable\n * (#19684/#19687). A droplet integration point wires a droplet to a mist;\n * creating one auto-provisions the droplet env + triggers a redeploy\n * (#19685).\n *\n * `POST/GET /api/v202604/mists/:mist_id/integration_points`\n * `DELETE …/integration_points/:id` (detach)\n *\n * The link is immutable: there is no update route and no `active` field —\n * toggling a surface is destroy + create (enablement lives on the surface\n * record itself, e.g. DropZone#active).\n */\n\nimport type { MistClient } from \"./client.js\";\nimport type {\n IntegrationPoint,\n IntegrationPointCreateInput,\n IntegrationPointKind,\n IntegrationPointShowResponse,\n IntegrationPointsListResponse,\n} from \"../types.js\";\nimport { resolveMistId } from \"./id-resolver.js\";\n\nfunction basePath(mistId: string): string {\n return `/api/v202604/mists/${mistId}/integration_points`;\n}\n\nexport async function listIntegrationPoints(\n client: MistClient,\n slug: string,\n): Promise<IntegrationPointsListResponse> {\n const id = await resolveMistId(client, slug);\n return client.get<IntegrationPointsListResponse>(basePath(id));\n}\n\nexport async function createIntegrationPoint(\n client: MistClient,\n slug: string,\n input: IntegrationPointCreateInput,\n): Promise<IntegrationPointShowResponse> {\n const id = await resolveMistId(client, slug);\n return client.post<IntegrationPointShowResponse>(basePath(id), {\n integration_point: input,\n });\n}\n\nexport async function deleteIntegrationPoint(\n client: MistClient,\n slug: string,\n integrationPointId: string,\n): Promise<void> {\n const id = await resolveMistId(client, slug);\n return client.delete<void>(`${basePath(id)}/${integrationPointId}`);\n}\n\n/**\n * First droplet-kind integration point in a list, or undefined. A droplet is\n * 1-per-mist (it provisions the managed env), so this is the idempotency key\n * for the droplet create+attach flow.\n */\nexport function findDropletIntegrationPoint(\n integrationPoints: IntegrationPoint[] | undefined | null,\n): IntegrationPoint | undefined {\n return (integrationPoints ?? []).find((ip) => ip.kind === \"droplet\");\n}\n\n/**\n * Find an integration point that already links a specific surface — matched\n * by uuid (droplet/drop-zone) or numeric id (mobile-embed), optionally scoped\n * to a kind. Used to make attach-existing idempotent (avoid the server's 422\n * \"already taken\"). Uses the `integratable_uuid` echo (#19689) when present.\n */\nexport function findIntegrationPointBySurface(\n integrationPoints: IntegrationPoint[] | undefined | null,\n target: { kind?: IntegrationPointKind; uuid?: string; id?: number },\n): IntegrationPoint | undefined {\n return (integrationPoints ?? []).find((ip) => {\n if (target.kind && ip.kind !== target.kind) return false;\n if (target.uuid && ip.integratable_uuid === target.uuid) return true;\n if (target.id != null && ip.integratable_id === target.id) return true;\n return false;\n });\n}\n","/** `fluid mist show <slug>` — single mist detail, including its attached\n * surfaces (integration points). Surfaces scheduled_destroy_at when state is\n * pending_destroy. */\n\nimport chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport { createMistClient } from \"../api/client.js\";\nimport { showMist } from \"../api/mists.js\";\nimport { listIntegrationPoints } from \"../api/integration_points.js\";\nimport { runAction } from \"../lib/error-handler.js\";\nimport { resolveSlug } from \"../lib/slug-resolver.js\";\nimport {\n colorState,\n formatAbsolute,\n mistLabel,\n printJson,\n relativeDate,\n} from \"../lib/output.js\";\nimport type { IntegrationPoint } from \"../types.js\";\n\ninterface Opts {\n json?: boolean;\n}\n\n/** Display ref for an attached surface: the uuid echo when present, else id. */\nfunction surfaceRef(ip: IntegrationPoint): string {\n if (ip.integratable_uuid) return ip.integratable_uuid;\n if (ip.integratable_id != null) return `#${ip.integratable_id}`;\n return \"(unknown)\";\n}\n\nexport const showCommand = new Command(\"show\")\n .description(\"Show one mist by slug\")\n .argument(\"[slug]\", \"The mist slug (defaults to .mist file in CWD)\")\n .option(\"--json\", \"Emit raw API JSON\")\n .action((slug: string | undefined, opts: Opts) =>\n runAction(async () => {\n const resolved = resolveSlug(slug);\n const client = createMistClient();\n const { mist } = await showMist(client, resolved);\n // Attached surfaces live on integration points now (hostable is retired).\n const integrationPoints = (await listIntegrationPoints(client, resolved))\n .integration_points;\n\n if (opts.json)\n return printJson({ mist, integration_points: integrationPoints });\n\n const lines: string[] = [];\n const label = mistLabel(mist.name, mist.vendor_name);\n lines.push(`${label} ${chalk.dim(mist.slug)}`);\n if (mist.vendor_name && mist.name && mist.name !== mist.vendor_name) {\n lines.push(` vendor ${chalk.dim(mist.vendor_name)}`);\n }\n lines.push(` state ${colorState(mist.state)}`);\n if (mist.kind) lines.push(` kind ${mist.kind}`);\n if (mist.public_url)\n lines.push(` url ${chalk.cyan(mist.public_url)}`);\n if (integrationPoints.length > 0) {\n lines.push(` surfaces ${integrationPoints.length}`);\n for (const ip of integrationPoints) {\n lines.push(\n ` ${chalk.dim(\"·\")} ${ip.kind} ${surfaceRef(ip)} ${chalk.dim(`integration ${ip.id}`)}`,\n );\n }\n }\n if (mist.provisioned_at)\n lines.push(` live ${relativeDate(mist.provisioned_at)}`);\n lines.push(` updated ${relativeDate(mist.updated_at)}`);\n if (mist.scheduled_destroy_at) {\n lines.push(\n chalk.magenta(\n ` destroy scheduled ${formatAbsolute(mist.scheduled_destroy_at)} (run \\`fluid mist restore ${mist.slug}\\` to undo)`,\n ),\n );\n }\n console.log(lines.join(\"\\n\"));\n }),\n );\n","/**\n * Spinner-backed `StepReporter` for interactive human runs. Split from\n * `step-reporter.ts` so tests can use the recording reporter without pulling\n * in `ora`'s ESM-only dependency chain.\n */\n\nimport ora, { type Ora } from \"ora\";\nimport type { StepReporter } from \"./step-reporter.js\";\n\nexport function createOraReporter(): StepReporter {\n const spinner: Ora = ora();\n return {\n start(text) {\n spinner.start(text);\n },\n update(text) {\n spinner.text = text;\n },\n succeed(text) {\n spinner.succeed(text);\n },\n skip(text) {\n // ora has no native \"skip\" — use the info glyph.\n spinner.info(text);\n },\n info(text) {\n // Persist an info line without killing the running spinner; the next\n // start() re-arms it.\n spinner.stopAndPersist({ symbol: \"•\", text });\n },\n warn(text) {\n spinner.warn(text);\n },\n fail(text) {\n spinner.fail(text);\n },\n };\n}\n","/**\n * Step reporter — the single seam the droplet provisioning engine uses to\n * surface progress. Human runs get `ora` spinners; `--json` runs get a\n * silent reporter (the command prints the final JSON); tests get a\n * recording reporter they can assert step ordering against.\n *\n * Keeping the engine reporter-agnostic is what lets the flow be exposed two\n * ways (`create --droplet` and `attach-droplet`) from one implementation.\n *\n * NOTE: this module deliberately imports nothing runtime-heavy (no `ora`) so\n * tests can pull in the recording reporter without loading the spinner's\n * ESM-only dependency chain. The spinner reporter lives in `ora-reporter.ts`.\n */\n\nexport interface StepReporter {\n /** Begin a step (starts/re-labels the spinner). */\n start(text: string): void;\n /** Update the in-progress step's label without ending it. */\n update(text: string): void;\n /** End the current step as succeeded. */\n succeed(text?: string): void;\n /** End the current step as skipped (already done — idempotent path). */\n skip(text: string): void;\n /** Informational note (does not end the step). */\n info(text: string): void;\n /** Non-fatal warning (ends the current step, flow continues). */\n warn(text: string): void;\n /** End the current step as failed. */\n fail(text?: string): void;\n}\n\n/** No-op reporter for `--json` mode (final payload printed by the command). */\nexport function createSilentReporter(): StepReporter {\n return {\n start() {},\n update() {},\n succeed() {},\n skip() {},\n info() {},\n warn() {},\n fail() {},\n };\n}\n\nexport interface RecordedStep {\n kind: \"start\" | \"update\" | \"succeed\" | \"skip\" | \"info\" | \"warn\" | \"fail\";\n text: string;\n}\n\nexport interface RecordingReporter extends StepReporter {\n readonly steps: RecordedStep[];\n}\n\n/** In-memory reporter for tests: records every call in order. */\nexport function createRecordingReporter(): RecordingReporter {\n const steps: RecordedStep[] = [];\n const push = (kind: RecordedStep[\"kind\"]) => (text?: string) => {\n steps.push({ kind, text: text ?? \"\" });\n };\n return {\n steps,\n start: push(\"start\"),\n update: push(\"update\"),\n succeed: push(\"succeed\"),\n skip: push(\"skip\"),\n info: push(\"info\"),\n warn: push(\"warn\"),\n fail: push(\"fail\"),\n };\n}\n","export interface DropletLifecycleUrls {\n installWebhookUrl: string;\n uninstallWebhookUrl: string;\n}\n\nexport function normalizeLifecycleOrigin(value: string): string {\n let url: URL;\n try {\n url = new URL(value);\n } catch {\n throw new Error(\"Lifecycle origin must be an absolute HTTP(S) URL\");\n }\n if (url.protocol !== \"http:\" && url.protocol !== \"https:\") {\n throw new Error(\"Lifecycle origin must be an absolute HTTP(S) URL\");\n }\n url.pathname = url.pathname.replace(/\\/+$/, \"\");\n url.search = \"\";\n url.hash = \"\";\n return url.toString().replace(/\\/$/, \"\");\n}\n\nexport function lifecycleUrls(\n publicUrl: string,\n paths: { installPath?: string; uninstallPath?: string } = {},\n): DropletLifecycleUrls {\n const origin = normalizeLifecycleOrigin(publicUrl);\n return {\n installWebhookUrl: `${origin}${normalizePath(paths.installPath ?? \"/api/webhooks/installed\")}`,\n uninstallWebhookUrl: `${origin}${normalizePath(paths.uninstallPath ?? \"/api/webhooks/uninstalled\")}`,\n };\n}\n\nexport function lifecycleUrlsMatch(\n actual: {\n installWebhookUrl?: string | null;\n uninstallWebhookUrl?: string | null;\n },\n expected: DropletLifecycleUrls,\n): boolean {\n return (\n normalizeComparableUrl(actual.installWebhookUrl) ===\n normalizeComparableUrl(expected.installWebhookUrl) &&\n normalizeComparableUrl(actual.uninstallWebhookUrl) ===\n normalizeComparableUrl(expected.uninstallWebhookUrl)\n );\n}\n\nfunction normalizePath(value: string): string {\n return `/${value.replace(/^\\/+/, \"\")}`;\n}\n\nfunction normalizeComparableUrl(\n value: string | null | undefined,\n): string | null {\n if (!value) return null;\n return normalizeLifecycleOrigin(value);\n}\n","/**\n * Droplet CRUD over `/api/droplets`. Thin wrapper — the provisioning\n * orchestration (idempotency, embed-url readiness, attach) lives in\n * `lib/droplet-provision.ts`.\n *\n * Note: droplets are a company-level resource, so unlike the mist\n * sub-resources these paths take the droplet `uuid` (drp_…) directly and\n * don't go through `resolveMistId`.\n */\n\nimport { lifecycleUrls } from \"@fluid-app/mist-core/lifecycle-urls\";\nimport type { MistClient } from \"./client.js\";\nimport type {\n DropletCreateInput,\n DropletCreateRequest,\n DropletCreateResponse,\n DropletRecord,\n DropletShowResponse,\n DropletsListResponse,\n DropletUpdateInput,\n} from \"../types.js\";\n\nconst BASE = \"/api/droplets\";\n\n/**\n * Full scope set from `Droplet::Installation::VALID_SCOPES`. We request\n * them all by default so a Mist-created droplet's installations carry write\n * access out of the box — matching what Mist Desktop sends. Callers can\n * narrow this via the flow's `requestedScopes` option.\n */\nexport const DEFAULT_DROPLET_SCOPES: readonly string[] = [\n \"main\",\n \"prospects\",\n \"members\",\n \"web\",\n \"settings\",\n \"products\",\n \"orders\",\n \"users\",\n \"website\",\n \"header_menus\",\n \"payments\",\n];\n\n/** Lifecycle webhook paths a Mist Next app serves (mirrors Mist Desktop).\n * `embedUrl` must be an absolute HTTP(S) URL so malformed deployment metadata\n * fails before a Droplet is created with unusable callback URLs. */\nexport function dropletWebhookUrls(embedUrl: string): {\n install_webhook_url: string;\n uninstall_webhook_url: string;\n} {\n const urls = lifecycleUrls(embedUrl);\n return {\n install_webhook_url: urls.installWebhookUrl,\n uninstall_webhook_url: urls.uninstallWebhookUrl,\n };\n}\n\n/**\n * Build a droplet create body from a name + (optional) embed URL. When the\n * embed URL is known we also derive the lifecycle webhook URLs from it.\n */\nexport function buildDropletCreateInput(opts: {\n name: string;\n embedUrl?: string | null;\n requestedScopes?: readonly string[];\n /** Ask Fluid for egress through its egress proxy (e.g. to reach Exigo);\n * a company admin's request admits the droplet. */\n egressProxy?: boolean;\n}): DropletCreateInput {\n const scopes = [...(opts.requestedScopes ?? DEFAULT_DROPLET_SCOPES)];\n const summary = \"Created by Fluid Mist\";\n const input: DropletCreateInput = {\n name: opts.name,\n requested_scopes: scopes,\n settings: {\n marketplace_page: { summary },\n details_page: { summary },\n },\n };\n if (opts.embedUrl) {\n input.embed_url = opts.embedUrl;\n Object.assign(input, dropletWebhookUrls(opts.embedUrl));\n }\n // Sent only when asked for: the API defaults it to off.\n if (opts.egressProxy) input.egress_proxy_requested = true;\n return input;\n}\n\nexport function createDroplet(\n client: MistClient,\n body: DropletCreateRequest,\n): Promise<DropletCreateResponse> {\n return client.post<DropletCreateResponse>(BASE, body);\n}\n\nexport function getDroplet(\n client: MistClient,\n uuid: string,\n): Promise<DropletShowResponse> {\n return client.get<DropletShowResponse>(`${BASE}/${encodeURIComponent(uuid)}`);\n}\n\n/** Page size + runaway guard for walking the page-paginated droplets list. */\nconst PER_PAGE = 100;\nconst MAX_PAGES = 25;\n\n/**\n * Recover a droplet's drp_ uuid from its numeric id by paging the company's\n * droplets list (`GET /api/droplets?page=&per_page=` — the list rows carry\n * `id`, unlike the create/show responses). Needed for legacy integration\n * points that only echo `integratable_id`: the show endpoint resolves by\n * uuid, so a numeric id can't be fetched directly. Returns null when no\n * droplet matches within the page cap.\n */\nexport async function findDropletUuidByNumericId(\n client: MistClient,\n id: number,\n): Promise<string | null> {\n for (let page = 1; page <= MAX_PAGES; page++) {\n const resp = await client.get<DropletsListResponse>(\n `${BASE}?page=${page}&per_page=${PER_PAGE}`,\n );\n const batch = resp.droplets ?? [];\n const match = batch.find((d) => d.id === id);\n if (match) return match.uuid ?? null;\n const totalPages = resp.meta?.pagination?.total_pages;\n const done =\n totalPages != null ? page >= totalPages : batch.length < PER_PAGE;\n if (done) break;\n }\n return null;\n}\n\n/**\n * Recover a Droplet created by an interrupted surface-provisioning run. The\n * canonical Mist embed URL is stable and unique to that deployment, so a\n * matching unlinked Droplet can be attached instead of creating a duplicate.\n */\nexport async function findDropletByEmbedUrl(\n client: MistClient,\n embedUrl: string,\n): Promise<DropletRecord | null> {\n const expectedUrl = embedUrl.replace(/\\/$/, \"\");\n for (let page = 1; page <= MAX_PAGES; page++) {\n const resp = await client.get<DropletsListResponse>(\n `${BASE}?page=${page}&per_page=${PER_PAGE}`,\n );\n const batch = resp.droplets ?? [];\n const match = batch.find(\n (droplet) => droplet.embed_url?.replace(/\\/$/, \"\") === expectedUrl,\n );\n if (match) return match;\n const totalPages = resp.meta?.pagination?.total_pages;\n const done =\n totalPages != null ? page >= totalPages : batch.length < PER_PAGE;\n if (done) break;\n }\n return null;\n}\n\n/** PATCH droplet fields, e.g. the embed URL once known or the egress proxy switch. */\nexport function updateDroplet(\n client: MistClient,\n uuid: string,\n input: DropletUpdateInput,\n): Promise<DropletShowResponse> {\n return client.patch<DropletShowResponse>(\n `${BASE}/${encodeURIComponent(uuid)}`,\n { droplet: input },\n );\n}\n\n/** Convenience: set embed URL + derived webhook URLs in one PATCH. */\nexport function setDropletEmbedUrl(\n client: MistClient,\n uuid: string,\n embedUrl: string,\n): Promise<DropletShowResponse> {\n return updateDroplet(client, uuid, {\n embed_url: embedUrl,\n ...dropletWebhookUrls(embedUrl),\n });\n}\n","/**\n * Droplet installations over `/api/droplet_installations`.\n *\n * Creating a Droplet::Application + linking it as an integration point does\n * NOT connect it — the droplet stays not-connected/inactive for the company,\n * so install-scoped features (dit/dex token exchange via\n * `/api/droplet_installations/exchange`, admin embed rendering, install\n * webhooks) silently don't work. A POST here is what actually installs it.\n *\n * Request shape mirrors Mist Desktop's `installDroplet` in\n * `apps/mist-desktop/src/main/services/mist-integration-points-api.service.ts`.\n * Like droplets, installations are a company-level resource — no mist id.\n */\n\nimport type { MistClient } from \"./client.js\";\nimport type {\n DropletInstallation,\n DropletInstallationCreateRequest,\n DropletInstallationCreateResponse,\n DropletInstallationsListResponse,\n} from \"../types.js\";\n\nconst BASE = \"/api/droplet_installations\";\n\n/** Page size + runaway guard for walking the page-paginated list. */\nconst PER_PAGE = 100;\nconst MAX_PAGES = 25;\n\n/**\n * List ALL of the current company's droplet installations, following the\n * endpoint's page-number pagination (`?page=&per_page=` — same shape as\n * fluid-admin's `getCompanyDroplets`). A single-page fetch would miss rows\n * beyond the first page, making the idempotency check (and the duplicate-\n * rejection confirmation) report a false \"not installed\" forever.\n */\nexport async function listAllDropletInstallations(\n client: MistClient,\n): Promise<DropletInstallation[]> {\n const rows: DropletInstallation[] = [];\n for (let page = 1; page <= MAX_PAGES; page++) {\n const resp = await client.get<DropletInstallationsListResponse>(\n `${BASE}?page=${page}&per_page=${PER_PAGE}`,\n );\n const batch = resp.droplet_installations ?? [];\n rows.push(...batch);\n const totalPages = resp.meta?.pagination?.total_pages;\n const done =\n totalPages != null ? page >= totalPages : batch.length < PER_PAGE;\n if (done) break;\n }\n return rows;\n}\n\n/** Install a droplet (by drp_ uuid) for the current company. */\nexport function createDropletInstallation(\n client: MistClient,\n input: DropletInstallationCreateRequest,\n): Promise<DropletInstallationCreateResponse> {\n return client.post<DropletInstallationCreateResponse>(BASE, input);\n}\n\n/**\n * The company's installation row for a droplet drp_ uuid, or undefined.\n * The idempotency key for the install step: an existing row means the\n * droplet is already connected (installed via a previous run, Mist Desktop,\n * or the admin marketplace).\n */\nexport function findInstallationForDroplet(\n installations: DropletInstallation[] | undefined | null,\n dropletUuid: string,\n): DropletInstallation | undefined {\n return (installations ?? []).find((i) => i.droplet_uuid === dropletUuid);\n}\n","/**\n * Drop-zone surface CRUD over `/api/drop_zones`. A drop zone injects the\n * Mist into a storefront slot; attach it to a mist by its `dz_` uuid via an\n * integration point.\n */\n\nimport type { MistClient } from \"./client.js\";\nimport type {\n DropZoneCreateInput,\n DropZoneCreateRequest,\n DropZoneCreateResponse,\n} from \"../types.js\";\n\nconst BASE = \"/api/drop_zones\";\n\n/**\n * Build a drop-zone create body. The slot id is `page__zone` (same split the\n * storefront preview uses, e.g. `checkout__below_total`) → `settings.page` +\n * `settings.zone`; height (px) → `settings.height`. Without a slot the zone\n * never renders, so the backend needs it to know WHERE to inject.\n */\nexport function buildDropZoneCreateInput(opts: {\n name: string;\n embedUrl?: string | null;\n zone?: string;\n heightPx?: number;\n}): DropZoneCreateInput {\n const [page, zone] = (opts.zone ?? \"\").split(\"__\");\n const settings: NonNullable<DropZoneCreateInput[\"settings\"]> = {};\n if (page) settings.page = page;\n if (zone) settings.zone = zone;\n if (opts.heightPx != null) settings.height = opts.heightPx;\n\n const input: DropZoneCreateInput = { name: opts.name };\n if (opts.embedUrl) input.embed_url = opts.embedUrl;\n if (Object.keys(settings).length > 0) input.settings = settings;\n return input;\n}\n\nexport function createDropZone(\n client: MistClient,\n body: DropZoneCreateRequest,\n): Promise<DropZoneCreateResponse> {\n return client.post<DropZoneCreateResponse>(BASE, body);\n}\n","/**\n * Mobile-embed surface CRUD over `/api/company/mobile_widgets`. A mobile\n * widget embeds the Mist in the mobile app; attach it to a mist by its\n * NUMERIC id (mobile widgets have no uuid — the integration point attaches\n * by `integratable_id`, unlike droplet/drop-zone which attach by uuid).\n */\n\nimport type { MistClient } from \"./client.js\";\nimport type {\n MobileWidgetCreateInput,\n MobileWidgetCreateRequest,\n MobileWidgetCreateResponse,\n} from \"../types.js\";\n\nconst BASE = \"/api/company/mobile_widgets\";\n\n/** Fluid-hosted placeholder cover image (the API requires one). */\nexport const DEFAULT_MOBILE_COVER_IMAGE =\n \"https://ik.imagekit.io/fluid/s3/app/image-placeholder.svg\";\n\n/** Default mobile embed height (px). Mirrors the admin Mobile builder. */\nexport const DEFAULT_MOBILE_HEIGHT = 600;\n\n/**\n * Build a mobile-widget create body. `cover_image_url` and `height` are\n * REQUIRED by the API (422 without them); we fall back to the Fluid\n * placeholder + default height when the caller doesn't supply them.\n */\nexport function buildMobileWidgetCreateInput(opts: {\n name: string;\n embedUrl: string;\n coverImageUrl?: string;\n heightPx?: number;\n}): MobileWidgetCreateInput {\n return {\n name: opts.name,\n embed_url: opts.embedUrl,\n cover_image_url: opts.coverImageUrl || DEFAULT_MOBILE_COVER_IMAGE,\n height: opts.heightPx ?? DEFAULT_MOBILE_HEIGHT,\n scrollable: true,\n };\n}\n\nexport function createMobileWidget(\n client: MistClient,\n body: MobileWidgetCreateRequest,\n): Promise<MobileWidgetCreateResponse> {\n return client.post<MobileWidgetCreateResponse>(BASE, body);\n}\n","/** Env-var sub-resource. Spec §4 + §3 \"Env vars\". */\n\nimport type { MistClient } from \"./client.js\";\nimport type { EnvVar, EnvVarsListResponse } from \"../types.js\";\nimport { resolveMistId } from \"./id-resolver.js\";\n\nfunction path(id: string, key?: string) {\n return key\n ? `/api/v202604/mists/${id}/env_vars/${encodeURIComponent(key)}`\n : `/api/v202604/mists/${id}/env_vars`;\n}\n\nexport async function listEnvVars(\n client: MistClient,\n slug: string,\n): Promise<EnvVarsListResponse> {\n const id = await resolveMistId(client, slug);\n return client.get<EnvVarsListResponse>(path(id));\n}\n\ninterface UpsertResponse {\n env_var: EnvVar;\n meta: unknown;\n}\n\n/** Create a new env var (the spec routes `set` to POST for new keys). */\nexport async function createEnvVar(\n client: MistClient,\n slug: string,\n key: string,\n value: string,\n targets?: EnvVar[\"targets\"],\n): Promise<UpsertResponse> {\n const id = await resolveMistId(client, slug);\n return client.post<UpsertResponse>(path(id), {\n env_var: { key, value, targets },\n });\n}\n\n/** Update an existing env var (the spec routes `set` to PATCH for existing keys). */\nexport async function updateEnvVar(\n client: MistClient,\n slug: string,\n key: string,\n value: string,\n targets?: EnvVar[\"targets\"],\n): Promise<UpsertResponse> {\n const id = await resolveMistId(client, slug);\n return client.patch<UpsertResponse>(path(id, key), {\n env_var: { value, targets },\n });\n}\n\nexport async function deleteEnvVar(\n client: MistClient,\n slug: string,\n key: string,\n): Promise<void> {\n const id = await resolveMistId(client, slug);\n return client.delete<void>(path(id, key));\n}\n","/**\n * Wire types for the Mist API. Hand-rolled until\n * `docs/openapi/storefront-v202604.yaml` lands in the Fluid repo and we can\n * generate them — at which point this file gets swapped for the generated\n * version (the field names are the same, so consumers don't change).\n *\n * Source of truth: `features/mist/cli-spec.md` § 4.\n */\n\nexport type MistKind = \"next_app\";\n\nexport type MistState =\n | \"pending\"\n | \"provisioning\"\n | \"live\"\n | \"failed\"\n | \"pending_destroy\"\n | \"archived\";\n\n/**\n * Canonical hostable type strings as used on the wire. These match the Rails\n * model class names exactly — the API rejects anything else with a 400.\n *\n * Source: fluid `features/mist/cli-spec.md` § \"What changed on the Rails side\"\n * (PR #18678).\n */\nexport type HostableType = \"Droplet::Application\" | \"MobileWidget\" | \"DropZone\";\n\nexport interface Droplet {\n id: number;\n name: string;\n uuid: string;\n}\n\n/**\n * Managed env keys the backend writes onto a Mist once a droplet is\n * attached (backfill + redeploy). The CLI VERIFIES these appear with\n * `managed_by: \"mist\"` — it never sets them (the API 422s on write).\n *\n * Source: settled backend contract (#19685 auto-provisions the droplet\n * env on integration-point create).\n */\nexport const MANAGED_DROPLET_ENV_KEYS = [\n \"FLUID_DROPLET_UUID\",\n \"FLUID_DROPLET_SECRET\",\n \"FLUID_WEBHOOK_AUTH_TOKEN\",\n] as const;\n\nexport type ManagedDropletEnvKey = (typeof MANAGED_DROPLET_ENV_KEYS)[number];\n\nexport interface Mist {\n id: string;\n slug: string;\n /** Optional display label set via `--name`; falls back to `hostable.name`\n * server-side when blank. */\n name: string | null;\n state: MistState;\n kind: MistKind;\n /**\n * @deprecated Hostable is retired (#19684/#19687). Droplet attach now\n * lives on integration points. These fields remain optional so reads of\n * older serializer payloads don't break, but nothing writes them anymore.\n */\n hostable_type?: HostableType | null;\n /** @deprecated see `hostable_type`. */\n hostable_id?: number | null;\n /**\n * Legacy vendor-assigned identifier. Observed as `null` on every mist in\n * production today — treat as optional and fall back to `name`/`slug`,\n * never assume it's present.\n */\n vendor_name: string | null;\n public_url: string | null;\n /** ISO-8601 when state === \"pending_destroy\"; null otherwise. */\n scheduled_destroy_at: string | null;\n droplet: Droplet | null;\n provisioned_at: string | null;\n created_at: string;\n updated_at: string;\n}\n\nexport interface PaginationCursor {\n cursor: string | null;\n limit: number;\n prev_cursor: string | null;\n next_cursor: string | null;\n}\n\nexport interface ApiMeta {\n request_id: string;\n timestamp: string;\n pagination?: PaginationCursor;\n}\n\nexport interface ApiCollection<TKey extends string, TItem> {\n meta: ApiMeta;\n status: number;\n}\n\nexport type MistListResponse = {\n mists: Mist[];\n meta: ApiMeta;\n status: number;\n};\n\nexport type MistShowResponse = {\n mist: Mist;\n meta: ApiMeta;\n status: number;\n};\n\nexport interface MistCreateRequest {\n mist: {\n name?: string;\n kind?: MistKind;\n };\n}\n\n/**\n * `PATCH /api/v202604/mists/:id` body. Rename only — attach/detach moved\n * off the Mist record onto integration points (#19684/#19687), so hostable\n * fields no longer belong here.\n */\nexport interface MistUpdateRequest {\n mist: {\n name?: string;\n kind?: MistKind;\n };\n}\n\n// Droplets — `POST /api/droplets`, `PATCH /api/droplets/:uuid`.\n\nexport interface DropletCreateInput {\n name: string;\n /** The Mist's `public_url`. Optional so we can create before first deploy\n * finishes and PATCH the embed URL in once it's ready. */\n embed_url?: string;\n install_webhook_url?: string;\n uninstall_webhook_url?: string;\n requested_scopes?: string[];\n /** Ask Fluid to let the droplet's installations reach external systems\n * (e.g. Exigo) through Fluid's egress proxy. The request alone admits\n * the droplet; a root admin's grant is only a record. Omitted means not\n * asked. */\n egress_proxy_requested?: boolean;\n settings?: Record<string, unknown>;\n}\n\nexport interface DropletUpdateInput {\n embed_url?: string;\n install_webhook_url?: string;\n uninstall_webhook_url?: string;\n /** Ask for (true) or withdraw (false) egress through the proxy. Only a\n * Fluid root admin sets the grant itself. */\n egress_proxy_requested?: boolean;\n}\n\nexport interface DropletCreateRequest {\n droplet: DropletCreateInput;\n}\n\nexport interface DropletUpdateRequest {\n droplet: DropletUpdateInput;\n}\n\nexport interface DropletRecord {\n /**\n * Numeric id is NOT returned by `POST /api/droplets` — attach by `uuid`\n * instead (see `IntegrationPointCreateInput.integratable_uuid`). The\n * `GET /api/droplets` list rows DO carry it, which is the only way to\n * map a legacy integration point's `integratable_id` back to a uuid.\n */\n id?: number;\n uuid: string;\n name: string;\n embed_url?: string | null;\n install_webhook_url?: string | null;\n uninstall_webhook_url?: string | null;\n /** The owner's egress request and the Fluid root-admin grant. Both are\n * absent when the caller is not a company admin, and from APIs that\n * predate them. */\n egress_proxy_requested?: boolean;\n egress_proxy_enabled?: boolean;\n}\n\nexport interface DropletCreateResponse {\n droplet: DropletRecord;\n /** Returned exactly once on create. We do NOT forward it — the backend\n * reads it server-side during env backfill. */\n webhook_secret?: string;\n meta?: ApiMeta;\n status?: number;\n}\n\nexport interface DropletShowResponse {\n droplet: DropletRecord;\n meta?: ApiMeta;\n status?: number;\n}\n\n/**\n * Page-number pagination used by the company-level droplet + installation\n * list endpoints (`?page=&per_page=`) — unlike the mist sub-resources,\n * these do NOT use cursor pagination. Shape mirrors fluid-admin's\n * `networking/droplets.api.ts` response schemas.\n */\nexport interface PagePaginationMeta {\n request_id?: string;\n timestamp?: string;\n pagination?: {\n current_page?: number;\n total_pages?: number;\n total_count?: number;\n per_page?: number;\n };\n}\n\n/** `GET /api/droplets` — the company's droplets, page-paginated. The list\n * rows carry the numeric `id` (unlike `POST /api/droplets`), which is how\n * a legacy integration point's `integratable_id` maps back to a uuid. */\nexport interface DropletsListResponse {\n droplets?: DropletRecord[];\n meta?: PagePaginationMeta;\n status?: number;\n}\n\n// Droplet installations — `GET/POST /api/droplet_installations`.\n// Creating a Droplet::Application + linking it via an integration point does\n// NOT connect it for the company — a separate installation is what activates\n// install-scoped features (dit/dex token exchange, admin embed rendering,\n// install webhooks). Shape mirrors Mist Desktop's\n// mist-integration-points-api.service.ts `installDroplet`.\n\nexport interface DropletInstallation {\n /** The installation's own dri_ uuid (needed to uninstall later). */\n uuid?: string;\n /** The installed droplet's drp_ uuid. */\n droplet_uuid?: string;\n active?: boolean;\n /** Read by the reinstall plan to report what the backend preserves. */\n scopes?: string[];\n role_id?: string | number | null;\n}\n\nexport interface DropletInstallationsListResponse {\n droplet_installations?: DropletInstallation[];\n meta?: PagePaginationMeta;\n status?: number;\n}\n\nexport interface DropletInstallationCreateRequest {\n droplet_uuid: string;\n scopes?: string[];\n}\n\nexport interface DropletInstallationCreateResponse {\n droplet_installation?: DropletInstallation;\n meta?: ApiMeta;\n status?: number;\n}\n\n// Integration points — the attach mechanism that replaced hostable.\n// `POST/GET /api/v202604/mists/:mist_id/integration_points`.\n\n/** Hyphenated wire kinds the integration-points API speaks. */\nexport type IntegrationPointKind = \"droplet\" | \"drop-zone\" | \"mobile-embed\";\n\nexport interface IntegrationPoint {\n /** The integration point's own id — a uuid string on the wire. */\n id: string;\n kind: IntegrationPointKind | string;\n /** Numeric id of the attached resource (server-resolved; present in reads). */\n integratable_id?: number;\n /**\n * Public uuid of the attached surface (drp_/dz_ for droplets/drop-zones;\n * a synthetic uuid for mobile embeds). Echoed on every row since #19689 —\n * the read path uses it for display + resolution.\n */\n integratable_uuid?: string | null;\n integratable_type?: string | null;\n mist_id?: string;\n}\n\n/**\n * Attach input. Name the surface by EXACTLY ONE identifier:\n * - droplet + drop-zone attach by `integratable_uuid` (their records carry a\n * drp_/dz_ uuid and no readable numeric id).\n * - mobile-embed attaches by `integratable_id` (a mobile widget has a numeric\n * id and no uuid — `integratable_uuid` 404s for it).\n * The API rejects neither/both with a 400. The link is immutable (no update\n * route, no `active`): toggling is destroy + create.\n */\nexport interface IntegrationPointCreateInput {\n kind: IntegrationPointKind;\n integratable_uuid?: string;\n integratable_id?: number;\n}\n\nexport interface IntegrationPointCreateRequest {\n integration_point: IntegrationPointCreateInput;\n}\n\nexport interface IntegrationPointsListResponse {\n integration_points: IntegrationPoint[];\n meta: ApiMeta;\n status: number;\n}\n\nexport interface IntegrationPointShowResponse {\n integration_point: IntegrationPoint;\n meta: ApiMeta;\n status: number;\n}\n\n// Drop zones — `POST /api/drop_zones`. Attach by the dz_ uuid.\n\nexport interface DropZoneCreateInput {\n name: string;\n embed_url?: string;\n /** Slot placement: { page, zone, height }. Without it the zone won't render. */\n settings?: { page?: string; zone?: string; height?: number };\n}\n\nexport interface DropZoneCreateRequest {\n drop_zone: DropZoneCreateInput;\n}\n\nexport interface DropZoneRecord {\n uuid: string;\n name: string;\n embed_url?: string | null;\n active?: boolean;\n settings?: Record<string, unknown>;\n}\n\nexport interface DropZoneCreateResponse {\n drop_zone: DropZoneRecord;\n meta?: ApiMeta;\n status?: number;\n}\n\n// Mobile embeds — `POST /api/company/mobile_widgets`. Attach by numeric id.\n\nexport interface MobileWidgetCreateInput {\n name: string;\n embed_url: string;\n /** REQUIRED by the API (422 without it). */\n cover_image_url: string;\n /** REQUIRED by the API (422 without it). */\n height: number;\n scrollable?: boolean;\n}\n\nexport interface MobileWidgetCreateRequest {\n mobile_widget: MobileWidgetCreateInput;\n}\n\nexport interface MobileWidgetRecord {\n id: number;\n name: string;\n embed_url?: string | null;\n cover_image_url?: string | null;\n height?: number;\n}\n\nexport interface MobileWidgetCreateResponse {\n mobile_widget: MobileWidgetRecord;\n meta?: ApiMeta;\n status?: number;\n}\n\nexport interface EnvVar {\n key: string;\n value: string;\n targets: (\"production\" | \"preview\" | \"development\")[];\n managed_by: \"user\" | \"mist\";\n}\n\nexport interface EnvVarsListResponse {\n env_vars: EnvVar[];\n meta: ApiMeta;\n status: number;\n}\n\nexport interface GitCredential {\n remote_url: string;\n expires_at: string;\n}\n\nexport interface GitCredentialResponse {\n git_credential: GitCredential;\n meta: ApiMeta;\n status: number;\n}\n\nexport type DeploymentState =\n | \"ready\"\n | \"building\"\n | \"error\"\n | \"canceled\"\n | \"queued\";\n\nexport interface Deployment {\n id: string;\n state: DeploymentState;\n url: string | null;\n commit_sha: string | null;\n commit_message: string | null;\n created_at: string;\n target: \"production\" | \"preview\";\n}\n\nexport interface DeploymentsListResponse {\n deployments: Deployment[];\n meta: ApiMeta;\n status: number;\n}\n\nexport interface DeploymentShowResponse {\n deployment: Deployment;\n meta: ApiMeta;\n status: number;\n}\n\nexport interface LogEntry {\n timestamp: string;\n level: \"stdout\" | \"stderr\";\n message: string;\n source: string;\n}\n\nexport interface LogsResponse {\n deployment_id: string;\n logs: LogEntry[];\n meta: ApiMeta;\n status: number;\n}\n\n/** The `.mist` file the CLI writes after `fluid mist clone <slug>`. */\nexport interface MistFile {\n slug: string;\n /**\n * Legacy vendor-assigned identifier. Observed as `null` on every mist in\n * production today — treat as optional and fall back to `name`/`slug`,\n * never assume it's present.\n */\n vendor_name: string | null;\n public_url: string | null;\n}\n","/**\n * Pure planning for the Droplet lifecycle repair: given a Mist's public URL,\n * the app's lifecycle webhook paths and the Droplet's current URLs, compute\n * the canonical embed/install/uninstall URLs and the repair summary.\n *\n * Used by the `fluid mist droplet repair` and `reinstall` CLI commands, which\n * Mist's agent runs through run_cli. Transport (auth, fetch, recorded\n * history) stays with the caller.\n */\n\nexport interface DropletLifecyclePaths {\n installPath: string;\n uninstallPath: string;\n}\n\nexport const DEFAULT_DROPLET_LIFECYCLE_PATHS: Readonly<DropletLifecyclePaths> =\n Object.freeze({\n installPath: \"/api/webhooks/installed\",\n uninstallPath: \"/api/webhooks/uninstalled\",\n });\n\n/** The note every repair summary carries: the repair fixes future delivery,\n * it does not replay `droplet.installed` for an existing installation. */\nexport const LIFECYCLE_REPAIR_NOTE =\n \"This repairs future Droplet install/uninstall lifecycle delivery. It does not replay droplet.installed for an already-installed Droplet.\";\n\n/** Strip trailing slashes; null for an absent or empty value. */\nexport function normalizePublicUrl(\n value: string | null | undefined,\n): string | null {\n if (!value) return null;\n return value.replace(/\\/+$/, \"\");\n}\n\n/** A blank or missing path falls back; a relative one gains a leading `/`. */\nexport function normalizeWebhookPath(value: unknown, fallback: string): string {\n if (typeof value !== \"string\") return fallback;\n const trimmed = value.trim();\n if (!trimmed) return fallback;\n return trimmed.startsWith(\"/\") ? trimmed : `/${trimmed}`;\n}\n\n/**\n * Lifecycle paths from a parsed `fluid.capabilities.json`\n * (`dropletLifecycle.installPath` / `uninstallPath`). Anything that is not\n * the expected shape yields the defaults.\n */\nexport function dropletLifecyclePathsFromCapabilities(\n capabilities: unknown,\n): DropletLifecyclePaths {\n if (!isRecord(capabilities)) return { ...DEFAULT_DROPLET_LIFECYCLE_PATHS };\n const lifecycle = capabilities[\"dropletLifecycle\"];\n const source = isRecord(lifecycle) ? lifecycle : {};\n return {\n installPath: normalizeWebhookPath(\n source[\"installPath\"],\n DEFAULT_DROPLET_LIFECYCLE_PATHS.installPath,\n ),\n uninstallPath: normalizeWebhookPath(\n source[\"uninstallPath\"],\n DEFAULT_DROPLET_LIFECYCLE_PATHS.uninstallPath,\n ),\n };\n}\n\nexport interface CanonicalDropletUrls {\n embedUrl: string;\n installWebhookUrl: string;\n uninstallWebhookUrl: string;\n}\n\n/** The canonical Droplet URLs for a Mist served at `publicUrl`. */\nexport function canonicalDropletUrls(\n publicUrl: string,\n paths: DropletLifecyclePaths = DEFAULT_DROPLET_LIFECYCLE_PATHS,\n): CanonicalDropletUrls {\n const embedUrl = normalizePublicUrl(publicUrl);\n if (!embedUrl) throw new Error(\"publicUrl is required\");\n return {\n embedUrl,\n installWebhookUrl: `${embedUrl}${paths.installPath}`,\n uninstallWebhookUrl: `${embedUrl}${paths.uninstallPath}`,\n };\n}\n\n/** The Droplet fields the repair reads and writes. */\nexport interface DropletUrlFields {\n embed_url?: string | null;\n install_webhook_url?: string | null;\n uninstall_webhook_url?: string | null;\n}\n\n/** Exact match on all three fields — the repair's no-op condition and its\n * post-PATCH verification. */\nexport function dropletUrlsMatch(\n droplet: DropletUrlFields | null | undefined,\n canonical: CanonicalDropletUrls,\n): boolean {\n return (\n droplet?.embed_url === canonical.embedUrl &&\n droplet.install_webhook_url === canonical.installWebhookUrl &&\n droplet.uninstall_webhook_url === canonical.uninstallWebhookUrl\n );\n}\n\n/** The `PATCH /api/droplets/:uuid` body. Only these three fields change. */\nexport function dropletRepairPatch(canonical: CanonicalDropletUrls): {\n droplet: Required<DropletUrlFields>;\n} {\n return {\n droplet: {\n embed_url: canonical.embedUrl,\n install_webhook_url: canonical.installWebhookUrl,\n uninstall_webhook_url: canonical.uninstallWebhookUrl,\n },\n };\n}\n\nexport type LifecycleRepairStatus = \"unchanged\" | \"planned\" | \"updated\";\n\nexport interface LifecycleRepairSummary extends CanonicalDropletUrls {\n status: LifecycleRepairStatus;\n mistId: string;\n mistSlug: string | null;\n dropletUuid: string;\n previous: {\n embedUrl: string | null;\n installWebhookUrl: string | null;\n uninstallWebhookUrl: string | null;\n };\n note: string;\n}\n\n/**\n * The repair summary before any mutation. `alreadyCorrect` means the caller\n * must not PATCH; otherwise a dry run reports \"planned\" and a live run\n * reports \"updated\" (callers overwrite it after verification).\n */\nexport function planLifecycleRepair(input: {\n mistId: string;\n mistSlug: string | null;\n dropletUuid: string;\n canonical: CanonicalDropletUrls;\n current: DropletUrlFields | null | undefined;\n dryRun: boolean;\n}): { alreadyCorrect: boolean; summary: LifecycleRepairSummary } {\n const alreadyCorrect = dropletUrlsMatch(input.current, input.canonical);\n return {\n alreadyCorrect,\n summary: {\n status: alreadyCorrect\n ? \"unchanged\"\n : input.dryRun\n ? \"planned\"\n : \"updated\",\n mistId: input.mistId,\n mistSlug: input.mistSlug,\n dropletUuid: input.dropletUuid,\n embedUrl: input.canonical.embedUrl,\n installWebhookUrl: input.canonical.installWebhookUrl,\n uninstallWebhookUrl: input.canonical.uninstallWebhookUrl,\n previous: {\n embedUrl: input.current?.embed_url ?? null,\n installWebhookUrl: input.current?.install_webhook_url ?? null,\n uninstallWebhookUrl: input.current?.uninstall_webhook_url ?? null,\n },\n note: LIFECYCLE_REPAIR_NOTE,\n },\n };\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n","export const lifecycleOperationStates = [\n \"queued\",\n \"running\",\n \"succeeded\",\n \"failed\",\n \"requires_action\",\n] as const;\n\nexport type LifecycleOperationState = (typeof lifecycleOperationStates)[number];\n\nexport const lifecycleOperationKinds = [\"replacement\"] as const;\n\nexport type LifecycleOperationKind = (typeof lifecycleOperationKinds)[number];\n\nexport const connectionStates = [\n \"pending\",\n \"connected\",\n \"degraded\",\n \"requires_action\",\n] as const;\n\nexport type ConnectionState = (typeof connectionStates)[number];\n\nexport interface LifecycleOperation {\n id: string;\n kind: LifecycleOperationKind;\n state: LifecycleOperationState;\n stage: string;\n mist_id: string;\n droplet_uuid?: string | null;\n installation_uuid?: string | null;\n connection_state?: ConnectionState | null;\n retry_after_seconds?: number | null;\n error_code?: string | null;\n recovery_action?: string | null;\n created_at: string;\n updated_at: string;\n terminal_at?: string | null;\n}\n\nexport interface LifecycleOperationResponse {\n replacement_operation?: LifecycleOperation;\n}\n\nexport function isTerminalOperation(operation: LifecycleOperation): boolean {\n return (\n operation.state === \"succeeded\" ||\n operation.state === \"failed\" ||\n operation.state === \"requires_action\"\n );\n}\n","import type { LifecycleOperation } from \"./lifecycle-types\";\nimport type { LifecyclePollResult } from \"./operation-polling\";\n\nexport type LifecycleRecovery =\n | { status: \"complete\"; action: null }\n | { status: \"retryable\"; action: string }\n | { status: \"requires_action\"; action: string }\n | { status: \"failed\"; action: string };\n\nexport function classifyLifecycleRecovery(\n result: LifecyclePollResult,\n): LifecycleRecovery {\n if (result.status === \"not_found\") {\n return { status: \"requires_action\", action: \"rediscover_operation\" };\n }\n if (result.status === \"timed_out\") {\n return { status: \"retryable\", action: \"resume_status_polling\" };\n }\n\n return classifyTerminalOperation(result.operation);\n}\n\nfunction classifyTerminalOperation(\n operation: LifecycleOperation,\n): LifecycleRecovery {\n if (operation.state === \"succeeded\") {\n return { status: \"complete\", action: null };\n }\n if (operation.state === \"failed\") {\n return {\n status: \"failed\",\n action: operation.recovery_action ?? \"verify_lifecycle_destination\",\n };\n }\n return {\n status: \"requires_action\",\n action: operation.recovery_action ?? \"reconcile_lifecycle\",\n };\n}\n","/**\n * Pure rules for the irreversible Droplet repair-and-reinstall flow: the\n * replacement-operation endpoints, response parsing, the request guards, the\n * installation choice, the plan and the final watch summary.\n *\n * Used by the `fluid mist droplet reinstall` CLI command, which Mist's agent\n * runs through run_cli (Mist adds its own plan + approval gate on top; see\n * apps/mist-desktop/src/main/tools/fluid-cli-policy.ts). The caller owns\n * transport and the wording of its refusals.\n */\n\nimport {\n connectionStates,\n lifecycleOperationKinds,\n lifecycleOperationStates,\n type LifecycleOperation,\n} from \"./lifecycle-types\";\nimport type { LifecyclePollResult } from \"./operation-polling\";\nimport {\n classifyLifecycleRecovery,\n type LifecycleRecovery,\n} from \"./recovery-classification\";\n\n/** How long one call watches a replacement before handing back a resumable\n * \"still_running\" result. The operation is durable and backend-owned, so\n * watching is resumable at no cost. */\nexport const REPLACEMENT_POLL_BUDGET_MS = 120_000;\n\nexport const REPLACEMENT_IDEMPOTENCY_SCOPE =\n \"single replacement attempt; a later approved retry receives a new key\";\n\nexport const REPLACEMENT_OWNERSHIP_NOTE =\n \"Fluid owns revocation, uninstall delivery, terminalization, replacement creation, and retry recovery as one durable operation.\";\n\nexport function replacementOperationsPath(\n mistId: string,\n installationUuid: string,\n): string {\n return `/api/v202604/mists/${encodeURIComponent(mistId)}/droplet_installations/${encodeURIComponent(installationUuid)}/replacement_operations`;\n}\n\nexport function replacementOperationStatusPath(\n mistId: string,\n installationUuid: string,\n operationId: string,\n): string {\n return `${replacementOperationsPath(mistId, installationUuid)}/${encodeURIComponent(operationId)}`;\n}\n\n/** The operation id a 409 \"active_operation_exists\" response points at. */\nexport function parseActiveOperationId(value: unknown): string | null {\n if (!isRecord(value)) return null;\n const error = value[\"error\"];\n if (!isRecord(error)) return null;\n const details = error[\"details\"];\n if (!isRecord(details)) return null;\n const operationId = details[\"operation_id\"];\n return typeof operationId === \"string\" ? operationId : null;\n}\n\n/** Seconds from a `Retry-After` header; undefined when absent or invalid. */\nexport function parseRetryAfterHeader(\n value: string | null | undefined,\n): number | undefined {\n if (!value) return undefined;\n const seconds = Number(value);\n return Number.isFinite(seconds) && seconds >= 0 ? seconds : undefined;\n}\n\n/** Parse and validate `{ replacement_operation: {...} }`. Throws on a\n * malformed body rather than trusting a partial operation. */\nexport function parseReplacementOperation(value: unknown): LifecycleOperation {\n const key = \"replacement_operation\";\n if (!isRecord(value)) throw new Error(\"Lifecycle response must be an object\");\n const candidate = value[key];\n if (!isRecord(candidate))\n throw new Error(`Lifecycle response is missing ${key}`);\n const id = candidate[\"id\"];\n const kind = candidate[\"kind\"];\n const state = candidate[\"state\"];\n const stage = candidate[\"stage\"];\n const mistId = candidate[\"mist_id\"];\n const createdAt = candidate[\"created_at\"];\n const updatedAt = candidate[\"updated_at\"];\n if (\n typeof id !== \"string\" ||\n !isLifecycleOperationKind(kind) ||\n !isLifecycleOperationState(state) ||\n typeof stage !== \"string\" ||\n typeof mistId !== \"string\" ||\n typeof createdAt !== \"string\" ||\n typeof updatedAt !== \"string\"\n ) {\n throw new Error(\"Lifecycle operation response is malformed\");\n }\n\n const dropletUuid = candidate[\"droplet_uuid\"];\n const installationUuid = candidate[\"installation_uuid\"];\n const connectionState = candidate[\"connection_state\"];\n const errorCode = candidate[\"error_code\"];\n const recoveryAction = candidate[\"recovery_action\"];\n const terminalAt = candidate[\"terminal_at\"];\n const retryAfterSeconds = candidate[\"retry_after_seconds\"];\n if (\n !isOptionalNullableString(dropletUuid) ||\n !isOptionalNullableString(installationUuid) ||\n !isOptionalConnectionState(connectionState) ||\n !isOptionalNullableString(errorCode) ||\n !isOptionalNullableString(recoveryAction) ||\n !isOptionalNullableString(terminalAt) ||\n !isOptionalNullableNumber(retryAfterSeconds)\n ) {\n throw new Error(\n \"Lifecycle operation response has malformed optional fields\",\n );\n }\n\n return {\n id,\n kind,\n state,\n stage,\n mist_id: mistId,\n created_at: createdAt,\n updated_at: updatedAt,\n ...(dropletUuid !== undefined ? { droplet_uuid: dropletUuid } : {}),\n ...(installationUuid !== undefined\n ? { installation_uuid: installationUuid }\n : {}),\n ...(connectionState !== undefined\n ? { connection_state: connectionState }\n : {}),\n ...(errorCode !== undefined ? { error_code: errorCode } : {}),\n ...(recoveryAction !== undefined\n ? { recovery_action: recoveryAction }\n : {}),\n ...(terminalAt !== undefined ? { terminal_at: terminalAt } : {}),\n ...(retryAfterSeconds !== undefined\n ? { retry_after_seconds: retryAfterSeconds }\n : {}),\n };\n}\n\nexport interface ReplacementRequestArgs {\n dryRun?: boolean;\n confirmDestructiveReinstall?: boolean;\n installationUuid?: string;\n resumeOperationId?: string;\n}\n\n/**\n * Guard the request before any network call. A resume must name the\n * installation it started on: status is scoped to the SOURCE installation,\n * the backend terminalizes that source mid-flight, and polling whichever\n * installation is attached now would report a false \"not_found\".\n */\nexport function checkReplacementRequest(\n args: ReplacementRequestArgs,\n):\n | { ok: true; resumeOperationId: string | null }\n | { ok: false; code: \"resume_requires_installation\" } {\n const resumeOperationId = args.resumeOperationId?.trim() || null;\n if (resumeOperationId && !args.installationUuid) {\n return { ok: false, code: \"resume_requires_installation\" };\n }\n return { ok: true, resumeOperationId };\n}\n\n/**\n * Whether the lifecycle URL repair that precedes a replacement must stay\n * read-only. It mutates only on a confirmed start: never on a dry run,\n * never without confirmation, and never on a resume (the call that started\n * the operation already repaired the URLs).\n */\nexport function lifecycleRepairIsReadOnly(\n args: ReplacementRequestArgs,\n resumeOperationId: string | null,\n): boolean {\n return (\n args.dryRun === true ||\n resumeOperationId !== null ||\n args.confirmDestructiveReinstall !== true\n );\n}\n\n/** Starting a new replacement needs explicit confirmation; resuming only\n * reads an operation the user already approved. */\nexport function replacementStartNeedsConfirmation(\n args: ReplacementRequestArgs,\n resumeOperationId: string | null,\n): boolean {\n return !resumeOperationId && args.confirmDestructiveReinstall !== true;\n}\n\nexport interface ReplacementInstallation {\n uuid?: string;\n droplet_uuid?: string;\n active?: boolean;\n scopes?: unknown;\n role_id?: unknown;\n}\n\n/**\n * The installation to replace. An explicitly named `installationUuid`\n * always wins: it is the recovery path for a source that a failed attempt\n * already terminalized (which may sit beside a newer active installation\n * of the same Droplet), and a resume must watch the installation its\n * operation started on, never a rediscovered one. Without a name, the\n * company's active installation of the attached Droplet is used.\n */\nexport function selectReplacementInstallation(input: {\n installations: readonly ReplacementInstallation[];\n dropletUuid: string;\n installationUuid?: string;\n resumeOperationId: string | null;\n}): ReplacementInstallation | undefined {\n if (input.installationUuid) {\n // Keep the listed record (scopes, role) when the named one is active.\n const listed = input.installations.find(\n (candidate) =>\n candidate.uuid === input.installationUuid &&\n candidate.droplet_uuid === input.dropletUuid,\n );\n return (\n listed ?? {\n uuid: input.installationUuid,\n droplet_uuid: input.dropletUuid,\n }\n );\n }\n if (input.resumeOperationId) return undefined;\n return input.installations.find(\n (candidate) => candidate.droplet_uuid === input.dropletUuid,\n );\n}\n\nexport interface ReplacementPlan<TRepair = unknown> {\n status: \"planned\" | \"watching\" | \"pending\";\n mistId: string;\n dropletUuid: string;\n installationUuid: string;\n lifecycleRepair: TRepair;\n replacementPath: string;\n idempotencyKey: string | null;\n idempotencyScope: string;\n irreversible: true;\n preservedByBackend: { scopes: boolean; role_id: boolean };\n note: string;\n}\n\nexport function buildReplacementPlan<TRepair>(input: {\n dryRun: boolean;\n resumeOperationId: string | null;\n mistId: string;\n dropletUuid: string;\n installation: ReplacementInstallation & { uuid: string };\n lifecycleRepair: TRepair;\n idempotencyKey: string;\n}): ReplacementPlan<TRepair> {\n const { installation } = input;\n return {\n status: input.dryRun\n ? \"planned\"\n : input.resumeOperationId\n ? \"watching\"\n : \"pending\",\n mistId: input.mistId,\n dropletUuid: input.dropletUuid,\n installationUuid: installation.uuid,\n lifecycleRepair: input.lifecycleRepair,\n replacementPath: replacementOperationsPath(input.mistId, installation.uuid),\n // A resume starts no new attempt, so it mints no attempt key.\n idempotencyKey: input.resumeOperationId ? null : input.idempotencyKey,\n idempotencyScope: REPLACEMENT_IDEMPOTENCY_SCOPE,\n irreversible: true,\n preservedByBackend: {\n scopes: Array.isArray(installation.scopes),\n role_id:\n typeof installation.role_id === \"string\" ||\n typeof installation.role_id === \"number\",\n },\n note: REPLACEMENT_OWNERSHIP_NOTE,\n };\n}\n\n/**\n * The final result of one watch. A timed-out poll is this caller's watch\n * budget expiring, NOT the backend giving up: it reports \"still_running\"\n * with the ids needed to resume, and is not an error. Only \"succeeded\" is\n * complete.\n */\nexport function summarizeReplacementWatch<TPlan extends object>(input: {\n plan: TPlan;\n pollResult: LifecyclePollResult;\n startedOperation: LifecycleOperation | null;\n operationId: string;\n installationUuid: string;\n watchedForMs: number;\n nextStep: (context: {\n operationId: string;\n installationUuid: string;\n stage: string;\n }) => string;\n}): {\n result: TPlan & {\n status: string;\n replacementOperation: LifecycleOperation | null;\n recovery: LifecycleRecovery;\n watchedForMs?: number;\n resumeOperationId?: string;\n nextStep?: string;\n };\n isError: boolean;\n} {\n const { pollResult, startedOperation } = input;\n const recovery = classifyLifecycleRecovery(pollResult);\n const stillRunning = pollResult.status === \"timed_out\";\n const latestOperation =\n pollResult.status === \"terminal\"\n ? pollResult.operation\n : pollResult.status === \"timed_out\"\n ? (pollResult.operation ?? startedOperation)\n : startedOperation;\n\n return {\n result: {\n ...input.plan,\n status:\n pollResult.status === \"terminal\"\n ? pollResult.operation.state\n : stillRunning\n ? \"still_running\"\n : pollResult.status,\n replacementOperation: latestOperation,\n recovery,\n ...(stillRunning\n ? {\n watchedForMs: input.watchedForMs,\n resumeOperationId: input.operationId,\n nextStep: input.nextStep({\n operationId: input.operationId,\n installationUuid: input.installationUuid,\n stage: latestOperation?.stage ?? \"unknown\",\n }),\n }\n : {}),\n },\n isError: !stillRunning && recovery.status !== \"complete\",\n };\n}\n\nfunction isLifecycleOperationKind(\n value: unknown,\n): value is LifecycleOperation[\"kind\"] {\n return lifecycleOperationKinds.some((candidate) => candidate === value);\n}\n\nfunction isLifecycleOperationState(\n value: unknown,\n): value is LifecycleOperation[\"state\"] {\n return lifecycleOperationStates.some((candidate) => candidate === value);\n}\n\nfunction isOptionalNullableString(\n value: unknown,\n): value is string | null | undefined {\n return value === undefined || value === null || typeof value === \"string\";\n}\n\nfunction isOptionalNullableNumber(\n value: unknown,\n): value is number | null | undefined {\n return value === undefined || value === null || typeof value === \"number\";\n}\n\nfunction isOptionalConnectionState(\n value: unknown,\n): value is LifecycleOperation[\"connection_state\"] {\n return (\n value === undefined ||\n value === null ||\n connectionStates.some((candidate) => candidate === value)\n );\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n","import {\n isTerminalOperation,\n type LifecycleOperation,\n} from \"./lifecycle-types\";\n\nexport interface LifecycleStatusResponse {\n status: number;\n operation?: LifecycleOperation;\n retryAfterSeconds?: number;\n}\n\nexport type LifecyclePollResult =\n | { status: \"terminal\"; operation: LifecycleOperation }\n | { status: \"not_found\" }\n | { status: \"timed_out\"; operation?: LifecycleOperation };\n\nexport interface LifecyclePollingOptions {\n fetchStatus: () => Promise<LifecycleStatusResponse>;\n timeoutMs?: number;\n minimumIntervalMs?: number;\n sleep?: (durationMs: number) => Promise<void>;\n now?: () => number;\n abortSignal?: AbortSignal;\n}\n\nexport async function pollLifecycleOperation({\n fetchStatus,\n timeoutMs = 30 * 60 * 1_000,\n minimumIntervalMs = 2_000,\n sleep: sleepOverride,\n now = Date.now,\n abortSignal,\n}: LifecyclePollingOptions): Promise<LifecyclePollResult> {\n const sleep =\n sleepOverride ??\n ((durationMs: number) => defaultSleep(durationMs, abortSignal));\n const startedAt = now();\n let latestOperation: LifecycleOperation | undefined;\n\n while (now() - startedAt < timeoutMs) {\n abortSignal?.throwIfAborted();\n const response = await fetchStatus();\n abortSignal?.throwIfAborted();\n if (response.status === 404) return { status: \"not_found\" };\n const transientServerError =\n response.status >= 500 && response.status < 600;\n if (\n response.status !== 200 &&\n response.status !== 429 &&\n !transientServerError\n ) {\n throw new Error(\n `Lifecycle status request failed with HTTP ${response.status}`,\n );\n }\n\n if (response.operation) {\n latestOperation = response.operation;\n if (isTerminalOperation(response.operation)) {\n return { status: \"terminal\", operation: response.operation };\n }\n }\n\n const retryAfterMs = Math.max(\n minimumIntervalMs,\n (response.retryAfterSeconds ??\n response.operation?.retry_after_seconds ??\n 0) * 1_000,\n );\n await sleep(\n Math.min(retryAfterMs, Math.max(0, timeoutMs - (now() - startedAt))),\n );\n abortSignal?.throwIfAborted();\n }\n\n return latestOperation\n ? { status: \"timed_out\", operation: latestOperation }\n : { status: \"timed_out\" };\n}\n\nfunction defaultSleep(\n durationMs: number,\n abortSignal?: AbortSignal,\n): Promise<void> {\n return new Promise((resolve, reject) => {\n // The signal belongs to the CALLER and outlives this sleep — for the\n // droplet repair path it is the AI turn's controller. `{ once: true }`\n // does not clean up here: it removes a listener only after the event\n // FIRES, and on the normal path abort never fires. Without an explicit\n // removal each poll iteration retained a listener, its closure and the\n // Timeout object for the life of the signal — up to timeoutMs /\n // minimumIntervalMs (30min / 2s = 900) per invocation. Node emits no\n // MaxListenersExceededWarning for AbortSignal, so nothing surfaced it.\n const onAbort = (): void => {\n clearTimeout(timer);\n reject(abortSignal?.reason);\n };\n const timer = setTimeout(() => {\n abortSignal?.removeEventListener(\"abort\", onAbort);\n resolve();\n }, durationMs);\n abortSignal?.addEventListener(\"abort\", onAbort, { once: true });\n });\n}\n","/**\n * Backend-owned Droplet replacement operations:\n *\n * POST /api/v202604/mists/:mist_id/droplet_installations/:installation_uuid/replacement_operations\n * GET …/replacement_operations/:id\n *\n * One POST starts a durable operation in which Fluid revokes, delivers the\n * uninstall, terminalizes the source installation and creates the\n * replacement. The CLI never DELETEs and re-POSTs an installation itself.\n *\n * Mirrors Mist Desktop's `droplet-lifecycle-api.service.ts`; the paths,\n * response parsing and polling come from `@fluid-app/mist-core`. These go\n * through `client.request` (raw fetch) because the flow reads the HTTP\n * status and `Retry-After` header, not just a parsed body.\n */\n\nimport type { LifecycleOperation } from \"@fluid-app/mist-core/lifecycle-types\";\nimport {\n parseActiveOperationId,\n parseReplacementOperation,\n parseRetryAfterHeader,\n replacementOperationStatusPath,\n replacementOperationsPath,\n} from \"@fluid-app/mist-core/droplet-replacement\";\nimport {\n pollLifecycleOperation,\n type LifecyclePollResult,\n} from \"@fluid-app/mist-core/operation-polling\";\nimport { MistApiError, type MistClient } from \"./client.js\";\n\nexport async function startDropletReplacement(\n client: MistClient,\n input: { mistId: string; installationUuid: string; idempotencyKey: string },\n): Promise<LifecycleOperation> {\n const response = await send(\n client,\n replacementOperationsPath(input.mistId, input.installationUuid),\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ idempotency_key: input.idempotencyKey }),\n },\n );\n const body = await readJson(response);\n if (response.ok) return parseReplacementOperation(body);\n\n // An equivalent replacement is already running: watch that one instead\n // of failing (the backend allows one active operation per installation).\n const activeOperationId =\n response.status === 409 ? parseActiveOperationId(body) : null;\n if (activeOperationId) {\n return fetchDropletReplacement(client, {\n mistId: input.mistId,\n installationUuid: input.installationUuid,\n operationId: activeOperationId,\n });\n }\n\n const retryAfterSeconds = parseRetryAfterHeader(\n response.headers.get(\"Retry-After\"),\n );\n const retryHint =\n retryAfterSeconds === undefined\n ? \"\"\n : `; retry after ${retryAfterSeconds}s`;\n throw new MistApiError(\n `Droplet replacement start failed with HTTP ${response.status}${retryHint}`,\n response.status,\n );\n}\n\nexport async function fetchDropletReplacement(\n client: MistClient,\n input: { mistId: string; installationUuid: string; operationId: string },\n): Promise<LifecycleOperation> {\n const response = await send(\n client,\n replacementOperationStatusPath(\n input.mistId,\n input.installationUuid,\n input.operationId,\n ),\n { method: \"GET\" },\n );\n if (!response.ok) {\n throw new MistApiError(\n `Active replacement status failed with HTTP ${response.status}`,\n response.status,\n );\n }\n return parseReplacementOperation(await readJson(response));\n}\n\n/** Poll an operation until it is terminal, gone, or `timeoutMs` elapses. */\nexport function pollDropletReplacement(\n client: MistClient,\n input: {\n mistId: string;\n installationUuid: string;\n operationId: string;\n timeoutMs: number;\n sleep?: (durationMs: number) => Promise<void>;\n },\n): Promise<LifecyclePollResult> {\n const statusPath = replacementOperationStatusPath(\n input.mistId,\n input.installationUuid,\n input.operationId,\n );\n return pollLifecycleOperation({\n timeoutMs: input.timeoutMs,\n ...(input.sleep ? { sleep: input.sleep } : {}),\n fetchStatus: async () => {\n const response = await send(client, statusPath, { method: \"GET\" });\n const retryAfterSeconds = parseRetryAfterHeader(\n response.headers.get(\"Retry-After\"),\n );\n if (response.status === 404 || response.status === 429) {\n return { status: response.status, retryAfterSeconds };\n }\n if (!response.ok) return { status: response.status };\n return {\n status: response.status,\n operation: parseReplacementOperation(await readJson(response)),\n retryAfterSeconds,\n };\n },\n });\n}\n\nasync function send(\n client: MistClient,\n path: string,\n init: RequestInit,\n): Promise<Response> {\n try {\n return await client.request(path, init);\n } catch (err) {\n throw new MistApiError(\n `Network error: ${err instanceof Error ? err.message : String(err)}`,\n 0,\n );\n }\n}\n\nasync function readJson(response: Response): Promise<unknown> {\n const text = await response.text();\n if (!text) return null;\n try {\n return JSON.parse(text) as unknown;\n } catch {\n return null;\n }\n}\n","/**\n * Plan receipts for `fluid mist droplet reinstall`.\n *\n * Mist Desktop's agent must show a dry-run plan and get explicit approval\n * before it may confirm the irreversible replacement. A CLI has no\n * conversation to hold that sequence, so `--dry-run` records a receipt of\n * the exact Droplet, installation and lifecycle URLs it showed, and\n * `--confirm` refuses unless a receipt for the same plan is younger than\n * {@link REINSTALL_PLAN_TTL_MS}. `--confirm` claims the receipt atomically\n * (a rename only one process can win) before checking it, so one plan\n * approves at most one attempt and every retry needs a fresh plan.\n *\n * Receipts live under the Fluid config dir (`~/.fluid/mist/reinstall-plans`)\n * and hold only ids and a timestamp — no tokens or secrets.\n */\n\nimport { randomUUID } from \"node:crypto\";\nimport {\n mkdirSync,\n readFileSync,\n renameSync,\n rmSync,\n writeFileSync,\n} from \"node:fs\";\nimport { join } from \"node:path\";\nimport { getConfigDir } from \"@fluid-app/fluid-cli\";\n\n/** How long a shown plan stays valid for `--confirm`. */\nexport const REINSTALL_PLAN_TTL_MS = 15 * 60 * 1_000;\n\nexport interface ReinstallPlanReceipt {\n mistId: string;\n dropletUuid: string;\n installationUuid: string;\n /** The lifecycle URLs the plan showed; `--confirm` will PATCH these. */\n embedUrl: string;\n installWebhookUrl: string;\n uninstallWebhookUrl: string;\n /** ISO-8601 time the plan was shown. */\n plannedAt: string;\n}\n\nexport interface ReinstallPlanStore {\n save(receipt: ReinstallPlanReceipt): void;\n load(mistId: string): ReinstallPlanReceipt | null;\n /** Atomically remove and return the receipt; null when none (or when a\n * concurrent claim won). */\n claim(mistId: string): ReinstallPlanReceipt | null;\n}\n\nexport function createFileReinstallPlanStore(\n dir: string = join(getConfigDir(), \"mist\", \"reinstall-plans\"),\n): ReinstallPlanStore {\n const fileFor = (mistId: string) =>\n join(dir, `${encodeURIComponent(mistId)}.json`);\n return {\n save(receipt) {\n mkdirSync(dir, { recursive: true, mode: 0o700 });\n writeFileSync(\n fileFor(receipt.mistId),\n JSON.stringify(receipt, null, 2) + \"\\n\",\n { encoding: \"utf8\", mode: 0o600 },\n );\n },\n load(mistId) {\n try {\n const parsed = JSON.parse(\n readFileSync(fileFor(mistId), \"utf8\"),\n ) as unknown;\n return isReceipt(parsed) && parsed.mistId === mistId ? parsed : null;\n } catch {\n return null;\n }\n },\n claim(mistId) {\n const claimed = `${fileFor(mistId)}.claimed-${process.pid}-${randomUUID()}`;\n try {\n renameSync(fileFor(mistId), claimed);\n } catch {\n return null;\n }\n try {\n const parsed = JSON.parse(readFileSync(claimed, \"utf8\")) as unknown;\n return isReceipt(parsed) && parsed.mistId === mistId ? parsed : null;\n } catch {\n return null;\n } finally {\n rmSync(claimed, { force: true });\n }\n },\n };\n}\n\nexport type ReceiptCheck =\n | { ok: true }\n | { ok: false; code: \"plan_required\" | \"plan_stale\" | \"plan_changed\" };\n\n/** Whether `receipt` approves executing the freshly computed plan. */\nexport function checkPlanReceipt(\n receipt: ReinstallPlanReceipt | null,\n fresh: Omit<ReinstallPlanReceipt, \"mistId\" | \"plannedAt\">,\n now: number,\n): ReceiptCheck {\n if (!receipt) return { ok: false, code: \"plan_required\" };\n const plannedAt = Date.parse(receipt.plannedAt);\n if (!Number.isFinite(plannedAt) || now - plannedAt > REINSTALL_PLAN_TTL_MS) {\n return { ok: false, code: \"plan_stale\" };\n }\n if (plannedAt > now) return { ok: false, code: \"plan_stale\" };\n if (\n receipt.dropletUuid !== fresh.dropletUuid ||\n receipt.installationUuid !== fresh.installationUuid ||\n receipt.embedUrl !== fresh.embedUrl ||\n receipt.installWebhookUrl !== fresh.installWebhookUrl ||\n receipt.uninstallWebhookUrl !== fresh.uninstallWebhookUrl\n ) {\n return { ok: false, code: \"plan_changed\" };\n }\n return { ok: true };\n}\n\nfunction isReceipt(value: unknown): value is ReinstallPlanReceipt {\n if (typeof value !== \"object\" || value === null) return false;\n const record = value as Record<string, unknown>;\n return (\n typeof record[\"mistId\"] === \"string\" &&\n typeof record[\"dropletUuid\"] === \"string\" &&\n typeof record[\"installationUuid\"] === \"string\" &&\n typeof record[\"embedUrl\"] === \"string\" &&\n typeof record[\"installWebhookUrl\"] === \"string\" &&\n typeof record[\"uninstallWebhookUrl\"] === \"string\" &&\n typeof record[\"plannedAt\"] === \"string\"\n );\n}\n","/**\n * Orchestration for `fluid mist droplet repair` and\n * `fluid mist droplet reinstall`, ported from Mist Desktop's\n * `repair_droplet_lifecycle_webhooks` and `repair_and_reinstall_droplet`\n * tools (`apps/mist-desktop/src/main/tools/index.ts` in the fluid repo).\n *\n * The planning and safety rules are shared through `@fluid-app/mist-core`\n * (`droplet-lifecycle-repair`, `droplet-replacement`); this module only\n * wires them to the CLI's API client, the `.mist` marker and the plan\n * receipt store. Every function takes its client and collaborators as\n * arguments so tests drive it with fakes.\n */\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport {\n canonicalDropletUrls,\n dropletLifecyclePathsFromCapabilities,\n dropletRepairPatch,\n dropletUrlsMatch,\n normalizePublicUrl,\n planLifecycleRepair,\n DEFAULT_DROPLET_LIFECYCLE_PATHS,\n type CanonicalDropletUrls,\n type DropletLifecyclePaths,\n type LifecycleRepairSummary,\n} from \"@fluid-app/mist-core/droplet-lifecycle-repair\";\nimport {\n buildReplacementPlan,\n checkReplacementRequest,\n replacementStartNeedsConfirmation,\n selectReplacementInstallation,\n summarizeReplacementWatch,\n REPLACEMENT_POLL_BUDGET_MS,\n} from \"@fluid-app/mist-core/droplet-replacement\";\nimport type { LifecycleOperation } from \"@fluid-app/mist-core/lifecycle-types\";\nimport { MistApiError, type MistClient } from \"../api/client.js\";\nimport {\n findDropletByEmbedUrl,\n findDropletUuidByNumericId,\n getDroplet,\n} from \"../api/droplets.js\";\nimport { listAllDropletInstallations } from \"../api/droplet_installations.js\";\nimport {\n pollDropletReplacement,\n startDropletReplacement,\n} from \"../api/droplet_replacements.js\";\nimport { resolveMistId } from \"../api/id-resolver.js\";\nimport {\n findDropletIntegrationPoint,\n listIntegrationPoints,\n} from \"../api/integration_points.js\";\nimport { showMist } from \"../api/mists.js\";\nimport type { DropletRecord, Mist } from \"../types.js\";\nimport {\n checkPlanReceipt,\n REINSTALL_PLAN_TTL_MS,\n type ReinstallPlanStore,\n} from \"./reinstall-plan-store.js\";\nimport { readMistFileFrom, resolveSlug } from \"./slug-resolver.js\";\n\n/** A refusal: the command stops before (or instead of) mutating anything\n * and exits non-zero. `code` is stable for scripts. */\nexport class DropletRepairRefusal extends Error {\n readonly code: string;\n constructor(code: string, message: string) {\n super(message);\n this.name = \"DropletRepairRefusal\";\n this.code = code;\n }\n}\n\nexport interface RepairTarget {\n mistId: string;\n mistSlug: string;\n dropletUuid: string;\n canonical: CanonicalDropletUrls;\n /** Where the install/uninstall paths came from. Only the `.mist` project\n * in the current directory can supply a `fluid.capabilities.json`. */\n lifecyclePathsSource: \"fluid.capabilities.json\" | \"defaults\";\n}\n\n/**\n * Resolve the Mist (slug argument or the `.mist` in `cwd`), its public URL,\n * the Droplet it is attached to, and the canonical Droplet URLs. Refuses\n * when the Mist has no public URL or no attached Droplet.\n */\nexport async function resolveRepairTarget(\n client: MistClient,\n input: { slug?: string; cwd: string },\n): Promise<RepairTarget> {\n const slug = resolveSlug(input.slug, input.cwd);\n const marker = readMistFileFrom(input.cwd);\n // The `.mist` here describes the project only when no other slug was\n // named; its capabilities must not leak onto another Mist.\n const isCwdProject = marker !== null && marker.slug === slug;\n\n const mistId = await resolveMistId(client, slug);\n const { mist } = await showMist(client, mistId);\n // Only the server's public_url is authoritative for a Droplet PATCH; the\n // local `.mist` marker's copy is never used to choose the target host.\n const publicUrl = normalizePublicUrl(mist.public_url);\n if (!publicUrl) {\n throw new DropletRepairRefusal(\n \"no_public_url\",\n \"Mist has no public_url yet. Publish the Mist first (`fluid mist push`), then repair lifecycle webhooks.\",\n );\n }\n\n const dropletUuid = await resolveAttachedDropletUuid(\n client,\n mist,\n mistId,\n publicUrl,\n );\n const paths = isCwdProject ? readLifecyclePaths(input.cwd) : null;\n return {\n mistId,\n mistSlug: mist.slug ?? slug,\n dropletUuid,\n canonical: canonicalDropletUrls(\n publicUrl,\n paths ?? DEFAULT_DROPLET_LIFECYCLE_PATHS,\n ),\n lifecyclePathsSource: paths ? \"fluid.capabilities.json\" : \"defaults\",\n };\n}\n\n/**\n * The Mist's own Droplet. The server-owned link wins (the Mist record, then\n * its droplet integration point); matching `embed_url` is only a legacy\n * fallback, because a broken embed URL is exactly what the repair fixes.\n */\nexport async function resolveAttachedDropletUuid(\n client: MistClient,\n mist: Mist,\n mistId: string,\n publicUrl: string | null,\n): Promise<string> {\n if (typeof mist.droplet?.uuid === \"string\" && mist.droplet.uuid.length > 0) {\n return mist.droplet.uuid;\n }\n\n try {\n const { integration_points } = await listIntegrationPoints(client, mistId);\n const attached = findDropletIntegrationPoint(integration_points);\n if (attached?.integratable_uuid) return attached.integratable_uuid;\n if (attached?.integratable_id != null) {\n const uuid = await findDropletUuidByNumericId(\n client,\n attached.integratable_id,\n );\n if (uuid) return uuid;\n }\n } catch (err) {\n // Older backends may not expose integration points; fall through to\n // the legacy embed-url lookup rather than making repair unavailable.\n // Network failures are not a backend shortfall.\n if (err instanceof MistApiError && err.status === 0) throw err;\n }\n\n const byEmbedUrl = publicUrl\n ? await findDropletByEmbedUrl(client, publicUrl)\n : null;\n if (byEmbedUrl) {\n if (!byEmbedUrl.uuid) {\n throw new DropletRepairRefusal(\n \"droplet_not_found\",\n `Droplet with embed_url ${publicUrl} was found, but its API response did not include a uuid.`,\n );\n }\n return byEmbedUrl.uuid;\n }\n throw new DropletRepairRefusal(\n \"droplet_not_found\",\n \"This Mist is not attached to a Droplet, and no Droplet with a matching embed_url was found. Attach or recreate the Droplet first (`fluid mist attach-droplet`), then retry.\",\n );\n}\n\n/** Lifecycle paths from `fluid.capabilities.json`, or null when absent or\n * unreadable (the caller then uses the defaults). */\nfunction readLifecyclePaths(cwd: string): DropletLifecyclePaths | null {\n const file = join(cwd, \"fluid.capabilities.json\");\n if (!existsSync(file)) return null;\n try {\n return dropletLifecyclePathsFromCapabilities(\n JSON.parse(readFileSync(file, \"utf8\")),\n );\n } catch {\n return null;\n }\n}\n\nexport interface LifecycleRepairResult extends Omit<\n LifecycleRepairSummary,\n \"status\"\n> {\n status: LifecycleRepairSummary[\"status\"] | \"error\";\n lifecyclePathsSource: RepairTarget[\"lifecyclePathsSource\"];\n verified?: boolean;\n verifyStatus?: number;\n}\n\n/** Read the Droplet and report the planned repair without mutating. */\nexport async function planDropletRepair(\n client: MistClient,\n target: RepairTarget,\n dryRun: boolean,\n): Promise<{ alreadyCorrect: boolean; summary: LifecycleRepairResult }> {\n const { droplet } = await getDroplet(client, target.dropletUuid);\n const { alreadyCorrect, summary } = planLifecycleRepair({\n mistId: target.mistId,\n mistSlug: target.mistSlug,\n dropletUuid: target.dropletUuid,\n canonical: target.canonical,\n current: droplet,\n dryRun,\n });\n return {\n alreadyCorrect,\n summary: { ...summary, lifecyclePathsSource: target.lifecyclePathsSource },\n };\n}\n\n/**\n * PATCH only the embed and lifecycle webhook URLs, then re-read the Droplet\n * to verify. A no-op when they already match.\n */\nexport async function applyDropletRepair(\n client: MistClient,\n target: RepairTarget,\n): Promise<{ summary: LifecycleRepairResult; isError: boolean }> {\n const planned = await planDropletRepair(client, target, false);\n if (planned.alreadyCorrect) {\n return { summary: planned.summary, isError: false };\n }\n\n await client.patch<{ droplet?: DropletRecord }>(\n `/api/droplets/${encodeURIComponent(target.dropletUuid)}`,\n dropletRepairPatch(target.canonical),\n );\n\n let verifyStatus = 200;\n let verified = false;\n try {\n const { droplet } = await getDroplet(client, target.dropletUuid);\n verified = dropletUrlsMatch(droplet, target.canonical);\n } catch (err) {\n verifyStatus = err instanceof MistApiError ? err.status : 0;\n }\n return {\n summary: {\n ...planned.summary,\n status: verified ? \"updated\" : \"error\",\n verified,\n verifyStatus,\n },\n isError: !verified,\n };\n}\n\nexport interface ReinstallOptions {\n dryRun?: boolean;\n confirm?: boolean;\n /** `--yes` is accepted only to refuse it: it never approves a reinstall. */\n yes?: boolean;\n installationUuid?: string;\n resumeOperationId?: string;\n watchTimeoutMs?: number;\n}\n\nexport interface ReinstallDeps {\n client: MistClient;\n cwd: string;\n slug?: string;\n planStore: ReinstallPlanStore;\n now?: () => number;\n newIdempotencyKey: () => string;\n sleep?: (durationMs: number) => Promise<void>;\n}\n\nexport interface ReinstallOutcome {\n output: Record<string, unknown>;\n exitCode: number;\n}\n\nconst PLAN_TTL_MINUTES = REINSTALL_PLAN_TTL_MS / 60_000;\n\n/**\n * `fluid mist droplet reinstall`. Same sequencing as Mist's\n * `repair_and_reinstall_droplet`: plan first, explicit approval, one\n * backend-owned replacement, resumable watching. Refusals return a\n * non-zero exit code; nothing irreversible starts without `--confirm`\n * matching a fresh `--dry-run` plan.\n */\nexport async function runDropletReinstall(\n deps: ReinstallDeps,\n opts: ReinstallOptions,\n): Promise<ReinstallOutcome> {\n const now = deps.now ?? Date.now;\n const refusal = (code: string, message: string): ReinstallOutcome => ({\n output: { status: \"refused\", code, message },\n exitCode: 1,\n });\n\n if (opts.dryRun && opts.confirm) {\n return refusal(\n \"conflicting_flags\",\n \"Pass either --dry-run (show the plan) or --confirm (execute it), not both.\",\n );\n }\n if (opts.dryRun && opts.resumeOperationId?.trim()) {\n return refusal(\n \"conflicting_flags\",\n \"--resume keeps watching a replacement that already started; it does not take --dry-run. Run it with --installation only.\",\n );\n }\n const request = {\n dryRun: opts.dryRun === true,\n confirmDestructiveReinstall: opts.confirm === true,\n installationUuid: opts.installationUuid?.trim() || undefined,\n resumeOperationId: opts.resumeOperationId,\n };\n const requestCheck = checkReplacementRequest(request);\n if (!requestCheck.ok) {\n return refusal(\n requestCheck.code,\n '--resume also needs --installation with the installationUuid from that same \"still_running\" result. Replacement status is scoped to the installation the operation started on, so resuming without it would watch the wrong one.',\n );\n }\n const { resumeOperationId } = requestCheck;\n if (\n !request.dryRun &&\n replacementStartNeedsConfirmation(request, resumeOperationId)\n ) {\n return refusal(\n \"confirmation_required\",\n opts.yes\n ? \"--yes does not approve an irreversible reinstall. Run with --dry-run, review the exact dropletUuid and installationUuid, then rerun with --confirm.\"\n : \"Refused to start an irreversible replacement without explicit confirmation. Run with --dry-run, review the exact dropletUuid and installationUuid, then rerun with --confirm.\",\n );\n }\n\n try {\n const target = await resolveRepairTarget(deps.client, {\n slug: deps.slug,\n cwd: deps.cwd,\n });\n // Read-only until the start is confirmed and the plan receipt checks out.\n const plannedRepair = await planDropletRepair(deps.client, target, true);\n\n const installations = await listAllDropletInstallations(deps.client);\n const installation = selectReplacementInstallation({\n installations,\n dropletUuid: target.dropletUuid,\n installationUuid: request.installationUuid,\n resumeOperationId,\n });\n if (!installation?.uuid) {\n return refusal(\n \"installation_not_found\",\n `No active installation for attached Droplet ${target.dropletUuid} was found. If a previous approved replacement terminalized the source, rerun with that plan's --installation <uuid>; otherwise install the Droplet once from Fluid first.`,\n );\n }\n const installationUuid = installation.uuid;\n const idempotencyKey = deps.newIdempotencyKey();\n const planInput = {\n dryRun: request.dryRun,\n resumeOperationId,\n mistId: target.mistId,\n dropletUuid: target.dropletUuid,\n installation: { ...installation, uuid: installationUuid },\n idempotencyKey,\n };\n\n if (request.dryRun) {\n deps.planStore.save({\n mistId: target.mistId,\n dropletUuid: target.dropletUuid,\n installationUuid,\n ...target.canonical,\n plannedAt: new Date(now()).toISOString(),\n });\n const confirmCommand = reinstallCommand(target.mistSlug, [\n \"--confirm\",\n ...(request.installationUuid\n ? [\"--installation\", installationUuid]\n : []),\n ]);\n return {\n output: {\n ...buildReplacementPlan({\n ...planInput,\n lifecycleRepair: plannedRepair.summary,\n }),\n nextStep: `Review the dropletUuid and installationUuid above. This replacement is irreversible. To approve it, run \\`${confirmCommand}\\` within ${PLAN_TTL_MINUTES} minutes.`,\n },\n exitCode: 0,\n };\n }\n\n let lifecycleRepair: LifecycleRepairResult = plannedRepair.summary;\n let startedOperation: LifecycleOperation | null = null;\n if (!resumeOperationId) {\n // Claiming removes the receipt atomically, so one plan approves one\n // attempt even when two --confirm runs race; any refusal or failure\n // from here on needs a fresh --dry-run.\n const receiptCheck = checkPlanReceipt(\n deps.planStore.claim(target.mistId),\n {\n dropletUuid: target.dropletUuid,\n installationUuid,\n ...target.canonical,\n },\n now(),\n );\n if (!receiptCheck.ok) {\n return refusal(\n receiptCheck.code,\n planReceiptMessage(receiptCheck.code),\n );\n }\n\n const repaired = await applyDropletRepair(deps.client, target);\n lifecycleRepair = repaired.summary;\n if (repaired.isError) {\n return {\n output: {\n status: \"error\",\n code: \"lifecycle_repair_failed\",\n message:\n \"The Droplet lifecycle URLs did not verify after the repair, so no replacement was started.\",\n lifecycleRepair,\n },\n exitCode: 1,\n };\n }\n\n startedOperation = await startDropletReplacement(deps.client, {\n mistId: target.mistId,\n installationUuid,\n idempotencyKey,\n });\n }\n\n const plan = buildReplacementPlan({ ...planInput, lifecycleRepair });\n const operationId = resumeOperationId ?? startedOperation?.id;\n if (!operationId) {\n return {\n output: {\n ...plan,\n status: \"error\",\n code: \"missing_operation_id\",\n message:\n \"Fluid accepted the replacement but did not return an operation id to watch.\",\n },\n exitCode: 1,\n };\n }\n\n const watchedForMs = opts.watchTimeoutMs ?? REPLACEMENT_POLL_BUDGET_MS;\n const pollResult = await pollDropletReplacement(deps.client, {\n mistId: target.mistId,\n installationUuid,\n operationId,\n timeoutMs: watchedForMs,\n ...(deps.sleep ? { sleep: deps.sleep } : {}),\n });\n const watch = summarizeReplacementWatch({\n plan,\n pollResult,\n startedOperation,\n operationId,\n installationUuid,\n watchedForMs,\n nextStep: ({ operationId: id, installationUuid: source, stage }) =>\n `Fluid is still working on this replacement: it is not finished and it did not fail. Current stage: ${stage}. Keep watching with \\`${reinstallCommand(target.mistSlug, [\"--resume\", id, \"--installation\", source])}\\`. The reinstall is complete only when status is \"succeeded\".`,\n });\n return { output: { ...watch.result }, exitCode: watch.isError ? 1 : 0 };\n } catch (err) {\n if (err instanceof DropletRepairRefusal)\n return refusal(err.code, err.message);\n throw err;\n }\n}\n\nfunction reinstallCommand(slug: string, flags: string[]): string {\n return [\"fluid mist droplet reinstall\", slug, ...flags].join(\" \");\n}\n\nfunction planReceiptMessage(\n code: \"plan_required\" | \"plan_stale\" | \"plan_changed\",\n): string {\n switch (code) {\n case \"plan_required\":\n return \"Refused: no reinstall plan has been shown for this Mist. Run with --dry-run first, review the plan, then rerun with --confirm.\";\n case \"plan_stale\":\n return `Refused: the reinstall plan is older than ${PLAN_TTL_MINUTES} minutes. Run with --dry-run again, review the plan, then rerun with --confirm.`;\n case \"plan_changed\":\n return \"Refused: the Droplet, installation or lifecycle URLs changed since the plan was shown. Run with --dry-run again, review the new plan, then rerun with --confirm.\";\n }\n}\n","/**\n * Ask Fluid for, or withdraw, egress through its egress proxy for the Mist's\n * attached Droplet (`fluid mist droplet egress-proxy`). The owner's ask is the\n * droplet's `egress_proxy_requested`, and the proxy admits the droplet on it\n * alone. A Fluid root admin's grant (`egress_proxy_enabled`) is now only a\n * record; withdrawing the request also clears it.\n */\n\nimport { MistApiError, type MistClient } from \"../api/client.js\";\nimport { getDroplet, updateDroplet } from \"../api/droplets.js\";\nimport { resolveMistId } from \"../api/id-resolver.js\";\nimport { showMist } from \"../api/mists.js\";\nimport { resolveAttachedDropletUuid } from \"./droplet-repair.js\";\nimport { resolveSlug } from \"./slug-resolver.js\";\n\n/** Why a droplet response can lack the egress fields. Fluid returns them only\n * to company admins (and root admins), and older Fluids not at all. */\nexport const EGRESS_NOT_RETURNED =\n \"Fluid did not return the droplet's egress fields: only a company admin can see or ask for egress, or this Fluid predates it.\";\n\n/** Fluid refused the egress request outright: it accepts one only from a\n * company admin (401), and only for their own company's droplets (403).\n * Distinct from an older Fluid, which ignores the field and omits it from\n * the response. */\nexport const EGRESS_REFUSED =\n \"Fluid refused the egress request: only a company admin can ask for egress, for their own company's droplets. The droplet was set up without it.\";\n\n/** Whether an API error is Fluid refusing the egress request: a 401 or 403\n * whose `errors` names `egress_proxy_requested`. Matched exactly, so a real\n * authentication failure is reported as one rather than as a refusal. */\nexport function isEgressRefusal(error: unknown): boolean {\n return (\n error instanceof MistApiError &&\n (error.status === 401 || error.status === 403) &&\n error.fieldErrors?.egress_proxy_requested !== undefined\n );\n}\n\n/** Fluid's own words for an egress refusal, falling back to EGRESS_REFUSED. */\nfunction egressRefusalMessage(error: MistApiError): string {\n const reasons = error.fieldErrors?.egress_proxy_requested;\n const reason = Array.isArray(reasons) ? reasons[0] : reasons;\n return typeof reason === \"string\"\n ? `Fluid refused the egress request: ${reason}.`\n : EGRESS_REFUSED;\n}\n\nexport interface DropletEgressProxyResult {\n mistSlug: string;\n dropletUuid: string;\n /** The owner's request as the API reports it after this run. */\n requested: boolean;\n /** Whether a Fluid root admin also recorded a grant. The proxy no longer\n * reads it; kept for JSON consumers. */\n granted: boolean;\n /** True when this run wrote the request (it was not already as asked). */\n changed: boolean;\n}\n\n/** Fluid refused to turn egress on for the droplet. Still an API error, so\n * it keeps the response's status and the 4xx exit code; only the message\n * is Fluid's reason instead of a bare status. */\nexport class EgressRequestRefusedError extends MistApiError {\n constructor(message: string, refused: MistApiError) {\n super(message, refused.status, refused.details, refused.fieldErrors);\n this.name = \"EgressRequestRefusedError\";\n }\n}\n\n/** The API answered without the egress fields. */\nexport class EgressProxyUnsupportedError extends Error {\n constructor() {\n super(EGRESS_NOT_RETURNED);\n this.name = \"EgressProxyUnsupportedError\";\n }\n}\n\n/**\n * Set the attached droplet's egress request. Reads it first so an\n * already-correct droplet is left untouched, then PATCHes and trusts only\n * the values the API returns.\n */\nexport async function setDropletEgressProxy(\n client: MistClient,\n input: { slug?: string; cwd: string; requested: boolean },\n): Promise<DropletEgressProxyResult> {\n const slug = resolveSlug(input.slug, input.cwd);\n const mistId = await resolveMistId(client, slug);\n const { mist } = await showMist(client, mistId);\n const dropletUuid = await resolveAttachedDropletUuid(\n client,\n mist,\n mistId,\n mist.public_url ?? null,\n );\n const mistSlug = mist.slug ?? slug;\n\n const current = (await getDroplet(client, dropletUuid)).droplet;\n if (current.egress_proxy_requested === undefined) {\n throw new EgressProxyUnsupportedError();\n }\n // Already as asked, except that withdrawing must also clear a grant a\n // Fluid admin gave without a request: the write withdraws both.\n const grantLeftOn = !input.requested && current.egress_proxy_enabled === true;\n if (current.egress_proxy_requested === input.requested && !grantLeftOn) {\n return {\n mistSlug,\n dropletUuid,\n requested: current.egress_proxy_requested,\n granted: current.egress_proxy_enabled === true,\n changed: false,\n };\n }\n\n const updated = (\n await updateDroplet(client, dropletUuid, {\n egress_proxy_requested: input.requested,\n }).catch((error: unknown) => {\n if (error instanceof MistApiError && isEgressRefusal(error)) {\n throw new EgressRequestRefusedError(egressRefusalMessage(error), error);\n }\n throw error;\n })\n ).droplet;\n if (updated.egress_proxy_requested === undefined) {\n throw new EgressProxyUnsupportedError();\n }\n return {\n mistSlug,\n dropletUuid,\n requested: updated.egress_proxy_requested,\n granted: updated.egress_proxy_enabled === true,\n changed: true,\n };\n}\n","/**\n * Surface provisioning engine — the one place the \"create a Fluid surface and\n * wire it to a mist\" flow lives, at parity with Mist Desktop's\n * mist-integration-points-api.service.ts. Exposed via `fluid mist create`\n * (create a bare mist first, then run this), `fluid mist attach` (any kind),\n * and `fluid mist attach-droplet` (droplet shortcut).\n *\n * Three surface kinds, each with its own create endpoint + attach identifier:\n * - droplet → POST /api/droplets → drp_ uuid → integratable_uuid\n * - drop-zone → POST /api/drop_zones → dz_ uuid → integratable_uuid\n * - mobile-embed→ POST /api/company/mobile_widgets→ numeric id → integratable_id\n *\n * Design tenets (repo philosophy — \"server is source of truth\"):\n * - No local progress file. State is re-derived from the API each run.\n * - Attach-existing is idempotent: if an integration point already links the\n * target surface (matched via the #19689 integratable_uuid echo, or the\n * numeric id for mobile), skip. A droplet is 1-per-mist, so creating a new\n * droplet also skips when one is already attached. Drop-zones/mobile\n * embeds are additive (a mist can have many).\n * - Only a droplet attach provisions managed env keys\n * (FLUID_DROPLET_UUID/_SECRET/_WEBHOOK_AUTH_TOKEN); this flow only\n * VERIFIES they landed — it never writes them (the API 422s on write).\n * - A droplet must also be INSTALLED (POST /api/droplet_installations) —\n * create + link alone leave it not-connected/inactive, so install-scoped\n * features (dit/dex exchange, admin embeds, install webhooks) don't work.\n * The install step is idempotent (skips when an installation exists).\n */\n\nimport { showMist } from \"../api/mists.js\";\nimport {\n createIntegrationPoint,\n findDropletIntegrationPoint,\n findIntegrationPointBySurface,\n listIntegrationPoints,\n} from \"../api/integration_points.js\";\nimport {\n buildDropletCreateInput,\n createDroplet,\n DEFAULT_DROPLET_SCOPES,\n findDropletByEmbedUrl,\n findDropletUuidByNumericId,\n getDroplet,\n updateDroplet,\n} from \"../api/droplets.js\";\nimport {\n createDropletInstallation,\n findInstallationForDroplet,\n listAllDropletInstallations,\n} from \"../api/droplet_installations.js\";\nimport { buildDropZoneCreateInput, createDropZone } from \"../api/drop_zones.js\";\nimport {\n buildMobileWidgetCreateInput,\n createMobileWidget,\n} from \"../api/mobile_widgets.js\";\nimport { listEnvVars } from \"../api/env_vars.js\";\nimport { MistApiError, type MistClient } from \"../api/client.js\";\nimport {\n MANAGED_DROPLET_ENV_KEYS,\n type DropletRecord,\n type IntegrationPointKind,\n type MistState,\n} from \"../types.js\";\nimport {\n EGRESS_NOT_RETURNED,\n EGRESS_REFUSED,\n isEgressRefusal,\n} from \"./droplet-egress.js\";\nimport type { StepReporter } from \"./step-reporter.js\";\n\nexport type SurfaceKind = IntegrationPointKind;\n\nexport interface ProvisionTiming {\n publicUrlTimeoutMs: number;\n publicUrlIntervalMs: number;\n envTimeoutMs: number;\n envIntervalMs: number;\n}\n\nconst DEFAULT_TIMING: ProvisionTiming = {\n publicUrlTimeoutMs: 90_000,\n publicUrlIntervalMs: 3_000,\n envTimeoutMs: 120_000,\n envIntervalMs: 5_000,\n};\n\nexport interface SurfaceProvisionOptions {\n /** Mist slug or UUID to attach to. Re-queried each run. */\n slugOrId: string;\n kind: SurfaceKind;\n /** Surface display name (create path). Defaults to the mist's name. */\n name?: string;\n /** droplet: requested scopes (defaults to the full set). */\n requestedScopes?: readonly string[];\n /**\n * droplet: ask Fluid for egress through its egress proxy (e.g. to reach\n * Exigo); a company admin's request admits the droplet. Asked on create,\n * and of an already-existing droplet too.\n */\n egressProxy?: boolean;\n /** drop-zone: slot id `page__zone` (e.g. checkout__below_total). */\n zone?: string;\n /** drop-zone + mobile: embed height in px. */\n heightPx?: number;\n /** mobile: cover image url (required by the API; a default is used if omitted). */\n coverImageUrl?: string;\n /** Attach an EXISTING droplet/drop-zone by uuid instead of creating one. */\n existingUuid?: string;\n /** Attach an EXISTING mobile widget by numeric id instead of creating one. */\n existingId?: number;\n /** Verify managed env keys after a droplet attach (default true). */\n verifyEnv?: boolean;\n reporter: StepReporter;\n timing?: Partial<ProvisionTiming>;\n /** Injectable sleep (tests pass a no-op). */\n sleep?: (ms: number) => Promise<void>;\n}\n\nexport interface SurfaceProvisionResult {\n mist: {\n id: string;\n slug: string;\n name: string | null;\n public_url: string | null;\n state: MistState;\n };\n kind: SurfaceKind;\n surface: { uuid: string | null; id: number | null; name: string | null };\n integrationPoint: {\n id: string;\n integratableUuid: string | null;\n created: boolean;\n };\n surfaceCreated: boolean;\n /** True only when THIS run set/backfilled the surface embed URL. */\n embedUrlSet: boolean;\n /**\n * Droplet only; null for other kinds. The company-level installation that\n * actually CONNECTS the droplet (create + link alone leave it inactive).\n * `created` is true when THIS run installed it.\n */\n installation: { uuid: string | null; created: boolean } | null;\n /** Droplet only; null for other kinds (they don't provision managed env). */\n managedEnv: { ready: boolean; present: string[]; missing: string[] } | null;\n /**\n * Droplet only, and only when `egressProxy` was asked for; null otherwise.\n * `requested` and `granted` are what the API reported back; `requested`\n * false means the request did not take (the caller is not a company admin,\n * or this Fluid predates it). `granted` is a root admin's record only.\n */\n egressProxy: {\n requested: boolean;\n granted: boolean;\n changed: boolean;\n } | null;\n}\n\nconst defaultSleep = (ms: number): Promise<void> =>\n new Promise((resolve) => setTimeout(resolve, ms));\n\nconst KIND_LABEL: Record<SurfaceKind, string> = {\n droplet: \"droplet\",\n \"drop-zone\": \"drop zone\",\n \"mobile-embed\": \"mobile embed\",\n};\n\n/**\n * Create (or reuse) a surface of `kind` and wire it to the mist. Idempotent\n * for droplet + attach-existing; additive for new drop-zones/mobile embeds.\n */\nexport async function provisionSurface(\n client: MistClient,\n options: SurfaceProvisionOptions,\n): Promise<SurfaceProvisionResult> {\n const { reporter, slugOrId, kind } = options;\n const timing = { ...DEFAULT_TIMING, ...options.timing };\n const sleep = options.sleep ?? defaultSleep;\n const label = KIND_LABEL[kind];\n\n // Step 1 — resolve the mist (re-query; server is source of truth).\n reporter.start(\"Resolving mist…\");\n const mist = (await showMist(client, slugOrId)).mist;\n reporter.succeed(`Mist ${mist.slug} (${mist.state})`);\n\n // Step 2 — inspect existing integrations (carries the integratable_uuid echo).\n reporter.start(\"Checking existing integrations…\");\n const points = (await listIntegrationPoints(client, slugOrId))\n .integration_points;\n\n let surfaceUuid: string | null = null;\n let surfaceNumericId: number | null = null;\n let surfaceName: string | null = null;\n let surfaceCreated = false;\n let embedUrlSet = false;\n // The droplet THIS run created, when it did: its response says whether the\n // egress request took, so no extra PATCH is needed for it.\n let createdEgressProxy: DropletRecord | undefined;\n let integrationPoint: SurfaceProvisionResult[\"integrationPoint\"] | undefined;\n\n const attachingExisting =\n options.existingUuid != null || options.existingId != null;\n\n // Detect an already-present integration point we can reuse (idempotency).\n const alreadyLinked = attachingExisting\n ? findIntegrationPointBySurface(points, {\n kind,\n uuid: options.existingUuid,\n id: options.existingId,\n })\n : kind === \"droplet\"\n ? findDropletIntegrationPoint(points)\n : undefined;\n\n if (alreadyLinked) {\n reporter.skip(\n `${label} already attached (integration #${alreadyLinked.id}) — skipping`,\n );\n surfaceUuid =\n alreadyLinked.integratable_uuid ?? options.existingUuid ?? null;\n surfaceNumericId =\n alreadyLinked.integratable_id ?? options.existingId ?? null;\n integrationPoint = {\n id: alreadyLinked.id,\n integratableUuid: alreadyLinked.integratable_uuid ?? null,\n created: false,\n };\n } else {\n reporter.succeed(\"No matching integration yet\");\n\n // Resolve what to attach by: an existing surface, or a freshly-created one.\n let attachUuid: string | undefined = options.existingUuid;\n let attachId: number | undefined = options.existingId;\n\n if (!attachingExisting) {\n const embedUrl = await resolvePublicUrl(client, {\n slugOrId,\n initial: mist.public_url,\n reporter,\n timing,\n sleep,\n });\n if (!embedUrl) {\n throw new Error(\n \"The mist has no public_url yet — wait for the first deploy before creating a surface.\",\n );\n }\n // `vendor_name` is frequently null in production — without this final\n // `mist.slug` fallback a nameless mist would send `name: null` in the\n // surface-create payload below.\n const name =\n options.name?.trim() ||\n mist.name?.trim() ||\n mist.vendor_name ||\n mist.slug;\n if (kind === \"droplet\") {\n reporter.start(\"Checking for an interrupted Droplet create…\");\n const recovered = await findDropletByEmbedUrl(client, embedUrl);\n if (recovered) {\n surfaceUuid = recovered.uuid;\n surfaceNumericId = recovered.id ?? null;\n surfaceName = recovered.name;\n attachUuid = recovered.uuid;\n embedUrlSet = true;\n reporter.skip(\n `Reusing droplet ${surfaceUuid} from an interrupted attach`,\n );\n } else {\n reporter.start(`Creating ${label} \"${name}\"…`);\n const createInput = (egressProxy: boolean | undefined) =>\n buildDropletCreateInput({\n name,\n embedUrl,\n requestedScopes: options.requestedScopes,\n egressProxy,\n });\n let created;\n try {\n created = await createDroplet(client, {\n droplet: createInput(options.egressProxy),\n });\n } catch (err) {\n // Fluid refuses the optional egress request from anyone but a\n // company admin (401). Create the droplet without it; the egress\n // step below reports the refusal instead of aborting provisioning.\n if (!(options.egressProxy && isEgressRefusal(err))) throw err;\n created = await createDroplet(client, {\n droplet: createInput(false),\n });\n }\n createdEgressProxy = created.droplet;\n surfaceUuid = created.droplet.uuid;\n surfaceNumericId = created.droplet.id ?? null;\n surfaceName = created.droplet.name;\n attachUuid = created.droplet.uuid;\n embedUrlSet = true;\n surfaceCreated = true;\n reporter.succeed(`Created droplet ${surfaceUuid}`);\n }\n } else if (kind === \"drop-zone\") {\n reporter.start(`Creating ${label} \"${name}\"…`);\n const created = await createDropZone(client, {\n drop_zone: buildDropZoneCreateInput({\n name,\n embedUrl,\n zone: options.zone,\n heightPx: options.heightPx,\n }),\n });\n surfaceUuid = created.drop_zone.uuid;\n surfaceName = created.drop_zone.name;\n attachUuid = created.drop_zone.uuid;\n embedUrlSet = true;\n surfaceCreated = true;\n reporter.succeed(`Created drop zone ${surfaceUuid}`);\n } else {\n // mobile-embed\n reporter.start(`Creating ${label} \"${name}\"…`);\n const created = await createMobileWidget(client, {\n mobile_widget: buildMobileWidgetCreateInput({\n name,\n embedUrl,\n coverImageUrl: options.coverImageUrl,\n heightPx: options.heightPx,\n }),\n });\n surfaceNumericId = created.mobile_widget.id;\n surfaceName = created.mobile_widget.name;\n attachId = created.mobile_widget.id;\n embedUrlSet = true;\n surfaceCreated = true;\n reporter.succeed(`Created mobile embed #${surfaceNumericId}`);\n }\n } else {\n // Attaching existing — nothing new re: embed URL; the caller owns it.\n surfaceUuid = options.existingUuid ?? null;\n surfaceNumericId = options.existingId ?? null;\n embedUrlSet = true;\n }\n\n // Step 3 — attach via integration point. droplet/drop-zone by uuid;\n // mobile-embed by numeric id.\n const attachInput =\n kind === \"mobile-embed\"\n ? { kind, integratable_id: attachId! }\n : { kind, integratable_uuid: attachUuid! };\n reporter.start(`Attaching ${label}…`);\n let ipResp;\n try {\n ipResp = await createIntegrationPoint(client, slugOrId, attachInput);\n } catch (err) {\n reporter.fail(\"Attach failed\");\n throw normalizeAttachError(err, attachUuid ?? String(attachId), kind);\n }\n integrationPoint = {\n id: ipResp.integration_point.id,\n integratableUuid: ipResp.integration_point.integratable_uuid ?? null,\n created: true,\n };\n // Carry the uuid we resolved so callers can display it even if the echo lags.\n if (!surfaceUuid) {\n surfaceUuid =\n ipResp.integration_point.integratable_uuid ?? attachUuid ?? null;\n }\n if (surfaceNumericId == null) {\n surfaceNumericId = ipResp.integration_point.integratable_id ?? null;\n }\n reporter.succeed(\n kind === \"droplet\"\n ? \"Droplet attached — provisioning env + redeploy…\"\n : `${label} attached`,\n );\n }\n\n // Step 4 — droplet only: install it for the company. Create + link alone\n // leave the droplet not-connected/inactive (the admin shows the Install\n // screen, `?dri=` never resolves, install webhooks don't fire) — the\n // installation is what connects it. Runs on the already-linked path too so\n // an interrupted run can finish the job on re-run.\n let installation: SurfaceProvisionResult[\"installation\"] = null;\n let egressProxy: SurfaceProvisionResult[\"egressProxy\"] = null;\n if (kind === \"droplet\") {\n if (!surfaceUuid && surfaceNumericId != null) {\n // Legacy integration points can omit `integratable_uuid` — recover it\n // by paging the company's droplets list and matching the numeric id\n // (the show endpoint resolves by uuid only), so older linked-but-\n // uninstalled droplets still get installed on re-run.\n try {\n surfaceUuid = await findDropletUuidByNumericId(\n client,\n surfaceNumericId,\n );\n } catch {\n // Recovery is best-effort; fall through to the hard error below.\n }\n }\n if (!surfaceUuid) {\n // Do NOT return success here — a linked-but-uninstalled droplet is\n // exactly the inactive state this flow exists to repair.\n reporter.fail(\"Droplet uuid unknown — cannot install\");\n throw new Error(\n \"Couldn't determine the droplet uuid, so the droplet is linked but NOT installed — \" +\n \"it stays inactive until an installation exists. Re-run with \" +\n \"`--existing <droplet-uuid>`, or install manually: \" +\n `POST /api/droplet_installations with {\"droplet_uuid\":\"<uuid>\"}.`,\n );\n }\n if (options.egressProxy) {\n egressProxy = await ensureEgressProxy(client, {\n dropletUuid: surfaceUuid,\n created: createdEgressProxy,\n reporter,\n });\n }\n installation = await ensureDropletInstalled(client, {\n dropletUuid: surfaceUuid,\n scopes: options.requestedScopes ?? DEFAULT_DROPLET_SCOPES,\n reporter,\n });\n }\n\n // Step 5 — droplet only: verify the Mist-managed env keys landed.\n const managedEnv =\n kind === \"droplet\"\n ? await verifyManagedEnv(client, {\n slugOrId,\n reporter,\n timing,\n sleep,\n enabled: options.verifyEnv !== false,\n })\n : null;\n\n return {\n mist: {\n id: mist.id,\n slug: mist.slug,\n name: mist.name,\n public_url: mist.public_url,\n state: mist.state,\n },\n kind,\n surface: { uuid: surfaceUuid, id: surfaceNumericId, name: surfaceName },\n integrationPoint: integrationPoint ?? {\n id: \"\",\n integratableUuid: null,\n created: false,\n },\n surfaceCreated,\n embedUrlSet,\n installation,\n managedEnv,\n egressProxy,\n };\n}\n\n/**\n * Make sure the droplet has asked Fluid for egress through its proxy. A\n * droplet this run created already carries the request; one that existed\n * (reused, already linked, or `--existing`) is read first and PATCHed only if\n * it has not asked yet, so the flag is never silently ignored and an existing\n * request is not reported as new. Fluid refusing the request (only a company\n * admin can ask) warns and lets provisioning carry on; a response without the\n * fields (a non-admin, or an older Fluid) warns instead of claiming success.\n */\nasync function ensureEgressProxy(\n client: MistClient,\n args: {\n dropletUuid: string;\n /** The droplet this run created (already carrying the request), if any. */\n created: DropletRecord | undefined;\n reporter: StepReporter;\n },\n): Promise<{ requested: boolean; granted: boolean; changed: boolean }> {\n const { reporter } = args;\n let record = args.created;\n // A droplet this run created with the request made it in this run.\n let changed = record?.egress_proxy_requested === true;\n if (record === undefined) {\n record = await readDropletIfAllowed(client, args.dropletUuid);\n }\n if (record?.egress_proxy_requested !== true) {\n reporter.start(\"Asking Fluid for egress through its proxy…\");\n try {\n record = (\n await updateDroplet(client, args.dropletUuid, {\n egress_proxy_requested: true,\n })\n ).droplet;\n } catch (err) {\n if (!isEgressRefusal(err)) throw err;\n reporter.warn(EGRESS_REFUSED);\n return { requested: false, granted: false, changed: false };\n }\n changed = record.egress_proxy_requested === true;\n }\n const requested = record?.egress_proxy_requested === true;\n const granted = record?.egress_proxy_enabled === true;\n if (requested) {\n reporter.succeed(\n \"Egress requested — Fluid's proxy admits requested droplets wherever the proxy honours Fluid's answer\",\n );\n } else {\n reporter.warn(\n `${EGRESS_NOT_RETURNED} Re-run \\`fluid mist droplet egress-proxy on\\` as a company admin.`,\n );\n }\n return { requested, granted, changed };\n}\n\n/**\n * Read an existing droplet so an existing request is not written again. Fluid\n * gates reading a droplet (`droplets.view`) separately from updating it\n * (`droplets.update`), so a caller allowed to update may not be allowed to\n * read: then this returns undefined and the caller just makes the request,\n * which is idempotent. Any other failure propagates.\n */\nasync function readDropletIfAllowed(\n client: MistClient,\n dropletUuid: string,\n): Promise<DropletRecord | undefined> {\n try {\n return (await getDroplet(client, dropletUuid)).droplet;\n } catch (err) {\n if (err instanceof MistApiError && err.status === 403) return undefined;\n throw err;\n }\n}\n\n/**\n * Install the droplet for the current company (idempotent). Checks the\n * installations list first; treats the API's already-installed rejection as\n * success too, so re-runs and races both converge. A hard failure throws with\n * the droplet uuid + recovery steps — the created droplet and mist link stay\n * in place, so an idempotent re-run finishes the job.\n */\nasync function ensureDropletInstalled(\n client: MistClient,\n args: {\n dropletUuid: string;\n scopes: readonly string[];\n reporter: StepReporter;\n },\n): Promise<{ uuid: string | null; created: boolean }> {\n const { reporter, dropletUuid } = args;\n reporter.start(\"Installing droplet…\");\n\n // Idempotency: already installed via a previous run, Mist Desktop, or the\n // admin marketplace. Best-effort — the POST below is the authoritative step.\n let existing: { uuid?: string } | undefined;\n try {\n existing = findInstallationForDroplet(\n await listAllDropletInstallations(client),\n dropletUuid,\n );\n } catch {\n // Listing is only an optimization; fall through to the POST.\n }\n if (existing) {\n reporter.skip(\n `Droplet already installed${existing.uuid ? ` (${existing.uuid})` : \"\"} — skipping`,\n );\n return { uuid: existing.uuid ?? null, created: false };\n }\n\n try {\n const resp = await createDropletInstallation(client, {\n droplet_uuid: dropletUuid,\n scopes: [...args.scopes],\n });\n const uuid = resp.droplet_installation?.uuid ?? null;\n reporter.succeed(`Droplet installed${uuid ? ` (${uuid})` : \"\"}`);\n return { uuid, created: true };\n } catch (err) {\n // The API rejects a duplicate install (list raced or missed it). Only\n // treat that as success when the installations list confirms a row —\n // a generic 409/422 that merely mentions \"exists\" must not mask a real\n // failure and leave the droplet inactive.\n if (\n err instanceof MistApiError &&\n (err.status === 409 || err.status === 422)\n ) {\n const confirmed = await lookupInstallation(client, dropletUuid);\n if (confirmed) {\n reporter.skip(\"Droplet already installed — skipping\");\n return { uuid: confirmed.uuid ?? null, created: false };\n }\n }\n reporter.fail(\"Droplet install failed\");\n throw normalizeInstallError(err, dropletUuid);\n }\n}\n\n/** Best-effort installation lookup for a droplet; null when the row can't\n * be found or the list call fails. */\nasync function lookupInstallation(\n client: MistClient,\n dropletUuid: string,\n): Promise<{ uuid?: string } | null> {\n try {\n return (\n findInstallationForDroplet(\n await listAllDropletInstallations(client),\n dropletUuid,\n ) ?? null\n );\n } catch {\n return null;\n }\n}\n\n/**\n * Install failures must NOT read as \"wired up\" — an uninstalled droplet means\n * dit/dex token exchange, admin embeds, and install webhooks silently don't\n * work. Surface the droplet uuid + recovery path; keep the MistApiError\n * status so the error handler exits with the spec'd non-zero code.\n */\nfunction normalizeInstallError(err: unknown, dropletUuid: string): Error {\n const detail = err instanceof Error ? err.message : String(err);\n const message =\n `Droplet ${dropletUuid} was created and linked to the mist, but installing it failed: ${detail}. ` +\n `The droplet and its link were left in place — re-run this command to finish the install, ` +\n `or install manually: POST /api/droplet_installations with {\"droplet_uuid\":\"${dropletUuid}\"}.`;\n return err instanceof MistApiError\n ? new MistApiError(message, err.status, err.details)\n : new Error(message);\n}\n\n/**\n * If the integration-points endpoint rejects the attach identifier (e.g. an\n * older API that predates #19688's attach-by-uuid support), surface an\n * actionable message noting the surface WAS created. Other errors pass through.\n */\nfunction normalizeAttachError(\n err: unknown,\n surfaceRef: string,\n kind: SurfaceKind,\n): Error {\n if (\n err instanceof MistApiError &&\n (err.status === 400 || err.status === 422)\n ) {\n const blob = `${err.message} ${JSON.stringify(err.details ?? {})}`;\n if (\n /integratable_uuid|integratable_id|unpermitted|unknown|param/i.test(blob)\n ) {\n return new MistApiError(\n `The ${KIND_LABEL[kind]} ${surfaceRef} was created, but this API rejected the attach ` +\n `(needs the #19688 integration-points endpoint). Re-run once the API is updated.`,\n err.status,\n err.details,\n );\n }\n }\n return err instanceof Error ? err : new Error(String(err));\n}\n\n/**\n * Return the mist's public_url, polling briefly if it isn't ready yet\n * (async-first-deploy). Returns null if it never appears within the window.\n */\nasync function resolvePublicUrl(\n client: MistClient,\n args: {\n slugOrId: string;\n initial: string | null;\n reporter: StepReporter;\n timing: ProvisionTiming;\n sleep: (ms: number) => Promise<void>;\n },\n): Promise<string | null> {\n if (args.initial) return args.initial;\n\n args.reporter.start(\"Waiting for the mist's public URL…\");\n const deadline = Date.now() + args.timing.publicUrlTimeoutMs;\n for (;;) {\n const url = (await showMist(client, args.slugOrId)).mist.public_url;\n if (url) {\n args.reporter.succeed(`Public URL ready: ${url}`);\n return url;\n }\n if (Date.now() >= deadline) break;\n await args.sleep(args.timing.publicUrlIntervalMs);\n }\n return null;\n}\n\n/**\n * Poll `GET …/env_vars` until the Mist-managed droplet keys appear with\n * `managed_by: \"mist\"`. Never fails the flow — a redeploy in progress is a\n * normal, transient state we simply report.\n */\nasync function verifyManagedEnv(\n client: MistClient,\n args: {\n slugOrId: string;\n reporter: StepReporter;\n timing: ProvisionTiming;\n sleep: (ms: number) => Promise<void>;\n enabled: boolean;\n },\n): Promise<{ ready: boolean; present: string[]; missing: string[] }> {\n const required = [...MANAGED_DROPLET_ENV_KEYS];\n if (!args.enabled) {\n return { ready: false, present: [], missing: required };\n }\n\n args.reporter.start(\"Verifying managed environment…\");\n const deadline = Date.now() + args.timing.envTimeoutMs;\n let present: string[] = [];\n\n for (;;) {\n const envVars = (await listEnvVars(client, args.slugOrId)).env_vars ?? [];\n const managed = new Set(\n envVars.filter((e) => e.managed_by === \"mist\").map((e) => e.key),\n );\n present = required.filter((k) => managed.has(k));\n if (present.length === required.length) {\n args.reporter.succeed(\"Managed env keys present — droplet is wired up\");\n return { ready: true, present, missing: [] };\n }\n if (Date.now() >= deadline) break;\n args.reporter.update(\n `Verifying managed environment… (${present.length}/${required.length} keys, redeploy in progress)`,\n );\n await args.sleep(args.timing.envIntervalMs);\n }\n\n const missing = required.filter((k) => !present.includes(k));\n args.reporter.warn(\n `Redeploy in progress — managed keys not visible yet (${present.length}/${required.length}). ` +\n `Check later with \\`fluid mist env list\\`. Missing: ${missing.join(\", \")}`,\n );\n return { ready: false, present, missing };\n}\n","/**\n * Shared parsing + validation for the surface-kind flags used by\n * `create` and `attach`. Keeps kind normalization and the per-kind required\n * flags in one place.\n */\n\nimport type { SurfaceKind } from \"./surface-provision.js\";\n\nexport interface SurfaceFlags {\n zone?: string;\n height?: string;\n coverImage?: string;\n egressProxy?: boolean;\n}\n\nexport const EGRESS_PROXY_FLAG_DESCRIPTION =\n \"Ask Fluid to let the droplet reach external systems such as Exigo through its egress proxy\";\n\n/**\n * `--egress-proxy` only means something for a droplet: only a droplet's\n * installation token authenticates to the proxy. Refuse it on other kinds\n * rather than silently ignoring it.\n */\nexport function egressProxyFromFlags(\n kind: SurfaceKind,\n flags: Pick<SurfaceFlags, \"egressProxy\">,\n): boolean | undefined {\n if (!flags.egressProxy) return undefined;\n if (kind !== \"droplet\") {\n throw new Error(\n \"`--egress-proxy` applies to droplets only — drop it, or use `--kind droplet`.\",\n );\n }\n return true;\n}\n\nconst KIND_ALIASES: Record<string, SurfaceKind> = {\n droplet: \"droplet\",\n droplets: \"droplet\",\n \"drop-zone\": \"drop-zone\",\n drop_zone: \"drop-zone\",\n dropzone: \"drop-zone\",\n zone: \"drop-zone\",\n \"mobile-embed\": \"mobile-embed\",\n mobile_embed: \"mobile-embed\",\n mobile: \"mobile-embed\",\n \"mobile-widget\": \"mobile-embed\",\n mobilewidget: \"mobile-embed\",\n};\n\nexport const SURFACE_KIND_LIST = \"droplet, drop-zone, mobile-embed\";\n\nexport function parseSurfaceKind(raw: string): SurfaceKind {\n const hit = KIND_ALIASES[raw.trim().toLowerCase()];\n if (!hit) {\n throw new Error(\n `Unknown surface kind \"${raw}\". Use one of: ${SURFACE_KIND_LIST}.`,\n );\n }\n return hit;\n}\n\nfunction parseHeight(raw: string | undefined): number | undefined {\n if (raw == null) return undefined;\n const n = Number(raw);\n if (!Number.isInteger(n) || n <= 0) {\n throw new Error(\n `Invalid --height \"${raw}\". Must be a positive integer (px).`,\n );\n }\n return n;\n}\n\n/**\n * Map CLI flags to the kind-specific `provisionSurface` options, enforcing the\n * per-kind requirements the API needs:\n * - drop-zone requires `--zone <page__zone>` (else it never renders).\n * - mobile-embed requires `--cover-image <url>` (the API 422s without one; the\n * CLI has no DAM picker, so the user must supply a url).\n */\nexport function surfaceProvisionOptionsFromFlags(\n kind: SurfaceKind,\n flags: SurfaceFlags,\n): { zone?: string; heightPx?: number; coverImageUrl?: string } {\n const heightPx = parseHeight(flags.height);\n if (kind === \"drop-zone\") {\n if (!flags.zone?.trim()) {\n throw new Error(\n \"A drop-zone needs a slot — pass `--zone <page__zone>` (e.g. --zone checkout__below_total).\",\n );\n }\n return { zone: flags.zone.trim(), heightPx };\n }\n if (kind === \"mobile-embed\") {\n if (!flags.coverImage?.trim()) {\n throw new Error(\n \"A mobile-embed needs a cover image — pass `--cover-image <url>` (the mobile API requires one; the CLI has no image picker).\",\n );\n }\n return { coverImageUrl: flags.coverImage.trim(), heightPx };\n }\n return {};\n}\n","/** Shared human-readable summary for a surface provision result. */\n\nimport chalk from \"chalk\";\nimport type { SurfaceProvisionResult } from \"./surface-provision.js\";\n\nconst KIND_LABEL: Record<SurfaceProvisionResult[\"kind\"], string> = {\n droplet: \"droplet\",\n \"drop-zone\": \"drop zone\",\n \"mobile-embed\": \"mobile embed\",\n};\n\nexport function printSurfaceSummary(result: SurfaceProvisionResult): void {\n const ref =\n result.surface.uuid ??\n (result.surface.id != null ? `#${result.surface.id}` : \"(unknown)\");\n const state = result.surfaceCreated ? \"created\" : \"existing\";\n console.log(\n chalk.dim(\n ` ${KIND_LABEL[result.kind]}: ${ref} (${state}) · integration ${result.integrationPoint.id}`,\n ),\n );\n if (result.kind === \"droplet\" && result.installation) {\n const ref = result.installation.uuid ?? \"(uuid unknown)\";\n console.log(\n chalk.dim(\n ` installation: ${ref} (${result.installation.created ? \"installed\" : \"already installed\"})`,\n ),\n );\n }\n if (result.kind === \"droplet\" && result.managedEnv) {\n if (result.managedEnv.ready) {\n console.log(chalk.dim(\" env: managed keys provisioned ✓\"));\n } else {\n console.log(\n chalk.yellow(\n \" env: redeploy in progress — run `fluid mist env list` shortly to confirm managed keys\",\n ),\n );\n }\n }\n if (result.kind === \"droplet\" && result.egressProxy) {\n console.log(\n result.egressProxy.requested\n ? chalk.dim(\" egress proxy: requested ✓\")\n : chalk.yellow(\n \" egress proxy: not requested — only a company admin can ask for it, or this Fluid predates it\",\n ),\n );\n }\n}\n","/**\n * `fluid mist create` — provision a new mist, optionally creating and attaching\n * a surface in one step. All attach paths go through integration points\n * (hostable is retired — #19684/#19687).\n *\n * --droplet [name] Create a NEW droplet and attach it. Bare →\n * droplet name defaults to the mist name. The\n * \"one command that sets everything up\" path.\n * --kind <kind> [flags] Create + attach a surface of any kind:\n * droplet | drop-zone | mobile-embed. Kind flags:\n * drop-zone --zone <page__zone> --height <px>\n * mobile-embed --cover-image <url> --height <px>\n * --hostable droplet:<uuid> Attach an EXISTING droplet by its drp_… uuid.\n * --egress-proxy With a droplet: let it reach external systems\n * such as Exigo through Fluid's egress proxy.\n *\n * With none of these, creates a plain standalone mist.\n */\n\nimport chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport { createMistClient } from \"../api/client.js\";\nimport { createMist } from \"../api/mists.js\";\nimport { runAction } from \"../lib/error-handler.js\";\nimport { colorState, mistLabel, printJson } from \"../lib/output.js\";\nimport { parseDropletRef } from \"../lib/hostable.js\";\nimport { createOraReporter } from \"../lib/ora-reporter.js\";\nimport { createSilentReporter } from \"../lib/step-reporter.js\";\nimport {\n provisionSurface,\n type SurfaceKind,\n type SurfaceProvisionResult,\n} from \"../lib/surface-provision.js\";\nimport {\n EGRESS_PROXY_FLAG_DESCRIPTION,\n egressProxyFromFlags,\n parseSurfaceKind,\n surfaceProvisionOptionsFromFlags,\n type SurfaceFlags,\n} from \"../lib/surface-flags.js\";\nimport { printSurfaceSummary } from \"../lib/surface-summary.js\";\nimport type { MistCreateRequest, MistShowResponse } from \"../types.js\";\n\ninterface Opts extends SurfaceFlags {\n name?: string;\n /** Create + attach a new droplet. Bare (true) or a name override. */\n droplet?: string | boolean;\n /** Create + attach a new surface of this kind. */\n kind?: string;\n /** Attach an existing droplet: `droplet:<uuid>`. */\n hostable?: string;\n mistKind?: \"next_app\";\n wait?: boolean;\n json?: boolean;\n}\n\nexport const createCommand = new Command(\"create\")\n .description(\n \"Provision a new mist (optionally create + attach a surface in one step)\",\n )\n .option(\n \"--droplet [name]\",\n \"Create a droplet and attach it. Bare uses the mist name; pass a value to override.\",\n )\n .option(\n \"--kind <kind>\",\n \"Create + attach a surface: droplet | drop-zone | mobile-embed\",\n )\n .option(\n \"--hostable <ref>\",\n \"Attach an existing droplet by uuid: droplet:<uuid> (e.g. droplet:drp_abc123)\",\n )\n .option(\"--name <label>\", \"Display label for the mist (and the surface)\")\n .option(\n \"--zone <slot>\",\n \"drop-zone slot id: page__zone (e.g. checkout__below_total)\",\n )\n .option(\"--height <px>\", \"Embed height in px (drop-zone + mobile-embed)\")\n .option(\n \"--cover-image <url>\",\n \"Mobile-embed cover image url (required for mobile-embed)\",\n )\n .option(\"--egress-proxy\", EGRESS_PROXY_FLAG_DESCRIPTION)\n .option(\"--mist-kind <kind>\", \"Mist kind (default: next_app)\")\n .option(\"--no-wait\", \"Don't wait for managed env keys to appear after attach\")\n .option(\"--json\", \"Emit raw JSON\")\n .action((opts: Opts) =>\n runAction(async () => {\n const wantDroplet = opts.droplet !== undefined;\n const wantKind = !!opts.kind;\n const wantHostable = !!opts.hostable;\n if ([wantDroplet, wantKind, wantHostable].filter(Boolean).length > 1) {\n throw new Error(\n \"Pass only one of `--droplet`, `--kind`, or `--hostable` — they're mutually exclusive.\",\n );\n }\n\n // Resolve the surface kind + attach mode before we create the mist so\n // flag errors surface immediately (no orphan mist on a typo).\n let kind: SurfaceKind | undefined;\n let existingDropletUuid: string | undefined;\n let nameOverride: string | undefined;\n if (wantDroplet) {\n kind = \"droplet\";\n nameOverride =\n typeof opts.droplet === \"string\" ? opts.droplet.trim() : undefined;\n } else if (wantKind) {\n kind = parseSurfaceKind(opts.kind!);\n } else if (wantHostable) {\n kind = \"droplet\";\n existingDropletUuid = parseDropletRef(opts.hostable!);\n }\n if (opts.egressProxy && !kind) {\n throw new Error(\n \"`--egress-proxy` needs a droplet — add `--droplet`, `--kind droplet`, or `--hostable droplet:<uuid>`.\",\n );\n }\n const egressProxy = kind ? egressProxyFromFlags(kind, opts) : undefined;\n\n // The API requires `kind`; default to next_app (the only kind today).\n const mistBody: MistCreateRequest[\"mist\"] = {\n kind: opts.mistKind ?? \"next_app\",\n };\n if (opts.name?.trim()) mistBody.name = opts.name.trim();\n\n const client = createMistClient();\n const reporter = opts.json ? createSilentReporter() : createOraReporter();\n\n // 1. Create the mist itself (synchronous; 30–90s).\n reporter.start(\"Setting up your Mist Cloud environment…\");\n let created: MistShowResponse;\n try {\n created = await createMist(client, { mist: mistBody });\n } catch (err) {\n reporter.fail(\"Provisioning failed\");\n throw err;\n }\n const mist = created.mist;\n reporter.succeed(`Provisioned ${mist.slug}`);\n\n // 2. Optionally create/attach the surface via the shared engine.\n let provision: SurfaceProvisionResult | undefined;\n if (kind) {\n provision = await provisionSurface(client, {\n slugOrId: mist.slug,\n kind,\n name: nameOverride ?? opts.name?.trim(),\n existingUuid: existingDropletUuid,\n egressProxy,\n verifyEnv: opts.wait !== false,\n reporter,\n ...surfaceProvisionOptionsFromFlags(kind, opts),\n });\n }\n\n if (opts.json) {\n return printJson(provision ? { mist, provision } : created);\n }\n\n const label = mistLabel(mist.name, mist.vendor_name);\n console.log();\n console.log(\n `${label} ${chalk.dim(mist.slug)} ${colorState(mist.state)}`,\n );\n if (mist.public_url) console.log(` ${chalk.cyan(mist.public_url)}`);\n if (provision) printSurfaceSummary(provision);\n console.log();\n console.log(chalk.dim(`Clone with: fluid mist clone ${mist.slug}`));\n }),\n );\n","/**\n * `fluid mist attach --kind <kind> [flags] [slug]` — create a surface and wire\n * it to an existing mist. One command for all three kinds:\n *\n * --kind droplet (managed-env-provisioning droplet)\n * --kind drop-zone --zone page__zone [--height <px>]\n * --kind mobile-embed --cover-image <url> [--height <px>]\n *\n * Attach an EXISTING surface instead of creating one with `--existing <ref>`:\n * droplet/drop-zone → droplet:drp_… / drop-zone:dz_… / bare uuid\n * mobile-embed → mobile-embed:<numericId>\n *\n * `--egress-proxy` (droplet only) lets the droplet reach external systems\n * such as Exigo through Fluid's egress proxy — on a new droplet, and on an\n * existing or already-attached one.\n *\n * Idempotent for droplet + attach-existing (skips when already linked).\n */\n\nimport chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport { createMistClient } from \"../api/client.js\";\nimport { runAction } from \"../lib/error-handler.js\";\nimport { resolveSlug } from \"../lib/slug-resolver.js\";\nimport { printJson } from \"../lib/output.js\";\nimport { createOraReporter } from \"../lib/ora-reporter.js\";\nimport { createSilentReporter } from \"../lib/step-reporter.js\";\nimport { provisionSurface } from \"../lib/surface-provision.js\";\nimport {\n EGRESS_PROXY_FLAG_DESCRIPTION,\n egressProxyFromFlags,\n parseSurfaceKind,\n surfaceProvisionOptionsFromFlags,\n SURFACE_KIND_LIST,\n type SurfaceFlags,\n} from \"../lib/surface-flags.js\";\nimport { printSurfaceSummary } from \"../lib/surface-summary.js\";\n\ninterface Opts extends SurfaceFlags {\n kind?: string;\n name?: string;\n existing?: string;\n wait?: boolean;\n json?: boolean;\n}\n\n/** Parse `--existing <ref>` into a uuid or numeric id for the given kind. */\nfunction parseExisting(\n kind: ReturnType<typeof parseSurfaceKind>,\n raw: string,\n): { existingUuid?: string; existingId?: number } {\n // Strip an optional `kind:` prefix (droplet:/drop-zone:/mobile-embed:).\n const value = raw.includes(\":\") ? raw.slice(raw.lastIndexOf(\":\") + 1) : raw;\n const trimmed = value.trim();\n if (!trimmed) throw new Error(`Empty --existing reference \"${raw}\".`);\n if (kind === \"mobile-embed\") {\n const id = Number(trimmed);\n if (!Number.isInteger(id) || id <= 0) {\n throw new Error(\n `mobile-embed attaches by numeric id — pass \\`--existing <id>\\` (got \"${raw}\").`,\n );\n }\n return { existingId: id };\n }\n return { existingUuid: trimmed };\n}\n\nexport const attachCommand = new Command(\"attach\")\n .description(\n `Create a surface (${SURFACE_KIND_LIST}) and wire it to an existing mist`,\n )\n .argument(\"[slug]\", \"Mist slug (defaults to the .mist file in CWD)\")\n .requiredOption(\"--kind <kind>\", `Surface kind: ${SURFACE_KIND_LIST}`)\n .option(\"--name <label>\", \"Surface name (defaults to the mist's name)\")\n .option(\n \"--zone <slot>\",\n \"drop-zone slot id: page__zone (e.g. checkout__below_total)\",\n )\n .option(\"--height <px>\", \"Embed height in px (drop-zone + mobile-embed)\")\n .option(\n \"--cover-image <url>\",\n \"Mobile-embed cover image url (required for mobile-embed)\",\n )\n .option(\n \"--existing <ref>\",\n \"Attach an existing surface instead of creating one (uuid, or numeric id for mobile-embed)\",\n )\n .option(\"--egress-proxy\", EGRESS_PROXY_FLAG_DESCRIPTION)\n .option(\"--no-wait\", \"Don't wait for managed env keys to appear after attach\")\n .option(\"--json\", \"Emit raw JSON\")\n .action((slug: string | undefined, opts: Opts) =>\n runAction(async () => {\n const kind = parseSurfaceKind(opts.kind!);\n const egressProxy = egressProxyFromFlags(kind, opts);\n const resolved = resolveSlug(slug);\n const client = createMistClient();\n const reporter = opts.json ? createSilentReporter() : createOraReporter();\n\n const existing = opts.existing\n ? parseExisting(kind, opts.existing)\n : undefined;\n\n const result = await provisionSurface(client, {\n slugOrId: resolved,\n kind,\n name: opts.name?.trim(),\n egressProxy,\n verifyEnv: opts.wait !== false,\n reporter,\n ...existing,\n // Kind flags only apply when creating a new surface.\n ...(existing ? {} : surfaceProvisionOptionsFromFlags(kind, opts)),\n });\n\n if (opts.json) return printJson(result);\n\n console.log();\n console.log(\n `${chalk.bold(result.mist.name ?? result.mist.slug)} ${chalk.dim(result.mist.slug)}`,\n );\n printSurfaceSummary(result);\n }),\n );\n","/**\n * `fluid mist attach-droplet [name] [slug]` — droplet shortcut for\n * `fluid mist attach --kind droplet`. Creates a droplet and wires it to an\n * existing standalone mist (provisioning the managed env). Idempotent: if a\n * droplet is already attached, it skips create + attach and just verifies env.\n *\n * name Optional droplet name (defaults to the mist's name).\n * slug Optional mist slug (defaults to the `.mist` file in CWD).\n *\n * `--egress-proxy` lets the droplet reach external systems such as Exigo\n * through Fluid's egress proxy, including one that was already attached.\n */\n\nimport chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport { createMistClient } from \"../api/client.js\";\nimport { runAction } from \"../lib/error-handler.js\";\nimport { resolveSlug } from \"../lib/slug-resolver.js\";\nimport { printJson } from \"../lib/output.js\";\nimport { createOraReporter } from \"../lib/ora-reporter.js\";\nimport { createSilentReporter } from \"../lib/step-reporter.js\";\nimport { provisionSurface } from \"../lib/surface-provision.js\";\nimport { printSurfaceSummary } from \"../lib/surface-summary.js\";\nimport { EGRESS_PROXY_FLAG_DESCRIPTION } from \"../lib/surface-flags.js\";\n\ninterface Opts {\n egressProxy?: boolean;\n wait?: boolean;\n json?: boolean;\n}\n\nexport const attachDropletCommand = new Command(\"attach-droplet\")\n .description(\n \"Create a droplet and wire it to an existing standalone mist (idempotent)\",\n )\n .argument(\"[name]\", \"Droplet name (defaults to the mist's name)\")\n .argument(\"[slug]\", \"Mist slug (defaults to the .mist file in CWD)\")\n .option(\"--egress-proxy\", EGRESS_PROXY_FLAG_DESCRIPTION)\n .option(\"--no-wait\", \"Don't wait for managed env keys to appear after attach\")\n .option(\"--json\", \"Emit raw JSON\")\n .action((name: string | undefined, slug: string | undefined, opts: Opts) =>\n runAction(async () => {\n const resolved = resolveSlug(slug);\n const client = createMistClient();\n const reporter = opts.json ? createSilentReporter() : createOraReporter();\n\n const result = await provisionSurface(client, {\n slugOrId: resolved,\n kind: \"droplet\",\n name: name?.trim() || undefined,\n egressProxy: opts.egressProxy === true || undefined,\n verifyEnv: opts.wait !== false,\n reporter,\n });\n\n if (opts.json) return printJson(result);\n\n console.log();\n console.log(\n `${chalk.bold(result.mist.name ?? result.mist.slug)} ${chalk.dim(result.mist.slug)}`,\n );\n printSurfaceSummary(result);\n }),\n );\n","/**\n * `fluid mist update` — mutate an existing mist. Hostable is retired\n * (#19684/#19687), so attach/detach operate on integration points:\n *\n * --name <label> Rename the mist (PATCH /mists/:id).\n * --hostable droplet:<uuid> Attach a droplet by uuid. No-ops if that same\n * droplet is already attached; replaces it if a\n * different droplet is attached (droplet is\n * 1-per-mist — it provisions the managed env).\n * --detach Remove the droplet integration point.\n *\n * The integration-point link is immutable (no update route, no `active`\n * field) — enablement lives on the surface record, so there's no toggle here.\n * All ops re-derive state from the integration-points index (which echoes\n * `integratable_uuid` since #19689) and only make the calls that change it.\n */\n\nimport chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport { createMistClient } from \"../api/client.js\";\nimport { updateMist } from \"../api/mists.js\";\nimport {\n createIntegrationPoint,\n deleteIntegrationPoint,\n findDropletIntegrationPoint,\n listIntegrationPoints,\n} from \"../api/integration_points.js\";\nimport { runAction } from \"../lib/error-handler.js\";\nimport { resolveSlug } from \"../lib/slug-resolver.js\";\nimport { parseDropletRef } from \"../lib/hostable.js\";\nimport { colorState, mistLabel, printJson } from \"../lib/output.js\";\nimport type { MistShowResponse } from \"../types.js\";\n\ninterface Opts {\n name?: string;\n hostable?: string;\n detach?: boolean;\n json?: boolean;\n}\n\nexport const updateCommand = new Command(\"update\")\n .description(\"Rename a mist, or attach/detach its droplet\")\n .argument(\"[slug]\", \"The mist slug (defaults to .mist file in CWD)\")\n .option(\"--name <label>\", \"Set the display label\")\n .option(\n \"--hostable <ref>\",\n \"Attach a droplet: droplet:<uuid> (e.g. droplet:drp_abc123)\",\n )\n .option(\"--detach\", \"Remove the droplet integration point\")\n .option(\"--json\", \"Emit raw API JSON\")\n .action((slug: string | undefined, opts: Opts) =>\n runAction(async () => {\n if (opts.hostable && opts.detach) {\n throw new Error(\n \"Pass either `--hostable` OR `--detach`, not both — they're mutually exclusive.\",\n );\n }\n if (!opts.name && !opts.hostable && !opts.detach) {\n throw new Error(\n \"Nothing to do — pass `--name`, `--hostable`, or `--detach`.\",\n );\n }\n\n const resolved = resolveSlug(slug);\n const client = createMistClient();\n const notes: string[] = [];\n\n // Rename (a plain mist PATCH). Don't early-return in `--json` mode — the\n // integration-point ops below still run; the rename response is folded\n // into the final JSON payload instead.\n let renameResp: MistShowResponse | undefined;\n if (opts.name?.trim()) {\n renameResp = await updateMist(client, resolved, {\n mist: { name: opts.name.trim() },\n });\n if (!opts.json) {\n const m = renameResp.mist;\n console.log(\n `${mistLabel(m.name, m.vendor_name)} ${chalk.dim(m.slug)} ${colorState(m.state)}`,\n );\n }\n }\n\n if (opts.hostable || opts.detach) {\n const existing = findDropletIntegrationPoint(\n (await listIntegrationPoints(client, resolved)).integration_points,\n );\n\n if (opts.hostable) {\n const dropletUuid = parseDropletRef(opts.hostable);\n if (existing && existing.integratable_uuid === dropletUuid) {\n notes.push(\n `droplet ${dropletUuid} already attached (integration ${existing.id})`,\n );\n } else {\n if (existing) {\n // A different droplet is attached — replace it (droplet is\n // 1-per-mist since it provisions the managed env).\n await deleteIntegrationPoint(client, resolved, existing.id);\n notes.push(`replaced integration ${existing.id}`);\n }\n const ip = await createIntegrationPoint(client, resolved, {\n kind: \"droplet\",\n integratable_uuid: dropletUuid,\n });\n notes.push(\n `attached droplet ${dropletUuid} (integration ${ip.integration_point.id})`,\n );\n }\n } else if (opts.detach) {\n if (existing) {\n await deleteIntegrationPoint(client, resolved, existing.id);\n notes.push(`detached droplet (removed integration ${existing.id})`);\n } else {\n notes.push(\"already standalone (no droplet integration)\");\n }\n }\n }\n\n if (opts.json) {\n return printJson(\n renameResp\n ? { ...renameResp, slug: resolved, notes }\n : { slug: resolved, notes },\n );\n }\n for (const note of notes) console.log(chalk.dim(` ${note}`));\n }),\n );\n","/**\n * `fluid mist delete` — scheduled teardown 15 days out (spec §3 \"15-day\n * grace period\"). DO NOT poll for completion.\n */\n\nimport chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport prompts from \"prompts\";\nimport { createMistClient } from \"../api/client.js\";\nimport { deleteMist, showMist } from \"../api/mists.js\";\nimport { runAction } from \"../lib/error-handler.js\";\nimport { resolveSlug } from \"../lib/slug-resolver.js\";\nimport { formatAbsolute, printJson } from \"../lib/output.js\";\n\ninterface Opts {\n yes?: boolean;\n json?: boolean;\n}\n\nexport const deleteCommand = new Command(\"delete\")\n .description(\"Schedule a mist for destruction (15-day grace window)\")\n .argument(\"[slug]\", \"The mist slug (defaults to .mist file in CWD)\")\n .option(\"-y, --yes\", \"Skip confirmation prompt\")\n .option(\"--json\", \"Emit raw API JSON\")\n .action((slug: string | undefined, opts: Opts) =>\n runAction(async () => {\n const resolved = resolveSlug(slug);\n const client = createMistClient();\n\n if (!opts.yes) {\n // Fetch first so the prompt shows what's about to happen.\n const { mist } = await showMist(client, resolved);\n const fifteenDaysOut = new Date(Date.now() + 15 * 86_400_000)\n .toISOString()\n .slice(0, 10);\n console.log();\n console.log(\n `About to schedule ${chalk.bold(mist.name ?? mist.vendor_name ?? mist.slug)} (${chalk.dim(mist.slug)}) for destruction on ${chalk.yellow(fifteenDaysOut)}.`,\n );\n console.log(\n chalk.dim(\n `Vendor resources stay alive until then; run \\`fluid mist restore ${mist.slug}\\` to undo.`,\n ),\n );\n const { confirmed } = await prompts(\n {\n type: \"confirm\",\n name: \"confirmed\",\n message: \"Continue?\",\n initial: false,\n },\n { onCancel: () => process.exit(130) },\n );\n if (!confirmed) {\n console.log(chalk.dim(\"Aborted.\"));\n process.exit(0);\n }\n }\n\n const { mist } = await deleteMist(client, resolved);\n if (opts.json) return printJson({ mist });\n console.log(\n chalk.magenta(\n `Scheduled for destruction on ${mist.scheduled_destroy_at ? formatAbsolute(mist.scheduled_destroy_at) : \"the 15-day mark\"}.`,\n ),\n );\n console.log(\n chalk.dim(\n `Vendor resources stay alive until then; run \\`fluid mist restore ${mist.slug}\\` to undo.`,\n ),\n );\n }),\n );\n","/** `fluid mist restore` — un-delete within the 15-day window. Spec §3. */\n\nimport chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport { createMistClient } from \"../api/client.js\";\nimport { restoreMist } from \"../api/mists.js\";\nimport { runAction } from \"../lib/error-handler.js\";\nimport { resolveSlug } from \"../lib/slug-resolver.js\";\nimport { colorState, printJson } from \"../lib/output.js\";\n\ninterface Opts {\n json?: boolean;\n}\n\nexport const restoreCommand = new Command(\"restore\")\n .description(\"Cancel a scheduled mist destruction (15-day window)\")\n .argument(\"[slug]\", \"The mist slug (defaults to .mist file in CWD)\")\n .option(\"--json\", \"Emit raw API JSON\")\n .action((slug: string | undefined, opts: Opts) =>\n runAction(async () => {\n const resolved = resolveSlug(slug);\n const client = createMistClient();\n const { mist } = await restoreMist(client, resolved);\n if (opts.json) return printJson({ mist });\n console.log(\n `Restored: ${mist.public_url ? chalk.cyan(mist.public_url) : (mist.name ?? mist.vendor_name ?? mist.slug)} (${colorState(mist.state)})`,\n );\n }),\n );\n","/**\n * `POST /git_credential` — mints a short-lived (~1h) remote URL with an\n * embedded installation token. The CLI calls this immediately before any\n * git op and discards the URL after. NEVER persist it.\n */\n\nimport type { MistClient } from \"./client.js\";\nimport type { GitCredentialResponse } from \"../types.js\";\nimport { resolveMistId } from \"./id-resolver.js\";\n\nexport async function createGitCredential(\n client: MistClient,\n slug: string,\n): Promise<GitCredentialResponse> {\n const id = await resolveMistId(client, slug);\n return client.post<GitCredentialResponse>(\n `/api/v202604/mists/${id}/git_credential`,\n );\n}\n","/**\n * After `fluid mist clone`, list the production env keys the checkout has no\n * local value for, as placeholders in `.env.local`.\n *\n * A fresh clone has no env file, so the app fails at the first missing key\n * with an error that rarely names it. Writing the keys (never the values —\n * Vercel returns ciphertext, and secrets don't belong on disk unasked) tells\n * the user exactly what to fill in. Mist-managed keys (DATABASE_URL,\n * FLUID_*) are left out: local dev provides its own.\n *\n * The placeholders are commented out on purpose. An uncommented `KEY=`\n * defines the variable as an empty string, which `process.env.KEY ?? default`\n * and Next.js's NEXT_PUBLIC_ inlining treat differently from unset — so an\n * empty placeholder could break an app that works with the key absent.\n */\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { EnvVar } from \"../types.js\";\n\n/** Env files Next.js and Vite load in local development. */\nexport const LOCAL_ENV_FILES = [\n \".env\",\n \".env.local\",\n \".env.development\",\n \".env.development.local\",\n] as const;\n\nconst ENV_ASSIGNMENT = /^\\s*#?\\s*(?:export\\s+)?([A-Za-z_][A-Za-z0-9_]*)\\s*=/;\n\n/** Keys assigned (or already listed as a placeholder) in the local env files. */\nexport function localEnvKeys(dir: string): Set<string> {\n const keys = new Set<string>();\n for (const name of LOCAL_ENV_FILES) {\n const file = join(dir, name);\n if (!existsSync(file)) continue;\n for (const line of readFileSync(file, \"utf8\").split(/\\r?\\n/)) {\n const key = ENV_ASSIGNMENT.exec(line)?.[1];\n if (key) keys.add(key);\n }\n }\n return keys;\n}\n\n/** Production keys a user set that the checkout doesn't define locally. */\nexport function missingProductionKeys(\n envVars: readonly EnvVar[],\n localKeys: ReadonlySet<string>,\n): string[] {\n const missing = new Set<string>();\n for (const v of envVars) {\n if (v.managed_by === \"mist\") continue;\n if (!v.targets.includes(\"production\")) continue;\n if (localKeys.has(v.key)) continue;\n missing.add(v.key);\n }\n return [...missing].sort();\n}\n\n/** The block appended to `.env.local`, or \"\" when nothing is missing. */\nexport function renderEnvPlaceholders(keys: readonly string[]): string {\n if (keys.length === 0) return \"\";\n return [\n \"# Set in production but not here. Uncomment and fill in the ones this\",\n \"# app needs locally (values are never copied — see `fluid mist env pull`).\",\n ...keys.map((key) => `# ${key}=`),\n \"\",\n ].join(\"\\n\");\n}\n","/**\n * `fluid mist clone <slug>` — mints a fresh git credential and clones\n * into ./<vendor-name>/, writes a `.mist` marker file. Spec §3 + §11.\n */\n\nimport chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport ora from \"ora\";\nimport { appendFileSync, existsSync, readFileSync } from \"node:fs\";\nimport { join, resolve } from \"node:path\";\nimport { createMistClient } from \"../api/client.js\";\nimport { showMist } from \"../api/mists.js\";\nimport { createGitCredential } from \"../api/git_credential.js\";\nimport { runAction } from \"../lib/error-handler.js\";\nimport { resolveSlug, writeMistFile } from \"../lib/slug-resolver.js\";\nimport { ensureGitAvailable, gitClone, isGitIgnored } from \"../lib/git.js\";\nimport { listEnvVars } from \"../api/env_vars.js\";\nimport {\n localEnvKeys,\n missingProductionKeys,\n renderEnvPlaceholders,\n} from \"../lib/env-placeholders.js\";\nimport type { MistClient } from \"../api/client.js\";\n\ninterface Opts {\n dir?: string;\n}\n\n/**\n * Render a path for the copy-pasteable `cd` hint so the printed command\n * enters the directory that was actually created:\n * - Prefixes `./` when the path starts with `~` (blocks tilde expansion —\n * the clone went into a literal `~…` directory under CWD) or `-` (stops\n * `cd` from parsing it as an option).\n * - Otherwise single-quotes anything beyond safe characters, escaping\n * embedded single quotes POSIX-style (`'\\''`).\n */\nexport function quotePathIfNeeded(path: string): string {\n const prefixed = /^[~-]/.test(path) ? `./${path}` : path;\n if (/^[A-Za-z0-9_./-]+$/.test(prefixed)) {\n return prefixed;\n }\n return `'${prefixed.replace(/'/g, \"'\\\\''\")}'`;\n}\n\nexport const cloneCommand = new Command(\"clone\")\n .description(\"Clone the mist's GitHub repo (mints a fresh token per call)\")\n .argument(\"[slug]\", \"The mist slug (defaults to .mist file in CWD)\")\n .option(\"--dir <dir>\", \"Target directory (defaults to ./<vendor-name>/)\")\n .action((slug: string | undefined, opts: Opts) =>\n runAction(async () => {\n await ensureGitAvailable();\n const resolvedSlug = resolveSlug(slug);\n const client = createMistClient();\n\n const spinner = ora(\"Resolving mist…\").start();\n const { mist } = await showMist(client, resolvedSlug);\n spinner.text = \"Minting git credential…\";\n const { git_credential: cred } = await createGitCredential(\n client,\n mist.slug,\n );\n // `vendor_name` is frequently null in production (it's only set for\n // older records) — fall back through `name` to the always-present\n // `slug` so a bare `fluid mist clone <slug>` never crashes trying to\n // resolve/join an undefined path.\n const dirName = mist.name ?? mist.vendor_name ?? mist.slug;\n const targetDir = resolve(opts.dir ?? dirName);\n if (existsSync(targetDir)) {\n spinner.fail(\n `Directory ${targetDir} already exists. Pass --dir or remove it first.`,\n );\n process.exit(1);\n }\n spinner.text = `Cloning into ${dirName}/…`;\n try {\n // Pass the token-bearing URL inline — never written to .git/config.\n await gitClone(cred.remote_url, targetDir);\n } catch (err) {\n spinner.fail(\"git clone failed\");\n throw err;\n }\n // Drop a `.mist` so future commands run in this dir can skip the slug.\n writeMistFile(targetDir, mist);\n spinner.succeed(`Cloned into ${chalk.cyan(targetDir)}`);\n await writeEnvPlaceholders(client, mist.id || mist.slug, targetDir);\n // Display the actual directory in the cd hint, not the mist's display name.\n // Use opts.dir if provided by the user, otherwise use relative path.\n const displayPath = opts.dir ?? join(\".\", dirName);\n console.log(chalk.dim(` cd ${quotePathIfNeeded(displayPath)}`));\n if (mist.public_url)\n console.log(chalk.dim(` ${chalk.cyan(mist.public_url)}`));\n }),\n );\n\n/**\n * List production env keys this checkout has no local value for as\n * commented placeholders in `.env.local` — keys only, never values. Only\n * when `.env.local` is git-ignored, so the file can't be committed.\n * Best-effort: a clone never fails over it.\n */\nexport async function writeEnvPlaceholders(\n client: MistClient,\n slug: string,\n targetDir: string,\n): Promise<number> {\n try {\n if (!(await isGitIgnored(targetDir, \".env.local\"))) return 0;\n const { env_vars } = await listEnvVars(client, slug);\n const keys = missingProductionKeys(env_vars ?? [], localEnvKeys(targetDir));\n const block = renderEnvPlaceholders(keys);\n if (!block) return 0;\n const target = join(targetDir, \".env.local\");\n const existing = existsSync(target) ? readFileSync(target, \"utf8\") : \"\";\n const separator =\n existing.length > 0 && !existing.endsWith(\"\\n\") ? \"\\n\" : \"\";\n appendFileSync(\n target,\n `${separator}${existing ? \"\\n\" : \"\"}${block}`,\n \"utf8\",\n );\n console.log(\n chalk.dim(\n ` Listed ${keys.length} production env key(s) in .env.local to fill in: ${keys.join(\", \")}`,\n ),\n );\n return keys.length;\n } catch {\n return 0;\n }\n}\n","/** Deployments — `--watch` polls these. Spec §4. */\n\nimport type { MistClient } from \"./client.js\";\nimport type {\n DeploymentShowResponse,\n DeploymentsListResponse,\n} from \"../types.js\";\nimport { resolveMistId } from \"./id-resolver.js\";\n\nexport async function listDeployments(\n client: MistClient,\n slug: string,\n limit?: number,\n): Promise<DeploymentsListResponse> {\n const id = await resolveMistId(client, slug);\n return client.get<DeploymentsListResponse>(\n `/api/v202604/mists/${id}/deployments`,\n limit != null ? { limit } : undefined,\n );\n}\n\nexport async function showDeployment(\n client: MistClient,\n slug: string,\n deploymentId: string,\n): Promise<DeploymentShowResponse> {\n const id = await resolveMistId(client, slug);\n return client.get<DeploymentShowResponse>(\n `/api/v202604/mists/${id}/deployments/${deploymentId}`,\n );\n}\n","/**\n * `GET /api/me` — fetch the authenticated user's identity. Used by the\n * push command to stamp commits in the canonical GitSync format\n * (`<name>: <action> · <timestamp> (<public_id>)`, see\n * `@fluid-app/fluid-cli`'s git-sync/commit-subject.ts) so deploy history\n * can be traced back to a real person even when the underlying git author\n * falls through to the synthetic bot identity.\n */\n\nimport type { MistClient } from \"./client.js\";\n\nexport interface MistMe {\n id: number;\n public_id: string;\n full_name: string | null;\n first_name: string | null;\n last_name: string | null;\n email: string | null;\n}\n\ninterface MeResponse {\n id: number;\n public_id?: string;\n full_name?: string;\n first_name?: string;\n last_name?: string;\n email?: string;\n}\n\nexport async function fetchCurrentUser(client: MistClient): Promise<MistMe> {\n const raw = await client.get<MeResponse>(\"/api/me\");\n const composed = [raw.first_name, raw.last_name]\n .filter(Boolean)\n .join(\" \")\n .trim();\n const fullName = raw.full_name ?? (composed.length > 0 ? composed : null);\n return {\n id: raw.id,\n public_id: raw.public_id ?? String(raw.id),\n full_name: fullName,\n first_name: raw.first_name ?? null,\n last_name: raw.last_name ?? null,\n email: raw.email ?? null,\n };\n}\n","import { execFile } from \"node:child_process\";\nimport { readFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { promisify } from \"node:util\";\n\nconst execFileAsync = promisify(execFile);\n\n/** The marker Mist Desktop writes when it rolls a checkout back. */\nexport const ROLLBACK_MARKER = \".mist-rollback.json\";\n\n/**\n * \"Roll back to 492c088 — Document reviews env vars\" for a push that carries\n * a fresh rollback marker, so History names what was restored instead of\n * \"Delete 23 files, update 8, add 1\". Null when there is no readable marker;\n * the caller then describes the diff as before.\n */\nexport async function rollbackActionFromMarker(\n cwd: string,\n): Promise<string | null> {\n let restored: unknown;\n try {\n const raw = await readFile(path.join(cwd, ROLLBACK_MARKER), \"utf8\");\n restored = (JSON.parse(raw) as { restored_commit?: unknown })\n .restored_commit;\n } catch {\n return null;\n }\n if (typeof restored !== \"string\" || !/^[0-9a-f]{7,40}$/i.test(restored)) {\n return null;\n }\n let subject = \"\";\n try {\n const { stdout } = await execFileAsync(\n \"git\",\n [\"log\", \"-1\", \"--format=%s\", restored],\n { cwd, windowsHide: true },\n );\n subject = stdout.trim();\n } catch {\n // The version may not be in this clone; the sha alone still says what\n // was restored.\n }\n return rollbackAction(restored, subject);\n}\n\n/** Pure part of {@link rollbackActionFromMarker}, for tests. */\nexport function rollbackAction(sha: string, subject: string): string {\n const short = sha.slice(0, 7);\n const title = subjectAction(subject);\n const base = `Roll back to ${short}`;\n if (!title) return base;\n const full = `${base} — ${title}`;\n return full.length > 72 ? `${full.slice(0, 71).trimEnd()}…` : full;\n}\n\n/** A Git Sync stamp: \"Name: action · YYYY-MM-DD HH:MM:SS (public id)\". */\nconst STAMPED_SUBJECT_RE =\n /^.+?: (.+) · \\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}(?: \\([^)]*\\))?$/;\n\n/** A stamped subject contributes only its action; others pass through. */\nfunction subjectAction(subject: string): string {\n const trimmed = subject.trim();\n return trimmed.match(STAMPED_SUBJECT_RE)?.[1]?.trim() ?? trimmed;\n}\n","/**\n * `fluid mist push [--watch]` — mints a credential, pushes to origin, and\n * (with --watch) polls /deployments until READY/ERROR. Spec §3 + §4.\n */\n\nimport chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport ora from \"ora\";\nimport {\n gitSyncActorFromMe,\n gitSyncCommitSubject,\n resolveGitSyncActor,\n summarizeChanges,\n} from \"@fluid-app/fluid-cli\";\nimport { createMistClient } from \"../api/client.js\";\nimport { createGitCredential } from \"../api/git_credential.js\";\nimport { listDeployments, showDeployment } from \"../api/deployments.js\";\nimport { fetchCurrentUser } from \"../api/me.js\";\nimport { showMist } from \"../api/mists.js\";\nimport { runAction } from \"../lib/error-handler.js\";\nimport { readyDeploymentMessage } from \"../lib/output.js\";\nimport { resolveSlug } from \"../lib/slug-resolver.js\";\nimport {\n ROLLBACK_MARKER,\n rollbackActionFromMarker,\n} from \"../lib/rollback-subject.js\";\nimport {\n ensureGitAvailable,\n gitAheadBehindVsFetchHead,\n gitAutoCommit,\n gitFetch,\n gitMergeFastForward,\n gitPush,\n gitStash,\n gitStashPop,\n getHeadSha,\n hasUncommittedChanges,\n} from \"../lib/git.js\";\n\n/** Used when there's no `-m` and no readable diff to describe. */\nconst FALLBACK_ACTION = \"Publish from Mist Desktop\";\n\ninterface Opts {\n watch?: boolean;\n branch?: string;\n /** Skip the auto-commit step. Power users who manage their own\n * commits can pass --no-commit to fall back to a bare git push. */\n commit?: boolean;\n /** Custom commit message for the auto-commit. Defaults to a\n * timestamped \"Publish from Mist\" line. */\n message?: string;\n}\n\nconst POLL_MS = 2_000;\n// GitHub/hosting webhook queues can occasionally take several minutes before\n// Fluid receives a deployment record. Keep the interactive watch bounded, but\n// don't misclassify a successful authenticated Git push as a failed publish.\nconst LOCATE_TIMEOUT_MS = 2 * 60_000;\nconst READY_TIMEOUT_MS = 5 * 60_000; // 5 minutes for it to flip to ready\nconst PUBLIC_URL_TIMEOUT_MS = 5_000;\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((r) => setTimeout(r, ms));\n}\n\nasync function withTimeout<T>(\n promise: Promise<T>,\n timeoutMs: number,\n): Promise<T> {\n let timer: ReturnType<typeof setTimeout> | null = null;\n try {\n return await Promise.race([\n promise,\n new Promise<never>((_, reject) => {\n timer = setTimeout(\n () => reject(new Error(`timed out after ${timeoutMs / 1000}s`)),\n timeoutMs,\n );\n }),\n ]);\n } finally {\n if (timer) clearTimeout(timer);\n }\n}\n\nexport const pushCommand = new Command(\"push\")\n .description(\n \"Commit working-tree changes and push to the Mist-managed remote (triggers a hosted deployment)\",\n )\n .argument(\"[slug]\", \"The mist slug (defaults to .mist file in CWD)\")\n .option(\"--watch\", \"Poll /deployments until ready or error\")\n .option(\"--branch <name>\", \"Branch to push (default: main)\")\n .option(\n \"--no-commit\",\n \"Skip the auto-commit step (push existing commits only)\",\n )\n .option(\n \"-m, --message <message>\",\n \"Commit message for the auto-commit (default: timestamped Publish)\",\n )\n .action((slug: string | undefined, opts: Opts) =>\n runAction(async () => {\n await ensureGitAvailable();\n const resolved = resolveSlug(slug);\n const client = createMistClient();\n const branch = opts.branch ?? \"main\";\n const cwd = process.cwd();\n\n // Step 0: detect remote drift. Fetch origin/main and check how\n // far HEAD is from it. If a teammate pushed since we last\n // synced, fast-forward in (auto-pull). Stash any dirty files\n // first so the FF can land cleanly; pop after.\n //\n // Diverged histories (we have local commits AND the remote moved)\n // would need a real rebase + conflict resolution — we bail\n // explicitly so the user knows to handle that case in a terminal.\n // For solo developers and non-overlapping concurrent edits, this\n // path Just Works.\n const driftSpinner = ora(\"Checking for remote changes…\").start();\n try {\n // Mint a short-lived credential for the fetch. We'll mint\n // another one later for push (tokens are ~1h but minting\n // again costs nothing and keeps the failure boundaries clear).\n const { git_credential: fetchCred } = await createGitCredential(\n client,\n resolved,\n );\n await gitFetch(cwd, fetchCred.remote_url, branch);\n const { ahead, behind } = await gitAheadBehindVsFetchHead(cwd);\n if (behind > 0 && ahead === 0) {\n // Safe auto-pull case.\n const wasDirty = await hasUncommittedChanges(cwd);\n driftSpinner.text = `Pulling ${behind} commit${behind === 1 ? \"\" : \"s\"} from origin/main…`;\n // Captured before the pull: a pop that collides with the\n // teammate's change resets back here and re-applies the stash,\n // so conflict markers can never reach the auto-commit below.\n const preHead = await getHeadSha(cwd);\n if (wasDirty) {\n await gitStash(cwd, \"mist-push: pre-pull stash\");\n }\n try {\n await gitMergeFastForward(cwd);\n } finally {\n if (wasDirty) {\n await gitStashPop(cwd, preHead); // throws GitError on conflict\n }\n }\n driftSpinner.succeed(\n `Pulled ${behind} commit${behind === 1 ? \"\" : \"s\"} from origin/main`,\n );\n } else if (behind > 0 && ahead > 0) {\n driftSpinner.fail(\n `Your branch and origin/main have diverged (you're ${ahead} ahead, ${behind} behind).`,\n );\n console.error(\n chalk.yellow(\n \" Resolve in a terminal: `git pull --rebase`, fix any conflicts, then re-run Publish.\",\n ),\n );\n process.exit(1);\n } else {\n driftSpinner.succeed(\"Up to date with origin/main\");\n }\n } catch (err) {\n // Wrap fetch/merge errors with context — the spinner already\n // failed via the explicit branch above when we knew enough.\n // Anything that throws past the auto-pull path is unexpected.\n if (\n err instanceof Error &&\n err.message.includes(\"Resolve in a terminal\")\n ) {\n throw err;\n }\n driftSpinner.fail(\"Couldn't sync with origin/main\");\n throw err;\n }\n\n // Step 1: auto-commit working-tree changes (unless --no-commit).\n // Mist Desktop's Publish UX expects \"ship my changes\" to actually\n // include the changes — without this, `git push` is a no-op against\n // a clean HEAD and Vercel never builds.\n const wantAutoCommit = opts.commit !== false;\n if (wantAutoCommit) {\n const dirty = await hasUncommittedChanges(cwd);\n if (dirty) {\n // Stamp the commit with the Fluid user so deploy history\n // traces back to a real person, in the canonical GitSync shape\n // shared with the theme / portal / widget CLIs:\n // \"Mike Tingey: Updated header · 2026-08-06 12:00:00 (0182d21e-…)\"\n // Best-effort: if /api/me fails, hangs, or returns a malformed\n // body we commit as a `Fluid:` system commit — publish is\n // essential, the attribution is not.\n // An explicit `-m` states the INTENT, which no diff can\n // recover — it always wins. Otherwise describe what actually\n // changed, falling back to the old constant for a diff we\n // can't read (summarizeChanges returns null on an empty set).\n const explicit = opts.message?.trim();\n // A rollback from Mist Desktop leaves a marker naming the restored\n // version; say that rather than counting the files it touched.\n const rollbackAction = explicit\n ? null\n : await rollbackActionFromMarker(cwd);\n // Cached on the profile after the first push — /api/me can be\n // slow and the name it returns changes approximately never.\n const actor = await resolveGitSyncActor(async () => {\n try {\n return gitSyncActorFromMe(\n await withTimeout(fetchCurrentUser(client), 5_000),\n );\n } catch (err) {\n const reason = err instanceof Error ? err.message : String(err);\n console.warn(\n chalk.yellow(\n ` (skipping user-stamp on commit — couldn't fetch /api/me: ${reason})`,\n ),\n );\n return null;\n }\n });\n // The subject can only be built once staging settles, so it\n // isn't known when the spinner starts — capture it from the\n // resolver and report it on success instead.\n let subject = \"\";\n const commitSpinner = ora(\"Committing changes…\").start();\n try {\n await gitAutoCommit(cwd, (changes) => {\n subject = gitSyncCommitSubject(\n explicit ||\n (rollbackAction &&\n changes.some((change) => change.path === ROLLBACK_MARKER)\n ? rollbackAction\n : null) ||\n summarizeChanges(changes) ||\n FALLBACK_ACTION,\n actor,\n );\n return subject;\n });\n commitSpinner.succeed(\n subject ? `Committed — ${subject}` : \"Committed\",\n );\n } catch (err) {\n commitSpinner.fail(\"Commit failed\");\n throw err;\n }\n }\n }\n\n const spinner = ora(\"Minting git credential…\").start();\n const { git_credential: cred } = await createGitCredential(\n client,\n resolved,\n );\n spinner.text = `Pushing ${branch} to origin…`;\n try {\n await gitPush(cwd, cred.remote_url, branch);\n } catch (err) {\n spinner.fail(\"git push failed\");\n throw err;\n }\n spinner.succeed(\"Push complete\");\n\n if (!opts.watch) return;\n\n // Snapshot the commit sha we just pushed so we can match the right\n // deployment in the (common) case where older deployments already\n // exist on this mist. Best-effort: if `git rev-parse HEAD` fails we\n // fall back to taking the newest entry.\n let pushedSha: string | null = null;\n try {\n const { execa } = await import(\"execa\");\n const r = await execa(\"git\", [\"rev-parse\", \"HEAD\"], { cwd });\n pushedSha = r.stdout.trim();\n } catch {\n // git unavailable / not a repo — fall back to newest-deployment heuristic\n }\n\n // Find the deployment our push triggered. We poll /deployments\n // for an entry whose commit_sha matches what we just pushed.\n const locateStart = Date.now();\n let deploymentId: string | null = null;\n let attempt = 0;\n while (Date.now() - locateStart < LOCATE_TIMEOUT_MS) {\n attempt++;\n const { deployments } = await listDeployments(client, resolved, 5);\n const match = pushedSha\n ? deployments.find((d) => d.commit_sha === pushedSha)\n : deployments[0];\n if (match) {\n deploymentId = match.id;\n // Print so users see something happen even in non-TTY where\n // ora's spinner doesn't redraw.\n console.log(`Located deployment ${match.id} (${match.state})`);\n break;\n }\n if (attempt === 1) {\n console.log(\n `Locating deployment for commit ${pushedSha?.slice(0, 7) ?? \"(unknown)\"}…`,\n );\n } else if (attempt % 5 === 0) {\n // Status ping every 10s so the user knows we're still working.\n console.log(` …still waiting (${attempt * 2}s elapsed)`);\n }\n await sleep(POLL_MS);\n }\n if (!deploymentId) {\n console.warn(\n chalk.yellow(\n `Push succeeded, but hosting has not started commit ${pushedSha?.slice(0, 7) ?? \"(unknown)\"} after ${LOCATE_TIMEOUT_MS / 1000}s.`,\n ),\n );\n console.warn(\n chalk.yellow(\n \"The deployment is still queued and may appear later. Run `fluid mist deployments` to check it — do not retry or rewrite the commit just because the hosting queue is slow.\",\n ),\n );\n return;\n }\n\n // Watch the located deployment until it's in a terminal state.\n const readyStart = Date.now();\n let lastState: string | null = null;\n while (Date.now() - readyStart < READY_TIMEOUT_MS) {\n const { deployment } = await showDeployment(\n client,\n resolved,\n deploymentId,\n );\n if (deployment.state !== lastState) {\n console.log(` ${deployment.state}…`);\n lastState = deployment.state;\n }\n if (deployment.state === \"ready\") {\n // The deployment URL is an ephemeral provider address and can be\n // protected by provider SSO. Resolve the stable Fluid public URL\n // instead so the success link is one the app's users can open.\n let publicUrl: string | null = null;\n try {\n publicUrl = (\n await withTimeout(\n showMist(client, resolved),\n PUBLIC_URL_TIMEOUT_MS,\n )\n ).mist.public_url;\n } catch (err) {\n const reason = err instanceof Error ? err.message : String(err);\n console.warn(\n chalk.yellow(\n ` Deployment is ready, but the public URL lookup failed: ${reason}`,\n ),\n );\n }\n console.log(readyDeploymentMessage(publicUrl, resolved));\n return;\n }\n if (deployment.state === \"error\" || deployment.state === \"canceled\") {\n console.error(`✘ Deployment ${deployment.state}`);\n process.exit(3);\n }\n await sleep(POLL_MS);\n }\n console.error(\n `Watch timed out after ${READY_TIMEOUT_MS / 1000}s. Last state: ${lastState}.`,\n );\n console.error(\"Run `fluid mist deployments` to check the latest state.\");\n process.exit(3);\n }),\n );\n","/** `fluid mist pull` — git pull origin main. Spec §3. */\n\nimport { Command } from \"commander\";\nimport ora from \"ora\";\nimport { createMistClient } from \"../api/client.js\";\nimport { createGitCredential } from \"../api/git_credential.js\";\nimport { runAction } from \"../lib/error-handler.js\";\nimport { resolveSlug } from \"../lib/slug-resolver.js\";\nimport { ensureGitAvailable, gitPull } from \"../lib/git.js\";\n\ninterface Opts {\n branch?: string;\n}\n\nexport const pullCommand = new Command(\"pull\")\n .description(\"git pull from the Mist-managed remote\")\n .argument(\"[slug]\", \"The mist slug (defaults to .mist file in CWD)\")\n .option(\"--branch <name>\", \"Branch to pull (default: main)\")\n .action((slug: string | undefined, opts: Opts) =>\n runAction(async () => {\n await ensureGitAvailable();\n const resolved = resolveSlug(slug);\n const client = createMistClient();\n const branch = opts.branch ?? \"main\";\n const spinner = ora(\"Minting git credential…\").start();\n const { git_credential: cred } = await createGitCredential(\n client,\n resolved,\n );\n spinner.text = `Pulling ${branch}…`;\n try {\n await gitPull(process.cwd(), cred.remote_url, branch);\n } catch (err) {\n spinner.fail(\"git pull failed\");\n throw err;\n }\n spinner.succeed(\"Pull complete\");\n }),\n );\n","/** `fluid mist open` — opens public_url in the default browser. Spec §3. */\n\nimport chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport open from \"open\";\nimport { createMistClient } from \"../api/client.js\";\nimport { showMist } from \"../api/mists.js\";\nimport { runAction } from \"../lib/error-handler.js\";\nimport { resolveSlug } from \"../lib/slug-resolver.js\";\n\nexport const openCommand = new Command(\"open\")\n .description(\"Open the mist's public URL in your browser\")\n .argument(\"[slug]\", \"The mist slug (defaults to .mist file in CWD)\")\n .action((slug: string | undefined) =>\n runAction(async () => {\n const resolved = resolveSlug(slug);\n const client = createMistClient();\n const { mist } = await showMist(client, resolved);\n if (!mist.public_url) {\n console.error(\n chalk.yellow(\n `Mist ${mist.slug} has no public URL yet (state: ${mist.state}).`,\n ),\n );\n process.exit(1);\n }\n console.log(chalk.cyan(mist.public_url));\n await open(mist.public_url);\n }),\n );\n","/** `fluid mist deployments` — list recent deployments. Spec §3. */\n\nimport chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport { createMistClient } from \"../api/client.js\";\nimport { listDeployments } from \"../api/deployments.js\";\nimport { runAction } from \"../lib/error-handler.js\";\nimport { resolveSlug } from \"../lib/slug-resolver.js\";\nimport { printJson, printTable, relativeDate } from \"../lib/output.js\";\n\ninterface Opts {\n limit?: string;\n json?: boolean;\n}\n\nconst STATE_COLOR: Record<string, (s: string) => string> = {\n ready: chalk.green,\n building: chalk.yellow,\n queued: chalk.dim,\n error: chalk.red,\n canceled: chalk.red,\n};\n\nexport const deploymentsCommand = new Command(\"deployments\")\n .description(\"List recent deployments for a mist\")\n .argument(\"[slug]\", \"The mist slug (defaults to .mist file in CWD)\")\n .option(\"--limit <n>\", \"How many to fetch (default 10)\")\n .option(\"--json\", \"Emit raw API JSON\")\n .action((slug: string | undefined, opts: Opts) =>\n runAction(async () => {\n const resolved = resolveSlug(slug);\n const client = createMistClient();\n const limit = opts.limit ? parseInt(opts.limit, 10) : 10;\n const response = await listDeployments(client, resolved, limit);\n if (opts.json) return printJson(response);\n const deployments = response.deployments ?? [];\n if (deployments.length === 0) {\n console.log(chalk.dim(\"No deployments yet.\"));\n return;\n }\n const header = [\n chalk.bold(\"ID\"),\n chalk.bold(\"STATE\"),\n chalk.bold(\"COMMIT\"),\n chalk.bold(\"MESSAGE\"),\n chalk.bold(\"AGE\"),\n ];\n const rows = deployments.map((d) => [\n d.id,\n (STATE_COLOR[d.state] ?? chalk.white)(d.state),\n d.commit_sha ? d.commit_sha.slice(0, 7) : chalk.dim(\"—\"),\n d.commit_message ? d.commit_message.slice(0, 48) : chalk.dim(\"—\"),\n relativeDate(d.created_at),\n ]);\n printTable([header, ...rows]);\n }),\n );\n","/** Logs — `--tail` polls every 2s with `since`. Spec §4. */\n\nimport type { MistClient } from \"./client.js\";\nimport type { LogsResponse } from \"../types.js\";\nimport { resolveMistId } from \"./id-resolver.js\";\n\nexport interface FetchLogsParams {\n deployment_id?: string;\n /** ISO-8601 — only logs newer than this. Used by --tail. */\n since?: string;\n limit?: number;\n}\n\nexport async function fetchLogs(\n client: MistClient,\n slug: string,\n params: FetchLogsParams = {},\n): Promise<LogsResponse> {\n const id = await resolveMistId(client, slug);\n return client.get<LogsResponse>(\n `/api/v202604/mists/${id}/logs`,\n params as Record<string, unknown>,\n );\n}\n","/** `fluid mist logs [--tail]` — poll /logs?since=<last> every 2s. Spec §4. */\n\nimport chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport { createMistClient } from \"../api/client.js\";\nimport { fetchLogs } from \"../api/logs.js\";\nimport { runAction } from \"../lib/error-handler.js\";\nimport { resolveSlug } from \"../lib/slug-resolver.js\";\nimport { printJson } from \"../lib/output.js\";\nimport type { LogEntry } from \"../types.js\";\n\ninterface Opts {\n tail?: boolean;\n limit?: string;\n deployment?: string;\n json?: boolean;\n}\n\nconst POLL_MS = 2_000;\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((r) => setTimeout(r, ms));\n}\n\nfunction levelColor(level: LogEntry[\"level\"]): (s: string) => string {\n return level === \"stderr\" ? chalk.red : chalk.dim;\n}\n\nfunction printLine(entry: LogEntry): void {\n const ts = entry.timestamp.slice(11, 19);\n process.stdout.write(\n `${chalk.dim(ts)} ${levelColor(entry.level)(entry.level.padEnd(6))} ${entry.message}\\n`,\n );\n}\n\nexport const logsCommand = new Command(\"logs\")\n .description(\"Fetch deployment logs (use --tail to stream)\")\n .argument(\"[slug]\", \"The mist slug (defaults to .mist file in CWD)\")\n .option(\"--tail\", \"Stream new logs every 2s\")\n .option(\"--limit <n>\", \"Initial fetch size (default 200)\")\n .option(\"--deployment <id>\", \"Logs from a specific deployment ID\")\n .option(\"--json\", \"Emit raw API JSON (one batch; ignored when --tail)\")\n .action((slug: string | undefined, opts: Opts) =>\n runAction(async () => {\n const resolved = resolveSlug(slug);\n const client = createMistClient();\n const limit = opts.limit ? parseInt(opts.limit, 10) : 200;\n\n const initial = await fetchLogs(client, resolved, {\n ...(opts.deployment ? { deployment_id: opts.deployment } : {}),\n limit,\n });\n if (opts.json && !opts.tail) return printJson(initial);\n for (const entry of initial.logs) printLine(entry);\n\n if (!opts.tail) return;\n\n // Say the stream is live. A quiet deployment can go minutes without a\n // line, and without this the tail (and Mist's Production logs tab,\n // which waits for the first line) looks like it never connected.\n process.stdout.write(\n `${chalk.dim(\n `Streaming production logs for ${resolved}${\n initial.deployment_id ? ` (deployment ${initial.deployment_id})` : \"\"\n } — waiting for new entries…`,\n )}\\n`,\n );\n\n let since =\n initial.logs.length > 0\n ? initial.logs[initial.logs.length - 1]!.timestamp\n : new Date().toISOString();\n\n // Tail until Ctrl-C. Plain polling loop, no fancy backoff.\n while (true) {\n await sleep(POLL_MS);\n const batch = await fetchLogs(client, resolved, {\n ...(opts.deployment ? { deployment_id: opts.deployment } : {}),\n since,\n limit,\n });\n for (const entry of batch.logs) {\n printLine(entry);\n since = entry.timestamp;\n }\n }\n }),\n );\n","/**\n * Detects Vercel-encrypted env var ciphertext.\n *\n * `GET /api/v202604/mists/:id/env_vars` is a thin proxy to the Mist's\n * Vercel project env — it returns values ENCRYPTED, never plaintext (see\n * apps/mist-desktop/src/main/services/mist-env-vars.service.ts:25-28,\n * which documents the same server behavior for the desktop app's copy of\n * this API). The ciphertext is a base64-encoded JSON envelope of shape\n * `{\"v\":\"v2\",\"c\":\"<base64 ciphertext>\",\"k\":[<byte array>]}`.\n *\n * `fluid mist env pull` uses this to avoid writing that ciphertext into\n * `.env.local` as if it were a usable value.\n */\n\nconst BASE64_RE = /^[A-Za-z0-9+/]+={0,2}$/;\n\nfunction looksLikeBase64(value: string): boolean {\n return value.length > 0 && value.length % 4 === 0 && BASE64_RE.test(value);\n}\n\nfunction hasEncryptedEnvelopeShape(value: unknown): boolean {\n return (\n typeof value === \"object\" &&\n value !== null &&\n !Array.isArray(value) &&\n \"v\" in value &&\n \"c\" in value &&\n \"k\" in value\n );\n}\n\n/**\n * True when `value` is the Vercel encrypted-env envelope: a base64 string\n * that decodes to a JSON object with the `{ v, c, k }` shape. Returns\n * false for empty strings, non-base64 strings, and base64 strings that\n * decode to plaintext or to JSON that doesn't match the envelope shape —\n * so real plaintext secrets that happen to look like base64 are never\n * misclassified as ciphertext.\n */\nexport function isVercelEncryptedEnvValue(value: string): boolean {\n if (!looksLikeBase64(value)) return false;\n\n let decoded: string;\n try {\n decoded = Buffer.from(value, \"base64\").toString(\"utf8\");\n } catch {\n return false;\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(decoded);\n } catch {\n return false;\n }\n\n return hasEncryptedEnvelopeShape(parsed);\n}\n","/** `fluid mist env list` — list env vars; mark managed_by entries. Spec §4. */\n\nimport chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport { createMistClient } from \"../../api/client.js\";\nimport { listEnvVars } from \"../../api/env_vars.js\";\nimport { isVercelEncryptedEnvValue } from \"../../lib/encrypted-env.js\";\nimport { runAction } from \"../../lib/error-handler.js\";\nimport { resolveSlug } from \"../../lib/slug-resolver.js\";\nimport { printJson, printTable } from \"../../lib/output.js\";\nimport type { EnvVar } from \"../../types.js\";\n\ninterface Opts {\n json?: boolean;\n showValues?: boolean;\n}\n\nfunction maskValue(value: string): string {\n if (value.length <= 8) return \"•\".repeat(value.length);\n return `${value.slice(0, 4)}${\"•\".repeat(8)}${value.slice(-2)}`;\n}\n\n/**\n * Values come back Vercel-encrypted ciphertext (see\n * `../../lib/encrypted-env.ts`) — never show that raw blob, masked or not,\n * since it isn't the real secret and can run to ~1KB of base64.\n */\nfunction displayValue(v: EnvVar, showValues: boolean | undefined): string {\n if (isVercelEncryptedEnvValue(v.value)) return chalk.dim(\"<encrypted>\");\n return showValues ? v.value : maskValue(v.value);\n}\n\nexport const envListCommand = new Command(\"list\")\n .description(\"List a mist's env vars\")\n .argument(\"[slug]\", \"The mist slug (defaults to .mist file in CWD)\")\n .option(\"--show-values\", \"Print full values instead of masking them\")\n .option(\"--json\", \"Emit raw API JSON\")\n .action((slug: string | undefined, opts: Opts) =>\n runAction(async () => {\n const resolved = resolveSlug(slug);\n const client = createMistClient();\n const response = await listEnvVars(client, resolved);\n if (opts.json) return printJson(response);\n const vars = response.env_vars ?? [];\n if (vars.length === 0) {\n console.log(chalk.dim(\"No env vars.\"));\n return;\n }\n const header = [\n chalk.bold(\"KEY\"),\n chalk.bold(\"VALUE\"),\n chalk.bold(\"TARGETS\"),\n chalk.bold(\"MANAGED\"),\n ];\n const rows = vars.map((v) => [\n v.key,\n displayValue(v, opts.showValues),\n v.targets.join(\",\"),\n v.managed_by === \"mist\" ? chalk.magenta(\"mist\") : chalk.dim(\"user\"),\n ]);\n printTable([header, ...rows]);\n }),\n );\n","// Generate cryptographically-strong random secrets for Mist app\n// config — webhook signing tokens, API keys, session secrets, etc.\n//\n// Mist's agent reaches for this when a Mist app needs a secret it doesn't\n// have yet (e.g. a WEBHOOK_AUTH_TOKEN referenced by fluid.capabilities.json,\n// or a session secret) so it can generate one and set it as an env var\n// instead of asking the user to invent one by hand. `fluid mist env set\n// KEY --generate` uses the same generator.\n//\n// Uses Web Crypto (`globalThis.crypto.getRandomValues`, a CSPRNG in Node\n// 19+, Electron and browsers) rather than `node:crypto`, so this shared\n// package stays runtime-agnostic.\n\nexport type SecretEncoding = \"hex\" | \"base64url\" | \"base64\";\n\nexport interface GenerateSecretOptions {\n /** Entropy in bytes. 32 bytes (256-bit) is a sensible default for a\n * signing/session secret. Clamped to [16, 128] so callers can't ask\n * for a uselessly-short or absurdly-long value. */\n bytes?: number;\n /** Output alphabet. base64url is URL/env-safe (no +/=), hex is the\n * most portable, base64 for callers that specifically want it. */\n encoding?: SecretEncoding;\n}\n\nconst MIN_BYTES = 16;\nconst MAX_BYTES = 128;\nconst DEFAULT_BYTES = 32;\n\nexport interface GeneratedSecret {\n secret: string;\n bytes: number;\n encoding: SecretEncoding;\n}\n\n/** Return a random secret. Pure + synchronous so it's trivially\n * testable (entropy + encoding + clamping) without any I/O. */\nexport function generateSecret(\n options: GenerateSecretOptions = {},\n): GeneratedSecret {\n const encoding: SecretEncoding = options.encoding ?? \"base64url\";\n const requested = options.bytes ?? DEFAULT_BYTES;\n const bytes = Math.min(MAX_BYTES, Math.max(MIN_BYTES, Math.trunc(requested)));\n const buf = new Uint8Array(bytes);\n globalThis.crypto.getRandomValues(buf);\n return { secret: encode(buf, encoding), bytes, encoding };\n}\n\nfunction encode(buf: Uint8Array, encoding: SecretEncoding): string {\n if (encoding === \"hex\") {\n return Array.from(buf, (byte) => byte.toString(16).padStart(2, \"0\")).join(\n \"\",\n );\n }\n const base64 = btoa(String.fromCharCode(...buf));\n if (encoding === \"base64\") return base64;\n // base64url: URL/env-safe alphabet, unpadded (matches Node's encoding).\n return base64.replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n}\n","/**\n * `fluid mist env set <slug> KEY=value [KEY=value ...]` — POST for new\n * keys, PATCH for existing. Spec §3 + §4.\n *\n * `fluid mist env set [slug] KEY --generate [--bytes N]` mints a random\n * secret for KEY instead (the generator Mist's `set_mist_env_var` uses),\n * writes it, and prints it exactly once.\n */\n\nimport chalk from \"chalk\";\nimport { Command, InvalidArgumentError } from \"commander\";\nimport { generateSecret } from \"@fluid-app/mist-core/secret-generator\";\nimport { createMistClient, type MistClient } from \"../../api/client.js\";\nimport { createEnvVar, listEnvVars, updateEnvVar } from \"../../api/env_vars.js\";\nimport { runAction } from \"../../lib/error-handler.js\";\nimport { printJson } from \"../../lib/output.js\";\nimport { resolveSlug } from \"../../lib/slug-resolver.js\";\n\ninterface Opts {\n json?: boolean;\n generate?: boolean;\n bytes?: number;\n}\n\n/** Env var name rule enforced by Rails (mirrors Mist's ENV_KEY_RE). */\nconst ENV_KEY_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;\n\nexport const GENERATED_SECRET_NOTICE =\n \"This secret is shown once and won't be shown again; Fluid stores it encrypted. Copy it now if you also need it in .env.local. Redeploy (fluid mist push) for the change to take effect.\";\n\nfunction parsePair(pair: string): { key: string; value: string } {\n const eq = pair.indexOf(\"=\");\n if (eq <= 0) {\n throw new Error(\n `Invalid pair \"${pair}\". Use KEY=value (no leading spaces).`,\n );\n }\n return { key: pair.slice(0, eq), value: pair.slice(eq + 1) };\n}\n\nfunction parseBytes(value: string): number {\n const bytes = Number(value);\n if (!Number.isInteger(bytes) || bytes < 16 || bytes > 128) {\n throw new InvalidArgumentError(\"Use a whole number from 16 to 128.\");\n }\n return bytes;\n}\n\nexport interface GeneratedEnvVarResult {\n key: string;\n action: \"created\" | \"updated\";\n generatedSecret: string;\n bytes: number;\n encoding: string;\n note: string;\n}\n\n/**\n * Generate a secret for `key` and write it to the Mist. Refuses invalid\n * and Mist-managed keys BEFORE generating, so a secret is only ever\n * returned once it has been stored.\n */\nexport async function setGeneratedEnvVar(\n client: MistClient,\n slug: string,\n key: string,\n bytes?: number,\n): Promise<GeneratedEnvVarResult> {\n if (!ENV_KEY_RE.test(key)) {\n throw new Error(\n `Invalid env var name \"${key}\". Use letters, digits and underscores, not starting with a digit.`,\n );\n }\n const existing = await listEnvVars(client, slug);\n const current = (existing.env_vars ?? []).find((v) => v.key === key);\n if (current?.managed_by === \"mist\") {\n throw new Error(\n `\"${key}\" is managed by Fluid and can't be set; it is provisioned automatically for the Mist.`,\n );\n }\n\n const secret = generateSecret(bytes === undefined ? {} : { bytes });\n if (current) await updateEnvVar(client, slug, key, secret.secret);\n else await createEnvVar(client, slug, key, secret.secret);\n return {\n key,\n action: current ? \"updated\" : \"created\",\n generatedSecret: secret.secret,\n bytes: secret.bytes,\n encoding: secret.encoding,\n note: GENERATED_SECRET_NOTICE,\n };\n}\n\nexport const envSetCommand = new Command(\"set\")\n .description(\n \"Set one or more env vars (KEY=value KEY=value …), or generate a secret for KEY with --generate\",\n )\n .argument(\n \"<slug-or-pair>\",\n \"The mist slug, or the first KEY=value pair (or KEY with --generate)\",\n )\n .argument(\"[pairs...]\", \"Additional KEY=value pairs (or KEY with --generate)\")\n .option(\n \"--generate\",\n \"Generate a random secret as the value of one KEY and print it once\",\n )\n .option(\n \"--bytes <n>\",\n \"Entropy for --generate, in bytes (16-128, default 32)\",\n parseBytes,\n )\n .option(\"--json\", \"Emit JSON (with --generate)\")\n .action(async (slugOrPair: string, pairs: string[], opts: Opts) => {\n await runAction(async () => {\n if (opts.generate) {\n await runGenerate([slugOrPair, ...pairs], opts);\n return;\n }\n if (opts.bytes !== undefined) {\n console.error(chalk.red(\"--bytes only applies with --generate.\"));\n process.exit(1);\n }\n // Allow both shapes: `env set <slug> KEY=val` and `env set KEY=val`\n // (with slug coming from .mist file). We detect by checking if the\n // first arg contains an `=`.\n let slug: string;\n let rawPairs: string[];\n if (slugOrPair.includes(\"=\")) {\n slug = resolveSlug();\n rawPairs = [slugOrPair, ...pairs];\n } else {\n slug = resolveSlug(slugOrPair);\n rawPairs = pairs;\n }\n if (rawPairs.length === 0) {\n console.error(chalk.red(\"Pass at least one KEY=value pair.\"));\n process.exit(1);\n }\n const client = createMistClient();\n // Fetch existing to decide POST vs PATCH per key.\n const existing = await listEnvVars(client, slug);\n const existingKeys = new Set((existing.env_vars ?? []).map((v) => v.key));\n for (const raw of rawPairs) {\n const { key, value } = parsePair(raw);\n if (existingKeys.has(key)) {\n await updateEnvVar(client, slug, key, value);\n console.log(`${chalk.green(\"updated\")} ${key}`);\n } else {\n await createEnvVar(client, slug, key, value);\n console.log(`${chalk.cyan(\"created\")} ${key}`);\n }\n }\n });\n });\n\n/** `env set [slug] KEY --generate`: exactly one KEY, without a value. */\nasync function runGenerate(args: string[], opts: Opts): Promise<void> {\n if (args.length > 2) {\n console.error(\n chalk.red(\n \"--generate sets one key at a time: env set [slug] KEY --generate\",\n ),\n );\n process.exit(1);\n }\n const [first, second] = args;\n const key = (second ?? first ?? \"\").trim();\n if (key.includes(\"=\")) {\n console.error(\n chalk.red(\n `--generate creates the value itself; pass the key alone (e.g. ${key.split(\"=\")[0]}).`,\n ),\n );\n process.exit(1);\n }\n const slug = second === undefined ? resolveSlug() : resolveSlug(first);\n const result = await setGeneratedEnvVar(\n createMistClient(),\n slug,\n key,\n opts.bytes,\n );\n if (opts.json) {\n printJson(result);\n return;\n }\n const verb =\n result.action === \"updated\"\n ? chalk.green(\"updated\")\n : chalk.cyan(\"created\");\n console.log(\n `${verb} ${result.key} (${result.bytes}-byte generated secret)`,\n );\n console.log();\n console.log(result.generatedSecret);\n console.log();\n console.log(chalk.yellow(result.note));\n}\n","/** `fluid mist env unset <slug> KEY [KEY ...]` — DELETE per key. Spec §3. */\n\nimport chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport { createMistClient } from \"../../api/client.js\";\nimport { deleteEnvVar } from \"../../api/env_vars.js\";\nimport { runAction } from \"../../lib/error-handler.js\";\nimport { resolveSlug } from \"../../lib/slug-resolver.js\";\n\nexport const envUnsetCommand = new Command(\"unset\")\n .description(\"Delete one or more env vars\")\n .argument(\n \"<slug-or-key>\",\n \"The mist slug, or the first KEY (if .mist file is present)\",\n )\n .argument(\"[keys...]\", \"Additional keys\")\n .action(async (slugOrKey: string, keys: string[]) => {\n await runAction(async () => {\n // Heuristic: KEYs are env-var-shaped (UPPERCASE_WITH_UNDERSCORES);\n // slugs are 6 lowercase alnum chars. We only need it to be specific\n // enough for the common case.\n let slug: string;\n let toDelete: string[];\n if (/^[A-Z_][A-Z0-9_]*$/.test(slugOrKey)) {\n slug = resolveSlug();\n toDelete = [slugOrKey, ...keys];\n } else {\n slug = resolveSlug(slugOrKey);\n toDelete = keys;\n }\n if (toDelete.length === 0) {\n console.error(chalk.red(\"Pass at least one KEY.\"));\n process.exit(1);\n }\n const client = createMistClient();\n for (const key of toDelete) {\n await deleteEnvVar(client, slug, key);\n console.log(`${chalk.red(\"deleted\")} ${key}`);\n }\n });\n });\n","/**\n * `fluid mist env pull` — writes `.env.local` with user-managed vars only.\n * Skips `managed_by: \"mist\"` entries (DATABASE_URL, FLUID_*) since local\n * dev uses PGlite, not the production database. Spec §4 + §5.\n *\n * Also skips user-managed vars whose value comes back as Vercel-encrypted\n * ciphertext (`GET .../env_vars` is a proxy to Vercel's project env, which\n * never returns plaintext — see `../../lib/encrypted-env.ts`). Writing that\n * ciphertext into `.env.local` verbatim would silently produce an unusable\n * file, so those keys are collected and surfaced as a warning instead.\n *\n * The output file is MERGED, not replaced: pulled plaintext values update\n * their existing lines (or are appended), while every other existing line\n * — comments, locally maintained secrets for encrypted/managed keys, and\n * local-only vars — is preserved verbatim. Replacing the file wholesale\n * would delete manually set values for skipped keys, and an all-encrypted\n * response would truncate the file to zero bytes right before telling the\n * user to set those same values locally.\n */\n\nimport chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport { existsSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { createMistClient } from \"../../api/client.js\";\nimport { listEnvVars } from \"../../api/env_vars.js\";\nimport { isVercelEncryptedEnvValue } from \"../../lib/encrypted-env.js\";\nimport { runAction } from \"../../lib/error-handler.js\";\nimport { resolveSlug } from \"../../lib/slug-resolver.js\";\nimport type { EnvVar } from \"../../types.js\";\n\ninterface Opts {\n out?: string;\n}\n\nfunction escapeValue(v: string): string {\n // Quote if value has whitespace, `#`, or quotes — bash-safe.\n if (/[\\s#\"']/.test(v)) {\n return `\"${v.replace(/([\"\\\\$`])/g, \"\\\\$1\")}\"`;\n }\n return v;\n}\n\nexport interface PullPartition {\n /** User-managed vars with a real value — safe to write to `.env.local`. */\n plaintext: EnvVar[];\n /** User-managed vars whose value is Vercel ciphertext — can't be pulled. */\n encrypted: EnvVar[];\n /** Mist-managed vars — never pulled (local dev uses PGlite). */\n managed: EnvVar[];\n}\n\n/**\n * Splits the raw env-var list into what `env pull` can safely write vs.\n * what it must skip, and why. Pure — no I/O — so it's unit-testable\n * without mocking the API client or the filesystem.\n */\nexport function partitionPullEnvVars(\n envVars: readonly EnvVar[],\n): PullPartition {\n const plaintext: EnvVar[] = [];\n const encrypted: EnvVar[] = [];\n const managed: EnvVar[] = [];\n for (const v of envVars) {\n if (v.managed_by === \"mist\") {\n managed.push(v);\n } else if (isVercelEncryptedEnvValue(v.value)) {\n encrypted.push(v);\n } else {\n plaintext.push(v);\n }\n }\n return { plaintext, encrypted, managed };\n}\n\n/** Matches a `KEY=` / `export KEY=` assignment line and captures the key. */\nconst ENV_ASSIGNMENT = /^\\s*(?:export\\s+)?([A-Za-z_][A-Za-z0-9_]*)\\s*=/;\n\n/**\n * Merges pulled plaintext vars into the existing env-file contents.\n * Existing assignment lines for pulled keys are updated in place; every\n * other line (comments, blank lines, locally maintained values for keys\n * the pull skipped) is preserved verbatim; new keys are appended. Pure —\n * no I/O — so it's unit-testable without touching the filesystem.\n */\nexport function mergeEnvContents(\n existing: string,\n plaintext: readonly EnvVar[],\n): string {\n const updates = new Map<string, string>(\n plaintext.map((v) => [v.key, `${v.key}=${escapeValue(v.value)}`]),\n );\n const lines = existing.length > 0 ? existing.split(\"\\n\") : [];\n // A trailing newline yields one empty final element — drop it so the\n // merge doesn't accumulate blank lines across repeated pulls.\n if (lines.at(-1) === \"\") {\n lines.pop();\n }\n const out: string[] = [];\n for (const line of lines) {\n const key = ENV_ASSIGNMENT.exec(line)?.[1];\n const replacement = key === undefined ? undefined : updates.get(key);\n if (key !== undefined && replacement !== undefined) {\n out.push(replacement);\n updates.delete(key);\n } else {\n out.push(line);\n }\n }\n for (const rendered of updates.values()) {\n out.push(rendered);\n }\n return out.length > 0 ? out.join(\"\\n\") + \"\\n\" : \"\";\n}\n\nexport const envPullCommand = new Command(\"pull\")\n .description(\"Write user-managed env vars to .env.local in the CWD\")\n .argument(\"[slug]\", \"The mist slug (defaults to .mist file in CWD)\")\n .option(\"--out <path>\", \"Output path (default: .env.local)\")\n .action((slug: string | undefined, opts: Opts) =>\n runAction(async () => {\n const resolved = resolveSlug(slug);\n const client = createMistClient();\n const { env_vars } = await listEnvVars(client, resolved);\n const { plaintext, encrypted, managed } = partitionPullEnvVars(env_vars);\n const outName = opts.out ?? \".env.local\";\n const target = join(process.cwd(), outName);\n const existing = existsSync(target) ? readFileSync(target, \"utf8\") : \"\";\n // Merge instead of replace: pulled keys update their lines, and\n // everything else in the existing file (locally maintained values\n // for encrypted/managed keys, local-only vars, comments) survives.\n // With zero plaintext vars the merge is a no-op on existing\n // contents, so the command still succeeds and exits 0 rather than\n // truncating the file when every var is ciphertext.\n writeFileSync(target, mergeEnvContents(existing, plaintext), \"utf8\");\n console.log(\n chalk.green(\n `Wrote ${plaintext.length} var(s) to ${target.replace(process.cwd() + \"/\", \"\")}${\n existing.length > 0\n ? \" (existing entries for other keys preserved)\"\n : \"\"\n }.`,\n ),\n );\n if (managed.length > 0) {\n console.log(\n chalk.dim(\n `Skipped ${managed.length} Mist-managed var(s): ${managed.map((v) => v.key).join(\", \")} (local dev uses PGlite)`,\n ),\n );\n }\n if (encrypted.length > 0) {\n console.log(\n chalk.yellow(\n `Skipped ${encrypted.length} var(s) whose values are stored encrypted and can't be pulled: ${encrypted\n .map((v) => v.key)\n .join(\n \", \",\n )} — set them locally in ${outName} yourself (values are visible in the Vercel dashboard) or re-set them via \\`fluid mist env set\\`.`,\n ),\n );\n }\n }),\n );\n","/** `fluid mist env` — sub-command aggregator. */\n\nimport { Command } from \"commander\";\nimport { envListCommand } from \"./list.js\";\nimport { envSetCommand } from \"./set.js\";\nimport { envUnsetCommand } from \"./unset.js\";\nimport { envPullCommand } from \"./pull.js\";\n\nexport const envCommand = new Command(\"env\")\n .description(\"Manage a mist's environment variables\")\n .addCommand(envListCommand)\n .addCommand(envSetCommand)\n .addCommand(envUnsetCommand)\n .addCommand(envPullCommand);\n","/**\n * `fluid mist droplet egress-proxy <on|off> [slug] [--json]`\n *\n * Ask Fluid (on) to let the Mist's attached Droplet reach external systems\n * such as Exigo through its egress proxy, or withdraw the request (off). The\n * proxy admits the droplet on the request alone; no Fluid admin approves it.\n * Leaves an already-correct droplet untouched. Safe Mode treats this as a\n * write (apps/mist-desktop fluid-cli-policy).\n */\n\nimport chalk from \"chalk\";\nimport { Command, InvalidArgumentError } from \"commander\";\nimport { createMistClient } from \"../../api/client.js\";\nimport { setDropletEgressProxy } from \"../../lib/droplet-egress.js\";\nimport { runAction } from \"../../lib/error-handler.js\";\nimport { printJson } from \"../../lib/output.js\";\n\ninterface Opts {\n json?: boolean;\n}\n\nexport function parseEgressProxyState(value: string): boolean {\n const normalized = value.trim().toLowerCase();\n if (normalized === \"on\") return true;\n if (normalized === \"off\") return false;\n throw new InvalidArgumentError('Use \"on\" or \"off\".');\n}\n\nexport const dropletEgressProxyCommand = new Command(\"egress-proxy\")\n .description(\n \"Ask Fluid (on) or stop asking (off) to let the attached Droplet reach external systems such as Exigo through its egress proxy\",\n )\n .argument(\"<state>\", \"on | off\", parseEgressProxyState)\n .argument(\"[slug]\", \"Mist slug (defaults to the .mist file in CWD)\")\n .option(\"--json\", \"Emit JSON\")\n .action((requested: boolean, slug: string | undefined, opts: Opts) =>\n runAction(async () => {\n const result = await setDropletEgressProxy(createMistClient(), {\n slug,\n cwd: process.cwd(),\n requested,\n });\n if (opts.json) return printJson(result);\n\n const target = `droplet ${result.dropletUuid} (${result.mistSlug})`;\n if (!result.requested) {\n console.log(\n result.changed\n ? `${chalk.green(\"✓\")} Egress request withdrawn for ${target}`\n : chalk.dim(`No egress request for ${target}`),\n );\n } else {\n console.log(\n result.changed\n ? `${chalk.green(\"✓\")} Egress requested for ${target}. Fluid's proxy admits requested droplets wherever the proxy honours Fluid's answer.`\n : chalk.dim(`Egress already requested for ${target}`),\n );\n }\n }),\n );\n","/**\n * `fluid mist droplet reinstall [slug] --dry-run | --confirm`\n * [--installation <uuid>] [--resume <operationId>] [--watch-timeout <s>]\n *\n * Port of Mist Desktop's `repair_and_reinstall_droplet`: an IRREVERSIBLE\n * repair plus backend-owned replacement of the Mist's own attached Droplet\n * installation. Same safety rules as Mist:\n *\n * - The Droplet is always the one attached to the Mist; there is no flag\n * to name another, and the backend rejects installations of any other\n * Droplet.\n * - `--dry-run` shows the exact plan and mutates nothing.\n * - `--confirm` executes, and only after a `--dry-run` plan for the same\n * Droplet and installation was shown within the last 15 minutes.\n * `--yes` never approves it.\n * - Execution repairs the lifecycle URLs, starts ONE durable replacement\n * operation, and watches it. It never DELETEs and re-creates an\n * installation itself and never reports success before Fluid does.\n * - A slow replacement returns \"still_running\" with the ids to resume:\n * `--resume <operationId> --installation <uuid>` (both required).\n * - `--installation <uuid>` alone recovers a source installation that a\n * failed attempt already terminalized.\n *\n * Output is always JSON; refusals and failures exit non-zero.\n */\n\nimport { randomUUID } from \"node:crypto\";\nimport { Command, InvalidArgumentError, Option } from \"commander\";\nimport { createMistClient, MistApiError } from \"../../api/client.js\";\nimport { runDropletReinstall } from \"../../lib/droplet-repair.js\";\nimport { runAction } from \"../../lib/error-handler.js\";\nimport { printJson } from \"../../lib/output.js\";\nimport { createFileReinstallPlanStore } from \"../../lib/reinstall-plan-store.js\";\n\ninterface Opts {\n dryRun?: boolean;\n confirm?: boolean;\n yes?: boolean;\n installation?: string;\n resume?: string;\n watchTimeout?: number;\n}\n\nfunction parseSeconds(value: string): number {\n const seconds = Number(value);\n if (!Number.isInteger(seconds) || seconds < 1 || seconds > 3_600) {\n throw new InvalidArgumentError(\"Use a whole number of seconds (1-3600).\");\n }\n return seconds;\n}\n\nexport const dropletReinstallCommand = new Command(\"reinstall\")\n .description(\n \"Irreversibly repair and replace the Mist's attached Droplet installation (plan with --dry-run, then --confirm)\",\n )\n .argument(\"[slug]\", \"Mist slug (defaults to the .mist file in CWD)\")\n .option(\"--dry-run\", \"Show the exact plan without changing anything\")\n .option(\n \"--confirm\",\n \"Execute the plan shown by the latest --dry-run (irreversible)\",\n )\n .option(\n \"--installation <uuid>\",\n \"Recovery: the source installation uuid from an earlier plan or still_running result\",\n )\n .option(\n \"--resume <operationId>\",\n \"Keep watching a replacement that already started (needs --installation)\",\n )\n .option(\n \"--watch-timeout <seconds>\",\n \"How long to watch before returning still_running (default 120)\",\n parseSeconds,\n )\n // Accepted only so it can be refused with a clear message: --yes never\n // approves an irreversible reinstall.\n .addOption(new Option(\"-y, --yes\", \"Not accepted; use --confirm\").hideHelp())\n .action((slug: string | undefined, opts: Opts) =>\n runAction(async () => {\n let outcome;\n try {\n outcome = await runDropletReinstall(\n {\n client: createMistClient(),\n cwd: process.cwd(),\n slug,\n planStore: createFileReinstallPlanStore(),\n newIdempotencyKey: () => `fluid-cli-repair-${randomUUID()}`,\n },\n {\n dryRun: opts.dryRun,\n confirm: opts.confirm,\n yes: opts.yes,\n installationUuid: opts.installation,\n resumeOperationId: opts.resume,\n watchTimeoutMs:\n opts.watchTimeout === undefined\n ? undefined\n : opts.watchTimeout * 1_000,\n },\n );\n } catch (err) {\n // Keep the output JSON on failure too, so scripts can parse it.\n printJson({\n status: \"error\",\n message: err instanceof Error ? err.message : String(err),\n ...(err instanceof MistApiError ? { httpStatus: err.status } : {}),\n });\n process.exit(err instanceof MistApiError ? err.exitCode : 1);\n }\n printJson(outcome.output);\n if (outcome.exitCode !== 0) process.exit(outcome.exitCode);\n }),\n );\n","/**\n * `fluid mist droplet repair [slug] [--dry-run] [--yes] [--json]`\n *\n * Port of Mist Desktop's `repair_droplet_lifecycle_webhooks`: point the\n * Mist's attached Droplet's embed URL and install/uninstall webhook URLs at\n * the Mist's public URL. Only those three Droplet fields change, and the\n * Droplet is re-read to verify. This does not replay `droplet.installed`\n * for an existing installation — use `fluid mist droplet reinstall` when\n * the app needs a fresh install event.\n */\n\nimport chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport prompts from \"prompts\";\nimport { createMistClient, MistApiError } from \"../../api/client.js\";\nimport {\n applyDropletRepair,\n DropletRepairRefusal,\n planDropletRepair,\n resolveRepairTarget,\n type LifecycleRepairResult,\n} from \"../../lib/droplet-repair.js\";\nimport { runAction } from \"../../lib/error-handler.js\";\nimport { printJson } from \"../../lib/output.js\";\n\ninterface Opts {\n dryRun?: boolean;\n yes?: boolean;\n json?: boolean;\n}\n\nexport const dropletRepairCommand = new Command(\"repair\")\n .description(\"Repair the attached Droplet's embed and lifecycle webhook URLs\")\n .argument(\"[slug]\", \"Mist slug (defaults to the .mist file in CWD)\")\n .option(\"--dry-run\", \"Show the planned change without updating the Droplet\")\n .option(\"-y, --yes\", \"Apply without a confirmation prompt\")\n .option(\"--json\", \"Emit JSON\")\n .action((slug: string | undefined, opts: Opts) =>\n runAction(async () => {\n const client = createMistClient();\n let result: { summary: LifecycleRepairResult; isError: boolean };\n try {\n const target = await resolveRepairTarget(client, {\n slug,\n cwd: process.cwd(),\n });\n const planned = await planDropletRepair(\n client,\n target,\n opts.dryRun === true || !opts.yes,\n );\n if (opts.dryRun || planned.alreadyCorrect) {\n result = { summary: planned.summary, isError: false };\n } else {\n if (!opts.yes) {\n const interactive = process.stdin.isTTY && !opts.json;\n if (!interactive) {\n throw new DropletRepairRefusal(\n \"confirmation_required\",\n \"Refused to update the Droplet without confirmation. Rerun with --yes, or with --dry-run to preview.\",\n );\n }\n printSummary(planned.summary, { preview: true });\n const { confirmed } = await prompts(\n {\n type: \"confirm\",\n name: \"confirmed\",\n message: \"Update these Droplet URLs?\",\n initial: false,\n },\n { onCancel: () => process.exit(130) },\n );\n if (!confirmed) {\n console.log(chalk.dim(\"Aborted.\"));\n return;\n }\n }\n result = await applyDropletRepair(client, target);\n }\n } catch (err) {\n if (opts.json && !(err instanceof DropletRepairRefusal)) {\n // Keep --json output parseable on API and other failures too.\n printJson({\n status: \"error\",\n message: err instanceof Error ? err.message : String(err),\n ...(err instanceof MistApiError ? { httpStatus: err.status } : {}),\n });\n process.exit(err instanceof MistApiError ? err.exitCode : 1);\n }\n if (!(err instanceof DropletRepairRefusal)) throw err;\n if (opts.json) {\n printJson({\n status: \"refused\",\n code: err.code,\n message: err.message,\n });\n } else {\n console.error(chalk.red(err.message));\n }\n process.exit(1);\n }\n\n if (opts.json) printJson(result.summary);\n else printSummary(result.summary);\n if (result.isError) process.exit(1);\n }),\n );\n\nfunction printSummary(\n summary: LifecycleRepairResult,\n options: { preview?: boolean } = {},\n): void {\n const label: Record<LifecycleRepairResult[\"status\"], string> = {\n unchanged: chalk.green(\"Already correct; nothing to change.\"),\n planned: chalk.yellow(\"Planned (dry run); nothing was changed.\"),\n updated: chalk.green(\"Updated and verified.\"),\n error: chalk.red(\"Updated, but the Droplet did not verify.\"),\n };\n console.log();\n console.log(\n `${chalk.bold(summary.mistSlug ?? summary.mistId)} ${chalk.dim(summary.dropletUuid)}`,\n );\n const row = (name: string, previous: string | null, next: string) =>\n previous === next\n ? ` ${name.padEnd(10)} ${next}`\n : ` ${name.padEnd(10)} ${chalk.dim(previous ?? \"(none)\")} → ${chalk.cyan(next)}`;\n console.log(row(\"embed\", summary.previous.embedUrl, summary.embedUrl));\n console.log(\n row(\n \"install\",\n summary.previous.installWebhookUrl,\n summary.installWebhookUrl,\n ),\n );\n console.log(\n row(\n \"uninstall\",\n summary.previous.uninstallWebhookUrl,\n summary.uninstallWebhookUrl,\n ),\n );\n if (summary.lifecyclePathsSource === \"defaults\") {\n console.log(\n chalk.dim(\n \" Webhook paths: defaults (no fluid.capabilities.json for this Mist in the current directory).\",\n ),\n );\n }\n if (!options.preview) console.log(` ${label[summary.status]}`);\n console.log(chalk.dim(` ${summary.note}`));\n}\n","/** `fluid mist droplet` — sub-command aggregator for the attached Droplet. */\n\nimport { Command } from \"commander\";\nimport { dropletEgressProxyCommand } from \"./egress-proxy.js\";\nimport { dropletReinstallCommand } from \"./reinstall.js\";\nimport { dropletRepairCommand } from \"./repair.js\";\n\nexport const dropletCommand = new Command(\"droplet\")\n .description(\n \"Repair, reinstall, or set egress for the Mist's attached Droplet\",\n )\n .addCommand(dropletRepairCommand)\n .addCommand(dropletReinstallCommand)\n .addCommand(dropletEgressProxyCommand);\n","/** `fluid mist staging status` — where staging stands. */\n\nimport chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport { createMistClient } from \"../../api/client.js\";\nimport { runAction } from \"../../lib/error-handler.js\";\nimport { printJson } from \"../../lib/output.js\";\nimport { resolveSlug } from \"../../lib/slug-resolver.js\";\nimport {\n describeProgress,\n describeUnavailable,\n readStagingStatus,\n} from \"../../lib/staging.js\";\n\ninterface Opts {\n json?: boolean;\n}\n\nexport const stagingStatusCommand = new Command(\"status\")\n .description(\"Show the staging environment and whether it can be requested\")\n .argument(\"[slug]\", \"The mist slug (defaults to .mist file in CWD)\")\n .option(\"--json\", \"Emit the status as JSON\")\n .action((slug: string | undefined, opts: Opts) =>\n runAction(async () => {\n const client = createMistClient();\n const status = await readStagingStatus(client, resolveSlug(slug));\n if (opts.json) {\n return printJson({\n capability: status.capability,\n environment: status.environment,\n active_operation: status.activeOperation,\n });\n }\n\n const { environment, activeOperation, capability } = status;\n if (environment) {\n console.log(`Staging: ${chalk.bold(environment.state)}`);\n if (environment.url) console.log(`URL: ${chalk.cyan(environment.url)}`);\n } else {\n console.log(chalk.dim(\"No staging environment yet.\"));\n }\n if (activeOperation) {\n console.log(\n `Operation ${activeOperation.id}: ${describeProgress(activeOperation)}`,\n );\n }\n console.log(\n capability.staging.available\n ? \"Staging can be requested with `fluid mist staging request --revision <sha>`.\"\n : chalk.yellow(describeUnavailable(capability.staging)),\n );\n }),\n );\n","/**\n * Staging requests the server hasn't confirmed yet.\n *\n * `fluid mist staging request` saves its operation key here before it sends\n * the request and removes it once the server answers with an operation. If\n * the CLI dies in between (crash, lost connection, closed terminal), the\n * next request for the same revision reuses the saved key, so the server\n * resumes the same operation instead of starting a second one. After the\n * server has confirmed, `fluid mist staging wait` follows the operation.\n *\n * Records live under the Fluid config dir (`~/.fluid/mist/staging-requests`)\n * and hold only ids, a commit SHA and a timestamp, no tokens or secrets.\n */\n\nimport { mkdirSync, readFileSync, rmSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { getConfigDir } from \"@fluid-app/fluid-cli\";\nimport {\n checkStagingRequest,\n isOperationKey,\n type BranchOperation,\n} from \"../api/branch_environments.js\";\nimport { MistApiError, type MistClient } from \"../api/client.js\";\nimport { resolveMistId } from \"../api/id-resolver.js\";\nimport { admitStaging, StagingOperationFailedError } from \"./staging.js\";\n\nexport interface PendingStagingRequest {\n mistId: string;\n operationKey: string;\n sourceRevision: string;\n /** ISO-8601 time the request was about to be sent. */\n requestedAt: string;\n}\n\nexport interface StagingRequestStore {\n save(request: PendingStagingRequest): void;\n load(mistId: string): PendingStagingRequest | null;\n remove(mistId: string): void;\n}\n\nexport function createFileStagingRequestStore(\n dir: string = join(getConfigDir(), \"mist\", \"staging-requests\"),\n): StagingRequestStore {\n const fileFor = (mistId: string) =>\n join(dir, `${encodeURIComponent(mistId)}.json`);\n return {\n save(request) {\n mkdirSync(dir, { recursive: true, mode: 0o700 });\n writeFileSync(\n fileFor(request.mistId),\n JSON.stringify(request, null, 2) + \"\\n\",\n { encoding: \"utf8\", mode: 0o600 },\n );\n },\n load(mistId) {\n try {\n const parsed = JSON.parse(\n readFileSync(fileFor(mistId), \"utf8\"),\n ) as unknown;\n return isPendingRequest(parsed) && parsed.mistId === mistId\n ? parsed\n : null;\n } catch {\n return null;\n }\n },\n remove(mistId) {\n rmSync(fileFor(mistId), { force: true });\n },\n };\n}\n\n/**\n * The key to send for `sourceRevision`: the saved one when an unconfirmed\n * request for the same revision is pending, otherwise none. A pending\n * request for another revision, or with a key the server would refuse, is\n * dropped; if the server did admit it, the capability check reports it as\n * the run in progress.\n */\nexport function pendingKeyFor(\n store: StagingRequestStore,\n mistId: string,\n sourceRevision: string,\n): string | null {\n const pending = store.load(mistId);\n if (!pending) return null;\n if (\n pending.sourceRevision === sourceRevision &&\n isOperationKey(pending.operationKey)\n ) {\n return pending.operationKey;\n }\n store.remove(mistId);\n return null;\n}\n\nfunction isPendingRequest(value: unknown): value is PendingStagingRequest {\n if (typeof value !== \"object\" || value === null) return false;\n const record = value as Record<string, unknown>;\n return (\n typeof record[\"mistId\"] === \"string\" &&\n typeof record[\"operationKey\"] === \"string\" &&\n typeof record[\"sourceRevision\"] === \"string\" &&\n typeof record[\"requestedAt\"] === \"string\"\n );\n}\n\nexport interface ResumableStagingRequest {\n sourceRevision: string;\n /** An explicit key wins over a saved one. */\n operationKey?: string;\n store: StagingRequestStore;\n /** Told the key before the request is sent. */\n onOperationKey?: (key: string) => void;\n /** Told when a saved, unconfirmed request is being resumed. */\n onResume?: (key: string) => void;\n}\n\n/**\n * Request staging, remembering the key until the server answers. A saved\n * key for the same revision is reused, so a request the CLI never heard\n * back about resumes the same operation. The record is removed once the\n * server answers, whether it admitted the request or refused it, and kept\n * when the request may never have reached it.\n */\nexport async function requestStagingResumably(\n client: MistClient,\n slug: string,\n request: ResumableStagingRequest,\n): Promise<BranchOperation> {\n const { store, sourceRevision } = request;\n // Malformed input never reaches the store, where it would be replayed.\n checkStagingRequest(sourceRevision, request.operationKey);\n const mistId = await resolveMistId(client, slug);\n const savedKey = request.operationKey\n ? null\n : pendingKeyFor(store, mistId, sourceRevision);\n if (savedKey) request.onResume?.(savedKey);\n\n try {\n const operation = await admitStaging(client, slug, {\n sourceRevision,\n operationKey: request.operationKey ?? savedKey ?? undefined,\n onOperationKey: (key) => {\n store.save({\n mistId,\n operationKey: key,\n sourceRevision,\n requestedAt: new Date().toISOString(),\n });\n request.onOperationKey?.(key);\n },\n });\n store.remove(mistId);\n return operation;\n } catch (err) {\n if (serverAnswered(err)) store.remove(mistId);\n throw err;\n }\n}\n\n/**\n * Whether the server's answer settled the request. A network failure, a\n * rate limit, a server error or a response the CLI can't read may come\n * after the operation was recorded, so the key is kept for those and the\n * next request replays it.\n */\nfunction serverAnswered(err: unknown): boolean {\n if (err instanceof MistApiError) {\n return err.status !== 0 && err.status !== 429 && err.status < 500;\n }\n // A response that breaks the contract may still mean \"admitted\", so only\n // a readable operation that already ended settles the request.\n return err instanceof StagingOperationFailedError;\n}\n","/** `fluid mist staging wait` — follow a staging operation to the end. */\n\nimport { Command } from \"commander\";\nimport { createMistClient } from \"../../api/client.js\";\nimport { runAction } from \"../../lib/error-handler.js\";\nimport { printJson } from \"../../lib/output.js\";\nimport { resolveSlug } from \"../../lib/slug-resolver.js\";\nimport {\n describeProgress,\n readStagingStatus,\n waitForOperation,\n type WaitOptions,\n} from \"../../lib/staging.js\";\n\nconst POLL_INTERVAL_MS = 5_000;\nconst DEFAULT_TIMEOUT_MINUTES = 30;\n\ninterface Opts {\n operation?: string;\n timeout?: string;\n json?: boolean;\n}\n\n/** Polling options for a `--timeout <minutes>` flag, progress on stderr. */\nexport function waitOptions(timeout: string | undefined): WaitOptions {\n const minutes = timeout ? Number(timeout) : DEFAULT_TIMEOUT_MINUTES;\n if (!Number.isFinite(minutes) || minutes <= 0) {\n throw new Error(\"--timeout must be a positive number of minutes\");\n }\n return {\n intervalMs: POLL_INTERVAL_MS,\n timeoutMs: minutes * 60_000,\n onChange: (operation) =>\n process.stderr.write(`${describeProgress(operation)}\\n`),\n };\n}\n\nexport const stagingWaitCommand = new Command(\"wait\")\n .description(\"Follow a staging operation until it finishes\")\n .argument(\"[slug]\", \"The mist slug (defaults to .mist file in CWD)\")\n .option(\n \"--operation <id>\",\n \"Operation to follow (defaults to the one preparing staging)\",\n )\n .option(\"--timeout <minutes>\", \"How long to follow it (default 30)\")\n .option(\"--json\", \"Emit the finished operation as JSON\")\n .action((slug: string | undefined, opts: Opts) =>\n runAction(async () => {\n const resolved = resolveSlug(slug);\n const client = createMistClient();\n const options = waitOptions(opts.timeout);\n const operationId =\n opts.operation ??\n (await readStagingStatus(client, resolved)).activeOperation?.id;\n if (!operationId) {\n throw new Error(\n \"No staging operation is running. Check `fluid mist staging status`.\",\n );\n }\n\n const operation = await waitForOperation(\n client,\n resolved,\n operationId,\n options,\n );\n if (opts.json) return printJson({ operation });\n console.log(\n \"✔ Staging is ready. Run `fluid mist staging status` for its URL.\",\n );\n }),\n );\n","/** `fluid mist staging request` — request staging at a revision. */\n\nimport chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport { createMistClient } from \"../../api/client.js\";\nimport { createGitCredential } from \"../../api/git_credential.js\";\nimport { runAction } from \"../../lib/error-handler.js\";\nimport { printJson } from \"../../lib/output.js\";\nimport { resolveSlug } from \"../../lib/slug-resolver.js\";\nimport { describeProgress, waitForOperation } from \"../../lib/staging.js\";\nimport {\n createFileStagingRequestStore,\n requestStagingResumably,\n} from \"../../lib/staging-request-store.js\";\nimport { resolveLocalStagingRevision } from \"../../lib/staging-revision.js\";\nimport { waitOptions } from \"./wait.js\";\n\ninterface Opts {\n revision?: string;\n dir?: string;\n operationKey?: string;\n wait?: boolean;\n timeout?: string;\n json?: boolean;\n}\n\nexport const stagingRequestCommand = new Command(\"request\")\n .description(\"Request the staging environment at a pushed commit\")\n .argument(\"[slug]\", \"The mist slug (defaults to .mist file in CWD)\")\n .option(\n \"--revision <sha>\",\n \"Full commit SHA to stage (defaults to this checkout's HEAD when it is clean and on main)\",\n )\n .option(\n \"--operation-key <key>\",\n \"Resume an interrupted request with the key and --revision it printed\",\n )\n .option(\n \"--dir <path>\",\n \"Checkout to stage from when --revision is omitted (defaults to the current directory)\",\n )\n .option(\"--wait\", \"Follow the operation until staging is ready\")\n .option(\"--timeout <minutes>\", \"How long --wait follows it (default 30)\")\n .option(\"--json\", \"Emit the operation as JSON\")\n .action((slug: string | undefined, opts: Opts) =>\n runAction(async () => {\n if (opts.operationKey && !opts.revision) {\n // The server ties a key to the revision it was first sent with, and\n // the checkout may have moved since, so a resume names it.\n throw new Error(\n \"--operation-key needs the --revision it was first sent with. The interrupted request printed both.\",\n );\n }\n const resolved = resolveSlug(slug);\n const client = createMistClient();\n const sourceRevision =\n opts.revision ??\n (await localRevision(client, resolved, opts.dir ?? process.cwd()));\n let operation = await requestStagingResumably(client, resolved, {\n sourceRevision,\n operationKey: opts.operationKey,\n store: createFileStagingRequestStore(),\n onResume: (key) =>\n process.stderr.write(\n chalk.dim(\n `Resuming the unconfirmed request for ${sourceRevision.slice(0, 7)} (key ${key}).\\n`,\n ),\n ),\n onOperationKey: (key) =>\n process.stderr.write(\n chalk.dim(\n `Operation key ${key}. If this is interrupted, rerun with --operation-key ${key} --revision ${sourceRevision}.\\n`,\n ),\n ),\n });\n process.stderr.write(\n `Staging operation ${operation.id}: ${describeProgress(operation)}\\n`,\n );\n\n if (opts.wait) {\n operation = await waitForOperation(\n client,\n resolved,\n operation.id,\n waitOptions(opts.timeout),\n );\n }\n if (opts.json) return printJson({ operation });\n if (operation.state === \"succeeded\") {\n console.log(\n `✔ Staging is ready. Run \\`fluid mist staging status\\` for its URL.`,\n );\n } else {\n console.log(\n `Requested. Follow it with \\`fluid mist staging wait --operation ${operation.id}\\`.`,\n );\n }\n }),\n );\n\n/** The checkout's HEAD, once it is clean and already on the remote's main. */\nasync function localRevision(\n client: ReturnType<typeof createMistClient>,\n slug: string,\n checkout: string,\n): Promise<string> {\n const { git_credential: credential } = await createGitCredential(\n client,\n slug,\n );\n const revision = await resolveLocalStagingRevision(\n checkout,\n credential.remote_url,\n );\n process.stderr.write(\n chalk.dim(`Staging this checkout's HEAD, ${revision.slice(0, 7)}.\\n`),\n );\n return revision;\n}\n","/** `fluid mist staging teardown` — remove the staging environment. */\n\nimport chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport { removeBranchEnvironment } from \"../../api/branch_environments.js\";\nimport { createMistClient } from \"../../api/client.js\";\nimport { runAction } from \"../../lib/error-handler.js\";\nimport { printJson } from \"../../lib/output.js\";\nimport { resolveSlug } from \"../../lib/slug-resolver.js\";\nimport { readStagingStatus } from \"../../lib/staging.js\";\n\ninterface Opts {\n yes?: boolean;\n json?: boolean;\n}\n\nexport const stagingTeardownCommand = new Command(\"teardown\")\n .description(\n \"Tear down the staging environment: its deployment, variables, Git branch and database branch\",\n )\n .argument(\"[slug]\", \"The mist slug (defaults to .mist file in CWD)\")\n .option(\"--yes\", \"Confirm the teardown; nothing is removed without it\")\n .option(\"--json\", \"Emit the environment as JSON\")\n .action((slug: string | undefined, opts: Opts) =>\n runAction(async () => {\n const client = createMistClient();\n const mist = resolveSlug(slug);\n const { environment } = await readStagingStatus(client, mist);\n if (!environment || environment.state === \"deleted\") {\n if (opts.json) return printJson({ environment: null });\n console.log(chalk.dim(\"No staging environment to tear down.\"));\n return;\n }\n if (!opts.yes) {\n throw new Error(\n \"Tearing down staging removes its deployment, variables, Git branch and database branch. Rerun with --yes to confirm.\",\n );\n }\n\n const removed = await removeBranchEnvironment(\n client,\n mist,\n environment.id,\n );\n if (opts.json) return printJson({ environment: removed });\n console.log(\n removed.state === \"deleted\"\n ? \"Staging is already torn down.\"\n : `Staging is ${chalk.bold(removed.state)}. Its resources are removed within about a minute; check with \\`fluid mist staging status\\`.`,\n );\n }),\n );\n","/** `fluid mist staging` — sub-command aggregator. */\n\nimport { Command } from \"commander\";\nimport { stagingStatusCommand } from \"./status.js\";\nimport { stagingRequestCommand } from \"./request.js\";\nimport { stagingWaitCommand } from \"./wait.js\";\nimport { stagingTeardownCommand } from \"./teardown.js\";\n\nexport const stagingCommand = new Command(\"staging\")\n .description(\"Use a mist's isolated staging environment\")\n .addCommand(stagingStatusCommand)\n .addCommand(stagingRequestCommand)\n .addCommand(stagingWaitCommand)\n .addCommand(stagingTeardownCommand);\n","/**\n * @fluid-app/fluid-cli-mist\n *\n * Fluid CLI plugin for managing Mist hosted extensions. Auto-discovered\n * by @fluid-app/fluid-cli via the `fluid-cli-*` naming convention.\n *\n * Source of truth for command surface + behavior:\n * fluid (sibling repo) features/mist/cli-spec.md\n */\n\nimport { Command } from \"commander\";\nimport type { FluidPlugin, PluginContext } from \"@fluid-app/fluid-cli\";\nimport { listCommand } from \"./commands/list.js\";\nimport { showCommand } from \"./commands/show.js\";\nimport { createCommand } from \"./commands/create.js\";\nimport { attachCommand } from \"./commands/attach.js\";\nimport { attachDropletCommand } from \"./commands/attach-droplet.js\";\nimport { updateCommand } from \"./commands/update.js\";\nimport { deleteCommand } from \"./commands/delete.js\";\nimport { restoreCommand } from \"./commands/restore.js\";\nimport { cloneCommand } from \"./commands/clone.js\";\nimport { pushCommand } from \"./commands/push.js\";\nimport { pullCommand } from \"./commands/pull.js\";\nimport { openCommand } from \"./commands/open.js\";\nimport { deploymentsCommand } from \"./commands/deployments.js\";\nimport { logsCommand } from \"./commands/logs.js\";\nimport { envCommand } from \"./commands/env/index.js\";\nimport { dropletCommand } from \"./commands/droplet/index.js\";\nimport { stagingCommand } from \"./commands/staging/index.js\";\n\nconst plugin: FluidPlugin = {\n name: \"fluid-cli-mist\",\n version: \"0.1.32\",\n async register(ctx: PluginContext) {\n const mist = new Command(\"mist\").description(\n \"Create, manage, and deploy Mist hosted extensions\",\n );\n\n mist.addCommand(listCommand);\n mist.addCommand(showCommand);\n mist.addCommand(createCommand);\n mist.addCommand(attachCommand);\n mist.addCommand(attachDropletCommand);\n mist.addCommand(updateCommand);\n mist.addCommand(deleteCommand);\n mist.addCommand(restoreCommand);\n mist.addCommand(cloneCommand);\n mist.addCommand(pushCommand);\n mist.addCommand(pullCommand);\n mist.addCommand(openCommand);\n mist.addCommand(deploymentsCommand);\n mist.addCommand(logsCommand);\n mist.addCommand(envCommand);\n mist.addCommand(dropletCommand);\n mist.addCommand(stagingCommand);\n\n ctx.program.addCommand(mist);\n },\n};\n\nexport default plugin;\nexport type * from \"./types.js\";\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":["BASE","STATE_COLOR","BASE","PER_PAGE","MAX_PAGES","BASE","BASE","path","isRecord","defaultSleep","KIND_LABEL","ENV_ASSIGNMENT","POLL_MS","sleep"],"sources":["../src/api/client.ts","../src/api/id-resolver.ts","../src/api/mists.ts","../src/api/branch_environments.ts","../src/lib/hostable.ts","../src/lib/git.ts","../src/lib/slug-resolver.ts","../src/lib/staging-revision.ts","../src/lib/staging.ts","../src/lib/error-handler.ts","../src/lib/output.ts","../src/commands/list.ts","../src/api/integration_points.ts","../src/commands/show.ts","../src/lib/ora-reporter.ts","../src/lib/step-reporter.ts","../../../mist/core/src/lifecycle-urls.ts","../src/api/droplets.ts","../src/api/droplet_installations.ts","../src/api/drop_zones.ts","../src/api/mobile_widgets.ts","../src/api/env_vars.ts","../src/types.ts","../../../mist/core/src/droplet-lifecycle-repair.ts","../../../mist/core/src/lifecycle-types.ts","../../../mist/core/src/recovery-classification.ts","../../../mist/core/src/droplet-replacement.ts","../../../mist/core/src/operation-polling.ts","../src/api/droplet_replacements.ts","../src/lib/reinstall-plan-store.ts","../src/lib/droplet-repair.ts","../src/lib/droplet-egress.ts","../src/lib/surface-provision.ts","../src/lib/surface-flags.ts","../src/lib/surface-summary.ts","../src/commands/create.ts","../src/commands/attach.ts","../src/commands/attach-droplet.ts","../src/commands/update.ts","../src/commands/delete.ts","../src/commands/restore.ts","../src/api/git_credential.ts","../src/lib/env-placeholders.ts","../src/commands/clone.ts","../src/api/deployments.ts","../src/api/me.ts","../src/lib/rollback-subject.ts","../src/commands/push.ts","../src/commands/pull.ts","../src/commands/open.ts","../src/commands/deployments.ts","../src/api/logs.ts","../src/commands/logs.ts","../src/lib/encrypted-env.ts","../src/commands/env/list.ts","../../../mist/core/src/secret-generator.ts","../src/commands/env/set.ts","../src/commands/env/unset.ts","../src/commands/env/pull.ts","../src/commands/env/index.ts","../src/commands/droplet/egress-proxy.ts","../src/commands/droplet/reinstall.ts","../src/commands/droplet/repair.ts","../src/commands/droplet/index.ts","../src/commands/staging/status.ts","../src/lib/staging-request-store.ts","../src/commands/staging/wait.ts","../src/commands/staging/request.ts","../src/commands/staging/teardown.ts","../src/commands/staging/index.ts","../src/index.ts"],"sourcesContent":["/**\n * Generic fetch wrapper for the Mist Rails API.\n *\n * Spec: `features/mist/cli-spec.md` §4.\n * - Base URL: `https://api.fluid.app` (overridable via FLUID_API_BASE)\n * - Auth: `Authorization: Bearer <token>` from the standard fluid-cli token\n * store; the host CLI's PluginContext provides `getAuthToken()`.\n * - Errors: maps API responses to a typed `MistApiError` that the\n * error-handler turns into the spec's exit codes (1/2/3/4).\n */\n\nimport { getAuthToken } from \"@fluid-app/fluid-cli\";\n\nconst DEFAULT_BASE = \"https://api.fluid.app\";\n\nexport interface MistApiErrorBody {\n error?: {\n message?: string;\n details?: Record<string, unknown>;\n };\n /** Rails' legacy envelope names refused fields here, e.g.\n * `{ errors: { egress_proxy_requested: [\"must be a company admin\"] } }`. */\n errors?: Record<string, unknown>;\n status?: number;\n}\n\nexport class MistApiError extends Error {\n /** HTTP status, or 0 for network-level failures. */\n readonly status: number;\n readonly details: Record<string, unknown> | undefined;\n /** The response's top-level `errors` object (Rails' legacy envelope), when\n * it has one: the fields the API refused. */\n readonly fieldErrors: Record<string, unknown> | undefined;\n /** Best-guess exit code per the spec: 2 for 4xx, 3 for 5xx/vendor, 4 for net. */\n readonly exitCode: 2 | 3 | 4;\n\n constructor(\n message: string,\n status: number,\n details?: Record<string, unknown>,\n fieldErrors?: Record<string, unknown>,\n ) {\n super(message);\n this.name = \"MistApiError\";\n this.status = status;\n this.details = details;\n this.fieldErrors = fieldErrors;\n if (status === 0) this.exitCode = 4;\n else if (status >= 500) this.exitCode = 3;\n else this.exitCode = 2;\n }\n}\n\nfunction apiBase(): string {\n return process.env[\"FLUID_API_BASE\"] ?? DEFAULT_BASE;\n}\n\nexport interface MistClient {\n get<T>(path: string, query?: Record<string, unknown>): Promise<T>;\n /** `headers` carries request metadata such as an idempotency key. */\n post<T>(\n path: string,\n body?: unknown,\n headers?: Record<string, string>,\n ): Promise<T>;\n patch<T>(path: string, body?: unknown): Promise<T>;\n delete<T>(path: string, body?: unknown): Promise<T>;\n /** Raw `fetch` for streaming endpoints (logs polling). */\n request(path: string, init?: RequestInit): Promise<Response>;\n}\n\n/** Build a typed Mist API client. Throws if the user isn't logged in. */\nexport function createMistClient(): MistClient {\n const token = getAuthToken();\n if (!token) {\n throw new MistApiError(\n \"No active Fluid session — run `fluid login` first.\",\n 401,\n );\n }\n const base = apiBase();\n\n function buildUrl(path: string, query?: Record<string, unknown>): string {\n const url = new URL(path.startsWith(\"/\") ? path : `/${path}`, base);\n if (query) {\n for (const [k, v] of Object.entries(query)) {\n if (v == null) continue;\n url.searchParams.set(k, String(v));\n }\n }\n return url.toString();\n }\n\n async function call<T>(\n method: string,\n path: string,\n opts?: {\n body?: unknown;\n query?: Record<string, unknown>;\n headers?: Record<string, string>;\n },\n ): Promise<T> {\n const url = buildUrl(path, opts?.query);\n let response: Response;\n try {\n response = await fetch(url, {\n method,\n headers: {\n Authorization: `Bearer ${token}`,\n Accept: \"application/json\",\n ...(opts?.body !== undefined\n ? { \"Content-Type\": \"application/json\" }\n : {}),\n ...opts?.headers,\n },\n ...(opts?.body !== undefined\n ? { body: JSON.stringify(opts.body) }\n : {}),\n });\n } catch (err) {\n throw new MistApiError(\n `Network error: ${err instanceof Error ? err.message : String(err)}`,\n 0,\n );\n }\n\n // 204 No Content\n if (response.status === 204) return undefined as T;\n\n const text = await response.text();\n let parsed: unknown;\n try {\n parsed = text.length > 0 ? JSON.parse(text) : {};\n } catch {\n // Non-JSON body — treat as opaque error if non-2xx.\n if (!response.ok) {\n throw new MistApiError(\n `HTTP ${response.status}: ${text.slice(0, 200)}`,\n response.status,\n );\n }\n return undefined as T;\n }\n\n if (!response.ok) {\n const body = parsed as MistApiErrorBody;\n const message = body.error?.message ?? `HTTP ${response.status}`;\n const fieldErrors =\n body.errors && typeof body.errors === \"object\"\n ? body.errors\n : undefined;\n throw new MistApiError(\n message,\n response.status,\n body.error?.details,\n fieldErrors,\n );\n }\n return parsed as T;\n }\n\n return {\n get: (path, query) => call(\"GET\", path, { query }),\n post: (path, body, headers) => call(\"POST\", path, { body, headers }),\n patch: (path, body) => call(\"PATCH\", path, { body }),\n delete: (path, body) =>\n call(\"DELETE\", path, body !== undefined ? { body } : undefined),\n async request(path, init) {\n const url = buildUrl(path);\n return fetch(url, {\n ...init,\n headers: {\n Authorization: `Bearer ${token}`,\n Accept: \"application/json\",\n ...(init?.headers ?? {}),\n },\n });\n },\n };\n}\n","/**\n * Slug → UUID id resolver.\n *\n * Despite the spec describing `/api/v202604/mists/:id` as accepting the\n * 6-char slug, the Rails route only accepts the UUID `id`. Every command\n * the user invokes (`show`, `delete`, `restore`, `clone`, `push`, `env *`,\n * `deployments`, `logs`) takes a slug and would otherwise 404. We hide\n * the lookup here so the API surface (`showMist`, `deleteMist`, etc.)\n * keeps accepting `slug` strings — they internally call this resolver\n * once and use the returned UUID.\n *\n * The cache is process-local; one terminal invocation hits the list\n * endpoint at most once.\n */\n\nimport type { MistClient } from \"./client.js\";\nimport type { MistListResponse } from \"../types.js\";\n\n// We deliberately don't import listMists from ./mists here — that\n// would create a cycle (mists.ts imports resolveMistId from this\n// file). Instead, hit the same endpoint directly. Kept private to\n// this module since it's just the cycle-breaker.\nconst MISTS_BASE = \"/api/v202604/mists\";\nfunction listMistsForResolve(\n client: MistClient,\n params: { limit?: number } = {},\n): Promise<MistListResponse> {\n return client.get<MistListResponse>(\n MISTS_BASE,\n params as Record<string, unknown>,\n );\n}\n\nconst cache = new Map<string, string>();\n\nconst UUID_REGEX =\n /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n/** Returns true if the input already looks like a UUID we can use as-is. */\nfunction isUuid(s: string): boolean {\n return UUID_REGEX.test(s.trim());\n}\n\n/**\n * Resolve a user-supplied slug-or-id to the API's canonical UUID id.\n * No-op when the input already looks like a UUID.\n */\nexport async function resolveMistId(\n client: MistClient,\n slugOrId: string,\n): Promise<string> {\n const key = slugOrId.trim();\n if (!key) throw new Error(\"Empty mist identifier.\");\n if (isUuid(key)) return key;\n const hit = cache.get(key);\n if (hit) return hit;\n // The slug isn't a UUID — look it up via the list endpoint. limit=100\n // covers the vast majority of users; if the user has more than 100\n // mists and the one they want isn't on page 1, we'd need to paginate.\n // For now we keep it cheap and accept that pagination is a follow-up.\n const response = await listMistsForResolve(client, { limit: 100 });\n for (const m of response.mists ?? []) {\n if (m.slug) cache.set(m.slug, m.id);\n }\n const found = cache.get(key);\n if (!found) {\n throw new Error(\n `No mist found with slug \"${key}\". Try \\`fluid mist list\\` to see what's available.`,\n );\n }\n return found;\n}\n","/**\n * Mist CRUD over `/api/v202604/mists`. Mirrors the spec's endpoint table\n * one-for-one — no business logic.\n */\n\nimport type { MistClient } from \"./client.js\";\nimport type {\n MistCreateRequest,\n MistListResponse,\n MistShowResponse,\n MistUpdateRequest,\n} from \"../types.js\";\nimport { resolveMistId } from \"./id-resolver.js\";\n\nconst BASE = \"/api/v202604/mists\";\n\nexport interface ListMistsParams {\n state?: \"live\" | \"provisioning\" | \"failed\" | \"pending_destroy\" | \"archived\";\n kind?: \"next_app\";\n limit?: number;\n cursor?: string;\n}\n\nexport function listMists(\n client: MistClient,\n params: ListMistsParams = {},\n): Promise<MistListResponse> {\n return client.get<MistListResponse>(BASE, params as Record<string, unknown>);\n}\n\nexport async function showMist(\n client: MistClient,\n slug: string,\n): Promise<MistShowResponse> {\n const id = await resolveMistId(client, slug);\n return client.get<MistShowResponse>(`${BASE}/${id}`);\n}\n\nexport function createMist(\n client: MistClient,\n body: MistCreateRequest,\n): Promise<MistShowResponse> {\n return client.post<MistShowResponse>(BASE, body);\n}\n\nexport async function updateMist(\n client: MistClient,\n slug: string,\n body: MistUpdateRequest,\n): Promise<MistShowResponse> {\n const id = await resolveMistId(client, slug);\n return client.patch<MistShowResponse>(`${BASE}/${id}`, body);\n}\n\nexport async function deleteMist(\n client: MistClient,\n slug: string,\n): Promise<MistShowResponse> {\n const id = await resolveMistId(client, slug);\n return client.delete<MistShowResponse>(`${BASE}/${id}`);\n}\n\nexport async function restoreMist(\n client: MistClient,\n slug: string,\n): Promise<MistShowResponse> {\n const id = await resolveMistId(client, slug);\n return client.post<MistShowResponse>(`${BASE}/${id}/restore`);\n}\n","/**\n * Branch environments and branch operations for a Mist project.\n *\n * Server contracts (Rails, internal API v2026-04):\n * apps/rails/docs/openapi/internal/mist-branch-environments-v2026-04.yaml\n * apps/rails/docs/openapi/internal/mist-branch-operations-v2026-04.yaml\n *\n * Every response is checked at runtime before a caller sees it. A server\n * whose listing has no `capability` block predates branch environments and\n * reads as unsupported. A response that does not match the contract throws\n * `BranchContractError`; neither case is ever a reason to fall back to a\n * production publish.\n */\n\nimport { randomUUID } from \"node:crypto\";\nimport type { MistClient } from \"./client.js\";\nimport { resolveMistId } from \"./id-resolver.js\";\n\nexport const ENVIRONMENT_STATES = [\n \"provisioning\",\n \"ready\",\n \"error\",\n \"expired\",\n \"deleting\",\n \"deleted\",\n] as const;\nexport type EnvironmentState = (typeof ENVIRONMENT_STATES)[number];\n\nexport const OPERATION_KINDS = [\n \"ensure_staging\",\n \"create\",\n \"delete\",\n \"promote\",\n \"publish\",\n] as const;\nexport type OperationKind = (typeof OPERATION_KINDS)[number];\n\nexport const OPERATION_STATES = [\n \"queued\",\n \"running\",\n \"awaiting_content\",\n \"awaiting_resolution\",\n \"retryable_failure\",\n \"succeeded\",\n \"failed\",\n \"cancelled\",\n \"stale\",\n] as const;\nexport type OperationState = (typeof OPERATION_STATES)[number];\n\n/** States after which the server will not advance the operation again. */\nexport const TERMINAL_OPERATION_STATES: readonly OperationState[] = [\n \"succeeded\",\n \"failed\",\n \"cancelled\",\n \"stale\",\n];\n\nexport interface BranchEnvironment {\n id: string;\n slug: string;\n state: EnvironmentState;\n /** True for the reserved staging environment. */\n seeded: boolean;\n /** Stable address; null unless ready, and null for some older ready ones. */\n url: string | null;\n created_at: string;\n updated_at: string;\n}\n\nexport interface StagingCapability {\n /** Whether a new staging request would be admitted now. */\n available: boolean;\n /** Why it would be refused (e.g. `credentials_not_retired`), or null. */\n reason: string | null;\n /** The operation already working on staging, to follow instead. */\n active_operation_id: string | null;\n}\n\nexport type BranchCapability =\n | { supported: false }\n | { supported: true; version: number; staging: StagingCapability };\n\nexport interface BranchEnvironmentsListing {\n environments: BranchEnvironment[];\n capability: BranchCapability;\n}\n\nexport interface OperationProgress {\n database_isolated: boolean;\n branch_published: boolean;\n build_ready: boolean;\n}\n\nexport interface BranchOperation {\n id: string;\n kind: OperationKind;\n state: OperationState;\n source_revision: string;\n branch_id: string | null;\n progress: OperationProgress;\n /** Failure classification such as `build_failed`; never a description. */\n failure: { type: string } | null;\n created_at: string;\n updated_at: string;\n}\n\n/** A server response that does not match the branch environments contract. */\nexport class BranchContractError extends Error {\n constructor(message: string) {\n super(`Unexpected branch environments response: ${message}`);\n this.name = \"BranchContractError\";\n }\n}\n\nconst SOURCE_REVISION = /^[0-9a-f]{40}$/;\nconst OPERATION_KEY = /^[A-Za-z0-9._:-]{1,128}$/;\n\nfunction base(mistId: string): string {\n return `/api/v202604/mists/${mistId}`;\n}\n\n/** List the project's branch environments and what the server offers. */\nexport async function listBranchEnvironments(\n client: MistClient,\n slug: string,\n): Promise<BranchEnvironmentsListing> {\n const id = await resolveMistId(client, slug);\n const body = await client.get<unknown>(`${base(id)}/branch-environments`);\n return parseListing(body);\n}\n\n/**\n * Request the staging branch at `sourceRevision`. Reusing `operationKey`\n * with the same revision returns the same operation, so a caller persists\n * the key before calling and retries with it.\n */\nexport async function requestStaging(\n client: MistClient,\n slug: string,\n request: { sourceRevision: string; operationKey: string },\n): Promise<BranchOperation> {\n checkStagingRequest(request.sourceRevision, request.operationKey);\n const id = await resolveMistId(client, slug);\n const body = await client.post<unknown>(\n `${base(id)}/branch-environments/staging`,\n { source_revision: request.sourceRevision },\n { \"X-Operation-Key\": request.operationKey },\n );\n return parseOperationEnvelope(body);\n}\n\n/**\n * Ask the server to tear down one branch environment. The server only takes\n * it out of service and answers with it deleting (or already deleting or\n * deleted); a worker removes its resources shortly after. Repeating the\n * call is harmless.\n */\nexport async function removeBranchEnvironment(\n client: MistClient,\n slug: string,\n environmentId: string,\n): Promise<BranchEnvironment> {\n const id = await resolveMistId(client, slug);\n const body = await client.delete<unknown>(\n `${base(id)}/branch-environments/${encodeURIComponent(environmentId)}`,\n );\n return parseEnvironment(\n record(body, \"response\")[\"environment\"],\n \"environment\",\n );\n}\n\n/** Read one branch operation of the project. Never advances it. */\nexport async function fetchBranchOperation(\n client: MistClient,\n slug: string,\n operationId: string,\n): Promise<BranchOperation> {\n const id = await resolveMistId(client, slug);\n const body = await client.get<unknown>(\n `${base(id)}/branch-operations/${encodeURIComponent(operationId)}`,\n );\n return parseOperationEnvelope(body);\n}\n\n/**\n * Refuse a revision or operation key the server would reject, before\n * anything is sent or saved.\n *\n * Raises Error naming the malformed value.\n */\nexport function checkStagingRequest(\n sourceRevision: string,\n operationKey?: string,\n): void {\n if (!SOURCE_REVISION.test(sourceRevision)) {\n throw new Error(\"Source revision must be a full 40-character commit SHA\");\n }\n if (operationKey !== undefined && !isOperationKey(operationKey)) {\n throw new Error(\"Operation key must be 1-128 of A-Z a-z 0-9 . _ : -\");\n }\n}\n\n/** Whether the server would accept `key` as an operation key. */\nexport function isOperationKey(key: string): boolean {\n return OPERATION_KEY.test(key);\n}\n\n/** A fresh key for a new staging request. */\nexport function newOperationKey(): string {\n return `staging-${randomUUID()}`;\n}\n\nexport function isTerminal(operation: BranchOperation): boolean {\n return TERMINAL_OPERATION_STATES.includes(operation.state);\n}\n\nexport function parseListing(body: unknown): BranchEnvironmentsListing {\n const envelope = record(body, \"response\");\n const environments = array(envelope[\"environments\"], \"environments\").map(\n (item, index) => parseEnvironment(item, `environments[${index}]`),\n );\n return {\n environments,\n capability: parseCapability(envelope[\"capability\"]),\n };\n}\n\nfunction parseCapability(raw: unknown): BranchCapability {\n if (raw === undefined) return { supported: false };\n\n const capability = record(raw, \"capability\");\n const version = capability[\"version\"];\n if (\n typeof version !== \"number\" ||\n !Number.isInteger(version) ||\n version < 1\n ) {\n throw new BranchContractError(\n \"capability.version is not a positive integer\",\n );\n }\n const staging = record(capability[\"staging\"], \"capability.staging\");\n return {\n supported: true,\n version,\n staging: {\n available: boolean(staging[\"available\"], \"capability.staging.available\"),\n reason: nullableString(staging[\"reason\"], \"capability.staging.reason\"),\n active_operation_id: nullableString(\n staging[\"active_operation_id\"],\n \"capability.staging.active_operation_id\",\n ),\n },\n };\n}\n\nfunction parseEnvironment(raw: unknown, path: string): BranchEnvironment {\n const environment = record(raw, path);\n const url = nullableString(environment[\"url\"], `${path}.url`);\n if (url !== null && !url.startsWith(\"https://\")) {\n throw new BranchContractError(`${path}.url is not an https address`);\n }\n return {\n id: string(environment[\"id\"], `${path}.id`),\n slug: string(environment[\"slug\"], `${path}.slug`),\n state: member(environment[\"state\"], ENVIRONMENT_STATES, `${path}.state`),\n seeded: boolean(environment[\"seeded\"], `${path}.seeded`),\n url,\n created_at: string(environment[\"created_at\"], `${path}.created_at`),\n updated_at: string(environment[\"updated_at\"], `${path}.updated_at`),\n };\n}\n\nexport function parseOperationEnvelope(body: unknown): BranchOperation {\n return parseOperation(record(body, \"response\")[\"operation\"]);\n}\n\nfunction parseOperation(raw: unknown): BranchOperation {\n const operation = record(raw, \"operation\");\n const progress = record(operation[\"progress\"], \"operation.progress\");\n const sourceRevision = string(\n operation[\"source_revision\"],\n \"operation.source_revision\",\n );\n if (!SOURCE_REVISION.test(sourceRevision)) {\n throw new BranchContractError(\n \"operation.source_revision is not a commit SHA\",\n );\n }\n return {\n id: string(operation[\"id\"], \"operation.id\"),\n kind: member(operation[\"kind\"], OPERATION_KINDS, \"operation.kind\"),\n state: member(operation[\"state\"], OPERATION_STATES, \"operation.state\"),\n source_revision: sourceRevision,\n branch_id: nullableString(operation[\"branch_id\"], \"operation.branch_id\"),\n progress: {\n database_isolated: boolean(\n progress[\"database_isolated\"],\n \"operation.progress.database_isolated\",\n ),\n branch_published: boolean(\n progress[\"branch_published\"],\n \"operation.progress.branch_published\",\n ),\n build_ready: boolean(\n progress[\"build_ready\"],\n \"operation.progress.build_ready\",\n ),\n },\n failure: parseFailure(operation[\"failure\"]),\n created_at: string(operation[\"created_at\"], \"operation.created_at\"),\n updated_at: string(operation[\"updated_at\"], \"operation.updated_at\"),\n };\n}\n\nfunction parseFailure(raw: unknown): { type: string } | null {\n if (raw === null) return null;\n const failure = record(raw, \"operation.failure\");\n return { type: string(failure[\"type\"], \"operation.failure.type\") };\n}\n\nfunction record(raw: unknown, path: string): Record<string, unknown> {\n if (typeof raw !== \"object\" || raw === null || Array.isArray(raw)) {\n throw new BranchContractError(`${path} is not an object`);\n }\n return raw as Record<string, unknown>;\n}\n\nfunction array(raw: unknown, path: string): unknown[] {\n if (!Array.isArray(raw))\n throw new BranchContractError(`${path} is not a list`);\n return raw;\n}\n\nfunction string(raw: unknown, path: string): string {\n if (typeof raw !== \"string\") {\n throw new BranchContractError(`${path} is not a string`);\n }\n return raw;\n}\n\nfunction nullableString(raw: unknown, path: string): string | null {\n return raw === null ? null : string(raw, path);\n}\n\nfunction boolean(raw: unknown, path: string): boolean {\n if (typeof raw !== \"boolean\") {\n throw new BranchContractError(`${path} is not a boolean`);\n }\n return raw;\n}\n\nfunction member<T extends string>(\n raw: unknown,\n allowed: readonly T[],\n path: string,\n): T {\n if (\n typeof raw !== \"string\" ||\n !(allowed as readonly string[]).includes(raw)\n ) {\n throw new BranchContractError(`${path} has an unknown value`);\n }\n return raw as T;\n}\n","/**\n * Hostable reference parser. Accepts `<type>:<id>` shorthand and maps the\n * type half to the canonical Rails class name the API expects.\n *\n * Wire mapping (spec table):\n * droplet → Droplet::Application\n * mobile_widget → MobileWidget\n * drop_zone → DropZone\n *\n * The shorthand is forgiving (case-insensitive, accepts dashes or\n * underscores), but the output is exact so the Rails `included_in?`\n * check accepts it.\n */\n\nimport type { HostableType } from \"../types.js\";\n\nconst SHORTHAND: Record<string, HostableType> = {\n droplet: \"Droplet::Application\",\n droplets: \"Droplet::Application\",\n mobile_widget: \"MobileWidget\",\n \"mobile-widget\": \"MobileWidget\",\n mobilewidget: \"MobileWidget\",\n widget: \"MobileWidget\",\n drop_zone: \"DropZone\",\n \"drop-zone\": \"DropZone\",\n dropzone: \"DropZone\",\n};\n\n/** Friendly label for help text + error output. */\nexport const SHORTHAND_LIST = \"droplet, mobile_widget, drop_zone\";\n\nexport class HostableParseError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"HostableParseError\";\n }\n}\n\nexport interface HostablePair {\n hostable_type: HostableType;\n hostable_id: number;\n}\n\n/**\n * Parse a `<type>:<id>` shorthand, or accept already-canonical\n * `Droplet::Application:42` if the user prefers the Rails-style form.\n */\nexport function parseHostableRef(raw: string): HostablePair {\n const trimmed = raw.trim();\n if (!trimmed) {\n throw new HostableParseError(\n `Empty hostable reference. Use <type>:<id> where <type> is one of ${SHORTHAND_LIST}.`,\n );\n }\n // Split on the LAST colon so `Droplet::Application:42` still works.\n const lastColon = trimmed.lastIndexOf(\":\");\n if (lastColon <= 0 || lastColon === trimmed.length - 1) {\n throw new HostableParseError(\n `Invalid hostable reference \"${raw}\". Expected <type>:<id>, e.g. droplet:42.`,\n );\n }\n const typePart = trimmed.slice(0, lastColon).trim();\n const idPart = trimmed.slice(lastColon + 1).trim();\n const id = Number(idPart);\n if (!Number.isInteger(id) || id <= 0) {\n throw new HostableParseError(\n `Invalid hostable id \"${idPart}\". Must be a positive integer.`,\n );\n }\n const canonical = canonicalizeHostableType(typePart);\n return { hostable_type: canonical, hostable_id: id };\n}\n\n/**\n * Parse a `--hostable` ref down to a droplet uuid. Hostable is retired\n * (#19684/#19687) and droplets attach by their `drp_…` uuid (the numeric id\n * isn't exposed anywhere the CLI can read), so the ref carries a uuid:\n *\n * droplet:drp_abc123 → \"drp_abc123\"\n * drp_abc123 → \"drp_abc123\" (bare uuid, no prefix)\n *\n * A numeric ref (the old contract) is rejected with a pointer to the uuid.\n */\nexport function parseDropletRef(raw: string): string {\n const trimmed = raw.trim();\n if (!trimmed) {\n throw new HostableParseError(\n \"Empty droplet reference. Use `droplet:<uuid>` (e.g. droplet:drp_abc123).\",\n );\n }\n // Bare uuid (no `type:` prefix).\n if (/^drp_[A-Za-z0-9]+$/.test(trimmed)) return trimmed;\n\n const lastColon = trimmed.lastIndexOf(\":\");\n if (lastColon <= 0 || lastColon === trimmed.length - 1) {\n throw new HostableParseError(\n `Invalid droplet reference \"${raw}\". Expected droplet:<uuid>, e.g. droplet:drp_abc123.`,\n );\n }\n const typePart = trimmed.slice(0, lastColon).trim();\n const idPart = trimmed.slice(lastColon + 1).trim();\n if (canonicalizeHostableType(typePart) !== \"Droplet::Application\") {\n throw new HostableParseError(\n `Only droplets can be attached now that hostable is retired. Use \\`droplet:<uuid>\\` (got \"${raw}\").`,\n );\n }\n if (/^\\d+$/.test(idPart)) {\n throw new HostableParseError(\n `Droplets now attach by uuid, not numeric id. Use \\`droplet:<uuid>\\` (e.g. droplet:drp_abc123), not \"${raw}\".`,\n );\n }\n if (!/^drp_[A-Za-z0-9]+$/.test(idPart)) {\n throw new HostableParseError(\n `Invalid droplet uuid \"${idPart}\". Expected a drp_… uuid.`,\n );\n }\n return idPart;\n}\n\n/** Map an input string to the canonical Rails class name, throwing on miss. */\nexport function canonicalizeHostableType(typePart: string): HostableType {\n const exact: HostableType[] = [\n \"Droplet::Application\",\n \"MobileWidget\",\n \"DropZone\",\n ];\n if (exact.includes(typePart as HostableType)) return typePart as HostableType;\n const looked = SHORTHAND[typePart.toLowerCase().replace(/\\s+/g, \"\")];\n if (looked) return looked;\n throw new HostableParseError(\n `Unknown hostable type \"${typePart}\". Use one of: ${SHORTHAND_LIST}.`,\n );\n}\n","/**\n * Git wrapper — shells out to system `git` via execa. Spec §11.1: we\n * deliberately do NOT bundle git or use a pure-JS impl; users are expected\n * to have it installed.\n *\n * Security: token-bearing `remote_url` from POST /git_credential must never\n * be logged, persisted, *or* exposed in the process table. The token is\n * stripped from the URL and handed to git through a GIT_ASKPASS script via\n * env vars (MIST_GIT_USER / MIST_GIT_PASS) — so it never appears in argv\n * (visible to `ps aux` on macOS, `/proc/<pid>/cmdline` on Linux) and is\n * never written to `.git/config`.\n */\n\nimport {\n parseNameStatusZ,\n type ChangeEntry,\n type CommitMessage,\n} from \"@fluid-app/fluid-cli\";\nimport {\n chmodSync,\n existsSync,\n mkdirSync,\n readFileSync,\n rmSync,\n writeFileSync,\n} from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport { execa, type ExecaError } from \"execa\";\n\nexport class GitError extends Error {\n readonly exitCode: number;\n\n constructor(message: string, exitCode = 1) {\n super(message);\n this.name = \"GitError\";\n this.exitCode = exitCode;\n }\n}\n\n/**\n * Git author for Mist publish commits. Mist app repos are connected to the\n * we-commerce Vercel team via Vercel's GitHub integration, and Vercel\n * attributes a git-triggered deploy to the *commit author's* GitHub account.\n * If that author is a user's personal GitHub account (not a member of the\n * Vercel team), Vercel blocks the deployment (\"… does not have a Vercel\n * account linked to their GitHub account\"). Authoring every publish commit as\n * the Mist GitHub App bot — which the Vercel integration recognizes — keeps\n * deploys unblocked regardless of the user's local/global git identity. Human\n * attribution is preserved in the commit *message* (see push.ts), not the\n * author field.\n *\n * These MUST match the identity set on cloned repos in Mist Desktop\n * (apps/mist-desktop/src/main/ipc.ts) so both paths produce the same author.\n */\nconst MIST_GIT_AUTHOR_NAME = \"mist-we-commerce[bot]\";\nconst MIST_GIT_AUTHOR_EMAIL =\n \"293596226+mist-we-commerce[bot]@users.noreply.github.com\";\n\n/** execa throws ENOENT when the git binary itself is missing — the\n * single most common failure on non-developer machines. Translate it\n * into an actionable message instead of \"git <verb> failed (exit 1)\".\n * Returns null for every other failure so call sites keep their\n * specific messages. */\nfunction gitMissingError(err: unknown): GitError | null {\n const code = (err as NodeJS.ErrnoException | undefined)?.code;\n if (code !== \"ENOENT\") return null;\n return new GitError(\n \"Git isn't installed on this machine. In Mist Desktop, use the Install button in the yellow banner at the top of the app. From a terminal: macOS `xcode-select --install`, or download from https://git-scm.com/downloads.\",\n 127,\n );\n}\n\n/**\n * Split a token-bearing remote URL into the userless URL + creds. Used\n * to keep the token off git's argv: we pass `cleanUrl` to git and feed\n * `username` / `password` back through GIT_ASKPASS env vars.\n *\n * If the URL has no embedded credentials, returns empty strings — fine\n * for public repos where git won't even invoke askpass.\n */\nfunction splitRemoteCredentials(remoteUrl: string): {\n cleanUrl: string;\n username: string;\n password: string;\n} {\n const u = new URL(remoteUrl);\n const username = decodeURIComponent(u.username);\n const password = decodeURIComponent(u.password);\n u.username = \"\";\n u.password = \"\";\n return { cleanUrl: u.toString(), username, password };\n}\n\n/**\n * Write a stub GIT_ASKPASS shell script to a per-process tmpdir and\n * return its path. The script itself contains no secret — it just\n * echoes back whichever env var (MIST_GIT_USER / MIST_GIT_PASS)\n * matches git's prompt (\"Username for …\" / \"Password for …\"). The\n * credentials only live in the spawned git process's env for the\n * duration of the call.\n *\n * One POSIX script for all platforms: Git for Windows executes\n * GIT_ASKPASS through its bundled sh, so a .cmd askpass fails with\n * \"unable to read askpass response\" (git 2.52.0.windows.1) while a\n * plain sh script works.\n *\n * Idempotent: subsequent calls reuse the same script path.\n */\nlet askpassPath: string | null = null;\nfunction ensureAskpassScript(): string {\n if (askpassPath && existsSync(askpassPath)) return askpassPath;\n const dir = path.join(os.tmpdir(), `mist-cli-askpass-${process.pid}`);\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true, mode: 0o700 });\n const p = path.join(dir, \"askpass.sh\");\n writeFileSync(\n p,\n `#!/bin/sh\\ncase \"$1\" in\\n Username*) printf '%s' \"$MIST_GIT_USER\" ;;\\n *) printf '%s' \"$MIST_GIT_PASS\" ;;\\nesac\\n`,\n { mode: 0o700 },\n );\n chmodSync(p, 0o700);\n askpassPath = p;\n return askpassPath;\n}\n\n/**\n * Build the spawn env + cleaned URL for a credentialed git invocation.\n * Returns:\n * - `cleanUrl`: URL with userinfo stripped — safe to pass as argv.\n * - `env`: parent env plus GIT_ASKPASS + MIST_GIT_USER / MIST_GIT_PASS.\n * GIT_TERMINAL_PROMPT=0 prevents the TTY-prompt fallback if\n * the credentials are empty (we want auth to fail fast, not\n * hang the desktop waiting for interactive input).\n */\nfunction gitAuthSpawn(remoteUrl: string): {\n cleanUrl: string;\n env: NodeJS.ProcessEnv;\n} {\n const { cleanUrl, username, password } = splitRemoteCredentials(remoteUrl);\n return {\n cleanUrl,\n env: {\n ...process.env,\n GIT_ASKPASS: ensureAskpassScript(),\n GIT_TERMINAL_PROMPT: \"0\",\n MIST_GIT_USER: username,\n MIST_GIT_PASS: password,\n },\n };\n}\n\n/** Confirm git is installed. Cheap call once at startup. */\nexport async function ensureGitAvailable(): Promise<void> {\n try {\n await execa(\"git\", [\"--version\"]);\n } catch {\n throw new GitError(\n \"`git` is required but not found on your PATH. Install it from https://git-scm.com/downloads or via `brew install git`.\",\n );\n }\n}\n\n/**\n * Abort a transfer that has moved under 1 KB/s for a full minute. Without\n * this, git on a dropped or captive-portal Wi-Fi connection waits forever\n * and the caller (Fluid Mist's clone/sync) shows a spinner that never ends;\n * with it, the command fails and can be retried.\n */\nexport const GIT_STALL_GUARD_ARGS = [\n \"-c\",\n \"http.lowSpeedLimit=1000\",\n \"-c\",\n \"http.lowSpeedTime=60\",\n] as const;\n\n/**\n * `git clone <cleanUrl> <targetDir>`. The original remoteUrl carries a\n * token; gitAuthSpawn strips it from the URL and re-injects it via\n * GIT_ASKPASS so it never appears in argv. `credential.helper=` stops\n * git from caching what askpass returned to any system credential\n * store.\n */\nexport async function gitClone(\n remoteUrl: string,\n targetDir: string,\n): Promise<void> {\n const { cleanUrl, env } = gitAuthSpawn(remoteUrl);\n try {\n await execa(\n \"git\",\n [\n ...GIT_STALL_GUARD_ARGS,\n \"-c\",\n \"credential.helper=\",\n \"clone\",\n \"--quiet\",\n cleanUrl,\n targetDir,\n ],\n { env, stdio: [\"ignore\", \"inherit\", \"inherit\"] },\n );\n } catch (err) {\n const missing = gitMissingError(err);\n if (missing) throw missing;\n const e = err as ExecaError;\n throw new GitError(\n `git clone failed${e.exitCode != null ? ` (exit ${e.exitCode})` : \"\"}`,\n e.exitCode ?? 1,\n );\n }\n // Every commit made in this checkout should be bot-authored (Vercel\n // only deploys we-commerce team members). See ensureBotIdentity.\n await ensureBotIdentity(targetDir);\n}\n\n/** `git push origin <branch>` — credentials supplied via GIT_ASKPASS,\n * never in argv. The `.extraheader=` override clears any cached\n * Authorization header that a CI-style git config might inject. */\nexport async function gitPush(\n cwd: string,\n remoteUrl: string,\n branch = \"main\",\n): Promise<void> {\n const { cleanUrl, env } = gitAuthSpawn(remoteUrl);\n try {\n await execa(\n \"git\",\n [\n \"-c\",\n \"credential.helper=\",\n \"-c\",\n `http.https://github.com/.extraheader=`,\n \"push\",\n cleanUrl,\n `HEAD:${branch}`,\n ],\n { cwd, env, stdio: [\"ignore\", \"inherit\", \"inherit\"] },\n );\n } catch (err) {\n const missing = gitMissingError(err);\n if (missing) throw missing;\n const e = err as ExecaError;\n throw new GitError(\n `git push failed${e.exitCode != null ? ` (exit ${e.exitCode})` : \"\"}`,\n e.exitCode ?? 1,\n );\n }\n}\n\n/** `git pull origin <branch>`. Token supplied via GIT_ASKPASS, same as push. */\nexport async function gitPull(\n cwd: string,\n remoteUrl: string,\n branch = \"main\",\n): Promise<void> {\n const { cleanUrl, env } = gitAuthSpawn(remoteUrl);\n try {\n await execa(\n \"git\",\n [\n ...GIT_STALL_GUARD_ARGS,\n \"-c\",\n \"credential.helper=\",\n \"pull\",\n \"--ff-only\",\n cleanUrl,\n branch,\n ],\n { cwd, env, stdio: [\"ignore\", \"inherit\", \"inherit\"] },\n );\n } catch (err) {\n const missing = gitMissingError(err);\n if (missing) throw missing;\n const e = err as ExecaError;\n throw new GitError(\n `git pull failed${e.exitCode != null ? ` (exit ${e.exitCode})` : \"\"}`,\n e.exitCode ?? 1,\n );\n }\n}\n\n/** True when `relativePath` is ignored by the repo at `cwd`. */\nexport async function isGitIgnored(\n cwd: string,\n relativePath: string,\n): Promise<boolean> {\n try {\n await execa(\"git\", [\"check-ignore\", \"--quiet\", \"--\", relativePath], {\n cwd,\n });\n return true;\n } catch {\n return false;\n }\n}\n\n/** Read `git remote get-url origin` (or null if no origin). */\nexport async function getOriginUrl(cwd: string): Promise<string | null> {\n try {\n const { stdout } = await execa(\"git\", [\"remote\", \"get-url\", \"origin\"], {\n cwd,\n });\n return stdout.trim();\n } catch {\n return null;\n }\n}\n\n/** Set `origin` to the given URL. Used by `fluid mist remote restore`. */\nexport async function setOriginUrl(cwd: string, url: string): Promise<void> {\n try {\n await execa(\"git\", [\"remote\", \"set-url\", \"origin\", url], { cwd });\n } catch (err) {\n const missing = gitMissingError(err);\n if (missing) throw missing;\n const e = err as ExecaError;\n throw new GitError(`git remote set-url failed`, e.exitCode ?? 1);\n }\n}\n\n/**\n * Are there uncommitted changes (working tree or index) in this repo?\n * `git status --porcelain` is empty when everything is clean, non-empty\n * otherwise — so the count is just \"is the output non-trivial\".\n */\nexport async function hasUncommittedChanges(cwd: string): Promise<boolean> {\n try {\n const { stdout } = await execa(\"git\", [\"status\", \"--porcelain\"], { cwd });\n return stdout.trim().length > 0;\n } catch {\n return false;\n }\n}\n\n/**\n * Fetch the given branch from the token-bearing remote URL, writing\n * `FETCH_HEAD` so `git rev-list HEAD...FETCH_HEAD` is meaningful.\n *\n * Same security stance as gitPush — installation token comes through\n * GIT_ASKPASS, never argv or `.git/config`.\n */\nexport async function gitFetch(\n cwd: string,\n remoteUrl: string,\n branch = \"main\",\n): Promise<void> {\n const { cleanUrl, env } = gitAuthSpawn(remoteUrl);\n try {\n await execa(\n \"git\",\n [\n ...GIT_STALL_GUARD_ARGS,\n \"-c\",\n \"credential.helper=\",\n \"fetch\",\n \"--quiet\",\n cleanUrl,\n branch,\n ],\n { cwd, env },\n );\n } catch (err) {\n const missing = gitMissingError(err);\n if (missing) throw missing;\n const e = err as ExecaError;\n throw new GitError(\n `git fetch failed${e.exitCode != null ? ` (exit ${e.exitCode})` : \"\"}`,\n e.exitCode ?? 1,\n );\n }\n}\n\n/**\n * Count commits in local HEAD vs `FETCH_HEAD` (after `gitFetch`). Returns\n * `{ahead, behind}`: how many commits HEAD has that the remote doesn't,\n * and vice versa. A `0/0` result means \"in sync\".\n */\nexport async function gitAheadBehindVsFetchHead(\n cwd: string,\n): Promise<{ ahead: number; behind: number }> {\n try {\n const { stdout } = await execa(\n \"git\",\n [\"rev-list\", \"--left-right\", \"--count\", \"HEAD...FETCH_HEAD\"],\n { cwd },\n );\n const [a, b] = stdout.trim().split(/\\s+/);\n const ahead = Number.parseInt(a ?? \"0\", 10);\n const behind = Number.parseInt(b ?? \"0\", 10);\n return {\n ahead: Number.isFinite(ahead) ? ahead : 0,\n behind: Number.isFinite(behind) ? behind : 0,\n };\n } catch {\n // No FETCH_HEAD yet → treat as \"no remote info\". Caller decides.\n return { ahead: 0, behind: 0 };\n }\n}\n\n/** Fast-forward `HEAD` to `FETCH_HEAD`. Fails (non-zero exit) if not\n * fast-forwardable — caller must check ahead-count first. */\nexport async function gitMergeFastForward(cwd: string): Promise<void> {\n try {\n await execa(\"git\", [\"merge\", \"--ff-only\", \"FETCH_HEAD\"], { cwd });\n } catch (err) {\n const missing = gitMissingError(err);\n if (missing) throw missing;\n const e = err as ExecaError;\n throw new GitError(\n `git fast-forward merge failed${e.exitCode != null ? ` (exit ${e.exitCode})` : \"\"}`,\n e.exitCode ?? 1,\n );\n }\n}\n\n/**\n * Stash working-tree + untracked changes with a Mist-tagged message.\n * Returns the stash ref (`stash@{0}`) we created. Callers should pair\n * this with `gitStashPop` after the dangerous step is done.\n */\nexport async function gitStash(cwd: string, message: string): Promise<void> {\n try {\n await execa(\n \"git\",\n [\"stash\", \"push\", \"--include-untracked\", \"--message\", message],\n { cwd },\n );\n } catch (err) {\n const missing = gitMissingError(err);\n if (missing) throw missing;\n const e = err as ExecaError;\n throw new GitError(`git stash push failed`, e.exitCode ?? 1);\n }\n}\n\n/** Read the current HEAD sha, or null if there is no commit yet. */\nexport async function getHeadSha(cwd: string): Promise<string | null> {\n try {\n const { stdout } = await execa(\"git\", [\"rev-parse\", \"--verify\", \"HEAD\"], {\n cwd,\n });\n return stdout.trim() || null;\n } catch {\n return null;\n }\n}\n\n/** Shown when a teammate's pulled change collides with local edits. */\nexport const STASH_CONFLICT_MESSAGE =\n \"Your unpublished edits conflict with a teammate's change to the same files. \" +\n \"Nothing was published and your edits are back exactly as they were. \" +\n \"Merge the teammate's change first (ask Mist to, or run `git pull` in a terminal), then publish again.\";\n\n/**\n * Pop the most recent stash.\n *\n * Without `restoreHead`, a failed pop throws and leaves the tree as git\n * left it. With `restoreHead` (the HEAD the stash was taken on, captured\n * BEFORE the pull), a failed pop never leaves conflict markers behind: the\n * tree is hard-reset to `restoreHead`, any untracked files the failed pop\n * already wrote are removed (the stash still holds them), and the stash is\n * popped again onto the base it was taken from — which applies cleanly.\n * The pulled commits are dropped from HEAD (they stay on the remote), and\n * a GitError with {@link STASH_CONFLICT_MESSAGE} is thrown so the caller\n * aborts the publish instead of committing `<<<<<<<` markers.\n */\nexport async function gitStashPop(\n cwd: string,\n restoreHead?: string | null,\n): Promise<void> {\n try {\n await execa(\"git\", [\"stash\", \"pop\"], { cwd });\n return;\n } catch (err) {\n const missing = gitMissingError(err);\n if (missing) throw missing;\n if (!restoreHead) {\n const e = err as ExecaError;\n throw new GitError(\n `Couldn't restore your local changes after pulling — the file edits collided with a teammate's. Run \\`git stash list\\` and resolve manually.`,\n e.exitCode ?? 1,\n );\n }\n }\n await restoreStashOnto(cwd, restoreHead);\n throw new GitError(STASH_CONFLICT_MESSAGE, 1);\n}\n\nasync function restoreStashOnto(cwd: string, head: string): Promise<void> {\n try {\n await execa(\"git\", [\"reset\", \"--hard\", \"--quiet\", head], { cwd });\n // A conflicting pop restores the stash's untracked files before the\n // tracked merge fails; they'd block the second pop (\"already exists\").\n // They're still in the stash's third parent, so removing them is safe.\n const untracked = await execa(\n \"git\",\n [\"ls-tree\", \"-r\", \"-z\", \"--name-only\", \"stash@{0}^3\"],\n { cwd, reject: false },\n );\n if (untracked.exitCode === 0) {\n const root = (\n await execa(\"git\", [\"rev-parse\", \"--show-toplevel\"], { cwd })\n ).stdout.trim();\n for (const rel of untracked.stdout.split(\"\\0\").filter(Boolean)) {\n rmSync(path.join(root, rel), { force: true });\n }\n }\n await execa(\"git\", [\"stash\", \"pop\"], { cwd });\n } catch (err) {\n const missing = gitMissingError(err);\n if (missing) throw missing;\n throw new GitError(\n \"Your edits conflict with a teammate's change and couldn't be restored automatically. Nothing was published; your edits are saved in `git stash list`.\",\n 1,\n );\n }\n}\n\n/**\n * Refuse to commit a tree that still carries merge-conflict state:\n * unmerged index entries (`git ls-files -u`), or — checked after staging —\n * a staged file with a line starting `<<<<<<< `. Publishing either would\n * deploy a broken site.\n */\nexport async function assertNoUnmergedPaths(cwd: string): Promise<void> {\n const { stdout } = await execa(\"git\", [\"ls-files\", \"-u\", \"-z\"], {\n cwd,\n reject: false,\n });\n if (stdout.trim().length > 0) {\n throw new GitError(CONFLICT_REFUSAL_MESSAGE, 1);\n }\n}\n\nconst CONFLICT_REFUSAL_MESSAGE =\n \"Some files still have unresolved merge conflicts (lines starting `<<<<<<<`). \" +\n \"Nothing was published. Resolve the conflicts, then publish again.\";\n\nexport async function assertNoStagedConflictMarkers(\n cwd: string,\n): Promise<void> {\n const staged = await execa(\n \"git\",\n [\"diff\", \"--cached\", \"--name-only\", \"--diff-filter=ACMR\", \"-z\"],\n { cwd, reject: false },\n );\n const stagedPaths = new Set(staged.stdout.split(\"\\0\").filter(Boolean));\n if (stagedPaths.size === 0) return;\n // `git grep --cached` searches index content; -I skips binaries; exit 1\n // means no match. `:(top)` + --full-name make it repo-wide and\n // root-relative, matching `git diff`'s paths, even from a subdirectory.\n const hits = await execa(\n \"git\",\n [\n \"grep\",\n \"--cached\",\n \"-I\",\n \"-l\",\n \"-z\",\n \"--full-name\",\n \"-E\",\n \"^<<<<<<< \",\n \"--\",\n \":(top)\",\n ],\n { cwd, reject: false },\n );\n if (hits.exitCode !== 0) return;\n const conflicted = hits.stdout\n .split(\"\\0\")\n .filter((p) => p && stagedPaths.has(p));\n if (conflicted.length > 0) {\n throw new GitError(\n `${CONFLICT_REFUSAL_MESSAGE} Conflicted: ${conflicted.slice(0, 5).join(\", \")}`,\n 1,\n );\n }\n}\n\n/**\n * Persist the Mist bot as the repo's LOCAL git identity\n * (`git config --local user.{name,email}`).\n *\n * `gitAutoCommit` already forces the bot author on the publish commit\n * via `-c`/env, but that only covers commits WE make. Commits made any\n * other way against the same checkout — the Mist chat agent shelling out\n * `git commit` via run_cli, or the user in a terminal — inherit the\n * repo's configured identity, which is the person's personal GitHub\n * account. Vercel rejects a deploy whose commit author isn't a\n * we-commerce team member, so those personal-authored commits silently\n * block the deploy (exactly the www-* \"stuck project\" case).\n *\n * Setting the identity at the repo level makes EVERY commit path\n * bot-authored, and heals repos cloned before this was in place on their\n * next publish. Best-effort: a config failure must never abort a push —\n * the publish commit is still bot-forced by gitAutoCommit regardless.\n */\nexport async function ensureBotIdentity(cwd: string): Promise<void> {\n try {\n await execa(\n \"git\",\n [\"config\", \"--local\", \"user.name\", MIST_GIT_AUTHOR_NAME],\n { cwd },\n );\n await execa(\n \"git\",\n [\"config\", \"--local\", \"user.email\", MIST_GIT_AUTHOR_EMAIL],\n { cwd },\n );\n } catch {\n // Non-fatal — see doc comment.\n }\n}\n\n/**\n * Append Mist's local-only state to `.git/info/exclude` when needed:\n * `.mist` is the clone marker from spec §3, while `.mist-desktop/`\n * contains private chat history, attachments, screenshots, and other\n * machine-local Desktop state. Neither belongs in the project remote.\n *\n * Why local exclude and not `.gitignore`: `.gitignore` lives in the\n * template repo and would need a template release to reach existing\n * clones. `.git/info/exclude` is per-checkout, so writing it here (at\n * clone time) AND defensively at the top of every `gitAutoCommit` (at\n * push time) heals pre-existing clones on their very next push, with no\n * template change required.\n *\n * Idempotent and best-effort: never throws. A repo with an unusual\n * `.git` layout (missing `info/`, `.git` as a worktree pointer file,\n * etc.) just skips the exclude — `gitAutoCommit` also cleans both paths\n * from the index after staging.\n */\nexport function excludeMistMarkerFromGit(cwd: string): void {\n try {\n const gitDir = path.join(cwd, \".git\");\n if (!existsSync(gitDir)) return;\n const infoDir = path.join(gitDir, \"info\");\n if (!existsSync(infoDir)) mkdirSync(infoDir, { recursive: true });\n const excludePath = path.join(infoDir, \"exclude\");\n const existing = existsSync(excludePath)\n ? readFileSync(excludePath, \"utf8\")\n : \"\";\n const existingLines = new Set(\n existing\n .split(\"\\n\")\n .map((line) => line.trim())\n .filter(Boolean),\n );\n const missing = [\".mist\", \".mist-desktop/\"].filter(\n (entry) => !existingLines.has(entry),\n );\n if (missing.length === 0) return;\n const needsLeadingNewline = existing.length > 0 && !existing.endsWith(\"\\n\");\n writeFileSync(\n excludePath,\n `${existing}${needsLeadingNewline ? \"\\n\" : \"\"}${missing.join(\"\\n\")}\\n`,\n \"utf8\",\n );\n } catch {\n // Best-effort — see doc comment. gitAutoCommit's explicit index cleanup\n // still keeps local Mist state out if this fails silently.\n }\n}\n\n/**\n * `git add -A && git commit -m <message>` in `cwd`.\n *\n * Mist Desktop's \"Publish\" UX target is non-technical: the user expects\n * \"ship my changes\" to ship the changes they made, not to know about\n * staging or commit messages. This helper bundles working-tree edits\n * into one commit per publish so push has something to send.\n *\n * The commit author is ALWAYS forced to the Mist GitHub App bot identity\n * (MIST_GIT_AUTHOR_*), overriding whatever local/global git identity the\n * machine has. This is what keeps Vercel from blocking the deploy: a commit\n * authored by a user's personal GitHub account that isn't a we-commerce Vercel\n * team member gets rejected. Passing `-c user.name=… -c user.email=…` beats\n * even a `--local` config, so every publish is bot-authored regardless of how\n * the repo landed on disk (fresh clone, pre-existing clone, or terminal CLI).\n * The real person is still recorded in the commit *message* (see push.ts).\n *\n * Local Mist state handling: clone writes `.mist` and `.mist-desktop/` to\n * `.git/info/exclude`, but this heals a pre-existing clone or a repo whose\n * exclude write silently failed. Belt AND suspenders:\n * 1. Re-run the exclude write here (idempotent) — heals pre-existing\n * clones on their very next push. Anchored to the repository root\n * (`git rev-parse --show-toplevel`), not cwd, so a push run from a\n * repo subdirectory heals the real `.git/info/exclude`.\n * 2. After `git add -A`, run `git reset -- ':(top).mist' .mist`\n * (root-anchored + cwd-relative pathspecs; best-effort,\n * ignored if it fails or matches nothing) to unstage the marker in\n * case the exclude write above failed and `.mist` got swept in\n * anyway. We deliberately do NOT ask `git add` to exclude `.mist`\n * via a `:(exclude)` pathspec — when a path is *also* already\n * listed in `.git/info/exclude`, combining `-A` with an explicit\n * exclude pathspec on that same path makes git exit 1 with\n * \"paths are ignored by one of your .gitignore files\" (a real git\n * quirk, confirmed against git 2.41), which would turn the normal\n * case (exclude already written) into a hard failure. Add-then-\n * reset avoids that entirely and is a no-op when there's nothing\n * to unstage.\n * 3. Run `git rm --cached -r --ignore-unmatch .mist-desktop` from the\n * repository root. This keeps every local file on disk while staging\n * their removal if an older publish already tracked them. Ignore rules\n * alone cannot untrack an existing path.\n * Note: a `.mist` file already committed in an existing repo's history\n * is left alone — this only prevents *new* adds, it does not untrack\n * anything or rewrite history.\n */\n/**\n * The staged file set, as {@link ChangeEntry}s — the input a\n * {@link CommitMessage} resolver summarizes.\n *\n * `-z` (NUL-delimited) rather than the default: paths with spaces or\n * quotes are legal in git and common in themes and asset folders, and\n * the newline form quotes and escapes them, which would corrupt both the\n * path and the count.\n *\n * Best-effort — a summary is a nicety, never a reason to fail a publish.\n * An empty result makes the resolver fall back to its own constant.\n */\nasync function stagedChanges(cwd: string): Promise<ChangeEntry[]> {\n try {\n const { stdout } = await execa(\n \"git\",\n [\"diff\", \"--cached\", \"--name-status\", \"-z\"],\n { cwd },\n );\n return parseNameStatusZ(stdout);\n } catch {\n return [];\n }\n}\n\nexport async function gitAutoCommit(\n cwd: string,\n message: CommitMessage,\n): Promise<void> {\n // Persist bot identity so this AND any later terminal/agent commit on\n // the checkout is bot-authored (not just this one via -c/env below).\n await ensureBotIdentity(cwd);\n // Both `.mist` defenses must anchor to the REPOSITORY ROOT, not cwd:\n // a push run from a repo subdirectory (explicit slug from a nested\n // terminal, say) would otherwise look for `<cwd>/.git` — silently\n // skipping the exclude write — while `git add -A` still stages the\n // whole repository, letting the root marker into the commit. Resolve\n // the root first; fall back to cwd (the common root-invocation case)\n // when resolution fails — the `git add` below surfaces the real error.\n let repoRoot = cwd;\n try {\n const { stdout } = await execa(\"git\", [\"rev-parse\", \"--show-toplevel\"], {\n cwd,\n });\n if (stdout.trim().length > 0) repoRoot = stdout.trim();\n } catch {\n // Not a repo / git missing — handled by the add/commit path below.\n }\n // Defensive: heal clones made before writeMistFile started excluding\n // `.mist`, or one whose exclude write failed at clone time.\n excludeMistMarkerFromGit(repoRoot);\n // Never publish an unfinished merge: `git add -A` would mark unmerged\n // paths resolved with their `<<<<<<<` markers still inside.\n await assertNoUnmergedPaths(cwd);\n // Stage everything (including deletes).\n try {\n await execa(\"git\", [\"add\", \"-A\"], { cwd });\n } catch (err) {\n const missing = gitMissingError(err);\n if (missing) throw missing;\n const e = err as ExecaError;\n throw new GitError(`git add failed`, e.exitCode ?? 1);\n }\n // Second line of defense: unstage `.mist` if it somehow got staged\n // above (only possible if the exclude write failed). Two pathspecs:\n // `:(top).mist` resolves against the working-tree ROOT, so a push\n // run from a repo subdirectory still unstages the root marker that\n // `git add -A` can sweep in; plain `.mist` additionally covers a\n // project directory that is itself nested inside a larger repo.\n // Best-effort — `git reset` on paths with nothing staged, or that\n // don't exist, is already a no-op that exits 0, so failures here\n // are unexpected and non-fatal to publish.\n try {\n await execa(\"git\", [\"reset\", \"-q\", \"--\", \":(top).mist\", \".mist\"], { cwd });\n } catch {\n // Non-fatal — see doc comment.\n }\n // `.mist-desktop/` may contain private chats, user attachments, and large\n // screenshots. Remove it from the index even if an older publish already\n // tracked it; --cached deliberately preserves the local directory.\n try {\n await execa(\n \"git\",\n [\"rm\", \"-q\", \"-r\", \"--cached\", \"--ignore-unmatch\", \"--\", \".mist-desktop\"],\n { cwd: repoRoot },\n );\n } catch {\n // Non-fatal — the exclude file remains the primary defense for new paths.\n }\n await assertNoStagedConflictMarkers(cwd);\n // Resolve the subject only now: everything above decides what is\n // actually staged (add -A, then the `.mist` / `.mist-desktop`\n // unstaging), so a summary built any earlier would describe a file set\n // the commit doesn't carry.\n const subject =\n typeof message === \"string\"\n ? message\n : await message(await stagedChanges(cwd));\n\n // Commit as the Mist bot. `-c` overrides any configured user.name/user.email\n // (including `--local`); the GIT_{AUTHOR,COMMITTER}_* env vars additionally\n // override even `-c` (git reads them ahead of config), so we set both to seal\n // every precedence layer and make the author deterministic and Vercel-safe.\n const identityArgs = [\n \"-c\",\n `user.name=${MIST_GIT_AUTHOR_NAME}`,\n \"-c\",\n `user.email=${MIST_GIT_AUTHOR_EMAIL}`,\n ];\n const identityEnv: NodeJS.ProcessEnv = {\n GIT_AUTHOR_NAME: MIST_GIT_AUTHOR_NAME,\n GIT_AUTHOR_EMAIL: MIST_GIT_AUTHOR_EMAIL,\n GIT_COMMITTER_NAME: MIST_GIT_AUTHOR_NAME,\n GIT_COMMITTER_EMAIL: MIST_GIT_AUTHOR_EMAIL,\n };\n try {\n await execa(\"git\", [...identityArgs, \"commit\", \"-m\", subject], {\n cwd,\n env: { ...process.env, ...identityEnv },\n stdio: [\"ignore\", \"inherit\", \"inherit\"],\n });\n } catch (err) {\n const missing = gitMissingError(err);\n if (missing) throw missing;\n const e = err as ExecaError;\n // exit 1 with \"nothing to commit\" means there was nothing actually\n // staged (race or .gitignore). Treat as a no-op rather than an error.\n if (typeof e.stderr === \"string\" && /nothing to commit/i.test(e.stderr)) {\n return;\n }\n throw new GitError(\n `git commit failed${e.exitCode != null ? ` (exit ${e.exitCode})` : \"\"}`,\n e.exitCode ?? 1,\n );\n }\n}\n","/**\n * Slug resolution — spec §3 conventions:\n * 1. If a slug arg is passed, use it.\n * 2. Otherwise look for a `.mist` file in CWD (written by `fluid mist\n * clone`) and use the slug from it.\n * 3. Otherwise error: tell the user to pass a slug or cd into a cloned dir.\n */\n\nimport { existsSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { excludeMistMarkerFromGit } from \"./git.js\";\nimport type { Mist, MistFile } from \"../types.js\";\n\nconst MIST_FILE = \".mist\";\n\nexport class SlugResolutionError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"SlugResolutionError\";\n }\n}\n\nfunction readMistFile(dir: string): MistFile | null {\n const filePath = join(dir, MIST_FILE);\n if (!existsSync(filePath)) return null;\n try {\n const raw = readFileSync(filePath, \"utf8\");\n const parsed = JSON.parse(raw) as unknown;\n if (!parsed || typeof parsed !== \"object\") return null;\n const obj = parsed as Record<string, unknown>;\n if (typeof obj[\"slug\"] !== \"string\") return null;\n return {\n slug: obj[\"slug\"] as string,\n vendor_name:\n typeof obj[\"vendor_name\"] === \"string\"\n ? (obj[\"vendor_name\"] as string)\n : \"\",\n public_url:\n typeof obj[\"public_url\"] === \"string\"\n ? (obj[\"public_url\"] as string)\n : null,\n };\n } catch {\n return null;\n }\n}\n\n/** Returns the slug from arg or `.mist` file; throws if neither is present. */\nexport function resolveSlug(arg?: string, cwd: string = process.cwd()): string {\n if (arg && arg.trim().length > 0) return arg.trim();\n const file = readMistFile(cwd);\n if (file) return file.slug;\n throw new SlugResolutionError(\n \"No slug provided and no `.mist` file in this directory. Pass the slug as an argument or cd into a cloned mist. \" +\n \"(if you reverted a commit that removed .mist, pass the slug explicitly or re-run fluid mist clone)\",\n );\n}\n\n/**\n * Write a `.mist` file after `fluid mist clone`.\n *\n * Also excludes `.mist` via `.git/info/exclude` so it can never be swept\n * into a publish commit by `gitAutoCommit`'s `git add -A` — a commit\n * containing `.mist` that later gets `git revert`-ed deletes the marker\n * and locks every subsequent `fluid mist <cmd>` out of slug resolution\n * in that clone. Best-effort: if the exclude write fails (e.g. `.git`\n * isn't set up yet), `writeFileSync` below still runs — this is a\n * defense-in-depth step, not a precondition for the marker itself.\n */\nexport function writeMistFile(dir: string, mist: Mist): void {\n excludeMistMarkerFromGit(dir);\n const payload: MistFile = {\n slug: mist.slug,\n vendor_name: mist.vendor_name,\n public_url: mist.public_url,\n };\n writeFileSync(\n join(dir, MIST_FILE),\n JSON.stringify(payload, null, 2) + \"\\n\",\n \"utf8\",\n );\n}\n\nexport function readMistFileFrom(dir: string = process.cwd()): MistFile | null {\n return readMistFile(dir);\n}\n","/**\n * Pick the commit `fluid mist staging request` stages when no `--revision`\n * is given: the local checkout's HEAD, but only when nothing local would be\n * left out or lost.\n *\n * Staging can only serve a commit the mist's repository already has, and\n * this never pushes or switches branches to get one there; publishing to\n * main would ship the work to production. So the checkout must be clean\n * and its HEAD must already be on the remote's main branch.\n */\n\nimport { execa } from \"execa\";\nimport { gitFetch } from \"./git.js\";\n\n/** The local checkout can't be staged as it is. Nothing was changed. */\nexport class StagingPreflightError extends Error {\n constructor(message: string) {\n super(`${message} Nothing was requested.`);\n this.name = \"StagingPreflightError\";\n }\n}\n\n/**\n * The full SHA of the checkout's HEAD once it's clean and on the remote's\n * main branch. `remoteUrl` may carry a short-lived token; it only reaches\n * git through `gitFetch`, never argv or config.\n *\n * Raises StagingPreflightError when the directory is not a git checkout,\n * git can't report its status, it has uncommitted or untracked files, or\n * HEAD is not on the remote's main.\n */\nexport async function resolveLocalStagingRevision(\n cwd: string,\n remoteUrl: string,\n): Promise<string> {\n const inside = await git(cwd, [\"rev-parse\", \"--is-inside-work-tree\"]);\n if (inside !== \"true\") {\n throw new StagingPreflightError(\n \"This directory isn't a git checkout of the mist. Run it from the mist's folder or pass --revision.\",\n );\n }\n // Only git saying \"clean\" counts; a status that can't be read is not.\n // Untracked files are listed whatever status.showUntrackedFiles says.\n const status = await git(cwd, [\n \"status\",\n \"--porcelain\",\n \"--untracked-files=all\",\n ]);\n if (status === null) {\n throw new StagingPreflightError(\n \"Git couldn't report whether this checkout has uncommitted changes. Pass --revision to stage a specific commit.\",\n );\n }\n if (status.length > 0) {\n throw new StagingPreflightError(\n \"This checkout has uncommitted or untracked changes, and staging only serves commits already on the mist's repository. Commit and publish them, or pass --revision.\",\n );\n }\n const head = await git(cwd, [\"rev-parse\", \"HEAD\"]);\n if (!head || !/^[0-9a-f]{40}$/.test(head)) {\n throw new StagingPreflightError(\"This checkout has no commits yet.\");\n }\n\n await gitFetch(cwd, remoteUrl, \"main\");\n const onMain = await execa(\n \"git\",\n [\"merge-base\", \"--is-ancestor\", head, \"FETCH_HEAD\"],\n { cwd, reject: false },\n );\n if (onMain.exitCode !== 0) {\n throw new StagingPreflightError(\n `Commit ${head.slice(0, 7)} isn't on the mist's main branch yet, and staging only serves commits the repository already has.`,\n );\n }\n return head;\n}\n\nasync function git(cwd: string, args: string[]): Promise<string | null> {\n const run = await execa(\"git\", args, { cwd, reject: false });\n return run.exitCode === 0 ? run.stdout.trim() : null;\n}\n","/**\n * Staging workflow for `fluid mist staging`: read where staging stands,\n * request it, and follow the operation until the server finishes it.\n *\n * The server is authoritative. Nothing here falls back to a production\n * publish: an unsupported server or an unavailable staging stops with an\n * explanation instead.\n */\n\nimport type { MistClient } from \"../api/client.js\";\nimport {\n fetchBranchOperation,\n isTerminal,\n listBranchEnvironments,\n newOperationKey,\n requestStaging,\n type BranchEnvironment,\n type BranchOperation,\n type StagingCapability,\n} from \"../api/branch_environments.js\";\n\n/** A staging step the user can act on, as opposed to a server fault. */\nexport class StagingUnavailableError extends Error {\n readonly reason: string;\n readonly activeOperationId: string | null;\n\n constructor(\n reason: string,\n message: string,\n activeOperationId: string | null = null,\n ) {\n super(message);\n this.name = \"StagingUnavailableError\";\n this.reason = reason;\n this.activeOperationId = activeOperationId;\n }\n}\n\n/** The operation stopped without succeeding. */\nexport class StagingOperationFailedError extends Error {\n readonly operation: BranchOperation;\n\n constructor(operation: BranchOperation) {\n const cause = operation.failure ? ` (${operation.failure.type})` : \"\";\n super(`Staging ${operation.state}${cause}. Operation ${operation.id}.`);\n this.name = \"StagingOperationFailedError\";\n this.operation = operation;\n }\n}\n\n/** The local wait ran out; the server keeps working on the operation. */\nexport class StagingWaitTimeoutError extends Error {\n readonly operation: BranchOperation;\n\n constructor(operation: BranchOperation) {\n super(\n `Still ${operation.state} after the wait limit. The server keeps working on it; follow it with \\`fluid mist staging wait --operation ${operation.id}\\`.`,\n );\n this.name = \"StagingWaitTimeoutError\";\n this.operation = operation;\n }\n}\n\nconst UNSUPPORTED =\n \"This Fluid server doesn't support branch environments yet, so there is no staging to use. Nothing was published.\";\n\n/** Why staging can't be requested now, in words a user can act on. */\nexport function describeUnavailable(staging: StagingCapability): string {\n switch (staging.reason) {\n case \"feature_disabled\":\n return \"Branch environments aren't enabled for this company.\";\n case \"execution_unavailable\":\n return \"Staging isn't running on the server right now. Try again later.\";\n case \"hosting_unavailable\":\n return \"This mist isn't live, so it can't have a staging environment.\";\n case \"credentials_not_retired\":\n return \"This mist's production credentials still reach preview builds. A Fluid operator has to retire them before staging can be used.\";\n case \"operation_in_progress\":\n return `Staging is already being prepared (operation ${staging.active_operation_id ?? \"unknown\"}). Follow it with \\`fluid mist staging wait\\`.`;\n case \"environment_deleting\":\n return \"Staging is being torn down. Request it again once it's removed.\";\n case \"staging_exists\":\n return \"Staging already exists. It can't be requested again until it's torn down.\";\n case \"quota_exceeded\":\n return \"This company has reached its branch environment limit.\";\n default:\n return `Staging isn't available right now (${staging.reason ?? \"no reason given\"}).`;\n }\n}\n\nexport interface StagingStatus {\n capability: { version: number; staging: StagingCapability };\n environment: BranchEnvironment | null;\n activeOperation: BranchOperation | null;\n}\n\n/** Where staging stands: capability, the staging environment and any run. */\nexport async function readStagingStatus(\n client: MistClient,\n slug: string,\n): Promise<StagingStatus> {\n const listing = await listBranchEnvironments(client, slug);\n if (!listing.capability.supported) {\n throw new StagingUnavailableError(\"unsupported\", UNSUPPORTED);\n }\n const { staging } = listing.capability;\n const activeOperation = staging.active_operation_id\n ? await fetchBranchOperation(client, slug, staging.active_operation_id)\n : null;\n return {\n capability: {\n version: listing.capability.version,\n staging,\n },\n environment: listing.environments.find((env) => env.seeded) ?? null,\n activeOperation,\n };\n}\n\nexport interface StagingRequest {\n sourceRevision: string;\n /** Reuse to resume an interrupted request; a new one is made otherwise. */\n operationKey?: string;\n /** Told the key before the request is sent, so it can be retried. */\n onOperationKey?: (key: string) => void;\n}\n\n/**\n * Request staging at a revision. A key the caller passes is retried even\n * while the capability reports a run in progress, because that run may be\n * the one this key started. Throws `StagingOperationFailedError` when the\n * key returns an operation that already failed, was cancelled or went stale.\n */\nexport async function admitStaging(\n client: MistClient,\n slug: string,\n request: StagingRequest,\n): Promise<BranchOperation> {\n if (!request.operationKey) {\n const listing = await listBranchEnvironments(client, slug);\n if (!listing.capability.supported) {\n throw new StagingUnavailableError(\"unsupported\", UNSUPPORTED);\n }\n const { staging } = listing.capability;\n if (!staging.available) {\n throw new StagingUnavailableError(\n staging.reason ?? \"unavailable\",\n describeUnavailable(staging),\n staging.active_operation_id,\n );\n }\n }\n\n const operationKey = request.operationKey ?? newOperationKey();\n request.onOperationKey?.(operationKey);\n const operation = await requestStaging(client, slug, {\n sourceRevision: request.sourceRevision,\n operationKey,\n });\n // A resumed key can return an operation that already ended badly.\n if (isTerminal(operation) && operation.state !== \"succeeded\") {\n throw new StagingOperationFailedError(operation);\n }\n return operation;\n}\n\nexport interface WaitOptions {\n intervalMs: number;\n timeoutMs: number;\n /** Called whenever the state or progress changes. */\n onChange?: (operation: BranchOperation) => void;\n sleep?: (ms: number) => Promise<void>;\n now?: () => number;\n}\n\n/**\n * Poll the staging operation until it finishes. Resolves on success;\n * throws `StagingUnavailableError` for an operation that isn't a staging\n * request, `StagingOperationFailedError` on any other terminal state and\n * `StagingWaitTimeoutError` when the local wait runs out, which never\n * cancels the server operation.\n */\nexport async function waitForOperation(\n client: MistClient,\n slug: string,\n operationId: string,\n options: WaitOptions,\n): Promise<BranchOperation> {\n const sleep =\n options.sleep ??\n ((ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms)));\n const now = options.now ?? Date.now;\n const deadline = now() + options.timeoutMs;\n let lastSeen = \"\";\n\n for (;;) {\n const operation = await fetchBranchOperation(client, slug, operationId);\n if (operation.kind !== \"ensure_staging\") {\n throw new StagingUnavailableError(\n \"not_a_staging_operation\",\n `Operation ${operation.id} is a ${operation.kind} operation, not a staging request. Follow staging with \\`fluid mist staging wait\\`.`,\n );\n }\n const seen = JSON.stringify([\n operation.state,\n operation.progress,\n operation.failure,\n ]);\n if (seen !== lastSeen) {\n lastSeen = seen;\n options.onChange?.(operation);\n }\n if (isTerminal(operation)) {\n if (operation.state === \"succeeded\") return operation;\n throw new StagingOperationFailedError(operation);\n }\n if (now() >= deadline) throw new StagingWaitTimeoutError(operation);\n await sleep(options.intervalMs);\n }\n}\n\n/** One line describing an operation's progress, for stderr. */\nexport function describeProgress(operation: BranchOperation): string {\n const { progress } = operation;\n const steps = [\n `database ${progress.database_isolated ? \"isolated\" : \"pending\"}`,\n `branch ${progress.branch_published ? \"published\" : \"pending\"}`,\n `build ${progress.build_ready ? \"ready\" : \"pending\"}`,\n ].join(\", \");\n const failure = operation.failure\n ? `, last error ${operation.failure.type}`\n : \"\";\n return `${operation.state}: ${steps}${failure}`;\n}\n","/**\n * Centralized error handling — maps thrown errors to spec'd exit codes\n * and friendly messages. Imported by every command's `.action()` so each\n * file doesn't repeat the same try/catch.\n *\n * Exit codes (spec §3 \"Conventions\"):\n * 0 success | 1 user error | 2 API 4xx | 3 API 5xx / vendor | 4 network\n */\n\nimport chalk from \"chalk\";\nimport { BranchContractError } from \"../api/branch_environments.js\";\nimport { MistApiError } from \"../api/client.js\";\nimport { HostableParseError } from \"./hostable.js\";\nimport { SlugResolutionError } from \"./slug-resolver.js\";\nimport { StagingPreflightError } from \"./staging-revision.js\";\nimport {\n StagingOperationFailedError,\n StagingUnavailableError,\n StagingWaitTimeoutError,\n} from \"./staging.js\";\n\n/** Run an async action and translate failures into pretty output + exit. */\nexport async function runAction(fn: () => Promise<void> | void): Promise<void> {\n try {\n await fn();\n } catch (err) {\n handle(err);\n }\n}\n\nfunction handle(err: unknown): never {\n if (err instanceof SlugResolutionError) {\n console.error(chalk.red(err.message));\n process.exit(1);\n }\n if (err instanceof HostableParseError) {\n console.error(chalk.red(err.message));\n process.exit(1);\n }\n if (\n err instanceof StagingPreflightError ||\n err instanceof StagingUnavailableError ||\n err instanceof StagingWaitTimeoutError\n ) {\n console.error(chalk.yellow(err.message));\n process.exit(1);\n }\n if (\n err instanceof StagingOperationFailedError ||\n err instanceof BranchContractError\n ) {\n console.error(chalk.red(err.message));\n process.exit(3);\n }\n if (err instanceof MistApiError) {\n // Friendlier messages for the well-known cases from the spec.\n if (err.status === 403) {\n const action = err.details?.[\"permission\"];\n console.error(\n chalk.red(\n action\n ? `Your role doesn't include \\`mist.${String(action)}\\` — ask your Fluid admin to grant it.`\n : err.message,\n ),\n );\n } else if (err.status === 409) {\n const date = err.details?.[\"scheduled_destroy_at\"];\n console.error(\n chalk.yellow(\n typeof date === \"string\"\n ? `This mist is scheduled for destruction on ${date.slice(0, 10)}. Run \\`fluid mist restore <slug>\\` first.`\n : err.message,\n ),\n );\n } else if (err.status === 422) {\n // Hostable-related 422s come back with structured `details` per the\n // spec. Surface them as a one-line hint above the raw API message.\n const details = err.details ?? {};\n const idErrors = details[\"integratable_id\"] ?? details[\"hostable_id\"];\n const managedKey =\n typeof err.message === \"string\" &&\n /managed_by|managed by mist|FLUID_DROPLET|FLUID_WEBHOOK/i.test(\n err.message,\n );\n if (\n Array.isArray(idErrors) &&\n idErrors.some((e) => /already taken|already attached/i.test(String(e)))\n ) {\n console.error(\n chalk.yellow(\n \"That droplet is already attached to another mist. Detach it first or pick a different one.\",\n ),\n );\n } else if (managedKey) {\n console.error(\n chalk.yellow(\n \"That env var is Mist-managed (FLUID_DROPLET_UUID / FLUID_DROPLET_SECRET / FLUID_WEBHOOK_AUTH_TOKEN) — it's set automatically when a droplet is attached and can't be written directly.\",\n ),\n );\n } else if (\n typeof err.message === \"string\" &&\n /Droplet not found|integratable/i.test(err.message)\n ) {\n const id = details[\"integratable_id\"] ?? details[\"hostable_id\"];\n console.error(\n chalk.yellow(\n `Couldn't find droplet${id != null ? ` #${String(id)}` : \"\"} in this company.`,\n ),\n );\n } else {\n console.error(chalk.red(err.message));\n }\n } else if (err.status === 410) {\n console.error(\n chalk.red(\n \"Grace period expired — vendor teardown is in flight. Create a fresh mist with `fluid mist create`.\",\n ),\n );\n } else {\n console.error(chalk.red(err.message));\n }\n process.exit(err.exitCode);\n }\n // Unknown — print and exit 1 (user/programmer error)\n if (err instanceof Error) {\n console.error(chalk.red(err.message));\n } else {\n console.error(chalk.red(String(err)));\n }\n process.exit(1);\n}\n","/**\n * Output helpers — human-readable by default, `--json` emits raw API\n * payload. NO_COLOR respected via chalk's built-in detection.\n */\n\nimport chalk from \"chalk\";\nimport type { Mist } from \"../types.js\";\n\nexport function printJson(payload: unknown): void {\n process.stdout.write(JSON.stringify(payload, null, 2) + \"\\n\");\n}\n\nconst STATE_COLOR: Record<Mist[\"state\"], (s: string) => string> = {\n pending: chalk.gray,\n provisioning: chalk.yellow,\n live: chalk.green,\n failed: chalk.red,\n pending_destroy: chalk.magenta,\n archived: chalk.dim,\n};\n\nexport function colorState(state: Mist[\"state\"]): string {\n return STATE_COLOR[state](state);\n}\n\n/** Completion copy for `mist push --watch`. Deployment records expose an\n * ephemeral provider URL that may require provider SSO; only the Mist's\n * stable public URL is suitable to hand to a user. */\nexport function readyDeploymentMessage(\n publicUrl: string | null,\n slug: string,\n): string {\n return publicUrl\n ? `✔ Ready: ${chalk.cyan(publicUrl)}`\n : `✔ Deployment ready. The public URL is still provisioning; run \\`fluid mist show ${slug}\\` to check it.`;\n}\n\n/**\n * Bold display label for a mist, falling back to a dim \"(unnamed)\"\n * placeholder when both `name` and `vendorName` are missing. Never nests\n * `chalk.bold(chalk.dim(...))` — most terminals render that as plain bold,\n * losing the \"this is a placeholder\" muted intent.\n */\nexport function mistLabel(\n name: string | null | undefined,\n vendorName: string | null | undefined,\n): string {\n const raw = name ?? vendorName;\n return raw ? chalk.bold(raw) : chalk.dim(\"(unnamed)\");\n}\n\n/**\n * Pad columns to width — cheap manual table; avoids cli-table3 dep for v1.\n * Cells are defensively normalized: API records can legitimately have every\n * optional label field (`name`, `vendor_name`, `droplet.name`, ...) come back\n * null/absent at once (e.g. a `pending_destroy` mist), so callers sometimes\n * hand us `undefined`/`null` after chaining `??` — that must render as an\n * empty cell, not crash the whole table.\n */\nexport function printTable(rows: (string | null | undefined)[][]): void {\n if (rows.length === 0) return;\n const widths: number[] = [];\n for (const row of rows) {\n row.forEach((cell, i) => {\n const visible = stripAnsi(cell).length;\n widths[i] = Math.max(widths[i] ?? 0, visible);\n });\n }\n for (const row of rows) {\n const padded = row.map((cell, i) => {\n const w = widths[i] ?? 0;\n const text = cell ?? \"\";\n return text + \" \".repeat(Math.max(0, w - stripAnsi(text).length));\n });\n process.stdout.write(padded.join(\" \") + \"\\n\");\n }\n}\n\n// Chalk respects NO_COLOR, but our padding math needs the visible length —\n// strip ANSI escape sequences for measurement only. Tolerates null/undefined\n// since table cells are built from chained `??` fallbacks that can bottom out.\nfunction stripAnsi(s: string | null | undefined): string {\n // eslint-disable-next-line no-control-regex\n return (s ?? \"\").replace(/\\x1b\\[[0-9;]*m/g, \"\");\n}\n\nexport function relativeDate(iso: string | null): string {\n if (!iso) return \"—\";\n const diff = Date.now() - new Date(iso).getTime();\n const minutes = Math.floor(diff / 60_000);\n if (minutes < 1) return \"just now\";\n if (minutes < 60) return `${minutes}m ago`;\n const hours = Math.floor(minutes / 60);\n if (hours < 24) return `${hours}h ago`;\n const days = Math.floor(hours / 24);\n if (days < 30) return `${days}d ago`;\n return new Date(iso).toISOString().slice(0, 10);\n}\n\n/** Human-friendly absolute date for one-off prints (scheduled_destroy_at). */\nexport function formatAbsolute(iso: string): string {\n const d = new Date(iso);\n return d.toISOString().slice(0, 10);\n}\n","/** `fluid mist list` — GET /mists with optional filters. Spec §3. */\n\nimport chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport { createMistClient } from \"../api/client.js\";\nimport { listMists, type ListMistsParams } from \"../api/mists.js\";\nimport { runAction } from \"../lib/error-handler.js\";\nimport {\n colorState,\n printJson,\n printTable,\n relativeDate,\n} from \"../lib/output.js\";\n\ninterface Opts {\n state?: ListMistsParams[\"state\"];\n kind?: ListMistsParams[\"kind\"];\n limit?: string;\n json?: boolean;\n}\n\nexport const listCommand = new Command(\"list\")\n .description(\"List the mists you have access to\")\n .option(\n \"--state <state>\",\n \"Filter by state: live | provisioning | failed | pending_destroy | archived\",\n )\n .option(\"--kind <kind>\", \"Filter by kind (default: next_app)\")\n .option(\"--limit <n>\", \"Page size 1..100 (default 25)\")\n .option(\"--json\", \"Emit raw API JSON\")\n .action((opts: Opts) =>\n runAction(async () => {\n const client = createMistClient();\n const limit = opts.limit ? parseInt(opts.limit, 10) : undefined;\n const response = await listMists(client, {\n ...(opts.state ? { state: opts.state } : {}),\n ...(opts.kind ? { kind: opts.kind } : {}),\n ...(limit && Number.isFinite(limit) ? { limit } : {}),\n });\n if (opts.json) return printJson(response);\n const mists = response.mists ?? [];\n if (mists.length === 0) {\n console.log(chalk.dim(\"No mists found.\"));\n return;\n }\n const header = [\n chalk.bold(\"SLUG\"),\n chalk.bold(\"NAME\"),\n chalk.bold(\"STATE\"),\n chalk.bold(\"ATTACHED\"),\n chalk.bold(\"UPDATED\"),\n ];\n const rows = mists.map((m) => [\n m.slug,\n m.name ?? m.droplet?.name ?? m.vendor_name ?? chalk.dim(\"(unnamed)\"),\n colorState(m.state),\n m.hostable_type ?? chalk.dim(\"standalone\"),\n relativeDate(m.updated_at),\n ]);\n printTable([header, ...rows]);\n }),\n );\n","/**\n * Integration points — the attach mechanism that replaced hostable\n * (#19684/#19687). A droplet integration point wires a droplet to a mist;\n * creating one auto-provisions the droplet env + triggers a redeploy\n * (#19685).\n *\n * `POST/GET /api/v202604/mists/:mist_id/integration_points`\n * `DELETE …/integration_points/:id` (detach)\n *\n * The link is immutable: there is no update route and no `active` field —\n * toggling a surface is destroy + create (enablement lives on the surface\n * record itself, e.g. DropZone#active).\n */\n\nimport type { MistClient } from \"./client.js\";\nimport type {\n IntegrationPoint,\n IntegrationPointCreateInput,\n IntegrationPointKind,\n IntegrationPointShowResponse,\n IntegrationPointsListResponse,\n} from \"../types.js\";\nimport { resolveMistId } from \"./id-resolver.js\";\n\nfunction basePath(mistId: string): string {\n return `/api/v202604/mists/${mistId}/integration_points`;\n}\n\nexport async function listIntegrationPoints(\n client: MistClient,\n slug: string,\n): Promise<IntegrationPointsListResponse> {\n const id = await resolveMistId(client, slug);\n return client.get<IntegrationPointsListResponse>(basePath(id));\n}\n\nexport async function createIntegrationPoint(\n client: MistClient,\n slug: string,\n input: IntegrationPointCreateInput,\n): Promise<IntegrationPointShowResponse> {\n const id = await resolveMistId(client, slug);\n return client.post<IntegrationPointShowResponse>(basePath(id), {\n integration_point: input,\n });\n}\n\nexport async function deleteIntegrationPoint(\n client: MistClient,\n slug: string,\n integrationPointId: string,\n): Promise<void> {\n const id = await resolveMistId(client, slug);\n return client.delete<void>(`${basePath(id)}/${integrationPointId}`);\n}\n\n/**\n * First droplet-kind integration point in a list, or undefined. A droplet is\n * 1-per-mist (it provisions the managed env), so this is the idempotency key\n * for the droplet create+attach flow.\n */\nexport function findDropletIntegrationPoint(\n integrationPoints: IntegrationPoint[] | undefined | null,\n): IntegrationPoint | undefined {\n return (integrationPoints ?? []).find((ip) => ip.kind === \"droplet\");\n}\n\n/**\n * Find an integration point that already links a specific surface — matched\n * by uuid (droplet/drop-zone) or numeric id (mobile-embed), optionally scoped\n * to a kind. Used to make attach-existing idempotent (avoid the server's 422\n * \"already taken\"). Uses the `integratable_uuid` echo (#19689) when present.\n */\nexport function findIntegrationPointBySurface(\n integrationPoints: IntegrationPoint[] | undefined | null,\n target: { kind?: IntegrationPointKind; uuid?: string; id?: number },\n): IntegrationPoint | undefined {\n return (integrationPoints ?? []).find((ip) => {\n if (target.kind && ip.kind !== target.kind) return false;\n if (target.uuid && ip.integratable_uuid === target.uuid) return true;\n if (target.id != null && ip.integratable_id === target.id) return true;\n return false;\n });\n}\n","/** `fluid mist show <slug>` — single mist detail, including its attached\n * surfaces (integration points). Surfaces scheduled_destroy_at when state is\n * pending_destroy. */\n\nimport chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport { createMistClient } from \"../api/client.js\";\nimport { showMist } from \"../api/mists.js\";\nimport { listIntegrationPoints } from \"../api/integration_points.js\";\nimport { runAction } from \"../lib/error-handler.js\";\nimport { resolveSlug } from \"../lib/slug-resolver.js\";\nimport {\n colorState,\n formatAbsolute,\n mistLabel,\n printJson,\n relativeDate,\n} from \"../lib/output.js\";\nimport type { IntegrationPoint } from \"../types.js\";\n\ninterface Opts {\n json?: boolean;\n}\n\n/** Display ref for an attached surface: the uuid echo when present, else id. */\nfunction surfaceRef(ip: IntegrationPoint): string {\n if (ip.integratable_uuid) return ip.integratable_uuid;\n if (ip.integratable_id != null) return `#${ip.integratable_id}`;\n return \"(unknown)\";\n}\n\nexport const showCommand = new Command(\"show\")\n .description(\"Show one mist by slug\")\n .argument(\"[slug]\", \"The mist slug (defaults to .mist file in CWD)\")\n .option(\"--json\", \"Emit raw API JSON\")\n .action((slug: string | undefined, opts: Opts) =>\n runAction(async () => {\n const resolved = resolveSlug(slug);\n const client = createMistClient();\n const { mist } = await showMist(client, resolved);\n // Attached surfaces live on integration points now (hostable is retired).\n const integrationPoints = (await listIntegrationPoints(client, resolved))\n .integration_points;\n\n if (opts.json)\n return printJson({ mist, integration_points: integrationPoints });\n\n const lines: string[] = [];\n const label = mistLabel(mist.name, mist.vendor_name);\n lines.push(`${label} ${chalk.dim(mist.slug)}`);\n if (mist.vendor_name && mist.name && mist.name !== mist.vendor_name) {\n lines.push(` vendor ${chalk.dim(mist.vendor_name)}`);\n }\n lines.push(` state ${colorState(mist.state)}`);\n if (mist.kind) lines.push(` kind ${mist.kind}`);\n if (mist.public_url)\n lines.push(` url ${chalk.cyan(mist.public_url)}`);\n if (integrationPoints.length > 0) {\n lines.push(` surfaces ${integrationPoints.length}`);\n for (const ip of integrationPoints) {\n lines.push(\n ` ${chalk.dim(\"·\")} ${ip.kind} ${surfaceRef(ip)} ${chalk.dim(`integration ${ip.id}`)}`,\n );\n }\n }\n if (mist.provisioned_at)\n lines.push(` live ${relativeDate(mist.provisioned_at)}`);\n lines.push(` updated ${relativeDate(mist.updated_at)}`);\n if (mist.scheduled_destroy_at) {\n lines.push(\n chalk.magenta(\n ` destroy scheduled ${formatAbsolute(mist.scheduled_destroy_at)} (run \\`fluid mist restore ${mist.slug}\\` to undo)`,\n ),\n );\n }\n console.log(lines.join(\"\\n\"));\n }),\n );\n","/**\n * Spinner-backed `StepReporter` for interactive human runs. Split from\n * `step-reporter.ts` so tests can use the recording reporter without pulling\n * in `ora`'s ESM-only dependency chain.\n */\n\nimport ora, { type Ora } from \"ora\";\nimport type { StepReporter } from \"./step-reporter.js\";\n\nexport function createOraReporter(): StepReporter {\n const spinner: Ora = ora();\n return {\n start(text) {\n spinner.start(text);\n },\n update(text) {\n spinner.text = text;\n },\n succeed(text) {\n spinner.succeed(text);\n },\n skip(text) {\n // ora has no native \"skip\" — use the info glyph.\n spinner.info(text);\n },\n info(text) {\n // Persist an info line without killing the running spinner; the next\n // start() re-arms it.\n spinner.stopAndPersist({ symbol: \"•\", text });\n },\n warn(text) {\n spinner.warn(text);\n },\n fail(text) {\n spinner.fail(text);\n },\n };\n}\n","/**\n * Step reporter — the single seam the droplet provisioning engine uses to\n * surface progress. Human runs get `ora` spinners; `--json` runs get a\n * silent reporter (the command prints the final JSON); tests get a\n * recording reporter they can assert step ordering against.\n *\n * Keeping the engine reporter-agnostic is what lets the flow be exposed two\n * ways (`create --droplet` and `attach-droplet`) from one implementation.\n *\n * NOTE: this module deliberately imports nothing runtime-heavy (no `ora`) so\n * tests can pull in the recording reporter without loading the spinner's\n * ESM-only dependency chain. The spinner reporter lives in `ora-reporter.ts`.\n */\n\nexport interface StepReporter {\n /** Begin a step (starts/re-labels the spinner). */\n start(text: string): void;\n /** Update the in-progress step's label without ending it. */\n update(text: string): void;\n /** End the current step as succeeded. */\n succeed(text?: string): void;\n /** End the current step as skipped (already done — idempotent path). */\n skip(text: string): void;\n /** Informational note (does not end the step). */\n info(text: string): void;\n /** Non-fatal warning (ends the current step, flow continues). */\n warn(text: string): void;\n /** End the current step as failed. */\n fail(text?: string): void;\n}\n\n/** No-op reporter for `--json` mode (final payload printed by the command). */\nexport function createSilentReporter(): StepReporter {\n return {\n start() {},\n update() {},\n succeed() {},\n skip() {},\n info() {},\n warn() {},\n fail() {},\n };\n}\n\nexport interface RecordedStep {\n kind: \"start\" | \"update\" | \"succeed\" | \"skip\" | \"info\" | \"warn\" | \"fail\";\n text: string;\n}\n\nexport interface RecordingReporter extends StepReporter {\n readonly steps: RecordedStep[];\n}\n\n/** In-memory reporter for tests: records every call in order. */\nexport function createRecordingReporter(): RecordingReporter {\n const steps: RecordedStep[] = [];\n const push = (kind: RecordedStep[\"kind\"]) => (text?: string) => {\n steps.push({ kind, text: text ?? \"\" });\n };\n return {\n steps,\n start: push(\"start\"),\n update: push(\"update\"),\n succeed: push(\"succeed\"),\n skip: push(\"skip\"),\n info: push(\"info\"),\n warn: push(\"warn\"),\n fail: push(\"fail\"),\n };\n}\n","export interface DropletLifecycleUrls {\n installWebhookUrl: string;\n uninstallWebhookUrl: string;\n}\n\nexport function normalizeLifecycleOrigin(value: string): string {\n let url: URL;\n try {\n url = new URL(value);\n } catch {\n throw new Error(\"Lifecycle origin must be an absolute HTTP(S) URL\");\n }\n if (url.protocol !== \"http:\" && url.protocol !== \"https:\") {\n throw new Error(\"Lifecycle origin must be an absolute HTTP(S) URL\");\n }\n url.pathname = url.pathname.replace(/\\/+$/, \"\");\n url.search = \"\";\n url.hash = \"\";\n return url.toString().replace(/\\/$/, \"\");\n}\n\nexport function lifecycleUrls(\n publicUrl: string,\n paths: { installPath?: string; uninstallPath?: string } = {},\n): DropletLifecycleUrls {\n const origin = normalizeLifecycleOrigin(publicUrl);\n return {\n installWebhookUrl: `${origin}${normalizePath(paths.installPath ?? \"/api/webhooks/installed\")}`,\n uninstallWebhookUrl: `${origin}${normalizePath(paths.uninstallPath ?? \"/api/webhooks/uninstalled\")}`,\n };\n}\n\nexport function lifecycleUrlsMatch(\n actual: {\n installWebhookUrl?: string | null;\n uninstallWebhookUrl?: string | null;\n },\n expected: DropletLifecycleUrls,\n): boolean {\n return (\n normalizeComparableUrl(actual.installWebhookUrl) ===\n normalizeComparableUrl(expected.installWebhookUrl) &&\n normalizeComparableUrl(actual.uninstallWebhookUrl) ===\n normalizeComparableUrl(expected.uninstallWebhookUrl)\n );\n}\n\nfunction normalizePath(value: string): string {\n return `/${value.replace(/^\\/+/, \"\")}`;\n}\n\nfunction normalizeComparableUrl(\n value: string | null | undefined,\n): string | null {\n if (!value) return null;\n return normalizeLifecycleOrigin(value);\n}\n","/**\n * Droplet CRUD over `/api/droplets`. Thin wrapper — the provisioning\n * orchestration (idempotency, embed-url readiness, attach) lives in\n * `lib/droplet-provision.ts`.\n *\n * Note: droplets are a company-level resource, so unlike the mist\n * sub-resources these paths take the droplet `uuid` (drp_…) directly and\n * don't go through `resolveMistId`.\n */\n\nimport { lifecycleUrls } from \"@fluid-app/mist-core/lifecycle-urls\";\nimport type { MistClient } from \"./client.js\";\nimport type {\n DropletCreateInput,\n DropletCreateRequest,\n DropletCreateResponse,\n DropletRecord,\n DropletShowResponse,\n DropletsListResponse,\n DropletUpdateInput,\n} from \"../types.js\";\n\nconst BASE = \"/api/droplets\";\n\n/**\n * Full scope set from `Droplet::Installation::VALID_SCOPES`. We request\n * them all by default so a Mist-created droplet's installations carry write\n * access out of the box — matching what Mist Desktop sends. Callers can\n * narrow this via the flow's `requestedScopes` option.\n */\nexport const DEFAULT_DROPLET_SCOPES: readonly string[] = [\n \"main\",\n \"prospects\",\n \"members\",\n \"web\",\n \"settings\",\n \"products\",\n \"orders\",\n \"users\",\n \"website\",\n \"header_menus\",\n \"payments\",\n];\n\n/** Lifecycle webhook paths a Mist Next app serves (mirrors Mist Desktop).\n * `embedUrl` must be an absolute HTTP(S) URL so malformed deployment metadata\n * fails before a Droplet is created with unusable callback URLs. */\nexport function dropletWebhookUrls(embedUrl: string): {\n install_webhook_url: string;\n uninstall_webhook_url: string;\n} {\n const urls = lifecycleUrls(embedUrl);\n return {\n install_webhook_url: urls.installWebhookUrl,\n uninstall_webhook_url: urls.uninstallWebhookUrl,\n };\n}\n\n/**\n * Build a droplet create body from a name + (optional) embed URL. When the\n * embed URL is known we also derive the lifecycle webhook URLs from it.\n */\nexport function buildDropletCreateInput(opts: {\n name: string;\n embedUrl?: string | null;\n requestedScopes?: readonly string[];\n /** Ask Fluid for egress through its egress proxy (e.g. to reach Exigo);\n * a company admin's request admits the droplet. */\n egressProxy?: boolean;\n}): DropletCreateInput {\n const scopes = [...(opts.requestedScopes ?? DEFAULT_DROPLET_SCOPES)];\n const summary = \"Created by Fluid Mist\";\n const input: DropletCreateInput = {\n name: opts.name,\n requested_scopes: scopes,\n settings: {\n marketplace_page: { summary },\n details_page: { summary },\n },\n };\n if (opts.embedUrl) {\n input.embed_url = opts.embedUrl;\n Object.assign(input, dropletWebhookUrls(opts.embedUrl));\n }\n // Sent only when asked for: the API defaults it to off.\n if (opts.egressProxy) input.egress_proxy_requested = true;\n return input;\n}\n\nexport function createDroplet(\n client: MistClient,\n body: DropletCreateRequest,\n): Promise<DropletCreateResponse> {\n return client.post<DropletCreateResponse>(BASE, body);\n}\n\nexport function getDroplet(\n client: MistClient,\n uuid: string,\n): Promise<DropletShowResponse> {\n return client.get<DropletShowResponse>(`${BASE}/${encodeURIComponent(uuid)}`);\n}\n\n/** Page size + runaway guard for walking the page-paginated droplets list. */\nconst PER_PAGE = 100;\nconst MAX_PAGES = 25;\n\n/**\n * Recover a droplet's drp_ uuid from its numeric id by paging the company's\n * droplets list (`GET /api/droplets?page=&per_page=` — the list rows carry\n * `id`, unlike the create/show responses). Needed for legacy integration\n * points that only echo `integratable_id`: the show endpoint resolves by\n * uuid, so a numeric id can't be fetched directly. Returns null when no\n * droplet matches within the page cap.\n */\nexport async function findDropletUuidByNumericId(\n client: MistClient,\n id: number,\n): Promise<string | null> {\n for (let page = 1; page <= MAX_PAGES; page++) {\n const resp = await client.get<DropletsListResponse>(\n `${BASE}?page=${page}&per_page=${PER_PAGE}`,\n );\n const batch = resp.droplets ?? [];\n const match = batch.find((d) => d.id === id);\n if (match) return match.uuid ?? null;\n const totalPages = resp.meta?.pagination?.total_pages;\n const done =\n totalPages != null ? page >= totalPages : batch.length < PER_PAGE;\n if (done) break;\n }\n return null;\n}\n\n/**\n * Recover a Droplet created by an interrupted surface-provisioning run. The\n * canonical Mist embed URL is stable and unique to that deployment, so a\n * matching unlinked Droplet can be attached instead of creating a duplicate.\n */\nexport async function findDropletByEmbedUrl(\n client: MistClient,\n embedUrl: string,\n): Promise<DropletRecord | null> {\n const expectedUrl = embedUrl.replace(/\\/$/, \"\");\n for (let page = 1; page <= MAX_PAGES; page++) {\n const resp = await client.get<DropletsListResponse>(\n `${BASE}?page=${page}&per_page=${PER_PAGE}`,\n );\n const batch = resp.droplets ?? [];\n const match = batch.find(\n (droplet) => droplet.embed_url?.replace(/\\/$/, \"\") === expectedUrl,\n );\n if (match) return match;\n const totalPages = resp.meta?.pagination?.total_pages;\n const done =\n totalPages != null ? page >= totalPages : batch.length < PER_PAGE;\n if (done) break;\n }\n return null;\n}\n\n/** PATCH droplet fields, e.g. the embed URL once known or the egress proxy switch. */\nexport function updateDroplet(\n client: MistClient,\n uuid: string,\n input: DropletUpdateInput,\n): Promise<DropletShowResponse> {\n return client.patch<DropletShowResponse>(\n `${BASE}/${encodeURIComponent(uuid)}`,\n { droplet: input },\n );\n}\n\n/** Convenience: set embed URL + derived webhook URLs in one PATCH. */\nexport function setDropletEmbedUrl(\n client: MistClient,\n uuid: string,\n embedUrl: string,\n): Promise<DropletShowResponse> {\n return updateDroplet(client, uuid, {\n embed_url: embedUrl,\n ...dropletWebhookUrls(embedUrl),\n });\n}\n","/**\n * Droplet installations over `/api/droplet_installations`.\n *\n * Creating a Droplet::Application + linking it as an integration point does\n * NOT connect it — the droplet stays not-connected/inactive for the company,\n * so install-scoped features (dit/dex token exchange via\n * `/api/droplet_installations/exchange`, admin embed rendering, install\n * webhooks) silently don't work. A POST here is what actually installs it.\n *\n * Request shape mirrors Mist Desktop's `installDroplet` in\n * `apps/mist-desktop/src/main/services/mist-integration-points-api.service.ts`.\n * Like droplets, installations are a company-level resource — no mist id.\n */\n\nimport type { MistClient } from \"./client.js\";\nimport type {\n DropletInstallation,\n DropletInstallationCreateRequest,\n DropletInstallationCreateResponse,\n DropletInstallationsListResponse,\n} from \"../types.js\";\n\nconst BASE = \"/api/droplet_installations\";\n\n/** Page size + runaway guard for walking the page-paginated list. */\nconst PER_PAGE = 100;\nconst MAX_PAGES = 25;\n\n/**\n * List ALL of the current company's droplet installations, following the\n * endpoint's page-number pagination (`?page=&per_page=` — same shape as\n * fluid-admin's `getCompanyDroplets`). A single-page fetch would miss rows\n * beyond the first page, making the idempotency check (and the duplicate-\n * rejection confirmation) report a false \"not installed\" forever.\n */\nexport async function listAllDropletInstallations(\n client: MistClient,\n): Promise<DropletInstallation[]> {\n const rows: DropletInstallation[] = [];\n for (let page = 1; page <= MAX_PAGES; page++) {\n const resp = await client.get<DropletInstallationsListResponse>(\n `${BASE}?page=${page}&per_page=${PER_PAGE}`,\n );\n const batch = resp.droplet_installations ?? [];\n rows.push(...batch);\n const totalPages = resp.meta?.pagination?.total_pages;\n const done =\n totalPages != null ? page >= totalPages : batch.length < PER_PAGE;\n if (done) break;\n }\n return rows;\n}\n\n/** Install a droplet (by drp_ uuid) for the current company. */\nexport function createDropletInstallation(\n client: MistClient,\n input: DropletInstallationCreateRequest,\n): Promise<DropletInstallationCreateResponse> {\n return client.post<DropletInstallationCreateResponse>(BASE, input);\n}\n\n/**\n * The company's installation row for a droplet drp_ uuid, or undefined.\n * The idempotency key for the install step: an existing row means the\n * droplet is already connected (installed via a previous run, Mist Desktop,\n * or the admin marketplace).\n */\nexport function findInstallationForDroplet(\n installations: DropletInstallation[] | undefined | null,\n dropletUuid: string,\n): DropletInstallation | undefined {\n return (installations ?? []).find((i) => i.droplet_uuid === dropletUuid);\n}\n","/**\n * Drop-zone surface CRUD over `/api/drop_zones`. A drop zone injects the\n * Mist into a storefront slot; attach it to a mist by its `dz_` uuid via an\n * integration point.\n */\n\nimport type { MistClient } from \"./client.js\";\nimport type {\n DropZoneCreateInput,\n DropZoneCreateRequest,\n DropZoneCreateResponse,\n} from \"../types.js\";\n\nconst BASE = \"/api/drop_zones\";\n\n/**\n * Build a drop-zone create body. The slot id is `page__zone` (same split the\n * storefront preview uses, e.g. `checkout__below_total`) → `settings.page` +\n * `settings.zone`; height (px) → `settings.height`. Without a slot the zone\n * never renders, so the backend needs it to know WHERE to inject.\n */\nexport function buildDropZoneCreateInput(opts: {\n name: string;\n embedUrl?: string | null;\n zone?: string;\n heightPx?: number;\n}): DropZoneCreateInput {\n const [page, zone] = (opts.zone ?? \"\").split(\"__\");\n const settings: NonNullable<DropZoneCreateInput[\"settings\"]> = {};\n if (page) settings.page = page;\n if (zone) settings.zone = zone;\n if (opts.heightPx != null) settings.height = opts.heightPx;\n\n const input: DropZoneCreateInput = { name: opts.name };\n if (opts.embedUrl) input.embed_url = opts.embedUrl;\n if (Object.keys(settings).length > 0) input.settings = settings;\n return input;\n}\n\nexport function createDropZone(\n client: MistClient,\n body: DropZoneCreateRequest,\n): Promise<DropZoneCreateResponse> {\n return client.post<DropZoneCreateResponse>(BASE, body);\n}\n","/**\n * Mobile-embed surface CRUD over `/api/company/mobile_widgets`. A mobile\n * widget embeds the Mist in the mobile app; attach it to a mist by its\n * NUMERIC id (mobile widgets have no uuid — the integration point attaches\n * by `integratable_id`, unlike droplet/drop-zone which attach by uuid).\n */\n\nimport type { MistClient } from \"./client.js\";\nimport type {\n MobileWidgetCreateInput,\n MobileWidgetCreateRequest,\n MobileWidgetCreateResponse,\n} from \"../types.js\";\n\nconst BASE = \"/api/company/mobile_widgets\";\n\n/** Fluid-hosted placeholder cover image (the API requires one). */\nexport const DEFAULT_MOBILE_COVER_IMAGE =\n \"https://ik.imagekit.io/fluid/s3/app/image-placeholder.svg\";\n\n/** Default mobile embed height (px). Mirrors the admin Mobile builder. */\nexport const DEFAULT_MOBILE_HEIGHT = 600;\n\n/**\n * Build a mobile-widget create body. `cover_image_url` and `height` are\n * REQUIRED by the API (422 without them); we fall back to the Fluid\n * placeholder + default height when the caller doesn't supply them.\n */\nexport function buildMobileWidgetCreateInput(opts: {\n name: string;\n embedUrl: string;\n coverImageUrl?: string;\n heightPx?: number;\n}): MobileWidgetCreateInput {\n return {\n name: opts.name,\n embed_url: opts.embedUrl,\n cover_image_url: opts.coverImageUrl || DEFAULT_MOBILE_COVER_IMAGE,\n height: opts.heightPx ?? DEFAULT_MOBILE_HEIGHT,\n scrollable: true,\n };\n}\n\nexport function createMobileWidget(\n client: MistClient,\n body: MobileWidgetCreateRequest,\n): Promise<MobileWidgetCreateResponse> {\n return client.post<MobileWidgetCreateResponse>(BASE, body);\n}\n","/** Env-var sub-resource. Spec §4 + §3 \"Env vars\". */\n\nimport type { MistClient } from \"./client.js\";\nimport type { EnvVar, EnvVarsListResponse } from \"../types.js\";\nimport { resolveMistId } from \"./id-resolver.js\";\n\nfunction path(id: string, key?: string) {\n return key\n ? `/api/v202604/mists/${id}/env_vars/${encodeURIComponent(key)}`\n : `/api/v202604/mists/${id}/env_vars`;\n}\n\nexport async function listEnvVars(\n client: MistClient,\n slug: string,\n): Promise<EnvVarsListResponse> {\n const id = await resolveMistId(client, slug);\n return client.get<EnvVarsListResponse>(path(id));\n}\n\ninterface UpsertResponse {\n env_var: EnvVar;\n meta: unknown;\n}\n\n/** Create a new env var (the spec routes `set` to POST for new keys). */\nexport async function createEnvVar(\n client: MistClient,\n slug: string,\n key: string,\n value: string,\n targets?: EnvVar[\"targets\"],\n): Promise<UpsertResponse> {\n const id = await resolveMistId(client, slug);\n return client.post<UpsertResponse>(path(id), {\n env_var: { key, value, targets },\n });\n}\n\n/** Update an existing env var (the spec routes `set` to PATCH for existing keys). */\nexport async function updateEnvVar(\n client: MistClient,\n slug: string,\n key: string,\n value: string,\n targets?: EnvVar[\"targets\"],\n): Promise<UpsertResponse> {\n const id = await resolveMistId(client, slug);\n return client.patch<UpsertResponse>(path(id, key), {\n env_var: { value, targets },\n });\n}\n\nexport async function deleteEnvVar(\n client: MistClient,\n slug: string,\n key: string,\n): Promise<void> {\n const id = await resolveMistId(client, slug);\n return client.delete<void>(path(id, key));\n}\n","/**\n * Wire types for the Mist API. Hand-rolled until\n * `docs/openapi/storefront-v202604.yaml` lands in the Fluid repo and we can\n * generate them — at which point this file gets swapped for the generated\n * version (the field names are the same, so consumers don't change).\n *\n * Source of truth: `features/mist/cli-spec.md` § 4.\n */\n\nexport type MistKind = \"next_app\";\n\nexport type MistState =\n | \"pending\"\n | \"provisioning\"\n | \"live\"\n | \"failed\"\n | \"pending_destroy\"\n | \"archived\";\n\n/**\n * Canonical hostable type strings as used on the wire. These match the Rails\n * model class names exactly — the API rejects anything else with a 400.\n *\n * Source: fluid `features/mist/cli-spec.md` § \"What changed on the Rails side\"\n * (PR #18678).\n */\nexport type HostableType = \"Droplet::Application\" | \"MobileWidget\" | \"DropZone\";\n\nexport interface Droplet {\n id: number;\n name: string;\n uuid: string;\n}\n\n/**\n * Managed env keys the backend writes onto a Mist once a droplet is\n * attached (backfill + redeploy). The CLI VERIFIES these appear with\n * `managed_by: \"mist\"` — it never sets them (the API 422s on write).\n *\n * Source: settled backend contract (#19685 auto-provisions the droplet\n * env on integration-point create).\n */\nexport const MANAGED_DROPLET_ENV_KEYS = [\n \"FLUID_DROPLET_UUID\",\n \"FLUID_DROPLET_SECRET\",\n \"FLUID_WEBHOOK_AUTH_TOKEN\",\n] as const;\n\nexport type ManagedDropletEnvKey = (typeof MANAGED_DROPLET_ENV_KEYS)[number];\n\nexport interface Mist {\n id: string;\n slug: string;\n /** Optional display label set via `--name`; falls back to `hostable.name`\n * server-side when blank. */\n name: string | null;\n state: MistState;\n kind: MistKind;\n /**\n * @deprecated Hostable is retired (#19684/#19687). Droplet attach now\n * lives on integration points. These fields remain optional so reads of\n * older serializer payloads don't break, but nothing writes them anymore.\n */\n hostable_type?: HostableType | null;\n /** @deprecated see `hostable_type`. */\n hostable_id?: number | null;\n /**\n * Legacy vendor-assigned identifier. Observed as `null` on every mist in\n * production today — treat as optional and fall back to `name`/`slug`,\n * never assume it's present.\n */\n vendor_name: string | null;\n public_url: string | null;\n /** ISO-8601 when state === \"pending_destroy\"; null otherwise. */\n scheduled_destroy_at: string | null;\n droplet: Droplet | null;\n provisioned_at: string | null;\n created_at: string;\n updated_at: string;\n}\n\nexport interface PaginationCursor {\n cursor: string | null;\n limit: number;\n prev_cursor: string | null;\n next_cursor: string | null;\n}\n\nexport interface ApiMeta {\n request_id: string;\n timestamp: string;\n pagination?: PaginationCursor;\n}\n\nexport interface ApiCollection<TKey extends string, TItem> {\n meta: ApiMeta;\n status: number;\n}\n\nexport type MistListResponse = {\n mists: Mist[];\n meta: ApiMeta;\n status: number;\n};\n\nexport type MistShowResponse = {\n mist: Mist;\n meta: ApiMeta;\n status: number;\n};\n\nexport interface MistCreateRequest {\n mist: {\n name?: string;\n kind?: MistKind;\n };\n}\n\n/**\n * `PATCH /api/v202604/mists/:id` body. Rename only — attach/detach moved\n * off the Mist record onto integration points (#19684/#19687), so hostable\n * fields no longer belong here.\n */\nexport interface MistUpdateRequest {\n mist: {\n name?: string;\n kind?: MistKind;\n };\n}\n\n// Droplets — `POST /api/droplets`, `PATCH /api/droplets/:uuid`.\n\nexport interface DropletCreateInput {\n name: string;\n /** The Mist's `public_url`. Optional so we can create before first deploy\n * finishes and PATCH the embed URL in once it's ready. */\n embed_url?: string;\n install_webhook_url?: string;\n uninstall_webhook_url?: string;\n requested_scopes?: string[];\n /** Ask Fluid to let the droplet's installations reach external systems\n * (e.g. Exigo) through Fluid's egress proxy. The request alone admits\n * the droplet; a root admin's grant is only a record. Omitted means not\n * asked. */\n egress_proxy_requested?: boolean;\n settings?: Record<string, unknown>;\n}\n\nexport interface DropletUpdateInput {\n embed_url?: string;\n install_webhook_url?: string;\n uninstall_webhook_url?: string;\n /** Ask for (true) or withdraw (false) egress through the proxy. Only a\n * Fluid root admin sets the grant itself. */\n egress_proxy_requested?: boolean;\n}\n\nexport interface DropletCreateRequest {\n droplet: DropletCreateInput;\n}\n\nexport interface DropletUpdateRequest {\n droplet: DropletUpdateInput;\n}\n\nexport interface DropletRecord {\n /**\n * Numeric id is NOT returned by `POST /api/droplets` — attach by `uuid`\n * instead (see `IntegrationPointCreateInput.integratable_uuid`). The\n * `GET /api/droplets` list rows DO carry it, which is the only way to\n * map a legacy integration point's `integratable_id` back to a uuid.\n */\n id?: number;\n uuid: string;\n name: string;\n embed_url?: string | null;\n install_webhook_url?: string | null;\n uninstall_webhook_url?: string | null;\n /** The owner's egress request and the Fluid root-admin grant. Both are\n * absent when the caller is not a company admin, and from APIs that\n * predate them. */\n egress_proxy_requested?: boolean;\n egress_proxy_enabled?: boolean;\n}\n\nexport interface DropletCreateResponse {\n droplet: DropletRecord;\n /** Returned exactly once on create. We do NOT forward it — the backend\n * reads it server-side during env backfill. */\n webhook_secret?: string;\n meta?: ApiMeta;\n status?: number;\n}\n\nexport interface DropletShowResponse {\n droplet: DropletRecord;\n meta?: ApiMeta;\n status?: number;\n}\n\n/**\n * Page-number pagination used by the company-level droplet + installation\n * list endpoints (`?page=&per_page=`) — unlike the mist sub-resources,\n * these do NOT use cursor pagination. Shape mirrors fluid-admin's\n * `networking/droplets.api.ts` response schemas.\n */\nexport interface PagePaginationMeta {\n request_id?: string;\n timestamp?: string;\n pagination?: {\n current_page?: number;\n total_pages?: number;\n total_count?: number;\n per_page?: number;\n };\n}\n\n/** `GET /api/droplets` — the company's droplets, page-paginated. The list\n * rows carry the numeric `id` (unlike `POST /api/droplets`), which is how\n * a legacy integration point's `integratable_id` maps back to a uuid. */\nexport interface DropletsListResponse {\n droplets?: DropletRecord[];\n meta?: PagePaginationMeta;\n status?: number;\n}\n\n// Droplet installations — `GET/POST /api/droplet_installations`.\n// Creating a Droplet::Application + linking it via an integration point does\n// NOT connect it for the company — a separate installation is what activates\n// install-scoped features (dit/dex token exchange, admin embed rendering,\n// install webhooks). Shape mirrors Mist Desktop's\n// mist-integration-points-api.service.ts `installDroplet`.\n\nexport interface DropletInstallation {\n /** The installation's own dri_ uuid (needed to uninstall later). */\n uuid?: string;\n /** The installed droplet's drp_ uuid. */\n droplet_uuid?: string;\n active?: boolean;\n /** Read by the reinstall plan to report what the backend preserves. */\n scopes?: string[];\n role_id?: string | number | null;\n}\n\nexport interface DropletInstallationsListResponse {\n droplet_installations?: DropletInstallation[];\n meta?: PagePaginationMeta;\n status?: number;\n}\n\nexport interface DropletInstallationCreateRequest {\n droplet_uuid: string;\n scopes?: string[];\n}\n\nexport interface DropletInstallationCreateResponse {\n droplet_installation?: DropletInstallation;\n meta?: ApiMeta;\n status?: number;\n}\n\n// Integration points — the attach mechanism that replaced hostable.\n// `POST/GET /api/v202604/mists/:mist_id/integration_points`.\n\n/** Hyphenated wire kinds the integration-points API speaks. */\nexport type IntegrationPointKind = \"droplet\" | \"drop-zone\" | \"mobile-embed\";\n\nexport interface IntegrationPoint {\n /** The integration point's own id — a uuid string on the wire. */\n id: string;\n kind: IntegrationPointKind | string;\n /** Numeric id of the attached resource (server-resolved; present in reads). */\n integratable_id?: number;\n /**\n * Public uuid of the attached surface (drp_/dz_ for droplets/drop-zones;\n * a synthetic uuid for mobile embeds). Echoed on every row since #19689 —\n * the read path uses it for display + resolution.\n */\n integratable_uuid?: string | null;\n integratable_type?: string | null;\n mist_id?: string;\n}\n\n/**\n * Attach input. Name the surface by EXACTLY ONE identifier:\n * - droplet + drop-zone attach by `integratable_uuid` (their records carry a\n * drp_/dz_ uuid and no readable numeric id).\n * - mobile-embed attaches by `integratable_id` (a mobile widget has a numeric\n * id and no uuid — `integratable_uuid` 404s for it).\n * The API rejects neither/both with a 400. The link is immutable (no update\n * route, no `active`): toggling is destroy + create.\n */\nexport interface IntegrationPointCreateInput {\n kind: IntegrationPointKind;\n integratable_uuid?: string;\n integratable_id?: number;\n}\n\nexport interface IntegrationPointCreateRequest {\n integration_point: IntegrationPointCreateInput;\n}\n\nexport interface IntegrationPointsListResponse {\n integration_points: IntegrationPoint[];\n meta: ApiMeta;\n status: number;\n}\n\nexport interface IntegrationPointShowResponse {\n integration_point: IntegrationPoint;\n meta: ApiMeta;\n status: number;\n}\n\n// Drop zones — `POST /api/drop_zones`. Attach by the dz_ uuid.\n\nexport interface DropZoneCreateInput {\n name: string;\n embed_url?: string;\n /** Slot placement: { page, zone, height }. Without it the zone won't render. */\n settings?: { page?: string; zone?: string; height?: number };\n}\n\nexport interface DropZoneCreateRequest {\n drop_zone: DropZoneCreateInput;\n}\n\nexport interface DropZoneRecord {\n uuid: string;\n name: string;\n embed_url?: string | null;\n active?: boolean;\n settings?: Record<string, unknown>;\n}\n\nexport interface DropZoneCreateResponse {\n drop_zone: DropZoneRecord;\n meta?: ApiMeta;\n status?: number;\n}\n\n// Mobile embeds — `POST /api/company/mobile_widgets`. Attach by numeric id.\n\nexport interface MobileWidgetCreateInput {\n name: string;\n embed_url: string;\n /** REQUIRED by the API (422 without it). */\n cover_image_url: string;\n /** REQUIRED by the API (422 without it). */\n height: number;\n scrollable?: boolean;\n}\n\nexport interface MobileWidgetCreateRequest {\n mobile_widget: MobileWidgetCreateInput;\n}\n\nexport interface MobileWidgetRecord {\n id: number;\n name: string;\n embed_url?: string | null;\n cover_image_url?: string | null;\n height?: number;\n}\n\nexport interface MobileWidgetCreateResponse {\n mobile_widget: MobileWidgetRecord;\n meta?: ApiMeta;\n status?: number;\n}\n\nexport interface EnvVar {\n key: string;\n value: string;\n targets: (\"production\" | \"preview\" | \"development\")[];\n managed_by: \"user\" | \"mist\";\n}\n\nexport interface EnvVarsListResponse {\n env_vars: EnvVar[];\n meta: ApiMeta;\n status: number;\n}\n\nexport interface GitCredential {\n remote_url: string;\n expires_at: string;\n}\n\nexport interface GitCredentialResponse {\n git_credential: GitCredential;\n meta: ApiMeta;\n status: number;\n}\n\nexport type DeploymentState =\n | \"ready\"\n | \"building\"\n | \"error\"\n | \"canceled\"\n | \"queued\";\n\nexport interface Deployment {\n id: string;\n state: DeploymentState;\n url: string | null;\n commit_sha: string | null;\n commit_message: string | null;\n created_at: string;\n target: \"production\" | \"preview\";\n}\n\nexport interface DeploymentsListResponse {\n deployments: Deployment[];\n meta: ApiMeta;\n status: number;\n}\n\nexport interface DeploymentShowResponse {\n deployment: Deployment;\n meta: ApiMeta;\n status: number;\n}\n\nexport interface LogEntry {\n timestamp: string;\n level: \"stdout\" | \"stderr\";\n message: string;\n source: string;\n}\n\nexport interface LogsResponse {\n deployment_id: string;\n logs: LogEntry[];\n meta: ApiMeta;\n status: number;\n}\n\n/** The `.mist` file the CLI writes after `fluid mist clone <slug>`. */\nexport interface MistFile {\n slug: string;\n /**\n * Legacy vendor-assigned identifier. Observed as `null` on every mist in\n * production today — treat as optional and fall back to `name`/`slug`,\n * never assume it's present.\n */\n vendor_name: string | null;\n public_url: string | null;\n}\n","/**\n * Pure planning for the Droplet lifecycle repair: given a Mist's public URL,\n * the app's lifecycle webhook paths and the Droplet's current URLs, compute\n * the canonical embed/install/uninstall URLs and the repair summary.\n *\n * Used by the `fluid mist droplet repair` and `reinstall` CLI commands, which\n * Mist's agent runs through run_cli. Transport (auth, fetch, recorded\n * history) stays with the caller.\n */\n\nexport interface DropletLifecyclePaths {\n installPath: string;\n uninstallPath: string;\n}\n\nexport const DEFAULT_DROPLET_LIFECYCLE_PATHS: Readonly<DropletLifecyclePaths> =\n Object.freeze({\n installPath: \"/api/webhooks/installed\",\n uninstallPath: \"/api/webhooks/uninstalled\",\n });\n\n/** The note every repair summary carries: the repair fixes future delivery,\n * it does not replay `droplet.installed` for an existing installation. */\nexport const LIFECYCLE_REPAIR_NOTE =\n \"This repairs future Droplet install/uninstall lifecycle delivery. It does not replay droplet.installed for an already-installed Droplet.\";\n\n/** Strip trailing slashes; null for an absent or empty value. */\nexport function normalizePublicUrl(\n value: string | null | undefined,\n): string | null {\n if (!value) return null;\n return value.replace(/\\/+$/, \"\");\n}\n\n/** A blank or missing path falls back; a relative one gains a leading `/`. */\nexport function normalizeWebhookPath(value: unknown, fallback: string): string {\n if (typeof value !== \"string\") return fallback;\n const trimmed = value.trim();\n if (!trimmed) return fallback;\n return trimmed.startsWith(\"/\") ? trimmed : `/${trimmed}`;\n}\n\n/**\n * Lifecycle paths from a parsed `fluid.capabilities.json`\n * (`dropletLifecycle.installPath` / `uninstallPath`). Anything that is not\n * the expected shape yields the defaults.\n */\nexport function dropletLifecyclePathsFromCapabilities(\n capabilities: unknown,\n): DropletLifecyclePaths {\n if (!isRecord(capabilities)) return { ...DEFAULT_DROPLET_LIFECYCLE_PATHS };\n const lifecycle = capabilities[\"dropletLifecycle\"];\n const source = isRecord(lifecycle) ? lifecycle : {};\n return {\n installPath: normalizeWebhookPath(\n source[\"installPath\"],\n DEFAULT_DROPLET_LIFECYCLE_PATHS.installPath,\n ),\n uninstallPath: normalizeWebhookPath(\n source[\"uninstallPath\"],\n DEFAULT_DROPLET_LIFECYCLE_PATHS.uninstallPath,\n ),\n };\n}\n\nexport interface CanonicalDropletUrls {\n embedUrl: string;\n installWebhookUrl: string;\n uninstallWebhookUrl: string;\n}\n\n/** The canonical Droplet URLs for a Mist served at `publicUrl`. */\nexport function canonicalDropletUrls(\n publicUrl: string,\n paths: DropletLifecyclePaths = DEFAULT_DROPLET_LIFECYCLE_PATHS,\n): CanonicalDropletUrls {\n const embedUrl = normalizePublicUrl(publicUrl);\n if (!embedUrl) throw new Error(\"publicUrl is required\");\n return {\n embedUrl,\n installWebhookUrl: `${embedUrl}${paths.installPath}`,\n uninstallWebhookUrl: `${embedUrl}${paths.uninstallPath}`,\n };\n}\n\n/** The Droplet fields the repair reads and writes. */\nexport interface DropletUrlFields {\n embed_url?: string | null;\n install_webhook_url?: string | null;\n uninstall_webhook_url?: string | null;\n}\n\n/** Exact match on all three fields — the repair's no-op condition and its\n * post-PATCH verification. */\nexport function dropletUrlsMatch(\n droplet: DropletUrlFields | null | undefined,\n canonical: CanonicalDropletUrls,\n): boolean {\n return (\n droplet?.embed_url === canonical.embedUrl &&\n droplet.install_webhook_url === canonical.installWebhookUrl &&\n droplet.uninstall_webhook_url === canonical.uninstallWebhookUrl\n );\n}\n\n/** The `PATCH /api/droplets/:uuid` body. Only these three fields change. */\nexport function dropletRepairPatch(canonical: CanonicalDropletUrls): {\n droplet: Required<DropletUrlFields>;\n} {\n return {\n droplet: {\n embed_url: canonical.embedUrl,\n install_webhook_url: canonical.installWebhookUrl,\n uninstall_webhook_url: canonical.uninstallWebhookUrl,\n },\n };\n}\n\nexport type LifecycleRepairStatus = \"unchanged\" | \"planned\" | \"updated\";\n\nexport interface LifecycleRepairSummary extends CanonicalDropletUrls {\n status: LifecycleRepairStatus;\n mistId: string;\n mistSlug: string | null;\n dropletUuid: string;\n previous: {\n embedUrl: string | null;\n installWebhookUrl: string | null;\n uninstallWebhookUrl: string | null;\n };\n note: string;\n}\n\n/**\n * The repair summary before any mutation. `alreadyCorrect` means the caller\n * must not PATCH; otherwise a dry run reports \"planned\" and a live run\n * reports \"updated\" (callers overwrite it after verification).\n */\nexport function planLifecycleRepair(input: {\n mistId: string;\n mistSlug: string | null;\n dropletUuid: string;\n canonical: CanonicalDropletUrls;\n current: DropletUrlFields | null | undefined;\n dryRun: boolean;\n}): { alreadyCorrect: boolean; summary: LifecycleRepairSummary } {\n const alreadyCorrect = dropletUrlsMatch(input.current, input.canonical);\n return {\n alreadyCorrect,\n summary: {\n status: alreadyCorrect\n ? \"unchanged\"\n : input.dryRun\n ? \"planned\"\n : \"updated\",\n mistId: input.mistId,\n mistSlug: input.mistSlug,\n dropletUuid: input.dropletUuid,\n embedUrl: input.canonical.embedUrl,\n installWebhookUrl: input.canonical.installWebhookUrl,\n uninstallWebhookUrl: input.canonical.uninstallWebhookUrl,\n previous: {\n embedUrl: input.current?.embed_url ?? null,\n installWebhookUrl: input.current?.install_webhook_url ?? null,\n uninstallWebhookUrl: input.current?.uninstall_webhook_url ?? null,\n },\n note: LIFECYCLE_REPAIR_NOTE,\n },\n };\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n","export const lifecycleOperationStates = [\n \"queued\",\n \"running\",\n \"succeeded\",\n \"failed\",\n \"requires_action\",\n] as const;\n\nexport type LifecycleOperationState = (typeof lifecycleOperationStates)[number];\n\nexport const lifecycleOperationKinds = [\"replacement\"] as const;\n\nexport type LifecycleOperationKind = (typeof lifecycleOperationKinds)[number];\n\nexport const connectionStates = [\n \"pending\",\n \"connected\",\n \"degraded\",\n \"requires_action\",\n] as const;\n\nexport type ConnectionState = (typeof connectionStates)[number];\n\nexport interface LifecycleOperation {\n id: string;\n kind: LifecycleOperationKind;\n state: LifecycleOperationState;\n stage: string;\n mist_id: string;\n droplet_uuid?: string | null;\n installation_uuid?: string | null;\n connection_state?: ConnectionState | null;\n retry_after_seconds?: number | null;\n error_code?: string | null;\n recovery_action?: string | null;\n created_at: string;\n updated_at: string;\n terminal_at?: string | null;\n}\n\nexport interface LifecycleOperationResponse {\n replacement_operation?: LifecycleOperation;\n}\n\nexport function isTerminalOperation(operation: LifecycleOperation): boolean {\n return (\n operation.state === \"succeeded\" ||\n operation.state === \"failed\" ||\n operation.state === \"requires_action\"\n );\n}\n","import type { LifecycleOperation } from \"./lifecycle-types\";\nimport type { LifecyclePollResult } from \"./operation-polling\";\n\nexport type LifecycleRecovery =\n | { status: \"complete\"; action: null }\n | { status: \"retryable\"; action: string }\n | { status: \"requires_action\"; action: string }\n | { status: \"failed\"; action: string };\n\nexport function classifyLifecycleRecovery(\n result: LifecyclePollResult,\n): LifecycleRecovery {\n if (result.status === \"not_found\") {\n return { status: \"requires_action\", action: \"rediscover_operation\" };\n }\n if (result.status === \"timed_out\") {\n return { status: \"retryable\", action: \"resume_status_polling\" };\n }\n\n return classifyTerminalOperation(result.operation);\n}\n\nfunction classifyTerminalOperation(\n operation: LifecycleOperation,\n): LifecycleRecovery {\n if (operation.state === \"succeeded\") {\n return { status: \"complete\", action: null };\n }\n if (operation.state === \"failed\") {\n return {\n status: \"failed\",\n action: operation.recovery_action ?? \"verify_lifecycle_destination\",\n };\n }\n return {\n status: \"requires_action\",\n action: operation.recovery_action ?? \"reconcile_lifecycle\",\n };\n}\n","/**\n * Pure rules for the irreversible Droplet repair-and-reinstall flow: the\n * replacement-operation endpoints, response parsing, the request guards, the\n * installation choice, the plan and the final watch summary.\n *\n * Used by the `fluid mist droplet reinstall` CLI command, which Mist's agent\n * runs through run_cli (Mist adds its own plan + approval gate on top; see\n * apps/mist-desktop/src/main/tools/fluid-cli-policy.ts). The caller owns\n * transport and the wording of its refusals.\n */\n\nimport {\n connectionStates,\n lifecycleOperationKinds,\n lifecycleOperationStates,\n type LifecycleOperation,\n} from \"./lifecycle-types\";\nimport type { LifecyclePollResult } from \"./operation-polling\";\nimport {\n classifyLifecycleRecovery,\n type LifecycleRecovery,\n} from \"./recovery-classification\";\n\n/** How long one call watches a replacement before handing back a resumable\n * \"still_running\" result. The operation is durable and backend-owned, so\n * watching is resumable at no cost. */\nexport const REPLACEMENT_POLL_BUDGET_MS = 120_000;\n\nexport const REPLACEMENT_IDEMPOTENCY_SCOPE =\n \"single replacement attempt; a later approved retry receives a new key\";\n\nexport const REPLACEMENT_OWNERSHIP_NOTE =\n \"Fluid owns revocation, uninstall delivery, terminalization, replacement creation, and retry recovery as one durable operation.\";\n\nexport function replacementOperationsPath(\n mistId: string,\n installationUuid: string,\n): string {\n return `/api/v202604/mists/${encodeURIComponent(mistId)}/droplet_installations/${encodeURIComponent(installationUuid)}/replacement_operations`;\n}\n\nexport function replacementOperationStatusPath(\n mistId: string,\n installationUuid: string,\n operationId: string,\n): string {\n return `${replacementOperationsPath(mistId, installationUuid)}/${encodeURIComponent(operationId)}`;\n}\n\n/** The operation id a 409 \"active_operation_exists\" response points at. */\nexport function parseActiveOperationId(value: unknown): string | null {\n if (!isRecord(value)) return null;\n const error = value[\"error\"];\n if (!isRecord(error)) return null;\n const details = error[\"details\"];\n if (!isRecord(details)) return null;\n const operationId = details[\"operation_id\"];\n return typeof operationId === \"string\" ? operationId : null;\n}\n\n/** Seconds from a `Retry-After` header; undefined when absent or invalid. */\nexport function parseRetryAfterHeader(\n value: string | null | undefined,\n): number | undefined {\n if (!value) return undefined;\n const seconds = Number(value);\n return Number.isFinite(seconds) && seconds >= 0 ? seconds : undefined;\n}\n\n/** Parse and validate `{ replacement_operation: {...} }`. Throws on a\n * malformed body rather than trusting a partial operation. */\nexport function parseReplacementOperation(value: unknown): LifecycleOperation {\n const key = \"replacement_operation\";\n if (!isRecord(value)) throw new Error(\"Lifecycle response must be an object\");\n const candidate = value[key];\n if (!isRecord(candidate))\n throw new Error(`Lifecycle response is missing ${key}`);\n const id = candidate[\"id\"];\n const kind = candidate[\"kind\"];\n const state = candidate[\"state\"];\n const stage = candidate[\"stage\"];\n const mistId = candidate[\"mist_id\"];\n const createdAt = candidate[\"created_at\"];\n const updatedAt = candidate[\"updated_at\"];\n if (\n typeof id !== \"string\" ||\n !isLifecycleOperationKind(kind) ||\n !isLifecycleOperationState(state) ||\n typeof stage !== \"string\" ||\n typeof mistId !== \"string\" ||\n typeof createdAt !== \"string\" ||\n typeof updatedAt !== \"string\"\n ) {\n throw new Error(\"Lifecycle operation response is malformed\");\n }\n\n const dropletUuid = candidate[\"droplet_uuid\"];\n const installationUuid = candidate[\"installation_uuid\"];\n const connectionState = candidate[\"connection_state\"];\n const errorCode = candidate[\"error_code\"];\n const recoveryAction = candidate[\"recovery_action\"];\n const terminalAt = candidate[\"terminal_at\"];\n const retryAfterSeconds = candidate[\"retry_after_seconds\"];\n if (\n !isOptionalNullableString(dropletUuid) ||\n !isOptionalNullableString(installationUuid) ||\n !isOptionalConnectionState(connectionState) ||\n !isOptionalNullableString(errorCode) ||\n !isOptionalNullableString(recoveryAction) ||\n !isOptionalNullableString(terminalAt) ||\n !isOptionalNullableNumber(retryAfterSeconds)\n ) {\n throw new Error(\n \"Lifecycle operation response has malformed optional fields\",\n );\n }\n\n return {\n id,\n kind,\n state,\n stage,\n mist_id: mistId,\n created_at: createdAt,\n updated_at: updatedAt,\n ...(dropletUuid !== undefined ? { droplet_uuid: dropletUuid } : {}),\n ...(installationUuid !== undefined\n ? { installation_uuid: installationUuid }\n : {}),\n ...(connectionState !== undefined\n ? { connection_state: connectionState }\n : {}),\n ...(errorCode !== undefined ? { error_code: errorCode } : {}),\n ...(recoveryAction !== undefined\n ? { recovery_action: recoveryAction }\n : {}),\n ...(terminalAt !== undefined ? { terminal_at: terminalAt } : {}),\n ...(retryAfterSeconds !== undefined\n ? { retry_after_seconds: retryAfterSeconds }\n : {}),\n };\n}\n\nexport interface ReplacementRequestArgs {\n dryRun?: boolean;\n confirmDestructiveReinstall?: boolean;\n installationUuid?: string;\n resumeOperationId?: string;\n}\n\n/**\n * Guard the request before any network call. A resume must name the\n * installation it started on: status is scoped to the SOURCE installation,\n * the backend terminalizes that source mid-flight, and polling whichever\n * installation is attached now would report a false \"not_found\".\n */\nexport function checkReplacementRequest(\n args: ReplacementRequestArgs,\n):\n | { ok: true; resumeOperationId: string | null }\n | { ok: false; code: \"resume_requires_installation\" } {\n const resumeOperationId = args.resumeOperationId?.trim() || null;\n if (resumeOperationId && !args.installationUuid) {\n return { ok: false, code: \"resume_requires_installation\" };\n }\n return { ok: true, resumeOperationId };\n}\n\n/**\n * Whether the lifecycle URL repair that precedes a replacement must stay\n * read-only. It mutates only on a confirmed start: never on a dry run,\n * never without confirmation, and never on a resume (the call that started\n * the operation already repaired the URLs).\n */\nexport function lifecycleRepairIsReadOnly(\n args: ReplacementRequestArgs,\n resumeOperationId: string | null,\n): boolean {\n return (\n args.dryRun === true ||\n resumeOperationId !== null ||\n args.confirmDestructiveReinstall !== true\n );\n}\n\n/** Starting a new replacement needs explicit confirmation; resuming only\n * reads an operation the user already approved. */\nexport function replacementStartNeedsConfirmation(\n args: ReplacementRequestArgs,\n resumeOperationId: string | null,\n): boolean {\n return !resumeOperationId && args.confirmDestructiveReinstall !== true;\n}\n\nexport interface ReplacementInstallation {\n uuid?: string;\n droplet_uuid?: string;\n active?: boolean;\n scopes?: unknown;\n role_id?: unknown;\n}\n\n/**\n * The installation to replace. An explicitly named `installationUuid`\n * always wins: it is the recovery path for a source that a failed attempt\n * already terminalized (which may sit beside a newer active installation\n * of the same Droplet), and a resume must watch the installation its\n * operation started on, never a rediscovered one. Without a name, the\n * company's active installation of the attached Droplet is used.\n */\nexport function selectReplacementInstallation(input: {\n installations: readonly ReplacementInstallation[];\n dropletUuid: string;\n installationUuid?: string;\n resumeOperationId: string | null;\n}): ReplacementInstallation | undefined {\n if (input.installationUuid) {\n // Keep the listed record (scopes, role) when the named one is active.\n const listed = input.installations.find(\n (candidate) =>\n candidate.uuid === input.installationUuid &&\n candidate.droplet_uuid === input.dropletUuid,\n );\n return (\n listed ?? {\n uuid: input.installationUuid,\n droplet_uuid: input.dropletUuid,\n }\n );\n }\n if (input.resumeOperationId) return undefined;\n return input.installations.find(\n (candidate) => candidate.droplet_uuid === input.dropletUuid,\n );\n}\n\nexport interface ReplacementPlan<TRepair = unknown> {\n status: \"planned\" | \"watching\" | \"pending\";\n mistId: string;\n dropletUuid: string;\n installationUuid: string;\n lifecycleRepair: TRepair;\n replacementPath: string;\n idempotencyKey: string | null;\n idempotencyScope: string;\n irreversible: true;\n preservedByBackend: { scopes: boolean; role_id: boolean };\n note: string;\n}\n\nexport function buildReplacementPlan<TRepair>(input: {\n dryRun: boolean;\n resumeOperationId: string | null;\n mistId: string;\n dropletUuid: string;\n installation: ReplacementInstallation & { uuid: string };\n lifecycleRepair: TRepair;\n idempotencyKey: string;\n}): ReplacementPlan<TRepair> {\n const { installation } = input;\n return {\n status: input.dryRun\n ? \"planned\"\n : input.resumeOperationId\n ? \"watching\"\n : \"pending\",\n mistId: input.mistId,\n dropletUuid: input.dropletUuid,\n installationUuid: installation.uuid,\n lifecycleRepair: input.lifecycleRepair,\n replacementPath: replacementOperationsPath(input.mistId, installation.uuid),\n // A resume starts no new attempt, so it mints no attempt key.\n idempotencyKey: input.resumeOperationId ? null : input.idempotencyKey,\n idempotencyScope: REPLACEMENT_IDEMPOTENCY_SCOPE,\n irreversible: true,\n preservedByBackend: {\n scopes: Array.isArray(installation.scopes),\n role_id:\n typeof installation.role_id === \"string\" ||\n typeof installation.role_id === \"number\",\n },\n note: REPLACEMENT_OWNERSHIP_NOTE,\n };\n}\n\n/**\n * The final result of one watch. A timed-out poll is this caller's watch\n * budget expiring, NOT the backend giving up: it reports \"still_running\"\n * with the ids needed to resume, and is not an error. Only \"succeeded\" is\n * complete.\n */\nexport function summarizeReplacementWatch<TPlan extends object>(input: {\n plan: TPlan;\n pollResult: LifecyclePollResult;\n startedOperation: LifecycleOperation | null;\n operationId: string;\n installationUuid: string;\n watchedForMs: number;\n nextStep: (context: {\n operationId: string;\n installationUuid: string;\n stage: string;\n }) => string;\n}): {\n result: TPlan & {\n status: string;\n replacementOperation: LifecycleOperation | null;\n recovery: LifecycleRecovery;\n watchedForMs?: number;\n resumeOperationId?: string;\n nextStep?: string;\n };\n isError: boolean;\n} {\n const { pollResult, startedOperation } = input;\n const recovery = classifyLifecycleRecovery(pollResult);\n const stillRunning = pollResult.status === \"timed_out\";\n const latestOperation =\n pollResult.status === \"terminal\"\n ? pollResult.operation\n : pollResult.status === \"timed_out\"\n ? (pollResult.operation ?? startedOperation)\n : startedOperation;\n\n return {\n result: {\n ...input.plan,\n status:\n pollResult.status === \"terminal\"\n ? pollResult.operation.state\n : stillRunning\n ? \"still_running\"\n : pollResult.status,\n replacementOperation: latestOperation,\n recovery,\n ...(stillRunning\n ? {\n watchedForMs: input.watchedForMs,\n resumeOperationId: input.operationId,\n nextStep: input.nextStep({\n operationId: input.operationId,\n installationUuid: input.installationUuid,\n stage: latestOperation?.stage ?? \"unknown\",\n }),\n }\n : {}),\n },\n isError: !stillRunning && recovery.status !== \"complete\",\n };\n}\n\nfunction isLifecycleOperationKind(\n value: unknown,\n): value is LifecycleOperation[\"kind\"] {\n return lifecycleOperationKinds.some((candidate) => candidate === value);\n}\n\nfunction isLifecycleOperationState(\n value: unknown,\n): value is LifecycleOperation[\"state\"] {\n return lifecycleOperationStates.some((candidate) => candidate === value);\n}\n\nfunction isOptionalNullableString(\n value: unknown,\n): value is string | null | undefined {\n return value === undefined || value === null || typeof value === \"string\";\n}\n\nfunction isOptionalNullableNumber(\n value: unknown,\n): value is number | null | undefined {\n return value === undefined || value === null || typeof value === \"number\";\n}\n\nfunction isOptionalConnectionState(\n value: unknown,\n): value is LifecycleOperation[\"connection_state\"] {\n return (\n value === undefined ||\n value === null ||\n connectionStates.some((candidate) => candidate === value)\n );\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n","import {\n isTerminalOperation,\n type LifecycleOperation,\n} from \"./lifecycle-types\";\n\nexport interface LifecycleStatusResponse {\n status: number;\n operation?: LifecycleOperation;\n retryAfterSeconds?: number;\n}\n\nexport type LifecyclePollResult =\n | { status: \"terminal\"; operation: LifecycleOperation }\n | { status: \"not_found\" }\n | { status: \"timed_out\"; operation?: LifecycleOperation };\n\nexport interface LifecyclePollingOptions {\n fetchStatus: () => Promise<LifecycleStatusResponse>;\n timeoutMs?: number;\n minimumIntervalMs?: number;\n sleep?: (durationMs: number) => Promise<void>;\n now?: () => number;\n abortSignal?: AbortSignal;\n}\n\nexport async function pollLifecycleOperation({\n fetchStatus,\n timeoutMs = 30 * 60 * 1_000,\n minimumIntervalMs = 2_000,\n sleep: sleepOverride,\n now = Date.now,\n abortSignal,\n}: LifecyclePollingOptions): Promise<LifecyclePollResult> {\n const sleep =\n sleepOverride ??\n ((durationMs: number) => defaultSleep(durationMs, abortSignal));\n const startedAt = now();\n let latestOperation: LifecycleOperation | undefined;\n\n while (now() - startedAt < timeoutMs) {\n abortSignal?.throwIfAborted();\n const response = await fetchStatus();\n abortSignal?.throwIfAborted();\n if (response.status === 404) return { status: \"not_found\" };\n const transientServerError =\n response.status >= 500 && response.status < 600;\n if (\n response.status !== 200 &&\n response.status !== 429 &&\n !transientServerError\n ) {\n throw new Error(\n `Lifecycle status request failed with HTTP ${response.status}`,\n );\n }\n\n if (response.operation) {\n latestOperation = response.operation;\n if (isTerminalOperation(response.operation)) {\n return { status: \"terminal\", operation: response.operation };\n }\n }\n\n const retryAfterMs = Math.max(\n minimumIntervalMs,\n (response.retryAfterSeconds ??\n response.operation?.retry_after_seconds ??\n 0) * 1_000,\n );\n await sleep(\n Math.min(retryAfterMs, Math.max(0, timeoutMs - (now() - startedAt))),\n );\n abortSignal?.throwIfAborted();\n }\n\n return latestOperation\n ? { status: \"timed_out\", operation: latestOperation }\n : { status: \"timed_out\" };\n}\n\nfunction defaultSleep(\n durationMs: number,\n abortSignal?: AbortSignal,\n): Promise<void> {\n return new Promise((resolve, reject) => {\n // The signal belongs to the CALLER and outlives this sleep — for the\n // droplet repair path it is the AI turn's controller. `{ once: true }`\n // does not clean up here: it removes a listener only after the event\n // FIRES, and on the normal path abort never fires. Without an explicit\n // removal each poll iteration retained a listener, its closure and the\n // Timeout object for the life of the signal — up to timeoutMs /\n // minimumIntervalMs (30min / 2s = 900) per invocation. Node emits no\n // MaxListenersExceededWarning for AbortSignal, so nothing surfaced it.\n const onAbort = (): void => {\n clearTimeout(timer);\n reject(abortSignal?.reason);\n };\n const timer = setTimeout(() => {\n abortSignal?.removeEventListener(\"abort\", onAbort);\n resolve();\n }, durationMs);\n abortSignal?.addEventListener(\"abort\", onAbort, { once: true });\n });\n}\n","/**\n * Backend-owned Droplet replacement operations:\n *\n * POST /api/v202604/mists/:mist_id/droplet_installations/:installation_uuid/replacement_operations\n * GET …/replacement_operations/:id\n *\n * One POST starts a durable operation in which Fluid revokes, delivers the\n * uninstall, terminalizes the source installation and creates the\n * replacement. The CLI never DELETEs and re-POSTs an installation itself.\n *\n * Mirrors Mist Desktop's `droplet-lifecycle-api.service.ts`; the paths,\n * response parsing and polling come from `@fluid-app/mist-core`. These go\n * through `client.request` (raw fetch) because the flow reads the HTTP\n * status and `Retry-After` header, not just a parsed body.\n */\n\nimport type { LifecycleOperation } from \"@fluid-app/mist-core/lifecycle-types\";\nimport {\n parseActiveOperationId,\n parseReplacementOperation,\n parseRetryAfterHeader,\n replacementOperationStatusPath,\n replacementOperationsPath,\n} from \"@fluid-app/mist-core/droplet-replacement\";\nimport {\n pollLifecycleOperation,\n type LifecyclePollResult,\n} from \"@fluid-app/mist-core/operation-polling\";\nimport { MistApiError, type MistClient } from \"./client.js\";\n\nexport async function startDropletReplacement(\n client: MistClient,\n input: { mistId: string; installationUuid: string; idempotencyKey: string },\n): Promise<LifecycleOperation> {\n const response = await send(\n client,\n replacementOperationsPath(input.mistId, input.installationUuid),\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ idempotency_key: input.idempotencyKey }),\n },\n );\n const body = await readJson(response);\n if (response.ok) return parseReplacementOperation(body);\n\n // An equivalent replacement is already running: watch that one instead\n // of failing (the backend allows one active operation per installation).\n const activeOperationId =\n response.status === 409 ? parseActiveOperationId(body) : null;\n if (activeOperationId) {\n return fetchDropletReplacement(client, {\n mistId: input.mistId,\n installationUuid: input.installationUuid,\n operationId: activeOperationId,\n });\n }\n\n const retryAfterSeconds = parseRetryAfterHeader(\n response.headers.get(\"Retry-After\"),\n );\n const retryHint =\n retryAfterSeconds === undefined\n ? \"\"\n : `; retry after ${retryAfterSeconds}s`;\n throw new MistApiError(\n `Droplet replacement start failed with HTTP ${response.status}${retryHint}`,\n response.status,\n );\n}\n\nexport async function fetchDropletReplacement(\n client: MistClient,\n input: { mistId: string; installationUuid: string; operationId: string },\n): Promise<LifecycleOperation> {\n const response = await send(\n client,\n replacementOperationStatusPath(\n input.mistId,\n input.installationUuid,\n input.operationId,\n ),\n { method: \"GET\" },\n );\n if (!response.ok) {\n throw new MistApiError(\n `Active replacement status failed with HTTP ${response.status}`,\n response.status,\n );\n }\n return parseReplacementOperation(await readJson(response));\n}\n\n/** Poll an operation until it is terminal, gone, or `timeoutMs` elapses. */\nexport function pollDropletReplacement(\n client: MistClient,\n input: {\n mistId: string;\n installationUuid: string;\n operationId: string;\n timeoutMs: number;\n sleep?: (durationMs: number) => Promise<void>;\n },\n): Promise<LifecyclePollResult> {\n const statusPath = replacementOperationStatusPath(\n input.mistId,\n input.installationUuid,\n input.operationId,\n );\n return pollLifecycleOperation({\n timeoutMs: input.timeoutMs,\n ...(input.sleep ? { sleep: input.sleep } : {}),\n fetchStatus: async () => {\n const response = await send(client, statusPath, { method: \"GET\" });\n const retryAfterSeconds = parseRetryAfterHeader(\n response.headers.get(\"Retry-After\"),\n );\n if (response.status === 404 || response.status === 429) {\n return { status: response.status, retryAfterSeconds };\n }\n if (!response.ok) return { status: response.status };\n return {\n status: response.status,\n operation: parseReplacementOperation(await readJson(response)),\n retryAfterSeconds,\n };\n },\n });\n}\n\nasync function send(\n client: MistClient,\n path: string,\n init: RequestInit,\n): Promise<Response> {\n try {\n return await client.request(path, init);\n } catch (err) {\n throw new MistApiError(\n `Network error: ${err instanceof Error ? err.message : String(err)}`,\n 0,\n );\n }\n}\n\nasync function readJson(response: Response): Promise<unknown> {\n const text = await response.text();\n if (!text) return null;\n try {\n return JSON.parse(text) as unknown;\n } catch {\n return null;\n }\n}\n","/**\n * Plan receipts for `fluid mist droplet reinstall`.\n *\n * Mist Desktop's agent must show a dry-run plan and get explicit approval\n * before it may confirm the irreversible replacement. A CLI has no\n * conversation to hold that sequence, so `--dry-run` records a receipt of\n * the exact Droplet, installation and lifecycle URLs it showed, and\n * `--confirm` refuses unless a receipt for the same plan is younger than\n * {@link REINSTALL_PLAN_TTL_MS}. `--confirm` claims the receipt atomically\n * (a rename only one process can win) before checking it, so one plan\n * approves at most one attempt and every retry needs a fresh plan.\n *\n * Receipts live under the Fluid config dir (`~/.fluid/mist/reinstall-plans`)\n * and hold only ids and a timestamp — no tokens or secrets.\n */\n\nimport { randomUUID } from \"node:crypto\";\nimport {\n mkdirSync,\n readFileSync,\n renameSync,\n rmSync,\n writeFileSync,\n} from \"node:fs\";\nimport { join } from \"node:path\";\nimport { getConfigDir } from \"@fluid-app/fluid-cli\";\n\n/** How long a shown plan stays valid for `--confirm`. */\nexport const REINSTALL_PLAN_TTL_MS = 15 * 60 * 1_000;\n\nexport interface ReinstallPlanReceipt {\n mistId: string;\n dropletUuid: string;\n installationUuid: string;\n /** The lifecycle URLs the plan showed; `--confirm` will PATCH these. */\n embedUrl: string;\n installWebhookUrl: string;\n uninstallWebhookUrl: string;\n /** ISO-8601 time the plan was shown. */\n plannedAt: string;\n}\n\nexport interface ReinstallPlanStore {\n save(receipt: ReinstallPlanReceipt): void;\n load(mistId: string): ReinstallPlanReceipt | null;\n /** Atomically remove and return the receipt; null when none (or when a\n * concurrent claim won). */\n claim(mistId: string): ReinstallPlanReceipt | null;\n}\n\nexport function createFileReinstallPlanStore(\n dir: string = join(getConfigDir(), \"mist\", \"reinstall-plans\"),\n): ReinstallPlanStore {\n const fileFor = (mistId: string) =>\n join(dir, `${encodeURIComponent(mistId)}.json`);\n return {\n save(receipt) {\n mkdirSync(dir, { recursive: true, mode: 0o700 });\n writeFileSync(\n fileFor(receipt.mistId),\n JSON.stringify(receipt, null, 2) + \"\\n\",\n { encoding: \"utf8\", mode: 0o600 },\n );\n },\n load(mistId) {\n try {\n const parsed = JSON.parse(\n readFileSync(fileFor(mistId), \"utf8\"),\n ) as unknown;\n return isReceipt(parsed) && parsed.mistId === mistId ? parsed : null;\n } catch {\n return null;\n }\n },\n claim(mistId) {\n const claimed = `${fileFor(mistId)}.claimed-${process.pid}-${randomUUID()}`;\n try {\n renameSync(fileFor(mistId), claimed);\n } catch {\n return null;\n }\n try {\n const parsed = JSON.parse(readFileSync(claimed, \"utf8\")) as unknown;\n return isReceipt(parsed) && parsed.mistId === mistId ? parsed : null;\n } catch {\n return null;\n } finally {\n rmSync(claimed, { force: true });\n }\n },\n };\n}\n\nexport type ReceiptCheck =\n | { ok: true }\n | { ok: false; code: \"plan_required\" | \"plan_stale\" | \"plan_changed\" };\n\n/** Whether `receipt` approves executing the freshly computed plan. */\nexport function checkPlanReceipt(\n receipt: ReinstallPlanReceipt | null,\n fresh: Omit<ReinstallPlanReceipt, \"mistId\" | \"plannedAt\">,\n now: number,\n): ReceiptCheck {\n if (!receipt) return { ok: false, code: \"plan_required\" };\n const plannedAt = Date.parse(receipt.plannedAt);\n if (!Number.isFinite(plannedAt) || now - plannedAt > REINSTALL_PLAN_TTL_MS) {\n return { ok: false, code: \"plan_stale\" };\n }\n if (plannedAt > now) return { ok: false, code: \"plan_stale\" };\n if (\n receipt.dropletUuid !== fresh.dropletUuid ||\n receipt.installationUuid !== fresh.installationUuid ||\n receipt.embedUrl !== fresh.embedUrl ||\n receipt.installWebhookUrl !== fresh.installWebhookUrl ||\n receipt.uninstallWebhookUrl !== fresh.uninstallWebhookUrl\n ) {\n return { ok: false, code: \"plan_changed\" };\n }\n return { ok: true };\n}\n\nfunction isReceipt(value: unknown): value is ReinstallPlanReceipt {\n if (typeof value !== \"object\" || value === null) return false;\n const record = value as Record<string, unknown>;\n return (\n typeof record[\"mistId\"] === \"string\" &&\n typeof record[\"dropletUuid\"] === \"string\" &&\n typeof record[\"installationUuid\"] === \"string\" &&\n typeof record[\"embedUrl\"] === \"string\" &&\n typeof record[\"installWebhookUrl\"] === \"string\" &&\n typeof record[\"uninstallWebhookUrl\"] === \"string\" &&\n typeof record[\"plannedAt\"] === \"string\"\n );\n}\n","/**\n * Orchestration for `fluid mist droplet repair` and\n * `fluid mist droplet reinstall`, ported from Mist Desktop's\n * `repair_droplet_lifecycle_webhooks` and `repair_and_reinstall_droplet`\n * tools (`apps/mist-desktop/src/main/tools/index.ts` in the fluid repo).\n *\n * The planning and safety rules are shared through `@fluid-app/mist-core`\n * (`droplet-lifecycle-repair`, `droplet-replacement`); this module only\n * wires them to the CLI's API client, the `.mist` marker and the plan\n * receipt store. Every function takes its client and collaborators as\n * arguments so tests drive it with fakes.\n */\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport {\n canonicalDropletUrls,\n dropletLifecyclePathsFromCapabilities,\n dropletRepairPatch,\n dropletUrlsMatch,\n normalizePublicUrl,\n planLifecycleRepair,\n DEFAULT_DROPLET_LIFECYCLE_PATHS,\n type CanonicalDropletUrls,\n type DropletLifecyclePaths,\n type LifecycleRepairSummary,\n} from \"@fluid-app/mist-core/droplet-lifecycle-repair\";\nimport {\n buildReplacementPlan,\n checkReplacementRequest,\n replacementStartNeedsConfirmation,\n selectReplacementInstallation,\n summarizeReplacementWatch,\n REPLACEMENT_POLL_BUDGET_MS,\n} from \"@fluid-app/mist-core/droplet-replacement\";\nimport type { LifecycleOperation } from \"@fluid-app/mist-core/lifecycle-types\";\nimport { MistApiError, type MistClient } from \"../api/client.js\";\nimport {\n findDropletByEmbedUrl,\n findDropletUuidByNumericId,\n getDroplet,\n} from \"../api/droplets.js\";\nimport { listAllDropletInstallations } from \"../api/droplet_installations.js\";\nimport {\n pollDropletReplacement,\n startDropletReplacement,\n} from \"../api/droplet_replacements.js\";\nimport { resolveMistId } from \"../api/id-resolver.js\";\nimport {\n findDropletIntegrationPoint,\n listIntegrationPoints,\n} from \"../api/integration_points.js\";\nimport { showMist } from \"../api/mists.js\";\nimport type { DropletRecord, Mist } from \"../types.js\";\nimport {\n checkPlanReceipt,\n REINSTALL_PLAN_TTL_MS,\n type ReinstallPlanStore,\n} from \"./reinstall-plan-store.js\";\nimport { readMistFileFrom, resolveSlug } from \"./slug-resolver.js\";\n\n/** A refusal: the command stops before (or instead of) mutating anything\n * and exits non-zero. `code` is stable for scripts. */\nexport class DropletRepairRefusal extends Error {\n readonly code: string;\n constructor(code: string, message: string) {\n super(message);\n this.name = \"DropletRepairRefusal\";\n this.code = code;\n }\n}\n\nexport interface RepairTarget {\n mistId: string;\n mistSlug: string;\n dropletUuid: string;\n canonical: CanonicalDropletUrls;\n /** Where the install/uninstall paths came from. Only the `.mist` project\n * in the current directory can supply a `fluid.capabilities.json`. */\n lifecyclePathsSource: \"fluid.capabilities.json\" | \"defaults\";\n}\n\n/**\n * Resolve the Mist (slug argument or the `.mist` in `cwd`), its public URL,\n * the Droplet it is attached to, and the canonical Droplet URLs. Refuses\n * when the Mist has no public URL or no attached Droplet.\n */\nexport async function resolveRepairTarget(\n client: MistClient,\n input: { slug?: string; cwd: string },\n): Promise<RepairTarget> {\n const slug = resolveSlug(input.slug, input.cwd);\n const marker = readMistFileFrom(input.cwd);\n // The `.mist` here describes the project only when no other slug was\n // named; its capabilities must not leak onto another Mist.\n const isCwdProject = marker !== null && marker.slug === slug;\n\n const mistId = await resolveMistId(client, slug);\n const { mist } = await showMist(client, mistId);\n // Only the server's public_url is authoritative for a Droplet PATCH; the\n // local `.mist` marker's copy is never used to choose the target host.\n const publicUrl = normalizePublicUrl(mist.public_url);\n if (!publicUrl) {\n throw new DropletRepairRefusal(\n \"no_public_url\",\n \"Mist has no public_url yet. Publish the Mist first (`fluid mist push`), then repair lifecycle webhooks.\",\n );\n }\n\n const dropletUuid = await resolveAttachedDropletUuid(\n client,\n mist,\n mistId,\n publicUrl,\n );\n const paths = isCwdProject ? readLifecyclePaths(input.cwd) : null;\n return {\n mistId,\n mistSlug: mist.slug ?? slug,\n dropletUuid,\n canonical: canonicalDropletUrls(\n publicUrl,\n paths ?? DEFAULT_DROPLET_LIFECYCLE_PATHS,\n ),\n lifecyclePathsSource: paths ? \"fluid.capabilities.json\" : \"defaults\",\n };\n}\n\n/**\n * The Mist's own Droplet. The server-owned link wins (the Mist record, then\n * its droplet integration point); matching `embed_url` is only a legacy\n * fallback, because a broken embed URL is exactly what the repair fixes.\n */\nexport async function resolveAttachedDropletUuid(\n client: MistClient,\n mist: Mist,\n mistId: string,\n publicUrl: string | null,\n): Promise<string> {\n if (typeof mist.droplet?.uuid === \"string\" && mist.droplet.uuid.length > 0) {\n return mist.droplet.uuid;\n }\n\n try {\n const { integration_points } = await listIntegrationPoints(client, mistId);\n const attached = findDropletIntegrationPoint(integration_points);\n if (attached?.integratable_uuid) return attached.integratable_uuid;\n if (attached?.integratable_id != null) {\n const uuid = await findDropletUuidByNumericId(\n client,\n attached.integratable_id,\n );\n if (uuid) return uuid;\n }\n } catch (err) {\n // Older backends may not expose integration points; fall through to\n // the legacy embed-url lookup rather than making repair unavailable.\n // Network failures are not a backend shortfall.\n if (err instanceof MistApiError && err.status === 0) throw err;\n }\n\n const byEmbedUrl = publicUrl\n ? await findDropletByEmbedUrl(client, publicUrl)\n : null;\n if (byEmbedUrl) {\n if (!byEmbedUrl.uuid) {\n throw new DropletRepairRefusal(\n \"droplet_not_found\",\n `Droplet with embed_url ${publicUrl} was found, but its API response did not include a uuid.`,\n );\n }\n return byEmbedUrl.uuid;\n }\n throw new DropletRepairRefusal(\n \"droplet_not_found\",\n \"This Mist is not attached to a Droplet, and no Droplet with a matching embed_url was found. Attach or recreate the Droplet first (`fluid mist attach-droplet`), then retry.\",\n );\n}\n\n/** Lifecycle paths from `fluid.capabilities.json`, or null when absent or\n * unreadable (the caller then uses the defaults). */\nfunction readLifecyclePaths(cwd: string): DropletLifecyclePaths | null {\n const file = join(cwd, \"fluid.capabilities.json\");\n if (!existsSync(file)) return null;\n try {\n return dropletLifecyclePathsFromCapabilities(\n JSON.parse(readFileSync(file, \"utf8\")),\n );\n } catch {\n return null;\n }\n}\n\nexport interface LifecycleRepairResult extends Omit<\n LifecycleRepairSummary,\n \"status\"\n> {\n status: LifecycleRepairSummary[\"status\"] | \"error\";\n lifecyclePathsSource: RepairTarget[\"lifecyclePathsSource\"];\n verified?: boolean;\n verifyStatus?: number;\n}\n\n/** Read the Droplet and report the planned repair without mutating. */\nexport async function planDropletRepair(\n client: MistClient,\n target: RepairTarget,\n dryRun: boolean,\n): Promise<{ alreadyCorrect: boolean; summary: LifecycleRepairResult }> {\n const { droplet } = await getDroplet(client, target.dropletUuid);\n const { alreadyCorrect, summary } = planLifecycleRepair({\n mistId: target.mistId,\n mistSlug: target.mistSlug,\n dropletUuid: target.dropletUuid,\n canonical: target.canonical,\n current: droplet,\n dryRun,\n });\n return {\n alreadyCorrect,\n summary: { ...summary, lifecyclePathsSource: target.lifecyclePathsSource },\n };\n}\n\n/**\n * PATCH only the embed and lifecycle webhook URLs, then re-read the Droplet\n * to verify. A no-op when they already match.\n */\nexport async function applyDropletRepair(\n client: MistClient,\n target: RepairTarget,\n): Promise<{ summary: LifecycleRepairResult; isError: boolean }> {\n const planned = await planDropletRepair(client, target, false);\n if (planned.alreadyCorrect) {\n return { summary: planned.summary, isError: false };\n }\n\n await client.patch<{ droplet?: DropletRecord }>(\n `/api/droplets/${encodeURIComponent(target.dropletUuid)}`,\n dropletRepairPatch(target.canonical),\n );\n\n let verifyStatus = 200;\n let verified = false;\n try {\n const { droplet } = await getDroplet(client, target.dropletUuid);\n verified = dropletUrlsMatch(droplet, target.canonical);\n } catch (err) {\n verifyStatus = err instanceof MistApiError ? err.status : 0;\n }\n return {\n summary: {\n ...planned.summary,\n status: verified ? \"updated\" : \"error\",\n verified,\n verifyStatus,\n },\n isError: !verified,\n };\n}\n\nexport interface ReinstallOptions {\n dryRun?: boolean;\n confirm?: boolean;\n /** `--yes` is accepted only to refuse it: it never approves a reinstall. */\n yes?: boolean;\n installationUuid?: string;\n resumeOperationId?: string;\n watchTimeoutMs?: number;\n}\n\nexport interface ReinstallDeps {\n client: MistClient;\n cwd: string;\n slug?: string;\n planStore: ReinstallPlanStore;\n now?: () => number;\n newIdempotencyKey: () => string;\n sleep?: (durationMs: number) => Promise<void>;\n}\n\nexport interface ReinstallOutcome {\n output: Record<string, unknown>;\n exitCode: number;\n}\n\nconst PLAN_TTL_MINUTES = REINSTALL_PLAN_TTL_MS / 60_000;\n\n/**\n * `fluid mist droplet reinstall`. Same sequencing as Mist's\n * `repair_and_reinstall_droplet`: plan first, explicit approval, one\n * backend-owned replacement, resumable watching. Refusals return a\n * non-zero exit code; nothing irreversible starts without `--confirm`\n * matching a fresh `--dry-run` plan.\n */\nexport async function runDropletReinstall(\n deps: ReinstallDeps,\n opts: ReinstallOptions,\n): Promise<ReinstallOutcome> {\n const now = deps.now ?? Date.now;\n const refusal = (code: string, message: string): ReinstallOutcome => ({\n output: { status: \"refused\", code, message },\n exitCode: 1,\n });\n\n if (opts.dryRun && opts.confirm) {\n return refusal(\n \"conflicting_flags\",\n \"Pass either --dry-run (show the plan) or --confirm (execute it), not both.\",\n );\n }\n if (opts.dryRun && opts.resumeOperationId?.trim()) {\n return refusal(\n \"conflicting_flags\",\n \"--resume keeps watching a replacement that already started; it does not take --dry-run. Run it with --installation only.\",\n );\n }\n const request = {\n dryRun: opts.dryRun === true,\n confirmDestructiveReinstall: opts.confirm === true,\n installationUuid: opts.installationUuid?.trim() || undefined,\n resumeOperationId: opts.resumeOperationId,\n };\n const requestCheck = checkReplacementRequest(request);\n if (!requestCheck.ok) {\n return refusal(\n requestCheck.code,\n '--resume also needs --installation with the installationUuid from that same \"still_running\" result. Replacement status is scoped to the installation the operation started on, so resuming without it would watch the wrong one.',\n );\n }\n const { resumeOperationId } = requestCheck;\n if (\n !request.dryRun &&\n replacementStartNeedsConfirmation(request, resumeOperationId)\n ) {\n return refusal(\n \"confirmation_required\",\n opts.yes\n ? \"--yes does not approve an irreversible reinstall. Run with --dry-run, review the exact dropletUuid and installationUuid, then rerun with --confirm.\"\n : \"Refused to start an irreversible replacement without explicit confirmation. Run with --dry-run, review the exact dropletUuid and installationUuid, then rerun with --confirm.\",\n );\n }\n\n try {\n const target = await resolveRepairTarget(deps.client, {\n slug: deps.slug,\n cwd: deps.cwd,\n });\n // Read-only until the start is confirmed and the plan receipt checks out.\n const plannedRepair = await planDropletRepair(deps.client, target, true);\n\n const installations = await listAllDropletInstallations(deps.client);\n const installation = selectReplacementInstallation({\n installations,\n dropletUuid: target.dropletUuid,\n installationUuid: request.installationUuid,\n resumeOperationId,\n });\n if (!installation?.uuid) {\n return refusal(\n \"installation_not_found\",\n `No active installation for attached Droplet ${target.dropletUuid} was found. If a previous approved replacement terminalized the source, rerun with that plan's --installation <uuid>; otherwise install the Droplet once from Fluid first.`,\n );\n }\n const installationUuid = installation.uuid;\n const idempotencyKey = deps.newIdempotencyKey();\n const planInput = {\n dryRun: request.dryRun,\n resumeOperationId,\n mistId: target.mistId,\n dropletUuid: target.dropletUuid,\n installation: { ...installation, uuid: installationUuid },\n idempotencyKey,\n };\n\n if (request.dryRun) {\n deps.planStore.save({\n mistId: target.mistId,\n dropletUuid: target.dropletUuid,\n installationUuid,\n ...target.canonical,\n plannedAt: new Date(now()).toISOString(),\n });\n const confirmCommand = reinstallCommand(target.mistSlug, [\n \"--confirm\",\n ...(request.installationUuid\n ? [\"--installation\", installationUuid]\n : []),\n ]);\n return {\n output: {\n ...buildReplacementPlan({\n ...planInput,\n lifecycleRepair: plannedRepair.summary,\n }),\n nextStep: `Review the dropletUuid and installationUuid above. This replacement is irreversible. To approve it, run \\`${confirmCommand}\\` within ${PLAN_TTL_MINUTES} minutes.`,\n },\n exitCode: 0,\n };\n }\n\n let lifecycleRepair: LifecycleRepairResult = plannedRepair.summary;\n let startedOperation: LifecycleOperation | null = null;\n if (!resumeOperationId) {\n // Claiming removes the receipt atomically, so one plan approves one\n // attempt even when two --confirm runs race; any refusal or failure\n // from here on needs a fresh --dry-run.\n const receiptCheck = checkPlanReceipt(\n deps.planStore.claim(target.mistId),\n {\n dropletUuid: target.dropletUuid,\n installationUuid,\n ...target.canonical,\n },\n now(),\n );\n if (!receiptCheck.ok) {\n return refusal(\n receiptCheck.code,\n planReceiptMessage(receiptCheck.code),\n );\n }\n\n const repaired = await applyDropletRepair(deps.client, target);\n lifecycleRepair = repaired.summary;\n if (repaired.isError) {\n return {\n output: {\n status: \"error\",\n code: \"lifecycle_repair_failed\",\n message:\n \"The Droplet lifecycle URLs did not verify after the repair, so no replacement was started.\",\n lifecycleRepair,\n },\n exitCode: 1,\n };\n }\n\n startedOperation = await startDropletReplacement(deps.client, {\n mistId: target.mistId,\n installationUuid,\n idempotencyKey,\n });\n }\n\n const plan = buildReplacementPlan({ ...planInput, lifecycleRepair });\n const operationId = resumeOperationId ?? startedOperation?.id;\n if (!operationId) {\n return {\n output: {\n ...plan,\n status: \"error\",\n code: \"missing_operation_id\",\n message:\n \"Fluid accepted the replacement but did not return an operation id to watch.\",\n },\n exitCode: 1,\n };\n }\n\n const watchedForMs = opts.watchTimeoutMs ?? REPLACEMENT_POLL_BUDGET_MS;\n const pollResult = await pollDropletReplacement(deps.client, {\n mistId: target.mistId,\n installationUuid,\n operationId,\n timeoutMs: watchedForMs,\n ...(deps.sleep ? { sleep: deps.sleep } : {}),\n });\n const watch = summarizeReplacementWatch({\n plan,\n pollResult,\n startedOperation,\n operationId,\n installationUuid,\n watchedForMs,\n nextStep: ({ operationId: id, installationUuid: source, stage }) =>\n `Fluid is still working on this replacement: it is not finished and it did not fail. Current stage: ${stage}. Keep watching with \\`${reinstallCommand(target.mistSlug, [\"--resume\", id, \"--installation\", source])}\\`. The reinstall is complete only when status is \"succeeded\".`,\n });\n return { output: { ...watch.result }, exitCode: watch.isError ? 1 : 0 };\n } catch (err) {\n if (err instanceof DropletRepairRefusal)\n return refusal(err.code, err.message);\n throw err;\n }\n}\n\nfunction reinstallCommand(slug: string, flags: string[]): string {\n return [\"fluid mist droplet reinstall\", slug, ...flags].join(\" \");\n}\n\nfunction planReceiptMessage(\n code: \"plan_required\" | \"plan_stale\" | \"plan_changed\",\n): string {\n switch (code) {\n case \"plan_required\":\n return \"Refused: no reinstall plan has been shown for this Mist. Run with --dry-run first, review the plan, then rerun with --confirm.\";\n case \"plan_stale\":\n return `Refused: the reinstall plan is older than ${PLAN_TTL_MINUTES} minutes. Run with --dry-run again, review the plan, then rerun with --confirm.`;\n case \"plan_changed\":\n return \"Refused: the Droplet, installation or lifecycle URLs changed since the plan was shown. Run with --dry-run again, review the new plan, then rerun with --confirm.\";\n }\n}\n","/**\n * Ask Fluid for, or withdraw, egress through its egress proxy for the Mist's\n * attached Droplet (`fluid mist droplet egress-proxy`). The owner's ask is the\n * droplet's `egress_proxy_requested`, and the proxy admits the droplet on it\n * alone. A Fluid root admin's grant (`egress_proxy_enabled`) is now only a\n * record; withdrawing the request also clears it.\n */\n\nimport { MistApiError, type MistClient } from \"../api/client.js\";\nimport { getDroplet, updateDroplet } from \"../api/droplets.js\";\nimport { resolveMistId } from \"../api/id-resolver.js\";\nimport { showMist } from \"../api/mists.js\";\nimport { resolveAttachedDropletUuid } from \"./droplet-repair.js\";\nimport { resolveSlug } from \"./slug-resolver.js\";\n\n/** Why a droplet response can lack the egress fields. Fluid returns them only\n * to company admins (and root admins), and older Fluids not at all. */\nexport const EGRESS_NOT_RETURNED =\n \"Fluid did not return the droplet's egress fields: only a company admin can see or ask for egress, or this Fluid predates it.\";\n\n/** Fluid refused the egress request outright: it accepts one only from a\n * company admin (401), and only for their own company's droplets (403).\n * Distinct from an older Fluid, which ignores the field and omits it from\n * the response. */\nexport const EGRESS_REFUSED =\n \"Fluid refused the egress request: only a company admin can ask for egress, for their own company's droplets. The droplet was set up without it.\";\n\n/** Whether an API error is Fluid refusing the egress request: a 401 or 403\n * whose `errors` names `egress_proxy_requested`. Matched exactly, so a real\n * authentication failure is reported as one rather than as a refusal. */\nexport function isEgressRefusal(error: unknown): boolean {\n return (\n error instanceof MistApiError &&\n (error.status === 401 || error.status === 403) &&\n error.fieldErrors?.egress_proxy_requested !== undefined\n );\n}\n\n/** Fluid's own words for an egress refusal, falling back to EGRESS_REFUSED. */\nfunction egressRefusalMessage(error: MistApiError): string {\n const reasons = error.fieldErrors?.egress_proxy_requested;\n const reason = Array.isArray(reasons) ? reasons[0] : reasons;\n return typeof reason === \"string\"\n ? `Fluid refused the egress request: ${reason}.`\n : EGRESS_REFUSED;\n}\n\nexport interface DropletEgressProxyResult {\n mistSlug: string;\n dropletUuid: string;\n /** The owner's request as the API reports it after this run. The proxy\n * admits the droplet on it alone. */\n requested: boolean;\n /** True when this run wrote the request (it was not already as asked). */\n changed: boolean;\n}\n\n/** Fluid refused to turn egress on for the droplet. Still an API error, so\n * it keeps the response's status and the 4xx exit code; only the message\n * is Fluid's reason instead of a bare status. */\nexport class EgressRequestRefusedError extends MistApiError {\n constructor(message: string, refused: MistApiError) {\n super(message, refused.status, refused.details, refused.fieldErrors);\n this.name = \"EgressRequestRefusedError\";\n }\n}\n\n/** The API answered without the egress fields. */\nexport class EgressProxyUnsupportedError extends Error {\n constructor() {\n super(EGRESS_NOT_RETURNED);\n this.name = \"EgressProxyUnsupportedError\";\n }\n}\n\n/**\n * Set the attached droplet's egress request. Reads it first so an\n * already-correct droplet is left untouched, then PATCHes and trusts only\n * the values the API returns.\n */\nexport async function setDropletEgressProxy(\n client: MistClient,\n input: { slug?: string; cwd: string; requested: boolean },\n): Promise<DropletEgressProxyResult> {\n const slug = resolveSlug(input.slug, input.cwd);\n const mistId = await resolveMistId(client, slug);\n const { mist } = await showMist(client, mistId);\n const dropletUuid = await resolveAttachedDropletUuid(\n client,\n mist,\n mistId,\n mist.public_url ?? null,\n );\n const mistSlug = mist.slug ?? slug;\n\n const current = (await getDroplet(client, dropletUuid)).droplet;\n if (current.egress_proxy_requested === undefined) {\n throw new EgressProxyUnsupportedError();\n }\n // Already as asked, except that withdrawing must also clear a grant a\n // Fluid admin gave without a request: the write withdraws both.\n const grantLeftOn = !input.requested && current.egress_proxy_enabled === true;\n if (current.egress_proxy_requested === input.requested && !grantLeftOn) {\n return {\n mistSlug,\n dropletUuid,\n requested: current.egress_proxy_requested,\n changed: false,\n };\n }\n\n const updated = (\n await updateDroplet(client, dropletUuid, {\n egress_proxy_requested: input.requested,\n }).catch((error: unknown) => {\n if (error instanceof MistApiError && isEgressRefusal(error)) {\n throw new EgressRequestRefusedError(egressRefusalMessage(error), error);\n }\n throw error;\n })\n ).droplet;\n if (updated.egress_proxy_requested === undefined) {\n throw new EgressProxyUnsupportedError();\n }\n return {\n mistSlug,\n dropletUuid,\n requested: updated.egress_proxy_requested,\n changed: true,\n };\n}\n","/**\n * Surface provisioning engine — the one place the \"create a Fluid surface and\n * wire it to a mist\" flow lives, at parity with Mist Desktop's\n * mist-integration-points-api.service.ts. Exposed via `fluid mist create`\n * (create a bare mist first, then run this), `fluid mist attach` (any kind),\n * and `fluid mist attach-droplet` (droplet shortcut).\n *\n * Three surface kinds, each with its own create endpoint + attach identifier:\n * - droplet → POST /api/droplets → drp_ uuid → integratable_uuid\n * - drop-zone → POST /api/drop_zones → dz_ uuid → integratable_uuid\n * - mobile-embed→ POST /api/company/mobile_widgets→ numeric id → integratable_id\n *\n * Design tenets (repo philosophy — \"server is source of truth\"):\n * - No local progress file. State is re-derived from the API each run.\n * - Attach-existing is idempotent: if an integration point already links the\n * target surface (matched via the #19689 integratable_uuid echo, or the\n * numeric id for mobile), skip. A droplet is 1-per-mist, so creating a new\n * droplet also skips when one is already attached. Drop-zones/mobile\n * embeds are additive (a mist can have many).\n * - Only a droplet attach provisions managed env keys\n * (FLUID_DROPLET_UUID/_SECRET/_WEBHOOK_AUTH_TOKEN); this flow only\n * VERIFIES they landed — it never writes them (the API 422s on write).\n * - A droplet must also be INSTALLED (POST /api/droplet_installations) —\n * create + link alone leave it not-connected/inactive, so install-scoped\n * features (dit/dex exchange, admin embeds, install webhooks) don't work.\n * The install step is idempotent (skips when an installation exists).\n */\n\nimport { showMist } from \"../api/mists.js\";\nimport {\n createIntegrationPoint,\n findDropletIntegrationPoint,\n findIntegrationPointBySurface,\n listIntegrationPoints,\n} from \"../api/integration_points.js\";\nimport {\n buildDropletCreateInput,\n createDroplet,\n DEFAULT_DROPLET_SCOPES,\n findDropletByEmbedUrl,\n findDropletUuidByNumericId,\n getDroplet,\n updateDroplet,\n} from \"../api/droplets.js\";\nimport {\n createDropletInstallation,\n findInstallationForDroplet,\n listAllDropletInstallations,\n} from \"../api/droplet_installations.js\";\nimport { buildDropZoneCreateInput, createDropZone } from \"../api/drop_zones.js\";\nimport {\n buildMobileWidgetCreateInput,\n createMobileWidget,\n} from \"../api/mobile_widgets.js\";\nimport { listEnvVars } from \"../api/env_vars.js\";\nimport { MistApiError, type MistClient } from \"../api/client.js\";\nimport {\n MANAGED_DROPLET_ENV_KEYS,\n type DropletRecord,\n type IntegrationPointKind,\n type MistState,\n} from \"../types.js\";\nimport {\n EGRESS_NOT_RETURNED,\n EGRESS_REFUSED,\n isEgressRefusal,\n} from \"./droplet-egress.js\";\nimport type { StepReporter } from \"./step-reporter.js\";\n\nexport type SurfaceKind = IntegrationPointKind;\n\nexport interface ProvisionTiming {\n publicUrlTimeoutMs: number;\n publicUrlIntervalMs: number;\n envTimeoutMs: number;\n envIntervalMs: number;\n}\n\nconst DEFAULT_TIMING: ProvisionTiming = {\n publicUrlTimeoutMs: 90_000,\n publicUrlIntervalMs: 3_000,\n envTimeoutMs: 120_000,\n envIntervalMs: 5_000,\n};\n\nexport interface SurfaceProvisionOptions {\n /** Mist slug or UUID to attach to. Re-queried each run. */\n slugOrId: string;\n kind: SurfaceKind;\n /** Surface display name (create path). Defaults to the mist's name. */\n name?: string;\n /** droplet: requested scopes (defaults to the full set). */\n requestedScopes?: readonly string[];\n /**\n * droplet: ask Fluid for egress through its egress proxy (e.g. to reach\n * Exigo); a company admin's request admits the droplet. Asked on create,\n * and of an already-existing droplet too.\n */\n egressProxy?: boolean;\n /** drop-zone: slot id `page__zone` (e.g. checkout__below_total). */\n zone?: string;\n /** drop-zone + mobile: embed height in px. */\n heightPx?: number;\n /** mobile: cover image url (required by the API; a default is used if omitted). */\n coverImageUrl?: string;\n /** Attach an EXISTING droplet/drop-zone by uuid instead of creating one. */\n existingUuid?: string;\n /** Attach an EXISTING mobile widget by numeric id instead of creating one. */\n existingId?: number;\n /** Verify managed env keys after a droplet attach (default true). */\n verifyEnv?: boolean;\n reporter: StepReporter;\n timing?: Partial<ProvisionTiming>;\n /** Injectable sleep (tests pass a no-op). */\n sleep?: (ms: number) => Promise<void>;\n}\n\nexport interface SurfaceProvisionResult {\n mist: {\n id: string;\n slug: string;\n name: string | null;\n public_url: string | null;\n state: MistState;\n };\n kind: SurfaceKind;\n surface: { uuid: string | null; id: number | null; name: string | null };\n integrationPoint: {\n id: string;\n integratableUuid: string | null;\n created: boolean;\n };\n surfaceCreated: boolean;\n /** True only when THIS run set/backfilled the surface embed URL. */\n embedUrlSet: boolean;\n /**\n * Droplet only; null for other kinds. The company-level installation that\n * actually CONNECTS the droplet (create + link alone leave it inactive).\n * `created` is true when THIS run installed it.\n */\n installation: { uuid: string | null; created: boolean } | null;\n /** Droplet only; null for other kinds (they don't provision managed env). */\n managedEnv: { ready: boolean; present: string[]; missing: string[] } | null;\n /**\n * Droplet only, and only when `egressProxy` was asked for; null otherwise.\n * `requested` is what the API reported back, and the proxy admits the\n * droplet on it alone; false means the request did not take (the caller is\n * not a company admin, or this Fluid predates it).\n */\n egressProxy: {\n requested: boolean;\n changed: boolean;\n } | null;\n}\n\nconst defaultSleep = (ms: number): Promise<void> =>\n new Promise((resolve) => setTimeout(resolve, ms));\n\nconst KIND_LABEL: Record<SurfaceKind, string> = {\n droplet: \"droplet\",\n \"drop-zone\": \"drop zone\",\n \"mobile-embed\": \"mobile embed\",\n};\n\n/**\n * Create (or reuse) a surface of `kind` and wire it to the mist. Idempotent\n * for droplet + attach-existing; additive for new drop-zones/mobile embeds.\n */\nexport async function provisionSurface(\n client: MistClient,\n options: SurfaceProvisionOptions,\n): Promise<SurfaceProvisionResult> {\n const { reporter, slugOrId, kind } = options;\n const timing = { ...DEFAULT_TIMING, ...options.timing };\n const sleep = options.sleep ?? defaultSleep;\n const label = KIND_LABEL[kind];\n\n // Step 1 — resolve the mist (re-query; server is source of truth).\n reporter.start(\"Resolving mist…\");\n const mist = (await showMist(client, slugOrId)).mist;\n reporter.succeed(`Mist ${mist.slug} (${mist.state})`);\n\n // Step 2 — inspect existing integrations (carries the integratable_uuid echo).\n reporter.start(\"Checking existing integrations…\");\n const points = (await listIntegrationPoints(client, slugOrId))\n .integration_points;\n\n let surfaceUuid: string | null = null;\n let surfaceNumericId: number | null = null;\n let surfaceName: string | null = null;\n let surfaceCreated = false;\n let embedUrlSet = false;\n // The droplet THIS run created, when it did: its response says whether the\n // egress request took, so no extra PATCH is needed for it.\n let createdEgressProxy: DropletRecord | undefined;\n let integrationPoint: SurfaceProvisionResult[\"integrationPoint\"] | undefined;\n\n const attachingExisting =\n options.existingUuid != null || options.existingId != null;\n\n // Detect an already-present integration point we can reuse (idempotency).\n const alreadyLinked = attachingExisting\n ? findIntegrationPointBySurface(points, {\n kind,\n uuid: options.existingUuid,\n id: options.existingId,\n })\n : kind === \"droplet\"\n ? findDropletIntegrationPoint(points)\n : undefined;\n\n if (alreadyLinked) {\n reporter.skip(\n `${label} already attached (integration #${alreadyLinked.id}) — skipping`,\n );\n surfaceUuid =\n alreadyLinked.integratable_uuid ?? options.existingUuid ?? null;\n surfaceNumericId =\n alreadyLinked.integratable_id ?? options.existingId ?? null;\n integrationPoint = {\n id: alreadyLinked.id,\n integratableUuid: alreadyLinked.integratable_uuid ?? null,\n created: false,\n };\n } else {\n reporter.succeed(\"No matching integration yet\");\n\n // Resolve what to attach by: an existing surface, or a freshly-created one.\n let attachUuid: string | undefined = options.existingUuid;\n let attachId: number | undefined = options.existingId;\n\n if (!attachingExisting) {\n const embedUrl = await resolvePublicUrl(client, {\n slugOrId,\n initial: mist.public_url,\n reporter,\n timing,\n sleep,\n });\n if (!embedUrl) {\n throw new Error(\n \"The mist has no public_url yet — wait for the first deploy before creating a surface.\",\n );\n }\n // `vendor_name` is frequently null in production — without this final\n // `mist.slug` fallback a nameless mist would send `name: null` in the\n // surface-create payload below.\n const name =\n options.name?.trim() ||\n mist.name?.trim() ||\n mist.vendor_name ||\n mist.slug;\n if (kind === \"droplet\") {\n reporter.start(\"Checking for an interrupted Droplet create…\");\n const recovered = await findDropletByEmbedUrl(client, embedUrl);\n if (recovered) {\n surfaceUuid = recovered.uuid;\n surfaceNumericId = recovered.id ?? null;\n surfaceName = recovered.name;\n attachUuid = recovered.uuid;\n embedUrlSet = true;\n reporter.skip(\n `Reusing droplet ${surfaceUuid} from an interrupted attach`,\n );\n } else {\n reporter.start(`Creating ${label} \"${name}\"…`);\n const createInput = (egressProxy: boolean | undefined) =>\n buildDropletCreateInput({\n name,\n embedUrl,\n requestedScopes: options.requestedScopes,\n egressProxy,\n });\n let created;\n try {\n created = await createDroplet(client, {\n droplet: createInput(options.egressProxy),\n });\n } catch (err) {\n // Fluid refuses the optional egress request from anyone but a\n // company admin (401). Create the droplet without it; the egress\n // step below reports the refusal instead of aborting provisioning.\n if (!(options.egressProxy && isEgressRefusal(err))) throw err;\n created = await createDroplet(client, {\n droplet: createInput(false),\n });\n }\n createdEgressProxy = created.droplet;\n surfaceUuid = created.droplet.uuid;\n surfaceNumericId = created.droplet.id ?? null;\n surfaceName = created.droplet.name;\n attachUuid = created.droplet.uuid;\n embedUrlSet = true;\n surfaceCreated = true;\n reporter.succeed(`Created droplet ${surfaceUuid}`);\n }\n } else if (kind === \"drop-zone\") {\n reporter.start(`Creating ${label} \"${name}\"…`);\n const created = await createDropZone(client, {\n drop_zone: buildDropZoneCreateInput({\n name,\n embedUrl,\n zone: options.zone,\n heightPx: options.heightPx,\n }),\n });\n surfaceUuid = created.drop_zone.uuid;\n surfaceName = created.drop_zone.name;\n attachUuid = created.drop_zone.uuid;\n embedUrlSet = true;\n surfaceCreated = true;\n reporter.succeed(`Created drop zone ${surfaceUuid}`);\n } else {\n // mobile-embed\n reporter.start(`Creating ${label} \"${name}\"…`);\n const created = await createMobileWidget(client, {\n mobile_widget: buildMobileWidgetCreateInput({\n name,\n embedUrl,\n coverImageUrl: options.coverImageUrl,\n heightPx: options.heightPx,\n }),\n });\n surfaceNumericId = created.mobile_widget.id;\n surfaceName = created.mobile_widget.name;\n attachId = created.mobile_widget.id;\n embedUrlSet = true;\n surfaceCreated = true;\n reporter.succeed(`Created mobile embed #${surfaceNumericId}`);\n }\n } else {\n // Attaching existing — nothing new re: embed URL; the caller owns it.\n surfaceUuid = options.existingUuid ?? null;\n surfaceNumericId = options.existingId ?? null;\n embedUrlSet = true;\n }\n\n // Step 3 — attach via integration point. droplet/drop-zone by uuid;\n // mobile-embed by numeric id.\n const attachInput =\n kind === \"mobile-embed\"\n ? { kind, integratable_id: attachId! }\n : { kind, integratable_uuid: attachUuid! };\n reporter.start(`Attaching ${label}…`);\n let ipResp;\n try {\n ipResp = await createIntegrationPoint(client, slugOrId, attachInput);\n } catch (err) {\n reporter.fail(\"Attach failed\");\n throw normalizeAttachError(err, attachUuid ?? String(attachId), kind);\n }\n integrationPoint = {\n id: ipResp.integration_point.id,\n integratableUuid: ipResp.integration_point.integratable_uuid ?? null,\n created: true,\n };\n // Carry the uuid we resolved so callers can display it even if the echo lags.\n if (!surfaceUuid) {\n surfaceUuid =\n ipResp.integration_point.integratable_uuid ?? attachUuid ?? null;\n }\n if (surfaceNumericId == null) {\n surfaceNumericId = ipResp.integration_point.integratable_id ?? null;\n }\n reporter.succeed(\n kind === \"droplet\"\n ? \"Droplet attached — provisioning env + redeploy…\"\n : `${label} attached`,\n );\n }\n\n // Step 4 — droplet only: install it for the company. Create + link alone\n // leave the droplet not-connected/inactive (the admin shows the Install\n // screen, `?dri=` never resolves, install webhooks don't fire) — the\n // installation is what connects it. Runs on the already-linked path too so\n // an interrupted run can finish the job on re-run.\n let installation: SurfaceProvisionResult[\"installation\"] = null;\n let egressProxy: SurfaceProvisionResult[\"egressProxy\"] = null;\n if (kind === \"droplet\") {\n if (!surfaceUuid && surfaceNumericId != null) {\n // Legacy integration points can omit `integratable_uuid` — recover it\n // by paging the company's droplets list and matching the numeric id\n // (the show endpoint resolves by uuid only), so older linked-but-\n // uninstalled droplets still get installed on re-run.\n try {\n surfaceUuid = await findDropletUuidByNumericId(\n client,\n surfaceNumericId,\n );\n } catch {\n // Recovery is best-effort; fall through to the hard error below.\n }\n }\n if (!surfaceUuid) {\n // Do NOT return success here — a linked-but-uninstalled droplet is\n // exactly the inactive state this flow exists to repair.\n reporter.fail(\"Droplet uuid unknown — cannot install\");\n throw new Error(\n \"Couldn't determine the droplet uuid, so the droplet is linked but NOT installed — \" +\n \"it stays inactive until an installation exists. Re-run with \" +\n \"`--existing <droplet-uuid>`, or install manually: \" +\n `POST /api/droplet_installations with {\"droplet_uuid\":\"<uuid>\"}.`,\n );\n }\n if (options.egressProxy) {\n egressProxy = await ensureEgressProxy(client, {\n dropletUuid: surfaceUuid,\n created: createdEgressProxy,\n reporter,\n });\n }\n installation = await ensureDropletInstalled(client, {\n dropletUuid: surfaceUuid,\n scopes: options.requestedScopes ?? DEFAULT_DROPLET_SCOPES,\n reporter,\n });\n }\n\n // Step 5 — droplet only: verify the Mist-managed env keys landed.\n const managedEnv =\n kind === \"droplet\"\n ? await verifyManagedEnv(client, {\n slugOrId,\n reporter,\n timing,\n sleep,\n enabled: options.verifyEnv !== false,\n })\n : null;\n\n return {\n mist: {\n id: mist.id,\n slug: mist.slug,\n name: mist.name,\n public_url: mist.public_url,\n state: mist.state,\n },\n kind,\n surface: { uuid: surfaceUuid, id: surfaceNumericId, name: surfaceName },\n integrationPoint: integrationPoint ?? {\n id: \"\",\n integratableUuid: null,\n created: false,\n },\n surfaceCreated,\n embedUrlSet,\n installation,\n managedEnv,\n egressProxy,\n };\n}\n\n/**\n * Make sure the droplet has asked Fluid for egress through its proxy. A\n * droplet this run created already carries the request; one that existed\n * (reused, already linked, or `--existing`) is read first and PATCHed only if\n * it has not asked yet, so the flag is never silently ignored and an existing\n * request is not reported as new. Fluid refusing the request (only a company\n * admin can ask) warns and lets provisioning carry on; a response without the\n * fields (a non-admin, or an older Fluid) warns instead of claiming success.\n */\nasync function ensureEgressProxy(\n client: MistClient,\n args: {\n dropletUuid: string;\n /** The droplet this run created (already carrying the request), if any. */\n created: DropletRecord | undefined;\n reporter: StepReporter;\n },\n): Promise<{ requested: boolean; changed: boolean }> {\n const { reporter } = args;\n let record = args.created;\n // A droplet this run created with the request made it in this run.\n let changed = record?.egress_proxy_requested === true;\n if (record === undefined) {\n record = await readDropletIfAllowed(client, args.dropletUuid);\n }\n if (record?.egress_proxy_requested !== true) {\n reporter.start(\"Asking Fluid for egress through its proxy…\");\n try {\n record = (\n await updateDroplet(client, args.dropletUuid, {\n egress_proxy_requested: true,\n })\n ).droplet;\n } catch (err) {\n if (!isEgressRefusal(err)) throw err;\n reporter.warn(EGRESS_REFUSED);\n return { requested: false, changed: false };\n }\n changed = record.egress_proxy_requested === true;\n }\n const requested = record?.egress_proxy_requested === true;\n if (requested) {\n reporter.succeed(\n \"Egress requested — Fluid's proxy admits requested droplets wherever the proxy honours Fluid's answer\",\n );\n } else {\n reporter.warn(\n `${EGRESS_NOT_RETURNED} Re-run \\`fluid mist droplet egress-proxy on\\` as a company admin.`,\n );\n }\n return { requested, changed };\n}\n\n/**\n * Read an existing droplet so an existing request is not written again. Fluid\n * gates reading a droplet (`droplets.view`) separately from updating it\n * (`droplets.update`), so a caller allowed to update may not be allowed to\n * read: then this returns undefined and the caller just makes the request,\n * which is idempotent. Any other failure propagates.\n */\nasync function readDropletIfAllowed(\n client: MistClient,\n dropletUuid: string,\n): Promise<DropletRecord | undefined> {\n try {\n return (await getDroplet(client, dropletUuid)).droplet;\n } catch (err) {\n if (err instanceof MistApiError && err.status === 403) return undefined;\n throw err;\n }\n}\n\n/**\n * Install the droplet for the current company (idempotent). Checks the\n * installations list first; treats the API's already-installed rejection as\n * success too, so re-runs and races both converge. A hard failure throws with\n * the droplet uuid + recovery steps — the created droplet and mist link stay\n * in place, so an idempotent re-run finishes the job.\n */\nasync function ensureDropletInstalled(\n client: MistClient,\n args: {\n dropletUuid: string;\n scopes: readonly string[];\n reporter: StepReporter;\n },\n): Promise<{ uuid: string | null; created: boolean }> {\n const { reporter, dropletUuid } = args;\n reporter.start(\"Installing droplet…\");\n\n // Idempotency: already installed via a previous run, Mist Desktop, or the\n // admin marketplace. Best-effort — the POST below is the authoritative step.\n let existing: { uuid?: string } | undefined;\n try {\n existing = findInstallationForDroplet(\n await listAllDropletInstallations(client),\n dropletUuid,\n );\n } catch {\n // Listing is only an optimization; fall through to the POST.\n }\n if (existing) {\n reporter.skip(\n `Droplet already installed${existing.uuid ? ` (${existing.uuid})` : \"\"} — skipping`,\n );\n return { uuid: existing.uuid ?? null, created: false };\n }\n\n try {\n const resp = await createDropletInstallation(client, {\n droplet_uuid: dropletUuid,\n scopes: [...args.scopes],\n });\n const uuid = resp.droplet_installation?.uuid ?? null;\n reporter.succeed(`Droplet installed${uuid ? ` (${uuid})` : \"\"}`);\n return { uuid, created: true };\n } catch (err) {\n // The API rejects a duplicate install (list raced or missed it). Only\n // treat that as success when the installations list confirms a row —\n // a generic 409/422 that merely mentions \"exists\" must not mask a real\n // failure and leave the droplet inactive.\n if (\n err instanceof MistApiError &&\n (err.status === 409 || err.status === 422)\n ) {\n const confirmed = await lookupInstallation(client, dropletUuid);\n if (confirmed) {\n reporter.skip(\"Droplet already installed — skipping\");\n return { uuid: confirmed.uuid ?? null, created: false };\n }\n }\n reporter.fail(\"Droplet install failed\");\n throw normalizeInstallError(err, dropletUuid);\n }\n}\n\n/** Best-effort installation lookup for a droplet; null when the row can't\n * be found or the list call fails. */\nasync function lookupInstallation(\n client: MistClient,\n dropletUuid: string,\n): Promise<{ uuid?: string } | null> {\n try {\n return (\n findInstallationForDroplet(\n await listAllDropletInstallations(client),\n dropletUuid,\n ) ?? null\n );\n } catch {\n return null;\n }\n}\n\n/**\n * Install failures must NOT read as \"wired up\" — an uninstalled droplet means\n * dit/dex token exchange, admin embeds, and install webhooks silently don't\n * work. Surface the droplet uuid + recovery path; keep the MistApiError\n * status so the error handler exits with the spec'd non-zero code.\n */\nfunction normalizeInstallError(err: unknown, dropletUuid: string): Error {\n const detail = err instanceof Error ? err.message : String(err);\n const message =\n `Droplet ${dropletUuid} was created and linked to the mist, but installing it failed: ${detail}. ` +\n `The droplet and its link were left in place — re-run this command to finish the install, ` +\n `or install manually: POST /api/droplet_installations with {\"droplet_uuid\":\"${dropletUuid}\"}.`;\n return err instanceof MistApiError\n ? new MistApiError(message, err.status, err.details)\n : new Error(message);\n}\n\n/**\n * If the integration-points endpoint rejects the attach identifier (e.g. an\n * older API that predates #19688's attach-by-uuid support), surface an\n * actionable message noting the surface WAS created. Other errors pass through.\n */\nfunction normalizeAttachError(\n err: unknown,\n surfaceRef: string,\n kind: SurfaceKind,\n): Error {\n if (\n err instanceof MistApiError &&\n (err.status === 400 || err.status === 422)\n ) {\n const blob = `${err.message} ${JSON.stringify(err.details ?? {})}`;\n if (\n /integratable_uuid|integratable_id|unpermitted|unknown|param/i.test(blob)\n ) {\n return new MistApiError(\n `The ${KIND_LABEL[kind]} ${surfaceRef} was created, but this API rejected the attach ` +\n `(needs the #19688 integration-points endpoint). Re-run once the API is updated.`,\n err.status,\n err.details,\n );\n }\n }\n return err instanceof Error ? err : new Error(String(err));\n}\n\n/**\n * Return the mist's public_url, polling briefly if it isn't ready yet\n * (async-first-deploy). Returns null if it never appears within the window.\n */\nasync function resolvePublicUrl(\n client: MistClient,\n args: {\n slugOrId: string;\n initial: string | null;\n reporter: StepReporter;\n timing: ProvisionTiming;\n sleep: (ms: number) => Promise<void>;\n },\n): Promise<string | null> {\n if (args.initial) return args.initial;\n\n args.reporter.start(\"Waiting for the mist's public URL…\");\n const deadline = Date.now() + args.timing.publicUrlTimeoutMs;\n for (;;) {\n const url = (await showMist(client, args.slugOrId)).mist.public_url;\n if (url) {\n args.reporter.succeed(`Public URL ready: ${url}`);\n return url;\n }\n if (Date.now() >= deadline) break;\n await args.sleep(args.timing.publicUrlIntervalMs);\n }\n return null;\n}\n\n/**\n * Poll `GET …/env_vars` until the Mist-managed droplet keys appear with\n * `managed_by: \"mist\"`. Never fails the flow — a redeploy in progress is a\n * normal, transient state we simply report.\n */\nasync function verifyManagedEnv(\n client: MistClient,\n args: {\n slugOrId: string;\n reporter: StepReporter;\n timing: ProvisionTiming;\n sleep: (ms: number) => Promise<void>;\n enabled: boolean;\n },\n): Promise<{ ready: boolean; present: string[]; missing: string[] }> {\n const required = [...MANAGED_DROPLET_ENV_KEYS];\n if (!args.enabled) {\n return { ready: false, present: [], missing: required };\n }\n\n args.reporter.start(\"Verifying managed environment…\");\n const deadline = Date.now() + args.timing.envTimeoutMs;\n let present: string[] = [];\n\n for (;;) {\n const envVars = (await listEnvVars(client, args.slugOrId)).env_vars ?? [];\n const managed = new Set(\n envVars.filter((e) => e.managed_by === \"mist\").map((e) => e.key),\n );\n present = required.filter((k) => managed.has(k));\n if (present.length === required.length) {\n args.reporter.succeed(\"Managed env keys present — droplet is wired up\");\n return { ready: true, present, missing: [] };\n }\n if (Date.now() >= deadline) break;\n args.reporter.update(\n `Verifying managed environment… (${present.length}/${required.length} keys, redeploy in progress)`,\n );\n await args.sleep(args.timing.envIntervalMs);\n }\n\n const missing = required.filter((k) => !present.includes(k));\n args.reporter.warn(\n `Redeploy in progress — managed keys not visible yet (${present.length}/${required.length}). ` +\n `Check later with \\`fluid mist env list\\`. Missing: ${missing.join(\", \")}`,\n );\n return { ready: false, present, missing };\n}\n","/**\n * Shared parsing + validation for the surface-kind flags used by\n * `create` and `attach`. Keeps kind normalization and the per-kind required\n * flags in one place.\n */\n\nimport type { SurfaceKind } from \"./surface-provision.js\";\n\nexport interface SurfaceFlags {\n zone?: string;\n height?: string;\n coverImage?: string;\n egressProxy?: boolean;\n}\n\nexport const EGRESS_PROXY_FLAG_DESCRIPTION =\n \"Ask Fluid to let the droplet reach external systems such as Exigo through its egress proxy\";\n\n/**\n * `--egress-proxy` only means something for a droplet: only a droplet's\n * installation token authenticates to the proxy. Refuse it on other kinds\n * rather than silently ignoring it.\n */\nexport function egressProxyFromFlags(\n kind: SurfaceKind,\n flags: Pick<SurfaceFlags, \"egressProxy\">,\n): boolean | undefined {\n if (!flags.egressProxy) return undefined;\n if (kind !== \"droplet\") {\n throw new Error(\n \"`--egress-proxy` applies to droplets only — drop it, or use `--kind droplet`.\",\n );\n }\n return true;\n}\n\nconst KIND_ALIASES: Record<string, SurfaceKind> = {\n droplet: \"droplet\",\n droplets: \"droplet\",\n \"drop-zone\": \"drop-zone\",\n drop_zone: \"drop-zone\",\n dropzone: \"drop-zone\",\n zone: \"drop-zone\",\n \"mobile-embed\": \"mobile-embed\",\n mobile_embed: \"mobile-embed\",\n mobile: \"mobile-embed\",\n \"mobile-widget\": \"mobile-embed\",\n mobilewidget: \"mobile-embed\",\n};\n\nexport const SURFACE_KIND_LIST = \"droplet, drop-zone, mobile-embed\";\n\nexport function parseSurfaceKind(raw: string): SurfaceKind {\n const hit = KIND_ALIASES[raw.trim().toLowerCase()];\n if (!hit) {\n throw new Error(\n `Unknown surface kind \"${raw}\". Use one of: ${SURFACE_KIND_LIST}.`,\n );\n }\n return hit;\n}\n\nfunction parseHeight(raw: string | undefined): number | undefined {\n if (raw == null) return undefined;\n const n = Number(raw);\n if (!Number.isInteger(n) || n <= 0) {\n throw new Error(\n `Invalid --height \"${raw}\". Must be a positive integer (px).`,\n );\n }\n return n;\n}\n\n/**\n * Map CLI flags to the kind-specific `provisionSurface` options, enforcing the\n * per-kind requirements the API needs:\n * - drop-zone requires `--zone <page__zone>` (else it never renders).\n * - mobile-embed requires `--cover-image <url>` (the API 422s without one; the\n * CLI has no DAM picker, so the user must supply a url).\n */\nexport function surfaceProvisionOptionsFromFlags(\n kind: SurfaceKind,\n flags: SurfaceFlags,\n): { zone?: string; heightPx?: number; coverImageUrl?: string } {\n const heightPx = parseHeight(flags.height);\n if (kind === \"drop-zone\") {\n if (!flags.zone?.trim()) {\n throw new Error(\n \"A drop-zone needs a slot — pass `--zone <page__zone>` (e.g. --zone checkout__below_total).\",\n );\n }\n return { zone: flags.zone.trim(), heightPx };\n }\n if (kind === \"mobile-embed\") {\n if (!flags.coverImage?.trim()) {\n throw new Error(\n \"A mobile-embed needs a cover image — pass `--cover-image <url>` (the mobile API requires one; the CLI has no image picker).\",\n );\n }\n return { coverImageUrl: flags.coverImage.trim(), heightPx };\n }\n return {};\n}\n","/** Shared human-readable summary for a surface provision result. */\n\nimport chalk from \"chalk\";\nimport type { SurfaceProvisionResult } from \"./surface-provision.js\";\n\nconst KIND_LABEL: Record<SurfaceProvisionResult[\"kind\"], string> = {\n droplet: \"droplet\",\n \"drop-zone\": \"drop zone\",\n \"mobile-embed\": \"mobile embed\",\n};\n\nexport function printSurfaceSummary(result: SurfaceProvisionResult): void {\n const ref =\n result.surface.uuid ??\n (result.surface.id != null ? `#${result.surface.id}` : \"(unknown)\");\n const state = result.surfaceCreated ? \"created\" : \"existing\";\n console.log(\n chalk.dim(\n ` ${KIND_LABEL[result.kind]}: ${ref} (${state}) · integration ${result.integrationPoint.id}`,\n ),\n );\n if (result.kind === \"droplet\" && result.installation) {\n const ref = result.installation.uuid ?? \"(uuid unknown)\";\n console.log(\n chalk.dim(\n ` installation: ${ref} (${result.installation.created ? \"installed\" : \"already installed\"})`,\n ),\n );\n }\n if (result.kind === \"droplet\" && result.managedEnv) {\n if (result.managedEnv.ready) {\n console.log(chalk.dim(\" env: managed keys provisioned ✓\"));\n } else {\n console.log(\n chalk.yellow(\n \" env: redeploy in progress — run `fluid mist env list` shortly to confirm managed keys\",\n ),\n );\n }\n }\n if (result.kind === \"droplet\" && result.egressProxy) {\n console.log(\n result.egressProxy.requested\n ? chalk.dim(\" egress proxy: requested ✓\")\n : chalk.yellow(\n \" egress proxy: not requested — only a company admin can ask for it, or this Fluid predates it\",\n ),\n );\n }\n}\n","/**\n * `fluid mist create` — provision a new mist, optionally creating and attaching\n * a surface in one step. All attach paths go through integration points\n * (hostable is retired — #19684/#19687).\n *\n * --droplet [name] Create a NEW droplet and attach it. Bare →\n * droplet name defaults to the mist name. The\n * \"one command that sets everything up\" path.\n * --kind <kind> [flags] Create + attach a surface of any kind:\n * droplet | drop-zone | mobile-embed. Kind flags:\n * drop-zone --zone <page__zone> --height <px>\n * mobile-embed --cover-image <url> --height <px>\n * --hostable droplet:<uuid> Attach an EXISTING droplet by its drp_… uuid.\n * --egress-proxy With a droplet: let it reach external systems\n * such as Exigo through Fluid's egress proxy.\n *\n * With none of these, creates a plain standalone mist.\n */\n\nimport chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport { createMistClient } from \"../api/client.js\";\nimport { createMist } from \"../api/mists.js\";\nimport { runAction } from \"../lib/error-handler.js\";\nimport { colorState, mistLabel, printJson } from \"../lib/output.js\";\nimport { parseDropletRef } from \"../lib/hostable.js\";\nimport { createOraReporter } from \"../lib/ora-reporter.js\";\nimport { createSilentReporter } from \"../lib/step-reporter.js\";\nimport {\n provisionSurface,\n type SurfaceKind,\n type SurfaceProvisionResult,\n} from \"../lib/surface-provision.js\";\nimport {\n EGRESS_PROXY_FLAG_DESCRIPTION,\n egressProxyFromFlags,\n parseSurfaceKind,\n surfaceProvisionOptionsFromFlags,\n type SurfaceFlags,\n} from \"../lib/surface-flags.js\";\nimport { printSurfaceSummary } from \"../lib/surface-summary.js\";\nimport type { MistCreateRequest, MistShowResponse } from \"../types.js\";\n\ninterface Opts extends SurfaceFlags {\n name?: string;\n /** Create + attach a new droplet. Bare (true) or a name override. */\n droplet?: string | boolean;\n /** Create + attach a new surface of this kind. */\n kind?: string;\n /** Attach an existing droplet: `droplet:<uuid>`. */\n hostable?: string;\n mistKind?: \"next_app\";\n wait?: boolean;\n json?: boolean;\n}\n\nexport const createCommand = new Command(\"create\")\n .description(\n \"Provision a new mist (optionally create + attach a surface in one step)\",\n )\n .option(\n \"--droplet [name]\",\n \"Create a droplet and attach it. Bare uses the mist name; pass a value to override.\",\n )\n .option(\n \"--kind <kind>\",\n \"Create + attach a surface: droplet | drop-zone | mobile-embed\",\n )\n .option(\n \"--hostable <ref>\",\n \"Attach an existing droplet by uuid: droplet:<uuid> (e.g. droplet:drp_abc123)\",\n )\n .option(\"--name <label>\", \"Display label for the mist (and the surface)\")\n .option(\n \"--zone <slot>\",\n \"drop-zone slot id: page__zone (e.g. checkout__below_total)\",\n )\n .option(\"--height <px>\", \"Embed height in px (drop-zone + mobile-embed)\")\n .option(\n \"--cover-image <url>\",\n \"Mobile-embed cover image url (required for mobile-embed)\",\n )\n .option(\"--egress-proxy\", EGRESS_PROXY_FLAG_DESCRIPTION)\n .option(\"--mist-kind <kind>\", \"Mist kind (default: next_app)\")\n .option(\"--no-wait\", \"Don't wait for managed env keys to appear after attach\")\n .option(\"--json\", \"Emit raw JSON\")\n .action((opts: Opts) =>\n runAction(async () => {\n const wantDroplet = opts.droplet !== undefined;\n const wantKind = !!opts.kind;\n const wantHostable = !!opts.hostable;\n if ([wantDroplet, wantKind, wantHostable].filter(Boolean).length > 1) {\n throw new Error(\n \"Pass only one of `--droplet`, `--kind`, or `--hostable` — they're mutually exclusive.\",\n );\n }\n\n // Resolve the surface kind + attach mode before we create the mist so\n // flag errors surface immediately (no orphan mist on a typo).\n let kind: SurfaceKind | undefined;\n let existingDropletUuid: string | undefined;\n let nameOverride: string | undefined;\n if (wantDroplet) {\n kind = \"droplet\";\n nameOverride =\n typeof opts.droplet === \"string\" ? opts.droplet.trim() : undefined;\n } else if (wantKind) {\n kind = parseSurfaceKind(opts.kind!);\n } else if (wantHostable) {\n kind = \"droplet\";\n existingDropletUuid = parseDropletRef(opts.hostable!);\n }\n if (opts.egressProxy && !kind) {\n throw new Error(\n \"`--egress-proxy` needs a droplet — add `--droplet`, `--kind droplet`, or `--hostable droplet:<uuid>`.\",\n );\n }\n const egressProxy = kind ? egressProxyFromFlags(kind, opts) : undefined;\n\n // The API requires `kind`; default to next_app (the only kind today).\n const mistBody: MistCreateRequest[\"mist\"] = {\n kind: opts.mistKind ?? \"next_app\",\n };\n if (opts.name?.trim()) mistBody.name = opts.name.trim();\n\n const client = createMistClient();\n const reporter = opts.json ? createSilentReporter() : createOraReporter();\n\n // 1. Create the mist itself (synchronous; 30–90s).\n reporter.start(\"Setting up your Mist Cloud environment…\");\n let created: MistShowResponse;\n try {\n created = await createMist(client, { mist: mistBody });\n } catch (err) {\n reporter.fail(\"Provisioning failed\");\n throw err;\n }\n const mist = created.mist;\n reporter.succeed(`Provisioned ${mist.slug}`);\n\n // 2. Optionally create/attach the surface via the shared engine.\n let provision: SurfaceProvisionResult | undefined;\n if (kind) {\n provision = await provisionSurface(client, {\n slugOrId: mist.slug,\n kind,\n name: nameOverride ?? opts.name?.trim(),\n existingUuid: existingDropletUuid,\n egressProxy,\n verifyEnv: opts.wait !== false,\n reporter,\n ...surfaceProvisionOptionsFromFlags(kind, opts),\n });\n }\n\n if (opts.json) {\n return printJson(provision ? { mist, provision } : created);\n }\n\n const label = mistLabel(mist.name, mist.vendor_name);\n console.log();\n console.log(\n `${label} ${chalk.dim(mist.slug)} ${colorState(mist.state)}`,\n );\n if (mist.public_url) console.log(` ${chalk.cyan(mist.public_url)}`);\n if (provision) printSurfaceSummary(provision);\n console.log();\n console.log(chalk.dim(`Clone with: fluid mist clone ${mist.slug}`));\n }),\n );\n","/**\n * `fluid mist attach --kind <kind> [flags] [slug]` — create a surface and wire\n * it to an existing mist. One command for all three kinds:\n *\n * --kind droplet (managed-env-provisioning droplet)\n * --kind drop-zone --zone page__zone [--height <px>]\n * --kind mobile-embed --cover-image <url> [--height <px>]\n *\n * Attach an EXISTING surface instead of creating one with `--existing <ref>`:\n * droplet/drop-zone → droplet:drp_… / drop-zone:dz_… / bare uuid\n * mobile-embed → mobile-embed:<numericId>\n *\n * `--egress-proxy` (droplet only) lets the droplet reach external systems\n * such as Exigo through Fluid's egress proxy — on a new droplet, and on an\n * existing or already-attached one.\n *\n * Idempotent for droplet + attach-existing (skips when already linked).\n */\n\nimport chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport { createMistClient } from \"../api/client.js\";\nimport { runAction } from \"../lib/error-handler.js\";\nimport { resolveSlug } from \"../lib/slug-resolver.js\";\nimport { printJson } from \"../lib/output.js\";\nimport { createOraReporter } from \"../lib/ora-reporter.js\";\nimport { createSilentReporter } from \"../lib/step-reporter.js\";\nimport { provisionSurface } from \"../lib/surface-provision.js\";\nimport {\n EGRESS_PROXY_FLAG_DESCRIPTION,\n egressProxyFromFlags,\n parseSurfaceKind,\n surfaceProvisionOptionsFromFlags,\n SURFACE_KIND_LIST,\n type SurfaceFlags,\n} from \"../lib/surface-flags.js\";\nimport { printSurfaceSummary } from \"../lib/surface-summary.js\";\n\ninterface Opts extends SurfaceFlags {\n kind?: string;\n name?: string;\n existing?: string;\n wait?: boolean;\n json?: boolean;\n}\n\n/** Parse `--existing <ref>` into a uuid or numeric id for the given kind. */\nfunction parseExisting(\n kind: ReturnType<typeof parseSurfaceKind>,\n raw: string,\n): { existingUuid?: string; existingId?: number } {\n // Strip an optional `kind:` prefix (droplet:/drop-zone:/mobile-embed:).\n const value = raw.includes(\":\") ? raw.slice(raw.lastIndexOf(\":\") + 1) : raw;\n const trimmed = value.trim();\n if (!trimmed) throw new Error(`Empty --existing reference \"${raw}\".`);\n if (kind === \"mobile-embed\") {\n const id = Number(trimmed);\n if (!Number.isInteger(id) || id <= 0) {\n throw new Error(\n `mobile-embed attaches by numeric id — pass \\`--existing <id>\\` (got \"${raw}\").`,\n );\n }\n return { existingId: id };\n }\n return { existingUuid: trimmed };\n}\n\nexport const attachCommand = new Command(\"attach\")\n .description(\n `Create a surface (${SURFACE_KIND_LIST}) and wire it to an existing mist`,\n )\n .argument(\"[slug]\", \"Mist slug (defaults to the .mist file in CWD)\")\n .requiredOption(\"--kind <kind>\", `Surface kind: ${SURFACE_KIND_LIST}`)\n .option(\"--name <label>\", \"Surface name (defaults to the mist's name)\")\n .option(\n \"--zone <slot>\",\n \"drop-zone slot id: page__zone (e.g. checkout__below_total)\",\n )\n .option(\"--height <px>\", \"Embed height in px (drop-zone + mobile-embed)\")\n .option(\n \"--cover-image <url>\",\n \"Mobile-embed cover image url (required for mobile-embed)\",\n )\n .option(\n \"--existing <ref>\",\n \"Attach an existing surface instead of creating one (uuid, or numeric id for mobile-embed)\",\n )\n .option(\"--egress-proxy\", EGRESS_PROXY_FLAG_DESCRIPTION)\n .option(\"--no-wait\", \"Don't wait for managed env keys to appear after attach\")\n .option(\"--json\", \"Emit raw JSON\")\n .action((slug: string | undefined, opts: Opts) =>\n runAction(async () => {\n const kind = parseSurfaceKind(opts.kind!);\n const egressProxy = egressProxyFromFlags(kind, opts);\n const resolved = resolveSlug(slug);\n const client = createMistClient();\n const reporter = opts.json ? createSilentReporter() : createOraReporter();\n\n const existing = opts.existing\n ? parseExisting(kind, opts.existing)\n : undefined;\n\n const result = await provisionSurface(client, {\n slugOrId: resolved,\n kind,\n name: opts.name?.trim(),\n egressProxy,\n verifyEnv: opts.wait !== false,\n reporter,\n ...existing,\n // Kind flags only apply when creating a new surface.\n ...(existing ? {} : surfaceProvisionOptionsFromFlags(kind, opts)),\n });\n\n if (opts.json) return printJson(result);\n\n console.log();\n console.log(\n `${chalk.bold(result.mist.name ?? result.mist.slug)} ${chalk.dim(result.mist.slug)}`,\n );\n printSurfaceSummary(result);\n }),\n );\n","/**\n * `fluid mist attach-droplet [name] [slug]` — droplet shortcut for\n * `fluid mist attach --kind droplet`. Creates a droplet and wires it to an\n * existing standalone mist (provisioning the managed env). Idempotent: if a\n * droplet is already attached, it skips create + attach and just verifies env.\n *\n * name Optional droplet name (defaults to the mist's name).\n * slug Optional mist slug (defaults to the `.mist` file in CWD).\n *\n * `--egress-proxy` lets the droplet reach external systems such as Exigo\n * through Fluid's egress proxy, including one that was already attached.\n */\n\nimport chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport { createMistClient } from \"../api/client.js\";\nimport { runAction } from \"../lib/error-handler.js\";\nimport { resolveSlug } from \"../lib/slug-resolver.js\";\nimport { printJson } from \"../lib/output.js\";\nimport { createOraReporter } from \"../lib/ora-reporter.js\";\nimport { createSilentReporter } from \"../lib/step-reporter.js\";\nimport { provisionSurface } from \"../lib/surface-provision.js\";\nimport { printSurfaceSummary } from \"../lib/surface-summary.js\";\nimport { EGRESS_PROXY_FLAG_DESCRIPTION } from \"../lib/surface-flags.js\";\n\ninterface Opts {\n egressProxy?: boolean;\n wait?: boolean;\n json?: boolean;\n}\n\nexport const attachDropletCommand = new Command(\"attach-droplet\")\n .description(\n \"Create a droplet and wire it to an existing standalone mist (idempotent)\",\n )\n .argument(\"[name]\", \"Droplet name (defaults to the mist's name)\")\n .argument(\"[slug]\", \"Mist slug (defaults to the .mist file in CWD)\")\n .option(\"--egress-proxy\", EGRESS_PROXY_FLAG_DESCRIPTION)\n .option(\"--no-wait\", \"Don't wait for managed env keys to appear after attach\")\n .option(\"--json\", \"Emit raw JSON\")\n .action((name: string | undefined, slug: string | undefined, opts: Opts) =>\n runAction(async () => {\n const resolved = resolveSlug(slug);\n const client = createMistClient();\n const reporter = opts.json ? createSilentReporter() : createOraReporter();\n\n const result = await provisionSurface(client, {\n slugOrId: resolved,\n kind: \"droplet\",\n name: name?.trim() || undefined,\n egressProxy: opts.egressProxy === true || undefined,\n verifyEnv: opts.wait !== false,\n reporter,\n });\n\n if (opts.json) return printJson(result);\n\n console.log();\n console.log(\n `${chalk.bold(result.mist.name ?? result.mist.slug)} ${chalk.dim(result.mist.slug)}`,\n );\n printSurfaceSummary(result);\n }),\n );\n","/**\n * `fluid mist update` — mutate an existing mist. Hostable is retired\n * (#19684/#19687), so attach/detach operate on integration points:\n *\n * --name <label> Rename the mist (PATCH /mists/:id).\n * --hostable droplet:<uuid> Attach a droplet by uuid. No-ops if that same\n * droplet is already attached; replaces it if a\n * different droplet is attached (droplet is\n * 1-per-mist — it provisions the managed env).\n * --detach Remove the droplet integration point.\n *\n * The integration-point link is immutable (no update route, no `active`\n * field) — enablement lives on the surface record, so there's no toggle here.\n * All ops re-derive state from the integration-points index (which echoes\n * `integratable_uuid` since #19689) and only make the calls that change it.\n */\n\nimport chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport { createMistClient } from \"../api/client.js\";\nimport { updateMist } from \"../api/mists.js\";\nimport {\n createIntegrationPoint,\n deleteIntegrationPoint,\n findDropletIntegrationPoint,\n listIntegrationPoints,\n} from \"../api/integration_points.js\";\nimport { runAction } from \"../lib/error-handler.js\";\nimport { resolveSlug } from \"../lib/slug-resolver.js\";\nimport { parseDropletRef } from \"../lib/hostable.js\";\nimport { colorState, mistLabel, printJson } from \"../lib/output.js\";\nimport type { MistShowResponse } from \"../types.js\";\n\ninterface Opts {\n name?: string;\n hostable?: string;\n detach?: boolean;\n json?: boolean;\n}\n\nexport const updateCommand = new Command(\"update\")\n .description(\"Rename a mist, or attach/detach its droplet\")\n .argument(\"[slug]\", \"The mist slug (defaults to .mist file in CWD)\")\n .option(\"--name <label>\", \"Set the display label\")\n .option(\n \"--hostable <ref>\",\n \"Attach a droplet: droplet:<uuid> (e.g. droplet:drp_abc123)\",\n )\n .option(\"--detach\", \"Remove the droplet integration point\")\n .option(\"--json\", \"Emit raw API JSON\")\n .action((slug: string | undefined, opts: Opts) =>\n runAction(async () => {\n if (opts.hostable && opts.detach) {\n throw new Error(\n \"Pass either `--hostable` OR `--detach`, not both — they're mutually exclusive.\",\n );\n }\n if (!opts.name && !opts.hostable && !opts.detach) {\n throw new Error(\n \"Nothing to do — pass `--name`, `--hostable`, or `--detach`.\",\n );\n }\n\n const resolved = resolveSlug(slug);\n const client = createMistClient();\n const notes: string[] = [];\n\n // Rename (a plain mist PATCH). Don't early-return in `--json` mode — the\n // integration-point ops below still run; the rename response is folded\n // into the final JSON payload instead.\n let renameResp: MistShowResponse | undefined;\n if (opts.name?.trim()) {\n renameResp = await updateMist(client, resolved, {\n mist: { name: opts.name.trim() },\n });\n if (!opts.json) {\n const m = renameResp.mist;\n console.log(\n `${mistLabel(m.name, m.vendor_name)} ${chalk.dim(m.slug)} ${colorState(m.state)}`,\n );\n }\n }\n\n if (opts.hostable || opts.detach) {\n const existing = findDropletIntegrationPoint(\n (await listIntegrationPoints(client, resolved)).integration_points,\n );\n\n if (opts.hostable) {\n const dropletUuid = parseDropletRef(opts.hostable);\n if (existing && existing.integratable_uuid === dropletUuid) {\n notes.push(\n `droplet ${dropletUuid} already attached (integration ${existing.id})`,\n );\n } else {\n if (existing) {\n // A different droplet is attached — replace it (droplet is\n // 1-per-mist since it provisions the managed env).\n await deleteIntegrationPoint(client, resolved, existing.id);\n notes.push(`replaced integration ${existing.id}`);\n }\n const ip = await createIntegrationPoint(client, resolved, {\n kind: \"droplet\",\n integratable_uuid: dropletUuid,\n });\n notes.push(\n `attached droplet ${dropletUuid} (integration ${ip.integration_point.id})`,\n );\n }\n } else if (opts.detach) {\n if (existing) {\n await deleteIntegrationPoint(client, resolved, existing.id);\n notes.push(`detached droplet (removed integration ${existing.id})`);\n } else {\n notes.push(\"already standalone (no droplet integration)\");\n }\n }\n }\n\n if (opts.json) {\n return printJson(\n renameResp\n ? { ...renameResp, slug: resolved, notes }\n : { slug: resolved, notes },\n );\n }\n for (const note of notes) console.log(chalk.dim(` ${note}`));\n }),\n );\n","/**\n * `fluid mist delete` — scheduled teardown 15 days out (spec §3 \"15-day\n * grace period\"). DO NOT poll for completion.\n */\n\nimport chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport prompts from \"prompts\";\nimport { createMistClient } from \"../api/client.js\";\nimport { deleteMist, showMist } from \"../api/mists.js\";\nimport { runAction } from \"../lib/error-handler.js\";\nimport { resolveSlug } from \"../lib/slug-resolver.js\";\nimport { formatAbsolute, printJson } from \"../lib/output.js\";\n\ninterface Opts {\n yes?: boolean;\n json?: boolean;\n}\n\nexport const deleteCommand = new Command(\"delete\")\n .description(\"Schedule a mist for destruction (15-day grace window)\")\n .argument(\"[slug]\", \"The mist slug (defaults to .mist file in CWD)\")\n .option(\"-y, --yes\", \"Skip confirmation prompt\")\n .option(\"--json\", \"Emit raw API JSON\")\n .action((slug: string | undefined, opts: Opts) =>\n runAction(async () => {\n const resolved = resolveSlug(slug);\n const client = createMistClient();\n\n if (!opts.yes) {\n // Fetch first so the prompt shows what's about to happen.\n const { mist } = await showMist(client, resolved);\n const fifteenDaysOut = new Date(Date.now() + 15 * 86_400_000)\n .toISOString()\n .slice(0, 10);\n console.log();\n console.log(\n `About to schedule ${chalk.bold(mist.name ?? mist.vendor_name ?? mist.slug)} (${chalk.dim(mist.slug)}) for destruction on ${chalk.yellow(fifteenDaysOut)}.`,\n );\n console.log(\n chalk.dim(\n `Vendor resources stay alive until then; run \\`fluid mist restore ${mist.slug}\\` to undo.`,\n ),\n );\n const { confirmed } = await prompts(\n {\n type: \"confirm\",\n name: \"confirmed\",\n message: \"Continue?\",\n initial: false,\n },\n { onCancel: () => process.exit(130) },\n );\n if (!confirmed) {\n console.log(chalk.dim(\"Aborted.\"));\n process.exit(0);\n }\n }\n\n const { mist } = await deleteMist(client, resolved);\n if (opts.json) return printJson({ mist });\n console.log(\n chalk.magenta(\n `Scheduled for destruction on ${mist.scheduled_destroy_at ? formatAbsolute(mist.scheduled_destroy_at) : \"the 15-day mark\"}.`,\n ),\n );\n console.log(\n chalk.dim(\n `Vendor resources stay alive until then; run \\`fluid mist restore ${mist.slug}\\` to undo.`,\n ),\n );\n }),\n );\n","/** `fluid mist restore` — un-delete within the 15-day window. Spec §3. */\n\nimport chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport { createMistClient } from \"../api/client.js\";\nimport { restoreMist } from \"../api/mists.js\";\nimport { runAction } from \"../lib/error-handler.js\";\nimport { resolveSlug } from \"../lib/slug-resolver.js\";\nimport { colorState, printJson } from \"../lib/output.js\";\n\ninterface Opts {\n json?: boolean;\n}\n\nexport const restoreCommand = new Command(\"restore\")\n .description(\"Cancel a scheduled mist destruction (15-day window)\")\n .argument(\"[slug]\", \"The mist slug (defaults to .mist file in CWD)\")\n .option(\"--json\", \"Emit raw API JSON\")\n .action((slug: string | undefined, opts: Opts) =>\n runAction(async () => {\n const resolved = resolveSlug(slug);\n const client = createMistClient();\n const { mist } = await restoreMist(client, resolved);\n if (opts.json) return printJson({ mist });\n console.log(\n `Restored: ${mist.public_url ? chalk.cyan(mist.public_url) : (mist.name ?? mist.vendor_name ?? mist.slug)} (${colorState(mist.state)})`,\n );\n }),\n );\n","/**\n * `POST /git_credential` — mints a short-lived (~1h) remote URL with an\n * embedded installation token. The CLI calls this immediately before any\n * git op and discards the URL after. NEVER persist it.\n */\n\nimport type { MistClient } from \"./client.js\";\nimport type { GitCredentialResponse } from \"../types.js\";\nimport { resolveMistId } from \"./id-resolver.js\";\n\nexport async function createGitCredential(\n client: MistClient,\n slug: string,\n): Promise<GitCredentialResponse> {\n const id = await resolveMistId(client, slug);\n return client.post<GitCredentialResponse>(\n `/api/v202604/mists/${id}/git_credential`,\n );\n}\n","/**\n * After `fluid mist clone`, list the production env keys the checkout has no\n * local value for, as placeholders in `.env.local`.\n *\n * A fresh clone has no env file, so the app fails at the first missing key\n * with an error that rarely names it. Writing the keys (never the values —\n * Vercel returns ciphertext, and secrets don't belong on disk unasked) tells\n * the user exactly what to fill in. Mist-managed keys (DATABASE_URL,\n * FLUID_*) are left out: local dev provides its own.\n *\n * The placeholders are commented out on purpose. An uncommented `KEY=`\n * defines the variable as an empty string, which `process.env.KEY ?? default`\n * and Next.js's NEXT_PUBLIC_ inlining treat differently from unset — so an\n * empty placeholder could break an app that works with the key absent.\n */\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { EnvVar } from \"../types.js\";\n\n/** Env files Next.js and Vite load in local development. */\nexport const LOCAL_ENV_FILES = [\n \".env\",\n \".env.local\",\n \".env.development\",\n \".env.development.local\",\n] as const;\n\nconst ENV_ASSIGNMENT = /^\\s*#?\\s*(?:export\\s+)?([A-Za-z_][A-Za-z0-9_]*)\\s*=/;\n\n/** Keys assigned (or already listed as a placeholder) in the local env files. */\nexport function localEnvKeys(dir: string): Set<string> {\n const keys = new Set<string>();\n for (const name of LOCAL_ENV_FILES) {\n const file = join(dir, name);\n if (!existsSync(file)) continue;\n for (const line of readFileSync(file, \"utf8\").split(/\\r?\\n/)) {\n const key = ENV_ASSIGNMENT.exec(line)?.[1];\n if (key) keys.add(key);\n }\n }\n return keys;\n}\n\n/** Production keys a user set that the checkout doesn't define locally. */\nexport function missingProductionKeys(\n envVars: readonly EnvVar[],\n localKeys: ReadonlySet<string>,\n): string[] {\n const missing = new Set<string>();\n for (const v of envVars) {\n if (v.managed_by === \"mist\") continue;\n if (!v.targets.includes(\"production\")) continue;\n if (localKeys.has(v.key)) continue;\n missing.add(v.key);\n }\n return [...missing].sort();\n}\n\n/** The block appended to `.env.local`, or \"\" when nothing is missing. */\nexport function renderEnvPlaceholders(keys: readonly string[]): string {\n if (keys.length === 0) return \"\";\n return [\n \"# Set in production but not here. Uncomment and fill in the ones this\",\n \"# app needs locally (values are never copied — see `fluid mist env pull`).\",\n ...keys.map((key) => `# ${key}=`),\n \"\",\n ].join(\"\\n\");\n}\n","/**\n * `fluid mist clone <slug>` — mints a fresh git credential and clones\n * into ./<vendor-name>/, writes a `.mist` marker file. Spec §3 + §11.\n */\n\nimport chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport ora from \"ora\";\nimport { appendFileSync, existsSync, readFileSync } from \"node:fs\";\nimport { join, resolve } from \"node:path\";\nimport { createMistClient } from \"../api/client.js\";\nimport { showMist } from \"../api/mists.js\";\nimport { createGitCredential } from \"../api/git_credential.js\";\nimport { runAction } from \"../lib/error-handler.js\";\nimport { resolveSlug, writeMistFile } from \"../lib/slug-resolver.js\";\nimport { ensureGitAvailable, gitClone, isGitIgnored } from \"../lib/git.js\";\nimport { listEnvVars } from \"../api/env_vars.js\";\nimport {\n localEnvKeys,\n missingProductionKeys,\n renderEnvPlaceholders,\n} from \"../lib/env-placeholders.js\";\nimport type { MistClient } from \"../api/client.js\";\n\ninterface Opts {\n dir?: string;\n}\n\n/**\n * Render a path for the copy-pasteable `cd` hint so the printed command\n * enters the directory that was actually created:\n * - Prefixes `./` when the path starts with `~` (blocks tilde expansion —\n * the clone went into a literal `~…` directory under CWD) or `-` (stops\n * `cd` from parsing it as an option).\n * - Otherwise single-quotes anything beyond safe characters, escaping\n * embedded single quotes POSIX-style (`'\\''`).\n */\nexport function quotePathIfNeeded(path: string): string {\n const prefixed = /^[~-]/.test(path) ? `./${path}` : path;\n if (/^[A-Za-z0-9_./-]+$/.test(prefixed)) {\n return prefixed;\n }\n return `'${prefixed.replace(/'/g, \"'\\\\''\")}'`;\n}\n\nexport const cloneCommand = new Command(\"clone\")\n .description(\"Clone the mist's GitHub repo (mints a fresh token per call)\")\n .argument(\"[slug]\", \"The mist slug (defaults to .mist file in CWD)\")\n .option(\"--dir <dir>\", \"Target directory (defaults to ./<vendor-name>/)\")\n .action((slug: string | undefined, opts: Opts) =>\n runAction(async () => {\n await ensureGitAvailable();\n const resolvedSlug = resolveSlug(slug);\n const client = createMistClient();\n\n const spinner = ora(\"Resolving mist…\").start();\n const { mist } = await showMist(client, resolvedSlug);\n spinner.text = \"Minting git credential…\";\n const { git_credential: cred } = await createGitCredential(\n client,\n mist.slug,\n );\n // `vendor_name` is frequently null in production (it's only set for\n // older records) — fall back through `name` to the always-present\n // `slug` so a bare `fluid mist clone <slug>` never crashes trying to\n // resolve/join an undefined path.\n const dirName = mist.name ?? mist.vendor_name ?? mist.slug;\n const targetDir = resolve(opts.dir ?? dirName);\n if (existsSync(targetDir)) {\n spinner.fail(\n `Directory ${targetDir} already exists. Pass --dir or remove it first.`,\n );\n process.exit(1);\n }\n spinner.text = `Cloning into ${dirName}/…`;\n try {\n // Pass the token-bearing URL inline — never written to .git/config.\n await gitClone(cred.remote_url, targetDir);\n } catch (err) {\n spinner.fail(\"git clone failed\");\n throw err;\n }\n // Drop a `.mist` so future commands run in this dir can skip the slug.\n writeMistFile(targetDir, mist);\n spinner.succeed(`Cloned into ${chalk.cyan(targetDir)}`);\n await writeEnvPlaceholders(client, mist.id || mist.slug, targetDir);\n // Display the actual directory in the cd hint, not the mist's display name.\n // Use opts.dir if provided by the user, otherwise use relative path.\n const displayPath = opts.dir ?? join(\".\", dirName);\n console.log(chalk.dim(` cd ${quotePathIfNeeded(displayPath)}`));\n if (mist.public_url)\n console.log(chalk.dim(` ${chalk.cyan(mist.public_url)}`));\n }),\n );\n\n/**\n * List production env keys this checkout has no local value for as\n * commented placeholders in `.env.local` — keys only, never values. Only\n * when `.env.local` is git-ignored, so the file can't be committed.\n * Best-effort: a clone never fails over it.\n */\nexport async function writeEnvPlaceholders(\n client: MistClient,\n slug: string,\n targetDir: string,\n): Promise<number> {\n try {\n if (!(await isGitIgnored(targetDir, \".env.local\"))) return 0;\n const { env_vars } = await listEnvVars(client, slug);\n const keys = missingProductionKeys(env_vars ?? [], localEnvKeys(targetDir));\n const block = renderEnvPlaceholders(keys);\n if (!block) return 0;\n const target = join(targetDir, \".env.local\");\n const existing = existsSync(target) ? readFileSync(target, \"utf8\") : \"\";\n const separator =\n existing.length > 0 && !existing.endsWith(\"\\n\") ? \"\\n\" : \"\";\n appendFileSync(\n target,\n `${separator}${existing ? \"\\n\" : \"\"}${block}`,\n \"utf8\",\n );\n console.log(\n chalk.dim(\n ` Listed ${keys.length} production env key(s) in .env.local to fill in: ${keys.join(\", \")}`,\n ),\n );\n return keys.length;\n } catch {\n return 0;\n }\n}\n","/** Deployments — `--watch` polls these. Spec §4. */\n\nimport type { MistClient } from \"./client.js\";\nimport type {\n DeploymentShowResponse,\n DeploymentsListResponse,\n} from \"../types.js\";\nimport { resolveMistId } from \"./id-resolver.js\";\n\nexport async function listDeployments(\n client: MistClient,\n slug: string,\n limit?: number,\n): Promise<DeploymentsListResponse> {\n const id = await resolveMistId(client, slug);\n return client.get<DeploymentsListResponse>(\n `/api/v202604/mists/${id}/deployments`,\n limit != null ? { limit } : undefined,\n );\n}\n\nexport async function showDeployment(\n client: MistClient,\n slug: string,\n deploymentId: string,\n): Promise<DeploymentShowResponse> {\n const id = await resolveMistId(client, slug);\n return client.get<DeploymentShowResponse>(\n `/api/v202604/mists/${id}/deployments/${deploymentId}`,\n );\n}\n","/**\n * `GET /api/me` — fetch the authenticated user's identity. Used by the\n * push command to stamp commits in the canonical GitSync format\n * (`<name>: <action> · <timestamp> (<public_id>)`, see\n * `@fluid-app/fluid-cli`'s git-sync/commit-subject.ts) so deploy history\n * can be traced back to a real person even when the underlying git author\n * falls through to the synthetic bot identity.\n */\n\nimport type { MistClient } from \"./client.js\";\n\nexport interface MistMe {\n id: number;\n public_id: string;\n full_name: string | null;\n first_name: string | null;\n last_name: string | null;\n email: string | null;\n}\n\ninterface MeResponse {\n id: number;\n public_id?: string;\n full_name?: string;\n first_name?: string;\n last_name?: string;\n email?: string;\n}\n\nexport async function fetchCurrentUser(client: MistClient): Promise<MistMe> {\n const raw = await client.get<MeResponse>(\"/api/me\");\n const composed = [raw.first_name, raw.last_name]\n .filter(Boolean)\n .join(\" \")\n .trim();\n const fullName = raw.full_name ?? (composed.length > 0 ? composed : null);\n return {\n id: raw.id,\n public_id: raw.public_id ?? String(raw.id),\n full_name: fullName,\n first_name: raw.first_name ?? null,\n last_name: raw.last_name ?? null,\n email: raw.email ?? null,\n };\n}\n","import { execFile } from \"node:child_process\";\nimport { readFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { promisify } from \"node:util\";\n\nconst execFileAsync = promisify(execFile);\n\n/** The marker Mist Desktop writes when it rolls a checkout back. */\nexport const ROLLBACK_MARKER = \".mist-rollback.json\";\n\n/**\n * \"Roll back to 492c088 — Document reviews env vars\" for a push that carries\n * a fresh rollback marker, so History names what was restored instead of\n * \"Delete 23 files, update 8, add 1\". Null when there is no readable marker;\n * the caller then describes the diff as before.\n */\nexport async function rollbackActionFromMarker(\n cwd: string,\n): Promise<string | null> {\n let restored: unknown;\n try {\n const raw = await readFile(path.join(cwd, ROLLBACK_MARKER), \"utf8\");\n restored = (JSON.parse(raw) as { restored_commit?: unknown })\n .restored_commit;\n } catch {\n return null;\n }\n if (typeof restored !== \"string\" || !/^[0-9a-f]{7,40}$/i.test(restored)) {\n return null;\n }\n let subject = \"\";\n try {\n const { stdout } = await execFileAsync(\n \"git\",\n [\"log\", \"-1\", \"--format=%s\", restored],\n { cwd, windowsHide: true },\n );\n subject = stdout.trim();\n } catch {\n // The version may not be in this clone; the sha alone still says what\n // was restored.\n }\n return rollbackAction(restored, subject);\n}\n\n/** Pure part of {@link rollbackActionFromMarker}, for tests. */\nexport function rollbackAction(sha: string, subject: string): string {\n const short = sha.slice(0, 7);\n const title = subjectAction(subject);\n const base = `Roll back to ${short}`;\n if (!title) return base;\n const full = `${base} — ${title}`;\n return full.length > 72 ? `${full.slice(0, 71).trimEnd()}…` : full;\n}\n\n/** A Git Sync stamp: \"Name: action · YYYY-MM-DD HH:MM:SS (public id)\". */\nconst STAMPED_SUBJECT_RE =\n /^.+?: (.+) · \\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}(?: \\([^)]*\\))?$/;\n\n/** A stamped subject contributes only its action; others pass through. */\nfunction subjectAction(subject: string): string {\n const trimmed = subject.trim();\n return trimmed.match(STAMPED_SUBJECT_RE)?.[1]?.trim() ?? trimmed;\n}\n","/**\n * `fluid mist push [--watch]` — mints a credential, pushes to origin, and\n * (with --watch) polls /deployments until READY/ERROR. Spec §3 + §4.\n */\n\nimport chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport ora from \"ora\";\nimport {\n gitSyncActorFromMe,\n gitSyncCommitSubject,\n resolveGitSyncActor,\n summarizeChanges,\n} from \"@fluid-app/fluid-cli\";\nimport { createMistClient } from \"../api/client.js\";\nimport { createGitCredential } from \"../api/git_credential.js\";\nimport { listDeployments, showDeployment } from \"../api/deployments.js\";\nimport { fetchCurrentUser } from \"../api/me.js\";\nimport { showMist } from \"../api/mists.js\";\nimport { runAction } from \"../lib/error-handler.js\";\nimport { readyDeploymentMessage } from \"../lib/output.js\";\nimport { resolveSlug } from \"../lib/slug-resolver.js\";\nimport {\n ROLLBACK_MARKER,\n rollbackActionFromMarker,\n} from \"../lib/rollback-subject.js\";\nimport {\n ensureGitAvailable,\n gitAheadBehindVsFetchHead,\n gitAutoCommit,\n gitFetch,\n gitMergeFastForward,\n gitPush,\n gitStash,\n gitStashPop,\n getHeadSha,\n hasUncommittedChanges,\n} from \"../lib/git.js\";\n\n/** Used when there's no `-m` and no readable diff to describe. */\nconst FALLBACK_ACTION = \"Publish from Mist Desktop\";\n\ninterface Opts {\n watch?: boolean;\n branch?: string;\n /** Skip the auto-commit step. Power users who manage their own\n * commits can pass --no-commit to fall back to a bare git push. */\n commit?: boolean;\n /** Custom commit message for the auto-commit. Defaults to a\n * timestamped \"Publish from Mist\" line. */\n message?: string;\n}\n\nconst POLL_MS = 2_000;\n// GitHub/hosting webhook queues can occasionally take several minutes before\n// Fluid receives a deployment record. Keep the interactive watch bounded, but\n// don't misclassify a successful authenticated Git push as a failed publish.\nconst LOCATE_TIMEOUT_MS = 2 * 60_000;\nconst READY_TIMEOUT_MS = 5 * 60_000; // 5 minutes for it to flip to ready\nconst PUBLIC_URL_TIMEOUT_MS = 5_000;\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((r) => setTimeout(r, ms));\n}\n\nasync function withTimeout<T>(\n promise: Promise<T>,\n timeoutMs: number,\n): Promise<T> {\n let timer: ReturnType<typeof setTimeout> | null = null;\n try {\n return await Promise.race([\n promise,\n new Promise<never>((_, reject) => {\n timer = setTimeout(\n () => reject(new Error(`timed out after ${timeoutMs / 1000}s`)),\n timeoutMs,\n );\n }),\n ]);\n } finally {\n if (timer) clearTimeout(timer);\n }\n}\n\nexport const pushCommand = new Command(\"push\")\n .description(\n \"Commit working-tree changes and push to the Mist-managed remote (triggers a hosted deployment)\",\n )\n .argument(\"[slug]\", \"The mist slug (defaults to .mist file in CWD)\")\n .option(\"--watch\", \"Poll /deployments until ready or error\")\n .option(\"--branch <name>\", \"Branch to push (default: main)\")\n .option(\n \"--no-commit\",\n \"Skip the auto-commit step (push existing commits only)\",\n )\n .option(\n \"-m, --message <message>\",\n \"Commit message for the auto-commit (default: timestamped Publish)\",\n )\n .action((slug: string | undefined, opts: Opts) =>\n runAction(async () => {\n await ensureGitAvailable();\n const resolved = resolveSlug(slug);\n const client = createMistClient();\n const branch = opts.branch ?? \"main\";\n const cwd = process.cwd();\n\n // Step 0: detect remote drift. Fetch origin/main and check how\n // far HEAD is from it. If a teammate pushed since we last\n // synced, fast-forward in (auto-pull). Stash any dirty files\n // first so the FF can land cleanly; pop after.\n //\n // Diverged histories (we have local commits AND the remote moved)\n // would need a real rebase + conflict resolution — we bail\n // explicitly so the user knows to handle that case in a terminal.\n // For solo developers and non-overlapping concurrent edits, this\n // path Just Works.\n const driftSpinner = ora(\"Checking for remote changes…\").start();\n try {\n // Mint a short-lived credential for the fetch. We'll mint\n // another one later for push (tokens are ~1h but minting\n // again costs nothing and keeps the failure boundaries clear).\n const { git_credential: fetchCred } = await createGitCredential(\n client,\n resolved,\n );\n await gitFetch(cwd, fetchCred.remote_url, branch);\n const { ahead, behind } = await gitAheadBehindVsFetchHead(cwd);\n if (behind > 0 && ahead === 0) {\n // Safe auto-pull case.\n const wasDirty = await hasUncommittedChanges(cwd);\n driftSpinner.text = `Pulling ${behind} commit${behind === 1 ? \"\" : \"s\"} from origin/main…`;\n // Captured before the pull: a pop that collides with the\n // teammate's change resets back here and re-applies the stash,\n // so conflict markers can never reach the auto-commit below.\n const preHead = await getHeadSha(cwd);\n if (wasDirty) {\n await gitStash(cwd, \"mist-push: pre-pull stash\");\n }\n try {\n await gitMergeFastForward(cwd);\n } finally {\n if (wasDirty) {\n await gitStashPop(cwd, preHead); // throws GitError on conflict\n }\n }\n driftSpinner.succeed(\n `Pulled ${behind} commit${behind === 1 ? \"\" : \"s\"} from origin/main`,\n );\n } else if (behind > 0 && ahead > 0) {\n driftSpinner.fail(\n `Your branch and origin/main have diverged (you're ${ahead} ahead, ${behind} behind).`,\n );\n console.error(\n chalk.yellow(\n \" Resolve in a terminal: `git pull --rebase`, fix any conflicts, then re-run Publish.\",\n ),\n );\n process.exit(1);\n } else {\n driftSpinner.succeed(\"Up to date with origin/main\");\n }\n } catch (err) {\n // Wrap fetch/merge errors with context — the spinner already\n // failed via the explicit branch above when we knew enough.\n // Anything that throws past the auto-pull path is unexpected.\n if (\n err instanceof Error &&\n err.message.includes(\"Resolve in a terminal\")\n ) {\n throw err;\n }\n driftSpinner.fail(\"Couldn't sync with origin/main\");\n throw err;\n }\n\n // Step 1: auto-commit working-tree changes (unless --no-commit).\n // Mist Desktop's Publish UX expects \"ship my changes\" to actually\n // include the changes — without this, `git push` is a no-op against\n // a clean HEAD and Vercel never builds.\n const wantAutoCommit = opts.commit !== false;\n if (wantAutoCommit) {\n const dirty = await hasUncommittedChanges(cwd);\n if (dirty) {\n // Stamp the commit with the Fluid user so deploy history\n // traces back to a real person, in the canonical GitSync shape\n // shared with the theme / portal / widget CLIs:\n // \"Mike Tingey: Updated header · 2026-08-06 12:00:00 (0182d21e-…)\"\n // Best-effort: if /api/me fails, hangs, or returns a malformed\n // body we commit as a `Fluid:` system commit — publish is\n // essential, the attribution is not.\n // An explicit `-m` states the INTENT, which no diff can\n // recover — it always wins. Otherwise describe what actually\n // changed, falling back to the old constant for a diff we\n // can't read (summarizeChanges returns null on an empty set).\n const explicit = opts.message?.trim();\n // A rollback from Mist Desktop leaves a marker naming the restored\n // version; say that rather than counting the files it touched.\n const rollbackAction = explicit\n ? null\n : await rollbackActionFromMarker(cwd);\n // Cached on the profile after the first push — /api/me can be\n // slow and the name it returns changes approximately never.\n const actor = await resolveGitSyncActor(async () => {\n try {\n return gitSyncActorFromMe(\n await withTimeout(fetchCurrentUser(client), 5_000),\n );\n } catch (err) {\n const reason = err instanceof Error ? err.message : String(err);\n console.warn(\n chalk.yellow(\n ` (skipping user-stamp on commit — couldn't fetch /api/me: ${reason})`,\n ),\n );\n return null;\n }\n });\n // The subject can only be built once staging settles, so it\n // isn't known when the spinner starts — capture it from the\n // resolver and report it on success instead.\n let subject = \"\";\n const commitSpinner = ora(\"Committing changes…\").start();\n try {\n await gitAutoCommit(cwd, (changes) => {\n subject = gitSyncCommitSubject(\n explicit ||\n (rollbackAction &&\n changes.some((change) => change.path === ROLLBACK_MARKER)\n ? rollbackAction\n : null) ||\n summarizeChanges(changes) ||\n FALLBACK_ACTION,\n actor,\n );\n return subject;\n });\n commitSpinner.succeed(\n subject ? `Committed — ${subject}` : \"Committed\",\n );\n } catch (err) {\n commitSpinner.fail(\"Commit failed\");\n throw err;\n }\n }\n }\n\n const spinner = ora(\"Minting git credential…\").start();\n const { git_credential: cred } = await createGitCredential(\n client,\n resolved,\n );\n spinner.text = `Pushing ${branch} to origin…`;\n try {\n await gitPush(cwd, cred.remote_url, branch);\n } catch (err) {\n spinner.fail(\"git push failed\");\n throw err;\n }\n spinner.succeed(\"Push complete\");\n\n if (!opts.watch) return;\n\n // Snapshot the commit sha we just pushed so we can match the right\n // deployment in the (common) case where older deployments already\n // exist on this mist. Best-effort: if `git rev-parse HEAD` fails we\n // fall back to taking the newest entry.\n let pushedSha: string | null = null;\n try {\n const { execa } = await import(\"execa\");\n const r = await execa(\"git\", [\"rev-parse\", \"HEAD\"], { cwd });\n pushedSha = r.stdout.trim();\n } catch {\n // git unavailable / not a repo — fall back to newest-deployment heuristic\n }\n\n // Find the deployment our push triggered. We poll /deployments\n // for an entry whose commit_sha matches what we just pushed.\n const locateStart = Date.now();\n let deploymentId: string | null = null;\n let attempt = 0;\n while (Date.now() - locateStart < LOCATE_TIMEOUT_MS) {\n attempt++;\n const { deployments } = await listDeployments(client, resolved, 5);\n const match = pushedSha\n ? deployments.find((d) => d.commit_sha === pushedSha)\n : deployments[0];\n if (match) {\n deploymentId = match.id;\n // Print so users see something happen even in non-TTY where\n // ora's spinner doesn't redraw.\n console.log(`Located deployment ${match.id} (${match.state})`);\n break;\n }\n if (attempt === 1) {\n console.log(\n `Locating deployment for commit ${pushedSha?.slice(0, 7) ?? \"(unknown)\"}…`,\n );\n } else if (attempt % 5 === 0) {\n // Status ping every 10s so the user knows we're still working.\n console.log(` …still waiting (${attempt * 2}s elapsed)`);\n }\n await sleep(POLL_MS);\n }\n if (!deploymentId) {\n console.warn(\n chalk.yellow(\n `Push succeeded, but hosting has not started commit ${pushedSha?.slice(0, 7) ?? \"(unknown)\"} after ${LOCATE_TIMEOUT_MS / 1000}s.`,\n ),\n );\n console.warn(\n chalk.yellow(\n \"The deployment is still queued and may appear later. Run `fluid mist deployments` to check it — do not retry or rewrite the commit just because the hosting queue is slow.\",\n ),\n );\n return;\n }\n\n // Watch the located deployment until it's in a terminal state.\n const readyStart = Date.now();\n let lastState: string | null = null;\n while (Date.now() - readyStart < READY_TIMEOUT_MS) {\n const { deployment } = await showDeployment(\n client,\n resolved,\n deploymentId,\n );\n if (deployment.state !== lastState) {\n console.log(` ${deployment.state}…`);\n lastState = deployment.state;\n }\n if (deployment.state === \"ready\") {\n // The deployment URL is an ephemeral provider address and can be\n // protected by provider SSO. Resolve the stable Fluid public URL\n // instead so the success link is one the app's users can open.\n let publicUrl: string | null = null;\n try {\n publicUrl = (\n await withTimeout(\n showMist(client, resolved),\n PUBLIC_URL_TIMEOUT_MS,\n )\n ).mist.public_url;\n } catch (err) {\n const reason = err instanceof Error ? err.message : String(err);\n console.warn(\n chalk.yellow(\n ` Deployment is ready, but the public URL lookup failed: ${reason}`,\n ),\n );\n }\n console.log(readyDeploymentMessage(publicUrl, resolved));\n return;\n }\n if (deployment.state === \"error\" || deployment.state === \"canceled\") {\n console.error(`✘ Deployment ${deployment.state}`);\n process.exit(3);\n }\n await sleep(POLL_MS);\n }\n console.error(\n `Watch timed out after ${READY_TIMEOUT_MS / 1000}s. Last state: ${lastState}.`,\n );\n console.error(\"Run `fluid mist deployments` to check the latest state.\");\n process.exit(3);\n }),\n );\n","/** `fluid mist pull` — git pull origin main. Spec §3. */\n\nimport { Command } from \"commander\";\nimport ora from \"ora\";\nimport { createMistClient } from \"../api/client.js\";\nimport { createGitCredential } from \"../api/git_credential.js\";\nimport { runAction } from \"../lib/error-handler.js\";\nimport { resolveSlug } from \"../lib/slug-resolver.js\";\nimport { ensureGitAvailable, gitPull } from \"../lib/git.js\";\n\ninterface Opts {\n branch?: string;\n}\n\nexport const pullCommand = new Command(\"pull\")\n .description(\"git pull from the Mist-managed remote\")\n .argument(\"[slug]\", \"The mist slug (defaults to .mist file in CWD)\")\n .option(\"--branch <name>\", \"Branch to pull (default: main)\")\n .action((slug: string | undefined, opts: Opts) =>\n runAction(async () => {\n await ensureGitAvailable();\n const resolved = resolveSlug(slug);\n const client = createMistClient();\n const branch = opts.branch ?? \"main\";\n const spinner = ora(\"Minting git credential…\").start();\n const { git_credential: cred } = await createGitCredential(\n client,\n resolved,\n );\n spinner.text = `Pulling ${branch}…`;\n try {\n await gitPull(process.cwd(), cred.remote_url, branch);\n } catch (err) {\n spinner.fail(\"git pull failed\");\n throw err;\n }\n spinner.succeed(\"Pull complete\");\n }),\n );\n","/** `fluid mist open` — opens public_url in the default browser. Spec §3. */\n\nimport chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport open from \"open\";\nimport { createMistClient } from \"../api/client.js\";\nimport { showMist } from \"../api/mists.js\";\nimport { runAction } from \"../lib/error-handler.js\";\nimport { resolveSlug } from \"../lib/slug-resolver.js\";\n\nexport const openCommand = new Command(\"open\")\n .description(\"Open the mist's public URL in your browser\")\n .argument(\"[slug]\", \"The mist slug (defaults to .mist file in CWD)\")\n .action((slug: string | undefined) =>\n runAction(async () => {\n const resolved = resolveSlug(slug);\n const client = createMistClient();\n const { mist } = await showMist(client, resolved);\n if (!mist.public_url) {\n console.error(\n chalk.yellow(\n `Mist ${mist.slug} has no public URL yet (state: ${mist.state}).`,\n ),\n );\n process.exit(1);\n }\n console.log(chalk.cyan(mist.public_url));\n await open(mist.public_url);\n }),\n );\n","/** `fluid mist deployments` — list recent deployments. Spec §3. */\n\nimport chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport { createMistClient } from \"../api/client.js\";\nimport { listDeployments } from \"../api/deployments.js\";\nimport { runAction } from \"../lib/error-handler.js\";\nimport { resolveSlug } from \"../lib/slug-resolver.js\";\nimport { printJson, printTable, relativeDate } from \"../lib/output.js\";\n\ninterface Opts {\n limit?: string;\n json?: boolean;\n}\n\nconst STATE_COLOR: Record<string, (s: string) => string> = {\n ready: chalk.green,\n building: chalk.yellow,\n queued: chalk.dim,\n error: chalk.red,\n canceled: chalk.red,\n};\n\nexport const deploymentsCommand = new Command(\"deployments\")\n .description(\"List recent deployments for a mist\")\n .argument(\"[slug]\", \"The mist slug (defaults to .mist file in CWD)\")\n .option(\"--limit <n>\", \"How many to fetch (default 10)\")\n .option(\"--json\", \"Emit raw API JSON\")\n .action((slug: string | undefined, opts: Opts) =>\n runAction(async () => {\n const resolved = resolveSlug(slug);\n const client = createMistClient();\n const limit = opts.limit ? parseInt(opts.limit, 10) : 10;\n const response = await listDeployments(client, resolved, limit);\n if (opts.json) return printJson(response);\n const deployments = response.deployments ?? [];\n if (deployments.length === 0) {\n console.log(chalk.dim(\"No deployments yet.\"));\n return;\n }\n const header = [\n chalk.bold(\"ID\"),\n chalk.bold(\"STATE\"),\n chalk.bold(\"COMMIT\"),\n chalk.bold(\"MESSAGE\"),\n chalk.bold(\"AGE\"),\n ];\n const rows = deployments.map((d) => [\n d.id,\n (STATE_COLOR[d.state] ?? chalk.white)(d.state),\n d.commit_sha ? d.commit_sha.slice(0, 7) : chalk.dim(\"—\"),\n d.commit_message ? d.commit_message.slice(0, 48) : chalk.dim(\"—\"),\n relativeDate(d.created_at),\n ]);\n printTable([header, ...rows]);\n }),\n );\n","/** Logs — `--tail` polls every 2s with `since`. Spec §4. */\n\nimport type { MistClient } from \"./client.js\";\nimport type { LogsResponse } from \"../types.js\";\nimport { resolveMistId } from \"./id-resolver.js\";\n\nexport interface FetchLogsParams {\n deployment_id?: string;\n /** ISO-8601 — only logs newer than this. Used by --tail. */\n since?: string;\n limit?: number;\n}\n\nexport async function fetchLogs(\n client: MistClient,\n slug: string,\n params: FetchLogsParams = {},\n): Promise<LogsResponse> {\n const id = await resolveMistId(client, slug);\n return client.get<LogsResponse>(\n `/api/v202604/mists/${id}/logs`,\n params as Record<string, unknown>,\n );\n}\n","/** `fluid mist logs [--tail]` — poll /logs?since=<last> every 2s. Spec §4. */\n\nimport chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport { createMistClient } from \"../api/client.js\";\nimport { fetchLogs } from \"../api/logs.js\";\nimport { runAction } from \"../lib/error-handler.js\";\nimport { resolveSlug } from \"../lib/slug-resolver.js\";\nimport { printJson } from \"../lib/output.js\";\nimport type { LogEntry } from \"../types.js\";\n\ninterface Opts {\n tail?: boolean;\n limit?: string;\n deployment?: string;\n json?: boolean;\n}\n\nconst POLL_MS = 2_000;\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((r) => setTimeout(r, ms));\n}\n\nfunction levelColor(level: LogEntry[\"level\"]): (s: string) => string {\n return level === \"stderr\" ? chalk.red : chalk.dim;\n}\n\nfunction printLine(entry: LogEntry): void {\n const ts = entry.timestamp.slice(11, 19);\n process.stdout.write(\n `${chalk.dim(ts)} ${levelColor(entry.level)(entry.level.padEnd(6))} ${entry.message}\\n`,\n );\n}\n\nexport const logsCommand = new Command(\"logs\")\n .description(\"Fetch deployment logs (use --tail to stream)\")\n .argument(\"[slug]\", \"The mist slug (defaults to .mist file in CWD)\")\n .option(\"--tail\", \"Stream new logs every 2s\")\n .option(\"--limit <n>\", \"Initial fetch size (default 200)\")\n .option(\"--deployment <id>\", \"Logs from a specific deployment ID\")\n .option(\"--json\", \"Emit raw API JSON (one batch; ignored when --tail)\")\n .action((slug: string | undefined, opts: Opts) =>\n runAction(async () => {\n const resolved = resolveSlug(slug);\n const client = createMistClient();\n const limit = opts.limit ? parseInt(opts.limit, 10) : 200;\n\n const initial = await fetchLogs(client, resolved, {\n ...(opts.deployment ? { deployment_id: opts.deployment } : {}),\n limit,\n });\n if (opts.json && !opts.tail) return printJson(initial);\n for (const entry of initial.logs) printLine(entry);\n\n if (!opts.tail) return;\n\n // Say the stream is live. A quiet deployment can go minutes without a\n // line, and without this the tail (and Mist's Production logs tab,\n // which waits for the first line) looks like it never connected.\n process.stdout.write(\n `${chalk.dim(\n `Streaming production logs for ${resolved}${\n initial.deployment_id ? ` (deployment ${initial.deployment_id})` : \"\"\n } — waiting for new entries…`,\n )}\\n`,\n );\n\n let since =\n initial.logs.length > 0\n ? initial.logs[initial.logs.length - 1]!.timestamp\n : new Date().toISOString();\n\n // Tail until Ctrl-C. Plain polling loop, no fancy backoff.\n while (true) {\n await sleep(POLL_MS);\n const batch = await fetchLogs(client, resolved, {\n ...(opts.deployment ? { deployment_id: opts.deployment } : {}),\n since,\n limit,\n });\n for (const entry of batch.logs) {\n printLine(entry);\n since = entry.timestamp;\n }\n }\n }),\n );\n","/**\n * Detects Vercel-encrypted env var ciphertext.\n *\n * `GET /api/v202604/mists/:id/env_vars` is a thin proxy to the Mist's\n * Vercel project env — it returns values ENCRYPTED, never plaintext (see\n * apps/mist-desktop/src/main/services/mist-env-vars.service.ts:25-28,\n * which documents the same server behavior for the desktop app's copy of\n * this API). The ciphertext is a base64-encoded JSON envelope of shape\n * `{\"v\":\"v2\",\"c\":\"<base64 ciphertext>\",\"k\":[<byte array>]}`.\n *\n * `fluid mist env pull` uses this to avoid writing that ciphertext into\n * `.env.local` as if it were a usable value.\n */\n\nconst BASE64_RE = /^[A-Za-z0-9+/]+={0,2}$/;\n\nfunction looksLikeBase64(value: string): boolean {\n return value.length > 0 && value.length % 4 === 0 && BASE64_RE.test(value);\n}\n\nfunction hasEncryptedEnvelopeShape(value: unknown): boolean {\n return (\n typeof value === \"object\" &&\n value !== null &&\n !Array.isArray(value) &&\n \"v\" in value &&\n \"c\" in value &&\n \"k\" in value\n );\n}\n\n/**\n * True when `value` is the Vercel encrypted-env envelope: a base64 string\n * that decodes to a JSON object with the `{ v, c, k }` shape. Returns\n * false for empty strings, non-base64 strings, and base64 strings that\n * decode to plaintext or to JSON that doesn't match the envelope shape —\n * so real plaintext secrets that happen to look like base64 are never\n * misclassified as ciphertext.\n */\nexport function isVercelEncryptedEnvValue(value: string): boolean {\n if (!looksLikeBase64(value)) return false;\n\n let decoded: string;\n try {\n decoded = Buffer.from(value, \"base64\").toString(\"utf8\");\n } catch {\n return false;\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(decoded);\n } catch {\n return false;\n }\n\n return hasEncryptedEnvelopeShape(parsed);\n}\n","/** `fluid mist env list` — list env vars; mark managed_by entries. Spec §4. */\n\nimport chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport { createMistClient } from \"../../api/client.js\";\nimport { listEnvVars } from \"../../api/env_vars.js\";\nimport { isVercelEncryptedEnvValue } from \"../../lib/encrypted-env.js\";\nimport { runAction } from \"../../lib/error-handler.js\";\nimport { resolveSlug } from \"../../lib/slug-resolver.js\";\nimport { printJson, printTable } from \"../../lib/output.js\";\nimport type { EnvVar } from \"../../types.js\";\n\ninterface Opts {\n json?: boolean;\n showValues?: boolean;\n}\n\nfunction maskValue(value: string): string {\n if (value.length <= 8) return \"•\".repeat(value.length);\n return `${value.slice(0, 4)}${\"•\".repeat(8)}${value.slice(-2)}`;\n}\n\n/**\n * Values come back Vercel-encrypted ciphertext (see\n * `../../lib/encrypted-env.ts`) — never show that raw blob, masked or not,\n * since it isn't the real secret and can run to ~1KB of base64.\n */\nfunction displayValue(v: EnvVar, showValues: boolean | undefined): string {\n if (isVercelEncryptedEnvValue(v.value)) return chalk.dim(\"<encrypted>\");\n return showValues ? v.value : maskValue(v.value);\n}\n\nexport const envListCommand = new Command(\"list\")\n .description(\"List a mist's env vars\")\n .argument(\"[slug]\", \"The mist slug (defaults to .mist file in CWD)\")\n .option(\"--show-values\", \"Print full values instead of masking them\")\n .option(\"--json\", \"Emit raw API JSON\")\n .action((slug: string | undefined, opts: Opts) =>\n runAction(async () => {\n const resolved = resolveSlug(slug);\n const client = createMistClient();\n const response = await listEnvVars(client, resolved);\n if (opts.json) return printJson(response);\n const vars = response.env_vars ?? [];\n if (vars.length === 0) {\n console.log(chalk.dim(\"No env vars.\"));\n return;\n }\n const header = [\n chalk.bold(\"KEY\"),\n chalk.bold(\"VALUE\"),\n chalk.bold(\"TARGETS\"),\n chalk.bold(\"MANAGED\"),\n ];\n const rows = vars.map((v) => [\n v.key,\n displayValue(v, opts.showValues),\n v.targets.join(\",\"),\n v.managed_by === \"mist\" ? chalk.magenta(\"mist\") : chalk.dim(\"user\"),\n ]);\n printTable([header, ...rows]);\n }),\n );\n","// Generate cryptographically-strong random secrets for Mist app\n// config — webhook signing tokens, API keys, session secrets, etc.\n//\n// Mist's agent reaches for this when a Mist app needs a secret it doesn't\n// have yet (e.g. a WEBHOOK_AUTH_TOKEN referenced by fluid.capabilities.json,\n// or a session secret) so it can generate one and set it as an env var\n// instead of asking the user to invent one by hand. `fluid mist env set\n// KEY --generate` uses the same generator.\n//\n// Uses Web Crypto (`globalThis.crypto.getRandomValues`, a CSPRNG in Node\n// 19+, Electron and browsers) rather than `node:crypto`, so this shared\n// package stays runtime-agnostic.\n\nexport type SecretEncoding = \"hex\" | \"base64url\" | \"base64\";\n\nexport interface GenerateSecretOptions {\n /** Entropy in bytes. 32 bytes (256-bit) is a sensible default for a\n * signing/session secret. Clamped to [16, 128] so callers can't ask\n * for a uselessly-short or absurdly-long value. */\n bytes?: number;\n /** Output alphabet. base64url is URL/env-safe (no +/=), hex is the\n * most portable, base64 for callers that specifically want it. */\n encoding?: SecretEncoding;\n}\n\nconst MIN_BYTES = 16;\nconst MAX_BYTES = 128;\nconst DEFAULT_BYTES = 32;\n\nexport interface GeneratedSecret {\n secret: string;\n bytes: number;\n encoding: SecretEncoding;\n}\n\n/** Return a random secret. Pure + synchronous so it's trivially\n * testable (entropy + encoding + clamping) without any I/O. */\nexport function generateSecret(\n options: GenerateSecretOptions = {},\n): GeneratedSecret {\n const encoding: SecretEncoding = options.encoding ?? \"base64url\";\n const requested = options.bytes ?? DEFAULT_BYTES;\n const bytes = Math.min(MAX_BYTES, Math.max(MIN_BYTES, Math.trunc(requested)));\n const buf = new Uint8Array(bytes);\n globalThis.crypto.getRandomValues(buf);\n return { secret: encode(buf, encoding), bytes, encoding };\n}\n\nfunction encode(buf: Uint8Array, encoding: SecretEncoding): string {\n if (encoding === \"hex\") {\n return Array.from(buf, (byte) => byte.toString(16).padStart(2, \"0\")).join(\n \"\",\n );\n }\n const base64 = btoa(String.fromCharCode(...buf));\n if (encoding === \"base64\") return base64;\n // base64url: URL/env-safe alphabet, unpadded (matches Node's encoding).\n return base64.replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n}\n","/**\n * `fluid mist env set <slug> KEY=value [KEY=value ...]` — POST for new\n * keys, PATCH for existing. Spec §3 + §4.\n *\n * `fluid mist env set [slug] KEY --generate [--bytes N]` mints a random\n * secret for KEY instead (the generator Mist's `set_mist_env_var` uses),\n * writes it, and prints it exactly once.\n */\n\nimport chalk from \"chalk\";\nimport { Command, InvalidArgumentError } from \"commander\";\nimport { generateSecret } from \"@fluid-app/mist-core/secret-generator\";\nimport { createMistClient, type MistClient } from \"../../api/client.js\";\nimport { createEnvVar, listEnvVars, updateEnvVar } from \"../../api/env_vars.js\";\nimport { runAction } from \"../../lib/error-handler.js\";\nimport { printJson } from \"../../lib/output.js\";\nimport { resolveSlug } from \"../../lib/slug-resolver.js\";\n\ninterface Opts {\n json?: boolean;\n generate?: boolean;\n bytes?: number;\n}\n\n/** Env var name rule enforced by Rails (mirrors Mist's ENV_KEY_RE). */\nconst ENV_KEY_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;\n\nexport const GENERATED_SECRET_NOTICE =\n \"This secret is shown once and won't be shown again; Fluid stores it encrypted. Copy it now if you also need it in .env.local. Redeploy (fluid mist push) for the change to take effect.\";\n\nfunction parsePair(pair: string): { key: string; value: string } {\n const eq = pair.indexOf(\"=\");\n if (eq <= 0) {\n throw new Error(\n `Invalid pair \"${pair}\". Use KEY=value (no leading spaces).`,\n );\n }\n return { key: pair.slice(0, eq), value: pair.slice(eq + 1) };\n}\n\nfunction parseBytes(value: string): number {\n const bytes = Number(value);\n if (!Number.isInteger(bytes) || bytes < 16 || bytes > 128) {\n throw new InvalidArgumentError(\"Use a whole number from 16 to 128.\");\n }\n return bytes;\n}\n\nexport interface GeneratedEnvVarResult {\n key: string;\n action: \"created\" | \"updated\";\n generatedSecret: string;\n bytes: number;\n encoding: string;\n note: string;\n}\n\n/**\n * Generate a secret for `key` and write it to the Mist. Refuses invalid\n * and Mist-managed keys BEFORE generating, so a secret is only ever\n * returned once it has been stored.\n */\nexport async function setGeneratedEnvVar(\n client: MistClient,\n slug: string,\n key: string,\n bytes?: number,\n): Promise<GeneratedEnvVarResult> {\n if (!ENV_KEY_RE.test(key)) {\n throw new Error(\n `Invalid env var name \"${key}\". Use letters, digits and underscores, not starting with a digit.`,\n );\n }\n const existing = await listEnvVars(client, slug);\n const current = (existing.env_vars ?? []).find((v) => v.key === key);\n if (current?.managed_by === \"mist\") {\n throw new Error(\n `\"${key}\" is managed by Fluid and can't be set; it is provisioned automatically for the Mist.`,\n );\n }\n\n const secret = generateSecret(bytes === undefined ? {} : { bytes });\n if (current) await updateEnvVar(client, slug, key, secret.secret);\n else await createEnvVar(client, slug, key, secret.secret);\n return {\n key,\n action: current ? \"updated\" : \"created\",\n generatedSecret: secret.secret,\n bytes: secret.bytes,\n encoding: secret.encoding,\n note: GENERATED_SECRET_NOTICE,\n };\n}\n\nexport const envSetCommand = new Command(\"set\")\n .description(\n \"Set one or more env vars (KEY=value KEY=value …), or generate a secret for KEY with --generate\",\n )\n .argument(\n \"<slug-or-pair>\",\n \"The mist slug, or the first KEY=value pair (or KEY with --generate)\",\n )\n .argument(\"[pairs...]\", \"Additional KEY=value pairs (or KEY with --generate)\")\n .option(\n \"--generate\",\n \"Generate a random secret as the value of one KEY and print it once\",\n )\n .option(\n \"--bytes <n>\",\n \"Entropy for --generate, in bytes (16-128, default 32)\",\n parseBytes,\n )\n .option(\"--json\", \"Emit JSON (with --generate)\")\n .action(async (slugOrPair: string, pairs: string[], opts: Opts) => {\n await runAction(async () => {\n if (opts.generate) {\n await runGenerate([slugOrPair, ...pairs], opts);\n return;\n }\n if (opts.bytes !== undefined) {\n console.error(chalk.red(\"--bytes only applies with --generate.\"));\n process.exit(1);\n }\n // Allow both shapes: `env set <slug> KEY=val` and `env set KEY=val`\n // (with slug coming from .mist file). We detect by checking if the\n // first arg contains an `=`.\n let slug: string;\n let rawPairs: string[];\n if (slugOrPair.includes(\"=\")) {\n slug = resolveSlug();\n rawPairs = [slugOrPair, ...pairs];\n } else {\n slug = resolveSlug(slugOrPair);\n rawPairs = pairs;\n }\n if (rawPairs.length === 0) {\n console.error(chalk.red(\"Pass at least one KEY=value pair.\"));\n process.exit(1);\n }\n const client = createMistClient();\n // Fetch existing to decide POST vs PATCH per key.\n const existing = await listEnvVars(client, slug);\n const existingKeys = new Set((existing.env_vars ?? []).map((v) => v.key));\n for (const raw of rawPairs) {\n const { key, value } = parsePair(raw);\n if (existingKeys.has(key)) {\n await updateEnvVar(client, slug, key, value);\n console.log(`${chalk.green(\"updated\")} ${key}`);\n } else {\n await createEnvVar(client, slug, key, value);\n console.log(`${chalk.cyan(\"created\")} ${key}`);\n }\n }\n });\n });\n\n/** `env set [slug] KEY --generate`: exactly one KEY, without a value. */\nasync function runGenerate(args: string[], opts: Opts): Promise<void> {\n if (args.length > 2) {\n console.error(\n chalk.red(\n \"--generate sets one key at a time: env set [slug] KEY --generate\",\n ),\n );\n process.exit(1);\n }\n const [first, second] = args;\n const key = (second ?? first ?? \"\").trim();\n if (key.includes(\"=\")) {\n console.error(\n chalk.red(\n `--generate creates the value itself; pass the key alone (e.g. ${key.split(\"=\")[0]}).`,\n ),\n );\n process.exit(1);\n }\n const slug = second === undefined ? resolveSlug() : resolveSlug(first);\n const result = await setGeneratedEnvVar(\n createMistClient(),\n slug,\n key,\n opts.bytes,\n );\n if (opts.json) {\n printJson(result);\n return;\n }\n const verb =\n result.action === \"updated\"\n ? chalk.green(\"updated\")\n : chalk.cyan(\"created\");\n console.log(\n `${verb} ${result.key} (${result.bytes}-byte generated secret)`,\n );\n console.log();\n console.log(result.generatedSecret);\n console.log();\n console.log(chalk.yellow(result.note));\n}\n","/** `fluid mist env unset <slug> KEY [KEY ...]` — DELETE per key. Spec §3. */\n\nimport chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport { createMistClient } from \"../../api/client.js\";\nimport { deleteEnvVar } from \"../../api/env_vars.js\";\nimport { runAction } from \"../../lib/error-handler.js\";\nimport { resolveSlug } from \"../../lib/slug-resolver.js\";\n\nexport const envUnsetCommand = new Command(\"unset\")\n .description(\"Delete one or more env vars\")\n .argument(\n \"<slug-or-key>\",\n \"The mist slug, or the first KEY (if .mist file is present)\",\n )\n .argument(\"[keys...]\", \"Additional keys\")\n .action(async (slugOrKey: string, keys: string[]) => {\n await runAction(async () => {\n // Heuristic: KEYs are env-var-shaped (UPPERCASE_WITH_UNDERSCORES);\n // slugs are 6 lowercase alnum chars. We only need it to be specific\n // enough for the common case.\n let slug: string;\n let toDelete: string[];\n if (/^[A-Z_][A-Z0-9_]*$/.test(slugOrKey)) {\n slug = resolveSlug();\n toDelete = [slugOrKey, ...keys];\n } else {\n slug = resolveSlug(slugOrKey);\n toDelete = keys;\n }\n if (toDelete.length === 0) {\n console.error(chalk.red(\"Pass at least one KEY.\"));\n process.exit(1);\n }\n const client = createMistClient();\n for (const key of toDelete) {\n await deleteEnvVar(client, slug, key);\n console.log(`${chalk.red(\"deleted\")} ${key}`);\n }\n });\n });\n","/**\n * `fluid mist env pull` — writes `.env.local` with user-managed vars only.\n * Skips `managed_by: \"mist\"` entries (DATABASE_URL, FLUID_*) since local\n * dev uses PGlite, not the production database. Spec §4 + §5.\n *\n * Also skips user-managed vars whose value comes back as Vercel-encrypted\n * ciphertext (`GET .../env_vars` is a proxy to Vercel's project env, which\n * never returns plaintext — see `../../lib/encrypted-env.ts`). Writing that\n * ciphertext into `.env.local` verbatim would silently produce an unusable\n * file, so those keys are collected and surfaced as a warning instead.\n *\n * The output file is MERGED, not replaced: pulled plaintext values update\n * their existing lines (or are appended), while every other existing line\n * — comments, locally maintained secrets for encrypted/managed keys, and\n * local-only vars — is preserved verbatim. Replacing the file wholesale\n * would delete manually set values for skipped keys, and an all-encrypted\n * response would truncate the file to zero bytes right before telling the\n * user to set those same values locally.\n */\n\nimport chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport { existsSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { createMistClient } from \"../../api/client.js\";\nimport { listEnvVars } from \"../../api/env_vars.js\";\nimport { isVercelEncryptedEnvValue } from \"../../lib/encrypted-env.js\";\nimport { runAction } from \"../../lib/error-handler.js\";\nimport { resolveSlug } from \"../../lib/slug-resolver.js\";\nimport type { EnvVar } from \"../../types.js\";\n\ninterface Opts {\n out?: string;\n}\n\nfunction escapeValue(v: string): string {\n // Quote if value has whitespace, `#`, or quotes — bash-safe.\n if (/[\\s#\"']/.test(v)) {\n return `\"${v.replace(/([\"\\\\$`])/g, \"\\\\$1\")}\"`;\n }\n return v;\n}\n\nexport interface PullPartition {\n /** User-managed vars with a real value — safe to write to `.env.local`. */\n plaintext: EnvVar[];\n /** User-managed vars whose value is Vercel ciphertext — can't be pulled. */\n encrypted: EnvVar[];\n /** Mist-managed vars — never pulled (local dev uses PGlite). */\n managed: EnvVar[];\n}\n\n/**\n * Splits the raw env-var list into what `env pull` can safely write vs.\n * what it must skip, and why. Pure — no I/O — so it's unit-testable\n * without mocking the API client or the filesystem.\n */\nexport function partitionPullEnvVars(\n envVars: readonly EnvVar[],\n): PullPartition {\n const plaintext: EnvVar[] = [];\n const encrypted: EnvVar[] = [];\n const managed: EnvVar[] = [];\n for (const v of envVars) {\n if (v.managed_by === \"mist\") {\n managed.push(v);\n } else if (isVercelEncryptedEnvValue(v.value)) {\n encrypted.push(v);\n } else {\n plaintext.push(v);\n }\n }\n return { plaintext, encrypted, managed };\n}\n\n/** Matches a `KEY=` / `export KEY=` assignment line and captures the key. */\nconst ENV_ASSIGNMENT = /^\\s*(?:export\\s+)?([A-Za-z_][A-Za-z0-9_]*)\\s*=/;\n\n/**\n * Merges pulled plaintext vars into the existing env-file contents.\n * Existing assignment lines for pulled keys are updated in place; every\n * other line (comments, blank lines, locally maintained values for keys\n * the pull skipped) is preserved verbatim; new keys are appended. Pure —\n * no I/O — so it's unit-testable without touching the filesystem.\n */\nexport function mergeEnvContents(\n existing: string,\n plaintext: readonly EnvVar[],\n): string {\n const updates = new Map<string, string>(\n plaintext.map((v) => [v.key, `${v.key}=${escapeValue(v.value)}`]),\n );\n const lines = existing.length > 0 ? existing.split(\"\\n\") : [];\n // A trailing newline yields one empty final element — drop it so the\n // merge doesn't accumulate blank lines across repeated pulls.\n if (lines.at(-1) === \"\") {\n lines.pop();\n }\n const out: string[] = [];\n for (const line of lines) {\n const key = ENV_ASSIGNMENT.exec(line)?.[1];\n const replacement = key === undefined ? undefined : updates.get(key);\n if (key !== undefined && replacement !== undefined) {\n out.push(replacement);\n updates.delete(key);\n } else {\n out.push(line);\n }\n }\n for (const rendered of updates.values()) {\n out.push(rendered);\n }\n return out.length > 0 ? out.join(\"\\n\") + \"\\n\" : \"\";\n}\n\nexport const envPullCommand = new Command(\"pull\")\n .description(\"Write user-managed env vars to .env.local in the CWD\")\n .argument(\"[slug]\", \"The mist slug (defaults to .mist file in CWD)\")\n .option(\"--out <path>\", \"Output path (default: .env.local)\")\n .action((slug: string | undefined, opts: Opts) =>\n runAction(async () => {\n const resolved = resolveSlug(slug);\n const client = createMistClient();\n const { env_vars } = await listEnvVars(client, resolved);\n const { plaintext, encrypted, managed } = partitionPullEnvVars(env_vars);\n const outName = opts.out ?? \".env.local\";\n const target = join(process.cwd(), outName);\n const existing = existsSync(target) ? readFileSync(target, \"utf8\") : \"\";\n // Merge instead of replace: pulled keys update their lines, and\n // everything else in the existing file (locally maintained values\n // for encrypted/managed keys, local-only vars, comments) survives.\n // With zero plaintext vars the merge is a no-op on existing\n // contents, so the command still succeeds and exits 0 rather than\n // truncating the file when every var is ciphertext.\n writeFileSync(target, mergeEnvContents(existing, plaintext), \"utf8\");\n console.log(\n chalk.green(\n `Wrote ${plaintext.length} var(s) to ${target.replace(process.cwd() + \"/\", \"\")}${\n existing.length > 0\n ? \" (existing entries for other keys preserved)\"\n : \"\"\n }.`,\n ),\n );\n if (managed.length > 0) {\n console.log(\n chalk.dim(\n `Skipped ${managed.length} Mist-managed var(s): ${managed.map((v) => v.key).join(\", \")} (local dev uses PGlite)`,\n ),\n );\n }\n if (encrypted.length > 0) {\n console.log(\n chalk.yellow(\n `Skipped ${encrypted.length} var(s) whose values are stored encrypted and can't be pulled: ${encrypted\n .map((v) => v.key)\n .join(\n \", \",\n )} — set them locally in ${outName} yourself (values are visible in the Vercel dashboard) or re-set them via \\`fluid mist env set\\`.`,\n ),\n );\n }\n }),\n );\n","/** `fluid mist env` — sub-command aggregator. */\n\nimport { Command } from \"commander\";\nimport { envListCommand } from \"./list.js\";\nimport { envSetCommand } from \"./set.js\";\nimport { envUnsetCommand } from \"./unset.js\";\nimport { envPullCommand } from \"./pull.js\";\n\nexport const envCommand = new Command(\"env\")\n .description(\"Manage a mist's environment variables\")\n .addCommand(envListCommand)\n .addCommand(envSetCommand)\n .addCommand(envUnsetCommand)\n .addCommand(envPullCommand);\n","/**\n * `fluid mist droplet egress-proxy <on|off> [slug] [--json]`\n *\n * Ask Fluid (on) to let the Mist's attached Droplet reach external systems\n * such as Exigo through its egress proxy, or withdraw the request (off). The\n * proxy admits the droplet on the request alone; no Fluid admin approves it.\n * Leaves an already-correct droplet untouched. Safe Mode treats this as a\n * write (apps/mist-desktop fluid-cli-policy).\n */\n\nimport chalk from \"chalk\";\nimport { Command, InvalidArgumentError } from \"commander\";\nimport { createMistClient } from \"../../api/client.js\";\nimport { setDropletEgressProxy } from \"../../lib/droplet-egress.js\";\nimport { runAction } from \"../../lib/error-handler.js\";\nimport { printJson } from \"../../lib/output.js\";\n\ninterface Opts {\n json?: boolean;\n}\n\nexport function parseEgressProxyState(value: string): boolean {\n const normalized = value.trim().toLowerCase();\n if (normalized === \"on\") return true;\n if (normalized === \"off\") return false;\n throw new InvalidArgumentError('Use \"on\" or \"off\".');\n}\n\nexport const dropletEgressProxyCommand = new Command(\"egress-proxy\")\n .description(\n \"Ask Fluid (on) or stop asking (off) to let the attached Droplet reach external systems such as Exigo through its egress proxy\",\n )\n .argument(\"<state>\", \"on | off\", parseEgressProxyState)\n .argument(\"[slug]\", \"Mist slug (defaults to the .mist file in CWD)\")\n .option(\"--json\", \"Emit JSON\")\n .action((requested: boolean, slug: string | undefined, opts: Opts) =>\n runAction(async () => {\n const result = await setDropletEgressProxy(createMistClient(), {\n slug,\n cwd: process.cwd(),\n requested,\n });\n if (opts.json) return printJson(result);\n\n const target = `droplet ${result.dropletUuid} (${result.mistSlug})`;\n if (!result.requested) {\n console.log(\n result.changed\n ? `${chalk.green(\"✓\")} Egress request withdrawn for ${target}`\n : chalk.dim(`No egress request for ${target}`),\n );\n } else {\n console.log(\n result.changed\n ? `${chalk.green(\"✓\")} Egress requested for ${target}. Fluid's proxy admits requested droplets wherever the proxy honours Fluid's answer.`\n : chalk.dim(`Egress already requested for ${target}`),\n );\n }\n }),\n );\n","/**\n * `fluid mist droplet reinstall [slug] --dry-run | --confirm`\n * [--installation <uuid>] [--resume <operationId>] [--watch-timeout <s>]\n *\n * Port of Mist Desktop's `repair_and_reinstall_droplet`: an IRREVERSIBLE\n * repair plus backend-owned replacement of the Mist's own attached Droplet\n * installation. Same safety rules as Mist:\n *\n * - The Droplet is always the one attached to the Mist; there is no flag\n * to name another, and the backend rejects installations of any other\n * Droplet.\n * - `--dry-run` shows the exact plan and mutates nothing.\n * - `--confirm` executes, and only after a `--dry-run` plan for the same\n * Droplet and installation was shown within the last 15 minutes.\n * `--yes` never approves it.\n * - Execution repairs the lifecycle URLs, starts ONE durable replacement\n * operation, and watches it. It never DELETEs and re-creates an\n * installation itself and never reports success before Fluid does.\n * - A slow replacement returns \"still_running\" with the ids to resume:\n * `--resume <operationId> --installation <uuid>` (both required).\n * - `--installation <uuid>` alone recovers a source installation that a\n * failed attempt already terminalized.\n *\n * Output is always JSON; refusals and failures exit non-zero.\n */\n\nimport { randomUUID } from \"node:crypto\";\nimport { Command, InvalidArgumentError, Option } from \"commander\";\nimport { createMistClient, MistApiError } from \"../../api/client.js\";\nimport { runDropletReinstall } from \"../../lib/droplet-repair.js\";\nimport { runAction } from \"../../lib/error-handler.js\";\nimport { printJson } from \"../../lib/output.js\";\nimport { createFileReinstallPlanStore } from \"../../lib/reinstall-plan-store.js\";\n\ninterface Opts {\n dryRun?: boolean;\n confirm?: boolean;\n yes?: boolean;\n installation?: string;\n resume?: string;\n watchTimeout?: number;\n}\n\nfunction parseSeconds(value: string): number {\n const seconds = Number(value);\n if (!Number.isInteger(seconds) || seconds < 1 || seconds > 3_600) {\n throw new InvalidArgumentError(\"Use a whole number of seconds (1-3600).\");\n }\n return seconds;\n}\n\nexport const dropletReinstallCommand = new Command(\"reinstall\")\n .description(\n \"Irreversibly repair and replace the Mist's attached Droplet installation (plan with --dry-run, then --confirm)\",\n )\n .argument(\"[slug]\", \"Mist slug (defaults to the .mist file in CWD)\")\n .option(\"--dry-run\", \"Show the exact plan without changing anything\")\n .option(\n \"--confirm\",\n \"Execute the plan shown by the latest --dry-run (irreversible)\",\n )\n .option(\n \"--installation <uuid>\",\n \"Recovery: the source installation uuid from an earlier plan or still_running result\",\n )\n .option(\n \"--resume <operationId>\",\n \"Keep watching a replacement that already started (needs --installation)\",\n )\n .option(\n \"--watch-timeout <seconds>\",\n \"How long to watch before returning still_running (default 120)\",\n parseSeconds,\n )\n // Accepted only so it can be refused with a clear message: --yes never\n // approves an irreversible reinstall.\n .addOption(new Option(\"-y, --yes\", \"Not accepted; use --confirm\").hideHelp())\n .action((slug: string | undefined, opts: Opts) =>\n runAction(async () => {\n let outcome;\n try {\n outcome = await runDropletReinstall(\n {\n client: createMistClient(),\n cwd: process.cwd(),\n slug,\n planStore: createFileReinstallPlanStore(),\n newIdempotencyKey: () => `fluid-cli-repair-${randomUUID()}`,\n },\n {\n dryRun: opts.dryRun,\n confirm: opts.confirm,\n yes: opts.yes,\n installationUuid: opts.installation,\n resumeOperationId: opts.resume,\n watchTimeoutMs:\n opts.watchTimeout === undefined\n ? undefined\n : opts.watchTimeout * 1_000,\n },\n );\n } catch (err) {\n // Keep the output JSON on failure too, so scripts can parse it.\n printJson({\n status: \"error\",\n message: err instanceof Error ? err.message : String(err),\n ...(err instanceof MistApiError ? { httpStatus: err.status } : {}),\n });\n process.exit(err instanceof MistApiError ? err.exitCode : 1);\n }\n printJson(outcome.output);\n if (outcome.exitCode !== 0) process.exit(outcome.exitCode);\n }),\n );\n","/**\n * `fluid mist droplet repair [slug] [--dry-run] [--yes] [--json]`\n *\n * Port of Mist Desktop's `repair_droplet_lifecycle_webhooks`: point the\n * Mist's attached Droplet's embed URL and install/uninstall webhook URLs at\n * the Mist's public URL. Only those three Droplet fields change, and the\n * Droplet is re-read to verify. This does not replay `droplet.installed`\n * for an existing installation — use `fluid mist droplet reinstall` when\n * the app needs a fresh install event.\n */\n\nimport chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport prompts from \"prompts\";\nimport { createMistClient, MistApiError } from \"../../api/client.js\";\nimport {\n applyDropletRepair,\n DropletRepairRefusal,\n planDropletRepair,\n resolveRepairTarget,\n type LifecycleRepairResult,\n} from \"../../lib/droplet-repair.js\";\nimport { runAction } from \"../../lib/error-handler.js\";\nimport { printJson } from \"../../lib/output.js\";\n\ninterface Opts {\n dryRun?: boolean;\n yes?: boolean;\n json?: boolean;\n}\n\nexport const dropletRepairCommand = new Command(\"repair\")\n .description(\"Repair the attached Droplet's embed and lifecycle webhook URLs\")\n .argument(\"[slug]\", \"Mist slug (defaults to the .mist file in CWD)\")\n .option(\"--dry-run\", \"Show the planned change without updating the Droplet\")\n .option(\"-y, --yes\", \"Apply without a confirmation prompt\")\n .option(\"--json\", \"Emit JSON\")\n .action((slug: string | undefined, opts: Opts) =>\n runAction(async () => {\n const client = createMistClient();\n let result: { summary: LifecycleRepairResult; isError: boolean };\n try {\n const target = await resolveRepairTarget(client, {\n slug,\n cwd: process.cwd(),\n });\n const planned = await planDropletRepair(\n client,\n target,\n opts.dryRun === true || !opts.yes,\n );\n if (opts.dryRun || planned.alreadyCorrect) {\n result = { summary: planned.summary, isError: false };\n } else {\n if (!opts.yes) {\n const interactive = process.stdin.isTTY && !opts.json;\n if (!interactive) {\n throw new DropletRepairRefusal(\n \"confirmation_required\",\n \"Refused to update the Droplet without confirmation. Rerun with --yes, or with --dry-run to preview.\",\n );\n }\n printSummary(planned.summary, { preview: true });\n const { confirmed } = await prompts(\n {\n type: \"confirm\",\n name: \"confirmed\",\n message: \"Update these Droplet URLs?\",\n initial: false,\n },\n { onCancel: () => process.exit(130) },\n );\n if (!confirmed) {\n console.log(chalk.dim(\"Aborted.\"));\n return;\n }\n }\n result = await applyDropletRepair(client, target);\n }\n } catch (err) {\n if (opts.json && !(err instanceof DropletRepairRefusal)) {\n // Keep --json output parseable on API and other failures too.\n printJson({\n status: \"error\",\n message: err instanceof Error ? err.message : String(err),\n ...(err instanceof MistApiError ? { httpStatus: err.status } : {}),\n });\n process.exit(err instanceof MistApiError ? err.exitCode : 1);\n }\n if (!(err instanceof DropletRepairRefusal)) throw err;\n if (opts.json) {\n printJson({\n status: \"refused\",\n code: err.code,\n message: err.message,\n });\n } else {\n console.error(chalk.red(err.message));\n }\n process.exit(1);\n }\n\n if (opts.json) printJson(result.summary);\n else printSummary(result.summary);\n if (result.isError) process.exit(1);\n }),\n );\n\nfunction printSummary(\n summary: LifecycleRepairResult,\n options: { preview?: boolean } = {},\n): void {\n const label: Record<LifecycleRepairResult[\"status\"], string> = {\n unchanged: chalk.green(\"Already correct; nothing to change.\"),\n planned: chalk.yellow(\"Planned (dry run); nothing was changed.\"),\n updated: chalk.green(\"Updated and verified.\"),\n error: chalk.red(\"Updated, but the Droplet did not verify.\"),\n };\n console.log();\n console.log(\n `${chalk.bold(summary.mistSlug ?? summary.mistId)} ${chalk.dim(summary.dropletUuid)}`,\n );\n const row = (name: string, previous: string | null, next: string) =>\n previous === next\n ? ` ${name.padEnd(10)} ${next}`\n : ` ${name.padEnd(10)} ${chalk.dim(previous ?? \"(none)\")} → ${chalk.cyan(next)}`;\n console.log(row(\"embed\", summary.previous.embedUrl, summary.embedUrl));\n console.log(\n row(\n \"install\",\n summary.previous.installWebhookUrl,\n summary.installWebhookUrl,\n ),\n );\n console.log(\n row(\n \"uninstall\",\n summary.previous.uninstallWebhookUrl,\n summary.uninstallWebhookUrl,\n ),\n );\n if (summary.lifecyclePathsSource === \"defaults\") {\n console.log(\n chalk.dim(\n \" Webhook paths: defaults (no fluid.capabilities.json for this Mist in the current directory).\",\n ),\n );\n }\n if (!options.preview) console.log(` ${label[summary.status]}`);\n console.log(chalk.dim(` ${summary.note}`));\n}\n","/** `fluid mist droplet` — sub-command aggregator for the attached Droplet. */\n\nimport { Command } from \"commander\";\nimport { dropletEgressProxyCommand } from \"./egress-proxy.js\";\nimport { dropletReinstallCommand } from \"./reinstall.js\";\nimport { dropletRepairCommand } from \"./repair.js\";\n\nexport const dropletCommand = new Command(\"droplet\")\n .description(\n \"Repair, reinstall, or set egress for the Mist's attached Droplet\",\n )\n .addCommand(dropletRepairCommand)\n .addCommand(dropletReinstallCommand)\n .addCommand(dropletEgressProxyCommand);\n","/** `fluid mist staging status` — where staging stands. */\n\nimport chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport { createMistClient } from \"../../api/client.js\";\nimport { runAction } from \"../../lib/error-handler.js\";\nimport { printJson } from \"../../lib/output.js\";\nimport { resolveSlug } from \"../../lib/slug-resolver.js\";\nimport {\n describeProgress,\n describeUnavailable,\n readStagingStatus,\n} from \"../../lib/staging.js\";\n\ninterface Opts {\n json?: boolean;\n}\n\nexport const stagingStatusCommand = new Command(\"status\")\n .description(\"Show the staging environment and whether it can be requested\")\n .argument(\"[slug]\", \"The mist slug (defaults to .mist file in CWD)\")\n .option(\"--json\", \"Emit the status as JSON\")\n .action((slug: string | undefined, opts: Opts) =>\n runAction(async () => {\n const client = createMistClient();\n const status = await readStagingStatus(client, resolveSlug(slug));\n if (opts.json) {\n return printJson({\n capability: status.capability,\n environment: status.environment,\n active_operation: status.activeOperation,\n });\n }\n\n const { environment, activeOperation, capability } = status;\n if (environment) {\n console.log(`Staging: ${chalk.bold(environment.state)}`);\n if (environment.url) console.log(`URL: ${chalk.cyan(environment.url)}`);\n } else {\n console.log(chalk.dim(\"No staging environment yet.\"));\n }\n if (activeOperation) {\n console.log(\n `Operation ${activeOperation.id}: ${describeProgress(activeOperation)}`,\n );\n }\n console.log(\n capability.staging.available\n ? \"Staging can be requested with `fluid mist staging request --revision <sha>`.\"\n : chalk.yellow(describeUnavailable(capability.staging)),\n );\n }),\n );\n","/**\n * Staging requests the server hasn't confirmed yet.\n *\n * `fluid mist staging request` saves its operation key here before it sends\n * the request and removes it once the server answers with an operation. If\n * the CLI dies in between (crash, lost connection, closed terminal), the\n * next request for the same revision reuses the saved key, so the server\n * resumes the same operation instead of starting a second one. After the\n * server has confirmed, `fluid mist staging wait` follows the operation.\n *\n * Records live under the Fluid config dir (`~/.fluid/mist/staging-requests`)\n * and hold only ids, a commit SHA and a timestamp, no tokens or secrets.\n */\n\nimport { mkdirSync, readFileSync, rmSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { getConfigDir } from \"@fluid-app/fluid-cli\";\nimport {\n checkStagingRequest,\n isOperationKey,\n type BranchOperation,\n} from \"../api/branch_environments.js\";\nimport { MistApiError, type MistClient } from \"../api/client.js\";\nimport { resolveMistId } from \"../api/id-resolver.js\";\nimport { admitStaging, StagingOperationFailedError } from \"./staging.js\";\n\nexport interface PendingStagingRequest {\n mistId: string;\n operationKey: string;\n sourceRevision: string;\n /** ISO-8601 time the request was about to be sent. */\n requestedAt: string;\n}\n\nexport interface StagingRequestStore {\n save(request: PendingStagingRequest): void;\n load(mistId: string): PendingStagingRequest | null;\n remove(mistId: string): void;\n}\n\nexport function createFileStagingRequestStore(\n dir: string = join(getConfigDir(), \"mist\", \"staging-requests\"),\n): StagingRequestStore {\n const fileFor = (mistId: string) =>\n join(dir, `${encodeURIComponent(mistId)}.json`);\n return {\n save(request) {\n mkdirSync(dir, { recursive: true, mode: 0o700 });\n writeFileSync(\n fileFor(request.mistId),\n JSON.stringify(request, null, 2) + \"\\n\",\n { encoding: \"utf8\", mode: 0o600 },\n );\n },\n load(mistId) {\n try {\n const parsed = JSON.parse(\n readFileSync(fileFor(mistId), \"utf8\"),\n ) as unknown;\n return isPendingRequest(parsed) && parsed.mistId === mistId\n ? parsed\n : null;\n } catch {\n return null;\n }\n },\n remove(mistId) {\n rmSync(fileFor(mistId), { force: true });\n },\n };\n}\n\n/**\n * The key to send for `sourceRevision`: the saved one when an unconfirmed\n * request for the same revision is pending, otherwise none. A pending\n * request for another revision, or with a key the server would refuse, is\n * dropped; if the server did admit it, the capability check reports it as\n * the run in progress.\n */\nexport function pendingKeyFor(\n store: StagingRequestStore,\n mistId: string,\n sourceRevision: string,\n): string | null {\n const pending = store.load(mistId);\n if (!pending) return null;\n if (\n pending.sourceRevision === sourceRevision &&\n isOperationKey(pending.operationKey)\n ) {\n return pending.operationKey;\n }\n store.remove(mistId);\n return null;\n}\n\nfunction isPendingRequest(value: unknown): value is PendingStagingRequest {\n if (typeof value !== \"object\" || value === null) return false;\n const record = value as Record<string, unknown>;\n return (\n typeof record[\"mistId\"] === \"string\" &&\n typeof record[\"operationKey\"] === \"string\" &&\n typeof record[\"sourceRevision\"] === \"string\" &&\n typeof record[\"requestedAt\"] === \"string\"\n );\n}\n\nexport interface ResumableStagingRequest {\n sourceRevision: string;\n /** An explicit key wins over a saved one. */\n operationKey?: string;\n store: StagingRequestStore;\n /** Told the key before the request is sent. */\n onOperationKey?: (key: string) => void;\n /** Told when a saved, unconfirmed request is being resumed. */\n onResume?: (key: string) => void;\n}\n\n/**\n * Request staging, remembering the key until the server answers. A saved\n * key for the same revision is reused, so a request the CLI never heard\n * back about resumes the same operation. The record is removed once the\n * server answers, whether it admitted the request or refused it, and kept\n * when the request may never have reached it.\n */\nexport async function requestStagingResumably(\n client: MistClient,\n slug: string,\n request: ResumableStagingRequest,\n): Promise<BranchOperation> {\n const { store, sourceRevision } = request;\n // Malformed input never reaches the store, where it would be replayed.\n checkStagingRequest(sourceRevision, request.operationKey);\n const mistId = await resolveMistId(client, slug);\n const savedKey = request.operationKey\n ? null\n : pendingKeyFor(store, mistId, sourceRevision);\n if (savedKey) request.onResume?.(savedKey);\n\n try {\n const operation = await admitStaging(client, slug, {\n sourceRevision,\n operationKey: request.operationKey ?? savedKey ?? undefined,\n onOperationKey: (key) => {\n store.save({\n mistId,\n operationKey: key,\n sourceRevision,\n requestedAt: new Date().toISOString(),\n });\n request.onOperationKey?.(key);\n },\n });\n store.remove(mistId);\n return operation;\n } catch (err) {\n if (serverAnswered(err)) store.remove(mistId);\n throw err;\n }\n}\n\n/**\n * Whether the server's answer settled the request. A network failure, a\n * rate limit, a server error or a response the CLI can't read may come\n * after the operation was recorded, so the key is kept for those and the\n * next request replays it.\n */\nfunction serverAnswered(err: unknown): boolean {\n if (err instanceof MistApiError) {\n return err.status !== 0 && err.status !== 429 && err.status < 500;\n }\n // A response that breaks the contract may still mean \"admitted\", so only\n // a readable operation that already ended settles the request.\n return err instanceof StagingOperationFailedError;\n}\n","/** `fluid mist staging wait` — follow a staging operation to the end. */\n\nimport { Command } from \"commander\";\nimport { createMistClient } from \"../../api/client.js\";\nimport { runAction } from \"../../lib/error-handler.js\";\nimport { printJson } from \"../../lib/output.js\";\nimport { resolveSlug } from \"../../lib/slug-resolver.js\";\nimport {\n describeProgress,\n readStagingStatus,\n waitForOperation,\n type WaitOptions,\n} from \"../../lib/staging.js\";\n\nconst POLL_INTERVAL_MS = 5_000;\nconst DEFAULT_TIMEOUT_MINUTES = 30;\n\ninterface Opts {\n operation?: string;\n timeout?: string;\n json?: boolean;\n}\n\n/** Polling options for a `--timeout <minutes>` flag, progress on stderr. */\nexport function waitOptions(timeout: string | undefined): WaitOptions {\n const minutes = timeout ? Number(timeout) : DEFAULT_TIMEOUT_MINUTES;\n if (!Number.isFinite(minutes) || minutes <= 0) {\n throw new Error(\"--timeout must be a positive number of minutes\");\n }\n return {\n intervalMs: POLL_INTERVAL_MS,\n timeoutMs: minutes * 60_000,\n onChange: (operation) =>\n process.stderr.write(`${describeProgress(operation)}\\n`),\n };\n}\n\nexport const stagingWaitCommand = new Command(\"wait\")\n .description(\"Follow a staging operation until it finishes\")\n .argument(\"[slug]\", \"The mist slug (defaults to .mist file in CWD)\")\n .option(\n \"--operation <id>\",\n \"Operation to follow (defaults to the one preparing staging)\",\n )\n .option(\"--timeout <minutes>\", \"How long to follow it (default 30)\")\n .option(\"--json\", \"Emit the finished operation as JSON\")\n .action((slug: string | undefined, opts: Opts) =>\n runAction(async () => {\n const resolved = resolveSlug(slug);\n const client = createMistClient();\n const options = waitOptions(opts.timeout);\n const operationId =\n opts.operation ??\n (await readStagingStatus(client, resolved)).activeOperation?.id;\n if (!operationId) {\n throw new Error(\n \"No staging operation is running. Check `fluid mist staging status`.\",\n );\n }\n\n const operation = await waitForOperation(\n client,\n resolved,\n operationId,\n options,\n );\n if (opts.json) return printJson({ operation });\n console.log(\n \"✔ Staging is ready. Run `fluid mist staging status` for its URL.\",\n );\n }),\n );\n","/** `fluid mist staging request` — request staging at a revision. */\n\nimport chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport { createMistClient } from \"../../api/client.js\";\nimport { createGitCredential } from \"../../api/git_credential.js\";\nimport { runAction } from \"../../lib/error-handler.js\";\nimport { printJson } from \"../../lib/output.js\";\nimport { resolveSlug } from \"../../lib/slug-resolver.js\";\nimport { describeProgress, waitForOperation } from \"../../lib/staging.js\";\nimport {\n createFileStagingRequestStore,\n requestStagingResumably,\n} from \"../../lib/staging-request-store.js\";\nimport { resolveLocalStagingRevision } from \"../../lib/staging-revision.js\";\nimport { waitOptions } from \"./wait.js\";\n\ninterface Opts {\n revision?: string;\n dir?: string;\n operationKey?: string;\n wait?: boolean;\n timeout?: string;\n json?: boolean;\n}\n\nexport const stagingRequestCommand = new Command(\"request\")\n .description(\"Request the staging environment at a pushed commit\")\n .argument(\"[slug]\", \"The mist slug (defaults to .mist file in CWD)\")\n .option(\n \"--revision <sha>\",\n \"Full commit SHA to stage (defaults to this checkout's HEAD when it is clean and on main)\",\n )\n .option(\n \"--operation-key <key>\",\n \"Resume an interrupted request with the key and --revision it printed\",\n )\n .option(\n \"--dir <path>\",\n \"Checkout to stage from when --revision is omitted (defaults to the current directory)\",\n )\n .option(\"--wait\", \"Follow the operation until staging is ready\")\n .option(\"--timeout <minutes>\", \"How long --wait follows it (default 30)\")\n .option(\"--json\", \"Emit the operation as JSON\")\n .action((slug: string | undefined, opts: Opts) =>\n runAction(async () => {\n if (opts.operationKey && !opts.revision) {\n // The server ties a key to the revision it was first sent with, and\n // the checkout may have moved since, so a resume names it.\n throw new Error(\n \"--operation-key needs the --revision it was first sent with. The interrupted request printed both.\",\n );\n }\n const resolved = resolveSlug(slug);\n const client = createMistClient();\n const sourceRevision =\n opts.revision ??\n (await localRevision(client, resolved, opts.dir ?? process.cwd()));\n let operation = await requestStagingResumably(client, resolved, {\n sourceRevision,\n operationKey: opts.operationKey,\n store: createFileStagingRequestStore(),\n onResume: (key) =>\n process.stderr.write(\n chalk.dim(\n `Resuming the unconfirmed request for ${sourceRevision.slice(0, 7)} (key ${key}).\\n`,\n ),\n ),\n onOperationKey: (key) =>\n process.stderr.write(\n chalk.dim(\n `Operation key ${key}. If this is interrupted, rerun with --operation-key ${key} --revision ${sourceRevision}.\\n`,\n ),\n ),\n });\n process.stderr.write(\n `Staging operation ${operation.id}: ${describeProgress(operation)}\\n`,\n );\n\n if (opts.wait) {\n operation = await waitForOperation(\n client,\n resolved,\n operation.id,\n waitOptions(opts.timeout),\n );\n }\n if (opts.json) return printJson({ operation });\n if (operation.state === \"succeeded\") {\n console.log(\n `✔ Staging is ready. Run \\`fluid mist staging status\\` for its URL.`,\n );\n } else {\n console.log(\n `Requested. Follow it with \\`fluid mist staging wait --operation ${operation.id}\\`.`,\n );\n }\n }),\n );\n\n/** The checkout's HEAD, once it is clean and already on the remote's main. */\nasync function localRevision(\n client: ReturnType<typeof createMistClient>,\n slug: string,\n checkout: string,\n): Promise<string> {\n const { git_credential: credential } = await createGitCredential(\n client,\n slug,\n );\n const revision = await resolveLocalStagingRevision(\n checkout,\n credential.remote_url,\n );\n process.stderr.write(\n chalk.dim(`Staging this checkout's HEAD, ${revision.slice(0, 7)}.\\n`),\n );\n return revision;\n}\n","/** `fluid mist staging teardown` — remove the staging environment. */\n\nimport chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport { removeBranchEnvironment } from \"../../api/branch_environments.js\";\nimport { createMistClient } from \"../../api/client.js\";\nimport { runAction } from \"../../lib/error-handler.js\";\nimport { printJson } from \"../../lib/output.js\";\nimport { resolveSlug } from \"../../lib/slug-resolver.js\";\nimport { readStagingStatus } from \"../../lib/staging.js\";\n\ninterface Opts {\n yes?: boolean;\n json?: boolean;\n}\n\nexport const stagingTeardownCommand = new Command(\"teardown\")\n .description(\n \"Tear down the staging environment: its deployment, variables, Git branch and database branch\",\n )\n .argument(\"[slug]\", \"The mist slug (defaults to .mist file in CWD)\")\n .option(\"--yes\", \"Confirm the teardown; nothing is removed without it\")\n .option(\"--json\", \"Emit the environment as JSON\")\n .action((slug: string | undefined, opts: Opts) =>\n runAction(async () => {\n const client = createMistClient();\n const mist = resolveSlug(slug);\n const { environment } = await readStagingStatus(client, mist);\n if (!environment || environment.state === \"deleted\") {\n if (opts.json) return printJson({ environment: null });\n console.log(chalk.dim(\"No staging environment to tear down.\"));\n return;\n }\n if (!opts.yes) {\n throw new Error(\n \"Tearing down staging removes its deployment, variables, Git branch and database branch. Rerun with --yes to confirm.\",\n );\n }\n\n const removed = await removeBranchEnvironment(\n client,\n mist,\n environment.id,\n );\n if (opts.json) return printJson({ environment: removed });\n console.log(\n removed.state === \"deleted\"\n ? \"Staging is already torn down.\"\n : `Staging is ${chalk.bold(removed.state)}. Its resources are removed within about a minute; check with \\`fluid mist staging status\\`.`,\n );\n }),\n );\n","/** `fluid mist staging` — sub-command aggregator. */\n\nimport { Command } from \"commander\";\nimport { stagingStatusCommand } from \"./status.js\";\nimport { stagingRequestCommand } from \"./request.js\";\nimport { stagingWaitCommand } from \"./wait.js\";\nimport { stagingTeardownCommand } from \"./teardown.js\";\n\nexport const stagingCommand = new Command(\"staging\")\n .description(\"Use a mist's isolated staging environment\")\n .addCommand(stagingStatusCommand)\n .addCommand(stagingRequestCommand)\n .addCommand(stagingWaitCommand)\n .addCommand(stagingTeardownCommand);\n","/**\n * @fluid-app/fluid-cli-mist\n *\n * Fluid CLI plugin for managing Mist hosted extensions. Auto-discovered\n * by @fluid-app/fluid-cli via the `fluid-cli-*` naming convention.\n *\n * Source of truth for command surface + behavior:\n * fluid (sibling repo) features/mist/cli-spec.md\n */\n\nimport { Command } from \"commander\";\nimport type { FluidPlugin, PluginContext } from \"@fluid-app/fluid-cli\";\nimport { listCommand } from \"./commands/list.js\";\nimport { showCommand } from \"./commands/show.js\";\nimport { createCommand } from \"./commands/create.js\";\nimport { attachCommand } from \"./commands/attach.js\";\nimport { attachDropletCommand } from \"./commands/attach-droplet.js\";\nimport { updateCommand } from \"./commands/update.js\";\nimport { deleteCommand } from \"./commands/delete.js\";\nimport { restoreCommand } from \"./commands/restore.js\";\nimport { cloneCommand } from \"./commands/clone.js\";\nimport { pushCommand } from \"./commands/push.js\";\nimport { pullCommand } from \"./commands/pull.js\";\nimport { openCommand } from \"./commands/open.js\";\nimport { deploymentsCommand } from \"./commands/deployments.js\";\nimport { logsCommand } from \"./commands/logs.js\";\nimport { envCommand } from \"./commands/env/index.js\";\nimport { dropletCommand } from \"./commands/droplet/index.js\";\nimport { stagingCommand } from \"./commands/staging/index.js\";\n\nconst plugin: FluidPlugin = {\n name: \"fluid-cli-mist\",\n version: \"0.1.32\",\n async register(ctx: PluginContext) {\n const mist = new Command(\"mist\").description(\n \"Create, manage, and deploy Mist hosted extensions\",\n );\n\n mist.addCommand(listCommand);\n mist.addCommand(showCommand);\n mist.addCommand(createCommand);\n mist.addCommand(attachCommand);\n mist.addCommand(attachDropletCommand);\n mist.addCommand(updateCommand);\n mist.addCommand(deleteCommand);\n mist.addCommand(restoreCommand);\n mist.addCommand(cloneCommand);\n mist.addCommand(pushCommand);\n mist.addCommand(pullCommand);\n mist.addCommand(openCommand);\n mist.addCommand(deploymentsCommand);\n mist.addCommand(logsCommand);\n mist.addCommand(envCommand);\n mist.addCommand(dropletCommand);\n mist.addCommand(stagingCommand);\n\n ctx.program.addCommand(mist);\n },\n};\n\nexport default plugin;\nexport type * from \"./types.js\";\n"],"mappings":"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"}