@fluid-app/fluid-cli-mist 0.1.3 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.mjs +27 -9
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -331,7 +331,24 @@ const STATE_COLOR$1 = {
|
|
|
331
331
|
function colorState(state) {
|
|
332
332
|
return STATE_COLOR$1[state](state);
|
|
333
333
|
}
|
|
334
|
-
/**
|
|
334
|
+
/**
|
|
335
|
+
* Bold display label for a mist, falling back to a dim "(unnamed)"
|
|
336
|
+
* placeholder when both `name` and `vendorName` are missing. Never nests
|
|
337
|
+
* `chalk.bold(chalk.dim(...))` — most terminals render that as plain bold,
|
|
338
|
+
* losing the "this is a placeholder" muted intent.
|
|
339
|
+
*/
|
|
340
|
+
function mistLabel(name, vendorName) {
|
|
341
|
+
const raw = name ?? vendorName;
|
|
342
|
+
return raw ? chalk.bold(raw) : chalk.dim("(unnamed)");
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* Pad columns to width — cheap manual table; avoids cli-table3 dep for v1.
|
|
346
|
+
* Cells are defensively normalized: API records can legitimately have every
|
|
347
|
+
* optional label field (`name`, `vendor_name`, `droplet.name`, ...) come back
|
|
348
|
+
* null/absent at once (e.g. a `pending_destroy` mist), so callers sometimes
|
|
349
|
+
* hand us `undefined`/`null` after chaining `??` — that must render as an
|
|
350
|
+
* empty cell, not crash the whole table.
|
|
351
|
+
*/
|
|
335
352
|
function printTable(rows) {
|
|
336
353
|
if (rows.length === 0) return;
|
|
337
354
|
const widths = [];
|
|
@@ -342,13 +359,14 @@ function printTable(rows) {
|
|
|
342
359
|
for (const row of rows) {
|
|
343
360
|
const padded = row.map((cell, i) => {
|
|
344
361
|
const w = widths[i] ?? 0;
|
|
345
|
-
|
|
362
|
+
const text = cell ?? "";
|
|
363
|
+
return text + " ".repeat(Math.max(0, w - stripAnsi(text).length));
|
|
346
364
|
});
|
|
347
365
|
process.stdout.write(padded.join(" ") + "\n");
|
|
348
366
|
}
|
|
349
367
|
}
|
|
350
368
|
function stripAnsi(s) {
|
|
351
|
-
return s.replace(/\x1b\[[0-9;]*m/g, "");
|
|
369
|
+
return (s ?? "").replace(/\x1b\[[0-9;]*m/g, "");
|
|
352
370
|
}
|
|
353
371
|
function relativeDate(iso) {
|
|
354
372
|
if (!iso) return "—";
|
|
@@ -391,7 +409,7 @@ const listCommand = new Command("list").description("List the mists you have acc
|
|
|
391
409
|
chalk.bold("UPDATED")
|
|
392
410
|
], ...mists.map((m) => [
|
|
393
411
|
m.slug,
|
|
394
|
-
m.name ?? m.droplet?.name ?? m.vendor_name,
|
|
412
|
+
m.name ?? m.droplet?.name ?? m.vendor_name ?? chalk.dim("(unnamed)"),
|
|
395
413
|
colorState(m.state),
|
|
396
414
|
m.hostable_type ?? chalk.dim("standalone"),
|
|
397
415
|
relativeDate(m.updated_at)
|
|
@@ -457,8 +475,8 @@ const showCommand = new Command("show").description("Show one mist by slug").arg
|
|
|
457
475
|
integration_points: integrationPoints
|
|
458
476
|
});
|
|
459
477
|
const lines = [];
|
|
460
|
-
const label = mist.name
|
|
461
|
-
lines.push(`${
|
|
478
|
+
const label = mistLabel(mist.name, mist.vendor_name);
|
|
479
|
+
lines.push(`${label} ${chalk.dim(mist.slug)}`);
|
|
462
480
|
if (mist.vendor_name && mist.name && mist.name !== mist.vendor_name) lines.push(` vendor ${chalk.dim(mist.vendor_name)}`);
|
|
463
481
|
lines.push(` state ${colorState(mist.state)}`);
|
|
464
482
|
if (mist.kind) lines.push(` kind ${mist.kind}`);
|
|
@@ -1082,9 +1100,9 @@ const createCommand = new Command("create").description("Provision a new mist (o
|
|
|
1082
1100
|
mist,
|
|
1083
1101
|
provision
|
|
1084
1102
|
} : created);
|
|
1085
|
-
const label = mist.name
|
|
1103
|
+
const label = mistLabel(mist.name, mist.vendor_name);
|
|
1086
1104
|
console.log();
|
|
1087
|
-
console.log(`${
|
|
1105
|
+
console.log(`${label} ${chalk.dim(mist.slug)} ${colorState(mist.state)}`);
|
|
1088
1106
|
if (mist.public_url) console.log(` ${chalk.cyan(mist.public_url)}`);
|
|
1089
1107
|
if (provision) printSurfaceSummary(provision);
|
|
1090
1108
|
console.log();
|
|
@@ -1193,7 +1211,7 @@ const updateCommand = new Command("update").description("Rename a mist, or attac
|
|
|
1193
1211
|
renameResp = await updateMist(client, resolved, { mist: { name: opts.name.trim() } });
|
|
1194
1212
|
if (!opts.json) {
|
|
1195
1213
|
const m = renameResp.mist;
|
|
1196
|
-
console.log(`${
|
|
1214
|
+
console.log(`${mistLabel(m.name, m.vendor_name)} ${chalk.dim(m.slug)} ${colorState(m.state)}`);
|
|
1197
1215
|
}
|
|
1198
1216
|
}
|
|
1199
1217
|
if (opts.hostable || opts.detach) {
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["BASE","STATE_COLOR","BASE","BASE","path","KIND_LABEL","POLL_MS","sleep"],"sources":["../src/api/client.ts","../src/api/id-resolver.ts","../src/api/mists.ts","../src/lib/hostable.ts","../src/lib/slug-resolver.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","../src/api/droplets.ts","../src/api/drop_zones.ts","../src/api/mobile_widgets.ts","../src/api/env_vars.ts","../src/types.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/git.ts","../src/commands/clone.ts","../src/api/deployments.ts","../src/api/me.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/commands/env/list.ts","../src/commands/env/set.ts","../src/commands/env/unset.ts","../src/commands/env/pull.ts","../src/commands/env/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 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 /** 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 ) {\n super(message);\n this.name = \"MistApiError\";\n this.status = status;\n this.details = details;\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 post<T>(path: string, body?: unknown): 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?: { body?: unknown; query?: Record<string, unknown> },\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 },\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 throw new MistApiError(message, response.status, body.error?.details);\n }\n return parsed as T;\n }\n\n return {\n get: (path, query) => call(\"GET\", path, { query }),\n post: (path, body) => call(\"POST\", path, { body }),\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 * 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 * 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 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 );\n}\n\n/** Write a `.mist` file after `fluid mist clone`. */\nexport function writeMistFile(dir: string, mist: Mist): void {\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 * 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 { MistApiError } from \"../api/client.js\";\nimport { HostableParseError } from \"./hostable.js\";\nimport { SlugResolutionError } from \"./slug-resolver.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 (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/** Pad columns to width — cheap manual table; avoids cli-table3 dep for v1. */\nexport function printTable(rows: string[][]): 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 return cell + \" \".repeat(Math.max(0, w - stripAnsi(cell).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.\nfunction stripAnsi(s: string): 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,\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 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 = mist.name ?? mist.vendor_name;\n lines.push(`${chalk.bold(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","/**\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 type { MistClient } from \"./client.js\";\nimport type {\n DropletCreateInput,\n DropletCreateRequest,\n DropletCreateResponse,\n DropletShowResponse,\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). */\nexport function dropletWebhookUrls(embedUrl: string): {\n install_webhook_url: string;\n uninstall_webhook_url: string;\n} {\n const base = embedUrl.replace(/\\/+$/, \"\");\n return {\n install_webhook_url: `${base}/api/webhooks/installed`,\n uninstall_webhook_url: `${base}/api/webhooks/uninstalled`,\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}): 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 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/** PATCH the droplet's embed URL (+ derived webhook URLs) once known. */\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 * 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 vendor_name: string;\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// ---------------------------------------------------------------------------\n// Droplets — `POST /api/droplets`, `PATCH /api/droplets/:uuid`.\n// ---------------------------------------------------------------------------\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 settings?: Record<string, unknown>;\n}\n\nexport interface DropletUpdateInput {\n embed_url?: string;\n install_webhook_url?: string;\n uninstall_webhook_url?: string;\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` and isn't exposed\n * anywhere the CLI can read it — attach by `uuid` instead (see\n * `IntegrationPointCreateInput.integratable_uuid`).\n */\n id?: number;\n uuid: string;\n name: string;\n embed_url?: string | null;\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// Integration points — the attach mechanism that replaced hostable.\n// `POST/GET /api/v202604/mists/:mist_id/integration_points`.\n// ---------------------------------------------------------------------------\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// ---------------------------------------------------------------------------\n// Drop zones — `POST /api/drop_zones`. Attach by the dz_ uuid.\n// ---------------------------------------------------------------------------\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// ---------------------------------------------------------------------------\n// Mobile embeds — `POST /api/company/mobile_widgets`. Attach by numeric id.\n// ---------------------------------------------------------------------------\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 vendor_name: string;\n public_url: string | null;\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 */\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 setDropletEmbedUrl,\n} from \"../api/droplets.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 IntegrationPointKind,\n type MistState,\n} from \"../types.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 /** 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 /** Droplet only; null for other kinds (they don't provision managed env). */\n managedEnv: { ready: boolean; present: string[]; missing: string[] } | 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 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 const name =\n options.name?.trim() || mist.name?.trim() || mist.vendor_name;\n reporter.start(`Creating ${label} \"${name}\"…`);\n\n if (kind === \"droplet\") {\n const created = await createDroplet(client, {\n droplet: buildDropletCreateInput({\n name,\n embedUrl,\n requestedScopes: options.requestedScopes,\n }),\n });\n surfaceUuid = created.droplet.uuid;\n surfaceNumericId = created.droplet.id ?? null;\n surfaceName = created.droplet.name;\n attachUuid = created.droplet.uuid;\n embedUrlSet = Boolean(embedUrl);\n reporter.succeed(\n `Created droplet ${surfaceUuid}${embedUrl ? \"\" : \" (embed URL pending)\"}`,\n );\n // Backfill embed URL if public_url wasn't ready at create time.\n if (!embedUrl && surfaceUuid) {\n const laterUrl = await resolvePublicUrl(client, {\n slugOrId,\n initial: null,\n reporter,\n timing,\n sleep,\n });\n if (laterUrl) {\n reporter.start(\"Backfilling droplet embed URL…\");\n await setDropletEmbedUrl(client, surfaceUuid, laterUrl);\n embedUrlSet = true;\n reporter.succeed(\"Embed URL set\");\n } else {\n reporter.warn(\n \"Mist public_url not ready yet — re-run once the first deploy finishes to set the embed URL\",\n );\n }\n }\n } else if (kind === \"drop-zone\") {\n if (!embedUrl) {\n throw new Error(\n \"The mist has no public_url yet — wait for the first deploy, then attach the drop zone.\",\n );\n }\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 reporter.succeed(`Created drop zone ${surfaceUuid}`);\n } else {\n // mobile-embed\n if (!embedUrl) {\n throw new Error(\n \"The mist has no public_url yet — wait for the first deploy, then attach the mobile embed.\",\n );\n }\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 reporter.succeed(`Created mobile embed #${surfaceNumericId}`);\n }\n surfaceCreated = true;\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: 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 managedEnv,\n };\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}\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.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}\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 *\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, 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 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(\"--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\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 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 = mist.name ?? mist.vendor_name;\n console.log();\n console.log(\n `${chalk.bold(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 * 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 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(\"--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 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 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\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\";\n\ninterface Opts {\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(\"--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 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, 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 `${chalk.bold(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.vendor_name)} (${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.vendor_name} (${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 * 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 { chmodSync, existsSync, mkdirSync, writeFileSync } 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 * `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 [\"-c\", \"credential.helper=\", \"clone\", \"--quiet\", cleanUrl, targetDir],\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}\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 [\"-c\", \"credential.helper=\", \"pull\", \"--ff-only\", cleanUrl, branch],\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/** 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 [\"-c\", \"credential.helper=\", \"fetch\", \"--quiet\", cleanUrl, branch],\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/**\n * Pop the most recent stash. Throws on conflict so callers can surface\n * the situation (the working tree will have conflict markers; the user\n * needs to resolve manually).\n */\nexport async function gitStashPop(cwd: string): Promise<void> {\n try {\n await execa(\"git\", [\"stash\", \"pop\"], { 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 `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\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 */\nexport async function gitAutoCommit(\n cwd: string,\n message: string,\n): Promise<void> {\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 // 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\", message], {\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 * `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 { existsSync } 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 } from \"../lib/git.js\";\n\ninterface Opts {\n dir?: string;\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 const targetDir = resolve(opts.dir ?? mist.vendor_name);\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 ${mist.vendor_name}/…`;\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 console.log(chalk.dim(` cd ${join(\".\", mist.vendor_name)}`));\n if (mist.public_url)\n console.log(chalk.dim(` ${chalk.cyan(mist.public_url)}`));\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 with `<full_name> (<public_id>):` so\n * deploy history can be traced back to a real person even when the\n * underlying git author 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","/**\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 { 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 { runAction } from \"../lib/error-handler.js\";\nimport { resolveSlug } from \"../lib/slug-resolver.js\";\nimport {\n ensureGitAvailable,\n gitAheadBehindVsFetchHead,\n gitAutoCommit,\n gitFetch,\n gitMergeFastForward,\n gitPush,\n gitStash,\n gitStashPop,\n hasUncommittedChanges,\n} from \"../lib/git.js\";\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;\nconst LOCATE_TIMEOUT_MS = 2 * 60_000; // 2 minutes to find the deployment id\nconst READY_TIMEOUT_MS = 5 * 60_000; // 5 minutes for it to flip to ready\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((r) => setTimeout(r, ms));\n}\n\nexport const pushCommand = new Command(\"push\")\n .description(\n \"Commit working-tree changes and push to the Mist-managed remote (triggers a Vercel deploy)\",\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 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); // 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 const baseMessage =\n opts.message ??\n `Publish from Mist Desktop · ${new Date().toISOString().slice(0, 19).replace(\"T\", \" \")}`;\n // Stamp the commit with the Fluid user so deploy history\n // traces back to a real person. Format:\n // \"Mike Tingey: Updated header (0182d21e-…)\"\n // Name + description go first so the prose stays scannable\n // in `git log --oneline` — the long UUID at the end won't\n // truncate the message. Best-effort: if /api/me fails,\n // hangs, or returns a malformed body, we still commit with\n // the bare message — publish is essential, the stamp is not.\n let namePrefix = \"\";\n let idSuffix = \"\";\n try {\n const me = await Promise.race([\n fetchCurrentUser(client),\n new Promise<never>((_, reject) =>\n setTimeout(\n () => reject(new Error(\"timed out after 5s\")),\n 5_000,\n ),\n ),\n ]);\n const name =\n (me.full_name && me.full_name.trim()) ||\n me.email ||\n (Number.isFinite(me.id) ? `user-${me.id}` : null);\n const id = me.public_id && me.public_id.trim();\n if (name) namePrefix = `${name}: `;\n if (id) idSuffix = ` (${id})`;\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 }\n const message = `${namePrefix}${baseMessage}${idSuffix}`;\n const commitSpinner = ora(\n `Committing changes (${message.length > 60 ? message.slice(0, 57) + \"…\" : message})…`,\n ).start();\n try {\n await gitAutoCommit(cwd, message);\n commitSpinner.succeed(\"Committed\");\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 // Without this, the loop used to grab whatever was first in the\n // list — including a stale ready deploy from a prior push that\n // skipped Vercel — and exit immediately as \"ready\".\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.error(\n `No deployment matching ${pushedSha?.slice(0, 7) ?? \"the push\"} appeared within ${LOCATE_TIMEOUT_MS / 1000}s.`,\n );\n console.error(\n \"Vercel may still be queueing — run `fluid mist deployments` to check.\",\n );\n process.exit(3);\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 console.log(`✔ Ready: ${chalk.cyan(deployment.url ?? \"(no url)\")}`);\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 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","/** `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 { runAction } from \"../../lib/error-handler.js\";\nimport { resolveSlug } from \"../../lib/slug-resolver.js\";\nimport { printJson, printTable } from \"../../lib/output.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\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 opts.showValues ? v.value : maskValue(v.value),\n v.targets.join(\",\"),\n v.managed_by === \"mist\" ? chalk.magenta(\"mist\") : chalk.dim(\"user\"),\n ]);\n printTable([header, ...rows]);\n }),\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\nimport chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport { createMistClient } from \"../../api/client.js\";\nimport { createEnvVar, listEnvVars, updateEnvVar } from \"../../api/env_vars.js\";\nimport { runAction } from \"../../lib/error-handler.js\";\nimport { resolveSlug } from \"../../lib/slug-resolver.js\";\n\ninterface Opts {\n json?: boolean;\n}\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\nexport const envSetCommand = new Command(\"set\")\n .description(\"Set one or more env vars (KEY=value KEY=value …)\")\n .argument(\"<slug-or-pair>\", \"The mist slug, or the first KEY=value pair\")\n .argument(\"[pairs...]\", \"Additional KEY=value pairs\")\n .action(async (slugOrPair: string, pairs: string[]) => {\n await runAction(async () => {\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","/** `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\nimport chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport { writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { createMistClient } from \"../../api/client.js\";\nimport { listEnvVars } from \"../../api/env_vars.js\";\nimport { runAction } from \"../../lib/error-handler.js\";\nimport { resolveSlug } from \"../../lib/slug-resolver.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 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 user = env_vars.filter((v) => v.managed_by === \"user\");\n const skipped = env_vars.filter((v) => v.managed_by === \"mist\");\n const target = join(process.cwd(), opts.out ?? \".env.local\");\n const body = user\n .map((v) => `${v.key}=${escapeValue(v.value)}`)\n .join(\"\\n\");\n writeFileSync(target, body + (body.length ? \"\\n\" : \"\"), \"utf8\");\n console.log(\n chalk.green(\n `Wrote ${user.length} var(s) to ${target.replace(process.cwd() + \"/\", \"\")}.`,\n ),\n );\n if (skipped.length > 0) {\n console.log(\n chalk.dim(\n `Skipped ${skipped.length} Mist-managed var(s): ${skipped.map((v) => v.key).join(\", \")} (local dev uses PGlite)`,\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-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\";\n\nconst plugin: FluidPlugin = {\n name: \"fluid-cli-mist\",\n version: \"0.1.0\",\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\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","BASE","path","KIND_LABEL","POLL_MS","sleep"],"sources":["../src/api/client.ts","../src/api/id-resolver.ts","../src/api/mists.ts","../src/lib/hostable.ts","../src/lib/slug-resolver.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","../src/api/droplets.ts","../src/api/drop_zones.ts","../src/api/mobile_widgets.ts","../src/api/env_vars.ts","../src/types.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/git.ts","../src/commands/clone.ts","../src/api/deployments.ts","../src/api/me.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/commands/env/list.ts","../src/commands/env/set.ts","../src/commands/env/unset.ts","../src/commands/env/pull.ts","../src/commands/env/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 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 /** 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 ) {\n super(message);\n this.name = \"MistApiError\";\n this.status = status;\n this.details = details;\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 post<T>(path: string, body?: unknown): 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?: { body?: unknown; query?: Record<string, unknown> },\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 },\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 throw new MistApiError(message, response.status, body.error?.details);\n }\n return parsed as T;\n }\n\n return {\n get: (path, query) => call(\"GET\", path, { query }),\n post: (path, body) => call(\"POST\", path, { body }),\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 * 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 * 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 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 );\n}\n\n/** Write a `.mist` file after `fluid mist clone`. */\nexport function writeMistFile(dir: string, mist: Mist): void {\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 * 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 { MistApiError } from \"../api/client.js\";\nimport { HostableParseError } from \"./hostable.js\";\nimport { SlugResolutionError } from \"./slug-resolver.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 (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/**\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","/**\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 type { MistClient } from \"./client.js\";\nimport type {\n DropletCreateInput,\n DropletCreateRequest,\n DropletCreateResponse,\n DropletShowResponse,\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). */\nexport function dropletWebhookUrls(embedUrl: string): {\n install_webhook_url: string;\n uninstall_webhook_url: string;\n} {\n const base = embedUrl.replace(/\\/+$/, \"\");\n return {\n install_webhook_url: `${base}/api/webhooks/installed`,\n uninstall_webhook_url: `${base}/api/webhooks/uninstalled`,\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}): 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 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/** PATCH the droplet's embed URL (+ derived webhook URLs) once known. */\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 * 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 vendor_name: string;\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// ---------------------------------------------------------------------------\n// Droplets — `POST /api/droplets`, `PATCH /api/droplets/:uuid`.\n// ---------------------------------------------------------------------------\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 settings?: Record<string, unknown>;\n}\n\nexport interface DropletUpdateInput {\n embed_url?: string;\n install_webhook_url?: string;\n uninstall_webhook_url?: string;\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` and isn't exposed\n * anywhere the CLI can read it — attach by `uuid` instead (see\n * `IntegrationPointCreateInput.integratable_uuid`).\n */\n id?: number;\n uuid: string;\n name: string;\n embed_url?: string | null;\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// Integration points — the attach mechanism that replaced hostable.\n// `POST/GET /api/v202604/mists/:mist_id/integration_points`.\n// ---------------------------------------------------------------------------\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// ---------------------------------------------------------------------------\n// Drop zones — `POST /api/drop_zones`. Attach by the dz_ uuid.\n// ---------------------------------------------------------------------------\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// ---------------------------------------------------------------------------\n// Mobile embeds — `POST /api/company/mobile_widgets`. Attach by numeric id.\n// ---------------------------------------------------------------------------\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 vendor_name: string;\n public_url: string | null;\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 */\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 setDropletEmbedUrl,\n} from \"../api/droplets.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 IntegrationPointKind,\n type MistState,\n} from \"../types.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 /** 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 /** Droplet only; null for other kinds (they don't provision managed env). */\n managedEnv: { ready: boolean; present: string[]; missing: string[] } | 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 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 const name =\n options.name?.trim() || mist.name?.trim() || mist.vendor_name;\n reporter.start(`Creating ${label} \"${name}\"…`);\n\n if (kind === \"droplet\") {\n const created = await createDroplet(client, {\n droplet: buildDropletCreateInput({\n name,\n embedUrl,\n requestedScopes: options.requestedScopes,\n }),\n });\n surfaceUuid = created.droplet.uuid;\n surfaceNumericId = created.droplet.id ?? null;\n surfaceName = created.droplet.name;\n attachUuid = created.droplet.uuid;\n embedUrlSet = Boolean(embedUrl);\n reporter.succeed(\n `Created droplet ${surfaceUuid}${embedUrl ? \"\" : \" (embed URL pending)\"}`,\n );\n // Backfill embed URL if public_url wasn't ready at create time.\n if (!embedUrl && surfaceUuid) {\n const laterUrl = await resolvePublicUrl(client, {\n slugOrId,\n initial: null,\n reporter,\n timing,\n sleep,\n });\n if (laterUrl) {\n reporter.start(\"Backfilling droplet embed URL…\");\n await setDropletEmbedUrl(client, surfaceUuid, laterUrl);\n embedUrlSet = true;\n reporter.succeed(\"Embed URL set\");\n } else {\n reporter.warn(\n \"Mist public_url not ready yet — re-run once the first deploy finishes to set the embed URL\",\n );\n }\n }\n } else if (kind === \"drop-zone\") {\n if (!embedUrl) {\n throw new Error(\n \"The mist has no public_url yet — wait for the first deploy, then attach the drop zone.\",\n );\n }\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 reporter.succeed(`Created drop zone ${surfaceUuid}`);\n } else {\n // mobile-embed\n if (!embedUrl) {\n throw new Error(\n \"The mist has no public_url yet — wait for the first deploy, then attach the mobile embed.\",\n );\n }\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 reporter.succeed(`Created mobile embed #${surfaceNumericId}`);\n }\n surfaceCreated = true;\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: 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 managedEnv,\n };\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}\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.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}\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 *\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 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(\"--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\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 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 * 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 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(\"--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 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 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\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\";\n\ninterface Opts {\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(\"--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 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.vendor_name)} (${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.vendor_name} (${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 * 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 { chmodSync, existsSync, mkdirSync, writeFileSync } 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 * `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 [\"-c\", \"credential.helper=\", \"clone\", \"--quiet\", cleanUrl, targetDir],\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}\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 [\"-c\", \"credential.helper=\", \"pull\", \"--ff-only\", cleanUrl, branch],\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/** 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 [\"-c\", \"credential.helper=\", \"fetch\", \"--quiet\", cleanUrl, branch],\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/**\n * Pop the most recent stash. Throws on conflict so callers can surface\n * the situation (the working tree will have conflict markers; the user\n * needs to resolve manually).\n */\nexport async function gitStashPop(cwd: string): Promise<void> {\n try {\n await execa(\"git\", [\"stash\", \"pop\"], { 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 `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\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 */\nexport async function gitAutoCommit(\n cwd: string,\n message: string,\n): Promise<void> {\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 // 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\", message], {\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 * `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 { existsSync } 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 } from \"../lib/git.js\";\n\ninterface Opts {\n dir?: string;\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 const targetDir = resolve(opts.dir ?? mist.vendor_name);\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 ${mist.vendor_name}/…`;\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 console.log(chalk.dim(` cd ${join(\".\", mist.vendor_name)}`));\n if (mist.public_url)\n console.log(chalk.dim(` ${chalk.cyan(mist.public_url)}`));\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 with `<full_name> (<public_id>):` so\n * deploy history can be traced back to a real person even when the\n * underlying git author 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","/**\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 { 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 { runAction } from \"../lib/error-handler.js\";\nimport { resolveSlug } from \"../lib/slug-resolver.js\";\nimport {\n ensureGitAvailable,\n gitAheadBehindVsFetchHead,\n gitAutoCommit,\n gitFetch,\n gitMergeFastForward,\n gitPush,\n gitStash,\n gitStashPop,\n hasUncommittedChanges,\n} from \"../lib/git.js\";\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;\nconst LOCATE_TIMEOUT_MS = 2 * 60_000; // 2 minutes to find the deployment id\nconst READY_TIMEOUT_MS = 5 * 60_000; // 5 minutes for it to flip to ready\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((r) => setTimeout(r, ms));\n}\n\nexport const pushCommand = new Command(\"push\")\n .description(\n \"Commit working-tree changes and push to the Mist-managed remote (triggers a Vercel deploy)\",\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 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); // 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 const baseMessage =\n opts.message ??\n `Publish from Mist Desktop · ${new Date().toISOString().slice(0, 19).replace(\"T\", \" \")}`;\n // Stamp the commit with the Fluid user so deploy history\n // traces back to a real person. Format:\n // \"Mike Tingey: Updated header (0182d21e-…)\"\n // Name + description go first so the prose stays scannable\n // in `git log --oneline` — the long UUID at the end won't\n // truncate the message. Best-effort: if /api/me fails,\n // hangs, or returns a malformed body, we still commit with\n // the bare message — publish is essential, the stamp is not.\n let namePrefix = \"\";\n let idSuffix = \"\";\n try {\n const me = await Promise.race([\n fetchCurrentUser(client),\n new Promise<never>((_, reject) =>\n setTimeout(\n () => reject(new Error(\"timed out after 5s\")),\n 5_000,\n ),\n ),\n ]);\n const name =\n (me.full_name && me.full_name.trim()) ||\n me.email ||\n (Number.isFinite(me.id) ? `user-${me.id}` : null);\n const id = me.public_id && me.public_id.trim();\n if (name) namePrefix = `${name}: `;\n if (id) idSuffix = ` (${id})`;\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 }\n const message = `${namePrefix}${baseMessage}${idSuffix}`;\n const commitSpinner = ora(\n `Committing changes (${message.length > 60 ? message.slice(0, 57) + \"…\" : message})…`,\n ).start();\n try {\n await gitAutoCommit(cwd, message);\n commitSpinner.succeed(\"Committed\");\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 // Without this, the loop used to grab whatever was first in the\n // list — including a stale ready deploy from a prior push that\n // skipped Vercel — and exit immediately as \"ready\".\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.error(\n `No deployment matching ${pushedSha?.slice(0, 7) ?? \"the push\"} appeared within ${LOCATE_TIMEOUT_MS / 1000}s.`,\n );\n console.error(\n \"Vercel may still be queueing — run `fluid mist deployments` to check.\",\n );\n process.exit(3);\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 console.log(`✔ Ready: ${chalk.cyan(deployment.url ?? \"(no url)\")}`);\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 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","/** `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 { runAction } from \"../../lib/error-handler.js\";\nimport { resolveSlug } from \"../../lib/slug-resolver.js\";\nimport { printJson, printTable } from \"../../lib/output.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\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 opts.showValues ? v.value : maskValue(v.value),\n v.targets.join(\",\"),\n v.managed_by === \"mist\" ? chalk.magenta(\"mist\") : chalk.dim(\"user\"),\n ]);\n printTable([header, ...rows]);\n }),\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\nimport chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport { createMistClient } from \"../../api/client.js\";\nimport { createEnvVar, listEnvVars, updateEnvVar } from \"../../api/env_vars.js\";\nimport { runAction } from \"../../lib/error-handler.js\";\nimport { resolveSlug } from \"../../lib/slug-resolver.js\";\n\ninterface Opts {\n json?: boolean;\n}\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\nexport const envSetCommand = new Command(\"set\")\n .description(\"Set one or more env vars (KEY=value KEY=value …)\")\n .argument(\"<slug-or-pair>\", \"The mist slug, or the first KEY=value pair\")\n .argument(\"[pairs...]\", \"Additional KEY=value pairs\")\n .action(async (slugOrPair: string, pairs: string[]) => {\n await runAction(async () => {\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","/** `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\nimport chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport { writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { createMistClient } from \"../../api/client.js\";\nimport { listEnvVars } from \"../../api/env_vars.js\";\nimport { runAction } from \"../../lib/error-handler.js\";\nimport { resolveSlug } from \"../../lib/slug-resolver.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 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 user = env_vars.filter((v) => v.managed_by === \"user\");\n const skipped = env_vars.filter((v) => v.managed_by === \"mist\");\n const target = join(process.cwd(), opts.out ?? \".env.local\");\n const body = user\n .map((v) => `${v.key}=${escapeValue(v.value)}`)\n .join(\"\\n\");\n writeFileSync(target, body + (body.length ? \"\\n\" : \"\"), \"utf8\");\n console.log(\n chalk.green(\n `Wrote ${user.length} var(s) to ${target.replace(process.cwd() + \"/\", \"\")}.`,\n ),\n );\n if (skipped.length > 0) {\n console.log(\n chalk.dim(\n `Skipped ${skipped.length} Mist-managed var(s): ${skipped.map((v) => v.key).join(\", \")} (local dev uses PGlite)`,\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-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\";\n\nconst plugin: FluidPlugin = {\n name: \"fluid-cli-mist\",\n version: \"0.1.0\",\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\n ctx.program.addCommand(mist);\n },\n};\n\nexport default plugin;\nexport type * from \"./types.js\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAaA,MAAM,eAAe;AAUrB,IAAa,eAAb,cAAkC,MAAM;;CAEtC;CACA;;CAEA;CAEA,YACE,SACA,QACA,SACA;AACA,QAAM,QAAQ;AACd,OAAK,OAAO;AACZ,OAAK,SAAS;AACd,OAAK,UAAU;AACf,MAAI,WAAW,EAAG,MAAK,WAAW;WACzB,UAAU,IAAK,MAAK,WAAW;MACnC,MAAK,WAAW;;;AAIzB,SAAS,UAAkB;AACzB,QAAO,QAAQ,IAAI,qBAAqB;;;AAa1C,SAAgB,mBAA+B;CAC7C,MAAM,QAAQ,cAAc;AAC5B,KAAI,CAAC,MACH,OAAM,IAAI,aACR,sDACA,IACD;CAEH,MAAM,OAAO,SAAS;CAEtB,SAAS,SAAS,MAAc,OAAyC;EACvE,MAAM,MAAM,IAAI,IAAI,KAAK,WAAW,IAAI,GAAG,OAAO,IAAI,QAAQ,KAAK;AACnE,MAAI,MACF,MAAK,MAAM,CAAC,GAAG,MAAM,OAAO,QAAQ,MAAM,EAAE;AAC1C,OAAI,KAAK,KAAM;AACf,OAAI,aAAa,IAAI,GAAG,OAAO,EAAE,CAAC;;AAGtC,SAAO,IAAI,UAAU;;CAGvB,eAAe,KACb,QACA,MACA,MACY;EACZ,MAAM,MAAM,SAAS,MAAM,MAAM,MAAM;EACvC,IAAI;AACJ,MAAI;AACF,cAAW,MAAM,MAAM,KAAK;IAC1B;IACA,SAAS;KACP,eAAe,UAAU;KACzB,QAAQ;KACR,GAAI,MAAM,SAAS,KAAA,IACf,EAAE,gBAAgB,oBAAoB,GACtC,EAAE;KACP;IACD,GAAI,MAAM,SAAS,KAAA,IACf,EAAE,MAAM,KAAK,UAAU,KAAK,KAAK,EAAE,GACnC,EAAE;IACP,CAAC;WACK,KAAK;AACZ,SAAM,IAAI,aACR,kBAAkB,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,IAClE,EACD;;AAIH,MAAI,SAAS,WAAW,IAAK,QAAO,KAAA;EAEpC,MAAM,OAAO,MAAM,SAAS,MAAM;EAClC,IAAI;AACJ,MAAI;AACF,YAAS,KAAK,SAAS,IAAI,KAAK,MAAM,KAAK,GAAG,EAAE;UAC1C;AAEN,OAAI,CAAC,SAAS,GACZ,OAAM,IAAI,aACR,QAAQ,SAAS,OAAO,IAAI,KAAK,MAAM,GAAG,IAAI,IAC9C,SAAS,OACV;AAEH;;AAGF,MAAI,CAAC,SAAS,IAAI;GAChB,MAAM,OAAO;AAEb,SAAM,IAAI,aADM,KAAK,OAAO,WAAW,QAAQ,SAAS,UACxB,SAAS,QAAQ,KAAK,OAAO,QAAQ;;AAEvE,SAAO;;AAGT,QAAO;EACL,MAAM,MAAM,UAAU,KAAK,OAAO,MAAM,EAAE,OAAO,CAAC;EAClD,OAAO,MAAM,SAAS,KAAK,QAAQ,MAAM,EAAE,MAAM,CAAC;EAClD,QAAQ,MAAM,SAAS,KAAK,SAAS,MAAM,EAAE,MAAM,CAAC;EACpD,SAAS,MAAM,SACb,KAAK,UAAU,MAAM,SAAS,KAAA,IAAY,EAAE,MAAM,GAAG,KAAA,EAAU;EACjE,MAAM,QAAQ,MAAM,MAAM;GACxB,MAAM,MAAM,SAAS,KAAK;AAC1B,UAAO,MAAM,KAAK;IAChB,GAAG;IACH,SAAS;KACP,eAAe,UAAU;KACzB,QAAQ;KACR,GAAI,MAAM,WAAW,EAAE;KACxB;IACF,CAAC;;EAEL;;;;ACjIH,MAAM,aAAa;AACnB,SAAS,oBACP,QACA,SAA6B,EAAE,EACJ;AAC3B,QAAO,OAAO,IACZ,YACA,OACD;;AAGH,MAAM,wBAAQ,IAAI,KAAqB;AAEvC,MAAM,aACJ;;AAGF,SAAS,OAAO,GAAoB;AAClC,QAAO,WAAW,KAAK,EAAE,MAAM,CAAC;;;;;;AAOlC,eAAsB,cACpB,QACA,UACiB;CACjB,MAAM,MAAM,SAAS,MAAM;AAC3B,KAAI,CAAC,IAAK,OAAM,IAAI,MAAM,yBAAyB;AACnD,KAAI,OAAO,IAAI,CAAE,QAAO;CACxB,MAAM,MAAM,MAAM,IAAI,IAAI;AAC1B,KAAI,IAAK,QAAO;CAKhB,MAAM,WAAW,MAAM,oBAAoB,QAAQ,EAAE,OAAO,KAAK,CAAC;AAClE,MAAK,MAAM,KAAK,SAAS,SAAS,EAAE,CAClC,KAAI,EAAE,KAAM,OAAM,IAAI,EAAE,MAAM,EAAE,GAAG;CAErC,MAAM,QAAQ,MAAM,IAAI,IAAI;AAC5B,KAAI,CAAC,MACH,OAAM,IAAI,MACR,4BAA4B,IAAI,qDACjC;AAEH,QAAO;;;;ACxDT,MAAMA,SAAO;AASb,SAAgB,UACd,QACA,SAA0B,EAAE,EACD;AAC3B,QAAO,OAAO,IAAsBA,QAAM,OAAkC;;AAG9E,eAAsB,SACpB,QACA,MAC2B;CAC3B,MAAM,KAAK,MAAM,cAAc,QAAQ,KAAK;AAC5C,QAAO,OAAO,IAAsB,GAAGA,OAAK,GAAG,KAAK;;AAGtD,SAAgB,WACd,QACA,MAC2B;AAC3B,QAAO,OAAO,KAAuBA,QAAM,KAAK;;AAGlD,eAAsB,WACpB,QACA,MACA,MAC2B;CAC3B,MAAM,KAAK,MAAM,cAAc,QAAQ,KAAK;AAC5C,QAAO,OAAO,MAAwB,GAAGA,OAAK,GAAG,MAAM,KAAK;;AAG9D,eAAsB,WACpB,QACA,MAC2B;CAC3B,MAAM,KAAK,MAAM,cAAc,QAAQ,KAAK;AAC5C,QAAO,OAAO,OAAyB,GAAGA,OAAK,GAAG,KAAK;;AAGzD,eAAsB,YACpB,QACA,MAC2B;CAC3B,MAAM,KAAK,MAAM,cAAc,QAAQ,KAAK;AAC5C,QAAO,OAAO,KAAuB,GAAGA,OAAK,GAAG,GAAG,UAAU;;;;ACnD/D,MAAM,YAA0C;CAC9C,SAAS;CACT,UAAU;CACV,eAAe;CACf,iBAAiB;CACjB,cAAc;CACd,QAAQ;CACR,WAAW;CACX,aAAa;CACb,UAAU;CACX;;AAGD,MAAa,iBAAiB;AAE9B,IAAa,qBAAb,cAAwC,MAAM;CAC5C,YAAY,SAAiB;AAC3B,QAAM,QAAQ;AACd,OAAK,OAAO;;;;;;;;;;;;;AAiDhB,SAAgB,gBAAgB,KAAqB;CACnD,MAAM,UAAU,IAAI,MAAM;AAC1B,KAAI,CAAC,QACH,OAAM,IAAI,mBACR,2EACD;AAGH,KAAI,qBAAqB,KAAK,QAAQ,CAAE,QAAO;CAE/C,MAAM,YAAY,QAAQ,YAAY,IAAI;AAC1C,KAAI,aAAa,KAAK,cAAc,QAAQ,SAAS,EACnD,OAAM,IAAI,mBACR,8BAA8B,IAAI,sDACnC;CAEH,MAAM,WAAW,QAAQ,MAAM,GAAG,UAAU,CAAC,MAAM;CACnD,MAAM,SAAS,QAAQ,MAAM,YAAY,EAAE,CAAC,MAAM;AAClD,KAAI,yBAAyB,SAAS,KAAK,uBACzC,OAAM,IAAI,mBACR,4FAA4F,IAAI,KACjG;AAEH,KAAI,QAAQ,KAAK,OAAO,CACtB,OAAM,IAAI,mBACR,uGAAuG,IAAI,IAC5G;AAEH,KAAI,CAAC,qBAAqB,KAAK,OAAO,CACpC,OAAM,IAAI,mBACR,yBAAyB,OAAO,2BACjC;AAEH,QAAO;;;AAIT,SAAgB,yBAAyB,UAAgC;AAMvE,KAL8B;EAC5B;EACA;EACA;EACD,CACS,SAAS,SAAyB,CAAE,QAAO;CACrD,MAAM,SAAS,UAAU,SAAS,aAAa,CAAC,QAAQ,QAAQ,GAAG;AACnE,KAAI,OAAQ,QAAO;AACnB,OAAM,IAAI,mBACR,0BAA0B,SAAS,iBAAiB,eAAe,GACpE;;;;;;;;;;;ACvHH,MAAM,YAAY;AAElB,IAAa,sBAAb,cAAyC,MAAM;CAC7C,YAAY,SAAiB;AAC3B,QAAM,QAAQ;AACd,OAAK,OAAO;;;AAIhB,SAAS,aAAa,KAA8B;CAClD,MAAM,WAAW,KAAK,KAAK,UAAU;AACrC,KAAI,CAAC,WAAW,SAAS,CAAE,QAAO;AAClC,KAAI;EACF,MAAM,MAAM,aAAa,UAAU,OAAO;EAC1C,MAAM,SAAS,KAAK,MAAM,IAAI;AAC9B,MAAI,CAAC,UAAU,OAAO,WAAW,SAAU,QAAO;EAClD,MAAM,MAAM;AACZ,MAAI,OAAO,IAAI,YAAY,SAAU,QAAO;AAC5C,SAAO;GACL,MAAM,IAAI;GACV,aACE,OAAO,IAAI,mBAAmB,WACzB,IAAI,iBACL;GACN,YACE,OAAO,IAAI,kBAAkB,WACxB,IAAI,gBACL;GACP;SACK;AACN,SAAO;;;;AAKX,SAAgB,YAAY,KAAc,MAAc,QAAQ,KAAK,EAAU;AAC7E,KAAI,OAAO,IAAI,MAAM,CAAC,SAAS,EAAG,QAAO,IAAI,MAAM;CACnD,MAAM,OAAO,aAAa,IAAI;AAC9B,KAAI,KAAM,QAAO,KAAK;AACtB,OAAM,IAAI,oBACR,iHACD;;;AAIH,SAAgB,cAAc,KAAa,MAAkB;CAC3D,MAAM,UAAoB;EACxB,MAAM,KAAK;EACX,aAAa,KAAK;EAClB,YAAY,KAAK;EAClB;AACD,eACE,KAAK,KAAK,UAAU,EACpB,KAAK,UAAU,SAAS,MAAM,EAAE,GAAG,MACnC,OACD;;;;;;;;;;;;;ACpDH,eAAsB,UAAU,IAA+C;AAC7E,KAAI;AACF,QAAM,IAAI;UACH,KAAK;AACZ,SAAO,IAAI;;;AAIf,SAAS,OAAO,KAAqB;AACnC,KAAI,eAAe,qBAAqB;AACtC,UAAQ,MAAM,MAAM,IAAI,IAAI,QAAQ,CAAC;AACrC,UAAQ,KAAK,EAAE;;AAEjB,KAAI,eAAe,oBAAoB;AACrC,UAAQ,MAAM,MAAM,IAAI,IAAI,QAAQ,CAAC;AACrC,UAAQ,KAAK,EAAE;;AAEjB,KAAI,eAAe,cAAc;AAE/B,MAAI,IAAI,WAAW,KAAK;GACtB,MAAM,SAAS,IAAI,UAAU;AAC7B,WAAQ,MACN,MAAM,IACJ,SACI,oCAAoC,OAAO,OAAO,CAAC,0CACnD,IAAI,QACT,CACF;aACQ,IAAI,WAAW,KAAK;GAC7B,MAAM,OAAO,IAAI,UAAU;AAC3B,WAAQ,MACN,MAAM,OACJ,OAAO,SAAS,WACZ,6CAA6C,KAAK,MAAM,GAAG,GAAG,CAAC,8CAC/D,IAAI,QACT,CACF;aACQ,IAAI,WAAW,KAAK;GAG7B,MAAM,UAAU,IAAI,WAAW,EAAE;GACjC,MAAM,WAAW,QAAQ,sBAAsB,QAAQ;GACvD,MAAM,aACJ,OAAO,IAAI,YAAY,YACvB,0DAA0D,KACxD,IAAI,QACL;AACH,OACE,MAAM,QAAQ,SAAS,IACvB,SAAS,MAAM,MAAM,kCAAkC,KAAK,OAAO,EAAE,CAAC,CAAC,CAEvE,SAAQ,MACN,MAAM,OACJ,6FACD,CACF;YACQ,WACT,SAAQ,MACN,MAAM,OACJ,yLACD,CACF;YAED,OAAO,IAAI,YAAY,YACvB,kCAAkC,KAAK,IAAI,QAAQ,EACnD;IACA,MAAM,KAAK,QAAQ,sBAAsB,QAAQ;AACjD,YAAQ,MACN,MAAM,OACJ,wBAAwB,MAAM,OAAO,KAAK,OAAO,GAAG,KAAK,GAAG,mBAC7D,CACF;SAED,SAAQ,MAAM,MAAM,IAAI,IAAI,QAAQ,CAAC;aAE9B,IAAI,WAAW,IACxB,SAAQ,MACN,MAAM,IACJ,qGACD,CACF;MAED,SAAQ,MAAM,MAAM,IAAI,IAAI,QAAQ,CAAC;AAEvC,UAAQ,KAAK,IAAI,SAAS;;AAG5B,KAAI,eAAe,MACjB,SAAQ,MAAM,MAAM,IAAI,IAAI,QAAQ,CAAC;KAErC,SAAQ,MAAM,MAAM,IAAI,OAAO,IAAI,CAAC,CAAC;AAEvC,SAAQ,KAAK,EAAE;;;;;;;;ACnGjB,SAAgB,UAAU,SAAwB;AAChD,SAAQ,OAAO,MAAM,KAAK,UAAU,SAAS,MAAM,EAAE,GAAG,KAAK;;AAG/D,MAAMC,gBAA4D;CAChE,SAAS,MAAM;CACf,cAAc,MAAM;CACpB,MAAM,MAAM;CACZ,QAAQ,MAAM;CACd,iBAAiB,MAAM;CACvB,UAAU,MAAM;CACjB;AAED,SAAgB,WAAW,OAA8B;AACvD,QAAOA,cAAY,OAAO,MAAM;;;;;;;;AASlC,SAAgB,UACd,MACA,YACQ;CACR,MAAM,MAAM,QAAQ;AACpB,QAAO,MAAM,MAAM,KAAK,IAAI,GAAG,MAAM,IAAI,YAAY;;;;;;;;;;AAWvD,SAAgB,WAAW,MAA6C;AACtE,KAAI,KAAK,WAAW,EAAG;CACvB,MAAM,SAAmB,EAAE;AAC3B,MAAK,MAAM,OAAO,KAChB,KAAI,SAAS,MAAM,MAAM;EACvB,MAAM,UAAU,UAAU,KAAK,CAAC;AAChC,SAAO,KAAK,KAAK,IAAI,OAAO,MAAM,GAAG,QAAQ;GAC7C;AAEJ,MAAK,MAAM,OAAO,MAAM;EACtB,MAAM,SAAS,IAAI,KAAK,MAAM,MAAM;GAClC,MAAM,IAAI,OAAO,MAAM;GACvB,MAAM,OAAO,QAAQ;AACrB,UAAO,OAAO,IAAI,OAAO,KAAK,IAAI,GAAG,IAAI,UAAU,KAAK,CAAC,OAAO,CAAC;IACjE;AACF,UAAQ,OAAO,MAAM,OAAO,KAAK,KAAK,GAAG,KAAK;;;AAOlD,SAAS,UAAU,GAAsC;AAEvD,SAAQ,KAAK,IAAI,QAAQ,mBAAmB,GAAG;;AAGjD,SAAgB,aAAa,KAA4B;AACvD,KAAI,CAAC,IAAK,QAAO;CACjB,MAAM,OAAO,KAAK,KAAK,GAAG,IAAI,KAAK,IAAI,CAAC,SAAS;CACjD,MAAM,UAAU,KAAK,MAAM,OAAO,IAAO;AACzC,KAAI,UAAU,EAAG,QAAO;AACxB,KAAI,UAAU,GAAI,QAAO,GAAG,QAAQ;CACpC,MAAM,QAAQ,KAAK,MAAM,UAAU,GAAG;AACtC,KAAI,QAAQ,GAAI,QAAO,GAAG,MAAM;CAChC,MAAM,OAAO,KAAK,MAAM,QAAQ,GAAG;AACnC,KAAI,OAAO,GAAI,QAAO,GAAG,KAAK;AAC9B,QAAO,IAAI,KAAK,IAAI,CAAC,aAAa,CAAC,MAAM,GAAG,GAAG;;;AAIjD,SAAgB,eAAe,KAAqB;AAElD,QADU,IAAI,KAAK,IAAI,CACd,aAAa,CAAC,MAAM,GAAG,GAAG;;;;;ACrErC,MAAa,cAAc,IAAI,QAAQ,OAAO,CAC3C,YAAY,oCAAoC,CAChD,OACC,mBACA,6EACD,CACA,OAAO,iBAAiB,qCAAqC,CAC7D,OAAO,eAAe,gCAAgC,CACtD,OAAO,UAAU,oBAAoB,CACrC,QAAQ,SACP,UAAU,YAAY;CACpB,MAAM,SAAS,kBAAkB;CACjC,MAAM,QAAQ,KAAK,QAAQ,SAAS,KAAK,OAAO,GAAG,GAAG,KAAA;CACtD,MAAM,WAAW,MAAM,UAAU,QAAQ;EACvC,GAAI,KAAK,QAAQ,EAAE,OAAO,KAAK,OAAO,GAAG,EAAE;EAC3C,GAAI,KAAK,OAAO,EAAE,MAAM,KAAK,MAAM,GAAG,EAAE;EACxC,GAAI,SAAS,OAAO,SAAS,MAAM,GAAG,EAAE,OAAO,GAAG,EAAE;EACrD,CAAC;AACF,KAAI,KAAK,KAAM,QAAO,UAAU,SAAS;CACzC,MAAM,QAAQ,SAAS,SAAS,EAAE;AAClC,KAAI,MAAM,WAAW,GAAG;AACtB,UAAQ,IAAI,MAAM,IAAI,kBAAkB,CAAC;AACzC;;AAgBF,YAAW,CAdI;EACb,MAAM,KAAK,OAAO;EAClB,MAAM,KAAK,OAAO;EAClB,MAAM,KAAK,QAAQ;EACnB,MAAM,KAAK,WAAW;EACtB,MAAM,KAAK,UAAU;EACtB,EAQmB,GAPP,MAAM,KAAK,MAAM;EAC5B,EAAE;EACF,EAAE,QAAQ,EAAE,SAAS,QAAQ,EAAE,eAAe,MAAM,IAAI,YAAY;EACpE,WAAW,EAAE,MAAM;EACnB,EAAE,iBAAiB,MAAM,IAAI,aAAa;EAC1C,aAAa,EAAE,WAAW;EAC3B,CAAC,CAC0B,CAAC;EAC7B,CACH;;;ACrCH,SAAS,SAAS,QAAwB;AACxC,QAAO,sBAAsB,OAAO;;AAGtC,eAAsB,sBACpB,QACA,MACwC;CACxC,MAAM,KAAK,MAAM,cAAc,QAAQ,KAAK;AAC5C,QAAO,OAAO,IAAmC,SAAS,GAAG,CAAC;;AAGhE,eAAsB,uBACpB,QACA,MACA,OACuC;CACvC,MAAM,KAAK,MAAM,cAAc,QAAQ,KAAK;AAC5C,QAAO,OAAO,KAAmC,SAAS,GAAG,EAAE,EAC7D,mBAAmB,OACpB,CAAC;;AAGJ,eAAsB,uBACpB,QACA,MACA,oBACe;CACf,MAAM,KAAK,MAAM,cAAc,QAAQ,KAAK;AAC5C,QAAO,OAAO,OAAa,GAAG,SAAS,GAAG,CAAC,GAAG,qBAAqB;;;;;;;AAQrE,SAAgB,4BACd,mBAC8B;AAC9B,SAAQ,qBAAqB,EAAE,EAAE,MAAM,OAAO,GAAG,SAAS,UAAU;;;;;;;;AAStE,SAAgB,8BACd,mBACA,QAC8B;AAC9B,SAAQ,qBAAqB,EAAE,EAAE,MAAM,OAAO;AAC5C,MAAI,OAAO,QAAQ,GAAG,SAAS,OAAO,KAAM,QAAO;AACnD,MAAI,OAAO,QAAQ,GAAG,sBAAsB,OAAO,KAAM,QAAO;AAChE,MAAI,OAAO,MAAM,QAAQ,GAAG,oBAAoB,OAAO,GAAI,QAAO;AAClE,SAAO;GACP;;;;;;;;ACzDJ,SAAS,WAAW,IAA8B;AAChD,KAAI,GAAG,kBAAmB,QAAO,GAAG;AACpC,KAAI,GAAG,mBAAmB,KAAM,QAAO,IAAI,GAAG;AAC9C,QAAO;;AAGT,MAAa,cAAc,IAAI,QAAQ,OAAO,CAC3C,YAAY,wBAAwB,CACpC,SAAS,UAAU,gDAAgD,CACnE,OAAO,UAAU,oBAAoB,CACrC,QAAQ,MAA0B,SACjC,UAAU,YAAY;CACpB,MAAM,WAAW,YAAY,KAAK;CAClC,MAAM,SAAS,kBAAkB;CACjC,MAAM,EAAE,SAAS,MAAM,SAAS,QAAQ,SAAS;CAEjD,MAAM,qBAAqB,MAAM,sBAAsB,QAAQ,SAAS,EACrE;AAEH,KAAI,KAAK,KACP,QAAO,UAAU;EAAE;EAAM,oBAAoB;EAAmB,CAAC;CAEnE,MAAM,QAAkB,EAAE;CAC1B,MAAM,QAAQ,UAAU,KAAK,MAAM,KAAK,YAAY;AACpD,OAAM,KAAK,GAAG,MAAM,IAAI,MAAM,IAAI,KAAK,KAAK,GAAG;AAC/C,KAAI,KAAK,eAAe,KAAK,QAAQ,KAAK,SAAS,KAAK,YACtD,OAAM,KAAK,cAAc,MAAM,IAAI,KAAK,YAAY,GAAG;AAEzD,OAAM,KAAK,cAAc,WAAW,KAAK,MAAM,GAAG;AAClD,KAAI,KAAK,KAAM,OAAM,KAAK,cAAc,KAAK,OAAO;AACpD,KAAI,KAAK,WACP,OAAM,KAAK,cAAc,MAAM,KAAK,KAAK,WAAW,GAAG;AACzD,KAAI,kBAAkB,SAAS,GAAG;AAChC,QAAM,KAAK,cAAc,kBAAkB,SAAS;AACpD,OAAK,MAAM,MAAM,kBACf,OAAM,KACJ,OAAO,MAAM,IAAI,IAAI,CAAC,GAAG,GAAG,KAAK,IAAI,WAAW,GAAG,CAAC,IAAI,MAAM,IAAI,eAAe,GAAG,KAAK,GAC1F;;AAGL,KAAI,KAAK,eACP,OAAM,KAAK,cAAc,aAAa,KAAK,eAAe,GAAG;AAC/D,OAAM,KAAK,cAAc,aAAa,KAAK,WAAW,GAAG;AACzD,KAAI,KAAK,qBACP,OAAM,KACJ,MAAM,QACJ,wBAAwB,eAAe,KAAK,qBAAqB,CAAC,6BAA6B,KAAK,KAAK,aAC1G,CACF;AAEH,SAAQ,IAAI,MAAM,KAAK,KAAK,CAAC;EAC7B,CACH;;;;;;;;ACpEH,SAAgB,oBAAkC;CAChD,MAAM,UAAe,KAAK;AAC1B,QAAO;EACL,MAAM,MAAM;AACV,WAAQ,MAAM,KAAK;;EAErB,OAAO,MAAM;AACX,WAAQ,OAAO;;EAEjB,QAAQ,MAAM;AACZ,WAAQ,QAAQ,KAAK;;EAEvB,KAAK,MAAM;AAET,WAAQ,KAAK,KAAK;;EAEpB,KAAK,MAAM;AAGT,WAAQ,eAAe;IAAE,QAAQ;IAAK;IAAM,CAAC;;EAE/C,KAAK,MAAM;AACT,WAAQ,KAAK,KAAK;;EAEpB,KAAK,MAAM;AACT,WAAQ,KAAK,KAAK;;EAErB;;;;;ACJH,SAAgB,uBAAqC;AACnD,QAAO;EACL,QAAQ;EACR,SAAS;EACT,UAAU;EACV,OAAO;EACP,OAAO;EACP,OAAO;EACP,OAAO;EACR;;;;ACtBH,MAAMC,SAAO;;;;;;;AAQb,MAAa,yBAA4C;CACvD;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;;AAGD,SAAgB,mBAAmB,UAGjC;CACA,MAAM,OAAO,SAAS,QAAQ,QAAQ,GAAG;AACzC,QAAO;EACL,qBAAqB,GAAG,KAAK;EAC7B,uBAAuB,GAAG,KAAK;EAChC;;;;;;AAOH,SAAgB,wBAAwB,MAIjB;CACrB,MAAM,SAAS,CAAC,GAAI,KAAK,mBAAmB,uBAAwB;CACpE,MAAM,UAAU;CAChB,MAAM,QAA4B;EAChC,MAAM,KAAK;EACX,kBAAkB;EAClB,UAAU;GACR,kBAAkB,EAAE,SAAS;GAC7B,cAAc,EAAE,SAAS;GAC1B;EACF;AACD,KAAI,KAAK,UAAU;AACjB,QAAM,YAAY,KAAK;AACvB,SAAO,OAAO,OAAO,mBAAmB,KAAK,SAAS,CAAC;;AAEzD,QAAO;;AAGT,SAAgB,cACd,QACA,MACgC;AAChC,QAAO,OAAO,KAA4BA,QAAM,KAAK;;;AAWvD,SAAgB,cACd,QACA,MACA,OAC8B;AAC9B,QAAO,OAAO,MACZ,GAAGA,OAAK,GAAG,mBAAmB,KAAK,IACnC,EAAE,SAAS,OAAO,CACnB;;;AAIH,SAAgB,mBACd,QACA,MACA,UAC8B;AAC9B,QAAO,cAAc,QAAQ,MAAM;EACjC,WAAW;EACX,GAAG,mBAAmB,SAAS;EAChC,CAAC;;;;ACrGJ,MAAMC,SAAO;;;;;;;AAQb,SAAgB,yBAAyB,MAKjB;CACtB,MAAM,CAAC,MAAM,SAAS,KAAK,QAAQ,IAAI,MAAM,KAAK;CAClD,MAAM,WAAyD,EAAE;AACjE,KAAI,KAAM,UAAS,OAAO;AAC1B,KAAI,KAAM,UAAS,OAAO;AAC1B,KAAI,KAAK,YAAY,KAAM,UAAS,SAAS,KAAK;CAElD,MAAM,QAA6B,EAAE,MAAM,KAAK,MAAM;AACtD,KAAI,KAAK,SAAU,OAAM,YAAY,KAAK;AAC1C,KAAI,OAAO,KAAK,SAAS,CAAC,SAAS,EAAG,OAAM,WAAW;AACvD,QAAO;;AAGT,SAAgB,eACd,QACA,MACiC;AACjC,QAAO,OAAO,KAA6BA,QAAM,KAAK;;;;AC7BxD,MAAM,OAAO;;;;;;AAcb,SAAgB,6BAA6B,MAKjB;AAC1B,QAAO;EACL,MAAM,KAAK;EACX,WAAW,KAAK;EAChB,iBAAiB,KAAK,iBAAA;EACtB,QAAQ,KAAK,YAAA;EACb,YAAY;EACb;;AAGH,SAAgB,mBACd,QACA,MACqC;AACrC,QAAO,OAAO,KAAiC,MAAM,KAAK;;;;ACzC5D,SAASC,OAAK,IAAY,KAAc;AACtC,QAAO,MACH,sBAAsB,GAAG,YAAY,mBAAmB,IAAI,KAC5D,sBAAsB,GAAG;;AAG/B,eAAsB,YACpB,QACA,MAC8B;CAC9B,MAAM,KAAK,MAAM,cAAc,QAAQ,KAAK;AAC5C,QAAO,OAAO,IAAyBA,OAAK,GAAG,CAAC;;;AASlD,eAAsB,aACpB,QACA,MACA,KACA,OACA,SACyB;CACzB,MAAM,KAAK,MAAM,cAAc,QAAQ,KAAK;AAC5C,QAAO,OAAO,KAAqBA,OAAK,GAAG,EAAE,EAC3C,SAAS;EAAE;EAAK;EAAO;EAAS,EACjC,CAAC;;;AAIJ,eAAsB,aACpB,QACA,MACA,KACA,OACA,SACyB;CACzB,MAAM,KAAK,MAAM,cAAc,QAAQ,KAAK;AAC5C,QAAO,OAAO,MAAsBA,OAAK,IAAI,IAAI,EAAE,EACjD,SAAS;EAAE;EAAO;EAAS,EAC5B,CAAC;;AAGJ,eAAsB,aACpB,QACA,MACA,KACe;CACf,MAAM,KAAK,MAAM,cAAc,QAAQ,KAAK;AAC5C,QAAO,OAAO,OAAaA,OAAK,IAAI,IAAI,CAAC;;;;;;;;;;;;ACjB3C,MAAa,2BAA2B;CACtC;CACA;CACA;CACD;;;;;;;;;;;;;;;;;;;;;;;;;;ACaD,MAAM,iBAAkC;CACtC,oBAAoB;CACpB,qBAAqB;CACrB,cAAc;CACd,eAAe;CAChB;AAkDD,MAAM,gBAAgB,OACpB,IAAI,SAAS,YAAY,WAAW,SAAS,GAAG,CAAC;AAEnD,MAAMC,eAA0C;CAC9C,SAAS;CACT,aAAa;CACb,gBAAgB;CACjB;;;;;AAMD,eAAsB,iBACpB,QACA,SACiC;CACjC,MAAM,EAAE,UAAU,UAAU,SAAS;CACrC,MAAM,SAAS;EAAE,GAAG;EAAgB,GAAG,QAAQ;EAAQ;CACvD,MAAM,QAAQ,QAAQ,SAAS;CAC/B,MAAM,QAAQA,aAAW;AAGzB,UAAS,MAAM,kBAAkB;CACjC,MAAM,QAAQ,MAAM,SAAS,QAAQ,SAAS,EAAE;AAChD,UAAS,QAAQ,QAAQ,KAAK,KAAK,IAAI,KAAK,MAAM,GAAG;AAGrD,UAAS,MAAM,kCAAkC;CACjD,MAAM,UAAU,MAAM,sBAAsB,QAAQ,SAAS,EAC1D;CAEH,IAAI,cAA6B;CACjC,IAAI,mBAAkC;CACtC,IAAI,cAA6B;CACjC,IAAI,iBAAiB;CACrB,IAAI,cAAc;CAClB,IAAI;CAEJ,MAAM,oBACJ,QAAQ,gBAAgB,QAAQ,QAAQ,cAAc;CAGxD,MAAM,gBAAgB,oBAClB,8BAA8B,QAAQ;EACpC;EACA,MAAM,QAAQ;EACd,IAAI,QAAQ;EACb,CAAC,GACF,SAAS,YACP,4BAA4B,OAAO,GACnC,KAAA;AAEN,KAAI,eAAe;AACjB,WAAS,KACP,GAAG,MAAM,kCAAkC,cAAc,GAAG,cAC7D;AACD,gBACE,cAAc,qBAAqB,QAAQ,gBAAgB;AAC7D,qBACE,cAAc,mBAAmB,QAAQ,cAAc;AACzD,qBAAmB;GACjB,IAAI,cAAc;GAClB,kBAAkB,cAAc,qBAAqB;GACrD,SAAS;GACV;QACI;AACL,WAAS,QAAQ,8BAA8B;EAG/C,IAAI,aAAiC,QAAQ;EAC7C,IAAI,WAA+B,QAAQ;AAE3C,MAAI,CAAC,mBAAmB;GACtB,MAAM,WAAW,MAAM,iBAAiB,QAAQ;IAC9C;IACA,SAAS,KAAK;IACd;IACA;IACA;IACD,CAAC;GACF,MAAM,OACJ,QAAQ,MAAM,MAAM,IAAI,KAAK,MAAM,MAAM,IAAI,KAAK;AACpD,YAAS,MAAM,YAAY,MAAM,IAAI,KAAK,IAAI;AAE9C,OAAI,SAAS,WAAW;IACtB,MAAM,UAAU,MAAM,cAAc,QAAQ,EAC1C,SAAS,wBAAwB;KAC/B;KACA;KACA,iBAAiB,QAAQ;KAC1B,CAAC,EACH,CAAC;AACF,kBAAc,QAAQ,QAAQ;AAC9B,uBAAmB,QAAQ,QAAQ,MAAM;AACzC,kBAAc,QAAQ,QAAQ;AAC9B,iBAAa,QAAQ,QAAQ;AAC7B,kBAAc,QAAQ,SAAS;AAC/B,aAAS,QACP,mBAAmB,cAAc,WAAW,KAAK,yBAClD;AAED,QAAI,CAAC,YAAY,aAAa;KAC5B,MAAM,WAAW,MAAM,iBAAiB,QAAQ;MAC9C;MACA,SAAS;MACT;MACA;MACA;MACD,CAAC;AACF,SAAI,UAAU;AACZ,eAAS,MAAM,iCAAiC;AAChD,YAAM,mBAAmB,QAAQ,aAAa,SAAS;AACvD,oBAAc;AACd,eAAS,QAAQ,gBAAgB;WAEjC,UAAS,KACP,6FACD;;cAGI,SAAS,aAAa;AAC/B,QAAI,CAAC,SACH,OAAM,IAAI,MACR,yFACD;IAEH,MAAM,UAAU,MAAM,eAAe,QAAQ,EAC3C,WAAW,yBAAyB;KAClC;KACA;KACA,MAAM,QAAQ;KACd,UAAU,QAAQ;KACnB,CAAC,EACH,CAAC;AACF,kBAAc,QAAQ,UAAU;AAChC,kBAAc,QAAQ,UAAU;AAChC,iBAAa,QAAQ,UAAU;AAC/B,kBAAc;AACd,aAAS,QAAQ,qBAAqB,cAAc;UAC/C;AAEL,QAAI,CAAC,SACH,OAAM,IAAI,MACR,4FACD;IAEH,MAAM,UAAU,MAAM,mBAAmB,QAAQ,EAC/C,eAAe,6BAA6B;KAC1C;KACA;KACA,eAAe,QAAQ;KACvB,UAAU,QAAQ;KACnB,CAAC,EACH,CAAC;AACF,uBAAmB,QAAQ,cAAc;AACzC,kBAAc,QAAQ,cAAc;AACpC,eAAW,QAAQ,cAAc;AACjC,kBAAc;AACd,aAAS,QAAQ,yBAAyB,mBAAmB;;AAE/D,oBAAiB;SACZ;AAEL,iBAAc,QAAQ,gBAAgB;AACtC,sBAAmB,QAAQ,cAAc;AACzC,iBAAc;;EAKhB,MAAM,cACJ,SAAS,iBACL;GAAE;GAAM,iBAAiB;GAAW,GACpC;GAAE;GAAM,mBAAmB;GAAa;AAC9C,WAAS,MAAM,aAAa,MAAM,GAAG;EACrC,IAAI;AACJ,MAAI;AACF,YAAS,MAAM,uBAAuB,QAAQ,UAAU,YAAY;WAC7D,KAAK;AACZ,YAAS,KAAK,gBAAgB;AAC9B,SAAM,qBAAqB,KAAK,cAAc,OAAO,SAAS,EAAE,KAAK;;AAEvE,qBAAmB;GACjB,IAAI,OAAO,kBAAkB;GAC7B,kBAAkB,OAAO,kBAAkB,qBAAqB;GAChE,SAAS;GACV;AAED,MAAI,CAAC,YACH,eACE,OAAO,kBAAkB,qBAAqB,cAAc;AAEhE,MAAI,oBAAoB,KACtB,oBAAmB,OAAO,kBAAkB,mBAAmB;AAEjE,WAAS,QACP,SAAS,YACL,oDACA,GAAG,MAAM,WACd;;CAIH,MAAM,aACJ,SAAS,YACL,MAAM,iBAAiB,QAAQ;EAC7B;EACA;EACA;EACA;EACA,SAAS,QAAQ,cAAc;EAChC,CAAC,GACF;AAEN,QAAO;EACL,MAAM;GACJ,IAAI,KAAK;GACT,MAAM,KAAK;GACX,MAAM,KAAK;GACX,YAAY,KAAK;GACjB,OAAO,KAAK;GACb;EACD;EACA,SAAS;GAAE,MAAM;GAAa,IAAI;GAAkB,MAAM;GAAa;EACvE,kBAAkB,oBAAoB;GACpC,IAAI;GACJ,kBAAkB;GAClB,SAAS;GACV;EACD;EACA;EACA;EACD;;;;;;;AAQH,SAAS,qBACP,KACA,YACA,MACO;AACP,KACE,eAAe,iBACd,IAAI,WAAW,OAAO,IAAI,WAAW,MACtC;EACA,MAAM,OAAO,GAAG,IAAI,QAAQ,GAAG,KAAK,UAAU,IAAI,WAAW,EAAE,CAAC;AAChE,MACE,+DAA+D,KAAK,KAAK,CAEzE,QAAO,IAAI,aACT,OAAOA,aAAW,MAAM,GAAG,WAAW,iIAEtC,IAAI,QACJ,IAAI,QACL;;AAGL,QAAO,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,IAAI,CAAC;;;;;;AAO5D,eAAe,iBACb,QACA,MAOwB;AACxB,KAAI,KAAK,QAAS,QAAO,KAAK;AAE9B,MAAK,SAAS,MAAM,qCAAqC;CACzD,MAAM,WAAW,KAAK,KAAK,GAAG,KAAK,OAAO;AAC1C,UAAS;EACP,MAAM,OAAO,MAAM,SAAS,QAAQ,KAAK,SAAS,EAAE,KAAK;AACzD,MAAI,KAAK;AACP,QAAK,SAAS,QAAQ,qBAAqB,MAAM;AACjD,UAAO;;AAET,MAAI,KAAK,KAAK,IAAI,SAAU;AAC5B,QAAM,KAAK,MAAM,KAAK,OAAO,oBAAoB;;AAEnD,QAAO;;;;;;;AAQT,eAAe,iBACb,QACA,MAOmE;CACnE,MAAM,WAAW,CAAC,GAAG,yBAAyB;AAC9C,KAAI,CAAC,KAAK,QACR,QAAO;EAAE,OAAO;EAAO,SAAS,EAAE;EAAE,SAAS;EAAU;AAGzD,MAAK,SAAS,MAAM,iCAAiC;CACrD,MAAM,WAAW,KAAK,KAAK,GAAG,KAAK,OAAO;CAC1C,IAAI,UAAoB,EAAE;AAE1B,UAAS;EACP,MAAM,WAAW,MAAM,YAAY,QAAQ,KAAK,SAAS,EAAE,YAAY,EAAE;EACzE,MAAM,UAAU,IAAI,IAClB,QAAQ,QAAQ,MAAM,EAAE,eAAe,OAAO,CAAC,KAAK,MAAM,EAAE,IAAI,CACjE;AACD,YAAU,SAAS,QAAQ,MAAM,QAAQ,IAAI,EAAE,CAAC;AAChD,MAAI,QAAQ,WAAW,SAAS,QAAQ;AACtC,QAAK,SAAS,QAAQ,iDAAiD;AACvE,UAAO;IAAE,OAAO;IAAM;IAAS,SAAS,EAAE;IAAE;;AAE9C,MAAI,KAAK,KAAK,IAAI,SAAU;AAC5B,OAAK,SAAS,OACZ,mCAAmC,QAAQ,OAAO,GAAG,SAAS,OAAO,8BACtE;AACD,QAAM,KAAK,MAAM,KAAK,OAAO,cAAc;;CAG7C,MAAM,UAAU,SAAS,QAAQ,MAAM,CAAC,QAAQ,SAAS,EAAE,CAAC;AAC5D,MAAK,SAAS,KACZ,wDAAwD,QAAQ,OAAO,GAAG,SAAS,OAAO,wDAClC,QAAQ,KAAK,KAAK,GAC3E;AACD,QAAO;EAAE,OAAO;EAAO;EAAS;EAAS;;;;ACzb3C,MAAM,eAA4C;CAChD,SAAS;CACT,UAAU;CACV,aAAa;CACb,WAAW;CACX,UAAU;CACV,MAAM;CACN,gBAAgB;CAChB,cAAc;CACd,QAAQ;CACR,iBAAiB;CACjB,cAAc;CACf;AAED,MAAa,oBAAoB;AAEjC,SAAgB,iBAAiB,KAA0B;CACzD,MAAM,MAAM,aAAa,IAAI,MAAM,CAAC,aAAa;AACjD,KAAI,CAAC,IACH,OAAM,IAAI,MACR,yBAAyB,IAAI,iBAAiB,kBAAkB,GACjE;AAEH,QAAO;;AAGT,SAAS,YAAY,KAA6C;AAChE,KAAI,OAAO,KAAM,QAAO,KAAA;CACxB,MAAM,IAAI,OAAO,IAAI;AACrB,KAAI,CAAC,OAAO,UAAU,EAAE,IAAI,KAAK,EAC/B,OAAM,IAAI,MACR,qBAAqB,IAAI,qCAC1B;AAEH,QAAO;;;;;;;;;AAUT,SAAgB,iCACd,MACA,OAC8D;CAC9D,MAAM,WAAW,YAAY,MAAM,OAAO;AAC1C,KAAI,SAAS,aAAa;AACxB,MAAI,CAAC,MAAM,MAAM,MAAM,CACrB,OAAM,IAAI,MACR,6FACD;AAEH,SAAO;GAAE,MAAM,MAAM,KAAK,MAAM;GAAE;GAAU;;AAE9C,KAAI,SAAS,gBAAgB;AAC3B,MAAI,CAAC,MAAM,YAAY,MAAM,CAC3B,OAAM,IAAI,MACR,8HACD;AAEH,SAAO;GAAE,eAAe,MAAM,WAAW,MAAM;GAAE;GAAU;;AAE7D,QAAO,EAAE;;;;;AC1EX,MAAM,aAA6D;CACjE,SAAS;CACT,aAAa;CACb,gBAAgB;CACjB;AAED,SAAgB,oBAAoB,QAAsC;CACxE,MAAM,MACJ,OAAO,QAAQ,SACd,OAAO,QAAQ,MAAM,OAAO,IAAI,OAAO,QAAQ,OAAO;CACzD,MAAM,QAAQ,OAAO,iBAAiB,YAAY;AAClD,SAAQ,IACN,MAAM,IACJ,KAAK,WAAW,OAAO,MAAM,IAAI,IAAI,IAAI,MAAM,kBAAkB,OAAO,iBAAiB,KAC1F,CACF;AACD,KAAI,OAAO,SAAS,aAAa,OAAO,WACtC,KAAI,OAAO,WAAW,MACpB,SAAQ,IAAI,MAAM,IAAI,oCAAoC,CAAC;KAE3D,SAAQ,IACN,MAAM,OACJ,0FACD,CACF;;;;;;;;;;;;;;;;;;;;ACuBP,MAAa,gBAAgB,IAAI,QAAQ,SAAS,CAC/C,YACC,0EACD,CACA,OACC,oBACA,qFACD,CACA,OACC,iBACA,gEACD,CACA,OACC,oBACA,+EACD,CACA,OAAO,kBAAkB,+CAA+C,CACxE,OACC,iBACA,6DACD,CACA,OAAO,iBAAiB,gDAAgD,CACxE,OACC,uBACA,2DACD,CACA,OAAO,sBAAsB,gCAAgC,CAC7D,OAAO,aAAa,yDAAyD,CAC7E,OAAO,UAAU,gBAAgB,CACjC,QAAQ,SACP,UAAU,YAAY;CACpB,MAAM,cAAc,KAAK,YAAY,KAAA;CACrC,MAAM,WAAW,CAAC,CAAC,KAAK;CACxB,MAAM,eAAe,CAAC,CAAC,KAAK;AAC5B,KAAI;EAAC;EAAa;EAAU;EAAa,CAAC,OAAO,QAAQ,CAAC,SAAS,EACjE,OAAM,IAAI,MACR,wFACD;CAKH,IAAI;CACJ,IAAI;CACJ,IAAI;AACJ,KAAI,aAAa;AACf,SAAO;AACP,iBACE,OAAO,KAAK,YAAY,WAAW,KAAK,QAAQ,MAAM,GAAG,KAAA;YAClD,SACT,QAAO,iBAAiB,KAAK,KAAM;UAC1B,cAAc;AACvB,SAAO;AACP,wBAAsB,gBAAgB,KAAK,SAAU;;CAIvD,MAAM,WAAsC,EAC1C,MAAM,KAAK,YAAY,YACxB;AACD,KAAI,KAAK,MAAM,MAAM,CAAE,UAAS,OAAO,KAAK,KAAK,MAAM;CAEvD,MAAM,SAAS,kBAAkB;CACjC,MAAM,WAAW,KAAK,OAAO,sBAAsB,GAAG,mBAAmB;AAGzE,UAAS,MAAM,0CAA0C;CACzD,IAAI;AACJ,KAAI;AACF,YAAU,MAAM,WAAW,QAAQ,EAAE,MAAM,UAAU,CAAC;UAC/C,KAAK;AACZ,WAAS,KAAK,sBAAsB;AACpC,QAAM;;CAER,MAAM,OAAO,QAAQ;AACrB,UAAS,QAAQ,eAAe,KAAK,OAAO;CAG5C,IAAI;AACJ,KAAI,KACF,aAAY,MAAM,iBAAiB,QAAQ;EACzC,UAAU,KAAK;EACf;EACA,MAAM,gBAAgB,KAAK,MAAM,MAAM;EACvC,cAAc;EACd,WAAW,KAAK,SAAS;EACzB;EACA,GAAG,iCAAiC,MAAM,KAAK;EAChD,CAAC;AAGJ,KAAI,KAAK,KACP,QAAO,UAAU,YAAY;EAAE;EAAM;EAAW,GAAG,QAAQ;CAG7D,MAAM,QAAQ,UAAU,KAAK,MAAM,KAAK,YAAY;AACpD,SAAQ,KAAK;AACb,SAAQ,IACN,GAAG,MAAM,IAAI,MAAM,IAAI,KAAK,KAAK,CAAC,IAAI,WAAW,KAAK,MAAM,GAC7D;AACD,KAAI,KAAK,WAAY,SAAQ,IAAI,KAAK,MAAM,KAAK,KAAK,WAAW,GAAG;AACpE,KAAI,UAAW,qBAAoB,UAAU;AAC7C,SAAQ,KAAK;AACb,SAAQ,IAAI,MAAM,IAAI,gCAAgC,KAAK,OAAO,CAAC;EACnE,CACH;;;;;;;;;;;;;;;;;;ACpHH,SAAS,cACP,MACA,KACgD;CAGhD,MAAM,WADQ,IAAI,SAAS,IAAI,GAAG,IAAI,MAAM,IAAI,YAAY,IAAI,GAAG,EAAE,GAAG,KAClD,MAAM;AAC5B,KAAI,CAAC,QAAS,OAAM,IAAI,MAAM,+BAA+B,IAAI,IAAI;AACrE,KAAI,SAAS,gBAAgB;EAC3B,MAAM,KAAK,OAAO,QAAQ;AAC1B,MAAI,CAAC,OAAO,UAAU,GAAG,IAAI,MAAM,EACjC,OAAM,IAAI,MACR,wEAAwE,IAAI,KAC7E;AAEH,SAAO,EAAE,YAAY,IAAI;;AAE3B,QAAO,EAAE,cAAc,SAAS;;AAGlC,MAAa,gBAAgB,IAAI,QAAQ,SAAS,CAC/C,YACC,qBAAqB,kBAAkB,mCACxC,CACA,SAAS,UAAU,gDAAgD,CACnE,eAAe,iBAAiB,iBAAiB,oBAAoB,CACrE,OAAO,kBAAkB,6CAA6C,CACtE,OACC,iBACA,6DACD,CACA,OAAO,iBAAiB,gDAAgD,CACxE,OACC,uBACA,2DACD,CACA,OACC,oBACA,4FACD,CACA,OAAO,aAAa,yDAAyD,CAC7E,OAAO,UAAU,gBAAgB,CACjC,QAAQ,MAA0B,SACjC,UAAU,YAAY;CACpB,MAAM,OAAO,iBAAiB,KAAK,KAAM;CACzC,MAAM,WAAW,YAAY,KAAK;CAClC,MAAM,SAAS,kBAAkB;CACjC,MAAM,WAAW,KAAK,OAAO,sBAAsB,GAAG,mBAAmB;CAEzE,MAAM,WAAW,KAAK,WAClB,cAAc,MAAM,KAAK,SAAS,GAClC,KAAA;CAEJ,MAAM,SAAS,MAAM,iBAAiB,QAAQ;EAC5C,UAAU;EACV;EACA,MAAM,KAAK,MAAM,MAAM;EACvB,WAAW,KAAK,SAAS;EACzB;EACA,GAAG;EAEH,GAAI,WAAW,EAAE,GAAG,iCAAiC,MAAM,KAAK;EACjE,CAAC;AAEF,KAAI,KAAK,KAAM,QAAO,UAAU,OAAO;AAEvC,SAAQ,KAAK;AACb,SAAQ,IACN,GAAG,MAAM,KAAK,OAAO,KAAK,QAAQ,OAAO,KAAK,KAAK,CAAC,IAAI,MAAM,IAAI,OAAO,KAAK,KAAK,GACpF;AACD,qBAAoB,OAAO;EAC3B,CACH;;;;;;;;;;;;ACvFH,MAAa,uBAAuB,IAAI,QAAQ,iBAAiB,CAC9D,YACC,2EACD,CACA,SAAS,UAAU,6CAA6C,CAChE,SAAS,UAAU,gDAAgD,CACnE,OAAO,aAAa,yDAAyD,CAC7E,OAAO,UAAU,gBAAgB,CACjC,QAAQ,MAA0B,MAA0B,SAC3D,UAAU,YAAY;CACpB,MAAM,WAAW,YAAY,KAAK;CAClC,MAAM,SAAS,kBAAkB;CACjC,MAAM,WAAW,KAAK,OAAO,sBAAsB,GAAG,mBAAmB;CAEzE,MAAM,SAAS,MAAM,iBAAiB,QAAQ;EAC5C,UAAU;EACV,MAAM;EACN,MAAM,MAAM,MAAM,IAAI,KAAA;EACtB,WAAW,KAAK,SAAS;EACzB;EACD,CAAC;AAEF,KAAI,KAAK,KAAM,QAAO,UAAU,OAAO;AAEvC,SAAQ,KAAK;AACb,SAAQ,IACN,GAAG,MAAM,KAAK,OAAO,KAAK,QAAQ,OAAO,KAAK,KAAK,CAAC,IAAI,MAAM,IAAI,OAAO,KAAK,KAAK,GACpF;AACD,qBAAoB,OAAO;EAC3B,CACH;;;;;;;;;;;;;;;;;;;AChBH,MAAa,gBAAgB,IAAI,QAAQ,SAAS,CAC/C,YAAY,8CAA8C,CAC1D,SAAS,UAAU,gDAAgD,CACnE,OAAO,kBAAkB,wBAAwB,CACjD,OACC,oBACA,6DACD,CACA,OAAO,YAAY,uCAAuC,CAC1D,OAAO,UAAU,oBAAoB,CACrC,QAAQ,MAA0B,SACjC,UAAU,YAAY;AACpB,KAAI,KAAK,YAAY,KAAK,OACxB,OAAM,IAAI,MACR,iFACD;AAEH,KAAI,CAAC,KAAK,QAAQ,CAAC,KAAK,YAAY,CAAC,KAAK,OACxC,OAAM,IAAI,MACR,8DACD;CAGH,MAAM,WAAW,YAAY,KAAK;CAClC,MAAM,SAAS,kBAAkB;CACjC,MAAM,QAAkB,EAAE;CAK1B,IAAI;AACJ,KAAI,KAAK,MAAM,MAAM,EAAE;AACrB,eAAa,MAAM,WAAW,QAAQ,UAAU,EAC9C,MAAM,EAAE,MAAM,KAAK,KAAK,MAAM,EAAE,EACjC,CAAC;AACF,MAAI,CAAC,KAAK,MAAM;GACd,MAAM,IAAI,WAAW;AACrB,WAAQ,IACN,GAAG,UAAU,EAAE,MAAM,EAAE,YAAY,CAAC,IAAI,MAAM,IAAI,EAAE,KAAK,CAAC,IAAI,WAAW,EAAE,MAAM,GAClF;;;AAIL,KAAI,KAAK,YAAY,KAAK,QAAQ;EAChC,MAAM,WAAW,6BACd,MAAM,sBAAsB,QAAQ,SAAS,EAAE,mBACjD;AAED,MAAI,KAAK,UAAU;GACjB,MAAM,cAAc,gBAAgB,KAAK,SAAS;AAClD,OAAI,YAAY,SAAS,sBAAsB,YAC7C,OAAM,KACJ,WAAW,YAAY,iCAAiC,SAAS,GAAG,GACrE;QACI;AACL,QAAI,UAAU;AAGZ,WAAM,uBAAuB,QAAQ,UAAU,SAAS,GAAG;AAC3D,WAAM,KAAK,wBAAwB,SAAS,KAAK;;IAEnD,MAAM,KAAK,MAAM,uBAAuB,QAAQ,UAAU;KACxD,MAAM;KACN,mBAAmB;KACpB,CAAC;AACF,UAAM,KACJ,oBAAoB,YAAY,gBAAgB,GAAG,kBAAkB,GAAG,GACzE;;aAEM,KAAK,OACd,KAAI,UAAU;AACZ,SAAM,uBAAuB,QAAQ,UAAU,SAAS,GAAG;AAC3D,SAAM,KAAK,yCAAyC,SAAS,GAAG,GAAG;QAEnE,OAAM,KAAK,8CAA8C;;AAK/D,KAAI,KAAK,KACP,QAAO,UACL,aACI;EAAE,GAAG;EAAY,MAAM;EAAU;EAAO,GACxC;EAAE,MAAM;EAAU;EAAO,CAC9B;AAEH,MAAK,MAAM,QAAQ,MAAO,SAAQ,IAAI,MAAM,IAAI,KAAK,OAAO,CAAC;EAC7D,CACH;;;;;;;AC7GH,MAAa,gBAAgB,IAAI,QAAQ,SAAS,CAC/C,YAAY,wDAAwD,CACpE,SAAS,UAAU,gDAAgD,CACnE,OAAO,aAAa,2BAA2B,CAC/C,OAAO,UAAU,oBAAoB,CACrC,QAAQ,MAA0B,SACjC,UAAU,YAAY;CACpB,MAAM,WAAW,YAAY,KAAK;CAClC,MAAM,SAAS,kBAAkB;AAEjC,KAAI,CAAC,KAAK,KAAK;EAEb,MAAM,EAAE,SAAS,MAAM,SAAS,QAAQ,SAAS;EACjD,MAAM,iBAAiB,IAAI,KAAK,KAAK,KAAK,GAAG,KAAK,MAAW,CAC1D,aAAa,CACb,MAAM,GAAG,GAAG;AACf,UAAQ,KAAK;AACb,UAAQ,IACN,qBAAqB,MAAM,KAAK,KAAK,YAAY,CAAC,IAAI,MAAM,IAAI,KAAK,KAAK,CAAC,uBAAuB,MAAM,OAAO,eAAe,CAAC,GAChI;AACD,UAAQ,IACN,MAAM,IACJ,oEAAoE,KAAK,KAAK,aAC/E,CACF;EACD,MAAM,EAAE,cAAc,MAAM,QAC1B;GACE,MAAM;GACN,MAAM;GACN,SAAS;GACT,SAAS;GACV,EACD,EAAE,gBAAgB,QAAQ,KAAK,IAAI,EAAE,CACtC;AACD,MAAI,CAAC,WAAW;AACd,WAAQ,IAAI,MAAM,IAAI,WAAW,CAAC;AAClC,WAAQ,KAAK,EAAE;;;CAInB,MAAM,EAAE,SAAS,MAAM,WAAW,QAAQ,SAAS;AACnD,KAAI,KAAK,KAAM,QAAO,UAAU,EAAE,MAAM,CAAC;AACzC,SAAQ,IACN,MAAM,QACJ,gCAAgC,KAAK,uBAAuB,eAAe,KAAK,qBAAqB,GAAG,kBAAkB,GAC3H,CACF;AACD,SAAQ,IACN,MAAM,IACJ,oEAAoE,KAAK,KAAK,aAC/E,CACF;EACD,CACH;;;;AC1DH,MAAa,iBAAiB,IAAI,QAAQ,UAAU,CACjD,YAAY,sDAAsD,CAClE,SAAS,UAAU,gDAAgD,CACnE,OAAO,UAAU,oBAAoB,CACrC,QAAQ,MAA0B,SACjC,UAAU,YAAY;CACpB,MAAM,WAAW,YAAY,KAAK;CAElC,MAAM,EAAE,SAAS,MAAM,YADR,kBAAkB,EACU,SAAS;AACpD,KAAI,KAAK,KAAM,QAAO,UAAU,EAAE,MAAM,CAAC;AACzC,SAAQ,IACN,aAAa,KAAK,aAAa,MAAM,KAAK,KAAK,WAAW,GAAG,KAAK,YAAY,IAAI,WAAW,KAAK,MAAM,CAAC,GAC1G;EACD,CACH;;;AClBH,eAAsB,oBACpB,QACA,MACgC;CAChC,MAAM,KAAK,MAAM,cAAc,QAAQ,KAAK;AAC5C,QAAO,OAAO,KACZ,sBAAsB,GAAG,iBAC1B;;;;;;;;;;;;;;;;ACCH,IAAa,WAAb,cAA8B,MAAM;CAClC;CAEA,YAAY,SAAiB,WAAW,GAAG;AACzC,QAAM,QAAQ;AACd,OAAK,OAAO;AACZ,OAAK,WAAW;;;;;;;;;;;;;;;;;;AAmBpB,MAAM,uBAAuB;AAC7B,MAAM,wBACJ;;;;;;AAOF,SAAS,gBAAgB,KAA+B;AAEtD,KADc,KAA2C,SAC5C,SAAU,QAAO;AAC9B,QAAO,IAAI,SACT,6NACA,IACD;;;;;;;;;;AAWH,SAAS,uBAAuB,WAI9B;CACA,MAAM,IAAI,IAAI,IAAI,UAAU;CAC5B,MAAM,WAAW,mBAAmB,EAAE,SAAS;CAC/C,MAAM,WAAW,mBAAmB,EAAE,SAAS;AAC/C,GAAE,WAAW;AACb,GAAE,WAAW;AACb,QAAO;EAAE,UAAU,EAAE,UAAU;EAAE;EAAU;EAAU;;;;;;;;;;;;;;;;;AAkBvD,IAAI,cAA6B;AACjC,SAAS,sBAA8B;AACrC,KAAI,eAAe,WAAW,YAAY,CAAE,QAAO;CACnD,MAAM,MAAM,KAAK,KAAK,GAAG,QAAQ,EAAE,oBAAoB,QAAQ,MAAM;AACrE,KAAI,CAAC,WAAW,IAAI,CAAE,WAAU,KAAK;EAAE,WAAW;EAAM,MAAM;EAAO,CAAC;CACtE,MAAM,IAAI,KAAK,KAAK,KAAK,aAAa;AACtC,eACE,GACA,uHACA,EAAE,MAAM,KAAO,CAChB;AACD,WAAU,GAAG,IAAM;AACnB,eAAc;AACd,QAAO;;;;;;;;;;;AAYT,SAAS,aAAa,WAGpB;CACA,MAAM,EAAE,UAAU,UAAU,aAAa,uBAAuB,UAAU;AAC1E,QAAO;EACL;EACA,KAAK;GACH,GAAG,QAAQ;GACX,aAAa,qBAAqB;GAClC,qBAAqB;GACrB,eAAe;GACf,eAAe;GAChB;EACF;;;AAIH,eAAsB,qBAAoC;AACxD,KAAI;AACF,QAAM,MAAM,OAAO,CAAC,YAAY,CAAC;SAC3B;AACN,QAAM,IAAI,SACR,yHACD;;;;;;;;;;AAWL,eAAsB,SACpB,WACA,WACe;CACf,MAAM,EAAE,UAAU,QAAQ,aAAa,UAAU;AACjD,KAAI;AACF,QAAM,MACJ,OACA;GAAC;GAAM;GAAsB;GAAS;GAAW;GAAU;GAAU,EACrE;GAAE;GAAK,OAAO;IAAC;IAAU;IAAW;IAAU;GAAE,CACjD;UACM,KAAK;EACZ,MAAM,UAAU,gBAAgB,IAAI;AACpC,MAAI,QAAS,OAAM;EACnB,MAAM,IAAI;AACV,QAAM,IAAI,SACR,mBAAmB,EAAE,YAAY,OAAO,UAAU,EAAE,SAAS,KAAK,MAClE,EAAE,YAAY,EACf;;;;;;AAOL,eAAsB,QACpB,KACA,WACA,SAAS,QACM;CACf,MAAM,EAAE,UAAU,QAAQ,aAAa,UAAU;AACjD,KAAI;AACF,QAAM,MACJ,OACA;GACE;GACA;GACA;GACA;GACA;GACA;GACA,QAAQ;GACT,EACD;GAAE;GAAK;GAAK,OAAO;IAAC;IAAU;IAAW;IAAU;GAAE,CACtD;UACM,KAAK;EACZ,MAAM,UAAU,gBAAgB,IAAI;AACpC,MAAI,QAAS,OAAM;EACnB,MAAM,IAAI;AACV,QAAM,IAAI,SACR,kBAAkB,EAAE,YAAY,OAAO,UAAU,EAAE,SAAS,KAAK,MACjE,EAAE,YAAY,EACf;;;;AAKL,eAAsB,QACpB,KACA,WACA,SAAS,QACM;CACf,MAAM,EAAE,UAAU,QAAQ,aAAa,UAAU;AACjD,KAAI;AACF,QAAM,MACJ,OACA;GAAC;GAAM;GAAsB;GAAQ;GAAa;GAAU;GAAO,EACnE;GAAE;GAAK;GAAK,OAAO;IAAC;IAAU;IAAW;IAAU;GAAE,CACtD;UACM,KAAK;EACZ,MAAM,UAAU,gBAAgB,IAAI;AACpC,MAAI,QAAS,OAAM;EACnB,MAAM,IAAI;AACV,QAAM,IAAI,SACR,kBAAkB,EAAE,YAAY,OAAO,UAAU,EAAE,SAAS,KAAK,MACjE,EAAE,YAAY,EACf;;;;;;;;AAiCL,eAAsB,sBAAsB,KAA+B;AACzE,KAAI;EACF,MAAM,EAAE,WAAW,MAAM,MAAM,OAAO,CAAC,UAAU,cAAc,EAAE,EAAE,KAAK,CAAC;AACzE,SAAO,OAAO,MAAM,CAAC,SAAS;SACxB;AACN,SAAO;;;;;;;;;;AAWX,eAAsB,SACpB,KACA,WACA,SAAS,QACM;CACf,MAAM,EAAE,UAAU,QAAQ,aAAa,UAAU;AACjD,KAAI;AACF,QAAM,MACJ,OACA;GAAC;GAAM;GAAsB;GAAS;GAAW;GAAU;GAAO,EAClE;GAAE;GAAK;GAAK,CACb;UACM,KAAK;EACZ,MAAM,UAAU,gBAAgB,IAAI;AACpC,MAAI,QAAS,OAAM;EACnB,MAAM,IAAI;AACV,QAAM,IAAI,SACR,mBAAmB,EAAE,YAAY,OAAO,UAAU,EAAE,SAAS,KAAK,MAClE,EAAE,YAAY,EACf;;;;;;;;AASL,eAAsB,0BACpB,KAC4C;AAC5C,KAAI;EACF,MAAM,EAAE,WAAW,MAAM,MACvB,OACA;GAAC;GAAY;GAAgB;GAAW;GAAoB,EAC5D,EAAE,KAAK,CACR;EACD,MAAM,CAAC,GAAG,KAAK,OAAO,MAAM,CAAC,MAAM,MAAM;EACzC,MAAM,QAAQ,OAAO,SAAS,KAAK,KAAK,GAAG;EAC3C,MAAM,SAAS,OAAO,SAAS,KAAK,KAAK,GAAG;AAC5C,SAAO;GACL,OAAO,OAAO,SAAS,MAAM,GAAG,QAAQ;GACxC,QAAQ,OAAO,SAAS,OAAO,GAAG,SAAS;GAC5C;SACK;AAEN,SAAO;GAAE,OAAO;GAAG,QAAQ;GAAG;;;;;AAMlC,eAAsB,oBAAoB,KAA4B;AACpE,KAAI;AACF,QAAM,MAAM,OAAO;GAAC;GAAS;GAAa;GAAa,EAAE,EAAE,KAAK,CAAC;UAC1D,KAAK;EACZ,MAAM,UAAU,gBAAgB,IAAI;AACpC,MAAI,QAAS,OAAM;EACnB,MAAM,IAAI;AACV,QAAM,IAAI,SACR,gCAAgC,EAAE,YAAY,OAAO,UAAU,EAAE,SAAS,KAAK,MAC/E,EAAE,YAAY,EACf;;;;;;;;AASL,eAAsB,SAAS,KAAa,SAAgC;AAC1E,KAAI;AACF,QAAM,MACJ,OACA;GAAC;GAAS;GAAQ;GAAuB;GAAa;GAAQ,EAC9D,EAAE,KAAK,CACR;UACM,KAAK;EACZ,MAAM,UAAU,gBAAgB,IAAI;AACpC,MAAI,QAAS,OAAM;AAEnB,QAAM,IAAI,SAAS,yBADT,IACoC,YAAY,EAAE;;;;;;;;AAShE,eAAsB,YAAY,KAA4B;AAC5D,KAAI;AACF,QAAM,MAAM,OAAO,CAAC,SAAS,MAAM,EAAE,EAAE,KAAK,CAAC;UACtC,KAAK;EACZ,MAAM,UAAU,gBAAgB,IAAI;AACpC,MAAI,QAAS,OAAM;AAEnB,QAAM,IAAI,SACR,+IAFQ,IAGN,YAAY,EACf;;;;;;;;;;;;;;;;;;;;AAqBL,eAAsB,cACpB,KACA,SACe;AAEf,KAAI;AACF,QAAM,MAAM,OAAO,CAAC,OAAO,KAAK,EAAE,EAAE,KAAK,CAAC;UACnC,KAAK;EACZ,MAAM,UAAU,gBAAgB,IAAI;AACpC,MAAI,QAAS,OAAM;AAEnB,QAAM,IAAI,SAAS,kBADT,IAC6B,YAAY,EAAE;;CAMvD,MAAM,eAAe;EACnB;EACA,aAAa;EACb;EACA,cAAc;EACf;CACD,MAAM,cAAiC;EACrC,iBAAiB;EACjB,kBAAkB;EAClB,oBAAoB;EACpB,qBAAqB;EACtB;AACD,KAAI;AACF,QAAM,MAAM,OAAO;GAAC,GAAG;GAAc;GAAU;GAAM;GAAQ,EAAE;GAC7D;GACA,KAAK;IAAE,GAAG,QAAQ;IAAK,GAAG;IAAa;GACvC,OAAO;IAAC;IAAU;IAAW;IAAU;GACxC,CAAC;UACK,KAAK;EACZ,MAAM,UAAU,gBAAgB,IAAI;AACpC,MAAI,QAAS,OAAM;EACnB,MAAM,IAAI;AAGV,MAAI,OAAO,EAAE,WAAW,YAAY,qBAAqB,KAAK,EAAE,OAAO,CACrE;AAEF,QAAM,IAAI,SACR,oBAAoB,EAAE,YAAY,OAAO,UAAU,EAAE,SAAS,KAAK,MACnE,EAAE,YAAY,EACf;;;;;;;;;AC9aL,MAAa,eAAe,IAAI,QAAQ,QAAQ,CAC7C,YAAY,8DAA8D,CAC1E,SAAS,UAAU,gDAAgD,CACnE,OAAO,eAAe,kDAAkD,CACxE,QAAQ,MAA0B,SACjC,UAAU,YAAY;AACpB,OAAM,oBAAoB;CAC1B,MAAM,eAAe,YAAY,KAAK;CACtC,MAAM,SAAS,kBAAkB;CAEjC,MAAM,UAAU,IAAI,kBAAkB,CAAC,OAAO;CAC9C,MAAM,EAAE,SAAS,MAAM,SAAS,QAAQ,aAAa;AACrD,SAAQ,OAAO;CACf,MAAM,EAAE,gBAAgB,SAAS,MAAM,oBACrC,QACA,KAAK,KACN;CACD,MAAM,YAAY,QAAQ,KAAK,OAAO,KAAK,YAAY;AACvD,KAAI,WAAW,UAAU,EAAE;AACzB,UAAQ,KACN,aAAa,UAAU,iDACxB;AACD,UAAQ,KAAK,EAAE;;AAEjB,SAAQ,OAAO,gBAAgB,KAAK,YAAY;AAChD,KAAI;AAEF,QAAM,SAAS,KAAK,YAAY,UAAU;UACnC,KAAK;AACZ,UAAQ,KAAK,mBAAmB;AAChC,QAAM;;AAGR,eAAc,WAAW,KAAK;AAC9B,SAAQ,QAAQ,eAAe,MAAM,KAAK,UAAU,GAAG;AACvD,SAAQ,IAAI,MAAM,IAAI,QAAQ,KAAK,KAAK,KAAK,YAAY,GAAG,CAAC;AAC7D,KAAI,KAAK,WACP,SAAQ,IAAI,MAAM,IAAI,KAAK,MAAM,KAAK,KAAK,WAAW,GAAG,CAAC;EAC5D,CACH;;;ACnDH,eAAsB,gBACpB,QACA,MACA,OACkC;CAClC,MAAM,KAAK,MAAM,cAAc,QAAQ,KAAK;AAC5C,QAAO,OAAO,IACZ,sBAAsB,GAAG,eACzB,SAAS,OAAO,EAAE,OAAO,GAAG,KAAA,EAC7B;;AAGH,eAAsB,eACpB,QACA,MACA,cACiC;CACjC,MAAM,KAAK,MAAM,cAAc,QAAQ,KAAK;AAC5C,QAAO,OAAO,IACZ,sBAAsB,GAAG,eAAe,eACzC;;;;ACFH,eAAsB,iBAAiB,QAAqC;CAC1E,MAAM,MAAM,MAAM,OAAO,IAAgB,UAAU;CACnD,MAAM,WAAW,CAAC,IAAI,YAAY,IAAI,UAAU,CAC7C,OAAO,QAAQ,CACf,KAAK,IAAI,CACT,MAAM;CACT,MAAM,WAAW,IAAI,cAAc,SAAS,SAAS,IAAI,WAAW;AACpE,QAAO;EACL,IAAI,IAAI;EACR,WAAW,IAAI,aAAa,OAAO,IAAI,GAAG;EAC1C,WAAW;EACX,YAAY,IAAI,cAAc;EAC9B,WAAW,IAAI,aAAa;EAC5B,OAAO,IAAI,SAAS;EACrB;;;;;;;;ACJH,MAAMC,YAAU;AAChB,MAAM,oBAAoB,IAAI;AAC9B,MAAM,mBAAmB,IAAI;AAE7B,SAASC,QAAM,IAA2B;AACxC,QAAO,IAAI,SAAS,MAAM,WAAW,GAAG,GAAG,CAAC;;AAG9C,MAAa,cAAc,IAAI,QAAQ,OAAO,CAC3C,YACC,6FACD,CACA,SAAS,UAAU,gDAAgD,CACnE,OAAO,WAAW,yCAAyC,CAC3D,OAAO,mBAAmB,iCAAiC,CAC3D,OACC,eACA,yDACD,CACA,OACC,2BACA,oEACD,CACA,QAAQ,MAA0B,SACjC,UAAU,YAAY;AACpB,OAAM,oBAAoB;CAC1B,MAAM,WAAW,YAAY,KAAK;CAClC,MAAM,SAAS,kBAAkB;CACjC,MAAM,SAAS,KAAK,UAAU;CAC9B,MAAM,MAAM,QAAQ,KAAK;CAYzB,MAAM,eAAe,IAAI,+BAA+B,CAAC,OAAO;AAChE,KAAI;EAIF,MAAM,EAAE,gBAAgB,cAAc,MAAM,oBAC1C,QACA,SACD;AACD,QAAM,SAAS,KAAK,UAAU,YAAY,OAAO;EACjD,MAAM,EAAE,OAAO,WAAW,MAAM,0BAA0B,IAAI;AAC9D,MAAI,SAAS,KAAK,UAAU,GAAG;GAE7B,MAAM,WAAW,MAAM,sBAAsB,IAAI;AACjD,gBAAa,OAAO,WAAW,OAAO,SAAS,WAAW,IAAI,KAAK,IAAI;AACvE,OAAI,SACF,OAAM,SAAS,KAAK,4BAA4B;AAElD,OAAI;AACF,UAAM,oBAAoB,IAAI;aACtB;AACR,QAAI,SACF,OAAM,YAAY,IAAI;;AAG1B,gBAAa,QACX,UAAU,OAAO,SAAS,WAAW,IAAI,KAAK,IAAI,mBACnD;aACQ,SAAS,KAAK,QAAQ,GAAG;AAClC,gBAAa,KACX,qDAAqD,MAAM,UAAU,OAAO,WAC7E;AACD,WAAQ,MACN,MAAM,OACJ,wFACD,CACF;AACD,WAAQ,KAAK,EAAE;QAEf,cAAa,QAAQ,8BAA8B;UAE9C,KAAK;AAIZ,MACE,eAAe,SACf,IAAI,QAAQ,SAAS,wBAAwB,CAE7C,OAAM;AAER,eAAa,KAAK,iCAAiC;AACnD,QAAM;;AAQR,KADuB,KAAK,WAAW;MAEvB,MAAM,sBAAsB,IAAI,EACnC;GACT,MAAM,cACJ,KAAK,WACL,gDAA+B,IAAI,MAAM,EAAC,aAAa,CAAC,MAAM,GAAG,GAAG,CAAC,QAAQ,KAAK,IAAI;GASxF,IAAI,aAAa;GACjB,IAAI,WAAW;AACf,OAAI;IACF,MAAM,KAAK,MAAM,QAAQ,KAAK,CAC5B,iBAAiB,OAAO,EACxB,IAAI,SAAgB,GAAG,WACrB,iBACQ,uBAAO,IAAI,MAAM,qBAAqB,CAAC,EAC7C,IACD,CACF,CACF,CAAC;IACF,MAAM,OACH,GAAG,aAAa,GAAG,UAAU,MAAM,IACpC,GAAG,UACF,OAAO,SAAS,GAAG,GAAG,GAAG,QAAQ,GAAG,OAAO;IAC9C,MAAM,KAAK,GAAG,aAAa,GAAG,UAAU,MAAM;AAC9C,QAAI,KAAM,cAAa,GAAG,KAAK;AAC/B,QAAI,GAAI,YAAW,KAAK,GAAG;YACpB,KAAK;IACZ,MAAM,SAAS,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;AAC/D,YAAQ,KACN,MAAM,OACJ,8DAA8D,OAAO,GACtE,CACF;;GAEH,MAAM,UAAU,GAAG,aAAa,cAAc;GAC9C,MAAM,gBAAgB,IACpB,uBAAuB,QAAQ,SAAS,KAAK,QAAQ,MAAM,GAAG,GAAG,GAAG,MAAM,QAAQ,IACnF,CAAC,OAAO;AACT,OAAI;AACF,UAAM,cAAc,KAAK,QAAQ;AACjC,kBAAc,QAAQ,YAAY;YAC3B,KAAK;AACZ,kBAAc,KAAK,gBAAgB;AACnC,UAAM;;;;CAKZ,MAAM,UAAU,IAAI,0BAA0B,CAAC,OAAO;CACtD,MAAM,EAAE,gBAAgB,SAAS,MAAM,oBACrC,QACA,SACD;AACD,SAAQ,OAAO,WAAW,OAAO;AACjC,KAAI;AACF,QAAM,QAAQ,KAAK,KAAK,YAAY,OAAO;UACpC,KAAK;AACZ,UAAQ,KAAK,kBAAkB;AAC/B,QAAM;;AAER,SAAQ,QAAQ,gBAAgB;AAEhC,KAAI,CAAC,KAAK,MAAO;CAMjB,IAAI,YAA2B;AAC/B,KAAI;EACF,MAAM,EAAE,UAAU,MAAM,OAAO;AAE/B,eADU,MAAM,MAAM,OAAO,CAAC,aAAa,OAAO,EAAE,EAAE,KAAK,CAAC,EAC9C,OAAO,MAAM;SACrB;CASR,MAAM,cAAc,KAAK,KAAK;CAC9B,IAAI,eAA8B;CAClC,IAAI,UAAU;AACd,QAAO,KAAK,KAAK,GAAG,cAAc,mBAAmB;AACnD;EACA,MAAM,EAAE,gBAAgB,MAAM,gBAAgB,QAAQ,UAAU,EAAE;EAClE,MAAM,QAAQ,YACV,YAAY,MAAM,MAAM,EAAE,eAAe,UAAU,GACnD,YAAY;AAChB,MAAI,OAAO;AACT,kBAAe,MAAM;AAGrB,WAAQ,IAAI,sBAAsB,MAAM,GAAG,IAAI,MAAM,MAAM,GAAG;AAC9D;;AAEF,MAAI,YAAY,EACd,SAAQ,IACN,kCAAkC,WAAW,MAAM,GAAG,EAAE,IAAI,YAAY,GACzE;WACQ,UAAU,MAAM,EAEzB,SAAQ,IAAI,qBAAqB,UAAU,EAAE,YAAY;AAE3D,QAAMA,QAAMD,UAAQ;;AAEtB,KAAI,CAAC,cAAc;AACjB,UAAQ,MACN,0BAA0B,WAAW,MAAM,GAAG,EAAE,IAAI,WAAW,mBAAmB,oBAAoB,IAAK,IAC5G;AACD,UAAQ,MACN,wEACD;AACD,UAAQ,KAAK,EAAE;;CAIjB,MAAM,aAAa,KAAK,KAAK;CAC7B,IAAI,YAA2B;AAC/B,QAAO,KAAK,KAAK,GAAG,aAAa,kBAAkB;EACjD,MAAM,EAAE,eAAe,MAAM,eAC3B,QACA,UACA,aACD;AACD,MAAI,WAAW,UAAU,WAAW;AAClC,WAAQ,IAAI,KAAK,WAAW,MAAM,GAAG;AACrC,eAAY,WAAW;;AAEzB,MAAI,WAAW,UAAU,SAAS;AAChC,WAAQ,IAAI,YAAY,MAAM,KAAK,WAAW,OAAO,WAAW,GAAG;AACnE;;AAEF,MAAI,WAAW,UAAU,WAAW,WAAW,UAAU,YAAY;AACnE,WAAQ,MAAM,gBAAgB,WAAW,QAAQ;AACjD,WAAQ,KAAK,EAAE;;AAEjB,QAAMC,QAAMD,UAAQ;;AAEtB,SAAQ,MACN,yBAAyB,mBAAmB,IAAK,iBAAiB,UAAU,GAC7E;AACD,SAAQ,MAAM,0DAA0D;AACxE,SAAQ,KAAK,EAAE;EACf,CACH;;;;ACtRH,MAAa,cAAc,IAAI,QAAQ,OAAO,CAC3C,YAAY,wCAAwC,CACpD,SAAS,UAAU,gDAAgD,CACnE,OAAO,mBAAmB,iCAAiC,CAC3D,QAAQ,MAA0B,SACjC,UAAU,YAAY;AACpB,OAAM,oBAAoB;CAC1B,MAAM,WAAW,YAAY,KAAK;CAClC,MAAM,SAAS,kBAAkB;CACjC,MAAM,SAAS,KAAK,UAAU;CAC9B,MAAM,UAAU,IAAI,0BAA0B,CAAC,OAAO;CACtD,MAAM,EAAE,gBAAgB,SAAS,MAAM,oBACrC,QACA,SACD;AACD,SAAQ,OAAO,WAAW,OAAO;AACjC,KAAI;AACF,QAAM,QAAQ,QAAQ,KAAK,EAAE,KAAK,YAAY,OAAO;UAC9C,KAAK;AACZ,UAAQ,KAAK,kBAAkB;AAC/B,QAAM;;AAER,SAAQ,QAAQ,gBAAgB;EAChC,CACH;;;;AC5BH,MAAa,cAAc,IAAI,QAAQ,OAAO,CAC3C,YAAY,6CAA6C,CACzD,SAAS,UAAU,gDAAgD,CACnE,QAAQ,SACP,UAAU,YAAY;CACpB,MAAM,WAAW,YAAY,KAAK;CAElC,MAAM,EAAE,SAAS,MAAM,SADR,kBAAkB,EACO,SAAS;AACjD,KAAI,CAAC,KAAK,YAAY;AACpB,UAAQ,MACN,MAAM,OACJ,QAAQ,KAAK,KAAK,iCAAiC,KAAK,MAAM,IAC/D,CACF;AACD,UAAQ,KAAK,EAAE;;AAEjB,SAAQ,IAAI,MAAM,KAAK,KAAK,WAAW,CAAC;AACxC,OAAM,KAAK,KAAK,WAAW;EAC3B,CACH;;;;ACdH,MAAM,cAAqD;CACzD,OAAO,MAAM;CACb,UAAU,MAAM;CAChB,QAAQ,MAAM;CACd,OAAO,MAAM;CACb,UAAU,MAAM;CACjB;AAED,MAAa,qBAAqB,IAAI,QAAQ,cAAc,CACzD,YAAY,qCAAqC,CACjD,SAAS,UAAU,gDAAgD,CACnE,OAAO,eAAe,iCAAiC,CACvD,OAAO,UAAU,oBAAoB,CACrC,QAAQ,MAA0B,SACjC,UAAU,YAAY;CACpB,MAAM,WAAW,YAAY,KAAK;CAGlC,MAAM,WAAW,MAAM,gBAFR,kBAAkB,EAEc,UADjC,KAAK,QAAQ,SAAS,KAAK,OAAO,GAAG,GAAG,GACS;AAC/D,KAAI,KAAK,KAAM,QAAO,UAAU,SAAS;CACzC,MAAM,cAAc,SAAS,eAAe,EAAE;AAC9C,KAAI,YAAY,WAAW,GAAG;AAC5B,UAAQ,IAAI,MAAM,IAAI,sBAAsB,CAAC;AAC7C;;AAgBF,YAAW,CAdI;EACb,MAAM,KAAK,KAAK;EAChB,MAAM,KAAK,QAAQ;EACnB,MAAM,KAAK,SAAS;EACpB,MAAM,KAAK,UAAU;EACrB,MAAM,KAAK,MAAM;EAClB,EAQmB,GAPP,YAAY,KAAK,MAAM;EAClC,EAAE;GACD,YAAY,EAAE,UAAU,MAAM,OAAO,EAAE,MAAM;EAC9C,EAAE,aAAa,EAAE,WAAW,MAAM,GAAG,EAAE,GAAG,MAAM,IAAI,IAAI;EACxD,EAAE,iBAAiB,EAAE,eAAe,MAAM,GAAG,GAAG,GAAG,MAAM,IAAI,IAAI;EACjE,aAAa,EAAE,WAAW;EAC3B,CAAC,CAC0B,CAAC;EAC7B,CACH;;;AC3CH,eAAsB,UACpB,QACA,MACA,SAA0B,EAAE,EACL;CACvB,MAAM,KAAK,MAAM,cAAc,QAAQ,KAAK;AAC5C,QAAO,OAAO,IACZ,sBAAsB,GAAG,QACzB,OACD;;;;;ACJH,MAAM,UAAU;AAEhB,SAAS,MAAM,IAA2B;AACxC,QAAO,IAAI,SAAS,MAAM,WAAW,GAAG,GAAG,CAAC;;AAG9C,SAAS,WAAW,OAAiD;AACnE,QAAO,UAAU,WAAW,MAAM,MAAM,MAAM;;AAGhD,SAAS,UAAU,OAAuB;CACxC,MAAM,KAAK,MAAM,UAAU,MAAM,IAAI,GAAG;AACxC,SAAQ,OAAO,MACb,GAAG,MAAM,IAAI,GAAG,CAAC,GAAG,WAAW,MAAM,MAAM,CAAC,MAAM,MAAM,OAAO,EAAE,CAAC,CAAC,GAAG,MAAM,QAAQ,IACrF;;AAGH,MAAa,cAAc,IAAI,QAAQ,OAAO,CAC3C,YAAY,+CAA+C,CAC3D,SAAS,UAAU,gDAAgD,CACnE,OAAO,UAAU,2BAA2B,CAC5C,OAAO,eAAe,mCAAmC,CACzD,OAAO,qBAAqB,qCAAqC,CACjE,OAAO,UAAU,qDAAqD,CACtE,QAAQ,MAA0B,SACjC,UAAU,YAAY;CACpB,MAAM,WAAW,YAAY,KAAK;CAClC,MAAM,SAAS,kBAAkB;CACjC,MAAM,QAAQ,KAAK,QAAQ,SAAS,KAAK,OAAO,GAAG,GAAG;CAEtD,MAAM,UAAU,MAAM,UAAU,QAAQ,UAAU;EAChD,GAAI,KAAK,aAAa,EAAE,eAAe,KAAK,YAAY,GAAG,EAAE;EAC7D;EACD,CAAC;AACF,KAAI,KAAK,QAAQ,CAAC,KAAK,KAAM,QAAO,UAAU,QAAQ;AACtD,MAAK,MAAM,SAAS,QAAQ,KAAM,WAAU,MAAM;AAElD,KAAI,CAAC,KAAK,KAAM;CAEhB,IAAI,QACF,QAAQ,KAAK,SAAS,IAClB,QAAQ,KAAK,QAAQ,KAAK,SAAS,GAAI,6BACvC,IAAI,MAAM,EAAC,aAAa;AAG9B,QAAO,MAAM;AACX,QAAM,MAAM,QAAQ;EACpB,MAAM,QAAQ,MAAM,UAAU,QAAQ,UAAU;GAC9C,GAAI,KAAK,aAAa,EAAE,eAAe,KAAK,YAAY,GAAG,EAAE;GAC7D;GACA;GACD,CAAC;AACF,OAAK,MAAM,SAAS,MAAM,MAAM;AAC9B,aAAU,MAAM;AAChB,WAAQ,MAAM;;;EAGlB,CACH;;;;AC7DH,SAAS,UAAU,OAAuB;AACxC,KAAI,MAAM,UAAU,EAAG,QAAO,IAAI,OAAO,MAAM,OAAO;AACtD,QAAO,GAAG,MAAM,MAAM,GAAG,EAAE,GAAG,IAAI,OAAO,EAAE,GAAG,MAAM,MAAM,GAAG;;AAG/D,MAAa,iBAAiB,IAAI,QAAQ,OAAO,CAC9C,YAAY,yBAAyB,CACrC,SAAS,UAAU,gDAAgD,CACnE,OAAO,iBAAiB,4CAA4C,CACpE,OAAO,UAAU,oBAAoB,CACrC,QAAQ,MAA0B,SACjC,UAAU,YAAY;CACpB,MAAM,WAAW,YAAY,KAAK;CAElC,MAAM,WAAW,MAAM,YADR,kBAAkB,EACU,SAAS;AACpD,KAAI,KAAK,KAAM,QAAO,UAAU,SAAS;CACzC,MAAM,OAAO,SAAS,YAAY,EAAE;AACpC,KAAI,KAAK,WAAW,GAAG;AACrB,UAAQ,IAAI,MAAM,IAAI,eAAe,CAAC;AACtC;;AAcF,YAAW,CAZI;EACb,MAAM,KAAK,MAAM;EACjB,MAAM,KAAK,QAAQ;EACnB,MAAM,KAAK,UAAU;EACrB,MAAM,KAAK,UAAU;EACtB,EAOmB,GANP,KAAK,KAAK,MAAM;EAC3B,EAAE;EACF,KAAK,aAAa,EAAE,QAAQ,UAAU,EAAE,MAAM;EAC9C,EAAE,QAAQ,KAAK,IAAI;EACnB,EAAE,eAAe,SAAS,MAAM,QAAQ,OAAO,GAAG,MAAM,IAAI,OAAO;EACpE,CAAC,CAC0B,CAAC;EAC7B,CACH;;;;;;;AClCH,SAAS,UAAU,MAA8C;CAC/D,MAAM,KAAK,KAAK,QAAQ,IAAI;AAC5B,KAAI,MAAM,EACR,OAAM,IAAI,MACR,iBAAiB,KAAK,uCACvB;AAEH,QAAO;EAAE,KAAK,KAAK,MAAM,GAAG,GAAG;EAAE,OAAO,KAAK,MAAM,KAAK,EAAE;EAAE;;AAG9D,MAAa,gBAAgB,IAAI,QAAQ,MAAM,CAC5C,YAAY,mDAAmD,CAC/D,SAAS,kBAAkB,6CAA6C,CACxE,SAAS,cAAc,6BAA6B,CACpD,OAAO,OAAO,YAAoB,UAAoB;AACrD,OAAM,UAAU,YAAY;EAI1B,IAAI;EACJ,IAAI;AACJ,MAAI,WAAW,SAAS,IAAI,EAAE;AAC5B,UAAO,aAAa;AACpB,cAAW,CAAC,YAAY,GAAG,MAAM;SAC5B;AACL,UAAO,YAAY,WAAW;AAC9B,cAAW;;AAEb,MAAI,SAAS,WAAW,GAAG;AACzB,WAAQ,MAAM,MAAM,IAAI,oCAAoC,CAAC;AAC7D,WAAQ,KAAK,EAAE;;EAEjB,MAAM,SAAS,kBAAkB;EAEjC,MAAM,WAAW,MAAM,YAAY,QAAQ,KAAK;EAChD,MAAM,eAAe,IAAI,KAAK,SAAS,YAAY,EAAE,EAAE,KAAK,MAAM,EAAE,IAAI,CAAC;AACzE,OAAK,MAAM,OAAO,UAAU;GAC1B,MAAM,EAAE,KAAK,UAAU,UAAU,IAAI;AACrC,OAAI,aAAa,IAAI,IAAI,EAAE;AACzB,UAAM,aAAa,QAAQ,MAAM,KAAK,MAAM;AAC5C,YAAQ,IAAI,GAAG,MAAM,MAAM,UAAU,CAAC,IAAI,MAAM;UAC3C;AACL,UAAM,aAAa,QAAQ,MAAM,KAAK,MAAM;AAC5C,YAAQ,IAAI,GAAG,MAAM,KAAK,UAAU,CAAC,IAAI,MAAM;;;GAGnD;EACF;;;;ACtDJ,MAAa,kBAAkB,IAAI,QAAQ,QAAQ,CAChD,YAAY,8BAA8B,CAC1C,SACC,iBACA,6DACD,CACA,SAAS,aAAa,kBAAkB,CACxC,OAAO,OAAO,WAAmB,SAAmB;AACnD,OAAM,UAAU,YAAY;EAI1B,IAAI;EACJ,IAAI;AACJ,MAAI,qBAAqB,KAAK,UAAU,EAAE;AACxC,UAAO,aAAa;AACpB,cAAW,CAAC,WAAW,GAAG,KAAK;SAC1B;AACL,UAAO,YAAY,UAAU;AAC7B,cAAW;;AAEb,MAAI,SAAS,WAAW,GAAG;AACzB,WAAQ,MAAM,MAAM,IAAI,yBAAyB,CAAC;AAClD,WAAQ,KAAK,EAAE;;EAEjB,MAAM,SAAS,kBAAkB;AACjC,OAAK,MAAM,OAAO,UAAU;AAC1B,SAAM,aAAa,QAAQ,MAAM,IAAI;AACrC,WAAQ,IAAI,GAAG,MAAM,IAAI,UAAU,CAAC,IAAI,MAAM;;GAEhD;EACF;;;;;;;;ACrBJ,SAAS,YAAY,GAAmB;AAEtC,KAAI,UAAU,KAAK,EAAE,CACnB,QAAO,IAAI,EAAE,QAAQ,cAAc,OAAO,CAAC;AAE7C,QAAO;;AAGT,MAAa,iBAAiB,IAAI,QAAQ,OAAO,CAC9C,YAAY,uDAAuD,CACnE,SAAS,UAAU,gDAAgD,CACnE,OAAO,gBAAgB,oCAAoC,CAC3D,QAAQ,MAA0B,SACjC,UAAU,YAAY;CACpB,MAAM,WAAW,YAAY,KAAK;CAElC,MAAM,EAAE,aAAa,MAAM,YADZ,kBAAkB,EACc,SAAS;CACxD,MAAM,OAAO,SAAS,QAAQ,MAAM,EAAE,eAAe,OAAO;CAC5D,MAAM,UAAU,SAAS,QAAQ,MAAM,EAAE,eAAe,OAAO;CAC/D,MAAM,SAAS,KAAK,QAAQ,KAAK,EAAE,KAAK,OAAO,aAAa;CAC5D,MAAM,OAAO,KACV,KAAK,MAAM,GAAG,EAAE,IAAI,GAAG,YAAY,EAAE,MAAM,GAAG,CAC9C,KAAK,KAAK;AACb,eAAc,QAAQ,QAAQ,KAAK,SAAS,OAAO,KAAK,OAAO;AAC/D,SAAQ,IACN,MAAM,MACJ,SAAS,KAAK,OAAO,aAAa,OAAO,QAAQ,QAAQ,KAAK,GAAG,KAAK,GAAG,CAAC,GAC3E,CACF;AACD,KAAI,QAAQ,SAAS,EACnB,SAAQ,IACN,MAAM,IACJ,WAAW,QAAQ,OAAO,wBAAwB,QAAQ,KAAK,MAAM,EAAE,IAAI,CAAC,KAAK,KAAK,CAAC,0BACxF,CACF;EAEH,CACH;;;;AChDH,MAAa,aAAa,IAAI,QAAQ,MAAM,CACzC,YAAY,wCAAwC,CACpD,WAAW,eAAe,CAC1B,WAAW,cAAc,CACzB,WAAW,gBAAgB,CAC3B,WAAW,eAAe;;;;;;;;;;;;ACe7B,MAAM,SAAsB;CAC1B,MAAM;CACN,SAAS;CACT,MAAM,SAAS,KAAoB;EACjC,MAAM,OAAO,IAAI,QAAQ,OAAO,CAAC,YAC/B,oDACD;AAED,OAAK,WAAW,YAAY;AAC5B,OAAK,WAAW,YAAY;AAC5B,OAAK,WAAW,cAAc;AAC9B,OAAK,WAAW,cAAc;AAC9B,OAAK,WAAW,qBAAqB;AACrC,OAAK,WAAW,cAAc;AAC9B,OAAK,WAAW,cAAc;AAC9B,OAAK,WAAW,eAAe;AAC/B,OAAK,WAAW,aAAa;AAC7B,OAAK,WAAW,YAAY;AAC5B,OAAK,WAAW,YAAY;AAC5B,OAAK,WAAW,YAAY;AAC5B,OAAK,WAAW,mBAAmB;AACnC,OAAK,WAAW,YAAY;AAC5B,OAAK,WAAW,WAAW;AAE3B,MAAI,QAAQ,WAAW,KAAK;;CAE/B"}
|