@ait-co/console-cli 0.1.23 → 0.1.25

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/cli.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.mjs","names":["BASE","DEFAULT_NAMES","fileExists","parseYaml","parsePositiveInt","parsePositiveInt","BASE","fileExists","parseYaml","resolvePath","isPromptCancelled","emitInteractiveRequired","lsCommand","showCommand","statusCommand","resolvePath","categoriesCommand","MISSING_HINT_FULL","MISSING_HINT_SHORT","password","passwordPrompt","BASE","lsCommand","winPath","fsConstants","password","passwordPrompt","termsCommand","BASE","lsCommand","lsCommand","showCommand","parsePositiveInt"],"sources":["../src/api/http.ts","../src/api/mini-apps.ts","../src/exit.ts","../src/flush.ts","../src/paths.ts","../src/session.ts","../src/api/error-messages.ts","../src/config/project-context.ts","../src/commands/_shared.ts","../src/config/ait-bundle.ts","../src/commands/app-deploy.ts","../src/api/me.ts","../src/config/app-manifest.ts","../src/commands/app-init-template.ts","../src/commands/app-init.ts","../src/config/image-validator.ts","../src/commands/register-payload.ts","../src/commands/register.ts","../src/commands/app.ts","../src/auth/backend.ts","../src/auth/backends/linux.ts","../src/auth/backends/macos.ts","../src/auth/backends/windows.ts","../src/auth/credentials.ts","../src/auth/kr-only-warning.ts","../src/commands/auth-export.ts","../src/commands/auth-import.ts","../src/commands/auth.ts","../src/commands/completion.ts","../src/api/api-keys.ts","../src/commands/keys.ts","../src/cdp.ts","../src/chrome.ts","../src/login-headless.ts","../src/commands/login.ts","../src/commands/logout.ts","../src/commands/me.ts","../src/api/members.ts","../src/commands/members.ts","../src/api/ipd-thor.ts","../src/commands/notices.ts","../src/github.ts","../src/platform.ts","../src/semver.ts","../src/sha256.ts","../src/version.ts","../src/commands/upgrade.ts","../src/update-check.ts","../src/commands/whoami.ts","../src/api/workspaces.ts","../src/commands/workspace.ts","../src/cli.ts"],"sourcesContent":["// Thin HTTP layer for driving the Apps in Toss console API.\n//\n// Two concerns live here:\n// 1. Serialising the session's captured cookies into a `Cookie` header\n// per request origin (we drop cookies whose Domain/Path don't match\n// the target URL — feeding `apps-in-toss.toss.im` session cookies to\n// `business-accounts.toss.im` would be either ignored or rejected).\n// 2. Unwrapping the Toss `{ resultType, success, error? }` envelope that\n// every console endpoint uses. Upstream callers get `T` on success or\n// a typed `TossApiError` on failure — no need to repeat envelope\n// dispatch in every command.\n//\n// We don't try to be a full cookie jar. The cookie set we care about is\n// captured in one shot at login time and replayed verbatim thereafter;\n// Set-Cookie responses from API calls are ignored. A later PR will add\n// refresh logic once we see whether the console issues sliding sessions.\n\nimport type { CdpCookie } from '../cdp.js';\n\nexport interface TossEnvelopeSuccess<T> {\n readonly resultType: 'SUCCESS';\n readonly success: T;\n}\n\nexport interface TossEnvelopeFailure {\n readonly resultType: 'FAIL';\n readonly success: null;\n readonly error: {\n readonly errorType: number;\n readonly errorCode: string;\n readonly reason: string;\n readonly data?: unknown;\n readonly title?: string | null;\n };\n}\n\nexport type TossEnvelope<T> = TossEnvelopeSuccess<T> | TossEnvelopeFailure;\n\nexport class TossApiError extends Error {\n constructor(\n readonly status: number,\n readonly errorCode: string,\n readonly reason: string,\n readonly errorType: number,\n ) {\n super(`Toss API error ${errorCode}: ${reason} (HTTP ${status})`);\n this.name = 'TossApiError';\n }\n\n /** Cookie-based auth rejected — session missing/expired/invalidated. */\n get isAuthError(): boolean {\n return this.status === 401 || this.errorCode === '4010';\n }\n}\n\nexport class NetworkError extends Error {\n constructor(\n readonly url: string,\n cause: Error,\n ) {\n super(`Network request to ${url} failed: ${cause.message}`);\n this.name = 'NetworkError';\n this.cause = cause;\n }\n}\n\nexport class MalformedResponseError extends Error {\n constructor(\n readonly url: string,\n readonly status: number,\n message: string,\n readonly bodyPreview?: string,\n ) {\n const suffix = bodyPreview ? ` (body: ${bodyPreview})` : '';\n super(`Malformed response from ${url} (HTTP ${status}): ${message}${suffix}`);\n this.name = 'MalformedResponseError';\n }\n}\n\n// --- Cookie matching ---\n\n/**\n * RFC 6265-ish domain match. We accept the bare hostname case plus the\n * standard suffix match (`.example.com` cookie matches `foo.example.com`),\n * because CDP `Network.getAllCookies` normalises cookie Domain to a form\n * with a leading dot for host-matching cookies but without for explicit-host\n * cookies. Either form should round-trip correctly.\n */\nexport function domainMatches(cookieDomain: string, hostname: string): boolean {\n if (cookieDomain.length === 0) return false;\n const lower = cookieDomain.toLowerCase();\n const host = hostname.toLowerCase();\n if (lower === host) return true;\n if (lower.startsWith('.') && host.endsWith(lower)) return true;\n // Host cookies without a leading dot: cookie Domain must equal the host.\n // Suffix-match only applies when there's an explicit leading dot.\n if (!lower.startsWith('.') && host.endsWith(`.${lower}`)) return true;\n return false;\n}\n\n/**\n * RFC 6265 §5.1.4 path match. A cookie Path C matches a request path P iff\n * C and P are identical, OR C is a prefix of P and either C already ends\n * with '/' or the character in P immediately after C is '/'. Notably\n * \"/foo\" does NOT match \"/foobar\".\n */\nexport function pathMatches(cookiePath: string, requestPath: string): boolean {\n if (!cookiePath) return true;\n if (cookiePath === requestPath) return true;\n if (!requestPath.startsWith(cookiePath)) return false;\n return cookiePath.endsWith('/') || requestPath.charAt(cookiePath.length) === '/';\n}\n\n// Cookie name/value must not contain control characters or separators that\n// would let an attacker smuggle header fields via CRLF injection. CDP\n// returns cookie values verbatim, so we defend at the serialisation edge.\nfunction isSafeCookiePart(s: string): boolean {\n // Reject CR, LF, NUL, and ';' (cookie separator). Tabs and spaces are\n // technically allowed but we block them too to avoid header confusion.\n // biome-ignore lint/suspicious/noControlCharactersInRegex: explicit control-char filter\n return !/[\\x00-\\x1f;\\x7f]/.test(s);\n}\n\n/**\n * Build a `Cookie:` header value for the given URL from a captured cookie\n * set. Returns `null` when no cookies match — the caller should skip the\n * header entirely rather than emit `Cookie: ` with an empty value.\n *\n * Ordering follows RFC 6265 §5.4: cookies with longer paths come before\n * cookies with shorter paths; ties break on earliest creation time, which\n * we don't track, so we preserve capture order as a stable tiebreaker.\n */\nexport function cookieHeaderFor(url: URL, cookies: readonly CdpCookie[]): string | null {\n const matching = cookies\n .map((c, i) => ({ c, i }))\n .filter(({ c }) => {\n if (!isSafeCookiePart(c.name) || !isSafeCookiePart(c.value)) return false;\n if (!domainMatches(c.domain, url.hostname)) return false;\n if (!pathMatches(c.path, url.pathname)) return false;\n if (c.secure && url.protocol !== 'https:') return false;\n return true;\n })\n .sort((a, b) => {\n const byPath = b.c.path.length - a.c.path.length;\n return byPath !== 0 ? byPath : a.i - b.i;\n })\n .map(({ c }) => c);\n if (matching.length === 0) return null;\n return matching.map((c) => `${c.name}=${c.value}`).join('; ');\n}\n\n// --- Request helper ---\n\n// Narrow fetch signature that callers (and tests) can satisfy without\n// implementing Bun-specific extensions like `fetch.preconnect`.\nexport type FetchLike = (input: URL | string, init?: RequestInit) => Promise<Response>;\n\nexport interface RequestOptions {\n readonly method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';\n readonly url: string;\n readonly cookies: readonly CdpCookie[];\n readonly body?: unknown;\n readonly fetchImpl?: FetchLike;\n readonly headers?: Record<string, string>;\n}\n\n/**\n * Perform a request against the console API and unwrap the Toss envelope.\n *\n * Always sets `Accept: application/json` and propagates the captured cookie\n * set. Callers may pass additional headers (useful for CSRF tokens that\n * later endpoints turn out to require — discovery is per-feature).\n */\nexport async function requestConsoleApi<T>(options: RequestOptions): Promise<T> {\n const url = new URL(options.url);\n const cookieHeader = cookieHeaderFor(url, options.cookies);\n const headers: Record<string, string> = {\n Accept: 'application/json, text/plain, */*',\n ...options.headers,\n };\n if (cookieHeader) headers.Cookie = cookieHeader;\n\n const init: RequestInit = {\n method: options.method ?? 'GET',\n headers,\n // Cookies handled manually; disable any built-in cookie jar behaviour.\n redirect: 'follow',\n };\n if (options.body !== undefined) {\n headers['Content-Type'] = 'application/json';\n init.body = JSON.stringify(options.body);\n }\n\n return executeAndUnwrap<T>(url, init, options.fetchImpl);\n}\n\n/**\n * Send a pre-built `RequestInit` against the console API and unwrap the\n * Toss envelope. Use this when the caller needs to build the body itself\n * (multipart uploads, binary requests, anything that can't live under\n * `requestConsoleApi`'s JSON-body assumption). Cookie header composition\n * and any additional headers remain the caller's responsibility.\n *\n * Exists so `uploadMiniAppResource` doesn't have to re-implement the\n * text→JSON→envelope→error branch in `requestConsoleApi`; drift between\n * the two paths has bitten us once (cf. the refactor in the\n * `app register` review).\n */\nexport async function executeAndUnwrap<T>(\n url: URL,\n init: RequestInit,\n fetchImpl?: FetchLike,\n): Promise<T> {\n const impl: FetchLike = fetchImpl ?? ((input, i) => fetch(input, i));\n let res: Response;\n try {\n res = await impl(url, init);\n } catch (err) {\n throw new NetworkError(url.toString(), err as Error);\n }\n\n // Read the body as text first so a parse failure can include a preview\n // in the error. Empty responses or non-JSON WAF pages are a lot easier\n // to diagnose when you can see the first few hundred bytes.\n let text: string;\n try {\n text = await res.text();\n } catch (err) {\n throw new MalformedResponseError(url.toString(), res.status, (err as Error).message);\n }\n let parsed: TossEnvelope<T>;\n try {\n parsed = JSON.parse(text) as TossEnvelope<T>;\n } catch (err) {\n const preview = text.slice(0, 200).replace(/\\s+/g, ' ').trim();\n throw new MalformedResponseError(url.toString(), res.status, (err as Error).message, preview);\n }\n\n if (parsed.resultType === 'SUCCESS') {\n return parsed.success;\n }\n throw new TossApiError(\n res.status,\n parsed.error.errorCode,\n parsed.error.reason,\n parsed.error.errorType,\n );\n}\n","import type { CdpCookie } from '../cdp.js';\nimport {\n cookieHeaderFor,\n executeAndUnwrap,\n type FetchLike,\n MalformedResponseError,\n requestConsoleApi,\n} from './http.js';\n\n// Two endpoints cover the \"list my apps\" surface:\n//\n// GET /workspaces/:id/mini-app → array of app summaries\n// GET /workspaces/:id/mini-apps/review-status → { hasPolicyViolation, miniApps: [...] }\n//\n// Note the singular/plural inconsistency (`mini-app` vs `mini-apps`) is\n// how the upstream API actually spells them, not a transcription error —\n// see TODO.md's console feature inventory.\n//\n// The detailed field shape inside each array element is not yet known to\n// us (the confirmed workspaces currently have zero apps). We model each\n// element as a minimal \"id + name + extras\" envelope so the CLI can show\n// something useful today and layer on specific fields as they're observed.\n// `extra` is typed as unknown-valued so we don't pretend to know more.\n\nconst BASE = 'https://apps-in-toss.toss.im/console/api-public/v3/appsintossconsole';\n\nexport interface MiniAppSummary {\n readonly id: string | number;\n readonly name: string | undefined;\n readonly extra: Readonly<Record<string, unknown>>;\n}\n\nexport interface ReviewStatusSummary {\n readonly hasPolicyViolation: boolean;\n readonly miniApps: readonly Readonly<Record<string, unknown>>[];\n}\n\nexport async function fetchMiniApps(\n workspaceId: number,\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<MiniAppSummary[]> {\n const url = `${BASE}/workspaces/${workspaceId}/mini-app`;\n const raw = await requestConsoleApi<unknown>({\n url,\n cookies,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n if (!Array.isArray(raw)) {\n throw new Error(`Unexpected mini-app list shape for workspace=${workspaceId}`);\n }\n return raw.map((item, index) => normalizeMiniApp(item, workspaceId, index));\n}\n\nfunction normalizeMiniApp(item: unknown, workspaceId: number, index: number): MiniAppSummary {\n if (item === null || typeof item !== 'object') {\n throw new Error(\n `Unexpected mini-app entry at index ${index} for workspace=${workspaceId}: not an object`,\n );\n }\n const rec = item as Record<string, unknown>;\n const rawId = rec.id ?? rec.miniAppId ?? rec.appId;\n if (typeof rawId !== 'string' && typeof rawId !== 'number') {\n throw new Error(\n `Unexpected mini-app entry at index ${index} for workspace=${workspaceId}: missing id`,\n );\n }\n const rawName = rec.name ?? rec.miniAppName ?? rec.appName;\n const name = typeof rawName === 'string' ? rawName : undefined;\n const {\n id: _id,\n miniAppId: _mid,\n appId: _aid,\n name: _n,\n miniAppName: _mn,\n appName: _an,\n ...extra\n } = rec;\n return { id: rawId, name, extra };\n}\n\nexport async function fetchReviewStatus(\n workspaceId: number,\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<ReviewStatusSummary> {\n const url = `${BASE}/workspaces/${workspaceId}/mini-apps/review-status`;\n const raw = await requestConsoleApi<unknown>({\n url,\n cookies,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n if (raw === null || typeof raw !== 'object') {\n throw new Error(`Unexpected review-status shape for workspace=${workspaceId}`);\n }\n const rec = raw as Record<string, unknown>;\n const hasPolicyViolation = Boolean(rec.hasPolicyViolation);\n const miniAppsRaw = rec.miniApps;\n if (!Array.isArray(miniAppsRaw)) {\n throw new Error(\n `Unexpected review-status shape for workspace=${workspaceId}: miniApps is not an array`,\n );\n }\n const miniApps = miniAppsRaw.map((m) => {\n if (m === null || typeof m !== 'object') return {};\n return m as Record<string, unknown>;\n });\n return { hasPolicyViolation, miniApps };\n}\n\nexport interface MiniAppWithDraft {\n readonly current: Record<string, unknown> | null;\n readonly draft: Record<string, unknown> | null;\n // Top-level envelope fields (not inside current/draft). Present on every\n // with-draft response. `approvalType` distinguishes REVIEW-submitted apps\n // from drafts that haven't been sent for review; `rejectedMessage` is\n // non-null iff the review came back rejected. Together with `current`\n // (null until an approved record exists) they derive the UI banner state.\n readonly approvalType: string | null;\n readonly rejectedMessage: string | null;\n}\n\nexport async function fetchMiniAppWithDraft(\n workspaceId: number,\n miniAppId: number,\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<MiniAppWithDraft> {\n const url = `${BASE}/workspaces/${workspaceId}/mini-app/${miniAppId}/with-draft`;\n const raw = await requestConsoleApi<unknown>({\n url,\n cookies,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n if (raw === null || typeof raw !== 'object') {\n throw new Error(`Unexpected with-draft shape for mini-app=${miniAppId}`);\n }\n const rec = raw as Record<string, unknown>;\n const current = isRecordOrNull(rec.current)\n ? (rec.current as Record<string, unknown> | null)\n : null;\n const draft = isRecordOrNull(rec.draft) ? (rec.draft as Record<string, unknown> | null) : null;\n const approvalType = typeof rec.approvalType === 'string' ? rec.approvalType : null;\n const rejectedMessage = typeof rec.rejectedMessage === 'string' ? rec.rejectedMessage : null;\n return { current, draft, approvalType, rejectedMessage };\n}\n\nfunction isRecordOrNull(v: unknown): v is Record<string, unknown> | null {\n return v === null || (typeof v === 'object' && !Array.isArray(v));\n}\n\n// --- Ratings & reviews ---\n//\n// GET /workspaces/:wid/mini-app/:aid/app-ratings\n// ?page=0&size=20&sortField=CREATED_AT&sortDirection=DESC\n//\n// Response envelope (observed 2026-04-23 on app 29405, empty case):\n// { ratings: [...], paging: { pageNumber, pageSize, hasNext, totalCount },\n// averageRating, totalReviewCount }\n//\n// Individual rating records have not yet been observed (sdk-example has\n// zero reviews while under review). We pass each record through as an\n// opaque Record<string, unknown> for now; once a real review lands we can\n// pin the per-row shape without breaking the wrapper.\n\nexport type RatingSortField = 'CREATED_AT' | 'SCORE';\nexport type RatingSortDirection = 'ASC' | 'DESC';\n\nexport interface RatingsPaging {\n readonly pageNumber: number;\n readonly pageSize: number;\n readonly hasNext: boolean;\n readonly totalCount: number;\n}\n\nexport interface MiniAppRatingsPage {\n readonly ratings: readonly Readonly<Record<string, unknown>>[];\n readonly paging: RatingsPaging;\n readonly averageRating: number;\n readonly totalReviewCount: number;\n}\n\nexport interface FetchRatingsParams {\n readonly workspaceId: number;\n readonly miniAppId: number;\n readonly page?: number;\n readonly size?: number;\n readonly sortField?: RatingSortField;\n readonly sortDirection?: RatingSortDirection;\n}\n\nexport async function fetchMiniAppRatings(\n params: FetchRatingsParams,\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<MiniAppRatingsPage> {\n const page = params.page ?? 0;\n const size = params.size ?? 20;\n const sortField = params.sortField ?? 'CREATED_AT';\n const sortDirection = params.sortDirection ?? 'DESC';\n const url =\n `${BASE}/workspaces/${params.workspaceId}/mini-app/${params.miniAppId}/app-ratings` +\n `?page=${page}&size=${size}&sortField=${sortField}&sortDirection=${sortDirection}`;\n\n const raw = await requestConsoleApi<unknown>({\n url,\n cookies,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n if (raw === null || typeof raw !== 'object') {\n throw new Error(`Unexpected ratings shape for app=${params.miniAppId}`);\n }\n const rec = raw as Record<string, unknown>;\n const ratingsRaw = rec.ratings;\n if (!Array.isArray(ratingsRaw)) {\n throw new Error(`Unexpected ratings shape: ratings is not an array (app=${params.miniAppId})`);\n }\n const ratings = ratingsRaw.map((r) => {\n if (r === null || typeof r !== 'object') return {};\n return r as Record<string, unknown>;\n });\n const pagingRaw = rec.paging;\n if (pagingRaw === null || typeof pagingRaw !== 'object') {\n throw new Error(`Unexpected ratings shape: paging missing (app=${params.miniAppId})`);\n }\n const p = pagingRaw as Record<string, unknown>;\n const paging: RatingsPaging = {\n pageNumber: typeof p.pageNumber === 'number' ? p.pageNumber : page,\n pageSize: typeof p.pageSize === 'number' ? p.pageSize : size,\n hasNext: Boolean(p.hasNext),\n totalCount: typeof p.totalCount === 'number' ? p.totalCount : 0,\n };\n const averageRating = typeof rec.averageRating === 'number' ? rec.averageRating : 0;\n const totalReviewCount = typeof rec.totalReviewCount === 'number' ? rec.totalReviewCount : 0;\n return { ratings, paging, averageRating, totalReviewCount };\n}\n\n// --- User reports (신고 내역) ---\n//\n// GET /workspaces/:wid/mini-apps/:aid/user-reports?pageSize=N[&cursor=...]\n//\n// Note the URL uses **plural** `mini-apps` — same split-personality as\n// `mini-apps/review-status`. Don't \"normalize\" it.\n//\n// Response envelope (observed 2026-04-23 on app 29405, empty case):\n// { reports: [...], nextCursor: null | string, hasMore: boolean }\n//\n// Pagination is cursor-based here (unlike ratings which is page-based).\n// We expose `cursor` as an optional param and echo back `nextCursor` /\n// `hasMore` so callers can page through without guessing the scheme.\n\nexport interface UserReportsPage {\n readonly reports: readonly Readonly<Record<string, unknown>>[];\n readonly nextCursor: string | null;\n readonly hasMore: boolean;\n}\n\nexport interface FetchUserReportsParams {\n readonly workspaceId: number;\n readonly miniAppId: number;\n readonly pageSize?: number;\n readonly cursor?: string;\n}\n\nexport async function fetchUserReports(\n params: FetchUserReportsParams,\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<UserReportsPage> {\n const pageSize = params.pageSize ?? 20;\n const qs = new URLSearchParams();\n qs.set('pageSize', String(pageSize));\n if (params.cursor !== undefined) qs.set('cursor', params.cursor);\n const url =\n `${BASE}/workspaces/${params.workspaceId}/mini-apps/${params.miniAppId}/user-reports?` +\n qs.toString();\n\n const raw = await requestConsoleApi<unknown>({\n url,\n cookies,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n if (raw === null || typeof raw !== 'object') {\n throw new Error(`Unexpected user-reports shape for app=${params.miniAppId}`);\n }\n const rec = raw as Record<string, unknown>;\n const reportsRaw = rec.reports;\n if (!Array.isArray(reportsRaw)) {\n throw new Error(\n `Unexpected user-reports shape: reports is not an array (app=${params.miniAppId})`,\n );\n }\n const reports = reportsRaw.map((r) => {\n if (r === null || typeof r !== 'object') return {};\n return r as Record<string, unknown>;\n });\n const nextCursor = typeof rec.nextCursor === 'string' ? rec.nextCursor : null;\n const hasMore = Boolean(rec.hasMore);\n return { reports, nextCursor, hasMore };\n}\n\n// --- Bundles (앱 번들 배포) ---\n//\n// GET /workspaces/:wid/mini-app/:aid/bundles[?page=&tested=&deployStatus=]\n// GET /workspaces/:wid/mini-app/:aid/bundles/deployed → single bundle | null\n//\n// Response envelope (observed 2026-04-23 on app 29405, empty case):\n// { contents: [...], totalPage: 0, currentPage: 0 }\n//\n// Page-based pagination (unlike ratings's {paging:{...}} or user-reports\n// cursor-based). Filter query params: `tested=true` for TESTED-only, and\n// `deployStatus=DEPLOYED` to narrow to live bundles. We expose those as\n// optional opaque-string filters so callers can pass them through without\n// the API layer enumerating every enum value ahead of time.\n//\n// `bundles/deployed` (singular route) returns null until a first deploy\n// lands; the shape of a populated record is not yet observed, so we pass\n// it through opaquely.\n\nexport interface BundlesPage {\n readonly contents: readonly Readonly<Record<string, unknown>>[];\n readonly totalPage: number;\n readonly currentPage: number;\n}\n\nexport interface FetchBundlesParams {\n readonly workspaceId: number;\n readonly miniAppId: number;\n readonly page?: number;\n readonly tested?: boolean;\n readonly deployStatus?: string;\n}\n\nexport async function fetchBundles(\n params: FetchBundlesParams,\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<BundlesPage> {\n const qs = new URLSearchParams();\n if (params.page !== undefined) qs.set('page', String(params.page));\n if (params.tested !== undefined) qs.set('tested', String(params.tested));\n if (params.deployStatus !== undefined) qs.set('deployStatus', params.deployStatus);\n const query = qs.toString();\n const url =\n `${BASE}/workspaces/${params.workspaceId}/mini-app/${params.miniAppId}/bundles` +\n (query ? `?${query}` : '');\n\n const raw = await requestConsoleApi<unknown>({\n url,\n cookies,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n if (raw === null || typeof raw !== 'object') {\n throw new Error(`Unexpected bundles shape for app=${params.miniAppId}`);\n }\n const rec = raw as Record<string, unknown>;\n const contentsRaw = rec.contents;\n if (!Array.isArray(contentsRaw)) {\n throw new Error(`Unexpected bundles shape: contents is not an array (app=${params.miniAppId})`);\n }\n const contents = contentsRaw.map((b) => {\n if (b === null || typeof b !== 'object') return {};\n return b as Record<string, unknown>;\n });\n const totalPage = typeof rec.totalPage === 'number' ? rec.totalPage : 0;\n const currentPage = typeof rec.currentPage === 'number' ? rec.currentPage : 0;\n return { contents, totalPage, currentPage };\n}\n\n// --- Conversion metrics ---\n//\n// GET /workspaces/:wid/mini-app/:aid/conversion-metrics\n// ?refresh=false&timeUnitType=DAY|WEEK|MONTH&startDate=YYYY-MM-DD&endDate=YYYY-MM-DD\n//\n// Response envelope (observed 2026-04-23 on app 29405, empty case):\n// { metrics: [], cacheTime: '2026-04-23T13:49:42.693498831' }\n//\n// All apps in the current workspace are PREPARE-state (no live traffic),\n// so the shape of a populated `metrics[]` entry isn't observed yet — we\n// pass records through opaquely until a live app lands. `cacheTime` is\n// the server-side cache timestamp (ISO-ish with nanoseconds); we surface\n// it verbatim so agent-plugin can reason about freshness.\n//\n// `refresh=true` bypasses the server cache; we default to `false` to\n// match the console UI's default request and avoid hammering the\n// underlying data warehouse.\n\nexport type MetricsTimeUnit = 'DAY' | 'WEEK' | 'MONTH';\n\nexport interface MetricsResult {\n readonly metrics: readonly Readonly<Record<string, unknown>>[];\n readonly cacheTime: string | undefined;\n}\n\nexport interface FetchMetricsParams {\n readonly workspaceId: number;\n readonly miniAppId: number;\n readonly timeUnitType: MetricsTimeUnit;\n readonly startDate: string;\n readonly endDate: string;\n readonly refresh?: boolean;\n}\n\nexport async function fetchConversionMetrics(\n params: FetchMetricsParams,\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<MetricsResult> {\n const qs = new URLSearchParams();\n qs.set('refresh', String(params.refresh ?? false));\n qs.set('timeUnitType', params.timeUnitType);\n qs.set('startDate', params.startDate);\n qs.set('endDate', params.endDate);\n const url =\n `${BASE}/workspaces/${params.workspaceId}/mini-app/${params.miniAppId}/conversion-metrics` +\n `?${qs.toString()}`;\n\n const raw = await requestConsoleApi<unknown>({\n url,\n cookies,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n if (raw === null || typeof raw !== 'object') {\n throw new Error(`Unexpected metrics shape for app=${params.miniAppId}`);\n }\n const rec = raw as Record<string, unknown>;\n const metricsRaw = rec.metrics;\n if (!Array.isArray(metricsRaw)) {\n throw new Error(`Unexpected metrics shape: metrics is not an array (app=${params.miniAppId})`);\n }\n const metrics = metricsRaw.map((m) => {\n if (m === null || typeof m !== 'object') return {};\n return m as Record<string, unknown>;\n });\n const cacheTime = typeof rec.cacheTime === 'string' ? rec.cacheTime : undefined;\n return { metrics, cacheTime };\n}\n\n// --- mTLS certs ---\n//\n// Three endpoints, with a path quirk worth pinning at the import site:\n//\n// GET /workspaces/:wid/mini-app/:aid/certs → list\n// POST /workspaces/:wid/mini-app/:aid/cert/issue → issue (singular!)\n// POST /workspaces/:wid/mini-app/:aid/certs/:certId/disable → revoke\n//\n// The console SPA defines all three side-by-side; only `issue` uses the\n// singular `cert` segment. Confirmed by static-analysing the mTLS page\n// chunk (`index.Bw6JQUAu.js`) — see docs/api/mini-app-misc.md\n// \"mTLS cert issue / disable / list\" for the receipts.\n\nexport async function fetchCerts(\n workspaceId: number,\n miniAppId: number,\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<readonly Readonly<Record<string, unknown>>[]> {\n const url = `${BASE}/workspaces/${workspaceId}/mini-app/${miniAppId}/certs`;\n const raw = await requestConsoleApi<unknown>({\n url,\n cookies,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n if (!Array.isArray(raw)) {\n throw new Error(`Unexpected certs shape for app=${miniAppId}: not an array`);\n }\n return raw.map((c) => {\n if (c === null || typeof c !== 'object') return {};\n return c as Record<string, unknown>;\n });\n}\n\n// Issue (`POST .../cert/issue`) is the only path that exposes the private\n// key — list responses only carry metadata. Callers that intend to persist\n// the material must do so atomically; the server side has no re-issue\n// endpoint for the same `name`, just disable + create-fresh.\nexport interface IssueCertResult {\n readonly privateKey: string;\n readonly publicKey: string;\n}\n\nexport async function issueCert(\n workspaceId: number,\n miniAppId: number,\n name: string,\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<IssueCertResult> {\n const url = `${BASE}/workspaces/${workspaceId}/mini-app/${miniAppId}/cert/issue`;\n const raw = await requestConsoleApi<unknown>({\n url,\n method: 'POST',\n body: { name },\n cookies,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n if (raw === null || typeof raw !== 'object') {\n throw new Error(`Unexpected issue-cert shape for app=${miniAppId}: not an object`);\n }\n const rec = raw as Record<string, unknown>;\n const privateKey = typeof rec.privateKey === 'string' ? rec.privateKey : null;\n const publicKey = typeof rec.publicKey === 'string' ? rec.publicKey : null;\n if (privateKey === null || publicKey === null) {\n throw new Error(\n `Unexpected issue-cert shape for app=${miniAppId}: missing privateKey/publicKey`,\n );\n }\n return { privateKey, publicKey };\n}\n\n// `disable` is what the wire calls it; the CLI surfaces it as `revoke`\n// because the console UI itself has no reactivate path.\nexport async function revokeCert(\n workspaceId: number,\n miniAppId: number,\n certId: string,\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<void> {\n const url = `${BASE}/workspaces/${workspaceId}/mini-app/${miniAppId}/certs/${encodeURIComponent(certId)}/disable`;\n await requestConsoleApi<unknown>({\n url,\n method: 'POST',\n body: {},\n cookies,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n}\n\n// --- Share rewards ---\n//\n// GET /workspaces/:wid/mini-app/:aid/share-rewards[?search=]\n//\n// Response envelope (observed 2026-04-23 on app 29405, empty case):\n// []\n//\n// Simple array. The console UI passes `search=` as a title-contains\n// filter (observed as the default XHR on the 공유 리워드 page) — the empty\n// string matches everything. Per-record shape is passed through opaquely\n// until a populated response is observed; promotions won't exist on\n// unreleased apps.\n\nexport interface FetchShareRewardsParams {\n readonly workspaceId: number;\n readonly miniAppId: number;\n readonly search?: string;\n}\n\nexport async function fetchShareRewards(\n params: FetchShareRewardsParams,\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<readonly Readonly<Record<string, unknown>>[]> {\n const qs = new URLSearchParams();\n // Match the console UI's default (always sends `search=`). Callers that\n // don't pass a filter still include it as empty so the request shape\n // matches what the server expects.\n qs.set('search', params.search ?? '');\n const url = `${BASE}/workspaces/${params.workspaceId}/mini-app/${params.miniAppId}/share-rewards?${qs.toString()}`;\n const raw = await requestConsoleApi<unknown>({\n url,\n cookies,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n if (!Array.isArray(raw)) {\n throw new Error(`Unexpected share-rewards shape for app=${params.miniAppId}: not an array`);\n }\n return raw.map((r) => {\n if (r === null || typeof r !== 'object') return {};\n return r as Record<string, unknown>;\n });\n}\n\n// --- Smart-message campaigns ---\n//\n// \"스마트 발송\" (smart-message) is the replacement for the legacy\n// push-notifications menu. List endpoint is a POST with the filter body\n// in JSON and paging in the querystring — that shape is the console\n// UI's XHR, and we mirror it so the request is indistinguishable.\n//\n// Empirical response shape (live workspace 3095, PREPARE-state app):\n// { items: [], paging: { pageNumber, pageSize, hasNext, totalCount } }\n// Items are passed through opaquely until a populated campaign is\n// observed.\n\nexport type SmartMessageSort = { field: string; direction: 'ASC' | 'DESC' };\n// Open-ended filters bag. The UI currently sends `{}` or\n// `{ channelTypes: [] }` depending on which tab is active — we keep\n// the type permissive because more facets may surface over time.\nexport type SmartMessageFilters = Readonly<Record<string, unknown>>;\n\nexport interface FetchSmartMessageCampaignsParams {\n readonly workspaceId: number;\n readonly miniAppId: number;\n readonly page?: number;\n readonly size?: number;\n readonly search?: string;\n readonly sort?: readonly SmartMessageSort[];\n readonly filters?: SmartMessageFilters;\n}\n\nexport interface SmartMessagePaging {\n readonly pageNumber: number;\n readonly pageSize: number;\n readonly hasNext: boolean;\n readonly totalCount: number;\n}\n\nexport interface SmartMessageCampaignsResult {\n readonly items: readonly Readonly<Record<string, unknown>>[];\n readonly paging: SmartMessagePaging;\n}\n\nexport async function fetchSmartMessageCampaigns(\n params: FetchSmartMessageCampaignsParams,\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<SmartMessageCampaignsResult> {\n const page = params.page ?? 0;\n const size = params.size ?? 20;\n const qs = new URLSearchParams();\n qs.set('page', String(page));\n qs.set('size', String(size));\n const url = `${BASE}/workspaces/${params.workspaceId}/mini-app/${params.miniAppId}/smart-message/campaigns?${qs.toString()}`;\n const body = {\n sort: params.sort ?? [{ field: 'regTs', direction: 'DESC' }],\n search: params.search ?? '',\n filters: params.filters ?? {},\n };\n const raw = await requestConsoleApi<unknown>({\n method: 'POST',\n url,\n body,\n cookies,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) {\n throw new Error(`Unexpected smart-message campaigns shape for app=${params.miniAppId}`);\n }\n const data = raw as Record<string, unknown>;\n const items = Array.isArray(data.items) ? data.items : [];\n const rawPaging = data.paging as Record<string, unknown> | undefined;\n const paging: SmartMessagePaging = {\n pageNumber: typeof rawPaging?.pageNumber === 'number' ? rawPaging.pageNumber : page,\n pageSize: typeof rawPaging?.pageSize === 'number' ? rawPaging.pageSize : size,\n hasNext: Boolean(rawPaging?.hasNext),\n totalCount: typeof rawPaging?.totalCount === 'number' ? rawPaging.totalCount : items.length,\n };\n return {\n items: items.map((r) => (r && typeof r === 'object' ? (r as Record<string, unknown>) : {})),\n paging,\n };\n}\n\n// --- Event catalogs (log search) ---\n//\n// The console \"이벤트\" (events) page is powered by a POST search endpoint\n// that returns the catalog of custom events recorded for a mini-app.\n// Body shape from observed UI XHR: `{isRefresh, pageNumber, pageSize, search}`.\n// Response: `{results, cacheTime, paging: {pageNumber, pageSize, hasNext,\n// totalCount, totalPages}}`. On a PREPARE-state app with no traffic,\n// `results` is empty and `cacheTime` carries a server-cache timestamp —\n// same pattern as conversion-metrics.\n\nexport interface FetchAppEventCatalogsParams {\n readonly workspaceId: number;\n readonly miniAppId: number;\n readonly pageNumber?: number;\n readonly pageSize?: number;\n readonly search?: string;\n readonly refresh?: boolean;\n}\n\nexport interface AppEventCatalogsPaging {\n readonly pageNumber: number;\n readonly pageSize: number;\n readonly hasNext: boolean;\n readonly totalCount: number;\n readonly totalPages: number;\n}\n\nexport interface AppEventCatalogsResult {\n readonly results: readonly Readonly<Record<string, unknown>>[];\n readonly cacheTime: string | undefined;\n readonly paging: AppEventCatalogsPaging;\n}\n\nexport async function fetchAppEventCatalogs(\n params: FetchAppEventCatalogsParams,\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<AppEventCatalogsResult> {\n const pageNumber = params.pageNumber ?? 0;\n const pageSize = params.pageSize ?? 20;\n const url = `${BASE}/workspaces/${params.workspaceId}/mini-app/${params.miniAppId}/log/catalogs/search`;\n const body = {\n isRefresh: params.refresh ?? false,\n pageNumber,\n pageSize,\n search: params.search ?? '',\n };\n const raw = await requestConsoleApi<unknown>({\n method: 'POST',\n url,\n body,\n cookies,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) {\n throw new Error(`Unexpected event-catalogs shape for app=${params.miniAppId}`);\n }\n const data = raw as Record<string, unknown>;\n const results = Array.isArray(data.results) ? data.results : [];\n const rawPaging = data.paging as Record<string, unknown> | undefined;\n const paging: AppEventCatalogsPaging = {\n pageNumber: typeof rawPaging?.pageNumber === 'number' ? rawPaging.pageNumber : pageNumber,\n pageSize: typeof rawPaging?.pageSize === 'number' ? rawPaging.pageSize : pageSize,\n hasNext: Boolean(rawPaging?.hasNext),\n totalCount: typeof rawPaging?.totalCount === 'number' ? rawPaging.totalCount : results.length,\n totalPages: typeof rawPaging?.totalPages === 'number' ? rawPaging.totalPages : 0,\n };\n return {\n results: results.map((r) => (r && typeof r === 'object' ? (r as Record<string, unknown>) : {})),\n cacheTime: typeof data.cacheTime === 'string' ? data.cacheTime : undefined,\n paging,\n };\n}\n\n// --- Templates (smart-message composer) ---\n//\n// GET /mini-app/:id/templates/search\n// ?page=0&size=20&contentReachType=FUNCTIONAL|MARKETING&isSmartMessage=true|false\n//\n// Response shape (observed on empty app 29405):\n// { page: { totalPageCount }, groupSendContextSimpleView: [] }\n//\n// The odd bucket key name (`groupSendContextSimpleView`) is the server's\n// own — we surface it as `templates` at the CLI layer so the output is\n// readable without leaking the internal naming. Per-template record\n// shape is passed through opaquely until a populated response is seen.\n\nexport type TemplateContentReachType = 'FUNCTIONAL' | 'MARKETING';\nexport const TEMPLATE_CONTENT_REACH_TYPES: readonly TemplateContentReachType[] = [\n 'FUNCTIONAL',\n 'MARKETING',\n];\n\nexport interface FetchAppTemplatesParams {\n readonly workspaceId: number;\n readonly miniAppId: number;\n readonly page?: number;\n readonly size?: number;\n readonly contentReachType?: TemplateContentReachType;\n readonly isSmartMessage?: boolean;\n}\n\nexport interface AppTemplatesResult {\n readonly templates: readonly Readonly<Record<string, unknown>>[];\n readonly totalPageCount: number;\n}\n\nexport async function fetchAppTemplates(\n params: FetchAppTemplatesParams,\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<AppTemplatesResult> {\n const page = params.page ?? 0;\n const size = params.size ?? 20;\n const qs = new URLSearchParams();\n qs.set('page', String(page));\n qs.set('size', String(size));\n if (params.contentReachType) qs.set('contentReachType', params.contentReachType);\n if (params.isSmartMessage !== undefined) qs.set('isSmartMessage', String(params.isSmartMessage));\n const url = `${BASE}/workspaces/${params.workspaceId}/mini-app/${params.miniAppId}/templates/search?${qs.toString()}`;\n const raw = await requestConsoleApi<unknown>({\n url,\n cookies,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) {\n throw new Error(`Unexpected templates shape for app=${params.miniAppId}`);\n }\n const data = raw as Record<string, unknown>;\n const list = Array.isArray(data.groupSendContextSimpleView)\n ? data.groupSendContextSimpleView\n : [];\n const pageMeta = data.page as Record<string, unknown> | undefined;\n return {\n templates: list.map((r) => (r && typeof r === 'object' ? (r as Record<string, unknown>) : {})),\n totalPageCount: typeof pageMeta?.totalPageCount === 'number' ? pageMeta.totalPageCount : 0,\n };\n}\n\n// --- Impression category tree ---\n//\n// GET /impression/category-list returns the full category hierarchy used\n// by `app register`. It's a global, workspace-independent lookup — the\n// console navigates directly to the `/impression` prefix (NOT\n// `/workspaces/:id/impression`). `isSelectable: true` marks which\n// entries callers may actually reference from `categoryIds`.\n//\n// Tree: [{ categoryGroup, categoryList: [{ id, name, isSelectable,\n// subCategoryList: [{ id, name, isSelectable }] }] }]\n\nexport interface CategoryGroupNode {\n readonly id: number;\n readonly name: string;\n readonly isSelectable: boolean;\n}\n\nexport interface SubCategoryNode {\n readonly id: number;\n readonly name: string;\n readonly isSelectable: boolean;\n}\n\nexport interface CategoryNode {\n readonly id: number;\n readonly name: string;\n readonly isSelectable: boolean;\n readonly subCategoryList: readonly SubCategoryNode[];\n}\n\nexport interface CategoryTreeEntry {\n readonly categoryGroup: CategoryGroupNode;\n readonly categoryList: readonly CategoryNode[];\n}\n\nexport async function fetchImpressionCategoryList(\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<readonly CategoryTreeEntry[]> {\n const url = `${BASE}/impression/category-list`;\n const raw = await requestConsoleApi<unknown>({\n url,\n cookies,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n if (!Array.isArray(raw)) {\n throw new Error('Unexpected impression/category-list shape: not an array');\n }\n return raw.map((entry, i): CategoryTreeEntry => {\n if (!entry || typeof entry !== 'object') {\n throw new Error(`Unexpected category-list entry at index ${i}`);\n }\n const e = entry as Record<string, unknown>;\n const group = e.categoryGroup as Record<string, unknown> | undefined;\n const list = Array.isArray(e.categoryList) ? e.categoryList : [];\n return {\n categoryGroup: {\n id: typeof group?.id === 'number' ? group.id : 0,\n name: typeof group?.name === 'string' ? group.name : '',\n isSelectable: Boolean(group?.isSelectable),\n },\n categoryList: list.map((c): CategoryNode => {\n if (!c || typeof c !== 'object') {\n return { id: 0, name: '', isSelectable: false, subCategoryList: [] };\n }\n const cr = c as Record<string, unknown>;\n const subs = Array.isArray(cr.subCategoryList) ? cr.subCategoryList : [];\n return {\n id: typeof cr.id === 'number' ? cr.id : 0,\n name: typeof cr.name === 'string' ? cr.name : '',\n isSelectable: Boolean(cr.isSelectable),\n subCategoryList: subs.map((s): SubCategoryNode => {\n if (!s || typeof s !== 'object') {\n return { id: 0, name: '', isSelectable: false };\n }\n const sr = s as Record<string, unknown>;\n return {\n id: typeof sr.id === 'number' ? sr.id : 0,\n name: typeof sr.name === 'string' ? sr.name : '',\n isSelectable: Boolean(sr.isSelectable),\n };\n }),\n };\n }),\n };\n });\n}\n\n// --- App service status (per-mini-app shutdown/service state) ---\n//\n// GET /mini-app/:id/review-status (singular `mini-app`) returns the\n// runtime service status of a single mini-app. Distinct from the\n// workspace-level `mini-apps/review-status` (plural) which reports the\n// pending/approved state of every app in a workspace; this one is the\n// server-authoritative view of whether the app is live, preparing, or\n// scheduled for shutdown.\n//\n// Observed shape on a PREPARE app:\n// { shutdownCandidateStatus: null, scheduledShutdownAt: null,\n// serviceStatus: 'PREPARE' }\n\nexport interface AppServiceStatus {\n readonly shutdownCandidateStatus: string | null;\n readonly scheduledShutdownAt: string | null;\n readonly serviceStatus: string;\n}\n\nexport async function fetchAppServiceStatus(\n workspaceId: number,\n miniAppId: number,\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<AppServiceStatus> {\n const url = `${BASE}/workspaces/${workspaceId}/mini-app/${miniAppId}/review-status`;\n const raw = await requestConsoleApi<unknown>({\n url,\n cookies,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) {\n throw new Error(`Unexpected app service-status shape for app=${miniAppId}`);\n }\n const data = raw as Record<string, unknown>;\n const serviceStatus = data.serviceStatus;\n if (typeof serviceStatus !== 'string') {\n throw new Error(\n `Unexpected app service-status shape for app=${miniAppId}: missing serviceStatus`,\n );\n }\n return {\n shutdownCandidateStatus:\n typeof data.shutdownCandidateStatus === 'string' ? data.shutdownCandidateStatus : null,\n scheduledShutdownAt:\n typeof data.scheduledShutdownAt === 'string' ? data.scheduledShutdownAt : null,\n serviceStatus,\n };\n}\n\nexport async function fetchDeployedBundle(\n workspaceId: number,\n miniAppId: number,\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<Record<string, unknown> | null> {\n const url = `${BASE}/workspaces/${workspaceId}/mini-app/${miniAppId}/bundles/deployed`;\n const raw = await requestConsoleApi<unknown>({\n url,\n cookies,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n if (raw === null) return null;\n if (typeof raw !== 'object' || Array.isArray(raw)) {\n throw new Error(`Unexpected deployed-bundle shape for app=${miniAppId}`);\n }\n return raw as Record<string, unknown>;\n}\n\n// --- Bundle upload (deploy) ---\n//\n// The console's \"앱 출시 > 등록하기\" dialog walks a 3-step server dance\n// plus an optional memo write:\n//\n// 1. POST /mini-app/:id/deployments/initialize body {deploymentId}\n// → { deployment: { reviewStatus, ... }, uploadUrl }\n// 2. PUT <uploadUrl> Content-Type: application/zip, body = raw .ait bytes\n// (S3 presigned URL; goes direct to S3, no Toss envelope)\n// 3. POST /mini-app/:id/deployments/complete body {deploymentId}\n// → bundle record (server-side confirmation)\n// 4. (optional) POST /mini-app/:id/bundles/memos body {deploymentId, memo}\n//\n// `deploymentId` is embedded in the .ait bundle itself — the toolchain\n// that packs the bundle writes it into `app.json._metadata.deploymentId`\n// (observed via static analysis of the console's client-side parser in\n// `index.ZIQgZB74.js`, 2026-04-23). We take it as an explicit parameter\n// here to keep this layer free of zip-parsing logic; the command layer\n// can either crack the zip or let the user pass `--deployment-id` by\n// hand.\n//\n// The initialize response's `deployment.reviewStatus` must be `PREPARE`\n// before the PUT fires — any other value means the deploymentId has\n// already been used and the console raises \"이미 존재하는 버전이에요.\"\n\nexport interface DeploymentInitializeResult {\n readonly uploadUrl: string;\n readonly deployment: Readonly<Record<string, unknown>>;\n readonly reviewStatus: string;\n}\n\nexport async function postDeploymentsInitialize(\n workspaceId: number,\n miniAppId: number,\n deploymentId: string,\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<DeploymentInitializeResult> {\n const url = `${BASE}/workspaces/${workspaceId}/mini-app/${miniAppId}/deployments/initialize`;\n const raw = await requestConsoleApi<unknown>({\n method: 'POST',\n url,\n body: { deploymentId },\n cookies,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) {\n throw new Error(`Unexpected deployments/initialize shape for app=${miniAppId}`);\n }\n const data = raw as Record<string, unknown>;\n const uploadUrl = data.uploadUrl;\n if (typeof uploadUrl !== 'string') {\n throw new Error(\n `Unexpected deployments/initialize shape for app=${miniAppId}: missing uploadUrl`,\n );\n }\n const deployment =\n data.deployment && typeof data.deployment === 'object'\n ? (data.deployment as Record<string, unknown>)\n : {};\n const reviewStatus =\n typeof deployment.reviewStatus === 'string' ? deployment.reviewStatus : 'UNKNOWN';\n return { uploadUrl, deployment, reviewStatus };\n}\n\nexport async function postDeploymentsComplete(\n workspaceId: number,\n miniAppId: number,\n deploymentId: string,\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<Readonly<Record<string, unknown>>> {\n const url = `${BASE}/workspaces/${workspaceId}/mini-app/${miniAppId}/deployments/complete`;\n const raw = await requestConsoleApi<unknown>({\n method: 'POST',\n url,\n body: { deploymentId },\n cookies,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n // Complete returns the persisted bundle record; pass it through opaquely\n // until we see populated shape in dog-food.\n if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) return {};\n return raw as Record<string, unknown>;\n}\n\nexport async function postBundleMemo(\n workspaceId: number,\n miniAppId: number,\n deploymentId: string,\n memo: string,\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<Readonly<Record<string, unknown>>> {\n const url = `${BASE}/workspaces/${workspaceId}/mini-app/${miniAppId}/bundles/memos`;\n const raw = await requestConsoleApi<unknown>({\n method: 'POST',\n url,\n body: { deploymentId, memo },\n cookies,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) return {};\n return raw as Record<string, unknown>;\n}\n\n/**\n * PUT the raw .ait bytes to the S3 presigned URL returned by\n * `postDeploymentsInitialize`. This is a direct-to-S3 call with NO Toss\n * envelope — the response is empty on success (HTTP 200). Any cookies are\n * intentionally NOT sent because S3 would reject the signed request if\n * extra auth headers contradict the signature.\n */\nexport async function putBundleToUploadUrl(\n uploadUrl: string,\n body: Uint8Array,\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<void> {\n const impl: FetchLike = opts.fetchImpl ?? ((i, init) => fetch(i, init));\n // BodyInit's Uint8Array overload requires `ArrayBufferView<ArrayBuffer>`,\n // so we explicitly view the bytes with a plain ArrayBuffer backing — same\n // trick used in `uploadMiniAppResource` above. A `Buffer` from readFile\n // may be backed by a `SharedArrayBuffer` which `BodyInit` rejects.\n const view = new Uint8Array(body.buffer as ArrayBuffer, body.byteOffset, body.byteLength);\n let res: Response;\n try {\n res = await impl(uploadUrl, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/zip' },\n body: view,\n });\n } catch (err) {\n throw new Error(`PUT to upload URL failed: ${(err as Error).message}`);\n }\n if (!res.ok) {\n const preview = await res.text().catch(() => '');\n throw new Error(`PUT to upload URL returned HTTP ${res.status}: ${preview.slice(0, 200)}`);\n }\n}\n\n// --- Bundle review / release / withdraw / test ---\n//\n// After a bundle is uploaded it sits in reviewStatus=PREPARE on the server.\n// To actually ship it, three further mutations may fire:\n//\n// POST /bundles/reviews body {deploymentId, releaseNotes, featureList?, screenshotImagePaths?}\n// → submits for Toss review. reviewAppBundle()\n// POST /bundles/reviews/withdrawal body {deploymentId}\n// → cancels an in-flight review. postWithdrawAppBundleReview()\n// POST /bundles/release body {deploymentId, contentImages?}\n// → flips an APPROVED bundle live in the marketplace. releaseAppBundle()\n//\n// Plus two read/test helpers:\n//\n// POST /bundles/test-push body {deploymentId} → sends a push so the uploader can open the build on their device\n// GET /bundles/test-links → returns per-device test URLs\n//\n// All four mutation bodies, plus the test-push trigger, were observed\n// via static analysis of the console's `index.ZIQgZB74.js` chunk\n// (2026-04-23). No live XHR was triggered for these from the CLI — they\n// are destructive write paths (release especially is visible to end\n// users) and must be dog-fooded with care.\n\nexport interface ReviewAppBundleParams {\n readonly workspaceId: number;\n readonly miniAppId: number;\n readonly deploymentId: string;\n readonly releaseNotes: string;\n readonly featureList?: readonly Readonly<Record<string, unknown>>[];\n readonly screenshotImagePaths?: readonly string[];\n}\n\nexport async function postBundleReview(\n params: ReviewAppBundleParams,\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<Readonly<Record<string, unknown>>> {\n const url = `${BASE}/workspaces/${params.workspaceId}/mini-app/${params.miniAppId}/bundles/reviews`;\n const body: Record<string, unknown> = {\n deploymentId: params.deploymentId,\n releaseNotes: params.releaseNotes,\n };\n if (params.featureList !== undefined) body.featureList = params.featureList;\n if (params.screenshotImagePaths !== undefined)\n body.screenshotImagePaths = params.screenshotImagePaths;\n const raw = await requestConsoleApi<unknown>({\n method: 'POST',\n url,\n body,\n cookies,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) return {};\n return raw as Record<string, unknown>;\n}\n\nexport async function postBundleReviewWithdrawal(\n workspaceId: number,\n miniAppId: number,\n deploymentId: string,\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<Readonly<Record<string, unknown>>> {\n const url = `${BASE}/workspaces/${workspaceId}/mini-app/${miniAppId}/bundles/reviews/withdrawal`;\n const raw = await requestConsoleApi<unknown>({\n method: 'POST',\n url,\n body: { deploymentId },\n cookies,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) return {};\n return raw as Record<string, unknown>;\n}\n\nexport interface ReleaseAppBundleParams {\n readonly workspaceId: number;\n readonly miniAppId: number;\n readonly deploymentId: string;\n readonly contentImages?: readonly string[];\n}\n\nexport async function postBundleRelease(\n params: ReleaseAppBundleParams,\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<Readonly<Record<string, unknown>>> {\n const url = `${BASE}/workspaces/${params.workspaceId}/mini-app/${params.miniAppId}/bundles/release`;\n const body: Record<string, unknown> = { deploymentId: params.deploymentId };\n if (params.contentImages !== undefined) body.contentImages = params.contentImages;\n const raw = await requestConsoleApi<unknown>({\n method: 'POST',\n url,\n body,\n cookies,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) return {};\n return raw as Record<string, unknown>;\n}\n\nexport async function postBundleTestPush(\n workspaceId: number,\n miniAppId: number,\n deploymentId: string,\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<Readonly<Record<string, unknown>>> {\n const url = `${BASE}/workspaces/${workspaceId}/mini-app/${miniAppId}/bundles/test-push`;\n const raw = await requestConsoleApi<unknown>({\n method: 'POST',\n url,\n body: { deploymentId },\n cookies,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) return {};\n return raw as Record<string, unknown>;\n}\n\nexport async function fetchBundleTestLinks(\n workspaceId: number,\n miniAppId: number,\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<Readonly<Record<string, unknown>>> {\n const url = `${BASE}/workspaces/${workspaceId}/mini-app/${miniAppId}/bundles/test-links`;\n const raw = await requestConsoleApi<unknown>({\n url,\n cookies,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) return {};\n return raw as Record<string, unknown>;\n}\n\n// --- Register (create) ---\n//\n// `createMiniApp` and `uploadMiniAppResource` back the `app register`\n// command. The submit payload shape below is *inferred* from static\n// bundle analysis (`VALIDATION-RULES.md` in the local `.playwright-\n// mcp/`); the console UI never round-trips intermediate drafts, so the\n// only authoritative record will come from dog-food task #23. Field\n// names here intentionally mirror the `Xc` function from the bundle so\n// when #23 runs, any correction is a direct rename rather than a\n// restructure.\n\n// Exposed as `unknown` per-image so the caller (not this layer) is the\n// place where a cross-field invariant like \"at least 3 PREVIEW/VERTICAL\"\n// is enforced. Keeping this type open also makes it trivial to add the\n// `LOGO` imageType or other orientation values without touching the\n// network module.\nexport type MiniAppImageType = 'LOGO' | 'THUMBNAIL' | 'PREVIEW';\nexport type MiniAppImageOrientation = 'HORIZONTAL' | 'VERTICAL';\n\nexport interface MiniAppImageEntry {\n readonly imageUrl: string;\n readonly imageType: MiniAppImageType;\n readonly orientation: MiniAppImageOrientation;\n}\n\nexport interface MiniAppSubmitPayload {\n readonly miniApp: {\n readonly title: string;\n readonly titleEn: string;\n readonly appName: string;\n readonly iconUri: string;\n readonly status: 'PREPARE';\n readonly darkModeIconUri?: string;\n readonly homePageUri?: string;\n readonly csEmail: string;\n readonly description: string; // subtitle (≤20 chars)\n readonly detailDescription: string;\n readonly images: readonly MiniAppImageEntry[];\n };\n readonly impression: {\n readonly keywordList: readonly string[];\n readonly categoryIds: readonly number[];\n readonly subCategoryIds?: readonly number[];\n };\n}\n\nexport interface CreateMiniAppResult {\n readonly miniAppId: string | number | undefined;\n readonly reviewState: string | undefined;\n readonly extra: Readonly<Record<string, unknown>>;\n}\n\nexport async function createMiniApp(\n workspaceId: number,\n payload: MiniAppSubmitPayload,\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<CreateMiniAppResult> {\n const url = `${BASE}/workspaces/${workspaceId}/mini-app/review`;\n const raw = await requestConsoleApi<unknown>({\n url,\n method: 'POST',\n cookies,\n body: payload,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n return normalizeCreateResult(raw);\n}\n\nfunction normalizeCreateResult(raw: unknown): CreateMiniAppResult {\n if (raw === null || typeof raw !== 'object') {\n return { miniAppId: undefined, reviewState: undefined, extra: {} };\n }\n const rec = raw as Record<string, unknown>;\n const rawId = rec.miniAppId ?? rec.id ?? rec.appId;\n const miniAppId = typeof rawId === 'string' || typeof rawId === 'number' ? rawId : undefined;\n const rawState = rec.reviewState ?? rec.status;\n const reviewState = typeof rawState === 'string' ? rawState : undefined;\n return { miniAppId, reviewState, extra: rec };\n}\n\nexport interface UploadFile {\n readonly buffer: Buffer;\n readonly fileName: string;\n readonly contentType: string;\n}\n\nexport interface UploadParams {\n readonly workspaceId: number;\n readonly validWidth: number;\n readonly validHeight: number;\n readonly file: UploadFile;\n readonly cookies: readonly CdpCookie[];\n}\n\n/**\n * Upload an image to `/resource/:wid/upload?validWidth=W&validHeight=H`\n * and return the CDN URL the server hands back. The endpoint is a\n * multipart/form-data POST; we build a FormData with a single `resource`\n * field because that matches the bundle analysis for the console's\n * uploader, which pairs a `fileName` string field with a `resource`\n * Blob (see VALIDATION-RULES.md → iconUri). Dog-food #23 may reveal that\n * the field name is actually `file` — if so, swap it in one place here.\n */\nexport async function uploadMiniAppResource(\n params: UploadParams,\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<string> {\n const url = new URL(`${BASE}/resource/${params.workspaceId}/upload`);\n url.searchParams.set('validWidth', String(params.validWidth));\n url.searchParams.set('validHeight', String(params.validHeight));\n\n const form = new FormData();\n // A `Buffer` is already a `Uint8Array`, but its `ArrayBufferLike`\n // backing can be a `SharedArrayBuffer` which `BlobPart` doesn't accept.\n // Wrapping in a `Uint8Array` view over the same bytes (byteOffset +\n // byteLength) keeps the type happy without the extra copy that\n // `new Uint8Array(buffer)` would force.\n const view = new Uint8Array(\n params.file.buffer.buffer as ArrayBuffer,\n params.file.buffer.byteOffset,\n params.file.buffer.byteLength,\n );\n const blob = new Blob([view], { type: params.file.contentType });\n form.append('resource', blob, params.file.fileName);\n form.append('fileName', params.file.fileName);\n\n const cookieHeader = cookieHeaderFor(url, params.cookies);\n const headers: Record<string, string> = {\n Accept: 'application/json, text/plain, */*',\n };\n if (cookieHeader) headers.Cookie = cookieHeader;\n\n const imageUrl = await executeAndUnwrap<unknown>(\n url,\n { method: 'POST', headers, body: form },\n opts.fetchImpl,\n );\n if (typeof imageUrl !== 'string') {\n throw new MalformedResponseError(\n url.toString(),\n 200,\n `expected string imageUrl, got ${typeof imageUrl}`,\n );\n }\n return imageUrl;\n}\n","// Centralized exit codes so every command and the agent-plugin side agree.\n\nexport const ExitCode = {\n Ok: 0,\n Generic: 1,\n Usage: 2,\n NotAuthenticated: 10,\n NetworkError: 11,\n LoginTimeout: 12,\n // Reserved historical slot (was LoginStateMismatch under the OAuth\n // callback flow). Unused by the CDP login path but kept stable so the\n // agent-plugin side doesn't need to renumber.\n LoginStateMismatch: 13,\n LoginBrowserNotFound: 14,\n LoginBrowserFailed: 15,\n LoginCookieCaptureFailed: 16,\n ApiError: 17,\n UpgradeUnavailable: 20,\n UpgradeAlreadyLatest: 21,\n // SHA-256 verification of a downloaded upgrade asset failed (bad sum,\n // missing entry, or no SHA256SUMS asset on the release).\n UpgradeChecksumFailed: 22,\n // New binary downloaded and verified, but `--version` smoke test after\n // atomic replace failed (timeout, non-zero exit, or empty stdout). Rollback\n // to the previous binary is attempted automatically; the JSON payload\n // reports `rolledBack: true` on success or `rollbackError` on failure.\n UpgradeSmokeTestFailed: 23,\n} as const;\n\nexport type ExitCode = (typeof ExitCode)[keyof typeof ExitCode];\n","// Flush-safe exit: drain stdout before calling `process.exit` so a piped\n// consumer never loses the final JSON line. Callers typically write the\n// JSON payload (or plain-text result) to stdout immediately before\n// calling `return exitAfterFlush(code)`.\n\nexport async function exitAfterFlush(code: number): Promise<never> {\n await new Promise<void>((resolve) => process.stdout.write('', () => resolve()));\n process.exit(code);\n}\n","import { homedir } from 'node:os';\nimport { join } from 'node:path';\n\n// Resolve the config directory following the XDG Base Directory spec on\n// POSIX systems and using %APPDATA% on Windows. Falls back gracefully if\n// environment variables are missing (e.g. minimal containers without HOME).\n\nconst APP_NAME = 'aitcc';\n\nexport function configDir(): string {\n if (process.platform === 'win32') {\n const appData = process.env.APPDATA;\n if (appData && appData.length > 0) return join(appData, APP_NAME);\n return join(homedir() || '.', 'AppData', 'Roaming', APP_NAME);\n }\n const xdg = process.env.XDG_CONFIG_HOME;\n if (xdg && xdg.length > 0) return join(xdg, APP_NAME);\n return join(homedir() || '.', '.config', APP_NAME);\n}\n\nexport function sessionFilePath(): string {\n return join(configDir(), 'session.json');\n}\n\n// Pointer file for credential state. Stores the email of the credential\n// currently active in the OS keychain (so the next `aitcc login` can look\n// it up without prompting). NEVER contains the password — that lives in\n// the keychain, keyed by this email. Distinct from session.json on\n// purpose: a session has volatile cookies, credentials are durable.\nexport function authStateFilePath(): string {\n return join(configDir(), 'auth-state.json');\n}\n\n// Cache directory — for non-secret, regenerable state. Distinct from\n// `configDir()` so a `rm -rf ~/.cache/aitcc` cannot take the session with\n// it, and so packagers can mount the two on different volumes.\nexport function cacheDir(): string {\n if (process.platform === 'win32') {\n const localAppData = process.env.LOCALAPPDATA;\n if (localAppData && localAppData.length > 0) return join(localAppData, APP_NAME, 'Cache');\n return join(homedir() || '.', 'AppData', 'Local', APP_NAME, 'Cache');\n }\n const xdg = process.env.XDG_CACHE_HOME;\n if (xdg && xdg.length > 0) return join(xdg, APP_NAME);\n return join(homedir() || '.', '.cache', APP_NAME);\n}\n\nexport function upgradeCheckPath(): string {\n return join(cacheDir(), 'upgrade-check.json');\n}\n","import { chmod, mkdir, readFile, unlink, writeFile } from 'node:fs/promises';\nimport { dirname } from 'node:path';\nimport type { CdpCookie } from './cdp.js';\nimport { configDir, sessionFilePath } from './paths.js';\n\n// Minimal, forward-compatible session shape. `cookies` mirrors the CDP\n// `Network.getAllCookies` payload so the login command can drop it in\n// directly and the http layer can replay it against the console API.\n//\n// SECURITY: this module is the only place that touches the secret material.\n// - Never log raw cookies / origins.\n// - Treat file IO errors as \"no session\" in user-facing commands.\n\nexport interface SessionUser {\n id: string;\n email: string;\n displayName?: string;\n}\n\nexport interface Session {\n schemaVersion: 2;\n user: SessionUser;\n // CDP-native cookie list from `Network.getAllCookies`. Treat as opaque\n // secret material outside the login/http code paths.\n cookies: readonly CdpCookie[];\n // Reserved for Playwright `storageState`-style `localStorage` snapshots;\n // empty until a feature needs it.\n origins: unknown[];\n capturedAt: string; // ISO-8601\n // Workspace context. Unset until the user runs `aitcc workspace use <id>`\n // or provides `--workspace` on first use. Writes are explicit — we never\n // guess a default (e.g. \"first workspace the user has access to\") because\n // a silent guess is exactly the class of bug that causes a deploy to land\n // in the wrong account.\n currentWorkspaceId?: number;\n}\n\n// Public-safe projection for `whoami` and other diagnostics.\nexport interface SessionSummary {\n user: SessionUser;\n capturedAt: string;\n}\n\nfunction summarize(session: Session): SessionSummary {\n return { user: session.user, capturedAt: session.capturedAt };\n}\n\n/**\n * Read the persisted session. Returns `null` when no session exists, when\n * the file is corrupt, or when the shape fails validation — each of those\n * emits a one-line warning on stderr for diagnostics.\n *\n * **`AITCC_SESSION` env precedence**: when the env var is set with a valid\n * blob (raw JSON or base64-encoded JSON), this function returns it directly\n * and never touches the session file. This is the read path for the CI\n * single-shot flow seeded by `aitcc auth export`. Invalid env content\n * falls back to the file with a one-shot warning so a typo doesn't\n * silently strand a CI run.\n *\n * **Side effect**: a v1 session file is transparently rewritten to v2 on\n * the first successful read of this process. This keeps read-only callers\n * (`whoami`, `workspace ls`) from stranding users on an old schema. If the\n * rewrite fails, we warn once per process and continue with the in-memory\n * v2 value so the calling command still succeeds. The env path performs\n * the same v1 → v2 upgrade in memory only — env mode never writes.\n */\nexport async function readSession(): Promise<Session | null> {\n const fromEnv = readSessionFromEnv();\n if (fromEnv !== undefined) return fromEnv;\n const path = sessionFilePath();\n let raw: string;\n try {\n raw = await readFile(path, 'utf8');\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code === 'ENOENT') return null;\n // Some other IO error — surface one-line diagnostic on stderr so the\n // user can tell \"permission denied\" from \"no session\". The command\n // still falls back to \"not logged in\" behaviour.\n process.stderr.write(`warning: could not read session file at ${path}: ${code ?? 'unknown'}\\n`);\n return null;\n }\n let rawParsed: unknown;\n try {\n rawParsed = JSON.parse(raw);\n } catch {\n // Malformed JSON — warn once, then fall back to \"not logged in\". The\n // user can re-run `aitcc login` to replace the broken file.\n process.stderr.write(`warning: session file at ${path} is corrupt and will be ignored\\n`);\n return null;\n }\n const schemaReason = validateSessionShape(rawParsed);\n if (schemaReason) {\n process.stderr.write(\n `warning: session file at ${path} ignored (${schemaReason}); re-run \\`aitcc login\\`\\n`,\n );\n return null;\n }\n // Post-validation: the shape is trusted. `schemaVersion` is now 1 or 2;\n // v1 files are transparently upgraded to v2 in memory. We best-effort\n // rewrite the file so long-lived v1-on-disk sessions eventually migrate\n // without requiring the user to run a write-shaped command. A failed\n // rewrite is non-fatal (the in-memory shape is correct) but we still\n // emit a one-line stderr warning once per process so a read-only mount\n // or permission issue does not silently persist. We await rather than\n // fire-and-forget so concurrent callers observe consistent on-disk state.\n const validated = rawParsed as { schemaVersion: 1 | 2 } & Omit<Session, 'schemaVersion'>;\n if (validated.schemaVersion === 1) {\n const upgraded: Session = { ...validated, schemaVersion: 2 };\n try {\n await writeSession(upgraded);\n } catch (err) {\n warnMigrationOnce(path, (err as NodeJS.ErrnoException).code);\n }\n return upgraded;\n }\n return validated as Session;\n}\n\n// One-shot latch so a failing migration doesn't spam stderr on every call\n// inside the same process. Users still get the diagnostic the first time.\nlet migrationWarned = false;\nfunction warnMigrationOnce(path: string, code: string | undefined): void {\n if (migrationWarned) return;\n migrationWarned = true;\n process.stderr.write(\n `warning: could not migrate session file at ${path} to schemaVersion 2: ${code ?? 'unknown'}\\n`,\n );\n}\n\n// One-shot latches so we don't spam stderr when many commands read the\n// session in a single process (e.g. ctx resolver + http call + diagnostics).\nlet envFallbackWarned = false;\nlet envWriteWarned = false;\n\n/**\n * Test-only: reset the one-shot stderr warning latches. Production\n * code should never call this — the latches exist precisely so a\n * single CLI invocation that reads the session many times only warns\n * once. The shared vitest process keeps modules alive across tests so\n * a latch tripped by an earlier case would suppress the warning the\n * later case asserts on.\n */\nexport function __resetSessionWarningsForTests(): void {\n envFallbackWarned = false;\n envWriteWarned = false;\n migrationWarned = false;\n}\n\n/**\n * Decode `AITCC_SESSION` env var into a `Session`. Returns:\n * - `undefined` when the env var is unset (caller falls through to file).\n * - `Session` when set + valid (caller uses it, ignores file).\n * - `null` when set but malformed/invalid; emits a one-shot stderr warning\n * and the caller falls through to the file path. We use `null` here as\n * \"tried env, gave up\" so the file path still runs — the goal is to\n * never strand a developer who accidentally exported garbage but has a\n * working session file underneath.\n *\n * The blob may be either raw JSON or base64-encoded JSON; we autodetect by\n * peeking at the decoded body for a leading `{`. This matches the symmetry\n * `auth export` ↔ `auth import` even when a user hand-edits a secret.\n */\nfunction readSessionFromEnv(): Session | null | undefined {\n const raw = process.env.AITCC_SESSION;\n if (!raw || raw.length === 0) return undefined;\n const decoded = decodeSessionBlob(raw);\n if (decoded === null) {\n warnEnvFallbackOnce('AITCC_SESSION env is set but not valid JSON');\n return undefined;\n }\n const reason = validateSessionShape(decoded);\n if (reason) {\n warnEnvFallbackOnce(`AITCC_SESSION env ignored (${reason})`);\n return undefined;\n }\n const validated = decoded as { schemaVersion: 1 | 2 } & Omit<Session, 'schemaVersion'>;\n // v1 → v2 upgrade in memory only — env mode is read-only by contract.\n return validated.schemaVersion === 1\n ? { ...validated, schemaVersion: 2 }\n : (validated as Session);\n}\n\n/**\n * True iff `AITCC_SESSION` is set AND parses+validates as a session.\n * The write/clear no-ops MUST gate on this rather than on\n * `process.env.AITCC_SESSION` truthiness alone — otherwise a corrupted\n * env blob silently swallows `workspace use` writes even though\n * `readSession()` correctly fell back to the file. Callers in this\n * module use it instead of inlining the check; `auth import` does NOT\n * call this (it should always write when invoked, see `forceWrite`).\n */\nfunction envSessionActive(): boolean {\n return readSessionFromEnv() !== undefined;\n}\n\nfunction warnEnvFallbackOnce(message: string): void {\n if (envFallbackWarned) return;\n envFallbackWarned = true;\n process.stderr.write(`warning: ${message}; falling back to session file\\n`);\n}\n\nfunction warnEnvWriteOnce(): void {\n if (envWriteWarned) return;\n envWriteWarned = true;\n process.stderr.write('warning: AITCC_SESSION env active — session updates not persisted\\n');\n}\n\n/**\n * Best-effort blob decoder shared by `readSessionFromEnv` and the\n * `auth import` command. Tries base64 first; if the result doesn't look\n * like JSON, falls back to treating the input as raw JSON. Returns the\n * parsed value on success, `null` on parse failure.\n */\nexport function decodeSessionBlob(raw: string): unknown {\n const trimmed = raw.trim();\n if (trimmed.length === 0) return null;\n // Direct JSON: cheap path, no base64 round-trip.\n if (trimmed.startsWith('{')) {\n try {\n return JSON.parse(trimmed);\n } catch {\n return null;\n }\n }\n // Try base64 → JSON.\n let decoded: string;\n try {\n decoded = Buffer.from(trimmed, 'base64').toString('utf8');\n } catch {\n decoded = '';\n }\n const decodedTrim = decoded.trim();\n if (decodedTrim.startsWith('{')) {\n try {\n return JSON.parse(decodedTrim);\n } catch {\n return null;\n }\n }\n return null;\n}\n\n/**\n * Validate a candidate session blob. Returns `null` on success or a\n * short reason string on failure. Exported so `auth import` can run the\n * exact same validation `readSession` uses, eliminating drift between\n * the two write paths.\n */\nexport function validateSessionBlob(input: unknown): string | null {\n return validateSessionShape(input);\n}\n\n/**\n * Normalise a validated blob to a `Session` (auto-upgrading v1 → v2).\n * Caller must have already passed the value through `validateSessionBlob`.\n */\nexport function normalizeValidatedSession(input: unknown): Session {\n const validated = input as { schemaVersion: 1 | 2 } & Omit<Session, 'schemaVersion'>;\n return validated.schemaVersion === 1\n ? { ...validated, schemaVersion: 2 }\n : (validated as Session);\n}\n\n// v1 → v2 migration: v1 files are still valid, we just treat the absent\n// `currentWorkspaceId` as \"no workspace selected yet\". The next write (e.g.\n// from `workspace use`) bumps the stored schemaVersion. The validator input\n// is `unknown` so we can inspect raw JSON without the TS type narrowing\n// away the v1 branch.\nfunction validateSessionShape(input: unknown): string | null {\n if (input === null || typeof input !== 'object') return 'root is not an object';\n const parsed = input as {\n schemaVersion?: unknown;\n user?: { id?: unknown; email?: unknown; displayName?: unknown };\n cookies?: unknown;\n origins?: unknown;\n capturedAt?: unknown;\n currentWorkspaceId?: unknown;\n };\n if (parsed.schemaVersion !== 1 && parsed.schemaVersion !== 2) {\n return `unknown schemaVersion ${String(parsed.schemaVersion)}`;\n }\n if (!parsed.user || typeof parsed.user.id !== 'string') return 'missing user.id';\n if (typeof parsed.user.email !== 'string') return 'missing user.email';\n if (parsed.user.displayName !== undefined && typeof parsed.user.displayName !== 'string') {\n return 'user.displayName has wrong type';\n }\n if (!Array.isArray(parsed.cookies)) return 'cookies is not an array';\n if (parsed.origins !== undefined && !Array.isArray(parsed.origins)) {\n return 'origins is not an array';\n }\n if (parsed.capturedAt !== undefined && typeof parsed.capturedAt !== 'string') {\n return 'capturedAt has wrong type';\n }\n if (parsed.currentWorkspaceId !== undefined) {\n const wid = parsed.currentWorkspaceId;\n if (typeof wid !== 'number' || !Number.isInteger(wid) || wid <= 0) {\n return 'currentWorkspaceId has wrong type';\n }\n }\n return null;\n}\n\nexport async function readSessionSummary(): Promise<SessionSummary | null> {\n const s = await readSession();\n return s ? summarize(s) : null;\n}\n\nexport interface WriteSessionOptions {\n /**\n * Force the write even when `AITCC_SESSION` env is active. Only\n * `auth import` should set this — the user explicitly asked us to\n * persist, and the env no-op would otherwise swallow that intent.\n * Bypasses the env mutation hack the previous revision relied on.\n */\n readonly forceWrite?: boolean;\n}\n\nexport async function writeSession(\n session: Session,\n options: WriteSessionOptions = {},\n): Promise<void> {\n // CI single-shot mode (`AITCC_SESSION` env active and valid) is\n // read-only by contract — silently creating or overwriting a file\n // would defeat the 0600 guarantee on hosts that don't expect a\n // persistent session and leave a stale blob behind on shared runners.\n // One-shot warn so a misuse (`workspace use` on a CI box) is visible\n // without spamming. Gated on the same \"set and valid\" predicate that\n // `readSession` uses so a corrupted env blob doesn't silently swallow\n // a legitimate write.\n if (!options.forceWrite && envSessionActive()) {\n warnEnvWriteOnce();\n return;\n }\n const dir = dirname(sessionFilePath());\n await mkdir(dir, { recursive: true, mode: 0o700 });\n await writeFile(sessionFilePath(), JSON.stringify(session, null, 2), {\n mode: 0o600,\n });\n // writeFile's mode only applies on creation; tighten existing files too.\n try {\n await chmod(sessionFilePath(), 0o600);\n } catch {\n // Windows / exotic FS: best-effort only.\n }\n}\n\n/**\n * Persist a new `currentWorkspaceId` on an existing session. Returns the\n * updated session, or `null` if there is no session to update (callers\n * should surface \"not logged in\" in that case).\n */\nexport async function setCurrentWorkspaceId(workspaceId: number): Promise<Session | null> {\n const session = await readSession();\n if (!session) return null;\n const updated: Session = { ...session, currentWorkspaceId: workspaceId };\n await writeSession(updated);\n return updated;\n}\n\nexport async function clearSession(): Promise<{ existed: boolean }> {\n // Env mode is read-only — pretend we cleared, but warn so the operator\n // knows the AITCC_SESSION secret in their pipeline still authenticates\n // the next command. (Symmetric with `writeSession` above; same \"set and\n // valid\" gate so a corrupted env blob doesn't suppress a legitimate\n // logout against the underlying file.)\n if (envSessionActive()) {\n warnEnvWriteOnce();\n return { existed: false };\n }\n try {\n await unlink(sessionFilePath());\n return { existed: true };\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code === 'ENOENT') return { existed: false };\n throw err;\n }\n}\n\nexport function sessionPathForDiagnostics(): string {\n return sessionFilePath();\n}\n\nexport function configDirForDiagnostics(): string {\n return configDir();\n}\n","// Map console-API `errorCode` values into actionable user messages.\n//\n// Two shapes coexist on the wire (envelope: `{resultType:'FAIL', error:\n// {errorCode, reason, ...}}`):\n//\n// 1. Numeric strings — `\"4046\"`, `\"4032\"`, `\"4010\"`. Catalogued in\n// `docs/api/_error-codes.md`. CLI does not rewrite these; the raw\n// `error.reason` is already a Korean sentence the console UI shows\n// verbatim, and we surface the same string so behaviour matches what\n// a user sees in the browser. This module returns `null` for that\n// case so callers fall back to the existing message.\n//\n// 2. Prefix-form (`<camelCaseDomain>.<PascalCaseReason>`) — e.g.\n// `\"miniApp.InvalidTitle\"` from `POST /mini-app/review`. Discovered\n// during sdk-example#39 dog-food. The accompanying `reason` is\n// sometimes generic (\"앱 영문 이름은 영어, 숫자, 공백, 콜론(:)만 사용\n// 가능해요\") and the LLM consumer in agent-plugin has no way to know\n// the rule from the code alone. For the small set we've directly\n// observed, surface a single actionable line. Unknown prefix codes\n// pass through with the code embedded in the message so a downstream\n// reader at least sees the dotted identifier rather than just a raw\n// reason — they get added to the catalog when next observed.\n//\n// The numeric/prefix split mirrors the table layout in\n// `docs/api/_error-codes.md` exactly so the doc and the code stay in sync;\n// `_error-codes.md` is the source of truth for which prefix codes are\n// known.\n\n// `<camelCaseDomain>.<PascalCaseReason>`. The reason segment requires at\n// least two characters (one uppercase + one more) — every prefix code we\n// have observed in console responses has a multi-character reason, and\n// matching a single uppercase letter would also accept things like\n// `module.X` that look more like internal logger tags than user-visible\n// errorCodes. If a real one-character reason ever shows up we'll widen\n// this to `[a-zA-Z0-9]*`.\nconst PREFIX_ERROR_CODE_PATTERN = /^[a-z][a-zA-Z0-9]*\\.[A-Z][a-zA-Z0-9]+$/;\n\nexport function isPrefixFormErrorCode(code: string): boolean {\n return PREFIX_ERROR_CODE_PATTERN.test(code);\n}\n\n// Known prefix codes mapped to a one-line action sentence. Keep entries\n// terse (no leading bullet, no trailing period) — the caller wraps them\n// into a longer string with the raw code/reason.\n//\n// Only add an entry once the code has been observed end-to-end with a\n// known fix. Speculation belongs in a TODO, not here — the unknown-prefix\n// path already surfaces the code so a user can search the catalog.\nconst KNOWN_PREFIX_MESSAGES: Record<string, string> = {\n 'miniApp.InvalidTitle':\n 'titleKo violates the server rule (≤ 10 code points excluding spaces, only Korean/English letters, digits, spaces, and \":·?\"). Edit `titleKo` in aitcc.yaml.',\n 'miniApp.InvalidTitleEn':\n 'titleEn violates the server rule (≤ 15 code points excluding spaces, only English letters/digits/spaces/`:·?`, and each word title-case — first letter uppercase, rest lowercase; `AITC`-style all-caps tokens are rejected). Edit `titleEn` in aitcc.yaml.',\n};\n\nexport interface DescribeApiErrorInput {\n /** Raw `error.errorCode` from the Toss envelope (may be any string). */\n readonly errorCode: string | undefined;\n /** Raw `error.reason` from the Toss envelope. */\n readonly reason: string | undefined;\n /**\n * The fallback message the caller would have used absent prefix mapping\n * (typically `TossApiError.message`). Returned verbatim when the code is\n * empty or numeric so existing behaviour is preserved.\n */\n readonly fallback: string;\n}\n\n/**\n * Compose the user-visible message for a Toss API error.\n *\n * Behaviour by code shape:\n *\n * - **numeric / null / non-matching shape**: returns `fallback` verbatim.\n * This preserves the pre-existing `TossApiError.message` text byte-for-\n * byte, so commands that already surface things like\n * `\"Toss API error 4046: 검수중인 요청이 있어 검수요청을 할 수 없어요 (HTTP 400)\"`\n * keep doing exactly that.\n *\n * - **known prefix code**: returns the mapped action sentence, with the\n * server `reason` appended in a `(server reason: …)` clause when\n * present. The `fallback` is intentionally NOT included — the mapped\n * sentence is self-contained and already names the field/file the user\n * needs to edit, so prepending the `Toss API error …` envelope text\n * would just bury the action.\n *\n * - **unknown prefix code**: returns `fallback` verbatim. `TossApiError`'s\n * own template already embeds the dotted identifier\n * (`Toss API error <code>: <reason> (HTTP <status>)`), so the code is\n * discoverable in logs without us double-wrapping it.\n *\n * `errorCode` itself is left to the caller — this only shapes `message`.\n */\nexport function describeApiError(input: DescribeApiErrorInput): string {\n const { errorCode, reason, fallback } = input;\n if (!errorCode) return fallback;\n if (!isPrefixFormErrorCode(errorCode)) return fallback;\n const mapped = KNOWN_PREFIX_MESSAGES[errorCode];\n if (mapped !== undefined) {\n const reasonSuffix = reason ? ` (server reason: ${reason})` : '';\n return `${mapped}${reasonSuffix}`;\n }\n // Unknown prefix code: defer to the caller's fallback. `TossApiError`\n // formats its `message` as `Toss API error <code>: <reason> (HTTP …)`,\n // which already exposes the dotted identifier — wrapping it in\n // `(<code>) …` here just duplicated the code without adding signal.\n return fallback;\n}\n","import { access, readFile, writeFile } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\nimport { isMap, parseDocument, parse as parseYaml } from 'yaml';\n\n// Project-level context (workspace + active mini-app) discovered by walking\n// from the cwd up to the nearest git repo root. Read by every app-scoped\n// command so the user does not have to repeat `--workspace` and `<appId>`\n// on each invocation. Distinct from `app-manifest.ts`: the manifest is the\n// `register`/`update` payload contract, while the project context is the\n// resolver input that picks *which* app/workspace a given command targets.\n\nconst DEFAULT_NAMES = ['aitcc.yaml', 'aitcc.json'] as const;\nconst MAX_HOPS = 32;\n\nexport interface ProjectContext {\n readonly workspaceId?: number;\n readonly miniAppId?: number;\n /** Absolute path of the file the context was read from (for diagnostics). */\n readonly source: string;\n}\n\nexport class ProjectContextError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'ProjectContextError';\n }\n}\n\nasync function fileExists(path: string): Promise<boolean> {\n try {\n await access(path);\n return true;\n } catch {\n return false;\n }\n}\n\nasync function findContextFile(startDir: string): Promise<string | null> {\n let dir = startDir;\n const home = process.env.HOME ?? process.env.USERPROFILE;\n for (let i = 0; i < MAX_HOPS; i++) {\n for (const name of DEFAULT_NAMES) {\n const candidate = join(dir, name);\n if (await fileExists(candidate)) return candidate;\n }\n // Stop walking when we hit a git repo boundary. `.git` may be a\n // directory (normal repo) or a file (submodule/worktree); `access`\n // succeeds for both.\n if (await fileExists(join(dir, '.git'))) return null;\n if (home && dir === home) return null;\n const parent = dirname(dir);\n if (parent === dir) return null;\n dir = parent;\n }\n return null;\n}\n\nfunction pickPositiveInt(input: Record<string, unknown>, key: string): number | undefined {\n const v = input[key];\n if (v === undefined || v === null) return undefined;\n if (typeof v !== 'number' || !Number.isInteger(v) || v <= 0) return undefined;\n return v;\n}\n\n/**\n * Walk from `cwd` upward looking for `aitcc.yaml`/`aitcc.json`. Returns the\n * parsed `ProjectContext` of the first match, or `null` when no file is\n * found. Walks halt at the enclosing `.git` boundary or `$HOME`, whichever\n * comes first.\n *\n * Throws `ProjectContextError` when a file is found but cannot be parsed —\n * a present-but-broken config is more useful as a hard error than as a\n * silent miss. Unknown / mistyped fields (`workspaceId`, `miniAppId` not\n * being a positive integer) are dropped silently while preserving `source`.\n */\nexport async function findProjectContext(cwd: string): Promise<ProjectContext | null> {\n const path = await findContextFile(cwd);\n if (!path) return null;\n const raw = await readFile(path, 'utf8');\n let parsed: unknown;\n try {\n parsed = path.toLowerCase().endsWith('.json') ? JSON.parse(raw) : parseYaml(raw);\n } catch (err) {\n const detail = err instanceof Error ? err.message : String(err);\n throw new ProjectContextError(`failed to parse project context at ${path}: ${detail}`);\n }\n if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {\n throw new ProjectContextError(`project context at ${path} is not a mapping`);\n }\n const obj = parsed as Record<string, unknown>;\n const workspaceId = pickPositiveInt(obj, 'workspaceId');\n const miniAppId = pickPositiveInt(obj, 'miniAppId');\n return {\n ...(workspaceId !== undefined ? { workspaceId } : {}),\n ...(miniAppId !== undefined ? { miniAppId } : {}),\n source: path,\n };\n}\n\nexport type WriteMiniAppIdOutcome =\n | { readonly status: 'written'; readonly path: string }\n | { readonly status: 'unchanged'; readonly path: string };\n\n/**\n * Persist `miniAppId` into an existing project-context file at `path`.\n *\n * - YAML files are updated via `parseDocument` so user comments, key\n * ordering, and quoting style survive the round-trip. Only the\n * `miniAppId` scalar is touched (added if absent, replaced if present).\n * - JSON files are parsed, mutated, and re-serialized with the indent the\n * file already used (detected from the first indented line, defaulting\n * to 2). The trailing newline is preserved if it was present.\n * - When the file already pins the same `miniAppId`, the write is a\n * no-op and `status` is `\"unchanged\"` so callers can suppress the\n * diagnostic line.\n *\n * Throws `ProjectContextError` when the file is missing, unparseable, or\n * not a top-level mapping. Caller is responsible for the \"no project\n * file at all\" case (`findProjectContext` returns `null`).\n */\nexport async function writeProjectMiniAppId(\n path: string,\n miniAppId: number,\n): Promise<WriteMiniAppIdOutcome> {\n if (!Number.isInteger(miniAppId) || miniAppId <= 0) {\n throw new ProjectContextError(`refusing to write non-positive-integer miniAppId: ${miniAppId}`);\n }\n let raw: string;\n try {\n raw = await readFile(path, 'utf8');\n } catch (err) {\n const detail = err instanceof Error ? err.message : String(err);\n throw new ProjectContextError(`failed to read project context at ${path}: ${detail}`);\n }\n const isJson = path.toLowerCase().endsWith('.json');\n const next = isJson ? rewriteJson(path, raw, miniAppId) : rewriteYaml(path, raw, miniAppId);\n if (next === null) return { status: 'unchanged', path };\n await writeFile(path, next, 'utf8');\n return { status: 'written', path };\n}\n\nfunction rewriteYaml(path: string, raw: string, miniAppId: number): string | null {\n const doc = parseDocument(raw);\n if (doc.errors.length > 0) {\n throw new ProjectContextError(\n `failed to parse project context at ${path}: ${doc.errors[0]?.message ?? 'parse error'}`,\n );\n }\n if (!isMap(doc.contents)) {\n throw new ProjectContextError(`project context at ${path} is not a mapping`);\n }\n const existing = doc.get('miniAppId');\n if (typeof existing === 'number' && existing === miniAppId) return null;\n doc.set('miniAppId', miniAppId);\n return doc.toString();\n}\n\nfunction rewriteJson(path: string, raw: string, miniAppId: number): string | null {\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (err) {\n const detail = err instanceof Error ? err.message : String(err);\n throw new ProjectContextError(`failed to parse project context at ${path}: ${detail}`);\n }\n if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {\n throw new ProjectContextError(`project context at ${path} is not a mapping`);\n }\n const obj = parsed as Record<string, unknown>;\n if (obj.miniAppId === miniAppId) return null;\n obj.miniAppId = miniAppId;\n const indent = detectJsonIndent(raw);\n const trailing = raw.endsWith('\\n') ? '\\n' : '';\n return JSON.stringify(obj, null, indent) + trailing;\n}\n\n// Returns the original file's indentation token so JSON.stringify can\n// reproduce it (number = spaces, string = literal token, e.g. '\\t').\n// `0` keeps a single-line / compact file compact instead of expanding\n// it to multi-line on a one-key edit. Default is two spaces, matching\n// the format examples in README and existing repo style.\nfunction detectJsonIndent(raw: string): number | string {\n if (!raw.includes('\\n')) return 0;\n const match = raw.match(/\\n([ \\t]+)\\S/);\n const token = match?.[1];\n if (!token) return 2;\n if (token.includes('\\t')) return '\\t';\n return token.length;\n}\n","import { describeApiError } from '../api/error-messages.js';\nimport { NetworkError, TossApiError } from '../api/http.js';\nimport { findProjectContext, type ProjectContext } from '../config/project-context.js';\nimport { ExitCode } from '../exit.js';\nimport { exitAfterFlush } from '../flush.js';\nimport { readSession, type Session, sessionPathForDiagnostics } from '../session.js';\n\n// Shared output helpers used by every session-scoped subcommand\n// (`workspace`, `app`, `members`, `keys`, and the in-flight `deploy`/`logs`).\n// Kept in one place so all commands agree on the `--json` contract — one\n// line, trailing \\n, stdout for structured output, stderr for diagnostics.\n//\n// Cross-cutting failure shapes (emitted from every session/app/workspace-\n// scoped command — per-command `--json contract` blocks may add their own\n// reasons on top, but these are universal):\n//\n// Auth/network/API:\n// { ok: true, authenticated: false } exit 10\n// { ok: false, reason: 'network-error', message } exit 11\n// { ok: false, reason: 'api-error', status?, errorCode?, message } exit 17\n//\n// Context resolution (any command that goes through\n// `resolveWorkspaceContext` / `resolveAppOrFail`):\n// { ok: false, reason: 'invalid-id', message } exit 2\n// (--workspace value, positional <appId>, or --app value malformed)\n// { ok: false, reason: 'invalid-env', message } exit 2\n// (AITCC_WORKSPACE or AITCC_APP env var contains a non-positive-int)\n// { ok: false, reason: 'no-workspace-selected' } exit 2\n// (no flag/env/yaml/session source supplied a workspace id)\n//\n// App-scoped commands additionally:\n// { ok: false, reason: 'missing-app-id', message } exit 2\n// (workspace resolved but no source supplied a miniApp id)\n//\n// See any per-command `--json contract` block (e.g. `commands/workspace.ts`)\n// for the success-shape specific to that command and any command-specific\n// reasons stacked on top of these.\n\nexport interface NotAuthenticatedPayload {\n readonly ok: true;\n readonly authenticated: false;\n readonly reason?: 'session-expired';\n}\n\nexport function emitJson(payload: unknown): void {\n process.stdout.write(`${JSON.stringify(payload)}\\n`);\n}\n\nexport function emitNotAuthenticated(json: boolean, reason?: 'session-expired'): void {\n if (json) {\n // `exactOptionalPropertyTypes` forbids `reason: undefined`, so we omit\n // the key entirely when we don't have a value — hence the branch\n // rather than a single object literal.\n const payload: NotAuthenticatedPayload = reason\n ? { ok: true, authenticated: false, reason }\n : { ok: true, authenticated: false };\n emitJson(payload);\n } else {\n process.stderr.write(\n reason === 'session-expired'\n ? 'Session is no longer valid. Run `aitcc login` again.\\n'\n : 'Not logged in. Run `aitcc login` to start a session.\\n',\n );\n process.stderr.write(`Session file checked: ${sessionPathForDiagnostics()}\\n`);\n }\n}\n\nexport function emitNetworkError(json: boolean, message: string): void {\n if (json) {\n emitJson({ ok: false, reason: 'network-error', message });\n } else {\n process.stderr.write(`Network error reaching the console API: ${message}.\\n`);\n }\n}\n\nexport function emitApiError(\n json: boolean,\n message: string,\n details?: { status?: number; errorCode?: string },\n): void {\n if (json) {\n emitJson({\n ok: false,\n reason: 'api-error',\n ...(details?.status !== undefined ? { status: details.status } : {}),\n ...(details?.errorCode !== undefined ? { errorCode: details.errorCode } : {}),\n message,\n });\n } else {\n process.stderr.write(`Unexpected error: ${message}\\n`);\n }\n}\n\n/**\n * Shared auth/network/api dispatch. Every session-scoped command's\n * `catch (err)` block boils down to the same sequence: TossApiError\n * (auth → exit 10, otherwise → exit 17 with status + errorCode),\n * NetworkError (exit 11), fallback (exit 17 with just a message).\n * Exists so we get a single source of truth for the api-error JSON\n * shape — previously each command duplicated the if/else ladder and\n * `register` diverged (it exposed `status`/`errorCode` that the others\n * didn't) until this extraction lined them up.\n *\n * Returns `Promise<void>` but never returns at runtime: every branch\n * awaits `exitAfterFlush` which calls `process.exit`.\n */\nexport async function emitFailureFromError(json: boolean, err: unknown): Promise<void> {\n if (err instanceof TossApiError && err.isAuthError) {\n emitNotAuthenticated(json, 'session-expired');\n return exitAfterFlush(ExitCode.NotAuthenticated);\n }\n if (err instanceof TossApiError) {\n const message = describeApiError({\n errorCode: err.errorCode,\n reason: err.reason,\n fallback: err.message,\n });\n emitApiError(json, message, { status: err.status, errorCode: err.errorCode });\n return exitAfterFlush(ExitCode.ApiError);\n }\n if (err instanceof NetworkError) {\n emitNetworkError(json, err.message);\n return exitAfterFlush(ExitCode.NetworkError);\n }\n emitApiError(json, (err as Error).message);\n return exitAfterFlush(ExitCode.ApiError);\n}\n\n// Parse a CLI-provided workspace id strictly: only the form `^[1-9]\\d*$`\n// is accepted. `Number.parseInt('36577x', 10)` returns 36577, so the CLI\n// would otherwise silently accept `workspace use 36577x` and persist the\n// wrong thing on a typo. Returning `null` triggers the caller's usage-error\n// path. Exported so unit tests can guard against \"just use parseInt\"\n// simplification regressions.\nexport function parsePositiveInt(raw: string): number | null {\n if (!/^[1-9]\\d*$/.test(raw)) return null;\n const n = Number.parseInt(raw, 10);\n return Number.isSafeInteger(n) ? n : null;\n}\n\n/**\n * Boilerplate wrapper for any workspace-scoped command (`app ls`,\n * `members ls`, `keys ls`, ...). Loads the session, resolves the workspace\n * id through the full priority chain (`--workspace` flag → `AITCC_WORKSPACE`\n * env → `aitcc.yaml` → persisted selection), and handles the common failure\n * branches (`no session`, `invalid id`, `invalid env`, `no workspace\n * selected`). On success, the caller gets the session + resolved id +\n * source bookkeeping back so it can `printContextHeader` consistently.\n *\n * The return type is `Promise<... | null>` but the `null` branch is never\n * observed at runtime: every failure path `await`s `exitAfterFlush` which\n * calls `process.exit(...)` and doesn't return. The `| null` is a type-\n * level handshake that forces callers to add `if (!ctx) return;`, keeping\n * the bail-out readable.\n */\nexport async function resolveWorkspaceContext(args: {\n workspace?: string | undefined;\n json: boolean;\n}): Promise<\n | (AppContext & {\n session: Session;\n })\n | null\n> {\n const session = await readSession();\n if (!session) {\n emitNotAuthenticated(args.json);\n await exitAfterFlush(ExitCode.NotAuthenticated);\n return null;\n }\n\n let flagWorkspaceId: number | undefined;\n if (args.workspace) {\n const raw = String(args.workspace);\n const parsed = parsePositiveInt(raw);\n if (parsed === null) {\n const message = `--workspace must be a positive integer (got ${raw})`;\n if (args.json) emitJson({ ok: false, reason: 'invalid-id', message });\n else process.stderr.write(`${message}\\n`);\n await exitAfterFlush(ExitCode.Usage);\n return null;\n }\n flagWorkspaceId = parsed;\n }\n\n let app: AppContext;\n try {\n app = await resolveAppContext({\n ...(flagWorkspaceId !== undefined ? { flagWorkspaceId } : {}),\n ...(session.currentWorkspaceId !== undefined\n ? { sessionWorkspaceId: session.currentWorkspaceId }\n : {}),\n });\n } catch (err) {\n await emitAppContextErrorAndExit(args.json, err);\n return null;\n }\n\n return { session, ...app };\n}\n\n/**\n * Session-only sibling of `resolveWorkspaceContext` for commands that\n * don't need a workspace id (notices come from a shared Toss workspace,\n * whoami is self-scoped). Same \"exits on miss, returns null to force\n * `if (!session) return`\" pattern.\n */\nexport async function requireSession(json: boolean): Promise<Session | null> {\n const session = await readSession();\n if (!session) {\n emitNotAuthenticated(json);\n await exitAfterFlush(ExitCode.NotAuthenticated);\n return null;\n }\n return session;\n}\n\nexport type ContextSource = 'flag' | 'env' | 'yaml' | 'session';\n\nexport interface AppContext {\n readonly workspaceId: number;\n readonly miniAppId?: number;\n readonly workspaceSource: ContextSource;\n readonly miniAppIdSource?: ContextSource;\n /** Path of the yaml that contributed to the resolution, if any. */\n readonly projectFile?: string;\n}\n\nexport interface ResolveAppContextInput {\n /** Value from `--workspace <id>` (already parsed by the command). */\n readonly flagWorkspaceId?: number;\n /** Value from a positional `<appId>` (or equivalent flag). */\n readonly flagMiniAppId?: number;\n /** Persisted `currentWorkspaceId`, if a session is loaded. */\n readonly sessionWorkspaceId?: number;\n /** Override for tests; defaults to `process.cwd()`. */\n readonly cwd?: string;\n}\n\nexport class AppContextError extends Error {\n readonly reason: 'invalid-env' | 'no-workspace-selected';\n constructor(reason: 'invalid-env' | 'no-workspace-selected', message: string) {\n super(message);\n this.name = 'AppContextError';\n this.reason = reason;\n }\n}\n\nfunction readEnvPositiveInt(name: string): number | undefined {\n const raw = process.env[name];\n if (raw === undefined || raw === '') return undefined;\n const parsed = parsePositiveInt(raw);\n if (parsed === null) {\n throw new AppContextError('invalid-env', `${name} must be a positive integer (got ${raw})`);\n }\n return parsed;\n}\n\n/**\n * Resolve the app/workspace context for a command invocation by combining\n * flags, env vars, an optional `aitcc.yaml`, and the persisted session.\n *\n * Priority chains (highest first):\n * workspace: flag > env(AITCC_WORKSPACE) > yaml(workspaceId) > session.currentWorkspaceId\n * miniApp: flag > env(AITCC_APP) > yaml(miniAppId)\n *\n * When the workspace comes from `flag`, any `miniAppId` sourced from\n * `yaml` is dropped — the flag explicitly redirects the workspace, so a\n * yaml `miniAppId` may belong to a different workspace and is unsafe to\n * carry forward. We never fetch the API to verify; that is the caller's\n * job if it matters.\n *\n * Throws `AppContextError('no-workspace-selected', ...)` when no source\n * provides a `workspaceId`. The caller decides how to surface it (most\n * commands map it to `{ ok: false, reason: 'no-workspace-selected' }`\n * with exit code 2 — see `resolveWorkspaceContext`).\n */\nexport async function resolveAppContext(input: ResolveAppContextInput): Promise<AppContext> {\n const cwd = input.cwd ?? process.cwd();\n\n let project: ProjectContext | null = null;\n try {\n project = await findProjectContext(cwd);\n } catch {\n // A broken yaml shouldn't take down commands that don't actually need\n // it (the user may have flag-provided everything). Treat as \"no\n // project context\"; the dedicated manifest loader surfaces a precise\n // error from the commands that do need to read it.\n project = null;\n }\n\n const envWorkspace = readEnvPositiveInt('AITCC_WORKSPACE');\n const envMiniApp = readEnvPositiveInt('AITCC_APP');\n\n let workspaceId: number | undefined;\n let workspaceSource: ContextSource | undefined;\n if (input.flagWorkspaceId !== undefined) {\n workspaceId = input.flagWorkspaceId;\n workspaceSource = 'flag';\n } else if (envWorkspace !== undefined) {\n workspaceId = envWorkspace;\n workspaceSource = 'env';\n } else if (project?.workspaceId !== undefined) {\n workspaceId = project.workspaceId;\n workspaceSource = 'yaml';\n } else if (input.sessionWorkspaceId !== undefined) {\n workspaceId = input.sessionWorkspaceId;\n workspaceSource = 'session';\n }\n\n if (workspaceId === undefined || workspaceSource === undefined) {\n throw new AppContextError(\n 'no-workspace-selected',\n 'No workspace selected. Pass `--workspace <id>`, set AITCC_WORKSPACE, add `workspaceId` to aitcc.yaml, or run `aitcc workspace use <id>`.',\n );\n }\n\n let miniApp: { miniAppId: number; miniAppIdSource: ContextSource } | undefined;\n if (input.flagMiniAppId !== undefined) {\n miniApp = { miniAppId: input.flagMiniAppId, miniAppIdSource: 'flag' };\n } else if (envMiniApp !== undefined) {\n miniApp = { miniAppId: envMiniApp, miniAppIdSource: 'env' };\n } else if (project?.miniAppId !== undefined && workspaceSource !== 'flag') {\n miniApp = { miniAppId: project.miniAppId, miniAppIdSource: 'yaml' };\n }\n\n return {\n workspaceId,\n workspaceSource,\n ...(miniApp !== undefined ? miniApp : {}),\n ...(project !== null ? { projectFile: project.source } : {}),\n };\n}\n\n/**\n * Common failure-emitter for `AppContextError` produced by the priority\n * chain. Workspace-scoped (`resolveWorkspaceContext`) and app-scoped\n * (`resolveAppOrFail`) callers both feed any thrown `AppContextError`\n * through here so the `--json` shape and exit code stay aligned across\n * the two entry points.\n */\nasync function emitAppContextErrorAndExit(json: boolean, err: unknown): Promise<void> {\n if (err instanceof AppContextError && err.reason === 'invalid-env') {\n if (json) emitJson({ ok: false, reason: 'invalid-env', message: err.message });\n else process.stderr.write(`${err.message}\\n`);\n await exitAfterFlush(ExitCode.Usage);\n return;\n }\n if (err instanceof AppContextError && err.reason === 'no-workspace-selected') {\n if (json) emitJson({ ok: false, reason: 'no-workspace-selected' });\n else process.stderr.write(`${err.message}\\n`);\n await exitAfterFlush(ExitCode.Usage);\n return;\n }\n throw err;\n}\n\n/**\n * Boilerplate wrapper for app-scoped commands (`app show`, `app status`,\n * `app bundles ls`, ...). Builds on `resolveWorkspaceContext` — every\n * app-scoped command also loads the session and resolves the workspace\n * — and additionally accepts a positional `<appId>` value (or per-command\n * `--app` flag value) that feeds into the miniApp priority chain.\n *\n * `args.id` is the raw positional value as citty surfaces it. We accept\n * `unknown` because citty hands back `string | undefined`, but tests\n * sometimes pass `number` literals; guarding with `String(...)` matches\n * the parsing the per-command early-validation used to do.\n *\n * On a malformed positional we emit the same `invalid-id` shape that the\n * pre-PR-1b commands used so agent-plugin's parsing is unchanged.\n *\n * Returns `null` after exiting on every failure branch, mirroring\n * `resolveWorkspaceContext`. Callers must `if (!ctx) return;`.\n */\nexport async function resolveAppOrFail(args: {\n workspace?: string | undefined;\n /** Raw positional/flag value for the mini-app id. */\n appIdRaw?: unknown;\n /** Field name to surface in error messages (`id` or `app`). */\n appIdField?: 'id' | 'app';\n json: boolean;\n}): Promise<\n | (AppContext & {\n session: Session;\n })\n | null\n> {\n // Parse the explicit positional/flag first so a typo is rejected before\n // we let yaml/env quietly take over — `aitcc app status 42x` should not\n // silently fall through to the yaml miniAppId.\n let flagMiniAppId: number | undefined;\n const raw = args.appIdRaw;\n if (raw !== undefined && raw !== null && raw !== '') {\n const str = typeof raw === 'string' ? raw : String(raw);\n const parsed = parsePositiveInt(str);\n if (parsed === null) {\n const field = args.appIdField === 'app' ? '--app' : 'app id';\n const message = `${field} must be a positive integer (got ${JSON.stringify(str)})`;\n if (args.json) emitJson({ ok: false, reason: 'invalid-id', message });\n else process.stderr.write(`${message}\\n`);\n await exitAfterFlush(ExitCode.Usage);\n return null;\n }\n flagMiniAppId = parsed;\n }\n\n const session = await readSession();\n if (!session) {\n emitNotAuthenticated(args.json);\n await exitAfterFlush(ExitCode.NotAuthenticated);\n return null;\n }\n\n let flagWorkspaceId: number | undefined;\n if (args.workspace) {\n const wsRaw = String(args.workspace);\n const parsed = parsePositiveInt(wsRaw);\n if (parsed === null) {\n const message = `--workspace must be a positive integer (got ${wsRaw})`;\n if (args.json) emitJson({ ok: false, reason: 'invalid-id', message });\n else process.stderr.write(`${message}\\n`);\n await exitAfterFlush(ExitCode.Usage);\n return null;\n }\n flagWorkspaceId = parsed;\n }\n\n let app: AppContext;\n try {\n app = await resolveAppContext({\n ...(flagWorkspaceId !== undefined ? { flagWorkspaceId } : {}),\n ...(flagMiniAppId !== undefined ? { flagMiniAppId } : {}),\n ...(session.currentWorkspaceId !== undefined\n ? { sessionWorkspaceId: session.currentWorkspaceId }\n : {}),\n });\n } catch (err) {\n await emitAppContextErrorAndExit(args.json, err);\n return null;\n }\n\n return { session, ...app };\n}\n\n/**\n * Emit the standard \"miniApp id required\" failure for app-scoped commands\n * whose context resolved a workspace but no miniApp. Returns `null` after\n * exiting so the caller can `if (!miniAppId) return;` without an extra\n * branch. Lifted into `_shared.ts` so every Group A command emits the\n * same JSON shape.\n */\nexport async function requireMiniAppId(ctx: AppContext, json: boolean): Promise<number | null> {\n if (ctx.miniAppId !== undefined) return ctx.miniAppId;\n const message =\n 'app id required (provide as argument, --app flag, AITCC_APP env, or `miniAppId` in aitcc.yaml)';\n if (json) emitJson({ ok: false, reason: 'missing-app-id', message });\n else process.stderr.write(`${message}\\n`);\n await exitAfterFlush(ExitCode.Usage);\n return null;\n}\n\nfunction describeSource(\n source: ContextSource,\n kind: 'workspace' | 'app',\n projectFile: string | undefined,\n): string {\n if (source === 'flag') return kind === 'workspace' ? '(from --workspace)' : '(from --app)';\n if (source === 'env')\n return kind === 'workspace' ? '(from $AITCC_WORKSPACE)' : '(from $AITCC_APP)';\n if (source === 'yaml') {\n // Use the simple file label (`aitcc.yaml` / `aitcc.json`) rather than\n // the absolute path so the header stays short on deep checkouts. The\n // exact path is in `ctx.projectFile` for callers that want it.\n if (projectFile !== undefined) {\n const slash = Math.max(projectFile.lastIndexOf('/'), projectFile.lastIndexOf('\\\\'));\n const base = slash >= 0 ? projectFile.slice(slash + 1) : projectFile;\n return `(from ${base})`;\n }\n return '(from aitcc.yaml)';\n }\n return '(from session)';\n}\n\n/**\n * Print the one-line `[workspace: … · app: …]` context header to stderr\n * before a command emits its real output. Suppressed under `--json` so\n * machine-readable callers see only the structured stdout. Stays on\n * stderr (not stdout) so it never lands in pipes that grep stdout, and\n * stays out of TTY-only branches so CI logs still record what context\n * was used.\n */\nexport function printContextHeader(ctx: AppContext, opts: { json: boolean }): void {\n if (opts.json) return;\n const wsTag = describeSource(ctx.workspaceSource, 'workspace', ctx.projectFile);\n let line = `[workspace: ${ctx.workspaceId} ${wsTag}`;\n if (ctx.miniAppId !== undefined && ctx.miniAppIdSource !== undefined) {\n const appTag = describeSource(ctx.miniAppIdSource, 'app', ctx.projectFile);\n line += ` · app: ${ctx.miniAppId} ${appTag}`;\n }\n line += ']\\n';\n process.stderr.write(line);\n}\n","import { readFile } from 'node:fs/promises';\nimport { unzipSync } from 'fflate';\n\n// A `.ait` bundle today comes in two on-disk shapes, and we have to read\n// both. `@apps-in-toss/web-framework`'s build toolchain packs via\n// `@apps-in-toss/ait-format`, which writes the modern shape:\n//\n// [0..8) MAGIC = ASCII \"AITBUNDL\"\n// [8..12) formatVersion : uint32 BE\n// [12..20) bundleLen : uint64 BE\n// [20..20+bundleLen) protobuf-encoded AITBundle (deploymentId, appName,\n// permissions, …)\n// next 8 bytes zipLen : uint64 BE\n// next zipLen zip blob (fflate-compatible)\n// next 8 bytes sigLen : uint64 BE (may be 0)\n// next sigLen signature (optional)\n//\n// Legacy builds just emit a plain zip whose root contains `app.json`,\n// and the console's own uploader still accepts that shape (it branches\n// on the first 8 bytes: `AITBUNDL` → AIT, `PK\\x03\\x04` → ZIP). We\n// replicate the same branch so `aitcc app deploy` works on either\n// toolchain version without the user having to know which one their\n// build produced.\n//\n// For the AIT path we only need `app.json._metadata.deploymentId`\n// (really: the protobuf `deploymentId`, which the console mirrors into\n// app.json). The header's bundle protobuf carries it directly, so we\n// read it from the header instead of cracking the inner zip — cheaper\n// and avoids depending on `@apps-in-toss/ait-format` (which would pull\n// in protobufjs + long just to read two string fields). A tiny inline\n// wire-format reader for protobuf fields 2 (deploymentId) and 3\n// (appName) is enough.\n\nexport type AitBundleErrorReason =\n | 'file-unreadable'\n | 'unrecognized-format'\n | 'invalid-ait'\n | 'invalid-zip'\n | 'missing-app-json'\n | 'invalid-app-json'\n | 'missing-deployment-id';\n\nexport class AitBundleError extends Error {\n readonly path: string;\n readonly reason: AitBundleErrorReason;\n\n constructor(args: { path: string; reason: AitBundleErrorReason; message: string }) {\n super(args.message);\n this.name = 'AitBundleError';\n this.path = args.path;\n this.reason = args.reason;\n }\n}\n\nexport type AitBundleFormat = 'ait' | 'zip';\n\nexport interface AitBundleInfo {\n readonly deploymentId: string;\n readonly bytes: Uint8Array;\n readonly format: AitBundleFormat;\n}\n\n// ASCII \"AITBUNDL\"\nconst AIT_MAGIC = new Uint8Array([0x41, 0x49, 0x54, 0x42, 0x55, 0x4e, 0x44, 0x4c]);\n// Standard zip local file header signature \"PK\\x03\\x04\"\nconst ZIP_MAGIC = new Uint8Array([0x50, 0x4b, 0x03, 0x04]);\n\nfunction startsWith(bytes: Uint8Array, prefix: Uint8Array): boolean {\n if (bytes.length < prefix.length) return false;\n for (let i = 0; i < prefix.length; i++) {\n if (bytes[i] !== prefix[i]) return false;\n }\n return true;\n}\n\nexport function detectBundleFormat(bytes: Uint8Array): AitBundleFormat | 'unknown' {\n if (startsWith(bytes, AIT_MAGIC)) return 'ait';\n if (startsWith(bytes, ZIP_MAGIC)) return 'zip';\n return 'unknown';\n}\n\n/**\n * Read the `.ait` at `path` and pull out `deploymentId`, auto-detecting\n * whether the file is the modern AIT header format or a legacy plain\n * zip. Returns the raw bytes so the caller can forward them to S3\n * without re-reading the file.\n */\nexport async function readAitBundle(path: string): Promise<AitBundleInfo> {\n let buf: Buffer;\n try {\n buf = await readFile(path);\n } catch (err) {\n throw new AitBundleError({\n path,\n reason: 'file-unreadable',\n message: (err as Error).message,\n });\n }\n const bytes = new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);\n const { deploymentId, format } = deploymentIdFromBundleBytes(bytes, path);\n return { deploymentId, bytes, format };\n}\n\n/**\n * Pure helper split out so tests can feed raw bytes without a tmp file.\n * Throws `AitBundleError` on any parse failure. Returns the detected\n * format so callers that want to log it can.\n */\nexport function deploymentIdFromBundleBytes(\n bytes: Uint8Array,\n pathForError: string,\n): { deploymentId: string; format: AitBundleFormat } {\n const format = detectBundleFormat(bytes);\n if (format === 'ait') {\n return { deploymentId: deploymentIdFromAitHeader(bytes, pathForError), format };\n }\n if (format === 'zip') {\n return { deploymentId: deploymentIdFromLegacyZip(bytes, pathForError), format };\n }\n throw new AitBundleError({\n path: pathForError,\n reason: 'unrecognized-format',\n message:\n 'bundle does not start with AITBUNDL or PK magic bytes — not a valid .ait or legacy zip bundle',\n });\n}\n\n// --- AIT header path ------------------------------------------------------\n\n// Header layout sizes, mirrored from @apps-in-toss/ait-format constants.\nconst AIT_MAGIC_SIZE = 8;\nconst AIT_VERSION_SIZE = 4;\nconst AIT_LENGTH_SIZE = 8;\nconst AIT_HEADER_SIZE = AIT_MAGIC_SIZE + AIT_VERSION_SIZE + AIT_LENGTH_SIZE; // 20\n\nfunction deploymentIdFromAitHeader(bytes: Uint8Array, pathForError: string): string {\n if (bytes.length < AIT_HEADER_SIZE) {\n throw new AitBundleError({\n path: pathForError,\n reason: 'invalid-ait',\n message: 'buffer too small to be a valid AIT file',\n });\n }\n const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);\n // AIT uses big-endian for header integers (see ait-format/index.mjs:\n // `view.getUint32(h, !1)` / `view.getBigUint64(..., !1)`).\n const bundleLen = Number(view.getBigUint64(AIT_MAGIC_SIZE + AIT_VERSION_SIZE, false));\n if (!Number.isFinite(bundleLen) || bundleLen <= 0) {\n throw new AitBundleError({\n path: pathForError,\n reason: 'invalid-ait',\n message: `AIT bundle length is invalid (${bundleLen})`,\n });\n }\n const bundleStart = AIT_HEADER_SIZE;\n const bundleEnd = bundleStart + bundleLen;\n if (bytes.length < bundleEnd) {\n throw new AitBundleError({\n path: pathForError,\n reason: 'invalid-ait',\n message: 'unexpected end of buffer reading AIT bundle protobuf',\n });\n }\n const bundleBytes = bytes.subarray(bundleStart, bundleEnd);\n const fields = readProtobufStringFields(bundleBytes, [2, 3]);\n const deploymentId = fields.get(2);\n if (typeof deploymentId !== 'string' || deploymentId === '') {\n throw new AitBundleError({\n path: pathForError,\n reason: 'missing-deployment-id',\n message: 'AIT bundle protobuf is missing deploymentId (field 2)',\n });\n }\n return deploymentId;\n}\n\n// Minimal protobuf reader: scans a length-delimited varint/string\n// stream and returns the most recent value for each of the requested\n// string field numbers. Only understands the wire types we expect to\n// see in an AITBundle header (varint = 0, 64-bit = 1, length-delimited\n// = 2, 32-bit = 5); for other fields it skips the payload. Good enough\n// to extract deploymentId (field 2) and appName (field 3) without\n// linking protobufjs.\nfunction readProtobufStringFields(\n bytes: Uint8Array,\n wantedFieldNumbers: number[],\n): Map<number, string> {\n const wanted = new Set(wantedFieldNumbers);\n const out = new Map<number, string>();\n const decoder = new TextDecoder('utf-8', { fatal: false });\n let offset = 0;\n while (offset < bytes.length) {\n const { value: tag, next } = readVarint(bytes, offset);\n offset = next;\n const fieldNumber = Number(tag >> 3n);\n const wireType = Number(tag & 7n);\n if (wireType === 0) {\n // varint\n offset = readVarint(bytes, offset).next;\n } else if (wireType === 1) {\n // fixed 64-bit\n offset += 8;\n } else if (wireType === 2) {\n // length-delimited (strings, bytes, submessages, packed repeated)\n const { value: len, next: afterLen } = readVarint(bytes, offset);\n offset = afterLen;\n const payloadEnd = offset + Number(len);\n if (payloadEnd > bytes.length) {\n // Truncated; stop scanning. We return whatever we've already\n // extracted, and the caller decides whether that's enough.\n break;\n }\n if (wanted.has(fieldNumber)) {\n out.set(fieldNumber, decoder.decode(bytes.subarray(offset, payloadEnd)));\n }\n offset = payloadEnd;\n } else if (wireType === 5) {\n // fixed 32-bit\n offset += 4;\n } else {\n // Wire type 3 (start-group) / 4 (end-group) / 6+ (reserved). Not\n // expected inside AITBundle, and we can't safely skip them, so\n // stop.\n break;\n }\n }\n return out;\n}\n\nfunction readVarint(bytes: Uint8Array, start: number): { value: bigint; next: number } {\n let value = 0n;\n let shift = 0n;\n let i = start;\n // Varints are capped at 10 bytes in the protobuf wire format.\n for (let n = 0; n < 10 && i < bytes.length; n++, i++) {\n const byte = bytes[i];\n if (byte === undefined) break;\n value |= BigInt(byte & 0x7f) << shift;\n if ((byte & 0x80) === 0) {\n return { value, next: i + 1 };\n }\n shift += 7n;\n }\n // Malformed — return what we've got so the caller aborts.\n return { value, next: i };\n}\n\n// --- Legacy zip path ------------------------------------------------------\n\nfunction deploymentIdFromLegacyZip(bytes: Uint8Array, pathForError: string): string {\n let entries: Record<string, Uint8Array>;\n try {\n // `filter` restricts extraction to just `app.json` so we don't\n // decompress megabytes of asset payload just to read a few lines of\n // metadata. fflate's unzipSync operates in-memory on a Uint8Array,\n // which is fine for `.ait` bundles (tens of MB worst case).\n entries = unzipSync(bytes, { filter: (file) => file.name === 'app.json' });\n } catch (err) {\n throw new AitBundleError({\n path: pathForError,\n reason: 'invalid-zip',\n message: `not a valid zip: ${(err as Error).message}`,\n });\n }\n const entry = entries['app.json'];\n if (!entry) {\n throw new AitBundleError({\n path: pathForError,\n reason: 'missing-app-json',\n message: 'app.json is not present at the root of the bundle',\n });\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(new TextDecoder().decode(entry));\n } catch (err) {\n throw new AitBundleError({\n path: pathForError,\n reason: 'invalid-app-json',\n message: `app.json is not valid JSON: ${(err as Error).message}`,\n });\n }\n if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {\n throw new AitBundleError({\n path: pathForError,\n reason: 'invalid-app-json',\n message: 'app.json is not a JSON object',\n });\n }\n const metadata = (parsed as Record<string, unknown>)._metadata;\n if (metadata === null || typeof metadata !== 'object' || Array.isArray(metadata)) {\n throw new AitBundleError({\n path: pathForError,\n reason: 'missing-deployment-id',\n message:\n 'app.json._metadata is missing; is your build outputting the modern app.json schema?',\n });\n }\n const deploymentId = (metadata as Record<string, unknown>).deploymentId;\n if (typeof deploymentId !== 'string' || deploymentId === '') {\n throw new AitBundleError({\n path: pathForError,\n reason: 'missing-deployment-id',\n message:\n 'app.json._metadata.deploymentId is missing or empty; is your build outputting the modern app.json schema?',\n });\n }\n return deploymentId;\n}\n","import { describeApiError } from '../api/error-messages.js';\nimport { type FetchLike, NetworkError, TossApiError } from '../api/http.js';\nimport {\n postBundleMemo,\n postBundleRelease,\n postBundleReview,\n postDeploymentsComplete,\n postDeploymentsInitialize,\n putBundleToUploadUrl,\n} from '../api/mini-apps.js';\nimport { AitBundleError, type AitBundleInfo, readAitBundle } from '../config/ait-bundle.js';\nimport { ExitCode } from '../exit.js';\nimport { exitAfterFlush } from '../flush.js';\nimport {\n emitFailureFromError,\n emitJson,\n parsePositiveInt,\n printContextHeader,\n requireMiniAppId,\n resolveAppOrFail,\n} from './_shared.js';\n\n// `runDeploy` is the testable seam for `aitcc app deploy`. The citty\n// wrapper in `app.ts` is a thin shim; tests pass a fake `fetchImpl` and\n// override the bundle reader to pin each `--json` branch without\n// spawning a subprocess.\n//\n// --json contract (consumed by agent-plugin):\n//\n// success (all requested steps completed):\n// { ok: true, workspaceId, appId, deploymentId,\n// bundleFormat: 'ait' | 'zip',\n// uploaded: true, reviewed: boolean, released: boolean,\n// bundle: { ... } | null,\n// reviewResult: { ... } | null,\n// releaseResult: { ... } | null } exit 0\n//\n// dry run:\n// { ok: true, dryRun: true, workspaceId, appId, deploymentId,\n// bundleFormat: 'ait' | 'zip', bytes,\n// steps: ['upload', ...], memo: string|null,\n// releaseNotes: string|null, confirmed: boolean } exit 0\n//\n// usage errors:\n// { ok: false, reason: 'missing-app-id' | 'invalid-id'\n// | 'missing-path' | 'invalid-bundle'\n// | 'missing-release-notes' | 'not-confirmed'\n// | 'bundle-not-prepare' | 'file-unreadable',\n// ... } exit 2\n//\n// partial-success failures (keeps agent-plugin informed so it can\n// retry downstream steps without re-uploading):\n// { ok: false, uploaded: true, reviewed: false,\n// reason: 'api-error', status?, errorCode?, message } exit 17\n// { ok: false, uploaded: true, reviewed: true, released: false,\n// reason: 'api-error', ... } exit 17\n//\n// Standard auth/network follow the shared contract from _shared.ts\n// (ok:true authenticated:false exit 10, network-error exit 11,\n// api-error exit 17).\n//\n// --release note: the server requires the bundle to be in APPROVED state\n// before `/bundles/release` succeeds. In practice that means users run\n// this command twice: once with `--request-review` (bundle uploaded and\n// queued), then again days later with `--release --confirm` after the\n// review landed. Running upload + review + release in one shot only\n// works if the reviewer was already asked to auto-approve, which is\n// rare — we document rather than enforce, since a future auto-approved\n// workspace flow could legitimately chain all three.\n\nexport interface DeployArgs {\n readonly path: string;\n readonly app: string | undefined;\n readonly deploymentId?: string | undefined;\n readonly memo?: string | undefined;\n readonly requestReview?: boolean | undefined;\n readonly releaseNotes?: string | undefined;\n readonly release?: boolean | undefined;\n readonly confirm?: boolean | undefined;\n readonly workspace?: string | undefined;\n readonly dryRun?: boolean | undefined;\n readonly json: boolean;\n}\n\nexport interface DeployDeps {\n readonly fetchImpl?: FetchLike;\n readonly readBundleImpl?: (path: string) => Promise<AitBundleInfo>;\n}\n\nexport async function runDeploy(args: DeployArgs, deps: DeployDeps = {}): Promise<void> {\n // 1. Validate flag shape before reading the bundle / loading the session\n // so bad invocations fail fast without disk I/O or the Chrome-spawn\n // detour. `--app`'s presence is now optional (yaml/env can supply it),\n // but a malformed *value* should still short-circuit before we read\n // the bundle file — same fast-fail invariant the pre-PR-1b code had.\n // `resolveAppOrFail` re-parses below; this guard is only here to\n // reject `--app abc` before the bundle is opened.\n if (typeof args.app === 'string' && args.app !== '' && parsePositiveInt(args.app) === null) {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'invalid-id',\n message: `--app must be a positive integer (got ${JSON.stringify(args.app)})`,\n });\n } else {\n process.stderr.write(`app deploy: invalid --app ${JSON.stringify(args.app)}\\n`);\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n\n if (typeof args.path !== 'string' || args.path === '') {\n if (args.json) {\n emitJson({ ok: false, reason: 'missing-path', message: 'path to .ait bundle is required' });\n } else {\n process.stderr.write('app deploy: path to .ait bundle is required.\\n');\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n\n const requestReview = Boolean(args.requestReview);\n const release = Boolean(args.release);\n const confirm = Boolean(args.confirm);\n const releaseNotes = typeof args.releaseNotes === 'string' ? args.releaseNotes : undefined;\n\n if (requestReview && releaseNotes === undefined) {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'missing-release-notes',\n message: '--release-notes <text> is required with --request-review',\n });\n } else {\n process.stderr.write(\n 'app deploy: --release-notes <text> is required with --request-review.\\n',\n );\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n\n if (release && !confirm) {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'not-confirmed',\n message: '--release is destructive; pass --confirm to proceed',\n });\n } else {\n process.stderr.write(\n 'app deploy: --release publishes the bundle to end users.\\n' +\n ' Re-run with --confirm to proceed.\\n',\n );\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n\n // 2. Read the bundle. In dry-run mode we still read it so the plan we\n // print matches what a real run would do (bytes count, embedded\n // deploymentId).\n const readBundle = deps.readBundleImpl ?? readAitBundle;\n let bundleInfo: AitBundleInfo;\n try {\n bundleInfo = await readBundle(args.path);\n } catch (err) {\n if (err instanceof AitBundleError) {\n const reason = err.reason === 'file-unreadable' ? 'file-unreadable' : 'invalid-bundle';\n if (args.json) {\n emitJson({\n ok: false,\n reason,\n path: err.path,\n bundleReason: err.reason,\n message: err.message,\n });\n } else {\n process.stderr.write(`app deploy: ${err.message}\\n`);\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n throw err;\n }\n\n // 3. If --deployment-id was passed explicitly, use it verbatim (the\n // bundle's embedded id is still useful for the plan output but does\n // not override). This matches `app bundles upload`'s flag\n // semantics; the wrapper's convenience is auto-detect, not\n // override.\n const deploymentId =\n typeof args.deploymentId === 'string' && args.deploymentId !== ''\n ? args.deploymentId\n : bundleInfo.deploymentId;\n if (deploymentId === '') {\n // Defensive: readAitBundle throws when the id is empty, but a\n // caller-provided impl could return one. Surface as invalid-bundle\n // so the agent-plugin error branch is consistent.\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'invalid-bundle',\n path: args.path,\n message: 'deploymentId is empty',\n });\n } else {\n process.stderr.write('app deploy: deploymentId is empty.\\n');\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n\n // 4. Resolve workspace + miniApp (loads session + checks auth). In\n // dry-run we still do this because the `--json` plan includes\n // `workspaceId`/`appId` and the agent-plugin parses those fields\n // unconditionally. `--app` is now optional when `aitcc.yaml` (or\n // `AITCC_APP`) supplies a `miniAppId`.\n const ctx = await resolveAppOrFail({\n json: args.json,\n appIdRaw: args.app,\n appIdField: 'app',\n ...(args.workspace !== undefined ? { workspace: args.workspace } : {}),\n });\n if (!ctx) return;\n const appId = await requireMiniAppId(ctx, args.json);\n if (appId === null) return;\n printContextHeader(ctx, { json: args.json });\n const { session, workspaceId } = ctx;\n\n const memo = typeof args.memo === 'string' && args.memo.length > 0 ? args.memo : undefined;\n const steps: string[] = ['upload'];\n if (requestReview) steps.push('review');\n if (release) steps.push('release');\n\n if (args.dryRun) {\n if (args.json) {\n emitJson({\n ok: true,\n dryRun: true,\n workspaceId,\n appId,\n deploymentId,\n bundleFormat: bundleInfo.format,\n bytes: bundleInfo.bytes.byteLength,\n steps,\n memo: memo ?? null,\n releaseNotes: releaseNotes ?? null,\n confirmed: confirm,\n });\n } else {\n const stepsLine = steps\n .map((s) => {\n if (s === 'review') return `review (releaseNotes: ${JSON.stringify(releaseNotes ?? '')})`;\n if (s === 'release') return `release (${confirm ? 'confirmed' : 'NOT confirmed'})`;\n return s;\n })\n .join(' → ');\n process.stdout.write(\n `DRY RUN\\n` +\n ` app ${appId}\\n` +\n ` workspace ${workspaceId}\\n` +\n ` bundle ${args.path} (${bundleInfo.bytes.byteLength} bytes)\\n` +\n ` deploymentId ${deploymentId}\\n` +\n ` memo ${memo ?? '(none)'}\\n` +\n ` steps ${stepsLine}\\n`,\n );\n }\n return exitAfterFlush(ExitCode.Ok);\n }\n\n // 5. Real execution. Each step tracks its success so a partial\n // failure downstream can report which earlier steps already ran.\n const apiOpts = deps.fetchImpl ? { fetchImpl: deps.fetchImpl } : {};\n let uploaded = false;\n let bundleRecord: Readonly<Record<string, unknown>> | null = null;\n let reviewed = false;\n let reviewResult: Readonly<Record<string, unknown>> | null = null;\n\n try {\n const init = await postDeploymentsInitialize(\n workspaceId,\n appId,\n deploymentId,\n session.cookies,\n apiOpts,\n );\n if (init.reviewStatus !== 'PREPARE') {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'bundle-not-prepare',\n workspaceId,\n appId,\n deploymentId,\n reviewStatus: init.reviewStatus,\n message: '이미 존재하는 버전이에요.',\n });\n } else {\n process.stderr.write(\n `app deploy: deployment ${deploymentId} is already in state ${init.reviewStatus}; upload refused.\\n`,\n );\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n await putBundleToUploadUrl(init.uploadUrl, bundleInfo.bytes, apiOpts);\n bundleRecord = await postDeploymentsComplete(\n workspaceId,\n appId,\n deploymentId,\n session.cookies,\n apiOpts,\n );\n if (memo !== undefined) {\n await postBundleMemo(workspaceId, appId, deploymentId, memo, session.cookies, apiOpts);\n }\n uploaded = true;\n } catch (err) {\n // Upload failure — nothing downstream ran, so the shared dispatcher\n // is fine; partial-success reporting kicks in only once\n // `uploaded === true`.\n return emitFailureFromError(args.json, err);\n }\n\n if (requestReview) {\n try {\n reviewResult = await postBundleReview(\n {\n workspaceId,\n miniAppId: appId,\n deploymentId,\n releaseNotes: releaseNotes ?? '',\n },\n session.cookies,\n apiOpts,\n );\n reviewed = true;\n } catch (err) {\n return emitPartialFailure(args.json, err, {\n workspaceId,\n appId,\n deploymentId,\n uploaded: true,\n reviewed: false,\n released: false,\n });\n }\n }\n\n let releaseResult: Readonly<Record<string, unknown>> | null = null;\n if (release) {\n try {\n releaseResult = await postBundleRelease(\n { workspaceId, miniAppId: appId, deploymentId },\n session.cookies,\n apiOpts,\n );\n } catch (err) {\n return emitPartialFailure(args.json, err, {\n workspaceId,\n appId,\n deploymentId,\n uploaded: true,\n reviewed,\n released: false,\n });\n }\n }\n\n if (args.json) {\n emitJson({\n ok: true,\n workspaceId,\n appId,\n deploymentId,\n bundleFormat: bundleInfo.format,\n uploaded,\n reviewed,\n released: release,\n bundle: bundleRecord,\n reviewResult,\n releaseResult,\n });\n return exitAfterFlush(ExitCode.Ok);\n }\n\n process.stdout.write(\n `Deployed bundle for app ${appId} (ws ${workspaceId})\\n` +\n ` deploymentId ${deploymentId}\\n` +\n ` bytes ${bundleInfo.bytes.byteLength}\\n` +\n ` steps ${steps.join(' → ')}\\n`,\n );\n return exitAfterFlush(ExitCode.Ok);\n}\n\n/**\n * Partial-failure emitter. The upload succeeded (so the user does NOT\n * need to re-upload on retry) but a downstream step failed. Keeping the\n * `uploaded: true` bit in the JSON lets agent-plugin skip to the\n * specific failing step on retry instead of re-running the whole\n * pipeline.\n */\nasync function emitPartialFailure(\n json: boolean,\n err: unknown,\n progress: {\n workspaceId: number;\n appId: number;\n deploymentId: string;\n uploaded: boolean;\n reviewed: boolean;\n released: boolean;\n },\n): Promise<void> {\n if (err instanceof TossApiError && err.isAuthError) {\n if (json) {\n emitJson({\n ok: true,\n authenticated: false,\n reason: 'session-expired',\n ...progress,\n });\n } else {\n process.stderr.write('Session is no longer valid. Run `aitcc login` again.\\n');\n }\n return exitAfterFlush(ExitCode.NotAuthenticated);\n }\n if (err instanceof TossApiError) {\n const message = describeApiError({\n errorCode: err.errorCode,\n reason: err.reason,\n fallback: err.message,\n });\n if (json) {\n emitJson({\n ok: false,\n reason: 'api-error',\n status: err.status,\n ...(err.errorCode !== undefined ? { errorCode: err.errorCode } : {}),\n message,\n ...progress,\n });\n } else {\n process.stderr.write(`Unexpected error: ${message}\\n`);\n }\n return exitAfterFlush(ExitCode.ApiError);\n }\n if (err instanceof NetworkError) {\n if (json) {\n emitJson({\n ok: false,\n reason: 'network-error',\n message: err.message,\n ...progress,\n });\n } else {\n process.stderr.write(`Network error reaching the console API: ${err.message}.\\n`);\n }\n return exitAfterFlush(ExitCode.NetworkError);\n }\n if (json) {\n emitJson({\n ok: false,\n reason: 'api-error',\n message: (err as Error).message,\n ...progress,\n });\n } else {\n process.stderr.write(`Unexpected error: ${(err as Error).message}\\n`);\n }\n return exitAfterFlush(ExitCode.ApiError);\n}\n","import type { CdpCookie } from '../cdp.js';\nimport { type FetchLike, requestConsoleApi } from './http.js';\n\n// Console-scoped \"who am I\" endpoint, discovered by observing the console UI\n// boot requests. Returned shape is stable across the sample workspace; new\n// fields may appear but we read it conservatively.\n\nexport interface ConsoleMemberWorkspace {\n readonly workspaceId: number;\n readonly workspaceName: string;\n readonly role: string;\n readonly isOwnerDelegationRequested: boolean;\n}\n\nexport interface ConsoleMemberUserInfo {\n readonly id: number;\n readonly bizUserNo: number;\n readonly name: string;\n readonly email: string;\n readonly role: string;\n readonly workspaces: readonly ConsoleMemberWorkspace[];\n readonly isAdult: boolean;\n readonly isOverseasBusiness: boolean;\n}\n\nconst BASE = 'https://apps-in-toss.toss.im/console/api-public/v3/appsintossconsole';\nconst MEMBER_USER_INFO_URL = `${BASE}/members/me/user-info`;\n\nexport async function fetchConsoleMemberUserInfo(\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<ConsoleMemberUserInfo> {\n return requestConsoleApi<ConsoleMemberUserInfo>({\n url: MEMBER_USER_INFO_URL,\n cookies,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n}\n\n// Console account-level terms (distinct from workspace-level terms).\n// `/console-user-terms/me` returns the user's own acceptance of the\n// top-level console TOS, which is required to use the console at all.\n// Shape matches the workspace-terms bucket entries exactly.\nexport interface UserTerm {\n readonly required: boolean;\n readonly termsId: number;\n readonly revisionId: number;\n readonly title: string;\n readonly contentsUrl: string;\n readonly actionType: string;\n readonly isAgreed: boolean;\n readonly isOneTimeConsent: boolean;\n}\n\nexport async function fetchUserTerms(\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<readonly UserTerm[]> {\n const url = `${BASE}/console-user-terms/me`;\n const raw = await requestConsoleApi<unknown>({\n url,\n cookies,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n if (!Array.isArray(raw)) {\n throw new Error('Unexpected user-terms shape: not an array');\n }\n return raw.map((entry, i): UserTerm => {\n if (!entry || typeof entry !== 'object') {\n throw new Error(`Unexpected user-terms entry at index ${i}`);\n }\n const e = entry as Record<string, unknown>;\n return {\n required: Boolean(e.required),\n termsId: typeof e.termsId === 'number' ? e.termsId : 0,\n revisionId: typeof e.revisionId === 'number' ? e.revisionId : 0,\n title: typeof e.title === 'string' ? e.title : '',\n contentsUrl: typeof e.contentsUrl === 'string' ? e.contentsUrl : '',\n actionType: typeof e.actionType === 'string' ? e.actionType : '',\n isAgreed: Boolean(e.isAgreed),\n isOneTimeConsent: Boolean(e.isOneTimeConsent),\n };\n });\n}\n","import { access, readFile } from 'node:fs/promises';\nimport { dirname, isAbsolute, resolve } from 'node:path';\nimport { parse as parseYaml } from 'yaml';\n\n// The app manifest is the CLI's user-facing contract for `aitcc app\n// register`. It intentionally mirrors the console's form field names only\n// at the top level — the inferred submit payload (which nests fields into\n// step1/step2/miniApp/impression) is built by a separate payload module\n// so the manifest shape is stable even if the bundle analysis turns out\n// to be off. Dog-food task #23 is expected to correct the payload\n// transformation but should not force a manifest rewrite.\n//\n// Validation here is \"config shape only\": presence, type, and cheap\n// numeric/length constraints from `VALIDATION-RULES.md` that we can\n// enforce without reading image files. Image-dimension checks live in a\n// sibling module (`image-validator.ts`) because they need FS reads.\n//\n// ManifestError is a single error class carrying (kind, field?, path?)\n// so the command layer can translate it into the documented `--json`\n// error shapes without re-classifying.\n\nexport type ManifestErrorKind = 'invalid-config' | 'missing-required-field';\n\nexport class ManifestError extends Error {\n readonly kind: ManifestErrorKind;\n readonly field: string | undefined;\n\n constructor(kind: ManifestErrorKind, message: string, field?: string) {\n super(message);\n this.name = 'ManifestError';\n this.kind = kind;\n this.field = field;\n }\n}\n\nexport interface AppManifest {\n readonly titleKo: string;\n readonly titleEn: string;\n readonly appName: string;\n readonly homePageUri: string | undefined;\n readonly csEmail: string;\n readonly logo: string;\n readonly logoDarkMode: string | undefined;\n readonly horizontalThumbnail: string;\n readonly categoryIds: readonly number[];\n readonly subtitle: string;\n readonly description: string;\n readonly keywords: readonly string[];\n readonly verticalScreenshots: readonly string[];\n readonly horizontalScreenshots: readonly string[];\n}\n\nconst DEFAULT_NAMES = ['aitcc.yaml', 'aitcc.json'] as const;\n\nasync function fileExists(path: string): Promise<boolean> {\n try {\n await access(path);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Resolve the manifest file path. When `explicit` is provided, we use it\n * verbatim (resolved against `cwd`) and require it to exist. Otherwise we\n * auto-detect `aitcc.yaml` then `aitcc.json` under `cwd`.\n */\nexport async function resolveManifestPath(\n explicit: string | undefined,\n cwd: string,\n): Promise<string> {\n if (explicit) {\n const abs = isAbsolute(explicit) ? explicit : resolve(cwd, explicit);\n if (!(await fileExists(abs))) {\n throw new ManifestError('invalid-config', `manifest file not found at ${abs}`);\n }\n return abs;\n }\n for (const name of DEFAULT_NAMES) {\n const abs = resolve(cwd, name);\n if (await fileExists(abs)) return abs;\n }\n throw new ManifestError(\n 'invalid-config',\n `no app manifest found (looked for ${DEFAULT_NAMES.join(', ')} in ${cwd})`,\n );\n}\n\nexport async function loadAppManifest(path: string): Promise<AppManifest> {\n const raw = await readFile(path, 'utf8');\n const parsed = parseManifestFile(path, raw);\n return validateManifest(parsed, dirname(path));\n}\n\nfunction parseManifestFile(path: string, raw: string): Record<string, unknown> {\n const isJson = path.toLowerCase().endsWith('.json');\n try {\n const out = isJson ? JSON.parse(raw) : parseYaml(raw);\n if (out === null || typeof out !== 'object' || Array.isArray(out)) {\n throw new ManifestError('invalid-config', `manifest at ${path} is not a mapping`);\n }\n return out as Record<string, unknown>;\n } catch (err) {\n if (err instanceof ManifestError) throw err;\n const msg = (err as Error).message;\n throw new ManifestError('invalid-config', `failed to parse manifest at ${path}: ${msg}`);\n }\n}\n\nfunction requireString(input: Record<string, unknown>, key: string): string {\n const v = input[key];\n if (v === undefined || v === null) {\n throw new ManifestError('missing-required-field', `${key} is required`, key);\n }\n if (typeof v !== 'string') {\n throw new ManifestError('invalid-config', `${key} must be a string`, key);\n }\n if (v.trim().length === 0) {\n throw new ManifestError('missing-required-field', `${key} is required`, key);\n }\n return v;\n}\n\nfunction optionalString(input: Record<string, unknown>, key: string): string | undefined {\n const v = input[key];\n if (v === undefined || v === null) return undefined;\n if (typeof v !== 'string') {\n throw new ManifestError('invalid-config', `${key} must be a string when provided`, key);\n }\n return v;\n}\n\nfunction requirePath(input: Record<string, unknown>, key: string, configDir: string): string {\n const rel = requireString(input, key);\n return isAbsolute(rel) ? rel : resolve(configDir, rel);\n}\n\nfunction optionalPath(\n input: Record<string, unknown>,\n key: string,\n configDir: string,\n): string | undefined {\n const rel = optionalString(input, key);\n if (rel === undefined) return undefined;\n return isAbsolute(rel) ? rel : resolve(configDir, rel);\n}\n\nfunction requireNumberArray(\n input: Record<string, unknown>,\n key: string,\n { min }: { min: number },\n): number[] {\n const v = input[key];\n if (!Array.isArray(v)) {\n throw new ManifestError('invalid-config', `${key} must be an array of numbers`, key);\n }\n if (v.length < min) {\n throw new ManifestError('invalid-config', `${key} must contain at least ${min} item(s)`, key);\n }\n return v.map((item, idx) => {\n if (typeof item !== 'number' || !Number.isInteger(item)) {\n throw new ManifestError('invalid-config', `${key}[${idx}] must be an integer`, key);\n }\n return item;\n });\n}\n\nfunction optionalStringArray(\n input: Record<string, unknown>,\n key: string,\n { max }: { max?: number } = {},\n): string[] {\n const v = input[key];\n if (v === undefined || v === null) return [];\n if (!Array.isArray(v)) {\n throw new ManifestError('invalid-config', `${key} must be an array of strings`, key);\n }\n if (max !== undefined && v.length > max) {\n throw new ManifestError(\n 'invalid-config',\n `${key} accepts at most ${max} entries (got ${v.length})`,\n key,\n );\n }\n return v.map((item, idx) => {\n if (typeof item !== 'string') {\n throw new ManifestError('invalid-config', `${key}[${idx}] must be a string`, key);\n }\n return item;\n });\n}\n\nfunction requirePathArray(\n input: Record<string, unknown>,\n key: string,\n configDir: string,\n { min }: { min: number },\n): string[] {\n const v = input[key];\n if (!Array.isArray(v)) {\n throw new ManifestError('invalid-config', `${key} must be an array of paths`, key);\n }\n if (v.length < min) {\n throw new ManifestError('invalid-config', `${key} must contain at least ${min} item(s)`, key);\n }\n return v.map((item, idx) => {\n if (typeof item !== 'string' || item.trim().length === 0) {\n throw new ManifestError('invalid-config', `${key}[${idx}] must be a non-empty string`, key);\n }\n return isAbsolute(item) ? item : resolve(configDir, item);\n });\n}\n\nfunction optionalPathArray(\n input: Record<string, unknown>,\n key: string,\n configDir: string,\n): string[] {\n const v = input[key];\n if (v === undefined || v === null) return [];\n if (!Array.isArray(v)) {\n throw new ManifestError('invalid-config', `${key} must be an array of paths`, key);\n }\n return v.map((item, idx) => {\n if (typeof item !== 'string' || item.trim().length === 0) {\n throw new ManifestError('invalid-config', `${key}[${idx}] must be a non-empty string`, key);\n }\n return isAbsolute(item) ? item : resolve(configDir, item);\n });\n}\n\n// Regexes mirror the console's client-side validators (`is-email` CDgIL0c0\n// bundle + VALIDATION-RULES.md). Keeping local validators lets agent-\n// plugin surface `missing-required-field`/`invalid-config` instead of a\n// pass-through `api-error` when the user supplies garbage.\n//\n// These constants/helpers are exported so `aitcc app init`'s prompt-level\n// validators can reuse them without duplicating the rules (drift risk).\nexport const EMAIL_REGEX =\n /^(([^<>()[\\]\\\\.,;:\\s@\"]+(\\.[^<>()[\\]\\\\.,;:\\s@\"]+)*)|(\".+\"))@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}])|(([a-zA-Z\\-0-9]+\\.)+[a-zA-Z]{2,}))$/;\n\nexport function isValidEmail(v: string): boolean {\n return EMAIL_REGEX.test(v.toLowerCase());\n}\n\n// titleKo/titleEn server-side rules (sdk-example#39, 2026-05-03 dog-food).\n// Server returns prefix-form errorCodes `miniApp.InvalidTitle` /\n// `miniApp.InvalidTitleEn` (distinct from the legacy numeric catalog like\n// `4046`). We preflight here so the user does not eat a register round-trip\n// just to discover a typo.\n//\n// titleKo: Korean / English / digits / space / `:·?` only; ≤ 10 code points\n// excluding spaces.\n// titleEn: `^[A-Za-z0-9 :·?]+$`; ≤ 15 code points excluding spaces; AND\n// each space-separated word must be title-case (first char uppercase, rest\n// lowercase). All-caps tokens like `AITC`/`SDK` are rejected by the server.\nexport const TITLE_KO_REGEX = /^[가-힣A-Za-z0-9 :·?]+$/;\nexport const TITLE_EN_REGEX = /^[A-Za-z0-9 :·?]+$/;\n// appName slug: kebab-case starting with a lowercase letter. Mirrors the\n// console's slug rule observed during dog-food (server rejects uppercase\n// or leading digits). Exported for prompt validators in `app init`.\nexport const APP_NAME_REGEX = /^[a-z][a-z0-9-]*$/;\n\nexport const MANIFEST_LIMITS = {\n titleKoMaxCodepoints: 10,\n titleEnMaxCodepoints: 15,\n subtitleMaxChars: 20,\n descriptionMaxCodepoints: 500,\n keywordsMax: 10,\n verticalScreenshotsMin: 3,\n} as const;\n\nconst TITLE_KO_MAX_CODEPOINTS_NO_SPACE = MANIFEST_LIMITS.titleKoMaxCodepoints;\nconst TITLE_EN_MAX_CODEPOINTS_NO_SPACE = MANIFEST_LIMITS.titleEnMaxCodepoints;\n\nexport function countCodepointsExcludingSpaces(v: string): number {\n return [...v].filter((ch) => ch !== ' ').length;\n}\n\n// Backwards-compatible alias retained as a private name for the existing\n// validator callsites — public consumers go through `countCodepointsExcludingSpaces`.\nconst codePointsExcludingSpaces = countCodepointsExcludingSpaces;\n\nexport function isTitleCaseWord(word: string): boolean {\n // Only enforced on words that contain ASCII letters; pure-digit or\n // punctuation-only tokens (e.g. `:`, `V2`, `123`) pass through. The\n // server's exact tokenization is not documented — using \"first letter\n // uppercase, remaining letters lowercase\" mirrors the rejections we\n // observed for `AITC` and `SDK`. A token like `v2` is rejected because\n // the leading letter must be uppercase.\n const chars = [...word];\n const firstLetterIdx = chars.findIndex((ch) => /[A-Za-z]/.test(ch));\n if (firstLetterIdx === -1) return true;\n for (let i = 0; i < chars.length; i++) {\n const ch = chars[i];\n if (ch === undefined || !/[A-Za-z]/.test(ch)) continue;\n const expectUpper = i === firstLetterIdx;\n if (expectUpper && ch !== ch.toUpperCase()) return false;\n if (!expectUpper && ch !== ch.toLowerCase()) return false;\n }\n return true;\n}\n\n// Dog-food #23 (2026-04-22) HTTP 400 errorCode=4000:\n// \"앱 상세설명은 최대 500자를 넘어갈 수 없어요\". Counted by code points\n// (`[...str].length`) to err on the strict side — the server's internal\n// counting rule is not documented, so we use the count that treats\n// astral characters as single units.\nconst DETAIL_DESCRIPTION_MAX_CODEPOINTS = MANIFEST_LIMITS.descriptionMaxCodepoints;\n\nfunction isValidHttpUrl(v: string): boolean {\n try {\n const parsed = new URL(v);\n return parsed.protocol === 'http:' || parsed.protocol === 'https:';\n } catch {\n return false;\n }\n}\n\nfunction validateManifest(raw: Record<string, unknown>, configDir: string): AppManifest {\n const titleKo = requireString(raw, 'titleKo');\n if (!TITLE_KO_REGEX.test(titleKo)) {\n throw new ManifestError(\n 'invalid-config',\n `titleKo may only contain Korean/English letters, digits, spaces, and \":·?\" (got \"${titleKo}\"; server-side rule, errorCode: miniApp.InvalidTitle)`,\n 'titleKo',\n );\n }\n const titleKoLen = codePointsExcludingSpaces(titleKo);\n if (titleKoLen > TITLE_KO_MAX_CODEPOINTS_NO_SPACE) {\n throw new ManifestError(\n 'invalid-config',\n `titleKo must be ${TITLE_KO_MAX_CODEPOINTS_NO_SPACE} characters or fewer excluding spaces (got ${titleKoLen}; server-side rule, errorCode: miniApp.InvalidTitle)`,\n 'titleKo',\n );\n }\n const titleEn = requireString(raw, 'titleEn');\n if (!TITLE_EN_REGEX.test(titleEn)) {\n throw new ManifestError(\n 'invalid-config',\n `titleEn may only contain English letters, digits, spaces, and \":·?\" (got \"${titleEn}\"; server-side rule, errorCode: miniApp.InvalidTitleEn)`,\n 'titleEn',\n );\n }\n const titleEnLen = codePointsExcludingSpaces(titleEn);\n if (titleEnLen > TITLE_EN_MAX_CODEPOINTS_NO_SPACE) {\n throw new ManifestError(\n 'invalid-config',\n `titleEn must be ${TITLE_EN_MAX_CODEPOINTS_NO_SPACE} characters or fewer excluding spaces (got ${titleEnLen}; server-side rule, errorCode: miniApp.InvalidTitleEn)`,\n 'titleEn',\n );\n }\n for (const word of titleEn.split(' ')) {\n if (word.length === 0) continue;\n if (!isTitleCaseWord(word)) {\n throw new ManifestError(\n 'invalid-config',\n `titleEn word \"${word}\" must be title-case (first letter uppercase, rest lowercase); server-side rule, errorCode: miniApp.InvalidTitleEn`,\n 'titleEn',\n );\n }\n }\n const appName = requireString(raw, 'appName');\n const csEmail = requireString(raw, 'csEmail');\n if (!isValidEmail(csEmail)) {\n throw new ManifestError(\n 'invalid-config',\n `csEmail is not a valid email address (got ${csEmail})`,\n 'csEmail',\n );\n }\n const subtitle = requireString(raw, 'subtitle');\n // subtitle ≤ 20 chars (F(20) in VALIDATION-RULES).\n if (subtitle.length > MANIFEST_LIMITS.subtitleMaxChars) {\n throw new ManifestError(\n 'invalid-config',\n `subtitle must be ${MANIFEST_LIMITS.subtitleMaxChars} characters or fewer (got ${subtitle.length})`,\n 'subtitle',\n );\n }\n const description = requireString(raw, 'description');\n const descriptionCodepoints = [...description].length;\n if (descriptionCodepoints > DETAIL_DESCRIPTION_MAX_CODEPOINTS) {\n throw new ManifestError(\n 'invalid-config',\n `description must be ${DETAIL_DESCRIPTION_MAX_CODEPOINTS} characters or fewer (got ${descriptionCodepoints})`,\n 'description',\n );\n }\n const homePageUri = optionalString(raw, 'homePageUri');\n if (homePageUri !== undefined && !isValidHttpUrl(homePageUri)) {\n throw new ManifestError(\n 'invalid-config',\n `homePageUri must be a http(s) URL (got ${homePageUri})`,\n 'homePageUri',\n );\n }\n const logo = requirePath(raw, 'logo', configDir);\n const logoDarkMode = optionalPath(raw, 'logoDarkMode', configDir);\n const horizontalThumbnail = requirePath(raw, 'horizontalThumbnail', configDir);\n const categoryIds = requireNumberArray(raw, 'categoryIds', { min: 1 });\n const keywords = optionalStringArray(raw, 'keywords', { max: 10 });\n const verticalScreenshots = requirePathArray(raw, 'verticalScreenshots', configDir, { min: 3 });\n const horizontalScreenshots = optionalPathArray(raw, 'horizontalScreenshots', configDir);\n\n return {\n titleKo,\n titleEn,\n appName,\n homePageUri,\n csEmail,\n logo,\n logoDarkMode,\n horizontalThumbnail,\n categoryIds,\n subtitle,\n description,\n keywords,\n verticalScreenshots,\n horizontalScreenshots,\n };\n}\n","// Pure renderer for `aitcc app init`. Lives separately from the handler\n// so its only dependency is on the answers object; tests pin the literal\n// output without spinning up any I/O. The yaml is hand-rolled (rather\n// than going through `yaml.stringify` + comment injection) because the\n// commented-out optional block needs precise placement to be useful, and\n// mixing programmatic comments into a `Document` ends up more brittle\n// than a template string.\n\nexport interface InitAnswers {\n readonly workspaceId: number;\n readonly titleKo: string;\n readonly titleEn: string;\n readonly appName: string;\n readonly csEmail: string;\n readonly subtitle: string;\n readonly description: string;\n readonly categoryIds: readonly number[];\n}\n\nexport function renderInitYaml(answers: InitAnswers): string {\n const description = indentBlock(answers.description, ' ');\n return `# Project context — read by every aitcc command in this directory tree.\n# Generated by \\`aitcc app init\\` (https://github.com/apps-in-toss-community/console-cli).\nworkspaceId: ${answers.workspaceId}\n# miniAppId is written back automatically by \\`aitcc app register\\`.\n\n# --- App manifest (required) ---\ntitleKo: ${yamlDoubleQuoted(answers.titleKo)}\ntitleEn: ${yamlDoubleQuoted(answers.titleEn)}\nappName: ${answers.appName}\ncsEmail: ${answers.csEmail}\nsubtitle: ${yamlDoubleQuoted(answers.subtitle)}\ndescription: |-\n${description}\ncategoryIds: [${answers.categoryIds.join(', ')}]\nlogo: ./assets/logo.png\nhorizontalThumbnail: ./assets/thumbnail.png\nverticalScreenshots:\n - ./assets/screenshot-1.png\n - ./assets/screenshot-2.png\n - ./assets/screenshot-3.png\n\n# --- Optional fields (uncomment + fill in if needed) ---\n# homePageUri: \"https://example.com/\"\n# logoDarkMode: ./assets/logo-dark.png\n# keywords: [foo, bar]\n# horizontalScreenshots:\n# - ./assets/horizontal-1.png\n`;\n}\n\n// Forces a quoted scalar so colons, leading whitespace, or yaml-significant\n// chars in user input can't accidentally break the document shape.\nfunction yamlDoubleQuoted(value: string): string {\n const escaped = value.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"');\n return `\"${escaped}\"`;\n}\n\n// `|-` block scalars require every content line to share the same indent;\n// without this helper a multi-line description would silently corrupt the\n// surrounding document.\nfunction indentBlock(value: string, prefix: string): string {\n return value\n .split('\\n')\n .map((line) => `${prefix}${line}`)\n .join('\\n');\n}\n","import { access, mkdir, writeFile } from 'node:fs/promises';\nimport { isAbsolute, resolve as resolvePath } from 'node:path';\nimport { checkbox, editor, input, select } from '@inquirer/prompts';\nimport { parseDocument } from 'yaml';\nimport { fetchConsoleMemberUserInfo } from '../api/me.js';\nimport { fetchImpressionCategoryList } from '../api/mini-apps.js';\nimport type { CdpCookie } from '../cdp.js';\nimport {\n APP_NAME_REGEX,\n countCodepointsExcludingSpaces,\n isTitleCaseWord,\n isValidEmail,\n MANIFEST_LIMITS,\n TITLE_EN_REGEX,\n TITLE_KO_REGEX,\n} from '../config/app-manifest.js';\nimport { ExitCode } from '../exit.js';\nimport { exitAfterFlush } from '../flush.js';\nimport { readSession } from '../session.js';\nimport { emitFailureFromError, emitJson, emitNotAuthenticated } from './_shared.js';\nimport { type InitAnswers, renderInitYaml } from './app-init-template.js';\n\n// `runAppInit` is the testable seam for `aitcc app init`. The citty\n// wrapper in `app.ts` is a thin shim. Tests cover the render + yaml\n// round-trip, the conflict path (existing manifest), and the non-TTY\n// refusal — the inquirer prompts themselves are not mocked because the\n// scope of the test is the file-IO contract, not the keystroke flow.\n//\n// --json contract:\n//\n// The command itself does not support interactive prompts under\n// `--json` or in non-TTY environments. Both refuse with:\n// { ok: false, reason: 'interactive-required', message } exit 2\n//\n// On success the user only ever sees the human-readable next-steps\n// block on stderr. There is no machine-readable success shape — by\n// design, this command is a one-shot bootstrap meant for humans.\n\nexport interface AppInitArgs {\n readonly cwd?: string;\n readonly force: boolean;\n readonly json: boolean;\n}\n\nexport async function runAppInit(args: AppInitArgs): Promise<void> {\n const cwd = args.cwd ?? process.cwd();\n\n if (args.json) {\n emitInteractiveRequired(true);\n return exitAfterFlush(ExitCode.Usage);\n }\n if (!process.stdout.isTTY || !process.stdin.isTTY) {\n emitInteractiveRequired(false);\n return exitAfterFlush(ExitCode.Usage);\n }\n\n const yamlPath = resolvePath(cwd, 'aitcc.yaml');\n const jsonPath = resolvePath(cwd, 'aitcc.json');\n if (!args.force) {\n const existing = (await fileExists(yamlPath))\n ? yamlPath\n : (await fileExists(jsonPath))\n ? jsonPath\n : null;\n if (existing) {\n process.stderr.write(\n `A project file already exists at ${existing}. Pass --force to overwrite.\\n`,\n );\n return exitAfterFlush(ExitCode.Usage);\n }\n }\n\n const session = await readSession();\n if (!session) {\n emitNotAuthenticated(false);\n return exitAfterFlush(ExitCode.NotAuthenticated);\n }\n\n let workspaceId: number;\n let categoryIds: number[];\n try {\n workspaceId = await pickWorkspace(session.cookies);\n categoryIds = await pickCategories(session.cookies);\n } catch (err) {\n if (isPromptCancelled(err)) {\n process.stderr.write('Aborted.\\n');\n return exitAfterFlush(ExitCode.Usage);\n }\n return emitFailureFromError(false, err);\n }\n\n let answers: InitAnswers;\n try {\n const titleKo = await input({\n message: 'App title (Korean):',\n validate: validateTitleKo,\n });\n const titleEn = await input({\n message: 'App title (English):',\n validate: validateTitleEn,\n });\n const appName = await input({\n message: 'App slug (kebab-case):',\n validate: validateAppName,\n });\n const csEmail = await input({\n message: 'Customer-support email:',\n validate: validateEmail,\n });\n const subtitle = await input({\n message: `Subtitle (≤${MANIFEST_LIMITS.subtitleMaxChars} chars):`,\n validate: validateSubtitle,\n });\n const description = await editor({\n message: 'Long description (opens $EDITOR):',\n validate: validateDescription,\n });\n answers = {\n workspaceId,\n titleKo,\n titleEn,\n appName,\n csEmail,\n subtitle,\n description,\n categoryIds,\n };\n } catch (err) {\n if (isPromptCancelled(err)) {\n process.stderr.write('Aborted.\\n');\n return exitAfterFlush(ExitCode.Usage);\n }\n throw err;\n }\n\n const rendered = renderInitYaml(answers);\n\n // Round-trip the rendered yaml so a template bug surfaces here, not\n // in the user's next `aitcc app register` run. Failure here is a CLI\n // bug (renderer fault), not user input.\n const doc = parseDocument(rendered);\n if (doc.errors.length > 0) {\n const detail = doc.errors[0]?.message ?? 'unknown parse error';\n process.stderr.write(`internal error: rendered yaml failed to parse (${detail})\\n`);\n return exitAfterFlush(ExitCode.Generic);\n }\n\n // Create the assets dir before writing the manifest so a permission /\n // disk error can't leave a `aitcc.yaml` pointing at a directory we\n // failed to provision. Both calls are wrapped so an FS failure surfaces\n // as a clean error + exit code rather than an unhandled rejection.\n try {\n const assetsDir = resolvePath(cwd, 'assets');\n await mkdir(assetsDir, { recursive: true });\n await writeFile(yamlPath, rendered, 'utf8');\n } catch (err) {\n const detail = err instanceof Error ? err.message : String(err);\n process.stderr.write(`failed to write project files: ${detail}\\n`);\n return exitAfterFlush(ExitCode.Generic);\n }\n\n emitNextSteps(yamlPath);\n return exitAfterFlush(ExitCode.Ok);\n}\n\nfunction emitInteractiveRequired(json: boolean): void {\n const message = 'aitcc app init requires an interactive TTY.';\n if (json) {\n emitJson({ ok: false, reason: 'interactive-required', message });\n } else {\n process.stderr.write(`${message}\\n`);\n }\n}\n\nfunction emitNextSteps(yamlPath: string): void {\n const rel = isAbsolute(yamlPath) ? `./${relativeFromCwd(yamlPath)}` : yamlPath;\n process.stderr.write(`✓ wrote ${rel}\\n`);\n process.stderr.write('Next steps:\\n');\n process.stderr.write(\n ' 1. Drop these images into ./assets/ (dimensions enforced on register):\\n',\n );\n process.stderr.write(' logo.png 600×600\\n');\n process.stderr.write(' thumbnail.png 1932×828\\n');\n process.stderr.write(' screenshot-1.png 636×1048\\n');\n process.stderr.write(' screenshot-2.png 636×1048\\n');\n process.stderr.write(' screenshot-3.png 636×1048\\n');\n process.stderr.write(' 2. Run `aitcc app register` to create the mini-app.\\n');\n process.stderr.write(' (`miniAppId` is written back into ./aitcc.yaml automatically.)\\n');\n}\n\nfunction relativeFromCwd(absPath: string): string {\n const cwd = process.cwd();\n if (absPath.startsWith(`${cwd}/`)) return absPath.slice(cwd.length + 1);\n return absPath;\n}\n\nasync function fileExists(path: string): Promise<boolean> {\n try {\n await access(path);\n return true;\n } catch {\n return false;\n }\n}\n\n// Inquirer raises `ExitPromptError` (Ctrl+C / SIGINT) so the caller can\n// distinguish a deliberate abort from a programming error. We sniff\n// `err.name` rather than `instanceof` because the class lives in\n// `@inquirer/core`, which we don't depend on directly — this avoids\n// pulling in a transitive package as a top-level dep just for an\n// `instanceof` check the inquirer docs already recommend by name.\nfunction isPromptCancelled(err: unknown): boolean {\n return err instanceof Error && err.name === 'ExitPromptError';\n}\n\nasync function pickWorkspace(cookies: readonly CdpCookie[]): Promise<number> {\n const info = await fetchConsoleMemberUserInfo(cookies);\n const workspaces = info.workspaces;\n if (workspaces.length === 0) {\n process.stderr.write(\n 'Your account has no workspaces. Create one in the Apps in Toss console first.\\n',\n );\n await exitAfterFlush(ExitCode.Usage);\n }\n if (workspaces.length === 1) {\n const only = workspaces[0];\n if (!only) throw new Error('unreachable: workspaces[0] missing');\n process.stderr.write(\n `Using workspace ${only.workspaceId} (${only.workspaceName}) — only one available.\\n`,\n );\n return only.workspaceId;\n }\n const choice = await select<number>({\n message: 'Workspace:',\n choices: workspaces.map((w) => ({\n name: `${w.workspaceId} ${w.workspaceName}`,\n value: w.workspaceId,\n })),\n });\n return choice;\n}\n\nasync function pickCategories(cookies: readonly CdpCookie[]): Promise<number[]> {\n const tree = await fetchImpressionCategoryList(cookies);\n // Build a flat list of selectable leaves (sub-categories where present,\n // otherwise top-level categories). Group headings are interleaved as\n // disabled items so the user can still see the hierarchy.\n type Choice = {\n name: string;\n value: number;\n disabled?: string | true;\n };\n const choices: Choice[] = [];\n for (const group of tree) {\n if (!group.categoryGroup.isSelectable) continue;\n choices.push({\n name: `── ${group.categoryGroup.name} ──`,\n value: -group.categoryGroup.id,\n disabled: ' ',\n });\n for (const cat of group.categoryList) {\n if (cat.subCategoryList.length > 0) {\n for (const sub of cat.subCategoryList) {\n if (!sub.isSelectable) continue;\n choices.push({\n name: ` ${cat.name} › ${sub.name} [${sub.id}]`,\n value: sub.id,\n });\n }\n } else if (cat.isSelectable) {\n choices.push({\n name: ` ${cat.name} [${cat.id}]`,\n value: cat.id,\n });\n }\n }\n }\n if (choices.every((c) => c.disabled !== undefined)) {\n throw new Error('No selectable categories returned by the server.');\n }\n const picked = await checkbox<number>({\n message: 'Categories (space to toggle, enter to confirm; pick at least one):',\n choices,\n validate: (entries) => (entries.length === 0 ? 'pick at least one category' : true),\n pageSize: 20,\n });\n return picked;\n}\n\n// --- Prompt validators ---\n//\n// inquirer expects `true` for \"valid\" or a string error message otherwise.\n// Each validator mirrors the corresponding manifest-level rule so the\n// values produced by `init` will always pass `register`'s validator —\n// see `app-manifest.ts` for the source-of-truth constants/regexes.\n\nfunction validateTitleKo(raw: string): true | string {\n if (raw.trim().length === 0) return 'titleKo is required';\n if (!TITLE_KO_REGEX.test(raw)) {\n return 'only Korean/English letters, digits, spaces, and \":·?\" are allowed';\n }\n const len = countCodepointsExcludingSpaces(raw);\n if (len > MANIFEST_LIMITS.titleKoMaxCodepoints) {\n return `must be ≤ ${MANIFEST_LIMITS.titleKoMaxCodepoints} characters excluding spaces (got ${len})`;\n }\n return true;\n}\n\nfunction validateTitleEn(raw: string): true | string {\n if (raw.trim().length === 0) return 'titleEn is required';\n if (!TITLE_EN_REGEX.test(raw)) {\n return 'only English letters, digits, spaces, and \":·?\" are allowed';\n }\n const len = countCodepointsExcludingSpaces(raw);\n if (len > MANIFEST_LIMITS.titleEnMaxCodepoints) {\n return `must be ≤ ${MANIFEST_LIMITS.titleEnMaxCodepoints} characters excluding spaces (got ${len})`;\n }\n for (const word of raw.split(' ')) {\n if (word.length === 0) continue;\n if (!isTitleCaseWord(word)) {\n return `word \"${word}\" must be title-case (first letter upper, rest lower)`;\n }\n }\n return true;\n}\n\nfunction validateAppName(raw: string): true | string {\n if (!APP_NAME_REGEX.test(raw)) {\n return 'must be kebab-case starting with a lowercase letter (a-z, 0-9, hyphen)';\n }\n return true;\n}\n\nfunction validateEmail(raw: string): true | string {\n if (!isValidEmail(raw)) return 'not a valid email address';\n return true;\n}\n\nfunction validateSubtitle(raw: string): true | string {\n if (raw.trim().length === 0) return 'subtitle is required';\n if (raw.length > MANIFEST_LIMITS.subtitleMaxChars) {\n return `must be ≤ ${MANIFEST_LIMITS.subtitleMaxChars} characters (got ${raw.length})`;\n }\n return true;\n}\n\nfunction validateDescription(raw: string): true | string {\n if (raw.trim().length === 0) return 'description is required';\n const len = [...raw].length;\n if (len > MANIFEST_LIMITS.descriptionMaxCodepoints) {\n return `must be ≤ ${MANIFEST_LIMITS.descriptionMaxCodepoints} characters (got ${len})`;\n }\n return true;\n}\n","import { readFile } from 'node:fs/promises';\nimport { imageSize } from 'image-size';\n\n// Image dimension validation lives here (rather than inline in the\n// command) so a future swap of the underlying library is confined. The\n// console upload endpoint validates validWidth/validHeight server-side\n// with a hard 400, but failing locally gives the agent-plugin consumer\n// a structured error (path + expected + actual) instead of a pass-\n// through api-error reason.\n//\n// image-size reads the PNG/JPEG/etc. header directly; we pass a Buffer\n// so we can distinguish \"file missing\" (ENOENT → unreadable) from\n// \"unknown format\" (library throws → unreadable) without two code paths.\n\nexport type ImageDimensionErrorReason = 'mismatch' | 'unreadable';\n\nexport class ImageDimensionError extends Error {\n readonly path: string;\n readonly expected: string;\n readonly actual: string | undefined;\n readonly reason: ImageDimensionErrorReason;\n\n constructor(args: {\n path: string;\n expected: string;\n actual: string | undefined;\n reason: ImageDimensionErrorReason;\n message: string;\n }) {\n super(args.message);\n this.name = 'ImageDimensionError';\n this.path = args.path;\n this.expected = args.expected;\n this.actual = args.actual;\n this.reason = args.reason;\n }\n}\n\nexport interface Dimension {\n readonly width: number;\n readonly height: number;\n}\n\nfunction format(dim: Dimension): string {\n return `${dim.width}x${dim.height}`;\n}\n\nexport async function validateImageDimensions(path: string, expected: Dimension): Promise<void> {\n let buffer: Buffer;\n try {\n buffer = await readFile(path);\n } catch (err) {\n throw new ImageDimensionError({\n path,\n expected: format(expected),\n actual: undefined,\n reason: 'unreadable',\n message: `could not read image at ${path}: ${(err as Error).message}`,\n });\n }\n let dims: { width?: number; height?: number };\n try {\n dims = imageSize(buffer);\n } catch (err) {\n throw new ImageDimensionError({\n path,\n expected: format(expected),\n actual: undefined,\n reason: 'unreadable',\n message: `could not decode image header at ${path}: ${(err as Error).message}`,\n });\n }\n if (typeof dims.width !== 'number' || typeof dims.height !== 'number') {\n throw new ImageDimensionError({\n path,\n expected: format(expected),\n actual: undefined,\n reason: 'unreadable',\n message: `image header at ${path} did not expose width/height`,\n });\n }\n if (dims.width !== expected.width || dims.height !== expected.height) {\n const actual = `${dims.width}x${dims.height}`;\n throw new ImageDimensionError({\n path,\n expected: format(expected),\n actual,\n reason: 'mismatch',\n message: `image ${path} has dimensions ${actual}; expected ${format(expected)}`,\n });\n }\n}\n\n// Canonical dimension specs for the register flow. Centralising here keeps\n// the command and the payload builder agreeing on the same source of truth.\nexport const DIMENSIONS = {\n logo: { width: 600, height: 600 },\n horizontalThumbnail: { width: 1932, height: 828 },\n verticalScreenshot: { width: 636, height: 1048 },\n horizontalScreenshot: { width: 1504, height: 741 },\n} as const;\n","import type { MiniAppImageEntry, MiniAppSubmitPayload } from '../api/mini-apps.js';\nimport type { AppManifest } from '../config/app-manifest.js';\n\n// Pure transformation from a loaded AppManifest + the URLs produced by\n// the upload step into the `{miniApp, impression}` submit body. The\n// structure mirrors the `Xc` function from the console bundle (see\n// VALIDATION-RULES.md in the local `.playwright-mcp/`). Dog-food task\n// #23 captures the first real network exchange and will either confirm\n// this shape or correct it here.\n\nexport interface UploadedImageUrls {\n readonly logo: string;\n readonly logoDarkMode: string | undefined;\n readonly horizontalThumbnail: string;\n readonly verticalScreenshots: readonly string[];\n readonly horizontalScreenshots: readonly string[];\n}\n\nexport function buildSubmitPayload(\n manifest: AppManifest,\n urls: UploadedImageUrls,\n): MiniAppSubmitPayload {\n const images: MiniAppImageEntry[] = [\n { imageUrl: urls.horizontalThumbnail, imageType: 'THUMBNAIL', orientation: 'HORIZONTAL' },\n ...urls.verticalScreenshots.map<MiniAppImageEntry>((u) => ({\n imageUrl: u,\n imageType: 'PREVIEW',\n orientation: 'VERTICAL',\n })),\n ...urls.horizontalScreenshots.map<MiniAppImageEntry>((u) => ({\n imageUrl: u,\n imageType: 'PREVIEW',\n orientation: 'HORIZONTAL',\n })),\n ];\n\n const miniApp: MiniAppSubmitPayload['miniApp'] = {\n title: manifest.titleKo,\n titleEn: manifest.titleEn,\n appName: manifest.appName,\n iconUri: urls.logo,\n status: 'PREPARE',\n csEmail: manifest.csEmail,\n description: manifest.subtitle,\n detailDescription: manifest.description,\n images,\n ...(urls.logoDarkMode !== undefined ? { darkModeIconUri: urls.logoDarkMode } : {}),\n ...(manifest.homePageUri !== undefined ? { homePageUri: manifest.homePageUri } : {}),\n };\n\n const impression: MiniAppSubmitPayload['impression'] = {\n keywordList: manifest.keywords,\n categoryIds: manifest.categoryIds,\n };\n\n return { miniApp, impression };\n}\n","import { readFile } from 'node:fs/promises';\nimport { basename } from 'node:path';\nimport {\n type CreateMiniAppResult,\n createMiniApp,\n type UploadParams,\n uploadMiniAppResource,\n} from '../api/mini-apps.js';\nimport type { CdpCookie } from '../cdp.js';\nimport {\n type AppManifest,\n loadAppManifest,\n ManifestError,\n resolveManifestPath,\n} from '../config/app-manifest.js';\nimport {\n DIMENSIONS,\n ImageDimensionError,\n validateImageDimensions,\n} from '../config/image-validator.js';\nimport { findProjectContext, writeProjectMiniAppId } from '../config/project-context.js';\nimport { ExitCode } from '../exit.js';\nimport { exitAfterFlush } from '../flush.js';\nimport {\n emitFailureFromError,\n emitJson,\n printContextHeader,\n resolveWorkspaceContext,\n} from './_shared.js';\nimport { buildSubmitPayload, type UploadedImageUrls } from './register-payload.js';\n\n// `runRegister` is the testable seam for `aitcc app register`. The public\n// command (defined in `app.ts`) is a thin citty wrapper that supplies the\n// real `uploadMiniAppResource` and `createMiniApp` implementations;\n// tests pass stubs so each branch of the documented `--json` contract\n// gets pinned byte-for-byte without spawning a subprocess.\n//\n// --json contract (consumed by agent-plugin):\n//\n// success:\n// { ok: true, workspaceId, appId, reviewState, consoleUrl } exit 0\n// consoleUrl:\n// https://apps-in-toss.toss.im/console/workspace/<wid>/mini-app/<id>\n// (null when the server response omitted miniAppId)\n//\n// failures:\n// { ok: false, reason: 'no-workspace-selected' } exit 2\n// { ok: false, reason: 'invalid-config', message } exit 2\n// { ok: false, reason: 'missing-required-field', field, message } exit 2\n// { ok: false, reason: 'image-dimension-mismatch',\n// path, expected, actual, message } exit 2\n// { ok: false, reason: 'image-unreadable', path, message } exit 2\n// { ok: true, authenticated: false } exit 10\n// { ok: false, reason: 'network-error', message } exit 11\n// { ok: false, reason: 'api-error',\n// status?, errorCode?, message } exit 17\n//\n// --dry-run:\n// { ok: true, dryRun: true, workspaceId, payload } exit 0\n// (no uploads, no submit — manifest + image dimensions are still\n// validated so dry-run catches the same local errors as a real run.)\n//\n// --accept-terms required for real submits:\n// The console UI gates \"검토 요청하기\" on several mandatory legal-\n// agreement checkboxes (common + category-dependent — see\n// VALIDATION-RULES.md). We can't see those on the wire yet (payload\n// shape is inferred), so the CLI enforces the gate locally: submit\n// refuses without --accept-terms. The flag is not required for\n// --dry-run.\n// { ok: false, reason: 'terms-not-accepted', message } exit 2\n\nexport interface RegisterArgs {\n readonly workspace?: string | undefined;\n readonly config?: string | undefined;\n readonly json: boolean;\n readonly dryRun?: boolean | undefined;\n readonly acceptTerms?: boolean | undefined;\n}\n\nexport interface RegisterDeps {\n readonly cwd?: string;\n readonly uploadImpl?: (params: UploadParams) => Promise<string>;\n readonly submitImpl?: (\n workspaceId: number,\n payload: ReturnType<typeof buildSubmitPayload>,\n cookies: readonly CdpCookie[],\n ) => Promise<CreateMiniAppResult>;\n}\n\n// `runRegister` returns `Promise<void>` as a type-level handshake — at\n// runtime every code path either awaits `exitAfterFlush` (which calls\n// `process.exit` and never returns) or bubbles a thrown exception. A\n// future maintainer should not try to `catch` the absence of a return\n// value as \"success\"; the success signal is `process.exit(0)` itself.\n//\n// `deps` defaults to `{}` so the citty wrapper (`app.ts`) doesn't need\n// to pass a literal every call; tests override specific fields.\nexport async function runRegister(args: RegisterArgs, deps: RegisterDeps = {}): Promise<void> {\n const ctx = await resolveWorkspaceContext({\n json: args.json,\n ...(args.workspace !== undefined ? { workspace: args.workspace } : {}),\n });\n if (!ctx) return;\n const { session, workspaceId } = ctx;\n printContextHeader(ctx, { json: args.json });\n\n const manifest = await loadAndValidateManifest(args, deps);\n if (!manifest) return;\n\n // --accept-terms gate: required for real submits only. --dry-run\n // skips it so users can iterate on their manifest without being\n // forced to attest to the legal-agreement checkboxes each time.\n if (!args.dryRun && !args.acceptTerms) {\n emitTermsNotAccepted(args.json);\n await exitAfterFlush(ExitCode.Usage);\n return;\n }\n\n try {\n if (args.dryRun) {\n // Emit the payload with placeholder URLs so the user can\n // inspect exactly what would be sent without spending a round\n // of uploads. Useful during dog-food verification when the\n // inferred payload shape is in flight.\n const placeholderUrls: UploadedImageUrls = {\n logo: '<dry-run:logo>',\n logoDarkMode: manifest.logoDarkMode !== undefined ? '<dry-run:logoDarkMode>' : undefined,\n horizontalThumbnail: '<dry-run:horizontalThumbnail>',\n verticalScreenshots: manifest.verticalScreenshots.map(\n (_, i) => `<dry-run:verticalScreenshots[${i}]>`,\n ),\n horizontalScreenshots: manifest.horizontalScreenshots.map(\n (_, i) => `<dry-run:horizontalScreenshots[${i}]>`,\n ),\n };\n const payload = buildSubmitPayload(manifest, placeholderUrls);\n emitDryRun(args.json, workspaceId, payload);\n return exitAfterFlush(ExitCode.Ok);\n }\n\n const urls = await uploadAllImages(workspaceId, manifest, session.cookies, deps);\n const payload = buildSubmitPayload(manifest, urls);\n const submitImpl = deps.submitImpl ?? ((wid, p, c) => createMiniApp(wid, p, c));\n const result = await submitImpl(workspaceId, payload, session.cookies);\n emitSuccess(args.json, workspaceId, result);\n await persistMiniAppIdToProject(args.json, result.miniAppId, deps.cwd ?? process.cwd());\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureAndExit(args.json, err);\n }\n}\n\nasync function loadAndValidateManifest(\n args: RegisterArgs,\n deps: RegisterDeps,\n): Promise<AppManifest | null> {\n const cwd = deps.cwd ?? process.cwd();\n let manifest: AppManifest;\n try {\n const manifestPath = await resolveManifestPath(args.config, cwd);\n manifest = await loadAppManifest(manifestPath);\n } catch (err) {\n if (err instanceof ManifestError) {\n emitManifestError(args.json, err);\n await exitAfterFlush(ExitCode.Usage);\n return null;\n }\n throw err;\n }\n\n // Image dimension checks run after manifest validation because they\n // need paths that the manifest already resolved to absolute.\n try {\n await validateImageDimensions(manifest.logo, DIMENSIONS.logo);\n if (manifest.logoDarkMode !== undefined) {\n await validateImageDimensions(manifest.logoDarkMode, DIMENSIONS.logo);\n }\n await validateImageDimensions(manifest.horizontalThumbnail, DIMENSIONS.horizontalThumbnail);\n for (const p of manifest.verticalScreenshots) {\n await validateImageDimensions(p, DIMENSIONS.verticalScreenshot);\n }\n for (const p of manifest.horizontalScreenshots) {\n await validateImageDimensions(p, DIMENSIONS.horizontalScreenshot);\n }\n } catch (err) {\n if (err instanceof ImageDimensionError) {\n emitImageDimensionError(args.json, err);\n await exitAfterFlush(ExitCode.Usage);\n return null;\n }\n throw err;\n }\n\n return manifest;\n}\n\nasync function uploadAllImages(\n workspaceId: number,\n manifest: AppManifest,\n cookies: readonly CdpCookie[],\n deps: RegisterDeps,\n): Promise<UploadedImageUrls> {\n // Serial on purpose: dog-food task #23 has not yet confirmed that the\n // console's `/resource/:wid/upload` endpoint tolerates concurrent\n // POSTs from the same session. `Promise.all` would shave a few seconds\n // off a 5-image upload, but until we've observed the server under\n // parallel load, the failure mode (\"429? 503? silent drop?\") is\n // unknown and a first-registration flake is much more expensive to\n // debug than a slower linear run.\n const uploadImpl = deps.uploadImpl ?? ((p) => uploadMiniAppResource(p));\n\n const logo = await uploadOne(uploadImpl, {\n workspaceId,\n validWidth: DIMENSIONS.logo.width,\n validHeight: DIMENSIONS.logo.height,\n cookies,\n path: manifest.logo,\n });\n const logoDarkMode =\n manifest.logoDarkMode !== undefined\n ? await uploadOne(uploadImpl, {\n workspaceId,\n validWidth: DIMENSIONS.logo.width,\n validHeight: DIMENSIONS.logo.height,\n cookies,\n path: manifest.logoDarkMode,\n })\n : undefined;\n const horizontalThumbnail = await uploadOne(uploadImpl, {\n workspaceId,\n validWidth: DIMENSIONS.horizontalThumbnail.width,\n validHeight: DIMENSIONS.horizontalThumbnail.height,\n cookies,\n path: manifest.horizontalThumbnail,\n });\n const verticalScreenshots: string[] = [];\n for (const p of manifest.verticalScreenshots) {\n verticalScreenshots.push(\n await uploadOne(uploadImpl, {\n workspaceId,\n validWidth: DIMENSIONS.verticalScreenshot.width,\n validHeight: DIMENSIONS.verticalScreenshot.height,\n cookies,\n path: p,\n }),\n );\n }\n const horizontalScreenshots: string[] = [];\n for (const p of manifest.horizontalScreenshots) {\n horizontalScreenshots.push(\n await uploadOne(uploadImpl, {\n workspaceId,\n validWidth: DIMENSIONS.horizontalScreenshot.width,\n validHeight: DIMENSIONS.horizontalScreenshot.height,\n cookies,\n path: p,\n }),\n );\n }\n return { logo, logoDarkMode, horizontalThumbnail, verticalScreenshots, horizontalScreenshots };\n}\n\nasync function uploadOne(\n uploadImpl: (params: UploadParams) => Promise<string>,\n input: {\n workspaceId: number;\n validWidth: number;\n validHeight: number;\n cookies: readonly CdpCookie[];\n path: string;\n },\n): Promise<string> {\n const buffer = await readFile(input.path);\n return uploadImpl({\n workspaceId: input.workspaceId,\n validWidth: input.validWidth,\n validHeight: input.validHeight,\n cookies: input.cookies,\n file: {\n buffer,\n fileName: basename(input.path),\n contentType: 'image/png',\n },\n });\n}\n\n// Errors that touch `categoryIds` always reference that key explicitly in\n// the message (see `config/app-manifest.ts`). Point the user at\n// `aitcc app categories --selectable` so they don't have to hunt for a\n// live example — this is the only plain-text hint we surface beyond the\n// raw message, and we keep the JSON payload unchanged so the contract\n// with agent-plugin is stable.\nfunction categoryHintFor(err: ManifestError): string | null {\n const target = err.field ?? '';\n if (target === 'categoryIds' || /categoryIds/.test(err.message)) {\n return 'Tip: run `aitcc app categories --selectable` to list valid category ids.';\n }\n return null;\n}\n\nfunction emitManifestError(json: boolean, err: ManifestError): void {\n if (json) {\n if (err.kind === 'missing-required-field') {\n emitJson({\n ok: false,\n reason: 'missing-required-field',\n field: err.field ?? null,\n message: err.message,\n });\n } else {\n emitJson({ ok: false, reason: 'invalid-config', message: err.message });\n }\n } else {\n process.stderr.write(`${err.message}\\n`);\n const hint = categoryHintFor(err);\n if (hint) process.stderr.write(`${hint}\\n`);\n }\n}\n\nfunction emitImageDimensionError(json: boolean, err: ImageDimensionError): void {\n if (json) {\n if (err.reason === 'mismatch') {\n emitJson({\n ok: false,\n reason: 'image-dimension-mismatch',\n path: err.path,\n expected: err.expected,\n actual: err.actual ?? null,\n message: err.message,\n });\n } else {\n // 'unreadable' covers missing files, permission errors, and\n // decode failures — distinct from a genuine dimension mismatch\n // so agent-plugin can branch (e.g., \"path typo\" vs \"resize me\").\n emitJson({\n ok: false,\n reason: 'image-unreadable',\n path: err.path,\n message: err.message,\n });\n }\n } else {\n process.stderr.write(`${err.message}\\n`);\n }\n}\n\nfunction emitTermsNotAccepted(json: boolean): void {\n const message =\n 'The console requires several legal-agreement checkboxes before submitting a mini-app for review. ' +\n 'Re-run with --accept-terms to attest that you have read and agree to each of them ' +\n '(see VALIDATION-RULES.md or the console UI), or use --dry-run to preview the payload without submitting.';\n if (json) {\n emitJson({ ok: false, reason: 'terms-not-accepted', message });\n } else {\n process.stderr.write(`${message}\\n`);\n }\n}\n\nfunction emitDryRun(\n json: boolean,\n workspaceId: number,\n payload: ReturnType<typeof buildSubmitPayload>,\n): void {\n if (json) {\n emitJson({ ok: true, dryRun: true, workspaceId, payload });\n } else {\n process.stdout.write('[dry-run] Would POST to ');\n process.stdout.write(\n `https://apps-in-toss.toss.im/console/api-public/v3/appsintossconsole/workspaces/${workspaceId}/mini-app/review\\n`,\n );\n process.stdout.write(`${JSON.stringify(payload, null, 2)}\\n`);\n }\n}\n\nfunction consoleUrlFor(workspaceId: number, appId: string | number): string {\n return `https://apps-in-toss.toss.im/console/workspace/${workspaceId}/mini-app/${appId}`;\n}\n\nfunction emitSuccess(json: boolean, workspaceId: number, result: CreateMiniAppResult): void {\n const consoleUrl =\n result.miniAppId !== undefined ? consoleUrlFor(workspaceId, result.miniAppId) : null;\n if (json) {\n emitJson({\n ok: true,\n workspaceId,\n appId: result.miniAppId ?? null,\n reviewState: result.reviewState ?? null,\n consoleUrl,\n });\n } else {\n process.stdout.write(\n `Registered mini-app ${result.miniAppId ?? '(id unknown)'} in workspace ${workspaceId}` +\n ` (reviewState=${result.reviewState ?? 'unknown'}).\\n`,\n );\n if (consoleUrl !== null) {\n process.stdout.write(`🔗 console: ${consoleUrl}\\n`);\n }\n }\n}\n\n// Thin wrapper kept for local readability — the real dispatch lives in\n// `_shared.ts::emitFailureFromError` and is shared with app/keys/\n// members/workspace so every command's auth/network/api-error JSON\n// shape agrees.\nasync function emitFailureAndExit(json: boolean, err: unknown): Promise<void> {\n return emitFailureFromError(json, err);\n}\n\n// After a successful submit, persist the returned miniAppId back into\n// the resolved project-context file (`aitcc.yaml`/`aitcc.json`) so\n// follow-up commands like `app status` / `app deploy` resolve the same\n// app without an explicit `--app`. Skipped silently in --dry-run (we\n// never reach this code path) and when the response omitted the id.\n//\n// The write is best-effort: a failure here MUST NOT change the exit\n// code, since the submit itself already succeeded — the user can\n// recover by adding `miniAppId: <id>` manually. Errors are surfaced\n// only on stderr (suppressed under --json so the stdout contract\n// stays single-line). The \"no project file at all\" case prints a one-\n// line stderr hint pointing at the discoverability win.\nasync function persistMiniAppIdToProject(\n json: boolean,\n miniAppId: string | number | undefined,\n cwd: string,\n): Promise<void> {\n if (typeof miniAppId !== 'number' || !Number.isInteger(miniAppId) || miniAppId <= 0) {\n return;\n }\n let ctx: Awaited<ReturnType<typeof findProjectContext>>;\n try {\n ctx = await findProjectContext(cwd);\n } catch (err) {\n if (!json) {\n const detail = err instanceof Error ? err.message : String(err);\n process.stderr.write(\n `warning: could not read project context to persist miniAppId: ${detail}\\n`,\n );\n }\n return;\n }\n if (ctx === null) {\n if (!json) {\n process.stderr.write(\n `tip: drop an aitcc.yaml with \\`miniAppId: ${miniAppId}\\` in your project root to skip --app on later commands.\\n`,\n );\n }\n return;\n }\n try {\n const outcome = await writeProjectMiniAppId(ctx.source, miniAppId);\n if (!json && outcome.status === 'written') {\n process.stderr.write(`Updated ${ctx.source} with miniAppId: ${miniAppId}.\\n`);\n }\n } catch (err) {\n if (!json) {\n const detail = err instanceof Error ? err.message : String(err);\n process.stderr.write(`warning: could not persist miniAppId to ${ctx.source}: ${detail}\\n`);\n }\n }\n}\n","import { mkdir, writeFile } from 'node:fs/promises';\nimport { resolve as resolvePath } from 'node:path';\nimport { defineCommand } from 'citty';\nimport {\n fetchAppEventCatalogs,\n fetchAppServiceStatus,\n fetchAppTemplates,\n fetchBundles,\n fetchBundleTestLinks,\n fetchCerts,\n fetchConversionMetrics,\n fetchDeployedBundle,\n fetchImpressionCategoryList,\n fetchMiniAppRatings,\n fetchMiniApps,\n fetchMiniAppWithDraft,\n fetchReviewStatus,\n fetchShareRewards,\n fetchSmartMessageCampaigns,\n fetchUserReports,\n issueCert,\n type MetricsTimeUnit,\n postBundleMemo,\n postBundleRelease,\n postBundleReview,\n postBundleReviewWithdrawal,\n postBundleTestPush,\n postDeploymentsComplete,\n postDeploymentsInitialize,\n putBundleToUploadUrl,\n type RatingSortDirection,\n type RatingSortField,\n revokeCert,\n TEMPLATE_CONTENT_REACH_TYPES,\n type TemplateContentReachType,\n} from '../api/mini-apps.js';\nimport { ExitCode } from '../exit.js';\nimport { exitAfterFlush } from '../flush.js';\nimport { readSession } from '../session.js';\nimport {\n emitFailureFromError,\n emitJson,\n emitNotAuthenticated,\n printContextHeader,\n requireMiniAppId,\n resolveAppOrFail,\n resolveWorkspaceContext,\n} from './_shared.js';\nimport { runDeploy } from './app-deploy.js';\nimport { runAppInit } from './app-init.js';\nimport { runRegister } from './register.js';\n\n// --json contract (consumed by agent-plugin):\n//\n// app ls [--workspace <id>]:\n// { ok: true, workspaceId, hasPolicyViolation,\n// apps: [{id, name, reviewState?, status, locked, lockReason, extra}] } exit 0\n// { ok: false, reason: 'no-workspace-selected' } exit 2\n// { ok: false, reason: 'invalid-id', message } exit 2\n//\n// `status` is derived (kebab-case enum: not-submitted | under-review | approved |\n// approved-with-edits | rejected | in-service | unknown). `locked` is\n// `approvalType === 'REVIEW'` (see docs/api/mini-apps.md \"REVIEW lock 권위\").\n// Auth/network/api failures follow the shared contract from workspace/whoami\n// (ok: true authenticated: false exit 10, network-error exit 11, api-error exit 17).\n\n// Best-effort match of review-status entries against mini-app summaries.\n// The list endpoint and the review-status endpoint key off the same id,\n// but we don't assume the field name is uniform — we compare by `.id` on\n// each record, falling back to `miniAppId` / `appId` (same order as the\n// list normaliser). Exported so the join semantics are unit-testable.\n// Returns `null` if no plausible match; callers render that as \"no review\n// status\" in the output rather than a failure.\nexport function findReviewEntry(\n reviewEntries: readonly Readonly<Record<string, unknown>>[],\n appId: string | number,\n): Readonly<Record<string, unknown>> | null {\n const target = String(appId);\n for (const entry of reviewEntries) {\n const candidate = entry.id ?? entry.miniAppId ?? entry.appId;\n if (candidate !== undefined && String(candidate) === target) return entry;\n }\n return null;\n}\n\nexport function reviewStateFor(\n entry: Readonly<Record<string, unknown>> | null,\n): string | undefined {\n if (!entry) return undefined;\n const raw = entry.reviewState ?? entry.status;\n return typeof raw === 'string' ? raw : undefined;\n}\n\nconst lsCommand = defineCommand({\n meta: {\n name: 'ls',\n description: 'List mini-apps in the selected workspace.',\n },\n args: {\n workspace: {\n type: 'string',\n description: 'Workspace ID. Defaults to the selected workspace (`aitcc workspace use`).',\n },\n json: { type: 'boolean', description: 'Emit machine-readable JSON to stdout.', default: false },\n },\n async run({ args }) {\n const ctx = await resolveWorkspaceContext(args);\n if (!ctx) return;\n const { session, workspaceId } = ctx;\n printContextHeader(ctx, { json: args.json });\n\n try {\n // List + review-status are independent read endpoints. Fire in parallel\n // so a slow endpoint doesn't serialise the wait. Review-status failures\n // currently propagate (rather than being downgraded to \"unknown\n // review\") because they almost always indicate a shared auth/network\n // problem — if that ever stops being true we can degrade gracefully.\n const [apps, review] = await Promise.all([\n fetchMiniApps(workspaceId, session.cookies),\n fetchReviewStatus(workspaceId, session.cookies),\n ]);\n\n // The workspace-level review-status entry only exposes serviceStatus +\n // a few flags — not the approvalType/current/draft trio that drives\n // the \"approved-with-edits\" vs \"under-review\" distinction. So per-app\n // /with-draft is the only authority. Fan out in parallel and degrade\n // a single failure to `unknown` so one stale id doesn't block the\n // rest of the list.\n const drafts = await Promise.all(\n apps.map(async (app) => {\n const numericId = typeof app.id === 'number' ? app.id : Number(app.id);\n if (!Number.isFinite(numericId)) return null;\n try {\n const env = await fetchMiniAppWithDraft(workspaceId, numericId, session.cookies);\n return deriveReviewState(env);\n } catch {\n return null;\n }\n }),\n );\n\n const rows = apps.map((app, i) => {\n const entry = findReviewEntry(review.miniApps, app.id);\n const derived = drafts[i] ?? null;\n const ls = deriveLsStatus(derived, serviceStatusFor(entry));\n const reviewState = reviewStateFor(entry);\n return { app, entry, derived, ls, reviewState };\n });\n\n if (args.json) {\n const joined = rows.map(({ app, ls, reviewState }) => ({\n id: app.id,\n name: app.name ?? null,\n ...(reviewState !== undefined ? { reviewState } : {}),\n status: ls.status,\n locked: ls.locked,\n lockReason: ls.lockReason,\n extra: app.extra,\n }));\n emitJson({\n ok: true,\n workspaceId,\n hasPolicyViolation: review.hasPolicyViolation,\n apps: joined,\n });\n return exitAfterFlush(ExitCode.Ok);\n }\n\n if (apps.length === 0) {\n process.stdout.write(`No apps in workspace ${workspaceId}.\\n`);\n if (review.hasPolicyViolation) {\n process.stderr.write('Note: workspace-wide policy violation flag is set.\\n');\n }\n return exitAfterFlush(ExitCode.Ok);\n }\n // NO_COLOR honoured per the project convention; isTTY gate keeps ANSI\n // escapes out of pipes (`update-check.ts` bails earlier on non-TTY for\n // the same reason).\n const useColor = process.stdout.isTTY && !process.env.NO_COLOR;\n const yellow = useColor ? '\\x1b[33m' : '';\n const reset = useColor ? '\\x1b[0m' : '';\n for (const { app, ls } of rows) {\n // Defensive: the upstream mini-app payload shape is not yet fully\n // observed (no registered apps in our workspaces). Tighten this\n // once sdk-example is registered and `name` is confirmed required.\n const name = app.name ?? '(unnamed)';\n const lockMark = ls.locked ? ` ${yellow}🔒${reset}` : '';\n process.stdout.write(`${app.id}\\t${name}\\t${ls.status}${lockMark}\\n`);\n }\n if (review.hasPolicyViolation) {\n process.stderr.write('Note: workspace-wide policy violation flag is set.\\n');\n }\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\n// --json contract (consumed by agent-plugin):\n//\n// app show <id> [--workspace <id>] [--view draft|current|merged]:\n// { ok: true, workspaceId, appId, view, miniApp: {...},\n// reviewState, locked, lockReason,\n// serviceStatus, shutdownCandidateStatus, scheduledShutdownAt } exit 0\n// { ok: false, reason: 'no-workspace-selected' } exit 2\n// { ok: false, reason: 'invalid-id', message } exit 2\n// { ok: false, reason: 'app-not-found', appId } exit 2\n//\n// app show <id> --diff:\n// { ok: true, workspaceId, appId, diffMode: true,\n// reviewState, locked, lockReason,\n// serviceStatus, shutdownCandidateStatus, scheduledShutdownAt,\n// diff: { hasDraft, hasCurrent, changed: [{field, draft, current}], unchangedCount } } exit 0\n//\n// `view` picks which part of the with-draft envelope to surface:\n// - `draft` (default) — the editor's latest state, populated as soon as\n// the app is created. This is what `app register` just wrote; it's the\n// only reliable view until the app is approved and published.\n// - `current` — the published/reviewed record end users see. Empty until\n// the app's first approval, so defaulting here would hide almost every\n// field we care about — hence the default is `draft`.\n// - `merged` — current with draft overlaid on top (draft wins per field).\n// Useful once both exist and the user wants the \"authoritative\" snapshot.\n//\n// The `--view` flag intentionally never falls back on its own. If the\n// caller asks for `current` on an unreviewed app they get `miniApp: null`\n// with `view: 'current'` so agent-plugin can tell the two cases apart.\n//\n// `reviewState`/`locked`/`lockReason` are derived from the envelope-level\n// `approvalType` + `current`/`draft`/`rejectedMessage` combo (same shape as\n// `app status`). `serviceStatus`/`shutdownCandidateStatus`/`scheduledShutdownAt`\n// come from the `/review-status` endpoint and may be `null` if that call\n// fails (we still return the review derivation since it's authoritative on\n// its own — see docs/api/mini-apps.md \"REVIEW lock 권위\").\nexport function pickMiniAppView(\n envelope: { current: Record<string, unknown> | null; draft: Record<string, unknown> | null },\n view: 'draft' | 'current' | 'merged',\n): Record<string, unknown> | null {\n const extract = (side: Record<string, unknown> | null): Record<string, unknown> | null => {\n if (side === null) return null;\n const ma = side.miniApp;\n if (ma !== null && typeof ma === 'object' && !Array.isArray(ma)) {\n return ma as Record<string, unknown>;\n }\n return null;\n };\n const draft = extract(envelope.draft);\n const current = extract(envelope.current);\n if (view === 'draft') return draft;\n if (view === 'current') return current;\n if (current !== null && draft !== null) return { ...current, ...draft };\n return draft ?? current;\n}\n\n// Whitelist + shallow comparison for `app show --diff`. Deep recursive\n// diffs over arbitrary console payloads aren't useful here — most fields\n// are server-controlled and noisy. We pick the top-level fields the user\n// actually edits via `app register`, plus two shallow `impression`\n// signals (keywordList, first categoryPath name).\n//\n// Each entry maps a stable diff field name (the key surfaced in JSON +\n// printed to stdout) to a getter that pulls the value out of the\n// `miniApp` view returned by `pickMiniAppView` (which already has\n// `impression` nested as a read-side convenience — see docs/api/mini-apps.md\n// \"Update-mode payload\" for why we don't strip it on read).\ntype DiffGetter = (m: Record<string, unknown>) => unknown;\nconst DIFF_FIELDS: ReadonlyArray<readonly [string, DiffGetter]> = [\n ['title', (m) => m.title],\n ['titleEn', (m) => m.titleEn],\n ['appName', (m) => m.appName],\n ['description', (m) => m.description],\n ['detailDescription', (m) => m.detailDescription],\n ['homePageUri', (m) => m.homePageUri],\n ['csEmail', (m) => m.csEmail],\n ['iconUri', (m) => m.iconUri],\n ['darkModeIconUri', (m) => m.darkModeIconUri],\n [\n 'impression.keywordList',\n (m) => {\n const imp = m.impression;\n if (imp === null || typeof imp !== 'object' || Array.isArray(imp)) return undefined;\n const kw = (imp as Record<string, unknown>).keywordList;\n return Array.isArray(kw) ? kw : undefined;\n },\n ],\n [\n 'impression.categoryPath',\n (m) => {\n // Just the first path's name parts joined \"group > category > subCategory\".\n // Single string keeps the diff readable; full path objects are noisy.\n const imp = m.impression;\n if (imp === null || typeof imp !== 'object' || Array.isArray(imp)) return undefined;\n const paths = (imp as Record<string, unknown>).categoryPaths;\n if (!Array.isArray(paths) || paths.length === 0) return undefined;\n const first = paths[0];\n if (first === null || typeof first !== 'object') return undefined;\n const fp = first as Record<string, unknown>;\n const parts: string[] = [];\n for (const key of ['group', 'category', 'subCategory']) {\n const node = fp[key];\n if (node !== null && typeof node === 'object') {\n const nm = (node as Record<string, unknown>).name;\n if (typeof nm === 'string') parts.push(nm);\n }\n }\n return parts.join(' > ');\n },\n ],\n];\n\nexport interface DiffEntry {\n readonly field: string;\n readonly draft: unknown;\n readonly current: unknown;\n}\n\nexport interface MiniAppDiff {\n readonly hasDraft: boolean;\n readonly hasCurrent: boolean;\n readonly changed: readonly DiffEntry[];\n readonly unchangedCount: number;\n}\n\n// Equality used by the diff: structural for arrays/objects, strict for\n// primitives. Arrays compare element-by-element with the same predicate;\n// objects key-by-key. Cycles aren't a concern — the with-draft response is\n// JSON, no self-references.\nfunction diffEquals(a: unknown, b: unknown): boolean {\n if (a === b) return true;\n if (a === undefined || b === undefined) return false;\n if (a === null || b === null) return false;\n if (Array.isArray(a) && Array.isArray(b)) {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (!diffEquals(a[i], b[i])) return false;\n }\n return true;\n }\n if (typeof a === 'object' && typeof b === 'object') {\n const ao = a as Record<string, unknown>;\n const bo = b as Record<string, unknown>;\n const ak = Object.keys(ao);\n const bk = Object.keys(bo);\n if (ak.length !== bk.length) return false;\n for (const k of ak) {\n if (!diffEquals(ao[k], bo[k])) return false;\n }\n return true;\n }\n return false;\n}\n\nexport function compareMiniAppViews(\n draft: Record<string, unknown> | null,\n current: Record<string, unknown> | null,\n): MiniAppDiff {\n const hasDraft = draft !== null;\n const hasCurrent = current !== null;\n if (!hasDraft || !hasCurrent) {\n return { hasDraft, hasCurrent, changed: [], unchangedCount: 0 };\n }\n const changed: DiffEntry[] = [];\n let unchangedCount = 0;\n for (const [field, getter] of DIFF_FIELDS) {\n // We pass the non-null branches so getters don't have to re-check.\n const d = getter(draft);\n const c = getter(current);\n if (d === undefined && c === undefined) continue;\n if (diffEquals(d, c)) {\n unchangedCount++;\n } else {\n changed.push({ field, draft: d, current: c });\n }\n }\n return { hasDraft, hasCurrent, changed, unchangedCount };\n}\n\n// Plain-mode rendering of a single diff value. Long strings collapse to\n// `<N> chars`; arrays show the count. Goal is \"scannable in a terminal\",\n// not \"round-trippable\" — the JSON path keeps the raw values for that.\nfunction formatDiffValue(v: unknown): string {\n if (v === null) return 'null';\n if (v === undefined) return '-';\n if (typeof v === 'string') {\n // Codepoint count (not UTF-16 length) on both the threshold and the\n // label — otherwise a string with surrogate-pair emoji could trip the\n // threshold but report a count that looks too small.\n const cp = [...v].length;\n if (cp > 60) return `${cp} chars`;\n return JSON.stringify(v);\n }\n if (Array.isArray(v)) {\n return `[${v.length} items]`;\n }\n if (typeof v === 'object') {\n return '<object>';\n }\n return String(v);\n}\n\n// Service-status string → human label. Unknown values pass through verbatim\n// (we'd rather show a raw enum we don't recognise than swallow it).\nfunction describeServiceStatus(s: string): string {\n if (s === 'PREPARE') return 'PREPARE (출시 전 — release 토글 미사용)';\n if (s === 'RUNNING') return 'RUNNING (출시 중)';\n if (s === 'STOPPED') return 'STOPPED (운영 중지)';\n return s;\n}\n\nconst showCommand = defineCommand({\n meta: {\n name: 'show',\n description:\n 'Show full details of a mini-app, including fields only visible in the draft view.',\n },\n args: {\n id: {\n type: 'positional',\n description:\n 'Mini-app ID (the numeric `appId` from `app ls` or `app register`). Optional when `aitcc.yaml` provides `miniAppId`.',\n required: false,\n },\n workspace: {\n type: 'string',\n description: 'Workspace ID. Defaults to the selected workspace.',\n },\n view: {\n type: 'string',\n description: 'Which view to render: `draft` (default), `current`, or `merged`.',\n default: 'draft',\n },\n diff: {\n type: 'boolean',\n description:\n 'Compare draft vs current view across the fields users edit (title, description, ' +\n 'iconUri, keywords, category, …). Shallow whitelist — overrides --view.',\n default: false,\n },\n json: { type: 'boolean', description: 'Emit machine-readable JSON to stdout.', default: false },\n },\n async run({ args }) {\n const view = args.view;\n if (view !== 'draft' && view !== 'current' && view !== 'merged') {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'invalid-config',\n field: 'view',\n message: `--view must be one of draft|current|merged (got ${JSON.stringify(view)})`,\n });\n } else {\n process.stderr.write(`app show: invalid --view ${JSON.stringify(view)}\\n`);\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n\n // `--diff` is a different mode of `app show`, not a view selector. If\n // the user passes both we don't hard-fail (it's harmless and keeps\n // shell aliases like `aitcc app show $@ --diff` working) — just warn\n // on stderr and let --diff win. Suppressed under `--json` so machine\n // consumers don't see warning chatter; `diffMode: true` in the JSON\n // payload tells them which mode actually ran.\n if (args.diff && args.view !== 'draft' && !args.json) {\n process.stderr.write(\n `app show: --diff overrides --view ${JSON.stringify(args.view)} (ignored)\\n`,\n );\n }\n\n const ctx = await resolveAppOrFail({\n json: args.json,\n appIdRaw: args.id,\n ...(args.workspace !== undefined ? { workspace: args.workspace } : {}),\n });\n if (!ctx) return;\n const appId = await requireMiniAppId(ctx, args.json);\n if (appId === null) return;\n printContextHeader(ctx, { json: args.json });\n const { session, workspaceId } = ctx;\n\n try {\n // Fire both requests in parallel — the service-status endpoint is\n // best-effort: if it fails we still want the review derivation,\n // which is authoritative on its own (see docs/api/mini-apps.md\n // \"REVIEW lock 권위\").\n const [envelope, service] = await Promise.all([\n fetchMiniAppWithDraft(workspaceId, appId, session.cookies),\n fetchAppServiceStatus(workspaceId, appId, session.cookies).catch(() => null),\n ]);\n const derived = deriveReviewState(envelope);\n\n if (args.diff) {\n const draftMini = pickMiniAppView(envelope, 'draft');\n const currentMini = pickMiniAppView(envelope, 'current');\n const diff = compareMiniAppViews(draftMini, currentMini);\n\n if (args.json) {\n emitJson({\n ok: true,\n workspaceId,\n appId,\n diffMode: true,\n reviewState: derived.state,\n locked: derived.locked,\n lockReason: derived.lockReason,\n serviceStatus: service?.serviceStatus ?? null,\n shutdownCandidateStatus: service?.shutdownCandidateStatus ?? null,\n scheduledShutdownAt: service?.scheduledShutdownAt ?? null,\n diff,\n });\n return exitAfterFlush(ExitCode.Ok);\n }\n\n process.stdout.write(`# App ${appId} — diff (draft → current)\\n\\n`);\n process.stdout.write(\n `Review state ${derived.state}` +\n (derived.locked ? ' ⚠️ update locked (운영팀 검수 큐 처리 대기)' : '') +\n '\\n',\n );\n if (service !== null) {\n process.stdout.write(`Service ${describeServiceStatus(service.serviceStatus)}\\n`);\n }\n process.stdout.write('\\n');\n\n // Both null is unreachable in practice — `app show` would have\n // 4xx-failed before reaching this branch — but we keep the guard\n // so the message ladder reads top-to-bottom rather than relying\n // on the next two branches to cover the both-null shape.\n if (!diff.hasDraft && !diff.hasCurrent) {\n process.stdout.write('App has neither a draft nor a current view yet.\\n');\n return exitAfterFlush(ExitCode.Ok);\n }\n if (!diff.hasCurrent) {\n process.stdout.write('no current view yet — draft is the only state\\n');\n return exitAfterFlush(ExitCode.Ok);\n }\n if (!diff.hasDraft) {\n process.stdout.write('no draft pending — current is the only state\\n');\n return exitAfterFlush(ExitCode.Ok);\n }\n if (diff.changed.length === 0) {\n process.stdout.write('No changes between draft and current.\\n');\n return exitAfterFlush(ExitCode.Ok);\n }\n\n // Align field column to the widest changed name for readability.\n // Cap at 24 so a stray long field name doesn't push the values off\n // the right edge of an 80-col terminal.\n const fieldWidth = Math.min(24, Math.max(...diff.changed.map((c) => c.field.length)) + 2);\n process.stdout.write('Changed:\\n');\n for (const entry of diff.changed) {\n const name = entry.field.padEnd(fieldWidth);\n process.stdout.write(\n ` ${name} ${formatDiffValue(entry.draft)} → ${formatDiffValue(entry.current)}\\n`,\n );\n }\n if (diff.unchangedCount > 0) {\n process.stdout.write(`\\nUnchanged: ${diff.unchangedCount} fields\\n`);\n }\n return exitAfterFlush(ExitCode.Ok);\n }\n\n const miniApp = pickMiniAppView(envelope, view);\n\n // Emit a one-line stderr hint when `--view current` comes back\n // empty but a draft exists — this is the most common confusion\n // (unreviewed apps have a populated draft and an empty current).\n // stderr so both JSON and plain callers see it without the JSON\n // shape changing.\n if (miniApp === null && view === 'current' && envelope.draft !== null) {\n process.stderr.write(\n `App ${appId} has no \\`current\\` view yet (not reviewed). Re-run with \\`--view draft\\` to see the pending record.\\n`,\n );\n }\n\n if (args.json) {\n emitJson({\n ok: true,\n workspaceId,\n appId,\n view,\n miniApp,\n reviewState: derived.state,\n locked: derived.locked,\n lockReason: derived.lockReason,\n serviceStatus: service?.serviceStatus ?? null,\n shutdownCandidateStatus: service?.shutdownCandidateStatus ?? null,\n scheduledShutdownAt: service?.scheduledShutdownAt ?? null,\n });\n return exitAfterFlush(ExitCode.Ok);\n }\n\n if (miniApp === null) {\n // Plain-text path keeps the stdout summary (the stderr hint is\n // already out of the way). Avoid duplicating it here.\n if (view === 'current' && envelope.draft !== null) {\n process.stdout.write(`App ${appId} has no \\`current\\` view yet.\\n`);\n } else {\n process.stdout.write(`App ${appId} has no data for view=${view}.\\n`);\n }\n return exitAfterFlush(ExitCode.Ok);\n }\n\n const pick = (k: string): string => {\n const v = miniApp[k];\n return v === null || v === undefined ? '-' : String(v);\n };\n const images = Array.isArray(miniApp.images) ? miniApp.images : [];\n const impression =\n miniApp.impression !== null && typeof miniApp.impression === 'object'\n ? (miniApp.impression as Record<string, unknown>)\n : {};\n const keywords = Array.isArray(impression.keywordList) ? impression.keywordList : [];\n const categoryPaths = Array.isArray(impression.categoryPaths) ? impression.categoryPaths : [];\n\n process.stdout.write(`# App ${appId} (view=${view})\\n\\n`);\n process.stdout.write(`Name (ko) ${pick('title')}\\n`);\n process.stdout.write(`Name (en) ${pick('titleEn')}\\n`);\n process.stdout.write(`App slug ${pick('appName')}\\n`);\n process.stdout.write(`Status ${pick('status')}\\n`);\n // `Review state` and `Service` are envelope-level — view-orthogonal —\n // so they sit right after the raw `Status` line regardless of which\n // view was selected. `Service` is omitted silently when the\n // `/review-status` call failed; the JSON path still emits\n // `serviceStatus: null` for callers that care.\n process.stdout.write(\n `Review state ${derived.state}` +\n (derived.locked ? ' ⚠️ update locked (운영팀 검수 큐 처리 대기)' : '') +\n '\\n',\n );\n if (service !== null) {\n process.stdout.write(`Service ${describeServiceStatus(service.serviceStatus)}\\n`);\n }\n process.stdout.write(`Home page ${pick('homePageUri')}\\n`);\n process.stdout.write(`CS email ${pick('csEmail')}\\n`);\n process.stdout.write(`Logo ${pick('iconUri')}\\n`);\n process.stdout.write(`Subtitle ${pick('description')}\\n`);\n const detail =\n typeof miniApp.detailDescription === 'string'\n ? `${[...miniApp.detailDescription].length} chars`\n : '-';\n process.stdout.write(`Detail desc ${detail}\\n`);\n process.stdout.write(`Images ${images.length}\\n`);\n process.stdout.write(`Keywords ${keywords.length} (${keywords.join(', ')})\\n`);\n const firstPath = categoryPaths[0];\n if (firstPath && typeof firstPath === 'object') {\n const fp = firstPath as Record<string, unknown>;\n const parts: string[] = [];\n for (const key of ['group', 'category', 'subCategory']) {\n const node = fp[key];\n if (node !== null && typeof node === 'object') {\n const nm = (node as Record<string, unknown>).name;\n if (typeof nm === 'string') parts.push(nm);\n }\n }\n process.stdout.write(`Category ${parts.join(' > ') || '-'}\\n`);\n } else {\n process.stdout.write(`Category -\\n`);\n }\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\n// Derived review state. The console UI's \"검토 중\" banner is not a single\n// API field — it's composed from the /with-draft envelope. We surface the\n// derivation so `aitcc app status` is the one place the rule lives.\n//\n// Observed combinations (2026-04-23 on workspace 3095, apps 29349/29356/\n// 29397/29405 all under review):\n//\n// approvalType=REVIEW current=null rejectedMessage=null → under-review\n// approvalType=REVIEW current=null rejectedMessage=STR → rejected\n// approvalType=REVIEW current=ROW (draft is edits-in-flight) → approved (with pending edits)\n// approvalType=REVIEW current=ROW draft=null or equal → approved\n// approvalType=null → not-submitted (edit draft only)\n//\n// Unknown combinations fall through to `unknown` so callers can log and\n// we can extend the ladder as new signals come in.\nexport type ReviewState =\n | 'not-submitted'\n | 'under-review'\n | 'rejected'\n | 'approved'\n | 'approved-with-edits'\n | 'unknown';\n\n// `locked` is the operational signal for \"can I update this app right now?\".\n// It is authoritative — derived `state` (e.g. `approved-with-edits`) is not.\n// 2026-05-02 dog-food showed apps in different derived states all rejecting\n// updates with `errorCode: 4046` because the envelope-level `approvalType`\n// was `REVIEW`. Tracked in docs/api/mini-apps.md \"REVIEW lock 권위\".\nexport type LockReason = 'review-pending';\n\nexport interface DerivedStatus {\n readonly state: ReviewState;\n readonly approvalType: string | null;\n readonly rejectedMessage: string | null;\n readonly hasCurrent: boolean;\n readonly hasDraft: boolean;\n readonly locked: boolean;\n readonly lockReason: LockReason | null;\n}\n\nexport function deriveReviewState(env: {\n current: Record<string, unknown> | null;\n draft: Record<string, unknown> | null;\n approvalType: string | null;\n rejectedMessage: string | null;\n}): DerivedStatus {\n const hasCurrent = env.current !== null;\n const hasDraft = env.draft !== null;\n const approvalType = env.approvalType;\n const rejectedMessage = env.rejectedMessage;\n\n let state: ReviewState;\n if (approvalType === null) {\n state = 'not-submitted';\n } else if (rejectedMessage !== null) {\n state = 'rejected';\n } else if (!hasCurrent) {\n state = 'under-review';\n } else if (hasDraft) {\n state = 'approved-with-edits';\n } else {\n state = 'approved';\n }\n // approvalType values other than REVIEW (we haven't observed any yet)\n // or unexpected combinations get flagged as unknown rather than misreported.\n if (approvalType !== null && approvalType !== 'REVIEW' && state === 'under-review') {\n state = 'unknown';\n }\n const locked = approvalType === 'REVIEW';\n const lockReason: LockReason | null = locked ? 'review-pending' : null;\n return { state, approvalType, rejectedMessage, hasCurrent, hasDraft, locked, lockReason };\n}\n\n// Status column for `app ls`. Composes the with-draft-derived `ReviewState`\n// with the workspace review-status entry's `serviceStatus` so a published,\n// running app shows `in-service` instead of the bare `approved`. lock is\n// authoritative on `approvalType === 'REVIEW'` (see docs/api/mini-apps.md\n// \"REVIEW lock 권위\") — derived ladder labels like `approved-with-edits`\n// imply lock but are not the gate themselves.\nexport type AppLsStatus =\n | 'not-submitted'\n | 'under-review'\n | 'approved'\n | 'approved-with-edits'\n | 'rejected'\n | 'in-service'\n | 'unknown';\n\nexport interface AppLsStatusRow {\n readonly status: AppLsStatus;\n readonly locked: boolean;\n readonly lockReason: 'review-pending' | null;\n}\n\nexport function deriveLsStatus(\n derived: DerivedStatus | null,\n serviceStatus: string | undefined,\n): AppLsStatusRow {\n if (!derived) {\n return { status: 'unknown', locked: false, lockReason: null };\n }\n const locked = derived.approvalType === 'REVIEW';\n const lockReason = locked ? 'review-pending' : null;\n let status: AppLsStatus = derived.state;\n if (status === 'approved' && serviceStatus === 'RUNNING') {\n status = 'in-service';\n }\n return { status, locked, lockReason };\n}\n\nexport function serviceStatusFor(\n entry: Readonly<Record<string, unknown>> | null,\n): string | undefined {\n if (!entry) return undefined;\n const raw = entry.serviceStatus;\n return typeof raw === 'string' ? raw : undefined;\n}\n\nconst POLL_MIN_INTERVAL_SEC = 30;\nconst POLL_MAX_INTERVAL_SEC = 3600;\n\nconst statusCommand = defineCommand({\n meta: {\n name: 'status',\n description:\n 'Show the derived review state of a mini-app (under-review / rejected / approved).',\n },\n args: {\n id: {\n type: 'positional',\n description: 'Mini-app ID. Optional when `aitcc.yaml` provides `miniAppId`.',\n required: false,\n },\n workspace: {\n type: 'string',\n description: 'Workspace ID. Defaults to the selected workspace.',\n },\n watch: {\n type: 'boolean',\n description:\n 'Poll until the review state flips off `under-review` (rejected or approved). ' +\n 'Combine with `--interval <seconds>`.',\n default: false,\n },\n interval: {\n type: 'string',\n description: 'Polling interval in seconds when --watch is set. Clamped to [30, 3600].',\n default: '60',\n },\n json: { type: 'boolean', description: 'Emit machine-readable JSON.', default: false },\n },\n async run({ args }) {\n const intervalRaw = Number(args.interval);\n if (!Number.isFinite(intervalRaw) || intervalRaw <= 0) {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'invalid-config',\n field: 'interval',\n message: `--interval must be a positive number (got ${JSON.stringify(args.interval)})`,\n });\n } else {\n process.stderr.write(`app status: invalid --interval ${JSON.stringify(args.interval)}\\n`);\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n const intervalSec = Math.max(\n POLL_MIN_INTERVAL_SEC,\n Math.min(POLL_MAX_INTERVAL_SEC, Math.floor(intervalRaw)),\n );\n\n const ctx = await resolveAppOrFail({\n json: args.json,\n appIdRaw: args.id,\n ...(args.workspace !== undefined ? { workspace: args.workspace } : {}),\n });\n if (!ctx) return;\n const appId = await requireMiniAppId(ctx, args.json);\n if (appId === null) return;\n printContextHeader(ctx, { json: args.json });\n const { session, workspaceId } = ctx;\n\n // `serviceStatus` is a server-side string (PREPARE / RUNNING / …) that\n // is orthogonal to the client-derived review `state`. We surface both\n // so operators see the review stage and the runtime stage at once —\n // important because an app can be `approved` (review done) yet still\n // `PREPARE` (not live), and we never want the --json consumer to have\n // to make a second call to tell them apart.\n const emit = (\n status: DerivedStatus,\n service: {\n serviceStatus: string;\n shutdownCandidateStatus: string | null;\n scheduledShutdownAt: string | null;\n } | null,\n ) => {\n if (args.json) {\n emitJson({\n ok: true,\n workspaceId,\n appId,\n ...status,\n // `null` only in the unlikely case the service-status endpoint\n // failed but with-draft succeeded — we fall through rather than\n // hard-failing, because the derived review state is still useful.\n serviceStatus: service?.serviceStatus ?? null,\n shutdownCandidateStatus: service?.shutdownCandidateStatus ?? null,\n scheduledShutdownAt: service?.scheduledShutdownAt ?? null,\n });\n } else {\n const svc = service ? ` [${service.serviceStatus}]` : '';\n process.stdout.write(\n `App ${appId} (ws ${workspaceId}): ${status.state}${svc}` +\n (status.locked ? '\\n ⚠️ update locked (운영팀 검수 큐 처리 대기)' : '') +\n (status.rejectedMessage ? `\\n reason: ${status.rejectedMessage}` : '') +\n (service?.scheduledShutdownAt\n ? `\\n scheduled shutdown: ${service.scheduledShutdownAt}`\n : '') +\n '\\n',\n );\n }\n };\n\n try {\n const once = async (): Promise<\n [\n DerivedStatus,\n {\n serviceStatus: string;\n shutdownCandidateStatus: string | null;\n scheduledShutdownAt: string | null;\n } | null,\n ]\n > => {\n // Fire both requests in parallel — they share the same session\n // cookie and the console backend has no cross-rate-limit we've\n // observed. `service-status` is best-effort: if it fails we still\n // want the review state through.\n const [env, service] = await Promise.all([\n fetchMiniAppWithDraft(workspaceId, appId, session.cookies),\n fetchAppServiceStatus(workspaceId, appId, session.cookies).catch(() => null),\n ]);\n return [deriveReviewState(env), service];\n };\n\n if (!args.watch) {\n const [status, service] = await once();\n emit(status, service);\n return exitAfterFlush(ExitCode.Ok);\n }\n\n // --watch: poll with clear line-per-tick JSON emission. Each JSON line\n // is a self-contained object, NDJSON-style, so agents/shells can pipe\n // it into `jq -c` without waiting for a terminal. Stop when the state\n // is no longer `under-review` (reviewed) or when the process is\n // interrupted — we don't synthesise a \"watch-ended\" record.\n // Human mode prints a one-line update only when the state changes.\n let lastState: ReviewState | null = null;\n let lastServiceStatus: string | null = null;\n while (true) {\n const [status, service] = await once();\n const svc = service?.serviceStatus ?? null;\n if (args.json) {\n emit(status, service);\n } else if (status.state !== lastState || svc !== lastServiceStatus) {\n emit(status, service);\n }\n lastState = status.state;\n lastServiceStatus = svc;\n if (status.state !== 'under-review') {\n return exitAfterFlush(ExitCode.Ok);\n }\n await new Promise((resolve) => setTimeout(resolve, intervalSec * 1000));\n }\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\n// --json contract (consumed by agent-plugin):\n//\n// app ratings <id> [--workspace <id>] [--page N] [--size N]\n// [--sort-field CREATED_AT|SCORE] [--sort-direction ASC|DESC]:\n// { ok: true, workspaceId, appId, page, size, paging, averageRating,\n// totalReviewCount, ratings: [...] } exit 0\n// { ok: false, reason: 'no-workspace-selected' } exit 2\n// { ok: false, reason: 'invalid-id', message } exit 2\n// { ok: false, reason: 'invalid-config', field, message } exit 2\n//\n// The `sortField` values reflect what the console UI emits; the server\n// accepts them exactly. We don't enumerate more values because no other\n// orderings are observed — if the server supports them, add them once\n// they appear in a real capture.\n\nconst VALID_SORT_FIELDS: readonly RatingSortField[] = ['CREATED_AT', 'SCORE'];\nconst VALID_SORT_DIRECTIONS: readonly RatingSortDirection[] = ['ASC', 'DESC'];\n\nfunction parseNonNegativeInt(raw: string, field: string): { value: number } | { error: string } {\n const n = Number(raw);\n if (!Number.isFinite(n) || !Number.isInteger(n) || n < 0) {\n return { error: `--${field} must be a non-negative integer (got ${JSON.stringify(raw)})` };\n }\n return { value: n };\n}\n\nconst ratingsCommand = defineCommand({\n meta: {\n name: 'ratings',\n description: 'List user ratings and reviews left for a mini-app.',\n },\n args: {\n id: {\n type: 'positional',\n description: 'Mini-app ID. Optional when `aitcc.yaml` provides `miniAppId`.',\n required: false,\n },\n workspace: {\n type: 'string',\n description: 'Workspace ID. Defaults to the selected workspace.',\n },\n page: { type: 'string', description: 'Page number (0-indexed).', default: '0' },\n size: { type: 'string', description: 'Page size.', default: '20' },\n 'sort-field': {\n type: 'string',\n description: 'Sort field: CREATED_AT (default) or SCORE.',\n default: 'CREATED_AT',\n },\n 'sort-direction': {\n type: 'string',\n description: 'Sort direction: ASC or DESC (default).',\n default: 'DESC',\n },\n json: { type: 'boolean', description: 'Emit machine-readable JSON.', default: false },\n },\n async run({ args }) {\n const pageResult = parseNonNegativeInt(args.page, 'page');\n if ('error' in pageResult) {\n if (args.json) {\n emitJson({ ok: false, reason: 'invalid-config', field: 'page', message: pageResult.error });\n } else {\n process.stderr.write(`app ratings: ${pageResult.error}\\n`);\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n const sizeResult = parseNonNegativeInt(args.size, 'size');\n if ('error' in sizeResult) {\n if (args.json) {\n emitJson({ ok: false, reason: 'invalid-config', field: 'size', message: sizeResult.error });\n } else {\n process.stderr.write(`app ratings: ${sizeResult.error}\\n`);\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n if (sizeResult.value === 0) {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'invalid-config',\n field: 'size',\n message: '--size must be at least 1',\n });\n } else {\n process.stderr.write('app ratings: --size must be at least 1\\n');\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n const sortField = args['sort-field'];\n if (!VALID_SORT_FIELDS.includes(sortField as RatingSortField)) {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'invalid-config',\n field: 'sort-field',\n message: `--sort-field must be one of ${VALID_SORT_FIELDS.join('|')} (got ${JSON.stringify(sortField)})`,\n });\n } else {\n process.stderr.write(`app ratings: invalid --sort-field ${JSON.stringify(sortField)}\\n`);\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n const sortDirection = args['sort-direction'];\n if (!VALID_SORT_DIRECTIONS.includes(sortDirection as RatingSortDirection)) {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'invalid-config',\n field: 'sort-direction',\n message: `--sort-direction must be one of ${VALID_SORT_DIRECTIONS.join('|')} (got ${JSON.stringify(sortDirection)})`,\n });\n } else {\n process.stderr.write(\n `app ratings: invalid --sort-direction ${JSON.stringify(sortDirection)}\\n`,\n );\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n\n const ctx = await resolveAppOrFail({\n json: args.json,\n appIdRaw: args.id,\n ...(args.workspace !== undefined ? { workspace: args.workspace } : {}),\n });\n if (!ctx) return;\n const appId = await requireMiniAppId(ctx, args.json);\n if (appId === null) return;\n printContextHeader(ctx, { json: args.json });\n const { session, workspaceId } = ctx;\n\n try {\n const result = await fetchMiniAppRatings(\n {\n workspaceId,\n miniAppId: appId,\n page: pageResult.value,\n size: sizeResult.value,\n sortField: sortField as RatingSortField,\n sortDirection: sortDirection as RatingSortDirection,\n },\n session.cookies,\n );\n\n if (args.json) {\n emitJson({\n ok: true,\n workspaceId,\n appId,\n page: pageResult.value,\n size: sizeResult.value,\n sortField,\n sortDirection,\n averageRating: result.averageRating,\n totalReviewCount: result.totalReviewCount,\n paging: result.paging,\n ratings: result.ratings,\n });\n return exitAfterFlush(ExitCode.Ok);\n }\n\n process.stdout.write(\n `App ${appId} (ws ${workspaceId}): ${result.totalReviewCount} review(s), avg ${result.averageRating.toFixed(2)}\\n`,\n );\n if (result.ratings.length === 0) {\n process.stdout.write('No ratings on this page.\\n');\n return exitAfterFlush(ExitCode.Ok);\n }\n for (const r of result.ratings) {\n const score = typeof r.score === 'number' ? r.score : (r.rating ?? '-');\n const author =\n typeof r.nickname === 'string'\n ? r.nickname\n : typeof r.userName === 'string'\n ? r.userName\n : '(anon)';\n const text =\n typeof r.content === 'string'\n ? r.content\n : typeof r.reviewContent === 'string'\n ? r.reviewContent\n : '';\n const createdAt =\n typeof r.createdAt === 'string'\n ? r.createdAt\n : typeof r.reviewedAt === 'string'\n ? r.reviewedAt\n : '';\n process.stdout.write(`${score}\\t${createdAt}\\t${author}\\t${text}\\n`);\n }\n if (result.paging.hasNext) {\n process.stdout.write(\n `(more: --page ${pageResult.value + 1} for next ${sizeResult.value})\\n`,\n );\n }\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\n// --json contract (consumed by agent-plugin):\n//\n// app reports <id> [--workspace <id>] [--page-size N] [--cursor <str>]:\n// { ok: true, workspaceId, appId, pageSize, cursor, nextCursor,\n// hasMore, reports: [...] } exit 0\n// { ok: false, reason: 'invalid-id' | 'invalid-config', ... } exit 2\n//\n// The endpoint is `/workspaces/:wid/mini-apps/:aid/user-reports` —\n// note the **plural** `mini-apps` (same split-personality as review-status).\n// Pagination is cursor-based: the server hands back `nextCursor` + `hasMore`,\n// we pass `--cursor` as an opaque string next call.\n\nconst reportsCommand = defineCommand({\n meta: {\n name: 'reports',\n description: 'List user-submitted reports (신고 내역) for a mini-app.',\n },\n args: {\n id: {\n type: 'positional',\n description: 'Mini-app ID. Optional when `aitcc.yaml` provides `miniAppId`.',\n required: false,\n },\n workspace: {\n type: 'string',\n description: 'Workspace ID. Defaults to the selected workspace.',\n },\n 'page-size': { type: 'string', description: 'Page size (default 20).', default: '20' },\n cursor: {\n type: 'string',\n description: 'Opaque cursor from a previous response `nextCursor`.',\n },\n json: { type: 'boolean', description: 'Emit machine-readable JSON.', default: false },\n },\n async run({ args }) {\n const pageSizeResult = parseNonNegativeInt(args['page-size'], 'page-size');\n if ('error' in pageSizeResult) {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'invalid-config',\n field: 'page-size',\n message: pageSizeResult.error,\n });\n } else {\n process.stderr.write(`app reports: ${pageSizeResult.error}\\n`);\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n if (pageSizeResult.value === 0) {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'invalid-config',\n field: 'page-size',\n message: '--page-size must be at least 1',\n });\n } else {\n process.stderr.write('app reports: --page-size must be at least 1\\n');\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n\n const ctx = await resolveAppOrFail({\n json: args.json,\n appIdRaw: args.id,\n ...(args.workspace !== undefined ? { workspace: args.workspace } : {}),\n });\n if (!ctx) return;\n const appId = await requireMiniAppId(ctx, args.json);\n if (appId === null) return;\n printContextHeader(ctx, { json: args.json });\n const { session, workspaceId } = ctx;\n\n try {\n const result = await fetchUserReports(\n {\n workspaceId,\n miniAppId: appId,\n pageSize: pageSizeResult.value,\n ...(typeof args.cursor === 'string' && args.cursor.length > 0\n ? { cursor: args.cursor }\n : {}),\n },\n session.cookies,\n );\n\n if (args.json) {\n emitJson({\n ok: true,\n workspaceId,\n appId,\n pageSize: pageSizeResult.value,\n cursor: args.cursor ?? null,\n nextCursor: result.nextCursor,\n hasMore: result.hasMore,\n reports: result.reports,\n });\n return exitAfterFlush(ExitCode.Ok);\n }\n\n process.stdout.write(\n `App ${appId} (ws ${workspaceId}): ${result.reports.length} report(s) on this page\\n`,\n );\n if (result.reports.length === 0) {\n process.stdout.write('No reports.\\n');\n return exitAfterFlush(ExitCode.Ok);\n }\n for (const r of result.reports) {\n const id = typeof r.id === 'string' || typeof r.id === 'number' ? r.id : '-';\n const reason = typeof r.reason === 'string' ? r.reason : (r.reportType ?? '-');\n const text =\n typeof r.content === 'string' ? r.content : typeof r.detail === 'string' ? r.detail : '';\n const createdAt =\n typeof r.createdAt === 'string'\n ? r.createdAt\n : typeof r.reportedAt === 'string'\n ? r.reportedAt\n : '';\n process.stdout.write(`${id}\\t${createdAt}\\t${reason}\\t${text}\\n`);\n }\n if (result.hasMore && result.nextCursor) {\n process.stdout.write(`(more: --cursor ${result.nextCursor})\\n`);\n }\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\n// --json contract (consumed by agent-plugin):\n//\n// app bundles ls <id> [--workspace <id>] [--page N]\n// [--tested true|false] [--deploy-status STR]:\n// { ok: true, workspaceId, appId, page, totalPage, currentPage,\n// bundles: [...] } exit 0\n// { ok: false, reason: 'invalid-id' | 'invalid-config' ... } exit 2\n//\n// app bundles deployed <id> [--workspace <id>]:\n// { ok: true, workspaceId, appId, bundle: {...} | null } exit 0\n// { ok: false, reason: 'invalid-id' ... } exit 2\n//\n// Bundles are the artefact that `aitcc deploy` will eventually upload\n// (task #24). Listing them now lets agent-plugin and humans see the\n// deploy surface even before we can write new ones.\n\nconst bundlesLsCommand = defineCommand({\n meta: {\n name: 'ls',\n description: 'List upload bundles for a mini-app (page-based pagination).',\n },\n args: {\n id: {\n type: 'positional',\n description: 'Mini-app ID. Optional when `aitcc.yaml` provides `miniAppId`.',\n required: false,\n },\n workspace: {\n type: 'string',\n description: 'Workspace ID. Defaults to the selected workspace.',\n },\n page: { type: 'string', description: 'Page number (0-indexed).', default: '0' },\n tested: {\n type: 'string',\n description: 'Filter: `true` to show only tested bundles (or `false`).',\n },\n 'deploy-status': {\n type: 'string',\n description: 'Filter by deploy status (e.g. DEPLOYED).',\n },\n json: { type: 'boolean', description: 'Emit machine-readable JSON.', default: false },\n },\n async run({ args }) {\n const pageResult = parseNonNegativeInt(args.page, 'page');\n if ('error' in pageResult) {\n if (args.json) {\n emitJson({ ok: false, reason: 'invalid-config', field: 'page', message: pageResult.error });\n } else {\n process.stderr.write(`app bundles ls: ${pageResult.error}\\n`);\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n let tested: boolean | undefined;\n if (args.tested !== undefined) {\n if (args.tested === 'true') tested = true;\n else if (args.tested === 'false') tested = false;\n else {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'invalid-config',\n field: 'tested',\n message: `--tested must be \"true\" or \"false\" (got ${JSON.stringify(args.tested)})`,\n });\n } else {\n process.stderr.write(`app bundles ls: invalid --tested ${JSON.stringify(args.tested)}\\n`);\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n }\n\n const ctx = await resolveAppOrFail({\n json: args.json,\n appIdRaw: args.id,\n ...(args.workspace !== undefined ? { workspace: args.workspace } : {}),\n });\n if (!ctx) return;\n const appId = await requireMiniAppId(ctx, args.json);\n if (appId === null) return;\n printContextHeader(ctx, { json: args.json });\n const { session, workspaceId } = ctx;\n\n try {\n const result = await fetchBundles(\n {\n workspaceId,\n miniAppId: appId,\n page: pageResult.value,\n ...(tested !== undefined ? { tested } : {}),\n ...(typeof args['deploy-status'] === 'string' && args['deploy-status'].length > 0\n ? { deployStatus: args['deploy-status'] }\n : {}),\n },\n session.cookies,\n );\n\n if (args.json) {\n emitJson({\n ok: true,\n workspaceId,\n appId,\n page: pageResult.value,\n totalPage: result.totalPage,\n currentPage: result.currentPage,\n bundles: result.contents,\n });\n return exitAfterFlush(ExitCode.Ok);\n }\n\n process.stdout.write(\n `App ${appId} (ws ${workspaceId}): page ${result.currentPage + 1}/${Math.max(result.totalPage, 1)}, ${result.contents.length} bundle(s)\\n`,\n );\n if (result.contents.length === 0) {\n process.stdout.write('No bundles on this page.\\n');\n return exitAfterFlush(ExitCode.Ok);\n }\n for (const b of result.contents) {\n const id = typeof b.id === 'string' || typeof b.id === 'number' ? b.id : '-';\n const version = typeof b.version === 'string' ? b.version : '-';\n const status = typeof b.deployStatus === 'string' ? b.deployStatus : '-';\n const createdAt = typeof b.createdAt === 'string' ? b.createdAt : '';\n process.stdout.write(`${id}\\t${version}\\t${status}\\t${createdAt}\\n`);\n }\n if (result.currentPage + 1 < result.totalPage) {\n process.stdout.write(`(more: --page ${result.currentPage + 1})\\n`);\n }\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\nconst bundlesDeployedCommand = defineCommand({\n meta: {\n name: 'deployed',\n description: 'Show the currently deployed bundle for a mini-app (or null if none).',\n },\n args: {\n id: {\n type: 'positional',\n description: 'Mini-app ID. Optional when `aitcc.yaml` provides `miniAppId`.',\n required: false,\n },\n workspace: {\n type: 'string',\n description: 'Workspace ID. Defaults to the selected workspace.',\n },\n json: { type: 'boolean', description: 'Emit machine-readable JSON.', default: false },\n },\n async run({ args }) {\n const ctx = await resolveAppOrFail({\n json: args.json,\n appIdRaw: args.id,\n ...(args.workspace !== undefined ? { workspace: args.workspace } : {}),\n });\n if (!ctx) return;\n const appId = await requireMiniAppId(ctx, args.json);\n if (appId === null) return;\n printContextHeader(ctx, { json: args.json });\n const { session, workspaceId } = ctx;\n\n try {\n const bundle = await fetchDeployedBundle(workspaceId, appId, session.cookies);\n if (args.json) {\n emitJson({ ok: true, workspaceId, appId, bundle });\n return exitAfterFlush(ExitCode.Ok);\n }\n if (bundle === null) {\n process.stdout.write(`App ${appId} (ws ${workspaceId}): no deployed bundle\\n`);\n return exitAfterFlush(ExitCode.Ok);\n }\n const id = typeof bundle.id === 'string' || typeof bundle.id === 'number' ? bundle.id : '-';\n const version = typeof bundle.version === 'string' ? bundle.version : '-';\n const status = typeof bundle.deployStatus === 'string' ? bundle.deployStatus : '-';\n const deployedAt = typeof bundle.deployedAt === 'string' ? bundle.deployedAt : '';\n process.stdout.write(`App ${appId} deployed bundle:\\n`);\n process.stdout.write(` id ${id}\\n`);\n process.stdout.write(` version ${version}\\n`);\n process.stdout.write(` status ${status}\\n`);\n process.stdout.write(` deployedAt ${deployedAt}\\n`);\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\n// --json contract (consumed by agent-plugin):\n//\n// app bundles upload <id> <path> [--deployment-id <uuid>] [--memo <text>]\n// [--workspace <id>] [--dry-run]:\n// { ok: true, workspaceId, appId, deploymentId, reviewStatus,\n// bundle: { ... }, memoApplied: boolean } exit 0\n// { ok: true, dryRun: true, workspaceId, appId, deploymentId,\n// bytes, memo } exit 0\n// { ok: false, reason: 'invalid-id' | 'file-unreadable'\n// | 'missing-deployment-id'\n// | 'bundle-not-prepare', ... } exit 2\n//\n// 3-step upload: initialize → PUT to S3 presigned URL → complete,\n// optionally followed by POST /bundles/memos. deployment-id is the\n// `_metadata.deploymentId` inside the .ait's app.json — for now we ask\n// the caller to supply it explicitly (zip-cracking is a follow-up).\n// On mismatched reviewStatus (\"이미 존재하는 버전이에요.\") we bail\n// before touching S3 with exit 2 / reason `bundle-not-prepare`, matching\n// the console's own client-side guard.\n\nconst bundlesUploadCommand = defineCommand({\n meta: {\n name: 'upload',\n description: 'Upload an .ait bundle (initialize → PUT → complete [+ memo]).',\n },\n args: {\n // Two positionals (`id` then `path`) — citty assigns positionals in\n // declaration order, so `id` must stay required: leaving it optional\n // would let citty hand `./bundle.ait` to `id` and then complain that\n // `path` is missing. Yaml miniAppId fallback is reserved for commands\n // with a single positional or with the app id behind a flag (see\n // `app deploy`'s `--app`).\n id: {\n type: 'positional',\n description: 'Mini-app ID.',\n required: true,\n },\n path: { type: 'positional', description: 'Path to the .ait bundle file.', required: true },\n 'deployment-id': {\n type: 'string',\n description: 'deploymentId embedded in the bundle (from app.json._metadata.deploymentId).',\n },\n memo: { type: 'string', description: 'Optional memo attached to this bundle version.' },\n workspace: {\n type: 'string',\n description: 'Workspace ID. Defaults to the selected workspace.',\n },\n 'dry-run': {\n type: 'boolean',\n description: 'Validate inputs and show what would be sent, without touching the server.',\n default: false,\n },\n json: { type: 'boolean', description: 'Emit machine-readable JSON.', default: false },\n },\n async run({ args }) {\n const deploymentId = typeof args['deployment-id'] === 'string' ? args['deployment-id'] : '';\n if (deploymentId === '') {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'missing-deployment-id',\n message:\n '--deployment-id is required; read app.json._metadata.deploymentId from inside the .ait',\n });\n } else {\n process.stderr.write(\n 'app bundles upload: --deployment-id <uuid> is required.\\n' +\n ' The .ait bundle is a zip; read app.json inside and copy _metadata.deploymentId.\\n',\n );\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n const filePath = typeof args.path === 'string' ? args.path : '';\n let bytes: Uint8Array;\n try {\n const { readFile } = await import('node:fs/promises');\n const buf = await readFile(filePath);\n bytes = new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n if (args.json) {\n emitJson({ ok: false, reason: 'file-unreadable', path: filePath, message });\n } else {\n process.stderr.write(`app bundles upload: cannot read ${filePath}: ${message}\\n`);\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n\n const ctx = await resolveAppOrFail({\n json: args.json,\n appIdRaw: args.id,\n ...(args.workspace !== undefined ? { workspace: args.workspace } : {}),\n });\n if (!ctx) return;\n const appId = await requireMiniAppId(ctx, args.json);\n if (appId === null) return;\n printContextHeader(ctx, { json: args.json });\n const { session, workspaceId } = ctx;\n const memo = typeof args.memo === 'string' && args.memo.length > 0 ? args.memo : undefined;\n\n if (args['dry-run']) {\n if (args.json) {\n emitJson({\n ok: true,\n dryRun: true,\n workspaceId,\n appId,\n deploymentId,\n bytes: bytes.byteLength,\n memo: memo ?? null,\n });\n } else {\n process.stdout.write(\n `DRY RUN\\n` +\n ` workspace ${workspaceId}\\n` +\n ` appId ${appId}\\n` +\n ` deploymentId ${deploymentId}\\n` +\n ` bytes ${bytes.byteLength}\\n` +\n ` memo ${memo ?? '(none)'}\\n`,\n );\n }\n return exitAfterFlush(ExitCode.Ok);\n }\n\n try {\n const init = await postDeploymentsInitialize(\n workspaceId,\n appId,\n deploymentId,\n session.cookies,\n );\n if (init.reviewStatus !== 'PREPARE') {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'bundle-not-prepare',\n workspaceId,\n appId,\n deploymentId,\n reviewStatus: init.reviewStatus,\n message: '이미 존재하는 버전이에요.',\n });\n } else {\n process.stderr.write(\n `app bundles upload: deployment ${deploymentId} is already in state ${init.reviewStatus}; bundle upload refused.\\n`,\n );\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n await putBundleToUploadUrl(init.uploadUrl, bytes);\n const bundle = await postDeploymentsComplete(\n workspaceId,\n appId,\n deploymentId,\n session.cookies,\n );\n let memoApplied = false;\n if (memo !== undefined) {\n await postBundleMemo(workspaceId, appId, deploymentId, memo, session.cookies);\n memoApplied = true;\n }\n if (args.json) {\n emitJson({\n ok: true,\n workspaceId,\n appId,\n deploymentId,\n reviewStatus: init.reviewStatus,\n bundle,\n memoApplied,\n });\n return exitAfterFlush(ExitCode.Ok);\n }\n process.stdout.write(\n `Uploaded bundle for app ${appId} (ws ${workspaceId})\\n` +\n ` deploymentId ${deploymentId}\\n` +\n ` bytes ${bytes.byteLength}\\n` +\n ` memo ${memoApplied ? 'applied' : '(none)'}\\n`,\n );\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\n// --json contract (consumed by agent-plugin):\n//\n// app bundles review <id> --deployment-id <uuid> --release-notes <text>\n// [--withdraw] [--workspace <id>]:\n// { ok: true, workspaceId, appId, deploymentId, action: 'submit'|'withdraw',\n// result: { ... } } exit 0\n// { ok: false, reason: 'invalid-id' | 'missing-deployment-id'\n// | 'missing-release-notes' } exit 2\n//\n// Submits (default) or withdraws a review request on an uploaded bundle.\n// Server validates the deploymentId belongs to the app — we don't\n// double-check here. `--withdraw` is the opposite action and takes no\n// release-notes. Submitting requires release-notes even if empty on the\n// wire (the UI sends \"\") — we forward whatever the caller supplies.\n\nconst bundlesReviewCommand = defineCommand({\n meta: {\n name: 'review',\n description: 'Submit (or withdraw) an uploaded bundle for review.',\n },\n args: {\n id: {\n type: 'positional',\n description: 'Mini-app ID. Optional when `aitcc.yaml` provides `miniAppId`.',\n required: false,\n },\n 'deployment-id': {\n type: 'string',\n description: 'deploymentId of the uploaded bundle.',\n },\n 'release-notes': {\n type: 'string',\n description: 'Release notes shown to the reviewer. Ignored with --withdraw.',\n },\n withdraw: {\n type: 'boolean',\n description: 'Withdraw the existing review request instead of submitting a new one.',\n default: false,\n },\n workspace: {\n type: 'string',\n description: 'Workspace ID. Defaults to the selected workspace.',\n },\n json: { type: 'boolean', description: 'Emit machine-readable JSON.', default: false },\n },\n async run({ args }) {\n const deploymentId = typeof args['deployment-id'] === 'string' ? args['deployment-id'] : '';\n if (deploymentId === '') {\n if (args.json) {\n emitJson({ ok: false, reason: 'missing-deployment-id' });\n } else {\n process.stderr.write('app bundles review: --deployment-id <uuid> is required.\\n');\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n const withdraw = Boolean(args.withdraw);\n const releaseNotes =\n typeof args['release-notes'] === 'string' ? args['release-notes'] : undefined;\n if (!withdraw && releaseNotes === undefined) {\n if (args.json) {\n emitJson({ ok: false, reason: 'missing-release-notes' });\n } else {\n process.stderr.write(\n 'app bundles review: --release-notes <text> is required to submit for review.\\n',\n );\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n\n const ctx = await resolveAppOrFail({\n json: args.json,\n appIdRaw: args.id,\n ...(args.workspace !== undefined ? { workspace: args.workspace } : {}),\n });\n if (!ctx) return;\n const appId = await requireMiniAppId(ctx, args.json);\n if (appId === null) return;\n printContextHeader(ctx, { json: args.json });\n const { session, workspaceId } = ctx;\n\n try {\n if (withdraw) {\n const result = await postBundleReviewWithdrawal(\n workspaceId,\n appId,\n deploymentId,\n session.cookies,\n );\n if (args.json) {\n emitJson({\n ok: true,\n workspaceId,\n appId,\n deploymentId,\n action: 'withdraw',\n result,\n });\n return exitAfterFlush(ExitCode.Ok);\n }\n process.stdout.write(\n `Withdrew review for bundle ${deploymentId} (app ${appId}, ws ${workspaceId})\\n`,\n );\n return exitAfterFlush(ExitCode.Ok);\n }\n const result = await postBundleReview(\n {\n workspaceId,\n miniAppId: appId,\n deploymentId,\n releaseNotes: releaseNotes ?? '',\n },\n session.cookies,\n );\n if (args.json) {\n emitJson({\n ok: true,\n workspaceId,\n appId,\n deploymentId,\n action: 'submit',\n result,\n });\n return exitAfterFlush(ExitCode.Ok);\n }\n const versionName = typeof result.versionName === 'string' ? result.versionName : '';\n process.stdout.write(\n `Submitted bundle ${deploymentId} for review (app ${appId}, ws ${workspaceId})` +\n (versionName ? ` — version ${versionName}` : '') +\n '\\n',\n );\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\n// --json contract (consumed by agent-plugin):\n//\n// app bundles release <id> --deployment-id <uuid> [--workspace <id>]:\n// { ok: true, workspaceId, appId, deploymentId, result: { ... } } exit 0\n// { ok: false, reason: 'invalid-id' | 'missing-deployment-id'\n// | 'not-confirmed' } exit 2\n//\n// Flips an APPROVED bundle live. This is the destructive write path —\n// end users will see the new version. Guarded behind `--confirm` to\n// prevent accidental invocation from a loose shell history. No `--json`\n// bypass: the confirmation flag is mandatory.\n\nconst bundlesReleaseCommand = defineCommand({\n meta: {\n name: 'release',\n description: 'Release (publish) an APPROVED bundle to end users.',\n },\n args: {\n id: {\n type: 'positional',\n description: 'Mini-app ID. Optional when `aitcc.yaml` provides `miniAppId`.',\n required: false,\n },\n 'deployment-id': {\n type: 'string',\n description: 'deploymentId of the APPROVED bundle to publish.',\n },\n confirm: {\n type: 'boolean',\n description: 'Required to actually release — without it, the command refuses.',\n default: false,\n },\n workspace: {\n type: 'string',\n description: 'Workspace ID. Defaults to the selected workspace.',\n },\n json: { type: 'boolean', description: 'Emit machine-readable JSON.', default: false },\n },\n async run({ args }) {\n const deploymentId = typeof args['deployment-id'] === 'string' ? args['deployment-id'] : '';\n if (deploymentId === '') {\n if (args.json) {\n emitJson({ ok: false, reason: 'missing-deployment-id' });\n } else {\n process.stderr.write('app bundles release: --deployment-id <uuid> is required.\\n');\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n if (!args.confirm) {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'not-confirmed',\n message: 'release is destructive; pass --confirm to proceed',\n });\n } else {\n process.stderr.write(\n 'app bundles release: this publishes the bundle to end users.\\n' +\n ' Re-run with --confirm to proceed.\\n',\n );\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n\n const ctx = await resolveAppOrFail({\n json: args.json,\n appIdRaw: args.id,\n ...(args.workspace !== undefined ? { workspace: args.workspace } : {}),\n });\n if (!ctx) return;\n const appId = await requireMiniAppId(ctx, args.json);\n if (appId === null) return;\n printContextHeader(ctx, { json: args.json });\n const { session, workspaceId } = ctx;\n\n try {\n const result = await postBundleRelease(\n { workspaceId, miniAppId: appId, deploymentId },\n session.cookies,\n );\n if (args.json) {\n emitJson({ ok: true, workspaceId, appId, deploymentId, result });\n return exitAfterFlush(ExitCode.Ok);\n }\n process.stdout.write(\n `Released bundle ${deploymentId} for app ${appId} (ws ${workspaceId})\\n`,\n );\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\n// --json contract (consumed by agent-plugin):\n//\n// app bundles test-push <id> --deployment-id <uuid> [--workspace <id>]:\n// { ok: true, workspaceId, appId, deploymentId, result: { ... } } exit 0\n//\n// app bundles test-links <id> [--workspace <id>]:\n// { ok: true, workspaceId, appId, links: { ... } } exit 0\n\nconst bundlesTestPushCommand = defineCommand({\n meta: {\n name: 'test-push',\n description: 'Send a test push so the uploader can open this bundle on their device.',\n },\n args: {\n id: {\n type: 'positional',\n description: 'Mini-app ID. Optional when `aitcc.yaml` provides `miniAppId`.',\n required: false,\n },\n 'deployment-id': {\n type: 'string',\n description: 'deploymentId of the bundle to test.',\n },\n workspace: {\n type: 'string',\n description: 'Workspace ID. Defaults to the selected workspace.',\n },\n json: { type: 'boolean', description: 'Emit machine-readable JSON.', default: false },\n },\n async run({ args }) {\n const deploymentId = typeof args['deployment-id'] === 'string' ? args['deployment-id'] : '';\n if (deploymentId === '') {\n if (args.json) {\n emitJson({ ok: false, reason: 'missing-deployment-id' });\n } else {\n process.stderr.write('app bundles test-push: --deployment-id <uuid> is required.\\n');\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n const ctx = await resolveAppOrFail({\n json: args.json,\n appIdRaw: args.id,\n ...(args.workspace !== undefined ? { workspace: args.workspace } : {}),\n });\n if (!ctx) return;\n const appId = await requireMiniAppId(ctx, args.json);\n if (appId === null) return;\n printContextHeader(ctx, { json: args.json });\n const { session, workspaceId } = ctx;\n try {\n const result = await postBundleTestPush(workspaceId, appId, deploymentId, session.cookies);\n if (args.json) {\n emitJson({ ok: true, workspaceId, appId, deploymentId, result });\n return exitAfterFlush(ExitCode.Ok);\n }\n process.stdout.write(`Sent test push for bundle ${deploymentId} (app ${appId})\\n`);\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\nconst bundlesTestLinksCommand = defineCommand({\n meta: {\n name: 'test-links',\n description: 'Show per-device test URLs for the mini-app.',\n },\n args: {\n id: {\n type: 'positional',\n description: 'Mini-app ID. Optional when `aitcc.yaml` provides `miniAppId`.',\n required: false,\n },\n workspace: {\n type: 'string',\n description: 'Workspace ID. Defaults to the selected workspace.',\n },\n json: { type: 'boolean', description: 'Emit machine-readable JSON.', default: false },\n },\n async run({ args }) {\n const ctx = await resolveAppOrFail({\n json: args.json,\n appIdRaw: args.id,\n ...(args.workspace !== undefined ? { workspace: args.workspace } : {}),\n });\n if (!ctx) return;\n const appId = await requireMiniAppId(ctx, args.json);\n if (appId === null) return;\n printContextHeader(ctx, { json: args.json });\n const { session, workspaceId } = ctx;\n try {\n const links = await fetchBundleTestLinks(workspaceId, appId, session.cookies);\n if (args.json) {\n emitJson({ ok: true, workspaceId, appId, links });\n return exitAfterFlush(ExitCode.Ok);\n }\n const keys = Object.keys(links);\n if (keys.length === 0) {\n process.stdout.write(`App ${appId} (ws ${workspaceId}): no test links available\\n`);\n return exitAfterFlush(ExitCode.Ok);\n }\n process.stdout.write(`App ${appId} (ws ${workspaceId}):\\n`);\n for (const k of keys) {\n const v = links[k];\n process.stdout.write(` ${k}\\t${typeof v === 'string' ? v : JSON.stringify(v)}\\n`);\n }\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\nconst bundlesCommand = defineCommand({\n meta: {\n name: 'bundles',\n description: 'Inspect and manage upload bundles for a mini-app.',\n },\n subCommands: {\n ls: bundlesLsCommand,\n deployed: bundlesDeployedCommand,\n upload: bundlesUploadCommand,\n review: bundlesReviewCommand,\n release: bundlesReleaseCommand,\n 'test-push': bundlesTestPushCommand,\n 'test-links': bundlesTestLinksCommand,\n },\n});\n\n// --json contract (consumed by agent-plugin):\n//\n// app certs ls <id> [--workspace <id>]:\n// { ok: true, workspaceId, appId, certs: [...] } exit 0\n// { ok: false, reason: 'invalid-id' | ... } exit 2\n//\n// mTLS certs for an app. Empty array is the common case (no certs\n// generated yet). Per-record shape is passed through opaquely until\n// we observe a populated response — agent-plugin consumers should\n// treat each entry as `Record<string, unknown>`.\n\nconst certsLsCommand = defineCommand({\n meta: {\n name: 'ls',\n description: 'List mTLS certificates issued for a mini-app.',\n },\n args: {\n id: {\n type: 'positional',\n description: 'Mini-app ID. Optional when `aitcc.yaml` provides `miniAppId`.',\n required: false,\n },\n workspace: {\n type: 'string',\n description: 'Workspace ID. Defaults to the selected workspace.',\n },\n json: { type: 'boolean', description: 'Emit machine-readable JSON.', default: false },\n },\n async run({ args }) {\n const ctx = await resolveAppOrFail({\n json: args.json,\n appIdRaw: args.id,\n ...(args.workspace !== undefined ? { workspace: args.workspace } : {}),\n });\n if (!ctx) return;\n const appId = await requireMiniAppId(ctx, args.json);\n if (appId === null) return;\n printContextHeader(ctx, { json: args.json });\n const { session, workspaceId } = ctx;\n\n try {\n const certs = await fetchCerts(workspaceId, appId, session.cookies);\n if (args.json) {\n emitJson({ ok: true, workspaceId, appId, certs });\n return exitAfterFlush(ExitCode.Ok);\n }\n if (certs.length === 0) {\n process.stdout.write(`App ${appId} (ws ${workspaceId}): no mTLS certs\\n`);\n return exitAfterFlush(ExitCode.Ok);\n }\n process.stdout.write(`App ${appId} (ws ${workspaceId}): ${certs.length} cert(s)\\n`);\n for (const c of certs) {\n const id =\n typeof c.id === 'string' || typeof c.id === 'number'\n ? c.id\n : typeof c.certId === 'string' || typeof c.certId === 'number'\n ? c.certId\n : '-';\n const cn = typeof c.commonName === 'string' ? c.commonName : '-';\n const createdAt = typeof c.createdAt === 'string' ? c.createdAt : '';\n const expiresAt =\n typeof c.expiresAt === 'string'\n ? c.expiresAt\n : typeof c.validUntil === 'string'\n ? c.validUntil\n : '';\n process.stdout.write(`${id}\\t${cn}\\t${createdAt}\\t${expiresAt}\\n`);\n }\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\n// --json contract (consumed by agent-plugin):\n//\n// app certs issue <id> --name <name> [--workspace <id>]\n// [--out <dir>] [--print-key]:\n// { ok: true, workspaceId, appId, name,\n// publicKey, privateKey?, savedTo?: { publicKey, privateKey } } exit 0\n// { ok: false, reason: 'invalid-id' | 'invalid-name'\n// | 'invalid-out-dir' } exit 2\n//\n// Issues an mTLS client certificate. The console UI's name placeholder\n// reads \"공백, 한글, 특수문자 제외\", so the CLI mirrors that rule client-side\n// before any network call: 1+ char, ASCII letters/digits/`-`/`_` only.\n//\n// `privateKey` is only present in the response when `--print-key` is set\n// or when `--out` is *not* set (i.e. the caller has explicitly asked for\n// the material on stdout). With `--out <dir>` (default agent-plugin\n// pattern) two files are written with mode 0o600 — `<name>.crt` (cert)\n// and `<name>.key` (private key) — and the JSON payload only echoes\n// the two paths. The plain-text emitter never prints the private key\n// regardless of flags; it points the user at the file and `--json` if\n// they need it programmatically.\n//\n// app certs revoke <certId> --app <id> [--workspace <id>]:\n// { ok: true, workspaceId, appId, certId } exit 0\n// { ok: false, reason: 'invalid-id' | 'missing-cert-id' } exit 2\n//\n// Hits the wire's `disable` endpoint. Naming follows the user mental\n// model (issue ↔ revoke); the console UI button itself is labelled\n// \"삭제\" and there is no reactivate path, so `revoke` is the honest\n// surface even though the path is named `disable`.\n\nconst CERT_NAME_RE = /^[A-Za-z0-9_-]+$/;\n\nfunction parseCertName(raw: string | undefined): { value: string } | { error: string } {\n if (typeof raw !== 'string' || raw.length === 0) {\n return { error: '--name is required (cert display name)' };\n }\n if (!CERT_NAME_RE.test(raw)) {\n return {\n error:\n '--name must contain only ASCII letters, digits, `-`, or `_` (no spaces, no Korean, no special chars)',\n };\n }\n return { value: raw };\n}\n\nconst certsIssueCommand = defineCommand({\n meta: {\n name: 'issue',\n description: 'Issue a new mTLS certificate for a mini-app.',\n },\n args: {\n id: {\n type: 'positional',\n description: 'Mini-app ID. Optional when `aitcc.yaml` provides `miniAppId`.',\n required: false,\n },\n workspace: {\n type: 'string',\n description: 'Workspace ID. Defaults to the selected workspace.',\n },\n name: {\n type: 'string',\n description: 'Cert display name. ASCII letters/digits/`-`/`_` only.',\n },\n out: {\n type: 'string',\n description: 'Directory to write `<name>.crt` and `<name>.key` (mode 0600).',\n },\n 'print-key': {\n type: 'boolean',\n description: 'Include the private key in the response (default: cert only).',\n default: false,\n },\n json: { type: 'boolean', description: 'Emit machine-readable JSON.', default: false },\n },\n async run({ args }) {\n const nameResult = parseCertName(args.name);\n if ('error' in nameResult) {\n if (args.json) {\n emitJson({ ok: false, reason: 'invalid-name', message: nameResult.error });\n } else {\n process.stderr.write(`app certs issue: ${nameResult.error}\\n`);\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n const name = nameResult.value;\n\n const ctx = await resolveAppOrFail({\n json: args.json,\n appIdRaw: args.id,\n ...(args.workspace !== undefined ? { workspace: args.workspace } : {}),\n });\n if (!ctx) return;\n const appId = await requireMiniAppId(ctx, args.json);\n if (appId === null) return;\n printContextHeader(ctx, { json: args.json });\n const { session, workspaceId } = ctx;\n\n try {\n const result = await issueCert(workspaceId, appId, name, session.cookies);\n\n let savedTo: { publicKey: string; privateKey: string } | undefined;\n if (typeof args.out === 'string' && args.out.length > 0) {\n const dir = resolvePath(args.out);\n try {\n await mkdir(dir, { recursive: true });\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'invalid-out-dir',\n message: `--out: cannot create ${JSON.stringify(args.out)}: ${message}`,\n });\n } else {\n process.stderr.write(\n `app certs issue: cannot create --out directory ${JSON.stringify(args.out)}: ${message}\\n`,\n );\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n const certPath = resolvePath(dir, `${name}.crt`);\n const keyPath = resolvePath(dir, `${name}.key`);\n await writeFile(certPath, result.publicKey, { mode: 0o600 });\n await writeFile(keyPath, result.privateKey, { mode: 0o600 });\n savedTo = { publicKey: certPath, privateKey: keyPath };\n }\n\n if (args.json) {\n const includeKey = args['print-key'] === true || savedTo === undefined;\n emitJson({\n ok: true,\n workspaceId,\n appId,\n name,\n publicKey: result.publicKey,\n ...(includeKey ? { privateKey: result.privateKey } : {}),\n ...(savedTo ? { savedTo } : {}),\n });\n return exitAfterFlush(ExitCode.Ok);\n }\n\n // Plain output never prints the private key; it points the user at\n // the saved files (or `--json` / `--print-key` for programmatic use).\n if (savedTo) {\n process.stdout.write(\n `App ${appId} (ws ${workspaceId}): issued cert \"${name}\"\\n` +\n ` cert: ${savedTo.publicKey}\\n` +\n ` key: ${savedTo.privateKey}\\n`,\n );\n } else {\n process.stdout.write(\n `App ${appId} (ws ${workspaceId}): issued cert \"${name}\"\\n${result.publicKey}` +\n (result.publicKey.endsWith('\\n') ? '' : '\\n') +\n 'Private key not shown. Re-run with --out <dir> or --json --print-key to capture it.\\n',\n );\n }\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\nconst certsRevokeCommand = defineCommand({\n meta: {\n name: 'revoke',\n description: 'Revoke (disable) an mTLS certificate.',\n },\n args: {\n certId: { type: 'positional', description: 'Cert ID (from `app certs ls`).', required: true },\n app: {\n type: 'string',\n description: 'Mini-app ID the cert belongs to.',\n },\n workspace: {\n type: 'string',\n description: 'Workspace ID. Defaults to the selected workspace.',\n },\n json: { type: 'boolean', description: 'Emit machine-readable JSON.', default: false },\n },\n async run({ args }) {\n const certId = typeof args.certId === 'string' ? args.certId.trim() : '';\n if (certId.length === 0) {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'missing-cert-id',\n message: 'certId positional is required',\n });\n } else {\n process.stderr.write('app certs revoke: certId is required\\n');\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n\n // certs revoke is the one Group A command whose mini-app id is a\n // dedicated `--app` flag (not a positional, because the positional is\n // <certId>). Hand the flag value to `resolveAppOrFail` as the\n // appIdRaw — `appIdField: 'app'` makes the error message say `--app\n // must be a positive integer` rather than `app id must be …`.\n const ctx = await resolveAppOrFail({\n json: args.json,\n appIdRaw: args.app,\n appIdField: 'app',\n ...(args.workspace !== undefined ? { workspace: args.workspace } : {}),\n });\n if (!ctx) return;\n const appId = await requireMiniAppId(ctx, args.json);\n if (appId === null) return;\n printContextHeader(ctx, { json: args.json });\n const { session, workspaceId } = ctx;\n\n try {\n await revokeCert(workspaceId, appId, certId, session.cookies);\n if (args.json) {\n emitJson({ ok: true, workspaceId, appId, certId });\n return exitAfterFlush(ExitCode.Ok);\n }\n process.stdout.write(`App ${appId} (ws ${workspaceId}): revoked cert ${certId}\\n`);\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\nconst certsCommand = defineCommand({\n meta: {\n name: 'certs',\n description: 'Inspect mTLS certificates for a mini-app.',\n },\n subCommands: {\n ls: certsLsCommand,\n issue: certsIssueCommand,\n revoke: certsRevokeCommand,\n },\n});\n\n// --json contract (consumed by agent-plugin):\n//\n// app metrics <id> [--workspace <id>] [--time-unit DAY|WEEK|MONTH]\n// [--start YYYY-MM-DD] [--end YYYY-MM-DD] [--refresh]:\n// { ok: true, workspaceId, appId, timeUnitType, startDate, endDate,\n// cacheTime?, metrics: [...] } exit 0\n// { ok: false, reason: 'invalid-id' | 'invalid-date'\n// | 'invalid-time-unit' | ... } exit 2\n//\n// Conversion metrics for an app over a date range. An empty `metrics`\n// array is the common case for PREPARE-state apps (no live traffic);\n// per-record shape is passed through opaquely. Default window: the last\n// 30 days ending today (host local date). `--refresh` bypasses the\n// server-side cache.\n\nconst VALID_TIME_UNITS: readonly MetricsTimeUnit[] = ['DAY', 'WEEK', 'MONTH'];\n\nfunction parseIsoDate(raw: string, field: string): { value: string } | { error: string } {\n if (!/^\\d{4}-\\d{2}-\\d{2}$/.test(raw)) {\n return { error: `--${field} must be YYYY-MM-DD (got ${JSON.stringify(raw)})` };\n }\n const d = new Date(`${raw}T00:00:00Z`);\n if (Number.isNaN(d.getTime())) {\n return { error: `--${field} is not a valid date (got ${JSON.stringify(raw)})` };\n }\n return { value: raw };\n}\n\nfunction todayLocalIso(): string {\n const d = new Date();\n const y = d.getFullYear();\n const m = String(d.getMonth() + 1).padStart(2, '0');\n const day = String(d.getDate()).padStart(2, '0');\n return `${y}-${m}-${day}`;\n}\n\nfunction daysAgoLocalIso(days: number): string {\n const d = new Date();\n d.setDate(d.getDate() - days);\n const y = d.getFullYear();\n const m = String(d.getMonth() + 1).padStart(2, '0');\n const day = String(d.getDate()).padStart(2, '0');\n return `${y}-${m}-${day}`;\n}\n\nconst metricsCommand = defineCommand({\n meta: {\n name: 'metrics',\n description: 'Show conversion metrics for a mini-app over a date range.',\n },\n args: {\n id: {\n type: 'positional',\n description: 'Mini-app ID. Optional when `aitcc.yaml` provides `miniAppId`.',\n required: false,\n },\n workspace: {\n type: 'string',\n description: 'Workspace ID. Defaults to the selected workspace.',\n },\n 'time-unit': {\n type: 'string',\n description: 'Bucket size: DAY | WEEK | MONTH.',\n default: 'DAY',\n },\n start: {\n type: 'string',\n description: 'Start date (YYYY-MM-DD). Defaults to 30 days before --end.',\n },\n end: {\n type: 'string',\n description: 'End date (YYYY-MM-DD). Defaults to today (host local).',\n },\n refresh: {\n type: 'boolean',\n description: 'Bypass server-side cache.',\n default: false,\n },\n json: { type: 'boolean', description: 'Emit machine-readable JSON.', default: false },\n },\n async run({ args }) {\n const timeUnit = String(args['time-unit']).toUpperCase();\n if (!VALID_TIME_UNITS.includes(timeUnit as MetricsTimeUnit)) {\n const message = `--time-unit must be one of ${VALID_TIME_UNITS.join('|')} (got ${JSON.stringify(args['time-unit'])})`;\n if (args.json) emitJson({ ok: false, reason: 'invalid-time-unit', message });\n else process.stderr.write(`${message}\\n`);\n return exitAfterFlush(ExitCode.Usage);\n }\n\n const endDate = args.end ? String(args.end) : todayLocalIso();\n const endResult = parseIsoDate(endDate, 'end');\n if ('error' in endResult) {\n if (args.json) emitJson({ ok: false, reason: 'invalid-date', message: endResult.error });\n else process.stderr.write(`${endResult.error}\\n`);\n return exitAfterFlush(ExitCode.Usage);\n }\n\n const startDate = args.start ? String(args.start) : daysAgoLocalIso(30);\n const startResult = parseIsoDate(startDate, 'start');\n if ('error' in startResult) {\n if (args.json) emitJson({ ok: false, reason: 'invalid-date', message: startResult.error });\n else process.stderr.write(`${startResult.error}\\n`);\n return exitAfterFlush(ExitCode.Usage);\n }\n\n if (startResult.value > endResult.value) {\n const message = `--start (${startResult.value}) must be on or before --end (${endResult.value})`;\n if (args.json) emitJson({ ok: false, reason: 'invalid-date', message });\n else process.stderr.write(`${message}\\n`);\n return exitAfterFlush(ExitCode.Usage);\n }\n\n const ctx = await resolveAppOrFail({\n json: args.json,\n appIdRaw: args.id,\n ...(args.workspace !== undefined ? { workspace: args.workspace } : {}),\n });\n if (!ctx) return;\n const appId = await requireMiniAppId(ctx, args.json);\n if (appId === null) return;\n printContextHeader(ctx, { json: args.json });\n const { session, workspaceId } = ctx;\n\n try {\n const result = await fetchConversionMetrics(\n {\n workspaceId,\n miniAppId: appId,\n timeUnitType: timeUnit as MetricsTimeUnit,\n startDate: startResult.value,\n endDate: endResult.value,\n refresh: args.refresh,\n },\n session.cookies,\n );\n if (args.json) {\n emitJson({\n ok: true,\n workspaceId,\n appId,\n timeUnitType: timeUnit,\n startDate: startResult.value,\n endDate: endResult.value,\n ...(result.cacheTime !== undefined ? { cacheTime: result.cacheTime } : {}),\n metrics: result.metrics,\n });\n return exitAfterFlush(ExitCode.Ok);\n }\n const header = `App ${appId} (ws ${workspaceId}) · ${timeUnit} · ${startResult.value} → ${endResult.value}`;\n if (result.metrics.length === 0) {\n process.stdout.write(`${header}: no metrics\\n`);\n return exitAfterFlush(ExitCode.Ok);\n }\n process.stdout.write(`${header}: ${result.metrics.length} bucket(s)\\n`);\n for (const m of result.metrics) {\n const date =\n typeof m.date === 'string'\n ? m.date\n : typeof m.bucketDate === 'string'\n ? m.bucketDate\n : '';\n const impressions =\n typeof m.impressions === 'number'\n ? m.impressions\n : typeof m.impressionCount === 'number'\n ? m.impressionCount\n : '';\n const clicks =\n typeof m.clicks === 'number'\n ? m.clicks\n : typeof m.clickCount === 'number'\n ? m.clickCount\n : '';\n process.stdout.write(`${date}\\t${impressions}\\t${clicks}\\n`);\n }\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\n// --json contract (consumed by agent-plugin):\n//\n// app share-rewards ls <id> [--workspace <id>] [--search <text>]:\n// { ok: true, workspaceId, appId, rewards: [...] } exit 0\n// { ok: false, reason: 'invalid-id' | ... } exit 2\n//\n// app messages ls <id> [--workspace <id>] [--page N] [--size N] [--search <text>]:\n// { ok: true, workspaceId, appId, campaigns: [...], paging: {...} } exit 0\n// { ok: false, reason: 'invalid-id' | 'invalid-page' | 'invalid-size' | ... } exit 2\n//\n// app events ls <id> [--workspace <id>] [--page N] [--size N] [--search <text>] [--refresh]:\n// { ok: true, workspaceId, appId, events: [...], cacheTime, paging: {...} } exit 0\n// { ok: false, reason: 'invalid-id' | 'invalid-page' | 'invalid-size' | ... } exit 2\n//\n// app templates ls <id> [--workspace <id>] [--page N] [--size N] [--content-reach-type FUNCTIONAL|MARKETING] [--smart-message true|false]:\n// { ok: true, workspaceId, appId, templates: [...], totalPageCount } exit 0\n// { ok: false, reason: 'invalid-id' | 'invalid-page' | 'invalid-size' |\n// 'invalid-content-reach-type' | 'invalid-smart-message' } exit 2\n//\n// app categories [--selectable]:\n// { ok: true, categories: CategoryTreeEntry[] } exit 0\n// { ok: true, authenticated: false } exit 10\n//\n// app service-status <id> [--workspace <id>]:\n// { ok: true, workspaceId, appId, serviceStatus, shutdownCandidateStatus,\n// scheduledShutdownAt } exit 0\n// { ok: false, reason: 'invalid-id' | ... } exit 2\n//\n// Share-reward promotions for an app. Empty array is the common case\n// (no promotions set up). Per-record shape is passed through opaquely\n// until a populated response is observed.\n\nconst shareRewardsLsCommand = defineCommand({\n meta: {\n name: 'ls',\n description: 'List share-reward promotions configured for a mini-app.',\n },\n args: {\n id: {\n type: 'positional',\n description: 'Mini-app ID. Optional when `aitcc.yaml` provides `miniAppId`.',\n required: false,\n },\n workspace: {\n type: 'string',\n description: 'Workspace ID. Defaults to the selected workspace.',\n },\n search: {\n type: 'string',\n description: 'Filter by title (server-side title-contains match). Empty matches everything.',\n },\n json: { type: 'boolean', description: 'Emit machine-readable JSON.', default: false },\n },\n async run({ args }) {\n const ctx = await resolveAppOrFail({\n json: args.json,\n appIdRaw: args.id,\n ...(args.workspace !== undefined ? { workspace: args.workspace } : {}),\n });\n if (!ctx) return;\n const appId = await requireMiniAppId(ctx, args.json);\n if (appId === null) return;\n printContextHeader(ctx, { json: args.json });\n const { session, workspaceId } = ctx;\n\n try {\n const rewards = await fetchShareRewards(\n {\n workspaceId,\n miniAppId: appId,\n ...(args.search !== undefined ? { search: String(args.search) } : {}),\n },\n session.cookies,\n );\n if (args.json) {\n emitJson({ ok: true, workspaceId, appId, rewards });\n return exitAfterFlush(ExitCode.Ok);\n }\n if (rewards.length === 0) {\n process.stdout.write(`App ${appId} (ws ${workspaceId}): no share-reward promotions\\n`);\n return exitAfterFlush(ExitCode.Ok);\n }\n process.stdout.write(`App ${appId} (ws ${workspaceId}): ${rewards.length} share-reward(s)\\n`);\n for (const r of rewards) {\n const id =\n typeof r.id === 'string' || typeof r.id === 'number'\n ? r.id\n : typeof r.rewardId === 'string' || typeof r.rewardId === 'number'\n ? r.rewardId\n : '-';\n const title =\n typeof r.title === 'string' ? r.title : typeof r.name === 'string' ? r.name : '-';\n const status = typeof r.status === 'string' ? r.status : '-';\n process.stdout.write(`${id}\\t${title}\\t${status}\\n`);\n }\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\nconst shareRewardsCommand = defineCommand({\n meta: {\n name: 'share-rewards',\n description: 'Inspect share-reward promotions for a mini-app.',\n },\n subCommands: {\n ls: shareRewardsLsCommand,\n },\n});\n\nconst messagesLsCommand = defineCommand({\n meta: {\n name: 'ls',\n description: 'List smart-message campaigns (formerly \"push\" — the 스마트 발송 menu).',\n },\n args: {\n id: {\n type: 'positional',\n description: 'Mini-app ID. Optional when `aitcc.yaml` provides `miniAppId`.',\n required: false,\n },\n workspace: {\n type: 'string',\n description: 'Workspace ID. Defaults to the selected workspace.',\n },\n page: { type: 'string', description: 'Page number (0-indexed).', default: '0' },\n size: { type: 'string', description: 'Page size.', default: '20' },\n search: { type: 'string', description: 'Title-contains filter. Empty matches everything.' },\n json: { type: 'boolean', description: 'Emit machine-readable JSON.', default: false },\n },\n async run({ args }) {\n const pageResult = parseNonNegativeInt(String(args.page), 'page');\n if ('error' in pageResult) {\n if (args.json) emitJson({ ok: false, reason: 'invalid-page', message: pageResult.error });\n else process.stderr.write(`${pageResult.error}\\n`);\n return exitAfterFlush(ExitCode.Usage);\n }\n const sizeResult = parseNonNegativeInt(String(args.size), 'size');\n if ('error' in sizeResult) {\n if (args.json) emitJson({ ok: false, reason: 'invalid-size', message: sizeResult.error });\n else process.stderr.write(`${sizeResult.error}\\n`);\n return exitAfterFlush(ExitCode.Usage);\n }\n\n const ctx = await resolveAppOrFail({\n json: args.json,\n appIdRaw: args.id,\n ...(args.workspace !== undefined ? { workspace: args.workspace } : {}),\n });\n if (!ctx) return;\n const appId = await requireMiniAppId(ctx, args.json);\n if (appId === null) return;\n printContextHeader(ctx, { json: args.json });\n const { session, workspaceId } = ctx;\n\n try {\n const result = await fetchSmartMessageCampaigns(\n {\n workspaceId,\n miniAppId: appId,\n page: pageResult.value,\n size: sizeResult.value,\n ...(args.search !== undefined ? { search: String(args.search) } : {}),\n },\n session.cookies,\n );\n if (args.json) {\n emitJson({\n ok: true,\n workspaceId,\n appId,\n campaigns: result.items,\n paging: result.paging,\n });\n return exitAfterFlush(ExitCode.Ok);\n }\n if (result.items.length === 0) {\n process.stdout.write(`App ${appId} (ws ${workspaceId}): no smart-message campaigns\\n`);\n return exitAfterFlush(ExitCode.Ok);\n }\n process.stdout.write(\n `App ${appId} (ws ${workspaceId}): ${result.items.length} campaign(s) on page ${result.paging.pageNumber} of ${result.paging.totalCount}\\n`,\n );\n for (const c of result.items) {\n const id =\n typeof c.id === 'string' || typeof c.id === 'number'\n ? c.id\n : typeof c.campaignId === 'string' || typeof c.campaignId === 'number'\n ? c.campaignId\n : '-';\n const title =\n typeof c.title === 'string' ? c.title : typeof c.name === 'string' ? c.name : '-';\n const status = typeof c.status === 'string' ? c.status : '-';\n process.stdout.write(`${id}\\t${title}\\t${status}\\n`);\n }\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\nconst messagesCommand = defineCommand({\n meta: {\n name: 'messages',\n description: 'Inspect smart-message (formerly push) campaigns for a mini-app.',\n },\n subCommands: {\n ls: messagesLsCommand,\n },\n});\n\nconst eventsLsCommand = defineCommand({\n meta: {\n name: 'ls',\n description: 'List custom event catalogs recorded for a mini-app (the 이벤트 menu).',\n },\n args: {\n id: {\n type: 'positional',\n description: 'Mini-app ID. Optional when `aitcc.yaml` provides `miniAppId`.',\n required: false,\n },\n workspace: {\n type: 'string',\n description: 'Workspace ID. Defaults to the selected workspace.',\n },\n page: { type: 'string', description: 'Page number (0-indexed).', default: '0' },\n size: { type: 'string', description: 'Page size.', default: '20' },\n search: { type: 'string', description: 'Event-name filter. Empty matches everything.' },\n refresh: {\n type: 'boolean',\n description: 'Bypass the server cache and rebuild the catalog list.',\n default: false,\n },\n json: { type: 'boolean', description: 'Emit machine-readable JSON.', default: false },\n },\n async run({ args }) {\n const pageResult = parseNonNegativeInt(String(args.page), 'page');\n if ('error' in pageResult) {\n if (args.json) emitJson({ ok: false, reason: 'invalid-page', message: pageResult.error });\n else process.stderr.write(`${pageResult.error}\\n`);\n return exitAfterFlush(ExitCode.Usage);\n }\n const sizeResult = parseNonNegativeInt(String(args.size), 'size');\n if ('error' in sizeResult) {\n if (args.json) emitJson({ ok: false, reason: 'invalid-size', message: sizeResult.error });\n else process.stderr.write(`${sizeResult.error}\\n`);\n return exitAfterFlush(ExitCode.Usage);\n }\n\n const ctx = await resolveAppOrFail({\n json: args.json,\n appIdRaw: args.id,\n ...(args.workspace !== undefined ? { workspace: args.workspace } : {}),\n });\n if (!ctx) return;\n const appId = await requireMiniAppId(ctx, args.json);\n if (appId === null) return;\n printContextHeader(ctx, { json: args.json });\n const { session, workspaceId } = ctx;\n\n try {\n const result = await fetchAppEventCatalogs(\n {\n workspaceId,\n miniAppId: appId,\n pageNumber: pageResult.value,\n pageSize: sizeResult.value,\n ...(args.search !== undefined ? { search: String(args.search) } : {}),\n ...(args.refresh ? { refresh: true } : {}),\n },\n session.cookies,\n );\n if (args.json) {\n emitJson({\n ok: true,\n workspaceId,\n appId,\n events: result.results,\n cacheTime: result.cacheTime ?? null,\n paging: result.paging,\n });\n return exitAfterFlush(ExitCode.Ok);\n }\n if (result.results.length === 0) {\n const ct = result.cacheTime ? ` (cached ${result.cacheTime})` : '';\n process.stdout.write(`App ${appId} (ws ${workspaceId}): no event catalogs${ct}\\n`);\n return exitAfterFlush(ExitCode.Ok);\n }\n process.stdout.write(\n `App ${appId} (ws ${workspaceId}): ${result.results.length} event(s) on page ${result.paging.pageNumber} of ${result.paging.totalPages}\\n`,\n );\n for (const e of result.results) {\n const name =\n typeof e.name === 'string' ? e.name : typeof e.eventName === 'string' ? e.eventName : '-';\n const count =\n typeof e.count === 'number'\n ? String(e.count)\n : typeof e.totalCount === 'number'\n ? String(e.totalCount)\n : '-';\n process.stdout.write(`${name}\\t${count}\\n`);\n }\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\nconst eventsCommand = defineCommand({\n meta: {\n name: 'events',\n description: 'Inspect custom event catalogs (log search) for a mini-app.',\n },\n subCommands: {\n ls: eventsLsCommand,\n },\n});\n\nconst templatesLsCommand = defineCommand({\n meta: {\n name: 'ls',\n description:\n 'List the smart-message composer templates available for a mini-app (the 템플릿 picker in 스마트 발송).',\n },\n args: {\n id: {\n type: 'positional',\n description: 'Mini-app ID. Optional when `aitcc.yaml` provides `miniAppId`.',\n required: false,\n },\n workspace: {\n type: 'string',\n description: 'Workspace ID. Defaults to the selected workspace.',\n },\n page: { type: 'string', description: 'Page number (0-indexed).', default: '0' },\n size: { type: 'string', description: 'Page size.', default: '20' },\n 'content-reach-type': {\n type: 'string',\n description: `Template reach bucket: ${TEMPLATE_CONTENT_REACH_TYPES.join(' | ')}. Omit for all.`,\n },\n 'smart-message': {\n type: 'string',\n description:\n 'Filter to templates compatible with smart-message (\"true\") or legacy push (\"false\"). Omit for all.',\n },\n json: { type: 'boolean', description: 'Emit machine-readable JSON.', default: false },\n },\n async run({ args }) {\n const pageResult = parseNonNegativeInt(String(args.page), 'page');\n if ('error' in pageResult) {\n if (args.json) emitJson({ ok: false, reason: 'invalid-page', message: pageResult.error });\n else process.stderr.write(`${pageResult.error}\\n`);\n return exitAfterFlush(ExitCode.Usage);\n }\n const sizeResult = parseNonNegativeInt(String(args.size), 'size');\n if ('error' in sizeResult) {\n if (args.json) emitJson({ ok: false, reason: 'invalid-size', message: sizeResult.error });\n else process.stderr.write(`${sizeResult.error}\\n`);\n return exitAfterFlush(ExitCode.Usage);\n }\n\n let contentReachType: TemplateContentReachType | undefined;\n if (args['content-reach-type'] !== undefined) {\n const upper = String(args['content-reach-type']).toUpperCase();\n if ((TEMPLATE_CONTENT_REACH_TYPES as readonly string[]).includes(upper)) {\n contentReachType = upper as TemplateContentReachType;\n } else {\n const message = `--content-reach-type must be one of: ${TEMPLATE_CONTENT_REACH_TYPES.join(', ')}`;\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'invalid-content-reach-type',\n allowed: [...TEMPLATE_CONTENT_REACH_TYPES],\n });\n } else {\n process.stderr.write(`${message}\\n`);\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n }\n\n let isSmartMessage: boolean | undefined;\n if (args['smart-message'] !== undefined) {\n const raw = String(args['smart-message']).toLowerCase();\n if (raw === 'true') isSmartMessage = true;\n else if (raw === 'false') isSmartMessage = false;\n else {\n const message = '--smart-message must be \"true\" or \"false\"';\n if (args.json) emitJson({ ok: false, reason: 'invalid-smart-message', message });\n else process.stderr.write(`${message}\\n`);\n return exitAfterFlush(ExitCode.Usage);\n }\n }\n\n const ctx = await resolveAppOrFail({\n json: args.json,\n appIdRaw: args.id,\n ...(args.workspace !== undefined ? { workspace: args.workspace } : {}),\n });\n if (!ctx) return;\n const appId = await requireMiniAppId(ctx, args.json);\n if (appId === null) return;\n printContextHeader(ctx, { json: args.json });\n const { session, workspaceId } = ctx;\n\n try {\n const result = await fetchAppTemplates(\n {\n workspaceId,\n miniAppId: appId,\n page: pageResult.value,\n size: sizeResult.value,\n ...(contentReachType !== undefined ? { contentReachType } : {}),\n ...(isSmartMessage !== undefined ? { isSmartMessage } : {}),\n },\n session.cookies,\n );\n if (args.json) {\n emitJson({\n ok: true,\n workspaceId,\n appId,\n templates: result.templates,\n totalPageCount: result.totalPageCount,\n });\n return exitAfterFlush(ExitCode.Ok);\n }\n if (result.templates.length === 0) {\n process.stdout.write(`App ${appId} (ws ${workspaceId}): no templates\\n`);\n return exitAfterFlush(ExitCode.Ok);\n }\n process.stdout.write(\n `App ${appId} (ws ${workspaceId}): ${result.templates.length} template(s) of ${result.totalPageCount} page(s)\\n`,\n );\n for (const t of result.templates) {\n const id =\n typeof t.id === 'string' || typeof t.id === 'number'\n ? t.id\n : typeof t.templateId === 'string' || typeof t.templateId === 'number'\n ? t.templateId\n : '-';\n const title =\n typeof t.title === 'string' ? t.title : typeof t.name === 'string' ? t.name : '-';\n const type = typeof t.templateType === 'string' ? t.templateType : '-';\n process.stdout.write(`${id}\\t${title}\\t${type}\\n`);\n }\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\nconst templatesCommand = defineCommand({\n meta: {\n name: 'templates',\n description: 'Inspect smart-message composer templates available for a mini-app.',\n },\n subCommands: {\n ls: templatesLsCommand,\n },\n});\n\nconst categoriesCommand = defineCommand({\n meta: {\n name: 'categories',\n description: \"List the impression category tree used by `app register`'s `categoryIds` field.\",\n },\n args: {\n selectable: {\n type: 'boolean',\n description: 'Only show categories flagged `isSelectable: true` — the ones you can pick.',\n default: false,\n },\n json: { type: 'boolean', description: 'Emit machine-readable JSON to stdout.', default: false },\n },\n async run({ args }) {\n const session = await readSession();\n if (!session) {\n emitNotAuthenticated(args.json);\n return exitAfterFlush(ExitCode.NotAuthenticated);\n }\n\n try {\n const tree = await fetchImpressionCategoryList(session.cookies);\n\n // Optional filter collapses non-selectable groups and categories. Kept\n // off by default because the flag `isSelectable: false` entries still\n // show up in live app payloads (eg. `금융` group) and are useful for\n // agents debugging a rejected registration.\n const filtered = args.selectable\n ? tree\n .filter((g) => g.categoryGroup.isSelectable)\n .map((g) => ({\n ...g,\n categoryList: g.categoryList\n .filter((c) => c.isSelectable)\n .map((c) => ({\n ...c,\n subCategoryList: c.subCategoryList.filter((s) => s.isSelectable),\n })),\n }))\n : tree;\n\n if (args.json) {\n emitJson({ ok: true, categories: filtered });\n return exitAfterFlush(ExitCode.Ok);\n }\n for (const g of filtered) {\n const mark = g.categoryGroup.isSelectable ? '' : ' (not selectable)';\n process.stdout.write(`[${g.categoryGroup.id}] ${g.categoryGroup.name}${mark}\\n`);\n for (const c of g.categoryList) {\n const cmark = c.isSelectable ? '' : ' (not selectable)';\n process.stdout.write(` ${c.id}\\t${c.name}${cmark}\\n`);\n for (const s of c.subCategoryList) {\n const smark = s.isSelectable ? '' : ' (not selectable)';\n process.stdout.write(` ${s.id}\\t${s.name}${smark}\\n`);\n }\n }\n }\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\nconst serviceStatusCommand = defineCommand({\n meta: {\n name: 'service-status',\n description:\n 'Show the server-authoritative runtime status of a mini-app (serviceStatus, shutdown schedule).',\n },\n args: {\n id: {\n type: 'positional',\n description: 'Mini-app ID. Optional when `aitcc.yaml` provides `miniAppId`.',\n required: false,\n },\n workspace: {\n type: 'string',\n description: 'Workspace ID. Defaults to the selected workspace.',\n },\n json: { type: 'boolean', description: 'Emit machine-readable JSON.', default: false },\n },\n async run({ args }) {\n const ctx = await resolveAppOrFail({\n json: args.json,\n appIdRaw: args.id,\n ...(args.workspace !== undefined ? { workspace: args.workspace } : {}),\n });\n if (!ctx) return;\n const appId = await requireMiniAppId(ctx, args.json);\n if (appId === null) return;\n printContextHeader(ctx, { json: args.json });\n const { session, workspaceId } = ctx;\n\n try {\n const st = await fetchAppServiceStatus(workspaceId, appId, session.cookies);\n if (args.json) {\n emitJson({ ok: true, workspaceId, appId, ...st });\n return exitAfterFlush(ExitCode.Ok);\n }\n process.stdout.write(`App ${appId} (ws ${workspaceId}):\\n`);\n process.stdout.write(` serviceStatus: ${st.serviceStatus}\\n`);\n process.stdout.write(` shutdownCandidateStatus: ${st.shutdownCandidateStatus ?? 'null'}\\n`);\n process.stdout.write(` scheduledShutdownAt: ${st.scheduledShutdownAt ?? 'null'}\\n`);\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\nconst initCommand = defineCommand({\n meta: {\n name: 'init',\n description:\n 'Scaffold a well-formed `aitcc.yaml` interactively. Asks for the required ' +\n 'manifest fields, picks the workspace from the live API list, and lays the ' +\n 'optional fields as commented lines for later edits.',\n },\n args: {\n cwd: {\n type: 'string',\n description: 'Directory to write `aitcc.yaml` into (default: current directory).',\n },\n force: {\n type: 'boolean',\n description: 'Overwrite an existing project file instead of erroring.',\n default: false,\n },\n json: {\n type: 'boolean',\n description: 'Emit machine-readable JSON to stdout.',\n default: false,\n },\n },\n async run({ args }) {\n await runAppInit({\n ...(args.cwd !== undefined ? { cwd: args.cwd as string } : {}),\n force: args.force,\n json: args.json,\n });\n },\n});\n\nconst registerCommand = defineCommand({\n meta: {\n name: 'register',\n description:\n 'Register a mini-app in the selected workspace from a YAML/JSON manifest. ' +\n 'Uploads logo/thumbnail/screenshots, then submits the create payload.',\n },\n args: {\n workspace: {\n type: 'string',\n description: 'Workspace ID. Defaults to the selected workspace (`aitcc workspace use`).',\n },\n config: {\n type: 'string',\n description: 'Path to the app manifest. Defaults to `./aitcc.yaml`, then `./aitcc.json`.',\n },\n 'dry-run': {\n type: 'boolean',\n description: 'Validate manifest + images and print the inferred submit payload; no uploads.',\n default: false,\n },\n 'accept-terms': {\n type: 'boolean',\n description:\n 'Attest to the required console legal-agreement checkboxes (see VALIDATION-RULES.md). Required for real submits.',\n default: false,\n },\n json: { type: 'boolean', description: 'Emit machine-readable JSON to stdout.', default: false },\n },\n async run({ args }) {\n await runRegister({\n json: args.json,\n dryRun: args['dry-run'],\n acceptTerms: args['accept-terms'],\n ...(args.workspace !== undefined ? { workspace: args.workspace } : {}),\n ...(args.config !== undefined ? { config: args.config } : {}),\n });\n },\n});\n\n// --json contract (consumed by agent-plugin):\n//\n// app deploy <path> --app <id> [--deployment-id <uuid>] [--memo <text>]\n// [--request-review --release-notes <text>]\n// [--release --confirm] [--workspace <id>]\n// [--dry-run] [--json]:\n//\n// One-shot wrapper over `app bundles upload|review|release`. Always\n// performs the 3-step upload; downstream review/release steps run only\n// when their opt-in flags are passed. See `runDeploy` for the full\n// success/failure shape — this citty wrapper is just an argument shim.\n//\n// deploymentId auto-detect: if `--deployment-id` is omitted, the .ait is\n// cracked to read `_metadata.deploymentId` from `app.json`. This is the\n// primary convenience the wrapper adds over the three-command dance.\nconst deployCommand = defineCommand({\n meta: {\n name: 'deploy',\n description:\n 'Upload a bundle, optionally request review, optionally release. ' +\n 'Auto-detects deploymentId from the .ait if --deployment-id is omitted.',\n },\n args: {\n path: { type: 'positional', description: 'Path to the .ait bundle file.', required: true },\n app: {\n type: 'string',\n description:\n 'Mini-app ID. Optional when AITCC_APP env or `miniAppId` in aitcc.yaml supplies one.',\n },\n 'deployment-id': {\n type: 'string',\n description:\n 'deploymentId of the bundle. Defaults to app.json._metadata.deploymentId inside the .ait.',\n },\n memo: { type: 'string', description: 'Optional memo attached to the uploaded bundle.' },\n 'request-review': {\n type: 'boolean',\n description: 'After upload, submit the bundle for review.',\n default: false,\n },\n 'release-notes': {\n type: 'string',\n description: 'Release notes for the review request. Required with --request-review.',\n },\n release: {\n type: 'boolean',\n description:\n 'After review submit, publish the bundle. Requires the bundle to already be APPROVED; ' +\n 'typically used on a second `app deploy` run.',\n default: false,\n },\n confirm: {\n type: 'boolean',\n description: 'Required with --release — confirms the destructive publish step.',\n default: false,\n },\n workspace: {\n type: 'string',\n description: 'Workspace ID. Defaults to the selected workspace.',\n },\n 'dry-run': {\n type: 'boolean',\n description: 'Print the planned pipeline without touching the server.',\n default: false,\n },\n json: { type: 'boolean', description: 'Emit machine-readable JSON.', default: false },\n },\n async run({ args }) {\n await runDeploy({\n path: typeof args.path === 'string' ? args.path : '',\n app: typeof args.app === 'string' ? args.app : undefined,\n json: args.json,\n dryRun: args['dry-run'],\n requestReview: args['request-review'],\n release: args.release,\n confirm: args.confirm,\n ...(args['deployment-id'] !== undefined\n ? { deploymentId: args['deployment-id'] as string }\n : {}),\n ...(args.memo !== undefined ? { memo: args.memo as string } : {}),\n ...(args['release-notes'] !== undefined\n ? { releaseNotes: args['release-notes'] as string }\n : {}),\n ...(args.workspace !== undefined ? { workspace: args.workspace as string } : {}),\n });\n },\n});\n\nexport const appCommand = defineCommand({\n meta: {\n name: 'app',\n description: 'Inspect mini-apps in a workspace.',\n },\n subCommands: {\n init: initCommand,\n ls: lsCommand,\n show: showCommand,\n status: statusCommand,\n ratings: ratingsCommand,\n reports: reportsCommand,\n bundles: bundlesCommand,\n certs: certsCommand,\n metrics: metricsCommand,\n 'share-rewards': shareRewardsCommand,\n messages: messagesCommand,\n events: eventsCommand,\n templates: templatesCommand,\n categories: categoriesCommand,\n 'service-status': serviceStatusCommand,\n register: registerCommand,\n deploy: deployCommand,\n },\n});\n","import { spawn } from 'node:child_process';\n\n// Service identifier used as the keychain \"service\" / Windows target name\n// prefix / libsecret schema attribute. Stable across versions — changing it\n// would orphan existing entries on user machines.\nexport const CREDENTIAL_SERVICE = 'aitcc.credentials';\n\nexport interface CredentialBackend {\n readonly name: string;\n get(account: string): Promise<string | null>;\n set(account: string, password: string): Promise<void>;\n clear(account: string): Promise<{ existed: boolean }>;\n}\n\nexport class CredentialBackendUnsupportedError extends Error {\n constructor(\n readonly platform: NodeJS.Platform,\n readonly hint: string,\n ) {\n super(`No supported credential backend for platform \"${platform}\". ${hint}`);\n this.name = 'CredentialBackendUnsupportedError';\n }\n}\n\nexport class CredentialBackendCommandError extends Error {\n constructor(\n readonly command: string,\n readonly exitCode: number | null,\n // Pass stderr through `redactStderr` before constructing — that helper\n // bounds the length so a runaway stderr can't blow up logs, but does\n // NOT scrub argv-echoed secrets. macOS `security` and Linux\n // `secret-tool` don't echo argv on error in observed failure paths;\n // Linux uses stdin so its argv is clean to begin with. If a backend\n // ever starts echoing, tighten this here.\n readonly redactedStderr: string,\n ) {\n super(\n `Credential backend command \"${command}\" failed (exit=${exitCode ?? 'null'}): ${redactedStderr}`,\n );\n this.name = 'CredentialBackendCommandError';\n }\n}\n\nexport interface RunOpts {\n readonly args: readonly string[];\n readonly stdin?: string;\n}\n\nexport interface RunResult {\n readonly exitCode: number | null;\n readonly stdout: string;\n readonly stderr: string;\n}\n\nexport async function runCommand(command: string, opts: RunOpts): Promise<RunResult> {\n return new Promise<RunResult>((resolve, reject) => {\n const child = spawn(command, [...opts.args], { stdio: ['pipe', 'pipe', 'pipe'] });\n let stdout = '';\n let stderr = '';\n child.stdout?.on('data', (c: Buffer) => {\n stdout += c.toString('utf8');\n });\n child.stderr?.on('data', (c: Buffer) => {\n stderr += c.toString('utf8');\n });\n child.on('error', (err) => reject(err));\n child.on('close', (code) => {\n resolve({ exitCode: code, stdout, stderr });\n });\n if (opts.stdin !== undefined) {\n child.stdin?.write(opts.stdin);\n }\n child.stdin?.end();\n });\n}\n\nexport function isCommandNotFound(err: unknown): boolean {\n return (err as NodeJS.ErrnoException).code === 'ENOENT';\n}\n\nexport function stripTrailingNewline(s: string): string {\n return s.replace(/\\r?\\n$/, '');\n}\n\n// Backend command failures land in user-facing logs. macOS `security` and\n// Linux `secret-tool` don't echo argv on error in our smoke tests, but a\n// kernel-level oddity (e.g. failing exec) can make stderr unbounded.\n// Truncate aggressively + drop anything that looks remotely password-ish.\nexport function redactStderr(stderr: string): string {\n const trimmed = stderr.trim();\n if (trimmed.length === 0) return '<no stderr>';\n if (trimmed.length > 200) return `${trimmed.slice(0, 200)}… <truncated>`;\n return trimmed;\n}\n","import {\n CREDENTIAL_SERVICE,\n type CredentialBackend,\n CredentialBackendCommandError,\n CredentialBackendUnsupportedError,\n isCommandNotFound,\n type RunResult,\n redactStderr,\n runCommand,\n stripTrailingNewline,\n} from '../backend.js';\n\nconst MISSING_HINT_FULL =\n 'libsecret tools are missing. Install `libsecret-tools` (Debian/Ubuntu) or the equivalent and ensure a Secret Service provider (gnome-keyring / KWallet) is running.';\nconst MISSING_HINT_SHORT =\n 'libsecret tools are missing. Install `libsecret-tools` and ensure a Secret Service provider is running.';\n\nexport const LINUX_BACKEND: CredentialBackend = {\n name: 'libsecret',\n async get(account) {\n let result: RunResult;\n try {\n result = await runCommand('secret-tool', {\n args: ['lookup', 'service', CREDENTIAL_SERVICE, 'account', account],\n });\n } catch (err) {\n if (isCommandNotFound(err)) {\n throw new CredentialBackendUnsupportedError('linux', MISSING_HINT_FULL);\n }\n throw err;\n }\n // `secret-tool lookup` exits 0 with no stdout when the entry is\n // missing on some distros, non-zero on others. Empty stdout = missing\n // either way. We check exit code first so a failed lookup that wrote\n // partial bytes to stdout never gets returned as the \"password\".\n if (result.exitCode !== 0) {\n if (result.stdout.length === 0) return null;\n throw new CredentialBackendCommandError(\n 'secret-tool lookup',\n result.exitCode,\n redactStderr(result.stderr),\n );\n }\n if (result.stdout.length === 0) return null;\n const password = stripTrailingNewline(result.stdout);\n return password.length > 0 ? password : null;\n },\n async set(account, password) {\n let result: RunResult;\n try {\n // `store` reads the secret from stdin — keeps argv clean.\n result = await runCommand('secret-tool', {\n args: [\n 'store',\n '--label',\n 'aitcc Toss Business credentials',\n 'service',\n CREDENTIAL_SERVICE,\n 'account',\n account,\n ],\n stdin: password,\n });\n } catch (err) {\n if (isCommandNotFound(err)) {\n throw new CredentialBackendUnsupportedError('linux', MISSING_HINT_SHORT);\n }\n throw err;\n }\n if (result.exitCode !== 0) {\n throw new CredentialBackendCommandError(\n 'secret-tool store',\n result.exitCode,\n redactStderr(result.stderr),\n );\n }\n },\n async clear(account) {\n let result: RunResult;\n try {\n result = await runCommand('secret-tool', {\n args: ['clear', 'service', CREDENTIAL_SERVICE, 'account', account],\n });\n } catch (err) {\n if (isCommandNotFound(err)) {\n throw new CredentialBackendUnsupportedError('linux', 'libsecret tools are missing.');\n }\n throw err;\n }\n // `secret-tool clear` always exits 0 even when the entry was absent.\n // Probe with `lookup` after to know whether something existed; cheaper\n // alternative is just to report `existed: true` optimistically, since\n // the user-facing impact is \"credentials are gone now\" either way.\n if (result.exitCode !== 0) {\n throw new CredentialBackendCommandError(\n 'secret-tool clear',\n result.exitCode,\n redactStderr(result.stderr),\n );\n }\n return { existed: true };\n },\n};\n","import {\n CREDENTIAL_SERVICE,\n type CredentialBackend,\n CredentialBackendCommandError,\n CredentialBackendUnsupportedError,\n isCommandNotFound,\n type RunResult,\n redactStderr,\n runCommand,\n stripTrailingNewline,\n} from '../backend.js';\n\nconst MISSING_HINT = 'macOS `security` is missing from PATH.';\n\nexport const MACOS_BACKEND: CredentialBackend = {\n name: 'macos-keychain',\n async get(account) {\n let result: RunResult;\n try {\n result = await runCommand('security', {\n args: ['find-generic-password', '-s', CREDENTIAL_SERVICE, '-a', account, '-w'],\n });\n } catch (err) {\n if (isCommandNotFound(err)) {\n throw new CredentialBackendUnsupportedError('darwin', MISSING_HINT);\n }\n throw err;\n }\n if (result.exitCode === 44) return null; // errSecItemNotFound\n if (result.exitCode !== 0) return null; // be lenient on `find` — assume missing\n const password = stripTrailingNewline(result.stdout);\n return password.length > 0 ? password : null;\n },\n async set(account, password) {\n let result: RunResult;\n try {\n // -U upserts. -A opens the ACL so subsequent `find-generic-password`\n // reads do not raise the keychain unlock prompt every call (saving\n // one prompt per `aitcc` run, including the read inside the\n // unchanged-detection path of `saveCredentials`). The trade-off is a\n // permissive ACL: any process running as the same user can read the\n // entry. We accept this for the same reason we accept argv-visible\n // passwords on `security` — the threat model is a single-user\n // machine and the OS keychain is no stronger than the login\n // session. `security` reads the password from `-w`; no stdin path\n // exists on this command.\n result = await runCommand('security', {\n args: [\n 'add-generic-password',\n '-U',\n '-A',\n '-s',\n CREDENTIAL_SERVICE,\n '-a',\n account,\n '-w',\n password,\n ],\n });\n } catch (err) {\n if (isCommandNotFound(err)) {\n throw new CredentialBackendUnsupportedError('darwin', MISSING_HINT);\n }\n throw err;\n }\n if (result.exitCode !== 0) {\n throw new CredentialBackendCommandError(\n 'security add-generic-password',\n result.exitCode,\n redactStderr(result.stderr),\n );\n }\n },\n async clear(account) {\n let result: RunResult;\n try {\n result = await runCommand('security', {\n args: ['delete-generic-password', '-s', CREDENTIAL_SERVICE, '-a', account],\n });\n } catch (err) {\n if (isCommandNotFound(err)) {\n throw new CredentialBackendUnsupportedError('darwin', MISSING_HINT);\n }\n throw err;\n }\n if (result.exitCode === 44) return { existed: false };\n if (result.exitCode === 0) return { existed: true };\n throw new CredentialBackendCommandError(\n 'security delete-generic-password',\n result.exitCode,\n redactStderr(result.stderr),\n );\n },\n};\n","import {\n CREDENTIAL_SERVICE,\n type CredentialBackend,\n CredentialBackendCommandError,\n CredentialBackendUnsupportedError,\n isCommandNotFound,\n type RunResult,\n redactStderr,\n runCommand,\n} from '../backend.js';\n\n// Stock Windows ships PowerShell which can call the CredentialManager API\n// via P/Invoke. No extra modules to install. The password round-trips as\n// hex bytes through the script body so a `ps` listing shows hex, not\n// cleartext.\n\nconst PS_HEADER = `\n$ErrorActionPreference = 'Stop';\nAdd-Type @\"\nusing System;\nusing System.Runtime.InteropServices;\n\npublic class AitccCredApi {\n [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]\n public struct CREDENTIAL {\n public uint Flags;\n public uint Type;\n public IntPtr TargetName;\n public IntPtr Comment;\n public System.Runtime.InteropServices.ComTypes.FILETIME LastWritten;\n public uint CredentialBlobSize;\n public IntPtr CredentialBlob;\n public uint Persist;\n public uint AttributeCount;\n public IntPtr Attributes;\n public IntPtr TargetAlias;\n public IntPtr UserName;\n }\n [DllImport(\"Advapi32.dll\", SetLastError = true, EntryPoint = \"CredWriteW\", CharSet = CharSet.Unicode)]\n public static extern bool CredWrite([In] ref CREDENTIAL Credential, [In] uint Flags);\n [DllImport(\"Advapi32.dll\", SetLastError = true, EntryPoint = \"CredReadW\", CharSet = CharSet.Unicode)]\n public static extern bool CredRead(string target, uint type, uint reservedFlag, out IntPtr CredentialPtr);\n [DllImport(\"Advapi32.dll\", SetLastError = true, EntryPoint = \"CredDeleteW\", CharSet = CharSet.Unicode)]\n public static extern bool CredDelete(string target, uint type, uint flags);\n [DllImport(\"Advapi32.dll\", SetLastError = true, EntryPoint = \"CredFree\")]\n public static extern void CredFree([In] IntPtr cred);\n}\n\"@\n`;\n\nconst MISSING_HINT_FULL =\n '`powershell.exe` is missing from PATH. Windows credential storage requires PowerShell.';\nconst MISSING_HINT_SHORT = '`powershell.exe` is missing from PATH.';\n\nfunction targetName(account: string): string {\n return `${CREDENTIAL_SERVICE}/${account}`;\n}\n\nfunction powerShellArgs(script: string): readonly string[] {\n return ['-NoProfile', '-NonInteractive', '-Command', script];\n}\n\nfunction escapeSingleQuotes(s: string): string {\n return s.replace(/'/g, \"''\");\n}\n\nasync function runPowerShell(script: string): Promise<RunResult> {\n try {\n return await runCommand('powershell.exe', { args: powerShellArgs(script) });\n } catch (err) {\n if (isCommandNotFound(err)) {\n throw new CredentialBackendUnsupportedError('win32', MISSING_HINT_FULL);\n }\n throw err;\n }\n}\n\nexport const WINDOWS_BACKEND: CredentialBackend = {\n name: 'windows-credential-manager',\n async get(account) {\n const target = targetName(account);\n const script = `\n${PS_HEADER}\n$target = '${escapeSingleQuotes(target)}';\n$ptr = [IntPtr]::Zero;\n$ok = [AitccCredApi]::CredRead($target, 1, 0, [ref]$ptr);\nif (-not $ok) { exit 0; }\n$cred = [Runtime.InteropServices.Marshal]::PtrToStructure($ptr, [Type][AitccCredApi+CREDENTIAL]);\n$blob = New-Object byte[] $cred.CredentialBlobSize;\n[Runtime.InteropServices.Marshal]::Copy($cred.CredentialBlob, $blob, 0, $cred.CredentialBlobSize);\n$pw = [System.Text.Encoding]::Unicode.GetString($blob);\n[AitccCredApi]::CredFree($ptr);\n[Console]::Out.Write($pw);\n`;\n const result = await runPowerShell(script);\n if (result.exitCode !== 0) {\n throw new CredentialBackendCommandError(\n 'powershell CredRead',\n result.exitCode,\n redactStderr(result.stderr),\n );\n }\n return result.stdout.length > 0 ? result.stdout : null;\n },\n async set(account, password) {\n const target = targetName(account);\n // Encode the password as hex so PowerShell's argv (visible in Task\n // Manager) shows hex, not cleartext. `account` is the email; it's\n // intentionally cleartext on argv since the email is not secret.\n const passwordHex = Buffer.from(password, 'utf8').toString('hex');\n const script = `\n${PS_HEADER}\n$target = '${escapeSingleQuotes(target)}';\n$user = '${escapeSingleQuotes(account)}';\n$pwHex = '${passwordHex}';\n$pwBytes = New-Object byte[] ($pwHex.Length / 2);\nfor ($i = 0; $i -lt $pwBytes.Length; $i++) {\n $pwBytes[$i] = [Convert]::ToByte($pwHex.Substring($i * 2, 2), 16);\n}\n$pwUtf16 = [System.Text.Encoding]::Unicode.GetBytes([System.Text.Encoding]::UTF8.GetString($pwBytes));\n$cred = New-Object AitccCredApi+CREDENTIAL;\n$cred.Type = 1;\n$cred.TargetName = [Runtime.InteropServices.Marshal]::StringToHGlobalUni($target);\n$cred.CredentialBlobSize = [uint32]$pwUtf16.Length;\n$cred.CredentialBlob = [Runtime.InteropServices.Marshal]::AllocHGlobal($pwUtf16.Length);\n[Runtime.InteropServices.Marshal]::Copy($pwUtf16, 0, $cred.CredentialBlob, $pwUtf16.Length);\n$cred.Persist = 2;\n$cred.UserName = [Runtime.InteropServices.Marshal]::StringToHGlobalUni($user);\ntry {\n $ok = [AitccCredApi]::CredWrite([ref]$cred, 0);\n if (-not $ok) { Write-Error 'CredWrite failed'; exit 1; }\n} finally {\n [Runtime.InteropServices.Marshal]::FreeHGlobal($cred.TargetName);\n [Runtime.InteropServices.Marshal]::FreeHGlobal($cred.UserName);\n [Runtime.InteropServices.Marshal]::FreeHGlobal($cred.CredentialBlob);\n}\n`;\n let result: RunResult;\n try {\n result = await runPowerShell(script);\n } catch (err) {\n if (err instanceof CredentialBackendUnsupportedError) {\n throw new CredentialBackendUnsupportedError('win32', MISSING_HINT_SHORT);\n }\n throw err;\n }\n if (result.exitCode !== 0) {\n throw new CredentialBackendCommandError(\n 'powershell CredWrite',\n result.exitCode,\n redactStderr(result.stderr),\n );\n }\n },\n async clear(account) {\n const target = targetName(account);\n const script = `\n${PS_HEADER}\n$target = '${escapeSingleQuotes(target)}';\n$ok = [AitccCredApi]::CredDelete($target, 1, 0);\nif ($ok) { [Console]::Out.Write('deleted'); } else { [Console]::Out.Write('absent'); }\n`;\n let result: RunResult;\n try {\n result = await runPowerShell(script);\n } catch (err) {\n if (err instanceof CredentialBackendUnsupportedError) {\n throw new CredentialBackendUnsupportedError('win32', MISSING_HINT_SHORT);\n }\n throw err;\n }\n if (result.exitCode !== 0) {\n throw new CredentialBackendCommandError(\n 'powershell CredDelete',\n result.exitCode,\n redactStderr(result.stderr),\n );\n }\n return { existed: result.stdout.includes('deleted') };\n },\n};\n","import { chmod, mkdir, readFile, unlink, writeFile } from 'node:fs/promises';\nimport { dirname } from 'node:path';\nimport { authStateFilePath } from '../paths.js';\nimport { type CredentialBackend, CredentialBackendUnsupportedError } from './backend.js';\nimport { LINUX_BACKEND } from './backends/linux.js';\nimport { MACOS_BACKEND } from './backends/macos.js';\nimport { WINDOWS_BACKEND } from './backends/windows.js';\n\n// Toss Business email + password persisted across two layers so a future\n// `aitcc login` can drive the sign-in form headlessly:\n// - the password lives in the OS keychain, keyed by `service=SERVICE,\n// account=<email>`. The keychain is the only place the secret ever\n// touches disk.\n// - the active email is mirrored to `auth-state.json` (0600) so we can\n// look up the keychain entry without the user re-typing the address\n// every time.\n//\n// `loadCredentials()` first checks env vars (`AITCC_EMAIL` +\n// `AITCC_PASSWORD`) so CI runs can inject single-shot credentials without\n// touching the keychain. The returned discriminated union tells callers\n// which source they got.\n//\n// SECURITY MODEL\n// - Single-user machine assumption. The native tools (`security`,\n// `secret-tool`, PowerShell + CredWrite) accept the password on argv on\n// macOS and Windows, briefly visible in `ps`/Task Manager to other\n// processes running as the same user. That's the OS tool's own limit;\n// we surface it in user-facing copy and don't pretend to defend\n// against an attacker already running as the user.\n// - Linux uses `secret-tool` which streams the password on stdin; argv\n// stays clean.\n// - This module never logs or prints passwords. Errors include backend\n// exit codes / stderr only — they must NOT include credential values.\n\nexport {\n CREDENTIAL_SERVICE,\n type CredentialBackend,\n CredentialBackendCommandError,\n CredentialBackendUnsupportedError,\n} from './backend.js';\n\nexport interface Credentials {\n readonly email: string;\n readonly password: string;\n}\n\nexport type CredentialsSource =\n | { readonly kind: 'env'; readonly email: string; readonly password: string }\n | { readonly kind: 'keychain'; readonly email: string; readonly password: string };\n// 'file' fallback (~/.config/aitcc/credentials.json) — TODO follow-up; not\n// implemented in PR α. Add a third variant when wired so callers can\n// distinguish at the type level.\n\n// --- Backend dispatch ---\n\nexport interface ResolveBackendOptions {\n readonly platform?: NodeJS.Platform;\n // Test seam — bypass platform detection.\n readonly override?: CredentialBackend;\n}\n\nexport function resolveBackend(opts: ResolveBackendOptions = {}): CredentialBackend {\n if (opts.override) return opts.override;\n const platform = opts.platform ?? process.platform;\n switch (platform) {\n case 'darwin':\n return MACOS_BACKEND;\n case 'linux':\n return LINUX_BACKEND;\n case 'win32':\n return WINDOWS_BACKEND;\n default:\n throw new CredentialBackendUnsupportedError(\n platform,\n 'Only macOS, Linux (libsecret), and Windows are supported.',\n );\n }\n}\n\n// --- Auth state (active email pointer) ---\n\ninterface AuthState {\n readonly schemaVersion: 1;\n readonly activeEmail: string;\n}\n\nasync function readAuthState(): Promise<AuthState | null> {\n let raw: string;\n try {\n raw = await readFile(authStateFilePath(), 'utf8');\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return null;\n throw err;\n }\n try {\n const parsed = JSON.parse(raw) as Partial<AuthState>;\n if (parsed.schemaVersion !== 1) return null;\n if (typeof parsed.activeEmail !== 'string' || parsed.activeEmail.length === 0) return null;\n return { schemaVersion: 1, activeEmail: parsed.activeEmail };\n } catch {\n return null;\n }\n}\n\nasync function writeAuthState(state: AuthState): Promise<void> {\n const path = authStateFilePath();\n await mkdir(dirname(path), { recursive: true, mode: 0o700 });\n await writeFile(path, JSON.stringify(state, null, 2), { mode: 0o600 });\n try {\n await chmod(path, 0o600);\n } catch {\n // Windows / exotic FS — best-effort.\n }\n}\n\nasync function clearAuthState(): Promise<{ existed: boolean }> {\n try {\n await unlink(authStateFilePath());\n return { existed: true };\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return { existed: false };\n throw err;\n }\n}\n\n// --- Public API ---\n\nexport interface LoadCredentialsOptions extends ResolveBackendOptions {\n readonly env?: NodeJS.ProcessEnv;\n}\n\n/**\n * Resolve credentials from the highest-priority source available:\n * 1. `AITCC_EMAIL` + `AITCC_PASSWORD` env vars (CI single-shot use).\n * 2. OS keychain entry whose email is recorded in `auth-state.json`.\n *\n * Returns `null` when no source is configured. The discriminated `kind`\n * lets callers (e.g. PR β's login flow) tell why a credential was found\n * without having to peek at process env themselves — useful for\n * \"auto-login from CI\" diagnostics.\n *\n * A future `'file'` source (~/.config/aitcc/credentials.json) is left as a\n * follow-up; once added, it slots between (1) and (2).\n */\nexport async function loadCredentials(\n opts: LoadCredentialsOptions = {},\n): Promise<CredentialsSource | null> {\n const env = opts.env ?? process.env;\n const envEmail = env.AITCC_EMAIL;\n const envPassword = env.AITCC_PASSWORD;\n if (envEmail && envPassword) {\n return { kind: 'env', email: envEmail, password: envPassword };\n }\n const state = await readAuthState();\n if (!state) return null;\n const password = await resolveBackend(opts).get(state.activeEmail);\n if (password === null) {\n // The pointer exists but the keychain entry is gone — partial state.\n // Treat as \"no credentials\" rather than fatal; callers can re-save.\n return null;\n }\n return { kind: 'keychain', email: state.activeEmail, password };\n}\n\nexport type SaveCredentialsStatus = 'created' | 'updated' | 'unchanged';\n\n/**\n * Persist credentials to the OS keychain and update the active-email\n * pointer. Returns `'unchanged'` (no keychain write) when the same email\n * + password is already stored — avoids triggering OS keychain prompts on\n * every call when the user re-runs `auth set` with the same input.\n */\nexport async function saveCredentials(\n email: string,\n password: string,\n opts: ResolveBackendOptions = {},\n): Promise<{ status: SaveCredentialsStatus }> {\n if (!email) throw new Error('email is required');\n if (!password) throw new Error('password is required');\n\n const backend = resolveBackend(opts);\n const previousState = await readAuthState();\n\n let status: SaveCredentialsStatus;\n if (previousState && previousState.activeEmail === email) {\n const existing = await backend.get(email);\n if (existing === password) {\n // Same email + same password already stored. No-op.\n return { status: 'unchanged' };\n }\n status = 'updated';\n } else {\n status = previousState ? 'updated' : 'created';\n }\n\n await backend.set(email, password);\n // If we are switching emails, the previous keychain entry would otherwise\n // dangle. Best-effort cleanup so the keychain reflects the active email.\n if (previousState && previousState.activeEmail !== email) {\n try {\n await backend.clear(previousState.activeEmail);\n } catch {\n // Old entry might already be gone or backend may flake — non-fatal.\n }\n }\n await writeAuthState({ schemaVersion: 1, activeEmail: email });\n return { status };\n}\n\n/**\n * Read just the active-email pointer without touching the OS keychain.\n * Useful for surfaces like `auth status --json` that want to report\n * whether credentials are configured without triggering a Touch ID /\n * libsecret prompt for the password.\n *\n * Returns the email and where it was found (`'env'` when\n * `AITCC_EMAIL` + `AITCC_PASSWORD` are present, `'keychain'` when the\n * `auth-state.json` pointer exists), or `null` when nothing is\n * configured.\n */\nexport async function getActiveCredentialEmail(\n opts: { readonly env?: NodeJS.ProcessEnv } = {},\n): Promise<{ kind: 'env' | 'keychain'; email: string } | null> {\n const env = opts.env ?? process.env;\n if (env.AITCC_EMAIL && env.AITCC_PASSWORD) {\n return { kind: 'env', email: env.AITCC_EMAIL };\n }\n const state = await readAuthState();\n if (!state) return null;\n return { kind: 'keychain', email: state.activeEmail };\n}\n\n/**\n * Remove the keychain entry and the auth-state pointer. Returns\n * `existed: true` if either side previously held data.\n */\nexport async function deleteCredentials(\n opts: ResolveBackendOptions = {},\n): Promise<{ existed: boolean }> {\n const state = await readAuthState();\n let backendExisted = false;\n if (state) {\n try {\n const result = await resolveBackend(opts).clear(state.activeEmail);\n backendExisted = result.existed;\n } catch (err) {\n if (err instanceof CredentialBackendUnsupportedError) {\n // No backend — auth-state alone is the only thing to clear.\n } else {\n throw err;\n }\n }\n }\n const stateResult = await clearAuthState();\n return { existed: backendExisted || stateResult.existed };\n}\n","// Single source of truth for the KR-only cookie warning shown on every\n// surface that emits or accepts an exported session blob.\n//\n// The console session cookies (`TBIZAUTH` and the rest) are country-bound\n// to KR residential IPs. The same blob succeeds from a Korean machine but\n// returns 401 / errorCode 4010 from non-KR egress (GHA-hosted runners,\n// most cloud providers' US/EU regions). Spike data + decision rationale:\n// docs/api/auth-session.md \"Cookie portability (실측)\".\n//\n// Keeping the wording in one place avoids drift between `auth export` /\n// `auth import` stderr, --json envelopes, --help blurbs, README, and docs.\n\nexport const KR_ONLY_WARNING_KEY = 'kr-only-cookies' as const;\n\nexport const KR_ONLY_WARNING_SHORT =\n 'console session cookies are KR-only — they fail with errorCode 4010 from non-KR IPs (e.g. GitHub-hosted runners).';\n\nexport const KR_ONLY_WARNING_LONG = `${KR_ONLY_WARNING_SHORT} See docs/api/auth-session.md.`;\n","import { defineCommand } from 'citty';\nimport {\n KR_ONLY_WARNING_KEY,\n KR_ONLY_WARNING_LONG,\n KR_ONLY_WARNING_SHORT,\n} from '../auth/kr-only-warning.js';\nimport { ExitCode } from '../exit.js';\nimport { exitAfterFlush } from '../flush.js';\nimport { readSession, type Session } from '../session.js';\nimport { emitJson } from './_shared.js';\n\n// `aitcc auth export` — dump the live session into a portable blob so it\n// can be moved to a KR-resident CI runner via secret manager. Symmetric\n// with `auth import`. Read path is `AITCC_SESSION` env var, handled in\n// `src/session.ts:readSessionFromEnv` (see CLAUDE.md \"Session storage\").\n//\n// --json contract (consumed by agent-plugin):\n//\n// { ok: true,\n// format: 'env'|'json',\n// payload: <string for env, object for json>,\n// warning: 'kr-only-cookies',\n// warningMessage: <string> } exit 0\n// { ok: true, authenticated: false } exit 10\n//\n// SECURITY: this is the ONE command that is allowed to emit cookie\n// material on stdout (it's the user's explicit intent). All other\n// surfaces redact. The shell-friendly `--format env` line is exactly\n// `AITCC_SESSION=<base64>\\n` so `eval $(...)` and `>> $GITHUB_ENV`\n// both work without quoting gymnastics.\n\nexport type AuthExportFormat = 'env' | 'json';\n\nexport interface AuthExportArgs {\n readonly json: boolean;\n readonly format: AuthExportFormat;\n readonly quiet: boolean;\n}\n\nexport interface AuthExportDeps {\n // Test seam: override session source. Production reads `readSession()`\n // which honours `AITCC_SESSION` precedence (re-exporting an env-loaded\n // session is an idempotent no-op by design).\n readonly readSession?: () => Promise<Session | null>;\n readonly stdoutIsTTY?: boolean;\n}\n\nexport async function runAuthExport(\n args: AuthExportArgs,\n deps: AuthExportDeps = {},\n): Promise<void> {\n const session = await (deps.readSession ?? readSession)();\n\n if (!session) {\n if (args.json) {\n emitJson({ ok: true, authenticated: false });\n } else {\n process.stderr.write('Not logged in. Run `aitcc login` to start a session.\\n');\n }\n return exitAfterFlush(ExitCode.NotAuthenticated);\n }\n\n const blobJson = JSON.stringify(session);\n\n // --json envelope is independent from --format. The agent-plugin always\n // shells out with --json; --format then picks how the inner `payload`\n // is shaped (one-line env string vs. raw object the user can stuff into\n // a secret manager).\n if (args.json) {\n const payload =\n args.format === 'env'\n ? `AITCC_SESSION=${Buffer.from(blobJson, 'utf8').toString('base64')}`\n : session;\n emitJson({\n ok: true,\n format: args.format,\n payload,\n warning: KR_ONLY_WARNING_KEY,\n warningMessage: KR_ONLY_WARNING_LONG,\n });\n return exitAfterFlush(ExitCode.Ok);\n }\n\n // Human + shell paths.\n const stdoutIsTTY = deps.stdoutIsTTY ?? Boolean(process.stdout.isTTY);\n\n if (args.format === 'env') {\n const base64 = Buffer.from(blobJson, 'utf8').toString('base64');\n // Exactly one line, single trailing newline — `eval` and `>> $GITHUB_ENV`\n // both depend on this. Resist the urge to add a header/comment.\n process.stdout.write(`AITCC_SESSION=${base64}\\n`);\n } else {\n // Raw shape pretty-printed so a human pasting into a secret manager\n // can sanity-check the email / capturedAt before storing.\n process.stdout.write(`${JSON.stringify(session, null, 2)}\\n`);\n }\n\n if (!args.quiet) {\n if (stdoutIsTTY) {\n process.stderr.write(\n 'Hint: stdout looks like a TTY — redirect to a file or pipe to your secret manager (e.g. `>> $GITHUB_ENV`).\\n',\n );\n }\n process.stderr.write(`warning: ${KR_ONLY_WARNING_SHORT}\\n`);\n process.stderr.write('See docs/api/auth-session.md for the full constraint.\\n');\n }\n\n return exitAfterFlush(ExitCode.Ok);\n}\n\nexport const authExportCommand = defineCommand({\n meta: {\n name: 'export',\n description:\n 'Dump the active session as a portable blob for CI. Note: KR-only — fails from non-KR IPs.',\n },\n args: {\n json: {\n type: 'boolean',\n description: 'Emit machine-readable JSON envelope to stdout.',\n default: false,\n },\n format: {\n type: 'string',\n description: 'Output shape: `env` (one-line AITCC_SESSION=...) or `json` (raw blob).',\n default: 'env',\n },\n quiet: {\n type: 'boolean',\n description: 'Silence the KR-only warning on stderr (the warning is non-fatal).',\n default: false,\n },\n },\n async run({ args }) {\n const format = parseFormat(args.format);\n if (format === null) {\n // Citty already coerces unknown strings; we still validate to give a\n // crisp `--json` error rather than emitting an env blob with a\n // garbage format label.\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'invalid-format',\n message: `unknown --format: ${String(args.format)} (expected 'env' or 'json')`,\n });\n } else {\n process.stderr.write(\n `Unknown --format: ${String(args.format)} (expected 'env' or 'json').\\n`,\n );\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n return runAuthExport({ json: args.json, format, quiet: args.quiet });\n },\n});\n\nfunction parseFormat(raw: unknown): AuthExportFormat | null {\n if (raw === 'env' || raw === 'json') return raw;\n return null;\n}\n","import { defineCommand } from 'citty';\nimport {\n KR_ONLY_WARNING_KEY,\n KR_ONLY_WARNING_LONG,\n KR_ONLY_WARNING_SHORT,\n} from '../auth/kr-only-warning.js';\nimport { ExitCode } from '../exit.js';\nimport { exitAfterFlush } from '../flush.js';\nimport {\n decodeSessionBlob,\n normalizeValidatedSession,\n readSession,\n type Session,\n sessionPathForDiagnostics,\n validateSessionBlob,\n type WriteSessionOptions,\n writeSession,\n} from '../session.js';\nimport { emitJson } from './_shared.js';\n\n// `aitcc auth import` — validate a portable session blob and write it\n// (mode 0600) to the local session file. Symmetric with `auth export`.\n//\n// Use cases (in order of frequency):\n// - Restore a session on a new desktop without redoing OAuth flow.\n// - Recover from a hand-edited session.json with `--dry-run` for shape\n// validation feedback before writing.\n// CI almost never needs this: in CI the recommended path is to set the\n// `AITCC_SESSION` env var directly (no file write, no 0600 negotiation\n// with shared runners).\n//\n// --json contract (consumed by agent-plugin):\n//\n// { ok: true, replaced: boolean, user: { id, email, displayName? },\n// dryRun?: true,\n// warning: 'kr-only-cookies', warningMessage: <string> } exit 0\n// { ok: false, reason: 'no-input', message } exit 2\n// { ok: false, reason: 'env-not-set', message } exit 2\n// { ok: false, reason: 'invalid-blob', detail } exit 2\n// { ok: false, reason: 'write-failed', message } exit 1\n\nexport interface AuthImportArgs {\n readonly json: boolean;\n readonly fromEnv: boolean;\n readonly dryRun: boolean;\n}\n\nexport interface AuthImportDeps {\n // Test seams.\n readonly readStdin?: () => Promise<string>;\n readonly env?: NodeJS.ProcessEnv;\n readonly stdinIsTTY?: boolean;\n readonly readExistingSession?: () => Promise<Session | null>;\n readonly writeSession?: (s: Session, opts?: WriteSessionOptions) => Promise<void>;\n}\n\nexport async function runAuthImport(\n args: AuthImportArgs,\n deps: AuthImportDeps = {},\n): Promise<void> {\n const env = deps.env ?? process.env;\n\n // Sourcing: explicit --from-env wins; otherwise stdin. We refuse to\n // prompt or block on a TTY-attached stdin — the import flow is meant\n // for piped use (`aitcc auth import < session.json`) or `--from-env`,\n // and a hung blocking read on a developer terminal would look broken.\n let raw: string;\n if (args.fromEnv) {\n const envValue = env.AITCC_SESSION;\n if (!envValue) {\n emitFailure(args.json, 'env-not-set', 'AITCC_SESSION is not set; export it first.');\n return exitAfterFlush(ExitCode.Usage);\n }\n raw = envValue;\n } else {\n const stdinIsTTY = deps.stdinIsTTY ?? Boolean(process.stdin.isTTY);\n if (stdinIsTTY) {\n emitFailure(\n args.json,\n 'no-input',\n 'No stdin pipe detected. Pipe a session JSON in or pass --from-env.',\n );\n return exitAfterFlush(ExitCode.Usage);\n }\n raw = await (deps.readStdin ?? readStdinToString)();\n if (raw.trim().length === 0) {\n emitFailure(args.json, 'no-input', 'stdin was empty.');\n return exitAfterFlush(ExitCode.Usage);\n }\n }\n\n // Auto-detect base64 vs raw JSON. `decodeSessionBlob` peeks at the\n // body for a leading `{` so users can paste either shape without a\n // flag. Symmetric with the env-precedence path in session.ts.\n const decoded = decodeSessionBlob(raw);\n if (decoded === null) {\n emitFailure(args.json, 'invalid-blob', 'could not parse blob as JSON or base64-encoded JSON');\n return exitAfterFlush(ExitCode.Usage);\n }\n const reason = validateSessionBlob(decoded);\n if (reason) {\n emitFailure(args.json, 'invalid-blob', reason);\n return exitAfterFlush(ExitCode.Usage);\n }\n\n const session = normalizeValidatedSession(decoded);\n\n // `replaced` is informational — no overwrite confirm. CI use of\n // `auth import` is rare (env path is preferred); local restore is the\n // primary case and the user is the one running the command.\n const existing = await (deps.readExistingSession ?? readSession)().catch(() => null);\n const replaced = existing !== null;\n\n if (args.dryRun) {\n if (args.json) {\n emitJson({\n ok: true,\n dryRun: true,\n replaced,\n user: session.user,\n warning: KR_ONLY_WARNING_KEY,\n warningMessage: KR_ONLY_WARNING_LONG,\n });\n } else {\n const cookies = session.cookies.length;\n process.stdout.write(\n `dry-run: blob is valid (${session.user.email}, ${cookies} cookies). Skipping write.\\n`,\n );\n process.stderr.write(`warning: ${KR_ONLY_WARNING_SHORT}\\n`);\n }\n return exitAfterFlush(ExitCode.Ok);\n }\n\n // Write path. We deliberately call `writeSession` even when reading\n // from --from-env; the user explicitly invoked import to persist, and\n // the env-write no-op in session.ts would otherwise swallow the write\n // silently. The `forceWrite` option opts into the write path without\n // touching `process.env` — the env no-op exists to protect commands\n // that *might* accidentally write (like `workspace use` on a CI host),\n // not the command whose entire job is to write.\n try {\n await (deps.writeSession ?? writeSession)(session, { forceWrite: true });\n } catch (err) {\n const message = (err as Error).message;\n if (args.json) {\n emitJson({ ok: false, reason: 'write-failed', message });\n } else {\n process.stderr.write(`Failed to write session file: ${message}\\n`);\n }\n return exitAfterFlush(ExitCode.Generic);\n }\n\n if (args.json) {\n emitJson({\n ok: true,\n replaced,\n user: session.user,\n warning: KR_ONLY_WARNING_KEY,\n warningMessage: KR_ONLY_WARNING_LONG,\n });\n } else {\n const path = sessionPathForDiagnostics();\n const cookies = session.cookies.length;\n const verb = replaced ? 'replaced' : 'wrote';\n process.stdout.write(\n `ok — ${verb} session at ${path} (${session.user.email}, ${cookies} cookies)\\n`,\n );\n process.stderr.write(`warning: ${KR_ONLY_WARNING_SHORT}\\n`);\n }\n return exitAfterFlush(ExitCode.Ok);\n}\n\nfunction emitFailure(json: boolean, reason: string, detail: string): void {\n if (json) {\n emitJson({\n ok: false,\n reason,\n ...(reason === 'invalid-blob' ? { detail } : { message: detail }),\n });\n } else {\n process.stderr.write(`${reason}: ${detail}\\n`);\n }\n}\n\nasync function readStdinToString(): Promise<string> {\n // process.stdin is a Readable stream; collect to one buffer.\n const chunks: Buffer[] = [];\n for await (const chunk of process.stdin) {\n chunks.push(typeof chunk === 'string' ? Buffer.from(chunk) : chunk);\n }\n return Buffer.concat(chunks).toString('utf8');\n}\n\nexport const authImportCommand = defineCommand({\n meta: {\n name: 'import',\n description:\n 'Validate a portable session blob and write it to the local session file. KR-only.',\n },\n args: {\n json: {\n type: 'boolean',\n description: 'Emit machine-readable JSON to stdout.',\n default: false,\n },\n 'from-env': {\n type: 'boolean',\n description: 'Read the blob from the AITCC_SESSION env var instead of stdin.',\n default: false,\n },\n 'dry-run': {\n type: 'boolean',\n description: 'Validate the blob without writing the session file.',\n default: false,\n },\n },\n async run({ args }) {\n return runAuthImport({\n json: args.json,\n fromEnv: Boolean(args['from-env']),\n dryRun: Boolean(args['dry-run']),\n });\n },\n});\n","import { confirm, input, password as passwordPrompt } from '@inquirer/prompts';\nimport { defineCommand } from 'citty';\nimport {\n type CredentialBackend,\n deleteCredentials,\n getActiveCredentialEmail,\n saveCredentials,\n} from '../auth/credentials.js';\nimport { ExitCode } from '../exit.js';\nimport { exitAfterFlush } from '../flush.js';\nimport { readSession } from '../session.js';\nimport { emitJson } from './_shared.js';\nimport { authExportCommand } from './auth-export.js';\nimport { authImportCommand } from './auth-import.js';\n\n// `aitcc auth` — user-facing surface over the credentials library\n// introduced in PR α. The library handles env precedence, OS keychain\n// dispatch, and idempotent writes; this file is the thin CLI shell that\n// turns those primitives into `set` / `clear` / `status` subcommands.\n//\n// --json contract (consumed by agent-plugin):\n//\n// auth set:\n// { ok: true, status: 'created'|'updated'|'unchanged', email } exit 0\n// { ok: false, reason: 'interactive-required'|'invalid-email'|... } exit 2/...\n//\n// auth clear:\n// { ok: true, status: 'deleted'|'absent'|'cancelled' } exit 0\n// { ok: false, reason: 'confirmation-required', ... } exit 2\n//\n// auth status:\n// { ok: true, credentials: { stored, email?, source? },\n// session: { active, user?, capturedAt? } } exit 0\n//\n// Passwords NEVER appear in any output (stdout/stderr/JSON). The auth\n// state pointer file (`auth-state.json`) only carries the email, and\n// the keychain backend never echoes the password to its own logs.\n\n// --- auth set ---\n\nexport interface AuthSetArgs {\n readonly json: boolean;\n readonly email?: string | undefined;\n readonly password?: string | undefined;\n}\n\nexport interface AuthDeps {\n readonly backend?: CredentialBackend;\n readonly env?: NodeJS.ProcessEnv;\n}\n\nexport async function runAuthSet(args: AuthSetArgs, deps: AuthDeps = {}): Promise<void> {\n const env = deps.env ?? process.env;\n\n // Resolve email + password from (in order): explicit flags, env vars,\n // interactive prompts. argv passwords are visible in `ps` so we warn;\n // env vars are the recommended scripted path.\n let email = args.email?.trim();\n let password = args.password;\n const argvPasswordUsed = password !== undefined;\n\n if (!email && env.AITCC_EMAIL) email = env.AITCC_EMAIL;\n if (password === undefined && env.AITCC_PASSWORD) password = env.AITCC_PASSWORD;\n\n // Argv password is a footgun. Surface it loud and only once, regardless\n // of whether we end up prompting for the email afterwards.\n if (argvPasswordUsed) {\n process.stderr.write(\n 'Warning: --password on argv is visible in `ps`/Task Manager. ' +\n 'Prefer the AITCC_PASSWORD environment variable for scripted use.\\n',\n );\n }\n\n const interactive = process.stdout.isTTY && process.stdin.isTTY && !args.json;\n\n if (!email) {\n if (!interactive) {\n emitInteractiveRequired(args.json, 'email');\n return exitAfterFlush(ExitCode.Usage);\n }\n try {\n email = (\n await input({\n message: 'Email:',\n validate: (raw) => (raw.trim().length > 0 ? true : 'email is required'),\n })\n ).trim();\n } catch (err) {\n if (isPromptCancelled(err)) {\n process.stderr.write('Aborted.\\n');\n return exitAfterFlush(ExitCode.Usage);\n }\n throw err;\n }\n }\n\n if (!email.includes('@')) {\n // Cheap sanity check — keychain backends accept any string but storing\n // a non-email pointer would silently desync from the login form.\n if (args.json) emitJson({ ok: false, reason: 'invalid-email', message: 'invalid email' });\n else process.stderr.write(`Invalid email: ${email}\\n`);\n return exitAfterFlush(ExitCode.Usage);\n }\n\n if (password === undefined) {\n if (!interactive) {\n emitInteractiveRequired(args.json, 'password');\n return exitAfterFlush(ExitCode.Usage);\n }\n try {\n password = await passwordPrompt({\n message: 'Password:',\n mask: true,\n validate: (raw) => (raw.length > 0 ? true : 'password is required'),\n });\n } catch (err) {\n if (isPromptCancelled(err)) {\n process.stderr.write('Aborted.\\n');\n return exitAfterFlush(ExitCode.Usage);\n }\n throw err;\n }\n }\n\n if (password.length === 0) {\n if (args.json)\n emitJson({ ok: false, reason: 'invalid-password', message: 'password is empty' });\n else process.stderr.write('Password is empty.\\n');\n return exitAfterFlush(ExitCode.Usage);\n }\n\n let result: { status: 'created' | 'updated' | 'unchanged' };\n try {\n result = await saveCredentials(email, password, deps.backend ? { override: deps.backend } : {});\n } catch (err) {\n const message = (err as Error).message;\n if (args.json) emitJson({ ok: false, reason: 'keychain-error', message });\n else process.stderr.write(`Failed to save credentials: ${message}\\n`);\n return exitAfterFlush(ExitCode.Generic);\n }\n\n if (args.json) {\n emitJson({ ok: true, status: result.status, email });\n } else if (result.status === 'unchanged') {\n process.stdout.write('Credentials already saved (no change).\\n');\n } else {\n process.stdout.write(`Credentials saved for ${email} (keychain).\\n`);\n }\n return exitAfterFlush(ExitCode.Ok);\n}\n\n// --- auth clear ---\n\nexport interface AuthClearArgs {\n readonly json: boolean;\n readonly yes: boolean;\n}\n\nexport async function runAuthClear(args: AuthClearArgs, deps: AuthDeps = {}): Promise<void> {\n const interactive = process.stdout.isTTY && process.stdin.isTTY && !args.json;\n\n // Look up the active email up front so we can show it in the confirm\n // prompt and the human success line. Failing here is non-fatal — the\n // clear path below will still try to wipe whatever it finds.\n const active = await getActiveCredentialEmail(deps.env ? { env: deps.env } : {}).catch(\n () => null,\n );\n\n if (!args.yes) {\n if (!interactive) {\n // Non-TTY (script, pipe, --json) without --yes is treated as a\n // mistake — silently wiping credentials from a piped invocation\n // would surprise the operator. Refuse and ask for the explicit\n // opt-in. The TTY branch below covers the human-confirm path.\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'confirmation-required',\n message: 'pass --yes to clear credentials in non-interactive mode',\n });\n } else {\n process.stderr.write(\n 'Refusing to clear credentials without confirmation. Pass --yes to proceed.\\n',\n );\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n const label = active?.email ?? '(unknown)';\n let confirmed: boolean;\n try {\n confirmed = await confirm({\n message: `Delete saved credentials for ${label}?`,\n default: false,\n });\n } catch (err) {\n if (isPromptCancelled(err)) {\n process.stderr.write('Aborted.\\n');\n return exitAfterFlush(ExitCode.Usage);\n }\n throw err;\n }\n if (!confirmed) {\n // Distinct status from `'absent'`: the user actively said no even\n // though credentials may exist, vs. nothing was there to begin with.\n if (args.json) emitJson({ ok: true, status: 'cancelled' });\n else process.stdout.write('Aborted.\\n');\n return exitAfterFlush(ExitCode.Ok);\n }\n }\n\n let result: { existed: boolean };\n try {\n result = await deleteCredentials(deps.backend ? { override: deps.backend } : {});\n } catch (err) {\n const message = (err as Error).message;\n if (args.json) emitJson({ ok: false, reason: 'keychain-error', message });\n else process.stderr.write(`Failed to clear credentials: ${message}\\n`);\n return exitAfterFlush(ExitCode.Generic);\n }\n\n const status = result.existed ? 'deleted' : 'absent';\n if (args.json) {\n emitJson({ ok: true, status });\n } else if (result.existed) {\n process.stdout.write('Credentials cleared.\\n');\n } else {\n process.stdout.write('No saved credentials.\\n');\n }\n return exitAfterFlush(ExitCode.Ok);\n}\n\n// --- auth status ---\n\nexport interface AuthStatusArgs {\n readonly json: boolean;\n}\n\nexport async function runAuthStatus(args: AuthStatusArgs, deps: AuthDeps = {}): Promise<void> {\n // Read the email pointer without touching the keychain — `auth status`\n // shouldn't trigger a Touch ID / libsecret prompt just to answer \"do I\n // have credentials configured?\". Password retrieval lives in the login\n // path, which already needs it.\n const active = await getActiveCredentialEmail(deps.env ? { env: deps.env } : {}).catch(\n () => null,\n );\n const session = await readSession();\n\n if (args.json) {\n const credentials = active\n ? { stored: true as const, email: active.email, source: active.kind }\n : { stored: false as const };\n const sessionShape = session\n ? {\n active: true as const,\n user: session.user,\n capturedAt: session.capturedAt,\n }\n : { active: false as const };\n emitJson({ ok: true, credentials, session: sessionShape });\n return exitAfterFlush(ExitCode.Ok);\n }\n\n if (active) {\n const sourceLabel = active.kind === 'env' ? 'environment (AITCC_EMAIL/PASSWORD)' : 'keychain';\n process.stdout.write(`Email: ${active.email}\\n`);\n process.stdout.write(`Source: ${sourceLabel}\\n`);\n } else {\n process.stdout.write('Email: (not configured)\\n');\n }\n if (session) {\n const label = session.user.displayName\n ? `${session.user.displayName} <${session.user.email}>`\n : session.user.email;\n process.stdout.write(`Session: active — ${label}\\n`);\n process.stdout.write(`Captured: ${session.capturedAt}\\n`);\n } else {\n process.stdout.write('Session: none (run `aitcc login`)\\n');\n }\n return exitAfterFlush(ExitCode.Ok);\n}\n\n// --- helpers ---\n\nfunction emitInteractiveRequired(json: boolean, missing: 'email' | 'password'): void {\n if (json) {\n emitJson({\n ok: false,\n reason: 'interactive-required',\n message: `${missing} prompt requires a TTY; use --${missing} or AITCC_${missing.toUpperCase()}`,\n });\n } else {\n process.stderr.write(\n `Cannot prompt for ${missing} in non-interactive mode. ` +\n `Use --${missing} or set AITCC_${missing.toUpperCase()}.\\n`,\n );\n }\n}\n\n// Mirrors the helper in app-init.ts — `@inquirer/prompts` throws an\n// `ExitPromptError` (name only, the class isn't exported from the\n// top-level package) when the user hits Ctrl-C. We don't want to surface\n// that as a stack trace.\nfunction isPromptCancelled(err: unknown): boolean {\n return err instanceof Error && err.name === 'ExitPromptError';\n}\n\n// --- citty wrappers ---\n\nconst setCommand = defineCommand({\n meta: {\n name: 'set',\n description: 'Save email + password to the OS keychain for future headless logins.',\n },\n args: {\n json: { type: 'boolean', description: 'Emit machine-readable JSON to stdout.', default: false },\n email: { type: 'string', description: 'Email (skip prompt).' },\n password: {\n type: 'string',\n description: 'Password (skip prompt; visible in `ps` — prefer AITCC_PASSWORD env var).',\n },\n },\n async run({ args }) {\n return runAuthSet({\n json: args.json,\n email: typeof args.email === 'string' ? args.email : undefined,\n password: typeof args.password === 'string' ? args.password : undefined,\n });\n },\n});\n\nconst clearCommand = defineCommand({\n meta: {\n name: 'clear',\n description: 'Delete the saved credentials and the auth-state pointer.',\n },\n args: {\n json: { type: 'boolean', description: 'Emit machine-readable JSON to stdout.', default: false },\n yes: {\n type: 'boolean',\n alias: 'y',\n description: 'Skip the confirmation prompt.',\n default: false,\n },\n },\n async run({ args }) {\n return runAuthClear({ json: args.json, yes: args.yes });\n },\n});\n\nconst statusCommand = defineCommand({\n meta: {\n name: 'status',\n description: 'Report whether credentials and a session are configured.',\n },\n args: {\n json: { type: 'boolean', description: 'Emit machine-readable JSON to stdout.', default: false },\n },\n async run({ args }) {\n return runAuthStatus({ json: args.json });\n },\n});\n\nexport const authCommand = defineCommand({\n meta: {\n name: 'auth',\n description: 'Manage saved login credentials and export/import portable session blobs for CI.',\n },\n subCommands: {\n set: setCommand,\n clear: clearCommand,\n status: statusCommand,\n export: authExportCommand,\n import: authImportCommand,\n },\n});\n","import { defineCommand } from 'citty';\nimport { ExitCode } from '../exit.js';\nimport { exitAfterFlush } from '../flush.js';\nimport { emitJson } from './_shared.js';\n\n// Shell completion scripts for aitcc.\n//\n// Philosophy: keep it static + shallow. citty does not ship its own\n// completion generator, and introspecting a live command tree at runtime\n// would duplicate the source of truth. Instead we hard-code the one-level\n// subcommand list that matters for tab-completion; the shell scripts only\n// need to know top-level commands and the common flags. Deeper sub-sub\n// completion (e.g. `app bundles ls`) stays uncompleted — users type\n// `bundles <TAB>` and the shell falls back to filenames, which is fine.\n//\n// Install flow is one-shot per shell:\n// bash → aitcc completion bash > /etc/bash_completion.d/aitcc\n// or `source <(aitcc completion bash)` in .bashrc\n// zsh → aitcc completion zsh > \"${fpath[1]}/_aitcc\" && autoload -U _aitcc\n// (or drop it in ~/.zsh/completions/_aitcc and rebuild fpath)\n// fish → aitcc completion fish > ~/.config/fish/completions/aitcc.fish\n//\n// install.sh prints the right one-liner after install based on $SHELL;\n// we don't modify user rc files automatically.\n\n// Top-level commands.\nconst TOP_LEVEL: readonly string[] = [\n 'whoami',\n 'login',\n 'logout',\n 'auth',\n 'upgrade',\n 'workspace',\n 'app',\n 'members',\n 'keys',\n 'notices',\n 'me',\n 'completion',\n];\n\n// Subcommands per namespace. Order is for display (alpha).\nconst SUB_COMMANDS: Record<string, readonly string[]> = {\n workspace: ['ls', 'partner', 'segments', 'show', 'terms', 'use'],\n app: [\n 'bundles',\n 'categories',\n 'certs',\n 'events',\n 'init',\n 'ls',\n 'messages',\n 'metrics',\n 'ratings',\n 'register',\n 'reports',\n 'service-status',\n 'share-rewards',\n 'show',\n 'status',\n 'templates',\n ],\n // 3rd-level completion (`app bundles <TAB>`) is out of scope — shells\n // fall back to filename completion there. But we list the bundles\n // subcommands here as a comment for reviewers: ls, deployed, upload,\n // review, release, test-push, test-links.\n notices: ['categories', 'ls', 'show'],\n me: ['terms'],\n auth: ['set', 'clear', 'status'],\n completion: ['bash', 'zsh', 'fish'],\n};\n\nfunction emitBash(): string {\n const top = TOP_LEVEL.join(' ');\n // Per-namespace case branch. We keep this small — only the immediate\n // subcommand layer is offered; the shell then falls back to filename\n // completion for the 3rd word onward.\n const cases: string[] = [];\n for (const [ns, subs] of Object.entries(SUB_COMMANDS)) {\n cases.push(` ${ns})`);\n cases.push(` COMPREPLY=( $(compgen -W \"${subs.join(' ')}\" -- \"$cur\") )`);\n cases.push(' return 0 ;;');\n }\n return `# bash completion for aitcc. Generated by \\`aitcc completion bash\\`.\n# Install:\n# aitcc completion bash > /etc/bash_completion.d/aitcc\n# # or add to ~/.bashrc:\n# source <(aitcc completion bash)\n\n_aitcc_completion() {\n local cur prev\n COMPREPLY=()\n cur=\"\\${COMP_WORDS[COMP_CWORD]}\"\n prev=\"\\${COMP_WORDS[COMP_CWORD-1]}\"\n\n if [[ $COMP_CWORD -eq 1 ]]; then\n COMPREPLY=( $(compgen -W \"${top}\" -- \"$cur\") )\n return 0\n fi\n\n case \"\\${COMP_WORDS[1]}\" in\n${cases.join('\\n')}\n esac\n\n return 0\n}\n\ncomplete -F _aitcc_completion aitcc\n`;\n}\n\nfunction emitZsh(): string {\n // Use zsh's _arguments with a nested state machine. Keep the definition\n // readable; zsh users often inspect their completion files.\n const top = TOP_LEVEL.join(' ');\n const nsClauses: string[] = [];\n for (const [ns, subs] of Object.entries(SUB_COMMANDS)) {\n nsClauses.push(` ${ns}) _values 'subcommand' ${subs.map((s) => `'${s}'`).join(' ')} ;;`);\n }\n return `#compdef aitcc\n# zsh completion for aitcc. Generated by \\`aitcc completion zsh\\`.\n# Install:\n# aitcc completion zsh > \"\\${fpath[1]}/_aitcc\"\n# # then in a fresh shell (or run \\`autoload -U compinit && compinit\\`)\n\n_aitcc() {\n local -a commands\n commands=(${TOP_LEVEL.map((c) => `'${c}'`).join(' ')})\n\n if (( CURRENT == 2 )); then\n _values 'command' ${TOP_LEVEL.map((c) => `'${c}'`).join(' ')}\n return\n fi\n\n case \"\\${words[2]}\" in\n${nsClauses.join('\\n')}\n esac\n}\n\n_aitcc \"$@\"\n# ${top}\n`;\n}\n\nfunction emitFish(): string {\n const lines: string[] = [\n '# fish completion for aitcc. Generated by `aitcc completion fish`.',\n '# Install:',\n '# aitcc completion fish > ~/.config/fish/completions/aitcc.fish',\n '',\n ];\n // Top-level commands (only when no subcommand given yet).\n for (const c of TOP_LEVEL) {\n lines.push(`complete -c aitcc -n \"__fish_use_subcommand\" -a \"${c}\" -f`);\n }\n for (const [ns, subs] of Object.entries(SUB_COMMANDS)) {\n for (const s of subs) {\n lines.push(`complete -c aitcc -n \"__fish_seen_subcommand_from ${ns}\" -a \"${s}\" -f`);\n }\n }\n return `${lines.join('\\n')}\\n`;\n}\n\nexport const completionCommand = defineCommand({\n meta: {\n name: 'completion',\n description: 'Emit a shell completion script for bash, zsh, or fish.',\n },\n args: {\n shell: {\n type: 'positional',\n description: 'Target shell: bash, zsh, or fish.',\n required: false,\n },\n json: {\n type: 'boolean',\n description: \"Emit { ok: false, reason: 'invalid-shell' } on bad input.\",\n default: false,\n },\n },\n async run({ args }) {\n const raw = args.shell === undefined ? '' : String(args.shell).toLowerCase();\n if (raw === 'bash') {\n process.stdout.write(emitBash());\n return exitAfterFlush(ExitCode.Ok);\n }\n if (raw === 'zsh') {\n process.stdout.write(emitZsh());\n return exitAfterFlush(ExitCode.Ok);\n }\n if (raw === 'fish') {\n process.stdout.write(emitFish());\n return exitAfterFlush(ExitCode.Ok);\n }\n // No shell given (or unknown) — print usage to stderr. We expose a\n // --json failure mode so agent-plugin can probe capability without\n // parsing human text.\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'invalid-shell',\n allowed: ['bash', 'zsh', 'fish'],\n message: `completion shell must be one of: bash, zsh, fish (got ${JSON.stringify(raw)})`,\n });\n } else {\n process.stderr.write(\n 'Usage: aitcc completion <bash|zsh|fish>\\n' +\n '\\n' +\n 'Examples:\\n' +\n ' aitcc completion bash > /etc/bash_completion.d/aitcc\\n' +\n // biome-ignore lint/suspicious/noTemplateCurlyInString: literal zsh syntax, not a JS template placeholder\n ' aitcc completion zsh > \"${fpath[1]}/_aitcc\"\\n' +\n ' aitcc completion fish > ~/.config/fish/completions/aitcc.fish\\n',\n );\n }\n return exitAfterFlush(ExitCode.Usage);\n },\n});\n","import type { CdpCookie } from '../cdp.js';\nimport { type FetchLike, requestConsoleApi } from './http.js';\n\n// Console API keys: workspace-scoped credentials used for deploy automation\n// (see docs/api/api-keys.md). Three endpoints, all confirmed against the\n// console UI bundle (`static/index.ZsA5htf8.js`):\n//\n// GET /workspaces/:wid/api-keys → list (`{id, name, expireTs}`)\n// POST /workspaces/:wid/api-keys → issue (returns `{apiKey, ...}`)\n// PUT /workspaces/:wid/api-keys/:keyId/disable → revoke\n//\n// The plaintext key is surfaced **once** in the POST response under the\n// `apiKey` field. The list response intentionally omits it (the UI only\n// shows the user-supplied `name` plus an expiry countdown), so a key that\n// wasn't captured at creation cannot be recovered — same pattern as GitHub\n// PATs and `gh`'s `auth token`.\n//\n// `name` is the user-supplied label (≤16 chars, no whitespace/Korean per UI\n// validation). `expireTs` is epoch ms.\n\nconst BASE = 'https://apps-in-toss.toss.im/console/api-public/v3/appsintossconsole';\n\nexport interface ApiKeySummary {\n readonly id: string | number;\n readonly name: string | undefined;\n readonly expireTs: number | undefined;\n readonly extra: Readonly<Record<string, unknown>>;\n}\n\nexport async function fetchApiKeys(\n workspaceId: number,\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<ApiKeySummary[]> {\n const url = `${BASE}/workspaces/${workspaceId}/api-keys`;\n const raw = await requestConsoleApi<unknown>({\n url,\n cookies,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n if (!Array.isArray(raw)) {\n throw new Error(`Unexpected api-keys shape for workspace=${workspaceId}: not an array`);\n }\n return raw.map((entry, index) => normalizeKey(entry, workspaceId, index));\n}\n\nfunction normalizeKey(raw: unknown, workspaceId: number, index: number): ApiKeySummary {\n if (raw === null || typeof raw !== 'object') {\n throw new Error(\n `Unexpected api-key entry at index ${index} for workspace=${workspaceId}: not an object`,\n );\n }\n const rec = raw as Record<string, unknown>;\n // Field names confirmed from the management page chunk\n // (`static/index.ZsA5htf8.js`); fallbacks kept for `id`/`name` because\n // the bundle only proves they exist on the read path, not what the\n // server emits if a future migration renames them.\n const rawId = rec.id ?? rec.apiKeyId ?? rec.keyId;\n if (typeof rawId !== 'string' && typeof rawId !== 'number') {\n throw new Error(\n `Unexpected api-key entry at index ${index} for workspace=${workspaceId}: missing id`,\n );\n }\n const rawName = rec.name ?? rec.apiKeyName ?? rec.keyName ?? rec.description;\n const name = typeof rawName === 'string' ? rawName : undefined;\n const expireTs = typeof rec.expireTs === 'number' ? rec.expireTs : undefined;\n const {\n id: _id,\n apiKeyId: _aid,\n keyId: _kid,\n name: _n,\n apiKeyName: _an,\n keyName: _kn,\n description: _d,\n expireTs: _e,\n ...extra\n } = rec;\n return { id: rawId, name, expireTs, extra };\n}\n\n// `target` mirrors the console UI dialog: `isAll: true` issues a key valid\n// for every mini-app in the workspace; `isAll: false` scopes it to a list\n// of `appName` slugs (the kebab-case slug, not numeric `miniAppId`).\n//\n// The upstream component (`he` in `static/index.ZsA5htf8.js`) sends `[]` in\n// the all-apps case rather than omitting the field; we replicate that to\n// avoid relying on the server treating an absent key as \"all apps\".\nexport interface CreateApiKeyTarget {\n readonly isAll: boolean;\n readonly appNames: readonly string[];\n}\n\nexport interface CreateApiKeyResult {\n /** Plaintext key, surfaced **only** in the create response. */\n readonly apiKey: string;\n /** Any fields the server returned beyond `apiKey` (`id`, `expireTs`, ...). */\n readonly extra: Readonly<Record<string, unknown>>;\n}\n\nexport async function createApiKey(\n workspaceId: number,\n body: { name: string; target: CreateApiKeyTarget },\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<CreateApiKeyResult> {\n const url = `${BASE}/workspaces/${workspaceId}/api-keys`;\n const raw = await requestConsoleApi<unknown>({\n method: 'POST',\n url,\n cookies,\n body: { workspaceId, name: body.name, target: body.target },\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n if (raw === null || typeof raw !== 'object') {\n throw new Error(`Unexpected api-keys create response for workspace=${workspaceId}`);\n }\n const rec = raw as Record<string, unknown>;\n const apiKey = rec.apiKey;\n if (typeof apiKey !== 'string' || apiKey.length === 0) {\n throw new Error(\n `Unexpected api-keys create response for workspace=${workspaceId}: missing plaintext key`,\n );\n }\n const { apiKey: _k, ...extra } = rec;\n return { apiKey, extra };\n}\n\nexport async function disableApiKey(\n workspaceId: number,\n apiKeyId: string | number,\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<void> {\n const url = `${BASE}/workspaces/${workspaceId}/api-keys/${apiKeyId}/disable`;\n await requestConsoleApi<unknown>({\n method: 'PUT',\n url,\n cookies,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n}\n","import { defineCommand } from 'citty';\nimport {\n type CreateApiKeyTarget,\n createApiKey,\n disableApiKey,\n fetchApiKeys,\n} from '../api/api-keys.js';\nimport { APP_NAME_REGEX } from '../config/app-manifest.js';\nimport { ExitCode } from '../exit.js';\nimport { exitAfterFlush } from '../flush.js';\nimport {\n emitFailureFromError,\n emitJson,\n printContextHeader,\n resolveWorkspaceContext,\n} from './_shared.js';\n\n// --json contract (consumed by agent-plugin):\n//\n// keys ls [--workspace <id>]:\n// { ok: true, workspaceId, keys: [{id, name, expireTs, extra}], needsKey? } exit 0\n// { ok: false, reason: 'no-workspace-selected' } exit 2\n// { ok: false, reason: 'invalid-id', message } exit 2\n//\n// `needsKey: true` is emitted when the key list is empty. The flag is\n// there so `/ait deploy` (and similar agent-plugin skills) can bail\n// with a friendly \"issue a key first\" message instead of attempting a\n// deploy that will 401 server-side. We keep the UI-specific Korean\n// wording out of JSON (it lives on stderr plain output only).\n//\n// keys create --name <label> [--apps <slug,slug>] [--workspace <id>]:\n// { ok: true, workspaceId, apiKey, name, target: {isAll, appNames}, extra } exit 0\n// { ok: false, reason: 'invalid-name', message } exit 2\n// { ok: false, reason: 'invalid-apps', message } exit 2\n// { ok: false, reason: 'no-workspace-selected' } exit 2\n// { ok: false, reason: 'invalid-id', message } exit 2\n//\n// The `apiKey` field carries the plaintext token and is surfaced **only\n// here** — the list endpoint does not echo it back. Agent-plugin skills\n// should pipe it straight into a secret manager and never log the raw\n// value. The CLI itself prints it to stdout once and never persists it.\n//\n// keys revoke <id> [--workspace <id>]:\n// { ok: true, workspaceId, apiKeyId } exit 0\n// { ok: false, reason: 'invalid-id', message } exit 2\n// { ok: false, reason: 'no-workspace-selected' } exit 2\n//\n// Auth/network/api failures follow the shared contract (exit 10/11/17).\n//\n// \"Console API key\" in upstream terminology — used to authenticate\n// automated deploys. Endpoints + payload shapes confirmed from the console\n// management-page chunk; full capture in docs/api/api-keys.md.\n\n// `name` validation mirrors the UI dialog (`he` in static/index.ZsA5htf8.js):\n// - max 16 codepoints (UI shows length/16 counter, disables submit > 16)\n// - \"공백, 한글, 특수문자 제외\" placeholder = ASCII letters/digits/-/_ only\n// We mirror the UI rule rather than rely on the server because the server\n// returns a generic FAIL on rejection and a local check gives a better hint.\nexport const NAME_MAX = 16;\nconst NAME_REGEX = /^[A-Za-z0-9_-]+$/;\n\n// `appName` slugs are kebab-case ASCII per the mini-app registration regex.\n// Reuse the canonical regex so a future tightening (length cap, allowed\n// chars) flows here automatically instead of drifting.\n\nexport type NameValidationError = 'too-short' | 'too-long' | 'bad-chars';\nexport function validateKeyName(raw: string): NameValidationError | null {\n if (raw.length === 0) return 'too-short';\n if (raw.length > NAME_MAX) return 'too-long';\n if (!NAME_REGEX.test(raw)) return 'bad-chars';\n return null;\n}\n\nexport type AppsParseResult =\n | { ok: true; slugs: string[] }\n | { ok: false; reason: 'empty' | 'invalid'; bad?: string[] };\n\nexport function parseAppsFlag(raw: string): AppsParseResult {\n const slugs = raw\n .split(',')\n .map((s) => s.trim())\n .filter((s) => s.length > 0);\n if (slugs.length === 0) return { ok: false, reason: 'empty' };\n const bad = slugs.filter((s) => !APP_NAME_REGEX.test(s));\n if (bad.length > 0) return { ok: false, reason: 'invalid', bad };\n return { ok: true, slugs };\n}\n\nconst lsCommand = defineCommand({\n meta: {\n name: 'ls',\n description: 'List console API keys in the selected workspace.',\n },\n args: {\n workspace: {\n type: 'string',\n description: 'Workspace ID. Defaults to the selected workspace (`aitcc workspace use`).',\n },\n json: { type: 'boolean', description: 'Emit machine-readable JSON to stdout.', default: false },\n },\n async run({ args }) {\n const ctx = await resolveWorkspaceContext(args);\n if (!ctx) return;\n const { session, workspaceId } = ctx;\n printContextHeader(ctx, { json: args.json });\n\n try {\n const keys = await fetchApiKeys(workspaceId, session.cookies);\n if (args.json) {\n emitJson({\n ok: true,\n workspaceId,\n keys: keys.map((k) => ({\n id: k.id,\n name: k.name ?? null,\n expireTs: k.expireTs ?? null,\n extra: k.extra,\n })),\n ...(keys.length === 0 ? { needsKey: true } : {}),\n });\n return exitAfterFlush(ExitCode.Ok);\n }\n if (keys.length === 0) {\n process.stdout.write(`No API keys in workspace ${workspaceId}.\\n`);\n process.stderr.write(\n 'Hint: `aitcc keys create --name <label>` to issue one (deploy automation requires a key).\\n',\n );\n return exitAfterFlush(ExitCode.Ok);\n }\n process.stdout.write(`${keys.length} API key(s) in workspace ${workspaceId}:\\n`);\n const now = Date.now();\n for (const k of keys) {\n const name = k.name ?? '(unnamed)';\n const expiry = formatExpiry(k.expireTs, now);\n process.stdout.write(`${k.id}\\t${name}\\t${expiry}\\n`);\n }\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\nconst createCommand = defineCommand({\n meta: {\n name: 'create',\n description: 'Issue a new console API key. Plaintext is shown once.',\n },\n args: {\n name: {\n type: 'string',\n description:\n 'Label for the key (≤16 ASCII chars: letters/digits/-/_). Required, mirrors the UI dialog.',\n required: true,\n },\n apps: {\n type: 'string',\n description:\n 'Comma-separated mini-app `appName` slugs to scope the key to. Omit for an all-apps key (default).',\n },\n workspace: {\n type: 'string',\n description: 'Workspace ID. Defaults to the selected workspace.',\n },\n json: { type: 'boolean', description: 'Emit machine-readable JSON to stdout.', default: false },\n },\n async run({ args }) {\n const ctx = await resolveWorkspaceContext(args);\n if (!ctx) return;\n const { session, workspaceId } = ctx;\n printContextHeader(ctx, { json: args.json });\n\n const name = String(args.name);\n const nameErr = validateKeyName(name);\n if (nameErr !== null) {\n const message =\n nameErr === 'too-short'\n ? '--name is required (1..16 chars)'\n : nameErr === 'too-long'\n ? `--name must be ≤${NAME_MAX} chars (got ${name.length})`\n : '--name may contain only ASCII letters, digits, hyphen, and underscore (no spaces, Korean, or special chars)';\n if (args.json) emitJson({ ok: false, reason: 'invalid-name', message });\n else process.stderr.write(`${message}\\n`);\n return exitAfterFlush(ExitCode.Usage);\n }\n\n let target: CreateApiKeyTarget;\n if (args.apps) {\n const parsed = parseAppsFlag(String(args.apps));\n if (!parsed.ok) {\n const message =\n parsed.reason === 'empty'\n ? '--apps was empty (drop the flag for an all-apps key)'\n : `--apps contains invalid slug(s): ${(parsed.bad ?? []).join(', ')} (expected kebab-case: [a-z][a-z0-9-]*)`;\n if (args.json) emitJson({ ok: false, reason: 'invalid-apps', message });\n else process.stderr.write(`${message}\\n`);\n return exitAfterFlush(ExitCode.Usage);\n }\n target = { isAll: false, appNames: parsed.slugs };\n } else {\n target = { isAll: true, appNames: [] };\n }\n\n try {\n const result = await createApiKey(workspaceId, { name, target }, session.cookies);\n if (args.json) {\n emitJson({\n ok: true,\n workspaceId,\n apiKey: result.apiKey,\n name,\n target: { isAll: target.isAll, appNames: [...target.appNames] },\n extra: result.extra,\n });\n return exitAfterFlush(ExitCode.Ok);\n }\n // Plaintext is shown exactly once. The console UI surfaces the same\n // \"이 키는 한 번만 표시되니 복사해서 안전하게 보관해주세요.\" warning;\n // we mirror it on stderr so stdout stays a clean single line that's\n // friendly to `aitcc keys create ... | secret-tool store ...` pipes.\n process.stdout.write(`${result.apiKey}\\n`);\n process.stderr.write(\n '⚠️ This key is shown only once. Save it to a secret manager now — it cannot be retrieved later.\\n',\n );\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\nconst revokeCommand = defineCommand({\n meta: {\n name: 'revoke',\n description: 'Disable a console API key by id.',\n },\n args: {\n id: {\n type: 'positional',\n required: true,\n description: 'API key id (from `aitcc keys ls`).',\n },\n workspace: {\n type: 'string',\n description: 'Workspace ID. Defaults to the selected workspace.',\n },\n json: { type: 'boolean', description: 'Emit machine-readable JSON to stdout.', default: false },\n },\n async run({ args }) {\n const ctx = await resolveWorkspaceContext(args);\n if (!ctx) return;\n const { session, workspaceId } = ctx;\n printContextHeader(ctx, { json: args.json });\n\n // citty enforces `required: true` on the positional, so `args.id` is\n // always present when `run` is called.\n const rawId = String(args.id);\n\n try {\n await disableApiKey(workspaceId, rawId, session.cookies);\n if (args.json) {\n emitJson({ ok: true, workspaceId, apiKeyId: rawId });\n return exitAfterFlush(ExitCode.Ok);\n }\n process.stdout.write(`Revoked API key ${rawId} in workspace ${workspaceId}.\\n`);\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\nexport function formatExpiry(expireTs: number | undefined, now: number): string {\n if (expireTs === undefined) return '';\n const diffMs = expireTs - now;\n const days = Math.floor(diffMs / 86_400_000);\n if (diffMs < 0) return 'expired';\n return `D-${days}`;\n}\n\nexport const keysCommand = defineCommand({\n meta: {\n name: 'keys',\n description: 'Manage console API keys used for deploy automation.',\n },\n subCommands: {\n ls: lsCommand,\n create: createCommand,\n revoke: revokeCommand,\n },\n});\n","// Tiny Chrome DevTools Protocol client. Enough to navigate a tab, listen for\n// frame-navigation events, and dump cookies via `Network.getAllCookies`.\n//\n// Deliberately does NOT pull in `ws` or any WebSocket userland lib: Node 22+\n// and Bun both expose `globalThis.WebSocket` with the standard interface,\n// which is what we use. Keeps `bun build --compile` tiny and avoids the\n// optional-native-deps dance.\n//\n// Threading model: one `CdpClient` wraps one WebSocket connection to the\n// *browser* (the URL printed on Chrome's stderr). Per-target sessions are\n// attached lazily via `Target.attachToTarget` — we shuttle messages over\n// the single connection using `sessionId` routing, the same way the DevTools\n// frontend and `chrome-remote-interface` do. Only the APIs we actually need\n// for login capture are wrapped; everything else is available through the\n// raw `send(method, params, sessionId?)` escape hatch.\n\ntype JsonValue =\n | null\n | string\n | number\n | boolean\n | readonly JsonValue[]\n | { readonly [k: string]: JsonValue };\n\ninterface CdpSuccess {\n readonly id: number;\n readonly result: Record<string, unknown>;\n readonly sessionId?: string;\n}\n\ninterface CdpError {\n readonly id: number;\n readonly error: { readonly code: number; readonly message: string; readonly data?: unknown };\n readonly sessionId?: string;\n}\n\ninterface CdpEvent {\n readonly method: string;\n readonly params: Record<string, unknown>;\n readonly sessionId?: string;\n}\n\ntype CdpMessage = CdpSuccess | CdpError | CdpEvent;\n\nfunction isResponse(m: CdpMessage): m is CdpSuccess | CdpError {\n return 'id' in m;\n}\n\nexport class CdpProtocolError extends Error {\n constructor(\n readonly method: string,\n readonly code: number,\n message: string,\n ) {\n super(`CDP error for ${method}: ${message} (code=${code})`);\n this.name = 'CdpProtocolError';\n }\n}\n\nexport class CdpConnectionClosedError extends Error {\n constructor() {\n super('CDP connection closed before the response arrived.');\n this.name = 'CdpConnectionClosedError';\n }\n}\n\nexport interface CdpCookie {\n readonly name: string;\n readonly value: string;\n readonly domain: string;\n readonly path: string;\n readonly expires: number;\n readonly httpOnly: boolean;\n readonly secure: boolean;\n readonly session: boolean;\n readonly sameSite?: 'Strict' | 'Lax' | 'None';\n}\n\nexport type CdpEventListener = (event: CdpEvent) => void;\n\nexport interface ConnectCdpOptions {\n readonly url: string;\n // Injected for tests. Must match the subset of WebSocket we use: `onopen`,\n // `onmessage`, `onerror`, `onclose`, `send`, `close`, and the `readyState`\n // constants (OPEN, CLOSED).\n readonly webSocketFactory?: (url: string) => WebSocket;\n}\n\nexport class CdpClient {\n private readonly socket: WebSocket;\n private nextId = 1;\n private readonly pending = new Map<\n number,\n { resolve(result: Record<string, unknown>): void; reject(err: Error): void; method: string }\n >();\n private readonly listeners = new Set<CdpEventListener>();\n private closed = false;\n\n private constructor(socket: WebSocket) {\n this.socket = socket;\n socket.addEventListener('message', (ev: MessageEvent) => this.handleMessage(ev));\n socket.addEventListener('close', () => this.handleClose());\n socket.addEventListener('error', () => {\n // Let the `close` event handle pending-promise rejection. Surfacing the\n // error here as well would double-reject; browsers emit both events in\n // quick succession on a failed handshake.\n });\n }\n\n static async connect(options: ConnectCdpOptions): Promise<CdpClient> {\n const factory = options.webSocketFactory ?? ((url: string) => new WebSocket(url));\n const socket = factory(options.url);\n await new Promise<void>((resolve, reject) => {\n const onOpen = () => {\n cleanup();\n resolve();\n };\n const onError = () => {\n cleanup();\n reject(new Error(`Failed to open CDP WebSocket at ${options.url}`));\n };\n const onClose = () => {\n cleanup();\n reject(new Error(`CDP WebSocket closed before opening (${options.url})`));\n };\n const cleanup = () => {\n socket.removeEventListener('open', onOpen);\n socket.removeEventListener('error', onError);\n socket.removeEventListener('close', onClose);\n };\n socket.addEventListener('open', onOpen);\n socket.addEventListener('error', onError);\n socket.addEventListener('close', onClose);\n });\n return new CdpClient(socket);\n }\n\n on(listener: CdpEventListener): () => void {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n async send<T = Record<string, unknown>>(\n method: string,\n params?: Record<string, JsonValue>,\n sessionId?: string,\n ): Promise<T> {\n if (this.closed) throw new CdpConnectionClosedError();\n const id = this.nextId++;\n // Assemble as a plain record and let the JSON serialiser drop keys\n // that are absent — matches the exactOptionalPropertyTypes contract\n // without the triple-nested ternary.\n const req: Record<string, unknown> = { id, method };\n if (params) req.params = params;\n if (sessionId) req.sessionId = sessionId;\n const waiter = new Promise<Record<string, unknown>>((resolve, reject) => {\n this.pending.set(id, { resolve, reject, method });\n });\n this.socket.send(JSON.stringify(req));\n const result = await waiter;\n return result as T;\n }\n\n async close(): Promise<void> {\n if (this.closed) return;\n this.closed = true;\n // Reject any outstanding requests so callers don't hang forever.\n for (const [, pending] of this.pending) {\n pending.reject(new CdpConnectionClosedError());\n }\n this.pending.clear();\n try {\n this.socket.close();\n } catch {\n // already closed\n }\n }\n\n private handleMessage(ev: MessageEvent): void {\n let parsed: CdpMessage;\n try {\n const raw =\n typeof ev.data === 'string' ? ev.data : new TextDecoder().decode(ev.data as ArrayBuffer);\n parsed = JSON.parse(raw) as CdpMessage;\n } catch {\n // Non-JSON payload — shouldn't happen on CDP, ignore.\n return;\n }\n if (isResponse(parsed)) {\n const pending = this.pending.get(parsed.id);\n if (!pending) return;\n this.pending.delete(parsed.id);\n if ('error' in parsed) {\n pending.reject(\n new CdpProtocolError(pending.method, parsed.error.code, parsed.error.message),\n );\n } else {\n pending.resolve(parsed.result);\n }\n return;\n }\n for (const listener of this.listeners) {\n try {\n listener(parsed);\n } catch {\n // Listener errors should not crash the dispatch loop.\n }\n }\n }\n\n private handleClose(): void {\n if (this.closed) return;\n this.closed = true;\n for (const [, pending] of this.pending) {\n pending.reject(new CdpConnectionClosedError());\n }\n this.pending.clear();\n }\n}\n\n// --- High-level helpers ---\n\nexport interface AttachedTarget {\n readonly sessionId: string;\n readonly targetId: string;\n}\n\n/**\n * Attach to the first \"page\" target exposed by the browser. Chrome always\n * opens at least one page target when launched with an initial URL, so this\n * is a reliable way to grab a session without guessing target IDs.\n */\nexport async function attachToFirstPage(client: CdpClient): Promise<AttachedTarget> {\n const { targetInfos } = await client.send<{\n targetInfos: Array<{ targetId: string; type: string }>;\n }>('Target.getTargets');\n const page = targetInfos.find((t) => t.type === 'page');\n if (!page) {\n throw new Error('No page target found; Chrome launched without an initial tab.');\n }\n const { sessionId } = await client.send<{ sessionId: string }>('Target.attachToTarget', {\n targetId: page.targetId,\n flatten: true,\n });\n return { sessionId, targetId: page.targetId };\n}\n\nexport interface FrameNavigatedEvent {\n readonly url: string;\n readonly frameId: string;\n readonly isMainFrame: boolean;\n}\n\n/**\n * Subscribe to main-frame navigations on the attached page session. Returns\n * an unsubscribe function.\n *\n * Chrome emits `Page.frameNavigated` for every frame — we filter to the main\n * frame (top-level document) since auxiliary iframes (analytics, chat\n * widgets) would otherwise trigger false matches.\n */\nexport async function watchMainFrameNavigations(\n client: CdpClient,\n sessionId: string,\n onNavigate: (ev: FrameNavigatedEvent) => void,\n): Promise<() => void> {\n await client.send('Page.enable', {}, sessionId);\n const off = client.on((event) => {\n if (event.sessionId !== sessionId) return;\n if (event.method !== 'Page.frameNavigated') return;\n const frame = event.params.frame as\n | { url?: string; id?: string; parentId?: string }\n | undefined;\n if (!frame?.url || !frame.id) return;\n onNavigate({\n url: frame.url,\n frameId: frame.id,\n isMainFrame: frame.parentId === undefined,\n });\n });\n return off;\n}\n\n/**\n * `Network.getAllCookies` is scoped to a target session — Chrome rejects it\n * on the browser-level endpoint with `method not found`. Requiring sessionId\n * here surfaces that constraint at compile time.\n *\n * The response shape is fixed in the CDP spec, but we still validate every\n * cookie's required string/number fields at runtime so a malformed entry\n * (from a future Chrome change, say) fails loud instead of propagating\n * `undefined` into the Cookie: header or the on-disk session file.\n */\nexport async function getAllCookies(\n client: CdpClient,\n sessionId: string,\n): Promise<readonly CdpCookie[]> {\n const result = await client.send<{ cookies: unknown }>('Network.getAllCookies', {}, sessionId);\n if (!Array.isArray(result.cookies)) {\n throw new Error('Network.getAllCookies returned a non-array payload');\n }\n return result.cookies.map((raw, index) => validateCookie(raw, index));\n}\n\n/**\n * Override the User-Agent on the attached page. The default headless\n * Chrome UA contains a \"HeadlessChrome\" token that some servers use as a\n * bot signal (toss.im included, per the auth spike). We spoof a current\n * stable Chrome UA before the first navigation kicks off.\n */\nexport async function setUserAgentOverride(\n client: CdpClient,\n sessionId: string,\n userAgent: string,\n): Promise<void> {\n await client.send('Network.setUserAgentOverride', { userAgent }, sessionId);\n}\n\n/**\n * Run a JS expression in the main frame and return the resolved value.\n * Wraps the awkward `Runtime.evaluate` shape so callers can `.ok`-check\n * once. `awaitPromise` is on so async expressions like `(async () => …)()`\n * resolve before we read the result.\n */\nexport interface RuntimeEvalSuccess<T> {\n readonly ok: true;\n readonly value: T;\n}\nexport interface RuntimeEvalFailure {\n readonly ok: false;\n readonly error: string;\n}\nexport type RuntimeEvalResult<T> = RuntimeEvalSuccess<T> | RuntimeEvalFailure;\n\nexport async function evaluateInPage<T>(\n client: CdpClient,\n sessionId: string,\n expression: string,\n): Promise<RuntimeEvalResult<T>> {\n let raw: {\n result?: { value?: unknown };\n exceptionDetails?: { text?: string; exception?: { description?: string } };\n };\n try {\n raw = await client.send<{\n result?: { value?: unknown };\n exceptionDetails?: { text?: string; exception?: { description?: string } };\n }>(\n 'Runtime.evaluate',\n {\n expression,\n returnByValue: true,\n awaitPromise: true,\n userGesture: true,\n },\n sessionId,\n );\n } catch (err) {\n return { ok: false, error: (err as Error).message };\n }\n if (raw.exceptionDetails) {\n return {\n ok: false,\n error:\n raw.exceptionDetails.exception?.description ??\n raw.exceptionDetails.text ??\n 'unknown evaluate exception',\n };\n }\n return { ok: true, value: raw.result?.value as T };\n}\n\n/**\n * Read the current main-frame URL from the frame tree. Cheaper than\n * waiting for a `Page.frameNavigated` event and works even if the page\n * already finished navigating before we attached.\n */\nexport async function getMainFrameUrl(\n client: CdpClient,\n sessionId: string,\n): Promise<string | null> {\n const tree = await client\n .send<{ frameTree: { frame: { url?: string } } }>('Page.getFrameTree', {}, sessionId)\n .catch(() => null);\n return tree?.frameTree.frame?.url ?? null;\n}\n\nfunction validateCookie(raw: unknown, index: number): CdpCookie {\n if (!raw || typeof raw !== 'object') {\n throw new Error(`Cookie #${index} is not an object`);\n }\n const c = raw as Record<string, unknown>;\n const str = (field: string): string => {\n const v = c[field];\n if (typeof v !== 'string') throw new Error(`Cookie #${index}.${field} is not a string`);\n return v;\n };\n const num = (field: string): number => {\n const v = c[field];\n if (typeof v !== 'number') throw new Error(`Cookie #${index}.${field} is not a number`);\n return v;\n };\n const bool = (field: string): boolean => {\n const v = c[field];\n if (typeof v !== 'boolean') throw new Error(`Cookie #${index}.${field} is not a boolean`);\n return v;\n };\n const base = {\n name: str('name'),\n value: str('value'),\n domain: str('domain'),\n path: str('path'),\n expires: num('expires'),\n httpOnly: bool('httpOnly'),\n secure: bool('secure'),\n session: bool('session'),\n };\n const sameSite = c.sameSite;\n if (sameSite === 'Strict' || sameSite === 'Lax' || sameSite === 'None') {\n return { ...base, sameSite };\n }\n return base;\n}\n","import { type ChildProcess, spawn } from 'node:child_process';\nimport { constants as fsConstants } from 'node:fs';\nimport { mkdtemp, rm } from 'node:fs/promises';\nimport { homedir, tmpdir } from 'node:os';\nimport { join, win32 as winPath } from 'node:path';\n\n// Thin cross-platform launcher for an existing Chrome/Chromium-family\n// browser with the Chrome DevTools Protocol enabled. We drive the session\n// over CDP rather than relying on Playwright so `bun build --compile` keeps\n// producing a ~10 MB standalone binary with no bundled Chromium.\n//\n// We deliberately use an ephemeral `--user-data-dir` so the login session\n// is isolated from the user's everyday browser profile. The caller is\n// responsible for disposing the session (we expose a `dispose()` helper\n// that kills the process and removes the temp dir).\n\nexport interface ChromePaths {\n readonly candidates: readonly string[];\n}\n\nexport class ChromeNotFoundError extends Error {\n constructor(readonly candidates: readonly string[]) {\n super(\n `Could not find Chrome or a Chromium-family browser. Tried: ${candidates.join(', ')}.\\n` +\n 'Install Chrome, or set AITCC_BROWSER to an executable path.',\n );\n this.name = 'ChromeNotFoundError';\n }\n}\n\nexport class ChromeLaunchError extends Error {\n constructor(\n readonly executable: string,\n cause: Error,\n ) {\n super(`Failed to launch ${executable}: ${cause.message}`);\n this.name = 'ChromeLaunchError';\n this.cause = cause;\n }\n}\n\nexport class ChromeEndpointTimeoutError extends Error {\n constructor(readonly executable: string) {\n super(\n `${executable} did not print a DevTools endpoint within the timeout. ` +\n 'It may have been blocked by the OS or launched a GUI-less variant.',\n );\n this.name = 'ChromeEndpointTimeoutError';\n }\n}\n\n// Probe order: the common install paths, favouring the vendor's own packaging\n// over snap/flatpak (those sometimes restrict --remote-debugging-port writes\n// due to sandboxing). Respect $AITCC_BROWSER as an override.\nexport function chromeCandidates(\n env: NodeJS.ProcessEnv = process.env,\n platform: NodeJS.Platform = process.platform,\n): ChromePaths {\n const override = env.AITCC_BROWSER;\n const out: string[] = [];\n if (override && override.length > 0) out.push(override);\n\n if (platform === 'darwin') {\n out.push(\n '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',\n '/Applications/Google Chrome Beta.app/Contents/MacOS/Google Chrome Beta',\n '/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary',\n '/Applications/Chromium.app/Contents/MacOS/Chromium',\n '/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge',\n '/Applications/Arc.app/Contents/MacOS/Arc',\n );\n } else if (platform === 'win32') {\n // `path.win32.join` produces backslash-separated paths even when the\n // test/build runner is POSIX, so the candidate list matches what\n // Windows actually uses on disk.\n const pf = env.PROGRAMFILES ?? 'C:\\\\Program Files';\n const pf86 = env['PROGRAMFILES(X86)'] ?? 'C:\\\\Program Files (x86)';\n const local = env.LOCALAPPDATA ?? winPath.join(homedir() || 'C:\\\\', 'AppData', 'Local');\n out.push(\n winPath.join(pf, 'Google', 'Chrome', 'Application', 'chrome.exe'),\n winPath.join(pf86, 'Google', 'Chrome', 'Application', 'chrome.exe'),\n winPath.join(local, 'Google', 'Chrome', 'Application', 'chrome.exe'),\n winPath.join(pf, 'Microsoft', 'Edge', 'Application', 'msedge.exe'),\n winPath.join(pf86, 'Microsoft', 'Edge', 'Application', 'msedge.exe'),\n );\n } else {\n // Linux and the rest: rely on PATH lookup via plain command names.\n out.push(\n 'google-chrome-stable',\n 'google-chrome',\n 'chromium-browser',\n 'chromium',\n 'microsoft-edge-stable',\n 'microsoft-edge',\n );\n }\n\n return { candidates: out };\n}\n\nfunction isAbsolutePath(p: string, platform: NodeJS.Platform): boolean {\n if (platform === 'win32') return /^[A-Za-z]:\\\\/.test(p);\n return p.startsWith('/');\n}\n\nasync function resolveOnPath(\n name: string,\n env: NodeJS.ProcessEnv,\n platform: NodeJS.Platform,\n): Promise<string | null> {\n const path = env.PATH ?? env.Path ?? env.path ?? '';\n if (path.length === 0) return null;\n const sep = platform === 'win32' ? ';' : ':';\n const fs = await import('node:fs/promises');\n // Windows picks the matching executable based on PATHEXT — we reproduce\n // the common case so a bare AITCC_BROWSER=chrome still resolves to\n // chrome.exe on disk.\n const extensions =\n platform === 'win32'\n ? ['', ...(env.PATHEXT ?? '.EXE;.CMD;.BAT').split(';').filter((e) => e.length > 0)]\n : [''];\n for (const dir of path.split(sep)) {\n if (dir.length === 0) continue;\n for (const ext of extensions) {\n const candidate = join(dir, name + ext);\n try {\n // Require executable access, not just presence — otherwise a shell\n // alias file or a build artefact sitting on PATH could be picked\n // up as \"Chrome\".\n await fs.access(candidate, fsConstants.X_OK);\n return candidate;\n } catch {\n // try next\n }\n }\n }\n return null;\n}\n\nexport async function findChrome(\n env: NodeJS.ProcessEnv = process.env,\n platform: NodeJS.Platform = process.platform,\n): Promise<string> {\n const { candidates } = chromeCandidates(env, platform);\n const fs = await import('node:fs/promises');\n for (const candidate of candidates) {\n if (isAbsolutePath(candidate, platform)) {\n try {\n await fs.access(candidate, fsConstants.X_OK);\n return candidate;\n } catch {\n // try next\n }\n continue;\n }\n const resolved = await resolveOnPath(candidate, env, platform);\n if (resolved) return resolved;\n }\n throw new ChromeNotFoundError(candidates);\n}\n\nexport interface LaunchedChrome {\n readonly process: ChildProcess;\n readonly webSocketDebuggerUrl: string;\n readonly userDataDir: string;\n dispose(): Promise<void>;\n}\n\nexport interface LaunchChromeOptions {\n readonly initialUrl: string;\n readonly executable?: string;\n readonly endpointTimeoutMs?: number;\n // Run Chrome in `--headless=new` mode for the form-fill login path. The\n // CDP UA spoof handles the \"HeadlessChrome\" token in the default UA, so\n // this flag is just about hiding the GUI.\n readonly headless?: boolean;\n // Hook for tests: if set, skip actually spawning Chrome and feed these\n // bytes to the stderr parser instead. Keeps the parser in the hot path\n // under test without requiring a real Chrome install on CI.\n readonly spawnOverride?: (args: readonly string[]) => ChildProcess;\n}\n\nconst DEVTOOLS_BANNER = /^DevTools listening on (ws:\\/\\/[^\\s]+)\\s*$/m;\n\nfunction consumeDevtoolsEndpoint(buffer: string): string | null {\n const match = DEVTOOLS_BANNER.exec(buffer);\n return match ? (match[1] ?? null) : null;\n}\n\nexport async function launchChrome(options: LaunchChromeOptions): Promise<LaunchedChrome> {\n const executable = options.executable ?? (await findChrome());\n const endpointTimeoutMs = options.endpointTimeoutMs ?? 15_000;\n\n const userDataDir = await mkdtemp(join(tmpdir(), 'aitcc-chrome-'));\n\n // Minimum viable flags:\n // --remote-debugging-port=0 pick an ephemeral port (printed on stderr)\n // --user-data-dir=<tmp> isolate from the user's real profile\n // --no-first-run / --no-default-browser-check skip greeter dialogs\n // --password-store=basic avoid prompting for keyring unlocks on Linux\n // --use-mock-keychain same, but for macOS keychain\n const args: string[] = [\n '--remote-debugging-port=0',\n `--user-data-dir=${userDataDir}`,\n '--no-first-run',\n '--no-default-browser-check',\n '--disable-features=Translate,OptimizationHints',\n '--password-store=basic',\n '--use-mock-keychain',\n ];\n if (options.headless) {\n // `--headless=new` is the modern, GPU-friendly headless mode (the old\n // `--headless` is a separate codebase Google deprecated). `--disable-gpu`\n // keeps it well-behaved on machines with broken GL drivers, and the\n // window-size shapes the layout used by the form-fill JS.\n args.push('--headless=new', '--disable-gpu', '--window-size=1280,900');\n }\n args.push(options.initialUrl);\n\n const spawnFn = options.spawnOverride ?? ((a: readonly string[]) => spawn(executable, [...a]));\n let child: ChildProcess;\n try {\n child = spawnFn(args);\n } catch (err) {\n await rm(userDataDir, { recursive: true, force: true }).catch(() => {});\n throw new ChromeLaunchError(executable, err as Error);\n }\n // Don't block Node's exit on the Chrome child — dispose() kills it\n // explicitly on the happy path; on a hard parent exit we'd rather drop\n // Chrome than hang.\n try {\n child.unref();\n } catch {\n // best-effort\n }\n\n const dispose = async (): Promise<void> => {\n try {\n if (!child.killed) child.kill('SIGTERM');\n } catch {\n // best-effort\n }\n await rm(userDataDir, { recursive: true, force: true }).catch(() => {});\n };\n\n let stderrBuf = '';\n const wsUrl = await new Promise<string>((resolve, reject) => {\n const timer = setTimeout(() => {\n cleanup();\n reject(new ChromeEndpointTimeoutError(executable));\n }, endpointTimeoutMs);\n if (typeof timer.unref === 'function') timer.unref();\n\n const onStderr = (chunk: Buffer) => {\n stderrBuf += chunk.toString('utf8');\n const found = consumeDevtoolsEndpoint(stderrBuf);\n if (found) {\n cleanup();\n resolve(found);\n }\n };\n const onExit = (code: number | null) => {\n cleanup();\n reject(\n new ChromeLaunchError(\n executable,\n new Error(`process exited with code ${code ?? 'null'} before printing endpoint`),\n ),\n );\n };\n const onError = (err: Error) => {\n cleanup();\n reject(new ChromeLaunchError(executable, err));\n };\n const cleanup = () => {\n clearTimeout(timer);\n child.stderr?.off('data', onStderr);\n child.off('exit', onExit);\n child.off('error', onError);\n };\n child.stderr?.on('data', onStderr);\n child.on('exit', onExit);\n child.on('error', onError);\n }).catch(async (err) => {\n await dispose();\n throw err;\n });\n\n return {\n process: child,\n webSocketDebuggerUrl: wsUrl,\n userDataDir,\n dispose,\n };\n}\n\n// Exported for unit tests.\nexport const __test = { consumeDevtoolsEndpoint };\n","// Headless form-fill login flow. Drives Chrome via CDP just like the\n// interactive path, but injects credentials into the Toss Business\n// sign-in form instead of waiting for a human to type them. If the form\n// fill fails (selector mismatch, form not present, etc.) the caller is\n// expected to fall back to the interactive path — this module never\n// retries or loops on its own (rate-limit risk).\n//\n// Step-up auth (Toss app push, OTP, …) is detected by URL pattern OR by\n// Korean text in the page body. When triggered we ask the user to\n// complete the prompt in their Toss app and keep polling for the\n// landing URL.\n//\n// SECURITY: the password value flows through Runtime.evaluate (over the\n// CDP WebSocket on localhost) and never out of this process. We must\n// not log it, embed it in error messages, or surface it via --json. The\n// public errors below redact deliberately.\n\nimport {\n type CdpClient,\n evaluateInPage,\n getMainFrameUrl,\n setUserAgentOverride,\n watchMainFrameNavigations,\n} from './cdp.js';\nimport { isLoginLanding } from './commands/login.js';\n\n// Stock Chrome 130 UA on macOS — the auth spike confirmed servers stop\n// flagging the request once the \"HeadlessChrome\" token is gone. We don't\n// vary by platform: toss.im doesn't OS-fingerprint here, the only goal\n// is to drop the headless token. Last verified against business.toss.im\n// 2026-05-08; bump the version string when the next reviewer touches\n// this file and Chrome stable has moved.\nexport const SPOOFED_USER_AGENT =\n 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) ' +\n 'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36';\n\n// URL fragments that indicate we've been bumped to a step-up challenge.\n// Matched case-insensitively against the main-frame URL.\nexport const STEP_UP_URL_PATTERN = /verify|step.?up|2fa|otp/i;\n\n// Korean copy that the console uses on step-up prompts. Spike never\n// triggered the path, so these come from the patterns the Toss web team\n// uses across other surfaces (\"토스 앱에서 확인\", \"간편인증\", \"전자서명\").\nexport const STEP_UP_BODY_PATTERN = /토스 ?앱|간편인증|전자서명|앱.{0,3}확인/;\n\n// Match against pathname only, not the full URL. The OAuth sign-in URL\n// embeds `redirect_uri=https%3A%2F%2Fapps-in-toss…` in its query string;\n// the raw characters `%2F%2Fa` contain the literal substring `2fa`\n// (case-insensitive), which the `2fa` alternation matches and trips a\n// false step-up on the very first poll.\nexport function urlIndicatesStepUp(url: string): boolean {\n let pathname: string;\n try {\n pathname = new URL(url).pathname;\n } catch {\n return false;\n }\n return STEP_UP_URL_PATTERN.test(pathname);\n}\n\nexport function bodyIndicatesStepUp(bodyText: string): boolean {\n return STEP_UP_BODY_PATTERN.test(bodyText);\n}\n\nexport interface HeadlessLoginCredentials {\n readonly email: string;\n readonly password: string;\n}\n\n// Wider than just success/failure so the caller knows whether to message\n// the user about step-up or to silently fall back to interactive.\nexport type HeadlessLoginOutcome =\n | { readonly kind: 'ok'; readonly stepUp: boolean }\n | { readonly kind: 'fallback'; readonly reason: string }\n | { readonly kind: 'timeout'; readonly stage: 'submit' | 'step-up' };\n\nexport interface RunHeadlessLoginOptions {\n readonly client: CdpClient;\n readonly sessionId: string;\n readonly credentials: HeadlessLoginCredentials;\n // Overall observation window after the form submit. The interactive\n // path's --timeout is much larger because a human types; here we only\n // need long enough for the OAuth chain (~1.3s observed) to finish, so\n // 30s is a comfortable default.\n readonly submitObservationMs?: number;\n // How long to wait for the user to complete a step-up prompt (Toss app\n // push, OTP, …). Defaults to the caller's overall --timeout so we\n // honour the user's intent.\n readonly stepUpTimeoutMs: number;\n // Hook so the CLI command can print a single \"토스 앱에서 …\" line on\n // stderr without this module taking a dependency on process.stderr.\n readonly onStepUp?: () => void;\n}\n\nconst FORM_READY_POLL_MS = 500;\nconst FORM_READY_TIMEOUT_MS = 20_000;\nconst POST_SUBMIT_POLL_MS = 250;\nconst STEP_UP_POLL_MS = 1000;\n\n// Form-fill JS, evaluated in the Toss Business sign-in page. Lives as a\n// string so we don't have to worry about TypeScript transforms changing\n// the shape — the eval target is the browser, not Node.\n//\n// React (Radix UI) treats inputs as controlled components: a plain\n// `input.value = …` assignment is invisible to React state. We have to\n// call the native value setter and dispatch an `input` event so React's\n// onChange handler picks it up. The same trick worked in the spike.\n//\n// Selectors are intentionally robust to id changes (Radix ids look like\n// `radix-:r0:` and aren't stable):\n// - email input matched by name (`email`/`loginId`/`username`),\n// then by `type=email` (which is semantically unambiguous). We do\n// NOT fall back to `type=text` — a search box or other unrelated\n// text input rendered above the form would otherwise receive the\n// credentials in plaintext.\n// - password input matched by name then by `type=password`.\n// - submit button matched by `type=submit` first, then by visible text\n// containing \"로그인\" / \"sign in\" / \"login\".\nconst FILL_AND_SUBMIT_FN = `\n async (email, password) => {\n function pickByName(names) {\n for (const n of names) {\n const el = document.querySelector('input[name=\"' + n + '\"]');\n if (el) return el;\n }\n return null;\n }\n function pickInputByType(types) {\n const inputs = Array.from(document.querySelectorAll('input'));\n for (const t of types) {\n const hit = inputs.find(i => (i.type || '').toLowerCase() === t);\n if (hit) return hit;\n }\n return null;\n }\n function setNative(input, value) {\n const proto = Object.getPrototypeOf(input);\n const desc = Object.getOwnPropertyDescriptor(proto, 'value');\n if (desc && desc.set) desc.set.call(input, value);\n else input.value = value;\n input.dispatchEvent(new Event('input', { bubbles: true }));\n input.dispatchEvent(new Event('change', { bubbles: true }));\n }\n const emailInput =\n pickByName(['email', 'loginId', 'username']) ||\n pickInputByType(['email']);\n const passwordInput =\n pickByName(['password', 'loginPassword']) ||\n pickInputByType(['password']);\n if (!emailInput) return { ok: false, stage: 'find-email' };\n if (!passwordInput) return { ok: false, stage: 'find-password' };\n setNative(emailInput, email);\n setNative(passwordInput, password);\n const buttons = Array.from(document.querySelectorAll('button'));\n const submitBtn = buttons.find(b => {\n if (b.disabled) return false;\n const t = (b.type || '').toLowerCase();\n if (t === 'submit') return true;\n const txt = (b.textContent || '').replace(/\\\\s+/g, '');\n return /로그인|sign-?in|login/i.test(txt);\n });\n if (submitBtn) {\n submitBtn.click();\n return { ok: true, stage: 'submit-button' };\n }\n const form = emailInput.closest('form');\n if (form) {\n if (typeof form.requestSubmit === 'function') form.requestSubmit();\n else form.submit();\n return { ok: true, stage: 'submit-form' };\n }\n return { ok: false, stage: 'submit' };\n }\n`;\n\n// Probe the page to see whether the email + password inputs have\n// rendered. The form arrives async (React boot), so we have to poll\n// before we can fill.\nconst FORM_READY_PROBE_FN = `\n () => {\n const inputs = Array.from(document.querySelectorAll('input'));\n const hasEmail = inputs.some(i => {\n const name = (i.name || '').toLowerCase();\n const type = (i.type || '').toLowerCase();\n const placeholder = (i.placeholder || '').toLowerCase();\n const id = (i.id || '').toLowerCase();\n if (name === 'email' || name === 'loginid' || name === 'username') return true;\n if (type === 'email') return true;\n if (type === 'text' && /id|email|username/.test(name + ' ' + id + ' ' + placeholder)) return true;\n return false;\n });\n const hasPassword = inputs.some(i =>\n (i.type || '').toLowerCase() === 'password' || (i.name || '').toLowerCase() === 'password',\n );\n return { ready: hasEmail && hasPassword, count: inputs.length };\n }\n`;\n\n// Snapshot of post-submit state used to decide between landing /\n// step-up / fallback. Body text capped at 4 KB so we don't pull\n// arbitrarily-large pages over the CDP wire.\nconst POST_SUBMIT_PROBE_FN = `\n () => {\n const bodyText = (document.body && document.body.innerText || '').slice(0, 4000);\n const hasCaptchaIframe = !!document.querySelector(\n 'iframe[src*=\"recaptcha\"], iframe[src*=\"hcaptcha\"], iframe[src*=\"turnstile\"], iframe[src*=\"cloudflare\"]'\n );\n const hasErrorBanner = /비밀번호.{0,10}(틀|일치)|아이디.{0,10}(틀|일치)|로그인.{0,5}(실패|불가)|차단|locked/i.test(bodyText);\n return { url: location.href, bodyText, hasCaptchaIframe, hasErrorBanner };\n }\n`;\n\ninterface FormReadyProbe {\n ready: boolean;\n count: number;\n}\n\ninterface FillResult {\n ok: boolean;\n stage: string;\n}\n\ninterface PostSubmitProbe {\n url: string;\n bodyText: string;\n hasCaptchaIframe: boolean;\n hasErrorBanner: boolean;\n}\n\n/**\n * Drive the sign-in form and return when we either landed on the\n * console workspace, hit a step-up prompt that the user resolved, or\n * decided the headless path can't make progress. This function never\n * touches the cookie store or session file — that stays in the calling\n * command after we return `'ok'`.\n *\n * Errors that should fall back to interactive (form not found, captcha,\n * eval failure) are returned as `{ kind: 'fallback', reason }` rather\n * than thrown. Real I/O errors (CDP socket dies) propagate.\n */\nexport async function runHeadlessLogin(\n options: RunHeadlessLoginOptions,\n): Promise<HeadlessLoginOutcome> {\n const {\n client,\n sessionId,\n credentials,\n submitObservationMs = 30_000,\n stepUpTimeoutMs,\n onStepUp,\n } = options;\n\n // Set the UA before we let the page do any more network. Network/Runtime\n // need explicit enable; Page is enabled lazily by `watchMainFrameNavigations`\n // before we start polling.\n await client.send('Network.enable', {}, sessionId);\n await client.send('Runtime.enable', {}, sessionId);\n await setUserAgentOverride(client, sessionId, SPOOFED_USER_AGENT);\n\n // The page may have started loading with the original headless UA before\n // we got to override it. Reload so the request actually carries our\n // spoofed header.\n await client.send('Page.reload', { ignoreCache: true }, sessionId).catch(() => {\n // best-effort: if reload fails (e.g. about:blank), the next nav still\n // picks up the override.\n });\n\n const ready = await waitForFormReady(client, sessionId);\n if (!ready.ok) {\n return { kind: 'fallback', reason: ready.reason };\n }\n\n const fill = await evaluateInPage<FillResult>(\n client,\n sessionId,\n `(${FILL_AND_SUBMIT_FN})(${JSON.stringify(credentials.email)}, ${JSON.stringify(credentials.password)})`,\n );\n if (!fill.ok) {\n // Don't surface the eval error message verbatim — a future Chrome\n // could echo the original expression and leak the password. The\n // Runtime.evaluate path doesn't actually do that today, but the\n // redaction is cheap insurance.\n return { kind: 'fallback', reason: 'form-fill-eval-failed' };\n }\n if (!fill.value.ok) {\n return { kind: 'fallback', reason: `form-fill-${fill.value.stage}` };\n }\n\n // Watch live navigations so we react to the OAuth-redirect chain in\n // ~ms rather than ~1s polls. Polling is still the primary signal.\n let liveLandingUrl: string | null = null;\n const offNav = await watchMainFrameNavigations(client, sessionId, (ev) => {\n if (!ev.isMainFrame) return;\n if (isLoginLanding(ev.url)) liveLandingUrl = ev.url;\n });\n\n try {\n // Phase 1: poll for either landing or step-up over `submitObservationMs`.\n const phase1 = await observeUntilLandingOrStepUp(\n client,\n sessionId,\n submitObservationMs,\n () => liveLandingUrl,\n );\n\n if (phase1.kind === 'landed') {\n return { kind: 'ok', stepUp: false };\n }\n if (phase1.kind === 'fallback') {\n return { kind: 'fallback', reason: phase1.reason };\n }\n if (phase1.kind === 'timeout') {\n return { kind: 'timeout', stage: 'submit' };\n }\n\n // Phase 2: step-up. Inform the caller and wait — much longer — for\n // the user to complete the Toss-app prompt.\n onStepUp?.();\n const phase2 = await pollForLanding(client, sessionId, stepUpTimeoutMs, () => liveLandingUrl);\n if (phase2 === 'landed') return { kind: 'ok', stepUp: true };\n // `pollForLanding` is typed `'landed' | 'timeout'`; the assignment below\n // is a compile-time exhaustiveness check that catches a future return\n // value being added without the matching case here.\n const _: 'timeout' = phase2;\n void _;\n return { kind: 'timeout', stage: 'step-up' };\n } finally {\n offNav();\n }\n}\n\ninterface FormReadyOk {\n readonly ok: true;\n}\ninterface FormReadyFail {\n readonly ok: false;\n readonly reason: string;\n}\n\nasync function waitForFormReady(\n client: CdpClient,\n sessionId: string,\n): Promise<FormReadyOk | FormReadyFail> {\n const deadline = Date.now() + FORM_READY_TIMEOUT_MS;\n let lastReason = 'timeout';\n while (Date.now() < deadline) {\n const probe = await evaluateInPage<FormReadyProbe>(\n client,\n sessionId,\n `(${FORM_READY_PROBE_FN})()`,\n );\n if (probe.ok) {\n if (probe.value.ready) return { ok: true };\n lastReason = `inputs-not-ready (${probe.value.count} input(s) on page)`;\n } else {\n // The page may still be loading and Runtime.evaluate may transiently\n // fail (`Execution context was destroyed`); keep retrying. Don't fold\n // `probe.error` into the reason — same redaction discipline as the\n // form-fill eval path: today's CDP error text is benign, but a future\n // Chrome could echo the original expression in the message.\n lastReason = 'eval-failed';\n }\n await sleep(FORM_READY_POLL_MS);\n }\n return { ok: false, reason: `form-not-ready: ${lastReason}` };\n}\n\ntype Phase1Result =\n | { kind: 'landed' }\n | { kind: 'step-up' }\n | { kind: 'fallback'; reason: string }\n | { kind: 'timeout' };\n\nasync function observeUntilLandingOrStepUp(\n client: CdpClient,\n sessionId: string,\n totalMs: number,\n liveLanding: () => string | null,\n): Promise<Phase1Result> {\n const deadline = Date.now() + totalMs;\n while (Date.now() < deadline) {\n if (liveLanding()) return { kind: 'landed' };\n const fromTree = await getMainFrameUrl(client, sessionId);\n if (fromTree && isLoginLanding(fromTree)) return { kind: 'landed' };\n\n const probe = await evaluateInPage<PostSubmitProbe>(\n client,\n sessionId,\n `(${POST_SUBMIT_PROBE_FN})()`,\n );\n if (probe.ok) {\n if (isLoginLanding(probe.value.url)) return { kind: 'landed' };\n if (probe.value.hasCaptchaIframe) {\n return { kind: 'fallback', reason: 'captcha-detected' };\n }\n if (probe.value.hasErrorBanner) {\n // Could be a wrong-password case — let the user retype manually\n // rather than silently re-trying with the same credentials and\n // tripping a rate-limit lockout.\n return { kind: 'fallback', reason: 'login-error-banner' };\n }\n if (urlIndicatesStepUp(probe.value.url) || bodyIndicatesStepUp(probe.value.bodyText)) {\n return { kind: 'step-up' };\n }\n }\n await sleep(POST_SUBMIT_POLL_MS);\n }\n return { kind: 'timeout' };\n}\n\nasync function pollForLanding(\n client: CdpClient,\n sessionId: string,\n totalMs: number,\n liveLanding: () => string | null,\n): Promise<'landed' | 'timeout'> {\n const deadline = Date.now() + totalMs;\n while (Date.now() < deadline) {\n if (liveLanding()) return 'landed';\n const url = await getMainFrameUrl(client, sessionId);\n if (url && isLoginLanding(url)) return 'landed';\n await sleep(STEP_UP_POLL_MS);\n }\n return 'timeout';\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => {\n const t = setTimeout(resolve, ms);\n if (typeof t.unref === 'function') t.unref();\n });\n}\n\n// Exported for unit tests. The real flow takes a CdpClient; tests can\n// drive the matchers directly without standing up a fake socket.\nexport const __test = {\n FILL_AND_SUBMIT_FN,\n FORM_READY_PROBE_FN,\n POST_SUBMIT_PROBE_FN,\n};\n","import { confirm, password as passwordPrompt } from '@inquirer/prompts';\nimport { defineCommand } from 'citty';\nimport { type FetchLike, TossApiError } from '../api/http.js';\nimport { fetchConsoleMemberUserInfo } from '../api/me.js';\nimport {\n type CredentialsSource,\n loadCredentials,\n type SaveCredentialsStatus,\n saveCredentials,\n} from '../auth/credentials.js';\nimport {\n attachToFirstPage,\n CdpClient,\n type CdpCookie,\n getAllCookies,\n watchMainFrameNavigations,\n} from '../cdp.js';\nimport {\n ChromeEndpointTimeoutError,\n ChromeLaunchError,\n ChromeNotFoundError,\n launchChrome,\n} from '../chrome.js';\nimport { ExitCode } from '../exit.js';\nimport { exitAfterFlush } from '../flush.js';\nimport { type HeadlessLoginOutcome, runHeadlessLogin } from '../login-headless.js';\nimport { type Session, writeSession } from '../session.js';\n\n// Login flow (replaces the prior OAuth-callback-server scaffold):\n//\n// 1. Launch a Chrome-family browser with an isolated user-data-dir,\n// pointed at the Toss Business sign-in URL that redirects into the\n// Apps in Toss console after authentication.\n// 2. Watch main-frame navigations over CDP. Once the URL lands on the\n// console's post-login workspace page, we know the auth cookies have\n// been set (HttpOnly, so JS can't see them — CDP can).\n// 3. Dump all cookies via `Network.getAllCookies`, resolve the member\n// user-info from the console API to capture a stable identity, and\n// persist `{ user, cookies, capturedAt }` at `$XDG_CONFIG_HOME/\n// aitcc/session.json` (0600).\n// 4. Dispose the Chrome process and wipe the ephemeral user-data-dir.\n//\n// The CDP-discovered redirect URL (`https://apps-in-toss.toss.im/workspace`\n// with optional `?code=...&state=...` auth-code tail) is the production\n// redirect configured on the client_id. We never need a localhost callback.\n\nconst DEFAULT_AUTHORIZE_URL =\n 'https://business.toss.im/account/sign-in' +\n '?client_id=4uktpjgqd0cp9txybqzuxc2y6w0cuupb' +\n '&redirect_uri=https%3A%2F%2Fapps-in-toss.toss.im%2Fsign-up' +\n '&state=%2Fworkspace';\n\n// The CDP login is complete once the main frame lands on the workspace URL.\nconst LOGIN_LANDING_HOST = 'apps-in-toss.toss.im';\nconst LOGIN_LANDING_PATH_PREFIX = '/workspace';\n\n// Hosts we'll drive a login flow to. `AITCC_OAUTH_URL` is meant as a\n// staging-environment escape hatch, not a way to redirect the CLI to an\n// attacker-controlled URL via a tampered shell rc. A `.toss.im` suffix\n// match is the tightest allowlist that still permits internal hosts.\nconst ALLOWED_AUTHORIZE_HOST_SUFFIXES = ['.toss.im'] as const;\n\n// Minimum time we hand to the interactive-fallback path even if the\n// headless attempt already consumed most of `--timeout`. Without a floor\n// the user could see \"Login timed out after 0s\" right after the visible\n// Chrome appears — comfortably long enough to actually type the form.\nexport const INTERACTIVE_FALLBACK_FLOOR_MS = 30_000;\n\n/**\n * Compute the timeout budget for the interactive fallback attempt after a\n * headless attempt has already burned `elapsedMs`. Caps the result at the\n * user's overall `--timeout` and floors it at `INTERACTIVE_FALLBACK_FLOOR_MS`\n * so a near-exhausted budget still gives the user enough time to type.\n *\n * Pure decision function — extracted so the policy can be exercised\n * without standing up the Chrome/CDP machinery.\n */\nexport function computeFallbackTimeoutMs(totalTimeoutMs: number, elapsedMs: number): number {\n return Math.max(INTERACTIVE_FALLBACK_FLOOR_MS, totalTimeoutMs - elapsedMs);\n}\n\nexport function isAllowedAuthorizeHost(host: string): boolean {\n const lower = host.toLowerCase();\n return ALLOWED_AUTHORIZE_HOST_SUFFIXES.some(\n (suffix) => lower === suffix.slice(1) || lower.endsWith(suffix),\n );\n}\n\nexport function isLoginLanding(url: string): boolean {\n try {\n const u = new URL(url);\n // Use hostname (no port) so a same-host landing on a non-default port\n // still matches — the console hasn't shipped a custom port in the\n // wild but we shouldn't trip on one if it appears.\n if (u.hostname !== LOGIN_LANDING_HOST) return false;\n if (\n u.pathname !== LOGIN_LANDING_PATH_PREFIX &&\n !u.pathname.startsWith(`${LOGIN_LANDING_PATH_PREFIX}/`)\n ) {\n return false;\n }\n // Reject things like `/workspacely`: require the prefix to be followed\n // by end-of-path or a '/'.\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Decide which login mode to enter on first attempt. Pure so the\n * branching policy is testable without standing up Chrome.\n *\n * Rules: `--interactive` always wins. Otherwise headless if and only if\n * we have credentials. The caller is responsible for falling back to\n * interactive on a mid-flight headless failure.\n */\nexport function chooseLoginMode(input: {\n readonly interactiveFlag: boolean;\n readonly hasCredentials: boolean;\n}): LoginMode {\n if (input.interactiveFlag) return 'interactive';\n return input.hasCredentials ? 'headless' : 'interactive';\n}\n\n// Two top-level paths into the login flow:\n// - `interactive`: launch a visible Chrome and let the user type\n// credentials themselves. This is the historical path and the\n// fallback whenever headless can't proceed.\n// - `headless`: launch Chrome with --headless=new, fill the form via\n// CDP using credentials from the OS keychain (or env vars), and\n// wait for the same workspace landing URL.\n// Cookie capture / session write / output is shared after the browser\n// has reached the workspace page — both paths converge there.\nexport type LoginMode = 'interactive' | 'headless';\n\nexport interface LoginDeps {\n // DI seam for tests and for keeping the CLI entrypoint as the only\n // module that imports `loadCredentials` directly. `null` means \"no\n // credentials configured, take the interactive path\".\n readonly getCredentials?: () => Promise<CredentialsSource | null>;\n // DI seam for the onboarding prompt's keychain write. Tests can\n // substitute an in-memory backend; production wires `saveCredentials`.\n readonly saveCredentials?: (\n email: string,\n password: string,\n ) => Promise<{ status: SaveCredentialsStatus }>;\n}\n\nexport const loginCommand = defineCommand({\n meta: {\n name: 'login',\n description: 'Open a browser to sign in, then capture the console session cookies.',\n },\n args: {\n json: {\n type: 'boolean',\n description: 'Emit machine-readable JSON to stdout.',\n default: false,\n },\n timeout: {\n type: 'string',\n description: 'Abort if login does not complete within N seconds (default 300).',\n default: '300',\n },\n interactive: {\n type: 'boolean',\n description: 'Force the visible-browser flow even if credentials are configured.',\n default: false,\n },\n 'skip-onboarding': {\n type: 'boolean',\n description: 'Skip the post-login prompt to save credentials to the OS keychain.',\n default: false,\n },\n },\n async run({ args }) {\n return runLoginCommand(\n {\n json: args.json,\n timeout: args.timeout,\n interactive: args.interactive,\n skipOnboarding: args['skip-onboarding'],\n },\n { getCredentials: loadCredentials, saveCredentials },\n );\n },\n});\n\nexport interface LoginCommandArgs {\n readonly json: boolean;\n readonly timeout: string;\n readonly interactive: boolean;\n readonly skipOnboarding: boolean;\n}\n\nexport async function runLoginCommand(args: LoginCommandArgs, deps: LoginDeps): Promise<never> {\n const emitError = (payload: Record<string, unknown>, human: string) => {\n if (args.json) {\n process.stdout.write(`${JSON.stringify({ ok: false, ...payload })}\\n`);\n }\n process.stderr.write(`${human}\\n`);\n };\n\n const timeoutSec = Number(args.timeout);\n if (!Number.isFinite(timeoutSec) || timeoutSec < 1) {\n emitError(\n { reason: 'invalid-timeout', given: args.timeout },\n `Invalid --timeout value: ${args.timeout}`,\n );\n return exitAfterFlush(ExitCode.Usage);\n }\n const timeoutMs = timeoutSec * 1000;\n\n const rawAuthorizeUrl = process.env.AITCC_OAUTH_URL;\n const authorizeUrl = rawAuthorizeUrl ?? DEFAULT_AUTHORIZE_URL;\n if (rawAuthorizeUrl) {\n let parsed: URL | null = null;\n try {\n parsed = new URL(rawAuthorizeUrl);\n } catch {\n // fall through\n }\n if (!parsed || (parsed.protocol !== 'https:' && parsed.protocol !== 'http:')) {\n emitError(\n { reason: 'invalid-authorize-url' },\n `AITCC_OAUTH_URL is not a valid http(s) URL: ${rawAuthorizeUrl}`,\n );\n return exitAfterFlush(ExitCode.Usage);\n }\n if (!isAllowedAuthorizeHost(parsed.hostname)) {\n emitError(\n { reason: 'authorize-host-not-allowed', host: parsed.hostname },\n `Refusing to open ${parsed.hostname}: only *.toss.im hosts are allowed for sign-in.`,\n );\n return exitAfterFlush(ExitCode.Usage);\n }\n process.stderr.write(`Using custom authorize URL from AITCC_OAUTH_URL: ${authorizeUrl}\\n`);\n }\n\n // Decide which mode to run in. `--interactive` always forces the\n // visible-browser path. Otherwise we ask `loadCredentials()` and use\n // them if present.\n let credentials: CredentialsSource | null = null;\n if (!args.interactive) {\n const getCredentials = deps.getCredentials;\n if (getCredentials) {\n credentials = await getCredentials().catch((err: Error) => {\n // A credential backend hiccup shouldn't kill `aitcc login` —\n // log a one-line diagnostic and fall back to interactive.\n process.stderr.write(\n `Credential lookup failed (${err.message}); using interactive login.\\n`,\n );\n return null;\n });\n }\n }\n\n const initialMode: LoginMode = chooseLoginMode({\n interactiveFlag: args.interactive,\n hasCredentials: credentials !== null,\n });\n\n // Cap Chrome's own startup window at half the overall --timeout, with\n // a 30-second floor and 60-second ceiling. Corporate anti-virus can\n // easily push a cold Chrome launch past the default 15s; short\n // `--timeout` values shouldn't starve the launch itself.\n const endpointTimeoutMs = Math.min(60_000, Math.max(30_000, Math.floor(timeoutMs / 2)));\n\n // First attempt: in the chosen mode. If headless declines, we recurse\n // once into interactive — never the other way around.\n const firstAttemptStart = Date.now();\n const result = await attemptLogin({\n args,\n timeoutMs,\n endpointTimeoutMs,\n authorizeUrl,\n mode: initialMode,\n credentials,\n emitError,\n deps,\n });\n\n if (result.status === 'fallback-to-interactive') {\n process.stderr.write(`${result.message}\\n`);\n // Subtract the time the headless attempt already burned so the user's\n // overall `--timeout` budget is honoured. A small floor protects the\n // human-typing window — if headless ate most of the budget we still\n // give the user a usable interactive session, with the cost showing\n // up as the command running slightly past the requested timeout.\n const fallbackTimeoutMs = computeFallbackTimeoutMs(timeoutMs, Date.now() - firstAttemptStart);\n const second = await attemptLogin({\n args,\n timeoutMs: fallbackTimeoutMs,\n endpointTimeoutMs,\n authorizeUrl,\n mode: 'interactive',\n credentials: null,\n emitError,\n deps,\n });\n if (second.status === 'exit') return exitAfterFlush(second.code);\n // A fallback returning fallback again is a programmer error — we\n // never request fallback while already interactive. Narrow on the\n // discriminant so a future variant can't silently land here.\n const _: 'fallback-to-interactive' = second.status;\n void _;\n return exitAfterFlush(ExitCode.Generic);\n }\n\n return exitAfterFlush(result.code);\n}\n\ninterface AttemptOptions {\n readonly args: LoginCommandArgs;\n readonly timeoutMs: number;\n readonly endpointTimeoutMs: number;\n readonly authorizeUrl: string;\n readonly mode: LoginMode;\n readonly credentials: CredentialsSource | null;\n readonly emitError: (payload: Record<string, unknown>, human: string) => void;\n readonly deps: LoginDeps;\n}\n\ntype AttemptResult =\n | { readonly status: 'exit'; readonly code: number }\n | { readonly status: 'fallback-to-interactive'; readonly message: string };\n\nasync function attemptLogin(opts: AttemptOptions): Promise<AttemptResult> {\n const { args, timeoutMs, endpointTimeoutMs, authorizeUrl, mode, credentials, emitError, deps } =\n opts;\n const headless = mode === 'headless';\n\n const launched = await launchChrome({\n initialUrl: authorizeUrl,\n endpointTimeoutMs,\n headless,\n }).catch((err: Error) => err);\n if (launched instanceof ChromeNotFoundError) {\n emitError({ reason: 'chrome-not-found', candidates: launched.candidates }, launched.message);\n return { status: 'exit', code: ExitCode.LoginBrowserNotFound };\n }\n if (launched instanceof ChromeLaunchError || launched instanceof ChromeEndpointTimeoutError) {\n emitError(\n { reason: 'chrome-launch-failed', message: launched.message },\n `Failed to launch browser: ${launched.message}`,\n );\n return { status: 'exit', code: ExitCode.LoginBrowserFailed };\n }\n if (launched instanceof Error) {\n emitError(\n { reason: 'chrome-launch-failed', errorName: launched.name, message: launched.message },\n `Failed to launch browser (${launched.name}): ${launched.message}`,\n );\n return { status: 'exit', code: ExitCode.LoginBrowserFailed };\n }\n\n if (mode === 'interactive') {\n process.stderr.write(\n 'Opened a browser window — complete the sign-in there. The CLI will capture the session automatically.\\n',\n );\n } else {\n const source = credentials?.kind === 'env' ? 'env' : 'keychain';\n process.stderr.write(`Signing in headlessly with credentials from ${source}…\\n`);\n }\n\n // Resource disposal must happen BEFORE `exitAfterFlush` is called:\n // exitAfterFlush terminates the process, and Chrome children on POSIX\n // are not killed automatically when the parent exits.\n let client: CdpClient | null = null;\n const disposeAll = async (): Promise<void> => {\n if (client) {\n await client.close().catch(() => {});\n client = null;\n }\n await launched.dispose().catch(() => {});\n };\n\n try {\n client = await CdpClient.connect({ url: launched.webSocketDebuggerUrl });\n } catch (err) {\n emitError(\n { reason: 'cdp-connect-failed', message: (err as Error).message },\n `Could not connect to the browser over CDP: ${(err as Error).message}`,\n );\n await disposeAll();\n return { status: 'exit', code: ExitCode.LoginBrowserFailed };\n }\n\n let attached: Awaited<ReturnType<typeof attachToFirstPage>>;\n try {\n attached = await attachToFirstPage(client);\n } catch (err) {\n emitError(\n { reason: 'cdp-attach-failed', message: (err as Error).message },\n `Could not attach to the browser tab: ${(err as Error).message}`,\n );\n await disposeAll();\n return { status: 'exit', code: ExitCode.LoginBrowserFailed };\n }\n\n let stepUp = false;\n if (mode === 'headless') {\n if (!credentials) {\n // Defensive — caller should never put us here without credentials.\n await disposeAll();\n return {\n status: 'fallback-to-interactive',\n message: 'No credentials available; switching to interactive login.',\n };\n }\n let outcome: HeadlessLoginOutcome;\n try {\n outcome = await runHeadlessLogin({\n client,\n sessionId: attached.sessionId,\n credentials: { email: credentials.email, password: credentials.password },\n stepUpTimeoutMs: timeoutMs,\n onStepUp: () =>\n process.stderr.write(\n 'Step-up auth requested — complete the prompt in the Toss app to continue…\\n',\n ),\n });\n } catch (err) {\n // Real I/O failure inside the headless flow. Don't fall back —\n // surface it so the user can see what went wrong.\n emitError(\n { reason: 'headless-login-failed', message: (err as Error).message },\n `Headless login failed: ${(err as Error).message}`,\n );\n await disposeAll();\n return { status: 'exit', code: ExitCode.LoginBrowserFailed };\n }\n\n if (outcome.kind === 'fallback') {\n await disposeAll();\n return {\n status: 'fallback-to-interactive',\n message: `headless login failed: ${outcome.reason}, falling back to interactive`,\n };\n }\n if (outcome.kind === 'timeout') {\n emitError(\n { reason: 'login-timeout', timeoutSec: Math.floor(timeoutMs / 1000), stage: outcome.stage },\n `Login timed out after ${Math.floor(timeoutMs / 1000)}s (${outcome.stage}).`,\n );\n await disposeAll();\n return { status: 'exit', code: ExitCode.LoginTimeout };\n }\n stepUp = outcome.stepUp;\n } else {\n const landing = await waitForLanding(client, attached.sessionId, timeoutMs);\n if (landing === 'timeout') {\n emitError(\n { reason: 'login-timeout', timeoutSec: Math.floor(timeoutMs / 1000) },\n `Login timed out after ${Math.floor(timeoutMs / 1000)}s.`,\n );\n await disposeAll();\n return { status: 'exit', code: ExitCode.LoginTimeout };\n }\n if (landing === 'aborted') {\n emitError(\n { reason: 'login-aborted' },\n 'Login was aborted (browser closed before reaching the console).',\n );\n await disposeAll();\n return { status: 'exit', code: ExitCode.LoginBrowserFailed };\n }\n }\n\n // Both paths converge here: pull cookies, resolve identity, write\n // session, emit human/JSON output.\n const cookies = await getAllCookies(client, attached.sessionId).catch((err: Error) => err);\n if (cookies instanceof Error) {\n emitError(\n { reason: 'cookie-capture-failed', message: cookies.message },\n `Failed to capture cookies: ${cookies.message}`,\n );\n await disposeAll();\n return { status: 'exit', code: ExitCode.LoginCookieCaptureFailed };\n }\n\n const user = await resolveUserWithRetry(cookies, {\n onRetry: (ms) =>\n process.stderr.write(\n `Cookies not yet accepted by the console API — retrying in ${ms}ms...\\n`,\n ),\n }).catch((err: Error) => err);\n if (user instanceof Error) {\n const authFailed = user instanceof TossApiError && user.isAuthError;\n emitError(\n {\n reason: authFailed ? 'login-auth-not-active' : 'member-info-failed',\n message: user.message,\n },\n authFailed\n ? 'Browser session did not produce valid console cookies. Try again and wait for the workspace page to load.'\n : `Failed to read member info: ${user.message}`,\n );\n await disposeAll();\n return {\n status: 'exit',\n code: authFailed ? ExitCode.LoginCookieCaptureFailed : ExitCode.ApiError,\n };\n }\n\n const session: Session = {\n schemaVersion: 2,\n user: {\n id: String(user.id),\n email: user.email,\n displayName: user.name,\n },\n cookies,\n origins: [],\n capturedAt: new Date().toISOString(),\n };\n try {\n await writeSession(session);\n } catch (err) {\n emitError(\n { reason: 'session-write-failed', message: (err as Error).message },\n `Failed to write session file: ${(err as Error).message}`,\n );\n await disposeAll();\n return { status: 'exit', code: ExitCode.Generic };\n }\n\n if (args.json) {\n process.stdout.write(\n `${JSON.stringify({\n ok: true,\n status: 'logged-in',\n user: session.user,\n capturedAt: session.capturedAt,\n cookieCount: cookies.length,\n mode,\n stepUp,\n })}\\n`,\n );\n } else {\n process.stdout.write(`Logged in as ${user.name} <${user.email}>\\n`);\n }\n\n // Dispose Chrome BEFORE running the onboarding prompt — the user is\n // about to type a password into the terminal and shouldn't be\n // distracted by a Chrome window still hanging around.\n await disposeAll();\n\n // Onboarding only runs after a successful interactive login on the\n // first attempt where no credentials are configured. Headless logins\n // already have credentials by definition; --json / non-TTY callers\n // are scripted; --skip-onboarding is the explicit opt-out.\n if (\n mode === 'interactive' &&\n credentials === null &&\n !args.json &&\n !args.skipOnboarding &&\n process.stdout.isTTY &&\n process.stdin.isTTY &&\n deps.saveCredentials\n ) {\n await runOnboardingPrompt(user.email, deps.saveCredentials);\n }\n\n return { status: 'exit', code: ExitCode.Ok };\n}\n\n/**\n * Post-login prompt that offers to persist the user's email + password\n * to the OS keychain so subsequent `aitcc login` runs can take the\n * headless form-fill path. Failures are non-fatal: we already wrote a\n * valid session, so a keychain hiccup just means the next login will\n * fall back to interactive — exactly the same UX as before.\n */\nexport async function runOnboardingPrompt(\n email: string,\n save: (email: string, password: string) => Promise<{ status: SaveCredentialsStatus }>,\n): Promise<void> {\n process.stdout.write('\\n');\n process.stdout.write(\n 'Would you like to save your password to the OS keychain so the next ' +\n '`aitcc login` runs headlessly?\\n',\n );\n let agreed: boolean;\n try {\n agreed = await confirm({\n message: 'Save credentials?',\n default: true,\n });\n } catch (err) {\n if (err instanceof Error && err.name === 'ExitPromptError') {\n // User Ctrl-C'd the prompt — silent skip, the session is still good.\n return;\n }\n process.stderr.write(`Onboarding prompt failed: ${(err as Error).message}\\n`);\n return;\n }\n if (!agreed) return;\n\n let password: string;\n try {\n password = await passwordPrompt({\n message: 'Password:',\n mask: true,\n validate: (raw) => (raw.length > 0 ? true : 'password is required'),\n });\n } catch (err) {\n if (err instanceof Error && err.name === 'ExitPromptError') {\n return;\n }\n process.stderr.write(`Could not read password: ${(err as Error).message}\\n`);\n return;\n }\n\n try {\n await save(email, password);\n process.stdout.write('Saved. Next `aitcc login` will run headlessly.\\n');\n } catch (err) {\n // Don't surface the password or stack — just the message. The\n // session was already written, so login itself succeeded; this is a\n // best-effort enhancement and the user can retry via `aitcc auth set`.\n process.stderr.write(\n `Could not save credentials: ${(err as Error).message}. ` +\n 'You can retry later with `aitcc auth set`.\\n',\n );\n }\n}\n\nexport async function waitForLanding(\n client: CdpClient,\n sessionId: string,\n timeoutMs: number,\n): Promise<'ok' | 'timeout' | 'aborted'> {\n // Two signals, run together, first wins:\n // (a) Page.frameNavigated events — responsive, catches the final redirect.\n // (b) Polling Page.getFrameTree — a safety net for the race where Chrome\n // finishes the auth redirects before we finish attaching and\n // subscribing. The navigation event won't re-fire for pages that\n // already landed, so we have to poll the current URL at least once\n // (and continue polling in case CDP events are dropped on slow links).\n return await new Promise<'ok' | 'timeout' | 'aborted'>((resolve) => {\n let settled = false;\n const stops: Array<() => void | Promise<void>> = [];\n const settle = (outcome: 'ok' | 'timeout' | 'aborted') => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n clearInterval(pollTimer);\n for (const s of stops) {\n try {\n void s();\n } catch {\n // best effort\n }\n }\n resolve(outcome);\n };\n\n const timer = setTimeout(() => settle('timeout'), timeoutMs);\n if (typeof timer.unref === 'function') timer.unref();\n\n // Target-destroyed → the user closed the tab before landing.\n stops.push(\n client.on((event) => {\n if (event.method === 'Target.targetDestroyed') settle('aborted');\n }),\n );\n\n // (a) Live event subscription. Fires on fresh navigations after we\n // Page.enable — may not trigger if Chrome already finished all\n // redirects before we attached (handled by (b)).\n watchMainFrameNavigations(client, sessionId, (ev) => {\n if (!ev.isMainFrame) return;\n if (isLoginLanding(ev.url)) settle('ok');\n })\n .then((off) => {\n // Polling may have already settled by the time subscribe returns;\n // in that case unregister the listener immediately rather than\n // leaving it dangling on the client.\n if (settled) off();\n else stops.push(off);\n })\n .catch((err: Error) => {\n if (settled) return;\n process.stderr.write(`Could not watch for navigation: ${err.message}\\n`);\n });\n\n // (b) Poll the current main-frame URL every second. Cheap, robust.\n const checkCurrent = async () => {\n if (settled) return;\n const tree = await client\n .send<{ frameTree: { frame: { url?: string; parentId?: string } } }>(\n 'Page.getFrameTree',\n {},\n sessionId,\n )\n .catch(() => null);\n const url = tree?.frameTree.frame?.url;\n if (url && isLoginLanding(url)) settle('ok');\n };\n // Kick off an immediate check — covers the \"already landed\" case.\n void checkCurrent();\n const pollTimer = setInterval(() => {\n void checkCurrent();\n }, 1000);\n if (typeof pollTimer.unref === 'function') pollTimer.unref();\n });\n}\n\n// The console issues auth cookies a beat after the landing navigation\n// fires — if the first /me call 401s, we wait this long and retry once.\n// Larger than the fastest observed exchange (~200 ms), small enough to\n// keep the user from wondering whether the CLI hung.\nexport const AUTH_SETTLE_DELAY_MS = 750;\n\nexport async function resolveUserWithRetry(\n cookies: readonly CdpCookie[],\n opts: {\n onRetry?: (delayMs: number) => void;\n fetchImpl?: FetchLike;\n } = {},\n): Promise<Awaited<ReturnType<typeof fetchConsoleMemberUserInfo>>> {\n const callArgs = opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {};\n try {\n return await fetchConsoleMemberUserInfo(cookies, callArgs);\n } catch (err) {\n if (err instanceof TossApiError && err.isAuthError) {\n opts.onRetry?.(AUTH_SETTLE_DELAY_MS);\n await new Promise((r) => {\n const t = setTimeout(r, AUTH_SETTLE_DELAY_MS);\n if (typeof t.unref === 'function') t.unref();\n });\n return await fetchConsoleMemberUserInfo(cookies, callArgs);\n }\n throw err;\n }\n}\n","import { defineCommand } from 'citty';\nimport { ExitCode } from '../exit.js';\nimport { exitAfterFlush } from '../flush.js';\nimport { clearSession, sessionPathForDiagnostics } from '../session.js';\n\nexport const logoutCommand = defineCommand({\n meta: {\n name: 'logout',\n description: 'Delete the local session file.',\n },\n args: {\n json: {\n type: 'boolean',\n description: 'Emit machine-readable JSON to stdout.',\n default: false,\n },\n },\n async run({ args }) {\n const path = sessionPathForDiagnostics();\n\n let existed: boolean;\n try {\n const result = await clearSession();\n existed = result.existed;\n } catch (err) {\n const message = (err as Error).message;\n if (args.json) {\n process.stdout.write(\n `${JSON.stringify({ ok: false, reason: 'unlink-failed', path, message })}\\n`,\n );\n }\n process.stderr.write(`Failed to remove session file at ${path}: ${message}\\n`);\n return exitAfterFlush(ExitCode.Generic);\n }\n\n if (args.json) {\n process.stdout.write(\n `${JSON.stringify({ ok: true, status: existed ? 'logged-out' : 'no-session', path })}\\n`,\n );\n } else if (existed) {\n process.stdout.write(`Logged out. Session removed from ${path}\\n`);\n } else {\n process.stdout.write(`No active session at ${path}.\\n`);\n }\n return exitAfterFlush(ExitCode.Ok);\n },\n});\n","import { defineCommand } from 'citty';\nimport { fetchUserTerms, type UserTerm } from '../api/me.js';\nimport { ExitCode } from '../exit.js';\nimport { exitAfterFlush } from '../flush.js';\nimport { readSession } from '../session.js';\nimport { emitFailureFromError, emitJson, emitNotAuthenticated } from './_shared.js';\n\n// --json contract (consumed by agent-plugin):\n//\n// me terms:\n// { ok: true, terms: UserTerm[] } exit 0\n// { ok: true, authenticated: false } exit 10\n// { ok: false, reason: 'network-error' | 'api-error', message, ... } exit 11/17\n//\n// `me` is the user-scoped sibling to `workspace` — anything that describes\n// the logged-in account itself (current console-level terms agreements,\n// future: profile display name, notification preferences) lives here.\n\nfunction formatTermLine(t: UserTerm): string {\n const tag = t.isAgreed ? '[agreed]' : '[pending]';\n const req = t.required ? ' required' : '';\n return ` ${tag}${req} ${t.title}\\n ${t.contentsUrl}\\n`;\n}\n\nconst termsCommand = defineCommand({\n meta: {\n name: 'terms',\n description: 'Show the console-level terms of agreement for the signed-in account.',\n },\n args: {\n json: { type: 'boolean', description: 'Emit machine-readable JSON to stdout.', default: false },\n },\n async run({ args }) {\n const session = await readSession();\n if (!session) {\n emitNotAuthenticated(args.json);\n return exitAfterFlush(ExitCode.NotAuthenticated);\n }\n try {\n const terms = await fetchUserTerms(session.cookies);\n if (args.json) {\n emitJson({ ok: true, terms });\n return exitAfterFlush(ExitCode.Ok);\n }\n if (terms.length === 0) {\n process.stdout.write('No console-level terms required.\\n');\n return exitAfterFlush(ExitCode.Ok);\n }\n process.stdout.write('Console account terms:\\n');\n for (const t of terms) process.stdout.write(formatTermLine(t));\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\nexport const meCommand = defineCommand({\n meta: {\n name: 'me',\n description: 'Inspect account-level settings for the signed-in user.',\n },\n subCommands: {\n terms: termsCommand,\n },\n});\n","import type { CdpCookie } from '../cdp.js';\nimport { type FetchLike, requestConsoleApi } from './http.js';\n\n// GET /workspaces/:id/members — confirmed shape (as of 2026-04):\n// [{ workspaceId, bizUserNo, name, email, status, role,\n// isOwnerDelegationRequested, isAdult }]\n// `bizUserNo` is the stable per-person identifier across workspaces —\n// future `members remove` / role-change commands will key off it.\n\nconst BASE = 'https://apps-in-toss.toss.im/console/api-public/v3/appsintossconsole';\n\nexport interface WorkspaceMember {\n readonly workspaceId: number;\n readonly bizUserNo: number;\n readonly name: string;\n readonly email: string;\n readonly status: string;\n readonly role: string;\n readonly isOwnerDelegationRequested: boolean;\n readonly isAdult: boolean;\n}\n\nexport async function fetchWorkspaceMembers(\n workspaceId: number,\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<WorkspaceMember[]> {\n const url = `${BASE}/workspaces/${workspaceId}/members`;\n const raw = await requestConsoleApi<unknown>({\n url,\n cookies,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n if (!Array.isArray(raw)) {\n throw new Error(`Unexpected members shape for workspace=${workspaceId}: not an array`);\n }\n return raw.map((entry, index) => normalizeMember(entry, workspaceId, index));\n}\n\nfunction normalizeMember(raw: unknown, workspaceId: number, index: number): WorkspaceMember {\n if (raw === null || typeof raw !== 'object') {\n throw new Error(\n `Unexpected member entry at index ${index} for workspace=${workspaceId}: not an object`,\n );\n }\n const rec = raw as Record<string, unknown>;\n const stringField = (k: string): string => {\n const v = rec[k];\n if (typeof v !== 'string') {\n throw new Error(\n `Unexpected member entry at index ${index} for workspace=${workspaceId}: missing ${k}`,\n );\n }\n return v;\n };\n const numField = (k: string): number => {\n const v = rec[k];\n if (typeof v !== 'number' || !Number.isFinite(v)) {\n throw new Error(\n `Unexpected member entry at index ${index} for workspace=${workspaceId}: missing ${k}`,\n );\n }\n return v;\n };\n return {\n workspaceId: numField('workspaceId'),\n bizUserNo: numField('bizUserNo'),\n name: stringField('name'),\n email: stringField('email'),\n status: stringField('status'),\n role: stringField('role'),\n isOwnerDelegationRequested: Boolean(rec.isOwnerDelegationRequested),\n isAdult: Boolean(rec.isAdult),\n };\n}\n","import { defineCommand } from 'citty';\nimport { fetchWorkspaceMembers } from '../api/members.js';\nimport { ExitCode } from '../exit.js';\nimport { exitAfterFlush } from '../flush.js';\nimport {\n emitFailureFromError,\n emitJson,\n printContextHeader,\n resolveWorkspaceContext,\n} from './_shared.js';\n\n// --json contract (consumed by agent-plugin):\n//\n// members ls [--workspace <id>]:\n// { ok: true, workspaceId, members: [{bizUserNo, name, email, status, role, ...}] } exit 0\n// { ok: false, reason: 'no-workspace-selected' } exit 2\n// { ok: false, reason: 'invalid-id', message } exit 2\n//\n// Auth/network/api failures follow the shared contract (exit 10/11/17).\n\nconst lsCommand = defineCommand({\n meta: {\n name: 'ls',\n description: 'List members of the selected workspace.',\n },\n args: {\n workspace: {\n type: 'string',\n description: 'Workspace ID. Defaults to the selected workspace (`aitcc workspace use`).',\n },\n json: { type: 'boolean', description: 'Emit machine-readable JSON to stdout.', default: false },\n },\n async run({ args }) {\n const ctx = await resolveWorkspaceContext(args);\n if (!ctx) return;\n const { session, workspaceId } = ctx;\n printContextHeader(ctx, { json: args.json });\n\n try {\n const members = await fetchWorkspaceMembers(workspaceId, session.cookies);\n if (args.json) {\n // `workspaceId` is omitted per-member (redundant with top level)\n // and `isAdult` is intentionally dropped — it is a Korean-specific\n // age-verification flag (성인 인증) classed as PII under local\n // compliance. Owners see *all* co-members, not just themselves, so\n // default-emitting it would leak every member's adult-verification\n // bit through `--json`. No CLI automation use case justifies\n // exposing it; if one ever arises, an opt-in flag is safer.\n emitJson({\n ok: true,\n workspaceId,\n members: members.map((m) => ({\n bizUserNo: m.bizUserNo,\n name: m.name,\n email: m.email,\n status: m.status,\n role: m.role,\n isOwnerDelegationRequested: m.isOwnerDelegationRequested,\n })),\n });\n return exitAfterFlush(ExitCode.Ok);\n }\n if (members.length === 0) {\n process.stdout.write(`No members in workspace ${workspaceId}.\\n`);\n return exitAfterFlush(ExitCode.Ok);\n }\n for (const m of members) {\n process.stdout.write(`${m.bizUserNo}\\t${m.name}\\t${m.email}\\t${m.role}\\t${m.status}\\n`);\n }\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\nexport const membersCommand = defineCommand({\n meta: {\n name: 'members',\n description: 'Inspect workspace members.',\n },\n subCommands: {\n ls: lsCommand,\n },\n});\n","// Client for the \"ipd-thor\" service, which backs the console's notice\n// board (공지사항). Different base URL than everything else\n// (`api-public.toss.im/api-public/v3/ipd-thor`) and a fixed workspaceId\n// of 129 — that's Toss's shared \"앱인토스 콘솔 공지사항\" workspace, not\n// the caller's business workspace. Every console user reads the same\n// notices from this one bucket.\n//\n// Shares the Toss `{resultType, success, error}` envelope, so we can\n// reuse `requestConsoleApi` from http.ts. Captured session cookies are\n// domain-matched against `.toss.im` which matches both the console\n// and ipd-thor hosts, so no separate auth handshake is needed.\n\nimport type { CdpCookie } from '../cdp.js';\nimport { type FetchLike, requestConsoleApi } from './http.js';\n\nexport const IPD_THOR_WORKSPACE_ID = 129;\nconst BASE = 'https://api-public.toss.im/api-public/v3/ipd-thor/api/v1';\n\n// --- Posts (공지사항) ---\n//\n// GET /workspaces/129/posts?page=&size=&title__icontains=\n//\n// Response envelope (observed 2026-04-23):\n// { page, pageSize, count, next, previous, results: [post] }\n//\n// Pagination is 1-indexed. The server echoes `page: 1` when you send\n// `page=0`, so we expose the page back to callers as the server returned\n// it — don't re-normalize to 0-indexed, that would lie about what the\n// API actually does.\n\nexport interface NoticePostsPage {\n readonly page: number;\n readonly pageSize: number;\n readonly count: number;\n readonly next: string | null;\n readonly previous: string | null;\n readonly results: readonly Readonly<Record<string, unknown>>[];\n}\n\nexport interface FetchNoticesParams {\n readonly page?: number;\n readonly size?: number;\n readonly titleContains?: string;\n}\n\nexport async function fetchNotices(\n params: FetchNoticesParams,\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<NoticePostsPage> {\n const qs = new URLSearchParams();\n qs.set('page', String(params.page ?? 0));\n qs.set('size', String(params.size ?? 20));\n if (params.titleContains !== undefined) {\n qs.set('title__icontains', params.titleContains);\n }\n const url = `${BASE}/workspaces/${IPD_THOR_WORKSPACE_ID}/posts?${qs.toString()}`;\n\n const raw = await requestConsoleApi<unknown>({\n url,\n cookies,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n if (raw === null || typeof raw !== 'object') {\n throw new Error('Unexpected notices shape: not an object');\n }\n const rec = raw as Record<string, unknown>;\n const resultsRaw = rec.results;\n if (!Array.isArray(resultsRaw)) {\n throw new Error('Unexpected notices shape: results is not an array');\n }\n const results = resultsRaw.map((r) => {\n if (r === null || typeof r !== 'object') return {};\n return r as Record<string, unknown>;\n });\n return {\n page: typeof rec.page === 'number' ? rec.page : 1,\n pageSize: typeof rec.pageSize === 'number' ? rec.pageSize : (params.size ?? 20),\n count: typeof rec.count === 'number' ? rec.count : results.length,\n next: typeof rec.next === 'string' ? rec.next : null,\n previous: typeof rec.previous === 'string' ? rec.previous : null,\n results,\n };\n}\n\n// --- Categories ---\n//\n// GET /workspaces/129/categories → array of category objects with\n// { id, name, postCount, children, ... }. Children is always empty in\n// the observed response; if Toss adds hierarchy later we pass it\n// through unchanged.\n\nexport async function fetchNoticeCategories(\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<readonly Readonly<Record<string, unknown>>[]> {\n const url = `${BASE}/workspaces/${IPD_THOR_WORKSPACE_ID}/categories`;\n const raw = await requestConsoleApi<unknown>({\n url,\n cookies,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n if (!Array.isArray(raw)) {\n throw new Error('Unexpected categories shape: not an array');\n }\n return raw.map((c) => {\n if (c === null || typeof c !== 'object') return {};\n return c as Record<string, unknown>;\n });\n}\n\n// --- Single post ---\n//\n// GET /workspaces/129/posts/:id — not yet observed in a live capture\n// (sidebar list endpoint only pulls the collection). Included on\n// speculation so `aitcc notices show <id>` has a place to live; the\n// fetch path follows the same envelope as the list endpoint.\n\nexport async function fetchNoticePost(\n postId: number,\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<Record<string, unknown>> {\n const url = `${BASE}/workspaces/${IPD_THOR_WORKSPACE_ID}/posts/${postId}`;\n const raw = await requestConsoleApi<unknown>({\n url,\n cookies,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) {\n throw new Error(`Unexpected notice-post shape for id=${postId}`);\n }\n return raw as Record<string, unknown>;\n}\n","import { defineCommand } from 'citty';\nimport { fetchNoticeCategories, fetchNoticePost, fetchNotices } from '../api/ipd-thor.js';\nimport { ExitCode } from '../exit.js';\nimport { exitAfterFlush } from '../flush.js';\nimport { emitFailureFromError, emitJson, requireSession } from './_shared.js';\n\n// --json contract (consumed by agent-plugin):\n//\n// notices ls [--page N] [--size N] [--search STR]:\n// { ok: true, page, pageSize, count, hasNext, notices: [...] } exit 0\n//\n// notices show <id>:\n// { ok: true, id, notice: {...} } exit 0\n// { ok: false, reason: 'invalid-id', message } exit 2\n//\n// notices categories:\n// { ok: true, categories: [...] } exit 0\n//\n// Notices live on a separate service (ipd-thor) from the rest of the\n// console API. There's no per-user workspace scoping — all notices come\n// from Toss's shared workspace 129. Session cookies captured at login\n// include the `.toss.im` suffix so they're sent automatically to the\n// ipd-thor host.\n\nfunction parsePositiveInt(raw: string, field: string): { value: number } | { error: string } {\n const n = Number(raw);\n if (!Number.isFinite(n) || !Number.isInteger(n) || n < 0) {\n return { error: `--${field} must be a non-negative integer (got ${JSON.stringify(raw)})` };\n }\n return { value: n };\n}\n\nconst lsCommand = defineCommand({\n meta: {\n name: 'ls',\n description: 'List notices (공지사항) from Apps in Toss.',\n },\n args: {\n page: { type: 'string', description: 'Page number (0-indexed).', default: '0' },\n size: { type: 'string', description: 'Page size.', default: '20' },\n search: { type: 'string', description: 'Filter by title substring (case-insensitive).' },\n json: { type: 'boolean', description: 'Emit machine-readable JSON.', default: false },\n },\n async run({ args }) {\n const session = await requireSession(args.json);\n if (!session) return;\n const pageResult = parsePositiveInt(args.page, 'page');\n if ('error' in pageResult) {\n if (args.json) {\n emitJson({ ok: false, reason: 'invalid-config', field: 'page', message: pageResult.error });\n } else {\n process.stderr.write(`notices ls: ${pageResult.error}\\n`);\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n const sizeResult = parsePositiveInt(args.size, 'size');\n if ('error' in sizeResult) {\n if (args.json) {\n emitJson({ ok: false, reason: 'invalid-config', field: 'size', message: sizeResult.error });\n } else {\n process.stderr.write(`notices ls: ${sizeResult.error}\\n`);\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n if (sizeResult.value === 0) {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'invalid-config',\n field: 'size',\n message: '--size must be at least 1',\n });\n } else {\n process.stderr.write('notices ls: --size must be at least 1\\n');\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n\n try {\n const result = await fetchNotices(\n {\n page: pageResult.value,\n size: sizeResult.value,\n ...(typeof args.search === 'string' && args.search.length > 0\n ? { titleContains: args.search }\n : {}),\n },\n session.cookies,\n );\n\n if (args.json) {\n emitJson({\n ok: true,\n page: result.page,\n pageSize: result.pageSize,\n count: result.count,\n hasNext: result.next !== null,\n notices: result.results,\n });\n return exitAfterFlush(ExitCode.Ok);\n }\n\n process.stdout.write(\n `Notices: page ${result.page}, ${result.results.length}/${result.count} shown\\n`,\n );\n if (result.results.length === 0) {\n process.stdout.write('No notices on this page.\\n');\n return exitAfterFlush(ExitCode.Ok);\n }\n for (const n of result.results) {\n const id = typeof n.id === 'string' || typeof n.id === 'number' ? n.id : '-';\n const category = typeof n.category === 'string' ? n.category : '-';\n const title = typeof n.title === 'string' ? n.title : '';\n const publishedTime = typeof n.publishedTime === 'string' ? n.publishedTime : '';\n process.stdout.write(`${id}\\t${publishedTime}\\t[${category}]\\t${title}\\n`);\n }\n if (result.next !== null) {\n process.stdout.write(`(more: --page ${result.page + 1})\\n`);\n }\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\nconst showCommand = defineCommand({\n meta: {\n name: 'show',\n description: 'Show a single notice post by ID.',\n },\n args: {\n id: { type: 'positional', description: 'Notice post ID.', required: true },\n json: { type: 'boolean', description: 'Emit machine-readable JSON.', default: false },\n },\n async run({ args }) {\n const postId = Number(args.id);\n if (!Number.isFinite(postId) || !Number.isInteger(postId) || postId <= 0) {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'invalid-id',\n message: `notice id must be a positive integer (got ${JSON.stringify(args.id)})`,\n });\n } else {\n process.stderr.write(`notices show: invalid id ${JSON.stringify(args.id)}\\n`);\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n const session = await requireSession(args.json);\n if (!session) return;\n try {\n const notice = await fetchNoticePost(postId, session.cookies);\n if (args.json) {\n emitJson({ ok: true, id: postId, notice });\n return exitAfterFlush(ExitCode.Ok);\n }\n const title = typeof notice.title === 'string' ? notice.title : '';\n const subtitle = typeof notice.subtitle === 'string' ? notice.subtitle : '';\n const category = typeof notice.category === 'string' ? notice.category : '';\n const publishedTime = typeof notice.publishedTime === 'string' ? notice.publishedTime : '';\n const body =\n typeof notice.fullDescription === 'string'\n ? notice.fullDescription\n : typeof notice.shortDescription === 'string'\n ? notice.shortDescription\n : '';\n process.stdout.write(`# ${title}\\n`);\n if (subtitle) process.stdout.write(`${subtitle}\\n`);\n process.stdout.write(`\\n[${category}] ${publishedTime}\\n\\n`);\n process.stdout.write(body);\n if (!body.endsWith('\\n')) process.stdout.write('\\n');\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\nconst categoriesCommand = defineCommand({\n meta: {\n name: 'categories',\n description: 'List notice categories and their post counts.',\n },\n args: {\n json: { type: 'boolean', description: 'Emit machine-readable JSON.', default: false },\n },\n async run({ args }) {\n const session = await requireSession(args.json);\n if (!session) return;\n try {\n const categories = await fetchNoticeCategories(session.cookies);\n if (args.json) {\n emitJson({ ok: true, categories });\n return exitAfterFlush(ExitCode.Ok);\n }\n for (const c of categories) {\n const id = typeof c.id === 'string' || typeof c.id === 'number' ? c.id : '-';\n const name = typeof c.name === 'string' ? c.name : '-';\n const postCount = typeof c.postCount === 'number' ? c.postCount : 0;\n process.stdout.write(`${id}\\t${postCount}\\t${name}\\n`);\n }\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\nexport const noticesCommand = defineCommand({\n meta: {\n name: 'notices',\n description: 'Read Apps in Toss notices (공지사항). Shared across all users.',\n },\n subCommands: {\n ls: lsCommand,\n show: showCommand,\n categories: categoriesCommand,\n },\n});\n","// Thin GitHub Releases API client. Only reads public endpoints, never writes.\n\nconst REPO_OWNER = 'apps-in-toss-community';\nconst REPO_NAME = 'console-cli';\n\nexport interface ReleaseAsset {\n name: string;\n browser_download_url: string;\n size: number;\n}\n\nexport interface Release {\n tag_name: string;\n name: string | null;\n html_url: string;\n assets: ReleaseAsset[];\n}\n\nfunction defaultHeaders(): HeadersInit {\n const headers: Record<string, string> = {\n Accept: 'application/vnd.github+json',\n 'User-Agent': 'aitcc',\n 'X-GitHub-Api-Version': '2022-11-28',\n };\n const token = process.env.GITHUB_TOKEN;\n if (token && token.length > 0) {\n headers.Authorization = `Bearer ${token}`;\n }\n return headers;\n}\n\nexport async function fetchLatestRelease(): Promise<Release> {\n const url = `https://api.github.com/repos/${REPO_OWNER}/${REPO_NAME}/releases/latest`;\n const res = await fetch(url, { headers: defaultHeaders() });\n if (!res.ok) {\n throw new Error(`GitHub releases/latest returned ${res.status} ${res.statusText}`);\n }\n return (await res.json()) as Release;\n}\n\nexport type ConditionalReleaseResult =\n | { readonly status: 'not-modified'; readonly etag: string | undefined }\n | { readonly status: 'updated'; readonly release: Release; readonly etag: string | undefined };\n\n/**\n * Conditional GET against `releases/latest`. If the server returns 304 we\n * learn \"no change\" without consuming a core rate-limit slot. Intended for\n * the background update check, which re-runs often; `fetchLatestRelease()`\n * remains the right call when the upgrade command actually needs the body.\n */\nexport async function fetchLatestReleaseConditional(\n previousEtag: string | undefined,\n): Promise<ConditionalReleaseResult> {\n const url = `https://api.github.com/repos/${REPO_OWNER}/${REPO_NAME}/releases/latest`;\n const headers = defaultHeaders() as Record<string, string>;\n if (previousEtag && previousEtag.length > 0) {\n headers['If-None-Match'] = previousEtag;\n }\n const res = await fetch(url, { headers });\n const etag = res.headers.get('etag') ?? undefined;\n if (res.status === 304) {\n return { status: 'not-modified', etag };\n }\n if (!res.ok) {\n throw new Error(`GitHub releases/latest returned ${res.status} ${res.statusText}`);\n }\n const release = (await res.json()) as Release;\n return { status: 'updated', release, etag };\n}\n\nexport function findSha256SumsAsset(release: Release): ReleaseAsset | undefined {\n return release.assets.find((a) => a.name === 'SHA256SUMS');\n}\n\n// Parse `tag_name` into a comparable semver string. Changesets tags this repo\n// as `@ait-co/console-cli@0.1.2`; older ad-hoc tags may be `v0.1.2`. We\n// accept both.\nexport function versionFromTag(tag: string): string {\n const at = tag.lastIndexOf('@');\n const candidate = at >= 0 ? tag.slice(at + 1) : tag;\n return candidate.startsWith('v') ? candidate.slice(1) : candidate;\n}\n","// Map Node's `process.platform` / `process.arch` to the binary asset names\n// produced by `scripts/build-bin.ts` and attached to GitHub Releases.\n\nexport interface PlatformTarget {\n os: 'linux' | 'darwin' | 'windows';\n arch: 'x64' | 'arm64';\n assetName: string;\n}\n\nexport function detectPlatform(): PlatformTarget | null {\n let os: PlatformTarget['os'];\n switch (process.platform) {\n case 'linux':\n os = 'linux';\n break;\n case 'darwin':\n os = 'darwin';\n break;\n case 'win32':\n os = 'windows';\n break;\n default:\n return null;\n }\n\n let arch: PlatformTarget['arch'];\n switch (process.arch) {\n case 'x64':\n arch = 'x64';\n break;\n case 'arm64':\n arch = 'arm64';\n break;\n default:\n return null;\n }\n\n // We don't ship windows-arm64 yet — Bun's `--compile` target support is still partial.\n if (os === 'windows' && arch === 'arm64') return null;\n\n const suffix = os === 'windows' ? '.exe' : '';\n return { os, arch, assetName: `aitcc-${os}-${arch}${suffix}` };\n}\n","// Minimal semver comparator. We only need \"is A strictly newer than B?\" for\n// the upgrade check. Pulling the full `semver` package would bloat the\n// compiled binary for one function.\n\nexport function parseSemver(\n v: string,\n): { major: number; minor: number; patch: number; pre: string } | null {\n const m = /^(\\d+)\\.(\\d+)\\.(\\d+)(?:-([0-9A-Za-z.-]+))?/.exec(v);\n if (!m) return null;\n return { major: +m[1]!, minor: +m[2]!, patch: +m[3]!, pre: m[4] ?? '' };\n}\n\n// Returns 1 if a > b, -1 if a < b, 0 if equal. Returns 0 if either is\n// unparseable (defensive — upgrade will treat that as \"already latest\").\nexport function compareSemver(a: string, b: string): number {\n const pa = parseSemver(a);\n const pb = parseSemver(b);\n if (!pa || !pb) return 0;\n if (pa.major !== pb.major) return pa.major > pb.major ? 1 : -1;\n if (pa.minor !== pb.minor) return pa.minor > pb.minor ? 1 : -1;\n if (pa.patch !== pb.patch) return pa.patch > pb.patch ? 1 : -1;\n // Treat \"no prerelease\" as greater than \"has prerelease\" (1.0.0 > 1.0.0-rc).\n if (pa.pre === pb.pre) return 0;\n if (pa.pre === '') return 1;\n if (pb.pre === '') return -1;\n return pa.pre > pb.pre ? 1 : -1;\n}\n","import { createHash } from 'node:crypto';\nimport { createReadStream } from 'node:fs';\n\n// Parse the GNU coreutils `sha256sum` output format: one entry per line,\n// `<hex> <name>`. The two-space separator is canonical; a leading `*` on\n// the name marks binary mode, which we strip. Blank lines and `#` comments\n// are ignored. Hex is normalized to lowercase so callers can compare with\n// `===`.\nexport function parseSha256Sums(text: string): Map<string, string> {\n const out = new Map<string, string>();\n for (const rawLine of text.split(/\\r?\\n/)) {\n const line = rawLine.trim();\n if (line === '' || line.startsWith('#')) continue;\n const match = line.match(/^([0-9a-fA-F]{64})\\s+\\*?(.+)$/);\n if (!match) continue;\n const hash = match[1]?.toLowerCase();\n const name = match[2]?.trim();\n if (!hash || !name) continue;\n out.set(name, hash);\n }\n return out;\n}\n\nexport function sha256OfFile(path: string): Promise<string> {\n return new Promise((resolve, reject) => {\n const hash = createHash('sha256');\n const stream = createReadStream(path);\n stream.on('error', reject);\n stream.on('data', (chunk) => hash.update(chunk));\n stream.on('end', () => resolve(hash.digest('hex')));\n });\n}\n","// Single source of truth for the embedded CLI version.\n//\n// The value is replaced at build time:\n// - tsdown → via the `define` block in `tsdown.config.ts`\n// - bun → via `--define AITCC_VERSION=...` in `scripts/build-bin.ts`\n//\n// During `pnpm test` / `ts-node` execution the define isn't applied, so we fall\n// back to reading `package.json` at runtime. That path is never hit in the\n// shipped artifacts.\n\ndeclare const AITCC_VERSION: string | undefined;\n\nfunction resolveVersion(): string {\n try {\n // biome-ignore lint/suspicious/noExplicitAny: globalThis lookup for optional build-time define\n const injected = (globalThis as any).AITCC_VERSION as string | undefined;\n if (typeof injected === 'string' && injected.length > 0) return injected;\n } catch {\n // ignore\n }\n try {\n if (typeof AITCC_VERSION === 'string' && AITCC_VERSION.length > 0) {\n return AITCC_VERSION;\n }\n } catch {\n // ignore\n }\n return '0.0.0-dev';\n}\n\nexport const VERSION = resolveVersion();\n","import { execFile } from 'node:child_process';\nimport { chmod, copyFile, rename, unlink, writeFile } from 'node:fs/promises';\nimport { basename, dirname } from 'node:path';\nimport { promisify } from 'node:util';\nimport { defineCommand } from 'citty';\nimport { ExitCode } from '../exit.js';\nimport { fetchLatestRelease, findSha256SumsAsset, versionFromTag } from '../github.js';\nimport { detectPlatform } from '../platform.js';\nimport { compareSemver } from '../semver.js';\nimport { parseSha256Sums, sha256OfFile } from '../sha256.js';\nimport { VERSION } from '../version.js';\n\nconst execFileP = promisify(execFile);\n\n// Distinguishes a Bun-compiled standalone (where `process.execPath` points at\n// the binary itself) from a Node-hosted install (where it points at `node`).\n// Only the former can atomically replace itself; the latter should upgrade\n// via npm.\nfunction isStandaloneBinary(): boolean {\n const exe = basename(process.execPath).toLowerCase();\n return exe.startsWith('aitcc');\n}\n\n// Best-effort cleanup of the `<exePath>.old` left behind by a previous Windows\n// upgrade. Windows can't rename a running exe, so the upgrade flow renames the\n// old binary aside before moving the new one in; the leftover has to be\n// reaped on a later boot when nothing holds it open. Intentionally swallows\n// every error: file may still be in use, perms may differ, or this is POSIX\n// where the path doesn't exist at all. Never logs.\nexport async function cleanupStaleUpgradeArtifacts(\n exePath: string = process.execPath,\n): Promise<void> {\n if (process.platform !== 'win32') return;\n if (!exePath) return;\n try {\n await unlink(`${exePath}.old`);\n } catch {\n // ENOENT, EBUSY, EACCES — all swallowed by design.\n }\n}\n\nexport const upgradeCommand = defineCommand({\n meta: {\n name: 'upgrade',\n description: 'Download the latest release binary from GitHub and replace the current one.',\n },\n args: {\n json: {\n type: 'boolean',\n description: 'Emit machine-readable JSON to stdout.',\n default: false,\n },\n force: {\n type: 'boolean',\n description: 'Re-install even if already on the latest version.',\n default: false,\n },\n 'dry-run': {\n type: 'boolean',\n description: 'Check for updates without downloading or replacing.',\n default: false,\n },\n },\n async run({ args }) {\n const emit = (payload: Record<string, unknown>, human: string) => {\n if (args.json) {\n process.stdout.write(`${JSON.stringify(payload)}\\n`);\n } else {\n process.stdout.write(`${human}\\n`);\n }\n };\n const emitError = (payload: Record<string, unknown>, human: string) => {\n if (args.json) {\n process.stdout.write(`${JSON.stringify({ ok: false, ...payload })}\\n`);\n } else {\n process.stderr.write(`${human}\\n`);\n }\n };\n\n let release: Awaited<ReturnType<typeof fetchLatestRelease>>;\n try {\n release = await fetchLatestRelease();\n } catch (err) {\n emitError(\n { reason: 'network-error', message: (err as Error).message },\n `Failed to query GitHub releases: ${(err as Error).message}`,\n );\n process.exit(ExitCode.NetworkError);\n }\n\n const latest = versionFromTag(release.tag_name);\n const current = VERSION;\n const cmp = compareSemver(latest, current);\n const needsUpdate = cmp > 0 || args.force;\n\n if (!needsUpdate) {\n emit(\n { ok: true, status: 'already-latest', current, latest },\n `Already on the latest version (${current}).`,\n );\n process.exit(ExitCode.UpgradeAlreadyLatest);\n }\n\n if (args['dry-run']) {\n emit(\n { ok: true, status: 'update-available', current, latest, url: release.html_url },\n `Update available: ${current} → ${latest}\\n${release.html_url}`,\n );\n return;\n }\n\n if (!isStandaloneBinary()) {\n emitError(\n {\n reason: 'not-standalone',\n current,\n latest,\n hint: 'npm i -g @ait-co/console-cli@latest',\n },\n [\n 'This install was launched via Node, not the standalone binary.',\n 'Self-upgrade is only supported for the compiled binary.',\n `Run: npm i -g @ait-co/console-cli@latest (currently ${current}, latest ${latest})`,\n ].join('\\n'),\n );\n process.exit(ExitCode.UpgradeUnavailable);\n }\n\n const platform = detectPlatform();\n if (!platform) {\n emitError(\n {\n reason: 'unsupported-platform',\n platform: process.platform,\n arch: process.arch,\n },\n `No prebuilt binary for ${process.platform}/${process.arch}.`,\n );\n process.exit(ExitCode.UpgradeUnavailable);\n }\n\n const asset = release.assets.find((a) => a.name === platform.assetName);\n if (!asset) {\n emitError(\n { reason: 'asset-missing', assetName: platform.assetName, tag: release.tag_name },\n `Release ${release.tag_name} has no asset named ${platform.assetName}. It may still be uploading.`,\n );\n process.exit(ExitCode.UpgradeUnavailable);\n }\n\n const exePath = process.execPath;\n const stagingPath = `${exePath}.new.${Date.now()}`;\n\n if (!args.json) {\n process.stdout.write(`Downloading ${asset.name} (${latest})...\\n`);\n }\n\n try {\n const res = await fetch(asset.browser_download_url);\n if (!res.ok || !res.body) {\n throw new Error(`Download failed: ${res.status} ${res.statusText}`);\n }\n const buf = new Uint8Array(await res.arrayBuffer());\n await writeFile(stagingPath, buf, { mode: 0o755 });\n await chmod(stagingPath, 0o755);\n } catch (err) {\n emitError(\n { reason: 'download-failed', message: (err as Error).message },\n `Failed to download new binary: ${(err as Error).message}`,\n );\n process.exit(ExitCode.NetworkError);\n }\n\n // Verify the downloaded binary against `SHA256SUMS` from the same release\n // before letting it replace the running executable. Mirrors the check in\n // `install.sh`. There is no opt-out; the gate exists because the binary\n // is about to be `chmod 0755` and renamed over `process.execPath`.\n const sumsAsset = findSha256SumsAsset(release);\n if (!sumsAsset) {\n await unlink(stagingPath).catch(() => {});\n emitError(\n { reason: 'sums-missing', tag: release.tag_name },\n `Release ${release.tag_name} has no SHA256SUMS asset. It may still be uploading; retry shortly.`,\n );\n process.exit(ExitCode.UpgradeChecksumFailed);\n }\n\n let expected: string | undefined;\n let actual: string;\n try {\n const sumsRes = await fetch(sumsAsset.browser_download_url);\n if (!sumsRes.ok) {\n throw new Error(`SHA256SUMS download failed: ${sumsRes.status} ${sumsRes.statusText}`);\n }\n const sumsText = await sumsRes.text();\n const sums = parseSha256Sums(sumsText);\n expected = sums.get(platform.assetName);\n actual = (await sha256OfFile(stagingPath)).toLowerCase();\n } catch (err) {\n await unlink(stagingPath).catch(() => {});\n emitError(\n { reason: 'sums-fetch-failed', message: (err as Error).message },\n `Failed to verify checksum: ${(err as Error).message}`,\n );\n process.exit(ExitCode.UpgradeChecksumFailed);\n }\n\n if (!expected) {\n await unlink(stagingPath).catch(() => {});\n emitError(\n { reason: 'sums-no-entry', assetName: platform.assetName, tag: release.tag_name },\n `SHA256SUMS for ${release.tag_name} has no entry for ${platform.assetName}.`,\n );\n process.exit(ExitCode.UpgradeChecksumFailed);\n }\n\n if (expected.toLowerCase() !== actual) {\n await unlink(stagingPath).catch(() => {});\n emitError(\n {\n reason: 'sha256-mismatch',\n assetName: platform.assetName,\n expected: expected.toLowerCase(),\n actual,\n },\n `Checksum mismatch for ${platform.assetName}: expected ${expected.toLowerCase()}, got ${actual}.`,\n );\n process.exit(ExitCode.UpgradeChecksumFailed);\n }\n\n if (!args.json) {\n process.stdout.write('Checksum OK.\\n');\n }\n\n // POSIX backup: copy the current binary so a failed smoke test can be\n // rolled back via `rename(backup, exe)`. We can't hard-link because the\n // staging dir might be on a different fs or have stricter perms. Windows\n // gets backup-for-free via the `<exe>.old` move below.\n const backupPath = process.platform === 'win32' ? null : `${exePath}.bak.${Date.now()}`;\n if (backupPath) {\n try {\n await copyFile(exePath, backupPath);\n } catch (err) {\n await unlink(stagingPath).catch(() => {});\n emitError(\n { reason: 'backup-failed', message: (err as Error).message, exePath, backupPath },\n `Failed to create rollback backup at ${backupPath}: ${(err as Error).message}`,\n );\n process.exit(ExitCode.Generic);\n }\n }\n\n // Atomic replace. POSIX `rename(2)` on the same filesystem is atomic.\n // On Windows a running exe can't be overwritten directly; the staging\n // path is in the same dir, so rename-over works on most shells, and we\n // leave `<exe>.old` handling to a future refinement.\n try {\n if (process.platform === 'win32') {\n await rename(exePath, `${exePath}.old`);\n await rename(stagingPath, exePath);\n } else {\n await rename(stagingPath, exePath);\n }\n } catch (err) {\n if (backupPath) await unlink(backupPath).catch(() => {});\n emitError(\n { reason: 'replace-failed', message: (err as Error).message, exePath, stagingPath },\n `Failed to replace binary at ${exePath}: ${(err as Error).message}`,\n );\n process.exit(ExitCode.Generic);\n }\n\n // Smoke test: invoke the just-installed binary with `--version`. Catches\n // \"valid bytes but won't run\" cases (wrong platform asset, broken\n // entitlement, OS gating) that SHA-256 verification can't see. Strict\n // version-string equality is intentionally NOT checked — a release-\n // pipeline embedding mismatch shouldn't trigger an auto-rollback.\n let smokeFailure: string | null = null;\n try {\n const { stdout } = await execFileP(exePath, ['--version'], {\n timeout: 10_000,\n windowsHide: true,\n });\n if (!stdout.trim()) smokeFailure = 'empty stdout from --version';\n } catch (err) {\n smokeFailure = (err as Error).message;\n }\n\n if (smokeFailure) {\n let rollbackError: string | null = null;\n // Track which rollback step failed so the user message points at the\n // file that actually needs manual intervention.\n let recoveryHint: string | null = null;\n try {\n if (process.platform === 'win32') {\n try {\n await unlink(exePath);\n } catch (err) {\n recoveryHint = `Failed to remove broken binary at ${exePath}; remove it manually then rename ${exePath}.old back to ${exePath}.`;\n throw err;\n }\n await rename(`${exePath}.old`, exePath);\n } else if (backupPath) {\n await rename(backupPath, exePath);\n }\n } catch (err) {\n rollbackError = (err as Error).message;\n if (!recoveryHint) {\n recoveryHint =\n process.platform === 'win32'\n ? `Rename ${exePath}.old back to ${exePath} to restore the previous binary.`\n : `Rename ${backupPath} back to ${exePath} to restore the previous binary.`;\n }\n }\n emitError(\n {\n reason: 'smoke-test-failed',\n message: smokeFailure,\n exePath,\n ...(rollbackError ? { rollbackError, backupPath } : { rolledBack: true }),\n },\n rollbackError\n ? `New binary failed --version smoke test: ${smokeFailure}\\nRollback also failed: ${rollbackError}\\n${recoveryHint}`\n : `New binary failed --version smoke test: ${smokeFailure}\\nReverted to previous binary.`,\n );\n process.exit(ExitCode.UpgradeSmokeTestFailed);\n }\n\n if (backupPath) await unlink(backupPath).catch(() => {});\n\n emit(\n {\n ok: true,\n status: 'upgraded',\n from: current,\n to: latest,\n installedAt: exePath,\n installedIn: dirname(exePath),\n },\n `Upgraded aitcc: ${current} → ${latest}`,\n );\n },\n});\n","import { mkdir, readdir, readFile, rename, stat, unlink, writeFile } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\nimport { fetchLatestReleaseConditional, versionFromTag } from './github.js';\nimport { upgradeCheckPath } from './paths.js';\nimport { compareSemver } from './semver.js';\nimport { VERSION } from './version.js';\n\n// Background \"is there a newer aitcc?\" probe. Rate-limit friendly by design:\n//\n// * At most one network call every 24 hours, regardless of how often the\n// user runs a command that opts in.\n// * Even a failed probe updates the cache timestamp, so a broken network\n// (or a 403 from GitHub) does not loop us back within minutes.\n// * Cache write is stamped BEFORE the network call and promoted atomically\n// via tempfile+rename, so two concurrent `aitcc whoami` invocations can't\n// both escape the throttle and an `exitAfterFlush` mid-write can't leave\n// a truncated JSON file.\n// * Uses a conditional GET with the previous ETag — a 304 response does\n// not consume the anonymous 60/hr core rate-limit bucket.\n// * Fully opt-out via AITCC_NO_UPDATE_CHECK=1 (and implicitly disabled\n// when stderr is not a TTY, so agent-plugin / script consumers never\n// see a stray notice line).\n//\n// The `upgrade` command does NOT use this path — it's the explicit fetch,\n// runs immediately on demand, and its output is the point.\n\nexport const UPDATE_CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;\n\nexport interface UpdateCheckCache {\n readonly lastCheckedAt: string; // ISO 8601\n readonly latestTag?: string;\n readonly etag?: string;\n}\n\nexport async function readCache(): Promise<UpdateCheckCache | null> {\n let raw: string;\n try {\n raw = await readFile(upgradeCheckPath(), 'utf8');\n } catch {\n return null;\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return null;\n }\n if (!parsed || typeof parsed !== 'object') return null;\n const obj = parsed as Record<string, unknown>;\n if (typeof obj.lastCheckedAt !== 'string') return null;\n // Reject non-string optional fields — a hand-edited or cross-version cache\n // with wrong types shouldn't corrupt later string operations.\n if (obj.latestTag !== undefined && typeof obj.latestTag !== 'string') return null;\n if (obj.etag !== undefined && typeof obj.etag !== 'string') return null;\n const result: UpdateCheckCache = {\n lastCheckedAt: obj.lastCheckedAt,\n ...(obj.latestTag !== undefined ? { latestTag: obj.latestTag as string } : {}),\n ...(obj.etag !== undefined ? { etag: obj.etag as string } : {}),\n };\n return result;\n}\n\nexport async function writeCache(entry: UpdateCheckCache): Promise<void> {\n const path = upgradeCheckPath();\n const dir = dirname(path);\n await mkdir(dir, { recursive: true });\n // Best-effort: clean up tempfiles a previous SIGKILL/power-loss left\n // behind between writeFile() and rename() below. Fire-and-forget so\n // it never blocks the actual write; failures are swallowed silently.\n void sweepStaleTempfiles(dir, path);\n // Atomic promote: write to a unique sibling tempfile, then rename. On\n // POSIX rename(2) is atomic within the same filesystem, so a truncated\n // or crash-interrupted write never becomes the canonical cache file.\n // Windows is best-effort (ReplaceFileW is atomic for same-volume targets\n // but not guaranteed cross-process).\n // Unique-per-caller tempfile: pid + wall-clock + random suffix. Prevents\n // concurrent writers in the same process (Date.now() has ms resolution and\n // can collide) from stealing each other's tempfiles between writeFile and\n // rename.\n const tmp = `${path}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 10)}.tmp`;\n try {\n // Cache body is non-secret, but mtime + the ETag are a mild leak of\n // \"when this user last ran aitcc\" on a multi-user box. Match session\n // storage's 0600 mode for consistency and defence in depth.\n await writeFile(tmp, JSON.stringify(entry, null, 2), { mode: 0o600 });\n await rename(tmp, path);\n } catch (err) {\n // If rename failed we may have left the tempfile behind; clean up.\n await unlink(tmp).catch(() => {});\n throw err;\n }\n}\n\nconst TEMPFILE_TTL_MS = 7 * 24 * 60 * 60 * 1000;\n// Matches the suffix shape produced by writeCache:\n// `<path>.<pid>.<ts>.<rand>.tmp`\n// where pid + ts are decimals and rand is base36 (a-z0-9). Anchored so an\n// arbitrary `.tmp` file (someone's editor swap, a future tool's tempfile)\n// is left alone.\nconst TEMPFILE_SUFFIX_RE = /\\.\\d+\\.\\d+\\.[a-z0-9]+\\.tmp$/i;\n\nexport async function sweepStaleTempfiles(dir: string, basePath: string): Promise<void> {\n let entries: string[];\n try {\n entries = await readdir(dir);\n } catch {\n return;\n }\n const cutoff = Date.now() - TEMPFILE_TTL_MS;\n const baseName = basePath.slice(dir.length + 1);\n await Promise.all(\n entries.map(async (name) => {\n // Only sweep tempfiles that this writer would itself produce. Belt-\n // and-suspenders: prefix match on `<base>.` plus the suffix regex.\n if (!name.startsWith(`${baseName}.`)) return;\n if (!TEMPFILE_SUFFIX_RE.test(name)) return;\n const p = join(dir, name);\n try {\n const st = await stat(p);\n if (st.mtimeMs < cutoff) await unlink(p);\n } catch {\n // ENOENT (race), EACCES, etc — best-effort, ignore.\n }\n }),\n );\n}\n\n/** Has the throttle window elapsed since the last recorded check? */\nexport function isDueForCheck(\n cache: UpdateCheckCache | null,\n now: number = Date.now(),\n intervalMs: number = UPDATE_CHECK_INTERVAL_MS,\n): boolean {\n if (!cache) return true;\n const last = Date.parse(cache.lastCheckedAt);\n if (!Number.isFinite(last)) return true;\n // If the system clock jumps backwards (NTP resync, VM resume), treat the\n // cache as stale and re-check. Better to probe once than to be silently\n // stuck until wall-time catches up to `last + interval`.\n if (now < last) return true;\n return now - last >= intervalMs;\n}\n\nexport interface UpdateCheckOptions {\n readonly env?: NodeJS.ProcessEnv;\n readonly isTTY?: boolean;\n readonly now?: number;\n readonly intervalMs?: number;\n}\n\n/**\n * Perform the throttled update check. Returns the final cache entry (for\n * testing) or null when skipped. Never throws — network errors are\n * intentionally swallowed so they never interrupt the foreground command.\n */\nexport async function maybeCheckForUpdate(\n opts: UpdateCheckOptions = {},\n): Promise<UpdateCheckCache | null> {\n const env = opts.env ?? process.env;\n const isTTY = opts.isTTY ?? Boolean(process.stderr.isTTY);\n const now = opts.now ?? Date.now();\n const intervalMs = opts.intervalMs ?? UPDATE_CHECK_INTERVAL_MS;\n\n // Opt-out: any non-empty value that isn't explicitly falsey counts. Matches\n // the loose convention used by CI / DEBUG env vars — \"AITCC_NO_UPDATE_CHECK=true\"\n // works alongside \"=1\".\n const optOut = env.AITCC_NO_UPDATE_CHECK;\n if (optOut && optOut !== '0' && optOut.toLowerCase() !== 'false') return null;\n // Notice lines are targeted at interactive users. Checking stderr (where\n // the notice is written) rather than stdout means `aitcc whoami > out.log`\n // still shows the notice on the terminal, while a fully piped invocation\n // (stderr redirected or captured) is silent.\n if (!isTTY) return null;\n\n const cache = await readCache();\n if (!isDueForCheck(cache, now, intervalMs)) return null;\n\n // Stamp the cache BEFORE the network call so a concurrent `aitcc whoami`\n // that reads AFTER this write sees \"not due\" and skips its probe. (Two\n // racers that both read before either writes will each fire once — the\n // window is a handful of µs and the anonymous GitHub limit plus the\n // ETag-based 304 path keep the damage bounded.) If the probe crashes\n // the process, this placeholder also naturally satisfies the \"failed\n // probes still update the window\" invariant — the next run is bounded\n // by the interval.\n const nowIso = new Date(now).toISOString();\n const placeholder: UpdateCheckCache = {\n lastCheckedAt: nowIso,\n ...(cache?.latestTag !== undefined ? { latestTag: cache.latestTag } : {}),\n ...(cache?.etag !== undefined ? { etag: cache.etag } : {}),\n };\n await writeCache(placeholder).catch(() => {\n // Non-fatal: proceed with the probe, we'll try to write again on\n // completion. The throttle guarantee weakens here but bounded by\n // whatever caused the write to fail in the first place.\n });\n\n const previousEtag = cache?.etag;\n let entry: UpdateCheckCache = placeholder;\n try {\n const result = await fetchLatestReleaseConditional(previousEtag);\n if (result.status === 'not-modified') {\n // 304: server had no new body. Keep the latestTag we already know\n // about, and refresh the ETag only if the server happened to include\n // one on the 304.\n entry = {\n lastCheckedAt: nowIso,\n ...(cache?.latestTag !== undefined ? { latestTag: cache.latestTag } : {}),\n ...(result.etag !== undefined\n ? { etag: result.etag }\n : cache?.etag !== undefined\n ? { etag: cache.etag }\n : {}),\n };\n } else {\n entry = {\n lastCheckedAt: nowIso,\n latestTag: result.release.tag_name,\n ...(result.etag !== undefined ? { etag: result.etag } : {}),\n };\n }\n await writeCache(entry).catch(() => {\n // Placeholder already wrote above; ignore secondary write failure.\n });\n } catch {\n // Network / parse failure. Placeholder is already on disk, so the\n // throttle invariant holds — just skip the second write.\n }\n\n // If the probe failed, `entry` is still the placeholder — so the notice\n // uses the *previous* known latestTag from cache. That's fine: the\n // throttle window bounds staleness to 24h and semver doesn't move\n // backwards, so an out-of-date tag can only under-report a new release,\n // never falsely suggest a newer one than really exists.\n maybeEmitNotice(entry, env);\n return entry;\n}\n\nfunction maybeEmitNotice(entry: UpdateCheckCache, env: NodeJS.ProcessEnv): void {\n if (!entry.latestTag) return;\n // In the dev fallback VERSION (`0.0.0-dev`, from `src/version.ts` when no\n // build-time define is injected) every released tag looks \"newer\" and the\n // notice would fire on every `pnpm dev` run. Skip it instead — developers\n // running from source don't need an upgrade nag.\n if (VERSION.startsWith('0.0.0-dev')) return;\n const latest = versionFromTag(entry.latestTag);\n if (!latest) return;\n if (compareSemver(latest, VERSION) <= 0) return;\n // Respect NO_COLOR — CLAUDE.md documents it as honored across the CLI.\n const dim = env.NO_COLOR ? '' : '\\x1b[2m';\n const reset = env.NO_COLOR ? '' : '\\x1b[0m';\n // Notice goes to stderr so it never pollutes `--json` stdout.\n process.stderr.write(\n `\\n${dim}(aitcc ${latest} is available — run \\`aitcc upgrade\\` to install)${reset}\\n`,\n );\n}\n","import { defineCommand } from 'citty';\nimport { NetworkError, TossApiError } from '../api/http.js';\nimport { fetchConsoleMemberUserInfo } from '../api/me.js';\nimport { ExitCode } from '../exit.js';\nimport { exitAfterFlush } from '../flush.js';\nimport { readSession, sessionPathForDiagnostics } from '../session.js';\nimport { maybeCheckForUpdate } from '../update-check.js';\n\n// --json contract (consumed by agent-plugin):\n//\n// Success (session present + — for live mode — reachable):\n// { ok: true, authenticated: true, source: 'live'|'cache', user, capturedAt, ... }\n// Session missing:\n// { ok: true, authenticated: false } exit 10\n// Session expired (console rejected our cookies):\n// { ok: true, authenticated: false, reason: 'session-expired' } exit 10\n// Network failure talking to the console:\n// { ok: false, reason: 'network-error', message } exit 11\n// Any other API / unexpected error:\n// { ok: false, reason: 'api-error', message } exit 17\n//\n// The top-level `ok` is always present and indicates whether the command\n// ran cleanly; `authenticated` is only meaningful when `ok: true`.\n\n// Run the throttled background update check — but bound the wall-clock cost\n// so a slow network never delays the user's whoami output. 500 ms is enough\n// for a 304 (fast path after the first check) and for most 200s; a cold\n// probe that goes long just gets cancelled, and the next whoami within 24h\n// will not retry anyway (cache was written when the probe started).\n//\n// Skipped entirely when `--json` is set — machine consumers (agent-plugin)\n// should never see a \"new version available\" notice line interleaved with\n// their parsed output. The notice in update-check.ts already targets stderr\n// and checks `isTTY`, but belt-and-suspenders costs nothing here.\nasync function runBackgroundUpdateCheck(json: boolean): Promise<void> {\n if (json) return;\n const timeoutMs = 500;\n await Promise.race([\n maybeCheckForUpdate().catch(() => null),\n new Promise<null>((resolve) => {\n const t = setTimeout(() => resolve(null), timeoutMs);\n if (typeof t.unref === 'function') t.unref();\n }),\n ]);\n}\n\nexport const whoamiCommand = defineCommand({\n meta: {\n name: 'whoami',\n description: 'Show the currently authenticated user (live from the console API by default).',\n },\n args: {\n json: {\n type: 'boolean',\n description: 'Emit machine-readable JSON to stdout.',\n default: false,\n },\n offline: {\n type: 'boolean',\n description: 'Skip the live API call and read only the cached session summary.',\n default: false,\n },\n },\n async run({ args }) {\n const session = await readSession();\n\n if (!session) {\n if (args.json) {\n process.stdout.write(`${JSON.stringify({ ok: true, authenticated: false })}\\n`);\n } else {\n process.stderr.write('Not logged in. Run `aitcc login` to start a session.\\n');\n process.stderr.write(`Session file checked: ${sessionPathForDiagnostics()}\\n`);\n }\n return exitAfterFlush(ExitCode.NotAuthenticated);\n }\n\n if (args.offline) {\n if (args.json) {\n process.stdout.write(\n `${JSON.stringify({\n ok: true,\n authenticated: true,\n source: 'cache',\n user: session.user,\n capturedAt: session.capturedAt,\n })}\\n`,\n );\n return exitAfterFlush(ExitCode.Ok);\n }\n const label = session.user.displayName\n ? `${session.user.displayName} <${session.user.email}>`\n : session.user.email;\n process.stdout.write(`Logged in as ${label} (cached)\\n`);\n process.stdout.write(`Session captured: ${session.capturedAt}\\n`);\n await runBackgroundUpdateCheck(args.json);\n return exitAfterFlush(ExitCode.Ok);\n }\n\n try {\n const info = await fetchConsoleMemberUserInfo(session.cookies);\n if (args.json) {\n process.stdout.write(\n `${JSON.stringify({\n ok: true,\n authenticated: true,\n source: 'live',\n user: {\n id: String(info.id),\n bizUserNo: info.bizUserNo,\n name: info.name,\n email: info.email,\n role: info.role,\n },\n workspaces: info.workspaces.map((w) => ({\n workspaceId: w.workspaceId,\n workspaceName: w.workspaceName,\n role: w.role,\n })),\n capturedAt: session.capturedAt,\n })}\\n`,\n );\n return exitAfterFlush(ExitCode.Ok);\n }\n process.stdout.write(`Logged in as ${info.name} <${info.email}> (${info.role})\\n`);\n if (info.workspaces.length > 0) {\n process.stdout.write('Workspaces:\\n');\n for (const w of info.workspaces) {\n process.stdout.write(` - ${w.workspaceName} (id ${w.workspaceId}, ${w.role})\\n`);\n }\n }\n await runBackgroundUpdateCheck(args.json);\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n if (err instanceof TossApiError && err.isAuthError) {\n if (args.json) {\n process.stdout.write(\n `${JSON.stringify({\n ok: true,\n authenticated: false,\n reason: 'session-expired',\n errorCode: err.errorCode,\n })}\\n`,\n );\n } else {\n process.stderr.write('Session is no longer valid. Run `aitcc login` again.\\n');\n }\n return exitAfterFlush(ExitCode.NotAuthenticated);\n }\n if (err instanceof NetworkError) {\n // Network failures are surfaced as hard errors — we don't silently\n // fall back to the cache because agent-plugin callers branching on\n // exit code would miss the degradation. Users who explicitly want\n // the cached identity have `--offline` for that.\n if (args.json) {\n process.stdout.write(\n `${JSON.stringify({ ok: false, reason: 'network-error', message: err.message })}\\n`,\n );\n } else {\n process.stderr.write(\n `Network error reaching the console API: ${err.message}. Use \\`aitcc whoami --offline\\` for the cached identity.\\n`,\n );\n }\n return exitAfterFlush(ExitCode.NetworkError);\n }\n if (args.json) {\n process.stdout.write(\n `${JSON.stringify({ ok: false, reason: 'api-error', message: (err as Error).message })}\\n`,\n );\n } else {\n process.stderr.write(`Unexpected error: ${(err as Error).message}\\n`);\n }\n return exitAfterFlush(ExitCode.ApiError);\n }\n },\n});\n","import type { CdpCookie } from '../cdp.js';\nimport { type FetchLike, requestConsoleApi } from './http.js';\n\n// The list of workspaces a user can see is already baked into the\n// `members/me/user-info` response (see `./me.ts`), so we don't expose a\n// separate `GET /workspaces` wrapper — every caller that needs the list\n// goes through `fetchConsoleMemberUserInfo` and keys off `workspaces`.\n// This module only covers per-workspace detail and future write endpoints.\n\n// Note: the list endpoint (members/me/user-info) and the detail endpoint\n// disagree on field names — list uses workspaceId/workspaceName while\n// detail uses id/name. We normalise detail into the same vocabulary so\n// callers don't have to track which endpoint they came from.\nexport interface WorkspaceDetail {\n readonly workspaceId: number;\n readonly workspaceName: string;\n // The full shape of `/workspaces/:id` has many secondary fields (business\n // registration, verification, licence type, review state, etc) that may\n // grow over time. Stash everything beyond the normalised keys under\n // `extra` so commands like `workspace show --json` can dump the payload\n // without us having to type every field up-front.\n readonly extra?: Readonly<Record<string, unknown>>;\n}\n\nconst WORKSPACES_BASE = 'https://apps-in-toss.toss.im/console/api-public/v3/appsintossconsole';\n\nexport async function fetchWorkspaceDetail(\n workspaceId: number,\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<WorkspaceDetail> {\n // workspaceId is a number at compile time — `encodeURIComponent` on the\n // stringified form would be a no-op, so we inline the interpolation.\n const url = `${WORKSPACES_BASE}/workspaces/${workspaceId}`;\n const raw = await requestConsoleApi<Record<string, unknown>>({\n url,\n cookies,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n const id = raw.id;\n const name = raw.name;\n if (typeof id !== 'number' || !Number.isInteger(id) || id <= 0 || typeof name !== 'string') {\n throw new Error(`Unexpected workspace detail shape for id=${workspaceId}`);\n }\n const { id: _id, name: _name, ...extra } = raw;\n return { workspaceId: id, workspaceName: name, extra };\n}\n\n// Partner = the billing/payout entity that must be registered before\n// promotions/IAP can be used. `registered: false` + `approvalType: 'DRAFT'`\n// is the initial state we observed on a fresh workspace. `partner` is the\n// detail record once approval lands; keep it opaque until we see a live\n// example.\nexport interface WorkspacePartnerState {\n readonly registered: boolean;\n readonly approvalType: string | null;\n readonly rejectMessage: string | null;\n readonly partner: Readonly<Record<string, unknown>> | null;\n}\n\nexport async function fetchWorkspacePartner(\n workspaceId: number,\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<WorkspacePartnerState> {\n const url = `${WORKSPACES_BASE}/workspaces/${workspaceId}/partner`;\n const raw = await requestConsoleApi<Record<string, unknown>>({\n url,\n cookies,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n const registered = raw.registered;\n if (typeof registered !== 'boolean') {\n throw new Error(`Unexpected workspace partner shape for id=${workspaceId}`);\n }\n const approvalType = typeof raw.approvalType === 'string' ? raw.approvalType : null;\n const rejectMessage = typeof raw.rejectMessage === 'string' ? raw.rejectMessage : null;\n const partner =\n raw.partner && typeof raw.partner === 'object'\n ? (raw.partner as Readonly<Record<string, unknown>>)\n : null;\n return { registered, approvalType, rejectMessage, partner };\n}\n\n// `console-workspace-terms/:type/skip-permission` returns the terms a\n// workspace owner must agree to before the feature gated by `:type`\n// becomes available. The supported types are enumerated here verbatim\n// from the console UI — each one gates a distinct feature surface\n// (Toss login scopes, biz workspace eligibility, promotion-money,\n// in-app advertising, in-app purchase). Other values currently 404.\nexport const WORKSPACE_TERM_TYPES = [\n 'TOSS_LOGIN',\n 'BIZ_WORKSPACE',\n 'TOSS_PROMOTION_MONEY',\n 'IAA',\n 'IAP',\n] as const;\nexport type WorkspaceTermType = (typeof WORKSPACE_TERM_TYPES)[number];\n\nexport interface WorkspaceTerm {\n readonly required: boolean;\n readonly termsId: number;\n readonly revisionId: number;\n readonly title: string;\n readonly contentsUrl: string;\n readonly actionType: string;\n readonly isAgreed: boolean;\n readonly isOneTimeConsent: boolean;\n}\n\n// Segments are workspace-scoped (not per-app). The category axis appears\n// in the UI as tabs like \"생성된 세그먼트\"/\"추천 세그먼트\"; the server\n// accepts any string and just filters whatever bucket it matches (we've\n// observed all common category strings return an empty list on a fresh\n// workspace rather than 400-ing). Default bucket matches the UI.\nconst DEFAULT_SEGMENT_CATEGORY = '생성된 세그먼트';\n\nexport interface FetchWorkspaceSegmentsParams {\n readonly workspaceId: number;\n readonly category?: string;\n readonly search?: string;\n readonly page?: number;\n}\n\nexport interface WorkspaceSegmentsPage {\n readonly contents: readonly Readonly<Record<string, unknown>>[];\n readonly totalPage: number;\n readonly currentPage: number;\n}\n\nexport async function fetchWorkspaceSegments(\n params: FetchWorkspaceSegmentsParams,\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<WorkspaceSegmentsPage> {\n const page = params.page ?? 0;\n const qs = new URLSearchParams();\n qs.set('category', params.category ?? DEFAULT_SEGMENT_CATEGORY);\n qs.set('search', params.search ?? '');\n qs.set('page', String(page));\n const url = `${WORKSPACES_BASE}/workspaces/${params.workspaceId}/segments/list?${qs.toString()}`;\n const raw = await requestConsoleApi<unknown>({\n url,\n cookies,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) {\n throw new Error(`Unexpected segments shape for workspace=${params.workspaceId}`);\n }\n const data = raw as Record<string, unknown>;\n const contents = Array.isArray(data.contents) ? data.contents : [];\n return {\n contents: contents.map((c) =>\n c && typeof c === 'object' ? (c as Record<string, unknown>) : {},\n ),\n totalPage: typeof data.totalPage === 'number' ? data.totalPage : 0,\n currentPage: typeof data.currentPage === 'number' ? data.currentPage : page,\n };\n}\n\nexport async function fetchWorkspaceTerms(\n workspaceId: number,\n type: WorkspaceTermType,\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<readonly WorkspaceTerm[]> {\n const url = `${WORKSPACES_BASE}/workspaces/${workspaceId}/console-workspace-terms/${type}/skip-permission`;\n const raw = await requestConsoleApi<unknown>({\n url,\n cookies,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n if (!Array.isArray(raw)) {\n throw new Error(`Unexpected workspace terms shape for type=${type}`);\n }\n return raw.map((entry, i): WorkspaceTerm => {\n if (!entry || typeof entry !== 'object') {\n throw new Error(`Unexpected workspace terms entry at index ${i} for type=${type}`);\n }\n const e = entry as Record<string, unknown>;\n // The console UI currently always sends all fields, and Zod-level\n // strict validation would break if Toss adds an enum value. Trust\n // the types we rely on and pass through with a narrow normalisation.\n return {\n required: Boolean(e.required),\n termsId: typeof e.termsId === 'number' ? e.termsId : 0,\n revisionId: typeof e.revisionId === 'number' ? e.revisionId : 0,\n title: typeof e.title === 'string' ? e.title : '',\n contentsUrl: typeof e.contentsUrl === 'string' ? e.contentsUrl : '',\n actionType: typeof e.actionType === 'string' ? e.actionType : '',\n isAgreed: Boolean(e.isAgreed),\n isOneTimeConsent: Boolean(e.isOneTimeConsent),\n };\n });\n}\n\n// `(termsId, revisionId)` pair the agree endpoint expects per term. The\n// shape is intentionally loose — `WorkspaceTerm` carries a lot more (title,\n// actionType, isAgreed) that the server has no use for on submit, so this\n// helper stays narrow.\nexport interface WorkspaceTermAgreement {\n readonly termsId: number;\n readonly revisionId: number;\n}\n\n/**\n * Persist agreement for one-or-more workspace terms. The endpoint takes a\n * single `agreedList` regardless of which bucket the terms came from — the\n * type tag is implicit in the (termsId, revisionId) pairs.\n *\n * Captured behaviour (2026-05-08, ws=36577):\n * - `POST /workspaces/<wid>/console-workspace-terms` with body\n * `{\"agreedList\":[{\"termsId\": <int>, \"revisionId\": <int>}, ...]}`\n * - Response on success: `{\"resultType\":\"SUCCESS\",\"success\":{}}` — no\n * useful payload. We resolve `void`.\n * - Re-submitting an already-agreed term returns `errorCode: 500`\n * (Internal Server Error). The server is NOT idempotent, so callers\n * must filter to `isAgreed === false` before invoking.\n * - Empty `agreedList` returns SUCCESS (no-op), but we throw client-side\n * before sending — round-tripping a no-op request is wasted.\n *\n * Failure surfaces through `TossApiError` like every other write helper.\n */\nexport async function agreeWorkspaceTerms(\n workspaceId: number,\n terms: readonly WorkspaceTermAgreement[],\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<void> {\n if (terms.length === 0) {\n throw new Error('agreeWorkspaceTerms requires at least one term');\n }\n const url = `${WORKSPACES_BASE}/workspaces/${workspaceId}/console-workspace-terms`;\n await requestConsoleApi<unknown>({\n method: 'POST',\n url,\n cookies,\n body: { agreedList: terms.map(({ termsId, revisionId }) => ({ termsId, revisionId })) },\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n}\n","import { defineCommand } from 'citty';\nimport { NetworkError, TossApiError } from '../api/http.js';\nimport { fetchConsoleMemberUserInfo } from '../api/me.js';\nimport {\n agreeWorkspaceTerms,\n fetchWorkspaceDetail,\n fetchWorkspacePartner,\n fetchWorkspaceSegments,\n fetchWorkspaceTerms,\n WORKSPACE_TERM_TYPES,\n type WorkspaceTerm,\n type WorkspaceTermType,\n} from '../api/workspaces.js';\nimport { ExitCode } from '../exit.js';\nimport { exitAfterFlush } from '../flush.js';\nimport { readSession, setCurrentWorkspaceId } from '../session.js';\nimport {\n emitFailureFromError,\n emitJson,\n emitNotAuthenticated,\n parsePositiveInt,\n printContextHeader,\n resolveWorkspaceContext,\n} from './_shared.js';\n\n// --json contract (consumed by agent-plugin):\n//\n// workspace ls:\n// { ok: true, workspaces: [{workspaceId, workspaceName, role, current}] }\n// ^--- matches currentWorkspaceId\n// workspace use <id>:\n// { ok: true, workspaceId, workspaceName } exit 0\n// { ok: false, reason: 'not-found', workspaceId } exit 2\n// { ok: false, reason: 'invalid-id', message } exit 2\n// workspace show [--workspace <id>]:\n// { ok: true, workspaceId, workspaceName, extra } exit 0\n// { ok: false, reason: 'no-workspace-selected' } exit 2\n// { ok: false, reason: 'invalid-id', message } exit 2\n// workspace partner [--workspace <id>]:\n// { ok: true, workspaceId, registered, approvalType,\n// rejectMessage, partner } exit 0\n// { ok: false, reason: 'no-workspace-selected' } exit 2\n// { ok: false, reason: 'invalid-id', message } exit 2\n// workspace terms [--type TYPE] [--workspace <id>]:\n// { ok: true, workspaceId, type, terms: (WorkspaceTerm & {blocks: string[]})[] } exit 0 (single type)\n// { ok: true, workspaceId, byType: { TYPE: (WorkspaceTerm & {blocks: string[]})[] } } exit 0 (default — every bucket)\n// { ok: false, reason: 'invalid-type', allowed: TYPES[] } exit 2\n// { ok: false, reason: 'no-workspace-selected' } exit 2\n// { ok: false, reason: 'invalid-id', message } exit 2\n//\n// `blocks` is a per-type list of feature surfaces that become unavailable\n// while the bucket is un-agreed. The same list appears on every term in\n// the bucket — it's a property of the bucket, not the individual term.\n//\n// workspace terms agree <type> [--workspace <id>]:\n// workspace terms agree --all [--workspace <id>]:\n// { ok: true, partial: false, workspaceId, agreed: AgreedTerm[],\n// unchanged: AgreedTerm[], failed: [] } exit 0 (full success or all-already-agreed)\n// { ok: false, partial: true, workspaceId, agreed: AgreedTerm[],\n// unchanged: AgreedTerm[],\n// failed: { termsId, revisionId, type, message }[] } exit 1 (some buckets succeeded, others failed)\n// exit 17 (full transport-level failure handled by emitFailureFromError)\n// { ok: false, reason: 'argument-required', message } exit 2 (no <type> and no --all)\n// { ok: false, reason: 'mutually-exclusive', message } exit 2 (<type> and --all both given)\n// { ok: false, reason: 'unknown-term-type', given, allowed: TYPES[] } exit 2 (positional <type> not in enum)\n// { ok: false, reason: 'no-workspace-selected' } exit 2\n// { ok: false, reason: 'invalid-id', message } exit 2\n//\n// `AgreedTerm = { termsId, revisionId, type, title }`. `agreed` lists what\n// this run flipped from pending → agreed; `unchanged` lists terms that\n// were already agreed at fetch time and got skipped (idempotent path).\n// `failed` lists transport/server failures per term batch — populated only\n// when `partial === true`. `ok` is a single-bool decision flag for\n// agent-plugin (`ok && !failed.length`-style branching is unnecessary):\n// `ok: true` means every requested bucket either succeeded or was already\n// agreed; `ok: false, partial: true` means at least one bucket failed and\n// the caller should inspect `failed`.\n//\n// workspace segments ls [--category <cat>] [--search <text>] [--page N] [--workspace <id>]:\n// { ok: true, workspaceId, category, segments: [...], totalPage, currentPage } exit 0\n// { ok: false, reason: 'invalid-page', message } exit 2\n// { ok: false, reason: 'no-workspace-selected' } exit 2\n// { ok: false, reason: 'invalid-id', message } exit 2\n//\n// Every workspace subcommand inherits the standard auth failure modes from\n// whoami: { ok: true, authenticated: false } exit 10, network-error exit 11,\n// api-error exit 17. All JSON writes go through the shared `emitJson` so the\n// single-line-with-trailing-newline invariant is enforced in one place.\n\n// Formatting helper for the plain-text `show` output. `--json` is the\n// structured consumption path; this is a crude fallback so a human can\n// skim the response at a glance. Objects/arrays collapse to a single\n// JSON line on purpose — nested structures are rare in the detail\n// response and unreadable in any form without real tabular formatting.\nfunction formatScalar(v: unknown): string {\n if (v === null) return 'null';\n if (typeof v === 'string' || typeof v === 'number' || typeof v === 'boolean') return String(v);\n return JSON.stringify(v);\n}\n\nconst lsCommand = defineCommand({\n meta: {\n name: 'ls',\n description: 'List workspaces the current user has access to.',\n },\n args: {\n json: { type: 'boolean', description: 'Emit machine-readable JSON to stdout.', default: false },\n },\n async run({ args }) {\n const session = await readSession();\n if (!session) {\n emitNotAuthenticated(args.json);\n return exitAfterFlush(ExitCode.NotAuthenticated);\n }\n try {\n const info = await fetchConsoleMemberUserInfo(session.cookies);\n const current = session.currentWorkspaceId;\n if (args.json) {\n const workspaces = info.workspaces.map((w) => ({\n workspaceId: w.workspaceId,\n workspaceName: w.workspaceName,\n role: w.role,\n current: w.workspaceId === current,\n }));\n emitJson({ ok: true, workspaces });\n return exitAfterFlush(ExitCode.Ok);\n }\n if (info.workspaces.length === 0) {\n process.stdout.write('No workspaces.\\n');\n return exitAfterFlush(ExitCode.Ok);\n }\n for (const w of info.workspaces) {\n const marker = w.workspaceId === current ? '* ' : ' ';\n process.stdout.write(`${marker}${w.workspaceId} ${w.workspaceName} (${w.role})\\n`);\n }\n if (current === undefined) {\n process.stderr.write('No workspace selected. Run `aitcc workspace use <id>`.\\n');\n }\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\nconst useCommand = defineCommand({\n meta: {\n name: 'use',\n description: 'Select the current workspace by ID. Subsequent commands use this.',\n },\n args: {\n id: { type: 'positional', description: 'Workspace ID', required: true },\n json: { type: 'boolean', description: 'Emit machine-readable JSON to stdout.', default: false },\n },\n async run({ args }) {\n const raw = String(args.id);\n const parsed = parsePositiveInt(raw);\n if (parsed === null) {\n const message = `workspace id must be a positive integer (got ${raw})`;\n if (args.json) {\n emitJson({ ok: false, reason: 'invalid-id', message });\n } else {\n process.stderr.write(`${message}\\n`);\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n\n const session = await readSession();\n if (!session) {\n emitNotAuthenticated(args.json);\n return exitAfterFlush(ExitCode.NotAuthenticated);\n }\n\n // Validate against the user's actual workspace list before writing the\n // selection. `members/me/user-info` is the live list, not the stored\n // one, so a workspace added after login is visible here. Only the\n // detail endpoint (not called here) could still 403 after this check.\n try {\n const info = await fetchConsoleMemberUserInfo(session.cookies);\n const match = info.workspaces.find((w) => w.workspaceId === parsed);\n if (!match) {\n if (args.json) {\n emitJson({ ok: false, reason: 'not-found', workspaceId: parsed });\n } else {\n process.stderr.write(\n `Workspace ${parsed} is not accessible from this account. Run \\`aitcc workspace ls\\` to see available workspaces.\\n`,\n );\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n // `setCurrentWorkspaceId` returns null only if the session disappeared\n // between our `readSession` above and here (e.g. concurrent logout).\n // Surface that as \"not logged in\" for consistency with other commands\n // instead of silently pretending the write landed. For v1 sessions\n // this is a double-read (readSession migrates, then this helper reads\n // again before writing) — benign, and preferable to threading the\n // already-loaded session through a new parameter just to save one IO.\n const updated = await setCurrentWorkspaceId(parsed);\n if (updated === null) {\n emitNotAuthenticated(args.json);\n return exitAfterFlush(ExitCode.NotAuthenticated);\n }\n if (args.json) {\n emitJson({\n ok: true,\n workspaceId: match.workspaceId,\n workspaceName: match.workspaceName,\n });\n } else {\n process.stdout.write(`Using workspace ${match.workspaceId} (${match.workspaceName}).\\n`);\n }\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\nconst showCommand = defineCommand({\n meta: {\n name: 'show',\n description: 'Show details of the selected workspace (or the one passed with --workspace).',\n },\n args: {\n workspace: {\n type: 'string',\n description: 'Workspace ID to inspect. Defaults to the selected workspace.',\n },\n json: { type: 'boolean', description: 'Emit machine-readable JSON to stdout.', default: false },\n },\n async run({ args }) {\n const ctx = await resolveWorkspaceContext(args);\n if (!ctx) return;\n const { session, workspaceId } = ctx;\n printContextHeader(ctx, { json: args.json });\n\n try {\n const detail = await fetchWorkspaceDetail(workspaceId, session.cookies);\n if (args.json) {\n emitJson({\n ok: true,\n workspaceId: detail.workspaceId,\n workspaceName: detail.workspaceName,\n extra: detail.extra ?? {},\n });\n return exitAfterFlush(ExitCode.Ok);\n }\n process.stdout.write(`Workspace ${detail.workspaceId}: ${detail.workspaceName}\\n`);\n if (detail.extra) {\n for (const [k, v] of Object.entries(detail.extra)) {\n process.stdout.write(` ${k}: ${formatScalar(v)}\\n`);\n }\n }\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\nconst partnerCommand = defineCommand({\n meta: {\n name: 'partner',\n description: 'Show the partner (billing/payout) registration state for the selected workspace.',\n },\n args: {\n workspace: {\n type: 'string',\n description: 'Workspace ID to inspect. Defaults to the selected workspace.',\n },\n json: { type: 'boolean', description: 'Emit machine-readable JSON to stdout.', default: false },\n },\n async run({ args }) {\n const ctx = await resolveWorkspaceContext(args);\n if (!ctx) return;\n const { session, workspaceId } = ctx;\n printContextHeader(ctx, { json: args.json });\n\n try {\n const state = await fetchWorkspacePartner(workspaceId, session.cookies);\n if (args.json) {\n emitJson({\n ok: true,\n workspaceId,\n registered: state.registered,\n approvalType: state.approvalType,\n rejectMessage: state.rejectMessage,\n partner: state.partner,\n });\n return exitAfterFlush(ExitCode.Ok);\n }\n process.stdout.write(`Workspace ${workspaceId} partner:\\n`);\n process.stdout.write(` registered: ${state.registered}\\n`);\n process.stdout.write(` approvalType: ${state.approvalType ?? 'null'}\\n`);\n if (state.rejectMessage) {\n process.stdout.write(` rejectMessage: ${state.rejectMessage}\\n`);\n }\n if (state.partner) {\n process.stdout.write(' partner:\\n');\n for (const [k, v] of Object.entries(state.partner)) {\n process.stdout.write(` ${k}: ${formatScalar(v)}\\n`);\n }\n }\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\nfunction formatTermLines(term: WorkspaceTerm): string {\n // One agreement per line in the plain-text rendering; the title + a\n // [agreed]/[pending] tag is the useful signal for a human operator.\n // Keep the contentsUrl on a second indented line so ops can Ctrl-click\n // to review it directly without switching to --json.\n const tag = term.isAgreed ? '[agreed]' : '[pending]';\n const req = term.required ? ' required' : '';\n return ` ${tag}${req} ${term.title}\\n ${term.contentsUrl}\\n`;\n}\n\n// What each term-bucket gates if it is left un-agreed. The pairings come\n// from docs/api/_error-codes.md \"Auth / 약관 family\" (4037/4039/4040/4099/5001\n// rows) cross-referenced with docs/api/workspaces.md `<type>` descriptions\n// — server is the source of truth for the term enum, this table only\n// documents the consumer-side feature surface each enum value gates.\n//\n// Surfaced both in plain-text output (`blocks if missing: …`) and in the\n// `--json` payload (`blocks: string[]`). Order within an entry mirrors the\n// spec PR description so dog-food output stays diffable. Strings are\n// human-readable feature names, not necessarily existing CLI command names\n// (some gated features are still on the roadmap).\nexport const TERM_BLOCKS: Record<WorkspaceTermType, readonly string[]> = {\n TOSS_LOGIN: ['toss login scope for mini-apps', 'app register (login configured apps)'],\n BIZ_WORKSPACE: ['app register', 'app deploy', 'workspace-level admin'],\n TOSS_PROMOTION_MONEY: ['promotion money campaign management'],\n IAA: ['ad campaign management'],\n IAP: ['iap product register', 'iap config'],\n};\n\nexport function blocksFor(type: WorkspaceTermType): readonly string[] {\n return TERM_BLOCKS[type] ?? [];\n}\n\nexport function formatBlocksHint(type: WorkspaceTermType, agreed: boolean): string {\n // Skip the hint entirely when we have no mapping for this type — keeps\n // the output clean if the server adds a new bucket before we update\n // the table. The spec calls this the \"simpler\" path over emitting\n // `blocks if missing: -`.\n const blocks = blocksFor(type);\n if (blocks.length === 0) return '';\n // Only highlight when the term is pending — agreed terms still get the\n // line for context but in default styling. NO_COLOR + non-TTY both\n // disable the escape; isTTY gate keeps ANSI out of pipes.\n const useColor = !agreed && process.stdout.isTTY && !process.env.NO_COLOR;\n const yellow = useColor ? '\\x1b[33m' : '';\n const reset = useColor ? '\\x1b[0m' : '';\n return ` ${yellow}blocks if missing: ${blocks.join(', ')}${reset}\\n`;\n}\n\nconst termsShowCommand = defineCommand({\n meta: {\n name: 'show',\n description:\n 'Show the console terms-of-agreement state that gate workspace-level features (Toss login, IAP, IAA, biz workspace, promotion money).',\n },\n args: {\n type: {\n type: 'string',\n description: `Term bucket to inspect: ${WORKSPACE_TERM_TYPES.join(' | ')}. Omit to query every bucket.`,\n },\n workspace: {\n type: 'string',\n description: 'Workspace ID to inspect. Defaults to the selected workspace.',\n },\n json: { type: 'boolean', description: 'Emit machine-readable JSON to stdout.', default: false },\n },\n async run({ args }) {\n const ctx = await resolveWorkspaceContext(args);\n if (!ctx) return;\n const { session, workspaceId } = ctx;\n\n const typesToQuery: readonly WorkspaceTermType[] = (() => {\n if (!args.type) return WORKSPACE_TERM_TYPES;\n const raw = String(args.type).toUpperCase();\n if ((WORKSPACE_TERM_TYPES as readonly string[]).includes(raw)) {\n return [raw as WorkspaceTermType];\n }\n return [];\n })();\n if (typesToQuery.length === 0) {\n const message = `--type must be one of: ${WORKSPACE_TERM_TYPES.join(', ')}`;\n if (args.json) {\n emitJson({ ok: false, reason: 'invalid-type', allowed: [...WORKSPACE_TERM_TYPES] });\n } else {\n process.stderr.write(`${message}\\n`);\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n printContextHeader(ctx, { json: args.json });\n\n try {\n // Single-type path keeps the JSON payload flat; --all (or the\n // default) groups results by type so consumers don't have to call\n // five times. Fire them in parallel — each is an independent GET\n // and the server has no cross-bucket rate-limit we've observed.\n const results = await Promise.all(\n typesToQuery.map(\n async (t) => [t, await fetchWorkspaceTerms(workspaceId, t, session.cookies)] as const,\n ),\n );\n\n // Attach the static `blocks` feature-gate list to every term. Same\n // list per bucket since it's a property of the term-type, not the\n // individual term entry — duplicating it on each row keeps the\n // JSON contract uniform (consumers don't have to look up the\n // bucket key separately).\n const enrich = (\n type: WorkspaceTermType,\n terms: readonly WorkspaceTerm[],\n ): readonly (WorkspaceTerm & { readonly blocks: readonly string[] })[] => {\n const blocks = blocksFor(type);\n return terms.map((t) => ({ ...t, blocks }));\n };\n\n if (typesToQuery.length === 1) {\n const [type, terms] = results[0] as readonly [WorkspaceTermType, readonly WorkspaceTerm[]];\n if (args.json) {\n emitJson({ ok: true, workspaceId, type, terms: enrich(type, terms) });\n return exitAfterFlush(ExitCode.Ok);\n }\n process.stdout.write(`Workspace ${workspaceId} terms (${type}):\\n`);\n if (terms.length === 0) {\n process.stdout.write(' (no terms required)\\n');\n } else {\n for (const t of terms) {\n process.stdout.write(formatTermLines(t));\n process.stdout.write(formatBlocksHint(type, t.isAgreed));\n }\n }\n return exitAfterFlush(ExitCode.Ok);\n }\n\n // --all path\n const byType: Record<\n string,\n readonly (WorkspaceTerm & { readonly blocks: readonly string[] })[]\n > = {};\n for (const [t, terms] of results) byType[t] = enrich(t, terms);\n if (args.json) {\n emitJson({ ok: true, workspaceId, byType });\n return exitAfterFlush(ExitCode.Ok);\n }\n for (const [type, terms] of results) {\n process.stdout.write(`\\n[${type}]\\n`);\n if (terms.length === 0) {\n process.stdout.write(' (no terms required)\\n');\n } else {\n for (const t of terms) {\n process.stdout.write(formatTermLines(t));\n process.stdout.write(formatBlocksHint(type, t.isAgreed));\n }\n }\n }\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\n// Per-term agreement record surfaced in the `agree` JSON payload. We carry\n// `(termsId, revisionId)` so consumers can correlate with `terms show`\n// output, plus `type` (bucket) + `title` so dog-food output and human\n// messages don't have to look the title back up. The shape is the same\n// for `agreed` and `unchanged` buckets — only the bucket says whether this\n// run flipped the state.\nexport interface AgreedTerm {\n readonly termsId: number;\n readonly revisionId: number;\n readonly type: WorkspaceTermType;\n readonly title: string;\n}\n\nexport interface FailedTerm {\n readonly termsId: number;\n readonly revisionId: number;\n readonly type: WorkspaceTermType;\n readonly message: string;\n}\n\nexport function describeAgreeError(err: unknown): string {\n // `TossApiError.message` already embeds errorCode + reason + HTTP status\n // (`\"Toss API error <code>: <reason> (HTTP <status>)\"`), so we don't tack\n // on a redundant `(errorCode: ...)` suffix.\n if (err instanceof TossApiError) return err.message;\n if (err instanceof NetworkError) return `network error: ${err.message}`;\n if (err instanceof Error) return err.message;\n return String(err);\n}\n\n// Pure validation of the (positional, --all) arg pair. Extracted so the\n// command-level mutually-exclusive / argument-required / unknown-term-type\n// branches can be unit-tested without invoking the full citty `run`.\n// Case-insensitive on the positional so `iap` and `IAP` both work.\nexport type AgreeArgsValidation =\n | { readonly ok: true; readonly types: readonly WorkspaceTermType[] }\n | { readonly ok: false; readonly reason: 'argument-required'; readonly message: string }\n | { readonly ok: false; readonly reason: 'mutually-exclusive'; readonly message: string }\n | {\n readonly ok: false;\n readonly reason: 'unknown-term-type';\n readonly given: string;\n readonly allowed: readonly WorkspaceTermType[];\n // `message` here is for stderr only — the `--json` emit path intentionally\n // omits it and surfaces `given` + `allowed` instead so the agent-plugin\n // can render its own message without parsing prose.\n readonly message: string;\n };\n\nexport function validateAgreeArgs(input: {\n positional: string;\n all: boolean;\n}): AgreeArgsValidation {\n const hasPositional = input.positional.length > 0;\n if (!hasPositional && !input.all) {\n return {\n ok: false,\n reason: 'argument-required',\n message: 'Specify a term bucket (e.g. `aitcc workspace terms agree IAP`) or pass --all.',\n };\n }\n if (hasPositional && input.all) {\n return {\n ok: false,\n reason: 'mutually-exclusive',\n message: '<type> and --all are mutually exclusive — pass one or the other, not both.',\n };\n }\n if (input.all) {\n return { ok: true, types: WORKSPACE_TERM_TYPES };\n }\n const upper = input.positional.toUpperCase();\n if (!(WORKSPACE_TERM_TYPES as readonly string[]).includes(upper)) {\n return {\n ok: false,\n reason: 'unknown-term-type',\n given: input.positional,\n allowed: [...WORKSPACE_TERM_TYPES],\n message: `Unknown term bucket: ${input.positional}. Allowed: ${WORKSPACE_TERM_TYPES.join(', ')}.`,\n };\n }\n return { ok: true, types: [upper as WorkspaceTermType] };\n}\n\n// Pure orchestration: given the fetched buckets and an injectable agree\n// function, partition terms into agreed/unchanged/failed. Server is NOT\n// idempotent — already-agreed terms are dropped on the client side, and\n// each bucket's agree call is independent (one bucket failing does not\n// block the others).\nexport interface BucketSnapshot {\n readonly type: WorkspaceTermType;\n readonly terms: readonly WorkspaceTerm[];\n}\n\nexport interface AgreeOutcome {\n readonly agreed: readonly AgreedTerm[];\n readonly unchanged: readonly AgreedTerm[];\n readonly failed: readonly FailedTerm[];\n}\n\nexport async function processAgreeBuckets(\n buckets: readonly BucketSnapshot[],\n submit: (\n type: WorkspaceTermType,\n pending: readonly { termsId: number; revisionId: number }[],\n ) => Promise<void>,\n): Promise<AgreeOutcome> {\n const agreed: AgreedTerm[] = [];\n const unchanged: AgreedTerm[] = [];\n const failed: FailedTerm[] = [];\n\n for (const { type, terms } of buckets) {\n for (const t of terms) {\n if (t.isAgreed) {\n unchanged.push({ termsId: t.termsId, revisionId: t.revisionId, type, title: t.title });\n }\n }\n const pending = terms.filter((t) => !t.isAgreed);\n if (pending.length === 0) continue;\n try {\n await submit(\n type,\n pending.map((t) => ({ termsId: t.termsId, revisionId: t.revisionId })),\n );\n for (const t of pending) {\n agreed.push({ termsId: t.termsId, revisionId: t.revisionId, type, title: t.title });\n }\n } catch (err) {\n const message = describeAgreeError(err);\n for (const t of pending) {\n failed.push({ termsId: t.termsId, revisionId: t.revisionId, type, message });\n }\n }\n }\n return { agreed, unchanged, failed };\n}\n\nconst termsAgreeCommand = defineCommand({\n meta: {\n name: 'agree',\n description:\n 'Agree to workspace-level terms. Pass a single bucket as positional argument, or --all to agree to every pending bucket. Already-agreed terms are skipped (idempotent).',\n },\n args: {\n type: {\n type: 'positional',\n description: `Term bucket to agree to (positional, NOT --type): ${WORKSPACE_TERM_TYPES.join(' | ')}. Asymmetric with \\`terms show --type ...\\` because agree is a one-shot intent — pass the bucket name directly or --all.`,\n required: false,\n },\n all: {\n type: 'boolean',\n description:\n 'Agree to every pending bucket. Mutually exclusive with the positional argument.',\n default: false,\n },\n workspace: {\n type: 'string',\n description: 'Workspace ID to inspect. Defaults to the selected workspace.',\n },\n json: { type: 'boolean', description: 'Emit machine-readable JSON to stdout.', default: false },\n },\n async run({ args }) {\n const validation = validateAgreeArgs({\n positional: args.type !== undefined ? String(args.type) : '',\n all: Boolean(args.all),\n });\n if (!validation.ok) {\n if (args.json) {\n if (validation.reason === 'unknown-term-type') {\n emitJson({\n ok: false,\n reason: 'unknown-term-type',\n given: validation.given,\n allowed: validation.allowed,\n });\n } else {\n emitJson({ ok: false, reason: validation.reason, message: validation.message });\n }\n } else {\n process.stderr.write(`${validation.message}\\n`);\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n\n const ctx = await resolveWorkspaceContext(args);\n if (!ctx) return;\n const { session, workspaceId } = ctx;\n printContextHeader(ctx, { json: args.json });\n\n try {\n // Fetch every requested bucket in parallel — same pattern as `show`.\n // The agree endpoint is per-bucket but the GET isn't, and we need\n // the (termsId, revisionId) + isAgreed snapshot before deciding\n // what to submit (server is NOT idempotent — re-submitting an\n // already-agreed term returns 500).\n const buckets: readonly BucketSnapshot[] = await Promise.all(\n validation.types.map(async (type) => ({\n type,\n terms: await fetchWorkspaceTerms(workspaceId, type, session.cookies),\n })),\n );\n\n const { agreed, unchanged, failed } = await processAgreeBuckets(\n buckets,\n async (_type, pending) => {\n await agreeWorkspaceTerms(workspaceId, pending, session.cookies);\n },\n );\n\n const partial = failed.length > 0;\n if (args.json) {\n emitJson({\n ok: !partial,\n partial,\n workspaceId,\n agreed,\n unchanged,\n failed,\n });\n } else if (agreed.length === 0 && failed.length === 0) {\n process.stdout.write(\n unchanged.length === 0\n ? '(no terms to agree to)\\n'\n : `Already agreed: ${unchanged.length} term(s) — nothing to do.\\n`,\n );\n } else {\n if (agreed.length > 0) {\n // Group by type for a tighter rendering.\n const byType = new Map<WorkspaceTermType, AgreedTerm[]>();\n for (const a of agreed) {\n const arr = byType.get(a.type) ?? [];\n arr.push(a);\n byType.set(a.type, arr);\n }\n for (const [type, items] of byType) {\n const blocks = blocksFor(type);\n const blocksHint = blocks.length > 0 ? ` (unblocks: ${blocks.join(', ')})` : '';\n process.stdout.write(`✓ Agreed to ${items.length} term(s) in ${type}${blocksHint}\\n`);\n for (const a of items) {\n process.stdout.write(` - ${a.title}\\n`);\n }\n }\n }\n if (unchanged.length > 0) {\n process.stdout.write(`(skipped ${unchanged.length} already-agreed term(s))\\n`);\n }\n if (failed.length > 0) {\n process.stderr.write(`✗ ${failed.length} term(s) failed:\\n`);\n for (const f of failed) {\n process.stderr.write(` - [${f.type}] termsId=${f.termsId}: ${f.message}\\n`);\n }\n }\n }\n return exitAfterFlush(partial ? ExitCode.Generic : ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\nconst termsCommand = defineCommand({\n meta: {\n name: 'terms',\n description:\n 'Show or agree to console terms-of-agreement that gate workspace-level features (Toss login, IAP, IAA, biz workspace, promotion money).',\n },\n // citty's `findSubCommandIndex` walks the parent's argsDef to decide\n // whether `--flag VALUE` consumes one or two raw args. If the parent\n // doesn't declare a string-typed flag, the VALUE half gets read as a\n // positional and citty treats it as the subcommand name → \"Unknown\n // command 36577\". Mirror the `show` subcommand's value-flags here so\n // `aitcc workspace terms --workspace 36577 --type IAP` still routes\n // bare → show via `default` (the previous, no-subtree behaviour). These\n // declarations are PURE ROUTING SCAFFOLDING — the parent never reads\n // them; `runCommand` re-parses raw args inside the resolved subcommand.\n // Importantly, `--type` here does NOT mean `terms agree --type IAP` is\n // valid: agree takes the bucket as a positional. The flag name overlap\n // is incidental — see `termsAgreeCommand.args.type` for the real shape.\n args: {\n type: {\n type: 'string',\n description: 'Forwarded to `show` (routing only — see `agree --help`).',\n },\n workspace: { type: 'string', description: 'Forwarded to the resolved subcommand.' },\n json: { type: 'boolean', description: 'Forwarded to the resolved subcommand.', default: false },\n },\n subCommands: {\n show: termsShowCommand,\n agree: termsAgreeCommand,\n },\n // Bare `aitcc workspace terms` (no subcommand) routes to `show` so the\n // existing read-only behaviour is preserved.\n default: 'show',\n});\n\nconst segmentsLsCommand = defineCommand({\n meta: {\n name: 'ls',\n description: 'List user segments in the selected workspace (the 세그먼트 menu).',\n },\n args: {\n workspace: {\n type: 'string',\n description: 'Workspace ID. Defaults to the selected workspace.',\n },\n category: {\n type: 'string',\n description: 'Category bucket (tab). Defaults to \"생성된 세그먼트\" — the UI\\'s initial tab.',\n },\n search: { type: 'string', description: 'Name-contains filter. Empty matches everything.' },\n page: { type: 'string', description: 'Page number (0-indexed).', default: '0' },\n json: { type: 'boolean', description: 'Emit machine-readable JSON to stdout.', default: false },\n },\n async run({ args }) {\n const ctx = await resolveWorkspaceContext(args);\n if (!ctx) return;\n const { session, workspaceId } = ctx;\n\n const pageRaw = String(args.page);\n const pageNum = Number(pageRaw);\n if (!Number.isFinite(pageNum) || !Number.isInteger(pageNum) || pageNum < 0) {\n const message = `--page must be a non-negative integer (got ${JSON.stringify(pageRaw)})`;\n if (args.json) emitJson({ ok: false, reason: 'invalid-page', message });\n else process.stderr.write(`${message}\\n`);\n return exitAfterFlush(ExitCode.Usage);\n }\n printContextHeader(ctx, { json: args.json });\n\n try {\n const page = await fetchWorkspaceSegments(\n {\n workspaceId,\n ...(args.category !== undefined ? { category: String(args.category) } : {}),\n ...(args.search !== undefined ? { search: String(args.search) } : {}),\n page: pageNum,\n },\n session.cookies,\n );\n const category = args.category !== undefined ? String(args.category) : '생성된 세그먼트';\n if (args.json) {\n emitJson({\n ok: true,\n workspaceId,\n category,\n segments: page.contents,\n totalPage: page.totalPage,\n currentPage: page.currentPage,\n });\n return exitAfterFlush(ExitCode.Ok);\n }\n if (page.contents.length === 0) {\n process.stdout.write(\n `Workspace ${workspaceId} (${category}): no segments on page ${page.currentPage}\\n`,\n );\n return exitAfterFlush(ExitCode.Ok);\n }\n process.stdout.write(\n `Workspace ${workspaceId} (${category}): ${page.contents.length} segment(s), page ${page.currentPage} of ${page.totalPage}\\n`,\n );\n for (const s of page.contents) {\n const id =\n typeof s.id === 'string' || typeof s.id === 'number'\n ? s.id\n : typeof s.segmentId === 'string' || typeof s.segmentId === 'number'\n ? s.segmentId\n : '-';\n const name =\n typeof s.name === 'string' ? s.name : typeof s.title === 'string' ? s.title : '-';\n const userCount =\n typeof s.userCount === 'number'\n ? String(s.userCount)\n : typeof s.count === 'number'\n ? String(s.count)\n : '-';\n process.stdout.write(`${id}\\t${name}\\t${userCount}\\n`);\n }\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\nconst segmentsCommand = defineCommand({\n meta: {\n name: 'segments',\n description: 'Inspect user segments defined in a workspace.',\n },\n subCommands: {\n ls: segmentsLsCommand,\n },\n});\n\nexport const workspaceCommand = defineCommand({\n meta: {\n name: 'workspace',\n description: 'Inspect and switch between the workspaces this account can access.',\n },\n subCommands: {\n ls: lsCommand,\n use: useCommand,\n show: showCommand,\n partner: partnerCommand,\n terms: termsCommand,\n segments: segmentsCommand,\n },\n});\n","#!/usr/bin/env node\nimport { defineCommand, runMain } from 'citty';\nimport { appCommand } from './commands/app.js';\nimport { authCommand } from './commands/auth.js';\nimport { completionCommand } from './commands/completion.js';\nimport { keysCommand } from './commands/keys.js';\nimport { loginCommand } from './commands/login.js';\nimport { logoutCommand } from './commands/logout.js';\nimport { meCommand } from './commands/me.js';\nimport { membersCommand } from './commands/members.js';\nimport { noticesCommand } from './commands/notices.js';\nimport { cleanupStaleUpgradeArtifacts, upgradeCommand } from './commands/upgrade.js';\nimport { whoamiCommand } from './commands/whoami.js';\nimport { workspaceCommand } from './commands/workspace.js';\nimport { VERSION } from './version.js';\n\nconst main = defineCommand({\n meta: {\n name: 'aitcc',\n version: VERSION,\n description:\n 'aitcc — Apps in Toss Community Console CLI. Unofficial, not affiliated with Toss.',\n },\n subCommands: {\n whoami: whoamiCommand,\n login: loginCommand,\n logout: logoutCommand,\n auth: authCommand,\n upgrade: upgradeCommand,\n workspace: workspaceCommand,\n app: appCommand,\n members: membersCommand,\n keys: keysCommand,\n notices: noticesCommand,\n me: meCommand,\n completion: completionCommand,\n },\n});\n\ncleanupStaleUpgradeArtifacts().catch(() => {\n // best-effort; failure must not affect command execution.\n});\n\nrunMain(main);\n"],"mappings":"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"}
1
+ {"version":3,"file":"cli.mjs","names":["BASE","BASE","DEFAULT_NAMES","fileExists","parseYaml","parsePositiveInt","BASE","parsePositiveInt","fileExists","parseYaml","resolvePath","isPromptCancelled","emitInteractiveRequired","lsCommand","showCommand","statusCommand","resolvePath","categoriesCommand","stripTrailingNewline","MISSING_HINT_FULL","MISSING_HINT_SHORT","stripTrailingNewline","stripTrailingNewline","password","isPromptCancelled","passwordPrompt","BASE","lsCommand","winPath","fsConstants","passwordPrompt","termsCommand","BASE","lsCommand","lsCommand","showCommand","parsePositiveInt"],"sources":["../src/api/http.ts","../src/api/certs.ts","../src/api/mini-apps.ts","../src/exit.ts","../src/flush.ts","../src/paths.ts","../src/session.ts","../src/api/error-messages.ts","../src/config/project-context.ts","../src/commands/_shared.ts","../src/api/me.ts","../src/api/workspaces.ts","../src/config/ait-bundle.ts","../src/commands/app-deploy.ts","../src/config/app-manifest.ts","../src/commands/app-init-template.ts","../src/commands/app-init.ts","../src/config/image-validator.ts","../src/commands/register-payload.ts","../src/commands/register.ts","../src/commands/app.ts","../src/auth/backend.ts","../src/auth/backends/linux.ts","../src/auth/backends/macos.ts","../src/auth/backends/windows.ts","../src/auth/credentials.ts","../src/auth/kr-only-warning.ts","../src/commands/auth-export.ts","../src/commands/auth-import.ts","../src/commands/auth.ts","../src/commands/completion.ts","../src/api/api-keys.ts","../src/commands/keys.ts","../src/cdp.ts","../src/chrome.ts","../src/login-headless.ts","../src/commands/login.ts","../src/commands/logout.ts","../src/commands/me.ts","../src/api/members.ts","../src/commands/members.ts","../src/api/ipd-thor.ts","../src/commands/notices.ts","../src/github.ts","../src/platform.ts","../src/semver.ts","../src/sha256.ts","../src/version.ts","../src/commands/upgrade.ts","../src/update-check.ts","../src/commands/whoami.ts","../src/commands/workspace.ts","../src/cli.ts"],"sourcesContent":["// Thin HTTP layer for driving the Apps in Toss console API.\n//\n// Two concerns live here:\n// 1. Serialising the session's captured cookies into a `Cookie` header\n// per request origin (we drop cookies whose Domain/Path don't match\n// the target URL — feeding `apps-in-toss.toss.im` session cookies to\n// `business-accounts.toss.im` would be either ignored or rejected).\n// 2. Unwrapping the Toss `{ resultType, success, error? }` envelope that\n// every console endpoint uses. Upstream callers get `T` on success or\n// a typed `TossApiError` on failure — no need to repeat envelope\n// dispatch in every command.\n//\n// We don't try to be a full cookie jar. The cookie set we care about is\n// captured in one shot at login time and replayed verbatim thereafter;\n// Set-Cookie responses from API calls are ignored. A later PR will add\n// refresh logic once we see whether the console issues sliding sessions.\n\nimport type { CdpCookie } from '../cdp.js';\n\nexport interface TossEnvelopeSuccess<T> {\n readonly resultType: 'SUCCESS';\n readonly success: T;\n}\n\nexport interface TossEnvelopeFailure {\n readonly resultType: 'FAIL';\n readonly success: null;\n readonly error: {\n readonly errorType: number;\n readonly errorCode: string;\n readonly reason: string;\n readonly data?: unknown;\n readonly title?: string | null;\n };\n}\n\nexport type TossEnvelope<T> = TossEnvelopeSuccess<T> | TossEnvelopeFailure;\n\nexport class TossApiError extends Error {\n constructor(\n readonly status: number,\n readonly errorCode: string,\n readonly reason: string,\n readonly errorType: number,\n ) {\n super(`Toss API error ${errorCode}: ${reason} (HTTP ${status})`);\n this.name = 'TossApiError';\n }\n\n /** Cookie-based auth rejected — session missing/expired/invalidated. */\n get isAuthError(): boolean {\n return this.status === 401 || this.errorCode === '4010';\n }\n}\n\nexport class NetworkError extends Error {\n constructor(\n readonly url: string,\n cause: Error,\n ) {\n super(`Network request to ${url} failed: ${cause.message}`);\n this.name = 'NetworkError';\n this.cause = cause;\n }\n}\n\nexport class MalformedResponseError extends Error {\n constructor(\n readonly url: string,\n readonly status: number,\n message: string,\n readonly bodyPreview?: string,\n ) {\n const suffix = bodyPreview ? ` (body: ${bodyPreview})` : '';\n super(`Malformed response from ${url} (HTTP ${status}): ${message}${suffix}`);\n this.name = 'MalformedResponseError';\n }\n}\n\n// --- Cookie matching ---\n\n/**\n * RFC 6265-ish domain match. We accept the bare hostname case plus the\n * standard suffix match (`.example.com` cookie matches `foo.example.com`),\n * because CDP `Network.getAllCookies` normalises cookie Domain to a form\n * with a leading dot for host-matching cookies but without for explicit-host\n * cookies. Either form should round-trip correctly.\n */\nexport function domainMatches(cookieDomain: string, hostname: string): boolean {\n if (cookieDomain.length === 0) return false;\n const lower = cookieDomain.toLowerCase();\n const host = hostname.toLowerCase();\n if (lower === host) return true;\n if (lower.startsWith('.') && host.endsWith(lower)) return true;\n // Host cookies without a leading dot: cookie Domain must equal the host.\n // Suffix-match only applies when there's an explicit leading dot.\n if (!lower.startsWith('.') && host.endsWith(`.${lower}`)) return true;\n return false;\n}\n\n/**\n * RFC 6265 §5.1.4 path match. A cookie Path C matches a request path P iff\n * C and P are identical, OR C is a prefix of P and either C already ends\n * with '/' or the character in P immediately after C is '/'. Notably\n * \"/foo\" does NOT match \"/foobar\".\n */\nexport function pathMatches(cookiePath: string, requestPath: string): boolean {\n if (!cookiePath) return true;\n if (cookiePath === requestPath) return true;\n if (!requestPath.startsWith(cookiePath)) return false;\n return cookiePath.endsWith('/') || requestPath.charAt(cookiePath.length) === '/';\n}\n\n// Cookie name/value must not contain control characters or separators that\n// would let an attacker smuggle header fields via CRLF injection. CDP\n// returns cookie values verbatim, so we defend at the serialisation edge.\nfunction isSafeCookiePart(s: string): boolean {\n // Reject CR, LF, NUL, and ';' (cookie separator). Tabs and spaces are\n // technically allowed but we block them too to avoid header confusion.\n // biome-ignore lint/suspicious/noControlCharactersInRegex: explicit control-char filter\n return !/[\\x00-\\x1f;\\x7f]/.test(s);\n}\n\n/**\n * Build a `Cookie:` header value for the given URL from a captured cookie\n * set. Returns `null` when no cookies match — the caller should skip the\n * header entirely rather than emit `Cookie: ` with an empty value.\n *\n * Ordering follows RFC 6265 §5.4: cookies with longer paths come before\n * cookies with shorter paths; ties break on earliest creation time, which\n * we don't track, so we preserve capture order as a stable tiebreaker.\n */\nexport function cookieHeaderFor(url: URL, cookies: readonly CdpCookie[]): string | null {\n const matching = cookies\n .map((c, i) => ({ c, i }))\n .filter(({ c }) => {\n if (!isSafeCookiePart(c.name) || !isSafeCookiePart(c.value)) return false;\n if (!domainMatches(c.domain, url.hostname)) return false;\n if (!pathMatches(c.path, url.pathname)) return false;\n if (c.secure && url.protocol !== 'https:') return false;\n return true;\n })\n .sort((a, b) => {\n const byPath = b.c.path.length - a.c.path.length;\n return byPath !== 0 ? byPath : a.i - b.i;\n })\n .map(({ c }) => c);\n if (matching.length === 0) return null;\n return matching.map((c) => `${c.name}=${c.value}`).join('; ');\n}\n\n// --- Request helper ---\n\n// Narrow fetch signature that callers (and tests) can satisfy without\n// implementing Bun-specific extensions like `fetch.preconnect`.\nexport type FetchLike = (input: URL | string, init?: RequestInit) => Promise<Response>;\n\nexport interface RequestOptions {\n readonly method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';\n readonly url: string;\n readonly cookies: readonly CdpCookie[];\n readonly body?: unknown;\n readonly fetchImpl?: FetchLike;\n readonly headers?: Record<string, string>;\n}\n\n/**\n * Perform a request against the console API and unwrap the Toss envelope.\n *\n * Always sets `Accept: application/json` and propagates the captured cookie\n * set. Callers may pass additional headers (useful for CSRF tokens that\n * later endpoints turn out to require — discovery is per-feature).\n */\nexport async function requestConsoleApi<T>(options: RequestOptions): Promise<T> {\n const url = new URL(options.url);\n const cookieHeader = cookieHeaderFor(url, options.cookies);\n const headers: Record<string, string> = {\n Accept: 'application/json, text/plain, */*',\n ...options.headers,\n };\n if (cookieHeader) headers.Cookie = cookieHeader;\n\n const init: RequestInit = {\n method: options.method ?? 'GET',\n headers,\n // Cookies handled manually; disable any built-in cookie jar behaviour.\n redirect: 'follow',\n };\n if (options.body !== undefined) {\n headers['Content-Type'] = 'application/json';\n init.body = JSON.stringify(options.body);\n }\n\n return executeAndUnwrap<T>(url, init, options.fetchImpl);\n}\n\n/**\n * Send a pre-built `RequestInit` against the console API and unwrap the\n * Toss envelope. Use this when the caller needs to build the body itself\n * (multipart uploads, binary requests, anything that can't live under\n * `requestConsoleApi`'s JSON-body assumption). Cookie header composition\n * and any additional headers remain the caller's responsibility.\n *\n * Exists so `uploadMiniAppResource` doesn't have to re-implement the\n * text→JSON→envelope→error branch in `requestConsoleApi`; drift between\n * the two paths has bitten us once (cf. the refactor in the\n * `app register` review).\n */\nexport async function executeAndUnwrap<T>(\n url: URL,\n init: RequestInit,\n fetchImpl?: FetchLike,\n): Promise<T> {\n const impl: FetchLike = fetchImpl ?? ((input, i) => fetch(input, i));\n let res: Response;\n try {\n res = await impl(url, init);\n } catch (err) {\n throw new NetworkError(url.toString(), err as Error);\n }\n\n // Read the body as text first so a parse failure can include a preview\n // in the error. Empty responses or non-JSON WAF pages are a lot easier\n // to diagnose when you can see the first few hundred bytes.\n let text: string;\n try {\n text = await res.text();\n } catch (err) {\n throw new MalformedResponseError(url.toString(), res.status, (err as Error).message);\n }\n let parsed: TossEnvelope<T>;\n try {\n parsed = JSON.parse(text) as TossEnvelope<T>;\n } catch (err) {\n const preview = text.slice(0, 200).replace(/\\s+/g, ' ').trim();\n throw new MalformedResponseError(url.toString(), res.status, (err as Error).message, preview);\n }\n\n if (parsed.resultType === 'SUCCESS') {\n return parsed.success;\n }\n throw new TossApiError(\n res.status,\n parsed.error.errorCode,\n parsed.error.reason,\n parsed.error.errorType,\n );\n}\n","import type { CdpCookie } from '../cdp.js';\nimport { type FetchLike, requestConsoleApi } from './http.js';\n\n// mTLS certs: three endpoints with one path quirk worth pinning at the\n// import site:\n//\n// GET /workspaces/:wid/mini-app/:aid/certs → list\n// POST /workspaces/:wid/mini-app/:aid/cert/issue → issue (singular!)\n// POST /workspaces/:wid/mini-app/:aid/certs/:certId/disable → revoke\n//\n// The console SPA defines all three side-by-side; only `issue` uses the\n// singular `cert` segment. Confirmed by static-analysing the mTLS page\n// chunk (`index.Bw6JQUAu.js`) — see docs/api/mini-app-misc.md\n// \"mTLS cert issue / disable / list\" for the receipts.\n\nconst BASE = 'https://apps-in-toss.toss.im/console/api-public/v3/appsintossconsole';\n\nexport async function fetchCerts(\n workspaceId: number,\n miniAppId: number,\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<readonly Readonly<Record<string, unknown>>[]> {\n const url = `${BASE}/workspaces/${workspaceId}/mini-app/${miniAppId}/certs`;\n const raw = await requestConsoleApi<unknown>({\n url,\n cookies,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n if (!Array.isArray(raw)) {\n throw new Error(`Unexpected certs shape for app=${miniAppId}: not an array`);\n }\n return raw.map((c) => {\n if (c === null || typeof c !== 'object') return {};\n return c as Record<string, unknown>;\n });\n}\n\n// Issue (`POST .../cert/issue`) is the only path that exposes the private\n// key — list responses only carry metadata. Callers that intend to persist\n// the material must do so atomically; the server side has no re-issue\n// endpoint for the same `name`, just disable + create-fresh.\nexport interface IssueCertResult {\n readonly privateKey: string;\n readonly publicKey: string;\n}\n\nexport async function issueCert(\n workspaceId: number,\n miniAppId: number,\n name: string,\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<IssueCertResult> {\n const url = `${BASE}/workspaces/${workspaceId}/mini-app/${miniAppId}/cert/issue`;\n const raw = await requestConsoleApi<unknown>({\n url,\n method: 'POST',\n body: { name },\n cookies,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n if (raw === null || typeof raw !== 'object') {\n throw new Error(`Unexpected issue-cert shape for app=${miniAppId}: not an object`);\n }\n const rec = raw as Record<string, unknown>;\n const privateKey = typeof rec.privateKey === 'string' ? rec.privateKey : null;\n const publicKey = typeof rec.publicKey === 'string' ? rec.publicKey : null;\n if (privateKey === null || publicKey === null) {\n throw new Error(\n `Unexpected issue-cert shape for app=${miniAppId}: missing privateKey/publicKey`,\n );\n }\n return { privateKey, publicKey };\n}\n\n// `disable` is what the wire calls it; the CLI surfaces it as `revoke`\n// because the console UI itself has no reactivate path.\nexport async function revokeCert(\n workspaceId: number,\n miniAppId: number,\n certId: string,\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<void> {\n const url = `${BASE}/workspaces/${workspaceId}/mini-app/${miniAppId}/certs/${encodeURIComponent(certId)}/disable`;\n await requestConsoleApi<unknown>({\n url,\n method: 'POST',\n body: {},\n cookies,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n}\n","import type { CdpCookie } from '../cdp.js';\nimport {\n cookieHeaderFor,\n executeAndUnwrap,\n type FetchLike,\n MalformedResponseError,\n requestConsoleApi,\n} from './http.js';\n\n// Two endpoints cover the \"list my apps\" surface:\n//\n// GET /workspaces/:id/mini-app → array of app summaries\n// GET /workspaces/:id/mini-apps/review-status → { hasPolicyViolation, miniApps: [...] }\n//\n// Note the singular/plural inconsistency (`mini-app` vs `mini-apps`) is\n// how the upstream API actually spells them, not a transcription error —\n// see TODO.md's console feature inventory.\n//\n// The detailed field shape inside each array element is not yet known to\n// us (the confirmed workspaces currently have zero apps). We model each\n// element as a minimal \"id + name + extras\" envelope so the CLI can show\n// something useful today and layer on specific fields as they're observed.\n// `extra` is typed as unknown-valued so we don't pretend to know more.\n\nconst BASE = 'https://apps-in-toss.toss.im/console/api-public/v3/appsintossconsole';\n\nexport interface MiniAppSummary {\n readonly id: string | number;\n readonly name: string | undefined;\n readonly extra: Readonly<Record<string, unknown>>;\n}\n\nexport interface ReviewStatusSummary {\n readonly hasPolicyViolation: boolean;\n readonly miniApps: readonly Readonly<Record<string, unknown>>[];\n}\n\nexport async function fetchMiniApps(\n workspaceId: number,\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<MiniAppSummary[]> {\n const url = `${BASE}/workspaces/${workspaceId}/mini-app`;\n const raw = await requestConsoleApi<unknown>({\n url,\n cookies,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n if (!Array.isArray(raw)) {\n throw new Error(`Unexpected mini-app list shape for workspace=${workspaceId}`);\n }\n return raw.map((item, index) => normalizeMiniApp(item, workspaceId, index));\n}\n\nfunction normalizeMiniApp(item: unknown, workspaceId: number, index: number): MiniAppSummary {\n if (item === null || typeof item !== 'object') {\n throw new Error(\n `Unexpected mini-app entry at index ${index} for workspace=${workspaceId}: not an object`,\n );\n }\n const rec = item as Record<string, unknown>;\n const rawId = rec.id ?? rec.miniAppId ?? rec.appId;\n if (typeof rawId !== 'string' && typeof rawId !== 'number') {\n throw new Error(\n `Unexpected mini-app entry at index ${index} for workspace=${workspaceId}: missing id`,\n );\n }\n const rawName = rec.name ?? rec.miniAppName ?? rec.appName;\n const name = typeof rawName === 'string' ? rawName : undefined;\n const {\n id: _id,\n miniAppId: _mid,\n appId: _aid,\n name: _n,\n miniAppName: _mn,\n appName: _an,\n ...extra\n } = rec;\n return { id: rawId, name, extra };\n}\n\nexport async function fetchReviewStatus(\n workspaceId: number,\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<ReviewStatusSummary> {\n const url = `${BASE}/workspaces/${workspaceId}/mini-apps/review-status`;\n const raw = await requestConsoleApi<unknown>({\n url,\n cookies,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n if (raw === null || typeof raw !== 'object') {\n throw new Error(`Unexpected review-status shape for workspace=${workspaceId}`);\n }\n const rec = raw as Record<string, unknown>;\n const hasPolicyViolation = Boolean(rec.hasPolicyViolation);\n const miniAppsRaw = rec.miniApps;\n if (!Array.isArray(miniAppsRaw)) {\n throw new Error(\n `Unexpected review-status shape for workspace=${workspaceId}: miniApps is not an array`,\n );\n }\n const miniApps = miniAppsRaw.map((m) => {\n if (m === null || typeof m !== 'object') return {};\n return m as Record<string, unknown>;\n });\n return { hasPolicyViolation, miniApps };\n}\n\nexport interface MiniAppWithDraft {\n readonly current: Record<string, unknown> | null;\n readonly draft: Record<string, unknown> | null;\n // Top-level envelope fields (not inside current/draft). Present on every\n // with-draft response. `approvalType` distinguishes REVIEW-submitted apps\n // from drafts that haven't been sent for review; `rejectedMessage` is\n // non-null iff the review came back rejected. Together with `current`\n // (null until an approved record exists) they derive the UI banner state.\n readonly approvalType: string | null;\n readonly rejectedMessage: string | null;\n}\n\nexport async function fetchMiniAppWithDraft(\n workspaceId: number,\n miniAppId: number,\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<MiniAppWithDraft> {\n const url = `${BASE}/workspaces/${workspaceId}/mini-app/${miniAppId}/with-draft`;\n const raw = await requestConsoleApi<unknown>({\n url,\n cookies,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n if (raw === null || typeof raw !== 'object') {\n throw new Error(`Unexpected with-draft shape for mini-app=${miniAppId}`);\n }\n const rec = raw as Record<string, unknown>;\n const current = isRecordOrNull(rec.current)\n ? (rec.current as Record<string, unknown> | null)\n : null;\n const draft = isRecordOrNull(rec.draft) ? (rec.draft as Record<string, unknown> | null) : null;\n const approvalType = typeof rec.approvalType === 'string' ? rec.approvalType : null;\n const rejectedMessage = typeof rec.rejectedMessage === 'string' ? rec.rejectedMessage : null;\n return { current, draft, approvalType, rejectedMessage };\n}\n\nfunction isRecordOrNull(v: unknown): v is Record<string, unknown> | null {\n return v === null || (typeof v === 'object' && !Array.isArray(v));\n}\n\n// --- Ratings & reviews ---\n//\n// GET /workspaces/:wid/mini-app/:aid/app-ratings\n// ?page=0&size=20&sortField=CREATED_AT&sortDirection=DESC\n//\n// Response envelope (observed 2026-04-23 on app 29405, empty case):\n// { ratings: [...], paging: { pageNumber, pageSize, hasNext, totalCount },\n// averageRating, totalReviewCount }\n//\n// Individual rating records have not yet been observed (sdk-example has\n// zero reviews while under review). We pass each record through as an\n// opaque Record<string, unknown> for now; once a real review lands we can\n// pin the per-row shape without breaking the wrapper.\n\nexport type RatingSortField = 'CREATED_AT' | 'SCORE';\nexport type RatingSortDirection = 'ASC' | 'DESC';\n\nexport interface RatingsPaging {\n readonly pageNumber: number;\n readonly pageSize: number;\n readonly hasNext: boolean;\n readonly totalCount: number;\n}\n\nexport interface MiniAppRatingsPage {\n readonly ratings: readonly Readonly<Record<string, unknown>>[];\n readonly paging: RatingsPaging;\n readonly averageRating: number;\n readonly totalReviewCount: number;\n}\n\nexport interface FetchRatingsParams {\n readonly workspaceId: number;\n readonly miniAppId: number;\n readonly page?: number;\n readonly size?: number;\n readonly sortField?: RatingSortField;\n readonly sortDirection?: RatingSortDirection;\n}\n\nexport async function fetchMiniAppRatings(\n params: FetchRatingsParams,\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<MiniAppRatingsPage> {\n const page = params.page ?? 0;\n const size = params.size ?? 20;\n const sortField = params.sortField ?? 'CREATED_AT';\n const sortDirection = params.sortDirection ?? 'DESC';\n const url =\n `${BASE}/workspaces/${params.workspaceId}/mini-app/${params.miniAppId}/app-ratings` +\n `?page=${page}&size=${size}&sortField=${sortField}&sortDirection=${sortDirection}`;\n\n const raw = await requestConsoleApi<unknown>({\n url,\n cookies,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n if (raw === null || typeof raw !== 'object') {\n throw new Error(`Unexpected ratings shape for app=${params.miniAppId}`);\n }\n const rec = raw as Record<string, unknown>;\n const ratingsRaw = rec.ratings;\n if (!Array.isArray(ratingsRaw)) {\n throw new Error(`Unexpected ratings shape: ratings is not an array (app=${params.miniAppId})`);\n }\n const ratings = ratingsRaw.map((r) => {\n if (r === null || typeof r !== 'object') return {};\n return r as Record<string, unknown>;\n });\n const pagingRaw = rec.paging;\n if (pagingRaw === null || typeof pagingRaw !== 'object') {\n throw new Error(`Unexpected ratings shape: paging missing (app=${params.miniAppId})`);\n }\n const p = pagingRaw as Record<string, unknown>;\n const paging: RatingsPaging = {\n pageNumber: typeof p.pageNumber === 'number' ? p.pageNumber : page,\n pageSize: typeof p.pageSize === 'number' ? p.pageSize : size,\n hasNext: Boolean(p.hasNext),\n totalCount: typeof p.totalCount === 'number' ? p.totalCount : 0,\n };\n const averageRating = typeof rec.averageRating === 'number' ? rec.averageRating : 0;\n const totalReviewCount = typeof rec.totalReviewCount === 'number' ? rec.totalReviewCount : 0;\n return { ratings, paging, averageRating, totalReviewCount };\n}\n\n// --- User reports (신고 내역) ---\n//\n// GET /workspaces/:wid/mini-apps/:aid/user-reports?pageSize=N[&cursor=...]\n//\n// Note the URL uses **plural** `mini-apps` — same split-personality as\n// `mini-apps/review-status`. Don't \"normalize\" it.\n//\n// Response envelope (observed 2026-04-23 on app 29405, empty case):\n// { reports: [...], nextCursor: null | string, hasMore: boolean }\n//\n// Pagination is cursor-based here (unlike ratings which is page-based).\n// We expose `cursor` as an optional param and echo back `nextCursor` /\n// `hasMore` so callers can page through without guessing the scheme.\n\nexport interface UserReportsPage {\n readonly reports: readonly Readonly<Record<string, unknown>>[];\n readonly nextCursor: string | null;\n readonly hasMore: boolean;\n}\n\nexport interface FetchUserReportsParams {\n readonly workspaceId: number;\n readonly miniAppId: number;\n readonly pageSize?: number;\n readonly cursor?: string;\n}\n\nexport async function fetchUserReports(\n params: FetchUserReportsParams,\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<UserReportsPage> {\n const pageSize = params.pageSize ?? 20;\n const qs = new URLSearchParams();\n qs.set('pageSize', String(pageSize));\n if (params.cursor !== undefined) qs.set('cursor', params.cursor);\n const url =\n `${BASE}/workspaces/${params.workspaceId}/mini-apps/${params.miniAppId}/user-reports?` +\n qs.toString();\n\n const raw = await requestConsoleApi<unknown>({\n url,\n cookies,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n if (raw === null || typeof raw !== 'object') {\n throw new Error(`Unexpected user-reports shape for app=${params.miniAppId}`);\n }\n const rec = raw as Record<string, unknown>;\n const reportsRaw = rec.reports;\n if (!Array.isArray(reportsRaw)) {\n throw new Error(\n `Unexpected user-reports shape: reports is not an array (app=${params.miniAppId})`,\n );\n }\n const reports = reportsRaw.map((r) => {\n if (r === null || typeof r !== 'object') return {};\n return r as Record<string, unknown>;\n });\n const nextCursor = typeof rec.nextCursor === 'string' ? rec.nextCursor : null;\n const hasMore = Boolean(rec.hasMore);\n return { reports, nextCursor, hasMore };\n}\n\n// --- Bundles (앱 번들 배포) ---\n//\n// GET /workspaces/:wid/mini-app/:aid/bundles[?page=&tested=&deployStatus=]\n// GET /workspaces/:wid/mini-app/:aid/bundles/deployed → single bundle | null\n//\n// Response envelope (observed 2026-04-23 on app 29405, empty case):\n// { contents: [...], totalPage: 0, currentPage: 0 }\n//\n// Page-based pagination (unlike ratings's {paging:{...}} or user-reports\n// cursor-based). Filter query params: `tested=true` for TESTED-only, and\n// `deployStatus=DEPLOYED` to narrow to live bundles. We expose those as\n// optional opaque-string filters so callers can pass them through without\n// the API layer enumerating every enum value ahead of time.\n//\n// `bundles/deployed` (singular route) returns null until a first deploy\n// lands; the shape of a populated record is not yet observed, so we pass\n// it through opaquely.\n\nexport interface BundlesPage {\n readonly contents: readonly Readonly<Record<string, unknown>>[];\n readonly totalPage: number;\n readonly currentPage: number;\n}\n\nexport interface FetchBundlesParams {\n readonly workspaceId: number;\n readonly miniAppId: number;\n readonly page?: number;\n readonly tested?: boolean;\n readonly deployStatus?: string;\n}\n\nexport async function fetchBundles(\n params: FetchBundlesParams,\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<BundlesPage> {\n const qs = new URLSearchParams();\n if (params.page !== undefined) qs.set('page', String(params.page));\n if (params.tested !== undefined) qs.set('tested', String(params.tested));\n if (params.deployStatus !== undefined) qs.set('deployStatus', params.deployStatus);\n const query = qs.toString();\n const url =\n `${BASE}/workspaces/${params.workspaceId}/mini-app/${params.miniAppId}/bundles` +\n (query ? `?${query}` : '');\n\n const raw = await requestConsoleApi<unknown>({\n url,\n cookies,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n if (raw === null || typeof raw !== 'object') {\n throw new Error(`Unexpected bundles shape for app=${params.miniAppId}`);\n }\n const rec = raw as Record<string, unknown>;\n const contentsRaw = rec.contents;\n if (!Array.isArray(contentsRaw)) {\n throw new Error(`Unexpected bundles shape: contents is not an array (app=${params.miniAppId})`);\n }\n const contents = contentsRaw.map((b) => {\n if (b === null || typeof b !== 'object') return {};\n return b as Record<string, unknown>;\n });\n const totalPage = typeof rec.totalPage === 'number' ? rec.totalPage : 0;\n const currentPage = typeof rec.currentPage === 'number' ? rec.currentPage : 0;\n return { contents, totalPage, currentPage };\n}\n\n// --- Conversion metrics ---\n//\n// GET /workspaces/:wid/mini-app/:aid/conversion-metrics\n// ?refresh=false&timeUnitType=DAY|WEEK|MONTH&startDate=YYYY-MM-DD&endDate=YYYY-MM-DD\n//\n// Response envelope (observed 2026-04-23 on app 29405, empty case):\n// { metrics: [], cacheTime: '2026-04-23T13:49:42.693498831' }\n//\n// All apps in the current workspace are PREPARE-state (no live traffic),\n// so the shape of a populated `metrics[]` entry isn't observed yet — we\n// pass records through opaquely until a live app lands. `cacheTime` is\n// the server-side cache timestamp (ISO-ish with nanoseconds); we surface\n// it verbatim so agent-plugin can reason about freshness.\n//\n// `refresh=true` bypasses the server cache; we default to `false` to\n// match the console UI's default request and avoid hammering the\n// underlying data warehouse.\n\nexport type MetricsTimeUnit = 'DAY' | 'WEEK' | 'MONTH';\n\nexport interface MetricsResult {\n readonly metrics: readonly Readonly<Record<string, unknown>>[];\n readonly cacheTime: string | undefined;\n}\n\nexport interface FetchMetricsParams {\n readonly workspaceId: number;\n readonly miniAppId: number;\n readonly timeUnitType: MetricsTimeUnit;\n readonly startDate: string;\n readonly endDate: string;\n readonly refresh?: boolean;\n}\n\nexport async function fetchConversionMetrics(\n params: FetchMetricsParams,\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<MetricsResult> {\n const qs = new URLSearchParams();\n qs.set('refresh', String(params.refresh ?? false));\n qs.set('timeUnitType', params.timeUnitType);\n qs.set('startDate', params.startDate);\n qs.set('endDate', params.endDate);\n const url =\n `${BASE}/workspaces/${params.workspaceId}/mini-app/${params.miniAppId}/conversion-metrics` +\n `?${qs.toString()}`;\n\n const raw = await requestConsoleApi<unknown>({\n url,\n cookies,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n if (raw === null || typeof raw !== 'object') {\n throw new Error(`Unexpected metrics shape for app=${params.miniAppId}`);\n }\n const rec = raw as Record<string, unknown>;\n const metricsRaw = rec.metrics;\n if (!Array.isArray(metricsRaw)) {\n throw new Error(`Unexpected metrics shape: metrics is not an array (app=${params.miniAppId})`);\n }\n const metrics = metricsRaw.map((m) => {\n if (m === null || typeof m !== 'object') return {};\n return m as Record<string, unknown>;\n });\n const cacheTime = typeof rec.cacheTime === 'string' ? rec.cacheTime : undefined;\n return { metrics, cacheTime };\n}\n\n// mTLS cert API → src/api/certs.ts\n\n// --- Share rewards ---\n//\n// GET /workspaces/:wid/mini-app/:aid/share-rewards[?search=]\n//\n// Response envelope (observed 2026-04-23 on app 29405, empty case):\n// []\n//\n// Simple array. The console UI passes `search=` as a title-contains\n// filter (observed as the default XHR on the 공유 리워드 page) — the empty\n// string matches everything. Per-record shape is passed through opaquely\n// until a populated response is observed; promotions won't exist on\n// unreleased apps.\n\nexport interface FetchShareRewardsParams {\n readonly workspaceId: number;\n readonly miniAppId: number;\n readonly search?: string;\n}\n\nexport async function fetchShareRewards(\n params: FetchShareRewardsParams,\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<readonly Readonly<Record<string, unknown>>[]> {\n const qs = new URLSearchParams();\n // Match the console UI's default (always sends `search=`). Callers that\n // don't pass a filter still include it as empty so the request shape\n // matches what the server expects.\n qs.set('search', params.search ?? '');\n const url = `${BASE}/workspaces/${params.workspaceId}/mini-app/${params.miniAppId}/share-rewards?${qs.toString()}`;\n const raw = await requestConsoleApi<unknown>({\n url,\n cookies,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n if (!Array.isArray(raw)) {\n throw new Error(`Unexpected share-rewards shape for app=${params.miniAppId}: not an array`);\n }\n return raw.map((r) => {\n if (r === null || typeof r !== 'object') return {};\n return r as Record<string, unknown>;\n });\n}\n\n// --- Smart-message campaigns ---\n//\n// \"스마트 발송\" (smart-message) is the replacement for the legacy\n// push-notifications menu. List endpoint is a POST with the filter body\n// in JSON and paging in the querystring — that shape is the console\n// UI's XHR, and we mirror it so the request is indistinguishable.\n//\n// Empirical response shape (live workspace 3095, PREPARE-state app):\n// { items: [], paging: { pageNumber, pageSize, hasNext, totalCount } }\n// Items are passed through opaquely until a populated campaign is\n// observed.\n\nexport type SmartMessageSort = { field: string; direction: 'ASC' | 'DESC' };\n// Open-ended filters bag. The UI currently sends `{}` or\n// `{ channelTypes: [] }` depending on which tab is active — we keep\n// the type permissive because more facets may surface over time.\nexport type SmartMessageFilters = Readonly<Record<string, unknown>>;\n\nexport interface FetchSmartMessageCampaignsParams {\n readonly workspaceId: number;\n readonly miniAppId: number;\n readonly page?: number;\n readonly size?: number;\n readonly search?: string;\n readonly sort?: readonly SmartMessageSort[];\n readonly filters?: SmartMessageFilters;\n}\n\nexport interface SmartMessagePaging {\n readonly pageNumber: number;\n readonly pageSize: number;\n readonly hasNext: boolean;\n readonly totalCount: number;\n}\n\nexport interface SmartMessageCampaignsResult {\n readonly items: readonly Readonly<Record<string, unknown>>[];\n readonly paging: SmartMessagePaging;\n}\n\nexport async function fetchSmartMessageCampaigns(\n params: FetchSmartMessageCampaignsParams,\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<SmartMessageCampaignsResult> {\n const page = params.page ?? 0;\n const size = params.size ?? 20;\n const qs = new URLSearchParams();\n qs.set('page', String(page));\n qs.set('size', String(size));\n const url = `${BASE}/workspaces/${params.workspaceId}/mini-app/${params.miniAppId}/smart-message/campaigns?${qs.toString()}`;\n const body = {\n sort: params.sort ?? [{ field: 'regTs', direction: 'DESC' }],\n search: params.search ?? '',\n filters: params.filters ?? {},\n };\n const raw = await requestConsoleApi<unknown>({\n method: 'POST',\n url,\n body,\n cookies,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) {\n throw new Error(`Unexpected smart-message campaigns shape for app=${params.miniAppId}`);\n }\n const data = raw as Record<string, unknown>;\n const items = Array.isArray(data.items) ? data.items : [];\n const rawPaging = data.paging as Record<string, unknown> | undefined;\n const paging: SmartMessagePaging = {\n pageNumber: typeof rawPaging?.pageNumber === 'number' ? rawPaging.pageNumber : page,\n pageSize: typeof rawPaging?.pageSize === 'number' ? rawPaging.pageSize : size,\n hasNext: Boolean(rawPaging?.hasNext),\n totalCount: typeof rawPaging?.totalCount === 'number' ? rawPaging.totalCount : items.length,\n };\n return {\n items: items.map((r) => (r && typeof r === 'object' ? (r as Record<string, unknown>) : {})),\n paging,\n };\n}\n\n// --- Event catalogs (log search) ---\n//\n// The console \"이벤트\" (events) page is powered by a POST search endpoint\n// that returns the catalog of custom events recorded for a mini-app.\n// Body shape from observed UI XHR: `{isRefresh, pageNumber, pageSize, search}`.\n// Response: `{results, cacheTime, paging: {pageNumber, pageSize, hasNext,\n// totalCount, totalPages}}`. On a PREPARE-state app with no traffic,\n// `results` is empty and `cacheTime` carries a server-cache timestamp —\n// same pattern as conversion-metrics.\n\nexport interface FetchAppEventCatalogsParams {\n readonly workspaceId: number;\n readonly miniAppId: number;\n readonly pageNumber?: number;\n readonly pageSize?: number;\n readonly search?: string;\n readonly refresh?: boolean;\n}\n\nexport interface AppEventCatalogsPaging {\n readonly pageNumber: number;\n readonly pageSize: number;\n readonly hasNext: boolean;\n readonly totalCount: number;\n readonly totalPages: number;\n}\n\nexport interface AppEventCatalogsResult {\n readonly results: readonly Readonly<Record<string, unknown>>[];\n readonly cacheTime: string | undefined;\n readonly paging: AppEventCatalogsPaging;\n}\n\nexport async function fetchAppEventCatalogs(\n params: FetchAppEventCatalogsParams,\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<AppEventCatalogsResult> {\n const pageNumber = params.pageNumber ?? 0;\n const pageSize = params.pageSize ?? 20;\n const url = `${BASE}/workspaces/${params.workspaceId}/mini-app/${params.miniAppId}/log/catalogs/search`;\n const body = {\n isRefresh: params.refresh ?? false,\n pageNumber,\n pageSize,\n search: params.search ?? '',\n };\n const raw = await requestConsoleApi<unknown>({\n method: 'POST',\n url,\n body,\n cookies,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) {\n throw new Error(`Unexpected event-catalogs shape for app=${params.miniAppId}`);\n }\n const data = raw as Record<string, unknown>;\n const results = Array.isArray(data.results) ? data.results : [];\n const rawPaging = data.paging as Record<string, unknown> | undefined;\n const paging: AppEventCatalogsPaging = {\n pageNumber: typeof rawPaging?.pageNumber === 'number' ? rawPaging.pageNumber : pageNumber,\n pageSize: typeof rawPaging?.pageSize === 'number' ? rawPaging.pageSize : pageSize,\n hasNext: Boolean(rawPaging?.hasNext),\n totalCount: typeof rawPaging?.totalCount === 'number' ? rawPaging.totalCount : results.length,\n totalPages: typeof rawPaging?.totalPages === 'number' ? rawPaging.totalPages : 0,\n };\n return {\n results: results.map((r) => (r && typeof r === 'object' ? (r as Record<string, unknown>) : {})),\n cacheTime: typeof data.cacheTime === 'string' ? data.cacheTime : undefined,\n paging,\n };\n}\n\n// --- Templates (smart-message composer) ---\n//\n// GET /mini-app/:id/templates/search\n// ?page=0&size=20&contentReachType=FUNCTIONAL|MARKETING&isSmartMessage=true|false\n//\n// Response shape (observed on empty app 29405):\n// { page: { totalPageCount }, groupSendContextSimpleView: [] }\n//\n// The odd bucket key name (`groupSendContextSimpleView`) is the server's\n// own — we surface it as `templates` at the CLI layer so the output is\n// readable without leaking the internal naming. Per-template record\n// shape is passed through opaquely until a populated response is seen.\n\nexport type TemplateContentReachType = 'FUNCTIONAL' | 'MARKETING';\nexport const TEMPLATE_CONTENT_REACH_TYPES: readonly TemplateContentReachType[] = [\n 'FUNCTIONAL',\n 'MARKETING',\n];\n\nexport interface FetchAppTemplatesParams {\n readonly workspaceId: number;\n readonly miniAppId: number;\n readonly page?: number;\n readonly size?: number;\n readonly contentReachType?: TemplateContentReachType;\n readonly isSmartMessage?: boolean;\n}\n\nexport interface AppTemplatesResult {\n readonly templates: readonly Readonly<Record<string, unknown>>[];\n readonly totalPageCount: number;\n}\n\nexport async function fetchAppTemplates(\n params: FetchAppTemplatesParams,\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<AppTemplatesResult> {\n const page = params.page ?? 0;\n const size = params.size ?? 20;\n const qs = new URLSearchParams();\n qs.set('page', String(page));\n qs.set('size', String(size));\n if (params.contentReachType) qs.set('contentReachType', params.contentReachType);\n if (params.isSmartMessage !== undefined) qs.set('isSmartMessage', String(params.isSmartMessage));\n const url = `${BASE}/workspaces/${params.workspaceId}/mini-app/${params.miniAppId}/templates/search?${qs.toString()}`;\n const raw = await requestConsoleApi<unknown>({\n url,\n cookies,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) {\n throw new Error(`Unexpected templates shape for app=${params.miniAppId}`);\n }\n const data = raw as Record<string, unknown>;\n const list = Array.isArray(data.groupSendContextSimpleView)\n ? data.groupSendContextSimpleView\n : [];\n const pageMeta = data.page as Record<string, unknown> | undefined;\n return {\n templates: list.map((r) => (r && typeof r === 'object' ? (r as Record<string, unknown>) : {})),\n totalPageCount: typeof pageMeta?.totalPageCount === 'number' ? pageMeta.totalPageCount : 0,\n };\n}\n\n// --- Impression category tree ---\n//\n// GET /impression/category-list returns the full category hierarchy used\n// by `app register`. It's a global, workspace-independent lookup — the\n// console navigates directly to the `/impression` prefix (NOT\n// `/workspaces/:id/impression`). `isSelectable: true` marks which\n// entries callers may actually reference from `categoryIds`.\n//\n// Tree: [{ categoryGroup, categoryList: [{ id, name, isSelectable,\n// subCategoryList: [{ id, name, isSelectable }] }] }]\n\nexport interface CategoryGroupNode {\n readonly id: number;\n readonly name: string;\n readonly isSelectable: boolean;\n}\n\nexport interface SubCategoryNode {\n readonly id: number;\n readonly name: string;\n readonly isSelectable: boolean;\n}\n\nexport interface CategoryNode {\n readonly id: number;\n readonly name: string;\n readonly isSelectable: boolean;\n readonly subCategoryList: readonly SubCategoryNode[];\n}\n\nexport interface CategoryTreeEntry {\n readonly categoryGroup: CategoryGroupNode;\n readonly categoryList: readonly CategoryNode[];\n}\n\nexport async function fetchImpressionCategoryList(\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<readonly CategoryTreeEntry[]> {\n const url = `${BASE}/impression/category-list`;\n const raw = await requestConsoleApi<unknown>({\n url,\n cookies,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n if (!Array.isArray(raw)) {\n throw new Error('Unexpected impression/category-list shape: not an array');\n }\n return raw.map((entry, i): CategoryTreeEntry => {\n if (!entry || typeof entry !== 'object') {\n throw new Error(`Unexpected category-list entry at index ${i}`);\n }\n const e = entry as Record<string, unknown>;\n const group = e.categoryGroup as Record<string, unknown> | undefined;\n const list = Array.isArray(e.categoryList) ? e.categoryList : [];\n return {\n categoryGroup: {\n id: typeof group?.id === 'number' ? group.id : 0,\n name: typeof group?.name === 'string' ? group.name : '',\n isSelectable: Boolean(group?.isSelectable),\n },\n categoryList: list.map((c): CategoryNode => {\n if (!c || typeof c !== 'object') {\n return { id: 0, name: '', isSelectable: false, subCategoryList: [] };\n }\n const cr = c as Record<string, unknown>;\n const subs = Array.isArray(cr.subCategoryList) ? cr.subCategoryList : [];\n return {\n id: typeof cr.id === 'number' ? cr.id : 0,\n name: typeof cr.name === 'string' ? cr.name : '',\n isSelectable: Boolean(cr.isSelectable),\n subCategoryList: subs.map((s): SubCategoryNode => {\n if (!s || typeof s !== 'object') {\n return { id: 0, name: '', isSelectable: false };\n }\n const sr = s as Record<string, unknown>;\n return {\n id: typeof sr.id === 'number' ? sr.id : 0,\n name: typeof sr.name === 'string' ? sr.name : '',\n isSelectable: Boolean(sr.isSelectable),\n };\n }),\n };\n }),\n };\n });\n}\n\n// --- App service status (per-mini-app shutdown/service state) ---\n//\n// GET /mini-app/:id/review-status (singular `mini-app`) returns the\n// runtime service status of a single mini-app. Distinct from the\n// workspace-level `mini-apps/review-status` (plural) which reports the\n// pending/approved state of every app in a workspace; this one is the\n// server-authoritative view of whether the app is live, preparing, or\n// scheduled for shutdown.\n//\n// Observed shape on a PREPARE app:\n// { shutdownCandidateStatus: null, scheduledShutdownAt: null,\n// serviceStatus: 'PREPARE' }\n\nexport interface AppServiceStatus {\n readonly shutdownCandidateStatus: string | null;\n readonly scheduledShutdownAt: string | null;\n readonly serviceStatus: string;\n}\n\nexport async function fetchAppServiceStatus(\n workspaceId: number,\n miniAppId: number,\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<AppServiceStatus> {\n const url = `${BASE}/workspaces/${workspaceId}/mini-app/${miniAppId}/review-status`;\n const raw = await requestConsoleApi<unknown>({\n url,\n cookies,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) {\n throw new Error(`Unexpected app service-status shape for app=${miniAppId}`);\n }\n const data = raw as Record<string, unknown>;\n const serviceStatus = data.serviceStatus;\n if (typeof serviceStatus !== 'string') {\n throw new Error(\n `Unexpected app service-status shape for app=${miniAppId}: missing serviceStatus`,\n );\n }\n return {\n shutdownCandidateStatus:\n typeof data.shutdownCandidateStatus === 'string' ? data.shutdownCandidateStatus : null,\n scheduledShutdownAt:\n typeof data.scheduledShutdownAt === 'string' ? data.scheduledShutdownAt : null,\n serviceStatus,\n };\n}\n\nexport async function fetchDeployedBundle(\n workspaceId: number,\n miniAppId: number,\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<Record<string, unknown> | null> {\n const url = `${BASE}/workspaces/${workspaceId}/mini-app/${miniAppId}/bundles/deployed`;\n const raw = await requestConsoleApi<unknown>({\n url,\n cookies,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n if (raw === null) return null;\n if (typeof raw !== 'object' || Array.isArray(raw)) {\n throw new Error(`Unexpected deployed-bundle shape for app=${miniAppId}`);\n }\n return raw as Record<string, unknown>;\n}\n\n// --- Bundle upload (deploy) ---\n//\n// The console's \"앱 출시 > 등록하기\" dialog walks a 3-step server dance\n// plus an optional memo write:\n//\n// 1. POST /mini-app/:id/deployments/initialize body {deploymentId}\n// → { deployment: { reviewStatus, ... }, uploadUrl }\n// 2. PUT <uploadUrl> Content-Type: application/zip, body = raw .ait bytes\n// (S3 presigned URL; goes direct to S3, no Toss envelope)\n// 3. POST /mini-app/:id/deployments/complete body {deploymentId}\n// → bundle record (server-side confirmation)\n// 4. (optional) POST /mini-app/:id/bundles/memos body {deploymentId, memo}\n//\n// `deploymentId` is embedded in the .ait bundle itself — the toolchain\n// that packs the bundle writes it into `app.json._metadata.deploymentId`\n// (observed via static analysis of the console's client-side parser in\n// `index.ZIQgZB74.js`, 2026-04-23). We take it as an explicit parameter\n// here to keep this layer free of zip-parsing logic; the command layer\n// can either crack the zip or let the user pass `--deployment-id` by\n// hand.\n//\n// The initialize response's `deployment.reviewStatus` must be `PREPARE`\n// before the PUT fires — any other value means the deploymentId has\n// already been used and the console raises \"이미 존재하는 버전이에요.\"\n\nexport interface DeploymentInitializeResult {\n readonly uploadUrl: string;\n readonly deployment: Readonly<Record<string, unknown>>;\n readonly reviewStatus: string;\n}\n\nexport async function postDeploymentsInitialize(\n workspaceId: number,\n miniAppId: number,\n deploymentId: string,\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<DeploymentInitializeResult> {\n const url = `${BASE}/workspaces/${workspaceId}/mini-app/${miniAppId}/deployments/initialize`;\n const raw = await requestConsoleApi<unknown>({\n method: 'POST',\n url,\n body: { deploymentId },\n cookies,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) {\n throw new Error(`Unexpected deployments/initialize shape for app=${miniAppId}`);\n }\n const data = raw as Record<string, unknown>;\n const uploadUrl = data.uploadUrl;\n if (typeof uploadUrl !== 'string') {\n throw new Error(\n `Unexpected deployments/initialize shape for app=${miniAppId}: missing uploadUrl`,\n );\n }\n const deployment =\n data.deployment && typeof data.deployment === 'object'\n ? (data.deployment as Record<string, unknown>)\n : {};\n const reviewStatus =\n typeof deployment.reviewStatus === 'string' ? deployment.reviewStatus : 'UNKNOWN';\n return { uploadUrl, deployment, reviewStatus };\n}\n\nexport async function postDeploymentsComplete(\n workspaceId: number,\n miniAppId: number,\n deploymentId: string,\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<Readonly<Record<string, unknown>>> {\n const url = `${BASE}/workspaces/${workspaceId}/mini-app/${miniAppId}/deployments/complete`;\n const raw = await requestConsoleApi<unknown>({\n method: 'POST',\n url,\n body: { deploymentId },\n cookies,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n // Complete returns the persisted bundle record; pass it through opaquely\n // until we see populated shape in dog-food.\n if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) return {};\n return raw as Record<string, unknown>;\n}\n\nexport async function postBundleMemo(\n workspaceId: number,\n miniAppId: number,\n deploymentId: string,\n memo: string,\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<Readonly<Record<string, unknown>>> {\n const url = `${BASE}/workspaces/${workspaceId}/mini-app/${miniAppId}/bundles/memos`;\n const raw = await requestConsoleApi<unknown>({\n method: 'POST',\n url,\n body: { deploymentId, memo },\n cookies,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) return {};\n return raw as Record<string, unknown>;\n}\n\n/**\n * PUT the raw .ait bytes to the S3 presigned URL returned by\n * `postDeploymentsInitialize`. This is a direct-to-S3 call with NO Toss\n * envelope — the response is empty on success (HTTP 200). Any cookies are\n * intentionally NOT sent because S3 would reject the signed request if\n * extra auth headers contradict the signature.\n */\nexport async function putBundleToUploadUrl(\n uploadUrl: string,\n body: Uint8Array,\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<void> {\n const impl: FetchLike = opts.fetchImpl ?? ((i, init) => fetch(i, init));\n // BodyInit's Uint8Array overload requires `ArrayBufferView<ArrayBuffer>`,\n // so we explicitly view the bytes with a plain ArrayBuffer backing — same\n // trick used in `uploadMiniAppResource` above. A `Buffer` from readFile\n // may be backed by a `SharedArrayBuffer` which `BodyInit` rejects.\n const view = new Uint8Array(body.buffer as ArrayBuffer, body.byteOffset, body.byteLength);\n let res: Response;\n try {\n res = await impl(uploadUrl, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/zip' },\n body: view,\n });\n } catch (err) {\n throw new Error(`PUT to upload URL failed: ${(err as Error).message}`);\n }\n if (!res.ok) {\n const preview = await res.text().catch(() => '');\n throw new Error(`PUT to upload URL returned HTTP ${res.status}: ${preview.slice(0, 200)}`);\n }\n}\n\n// --- Bundle review / release / withdraw / test ---\n//\n// After a bundle is uploaded it sits in reviewStatus=PREPARE on the server.\n// To actually ship it, three further mutations may fire:\n//\n// POST /bundles/reviews body {deploymentId, releaseNotes, featureList?, screenshotImagePaths?}\n// → submits for Toss review. reviewAppBundle()\n// POST /bundles/reviews/withdrawal body {deploymentId}\n// → cancels an in-flight review. postWithdrawAppBundleReview()\n// POST /bundles/release body {deploymentId, contentImages?}\n// → flips an APPROVED bundle live in the marketplace. releaseAppBundle()\n//\n// Plus two read/test helpers:\n//\n// POST /bundles/test-push body {deploymentId} → sends a push so the uploader can open the build on their device\n// GET /bundles/test-links → returns per-device test URLs\n//\n// All four mutation bodies, plus the test-push trigger, were observed\n// via static analysis of the console's `index.ZIQgZB74.js` chunk\n// (2026-04-23). No live XHR was triggered for these from the CLI — they\n// are destructive write paths (release especially is visible to end\n// users) and must be dog-fooded with care.\n\nexport interface ReviewAppBundleParams {\n readonly workspaceId: number;\n readonly miniAppId: number;\n readonly deploymentId: string;\n readonly releaseNotes: string;\n readonly featureList?: readonly Readonly<Record<string, unknown>>[];\n readonly screenshotImagePaths?: readonly string[];\n}\n\nexport async function postBundleReview(\n params: ReviewAppBundleParams,\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<Readonly<Record<string, unknown>>> {\n const url = `${BASE}/workspaces/${params.workspaceId}/mini-app/${params.miniAppId}/bundles/reviews`;\n const body: Record<string, unknown> = {\n deploymentId: params.deploymentId,\n releaseNotes: params.releaseNotes,\n };\n if (params.featureList !== undefined) body.featureList = params.featureList;\n if (params.screenshotImagePaths !== undefined)\n body.screenshotImagePaths = params.screenshotImagePaths;\n const raw = await requestConsoleApi<unknown>({\n method: 'POST',\n url,\n body,\n cookies,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) return {};\n return raw as Record<string, unknown>;\n}\n\nexport async function postBundleReviewWithdrawal(\n workspaceId: number,\n miniAppId: number,\n deploymentId: string,\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<Readonly<Record<string, unknown>>> {\n const url = `${BASE}/workspaces/${workspaceId}/mini-app/${miniAppId}/bundles/reviews/withdrawal`;\n const raw = await requestConsoleApi<unknown>({\n method: 'POST',\n url,\n body: { deploymentId },\n cookies,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) return {};\n return raw as Record<string, unknown>;\n}\n\nexport interface ReleaseAppBundleParams {\n readonly workspaceId: number;\n readonly miniAppId: number;\n readonly deploymentId: string;\n readonly contentImages?: readonly string[];\n}\n\nexport async function postBundleRelease(\n params: ReleaseAppBundleParams,\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<Readonly<Record<string, unknown>>> {\n const url = `${BASE}/workspaces/${params.workspaceId}/mini-app/${params.miniAppId}/bundles/release`;\n const body: Record<string, unknown> = { deploymentId: params.deploymentId };\n if (params.contentImages !== undefined) body.contentImages = params.contentImages;\n const raw = await requestConsoleApi<unknown>({\n method: 'POST',\n url,\n body,\n cookies,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) return {};\n return raw as Record<string, unknown>;\n}\n\nexport async function postBundleTestPush(\n workspaceId: number,\n miniAppId: number,\n deploymentId: string,\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<Readonly<Record<string, unknown>>> {\n const url = `${BASE}/workspaces/${workspaceId}/mini-app/${miniAppId}/bundles/test-push`;\n const raw = await requestConsoleApi<unknown>({\n method: 'POST',\n url,\n body: { deploymentId },\n cookies,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) return {};\n return raw as Record<string, unknown>;\n}\n\nexport async function fetchBundleTestLinks(\n workspaceId: number,\n miniAppId: number,\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<Readonly<Record<string, unknown>>> {\n const url = `${BASE}/workspaces/${workspaceId}/mini-app/${miniAppId}/bundles/test-links`;\n const raw = await requestConsoleApi<unknown>({\n url,\n cookies,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) return {};\n return raw as Record<string, unknown>;\n}\n\n// --- Register (create) ---\n//\n// `createMiniApp` and `uploadMiniAppResource` back the `app register`\n// command. The submit payload shape below is *inferred* from static\n// bundle analysis (`VALIDATION-RULES.md` in the local `.playwright-\n// mcp/`); the console UI never round-trips intermediate drafts, so the\n// only authoritative record will come from dog-food task #23. Field\n// names here intentionally mirror the `Xc` function from the bundle so\n// when #23 runs, any correction is a direct rename rather than a\n// restructure.\n\n// Exposed as `unknown` per-image so the caller (not this layer) is the\n// place where a cross-field invariant like \"at least 3 PREVIEW/VERTICAL\"\n// is enforced. Keeping this type open also makes it trivial to add the\n// `LOGO` imageType or other orientation values without touching the\n// network module.\nexport type MiniAppImageType = 'LOGO' | 'THUMBNAIL' | 'PREVIEW';\nexport type MiniAppImageOrientation = 'HORIZONTAL' | 'VERTICAL';\n\nexport interface MiniAppImageEntry {\n readonly imageUrl: string;\n readonly imageType: MiniAppImageType;\n readonly orientation: MiniAppImageOrientation;\n}\n\nexport interface MiniAppSubmitPayload {\n readonly miniApp: {\n readonly title: string;\n readonly titleEn: string;\n readonly appName: string;\n readonly iconUri: string;\n readonly status: 'PREPARE';\n readonly darkModeIconUri?: string;\n readonly homePageUri?: string;\n readonly csEmail: string;\n readonly description: string; // subtitle (≤20 chars)\n readonly detailDescription: string;\n readonly images: readonly MiniAppImageEntry[];\n };\n readonly impression: {\n readonly keywordList: readonly string[];\n readonly categoryIds: readonly number[];\n readonly subCategoryIds?: readonly number[];\n };\n}\n\nexport interface CreateMiniAppResult {\n readonly miniAppId: string | number | undefined;\n readonly reviewState: string | undefined;\n readonly extra: Readonly<Record<string, unknown>>;\n}\n\nexport async function createMiniApp(\n workspaceId: number,\n payload: MiniAppSubmitPayload,\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<CreateMiniAppResult> {\n const url = `${BASE}/workspaces/${workspaceId}/mini-app/review`;\n const raw = await requestConsoleApi<unknown>({\n url,\n method: 'POST',\n cookies,\n body: payload,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n return normalizeCreateResult(raw);\n}\n\nfunction normalizeCreateResult(raw: unknown): CreateMiniAppResult {\n if (raw === null || typeof raw !== 'object') {\n return { miniAppId: undefined, reviewState: undefined, extra: {} };\n }\n const rec = raw as Record<string, unknown>;\n const rawId = rec.miniAppId ?? rec.id ?? rec.appId;\n const miniAppId = typeof rawId === 'string' || typeof rawId === 'number' ? rawId : undefined;\n const rawState = rec.reviewState ?? rec.status;\n const reviewState = typeof rawState === 'string' ? rawState : undefined;\n return { miniAppId, reviewState, extra: rec };\n}\n\nexport interface UploadFile {\n readonly buffer: Buffer;\n readonly fileName: string;\n readonly contentType: string;\n}\n\nexport interface UploadParams {\n readonly workspaceId: number;\n readonly validWidth: number;\n readonly validHeight: number;\n readonly file: UploadFile;\n readonly cookies: readonly CdpCookie[];\n}\n\n/**\n * Upload an image to `/resource/:wid/upload?validWidth=W&validHeight=H`\n * and return the CDN URL the server hands back. The endpoint is a\n * multipart/form-data POST; we build a FormData with a single `resource`\n * field because that matches the bundle analysis for the console's\n * uploader, which pairs a `fileName` string field with a `resource`\n * Blob (see VALIDATION-RULES.md → iconUri). Dog-food #23 may reveal that\n * the field name is actually `file` — if so, swap it in one place here.\n */\nexport async function uploadMiniAppResource(\n params: UploadParams,\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<string> {\n const url = new URL(`${BASE}/resource/${params.workspaceId}/upload`);\n url.searchParams.set('validWidth', String(params.validWidth));\n url.searchParams.set('validHeight', String(params.validHeight));\n\n const form = new FormData();\n // A `Buffer` is already a `Uint8Array`, but its `ArrayBufferLike`\n // backing can be a `SharedArrayBuffer` which `BlobPart` doesn't accept.\n // Wrapping in a `Uint8Array` view over the same bytes (byteOffset +\n // byteLength) keeps the type happy without the extra copy that\n // `new Uint8Array(buffer)` would force.\n const view = new Uint8Array(\n params.file.buffer.buffer as ArrayBuffer,\n params.file.buffer.byteOffset,\n params.file.buffer.byteLength,\n );\n const blob = new Blob([view], { type: params.file.contentType });\n form.append('resource', blob, params.file.fileName);\n form.append('fileName', params.file.fileName);\n\n const cookieHeader = cookieHeaderFor(url, params.cookies);\n const headers: Record<string, string> = {\n Accept: 'application/json, text/plain, */*',\n };\n if (cookieHeader) headers.Cookie = cookieHeader;\n\n const imageUrl = await executeAndUnwrap<unknown>(\n url,\n { method: 'POST', headers, body: form },\n opts.fetchImpl,\n );\n if (typeof imageUrl !== 'string') {\n throw new MalformedResponseError(\n url.toString(),\n 200,\n `expected string imageUrl, got ${typeof imageUrl}`,\n );\n }\n return imageUrl;\n}\n","// Centralized exit codes so every command and the agent-plugin side agree.\n\nexport const ExitCode = {\n Ok: 0,\n Generic: 1,\n Usage: 2,\n NotAuthenticated: 10,\n NetworkError: 11,\n LoginTimeout: 12,\n // Reserved historical slot (was LoginStateMismatch under the OAuth\n // callback flow). Unused by the CDP login path but kept stable so the\n // agent-plugin side doesn't need to renumber.\n LoginStateMismatch: 13,\n LoginBrowserNotFound: 14,\n LoginBrowserFailed: 15,\n LoginCookieCaptureFailed: 16,\n ApiError: 17,\n UpgradeUnavailable: 20,\n UpgradeAlreadyLatest: 21,\n // SHA-256 verification of a downloaded upgrade asset failed (bad sum,\n // missing entry, or no SHA256SUMS asset on the release).\n UpgradeChecksumFailed: 22,\n // New binary downloaded and verified, but `--version` smoke test after\n // atomic replace failed (timeout, non-zero exit, or empty stdout). Rollback\n // to the previous binary is attempted automatically; the JSON payload\n // reports `rolledBack: true` on success or `rollbackError` on failure.\n UpgradeSmokeTestFailed: 23,\n} as const;\n\nexport type ExitCode = (typeof ExitCode)[keyof typeof ExitCode];\n","// Flush-safe exit: drain stdout before calling `process.exit` so a piped\n// consumer never loses the final JSON line. Callers typically write the\n// JSON payload (or plain-text result) to stdout immediately before\n// calling `return exitAfterFlush(code)`.\n\nexport async function exitAfterFlush(code: number): Promise<never> {\n await new Promise<void>((resolve) => process.stdout.write('', () => resolve()));\n process.exit(code);\n}\n","import { homedir } from 'node:os';\nimport { join } from 'node:path';\n\n// Resolve the config directory following the XDG Base Directory spec on\n// POSIX systems and using %APPDATA% on Windows. Falls back gracefully if\n// environment variables are missing (e.g. minimal containers without HOME).\n\nconst APP_NAME = 'aitcc';\n\nexport function configDir(): string {\n if (process.platform === 'win32') {\n const appData = process.env.APPDATA;\n if (appData && appData.length > 0) return join(appData, APP_NAME);\n return join(homedir() || '.', 'AppData', 'Roaming', APP_NAME);\n }\n const xdg = process.env.XDG_CONFIG_HOME;\n if (xdg && xdg.length > 0) return join(xdg, APP_NAME);\n return join(homedir() || '.', '.config', APP_NAME);\n}\n\nexport function sessionFilePath(): string {\n return join(configDir(), 'session.json');\n}\n\n// Pointer file for credential state. Stores the email of the credential\n// currently active in the OS keychain (so the next `aitcc login` can look\n// it up without prompting). NEVER contains the password — that lives in\n// the keychain, keyed by this email. Distinct from session.json on\n// purpose: a session has volatile cookies, credentials are durable.\nexport function authStateFilePath(): string {\n return join(configDir(), 'auth-state.json');\n}\n\n// Cache directory — for non-secret, regenerable state. Distinct from\n// `configDir()` so a `rm -rf ~/.cache/aitcc` cannot take the session with\n// it, and so packagers can mount the two on different volumes.\nexport function cacheDir(): string {\n if (process.platform === 'win32') {\n const localAppData = process.env.LOCALAPPDATA;\n if (localAppData && localAppData.length > 0) return join(localAppData, APP_NAME, 'Cache');\n return join(homedir() || '.', 'AppData', 'Local', APP_NAME, 'Cache');\n }\n const xdg = process.env.XDG_CACHE_HOME;\n if (xdg && xdg.length > 0) return join(xdg, APP_NAME);\n return join(homedir() || '.', '.cache', APP_NAME);\n}\n\nexport function upgradeCheckPath(): string {\n return join(cacheDir(), 'upgrade-check.json');\n}\n","import { chmod, mkdir, readFile, unlink, writeFile } from 'node:fs/promises';\nimport { dirname } from 'node:path';\nimport type { CdpCookie } from './cdp.js';\nimport { configDir, sessionFilePath } from './paths.js';\n\n// Minimal, forward-compatible session shape. `cookies` mirrors the CDP\n// `Network.getAllCookies` payload so the login command can drop it in\n// directly and the http layer can replay it against the console API.\n//\n// SECURITY: this module is the only place that touches the secret material.\n// - Never log raw cookies / origins.\n// - Treat file IO errors as \"no session\" in user-facing commands.\n\nexport interface SessionUser {\n id: string;\n email: string;\n displayName?: string;\n}\n\nexport interface Session {\n schemaVersion: 2;\n user: SessionUser;\n // CDP-native cookie list from `Network.getAllCookies`. Treat as opaque\n // secret material outside the login/http code paths.\n cookies: readonly CdpCookie[];\n // Reserved for Playwright `storageState`-style `localStorage` snapshots;\n // empty until a feature needs it.\n origins: unknown[];\n capturedAt: string; // ISO-8601\n // Workspace context. Unset until the user runs `aitcc workspace use <id>`\n // or provides `--workspace` on first use. Writes are explicit — we never\n // guess a default (e.g. \"first workspace the user has access to\") because\n // a silent guess is exactly the class of bug that causes a deploy to land\n // in the wrong account.\n currentWorkspaceId?: number;\n}\n\n// Public-safe projection for `whoami` and other diagnostics.\nexport interface SessionSummary {\n user: SessionUser;\n capturedAt: string;\n}\n\nfunction summarize(session: Session): SessionSummary {\n return { user: session.user, capturedAt: session.capturedAt };\n}\n\n/**\n * Read the persisted session. Returns `null` when no session exists, when\n * the file is corrupt, or when the shape fails validation — each of those\n * emits a one-line warning on stderr for diagnostics.\n *\n * **`AITCC_SESSION` env precedence**: when the env var is set with a valid\n * blob (raw JSON or base64-encoded JSON), this function returns it directly\n * and never touches the session file. This is the read path for the CI\n * single-shot flow seeded by `aitcc auth export`. Invalid env content\n * falls back to the file with a one-shot warning so a typo doesn't\n * silently strand a CI run.\n *\n * **Side effect**: a v1 session file is transparently rewritten to v2 on\n * the first successful read of this process. This keeps read-only callers\n * (`whoami`, `workspace ls`) from stranding users on an old schema. If the\n * rewrite fails, we warn once per process and continue with the in-memory\n * v2 value so the calling command still succeeds. The env path performs\n * the same v1 → v2 upgrade in memory only — env mode never writes.\n */\nexport async function readSession(): Promise<Session | null> {\n const fromEnv = readSessionFromEnv();\n if (fromEnv !== undefined) return fromEnv;\n const path = sessionFilePath();\n let raw: string;\n try {\n raw = await readFile(path, 'utf8');\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code === 'ENOENT') return null;\n // Some other IO error — surface one-line diagnostic on stderr so the\n // user can tell \"permission denied\" from \"no session\". The command\n // still falls back to \"not logged in\" behaviour.\n process.stderr.write(`warning: could not read session file at ${path}: ${code ?? 'unknown'}\\n`);\n return null;\n }\n let rawParsed: unknown;\n try {\n rawParsed = JSON.parse(raw);\n } catch {\n // Malformed JSON — warn once, then fall back to \"not logged in\". The\n // user can re-run `aitcc login` to replace the broken file.\n process.stderr.write(`warning: session file at ${path} is corrupt and will be ignored\\n`);\n return null;\n }\n const schemaReason = validateSessionShape(rawParsed);\n if (schemaReason) {\n process.stderr.write(\n `warning: session file at ${path} ignored (${schemaReason}); re-run \\`aitcc login\\`\\n`,\n );\n return null;\n }\n // Post-validation: the shape is trusted. `schemaVersion` is now 1 or 2;\n // v1 files are transparently upgraded to v2 in memory. We best-effort\n // rewrite the file so long-lived v1-on-disk sessions eventually migrate\n // without requiring the user to run a write-shaped command. A failed\n // rewrite is non-fatal (the in-memory shape is correct) but we still\n // emit a one-line stderr warning once per process so a read-only mount\n // or permission issue does not silently persist. We await rather than\n // fire-and-forget so concurrent callers observe consistent on-disk state.\n const validated = rawParsed as { schemaVersion: 1 | 2 } & Omit<Session, 'schemaVersion'>;\n if (validated.schemaVersion === 1) {\n const upgraded: Session = { ...validated, schemaVersion: 2 };\n try {\n await writeSession(upgraded);\n } catch (err) {\n warnMigrationOnce(path, (err as NodeJS.ErrnoException).code);\n }\n return upgraded;\n }\n return validated as Session;\n}\n\n// One-shot latch so a failing migration doesn't spam stderr on every call\n// inside the same process. Users still get the diagnostic the first time.\nlet migrationWarned = false;\nfunction warnMigrationOnce(path: string, code: string | undefined): void {\n if (migrationWarned) return;\n migrationWarned = true;\n process.stderr.write(\n `warning: could not migrate session file at ${path} to schemaVersion 2: ${code ?? 'unknown'}\\n`,\n );\n}\n\n// One-shot latches so we don't spam stderr when many commands read the\n// session in a single process (e.g. ctx resolver + http call + diagnostics).\nlet envFallbackWarned = false;\nlet envWriteWarned = false;\n\n/**\n * Test-only: reset the one-shot stderr warning latches. Production\n * code should never call this — the latches exist precisely so a\n * single CLI invocation that reads the session many times only warns\n * once. The shared vitest process keeps modules alive across tests so\n * a latch tripped by an earlier case would suppress the warning the\n * later case asserts on.\n */\nexport function __resetSessionWarningsForTests(): void {\n envFallbackWarned = false;\n envWriteWarned = false;\n migrationWarned = false;\n}\n\n/**\n * Decode `AITCC_SESSION` env var into a `Session`. Returns:\n * - `undefined` when the env var is unset (caller falls through to file).\n * - `Session` when set + valid (caller uses it, ignores file).\n * - `null` when set but malformed/invalid; emits a one-shot stderr warning\n * and the caller falls through to the file path. We use `null` here as\n * \"tried env, gave up\" so the file path still runs — the goal is to\n * never strand a developer who accidentally exported garbage but has a\n * working session file underneath.\n *\n * The blob may be either raw JSON or base64-encoded JSON; we autodetect by\n * peeking at the decoded body for a leading `{`. This matches the symmetry\n * `auth export` ↔ `auth import` even when a user hand-edits a secret.\n */\nfunction readSessionFromEnv(): Session | null | undefined {\n const raw = process.env.AITCC_SESSION;\n if (!raw || raw.length === 0) return undefined;\n const decoded = decodeSessionBlob(raw);\n if (decoded === null) {\n warnEnvFallbackOnce('AITCC_SESSION env is set but not valid JSON');\n return undefined;\n }\n const reason = validateSessionShape(decoded);\n if (reason) {\n warnEnvFallbackOnce(`AITCC_SESSION env ignored (${reason})`);\n return undefined;\n }\n const validated = decoded as { schemaVersion: 1 | 2 } & Omit<Session, 'schemaVersion'>;\n // v1 → v2 upgrade in memory only — env mode is read-only by contract.\n return validated.schemaVersion === 1\n ? { ...validated, schemaVersion: 2 }\n : (validated as Session);\n}\n\n/**\n * True iff `AITCC_SESSION` is set AND parses+validates as a session.\n * The write/clear no-ops MUST gate on this rather than on\n * `process.env.AITCC_SESSION` truthiness alone — otherwise a corrupted\n * env blob silently swallows `workspace use` writes even though\n * `readSession()` correctly fell back to the file. Callers in this\n * module use it instead of inlining the check; `auth import` does NOT\n * call this (it should always write when invoked, see `forceWrite`).\n */\nfunction envSessionActive(): boolean {\n return readSessionFromEnv() !== undefined;\n}\n\nfunction warnEnvFallbackOnce(message: string): void {\n if (envFallbackWarned) return;\n envFallbackWarned = true;\n process.stderr.write(`warning: ${message}; falling back to session file\\n`);\n}\n\nfunction warnEnvWriteOnce(): void {\n if (envWriteWarned) return;\n envWriteWarned = true;\n process.stderr.write('warning: AITCC_SESSION env active — session updates not persisted\\n');\n}\n\n/**\n * Best-effort blob decoder shared by `readSessionFromEnv` and the\n * `auth import` command. Tries base64 first; if the result doesn't look\n * like JSON, falls back to treating the input as raw JSON. Returns the\n * parsed value on success, `null` on parse failure.\n */\nexport function decodeSessionBlob(raw: string): unknown {\n const trimmed = raw.trim();\n if (trimmed.length === 0) return null;\n // Direct JSON: cheap path, no base64 round-trip.\n if (trimmed.startsWith('{')) {\n try {\n return JSON.parse(trimmed);\n } catch {\n return null;\n }\n }\n // Try base64 → JSON.\n let decoded: string;\n try {\n decoded = Buffer.from(trimmed, 'base64').toString('utf8');\n } catch {\n decoded = '';\n }\n const decodedTrim = decoded.trim();\n if (decodedTrim.startsWith('{')) {\n try {\n return JSON.parse(decodedTrim);\n } catch {\n return null;\n }\n }\n return null;\n}\n\n/**\n * Validate a candidate session blob. Returns `null` on success or a\n * short reason string on failure. Exported so `auth import` can run the\n * exact same validation `readSession` uses, eliminating drift between\n * the two write paths.\n */\nexport function validateSessionBlob(input: unknown): string | null {\n return validateSessionShape(input);\n}\n\n/**\n * Normalise a validated blob to a `Session` (auto-upgrading v1 → v2).\n * Caller must have already passed the value through `validateSessionBlob`.\n */\nexport function normalizeValidatedSession(input: unknown): Session {\n const validated = input as { schemaVersion: 1 | 2 } & Omit<Session, 'schemaVersion'>;\n return validated.schemaVersion === 1\n ? { ...validated, schemaVersion: 2 }\n : (validated as Session);\n}\n\n// v1 → v2 migration: v1 files are still valid, we just treat the absent\n// `currentWorkspaceId` as \"no workspace selected yet\". The next write (e.g.\n// from `workspace use`) bumps the stored schemaVersion. The validator input\n// is `unknown` so we can inspect raw JSON without the TS type narrowing\n// away the v1 branch.\nfunction validateSessionShape(input: unknown): string | null {\n if (input === null || typeof input !== 'object') return 'root is not an object';\n const parsed = input as {\n schemaVersion?: unknown;\n user?: { id?: unknown; email?: unknown; displayName?: unknown };\n cookies?: unknown;\n origins?: unknown;\n capturedAt?: unknown;\n currentWorkspaceId?: unknown;\n };\n if (parsed.schemaVersion !== 1 && parsed.schemaVersion !== 2) {\n return `unknown schemaVersion ${String(parsed.schemaVersion)}`;\n }\n if (!parsed.user || typeof parsed.user.id !== 'string') return 'missing user.id';\n if (typeof parsed.user.email !== 'string') return 'missing user.email';\n if (parsed.user.displayName !== undefined && typeof parsed.user.displayName !== 'string') {\n return 'user.displayName has wrong type';\n }\n if (!Array.isArray(parsed.cookies)) return 'cookies is not an array';\n if (parsed.origins !== undefined && !Array.isArray(parsed.origins)) {\n return 'origins is not an array';\n }\n if (parsed.capturedAt !== undefined && typeof parsed.capturedAt !== 'string') {\n return 'capturedAt has wrong type';\n }\n if (parsed.currentWorkspaceId !== undefined) {\n const wid = parsed.currentWorkspaceId;\n if (typeof wid !== 'number' || !Number.isInteger(wid) || wid <= 0) {\n return 'currentWorkspaceId has wrong type';\n }\n }\n return null;\n}\n\nexport async function readSessionSummary(): Promise<SessionSummary | null> {\n const s = await readSession();\n return s ? summarize(s) : null;\n}\n\nexport interface WriteSessionOptions {\n /**\n * Force the write even when `AITCC_SESSION` env is active. Only\n * `auth import` should set this — the user explicitly asked us to\n * persist, and the env no-op would otherwise swallow that intent.\n * Bypasses the env mutation hack the previous revision relied on.\n */\n readonly forceWrite?: boolean;\n}\n\nexport async function writeSession(\n session: Session,\n options: WriteSessionOptions = {},\n): Promise<void> {\n // CI single-shot mode (`AITCC_SESSION` env active and valid) is\n // read-only by contract — silently creating or overwriting a file\n // would defeat the 0600 guarantee on hosts that don't expect a\n // persistent session and leave a stale blob behind on shared runners.\n // One-shot warn so a misuse (`workspace use` on a CI box) is visible\n // without spamming. Gated on the same \"set and valid\" predicate that\n // `readSession` uses so a corrupted env blob doesn't silently swallow\n // a legitimate write.\n if (!options.forceWrite && envSessionActive()) {\n warnEnvWriteOnce();\n return;\n }\n const dir = dirname(sessionFilePath());\n await mkdir(dir, { recursive: true, mode: 0o700 });\n await writeFile(sessionFilePath(), JSON.stringify(session, null, 2), {\n mode: 0o600,\n });\n // writeFile's mode only applies on creation; tighten existing files too.\n try {\n await chmod(sessionFilePath(), 0o600);\n } catch {\n // Windows / exotic FS: best-effort only.\n }\n}\n\n/**\n * Persist a new `currentWorkspaceId` on an existing session. Returns the\n * updated session, or `null` if there is no session to update (callers\n * should surface \"not logged in\" in that case).\n */\nexport async function setCurrentWorkspaceId(workspaceId: number): Promise<Session | null> {\n const session = await readSession();\n if (!session) return null;\n const updated: Session = { ...session, currentWorkspaceId: workspaceId };\n await writeSession(updated);\n return updated;\n}\n\nexport async function clearSession(): Promise<{ existed: boolean }> {\n // Env mode is read-only — pretend we cleared, but warn so the operator\n // knows the AITCC_SESSION secret in their pipeline still authenticates\n // the next command. (Symmetric with `writeSession` above; same \"set and\n // valid\" gate so a corrupted env blob doesn't suppress a legitimate\n // logout against the underlying file.)\n if (envSessionActive()) {\n warnEnvWriteOnce();\n return { existed: false };\n }\n try {\n await unlink(sessionFilePath());\n return { existed: true };\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code === 'ENOENT') return { existed: false };\n throw err;\n }\n}\n\nexport function sessionPathForDiagnostics(): string {\n return sessionFilePath();\n}\n\nexport function configDirForDiagnostics(): string {\n return configDir();\n}\n","// Map console-API `errorCode` values into actionable user messages.\n//\n// Two shapes coexist on the wire (envelope: `{resultType:'FAIL', error:\n// {errorCode, reason, ...}}`):\n//\n// 1. Numeric strings — `\"4046\"`, `\"4032\"`, `\"4010\"`. Catalogued in\n// `docs/api/_error-codes.md`. CLI does not rewrite these; the raw\n// `error.reason` is already a Korean sentence the console UI shows\n// verbatim, and we surface the same string so behaviour matches what\n// a user sees in the browser. This module returns `null` for that\n// case so callers fall back to the existing message.\n//\n// 2. Prefix-form (`<camelCaseDomain>.<PascalCaseReason>`) — e.g.\n// `\"miniApp.InvalidTitle\"` from `POST /mini-app/review`. Discovered\n// during sdk-example#39 dog-food. The accompanying `reason` is\n// sometimes generic (\"앱 영문 이름은 영어, 숫자, 공백, 콜론(:)만 사용\n// 가능해요\") and the LLM consumer in agent-plugin has no way to know\n// the rule from the code alone. For the small set we've directly\n// observed, surface a single actionable line. Unknown prefix codes\n// pass through with the code embedded in the message so a downstream\n// reader at least sees the dotted identifier rather than just a raw\n// reason — they get added to the catalog when next observed.\n//\n// The numeric/prefix split mirrors the table layout in\n// `docs/api/_error-codes.md` exactly so the doc and the code stay in sync;\n// `_error-codes.md` is the source of truth for which prefix codes are\n// known.\n\n// `<camelCaseDomain>.<PascalCaseReason>`. The reason segment requires at\n// least two characters (one uppercase + one more) — every prefix code we\n// have observed in console responses has a multi-character reason, and\n// matching a single uppercase letter would also accept things like\n// `module.X` that look more like internal logger tags than user-visible\n// errorCodes. If a real one-character reason ever shows up we'll widen\n// this to `[a-zA-Z0-9]*`.\nconst PREFIX_ERROR_CODE_PATTERN = /^[a-z][a-zA-Z0-9]*\\.[A-Z][a-zA-Z0-9]+$/;\n\nexport function isPrefixFormErrorCode(code: string): boolean {\n return PREFIX_ERROR_CODE_PATTERN.test(code);\n}\n\n// Known prefix codes mapped to a one-line action sentence. Keep entries\n// terse (no leading bullet, no trailing period) — the caller wraps them\n// into a longer string with the raw code/reason.\n//\n// Only add an entry once the code has been observed end-to-end with a\n// known fix. Speculation belongs in a TODO, not here — the unknown-prefix\n// path already surfaces the code so a user can search the catalog.\nconst KNOWN_PREFIX_MESSAGES: Record<string, string> = {\n 'miniApp.InvalidTitle':\n 'titleKo violates the server rule (≤ 10 code points excluding spaces, only Korean/English letters, digits, spaces, and \":·?\"). Edit `titleKo` in aitcc.yaml.',\n 'miniApp.InvalidTitleEn':\n 'titleEn violates the server rule (≤ 15 code points excluding spaces, only English letters/digits/spaces/`:·?`, and each word title-case — first letter uppercase, rest lowercase; `AITC`-style all-caps tokens are rejected). Edit `titleEn` in aitcc.yaml.',\n};\n\nexport interface DescribeApiErrorInput {\n /** Raw `error.errorCode` from the Toss envelope (may be any string). */\n readonly errorCode: string | undefined;\n /** Raw `error.reason` from the Toss envelope. */\n readonly reason: string | undefined;\n /**\n * The fallback message the caller would have used absent prefix mapping\n * (typically `TossApiError.message`). Returned verbatim when the code is\n * empty or numeric so existing behaviour is preserved.\n */\n readonly fallback: string;\n}\n\n/**\n * Compose the user-visible message for a Toss API error.\n *\n * Behaviour by code shape:\n *\n * - **numeric / null / non-matching shape**: returns `fallback` verbatim.\n * This preserves the pre-existing `TossApiError.message` text byte-for-\n * byte, so commands that already surface things like\n * `\"Toss API error 4046: 검수중인 요청이 있어 검수요청을 할 수 없어요 (HTTP 400)\"`\n * keep doing exactly that.\n *\n * - **known prefix code**: returns the mapped action sentence, with the\n * server `reason` appended in a `(server reason: …)` clause when\n * present. The `fallback` is intentionally NOT included — the mapped\n * sentence is self-contained and already names the field/file the user\n * needs to edit, so prepending the `Toss API error …` envelope text\n * would just bury the action.\n *\n * - **unknown prefix code**: returns `fallback` verbatim. `TossApiError`'s\n * own template already embeds the dotted identifier\n * (`Toss API error <code>: <reason> (HTTP <status>)`), so the code is\n * discoverable in logs without us double-wrapping it.\n *\n * `errorCode` itself is left to the caller — this only shapes `message`.\n */\nexport function describeApiError(input: DescribeApiErrorInput): string {\n const { errorCode, reason, fallback } = input;\n if (!errorCode) return fallback;\n if (!isPrefixFormErrorCode(errorCode)) return fallback;\n const mapped = KNOWN_PREFIX_MESSAGES[errorCode];\n if (mapped !== undefined) {\n const reasonSuffix = reason ? ` (server reason: ${reason})` : '';\n return `${mapped}${reasonSuffix}`;\n }\n // Unknown prefix code: defer to the caller's fallback. `TossApiError`\n // formats its `message` as `Toss API error <code>: <reason> (HTTP …)`,\n // which already exposes the dotted identifier — wrapping it in\n // `(<code>) …` here just duplicated the code without adding signal.\n return fallback;\n}\n","import { access, readFile, writeFile } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\nimport { isMap, parseDocument, parse as parseYaml } from 'yaml';\n\n// Project-level context (workspace + active mini-app) discovered by walking\n// from the cwd up to the nearest git repo root. Read by every app-scoped\n// command so the user does not have to repeat `--workspace` and `<appId>`\n// on each invocation. Distinct from `app-manifest.ts`: the manifest is the\n// `register`/`update` payload contract, while the project context is the\n// resolver input that picks *which* app/workspace a given command targets.\n\nconst DEFAULT_NAMES = ['aitcc.yaml', 'aitcc.json'] as const;\nconst MAX_HOPS = 32;\n\nexport interface ProjectContext {\n readonly workspaceId?: number;\n readonly miniAppId?: number;\n /** Absolute path of the file the context was read from (for diagnostics). */\n readonly source: string;\n}\n\nexport class ProjectContextError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'ProjectContextError';\n }\n}\n\nasync function fileExists(path: string): Promise<boolean> {\n try {\n await access(path);\n return true;\n } catch {\n return false;\n }\n}\n\nasync function findContextFile(startDir: string): Promise<string | null> {\n let dir = startDir;\n const home = process.env.HOME ?? process.env.USERPROFILE;\n for (let i = 0; i < MAX_HOPS; i++) {\n for (const name of DEFAULT_NAMES) {\n const candidate = join(dir, name);\n if (await fileExists(candidate)) return candidate;\n }\n // Stop walking when we hit a git repo boundary. `.git` may be a\n // directory (normal repo) or a file (submodule/worktree); `access`\n // succeeds for both.\n if (await fileExists(join(dir, '.git'))) return null;\n if (home && dir === home) return null;\n const parent = dirname(dir);\n if (parent === dir) return null;\n dir = parent;\n }\n return null;\n}\n\nfunction pickPositiveInt(input: Record<string, unknown>, key: string): number | undefined {\n const v = input[key];\n if (v === undefined || v === null) return undefined;\n if (typeof v !== 'number' || !Number.isInteger(v) || v <= 0) return undefined;\n return v;\n}\n\n/**\n * Walk from `cwd` upward looking for `aitcc.yaml`/`aitcc.json`. Returns the\n * parsed `ProjectContext` of the first match, or `null` when no file is\n * found. Walks halt at the enclosing `.git` boundary or `$HOME`, whichever\n * comes first.\n *\n * Throws `ProjectContextError` when a file is found but cannot be parsed —\n * a present-but-broken config is more useful as a hard error than as a\n * silent miss. Unknown / mistyped fields (`workspaceId`, `miniAppId` not\n * being a positive integer) are dropped silently while preserving `source`.\n */\nexport async function findProjectContext(cwd: string): Promise<ProjectContext | null> {\n const path = await findContextFile(cwd);\n if (!path) return null;\n const raw = await readFile(path, 'utf8');\n let parsed: unknown;\n try {\n parsed = path.toLowerCase().endsWith('.json') ? JSON.parse(raw) : parseYaml(raw);\n } catch (err) {\n const detail = err instanceof Error ? err.message : String(err);\n throw new ProjectContextError(`failed to parse project context at ${path}: ${detail}`);\n }\n if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {\n throw new ProjectContextError(`project context at ${path} is not a mapping`);\n }\n const obj = parsed as Record<string, unknown>;\n const workspaceId = pickPositiveInt(obj, 'workspaceId');\n const miniAppId = pickPositiveInt(obj, 'miniAppId');\n return {\n ...(workspaceId !== undefined ? { workspaceId } : {}),\n ...(miniAppId !== undefined ? { miniAppId } : {}),\n source: path,\n };\n}\n\nexport type WriteMiniAppIdOutcome =\n | { readonly status: 'written'; readonly path: string }\n | { readonly status: 'unchanged'; readonly path: string };\n\n/**\n * Persist `miniAppId` into an existing project-context file at `path`.\n *\n * - YAML files are updated via `parseDocument` so user comments, key\n * ordering, and quoting style survive the round-trip. Only the\n * `miniAppId` scalar is touched (added if absent, replaced if present).\n * - JSON files are parsed, mutated, and re-serialized with the indent the\n * file already used (detected from the first indented line, defaulting\n * to 2). The trailing newline is preserved if it was present.\n * - When the file already pins the same `miniAppId`, the write is a\n * no-op and `status` is `\"unchanged\"` so callers can suppress the\n * diagnostic line.\n *\n * Throws `ProjectContextError` when the file is missing, unparseable, or\n * not a top-level mapping. Caller is responsible for the \"no project\n * file at all\" case (`findProjectContext` returns `null`).\n */\nexport async function writeProjectMiniAppId(\n path: string,\n miniAppId: number,\n): Promise<WriteMiniAppIdOutcome> {\n if (!Number.isInteger(miniAppId) || miniAppId <= 0) {\n throw new ProjectContextError(`refusing to write non-positive-integer miniAppId: ${miniAppId}`);\n }\n let raw: string;\n try {\n raw = await readFile(path, 'utf8');\n } catch (err) {\n const detail = err instanceof Error ? err.message : String(err);\n throw new ProjectContextError(`failed to read project context at ${path}: ${detail}`);\n }\n const isJson = path.toLowerCase().endsWith('.json');\n const next = isJson ? rewriteJson(path, raw, miniAppId) : rewriteYaml(path, raw, miniAppId);\n if (next === null) return { status: 'unchanged', path };\n await writeFile(path, next, 'utf8');\n return { status: 'written', path };\n}\n\nfunction rewriteYaml(path: string, raw: string, miniAppId: number): string | null {\n const doc = parseDocument(raw);\n if (doc.errors.length > 0) {\n throw new ProjectContextError(\n `failed to parse project context at ${path}: ${doc.errors[0]?.message ?? 'parse error'}`,\n );\n }\n if (!isMap(doc.contents)) {\n throw new ProjectContextError(`project context at ${path} is not a mapping`);\n }\n const existing = doc.get('miniAppId');\n if (typeof existing === 'number' && existing === miniAppId) return null;\n doc.set('miniAppId', miniAppId);\n return doc.toString();\n}\n\nfunction rewriteJson(path: string, raw: string, miniAppId: number): string | null {\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (err) {\n const detail = err instanceof Error ? err.message : String(err);\n throw new ProjectContextError(`failed to parse project context at ${path}: ${detail}`);\n }\n if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {\n throw new ProjectContextError(`project context at ${path} is not a mapping`);\n }\n const obj = parsed as Record<string, unknown>;\n if (obj.miniAppId === miniAppId) return null;\n obj.miniAppId = miniAppId;\n const indent = detectJsonIndent(raw);\n const trailing = raw.endsWith('\\n') ? '\\n' : '';\n return JSON.stringify(obj, null, indent) + trailing;\n}\n\n// Returns the original file's indentation token so JSON.stringify can\n// reproduce it (number = spaces, string = literal token, e.g. '\\t').\n// `0` keeps a single-line / compact file compact instead of expanding\n// it to multi-line on a one-key edit. Default is two spaces, matching\n// the format examples in README and existing repo style.\nfunction detectJsonIndent(raw: string): number | string {\n if (!raw.includes('\\n')) return 0;\n const match = raw.match(/\\n([ \\t]+)\\S/);\n const token = match?.[1];\n if (!token) return 2;\n if (token.includes('\\t')) return '\\t';\n return token.length;\n}\n","import { describeApiError } from '../api/error-messages.js';\nimport { NetworkError, TossApiError } from '../api/http.js';\nimport { findProjectContext, type ProjectContext } from '../config/project-context.js';\nimport { ExitCode } from '../exit.js';\nimport { exitAfterFlush } from '../flush.js';\nimport { readSession, type Session, sessionPathForDiagnostics } from '../session.js';\n\n// Shared output helpers used by every session-scoped subcommand\n// (`workspace`, `app`, `members`, `keys`, and the in-flight `deploy`/`logs`).\n// Kept in one place so all commands agree on the `--json` contract — one\n// line, trailing \\n, stdout for structured output, stderr for diagnostics.\n//\n// Cross-cutting failure shapes (emitted from every session/app/workspace-\n// scoped command — per-command `--json contract` blocks may add their own\n// reasons on top, but these are universal):\n//\n// Auth/network/API:\n// { ok: true, authenticated: false } exit 10\n// { ok: false, reason: 'network-error', message } exit 11\n// { ok: false, reason: 'api-error', status?, errorCode?, message } exit 17\n//\n// Context resolution (any command that goes through\n// `resolveWorkspaceContext` / `resolveAppOrFail`):\n// { ok: false, reason: 'invalid-id', message } exit 2\n// (--workspace value, positional <appId>, or --app value malformed)\n// { ok: false, reason: 'invalid-env', message } exit 2\n// (AITCC_WORKSPACE or AITCC_APP env var contains a non-positive-int)\n// { ok: false, reason: 'no-workspace-selected' } exit 2\n// (no flag/env/yaml/session source supplied a workspace id)\n//\n// App-scoped commands additionally:\n// { ok: false, reason: 'missing-app-id', message } exit 2\n// (workspace resolved but no source supplied a miniApp id)\n//\n// See any per-command `--json contract` block (e.g. `commands/workspace.ts`)\n// for the success-shape specific to that command and any command-specific\n// reasons stacked on top of these.\n\nexport interface NotAuthenticatedPayload {\n readonly ok: true;\n readonly authenticated: false;\n readonly reason?: 'session-expired';\n}\n\nexport function emitJson(payload: unknown): void {\n process.stdout.write(`${JSON.stringify(payload)}\\n`);\n}\n\nexport function emitNotAuthenticated(json: boolean, reason?: 'session-expired'): void {\n if (json) {\n // `exactOptionalPropertyTypes` forbids `reason: undefined`, so we omit\n // the key entirely when we don't have a value — hence the branch\n // rather than a single object literal.\n const payload: NotAuthenticatedPayload = reason\n ? { ok: true, authenticated: false, reason }\n : { ok: true, authenticated: false };\n emitJson(payload);\n } else {\n process.stderr.write(\n reason === 'session-expired'\n ? 'Session is no longer valid. Run `aitcc login` again.\\n'\n : 'Not logged in. Run `aitcc login` to start a session.\\n',\n );\n process.stderr.write(`Session file checked: ${sessionPathForDiagnostics()}\\n`);\n }\n}\n\nexport function emitNetworkError(json: boolean, message: string): void {\n if (json) {\n emitJson({ ok: false, reason: 'network-error', message });\n } else {\n process.stderr.write(`Network error reaching the console API: ${message}.\\n`);\n }\n}\n\nexport function emitApiError(\n json: boolean,\n message: string,\n details?: { status?: number; errorCode?: string },\n): void {\n if (json) {\n emitJson({\n ok: false,\n reason: 'api-error',\n ...(details?.status !== undefined ? { status: details.status } : {}),\n ...(details?.errorCode !== undefined ? { errorCode: details.errorCode } : {}),\n message,\n });\n } else {\n process.stderr.write(`Unexpected error: ${message}\\n`);\n }\n}\n\n/**\n * Shared auth/network/api dispatch. Every session-scoped command's\n * `catch (err)` block boils down to the same sequence: TossApiError\n * (auth → exit 10, otherwise → exit 17 with status + errorCode),\n * NetworkError (exit 11), fallback (exit 17 with just a message).\n * Exists so we get a single source of truth for the api-error JSON\n * shape — previously each command duplicated the if/else ladder and\n * `register` diverged (it exposed `status`/`errorCode` that the others\n * didn't) until this extraction lined them up.\n *\n * Returns `Promise<void>` but never returns at runtime: every branch\n * awaits `exitAfterFlush` which calls `process.exit`.\n */\nexport async function emitFailureFromError(json: boolean, err: unknown): Promise<void> {\n if (err instanceof TossApiError && err.isAuthError) {\n emitNotAuthenticated(json, 'session-expired');\n return exitAfterFlush(ExitCode.NotAuthenticated);\n }\n if (err instanceof TossApiError) {\n const message = describeApiError({\n errorCode: err.errorCode,\n reason: err.reason,\n fallback: err.message,\n });\n emitApiError(json, message, { status: err.status, errorCode: err.errorCode });\n return exitAfterFlush(ExitCode.ApiError);\n }\n if (err instanceof NetworkError) {\n emitNetworkError(json, err.message);\n return exitAfterFlush(ExitCode.NetworkError);\n }\n emitApiError(json, (err as Error).message);\n return exitAfterFlush(ExitCode.ApiError);\n}\n\n// Parse a CLI-provided workspace id strictly: only the form `^[1-9]\\d*$`\n// is accepted. `Number.parseInt('36577x', 10)` returns 36577, so the CLI\n// would otherwise silently accept `workspace use 36577x` and persist the\n// wrong thing on a typo. Returning `null` triggers the caller's usage-error\n// path. Exported so unit tests can guard against \"just use parseInt\"\n// simplification regressions.\nexport function parsePositiveInt(raw: string): number | null {\n if (!/^[1-9]\\d*$/.test(raw)) return null;\n const n = Number.parseInt(raw, 10);\n return Number.isSafeInteger(n) ? n : null;\n}\n\n/**\n * Boilerplate wrapper for any workspace-scoped command (`app ls`,\n * `members ls`, `keys ls`, ...). Loads the session, resolves the workspace\n * id through the full priority chain (`--workspace` flag → `AITCC_WORKSPACE`\n * env → `aitcc.yaml` → persisted selection), and handles the common failure\n * branches (`no session`, `invalid id`, `invalid env`, `no workspace\n * selected`). On success, the caller gets the session + resolved id +\n * source bookkeeping back so it can `printContextHeader` consistently.\n *\n * The return type is `Promise<... | null>` but the `null` branch is never\n * observed at runtime: every failure path `await`s `exitAfterFlush` which\n * calls `process.exit(...)` and doesn't return. The `| null` is a type-\n * level handshake that forces callers to add `if (!ctx) return;`, keeping\n * the bail-out readable.\n */\nexport async function resolveWorkspaceContext(args: {\n workspace?: string | undefined;\n json: boolean;\n}): Promise<\n | (AppContext & {\n session: Session;\n })\n | null\n> {\n const session = await readSession();\n if (!session) {\n emitNotAuthenticated(args.json);\n await exitAfterFlush(ExitCode.NotAuthenticated);\n return null;\n }\n\n let flagWorkspaceId: number | undefined;\n if (args.workspace) {\n const raw = String(args.workspace);\n const parsed = parsePositiveInt(raw);\n if (parsed === null) {\n const message = `--workspace must be a positive integer (got ${raw})`;\n if (args.json) emitJson({ ok: false, reason: 'invalid-id', message });\n else process.stderr.write(`${message}\\n`);\n await exitAfterFlush(ExitCode.Usage);\n return null;\n }\n flagWorkspaceId = parsed;\n }\n\n let app: AppContext;\n try {\n app = await resolveAppContext({\n ...(flagWorkspaceId !== undefined ? { flagWorkspaceId } : {}),\n ...(session.currentWorkspaceId !== undefined\n ? { sessionWorkspaceId: session.currentWorkspaceId }\n : {}),\n });\n } catch (err) {\n await emitAppContextErrorAndExit(args.json, err);\n return null;\n }\n\n return { session, ...app };\n}\n\n/**\n * Session-only sibling of `resolveWorkspaceContext` for commands that\n * don't need a workspace id (notices come from a shared Toss workspace,\n * whoami is self-scoped). Same \"exits on miss, returns null to force\n * `if (!session) return`\" pattern.\n */\nexport async function requireSession(json: boolean): Promise<Session | null> {\n const session = await readSession();\n if (!session) {\n emitNotAuthenticated(json);\n await exitAfterFlush(ExitCode.NotAuthenticated);\n return null;\n }\n return session;\n}\n\nexport type ContextSource = 'flag' | 'env' | 'yaml' | 'session';\n\nexport interface AppContext {\n readonly workspaceId: number;\n readonly miniAppId?: number;\n readonly workspaceSource: ContextSource;\n readonly miniAppIdSource?: ContextSource;\n /** Path of the yaml that contributed to the resolution, if any. */\n readonly projectFile?: string;\n}\n\nexport interface ResolveAppContextInput {\n /** Value from `--workspace <id>` (already parsed by the command). */\n readonly flagWorkspaceId?: number;\n /** Value from a positional `<appId>` (or equivalent flag). */\n readonly flagMiniAppId?: number;\n /** Persisted `currentWorkspaceId`, if a session is loaded. */\n readonly sessionWorkspaceId?: number;\n /** Override for tests; defaults to `process.cwd()`. */\n readonly cwd?: string;\n}\n\nexport class AppContextError extends Error {\n readonly reason: 'invalid-env' | 'no-workspace-selected';\n constructor(reason: 'invalid-env' | 'no-workspace-selected', message: string) {\n super(message);\n this.name = 'AppContextError';\n this.reason = reason;\n }\n}\n\nfunction readEnvPositiveInt(name: string): number | undefined {\n const raw = process.env[name];\n if (raw === undefined || raw === '') return undefined;\n const parsed = parsePositiveInt(raw);\n if (parsed === null) {\n throw new AppContextError('invalid-env', `${name} must be a positive integer (got ${raw})`);\n }\n return parsed;\n}\n\n/**\n * Resolve the app/workspace context for a command invocation by combining\n * flags, env vars, an optional `aitcc.yaml`, and the persisted session.\n *\n * Priority chains (highest first):\n * workspace: flag > env(AITCC_WORKSPACE) > yaml(workspaceId) > session.currentWorkspaceId\n * miniApp: flag > env(AITCC_APP) > yaml(miniAppId)\n *\n * When the workspace comes from `flag`, any `miniAppId` sourced from\n * `yaml` is dropped — the flag explicitly redirects the workspace, so a\n * yaml `miniAppId` may belong to a different workspace and is unsafe to\n * carry forward. We never fetch the API to verify; that is the caller's\n * job if it matters.\n *\n * Throws `AppContextError('no-workspace-selected', ...)` when no source\n * provides a `workspaceId`. The caller decides how to surface it (most\n * commands map it to `{ ok: false, reason: 'no-workspace-selected' }`\n * with exit code 2 — see `resolveWorkspaceContext`).\n */\nexport async function resolveAppContext(input: ResolveAppContextInput): Promise<AppContext> {\n const cwd = input.cwd ?? process.cwd();\n\n let project: ProjectContext | null = null;\n try {\n project = await findProjectContext(cwd);\n } catch {\n // A broken yaml shouldn't take down commands that don't actually need\n // it (the user may have flag-provided everything). Treat as \"no\n // project context\"; the dedicated manifest loader surfaces a precise\n // error from the commands that do need to read it.\n project = null;\n }\n\n const envWorkspace = readEnvPositiveInt('AITCC_WORKSPACE');\n const envMiniApp = readEnvPositiveInt('AITCC_APP');\n\n let workspaceId: number | undefined;\n let workspaceSource: ContextSource | undefined;\n if (input.flagWorkspaceId !== undefined) {\n workspaceId = input.flagWorkspaceId;\n workspaceSource = 'flag';\n } else if (envWorkspace !== undefined) {\n workspaceId = envWorkspace;\n workspaceSource = 'env';\n } else if (project?.workspaceId !== undefined) {\n workspaceId = project.workspaceId;\n workspaceSource = 'yaml';\n } else if (input.sessionWorkspaceId !== undefined) {\n workspaceId = input.sessionWorkspaceId;\n workspaceSource = 'session';\n }\n\n if (workspaceId === undefined || workspaceSource === undefined) {\n throw new AppContextError(\n 'no-workspace-selected',\n 'No workspace selected. Pass `--workspace <id>`, set AITCC_WORKSPACE, add `workspaceId` to aitcc.yaml, or run `aitcc workspace use <id>`.',\n );\n }\n\n let miniApp: { miniAppId: number; miniAppIdSource: ContextSource } | undefined;\n if (input.flagMiniAppId !== undefined) {\n miniApp = { miniAppId: input.flagMiniAppId, miniAppIdSource: 'flag' };\n } else if (envMiniApp !== undefined) {\n miniApp = { miniAppId: envMiniApp, miniAppIdSource: 'env' };\n } else if (project?.miniAppId !== undefined && workspaceSource !== 'flag') {\n miniApp = { miniAppId: project.miniAppId, miniAppIdSource: 'yaml' };\n }\n\n return {\n workspaceId,\n workspaceSource,\n ...(miniApp !== undefined ? miniApp : {}),\n ...(project !== null ? { projectFile: project.source } : {}),\n };\n}\n\n/**\n * Common failure-emitter for `AppContextError` produced by the priority\n * chain. Workspace-scoped (`resolveWorkspaceContext`) and app-scoped\n * (`resolveAppOrFail`) callers both feed any thrown `AppContextError`\n * through here so the `--json` shape and exit code stay aligned across\n * the two entry points.\n */\nasync function emitAppContextErrorAndExit(json: boolean, err: unknown): Promise<void> {\n if (err instanceof AppContextError && err.reason === 'invalid-env') {\n if (json) emitJson({ ok: false, reason: 'invalid-env', message: err.message });\n else process.stderr.write(`${err.message}\\n`);\n await exitAfterFlush(ExitCode.Usage);\n return;\n }\n if (err instanceof AppContextError && err.reason === 'no-workspace-selected') {\n if (json) emitJson({ ok: false, reason: 'no-workspace-selected' });\n else process.stderr.write(`${err.message}\\n`);\n await exitAfterFlush(ExitCode.Usage);\n return;\n }\n throw err;\n}\n\n/**\n * Boilerplate wrapper for app-scoped commands (`app show`, `app status`,\n * `app bundles ls`, ...). Builds on `resolveWorkspaceContext` — every\n * app-scoped command also loads the session and resolves the workspace\n * — and additionally accepts a positional `<appId>` value (or per-command\n * `--app` flag value) that feeds into the miniApp priority chain.\n *\n * `args.id` is the raw positional value as citty surfaces it. We accept\n * `unknown` because citty hands back `string | undefined`, but tests\n * sometimes pass `number` literals; guarding with `String(...)` matches\n * the parsing the per-command early-validation used to do.\n *\n * On a malformed positional we emit the same `invalid-id` shape that the\n * pre-PR-1b commands used so agent-plugin's parsing is unchanged.\n *\n * Returns `null` after exiting on every failure branch, mirroring\n * `resolveWorkspaceContext`. Callers must `if (!ctx) return;`.\n */\nexport async function resolveAppOrFail(args: {\n workspace?: string | undefined;\n /** Raw positional/flag value for the mini-app id. */\n appIdRaw?: unknown;\n /** Field name to surface in error messages (`id` or `app`). */\n appIdField?: 'id' | 'app';\n json: boolean;\n}): Promise<\n | (AppContext & {\n session: Session;\n })\n | null\n> {\n // Parse the explicit positional/flag first so a typo is rejected before\n // we let yaml/env quietly take over — `aitcc app status 42x` should not\n // silently fall through to the yaml miniAppId.\n let flagMiniAppId: number | undefined;\n const raw = args.appIdRaw;\n if (raw !== undefined && raw !== null && raw !== '') {\n const str = typeof raw === 'string' ? raw : String(raw);\n const parsed = parsePositiveInt(str);\n if (parsed === null) {\n const field = args.appIdField === 'app' ? '--app' : 'app id';\n const message = `${field} must be a positive integer (got ${JSON.stringify(str)})`;\n if (args.json) emitJson({ ok: false, reason: 'invalid-id', message });\n else process.stderr.write(`${message}\\n`);\n await exitAfterFlush(ExitCode.Usage);\n return null;\n }\n flagMiniAppId = parsed;\n }\n\n const session = await readSession();\n if (!session) {\n emitNotAuthenticated(args.json);\n await exitAfterFlush(ExitCode.NotAuthenticated);\n return null;\n }\n\n let flagWorkspaceId: number | undefined;\n if (args.workspace) {\n const wsRaw = String(args.workspace);\n const parsed = parsePositiveInt(wsRaw);\n if (parsed === null) {\n const message = `--workspace must be a positive integer (got ${wsRaw})`;\n if (args.json) emitJson({ ok: false, reason: 'invalid-id', message });\n else process.stderr.write(`${message}\\n`);\n await exitAfterFlush(ExitCode.Usage);\n return null;\n }\n flagWorkspaceId = parsed;\n }\n\n let app: AppContext;\n try {\n app = await resolveAppContext({\n ...(flagWorkspaceId !== undefined ? { flagWorkspaceId } : {}),\n ...(flagMiniAppId !== undefined ? { flagMiniAppId } : {}),\n ...(session.currentWorkspaceId !== undefined\n ? { sessionWorkspaceId: session.currentWorkspaceId }\n : {}),\n });\n } catch (err) {\n await emitAppContextErrorAndExit(args.json, err);\n return null;\n }\n\n return { session, ...app };\n}\n\n/**\n * Emit the standard \"miniApp id required\" failure for app-scoped commands\n * whose context resolved a workspace but no miniApp. Returns `null` after\n * exiting so the caller can `if (!miniAppId) return;` without an extra\n * branch. Lifted into `_shared.ts` so every Group A command emits the\n * same JSON shape.\n */\nexport async function requireMiniAppId(ctx: AppContext, json: boolean): Promise<number | null> {\n if (ctx.miniAppId !== undefined) return ctx.miniAppId;\n const message =\n 'app id required (provide as argument, --app flag, AITCC_APP env, or `miniAppId` in aitcc.yaml)';\n if (json) emitJson({ ok: false, reason: 'missing-app-id', message });\n else process.stderr.write(`${message}\\n`);\n await exitAfterFlush(ExitCode.Usage);\n return null;\n}\n\nfunction describeSource(\n source: ContextSource,\n kind: 'workspace' | 'app',\n projectFile: string | undefined,\n): string {\n if (source === 'flag') return kind === 'workspace' ? '(from --workspace)' : '(from --app)';\n if (source === 'env')\n return kind === 'workspace' ? '(from $AITCC_WORKSPACE)' : '(from $AITCC_APP)';\n if (source === 'yaml') {\n // Use the simple file label (`aitcc.yaml` / `aitcc.json`) rather than\n // the absolute path so the header stays short on deep checkouts. The\n // exact path is in `ctx.projectFile` for callers that want it.\n if (projectFile !== undefined) {\n const slash = Math.max(projectFile.lastIndexOf('/'), projectFile.lastIndexOf('\\\\'));\n const base = slash >= 0 ? projectFile.slice(slash + 1) : projectFile;\n return `(from ${base})`;\n }\n return '(from aitcc.yaml)';\n }\n return '(from session)';\n}\n\n/**\n * Print the one-line `[workspace: … · app: …]` context header to stderr\n * before a command emits its real output. Suppressed under `--json` so\n * machine-readable callers see only the structured stdout. Stays on\n * stderr (not stdout) so it never lands in pipes that grep stdout, and\n * stays out of TTY-only branches so CI logs still record what context\n * was used.\n */\nexport function printContextHeader(ctx: AppContext, opts: { json: boolean }): void {\n if (opts.json) return;\n const wsTag = describeSource(ctx.workspaceSource, 'workspace', ctx.projectFile);\n let line = `[workspace: ${ctx.workspaceId} ${wsTag}`;\n if (ctx.miniAppId !== undefined && ctx.miniAppIdSource !== undefined) {\n const appTag = describeSource(ctx.miniAppIdSource, 'app', ctx.projectFile);\n line += ` · app: ${ctx.miniAppId} ${appTag}`;\n }\n line += ']\\n';\n process.stderr.write(line);\n}\n","import type { CdpCookie } from '../cdp.js';\nimport { type FetchLike, requestConsoleApi } from './http.js';\n\n// Console-scoped \"who am I\" endpoint, discovered by observing the console UI\n// boot requests. Returned shape is stable across the sample workspace; new\n// fields may appear but we read it conservatively.\n\nexport interface ConsoleMemberWorkspace {\n readonly workspaceId: number;\n readonly workspaceName: string;\n readonly role: string;\n readonly isOwnerDelegationRequested: boolean;\n}\n\nexport interface ConsoleMemberUserInfo {\n readonly id: number;\n readonly bizUserNo: number;\n readonly name: string;\n readonly email: string;\n readonly role: string;\n readonly workspaces: readonly ConsoleMemberWorkspace[];\n readonly isAdult: boolean;\n readonly isOverseasBusiness: boolean;\n}\n\nconst BASE = 'https://apps-in-toss.toss.im/console/api-public/v3/appsintossconsole';\nconst MEMBER_USER_INFO_URL = `${BASE}/members/me/user-info`;\n\nexport async function fetchConsoleMemberUserInfo(\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<ConsoleMemberUserInfo> {\n return requestConsoleApi<ConsoleMemberUserInfo>({\n url: MEMBER_USER_INFO_URL,\n cookies,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n}\n\n// Console account-level terms (distinct from workspace-level terms).\n// `/console-user-terms/me` returns the user's own acceptance of the\n// top-level console TOS, which is required to use the console at all.\n// Shape matches the workspace-terms bucket entries exactly.\nexport interface UserTerm {\n readonly required: boolean;\n readonly termsId: number;\n readonly revisionId: number;\n readonly title: string;\n readonly contentsUrl: string;\n readonly actionType: string;\n readonly isAgreed: boolean;\n readonly isOneTimeConsent: boolean;\n}\n\nexport async function fetchUserTerms(\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<readonly UserTerm[]> {\n const url = `${BASE}/console-user-terms/me`;\n const raw = await requestConsoleApi<unknown>({\n url,\n cookies,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n if (!Array.isArray(raw)) {\n throw new Error('Unexpected user-terms shape: not an array');\n }\n return raw.map((entry, i): UserTerm => {\n if (!entry || typeof entry !== 'object') {\n throw new Error(`Unexpected user-terms entry at index ${i}`);\n }\n const e = entry as Record<string, unknown>;\n return {\n required: Boolean(e.required),\n termsId: typeof e.termsId === 'number' ? e.termsId : 0,\n revisionId: typeof e.revisionId === 'number' ? e.revisionId : 0,\n title: typeof e.title === 'string' ? e.title : '',\n contentsUrl: typeof e.contentsUrl === 'string' ? e.contentsUrl : '',\n actionType: typeof e.actionType === 'string' ? e.actionType : '',\n isAgreed: Boolean(e.isAgreed),\n isOneTimeConsent: Boolean(e.isOneTimeConsent),\n };\n });\n}\n","import type { CdpCookie } from '../cdp.js';\nimport { type FetchLike, requestConsoleApi } from './http.js';\n\n// The list of workspaces a user can see is already baked into the\n// `members/me/user-info` response (see `./me.ts`), so we don't expose a\n// separate `GET /workspaces` wrapper — every caller that needs the list\n// goes through `fetchConsoleMemberUserInfo` and keys off `workspaces`.\n// This module only covers per-workspace detail and future write endpoints.\n\n// Note: the list endpoint (members/me/user-info) and the detail endpoint\n// disagree on field names — list uses workspaceId/workspaceName while\n// detail uses id/name. We normalise detail into the same vocabulary so\n// callers don't have to track which endpoint they came from.\nexport interface WorkspaceDetail {\n readonly workspaceId: number;\n readonly workspaceName: string;\n // The full shape of `/workspaces/:id` has many secondary fields (business\n // registration, verification, licence type, review state, etc) that may\n // grow over time. Stash everything beyond the normalised keys under\n // `extra` so commands like `workspace show --json` can dump the payload\n // without us having to type every field up-front.\n readonly extra?: Readonly<Record<string, unknown>>;\n}\n\nconst WORKSPACES_BASE = 'https://apps-in-toss.toss.im/console/api-public/v3/appsintossconsole';\n\nexport async function fetchWorkspaceDetail(\n workspaceId: number,\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<WorkspaceDetail> {\n // workspaceId is a number at compile time — `encodeURIComponent` on the\n // stringified form would be a no-op, so we inline the interpolation.\n const url = `${WORKSPACES_BASE}/workspaces/${workspaceId}`;\n const raw = await requestConsoleApi<Record<string, unknown>>({\n url,\n cookies,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n const id = raw.id;\n const name = raw.name;\n if (typeof id !== 'number' || !Number.isInteger(id) || id <= 0 || typeof name !== 'string') {\n throw new Error(`Unexpected workspace detail shape for id=${workspaceId}`);\n }\n const { id: _id, name: _name, ...extra } = raw;\n return { workspaceId: id, workspaceName: name, extra };\n}\n\n// Partner = the billing/payout entity that must be registered before\n// promotions/IAP can be used. `registered: false` + `approvalType: 'DRAFT'`\n// is the initial state we observed on a fresh workspace. `partner` is the\n// detail record once approval lands; keep it opaque until we see a live\n// example.\nexport interface WorkspacePartnerState {\n readonly registered: boolean;\n readonly approvalType: string | null;\n readonly rejectMessage: string | null;\n readonly partner: Readonly<Record<string, unknown>> | null;\n}\n\nexport async function fetchWorkspacePartner(\n workspaceId: number,\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<WorkspacePartnerState> {\n const url = `${WORKSPACES_BASE}/workspaces/${workspaceId}/partner`;\n const raw = await requestConsoleApi<Record<string, unknown>>({\n url,\n cookies,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n const registered = raw.registered;\n if (typeof registered !== 'boolean') {\n throw new Error(`Unexpected workspace partner shape for id=${workspaceId}`);\n }\n const approvalType = typeof raw.approvalType === 'string' ? raw.approvalType : null;\n const rejectMessage = typeof raw.rejectMessage === 'string' ? raw.rejectMessage : null;\n const partner =\n raw.partner && typeof raw.partner === 'object'\n ? (raw.partner as Readonly<Record<string, unknown>>)\n : null;\n return { registered, approvalType, rejectMessage, partner };\n}\n\n// `console-workspace-terms/:type/skip-permission` returns the terms a\n// workspace owner must agree to before the feature gated by `:type`\n// becomes available. The supported types are enumerated here verbatim\n// from the console UI — each one gates a distinct feature surface\n// (Toss login scopes, biz workspace eligibility, promotion-money,\n// in-app advertising, in-app purchase). Other values currently 404.\nexport const WORKSPACE_TERM_TYPES = [\n 'TOSS_LOGIN',\n 'BIZ_WORKSPACE',\n 'TOSS_PROMOTION_MONEY',\n 'IAA',\n 'IAP',\n] as const;\nexport type WorkspaceTermType = (typeof WORKSPACE_TERM_TYPES)[number];\n\nexport interface WorkspaceTerm {\n readonly required: boolean;\n readonly termsId: number;\n readonly revisionId: number;\n readonly title: string;\n readonly contentsUrl: string;\n readonly actionType: string;\n readonly isAgreed: boolean;\n readonly isOneTimeConsent: boolean;\n}\n\n// Segments are workspace-scoped (not per-app). The category axis appears\n// in the UI as tabs like \"생성된 세그먼트\"/\"추천 세그먼트\"; the server\n// accepts any string and just filters whatever bucket it matches (we've\n// observed all common category strings return an empty list on a fresh\n// workspace rather than 400-ing). Default bucket matches the UI.\nconst DEFAULT_SEGMENT_CATEGORY = '생성된 세그먼트';\n\nexport interface FetchWorkspaceSegmentsParams {\n readonly workspaceId: number;\n readonly category?: string;\n readonly search?: string;\n readonly page?: number;\n}\n\nexport interface WorkspaceSegmentsPage {\n readonly contents: readonly Readonly<Record<string, unknown>>[];\n readonly totalPage: number;\n readonly currentPage: number;\n}\n\nexport async function fetchWorkspaceSegments(\n params: FetchWorkspaceSegmentsParams,\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<WorkspaceSegmentsPage> {\n const page = params.page ?? 0;\n const qs = new URLSearchParams();\n qs.set('category', params.category ?? DEFAULT_SEGMENT_CATEGORY);\n qs.set('search', params.search ?? '');\n qs.set('page', String(page));\n const url = `${WORKSPACES_BASE}/workspaces/${params.workspaceId}/segments/list?${qs.toString()}`;\n const raw = await requestConsoleApi<unknown>({\n url,\n cookies,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) {\n throw new Error(`Unexpected segments shape for workspace=${params.workspaceId}`);\n }\n const data = raw as Record<string, unknown>;\n const contents = Array.isArray(data.contents) ? data.contents : [];\n return {\n contents: contents.map((c) =>\n c && typeof c === 'object' ? (c as Record<string, unknown>) : {},\n ),\n totalPage: typeof data.totalPage === 'number' ? data.totalPage : 0,\n currentPage: typeof data.currentPage === 'number' ? data.currentPage : page,\n };\n}\n\nexport async function fetchWorkspaceTerms(\n workspaceId: number,\n type: WorkspaceTermType,\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<readonly WorkspaceTerm[]> {\n const url = `${WORKSPACES_BASE}/workspaces/${workspaceId}/console-workspace-terms/${type}/skip-permission`;\n const raw = await requestConsoleApi<unknown>({\n url,\n cookies,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n if (!Array.isArray(raw)) {\n throw new Error(`Unexpected workspace terms shape for type=${type}`);\n }\n return raw.map((entry, i): WorkspaceTerm => {\n if (!entry || typeof entry !== 'object') {\n throw new Error(`Unexpected workspace terms entry at index ${i} for type=${type}`);\n }\n const e = entry as Record<string, unknown>;\n // The console UI currently always sends all fields, and Zod-level\n // strict validation would break if Toss adds an enum value. Trust\n // the types we rely on and pass through with a narrow normalisation.\n return {\n required: Boolean(e.required),\n termsId: typeof e.termsId === 'number' ? e.termsId : 0,\n revisionId: typeof e.revisionId === 'number' ? e.revisionId : 0,\n title: typeof e.title === 'string' ? e.title : '',\n contentsUrl: typeof e.contentsUrl === 'string' ? e.contentsUrl : '',\n actionType: typeof e.actionType === 'string' ? e.actionType : '',\n isAgreed: Boolean(e.isAgreed),\n isOneTimeConsent: Boolean(e.isOneTimeConsent),\n };\n });\n}\n\n// `(termsId, revisionId)` pair the agree endpoint expects per term. The\n// shape is intentionally loose — `WorkspaceTerm` carries a lot more (title,\n// actionType, isAgreed) that the server has no use for on submit, so this\n// helper stays narrow.\nexport interface WorkspaceTermAgreement {\n readonly termsId: number;\n readonly revisionId: number;\n}\n\n/**\n * Persist agreement for one-or-more workspace terms. The endpoint takes a\n * single `agreedList` regardless of which bucket the terms came from — the\n * type tag is implicit in the (termsId, revisionId) pairs.\n *\n * Captured behaviour (2026-05-08, ws=36577):\n * - `POST /workspaces/<wid>/console-workspace-terms` with body\n * `{\"agreedList\":[{\"termsId\": <int>, \"revisionId\": <int>}, ...]}`\n * - Response on success: `{\"resultType\":\"SUCCESS\",\"success\":{}}` — no\n * useful payload. We resolve `void`.\n * - Re-submitting an already-agreed term returns `errorCode: 500`\n * (Internal Server Error). The server is NOT idempotent, so callers\n * must filter to `isAgreed === false` before invoking.\n * - Empty `agreedList` returns SUCCESS (no-op), but we throw client-side\n * before sending — round-tripping a no-op request is wasted.\n *\n * Failure surfaces through `TossApiError` like every other write helper.\n */\nexport async function agreeWorkspaceTerms(\n workspaceId: number,\n terms: readonly WorkspaceTermAgreement[],\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<void> {\n if (terms.length === 0) {\n throw new Error('agreeWorkspaceTerms requires at least one term');\n }\n const url = `${WORKSPACES_BASE}/workspaces/${workspaceId}/console-workspace-terms`;\n await requestConsoleApi<unknown>({\n method: 'POST',\n url,\n cookies,\n body: { agreedList: terms.map(({ termsId, revisionId }) => ({ termsId, revisionId })) },\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n}\n","import { readFile } from 'node:fs/promises';\nimport { unzipSync } from 'fflate';\n\n// A `.ait` bundle today comes in two on-disk shapes, and we have to read\n// both. `@apps-in-toss/web-framework`'s build toolchain packs via\n// `@apps-in-toss/ait-format`, which writes the modern shape:\n//\n// [0..8) MAGIC = ASCII \"AITBUNDL\"\n// [8..12) formatVersion : uint32 BE\n// [12..20) bundleLen : uint64 BE\n// [20..20+bundleLen) protobuf-encoded AITBundle (deploymentId, appName,\n// permissions, …)\n// next 8 bytes zipLen : uint64 BE\n// next zipLen zip blob (fflate-compatible)\n// next 8 bytes sigLen : uint64 BE (may be 0)\n// next sigLen signature (optional)\n//\n// Legacy builds just emit a plain zip whose root contains `app.json`,\n// and the console's own uploader still accepts that shape (it branches\n// on the first 8 bytes: `AITBUNDL` → AIT, `PK\\x03\\x04` → ZIP). We\n// replicate the same branch so `aitcc app deploy` works on either\n// toolchain version without the user having to know which one their\n// build produced.\n//\n// For the AIT path we only need `app.json._metadata.deploymentId`\n// (really: the protobuf `deploymentId`, which the console mirrors into\n// app.json). The header's bundle protobuf carries it directly, so we\n// read it from the header instead of cracking the inner zip — cheaper\n// and avoids depending on `@apps-in-toss/ait-format` (which would pull\n// in protobufjs + long just to read two string fields). A tiny inline\n// wire-format reader for protobuf fields 2 (deploymentId) and 3\n// (appName) is enough.\n\nexport type AitBundleErrorReason =\n | 'file-unreadable'\n | 'unrecognized-format'\n | 'invalid-ait'\n | 'invalid-zip'\n | 'missing-app-json'\n | 'invalid-app-json'\n | 'missing-deployment-id';\n\nexport class AitBundleError extends Error {\n readonly path: string;\n readonly reason: AitBundleErrorReason;\n\n constructor(args: { path: string; reason: AitBundleErrorReason; message: string }) {\n super(args.message);\n this.name = 'AitBundleError';\n this.path = args.path;\n this.reason = args.reason;\n }\n}\n\nexport type AitBundleFormat = 'ait' | 'zip';\n\nexport interface AitBundleInfo {\n readonly deploymentId: string;\n readonly bytes: Uint8Array;\n readonly format: AitBundleFormat;\n}\n\n// ASCII \"AITBUNDL\"\nconst AIT_MAGIC = new Uint8Array([0x41, 0x49, 0x54, 0x42, 0x55, 0x4e, 0x44, 0x4c]);\n// Standard zip local file header signature \"PK\\x03\\x04\"\nconst ZIP_MAGIC = new Uint8Array([0x50, 0x4b, 0x03, 0x04]);\n\nfunction startsWith(bytes: Uint8Array, prefix: Uint8Array): boolean {\n if (bytes.length < prefix.length) return false;\n for (let i = 0; i < prefix.length; i++) {\n if (bytes[i] !== prefix[i]) return false;\n }\n return true;\n}\n\nexport function detectBundleFormat(bytes: Uint8Array): AitBundleFormat | 'unknown' {\n if (startsWith(bytes, AIT_MAGIC)) return 'ait';\n if (startsWith(bytes, ZIP_MAGIC)) return 'zip';\n return 'unknown';\n}\n\n/**\n * Read the `.ait` at `path` and pull out `deploymentId`, auto-detecting\n * whether the file is the modern AIT header format or a legacy plain\n * zip. Returns the raw bytes so the caller can forward them to S3\n * without re-reading the file.\n */\nexport async function readAitBundle(path: string): Promise<AitBundleInfo> {\n let buf: Buffer;\n try {\n buf = await readFile(path);\n } catch (err) {\n throw new AitBundleError({\n path,\n reason: 'file-unreadable',\n message: (err as Error).message,\n });\n }\n const bytes = new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);\n const { deploymentId, format } = deploymentIdFromBundleBytes(bytes, path);\n return { deploymentId, bytes, format };\n}\n\n/**\n * Pure helper split out so tests can feed raw bytes without a tmp file.\n * Throws `AitBundleError` on any parse failure. Returns the detected\n * format so callers that want to log it can.\n */\nexport function deploymentIdFromBundleBytes(\n bytes: Uint8Array,\n pathForError: string,\n): { deploymentId: string; format: AitBundleFormat } {\n const format = detectBundleFormat(bytes);\n if (format === 'ait') {\n return { deploymentId: deploymentIdFromAitHeader(bytes, pathForError), format };\n }\n if (format === 'zip') {\n return { deploymentId: deploymentIdFromLegacyZip(bytes, pathForError), format };\n }\n throw new AitBundleError({\n path: pathForError,\n reason: 'unrecognized-format',\n message:\n 'bundle does not start with AITBUNDL or PK magic bytes — not a valid .ait or legacy zip bundle',\n });\n}\n\n// --- AIT header path ------------------------------------------------------\n\n// Header layout sizes, mirrored from @apps-in-toss/ait-format constants.\nconst AIT_MAGIC_SIZE = 8;\nconst AIT_VERSION_SIZE = 4;\nconst AIT_LENGTH_SIZE = 8;\nconst AIT_HEADER_SIZE = AIT_MAGIC_SIZE + AIT_VERSION_SIZE + AIT_LENGTH_SIZE; // 20\n\nfunction deploymentIdFromAitHeader(bytes: Uint8Array, pathForError: string): string {\n if (bytes.length < AIT_HEADER_SIZE) {\n throw new AitBundleError({\n path: pathForError,\n reason: 'invalid-ait',\n message: 'buffer too small to be a valid AIT file',\n });\n }\n const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);\n // AIT uses big-endian for header integers (see ait-format/index.mjs:\n // `view.getUint32(h, !1)` / `view.getBigUint64(..., !1)`).\n const bundleLen = Number(view.getBigUint64(AIT_MAGIC_SIZE + AIT_VERSION_SIZE, false));\n if (!Number.isFinite(bundleLen) || bundleLen <= 0) {\n throw new AitBundleError({\n path: pathForError,\n reason: 'invalid-ait',\n message: `AIT bundle length is invalid (${bundleLen})`,\n });\n }\n const bundleStart = AIT_HEADER_SIZE;\n const bundleEnd = bundleStart + bundleLen;\n if (bytes.length < bundleEnd) {\n throw new AitBundleError({\n path: pathForError,\n reason: 'invalid-ait',\n message: 'unexpected end of buffer reading AIT bundle protobuf',\n });\n }\n const bundleBytes = bytes.subarray(bundleStart, bundleEnd);\n const fields = readProtobufStringFields(bundleBytes, [2, 3]);\n const deploymentId = fields.get(2);\n if (typeof deploymentId !== 'string' || deploymentId === '') {\n throw new AitBundleError({\n path: pathForError,\n reason: 'missing-deployment-id',\n message: 'AIT bundle protobuf is missing deploymentId (field 2)',\n });\n }\n return deploymentId;\n}\n\n// Minimal protobuf reader: scans a length-delimited varint/string\n// stream and returns the most recent value for each of the requested\n// string field numbers. Only understands the wire types we expect to\n// see in an AITBundle header (varint = 0, 64-bit = 1, length-delimited\n// = 2, 32-bit = 5); for other fields it skips the payload. Good enough\n// to extract deploymentId (field 2) and appName (field 3) without\n// linking protobufjs.\nfunction readProtobufStringFields(\n bytes: Uint8Array,\n wantedFieldNumbers: number[],\n): Map<number, string> {\n const wanted = new Set(wantedFieldNumbers);\n const out = new Map<number, string>();\n const decoder = new TextDecoder('utf-8', { fatal: false });\n let offset = 0;\n while (offset < bytes.length) {\n const { value: tag, next } = readVarint(bytes, offset);\n offset = next;\n const fieldNumber = Number(tag >> 3n);\n const wireType = Number(tag & 7n);\n if (wireType === 0) {\n // varint\n offset = readVarint(bytes, offset).next;\n } else if (wireType === 1) {\n // fixed 64-bit\n offset += 8;\n } else if (wireType === 2) {\n // length-delimited (strings, bytes, submessages, packed repeated)\n const { value: len, next: afterLen } = readVarint(bytes, offset);\n offset = afterLen;\n const payloadEnd = offset + Number(len);\n if (payloadEnd > bytes.length) {\n // Truncated; stop scanning. We return whatever we've already\n // extracted, and the caller decides whether that's enough.\n break;\n }\n if (wanted.has(fieldNumber)) {\n out.set(fieldNumber, decoder.decode(bytes.subarray(offset, payloadEnd)));\n }\n offset = payloadEnd;\n } else if (wireType === 5) {\n // fixed 32-bit\n offset += 4;\n } else {\n // Wire type 3 (start-group) / 4 (end-group) / 6+ (reserved). Not\n // expected inside AITBundle, and we can't safely skip them, so\n // stop.\n break;\n }\n }\n return out;\n}\n\nfunction readVarint(bytes: Uint8Array, start: number): { value: bigint; next: number } {\n let value = 0n;\n let shift = 0n;\n let i = start;\n // Varints are capped at 10 bytes in the protobuf wire format.\n for (let n = 0; n < 10 && i < bytes.length; n++, i++) {\n const byte = bytes[i];\n if (byte === undefined) break;\n value |= BigInt(byte & 0x7f) << shift;\n if ((byte & 0x80) === 0) {\n return { value, next: i + 1 };\n }\n shift += 7n;\n }\n // Malformed — return what we've got so the caller aborts.\n return { value, next: i };\n}\n\n// --- Legacy zip path ------------------------------------------------------\n\nfunction deploymentIdFromLegacyZip(bytes: Uint8Array, pathForError: string): string {\n let entries: Record<string, Uint8Array>;\n try {\n // `filter` restricts extraction to just `app.json` so we don't\n // decompress megabytes of asset payload just to read a few lines of\n // metadata. fflate's unzipSync operates in-memory on a Uint8Array,\n // which is fine for `.ait` bundles (tens of MB worst case).\n entries = unzipSync(bytes, { filter: (file) => file.name === 'app.json' });\n } catch (err) {\n throw new AitBundleError({\n path: pathForError,\n reason: 'invalid-zip',\n message: `not a valid zip: ${(err as Error).message}`,\n });\n }\n const entry = entries['app.json'];\n if (!entry) {\n throw new AitBundleError({\n path: pathForError,\n reason: 'missing-app-json',\n message: 'app.json is not present at the root of the bundle',\n });\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(new TextDecoder().decode(entry));\n } catch (err) {\n throw new AitBundleError({\n path: pathForError,\n reason: 'invalid-app-json',\n message: `app.json is not valid JSON: ${(err as Error).message}`,\n });\n }\n if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {\n throw new AitBundleError({\n path: pathForError,\n reason: 'invalid-app-json',\n message: 'app.json is not a JSON object',\n });\n }\n const metadata = (parsed as Record<string, unknown>)._metadata;\n if (metadata === null || typeof metadata !== 'object' || Array.isArray(metadata)) {\n throw new AitBundleError({\n path: pathForError,\n reason: 'missing-deployment-id',\n message:\n 'app.json._metadata is missing; is your build outputting the modern app.json schema?',\n });\n }\n const deploymentId = (metadata as Record<string, unknown>).deploymentId;\n if (typeof deploymentId !== 'string' || deploymentId === '') {\n throw new AitBundleError({\n path: pathForError,\n reason: 'missing-deployment-id',\n message:\n 'app.json._metadata.deploymentId is missing or empty; is your build outputting the modern app.json schema?',\n });\n }\n return deploymentId;\n}\n","import { describeApiError } from '../api/error-messages.js';\nimport { type FetchLike, NetworkError, TossApiError } from '../api/http.js';\nimport { fetchConsoleMemberUserInfo, fetchUserTerms } from '../api/me.js';\nimport {\n postBundleMemo,\n postBundleRelease,\n postBundleReview,\n postDeploymentsComplete,\n postDeploymentsInitialize,\n putBundleToUploadUrl,\n} from '../api/mini-apps.js';\nimport {\n fetchWorkspaceTerms,\n WORKSPACE_TERM_TYPES,\n type WorkspaceTermType,\n} from '../api/workspaces.js';\nimport { AitBundleError, type AitBundleInfo, readAitBundle } from '../config/ait-bundle.js';\nimport { ExitCode } from '../exit.js';\nimport { exitAfterFlush } from '../flush.js';\nimport type { Session } from '../session.js';\nimport {\n emitFailureFromError,\n emitJson,\n parsePositiveInt,\n printContextHeader,\n requireMiniAppId,\n resolveAppOrFail,\n} from './_shared.js';\n\n// `runDeploy` is the testable seam for `aitcc app deploy`. The citty\n// wrapper in `app.ts` is a thin shim; tests pass a fake `fetchImpl` and\n// override the bundle reader to pin each `--json` branch without\n// spawning a subprocess.\n//\n// --json contract (consumed by agent-plugin):\n//\n// success (all requested steps completed):\n// { ok: true, workspaceId, appId, deploymentId,\n// bundleFormat: 'ait' | 'zip',\n// uploaded: true, reviewed: boolean, released: boolean,\n// bundle: { ... } | null,\n// reviewResult: { ... } | null,\n// releaseResult: { ... } | null } exit 0\n//\n// dry run (always exit 0 — dry-run never returns a deploy-blocking\n// exit code; the `wouldSucceed` boolean is how callers learn whether\n// a live deploy would clear the same checks):\n// { ok: true, dryRun: true, wouldSucceed,\n// workspaceId, appId, deploymentId,\n// bundleFormat: 'ait' | 'zip', bytes,\n// steps: ['upload', ...], memo: string|null,\n// releaseNotes: string|null, confirmed: boolean,\n// bundle: { path, format, deploymentId, embeddedDeploymentId|null,\n// deploymentIdSource: 'flag'|'bundle',\n// flagMatch: boolean|null, size },\n// context: { workspaceId, appId, sessionValid: true,\n// permissions: { role|null, source: 'members/me'|'unknown',\n// error?: string } },\n// terms: { blockers: [{ scope, type, errorCode, title, action }],\n// checked: boolean, error?: string } } exit 0\n//\n// usage errors:\n// { ok: false, reason: 'missing-app-id' | 'invalid-id'\n// | 'missing-path' | 'invalid-bundle'\n// | 'missing-release-notes' | 'not-confirmed'\n// | 'bundle-not-prepare' | 'file-unreadable',\n// ... } exit 2\n//\n// partial-success failures (keeps agent-plugin informed so it can\n// retry downstream steps without re-uploading):\n// { ok: false, uploaded: true, reviewed: false,\n// reason: 'api-error', status?, errorCode?, message } exit 17\n// { ok: false, uploaded: true, reviewed: true, released: false,\n// reason: 'api-error', ... } exit 17\n//\n// Standard auth/network follow the shared contract from _shared.ts\n// (ok:true authenticated:false exit 10, network-error exit 11,\n// api-error exit 17).\n//\n// --release note: the server requires the bundle to be in APPROVED state\n// before `/bundles/release` succeeds. In practice that means users run\n// this command twice: once with `--request-review` (bundle uploaded and\n// queued), then again days later with `--release --confirm` after the\n// review landed. Running upload + review + release in one shot only\n// works if the reviewer was already asked to auto-approve, which is\n// rare — we document rather than enforce, since a future auto-approved\n// workspace flow could legitimately chain all three.\n\nexport interface DeployArgs {\n readonly path: string;\n readonly app: string | undefined;\n readonly deploymentId?: string | undefined;\n readonly memo?: string | undefined;\n readonly requestReview?: boolean | undefined;\n readonly releaseNotes?: string | undefined;\n readonly release?: boolean | undefined;\n readonly confirm?: boolean | undefined;\n readonly workspace?: string | undefined;\n readonly dryRun?: boolean | undefined;\n readonly json: boolean;\n}\n\nexport interface DeployDeps {\n readonly fetchImpl?: FetchLike;\n readonly readBundleImpl?: (path: string) => Promise<AitBundleInfo>;\n}\n\nexport async function runDeploy(args: DeployArgs, deps: DeployDeps = {}): Promise<void> {\n // 1. Validate flag shape before reading the bundle / loading the session\n // so bad invocations fail fast without disk I/O or the Chrome-spawn\n // detour. `--app`'s presence is now optional (yaml/env can supply it),\n // but a malformed *value* should still short-circuit before we read\n // the bundle file — same fast-fail invariant the pre-PR-1b code had.\n // `resolveAppOrFail` re-parses below; this guard is only here to\n // reject `--app abc` before the bundle is opened.\n if (typeof args.app === 'string' && args.app !== '' && parsePositiveInt(args.app) === null) {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'invalid-id',\n message: `--app must be a positive integer (got ${JSON.stringify(args.app)})`,\n });\n } else {\n process.stderr.write(`app deploy: invalid --app ${JSON.stringify(args.app)}\\n`);\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n\n if (typeof args.path !== 'string' || args.path === '') {\n if (args.json) {\n emitJson({ ok: false, reason: 'missing-path', message: 'path to .ait bundle is required' });\n } else {\n process.stderr.write('app deploy: path to .ait bundle is required.\\n');\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n\n const requestReview = Boolean(args.requestReview);\n const release = Boolean(args.release);\n const confirm = Boolean(args.confirm);\n const releaseNotes = typeof args.releaseNotes === 'string' ? args.releaseNotes : undefined;\n\n if (requestReview && releaseNotes === undefined) {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'missing-release-notes',\n message: '--release-notes <text> is required with --request-review',\n });\n } else {\n process.stderr.write(\n 'app deploy: --release-notes <text> is required with --request-review.\\n',\n );\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n\n if (release && !confirm) {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'not-confirmed',\n message: '--release is destructive; pass --confirm to proceed',\n });\n } else {\n process.stderr.write(\n 'app deploy: --release publishes the bundle to end users.\\n' +\n ' Re-run with --confirm to proceed.\\n',\n );\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n\n // 2. Read the bundle. In dry-run mode we still read it so the plan we\n // print matches what a real run would do (bytes count, embedded\n // deploymentId).\n const readBundle = deps.readBundleImpl ?? readAitBundle;\n let bundleInfo: AitBundleInfo;\n try {\n bundleInfo = await readBundle(args.path);\n } catch (err) {\n if (err instanceof AitBundleError) {\n const reason = err.reason === 'file-unreadable' ? 'file-unreadable' : 'invalid-bundle';\n if (args.json) {\n emitJson({\n ok: false,\n reason,\n path: err.path,\n bundleReason: err.reason,\n message: err.message,\n });\n } else {\n process.stderr.write(`app deploy: ${err.message}\\n`);\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n throw err;\n }\n\n // 3. If --deployment-id was passed explicitly, use it verbatim (the\n // bundle's embedded id is still useful for the plan output but does\n // not override). This matches `app bundles upload`'s flag\n // semantics; the wrapper's convenience is auto-detect, not\n // override.\n const deploymentId =\n typeof args.deploymentId === 'string' && args.deploymentId !== ''\n ? args.deploymentId\n : bundleInfo.deploymentId;\n if (deploymentId === '') {\n // Defensive: readAitBundle throws when the id is empty, but a\n // caller-provided impl could return one. Surface as invalid-bundle\n // so the agent-plugin error branch is consistent.\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'invalid-bundle',\n path: args.path,\n message: 'deploymentId is empty',\n });\n } else {\n process.stderr.write('app deploy: deploymentId is empty.\\n');\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n\n // 4. Resolve workspace + miniApp (loads session + checks auth). In\n // dry-run we still do this because the `--json` plan includes\n // `workspaceId`/`appId` and the agent-plugin parses those fields\n // unconditionally. `--app` is now optional when `aitcc.yaml` (or\n // `AITCC_APP`) supplies a `miniAppId`.\n const ctx = await resolveAppOrFail({\n json: args.json,\n appIdRaw: args.app,\n appIdField: 'app',\n ...(args.workspace !== undefined ? { workspace: args.workspace } : {}),\n });\n if (!ctx) return;\n const appId = await requireMiniAppId(ctx, args.json);\n if (appId === null) return;\n printContextHeader(ctx, { json: args.json });\n const { session, workspaceId } = ctx;\n\n const memo = typeof args.memo === 'string' && args.memo.length > 0 ? args.memo : undefined;\n const steps: DeployStep[] = ['upload'];\n if (requestReview) steps.push('review');\n if (release) steps.push('release');\n\n if (args.dryRun) {\n return runDryRun({\n json: args.json,\n path: args.path,\n bundleInfo,\n deploymentId,\n explicitDeploymentId: typeof args.deploymentId === 'string' && args.deploymentId !== '',\n workspaceId,\n appId,\n session,\n steps,\n memo,\n releaseNotes,\n confirm,\n fetchImpl: deps.fetchImpl,\n });\n }\n\n // 5. Real execution. Each step tracks its success so a partial\n // failure downstream can report which earlier steps already ran.\n const apiOpts = deps.fetchImpl ? { fetchImpl: deps.fetchImpl } : {};\n let uploaded = false;\n let bundleRecord: Readonly<Record<string, unknown>> | null = null;\n let reviewed = false;\n let reviewResult: Readonly<Record<string, unknown>> | null = null;\n\n try {\n const init = await postDeploymentsInitialize(\n workspaceId,\n appId,\n deploymentId,\n session.cookies,\n apiOpts,\n );\n if (init.reviewStatus !== 'PREPARE') {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'bundle-not-prepare',\n workspaceId,\n appId,\n deploymentId,\n reviewStatus: init.reviewStatus,\n message: '이미 존재하는 버전이에요.',\n });\n } else {\n process.stderr.write(\n `app deploy: deployment ${deploymentId} is already in state ${init.reviewStatus}; upload refused.\\n`,\n );\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n await putBundleToUploadUrl(init.uploadUrl, bundleInfo.bytes, apiOpts);\n bundleRecord = await postDeploymentsComplete(\n workspaceId,\n appId,\n deploymentId,\n session.cookies,\n apiOpts,\n );\n if (memo !== undefined) {\n await postBundleMemo(workspaceId, appId, deploymentId, memo, session.cookies, apiOpts);\n }\n uploaded = true;\n } catch (err) {\n // Upload failure — nothing downstream ran, so the shared dispatcher\n // is fine; partial-success reporting kicks in only once\n // `uploaded === true`.\n return emitFailureFromError(args.json, err);\n }\n\n if (requestReview) {\n try {\n reviewResult = await postBundleReview(\n {\n workspaceId,\n miniAppId: appId,\n deploymentId,\n releaseNotes: releaseNotes ?? '',\n },\n session.cookies,\n apiOpts,\n );\n reviewed = true;\n } catch (err) {\n return emitPartialFailure(args.json, err, {\n workspaceId,\n appId,\n deploymentId,\n uploaded: true,\n reviewed: false,\n released: false,\n });\n }\n }\n\n let releaseResult: Readonly<Record<string, unknown>> | null = null;\n if (release) {\n try {\n releaseResult = await postBundleRelease(\n { workspaceId, miniAppId: appId, deploymentId },\n session.cookies,\n apiOpts,\n );\n } catch (err) {\n return emitPartialFailure(args.json, err, {\n workspaceId,\n appId,\n deploymentId,\n uploaded: true,\n reviewed,\n released: false,\n });\n }\n }\n\n if (args.json) {\n emitJson({\n ok: true,\n workspaceId,\n appId,\n deploymentId,\n bundleFormat: bundleInfo.format,\n uploaded,\n reviewed,\n released: release,\n bundle: bundleRecord,\n reviewResult,\n releaseResult,\n });\n return exitAfterFlush(ExitCode.Ok);\n }\n\n process.stdout.write(\n `Deployed bundle for app ${appId} (ws ${workspaceId})\\n` +\n ` deploymentId ${deploymentId}\\n` +\n ` bytes ${bundleInfo.bytes.byteLength}\\n` +\n ` steps ${steps.join(' → ')}\\n`,\n );\n return exitAfterFlush(ExitCode.Ok);\n}\n\n/**\n * Partial-failure emitter. The upload succeeded (so the user does NOT\n * need to re-upload on retry) but a downstream step failed. Keeping the\n * `uploaded: true` bit in the JSON lets agent-plugin skip to the\n * specific failing step on retry instead of re-running the whole\n * pipeline.\n */\nasync function emitPartialFailure(\n json: boolean,\n err: unknown,\n progress: {\n workspaceId: number;\n appId: number;\n deploymentId: string;\n uploaded: boolean;\n reviewed: boolean;\n released: boolean;\n },\n): Promise<void> {\n if (err instanceof TossApiError && err.isAuthError) {\n if (json) {\n emitJson({\n ok: true,\n authenticated: false,\n reason: 'session-expired',\n ...progress,\n });\n } else {\n process.stderr.write('Session is no longer valid. Run `aitcc login` again.\\n');\n }\n return exitAfterFlush(ExitCode.NotAuthenticated);\n }\n if (err instanceof TossApiError) {\n const message = describeApiError({\n errorCode: err.errorCode,\n reason: err.reason,\n fallback: err.message,\n });\n if (json) {\n emitJson({\n ok: false,\n reason: 'api-error',\n status: err.status,\n ...(err.errorCode !== undefined ? { errorCode: err.errorCode } : {}),\n message,\n ...progress,\n });\n } else {\n process.stderr.write(`Unexpected error: ${message}\\n`);\n }\n return exitAfterFlush(ExitCode.ApiError);\n }\n if (err instanceof NetworkError) {\n if (json) {\n emitJson({\n ok: false,\n reason: 'network-error',\n message: err.message,\n ...progress,\n });\n } else {\n process.stderr.write(`Network error reaching the console API: ${err.message}.\\n`);\n }\n return exitAfterFlush(ExitCode.NetworkError);\n }\n if (json) {\n emitJson({\n ok: false,\n reason: 'api-error',\n message: (err as Error).message,\n ...progress,\n });\n } else {\n process.stderr.write(`Unexpected error: ${(err as Error).message}\\n`);\n }\n return exitAfterFlush(ExitCode.ApiError);\n}\n\n// --- dry-run pre-flight ---------------------------------------------------\n//\n// `runDryRun` runs the same up-front checks a live `app deploy` does\n// (bundle parse + workspace/app/session resolve), then layers on\n// read-only network probes (`members/me`, `console-user-terms/me`, the\n// five `console-workspace-terms/<type>/skip-permission` buckets) so the\n// caller sees every failure that a live deploy would surface — without\n// any write happening.\n//\n// Always exits 0. The `wouldSucceed` boolean tells the caller whether a\n// live run would clear the same checks; agent-plugin reads that field\n// and reads `terms.blockers` to derive a remediation step. Failure modes\n// like \"terms fetch errored\" are reported as warnings on top of the\n// payload, not as exit codes — dry-run is meant to be informational.\n\n// Workspace-term types whose missing-required entries map to a\n// terms-family errorCode the live deploy would emit. Mapping is from\n// docs/api/_error-codes.md \"Auth / 약관 family\". `BIZ_WORKSPACE` is the\n// one that explicitly gates `app deploy` per docs/api workspaces.md;\n// the other types gate adjacent surfaces (login scopes, IAP, IAA,\n// promotion money) and we surface them too because (a) they share the\n// \"must agree before this workspace works\" character, and (b) the\n// agent-plugin can decide which subset blocks its specific flow.\ntype DeployStep = 'upload' | 'review' | 'release';\n\nconst WORKSPACE_TERM_ERROR_CODES: Record<WorkspaceTermType, number> = {\n TOSS_LOGIN: 4037,\n BIZ_WORKSPACE: 4040,\n TOSS_PROMOTION_MONEY: 4039,\n IAA: 4099,\n IAP: 5001,\n};\n\ninterface DryRunInput {\n readonly json: boolean;\n readonly path: string;\n readonly bundleInfo: AitBundleInfo;\n readonly deploymentId: string;\n readonly explicitDeploymentId: boolean;\n readonly workspaceId: number;\n readonly appId: number;\n readonly session: Session;\n readonly steps: readonly DeployStep[];\n readonly memo: string | undefined;\n readonly releaseNotes: string | undefined;\n readonly confirm: boolean;\n readonly fetchImpl: FetchLike | undefined;\n}\n\ninterface PermissionsReport {\n readonly role: string | null;\n readonly source: 'members/me' | 'unknown';\n readonly error?: string;\n}\n\ninterface TermsBlocker {\n readonly scope: 'user' | 'workspace';\n readonly type: string;\n readonly errorCode: number;\n readonly title: string;\n readonly action: string;\n}\n\ninterface TermsReport {\n readonly blockers: readonly TermsBlocker[];\n readonly checked: boolean;\n readonly error?: string;\n}\n\nasync function runDryRun(input: DryRunInput): Promise<void> {\n const apiOpts = input.fetchImpl ? { fetchImpl: input.fetchImpl } : {};\n const embedded = input.bundleInfo.deploymentId;\n const flagMatch = input.explicitDeploymentId ? input.deploymentId === embedded : null;\n\n const [permissions, terms] = await Promise.all([\n fetchPermissions(input.workspaceId, input.session, apiOpts),\n fetchTermsBlockers(input.workspaceId, input.session, apiOpts),\n ]);\n\n const wouldSucceed = (flagMatch === null || flagMatch === true) && terms.blockers.length === 0;\n\n if (input.json) {\n emitJson({\n ok: true,\n dryRun: true,\n wouldSucceed,\n // Top-level fields kept for backwards compatibility with the\n // pre-enhancement --json shape (agent-plugin and existing\n // consumers reach for `workspaceId`/`appId`/`deploymentId`/etc\n // directly, not the nested `context`/`bundle` blocks).\n workspaceId: input.workspaceId,\n appId: input.appId,\n deploymentId: input.deploymentId,\n bundleFormat: input.bundleInfo.format,\n bytes: input.bundleInfo.bytes.byteLength,\n steps: input.steps,\n memo: input.memo ?? null,\n releaseNotes: input.releaseNotes ?? null,\n confirmed: input.confirm,\n bundle: {\n path: input.path,\n format: input.bundleInfo.format,\n deploymentId: input.deploymentId,\n embeddedDeploymentId: embedded,\n deploymentIdSource: input.explicitDeploymentId ? 'flag' : 'bundle',\n flagMatch,\n size: input.bundleInfo.bytes.byteLength,\n },\n context: {\n workspaceId: input.workspaceId,\n appId: input.appId,\n sessionValid: true,\n permissions,\n },\n terms,\n });\n return exitAfterFlush(ExitCode.Ok);\n }\n\n process.stdout.write(\n renderDryRunText(input, { embedded, flagMatch, permissions, terms, wouldSucceed }),\n );\n return exitAfterFlush(ExitCode.Ok);\n}\n\nasync function fetchPermissions(\n workspaceId: number,\n session: Session,\n apiOpts: { fetchImpl?: FetchLike },\n): Promise<PermissionsReport> {\n // Best-effort: a non-fatal failure here just means we render\n // `permissions: unknown` and live deploy would still try. We do NOT\n // map a 401 here to a session-expired exit — it would mask the rest\n // of the dry-run report. The user runs `aitcc whoami` to investigate.\n try {\n const info = await fetchConsoleMemberUserInfo(session.cookies, apiOpts);\n const ws = info.workspaces.find((w) => w.workspaceId === workspaceId);\n if (!ws) {\n return {\n role: null,\n source: 'unknown',\n error: `current user has no membership in workspace ${workspaceId}`,\n };\n }\n return { role: ws.role, source: 'members/me' };\n } catch (err) {\n return { role: null, source: 'unknown', error: (err as Error).message };\n }\n}\n\nasync function fetchTermsBlockers(\n workspaceId: number,\n session: Session,\n apiOpts: { fetchImpl?: FetchLike },\n): Promise<TermsReport> {\n // We fetch user-terms (account-level — 4032/4036 family) and every\n // workspace-terms bucket in parallel. Any single failure flips the\n // whole report into \"checked: false\" with a warning — partial term\n // results would mislead the wouldSucceed gate (a missing bucket\n // could hide a blocker), so we treat the surface as all-or-nothing.\n // Split the heterogeneous fetches into two homogeneous Promise.all\n // groups so TS can infer each tuple slot precisely (a single mixed\n // spread collapses everything to a union and forces casts at the use\n // sites).\n try {\n const [userTerms, workspaceResults] = await Promise.all([\n fetchUserTerms(session.cookies, apiOpts),\n Promise.all(\n WORKSPACE_TERM_TYPES.map(\n async (t) =>\n [t, await fetchWorkspaceTerms(workspaceId, t, session.cookies, apiOpts)] as const,\n ),\n ),\n ]);\n\n const blockers: TermsBlocker[] = [];\n for (const t of userTerms) {\n if (t.required && !t.isAgreed) {\n blockers.push({\n scope: 'user',\n type: 'USER_TERMS',\n // 4036 (유저_약관_미동의) is the consistent code; 4032\n // (앱인토스_미가입) only fires when the account itself is\n // unregistered, which we'd hit at session capture rather\n // than in this surface.\n errorCode: 4036,\n title: t.title,\n action: 'aitcc me terms',\n });\n }\n }\n for (const [type, terms] of workspaceResults) {\n for (const t of terms) {\n if (!t.required || t.isAgreed) continue;\n blockers.push({\n scope: 'workspace',\n type,\n errorCode: WORKSPACE_TERM_ERROR_CODES[type],\n title: t.title,\n action: `aitcc workspace terms --type ${type}`,\n });\n }\n }\n return { blockers, checked: true };\n } catch (err) {\n return { blockers: [], checked: false, error: (err as Error).message };\n }\n}\n\nfunction renderDryRunText(\n input: DryRunInput,\n derived: {\n embedded: string;\n flagMatch: boolean | null;\n permissions: PermissionsReport;\n terms: TermsReport;\n wouldSucceed: boolean;\n },\n): string {\n const lines: string[] = [];\n lines.push(`DRY RUN — app deploy ${input.appId}\\n`);\n\n // Bundle section\n lines.push('\\nBundle\\n');\n lines.push(` path ${input.path}\\n`);\n lines.push(` format ${input.bundleInfo.format.toUpperCase()}\\n`);\n lines.push(` deploymentId ${input.deploymentId}\\n`);\n if (derived.flagMatch === false) {\n lines.push(` flag match MISMATCH (bundle embeds ${derived.embedded})\\n`);\n } else if (derived.flagMatch === true) {\n lines.push(` flag match ok (matches embedded)\\n`);\n }\n lines.push(` size ${formatBytes(input.bundleInfo.bytes.byteLength)}\\n`);\n\n // Context section\n lines.push('\\nContext\\n');\n lines.push(` workspace ${input.workspaceId}\\n`);\n lines.push(` app ${input.appId}\\n`);\n lines.push(` session valid\\n`);\n if (derived.permissions.role !== null) {\n lines.push(` permissions ${derived.permissions.role}\\n`);\n } else {\n lines.push(\n ` permissions unknown${derived.permissions.error ? ` (${derived.permissions.error})` : ''}\\n`,\n );\n }\n\n // Terms section\n lines.push('\\nTerms\\n');\n if (!derived.terms.checked) {\n lines.push(\n ` warning: could not check terms status (${derived.terms.error ?? 'unknown error'}).\\n`,\n );\n lines.push(\n ' live deploy may still fail with a 4032/4036/4037/4039/4040/4099/5001 errorCode.\\n',\n );\n } else if (derived.terms.blockers.length === 0) {\n lines.push(' all deploy-related terms are agreed\\n');\n } else {\n for (const b of derived.terms.blockers) {\n lines.push(\n ` blocked: ${b.scope}/${b.type} — ${b.title} (errorCode ${b.errorCode})\\n` +\n ` action: ${b.action}\\n`,\n );\n }\n }\n\n // Steps + memo (kept from the pre-enhancement output so existing\n // operators reading the dry-run still see the planned pipeline).\n lines.push('\\nPlan\\n');\n const stepsLine = input.steps\n .map((s) => {\n if (s === 'review')\n return `review (releaseNotes: ${JSON.stringify(input.releaseNotes ?? '')})`;\n if (s === 'release') return `release (${input.confirm ? 'confirmed' : 'NOT confirmed'})`;\n return s;\n })\n .join(' → ');\n lines.push(` steps ${stepsLine}\\n`);\n lines.push(` memo ${input.memo ?? '(none)'}\\n`);\n\n // Result. `wouldSucceed` is the gated bundle/terms/flag-match check —\n // it intentionally does NOT factor in `permissions.role === null` (the\n // server is the authority on membership and the live deploy would\n // surface a precise error). Surface that caveat in the human-readable\n // line so an operator who skipped reading the Context block above\n // doesn't read \"all clear\" and then get a server failure.\n lines.push('\\nResult\\n');\n if (!derived.wouldSucceed) {\n lines.push(' Live deploy would fail. Resolve the blocked items above, then re-run.\\n');\n } else if (derived.permissions.role === null) {\n lines.push(\n ' Live deploy would clear bundle + terms checks. Workspace membership could not be confirmed; live deploy may still fail with a permissions error.\\n',\n );\n } else {\n lines.push(' Live deploy would clear every pre-flight check.\\n');\n }\n return lines.join('');\n}\n\nfunction formatBytes(n: number): string {\n if (n < 1024) return `${n} B`;\n if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)} KB`;\n return `${(n / (1024 * 1024)).toFixed(1)} MB`;\n}\n","import { access, readFile } from 'node:fs/promises';\nimport { dirname, isAbsolute, resolve } from 'node:path';\nimport { parse as parseYaml } from 'yaml';\n\n// The app manifest is the CLI's user-facing contract for `aitcc app\n// register`. It intentionally mirrors the console's form field names only\n// at the top level — the inferred submit payload (which nests fields into\n// step1/step2/miniApp/impression) is built by a separate payload module\n// so the manifest shape is stable even if the bundle analysis turns out\n// to be off. Dog-food task #23 is expected to correct the payload\n// transformation but should not force a manifest rewrite.\n//\n// Validation here is \"config shape only\": presence, type, and cheap\n// numeric/length constraints from `VALIDATION-RULES.md` that we can\n// enforce without reading image files. Image-dimension checks live in a\n// sibling module (`image-validator.ts`) because they need FS reads.\n//\n// ManifestError is a single error class carrying (kind, field?, path?)\n// so the command layer can translate it into the documented `--json`\n// error shapes without re-classifying.\n\nexport type ManifestErrorKind = 'invalid-config' | 'missing-required-field';\n\nexport class ManifestError extends Error {\n readonly kind: ManifestErrorKind;\n readonly field: string | undefined;\n\n constructor(kind: ManifestErrorKind, message: string, field?: string) {\n super(message);\n this.name = 'ManifestError';\n this.kind = kind;\n this.field = field;\n }\n}\n\nexport interface AppManifest {\n readonly titleKo: string;\n readonly titleEn: string;\n readonly appName: string;\n readonly homePageUri: string | undefined;\n readonly csEmail: string;\n readonly logo: string;\n readonly logoDarkMode: string | undefined;\n readonly horizontalThumbnail: string;\n readonly categoryIds: readonly number[];\n readonly subtitle: string;\n readonly description: string;\n readonly keywords: readonly string[];\n readonly verticalScreenshots: readonly string[];\n readonly horizontalScreenshots: readonly string[];\n}\n\nconst DEFAULT_NAMES = ['aitcc.yaml', 'aitcc.json'] as const;\n\nasync function fileExists(path: string): Promise<boolean> {\n try {\n await access(path);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Resolve the manifest file path. When `explicit` is provided, we use it\n * verbatim (resolved against `cwd`) and require it to exist. Otherwise we\n * auto-detect `aitcc.yaml` then `aitcc.json` under `cwd`.\n */\nexport async function resolveManifestPath(\n explicit: string | undefined,\n cwd: string,\n): Promise<string> {\n if (explicit) {\n const abs = isAbsolute(explicit) ? explicit : resolve(cwd, explicit);\n if (!(await fileExists(abs))) {\n throw new ManifestError('invalid-config', `manifest file not found at ${abs}`);\n }\n return abs;\n }\n for (const name of DEFAULT_NAMES) {\n const abs = resolve(cwd, name);\n if (await fileExists(abs)) return abs;\n }\n throw new ManifestError(\n 'invalid-config',\n `no app manifest found (looked for ${DEFAULT_NAMES.join(', ')} in ${cwd})`,\n );\n}\n\nexport async function loadAppManifest(path: string): Promise<AppManifest> {\n const raw = await readFile(path, 'utf8');\n const parsed = parseManifestFile(path, raw);\n return validateManifest(parsed, dirname(path));\n}\n\nfunction parseManifestFile(path: string, raw: string): Record<string, unknown> {\n const isJson = path.toLowerCase().endsWith('.json');\n try {\n const out = isJson ? JSON.parse(raw) : parseYaml(raw);\n if (out === null || typeof out !== 'object' || Array.isArray(out)) {\n throw new ManifestError('invalid-config', `manifest at ${path} is not a mapping`);\n }\n return out as Record<string, unknown>;\n } catch (err) {\n if (err instanceof ManifestError) throw err;\n const msg = (err as Error).message;\n throw new ManifestError('invalid-config', `failed to parse manifest at ${path}: ${msg}`);\n }\n}\n\nfunction requireString(input: Record<string, unknown>, key: string): string {\n const v = input[key];\n if (v === undefined || v === null) {\n throw new ManifestError('missing-required-field', `${key} is required`, key);\n }\n if (typeof v !== 'string') {\n throw new ManifestError('invalid-config', `${key} must be a string`, key);\n }\n if (v.trim().length === 0) {\n throw new ManifestError('missing-required-field', `${key} is required`, key);\n }\n return v;\n}\n\nfunction optionalString(input: Record<string, unknown>, key: string): string | undefined {\n const v = input[key];\n if (v === undefined || v === null) return undefined;\n if (typeof v !== 'string') {\n throw new ManifestError('invalid-config', `${key} must be a string when provided`, key);\n }\n return v;\n}\n\nfunction requirePath(input: Record<string, unknown>, key: string, configDir: string): string {\n const rel = requireString(input, key);\n return isAbsolute(rel) ? rel : resolve(configDir, rel);\n}\n\nfunction optionalPath(\n input: Record<string, unknown>,\n key: string,\n configDir: string,\n): string | undefined {\n const rel = optionalString(input, key);\n if (rel === undefined) return undefined;\n return isAbsolute(rel) ? rel : resolve(configDir, rel);\n}\n\nfunction requireNumberArray(\n input: Record<string, unknown>,\n key: string,\n { min }: { min: number },\n): number[] {\n const v = input[key];\n if (!Array.isArray(v)) {\n throw new ManifestError('invalid-config', `${key} must be an array of numbers`, key);\n }\n if (v.length < min) {\n throw new ManifestError('invalid-config', `${key} must contain at least ${min} item(s)`, key);\n }\n return v.map((item, idx) => {\n if (typeof item !== 'number' || !Number.isInteger(item)) {\n throw new ManifestError('invalid-config', `${key}[${idx}] must be an integer`, key);\n }\n return item;\n });\n}\n\nfunction optionalStringArray(\n input: Record<string, unknown>,\n key: string,\n { max }: { max?: number } = {},\n): string[] {\n const v = input[key];\n if (v === undefined || v === null) return [];\n if (!Array.isArray(v)) {\n throw new ManifestError('invalid-config', `${key} must be an array of strings`, key);\n }\n if (max !== undefined && v.length > max) {\n throw new ManifestError(\n 'invalid-config',\n `${key} accepts at most ${max} entries (got ${v.length})`,\n key,\n );\n }\n return v.map((item, idx) => {\n if (typeof item !== 'string') {\n throw new ManifestError('invalid-config', `${key}[${idx}] must be a string`, key);\n }\n return item;\n });\n}\n\nfunction requirePathArray(\n input: Record<string, unknown>,\n key: string,\n configDir: string,\n { min }: { min: number },\n): string[] {\n const v = input[key];\n if (!Array.isArray(v)) {\n throw new ManifestError('invalid-config', `${key} must be an array of paths`, key);\n }\n if (v.length < min) {\n throw new ManifestError('invalid-config', `${key} must contain at least ${min} item(s)`, key);\n }\n return v.map((item, idx) => {\n if (typeof item !== 'string' || item.trim().length === 0) {\n throw new ManifestError('invalid-config', `${key}[${idx}] must be a non-empty string`, key);\n }\n return isAbsolute(item) ? item : resolve(configDir, item);\n });\n}\n\nfunction optionalPathArray(\n input: Record<string, unknown>,\n key: string,\n configDir: string,\n): string[] {\n const v = input[key];\n if (v === undefined || v === null) return [];\n if (!Array.isArray(v)) {\n throw new ManifestError('invalid-config', `${key} must be an array of paths`, key);\n }\n return v.map((item, idx) => {\n if (typeof item !== 'string' || item.trim().length === 0) {\n throw new ManifestError('invalid-config', `${key}[${idx}] must be a non-empty string`, key);\n }\n return isAbsolute(item) ? item : resolve(configDir, item);\n });\n}\n\n// Regexes mirror the console's client-side validators (`is-email` CDgIL0c0\n// bundle + VALIDATION-RULES.md). Keeping local validators lets agent-\n// plugin surface `missing-required-field`/`invalid-config` instead of a\n// pass-through `api-error` when the user supplies garbage.\n//\n// These constants/helpers are exported so `aitcc app init`'s prompt-level\n// validators can reuse them without duplicating the rules (drift risk).\nexport const EMAIL_REGEX =\n /^(([^<>()[\\]\\\\.,;:\\s@\"]+(\\.[^<>()[\\]\\\\.,;:\\s@\"]+)*)|(\".+\"))@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}])|(([a-zA-Z\\-0-9]+\\.)+[a-zA-Z]{2,}))$/;\n\nexport function isValidEmail(v: string): boolean {\n return EMAIL_REGEX.test(v.toLowerCase());\n}\n\n// titleKo/titleEn server-side rules (sdk-example#39, 2026-05-03 dog-food).\n// Server returns prefix-form errorCodes `miniApp.InvalidTitle` /\n// `miniApp.InvalidTitleEn` (distinct from the legacy numeric catalog like\n// `4046`). We preflight here so the user does not eat a register round-trip\n// just to discover a typo.\n//\n// titleKo: Korean / English / digits / space / `:·?` only; ≤ 10 code points\n// excluding spaces.\n// titleEn: `^[A-Za-z0-9 :·?]+$`; ≤ 15 code points excluding spaces; AND\n// each space-separated word must be title-case (first char uppercase, rest\n// lowercase). All-caps tokens like `AITC`/`SDK` are rejected by the server.\nexport const TITLE_KO_REGEX = /^[가-힣A-Za-z0-9 :·?]+$/;\nexport const TITLE_EN_REGEX = /^[A-Za-z0-9 :·?]+$/;\n// appName slug: kebab-case starting with a lowercase letter. Mirrors the\n// console's slug rule observed during dog-food (server rejects uppercase\n// or leading digits). Exported for prompt validators in `app init`.\nexport const APP_NAME_REGEX = /^[a-z][a-z0-9-]*$/;\n\nexport const MANIFEST_LIMITS = {\n titleKoMaxCodepoints: 10,\n titleEnMaxCodepoints: 15,\n subtitleMaxChars: 20,\n descriptionMaxCodepoints: 500,\n keywordsMax: 10,\n verticalScreenshotsMin: 3,\n} as const;\n\nconst TITLE_KO_MAX_CODEPOINTS_NO_SPACE = MANIFEST_LIMITS.titleKoMaxCodepoints;\nconst TITLE_EN_MAX_CODEPOINTS_NO_SPACE = MANIFEST_LIMITS.titleEnMaxCodepoints;\n\nexport function countCodepointsExcludingSpaces(v: string): number {\n return [...v].filter((ch) => ch !== ' ').length;\n}\n\n// Backwards-compatible alias retained as a private name for the existing\n// validator callsites — public consumers go through `countCodepointsExcludingSpaces`.\nconst codePointsExcludingSpaces = countCodepointsExcludingSpaces;\n\nexport function isTitleCaseWord(word: string): boolean {\n // Only enforced on words that contain ASCII letters; pure-digit or\n // punctuation-only tokens (e.g. `:`, `V2`, `123`) pass through. The\n // server's exact tokenization is not documented — using \"first letter\n // uppercase, remaining letters lowercase\" mirrors the rejections we\n // observed for `AITC` and `SDK`. A token like `v2` is rejected because\n // the leading letter must be uppercase.\n const chars = [...word];\n const firstLetterIdx = chars.findIndex((ch) => /[A-Za-z]/.test(ch));\n if (firstLetterIdx === -1) return true;\n for (let i = 0; i < chars.length; i++) {\n const ch = chars[i];\n if (ch === undefined || !/[A-Za-z]/.test(ch)) continue;\n const expectUpper = i === firstLetterIdx;\n if (expectUpper && ch !== ch.toUpperCase()) return false;\n if (!expectUpper && ch !== ch.toLowerCase()) return false;\n }\n return true;\n}\n\n// Dog-food #23 (2026-04-22) HTTP 400 errorCode=4000:\n// \"앱 상세설명은 최대 500자를 넘어갈 수 없어요\". Counted by code points\n// (`[...str].length`) to err on the strict side — the server's internal\n// counting rule is not documented, so we use the count that treats\n// astral characters as single units.\nconst DETAIL_DESCRIPTION_MAX_CODEPOINTS = MANIFEST_LIMITS.descriptionMaxCodepoints;\n\nfunction isValidHttpUrl(v: string): boolean {\n try {\n const parsed = new URL(v);\n return parsed.protocol === 'http:' || parsed.protocol === 'https:';\n } catch {\n return false;\n }\n}\n\nfunction validateManifest(raw: Record<string, unknown>, configDir: string): AppManifest {\n const titleKo = requireString(raw, 'titleKo');\n if (!TITLE_KO_REGEX.test(titleKo)) {\n throw new ManifestError(\n 'invalid-config',\n `titleKo may only contain Korean/English letters, digits, spaces, and \":·?\" (got \"${titleKo}\"; server-side rule, errorCode: miniApp.InvalidTitle)`,\n 'titleKo',\n );\n }\n const titleKoLen = codePointsExcludingSpaces(titleKo);\n if (titleKoLen > TITLE_KO_MAX_CODEPOINTS_NO_SPACE) {\n throw new ManifestError(\n 'invalid-config',\n `titleKo must be ${TITLE_KO_MAX_CODEPOINTS_NO_SPACE} characters or fewer excluding spaces (got ${titleKoLen}; server-side rule, errorCode: miniApp.InvalidTitle)`,\n 'titleKo',\n );\n }\n const titleEn = requireString(raw, 'titleEn');\n if (!TITLE_EN_REGEX.test(titleEn)) {\n throw new ManifestError(\n 'invalid-config',\n `titleEn may only contain English letters, digits, spaces, and \":·?\" (got \"${titleEn}\"; server-side rule, errorCode: miniApp.InvalidTitleEn)`,\n 'titleEn',\n );\n }\n const titleEnLen = codePointsExcludingSpaces(titleEn);\n if (titleEnLen > TITLE_EN_MAX_CODEPOINTS_NO_SPACE) {\n throw new ManifestError(\n 'invalid-config',\n `titleEn must be ${TITLE_EN_MAX_CODEPOINTS_NO_SPACE} characters or fewer excluding spaces (got ${titleEnLen}; server-side rule, errorCode: miniApp.InvalidTitleEn)`,\n 'titleEn',\n );\n }\n for (const word of titleEn.split(' ')) {\n if (word.length === 0) continue;\n if (!isTitleCaseWord(word)) {\n throw new ManifestError(\n 'invalid-config',\n `titleEn word \"${word}\" must be title-case (first letter uppercase, rest lowercase); server-side rule, errorCode: miniApp.InvalidTitleEn`,\n 'titleEn',\n );\n }\n }\n const appName = requireString(raw, 'appName');\n const csEmail = requireString(raw, 'csEmail');\n if (!isValidEmail(csEmail)) {\n throw new ManifestError(\n 'invalid-config',\n `csEmail is not a valid email address (got ${csEmail})`,\n 'csEmail',\n );\n }\n const subtitle = requireString(raw, 'subtitle');\n // subtitle ≤ 20 chars (F(20) in VALIDATION-RULES).\n if (subtitle.length > MANIFEST_LIMITS.subtitleMaxChars) {\n throw new ManifestError(\n 'invalid-config',\n `subtitle must be ${MANIFEST_LIMITS.subtitleMaxChars} characters or fewer (got ${subtitle.length})`,\n 'subtitle',\n );\n }\n const description = requireString(raw, 'description');\n const descriptionCodepoints = [...description].length;\n if (descriptionCodepoints > DETAIL_DESCRIPTION_MAX_CODEPOINTS) {\n throw new ManifestError(\n 'invalid-config',\n `description must be ${DETAIL_DESCRIPTION_MAX_CODEPOINTS} characters or fewer (got ${descriptionCodepoints})`,\n 'description',\n );\n }\n const homePageUri = optionalString(raw, 'homePageUri');\n if (homePageUri !== undefined && !isValidHttpUrl(homePageUri)) {\n throw new ManifestError(\n 'invalid-config',\n `homePageUri must be a http(s) URL (got ${homePageUri})`,\n 'homePageUri',\n );\n }\n const logo = requirePath(raw, 'logo', configDir);\n const logoDarkMode = optionalPath(raw, 'logoDarkMode', configDir);\n const horizontalThumbnail = requirePath(raw, 'horizontalThumbnail', configDir);\n const categoryIds = requireNumberArray(raw, 'categoryIds', { min: 1 });\n const keywords = optionalStringArray(raw, 'keywords', { max: 10 });\n const verticalScreenshots = requirePathArray(raw, 'verticalScreenshots', configDir, { min: 3 });\n const horizontalScreenshots = optionalPathArray(raw, 'horizontalScreenshots', configDir);\n\n return {\n titleKo,\n titleEn,\n appName,\n homePageUri,\n csEmail,\n logo,\n logoDarkMode,\n horizontalThumbnail,\n categoryIds,\n subtitle,\n description,\n keywords,\n verticalScreenshots,\n horizontalScreenshots,\n };\n}\n","// Pure renderer for `aitcc app init`. Lives separately from the handler\n// so its only dependency is on the answers object; tests pin the literal\n// output without spinning up any I/O. The yaml is hand-rolled (rather\n// than going through `yaml.stringify` + comment injection) because the\n// commented-out optional block needs precise placement to be useful, and\n// mixing programmatic comments into a `Document` ends up more brittle\n// than a template string.\n\nexport interface InitAnswers {\n readonly workspaceId: number;\n readonly titleKo: string;\n readonly titleEn: string;\n readonly appName: string;\n readonly csEmail: string;\n readonly subtitle: string;\n readonly description: string;\n readonly categoryIds: readonly number[];\n}\n\nexport function renderInitYaml(answers: InitAnswers): string {\n const description = indentBlock(answers.description, ' ');\n return `# Project context — read by every aitcc command in this directory tree.\n# Generated by \\`aitcc app init\\` (https://github.com/apps-in-toss-community/console-cli).\nworkspaceId: ${answers.workspaceId}\n# miniAppId is written back automatically by \\`aitcc app register\\`.\n\n# --- App manifest (required) ---\ntitleKo: ${yamlDoubleQuoted(answers.titleKo)}\ntitleEn: ${yamlDoubleQuoted(answers.titleEn)}\nappName: ${answers.appName}\ncsEmail: ${answers.csEmail}\nsubtitle: ${yamlDoubleQuoted(answers.subtitle)}\ndescription: |-\n${description}\ncategoryIds: [${answers.categoryIds.join(', ')}]\nlogo: ./assets/logo.png\nhorizontalThumbnail: ./assets/thumbnail.png\nverticalScreenshots:\n - ./assets/screenshot-1.png\n - ./assets/screenshot-2.png\n - ./assets/screenshot-3.png\n\n# --- Optional fields (uncomment + fill in if needed) ---\n# homePageUri: \"https://example.com/\"\n# logoDarkMode: ./assets/logo-dark.png\n# keywords: [foo, bar]\n# horizontalScreenshots:\n# - ./assets/horizontal-1.png\n`;\n}\n\n// Forces a quoted scalar so colons, leading whitespace, or yaml-significant\n// chars in user input can't accidentally break the document shape.\nfunction yamlDoubleQuoted(value: string): string {\n const escaped = value.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"');\n return `\"${escaped}\"`;\n}\n\n// `|-` block scalars require every content line to share the same indent;\n// without this helper a multi-line description would silently corrupt the\n// surrounding document.\nfunction indentBlock(value: string, prefix: string): string {\n return value\n .split('\\n')\n .map((line) => `${prefix}${line}`)\n .join('\\n');\n}\n","import { access, mkdir, writeFile } from 'node:fs/promises';\nimport { isAbsolute, resolve as resolvePath } from 'node:path';\nimport { checkbox, editor, input, select } from '@inquirer/prompts';\nimport { parseDocument } from 'yaml';\nimport { fetchConsoleMemberUserInfo } from '../api/me.js';\nimport { fetchImpressionCategoryList } from '../api/mini-apps.js';\nimport type { CdpCookie } from '../cdp.js';\nimport {\n APP_NAME_REGEX,\n countCodepointsExcludingSpaces,\n isTitleCaseWord,\n isValidEmail,\n MANIFEST_LIMITS,\n TITLE_EN_REGEX,\n TITLE_KO_REGEX,\n} from '../config/app-manifest.js';\nimport { ExitCode } from '../exit.js';\nimport { exitAfterFlush } from '../flush.js';\nimport { readSession } from '../session.js';\nimport { emitFailureFromError, emitJson, emitNotAuthenticated } from './_shared.js';\nimport { type InitAnswers, renderInitYaml } from './app-init-template.js';\n\n// `runAppInit` is the testable seam for `aitcc app init`. The citty\n// wrapper in `app.ts` is a thin shim. Tests cover the render + yaml\n// round-trip, the conflict path (existing manifest), and the non-TTY\n// refusal — the inquirer prompts themselves are not mocked because the\n// scope of the test is the file-IO contract, not the keystroke flow.\n//\n// --json contract:\n//\n// The command itself does not support interactive prompts under\n// `--json` or in non-TTY environments. Both refuse with:\n// { ok: false, reason: 'interactive-required', message } exit 2\n//\n// On success the user only ever sees the human-readable next-steps\n// block on stderr. There is no machine-readable success shape — by\n// design, this command is a one-shot bootstrap meant for humans.\n\nexport interface AppInitArgs {\n readonly cwd?: string;\n readonly force: boolean;\n readonly json: boolean;\n}\n\nexport async function runAppInit(args: AppInitArgs): Promise<void> {\n const cwd = args.cwd ?? process.cwd();\n\n if (args.json) {\n emitInteractiveRequired(true);\n return exitAfterFlush(ExitCode.Usage);\n }\n if (!process.stdout.isTTY || !process.stdin.isTTY) {\n emitInteractiveRequired(false);\n return exitAfterFlush(ExitCode.Usage);\n }\n\n const yamlPath = resolvePath(cwd, 'aitcc.yaml');\n const jsonPath = resolvePath(cwd, 'aitcc.json');\n if (!args.force) {\n const existing = (await fileExists(yamlPath))\n ? yamlPath\n : (await fileExists(jsonPath))\n ? jsonPath\n : null;\n if (existing) {\n process.stderr.write(\n `A project file already exists at ${existing}. Pass --force to overwrite.\\n`,\n );\n return exitAfterFlush(ExitCode.Usage);\n }\n }\n\n const session = await readSession();\n if (!session) {\n emitNotAuthenticated(false);\n return exitAfterFlush(ExitCode.NotAuthenticated);\n }\n\n let workspaceId: number;\n let categoryIds: number[];\n try {\n workspaceId = await pickWorkspace(session.cookies);\n categoryIds = await pickCategories(session.cookies);\n } catch (err) {\n if (isPromptCancelled(err)) {\n process.stderr.write('Aborted.\\n');\n return exitAfterFlush(ExitCode.Usage);\n }\n return emitFailureFromError(false, err);\n }\n\n let answers: InitAnswers;\n try {\n const titleKo = await input({\n message: 'App title (Korean):',\n validate: validateTitleKo,\n });\n const titleEn = await input({\n message: 'App title (English):',\n validate: validateTitleEn,\n });\n const appName = await input({\n message: 'App slug (kebab-case):',\n validate: validateAppName,\n });\n const csEmail = await input({\n message: 'Customer-support email:',\n validate: validateEmail,\n });\n const subtitle = await input({\n message: `Subtitle (≤${MANIFEST_LIMITS.subtitleMaxChars} chars):`,\n validate: validateSubtitle,\n });\n const description = await editor({\n message: 'Long description (opens $EDITOR):',\n validate: validateDescription,\n });\n answers = {\n workspaceId,\n titleKo,\n titleEn,\n appName,\n csEmail,\n subtitle,\n description,\n categoryIds,\n };\n } catch (err) {\n if (isPromptCancelled(err)) {\n process.stderr.write('Aborted.\\n');\n return exitAfterFlush(ExitCode.Usage);\n }\n throw err;\n }\n\n const rendered = renderInitYaml(answers);\n\n // Round-trip the rendered yaml so a template bug surfaces here, not\n // in the user's next `aitcc app register` run. Failure here is a CLI\n // bug (renderer fault), not user input.\n const doc = parseDocument(rendered);\n if (doc.errors.length > 0) {\n const detail = doc.errors[0]?.message ?? 'unknown parse error';\n process.stderr.write(`internal error: rendered yaml failed to parse (${detail})\\n`);\n return exitAfterFlush(ExitCode.Generic);\n }\n\n // Create the assets dir before writing the manifest so a permission /\n // disk error can't leave a `aitcc.yaml` pointing at a directory we\n // failed to provision. Both calls are wrapped so an FS failure surfaces\n // as a clean error + exit code rather than an unhandled rejection.\n try {\n const assetsDir = resolvePath(cwd, 'assets');\n await mkdir(assetsDir, { recursive: true });\n await writeFile(yamlPath, rendered, 'utf8');\n } catch (err) {\n const detail = err instanceof Error ? err.message : String(err);\n process.stderr.write(`failed to write project files: ${detail}\\n`);\n return exitAfterFlush(ExitCode.Generic);\n }\n\n emitNextSteps(yamlPath);\n return exitAfterFlush(ExitCode.Ok);\n}\n\nfunction emitInteractiveRequired(json: boolean): void {\n const message = 'aitcc app init requires an interactive TTY.';\n if (json) {\n emitJson({ ok: false, reason: 'interactive-required', message });\n } else {\n process.stderr.write(`${message}\\n`);\n }\n}\n\nfunction emitNextSteps(yamlPath: string): void {\n const rel = isAbsolute(yamlPath) ? `./${relativeFromCwd(yamlPath)}` : yamlPath;\n process.stderr.write(`✓ wrote ${rel}\\n`);\n process.stderr.write('Next steps:\\n');\n process.stderr.write(\n ' 1. Drop these images into ./assets/ (dimensions enforced on register):\\n',\n );\n process.stderr.write(' logo.png 600×600\\n');\n process.stderr.write(' thumbnail.png 1932×828\\n');\n process.stderr.write(' screenshot-1.png 636×1048\\n');\n process.stderr.write(' screenshot-2.png 636×1048\\n');\n process.stderr.write(' screenshot-3.png 636×1048\\n');\n process.stderr.write(' 2. Run `aitcc app register` to create the mini-app.\\n');\n process.stderr.write(' (`miniAppId` is written back into ./aitcc.yaml automatically.)\\n');\n}\n\nfunction relativeFromCwd(absPath: string): string {\n const cwd = process.cwd();\n if (absPath.startsWith(`${cwd}/`)) return absPath.slice(cwd.length + 1);\n return absPath;\n}\n\nasync function fileExists(path: string): Promise<boolean> {\n try {\n await access(path);\n return true;\n } catch {\n return false;\n }\n}\n\n// Inquirer raises `ExitPromptError` (Ctrl+C / SIGINT) so the caller can\n// distinguish a deliberate abort from a programming error. We sniff\n// `err.name` rather than `instanceof` because the class lives in\n// `@inquirer/core`, which we don't depend on directly — this avoids\n// pulling in a transitive package as a top-level dep just for an\n// `instanceof` check the inquirer docs already recommend by name.\nfunction isPromptCancelled(err: unknown): boolean {\n return err instanceof Error && err.name === 'ExitPromptError';\n}\n\nasync function pickWorkspace(cookies: readonly CdpCookie[]): Promise<number> {\n const info = await fetchConsoleMemberUserInfo(cookies);\n const workspaces = info.workspaces;\n if (workspaces.length === 0) {\n process.stderr.write(\n 'Your account has no workspaces. Create one in the Apps in Toss console first.\\n',\n );\n await exitAfterFlush(ExitCode.Usage);\n }\n if (workspaces.length === 1) {\n const only = workspaces[0];\n if (!only) throw new Error('unreachable: workspaces[0] missing');\n process.stderr.write(\n `Using workspace ${only.workspaceId} (${only.workspaceName}) — only one available.\\n`,\n );\n return only.workspaceId;\n }\n const choice = await select<number>({\n message: 'Workspace:',\n choices: workspaces.map((w) => ({\n name: `${w.workspaceId} ${w.workspaceName}`,\n value: w.workspaceId,\n })),\n });\n return choice;\n}\n\nasync function pickCategories(cookies: readonly CdpCookie[]): Promise<number[]> {\n const tree = await fetchImpressionCategoryList(cookies);\n // Build a flat list of selectable leaves (sub-categories where present,\n // otherwise top-level categories). Group headings are interleaved as\n // disabled items so the user can still see the hierarchy.\n type Choice = {\n name: string;\n value: number;\n disabled?: string | true;\n };\n const choices: Choice[] = [];\n for (const group of tree) {\n if (!group.categoryGroup.isSelectable) continue;\n choices.push({\n name: `── ${group.categoryGroup.name} ──`,\n value: -group.categoryGroup.id,\n disabled: ' ',\n });\n for (const cat of group.categoryList) {\n if (cat.subCategoryList.length > 0) {\n for (const sub of cat.subCategoryList) {\n if (!sub.isSelectable) continue;\n choices.push({\n name: ` ${cat.name} › ${sub.name} [${sub.id}]`,\n value: sub.id,\n });\n }\n } else if (cat.isSelectable) {\n choices.push({\n name: ` ${cat.name} [${cat.id}]`,\n value: cat.id,\n });\n }\n }\n }\n if (choices.every((c) => c.disabled !== undefined)) {\n throw new Error('No selectable categories returned by the server.');\n }\n const picked = await checkbox<number>({\n message: 'Categories (space to toggle, enter to confirm; pick at least one):',\n choices,\n validate: (entries) => (entries.length === 0 ? 'pick at least one category' : true),\n pageSize: 20,\n });\n return picked;\n}\n\n// --- Prompt validators ---\n//\n// inquirer expects `true` for \"valid\" or a string error message otherwise.\n// Each validator mirrors the corresponding manifest-level rule so the\n// values produced by `init` will always pass `register`'s validator —\n// see `app-manifest.ts` for the source-of-truth constants/regexes.\n\nfunction validateTitleKo(raw: string): true | string {\n if (raw.trim().length === 0) return 'titleKo is required';\n if (!TITLE_KO_REGEX.test(raw)) {\n return 'only Korean/English letters, digits, spaces, and \":·?\" are allowed';\n }\n const len = countCodepointsExcludingSpaces(raw);\n if (len > MANIFEST_LIMITS.titleKoMaxCodepoints) {\n return `must be ≤ ${MANIFEST_LIMITS.titleKoMaxCodepoints} characters excluding spaces (got ${len})`;\n }\n return true;\n}\n\nfunction validateTitleEn(raw: string): true | string {\n if (raw.trim().length === 0) return 'titleEn is required';\n if (!TITLE_EN_REGEX.test(raw)) {\n return 'only English letters, digits, spaces, and \":·?\" are allowed';\n }\n const len = countCodepointsExcludingSpaces(raw);\n if (len > MANIFEST_LIMITS.titleEnMaxCodepoints) {\n return `must be ≤ ${MANIFEST_LIMITS.titleEnMaxCodepoints} characters excluding spaces (got ${len})`;\n }\n for (const word of raw.split(' ')) {\n if (word.length === 0) continue;\n if (!isTitleCaseWord(word)) {\n return `word \"${word}\" must be title-case (first letter upper, rest lower)`;\n }\n }\n return true;\n}\n\nfunction validateAppName(raw: string): true | string {\n if (!APP_NAME_REGEX.test(raw)) {\n return 'must be kebab-case starting with a lowercase letter (a-z, 0-9, hyphen)';\n }\n return true;\n}\n\nfunction validateEmail(raw: string): true | string {\n if (!isValidEmail(raw)) return 'not a valid email address';\n return true;\n}\n\nfunction validateSubtitle(raw: string): true | string {\n if (raw.trim().length === 0) return 'subtitle is required';\n if (raw.length > MANIFEST_LIMITS.subtitleMaxChars) {\n return `must be ≤ ${MANIFEST_LIMITS.subtitleMaxChars} characters (got ${raw.length})`;\n }\n return true;\n}\n\nfunction validateDescription(raw: string): true | string {\n if (raw.trim().length === 0) return 'description is required';\n const len = [...raw].length;\n if (len > MANIFEST_LIMITS.descriptionMaxCodepoints) {\n return `must be ≤ ${MANIFEST_LIMITS.descriptionMaxCodepoints} characters (got ${len})`;\n }\n return true;\n}\n","import { readFile } from 'node:fs/promises';\nimport { imageSize } from 'image-size';\n\n// Image dimension validation lives here (rather than inline in the\n// command) so a future swap of the underlying library is confined. The\n// console upload endpoint validates validWidth/validHeight server-side\n// with a hard 400, but failing locally gives the agent-plugin consumer\n// a structured error (path + expected + actual) instead of a pass-\n// through api-error reason.\n//\n// image-size reads the PNG/JPEG/etc. header directly; we pass a Buffer\n// so we can distinguish \"file missing\" (ENOENT → unreadable) from\n// \"unknown format\" (library throws → unreadable) without two code paths.\n\nexport type ImageDimensionErrorReason = 'mismatch' | 'unreadable';\n\nexport class ImageDimensionError extends Error {\n readonly path: string;\n readonly expected: string;\n readonly actual: string | undefined;\n readonly reason: ImageDimensionErrorReason;\n\n constructor(args: {\n path: string;\n expected: string;\n actual: string | undefined;\n reason: ImageDimensionErrorReason;\n message: string;\n }) {\n super(args.message);\n this.name = 'ImageDimensionError';\n this.path = args.path;\n this.expected = args.expected;\n this.actual = args.actual;\n this.reason = args.reason;\n }\n}\n\nexport interface Dimension {\n readonly width: number;\n readonly height: number;\n}\n\nfunction format(dim: Dimension): string {\n return `${dim.width}x${dim.height}`;\n}\n\nexport async function validateImageDimensions(path: string, expected: Dimension): Promise<void> {\n let buffer: Buffer;\n try {\n buffer = await readFile(path);\n } catch (err) {\n throw new ImageDimensionError({\n path,\n expected: format(expected),\n actual: undefined,\n reason: 'unreadable',\n message: `could not read image at ${path}: ${(err as Error).message}`,\n });\n }\n let dims: { width?: number; height?: number };\n try {\n dims = imageSize(buffer);\n } catch (err) {\n throw new ImageDimensionError({\n path,\n expected: format(expected),\n actual: undefined,\n reason: 'unreadable',\n message: `could not decode image header at ${path}: ${(err as Error).message}`,\n });\n }\n if (typeof dims.width !== 'number' || typeof dims.height !== 'number') {\n throw new ImageDimensionError({\n path,\n expected: format(expected),\n actual: undefined,\n reason: 'unreadable',\n message: `image header at ${path} did not expose width/height`,\n });\n }\n if (dims.width !== expected.width || dims.height !== expected.height) {\n const actual = `${dims.width}x${dims.height}`;\n throw new ImageDimensionError({\n path,\n expected: format(expected),\n actual,\n reason: 'mismatch',\n message: `image ${path} has dimensions ${actual}; expected ${format(expected)}`,\n });\n }\n}\n\n// Canonical dimension specs for the register flow. Centralising here keeps\n// the command and the payload builder agreeing on the same source of truth.\nexport const DIMENSIONS = {\n logo: { width: 600, height: 600 },\n horizontalThumbnail: { width: 1932, height: 828 },\n verticalScreenshot: { width: 636, height: 1048 },\n horizontalScreenshot: { width: 1504, height: 741 },\n} as const;\n","import type { MiniAppImageEntry, MiniAppSubmitPayload } from '../api/mini-apps.js';\nimport type { AppManifest } from '../config/app-manifest.js';\n\n// Pure transformation from a loaded AppManifest + the URLs produced by\n// the upload step into the `{miniApp, impression}` submit body. The\n// structure mirrors the `Xc` function from the console bundle (see\n// VALIDATION-RULES.md in the local `.playwright-mcp/`). Dog-food task\n// #23 captures the first real network exchange and will either confirm\n// this shape or correct it here.\n\nexport interface UploadedImageUrls {\n readonly logo: string;\n readonly logoDarkMode: string | undefined;\n readonly horizontalThumbnail: string;\n readonly verticalScreenshots: readonly string[];\n readonly horizontalScreenshots: readonly string[];\n}\n\nexport function buildSubmitPayload(\n manifest: AppManifest,\n urls: UploadedImageUrls,\n): MiniAppSubmitPayload {\n const images: MiniAppImageEntry[] = [\n { imageUrl: urls.horizontalThumbnail, imageType: 'THUMBNAIL', orientation: 'HORIZONTAL' },\n ...urls.verticalScreenshots.map<MiniAppImageEntry>((u) => ({\n imageUrl: u,\n imageType: 'PREVIEW',\n orientation: 'VERTICAL',\n })),\n ...urls.horizontalScreenshots.map<MiniAppImageEntry>((u) => ({\n imageUrl: u,\n imageType: 'PREVIEW',\n orientation: 'HORIZONTAL',\n })),\n ];\n\n const miniApp: MiniAppSubmitPayload['miniApp'] = {\n title: manifest.titleKo,\n titleEn: manifest.titleEn,\n appName: manifest.appName,\n iconUri: urls.logo,\n status: 'PREPARE',\n csEmail: manifest.csEmail,\n description: manifest.subtitle,\n detailDescription: manifest.description,\n images,\n ...(urls.logoDarkMode !== undefined ? { darkModeIconUri: urls.logoDarkMode } : {}),\n ...(manifest.homePageUri !== undefined ? { homePageUri: manifest.homePageUri } : {}),\n };\n\n const impression: MiniAppSubmitPayload['impression'] = {\n keywordList: manifest.keywords,\n categoryIds: manifest.categoryIds,\n };\n\n return { miniApp, impression };\n}\n","import { readFile } from 'node:fs/promises';\nimport { basename } from 'node:path';\nimport {\n type CreateMiniAppResult,\n createMiniApp,\n type UploadParams,\n uploadMiniAppResource,\n} from '../api/mini-apps.js';\nimport type { CdpCookie } from '../cdp.js';\nimport {\n type AppManifest,\n loadAppManifest,\n ManifestError,\n resolveManifestPath,\n} from '../config/app-manifest.js';\nimport {\n DIMENSIONS,\n ImageDimensionError,\n validateImageDimensions,\n} from '../config/image-validator.js';\nimport { findProjectContext, writeProjectMiniAppId } from '../config/project-context.js';\nimport { ExitCode } from '../exit.js';\nimport { exitAfterFlush } from '../flush.js';\nimport {\n emitFailureFromError,\n emitJson,\n printContextHeader,\n resolveWorkspaceContext,\n} from './_shared.js';\nimport { buildSubmitPayload, type UploadedImageUrls } from './register-payload.js';\n\n// `runRegister` is the testable seam for `aitcc app register`. The public\n// command (defined in `app.ts`) is a thin citty wrapper that supplies the\n// real `uploadMiniAppResource` and `createMiniApp` implementations;\n// tests pass stubs so each branch of the documented `--json` contract\n// gets pinned byte-for-byte without spawning a subprocess.\n//\n// --json contract (consumed by agent-plugin):\n//\n// success:\n// { ok: true, workspaceId, appId, reviewState, consoleUrl } exit 0\n// consoleUrl:\n// https://apps-in-toss.toss.im/console/workspace/<wid>/mini-app/<id>\n// (null when the server response omitted miniAppId)\n//\n// failures:\n// { ok: false, reason: 'no-workspace-selected' } exit 2\n// { ok: false, reason: 'invalid-config', message } exit 2\n// { ok: false, reason: 'missing-required-field', field, message } exit 2\n// { ok: false, reason: 'image-dimension-mismatch',\n// path, expected, actual, message } exit 2\n// { ok: false, reason: 'image-unreadable', path, message } exit 2\n// { ok: true, authenticated: false } exit 10\n// { ok: false, reason: 'network-error', message } exit 11\n// { ok: false, reason: 'api-error',\n// status?, errorCode?, message } exit 17\n//\n// --dry-run:\n// { ok: true, dryRun: true, workspaceId, payload } exit 0\n// (no uploads, no submit — manifest + image dimensions are still\n// validated so dry-run catches the same local errors as a real run.)\n//\n// --accept-terms required for real submits:\n// The console UI gates \"검토 요청하기\" on several mandatory legal-\n// agreement checkboxes (common + category-dependent — see\n// VALIDATION-RULES.md). We can't see those on the wire yet (payload\n// shape is inferred), so the CLI enforces the gate locally: submit\n// refuses without --accept-terms. The flag is not required for\n// --dry-run.\n// { ok: false, reason: 'terms-not-accepted', message } exit 2\n\nexport interface RegisterArgs {\n readonly workspace?: string | undefined;\n readonly config?: string | undefined;\n readonly json: boolean;\n readonly dryRun?: boolean | undefined;\n readonly acceptTerms?: boolean | undefined;\n}\n\nexport interface RegisterDeps {\n readonly cwd?: string;\n readonly uploadImpl?: (params: UploadParams) => Promise<string>;\n readonly submitImpl?: (\n workspaceId: number,\n payload: ReturnType<typeof buildSubmitPayload>,\n cookies: readonly CdpCookie[],\n ) => Promise<CreateMiniAppResult>;\n}\n\n// `runRegister` returns `Promise<void>` as a type-level handshake — at\n// runtime every code path either awaits `exitAfterFlush` (which calls\n// `process.exit` and never returns) or bubbles a thrown exception. A\n// future maintainer should not try to `catch` the absence of a return\n// value as \"success\"; the success signal is `process.exit(0)` itself.\n//\n// `deps` defaults to `{}` so the citty wrapper (`app.ts`) doesn't need\n// to pass a literal every call; tests override specific fields.\nexport async function runRegister(args: RegisterArgs, deps: RegisterDeps = {}): Promise<void> {\n const ctx = await resolveWorkspaceContext({\n json: args.json,\n ...(args.workspace !== undefined ? { workspace: args.workspace } : {}),\n });\n if (!ctx) return;\n const { session, workspaceId } = ctx;\n printContextHeader(ctx, { json: args.json });\n\n const manifest = await loadAndValidateManifest(args, deps);\n if (!manifest) return;\n\n // --accept-terms gate: required for real submits only. --dry-run\n // skips it so users can iterate on their manifest without being\n // forced to attest to the legal-agreement checkboxes each time.\n if (!args.dryRun && !args.acceptTerms) {\n emitTermsNotAccepted(args.json);\n await exitAfterFlush(ExitCode.Usage);\n return;\n }\n\n try {\n if (args.dryRun) {\n // Emit the payload with placeholder URLs so the user can\n // inspect exactly what would be sent without spending a round\n // of uploads. Useful during dog-food verification when the\n // inferred payload shape is in flight.\n const placeholderUrls: UploadedImageUrls = {\n logo: '<dry-run:logo>',\n logoDarkMode: manifest.logoDarkMode !== undefined ? '<dry-run:logoDarkMode>' : undefined,\n horizontalThumbnail: '<dry-run:horizontalThumbnail>',\n verticalScreenshots: manifest.verticalScreenshots.map(\n (_, i) => `<dry-run:verticalScreenshots[${i}]>`,\n ),\n horizontalScreenshots: manifest.horizontalScreenshots.map(\n (_, i) => `<dry-run:horizontalScreenshots[${i}]>`,\n ),\n };\n const payload = buildSubmitPayload(manifest, placeholderUrls);\n emitDryRun(args.json, workspaceId, payload);\n return exitAfterFlush(ExitCode.Ok);\n }\n\n const urls = await uploadAllImages(workspaceId, manifest, session.cookies, deps);\n const payload = buildSubmitPayload(manifest, urls);\n const submitImpl = deps.submitImpl ?? ((wid, p, c) => createMiniApp(wid, p, c));\n const result = await submitImpl(workspaceId, payload, session.cookies);\n emitSuccess(args.json, workspaceId, result);\n await persistMiniAppIdToProject(args.json, result.miniAppId, deps.cwd ?? process.cwd());\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureAndExit(args.json, err);\n }\n}\n\nasync function loadAndValidateManifest(\n args: RegisterArgs,\n deps: RegisterDeps,\n): Promise<AppManifest | null> {\n const cwd = deps.cwd ?? process.cwd();\n let manifest: AppManifest;\n try {\n const manifestPath = await resolveManifestPath(args.config, cwd);\n manifest = await loadAppManifest(manifestPath);\n } catch (err) {\n if (err instanceof ManifestError) {\n emitManifestError(args.json, err);\n await exitAfterFlush(ExitCode.Usage);\n return null;\n }\n throw err;\n }\n\n // Image dimension checks run after manifest validation because they\n // need paths that the manifest already resolved to absolute.\n try {\n await validateImageDimensions(manifest.logo, DIMENSIONS.logo);\n if (manifest.logoDarkMode !== undefined) {\n await validateImageDimensions(manifest.logoDarkMode, DIMENSIONS.logo);\n }\n await validateImageDimensions(manifest.horizontalThumbnail, DIMENSIONS.horizontalThumbnail);\n for (const p of manifest.verticalScreenshots) {\n await validateImageDimensions(p, DIMENSIONS.verticalScreenshot);\n }\n for (const p of manifest.horizontalScreenshots) {\n await validateImageDimensions(p, DIMENSIONS.horizontalScreenshot);\n }\n } catch (err) {\n if (err instanceof ImageDimensionError) {\n emitImageDimensionError(args.json, err);\n await exitAfterFlush(ExitCode.Usage);\n return null;\n }\n throw err;\n }\n\n return manifest;\n}\n\nasync function uploadAllImages(\n workspaceId: number,\n manifest: AppManifest,\n cookies: readonly CdpCookie[],\n deps: RegisterDeps,\n): Promise<UploadedImageUrls> {\n // Serial on purpose: dog-food task #23 has not yet confirmed that the\n // console's `/resource/:wid/upload` endpoint tolerates concurrent\n // POSTs from the same session. `Promise.all` would shave a few seconds\n // off a 5-image upload, but until we've observed the server under\n // parallel load, the failure mode (\"429? 503? silent drop?\") is\n // unknown and a first-registration flake is much more expensive to\n // debug than a slower linear run.\n const uploadImpl = deps.uploadImpl ?? ((p) => uploadMiniAppResource(p));\n\n const logo = await uploadOne(uploadImpl, {\n workspaceId,\n validWidth: DIMENSIONS.logo.width,\n validHeight: DIMENSIONS.logo.height,\n cookies,\n path: manifest.logo,\n });\n const logoDarkMode =\n manifest.logoDarkMode !== undefined\n ? await uploadOne(uploadImpl, {\n workspaceId,\n validWidth: DIMENSIONS.logo.width,\n validHeight: DIMENSIONS.logo.height,\n cookies,\n path: manifest.logoDarkMode,\n })\n : undefined;\n const horizontalThumbnail = await uploadOne(uploadImpl, {\n workspaceId,\n validWidth: DIMENSIONS.horizontalThumbnail.width,\n validHeight: DIMENSIONS.horizontalThumbnail.height,\n cookies,\n path: manifest.horizontalThumbnail,\n });\n const verticalScreenshots: string[] = [];\n for (const p of manifest.verticalScreenshots) {\n verticalScreenshots.push(\n await uploadOne(uploadImpl, {\n workspaceId,\n validWidth: DIMENSIONS.verticalScreenshot.width,\n validHeight: DIMENSIONS.verticalScreenshot.height,\n cookies,\n path: p,\n }),\n );\n }\n const horizontalScreenshots: string[] = [];\n for (const p of manifest.horizontalScreenshots) {\n horizontalScreenshots.push(\n await uploadOne(uploadImpl, {\n workspaceId,\n validWidth: DIMENSIONS.horizontalScreenshot.width,\n validHeight: DIMENSIONS.horizontalScreenshot.height,\n cookies,\n path: p,\n }),\n );\n }\n return { logo, logoDarkMode, horizontalThumbnail, verticalScreenshots, horizontalScreenshots };\n}\n\nasync function uploadOne(\n uploadImpl: (params: UploadParams) => Promise<string>,\n input: {\n workspaceId: number;\n validWidth: number;\n validHeight: number;\n cookies: readonly CdpCookie[];\n path: string;\n },\n): Promise<string> {\n const buffer = await readFile(input.path);\n return uploadImpl({\n workspaceId: input.workspaceId,\n validWidth: input.validWidth,\n validHeight: input.validHeight,\n cookies: input.cookies,\n file: {\n buffer,\n fileName: basename(input.path),\n contentType: 'image/png',\n },\n });\n}\n\n// Errors that touch `categoryIds` always reference that key explicitly in\n// the message (see `config/app-manifest.ts`). Point the user at\n// `aitcc app categories --selectable` so they don't have to hunt for a\n// live example — this is the only plain-text hint we surface beyond the\n// raw message, and we keep the JSON payload unchanged so the contract\n// with agent-plugin is stable.\nfunction categoryHintFor(err: ManifestError): string | null {\n const target = err.field ?? '';\n if (target === 'categoryIds' || /categoryIds/.test(err.message)) {\n return 'Tip: run `aitcc app categories --selectable` to list valid category ids.';\n }\n return null;\n}\n\nfunction emitManifestError(json: boolean, err: ManifestError): void {\n if (json) {\n if (err.kind === 'missing-required-field') {\n emitJson({\n ok: false,\n reason: 'missing-required-field',\n field: err.field ?? null,\n message: err.message,\n });\n } else {\n emitJson({ ok: false, reason: 'invalid-config', message: err.message });\n }\n } else {\n process.stderr.write(`${err.message}\\n`);\n const hint = categoryHintFor(err);\n if (hint) process.stderr.write(`${hint}\\n`);\n }\n}\n\nfunction emitImageDimensionError(json: boolean, err: ImageDimensionError): void {\n if (json) {\n if (err.reason === 'mismatch') {\n emitJson({\n ok: false,\n reason: 'image-dimension-mismatch',\n path: err.path,\n expected: err.expected,\n actual: err.actual ?? null,\n message: err.message,\n });\n } else {\n // 'unreadable' covers missing files, permission errors, and\n // decode failures — distinct from a genuine dimension mismatch\n // so agent-plugin can branch (e.g., \"path typo\" vs \"resize me\").\n emitJson({\n ok: false,\n reason: 'image-unreadable',\n path: err.path,\n message: err.message,\n });\n }\n } else {\n process.stderr.write(`${err.message}\\n`);\n }\n}\n\nfunction emitTermsNotAccepted(json: boolean): void {\n const message =\n 'The console requires several legal-agreement checkboxes before submitting a mini-app for review. ' +\n 'Re-run with --accept-terms to attest that you have read and agree to each of them ' +\n '(see VALIDATION-RULES.md or the console UI), or use --dry-run to preview the payload without submitting.';\n if (json) {\n emitJson({ ok: false, reason: 'terms-not-accepted', message });\n } else {\n process.stderr.write(`${message}\\n`);\n }\n}\n\nfunction emitDryRun(\n json: boolean,\n workspaceId: number,\n payload: ReturnType<typeof buildSubmitPayload>,\n): void {\n if (json) {\n emitJson({ ok: true, dryRun: true, workspaceId, payload });\n } else {\n process.stdout.write('[dry-run] Would POST to ');\n process.stdout.write(\n `https://apps-in-toss.toss.im/console/api-public/v3/appsintossconsole/workspaces/${workspaceId}/mini-app/review\\n`,\n );\n process.stdout.write(`${JSON.stringify(payload, null, 2)}\\n`);\n }\n}\n\nfunction consoleUrlFor(workspaceId: number, appId: string | number): string {\n return `https://apps-in-toss.toss.im/console/workspace/${workspaceId}/mini-app/${appId}`;\n}\n\nfunction emitSuccess(json: boolean, workspaceId: number, result: CreateMiniAppResult): void {\n const consoleUrl =\n result.miniAppId !== undefined ? consoleUrlFor(workspaceId, result.miniAppId) : null;\n if (json) {\n emitJson({\n ok: true,\n workspaceId,\n appId: result.miniAppId ?? null,\n reviewState: result.reviewState ?? null,\n consoleUrl,\n });\n } else {\n process.stdout.write(\n `Registered mini-app ${result.miniAppId ?? '(id unknown)'} in workspace ${workspaceId}` +\n ` (reviewState=${result.reviewState ?? 'unknown'}).\\n`,\n );\n if (consoleUrl !== null) {\n process.stdout.write(`🔗 console: ${consoleUrl}\\n`);\n }\n }\n}\n\n// Thin wrapper kept for local readability — the real dispatch lives in\n// `_shared.ts::emitFailureFromError` and is shared with app/keys/\n// members/workspace so every command's auth/network/api-error JSON\n// shape agrees.\nasync function emitFailureAndExit(json: boolean, err: unknown): Promise<void> {\n return emitFailureFromError(json, err);\n}\n\n// After a successful submit, persist the returned miniAppId back into\n// the resolved project-context file (`aitcc.yaml`/`aitcc.json`) so\n// follow-up commands like `app status` / `app deploy` resolve the same\n// app without an explicit `--app`. Skipped silently in --dry-run (we\n// never reach this code path) and when the response omitted the id.\n//\n// The write is best-effort: a failure here MUST NOT change the exit\n// code, since the submit itself already succeeded — the user can\n// recover by adding `miniAppId: <id>` manually. Errors are surfaced\n// only on stderr (suppressed under --json so the stdout contract\n// stays single-line). The \"no project file at all\" case prints a one-\n// line stderr hint pointing at the discoverability win.\nasync function persistMiniAppIdToProject(\n json: boolean,\n miniAppId: string | number | undefined,\n cwd: string,\n): Promise<void> {\n if (typeof miniAppId !== 'number' || !Number.isInteger(miniAppId) || miniAppId <= 0) {\n return;\n }\n let ctx: Awaited<ReturnType<typeof findProjectContext>>;\n try {\n ctx = await findProjectContext(cwd);\n } catch (err) {\n if (!json) {\n const detail = err instanceof Error ? err.message : String(err);\n process.stderr.write(\n `warning: could not read project context to persist miniAppId: ${detail}\\n`,\n );\n }\n return;\n }\n if (ctx === null) {\n if (!json) {\n process.stderr.write(\n `tip: drop an aitcc.yaml with \\`miniAppId: ${miniAppId}\\` in your project root to skip --app on later commands.\\n`,\n );\n }\n return;\n }\n try {\n const outcome = await writeProjectMiniAppId(ctx.source, miniAppId);\n if (!json && outcome.status === 'written') {\n process.stderr.write(`Updated ${ctx.source} with miniAppId: ${miniAppId}.\\n`);\n }\n } catch (err) {\n if (!json) {\n const detail = err instanceof Error ? err.message : String(err);\n process.stderr.write(`warning: could not persist miniAppId to ${ctx.source}: ${detail}\\n`);\n }\n }\n}\n","import { mkdir, writeFile } from 'node:fs/promises';\nimport { resolve as resolvePath } from 'node:path';\nimport { defineCommand } from 'citty';\nimport { fetchCerts, issueCert, revokeCert } from '../api/certs.js';\nimport {\n fetchAppEventCatalogs,\n fetchAppServiceStatus,\n fetchAppTemplates,\n fetchBundles,\n fetchBundleTestLinks,\n fetchConversionMetrics,\n fetchDeployedBundle,\n fetchImpressionCategoryList,\n fetchMiniAppRatings,\n fetchMiniApps,\n fetchMiniAppWithDraft,\n fetchReviewStatus,\n fetchShareRewards,\n fetchSmartMessageCampaigns,\n fetchUserReports,\n type MetricsTimeUnit,\n postBundleMemo,\n postBundleRelease,\n postBundleReview,\n postBundleReviewWithdrawal,\n postBundleTestPush,\n postDeploymentsComplete,\n postDeploymentsInitialize,\n putBundleToUploadUrl,\n type RatingSortDirection,\n type RatingSortField,\n TEMPLATE_CONTENT_REACH_TYPES,\n type TemplateContentReachType,\n} from '../api/mini-apps.js';\nimport { ExitCode } from '../exit.js';\nimport { exitAfterFlush } from '../flush.js';\nimport { readSession } from '../session.js';\nimport {\n emitFailureFromError,\n emitJson,\n emitNotAuthenticated,\n printContextHeader,\n requireMiniAppId,\n resolveAppOrFail,\n resolveWorkspaceContext,\n} from './_shared.js';\nimport { runDeploy } from './app-deploy.js';\nimport { runAppInit } from './app-init.js';\nimport { runRegister } from './register.js';\n\n// --json contract (consumed by agent-plugin):\n//\n// app ls [--workspace <id>]:\n// { ok: true, workspaceId, hasPolicyViolation,\n// apps: [{id, name, reviewState?, status, locked, lockReason, extra}] } exit 0\n// { ok: false, reason: 'no-workspace-selected' } exit 2\n// { ok: false, reason: 'invalid-id', message } exit 2\n//\n// `status` is derived (kebab-case enum: not-submitted | under-review | approved |\n// approved-with-edits | rejected | in-service | unknown). `locked` is\n// `approvalType === 'REVIEW'` (see docs/api/mini-apps.md \"REVIEW lock 권위\").\n// Auth/network/api failures follow the shared contract from workspace/whoami\n// (ok: true authenticated: false exit 10, network-error exit 11, api-error exit 17).\n\n// Best-effort match of review-status entries against mini-app summaries.\n// The list endpoint and the review-status endpoint key off the same id,\n// but we don't assume the field name is uniform — we compare by `.id` on\n// each record, falling back to `miniAppId` / `appId` (same order as the\n// list normaliser). Exported so the join semantics are unit-testable.\n// Returns `null` if no plausible match; callers render that as \"no review\n// status\" in the output rather than a failure.\nexport function findReviewEntry(\n reviewEntries: readonly Readonly<Record<string, unknown>>[],\n appId: string | number,\n): Readonly<Record<string, unknown>> | null {\n const target = String(appId);\n for (const entry of reviewEntries) {\n const candidate = entry.id ?? entry.miniAppId ?? entry.appId;\n if (candidate !== undefined && String(candidate) === target) return entry;\n }\n return null;\n}\n\nexport function reviewStateFor(\n entry: Readonly<Record<string, unknown>> | null,\n): string | undefined {\n if (!entry) return undefined;\n const raw = entry.reviewState ?? entry.status;\n return typeof raw === 'string' ? raw : undefined;\n}\n\nconst lsCommand = defineCommand({\n meta: {\n name: 'ls',\n description: 'List mini-apps in the selected workspace.',\n },\n args: {\n workspace: {\n type: 'string',\n description: 'Workspace ID. Defaults to the selected workspace (`aitcc workspace use`).',\n },\n json: { type: 'boolean', description: 'Emit machine-readable JSON to stdout.', default: false },\n },\n async run({ args }) {\n const ctx = await resolveWorkspaceContext(args);\n if (!ctx) return;\n const { session, workspaceId } = ctx;\n printContextHeader(ctx, { json: args.json });\n\n try {\n // List + review-status are independent read endpoints. Fire in parallel\n // so a slow endpoint doesn't serialise the wait. Review-status failures\n // currently propagate (rather than being downgraded to \"unknown\n // review\") because they almost always indicate a shared auth/network\n // problem — if that ever stops being true we can degrade gracefully.\n const [apps, review] = await Promise.all([\n fetchMiniApps(workspaceId, session.cookies),\n fetchReviewStatus(workspaceId, session.cookies),\n ]);\n\n // The workspace-level review-status entry only exposes serviceStatus +\n // a few flags — not the approvalType/current/draft trio that drives\n // the \"approved-with-edits\" vs \"under-review\" distinction. So per-app\n // /with-draft is the only authority. Fan out in parallel and degrade\n // a single failure to `unknown` so one stale id doesn't block the\n // rest of the list.\n const drafts = await Promise.all(\n apps.map(async (app) => {\n const numericId = typeof app.id === 'number' ? app.id : Number(app.id);\n if (!Number.isFinite(numericId)) return null;\n try {\n const env = await fetchMiniAppWithDraft(workspaceId, numericId, session.cookies);\n return deriveReviewState(env);\n } catch {\n return null;\n }\n }),\n );\n\n const rows = apps.map((app, i) => {\n const entry = findReviewEntry(review.miniApps, app.id);\n const derived = drafts[i] ?? null;\n const ls = deriveLsStatus(derived, serviceStatusFor(entry));\n const reviewState = reviewStateFor(entry);\n return { app, entry, derived, ls, reviewState };\n });\n\n if (args.json) {\n const joined = rows.map(({ app, ls, reviewState }) => ({\n id: app.id,\n name: app.name ?? null,\n ...(reviewState !== undefined ? { reviewState } : {}),\n status: ls.status,\n locked: ls.locked,\n lockReason: ls.lockReason,\n extra: app.extra,\n }));\n emitJson({\n ok: true,\n workspaceId,\n hasPolicyViolation: review.hasPolicyViolation,\n apps: joined,\n });\n return exitAfterFlush(ExitCode.Ok);\n }\n\n if (apps.length === 0) {\n process.stdout.write(`No apps in workspace ${workspaceId}.\\n`);\n if (review.hasPolicyViolation) {\n process.stderr.write('Note: workspace-wide policy violation flag is set.\\n');\n }\n return exitAfterFlush(ExitCode.Ok);\n }\n // NO_COLOR honoured per the project convention; isTTY gate keeps ANSI\n // escapes out of pipes (`update-check.ts` bails earlier on non-TTY for\n // the same reason).\n const useColor = process.stdout.isTTY && !process.env.NO_COLOR;\n const yellow = useColor ? '\\x1b[33m' : '';\n const reset = useColor ? '\\x1b[0m' : '';\n for (const { app, ls } of rows) {\n // Defensive: the upstream mini-app payload shape is not yet fully\n // observed (no registered apps in our workspaces). Tighten this\n // once sdk-example is registered and `name` is confirmed required.\n const name = app.name ?? '(unnamed)';\n const lockMark = ls.locked ? ` ${yellow}🔒${reset}` : '';\n process.stdout.write(`${app.id}\\t${name}\\t${ls.status}${lockMark}\\n`);\n }\n if (review.hasPolicyViolation) {\n process.stderr.write('Note: workspace-wide policy violation flag is set.\\n');\n }\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\n// --json contract (consumed by agent-plugin):\n//\n// app show <id> [--workspace <id>] [--view draft|current|merged]:\n// { ok: true, workspaceId, appId, view, miniApp: {...},\n// reviewState, locked, lockReason,\n// serviceStatus, shutdownCandidateStatus, scheduledShutdownAt } exit 0\n// { ok: false, reason: 'no-workspace-selected' } exit 2\n// { ok: false, reason: 'invalid-id', message } exit 2\n// { ok: false, reason: 'app-not-found', appId } exit 2\n//\n// app show <id> --diff:\n// { ok: true, workspaceId, appId, diffMode: true,\n// reviewState, locked, lockReason,\n// serviceStatus, shutdownCandidateStatus, scheduledShutdownAt,\n// diff: { hasDraft, hasCurrent, changed: [{field, draft, current}], unchangedCount } } exit 0\n//\n// `view` picks which part of the with-draft envelope to surface:\n// - `draft` (default) — the editor's latest state, populated as soon as\n// the app is created. This is what `app register` just wrote; it's the\n// only reliable view until the app is approved and published.\n// - `current` — the published/reviewed record end users see. Empty until\n// the app's first approval, so defaulting here would hide almost every\n// field we care about — hence the default is `draft`.\n// - `merged` — current with draft overlaid on top (draft wins per field).\n// Useful once both exist and the user wants the \"authoritative\" snapshot.\n//\n// The `--view` flag intentionally never falls back on its own. If the\n// caller asks for `current` on an unreviewed app they get `miniApp: null`\n// with `view: 'current'` so agent-plugin can tell the two cases apart.\n//\n// `reviewState`/`locked`/`lockReason` are derived from the envelope-level\n// `approvalType` + `current`/`draft`/`rejectedMessage` combo (same shape as\n// `app status`). `serviceStatus`/`shutdownCandidateStatus`/`scheduledShutdownAt`\n// come from the `/review-status` endpoint and may be `null` if that call\n// fails (we still return the review derivation since it's authoritative on\n// its own — see docs/api/mini-apps.md \"REVIEW lock 권위\").\nexport function pickMiniAppView(\n envelope: { current: Record<string, unknown> | null; draft: Record<string, unknown> | null },\n view: 'draft' | 'current' | 'merged',\n): Record<string, unknown> | null {\n const extract = (side: Record<string, unknown> | null): Record<string, unknown> | null => {\n if (side === null) return null;\n const ma = side.miniApp;\n if (ma !== null && typeof ma === 'object' && !Array.isArray(ma)) {\n return ma as Record<string, unknown>;\n }\n return null;\n };\n const draft = extract(envelope.draft);\n const current = extract(envelope.current);\n if (view === 'draft') return draft;\n if (view === 'current') return current;\n if (current !== null && draft !== null) return { ...current, ...draft };\n return draft ?? current;\n}\n\n// Whitelist + shallow comparison for `app show --diff`. Deep recursive\n// diffs over arbitrary console payloads aren't useful here — most fields\n// are server-controlled and noisy. We pick the top-level fields the user\n// actually edits via `app register`, plus two shallow `impression`\n// signals (keywordList, first categoryPath name).\n//\n// Each entry maps a stable diff field name (the key surfaced in JSON +\n// printed to stdout) to a getter that pulls the value out of the\n// `miniApp` view returned by `pickMiniAppView` (which already has\n// `impression` nested as a read-side convenience — see docs/api/mini-apps.md\n// \"Update-mode payload\" for why we don't strip it on read).\ntype DiffGetter = (m: Record<string, unknown>) => unknown;\nconst DIFF_FIELDS: ReadonlyArray<readonly [string, DiffGetter]> = [\n ['title', (m) => m.title],\n ['titleEn', (m) => m.titleEn],\n ['appName', (m) => m.appName],\n ['description', (m) => m.description],\n ['detailDescription', (m) => m.detailDescription],\n ['homePageUri', (m) => m.homePageUri],\n ['csEmail', (m) => m.csEmail],\n ['iconUri', (m) => m.iconUri],\n ['darkModeIconUri', (m) => m.darkModeIconUri],\n [\n 'impression.keywordList',\n (m) => {\n const imp = m.impression;\n if (imp === null || typeof imp !== 'object' || Array.isArray(imp)) return undefined;\n const kw = (imp as Record<string, unknown>).keywordList;\n return Array.isArray(kw) ? kw : undefined;\n },\n ],\n [\n 'impression.categoryPath',\n (m) => {\n // Just the first path's name parts joined \"group > category > subCategory\".\n // Single string keeps the diff readable; full path objects are noisy.\n const imp = m.impression;\n if (imp === null || typeof imp !== 'object' || Array.isArray(imp)) return undefined;\n const paths = (imp as Record<string, unknown>).categoryPaths;\n if (!Array.isArray(paths) || paths.length === 0) return undefined;\n const first = paths[0];\n if (first === null || typeof first !== 'object') return undefined;\n const fp = first as Record<string, unknown>;\n const parts: string[] = [];\n for (const key of ['group', 'category', 'subCategory']) {\n const node = fp[key];\n if (node !== null && typeof node === 'object') {\n const nm = (node as Record<string, unknown>).name;\n if (typeof nm === 'string') parts.push(nm);\n }\n }\n return parts.join(' > ');\n },\n ],\n];\n\nexport interface DiffEntry {\n readonly field: string;\n readonly draft: unknown;\n readonly current: unknown;\n}\n\nexport interface MiniAppDiff {\n readonly hasDraft: boolean;\n readonly hasCurrent: boolean;\n readonly changed: readonly DiffEntry[];\n readonly unchangedCount: number;\n}\n\n// Equality used by the diff: structural for arrays/objects, strict for\n// primitives. Arrays compare element-by-element with the same predicate;\n// objects key-by-key. Cycles aren't a concern — the with-draft response is\n// JSON, no self-references.\nfunction diffEquals(a: unknown, b: unknown): boolean {\n if (a === b) return true;\n if (a === undefined || b === undefined) return false;\n if (a === null || b === null) return false;\n if (Array.isArray(a) && Array.isArray(b)) {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (!diffEquals(a[i], b[i])) return false;\n }\n return true;\n }\n if (typeof a === 'object' && typeof b === 'object') {\n const ao = a as Record<string, unknown>;\n const bo = b as Record<string, unknown>;\n const ak = Object.keys(ao);\n const bk = Object.keys(bo);\n if (ak.length !== bk.length) return false;\n for (const k of ak) {\n if (!diffEquals(ao[k], bo[k])) return false;\n }\n return true;\n }\n return false;\n}\n\nexport function compareMiniAppViews(\n draft: Record<string, unknown> | null,\n current: Record<string, unknown> | null,\n): MiniAppDiff {\n const hasDraft = draft !== null;\n const hasCurrent = current !== null;\n if (!hasDraft || !hasCurrent) {\n return { hasDraft, hasCurrent, changed: [], unchangedCount: 0 };\n }\n const changed: DiffEntry[] = [];\n let unchangedCount = 0;\n for (const [field, getter] of DIFF_FIELDS) {\n // We pass the non-null branches so getters don't have to re-check.\n const d = getter(draft);\n const c = getter(current);\n if (d === undefined && c === undefined) continue;\n if (diffEquals(d, c)) {\n unchangedCount++;\n } else {\n changed.push({ field, draft: d, current: c });\n }\n }\n return { hasDraft, hasCurrent, changed, unchangedCount };\n}\n\n// Plain-mode rendering of a single diff value. Long strings collapse to\n// `<N> chars`; arrays show the count. Goal is \"scannable in a terminal\",\n// not \"round-trippable\" — the JSON path keeps the raw values for that.\nfunction formatDiffValue(v: unknown): string {\n if (v === null) return 'null';\n if (v === undefined) return '-';\n if (typeof v === 'string') {\n // Codepoint count (not UTF-16 length) on both the threshold and the\n // label — otherwise a string with surrogate-pair emoji could trip the\n // threshold but report a count that looks too small.\n const cp = [...v].length;\n if (cp > 60) return `${cp} chars`;\n return JSON.stringify(v);\n }\n if (Array.isArray(v)) {\n return `[${v.length} items]`;\n }\n if (typeof v === 'object') {\n return '<object>';\n }\n return String(v);\n}\n\n// Service-status string → human label. Unknown values pass through verbatim\n// (we'd rather show a raw enum we don't recognise than swallow it).\nfunction describeServiceStatus(s: string): string {\n if (s === 'PREPARE') return 'PREPARE (출시 전 — release 토글 미사용)';\n if (s === 'RUNNING') return 'RUNNING (출시 중)';\n if (s === 'STOPPED') return 'STOPPED (운영 중지)';\n return s;\n}\n\nconst showCommand = defineCommand({\n meta: {\n name: 'show',\n description:\n 'Show full details of a mini-app, including fields only visible in the draft view.',\n },\n args: {\n id: {\n type: 'positional',\n description:\n 'Mini-app ID (the numeric `appId` from `app ls` or `app register`). Optional when `aitcc.yaml` provides `miniAppId`.',\n required: false,\n },\n workspace: {\n type: 'string',\n description: 'Workspace ID. Defaults to the selected workspace.',\n },\n view: {\n type: 'string',\n description: 'Which view to render: `draft` (default), `current`, or `merged`.',\n default: 'draft',\n },\n diff: {\n type: 'boolean',\n description:\n 'Compare draft vs current view across the fields users edit (title, description, ' +\n 'iconUri, keywords, category, …). Shallow whitelist — overrides --view.',\n default: false,\n },\n json: { type: 'boolean', description: 'Emit machine-readable JSON to stdout.', default: false },\n },\n async run({ args }) {\n const view = args.view;\n if (view !== 'draft' && view !== 'current' && view !== 'merged') {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'invalid-config',\n field: 'view',\n message: `--view must be one of draft|current|merged (got ${JSON.stringify(view)})`,\n });\n } else {\n process.stderr.write(`app show: invalid --view ${JSON.stringify(view)}\\n`);\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n\n // `--diff` is a different mode of `app show`, not a view selector. If\n // the user passes both we don't hard-fail (it's harmless and keeps\n // shell aliases like `aitcc app show $@ --diff` working) — just warn\n // on stderr and let --diff win. Suppressed under `--json` so machine\n // consumers don't see warning chatter; `diffMode: true` in the JSON\n // payload tells them which mode actually ran.\n if (args.diff && args.view !== 'draft' && !args.json) {\n process.stderr.write(\n `app show: --diff overrides --view ${JSON.stringify(args.view)} (ignored)\\n`,\n );\n }\n\n const ctx = await resolveAppOrFail({\n json: args.json,\n appIdRaw: args.id,\n ...(args.workspace !== undefined ? { workspace: args.workspace } : {}),\n });\n if (!ctx) return;\n const appId = await requireMiniAppId(ctx, args.json);\n if (appId === null) return;\n printContextHeader(ctx, { json: args.json });\n const { session, workspaceId } = ctx;\n\n try {\n // Fire both requests in parallel — the service-status endpoint is\n // best-effort: if it fails we still want the review derivation,\n // which is authoritative on its own (see docs/api/mini-apps.md\n // \"REVIEW lock 권위\").\n const [envelope, service] = await Promise.all([\n fetchMiniAppWithDraft(workspaceId, appId, session.cookies),\n fetchAppServiceStatus(workspaceId, appId, session.cookies).catch(() => null),\n ]);\n const derived = deriveReviewState(envelope);\n\n if (args.diff) {\n const draftMini = pickMiniAppView(envelope, 'draft');\n const currentMini = pickMiniAppView(envelope, 'current');\n const diff = compareMiniAppViews(draftMini, currentMini);\n\n if (args.json) {\n emitJson({\n ok: true,\n workspaceId,\n appId,\n diffMode: true,\n reviewState: derived.state,\n locked: derived.locked,\n lockReason: derived.lockReason,\n serviceStatus: service?.serviceStatus ?? null,\n shutdownCandidateStatus: service?.shutdownCandidateStatus ?? null,\n scheduledShutdownAt: service?.scheduledShutdownAt ?? null,\n diff,\n });\n return exitAfterFlush(ExitCode.Ok);\n }\n\n process.stdout.write(`# App ${appId} — diff (draft → current)\\n\\n`);\n process.stdout.write(\n `Review state ${derived.state}` +\n (derived.locked ? ' ⚠️ update locked (운영팀 검수 큐 처리 대기)' : '') +\n '\\n',\n );\n if (service !== null) {\n process.stdout.write(`Service ${describeServiceStatus(service.serviceStatus)}\\n`);\n }\n process.stdout.write('\\n');\n\n // Both null is unreachable in practice — `app show` would have\n // 4xx-failed before reaching this branch — but we keep the guard\n // so the message ladder reads top-to-bottom rather than relying\n // on the next two branches to cover the both-null shape.\n if (!diff.hasDraft && !diff.hasCurrent) {\n process.stdout.write('App has neither a draft nor a current view yet.\\n');\n return exitAfterFlush(ExitCode.Ok);\n }\n if (!diff.hasCurrent) {\n process.stdout.write('no current view yet — draft is the only state\\n');\n return exitAfterFlush(ExitCode.Ok);\n }\n if (!diff.hasDraft) {\n process.stdout.write('no draft pending — current is the only state\\n');\n return exitAfterFlush(ExitCode.Ok);\n }\n if (diff.changed.length === 0) {\n process.stdout.write('No changes between draft and current.\\n');\n return exitAfterFlush(ExitCode.Ok);\n }\n\n // Align field column to the widest changed name for readability.\n // Cap at 24 so a stray long field name doesn't push the values off\n // the right edge of an 80-col terminal.\n const fieldWidth = Math.min(24, Math.max(...diff.changed.map((c) => c.field.length)) + 2);\n process.stdout.write('Changed:\\n');\n for (const entry of diff.changed) {\n const name = entry.field.padEnd(fieldWidth);\n process.stdout.write(\n ` ${name} ${formatDiffValue(entry.draft)} → ${formatDiffValue(entry.current)}\\n`,\n );\n }\n if (diff.unchangedCount > 0) {\n process.stdout.write(`\\nUnchanged: ${diff.unchangedCount} fields\\n`);\n }\n return exitAfterFlush(ExitCode.Ok);\n }\n\n const miniApp = pickMiniAppView(envelope, view);\n\n // Emit a one-line stderr hint when `--view current` comes back\n // empty but a draft exists — this is the most common confusion\n // (unreviewed apps have a populated draft and an empty current).\n // stderr so both JSON and plain callers see it without the JSON\n // shape changing.\n if (miniApp === null && view === 'current' && envelope.draft !== null) {\n process.stderr.write(\n `App ${appId} has no \\`current\\` view yet (not reviewed). Re-run with \\`--view draft\\` to see the pending record.\\n`,\n );\n }\n\n if (args.json) {\n emitJson({\n ok: true,\n workspaceId,\n appId,\n view,\n miniApp,\n reviewState: derived.state,\n locked: derived.locked,\n lockReason: derived.lockReason,\n serviceStatus: service?.serviceStatus ?? null,\n shutdownCandidateStatus: service?.shutdownCandidateStatus ?? null,\n scheduledShutdownAt: service?.scheduledShutdownAt ?? null,\n });\n return exitAfterFlush(ExitCode.Ok);\n }\n\n if (miniApp === null) {\n // Plain-text path keeps the stdout summary (the stderr hint is\n // already out of the way). Avoid duplicating it here.\n if (view === 'current' && envelope.draft !== null) {\n process.stdout.write(`App ${appId} has no \\`current\\` view yet.\\n`);\n } else {\n process.stdout.write(`App ${appId} has no data for view=${view}.\\n`);\n }\n return exitAfterFlush(ExitCode.Ok);\n }\n\n const pick = (k: string): string => {\n const v = miniApp[k];\n return v === null || v === undefined ? '-' : String(v);\n };\n const images = Array.isArray(miniApp.images) ? miniApp.images : [];\n const impression =\n miniApp.impression !== null && typeof miniApp.impression === 'object'\n ? (miniApp.impression as Record<string, unknown>)\n : {};\n const keywords = Array.isArray(impression.keywordList) ? impression.keywordList : [];\n const categoryPaths = Array.isArray(impression.categoryPaths) ? impression.categoryPaths : [];\n\n process.stdout.write(`# App ${appId} (view=${view})\\n\\n`);\n process.stdout.write(`Name (ko) ${pick('title')}\\n`);\n process.stdout.write(`Name (en) ${pick('titleEn')}\\n`);\n process.stdout.write(`App slug ${pick('appName')}\\n`);\n process.stdout.write(`Status ${pick('status')}\\n`);\n // `Review state` and `Service` are envelope-level — view-orthogonal —\n // so they sit right after the raw `Status` line regardless of which\n // view was selected. `Service` is omitted silently when the\n // `/review-status` call failed; the JSON path still emits\n // `serviceStatus: null` for callers that care.\n process.stdout.write(\n `Review state ${derived.state}` +\n (derived.locked ? ' ⚠️ update locked (운영팀 검수 큐 처리 대기)' : '') +\n '\\n',\n );\n if (service !== null) {\n process.stdout.write(`Service ${describeServiceStatus(service.serviceStatus)}\\n`);\n }\n process.stdout.write(`Home page ${pick('homePageUri')}\\n`);\n process.stdout.write(`CS email ${pick('csEmail')}\\n`);\n process.stdout.write(`Logo ${pick('iconUri')}\\n`);\n process.stdout.write(`Subtitle ${pick('description')}\\n`);\n const detail =\n typeof miniApp.detailDescription === 'string'\n ? `${[...miniApp.detailDescription].length} chars`\n : '-';\n process.stdout.write(`Detail desc ${detail}\\n`);\n process.stdout.write(`Images ${images.length}\\n`);\n process.stdout.write(`Keywords ${keywords.length} (${keywords.join(', ')})\\n`);\n const firstPath = categoryPaths[0];\n if (firstPath && typeof firstPath === 'object') {\n const fp = firstPath as Record<string, unknown>;\n const parts: string[] = [];\n for (const key of ['group', 'category', 'subCategory']) {\n const node = fp[key];\n if (node !== null && typeof node === 'object') {\n const nm = (node as Record<string, unknown>).name;\n if (typeof nm === 'string') parts.push(nm);\n }\n }\n process.stdout.write(`Category ${parts.join(' > ') || '-'}\\n`);\n } else {\n process.stdout.write(`Category -\\n`);\n }\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\n// Derived review state. The console UI's \"검토 중\" banner is not a single\n// API field — it's composed from the /with-draft envelope. We surface the\n// derivation so `aitcc app status` is the one place the rule lives.\n//\n// Observed combinations (2026-04-23 on workspace 3095, apps 29349/29356/\n// 29397/29405 all under review):\n//\n// approvalType=REVIEW current=null rejectedMessage=null → under-review\n// approvalType=REVIEW current=null rejectedMessage=STR → rejected\n// approvalType=REVIEW current=ROW (draft is edits-in-flight) → approved (with pending edits)\n// approvalType=REVIEW current=ROW draft=null or equal → approved\n// approvalType=null → not-submitted (edit draft only)\n//\n// Unknown combinations fall through to `unknown` so callers can log and\n// we can extend the ladder as new signals come in.\nexport type ReviewState =\n | 'not-submitted'\n | 'under-review'\n | 'rejected'\n | 'approved'\n | 'approved-with-edits'\n | 'unknown';\n\n// `locked` is the operational signal for \"can I update this app right now?\".\n// It is authoritative — derived `state` (e.g. `approved-with-edits`) is not.\n// 2026-05-02 dog-food showed apps in different derived states all rejecting\n// updates with `errorCode: 4046` because the envelope-level `approvalType`\n// was `REVIEW`. Tracked in docs/api/mini-apps.md \"REVIEW lock 권위\".\nexport type LockReason = 'review-pending';\n\nexport interface DerivedStatus {\n readonly state: ReviewState;\n readonly approvalType: string | null;\n readonly rejectedMessage: string | null;\n readonly hasCurrent: boolean;\n readonly hasDraft: boolean;\n readonly locked: boolean;\n readonly lockReason: LockReason | null;\n}\n\nexport function deriveReviewState(env: {\n current: Record<string, unknown> | null;\n draft: Record<string, unknown> | null;\n approvalType: string | null;\n rejectedMessage: string | null;\n}): DerivedStatus {\n const hasCurrent = env.current !== null;\n const hasDraft = env.draft !== null;\n const approvalType = env.approvalType;\n const rejectedMessage = env.rejectedMessage;\n\n let state: ReviewState;\n if (approvalType === null) {\n state = 'not-submitted';\n } else if (rejectedMessage !== null) {\n state = 'rejected';\n } else if (!hasCurrent) {\n state = 'under-review';\n } else if (hasDraft) {\n state = 'approved-with-edits';\n } else {\n state = 'approved';\n }\n // approvalType values other than REVIEW (we haven't observed any yet)\n // or unexpected combinations get flagged as unknown rather than misreported.\n if (approvalType !== null && approvalType !== 'REVIEW' && state === 'under-review') {\n state = 'unknown';\n }\n const locked = approvalType === 'REVIEW';\n const lockReason: LockReason | null = locked ? 'review-pending' : null;\n return { state, approvalType, rejectedMessage, hasCurrent, hasDraft, locked, lockReason };\n}\n\n// Status column for `app ls`. Composes the with-draft-derived `ReviewState`\n// with the workspace review-status entry's `serviceStatus` so a published,\n// running app shows `in-service` instead of the bare `approved`. lock is\n// authoritative on `approvalType === 'REVIEW'` (see docs/api/mini-apps.md\n// \"REVIEW lock 권위\") — derived ladder labels like `approved-with-edits`\n// imply lock but are not the gate themselves.\nexport type AppLsStatus =\n | 'not-submitted'\n | 'under-review'\n | 'approved'\n | 'approved-with-edits'\n | 'rejected'\n | 'in-service'\n | 'unknown';\n\nexport interface AppLsStatusRow {\n readonly status: AppLsStatus;\n readonly locked: boolean;\n readonly lockReason: 'review-pending' | null;\n}\n\nexport function deriveLsStatus(\n derived: DerivedStatus | null,\n serviceStatus: string | undefined,\n): AppLsStatusRow {\n if (!derived) {\n return { status: 'unknown', locked: false, lockReason: null };\n }\n const locked = derived.approvalType === 'REVIEW';\n const lockReason = locked ? 'review-pending' : null;\n let status: AppLsStatus = derived.state;\n if (status === 'approved' && serviceStatus === 'RUNNING') {\n status = 'in-service';\n }\n return { status, locked, lockReason };\n}\n\nexport function serviceStatusFor(\n entry: Readonly<Record<string, unknown>> | null,\n): string | undefined {\n if (!entry) return undefined;\n const raw = entry.serviceStatus;\n return typeof raw === 'string' ? raw : undefined;\n}\n\nconst POLL_MIN_INTERVAL_SEC = 30;\nconst POLL_MAX_INTERVAL_SEC = 3600;\n\nconst statusCommand = defineCommand({\n meta: {\n name: 'status',\n description:\n 'Show the derived review state of a mini-app (under-review / rejected / approved).',\n },\n args: {\n id: {\n type: 'positional',\n description: 'Mini-app ID. Optional when `aitcc.yaml` provides `miniAppId`.',\n required: false,\n },\n workspace: {\n type: 'string',\n description: 'Workspace ID. Defaults to the selected workspace.',\n },\n watch: {\n type: 'boolean',\n description:\n 'Poll until the review state flips off `under-review` (rejected or approved). ' +\n 'Combine with `--interval <seconds>`.',\n default: false,\n },\n interval: {\n type: 'string',\n description: 'Polling interval in seconds when --watch is set. Clamped to [30, 3600].',\n default: '60',\n },\n json: { type: 'boolean', description: 'Emit machine-readable JSON.', default: false },\n },\n async run({ args }) {\n const intervalRaw = Number(args.interval);\n if (!Number.isFinite(intervalRaw) || intervalRaw <= 0) {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'invalid-config',\n field: 'interval',\n message: `--interval must be a positive number (got ${JSON.stringify(args.interval)})`,\n });\n } else {\n process.stderr.write(`app status: invalid --interval ${JSON.stringify(args.interval)}\\n`);\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n const intervalSec = Math.max(\n POLL_MIN_INTERVAL_SEC,\n Math.min(POLL_MAX_INTERVAL_SEC, Math.floor(intervalRaw)),\n );\n\n const ctx = await resolveAppOrFail({\n json: args.json,\n appIdRaw: args.id,\n ...(args.workspace !== undefined ? { workspace: args.workspace } : {}),\n });\n if (!ctx) return;\n const appId = await requireMiniAppId(ctx, args.json);\n if (appId === null) return;\n printContextHeader(ctx, { json: args.json });\n const { session, workspaceId } = ctx;\n\n // `serviceStatus` is a server-side string (PREPARE / RUNNING / …) that\n // is orthogonal to the client-derived review `state`. We surface both\n // so operators see the review stage and the runtime stage at once —\n // important because an app can be `approved` (review done) yet still\n // `PREPARE` (not live), and we never want the --json consumer to have\n // to make a second call to tell them apart.\n const emit = (\n status: DerivedStatus,\n service: {\n serviceStatus: string;\n shutdownCandidateStatus: string | null;\n scheduledShutdownAt: string | null;\n } | null,\n ) => {\n if (args.json) {\n emitJson({\n ok: true,\n workspaceId,\n appId,\n ...status,\n // `null` only in the unlikely case the service-status endpoint\n // failed but with-draft succeeded — we fall through rather than\n // hard-failing, because the derived review state is still useful.\n serviceStatus: service?.serviceStatus ?? null,\n shutdownCandidateStatus: service?.shutdownCandidateStatus ?? null,\n scheduledShutdownAt: service?.scheduledShutdownAt ?? null,\n });\n } else {\n const svc = service ? ` [${service.serviceStatus}]` : '';\n process.stdout.write(\n `App ${appId} (ws ${workspaceId}): ${status.state}${svc}` +\n (status.locked ? '\\n ⚠️ update locked (운영팀 검수 큐 처리 대기)' : '') +\n (status.rejectedMessage ? `\\n reason: ${status.rejectedMessage}` : '') +\n (service?.scheduledShutdownAt\n ? `\\n scheduled shutdown: ${service.scheduledShutdownAt}`\n : '') +\n '\\n',\n );\n }\n };\n\n try {\n const once = async (): Promise<\n [\n DerivedStatus,\n {\n serviceStatus: string;\n shutdownCandidateStatus: string | null;\n scheduledShutdownAt: string | null;\n } | null,\n ]\n > => {\n // Fire both requests in parallel — they share the same session\n // cookie and the console backend has no cross-rate-limit we've\n // observed. `service-status` is best-effort: if it fails we still\n // want the review state through.\n const [env, service] = await Promise.all([\n fetchMiniAppWithDraft(workspaceId, appId, session.cookies),\n fetchAppServiceStatus(workspaceId, appId, session.cookies).catch(() => null),\n ]);\n return [deriveReviewState(env), service];\n };\n\n if (!args.watch) {\n const [status, service] = await once();\n emit(status, service);\n return exitAfterFlush(ExitCode.Ok);\n }\n\n // --watch: poll with clear line-per-tick JSON emission. Each JSON line\n // is a self-contained object, NDJSON-style, so agents/shells can pipe\n // it into `jq -c` without waiting for a terminal. Stop when the state\n // is no longer `under-review` (reviewed) or when the process is\n // interrupted — we don't synthesise a \"watch-ended\" record.\n // Human mode prints a one-line update only when the state changes.\n let lastState: ReviewState | null = null;\n let lastServiceStatus: string | null = null;\n while (true) {\n const [status, service] = await once();\n const svc = service?.serviceStatus ?? null;\n if (args.json) {\n emit(status, service);\n } else if (status.state !== lastState || svc !== lastServiceStatus) {\n emit(status, service);\n }\n lastState = status.state;\n lastServiceStatus = svc;\n if (status.state !== 'under-review') {\n return exitAfterFlush(ExitCode.Ok);\n }\n await new Promise((resolve) => setTimeout(resolve, intervalSec * 1000));\n }\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\n// --json contract (consumed by agent-plugin):\n//\n// app ratings <id> [--workspace <id>] [--page N] [--size N]\n// [--sort-field CREATED_AT|SCORE] [--sort-direction ASC|DESC]:\n// { ok: true, workspaceId, appId, page, size, paging, averageRating,\n// totalReviewCount, ratings: [...] } exit 0\n// { ok: false, reason: 'no-workspace-selected' } exit 2\n// { ok: false, reason: 'invalid-id', message } exit 2\n// { ok: false, reason: 'invalid-config', field, message } exit 2\n//\n// The `sortField` values reflect what the console UI emits; the server\n// accepts them exactly. We don't enumerate more values because no other\n// orderings are observed — if the server supports them, add them once\n// they appear in a real capture.\n\nconst VALID_SORT_FIELDS: readonly RatingSortField[] = ['CREATED_AT', 'SCORE'];\nconst VALID_SORT_DIRECTIONS: readonly RatingSortDirection[] = ['ASC', 'DESC'];\n\nfunction parseNonNegativeInt(raw: string, field: string): { value: number } | { error: string } {\n const n = Number(raw);\n if (!Number.isFinite(n) || !Number.isInteger(n) || n < 0) {\n return { error: `--${field} must be a non-negative integer (got ${JSON.stringify(raw)})` };\n }\n return { value: n };\n}\n\nconst ratingsCommand = defineCommand({\n meta: {\n name: 'ratings',\n description: 'List user ratings and reviews left for a mini-app.',\n },\n args: {\n id: {\n type: 'positional',\n description: 'Mini-app ID. Optional when `aitcc.yaml` provides `miniAppId`.',\n required: false,\n },\n workspace: {\n type: 'string',\n description: 'Workspace ID. Defaults to the selected workspace.',\n },\n page: { type: 'string', description: 'Page number (0-indexed).', default: '0' },\n size: { type: 'string', description: 'Page size.', default: '20' },\n 'sort-field': {\n type: 'string',\n description: 'Sort field: CREATED_AT (default) or SCORE.',\n default: 'CREATED_AT',\n },\n 'sort-direction': {\n type: 'string',\n description: 'Sort direction: ASC or DESC (default).',\n default: 'DESC',\n },\n json: { type: 'boolean', description: 'Emit machine-readable JSON.', default: false },\n },\n async run({ args }) {\n const pageResult = parseNonNegativeInt(args.page, 'page');\n if ('error' in pageResult) {\n if (args.json) {\n emitJson({ ok: false, reason: 'invalid-config', field: 'page', message: pageResult.error });\n } else {\n process.stderr.write(`app ratings: ${pageResult.error}\\n`);\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n const sizeResult = parseNonNegativeInt(args.size, 'size');\n if ('error' in sizeResult) {\n if (args.json) {\n emitJson({ ok: false, reason: 'invalid-config', field: 'size', message: sizeResult.error });\n } else {\n process.stderr.write(`app ratings: ${sizeResult.error}\\n`);\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n if (sizeResult.value === 0) {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'invalid-config',\n field: 'size',\n message: '--size must be at least 1',\n });\n } else {\n process.stderr.write('app ratings: --size must be at least 1\\n');\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n const sortField = args['sort-field'];\n if (!VALID_SORT_FIELDS.includes(sortField as RatingSortField)) {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'invalid-config',\n field: 'sort-field',\n message: `--sort-field must be one of ${VALID_SORT_FIELDS.join('|')} (got ${JSON.stringify(sortField)})`,\n });\n } else {\n process.stderr.write(`app ratings: invalid --sort-field ${JSON.stringify(sortField)}\\n`);\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n const sortDirection = args['sort-direction'];\n if (!VALID_SORT_DIRECTIONS.includes(sortDirection as RatingSortDirection)) {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'invalid-config',\n field: 'sort-direction',\n message: `--sort-direction must be one of ${VALID_SORT_DIRECTIONS.join('|')} (got ${JSON.stringify(sortDirection)})`,\n });\n } else {\n process.stderr.write(\n `app ratings: invalid --sort-direction ${JSON.stringify(sortDirection)}\\n`,\n );\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n\n const ctx = await resolveAppOrFail({\n json: args.json,\n appIdRaw: args.id,\n ...(args.workspace !== undefined ? { workspace: args.workspace } : {}),\n });\n if (!ctx) return;\n const appId = await requireMiniAppId(ctx, args.json);\n if (appId === null) return;\n printContextHeader(ctx, { json: args.json });\n const { session, workspaceId } = ctx;\n\n try {\n const result = await fetchMiniAppRatings(\n {\n workspaceId,\n miniAppId: appId,\n page: pageResult.value,\n size: sizeResult.value,\n sortField: sortField as RatingSortField,\n sortDirection: sortDirection as RatingSortDirection,\n },\n session.cookies,\n );\n\n if (args.json) {\n emitJson({\n ok: true,\n workspaceId,\n appId,\n page: pageResult.value,\n size: sizeResult.value,\n sortField,\n sortDirection,\n averageRating: result.averageRating,\n totalReviewCount: result.totalReviewCount,\n paging: result.paging,\n ratings: result.ratings,\n });\n return exitAfterFlush(ExitCode.Ok);\n }\n\n process.stdout.write(\n `App ${appId} (ws ${workspaceId}): ${result.totalReviewCount} review(s), avg ${result.averageRating.toFixed(2)}\\n`,\n );\n if (result.ratings.length === 0) {\n process.stdout.write('No ratings on this page.\\n');\n return exitAfterFlush(ExitCode.Ok);\n }\n for (const r of result.ratings) {\n const score = typeof r.score === 'number' ? r.score : (r.rating ?? '-');\n const author =\n typeof r.nickname === 'string'\n ? r.nickname\n : typeof r.userName === 'string'\n ? r.userName\n : '(anon)';\n const text =\n typeof r.content === 'string'\n ? r.content\n : typeof r.reviewContent === 'string'\n ? r.reviewContent\n : '';\n const createdAt =\n typeof r.createdAt === 'string'\n ? r.createdAt\n : typeof r.reviewedAt === 'string'\n ? r.reviewedAt\n : '';\n process.stdout.write(`${score}\\t${createdAt}\\t${author}\\t${text}\\n`);\n }\n if (result.paging.hasNext) {\n process.stdout.write(\n `(more: --page ${pageResult.value + 1} for next ${sizeResult.value})\\n`,\n );\n }\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\n// --json contract (consumed by agent-plugin):\n//\n// app reports <id> [--workspace <id>] [--page-size N] [--cursor <str>]:\n// { ok: true, workspaceId, appId, pageSize, cursor, nextCursor,\n// hasMore, reports: [...] } exit 0\n// { ok: false, reason: 'invalid-id' | 'invalid-config', ... } exit 2\n//\n// The endpoint is `/workspaces/:wid/mini-apps/:aid/user-reports` —\n// note the **plural** `mini-apps` (same split-personality as review-status).\n// Pagination is cursor-based: the server hands back `nextCursor` + `hasMore`,\n// we pass `--cursor` as an opaque string next call.\n\nconst reportsCommand = defineCommand({\n meta: {\n name: 'reports',\n description: 'List user-submitted reports (신고 내역) for a mini-app.',\n },\n args: {\n id: {\n type: 'positional',\n description: 'Mini-app ID. Optional when `aitcc.yaml` provides `miniAppId`.',\n required: false,\n },\n workspace: {\n type: 'string',\n description: 'Workspace ID. Defaults to the selected workspace.',\n },\n 'page-size': { type: 'string', description: 'Page size (default 20).', default: '20' },\n cursor: {\n type: 'string',\n description: 'Opaque cursor from a previous response `nextCursor`.',\n },\n json: { type: 'boolean', description: 'Emit machine-readable JSON.', default: false },\n },\n async run({ args }) {\n const pageSizeResult = parseNonNegativeInt(args['page-size'], 'page-size');\n if ('error' in pageSizeResult) {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'invalid-config',\n field: 'page-size',\n message: pageSizeResult.error,\n });\n } else {\n process.stderr.write(`app reports: ${pageSizeResult.error}\\n`);\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n if (pageSizeResult.value === 0) {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'invalid-config',\n field: 'page-size',\n message: '--page-size must be at least 1',\n });\n } else {\n process.stderr.write('app reports: --page-size must be at least 1\\n');\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n\n const ctx = await resolveAppOrFail({\n json: args.json,\n appIdRaw: args.id,\n ...(args.workspace !== undefined ? { workspace: args.workspace } : {}),\n });\n if (!ctx) return;\n const appId = await requireMiniAppId(ctx, args.json);\n if (appId === null) return;\n printContextHeader(ctx, { json: args.json });\n const { session, workspaceId } = ctx;\n\n try {\n const result = await fetchUserReports(\n {\n workspaceId,\n miniAppId: appId,\n pageSize: pageSizeResult.value,\n ...(typeof args.cursor === 'string' && args.cursor.length > 0\n ? { cursor: args.cursor }\n : {}),\n },\n session.cookies,\n );\n\n if (args.json) {\n emitJson({\n ok: true,\n workspaceId,\n appId,\n pageSize: pageSizeResult.value,\n cursor: args.cursor ?? null,\n nextCursor: result.nextCursor,\n hasMore: result.hasMore,\n reports: result.reports,\n });\n return exitAfterFlush(ExitCode.Ok);\n }\n\n process.stdout.write(\n `App ${appId} (ws ${workspaceId}): ${result.reports.length} report(s) on this page\\n`,\n );\n if (result.reports.length === 0) {\n process.stdout.write('No reports.\\n');\n return exitAfterFlush(ExitCode.Ok);\n }\n for (const r of result.reports) {\n const id = typeof r.id === 'string' || typeof r.id === 'number' ? r.id : '-';\n const reason = typeof r.reason === 'string' ? r.reason : (r.reportType ?? '-');\n const text =\n typeof r.content === 'string' ? r.content : typeof r.detail === 'string' ? r.detail : '';\n const createdAt =\n typeof r.createdAt === 'string'\n ? r.createdAt\n : typeof r.reportedAt === 'string'\n ? r.reportedAt\n : '';\n process.stdout.write(`${id}\\t${createdAt}\\t${reason}\\t${text}\\n`);\n }\n if (result.hasMore && result.nextCursor) {\n process.stdout.write(`(more: --cursor ${result.nextCursor})\\n`);\n }\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\n// --json contract (consumed by agent-plugin):\n//\n// app bundles ls <id> [--workspace <id>] [--page N]\n// [--tested true|false] [--deploy-status STR]:\n// { ok: true, workspaceId, appId, page, totalPage, currentPage,\n// bundles: [...] } exit 0\n// { ok: false, reason: 'invalid-id' | 'invalid-config' ... } exit 2\n//\n// app bundles deployed <id> [--workspace <id>]:\n// { ok: true, workspaceId, appId, bundle: {...} | null } exit 0\n// { ok: false, reason: 'invalid-id' ... } exit 2\n//\n// Bundles are the artefact that `aitcc deploy` will eventually upload\n// (task #24). Listing them now lets agent-plugin and humans see the\n// deploy surface even before we can write new ones.\n\nconst bundlesLsCommand = defineCommand({\n meta: {\n name: 'ls',\n description: 'List upload bundles for a mini-app (page-based pagination).',\n },\n args: {\n id: {\n type: 'positional',\n description: 'Mini-app ID. Optional when `aitcc.yaml` provides `miniAppId`.',\n required: false,\n },\n workspace: {\n type: 'string',\n description: 'Workspace ID. Defaults to the selected workspace.',\n },\n page: { type: 'string', description: 'Page number (0-indexed).', default: '0' },\n tested: {\n type: 'string',\n description: 'Filter: `true` to show only tested bundles (or `false`).',\n },\n 'deploy-status': {\n type: 'string',\n description: 'Filter by deploy status (e.g. DEPLOYED).',\n },\n json: { type: 'boolean', description: 'Emit machine-readable JSON.', default: false },\n },\n async run({ args }) {\n const pageResult = parseNonNegativeInt(args.page, 'page');\n if ('error' in pageResult) {\n if (args.json) {\n emitJson({ ok: false, reason: 'invalid-config', field: 'page', message: pageResult.error });\n } else {\n process.stderr.write(`app bundles ls: ${pageResult.error}\\n`);\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n let tested: boolean | undefined;\n if (args.tested !== undefined) {\n if (args.tested === 'true') tested = true;\n else if (args.tested === 'false') tested = false;\n else {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'invalid-config',\n field: 'tested',\n message: `--tested must be \"true\" or \"false\" (got ${JSON.stringify(args.tested)})`,\n });\n } else {\n process.stderr.write(`app bundles ls: invalid --tested ${JSON.stringify(args.tested)}\\n`);\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n }\n\n const ctx = await resolveAppOrFail({\n json: args.json,\n appIdRaw: args.id,\n ...(args.workspace !== undefined ? { workspace: args.workspace } : {}),\n });\n if (!ctx) return;\n const appId = await requireMiniAppId(ctx, args.json);\n if (appId === null) return;\n printContextHeader(ctx, { json: args.json });\n const { session, workspaceId } = ctx;\n\n try {\n const result = await fetchBundles(\n {\n workspaceId,\n miniAppId: appId,\n page: pageResult.value,\n ...(tested !== undefined ? { tested } : {}),\n ...(typeof args['deploy-status'] === 'string' && args['deploy-status'].length > 0\n ? { deployStatus: args['deploy-status'] }\n : {}),\n },\n session.cookies,\n );\n\n if (args.json) {\n emitJson({\n ok: true,\n workspaceId,\n appId,\n page: pageResult.value,\n totalPage: result.totalPage,\n currentPage: result.currentPage,\n bundles: result.contents,\n });\n return exitAfterFlush(ExitCode.Ok);\n }\n\n process.stdout.write(\n `App ${appId} (ws ${workspaceId}): page ${result.currentPage + 1}/${Math.max(result.totalPage, 1)}, ${result.contents.length} bundle(s)\\n`,\n );\n if (result.contents.length === 0) {\n process.stdout.write('No bundles on this page.\\n');\n return exitAfterFlush(ExitCode.Ok);\n }\n for (const b of result.contents) {\n const id = typeof b.id === 'string' || typeof b.id === 'number' ? b.id : '-';\n const version = typeof b.version === 'string' ? b.version : '-';\n const status = typeof b.deployStatus === 'string' ? b.deployStatus : '-';\n const createdAt = typeof b.createdAt === 'string' ? b.createdAt : '';\n process.stdout.write(`${id}\\t${version}\\t${status}\\t${createdAt}\\n`);\n }\n if (result.currentPage + 1 < result.totalPage) {\n process.stdout.write(`(more: --page ${result.currentPage + 1})\\n`);\n }\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\nconst bundlesDeployedCommand = defineCommand({\n meta: {\n name: 'deployed',\n description: 'Show the currently deployed bundle for a mini-app (or null if none).',\n },\n args: {\n id: {\n type: 'positional',\n description: 'Mini-app ID. Optional when `aitcc.yaml` provides `miniAppId`.',\n required: false,\n },\n workspace: {\n type: 'string',\n description: 'Workspace ID. Defaults to the selected workspace.',\n },\n json: { type: 'boolean', description: 'Emit machine-readable JSON.', default: false },\n },\n async run({ args }) {\n const ctx = await resolveAppOrFail({\n json: args.json,\n appIdRaw: args.id,\n ...(args.workspace !== undefined ? { workspace: args.workspace } : {}),\n });\n if (!ctx) return;\n const appId = await requireMiniAppId(ctx, args.json);\n if (appId === null) return;\n printContextHeader(ctx, { json: args.json });\n const { session, workspaceId } = ctx;\n\n try {\n const bundle = await fetchDeployedBundle(workspaceId, appId, session.cookies);\n if (args.json) {\n emitJson({ ok: true, workspaceId, appId, bundle });\n return exitAfterFlush(ExitCode.Ok);\n }\n if (bundle === null) {\n process.stdout.write(`App ${appId} (ws ${workspaceId}): no deployed bundle\\n`);\n return exitAfterFlush(ExitCode.Ok);\n }\n const id = typeof bundle.id === 'string' || typeof bundle.id === 'number' ? bundle.id : '-';\n const version = typeof bundle.version === 'string' ? bundle.version : '-';\n const status = typeof bundle.deployStatus === 'string' ? bundle.deployStatus : '-';\n const deployedAt = typeof bundle.deployedAt === 'string' ? bundle.deployedAt : '';\n process.stdout.write(`App ${appId} deployed bundle:\\n`);\n process.stdout.write(` id ${id}\\n`);\n process.stdout.write(` version ${version}\\n`);\n process.stdout.write(` status ${status}\\n`);\n process.stdout.write(` deployedAt ${deployedAt}\\n`);\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\n// --json contract (consumed by agent-plugin):\n//\n// app bundles upload <id> <path> [--deployment-id <uuid>] [--memo <text>]\n// [--workspace <id>] [--dry-run]:\n// { ok: true, workspaceId, appId, deploymentId, reviewStatus,\n// bundle: { ... }, memoApplied: boolean } exit 0\n// { ok: true, dryRun: true, workspaceId, appId, deploymentId,\n// bytes, memo } exit 0\n// { ok: false, reason: 'invalid-id' | 'file-unreadable'\n// | 'missing-deployment-id'\n// | 'bundle-not-prepare', ... } exit 2\n//\n// 3-step upload: initialize → PUT to S3 presigned URL → complete,\n// optionally followed by POST /bundles/memos. deployment-id is the\n// `_metadata.deploymentId` inside the .ait's app.json — for now we ask\n// the caller to supply it explicitly (zip-cracking is a follow-up).\n// On mismatched reviewStatus (\"이미 존재하는 버전이에요.\") we bail\n// before touching S3 with exit 2 / reason `bundle-not-prepare`, matching\n// the console's own client-side guard.\n\nconst bundlesUploadCommand = defineCommand({\n meta: {\n name: 'upload',\n description: 'Upload an .ait bundle (initialize → PUT → complete [+ memo]).',\n },\n args: {\n // Two positionals (`id` then `path`) — citty assigns positionals in\n // declaration order, so `id` must stay required: leaving it optional\n // would let citty hand `./bundle.ait` to `id` and then complain that\n // `path` is missing. Yaml miniAppId fallback is reserved for commands\n // with a single positional or with the app id behind a flag (see\n // `app deploy`'s `--app`).\n id: {\n type: 'positional',\n description: 'Mini-app ID.',\n required: true,\n },\n path: { type: 'positional', description: 'Path to the .ait bundle file.', required: true },\n 'deployment-id': {\n type: 'string',\n description: 'deploymentId embedded in the bundle (from app.json._metadata.deploymentId).',\n },\n memo: { type: 'string', description: 'Optional memo attached to this bundle version.' },\n workspace: {\n type: 'string',\n description: 'Workspace ID. Defaults to the selected workspace.',\n },\n 'dry-run': {\n type: 'boolean',\n description: 'Validate inputs and show what would be sent, without touching the server.',\n default: false,\n },\n json: { type: 'boolean', description: 'Emit machine-readable JSON.', default: false },\n },\n async run({ args }) {\n const deploymentId = typeof args['deployment-id'] === 'string' ? args['deployment-id'] : '';\n if (deploymentId === '') {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'missing-deployment-id',\n message:\n '--deployment-id is required; read app.json._metadata.deploymentId from inside the .ait',\n });\n } else {\n process.stderr.write(\n 'app bundles upload: --deployment-id <uuid> is required.\\n' +\n ' The .ait bundle is a zip; read app.json inside and copy _metadata.deploymentId.\\n',\n );\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n const filePath = typeof args.path === 'string' ? args.path : '';\n let bytes: Uint8Array;\n try {\n const { readFile } = await import('node:fs/promises');\n const buf = await readFile(filePath);\n bytes = new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n if (args.json) {\n emitJson({ ok: false, reason: 'file-unreadable', path: filePath, message });\n } else {\n process.stderr.write(`app bundles upload: cannot read ${filePath}: ${message}\\n`);\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n\n const ctx = await resolveAppOrFail({\n json: args.json,\n appIdRaw: args.id,\n ...(args.workspace !== undefined ? { workspace: args.workspace } : {}),\n });\n if (!ctx) return;\n const appId = await requireMiniAppId(ctx, args.json);\n if (appId === null) return;\n printContextHeader(ctx, { json: args.json });\n const { session, workspaceId } = ctx;\n const memo = typeof args.memo === 'string' && args.memo.length > 0 ? args.memo : undefined;\n\n if (args['dry-run']) {\n if (args.json) {\n emitJson({\n ok: true,\n dryRun: true,\n workspaceId,\n appId,\n deploymentId,\n bytes: bytes.byteLength,\n memo: memo ?? null,\n });\n } else {\n process.stdout.write(\n `DRY RUN\\n` +\n ` workspace ${workspaceId}\\n` +\n ` appId ${appId}\\n` +\n ` deploymentId ${deploymentId}\\n` +\n ` bytes ${bytes.byteLength}\\n` +\n ` memo ${memo ?? '(none)'}\\n`,\n );\n }\n return exitAfterFlush(ExitCode.Ok);\n }\n\n try {\n const init = await postDeploymentsInitialize(\n workspaceId,\n appId,\n deploymentId,\n session.cookies,\n );\n if (init.reviewStatus !== 'PREPARE') {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'bundle-not-prepare',\n workspaceId,\n appId,\n deploymentId,\n reviewStatus: init.reviewStatus,\n message: '이미 존재하는 버전이에요.',\n });\n } else {\n process.stderr.write(\n `app bundles upload: deployment ${deploymentId} is already in state ${init.reviewStatus}; bundle upload refused.\\n`,\n );\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n await putBundleToUploadUrl(init.uploadUrl, bytes);\n const bundle = await postDeploymentsComplete(\n workspaceId,\n appId,\n deploymentId,\n session.cookies,\n );\n let memoApplied = false;\n if (memo !== undefined) {\n await postBundleMemo(workspaceId, appId, deploymentId, memo, session.cookies);\n memoApplied = true;\n }\n if (args.json) {\n emitJson({\n ok: true,\n workspaceId,\n appId,\n deploymentId,\n reviewStatus: init.reviewStatus,\n bundle,\n memoApplied,\n });\n return exitAfterFlush(ExitCode.Ok);\n }\n process.stdout.write(\n `Uploaded bundle for app ${appId} (ws ${workspaceId})\\n` +\n ` deploymentId ${deploymentId}\\n` +\n ` bytes ${bytes.byteLength}\\n` +\n ` memo ${memoApplied ? 'applied' : '(none)'}\\n`,\n );\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\n// --json contract (consumed by agent-plugin):\n//\n// app bundles review <id> --deployment-id <uuid> --release-notes <text>\n// [--withdraw] [--workspace <id>]:\n// { ok: true, workspaceId, appId, deploymentId, action: 'submit'|'withdraw',\n// result: { ... } } exit 0\n// { ok: false, reason: 'invalid-id' | 'missing-deployment-id'\n// | 'missing-release-notes' } exit 2\n//\n// Submits (default) or withdraws a review request on an uploaded bundle.\n// Server validates the deploymentId belongs to the app — we don't\n// double-check here. `--withdraw` is the opposite action and takes no\n// release-notes. Submitting requires release-notes even if empty on the\n// wire (the UI sends \"\") — we forward whatever the caller supplies.\n\nconst bundlesReviewCommand = defineCommand({\n meta: {\n name: 'review',\n description: 'Submit (or withdraw) an uploaded bundle for review.',\n },\n args: {\n id: {\n type: 'positional',\n description: 'Mini-app ID. Optional when `aitcc.yaml` provides `miniAppId`.',\n required: false,\n },\n 'deployment-id': {\n type: 'string',\n description: 'deploymentId of the uploaded bundle.',\n },\n 'release-notes': {\n type: 'string',\n description: 'Release notes shown to the reviewer. Ignored with --withdraw.',\n },\n withdraw: {\n type: 'boolean',\n description: 'Withdraw the existing review request instead of submitting a new one.',\n default: false,\n },\n workspace: {\n type: 'string',\n description: 'Workspace ID. Defaults to the selected workspace.',\n },\n json: { type: 'boolean', description: 'Emit machine-readable JSON.', default: false },\n },\n async run({ args }) {\n const deploymentId = typeof args['deployment-id'] === 'string' ? args['deployment-id'] : '';\n if (deploymentId === '') {\n if (args.json) {\n emitJson({ ok: false, reason: 'missing-deployment-id' });\n } else {\n process.stderr.write('app bundles review: --deployment-id <uuid> is required.\\n');\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n const withdraw = Boolean(args.withdraw);\n const releaseNotes =\n typeof args['release-notes'] === 'string' ? args['release-notes'] : undefined;\n if (!withdraw && releaseNotes === undefined) {\n if (args.json) {\n emitJson({ ok: false, reason: 'missing-release-notes' });\n } else {\n process.stderr.write(\n 'app bundles review: --release-notes <text> is required to submit for review.\\n',\n );\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n\n const ctx = await resolveAppOrFail({\n json: args.json,\n appIdRaw: args.id,\n ...(args.workspace !== undefined ? { workspace: args.workspace } : {}),\n });\n if (!ctx) return;\n const appId = await requireMiniAppId(ctx, args.json);\n if (appId === null) return;\n printContextHeader(ctx, { json: args.json });\n const { session, workspaceId } = ctx;\n\n try {\n if (withdraw) {\n const result = await postBundleReviewWithdrawal(\n workspaceId,\n appId,\n deploymentId,\n session.cookies,\n );\n if (args.json) {\n emitJson({\n ok: true,\n workspaceId,\n appId,\n deploymentId,\n action: 'withdraw',\n result,\n });\n return exitAfterFlush(ExitCode.Ok);\n }\n process.stdout.write(\n `Withdrew review for bundle ${deploymentId} (app ${appId}, ws ${workspaceId})\\n`,\n );\n return exitAfterFlush(ExitCode.Ok);\n }\n const result = await postBundleReview(\n {\n workspaceId,\n miniAppId: appId,\n deploymentId,\n releaseNotes: releaseNotes ?? '',\n },\n session.cookies,\n );\n if (args.json) {\n emitJson({\n ok: true,\n workspaceId,\n appId,\n deploymentId,\n action: 'submit',\n result,\n });\n return exitAfterFlush(ExitCode.Ok);\n }\n const versionName = typeof result.versionName === 'string' ? result.versionName : '';\n process.stdout.write(\n `Submitted bundle ${deploymentId} for review (app ${appId}, ws ${workspaceId})` +\n (versionName ? ` — version ${versionName}` : '') +\n '\\n',\n );\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\n// --json contract (consumed by agent-plugin):\n//\n// app bundles release <id> --deployment-id <uuid> [--workspace <id>]:\n// { ok: true, workspaceId, appId, deploymentId, result: { ... } } exit 0\n// { ok: false, reason: 'invalid-id' | 'missing-deployment-id'\n// | 'not-confirmed' } exit 2\n//\n// Flips an APPROVED bundle live. This is the destructive write path —\n// end users will see the new version. Guarded behind `--confirm` to\n// prevent accidental invocation from a loose shell history. No `--json`\n// bypass: the confirmation flag is mandatory.\n\nconst bundlesReleaseCommand = defineCommand({\n meta: {\n name: 'release',\n description: 'Release (publish) an APPROVED bundle to end users.',\n },\n args: {\n id: {\n type: 'positional',\n description: 'Mini-app ID. Optional when `aitcc.yaml` provides `miniAppId`.',\n required: false,\n },\n 'deployment-id': {\n type: 'string',\n description: 'deploymentId of the APPROVED bundle to publish.',\n },\n confirm: {\n type: 'boolean',\n description: 'Required to actually release — without it, the command refuses.',\n default: false,\n },\n workspace: {\n type: 'string',\n description: 'Workspace ID. Defaults to the selected workspace.',\n },\n json: { type: 'boolean', description: 'Emit machine-readable JSON.', default: false },\n },\n async run({ args }) {\n const deploymentId = typeof args['deployment-id'] === 'string' ? args['deployment-id'] : '';\n if (deploymentId === '') {\n if (args.json) {\n emitJson({ ok: false, reason: 'missing-deployment-id' });\n } else {\n process.stderr.write('app bundles release: --deployment-id <uuid> is required.\\n');\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n if (!args.confirm) {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'not-confirmed',\n message: 'release is destructive; pass --confirm to proceed',\n });\n } else {\n process.stderr.write(\n 'app bundles release: this publishes the bundle to end users.\\n' +\n ' Re-run with --confirm to proceed.\\n',\n );\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n\n const ctx = await resolveAppOrFail({\n json: args.json,\n appIdRaw: args.id,\n ...(args.workspace !== undefined ? { workspace: args.workspace } : {}),\n });\n if (!ctx) return;\n const appId = await requireMiniAppId(ctx, args.json);\n if (appId === null) return;\n printContextHeader(ctx, { json: args.json });\n const { session, workspaceId } = ctx;\n\n try {\n const result = await postBundleRelease(\n { workspaceId, miniAppId: appId, deploymentId },\n session.cookies,\n );\n if (args.json) {\n emitJson({ ok: true, workspaceId, appId, deploymentId, result });\n return exitAfterFlush(ExitCode.Ok);\n }\n process.stdout.write(\n `Released bundle ${deploymentId} for app ${appId} (ws ${workspaceId})\\n`,\n );\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\n// --json contract (consumed by agent-plugin):\n//\n// app bundles test-push <id> --deployment-id <uuid> [--workspace <id>]:\n// { ok: true, workspaceId, appId, deploymentId, result: { ... } } exit 0\n//\n// app bundles test-links <id> [--workspace <id>]:\n// { ok: true, workspaceId, appId, links: { ... } } exit 0\n\nconst bundlesTestPushCommand = defineCommand({\n meta: {\n name: 'test-push',\n description: 'Send a test push so the uploader can open this bundle on their device.',\n },\n args: {\n id: {\n type: 'positional',\n description: 'Mini-app ID. Optional when `aitcc.yaml` provides `miniAppId`.',\n required: false,\n },\n 'deployment-id': {\n type: 'string',\n description: 'deploymentId of the bundle to test.',\n },\n workspace: {\n type: 'string',\n description: 'Workspace ID. Defaults to the selected workspace.',\n },\n json: { type: 'boolean', description: 'Emit machine-readable JSON.', default: false },\n },\n async run({ args }) {\n const deploymentId = typeof args['deployment-id'] === 'string' ? args['deployment-id'] : '';\n if (deploymentId === '') {\n if (args.json) {\n emitJson({ ok: false, reason: 'missing-deployment-id' });\n } else {\n process.stderr.write('app bundles test-push: --deployment-id <uuid> is required.\\n');\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n const ctx = await resolveAppOrFail({\n json: args.json,\n appIdRaw: args.id,\n ...(args.workspace !== undefined ? { workspace: args.workspace } : {}),\n });\n if (!ctx) return;\n const appId = await requireMiniAppId(ctx, args.json);\n if (appId === null) return;\n printContextHeader(ctx, { json: args.json });\n const { session, workspaceId } = ctx;\n try {\n const result = await postBundleTestPush(workspaceId, appId, deploymentId, session.cookies);\n if (args.json) {\n emitJson({ ok: true, workspaceId, appId, deploymentId, result });\n return exitAfterFlush(ExitCode.Ok);\n }\n process.stdout.write(`Sent test push for bundle ${deploymentId} (app ${appId})\\n`);\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\nconst bundlesTestLinksCommand = defineCommand({\n meta: {\n name: 'test-links',\n description: 'Show per-device test URLs for the mini-app.',\n },\n args: {\n id: {\n type: 'positional',\n description: 'Mini-app ID. Optional when `aitcc.yaml` provides `miniAppId`.',\n required: false,\n },\n workspace: {\n type: 'string',\n description: 'Workspace ID. Defaults to the selected workspace.',\n },\n json: { type: 'boolean', description: 'Emit machine-readable JSON.', default: false },\n },\n async run({ args }) {\n const ctx = await resolveAppOrFail({\n json: args.json,\n appIdRaw: args.id,\n ...(args.workspace !== undefined ? { workspace: args.workspace } : {}),\n });\n if (!ctx) return;\n const appId = await requireMiniAppId(ctx, args.json);\n if (appId === null) return;\n printContextHeader(ctx, { json: args.json });\n const { session, workspaceId } = ctx;\n try {\n const links = await fetchBundleTestLinks(workspaceId, appId, session.cookies);\n if (args.json) {\n emitJson({ ok: true, workspaceId, appId, links });\n return exitAfterFlush(ExitCode.Ok);\n }\n const keys = Object.keys(links);\n if (keys.length === 0) {\n process.stdout.write(`App ${appId} (ws ${workspaceId}): no test links available\\n`);\n return exitAfterFlush(ExitCode.Ok);\n }\n process.stdout.write(`App ${appId} (ws ${workspaceId}):\\n`);\n for (const k of keys) {\n const v = links[k];\n process.stdout.write(` ${k}\\t${typeof v === 'string' ? v : JSON.stringify(v)}\\n`);\n }\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\nconst bundlesCommand = defineCommand({\n meta: {\n name: 'bundles',\n description: 'Inspect and manage upload bundles for a mini-app.',\n },\n subCommands: {\n ls: bundlesLsCommand,\n deployed: bundlesDeployedCommand,\n upload: bundlesUploadCommand,\n review: bundlesReviewCommand,\n release: bundlesReleaseCommand,\n 'test-push': bundlesTestPushCommand,\n 'test-links': bundlesTestLinksCommand,\n },\n});\n\n// --json contract (consumed by agent-plugin):\n//\n// app certs ls <id> [--workspace <id>]:\n// { ok: true, workspaceId, appId,\n// certs: [{...passthrough, daysUntilExpiry: number | null}] } exit 0\n// { ok: false, reason: 'invalid-id' | ... } exit 2\n//\n// mTLS certs for an app. Empty array is the common case (no certs\n// generated yet). Per-record shape is passed through opaquely with one\n// client-augmented field: `daysUntilExpiry` is computed from `expireTs`\n// (server, ms epoch) — `null` when `expireTs` is absent or unparseable.\n// agent-plugin consumers can treat all other fields as `Record<string, unknown>`.\n\n// Threshold for the ⚠ marker in text output. Const-only by design — exposing\n// it as a flag would force every dog-food caller to remember a number.\nconst CERT_EXPIRY_WARN_DAYS = 30;\nconst MS_PER_DAY = 86_400_000;\n\n// `expireTs` is documented as ms-since-epoch (number) but the wire has\n// surfaced strings before for adjacent fields, so accept both forms.\n// Returns `null` when the input is missing or unparseable; we never fall\n// back to a sibling field — the cert page chunk only references `expireTs`,\n// guessing from `validUntil` would be making up data.\nexport function deriveDaysUntilExpiry(\n cert: Readonly<Record<string, unknown>>,\n now: number,\n): number | null {\n const raw = cert.expireTs;\n let ts: number | null = null;\n if (typeof raw === 'number' && Number.isFinite(raw)) ts = raw;\n else if (typeof raw === 'string') {\n const parsed = Date.parse(raw);\n if (Number.isFinite(parsed)) ts = parsed;\n }\n if (ts === null) return null;\n return Math.floor((ts - now) / MS_PER_DAY);\n}\n\nfunction expiryMarker(days: number | null): string {\n if (days === null) return '';\n if (days < 0) return `\\t⚠ 만료됨`;\n if (days <= CERT_EXPIRY_WARN_DAYS) return `\\t⚠ 만료 임박 (${days}일)`;\n return '';\n}\n\nconst certsLsCommand = defineCommand({\n meta: {\n name: 'ls',\n description: 'List mTLS certificates issued for a mini-app.',\n },\n args: {\n id: {\n type: 'positional',\n description: 'Mini-app ID. Optional when `aitcc.yaml` provides `miniAppId`.',\n required: false,\n },\n workspace: {\n type: 'string',\n description: 'Workspace ID. Defaults to the selected workspace.',\n },\n json: { type: 'boolean', description: 'Emit machine-readable JSON.', default: false },\n },\n async run({ args }) {\n const ctx = await resolveAppOrFail({\n json: args.json,\n appIdRaw: args.id,\n ...(args.workspace !== undefined ? { workspace: args.workspace } : {}),\n });\n if (!ctx) return;\n const appId = await requireMiniAppId(ctx, args.json);\n if (appId === null) return;\n printContextHeader(ctx, { json: args.json });\n const { session, workspaceId } = ctx;\n\n try {\n const certs = await fetchCerts(workspaceId, appId, session.cookies);\n const now = Date.now();\n const augmented: ReadonlyArray<\n Readonly<Record<string, unknown>> & { daysUntilExpiry: number | null }\n > = certs.map((c) => ({\n ...c,\n daysUntilExpiry: deriveDaysUntilExpiry(c, now),\n }));\n if (args.json) {\n emitJson({ ok: true, workspaceId, appId, certs: augmented });\n return exitAfterFlush(ExitCode.Ok);\n }\n if (augmented.length === 0) {\n process.stdout.write(`App ${appId} (ws ${workspaceId}): no mTLS certs\\n`);\n return exitAfterFlush(ExitCode.Ok);\n }\n process.stdout.write(`App ${appId} (ws ${workspaceId}): ${augmented.length} cert(s)\\n`);\n for (const c of augmented) {\n const id =\n typeof c.id === 'string' || typeof c.id === 'number'\n ? c.id\n : typeof c.certId === 'string' || typeof c.certId === 'number'\n ? c.certId\n : '-';\n const cn = typeof c.commonName === 'string' ? c.commonName : '-';\n const createdAt = typeof c.createdAt === 'string' ? c.createdAt : '';\n // `expireTs` (ms epoch) is the canonical server field per\n // docs/api/mini-app-misc.md; `expiresAt`/`validUntil` are tolerated\n // for forward-compat. Render whichever is present so the column\n // never blanks when only `expireTs` (the typical case) is returned.\n const expiresAt =\n typeof c.expiresAt === 'string'\n ? c.expiresAt\n : typeof c.validUntil === 'string'\n ? c.validUntil\n : typeof c.expireTs === 'number' && Number.isFinite(c.expireTs)\n ? new Date(c.expireTs).toISOString()\n : '';\n process.stdout.write(\n `${id}\\t${cn}\\t${createdAt}\\t${expiresAt}${expiryMarker(c.daysUntilExpiry)}\\n`,\n );\n }\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\n// `app certs show <certId>` rides on top of the same `GET .../certs` list\n// endpoint as `ls`. The console SPA's mTLS chunk (`index.Bw6JQUAu.js`)\n// defines exactly three endpoints — list, issue, disable — and there is no\n// per-cert detail call. We surface a single-cert convenience view by\n// fetching the list and filtering client-side; the round-trip cost is the\n// same one request as `ls`. Documented at docs/api/mini-app-misc.md\n// \"mTLS cert issue / disable / list\".\n//\n// `pickCertById` mirrors the id-resolution policy of `ls`: prefer `id`,\n// fall back to `certId`. Comparison is string-coerced because the server\n// has historically been ambivalent about whether ids are number- or\n// string-typed across endpoints.\nexport function pickCertById(\n certs: readonly Readonly<Record<string, unknown>>[],\n certId: string,\n): Readonly<Record<string, unknown>> | null {\n const target = certId.trim();\n if (target.length === 0) return null;\n for (const c of certs) {\n const candidate =\n typeof c.id === 'string' || typeof c.id === 'number'\n ? c.id\n : typeof c.certId === 'string' || typeof c.certId === 'number'\n ? c.certId\n : null;\n if (candidate !== null && String(candidate) === target) return c;\n }\n return null;\n}\n\n// `daysUntilExpiry` is derived rather than passed through because the\n// server gives the expiry in two shapes depending on which version of the\n// console chunk last touched the field — `expireTs` (millis since epoch,\n// the chunk's canonical name) or `expiresAt`/`validUntil` (ISO 8601, seen\n// in older captures). We accept all three and bail out (undefined) when\n// the field is missing or unparseable rather than pretending zero days.\n// Negative values are preserved so callers can tell \"expired 5 days ago\"\n// from \"expires in 5 days\".\nexport function augmentCertExpiry(\n cert: Readonly<Record<string, unknown>>,\n now: number = Date.now(),\n): { expiresAtMs?: number; daysUntilExpiry?: number } {\n let expiresAtMs: number | undefined;\n if (typeof cert.expireTs === 'number' && Number.isFinite(cert.expireTs)) {\n expiresAtMs = cert.expireTs;\n } else if (typeof cert.expiresAt === 'string') {\n const t = Date.parse(cert.expiresAt);\n if (Number.isFinite(t)) expiresAtMs = t;\n } else if (typeof cert.validUntil === 'string') {\n const t = Date.parse(cert.validUntil);\n if (Number.isFinite(t)) expiresAtMs = t;\n }\n if (expiresAtMs === undefined) return {};\n const daysUntilExpiry = Math.floor((expiresAtMs - now) / 86_400_000);\n return { expiresAtMs, daysUntilExpiry };\n}\n\n// --json contract (consumed by agent-plugin):\n//\n// app certs show <certId> --app <id> [--workspace <id>] [--json]:\n// { ok: true, workspaceId, appId, cert,\n// daysUntilExpiry?: number, expiresAtMs?: number } exit 0\n// { ok: false, reason: 'missing-cert-id' | 'not-found'\n// | 'invalid-id' | 'missing-app-id'\n// | 'no-workspace-selected' } exit 2\n//\n// Single-cert detail view. There is no dedicated detail endpoint on the\n// console (see comment above `pickCertById`); this is a list fetch +\n// client-side filter. PEM material is never present on list responses —\n// the issue endpoint is the only one that ever exposes it, so there is no\n// `--print-key` here.\n\nconst certsShowCommand = defineCommand({\n meta: {\n name: 'show',\n description: 'Show a single mTLS certificate by id (metadata only — no PEM).',\n },\n args: {\n certId: { type: 'positional', description: 'Cert ID (from `app certs ls`).', required: true },\n app: {\n type: 'string',\n description: 'Mini-app ID the cert belongs to.',\n },\n workspace: {\n type: 'string',\n description: 'Workspace ID. Defaults to the selected workspace.',\n },\n json: { type: 'boolean', description: 'Emit machine-readable JSON.', default: false },\n },\n async run({ args }) {\n const certId = typeof args.certId === 'string' ? args.certId.trim() : '';\n if (certId.length === 0) {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'missing-cert-id',\n message: 'certId positional is required',\n });\n } else {\n process.stderr.write('app certs show: certId is required\\n');\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n\n // Same shape as `certs revoke`: positional is <certId>, mini-app id is\n // a separate `--app` flag.\n const ctx = await resolveAppOrFail({\n json: args.json,\n appIdRaw: args.app,\n appIdField: 'app',\n ...(args.workspace !== undefined ? { workspace: args.workspace } : {}),\n });\n if (!ctx) return;\n const appId = await requireMiniAppId(ctx, args.json);\n if (appId === null) return;\n printContextHeader(ctx, { json: args.json });\n const { session, workspaceId } = ctx;\n\n try {\n const certs = await fetchCerts(workspaceId, appId, session.cookies);\n const match = pickCertById(certs, certId);\n if (match === null) {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'not-found',\n workspaceId,\n appId,\n certId,\n message: `cert ${certId} not found on app ${appId}`,\n });\n } else {\n process.stderr.write(\n `app certs show: cert ${certId} not found on app ${appId} (ws ${workspaceId})\\n`,\n );\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n\n const expiry = augmentCertExpiry(match);\n if (args.json) {\n emitJson({ ok: true, workspaceId, appId, cert: match, ...expiry });\n return exitAfterFlush(ExitCode.Ok);\n }\n\n const id =\n typeof match.id === 'string' || typeof match.id === 'number'\n ? match.id\n : typeof match.certId === 'string' || typeof match.certId === 'number'\n ? match.certId\n : '-';\n const name = typeof match.name === 'string' ? match.name : '-';\n const cn = typeof match.commonName === 'string' ? match.commonName : '';\n const createdAt = typeof match.createdAt === 'string' ? match.createdAt : '';\n const expiresAtIso =\n expiry.expiresAtMs !== undefined ? new Date(expiry.expiresAtMs).toISOString() : '';\n const status = typeof match.status === 'string' ? match.status : '';\n\n const lines: string[] = [\n `App ${appId} (ws ${workspaceId}): cert ${id}`,\n ` name: ${name}`,\n ];\n if (cn) lines.push(` commonName: ${cn}`);\n if (createdAt) lines.push(` createdAt: ${createdAt}`);\n if (expiresAtIso) lines.push(` expiresAt: ${expiresAtIso}`);\n if (expiry.daysUntilExpiry !== undefined) {\n const d = expiry.daysUntilExpiry;\n // d > 0 → D-N countdown; d === 0 → \"expires today\" (D-0 reads\n // ambiguously as both \"0 left\" and \"the countdown number\"); d < 0 →\n // already expired.\n const suffix =\n d > 0 ? ` (D-${d})` : d === 0 ? ' (expires today)' : ` (expired ${-d} day(s) ago)`;\n lines.push(` daysUntilExpiry: ${d}${suffix}`);\n }\n if (status) lines.push(` status: ${status}`);\n process.stdout.write(`${lines.join('\\n')}\\n`);\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\n// --json contract (consumed by agent-plugin):\n//\n// app certs issue <id> --name <name> [--workspace <id>]\n// [--out <dir>] [--print-key]:\n// { ok: true, workspaceId, appId, name,\n// publicKey, privateKey?, savedTo?: { publicKey, privateKey } } exit 0\n// { ok: false, reason: 'invalid-id' | 'invalid-name'\n// | 'invalid-out-dir' } exit 2\n//\n// Issues an mTLS client certificate. The console UI's name placeholder\n// reads \"공백, 한글, 특수문자 제외\", so the CLI mirrors that rule client-side\n// before any network call: 1+ char, ASCII letters/digits/`-`/`_` only.\n//\n// `privateKey` is only present in the response when `--print-key` is set\n// or when `--out` is *not* set (i.e. the caller has explicitly asked for\n// the material on stdout). With `--out <dir>` (default agent-plugin\n// pattern) two files are written with mode 0o600 — `<name>.crt` (cert)\n// and `<name>.key` (private key) — and the JSON payload only echoes\n// the two paths. The plain-text emitter never prints the private key\n// regardless of flags; it points the user at the file and `--json` if\n// they need it programmatically.\n//\n// app certs revoke <certId> --app <id> [--workspace <id>]:\n// { ok: true, workspaceId, appId, certId } exit 0\n// { ok: false, reason: 'invalid-id' | 'missing-cert-id' } exit 2\n//\n// Hits the wire's `disable` endpoint. Naming follows the user mental\n// model (issue ↔ revoke); the console UI button itself is labelled\n// \"삭제\" and there is no reactivate path, so `revoke` is the honest\n// surface even though the path is named `disable`.\n\nconst CERT_NAME_RE = /^[A-Za-z0-9_-]+$/;\n\nfunction parseCertName(raw: string | undefined): { value: string } | { error: string } {\n if (typeof raw !== 'string' || raw.length === 0) {\n return { error: '--name is required (cert display name)' };\n }\n if (!CERT_NAME_RE.test(raw)) {\n return {\n error:\n '--name must contain only ASCII letters, digits, `-`, or `_` (no spaces, no Korean, no special chars)',\n };\n }\n return { value: raw };\n}\n\nconst certsIssueCommand = defineCommand({\n meta: {\n name: 'issue',\n description: 'Issue a new mTLS certificate for a mini-app.',\n },\n args: {\n id: {\n type: 'positional',\n description: 'Mini-app ID. Optional when `aitcc.yaml` provides `miniAppId`.',\n required: false,\n },\n workspace: {\n type: 'string',\n description: 'Workspace ID. Defaults to the selected workspace.',\n },\n name: {\n type: 'string',\n description: 'Cert display name. ASCII letters/digits/`-`/`_` only.',\n },\n out: {\n type: 'string',\n description: 'Directory to write `<name>.crt` and `<name>.key` (mode 0600).',\n },\n 'print-key': {\n type: 'boolean',\n description: 'Include the private key in the response (default: cert only).',\n default: false,\n },\n json: { type: 'boolean', description: 'Emit machine-readable JSON.', default: false },\n },\n async run({ args }) {\n const nameResult = parseCertName(args.name);\n if ('error' in nameResult) {\n if (args.json) {\n emitJson({ ok: false, reason: 'invalid-name', message: nameResult.error });\n } else {\n process.stderr.write(`app certs issue: ${nameResult.error}\\n`);\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n const name = nameResult.value;\n\n const ctx = await resolveAppOrFail({\n json: args.json,\n appIdRaw: args.id,\n ...(args.workspace !== undefined ? { workspace: args.workspace } : {}),\n });\n if (!ctx) return;\n const appId = await requireMiniAppId(ctx, args.json);\n if (appId === null) return;\n printContextHeader(ctx, { json: args.json });\n const { session, workspaceId } = ctx;\n\n try {\n const result = await issueCert(workspaceId, appId, name, session.cookies);\n\n let savedTo: { publicKey: string; privateKey: string } | undefined;\n if (typeof args.out === 'string' && args.out.length > 0) {\n const dir = resolvePath(args.out);\n try {\n await mkdir(dir, { recursive: true });\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'invalid-out-dir',\n message: `--out: cannot create ${JSON.stringify(args.out)}: ${message}`,\n });\n } else {\n process.stderr.write(\n `app certs issue: cannot create --out directory ${JSON.stringify(args.out)}: ${message}\\n`,\n );\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n const certPath = resolvePath(dir, `${name}.crt`);\n const keyPath = resolvePath(dir, `${name}.key`);\n await writeFile(certPath, result.publicKey, { mode: 0o600 });\n await writeFile(keyPath, result.privateKey, { mode: 0o600 });\n savedTo = { publicKey: certPath, privateKey: keyPath };\n }\n\n if (args.json) {\n const includeKey = args['print-key'] === true || savedTo === undefined;\n emitJson({\n ok: true,\n workspaceId,\n appId,\n name,\n publicKey: result.publicKey,\n ...(includeKey ? { privateKey: result.privateKey } : {}),\n ...(savedTo ? { savedTo } : {}),\n });\n return exitAfterFlush(ExitCode.Ok);\n }\n\n // Plain output never prints the private key; it points the user at\n // the saved files (or `--json` / `--print-key` for programmatic use).\n if (savedTo) {\n process.stdout.write(\n `App ${appId} (ws ${workspaceId}): issued cert \"${name}\"\\n` +\n ` cert: ${savedTo.publicKey}\\n` +\n ` key: ${savedTo.privateKey}\\n`,\n );\n } else {\n process.stdout.write(\n `App ${appId} (ws ${workspaceId}): issued cert \"${name}\"\\n${result.publicKey}` +\n (result.publicKey.endsWith('\\n') ? '' : '\\n') +\n 'Private key not shown. Re-run with --out <dir> or --json --print-key to capture it.\\n',\n );\n }\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\nconst certsRevokeCommand = defineCommand({\n meta: {\n name: 'revoke',\n description: 'Revoke (disable) an mTLS certificate.',\n },\n args: {\n certId: { type: 'positional', description: 'Cert ID (from `app certs ls`).', required: true },\n app: {\n type: 'string',\n description: 'Mini-app ID the cert belongs to.',\n },\n workspace: {\n type: 'string',\n description: 'Workspace ID. Defaults to the selected workspace.',\n },\n json: { type: 'boolean', description: 'Emit machine-readable JSON.', default: false },\n },\n async run({ args }) {\n const certId = typeof args.certId === 'string' ? args.certId.trim() : '';\n if (certId.length === 0) {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'missing-cert-id',\n message: 'certId positional is required',\n });\n } else {\n process.stderr.write('app certs revoke: certId is required\\n');\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n\n // certs revoke is the one Group A command whose mini-app id is a\n // dedicated `--app` flag (not a positional, because the positional is\n // <certId>). Hand the flag value to `resolveAppOrFail` as the\n // appIdRaw — `appIdField: 'app'` makes the error message say `--app\n // must be a positive integer` rather than `app id must be …`.\n const ctx = await resolveAppOrFail({\n json: args.json,\n appIdRaw: args.app,\n appIdField: 'app',\n ...(args.workspace !== undefined ? { workspace: args.workspace } : {}),\n });\n if (!ctx) return;\n const appId = await requireMiniAppId(ctx, args.json);\n if (appId === null) return;\n printContextHeader(ctx, { json: args.json });\n const { session, workspaceId } = ctx;\n\n try {\n await revokeCert(workspaceId, appId, certId, session.cookies);\n if (args.json) {\n emitJson({ ok: true, workspaceId, appId, certId });\n return exitAfterFlush(ExitCode.Ok);\n }\n process.stdout.write(`App ${appId} (ws ${workspaceId}): revoked cert ${certId}\\n`);\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\nconst certsCommand = defineCommand({\n meta: {\n name: 'certs',\n description: 'Inspect mTLS certificates for a mini-app.',\n },\n subCommands: {\n ls: certsLsCommand,\n show: certsShowCommand,\n issue: certsIssueCommand,\n revoke: certsRevokeCommand,\n },\n});\n\n// --json contract (consumed by agent-plugin):\n//\n// app metrics <id> [--workspace <id>] [--time-unit DAY|WEEK|MONTH]\n// [--start YYYY-MM-DD] [--end YYYY-MM-DD] [--refresh]:\n// { ok: true, workspaceId, appId, timeUnitType, startDate, endDate,\n// cacheTime?, metrics: [...] } exit 0\n// { ok: false, reason: 'invalid-id' | 'invalid-date'\n// | 'invalid-time-unit' | ... } exit 2\n//\n// Conversion metrics for an app over a date range. An empty `metrics`\n// array is the common case for PREPARE-state apps (no live traffic);\n// per-record shape is passed through opaquely. Default window: the last\n// 30 days ending today (host local date). `--refresh` bypasses the\n// server-side cache.\n\nconst VALID_TIME_UNITS: readonly MetricsTimeUnit[] = ['DAY', 'WEEK', 'MONTH'];\n\nfunction parseIsoDate(raw: string, field: string): { value: string } | { error: string } {\n if (!/^\\d{4}-\\d{2}-\\d{2}$/.test(raw)) {\n return { error: `--${field} must be YYYY-MM-DD (got ${JSON.stringify(raw)})` };\n }\n const d = new Date(`${raw}T00:00:00Z`);\n if (Number.isNaN(d.getTime())) {\n return { error: `--${field} is not a valid date (got ${JSON.stringify(raw)})` };\n }\n return { value: raw };\n}\n\nfunction todayLocalIso(): string {\n const d = new Date();\n const y = d.getFullYear();\n const m = String(d.getMonth() + 1).padStart(2, '0');\n const day = String(d.getDate()).padStart(2, '0');\n return `${y}-${m}-${day}`;\n}\n\nfunction daysAgoLocalIso(days: number): string {\n const d = new Date();\n d.setDate(d.getDate() - days);\n const y = d.getFullYear();\n const m = String(d.getMonth() + 1).padStart(2, '0');\n const day = String(d.getDate()).padStart(2, '0');\n return `${y}-${m}-${day}`;\n}\n\nconst metricsCommand = defineCommand({\n meta: {\n name: 'metrics',\n description: 'Show conversion metrics for a mini-app over a date range.',\n },\n args: {\n id: {\n type: 'positional',\n description: 'Mini-app ID. Optional when `aitcc.yaml` provides `miniAppId`.',\n required: false,\n },\n workspace: {\n type: 'string',\n description: 'Workspace ID. Defaults to the selected workspace.',\n },\n 'time-unit': {\n type: 'string',\n description: 'Bucket size: DAY | WEEK | MONTH.',\n default: 'DAY',\n },\n start: {\n type: 'string',\n description: 'Start date (YYYY-MM-DD). Defaults to 30 days before --end.',\n },\n end: {\n type: 'string',\n description: 'End date (YYYY-MM-DD). Defaults to today (host local).',\n },\n refresh: {\n type: 'boolean',\n description: 'Bypass server-side cache.',\n default: false,\n },\n json: { type: 'boolean', description: 'Emit machine-readable JSON.', default: false },\n },\n async run({ args }) {\n const timeUnit = String(args['time-unit']).toUpperCase();\n if (!VALID_TIME_UNITS.includes(timeUnit as MetricsTimeUnit)) {\n const message = `--time-unit must be one of ${VALID_TIME_UNITS.join('|')} (got ${JSON.stringify(args['time-unit'])})`;\n if (args.json) emitJson({ ok: false, reason: 'invalid-time-unit', message });\n else process.stderr.write(`${message}\\n`);\n return exitAfterFlush(ExitCode.Usage);\n }\n\n const endDate = args.end ? String(args.end) : todayLocalIso();\n const endResult = parseIsoDate(endDate, 'end');\n if ('error' in endResult) {\n if (args.json) emitJson({ ok: false, reason: 'invalid-date', message: endResult.error });\n else process.stderr.write(`${endResult.error}\\n`);\n return exitAfterFlush(ExitCode.Usage);\n }\n\n const startDate = args.start ? String(args.start) : daysAgoLocalIso(30);\n const startResult = parseIsoDate(startDate, 'start');\n if ('error' in startResult) {\n if (args.json) emitJson({ ok: false, reason: 'invalid-date', message: startResult.error });\n else process.stderr.write(`${startResult.error}\\n`);\n return exitAfterFlush(ExitCode.Usage);\n }\n\n if (startResult.value > endResult.value) {\n const message = `--start (${startResult.value}) must be on or before --end (${endResult.value})`;\n if (args.json) emitJson({ ok: false, reason: 'invalid-date', message });\n else process.stderr.write(`${message}\\n`);\n return exitAfterFlush(ExitCode.Usage);\n }\n\n const ctx = await resolveAppOrFail({\n json: args.json,\n appIdRaw: args.id,\n ...(args.workspace !== undefined ? { workspace: args.workspace } : {}),\n });\n if (!ctx) return;\n const appId = await requireMiniAppId(ctx, args.json);\n if (appId === null) return;\n printContextHeader(ctx, { json: args.json });\n const { session, workspaceId } = ctx;\n\n try {\n const result = await fetchConversionMetrics(\n {\n workspaceId,\n miniAppId: appId,\n timeUnitType: timeUnit as MetricsTimeUnit,\n startDate: startResult.value,\n endDate: endResult.value,\n refresh: args.refresh,\n },\n session.cookies,\n );\n if (args.json) {\n emitJson({\n ok: true,\n workspaceId,\n appId,\n timeUnitType: timeUnit,\n startDate: startResult.value,\n endDate: endResult.value,\n ...(result.cacheTime !== undefined ? { cacheTime: result.cacheTime } : {}),\n metrics: result.metrics,\n });\n return exitAfterFlush(ExitCode.Ok);\n }\n const header = `App ${appId} (ws ${workspaceId}) · ${timeUnit} · ${startResult.value} → ${endResult.value}`;\n if (result.metrics.length === 0) {\n process.stdout.write(`${header}: no metrics\\n`);\n return exitAfterFlush(ExitCode.Ok);\n }\n process.stdout.write(`${header}: ${result.metrics.length} bucket(s)\\n`);\n for (const m of result.metrics) {\n const date =\n typeof m.date === 'string'\n ? m.date\n : typeof m.bucketDate === 'string'\n ? m.bucketDate\n : '';\n const impressions =\n typeof m.impressions === 'number'\n ? m.impressions\n : typeof m.impressionCount === 'number'\n ? m.impressionCount\n : '';\n const clicks =\n typeof m.clicks === 'number'\n ? m.clicks\n : typeof m.clickCount === 'number'\n ? m.clickCount\n : '';\n process.stdout.write(`${date}\\t${impressions}\\t${clicks}\\n`);\n }\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\n// --json contract (consumed by agent-plugin):\n//\n// app share-rewards ls <id> [--workspace <id>] [--search <text>]:\n// { ok: true, workspaceId, appId, rewards: [...] } exit 0\n// { ok: false, reason: 'invalid-id' | ... } exit 2\n//\n// app messages ls <id> [--workspace <id>] [--page N] [--size N] [--search <text>]:\n// { ok: true, workspaceId, appId, campaigns: [...], paging: {...} } exit 0\n// { ok: false, reason: 'invalid-id' | 'invalid-page' | 'invalid-size' | ... } exit 2\n//\n// app events ls <id> [--workspace <id>] [--page N] [--size N] [--search <text>] [--refresh]:\n// { ok: true, workspaceId, appId, events: [...], cacheTime, paging: {...} } exit 0\n// { ok: false, reason: 'invalid-id' | 'invalid-page' | 'invalid-size' | ... } exit 2\n//\n// app templates ls <id> [--workspace <id>] [--page N] [--size N] [--content-reach-type FUNCTIONAL|MARKETING] [--smart-message true|false]:\n// { ok: true, workspaceId, appId, templates: [...], totalPageCount } exit 0\n// { ok: false, reason: 'invalid-id' | 'invalid-page' | 'invalid-size' |\n// 'invalid-content-reach-type' | 'invalid-smart-message' } exit 2\n//\n// app categories [--selectable]:\n// { ok: true, categories: CategoryTreeEntry[] } exit 0\n// { ok: true, authenticated: false } exit 10\n//\n// app service-status <id> [--workspace <id>]:\n// { ok: true, workspaceId, appId, serviceStatus, shutdownCandidateStatus,\n// scheduledShutdownAt } exit 0\n// { ok: false, reason: 'invalid-id' | ... } exit 2\n//\n// Share-reward promotions for an app. Empty array is the common case\n// (no promotions set up). Per-record shape is passed through opaquely\n// until a populated response is observed.\n\nconst shareRewardsLsCommand = defineCommand({\n meta: {\n name: 'ls',\n description: 'List share-reward promotions configured for a mini-app.',\n },\n args: {\n id: {\n type: 'positional',\n description: 'Mini-app ID. Optional when `aitcc.yaml` provides `miniAppId`.',\n required: false,\n },\n workspace: {\n type: 'string',\n description: 'Workspace ID. Defaults to the selected workspace.',\n },\n search: {\n type: 'string',\n description: 'Filter by title (server-side title-contains match). Empty matches everything.',\n },\n json: { type: 'boolean', description: 'Emit machine-readable JSON.', default: false },\n },\n async run({ args }) {\n const ctx = await resolveAppOrFail({\n json: args.json,\n appIdRaw: args.id,\n ...(args.workspace !== undefined ? { workspace: args.workspace } : {}),\n });\n if (!ctx) return;\n const appId = await requireMiniAppId(ctx, args.json);\n if (appId === null) return;\n printContextHeader(ctx, { json: args.json });\n const { session, workspaceId } = ctx;\n\n try {\n const rewards = await fetchShareRewards(\n {\n workspaceId,\n miniAppId: appId,\n ...(args.search !== undefined ? { search: String(args.search) } : {}),\n },\n session.cookies,\n );\n if (args.json) {\n emitJson({ ok: true, workspaceId, appId, rewards });\n return exitAfterFlush(ExitCode.Ok);\n }\n if (rewards.length === 0) {\n process.stdout.write(`App ${appId} (ws ${workspaceId}): no share-reward promotions\\n`);\n return exitAfterFlush(ExitCode.Ok);\n }\n process.stdout.write(`App ${appId} (ws ${workspaceId}): ${rewards.length} share-reward(s)\\n`);\n for (const r of rewards) {\n const id =\n typeof r.id === 'string' || typeof r.id === 'number'\n ? r.id\n : typeof r.rewardId === 'string' || typeof r.rewardId === 'number'\n ? r.rewardId\n : '-';\n const title =\n typeof r.title === 'string' ? r.title : typeof r.name === 'string' ? r.name : '-';\n const status = typeof r.status === 'string' ? r.status : '-';\n process.stdout.write(`${id}\\t${title}\\t${status}\\n`);\n }\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\nconst shareRewardsCommand = defineCommand({\n meta: {\n name: 'share-rewards',\n description: 'Inspect share-reward promotions for a mini-app.',\n },\n subCommands: {\n ls: shareRewardsLsCommand,\n },\n});\n\nconst messagesLsCommand = defineCommand({\n meta: {\n name: 'ls',\n description: 'List smart-message campaigns (formerly \"push\" — the 스마트 발송 menu).',\n },\n args: {\n id: {\n type: 'positional',\n description: 'Mini-app ID. Optional when `aitcc.yaml` provides `miniAppId`.',\n required: false,\n },\n workspace: {\n type: 'string',\n description: 'Workspace ID. Defaults to the selected workspace.',\n },\n page: { type: 'string', description: 'Page number (0-indexed).', default: '0' },\n size: { type: 'string', description: 'Page size.', default: '20' },\n search: { type: 'string', description: 'Title-contains filter. Empty matches everything.' },\n json: { type: 'boolean', description: 'Emit machine-readable JSON.', default: false },\n },\n async run({ args }) {\n const pageResult = parseNonNegativeInt(String(args.page), 'page');\n if ('error' in pageResult) {\n if (args.json) emitJson({ ok: false, reason: 'invalid-page', message: pageResult.error });\n else process.stderr.write(`${pageResult.error}\\n`);\n return exitAfterFlush(ExitCode.Usage);\n }\n const sizeResult = parseNonNegativeInt(String(args.size), 'size');\n if ('error' in sizeResult) {\n if (args.json) emitJson({ ok: false, reason: 'invalid-size', message: sizeResult.error });\n else process.stderr.write(`${sizeResult.error}\\n`);\n return exitAfterFlush(ExitCode.Usage);\n }\n\n const ctx = await resolveAppOrFail({\n json: args.json,\n appIdRaw: args.id,\n ...(args.workspace !== undefined ? { workspace: args.workspace } : {}),\n });\n if (!ctx) return;\n const appId = await requireMiniAppId(ctx, args.json);\n if (appId === null) return;\n printContextHeader(ctx, { json: args.json });\n const { session, workspaceId } = ctx;\n\n try {\n const result = await fetchSmartMessageCampaigns(\n {\n workspaceId,\n miniAppId: appId,\n page: pageResult.value,\n size: sizeResult.value,\n ...(args.search !== undefined ? { search: String(args.search) } : {}),\n },\n session.cookies,\n );\n if (args.json) {\n emitJson({\n ok: true,\n workspaceId,\n appId,\n campaigns: result.items,\n paging: result.paging,\n });\n return exitAfterFlush(ExitCode.Ok);\n }\n if (result.items.length === 0) {\n process.stdout.write(`App ${appId} (ws ${workspaceId}): no smart-message campaigns\\n`);\n return exitAfterFlush(ExitCode.Ok);\n }\n process.stdout.write(\n `App ${appId} (ws ${workspaceId}): ${result.items.length} campaign(s) on page ${result.paging.pageNumber} of ${result.paging.totalCount}\\n`,\n );\n for (const c of result.items) {\n const id =\n typeof c.id === 'string' || typeof c.id === 'number'\n ? c.id\n : typeof c.campaignId === 'string' || typeof c.campaignId === 'number'\n ? c.campaignId\n : '-';\n const title =\n typeof c.title === 'string' ? c.title : typeof c.name === 'string' ? c.name : '-';\n const status = typeof c.status === 'string' ? c.status : '-';\n process.stdout.write(`${id}\\t${title}\\t${status}\\n`);\n }\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\nconst messagesCommand = defineCommand({\n meta: {\n name: 'messages',\n description: 'Inspect smart-message (formerly push) campaigns for a mini-app.',\n },\n subCommands: {\n ls: messagesLsCommand,\n },\n});\n\nconst eventsLsCommand = defineCommand({\n meta: {\n name: 'ls',\n description: 'List custom event catalogs recorded for a mini-app (the 이벤트 menu).',\n },\n args: {\n id: {\n type: 'positional',\n description: 'Mini-app ID. Optional when `aitcc.yaml` provides `miniAppId`.',\n required: false,\n },\n workspace: {\n type: 'string',\n description: 'Workspace ID. Defaults to the selected workspace.',\n },\n page: { type: 'string', description: 'Page number (0-indexed).', default: '0' },\n size: { type: 'string', description: 'Page size.', default: '20' },\n search: { type: 'string', description: 'Event-name filter. Empty matches everything.' },\n refresh: {\n type: 'boolean',\n description: 'Bypass the server cache and rebuild the catalog list.',\n default: false,\n },\n json: { type: 'boolean', description: 'Emit machine-readable JSON.', default: false },\n },\n async run({ args }) {\n const pageResult = parseNonNegativeInt(String(args.page), 'page');\n if ('error' in pageResult) {\n if (args.json) emitJson({ ok: false, reason: 'invalid-page', message: pageResult.error });\n else process.stderr.write(`${pageResult.error}\\n`);\n return exitAfterFlush(ExitCode.Usage);\n }\n const sizeResult = parseNonNegativeInt(String(args.size), 'size');\n if ('error' in sizeResult) {\n if (args.json) emitJson({ ok: false, reason: 'invalid-size', message: sizeResult.error });\n else process.stderr.write(`${sizeResult.error}\\n`);\n return exitAfterFlush(ExitCode.Usage);\n }\n\n const ctx = await resolveAppOrFail({\n json: args.json,\n appIdRaw: args.id,\n ...(args.workspace !== undefined ? { workspace: args.workspace } : {}),\n });\n if (!ctx) return;\n const appId = await requireMiniAppId(ctx, args.json);\n if (appId === null) return;\n printContextHeader(ctx, { json: args.json });\n const { session, workspaceId } = ctx;\n\n try {\n const result = await fetchAppEventCatalogs(\n {\n workspaceId,\n miniAppId: appId,\n pageNumber: pageResult.value,\n pageSize: sizeResult.value,\n ...(args.search !== undefined ? { search: String(args.search) } : {}),\n ...(args.refresh ? { refresh: true } : {}),\n },\n session.cookies,\n );\n if (args.json) {\n emitJson({\n ok: true,\n workspaceId,\n appId,\n events: result.results,\n cacheTime: result.cacheTime ?? null,\n paging: result.paging,\n });\n return exitAfterFlush(ExitCode.Ok);\n }\n if (result.results.length === 0) {\n const ct = result.cacheTime ? ` (cached ${result.cacheTime})` : '';\n process.stdout.write(`App ${appId} (ws ${workspaceId}): no event catalogs${ct}\\n`);\n return exitAfterFlush(ExitCode.Ok);\n }\n process.stdout.write(\n `App ${appId} (ws ${workspaceId}): ${result.results.length} event(s) on page ${result.paging.pageNumber} of ${result.paging.totalPages}\\n`,\n );\n for (const e of result.results) {\n const name =\n typeof e.name === 'string' ? e.name : typeof e.eventName === 'string' ? e.eventName : '-';\n const count =\n typeof e.count === 'number'\n ? String(e.count)\n : typeof e.totalCount === 'number'\n ? String(e.totalCount)\n : '-';\n process.stdout.write(`${name}\\t${count}\\n`);\n }\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\nconst eventsCommand = defineCommand({\n meta: {\n name: 'events',\n description: 'Inspect custom event catalogs (log search) for a mini-app.',\n },\n subCommands: {\n ls: eventsLsCommand,\n },\n});\n\nconst templatesLsCommand = defineCommand({\n meta: {\n name: 'ls',\n description:\n 'List the smart-message composer templates available for a mini-app (the 템플릿 picker in 스마트 발송).',\n },\n args: {\n id: {\n type: 'positional',\n description: 'Mini-app ID. Optional when `aitcc.yaml` provides `miniAppId`.',\n required: false,\n },\n workspace: {\n type: 'string',\n description: 'Workspace ID. Defaults to the selected workspace.',\n },\n page: { type: 'string', description: 'Page number (0-indexed).', default: '0' },\n size: { type: 'string', description: 'Page size.', default: '20' },\n 'content-reach-type': {\n type: 'string',\n description: `Template reach bucket: ${TEMPLATE_CONTENT_REACH_TYPES.join(' | ')}. Omit for all.`,\n },\n 'smart-message': {\n type: 'string',\n description:\n 'Filter to templates compatible with smart-message (\"true\") or legacy push (\"false\"). Omit for all.',\n },\n json: { type: 'boolean', description: 'Emit machine-readable JSON.', default: false },\n },\n async run({ args }) {\n const pageResult = parseNonNegativeInt(String(args.page), 'page');\n if ('error' in pageResult) {\n if (args.json) emitJson({ ok: false, reason: 'invalid-page', message: pageResult.error });\n else process.stderr.write(`${pageResult.error}\\n`);\n return exitAfterFlush(ExitCode.Usage);\n }\n const sizeResult = parseNonNegativeInt(String(args.size), 'size');\n if ('error' in sizeResult) {\n if (args.json) emitJson({ ok: false, reason: 'invalid-size', message: sizeResult.error });\n else process.stderr.write(`${sizeResult.error}\\n`);\n return exitAfterFlush(ExitCode.Usage);\n }\n\n let contentReachType: TemplateContentReachType | undefined;\n if (args['content-reach-type'] !== undefined) {\n const upper = String(args['content-reach-type']).toUpperCase();\n if ((TEMPLATE_CONTENT_REACH_TYPES as readonly string[]).includes(upper)) {\n contentReachType = upper as TemplateContentReachType;\n } else {\n const message = `--content-reach-type must be one of: ${TEMPLATE_CONTENT_REACH_TYPES.join(', ')}`;\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'invalid-content-reach-type',\n allowed: [...TEMPLATE_CONTENT_REACH_TYPES],\n });\n } else {\n process.stderr.write(`${message}\\n`);\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n }\n\n let isSmartMessage: boolean | undefined;\n if (args['smart-message'] !== undefined) {\n const raw = String(args['smart-message']).toLowerCase();\n if (raw === 'true') isSmartMessage = true;\n else if (raw === 'false') isSmartMessage = false;\n else {\n const message = '--smart-message must be \"true\" or \"false\"';\n if (args.json) emitJson({ ok: false, reason: 'invalid-smart-message', message });\n else process.stderr.write(`${message}\\n`);\n return exitAfterFlush(ExitCode.Usage);\n }\n }\n\n const ctx = await resolveAppOrFail({\n json: args.json,\n appIdRaw: args.id,\n ...(args.workspace !== undefined ? { workspace: args.workspace } : {}),\n });\n if (!ctx) return;\n const appId = await requireMiniAppId(ctx, args.json);\n if (appId === null) return;\n printContextHeader(ctx, { json: args.json });\n const { session, workspaceId } = ctx;\n\n try {\n const result = await fetchAppTemplates(\n {\n workspaceId,\n miniAppId: appId,\n page: pageResult.value,\n size: sizeResult.value,\n ...(contentReachType !== undefined ? { contentReachType } : {}),\n ...(isSmartMessage !== undefined ? { isSmartMessage } : {}),\n },\n session.cookies,\n );\n if (args.json) {\n emitJson({\n ok: true,\n workspaceId,\n appId,\n templates: result.templates,\n totalPageCount: result.totalPageCount,\n });\n return exitAfterFlush(ExitCode.Ok);\n }\n if (result.templates.length === 0) {\n process.stdout.write(`App ${appId} (ws ${workspaceId}): no templates\\n`);\n return exitAfterFlush(ExitCode.Ok);\n }\n process.stdout.write(\n `App ${appId} (ws ${workspaceId}): ${result.templates.length} template(s) of ${result.totalPageCount} page(s)\\n`,\n );\n for (const t of result.templates) {\n const id =\n typeof t.id === 'string' || typeof t.id === 'number'\n ? t.id\n : typeof t.templateId === 'string' || typeof t.templateId === 'number'\n ? t.templateId\n : '-';\n const title =\n typeof t.title === 'string' ? t.title : typeof t.name === 'string' ? t.name : '-';\n const type = typeof t.templateType === 'string' ? t.templateType : '-';\n process.stdout.write(`${id}\\t${title}\\t${type}\\n`);\n }\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\nconst templatesCommand = defineCommand({\n meta: {\n name: 'templates',\n description: 'Inspect smart-message composer templates available for a mini-app.',\n },\n subCommands: {\n ls: templatesLsCommand,\n },\n});\n\nconst categoriesCommand = defineCommand({\n meta: {\n name: 'categories',\n description: \"List the impression category tree used by `app register`'s `categoryIds` field.\",\n },\n args: {\n selectable: {\n type: 'boolean',\n description: 'Only show categories flagged `isSelectable: true` — the ones you can pick.',\n default: false,\n },\n json: { type: 'boolean', description: 'Emit machine-readable JSON to stdout.', default: false },\n },\n async run({ args }) {\n const session = await readSession();\n if (!session) {\n emitNotAuthenticated(args.json);\n return exitAfterFlush(ExitCode.NotAuthenticated);\n }\n\n try {\n const tree = await fetchImpressionCategoryList(session.cookies);\n\n // Optional filter collapses non-selectable groups and categories. Kept\n // off by default because the flag `isSelectable: false` entries still\n // show up in live app payloads (eg. `금융` group) and are useful for\n // agents debugging a rejected registration.\n const filtered = args.selectable\n ? tree\n .filter((g) => g.categoryGroup.isSelectable)\n .map((g) => ({\n ...g,\n categoryList: g.categoryList\n .filter((c) => c.isSelectable)\n .map((c) => ({\n ...c,\n subCategoryList: c.subCategoryList.filter((s) => s.isSelectable),\n })),\n }))\n : tree;\n\n if (args.json) {\n emitJson({ ok: true, categories: filtered });\n return exitAfterFlush(ExitCode.Ok);\n }\n for (const g of filtered) {\n const mark = g.categoryGroup.isSelectable ? '' : ' (not selectable)';\n process.stdout.write(`[${g.categoryGroup.id}] ${g.categoryGroup.name}${mark}\\n`);\n for (const c of g.categoryList) {\n const cmark = c.isSelectable ? '' : ' (not selectable)';\n process.stdout.write(` ${c.id}\\t${c.name}${cmark}\\n`);\n for (const s of c.subCategoryList) {\n const smark = s.isSelectable ? '' : ' (not selectable)';\n process.stdout.write(` ${s.id}\\t${s.name}${smark}\\n`);\n }\n }\n }\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\nconst serviceStatusCommand = defineCommand({\n meta: {\n name: 'service-status',\n description:\n 'Show the server-authoritative runtime status of a mini-app (serviceStatus, shutdown schedule).',\n },\n args: {\n id: {\n type: 'positional',\n description: 'Mini-app ID. Optional when `aitcc.yaml` provides `miniAppId`.',\n required: false,\n },\n workspace: {\n type: 'string',\n description: 'Workspace ID. Defaults to the selected workspace.',\n },\n json: { type: 'boolean', description: 'Emit machine-readable JSON.', default: false },\n },\n async run({ args }) {\n const ctx = await resolveAppOrFail({\n json: args.json,\n appIdRaw: args.id,\n ...(args.workspace !== undefined ? { workspace: args.workspace } : {}),\n });\n if (!ctx) return;\n const appId = await requireMiniAppId(ctx, args.json);\n if (appId === null) return;\n printContextHeader(ctx, { json: args.json });\n const { session, workspaceId } = ctx;\n\n try {\n const st = await fetchAppServiceStatus(workspaceId, appId, session.cookies);\n if (args.json) {\n emitJson({ ok: true, workspaceId, appId, ...st });\n return exitAfterFlush(ExitCode.Ok);\n }\n process.stdout.write(`App ${appId} (ws ${workspaceId}):\\n`);\n process.stdout.write(` serviceStatus: ${st.serviceStatus}\\n`);\n process.stdout.write(` shutdownCandidateStatus: ${st.shutdownCandidateStatus ?? 'null'}\\n`);\n process.stdout.write(` scheduledShutdownAt: ${st.scheduledShutdownAt ?? 'null'}\\n`);\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\nconst initCommand = defineCommand({\n meta: {\n name: 'init',\n description:\n 'Scaffold a well-formed `aitcc.yaml` interactively. Asks for the required ' +\n 'manifest fields, picks the workspace from the live API list, and lays the ' +\n 'optional fields as commented lines for later edits.',\n },\n args: {\n cwd: {\n type: 'string',\n description: 'Directory to write `aitcc.yaml` into (default: current directory).',\n },\n force: {\n type: 'boolean',\n description: 'Overwrite an existing project file instead of erroring.',\n default: false,\n },\n json: {\n type: 'boolean',\n description: 'Emit machine-readable JSON to stdout.',\n default: false,\n },\n },\n async run({ args }) {\n await runAppInit({\n ...(args.cwd !== undefined ? { cwd: args.cwd as string } : {}),\n force: args.force,\n json: args.json,\n });\n },\n});\n\nconst registerCommand = defineCommand({\n meta: {\n name: 'register',\n description:\n 'Register a mini-app in the selected workspace from a YAML/JSON manifest. ' +\n 'Uploads logo/thumbnail/screenshots, then submits the create payload.',\n },\n args: {\n workspace: {\n type: 'string',\n description: 'Workspace ID. Defaults to the selected workspace (`aitcc workspace use`).',\n },\n config: {\n type: 'string',\n description: 'Path to the app manifest. Defaults to `./aitcc.yaml`, then `./aitcc.json`.',\n },\n 'dry-run': {\n type: 'boolean',\n description: 'Validate manifest + images and print the inferred submit payload; no uploads.',\n default: false,\n },\n 'accept-terms': {\n type: 'boolean',\n description:\n 'Attest to the required console legal-agreement checkboxes (see VALIDATION-RULES.md). Required for real submits.',\n default: false,\n },\n json: { type: 'boolean', description: 'Emit machine-readable JSON to stdout.', default: false },\n },\n async run({ args }) {\n await runRegister({\n json: args.json,\n dryRun: args['dry-run'],\n acceptTerms: args['accept-terms'],\n ...(args.workspace !== undefined ? { workspace: args.workspace } : {}),\n ...(args.config !== undefined ? { config: args.config } : {}),\n });\n },\n});\n\n// --json contract (consumed by agent-plugin):\n//\n// app deploy <path> --app <id> [--deployment-id <uuid>] [--memo <text>]\n// [--request-review --release-notes <text>]\n// [--release --confirm] [--workspace <id>]\n// [--dry-run] [--json]:\n//\n// One-shot wrapper over `app bundles upload|review|release`. Always\n// performs the 3-step upload; downstream review/release steps run only\n// when their opt-in flags are passed. See `runDeploy` for the full\n// success/failure shape — this citty wrapper is just an argument shim.\n//\n// deploymentId auto-detect: if `--deployment-id` is omitted, the .ait is\n// cracked to read `_metadata.deploymentId` from `app.json`. This is the\n// primary convenience the wrapper adds over the three-command dance.\nconst deployCommand = defineCommand({\n meta: {\n name: 'deploy',\n description:\n 'Upload a bundle, optionally request review, optionally release. ' +\n 'Auto-detects deploymentId from the .ait if --deployment-id is omitted.',\n },\n args: {\n path: { type: 'positional', description: 'Path to the .ait bundle file.', required: true },\n app: {\n type: 'string',\n description:\n 'Mini-app ID. Optional when AITCC_APP env or `miniAppId` in aitcc.yaml supplies one.',\n },\n 'deployment-id': {\n type: 'string',\n description:\n 'deploymentId of the bundle. Defaults to app.json._metadata.deploymentId inside the .ait.',\n },\n memo: { type: 'string', description: 'Optional memo attached to the uploaded bundle.' },\n 'request-review': {\n type: 'boolean',\n description: 'After upload, submit the bundle for review.',\n default: false,\n },\n 'release-notes': {\n type: 'string',\n description: 'Release notes for the review request. Required with --request-review.',\n },\n release: {\n type: 'boolean',\n description:\n 'After review submit, publish the bundle. Requires the bundle to already be APPROVED; ' +\n 'typically used on a second `app deploy` run.',\n default: false,\n },\n confirm: {\n type: 'boolean',\n description: 'Required with --release — confirms the destructive publish step.',\n default: false,\n },\n workspace: {\n type: 'string',\n description: 'Workspace ID. Defaults to the selected workspace.',\n },\n 'dry-run': {\n type: 'boolean',\n description: 'Print the planned pipeline without touching the server.',\n default: false,\n },\n json: { type: 'boolean', description: 'Emit machine-readable JSON.', default: false },\n },\n async run({ args }) {\n await runDeploy({\n path: typeof args.path === 'string' ? args.path : '',\n app: typeof args.app === 'string' ? args.app : undefined,\n json: args.json,\n dryRun: args['dry-run'],\n requestReview: args['request-review'],\n release: args.release,\n confirm: args.confirm,\n ...(args['deployment-id'] !== undefined\n ? { deploymentId: args['deployment-id'] as string }\n : {}),\n ...(args.memo !== undefined ? { memo: args.memo as string } : {}),\n ...(args['release-notes'] !== undefined\n ? { releaseNotes: args['release-notes'] as string }\n : {}),\n ...(args.workspace !== undefined ? { workspace: args.workspace as string } : {}),\n });\n },\n});\n\nexport const appCommand = defineCommand({\n meta: {\n name: 'app',\n description: 'Inspect mini-apps in a workspace.',\n },\n subCommands: {\n init: initCommand,\n ls: lsCommand,\n show: showCommand,\n status: statusCommand,\n ratings: ratingsCommand,\n reports: reportsCommand,\n bundles: bundlesCommand,\n certs: certsCommand,\n metrics: metricsCommand,\n 'share-rewards': shareRewardsCommand,\n messages: messagesCommand,\n events: eventsCommand,\n templates: templatesCommand,\n categories: categoriesCommand,\n 'service-status': serviceStatusCommand,\n register: registerCommand,\n deploy: deployCommand,\n },\n});\n","import { spawn } from 'node:child_process';\n\n// Service identifier used as the keychain \"service\" / Windows target name\n// prefix / libsecret schema attribute. Stable across versions — changing it\n// would orphan existing entries on user machines.\nexport const CREDENTIAL_SERVICE = 'aitcc.credentials';\n\nexport interface CredentialBackend {\n readonly name: string;\n get(account: string): Promise<string | null>;\n set(account: string, password: string): Promise<void>;\n clear(account: string): Promise<{ existed: boolean }>;\n}\n\nexport class CredentialBackendUnsupportedError extends Error {\n constructor(\n readonly platform: NodeJS.Platform,\n readonly hint: string,\n ) {\n super(`No supported credential backend for platform \"${platform}\". ${hint}`);\n this.name = 'CredentialBackendUnsupportedError';\n }\n}\n\nexport class CredentialBackendCommandError extends Error {\n constructor(\n readonly command: string,\n readonly exitCode: number | null,\n // Pass stderr through `redactStderr` before constructing — that helper\n // bounds the length so a runaway stderr can't blow up logs, but does\n // NOT scrub argv-echoed secrets. macOS `security` and Linux\n // `secret-tool` don't echo argv on error in observed failure paths;\n // Linux uses stdin so its argv is clean to begin with. If a backend\n // ever starts echoing, tighten this here.\n readonly redactedStderr: string,\n ) {\n super(\n `Credential backend command \"${command}\" failed (exit=${exitCode ?? 'null'}): ${redactedStderr}`,\n );\n this.name = 'CredentialBackendCommandError';\n }\n}\n\nexport interface RunOpts {\n readonly args: readonly string[];\n readonly stdin?: string;\n}\n\nexport interface RunResult {\n readonly exitCode: number | null;\n readonly stdout: string;\n readonly stderr: string;\n}\n\nexport async function runCommand(command: string, opts: RunOpts): Promise<RunResult> {\n return new Promise<RunResult>((resolve, reject) => {\n const child = spawn(command, [...opts.args], { stdio: ['pipe', 'pipe', 'pipe'] });\n let stdout = '';\n let stderr = '';\n child.stdout?.on('data', (c: Buffer) => {\n stdout += c.toString('utf8');\n });\n child.stderr?.on('data', (c: Buffer) => {\n stderr += c.toString('utf8');\n });\n child.on('error', (err) => reject(err));\n child.on('close', (code) => {\n resolve({ exitCode: code, stdout, stderr });\n });\n if (opts.stdin !== undefined) {\n child.stdin?.write(opts.stdin);\n }\n child.stdin?.end();\n });\n}\n\nexport function isCommandNotFound(err: unknown): boolean {\n return (err as NodeJS.ErrnoException).code === 'ENOENT';\n}\n\nexport function stripTrailingNewline(s: string): string {\n return s.replace(/\\r?\\n$/, '');\n}\n\n// Backend command failures land in user-facing logs. macOS `security` and\n// Linux `secret-tool` don't echo argv on error in our smoke tests, but a\n// kernel-level oddity (e.g. failing exec) can make stderr unbounded.\n// Truncate aggressively + drop anything that looks remotely password-ish.\nexport function redactStderr(stderr: string): string {\n const trimmed = stderr.trim();\n if (trimmed.length === 0) return '<no stderr>';\n if (trimmed.length > 200) return `${trimmed.slice(0, 200)}… <truncated>`;\n return trimmed;\n}\n","import {\n CREDENTIAL_SERVICE,\n type CredentialBackend,\n CredentialBackendCommandError,\n CredentialBackendUnsupportedError,\n isCommandNotFound,\n type RunResult,\n redactStderr,\n runCommand,\n stripTrailingNewline,\n} from '../backend.js';\n\nconst MISSING_HINT_FULL =\n 'libsecret tools are missing. Install `libsecret-tools` (Debian/Ubuntu) or the equivalent and ensure a Secret Service provider (gnome-keyring / KWallet) is running.';\nconst MISSING_HINT_SHORT =\n 'libsecret tools are missing. Install `libsecret-tools` and ensure a Secret Service provider is running.';\n\nexport const LINUX_BACKEND: CredentialBackend = {\n name: 'libsecret',\n async get(account) {\n let result: RunResult;\n try {\n result = await runCommand('secret-tool', {\n args: ['lookup', 'service', CREDENTIAL_SERVICE, 'account', account],\n });\n } catch (err) {\n if (isCommandNotFound(err)) {\n throw new CredentialBackendUnsupportedError('linux', MISSING_HINT_FULL);\n }\n throw err;\n }\n // `secret-tool lookup` exits 0 with no stdout when the entry is\n // missing on some distros, non-zero on others. Empty stdout = missing\n // either way. We check exit code first so a failed lookup that wrote\n // partial bytes to stdout never gets returned as the \"password\".\n if (result.exitCode !== 0) {\n if (result.stdout.length === 0) return null;\n throw new CredentialBackendCommandError(\n 'secret-tool lookup',\n result.exitCode,\n redactStderr(result.stderr),\n );\n }\n if (result.stdout.length === 0) return null;\n const password = stripTrailingNewline(result.stdout);\n return password.length > 0 ? password : null;\n },\n async set(account, password) {\n let result: RunResult;\n try {\n // `store` reads the secret from stdin — keeps argv clean.\n result = await runCommand('secret-tool', {\n args: [\n 'store',\n '--label',\n 'aitcc Toss Business credentials',\n 'service',\n CREDENTIAL_SERVICE,\n 'account',\n account,\n ],\n stdin: password,\n });\n } catch (err) {\n if (isCommandNotFound(err)) {\n throw new CredentialBackendUnsupportedError('linux', MISSING_HINT_SHORT);\n }\n throw err;\n }\n if (result.exitCode !== 0) {\n throw new CredentialBackendCommandError(\n 'secret-tool store',\n result.exitCode,\n redactStderr(result.stderr),\n );\n }\n },\n async clear(account) {\n let result: RunResult;\n try {\n result = await runCommand('secret-tool', {\n args: ['clear', 'service', CREDENTIAL_SERVICE, 'account', account],\n });\n } catch (err) {\n if (isCommandNotFound(err)) {\n throw new CredentialBackendUnsupportedError('linux', 'libsecret tools are missing.');\n }\n throw err;\n }\n // `secret-tool clear` always exits 0 even when the entry was absent.\n // Probe with `lookup` after to know whether something existed; cheaper\n // alternative is just to report `existed: true` optimistically, since\n // the user-facing impact is \"credentials are gone now\" either way.\n if (result.exitCode !== 0) {\n throw new CredentialBackendCommandError(\n 'secret-tool clear',\n result.exitCode,\n redactStderr(result.stderr),\n );\n }\n return { existed: true };\n },\n};\n","import {\n CREDENTIAL_SERVICE,\n type CredentialBackend,\n CredentialBackendCommandError,\n CredentialBackendUnsupportedError,\n isCommandNotFound,\n type RunResult,\n redactStderr,\n runCommand,\n stripTrailingNewline,\n} from '../backend.js';\n\nconst MISSING_HINT = 'macOS `security` is missing from PATH.';\n\nexport const MACOS_BACKEND: CredentialBackend = {\n name: 'macos-keychain',\n async get(account) {\n let result: RunResult;\n try {\n result = await runCommand('security', {\n args: ['find-generic-password', '-s', CREDENTIAL_SERVICE, '-a', account, '-w'],\n });\n } catch (err) {\n if (isCommandNotFound(err)) {\n throw new CredentialBackendUnsupportedError('darwin', MISSING_HINT);\n }\n throw err;\n }\n if (result.exitCode === 44) return null; // errSecItemNotFound\n if (result.exitCode !== 0) return null; // be lenient on `find` — assume missing\n const password = stripTrailingNewline(result.stdout);\n return password.length > 0 ? password : null;\n },\n async set(account, password) {\n let result: RunResult;\n try {\n // -U upserts. -A opens the ACL so subsequent `find-generic-password`\n // reads do not raise the keychain unlock prompt every call (saving\n // one prompt per `aitcc` run, including the read inside the\n // unchanged-detection path of `saveCredentials`). The trade-off is a\n // permissive ACL: any process running as the same user can read the\n // entry. We accept this for the same reason we accept argv-visible\n // passwords on `security` — the threat model is a single-user\n // machine and the OS keychain is no stronger than the login\n // session. `security` reads the password from `-w`; no stdin path\n // exists on this command.\n result = await runCommand('security', {\n args: [\n 'add-generic-password',\n '-U',\n '-A',\n '-s',\n CREDENTIAL_SERVICE,\n '-a',\n account,\n '-w',\n password,\n ],\n });\n } catch (err) {\n if (isCommandNotFound(err)) {\n throw new CredentialBackendUnsupportedError('darwin', MISSING_HINT);\n }\n throw err;\n }\n if (result.exitCode !== 0) {\n throw new CredentialBackendCommandError(\n 'security add-generic-password',\n result.exitCode,\n redactStderr(result.stderr),\n );\n }\n },\n async clear(account) {\n let result: RunResult;\n try {\n result = await runCommand('security', {\n args: ['delete-generic-password', '-s', CREDENTIAL_SERVICE, '-a', account],\n });\n } catch (err) {\n if (isCommandNotFound(err)) {\n throw new CredentialBackendUnsupportedError('darwin', MISSING_HINT);\n }\n throw err;\n }\n if (result.exitCode === 44) return { existed: false };\n if (result.exitCode === 0) return { existed: true };\n throw new CredentialBackendCommandError(\n 'security delete-generic-password',\n result.exitCode,\n redactStderr(result.stderr),\n );\n },\n};\n","import {\n CREDENTIAL_SERVICE,\n type CredentialBackend,\n CredentialBackendCommandError,\n CredentialBackendUnsupportedError,\n isCommandNotFound,\n type RunResult,\n redactStderr,\n runCommand,\n} from '../backend.js';\n\n// Stock Windows ships PowerShell which can call the CredentialManager API\n// via P/Invoke. No extra modules to install. The password round-trips as\n// hex bytes through the script body so a `ps` listing shows hex, not\n// cleartext.\n\nconst PS_HEADER = `\n$ErrorActionPreference = 'Stop';\nAdd-Type @\"\nusing System;\nusing System.Runtime.InteropServices;\n\npublic class AitccCredApi {\n [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]\n public struct CREDENTIAL {\n public uint Flags;\n public uint Type;\n public IntPtr TargetName;\n public IntPtr Comment;\n public System.Runtime.InteropServices.ComTypes.FILETIME LastWritten;\n public uint CredentialBlobSize;\n public IntPtr CredentialBlob;\n public uint Persist;\n public uint AttributeCount;\n public IntPtr Attributes;\n public IntPtr TargetAlias;\n public IntPtr UserName;\n }\n [DllImport(\"Advapi32.dll\", SetLastError = true, EntryPoint = \"CredWriteW\", CharSet = CharSet.Unicode)]\n public static extern bool CredWrite([In] ref CREDENTIAL Credential, [In] uint Flags);\n [DllImport(\"Advapi32.dll\", SetLastError = true, EntryPoint = \"CredReadW\", CharSet = CharSet.Unicode)]\n public static extern bool CredRead(string target, uint type, uint reservedFlag, out IntPtr CredentialPtr);\n [DllImport(\"Advapi32.dll\", SetLastError = true, EntryPoint = \"CredDeleteW\", CharSet = CharSet.Unicode)]\n public static extern bool CredDelete(string target, uint type, uint flags);\n [DllImport(\"Advapi32.dll\", SetLastError = true, EntryPoint = \"CredFree\")]\n public static extern void CredFree([In] IntPtr cred);\n}\n\"@\n`;\n\nconst MISSING_HINT_FULL =\n '`powershell.exe` is missing from PATH. Windows credential storage requires PowerShell.';\nconst MISSING_HINT_SHORT = '`powershell.exe` is missing from PATH.';\n\nfunction targetName(account: string): string {\n return `${CREDENTIAL_SERVICE}/${account}`;\n}\n\nfunction powerShellArgs(script: string): readonly string[] {\n return ['-NoProfile', '-NonInteractive', '-Command', script];\n}\n\nfunction escapeSingleQuotes(s: string): string {\n return s.replace(/'/g, \"''\");\n}\n\nasync function runPowerShell(script: string): Promise<RunResult> {\n try {\n return await runCommand('powershell.exe', { args: powerShellArgs(script) });\n } catch (err) {\n if (isCommandNotFound(err)) {\n throw new CredentialBackendUnsupportedError('win32', MISSING_HINT_FULL);\n }\n throw err;\n }\n}\n\nexport const WINDOWS_BACKEND: CredentialBackend = {\n name: 'windows-credential-manager',\n async get(account) {\n const target = targetName(account);\n const script = `\n${PS_HEADER}\n$target = '${escapeSingleQuotes(target)}';\n$ptr = [IntPtr]::Zero;\n$ok = [AitccCredApi]::CredRead($target, 1, 0, [ref]$ptr);\nif (-not $ok) { exit 0; }\n$cred = [Runtime.InteropServices.Marshal]::PtrToStructure($ptr, [Type][AitccCredApi+CREDENTIAL]);\n$blob = New-Object byte[] $cred.CredentialBlobSize;\n[Runtime.InteropServices.Marshal]::Copy($cred.CredentialBlob, $blob, 0, $cred.CredentialBlobSize);\n$pw = [System.Text.Encoding]::Unicode.GetString($blob);\n[AitccCredApi]::CredFree($ptr);\n[Console]::Out.Write($pw);\n`;\n const result = await runPowerShell(script);\n if (result.exitCode !== 0) {\n throw new CredentialBackendCommandError(\n 'powershell CredRead',\n result.exitCode,\n redactStderr(result.stderr),\n );\n }\n return result.stdout.length > 0 ? result.stdout : null;\n },\n async set(account, password) {\n const target = targetName(account);\n // Encode the password as hex so PowerShell's argv (visible in Task\n // Manager) shows hex, not cleartext. `account` is the email; it's\n // intentionally cleartext on argv since the email is not secret.\n const passwordHex = Buffer.from(password, 'utf8').toString('hex');\n const script = `\n${PS_HEADER}\n$target = '${escapeSingleQuotes(target)}';\n$user = '${escapeSingleQuotes(account)}';\n$pwHex = '${passwordHex}';\n$pwBytes = New-Object byte[] ($pwHex.Length / 2);\nfor ($i = 0; $i -lt $pwBytes.Length; $i++) {\n $pwBytes[$i] = [Convert]::ToByte($pwHex.Substring($i * 2, 2), 16);\n}\n$pwUtf16 = [System.Text.Encoding]::Unicode.GetBytes([System.Text.Encoding]::UTF8.GetString($pwBytes));\n$cred = New-Object AitccCredApi+CREDENTIAL;\n$cred.Type = 1;\n$cred.TargetName = [Runtime.InteropServices.Marshal]::StringToHGlobalUni($target);\n$cred.CredentialBlobSize = [uint32]$pwUtf16.Length;\n$cred.CredentialBlob = [Runtime.InteropServices.Marshal]::AllocHGlobal($pwUtf16.Length);\n[Runtime.InteropServices.Marshal]::Copy($pwUtf16, 0, $cred.CredentialBlob, $pwUtf16.Length);\n$cred.Persist = 2;\n$cred.UserName = [Runtime.InteropServices.Marshal]::StringToHGlobalUni($user);\ntry {\n $ok = [AitccCredApi]::CredWrite([ref]$cred, 0);\n if (-not $ok) { Write-Error 'CredWrite failed'; exit 1; }\n} finally {\n [Runtime.InteropServices.Marshal]::FreeHGlobal($cred.TargetName);\n [Runtime.InteropServices.Marshal]::FreeHGlobal($cred.UserName);\n [Runtime.InteropServices.Marshal]::FreeHGlobal($cred.CredentialBlob);\n}\n`;\n let result: RunResult;\n try {\n result = await runPowerShell(script);\n } catch (err) {\n if (err instanceof CredentialBackendUnsupportedError) {\n throw new CredentialBackendUnsupportedError('win32', MISSING_HINT_SHORT);\n }\n throw err;\n }\n if (result.exitCode !== 0) {\n throw new CredentialBackendCommandError(\n 'powershell CredWrite',\n result.exitCode,\n redactStderr(result.stderr),\n );\n }\n },\n async clear(account) {\n const target = targetName(account);\n const script = `\n${PS_HEADER}\n$target = '${escapeSingleQuotes(target)}';\n$ok = [AitccCredApi]::CredDelete($target, 1, 0);\nif ($ok) { [Console]::Out.Write('deleted'); } else { [Console]::Out.Write('absent'); }\n`;\n let result: RunResult;\n try {\n result = await runPowerShell(script);\n } catch (err) {\n if (err instanceof CredentialBackendUnsupportedError) {\n throw new CredentialBackendUnsupportedError('win32', MISSING_HINT_SHORT);\n }\n throw err;\n }\n if (result.exitCode !== 0) {\n throw new CredentialBackendCommandError(\n 'powershell CredDelete',\n result.exitCode,\n redactStderr(result.stderr),\n );\n }\n return { existed: result.stdout.includes('deleted') };\n },\n};\n","import { chmod, mkdir, readFile, unlink, writeFile } from 'node:fs/promises';\nimport { dirname } from 'node:path';\nimport { authStateFilePath } from '../paths.js';\nimport { type CredentialBackend, CredentialBackendUnsupportedError } from './backend.js';\nimport { LINUX_BACKEND } from './backends/linux.js';\nimport { MACOS_BACKEND } from './backends/macos.js';\nimport { WINDOWS_BACKEND } from './backends/windows.js';\n\n// Toss Business email + password persisted across two layers so a future\n// `aitcc login` can drive the sign-in form headlessly:\n// - the password lives in the OS keychain, keyed by `service=SERVICE,\n// account=<email>`. The keychain is the only place the secret ever\n// touches disk.\n// - the active email is mirrored to `auth-state.json` (0600) so we can\n// look up the keychain entry without the user re-typing the address\n// every time.\n//\n// `loadCredentials()` first checks env vars (`AITCC_EMAIL` +\n// `AITCC_PASSWORD`) so CI runs can inject single-shot credentials without\n// touching the keychain. The returned discriminated union tells callers\n// which source they got.\n//\n// SECURITY MODEL\n// - Single-user machine assumption. The native tools (`security`,\n// `secret-tool`, PowerShell + CredWrite) accept the password on argv on\n// macOS and Windows, briefly visible in `ps`/Task Manager to other\n// processes running as the same user. That's the OS tool's own limit;\n// we surface it in user-facing copy and don't pretend to defend\n// against an attacker already running as the user.\n// - Linux uses `secret-tool` which streams the password on stdin; argv\n// stays clean.\n// - This module never logs or prints passwords. Errors include backend\n// exit codes / stderr only — they must NOT include credential values.\n\nexport {\n CREDENTIAL_SERVICE,\n type CredentialBackend,\n CredentialBackendCommandError,\n CredentialBackendUnsupportedError,\n} from './backend.js';\n\nexport interface Credentials {\n readonly email: string;\n readonly password: string;\n}\n\nexport type CredentialsSource =\n | { readonly kind: 'env'; readonly email: string; readonly password: string }\n | { readonly kind: 'keychain'; readonly email: string; readonly password: string };\n// 'file' fallback (~/.config/aitcc/credentials.json) — TODO follow-up; not\n// implemented in PR α. Add a third variant when wired so callers can\n// distinguish at the type level.\n\n// --- Backend dispatch ---\n\nexport interface ResolveBackendOptions {\n readonly platform?: NodeJS.Platform;\n // Test seam — bypass platform detection.\n readonly override?: CredentialBackend;\n}\n\nexport function resolveBackend(opts: ResolveBackendOptions = {}): CredentialBackend {\n if (opts.override) return opts.override;\n const platform = opts.platform ?? process.platform;\n switch (platform) {\n case 'darwin':\n return MACOS_BACKEND;\n case 'linux':\n return LINUX_BACKEND;\n case 'win32':\n return WINDOWS_BACKEND;\n default:\n throw new CredentialBackendUnsupportedError(\n platform,\n 'Only macOS, Linux (libsecret), and Windows are supported.',\n );\n }\n}\n\n// --- Auth state (active email pointer) ---\n\ninterface AuthState {\n readonly schemaVersion: 1;\n readonly activeEmail: string;\n}\n\nasync function readAuthState(): Promise<AuthState | null> {\n let raw: string;\n try {\n raw = await readFile(authStateFilePath(), 'utf8');\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return null;\n throw err;\n }\n try {\n const parsed = JSON.parse(raw) as Partial<AuthState>;\n if (parsed.schemaVersion !== 1) return null;\n if (typeof parsed.activeEmail !== 'string' || parsed.activeEmail.length === 0) return null;\n return { schemaVersion: 1, activeEmail: parsed.activeEmail };\n } catch {\n return null;\n }\n}\n\nasync function writeAuthState(state: AuthState): Promise<void> {\n const path = authStateFilePath();\n await mkdir(dirname(path), { recursive: true, mode: 0o700 });\n await writeFile(path, JSON.stringify(state, null, 2), { mode: 0o600 });\n try {\n await chmod(path, 0o600);\n } catch {\n // Windows / exotic FS — best-effort.\n }\n}\n\nasync function clearAuthState(): Promise<{ existed: boolean }> {\n try {\n await unlink(authStateFilePath());\n return { existed: true };\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return { existed: false };\n throw err;\n }\n}\n\n// --- Public API ---\n\nexport interface LoadCredentialsOptions extends ResolveBackendOptions {\n readonly env?: NodeJS.ProcessEnv;\n}\n\n/**\n * Resolve credentials from the highest-priority source available:\n * 1. `AITCC_EMAIL` + `AITCC_PASSWORD` env vars (CI single-shot use).\n * 2. OS keychain entry whose email is recorded in `auth-state.json`.\n *\n * Returns `null` when no source is configured. The discriminated `kind`\n * lets callers (e.g. PR β's login flow) tell why a credential was found\n * without having to peek at process env themselves — useful for\n * \"auto-login from CI\" diagnostics.\n *\n * A future `'file'` source (~/.config/aitcc/credentials.json) is left as a\n * follow-up; once added, it slots between (1) and (2).\n */\nexport async function loadCredentials(\n opts: LoadCredentialsOptions = {},\n): Promise<CredentialsSource | null> {\n const env = opts.env ?? process.env;\n const envEmail = env.AITCC_EMAIL;\n const envPassword = env.AITCC_PASSWORD;\n if (envEmail && envPassword) {\n return { kind: 'env', email: envEmail, password: envPassword };\n }\n const state = await readAuthState();\n if (!state) return null;\n const password = await resolveBackend(opts).get(state.activeEmail);\n if (password === null) {\n // The pointer exists but the keychain entry is gone — partial state.\n // Treat as \"no credentials\" rather than fatal; callers can re-save.\n return null;\n }\n return { kind: 'keychain', email: state.activeEmail, password };\n}\n\nexport type SaveCredentialsStatus = 'created' | 'updated' | 'unchanged';\n\n/**\n * Persist credentials to the OS keychain and update the active-email\n * pointer. Returns `'unchanged'` (no keychain write) when the same email\n * + password is already stored — avoids triggering OS keychain prompts on\n * every call when the user re-runs `auth set` with the same input.\n */\nexport async function saveCredentials(\n email: string,\n password: string,\n opts: ResolveBackendOptions = {},\n): Promise<{ status: SaveCredentialsStatus }> {\n if (!email) throw new Error('email is required');\n if (!password) throw new Error('password is required');\n\n const backend = resolveBackend(opts);\n const previousState = await readAuthState();\n\n let status: SaveCredentialsStatus;\n if (previousState && previousState.activeEmail === email) {\n const existing = await backend.get(email);\n if (existing === password) {\n // Same email + same password already stored. No-op.\n return { status: 'unchanged' };\n }\n status = 'updated';\n } else {\n status = previousState ? 'updated' : 'created';\n }\n\n await backend.set(email, password);\n // If we are switching emails, the previous keychain entry would otherwise\n // dangle. Best-effort cleanup so the keychain reflects the active email.\n if (previousState && previousState.activeEmail !== email) {\n try {\n await backend.clear(previousState.activeEmail);\n } catch {\n // Old entry might already be gone or backend may flake — non-fatal.\n }\n }\n await writeAuthState({ schemaVersion: 1, activeEmail: email });\n return { status };\n}\n\n/**\n * Read just the active-email pointer without touching the OS keychain.\n * Useful for surfaces like `auth status --json` that want to report\n * whether credentials are configured without triggering a Touch ID /\n * libsecret prompt for the password.\n *\n * Returns the email and where it was found (`'env'` when\n * `AITCC_EMAIL` + `AITCC_PASSWORD` are present, `'keychain'` when the\n * `auth-state.json` pointer exists), or `null` when nothing is\n * configured.\n */\nexport async function getActiveCredentialEmail(\n opts: { readonly env?: NodeJS.ProcessEnv } = {},\n): Promise<{ kind: 'env' | 'keychain'; email: string } | null> {\n const env = opts.env ?? process.env;\n if (env.AITCC_EMAIL && env.AITCC_PASSWORD) {\n return { kind: 'env', email: env.AITCC_EMAIL };\n }\n const state = await readAuthState();\n if (!state) return null;\n return { kind: 'keychain', email: state.activeEmail };\n}\n\n/**\n * Remove the keychain entry and the auth-state pointer. Returns\n * `existed: true` if either side previously held data.\n */\nexport async function deleteCredentials(\n opts: ResolveBackendOptions = {},\n): Promise<{ existed: boolean }> {\n const state = await readAuthState();\n let backendExisted = false;\n if (state) {\n try {\n const result = await resolveBackend(opts).clear(state.activeEmail);\n backendExisted = result.existed;\n } catch (err) {\n if (err instanceof CredentialBackendUnsupportedError) {\n // No backend — auth-state alone is the only thing to clear.\n } else {\n throw err;\n }\n }\n }\n const stateResult = await clearAuthState();\n return { existed: backendExisted || stateResult.existed };\n}\n","// Single source of truth for the KR-only cookie warning shown on every\n// surface that emits or accepts an exported session blob.\n//\n// The console session cookies (`TBIZAUTH` and the rest) are country-bound\n// to KR residential IPs. The same blob succeeds from a Korean machine but\n// returns 401 / errorCode 4010 from non-KR egress (GHA-hosted runners,\n// most cloud providers' US/EU regions). Spike data + decision rationale:\n// docs/api/auth-session.md \"Cookie portability (실측)\".\n//\n// Keeping the wording in one place avoids drift between `auth export` /\n// `auth import` stderr, --json envelopes, --help blurbs, README, and docs.\n\nexport const KR_ONLY_WARNING_KEY = 'kr-only-cookies' as const;\n\nexport const KR_ONLY_WARNING_SHORT =\n 'console session cookies are KR-only — they fail with errorCode 4010 from non-KR IPs (e.g. GitHub-hosted runners).';\n\nexport const KR_ONLY_WARNING_LONG = `${KR_ONLY_WARNING_SHORT} See docs/api/auth-session.md.`;\n","import { defineCommand } from 'citty';\nimport {\n KR_ONLY_WARNING_KEY,\n KR_ONLY_WARNING_LONG,\n KR_ONLY_WARNING_SHORT,\n} from '../auth/kr-only-warning.js';\nimport { ExitCode } from '../exit.js';\nimport { exitAfterFlush } from '../flush.js';\nimport { readSession, type Session } from '../session.js';\nimport { emitJson } from './_shared.js';\n\n// `aitcc auth export` — dump the live session into a portable blob so it\n// can be moved to a KR-resident CI runner via secret manager. Symmetric\n// with `auth import`. Read path is `AITCC_SESSION` env var, handled in\n// `src/session.ts:readSessionFromEnv` (see CLAUDE.md \"Session storage\").\n//\n// --json contract (consumed by agent-plugin):\n//\n// { ok: true,\n// format: 'env'|'json',\n// payload: <string for env, object for json>,\n// warning: 'kr-only-cookies',\n// warningMessage: <string> } exit 0\n// { ok: true, authenticated: false } exit 10\n//\n// SECURITY: this is the ONE command that is allowed to emit cookie\n// material on stdout (it's the user's explicit intent). All other\n// surfaces redact. The shell-friendly `--format env` line is exactly\n// `AITCC_SESSION=<base64>\\n` so `eval $(...)` and `>> $GITHUB_ENV`\n// both work without quoting gymnastics.\n\nexport type AuthExportFormat = 'env' | 'json';\n\nexport interface AuthExportArgs {\n readonly json: boolean;\n readonly format: AuthExportFormat;\n readonly quiet: boolean;\n}\n\nexport interface AuthExportDeps {\n // Test seam: override session source. Production reads `readSession()`\n // which honours `AITCC_SESSION` precedence (re-exporting an env-loaded\n // session is an idempotent no-op by design).\n readonly readSession?: () => Promise<Session | null>;\n readonly stdoutIsTTY?: boolean;\n}\n\nexport async function runAuthExport(\n args: AuthExportArgs,\n deps: AuthExportDeps = {},\n): Promise<void> {\n const session = await (deps.readSession ?? readSession)();\n\n if (!session) {\n if (args.json) {\n emitJson({ ok: true, authenticated: false });\n } else {\n process.stderr.write('Not logged in. Run `aitcc login` to start a session.\\n');\n }\n return exitAfterFlush(ExitCode.NotAuthenticated);\n }\n\n const blobJson = JSON.stringify(session);\n\n // --json envelope is independent from --format. The agent-plugin always\n // shells out with --json; --format then picks how the inner `payload`\n // is shaped (one-line env string vs. raw object the user can stuff into\n // a secret manager).\n if (args.json) {\n const payload =\n args.format === 'env'\n ? `AITCC_SESSION=${Buffer.from(blobJson, 'utf8').toString('base64')}`\n : session;\n emitJson({\n ok: true,\n format: args.format,\n payload,\n warning: KR_ONLY_WARNING_KEY,\n warningMessage: KR_ONLY_WARNING_LONG,\n });\n return exitAfterFlush(ExitCode.Ok);\n }\n\n // Human + shell paths.\n const stdoutIsTTY = deps.stdoutIsTTY ?? Boolean(process.stdout.isTTY);\n\n if (args.format === 'env') {\n const base64 = Buffer.from(blobJson, 'utf8').toString('base64');\n // Exactly one line, single trailing newline — `eval` and `>> $GITHUB_ENV`\n // both depend on this. Resist the urge to add a header/comment.\n process.stdout.write(`AITCC_SESSION=${base64}\\n`);\n } else {\n // Raw shape pretty-printed so a human pasting into a secret manager\n // can sanity-check the email / capturedAt before storing.\n process.stdout.write(`${JSON.stringify(session, null, 2)}\\n`);\n }\n\n if (!args.quiet) {\n if (stdoutIsTTY) {\n process.stderr.write(\n 'Hint: stdout looks like a TTY — redirect to a file or pipe to your secret manager (e.g. `>> $GITHUB_ENV`).\\n',\n );\n }\n process.stderr.write(`warning: ${KR_ONLY_WARNING_SHORT}\\n`);\n process.stderr.write('See docs/api/auth-session.md for the full constraint.\\n');\n }\n\n return exitAfterFlush(ExitCode.Ok);\n}\n\nexport const authExportCommand = defineCommand({\n meta: {\n name: 'export',\n description:\n 'Dump the active session as a portable blob for CI. Note: KR-only — fails from non-KR IPs.',\n },\n args: {\n json: {\n type: 'boolean',\n description: 'Emit machine-readable JSON envelope to stdout.',\n default: false,\n },\n format: {\n type: 'string',\n description: 'Output shape: `env` (one-line AITCC_SESSION=...) or `json` (raw blob).',\n default: 'env',\n },\n quiet: {\n type: 'boolean',\n description: 'Silence the KR-only warning on stderr (the warning is non-fatal).',\n default: false,\n },\n },\n async run({ args }) {\n const format = parseFormat(args.format);\n if (format === null) {\n // Citty already coerces unknown strings; we still validate to give a\n // crisp `--json` error rather than emitting an env blob with a\n // garbage format label.\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'invalid-format',\n message: `unknown --format: ${String(args.format)} (expected 'env' or 'json')`,\n });\n } else {\n process.stderr.write(\n `Unknown --format: ${String(args.format)} (expected 'env' or 'json').\\n`,\n );\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n return runAuthExport({ json: args.json, format, quiet: args.quiet });\n },\n});\n\nfunction parseFormat(raw: unknown): AuthExportFormat | null {\n if (raw === 'env' || raw === 'json') return raw;\n return null;\n}\n","import { defineCommand } from 'citty';\nimport {\n KR_ONLY_WARNING_KEY,\n KR_ONLY_WARNING_LONG,\n KR_ONLY_WARNING_SHORT,\n} from '../auth/kr-only-warning.js';\nimport { ExitCode } from '../exit.js';\nimport { exitAfterFlush } from '../flush.js';\nimport {\n decodeSessionBlob,\n normalizeValidatedSession,\n readSession,\n type Session,\n sessionPathForDiagnostics,\n validateSessionBlob,\n type WriteSessionOptions,\n writeSession,\n} from '../session.js';\nimport { emitJson } from './_shared.js';\n\n// `aitcc auth import` — validate a portable session blob and write it\n// (mode 0600) to the local session file. Symmetric with `auth export`.\n//\n// Use cases (in order of frequency):\n// - Restore a session on a new desktop without redoing OAuth flow.\n// - Recover from a hand-edited session.json with `--dry-run` for shape\n// validation feedback before writing.\n// CI almost never needs this: in CI the recommended path is to set the\n// `AITCC_SESSION` env var directly (no file write, no 0600 negotiation\n// with shared runners).\n//\n// --json contract (consumed by agent-plugin):\n//\n// { ok: true, replaced: boolean, user: { id, email, displayName? },\n// dryRun?: true,\n// warning: 'kr-only-cookies', warningMessage: <string> } exit 0\n// { ok: false, reason: 'no-input', message } exit 2\n// { ok: false, reason: 'env-not-set', message } exit 2\n// { ok: false, reason: 'invalid-blob', detail } exit 2\n// { ok: false, reason: 'write-failed', message } exit 1\n\nexport interface AuthImportArgs {\n readonly json: boolean;\n readonly fromEnv: boolean;\n readonly dryRun: boolean;\n}\n\nexport interface AuthImportDeps {\n // Test seams.\n readonly readStdin?: () => Promise<string>;\n readonly env?: NodeJS.ProcessEnv;\n readonly stdinIsTTY?: boolean;\n readonly readExistingSession?: () => Promise<Session | null>;\n readonly writeSession?: (s: Session, opts?: WriteSessionOptions) => Promise<void>;\n}\n\nexport async function runAuthImport(\n args: AuthImportArgs,\n deps: AuthImportDeps = {},\n): Promise<void> {\n const env = deps.env ?? process.env;\n\n // Sourcing: explicit --from-env wins; otherwise stdin. We refuse to\n // prompt or block on a TTY-attached stdin — the import flow is meant\n // for piped use (`aitcc auth import < session.json`) or `--from-env`,\n // and a hung blocking read on a developer terminal would look broken.\n let raw: string;\n if (args.fromEnv) {\n const envValue = env.AITCC_SESSION;\n if (!envValue) {\n emitFailure(args.json, 'env-not-set', 'AITCC_SESSION is not set; export it first.');\n return exitAfterFlush(ExitCode.Usage);\n }\n raw = envValue;\n } else {\n const stdinIsTTY = deps.stdinIsTTY ?? Boolean(process.stdin.isTTY);\n if (stdinIsTTY) {\n emitFailure(\n args.json,\n 'no-input',\n 'No stdin pipe detected. Pipe a session JSON in or pass --from-env.',\n );\n return exitAfterFlush(ExitCode.Usage);\n }\n raw = await (deps.readStdin ?? readStdinToString)();\n if (raw.trim().length === 0) {\n emitFailure(args.json, 'no-input', 'stdin was empty.');\n return exitAfterFlush(ExitCode.Usage);\n }\n }\n\n // Auto-detect base64 vs raw JSON. `decodeSessionBlob` peeks at the\n // body for a leading `{` so users can paste either shape without a\n // flag. Symmetric with the env-precedence path in session.ts.\n const decoded = decodeSessionBlob(raw);\n if (decoded === null) {\n emitFailure(args.json, 'invalid-blob', 'could not parse blob as JSON or base64-encoded JSON');\n return exitAfterFlush(ExitCode.Usage);\n }\n const reason = validateSessionBlob(decoded);\n if (reason) {\n emitFailure(args.json, 'invalid-blob', reason);\n return exitAfterFlush(ExitCode.Usage);\n }\n\n const session = normalizeValidatedSession(decoded);\n\n // `replaced` is informational — no overwrite confirm. CI use of\n // `auth import` is rare (env path is preferred); local restore is the\n // primary case and the user is the one running the command.\n const existing = await (deps.readExistingSession ?? readSession)().catch(() => null);\n const replaced = existing !== null;\n\n if (args.dryRun) {\n if (args.json) {\n emitJson({\n ok: true,\n dryRun: true,\n replaced,\n user: session.user,\n warning: KR_ONLY_WARNING_KEY,\n warningMessage: KR_ONLY_WARNING_LONG,\n });\n } else {\n const cookies = session.cookies.length;\n process.stdout.write(\n `dry-run: blob is valid (${session.user.email}, ${cookies} cookies). Skipping write.\\n`,\n );\n process.stderr.write(`warning: ${KR_ONLY_WARNING_SHORT}\\n`);\n }\n return exitAfterFlush(ExitCode.Ok);\n }\n\n // Write path. We deliberately call `writeSession` even when reading\n // from --from-env; the user explicitly invoked import to persist, and\n // the env-write no-op in session.ts would otherwise swallow the write\n // silently. The `forceWrite` option opts into the write path without\n // touching `process.env` — the env no-op exists to protect commands\n // that *might* accidentally write (like `workspace use` on a CI host),\n // not the command whose entire job is to write.\n try {\n await (deps.writeSession ?? writeSession)(session, { forceWrite: true });\n } catch (err) {\n const message = (err as Error).message;\n if (args.json) {\n emitJson({ ok: false, reason: 'write-failed', message });\n } else {\n process.stderr.write(`Failed to write session file: ${message}\\n`);\n }\n return exitAfterFlush(ExitCode.Generic);\n }\n\n if (args.json) {\n emitJson({\n ok: true,\n replaced,\n user: session.user,\n warning: KR_ONLY_WARNING_KEY,\n warningMessage: KR_ONLY_WARNING_LONG,\n });\n } else {\n const path = sessionPathForDiagnostics();\n const cookies = session.cookies.length;\n const verb = replaced ? 'replaced' : 'wrote';\n process.stdout.write(\n `ok — ${verb} session at ${path} (${session.user.email}, ${cookies} cookies)\\n`,\n );\n process.stderr.write(`warning: ${KR_ONLY_WARNING_SHORT}\\n`);\n }\n return exitAfterFlush(ExitCode.Ok);\n}\n\nfunction emitFailure(json: boolean, reason: string, detail: string): void {\n if (json) {\n emitJson({\n ok: false,\n reason,\n ...(reason === 'invalid-blob' ? { detail } : { message: detail }),\n });\n } else {\n process.stderr.write(`${reason}: ${detail}\\n`);\n }\n}\n\nasync function readStdinToString(): Promise<string> {\n // process.stdin is a Readable stream; collect to one buffer.\n const chunks: Buffer[] = [];\n for await (const chunk of process.stdin) {\n chunks.push(typeof chunk === 'string' ? Buffer.from(chunk) : chunk);\n }\n return Buffer.concat(chunks).toString('utf8');\n}\n\nexport const authImportCommand = defineCommand({\n meta: {\n name: 'import',\n description:\n 'Validate a portable session blob and write it to the local session file. KR-only.',\n },\n args: {\n json: {\n type: 'boolean',\n description: 'Emit machine-readable JSON to stdout.',\n default: false,\n },\n 'from-env': {\n type: 'boolean',\n description: 'Read the blob from the AITCC_SESSION env var instead of stdin.',\n default: false,\n },\n 'dry-run': {\n type: 'boolean',\n description: 'Validate the blob without writing the session file.',\n default: false,\n },\n },\n async run({ args }) {\n return runAuthImport({\n json: args.json,\n fromEnv: Boolean(args['from-env']),\n dryRun: Boolean(args['dry-run']),\n });\n },\n});\n","import { confirm, input, password as passwordPrompt } from '@inquirer/prompts';\nimport { defineCommand } from 'citty';\nimport {\n type CredentialBackend,\n deleteCredentials,\n getActiveCredentialEmail,\n saveCredentials,\n} from '../auth/credentials.js';\nimport { ExitCode } from '../exit.js';\nimport { exitAfterFlush } from '../flush.js';\nimport { readSession } from '../session.js';\nimport { emitJson } from './_shared.js';\nimport { authExportCommand } from './auth-export.js';\nimport { authImportCommand } from './auth-import.js';\n\n// `aitcc auth` — user-facing surface over the credentials library\n// introduced in PR α. The library handles env precedence, OS keychain\n// dispatch, and idempotent writes; this file is the thin CLI shell that\n// turns those primitives into `set` / `clear` / `status` subcommands.\n//\n// **Deprecation (0.1.25)**: the three subcommands here have been folded\n// into `aitcc login` (interactive prompt) / `aitcc logout --purge` /\n// `aitcc whoami`. Each entry point emits a one-line stderr warning\n// pointing at the new surface but otherwise keeps its existing behavior\n// and exit codes so users on the old surface aren't broken. Removal is\n// scheduled for 1.0. `auth export` / `auth import` are NOT deprecated —\n// they remain the only path for portable session blobs.\n//\n// --json contract (consumed by agent-plugin):\n//\n// auth set:\n// { ok: true, status: 'created'|'updated'|'unchanged', email } exit 0\n// { ok: false, reason: 'interactive-required'|'invalid-email'|... } exit 2/...\n//\n// auth clear:\n// { ok: true, status: 'deleted'|'absent'|'cancelled' } exit 0\n// { ok: false, reason: 'confirmation-required', ... } exit 2\n//\n// auth status:\n// { ok: true, credentials: { stored, email?, source? },\n// session: { active, user?, capturedAt? } } exit 0\n//\n// Passwords NEVER appear in any output (stdout/stderr/JSON). The auth\n// state pointer file (`auth-state.json`) only carries the email, and\n// the keychain backend never echoes the password to its own logs.\n\n// One-line deprecation banner — emitted to stderr regardless of --json so\n// machine consumers don't see it in their parsed payload but the human\n// running the command in a terminal sees it. Plain prefix (\"warning:\")\n// matches the convention used elsewhere in the CLI (session migration,\n// AITCC_SESSION fallback, ...).\nfunction emitDeprecation(replacement: string): void {\n process.stderr.write(\n `warning: this command is deprecated and will be removed in 1.0; ${replacement}\\n`,\n );\n}\n\n// --- auth set ---\n\nexport interface AuthSetArgs {\n readonly json: boolean;\n readonly email?: string | undefined;\n readonly password?: string | undefined;\n}\n\nexport interface AuthDeps {\n readonly backend?: CredentialBackend;\n readonly env?: NodeJS.ProcessEnv;\n}\n\nexport async function runAuthSet(args: AuthSetArgs, deps: AuthDeps = {}): Promise<void> {\n emitDeprecation('use `aitcc login` (interactive prompt offers a save option).');\n const env = deps.env ?? process.env;\n\n // Resolve email + password from (in order): explicit flags, env vars,\n // interactive prompts. argv passwords are visible in `ps` so we warn;\n // env vars are the recommended scripted path.\n let email = args.email?.trim();\n let password = args.password;\n const argvPasswordUsed = password !== undefined;\n\n if (!email && env.AITCC_EMAIL) email = env.AITCC_EMAIL;\n if (password === undefined && env.AITCC_PASSWORD) password = env.AITCC_PASSWORD;\n\n // Argv password is a footgun. Surface it loud and only once, regardless\n // of whether we end up prompting for the email afterwards.\n if (argvPasswordUsed) {\n process.stderr.write(\n 'Warning: --password on argv is visible in `ps`/Task Manager. ' +\n 'Prefer the AITCC_PASSWORD environment variable for scripted use.\\n',\n );\n }\n\n const interactive = process.stdout.isTTY && process.stdin.isTTY && !args.json;\n\n if (!email) {\n if (!interactive) {\n emitInteractiveRequired(args.json, 'email');\n return exitAfterFlush(ExitCode.Usage);\n }\n try {\n email = (\n await input({\n message: 'Email:',\n validate: (raw) => (raw.trim().length > 0 ? true : 'email is required'),\n })\n ).trim();\n } catch (err) {\n if (isPromptCancelled(err)) {\n process.stderr.write('Aborted.\\n');\n return exitAfterFlush(ExitCode.Usage);\n }\n throw err;\n }\n }\n\n if (!email.includes('@')) {\n // Cheap sanity check — keychain backends accept any string but storing\n // a non-email pointer would silently desync from the login form.\n if (args.json) emitJson({ ok: false, reason: 'invalid-email', message: 'invalid email' });\n else process.stderr.write(`Invalid email: ${email}\\n`);\n return exitAfterFlush(ExitCode.Usage);\n }\n\n if (password === undefined) {\n if (!interactive) {\n emitInteractiveRequired(args.json, 'password');\n return exitAfterFlush(ExitCode.Usage);\n }\n try {\n password = await passwordPrompt({\n message: 'Password:',\n mask: true,\n validate: (raw) => (raw.length > 0 ? true : 'password is required'),\n });\n } catch (err) {\n if (isPromptCancelled(err)) {\n process.stderr.write('Aborted.\\n');\n return exitAfterFlush(ExitCode.Usage);\n }\n throw err;\n }\n }\n\n if (password.length === 0) {\n if (args.json)\n emitJson({ ok: false, reason: 'invalid-password', message: 'password is empty' });\n else process.stderr.write('Password is empty.\\n');\n return exitAfterFlush(ExitCode.Usage);\n }\n\n let result: { status: 'created' | 'updated' | 'unchanged' };\n try {\n result = await saveCredentials(email, password, deps.backend ? { override: deps.backend } : {});\n } catch (err) {\n const message = (err as Error).message;\n if (args.json) emitJson({ ok: false, reason: 'keychain-error', message });\n else process.stderr.write(`Failed to save credentials: ${message}\\n`);\n return exitAfterFlush(ExitCode.Generic);\n }\n\n if (args.json) {\n emitJson({ ok: true, status: result.status, email });\n } else if (result.status === 'unchanged') {\n process.stdout.write('Credentials already saved (no change).\\n');\n } else {\n process.stdout.write(`Credentials saved for ${email} (keychain).\\n`);\n }\n return exitAfterFlush(ExitCode.Ok);\n}\n\n// --- auth clear ---\n\nexport interface AuthClearArgs {\n readonly json: boolean;\n readonly yes: boolean;\n}\n\nexport async function runAuthClear(args: AuthClearArgs, deps: AuthDeps = {}): Promise<void> {\n emitDeprecation('use `aitcc logout --purge` to remove session and saved credentials together.');\n const interactive = process.stdout.isTTY && process.stdin.isTTY && !args.json;\n\n // Look up the active email up front so we can show it in the confirm\n // prompt and the human success line. Failing here is non-fatal — the\n // clear path below will still try to wipe whatever it finds.\n const active = await getActiveCredentialEmail(deps.env ? { env: deps.env } : {}).catch(\n () => null,\n );\n\n if (!args.yes) {\n if (!interactive) {\n // Non-TTY (script, pipe, --json) without --yes is treated as a\n // mistake — silently wiping credentials from a piped invocation\n // would surprise the operator. Refuse and ask for the explicit\n // opt-in. The TTY branch below covers the human-confirm path.\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'confirmation-required',\n message: 'pass --yes to clear credentials in non-interactive mode',\n });\n } else {\n process.stderr.write(\n 'Refusing to clear credentials without confirmation. Pass --yes to proceed.\\n',\n );\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n const label = active?.email ?? '(unknown)';\n let confirmed: boolean;\n try {\n confirmed = await confirm({\n message: `Delete saved credentials for ${label}?`,\n default: false,\n });\n } catch (err) {\n if (isPromptCancelled(err)) {\n process.stderr.write('Aborted.\\n');\n return exitAfterFlush(ExitCode.Usage);\n }\n throw err;\n }\n if (!confirmed) {\n // Distinct status from `'absent'`: the user actively said no even\n // though credentials may exist, vs. nothing was there to begin with.\n if (args.json) emitJson({ ok: true, status: 'cancelled' });\n else process.stdout.write('Aborted.\\n');\n return exitAfterFlush(ExitCode.Ok);\n }\n }\n\n let result: { existed: boolean };\n try {\n result = await deleteCredentials(deps.backend ? { override: deps.backend } : {});\n } catch (err) {\n const message = (err as Error).message;\n if (args.json) emitJson({ ok: false, reason: 'keychain-error', message });\n else process.stderr.write(`Failed to clear credentials: ${message}\\n`);\n return exitAfterFlush(ExitCode.Generic);\n }\n\n const status = result.existed ? 'deleted' : 'absent';\n if (args.json) {\n emitJson({ ok: true, status });\n } else if (result.existed) {\n process.stdout.write('Credentials cleared.\\n');\n } else {\n process.stdout.write('No saved credentials.\\n');\n }\n return exitAfterFlush(ExitCode.Ok);\n}\n\n// --- auth status ---\n\nexport interface AuthStatusArgs {\n readonly json: boolean;\n}\n\nexport async function runAuthStatus(args: AuthStatusArgs, deps: AuthDeps = {}): Promise<void> {\n emitDeprecation('use `aitcc whoami` (now reports credential source).');\n // Read the email pointer without touching the keychain — `auth status`\n // shouldn't trigger a Touch ID / libsecret prompt just to answer \"do I\n // have credentials configured?\". Password retrieval lives in the login\n // path, which already needs it.\n const active = await getActiveCredentialEmail(deps.env ? { env: deps.env } : {}).catch(\n () => null,\n );\n const session = await readSession();\n\n if (args.json) {\n const credentials = active\n ? { stored: true as const, email: active.email, source: active.kind }\n : { stored: false as const };\n const sessionShape = session\n ? {\n active: true as const,\n user: session.user,\n capturedAt: session.capturedAt,\n }\n : { active: false as const };\n emitJson({ ok: true, credentials, session: sessionShape });\n return exitAfterFlush(ExitCode.Ok);\n }\n\n if (active) {\n const sourceLabel = active.kind === 'env' ? 'environment (AITCC_EMAIL/PASSWORD)' : 'keychain';\n process.stdout.write(`Email: ${active.email}\\n`);\n process.stdout.write(`Source: ${sourceLabel}\\n`);\n } else {\n process.stdout.write('Email: (not configured)\\n');\n }\n if (session) {\n const label = session.user.displayName\n ? `${session.user.displayName} <${session.user.email}>`\n : session.user.email;\n process.stdout.write(`Session: active — ${label}\\n`);\n process.stdout.write(`Captured: ${session.capturedAt}\\n`);\n } else {\n process.stdout.write('Session: none (run `aitcc login`)\\n');\n }\n return exitAfterFlush(ExitCode.Ok);\n}\n\n// --- helpers ---\n\nfunction emitInteractiveRequired(json: boolean, missing: 'email' | 'password'): void {\n if (json) {\n emitJson({\n ok: false,\n reason: 'interactive-required',\n message: `${missing} prompt requires a TTY; use --${missing} or AITCC_${missing.toUpperCase()}`,\n });\n } else {\n process.stderr.write(\n `Cannot prompt for ${missing} in non-interactive mode. ` +\n `Use --${missing} or set AITCC_${missing.toUpperCase()}.\\n`,\n );\n }\n}\n\n// Mirrors the helper in app-init.ts — `@inquirer/prompts` throws an\n// `ExitPromptError` (name only, the class isn't exported from the\n// top-level package) when the user hits Ctrl-C. We don't want to surface\n// that as a stack trace.\nfunction isPromptCancelled(err: unknown): boolean {\n return err instanceof Error && err.name === 'ExitPromptError';\n}\n\n// --- citty wrappers ---\n\nconst setCommand = defineCommand({\n meta: {\n name: 'set',\n description: '[deprecated] Use `aitcc login` instead — the prompt now offers a save option.',\n },\n args: {\n json: { type: 'boolean', description: 'Emit machine-readable JSON to stdout.', default: false },\n email: { type: 'string', description: 'Email (skip prompt).' },\n password: {\n type: 'string',\n description: 'Password (skip prompt; visible in `ps` — prefer AITCC_PASSWORD env var).',\n },\n },\n async run({ args }) {\n return runAuthSet({\n json: args.json,\n email: typeof args.email === 'string' ? args.email : undefined,\n password: typeof args.password === 'string' ? args.password : undefined,\n });\n },\n});\n\nconst clearCommand = defineCommand({\n meta: {\n name: 'clear',\n description: '[deprecated] Use `aitcc logout --purge` instead.',\n },\n args: {\n json: { type: 'boolean', description: 'Emit machine-readable JSON to stdout.', default: false },\n yes: {\n type: 'boolean',\n alias: 'y',\n description: 'Skip the confirmation prompt.',\n default: false,\n },\n },\n async run({ args }) {\n return runAuthClear({ json: args.json, yes: args.yes });\n },\n});\n\nconst statusCommand = defineCommand({\n meta: {\n name: 'status',\n description: '[deprecated] Use `aitcc whoami` (now reports credential source).',\n },\n args: {\n json: { type: 'boolean', description: 'Emit machine-readable JSON to stdout.', default: false },\n },\n async run({ args }) {\n return runAuthStatus({ json: args.json });\n },\n});\n\nexport const authCommand = defineCommand({\n meta: {\n name: 'auth',\n description: 'Manage saved login credentials and export/import portable session blobs for CI.',\n },\n subCommands: {\n set: setCommand,\n clear: clearCommand,\n status: statusCommand,\n export: authExportCommand,\n import: authImportCommand,\n },\n});\n","import { defineCommand } from 'citty';\nimport { ExitCode } from '../exit.js';\nimport { exitAfterFlush } from '../flush.js';\nimport { emitJson } from './_shared.js';\n\n// Shell completion scripts for aitcc.\n//\n// Philosophy: keep it static + shallow. citty does not ship its own\n// completion generator, and introspecting a live command tree at runtime\n// would duplicate the source of truth. Instead we hard-code the one-level\n// subcommand list that matters for tab-completion; the shell scripts only\n// need to know top-level commands and the common flags. Deeper sub-sub\n// completion (e.g. `app bundles ls`) stays uncompleted — users type\n// `bundles <TAB>` and the shell falls back to filenames, which is fine.\n//\n// Install flow is one-shot per shell:\n// bash → aitcc completion bash > /etc/bash_completion.d/aitcc\n// or `source <(aitcc completion bash)` in .bashrc\n// zsh → aitcc completion zsh > \"${fpath[1]}/_aitcc\" && autoload -U _aitcc\n// (or drop it in ~/.zsh/completions/_aitcc and rebuild fpath)\n// fish → aitcc completion fish > ~/.config/fish/completions/aitcc.fish\n//\n// install.sh prints the right one-liner after install based on $SHELL;\n// we don't modify user rc files automatically.\n\n// Top-level commands.\nconst TOP_LEVEL: readonly string[] = [\n 'whoami',\n 'login',\n 'logout',\n 'auth',\n 'upgrade',\n 'workspace',\n 'app',\n 'members',\n 'keys',\n 'notices',\n 'me',\n 'completion',\n];\n\n// Subcommands per namespace. Order is for display (alpha).\nconst SUB_COMMANDS: Record<string, readonly string[]> = {\n workspace: ['ls', 'partner', 'segments', 'show', 'terms', 'use'],\n app: [\n 'bundles',\n 'categories',\n 'certs',\n 'events',\n 'init',\n 'ls',\n 'messages',\n 'metrics',\n 'ratings',\n 'register',\n 'reports',\n 'service-status',\n 'share-rewards',\n 'show',\n 'status',\n 'templates',\n ],\n // 3rd-level completion (`app bundles <TAB>`) is out of scope — shells\n // fall back to filename completion there. But we list the bundles\n // subcommands here as a comment for reviewers: ls, deployed, upload,\n // review, release, test-push, test-links.\n notices: ['categories', 'ls', 'show'],\n me: ['terms'],\n auth: ['set', 'clear', 'status'],\n completion: ['bash', 'zsh', 'fish'],\n};\n\nfunction emitBash(): string {\n const top = TOP_LEVEL.join(' ');\n // Per-namespace case branch. We keep this small — only the immediate\n // subcommand layer is offered; the shell then falls back to filename\n // completion for the 3rd word onward.\n const cases: string[] = [];\n for (const [ns, subs] of Object.entries(SUB_COMMANDS)) {\n cases.push(` ${ns})`);\n cases.push(` COMPREPLY=( $(compgen -W \"${subs.join(' ')}\" -- \"$cur\") )`);\n cases.push(' return 0 ;;');\n }\n return `# bash completion for aitcc. Generated by \\`aitcc completion bash\\`.\n# Install:\n# aitcc completion bash > /etc/bash_completion.d/aitcc\n# # or add to ~/.bashrc:\n# source <(aitcc completion bash)\n\n_aitcc_completion() {\n local cur prev\n COMPREPLY=()\n cur=\"\\${COMP_WORDS[COMP_CWORD]}\"\n prev=\"\\${COMP_WORDS[COMP_CWORD-1]}\"\n\n if [[ $COMP_CWORD -eq 1 ]]; then\n COMPREPLY=( $(compgen -W \"${top}\" -- \"$cur\") )\n return 0\n fi\n\n case \"\\${COMP_WORDS[1]}\" in\n${cases.join('\\n')}\n esac\n\n return 0\n}\n\ncomplete -F _aitcc_completion aitcc\n`;\n}\n\nfunction emitZsh(): string {\n // Use zsh's _arguments with a nested state machine. Keep the definition\n // readable; zsh users often inspect their completion files.\n const top = TOP_LEVEL.join(' ');\n const nsClauses: string[] = [];\n for (const [ns, subs] of Object.entries(SUB_COMMANDS)) {\n nsClauses.push(` ${ns}) _values 'subcommand' ${subs.map((s) => `'${s}'`).join(' ')} ;;`);\n }\n return `#compdef aitcc\n# zsh completion for aitcc. Generated by \\`aitcc completion zsh\\`.\n# Install:\n# aitcc completion zsh > \"\\${fpath[1]}/_aitcc\"\n# # then in a fresh shell (or run \\`autoload -U compinit && compinit\\`)\n\n_aitcc() {\n local -a commands\n commands=(${TOP_LEVEL.map((c) => `'${c}'`).join(' ')})\n\n if (( CURRENT == 2 )); then\n _values 'command' ${TOP_LEVEL.map((c) => `'${c}'`).join(' ')}\n return\n fi\n\n case \"\\${words[2]}\" in\n${nsClauses.join('\\n')}\n esac\n}\n\n_aitcc \"$@\"\n# ${top}\n`;\n}\n\nfunction emitFish(): string {\n const lines: string[] = [\n '# fish completion for aitcc. Generated by `aitcc completion fish`.',\n '# Install:',\n '# aitcc completion fish > ~/.config/fish/completions/aitcc.fish',\n '',\n ];\n // Top-level commands (only when no subcommand given yet).\n for (const c of TOP_LEVEL) {\n lines.push(`complete -c aitcc -n \"__fish_use_subcommand\" -a \"${c}\" -f`);\n }\n for (const [ns, subs] of Object.entries(SUB_COMMANDS)) {\n for (const s of subs) {\n lines.push(`complete -c aitcc -n \"__fish_seen_subcommand_from ${ns}\" -a \"${s}\" -f`);\n }\n }\n return `${lines.join('\\n')}\\n`;\n}\n\nexport const completionCommand = defineCommand({\n meta: {\n name: 'completion',\n description: 'Emit a shell completion script for bash, zsh, or fish.',\n },\n args: {\n shell: {\n type: 'positional',\n description: 'Target shell: bash, zsh, or fish.',\n required: false,\n },\n json: {\n type: 'boolean',\n description: \"Emit { ok: false, reason: 'invalid-shell' } on bad input.\",\n default: false,\n },\n },\n async run({ args }) {\n const raw = args.shell === undefined ? '' : String(args.shell).toLowerCase();\n if (raw === 'bash') {\n process.stdout.write(emitBash());\n return exitAfterFlush(ExitCode.Ok);\n }\n if (raw === 'zsh') {\n process.stdout.write(emitZsh());\n return exitAfterFlush(ExitCode.Ok);\n }\n if (raw === 'fish') {\n process.stdout.write(emitFish());\n return exitAfterFlush(ExitCode.Ok);\n }\n // No shell given (or unknown) — print usage to stderr. We expose a\n // --json failure mode so agent-plugin can probe capability without\n // parsing human text.\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'invalid-shell',\n allowed: ['bash', 'zsh', 'fish'],\n message: `completion shell must be one of: bash, zsh, fish (got ${JSON.stringify(raw)})`,\n });\n } else {\n process.stderr.write(\n 'Usage: aitcc completion <bash|zsh|fish>\\n' +\n '\\n' +\n 'Examples:\\n' +\n ' aitcc completion bash > /etc/bash_completion.d/aitcc\\n' +\n // biome-ignore lint/suspicious/noTemplateCurlyInString: literal zsh syntax, not a JS template placeholder\n ' aitcc completion zsh > \"${fpath[1]}/_aitcc\"\\n' +\n ' aitcc completion fish > ~/.config/fish/completions/aitcc.fish\\n',\n );\n }\n return exitAfterFlush(ExitCode.Usage);\n },\n});\n","import type { CdpCookie } from '../cdp.js';\nimport { type FetchLike, requestConsoleApi } from './http.js';\n\n// Console API keys: workspace-scoped credentials used for deploy automation\n// (see docs/api/api-keys.md). Three endpoints, all confirmed against the\n// console UI bundle (`static/index.ZsA5htf8.js`):\n//\n// GET /workspaces/:wid/api-keys → list (`{id, name, expireTs}`)\n// POST /workspaces/:wid/api-keys → issue (returns `{apiKey, ...}`)\n// PUT /workspaces/:wid/api-keys/:keyId/disable → revoke\n//\n// The plaintext key is surfaced **once** in the POST response under the\n// `apiKey` field. The list response intentionally omits it (the UI only\n// shows the user-supplied `name` plus an expiry countdown), so a key that\n// wasn't captured at creation cannot be recovered — same pattern as GitHub\n// PATs and `gh`'s `auth token`.\n//\n// `name` is the user-supplied label (≤16 chars, no whitespace/Korean per UI\n// validation). `expireTs` is epoch ms.\n\nconst BASE = 'https://apps-in-toss.toss.im/console/api-public/v3/appsintossconsole';\n\nexport interface ApiKeySummary {\n readonly id: string | number;\n readonly name: string | undefined;\n readonly expireTs: number | undefined;\n readonly extra: Readonly<Record<string, unknown>>;\n}\n\nexport async function fetchApiKeys(\n workspaceId: number,\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<ApiKeySummary[]> {\n const url = `${BASE}/workspaces/${workspaceId}/api-keys`;\n const raw = await requestConsoleApi<unknown>({\n url,\n cookies,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n if (!Array.isArray(raw)) {\n throw new Error(`Unexpected api-keys shape for workspace=${workspaceId}: not an array`);\n }\n return raw.map((entry, index) => normalizeKey(entry, workspaceId, index));\n}\n\nfunction normalizeKey(raw: unknown, workspaceId: number, index: number): ApiKeySummary {\n if (raw === null || typeof raw !== 'object') {\n throw new Error(\n `Unexpected api-key entry at index ${index} for workspace=${workspaceId}: not an object`,\n );\n }\n const rec = raw as Record<string, unknown>;\n // Field names confirmed from the management page chunk\n // (`static/index.ZsA5htf8.js`); fallbacks kept for `id`/`name` because\n // the bundle only proves they exist on the read path, not what the\n // server emits if a future migration renames them.\n const rawId = rec.id ?? rec.apiKeyId ?? rec.keyId;\n if (typeof rawId !== 'string' && typeof rawId !== 'number') {\n throw new Error(\n `Unexpected api-key entry at index ${index} for workspace=${workspaceId}: missing id`,\n );\n }\n const rawName = rec.name ?? rec.apiKeyName ?? rec.keyName ?? rec.description;\n const name = typeof rawName === 'string' ? rawName : undefined;\n const expireTs = typeof rec.expireTs === 'number' ? rec.expireTs : undefined;\n const {\n id: _id,\n apiKeyId: _aid,\n keyId: _kid,\n name: _n,\n apiKeyName: _an,\n keyName: _kn,\n description: _d,\n expireTs: _e,\n ...extra\n } = rec;\n return { id: rawId, name, expireTs, extra };\n}\n\n// `target` mirrors the console UI dialog: `isAll: true` issues a key valid\n// for every mini-app in the workspace; `isAll: false` scopes it to a list\n// of `appName` slugs (the kebab-case slug, not numeric `miniAppId`).\n//\n// The upstream component (`he` in `static/index.ZsA5htf8.js`) sends `[]` in\n// the all-apps case rather than omitting the field; we replicate that to\n// avoid relying on the server treating an absent key as \"all apps\".\nexport interface CreateApiKeyTarget {\n readonly isAll: boolean;\n readonly appNames: readonly string[];\n}\n\nexport interface CreateApiKeyResult {\n /** Plaintext key, surfaced **only** in the create response. */\n readonly apiKey: string;\n /** Any fields the server returned beyond `apiKey` (`id`, `expireTs`, ...). */\n readonly extra: Readonly<Record<string, unknown>>;\n}\n\nexport async function createApiKey(\n workspaceId: number,\n body: { name: string; target: CreateApiKeyTarget },\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<CreateApiKeyResult> {\n const url = `${BASE}/workspaces/${workspaceId}/api-keys`;\n const raw = await requestConsoleApi<unknown>({\n method: 'POST',\n url,\n cookies,\n body: { workspaceId, name: body.name, target: body.target },\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n if (raw === null || typeof raw !== 'object') {\n throw new Error(`Unexpected api-keys create response for workspace=${workspaceId}`);\n }\n const rec = raw as Record<string, unknown>;\n const apiKey = rec.apiKey;\n if (typeof apiKey !== 'string' || apiKey.length === 0) {\n throw new Error(\n `Unexpected api-keys create response for workspace=${workspaceId}: missing plaintext key`,\n );\n }\n const { apiKey: _k, ...extra } = rec;\n return { apiKey, extra };\n}\n\nexport async function disableApiKey(\n workspaceId: number,\n apiKeyId: string | number,\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<void> {\n const url = `${BASE}/workspaces/${workspaceId}/api-keys/${apiKeyId}/disable`;\n await requestConsoleApi<unknown>({\n method: 'PUT',\n url,\n cookies,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n}\n","import { defineCommand } from 'citty';\nimport {\n type CreateApiKeyTarget,\n createApiKey,\n disableApiKey,\n fetchApiKeys,\n} from '../api/api-keys.js';\nimport { APP_NAME_REGEX } from '../config/app-manifest.js';\nimport { ExitCode } from '../exit.js';\nimport { exitAfterFlush } from '../flush.js';\nimport {\n emitFailureFromError,\n emitJson,\n printContextHeader,\n resolveWorkspaceContext,\n} from './_shared.js';\n\n// --json contract (consumed by agent-plugin):\n//\n// keys ls [--workspace <id>]:\n// { ok: true, workspaceId, keys: [{id, name, expireTs, extra}], needsKey? } exit 0\n// { ok: false, reason: 'no-workspace-selected' } exit 2\n// { ok: false, reason: 'invalid-id', message } exit 2\n//\n// `needsKey: true` is emitted when the key list is empty. The flag is\n// there so `/ait deploy` (and similar agent-plugin skills) can bail\n// with a friendly \"issue a key first\" message instead of attempting a\n// deploy that will 401 server-side. We keep the UI-specific Korean\n// wording out of JSON (it lives on stderr plain output only).\n//\n// keys create --name <label> [--apps <slug,slug>] [--workspace <id>]:\n// { ok: true, workspaceId, apiKey, name, target: {isAll, appNames}, extra } exit 0\n// { ok: false, reason: 'invalid-name', message } exit 2\n// { ok: false, reason: 'invalid-apps', message } exit 2\n// { ok: false, reason: 'no-workspace-selected' } exit 2\n// { ok: false, reason: 'invalid-id', message } exit 2\n//\n// The `apiKey` field carries the plaintext token and is surfaced **only\n// here** — the list endpoint does not echo it back. Agent-plugin skills\n// should pipe it straight into a secret manager and never log the raw\n// value. The CLI itself prints it to stdout once and never persists it.\n//\n// keys revoke <id> [--workspace <id>]:\n// { ok: true, workspaceId, apiKeyId } exit 0\n// { ok: false, reason: 'invalid-id', message } exit 2\n// { ok: false, reason: 'no-workspace-selected' } exit 2\n//\n// Auth/network/api failures follow the shared contract (exit 10/11/17).\n//\n// \"Console API key\" in upstream terminology — used to authenticate\n// automated deploys. Endpoints + payload shapes confirmed from the console\n// management-page chunk; full capture in docs/api/api-keys.md.\n\n// `name` validation mirrors the UI dialog (`he` in static/index.ZsA5htf8.js):\n// - max 16 codepoints (UI shows length/16 counter, disables submit > 16)\n// - \"공백, 한글, 특수문자 제외\" placeholder = ASCII letters/digits/-/_ only\n// We mirror the UI rule rather than rely on the server because the server\n// returns a generic FAIL on rejection and a local check gives a better hint.\nexport const NAME_MAX = 16;\nconst NAME_REGEX = /^[A-Za-z0-9_-]+$/;\n\n// `appName` slugs are kebab-case ASCII per the mini-app registration regex.\n// Reuse the canonical regex so a future tightening (length cap, allowed\n// chars) flows here automatically instead of drifting.\n\nexport type NameValidationError = 'too-short' | 'too-long' | 'bad-chars';\nexport function validateKeyName(raw: string): NameValidationError | null {\n if (raw.length === 0) return 'too-short';\n if (raw.length > NAME_MAX) return 'too-long';\n if (!NAME_REGEX.test(raw)) return 'bad-chars';\n return null;\n}\n\nexport type AppsParseResult =\n | { ok: true; slugs: string[] }\n | { ok: false; reason: 'empty' | 'invalid'; bad?: string[] };\n\nexport function parseAppsFlag(raw: string): AppsParseResult {\n const slugs = raw\n .split(',')\n .map((s) => s.trim())\n .filter((s) => s.length > 0);\n if (slugs.length === 0) return { ok: false, reason: 'empty' };\n const bad = slugs.filter((s) => !APP_NAME_REGEX.test(s));\n if (bad.length > 0) return { ok: false, reason: 'invalid', bad };\n return { ok: true, slugs };\n}\n\nconst lsCommand = defineCommand({\n meta: {\n name: 'ls',\n description: 'List console API keys in the selected workspace.',\n },\n args: {\n workspace: {\n type: 'string',\n description: 'Workspace ID. Defaults to the selected workspace (`aitcc workspace use`).',\n },\n json: { type: 'boolean', description: 'Emit machine-readable JSON to stdout.', default: false },\n },\n async run({ args }) {\n const ctx = await resolveWorkspaceContext(args);\n if (!ctx) return;\n const { session, workspaceId } = ctx;\n printContextHeader(ctx, { json: args.json });\n\n try {\n const keys = await fetchApiKeys(workspaceId, session.cookies);\n if (args.json) {\n emitJson({\n ok: true,\n workspaceId,\n keys: keys.map((k) => ({\n id: k.id,\n name: k.name ?? null,\n expireTs: k.expireTs ?? null,\n extra: k.extra,\n })),\n ...(keys.length === 0 ? { needsKey: true } : {}),\n });\n return exitAfterFlush(ExitCode.Ok);\n }\n if (keys.length === 0) {\n process.stdout.write(`No API keys in workspace ${workspaceId}.\\n`);\n process.stderr.write(\n 'Hint: `aitcc keys create --name <label>` to issue one (deploy automation requires a key).\\n',\n );\n return exitAfterFlush(ExitCode.Ok);\n }\n process.stdout.write(`${keys.length} API key(s) in workspace ${workspaceId}:\\n`);\n const now = Date.now();\n for (const k of keys) {\n const name = k.name ?? '(unnamed)';\n const expiry = formatExpiry(k.expireTs, now);\n process.stdout.write(`${k.id}\\t${name}\\t${expiry}\\n`);\n }\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\nconst createCommand = defineCommand({\n meta: {\n name: 'create',\n description: 'Issue a new console API key. Plaintext is shown once.',\n },\n args: {\n name: {\n type: 'string',\n description:\n 'Label for the key (≤16 ASCII chars: letters/digits/-/_). Required, mirrors the UI dialog.',\n required: true,\n },\n apps: {\n type: 'string',\n description:\n 'Comma-separated mini-app `appName` slugs to scope the key to. Omit for an all-apps key (default).',\n },\n workspace: {\n type: 'string',\n description: 'Workspace ID. Defaults to the selected workspace.',\n },\n json: { type: 'boolean', description: 'Emit machine-readable JSON to stdout.', default: false },\n },\n async run({ args }) {\n const ctx = await resolveWorkspaceContext(args);\n if (!ctx) return;\n const { session, workspaceId } = ctx;\n printContextHeader(ctx, { json: args.json });\n\n const name = String(args.name);\n const nameErr = validateKeyName(name);\n if (nameErr !== null) {\n const message =\n nameErr === 'too-short'\n ? '--name is required (1..16 chars)'\n : nameErr === 'too-long'\n ? `--name must be ≤${NAME_MAX} chars (got ${name.length})`\n : '--name may contain only ASCII letters, digits, hyphen, and underscore (no spaces, Korean, or special chars)';\n if (args.json) emitJson({ ok: false, reason: 'invalid-name', message });\n else process.stderr.write(`${message}\\n`);\n return exitAfterFlush(ExitCode.Usage);\n }\n\n let target: CreateApiKeyTarget;\n if (args.apps) {\n const parsed = parseAppsFlag(String(args.apps));\n if (!parsed.ok) {\n const message =\n parsed.reason === 'empty'\n ? '--apps was empty (drop the flag for an all-apps key)'\n : `--apps contains invalid slug(s): ${(parsed.bad ?? []).join(', ')} (expected kebab-case: [a-z][a-z0-9-]*)`;\n if (args.json) emitJson({ ok: false, reason: 'invalid-apps', message });\n else process.stderr.write(`${message}\\n`);\n return exitAfterFlush(ExitCode.Usage);\n }\n target = { isAll: false, appNames: parsed.slugs };\n } else {\n target = { isAll: true, appNames: [] };\n }\n\n try {\n const result = await createApiKey(workspaceId, { name, target }, session.cookies);\n if (args.json) {\n emitJson({\n ok: true,\n workspaceId,\n apiKey: result.apiKey,\n name,\n target: { isAll: target.isAll, appNames: [...target.appNames] },\n extra: result.extra,\n });\n return exitAfterFlush(ExitCode.Ok);\n }\n // Plaintext is shown exactly once. The console UI surfaces the same\n // \"이 키는 한 번만 표시되니 복사해서 안전하게 보관해주세요.\" warning;\n // we mirror it on stderr so stdout stays a clean single line that's\n // friendly to `aitcc keys create ... | secret-tool store ...` pipes.\n process.stdout.write(`${result.apiKey}\\n`);\n process.stderr.write(\n '⚠️ This key is shown only once. Save it to a secret manager now — it cannot be retrieved later.\\n',\n );\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\nconst revokeCommand = defineCommand({\n meta: {\n name: 'revoke',\n description: 'Disable a console API key by id.',\n },\n args: {\n id: {\n type: 'positional',\n required: true,\n description: 'API key id (from `aitcc keys ls`).',\n },\n workspace: {\n type: 'string',\n description: 'Workspace ID. Defaults to the selected workspace.',\n },\n json: { type: 'boolean', description: 'Emit machine-readable JSON to stdout.', default: false },\n },\n async run({ args }) {\n const ctx = await resolveWorkspaceContext(args);\n if (!ctx) return;\n const { session, workspaceId } = ctx;\n printContextHeader(ctx, { json: args.json });\n\n // citty enforces `required: true` on the positional, so `args.id` is\n // always present when `run` is called.\n const rawId = String(args.id);\n\n try {\n await disableApiKey(workspaceId, rawId, session.cookies);\n if (args.json) {\n emitJson({ ok: true, workspaceId, apiKeyId: rawId });\n return exitAfterFlush(ExitCode.Ok);\n }\n process.stdout.write(`Revoked API key ${rawId} in workspace ${workspaceId}.\\n`);\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\nexport function formatExpiry(expireTs: number | undefined, now: number): string {\n if (expireTs === undefined) return '';\n const diffMs = expireTs - now;\n const days = Math.floor(diffMs / 86_400_000);\n if (diffMs < 0) return 'expired';\n return `D-${days}`;\n}\n\nexport const keysCommand = defineCommand({\n meta: {\n name: 'keys',\n description: 'Manage console API keys used for deploy automation.',\n },\n subCommands: {\n ls: lsCommand,\n create: createCommand,\n revoke: revokeCommand,\n },\n});\n","// Tiny Chrome DevTools Protocol client. Enough to navigate a tab, listen for\n// frame-navigation events, and dump cookies via `Network.getAllCookies`.\n//\n// Deliberately does NOT pull in `ws` or any WebSocket userland lib: Node 22+\n// and Bun both expose `globalThis.WebSocket` with the standard interface,\n// which is what we use. Keeps `bun build --compile` tiny and avoids the\n// optional-native-deps dance.\n//\n// Threading model: one `CdpClient` wraps one WebSocket connection to the\n// *browser* (the URL printed on Chrome's stderr). Per-target sessions are\n// attached lazily via `Target.attachToTarget` — we shuttle messages over\n// the single connection using `sessionId` routing, the same way the DevTools\n// frontend and `chrome-remote-interface` do. Only the APIs we actually need\n// for login capture are wrapped; everything else is available through the\n// raw `send(method, params, sessionId?)` escape hatch.\n\ntype JsonValue =\n | null\n | string\n | number\n | boolean\n | readonly JsonValue[]\n | { readonly [k: string]: JsonValue };\n\ninterface CdpSuccess {\n readonly id: number;\n readonly result: Record<string, unknown>;\n readonly sessionId?: string;\n}\n\ninterface CdpError {\n readonly id: number;\n readonly error: { readonly code: number; readonly message: string; readonly data?: unknown };\n readonly sessionId?: string;\n}\n\ninterface CdpEvent {\n readonly method: string;\n readonly params: Record<string, unknown>;\n readonly sessionId?: string;\n}\n\ntype CdpMessage = CdpSuccess | CdpError | CdpEvent;\n\nfunction isResponse(m: CdpMessage): m is CdpSuccess | CdpError {\n return 'id' in m;\n}\n\nexport class CdpProtocolError extends Error {\n constructor(\n readonly method: string,\n readonly code: number,\n message: string,\n ) {\n super(`CDP error for ${method}: ${message} (code=${code})`);\n this.name = 'CdpProtocolError';\n }\n}\n\nexport class CdpConnectionClosedError extends Error {\n constructor() {\n super('CDP connection closed before the response arrived.');\n this.name = 'CdpConnectionClosedError';\n }\n}\n\nexport interface CdpCookie {\n readonly name: string;\n readonly value: string;\n readonly domain: string;\n readonly path: string;\n readonly expires: number;\n readonly httpOnly: boolean;\n readonly secure: boolean;\n readonly session: boolean;\n readonly sameSite?: 'Strict' | 'Lax' | 'None';\n}\n\nexport type CdpEventListener = (event: CdpEvent) => void;\n\nexport interface ConnectCdpOptions {\n readonly url: string;\n // Injected for tests. Must match the subset of WebSocket we use: `onopen`,\n // `onmessage`, `onerror`, `onclose`, `send`, `close`, and the `readyState`\n // constants (OPEN, CLOSED).\n readonly webSocketFactory?: (url: string) => WebSocket;\n}\n\nexport class CdpClient {\n private readonly socket: WebSocket;\n private nextId = 1;\n private readonly pending = new Map<\n number,\n { resolve(result: Record<string, unknown>): void; reject(err: Error): void; method: string }\n >();\n private readonly listeners = new Set<CdpEventListener>();\n private closed = false;\n\n private constructor(socket: WebSocket) {\n this.socket = socket;\n socket.addEventListener('message', (ev: MessageEvent) => this.handleMessage(ev));\n socket.addEventListener('close', () => this.handleClose());\n socket.addEventListener('error', () => {\n // Let the `close` event handle pending-promise rejection. Surfacing the\n // error here as well would double-reject; browsers emit both events in\n // quick succession on a failed handshake.\n });\n }\n\n static async connect(options: ConnectCdpOptions): Promise<CdpClient> {\n const factory = options.webSocketFactory ?? ((url: string) => new WebSocket(url));\n const socket = factory(options.url);\n await new Promise<void>((resolve, reject) => {\n const onOpen = () => {\n cleanup();\n resolve();\n };\n const onError = () => {\n cleanup();\n reject(new Error(`Failed to open CDP WebSocket at ${options.url}`));\n };\n const onClose = () => {\n cleanup();\n reject(new Error(`CDP WebSocket closed before opening (${options.url})`));\n };\n const cleanup = () => {\n socket.removeEventListener('open', onOpen);\n socket.removeEventListener('error', onError);\n socket.removeEventListener('close', onClose);\n };\n socket.addEventListener('open', onOpen);\n socket.addEventListener('error', onError);\n socket.addEventListener('close', onClose);\n });\n return new CdpClient(socket);\n }\n\n on(listener: CdpEventListener): () => void {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n async send<T = Record<string, unknown>>(\n method: string,\n params?: Record<string, JsonValue>,\n sessionId?: string,\n ): Promise<T> {\n if (this.closed) throw new CdpConnectionClosedError();\n const id = this.nextId++;\n // Assemble as a plain record and let the JSON serialiser drop keys\n // that are absent — matches the exactOptionalPropertyTypes contract\n // without the triple-nested ternary.\n const req: Record<string, unknown> = { id, method };\n if (params) req.params = params;\n if (sessionId) req.sessionId = sessionId;\n const waiter = new Promise<Record<string, unknown>>((resolve, reject) => {\n this.pending.set(id, { resolve, reject, method });\n });\n this.socket.send(JSON.stringify(req));\n const result = await waiter;\n return result as T;\n }\n\n async close(): Promise<void> {\n if (this.closed) return;\n this.closed = true;\n // Reject any outstanding requests so callers don't hang forever.\n for (const [, pending] of this.pending) {\n pending.reject(new CdpConnectionClosedError());\n }\n this.pending.clear();\n try {\n this.socket.close();\n } catch {\n // already closed\n }\n }\n\n private handleMessage(ev: MessageEvent): void {\n let parsed: CdpMessage;\n try {\n const raw =\n typeof ev.data === 'string' ? ev.data : new TextDecoder().decode(ev.data as ArrayBuffer);\n parsed = JSON.parse(raw) as CdpMessage;\n } catch {\n // Non-JSON payload — shouldn't happen on CDP, ignore.\n return;\n }\n if (isResponse(parsed)) {\n const pending = this.pending.get(parsed.id);\n if (!pending) return;\n this.pending.delete(parsed.id);\n if ('error' in parsed) {\n pending.reject(\n new CdpProtocolError(pending.method, parsed.error.code, parsed.error.message),\n );\n } else {\n pending.resolve(parsed.result);\n }\n return;\n }\n for (const listener of this.listeners) {\n try {\n listener(parsed);\n } catch {\n // Listener errors should not crash the dispatch loop.\n }\n }\n }\n\n private handleClose(): void {\n if (this.closed) return;\n this.closed = true;\n for (const [, pending] of this.pending) {\n pending.reject(new CdpConnectionClosedError());\n }\n this.pending.clear();\n }\n}\n\n// --- High-level helpers ---\n\nexport interface AttachedTarget {\n readonly sessionId: string;\n readonly targetId: string;\n}\n\n/**\n * Attach to the first \"page\" target exposed by the browser. Chrome always\n * opens at least one page target when launched with an initial URL, so this\n * is a reliable way to grab a session without guessing target IDs.\n */\nexport async function attachToFirstPage(client: CdpClient): Promise<AttachedTarget> {\n const { targetInfos } = await client.send<{\n targetInfos: Array<{ targetId: string; type: string }>;\n }>('Target.getTargets');\n const page = targetInfos.find((t) => t.type === 'page');\n if (!page) {\n throw new Error('No page target found; Chrome launched without an initial tab.');\n }\n const { sessionId } = await client.send<{ sessionId: string }>('Target.attachToTarget', {\n targetId: page.targetId,\n flatten: true,\n });\n return { sessionId, targetId: page.targetId };\n}\n\nexport interface FrameNavigatedEvent {\n readonly url: string;\n readonly frameId: string;\n readonly isMainFrame: boolean;\n}\n\n/**\n * Subscribe to main-frame navigations on the attached page session. Returns\n * an unsubscribe function.\n *\n * Chrome emits `Page.frameNavigated` for every frame — we filter to the main\n * frame (top-level document) since auxiliary iframes (analytics, chat\n * widgets) would otherwise trigger false matches.\n */\nexport async function watchMainFrameNavigations(\n client: CdpClient,\n sessionId: string,\n onNavigate: (ev: FrameNavigatedEvent) => void,\n): Promise<() => void> {\n await client.send('Page.enable', {}, sessionId);\n const off = client.on((event) => {\n if (event.sessionId !== sessionId) return;\n if (event.method !== 'Page.frameNavigated') return;\n const frame = event.params.frame as\n | { url?: string; id?: string; parentId?: string }\n | undefined;\n if (!frame?.url || !frame.id) return;\n onNavigate({\n url: frame.url,\n frameId: frame.id,\n isMainFrame: frame.parentId === undefined,\n });\n });\n return off;\n}\n\n/**\n * `Network.getAllCookies` is scoped to a target session — Chrome rejects it\n * on the browser-level endpoint with `method not found`. Requiring sessionId\n * here surfaces that constraint at compile time.\n *\n * The response shape is fixed in the CDP spec, but we still validate every\n * cookie's required string/number fields at runtime so a malformed entry\n * (from a future Chrome change, say) fails loud instead of propagating\n * `undefined` into the Cookie: header or the on-disk session file.\n */\nexport async function getAllCookies(\n client: CdpClient,\n sessionId: string,\n): Promise<readonly CdpCookie[]> {\n const result = await client.send<{ cookies: unknown }>('Network.getAllCookies', {}, sessionId);\n if (!Array.isArray(result.cookies)) {\n throw new Error('Network.getAllCookies returned a non-array payload');\n }\n return result.cookies.map((raw, index) => validateCookie(raw, index));\n}\n\n/**\n * Override the User-Agent on the attached page. The default headless\n * Chrome UA contains a \"HeadlessChrome\" token that some servers use as a\n * bot signal (toss.im included, per the auth spike). We spoof a current\n * stable Chrome UA before the first navigation kicks off.\n */\nexport async function setUserAgentOverride(\n client: CdpClient,\n sessionId: string,\n userAgent: string,\n): Promise<void> {\n await client.send('Network.setUserAgentOverride', { userAgent }, sessionId);\n}\n\n/**\n * Run a JS expression in the main frame and return the resolved value.\n * Wraps the awkward `Runtime.evaluate` shape so callers can `.ok`-check\n * once. `awaitPromise` is on so async expressions like `(async () => …)()`\n * resolve before we read the result.\n */\nexport interface RuntimeEvalSuccess<T> {\n readonly ok: true;\n readonly value: T;\n}\nexport interface RuntimeEvalFailure {\n readonly ok: false;\n readonly error: string;\n}\nexport type RuntimeEvalResult<T> = RuntimeEvalSuccess<T> | RuntimeEvalFailure;\n\nexport async function evaluateInPage<T>(\n client: CdpClient,\n sessionId: string,\n expression: string,\n): Promise<RuntimeEvalResult<T>> {\n let raw: {\n result?: { value?: unknown };\n exceptionDetails?: { text?: string; exception?: { description?: string } };\n };\n try {\n raw = await client.send<{\n result?: { value?: unknown };\n exceptionDetails?: { text?: string; exception?: { description?: string } };\n }>(\n 'Runtime.evaluate',\n {\n expression,\n returnByValue: true,\n awaitPromise: true,\n userGesture: true,\n },\n sessionId,\n );\n } catch (err) {\n return { ok: false, error: (err as Error).message };\n }\n if (raw.exceptionDetails) {\n return {\n ok: false,\n error:\n raw.exceptionDetails.exception?.description ??\n raw.exceptionDetails.text ??\n 'unknown evaluate exception',\n };\n }\n return { ok: true, value: raw.result?.value as T };\n}\n\n/**\n * Read the current main-frame URL from the frame tree. Cheaper than\n * waiting for a `Page.frameNavigated` event and works even if the page\n * already finished navigating before we attached.\n */\nexport async function getMainFrameUrl(\n client: CdpClient,\n sessionId: string,\n): Promise<string | null> {\n const tree = await client\n .send<{ frameTree: { frame: { url?: string } } }>('Page.getFrameTree', {}, sessionId)\n .catch(() => null);\n return tree?.frameTree.frame?.url ?? null;\n}\n\nfunction validateCookie(raw: unknown, index: number): CdpCookie {\n if (!raw || typeof raw !== 'object') {\n throw new Error(`Cookie #${index} is not an object`);\n }\n const c = raw as Record<string, unknown>;\n const str = (field: string): string => {\n const v = c[field];\n if (typeof v !== 'string') throw new Error(`Cookie #${index}.${field} is not a string`);\n return v;\n };\n const num = (field: string): number => {\n const v = c[field];\n if (typeof v !== 'number') throw new Error(`Cookie #${index}.${field} is not a number`);\n return v;\n };\n const bool = (field: string): boolean => {\n const v = c[field];\n if (typeof v !== 'boolean') throw new Error(`Cookie #${index}.${field} is not a boolean`);\n return v;\n };\n const base = {\n name: str('name'),\n value: str('value'),\n domain: str('domain'),\n path: str('path'),\n expires: num('expires'),\n httpOnly: bool('httpOnly'),\n secure: bool('secure'),\n session: bool('session'),\n };\n const sameSite = c.sameSite;\n if (sameSite === 'Strict' || sameSite === 'Lax' || sameSite === 'None') {\n return { ...base, sameSite };\n }\n return base;\n}\n","import { type ChildProcess, spawn } from 'node:child_process';\nimport { constants as fsConstants } from 'node:fs';\nimport { mkdtemp, rm } from 'node:fs/promises';\nimport { homedir, tmpdir } from 'node:os';\nimport { join, win32 as winPath } from 'node:path';\n\n// Thin cross-platform launcher for an existing Chrome/Chromium-family\n// browser with the Chrome DevTools Protocol enabled. We drive the session\n// over CDP rather than relying on Playwright so `bun build --compile` keeps\n// producing a ~10 MB standalone binary with no bundled Chromium.\n//\n// We deliberately use an ephemeral `--user-data-dir` so the login session\n// is isolated from the user's everyday browser profile. The caller is\n// responsible for disposing the session (we expose a `dispose()` helper\n// that kills the process and removes the temp dir).\n\nexport interface ChromePaths {\n readonly candidates: readonly string[];\n}\n\nexport class ChromeNotFoundError extends Error {\n constructor(readonly candidates: readonly string[]) {\n super(\n `Could not find Chrome or a Chromium-family browser. Tried: ${candidates.join(', ')}.\\n` +\n 'Install Chrome, or set AITCC_BROWSER to an executable path.',\n );\n this.name = 'ChromeNotFoundError';\n }\n}\n\nexport class ChromeLaunchError extends Error {\n constructor(\n readonly executable: string,\n cause: Error,\n ) {\n super(`Failed to launch ${executable}: ${cause.message}`);\n this.name = 'ChromeLaunchError';\n this.cause = cause;\n }\n}\n\nexport class ChromeEndpointTimeoutError extends Error {\n constructor(readonly executable: string) {\n super(\n `${executable} did not print a DevTools endpoint within the timeout. ` +\n 'It may have been blocked by the OS or launched a GUI-less variant.',\n );\n this.name = 'ChromeEndpointTimeoutError';\n }\n}\n\n// Probe order: the common install paths, favouring the vendor's own packaging\n// over snap/flatpak (those sometimes restrict --remote-debugging-port writes\n// due to sandboxing). Respect $AITCC_BROWSER as an override.\nexport function chromeCandidates(\n env: NodeJS.ProcessEnv = process.env,\n platform: NodeJS.Platform = process.platform,\n): ChromePaths {\n const override = env.AITCC_BROWSER;\n const out: string[] = [];\n if (override && override.length > 0) out.push(override);\n\n if (platform === 'darwin') {\n out.push(\n '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',\n '/Applications/Google Chrome Beta.app/Contents/MacOS/Google Chrome Beta',\n '/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary',\n '/Applications/Chromium.app/Contents/MacOS/Chromium',\n '/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge',\n '/Applications/Arc.app/Contents/MacOS/Arc',\n );\n } else if (platform === 'win32') {\n // `path.win32.join` produces backslash-separated paths even when the\n // test/build runner is POSIX, so the candidate list matches what\n // Windows actually uses on disk.\n const pf = env.PROGRAMFILES ?? 'C:\\\\Program Files';\n const pf86 = env['PROGRAMFILES(X86)'] ?? 'C:\\\\Program Files (x86)';\n const local = env.LOCALAPPDATA ?? winPath.join(homedir() || 'C:\\\\', 'AppData', 'Local');\n out.push(\n winPath.join(pf, 'Google', 'Chrome', 'Application', 'chrome.exe'),\n winPath.join(pf86, 'Google', 'Chrome', 'Application', 'chrome.exe'),\n winPath.join(local, 'Google', 'Chrome', 'Application', 'chrome.exe'),\n winPath.join(pf, 'Microsoft', 'Edge', 'Application', 'msedge.exe'),\n winPath.join(pf86, 'Microsoft', 'Edge', 'Application', 'msedge.exe'),\n );\n } else {\n // Linux and the rest: rely on PATH lookup via plain command names.\n out.push(\n 'google-chrome-stable',\n 'google-chrome',\n 'chromium-browser',\n 'chromium',\n 'microsoft-edge-stable',\n 'microsoft-edge',\n );\n }\n\n return { candidates: out };\n}\n\nfunction isAbsolutePath(p: string, platform: NodeJS.Platform): boolean {\n if (platform === 'win32') return /^[A-Za-z]:\\\\/.test(p);\n return p.startsWith('/');\n}\n\nasync function resolveOnPath(\n name: string,\n env: NodeJS.ProcessEnv,\n platform: NodeJS.Platform,\n): Promise<string | null> {\n const path = env.PATH ?? env.Path ?? env.path ?? '';\n if (path.length === 0) return null;\n const sep = platform === 'win32' ? ';' : ':';\n const fs = await import('node:fs/promises');\n // Windows picks the matching executable based on PATHEXT — we reproduce\n // the common case so a bare AITCC_BROWSER=chrome still resolves to\n // chrome.exe on disk.\n const extensions =\n platform === 'win32'\n ? ['', ...(env.PATHEXT ?? '.EXE;.CMD;.BAT').split(';').filter((e) => e.length > 0)]\n : [''];\n for (const dir of path.split(sep)) {\n if (dir.length === 0) continue;\n for (const ext of extensions) {\n const candidate = join(dir, name + ext);\n try {\n // Require executable access, not just presence — otherwise a shell\n // alias file or a build artefact sitting on PATH could be picked\n // up as \"Chrome\".\n await fs.access(candidate, fsConstants.X_OK);\n return candidate;\n } catch {\n // try next\n }\n }\n }\n return null;\n}\n\nexport async function findChrome(\n env: NodeJS.ProcessEnv = process.env,\n platform: NodeJS.Platform = process.platform,\n): Promise<string> {\n const { candidates } = chromeCandidates(env, platform);\n const fs = await import('node:fs/promises');\n for (const candidate of candidates) {\n if (isAbsolutePath(candidate, platform)) {\n try {\n await fs.access(candidate, fsConstants.X_OK);\n return candidate;\n } catch {\n // try next\n }\n continue;\n }\n const resolved = await resolveOnPath(candidate, env, platform);\n if (resolved) return resolved;\n }\n throw new ChromeNotFoundError(candidates);\n}\n\nexport interface LaunchedChrome {\n readonly process: ChildProcess;\n readonly webSocketDebuggerUrl: string;\n readonly userDataDir: string;\n dispose(): Promise<void>;\n}\n\nexport interface LaunchChromeOptions {\n readonly initialUrl: string;\n readonly executable?: string;\n readonly endpointTimeoutMs?: number;\n // Run Chrome in `--headless=new` mode for the form-fill login path. The\n // CDP UA spoof handles the \"HeadlessChrome\" token in the default UA, so\n // this flag is just about hiding the GUI.\n readonly headless?: boolean;\n // Hook for tests: if set, skip actually spawning Chrome and feed these\n // bytes to the stderr parser instead. Keeps the parser in the hot path\n // under test without requiring a real Chrome install on CI.\n readonly spawnOverride?: (args: readonly string[]) => ChildProcess;\n}\n\nconst DEVTOOLS_BANNER = /^DevTools listening on (ws:\\/\\/[^\\s]+)\\s*$/m;\n\nfunction consumeDevtoolsEndpoint(buffer: string): string | null {\n const match = DEVTOOLS_BANNER.exec(buffer);\n return match ? (match[1] ?? null) : null;\n}\n\nexport async function launchChrome(options: LaunchChromeOptions): Promise<LaunchedChrome> {\n const executable = options.executable ?? (await findChrome());\n const endpointTimeoutMs = options.endpointTimeoutMs ?? 15_000;\n\n const userDataDir = await mkdtemp(join(tmpdir(), 'aitcc-chrome-'));\n\n // Minimum viable flags:\n // --remote-debugging-port=0 pick an ephemeral port (printed on stderr)\n // --user-data-dir=<tmp> isolate from the user's real profile\n // --no-first-run / --no-default-browser-check skip greeter dialogs\n // --password-store=basic avoid prompting for keyring unlocks on Linux\n // --use-mock-keychain same, but for macOS keychain\n const args: string[] = [\n '--remote-debugging-port=0',\n `--user-data-dir=${userDataDir}`,\n '--no-first-run',\n '--no-default-browser-check',\n '--disable-features=Translate,OptimizationHints',\n '--password-store=basic',\n '--use-mock-keychain',\n ];\n if (options.headless) {\n // `--headless=new` is the modern, GPU-friendly headless mode (the old\n // `--headless` is a separate codebase Google deprecated). `--disable-gpu`\n // keeps it well-behaved on machines with broken GL drivers, and the\n // window-size shapes the layout used by the form-fill JS.\n args.push('--headless=new', '--disable-gpu', '--window-size=1280,900');\n }\n args.push(options.initialUrl);\n\n const spawnFn = options.spawnOverride ?? ((a: readonly string[]) => spawn(executable, [...a]));\n let child: ChildProcess;\n try {\n child = spawnFn(args);\n } catch (err) {\n await rm(userDataDir, { recursive: true, force: true }).catch(() => {});\n throw new ChromeLaunchError(executable, err as Error);\n }\n // Don't block Node's exit on the Chrome child — dispose() kills it\n // explicitly on the happy path; on a hard parent exit we'd rather drop\n // Chrome than hang.\n try {\n child.unref();\n } catch {\n // best-effort\n }\n\n const dispose = async (): Promise<void> => {\n try {\n if (!child.killed) child.kill('SIGTERM');\n } catch {\n // best-effort\n }\n await rm(userDataDir, { recursive: true, force: true }).catch(() => {});\n };\n\n let stderrBuf = '';\n const wsUrl = await new Promise<string>((resolve, reject) => {\n const timer = setTimeout(() => {\n cleanup();\n reject(new ChromeEndpointTimeoutError(executable));\n }, endpointTimeoutMs);\n if (typeof timer.unref === 'function') timer.unref();\n\n const onStderr = (chunk: Buffer) => {\n stderrBuf += chunk.toString('utf8');\n const found = consumeDevtoolsEndpoint(stderrBuf);\n if (found) {\n cleanup();\n resolve(found);\n }\n };\n const onExit = (code: number | null) => {\n cleanup();\n reject(\n new ChromeLaunchError(\n executable,\n new Error(`process exited with code ${code ?? 'null'} before printing endpoint`),\n ),\n );\n };\n const onError = (err: Error) => {\n cleanup();\n reject(new ChromeLaunchError(executable, err));\n };\n const cleanup = () => {\n clearTimeout(timer);\n child.stderr?.off('data', onStderr);\n child.off('exit', onExit);\n child.off('error', onError);\n };\n child.stderr?.on('data', onStderr);\n child.on('exit', onExit);\n child.on('error', onError);\n }).catch(async (err) => {\n await dispose();\n throw err;\n });\n\n return {\n process: child,\n webSocketDebuggerUrl: wsUrl,\n userDataDir,\n dispose,\n };\n}\n\n// Exported for unit tests.\nexport const __test = { consumeDevtoolsEndpoint };\n","// Headless form-fill login flow. Drives Chrome via CDP just like the\n// interactive path, but injects credentials into the Toss Business\n// sign-in form instead of waiting for a human to type them. If the form\n// fill fails (selector mismatch, form not present, etc.) the caller is\n// expected to fall back to the interactive path — this module never\n// retries or loops on its own (rate-limit risk).\n//\n// Step-up auth (Toss app push, OTP, …) is detected by URL pattern OR by\n// Korean text in the page body. When triggered we ask the user to\n// complete the prompt in their Toss app and keep polling for the\n// landing URL.\n//\n// SECURITY: the password value flows through Runtime.evaluate (over the\n// CDP WebSocket on localhost) and never out of this process. We must\n// not log it, embed it in error messages, or surface it via --json. The\n// public errors below redact deliberately.\n\nimport {\n type CdpClient,\n evaluateInPage,\n getMainFrameUrl,\n setUserAgentOverride,\n watchMainFrameNavigations,\n} from './cdp.js';\nimport { isLoginLanding } from './commands/login.js';\n\n// Stock Chrome 130 UA on macOS — the auth spike confirmed servers stop\n// flagging the request once the \"HeadlessChrome\" token is gone. We don't\n// vary by platform: toss.im doesn't OS-fingerprint here, the only goal\n// is to drop the headless token. Last verified against business.toss.im\n// 2026-05-08; bump the version string when the next reviewer touches\n// this file and Chrome stable has moved.\nexport const SPOOFED_USER_AGENT =\n 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) ' +\n 'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36';\n\n// URL fragments that indicate we've been bumped to a step-up challenge.\n// Matched case-insensitively against the main-frame URL.\nexport const STEP_UP_URL_PATTERN = /verify|step.?up|2fa|otp/i;\n\n// Korean copy that the console uses on step-up prompts. Spike never\n// triggered the path, so these come from the patterns the Toss web team\n// uses across other surfaces (\"토스 앱에서 확인\", \"간편인증\", \"전자서명\").\nexport const STEP_UP_BODY_PATTERN = /토스 ?앱|간편인증|전자서명|앱.{0,3}확인/;\n\n// Match against pathname only, not the full URL. The OAuth sign-in URL\n// embeds `redirect_uri=https%3A%2F%2Fapps-in-toss…` in its query string;\n// the raw characters `%2F%2Fa` contain the literal substring `2fa`\n// (case-insensitive), which the `2fa` alternation matches and trips a\n// false step-up on the very first poll.\nexport function urlIndicatesStepUp(url: string): boolean {\n let pathname: string;\n try {\n pathname = new URL(url).pathname;\n } catch {\n return false;\n }\n return STEP_UP_URL_PATTERN.test(pathname);\n}\n\nexport function bodyIndicatesStepUp(bodyText: string): boolean {\n return STEP_UP_BODY_PATTERN.test(bodyText);\n}\n\nexport interface HeadlessLoginCredentials {\n readonly email: string;\n readonly password: string;\n}\n\n// Wider than just success/failure so the caller knows whether to message\n// the user about step-up or to silently fall back to interactive.\nexport type HeadlessLoginOutcome =\n | { readonly kind: 'ok'; readonly stepUp: boolean }\n | { readonly kind: 'fallback'; readonly reason: string }\n | { readonly kind: 'timeout'; readonly stage: 'submit' | 'step-up' };\n\nexport interface RunHeadlessLoginOptions {\n readonly client: CdpClient;\n readonly sessionId: string;\n readonly credentials: HeadlessLoginCredentials;\n // Overall observation window after the form submit. The interactive\n // path's --timeout is much larger because a human types; here we only\n // need long enough for the OAuth chain (~1.3s observed) to finish, so\n // 30s is a comfortable default.\n readonly submitObservationMs?: number;\n // How long to wait for the user to complete a step-up prompt (Toss app\n // push, OTP, …). Defaults to the caller's overall --timeout so we\n // honour the user's intent.\n readonly stepUpTimeoutMs: number;\n // Hook so the CLI command can print a single \"토스 앱에서 …\" line on\n // stderr without this module taking a dependency on process.stderr.\n readonly onStepUp?: () => void;\n}\n\nconst FORM_READY_POLL_MS = 500;\nconst FORM_READY_TIMEOUT_MS = 20_000;\nconst POST_SUBMIT_POLL_MS = 250;\nconst STEP_UP_POLL_MS = 1000;\n\n// Form-fill JS, evaluated in the Toss Business sign-in page. Lives as a\n// string so we don't have to worry about TypeScript transforms changing\n// the shape — the eval target is the browser, not Node.\n//\n// React (Radix UI) treats inputs as controlled components: a plain\n// `input.value = …` assignment is invisible to React state. We have to\n// call the native value setter and dispatch an `input` event so React's\n// onChange handler picks it up. The same trick worked in the spike.\n//\n// Selectors are intentionally robust to id changes (Radix ids look like\n// `radix-:r0:` and aren't stable):\n// - email input matched by name (`email`/`loginId`/`username`),\n// then by `type=email` (which is semantically unambiguous). We do\n// NOT fall back to `type=text` — a search box or other unrelated\n// text input rendered above the form would otherwise receive the\n// credentials in plaintext.\n// - password input matched by name then by `type=password`.\n// - submit button matched by `type=submit` first, then by visible text\n// containing \"로그인\" / \"sign in\" / \"login\".\nconst FILL_AND_SUBMIT_FN = `\n async (email, password) => {\n function pickByName(names) {\n for (const n of names) {\n const el = document.querySelector('input[name=\"' + n + '\"]');\n if (el) return el;\n }\n return null;\n }\n function pickInputByType(types) {\n const inputs = Array.from(document.querySelectorAll('input'));\n for (const t of types) {\n const hit = inputs.find(i => (i.type || '').toLowerCase() === t);\n if (hit) return hit;\n }\n return null;\n }\n function setNative(input, value) {\n const proto = Object.getPrototypeOf(input);\n const desc = Object.getOwnPropertyDescriptor(proto, 'value');\n if (desc && desc.set) desc.set.call(input, value);\n else input.value = value;\n input.dispatchEvent(new Event('input', { bubbles: true }));\n input.dispatchEvent(new Event('change', { bubbles: true }));\n }\n const emailInput =\n pickByName(['email', 'loginId', 'username']) ||\n pickInputByType(['email']);\n const passwordInput =\n pickByName(['password', 'loginPassword']) ||\n pickInputByType(['password']);\n if (!emailInput) return { ok: false, stage: 'find-email' };\n if (!passwordInput) return { ok: false, stage: 'find-password' };\n setNative(emailInput, email);\n setNative(passwordInput, password);\n const buttons = Array.from(document.querySelectorAll('button'));\n const submitBtn = buttons.find(b => {\n if (b.disabled) return false;\n const t = (b.type || '').toLowerCase();\n if (t === 'submit') return true;\n const txt = (b.textContent || '').replace(/\\\\s+/g, '');\n return /로그인|sign-?in|login/i.test(txt);\n });\n if (submitBtn) {\n submitBtn.click();\n return { ok: true, stage: 'submit-button' };\n }\n const form = emailInput.closest('form');\n if (form) {\n if (typeof form.requestSubmit === 'function') form.requestSubmit();\n else form.submit();\n return { ok: true, stage: 'submit-form' };\n }\n return { ok: false, stage: 'submit' };\n }\n`;\n\n// Probe the page to see whether the email + password inputs have\n// rendered. The form arrives async (React boot), so we have to poll\n// before we can fill.\nconst FORM_READY_PROBE_FN = `\n () => {\n const inputs = Array.from(document.querySelectorAll('input'));\n const hasEmail = inputs.some(i => {\n const name = (i.name || '').toLowerCase();\n const type = (i.type || '').toLowerCase();\n const placeholder = (i.placeholder || '').toLowerCase();\n const id = (i.id || '').toLowerCase();\n if (name === 'email' || name === 'loginid' || name === 'username') return true;\n if (type === 'email') return true;\n if (type === 'text' && /id|email|username/.test(name + ' ' + id + ' ' + placeholder)) return true;\n return false;\n });\n const hasPassword = inputs.some(i =>\n (i.type || '').toLowerCase() === 'password' || (i.name || '').toLowerCase() === 'password',\n );\n return { ready: hasEmail && hasPassword, count: inputs.length };\n }\n`;\n\n// Snapshot of post-submit state used to decide between landing /\n// step-up / fallback. Body text capped at 4 KB so we don't pull\n// arbitrarily-large pages over the CDP wire.\nconst POST_SUBMIT_PROBE_FN = `\n () => {\n const bodyText = (document.body && document.body.innerText || '').slice(0, 4000);\n const hasCaptchaIframe = !!document.querySelector(\n 'iframe[src*=\"recaptcha\"], iframe[src*=\"hcaptcha\"], iframe[src*=\"turnstile\"], iframe[src*=\"cloudflare\"]'\n );\n const hasErrorBanner = /비밀번호.{0,10}(틀|일치)|아이디.{0,10}(틀|일치)|로그인.{0,5}(실패|불가)|차단|locked/i.test(bodyText);\n return { url: location.href, bodyText, hasCaptchaIframe, hasErrorBanner };\n }\n`;\n\ninterface FormReadyProbe {\n ready: boolean;\n count: number;\n}\n\ninterface FillResult {\n ok: boolean;\n stage: string;\n}\n\ninterface PostSubmitProbe {\n url: string;\n bodyText: string;\n hasCaptchaIframe: boolean;\n hasErrorBanner: boolean;\n}\n\n/**\n * Drive the sign-in form and return when we either landed on the\n * console workspace, hit a step-up prompt that the user resolved, or\n * decided the headless path can't make progress. This function never\n * touches the cookie store or session file — that stays in the calling\n * command after we return `'ok'`.\n *\n * Errors that should fall back to interactive (form not found, captcha,\n * eval failure) are returned as `{ kind: 'fallback', reason }` rather\n * than thrown. Real I/O errors (CDP socket dies) propagate.\n */\nexport async function runHeadlessLogin(\n options: RunHeadlessLoginOptions,\n): Promise<HeadlessLoginOutcome> {\n const {\n client,\n sessionId,\n credentials,\n submitObservationMs = 30_000,\n stepUpTimeoutMs,\n onStepUp,\n } = options;\n\n // Set the UA before we let the page do any more network. Network/Runtime\n // need explicit enable; Page is enabled lazily by `watchMainFrameNavigations`\n // before we start polling.\n await client.send('Network.enable', {}, sessionId);\n await client.send('Runtime.enable', {}, sessionId);\n await setUserAgentOverride(client, sessionId, SPOOFED_USER_AGENT);\n\n // The page may have started loading with the original headless UA before\n // we got to override it. Reload so the request actually carries our\n // spoofed header.\n await client.send('Page.reload', { ignoreCache: true }, sessionId).catch(() => {\n // best-effort: if reload fails (e.g. about:blank), the next nav still\n // picks up the override.\n });\n\n const ready = await waitForFormReady(client, sessionId);\n if (!ready.ok) {\n return { kind: 'fallback', reason: ready.reason };\n }\n\n const fill = await evaluateInPage<FillResult>(\n client,\n sessionId,\n `(${FILL_AND_SUBMIT_FN})(${JSON.stringify(credentials.email)}, ${JSON.stringify(credentials.password)})`,\n );\n if (!fill.ok) {\n // Don't surface the eval error message verbatim — a future Chrome\n // could echo the original expression and leak the password. The\n // Runtime.evaluate path doesn't actually do that today, but the\n // redaction is cheap insurance.\n return { kind: 'fallback', reason: 'form-fill-eval-failed' };\n }\n if (!fill.value.ok) {\n return { kind: 'fallback', reason: `form-fill-${fill.value.stage}` };\n }\n\n // Watch live navigations so we react to the OAuth-redirect chain in\n // ~ms rather than ~1s polls. Polling is still the primary signal.\n let liveLandingUrl: string | null = null;\n const offNav = await watchMainFrameNavigations(client, sessionId, (ev) => {\n if (!ev.isMainFrame) return;\n if (isLoginLanding(ev.url)) liveLandingUrl = ev.url;\n });\n\n try {\n // Phase 1: poll for either landing or step-up over `submitObservationMs`.\n const phase1 = await observeUntilLandingOrStepUp(\n client,\n sessionId,\n submitObservationMs,\n () => liveLandingUrl,\n );\n\n if (phase1.kind === 'landed') {\n return { kind: 'ok', stepUp: false };\n }\n if (phase1.kind === 'fallback') {\n return { kind: 'fallback', reason: phase1.reason };\n }\n if (phase1.kind === 'timeout') {\n return { kind: 'timeout', stage: 'submit' };\n }\n\n // Phase 2: step-up. Inform the caller and wait — much longer — for\n // the user to complete the Toss-app prompt.\n onStepUp?.();\n const phase2 = await pollForLanding(client, sessionId, stepUpTimeoutMs, () => liveLandingUrl);\n if (phase2 === 'landed') return { kind: 'ok', stepUp: true };\n // `pollForLanding` is typed `'landed' | 'timeout'`; the assignment below\n // is a compile-time exhaustiveness check that catches a future return\n // value being added without the matching case here.\n const _: 'timeout' = phase2;\n void _;\n return { kind: 'timeout', stage: 'step-up' };\n } finally {\n offNav();\n }\n}\n\ninterface FormReadyOk {\n readonly ok: true;\n}\ninterface FormReadyFail {\n readonly ok: false;\n readonly reason: string;\n}\n\nasync function waitForFormReady(\n client: CdpClient,\n sessionId: string,\n): Promise<FormReadyOk | FormReadyFail> {\n const deadline = Date.now() + FORM_READY_TIMEOUT_MS;\n let lastReason = 'timeout';\n while (Date.now() < deadline) {\n const probe = await evaluateInPage<FormReadyProbe>(\n client,\n sessionId,\n `(${FORM_READY_PROBE_FN})()`,\n );\n if (probe.ok) {\n if (probe.value.ready) return { ok: true };\n lastReason = `inputs-not-ready (${probe.value.count} input(s) on page)`;\n } else {\n // The page may still be loading and Runtime.evaluate may transiently\n // fail (`Execution context was destroyed`); keep retrying. Don't fold\n // `probe.error` into the reason — same redaction discipline as the\n // form-fill eval path: today's CDP error text is benign, but a future\n // Chrome could echo the original expression in the message.\n lastReason = 'eval-failed';\n }\n await sleep(FORM_READY_POLL_MS);\n }\n return { ok: false, reason: `form-not-ready: ${lastReason}` };\n}\n\ntype Phase1Result =\n | { kind: 'landed' }\n | { kind: 'step-up' }\n | { kind: 'fallback'; reason: string }\n | { kind: 'timeout' };\n\nasync function observeUntilLandingOrStepUp(\n client: CdpClient,\n sessionId: string,\n totalMs: number,\n liveLanding: () => string | null,\n): Promise<Phase1Result> {\n const deadline = Date.now() + totalMs;\n while (Date.now() < deadline) {\n if (liveLanding()) return { kind: 'landed' };\n const fromTree = await getMainFrameUrl(client, sessionId);\n if (fromTree && isLoginLanding(fromTree)) return { kind: 'landed' };\n\n const probe = await evaluateInPage<PostSubmitProbe>(\n client,\n sessionId,\n `(${POST_SUBMIT_PROBE_FN})()`,\n );\n if (probe.ok) {\n if (isLoginLanding(probe.value.url)) return { kind: 'landed' };\n if (probe.value.hasCaptchaIframe) {\n return { kind: 'fallback', reason: 'captcha-detected' };\n }\n if (probe.value.hasErrorBanner) {\n // Could be a wrong-password case — let the user retype manually\n // rather than silently re-trying with the same credentials and\n // tripping a rate-limit lockout.\n return { kind: 'fallback', reason: 'login-error-banner' };\n }\n if (urlIndicatesStepUp(probe.value.url) || bodyIndicatesStepUp(probe.value.bodyText)) {\n return { kind: 'step-up' };\n }\n }\n await sleep(POST_SUBMIT_POLL_MS);\n }\n return { kind: 'timeout' };\n}\n\nasync function pollForLanding(\n client: CdpClient,\n sessionId: string,\n totalMs: number,\n liveLanding: () => string | null,\n): Promise<'landed' | 'timeout'> {\n const deadline = Date.now() + totalMs;\n while (Date.now() < deadline) {\n if (liveLanding()) return 'landed';\n const url = await getMainFrameUrl(client, sessionId);\n if (url && isLoginLanding(url)) return 'landed';\n await sleep(STEP_UP_POLL_MS);\n }\n return 'timeout';\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => {\n const t = setTimeout(resolve, ms);\n if (typeof t.unref === 'function') t.unref();\n });\n}\n\n// Exported for unit tests. The real flow takes a CdpClient; tests can\n// drive the matchers directly without standing up a fake socket.\nexport const __test = {\n FILL_AND_SUBMIT_FN,\n FORM_READY_PROBE_FN,\n POST_SUBMIT_PROBE_FN,\n};\n","import { input, password as passwordPrompt, select } from '@inquirer/prompts';\nimport { defineCommand } from 'citty';\nimport { type FetchLike, TossApiError } from '../api/http.js';\nimport { fetchConsoleMemberUserInfo } from '../api/me.js';\nimport {\n type CredentialsSource,\n loadCredentials,\n type SaveCredentialsStatus,\n saveCredentials,\n} from '../auth/credentials.js';\nimport {\n attachToFirstPage,\n CdpClient,\n type CdpCookie,\n getAllCookies,\n watchMainFrameNavigations,\n} from '../cdp.js';\nimport {\n ChromeEndpointTimeoutError,\n ChromeLaunchError,\n ChromeNotFoundError,\n launchChrome,\n} from '../chrome.js';\nimport { ExitCode } from '../exit.js';\nimport { exitAfterFlush } from '../flush.js';\nimport { type HeadlessLoginOutcome, runHeadlessLogin } from '../login-headless.js';\nimport { type Session, writeSession } from '../session.js';\n\n// Login flow (replaces the prior OAuth-callback-server scaffold):\n//\n// 1. Resolve credentials in priority order — explicit --email/--password*\n// flags > AITCC_EMAIL/AITCC_PASSWORD env > OS keychain > interactive\n// prompt (TTY) > error (non-TTY). Optionally persist to the keychain\n// via the unified --save flag (or by selecting \"keychain\" in the\n// interactive prompt) so the next login runs without prompting.\n// 2. Launch a Chrome-family browser with an isolated user-data-dir.\n// Headless when we have credentials and the user didn't request the\n// visible flow; visible (`--interactive`) when they did or when no\n// credentials were available.\n// 3. Watch main-frame navigations over CDP. Once the URL lands on the\n// console's post-login workspace page, the auth cookies have been set\n// (HttpOnly, so JS can't see them — CDP can).\n// 4. Dump all cookies via `Network.getAllCookies`, resolve the member\n// user-info from the console API, and persist `{ user, cookies,\n// capturedAt }` at `$XDG_CONFIG_HOME/aitcc/session.json` (0600).\n// 5. Dispose the Chrome process and wipe the ephemeral user-data-dir.\n\nconst DEFAULT_AUTHORIZE_URL =\n 'https://business.toss.im/account/sign-in' +\n '?client_id=4uktpjgqd0cp9txybqzuxc2y6w0cuupb' +\n '&redirect_uri=https%3A%2F%2Fapps-in-toss.toss.im%2Fsign-up' +\n '&state=%2Fworkspace';\n\n// The CDP login is complete once the main frame lands on the workspace URL.\nconst LOGIN_LANDING_HOST = 'apps-in-toss.toss.im';\nconst LOGIN_LANDING_PATH_PREFIX = '/workspace';\n\n// Hosts we'll drive a login flow to. `AITCC_OAUTH_URL` is meant as a\n// staging-environment escape hatch, not a way to redirect the CLI to an\n// attacker-controlled URL via a tampered shell rc. A `.toss.im` suffix\n// match is the tightest allowlist that still permits internal hosts.\nconst ALLOWED_AUTHORIZE_HOST_SUFFIXES = ['.toss.im'] as const;\n\n// Minimum time we hand to the interactive-fallback path even if the\n// headless attempt already consumed most of `--timeout`. Without a floor\n// the user could see \"Login timed out after 0s\" right after the visible\n// Chrome appears — comfortably long enough to actually type the form.\nexport const INTERACTIVE_FALLBACK_FLOOR_MS = 30_000;\n\n/**\n * Compute the timeout budget for the interactive fallback attempt after a\n * headless attempt has already burned `elapsedMs`. Caps the result at the\n * user's overall `--timeout` and floors it at `INTERACTIVE_FALLBACK_FLOOR_MS`\n * so a near-exhausted budget still gives the user enough time to type.\n *\n * Pure decision function — extracted so the policy can be exercised\n * without standing up the Chrome/CDP machinery.\n */\nexport function computeFallbackTimeoutMs(totalTimeoutMs: number, elapsedMs: number): number {\n return Math.max(INTERACTIVE_FALLBACK_FLOOR_MS, totalTimeoutMs - elapsedMs);\n}\n\nexport function isAllowedAuthorizeHost(host: string): boolean {\n const lower = host.toLowerCase();\n return ALLOWED_AUTHORIZE_HOST_SUFFIXES.some(\n (suffix) => lower === suffix.slice(1) || lower.endsWith(suffix),\n );\n}\n\nexport function isLoginLanding(url: string): boolean {\n try {\n const u = new URL(url);\n // Use hostname (no port) so a same-host landing on a non-default port\n // still matches — the console hasn't shipped a custom port in the\n // wild but we shouldn't trip on one if it appears.\n if (u.hostname !== LOGIN_LANDING_HOST) return false;\n if (\n u.pathname !== LOGIN_LANDING_PATH_PREFIX &&\n !u.pathname.startsWith(`${LOGIN_LANDING_PATH_PREFIX}/`)\n ) {\n return false;\n }\n // Reject things like `/workspacely`: require the prefix to be followed\n // by end-of-path or a '/'.\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Decide which login mode to enter on first attempt. Pure so the\n * branching policy is testable without standing up Chrome.\n *\n * Rules: `--interactive` always wins. Otherwise headless if and only if\n * we have credentials. The caller is responsible for falling back to\n * interactive on a mid-flight headless failure.\n */\nexport function chooseLoginMode(input: {\n readonly interactiveFlag: boolean;\n readonly hasCredentials: boolean;\n}): LoginMode {\n if (input.interactiveFlag) return 'interactive';\n return input.hasCredentials ? 'headless' : 'interactive';\n}\n\n// Two top-level paths into the login flow:\n// - `interactive`: launch a visible Chrome and let the user type\n// credentials themselves. This is the historical path and the\n// fallback whenever headless can't proceed.\n// - `headless`: launch Chrome with --headless=new, fill the form via\n// CDP using credentials from the OS keychain (or env vars), and\n// wait for the same workspace landing URL.\n// Cookie capture / session write / output is shared after the browser\n// has reached the workspace page — both paths converge there.\nexport type LoginMode = 'interactive' | 'headless';\n\nexport type SaveTarget = 'keychain' | 'none';\n\nexport interface LoginDeps {\n // DI seam for tests and for keeping the CLI entrypoint as the only\n // module that imports `loadCredentials` directly. `null` means \"no\n // credentials configured, take the interactive path\".\n readonly getCredentials?: () => Promise<CredentialsSource | null>;\n // DI seam for the keychain write. Tests can substitute an in-memory\n // backend; production wires `saveCredentials`.\n readonly saveCredentials?: (\n email: string,\n password: string,\n ) => Promise<{ status: SaveCredentialsStatus }>;\n // DI seam for stdin reads (`--password-stdin`). Tests inject the\n // expected password without having to feed a real stream.\n readonly readStdin?: () => Promise<string>;\n // DI seam for prompts so tests don't have to run the inquirer renderer.\n readonly prompts?: PromptDeps;\n}\n\nexport interface PromptDeps {\n readonly email: (defaultValue?: string) => Promise<string>;\n readonly password: () => Promise<string>;\n readonly saveTarget: () => Promise<SaveTarget>;\n}\n\nconst defaultPromptDeps: PromptDeps = {\n email: (defaultValue) =>\n input({\n message: 'Email:',\n ...(defaultValue !== undefined ? { default: defaultValue } : {}),\n validate: (raw) => {\n const trimmed = raw.trim();\n if (trimmed.length === 0) return 'email is required';\n if (!trimmed.includes('@')) return 'must contain \"@\"';\n return true;\n },\n }).then((s) => s.trim()),\n password: () =>\n passwordPrompt({\n message: 'Password:',\n mask: true,\n validate: (raw) => (raw.length > 0 ? true : 'password is required'),\n }),\n saveTarget: () =>\n select<SaveTarget>({\n message: 'Where would you like to save the credentials?',\n default: 'keychain',\n choices: [\n {\n name: 'OS keychain (recommended) — next login runs headlessly',\n value: 'keychain',\n },\n {\n name: 'Do not save — one-shot. (Tip: AITCC_EMAIL/AITCC_PASSWORD env for CI.)',\n value: 'none',\n },\n ],\n }),\n};\n\nexport const loginCommand = defineCommand({\n meta: {\n name: 'login',\n description: 'Sign in to the Apps in Toss console and capture the session cookies.',\n },\n args: {\n json: {\n type: 'boolean',\n description: 'Emit machine-readable JSON to stdout.',\n default: false,\n },\n timeout: {\n type: 'string',\n description: 'Abort if login does not complete within N seconds (default 300).',\n default: '300',\n },\n interactive: {\n type: 'boolean',\n description: 'Force the visible-browser flow even if credentials are configured.',\n default: false,\n },\n email: {\n type: 'string',\n description: 'Email (skip prompt; required for non-interactive use).',\n },\n password: {\n type: 'string',\n description:\n 'Password (skip prompt; visible in `ps`/Task Manager — prefer --password-stdin or AITCC_PASSWORD env).',\n },\n 'password-stdin': {\n type: 'boolean',\n description: 'Read the password from stdin (recommended for non-interactive use).',\n default: false,\n },\n save: {\n type: 'string',\n description:\n 'Where to persist credentials when --email/--password* are passed: \"keychain\" or \"none\" (default).',\n },\n 'skip-onboarding': {\n type: 'boolean',\n description: 'Deprecated no-op; kept so existing scripts do not break.',\n default: false,\n },\n },\n async run({ args }) {\n return runLoginCommand(\n {\n json: args.json,\n timeout: args.timeout,\n interactive: args.interactive,\n email: typeof args.email === 'string' ? args.email : undefined,\n password: typeof args.password === 'string' ? args.password : undefined,\n passwordStdin: args['password-stdin'],\n save: typeof args.save === 'string' ? args.save : undefined,\n },\n { getCredentials: loadCredentials, saveCredentials },\n );\n },\n});\n\nexport interface LoginCommandArgs {\n readonly json: boolean;\n readonly timeout: string;\n readonly interactive: boolean;\n readonly email?: string | undefined;\n readonly password?: string | undefined;\n readonly passwordStdin: boolean;\n readonly save?: string | undefined;\n}\n\ninterface ResolvedCredentials {\n readonly source: 'argv' | 'env' | 'keychain' | 'prompt';\n readonly email: string;\n readonly password: string;\n}\n\ninterface ResolveCredentialsOk {\n readonly kind: 'ok';\n readonly credentials: ResolvedCredentials | null;\n // 'request' means the user (via --save keychain or interactive prompt)\n // asked us to persist credentials before login. 'none' means do not save.\n readonly saveTarget: SaveTarget;\n}\n\ninterface ResolveCredentialsError {\n readonly kind: 'error';\n readonly reason: string;\n readonly message: string;\n readonly exitCode: number;\n}\n\ntype ResolveCredentialsResult = ResolveCredentialsOk | ResolveCredentialsError;\n\nexport async function runLoginCommand(args: LoginCommandArgs, deps: LoginDeps): Promise<never> {\n const emitError = (payload: Record<string, unknown>, human: string) => {\n if (args.json) {\n process.stdout.write(`${JSON.stringify({ ok: false, ...payload })}\\n`);\n }\n process.stderr.write(`${human}\\n`);\n };\n\n const timeoutSec = Number(args.timeout);\n if (!Number.isFinite(timeoutSec) || timeoutSec < 1) {\n emitError(\n { reason: 'invalid-timeout', given: args.timeout },\n `Invalid --timeout value: ${args.timeout}`,\n );\n return exitAfterFlush(ExitCode.Usage);\n }\n const timeoutMs = timeoutSec * 1000;\n\n const rawAuthorizeUrl = process.env.AITCC_OAUTH_URL;\n const authorizeUrl = rawAuthorizeUrl ?? DEFAULT_AUTHORIZE_URL;\n if (rawAuthorizeUrl) {\n let parsed: URL | null = null;\n try {\n parsed = new URL(rawAuthorizeUrl);\n } catch {\n // fall through\n }\n if (!parsed || (parsed.protocol !== 'https:' && parsed.protocol !== 'http:')) {\n emitError(\n { reason: 'invalid-authorize-url' },\n `AITCC_OAUTH_URL is not a valid http(s) URL: ${rawAuthorizeUrl}`,\n );\n return exitAfterFlush(ExitCode.Usage);\n }\n if (!isAllowedAuthorizeHost(parsed.hostname)) {\n emitError(\n { reason: 'authorize-host-not-allowed', host: parsed.hostname },\n `Refusing to open ${parsed.hostname}: only *.toss.im hosts are allowed for sign-in.`,\n );\n return exitAfterFlush(ExitCode.Usage);\n }\n process.stderr.write(`Using custom authorize URL from AITCC_OAUTH_URL: ${authorizeUrl}\\n`);\n }\n\n // Resolve credentials before launching anything. The unified surface:\n // argv flags > env > keychain > interactive prompt > error (non-TTY).\n const resolved = await resolveCredentialsForLogin(args, deps);\n if (resolved.kind === 'error') {\n emitError({ reason: resolved.reason, message: resolved.message }, resolved.message);\n return exitAfterFlush(resolved.exitCode);\n }\n\n // Persist credentials BEFORE attempting login when the user asked us to.\n // Saving up-front means a successful save followed by a failed login still\n // leaves the keychain in the user-intended state (so they can re-run\n // `aitcc login` without re-prompting). Save failure with an explicit\n // `--save keychain` is fatal — silently downgrading to \"session-only\" would\n // be the opposite of the user's request.\n let saved: SaveCredentialsStatus | 'skipped' = 'skipped';\n if (resolved.saveTarget === 'keychain' && resolved.credentials !== null) {\n const save = deps.saveCredentials;\n if (!save) {\n emitError(\n { reason: 'save-unavailable', message: 'no save backend configured' },\n 'Cannot save credentials: no backend configured.',\n );\n return exitAfterFlush(ExitCode.Generic);\n }\n try {\n const result = await save(resolved.credentials.email, resolved.credentials.password);\n saved = result.status;\n if (!args.json) {\n if (result.status === 'unchanged') {\n process.stderr.write('Credentials already saved (no change).\\n');\n } else {\n process.stderr.write(\n `Credentials saved to OS keychain (${resolved.credentials.email}).\\n`,\n );\n }\n }\n } catch (err) {\n const message = (err as Error).message;\n emitError(\n { reason: 'keychain-save-failed', message },\n `Failed to save credentials to the OS keychain: ${message}\\n` +\n 'On Linux, install libsecret (`secret-tool`) and retry. ' +\n 'Re-run with `--save none` to skip persistence.',\n );\n return exitAfterFlush(ExitCode.Usage);\n }\n }\n\n const initialMode: LoginMode = chooseLoginMode({\n interactiveFlag: args.interactive,\n hasCredentials: resolved.credentials !== null,\n });\n\n // Cap Chrome's own startup window at half the overall --timeout, with\n // a 30-second floor and 60-second ceiling. Corporate anti-virus can\n // easily push a cold Chrome launch past the default 15s; short\n // `--timeout` values shouldn't starve the launch itself.\n const endpointTimeoutMs = Math.min(60_000, Math.max(30_000, Math.floor(timeoutMs / 2)));\n\n // First attempt: in the chosen mode. If headless declines, we recurse\n // once into interactive — never the other way around.\n const firstAttemptStart = Date.now();\n const result = await attemptLogin({\n args,\n timeoutMs,\n endpointTimeoutMs,\n authorizeUrl,\n mode: initialMode,\n credentials: resolved.credentials,\n saved,\n emitError,\n });\n\n if (result.status === 'fallback-to-interactive') {\n process.stderr.write(`${result.message}\\n`);\n // Subtract the time the headless attempt already burned so the user's\n // overall `--timeout` budget is honoured. A small floor protects the\n // human-typing window — if headless ate most of the budget we still\n // give the user a usable interactive session, with the cost showing\n // up as the command running slightly past the requested timeout.\n const fallbackTimeoutMs = computeFallbackTimeoutMs(timeoutMs, Date.now() - firstAttemptStart);\n const second = await attemptLogin({\n args,\n timeoutMs: fallbackTimeoutMs,\n endpointTimeoutMs,\n authorizeUrl,\n mode: 'interactive',\n credentials: null,\n saved,\n emitError,\n });\n if (second.status === 'exit') return exitAfterFlush(second.code);\n // A fallback returning fallback again is a programmer error — we\n // never request fallback while already interactive. Narrow on the\n // discriminant so a future variant can't silently land here.\n const _: 'fallback-to-interactive' = second.status;\n void _;\n return exitAfterFlush(ExitCode.Generic);\n }\n\n return exitAfterFlush(result.code);\n}\n\n/**\n * Resolve credentials and the requested save target for `aitcc login`.\n * Pure-ish: only side-effect is reading stdin via `deps.readStdin` (when\n * `--password-stdin` is set) and prompting via `deps.prompts` (when TTY).\n */\nexport async function resolveCredentialsForLogin(\n args: LoginCommandArgs,\n deps: LoginDeps,\n opts: {\n readonly env?: NodeJS.ProcessEnv;\n readonly stdoutIsTTY?: boolean;\n readonly stdinIsTTY?: boolean;\n } = {},\n): Promise<ResolveCredentialsResult> {\n const env = opts.env ?? process.env;\n const stdoutIsTTY = opts.stdoutIsTTY ?? Boolean(process.stdout.isTTY);\n const stdinIsTTY = opts.stdinIsTTY ?? Boolean(process.stdin.isTTY);\n const interactiveTty = stdoutIsTTY && stdinIsTTY && !args.json;\n\n // --password and --password-stdin are mutually exclusive: both ask us to\n // pick a different source for the same field.\n if (args.password !== undefined && args.passwordStdin) {\n return {\n kind: 'error',\n reason: 'conflicting-password-source',\n message: '--password and --password-stdin cannot be used together.',\n exitCode: ExitCode.Usage,\n };\n }\n\n // --interactive forces the visible-browser flow where the human types\n // credentials directly into the page. Combining it with any credential\n // source (--email/--password/--password-stdin) or with --save would\n // silently drop the user's stated intent — the credentials would never\n // be used to drive form-fill, and the save block (which guards on\n // `credentials !== null`) would never run. Reject the combination up\n // front so the user notices instead of seeing a no-op.\n if (\n args.interactive &&\n (args.email !== undefined ||\n args.password !== undefined ||\n args.passwordStdin ||\n args.save !== undefined)\n ) {\n return {\n kind: 'error',\n reason: 'conflicting-interactive-flags',\n message:\n '--interactive cannot be combined with --email/--password/--password-stdin/--save. ' +\n 'Drop --interactive to use credentials, or drop the credential flags to type in the browser.',\n exitCode: ExitCode.Usage,\n };\n }\n\n // Validate --save value early — it has to be defined here so we can\n // emit a clean error before we touch the network or any prompt.\n let saveTarget: SaveTarget | undefined;\n if (args.save !== undefined) {\n if (args.save !== 'keychain' && args.save !== 'none') {\n return {\n kind: 'error',\n reason: 'invalid-save',\n message: `--save must be \"keychain\" or \"none\" (got \"${args.save}\").`,\n exitCode: ExitCode.Usage,\n };\n }\n saveTarget = args.save;\n }\n\n // 1) Explicit --email + (--password | --password-stdin): full argv mode.\n if (args.email !== undefined || args.password !== undefined || args.passwordStdin) {\n if (args.email === undefined || args.email.trim().length === 0) {\n return {\n kind: 'error',\n reason: 'missing-email',\n message: '--email is required when --password / --password-stdin is passed.',\n exitCode: ExitCode.Usage,\n };\n }\n if (!args.email.includes('@')) {\n return {\n kind: 'error',\n reason: 'invalid-email',\n message: `Invalid email: ${args.email}`,\n exitCode: ExitCode.Usage,\n };\n }\n\n let password: string;\n if (args.passwordStdin) {\n const reader = deps.readStdin ?? readStdinAll;\n const raw = await reader();\n password = stripTrailingNewline(raw);\n if (password.length === 0) {\n return {\n kind: 'error',\n reason: 'invalid-password',\n message: '--password-stdin received an empty password on stdin.',\n exitCode: ExitCode.Usage,\n };\n }\n } else if (args.password !== undefined) {\n // Loud, single warning: argv passwords leak into `ps`/Task Manager.\n // Mirrors the warning `auth set` used to emit so existing users see\n // the same message.\n process.stderr.write(\n 'Warning: --password on argv is visible in `ps`/Task Manager. ' +\n 'Prefer --password-stdin or the AITCC_PASSWORD environment variable.\\n',\n );\n password = args.password;\n if (password.length === 0) {\n return {\n kind: 'error',\n reason: 'invalid-password',\n message: '--password value is empty.',\n exitCode: ExitCode.Usage,\n };\n }\n } else {\n // --email passed but no password source. We could prompt in TTY,\n // but a half-specified non-interactive call is more often a CI\n // typo than a feature; refuse to cover the failure mode.\n return {\n kind: 'error',\n reason: 'missing-password',\n message:\n '--email passed without a password. Add --password-stdin (recommended) or --password.',\n exitCode: ExitCode.Usage,\n };\n }\n\n return {\n kind: 'ok',\n credentials: { source: 'argv', email: args.email.trim(), password },\n saveTarget: saveTarget ?? 'none',\n };\n }\n\n // --interactive forces the visible-browser flow regardless of whether\n // credentials are configured. We still surface --save as user intent\n // for any subsequent re-run, but we don't drive form-fill ourselves.\n if (args.interactive) {\n return { kind: 'ok', credentials: null, saveTarget: saveTarget ?? 'none' };\n }\n\n // 2) AITCC_EMAIL + AITCC_PASSWORD env (CI single-shot).\n if (env.AITCC_EMAIL && env.AITCC_PASSWORD) {\n return {\n kind: 'ok',\n credentials: { source: 'env', email: env.AITCC_EMAIL, password: env.AITCC_PASSWORD },\n // Env credentials are intentionally ephemeral; --save has to be\n // explicit to persist them.\n saveTarget: saveTarget ?? 'none',\n };\n }\n\n // 3) OS keychain — auth-state pointer + keychain entry.\n const getCredentials = deps.getCredentials;\n if (getCredentials) {\n const fromStore = await getCredentials().catch((err: Error) => {\n // A credential backend hiccup shouldn't kill `aitcc login` — log a\n // one-line diagnostic and fall through to the interactive prompt.\n process.stderr.write(`Credential lookup failed (${err.message}); ignoring.\\n`);\n return null;\n });\n if (fromStore) {\n // `loadCredentials` already prefers env over keychain, so reaching\n // this branch means the env path above didn't fire — this is the\n // keychain entry. Emit a one-line stderr breadcrumb so the user\n // knows where the headless attempt is getting its credentials from.\n if (!args.json) {\n process.stderr.write(\n `Using credentials from OS keychain for ${fromStore.email}. ` +\n 'Pass --interactive to type a different account.\\n',\n );\n }\n return {\n kind: 'ok',\n credentials: {\n source: fromStore.kind,\n email: fromStore.email,\n password: fromStore.password,\n },\n // Already stored — no need to re-save unless the user explicitly\n // asked.\n saveTarget: saveTarget ?? 'none',\n };\n }\n }\n\n // 4) Interactive prompt (TTY only). This is the new \"first-run\" path:\n // ask for email/password and the save target in one sequence, then run\n // the headless flow with the captured credentials.\n if (interactiveTty) {\n const prompts = deps.prompts ?? defaultPromptDeps;\n let email: string;\n let password: string;\n try {\n email = await prompts.email();\n password = await prompts.password();\n } catch (err) {\n if (isPromptCancelled(err)) {\n return {\n kind: 'error',\n reason: 'aborted',\n message: 'Aborted.',\n exitCode: ExitCode.Usage,\n };\n }\n throw err;\n }\n\n let promptedSave: SaveTarget;\n if (saveTarget !== undefined) {\n // CLI flag pre-empts the prompt — useful when the user knows the\n // answer in advance and doesn't want a third question.\n promptedSave = saveTarget;\n } else {\n try {\n promptedSave = await prompts.saveTarget();\n } catch (err) {\n if (isPromptCancelled(err)) {\n return {\n kind: 'error',\n reason: 'aborted',\n message: 'Aborted.',\n exitCode: ExitCode.Usage,\n };\n }\n throw err;\n }\n }\n\n return {\n kind: 'ok',\n credentials: { source: 'prompt', email, password },\n saveTarget: promptedSave,\n };\n }\n\n // 5) Non-TTY with no credentials: refuse and tell the operator how to\n // make the call non-interactive.\n return {\n kind: 'error',\n reason: 'interactive-required',\n message:\n 'No credentials configured and stdin is not a TTY. ' +\n 'Pass --email + --password-stdin (or set AITCC_EMAIL + AITCC_PASSWORD).',\n exitCode: ExitCode.Usage,\n };\n}\n\ninterface AttemptOptions {\n readonly args: LoginCommandArgs;\n readonly timeoutMs: number;\n readonly endpointTimeoutMs: number;\n readonly authorizeUrl: string;\n readonly mode: LoginMode;\n readonly credentials: ResolvedCredentials | null;\n readonly saved: SaveCredentialsStatus | 'skipped';\n readonly emitError: (payload: Record<string, unknown>, human: string) => void;\n}\n\ntype AttemptResult =\n | { readonly status: 'exit'; readonly code: number }\n | { readonly status: 'fallback-to-interactive'; readonly message: string };\n\nasync function attemptLogin(opts: AttemptOptions): Promise<AttemptResult> {\n const { args, timeoutMs, endpointTimeoutMs, authorizeUrl, mode, credentials, saved, emitError } =\n opts;\n const headless = mode === 'headless';\n\n const launched = await launchChrome({\n initialUrl: authorizeUrl,\n endpointTimeoutMs,\n headless,\n }).catch((err: Error) => err);\n if (launched instanceof ChromeNotFoundError) {\n emitError({ reason: 'chrome-not-found', candidates: launched.candidates }, launched.message);\n return { status: 'exit', code: ExitCode.LoginBrowserNotFound };\n }\n if (launched instanceof ChromeLaunchError || launched instanceof ChromeEndpointTimeoutError) {\n emitError(\n { reason: 'chrome-launch-failed', message: launched.message },\n `Failed to launch browser: ${launched.message}`,\n );\n return { status: 'exit', code: ExitCode.LoginBrowserFailed };\n }\n if (launched instanceof Error) {\n emitError(\n { reason: 'chrome-launch-failed', errorName: launched.name, message: launched.message },\n `Failed to launch browser (${launched.name}): ${launched.message}`,\n );\n return { status: 'exit', code: ExitCode.LoginBrowserFailed };\n }\n\n if (mode === 'interactive') {\n process.stderr.write(\n 'Opened a browser window — complete the sign-in there. The CLI will capture the session automatically.\\n',\n );\n } else {\n const sourceLabel = credentials?.source ?? 'configured store';\n process.stderr.write(`Signing in headlessly with credentials from ${sourceLabel}…\\n`);\n }\n\n // Resource disposal must happen BEFORE `exitAfterFlush` is called:\n // exitAfterFlush terminates the process, and Chrome children on POSIX\n // are not killed automatically when the parent exits.\n let client: CdpClient | null = null;\n const disposeAll = async (): Promise<void> => {\n if (client) {\n await client.close().catch(() => {});\n client = null;\n }\n await launched.dispose().catch(() => {});\n };\n\n try {\n client = await CdpClient.connect({ url: launched.webSocketDebuggerUrl });\n } catch (err) {\n emitError(\n { reason: 'cdp-connect-failed', message: (err as Error).message },\n `Could not connect to the browser over CDP: ${(err as Error).message}`,\n );\n await disposeAll();\n return { status: 'exit', code: ExitCode.LoginBrowserFailed };\n }\n\n let attached: Awaited<ReturnType<typeof attachToFirstPage>>;\n try {\n attached = await attachToFirstPage(client);\n } catch (err) {\n emitError(\n { reason: 'cdp-attach-failed', message: (err as Error).message },\n `Could not attach to the browser tab: ${(err as Error).message}`,\n );\n await disposeAll();\n return { status: 'exit', code: ExitCode.LoginBrowserFailed };\n }\n\n let stepUp = false;\n if (mode === 'headless') {\n if (!credentials) {\n // Defensive — caller should never put us here without credentials.\n await disposeAll();\n return {\n status: 'fallback-to-interactive',\n message: 'No credentials available; switching to interactive login.',\n };\n }\n let outcome: HeadlessLoginOutcome;\n try {\n outcome = await runHeadlessLogin({\n client,\n sessionId: attached.sessionId,\n credentials: { email: credentials.email, password: credentials.password },\n stepUpTimeoutMs: timeoutMs,\n onStepUp: () =>\n process.stderr.write(\n 'Step-up auth requested — complete the prompt in the Toss app to continue…\\n',\n ),\n });\n } catch (err) {\n // Real I/O failure inside the headless flow. Don't fall back —\n // surface it so the user can see what went wrong.\n emitError(\n { reason: 'headless-login-failed', message: (err as Error).message },\n `Headless login failed: ${(err as Error).message}`,\n );\n await disposeAll();\n return { status: 'exit', code: ExitCode.LoginBrowserFailed };\n }\n\n if (outcome.kind === 'fallback') {\n await disposeAll();\n return {\n status: 'fallback-to-interactive',\n message: `headless login failed: ${outcome.reason}, falling back to interactive`,\n };\n }\n if (outcome.kind === 'timeout') {\n emitError(\n { reason: 'login-timeout', timeoutSec: Math.floor(timeoutMs / 1000), stage: outcome.stage },\n `Login timed out after ${Math.floor(timeoutMs / 1000)}s (${outcome.stage}).`,\n );\n await disposeAll();\n return { status: 'exit', code: ExitCode.LoginTimeout };\n }\n stepUp = outcome.stepUp;\n } else {\n const landing = await waitForLanding(client, attached.sessionId, timeoutMs);\n if (landing === 'timeout') {\n emitError(\n { reason: 'login-timeout', timeoutSec: Math.floor(timeoutMs / 1000) },\n `Login timed out after ${Math.floor(timeoutMs / 1000)}s.`,\n );\n await disposeAll();\n return { status: 'exit', code: ExitCode.LoginTimeout };\n }\n if (landing === 'aborted') {\n emitError(\n { reason: 'login-aborted' },\n 'Login was aborted (browser closed before reaching the console).',\n );\n await disposeAll();\n return { status: 'exit', code: ExitCode.LoginBrowserFailed };\n }\n }\n\n // Both paths converge here: pull cookies, resolve identity, write\n // session, emit human/JSON output.\n const cookies = await getAllCookies(client, attached.sessionId).catch((err: Error) => err);\n if (cookies instanceof Error) {\n emitError(\n { reason: 'cookie-capture-failed', message: cookies.message },\n `Failed to capture cookies: ${cookies.message}`,\n );\n await disposeAll();\n return { status: 'exit', code: ExitCode.LoginCookieCaptureFailed };\n }\n\n const user = await resolveUserWithRetry(cookies, {\n onRetry: (ms) =>\n process.stderr.write(\n `Cookies not yet accepted by the console API — retrying in ${ms}ms...\\n`,\n ),\n }).catch((err: Error) => err);\n if (user instanceof Error) {\n const authFailed = user instanceof TossApiError && user.isAuthError;\n emitError(\n {\n reason: authFailed ? 'login-auth-not-active' : 'member-info-failed',\n message: user.message,\n },\n authFailed\n ? 'Browser session did not produce valid console cookies. Try again and wait for the workspace page to load.'\n : `Failed to read member info: ${user.message}`,\n );\n await disposeAll();\n return {\n status: 'exit',\n code: authFailed ? ExitCode.LoginCookieCaptureFailed : ExitCode.ApiError,\n };\n }\n\n const session: Session = {\n schemaVersion: 2,\n user: {\n id: String(user.id),\n email: user.email,\n displayName: user.name,\n },\n cookies,\n origins: [],\n capturedAt: new Date().toISOString(),\n };\n try {\n await writeSession(session);\n } catch (err) {\n emitError(\n { reason: 'session-write-failed', message: (err as Error).message },\n `Failed to write session file: ${(err as Error).message}`,\n );\n await disposeAll();\n return { status: 'exit', code: ExitCode.Generic };\n }\n\n if (args.json) {\n process.stdout.write(\n `${JSON.stringify({\n ok: true,\n status: 'logged-in',\n user: session.user,\n capturedAt: session.capturedAt,\n cookieCount: cookies.length,\n mode,\n credentialSource: credentials?.source ?? 'browser',\n saved,\n stepUp,\n })}\\n`,\n );\n } else {\n process.stdout.write(`Logged in as ${user.name} <${user.email}>\\n`);\n }\n\n await disposeAll();\n return { status: 'exit', code: ExitCode.Ok };\n}\n\nasync function readStdinAll(): Promise<string> {\n if (process.stdin.isTTY) {\n // Reading from a TTY would block waiting for the user to type EOF —\n // a CI typo (`--password-stdin` without a pipe) shouldn't hang.\n throw new Error('--password-stdin requires stdin to be a pipe, not a TTY.');\n }\n process.stdin.setEncoding('utf8');\n let buf = '';\n for await (const chunk of process.stdin) {\n buf += chunk;\n }\n return buf;\n}\n\nfunction stripTrailingNewline(s: string): string {\n return s.replace(/\\r?\\n$/, '');\n}\n\n// Mirrors the helper in app-init.ts / auth.ts — `@inquirer/prompts` throws an\n// `ExitPromptError` (name only, the class isn't exported from the top-level\n// package) when the user hits Ctrl-C. We don't want to surface that as a\n// stack trace.\nfunction isPromptCancelled(err: unknown): boolean {\n return err instanceof Error && err.name === 'ExitPromptError';\n}\n\nexport async function waitForLanding(\n client: CdpClient,\n sessionId: string,\n timeoutMs: number,\n): Promise<'ok' | 'timeout' | 'aborted'> {\n // Two signals, run together, first wins:\n // (a) Page.frameNavigated events — responsive, catches the final redirect.\n // (b) Polling Page.getFrameTree — a safety net for the race where Chrome\n // finishes the auth redirects before we finish attaching and\n // subscribing. The navigation event won't re-fire for pages that\n // already landed, so we have to poll the current URL at least once\n // (and continue polling in case CDP events are dropped on slow links).\n return await new Promise<'ok' | 'timeout' | 'aborted'>((resolve) => {\n let settled = false;\n const stops: Array<() => void | Promise<void>> = [];\n const settle = (outcome: 'ok' | 'timeout' | 'aborted') => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n clearInterval(pollTimer);\n for (const s of stops) {\n try {\n void s();\n } catch {\n // best effort\n }\n }\n resolve(outcome);\n };\n\n const timer = setTimeout(() => settle('timeout'), timeoutMs);\n if (typeof timer.unref === 'function') timer.unref();\n\n // Target-destroyed → the user closed the tab before landing.\n stops.push(\n client.on((event) => {\n if (event.method === 'Target.targetDestroyed') settle('aborted');\n }),\n );\n\n // (a) Live event subscription. Fires on fresh navigations after we\n // Page.enable — may not trigger if Chrome already finished all\n // redirects before we attached (handled by (b)).\n watchMainFrameNavigations(client, sessionId, (ev) => {\n if (!ev.isMainFrame) return;\n if (isLoginLanding(ev.url)) settle('ok');\n })\n .then((off) => {\n // Polling may have already settled by the time subscribe returns;\n // in that case unregister the listener immediately rather than\n // leaving it dangling on the client.\n if (settled) off();\n else stops.push(off);\n })\n .catch((err: Error) => {\n if (settled) return;\n process.stderr.write(`Could not watch for navigation: ${err.message}\\n`);\n });\n\n // (b) Poll the current main-frame URL every second. Cheap, robust.\n const checkCurrent = async () => {\n if (settled) return;\n const tree = await client\n .send<{ frameTree: { frame: { url?: string; parentId?: string } } }>(\n 'Page.getFrameTree',\n {},\n sessionId,\n )\n .catch(() => null);\n const url = tree?.frameTree.frame?.url;\n if (url && isLoginLanding(url)) settle('ok');\n };\n // Kick off an immediate check — covers the \"already landed\" case.\n void checkCurrent();\n const pollTimer = setInterval(() => {\n void checkCurrent();\n }, 1000);\n if (typeof pollTimer.unref === 'function') pollTimer.unref();\n });\n}\n\n// The console issues auth cookies a beat after the landing navigation\n// fires — if the first /me call 401s, we wait this long and retry once.\n// Larger than the fastest observed exchange (~200 ms), small enough to\n// keep the user from wondering whether the CLI hung.\nexport const AUTH_SETTLE_DELAY_MS = 750;\n\nexport async function resolveUserWithRetry(\n cookies: readonly CdpCookie[],\n opts: {\n onRetry?: (delayMs: number) => void;\n fetchImpl?: FetchLike;\n } = {},\n): Promise<Awaited<ReturnType<typeof fetchConsoleMemberUserInfo>>> {\n const callArgs = opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {};\n try {\n return await fetchConsoleMemberUserInfo(cookies, callArgs);\n } catch (err) {\n if (err instanceof TossApiError && err.isAuthError) {\n opts.onRetry?.(AUTH_SETTLE_DELAY_MS);\n await new Promise((r) => {\n const t = setTimeout(r, AUTH_SETTLE_DELAY_MS);\n if (typeof t.unref === 'function') t.unref();\n });\n return await fetchConsoleMemberUserInfo(cookies, callArgs);\n }\n throw err;\n }\n}\n","import { defineCommand } from 'citty';\nimport { deleteCredentials } from '../auth/credentials.js';\nimport { ExitCode } from '../exit.js';\nimport { exitAfterFlush } from '../flush.js';\nimport { clearSession, sessionPathForDiagnostics } from '../session.js';\n\n// `aitcc logout` deletes the local session file. With `--purge` it also\n// wipes the saved keychain credentials and the auth-state pointer — the\n// \"log out and forget me on this machine\" intent that used to require a\n// separate `aitcc auth clear` invocation.\n\nexport const logoutCommand = defineCommand({\n meta: {\n name: 'logout',\n description: 'Delete the local session file (and optionally the saved credentials).',\n },\n args: {\n json: {\n type: 'boolean',\n description: 'Emit machine-readable JSON to stdout.',\n default: false,\n },\n purge: {\n type: 'boolean',\n description: 'Also delete saved keychain credentials and the auth-state pointer.',\n default: false,\n },\n },\n async run({ args }) {\n const path = sessionPathForDiagnostics();\n\n let sessionRemoved: boolean;\n try {\n const result = await clearSession();\n sessionRemoved = result.existed;\n } catch (err) {\n const message = (err as Error).message;\n if (args.json) {\n process.stdout.write(\n `${JSON.stringify({ ok: false, reason: 'unlink-failed', path, message })}\\n`,\n );\n }\n process.stderr.write(`Failed to remove session file at ${path}: ${message}\\n`);\n return exitAfterFlush(ExitCode.Generic);\n }\n\n let credentialsPurged = false;\n let purgeError: string | null = null;\n if (args.purge) {\n try {\n const result = await deleteCredentials();\n credentialsPurged = result.existed;\n } catch (err) {\n // The session is already gone — don't flip the whole command to\n // failure just because the keychain was unhappy. Surface the\n // problem so the user can clean up manually if needed.\n purgeError = (err as Error).message;\n }\n }\n\n if (args.json) {\n const payload: Record<string, unknown> = {\n ok: true,\n sessionRemoved,\n credentialsPurged,\n path,\n };\n if (purgeError !== null) payload.purgeError = purgeError;\n process.stdout.write(`${JSON.stringify(payload)}\\n`);\n } else {\n if (sessionRemoved) {\n process.stdout.write(`Logged out. Session removed from ${path}\\n`);\n } else {\n process.stdout.write(`No active session at ${path}.\\n`);\n }\n if (args.purge) {\n if (purgeError !== null) {\n process.stderr.write(`Could not delete saved credentials: ${purgeError}\\n`);\n } else if (credentialsPurged) {\n process.stdout.write('Saved credentials deleted from the OS keychain.\\n');\n } else {\n process.stdout.write('No saved credentials to delete.\\n');\n }\n }\n }\n return exitAfterFlush(ExitCode.Ok);\n },\n});\n","import { defineCommand } from 'citty';\nimport { fetchUserTerms, type UserTerm } from '../api/me.js';\nimport { ExitCode } from '../exit.js';\nimport { exitAfterFlush } from '../flush.js';\nimport { readSession } from '../session.js';\nimport { emitFailureFromError, emitJson, emitNotAuthenticated } from './_shared.js';\n\n// --json contract (consumed by agent-plugin):\n//\n// me terms:\n// { ok: true, terms: UserTerm[] } exit 0\n// { ok: true, authenticated: false } exit 10\n// { ok: false, reason: 'network-error' | 'api-error', message, ... } exit 11/17\n//\n// `me` is the user-scoped sibling to `workspace` — anything that describes\n// the logged-in account itself (current console-level terms agreements,\n// future: profile display name, notification preferences) lives here.\n\nfunction formatTermLine(t: UserTerm): string {\n const tag = t.isAgreed ? '[agreed]' : '[pending]';\n const req = t.required ? ' required' : '';\n return ` ${tag}${req} ${t.title}\\n ${t.contentsUrl}\\n`;\n}\n\nconst termsCommand = defineCommand({\n meta: {\n name: 'terms',\n description: 'Show the console-level terms of agreement for the signed-in account.',\n },\n args: {\n json: { type: 'boolean', description: 'Emit machine-readable JSON to stdout.', default: false },\n },\n async run({ args }) {\n const session = await readSession();\n if (!session) {\n emitNotAuthenticated(args.json);\n return exitAfterFlush(ExitCode.NotAuthenticated);\n }\n try {\n const terms = await fetchUserTerms(session.cookies);\n if (args.json) {\n emitJson({ ok: true, terms });\n return exitAfterFlush(ExitCode.Ok);\n }\n if (terms.length === 0) {\n process.stdout.write('No console-level terms required.\\n');\n return exitAfterFlush(ExitCode.Ok);\n }\n process.stdout.write('Console account terms:\\n');\n for (const t of terms) process.stdout.write(formatTermLine(t));\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\nexport const meCommand = defineCommand({\n meta: {\n name: 'me',\n description: 'Inspect account-level settings for the signed-in user.',\n },\n subCommands: {\n terms: termsCommand,\n },\n});\n","import type { CdpCookie } from '../cdp.js';\nimport { type FetchLike, requestConsoleApi } from './http.js';\n\n// GET /workspaces/:id/members — confirmed shape (as of 2026-04):\n// [{ workspaceId, bizUserNo, name, email, status, role,\n// isOwnerDelegationRequested, isAdult }]\n// `bizUserNo` is the stable per-person identifier across workspaces —\n// future `members remove` / role-change commands will key off it.\n\nconst BASE = 'https://apps-in-toss.toss.im/console/api-public/v3/appsintossconsole';\n\nexport interface WorkspaceMember {\n readonly workspaceId: number;\n readonly bizUserNo: number;\n readonly name: string;\n readonly email: string;\n readonly status: string;\n readonly role: string;\n readonly isOwnerDelegationRequested: boolean;\n readonly isAdult: boolean;\n}\n\nexport async function fetchWorkspaceMembers(\n workspaceId: number,\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<WorkspaceMember[]> {\n const url = `${BASE}/workspaces/${workspaceId}/members`;\n const raw = await requestConsoleApi<unknown>({\n url,\n cookies,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n if (!Array.isArray(raw)) {\n throw new Error(`Unexpected members shape for workspace=${workspaceId}: not an array`);\n }\n return raw.map((entry, index) => normalizeMember(entry, workspaceId, index));\n}\n\nfunction normalizeMember(raw: unknown, workspaceId: number, index: number): WorkspaceMember {\n if (raw === null || typeof raw !== 'object') {\n throw new Error(\n `Unexpected member entry at index ${index} for workspace=${workspaceId}: not an object`,\n );\n }\n const rec = raw as Record<string, unknown>;\n const stringField = (k: string): string => {\n const v = rec[k];\n if (typeof v !== 'string') {\n throw new Error(\n `Unexpected member entry at index ${index} for workspace=${workspaceId}: missing ${k}`,\n );\n }\n return v;\n };\n const numField = (k: string): number => {\n const v = rec[k];\n if (typeof v !== 'number' || !Number.isFinite(v)) {\n throw new Error(\n `Unexpected member entry at index ${index} for workspace=${workspaceId}: missing ${k}`,\n );\n }\n return v;\n };\n return {\n workspaceId: numField('workspaceId'),\n bizUserNo: numField('bizUserNo'),\n name: stringField('name'),\n email: stringField('email'),\n status: stringField('status'),\n role: stringField('role'),\n isOwnerDelegationRequested: Boolean(rec.isOwnerDelegationRequested),\n isAdult: Boolean(rec.isAdult),\n };\n}\n","import { defineCommand } from 'citty';\nimport { fetchWorkspaceMembers } from '../api/members.js';\nimport { ExitCode } from '../exit.js';\nimport { exitAfterFlush } from '../flush.js';\nimport {\n emitFailureFromError,\n emitJson,\n printContextHeader,\n resolveWorkspaceContext,\n} from './_shared.js';\n\n// --json contract (consumed by agent-plugin):\n//\n// members ls [--workspace <id>]:\n// { ok: true, workspaceId, members: [{bizUserNo, name, email, status, role, ...}] } exit 0\n// { ok: false, reason: 'no-workspace-selected' } exit 2\n// { ok: false, reason: 'invalid-id', message } exit 2\n//\n// Auth/network/api failures follow the shared contract (exit 10/11/17).\n\nconst lsCommand = defineCommand({\n meta: {\n name: 'ls',\n description: 'List members of the selected workspace.',\n },\n args: {\n workspace: {\n type: 'string',\n description: 'Workspace ID. Defaults to the selected workspace (`aitcc workspace use`).',\n },\n json: { type: 'boolean', description: 'Emit machine-readable JSON to stdout.', default: false },\n },\n async run({ args }) {\n const ctx = await resolveWorkspaceContext(args);\n if (!ctx) return;\n const { session, workspaceId } = ctx;\n printContextHeader(ctx, { json: args.json });\n\n try {\n const members = await fetchWorkspaceMembers(workspaceId, session.cookies);\n if (args.json) {\n // `workspaceId` is omitted per-member (redundant with top level)\n // and `isAdult` is intentionally dropped — it is a Korean-specific\n // age-verification flag (성인 인증) classed as PII under local\n // compliance. Owners see *all* co-members, not just themselves, so\n // default-emitting it would leak every member's adult-verification\n // bit through `--json`. No CLI automation use case justifies\n // exposing it; if one ever arises, an opt-in flag is safer.\n emitJson({\n ok: true,\n workspaceId,\n members: members.map((m) => ({\n bizUserNo: m.bizUserNo,\n name: m.name,\n email: m.email,\n status: m.status,\n role: m.role,\n isOwnerDelegationRequested: m.isOwnerDelegationRequested,\n })),\n });\n return exitAfterFlush(ExitCode.Ok);\n }\n if (members.length === 0) {\n process.stdout.write(`No members in workspace ${workspaceId}.\\n`);\n return exitAfterFlush(ExitCode.Ok);\n }\n for (const m of members) {\n process.stdout.write(`${m.bizUserNo}\\t${m.name}\\t${m.email}\\t${m.role}\\t${m.status}\\n`);\n }\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\nexport const membersCommand = defineCommand({\n meta: {\n name: 'members',\n description: 'Inspect workspace members.',\n },\n subCommands: {\n ls: lsCommand,\n },\n});\n","// Client for the \"ipd-thor\" service, which backs the console's notice\n// board (공지사항). Different base URL than everything else\n// (`api-public.toss.im/api-public/v3/ipd-thor`) and a fixed workspaceId\n// of 129 — that's Toss's shared \"앱인토스 콘솔 공지사항\" workspace, not\n// the caller's business workspace. Every console user reads the same\n// notices from this one bucket.\n//\n// Shares the Toss `{resultType, success, error}` envelope, so we can\n// reuse `requestConsoleApi` from http.ts. Captured session cookies are\n// domain-matched against `.toss.im` which matches both the console\n// and ipd-thor hosts, so no separate auth handshake is needed.\n\nimport type { CdpCookie } from '../cdp.js';\nimport { type FetchLike, requestConsoleApi } from './http.js';\n\nexport const IPD_THOR_WORKSPACE_ID = 129;\nconst BASE = 'https://api-public.toss.im/api-public/v3/ipd-thor/api/v1';\n\n// --- Posts (공지사항) ---\n//\n// GET /workspaces/129/posts?page=&size=&title__icontains=\n//\n// Response envelope (observed 2026-04-23):\n// { page, pageSize, count, next, previous, results: [post] }\n//\n// Pagination is 1-indexed. The server echoes `page: 1` when you send\n// `page=0`, so we expose the page back to callers as the server returned\n// it — don't re-normalize to 0-indexed, that would lie about what the\n// API actually does.\n\nexport interface NoticePostsPage {\n readonly page: number;\n readonly pageSize: number;\n readonly count: number;\n readonly next: string | null;\n readonly previous: string | null;\n readonly results: readonly Readonly<Record<string, unknown>>[];\n}\n\nexport interface FetchNoticesParams {\n readonly page?: number;\n readonly size?: number;\n readonly titleContains?: string;\n}\n\nexport async function fetchNotices(\n params: FetchNoticesParams,\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<NoticePostsPage> {\n const qs = new URLSearchParams();\n qs.set('page', String(params.page ?? 0));\n qs.set('size', String(params.size ?? 20));\n if (params.titleContains !== undefined) {\n qs.set('title__icontains', params.titleContains);\n }\n const url = `${BASE}/workspaces/${IPD_THOR_WORKSPACE_ID}/posts?${qs.toString()}`;\n\n const raw = await requestConsoleApi<unknown>({\n url,\n cookies,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n if (raw === null || typeof raw !== 'object') {\n throw new Error('Unexpected notices shape: not an object');\n }\n const rec = raw as Record<string, unknown>;\n const resultsRaw = rec.results;\n if (!Array.isArray(resultsRaw)) {\n throw new Error('Unexpected notices shape: results is not an array');\n }\n const results = resultsRaw.map((r) => {\n if (r === null || typeof r !== 'object') return {};\n return r as Record<string, unknown>;\n });\n return {\n page: typeof rec.page === 'number' ? rec.page : 1,\n pageSize: typeof rec.pageSize === 'number' ? rec.pageSize : (params.size ?? 20),\n count: typeof rec.count === 'number' ? rec.count : results.length,\n next: typeof rec.next === 'string' ? rec.next : null,\n previous: typeof rec.previous === 'string' ? rec.previous : null,\n results,\n };\n}\n\n// --- Categories ---\n//\n// GET /workspaces/129/categories → array of category objects with\n// { id, name, postCount, children, ... }. Children is always empty in\n// the observed response; if Toss adds hierarchy later we pass it\n// through unchanged.\n\nexport async function fetchNoticeCategories(\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<readonly Readonly<Record<string, unknown>>[]> {\n const url = `${BASE}/workspaces/${IPD_THOR_WORKSPACE_ID}/categories`;\n const raw = await requestConsoleApi<unknown>({\n url,\n cookies,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n if (!Array.isArray(raw)) {\n throw new Error('Unexpected categories shape: not an array');\n }\n return raw.map((c) => {\n if (c === null || typeof c !== 'object') return {};\n return c as Record<string, unknown>;\n });\n}\n\n// --- Single post ---\n//\n// GET /workspaces/129/posts/:id — not yet observed in a live capture\n// (sidebar list endpoint only pulls the collection). Included on\n// speculation so `aitcc notices show <id>` has a place to live; the\n// fetch path follows the same envelope as the list endpoint.\n\nexport async function fetchNoticePost(\n postId: number,\n cookies: readonly CdpCookie[],\n opts: { fetchImpl?: FetchLike } = {},\n): Promise<Record<string, unknown>> {\n const url = `${BASE}/workspaces/${IPD_THOR_WORKSPACE_ID}/posts/${postId}`;\n const raw = await requestConsoleApi<unknown>({\n url,\n cookies,\n ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),\n });\n if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) {\n throw new Error(`Unexpected notice-post shape for id=${postId}`);\n }\n return raw as Record<string, unknown>;\n}\n","import { defineCommand } from 'citty';\nimport { fetchNoticeCategories, fetchNoticePost, fetchNotices } from '../api/ipd-thor.js';\nimport { ExitCode } from '../exit.js';\nimport { exitAfterFlush } from '../flush.js';\nimport { emitFailureFromError, emitJson, requireSession } from './_shared.js';\n\n// --json contract (consumed by agent-plugin):\n//\n// notices ls [--page N] [--size N] [--search STR]:\n// { ok: true, page, pageSize, count, hasNext, notices: [...] } exit 0\n//\n// notices show <id>:\n// { ok: true, id, notice: {...} } exit 0\n// { ok: false, reason: 'invalid-id', message } exit 2\n//\n// notices categories:\n// { ok: true, categories: [...] } exit 0\n//\n// Notices live on a separate service (ipd-thor) from the rest of the\n// console API. There's no per-user workspace scoping — all notices come\n// from Toss's shared workspace 129. Session cookies captured at login\n// include the `.toss.im` suffix so they're sent automatically to the\n// ipd-thor host.\n\nfunction parsePositiveInt(raw: string, field: string): { value: number } | { error: string } {\n const n = Number(raw);\n if (!Number.isFinite(n) || !Number.isInteger(n) || n < 0) {\n return { error: `--${field} must be a non-negative integer (got ${JSON.stringify(raw)})` };\n }\n return { value: n };\n}\n\nconst lsCommand = defineCommand({\n meta: {\n name: 'ls',\n description: 'List notices (공지사항) from Apps in Toss.',\n },\n args: {\n page: { type: 'string', description: 'Page number (0-indexed).', default: '0' },\n size: { type: 'string', description: 'Page size.', default: '20' },\n search: { type: 'string', description: 'Filter by title substring (case-insensitive).' },\n json: { type: 'boolean', description: 'Emit machine-readable JSON.', default: false },\n },\n async run({ args }) {\n const session = await requireSession(args.json);\n if (!session) return;\n const pageResult = parsePositiveInt(args.page, 'page');\n if ('error' in pageResult) {\n if (args.json) {\n emitJson({ ok: false, reason: 'invalid-config', field: 'page', message: pageResult.error });\n } else {\n process.stderr.write(`notices ls: ${pageResult.error}\\n`);\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n const sizeResult = parsePositiveInt(args.size, 'size');\n if ('error' in sizeResult) {\n if (args.json) {\n emitJson({ ok: false, reason: 'invalid-config', field: 'size', message: sizeResult.error });\n } else {\n process.stderr.write(`notices ls: ${sizeResult.error}\\n`);\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n if (sizeResult.value === 0) {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'invalid-config',\n field: 'size',\n message: '--size must be at least 1',\n });\n } else {\n process.stderr.write('notices ls: --size must be at least 1\\n');\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n\n try {\n const result = await fetchNotices(\n {\n page: pageResult.value,\n size: sizeResult.value,\n ...(typeof args.search === 'string' && args.search.length > 0\n ? { titleContains: args.search }\n : {}),\n },\n session.cookies,\n );\n\n if (args.json) {\n emitJson({\n ok: true,\n page: result.page,\n pageSize: result.pageSize,\n count: result.count,\n hasNext: result.next !== null,\n notices: result.results,\n });\n return exitAfterFlush(ExitCode.Ok);\n }\n\n process.stdout.write(\n `Notices: page ${result.page}, ${result.results.length}/${result.count} shown\\n`,\n );\n if (result.results.length === 0) {\n process.stdout.write('No notices on this page.\\n');\n return exitAfterFlush(ExitCode.Ok);\n }\n for (const n of result.results) {\n const id = typeof n.id === 'string' || typeof n.id === 'number' ? n.id : '-';\n const category = typeof n.category === 'string' ? n.category : '-';\n const title = typeof n.title === 'string' ? n.title : '';\n const publishedTime = typeof n.publishedTime === 'string' ? n.publishedTime : '';\n process.stdout.write(`${id}\\t${publishedTime}\\t[${category}]\\t${title}\\n`);\n }\n if (result.next !== null) {\n process.stdout.write(`(more: --page ${result.page + 1})\\n`);\n }\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\nconst showCommand = defineCommand({\n meta: {\n name: 'show',\n description: 'Show a single notice post by ID.',\n },\n args: {\n id: { type: 'positional', description: 'Notice post ID.', required: true },\n json: { type: 'boolean', description: 'Emit machine-readable JSON.', default: false },\n },\n async run({ args }) {\n const postId = Number(args.id);\n if (!Number.isFinite(postId) || !Number.isInteger(postId) || postId <= 0) {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'invalid-id',\n message: `notice id must be a positive integer (got ${JSON.stringify(args.id)})`,\n });\n } else {\n process.stderr.write(`notices show: invalid id ${JSON.stringify(args.id)}\\n`);\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n const session = await requireSession(args.json);\n if (!session) return;\n try {\n const notice = await fetchNoticePost(postId, session.cookies);\n if (args.json) {\n emitJson({ ok: true, id: postId, notice });\n return exitAfterFlush(ExitCode.Ok);\n }\n const title = typeof notice.title === 'string' ? notice.title : '';\n const subtitle = typeof notice.subtitle === 'string' ? notice.subtitle : '';\n const category = typeof notice.category === 'string' ? notice.category : '';\n const publishedTime = typeof notice.publishedTime === 'string' ? notice.publishedTime : '';\n const body =\n typeof notice.fullDescription === 'string'\n ? notice.fullDescription\n : typeof notice.shortDescription === 'string'\n ? notice.shortDescription\n : '';\n process.stdout.write(`# ${title}\\n`);\n if (subtitle) process.stdout.write(`${subtitle}\\n`);\n process.stdout.write(`\\n[${category}] ${publishedTime}\\n\\n`);\n process.stdout.write(body);\n if (!body.endsWith('\\n')) process.stdout.write('\\n');\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\nconst categoriesCommand = defineCommand({\n meta: {\n name: 'categories',\n description: 'List notice categories and their post counts.',\n },\n args: {\n json: { type: 'boolean', description: 'Emit machine-readable JSON.', default: false },\n },\n async run({ args }) {\n const session = await requireSession(args.json);\n if (!session) return;\n try {\n const categories = await fetchNoticeCategories(session.cookies);\n if (args.json) {\n emitJson({ ok: true, categories });\n return exitAfterFlush(ExitCode.Ok);\n }\n for (const c of categories) {\n const id = typeof c.id === 'string' || typeof c.id === 'number' ? c.id : '-';\n const name = typeof c.name === 'string' ? c.name : '-';\n const postCount = typeof c.postCount === 'number' ? c.postCount : 0;\n process.stdout.write(`${id}\\t${postCount}\\t${name}\\n`);\n }\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\nexport const noticesCommand = defineCommand({\n meta: {\n name: 'notices',\n description: 'Read Apps in Toss notices (공지사항). Shared across all users.',\n },\n subCommands: {\n ls: lsCommand,\n show: showCommand,\n categories: categoriesCommand,\n },\n});\n","// Thin GitHub Releases API client. Only reads public endpoints, never writes.\n\nconst REPO_OWNER = 'apps-in-toss-community';\nconst REPO_NAME = 'console-cli';\n\nexport interface ReleaseAsset {\n name: string;\n browser_download_url: string;\n size: number;\n}\n\nexport interface Release {\n tag_name: string;\n name: string | null;\n html_url: string;\n assets: ReleaseAsset[];\n}\n\nfunction defaultHeaders(): HeadersInit {\n const headers: Record<string, string> = {\n Accept: 'application/vnd.github+json',\n 'User-Agent': 'aitcc',\n 'X-GitHub-Api-Version': '2022-11-28',\n };\n const token = process.env.GITHUB_TOKEN;\n if (token && token.length > 0) {\n headers.Authorization = `Bearer ${token}`;\n }\n return headers;\n}\n\nexport async function fetchLatestRelease(): Promise<Release> {\n const url = `https://api.github.com/repos/${REPO_OWNER}/${REPO_NAME}/releases/latest`;\n const res = await fetch(url, { headers: defaultHeaders() });\n if (!res.ok) {\n throw new Error(`GitHub releases/latest returned ${res.status} ${res.statusText}`);\n }\n return (await res.json()) as Release;\n}\n\nexport type ConditionalReleaseResult =\n | { readonly status: 'not-modified'; readonly etag: string | undefined }\n | { readonly status: 'updated'; readonly release: Release; readonly etag: string | undefined };\n\n/**\n * Conditional GET against `releases/latest`. If the server returns 304 we\n * learn \"no change\" without consuming a core rate-limit slot. Intended for\n * the background update check, which re-runs often; `fetchLatestRelease()`\n * remains the right call when the upgrade command actually needs the body.\n */\nexport async function fetchLatestReleaseConditional(\n previousEtag: string | undefined,\n): Promise<ConditionalReleaseResult> {\n const url = `https://api.github.com/repos/${REPO_OWNER}/${REPO_NAME}/releases/latest`;\n const headers = defaultHeaders() as Record<string, string>;\n if (previousEtag && previousEtag.length > 0) {\n headers['If-None-Match'] = previousEtag;\n }\n const res = await fetch(url, { headers });\n const etag = res.headers.get('etag') ?? undefined;\n if (res.status === 304) {\n return { status: 'not-modified', etag };\n }\n if (!res.ok) {\n throw new Error(`GitHub releases/latest returned ${res.status} ${res.statusText}`);\n }\n const release = (await res.json()) as Release;\n return { status: 'updated', release, etag };\n}\n\nexport function findSha256SumsAsset(release: Release): ReleaseAsset | undefined {\n return release.assets.find((a) => a.name === 'SHA256SUMS');\n}\n\n// Parse `tag_name` into a comparable semver string. Changesets tags this repo\n// as `@ait-co/console-cli@0.1.2`; older ad-hoc tags may be `v0.1.2`. We\n// accept both.\nexport function versionFromTag(tag: string): string {\n const at = tag.lastIndexOf('@');\n const candidate = at >= 0 ? tag.slice(at + 1) : tag;\n return candidate.startsWith('v') ? candidate.slice(1) : candidate;\n}\n","// Map Node's `process.platform` / `process.arch` to the binary asset names\n// produced by `scripts/build-bin.ts` and attached to GitHub Releases.\n\nexport interface PlatformTarget {\n os: 'linux' | 'darwin' | 'windows';\n arch: 'x64' | 'arm64';\n assetName: string;\n}\n\nexport function detectPlatform(): PlatformTarget | null {\n let os: PlatformTarget['os'];\n switch (process.platform) {\n case 'linux':\n os = 'linux';\n break;\n case 'darwin':\n os = 'darwin';\n break;\n case 'win32':\n os = 'windows';\n break;\n default:\n return null;\n }\n\n let arch: PlatformTarget['arch'];\n switch (process.arch) {\n case 'x64':\n arch = 'x64';\n break;\n case 'arm64':\n arch = 'arm64';\n break;\n default:\n return null;\n }\n\n // We don't ship windows-arm64 yet — Bun's `--compile` target support is still partial.\n if (os === 'windows' && arch === 'arm64') return null;\n\n const suffix = os === 'windows' ? '.exe' : '';\n return { os, arch, assetName: `aitcc-${os}-${arch}${suffix}` };\n}\n","// Minimal semver comparator. We only need \"is A strictly newer than B?\" for\n// the upgrade check. Pulling the full `semver` package would bloat the\n// compiled binary for one function.\n\nexport function parseSemver(\n v: string,\n): { major: number; minor: number; patch: number; pre: string } | null {\n const m = /^(\\d+)\\.(\\d+)\\.(\\d+)(?:-([0-9A-Za-z.-]+))?/.exec(v);\n if (!m) return null;\n return { major: +m[1]!, minor: +m[2]!, patch: +m[3]!, pre: m[4] ?? '' };\n}\n\n// Returns 1 if a > b, -1 if a < b, 0 if equal. Returns 0 if either is\n// unparseable (defensive — upgrade will treat that as \"already latest\").\nexport function compareSemver(a: string, b: string): number {\n const pa = parseSemver(a);\n const pb = parseSemver(b);\n if (!pa || !pb) return 0;\n if (pa.major !== pb.major) return pa.major > pb.major ? 1 : -1;\n if (pa.minor !== pb.minor) return pa.minor > pb.minor ? 1 : -1;\n if (pa.patch !== pb.patch) return pa.patch > pb.patch ? 1 : -1;\n // Treat \"no prerelease\" as greater than \"has prerelease\" (1.0.0 > 1.0.0-rc).\n if (pa.pre === pb.pre) return 0;\n if (pa.pre === '') return 1;\n if (pb.pre === '') return -1;\n return pa.pre > pb.pre ? 1 : -1;\n}\n","import { createHash } from 'node:crypto';\nimport { createReadStream } from 'node:fs';\n\n// Parse the GNU coreutils `sha256sum` output format: one entry per line,\n// `<hex> <name>`. The two-space separator is canonical; a leading `*` on\n// the name marks binary mode, which we strip. Blank lines and `#` comments\n// are ignored. Hex is normalized to lowercase so callers can compare with\n// `===`.\nexport function parseSha256Sums(text: string): Map<string, string> {\n const out = new Map<string, string>();\n for (const rawLine of text.split(/\\r?\\n/)) {\n const line = rawLine.trim();\n if (line === '' || line.startsWith('#')) continue;\n const match = line.match(/^([0-9a-fA-F]{64})\\s+\\*?(.+)$/);\n if (!match) continue;\n const hash = match[1]?.toLowerCase();\n const name = match[2]?.trim();\n if (!hash || !name) continue;\n out.set(name, hash);\n }\n return out;\n}\n\nexport function sha256OfFile(path: string): Promise<string> {\n return new Promise((resolve, reject) => {\n const hash = createHash('sha256');\n const stream = createReadStream(path);\n stream.on('error', reject);\n stream.on('data', (chunk) => hash.update(chunk));\n stream.on('end', () => resolve(hash.digest('hex')));\n });\n}\n","// Single source of truth for the embedded CLI version.\n//\n// The value is replaced at build time:\n// - tsdown → via the `define` block in `tsdown.config.ts`\n// - bun → via `--define AITCC_VERSION=...` in `scripts/build-bin.ts`\n//\n// During `pnpm test` / `ts-node` execution the define isn't applied, so we fall\n// back to reading `package.json` at runtime. That path is never hit in the\n// shipped artifacts.\n\ndeclare const AITCC_VERSION: string | undefined;\n\nfunction resolveVersion(): string {\n try {\n // biome-ignore lint/suspicious/noExplicitAny: globalThis lookup for optional build-time define\n const injected = (globalThis as any).AITCC_VERSION as string | undefined;\n if (typeof injected === 'string' && injected.length > 0) return injected;\n } catch {\n // ignore\n }\n try {\n if (typeof AITCC_VERSION === 'string' && AITCC_VERSION.length > 0) {\n return AITCC_VERSION;\n }\n } catch {\n // ignore\n }\n return '0.0.0-dev';\n}\n\nexport const VERSION = resolveVersion();\n","import { execFile } from 'node:child_process';\nimport { chmod, copyFile, rename, unlink, writeFile } from 'node:fs/promises';\nimport { basename, dirname } from 'node:path';\nimport { promisify } from 'node:util';\nimport { defineCommand } from 'citty';\nimport { ExitCode } from '../exit.js';\nimport { fetchLatestRelease, findSha256SumsAsset, versionFromTag } from '../github.js';\nimport { detectPlatform } from '../platform.js';\nimport { compareSemver } from '../semver.js';\nimport { parseSha256Sums, sha256OfFile } from '../sha256.js';\nimport { VERSION } from '../version.js';\n\nconst execFileP = promisify(execFile);\n\n// Distinguishes a Bun-compiled standalone (where `process.execPath` points at\n// the binary itself) from a Node-hosted install (where it points at `node`).\n// Only the former can atomically replace itself; the latter should upgrade\n// via npm.\nfunction isStandaloneBinary(): boolean {\n const exe = basename(process.execPath).toLowerCase();\n return exe.startsWith('aitcc');\n}\n\n// Best-effort cleanup of the `<exePath>.old` left behind by a previous Windows\n// upgrade. Windows can't rename a running exe, so the upgrade flow renames the\n// old binary aside before moving the new one in; the leftover has to be\n// reaped on a later boot when nothing holds it open. Intentionally swallows\n// every error: file may still be in use, perms may differ, or this is POSIX\n// where the path doesn't exist at all. Never logs.\nexport async function cleanupStaleUpgradeArtifacts(\n exePath: string = process.execPath,\n): Promise<void> {\n if (process.platform !== 'win32') return;\n if (!exePath) return;\n try {\n await unlink(`${exePath}.old`);\n } catch {\n // ENOENT, EBUSY, EACCES — all swallowed by design.\n }\n}\n\nexport const upgradeCommand = defineCommand({\n meta: {\n name: 'upgrade',\n description: 'Download the latest release binary from GitHub and replace the current one.',\n },\n args: {\n json: {\n type: 'boolean',\n description: 'Emit machine-readable JSON to stdout.',\n default: false,\n },\n force: {\n type: 'boolean',\n description: 'Re-install even if already on the latest version.',\n default: false,\n },\n 'dry-run': {\n type: 'boolean',\n description: 'Check for updates without downloading or replacing.',\n default: false,\n },\n },\n async run({ args }) {\n const emit = (payload: Record<string, unknown>, human: string) => {\n if (args.json) {\n process.stdout.write(`${JSON.stringify(payload)}\\n`);\n } else {\n process.stdout.write(`${human}\\n`);\n }\n };\n const emitError = (payload: Record<string, unknown>, human: string) => {\n if (args.json) {\n process.stdout.write(`${JSON.stringify({ ok: false, ...payload })}\\n`);\n } else {\n process.stderr.write(`${human}\\n`);\n }\n };\n\n let release: Awaited<ReturnType<typeof fetchLatestRelease>>;\n try {\n release = await fetchLatestRelease();\n } catch (err) {\n emitError(\n { reason: 'network-error', message: (err as Error).message },\n `Failed to query GitHub releases: ${(err as Error).message}`,\n );\n process.exit(ExitCode.NetworkError);\n }\n\n const latest = versionFromTag(release.tag_name);\n const current = VERSION;\n const cmp = compareSemver(latest, current);\n const needsUpdate = cmp > 0 || args.force;\n\n if (!needsUpdate) {\n emit(\n { ok: true, status: 'already-latest', current, latest },\n `Already on the latest version (${current}).`,\n );\n process.exit(ExitCode.UpgradeAlreadyLatest);\n }\n\n if (args['dry-run']) {\n emit(\n { ok: true, status: 'update-available', current, latest, url: release.html_url },\n `Update available: ${current} → ${latest}\\n${release.html_url}`,\n );\n return;\n }\n\n if (!isStandaloneBinary()) {\n emitError(\n {\n reason: 'not-standalone',\n current,\n latest,\n hint: 'npm i -g @ait-co/console-cli@latest',\n },\n [\n 'This install was launched via Node, not the standalone binary.',\n 'Self-upgrade is only supported for the compiled binary.',\n `Run: npm i -g @ait-co/console-cli@latest (currently ${current}, latest ${latest})`,\n ].join('\\n'),\n );\n process.exit(ExitCode.UpgradeUnavailable);\n }\n\n const platform = detectPlatform();\n if (!platform) {\n emitError(\n {\n reason: 'unsupported-platform',\n platform: process.platform,\n arch: process.arch,\n },\n `No prebuilt binary for ${process.platform}/${process.arch}.`,\n );\n process.exit(ExitCode.UpgradeUnavailable);\n }\n\n const asset = release.assets.find((a) => a.name === platform.assetName);\n if (!asset) {\n emitError(\n { reason: 'asset-missing', assetName: platform.assetName, tag: release.tag_name },\n `Release ${release.tag_name} has no asset named ${platform.assetName}. It may still be uploading.`,\n );\n process.exit(ExitCode.UpgradeUnavailable);\n }\n\n const exePath = process.execPath;\n const stagingPath = `${exePath}.new.${Date.now()}`;\n\n if (!args.json) {\n process.stdout.write(`Downloading ${asset.name} (${latest})...\\n`);\n }\n\n try {\n const res = await fetch(asset.browser_download_url);\n if (!res.ok || !res.body) {\n throw new Error(`Download failed: ${res.status} ${res.statusText}`);\n }\n const buf = new Uint8Array(await res.arrayBuffer());\n await writeFile(stagingPath, buf, { mode: 0o755 });\n await chmod(stagingPath, 0o755);\n } catch (err) {\n emitError(\n { reason: 'download-failed', message: (err as Error).message },\n `Failed to download new binary: ${(err as Error).message}`,\n );\n process.exit(ExitCode.NetworkError);\n }\n\n // Verify the downloaded binary against `SHA256SUMS` from the same release\n // before letting it replace the running executable. Mirrors the check in\n // `install.sh`. There is no opt-out; the gate exists because the binary\n // is about to be `chmod 0755` and renamed over `process.execPath`.\n const sumsAsset = findSha256SumsAsset(release);\n if (!sumsAsset) {\n await unlink(stagingPath).catch(() => {});\n emitError(\n { reason: 'sums-missing', tag: release.tag_name },\n `Release ${release.tag_name} has no SHA256SUMS asset. It may still be uploading; retry shortly.`,\n );\n process.exit(ExitCode.UpgradeChecksumFailed);\n }\n\n let expected: string | undefined;\n let actual: string;\n try {\n const sumsRes = await fetch(sumsAsset.browser_download_url);\n if (!sumsRes.ok) {\n throw new Error(`SHA256SUMS download failed: ${sumsRes.status} ${sumsRes.statusText}`);\n }\n const sumsText = await sumsRes.text();\n const sums = parseSha256Sums(sumsText);\n expected = sums.get(platform.assetName);\n actual = (await sha256OfFile(stagingPath)).toLowerCase();\n } catch (err) {\n await unlink(stagingPath).catch(() => {});\n emitError(\n { reason: 'sums-fetch-failed', message: (err as Error).message },\n `Failed to verify checksum: ${(err as Error).message}`,\n );\n process.exit(ExitCode.UpgradeChecksumFailed);\n }\n\n if (!expected) {\n await unlink(stagingPath).catch(() => {});\n emitError(\n { reason: 'sums-no-entry', assetName: platform.assetName, tag: release.tag_name },\n `SHA256SUMS for ${release.tag_name} has no entry for ${platform.assetName}.`,\n );\n process.exit(ExitCode.UpgradeChecksumFailed);\n }\n\n if (expected.toLowerCase() !== actual) {\n await unlink(stagingPath).catch(() => {});\n emitError(\n {\n reason: 'sha256-mismatch',\n assetName: platform.assetName,\n expected: expected.toLowerCase(),\n actual,\n },\n `Checksum mismatch for ${platform.assetName}: expected ${expected.toLowerCase()}, got ${actual}.`,\n );\n process.exit(ExitCode.UpgradeChecksumFailed);\n }\n\n if (!args.json) {\n process.stdout.write('Checksum OK.\\n');\n }\n\n // POSIX backup: copy the current binary so a failed smoke test can be\n // rolled back via `rename(backup, exe)`. We can't hard-link because the\n // staging dir might be on a different fs or have stricter perms. Windows\n // gets backup-for-free via the `<exe>.old` move below.\n const backupPath = process.platform === 'win32' ? null : `${exePath}.bak.${Date.now()}`;\n if (backupPath) {\n try {\n await copyFile(exePath, backupPath);\n } catch (err) {\n await unlink(stagingPath).catch(() => {});\n emitError(\n { reason: 'backup-failed', message: (err as Error).message, exePath, backupPath },\n `Failed to create rollback backup at ${backupPath}: ${(err as Error).message}`,\n );\n process.exit(ExitCode.Generic);\n }\n }\n\n // Atomic replace. POSIX `rename(2)` on the same filesystem is atomic.\n // On Windows a running exe can't be overwritten directly; the staging\n // path is in the same dir, so rename-over works on most shells, and we\n // leave `<exe>.old` handling to a future refinement.\n try {\n if (process.platform === 'win32') {\n await rename(exePath, `${exePath}.old`);\n await rename(stagingPath, exePath);\n } else {\n await rename(stagingPath, exePath);\n }\n } catch (err) {\n if (backupPath) await unlink(backupPath).catch(() => {});\n emitError(\n { reason: 'replace-failed', message: (err as Error).message, exePath, stagingPath },\n `Failed to replace binary at ${exePath}: ${(err as Error).message}`,\n );\n process.exit(ExitCode.Generic);\n }\n\n // Smoke test: invoke the just-installed binary with `--version`. Catches\n // \"valid bytes but won't run\" cases (wrong platform asset, broken\n // entitlement, OS gating) that SHA-256 verification can't see. Strict\n // version-string equality is intentionally NOT checked — a release-\n // pipeline embedding mismatch shouldn't trigger an auto-rollback.\n let smokeFailure: string | null = null;\n try {\n const { stdout } = await execFileP(exePath, ['--version'], {\n timeout: 10_000,\n windowsHide: true,\n });\n if (!stdout.trim()) smokeFailure = 'empty stdout from --version';\n } catch (err) {\n smokeFailure = (err as Error).message;\n }\n\n if (smokeFailure) {\n let rollbackError: string | null = null;\n // Track which rollback step failed so the user message points at the\n // file that actually needs manual intervention.\n let recoveryHint: string | null = null;\n try {\n if (process.platform === 'win32') {\n try {\n await unlink(exePath);\n } catch (err) {\n recoveryHint = `Failed to remove broken binary at ${exePath}; remove it manually then rename ${exePath}.old back to ${exePath}.`;\n throw err;\n }\n await rename(`${exePath}.old`, exePath);\n } else if (backupPath) {\n await rename(backupPath, exePath);\n }\n } catch (err) {\n rollbackError = (err as Error).message;\n if (!recoveryHint) {\n recoveryHint =\n process.platform === 'win32'\n ? `Rename ${exePath}.old back to ${exePath} to restore the previous binary.`\n : `Rename ${backupPath} back to ${exePath} to restore the previous binary.`;\n }\n }\n emitError(\n {\n reason: 'smoke-test-failed',\n message: smokeFailure,\n exePath,\n ...(rollbackError ? { rollbackError, backupPath } : { rolledBack: true }),\n },\n rollbackError\n ? `New binary failed --version smoke test: ${smokeFailure}\\nRollback also failed: ${rollbackError}\\n${recoveryHint}`\n : `New binary failed --version smoke test: ${smokeFailure}\\nReverted to previous binary.`,\n );\n process.exit(ExitCode.UpgradeSmokeTestFailed);\n }\n\n if (backupPath) await unlink(backupPath).catch(() => {});\n\n emit(\n {\n ok: true,\n status: 'upgraded',\n from: current,\n to: latest,\n installedAt: exePath,\n installedIn: dirname(exePath),\n },\n `Upgraded aitcc: ${current} → ${latest}`,\n );\n },\n});\n","import { mkdir, readdir, readFile, rename, stat, unlink, writeFile } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\nimport { fetchLatestReleaseConditional, versionFromTag } from './github.js';\nimport { upgradeCheckPath } from './paths.js';\nimport { compareSemver } from './semver.js';\nimport { VERSION } from './version.js';\n\n// Background \"is there a newer aitcc?\" probe. Rate-limit friendly by design:\n//\n// * At most one network call every 24 hours, regardless of how often the\n// user runs a command that opts in.\n// * Even a failed probe updates the cache timestamp, so a broken network\n// (or a 403 from GitHub) does not loop us back within minutes.\n// * Cache write is stamped BEFORE the network call and promoted atomically\n// via tempfile+rename, so two concurrent `aitcc whoami` invocations can't\n// both escape the throttle and an `exitAfterFlush` mid-write can't leave\n// a truncated JSON file.\n// * Uses a conditional GET with the previous ETag — a 304 response does\n// not consume the anonymous 60/hr core rate-limit bucket.\n// * Fully opt-out via AITCC_NO_UPDATE_CHECK=1 (and implicitly disabled\n// when stderr is not a TTY, so agent-plugin / script consumers never\n// see a stray notice line).\n//\n// The `upgrade` command does NOT use this path — it's the explicit fetch,\n// runs immediately on demand, and its output is the point.\n\nexport const UPDATE_CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;\n\nexport interface UpdateCheckCache {\n readonly lastCheckedAt: string; // ISO 8601\n readonly latestTag?: string;\n readonly etag?: string;\n}\n\nexport async function readCache(): Promise<UpdateCheckCache | null> {\n let raw: string;\n try {\n raw = await readFile(upgradeCheckPath(), 'utf8');\n } catch {\n return null;\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return null;\n }\n if (!parsed || typeof parsed !== 'object') return null;\n const obj = parsed as Record<string, unknown>;\n if (typeof obj.lastCheckedAt !== 'string') return null;\n // Reject non-string optional fields — a hand-edited or cross-version cache\n // with wrong types shouldn't corrupt later string operations.\n if (obj.latestTag !== undefined && typeof obj.latestTag !== 'string') return null;\n if (obj.etag !== undefined && typeof obj.etag !== 'string') return null;\n const result: UpdateCheckCache = {\n lastCheckedAt: obj.lastCheckedAt,\n ...(obj.latestTag !== undefined ? { latestTag: obj.latestTag as string } : {}),\n ...(obj.etag !== undefined ? { etag: obj.etag as string } : {}),\n };\n return result;\n}\n\nexport async function writeCache(entry: UpdateCheckCache): Promise<void> {\n const path = upgradeCheckPath();\n const dir = dirname(path);\n await mkdir(dir, { recursive: true });\n // Best-effort: clean up tempfiles a previous SIGKILL/power-loss left\n // behind between writeFile() and rename() below. Fire-and-forget so\n // it never blocks the actual write; failures are swallowed silently.\n void sweepStaleTempfiles(dir, path);\n // Atomic promote: write to a unique sibling tempfile, then rename. On\n // POSIX rename(2) is atomic within the same filesystem, so a truncated\n // or crash-interrupted write never becomes the canonical cache file.\n // Windows is best-effort (ReplaceFileW is atomic for same-volume targets\n // but not guaranteed cross-process).\n // Unique-per-caller tempfile: pid + wall-clock + random suffix. Prevents\n // concurrent writers in the same process (Date.now() has ms resolution and\n // can collide) from stealing each other's tempfiles between writeFile and\n // rename.\n const tmp = `${path}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 10)}.tmp`;\n try {\n // Cache body is non-secret, but mtime + the ETag are a mild leak of\n // \"when this user last ran aitcc\" on a multi-user box. Match session\n // storage's 0600 mode for consistency and defence in depth.\n await writeFile(tmp, JSON.stringify(entry, null, 2), { mode: 0o600 });\n await rename(tmp, path);\n } catch (err) {\n // If rename failed we may have left the tempfile behind; clean up.\n await unlink(tmp).catch(() => {});\n throw err;\n }\n}\n\nconst TEMPFILE_TTL_MS = 7 * 24 * 60 * 60 * 1000;\n// Matches the suffix shape produced by writeCache:\n// `<path>.<pid>.<ts>.<rand>.tmp`\n// where pid + ts are decimals and rand is base36 (a-z0-9). Anchored so an\n// arbitrary `.tmp` file (someone's editor swap, a future tool's tempfile)\n// is left alone.\nconst TEMPFILE_SUFFIX_RE = /\\.\\d+\\.\\d+\\.[a-z0-9]+\\.tmp$/i;\n\nexport async function sweepStaleTempfiles(dir: string, basePath: string): Promise<void> {\n let entries: string[];\n try {\n entries = await readdir(dir);\n } catch {\n return;\n }\n const cutoff = Date.now() - TEMPFILE_TTL_MS;\n const baseName = basePath.slice(dir.length + 1);\n await Promise.all(\n entries.map(async (name) => {\n // Only sweep tempfiles that this writer would itself produce. Belt-\n // and-suspenders: prefix match on `<base>.` plus the suffix regex.\n if (!name.startsWith(`${baseName}.`)) return;\n if (!TEMPFILE_SUFFIX_RE.test(name)) return;\n const p = join(dir, name);\n try {\n const st = await stat(p);\n if (st.mtimeMs < cutoff) await unlink(p);\n } catch {\n // ENOENT (race), EACCES, etc — best-effort, ignore.\n }\n }),\n );\n}\n\n/** Has the throttle window elapsed since the last recorded check? */\nexport function isDueForCheck(\n cache: UpdateCheckCache | null,\n now: number = Date.now(),\n intervalMs: number = UPDATE_CHECK_INTERVAL_MS,\n): boolean {\n if (!cache) return true;\n const last = Date.parse(cache.lastCheckedAt);\n if (!Number.isFinite(last)) return true;\n // If the system clock jumps backwards (NTP resync, VM resume), treat the\n // cache as stale and re-check. Better to probe once than to be silently\n // stuck until wall-time catches up to `last + interval`.\n if (now < last) return true;\n return now - last >= intervalMs;\n}\n\nexport interface UpdateCheckOptions {\n readonly env?: NodeJS.ProcessEnv;\n readonly isTTY?: boolean;\n readonly now?: number;\n readonly intervalMs?: number;\n}\n\n/**\n * Perform the throttled update check. Returns the final cache entry (for\n * testing) or null when skipped. Never throws — network errors are\n * intentionally swallowed so they never interrupt the foreground command.\n */\nexport async function maybeCheckForUpdate(\n opts: UpdateCheckOptions = {},\n): Promise<UpdateCheckCache | null> {\n const env = opts.env ?? process.env;\n const isTTY = opts.isTTY ?? Boolean(process.stderr.isTTY);\n const now = opts.now ?? Date.now();\n const intervalMs = opts.intervalMs ?? UPDATE_CHECK_INTERVAL_MS;\n\n // Opt-out: any non-empty value that isn't explicitly falsey counts. Matches\n // the loose convention used by CI / DEBUG env vars — \"AITCC_NO_UPDATE_CHECK=true\"\n // works alongside \"=1\".\n const optOut = env.AITCC_NO_UPDATE_CHECK;\n if (optOut && optOut !== '0' && optOut.toLowerCase() !== 'false') return null;\n // Notice lines are targeted at interactive users. Checking stderr (where\n // the notice is written) rather than stdout means `aitcc whoami > out.log`\n // still shows the notice on the terminal, while a fully piped invocation\n // (stderr redirected or captured) is silent.\n if (!isTTY) return null;\n\n const cache = await readCache();\n if (!isDueForCheck(cache, now, intervalMs)) return null;\n\n // Stamp the cache BEFORE the network call so a concurrent `aitcc whoami`\n // that reads AFTER this write sees \"not due\" and skips its probe. (Two\n // racers that both read before either writes will each fire once — the\n // window is a handful of µs and the anonymous GitHub limit plus the\n // ETag-based 304 path keep the damage bounded.) If the probe crashes\n // the process, this placeholder also naturally satisfies the \"failed\n // probes still update the window\" invariant — the next run is bounded\n // by the interval.\n const nowIso = new Date(now).toISOString();\n const placeholder: UpdateCheckCache = {\n lastCheckedAt: nowIso,\n ...(cache?.latestTag !== undefined ? { latestTag: cache.latestTag } : {}),\n ...(cache?.etag !== undefined ? { etag: cache.etag } : {}),\n };\n await writeCache(placeholder).catch(() => {\n // Non-fatal: proceed with the probe, we'll try to write again on\n // completion. The throttle guarantee weakens here but bounded by\n // whatever caused the write to fail in the first place.\n });\n\n const previousEtag = cache?.etag;\n let entry: UpdateCheckCache = placeholder;\n try {\n const result = await fetchLatestReleaseConditional(previousEtag);\n if (result.status === 'not-modified') {\n // 304: server had no new body. Keep the latestTag we already know\n // about, and refresh the ETag only if the server happened to include\n // one on the 304.\n entry = {\n lastCheckedAt: nowIso,\n ...(cache?.latestTag !== undefined ? { latestTag: cache.latestTag } : {}),\n ...(result.etag !== undefined\n ? { etag: result.etag }\n : cache?.etag !== undefined\n ? { etag: cache.etag }\n : {}),\n };\n } else {\n entry = {\n lastCheckedAt: nowIso,\n latestTag: result.release.tag_name,\n ...(result.etag !== undefined ? { etag: result.etag } : {}),\n };\n }\n await writeCache(entry).catch(() => {\n // Placeholder already wrote above; ignore secondary write failure.\n });\n } catch {\n // Network / parse failure. Placeholder is already on disk, so the\n // throttle invariant holds — just skip the second write.\n }\n\n // If the probe failed, `entry` is still the placeholder — so the notice\n // uses the *previous* known latestTag from cache. That's fine: the\n // throttle window bounds staleness to 24h and semver doesn't move\n // backwards, so an out-of-date tag can only under-report a new release,\n // never falsely suggest a newer one than really exists.\n maybeEmitNotice(entry, env);\n return entry;\n}\n\nfunction maybeEmitNotice(entry: UpdateCheckCache, env: NodeJS.ProcessEnv): void {\n if (!entry.latestTag) return;\n // In the dev fallback VERSION (`0.0.0-dev`, from `src/version.ts` when no\n // build-time define is injected) every released tag looks \"newer\" and the\n // notice would fire on every `pnpm dev` run. Skip it instead — developers\n // running from source don't need an upgrade nag.\n if (VERSION.startsWith('0.0.0-dev')) return;\n const latest = versionFromTag(entry.latestTag);\n if (!latest) return;\n if (compareSemver(latest, VERSION) <= 0) return;\n // Respect NO_COLOR — CLAUDE.md documents it as honored across the CLI.\n const dim = env.NO_COLOR ? '' : '\\x1b[2m';\n const reset = env.NO_COLOR ? '' : '\\x1b[0m';\n // Notice goes to stderr so it never pollutes `--json` stdout.\n process.stderr.write(\n `\\n${dim}(aitcc ${latest} is available — run \\`aitcc upgrade\\` to install)${reset}\\n`,\n );\n}\n","import { defineCommand } from 'citty';\nimport { NetworkError, TossApiError } from '../api/http.js';\nimport { fetchConsoleMemberUserInfo } from '../api/me.js';\nimport { getActiveCredentialEmail } from '../auth/credentials.js';\nimport { ExitCode } from '../exit.js';\nimport { exitAfterFlush } from '../flush.js';\nimport { readSession, sessionPathForDiagnostics } from '../session.js';\nimport { maybeCheckForUpdate } from '../update-check.js';\n\n// Resolve the credential source label without ever fetching the password —\n// `whoami` should never trigger a Touch ID / libsecret prompt. Returns\n// `null` when nothing is configured so the JSON shape can stay compact.\nasync function describeCredentialSource(): Promise<{\n source: 'env' | 'keychain' | 'none';\n email: string | null;\n}> {\n const active = await getActiveCredentialEmail().catch(() => null);\n if (!active) return { source: 'none', email: null };\n return { source: active.kind, email: active.email };\n}\n\nfunction formatCredentials(cred: {\n source: 'env' | 'keychain' | 'none';\n email: string | null;\n}): string {\n if (cred.source === 'none') return 'none (run `aitcc login` to save)';\n if (cred.source === 'env') {\n return `env (AITCC_EMAIL${cred.email ? ` = ${cred.email}` : ''})`;\n }\n return `keychain${cred.email ? ` (${cred.email})` : ''}`;\n}\n\n// --json contract (consumed by agent-plugin):\n//\n// Success (session present + — for live mode — reachable):\n// { ok: true, authenticated: true, source: 'live'|'cache', user, capturedAt, ... }\n// Session missing:\n// { ok: true, authenticated: false } exit 10\n// Session expired (console rejected our cookies):\n// { ok: true, authenticated: false, reason: 'session-expired' } exit 10\n// Network failure talking to the console:\n// { ok: false, reason: 'network-error', message } exit 11\n// Any other API / unexpected error:\n// { ok: false, reason: 'api-error', message } exit 17\n//\n// The top-level `ok` is always present and indicates whether the command\n// ran cleanly; `authenticated` is only meaningful when `ok: true`.\n\n// Run the throttled background update check — but bound the wall-clock cost\n// so a slow network never delays the user's whoami output. 500 ms is enough\n// for a 304 (fast path after the first check) and for most 200s; a cold\n// probe that goes long just gets cancelled, and the next whoami within 24h\n// will not retry anyway (cache was written when the probe started).\n//\n// Skipped entirely when `--json` is set — machine consumers (agent-plugin)\n// should never see a \"new version available\" notice line interleaved with\n// their parsed output. The notice in update-check.ts already targets stderr\n// and checks `isTTY`, but belt-and-suspenders costs nothing here.\nasync function runBackgroundUpdateCheck(json: boolean): Promise<void> {\n if (json) return;\n const timeoutMs = 500;\n await Promise.race([\n maybeCheckForUpdate().catch(() => null),\n new Promise<null>((resolve) => {\n const t = setTimeout(() => resolve(null), timeoutMs);\n if (typeof t.unref === 'function') t.unref();\n }),\n ]);\n}\n\nexport const whoamiCommand = defineCommand({\n meta: {\n name: 'whoami',\n description: 'Show the currently authenticated user (live from the console API by default).',\n },\n args: {\n json: {\n type: 'boolean',\n description: 'Emit machine-readable JSON to stdout.',\n default: false,\n },\n offline: {\n type: 'boolean',\n description: 'Skip the live API call and read only the cached session summary.',\n default: false,\n },\n },\n async run({ args }) {\n const session = await readSession();\n const cred = await describeCredentialSource();\n\n if (!session) {\n if (args.json) {\n process.stdout.write(\n `${JSON.stringify({\n ok: true,\n authenticated: false,\n credentialSource: cred.source,\n ...(cred.email ? { credentialEmail: cred.email } : {}),\n })}\\n`,\n );\n } else {\n process.stderr.write('Not logged in. Run `aitcc login` to start a session.\\n');\n process.stderr.write(`Session file checked: ${sessionPathForDiagnostics()}\\n`);\n process.stderr.write(`Credentials: ${formatCredentials(cred)}\\n`);\n }\n return exitAfterFlush(ExitCode.NotAuthenticated);\n }\n\n if (args.offline) {\n if (args.json) {\n process.stdout.write(\n `${JSON.stringify({\n ok: true,\n authenticated: true,\n source: 'cache',\n user: session.user,\n capturedAt: session.capturedAt,\n credentialSource: cred.source,\n ...(cred.email ? { credentialEmail: cred.email } : {}),\n })}\\n`,\n );\n return exitAfterFlush(ExitCode.Ok);\n }\n const label = session.user.displayName\n ? `${session.user.displayName} <${session.user.email}>`\n : session.user.email;\n process.stdout.write(`Logged in as ${label} (cached)\\n`);\n process.stdout.write(`Credentials: ${formatCredentials(cred)}\\n`);\n process.stdout.write(`Session captured: ${session.capturedAt}\\n`);\n await runBackgroundUpdateCheck(args.json);\n return exitAfterFlush(ExitCode.Ok);\n }\n\n try {\n const info = await fetchConsoleMemberUserInfo(session.cookies);\n if (args.json) {\n process.stdout.write(\n `${JSON.stringify({\n ok: true,\n authenticated: true,\n source: 'live',\n user: {\n id: String(info.id),\n bizUserNo: info.bizUserNo,\n name: info.name,\n email: info.email,\n role: info.role,\n },\n workspaces: info.workspaces.map((w) => ({\n workspaceId: w.workspaceId,\n workspaceName: w.workspaceName,\n role: w.role,\n })),\n capturedAt: session.capturedAt,\n credentialSource: cred.source,\n ...(cred.email ? { credentialEmail: cred.email } : {}),\n })}\\n`,\n );\n return exitAfterFlush(ExitCode.Ok);\n }\n process.stdout.write(`Logged in as ${info.name} <${info.email}> (${info.role})\\n`);\n process.stdout.write(`Credentials: ${formatCredentials(cred)}\\n`);\n if (info.workspaces.length > 0) {\n process.stdout.write('Workspaces:\\n');\n for (const w of info.workspaces) {\n process.stdout.write(` - ${w.workspaceName} (id ${w.workspaceId}, ${w.role})\\n`);\n }\n }\n await runBackgroundUpdateCheck(args.json);\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n if (err instanceof TossApiError && err.isAuthError) {\n if (args.json) {\n process.stdout.write(\n `${JSON.stringify({\n ok: true,\n authenticated: false,\n reason: 'session-expired',\n errorCode: err.errorCode,\n credentialSource: cred.source,\n ...(cred.email ? { credentialEmail: cred.email } : {}),\n })}\\n`,\n );\n } else {\n process.stderr.write('Session is no longer valid. Run `aitcc login` again.\\n');\n process.stderr.write(`Credentials: ${formatCredentials(cred)}\\n`);\n }\n return exitAfterFlush(ExitCode.NotAuthenticated);\n }\n if (err instanceof NetworkError) {\n // Network failures are surfaced as hard errors — we don't silently\n // fall back to the cache because agent-plugin callers branching on\n // exit code would miss the degradation. Users who explicitly want\n // the cached identity have `--offline` for that.\n if (args.json) {\n process.stdout.write(\n `${JSON.stringify({ ok: false, reason: 'network-error', message: err.message })}\\n`,\n );\n } else {\n process.stderr.write(\n `Network error reaching the console API: ${err.message}. Use \\`aitcc whoami --offline\\` for the cached identity.\\n`,\n );\n }\n return exitAfterFlush(ExitCode.NetworkError);\n }\n if (args.json) {\n process.stdout.write(\n `${JSON.stringify({ ok: false, reason: 'api-error', message: (err as Error).message })}\\n`,\n );\n } else {\n process.stderr.write(`Unexpected error: ${(err as Error).message}\\n`);\n }\n return exitAfterFlush(ExitCode.ApiError);\n }\n },\n});\n","import { defineCommand } from 'citty';\nimport { NetworkError, TossApiError } from '../api/http.js';\nimport { fetchConsoleMemberUserInfo } from '../api/me.js';\nimport {\n agreeWorkspaceTerms,\n fetchWorkspaceDetail,\n fetchWorkspacePartner,\n fetchWorkspaceSegments,\n fetchWorkspaceTerms,\n WORKSPACE_TERM_TYPES,\n type WorkspaceTerm,\n type WorkspaceTermType,\n} from '../api/workspaces.js';\nimport { ExitCode } from '../exit.js';\nimport { exitAfterFlush } from '../flush.js';\nimport { readSession, setCurrentWorkspaceId } from '../session.js';\nimport {\n emitFailureFromError,\n emitJson,\n emitNotAuthenticated,\n parsePositiveInt,\n printContextHeader,\n resolveWorkspaceContext,\n} from './_shared.js';\n\n// --json contract (consumed by agent-plugin):\n//\n// workspace ls:\n// { ok: true, workspaces: [{workspaceId, workspaceName, role, current}] }\n// ^--- matches currentWorkspaceId\n// workspace use <id>:\n// { ok: true, workspaceId, workspaceName } exit 0\n// { ok: false, reason: 'not-found', workspaceId } exit 2\n// { ok: false, reason: 'invalid-id', message } exit 2\n// workspace show [--workspace <id>]:\n// { ok: true, workspaceId, workspaceName, extra } exit 0\n// { ok: false, reason: 'no-workspace-selected' } exit 2\n// { ok: false, reason: 'invalid-id', message } exit 2\n// workspace partner [--workspace <id>]:\n// { ok: true, workspaceId, registered, approvalType,\n// rejectMessage, partner } exit 0\n// { ok: false, reason: 'no-workspace-selected' } exit 2\n// { ok: false, reason: 'invalid-id', message } exit 2\n// workspace terms [--type TYPE] [--workspace <id>]:\n// { ok: true, workspaceId, type, terms: (WorkspaceTerm & {blocks: string[]})[] } exit 0 (single type)\n// { ok: true, workspaceId, byType: { TYPE: (WorkspaceTerm & {blocks: string[]})[] } } exit 0 (default — every bucket)\n// { ok: false, reason: 'invalid-type', allowed: TYPES[] } exit 2\n// { ok: false, reason: 'no-workspace-selected' } exit 2\n// { ok: false, reason: 'invalid-id', message } exit 2\n//\n// `blocks` is a per-type list of feature surfaces that become unavailable\n// while the bucket is un-agreed. The same list appears on every term in\n// the bucket — it's a property of the bucket, not the individual term.\n//\n// workspace terms agree <type> [--workspace <id>]:\n// workspace terms agree --all [--workspace <id>]:\n// { ok: true, partial: false, workspaceId, agreed: AgreedTerm[],\n// unchanged: AgreedTerm[], failed: [] } exit 0 (full success or all-already-agreed)\n// { ok: false, partial: true, workspaceId, agreed: AgreedTerm[],\n// unchanged: AgreedTerm[],\n// failed: { termsId, revisionId, type, message }[] } exit 1 (some buckets succeeded, others failed)\n// exit 17 (full transport-level failure handled by emitFailureFromError)\n// { ok: false, reason: 'argument-required', message } exit 2 (no <type> and no --all)\n// { ok: false, reason: 'mutually-exclusive', message } exit 2 (<type> and --all both given)\n// { ok: false, reason: 'unknown-term-type', given, allowed: TYPES[] } exit 2 (positional <type> not in enum)\n// { ok: false, reason: 'no-workspace-selected' } exit 2\n// { ok: false, reason: 'invalid-id', message } exit 2\n//\n// `AgreedTerm = { termsId, revisionId, type, title }`. `agreed` lists what\n// this run flipped from pending → agreed; `unchanged` lists terms that\n// were already agreed at fetch time and got skipped (idempotent path).\n// `failed` lists transport/server failures per term batch — populated only\n// when `partial === true`. `ok` is a single-bool decision flag for\n// agent-plugin (`ok && !failed.length`-style branching is unnecessary):\n// `ok: true` means every requested bucket either succeeded or was already\n// agreed; `ok: false, partial: true` means at least one bucket failed and\n// the caller should inspect `failed`.\n//\n// workspace segments ls [--category <cat>] [--search <text>] [--page N] [--workspace <id>]:\n// { ok: true, workspaceId, category, segments: [...], totalPage, currentPage } exit 0\n// { ok: false, reason: 'invalid-page', message } exit 2\n// { ok: false, reason: 'no-workspace-selected' } exit 2\n// { ok: false, reason: 'invalid-id', message } exit 2\n//\n// Every workspace subcommand inherits the standard auth failure modes from\n// whoami: { ok: true, authenticated: false } exit 10, network-error exit 11,\n// api-error exit 17. All JSON writes go through the shared `emitJson` so the\n// single-line-with-trailing-newline invariant is enforced in one place.\n\n// Formatting helper for the plain-text `show` output. `--json` is the\n// structured consumption path; this is a crude fallback so a human can\n// skim the response at a glance. Objects/arrays collapse to a single\n// JSON line on purpose — nested structures are rare in the detail\n// response and unreadable in any form without real tabular formatting.\nfunction formatScalar(v: unknown): string {\n if (v === null) return 'null';\n if (typeof v === 'string' || typeof v === 'number' || typeof v === 'boolean') return String(v);\n return JSON.stringify(v);\n}\n\nconst lsCommand = defineCommand({\n meta: {\n name: 'ls',\n description: 'List workspaces the current user has access to.',\n },\n args: {\n json: { type: 'boolean', description: 'Emit machine-readable JSON to stdout.', default: false },\n },\n async run({ args }) {\n const session = await readSession();\n if (!session) {\n emitNotAuthenticated(args.json);\n return exitAfterFlush(ExitCode.NotAuthenticated);\n }\n try {\n const info = await fetchConsoleMemberUserInfo(session.cookies);\n const current = session.currentWorkspaceId;\n if (args.json) {\n const workspaces = info.workspaces.map((w) => ({\n workspaceId: w.workspaceId,\n workspaceName: w.workspaceName,\n role: w.role,\n current: w.workspaceId === current,\n }));\n emitJson({ ok: true, workspaces });\n return exitAfterFlush(ExitCode.Ok);\n }\n if (info.workspaces.length === 0) {\n process.stdout.write('No workspaces.\\n');\n return exitAfterFlush(ExitCode.Ok);\n }\n for (const w of info.workspaces) {\n const marker = w.workspaceId === current ? '* ' : ' ';\n process.stdout.write(`${marker}${w.workspaceId} ${w.workspaceName} (${w.role})\\n`);\n }\n if (current === undefined) {\n process.stderr.write('No workspace selected. Run `aitcc workspace use <id>`.\\n');\n }\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\nconst useCommand = defineCommand({\n meta: {\n name: 'use',\n description: 'Select the current workspace by ID. Subsequent commands use this.',\n },\n args: {\n id: { type: 'positional', description: 'Workspace ID', required: true },\n json: { type: 'boolean', description: 'Emit machine-readable JSON to stdout.', default: false },\n },\n async run({ args }) {\n const raw = String(args.id);\n const parsed = parsePositiveInt(raw);\n if (parsed === null) {\n const message = `workspace id must be a positive integer (got ${raw})`;\n if (args.json) {\n emitJson({ ok: false, reason: 'invalid-id', message });\n } else {\n process.stderr.write(`${message}\\n`);\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n\n const session = await readSession();\n if (!session) {\n emitNotAuthenticated(args.json);\n return exitAfterFlush(ExitCode.NotAuthenticated);\n }\n\n // Validate against the user's actual workspace list before writing the\n // selection. `members/me/user-info` is the live list, not the stored\n // one, so a workspace added after login is visible here. Only the\n // detail endpoint (not called here) could still 403 after this check.\n try {\n const info = await fetchConsoleMemberUserInfo(session.cookies);\n const match = info.workspaces.find((w) => w.workspaceId === parsed);\n if (!match) {\n if (args.json) {\n emitJson({ ok: false, reason: 'not-found', workspaceId: parsed });\n } else {\n process.stderr.write(\n `Workspace ${parsed} is not accessible from this account. Run \\`aitcc workspace ls\\` to see available workspaces.\\n`,\n );\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n // `setCurrentWorkspaceId` returns null only if the session disappeared\n // between our `readSession` above and here (e.g. concurrent logout).\n // Surface that as \"not logged in\" for consistency with other commands\n // instead of silently pretending the write landed. For v1 sessions\n // this is a double-read (readSession migrates, then this helper reads\n // again before writing) — benign, and preferable to threading the\n // already-loaded session through a new parameter just to save one IO.\n const updated = await setCurrentWorkspaceId(parsed);\n if (updated === null) {\n emitNotAuthenticated(args.json);\n return exitAfterFlush(ExitCode.NotAuthenticated);\n }\n if (args.json) {\n emitJson({\n ok: true,\n workspaceId: match.workspaceId,\n workspaceName: match.workspaceName,\n });\n } else {\n process.stdout.write(`Using workspace ${match.workspaceId} (${match.workspaceName}).\\n`);\n }\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\nconst showCommand = defineCommand({\n meta: {\n name: 'show',\n description: 'Show details of the selected workspace (or the one passed with --workspace).',\n },\n args: {\n workspace: {\n type: 'string',\n description: 'Workspace ID to inspect. Defaults to the selected workspace.',\n },\n json: { type: 'boolean', description: 'Emit machine-readable JSON to stdout.', default: false },\n },\n async run({ args }) {\n const ctx = await resolveWorkspaceContext(args);\n if (!ctx) return;\n const { session, workspaceId } = ctx;\n printContextHeader(ctx, { json: args.json });\n\n try {\n const detail = await fetchWorkspaceDetail(workspaceId, session.cookies);\n if (args.json) {\n emitJson({\n ok: true,\n workspaceId: detail.workspaceId,\n workspaceName: detail.workspaceName,\n extra: detail.extra ?? {},\n });\n return exitAfterFlush(ExitCode.Ok);\n }\n process.stdout.write(`Workspace ${detail.workspaceId}: ${detail.workspaceName}\\n`);\n if (detail.extra) {\n for (const [k, v] of Object.entries(detail.extra)) {\n process.stdout.write(` ${k}: ${formatScalar(v)}\\n`);\n }\n }\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\nconst partnerCommand = defineCommand({\n meta: {\n name: 'partner',\n description: 'Show the partner (billing/payout) registration state for the selected workspace.',\n },\n args: {\n workspace: {\n type: 'string',\n description: 'Workspace ID to inspect. Defaults to the selected workspace.',\n },\n json: { type: 'boolean', description: 'Emit machine-readable JSON to stdout.', default: false },\n },\n async run({ args }) {\n const ctx = await resolveWorkspaceContext(args);\n if (!ctx) return;\n const { session, workspaceId } = ctx;\n printContextHeader(ctx, { json: args.json });\n\n try {\n const state = await fetchWorkspacePartner(workspaceId, session.cookies);\n if (args.json) {\n emitJson({\n ok: true,\n workspaceId,\n registered: state.registered,\n approvalType: state.approvalType,\n rejectMessage: state.rejectMessage,\n partner: state.partner,\n });\n return exitAfterFlush(ExitCode.Ok);\n }\n process.stdout.write(`Workspace ${workspaceId} partner:\\n`);\n process.stdout.write(` registered: ${state.registered}\\n`);\n process.stdout.write(` approvalType: ${state.approvalType ?? 'null'}\\n`);\n if (state.rejectMessage) {\n process.stdout.write(` rejectMessage: ${state.rejectMessage}\\n`);\n }\n if (state.partner) {\n process.stdout.write(' partner:\\n');\n for (const [k, v] of Object.entries(state.partner)) {\n process.stdout.write(` ${k}: ${formatScalar(v)}\\n`);\n }\n }\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\nfunction formatTermLines(term: WorkspaceTerm): string {\n // One agreement per line in the plain-text rendering; the title + a\n // [agreed]/[pending] tag is the useful signal for a human operator.\n // Keep the contentsUrl on a second indented line so ops can Ctrl-click\n // to review it directly without switching to --json.\n const tag = term.isAgreed ? '[agreed]' : '[pending]';\n const req = term.required ? ' required' : '';\n return ` ${tag}${req} ${term.title}\\n ${term.contentsUrl}\\n`;\n}\n\n// What each term-bucket gates if it is left un-agreed. The pairings come\n// from docs/api/_error-codes.md \"Auth / 약관 family\" (4037/4039/4040/4099/5001\n// rows) cross-referenced with docs/api/workspaces.md `<type>` descriptions\n// — server is the source of truth for the term enum, this table only\n// documents the consumer-side feature surface each enum value gates.\n//\n// Surfaced both in plain-text output (`blocks if missing: …`) and in the\n// `--json` payload (`blocks: string[]`). Order within an entry mirrors the\n// spec PR description so dog-food output stays diffable. Strings are\n// human-readable feature names, not necessarily existing CLI command names\n// (some gated features are still on the roadmap).\nexport const TERM_BLOCKS: Record<WorkspaceTermType, readonly string[]> = {\n TOSS_LOGIN: ['toss login scope for mini-apps', 'app register (login configured apps)'],\n BIZ_WORKSPACE: ['app register', 'app deploy', 'workspace-level admin'],\n TOSS_PROMOTION_MONEY: ['promotion money campaign management'],\n IAA: ['ad campaign management'],\n IAP: ['iap product register', 'iap config'],\n};\n\nexport function blocksFor(type: WorkspaceTermType): readonly string[] {\n return TERM_BLOCKS[type] ?? [];\n}\n\nexport function formatBlocksHint(type: WorkspaceTermType, agreed: boolean): string {\n // Skip the hint entirely when we have no mapping for this type — keeps\n // the output clean if the server adds a new bucket before we update\n // the table. The spec calls this the \"simpler\" path over emitting\n // `blocks if missing: -`.\n const blocks = blocksFor(type);\n if (blocks.length === 0) return '';\n // Only highlight when the term is pending — agreed terms still get the\n // line for context but in default styling. NO_COLOR + non-TTY both\n // disable the escape; isTTY gate keeps ANSI out of pipes.\n const useColor = !agreed && process.stdout.isTTY && !process.env.NO_COLOR;\n const yellow = useColor ? '\\x1b[33m' : '';\n const reset = useColor ? '\\x1b[0m' : '';\n return ` ${yellow}blocks if missing: ${blocks.join(', ')}${reset}\\n`;\n}\n\nconst termsShowCommand = defineCommand({\n meta: {\n name: 'show',\n description:\n 'Show the console terms-of-agreement state that gate workspace-level features (Toss login, IAP, IAA, biz workspace, promotion money).',\n },\n args: {\n type: {\n type: 'string',\n description: `Term bucket to inspect: ${WORKSPACE_TERM_TYPES.join(' | ')}. Omit to query every bucket.`,\n },\n workspace: {\n type: 'string',\n description: 'Workspace ID to inspect. Defaults to the selected workspace.',\n },\n json: { type: 'boolean', description: 'Emit machine-readable JSON to stdout.', default: false },\n },\n async run({ args }) {\n const ctx = await resolveWorkspaceContext(args);\n if (!ctx) return;\n const { session, workspaceId } = ctx;\n\n const typesToQuery: readonly WorkspaceTermType[] = (() => {\n if (!args.type) return WORKSPACE_TERM_TYPES;\n const raw = String(args.type).toUpperCase();\n if ((WORKSPACE_TERM_TYPES as readonly string[]).includes(raw)) {\n return [raw as WorkspaceTermType];\n }\n return [];\n })();\n if (typesToQuery.length === 0) {\n const message = `--type must be one of: ${WORKSPACE_TERM_TYPES.join(', ')}`;\n if (args.json) {\n emitJson({ ok: false, reason: 'invalid-type', allowed: [...WORKSPACE_TERM_TYPES] });\n } else {\n process.stderr.write(`${message}\\n`);\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n printContextHeader(ctx, { json: args.json });\n\n try {\n // Single-type path keeps the JSON payload flat; --all (or the\n // default) groups results by type so consumers don't have to call\n // five times. Fire them in parallel — each is an independent GET\n // and the server has no cross-bucket rate-limit we've observed.\n const results = await Promise.all(\n typesToQuery.map(\n async (t) => [t, await fetchWorkspaceTerms(workspaceId, t, session.cookies)] as const,\n ),\n );\n\n // Attach the static `blocks` feature-gate list to every term. Same\n // list per bucket since it's a property of the term-type, not the\n // individual term entry — duplicating it on each row keeps the\n // JSON contract uniform (consumers don't have to look up the\n // bucket key separately).\n const enrich = (\n type: WorkspaceTermType,\n terms: readonly WorkspaceTerm[],\n ): readonly (WorkspaceTerm & { readonly blocks: readonly string[] })[] => {\n const blocks = blocksFor(type);\n return terms.map((t) => ({ ...t, blocks }));\n };\n\n if (typesToQuery.length === 1) {\n const [type, terms] = results[0] as readonly [WorkspaceTermType, readonly WorkspaceTerm[]];\n if (args.json) {\n emitJson({ ok: true, workspaceId, type, terms: enrich(type, terms) });\n return exitAfterFlush(ExitCode.Ok);\n }\n process.stdout.write(`Workspace ${workspaceId} terms (${type}):\\n`);\n if (terms.length === 0) {\n process.stdout.write(' (no terms required)\\n');\n } else {\n for (const t of terms) {\n process.stdout.write(formatTermLines(t));\n process.stdout.write(formatBlocksHint(type, t.isAgreed));\n }\n }\n return exitAfterFlush(ExitCode.Ok);\n }\n\n // --all path\n const byType: Record<\n string,\n readonly (WorkspaceTerm & { readonly blocks: readonly string[] })[]\n > = {};\n for (const [t, terms] of results) byType[t] = enrich(t, terms);\n if (args.json) {\n emitJson({ ok: true, workspaceId, byType });\n return exitAfterFlush(ExitCode.Ok);\n }\n for (const [type, terms] of results) {\n process.stdout.write(`\\n[${type}]\\n`);\n if (terms.length === 0) {\n process.stdout.write(' (no terms required)\\n');\n } else {\n for (const t of terms) {\n process.stdout.write(formatTermLines(t));\n process.stdout.write(formatBlocksHint(type, t.isAgreed));\n }\n }\n }\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\n// Per-term agreement record surfaced in the `agree` JSON payload. We carry\n// `(termsId, revisionId)` so consumers can correlate with `terms show`\n// output, plus `type` (bucket) + `title` so dog-food output and human\n// messages don't have to look the title back up. The shape is the same\n// for `agreed` and `unchanged` buckets — only the bucket says whether this\n// run flipped the state.\nexport interface AgreedTerm {\n readonly termsId: number;\n readonly revisionId: number;\n readonly type: WorkspaceTermType;\n readonly title: string;\n}\n\nexport interface FailedTerm {\n readonly termsId: number;\n readonly revisionId: number;\n readonly type: WorkspaceTermType;\n readonly message: string;\n}\n\nexport function describeAgreeError(err: unknown): string {\n // `TossApiError.message` already embeds errorCode + reason + HTTP status\n // (`\"Toss API error <code>: <reason> (HTTP <status>)\"`), so we don't tack\n // on a redundant `(errorCode: ...)` suffix.\n if (err instanceof TossApiError) return err.message;\n if (err instanceof NetworkError) return `network error: ${err.message}`;\n if (err instanceof Error) return err.message;\n return String(err);\n}\n\n// Pure validation of the (positional, --all) arg pair. Extracted so the\n// command-level mutually-exclusive / argument-required / unknown-term-type\n// branches can be unit-tested without invoking the full citty `run`.\n// Case-insensitive on the positional so `iap` and `IAP` both work.\nexport type AgreeArgsValidation =\n | { readonly ok: true; readonly types: readonly WorkspaceTermType[] }\n | { readonly ok: false; readonly reason: 'argument-required'; readonly message: string }\n | { readonly ok: false; readonly reason: 'mutually-exclusive'; readonly message: string }\n | {\n readonly ok: false;\n readonly reason: 'unknown-term-type';\n readonly given: string;\n readonly allowed: readonly WorkspaceTermType[];\n // `message` here is for stderr only — the `--json` emit path intentionally\n // omits it and surfaces `given` + `allowed` instead so the agent-plugin\n // can render its own message without parsing prose.\n readonly message: string;\n };\n\nexport function validateAgreeArgs(input: {\n positional: string;\n all: boolean;\n}): AgreeArgsValidation {\n const hasPositional = input.positional.length > 0;\n if (!hasPositional && !input.all) {\n return {\n ok: false,\n reason: 'argument-required',\n message: 'Specify a term bucket (e.g. `aitcc workspace terms agree IAP`) or pass --all.',\n };\n }\n if (hasPositional && input.all) {\n return {\n ok: false,\n reason: 'mutually-exclusive',\n message: '<type> and --all are mutually exclusive — pass one or the other, not both.',\n };\n }\n if (input.all) {\n return { ok: true, types: WORKSPACE_TERM_TYPES };\n }\n const upper = input.positional.toUpperCase();\n if (!(WORKSPACE_TERM_TYPES as readonly string[]).includes(upper)) {\n return {\n ok: false,\n reason: 'unknown-term-type',\n given: input.positional,\n allowed: [...WORKSPACE_TERM_TYPES],\n message: `Unknown term bucket: ${input.positional}. Allowed: ${WORKSPACE_TERM_TYPES.join(', ')}.`,\n };\n }\n return { ok: true, types: [upper as WorkspaceTermType] };\n}\n\n// Pure orchestration: given the fetched buckets and an injectable agree\n// function, partition terms into agreed/unchanged/failed. Server is NOT\n// idempotent — already-agreed terms are dropped on the client side, and\n// each bucket's agree call is independent (one bucket failing does not\n// block the others).\nexport interface BucketSnapshot {\n readonly type: WorkspaceTermType;\n readonly terms: readonly WorkspaceTerm[];\n}\n\nexport interface AgreeOutcome {\n readonly agreed: readonly AgreedTerm[];\n readonly unchanged: readonly AgreedTerm[];\n readonly failed: readonly FailedTerm[];\n}\n\nexport async function processAgreeBuckets(\n buckets: readonly BucketSnapshot[],\n submit: (\n type: WorkspaceTermType,\n pending: readonly { termsId: number; revisionId: number }[],\n ) => Promise<void>,\n): Promise<AgreeOutcome> {\n const agreed: AgreedTerm[] = [];\n const unchanged: AgreedTerm[] = [];\n const failed: FailedTerm[] = [];\n\n for (const { type, terms } of buckets) {\n for (const t of terms) {\n if (t.isAgreed) {\n unchanged.push({ termsId: t.termsId, revisionId: t.revisionId, type, title: t.title });\n }\n }\n const pending = terms.filter((t) => !t.isAgreed);\n if (pending.length === 0) continue;\n try {\n await submit(\n type,\n pending.map((t) => ({ termsId: t.termsId, revisionId: t.revisionId })),\n );\n for (const t of pending) {\n agreed.push({ termsId: t.termsId, revisionId: t.revisionId, type, title: t.title });\n }\n } catch (err) {\n const message = describeAgreeError(err);\n for (const t of pending) {\n failed.push({ termsId: t.termsId, revisionId: t.revisionId, type, message });\n }\n }\n }\n return { agreed, unchanged, failed };\n}\n\nconst termsAgreeCommand = defineCommand({\n meta: {\n name: 'agree',\n description:\n 'Agree to workspace-level terms. Pass a single bucket as positional argument, or --all to agree to every pending bucket. Already-agreed terms are skipped (idempotent).',\n },\n args: {\n type: {\n type: 'positional',\n description: `Term bucket to agree to (positional, NOT --type): ${WORKSPACE_TERM_TYPES.join(' | ')}. Asymmetric with \\`terms show --type ...\\` because agree is a one-shot intent — pass the bucket name directly or --all.`,\n required: false,\n },\n all: {\n type: 'boolean',\n description:\n 'Agree to every pending bucket. Mutually exclusive with the positional argument.',\n default: false,\n },\n workspace: {\n type: 'string',\n description: 'Workspace ID to inspect. Defaults to the selected workspace.',\n },\n json: { type: 'boolean', description: 'Emit machine-readable JSON to stdout.', default: false },\n },\n async run({ args }) {\n const validation = validateAgreeArgs({\n positional: args.type !== undefined ? String(args.type) : '',\n all: Boolean(args.all),\n });\n if (!validation.ok) {\n if (args.json) {\n if (validation.reason === 'unknown-term-type') {\n emitJson({\n ok: false,\n reason: 'unknown-term-type',\n given: validation.given,\n allowed: validation.allowed,\n });\n } else {\n emitJson({ ok: false, reason: validation.reason, message: validation.message });\n }\n } else {\n process.stderr.write(`${validation.message}\\n`);\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n\n const ctx = await resolveWorkspaceContext(args);\n if (!ctx) return;\n const { session, workspaceId } = ctx;\n printContextHeader(ctx, { json: args.json });\n\n try {\n // Fetch every requested bucket in parallel — same pattern as `show`.\n // The agree endpoint is per-bucket but the GET isn't, and we need\n // the (termsId, revisionId) + isAgreed snapshot before deciding\n // what to submit (server is NOT idempotent — re-submitting an\n // already-agreed term returns 500).\n const buckets: readonly BucketSnapshot[] = await Promise.all(\n validation.types.map(async (type) => ({\n type,\n terms: await fetchWorkspaceTerms(workspaceId, type, session.cookies),\n })),\n );\n\n const { agreed, unchanged, failed } = await processAgreeBuckets(\n buckets,\n async (_type, pending) => {\n await agreeWorkspaceTerms(workspaceId, pending, session.cookies);\n },\n );\n\n const partial = failed.length > 0;\n if (args.json) {\n emitJson({\n ok: !partial,\n partial,\n workspaceId,\n agreed,\n unchanged,\n failed,\n });\n } else if (agreed.length === 0 && failed.length === 0) {\n process.stdout.write(\n unchanged.length === 0\n ? '(no terms to agree to)\\n'\n : `Already agreed: ${unchanged.length} term(s) — nothing to do.\\n`,\n );\n } else {\n if (agreed.length > 0) {\n // Group by type for a tighter rendering.\n const byType = new Map<WorkspaceTermType, AgreedTerm[]>();\n for (const a of agreed) {\n const arr = byType.get(a.type) ?? [];\n arr.push(a);\n byType.set(a.type, arr);\n }\n for (const [type, items] of byType) {\n const blocks = blocksFor(type);\n const blocksHint = blocks.length > 0 ? ` (unblocks: ${blocks.join(', ')})` : '';\n process.stdout.write(`✓ Agreed to ${items.length} term(s) in ${type}${blocksHint}\\n`);\n for (const a of items) {\n process.stdout.write(` - ${a.title}\\n`);\n }\n }\n }\n if (unchanged.length > 0) {\n process.stdout.write(`(skipped ${unchanged.length} already-agreed term(s))\\n`);\n }\n if (failed.length > 0) {\n process.stderr.write(`✗ ${failed.length} term(s) failed:\\n`);\n for (const f of failed) {\n process.stderr.write(` - [${f.type}] termsId=${f.termsId}: ${f.message}\\n`);\n }\n }\n }\n return exitAfterFlush(partial ? ExitCode.Generic : ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\nconst termsCommand = defineCommand({\n meta: {\n name: 'terms',\n description:\n 'Show or agree to console terms-of-agreement that gate workspace-level features (Toss login, IAP, IAA, biz workspace, promotion money).',\n },\n // citty's `findSubCommandIndex` walks the parent's argsDef to decide\n // whether `--flag VALUE` consumes one or two raw args. If the parent\n // doesn't declare a string-typed flag, the VALUE half gets read as a\n // positional and citty treats it as the subcommand name → \"Unknown\n // command 36577\". Mirror the `show` subcommand's value-flags here so\n // `aitcc workspace terms --workspace 36577 --type IAP` still routes\n // bare → show via `default` (the previous, no-subtree behaviour). These\n // declarations are PURE ROUTING SCAFFOLDING — the parent never reads\n // them; `runCommand` re-parses raw args inside the resolved subcommand.\n // Importantly, `--type` here does NOT mean `terms agree --type IAP` is\n // valid: agree takes the bucket as a positional. The flag name overlap\n // is incidental — see `termsAgreeCommand.args.type` for the real shape.\n args: {\n type: {\n type: 'string',\n description: 'Forwarded to `show` (routing only — see `agree --help`).',\n },\n workspace: { type: 'string', description: 'Forwarded to the resolved subcommand.' },\n json: { type: 'boolean', description: 'Forwarded to the resolved subcommand.', default: false },\n },\n subCommands: {\n show: termsShowCommand,\n agree: termsAgreeCommand,\n },\n // Bare `aitcc workspace terms` (no subcommand) routes to `show` so the\n // existing read-only behaviour is preserved.\n default: 'show',\n});\n\nconst segmentsLsCommand = defineCommand({\n meta: {\n name: 'ls',\n description: 'List user segments in the selected workspace (the 세그먼트 menu).',\n },\n args: {\n workspace: {\n type: 'string',\n description: 'Workspace ID. Defaults to the selected workspace.',\n },\n category: {\n type: 'string',\n description: 'Category bucket (tab). Defaults to \"생성된 세그먼트\" — the UI\\'s initial tab.',\n },\n search: { type: 'string', description: 'Name-contains filter. Empty matches everything.' },\n page: { type: 'string', description: 'Page number (0-indexed).', default: '0' },\n json: { type: 'boolean', description: 'Emit machine-readable JSON to stdout.', default: false },\n },\n async run({ args }) {\n const ctx = await resolveWorkspaceContext(args);\n if (!ctx) return;\n const { session, workspaceId } = ctx;\n\n const pageRaw = String(args.page);\n const pageNum = Number(pageRaw);\n if (!Number.isFinite(pageNum) || !Number.isInteger(pageNum) || pageNum < 0) {\n const message = `--page must be a non-negative integer (got ${JSON.stringify(pageRaw)})`;\n if (args.json) emitJson({ ok: false, reason: 'invalid-page', message });\n else process.stderr.write(`${message}\\n`);\n return exitAfterFlush(ExitCode.Usage);\n }\n printContextHeader(ctx, { json: args.json });\n\n try {\n const page = await fetchWorkspaceSegments(\n {\n workspaceId,\n ...(args.category !== undefined ? { category: String(args.category) } : {}),\n ...(args.search !== undefined ? { search: String(args.search) } : {}),\n page: pageNum,\n },\n session.cookies,\n );\n const category = args.category !== undefined ? String(args.category) : '생성된 세그먼트';\n if (args.json) {\n emitJson({\n ok: true,\n workspaceId,\n category,\n segments: page.contents,\n totalPage: page.totalPage,\n currentPage: page.currentPage,\n });\n return exitAfterFlush(ExitCode.Ok);\n }\n if (page.contents.length === 0) {\n process.stdout.write(\n `Workspace ${workspaceId} (${category}): no segments on page ${page.currentPage}\\n`,\n );\n return exitAfterFlush(ExitCode.Ok);\n }\n process.stdout.write(\n `Workspace ${workspaceId} (${category}): ${page.contents.length} segment(s), page ${page.currentPage} of ${page.totalPage}\\n`,\n );\n for (const s of page.contents) {\n const id =\n typeof s.id === 'string' || typeof s.id === 'number'\n ? s.id\n : typeof s.segmentId === 'string' || typeof s.segmentId === 'number'\n ? s.segmentId\n : '-';\n const name =\n typeof s.name === 'string' ? s.name : typeof s.title === 'string' ? s.title : '-';\n const userCount =\n typeof s.userCount === 'number'\n ? String(s.userCount)\n : typeof s.count === 'number'\n ? String(s.count)\n : '-';\n process.stdout.write(`${id}\\t${name}\\t${userCount}\\n`);\n }\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\nconst segmentsCommand = defineCommand({\n meta: {\n name: 'segments',\n description: 'Inspect user segments defined in a workspace.',\n },\n subCommands: {\n ls: segmentsLsCommand,\n },\n});\n\nexport const workspaceCommand = defineCommand({\n meta: {\n name: 'workspace',\n description: 'Inspect and switch between the workspaces this account can access.',\n },\n subCommands: {\n ls: lsCommand,\n use: useCommand,\n show: showCommand,\n partner: partnerCommand,\n terms: termsCommand,\n segments: segmentsCommand,\n },\n});\n","#!/usr/bin/env node\nimport { defineCommand, runMain } from 'citty';\nimport { appCommand } from './commands/app.js';\nimport { authCommand } from './commands/auth.js';\nimport { completionCommand } from './commands/completion.js';\nimport { keysCommand } from './commands/keys.js';\nimport { loginCommand } from './commands/login.js';\nimport { logoutCommand } from './commands/logout.js';\nimport { meCommand } from './commands/me.js';\nimport { membersCommand } from './commands/members.js';\nimport { noticesCommand } from './commands/notices.js';\nimport { cleanupStaleUpgradeArtifacts, upgradeCommand } from './commands/upgrade.js';\nimport { whoamiCommand } from './commands/whoami.js';\nimport { workspaceCommand } from './commands/workspace.js';\nimport { VERSION } from './version.js';\n\nconst main = defineCommand({\n meta: {\n name: 'aitcc',\n version: VERSION,\n description:\n 'aitcc — Apps in Toss Community Console CLI. Unofficial, not affiliated with Toss.',\n },\n subCommands: {\n whoami: whoamiCommand,\n login: loginCommand,\n logout: logoutCommand,\n auth: authCommand,\n upgrade: upgradeCommand,\n workspace: workspaceCommand,\n app: appCommand,\n members: membersCommand,\n keys: keysCommand,\n notices: noticesCommand,\n me: meCommand,\n completion: completionCommand,\n },\n});\n\ncleanupStaleUpgradeArtifacts().catch(() => {\n // best-effort; failure must not affect command execution.\n});\n\nrunMain(main);\n"],"mappings":";;;;;;;;;;;;;;AAsCA,IAAa,eAAb,cAAkC,MAAM;CACtC,YACE,QACA,WACA,QACA,WACA;AACA,QAAM,kBAAkB,UAAU,IAAI,OAAO,SAAS,OAAO,GAAG;AALvD,OAAA,SAAA;AACA,OAAA,YAAA;AACA,OAAA,SAAA;AACA,OAAA,YAAA;AAGT,OAAK,OAAO;;;CAId,IAAI,cAAuB;AACzB,SAAO,KAAK,WAAW,OAAO,KAAK,cAAc;;;AAIrD,IAAa,eAAb,cAAkC,MAAM;CACtC,YACE,KACA,OACA;AACA,QAAM,sBAAsB,IAAI,WAAW,MAAM,UAAU;AAHlD,OAAA,MAAA;AAIT,OAAK,OAAO;AACZ,OAAK,QAAQ;;;AAIjB,IAAa,yBAAb,cAA4C,MAAM;CAChD,YACE,KACA,QACA,SACA,aACA;EACA,MAAM,SAAS,cAAc,WAAW,YAAY,KAAK;AACzD,QAAM,2BAA2B,IAAI,SAAS,OAAO,KAAK,UAAU,SAAS;AANpE,OAAA,MAAA;AACA,OAAA,SAAA;AAEA,OAAA,cAAA;AAIT,OAAK,OAAO;;;;;;;;;;AAahB,SAAgB,cAAc,cAAsB,UAA2B;AAC7E,KAAI,aAAa,WAAW,EAAG,QAAO;CACtC,MAAM,QAAQ,aAAa,aAAa;CACxC,MAAM,OAAO,SAAS,aAAa;AACnC,KAAI,UAAU,KAAM,QAAO;AAC3B,KAAI,MAAM,WAAW,IAAI,IAAI,KAAK,SAAS,MAAM,CAAE,QAAO;AAG1D,KAAI,CAAC,MAAM,WAAW,IAAI,IAAI,KAAK,SAAS,IAAI,QAAQ,CAAE,QAAO;AACjE,QAAO;;;;;;;;AAST,SAAgB,YAAY,YAAoB,aAA8B;AAC5E,KAAI,CAAC,WAAY,QAAO;AACxB,KAAI,eAAe,YAAa,QAAO;AACvC,KAAI,CAAC,YAAY,WAAW,WAAW,CAAE,QAAO;AAChD,QAAO,WAAW,SAAS,IAAI,IAAI,YAAY,OAAO,WAAW,OAAO,KAAK;;AAM/E,SAAS,iBAAiB,GAAoB;AAI5C,QAAO,CAAC,mBAAmB,KAAK,EAAE;;;;;;;;;;;AAYpC,SAAgB,gBAAgB,KAAU,SAA8C;CACtF,MAAM,WAAW,QACd,KAAK,GAAG,OAAO;EAAE;EAAG;EAAG,EAAE,CACzB,QAAQ,EAAE,QAAQ;AACjB,MAAI,CAAC,iBAAiB,EAAE,KAAK,IAAI,CAAC,iBAAiB,EAAE,MAAM,CAAE,QAAO;AACpE,MAAI,CAAC,cAAc,EAAE,QAAQ,IAAI,SAAS,CAAE,QAAO;AACnD,MAAI,CAAC,YAAY,EAAE,MAAM,IAAI,SAAS,CAAE,QAAO;AAC/C,MAAI,EAAE,UAAU,IAAI,aAAa,SAAU,QAAO;AAClD,SAAO;GACP,CACD,MAAM,GAAG,MAAM;EACd,MAAM,SAAS,EAAE,EAAE,KAAK,SAAS,EAAE,EAAE,KAAK;AAC1C,SAAO,WAAW,IAAI,SAAS,EAAE,IAAI,EAAE;GACvC,CACD,KAAK,EAAE,QAAQ,EAAE;AACpB,KAAI,SAAS,WAAW,EAAG,QAAO;AAClC,QAAO,SAAS,KAAK,MAAM,GAAG,EAAE,KAAK,GAAG,EAAE,QAAQ,CAAC,KAAK,KAAK;;;;;;;;;AAyB/D,eAAsB,kBAAqB,SAAqC;CAC9E,MAAM,MAAM,IAAI,IAAI,QAAQ,IAAI;CAChC,MAAM,eAAe,gBAAgB,KAAK,QAAQ,QAAQ;CAC1D,MAAM,UAAkC;EACtC,QAAQ;EACR,GAAG,QAAQ;EACZ;AACD,KAAI,aAAc,SAAQ,SAAS;CAEnC,MAAM,OAAoB;EACxB,QAAQ,QAAQ,UAAU;EAC1B;EAEA,UAAU;EACX;AACD,KAAI,QAAQ,SAAS,KAAA,GAAW;AAC9B,UAAQ,kBAAkB;AAC1B,OAAK,OAAO,KAAK,UAAU,QAAQ,KAAK;;AAG1C,QAAO,iBAAoB,KAAK,MAAM,QAAQ,UAAU;;;;;;;;;;;;;;AAe1D,eAAsB,iBACpB,KACA,MACA,WACY;CACZ,MAAM,OAAkB,eAAe,OAAO,MAAM,MAAM,OAAO,EAAE;CACnE,IAAI;AACJ,KAAI;AACF,QAAM,MAAM,KAAK,KAAK,KAAK;UACpB,KAAK;AACZ,QAAM,IAAI,aAAa,IAAI,UAAU,EAAE,IAAa;;CAMtD,IAAI;AACJ,KAAI;AACF,SAAO,MAAM,IAAI,MAAM;UAChB,KAAK;AACZ,QAAM,IAAI,uBAAuB,IAAI,UAAU,EAAE,IAAI,QAAS,IAAc,QAAQ;;CAEtF,IAAI;AACJ,KAAI;AACF,WAAS,KAAK,MAAM,KAAK;UAClB,KAAK;EACZ,MAAM,UAAU,KAAK,MAAM,GAAG,IAAI,CAAC,QAAQ,QAAQ,IAAI,CAAC,MAAM;AAC9D,QAAM,IAAI,uBAAuB,IAAI,UAAU,EAAE,IAAI,QAAS,IAAc,SAAS,QAAQ;;AAG/F,KAAI,OAAO,eAAe,UACxB,QAAO,OAAO;AAEhB,OAAM,IAAI,aACR,IAAI,QACJ,OAAO,MAAM,WACb,OAAO,MAAM,QACb,OAAO,MAAM,UACd;;;;ACvOH,MAAMA,SAAO;AAEb,eAAsB,WACpB,aACA,WACA,SACA,OAAkC,EAAE,EACmB;CAEvD,MAAM,MAAM,MAAM,kBAA2B;EAC3C,KAFU,GAAGA,OAAK,cAAc,YAAY,YAAY,UAAU;EAGlE;EACA,GAAI,KAAK,YAAY,EAAE,WAAW,KAAK,WAAW,GAAG,EAAE;EACxD,CAAC;AACF,KAAI,CAAC,MAAM,QAAQ,IAAI,CACrB,OAAM,IAAI,MAAM,kCAAkC,UAAU,gBAAgB;AAE9E,QAAO,IAAI,KAAK,MAAM;AACpB,MAAI,MAAM,QAAQ,OAAO,MAAM,SAAU,QAAO,EAAE;AAClD,SAAO;GACP;;AAYJ,eAAsB,UACpB,aACA,WACA,MACA,SACA,OAAkC,EAAE,EACV;CAE1B,MAAM,MAAM,MAAM,kBAA2B;EAC3C,KAFU,GAAGA,OAAK,cAAc,YAAY,YAAY,UAAU;EAGlE,QAAQ;EACR,MAAM,EAAE,MAAM;EACd;EACA,GAAI,KAAK,YAAY,EAAE,WAAW,KAAK,WAAW,GAAG,EAAE;EACxD,CAAC;AACF,KAAI,QAAQ,QAAQ,OAAO,QAAQ,SACjC,OAAM,IAAI,MAAM,uCAAuC,UAAU,iBAAiB;CAEpF,MAAM,MAAM;CACZ,MAAM,aAAa,OAAO,IAAI,eAAe,WAAW,IAAI,aAAa;CACzE,MAAM,YAAY,OAAO,IAAI,cAAc,WAAW,IAAI,YAAY;AACtE,KAAI,eAAe,QAAQ,cAAc,KACvC,OAAM,IAAI,MACR,uCAAuC,UAAU,gCAClD;AAEH,QAAO;EAAE;EAAY;EAAW;;AAKlC,eAAsB,WACpB,aACA,WACA,QACA,SACA,OAAkC,EAAE,EACrB;AAEf,OAAM,kBAA2B;EAC/B,KAFU,GAAGA,OAAK,cAAc,YAAY,YAAY,UAAU,SAAS,mBAAmB,OAAO,CAAC;EAGtG,QAAQ;EACR,MAAM,EAAE;EACR;EACA,GAAI,KAAK,YAAY,EAAE,WAAW,KAAK,WAAW,GAAG,EAAE;EACxD,CAAC;;;;ACpEJ,MAAMC,SAAO;AAab,eAAsB,cACpB,aACA,SACA,OAAkC,EAAE,EACT;CAE3B,MAAM,MAAM,MAAM,kBAA2B;EAC3C,KAFU,GAAGA,OAAK,cAAc,YAAY;EAG5C;EACA,GAAI,KAAK,YAAY,EAAE,WAAW,KAAK,WAAW,GAAG,EAAE;EACxD,CAAC;AACF,KAAI,CAAC,MAAM,QAAQ,IAAI,CACrB,OAAM,IAAI,MAAM,gDAAgD,cAAc;AAEhF,QAAO,IAAI,KAAK,MAAM,UAAU,iBAAiB,MAAM,aAAa,MAAM,CAAC;;AAG7E,SAAS,iBAAiB,MAAe,aAAqB,OAA+B;AAC3F,KAAI,SAAS,QAAQ,OAAO,SAAS,SACnC,OAAM,IAAI,MACR,sCAAsC,MAAM,iBAAiB,YAAY,iBAC1E;CAEH,MAAM,MAAM;CACZ,MAAM,QAAQ,IAAI,MAAM,IAAI,aAAa,IAAI;AAC7C,KAAI,OAAO,UAAU,YAAY,OAAO,UAAU,SAChD,OAAM,IAAI,MACR,sCAAsC,MAAM,iBAAiB,YAAY,cAC1E;CAEH,MAAM,UAAU,IAAI,QAAQ,IAAI,eAAe,IAAI;CACnD,MAAM,OAAO,OAAO,YAAY,WAAW,UAAU,KAAA;CACrD,MAAM,EACJ,IAAI,KACJ,WAAW,MACX,OAAO,MACP,MAAM,IACN,aAAa,KACb,SAAS,KACT,GAAG,UACD;AACJ,QAAO;EAAE,IAAI;EAAO;EAAM;EAAO;;AAGnC,eAAsB,kBACpB,aACA,SACA,OAAkC,EAAE,EACN;CAE9B,MAAM,MAAM,MAAM,kBAA2B;EAC3C,KAFU,GAAGA,OAAK,cAAc,YAAY;EAG5C;EACA,GAAI,KAAK,YAAY,EAAE,WAAW,KAAK,WAAW,GAAG,EAAE;EACxD,CAAC;AACF,KAAI,QAAQ,QAAQ,OAAO,QAAQ,SACjC,OAAM,IAAI,MAAM,gDAAgD,cAAc;CAEhF,MAAM,MAAM;CACZ,MAAM,qBAAqB,QAAQ,IAAI,mBAAmB;CAC1D,MAAM,cAAc,IAAI;AACxB,KAAI,CAAC,MAAM,QAAQ,YAAY,CAC7B,OAAM,IAAI,MACR,gDAAgD,YAAY,4BAC7D;AAMH,QAAO;EAAE;EAAoB,UAJZ,YAAY,KAAK,MAAM;AACtC,OAAI,MAAM,QAAQ,OAAO,MAAM,SAAU,QAAO,EAAE;AAClD,UAAO;IACP;EACqC;;AAezC,eAAsB,sBACpB,aACA,WACA,SACA,OAAkC,EAAE,EACT;CAE3B,MAAM,MAAM,MAAM,kBAA2B;EAC3C,KAFU,GAAGA,OAAK,cAAc,YAAY,YAAY,UAAU;EAGlE;EACA,GAAI,KAAK,YAAY,EAAE,WAAW,KAAK,WAAW,GAAG,EAAE;EACxD,CAAC;AACF,KAAI,QAAQ,QAAQ,OAAO,QAAQ,SACjC,OAAM,IAAI,MAAM,4CAA4C,YAAY;CAE1E,MAAM,MAAM;AAOZ,QAAO;EAAE,SANO,eAAe,IAAI,QAAQ,GACtC,IAAI,UACL;EAIc,OAHJ,eAAe,IAAI,MAAM,GAAI,IAAI,QAA2C;EAGjE,cAFJ,OAAO,IAAI,iBAAiB,WAAW,IAAI,eAAe;EAExC,iBADf,OAAO,IAAI,oBAAoB,WAAW,IAAI,kBAAkB;EAChC;;AAG1D,SAAS,eAAe,GAAiD;AACvE,QAAO,MAAM,QAAS,OAAO,MAAM,YAAY,CAAC,MAAM,QAAQ,EAAE;;AA2ClE,eAAsB,oBACpB,QACA,SACA,OAAkC,EAAE,EACP;CAC7B,MAAM,OAAO,OAAO,QAAQ;CAC5B,MAAM,OAAO,OAAO,QAAQ;CAC5B,MAAM,YAAY,OAAO,aAAa;CACtC,MAAM,gBAAgB,OAAO,iBAAiB;CAK9C,MAAM,MAAM,MAAM,kBAA2B;EAC3C,KAJA,GAAGA,OAAK,cAAc,OAAO,YAAY,YAAY,OAAO,UAAU,oBAC7D,KAAK,QAAQ,KAAK,aAAa,UAAU,iBAAiB;EAInE;EACA,GAAI,KAAK,YAAY,EAAE,WAAW,KAAK,WAAW,GAAG,EAAE;EACxD,CAAC;AACF,KAAI,QAAQ,QAAQ,OAAO,QAAQ,SACjC,OAAM,IAAI,MAAM,oCAAoC,OAAO,YAAY;CAEzE,MAAM,MAAM;CACZ,MAAM,aAAa,IAAI;AACvB,KAAI,CAAC,MAAM,QAAQ,WAAW,CAC5B,OAAM,IAAI,MAAM,0DAA0D,OAAO,UAAU,GAAG;CAEhG,MAAM,UAAU,WAAW,KAAK,MAAM;AACpC,MAAI,MAAM,QAAQ,OAAO,MAAM,SAAU,QAAO,EAAE;AAClD,SAAO;GACP;CACF,MAAM,YAAY,IAAI;AACtB,KAAI,cAAc,QAAQ,OAAO,cAAc,SAC7C,OAAM,IAAI,MAAM,iDAAiD,OAAO,UAAU,GAAG;CAEvF,MAAM,IAAI;AASV,QAAO;EAAE;EAAS,QARY;GAC5B,YAAY,OAAO,EAAE,eAAe,WAAW,EAAE,aAAa;GAC9D,UAAU,OAAO,EAAE,aAAa,WAAW,EAAE,WAAW;GACxD,SAAS,QAAQ,EAAE,QAAQ;GAC3B,YAAY,OAAO,EAAE,eAAe,WAAW,EAAE,aAAa;GAC/D;EAGyB,eAFJ,OAAO,IAAI,kBAAkB,WAAW,IAAI,gBAAgB;EAEzC,kBADhB,OAAO,IAAI,qBAAqB,WAAW,IAAI,mBAAmB;EAChC;;AA8B7D,eAAsB,iBACpB,QACA,SACA,OAAkC,EAAE,EACV;CAC1B,MAAM,WAAW,OAAO,YAAY;CACpC,MAAM,KAAK,IAAI,iBAAiB;AAChC,IAAG,IAAI,YAAY,OAAO,SAAS,CAAC;AACpC,KAAI,OAAO,WAAW,KAAA,EAAW,IAAG,IAAI,UAAU,OAAO,OAAO;CAKhE,MAAM,MAAM,MAAM,kBAA2B;EAC3C,KAJA,GAAGA,OAAK,cAAc,OAAO,YAAY,aAAa,OAAO,UAAU,kBACvE,GAAG,UAAU;EAIb;EACA,GAAI,KAAK,YAAY,EAAE,WAAW,KAAK,WAAW,GAAG,EAAE;EACxD,CAAC;AACF,KAAI,QAAQ,QAAQ,OAAO,QAAQ,SACjC,OAAM,IAAI,MAAM,yCAAyC,OAAO,YAAY;CAE9E,MAAM,MAAM;CACZ,MAAM,aAAa,IAAI;AACvB,KAAI,CAAC,MAAM,QAAQ,WAAW,CAC5B,OAAM,IAAI,MACR,+DAA+D,OAAO,UAAU,GACjF;AAQH,QAAO;EAAE,SANO,WAAW,KAAK,MAAM;AACpC,OAAI,MAAM,QAAQ,OAAO,MAAM,SAAU,QAAO,EAAE;AAClD,UAAO;IACP;EAGgB,YAFC,OAAO,IAAI,eAAe,WAAW,IAAI,aAAa;EAE3C,SADd,QAAQ,IAAI,QAAQ;EACG;;AAmCzC,eAAsB,aACpB,QACA,SACA,OAAkC,EAAE,EACd;CACtB,MAAM,KAAK,IAAI,iBAAiB;AAChC,KAAI,OAAO,SAAS,KAAA,EAAW,IAAG,IAAI,QAAQ,OAAO,OAAO,KAAK,CAAC;AAClE,KAAI,OAAO,WAAW,KAAA,EAAW,IAAG,IAAI,UAAU,OAAO,OAAO,OAAO,CAAC;AACxE,KAAI,OAAO,iBAAiB,KAAA,EAAW,IAAG,IAAI,gBAAgB,OAAO,aAAa;CAClF,MAAM,QAAQ,GAAG,UAAU;CAK3B,MAAM,MAAM,MAAM,kBAA2B;EAC3C,KAJA,GAAGA,OAAK,cAAc,OAAO,YAAY,YAAY,OAAO,UAAU,aACrE,QAAQ,IAAI,UAAU;EAIvB;EACA,GAAI,KAAK,YAAY,EAAE,WAAW,KAAK,WAAW,GAAG,EAAE;EACxD,CAAC;AACF,KAAI,QAAQ,QAAQ,OAAO,QAAQ,SACjC,OAAM,IAAI,MAAM,oCAAoC,OAAO,YAAY;CAEzE,MAAM,MAAM;CACZ,MAAM,cAAc,IAAI;AACxB,KAAI,CAAC,MAAM,QAAQ,YAAY,CAC7B,OAAM,IAAI,MAAM,2DAA2D,OAAO,UAAU,GAAG;AAQjG,QAAO;EAAE,UANQ,YAAY,KAAK,MAAM;AACtC,OAAI,MAAM,QAAQ,OAAO,MAAM,SAAU,QAAO,EAAE;AAClD,UAAO;IACP;EAGiB,WAFD,OAAO,IAAI,cAAc,WAAW,IAAI,YAAY;EAExC,aADV,OAAO,IAAI,gBAAgB,WAAW,IAAI,cAAc;EACjC;;AAqC7C,eAAsB,uBACpB,QACA,SACA,OAAkC,EAAE,EACZ;CACxB,MAAM,KAAK,IAAI,iBAAiB;AAChC,IAAG,IAAI,WAAW,OAAO,OAAO,WAAW,MAAM,CAAC;AAClD,IAAG,IAAI,gBAAgB,OAAO,aAAa;AAC3C,IAAG,IAAI,aAAa,OAAO,UAAU;AACrC,IAAG,IAAI,WAAW,OAAO,QAAQ;CAKjC,MAAM,MAAM,MAAM,kBAA2B;EAC3C,KAJA,GAAGA,OAAK,cAAc,OAAO,YAAY,YAAY,OAAO,UAAU,sBAClE,GAAG,UAAU;EAIjB;EACA,GAAI,KAAK,YAAY,EAAE,WAAW,KAAK,WAAW,GAAG,EAAE;EACxD,CAAC;AACF,KAAI,QAAQ,QAAQ,OAAO,QAAQ,SACjC,OAAM,IAAI,MAAM,oCAAoC,OAAO,YAAY;CAEzE,MAAM,MAAM;CACZ,MAAM,aAAa,IAAI;AACvB,KAAI,CAAC,MAAM,QAAQ,WAAW,CAC5B,OAAM,IAAI,MAAM,0DAA0D,OAAO,UAAU,GAAG;AAOhG,QAAO;EAAE,SALO,WAAW,KAAK,MAAM;AACpC,OAAI,MAAM,QAAQ,OAAO,MAAM,SAAU,QAAO,EAAE;AAClD,UAAO;IACP;EAEgB,WADA,OAAO,IAAI,cAAc,WAAW,IAAI,YAAY,KAAA;EACzC;;AAwB/B,eAAsB,kBACpB,QACA,SACA,OAAkC,EAAE,EACmB;CACvD,MAAM,KAAK,IAAI,iBAAiB;AAIhC,IAAG,IAAI,UAAU,OAAO,UAAU,GAAG;CAErC,MAAM,MAAM,MAAM,kBAA2B;EAC3C,KAFU,GAAGA,OAAK,cAAc,OAAO,YAAY,YAAY,OAAO,UAAU,iBAAiB,GAAG,UAAU;EAG9G;EACA,GAAI,KAAK,YAAY,EAAE,WAAW,KAAK,WAAW,GAAG,EAAE;EACxD,CAAC;AACF,KAAI,CAAC,MAAM,QAAQ,IAAI,CACrB,OAAM,IAAI,MAAM,0CAA0C,OAAO,UAAU,gBAAgB;AAE7F,QAAO,IAAI,KAAK,MAAM;AACpB,MAAI,MAAM,QAAQ,OAAO,MAAM,SAAU,QAAO,EAAE;AAClD,SAAO;GACP;;AA2CJ,eAAsB,2BACpB,QACA,SACA,OAAkC,EAAE,EACE;CACtC,MAAM,OAAO,OAAO,QAAQ;CAC5B,MAAM,OAAO,OAAO,QAAQ;CAC5B,MAAM,KAAK,IAAI,iBAAiB;AAChC,IAAG,IAAI,QAAQ,OAAO,KAAK,CAAC;AAC5B,IAAG,IAAI,QAAQ,OAAO,KAAK,CAAC;CAO5B,MAAM,MAAM,MAAM,kBAA2B;EAC3C,QAAQ;EACR,KARU,GAAGA,OAAK,cAAc,OAAO,YAAY,YAAY,OAAO,UAAU,2BAA2B,GAAG,UAAU;EASxH,MARW;GACX,MAAM,OAAO,QAAQ,CAAC;IAAE,OAAO;IAAS,WAAW;IAAQ,CAAC;GAC5D,QAAQ,OAAO,UAAU;GACzB,SAAS,OAAO,WAAW,EAAE;GAC9B;EAKC;EACA,GAAI,KAAK,YAAY,EAAE,WAAW,KAAK,WAAW,GAAG,EAAE;EACxD,CAAC;AACF,KAAI,QAAQ,QAAQ,OAAO,QAAQ,YAAY,MAAM,QAAQ,IAAI,CAC/D,OAAM,IAAI,MAAM,oDAAoD,OAAO,YAAY;CAEzF,MAAM,OAAO;CACb,MAAM,QAAQ,MAAM,QAAQ,KAAK,MAAM,GAAG,KAAK,QAAQ,EAAE;CACzD,MAAM,YAAY,KAAK;CACvB,MAAM,SAA6B;EACjC,YAAY,OAAO,WAAW,eAAe,WAAW,UAAU,aAAa;EAC/E,UAAU,OAAO,WAAW,aAAa,WAAW,UAAU,WAAW;EACzE,SAAS,QAAQ,WAAW,QAAQ;EACpC,YAAY,OAAO,WAAW,eAAe,WAAW,UAAU,aAAa,MAAM;EACtF;AACD,QAAO;EACL,OAAO,MAAM,KAAK,MAAO,KAAK,OAAO,MAAM,WAAY,IAAgC,EAAE,CAAE;EAC3F;EACD;;AAoCH,eAAsB,sBACpB,QACA,SACA,OAAkC,EAAE,EACH;CACjC,MAAM,aAAa,OAAO,cAAc;CACxC,MAAM,WAAW,OAAO,YAAY;CAQpC,MAAM,MAAM,MAAM,kBAA2B;EAC3C,QAAQ;EACR,KATU,GAAGA,OAAK,cAAc,OAAO,YAAY,YAAY,OAAO,UAAU;EAUhF,MATW;GACX,WAAW,OAAO,WAAW;GAC7B;GACA;GACA,QAAQ,OAAO,UAAU;GAC1B;EAKC;EACA,GAAI,KAAK,YAAY,EAAE,WAAW,KAAK,WAAW,GAAG,EAAE;EACxD,CAAC;AACF,KAAI,QAAQ,QAAQ,OAAO,QAAQ,YAAY,MAAM,QAAQ,IAAI,CAC/D,OAAM,IAAI,MAAM,2CAA2C,OAAO,YAAY;CAEhF,MAAM,OAAO;CACb,MAAM,UAAU,MAAM,QAAQ,KAAK,QAAQ,GAAG,KAAK,UAAU,EAAE;CAC/D,MAAM,YAAY,KAAK;CACvB,MAAM,SAAiC;EACrC,YAAY,OAAO,WAAW,eAAe,WAAW,UAAU,aAAa;EAC/E,UAAU,OAAO,WAAW,aAAa,WAAW,UAAU,WAAW;EACzE,SAAS,QAAQ,WAAW,QAAQ;EACpC,YAAY,OAAO,WAAW,eAAe,WAAW,UAAU,aAAa,QAAQ;EACvF,YAAY,OAAO,WAAW,eAAe,WAAW,UAAU,aAAa;EAChF;AACD,QAAO;EACL,SAAS,QAAQ,KAAK,MAAO,KAAK,OAAO,MAAM,WAAY,IAAgC,EAAE,CAAE;EAC/F,WAAW,OAAO,KAAK,cAAc,WAAW,KAAK,YAAY,KAAA;EACjE;EACD;;AAiBH,MAAa,+BAAoE,CAC/E,cACA,YACD;AAgBD,eAAsB,kBACpB,QACA,SACA,OAAkC,EAAE,EACP;CAC7B,MAAM,OAAO,OAAO,QAAQ;CAC5B,MAAM,OAAO,OAAO,QAAQ;CAC5B,MAAM,KAAK,IAAI,iBAAiB;AAChC,IAAG,IAAI,QAAQ,OAAO,KAAK,CAAC;AAC5B,IAAG,IAAI,QAAQ,OAAO,KAAK,CAAC;AAC5B,KAAI,OAAO,iBAAkB,IAAG,IAAI,oBAAoB,OAAO,iBAAiB;AAChF,KAAI,OAAO,mBAAmB,KAAA,EAAW,IAAG,IAAI,kBAAkB,OAAO,OAAO,eAAe,CAAC;CAEhG,MAAM,MAAM,MAAM,kBAA2B;EAC3C,KAFU,GAAGA,OAAK,cAAc,OAAO,YAAY,YAAY,OAAO,UAAU,oBAAoB,GAAG,UAAU;EAGjH;EACA,GAAI,KAAK,YAAY,EAAE,WAAW,KAAK,WAAW,GAAG,EAAE;EACxD,CAAC;AACF,KAAI,QAAQ,QAAQ,OAAO,QAAQ,YAAY,MAAM,QAAQ,IAAI,CAC/D,OAAM,IAAI,MAAM,sCAAsC,OAAO,YAAY;CAE3E,MAAM,OAAO;CACb,MAAM,OAAO,MAAM,QAAQ,KAAK,2BAA2B,GACvD,KAAK,6BACL,EAAE;CACN,MAAM,WAAW,KAAK;AACtB,QAAO;EACL,WAAW,KAAK,KAAK,MAAO,KAAK,OAAO,MAAM,WAAY,IAAgC,EAAE,CAAE;EAC9F,gBAAgB,OAAO,UAAU,mBAAmB,WAAW,SAAS,iBAAiB;EAC1F;;AAsCH,eAAsB,4BACpB,SACA,OAAkC,EAAE,EACG;CAEvC,MAAM,MAAM,MAAM,kBAA2B;EAC3C,KAFU,GAAGA,OAAK;EAGlB;EACA,GAAI,KAAK,YAAY,EAAE,WAAW,KAAK,WAAW,GAAG,EAAE;EACxD,CAAC;AACF,KAAI,CAAC,MAAM,QAAQ,IAAI,CACrB,OAAM,IAAI,MAAM,0DAA0D;AAE5E,QAAO,IAAI,KAAK,OAAO,MAAyB;AAC9C,MAAI,CAAC,SAAS,OAAO,UAAU,SAC7B,OAAM,IAAI,MAAM,2CAA2C,IAAI;EAEjE,MAAM,IAAI;EACV,MAAM,QAAQ,EAAE;EAChB,MAAM,OAAO,MAAM,QAAQ,EAAE,aAAa,GAAG,EAAE,eAAe,EAAE;AAChE,SAAO;GACL,eAAe;IACb,IAAI,OAAO,OAAO,OAAO,WAAW,MAAM,KAAK;IAC/C,MAAM,OAAO,OAAO,SAAS,WAAW,MAAM,OAAO;IACrD,cAAc,QAAQ,OAAO,aAAa;IAC3C;GACD,cAAc,KAAK,KAAK,MAAoB;AAC1C,QAAI,CAAC,KAAK,OAAO,MAAM,SACrB,QAAO;KAAE,IAAI;KAAG,MAAM;KAAI,cAAc;KAAO,iBAAiB,EAAE;KAAE;IAEtE,MAAM,KAAK;IACX,MAAM,OAAO,MAAM,QAAQ,GAAG,gBAAgB,GAAG,GAAG,kBAAkB,EAAE;AACxE,WAAO;KACL,IAAI,OAAO,GAAG,OAAO,WAAW,GAAG,KAAK;KACxC,MAAM,OAAO,GAAG,SAAS,WAAW,GAAG,OAAO;KAC9C,cAAc,QAAQ,GAAG,aAAa;KACtC,iBAAiB,KAAK,KAAK,MAAuB;AAChD,UAAI,CAAC,KAAK,OAAO,MAAM,SACrB,QAAO;OAAE,IAAI;OAAG,MAAM;OAAI,cAAc;OAAO;MAEjD,MAAM,KAAK;AACX,aAAO;OACL,IAAI,OAAO,GAAG,OAAO,WAAW,GAAG,KAAK;OACxC,MAAM,OAAO,GAAG,SAAS,WAAW,GAAG,OAAO;OAC9C,cAAc,QAAQ,GAAG,aAAa;OACvC;OACD;KACH;KACD;GACH;GACD;;AAsBJ,eAAsB,sBACpB,aACA,WACA,SACA,OAAkC,EAAE,EACT;CAE3B,MAAM,MAAM,MAAM,kBAA2B;EAC3C,KAFU,GAAGA,OAAK,cAAc,YAAY,YAAY,UAAU;EAGlE;EACA,GAAI,KAAK,YAAY,EAAE,WAAW,KAAK,WAAW,GAAG,EAAE;EACxD,CAAC;AACF,KAAI,QAAQ,QAAQ,OAAO,QAAQ,YAAY,MAAM,QAAQ,IAAI,CAC/D,OAAM,IAAI,MAAM,+CAA+C,YAAY;CAE7E,MAAM,OAAO;CACb,MAAM,gBAAgB,KAAK;AAC3B,KAAI,OAAO,kBAAkB,SAC3B,OAAM,IAAI,MACR,+CAA+C,UAAU,yBAC1D;AAEH,QAAO;EACL,yBACE,OAAO,KAAK,4BAA4B,WAAW,KAAK,0BAA0B;EACpF,qBACE,OAAO,KAAK,wBAAwB,WAAW,KAAK,sBAAsB;EAC5E;EACD;;AAGH,eAAsB,oBACpB,aACA,WACA,SACA,OAAkC,EAAE,EACK;CAEzC,MAAM,MAAM,MAAM,kBAA2B;EAC3C,KAFU,GAAGA,OAAK,cAAc,YAAY,YAAY,UAAU;EAGlE;EACA,GAAI,KAAK,YAAY,EAAE,WAAW,KAAK,WAAW,GAAG,EAAE;EACxD,CAAC;AACF,KAAI,QAAQ,KAAM,QAAO;AACzB,KAAI,OAAO,QAAQ,YAAY,MAAM,QAAQ,IAAI,CAC/C,OAAM,IAAI,MAAM,4CAA4C,YAAY;AAE1E,QAAO;;AAkCT,eAAsB,0BACpB,aACA,WACA,cACA,SACA,OAAkC,EAAE,EACC;CAErC,MAAM,MAAM,MAAM,kBAA2B;EAC3C,QAAQ;EACR,KAHU,GAAGA,OAAK,cAAc,YAAY,YAAY,UAAU;EAIlE,MAAM,EAAE,cAAc;EACtB;EACA,GAAI,KAAK,YAAY,EAAE,WAAW,KAAK,WAAW,GAAG,EAAE;EACxD,CAAC;AACF,KAAI,QAAQ,QAAQ,OAAO,QAAQ,YAAY,MAAM,QAAQ,IAAI,CAC/D,OAAM,IAAI,MAAM,mDAAmD,YAAY;CAEjF,MAAM,OAAO;CACb,MAAM,YAAY,KAAK;AACvB,KAAI,OAAO,cAAc,SACvB,OAAM,IAAI,MACR,mDAAmD,UAAU,qBAC9D;CAEH,MAAM,aACJ,KAAK,cAAc,OAAO,KAAK,eAAe,WACzC,KAAK,aACN,EAAE;AAGR,QAAO;EAAE;EAAW;EAAY,cAD9B,OAAO,WAAW,iBAAiB,WAAW,WAAW,eAAe;EAC5B;;AAGhD,eAAsB,wBACpB,aACA,WACA,cACA,SACA,OAAkC,EAAE,EACQ;CAE5C,MAAM,MAAM,MAAM,kBAA2B;EAC3C,QAAQ;EACR,KAHU,GAAGA,OAAK,cAAc,YAAY,YAAY,UAAU;EAIlE,MAAM,EAAE,cAAc;EACtB;EACA,GAAI,KAAK,YAAY,EAAE,WAAW,KAAK,WAAW,GAAG,EAAE;EACxD,CAAC;AAGF,KAAI,QAAQ,QAAQ,OAAO,QAAQ,YAAY,MAAM,QAAQ,IAAI,CAAE,QAAO,EAAE;AAC5E,QAAO;;AAGT,eAAsB,eACpB,aACA,WACA,cACA,MACA,SACA,OAAkC,EAAE,EACQ;CAE5C,MAAM,MAAM,MAAM,kBAA2B;EAC3C,QAAQ;EACR,KAHU,GAAGA,OAAK,cAAc,YAAY,YAAY,UAAU;EAIlE,MAAM;GAAE;GAAc;GAAM;EAC5B;EACA,GAAI,KAAK,YAAY,EAAE,WAAW,KAAK,WAAW,GAAG,EAAE;EACxD,CAAC;AACF,KAAI,QAAQ,QAAQ,OAAO,QAAQ,YAAY,MAAM,QAAQ,IAAI,CAAE,QAAO,EAAE;AAC5E,QAAO;;;;;;;;;AAUT,eAAsB,qBACpB,WACA,MACA,OAAkC,EAAE,EACrB;CACf,MAAM,OAAkB,KAAK,eAAe,GAAG,SAAS,MAAM,GAAG,KAAK;CAKtE,MAAM,OAAO,IAAI,WAAW,KAAK,QAAuB,KAAK,YAAY,KAAK,WAAW;CACzF,IAAI;AACJ,KAAI;AACF,QAAM,MAAM,KAAK,WAAW;GAC1B,QAAQ;GACR,SAAS,EAAE,gBAAgB,mBAAmB;GAC9C,MAAM;GACP,CAAC;UACK,KAAK;AACZ,QAAM,IAAI,MAAM,6BAA8B,IAAc,UAAU;;AAExE,KAAI,CAAC,IAAI,IAAI;EACX,MAAM,UAAU,MAAM,IAAI,MAAM,CAAC,YAAY,GAAG;AAChD,QAAM,IAAI,MAAM,mCAAmC,IAAI,OAAO,IAAI,QAAQ,MAAM,GAAG,IAAI,GAAG;;;AAoC9F,eAAsB,iBACpB,QACA,SACA,OAAkC,EAAE,EACQ;CAC5C,MAAM,MAAM,GAAGA,OAAK,cAAc,OAAO,YAAY,YAAY,OAAO,UAAU;CAClF,MAAM,OAAgC;EACpC,cAAc,OAAO;EACrB,cAAc,OAAO;EACtB;AACD,KAAI,OAAO,gBAAgB,KAAA,EAAW,MAAK,cAAc,OAAO;AAChE,KAAI,OAAO,yBAAyB,KAAA,EAClC,MAAK,uBAAuB,OAAO;CACrC,MAAM,MAAM,MAAM,kBAA2B;EAC3C,QAAQ;EACR;EACA;EACA;EACA,GAAI,KAAK,YAAY,EAAE,WAAW,KAAK,WAAW,GAAG,EAAE;EACxD,CAAC;AACF,KAAI,QAAQ,QAAQ,OAAO,QAAQ,YAAY,MAAM,QAAQ,IAAI,CAAE,QAAO,EAAE;AAC5E,QAAO;;AAGT,eAAsB,2BACpB,aACA,WACA,cACA,SACA,OAAkC,EAAE,EACQ;CAE5C,MAAM,MAAM,MAAM,kBAA2B;EAC3C,QAAQ;EACR,KAHU,GAAGA,OAAK,cAAc,YAAY,YAAY,UAAU;EAIlE,MAAM,EAAE,cAAc;EACtB;EACA,GAAI,KAAK,YAAY,EAAE,WAAW,KAAK,WAAW,GAAG,EAAE;EACxD,CAAC;AACF,KAAI,QAAQ,QAAQ,OAAO,QAAQ,YAAY,MAAM,QAAQ,IAAI,CAAE,QAAO,EAAE;AAC5E,QAAO;;AAUT,eAAsB,kBACpB,QACA,SACA,OAAkC,EAAE,EACQ;CAC5C,MAAM,MAAM,GAAGA,OAAK,cAAc,OAAO,YAAY,YAAY,OAAO,UAAU;CAClF,MAAM,OAAgC,EAAE,cAAc,OAAO,cAAc;AAC3E,KAAI,OAAO,kBAAkB,KAAA,EAAW,MAAK,gBAAgB,OAAO;CACpE,MAAM,MAAM,MAAM,kBAA2B;EAC3C,QAAQ;EACR;EACA;EACA;EACA,GAAI,KAAK,YAAY,EAAE,WAAW,KAAK,WAAW,GAAG,EAAE;EACxD,CAAC;AACF,KAAI,QAAQ,QAAQ,OAAO,QAAQ,YAAY,MAAM,QAAQ,IAAI,CAAE,QAAO,EAAE;AAC5E,QAAO;;AAGT,eAAsB,mBACpB,aACA,WACA,cACA,SACA,OAAkC,EAAE,EACQ;CAE5C,MAAM,MAAM,MAAM,kBAA2B;EAC3C,QAAQ;EACR,KAHU,GAAGA,OAAK,cAAc,YAAY,YAAY,UAAU;EAIlE,MAAM,EAAE,cAAc;EACtB;EACA,GAAI,KAAK,YAAY,EAAE,WAAW,KAAK,WAAW,GAAG,EAAE;EACxD,CAAC;AACF,KAAI,QAAQ,QAAQ,OAAO,QAAQ,YAAY,MAAM,QAAQ,IAAI,CAAE,QAAO,EAAE;AAC5E,QAAO;;AAGT,eAAsB,qBACpB,aACA,WACA,SACA,OAAkC,EAAE,EACQ;CAE5C,MAAM,MAAM,MAAM,kBAA2B;EAC3C,KAFU,GAAGA,OAAK,cAAc,YAAY,YAAY,UAAU;EAGlE;EACA,GAAI,KAAK,YAAY,EAAE,WAAW,KAAK,WAAW,GAAG,EAAE;EACxD,CAAC;AACF,KAAI,QAAQ,QAAQ,OAAO,QAAQ,YAAY,MAAM,QAAQ,IAAI,CAAE,QAAO,EAAE;AAC5E,QAAO;;AAuDT,eAAsB,cACpB,aACA,SACA,SACA,OAAkC,EAAE,EACN;AAS9B,QAAO,sBAPK,MAAM,kBAA2B;EAC3C,KAFU,GAAGA,OAAK,cAAc,YAAY;EAG5C,QAAQ;EACR;EACA,MAAM;EACN,GAAI,KAAK,YAAY,EAAE,WAAW,KAAK,WAAW,GAAG,EAAE;EACxD,CAAC,CAC+B;;AAGnC,SAAS,sBAAsB,KAAmC;AAChE,KAAI,QAAQ,QAAQ,OAAO,QAAQ,SACjC,QAAO;EAAE,WAAW,KAAA;EAAW,aAAa,KAAA;EAAW,OAAO,EAAE;EAAE;CAEpE,MAAM,MAAM;CACZ,MAAM,QAAQ,IAAI,aAAa,IAAI,MAAM,IAAI;CAC7C,MAAM,YAAY,OAAO,UAAU,YAAY,OAAO,UAAU,WAAW,QAAQ,KAAA;CACnF,MAAM,WAAW,IAAI,eAAe,IAAI;AAExC,QAAO;EAAE;EAAW,aADA,OAAO,aAAa,WAAW,WAAW,KAAA;EAC7B,OAAO;EAAK;;;;;;;;;;;AA0B/C,eAAsB,sBACpB,QACA,OAAkC,EAAE,EACnB;CACjB,MAAM,MAAM,IAAI,IAAI,GAAGA,OAAK,YAAY,OAAO,YAAY,SAAS;AACpE,KAAI,aAAa,IAAI,cAAc,OAAO,OAAO,WAAW,CAAC;AAC7D,KAAI,aAAa,IAAI,eAAe,OAAO,OAAO,YAAY,CAAC;CAE/D,MAAM,OAAO,IAAI,UAAU;CAM3B,MAAM,OAAO,IAAI,WACf,OAAO,KAAK,OAAO,QACnB,OAAO,KAAK,OAAO,YACnB,OAAO,KAAK,OAAO,WACpB;CACD,MAAM,OAAO,IAAI,KAAK,CAAC,KAAK,EAAE,EAAE,MAAM,OAAO,KAAK,aAAa,CAAC;AAChE,MAAK,OAAO,YAAY,MAAM,OAAO,KAAK,SAAS;AACnD,MAAK,OAAO,YAAY,OAAO,KAAK,SAAS;CAE7C,MAAM,eAAe,gBAAgB,KAAK,OAAO,QAAQ;CACzD,MAAM,UAAkC,EACtC,QAAQ,qCACT;AACD,KAAI,aAAc,SAAQ,SAAS;CAEnC,MAAM,WAAW,MAAM,iBACrB,KACA;EAAE,QAAQ;EAAQ;EAAS,MAAM;EAAM,EACvC,KAAK,UACN;AACD,KAAI,OAAO,aAAa,SACtB,OAAM,IAAI,uBACR,IAAI,UAAU,EACd,KACA,iCAAiC,OAAO,WACzC;AAEH,QAAO;;;;AChwCT,MAAa,WAAW;CACtB,IAAI;CACJ,SAAS;CACT,OAAO;CACP,kBAAkB;CAClB,cAAc;CACd,cAAc;CAId,oBAAoB;CACpB,sBAAsB;CACtB,oBAAoB;CACpB,0BAA0B;CAC1B,UAAU;CACV,oBAAoB;CACpB,sBAAsB;CAGtB,uBAAuB;CAKvB,wBAAwB;CACzB;;;ACtBD,eAAsB,eAAe,MAA8B;AACjE,OAAM,IAAI,SAAe,YAAY,QAAQ,OAAO,MAAM,UAAU,SAAS,CAAC,CAAC;AAC/E,SAAQ,KAAK,KAAK;;;;ACApB,MAAM,WAAW;AAEjB,SAAgB,YAAoB;AAClC,KAAI,QAAQ,aAAa,SAAS;EAChC,MAAM,UAAU,QAAQ,IAAI;AAC5B,MAAI,WAAW,QAAQ,SAAS,EAAG,QAAO,KAAK,SAAS,SAAS;AACjE,SAAO,KAAK,SAAS,IAAI,KAAK,WAAW,WAAW,SAAS;;CAE/D,MAAM,MAAM,QAAQ,IAAI;AACxB,KAAI,OAAO,IAAI,SAAS,EAAG,QAAO,KAAK,KAAK,SAAS;AACrD,QAAO,KAAK,SAAS,IAAI,KAAK,WAAW,SAAS;;AAGpD,SAAgB,kBAA0B;AACxC,QAAO,KAAK,WAAW,EAAE,eAAe;;AAQ1C,SAAgB,oBAA4B;AAC1C,QAAO,KAAK,WAAW,EAAE,kBAAkB;;AAM7C,SAAgB,WAAmB;AACjC,KAAI,QAAQ,aAAa,SAAS;EAChC,MAAM,eAAe,QAAQ,IAAI;AACjC,MAAI,gBAAgB,aAAa,SAAS,EAAG,QAAO,KAAK,cAAc,UAAU,QAAQ;AACzF,SAAO,KAAK,SAAS,IAAI,KAAK,WAAW,SAAS,UAAU,QAAQ;;CAEtE,MAAM,MAAM,QAAQ,IAAI;AACxB,KAAI,OAAO,IAAI,SAAS,EAAG,QAAO,KAAK,KAAK,SAAS;AACrD,QAAO,KAAK,SAAS,IAAI,KAAK,UAAU,SAAS;;AAGnD,SAAgB,mBAA2B;AACzC,QAAO,KAAK,UAAU,EAAE,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;ACkB/C,eAAsB,cAAuC;CAC3D,MAAM,UAAU,oBAAoB;AACpC,KAAI,YAAY,KAAA,EAAW,QAAO;CAClC,MAAM,OAAO,iBAAiB;CAC9B,IAAI;AACJ,KAAI;AACF,QAAM,MAAM,SAAS,MAAM,OAAO;UAC3B,KAAK;EACZ,MAAM,OAAQ,IAA8B;AAC5C,MAAI,SAAS,SAAU,QAAO;AAI9B,UAAQ,OAAO,MAAM,2CAA2C,KAAK,IAAI,QAAQ,UAAU,IAAI;AAC/F,SAAO;;CAET,IAAI;AACJ,KAAI;AACF,cAAY,KAAK,MAAM,IAAI;SACrB;AAGN,UAAQ,OAAO,MAAM,4BAA4B,KAAK,mCAAmC;AACzF,SAAO;;CAET,MAAM,eAAe,qBAAqB,UAAU;AACpD,KAAI,cAAc;AAChB,UAAQ,OAAO,MACb,4BAA4B,KAAK,YAAY,aAAa,6BAC3D;AACD,SAAO;;CAUT,MAAM,YAAY;AAClB,KAAI,UAAU,kBAAkB,GAAG;EACjC,MAAM,WAAoB;GAAE,GAAG;GAAW,eAAe;GAAG;AAC5D,MAAI;AACF,SAAM,aAAa,SAAS;WACrB,KAAK;AACZ,qBAAkB,MAAO,IAA8B,KAAK;;AAE9D,SAAO;;AAET,QAAO;;AAKT,IAAI,kBAAkB;AACtB,SAAS,kBAAkB,MAAc,MAAgC;AACvE,KAAI,gBAAiB;AACrB,mBAAkB;AAClB,SAAQ,OAAO,MACb,8CAA8C,KAAK,uBAAuB,QAAQ,UAAU,IAC7F;;AAKH,IAAI,oBAAoB;AACxB,IAAI,iBAAiB;;;;;;;;;;;;;;;AA8BrB,SAAS,qBAAiD;CACxD,MAAM,MAAM,QAAQ,IAAI;AACxB,KAAI,CAAC,OAAO,IAAI,WAAW,EAAG,QAAO,KAAA;CACrC,MAAM,UAAU,kBAAkB,IAAI;AACtC,KAAI,YAAY,MAAM;AACpB,sBAAoB,8CAA8C;AAClE;;CAEF,MAAM,SAAS,qBAAqB,QAAQ;AAC5C,KAAI,QAAQ;AACV,sBAAoB,8BAA8B,OAAO,GAAG;AAC5D;;CAEF,MAAM,YAAY;AAElB,QAAO,UAAU,kBAAkB,IAC/B;EAAE,GAAG;EAAW,eAAe;EAAG,GACjC;;;;;;;;;;;AAYP,SAAS,mBAA4B;AACnC,QAAO,oBAAoB,KAAK,KAAA;;AAGlC,SAAS,oBAAoB,SAAuB;AAClD,KAAI,kBAAmB;AACvB,qBAAoB;AACpB,SAAQ,OAAO,MAAM,YAAY,QAAQ,kCAAkC;;AAG7E,SAAS,mBAAyB;AAChC,KAAI,eAAgB;AACpB,kBAAiB;AACjB,SAAQ,OAAO,MAAM,sEAAsE;;;;;;;;AAS7F,SAAgB,kBAAkB,KAAsB;CACtD,MAAM,UAAU,IAAI,MAAM;AAC1B,KAAI,QAAQ,WAAW,EAAG,QAAO;AAEjC,KAAI,QAAQ,WAAW,IAAI,CACzB,KAAI;AACF,SAAO,KAAK,MAAM,QAAQ;SACpB;AACN,SAAO;;CAIX,IAAI;AACJ,KAAI;AACF,YAAU,OAAO,KAAK,SAAS,SAAS,CAAC,SAAS,OAAO;SACnD;AACN,YAAU;;CAEZ,MAAM,cAAc,QAAQ,MAAM;AAClC,KAAI,YAAY,WAAW,IAAI,CAC7B,KAAI;AACF,SAAO,KAAK,MAAM,YAAY;SACxB;AACN,SAAO;;AAGX,QAAO;;;;;;;;AAST,SAAgB,oBAAoB,OAA+B;AACjE,QAAO,qBAAqB,MAAM;;;;;;AAOpC,SAAgB,0BAA0B,OAAyB;CACjE,MAAM,YAAY;AAClB,QAAO,UAAU,kBAAkB,IAC/B;EAAE,GAAG;EAAW,eAAe;EAAG,GACjC;;AAQP,SAAS,qBAAqB,OAA+B;AAC3D,KAAI,UAAU,QAAQ,OAAO,UAAU,SAAU,QAAO;CACxD,MAAM,SAAS;AAQf,KAAI,OAAO,kBAAkB,KAAK,OAAO,kBAAkB,EACzD,QAAO,yBAAyB,OAAO,OAAO,cAAc;AAE9D,KAAI,CAAC,OAAO,QAAQ,OAAO,OAAO,KAAK,OAAO,SAAU,QAAO;AAC/D,KAAI,OAAO,OAAO,KAAK,UAAU,SAAU,QAAO;AAClD,KAAI,OAAO,KAAK,gBAAgB,KAAA,KAAa,OAAO,OAAO,KAAK,gBAAgB,SAC9E,QAAO;AAET,KAAI,CAAC,MAAM,QAAQ,OAAO,QAAQ,CAAE,QAAO;AAC3C,KAAI,OAAO,YAAY,KAAA,KAAa,CAAC,MAAM,QAAQ,OAAO,QAAQ,CAChE,QAAO;AAET,KAAI,OAAO,eAAe,KAAA,KAAa,OAAO,OAAO,eAAe,SAClE,QAAO;AAET,KAAI,OAAO,uBAAuB,KAAA,GAAW;EAC3C,MAAM,MAAM,OAAO;AACnB,MAAI,OAAO,QAAQ,YAAY,CAAC,OAAO,UAAU,IAAI,IAAI,OAAO,EAC9D,QAAO;;AAGX,QAAO;;AAkBT,eAAsB,aACpB,SACA,UAA+B,EAAE,EAClB;AASf,KAAI,CAAC,QAAQ,cAAc,kBAAkB,EAAE;AAC7C,oBAAkB;AAClB;;AAGF,OAAM,MADM,QAAQ,iBAAiB,CAAC,EACrB;EAAE,WAAW;EAAM,MAAM;EAAO,CAAC;AAClD,OAAM,UAAU,iBAAiB,EAAE,KAAK,UAAU,SAAS,MAAM,EAAE,EAAE,EACnE,MAAM,KACP,CAAC;AAEF,KAAI;AACF,QAAM,MAAM,iBAAiB,EAAE,IAAM;SAC/B;;;;;;;AAUV,eAAsB,sBAAsB,aAA8C;CACxF,MAAM,UAAU,MAAM,aAAa;AACnC,KAAI,CAAC,QAAS,QAAO;CACrB,MAAM,UAAmB;EAAE,GAAG;EAAS,oBAAoB;EAAa;AACxE,OAAM,aAAa,QAAQ;AAC3B,QAAO;;AAGT,eAAsB,eAA8C;AAMlE,KAAI,kBAAkB,EAAE;AACtB,oBAAkB;AAClB,SAAO,EAAE,SAAS,OAAO;;AAE3B,KAAI;AACF,QAAM,OAAO,iBAAiB,CAAC;AAC/B,SAAO,EAAE,SAAS,MAAM;UACjB,KAAK;AAEZ,MADc,IAA8B,SAC/B,SAAU,QAAO,EAAE,SAAS,OAAO;AAChD,QAAM;;;AAIV,SAAgB,4BAAoC;AAClD,QAAO,iBAAiB;;;;AC1V1B,MAAM,4BAA4B;AAElC,SAAgB,sBAAsB,MAAuB;AAC3D,QAAO,0BAA0B,KAAK,KAAK;;AAU7C,MAAM,wBAAgD;CACpD,wBACE;CACF,0BACE;CACH;;;;;;;;;;;;;;;;;;;;;;;;;;AAwCD,SAAgB,iBAAiB,OAAsC;CACrE,MAAM,EAAE,WAAW,QAAQ,aAAa;AACxC,KAAI,CAAC,UAAW,QAAO;AACvB,KAAI,CAAC,sBAAsB,UAAU,CAAE,QAAO;CAC9C,MAAM,SAAS,sBAAsB;AACrC,KAAI,WAAW,KAAA,EAEb,QAAO,GAAG,SADW,SAAS,oBAAoB,OAAO,KAAK;AAOhE,QAAO;;;;AC/FT,MAAMC,kBAAgB,CAAC,cAAc,aAAa;AAClD,MAAM,WAAW;AASjB,IAAa,sBAAb,cAAyC,MAAM;CAC7C,YAAY,SAAiB;AAC3B,QAAM,QAAQ;AACd,OAAK,OAAO;;;AAIhB,eAAeC,aAAW,MAAgC;AACxD,KAAI;AACF,QAAM,OAAO,KAAK;AAClB,SAAO;SACD;AACN,SAAO;;;AAIX,eAAe,gBAAgB,UAA0C;CACvE,IAAI,MAAM;CACV,MAAM,OAAO,QAAQ,IAAI,QAAQ,QAAQ,IAAI;AAC7C,MAAK,IAAI,IAAI,GAAG,IAAI,UAAU,KAAK;AACjC,OAAK,MAAM,QAAQD,iBAAe;GAChC,MAAM,YAAY,KAAK,KAAK,KAAK;AACjC,OAAI,MAAMC,aAAW,UAAU,CAAE,QAAO;;AAK1C,MAAI,MAAMA,aAAW,KAAK,KAAK,OAAO,CAAC,CAAE,QAAO;AAChD,MAAI,QAAQ,QAAQ,KAAM,QAAO;EACjC,MAAM,SAAS,QAAQ,IAAI;AAC3B,MAAI,WAAW,IAAK,QAAO;AAC3B,QAAM;;AAER,QAAO;;AAGT,SAAS,gBAAgB,OAAgC,KAAiC;CACxF,MAAM,IAAI,MAAM;AAChB,KAAI,MAAM,KAAA,KAAa,MAAM,KAAM,QAAO,KAAA;AAC1C,KAAI,OAAO,MAAM,YAAY,CAAC,OAAO,UAAU,EAAE,IAAI,KAAK,EAAG,QAAO,KAAA;AACpE,QAAO;;;;;;;;;;;;;AAcT,eAAsB,mBAAmB,KAA6C;CACpF,MAAM,OAAO,MAAM,gBAAgB,IAAI;AACvC,KAAI,CAAC,KAAM,QAAO;CAClB,MAAM,MAAM,MAAM,SAAS,MAAM,OAAO;CACxC,IAAI;AACJ,KAAI;AACF,WAAS,KAAK,aAAa,CAAC,SAAS,QAAQ,GAAG,KAAK,MAAM,IAAI,GAAGC,MAAU,IAAI;UACzE,KAAK;AAEZ,QAAM,IAAI,oBAAoB,sCAAsC,KAAK,IAD1D,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,GACuB;;AAExF,KAAI,WAAW,QAAQ,OAAO,WAAW,YAAY,MAAM,QAAQ,OAAO,CACxE,OAAM,IAAI,oBAAoB,sBAAsB,KAAK,mBAAmB;CAE9E,MAAM,MAAM;CACZ,MAAM,cAAc,gBAAgB,KAAK,cAAc;CACvD,MAAM,YAAY,gBAAgB,KAAK,YAAY;AACnD,QAAO;EACL,GAAI,gBAAgB,KAAA,IAAY,EAAE,aAAa,GAAG,EAAE;EACpD,GAAI,cAAc,KAAA,IAAY,EAAE,WAAW,GAAG,EAAE;EAChD,QAAQ;EACT;;;;;;;;;;;;;;;;;;;AAwBH,eAAsB,sBACpB,MACA,WACgC;AAChC,KAAI,CAAC,OAAO,UAAU,UAAU,IAAI,aAAa,EAC/C,OAAM,IAAI,oBAAoB,qDAAqD,YAAY;CAEjG,IAAI;AACJ,KAAI;AACF,QAAM,MAAM,SAAS,MAAM,OAAO;UAC3B,KAAK;AAEZ,QAAM,IAAI,oBAAoB,qCAAqC,KAAK,IADzD,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,GACsB;;CAGvF,MAAM,OADS,KAAK,aAAa,CAAC,SAAS,QAAQ,GAC7B,YAAY,MAAM,KAAK,UAAU,GAAG,YAAY,MAAM,KAAK,UAAU;AAC3F,KAAI,SAAS,KAAM,QAAO;EAAE,QAAQ;EAAa;EAAM;AACvD,OAAM,UAAU,MAAM,MAAM,OAAO;AACnC,QAAO;EAAE,QAAQ;EAAW;EAAM;;AAGpC,SAAS,YAAY,MAAc,KAAa,WAAkC;CAChF,MAAM,MAAM,cAAc,IAAI;AAC9B,KAAI,IAAI,OAAO,SAAS,EACtB,OAAM,IAAI,oBACR,sCAAsC,KAAK,IAAI,IAAI,OAAO,IAAI,WAAW,gBAC1E;AAEH,KAAI,CAAC,MAAM,IAAI,SAAS,CACtB,OAAM,IAAI,oBAAoB,sBAAsB,KAAK,mBAAmB;CAE9E,MAAM,WAAW,IAAI,IAAI,YAAY;AACrC,KAAI,OAAO,aAAa,YAAY,aAAa,UAAW,QAAO;AACnE,KAAI,IAAI,aAAa,UAAU;AAC/B,QAAO,IAAI,UAAU;;AAGvB,SAAS,YAAY,MAAc,KAAa,WAAkC;CAChF,IAAI;AACJ,KAAI;AACF,WAAS,KAAK,MAAM,IAAI;UACjB,KAAK;AAEZ,QAAM,IAAI,oBAAoB,sCAAsC,KAAK,IAD1D,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,GACuB;;AAExF,KAAI,WAAW,QAAQ,OAAO,WAAW,YAAY,MAAM,QAAQ,OAAO,CACxE,OAAM,IAAI,oBAAoB,sBAAsB,KAAK,mBAAmB;CAE9E,MAAM,MAAM;AACZ,KAAI,IAAI,cAAc,UAAW,QAAO;AACxC,KAAI,YAAY;CAChB,MAAM,SAAS,iBAAiB,IAAI;CACpC,MAAM,WAAW,IAAI,SAAS,KAAK,GAAG,OAAO;AAC7C,QAAO,KAAK,UAAU,KAAK,MAAM,OAAO,GAAG;;AAQ7C,SAAS,iBAAiB,KAA8B;AACtD,KAAI,CAAC,IAAI,SAAS,KAAK,CAAE,QAAO;CAEhC,MAAM,QADQ,IAAI,MAAM,eAAe,GACjB;AACtB,KAAI,CAAC,MAAO,QAAO;AACnB,KAAI,MAAM,SAAS,IAAK,CAAE,QAAO;AACjC,QAAO,MAAM;;;;AC/If,SAAgB,SAAS,SAAwB;AAC/C,SAAQ,OAAO,MAAM,GAAG,KAAK,UAAU,QAAQ,CAAC,IAAI;;AAGtD,SAAgB,qBAAqB,MAAe,QAAkC;AACpF,KAAI,KAOF,UAHyC,SACrC;EAAE,IAAI;EAAM,eAAe;EAAO;EAAQ,GAC1C;EAAE,IAAI;EAAM,eAAe;EAAO,CACrB;MACZ;AACL,UAAQ,OAAO,MACb,WAAW,oBACP,2DACA,yDACL;AACD,UAAQ,OAAO,MAAM,yBAAyB,2BAA2B,CAAC,IAAI;;;AAIlF,SAAgB,iBAAiB,MAAe,SAAuB;AACrE,KAAI,KACF,UAAS;EAAE,IAAI;EAAO,QAAQ;EAAiB;EAAS,CAAC;KAEzD,SAAQ,OAAO,MAAM,2CAA2C,QAAQ,KAAK;;AAIjF,SAAgB,aACd,MACA,SACA,SACM;AACN,KAAI,KACF,UAAS;EACP,IAAI;EACJ,QAAQ;EACR,GAAI,SAAS,WAAW,KAAA,IAAY,EAAE,QAAQ,QAAQ,QAAQ,GAAG,EAAE;EACnE,GAAI,SAAS,cAAc,KAAA,IAAY,EAAE,WAAW,QAAQ,WAAW,GAAG,EAAE;EAC5E;EACD,CAAC;KAEF,SAAQ,OAAO,MAAM,qBAAqB,QAAQ,IAAI;;;;;;;;;;;;;;;AAiB1D,eAAsB,qBAAqB,MAAe,KAA6B;AACrF,KAAI,eAAe,gBAAgB,IAAI,aAAa;AAClD,uBAAqB,MAAM,kBAAkB;AAC7C,SAAO,eAAe,SAAS,iBAAiB;;AAElD,KAAI,eAAe,cAAc;AAM/B,eAAa,MALG,iBAAiB;GAC/B,WAAW,IAAI;GACf,QAAQ,IAAI;GACZ,UAAU,IAAI;GACf,CAAC,EAC0B;GAAE,QAAQ,IAAI;GAAQ,WAAW,IAAI;GAAW,CAAC;AAC7E,SAAO,eAAe,SAAS,SAAS;;AAE1C,KAAI,eAAe,cAAc;AAC/B,mBAAiB,MAAM,IAAI,QAAQ;AACnC,SAAO,eAAe,SAAS,aAAa;;AAE9C,cAAa,MAAO,IAAc,QAAQ;AAC1C,QAAO,eAAe,SAAS,SAAS;;AAS1C,SAAgBC,mBAAiB,KAA4B;AAC3D,KAAI,CAAC,aAAa,KAAK,IAAI,CAAE,QAAO;CACpC,MAAM,IAAI,OAAO,SAAS,KAAK,GAAG;AAClC,QAAO,OAAO,cAAc,EAAE,GAAG,IAAI;;;;;;;;;;;;;;;;;AAkBvC,eAAsB,wBAAwB,MAQ5C;CACA,MAAM,UAAU,MAAM,aAAa;AACnC,KAAI,CAAC,SAAS;AACZ,uBAAqB,KAAK,KAAK;AAC/B,QAAM,eAAe,SAAS,iBAAiB;AAC/C,SAAO;;CAGT,IAAI;AACJ,KAAI,KAAK,WAAW;EAClB,MAAM,MAAM,OAAO,KAAK,UAAU;EAClC,MAAM,SAASA,mBAAiB,IAAI;AACpC,MAAI,WAAW,MAAM;GACnB,MAAM,UAAU,+CAA+C,IAAI;AACnE,OAAI,KAAK,KAAM,UAAS;IAAE,IAAI;IAAO,QAAQ;IAAc;IAAS,CAAC;OAChE,SAAQ,OAAO,MAAM,GAAG,QAAQ,IAAI;AACzC,SAAM,eAAe,SAAS,MAAM;AACpC,UAAO;;AAET,oBAAkB;;CAGpB,IAAI;AACJ,KAAI;AACF,QAAM,MAAM,kBAAkB;GAC5B,GAAI,oBAAoB,KAAA,IAAY,EAAE,iBAAiB,GAAG,EAAE;GAC5D,GAAI,QAAQ,uBAAuB,KAAA,IAC/B,EAAE,oBAAoB,QAAQ,oBAAoB,GAClD,EAAE;GACP,CAAC;UACK,KAAK;AACZ,QAAM,2BAA2B,KAAK,MAAM,IAAI;AAChD,SAAO;;AAGT,QAAO;EAAE;EAAS,GAAG;EAAK;;;;;;;;AAS5B,eAAsB,eAAe,MAAwC;CAC3E,MAAM,UAAU,MAAM,aAAa;AACnC,KAAI,CAAC,SAAS;AACZ,uBAAqB,KAAK;AAC1B,QAAM,eAAe,SAAS,iBAAiB;AAC/C,SAAO;;AAET,QAAO;;AAyBT,IAAa,kBAAb,cAAqC,MAAM;CACzC;CACA,YAAY,QAAiD,SAAiB;AAC5E,QAAM,QAAQ;AACd,OAAK,OAAO;AACZ,OAAK,SAAS;;;AAIlB,SAAS,mBAAmB,MAAkC;CAC5D,MAAM,MAAM,QAAQ,IAAI;AACxB,KAAI,QAAQ,KAAA,KAAa,QAAQ,GAAI,QAAO,KAAA;CAC5C,MAAM,SAASA,mBAAiB,IAAI;AACpC,KAAI,WAAW,KACb,OAAM,IAAI,gBAAgB,eAAe,GAAG,KAAK,mCAAmC,IAAI,GAAG;AAE7F,QAAO;;;;;;;;;;;;;;;;;;;;;AAsBT,eAAsB,kBAAkB,OAAoD;CAC1F,MAAM,MAAM,MAAM,OAAO,QAAQ,KAAK;CAEtC,IAAI,UAAiC;AACrC,KAAI;AACF,YAAU,MAAM,mBAAmB,IAAI;SACjC;AAKN,YAAU;;CAGZ,MAAM,eAAe,mBAAmB,kBAAkB;CAC1D,MAAM,aAAa,mBAAmB,YAAY;CAElD,IAAI;CACJ,IAAI;AACJ,KAAI,MAAM,oBAAoB,KAAA,GAAW;AACvC,gBAAc,MAAM;AACpB,oBAAkB;YACT,iBAAiB,KAAA,GAAW;AACrC,gBAAc;AACd,oBAAkB;YACT,SAAS,gBAAgB,KAAA,GAAW;AAC7C,gBAAc,QAAQ;AACtB,oBAAkB;YACT,MAAM,uBAAuB,KAAA,GAAW;AACjD,gBAAc,MAAM;AACpB,oBAAkB;;AAGpB,KAAI,gBAAgB,KAAA,KAAa,oBAAoB,KAAA,EACnD,OAAM,IAAI,gBACR,yBACA,2IACD;CAGH,IAAI;AACJ,KAAI,MAAM,kBAAkB,KAAA,EAC1B,WAAU;EAAE,WAAW,MAAM;EAAe,iBAAiB;EAAQ;UAC5D,eAAe,KAAA,EACxB,WAAU;EAAE,WAAW;EAAY,iBAAiB;EAAO;UAClD,SAAS,cAAc,KAAA,KAAa,oBAAoB,OACjE,WAAU;EAAE,WAAW,QAAQ;EAAW,iBAAiB;EAAQ;AAGrE,QAAO;EACL;EACA;EACA,GAAI,YAAY,KAAA,IAAY,UAAU,EAAE;EACxC,GAAI,YAAY,OAAO,EAAE,aAAa,QAAQ,QAAQ,GAAG,EAAE;EAC5D;;;;;;;;;AAUH,eAAe,2BAA2B,MAAe,KAA6B;AACpF,KAAI,eAAe,mBAAmB,IAAI,WAAW,eAAe;AAClE,MAAI,KAAM,UAAS;GAAE,IAAI;GAAO,QAAQ;GAAe,SAAS,IAAI;GAAS,CAAC;MACzE,SAAQ,OAAO,MAAM,GAAG,IAAI,QAAQ,IAAI;AAC7C,QAAM,eAAe,SAAS,MAAM;AACpC;;AAEF,KAAI,eAAe,mBAAmB,IAAI,WAAW,yBAAyB;AAC5E,MAAI,KAAM,UAAS;GAAE,IAAI;GAAO,QAAQ;GAAyB,CAAC;MAC7D,SAAQ,OAAO,MAAM,GAAG,IAAI,QAAQ,IAAI;AAC7C,QAAM,eAAe,SAAS,MAAM;AACpC;;AAEF,OAAM;;;;;;;;;;;;;;;;;;;;AAqBR,eAAsB,iBAAiB,MAYrC;CAIA,IAAI;CACJ,MAAM,MAAM,KAAK;AACjB,KAAI,QAAQ,KAAA,KAAa,QAAQ,QAAQ,QAAQ,IAAI;EACnD,MAAM,MAAM,OAAO,QAAQ,WAAW,MAAM,OAAO,IAAI;EACvD,MAAM,SAASA,mBAAiB,IAAI;AACpC,MAAI,WAAW,MAAM;GAEnB,MAAM,UAAU,GADF,KAAK,eAAe,QAAQ,UAAU,SAC3B,mCAAmC,KAAK,UAAU,IAAI,CAAC;AAChF,OAAI,KAAK,KAAM,UAAS;IAAE,IAAI;IAAO,QAAQ;IAAc;IAAS,CAAC;OAChE,SAAQ,OAAO,MAAM,GAAG,QAAQ,IAAI;AACzC,SAAM,eAAe,SAAS,MAAM;AACpC,UAAO;;AAET,kBAAgB;;CAGlB,MAAM,UAAU,MAAM,aAAa;AACnC,KAAI,CAAC,SAAS;AACZ,uBAAqB,KAAK,KAAK;AAC/B,QAAM,eAAe,SAAS,iBAAiB;AAC/C,SAAO;;CAGT,IAAI;AACJ,KAAI,KAAK,WAAW;EAClB,MAAM,QAAQ,OAAO,KAAK,UAAU;EACpC,MAAM,SAASA,mBAAiB,MAAM;AACtC,MAAI,WAAW,MAAM;GACnB,MAAM,UAAU,+CAA+C,MAAM;AACrE,OAAI,KAAK,KAAM,UAAS;IAAE,IAAI;IAAO,QAAQ;IAAc;IAAS,CAAC;OAChE,SAAQ,OAAO,MAAM,GAAG,QAAQ,IAAI;AACzC,SAAM,eAAe,SAAS,MAAM;AACpC,UAAO;;AAET,oBAAkB;;CAGpB,IAAI;AACJ,KAAI;AACF,QAAM,MAAM,kBAAkB;GAC5B,GAAI,oBAAoB,KAAA,IAAY,EAAE,iBAAiB,GAAG,EAAE;GAC5D,GAAI,kBAAkB,KAAA,IAAY,EAAE,eAAe,GAAG,EAAE;GACxD,GAAI,QAAQ,uBAAuB,KAAA,IAC/B,EAAE,oBAAoB,QAAQ,oBAAoB,GAClD,EAAE;GACP,CAAC;UACK,KAAK;AACZ,QAAM,2BAA2B,KAAK,MAAM,IAAI;AAChD,SAAO;;AAGT,QAAO;EAAE;EAAS,GAAG;EAAK;;;;;;;;;AAU5B,eAAsB,iBAAiB,KAAiB,MAAuC;AAC7F,KAAI,IAAI,cAAc,KAAA,EAAW,QAAO,IAAI;CAC5C,MAAM,UACJ;AACF,KAAI,KAAM,UAAS;EAAE,IAAI;EAAO,QAAQ;EAAkB;EAAS,CAAC;KAC/D,SAAQ,OAAO,MAAM,GAAG,QAAQ,IAAI;AACzC,OAAM,eAAe,SAAS,MAAM;AACpC,QAAO;;AAGT,SAAS,eACP,QACA,MACA,aACQ;AACR,KAAI,WAAW,OAAQ,QAAO,SAAS,cAAc,uBAAuB;AAC5E,KAAI,WAAW,MACb,QAAO,SAAS,cAAc,4BAA4B;AAC5D,KAAI,WAAW,QAAQ;AAIrB,MAAI,gBAAgB,KAAA,GAAW;GAC7B,MAAM,QAAQ,KAAK,IAAI,YAAY,YAAY,IAAI,EAAE,YAAY,YAAY,KAAK,CAAC;AAEnF,UAAO,SADM,SAAS,IAAI,YAAY,MAAM,QAAQ,EAAE,GAAG,YACpC;;AAEvB,SAAO;;AAET,QAAO;;;;;;;;;;AAWT,SAAgB,mBAAmB,KAAiB,MAA+B;AACjF,KAAI,KAAK,KAAM;CACf,MAAM,QAAQ,eAAe,IAAI,iBAAiB,aAAa,IAAI,YAAY;CAC/E,IAAI,OAAO,eAAe,IAAI,YAAY,GAAG;AAC7C,KAAI,IAAI,cAAc,KAAA,KAAa,IAAI,oBAAoB,KAAA,GAAW;EACpE,MAAM,SAAS,eAAe,IAAI,iBAAiB,OAAO,IAAI,YAAY;AAC1E,UAAQ,WAAW,IAAI,UAAU,GAAG;;AAEtC,SAAQ;AACR,SAAQ,OAAO,MAAM,KAAK;;;;AC5d5B,MAAMC,SAAO;AACb,MAAM,uBAAuB,GAAGA,OAAK;AAErC,eAAsB,2BACpB,SACA,OAAkC,EAAE,EACJ;AAChC,QAAO,kBAAyC;EAC9C,KAAK;EACL;EACA,GAAI,KAAK,YAAY,EAAE,WAAW,KAAK,WAAW,GAAG,EAAE;EACxD,CAAC;;AAkBJ,eAAsB,eACpB,SACA,OAAkC,EAAE,EACN;CAE9B,MAAM,MAAM,MAAM,kBAA2B;EAC3C,KAFU,GAAGA,OAAK;EAGlB;EACA,GAAI,KAAK,YAAY,EAAE,WAAW,KAAK,WAAW,GAAG,EAAE;EACxD,CAAC;AACF,KAAI,CAAC,MAAM,QAAQ,IAAI,CACrB,OAAM,IAAI,MAAM,4CAA4C;AAE9D,QAAO,IAAI,KAAK,OAAO,MAAgB;AACrC,MAAI,CAAC,SAAS,OAAO,UAAU,SAC7B,OAAM,IAAI,MAAM,wCAAwC,IAAI;EAE9D,MAAM,IAAI;AACV,SAAO;GACL,UAAU,QAAQ,EAAE,SAAS;GAC7B,SAAS,OAAO,EAAE,YAAY,WAAW,EAAE,UAAU;GACrD,YAAY,OAAO,EAAE,eAAe,WAAW,EAAE,aAAa;GAC9D,OAAO,OAAO,EAAE,UAAU,WAAW,EAAE,QAAQ;GAC/C,aAAa,OAAO,EAAE,gBAAgB,WAAW,EAAE,cAAc;GACjE,YAAY,OAAO,EAAE,eAAe,WAAW,EAAE,aAAa;GAC9D,UAAU,QAAQ,EAAE,SAAS;GAC7B,kBAAkB,QAAQ,EAAE,iBAAiB;GAC9C;GACD;;;;AC1DJ,MAAM,kBAAkB;AAExB,eAAsB,qBACpB,aACA,SACA,OAAkC,EAAE,EACV;CAI1B,MAAM,MAAM,MAAM,kBAA2C;EAC3D,KAFU,GAAG,gBAAgB,cAAc;EAG3C;EACA,GAAI,KAAK,YAAY,EAAE,WAAW,KAAK,WAAW,GAAG,EAAE;EACxD,CAAC;CACF,MAAM,KAAK,IAAI;CACf,MAAM,OAAO,IAAI;AACjB,KAAI,OAAO,OAAO,YAAY,CAAC,OAAO,UAAU,GAAG,IAAI,MAAM,KAAK,OAAO,SAAS,SAChF,OAAM,IAAI,MAAM,4CAA4C,cAAc;CAE5E,MAAM,EAAE,IAAI,KAAK,MAAM,OAAO,GAAG,UAAU;AAC3C,QAAO;EAAE,aAAa;EAAI,eAAe;EAAM;EAAO;;AAexD,eAAsB,sBACpB,aACA,SACA,OAAkC,EAAE,EACJ;CAEhC,MAAM,MAAM,MAAM,kBAA2C;EAC3D,KAFU,GAAG,gBAAgB,cAAc,YAAY;EAGvD;EACA,GAAI,KAAK,YAAY,EAAE,WAAW,KAAK,WAAW,GAAG,EAAE;EACxD,CAAC;CACF,MAAM,aAAa,IAAI;AACvB,KAAI,OAAO,eAAe,UACxB,OAAM,IAAI,MAAM,6CAA6C,cAAc;AAQ7E,QAAO;EAAE;EAAY,cANA,OAAO,IAAI,iBAAiB,WAAW,IAAI,eAAe;EAM5C,eALb,OAAO,IAAI,kBAAkB,WAAW,IAAI,gBAAgB;EAKhC,SAHhD,IAAI,WAAW,OAAO,IAAI,YAAY,WACjC,IAAI,UACL;EACqD;;AAS7D,MAAa,uBAAuB;CAClC;CACA;CACA;CACA;CACA;CACD;AAmBD,MAAM,2BAA2B;AAejC,eAAsB,uBACpB,QACA,SACA,OAAkC,EAAE,EACJ;CAChC,MAAM,OAAO,OAAO,QAAQ;CAC5B,MAAM,KAAK,IAAI,iBAAiB;AAChC,IAAG,IAAI,YAAY,OAAO,YAAY,yBAAyB;AAC/D,IAAG,IAAI,UAAU,OAAO,UAAU,GAAG;AACrC,IAAG,IAAI,QAAQ,OAAO,KAAK,CAAC;CAE5B,MAAM,MAAM,MAAM,kBAA2B;EAC3C,KAFU,GAAG,gBAAgB,cAAc,OAAO,YAAY,iBAAiB,GAAG,UAAU;EAG5F;EACA,GAAI,KAAK,YAAY,EAAE,WAAW,KAAK,WAAW,GAAG,EAAE;EACxD,CAAC;AACF,KAAI,QAAQ,QAAQ,OAAO,QAAQ,YAAY,MAAM,QAAQ,IAAI,CAC/D,OAAM,IAAI,MAAM,2CAA2C,OAAO,cAAc;CAElF,MAAM,OAAO;AAEb,QAAO;EACL,WAFe,MAAM,QAAQ,KAAK,SAAS,GAAG,KAAK,WAAW,EAAE,EAE7C,KAAK,MACtB,KAAK,OAAO,MAAM,WAAY,IAAgC,EAAE,CACjE;EACD,WAAW,OAAO,KAAK,cAAc,WAAW,KAAK,YAAY;EACjE,aAAa,OAAO,KAAK,gBAAgB,WAAW,KAAK,cAAc;EACxE;;AAGH,eAAsB,oBACpB,aACA,MACA,SACA,OAAkC,EAAE,EACD;CAEnC,MAAM,MAAM,MAAM,kBAA2B;EAC3C,KAFU,GAAG,gBAAgB,cAAc,YAAY,2BAA2B,KAAK;EAGvF;EACA,GAAI,KAAK,YAAY,EAAE,WAAW,KAAK,WAAW,GAAG,EAAE;EACxD,CAAC;AACF,KAAI,CAAC,MAAM,QAAQ,IAAI,CACrB,OAAM,IAAI,MAAM,6CAA6C,OAAO;AAEtE,QAAO,IAAI,KAAK,OAAO,MAAqB;AAC1C,MAAI,CAAC,SAAS,OAAO,UAAU,SAC7B,OAAM,IAAI,MAAM,6CAA6C,EAAE,YAAY,OAAO;EAEpF,MAAM,IAAI;AAIV,SAAO;GACL,UAAU,QAAQ,EAAE,SAAS;GAC7B,SAAS,OAAO,EAAE,YAAY,WAAW,EAAE,UAAU;GACrD,YAAY,OAAO,EAAE,eAAe,WAAW,EAAE,aAAa;GAC9D,OAAO,OAAO,EAAE,UAAU,WAAW,EAAE,QAAQ;GAC/C,aAAa,OAAO,EAAE,gBAAgB,WAAW,EAAE,cAAc;GACjE,YAAY,OAAO,EAAE,eAAe,WAAW,EAAE,aAAa;GAC9D,UAAU,QAAQ,EAAE,SAAS;GAC7B,kBAAkB,QAAQ,EAAE,iBAAiB;GAC9C;GACD;;;;;;;;;;;;;;;;;;;;AA8BJ,eAAsB,oBACpB,aACA,OACA,SACA,OAAkC,EAAE,EACrB;AACf,KAAI,MAAM,WAAW,EACnB,OAAM,IAAI,MAAM,iDAAiD;AAGnE,OAAM,kBAA2B;EAC/B,QAAQ;EACR,KAHU,GAAG,gBAAgB,cAAc,YAAY;EAIvD;EACA,MAAM,EAAE,YAAY,MAAM,KAAK,EAAE,SAAS,kBAAkB;GAAE;GAAS;GAAY,EAAE,EAAE;EACvF,GAAI,KAAK,YAAY,EAAE,WAAW,KAAK,WAAW,GAAG,EAAE;EACxD,CAAC;;;;ACrMJ,IAAa,iBAAb,cAAoC,MAAM;CACxC;CACA;CAEA,YAAY,MAAuE;AACjF,QAAM,KAAK,QAAQ;AACnB,OAAK,OAAO;AACZ,OAAK,OAAO,KAAK;AACjB,OAAK,SAAS,KAAK;;;AAavB,MAAM,YAAY,IAAI,WAAW;CAAC;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAK,CAAC;AAElF,MAAM,YAAY,IAAI,WAAW;CAAC;CAAM;CAAM;CAAM;CAAK,CAAC;AAE1D,SAAS,WAAW,OAAmB,QAA6B;AAClE,KAAI,MAAM,SAAS,OAAO,OAAQ,QAAO;AACzC,MAAK,IAAI,IAAI,GAAG,IAAI,OAAO,QAAQ,IACjC,KAAI,MAAM,OAAO,OAAO,GAAI,QAAO;AAErC,QAAO;;AAGT,SAAgB,mBAAmB,OAAgD;AACjF,KAAI,WAAW,OAAO,UAAU,CAAE,QAAO;AACzC,KAAI,WAAW,OAAO,UAAU,CAAE,QAAO;AACzC,QAAO;;;;;;;;AAST,eAAsB,cAAc,MAAsC;CACxE,IAAI;AACJ,KAAI;AACF,QAAM,MAAM,SAAS,KAAK;UACnB,KAAK;AACZ,QAAM,IAAI,eAAe;GACvB;GACA,QAAQ;GACR,SAAU,IAAc;GACzB,CAAC;;CAEJ,MAAM,QAAQ,IAAI,WAAW,IAAI,QAAQ,IAAI,YAAY,IAAI,WAAW;CACxE,MAAM,EAAE,cAAc,WAAW,4BAA4B,OAAO,KAAK;AACzE,QAAO;EAAE;EAAc;EAAO;EAAQ;;;;;;;AAQxC,SAAgB,4BACd,OACA,cACmD;CACnD,MAAM,SAAS,mBAAmB,MAAM;AACxC,KAAI,WAAW,MACb,QAAO;EAAE,cAAc,0BAA0B,OAAO,aAAa;EAAE;EAAQ;AAEjF,KAAI,WAAW,MACb,QAAO;EAAE,cAAc,0BAA0B,OAAO,aAAa;EAAE;EAAQ;AAEjF,OAAM,IAAI,eAAe;EACvB,MAAM;EACN,QAAQ;EACR,SACE;EACH,CAAC;;AAMJ,MAAM,iBAAiB;AACvB,MAAM,mBAAmB;AAEzB,MAAM,kBAAkB,iBAAiB,mBADjB;AAGxB,SAAS,0BAA0B,OAAmB,cAA8B;AAClF,KAAI,MAAM,SAAS,gBACjB,OAAM,IAAI,eAAe;EACvB,MAAM;EACN,QAAQ;EACR,SAAS;EACV,CAAC;CAEJ,MAAM,OAAO,IAAI,SAAS,MAAM,QAAQ,MAAM,YAAY,MAAM,WAAW;CAG3E,MAAM,YAAY,OAAO,KAAK,aAAa,iBAAiB,kBAAkB,MAAM,CAAC;AACrF,KAAI,CAAC,OAAO,SAAS,UAAU,IAAI,aAAa,EAC9C,OAAM,IAAI,eAAe;EACvB,MAAM;EACN,QAAQ;EACR,SAAS,iCAAiC,UAAU;EACrD,CAAC;CAEJ,MAAM,cAAc;CACpB,MAAM,YAAY,cAAc;AAChC,KAAI,MAAM,SAAS,UACjB,OAAM,IAAI,eAAe;EACvB,MAAM;EACN,QAAQ;EACR,SAAS;EACV,CAAC;CAIJ,MAAM,eADS,yBADK,MAAM,SAAS,aAAa,UAAU,EACL,CAAC,GAAG,EAAE,CAAC,CAChC,IAAI,EAAE;AAClC,KAAI,OAAO,iBAAiB,YAAY,iBAAiB,GACvD,OAAM,IAAI,eAAe;EACvB,MAAM;EACN,QAAQ;EACR,SAAS;EACV,CAAC;AAEJ,QAAO;;AAUT,SAAS,yBACP,OACA,oBACqB;CACrB,MAAM,SAAS,IAAI,IAAI,mBAAmB;CAC1C,MAAM,sBAAM,IAAI,KAAqB;CACrC,MAAM,UAAU,IAAI,YAAY,SAAS,EAAE,OAAO,OAAO,CAAC;CAC1D,IAAI,SAAS;AACb,QAAO,SAAS,MAAM,QAAQ;EAC5B,MAAM,EAAE,OAAO,KAAK,SAAS,WAAW,OAAO,OAAO;AACtD,WAAS;EACT,MAAM,cAAc,OAAO,OAAO,GAAG;EACrC,MAAM,WAAW,OAAO,MAAM,GAAG;AACjC,MAAI,aAAa,EAEf,UAAS,WAAW,OAAO,OAAO,CAAC;WAC1B,aAAa,EAEtB,WAAU;WACD,aAAa,GAAG;GAEzB,MAAM,EAAE,OAAO,KAAK,MAAM,aAAa,WAAW,OAAO,OAAO;AAChE,YAAS;GACT,MAAM,aAAa,SAAS,OAAO,IAAI;AACvC,OAAI,aAAa,MAAM,OAGrB;AAEF,OAAI,OAAO,IAAI,YAAY,CACzB,KAAI,IAAI,aAAa,QAAQ,OAAO,MAAM,SAAS,QAAQ,WAAW,CAAC,CAAC;AAE1E,YAAS;aACA,aAAa,EAEtB,WAAU;MAKV;;AAGJ,QAAO;;AAGT,SAAS,WAAW,OAAmB,OAAgD;CACrF,IAAI,QAAQ;CACZ,IAAI,QAAQ;CACZ,IAAI,IAAI;AAER,MAAK,IAAI,IAAI,GAAG,IAAI,MAAM,IAAI,MAAM,QAAQ,KAAK,KAAK;EACpD,MAAM,OAAO,MAAM;AACnB,MAAI,SAAS,KAAA,EAAW;AACxB,WAAS,OAAO,OAAO,IAAK,IAAI;AAChC,OAAK,OAAO,SAAU,EACpB,QAAO;GAAE;GAAO,MAAM,IAAI;GAAG;AAE/B,WAAS;;AAGX,QAAO;EAAE;EAAO,MAAM;EAAG;;AAK3B,SAAS,0BAA0B,OAAmB,cAA8B;CAClF,IAAI;AACJ,KAAI;AAKF,YAAU,UAAU,OAAO,EAAE,SAAS,SAAS,KAAK,SAAS,YAAY,CAAC;UACnE,KAAK;AACZ,QAAM,IAAI,eAAe;GACvB,MAAM;GACN,QAAQ;GACR,SAAS,oBAAqB,IAAc;GAC7C,CAAC;;CAEJ,MAAM,QAAQ,QAAQ;AACtB,KAAI,CAAC,MACH,OAAM,IAAI,eAAe;EACvB,MAAM;EACN,QAAQ;EACR,SAAS;EACV,CAAC;CAEJ,IAAI;AACJ,KAAI;AACF,WAAS,KAAK,MAAM,IAAI,aAAa,CAAC,OAAO,MAAM,CAAC;UAC7C,KAAK;AACZ,QAAM,IAAI,eAAe;GACvB,MAAM;GACN,QAAQ;GACR,SAAS,+BAAgC,IAAc;GACxD,CAAC;;AAEJ,KAAI,WAAW,QAAQ,OAAO,WAAW,YAAY,MAAM,QAAQ,OAAO,CACxE,OAAM,IAAI,eAAe;EACvB,MAAM;EACN,QAAQ;EACR,SAAS;EACV,CAAC;CAEJ,MAAM,WAAY,OAAmC;AACrD,KAAI,aAAa,QAAQ,OAAO,aAAa,YAAY,MAAM,QAAQ,SAAS,CAC9E,OAAM,IAAI,eAAe;EACvB,MAAM;EACN,QAAQ;EACR,SACE;EACH,CAAC;CAEJ,MAAM,eAAgB,SAAqC;AAC3D,KAAI,OAAO,iBAAiB,YAAY,iBAAiB,GACvD,OAAM,IAAI,eAAe;EACvB,MAAM;EACN,QAAQ;EACR,SACE;EACH,CAAC;AAEJ,QAAO;;;;ACxMT,eAAsB,UAAU,MAAkB,OAAmB,EAAE,EAAiB;AAQtF,KAAI,OAAO,KAAK,QAAQ,YAAY,KAAK,QAAQ,MAAMC,mBAAiB,KAAK,IAAI,KAAK,MAAM;AAC1F,MAAI,KAAK,KACP,UAAS;GACP,IAAI;GACJ,QAAQ;GACR,SAAS,yCAAyC,KAAK,UAAU,KAAK,IAAI,CAAC;GAC5E,CAAC;MAEF,SAAQ,OAAO,MAAM,6BAA6B,KAAK,UAAU,KAAK,IAAI,CAAC,IAAI;AAEjF,SAAO,eAAe,SAAS,MAAM;;AAGvC,KAAI,OAAO,KAAK,SAAS,YAAY,KAAK,SAAS,IAAI;AACrD,MAAI,KAAK,KACP,UAAS;GAAE,IAAI;GAAO,QAAQ;GAAgB,SAAS;GAAmC,CAAC;MAE3F,SAAQ,OAAO,MAAM,iDAAiD;AAExE,SAAO,eAAe,SAAS,MAAM;;CAGvC,MAAM,gBAAgB,QAAQ,KAAK,cAAc;CACjD,MAAM,UAAU,QAAQ,KAAK,QAAQ;CACrC,MAAM,UAAU,QAAQ,KAAK,QAAQ;CACrC,MAAM,eAAe,OAAO,KAAK,iBAAiB,WAAW,KAAK,eAAe,KAAA;AAEjF,KAAI,iBAAiB,iBAAiB,KAAA,GAAW;AAC/C,MAAI,KAAK,KACP,UAAS;GACP,IAAI;GACJ,QAAQ;GACR,SAAS;GACV,CAAC;MAEF,SAAQ,OAAO,MACb,0EACD;AAEH,SAAO,eAAe,SAAS,MAAM;;AAGvC,KAAI,WAAW,CAAC,SAAS;AACvB,MAAI,KAAK,KACP,UAAS;GACP,IAAI;GACJ,QAAQ;GACR,SAAS;GACV,CAAC;MAEF,SAAQ,OAAO,MACb,kGAED;AAEH,SAAO,eAAe,SAAS,MAAM;;CAMvC,MAAM,aAAa,KAAK,kBAAkB;CAC1C,IAAI;AACJ,KAAI;AACF,eAAa,MAAM,WAAW,KAAK,KAAK;UACjC,KAAK;AACZ,MAAI,eAAe,gBAAgB;GACjC,MAAM,SAAS,IAAI,WAAW,oBAAoB,oBAAoB;AACtE,OAAI,KAAK,KACP,UAAS;IACP,IAAI;IACJ;IACA,MAAM,IAAI;IACV,cAAc,IAAI;IAClB,SAAS,IAAI;IACd,CAAC;OAEF,SAAQ,OAAO,MAAM,eAAe,IAAI,QAAQ,IAAI;AAEtD,UAAO,eAAe,SAAS,MAAM;;AAEvC,QAAM;;CAQR,MAAM,eACJ,OAAO,KAAK,iBAAiB,YAAY,KAAK,iBAAiB,KAC3D,KAAK,eACL,WAAW;AACjB,KAAI,iBAAiB,IAAI;AAIvB,MAAI,KAAK,KACP,UAAS;GACP,IAAI;GACJ,QAAQ;GACR,MAAM,KAAK;GACX,SAAS;GACV,CAAC;MAEF,SAAQ,OAAO,MAAM,uCAAuC;AAE9D,SAAO,eAAe,SAAS,MAAM;;CAQvC,MAAM,MAAM,MAAM,iBAAiB;EACjC,MAAM,KAAK;EACX,UAAU,KAAK;EACf,YAAY;EACZ,GAAI,KAAK,cAAc,KAAA,IAAY,EAAE,WAAW,KAAK,WAAW,GAAG,EAAE;EACtE,CAAC;AACF,KAAI,CAAC,IAAK;CACV,MAAM,QAAQ,MAAM,iBAAiB,KAAK,KAAK,KAAK;AACpD,KAAI,UAAU,KAAM;AACpB,oBAAmB,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;CAC5C,MAAM,EAAE,SAAS,gBAAgB;CAEjC,MAAM,OAAO,OAAO,KAAK,SAAS,YAAY,KAAK,KAAK,SAAS,IAAI,KAAK,OAAO,KAAA;CACjF,MAAM,QAAsB,CAAC,SAAS;AACtC,KAAI,cAAe,OAAM,KAAK,SAAS;AACvC,KAAI,QAAS,OAAM,KAAK,UAAU;AAElC,KAAI,KAAK,OACP,QAAO,UAAU;EACf,MAAM,KAAK;EACX,MAAM,KAAK;EACX;EACA;EACA,sBAAsB,OAAO,KAAK,iBAAiB,YAAY,KAAK,iBAAiB;EACrF;EACA;EACA;EACA;EACA;EACA;EACA;EACA,WAAW,KAAK;EACjB,CAAC;CAKJ,MAAM,UAAU,KAAK,YAAY,EAAE,WAAW,KAAK,WAAW,GAAG,EAAE;CACnE,IAAI,WAAW;CACf,IAAI,eAAyD;CAC7D,IAAI,WAAW;CACf,IAAI,eAAyD;AAE7D,KAAI;EACF,MAAM,OAAO,MAAM,0BACjB,aACA,OACA,cACA,QAAQ,SACR,QACD;AACD,MAAI,KAAK,iBAAiB,WAAW;AACnC,OAAI,KAAK,KACP,UAAS;IACP,IAAI;IACJ,QAAQ;IACR;IACA;IACA;IACA,cAAc,KAAK;IACnB,SAAS;IACV,CAAC;OAEF,SAAQ,OAAO,MACb,0BAA0B,aAAa,uBAAuB,KAAK,aAAa,qBACjF;AAEH,UAAO,eAAe,SAAS,MAAM;;AAEvC,QAAM,qBAAqB,KAAK,WAAW,WAAW,OAAO,QAAQ;AACrE,iBAAe,MAAM,wBACnB,aACA,OACA,cACA,QAAQ,SACR,QACD;AACD,MAAI,SAAS,KAAA,EACX,OAAM,eAAe,aAAa,OAAO,cAAc,MAAM,QAAQ,SAAS,QAAQ;AAExF,aAAW;UACJ,KAAK;AAIZ,SAAO,qBAAqB,KAAK,MAAM,IAAI;;AAG7C,KAAI,cACF,KAAI;AACF,iBAAe,MAAM,iBACnB;GACE;GACA,WAAW;GACX;GACA,cAAc,gBAAgB;GAC/B,EACD,QAAQ,SACR,QACD;AACD,aAAW;UACJ,KAAK;AACZ,SAAO,mBAAmB,KAAK,MAAM,KAAK;GACxC;GACA;GACA;GACA,UAAU;GACV,UAAU;GACV,UAAU;GACX,CAAC;;CAIN,IAAI,gBAA0D;AAC9D,KAAI,QACF,KAAI;AACF,kBAAgB,MAAM,kBACpB;GAAE;GAAa,WAAW;GAAO;GAAc,EAC/C,QAAQ,SACR,QACD;UACM,KAAK;AACZ,SAAO,mBAAmB,KAAK,MAAM,KAAK;GACxC;GACA;GACA;GACA,UAAU;GACV;GACA,UAAU;GACX,CAAC;;AAIN,KAAI,KAAK,MAAM;AACb,WAAS;GACP,IAAI;GACJ;GACA;GACA;GACA,cAAc,WAAW;GACzB;GACA;GACA,UAAU;GACV,QAAQ;GACR;GACA;GACD,CAAC;AACF,SAAO,eAAe,SAAS,GAAG;;AAGpC,SAAQ,OAAO,MACb,2BAA2B,MAAM,OAAO,YAAY,oBAChC,aAAa,mBACb,WAAW,MAAM,WAAW,mBAC5B,MAAM,KAAK,MAAM,CAAC,IACvC;AACD,QAAO,eAAe,SAAS,GAAG;;;;;;;;;AAUpC,eAAe,mBACb,MACA,KACA,UAQe;AACf,KAAI,eAAe,gBAAgB,IAAI,aAAa;AAClD,MAAI,KACF,UAAS;GACP,IAAI;GACJ,eAAe;GACf,QAAQ;GACR,GAAG;GACJ,CAAC;MAEF,SAAQ,OAAO,MAAM,yDAAyD;AAEhF,SAAO,eAAe,SAAS,iBAAiB;;AAElD,KAAI,eAAe,cAAc;EAC/B,MAAM,UAAU,iBAAiB;GAC/B,WAAW,IAAI;GACf,QAAQ,IAAI;GACZ,UAAU,IAAI;GACf,CAAC;AACF,MAAI,KACF,UAAS;GACP,IAAI;GACJ,QAAQ;GACR,QAAQ,IAAI;GACZ,GAAI,IAAI,cAAc,KAAA,IAAY,EAAE,WAAW,IAAI,WAAW,GAAG,EAAE;GACnE;GACA,GAAG;GACJ,CAAC;MAEF,SAAQ,OAAO,MAAM,qBAAqB,QAAQ,IAAI;AAExD,SAAO,eAAe,SAAS,SAAS;;AAE1C,KAAI,eAAe,cAAc;AAC/B,MAAI,KACF,UAAS;GACP,IAAI;GACJ,QAAQ;GACR,SAAS,IAAI;GACb,GAAG;GACJ,CAAC;MAEF,SAAQ,OAAO,MAAM,2CAA2C,IAAI,QAAQ,KAAK;AAEnF,SAAO,eAAe,SAAS,aAAa;;AAE9C,KAAI,KACF,UAAS;EACP,IAAI;EACJ,QAAQ;EACR,SAAU,IAAc;EACxB,GAAG;EACJ,CAAC;KAEF,SAAQ,OAAO,MAAM,qBAAsB,IAAc,QAAQ,IAAI;AAEvE,QAAO,eAAe,SAAS,SAAS;;AA4B1C,MAAM,6BAAgE;CACpE,YAAY;CACZ,eAAe;CACf,sBAAsB;CACtB,KAAK;CACL,KAAK;CACN;AAsCD,eAAe,UAAU,OAAmC;CAC1D,MAAM,UAAU,MAAM,YAAY,EAAE,WAAW,MAAM,WAAW,GAAG,EAAE;CACrE,MAAM,WAAW,MAAM,WAAW;CAClC,MAAM,YAAY,MAAM,uBAAuB,MAAM,iBAAiB,WAAW;CAEjF,MAAM,CAAC,aAAa,SAAS,MAAM,QAAQ,IAAI,CAC7C,iBAAiB,MAAM,aAAa,MAAM,SAAS,QAAQ,EAC3D,mBAAmB,MAAM,aAAa,MAAM,SAAS,QAAQ,CAC9D,CAAC;CAEF,MAAM,gBAAgB,cAAc,QAAQ,cAAc,SAAS,MAAM,SAAS,WAAW;AAE7F,KAAI,MAAM,MAAM;AACd,WAAS;GACP,IAAI;GACJ,QAAQ;GACR;GAKA,aAAa,MAAM;GACnB,OAAO,MAAM;GACb,cAAc,MAAM;GACpB,cAAc,MAAM,WAAW;GAC/B,OAAO,MAAM,WAAW,MAAM;GAC9B,OAAO,MAAM;GACb,MAAM,MAAM,QAAQ;GACpB,cAAc,MAAM,gBAAgB;GACpC,WAAW,MAAM;GACjB,QAAQ;IACN,MAAM,MAAM;IACZ,QAAQ,MAAM,WAAW;IACzB,cAAc,MAAM;IACpB,sBAAsB;IACtB,oBAAoB,MAAM,uBAAuB,SAAS;IAC1D;IACA,MAAM,MAAM,WAAW,MAAM;IAC9B;GACD,SAAS;IACP,aAAa,MAAM;IACnB,OAAO,MAAM;IACb,cAAc;IACd;IACD;GACD;GACD,CAAC;AACF,SAAO,eAAe,SAAS,GAAG;;AAGpC,SAAQ,OAAO,MACb,iBAAiB,OAAO;EAAE;EAAU;EAAW;EAAa;EAAO;EAAc,CAAC,CACnF;AACD,QAAO,eAAe,SAAS,GAAG;;AAGpC,eAAe,iBACb,aACA,SACA,SAC4B;AAK5B,KAAI;EAEF,MAAM,MADO,MAAM,2BAA2B,QAAQ,SAAS,QAAQ,EACvD,WAAW,MAAM,MAAM,EAAE,gBAAgB,YAAY;AACrE,MAAI,CAAC,GACH,QAAO;GACL,MAAM;GACN,QAAQ;GACR,OAAO,+CAA+C;GACvD;AAEH,SAAO;GAAE,MAAM,GAAG;GAAM,QAAQ;GAAc;UACvC,KAAK;AACZ,SAAO;GAAE,MAAM;GAAM,QAAQ;GAAW,OAAQ,IAAc;GAAS;;;AAI3E,eAAe,mBACb,aACA,SACA,SACsB;AAUtB,KAAI;EACF,MAAM,CAAC,WAAW,oBAAoB,MAAM,QAAQ,IAAI,CACtD,eAAe,QAAQ,SAAS,QAAQ,EACxC,QAAQ,IACN,qBAAqB,IACnB,OAAO,MACL,CAAC,GAAG,MAAM,oBAAoB,aAAa,GAAG,QAAQ,SAAS,QAAQ,CAAC,CAC3E,CACF,CACF,CAAC;EAEF,MAAM,WAA2B,EAAE;AACnC,OAAK,MAAM,KAAK,UACd,KAAI,EAAE,YAAY,CAAC,EAAE,SACnB,UAAS,KAAK;GACZ,OAAO;GACP,MAAM;GAKN,WAAW;GACX,OAAO,EAAE;GACT,QAAQ;GACT,CAAC;AAGN,OAAK,MAAM,CAAC,MAAM,UAAU,iBAC1B,MAAK,MAAM,KAAK,OAAO;AACrB,OAAI,CAAC,EAAE,YAAY,EAAE,SAAU;AAC/B,YAAS,KAAK;IACZ,OAAO;IACP;IACA,WAAW,2BAA2B;IACtC,OAAO,EAAE;IACT,QAAQ,gCAAgC;IACzC,CAAC;;AAGN,SAAO;GAAE;GAAU,SAAS;GAAM;UAC3B,KAAK;AACZ,SAAO;GAAE,UAAU,EAAE;GAAE,SAAS;GAAO,OAAQ,IAAc;GAAS;;;AAI1E,SAAS,iBACP,OACA,SAOQ;CACR,MAAM,QAAkB,EAAE;AAC1B,OAAM,KAAK,wBAAwB,MAAM,MAAM,IAAI;AAGnD,OAAM,KAAK,aAAa;AACxB,OAAM,KAAK,mBAAmB,MAAM,KAAK,IAAI;AAC7C,OAAM,KAAK,mBAAmB,MAAM,WAAW,OAAO,aAAa,CAAC,IAAI;AACxE,OAAM,KAAK,mBAAmB,MAAM,aAAa,IAAI;AACrD,KAAI,QAAQ,cAAc,MACxB,OAAM,KAAK,2CAA2C,QAAQ,SAAS,KAAK;UACnE,QAAQ,cAAc,KAC/B,OAAM,KAAK,0CAA0C;AAEvD,OAAM,KAAK,mBAAmB,YAAY,MAAM,WAAW,MAAM,WAAW,CAAC,IAAI;AAGjF,OAAM,KAAK,cAAc;AACzB,OAAM,KAAK,mBAAmB,MAAM,YAAY,IAAI;AACpD,OAAM,KAAK,mBAAmB,MAAM,MAAM,IAAI;AAC9C,OAAM,KAAK,0BAA0B;AACrC,KAAI,QAAQ,YAAY,SAAS,KAC/B,OAAM,KAAK,mBAAmB,QAAQ,YAAY,KAAK,IAAI;KAE3D,OAAM,KACJ,0BAA0B,QAAQ,YAAY,QAAQ,KAAK,QAAQ,YAAY,MAAM,KAAK,GAAG,IAC9F;AAIH,OAAM,KAAK,YAAY;AACvB,KAAI,CAAC,QAAQ,MAAM,SAAS;AAC1B,QAAM,KACJ,4CAA4C,QAAQ,MAAM,SAAS,gBAAgB,MACpF;AACD,QAAM,KACJ,sFACD;YACQ,QAAQ,MAAM,SAAS,WAAW,EAC3C,OAAM,KAAK,0CAA0C;KAErD,MAAK,MAAM,KAAK,QAAQ,MAAM,SAC5B,OAAM,KACJ,cAAc,EAAE,MAAM,GAAG,EAAE,KAAK,KAAK,EAAE,MAAM,cAAc,EAAE,UAAU,iBACtD,EAAE,OAAO,IAC3B;AAML,OAAM,KAAK,WAAW;CACtB,MAAM,YAAY,MAAM,MACrB,KAAK,MAAM;AACV,MAAI,MAAM,SACR,QAAO,yBAAyB,KAAK,UAAU,MAAM,gBAAgB,GAAG,CAAC;AAC3E,MAAI,MAAM,UAAW,QAAO,YAAY,MAAM,UAAU,cAAc,gBAAgB;AACtF,SAAO;GACP,CACD,KAAK,MAAM;AACd,OAAM,KAAK,mBAAmB,UAAU,IAAI;AAC5C,OAAM,KAAK,mBAAmB,MAAM,QAAQ,SAAS,IAAI;AAQzD,OAAM,KAAK,aAAa;AACxB,KAAI,CAAC,QAAQ,aACX,OAAM,KAAK,4EAA4E;UAC9E,QAAQ,YAAY,SAAS,KACtC,OAAM,KACJ,uJACD;KAED,OAAM,KAAK,sDAAsD;AAEnE,QAAO,MAAM,KAAK,GAAG;;AAGvB,SAAS,YAAY,GAAmB;AACtC,KAAI,IAAI,KAAM,QAAO,GAAG,EAAE;AAC1B,KAAI,IAAI,OAAO,KAAM,QAAO,IAAI,IAAI,MAAM,QAAQ,EAAE,CAAC;AACrD,QAAO,IAAI,KAAK,OAAO,OAAO,QAAQ,EAAE,CAAC;;;;AC3uB3C,IAAa,gBAAb,cAAmC,MAAM;CACvC;CACA;CAEA,YAAY,MAAyB,SAAiB,OAAgB;AACpE,QAAM,QAAQ;AACd,OAAK,OAAO;AACZ,OAAK,OAAO;AACZ,OAAK,QAAQ;;;AAqBjB,MAAM,gBAAgB,CAAC,cAAc,aAAa;AAElD,eAAeC,aAAW,MAAgC;AACxD,KAAI;AACF,QAAM,OAAO,KAAK;AAClB,SAAO;SACD;AACN,SAAO;;;;;;;;AASX,eAAsB,oBACpB,UACA,KACiB;AACjB,KAAI,UAAU;EACZ,MAAM,MAAM,WAAW,SAAS,GAAG,WAAW,QAAQ,KAAK,SAAS;AACpE,MAAI,CAAE,MAAMA,aAAW,IAAI,CACzB,OAAM,IAAI,cAAc,kBAAkB,8BAA8B,MAAM;AAEhF,SAAO;;AAET,MAAK,MAAM,QAAQ,eAAe;EAChC,MAAM,MAAM,QAAQ,KAAK,KAAK;AAC9B,MAAI,MAAMA,aAAW,IAAI,CAAE,QAAO;;AAEpC,OAAM,IAAI,cACR,kBACA,qCAAqC,cAAc,KAAK,KAAK,CAAC,MAAM,IAAI,GACzE;;AAGH,eAAsB,gBAAgB,MAAoC;AAGxE,QAAO,iBADQ,kBAAkB,MADrB,MAAM,SAAS,MAAM,OAAO,CACG,EACX,QAAQ,KAAK,CAAC;;AAGhD,SAAS,kBAAkB,MAAc,KAAsC;CAC7E,MAAM,SAAS,KAAK,aAAa,CAAC,SAAS,QAAQ;AACnD,KAAI;EACF,MAAM,MAAM,SAAS,KAAK,MAAM,IAAI,GAAGC,MAAU,IAAI;AACrD,MAAI,QAAQ,QAAQ,OAAO,QAAQ,YAAY,MAAM,QAAQ,IAAI,CAC/D,OAAM,IAAI,cAAc,kBAAkB,eAAe,KAAK,mBAAmB;AAEnF,SAAO;UACA,KAAK;AACZ,MAAI,eAAe,cAAe,OAAM;EACxC,MAAM,MAAO,IAAc;AAC3B,QAAM,IAAI,cAAc,kBAAkB,+BAA+B,KAAK,IAAI,MAAM;;;AAI5F,SAAS,cAAc,OAAgC,KAAqB;CAC1E,MAAM,IAAI,MAAM;AAChB,KAAI,MAAM,KAAA,KAAa,MAAM,KAC3B,OAAM,IAAI,cAAc,0BAA0B,GAAG,IAAI,eAAe,IAAI;AAE9E,KAAI,OAAO,MAAM,SACf,OAAM,IAAI,cAAc,kBAAkB,GAAG,IAAI,oBAAoB,IAAI;AAE3E,KAAI,EAAE,MAAM,CAAC,WAAW,EACtB,OAAM,IAAI,cAAc,0BAA0B,GAAG,IAAI,eAAe,IAAI;AAE9E,QAAO;;AAGT,SAAS,eAAe,OAAgC,KAAiC;CACvF,MAAM,IAAI,MAAM;AAChB,KAAI,MAAM,KAAA,KAAa,MAAM,KAAM,QAAO,KAAA;AAC1C,KAAI,OAAO,MAAM,SACf,OAAM,IAAI,cAAc,kBAAkB,GAAG,IAAI,kCAAkC,IAAI;AAEzF,QAAO;;AAGT,SAAS,YAAY,OAAgC,KAAa,WAA2B;CAC3F,MAAM,MAAM,cAAc,OAAO,IAAI;AACrC,QAAO,WAAW,IAAI,GAAG,MAAM,QAAQ,WAAW,IAAI;;AAGxD,SAAS,aACP,OACA,KACA,WACoB;CACpB,MAAM,MAAM,eAAe,OAAO,IAAI;AACtC,KAAI,QAAQ,KAAA,EAAW,QAAO,KAAA;AAC9B,QAAO,WAAW,IAAI,GAAG,MAAM,QAAQ,WAAW,IAAI;;AAGxD,SAAS,mBACP,OACA,KACA,EAAE,OACQ;CACV,MAAM,IAAI,MAAM;AAChB,KAAI,CAAC,MAAM,QAAQ,EAAE,CACnB,OAAM,IAAI,cAAc,kBAAkB,GAAG,IAAI,+BAA+B,IAAI;AAEtF,KAAI,EAAE,SAAS,IACb,OAAM,IAAI,cAAc,kBAAkB,GAAG,IAAI,yBAAyB,IAAI,WAAW,IAAI;AAE/F,QAAO,EAAE,KAAK,MAAM,QAAQ;AAC1B,MAAI,OAAO,SAAS,YAAY,CAAC,OAAO,UAAU,KAAK,CACrD,OAAM,IAAI,cAAc,kBAAkB,GAAG,IAAI,GAAG,IAAI,uBAAuB,IAAI;AAErF,SAAO;GACP;;AAGJ,SAAS,oBACP,OACA,KACA,EAAE,QAA0B,EAAE,EACpB;CACV,MAAM,IAAI,MAAM;AAChB,KAAI,MAAM,KAAA,KAAa,MAAM,KAAM,QAAO,EAAE;AAC5C,KAAI,CAAC,MAAM,QAAQ,EAAE,CACnB,OAAM,IAAI,cAAc,kBAAkB,GAAG,IAAI,+BAA+B,IAAI;AAEtF,KAAI,QAAQ,KAAA,KAAa,EAAE,SAAS,IAClC,OAAM,IAAI,cACR,kBACA,GAAG,IAAI,mBAAmB,IAAI,gBAAgB,EAAE,OAAO,IACvD,IACD;AAEH,QAAO,EAAE,KAAK,MAAM,QAAQ;AAC1B,MAAI,OAAO,SAAS,SAClB,OAAM,IAAI,cAAc,kBAAkB,GAAG,IAAI,GAAG,IAAI,qBAAqB,IAAI;AAEnF,SAAO;GACP;;AAGJ,SAAS,iBACP,OACA,KACA,WACA,EAAE,OACQ;CACV,MAAM,IAAI,MAAM;AAChB,KAAI,CAAC,MAAM,QAAQ,EAAE,CACnB,OAAM,IAAI,cAAc,kBAAkB,GAAG,IAAI,6BAA6B,IAAI;AAEpF,KAAI,EAAE,SAAS,IACb,OAAM,IAAI,cAAc,kBAAkB,GAAG,IAAI,yBAAyB,IAAI,WAAW,IAAI;AAE/F,QAAO,EAAE,KAAK,MAAM,QAAQ;AAC1B,MAAI,OAAO,SAAS,YAAY,KAAK,MAAM,CAAC,WAAW,EACrD,OAAM,IAAI,cAAc,kBAAkB,GAAG,IAAI,GAAG,IAAI,+BAA+B,IAAI;AAE7F,SAAO,WAAW,KAAK,GAAG,OAAO,QAAQ,WAAW,KAAK;GACzD;;AAGJ,SAAS,kBACP,OACA,KACA,WACU;CACV,MAAM,IAAI,MAAM;AAChB,KAAI,MAAM,KAAA,KAAa,MAAM,KAAM,QAAO,EAAE;AAC5C,KAAI,CAAC,MAAM,QAAQ,EAAE,CACnB,OAAM,IAAI,cAAc,kBAAkB,GAAG,IAAI,6BAA6B,IAAI;AAEpF,QAAO,EAAE,KAAK,MAAM,QAAQ;AAC1B,MAAI,OAAO,SAAS,YAAY,KAAK,MAAM,CAAC,WAAW,EACrD,OAAM,IAAI,cAAc,kBAAkB,GAAG,IAAI,GAAG,IAAI,+BAA+B,IAAI;AAE7F,SAAO,WAAW,KAAK,GAAG,OAAO,QAAQ,WAAW,KAAK;GACzD;;AAUJ,MAAa,cACX;AAEF,SAAgB,aAAa,GAAoB;AAC/C,QAAO,YAAY,KAAK,EAAE,aAAa,CAAC;;AAc1C,MAAa,iBAAiB;AAC9B,MAAa,iBAAiB;AAI9B,MAAa,iBAAiB;AAE9B,MAAa,kBAAkB;CAC7B,sBAAsB;CACtB,sBAAsB;CACtB,kBAAkB;CAClB,0BAA0B;CAC1B,aAAa;CACb,wBAAwB;CACzB;AAED,MAAM,mCAAmC,gBAAgB;AACzD,MAAM,mCAAmC,gBAAgB;AAEzD,SAAgB,+BAA+B,GAAmB;AAChE,QAAO,CAAC,GAAG,EAAE,CAAC,QAAQ,OAAO,OAAO,IAAI,CAAC;;AAK3C,MAAM,4BAA4B;AAElC,SAAgB,gBAAgB,MAAuB;CAOrD,MAAM,QAAQ,CAAC,GAAG,KAAK;CACvB,MAAM,iBAAiB,MAAM,WAAW,OAAO,WAAW,KAAK,GAAG,CAAC;AACnE,KAAI,mBAAmB,GAAI,QAAO;AAClC,MAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;EACrC,MAAM,KAAK,MAAM;AACjB,MAAI,OAAO,KAAA,KAAa,CAAC,WAAW,KAAK,GAAG,CAAE;EAC9C,MAAM,cAAc,MAAM;AAC1B,MAAI,eAAe,OAAO,GAAG,aAAa,CAAE,QAAO;AACnD,MAAI,CAAC,eAAe,OAAO,GAAG,aAAa,CAAE,QAAO;;AAEtD,QAAO;;AAQT,MAAM,oCAAoC,gBAAgB;AAE1D,SAAS,eAAe,GAAoB;AAC1C,KAAI;EACF,MAAM,SAAS,IAAI,IAAI,EAAE;AACzB,SAAO,OAAO,aAAa,WAAW,OAAO,aAAa;SACpD;AACN,SAAO;;;AAIX,SAAS,iBAAiB,KAA8B,WAAgC;CACtF,MAAM,UAAU,cAAc,KAAK,UAAU;AAC7C,KAAI,CAAC,eAAe,KAAK,QAAQ,CAC/B,OAAM,IAAI,cACR,kBACA,oFAAoF,QAAQ,wDAC5F,UACD;CAEH,MAAM,aAAa,0BAA0B,QAAQ;AACrD,KAAI,aAAa,iCACf,OAAM,IAAI,cACR,kBACA,mBAAmB,iCAAiC,6CAA6C,WAAW,uDAC5G,UACD;CAEH,MAAM,UAAU,cAAc,KAAK,UAAU;AAC7C,KAAI,CAAC,eAAe,KAAK,QAAQ,CAC/B,OAAM,IAAI,cACR,kBACA,6EAA6E,QAAQ,0DACrF,UACD;CAEH,MAAM,aAAa,0BAA0B,QAAQ;AACrD,KAAI,aAAa,iCACf,OAAM,IAAI,cACR,kBACA,mBAAmB,iCAAiC,6CAA6C,WAAW,yDAC5G,UACD;AAEH,MAAK,MAAM,QAAQ,QAAQ,MAAM,IAAI,EAAE;AACrC,MAAI,KAAK,WAAW,EAAG;AACvB,MAAI,CAAC,gBAAgB,KAAK,CACxB,OAAM,IAAI,cACR,kBACA,iBAAiB,KAAK,qHACtB,UACD;;CAGL,MAAM,UAAU,cAAc,KAAK,UAAU;CAC7C,MAAM,UAAU,cAAc,KAAK,UAAU;AAC7C,KAAI,CAAC,aAAa,QAAQ,CACxB,OAAM,IAAI,cACR,kBACA,6CAA6C,QAAQ,IACrD,UACD;CAEH,MAAM,WAAW,cAAc,KAAK,WAAW;AAE/C,KAAI,SAAS,SAAS,gBAAgB,iBACpC,OAAM,IAAI,cACR,kBACA,oBAAoB,gBAAgB,iBAAiB,4BAA4B,SAAS,OAAO,IACjG,WACD;CAEH,MAAM,cAAc,cAAc,KAAK,cAAc;CACrD,MAAM,wBAAwB,CAAC,GAAG,YAAY,CAAC;AAC/C,KAAI,wBAAwB,kCAC1B,OAAM,IAAI,cACR,kBACA,uBAAuB,kCAAkC,4BAA4B,sBAAsB,IAC3G,cACD;CAEH,MAAM,cAAc,eAAe,KAAK,cAAc;AACtD,KAAI,gBAAgB,KAAA,KAAa,CAAC,eAAe,YAAY,CAC3D,OAAM,IAAI,cACR,kBACA,0CAA0C,YAAY,IACtD,cACD;AAUH,QAAO;EACL;EACA;EACA;EACA;EACA;EACA,MAdW,YAAY,KAAK,QAAQ,UAAU;EAe9C,cAdmB,aAAa,KAAK,gBAAgB,UAAU;EAe/D,qBAd0B,YAAY,KAAK,uBAAuB,UAAU;EAe5E,aAdkB,mBAAmB,KAAK,eAAe,EAAE,KAAK,GAAG,CAAC;EAepE;EACA;EACA,UAhBe,oBAAoB,KAAK,YAAY,EAAE,KAAK,IAAI,CAAC;EAiBhE,qBAhB0B,iBAAiB,KAAK,uBAAuB,WAAW,EAAE,KAAK,GAAG,CAAC;EAiB7F,uBAhB4B,kBAAkB,KAAK,yBAAyB,UAAU;EAiBvF;;;;AClZH,SAAgB,eAAe,SAA8B;CAC3D,MAAM,cAAc,YAAY,QAAQ,aAAa,KAAK;AAC1D,QAAO;;eAEM,QAAQ,YAAY;;;;WAIxB,iBAAiB,QAAQ,QAAQ,CAAC;WAClC,iBAAiB,QAAQ,QAAQ,CAAC;WAClC,QAAQ,QAAQ;WAChB,QAAQ,QAAQ;YACf,iBAAiB,QAAQ,SAAS,CAAC;;EAE7C,YAAY;gBACE,QAAQ,YAAY,KAAK,KAAK,CAAC;;;;;;;;;;;;;;;;AAmB/C,SAAS,iBAAiB,OAAuB;AAE/C,QAAO,IADS,MAAM,QAAQ,OAAO,OAAO,CAAC,QAAQ,MAAM,OAAM,CAC9C;;AAMrB,SAAS,YAAY,OAAe,QAAwB;AAC1D,QAAO,MACJ,MAAM,KAAK,CACX,KAAK,SAAS,GAAG,SAAS,OAAO,CACjC,KAAK,KAAK;;;;ACrBf,eAAsB,WAAW,MAAkC;CACjE,MAAM,MAAM,KAAK,OAAO,QAAQ,KAAK;AAErC,KAAI,KAAK,MAAM;AACb,4BAAwB,KAAK;AAC7B,SAAO,eAAe,SAAS,MAAM;;AAEvC,KAAI,CAAC,QAAQ,OAAO,SAAS,CAAC,QAAQ,MAAM,OAAO;AACjD,4BAAwB,MAAM;AAC9B,SAAO,eAAe,SAAS,MAAM;;CAGvC,MAAM,WAAWC,QAAY,KAAK,aAAa;CAC/C,MAAM,WAAWA,QAAY,KAAK,aAAa;AAC/C,KAAI,CAAC,KAAK,OAAO;EACf,MAAM,WAAY,MAAM,WAAW,SAAS,GACxC,WACC,MAAM,WAAW,SAAS,GACzB,WACA;AACN,MAAI,UAAU;AACZ,WAAQ,OAAO,MACb,oCAAoC,SAAS,gCAC9C;AACD,UAAO,eAAe,SAAS,MAAM;;;CAIzC,MAAM,UAAU,MAAM,aAAa;AACnC,KAAI,CAAC,SAAS;AACZ,uBAAqB,MAAM;AAC3B,SAAO,eAAe,SAAS,iBAAiB;;CAGlD,IAAI;CACJ,IAAI;AACJ,KAAI;AACF,gBAAc,MAAM,cAAc,QAAQ,QAAQ;AAClD,gBAAc,MAAM,eAAe,QAAQ,QAAQ;UAC5C,KAAK;AACZ,MAAIC,oBAAkB,IAAI,EAAE;AAC1B,WAAQ,OAAO,MAAM,aAAa;AAClC,UAAO,eAAe,SAAS,MAAM;;AAEvC,SAAO,qBAAqB,OAAO,IAAI;;CAGzC,IAAI;AACJ,KAAI;EACF,MAAM,UAAU,MAAM,MAAM;GAC1B,SAAS;GACT,UAAU;GACX,CAAC;EACF,MAAM,UAAU,MAAM,MAAM;GAC1B,SAAS;GACT,UAAU;GACX,CAAC;EACF,MAAM,UAAU,MAAM,MAAM;GAC1B,SAAS;GACT,UAAU;GACX,CAAC;EACF,MAAM,UAAU,MAAM,MAAM;GAC1B,SAAS;GACT,UAAU;GACX,CAAC;EACF,MAAM,WAAW,MAAM,MAAM;GAC3B,SAAS,cAAc,gBAAgB,iBAAiB;GACxD,UAAU;GACX,CAAC;EACF,MAAM,cAAc,MAAM,OAAO;GAC/B,SAAS;GACT,UAAU;GACX,CAAC;AACF,YAAU;GACR;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACD;UACM,KAAK;AACZ,MAAIA,oBAAkB,IAAI,EAAE;AAC1B,WAAQ,OAAO,MAAM,aAAa;AAClC,UAAO,eAAe,SAAS,MAAM;;AAEvC,QAAM;;CAGR,MAAM,WAAW,eAAe,QAAQ;CAKxC,MAAM,MAAM,cAAc,SAAS;AACnC,KAAI,IAAI,OAAO,SAAS,GAAG;EACzB,MAAM,SAAS,IAAI,OAAO,IAAI,WAAW;AACzC,UAAQ,OAAO,MAAM,kDAAkD,OAAO,KAAK;AACnF,SAAO,eAAe,SAAS,QAAQ;;AAOzC,KAAI;AAEF,QAAM,MADYD,QAAY,KAAK,SAAS,EACrB,EAAE,WAAW,MAAM,CAAC;AAC3C,QAAM,UAAU,UAAU,UAAU,OAAO;UACpC,KAAK;EACZ,MAAM,SAAS,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;AAC/D,UAAQ,OAAO,MAAM,kCAAkC,OAAO,IAAI;AAClE,SAAO,eAAe,SAAS,QAAQ;;AAGzC,eAAc,SAAS;AACvB,QAAO,eAAe,SAAS,GAAG;;AAGpC,SAASE,0BAAwB,MAAqB;CACpD,MAAM,UAAU;AAChB,KAAI,KACF,UAAS;EAAE,IAAI;EAAO,QAAQ;EAAwB;EAAS,CAAC;KAEhE,SAAQ,OAAO,MAAM,GAAG,QAAQ,IAAI;;AAIxC,SAAS,cAAc,UAAwB;CAC7C,MAAM,MAAM,WAAW,SAAS,GAAG,KAAK,gBAAgB,SAAS,KAAK;AACtE,SAAQ,OAAO,MAAM,WAAW,IAAI,IAAI;AACxC,SAAQ,OAAO,MAAM,gBAAgB;AACrC,SAAQ,OAAO,MACb,6EACD;AACD,SAAQ,OAAO,MAAM,yCAAyC;AAC9D,SAAQ,OAAO,MAAM,0CAA0C;AAC/D,SAAQ,OAAO,MAAM,0CAA0C;AAC/D,SAAQ,OAAO,MAAM,0CAA0C;AAC/D,SAAQ,OAAO,MAAM,0CAA0C;AAC/D,SAAQ,OAAO,MAAM,0DAA0D;AAC/E,SAAQ,OAAO,MAAM,wEAAwE;;AAG/F,SAAS,gBAAgB,SAAyB;CAChD,MAAM,MAAM,QAAQ,KAAK;AACzB,KAAI,QAAQ,WAAW,GAAG,IAAI,GAAG,CAAE,QAAO,QAAQ,MAAM,IAAI,SAAS,EAAE;AACvE,QAAO;;AAGT,eAAe,WAAW,MAAgC;AACxD,KAAI;AACF,QAAM,OAAO,KAAK;AAClB,SAAO;SACD;AACN,SAAO;;;AAUX,SAASD,oBAAkB,KAAuB;AAChD,QAAO,eAAe,SAAS,IAAI,SAAS;;AAG9C,eAAe,cAAc,SAAgD;CAE3E,MAAM,cADO,MAAM,2BAA2B,QAAQ,EAC9B;AACxB,KAAI,WAAW,WAAW,GAAG;AAC3B,UAAQ,OAAO,MACb,kFACD;AACD,QAAM,eAAe,SAAS,MAAM;;AAEtC,KAAI,WAAW,WAAW,GAAG;EAC3B,MAAM,OAAO,WAAW;AACxB,MAAI,CAAC,KAAM,OAAM,IAAI,MAAM,qCAAqC;AAChE,UAAQ,OAAO,MACb,mBAAmB,KAAK,YAAY,IAAI,KAAK,cAAc,2BAC5D;AACD,SAAO,KAAK;;AASd,QAPe,MAAM,OAAe;EAClC,SAAS;EACT,SAAS,WAAW,KAAK,OAAO;GAC9B,MAAM,GAAG,EAAE,YAAY,IAAI,EAAE;GAC7B,OAAO,EAAE;GACV,EAAE;EACJ,CAAC;;AAIJ,eAAe,eAAe,SAAkD;CAC9E,MAAM,OAAO,MAAM,4BAA4B,QAAQ;CASvD,MAAM,UAAoB,EAAE;AAC5B,MAAK,MAAM,SAAS,MAAM;AACxB,MAAI,CAAC,MAAM,cAAc,aAAc;AACvC,UAAQ,KAAK;GACX,MAAM,MAAM,MAAM,cAAc,KAAK;GACrC,OAAO,CAAC,MAAM,cAAc;GAC5B,UAAU;GACX,CAAC;AACF,OAAK,MAAM,OAAO,MAAM,aACtB,KAAI,IAAI,gBAAgB,SAAS,EAC/B,MAAK,MAAM,OAAO,IAAI,iBAAiB;AACrC,OAAI,CAAC,IAAI,aAAc;AACvB,WAAQ,KAAK;IACX,MAAM,KAAK,IAAI,KAAK,KAAK,IAAI,KAAK,KAAK,IAAI,GAAG;IAC9C,OAAO,IAAI;IACZ,CAAC;;WAEK,IAAI,aACb,SAAQ,KAAK;GACX,MAAM,KAAK,IAAI,KAAK,KAAK,IAAI,GAAG;GAChC,OAAO,IAAI;GACZ,CAAC;;AAIR,KAAI,QAAQ,OAAO,MAAM,EAAE,aAAa,KAAA,EAAU,CAChD,OAAM,IAAI,MAAM,mDAAmD;AAQrE,QANe,MAAM,SAAiB;EACpC,SAAS;EACT;EACA,WAAW,YAAa,QAAQ,WAAW,IAAI,+BAA+B;EAC9E,UAAU;EACX,CAAC;;AAWJ,SAAS,gBAAgB,KAA4B;AACnD,KAAI,IAAI,MAAM,CAAC,WAAW,EAAG,QAAO;AACpC,KAAI,CAAC,eAAe,KAAK,IAAI,CAC3B,QAAO;CAET,MAAM,MAAM,+BAA+B,IAAI;AAC/C,KAAI,MAAM,gBAAgB,qBACxB,QAAO,aAAa,gBAAgB,qBAAqB,oCAAoC,IAAI;AAEnG,QAAO;;AAGT,SAAS,gBAAgB,KAA4B;AACnD,KAAI,IAAI,MAAM,CAAC,WAAW,EAAG,QAAO;AACpC,KAAI,CAAC,eAAe,KAAK,IAAI,CAC3B,QAAO;CAET,MAAM,MAAM,+BAA+B,IAAI;AAC/C,KAAI,MAAM,gBAAgB,qBACxB,QAAO,aAAa,gBAAgB,qBAAqB,oCAAoC,IAAI;AAEnG,MAAK,MAAM,QAAQ,IAAI,MAAM,IAAI,EAAE;AACjC,MAAI,KAAK,WAAW,EAAG;AACvB,MAAI,CAAC,gBAAgB,KAAK,CACxB,QAAO,SAAS,KAAK;;AAGzB,QAAO;;AAGT,SAAS,gBAAgB,KAA4B;AACnD,KAAI,CAAC,eAAe,KAAK,IAAI,CAC3B,QAAO;AAET,QAAO;;AAGT,SAAS,cAAc,KAA4B;AACjD,KAAI,CAAC,aAAa,IAAI,CAAE,QAAO;AAC/B,QAAO;;AAGT,SAAS,iBAAiB,KAA4B;AACpD,KAAI,IAAI,MAAM,CAAC,WAAW,EAAG,QAAO;AACpC,KAAI,IAAI,SAAS,gBAAgB,iBAC/B,QAAO,aAAa,gBAAgB,iBAAiB,mBAAmB,IAAI,OAAO;AAErF,QAAO;;AAGT,SAAS,oBAAoB,KAA4B;AACvD,KAAI,IAAI,MAAM,CAAC,WAAW,EAAG,QAAO;CACpC,MAAM,MAAM,CAAC,GAAG,IAAI,CAAC;AACrB,KAAI,MAAM,gBAAgB,yBACxB,QAAO,aAAa,gBAAgB,yBAAyB,mBAAmB,IAAI;AAEtF,QAAO;;;;AChVT,IAAa,sBAAb,cAAyC,MAAM;CAC7C;CACA;CACA;CACA;CAEA,YAAY,MAMT;AACD,QAAM,KAAK,QAAQ;AACnB,OAAK,OAAO;AACZ,OAAK,OAAO,KAAK;AACjB,OAAK,WAAW,KAAK;AACrB,OAAK,SAAS,KAAK;AACnB,OAAK,SAAS,KAAK;;;AASvB,SAAS,OAAO,KAAwB;AACtC,QAAO,GAAG,IAAI,MAAM,GAAG,IAAI;;AAG7B,eAAsB,wBAAwB,MAAc,UAAoC;CAC9F,IAAI;AACJ,KAAI;AACF,WAAS,MAAM,SAAS,KAAK;UACtB,KAAK;AACZ,QAAM,IAAI,oBAAoB;GAC5B;GACA,UAAU,OAAO,SAAS;GAC1B,QAAQ,KAAA;GACR,QAAQ;GACR,SAAS,2BAA2B,KAAK,IAAK,IAAc;GAC7D,CAAC;;CAEJ,IAAI;AACJ,KAAI;AACF,SAAO,UAAU,OAAO;UACjB,KAAK;AACZ,QAAM,IAAI,oBAAoB;GAC5B;GACA,UAAU,OAAO,SAAS;GAC1B,QAAQ,KAAA;GACR,QAAQ;GACR,SAAS,oCAAoC,KAAK,IAAK,IAAc;GACtE,CAAC;;AAEJ,KAAI,OAAO,KAAK,UAAU,YAAY,OAAO,KAAK,WAAW,SAC3D,OAAM,IAAI,oBAAoB;EAC5B;EACA,UAAU,OAAO,SAAS;EAC1B,QAAQ,KAAA;EACR,QAAQ;EACR,SAAS,mBAAmB,KAAK;EAClC,CAAC;AAEJ,KAAI,KAAK,UAAU,SAAS,SAAS,KAAK,WAAW,SAAS,QAAQ;EACpE,MAAM,SAAS,GAAG,KAAK,MAAM,GAAG,KAAK;AACrC,QAAM,IAAI,oBAAoB;GAC5B;GACA,UAAU,OAAO,SAAS;GAC1B;GACA,QAAQ;GACR,SAAS,SAAS,KAAK,kBAAkB,OAAO,aAAa,OAAO,SAAS;GAC9E,CAAC;;;AAMN,MAAa,aAAa;CACxB,MAAM;EAAE,OAAO;EAAK,QAAQ;EAAK;CACjC,qBAAqB;EAAE,OAAO;EAAM,QAAQ;EAAK;CACjD,oBAAoB;EAAE,OAAO;EAAK,QAAQ;EAAM;CAChD,sBAAsB;EAAE,OAAO;EAAM,QAAQ;EAAK;CACnD;;;AClFD,SAAgB,mBACd,UACA,MACsB;CACtB,MAAM,SAA8B;EAClC;GAAE,UAAU,KAAK;GAAqB,WAAW;GAAa,aAAa;GAAc;EACzF,GAAG,KAAK,oBAAoB,KAAwB,OAAO;GACzD,UAAU;GACV,WAAW;GACX,aAAa;GACd,EAAE;EACH,GAAG,KAAK,sBAAsB,KAAwB,OAAO;GAC3D,UAAU;GACV,WAAW;GACX,aAAa;GACd,EAAE;EACJ;AAqBD,QAAO;EAAE,SAnBwC;GAC/C,OAAO,SAAS;GAChB,SAAS,SAAS;GAClB,SAAS,SAAS;GAClB,SAAS,KAAK;GACd,QAAQ;GACR,SAAS,SAAS;GAClB,aAAa,SAAS;GACtB,mBAAmB,SAAS;GAC5B;GACA,GAAI,KAAK,iBAAiB,KAAA,IAAY,EAAE,iBAAiB,KAAK,cAAc,GAAG,EAAE;GACjF,GAAI,SAAS,gBAAgB,KAAA,IAAY,EAAE,aAAa,SAAS,aAAa,GAAG,EAAE;GACpF;EAOiB,YALqC;GACrD,aAAa,SAAS;GACtB,aAAa,SAAS;GACvB;EAE6B;;;;AC0ChC,eAAsB,YAAY,MAAoB,OAAqB,EAAE,EAAiB;CAC5F,MAAM,MAAM,MAAM,wBAAwB;EACxC,MAAM,KAAK;EACX,GAAI,KAAK,cAAc,KAAA,IAAY,EAAE,WAAW,KAAK,WAAW,GAAG,EAAE;EACtE,CAAC;AACF,KAAI,CAAC,IAAK;CACV,MAAM,EAAE,SAAS,gBAAgB;AACjC,oBAAmB,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;CAE5C,MAAM,WAAW,MAAM,wBAAwB,MAAM,KAAK;AAC1D,KAAI,CAAC,SAAU;AAKf,KAAI,CAAC,KAAK,UAAU,CAAC,KAAK,aAAa;AACrC,uBAAqB,KAAK,KAAK;AAC/B,QAAM,eAAe,SAAS,MAAM;AACpC;;AAGF,KAAI;AACF,MAAI,KAAK,QAAQ;GAgBf,MAAM,UAAU,mBAAmB,UAXQ;IACzC,MAAM;IACN,cAAc,SAAS,iBAAiB,KAAA,IAAY,2BAA2B,KAAA;IAC/E,qBAAqB;IACrB,qBAAqB,SAAS,oBAAoB,KAC/C,GAAG,MAAM,gCAAgC,EAAE,IAC7C;IACD,uBAAuB,SAAS,sBAAsB,KACnD,GAAG,MAAM,kCAAkC,EAAE,IAC/C;IACF,CAC4D;AAC7D,cAAW,KAAK,MAAM,aAAa,QAAQ;AAC3C,UAAO,eAAe,SAAS,GAAG;;EAIpC,MAAM,UAAU,mBAAmB,UADtB,MAAM,gBAAgB,aAAa,UAAU,QAAQ,SAAS,KAAK,CAC9B;EAElD,MAAM,SAAS,OADI,KAAK,gBAAgB,KAAK,GAAG,MAAM,cAAc,KAAK,GAAG,EAAE,GAC9C,aAAa,SAAS,QAAQ,QAAQ;AACtE,cAAY,KAAK,MAAM,aAAa,OAAO;AAC3C,QAAM,0BAA0B,KAAK,MAAM,OAAO,WAAW,KAAK,OAAO,QAAQ,KAAK,CAAC;AACvF,SAAO,eAAe,SAAS,GAAG;UAC3B,KAAK;AACZ,SAAO,mBAAmB,KAAK,MAAM,IAAI;;;AAI7C,eAAe,wBACb,MACA,MAC6B;CAC7B,MAAM,MAAM,KAAK,OAAO,QAAQ,KAAK;CACrC,IAAI;AACJ,KAAI;AAEF,aAAW,MAAM,gBADI,MAAM,oBAAoB,KAAK,QAAQ,IAAI,CAClB;UACvC,KAAK;AACZ,MAAI,eAAe,eAAe;AAChC,qBAAkB,KAAK,MAAM,IAAI;AACjC,SAAM,eAAe,SAAS,MAAM;AACpC,UAAO;;AAET,QAAM;;AAKR,KAAI;AACF,QAAM,wBAAwB,SAAS,MAAM,WAAW,KAAK;AAC7D,MAAI,SAAS,iBAAiB,KAAA,EAC5B,OAAM,wBAAwB,SAAS,cAAc,WAAW,KAAK;AAEvE,QAAM,wBAAwB,SAAS,qBAAqB,WAAW,oBAAoB;AAC3F,OAAK,MAAM,KAAK,SAAS,oBACvB,OAAM,wBAAwB,GAAG,WAAW,mBAAmB;AAEjE,OAAK,MAAM,KAAK,SAAS,sBACvB,OAAM,wBAAwB,GAAG,WAAW,qBAAqB;UAE5D,KAAK;AACZ,MAAI,eAAe,qBAAqB;AACtC,2BAAwB,KAAK,MAAM,IAAI;AACvC,SAAM,eAAe,SAAS,MAAM;AACpC,UAAO;;AAET,QAAM;;AAGR,QAAO;;AAGT,eAAe,gBACb,aACA,UACA,SACA,MAC4B;CAQ5B,MAAM,aAAa,KAAK,gBAAgB,MAAM,sBAAsB,EAAE;CAEtE,MAAM,OAAO,MAAM,UAAU,YAAY;EACvC;EACA,YAAY,WAAW,KAAK;EAC5B,aAAa,WAAW,KAAK;EAC7B;EACA,MAAM,SAAS;EAChB,CAAC;CACF,MAAM,eACJ,SAAS,iBAAiB,KAAA,IACtB,MAAM,UAAU,YAAY;EAC1B;EACA,YAAY,WAAW,KAAK;EAC5B,aAAa,WAAW,KAAK;EAC7B;EACA,MAAM,SAAS;EAChB,CAAC,GACF,KAAA;CACN,MAAM,sBAAsB,MAAM,UAAU,YAAY;EACtD;EACA,YAAY,WAAW,oBAAoB;EAC3C,aAAa,WAAW,oBAAoB;EAC5C;EACA,MAAM,SAAS;EAChB,CAAC;CACF,MAAM,sBAAgC,EAAE;AACxC,MAAK,MAAM,KAAK,SAAS,oBACvB,qBAAoB,KAClB,MAAM,UAAU,YAAY;EAC1B;EACA,YAAY,WAAW,mBAAmB;EAC1C,aAAa,WAAW,mBAAmB;EAC3C;EACA,MAAM;EACP,CAAC,CACH;CAEH,MAAM,wBAAkC,EAAE;AAC1C,MAAK,MAAM,KAAK,SAAS,sBACvB,uBAAsB,KACpB,MAAM,UAAU,YAAY;EAC1B;EACA,YAAY,WAAW,qBAAqB;EAC5C,aAAa,WAAW,qBAAqB;EAC7C;EACA,MAAM;EACP,CAAC,CACH;AAEH,QAAO;EAAE;EAAM;EAAc;EAAqB;EAAqB;EAAuB;;AAGhG,eAAe,UACb,YACA,OAOiB;CACjB,MAAM,SAAS,MAAM,SAAS,MAAM,KAAK;AACzC,QAAO,WAAW;EAChB,aAAa,MAAM;EACnB,YAAY,MAAM;EAClB,aAAa,MAAM;EACnB,SAAS,MAAM;EACf,MAAM;GACJ;GACA,UAAU,SAAS,MAAM,KAAK;GAC9B,aAAa;GACd;EACF,CAAC;;AASJ,SAAS,gBAAgB,KAAmC;AAE1D,MADe,IAAI,SAAS,QACb,iBAAiB,cAAc,KAAK,IAAI,QAAQ,CAC7D,QAAO;AAET,QAAO;;AAGT,SAAS,kBAAkB,MAAe,KAA0B;AAClE,KAAI,KACF,KAAI,IAAI,SAAS,yBACf,UAAS;EACP,IAAI;EACJ,QAAQ;EACR,OAAO,IAAI,SAAS;EACpB,SAAS,IAAI;EACd,CAAC;KAEF,UAAS;EAAE,IAAI;EAAO,QAAQ;EAAkB,SAAS,IAAI;EAAS,CAAC;MAEpE;AACL,UAAQ,OAAO,MAAM,GAAG,IAAI,QAAQ,IAAI;EACxC,MAAM,OAAO,gBAAgB,IAAI;AACjC,MAAI,KAAM,SAAQ,OAAO,MAAM,GAAG,KAAK,IAAI;;;AAI/C,SAAS,wBAAwB,MAAe,KAAgC;AAC9E,KAAI,KACF,KAAI,IAAI,WAAW,WACjB,UAAS;EACP,IAAI;EACJ,QAAQ;EACR,MAAM,IAAI;EACV,UAAU,IAAI;EACd,QAAQ,IAAI,UAAU;EACtB,SAAS,IAAI;EACd,CAAC;KAKF,UAAS;EACP,IAAI;EACJ,QAAQ;EACR,MAAM,IAAI;EACV,SAAS,IAAI;EACd,CAAC;KAGJ,SAAQ,OAAO,MAAM,GAAG,IAAI,QAAQ,IAAI;;AAI5C,SAAS,qBAAqB,MAAqB;CACjD,MAAM,UACJ;AAGF,KAAI,KACF,UAAS;EAAE,IAAI;EAAO,QAAQ;EAAsB;EAAS,CAAC;KAE9D,SAAQ,OAAO,MAAM,GAAG,QAAQ,IAAI;;AAIxC,SAAS,WACP,MACA,aACA,SACM;AACN,KAAI,KACF,UAAS;EAAE,IAAI;EAAM,QAAQ;EAAM;EAAa;EAAS,CAAC;MACrD;AACL,UAAQ,OAAO,MAAM,2BAA2B;AAChD,UAAQ,OAAO,MACb,mFAAmF,YAAY,oBAChG;AACD,UAAQ,OAAO,MAAM,GAAG,KAAK,UAAU,SAAS,MAAM,EAAE,CAAC,IAAI;;;AAIjE,SAAS,cAAc,aAAqB,OAAgC;AAC1E,QAAO,kDAAkD,YAAY,YAAY;;AAGnF,SAAS,YAAY,MAAe,aAAqB,QAAmC;CAC1F,MAAM,aACJ,OAAO,cAAc,KAAA,IAAY,cAAc,aAAa,OAAO,UAAU,GAAG;AAClF,KAAI,KACF,UAAS;EACP,IAAI;EACJ;EACA,OAAO,OAAO,aAAa;EAC3B,aAAa,OAAO,eAAe;EACnC;EACD,CAAC;MACG;AACL,UAAQ,OAAO,MACb,uBAAuB,OAAO,aAAa,eAAe,gBAAgB,YAAA,gBACvD,OAAO,eAAe,UAAU,MACpD;AACD,MAAI,eAAe,KACjB,SAAQ,OAAO,MAAM,eAAe,WAAW,IAAI;;;AASzD,eAAe,mBAAmB,MAAe,KAA6B;AAC5E,QAAO,qBAAqB,MAAM,IAAI;;AAexC,eAAe,0BACb,MACA,WACA,KACe;AACf,KAAI,OAAO,cAAc,YAAY,CAAC,OAAO,UAAU,UAAU,IAAI,aAAa,EAChF;CAEF,IAAI;AACJ,KAAI;AACF,QAAM,MAAM,mBAAmB,IAAI;UAC5B,KAAK;AACZ,MAAI,CAAC,MAAM;GACT,MAAM,SAAS,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;AAC/D,WAAQ,OAAO,MACb,iEAAiE,OAAO,IACzE;;AAEH;;AAEF,KAAI,QAAQ,MAAM;AAChB,MAAI,CAAC,KACH,SAAQ,OAAO,MACb,6CAA6C,UAAU,4DACxD;AAEH;;AAEF,KAAI;EACF,MAAM,UAAU,MAAM,sBAAsB,IAAI,QAAQ,UAAU;AAClE,MAAI,CAAC,QAAQ,QAAQ,WAAW,UAC9B,SAAQ,OAAO,MAAM,WAAW,IAAI,OAAO,mBAAmB,UAAU,KAAK;UAExE,KAAK;AACZ,MAAI,CAAC,MAAM;GACT,MAAM,SAAS,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;AAC/D,WAAQ,OAAO,MAAM,2CAA2C,IAAI,OAAO,IAAI,OAAO,IAAI;;;;;;ACjYhG,SAAgB,gBACd,eACA,OAC0C;CAC1C,MAAM,SAAS,OAAO,MAAM;AAC5B,MAAK,MAAM,SAAS,eAAe;EACjC,MAAM,YAAY,MAAM,MAAM,MAAM,aAAa,MAAM;AACvD,MAAI,cAAc,KAAA,KAAa,OAAO,UAAU,KAAK,OAAQ,QAAO;;AAEtE,QAAO;;AAGT,SAAgB,eACd,OACoB;AACpB,KAAI,CAAC,MAAO,QAAO,KAAA;CACnB,MAAM,MAAM,MAAM,eAAe,MAAM;AACvC,QAAO,OAAO,QAAQ,WAAW,MAAM,KAAA;;AAGzC,MAAME,cAAY,cAAc;CAC9B,MAAM;EACJ,MAAM;EACN,aAAa;EACd;CACD,MAAM;EACJ,WAAW;GACT,MAAM;GACN,aAAa;GACd;EACD,MAAM;GAAE,MAAM;GAAW,aAAa;GAAyC,SAAS;GAAO;EAChG;CACD,MAAM,IAAI,EAAE,QAAQ;EAClB,MAAM,MAAM,MAAM,wBAAwB,KAAK;AAC/C,MAAI,CAAC,IAAK;EACV,MAAM,EAAE,SAAS,gBAAgB;AACjC,qBAAmB,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;AAE5C,MAAI;GAMF,MAAM,CAAC,MAAM,UAAU,MAAM,QAAQ,IAAI,CACvC,cAAc,aAAa,QAAQ,QAAQ,EAC3C,kBAAkB,aAAa,QAAQ,QAAQ,CAChD,CAAC;GAQF,MAAM,SAAS,MAAM,QAAQ,IAC3B,KAAK,IAAI,OAAO,QAAQ;IACtB,MAAM,YAAY,OAAO,IAAI,OAAO,WAAW,IAAI,KAAK,OAAO,IAAI,GAAG;AACtE,QAAI,CAAC,OAAO,SAAS,UAAU,CAAE,QAAO;AACxC,QAAI;AAEF,YAAO,kBADK,MAAM,sBAAsB,aAAa,WAAW,QAAQ,QAAQ,CACnD;YACvB;AACN,YAAO;;KAET,CACH;GAED,MAAM,OAAO,KAAK,KAAK,KAAK,MAAM;IAChC,MAAM,QAAQ,gBAAgB,OAAO,UAAU,IAAI,GAAG;IACtD,MAAM,UAAU,OAAO,MAAM;AAG7B,WAAO;KAAE;KAAK;KAAO;KAAS,IAFnB,eAAe,SAAS,iBAAiB,MAAM,CAAC;KAEzB,aADd,eAAe,MAAM;KACM;KAC/C;AAEF,OAAI,KAAK,MAAM;IACb,MAAM,SAAS,KAAK,KAAK,EAAE,KAAK,IAAI,mBAAmB;KACrD,IAAI,IAAI;KACR,MAAM,IAAI,QAAQ;KAClB,GAAI,gBAAgB,KAAA,IAAY,EAAE,aAAa,GAAG,EAAE;KACpD,QAAQ,GAAG;KACX,QAAQ,GAAG;KACX,YAAY,GAAG;KACf,OAAO,IAAI;KACZ,EAAE;AACH,aAAS;KACP,IAAI;KACJ;KACA,oBAAoB,OAAO;KAC3B,MAAM;KACP,CAAC;AACF,WAAO,eAAe,SAAS,GAAG;;AAGpC,OAAI,KAAK,WAAW,GAAG;AACrB,YAAQ,OAAO,MAAM,wBAAwB,YAAY,KAAK;AAC9D,QAAI,OAAO,mBACT,SAAQ,OAAO,MAAM,uDAAuD;AAE9E,WAAO,eAAe,SAAS,GAAG;;GAKpC,MAAM,WAAW,QAAQ,OAAO,SAAS,CAAC,QAAQ,IAAI;GACtD,MAAM,SAAS,WAAW,aAAa;GACvC,MAAM,QAAQ,WAAW,YAAY;AACrC,QAAK,MAAM,EAAE,KAAK,QAAQ,MAAM;IAI9B,MAAM,OAAO,IAAI,QAAQ;IACzB,MAAM,WAAW,GAAG,SAAS,IAAI,OAAO,IAAI,UAAU;AACtD,YAAQ,OAAO,MAAM,GAAG,IAAI,GAAG,IAAI,KAAK,IAAI,GAAG,SAAS,SAAS,IAAI;;AAEvE,OAAI,OAAO,mBACT,SAAQ,OAAO,MAAM,uDAAuD;AAE9E,UAAO,eAAe,SAAS,GAAG;WAC3B,KAAK;AACZ,UAAO,qBAAqB,KAAK,MAAM,IAAI;;;CAGhD,CAAC;AAsCF,SAAgB,gBACd,UACA,MACgC;CAChC,MAAM,WAAW,SAAyE;AACxF,MAAI,SAAS,KAAM,QAAO;EAC1B,MAAM,KAAK,KAAK;AAChB,MAAI,OAAO,QAAQ,OAAO,OAAO,YAAY,CAAC,MAAM,QAAQ,GAAG,CAC7D,QAAO;AAET,SAAO;;CAET,MAAM,QAAQ,QAAQ,SAAS,MAAM;CACrC,MAAM,UAAU,QAAQ,SAAS,QAAQ;AACzC,KAAI,SAAS,QAAS,QAAO;AAC7B,KAAI,SAAS,UAAW,QAAO;AAC/B,KAAI,YAAY,QAAQ,UAAU,KAAM,QAAO;EAAE,GAAG;EAAS,GAAG;EAAO;AACvE,QAAO,SAAS;;AAelB,MAAM,cAA4D;CAChE,CAAC,UAAU,MAAM,EAAE,MAAM;CACzB,CAAC,YAAY,MAAM,EAAE,QAAQ;CAC7B,CAAC,YAAY,MAAM,EAAE,QAAQ;CAC7B,CAAC,gBAAgB,MAAM,EAAE,YAAY;CACrC,CAAC,sBAAsB,MAAM,EAAE,kBAAkB;CACjD,CAAC,gBAAgB,MAAM,EAAE,YAAY;CACrC,CAAC,YAAY,MAAM,EAAE,QAAQ;CAC7B,CAAC,YAAY,MAAM,EAAE,QAAQ;CAC7B,CAAC,oBAAoB,MAAM,EAAE,gBAAgB;CAC7C,CACE,2BACC,MAAM;EACL,MAAM,MAAM,EAAE;AACd,MAAI,QAAQ,QAAQ,OAAO,QAAQ,YAAY,MAAM,QAAQ,IAAI,CAAE,QAAO,KAAA;EAC1E,MAAM,KAAM,IAAgC;AAC5C,SAAO,MAAM,QAAQ,GAAG,GAAG,KAAK,KAAA;GAEnC;CACD,CACE,4BACC,MAAM;EAGL,MAAM,MAAM,EAAE;AACd,MAAI,QAAQ,QAAQ,OAAO,QAAQ,YAAY,MAAM,QAAQ,IAAI,CAAE,QAAO,KAAA;EAC1E,MAAM,QAAS,IAAgC;AAC/C,MAAI,CAAC,MAAM,QAAQ,MAAM,IAAI,MAAM,WAAW,EAAG,QAAO,KAAA;EACxD,MAAM,QAAQ,MAAM;AACpB,MAAI,UAAU,QAAQ,OAAO,UAAU,SAAU,QAAO,KAAA;EACxD,MAAM,KAAK;EACX,MAAM,QAAkB,EAAE;AAC1B,OAAK,MAAM,OAAO;GAAC;GAAS;GAAY;GAAc,EAAE;GACtD,MAAM,OAAO,GAAG;AAChB,OAAI,SAAS,QAAQ,OAAO,SAAS,UAAU;IAC7C,MAAM,KAAM,KAAiC;AAC7C,QAAI,OAAO,OAAO,SAAU,OAAM,KAAK,GAAG;;;AAG9C,SAAO,MAAM,KAAK,MAAM;GAE3B;CACF;AAmBD,SAAS,WAAW,GAAY,GAAqB;AACnD,KAAI,MAAM,EAAG,QAAO;AACpB,KAAI,MAAM,KAAA,KAAa,MAAM,KAAA,EAAW,QAAO;AAC/C,KAAI,MAAM,QAAQ,MAAM,KAAM,QAAO;AACrC,KAAI,MAAM,QAAQ,EAAE,IAAI,MAAM,QAAQ,EAAE,EAAE;AACxC,MAAI,EAAE,WAAW,EAAE,OAAQ,QAAO;AAClC,OAAK,IAAI,IAAI,GAAG,IAAI,EAAE,QAAQ,IAC5B,KAAI,CAAC,WAAW,EAAE,IAAI,EAAE,GAAG,CAAE,QAAO;AAEtC,SAAO;;AAET,KAAI,OAAO,MAAM,YAAY,OAAO,MAAM,UAAU;EAClD,MAAM,KAAK;EACX,MAAM,KAAK;EACX,MAAM,KAAK,OAAO,KAAK,GAAG;EAC1B,MAAM,KAAK,OAAO,KAAK,GAAG;AAC1B,MAAI,GAAG,WAAW,GAAG,OAAQ,QAAO;AACpC,OAAK,MAAM,KAAK,GACd,KAAI,CAAC,WAAW,GAAG,IAAI,GAAG,GAAG,CAAE,QAAO;AAExC,SAAO;;AAET,QAAO;;AAGT,SAAgB,oBACd,OACA,SACa;CACb,MAAM,WAAW,UAAU;CAC3B,MAAM,aAAa,YAAY;AAC/B,KAAI,CAAC,YAAY,CAAC,WAChB,QAAO;EAAE;EAAU;EAAY,SAAS,EAAE;EAAE,gBAAgB;EAAG;CAEjE,MAAM,UAAuB,EAAE;CAC/B,IAAI,iBAAiB;AACrB,MAAK,MAAM,CAAC,OAAO,WAAW,aAAa;EAEzC,MAAM,IAAI,OAAO,MAAM;EACvB,MAAM,IAAI,OAAO,QAAQ;AACzB,MAAI,MAAM,KAAA,KAAa,MAAM,KAAA,EAAW;AACxC,MAAI,WAAW,GAAG,EAAE,CAClB;MAEA,SAAQ,KAAK;GAAE;GAAO,OAAO;GAAG,SAAS;GAAG,CAAC;;AAGjD,QAAO;EAAE;EAAU;EAAY;EAAS;EAAgB;;AAM1D,SAAS,gBAAgB,GAAoB;AAC3C,KAAI,MAAM,KAAM,QAAO;AACvB,KAAI,MAAM,KAAA,EAAW,QAAO;AAC5B,KAAI,OAAO,MAAM,UAAU;EAIzB,MAAM,KAAK,CAAC,GAAG,EAAE,CAAC;AAClB,MAAI,KAAK,GAAI,QAAO,GAAG,GAAG;AAC1B,SAAO,KAAK,UAAU,EAAE;;AAE1B,KAAI,MAAM,QAAQ,EAAE,CAClB,QAAO,IAAI,EAAE,OAAO;AAEtB,KAAI,OAAO,MAAM,SACf,QAAO;AAET,QAAO,OAAO,EAAE;;AAKlB,SAAS,sBAAsB,GAAmB;AAChD,KAAI,MAAM,UAAW,QAAO;AAC5B,KAAI,MAAM,UAAW,QAAO;AAC5B,KAAI,MAAM,UAAW,QAAO;AAC5B,QAAO;;AAGT,MAAMC,gBAAc,cAAc;CAChC,MAAM;EACJ,MAAM;EACN,aACE;EACH;CACD,MAAM;EACJ,IAAI;GACF,MAAM;GACN,aACE;GACF,UAAU;GACX;EACD,WAAW;GACT,MAAM;GACN,aAAa;GACd;EACD,MAAM;GACJ,MAAM;GACN,aAAa;GACb,SAAS;GACV;EACD,MAAM;GACJ,MAAM;GACN,aACE;GAEF,SAAS;GACV;EACD,MAAM;GAAE,MAAM;GAAW,aAAa;GAAyC,SAAS;GAAO;EAChG;CACD,MAAM,IAAI,EAAE,QAAQ;EAClB,MAAM,OAAO,KAAK;AAClB,MAAI,SAAS,WAAW,SAAS,aAAa,SAAS,UAAU;AAC/D,OAAI,KAAK,KACP,UAAS;IACP,IAAI;IACJ,QAAQ;IACR,OAAO;IACP,SAAS,mDAAmD,KAAK,UAAU,KAAK,CAAC;IAClF,CAAC;OAEF,SAAQ,OAAO,MAAM,4BAA4B,KAAK,UAAU,KAAK,CAAC,IAAI;AAE5E,UAAO,eAAe,SAAS,MAAM;;AASvC,MAAI,KAAK,QAAQ,KAAK,SAAS,WAAW,CAAC,KAAK,KAC9C,SAAQ,OAAO,MACb,qCAAqC,KAAK,UAAU,KAAK,KAAK,CAAC,cAChE;EAGH,MAAM,MAAM,MAAM,iBAAiB;GACjC,MAAM,KAAK;GACX,UAAU,KAAK;GACf,GAAI,KAAK,cAAc,KAAA,IAAY,EAAE,WAAW,KAAK,WAAW,GAAG,EAAE;GACtE,CAAC;AACF,MAAI,CAAC,IAAK;EACV,MAAM,QAAQ,MAAM,iBAAiB,KAAK,KAAK,KAAK;AACpD,MAAI,UAAU,KAAM;AACpB,qBAAmB,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;EAC5C,MAAM,EAAE,SAAS,gBAAgB;AAEjC,MAAI;GAKF,MAAM,CAAC,UAAU,WAAW,MAAM,QAAQ,IAAI,CAC5C,sBAAsB,aAAa,OAAO,QAAQ,QAAQ,EAC1D,sBAAsB,aAAa,OAAO,QAAQ,QAAQ,CAAC,YAAY,KAAK,CAC7E,CAAC;GACF,MAAM,UAAU,kBAAkB,SAAS;AAE3C,OAAI,KAAK,MAAM;IAGb,MAAM,OAAO,oBAFK,gBAAgB,UAAU,QAAQ,EAChC,gBAAgB,UAAU,UAAU,CACA;AAExD,QAAI,KAAK,MAAM;AACb,cAAS;MACP,IAAI;MACJ;MACA;MACA,UAAU;MACV,aAAa,QAAQ;MACrB,QAAQ,QAAQ;MAChB,YAAY,QAAQ;MACpB,eAAe,SAAS,iBAAiB;MACzC,yBAAyB,SAAS,2BAA2B;MAC7D,qBAAqB,SAAS,uBAAuB;MACrD;MACD,CAAC;AACF,YAAO,eAAe,SAAS,GAAG;;AAGpC,YAAQ,OAAO,MAAM,SAAS,MAAM,+BAA+B;AACnE,YAAQ,OAAO,MACb,kBAAkB,QAAQ,WACvB,QAAQ,SAAS,yCAAyC,MAC3D,KACH;AACD,QAAI,YAAY,KACd,SAAQ,OAAO,MAAM,kBAAkB,sBAAsB,QAAQ,cAAc,CAAC,IAAI;AAE1F,YAAQ,OAAO,MAAM,KAAK;AAM1B,QAAI,CAAC,KAAK,YAAY,CAAC,KAAK,YAAY;AACtC,aAAQ,OAAO,MAAM,oDAAoD;AACzE,YAAO,eAAe,SAAS,GAAG;;AAEpC,QAAI,CAAC,KAAK,YAAY;AACpB,aAAQ,OAAO,MAAM,kDAAkD;AACvE,YAAO,eAAe,SAAS,GAAG;;AAEpC,QAAI,CAAC,KAAK,UAAU;AAClB,aAAQ,OAAO,MAAM,iDAAiD;AACtE,YAAO,eAAe,SAAS,GAAG;;AAEpC,QAAI,KAAK,QAAQ,WAAW,GAAG;AAC7B,aAAQ,OAAO,MAAM,0CAA0C;AAC/D,YAAO,eAAe,SAAS,GAAG;;IAMpC,MAAM,aAAa,KAAK,IAAI,IAAI,KAAK,IAAI,GAAG,KAAK,QAAQ,KAAK,MAAM,EAAE,MAAM,OAAO,CAAC,GAAG,EAAE;AACzF,YAAQ,OAAO,MAAM,aAAa;AAClC,SAAK,MAAM,SAAS,KAAK,SAAS;KAChC,MAAM,OAAO,MAAM,MAAM,OAAO,WAAW;AAC3C,aAAQ,OAAO,MACb,KAAK,KAAK,GAAG,gBAAgB,MAAM,MAAM,CAAC,KAAK,gBAAgB,MAAM,QAAQ,CAAC,IAC/E;;AAEH,QAAI,KAAK,iBAAiB,EACxB,SAAQ,OAAO,MAAM,gBAAgB,KAAK,eAAe,WAAW;AAEtE,WAAO,eAAe,SAAS,GAAG;;GAGpC,MAAM,UAAU,gBAAgB,UAAU,KAAK;AAO/C,OAAI,YAAY,QAAQ,SAAS,aAAa,SAAS,UAAU,KAC/D,SAAQ,OAAO,MACb,OAAO,MAAM,wGACd;AAGH,OAAI,KAAK,MAAM;AACb,aAAS;KACP,IAAI;KACJ;KACA;KACA;KACA;KACA,aAAa,QAAQ;KACrB,QAAQ,QAAQ;KAChB,YAAY,QAAQ;KACpB,eAAe,SAAS,iBAAiB;KACzC,yBAAyB,SAAS,2BAA2B;KAC7D,qBAAqB,SAAS,uBAAuB;KACtD,CAAC;AACF,WAAO,eAAe,SAAS,GAAG;;AAGpC,OAAI,YAAY,MAAM;AAGpB,QAAI,SAAS,aAAa,SAAS,UAAU,KAC3C,SAAQ,OAAO,MAAM,OAAO,MAAM,iCAAiC;QAEnE,SAAQ,OAAO,MAAM,OAAO,MAAM,wBAAwB,KAAK,KAAK;AAEtE,WAAO,eAAe,SAAS,GAAG;;GAGpC,MAAM,QAAQ,MAAsB;IAClC,MAAM,IAAI,QAAQ;AAClB,WAAO,MAAM,QAAQ,MAAM,KAAA,IAAY,MAAM,OAAO,EAAE;;GAExD,MAAM,SAAS,MAAM,QAAQ,QAAQ,OAAO,GAAG,QAAQ,SAAS,EAAE;GAClE,MAAM,aACJ,QAAQ,eAAe,QAAQ,OAAO,QAAQ,eAAe,WACxD,QAAQ,aACT,EAAE;GACR,MAAM,WAAW,MAAM,QAAQ,WAAW,YAAY,GAAG,WAAW,cAAc,EAAE;GACpF,MAAM,gBAAgB,MAAM,QAAQ,WAAW,cAAc,GAAG,WAAW,gBAAgB,EAAE;AAE7F,WAAQ,OAAO,MAAM,SAAS,MAAM,SAAS,KAAK,OAAO;AACzD,WAAQ,OAAO,MAAM,kBAAkB,KAAK,QAAQ,CAAC,IAAI;AACzD,WAAQ,OAAO,MAAM,kBAAkB,KAAK,UAAU,CAAC,IAAI;AAC3D,WAAQ,OAAO,MAAM,kBAAkB,KAAK,UAAU,CAAC,IAAI;AAC3D,WAAQ,OAAO,MAAM,kBAAkB,KAAK,SAAS,CAAC,IAAI;AAM1D,WAAQ,OAAO,MACb,kBAAkB,QAAQ,WACvB,QAAQ,SAAS,yCAAyC,MAC3D,KACH;AACD,OAAI,YAAY,KACd,SAAQ,OAAO,MAAM,kBAAkB,sBAAsB,QAAQ,cAAc,CAAC,IAAI;AAE1F,WAAQ,OAAO,MAAM,kBAAkB,KAAK,cAAc,CAAC,IAAI;AAC/D,WAAQ,OAAO,MAAM,kBAAkB,KAAK,UAAU,CAAC,IAAI;AAC3D,WAAQ,OAAO,MAAM,kBAAkB,KAAK,UAAU,CAAC,IAAI;AAC3D,WAAQ,OAAO,MAAM,kBAAkB,KAAK,cAAc,CAAC,IAAI;GAC/D,MAAM,SACJ,OAAO,QAAQ,sBAAsB,WACjC,GAAG,CAAC,GAAG,QAAQ,kBAAkB,CAAC,OAAO,UACzC;AACN,WAAQ,OAAO,MAAM,kBAAkB,OAAO,IAAI;AAClD,WAAQ,OAAO,MAAM,kBAAkB,OAAO,OAAO,IAAI;AACzD,WAAQ,OAAO,MAAM,kBAAkB,SAAS,OAAO,IAAI,SAAS,KAAK,KAAK,CAAC,KAAK;GACpF,MAAM,YAAY,cAAc;AAChC,OAAI,aAAa,OAAO,cAAc,UAAU;IAC9C,MAAM,KAAK;IACX,MAAM,QAAkB,EAAE;AAC1B,SAAK,MAAM,OAAO;KAAC;KAAS;KAAY;KAAc,EAAE;KACtD,MAAM,OAAO,GAAG;AAChB,SAAI,SAAS,QAAQ,OAAO,SAAS,UAAU;MAC7C,MAAM,KAAM,KAAiC;AAC7C,UAAI,OAAO,OAAO,SAAU,OAAM,KAAK,GAAG;;;AAG9C,YAAQ,OAAO,MAAM,kBAAkB,MAAM,KAAK,MAAM,IAAI,IAAI,IAAI;SAEpE,SAAQ,OAAO,MAAM,qBAAqB;AAE5C,UAAO,eAAe,SAAS,GAAG;WAC3B,KAAK;AACZ,UAAO,qBAAqB,KAAK,MAAM,IAAI;;;CAGhD,CAAC;AA0CF,SAAgB,kBAAkB,KAKhB;CAChB,MAAM,aAAa,IAAI,YAAY;CACnC,MAAM,WAAW,IAAI,UAAU;CAC/B,MAAM,eAAe,IAAI;CACzB,MAAM,kBAAkB,IAAI;CAE5B,IAAI;AACJ,KAAI,iBAAiB,KACnB,SAAQ;UACC,oBAAoB,KAC7B,SAAQ;UACC,CAAC,WACV,SAAQ;UACC,SACT,SAAQ;KAER,SAAQ;AAIV,KAAI,iBAAiB,QAAQ,iBAAiB,YAAY,UAAU,eAClE,SAAQ;CAEV,MAAM,SAAS,iBAAiB;AAEhC,QAAO;EAAE;EAAO;EAAc;EAAiB;EAAY;EAAU;EAAQ,YADvC,SAAS,mBAAmB;EACuB;;AAwB3F,SAAgB,eACd,SACA,eACgB;AAChB,KAAI,CAAC,QACH,QAAO;EAAE,QAAQ;EAAW,QAAQ;EAAO,YAAY;EAAM;CAE/D,MAAM,SAAS,QAAQ,iBAAiB;CACxC,MAAM,aAAa,SAAS,mBAAmB;CAC/C,IAAI,SAAsB,QAAQ;AAClC,KAAI,WAAW,cAAc,kBAAkB,UAC7C,UAAS;AAEX,QAAO;EAAE;EAAQ;EAAQ;EAAY;;AAGvC,SAAgB,iBACd,OACoB;AACpB,KAAI,CAAC,MAAO,QAAO,KAAA;CACnB,MAAM,MAAM,MAAM;AAClB,QAAO,OAAO,QAAQ,WAAW,MAAM,KAAA;;AAGzC,MAAM,wBAAwB;AAC9B,MAAM,wBAAwB;AAE9B,MAAMC,kBAAgB,cAAc;CAClC,MAAM;EACJ,MAAM;EACN,aACE;EACH;CACD,MAAM;EACJ,IAAI;GACF,MAAM;GACN,aAAa;GACb,UAAU;GACX;EACD,WAAW;GACT,MAAM;GACN,aAAa;GACd;EACD,OAAO;GACL,MAAM;GACN,aACE;GAEF,SAAS;GACV;EACD,UAAU;GACR,MAAM;GACN,aAAa;GACb,SAAS;GACV;EACD,MAAM;GAAE,MAAM;GAAW,aAAa;GAA+B,SAAS;GAAO;EACtF;CACD,MAAM,IAAI,EAAE,QAAQ;EAClB,MAAM,cAAc,OAAO,KAAK,SAAS;AACzC,MAAI,CAAC,OAAO,SAAS,YAAY,IAAI,eAAe,GAAG;AACrD,OAAI,KAAK,KACP,UAAS;IACP,IAAI;IACJ,QAAQ;IACR,OAAO;IACP,SAAS,6CAA6C,KAAK,UAAU,KAAK,SAAS,CAAC;IACrF,CAAC;OAEF,SAAQ,OAAO,MAAM,kCAAkC,KAAK,UAAU,KAAK,SAAS,CAAC,IAAI;AAE3F,UAAO,eAAe,SAAS,MAAM;;EAEvC,MAAM,cAAc,KAAK,IACvB,uBACA,KAAK,IAAI,uBAAuB,KAAK,MAAM,YAAY,CAAC,CACzD;EAED,MAAM,MAAM,MAAM,iBAAiB;GACjC,MAAM,KAAK;GACX,UAAU,KAAK;GACf,GAAI,KAAK,cAAc,KAAA,IAAY,EAAE,WAAW,KAAK,WAAW,GAAG,EAAE;GACtE,CAAC;AACF,MAAI,CAAC,IAAK;EACV,MAAM,QAAQ,MAAM,iBAAiB,KAAK,KAAK,KAAK;AACpD,MAAI,UAAU,KAAM;AACpB,qBAAmB,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;EAC5C,MAAM,EAAE,SAAS,gBAAgB;EAQjC,MAAM,QACJ,QACA,YAKG;AACH,OAAI,KAAK,KACP,UAAS;IACP,IAAI;IACJ;IACA;IACA,GAAG;IAIH,eAAe,SAAS,iBAAiB;IACzC,yBAAyB,SAAS,2BAA2B;IAC7D,qBAAqB,SAAS,uBAAuB;IACtD,CAAC;QACG;IACL,MAAM,MAAM,UAAU,KAAK,QAAQ,cAAc,KAAK;AACtD,YAAQ,OAAO,MACb,OAAO,MAAM,OAAO,YAAY,KAAK,OAAO,QAAQ,SACjD,OAAO,SAAS,2CAA2C,OAC3D,OAAO,kBAAkB,eAAe,OAAO,oBAAoB,OACnE,SAAS,sBACN,2BAA2B,QAAQ,wBACnC,MACJ,KACH;;;AAIL,MAAI;GACF,MAAM,OAAO,YASR;IAKH,MAAM,CAAC,KAAK,WAAW,MAAM,QAAQ,IAAI,CACvC,sBAAsB,aAAa,OAAO,QAAQ,QAAQ,EAC1D,sBAAsB,aAAa,OAAO,QAAQ,QAAQ,CAAC,YAAY,KAAK,CAC7E,CAAC;AACF,WAAO,CAAC,kBAAkB,IAAI,EAAE,QAAQ;;AAG1C,OAAI,CAAC,KAAK,OAAO;IACf,MAAM,CAAC,QAAQ,WAAW,MAAM,MAAM;AACtC,SAAK,QAAQ,QAAQ;AACrB,WAAO,eAAe,SAAS,GAAG;;GASpC,IAAI,YAAgC;GACpC,IAAI,oBAAmC;AACvC,UAAO,MAAM;IACX,MAAM,CAAC,QAAQ,WAAW,MAAM,MAAM;IACtC,MAAM,MAAM,SAAS,iBAAiB;AACtC,QAAI,KAAK,KACP,MAAK,QAAQ,QAAQ;aACZ,OAAO,UAAU,aAAa,QAAQ,kBAC/C,MAAK,QAAQ,QAAQ;AAEvB,gBAAY,OAAO;AACnB,wBAAoB;AACpB,QAAI,OAAO,UAAU,eACnB,QAAO,eAAe,SAAS,GAAG;AAEpC,UAAM,IAAI,SAAS,YAAY,WAAW,SAAS,cAAc,IAAK,CAAC;;WAElE,KAAK;AACZ,UAAO,qBAAqB,KAAK,MAAM,IAAI;;;CAGhD,CAAC;AAiBF,MAAM,oBAAgD,CAAC,cAAc,QAAQ;AAC7E,MAAM,wBAAwD,CAAC,OAAO,OAAO;AAE7E,SAAS,oBAAoB,KAAa,OAAsD;CAC9F,MAAM,IAAI,OAAO,IAAI;AACrB,KAAI,CAAC,OAAO,SAAS,EAAE,IAAI,CAAC,OAAO,UAAU,EAAE,IAAI,IAAI,EACrD,QAAO,EAAE,OAAO,KAAK,MAAM,uCAAuC,KAAK,UAAU,IAAI,CAAC,IAAI;AAE5F,QAAO,EAAE,OAAO,GAAG;;AAGrB,MAAM,iBAAiB,cAAc;CACnC,MAAM;EACJ,MAAM;EACN,aAAa;EACd;CACD,MAAM;EACJ,IAAI;GACF,MAAM;GACN,aAAa;GACb,UAAU;GACX;EACD,WAAW;GACT,MAAM;GACN,aAAa;GACd;EACD,MAAM;GAAE,MAAM;GAAU,aAAa;GAA4B,SAAS;GAAK;EAC/E,MAAM;GAAE,MAAM;GAAU,aAAa;GAAc,SAAS;GAAM;EAClE,cAAc;GACZ,MAAM;GACN,aAAa;GACb,SAAS;GACV;EACD,kBAAkB;GAChB,MAAM;GACN,aAAa;GACb,SAAS;GACV;EACD,MAAM;GAAE,MAAM;GAAW,aAAa;GAA+B,SAAS;GAAO;EACtF;CACD,MAAM,IAAI,EAAE,QAAQ;EAClB,MAAM,aAAa,oBAAoB,KAAK,MAAM,OAAO;AACzD,MAAI,WAAW,YAAY;AACzB,OAAI,KAAK,KACP,UAAS;IAAE,IAAI;IAAO,QAAQ;IAAkB,OAAO;IAAQ,SAAS,WAAW;IAAO,CAAC;OAE3F,SAAQ,OAAO,MAAM,gBAAgB,WAAW,MAAM,IAAI;AAE5D,UAAO,eAAe,SAAS,MAAM;;EAEvC,MAAM,aAAa,oBAAoB,KAAK,MAAM,OAAO;AACzD,MAAI,WAAW,YAAY;AACzB,OAAI,KAAK,KACP,UAAS;IAAE,IAAI;IAAO,QAAQ;IAAkB,OAAO;IAAQ,SAAS,WAAW;IAAO,CAAC;OAE3F,SAAQ,OAAO,MAAM,gBAAgB,WAAW,MAAM,IAAI;AAE5D,UAAO,eAAe,SAAS,MAAM;;AAEvC,MAAI,WAAW,UAAU,GAAG;AAC1B,OAAI,KAAK,KACP,UAAS;IACP,IAAI;IACJ,QAAQ;IACR,OAAO;IACP,SAAS;IACV,CAAC;OAEF,SAAQ,OAAO,MAAM,2CAA2C;AAElE,UAAO,eAAe,SAAS,MAAM;;EAEvC,MAAM,YAAY,KAAK;AACvB,MAAI,CAAC,kBAAkB,SAAS,UAA6B,EAAE;AAC7D,OAAI,KAAK,KACP,UAAS;IACP,IAAI;IACJ,QAAQ;IACR,OAAO;IACP,SAAS,+BAA+B,kBAAkB,KAAK,IAAI,CAAC,QAAQ,KAAK,UAAU,UAAU,CAAC;IACvG,CAAC;OAEF,SAAQ,OAAO,MAAM,qCAAqC,KAAK,UAAU,UAAU,CAAC,IAAI;AAE1F,UAAO,eAAe,SAAS,MAAM;;EAEvC,MAAM,gBAAgB,KAAK;AAC3B,MAAI,CAAC,sBAAsB,SAAS,cAAqC,EAAE;AACzE,OAAI,KAAK,KACP,UAAS;IACP,IAAI;IACJ,QAAQ;IACR,OAAO;IACP,SAAS,mCAAmC,sBAAsB,KAAK,IAAI,CAAC,QAAQ,KAAK,UAAU,cAAc,CAAC;IACnH,CAAC;OAEF,SAAQ,OAAO,MACb,yCAAyC,KAAK,UAAU,cAAc,CAAC,IACxE;AAEH,UAAO,eAAe,SAAS,MAAM;;EAGvC,MAAM,MAAM,MAAM,iBAAiB;GACjC,MAAM,KAAK;GACX,UAAU,KAAK;GACf,GAAI,KAAK,cAAc,KAAA,IAAY,EAAE,WAAW,KAAK,WAAW,GAAG,EAAE;GACtE,CAAC;AACF,MAAI,CAAC,IAAK;EACV,MAAM,QAAQ,MAAM,iBAAiB,KAAK,KAAK,KAAK;AACpD,MAAI,UAAU,KAAM;AACpB,qBAAmB,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;EAC5C,MAAM,EAAE,SAAS,gBAAgB;AAEjC,MAAI;GACF,MAAM,SAAS,MAAM,oBACnB;IACE;IACA,WAAW;IACX,MAAM,WAAW;IACjB,MAAM,WAAW;IACN;IACI;IAChB,EACD,QAAQ,QACT;AAED,OAAI,KAAK,MAAM;AACb,aAAS;KACP,IAAI;KACJ;KACA;KACA,MAAM,WAAW;KACjB,MAAM,WAAW;KACjB;KACA;KACA,eAAe,OAAO;KACtB,kBAAkB,OAAO;KACzB,QAAQ,OAAO;KACf,SAAS,OAAO;KACjB,CAAC;AACF,WAAO,eAAe,SAAS,GAAG;;AAGpC,WAAQ,OAAO,MACb,OAAO,MAAM,OAAO,YAAY,KAAK,OAAO,iBAAiB,kBAAkB,OAAO,cAAc,QAAQ,EAAE,CAAC,IAChH;AACD,OAAI,OAAO,QAAQ,WAAW,GAAG;AAC/B,YAAQ,OAAO,MAAM,6BAA6B;AAClD,WAAO,eAAe,SAAS,GAAG;;AAEpC,QAAK,MAAM,KAAK,OAAO,SAAS;IAC9B,MAAM,QAAQ,OAAO,EAAE,UAAU,WAAW,EAAE,QAAS,EAAE,UAAU;IACnE,MAAM,SACJ,OAAO,EAAE,aAAa,WAClB,EAAE,WACF,OAAO,EAAE,aAAa,WACpB,EAAE,WACF;IACR,MAAM,OACJ,OAAO,EAAE,YAAY,WACjB,EAAE,UACF,OAAO,EAAE,kBAAkB,WACzB,EAAE,gBACF;IACR,MAAM,YACJ,OAAO,EAAE,cAAc,WACnB,EAAE,YACF,OAAO,EAAE,eAAe,WACtB,EAAE,aACF;AACR,YAAQ,OAAO,MAAM,GAAG,MAAM,IAAI,UAAU,IAAI,OAAO,IAAI,KAAK,IAAI;;AAEtE,OAAI,OAAO,OAAO,QAChB,SAAQ,OAAO,MACb,iBAAiB,WAAW,QAAQ,EAAE,YAAY,WAAW,MAAM,KACpE;AAEH,UAAO,eAAe,SAAS,GAAG;WAC3B,KAAK;AACZ,UAAO,qBAAqB,KAAK,MAAM,IAAI;;;CAGhD,CAAC;AAcF,MAAM,iBAAiB,cAAc;CACnC,MAAM;EACJ,MAAM;EACN,aAAa;EACd;CACD,MAAM;EACJ,IAAI;GACF,MAAM;GACN,aAAa;GACb,UAAU;GACX;EACD,WAAW;GACT,MAAM;GACN,aAAa;GACd;EACD,aAAa;GAAE,MAAM;GAAU,aAAa;GAA2B,SAAS;GAAM;EACtF,QAAQ;GACN,MAAM;GACN,aAAa;GACd;EACD,MAAM;GAAE,MAAM;GAAW,aAAa;GAA+B,SAAS;GAAO;EACtF;CACD,MAAM,IAAI,EAAE,QAAQ;EAClB,MAAM,iBAAiB,oBAAoB,KAAK,cAAc,YAAY;AAC1E,MAAI,WAAW,gBAAgB;AAC7B,OAAI,KAAK,KACP,UAAS;IACP,IAAI;IACJ,QAAQ;IACR,OAAO;IACP,SAAS,eAAe;IACzB,CAAC;OAEF,SAAQ,OAAO,MAAM,gBAAgB,eAAe,MAAM,IAAI;AAEhE,UAAO,eAAe,SAAS,MAAM;;AAEvC,MAAI,eAAe,UAAU,GAAG;AAC9B,OAAI,KAAK,KACP,UAAS;IACP,IAAI;IACJ,QAAQ;IACR,OAAO;IACP,SAAS;IACV,CAAC;OAEF,SAAQ,OAAO,MAAM,gDAAgD;AAEvE,UAAO,eAAe,SAAS,MAAM;;EAGvC,MAAM,MAAM,MAAM,iBAAiB;GACjC,MAAM,KAAK;GACX,UAAU,KAAK;GACf,GAAI,KAAK,cAAc,KAAA,IAAY,EAAE,WAAW,KAAK,WAAW,GAAG,EAAE;GACtE,CAAC;AACF,MAAI,CAAC,IAAK;EACV,MAAM,QAAQ,MAAM,iBAAiB,KAAK,KAAK,KAAK;AACpD,MAAI,UAAU,KAAM;AACpB,qBAAmB,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;EAC5C,MAAM,EAAE,SAAS,gBAAgB;AAEjC,MAAI;GACF,MAAM,SAAS,MAAM,iBACnB;IACE;IACA,WAAW;IACX,UAAU,eAAe;IACzB,GAAI,OAAO,KAAK,WAAW,YAAY,KAAK,OAAO,SAAS,IACxD,EAAE,QAAQ,KAAK,QAAQ,GACvB,EAAE;IACP,EACD,QAAQ,QACT;AAED,OAAI,KAAK,MAAM;AACb,aAAS;KACP,IAAI;KACJ;KACA;KACA,UAAU,eAAe;KACzB,QAAQ,KAAK,UAAU;KACvB,YAAY,OAAO;KACnB,SAAS,OAAO;KAChB,SAAS,OAAO;KACjB,CAAC;AACF,WAAO,eAAe,SAAS,GAAG;;AAGpC,WAAQ,OAAO,MACb,OAAO,MAAM,OAAO,YAAY,KAAK,OAAO,QAAQ,OAAO,2BAC5D;AACD,OAAI,OAAO,QAAQ,WAAW,GAAG;AAC/B,YAAQ,OAAO,MAAM,gBAAgB;AACrC,WAAO,eAAe,SAAS,GAAG;;AAEpC,QAAK,MAAM,KAAK,OAAO,SAAS;IAC9B,MAAM,KAAK,OAAO,EAAE,OAAO,YAAY,OAAO,EAAE,OAAO,WAAW,EAAE,KAAK;IACzE,MAAM,SAAS,OAAO,EAAE,WAAW,WAAW,EAAE,SAAU,EAAE,cAAc;IAC1E,MAAM,OACJ,OAAO,EAAE,YAAY,WAAW,EAAE,UAAU,OAAO,EAAE,WAAW,WAAW,EAAE,SAAS;IACxF,MAAM,YACJ,OAAO,EAAE,cAAc,WACnB,EAAE,YACF,OAAO,EAAE,eAAe,WACtB,EAAE,aACF;AACR,YAAQ,OAAO,MAAM,GAAG,GAAG,IAAI,UAAU,IAAI,OAAO,IAAI,KAAK,IAAI;;AAEnE,OAAI,OAAO,WAAW,OAAO,WAC3B,SAAQ,OAAO,MAAM,mBAAmB,OAAO,WAAW,KAAK;AAEjE,UAAO,eAAe,SAAS,GAAG;WAC3B,KAAK;AACZ,UAAO,qBAAqB,KAAK,MAAM,IAAI;;;CAGhD,CAAC;AAitBF,MAAM,iBAAiB,cAAc;CACnC,MAAM;EACJ,MAAM;EACN,aAAa;EACd;CACD,aAAa;EACX,IArsBqB,cAAc;GACrC,MAAM;IACJ,MAAM;IACN,aAAa;IACd;GACD,MAAM;IACJ,IAAI;KACF,MAAM;KACN,aAAa;KACb,UAAU;KACX;IACD,WAAW;KACT,MAAM;KACN,aAAa;KACd;IACD,MAAM;KAAE,MAAM;KAAU,aAAa;KAA4B,SAAS;KAAK;IAC/E,QAAQ;KACN,MAAM;KACN,aAAa;KACd;IACD,iBAAiB;KACf,MAAM;KACN,aAAa;KACd;IACD,MAAM;KAAE,MAAM;KAAW,aAAa;KAA+B,SAAS;KAAO;IACtF;GACD,MAAM,IAAI,EAAE,QAAQ;IAClB,MAAM,aAAa,oBAAoB,KAAK,MAAM,OAAO;AACzD,QAAI,WAAW,YAAY;AACzB,SAAI,KAAK,KACP,UAAS;MAAE,IAAI;MAAO,QAAQ;MAAkB,OAAO;MAAQ,SAAS,WAAW;MAAO,CAAC;SAE3F,SAAQ,OAAO,MAAM,mBAAmB,WAAW,MAAM,IAAI;AAE/D,YAAO,eAAe,SAAS,MAAM;;IAEvC,IAAI;AACJ,QAAI,KAAK,WAAW,KAAA,EAClB,KAAI,KAAK,WAAW,OAAQ,UAAS;aAC5B,KAAK,WAAW,QAAS,UAAS;SACtC;AACH,SAAI,KAAK,KACP,UAAS;MACP,IAAI;MACJ,QAAQ;MACR,OAAO;MACP,SAAS,2CAA2C,KAAK,UAAU,KAAK,OAAO,CAAC;MACjF,CAAC;SAEF,SAAQ,OAAO,MAAM,oCAAoC,KAAK,UAAU,KAAK,OAAO,CAAC,IAAI;AAE3F,YAAO,eAAe,SAAS,MAAM;;IAIzC,MAAM,MAAM,MAAM,iBAAiB;KACjC,MAAM,KAAK;KACX,UAAU,KAAK;KACf,GAAI,KAAK,cAAc,KAAA,IAAY,EAAE,WAAW,KAAK,WAAW,GAAG,EAAE;KACtE,CAAC;AACF,QAAI,CAAC,IAAK;IACV,MAAM,QAAQ,MAAM,iBAAiB,KAAK,KAAK,KAAK;AACpD,QAAI,UAAU,KAAM;AACpB,uBAAmB,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IAC5C,MAAM,EAAE,SAAS,gBAAgB;AAEjC,QAAI;KACF,MAAM,SAAS,MAAM,aACnB;MACE;MACA,WAAW;MACX,MAAM,WAAW;MACjB,GAAI,WAAW,KAAA,IAAY,EAAE,QAAQ,GAAG,EAAE;MAC1C,GAAI,OAAO,KAAK,qBAAqB,YAAY,KAAK,iBAAiB,SAAS,IAC5E,EAAE,cAAc,KAAK,kBAAkB,GACvC,EAAE;MACP,EACD,QAAQ,QACT;AAED,SAAI,KAAK,MAAM;AACb,eAAS;OACP,IAAI;OACJ;OACA;OACA,MAAM,WAAW;OACjB,WAAW,OAAO;OAClB,aAAa,OAAO;OACpB,SAAS,OAAO;OACjB,CAAC;AACF,aAAO,eAAe,SAAS,GAAG;;AAGpC,aAAQ,OAAO,MACb,OAAO,MAAM,OAAO,YAAY,UAAU,OAAO,cAAc,EAAE,GAAG,KAAK,IAAI,OAAO,WAAW,EAAE,CAAC,IAAI,OAAO,SAAS,OAAO,cAC9H;AACD,SAAI,OAAO,SAAS,WAAW,GAAG;AAChC,cAAQ,OAAO,MAAM,6BAA6B;AAClD,aAAO,eAAe,SAAS,GAAG;;AAEpC,UAAK,MAAM,KAAK,OAAO,UAAU;MAC/B,MAAM,KAAK,OAAO,EAAE,OAAO,YAAY,OAAO,EAAE,OAAO,WAAW,EAAE,KAAK;MACzE,MAAM,UAAU,OAAO,EAAE,YAAY,WAAW,EAAE,UAAU;MAC5D,MAAM,SAAS,OAAO,EAAE,iBAAiB,WAAW,EAAE,eAAe;MACrE,MAAM,YAAY,OAAO,EAAE,cAAc,WAAW,EAAE,YAAY;AAClE,cAAQ,OAAO,MAAM,GAAG,GAAG,IAAI,QAAQ,IAAI,OAAO,IAAI,UAAU,IAAI;;AAEtE,SAAI,OAAO,cAAc,IAAI,OAAO,UAClC,SAAQ,OAAO,MAAM,iBAAiB,OAAO,cAAc,EAAE,KAAK;AAEpE,YAAO,eAAe,SAAS,GAAG;aAC3B,KAAK;AACZ,YAAO,qBAAqB,KAAK,MAAM,IAAI;;;GAGhD,CAAC;EAmlBE,UAjlB2B,cAAc;GAC3C,MAAM;IACJ,MAAM;IACN,aAAa;IACd;GACD,MAAM;IACJ,IAAI;KACF,MAAM;KACN,aAAa;KACb,UAAU;KACX;IACD,WAAW;KACT,MAAM;KACN,aAAa;KACd;IACD,MAAM;KAAE,MAAM;KAAW,aAAa;KAA+B,SAAS;KAAO;IACtF;GACD,MAAM,IAAI,EAAE,QAAQ;IAClB,MAAM,MAAM,MAAM,iBAAiB;KACjC,MAAM,KAAK;KACX,UAAU,KAAK;KACf,GAAI,KAAK,cAAc,KAAA,IAAY,EAAE,WAAW,KAAK,WAAW,GAAG,EAAE;KACtE,CAAC;AACF,QAAI,CAAC,IAAK;IACV,MAAM,QAAQ,MAAM,iBAAiB,KAAK,KAAK,KAAK;AACpD,QAAI,UAAU,KAAM;AACpB,uBAAmB,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IAC5C,MAAM,EAAE,SAAS,gBAAgB;AAEjC,QAAI;KACF,MAAM,SAAS,MAAM,oBAAoB,aAAa,OAAO,QAAQ,QAAQ;AAC7E,SAAI,KAAK,MAAM;AACb,eAAS;OAAE,IAAI;OAAM;OAAa;OAAO;OAAQ,CAAC;AAClD,aAAO,eAAe,SAAS,GAAG;;AAEpC,SAAI,WAAW,MAAM;AACnB,cAAQ,OAAO,MAAM,OAAO,MAAM,OAAO,YAAY,yBAAyB;AAC9E,aAAO,eAAe,SAAS,GAAG;;KAEpC,MAAM,KAAK,OAAO,OAAO,OAAO,YAAY,OAAO,OAAO,OAAO,WAAW,OAAO,KAAK;KACxF,MAAM,UAAU,OAAO,OAAO,YAAY,WAAW,OAAO,UAAU;KACtE,MAAM,SAAS,OAAO,OAAO,iBAAiB,WAAW,OAAO,eAAe;KAC/E,MAAM,aAAa,OAAO,OAAO,eAAe,WAAW,OAAO,aAAa;AAC/E,aAAQ,OAAO,MAAM,OAAO,MAAM,qBAAqB;AACvD,aAAQ,OAAO,MAAM,iBAAiB,GAAG,IAAI;AAC7C,aAAQ,OAAO,MAAM,iBAAiB,QAAQ,IAAI;AAClD,aAAQ,OAAO,MAAM,iBAAiB,OAAO,IAAI;AACjD,aAAQ,OAAO,MAAM,iBAAiB,WAAW,IAAI;AACrD,YAAO,eAAe,SAAS,GAAG;aAC3B,KAAK;AACZ,YAAO,qBAAqB,KAAK,MAAM,IAAI;;;GAGhD,CAAC;EA6hBE,QAvgByB,cAAc;GACzC,MAAM;IACJ,MAAM;IACN,aAAa;IACd;GACD,MAAM;IAOJ,IAAI;KACF,MAAM;KACN,aAAa;KACb,UAAU;KACX;IACD,MAAM;KAAE,MAAM;KAAc,aAAa;KAAiC,UAAU;KAAM;IAC1F,iBAAiB;KACf,MAAM;KACN,aAAa;KACd;IACD,MAAM;KAAE,MAAM;KAAU,aAAa;KAAkD;IACvF,WAAW;KACT,MAAM;KACN,aAAa;KACd;IACD,WAAW;KACT,MAAM;KACN,aAAa;KACb,SAAS;KACV;IACD,MAAM;KAAE,MAAM;KAAW,aAAa;KAA+B,SAAS;KAAO;IACtF;GACD,MAAM,IAAI,EAAE,QAAQ;IAClB,MAAM,eAAe,OAAO,KAAK,qBAAqB,WAAW,KAAK,mBAAmB;AACzF,QAAI,iBAAiB,IAAI;AACvB,SAAI,KAAK,KACP,UAAS;MACP,IAAI;MACJ,QAAQ;MACR,SACE;MACH,CAAC;SAEF,SAAQ,OAAO,MACb,+IAED;AAEH,YAAO,eAAe,SAAS,MAAM;;IAEvC,MAAM,WAAW,OAAO,KAAK,SAAS,WAAW,KAAK,OAAO;IAC7D,IAAI;AACJ,QAAI;KACF,MAAM,EAAE,aAAa,MAAM,OAAO;KAClC,MAAM,MAAM,MAAM,SAAS,SAAS;AACpC,aAAQ,IAAI,WAAW,IAAI,QAAQ,IAAI,YAAY,IAAI,WAAW;aAC3D,KAAK;KACZ,MAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;AAChE,SAAI,KAAK,KACP,UAAS;MAAE,IAAI;MAAO,QAAQ;MAAmB,MAAM;MAAU;MAAS,CAAC;SAE3E,SAAQ,OAAO,MAAM,mCAAmC,SAAS,IAAI,QAAQ,IAAI;AAEnF,YAAO,eAAe,SAAS,MAAM;;IAGvC,MAAM,MAAM,MAAM,iBAAiB;KACjC,MAAM,KAAK;KACX,UAAU,KAAK;KACf,GAAI,KAAK,cAAc,KAAA,IAAY,EAAE,WAAW,KAAK,WAAW,GAAG,EAAE;KACtE,CAAC;AACF,QAAI,CAAC,IAAK;IACV,MAAM,QAAQ,MAAM,iBAAiB,KAAK,KAAK,KAAK;AACpD,QAAI,UAAU,KAAM;AACpB,uBAAmB,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IAC5C,MAAM,EAAE,SAAS,gBAAgB;IACjC,MAAM,OAAO,OAAO,KAAK,SAAS,YAAY,KAAK,KAAK,SAAS,IAAI,KAAK,OAAO,KAAA;AAEjF,QAAI,KAAK,YAAY;AACnB,SAAI,KAAK,KACP,UAAS;MACP,IAAI;MACJ,QAAQ;MACR;MACA;MACA;MACA,OAAO,MAAM;MACb,MAAM,QAAQ;MACf,CAAC;SAEF,SAAQ,OAAO,MACb,2BACoB,YAAY,mBACZ,MAAM,mBACN,aAAa,mBACb,MAAM,WAAW,mBACjB,QAAQ,SAAS,IACtC;AAEH,YAAO,eAAe,SAAS,GAAG;;AAGpC,QAAI;KACF,MAAM,OAAO,MAAM,0BACjB,aACA,OACA,cACA,QAAQ,QACT;AACD,SAAI,KAAK,iBAAiB,WAAW;AACnC,UAAI,KAAK,KACP,UAAS;OACP,IAAI;OACJ,QAAQ;OACR;OACA;OACA;OACA,cAAc,KAAK;OACnB,SAAS;OACV,CAAC;UAEF,SAAQ,OAAO,MACb,kCAAkC,aAAa,uBAAuB,KAAK,aAAa,4BACzF;AAEH,aAAO,eAAe,SAAS,MAAM;;AAEvC,WAAM,qBAAqB,KAAK,WAAW,MAAM;KACjD,MAAM,SAAS,MAAM,wBACnB,aACA,OACA,cACA,QAAQ,QACT;KACD,IAAI,cAAc;AAClB,SAAI,SAAS,KAAA,GAAW;AACtB,YAAM,eAAe,aAAa,OAAO,cAAc,MAAM,QAAQ,QAAQ;AAC7E,oBAAc;;AAEhB,SAAI,KAAK,MAAM;AACb,eAAS;OACP,IAAI;OACJ;OACA;OACA;OACA,cAAc,KAAK;OACnB;OACA;OACD,CAAC;AACF,aAAO,eAAe,SAAS,GAAG;;AAEpC,aAAQ,OAAO,MACb,2BAA2B,MAAM,OAAO,YAAY,oBAChC,aAAa,mBACb,MAAM,WAAW,mBACjB,cAAc,YAAY,SAAS,IACxD;AACD,YAAO,eAAe,SAAS,GAAG;aAC3B,KAAK;AACZ,YAAO,qBAAqB,KAAK,MAAM,IAAI;;;GAGhD,CAAC;EAoWE,QAnVyB,cAAc;GACzC,MAAM;IACJ,MAAM;IACN,aAAa;IACd;GACD,MAAM;IACJ,IAAI;KACF,MAAM;KACN,aAAa;KACb,UAAU;KACX;IACD,iBAAiB;KACf,MAAM;KACN,aAAa;KACd;IACD,iBAAiB;KACf,MAAM;KACN,aAAa;KACd;IACD,UAAU;KACR,MAAM;KACN,aAAa;KACb,SAAS;KACV;IACD,WAAW;KACT,MAAM;KACN,aAAa;KACd;IACD,MAAM;KAAE,MAAM;KAAW,aAAa;KAA+B,SAAS;KAAO;IACtF;GACD,MAAM,IAAI,EAAE,QAAQ;IAClB,MAAM,eAAe,OAAO,KAAK,qBAAqB,WAAW,KAAK,mBAAmB;AACzF,QAAI,iBAAiB,IAAI;AACvB,SAAI,KAAK,KACP,UAAS;MAAE,IAAI;MAAO,QAAQ;MAAyB,CAAC;SAExD,SAAQ,OAAO,MAAM,4DAA4D;AAEnF,YAAO,eAAe,SAAS,MAAM;;IAEvC,MAAM,WAAW,QAAQ,KAAK,SAAS;IACvC,MAAM,eACJ,OAAO,KAAK,qBAAqB,WAAW,KAAK,mBAAmB,KAAA;AACtE,QAAI,CAAC,YAAY,iBAAiB,KAAA,GAAW;AAC3C,SAAI,KAAK,KACP,UAAS;MAAE,IAAI;MAAO,QAAQ;MAAyB,CAAC;SAExD,SAAQ,OAAO,MACb,iFACD;AAEH,YAAO,eAAe,SAAS,MAAM;;IAGvC,MAAM,MAAM,MAAM,iBAAiB;KACjC,MAAM,KAAK;KACX,UAAU,KAAK;KACf,GAAI,KAAK,cAAc,KAAA,IAAY,EAAE,WAAW,KAAK,WAAW,GAAG,EAAE;KACtE,CAAC;AACF,QAAI,CAAC,IAAK;IACV,MAAM,QAAQ,MAAM,iBAAiB,KAAK,KAAK,KAAK;AACpD,QAAI,UAAU,KAAM;AACpB,uBAAmB,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IAC5C,MAAM,EAAE,SAAS,gBAAgB;AAEjC,QAAI;AACF,SAAI,UAAU;MACZ,MAAM,SAAS,MAAM,2BACnB,aACA,OACA,cACA,QAAQ,QACT;AACD,UAAI,KAAK,MAAM;AACb,gBAAS;QACP,IAAI;QACJ;QACA;QACA;QACA,QAAQ;QACR;QACD,CAAC;AACF,cAAO,eAAe,SAAS,GAAG;;AAEpC,cAAQ,OAAO,MACb,8BAA8B,aAAa,QAAQ,MAAM,OAAO,YAAY,KAC7E;AACD,aAAO,eAAe,SAAS,GAAG;;KAEpC,MAAM,SAAS,MAAM,iBACnB;MACE;MACA,WAAW;MACX;MACA,cAAc,gBAAgB;MAC/B,EACD,QAAQ,QACT;AACD,SAAI,KAAK,MAAM;AACb,eAAS;OACP,IAAI;OACJ;OACA;OACA;OACA,QAAQ;OACR;OACD,CAAC;AACF,aAAO,eAAe,SAAS,GAAG;;KAEpC,MAAM,cAAc,OAAO,OAAO,gBAAgB,WAAW,OAAO,cAAc;AAClF,aAAQ,OAAO,MACb,oBAAoB,aAAa,mBAAmB,MAAM,OAAO,YAAY,MAC1E,cAAc,cAAc,gBAAgB,MAC7C,KACH;AACD,YAAO,eAAe,SAAS,GAAG;aAC3B,KAAK;AACZ,YAAO,qBAAqB,KAAK,MAAM,IAAI;;;GAGhD,CAAC;EA4NE,SA9M0B,cAAc;GAC1C,MAAM;IACJ,MAAM;IACN,aAAa;IACd;GACD,MAAM;IACJ,IAAI;KACF,MAAM;KACN,aAAa;KACb,UAAU;KACX;IACD,iBAAiB;KACf,MAAM;KACN,aAAa;KACd;IACD,SAAS;KACP,MAAM;KACN,aAAa;KACb,SAAS;KACV;IACD,WAAW;KACT,MAAM;KACN,aAAa;KACd;IACD,MAAM;KAAE,MAAM;KAAW,aAAa;KAA+B,SAAS;KAAO;IACtF;GACD,MAAM,IAAI,EAAE,QAAQ;IAClB,MAAM,eAAe,OAAO,KAAK,qBAAqB,WAAW,KAAK,mBAAmB;AACzF,QAAI,iBAAiB,IAAI;AACvB,SAAI,KAAK,KACP,UAAS;MAAE,IAAI;MAAO,QAAQ;MAAyB,CAAC;SAExD,SAAQ,OAAO,MAAM,6DAA6D;AAEpF,YAAO,eAAe,SAAS,MAAM;;AAEvC,QAAI,CAAC,KAAK,SAAS;AACjB,SAAI,KAAK,KACP,UAAS;MACP,IAAI;MACJ,QAAQ;MACR,SAAS;MACV,CAAC;SAEF,SAAQ,OAAO,MACb,sGAED;AAEH,YAAO,eAAe,SAAS,MAAM;;IAGvC,MAAM,MAAM,MAAM,iBAAiB;KACjC,MAAM,KAAK;KACX,UAAU,KAAK;KACf,GAAI,KAAK,cAAc,KAAA,IAAY,EAAE,WAAW,KAAK,WAAW,GAAG,EAAE;KACtE,CAAC;AACF,QAAI,CAAC,IAAK;IACV,MAAM,QAAQ,MAAM,iBAAiB,KAAK,KAAK,KAAK;AACpD,QAAI,UAAU,KAAM;AACpB,uBAAmB,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IAC5C,MAAM,EAAE,SAAS,gBAAgB;AAEjC,QAAI;KACF,MAAM,SAAS,MAAM,kBACnB;MAAE;MAAa,WAAW;MAAO;MAAc,EAC/C,QAAQ,QACT;AACD,SAAI,KAAK,MAAM;AACb,eAAS;OAAE,IAAI;OAAM;OAAa;OAAO;OAAc;OAAQ,CAAC;AAChE,aAAO,eAAe,SAAS,GAAG;;AAEpC,aAAQ,OAAO,MACb,mBAAmB,aAAa,WAAW,MAAM,OAAO,YAAY,KACrE;AACD,YAAO,eAAe,SAAS,GAAG;aAC3B,KAAK;AACZ,YAAO,qBAAqB,KAAK,MAAM,IAAI;;;GAGhD,CAAC;EA+HE,aArH2B,cAAc;GAC3C,MAAM;IACJ,MAAM;IACN,aAAa;IACd;GACD,MAAM;IACJ,IAAI;KACF,MAAM;KACN,aAAa;KACb,UAAU;KACX;IACD,iBAAiB;KACf,MAAM;KACN,aAAa;KACd;IACD,WAAW;KACT,MAAM;KACN,aAAa;KACd;IACD,MAAM;KAAE,MAAM;KAAW,aAAa;KAA+B,SAAS;KAAO;IACtF;GACD,MAAM,IAAI,EAAE,QAAQ;IAClB,MAAM,eAAe,OAAO,KAAK,qBAAqB,WAAW,KAAK,mBAAmB;AACzF,QAAI,iBAAiB,IAAI;AACvB,SAAI,KAAK,KACP,UAAS;MAAE,IAAI;MAAO,QAAQ;MAAyB,CAAC;SAExD,SAAQ,OAAO,MAAM,+DAA+D;AAEtF,YAAO,eAAe,SAAS,MAAM;;IAEvC,MAAM,MAAM,MAAM,iBAAiB;KACjC,MAAM,KAAK;KACX,UAAU,KAAK;KACf,GAAI,KAAK,cAAc,KAAA,IAAY,EAAE,WAAW,KAAK,WAAW,GAAG,EAAE;KACtE,CAAC;AACF,QAAI,CAAC,IAAK;IACV,MAAM,QAAQ,MAAM,iBAAiB,KAAK,KAAK,KAAK;AACpD,QAAI,UAAU,KAAM;AACpB,uBAAmB,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IAC5C,MAAM,EAAE,SAAS,gBAAgB;AACjC,QAAI;KACF,MAAM,SAAS,MAAM,mBAAmB,aAAa,OAAO,cAAc,QAAQ,QAAQ;AAC1F,SAAI,KAAK,MAAM;AACb,eAAS;OAAE,IAAI;OAAM;OAAa;OAAO;OAAc;OAAQ,CAAC;AAChE,aAAO,eAAe,SAAS,GAAG;;AAEpC,aAAQ,OAAO,MAAM,6BAA6B,aAAa,QAAQ,MAAM,KAAK;AAClF,YAAO,eAAe,SAAS,GAAG;aAC3B,KAAK;AACZ,YAAO,qBAAqB,KAAK,MAAM,IAAI;;;GAGhD,CAAC;EAiEE,cA/D4B,cAAc;GAC5C,MAAM;IACJ,MAAM;IACN,aAAa;IACd;GACD,MAAM;IACJ,IAAI;KACF,MAAM;KACN,aAAa;KACb,UAAU;KACX;IACD,WAAW;KACT,MAAM;KACN,aAAa;KACd;IACD,MAAM;KAAE,MAAM;KAAW,aAAa;KAA+B,SAAS;KAAO;IACtF;GACD,MAAM,IAAI,EAAE,QAAQ;IAClB,MAAM,MAAM,MAAM,iBAAiB;KACjC,MAAM,KAAK;KACX,UAAU,KAAK;KACf,GAAI,KAAK,cAAc,KAAA,IAAY,EAAE,WAAW,KAAK,WAAW,GAAG,EAAE;KACtE,CAAC;AACF,QAAI,CAAC,IAAK;IACV,MAAM,QAAQ,MAAM,iBAAiB,KAAK,KAAK,KAAK;AACpD,QAAI,UAAU,KAAM;AACpB,uBAAmB,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IAC5C,MAAM,EAAE,SAAS,gBAAgB;AACjC,QAAI;KACF,MAAM,QAAQ,MAAM,qBAAqB,aAAa,OAAO,QAAQ,QAAQ;AAC7E,SAAI,KAAK,MAAM;AACb,eAAS;OAAE,IAAI;OAAM;OAAa;OAAO;OAAO,CAAC;AACjD,aAAO,eAAe,SAAS,GAAG;;KAEpC,MAAM,OAAO,OAAO,KAAK,MAAM;AAC/B,SAAI,KAAK,WAAW,GAAG;AACrB,cAAQ,OAAO,MAAM,OAAO,MAAM,OAAO,YAAY,8BAA8B;AACnF,aAAO,eAAe,SAAS,GAAG;;AAEpC,aAAQ,OAAO,MAAM,OAAO,MAAM,OAAO,YAAY,MAAM;AAC3D,UAAK,MAAM,KAAK,MAAM;MACpB,MAAM,IAAI,MAAM;AAChB,cAAQ,OAAO,MAAM,KAAK,EAAE,IAAI,OAAO,MAAM,WAAW,IAAI,KAAK,UAAU,EAAE,CAAC,IAAI;;AAEpF,YAAO,eAAe,SAAS,GAAG;aAC3B,KAAK;AACZ,YAAO,qBAAqB,KAAK,MAAM,IAAI;;;GAGhD,CAAC;EAeC;CACF,CAAC;AAiBF,MAAM,wBAAwB;AAC9B,MAAM,aAAa;AAOnB,SAAgB,sBACd,MACA,KACe;CACf,MAAM,MAAM,KAAK;CACjB,IAAI,KAAoB;AACxB,KAAI,OAAO,QAAQ,YAAY,OAAO,SAAS,IAAI,CAAE,MAAK;UACjD,OAAO,QAAQ,UAAU;EAChC,MAAM,SAAS,KAAK,MAAM,IAAI;AAC9B,MAAI,OAAO,SAAS,OAAO,CAAE,MAAK;;AAEpC,KAAI,OAAO,KAAM,QAAO;AACxB,QAAO,KAAK,OAAO,KAAK,OAAO,WAAW;;AAG5C,SAAS,aAAa,MAA6B;AACjD,KAAI,SAAS,KAAM,QAAO;AAC1B,KAAI,OAAO,EAAG,QAAO;AACrB,KAAI,QAAQ,sBAAuB,QAAO,cAAc,KAAK;AAC7D,QAAO;;AAGT,MAAM,iBAAiB,cAAc;CACnC,MAAM;EACJ,MAAM;EACN,aAAa;EACd;CACD,MAAM;EACJ,IAAI;GACF,MAAM;GACN,aAAa;GACb,UAAU;GACX;EACD,WAAW;GACT,MAAM;GACN,aAAa;GACd;EACD,MAAM;GAAE,MAAM;GAAW,aAAa;GAA+B,SAAS;GAAO;EACtF;CACD,MAAM,IAAI,EAAE,QAAQ;EAClB,MAAM,MAAM,MAAM,iBAAiB;GACjC,MAAM,KAAK;GACX,UAAU,KAAK;GACf,GAAI,KAAK,cAAc,KAAA,IAAY,EAAE,WAAW,KAAK,WAAW,GAAG,EAAE;GACtE,CAAC;AACF,MAAI,CAAC,IAAK;EACV,MAAM,QAAQ,MAAM,iBAAiB,KAAK,KAAK,KAAK;AACpD,MAAI,UAAU,KAAM;AACpB,qBAAmB,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;EAC5C,MAAM,EAAE,SAAS,gBAAgB;AAEjC,MAAI;GACF,MAAM,QAAQ,MAAM,WAAW,aAAa,OAAO,QAAQ,QAAQ;GACnE,MAAM,MAAM,KAAK,KAAK;GACtB,MAAM,YAEF,MAAM,KAAK,OAAO;IACpB,GAAG;IACH,iBAAiB,sBAAsB,GAAG,IAAI;IAC/C,EAAE;AACH,OAAI,KAAK,MAAM;AACb,aAAS;KAAE,IAAI;KAAM;KAAa;KAAO,OAAO;KAAW,CAAC;AAC5D,WAAO,eAAe,SAAS,GAAG;;AAEpC,OAAI,UAAU,WAAW,GAAG;AAC1B,YAAQ,OAAO,MAAM,OAAO,MAAM,OAAO,YAAY,oBAAoB;AACzE,WAAO,eAAe,SAAS,GAAG;;AAEpC,WAAQ,OAAO,MAAM,OAAO,MAAM,OAAO,YAAY,KAAK,UAAU,OAAO,YAAY;AACvF,QAAK,MAAM,KAAK,WAAW;IACzB,MAAM,KACJ,OAAO,EAAE,OAAO,YAAY,OAAO,EAAE,OAAO,WACxC,EAAE,KACF,OAAO,EAAE,WAAW,YAAY,OAAO,EAAE,WAAW,WAClD,EAAE,SACF;IACR,MAAM,KAAK,OAAO,EAAE,eAAe,WAAW,EAAE,aAAa;IAC7D,MAAM,YAAY,OAAO,EAAE,cAAc,WAAW,EAAE,YAAY;IAKlE,MAAM,YACJ,OAAO,EAAE,cAAc,WACnB,EAAE,YACF,OAAO,EAAE,eAAe,WACtB,EAAE,aACF,OAAO,EAAE,aAAa,YAAY,OAAO,SAAS,EAAE,SAAS,GAC3D,IAAI,KAAK,EAAE,SAAS,CAAC,aAAa,GAClC;AACV,YAAQ,OAAO,MACb,GAAG,GAAG,IAAI,GAAG,IAAI,UAAU,IAAI,YAAY,aAAa,EAAE,gBAAgB,CAAC,IAC5E;;AAEH,UAAO,eAAe,SAAS,GAAG;WAC3B,KAAK;AACZ,UAAO,qBAAqB,KAAK,MAAM,IAAI;;;CAGhD,CAAC;AAcF,SAAgB,aACd,OACA,QAC0C;CAC1C,MAAM,SAAS,OAAO,MAAM;AAC5B,KAAI,OAAO,WAAW,EAAG,QAAO;AAChC,MAAK,MAAM,KAAK,OAAO;EACrB,MAAM,YACJ,OAAO,EAAE,OAAO,YAAY,OAAO,EAAE,OAAO,WACxC,EAAE,KACF,OAAO,EAAE,WAAW,YAAY,OAAO,EAAE,WAAW,WAClD,EAAE,SACF;AACR,MAAI,cAAc,QAAQ,OAAO,UAAU,KAAK,OAAQ,QAAO;;AAEjE,QAAO;;AAWT,SAAgB,kBACd,MACA,MAAc,KAAK,KAAK,EAC4B;CACpD,IAAI;AACJ,KAAI,OAAO,KAAK,aAAa,YAAY,OAAO,SAAS,KAAK,SAAS,CACrE,eAAc,KAAK;UACV,OAAO,KAAK,cAAc,UAAU;EAC7C,MAAM,IAAI,KAAK,MAAM,KAAK,UAAU;AACpC,MAAI,OAAO,SAAS,EAAE,CAAE,eAAc;YAC7B,OAAO,KAAK,eAAe,UAAU;EAC9C,MAAM,IAAI,KAAK,MAAM,KAAK,WAAW;AACrC,MAAI,OAAO,SAAS,EAAE,CAAE,eAAc;;AAExC,KAAI,gBAAgB,KAAA,EAAW,QAAO,EAAE;CACxC,MAAM,kBAAkB,KAAK,OAAO,cAAc,OAAO,MAAW;AACpE,QAAO;EAAE;EAAa;EAAiB;;AAkBzC,MAAM,mBAAmB,cAAc;CACrC,MAAM;EACJ,MAAM;EACN,aAAa;EACd;CACD,MAAM;EACJ,QAAQ;GAAE,MAAM;GAAc,aAAa;GAAkC,UAAU;GAAM;EAC7F,KAAK;GACH,MAAM;GACN,aAAa;GACd;EACD,WAAW;GACT,MAAM;GACN,aAAa;GACd;EACD,MAAM;GAAE,MAAM;GAAW,aAAa;GAA+B,SAAS;GAAO;EACtF;CACD,MAAM,IAAI,EAAE,QAAQ;EAClB,MAAM,SAAS,OAAO,KAAK,WAAW,WAAW,KAAK,OAAO,MAAM,GAAG;AACtE,MAAI,OAAO,WAAW,GAAG;AACvB,OAAI,KAAK,KACP,UAAS;IACP,IAAI;IACJ,QAAQ;IACR,SAAS;IACV,CAAC;OAEF,SAAQ,OAAO,MAAM,uCAAuC;AAE9D,UAAO,eAAe,SAAS,MAAM;;EAKvC,MAAM,MAAM,MAAM,iBAAiB;GACjC,MAAM,KAAK;GACX,UAAU,KAAK;GACf,YAAY;GACZ,GAAI,KAAK,cAAc,KAAA,IAAY,EAAE,WAAW,KAAK,WAAW,GAAG,EAAE;GACtE,CAAC;AACF,MAAI,CAAC,IAAK;EACV,MAAM,QAAQ,MAAM,iBAAiB,KAAK,KAAK,KAAK;AACpD,MAAI,UAAU,KAAM;AACpB,qBAAmB,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;EAC5C,MAAM,EAAE,SAAS,gBAAgB;AAEjC,MAAI;GAEF,MAAM,QAAQ,aADA,MAAM,WAAW,aAAa,OAAO,QAAQ,QAAQ,EACjC,OAAO;AACzC,OAAI,UAAU,MAAM;AAClB,QAAI,KAAK,KACP,UAAS;KACP,IAAI;KACJ,QAAQ;KACR;KACA;KACA;KACA,SAAS,QAAQ,OAAO,oBAAoB;KAC7C,CAAC;QAEF,SAAQ,OAAO,MACb,wBAAwB,OAAO,oBAAoB,MAAM,OAAO,YAAY,KAC7E;AAEH,WAAO,eAAe,SAAS,MAAM;;GAGvC,MAAM,SAAS,kBAAkB,MAAM;AACvC,OAAI,KAAK,MAAM;AACb,aAAS;KAAE,IAAI;KAAM;KAAa;KAAO,MAAM;KAAO,GAAG;KAAQ,CAAC;AAClE,WAAO,eAAe,SAAS,GAAG;;GAGpC,MAAM,KACJ,OAAO,MAAM,OAAO,YAAY,OAAO,MAAM,OAAO,WAChD,MAAM,KACN,OAAO,MAAM,WAAW,YAAY,OAAO,MAAM,WAAW,WAC1D,MAAM,SACN;GACR,MAAM,OAAO,OAAO,MAAM,SAAS,WAAW,MAAM,OAAO;GAC3D,MAAM,KAAK,OAAO,MAAM,eAAe,WAAW,MAAM,aAAa;GACrE,MAAM,YAAY,OAAO,MAAM,cAAc,WAAW,MAAM,YAAY;GAC1E,MAAM,eACJ,OAAO,gBAAgB,KAAA,IAAY,IAAI,KAAK,OAAO,YAAY,CAAC,aAAa,GAAG;GAClF,MAAM,SAAS,OAAO,MAAM,WAAW,WAAW,MAAM,SAAS;GAEjE,MAAM,QAAkB,CACtB,OAAO,MAAM,OAAO,YAAY,UAAU,MAC1C,kBAAkB,OACnB;AACD,OAAI,GAAI,OAAM,KAAK,kBAAkB,KAAK;AAC1C,OAAI,UAAW,OAAM,KAAK,kBAAkB,YAAY;AACxD,OAAI,aAAc,OAAM,KAAK,kBAAkB,eAAe;AAC9D,OAAI,OAAO,oBAAoB,KAAA,GAAW;IACxC,MAAM,IAAI,OAAO;IAIjB,MAAM,SACJ,IAAI,IAAI,OAAO,EAAE,KAAK,MAAM,IAAI,qBAAqB,aAAa,CAAC,EAAE;AACvE,UAAM,KAAK,sBAAsB,IAAI,SAAS;;AAEhD,OAAI,OAAQ,OAAM,KAAK,kBAAkB,SAAS;AAClD,WAAQ,OAAO,MAAM,GAAG,MAAM,KAAK,KAAK,CAAC,IAAI;AAC7C,UAAO,eAAe,SAAS,GAAG;WAC3B,KAAK;AACZ,UAAO,qBAAqB,KAAK,MAAM,IAAI;;;CAGhD,CAAC;AAiCF,MAAM,eAAe;AAErB,SAAS,cAAc,KAAgE;AACrF,KAAI,OAAO,QAAQ,YAAY,IAAI,WAAW,EAC5C,QAAO,EAAE,OAAO,0CAA0C;AAE5D,KAAI,CAAC,aAAa,KAAK,IAAI,CACzB,QAAO,EACL,OACE,wGACH;AAEH,QAAO,EAAE,OAAO,KAAK;;AAyLvB,MAAM,eAAe,cAAc;CACjC,MAAM;EACJ,MAAM;EACN,aAAa;EACd;CACD,aAAa;EACX,IAAI;EACJ,MAAM;EACN,OA9LsB,cAAc;GACtC,MAAM;IACJ,MAAM;IACN,aAAa;IACd;GACD,MAAM;IACJ,IAAI;KACF,MAAM;KACN,aAAa;KACb,UAAU;KACX;IACD,WAAW;KACT,MAAM;KACN,aAAa;KACd;IACD,MAAM;KACJ,MAAM;KACN,aAAa;KACd;IACD,KAAK;KACH,MAAM;KACN,aAAa;KACd;IACD,aAAa;KACX,MAAM;KACN,aAAa;KACb,SAAS;KACV;IACD,MAAM;KAAE,MAAM;KAAW,aAAa;KAA+B,SAAS;KAAO;IACtF;GACD,MAAM,IAAI,EAAE,QAAQ;IAClB,MAAM,aAAa,cAAc,KAAK,KAAK;AAC3C,QAAI,WAAW,YAAY;AACzB,SAAI,KAAK,KACP,UAAS;MAAE,IAAI;MAAO,QAAQ;MAAgB,SAAS,WAAW;MAAO,CAAC;SAE1E,SAAQ,OAAO,MAAM,oBAAoB,WAAW,MAAM,IAAI;AAEhE,YAAO,eAAe,SAAS,MAAM;;IAEvC,MAAM,OAAO,WAAW;IAExB,MAAM,MAAM,MAAM,iBAAiB;KACjC,MAAM,KAAK;KACX,UAAU,KAAK;KACf,GAAI,KAAK,cAAc,KAAA,IAAY,EAAE,WAAW,KAAK,WAAW,GAAG,EAAE;KACtE,CAAC;AACF,QAAI,CAAC,IAAK;IACV,MAAM,QAAQ,MAAM,iBAAiB,KAAK,KAAK,KAAK;AACpD,QAAI,UAAU,KAAM;AACpB,uBAAmB,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IAC5C,MAAM,EAAE,SAAS,gBAAgB;AAEjC,QAAI;KACF,MAAM,SAAS,MAAM,UAAU,aAAa,OAAO,MAAM,QAAQ,QAAQ;KAEzE,IAAI;AACJ,SAAI,OAAO,KAAK,QAAQ,YAAY,KAAK,IAAI,SAAS,GAAG;MACvD,MAAM,MAAMC,QAAY,KAAK,IAAI;AACjC,UAAI;AACF,aAAM,MAAM,KAAK,EAAE,WAAW,MAAM,CAAC;eAC9B,KAAK;OACZ,MAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;AAChE,WAAI,KAAK,KACP,UAAS;QACP,IAAI;QACJ,QAAQ;QACR,SAAS,wBAAwB,KAAK,UAAU,KAAK,IAAI,CAAC,IAAI;QAC/D,CAAC;WAEF,SAAQ,OAAO,MACb,kDAAkD,KAAK,UAAU,KAAK,IAAI,CAAC,IAAI,QAAQ,IACxF;AAEH,cAAO,eAAe,SAAS,MAAM;;MAEvC,MAAM,WAAWA,QAAY,KAAK,GAAG,KAAK,MAAM;MAChD,MAAM,UAAUA,QAAY,KAAK,GAAG,KAAK,MAAM;AAC/C,YAAM,UAAU,UAAU,OAAO,WAAW,EAAE,MAAM,KAAO,CAAC;AAC5D,YAAM,UAAU,SAAS,OAAO,YAAY,EAAE,MAAM,KAAO,CAAC;AAC5D,gBAAU;OAAE,WAAW;OAAU,YAAY;OAAS;;AAGxD,SAAI,KAAK,MAAM;MACb,MAAM,aAAa,KAAK,iBAAiB,QAAQ,YAAY,KAAA;AAC7D,eAAS;OACP,IAAI;OACJ;OACA;OACA;OACA,WAAW,OAAO;OAClB,GAAI,aAAa,EAAE,YAAY,OAAO,YAAY,GAAG,EAAE;OACvD,GAAI,UAAU,EAAE,SAAS,GAAG,EAAE;OAC/B,CAAC;AACF,aAAO,eAAe,SAAS,GAAG;;AAKpC,SAAI,QACF,SAAQ,OAAO,MACb,OAAO,MAAM,OAAO,YAAY,kBAAkB,KAAK,aAC1C,QAAQ,UAAU,YAClB,QAAQ,WAAW,IACjC;SAED,SAAQ,OAAO,MACb,OAAO,MAAM,OAAO,YAAY,kBAAkB,KAAK,KAAK,OAAO,eAChE,OAAO,UAAU,SAAS,KAAK,GAAG,KAAK,QACxC,wFACH;AAEH,YAAO,eAAe,SAAS,GAAG;aAC3B,KAAK;AACZ,YAAO,qBAAqB,KAAK,MAAM,IAAI;;;GAGhD,CAAC;EA0EE,QAxEuB,cAAc;GACvC,MAAM;IACJ,MAAM;IACN,aAAa;IACd;GACD,MAAM;IACJ,QAAQ;KAAE,MAAM;KAAc,aAAa;KAAkC,UAAU;KAAM;IAC7F,KAAK;KACH,MAAM;KACN,aAAa;KACd;IACD,WAAW;KACT,MAAM;KACN,aAAa;KACd;IACD,MAAM;KAAE,MAAM;KAAW,aAAa;KAA+B,SAAS;KAAO;IACtF;GACD,MAAM,IAAI,EAAE,QAAQ;IAClB,MAAM,SAAS,OAAO,KAAK,WAAW,WAAW,KAAK,OAAO,MAAM,GAAG;AACtE,QAAI,OAAO,WAAW,GAAG;AACvB,SAAI,KAAK,KACP,UAAS;MACP,IAAI;MACJ,QAAQ;MACR,SAAS;MACV,CAAC;SAEF,SAAQ,OAAO,MAAM,yCAAyC;AAEhE,YAAO,eAAe,SAAS,MAAM;;IAQvC,MAAM,MAAM,MAAM,iBAAiB;KACjC,MAAM,KAAK;KACX,UAAU,KAAK;KACf,YAAY;KACZ,GAAI,KAAK,cAAc,KAAA,IAAY,EAAE,WAAW,KAAK,WAAW,GAAG,EAAE;KACtE,CAAC;AACF,QAAI,CAAC,IAAK;IACV,MAAM,QAAQ,MAAM,iBAAiB,KAAK,KAAK,KAAK;AACpD,QAAI,UAAU,KAAM;AACpB,uBAAmB,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IAC5C,MAAM,EAAE,SAAS,gBAAgB;AAEjC,QAAI;AACF,WAAM,WAAW,aAAa,OAAO,QAAQ,QAAQ,QAAQ;AAC7D,SAAI,KAAK,MAAM;AACb,eAAS;OAAE,IAAI;OAAM;OAAa;OAAO;OAAQ,CAAC;AAClD,aAAO,eAAe,SAAS,GAAG;;AAEpC,aAAQ,OAAO,MAAM,OAAO,MAAM,OAAO,YAAY,kBAAkB,OAAO,IAAI;AAClF,YAAO,eAAe,SAAS,GAAG;aAC3B,KAAK;AACZ,YAAO,qBAAqB,KAAK,MAAM,IAAI;;;GAGhD,CAAC;EAYC;CACF,CAAC;AAiBF,MAAM,mBAA+C;CAAC;CAAO;CAAQ;CAAQ;AAE7E,SAAS,aAAa,KAAa,OAAsD;AACvF,KAAI,CAAC,sBAAsB,KAAK,IAAI,CAClC,QAAO,EAAE,OAAO,KAAK,MAAM,2BAA2B,KAAK,UAAU,IAAI,CAAC,IAAI;CAEhF,MAAM,oBAAI,IAAI,KAAK,GAAG,IAAI,YAAY;AACtC,KAAI,OAAO,MAAM,EAAE,SAAS,CAAC,CAC3B,QAAO,EAAE,OAAO,KAAK,MAAM,4BAA4B,KAAK,UAAU,IAAI,CAAC,IAAI;AAEjF,QAAO,EAAE,OAAO,KAAK;;AAGvB,SAAS,gBAAwB;CAC/B,MAAM,oBAAI,IAAI,MAAM;AAIpB,QAAO,GAHG,EAAE,aAAa,CAGb,GAFF,OAAO,EAAE,UAAU,GAAG,EAAE,CAAC,SAAS,GAAG,IAAI,CAElC,GADL,OAAO,EAAE,SAAS,CAAC,CAAC,SAAS,GAAG,IAAI;;AAIlD,SAAS,gBAAgB,MAAsB;CAC7C,MAAM,oBAAI,IAAI,MAAM;AACpB,GAAE,QAAQ,EAAE,SAAS,GAAG,KAAK;AAI7B,QAAO,GAHG,EAAE,aAAa,CAGb,GAFF,OAAO,EAAE,UAAU,GAAG,EAAE,CAAC,SAAS,GAAG,IAAI,CAElC,GADL,OAAO,EAAE,SAAS,CAAC,CAAC,SAAS,GAAG,IAAI;;AAIlD,MAAM,iBAAiB,cAAc;CACnC,MAAM;EACJ,MAAM;EACN,aAAa;EACd;CACD,MAAM;EACJ,IAAI;GACF,MAAM;GACN,aAAa;GACb,UAAU;GACX;EACD,WAAW;GACT,MAAM;GACN,aAAa;GACd;EACD,aAAa;GACX,MAAM;GACN,aAAa;GACb,SAAS;GACV;EACD,OAAO;GACL,MAAM;GACN,aAAa;GACd;EACD,KAAK;GACH,MAAM;GACN,aAAa;GACd;EACD,SAAS;GACP,MAAM;GACN,aAAa;GACb,SAAS;GACV;EACD,MAAM;GAAE,MAAM;GAAW,aAAa;GAA+B,SAAS;GAAO;EACtF;CACD,MAAM,IAAI,EAAE,QAAQ;EAClB,MAAM,WAAW,OAAO,KAAK,aAAa,CAAC,aAAa;AACxD,MAAI,CAAC,iBAAiB,SAAS,SAA4B,EAAE;GAC3D,MAAM,UAAU,8BAA8B,iBAAiB,KAAK,IAAI,CAAC,QAAQ,KAAK,UAAU,KAAK,aAAa,CAAC;AACnH,OAAI,KAAK,KAAM,UAAS;IAAE,IAAI;IAAO,QAAQ;IAAqB;IAAS,CAAC;OACvE,SAAQ,OAAO,MAAM,GAAG,QAAQ,IAAI;AACzC,UAAO,eAAe,SAAS,MAAM;;EAIvC,MAAM,YAAY,aADF,KAAK,MAAM,OAAO,KAAK,IAAI,GAAG,eAAe,EACrB,MAAM;AAC9C,MAAI,WAAW,WAAW;AACxB,OAAI,KAAK,KAAM,UAAS;IAAE,IAAI;IAAO,QAAQ;IAAgB,SAAS,UAAU;IAAO,CAAC;OACnF,SAAQ,OAAO,MAAM,GAAG,UAAU,MAAM,IAAI;AACjD,UAAO,eAAe,SAAS,MAAM;;EAIvC,MAAM,cAAc,aADF,KAAK,QAAQ,OAAO,KAAK,MAAM,GAAG,gBAAgB,GAAG,EAC3B,QAAQ;AACpD,MAAI,WAAW,aAAa;AAC1B,OAAI,KAAK,KAAM,UAAS;IAAE,IAAI;IAAO,QAAQ;IAAgB,SAAS,YAAY;IAAO,CAAC;OACrF,SAAQ,OAAO,MAAM,GAAG,YAAY,MAAM,IAAI;AACnD,UAAO,eAAe,SAAS,MAAM;;AAGvC,MAAI,YAAY,QAAQ,UAAU,OAAO;GACvC,MAAM,UAAU,YAAY,YAAY,MAAM,gCAAgC,UAAU,MAAM;AAC9F,OAAI,KAAK,KAAM,UAAS;IAAE,IAAI;IAAO,QAAQ;IAAgB;IAAS,CAAC;OAClE,SAAQ,OAAO,MAAM,GAAG,QAAQ,IAAI;AACzC,UAAO,eAAe,SAAS,MAAM;;EAGvC,MAAM,MAAM,MAAM,iBAAiB;GACjC,MAAM,KAAK;GACX,UAAU,KAAK;GACf,GAAI,KAAK,cAAc,KAAA,IAAY,EAAE,WAAW,KAAK,WAAW,GAAG,EAAE;GACtE,CAAC;AACF,MAAI,CAAC,IAAK;EACV,MAAM,QAAQ,MAAM,iBAAiB,KAAK,KAAK,KAAK;AACpD,MAAI,UAAU,KAAM;AACpB,qBAAmB,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;EAC5C,MAAM,EAAE,SAAS,gBAAgB;AAEjC,MAAI;GACF,MAAM,SAAS,MAAM,uBACnB;IACE;IACA,WAAW;IACX,cAAc;IACd,WAAW,YAAY;IACvB,SAAS,UAAU;IACnB,SAAS,KAAK;IACf,EACD,QAAQ,QACT;AACD,OAAI,KAAK,MAAM;AACb,aAAS;KACP,IAAI;KACJ;KACA;KACA,cAAc;KACd,WAAW,YAAY;KACvB,SAAS,UAAU;KACnB,GAAI,OAAO,cAAc,KAAA,IAAY,EAAE,WAAW,OAAO,WAAW,GAAG,EAAE;KACzE,SAAS,OAAO;KACjB,CAAC;AACF,WAAO,eAAe,SAAS,GAAG;;GAEpC,MAAM,SAAS,OAAO,MAAM,OAAO,YAAY,MAAM,SAAS,KAAK,YAAY,MAAM,KAAK,UAAU;AACpG,OAAI,OAAO,QAAQ,WAAW,GAAG;AAC/B,YAAQ,OAAO,MAAM,GAAG,OAAO,gBAAgB;AAC/C,WAAO,eAAe,SAAS,GAAG;;AAEpC,WAAQ,OAAO,MAAM,GAAG,OAAO,IAAI,OAAO,QAAQ,OAAO,cAAc;AACvE,QAAK,MAAM,KAAK,OAAO,SAAS;IAC9B,MAAM,OACJ,OAAO,EAAE,SAAS,WACd,EAAE,OACF,OAAO,EAAE,eAAe,WACtB,EAAE,aACF;IACR,MAAM,cACJ,OAAO,EAAE,gBAAgB,WACrB,EAAE,cACF,OAAO,EAAE,oBAAoB,WAC3B,EAAE,kBACF;IACR,MAAM,SACJ,OAAO,EAAE,WAAW,WAChB,EAAE,SACF,OAAO,EAAE,eAAe,WACtB,EAAE,aACF;AACR,YAAQ,OAAO,MAAM,GAAG,KAAK,IAAI,YAAY,IAAI,OAAO,IAAI;;AAE9D,UAAO,eAAe,SAAS,GAAG;WAC3B,KAAK;AACZ,UAAO,qBAAqB,KAAK,MAAM,IAAI;;;CAGhD,CAAC;AAwGF,MAAM,sBAAsB,cAAc;CACxC,MAAM;EACJ,MAAM;EACN,aAAa;EACd;CACD,aAAa,EACX,IA5E0B,cAAc;EAC1C,MAAM;GACJ,MAAM;GACN,aAAa;GACd;EACD,MAAM;GACJ,IAAI;IACF,MAAM;IACN,aAAa;IACb,UAAU;IACX;GACD,WAAW;IACT,MAAM;IACN,aAAa;IACd;GACD,QAAQ;IACN,MAAM;IACN,aAAa;IACd;GACD,MAAM;IAAE,MAAM;IAAW,aAAa;IAA+B,SAAS;IAAO;GACtF;EACD,MAAM,IAAI,EAAE,QAAQ;GAClB,MAAM,MAAM,MAAM,iBAAiB;IACjC,MAAM,KAAK;IACX,UAAU,KAAK;IACf,GAAI,KAAK,cAAc,KAAA,IAAY,EAAE,WAAW,KAAK,WAAW,GAAG,EAAE;IACtE,CAAC;AACF,OAAI,CAAC,IAAK;GACV,MAAM,QAAQ,MAAM,iBAAiB,KAAK,KAAK,KAAK;AACpD,OAAI,UAAU,KAAM;AACpB,sBAAmB,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;GAC5C,MAAM,EAAE,SAAS,gBAAgB;AAEjC,OAAI;IACF,MAAM,UAAU,MAAM,kBACpB;KACE;KACA,WAAW;KACX,GAAI,KAAK,WAAW,KAAA,IAAY,EAAE,QAAQ,OAAO,KAAK,OAAO,EAAE,GAAG,EAAE;KACrE,EACD,QAAQ,QACT;AACD,QAAI,KAAK,MAAM;AACb,cAAS;MAAE,IAAI;MAAM;MAAa;MAAO;MAAS,CAAC;AACnD,YAAO,eAAe,SAAS,GAAG;;AAEpC,QAAI,QAAQ,WAAW,GAAG;AACxB,aAAQ,OAAO,MAAM,OAAO,MAAM,OAAO,YAAY,iCAAiC;AACtF,YAAO,eAAe,SAAS,GAAG;;AAEpC,YAAQ,OAAO,MAAM,OAAO,MAAM,OAAO,YAAY,KAAK,QAAQ,OAAO,oBAAoB;AAC7F,SAAK,MAAM,KAAK,SAAS;KACvB,MAAM,KACJ,OAAO,EAAE,OAAO,YAAY,OAAO,EAAE,OAAO,WACxC,EAAE,KACF,OAAO,EAAE,aAAa,YAAY,OAAO,EAAE,aAAa,WACtD,EAAE,WACF;KACR,MAAM,QACJ,OAAO,EAAE,UAAU,WAAW,EAAE,QAAQ,OAAO,EAAE,SAAS,WAAW,EAAE,OAAO;KAChF,MAAM,SAAS,OAAO,EAAE,WAAW,WAAW,EAAE,SAAS;AACzD,aAAQ,OAAO,MAAM,GAAG,GAAG,IAAI,MAAM,IAAI,OAAO,IAAI;;AAEtD,WAAO,eAAe,SAAS,GAAG;YAC3B,KAAK;AACZ,WAAO,qBAAqB,KAAK,MAAM,IAAI;;;EAGhD,CAAC,EASC;CACF,CAAC;AA8FF,MAAM,kBAAkB,cAAc;CACpC,MAAM;EACJ,MAAM;EACN,aAAa;EACd;CACD,aAAa,EACX,IAlGsB,cAAc;EACtC,MAAM;GACJ,MAAM;GACN,aAAa;GACd;EACD,MAAM;GACJ,IAAI;IACF,MAAM;IACN,aAAa;IACb,UAAU;IACX;GACD,WAAW;IACT,MAAM;IACN,aAAa;IACd;GACD,MAAM;IAAE,MAAM;IAAU,aAAa;IAA4B,SAAS;IAAK;GAC/E,MAAM;IAAE,MAAM;IAAU,aAAa;IAAc,SAAS;IAAM;GAClE,QAAQ;IAAE,MAAM;IAAU,aAAa;IAAoD;GAC3F,MAAM;IAAE,MAAM;IAAW,aAAa;IAA+B,SAAS;IAAO;GACtF;EACD,MAAM,IAAI,EAAE,QAAQ;GAClB,MAAM,aAAa,oBAAoB,OAAO,KAAK,KAAK,EAAE,OAAO;AACjE,OAAI,WAAW,YAAY;AACzB,QAAI,KAAK,KAAM,UAAS;KAAE,IAAI;KAAO,QAAQ;KAAgB,SAAS,WAAW;KAAO,CAAC;QACpF,SAAQ,OAAO,MAAM,GAAG,WAAW,MAAM,IAAI;AAClD,WAAO,eAAe,SAAS,MAAM;;GAEvC,MAAM,aAAa,oBAAoB,OAAO,KAAK,KAAK,EAAE,OAAO;AACjE,OAAI,WAAW,YAAY;AACzB,QAAI,KAAK,KAAM,UAAS;KAAE,IAAI;KAAO,QAAQ;KAAgB,SAAS,WAAW;KAAO,CAAC;QACpF,SAAQ,OAAO,MAAM,GAAG,WAAW,MAAM,IAAI;AAClD,WAAO,eAAe,SAAS,MAAM;;GAGvC,MAAM,MAAM,MAAM,iBAAiB;IACjC,MAAM,KAAK;IACX,UAAU,KAAK;IACf,GAAI,KAAK,cAAc,KAAA,IAAY,EAAE,WAAW,KAAK,WAAW,GAAG,EAAE;IACtE,CAAC;AACF,OAAI,CAAC,IAAK;GACV,MAAM,QAAQ,MAAM,iBAAiB,KAAK,KAAK,KAAK;AACpD,OAAI,UAAU,KAAM;AACpB,sBAAmB,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;GAC5C,MAAM,EAAE,SAAS,gBAAgB;AAEjC,OAAI;IACF,MAAM,SAAS,MAAM,2BACnB;KACE;KACA,WAAW;KACX,MAAM,WAAW;KACjB,MAAM,WAAW;KACjB,GAAI,KAAK,WAAW,KAAA,IAAY,EAAE,QAAQ,OAAO,KAAK,OAAO,EAAE,GAAG,EAAE;KACrE,EACD,QAAQ,QACT;AACD,QAAI,KAAK,MAAM;AACb,cAAS;MACP,IAAI;MACJ;MACA;MACA,WAAW,OAAO;MAClB,QAAQ,OAAO;MAChB,CAAC;AACF,YAAO,eAAe,SAAS,GAAG;;AAEpC,QAAI,OAAO,MAAM,WAAW,GAAG;AAC7B,aAAQ,OAAO,MAAM,OAAO,MAAM,OAAO,YAAY,iCAAiC;AACtF,YAAO,eAAe,SAAS,GAAG;;AAEpC,YAAQ,OAAO,MACb,OAAO,MAAM,OAAO,YAAY,KAAK,OAAO,MAAM,OAAO,uBAAuB,OAAO,OAAO,WAAW,MAAM,OAAO,OAAO,WAAW,IACzI;AACD,SAAK,MAAM,KAAK,OAAO,OAAO;KAC5B,MAAM,KACJ,OAAO,EAAE,OAAO,YAAY,OAAO,EAAE,OAAO,WACxC,EAAE,KACF,OAAO,EAAE,eAAe,YAAY,OAAO,EAAE,eAAe,WAC1D,EAAE,aACF;KACR,MAAM,QACJ,OAAO,EAAE,UAAU,WAAW,EAAE,QAAQ,OAAO,EAAE,SAAS,WAAW,EAAE,OAAO;KAChF,MAAM,SAAS,OAAO,EAAE,WAAW,WAAW,EAAE,SAAS;AACzD,aAAQ,OAAO,MAAM,GAAG,GAAG,IAAI,MAAM,IAAI,OAAO,IAAI;;AAEtD,WAAO,eAAe,SAAS,GAAG;YAC3B,KAAK;AACZ,WAAO,qBAAqB,KAAK,MAAM,IAAI;;;EAGhD,CAAC,EASC;CACF,CAAC;AAqGF,MAAM,gBAAgB,cAAc;CAClC,MAAM;EACJ,MAAM;EACN,aAAa;EACd;CACD,aAAa,EACX,IAzGoB,cAAc;EACpC,MAAM;GACJ,MAAM;GACN,aAAa;GACd;EACD,MAAM;GACJ,IAAI;IACF,MAAM;IACN,aAAa;IACb,UAAU;IACX;GACD,WAAW;IACT,MAAM;IACN,aAAa;IACd;GACD,MAAM;IAAE,MAAM;IAAU,aAAa;IAA4B,SAAS;IAAK;GAC/E,MAAM;IAAE,MAAM;IAAU,aAAa;IAAc,SAAS;IAAM;GAClE,QAAQ;IAAE,MAAM;IAAU,aAAa;IAAgD;GACvF,SAAS;IACP,MAAM;IACN,aAAa;IACb,SAAS;IACV;GACD,MAAM;IAAE,MAAM;IAAW,aAAa;IAA+B,SAAS;IAAO;GACtF;EACD,MAAM,IAAI,EAAE,QAAQ;GAClB,MAAM,aAAa,oBAAoB,OAAO,KAAK,KAAK,EAAE,OAAO;AACjE,OAAI,WAAW,YAAY;AACzB,QAAI,KAAK,KAAM,UAAS;KAAE,IAAI;KAAO,QAAQ;KAAgB,SAAS,WAAW;KAAO,CAAC;QACpF,SAAQ,OAAO,MAAM,GAAG,WAAW,MAAM,IAAI;AAClD,WAAO,eAAe,SAAS,MAAM;;GAEvC,MAAM,aAAa,oBAAoB,OAAO,KAAK,KAAK,EAAE,OAAO;AACjE,OAAI,WAAW,YAAY;AACzB,QAAI,KAAK,KAAM,UAAS;KAAE,IAAI;KAAO,QAAQ;KAAgB,SAAS,WAAW;KAAO,CAAC;QACpF,SAAQ,OAAO,MAAM,GAAG,WAAW,MAAM,IAAI;AAClD,WAAO,eAAe,SAAS,MAAM;;GAGvC,MAAM,MAAM,MAAM,iBAAiB;IACjC,MAAM,KAAK;IACX,UAAU,KAAK;IACf,GAAI,KAAK,cAAc,KAAA,IAAY,EAAE,WAAW,KAAK,WAAW,GAAG,EAAE;IACtE,CAAC;AACF,OAAI,CAAC,IAAK;GACV,MAAM,QAAQ,MAAM,iBAAiB,KAAK,KAAK,KAAK;AACpD,OAAI,UAAU,KAAM;AACpB,sBAAmB,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;GAC5C,MAAM,EAAE,SAAS,gBAAgB;AAEjC,OAAI;IACF,MAAM,SAAS,MAAM,sBACnB;KACE;KACA,WAAW;KACX,YAAY,WAAW;KACvB,UAAU,WAAW;KACrB,GAAI,KAAK,WAAW,KAAA,IAAY,EAAE,QAAQ,OAAO,KAAK,OAAO,EAAE,GAAG,EAAE;KACpE,GAAI,KAAK,UAAU,EAAE,SAAS,MAAM,GAAG,EAAE;KAC1C,EACD,QAAQ,QACT;AACD,QAAI,KAAK,MAAM;AACb,cAAS;MACP,IAAI;MACJ;MACA;MACA,QAAQ,OAAO;MACf,WAAW,OAAO,aAAa;MAC/B,QAAQ,OAAO;MAChB,CAAC;AACF,YAAO,eAAe,SAAS,GAAG;;AAEpC,QAAI,OAAO,QAAQ,WAAW,GAAG;KAC/B,MAAM,KAAK,OAAO,YAAY,YAAY,OAAO,UAAU,KAAK;AAChE,aAAQ,OAAO,MAAM,OAAO,MAAM,OAAO,YAAY,sBAAsB,GAAG,IAAI;AAClF,YAAO,eAAe,SAAS,GAAG;;AAEpC,YAAQ,OAAO,MACb,OAAO,MAAM,OAAO,YAAY,KAAK,OAAO,QAAQ,OAAO,oBAAoB,OAAO,OAAO,WAAW,MAAM,OAAO,OAAO,WAAW,IACxI;AACD,SAAK,MAAM,KAAK,OAAO,SAAS;KAC9B,MAAM,OACJ,OAAO,EAAE,SAAS,WAAW,EAAE,OAAO,OAAO,EAAE,cAAc,WAAW,EAAE,YAAY;KACxF,MAAM,QACJ,OAAO,EAAE,UAAU,WACf,OAAO,EAAE,MAAM,GACf,OAAO,EAAE,eAAe,WACtB,OAAO,EAAE,WAAW,GACpB;AACR,aAAQ,OAAO,MAAM,GAAG,KAAK,IAAI,MAAM,IAAI;;AAE7C,WAAO,eAAe,SAAS,GAAG;YAC3B,KAAK;AACZ,WAAO,qBAAqB,KAAK,MAAM,IAAI;;;EAGhD,CAAC,EASC;CACF,CAAC;AAyIF,MAAM,mBAAmB,cAAc;CACrC,MAAM;EACJ,MAAM;EACN,aAAa;EACd;CACD,aAAa,EACX,IA7IuB,cAAc;EACvC,MAAM;GACJ,MAAM;GACN,aACE;GACH;EACD,MAAM;GACJ,IAAI;IACF,MAAM;IACN,aAAa;IACb,UAAU;IACX;GACD,WAAW;IACT,MAAM;IACN,aAAa;IACd;GACD,MAAM;IAAE,MAAM;IAAU,aAAa;IAA4B,SAAS;IAAK;GAC/E,MAAM;IAAE,MAAM;IAAU,aAAa;IAAc,SAAS;IAAM;GAClE,sBAAsB;IACpB,MAAM;IACN,aAAa,0BAA0B,6BAA6B,KAAK,MAAM,CAAC;IACjF;GACD,iBAAiB;IACf,MAAM;IACN,aACE;IACH;GACD,MAAM;IAAE,MAAM;IAAW,aAAa;IAA+B,SAAS;IAAO;GACtF;EACD,MAAM,IAAI,EAAE,QAAQ;GAClB,MAAM,aAAa,oBAAoB,OAAO,KAAK,KAAK,EAAE,OAAO;AACjE,OAAI,WAAW,YAAY;AACzB,QAAI,KAAK,KAAM,UAAS;KAAE,IAAI;KAAO,QAAQ;KAAgB,SAAS,WAAW;KAAO,CAAC;QACpF,SAAQ,OAAO,MAAM,GAAG,WAAW,MAAM,IAAI;AAClD,WAAO,eAAe,SAAS,MAAM;;GAEvC,MAAM,aAAa,oBAAoB,OAAO,KAAK,KAAK,EAAE,OAAO;AACjE,OAAI,WAAW,YAAY;AACzB,QAAI,KAAK,KAAM,UAAS;KAAE,IAAI;KAAO,QAAQ;KAAgB,SAAS,WAAW;KAAO,CAAC;QACpF,SAAQ,OAAO,MAAM,GAAG,WAAW,MAAM,IAAI;AAClD,WAAO,eAAe,SAAS,MAAM;;GAGvC,IAAI;AACJ,OAAI,KAAK,0BAA0B,KAAA,GAAW;IAC5C,MAAM,QAAQ,OAAO,KAAK,sBAAsB,CAAC,aAAa;AAC9D,QAAK,6BAAmD,SAAS,MAAM,CACrE,oBAAmB;SACd;KACL,MAAM,UAAU,wCAAwC,6BAA6B,KAAK,KAAK;AAC/F,SAAI,KAAK,KACP,UAAS;MACP,IAAI;MACJ,QAAQ;MACR,SAAS,CAAC,GAAG,6BAA6B;MAC3C,CAAC;SAEF,SAAQ,OAAO,MAAM,GAAG,QAAQ,IAAI;AAEtC,YAAO,eAAe,SAAS,MAAM;;;GAIzC,IAAI;AACJ,OAAI,KAAK,qBAAqB,KAAA,GAAW;IACvC,MAAM,MAAM,OAAO,KAAK,iBAAiB,CAAC,aAAa;AACvD,QAAI,QAAQ,OAAQ,kBAAiB;aAC5B,QAAQ,QAAS,kBAAiB;SACtC;KACH,MAAM,UAAU;AAChB,SAAI,KAAK,KAAM,UAAS;MAAE,IAAI;MAAO,QAAQ;MAAyB;MAAS,CAAC;SAC3E,SAAQ,OAAO,MAAM,GAAG,QAAQ,IAAI;AACzC,YAAO,eAAe,SAAS,MAAM;;;GAIzC,MAAM,MAAM,MAAM,iBAAiB;IACjC,MAAM,KAAK;IACX,UAAU,KAAK;IACf,GAAI,KAAK,cAAc,KAAA,IAAY,EAAE,WAAW,KAAK,WAAW,GAAG,EAAE;IACtE,CAAC;AACF,OAAI,CAAC,IAAK;GACV,MAAM,QAAQ,MAAM,iBAAiB,KAAK,KAAK,KAAK;AACpD,OAAI,UAAU,KAAM;AACpB,sBAAmB,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;GAC5C,MAAM,EAAE,SAAS,gBAAgB;AAEjC,OAAI;IACF,MAAM,SAAS,MAAM,kBACnB;KACE;KACA,WAAW;KACX,MAAM,WAAW;KACjB,MAAM,WAAW;KACjB,GAAI,qBAAqB,KAAA,IAAY,EAAE,kBAAkB,GAAG,EAAE;KAC9D,GAAI,mBAAmB,KAAA,IAAY,EAAE,gBAAgB,GAAG,EAAE;KAC3D,EACD,QAAQ,QACT;AACD,QAAI,KAAK,MAAM;AACb,cAAS;MACP,IAAI;MACJ;MACA;MACA,WAAW,OAAO;MAClB,gBAAgB,OAAO;MACxB,CAAC;AACF,YAAO,eAAe,SAAS,GAAG;;AAEpC,QAAI,OAAO,UAAU,WAAW,GAAG;AACjC,aAAQ,OAAO,MAAM,OAAO,MAAM,OAAO,YAAY,mBAAmB;AACxE,YAAO,eAAe,SAAS,GAAG;;AAEpC,YAAQ,OAAO,MACb,OAAO,MAAM,OAAO,YAAY,KAAK,OAAO,UAAU,OAAO,kBAAkB,OAAO,eAAe,YACtG;AACD,SAAK,MAAM,KAAK,OAAO,WAAW;KAChC,MAAM,KACJ,OAAO,EAAE,OAAO,YAAY,OAAO,EAAE,OAAO,WACxC,EAAE,KACF,OAAO,EAAE,eAAe,YAAY,OAAO,EAAE,eAAe,WAC1D,EAAE,aACF;KACR,MAAM,QACJ,OAAO,EAAE,UAAU,WAAW,EAAE,QAAQ,OAAO,EAAE,SAAS,WAAW,EAAE,OAAO;KAChF,MAAM,OAAO,OAAO,EAAE,iBAAiB,WAAW,EAAE,eAAe;AACnE,aAAQ,OAAO,MAAM,GAAG,GAAG,IAAI,MAAM,IAAI,KAAK,IAAI;;AAEpD,WAAO,eAAe,SAAS,GAAG;YAC3B,KAAK;AACZ,WAAO,qBAAqB,KAAK,MAAM,IAAI;;;EAGhD,CAAC,EASC;CACF,CAAC;AAEF,MAAMC,sBAAoB,cAAc;CACtC,MAAM;EACJ,MAAM;EACN,aAAa;EACd;CACD,MAAM;EACJ,YAAY;GACV,MAAM;GACN,aAAa;GACb,SAAS;GACV;EACD,MAAM;GAAE,MAAM;GAAW,aAAa;GAAyC,SAAS;GAAO;EAChG;CACD,MAAM,IAAI,EAAE,QAAQ;EAClB,MAAM,UAAU,MAAM,aAAa;AACnC,MAAI,CAAC,SAAS;AACZ,wBAAqB,KAAK,KAAK;AAC/B,UAAO,eAAe,SAAS,iBAAiB;;AAGlD,MAAI;GACF,MAAM,OAAO,MAAM,4BAA4B,QAAQ,QAAQ;GAM/D,MAAM,WAAW,KAAK,aAClB,KACG,QAAQ,MAAM,EAAE,cAAc,aAAa,CAC3C,KAAK,OAAO;IACX,GAAG;IACH,cAAc,EAAE,aACb,QAAQ,MAAM,EAAE,aAAa,CAC7B,KAAK,OAAO;KACX,GAAG;KACH,iBAAiB,EAAE,gBAAgB,QAAQ,MAAM,EAAE,aAAa;KACjE,EAAE;IACN,EAAE,GACL;AAEJ,OAAI,KAAK,MAAM;AACb,aAAS;KAAE,IAAI;KAAM,YAAY;KAAU,CAAC;AAC5C,WAAO,eAAe,SAAS,GAAG;;AAEpC,QAAK,MAAM,KAAK,UAAU;IACxB,MAAM,OAAO,EAAE,cAAc,eAAe,KAAK;AACjD,YAAQ,OAAO,MAAM,IAAI,EAAE,cAAc,GAAG,IAAI,EAAE,cAAc,OAAO,KAAK,IAAI;AAChF,SAAK,MAAM,KAAK,EAAE,cAAc;KAC9B,MAAM,QAAQ,EAAE,eAAe,KAAK;AACpC,aAAQ,OAAO,MAAM,KAAK,EAAE,GAAG,IAAI,EAAE,OAAO,MAAM,IAAI;AACtD,UAAK,MAAM,KAAK,EAAE,iBAAiB;MACjC,MAAM,QAAQ,EAAE,eAAe,KAAK;AACpC,cAAQ,OAAO,MAAM,OAAO,EAAE,GAAG,IAAI,EAAE,OAAO,MAAM,IAAI;;;;AAI9D,UAAO,eAAe,SAAS,GAAG;WAC3B,KAAK;AACZ,UAAO,qBAAqB,KAAK,MAAM,IAAI;;;CAGhD,CAAC;AAEF,MAAM,uBAAuB,cAAc;CACzC,MAAM;EACJ,MAAM;EACN,aACE;EACH;CACD,MAAM;EACJ,IAAI;GACF,MAAM;GACN,aAAa;GACb,UAAU;GACX;EACD,WAAW;GACT,MAAM;GACN,aAAa;GACd;EACD,MAAM;GAAE,MAAM;GAAW,aAAa;GAA+B,SAAS;GAAO;EACtF;CACD,MAAM,IAAI,EAAE,QAAQ;EAClB,MAAM,MAAM,MAAM,iBAAiB;GACjC,MAAM,KAAK;GACX,UAAU,KAAK;GACf,GAAI,KAAK,cAAc,KAAA,IAAY,EAAE,WAAW,KAAK,WAAW,GAAG,EAAE;GACtE,CAAC;AACF,MAAI,CAAC,IAAK;EACV,MAAM,QAAQ,MAAM,iBAAiB,KAAK,KAAK,KAAK;AACpD,MAAI,UAAU,KAAM;AACpB,qBAAmB,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;EAC5C,MAAM,EAAE,SAAS,gBAAgB;AAEjC,MAAI;GACF,MAAM,KAAK,MAAM,sBAAsB,aAAa,OAAO,QAAQ,QAAQ;AAC3E,OAAI,KAAK,MAAM;AACb,aAAS;KAAE,IAAI;KAAM;KAAa;KAAO,GAAG;KAAI,CAAC;AACjD,WAAO,eAAe,SAAS,GAAG;;AAEpC,WAAQ,OAAO,MAAM,OAAO,MAAM,OAAO,YAAY,MAAM;AAC3D,WAAQ,OAAO,MAAM,oBAAoB,GAAG,cAAc,IAAI;AAC9D,WAAQ,OAAO,MAAM,8BAA8B,GAAG,2BAA2B,OAAO,IAAI;AAC5F,WAAQ,OAAO,MAAM,0BAA0B,GAAG,uBAAuB,OAAO,IAAI;AACpF,UAAO,eAAe,SAAS,GAAG;WAC3B,KAAK;AACZ,UAAO,qBAAqB,KAAK,MAAM,IAAI;;;CAGhD,CAAC;AAmKF,MAAa,aAAa,cAAc;CACtC,MAAM;EACJ,MAAM;EACN,aAAa;EACd;CACD,aAAa;EACX,MAvKgB,cAAc;GAChC,MAAM;IACJ,MAAM;IACN,aACE;IAGH;GACD,MAAM;IACJ,KAAK;KACH,MAAM;KACN,aAAa;KACd;IACD,OAAO;KACL,MAAM;KACN,aAAa;KACb,SAAS;KACV;IACD,MAAM;KACJ,MAAM;KACN,aAAa;KACb,SAAS;KACV;IACF;GACD,MAAM,IAAI,EAAE,QAAQ;AAClB,UAAM,WAAW;KACf,GAAI,KAAK,QAAQ,KAAA,IAAY,EAAE,KAAK,KAAK,KAAe,GAAG,EAAE;KAC7D,OAAO,KAAK;KACZ,MAAM,KAAK;KACZ,CAAC;;GAEL,CAAC;EAyIE,IAAIJ;EACJ,MAAMC;EACN,QAAQC;EACR,SAAS;EACT,SAAS;EACT,SAAS;EACT,OAAO;EACP,SAAS;EACT,iBAAiB;EACjB,UAAU;EACV,QAAQ;EACR,WAAW;EACX,YAAYE;EACZ,kBAAkB;EAClB,UArJoB,cAAc;GACpC,MAAM;IACJ,MAAM;IACN,aACE;IAEH;GACD,MAAM;IACJ,WAAW;KACT,MAAM;KACN,aAAa;KACd;IACD,QAAQ;KACN,MAAM;KACN,aAAa;KACd;IACD,WAAW;KACT,MAAM;KACN,aAAa;KACb,SAAS;KACV;IACD,gBAAgB;KACd,MAAM;KACN,aACE;KACF,SAAS;KACV;IACD,MAAM;KAAE,MAAM;KAAW,aAAa;KAAyC,SAAS;KAAO;IAChG;GACD,MAAM,IAAI,EAAE,QAAQ;AAClB,UAAM,YAAY;KAChB,MAAM,KAAK;KACX,QAAQ,KAAK;KACb,aAAa,KAAK;KAClB,GAAI,KAAK,cAAc,KAAA,IAAY,EAAE,WAAW,KAAK,WAAW,GAAG,EAAE;KACrE,GAAI,KAAK,WAAW,KAAA,IAAY,EAAE,QAAQ,KAAK,QAAQ,GAAG,EAAE;KAC7D,CAAC;;GAEL,CAAC;EAgHE,QA/FkB,cAAc;GAClC,MAAM;IACJ,MAAM;IACN,aACE;IAEH;GACD,MAAM;IACJ,MAAM;KAAE,MAAM;KAAc,aAAa;KAAiC,UAAU;KAAM;IAC1F,KAAK;KACH,MAAM;KACN,aACE;KACH;IACD,iBAAiB;KACf,MAAM;KACN,aACE;KACH;IACD,MAAM;KAAE,MAAM;KAAU,aAAa;KAAkD;IACvF,kBAAkB;KAChB,MAAM;KACN,aAAa;KACb,SAAS;KACV;IACD,iBAAiB;KACf,MAAM;KACN,aAAa;KACd;IACD,SAAS;KACP,MAAM;KACN,aACE;KAEF,SAAS;KACV;IACD,SAAS;KACP,MAAM;KACN,aAAa;KACb,SAAS;KACV;IACD,WAAW;KACT,MAAM;KACN,aAAa;KACd;IACD,WAAW;KACT,MAAM;KACN,aAAa;KACb,SAAS;KACV;IACD,MAAM;KAAE,MAAM;KAAW,aAAa;KAA+B,SAAS;KAAO;IACtF;GACD,MAAM,IAAI,EAAE,QAAQ;AAClB,UAAM,UAAU;KACd,MAAM,OAAO,KAAK,SAAS,WAAW,KAAK,OAAO;KAClD,KAAK,OAAO,KAAK,QAAQ,WAAW,KAAK,MAAM,KAAA;KAC/C,MAAM,KAAK;KACX,QAAQ,KAAK;KACb,eAAe,KAAK;KACpB,SAAS,KAAK;KACd,SAAS,KAAK;KACd,GAAI,KAAK,qBAAqB,KAAA,IAC1B,EAAE,cAAc,KAAK,kBAA4B,GACjD,EAAE;KACN,GAAI,KAAK,SAAS,KAAA,IAAY,EAAE,MAAM,KAAK,MAAgB,GAAG,EAAE;KAChE,GAAI,KAAK,qBAAqB,KAAA,IAC1B,EAAE,cAAc,KAAK,kBAA4B,GACjD,EAAE;KACN,GAAI,KAAK,cAAc,KAAA,IAAY,EAAE,WAAW,KAAK,WAAqB,GAAG,EAAE;KAChF,CAAC;;GAEL,CAAC;EAyBC;CACF,CAAC;;;AC36GF,MAAa,qBAAqB;AASlC,IAAa,oCAAb,cAAuD,MAAM;CAC3D,YACE,UACA,MACA;AACA,QAAM,iDAAiD,SAAS,KAAK,OAAO;AAHnE,OAAA,WAAA;AACA,OAAA,OAAA;AAGT,OAAK,OAAO;;;AAIhB,IAAa,gCAAb,cAAmD,MAAM;CACvD,YACE,SACA,UAOA,gBACA;AACA,QACE,+BAA+B,QAAQ,iBAAiB,YAAY,OAAO,KAAK,iBACjF;AAZQ,OAAA,UAAA;AACA,OAAA,WAAA;AAOA,OAAA,iBAAA;AAKT,OAAK,OAAO;;;AAehB,eAAsB,WAAW,SAAiB,MAAmC;AACnF,QAAO,IAAI,SAAoB,SAAS,WAAW;EACjD,MAAM,QAAQ,MAAM,SAAS,CAAC,GAAG,KAAK,KAAK,EAAE,EAAE,OAAO;GAAC;GAAQ;GAAQ;GAAO,EAAE,CAAC;EACjF,IAAI,SAAS;EACb,IAAI,SAAS;AACb,QAAM,QAAQ,GAAG,SAAS,MAAc;AACtC,aAAU,EAAE,SAAS,OAAO;IAC5B;AACF,QAAM,QAAQ,GAAG,SAAS,MAAc;AACtC,aAAU,EAAE,SAAS,OAAO;IAC5B;AACF,QAAM,GAAG,UAAU,QAAQ,OAAO,IAAI,CAAC;AACvC,QAAM,GAAG,UAAU,SAAS;AAC1B,WAAQ;IAAE,UAAU;IAAM;IAAQ;IAAQ,CAAC;IAC3C;AACF,MAAI,KAAK,UAAU,KAAA,EACjB,OAAM,OAAO,MAAM,KAAK,MAAM;AAEhC,QAAM,OAAO,KAAK;GAClB;;AAGJ,SAAgB,kBAAkB,KAAuB;AACvD,QAAQ,IAA8B,SAAS;;AAGjD,SAAgBC,uBAAqB,GAAmB;AACtD,QAAO,EAAE,QAAQ,UAAU,GAAG;;AAOhC,SAAgB,aAAa,QAAwB;CACnD,MAAM,UAAU,OAAO,MAAM;AAC7B,KAAI,QAAQ,WAAW,EAAG,QAAO;AACjC,KAAI,QAAQ,SAAS,IAAK,QAAO,GAAG,QAAQ,MAAM,GAAG,IAAI,CAAC;AAC1D,QAAO;;;;AChFT,MAAMC,sBACJ;AACF,MAAMC,uBACJ;AAEF,MAAa,gBAAmC;CAC9C,MAAM;CACN,MAAM,IAAI,SAAS;EACjB,IAAI;AACJ,MAAI;AACF,YAAS,MAAM,WAAW,eAAe,EACvC,MAAM;IAAC;IAAU;IAAW;IAAoB;IAAW;IAAQ,EACpE,CAAC;WACK,KAAK;AACZ,OAAI,kBAAkB,IAAI,CACxB,OAAM,IAAI,kCAAkC,SAASD,oBAAkB;AAEzE,SAAM;;AAMR,MAAI,OAAO,aAAa,GAAG;AACzB,OAAI,OAAO,OAAO,WAAW,EAAG,QAAO;AACvC,SAAM,IAAI,8BACR,sBACA,OAAO,UACP,aAAa,OAAO,OAAO,CAC5B;;AAEH,MAAI,OAAO,OAAO,WAAW,EAAG,QAAO;EACvC,MAAM,WAAWE,uBAAqB,OAAO,OAAO;AACpD,SAAO,SAAS,SAAS,IAAI,WAAW;;CAE1C,MAAM,IAAI,SAAS,UAAU;EAC3B,IAAI;AACJ,MAAI;AAEF,YAAS,MAAM,WAAW,eAAe;IACvC,MAAM;KACJ;KACA;KACA;KACA;KACA;KACA;KACA;KACD;IACD,OAAO;IACR,CAAC;WACK,KAAK;AACZ,OAAI,kBAAkB,IAAI,CACxB,OAAM,IAAI,kCAAkC,SAASD,qBAAmB;AAE1E,SAAM;;AAER,MAAI,OAAO,aAAa,EACtB,OAAM,IAAI,8BACR,qBACA,OAAO,UACP,aAAa,OAAO,OAAO,CAC5B;;CAGL,MAAM,MAAM,SAAS;EACnB,IAAI;AACJ,MAAI;AACF,YAAS,MAAM,WAAW,eAAe,EACvC,MAAM;IAAC;IAAS;IAAW;IAAoB;IAAW;IAAQ,EACnE,CAAC;WACK,KAAK;AACZ,OAAI,kBAAkB,IAAI,CACxB,OAAM,IAAI,kCAAkC,SAAS,+BAA+B;AAEtF,SAAM;;AAMR,MAAI,OAAO,aAAa,EACtB,OAAM,IAAI,8BACR,qBACA,OAAO,UACP,aAAa,OAAO,OAAO,CAC5B;AAEH,SAAO,EAAE,SAAS,MAAM;;CAE3B;;;AC1FD,MAAM,eAAe;AAErB,MAAa,gBAAmC;CAC9C,MAAM;CACN,MAAM,IAAI,SAAS;EACjB,IAAI;AACJ,MAAI;AACF,YAAS,MAAM,WAAW,YAAY,EACpC,MAAM;IAAC;IAAyB;IAAM;IAAoB;IAAM;IAAS;IAAK,EAC/E,CAAC;WACK,KAAK;AACZ,OAAI,kBAAkB,IAAI,CACxB,OAAM,IAAI,kCAAkC,UAAU,aAAa;AAErE,SAAM;;AAER,MAAI,OAAO,aAAa,GAAI,QAAO;AACnC,MAAI,OAAO,aAAa,EAAG,QAAO;EAClC,MAAM,WAAWE,uBAAqB,OAAO,OAAO;AACpD,SAAO,SAAS,SAAS,IAAI,WAAW;;CAE1C,MAAM,IAAI,SAAS,UAAU;EAC3B,IAAI;AACJ,MAAI;AAWF,YAAS,MAAM,WAAW,YAAY,EACpC,MAAM;IACJ;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACD,EACF,CAAC;WACK,KAAK;AACZ,OAAI,kBAAkB,IAAI,CACxB,OAAM,IAAI,kCAAkC,UAAU,aAAa;AAErE,SAAM;;AAER,MAAI,OAAO,aAAa,EACtB,OAAM,IAAI,8BACR,iCACA,OAAO,UACP,aAAa,OAAO,OAAO,CAC5B;;CAGL,MAAM,MAAM,SAAS;EACnB,IAAI;AACJ,MAAI;AACF,YAAS,MAAM,WAAW,YAAY,EACpC,MAAM;IAAC;IAA2B;IAAM;IAAoB;IAAM;IAAQ,EAC3E,CAAC;WACK,KAAK;AACZ,OAAI,kBAAkB,IAAI,CACxB,OAAM,IAAI,kCAAkC,UAAU,aAAa;AAErE,SAAM;;AAER,MAAI,OAAO,aAAa,GAAI,QAAO,EAAE,SAAS,OAAO;AACrD,MAAI,OAAO,aAAa,EAAG,QAAO,EAAE,SAAS,MAAM;AACnD,QAAM,IAAI,8BACR,oCACA,OAAO,UACP,aAAa,OAAO,OAAO,CAC5B;;CAEJ;;;AC7ED,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkClB,MAAM,oBACJ;AACF,MAAM,qBAAqB;AAE3B,SAAS,WAAW,SAAyB;AAC3C,QAAO,GAAG,mBAAmB,GAAG;;AAGlC,SAAS,eAAe,QAAmC;AACzD,QAAO;EAAC;EAAc;EAAmB;EAAY;EAAO;;AAG9D,SAAS,mBAAmB,GAAmB;AAC7C,QAAO,EAAE,QAAQ,MAAM,KAAK;;AAG9B,eAAe,cAAc,QAAoC;AAC/D,KAAI;AACF,SAAO,MAAM,WAAW,kBAAkB,EAAE,MAAM,eAAe,OAAO,EAAE,CAAC;UACpE,KAAK;AACZ,MAAI,kBAAkB,IAAI,CACxB,OAAM,IAAI,kCAAkC,SAAS,kBAAkB;AAEzE,QAAM;;;AAIV,MAAa,kBAAqC;CAChD,MAAM;CACN,MAAM,IAAI,SAAS;EAejB,MAAM,SAAS,MAAM,cAbN;EACjB,UAAU;aACC,mBAHM,WAAW,QAAQ,CAGC,CAAC;;;;;;;;;;EAWM;AAC1C,MAAI,OAAO,aAAa,EACtB,OAAM,IAAI,8BACR,uBACA,OAAO,UACP,aAAa,OAAO,OAAO,CAC5B;AAEH,SAAO,OAAO,OAAO,SAAS,IAAI,OAAO,SAAS;;CAEpD,MAAM,IAAI,SAAS,UAAU;EAC3B,MAAM,SAAS,WAAW,QAAQ;EAIlC,MAAM,cAAc,OAAO,KAAK,UAAU,OAAO,CAAC,SAAS,MAAM;EACjE,MAAM,SAAS;EACjB,UAAU;aACC,mBAAmB,OAAO,CAAC;WAC7B,mBAAmB,QAAQ,CAAC;YAC3B,YAAY;;;;;;;;;;;;;;;;;;;;;;;EAuBpB,IAAI;AACJ,MAAI;AACF,YAAS,MAAM,cAAc,OAAO;WAC7B,KAAK;AACZ,OAAI,eAAe,kCACjB,OAAM,IAAI,kCAAkC,SAAS,mBAAmB;AAE1E,SAAM;;AAER,MAAI,OAAO,aAAa,EACtB,OAAM,IAAI,8BACR,wBACA,OAAO,UACP,aAAa,OAAO,OAAO,CAC5B;;CAGL,MAAM,MAAM,SAAS;EAEnB,MAAM,SAAS;EACjB,UAAU;aACC,mBAHM,WAAW,QAAQ,CAGC,CAAC;;;;EAIpC,IAAI;AACJ,MAAI;AACF,YAAS,MAAM,cAAc,OAAO;WAC7B,KAAK;AACZ,OAAI,eAAe,kCACjB,OAAM,IAAI,kCAAkC,SAAS,mBAAmB;AAE1E,SAAM;;AAER,MAAI,OAAO,aAAa,EACtB,OAAM,IAAI,8BACR,yBACA,OAAO,UACP,aAAa,OAAO,OAAO,CAC5B;AAEH,SAAO,EAAE,SAAS,OAAO,OAAO,SAAS,UAAU,EAAE;;CAExD;;;ACvHD,SAAgB,eAAe,OAA8B,EAAE,EAAqB;AAClF,KAAI,KAAK,SAAU,QAAO,KAAK;CAC/B,MAAM,WAAW,KAAK,YAAY,QAAQ;AAC1C,SAAQ,UAAR;EACE,KAAK,SACH,QAAO;EACT,KAAK,QACH,QAAO;EACT,KAAK,QACH,QAAO;EACT,QACE,OAAM,IAAI,kCACR,UACA,4DACD;;;AAWP,eAAe,gBAA2C;CACxD,IAAI;AACJ,KAAI;AACF,QAAM,MAAM,SAAS,mBAAmB,EAAE,OAAO;UAC1C,KAAK;AACZ,MAAK,IAA8B,SAAS,SAAU,QAAO;AAC7D,QAAM;;AAER,KAAI;EACF,MAAM,SAAS,KAAK,MAAM,IAAI;AAC9B,MAAI,OAAO,kBAAkB,EAAG,QAAO;AACvC,MAAI,OAAO,OAAO,gBAAgB,YAAY,OAAO,YAAY,WAAW,EAAG,QAAO;AACtF,SAAO;GAAE,eAAe;GAAG,aAAa,OAAO;GAAa;SACtD;AACN,SAAO;;;AAIX,eAAe,eAAe,OAAiC;CAC7D,MAAM,OAAO,mBAAmB;AAChC,OAAM,MAAM,QAAQ,KAAK,EAAE;EAAE,WAAW;EAAM,MAAM;EAAO,CAAC;AAC5D,OAAM,UAAU,MAAM,KAAK,UAAU,OAAO,MAAM,EAAE,EAAE,EAAE,MAAM,KAAO,CAAC;AACtE,KAAI;AACF,QAAM,MAAM,MAAM,IAAM;SAClB;;AAKV,eAAe,iBAAgD;AAC7D,KAAI;AACF,QAAM,OAAO,mBAAmB,CAAC;AACjC,SAAO,EAAE,SAAS,MAAM;UACjB,KAAK;AACZ,MAAK,IAA8B,SAAS,SAAU,QAAO,EAAE,SAAS,OAAO;AAC/E,QAAM;;;;;;;;;;;;;;;;AAuBV,eAAsB,gBACpB,OAA+B,EAAE,EACE;CACnC,MAAM,MAAM,KAAK,OAAO,QAAQ;CAChC,MAAM,WAAW,IAAI;CACrB,MAAM,cAAc,IAAI;AACxB,KAAI,YAAY,YACd,QAAO;EAAE,MAAM;EAAO,OAAO;EAAU,UAAU;EAAa;CAEhE,MAAM,QAAQ,MAAM,eAAe;AACnC,KAAI,CAAC,MAAO,QAAO;CACnB,MAAM,WAAW,MAAM,eAAe,KAAK,CAAC,IAAI,MAAM,YAAY;AAClE,KAAI,aAAa,KAGf,QAAO;AAET,QAAO;EAAE,MAAM;EAAY,OAAO,MAAM;EAAa;EAAU;;;;;;;;AAWjE,eAAsB,gBACpB,OACA,UACA,OAA8B,EAAE,EACY;AAC5C,KAAI,CAAC,MAAO,OAAM,IAAI,MAAM,oBAAoB;AAChD,KAAI,CAAC,SAAU,OAAM,IAAI,MAAM,uBAAuB;CAEtD,MAAM,UAAU,eAAe,KAAK;CACpC,MAAM,gBAAgB,MAAM,eAAe;CAE3C,IAAI;AACJ,KAAI,iBAAiB,cAAc,gBAAgB,OAAO;AAExD,MADiB,MAAM,QAAQ,IAAI,MAAM,KACxB,SAEf,QAAO,EAAE,QAAQ,aAAa;AAEhC,WAAS;OAET,UAAS,gBAAgB,YAAY;AAGvC,OAAM,QAAQ,IAAI,OAAO,SAAS;AAGlC,KAAI,iBAAiB,cAAc,gBAAgB,MACjD,KAAI;AACF,QAAM,QAAQ,MAAM,cAAc,YAAY;SACxC;AAIV,OAAM,eAAe;EAAE,eAAe;EAAG,aAAa;EAAO,CAAC;AAC9D,QAAO,EAAE,QAAQ;;;;;;;;;;;;;AAcnB,eAAsB,yBACpB,OAA6C,EAAE,EACc;CAC7D,MAAM,MAAM,KAAK,OAAO,QAAQ;AAChC,KAAI,IAAI,eAAe,IAAI,eACzB,QAAO;EAAE,MAAM;EAAO,OAAO,IAAI;EAAa;CAEhD,MAAM,QAAQ,MAAM,eAAe;AACnC,KAAI,CAAC,MAAO,QAAO;AACnB,QAAO;EAAE,MAAM;EAAY,OAAO,MAAM;EAAa;;;;;;AAOvD,eAAsB,kBACpB,OAA8B,EAAE,EACD;CAC/B,MAAM,QAAQ,MAAM,eAAe;CACnC,IAAI,iBAAiB;AACrB,KAAI,MACF,KAAI;AAEF,oBADe,MAAM,eAAe,KAAK,CAAC,MAAM,MAAM,YAAY,EAC1C;UACjB,KAAK;AACZ,MAAI,eAAe,mCAAmC,OAGpD,OAAM;;CAIZ,MAAM,cAAc,MAAM,gBAAgB;AAC1C,QAAO,EAAE,SAAS,kBAAkB,YAAY,SAAS;;;;AClP3D,MAAa,sBAAsB;AAEnC,MAAa,wBACX;AAEF,MAAa,uBAAuB,GAAG,sBAAsB;;;AC8B7D,eAAsB,cACpB,MACA,OAAuB,EAAE,EACV;CACf,MAAM,UAAU,OAAO,KAAK,eAAe,cAAc;AAEzD,KAAI,CAAC,SAAS;AACZ,MAAI,KAAK,KACP,UAAS;GAAE,IAAI;GAAM,eAAe;GAAO,CAAC;MAE5C,SAAQ,OAAO,MAAM,yDAAyD;AAEhF,SAAO,eAAe,SAAS,iBAAiB;;CAGlD,MAAM,WAAW,KAAK,UAAU,QAAQ;AAMxC,KAAI,KAAK,MAAM;EACb,MAAM,UACJ,KAAK,WAAW,QACZ,iBAAiB,OAAO,KAAK,UAAU,OAAO,CAAC,SAAS,SAAS,KACjE;AACN,WAAS;GACP,IAAI;GACJ,QAAQ,KAAK;GACb;GACA,SAAS;GACT,gBAAgB;GACjB,CAAC;AACF,SAAO,eAAe,SAAS,GAAG;;CAIpC,MAAM,cAAc,KAAK,eAAe,QAAQ,QAAQ,OAAO,MAAM;AAErE,KAAI,KAAK,WAAW,OAAO;EACzB,MAAM,SAAS,OAAO,KAAK,UAAU,OAAO,CAAC,SAAS,SAAS;AAG/D,UAAQ,OAAO,MAAM,iBAAiB,OAAO,IAAI;OAIjD,SAAQ,OAAO,MAAM,GAAG,KAAK,UAAU,SAAS,MAAM,EAAE,CAAC,IAAI;AAG/D,KAAI,CAAC,KAAK,OAAO;AACf,MAAI,YACF,SAAQ,OAAO,MACb,+GACD;AAEH,UAAQ,OAAO,MAAM,YAAY,sBAAsB,IAAI;AAC3D,UAAQ,OAAO,MAAM,0DAA0D;;AAGjF,QAAO,eAAe,SAAS,GAAG;;AAGpC,MAAa,oBAAoB,cAAc;CAC7C,MAAM;EACJ,MAAM;EACN,aACE;EACH;CACD,MAAM;EACJ,MAAM;GACJ,MAAM;GACN,aAAa;GACb,SAAS;GACV;EACD,QAAQ;GACN,MAAM;GACN,aAAa;GACb,SAAS;GACV;EACD,OAAO;GACL,MAAM;GACN,aAAa;GACb,SAAS;GACV;EACF;CACD,MAAM,IAAI,EAAE,QAAQ;EAClB,MAAM,SAAS,YAAY,KAAK,OAAO;AACvC,MAAI,WAAW,MAAM;AAInB,OAAI,KAAK,KACP,UAAS;IACP,IAAI;IACJ,QAAQ;IACR,SAAS,qBAAqB,OAAO,KAAK,OAAO,CAAC;IACnD,CAAC;OAEF,SAAQ,OAAO,MACb,qBAAqB,OAAO,KAAK,OAAO,CAAC,gCAC1C;AAEH,UAAO,eAAe,SAAS,MAAM;;AAEvC,SAAO,cAAc;GAAE,MAAM,KAAK;GAAM;GAAQ,OAAO,KAAK;GAAO,CAAC;;CAEvE,CAAC;AAEF,SAAS,YAAY,KAAuC;AAC1D,KAAI,QAAQ,SAAS,QAAQ,OAAQ,QAAO;AAC5C,QAAO;;;;ACtGT,eAAsB,cACpB,MACA,OAAuB,EAAE,EACV;CACf,MAAM,MAAM,KAAK,OAAO,QAAQ;CAMhC,IAAI;AACJ,KAAI,KAAK,SAAS;EAChB,MAAM,WAAW,IAAI;AACrB,MAAI,CAAC,UAAU;AACb,eAAY,KAAK,MAAM,eAAe,6CAA6C;AACnF,UAAO,eAAe,SAAS,MAAM;;AAEvC,QAAM;QACD;AAEL,MADmB,KAAK,cAAc,QAAQ,QAAQ,MAAM,MAAM,EAClD;AACd,eACE,KAAK,MACL,YACA,qEACD;AACD,UAAO,eAAe,SAAS,MAAM;;AAEvC,QAAM,OAAO,KAAK,aAAa,oBAAoB;AACnD,MAAI,IAAI,MAAM,CAAC,WAAW,GAAG;AAC3B,eAAY,KAAK,MAAM,YAAY,mBAAmB;AACtD,UAAO,eAAe,SAAS,MAAM;;;CAOzC,MAAM,UAAU,kBAAkB,IAAI;AACtC,KAAI,YAAY,MAAM;AACpB,cAAY,KAAK,MAAM,gBAAgB,sDAAsD;AAC7F,SAAO,eAAe,SAAS,MAAM;;CAEvC,MAAM,SAAS,oBAAoB,QAAQ;AAC3C,KAAI,QAAQ;AACV,cAAY,KAAK,MAAM,gBAAgB,OAAO;AAC9C,SAAO,eAAe,SAAS,MAAM;;CAGvC,MAAM,UAAU,0BAA0B,QAAQ;CAMlD,MAAM,WADW,OAAO,KAAK,uBAAuB,cAAc,CAAC,YAAY,KAAK,KACtD;AAE9B,KAAI,KAAK,QAAQ;AACf,MAAI,KAAK,KACP,UAAS;GACP,IAAI;GACJ,QAAQ;GACR;GACA,MAAM,QAAQ;GACd,SAAS;GACT,gBAAgB;GACjB,CAAC;OACG;GACL,MAAM,UAAU,QAAQ,QAAQ;AAChC,WAAQ,OAAO,MACb,2BAA2B,QAAQ,KAAK,MAAM,IAAI,QAAQ,8BAC3D;AACD,WAAQ,OAAO,MAAM,YAAY,sBAAsB,IAAI;;AAE7D,SAAO,eAAe,SAAS,GAAG;;AAUpC,KAAI;AACF,SAAO,KAAK,gBAAgB,cAAc,SAAS,EAAE,YAAY,MAAM,CAAC;UACjE,KAAK;EACZ,MAAM,UAAW,IAAc;AAC/B,MAAI,KAAK,KACP,UAAS;GAAE,IAAI;GAAO,QAAQ;GAAgB;GAAS,CAAC;MAExD,SAAQ,OAAO,MAAM,iCAAiC,QAAQ,IAAI;AAEpE,SAAO,eAAe,SAAS,QAAQ;;AAGzC,KAAI,KAAK,KACP,UAAS;EACP,IAAI;EACJ;EACA,MAAM,QAAQ;EACd,SAAS;EACT,gBAAgB;EACjB,CAAC;MACG;EACL,MAAM,OAAO,2BAA2B;EACxC,MAAM,UAAU,QAAQ,QAAQ;EAChC,MAAM,OAAO,WAAW,aAAa;AACrC,UAAQ,OAAO,MACb,QAAQ,KAAK,cAAc,KAAK,IAAI,QAAQ,KAAK,MAAM,IAAI,QAAQ,aACpE;AACD,UAAQ,OAAO,MAAM,YAAY,sBAAsB,IAAI;;AAE7D,QAAO,eAAe,SAAS,GAAG;;AAGpC,SAAS,YAAY,MAAe,QAAgB,QAAsB;AACxE,KAAI,KACF,UAAS;EACP,IAAI;EACJ;EACA,GAAI,WAAW,iBAAiB,EAAE,QAAQ,GAAG,EAAE,SAAS,QAAQ;EACjE,CAAC;KAEF,SAAQ,OAAO,MAAM,GAAG,OAAO,IAAI,OAAO,IAAI;;AAIlD,eAAe,oBAAqC;CAElD,MAAM,SAAmB,EAAE;AAC3B,YAAW,MAAM,SAAS,QAAQ,MAChC,QAAO,KAAK,OAAO,UAAU,WAAW,OAAO,KAAK,MAAM,GAAG,MAAM;AAErE,QAAO,OAAO,OAAO,OAAO,CAAC,SAAS,OAAO;;AAG/C,MAAa,oBAAoB,cAAc;CAC7C,MAAM;EACJ,MAAM;EACN,aACE;EACH;CACD,MAAM;EACJ,MAAM;GACJ,MAAM;GACN,aAAa;GACb,SAAS;GACV;EACD,YAAY;GACV,MAAM;GACN,aAAa;GACb,SAAS;GACV;EACD,WAAW;GACT,MAAM;GACN,aAAa;GACb,SAAS;GACV;EACF;CACD,MAAM,IAAI,EAAE,QAAQ;AAClB,SAAO,cAAc;GACnB,MAAM,KAAK;GACX,SAAS,QAAQ,KAAK,YAAY;GAClC,QAAQ,QAAQ,KAAK,WAAW;GACjC,CAAC;;CAEL,CAAC;;;AC5KF,SAAS,gBAAgB,aAA2B;AAClD,SAAQ,OAAO,MACb,mEAAmE,YAAY,IAChF;;AAgBH,eAAsB,WAAW,MAAmB,OAAiB,EAAE,EAAiB;AACtF,iBAAgB,+DAA+D;CAC/E,MAAM,MAAM,KAAK,OAAO,QAAQ;CAKhC,IAAI,QAAQ,KAAK,OAAO,MAAM;CAC9B,IAAIC,aAAW,KAAK;CACpB,MAAM,mBAAmBA,eAAa,KAAA;AAEtC,KAAI,CAAC,SAAS,IAAI,YAAa,SAAQ,IAAI;AAC3C,KAAIA,eAAa,KAAA,KAAa,IAAI,eAAgB,cAAW,IAAI;AAIjE,KAAI,iBACF,SAAQ,OAAO,MACb,kIAED;CAGH,MAAM,cAAc,QAAQ,OAAO,SAAS,QAAQ,MAAM,SAAS,CAAC,KAAK;AAEzE,KAAI,CAAC,OAAO;AACV,MAAI,CAAC,aAAa;AAChB,2BAAwB,KAAK,MAAM,QAAQ;AAC3C,UAAO,eAAe,SAAS,MAAM;;AAEvC,MAAI;AACF,YACE,MAAM,MAAM;IACV,SAAS;IACT,WAAW,QAAS,IAAI,MAAM,CAAC,SAAS,IAAI,OAAO;IACpD,CAAC,EACF,MAAM;WACD,KAAK;AACZ,OAAIC,oBAAkB,IAAI,EAAE;AAC1B,YAAQ,OAAO,MAAM,aAAa;AAClC,WAAO,eAAe,SAAS,MAAM;;AAEvC,SAAM;;;AAIV,KAAI,CAAC,MAAM,SAAS,IAAI,EAAE;AAGxB,MAAI,KAAK,KAAM,UAAS;GAAE,IAAI;GAAO,QAAQ;GAAiB,SAAS;GAAiB,CAAC;MACpF,SAAQ,OAAO,MAAM,kBAAkB,MAAM,IAAI;AACtD,SAAO,eAAe,SAAS,MAAM;;AAGvC,KAAID,eAAa,KAAA,GAAW;AAC1B,MAAI,CAAC,aAAa;AAChB,2BAAwB,KAAK,MAAM,WAAW;AAC9C,UAAO,eAAe,SAAS,MAAM;;AAEvC,MAAI;AACF,gBAAW,MAAME,SAAe;IAC9B,SAAS;IACT,MAAM;IACN,WAAW,QAAS,IAAI,SAAS,IAAI,OAAO;IAC7C,CAAC;WACK,KAAK;AACZ,OAAID,oBAAkB,IAAI,EAAE;AAC1B,YAAQ,OAAO,MAAM,aAAa;AAClC,WAAO,eAAe,SAAS,MAAM;;AAEvC,SAAM;;;AAIV,KAAID,WAAS,WAAW,GAAG;AACzB,MAAI,KAAK,KACP,UAAS;GAAE,IAAI;GAAO,QAAQ;GAAoB,SAAS;GAAqB,CAAC;MAC9E,SAAQ,OAAO,MAAM,uBAAuB;AACjD,SAAO,eAAe,SAAS,MAAM;;CAGvC,IAAI;AACJ,KAAI;AACF,WAAS,MAAM,gBAAgB,OAAOA,YAAU,KAAK,UAAU,EAAE,UAAU,KAAK,SAAS,GAAG,EAAE,CAAC;UACxF,KAAK;EACZ,MAAM,UAAW,IAAc;AAC/B,MAAI,KAAK,KAAM,UAAS;GAAE,IAAI;GAAO,QAAQ;GAAkB;GAAS,CAAC;MACpE,SAAQ,OAAO,MAAM,+BAA+B,QAAQ,IAAI;AACrE,SAAO,eAAe,SAAS,QAAQ;;AAGzC,KAAI,KAAK,KACP,UAAS;EAAE,IAAI;EAAM,QAAQ,OAAO;EAAQ;EAAO,CAAC;UAC3C,OAAO,WAAW,YAC3B,SAAQ,OAAO,MAAM,2CAA2C;KAEhE,SAAQ,OAAO,MAAM,yBAAyB,MAAM,gBAAgB;AAEtE,QAAO,eAAe,SAAS,GAAG;;AAUpC,eAAsB,aAAa,MAAqB,OAAiB,EAAE,EAAiB;AAC1F,iBAAgB,+EAA+E;CAC/F,MAAM,cAAc,QAAQ,OAAO,SAAS,QAAQ,MAAM,SAAS,CAAC,KAAK;CAKzE,MAAM,SAAS,MAAM,yBAAyB,KAAK,MAAM,EAAE,KAAK,KAAK,KAAK,GAAG,EAAE,CAAC,CAAC,YACzE,KACP;AAED,KAAI,CAAC,KAAK,KAAK;AACb,MAAI,CAAC,aAAa;AAKhB,OAAI,KAAK,KACP,UAAS;IACP,IAAI;IACJ,QAAQ;IACR,SAAS;IACV,CAAC;OAEF,SAAQ,OAAO,MACb,+EACD;AAEH,UAAO,eAAe,SAAS,MAAM;;EAEvC,MAAM,QAAQ,QAAQ,SAAS;EAC/B,IAAI;AACJ,MAAI;AACF,eAAY,MAAM,QAAQ;IACxB,SAAS,gCAAgC,MAAM;IAC/C,SAAS;IACV,CAAC;WACK,KAAK;AACZ,OAAIC,oBAAkB,IAAI,EAAE;AAC1B,YAAQ,OAAO,MAAM,aAAa;AAClC,WAAO,eAAe,SAAS,MAAM;;AAEvC,SAAM;;AAER,MAAI,CAAC,WAAW;AAGd,OAAI,KAAK,KAAM,UAAS;IAAE,IAAI;IAAM,QAAQ;IAAa,CAAC;OACrD,SAAQ,OAAO,MAAM,aAAa;AACvC,UAAO,eAAe,SAAS,GAAG;;;CAItC,IAAI;AACJ,KAAI;AACF,WAAS,MAAM,kBAAkB,KAAK,UAAU,EAAE,UAAU,KAAK,SAAS,GAAG,EAAE,CAAC;UACzE,KAAK;EACZ,MAAM,UAAW,IAAc;AAC/B,MAAI,KAAK,KAAM,UAAS;GAAE,IAAI;GAAO,QAAQ;GAAkB;GAAS,CAAC;MACpE,SAAQ,OAAO,MAAM,gCAAgC,QAAQ,IAAI;AACtE,SAAO,eAAe,SAAS,QAAQ;;CAGzC,MAAM,SAAS,OAAO,UAAU,YAAY;AAC5C,KAAI,KAAK,KACP,UAAS;EAAE,IAAI;EAAM;EAAQ,CAAC;UACrB,OAAO,QAChB,SAAQ,OAAO,MAAM,yBAAyB;KAE9C,SAAQ,OAAO,MAAM,0BAA0B;AAEjD,QAAO,eAAe,SAAS,GAAG;;AASpC,eAAsB,cAAc,MAAsB,OAAiB,EAAE,EAAiB;AAC5F,iBAAgB,sDAAsD;CAKtE,MAAM,SAAS,MAAM,yBAAyB,KAAK,MAAM,EAAE,KAAK,KAAK,KAAK,GAAG,EAAE,CAAC,CAAC,YACzE,KACP;CACD,MAAM,UAAU,MAAM,aAAa;AAEnC,KAAI,KAAK,MAAM;AAWb,WAAS;GAAE,IAAI;GAAM,aAVD,SAChB;IAAE,QAAQ;IAAe,OAAO,OAAO;IAAO,QAAQ,OAAO;IAAM,GACnE,EAAE,QAAQ,OAAgB;GAQI,SAPb,UACjB;IACE,QAAQ;IACR,MAAM,QAAQ;IACd,YAAY,QAAQ;IACrB,GACD,EAAE,QAAQ,OAAgB;GAC2B,CAAC;AAC1D,SAAO,eAAe,SAAS,GAAG;;AAGpC,KAAI,QAAQ;EACV,MAAM,cAAc,OAAO,SAAS,QAAQ,uCAAuC;AACnF,UAAQ,OAAO,MAAM,UAAU,OAAO,MAAM,IAAI;AAChD,UAAQ,OAAO,MAAM,WAAW,YAAY,IAAI;OAEhD,SAAQ,OAAO,MAAM,4BAA4B;AAEnD,KAAI,SAAS;EACX,MAAM,QAAQ,QAAQ,KAAK,cACvB,GAAG,QAAQ,KAAK,YAAY,IAAI,QAAQ,KAAK,MAAM,KACnD,QAAQ,KAAK;AACjB,UAAQ,OAAO,MAAM,qBAAqB,MAAM,IAAI;AACpD,UAAQ,OAAO,MAAM,aAAa,QAAQ,WAAW,IAAI;OAEzD,SAAQ,OAAO,MAAM,sCAAsC;AAE7D,QAAO,eAAe,SAAS,GAAG;;AAKpC,SAAS,wBAAwB,MAAe,SAAqC;AACnF,KAAI,KACF,UAAS;EACP,IAAI;EACJ,QAAQ;EACR,SAAS,GAAG,QAAQ,gCAAgC,QAAQ,YAAY,QAAQ,aAAa;EAC9F,CAAC;KAEF,SAAQ,OAAO,MACb,qBAAqB,QAAQ,kCAClB,QAAQ,gBAAgB,QAAQ,aAAa,CAAC,KAC1D;;AAQL,SAASA,oBAAkB,KAAuB;AAChD,QAAO,eAAe,SAAS,IAAI,SAAS;;AA2D9C,MAAa,cAAc,cAAc;CACvC,MAAM;EACJ,MAAM;EACN,aAAa;EACd;CACD,aAAa;EACX,KA5De,cAAc;GAC/B,MAAM;IACJ,MAAM;IACN,aAAa;IACd;GACD,MAAM;IACJ,MAAM;KAAE,MAAM;KAAW,aAAa;KAAyC,SAAS;KAAO;IAC/F,OAAO;KAAE,MAAM;KAAU,aAAa;KAAwB;IAC9D,UAAU;KACR,MAAM;KACN,aAAa;KACd;IACF;GACD,MAAM,IAAI,EAAE,QAAQ;AAClB,WAAO,WAAW;KAChB,MAAM,KAAK;KACX,OAAO,OAAO,KAAK,UAAU,WAAW,KAAK,QAAQ,KAAA;KACrD,UAAU,OAAO,KAAK,aAAa,WAAW,KAAK,WAAW,KAAA;KAC/D,CAAC;;GAEL,CAAC;EAyCE,OAvCiB,cAAc;GACjC,MAAM;IACJ,MAAM;IACN,aAAa;IACd;GACD,MAAM;IACJ,MAAM;KAAE,MAAM;KAAW,aAAa;KAAyC,SAAS;KAAO;IAC/F,KAAK;KACH,MAAM;KACN,OAAO;KACP,aAAa;KACb,SAAS;KACV;IACF;GACD,MAAM,IAAI,EAAE,QAAQ;AAClB,WAAO,aAAa;KAAE,MAAM,KAAK;KAAM,KAAK,KAAK;KAAK,CAAC;;GAE1D,CAAC;EAuBE,QArBkB,cAAc;GAClC,MAAM;IACJ,MAAM;IACN,aAAa;IACd;GACD,MAAM,EACJ,MAAM;IAAE,MAAM;IAAW,aAAa;IAAyC,SAAS;IAAO,EAChG;GACD,MAAM,IAAI,EAAE,QAAQ;AAClB,WAAO,cAAc,EAAE,MAAM,KAAK,MAAM,CAAC;;GAE5C,CAAC;EAWE,QAAQ;EACR,QAAQ;EACT;CACF,CAAC;;;AClXF,MAAM,YAA+B;CACnC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;AAGD,MAAM,eAAkD;CACtD,WAAW;EAAC;EAAM;EAAW;EAAY;EAAQ;EAAS;EAAM;CAChE,KAAK;EACH;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD;CAKD,SAAS;EAAC;EAAc;EAAM;EAAO;CACrC,IAAI,CAAC,QAAQ;CACb,MAAM;EAAC;EAAO;EAAS;EAAS;CAChC,YAAY;EAAC;EAAQ;EAAO;EAAO;CACpC;AAED,SAAS,WAAmB;CAC1B,MAAM,MAAM,UAAU,KAAK,IAAI;CAI/B,MAAM,QAAkB,EAAE;AAC1B,MAAK,MAAM,CAAC,IAAI,SAAS,OAAO,QAAQ,aAAa,EAAE;AACrD,QAAM,KAAK,OAAO,GAAG,GAAG;AACxB,QAAM,KAAK,mCAAmC,KAAK,KAAK,IAAI,CAAC,gBAAgB;AAC7E,QAAM,KAAK,oBAAoB;;AAEjC,QAAO;;;;;;;;;;;;;gCAauB,IAAI;;;;;EAKlC,MAAM,KAAK,KAAK,CAAC;;;;;;;;;AAUnB,SAAS,UAAkB;CAGzB,MAAM,MAAM,UAAU,KAAK,IAAI;CAC/B,MAAM,YAAsB,EAAE;AAC9B,MAAK,MAAM,CAAC,IAAI,SAAS,OAAO,QAAQ,aAAa,CACnD,WAAU,KAAK,SAAS,GAAG,yBAAyB,KAAK,KAAK,MAAM,IAAI,EAAE,GAAG,CAAC,KAAK,IAAI,CAAC,KAAK;AAE/F,QAAO;;;;;;;;cAQK,UAAU,KAAK,MAAM,IAAI,EAAE,GAAG,CAAC,KAAK,IAAI,CAAC;;;wBAG/B,UAAU,KAAK,MAAM,IAAI,EAAE,GAAG,CAAC,KAAK,IAAI,CAAC;;;;;EAK/D,UAAU,KAAK,KAAK,CAAC;;;;;IAKnB,IAAI;;;AAIR,SAAS,WAAmB;CAC1B,MAAM,QAAkB;EACtB;EACA;EACA;EACA;EACD;AAED,MAAK,MAAM,KAAK,UACd,OAAM,KAAK,oDAAoD,EAAE,MAAM;AAEzE,MAAK,MAAM,CAAC,IAAI,SAAS,OAAO,QAAQ,aAAa,CACnD,MAAK,MAAM,KAAK,KACd,OAAM,KAAK,qDAAqD,GAAG,QAAQ,EAAE,MAAM;AAGvF,QAAO,GAAG,MAAM,KAAK,KAAK,CAAC;;AAG7B,MAAa,oBAAoB,cAAc;CAC7C,MAAM;EACJ,MAAM;EACN,aAAa;EACd;CACD,MAAM;EACJ,OAAO;GACL,MAAM;GACN,aAAa;GACb,UAAU;GACX;EACD,MAAM;GACJ,MAAM;GACN,aAAa;GACb,SAAS;GACV;EACF;CACD,MAAM,IAAI,EAAE,QAAQ;EAClB,MAAM,MAAM,KAAK,UAAU,KAAA,IAAY,KAAK,OAAO,KAAK,MAAM,CAAC,aAAa;AAC5E,MAAI,QAAQ,QAAQ;AAClB,WAAQ,OAAO,MAAM,UAAU,CAAC;AAChC,UAAO,eAAe,SAAS,GAAG;;AAEpC,MAAI,QAAQ,OAAO;AACjB,WAAQ,OAAO,MAAM,SAAS,CAAC;AAC/B,UAAO,eAAe,SAAS,GAAG;;AAEpC,MAAI,QAAQ,QAAQ;AAClB,WAAQ,OAAO,MAAM,UAAU,CAAC;AAChC,UAAO,eAAe,SAAS,GAAG;;AAKpC,MAAI,KAAK,KACP,UAAS;GACP,IAAI;GACJ,QAAQ;GACR,SAAS;IAAC;IAAQ;IAAO;IAAO;GAChC,SAAS,yDAAyD,KAAK,UAAU,IAAI,CAAC;GACvF,CAAC;MAEF,SAAQ,OAAO,MACb,oOAOD;AAEH,SAAO,eAAe,SAAS,MAAM;;CAExC,CAAC;;;ACrMF,MAAME,SAAO;AASb,eAAsB,aACpB,aACA,SACA,OAAkC,EAAE,EACV;CAE1B,MAAM,MAAM,MAAM,kBAA2B;EAC3C,KAFU,GAAGA,OAAK,cAAc,YAAY;EAG5C;EACA,GAAI,KAAK,YAAY,EAAE,WAAW,KAAK,WAAW,GAAG,EAAE;EACxD,CAAC;AACF,KAAI,CAAC,MAAM,QAAQ,IAAI,CACrB,OAAM,IAAI,MAAM,2CAA2C,YAAY,gBAAgB;AAEzF,QAAO,IAAI,KAAK,OAAO,UAAU,aAAa,OAAO,aAAa,MAAM,CAAC;;AAG3E,SAAS,aAAa,KAAc,aAAqB,OAA8B;AACrF,KAAI,QAAQ,QAAQ,OAAO,QAAQ,SACjC,OAAM,IAAI,MACR,qCAAqC,MAAM,iBAAiB,YAAY,iBACzE;CAEH,MAAM,MAAM;CAKZ,MAAM,QAAQ,IAAI,MAAM,IAAI,YAAY,IAAI;AAC5C,KAAI,OAAO,UAAU,YAAY,OAAO,UAAU,SAChD,OAAM,IAAI,MACR,qCAAqC,MAAM,iBAAiB,YAAY,cACzE;CAEH,MAAM,UAAU,IAAI,QAAQ,IAAI,cAAc,IAAI,WAAW,IAAI;CACjE,MAAM,OAAO,OAAO,YAAY,WAAW,UAAU,KAAA;CACrD,MAAM,WAAW,OAAO,IAAI,aAAa,WAAW,IAAI,WAAW,KAAA;CACnE,MAAM,EACJ,IAAI,KACJ,UAAU,MACV,OAAO,MACP,MAAM,IACN,YAAY,KACZ,SAAS,KACT,aAAa,IACb,UAAU,IACV,GAAG,UACD;AACJ,QAAO;EAAE,IAAI;EAAO;EAAM;EAAU;EAAO;;AAsB7C,eAAsB,aACpB,aACA,MACA,SACA,OAAkC,EAAE,EACP;CAE7B,MAAM,MAAM,MAAM,kBAA2B;EAC3C,QAAQ;EACR,KAHU,GAAGA,OAAK,cAAc,YAAY;EAI5C;EACA,MAAM;GAAE;GAAa,MAAM,KAAK;GAAM,QAAQ,KAAK;GAAQ;EAC3D,GAAI,KAAK,YAAY,EAAE,WAAW,KAAK,WAAW,GAAG,EAAE;EACxD,CAAC;AACF,KAAI,QAAQ,QAAQ,OAAO,QAAQ,SACjC,OAAM,IAAI,MAAM,qDAAqD,cAAc;CAErF,MAAM,MAAM;CACZ,MAAM,SAAS,IAAI;AACnB,KAAI,OAAO,WAAW,YAAY,OAAO,WAAW,EAClD,OAAM,IAAI,MACR,qDAAqD,YAAY,yBAClE;CAEH,MAAM,EAAE,QAAQ,IAAI,GAAG,UAAU;AACjC,QAAO;EAAE;EAAQ;EAAO;;AAG1B,eAAsB,cACpB,aACA,UACA,SACA,OAAkC,EAAE,EACrB;AAEf,OAAM,kBAA2B;EAC/B,QAAQ;EACR,KAHU,GAAGA,OAAK,cAAc,YAAY,YAAY,SAAS;EAIjE;EACA,GAAI,KAAK,YAAY,EAAE,WAAW,KAAK,WAAW,GAAG,EAAE;EACxD,CAAC;;AChFJ,MAAM,aAAa;AAOnB,SAAgB,gBAAgB,KAAyC;AACvE,KAAI,IAAI,WAAW,EAAG,QAAO;AAC7B,KAAI,IAAI,SAAA,GAAmB,QAAO;AAClC,KAAI,CAAC,WAAW,KAAK,IAAI,CAAE,QAAO;AAClC,QAAO;;AAOT,SAAgB,cAAc,KAA8B;CAC1D,MAAM,QAAQ,IACX,MAAM,IAAI,CACV,KAAK,MAAM,EAAE,MAAM,CAAC,CACpB,QAAQ,MAAM,EAAE,SAAS,EAAE;AAC9B,KAAI,MAAM,WAAW,EAAG,QAAO;EAAE,IAAI;EAAO,QAAQ;EAAS;CAC7D,MAAM,MAAM,MAAM,QAAQ,MAAM,CAAC,eAAe,KAAK,EAAE,CAAC;AACxD,KAAI,IAAI,SAAS,EAAG,QAAO;EAAE,IAAI;EAAO,QAAQ;EAAW;EAAK;AAChE,QAAO;EAAE,IAAI;EAAM;EAAO;;AAG5B,MAAMC,cAAY,cAAc;CAC9B,MAAM;EACJ,MAAM;EACN,aAAa;EACd;CACD,MAAM;EACJ,WAAW;GACT,MAAM;GACN,aAAa;GACd;EACD,MAAM;GAAE,MAAM;GAAW,aAAa;GAAyC,SAAS;GAAO;EAChG;CACD,MAAM,IAAI,EAAE,QAAQ;EAClB,MAAM,MAAM,MAAM,wBAAwB,KAAK;AAC/C,MAAI,CAAC,IAAK;EACV,MAAM,EAAE,SAAS,gBAAgB;AACjC,qBAAmB,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;AAE5C,MAAI;GACF,MAAM,OAAO,MAAM,aAAa,aAAa,QAAQ,QAAQ;AAC7D,OAAI,KAAK,MAAM;AACb,aAAS;KACP,IAAI;KACJ;KACA,MAAM,KAAK,KAAK,OAAO;MACrB,IAAI,EAAE;MACN,MAAM,EAAE,QAAQ;MAChB,UAAU,EAAE,YAAY;MACxB,OAAO,EAAE;MACV,EAAE;KACH,GAAI,KAAK,WAAW,IAAI,EAAE,UAAU,MAAM,GAAG,EAAE;KAChD,CAAC;AACF,WAAO,eAAe,SAAS,GAAG;;AAEpC,OAAI,KAAK,WAAW,GAAG;AACrB,YAAQ,OAAO,MAAM,4BAA4B,YAAY,KAAK;AAClE,YAAQ,OAAO,MACb,8FACD;AACD,WAAO,eAAe,SAAS,GAAG;;AAEpC,WAAQ,OAAO,MAAM,GAAG,KAAK,OAAO,2BAA2B,YAAY,KAAK;GAChF,MAAM,MAAM,KAAK,KAAK;AACtB,QAAK,MAAM,KAAK,MAAM;IACpB,MAAM,OAAO,EAAE,QAAQ;IACvB,MAAM,SAAS,aAAa,EAAE,UAAU,IAAI;AAC5C,YAAQ,OAAO,MAAM,GAAG,EAAE,GAAG,IAAI,KAAK,IAAI,OAAO,IAAI;;AAEvD,UAAO,eAAe,SAAS,GAAG;WAC3B,KAAK;AACZ,UAAO,qBAAqB,KAAK,MAAM,IAAI;;;CAGhD,CAAC;AAEF,MAAM,gBAAgB,cAAc;CAClC,MAAM;EACJ,MAAM;EACN,aAAa;EACd;CACD,MAAM;EACJ,MAAM;GACJ,MAAM;GACN,aACE;GACF,UAAU;GACX;EACD,MAAM;GACJ,MAAM;GACN,aACE;GACH;EACD,WAAW;GACT,MAAM;GACN,aAAa;GACd;EACD,MAAM;GAAE,MAAM;GAAW,aAAa;GAAyC,SAAS;GAAO;EAChG;CACD,MAAM,IAAI,EAAE,QAAQ;EAClB,MAAM,MAAM,MAAM,wBAAwB,KAAK;AAC/C,MAAI,CAAC,IAAK;EACV,MAAM,EAAE,SAAS,gBAAgB;AACjC,qBAAmB,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;EAE5C,MAAM,OAAO,OAAO,KAAK,KAAK;EAC9B,MAAM,UAAU,gBAAgB,KAAK;AACrC,MAAI,YAAY,MAAM;GACpB,MAAM,UACJ,YAAY,cACR,qCACA,YAAY,aACV,iCAA0C,KAAK,OAAO,KACtD;AACR,OAAI,KAAK,KAAM,UAAS;IAAE,IAAI;IAAO,QAAQ;IAAgB;IAAS,CAAC;OAClE,SAAQ,OAAO,MAAM,GAAG,QAAQ,IAAI;AACzC,UAAO,eAAe,SAAS,MAAM;;EAGvC,IAAI;AACJ,MAAI,KAAK,MAAM;GACb,MAAM,SAAS,cAAc,OAAO,KAAK,KAAK,CAAC;AAC/C,OAAI,CAAC,OAAO,IAAI;IACd,MAAM,UACJ,OAAO,WAAW,UACd,yDACA,qCAAqC,OAAO,OAAO,EAAE,EAAE,KAAK,KAAK,CAAC;AACxE,QAAI,KAAK,KAAM,UAAS;KAAE,IAAI;KAAO,QAAQ;KAAgB;KAAS,CAAC;QAClE,SAAQ,OAAO,MAAM,GAAG,QAAQ,IAAI;AACzC,WAAO,eAAe,SAAS,MAAM;;AAEvC,YAAS;IAAE,OAAO;IAAO,UAAU,OAAO;IAAO;QAEjD,UAAS;GAAE,OAAO;GAAM,UAAU,EAAE;GAAE;AAGxC,MAAI;GACF,MAAM,SAAS,MAAM,aAAa,aAAa;IAAE;IAAM;IAAQ,EAAE,QAAQ,QAAQ;AACjF,OAAI,KAAK,MAAM;AACb,aAAS;KACP,IAAI;KACJ;KACA,QAAQ,OAAO;KACf;KACA,QAAQ;MAAE,OAAO,OAAO;MAAO,UAAU,CAAC,GAAG,OAAO,SAAS;MAAE;KAC/D,OAAO,OAAO;KACf,CAAC;AACF,WAAO,eAAe,SAAS,GAAG;;AAMpC,WAAQ,OAAO,MAAM,GAAG,OAAO,OAAO,IAAI;AAC1C,WAAQ,OAAO,MACb,qGACD;AACD,UAAO,eAAe,SAAS,GAAG;WAC3B,KAAK;AACZ,UAAO,qBAAqB,KAAK,MAAM,IAAI;;;CAGhD,CAAC;AAEF,MAAM,gBAAgB,cAAc;CAClC,MAAM;EACJ,MAAM;EACN,aAAa;EACd;CACD,MAAM;EACJ,IAAI;GACF,MAAM;GACN,UAAU;GACV,aAAa;GACd;EACD,WAAW;GACT,MAAM;GACN,aAAa;GACd;EACD,MAAM;GAAE,MAAM;GAAW,aAAa;GAAyC,SAAS;GAAO;EAChG;CACD,MAAM,IAAI,EAAE,QAAQ;EAClB,MAAM,MAAM,MAAM,wBAAwB,KAAK;AAC/C,MAAI,CAAC,IAAK;EACV,MAAM,EAAE,SAAS,gBAAgB;AACjC,qBAAmB,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;EAI5C,MAAM,QAAQ,OAAO,KAAK,GAAG;AAE7B,MAAI;AACF,SAAM,cAAc,aAAa,OAAO,QAAQ,QAAQ;AACxD,OAAI,KAAK,MAAM;AACb,aAAS;KAAE,IAAI;KAAM;KAAa,UAAU;KAAO,CAAC;AACpD,WAAO,eAAe,SAAS,GAAG;;AAEpC,WAAQ,OAAO,MAAM,mBAAmB,MAAM,gBAAgB,YAAY,KAAK;AAC/E,UAAO,eAAe,SAAS,GAAG;WAC3B,KAAK;AACZ,UAAO,qBAAqB,KAAK,MAAM,IAAI;;;CAGhD,CAAC;AAEF,SAAgB,aAAa,UAA8B,KAAqB;AAC9E,KAAI,aAAa,KAAA,EAAW,QAAO;CACnC,MAAM,SAAS,WAAW;CAC1B,MAAM,OAAO,KAAK,MAAM,SAAS,MAAW;AAC5C,KAAI,SAAS,EAAG,QAAO;AACvB,QAAO,KAAK;;AAGd,MAAa,cAAc,cAAc;CACvC,MAAM;EACJ,MAAM;EACN,aAAa;EACd;CACD,aAAa;EACX,IAAIA;EACJ,QAAQ;EACR,QAAQ;EACT;CACF,CAAC;;;ACtPF,SAAS,WAAW,GAA2C;AAC7D,QAAO,QAAQ;;AAGjB,IAAa,mBAAb,cAAsC,MAAM;CAC1C,YACE,QACA,MACA,SACA;AACA,QAAM,iBAAiB,OAAO,IAAI,QAAQ,SAAS,KAAK,GAAG;AAJlD,OAAA,SAAA;AACA,OAAA,OAAA;AAIT,OAAK,OAAO;;;AAIhB,IAAa,2BAAb,cAA8C,MAAM;CAClD,cAAc;AACZ,QAAM,qDAAqD;AAC3D,OAAK,OAAO;;;AA0BhB,IAAa,YAAb,MAAa,UAAU;CACrB;CACA,SAAiB;CACjB,0BAA2B,IAAI,KAG5B;CACH,4BAA6B,IAAI,KAAuB;CACxD,SAAiB;CAEjB,YAAoB,QAAmB;AACrC,OAAK,SAAS;AACd,SAAO,iBAAiB,YAAY,OAAqB,KAAK,cAAc,GAAG,CAAC;AAChF,SAAO,iBAAiB,eAAe,KAAK,aAAa,CAAC;AAC1D,SAAO,iBAAiB,eAAe,GAIrC;;CAGJ,aAAa,QAAQ,SAAgD;EAEnE,MAAM,UADU,QAAQ,sBAAsB,QAAgB,IAAI,UAAU,IAAI,GACzD,QAAQ,IAAI;AACnC,QAAM,IAAI,SAAe,SAAS,WAAW;GAC3C,MAAM,eAAe;AACnB,aAAS;AACT,aAAS;;GAEX,MAAM,gBAAgB;AACpB,aAAS;AACT,2BAAO,IAAI,MAAM,mCAAmC,QAAQ,MAAM,CAAC;;GAErE,MAAM,gBAAgB;AACpB,aAAS;AACT,2BAAO,IAAI,MAAM,wCAAwC,QAAQ,IAAI,GAAG,CAAC;;GAE3E,MAAM,gBAAgB;AACpB,WAAO,oBAAoB,QAAQ,OAAO;AAC1C,WAAO,oBAAoB,SAAS,QAAQ;AAC5C,WAAO,oBAAoB,SAAS,QAAQ;;AAE9C,UAAO,iBAAiB,QAAQ,OAAO;AACvC,UAAO,iBAAiB,SAAS,QAAQ;AACzC,UAAO,iBAAiB,SAAS,QAAQ;IACzC;AACF,SAAO,IAAI,UAAU,OAAO;;CAG9B,GAAG,UAAwC;AACzC,OAAK,UAAU,IAAI,SAAS;AAC5B,eAAa,KAAK,UAAU,OAAO,SAAS;;CAG9C,MAAM,KACJ,QACA,QACA,WACY;AACZ,MAAI,KAAK,OAAQ,OAAM,IAAI,0BAA0B;EACrD,MAAM,KAAK,KAAK;EAIhB,MAAM,MAA+B;GAAE;GAAI;GAAQ;AACnD,MAAI,OAAQ,KAAI,SAAS;AACzB,MAAI,UAAW,KAAI,YAAY;EAC/B,MAAM,SAAS,IAAI,SAAkC,SAAS,WAAW;AACvE,QAAK,QAAQ,IAAI,IAAI;IAAE;IAAS;IAAQ;IAAQ,CAAC;IACjD;AACF,OAAK,OAAO,KAAK,KAAK,UAAU,IAAI,CAAC;AAErC,SADe,MAAM;;CAIvB,MAAM,QAAuB;AAC3B,MAAI,KAAK,OAAQ;AACjB,OAAK,SAAS;AAEd,OAAK,MAAM,GAAG,YAAY,KAAK,QAC7B,SAAQ,OAAO,IAAI,0BAA0B,CAAC;AAEhD,OAAK,QAAQ,OAAO;AACpB,MAAI;AACF,QAAK,OAAO,OAAO;UACb;;CAKV,cAAsB,IAAwB;EAC5C,IAAI;AACJ,MAAI;GACF,MAAM,MACJ,OAAO,GAAG,SAAS,WAAW,GAAG,OAAO,IAAI,aAAa,CAAC,OAAO,GAAG,KAAoB;AAC1F,YAAS,KAAK,MAAM,IAAI;UAClB;AAEN;;AAEF,MAAI,WAAW,OAAO,EAAE;GACtB,MAAM,UAAU,KAAK,QAAQ,IAAI,OAAO,GAAG;AAC3C,OAAI,CAAC,QAAS;AACd,QAAK,QAAQ,OAAO,OAAO,GAAG;AAC9B,OAAI,WAAW,OACb,SAAQ,OACN,IAAI,iBAAiB,QAAQ,QAAQ,OAAO,MAAM,MAAM,OAAO,MAAM,QAAQ,CAC9E;OAED,SAAQ,QAAQ,OAAO,OAAO;AAEhC;;AAEF,OAAK,MAAM,YAAY,KAAK,UAC1B,KAAI;AACF,YAAS,OAAO;UACV;;CAMZ,cAA4B;AAC1B,MAAI,KAAK,OAAQ;AACjB,OAAK,SAAS;AACd,OAAK,MAAM,GAAG,YAAY,KAAK,QAC7B,SAAQ,OAAO,IAAI,0BAA0B,CAAC;AAEhD,OAAK,QAAQ,OAAO;;;;;;;;AAgBxB,eAAsB,kBAAkB,QAA4C;CAClF,MAAM,EAAE,gBAAgB,MAAM,OAAO,KAElC,oBAAoB;CACvB,MAAM,OAAO,YAAY,MAAM,MAAM,EAAE,SAAS,OAAO;AACvD,KAAI,CAAC,KACH,OAAM,IAAI,MAAM,gEAAgE;CAElF,MAAM,EAAE,cAAc,MAAM,OAAO,KAA4B,yBAAyB;EACtF,UAAU,KAAK;EACf,SAAS;EACV,CAAC;AACF,QAAO;EAAE;EAAW,UAAU,KAAK;EAAU;;;;;;;;;;AAiB/C,eAAsB,0BACpB,QACA,WACA,YACqB;AACrB,OAAM,OAAO,KAAK,eAAe,EAAE,EAAE,UAAU;AAc/C,QAbY,OAAO,IAAI,UAAU;AAC/B,MAAI,MAAM,cAAc,UAAW;AACnC,MAAI,MAAM,WAAW,sBAAuB;EAC5C,MAAM,QAAQ,MAAM,OAAO;AAG3B,MAAI,CAAC,OAAO,OAAO,CAAC,MAAM,GAAI;AAC9B,aAAW;GACT,KAAK,MAAM;GACX,SAAS,MAAM;GACf,aAAa,MAAM,aAAa,KAAA;GACjC,CAAC;GACF;;;;;;;;;;;;AAcJ,eAAsB,cACpB,QACA,WAC+B;CAC/B,MAAM,SAAS,MAAM,OAAO,KAA2B,yBAAyB,EAAE,EAAE,UAAU;AAC9F,KAAI,CAAC,MAAM,QAAQ,OAAO,QAAQ,CAChC,OAAM,IAAI,MAAM,qDAAqD;AAEvE,QAAO,OAAO,QAAQ,KAAK,KAAK,UAAU,eAAe,KAAK,MAAM,CAAC;;;;;;;;AASvE,eAAsB,qBACpB,QACA,WACA,WACe;AACf,OAAM,OAAO,KAAK,gCAAgC,EAAE,WAAW,EAAE,UAAU;;AAmB7E,eAAsB,eACpB,QACA,WACA,YAC+B;CAC/B,IAAI;AAIJ,KAAI;AACF,QAAM,MAAM,OAAO,KAIjB,oBACA;GACE;GACA,eAAe;GACf,cAAc;GACd,aAAa;GACd,EACD,UACD;UACM,KAAK;AACZ,SAAO;GAAE,IAAI;GAAO,OAAQ,IAAc;GAAS;;AAErD,KAAI,IAAI,iBACN,QAAO;EACL,IAAI;EACJ,OACE,IAAI,iBAAiB,WAAW,eAChC,IAAI,iBAAiB,QACrB;EACH;AAEH,QAAO;EAAE,IAAI;EAAM,OAAO,IAAI,QAAQ;EAAY;;;;;;;AAQpD,eAAsB,gBACpB,QACA,WACwB;AAIxB,SAHa,MAAM,OAChB,KAAiD,qBAAqB,EAAE,EAAE,UAAU,CACpF,YAAY,KAAK,GACP,UAAU,OAAO,OAAO;;AAGvC,SAAS,eAAe,KAAc,OAA0B;AAC9D,KAAI,CAAC,OAAO,OAAO,QAAQ,SACzB,OAAM,IAAI,MAAM,WAAW,MAAM,mBAAmB;CAEtD,MAAM,IAAI;CACV,MAAM,OAAO,UAA0B;EACrC,MAAM,IAAI,EAAE;AACZ,MAAI,OAAO,MAAM,SAAU,OAAM,IAAI,MAAM,WAAW,MAAM,GAAG,MAAM,kBAAkB;AACvF,SAAO;;CAET,MAAM,OAAO,UAA0B;EACrC,MAAM,IAAI,EAAE;AACZ,MAAI,OAAO,MAAM,SAAU,OAAM,IAAI,MAAM,WAAW,MAAM,GAAG,MAAM,kBAAkB;AACvF,SAAO;;CAET,MAAM,QAAQ,UAA2B;EACvC,MAAM,IAAI,EAAE;AACZ,MAAI,OAAO,MAAM,UAAW,OAAM,IAAI,MAAM,WAAW,MAAM,GAAG,MAAM,mBAAmB;AACzF,SAAO;;CAET,MAAM,OAAO;EACX,MAAM,IAAI,OAAO;EACjB,OAAO,IAAI,QAAQ;EACnB,QAAQ,IAAI,SAAS;EACrB,MAAM,IAAI,OAAO;EACjB,SAAS,IAAI,UAAU;EACvB,UAAU,KAAK,WAAW;EAC1B,QAAQ,KAAK,SAAS;EACtB,SAAS,KAAK,UAAU;EACzB;CACD,MAAM,WAAW,EAAE;AACnB,KAAI,aAAa,YAAY,aAAa,SAAS,aAAa,OAC9D,QAAO;EAAE,GAAG;EAAM;EAAU;AAE9B,QAAO;;;;ACjZT,IAAa,sBAAb,cAAyC,MAAM;CAC7C,YAAY,YAAwC;AAClD,QACE,8DAA8D,WAAW,KAAK,KAAK,CAAC,gEAErF;AAJkB,OAAA,aAAA;AAKnB,OAAK,OAAO;;;AAIhB,IAAa,oBAAb,cAAuC,MAAM;CAC3C,YACE,YACA,OACA;AACA,QAAM,oBAAoB,WAAW,IAAI,MAAM,UAAU;AAHhD,OAAA,aAAA;AAIT,OAAK,OAAO;AACZ,OAAK,QAAQ;;;AAIjB,IAAa,6BAAb,cAAgD,MAAM;CACpD,YAAY,YAA6B;AACvC,QACE,GAAG,WAAW,2HAEf;AAJkB,OAAA,aAAA;AAKnB,OAAK,OAAO;;;AAOhB,SAAgB,iBACd,MAAyB,QAAQ,KACjC,WAA4B,QAAQ,UACvB;CACb,MAAM,WAAW,IAAI;CACrB,MAAM,MAAgB,EAAE;AACxB,KAAI,YAAY,SAAS,SAAS,EAAG,KAAI,KAAK,SAAS;AAEvD,KAAI,aAAa,SACf,KAAI,KACF,gEACA,0EACA,8EACA,sDACA,kEACA,2CACD;UACQ,aAAa,SAAS;EAI/B,MAAM,KAAK,IAAI,gBAAgB;EAC/B,MAAM,OAAO,IAAI,wBAAwB;EACzC,MAAM,QAAQ,IAAI,gBAAgBC,MAAQ,KAAK,SAAS,IAAI,QAAQ,WAAW,QAAQ;AACvF,MAAI,KACFA,MAAQ,KAAK,IAAI,UAAU,UAAU,eAAe,aAAa,EACjEA,MAAQ,KAAK,MAAM,UAAU,UAAU,eAAe,aAAa,EACnEA,MAAQ,KAAK,OAAO,UAAU,UAAU,eAAe,aAAa,EACpEA,MAAQ,KAAK,IAAI,aAAa,QAAQ,eAAe,aAAa,EAClEA,MAAQ,KAAK,MAAM,aAAa,QAAQ,eAAe,aAAa,CACrE;OAGD,KAAI,KACF,wBACA,iBACA,oBACA,YACA,yBACA,iBACD;AAGH,QAAO,EAAE,YAAY,KAAK;;AAG5B,SAAS,eAAe,GAAW,UAAoC;AACrE,KAAI,aAAa,QAAS,QAAO,eAAe,KAAK,EAAE;AACvD,QAAO,EAAE,WAAW,IAAI;;AAG1B,eAAe,cACb,MACA,KACA,UACwB;CACxB,MAAM,OAAO,IAAI,QAAQ,IAAI,QAAQ,IAAI,QAAQ;AACjD,KAAI,KAAK,WAAW,EAAG,QAAO;CAC9B,MAAM,MAAM,aAAa,UAAU,MAAM;CACzC,MAAM,KAAK,MAAM,OAAO;CAIxB,MAAM,aACJ,aAAa,UACT,CAAC,IAAI,IAAI,IAAI,WAAW,kBAAkB,MAAM,IAAI,CAAC,QAAQ,MAAM,EAAE,SAAS,EAAE,CAAC,GACjF,CAAC,GAAG;AACV,MAAK,MAAM,OAAO,KAAK,MAAM,IAAI,EAAE;AACjC,MAAI,IAAI,WAAW,EAAG;AACtB,OAAK,MAAM,OAAO,YAAY;GAC5B,MAAM,YAAY,KAAK,KAAK,OAAO,IAAI;AACvC,OAAI;AAIF,UAAM,GAAG,OAAO,WAAWC,UAAY,KAAK;AAC5C,WAAO;WACD;;;AAKZ,QAAO;;AAGT,eAAsB,WACpB,MAAyB,QAAQ,KACjC,WAA4B,QAAQ,UACnB;CACjB,MAAM,EAAE,eAAe,iBAAiB,KAAK,SAAS;CACtD,MAAM,KAAK,MAAM,OAAO;AACxB,MAAK,MAAM,aAAa,YAAY;AAClC,MAAI,eAAe,WAAW,SAAS,EAAE;AACvC,OAAI;AACF,UAAM,GAAG,OAAO,WAAWA,UAAY,KAAK;AAC5C,WAAO;WACD;AAGR;;EAEF,MAAM,WAAW,MAAM,cAAc,WAAW,KAAK,SAAS;AAC9D,MAAI,SAAU,QAAO;;AAEvB,OAAM,IAAI,oBAAoB,WAAW;;AAwB3C,MAAM,kBAAkB;AAExB,SAAS,wBAAwB,QAA+B;CAC9D,MAAM,QAAQ,gBAAgB,KAAK,OAAO;AAC1C,QAAO,QAAS,MAAM,MAAM,OAAQ;;AAGtC,eAAsB,aAAa,SAAuD;CACxF,MAAM,aAAa,QAAQ,cAAe,MAAM,YAAY;CAC5D,MAAM,oBAAoB,QAAQ,qBAAqB;CAEvD,MAAM,cAAc,MAAM,QAAQ,KAAK,QAAQ,EAAE,gBAAgB,CAAC;CAQlE,MAAM,OAAiB;EACrB;EACA,mBAAmB;EACnB;EACA;EACA;EACA;EACA;EACD;AACD,KAAI,QAAQ,SAKV,MAAK,KAAK,kBAAkB,iBAAiB,yBAAyB;AAExE,MAAK,KAAK,QAAQ,WAAW;CAE7B,MAAM,UAAU,QAAQ,mBAAmB,MAAyB,MAAM,YAAY,CAAC,GAAG,EAAE,CAAC;CAC7F,IAAI;AACJ,KAAI;AACF,UAAQ,QAAQ,KAAK;UACd,KAAK;AACZ,QAAM,GAAG,aAAa;GAAE,WAAW;GAAM,OAAO;GAAM,CAAC,CAAC,YAAY,GAAG;AACvE,QAAM,IAAI,kBAAkB,YAAY,IAAa;;AAKvD,KAAI;AACF,QAAM,OAAO;SACP;CAIR,MAAM,UAAU,YAA2B;AACzC,MAAI;AACF,OAAI,CAAC,MAAM,OAAQ,OAAM,KAAK,UAAU;UAClC;AAGR,QAAM,GAAG,aAAa;GAAE,WAAW;GAAM,OAAO;GAAM,CAAC,CAAC,YAAY,GAAG;;CAGzE,IAAI,YAAY;CAChB,MAAM,QAAQ,MAAM,IAAI,SAAiB,SAAS,WAAW;EAC3D,MAAM,QAAQ,iBAAiB;AAC7B,YAAS;AACT,UAAO,IAAI,2BAA2B,WAAW,CAAC;KACjD,kBAAkB;AACrB,MAAI,OAAO,MAAM,UAAU,WAAY,OAAM,OAAO;EAEpD,MAAM,YAAY,UAAkB;AAClC,gBAAa,MAAM,SAAS,OAAO;GACnC,MAAM,QAAQ,wBAAwB,UAAU;AAChD,OAAI,OAAO;AACT,aAAS;AACT,YAAQ,MAAM;;;EAGlB,MAAM,UAAU,SAAwB;AACtC,YAAS;AACT,UACE,IAAI,kBACF,4BACA,IAAI,MAAM,4BAA4B,QAAQ,OAAO,2BAA2B,CACjF,CACF;;EAEH,MAAM,WAAW,QAAe;AAC9B,YAAS;AACT,UAAO,IAAI,kBAAkB,YAAY,IAAI,CAAC;;EAEhD,MAAM,gBAAgB;AACpB,gBAAa,MAAM;AACnB,SAAM,QAAQ,IAAI,QAAQ,SAAS;AACnC,SAAM,IAAI,QAAQ,OAAO;AACzB,SAAM,IAAI,SAAS,QAAQ;;AAE7B,QAAM,QAAQ,GAAG,QAAQ,SAAS;AAClC,QAAM,GAAG,QAAQ,OAAO;AACxB,QAAM,GAAG,SAAS,QAAQ;GAC1B,CAAC,MAAM,OAAO,QAAQ;AACtB,QAAM,SAAS;AACf,QAAM;GACN;AAEF,QAAO;EACL,SAAS;EACT,sBAAsB;EACtB;EACA;EACD;;;;ACrQH,MAAa,qBACX;AAKF,MAAa,sBAAsB;AAKnC,MAAa,uBAAuB;AAOpC,SAAgB,mBAAmB,KAAsB;CACvD,IAAI;AACJ,KAAI;AACF,aAAW,IAAI,IAAI,IAAI,CAAC;SAClB;AACN,SAAO;;AAET,QAAO,oBAAoB,KAAK,SAAS;;AAG3C,SAAgB,oBAAoB,UAA2B;AAC7D,QAAO,qBAAqB,KAAK,SAAS;;AAiC5C,MAAM,qBAAqB;AAC3B,MAAM,wBAAwB;AAC9B,MAAM,sBAAsB;AAC5B,MAAM,kBAAkB;AAqBxB,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4D3B,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;AAuB5B,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;AAuC7B,eAAsB,iBACpB,SAC+B;CAC/B,MAAM,EACJ,QACA,WACA,aACA,sBAAsB,KACtB,iBACA,aACE;AAKJ,OAAM,OAAO,KAAK,kBAAkB,EAAE,EAAE,UAAU;AAClD,OAAM,OAAO,KAAK,kBAAkB,EAAE,EAAE,UAAU;AAClD,OAAM,qBAAqB,QAAQ,WAAW,mBAAmB;AAKjE,OAAM,OAAO,KAAK,eAAe,EAAE,aAAa,MAAM,EAAE,UAAU,CAAC,YAAY,GAG7E;CAEF,MAAM,QAAQ,MAAM,iBAAiB,QAAQ,UAAU;AACvD,KAAI,CAAC,MAAM,GACT,QAAO;EAAE,MAAM;EAAY,QAAQ,MAAM;EAAQ;CAGnD,MAAM,OAAO,MAAM,eACjB,QACA,WACA,IAAI,mBAAmB,IAAI,KAAK,UAAU,YAAY,MAAM,CAAC,IAAI,KAAK,UAAU,YAAY,SAAS,CAAC,GACvG;AACD,KAAI,CAAC,KAAK,GAKR,QAAO;EAAE,MAAM;EAAY,QAAQ;EAAyB;AAE9D,KAAI,CAAC,KAAK,MAAM,GACd,QAAO;EAAE,MAAM;EAAY,QAAQ,aAAa,KAAK,MAAM;EAAS;CAKtE,IAAI,iBAAgC;CACpC,MAAM,SAAS,MAAM,0BAA0B,QAAQ,YAAY,OAAO;AACxE,MAAI,CAAC,GAAG,YAAa;AACrB,MAAI,eAAe,GAAG,IAAI,CAAE,kBAAiB,GAAG;GAChD;AAEF,KAAI;EAEF,MAAM,SAAS,MAAM,4BACnB,QACA,WACA,2BACM,eACP;AAED,MAAI,OAAO,SAAS,SAClB,QAAO;GAAE,MAAM;GAAM,QAAQ;GAAO;AAEtC,MAAI,OAAO,SAAS,WAClB,QAAO;GAAE,MAAM;GAAY,QAAQ,OAAO;GAAQ;AAEpD,MAAI,OAAO,SAAS,UAClB,QAAO;GAAE,MAAM;GAAW,OAAO;GAAU;AAK7C,cAAY;AAEZ,MADe,MAAM,eAAe,QAAQ,WAAW,uBAAuB,eAAe,KAC9E,SAAU,QAAO;GAAE,MAAM;GAAM,QAAQ;GAAM;AAM5D,SAAO;GAAE,MAAM;GAAW,OAAO;GAAW;WACpC;AACR,UAAQ;;;AAYZ,eAAe,iBACb,QACA,WACsC;CACtC,MAAM,WAAW,KAAK,KAAK,GAAG;CAC9B,IAAI,aAAa;AACjB,QAAO,KAAK,KAAK,GAAG,UAAU;EAC5B,MAAM,QAAQ,MAAM,eAClB,QACA,WACA,IAAI,oBAAoB,KACzB;AACD,MAAI,MAAM,IAAI;AACZ,OAAI,MAAM,MAAM,MAAO,QAAO,EAAE,IAAI,MAAM;AAC1C,gBAAa,qBAAqB,MAAM,MAAM,MAAM;QAOpD,cAAa;AAEf,QAAM,MAAM,mBAAmB;;AAEjC,QAAO;EAAE,IAAI;EAAO,QAAQ,mBAAmB;EAAc;;AAS/D,eAAe,4BACb,QACA,WACA,SACA,aACuB;CACvB,MAAM,WAAW,KAAK,KAAK,GAAG;AAC9B,QAAO,KAAK,KAAK,GAAG,UAAU;AAC5B,MAAI,aAAa,CAAE,QAAO,EAAE,MAAM,UAAU;EAC5C,MAAM,WAAW,MAAM,gBAAgB,QAAQ,UAAU;AACzD,MAAI,YAAY,eAAe,SAAS,CAAE,QAAO,EAAE,MAAM,UAAU;EAEnE,MAAM,QAAQ,MAAM,eAClB,QACA,WACA,IAAI,qBAAqB,KAC1B;AACD,MAAI,MAAM,IAAI;AACZ,OAAI,eAAe,MAAM,MAAM,IAAI,CAAE,QAAO,EAAE,MAAM,UAAU;AAC9D,OAAI,MAAM,MAAM,iBACd,QAAO;IAAE,MAAM;IAAY,QAAQ;IAAoB;AAEzD,OAAI,MAAM,MAAM,eAId,QAAO;IAAE,MAAM;IAAY,QAAQ;IAAsB;AAE3D,OAAI,mBAAmB,MAAM,MAAM,IAAI,IAAI,oBAAoB,MAAM,MAAM,SAAS,CAClF,QAAO,EAAE,MAAM,WAAW;;AAG9B,QAAM,MAAM,oBAAoB;;AAElC,QAAO,EAAE,MAAM,WAAW;;AAG5B,eAAe,eACb,QACA,WACA,SACA,aAC+B;CAC/B,MAAM,WAAW,KAAK,KAAK,GAAG;AAC9B,QAAO,KAAK,KAAK,GAAG,UAAU;AAC5B,MAAI,aAAa,CAAE,QAAO;EAC1B,MAAM,MAAM,MAAM,gBAAgB,QAAQ,UAAU;AACpD,MAAI,OAAO,eAAe,IAAI,CAAE,QAAO;AACvC,QAAM,MAAM,gBAAgB;;AAE9B,QAAO;;AAGT,SAAS,MAAM,IAA2B;AACxC,QAAO,IAAI,SAAS,YAAY;EAC9B,MAAM,IAAI,WAAW,SAAS,GAAG;AACjC,MAAI,OAAO,EAAE,UAAU,WAAY,GAAE,OAAO;GAC5C;;;;AC/XJ,MAAM,wBACJ;AAMF,MAAM,qBAAqB;AAC3B,MAAM,4BAA4B;AAMlC,MAAM,kCAAkC,CAAC,WAAW;AAMpD,MAAa,gCAAgC;;;;;;;;;;AAW7C,SAAgB,yBAAyB,gBAAwB,WAA2B;AAC1F,QAAO,KAAK,IAAI,+BAA+B,iBAAiB,UAAU;;AAG5E,SAAgB,uBAAuB,MAAuB;CAC5D,MAAM,QAAQ,KAAK,aAAa;AAChC,QAAO,gCAAgC,MACpC,WAAW,UAAU,OAAO,MAAM,EAAE,IAAI,MAAM,SAAS,OAAO,CAChE;;AAGH,SAAgB,eAAe,KAAsB;AACnD,KAAI;EACF,MAAM,IAAI,IAAI,IAAI,IAAI;AAItB,MAAI,EAAE,aAAa,mBAAoB,QAAO;AAC9C,MACE,EAAE,aAAa,6BACf,CAAC,EAAE,SAAS,WAAW,GAAG,0BAA0B,GAAG,CAEvD,QAAO;AAIT,SAAO;SACD;AACN,SAAO;;;;;;;;;;;AAYX,SAAgB,gBAAgB,OAGlB;AACZ,KAAI,MAAM,gBAAiB,QAAO;AAClC,QAAO,MAAM,iBAAiB,aAAa;;AAwC7C,MAAM,oBAAgC;CACpC,QAAQ,iBACN,MAAM;EACJ,SAAS;EACT,GAAI,iBAAiB,KAAA,IAAY,EAAE,SAAS,cAAc,GAAG,EAAE;EAC/D,WAAW,QAAQ;GACjB,MAAM,UAAU,IAAI,MAAM;AAC1B,OAAI,QAAQ,WAAW,EAAG,QAAO;AACjC,OAAI,CAAC,QAAQ,SAAS,IAAI,CAAE,QAAO;AACnC,UAAO;;EAEV,CAAC,CAAC,MAAM,MAAM,EAAE,MAAM,CAAC;CAC1B,gBACEC,SAAe;EACb,SAAS;EACT,MAAM;EACN,WAAW,QAAS,IAAI,SAAS,IAAI,OAAO;EAC7C,CAAC;CACJ,kBACE,OAAmB;EACjB,SAAS;EACT,SAAS;EACT,SAAS,CACP;GACE,MAAM;GACN,OAAO;GACR,EACD;GACE,MAAM;GACN,OAAO;GACR,CACF;EACF,CAAC;CACL;AAED,MAAa,eAAe,cAAc;CACxC,MAAM;EACJ,MAAM;EACN,aAAa;EACd;CACD,MAAM;EACJ,MAAM;GACJ,MAAM;GACN,aAAa;GACb,SAAS;GACV;EACD,SAAS;GACP,MAAM;GACN,aAAa;GACb,SAAS;GACV;EACD,aAAa;GACX,MAAM;GACN,aAAa;GACb,SAAS;GACV;EACD,OAAO;GACL,MAAM;GACN,aAAa;GACd;EACD,UAAU;GACR,MAAM;GACN,aACE;GACH;EACD,kBAAkB;GAChB,MAAM;GACN,aAAa;GACb,SAAS;GACV;EACD,MAAM;GACJ,MAAM;GACN,aACE;GACH;EACD,mBAAmB;GACjB,MAAM;GACN,aAAa;GACb,SAAS;GACV;EACF;CACD,MAAM,IAAI,EAAE,QAAQ;AAClB,SAAO,gBACL;GACE,MAAM,KAAK;GACX,SAAS,KAAK;GACd,aAAa,KAAK;GAClB,OAAO,OAAO,KAAK,UAAU,WAAW,KAAK,QAAQ,KAAA;GACrD,UAAU,OAAO,KAAK,aAAa,WAAW,KAAK,WAAW,KAAA;GAC9D,eAAe,KAAK;GACpB,MAAM,OAAO,KAAK,SAAS,WAAW,KAAK,OAAO,KAAA;GACnD,EACD;GAAE,gBAAgB;GAAiB;GAAiB,CACrD;;CAEJ,CAAC;AAmCF,eAAsB,gBAAgB,MAAwB,MAAiC;CAC7F,MAAM,aAAa,SAAkC,UAAkB;AACrE,MAAI,KAAK,KACP,SAAQ,OAAO,MAAM,GAAG,KAAK,UAAU;GAAE,IAAI;GAAO,GAAG;GAAS,CAAC,CAAC,IAAI;AAExE,UAAQ,OAAO,MAAM,GAAG,MAAM,IAAI;;CAGpC,MAAM,aAAa,OAAO,KAAK,QAAQ;AACvC,KAAI,CAAC,OAAO,SAAS,WAAW,IAAI,aAAa,GAAG;AAClD,YACE;GAAE,QAAQ;GAAmB,OAAO,KAAK;GAAS,EAClD,4BAA4B,KAAK,UAClC;AACD,SAAO,eAAe,SAAS,MAAM;;CAEvC,MAAM,YAAY,aAAa;CAE/B,MAAM,kBAAkB,QAAQ,IAAI;CACpC,MAAM,eAAe,mBAAmB;AACxC,KAAI,iBAAiB;EACnB,IAAI,SAAqB;AACzB,MAAI;AACF,YAAS,IAAI,IAAI,gBAAgB;UAC3B;AAGR,MAAI,CAAC,UAAW,OAAO,aAAa,YAAY,OAAO,aAAa,SAAU;AAC5E,aACE,EAAE,QAAQ,yBAAyB,EACnC,+CAA+C,kBAChD;AACD,UAAO,eAAe,SAAS,MAAM;;AAEvC,MAAI,CAAC,uBAAuB,OAAO,SAAS,EAAE;AAC5C,aACE;IAAE,QAAQ;IAA8B,MAAM,OAAO;IAAU,EAC/D,oBAAoB,OAAO,SAAS,iDACrC;AACD,UAAO,eAAe,SAAS,MAAM;;AAEvC,UAAQ,OAAO,MAAM,oDAAoD,aAAa,IAAI;;CAK5F,MAAM,WAAW,MAAM,2BAA2B,MAAM,KAAK;AAC7D,KAAI,SAAS,SAAS,SAAS;AAC7B,YAAU;GAAE,QAAQ,SAAS;GAAQ,SAAS,SAAS;GAAS,EAAE,SAAS,QAAQ;AACnF,SAAO,eAAe,SAAS,SAAS;;CAS1C,IAAI,QAA2C;AAC/C,KAAI,SAAS,eAAe,cAAc,SAAS,gBAAgB,MAAM;EACvE,MAAM,OAAO,KAAK;AAClB,MAAI,CAAC,MAAM;AACT,aACE;IAAE,QAAQ;IAAoB,SAAS;IAA8B,EACrE,kDACD;AACD,UAAO,eAAe,SAAS,QAAQ;;AAEzC,MAAI;GACF,MAAM,SAAS,MAAM,KAAK,SAAS,YAAY,OAAO,SAAS,YAAY,SAAS;AACpF,WAAQ,OAAO;AACf,OAAI,CAAC,KAAK,KACR,KAAI,OAAO,WAAW,YACpB,SAAQ,OAAO,MAAM,2CAA2C;OAEhE,SAAQ,OAAO,MACb,qCAAqC,SAAS,YAAY,MAAM,MACjE;WAGE,KAAK;GACZ,MAAM,UAAW,IAAc;AAC/B,aACE;IAAE,QAAQ;IAAwB;IAAS,EAC3C,kDAAkD,QAAQ,6GAG3D;AACD,UAAO,eAAe,SAAS,MAAM;;;CAIzC,MAAM,cAAyB,gBAAgB;EAC7C,iBAAiB,KAAK;EACtB,gBAAgB,SAAS,gBAAgB;EAC1C,CAAC;CAMF,MAAM,oBAAoB,KAAK,IAAI,KAAQ,KAAK,IAAI,KAAQ,KAAK,MAAM,YAAY,EAAE,CAAC,CAAC;CAIvF,MAAM,oBAAoB,KAAK,KAAK;CACpC,MAAM,SAAS,MAAM,aAAa;EAChC;EACA;EACA;EACA;EACA,MAAM;EACN,aAAa,SAAS;EACtB;EACA;EACD,CAAC;AAEF,KAAI,OAAO,WAAW,2BAA2B;AAC/C,UAAQ,OAAO,MAAM,GAAG,OAAO,QAAQ,IAAI;EAO3C,MAAM,SAAS,MAAM,aAAa;GAChC;GACA,WAHwB,yBAAyB,WAAW,KAAK,KAAK,GAAG,kBAAkB;GAI3F;GACA;GACA,MAAM;GACN,aAAa;GACb;GACA;GACD,CAAC;AACF,MAAI,OAAO,WAAW,OAAQ,QAAO,eAAe,OAAO,KAAK;AAI3B,SAAO;AAE5C,SAAO,eAAe,SAAS,QAAQ;;AAGzC,QAAO,eAAe,OAAO,KAAK;;;;;;;AAQpC,eAAsB,2BACpB,MACA,MACA,OAII,EAAE,EAC6B;CACnC,MAAM,MAAM,KAAK,OAAO,QAAQ;CAChC,MAAM,cAAc,KAAK,eAAe,QAAQ,QAAQ,OAAO,MAAM;CACrE,MAAM,aAAa,KAAK,cAAc,QAAQ,QAAQ,MAAM,MAAM;CAClE,MAAM,iBAAiB,eAAe,cAAc,CAAC,KAAK;AAI1D,KAAI,KAAK,aAAa,KAAA,KAAa,KAAK,cACtC,QAAO;EACL,MAAM;EACN,QAAQ;EACR,SAAS;EACT,UAAU,SAAS;EACpB;AAUH,KACE,KAAK,gBACJ,KAAK,UAAU,KAAA,KACd,KAAK,aAAa,KAAA,KAClB,KAAK,iBACL,KAAK,SAAS,KAAA,GAEhB,QAAO;EACL,MAAM;EACN,QAAQ;EACR,SACE;EAEF,UAAU,SAAS;EACpB;CAKH,IAAI;AACJ,KAAI,KAAK,SAAS,KAAA,GAAW;AAC3B,MAAI,KAAK,SAAS,cAAc,KAAK,SAAS,OAC5C,QAAO;GACL,MAAM;GACN,QAAQ;GACR,SAAS,6CAA6C,KAAK,KAAK;GAChE,UAAU,SAAS;GACpB;AAEH,eAAa,KAAK;;AAIpB,KAAI,KAAK,UAAU,KAAA,KAAa,KAAK,aAAa,KAAA,KAAa,KAAK,eAAe;AACjF,MAAI,KAAK,UAAU,KAAA,KAAa,KAAK,MAAM,MAAM,CAAC,WAAW,EAC3D,QAAO;GACL,MAAM;GACN,QAAQ;GACR,SAAS;GACT,UAAU,SAAS;GACpB;AAEH,MAAI,CAAC,KAAK,MAAM,SAAS,IAAI,CAC3B,QAAO;GACL,MAAM;GACN,QAAQ;GACR,SAAS,kBAAkB,KAAK;GAChC,UAAU,SAAS;GACpB;EAGH,IAAI;AACJ,MAAI,KAAK,eAAe;AAGtB,cAAW,qBADC,OADG,KAAK,aAAa,eACP,CACU;AACpC,OAAI,SAAS,WAAW,EACtB,QAAO;IACL,MAAM;IACN,QAAQ;IACR,SAAS;IACT,UAAU,SAAS;IACpB;aAEM,KAAK,aAAa,KAAA,GAAW;AAItC,WAAQ,OAAO,MACb,qIAED;AACD,cAAW,KAAK;AAChB,OAAI,SAAS,WAAW,EACtB,QAAO;IACL,MAAM;IACN,QAAQ;IACR,SAAS;IACT,UAAU,SAAS;IACpB;QAMH,QAAO;GACL,MAAM;GACN,QAAQ;GACR,SACE;GACF,UAAU,SAAS;GACpB;AAGH,SAAO;GACL,MAAM;GACN,aAAa;IAAE,QAAQ;IAAQ,OAAO,KAAK,MAAM,MAAM;IAAE;IAAU;GACnE,YAAY,cAAc;GAC3B;;AAMH,KAAI,KAAK,YACP,QAAO;EAAE,MAAM;EAAM,aAAa;EAAM,YAAY,cAAc;EAAQ;AAI5E,KAAI,IAAI,eAAe,IAAI,eACzB,QAAO;EACL,MAAM;EACN,aAAa;GAAE,QAAQ;GAAO,OAAO,IAAI;GAAa,UAAU,IAAI;GAAgB;EAGpF,YAAY,cAAc;EAC3B;CAIH,MAAM,iBAAiB,KAAK;AAC5B,KAAI,gBAAgB;EAClB,MAAM,YAAY,MAAM,gBAAgB,CAAC,OAAO,QAAe;AAG7D,WAAQ,OAAO,MAAM,6BAA6B,IAAI,QAAQ,gBAAgB;AAC9E,UAAO;IACP;AACF,MAAI,WAAW;AAKb,OAAI,CAAC,KAAK,KACR,SAAQ,OAAO,MACb,0CAA0C,UAAU,MAAM;EAE3D;AAEH,UAAO;IACL,MAAM;IACN,aAAa;KACX,QAAQ,UAAU;KAClB,OAAO,UAAU;KACjB,UAAU,UAAU;KACrB;IAGD,YAAY,cAAc;IAC3B;;;AAOL,KAAI,gBAAgB;EAClB,MAAM,UAAU,KAAK,WAAW;EAChC,IAAI;EACJ,IAAI;AACJ,MAAI;AACF,WAAQ,MAAM,QAAQ,OAAO;AAC7B,cAAW,MAAM,QAAQ,UAAU;WAC5B,KAAK;AACZ,OAAI,kBAAkB,IAAI,CACxB,QAAO;IACL,MAAM;IACN,QAAQ;IACR,SAAS;IACT,UAAU,SAAS;IACpB;AAEH,SAAM;;EAGR,IAAI;AACJ,MAAI,eAAe,KAAA,EAGjB,gBAAe;MAEf,KAAI;AACF,kBAAe,MAAM,QAAQ,YAAY;WAClC,KAAK;AACZ,OAAI,kBAAkB,IAAI,CACxB,QAAO;IACL,MAAM;IACN,QAAQ;IACR,SAAS;IACT,UAAU,SAAS;IACpB;AAEH,SAAM;;AAIV,SAAO;GACL,MAAM;GACN,aAAa;IAAE,QAAQ;IAAU;IAAO;IAAU;GAClD,YAAY;GACb;;AAKH,QAAO;EACL,MAAM;EACN,QAAQ;EACR,SACE;EAEF,UAAU,SAAS;EACpB;;AAkBH,eAAe,aAAa,MAA8C;CACxE,MAAM,EAAE,MAAM,WAAW,mBAAmB,cAAc,MAAM,aAAa,OAAO,cAClF;CAGF,MAAM,WAAW,MAAM,aAAa;EAClC,YAAY;EACZ;EACA,UALe,SAAS;EAMzB,CAAC,CAAC,OAAO,QAAe,IAAI;AAC7B,KAAI,oBAAoB,qBAAqB;AAC3C,YAAU;GAAE,QAAQ;GAAoB,YAAY,SAAS;GAAY,EAAE,SAAS,QAAQ;AAC5F,SAAO;GAAE,QAAQ;GAAQ,MAAM,SAAS;GAAsB;;AAEhE,KAAI,oBAAoB,qBAAqB,oBAAoB,4BAA4B;AAC3F,YACE;GAAE,QAAQ;GAAwB,SAAS,SAAS;GAAS,EAC7D,6BAA6B,SAAS,UACvC;AACD,SAAO;GAAE,QAAQ;GAAQ,MAAM,SAAS;GAAoB;;AAE9D,KAAI,oBAAoB,OAAO;AAC7B,YACE;GAAE,QAAQ;GAAwB,WAAW,SAAS;GAAM,SAAS,SAAS;GAAS,EACvF,6BAA6B,SAAS,KAAK,KAAK,SAAS,UAC1D;AACD,SAAO;GAAE,QAAQ;GAAQ,MAAM,SAAS;GAAoB;;AAG9D,KAAI,SAAS,cACX,SAAQ,OAAO,MACb,0GACD;MACI;EACL,MAAM,cAAc,aAAa,UAAU;AAC3C,UAAQ,OAAO,MAAM,+CAA+C,YAAY,KAAK;;CAMvF,IAAI,SAA2B;CAC/B,MAAM,aAAa,YAA2B;AAC5C,MAAI,QAAQ;AACV,SAAM,OAAO,OAAO,CAAC,YAAY,GAAG;AACpC,YAAS;;AAEX,QAAM,SAAS,SAAS,CAAC,YAAY,GAAG;;AAG1C,KAAI;AACF,WAAS,MAAM,UAAU,QAAQ,EAAE,KAAK,SAAS,sBAAsB,CAAC;UACjE,KAAK;AACZ,YACE;GAAE,QAAQ;GAAsB,SAAU,IAAc;GAAS,EACjE,8CAA+C,IAAc,UAC9D;AACD,QAAM,YAAY;AAClB,SAAO;GAAE,QAAQ;GAAQ,MAAM,SAAS;GAAoB;;CAG9D,IAAI;AACJ,KAAI;AACF,aAAW,MAAM,kBAAkB,OAAO;UACnC,KAAK;AACZ,YACE;GAAE,QAAQ;GAAqB,SAAU,IAAc;GAAS,EAChE,wCAAyC,IAAc,UACxD;AACD,QAAM,YAAY;AAClB,SAAO;GAAE,QAAQ;GAAQ,MAAM,SAAS;GAAoB;;CAG9D,IAAI,SAAS;AACb,KAAI,SAAS,YAAY;AACvB,MAAI,CAAC,aAAa;AAEhB,SAAM,YAAY;AAClB,UAAO;IACL,QAAQ;IACR,SAAS;IACV;;EAEH,IAAI;AACJ,MAAI;AACF,aAAU,MAAM,iBAAiB;IAC/B;IACA,WAAW,SAAS;IACpB,aAAa;KAAE,OAAO,YAAY;KAAO,UAAU,YAAY;KAAU;IACzE,iBAAiB;IACjB,gBACE,QAAQ,OAAO,MACb,8EACD;IACJ,CAAC;WACK,KAAK;AAGZ,aACE;IAAE,QAAQ;IAAyB,SAAU,IAAc;IAAS,EACpE,0BAA2B,IAAc,UAC1C;AACD,SAAM,YAAY;AAClB,UAAO;IAAE,QAAQ;IAAQ,MAAM,SAAS;IAAoB;;AAG9D,MAAI,QAAQ,SAAS,YAAY;AAC/B,SAAM,YAAY;AAClB,UAAO;IACL,QAAQ;IACR,SAAS,0BAA0B,QAAQ,OAAO;IACnD;;AAEH,MAAI,QAAQ,SAAS,WAAW;AAC9B,aACE;IAAE,QAAQ;IAAiB,YAAY,KAAK,MAAM,YAAY,IAAK;IAAE,OAAO,QAAQ;IAAO,EAC3F,yBAAyB,KAAK,MAAM,YAAY,IAAK,CAAC,KAAK,QAAQ,MAAM,IAC1E;AACD,SAAM,YAAY;AAClB,UAAO;IAAE,QAAQ;IAAQ,MAAM,SAAS;IAAc;;AAExD,WAAS,QAAQ;QACZ;EACL,MAAM,UAAU,MAAM,eAAe,QAAQ,SAAS,WAAW,UAAU;AAC3E,MAAI,YAAY,WAAW;AACzB,aACE;IAAE,QAAQ;IAAiB,YAAY,KAAK,MAAM,YAAY,IAAK;IAAE,EACrE,yBAAyB,KAAK,MAAM,YAAY,IAAK,CAAC,IACvD;AACD,SAAM,YAAY;AAClB,UAAO;IAAE,QAAQ;IAAQ,MAAM,SAAS;IAAc;;AAExD,MAAI,YAAY,WAAW;AACzB,aACE,EAAE,QAAQ,iBAAiB,EAC3B,kEACD;AACD,SAAM,YAAY;AAClB,UAAO;IAAE,QAAQ;IAAQ,MAAM,SAAS;IAAoB;;;CAMhE,MAAM,UAAU,MAAM,cAAc,QAAQ,SAAS,UAAU,CAAC,OAAO,QAAe,IAAI;AAC1F,KAAI,mBAAmB,OAAO;AAC5B,YACE;GAAE,QAAQ;GAAyB,SAAS,QAAQ;GAAS,EAC7D,8BAA8B,QAAQ,UACvC;AACD,QAAM,YAAY;AAClB,SAAO;GAAE,QAAQ;GAAQ,MAAM,SAAS;GAA0B;;CAGpE,MAAM,OAAO,MAAM,qBAAqB,SAAS,EAC/C,UAAU,OACR,QAAQ,OAAO,MACb,6DAA6D,GAAG,SACjE,EACJ,CAAC,CAAC,OAAO,QAAe,IAAI;AAC7B,KAAI,gBAAgB,OAAO;EACzB,MAAM,aAAa,gBAAgB,gBAAgB,KAAK;AACxD,YACE;GACE,QAAQ,aAAa,0BAA0B;GAC/C,SAAS,KAAK;GACf,EACD,aACI,8GACA,+BAA+B,KAAK,UACzC;AACD,QAAM,YAAY;AAClB,SAAO;GACL,QAAQ;GACR,MAAM,aAAa,SAAS,2BAA2B,SAAS;GACjE;;CAGH,MAAM,UAAmB;EACvB,eAAe;EACf,MAAM;GACJ,IAAI,OAAO,KAAK,GAAG;GACnB,OAAO,KAAK;GACZ,aAAa,KAAK;GACnB;EACD;EACA,SAAS,EAAE;EACX,6BAAY,IAAI,MAAM,EAAC,aAAa;EACrC;AACD,KAAI;AACF,QAAM,aAAa,QAAQ;UACpB,KAAK;AACZ,YACE;GAAE,QAAQ;GAAwB,SAAU,IAAc;GAAS,EACnE,iCAAkC,IAAc,UACjD;AACD,QAAM,YAAY;AAClB,SAAO;GAAE,QAAQ;GAAQ,MAAM,SAAS;GAAS;;AAGnD,KAAI,KAAK,KACP,SAAQ,OAAO,MACb,GAAG,KAAK,UAAU;EAChB,IAAI;EACJ,QAAQ;EACR,MAAM,QAAQ;EACd,YAAY,QAAQ;EACpB,aAAa,QAAQ;EACrB;EACA,kBAAkB,aAAa,UAAU;EACzC;EACA;EACD,CAAC,CAAC,IACJ;KAED,SAAQ,OAAO,MAAM,gBAAgB,KAAK,KAAK,IAAI,KAAK,MAAM,KAAK;AAGrE,OAAM,YAAY;AAClB,QAAO;EAAE,QAAQ;EAAQ,MAAM,SAAS;EAAI;;AAG9C,eAAe,eAAgC;AAC7C,KAAI,QAAQ,MAAM,MAGhB,OAAM,IAAI,MAAM,2DAA2D;AAE7E,SAAQ,MAAM,YAAY,OAAO;CACjC,IAAI,MAAM;AACV,YAAW,MAAM,SAAS,QAAQ,MAChC,QAAO;AAET,QAAO;;AAGT,SAAS,qBAAqB,GAAmB;AAC/C,QAAO,EAAE,QAAQ,UAAU,GAAG;;AAOhC,SAAS,kBAAkB,KAAuB;AAChD,QAAO,eAAe,SAAS,IAAI,SAAS;;AAG9C,eAAsB,eACpB,QACA,WACA,WACuC;AAQvC,QAAO,MAAM,IAAI,SAAuC,YAAY;EAClE,IAAI,UAAU;EACd,MAAM,QAA2C,EAAE;EACnD,MAAM,UAAU,YAA0C;AACxD,OAAI,QAAS;AACb,aAAU;AACV,gBAAa,MAAM;AACnB,iBAAc,UAAU;AACxB,QAAK,MAAM,KAAK,MACd,KAAI;AACG,OAAG;WACF;AAIV,WAAQ,QAAQ;;EAGlB,MAAM,QAAQ,iBAAiB,OAAO,UAAU,EAAE,UAAU;AAC5D,MAAI,OAAO,MAAM,UAAU,WAAY,OAAM,OAAO;AAGpD,QAAM,KACJ,OAAO,IAAI,UAAU;AACnB,OAAI,MAAM,WAAW,yBAA0B,QAAO,UAAU;IAChE,CACH;AAKD,4BAA0B,QAAQ,YAAY,OAAO;AACnD,OAAI,CAAC,GAAG,YAAa;AACrB,OAAI,eAAe,GAAG,IAAI,CAAE,QAAO,KAAK;IACxC,CACC,MAAM,QAAQ;AAIb,OAAI,QAAS,MAAK;OACb,OAAM,KAAK,IAAI;IACpB,CACD,OAAO,QAAe;AACrB,OAAI,QAAS;AACb,WAAQ,OAAO,MAAM,mCAAmC,IAAI,QAAQ,IAAI;IACxE;EAGJ,MAAM,eAAe,YAAY;AAC/B,OAAI,QAAS;GAQb,MAAM,OAPO,MAAM,OAChB,KACC,qBACA,EAAE,EACF,UACD,CACA,YAAY,KAAK,GACF,UAAU,OAAO;AACnC,OAAI,OAAO,eAAe,IAAI,CAAE,QAAO,KAAK;;AAGzC,gBAAc;EACnB,MAAM,YAAY,kBAAkB;AAC7B,iBAAc;KAClB,IAAK;AACR,MAAI,OAAO,UAAU,UAAU,WAAY,WAAU,OAAO;GAC5D;;AASJ,eAAsB,qBACpB,SACA,OAGI,EAAE,EAC2D;CACjE,MAAM,WAAW,KAAK,YAAY,EAAE,WAAW,KAAK,WAAW,GAAG,EAAE;AACpE,KAAI;AACF,SAAO,MAAM,2BAA2B,SAAS,SAAS;UACnD,KAAK;AACZ,MAAI,eAAe,gBAAgB,IAAI,aAAa;AAClD,QAAK,UAAA,IAA+B;AACpC,SAAM,IAAI,SAAS,MAAM;IACvB,MAAM,IAAI,WAAW,GAAA,IAAwB;AAC7C,QAAI,OAAO,EAAE,UAAU,WAAY,GAAE,OAAO;KAC5C;AACF,UAAO,MAAM,2BAA2B,SAAS,SAAS;;AAE5D,QAAM;;;;;AC1hCV,MAAa,gBAAgB,cAAc;CACzC,MAAM;EACJ,MAAM;EACN,aAAa;EACd;CACD,MAAM;EACJ,MAAM;GACJ,MAAM;GACN,aAAa;GACb,SAAS;GACV;EACD,OAAO;GACL,MAAM;GACN,aAAa;GACb,SAAS;GACV;EACF;CACD,MAAM,IAAI,EAAE,QAAQ;EAClB,MAAM,OAAO,2BAA2B;EAExC,IAAI;AACJ,MAAI;AAEF,qBADe,MAAM,cAAc,EACX;WACjB,KAAK;GACZ,MAAM,UAAW,IAAc;AAC/B,OAAI,KAAK,KACP,SAAQ,OAAO,MACb,GAAG,KAAK,UAAU;IAAE,IAAI;IAAO,QAAQ;IAAiB;IAAM;IAAS,CAAC,CAAC,IAC1E;AAEH,WAAQ,OAAO,MAAM,oCAAoC,KAAK,IAAI,QAAQ,IAAI;AAC9E,UAAO,eAAe,SAAS,QAAQ;;EAGzC,IAAI,oBAAoB;EACxB,IAAI,aAA4B;AAChC,MAAI,KAAK,MACP,KAAI;AAEF,wBADe,MAAM,mBAAmB,EACb;WACpB,KAAK;AAIZ,gBAAc,IAAc;;AAIhC,MAAI,KAAK,MAAM;GACb,MAAM,UAAmC;IACvC,IAAI;IACJ;IACA;IACA;IACD;AACD,OAAI,eAAe,KAAM,SAAQ,aAAa;AAC9C,WAAQ,OAAO,MAAM,GAAG,KAAK,UAAU,QAAQ,CAAC,IAAI;SAC/C;AACL,OAAI,eACF,SAAQ,OAAO,MAAM,oCAAoC,KAAK,IAAI;OAElE,SAAQ,OAAO,MAAM,wBAAwB,KAAK,KAAK;AAEzD,OAAI,KAAK,MACP,KAAI,eAAe,KACjB,SAAQ,OAAO,MAAM,uCAAuC,WAAW,IAAI;YAClE,kBACT,SAAQ,OAAO,MAAM,oDAAoD;OAEzE,SAAQ,OAAO,MAAM,oCAAoC;;AAI/D,SAAO,eAAe,SAAS,GAAG;;CAErC,CAAC;;;ACrEF,SAAS,eAAe,GAAqB;AAG3C,QAAO,KAFK,EAAE,WAAW,aAAa,cAC1B,EAAE,WAAW,cAAc,GACjB,IAAI,EAAE,MAAM,QAAQ,EAAE,YAAY;;AAoC1D,MAAa,YAAY,cAAc;CACrC,MAAM;EACJ,MAAM;EACN,aAAa;EACd;CACD,aAAa,EACX,OAvCiB,cAAc;EACjC,MAAM;GACJ,MAAM;GACN,aAAa;GACd;EACD,MAAM,EACJ,MAAM;GAAE,MAAM;GAAW,aAAa;GAAyC,SAAS;GAAO,EAChG;EACD,MAAM,IAAI,EAAE,QAAQ;GAClB,MAAM,UAAU,MAAM,aAAa;AACnC,OAAI,CAAC,SAAS;AACZ,yBAAqB,KAAK,KAAK;AAC/B,WAAO,eAAe,SAAS,iBAAiB;;AAElD,OAAI;IACF,MAAM,QAAQ,MAAM,eAAe,QAAQ,QAAQ;AACnD,QAAI,KAAK,MAAM;AACb,cAAS;MAAE,IAAI;MAAM;MAAO,CAAC;AAC7B,YAAO,eAAe,SAAS,GAAG;;AAEpC,QAAI,MAAM,WAAW,GAAG;AACtB,aAAQ,OAAO,MAAM,qCAAqC;AAC1D,YAAO,eAAe,SAAS,GAAG;;AAEpC,YAAQ,OAAO,MAAM,2BAA2B;AAChD,SAAK,MAAM,KAAK,MAAO,SAAQ,OAAO,MAAM,eAAe,EAAE,CAAC;AAC9D,WAAO,eAAe,SAAS,GAAG;YAC3B,KAAK;AACZ,WAAO,qBAAqB,KAAK,MAAM,IAAI;;;EAGhD,CAAC,EASC;CACF,CAAC;;;ACxDF,MAAME,SAAO;AAab,eAAsB,sBACpB,aACA,SACA,OAAkC,EAAE,EACR;CAE5B,MAAM,MAAM,MAAM,kBAA2B;EAC3C,KAFU,GAAGA,OAAK,cAAc,YAAY;EAG5C;EACA,GAAI,KAAK,YAAY,EAAE,WAAW,KAAK,WAAW,GAAG,EAAE;EACxD,CAAC;AACF,KAAI,CAAC,MAAM,QAAQ,IAAI,CACrB,OAAM,IAAI,MAAM,0CAA0C,YAAY,gBAAgB;AAExF,QAAO,IAAI,KAAK,OAAO,UAAU,gBAAgB,OAAO,aAAa,MAAM,CAAC;;AAG9E,SAAS,gBAAgB,KAAc,aAAqB,OAAgC;AAC1F,KAAI,QAAQ,QAAQ,OAAO,QAAQ,SACjC,OAAM,IAAI,MACR,oCAAoC,MAAM,iBAAiB,YAAY,iBACxE;CAEH,MAAM,MAAM;CACZ,MAAM,eAAe,MAAsB;EACzC,MAAM,IAAI,IAAI;AACd,MAAI,OAAO,MAAM,SACf,OAAM,IAAI,MACR,oCAAoC,MAAM,iBAAiB,YAAY,YAAY,IACpF;AAEH,SAAO;;CAET,MAAM,YAAY,MAAsB;EACtC,MAAM,IAAI,IAAI;AACd,MAAI,OAAO,MAAM,YAAY,CAAC,OAAO,SAAS,EAAE,CAC9C,OAAM,IAAI,MACR,oCAAoC,MAAM,iBAAiB,YAAY,YAAY,IACpF;AAEH,SAAO;;AAET,QAAO;EACL,aAAa,SAAS,cAAc;EACpC,WAAW,SAAS,YAAY;EAChC,MAAM,YAAY,OAAO;EACzB,OAAO,YAAY,QAAQ;EAC3B,QAAQ,YAAY,SAAS;EAC7B,MAAM,YAAY,OAAO;EACzB,4BAA4B,QAAQ,IAAI,2BAA2B;EACnE,SAAS,QAAQ,IAAI,QAAQ;EAC9B;;ACGH,MAAa,iBAAiB,cAAc;CAC1C,MAAM;EACJ,MAAM;EACN,aAAa;EACd;CACD,aAAa,EACX,IA9Dc,cAAc;EAC9B,MAAM;GACJ,MAAM;GACN,aAAa;GACd;EACD,MAAM;GACJ,WAAW;IACT,MAAM;IACN,aAAa;IACd;GACD,MAAM;IAAE,MAAM;IAAW,aAAa;IAAyC,SAAS;IAAO;GAChG;EACD,MAAM,IAAI,EAAE,QAAQ;GAClB,MAAM,MAAM,MAAM,wBAAwB,KAAK;AAC/C,OAAI,CAAC,IAAK;GACV,MAAM,EAAE,SAAS,gBAAgB;AACjC,sBAAmB,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;AAE5C,OAAI;IACF,MAAM,UAAU,MAAM,sBAAsB,aAAa,QAAQ,QAAQ;AACzE,QAAI,KAAK,MAAM;AAQb,cAAS;MACP,IAAI;MACJ;MACA,SAAS,QAAQ,KAAK,OAAO;OAC3B,WAAW,EAAE;OACb,MAAM,EAAE;OACR,OAAO,EAAE;OACT,QAAQ,EAAE;OACV,MAAM,EAAE;OACR,4BAA4B,EAAE;OAC/B,EAAE;MACJ,CAAC;AACF,YAAO,eAAe,SAAS,GAAG;;AAEpC,QAAI,QAAQ,WAAW,GAAG;AACxB,aAAQ,OAAO,MAAM,2BAA2B,YAAY,KAAK;AACjE,YAAO,eAAe,SAAS,GAAG;;AAEpC,SAAK,MAAM,KAAK,QACd,SAAQ,OAAO,MAAM,GAAG,EAAE,UAAU,IAAI,EAAE,KAAK,IAAI,EAAE,MAAM,IAAI,EAAE,KAAK,IAAI,EAAE,OAAO,IAAI;AAEzF,WAAO,eAAe,SAAS,GAAG;YAC3B,KAAK;AACZ,WAAO,qBAAqB,KAAK,MAAM,IAAI;;;EAGhD,CAAC,EASC;CACF,CAAC;ACpEF,MAAM,OAAO;AA6Bb,eAAsB,aACpB,QACA,SACA,OAAkC,EAAE,EACV;CAC1B,MAAM,KAAK,IAAI,iBAAiB;AAChC,IAAG,IAAI,QAAQ,OAAO,OAAO,QAAQ,EAAE,CAAC;AACxC,IAAG,IAAI,QAAQ,OAAO,OAAO,QAAQ,GAAG,CAAC;AACzC,KAAI,OAAO,kBAAkB,KAAA,EAC3B,IAAG,IAAI,oBAAoB,OAAO,cAAc;CAIlD,MAAM,MAAM,MAAM,kBAA2B;EAC3C,KAHU,GAAG,KAAK,wBAA6C,GAAG,UAAU;EAI5E;EACA,GAAI,KAAK,YAAY,EAAE,WAAW,KAAK,WAAW,GAAG,EAAE;EACxD,CAAC;AACF,KAAI,QAAQ,QAAQ,OAAO,QAAQ,SACjC,OAAM,IAAI,MAAM,0CAA0C;CAE5D,MAAM,MAAM;CACZ,MAAM,aAAa,IAAI;AACvB,KAAI,CAAC,MAAM,QAAQ,WAAW,CAC5B,OAAM,IAAI,MAAM,oDAAoD;CAEtE,MAAM,UAAU,WAAW,KAAK,MAAM;AACpC,MAAI,MAAM,QAAQ,OAAO,MAAM,SAAU,QAAO,EAAE;AAClD,SAAO;GACP;AACF,QAAO;EACL,MAAM,OAAO,IAAI,SAAS,WAAW,IAAI,OAAO;EAChD,UAAU,OAAO,IAAI,aAAa,WAAW,IAAI,WAAY,OAAO,QAAQ;EAC5E,OAAO,OAAO,IAAI,UAAU,WAAW,IAAI,QAAQ,QAAQ;EAC3D,MAAM,OAAO,IAAI,SAAS,WAAW,IAAI,OAAO;EAChD,UAAU,OAAO,IAAI,aAAa,WAAW,IAAI,WAAW;EAC5D;EACD;;AAUH,eAAsB,sBACpB,SACA,OAAkC,EAAE,EACmB;CAEvD,MAAM,MAAM,MAAM,kBAA2B;EAC3C,KAFU,GAAG,KAAK;EAGlB;EACA,GAAI,KAAK,YAAY,EAAE,WAAW,KAAK,WAAW,GAAG,EAAE;EACxD,CAAC;AACF,KAAI,CAAC,MAAM,QAAQ,IAAI,CACrB,OAAM,IAAI,MAAM,4CAA4C;AAE9D,QAAO,IAAI,KAAK,MAAM;AACpB,MAAI,MAAM,QAAQ,OAAO,MAAM,SAAU,QAAO,EAAE;AAClD,SAAO;GACP;;AAUJ,eAAsB,gBACpB,QACA,SACA,OAAkC,EAAE,EACF;CAElC,MAAM,MAAM,MAAM,kBAA2B;EAC3C,KAFU,GAAG,KAAK,wBAA6C;EAG/D;EACA,GAAI,KAAK,YAAY,EAAE,WAAW,KAAK,WAAW,GAAG,EAAE;EACxD,CAAC;AACF,KAAI,QAAQ,QAAQ,OAAO,QAAQ,YAAY,MAAM,QAAQ,IAAI,CAC/D,OAAM,IAAI,MAAM,uCAAuC,SAAS;AAElE,QAAO;;;;AC5GT,SAAS,iBAAiB,KAAa,OAAsD;CAC3F,MAAM,IAAI,OAAO,IAAI;AACrB,KAAI,CAAC,OAAO,SAAS,EAAE,IAAI,CAAC,OAAO,UAAU,EAAE,IAAI,IAAI,EACrD,QAAO,EAAE,OAAO,KAAK,MAAM,uCAAuC,KAAK,UAAU,IAAI,CAAC,IAAI;AAE5F,QAAO,EAAE,OAAO,GAAG;;AAoLrB,MAAa,iBAAiB,cAAc;CAC1C,MAAM;EACJ,MAAM;EACN,aAAa;EACd;CACD,aAAa;EACX,IAvLc,cAAc;GAC9B,MAAM;IACJ,MAAM;IACN,aAAa;IACd;GACD,MAAM;IACJ,MAAM;KAAE,MAAM;KAAU,aAAa;KAA4B,SAAS;KAAK;IAC/E,MAAM;KAAE,MAAM;KAAU,aAAa;KAAc,SAAS;KAAM;IAClE,QAAQ;KAAE,MAAM;KAAU,aAAa;KAAiD;IACxF,MAAM;KAAE,MAAM;KAAW,aAAa;KAA+B,SAAS;KAAO;IACtF;GACD,MAAM,IAAI,EAAE,QAAQ;IAClB,MAAM,UAAU,MAAM,eAAe,KAAK,KAAK;AAC/C,QAAI,CAAC,QAAS;IACd,MAAM,aAAa,iBAAiB,KAAK,MAAM,OAAO;AACtD,QAAI,WAAW,YAAY;AACzB,SAAI,KAAK,KACP,UAAS;MAAE,IAAI;MAAO,QAAQ;MAAkB,OAAO;MAAQ,SAAS,WAAW;MAAO,CAAC;SAE3F,SAAQ,OAAO,MAAM,eAAe,WAAW,MAAM,IAAI;AAE3D,YAAO,eAAe,SAAS,MAAM;;IAEvC,MAAM,aAAa,iBAAiB,KAAK,MAAM,OAAO;AACtD,QAAI,WAAW,YAAY;AACzB,SAAI,KAAK,KACP,UAAS;MAAE,IAAI;MAAO,QAAQ;MAAkB,OAAO;MAAQ,SAAS,WAAW;MAAO,CAAC;SAE3F,SAAQ,OAAO,MAAM,eAAe,WAAW,MAAM,IAAI;AAE3D,YAAO,eAAe,SAAS,MAAM;;AAEvC,QAAI,WAAW,UAAU,GAAG;AAC1B,SAAI,KAAK,KACP,UAAS;MACP,IAAI;MACJ,QAAQ;MACR,OAAO;MACP,SAAS;MACV,CAAC;SAEF,SAAQ,OAAO,MAAM,0CAA0C;AAEjE,YAAO,eAAe,SAAS,MAAM;;AAGvC,QAAI;KACF,MAAM,SAAS,MAAM,aACnB;MACE,MAAM,WAAW;MACjB,MAAM,WAAW;MACjB,GAAI,OAAO,KAAK,WAAW,YAAY,KAAK,OAAO,SAAS,IACxD,EAAE,eAAe,KAAK,QAAQ,GAC9B,EAAE;MACP,EACD,QAAQ,QACT;AAED,SAAI,KAAK,MAAM;AACb,eAAS;OACP,IAAI;OACJ,MAAM,OAAO;OACb,UAAU,OAAO;OACjB,OAAO,OAAO;OACd,SAAS,OAAO,SAAS;OACzB,SAAS,OAAO;OACjB,CAAC;AACF,aAAO,eAAe,SAAS,GAAG;;AAGpC,aAAQ,OAAO,MACb,iBAAiB,OAAO,KAAK,IAAI,OAAO,QAAQ,OAAO,GAAG,OAAO,MAAM,UACxE;AACD,SAAI,OAAO,QAAQ,WAAW,GAAG;AAC/B,cAAQ,OAAO,MAAM,6BAA6B;AAClD,aAAO,eAAe,SAAS,GAAG;;AAEpC,UAAK,MAAM,KAAK,OAAO,SAAS;MAC9B,MAAM,KAAK,OAAO,EAAE,OAAO,YAAY,OAAO,EAAE,OAAO,WAAW,EAAE,KAAK;MACzE,MAAM,WAAW,OAAO,EAAE,aAAa,WAAW,EAAE,WAAW;MAC/D,MAAM,QAAQ,OAAO,EAAE,UAAU,WAAW,EAAE,QAAQ;MACtD,MAAM,gBAAgB,OAAO,EAAE,kBAAkB,WAAW,EAAE,gBAAgB;AAC9E,cAAQ,OAAO,MAAM,GAAG,GAAG,IAAI,cAAc,KAAK,SAAS,KAAK,MAAM,IAAI;;AAE5E,SAAI,OAAO,SAAS,KAClB,SAAQ,OAAO,MAAM,iBAAiB,OAAO,OAAO,EAAE,KAAK;AAE7D,YAAO,eAAe,SAAS,GAAG;aAC3B,KAAK;AACZ,YAAO,qBAAqB,KAAK,MAAM,IAAI;;;GAGhD,CAAC;EA4FE,MA1FgB,cAAc;GAChC,MAAM;IACJ,MAAM;IACN,aAAa;IACd;GACD,MAAM;IACJ,IAAI;KAAE,MAAM;KAAc,aAAa;KAAmB,UAAU;KAAM;IAC1E,MAAM;KAAE,MAAM;KAAW,aAAa;KAA+B,SAAS;KAAO;IACtF;GACD,MAAM,IAAI,EAAE,QAAQ;IAClB,MAAM,SAAS,OAAO,KAAK,GAAG;AAC9B,QAAI,CAAC,OAAO,SAAS,OAAO,IAAI,CAAC,OAAO,UAAU,OAAO,IAAI,UAAU,GAAG;AACxE,SAAI,KAAK,KACP,UAAS;MACP,IAAI;MACJ,QAAQ;MACR,SAAS,6CAA6C,KAAK,UAAU,KAAK,GAAG,CAAC;MAC/E,CAAC;SAEF,SAAQ,OAAO,MAAM,4BAA4B,KAAK,UAAU,KAAK,GAAG,CAAC,IAAI;AAE/E,YAAO,eAAe,SAAS,MAAM;;IAEvC,MAAM,UAAU,MAAM,eAAe,KAAK,KAAK;AAC/C,QAAI,CAAC,QAAS;AACd,QAAI;KACF,MAAM,SAAS,MAAM,gBAAgB,QAAQ,QAAQ,QAAQ;AAC7D,SAAI,KAAK,MAAM;AACb,eAAS;OAAE,IAAI;OAAM,IAAI;OAAQ;OAAQ,CAAC;AAC1C,aAAO,eAAe,SAAS,GAAG;;KAEpC,MAAM,QAAQ,OAAO,OAAO,UAAU,WAAW,OAAO,QAAQ;KAChE,MAAM,WAAW,OAAO,OAAO,aAAa,WAAW,OAAO,WAAW;KACzE,MAAM,WAAW,OAAO,OAAO,aAAa,WAAW,OAAO,WAAW;KACzE,MAAM,gBAAgB,OAAO,OAAO,kBAAkB,WAAW,OAAO,gBAAgB;KACxF,MAAM,OACJ,OAAO,OAAO,oBAAoB,WAC9B,OAAO,kBACP,OAAO,OAAO,qBAAqB,WACjC,OAAO,mBACP;AACR,aAAQ,OAAO,MAAM,KAAK,MAAM,IAAI;AACpC,SAAI,SAAU,SAAQ,OAAO,MAAM,GAAG,SAAS,IAAI;AACnD,aAAQ,OAAO,MAAM,MAAM,SAAS,IAAI,cAAc,MAAM;AAC5D,aAAQ,OAAO,MAAM,KAAK;AAC1B,SAAI,CAAC,KAAK,SAAS,KAAK,CAAE,SAAQ,OAAO,MAAM,KAAK;AACpD,YAAO,eAAe,SAAS,GAAG;aAC3B,KAAK;AACZ,YAAO,qBAAqB,KAAK,MAAM,IAAI;;;GAGhD,CAAC;EAwCE,YAtCsB,cAAc;GACtC,MAAM;IACJ,MAAM;IACN,aAAa;IACd;GACD,MAAM,EACJ,MAAM;IAAE,MAAM;IAAW,aAAa;IAA+B,SAAS;IAAO,EACtF;GACD,MAAM,IAAI,EAAE,QAAQ;IAClB,MAAM,UAAU,MAAM,eAAe,KAAK,KAAK;AAC/C,QAAI,CAAC,QAAS;AACd,QAAI;KACF,MAAM,aAAa,MAAM,sBAAsB,QAAQ,QAAQ;AAC/D,SAAI,KAAK,MAAM;AACb,eAAS;OAAE,IAAI;OAAM;OAAY,CAAC;AAClC,aAAO,eAAe,SAAS,GAAG;;AAEpC,UAAK,MAAM,KAAK,YAAY;MAC1B,MAAM,KAAK,OAAO,EAAE,OAAO,YAAY,OAAO,EAAE,OAAO,WAAW,EAAE,KAAK;MACzE,MAAM,OAAO,OAAO,EAAE,SAAS,WAAW,EAAE,OAAO;MACnD,MAAM,YAAY,OAAO,EAAE,cAAc,WAAW,EAAE,YAAY;AAClE,cAAQ,OAAO,MAAM,GAAG,GAAG,IAAI,UAAU,IAAI,KAAK,IAAI;;AAExD,YAAO,eAAe,SAAS,GAAG;aAC3B,KAAK;AACZ,YAAO,qBAAqB,KAAK,MAAM,IAAI;;;GAGhD,CAAC;EAWC;CACF,CAAC;;;ACzNF,MAAM,aAAa;AACnB,MAAM,YAAY;AAelB,SAAS,iBAA8B;CACrC,MAAM,UAAkC;EACtC,QAAQ;EACR,cAAc;EACd,wBAAwB;EACzB;CACD,MAAM,QAAQ,QAAQ,IAAI;AAC1B,KAAI,SAAS,MAAM,SAAS,EAC1B,SAAQ,gBAAgB,UAAU;AAEpC,QAAO;;AAGT,eAAsB,qBAAuC;CAC3D,MAAM,MAAM,gCAAgC,WAAW,GAAG,UAAU;CACpE,MAAM,MAAM,MAAM,MAAM,KAAK,EAAE,SAAS,gBAAgB,EAAE,CAAC;AAC3D,KAAI,CAAC,IAAI,GACP,OAAM,IAAI,MAAM,mCAAmC,IAAI,OAAO,GAAG,IAAI,aAAa;AAEpF,QAAQ,MAAM,IAAI,MAAM;;;;;;;;AAa1B,eAAsB,8BACpB,cACmC;CACnC,MAAM,MAAM,gCAAgC,WAAW,GAAG,UAAU;CACpE,MAAM,UAAU,gBAAgB;AAChC,KAAI,gBAAgB,aAAa,SAAS,EACxC,SAAQ,mBAAmB;CAE7B,MAAM,MAAM,MAAM,MAAM,KAAK,EAAE,SAAS,CAAC;CACzC,MAAM,OAAO,IAAI,QAAQ,IAAI,OAAO,IAAI,KAAA;AACxC,KAAI,IAAI,WAAW,IACjB,QAAO;EAAE,QAAQ;EAAgB;EAAM;AAEzC,KAAI,CAAC,IAAI,GACP,OAAM,IAAI,MAAM,mCAAmC,IAAI,OAAO,GAAG,IAAI,aAAa;AAGpF,QAAO;EAAE,QAAQ;EAAW,SADX,MAAM,IAAI,MAAM;EACI;EAAM;;AAG7C,SAAgB,oBAAoB,SAA4C;AAC9E,QAAO,QAAQ,OAAO,MAAM,MAAM,EAAE,SAAS,aAAa;;AAM5D,SAAgB,eAAe,KAAqB;CAClD,MAAM,KAAK,IAAI,YAAY,IAAI;CAC/B,MAAM,YAAY,MAAM,IAAI,IAAI,MAAM,KAAK,EAAE,GAAG;AAChD,QAAO,UAAU,WAAW,IAAI,GAAG,UAAU,MAAM,EAAE,GAAG;;;;ACvE1D,SAAgB,iBAAwC;CACtD,IAAI;AACJ,SAAQ,QAAQ,UAAhB;EACE,KAAK;AACH,QAAK;AACL;EACF,KAAK;AACH,QAAK;AACL;EACF,KAAK;AACH,QAAK;AACL;EACF,QACE,QAAO;;CAGX,IAAI;AACJ,SAAQ,QAAQ,MAAhB;EACE,KAAK;AACH,UAAO;AACP;EACF,KAAK;AACH,UAAO;AACP;EACF,QACE,QAAO;;AAIX,KAAI,OAAO,aAAa,SAAS,QAAS,QAAO;AAGjD,QAAO;EAAE;EAAI;EAAM,WAAW,SAAS,GAAG,GAAG,OAD9B,OAAO,YAAY,SAAS;EACmB;;;;ACrChE,SAAgB,YACd,GACqE;CACrE,MAAM,IAAI,6CAA6C,KAAK,EAAE;AAC9D,KAAI,CAAC,EAAG,QAAO;AACf,QAAO;EAAE,OAAO,CAAC,EAAE;EAAK,OAAO,CAAC,EAAE;EAAK,OAAO,CAAC,EAAE;EAAK,KAAK,EAAE,MAAM;EAAI;;AAKzE,SAAgB,cAAc,GAAW,GAAmB;CAC1D,MAAM,KAAK,YAAY,EAAE;CACzB,MAAM,KAAK,YAAY,EAAE;AACzB,KAAI,CAAC,MAAM,CAAC,GAAI,QAAO;AACvB,KAAI,GAAG,UAAU,GAAG,MAAO,QAAO,GAAG,QAAQ,GAAG,QAAQ,IAAI;AAC5D,KAAI,GAAG,UAAU,GAAG,MAAO,QAAO,GAAG,QAAQ,GAAG,QAAQ,IAAI;AAC5D,KAAI,GAAG,UAAU,GAAG,MAAO,QAAO,GAAG,QAAQ,GAAG,QAAQ,IAAI;AAE5D,KAAI,GAAG,QAAQ,GAAG,IAAK,QAAO;AAC9B,KAAI,GAAG,QAAQ,GAAI,QAAO;AAC1B,KAAI,GAAG,QAAQ,GAAI,QAAO;AAC1B,QAAO,GAAG,MAAM,GAAG,MAAM,IAAI;;;;ACjB/B,SAAgB,gBAAgB,MAAmC;CACjE,MAAM,sBAAM,IAAI,KAAqB;AACrC,MAAK,MAAM,WAAW,KAAK,MAAM,QAAQ,EAAE;EACzC,MAAM,OAAO,QAAQ,MAAM;AAC3B,MAAI,SAAS,MAAM,KAAK,WAAW,IAAI,CAAE;EACzC,MAAM,QAAQ,KAAK,MAAM,gCAAgC;AACzD,MAAI,CAAC,MAAO;EACZ,MAAM,OAAO,MAAM,IAAI,aAAa;EACpC,MAAM,OAAO,MAAM,IAAI,MAAM;AAC7B,MAAI,CAAC,QAAQ,CAAC,KAAM;AACpB,MAAI,IAAI,MAAM,KAAK;;AAErB,QAAO;;AAGT,SAAgB,aAAa,MAA+B;AAC1D,QAAO,IAAI,SAAS,SAAS,WAAW;EACtC,MAAM,OAAO,WAAW,SAAS;EACjC,MAAM,SAAS,iBAAiB,KAAK;AACrC,SAAO,GAAG,SAAS,OAAO;AAC1B,SAAO,GAAG,SAAS,UAAU,KAAK,OAAO,MAAM,CAAC;AAChD,SAAO,GAAG,aAAa,QAAQ,KAAK,OAAO,MAAM,CAAC,CAAC;GACnD;;;;AClBJ,SAAS,iBAAyB;AAChC,KAAI;EAEF,MAAM,WAAY,WAAmB;AACrC,MAAI,OAAO,aAAa,YAAY,SAAS,SAAS,EAAG,QAAO;SAC1D;AAGR,KAAI;AAEA,SAAA;SAEI;AAGR,QAAO;;AAGT,MAAa,UAAU,gBAAgB;;;AClBvC,MAAM,YAAY,UAAU,SAAS;AAMrC,SAAS,qBAA8B;AAErC,QADY,SAAS,QAAQ,SAAS,CAAC,aAAa,CACzC,WAAW,QAAQ;;AAShC,eAAsB,6BACpB,UAAkB,QAAQ,UACX;AACf,KAAI,QAAQ,aAAa,QAAS;AAClC,KAAI,CAAC,QAAS;AACd,KAAI;AACF,QAAM,OAAO,GAAG,QAAQ,MAAM;SACxB;;AAKV,MAAa,iBAAiB,cAAc;CAC1C,MAAM;EACJ,MAAM;EACN,aAAa;EACd;CACD,MAAM;EACJ,MAAM;GACJ,MAAM;GACN,aAAa;GACb,SAAS;GACV;EACD,OAAO;GACL,MAAM;GACN,aAAa;GACb,SAAS;GACV;EACD,WAAW;GACT,MAAM;GACN,aAAa;GACb,SAAS;GACV;EACF;CACD,MAAM,IAAI,EAAE,QAAQ;EAClB,MAAM,QAAQ,SAAkC,UAAkB;AAChE,OAAI,KAAK,KACP,SAAQ,OAAO,MAAM,GAAG,KAAK,UAAU,QAAQ,CAAC,IAAI;OAEpD,SAAQ,OAAO,MAAM,GAAG,MAAM,IAAI;;EAGtC,MAAM,aAAa,SAAkC,UAAkB;AACrE,OAAI,KAAK,KACP,SAAQ,OAAO,MAAM,GAAG,KAAK,UAAU;IAAE,IAAI;IAAO,GAAG;IAAS,CAAC,CAAC,IAAI;OAEtE,SAAQ,OAAO,MAAM,GAAG,MAAM,IAAI;;EAItC,IAAI;AACJ,MAAI;AACF,aAAU,MAAM,oBAAoB;WAC7B,KAAK;AACZ,aACE;IAAE,QAAQ;IAAiB,SAAU,IAAc;IAAS,EAC5D,oCAAqC,IAAc,UACpD;AACD,WAAQ,KAAK,SAAS,aAAa;;EAGrC,MAAM,SAAS,eAAe,QAAQ,SAAS;EAC/C,MAAM,UAAU;AAIhB,MAAI,EAHQ,cAAc,QAAQ,QAAQ,GAChB,KAAK,KAAK,QAElB;AAChB,QACE;IAAE,IAAI;IAAM,QAAQ;IAAkB;IAAS;IAAQ,EACvD,kCAAkC,QAAQ,IAC3C;AACD,WAAQ,KAAK,SAAS,qBAAqB;;AAG7C,MAAI,KAAK,YAAY;AACnB,QACE;IAAE,IAAI;IAAM,QAAQ;IAAoB;IAAS;IAAQ,KAAK,QAAQ;IAAU,EAChF,qBAAqB,QAAQ,KAAK,OAAO,IAAI,QAAQ,WACtD;AACD;;AAGF,MAAI,CAAC,oBAAoB,EAAE;AACzB,aACE;IACE,QAAQ;IACR;IACA;IACA,MAAM;IACP,EACD;IACE;IACA;IACA,wDAAwD,QAAQ,WAAW,OAAO;IACnF,CAAC,KAAK,KAAK,CACb;AACD,WAAQ,KAAK,SAAS,mBAAmB;;EAG3C,MAAM,WAAW,gBAAgB;AACjC,MAAI,CAAC,UAAU;AACb,aACE;IACE,QAAQ;IACR,UAAU,QAAQ;IAClB,MAAM,QAAQ;IACf,EACD,0BAA0B,QAAQ,SAAS,GAAG,QAAQ,KAAK,GAC5D;AACD,WAAQ,KAAK,SAAS,mBAAmB;;EAG3C,MAAM,QAAQ,QAAQ,OAAO,MAAM,MAAM,EAAE,SAAS,SAAS,UAAU;AACvE,MAAI,CAAC,OAAO;AACV,aACE;IAAE,QAAQ;IAAiB,WAAW,SAAS;IAAW,KAAK,QAAQ;IAAU,EACjF,WAAW,QAAQ,SAAS,sBAAsB,SAAS,UAAU,8BACtE;AACD,WAAQ,KAAK,SAAS,mBAAmB;;EAG3C,MAAM,UAAU,QAAQ;EACxB,MAAM,cAAc,GAAG,QAAQ,OAAO,KAAK,KAAK;AAEhD,MAAI,CAAC,KAAK,KACR,SAAQ,OAAO,MAAM,eAAe,MAAM,KAAK,IAAI,OAAO,QAAQ;AAGpE,MAAI;GACF,MAAM,MAAM,MAAM,MAAM,MAAM,qBAAqB;AACnD,OAAI,CAAC,IAAI,MAAM,CAAC,IAAI,KAClB,OAAM,IAAI,MAAM,oBAAoB,IAAI,OAAO,GAAG,IAAI,aAAa;AAGrE,SAAM,UAAU,aADJ,IAAI,WAAW,MAAM,IAAI,aAAa,CAAC,EACjB,EAAE,MAAM,KAAO,CAAC;AAClD,SAAM,MAAM,aAAa,IAAM;WACxB,KAAK;AACZ,aACE;IAAE,QAAQ;IAAmB,SAAU,IAAc;IAAS,EAC9D,kCAAmC,IAAc,UAClD;AACD,WAAQ,KAAK,SAAS,aAAa;;EAOrC,MAAM,YAAY,oBAAoB,QAAQ;AAC9C,MAAI,CAAC,WAAW;AACd,SAAM,OAAO,YAAY,CAAC,YAAY,GAAG;AACzC,aACE;IAAE,QAAQ;IAAgB,KAAK,QAAQ;IAAU,EACjD,WAAW,QAAQ,SAAS,qEAC7B;AACD,WAAQ,KAAK,SAAS,sBAAsB;;EAG9C,IAAI;EACJ,IAAI;AACJ,MAAI;GACF,MAAM,UAAU,MAAM,MAAM,UAAU,qBAAqB;AAC3D,OAAI,CAAC,QAAQ,GACX,OAAM,IAAI,MAAM,+BAA+B,QAAQ,OAAO,GAAG,QAAQ,aAAa;AAIxF,cADa,gBADI,MAAM,QAAQ,MAAM,CACC,CACtB,IAAI,SAAS,UAAU;AACvC,aAAU,MAAM,aAAa,YAAY,EAAE,aAAa;WACjD,KAAK;AACZ,SAAM,OAAO,YAAY,CAAC,YAAY,GAAG;AACzC,aACE;IAAE,QAAQ;IAAqB,SAAU,IAAc;IAAS,EAChE,8BAA+B,IAAc,UAC9C;AACD,WAAQ,KAAK,SAAS,sBAAsB;;AAG9C,MAAI,CAAC,UAAU;AACb,SAAM,OAAO,YAAY,CAAC,YAAY,GAAG;AACzC,aACE;IAAE,QAAQ;IAAiB,WAAW,SAAS;IAAW,KAAK,QAAQ;IAAU,EACjF,kBAAkB,QAAQ,SAAS,oBAAoB,SAAS,UAAU,GAC3E;AACD,WAAQ,KAAK,SAAS,sBAAsB;;AAG9C,MAAI,SAAS,aAAa,KAAK,QAAQ;AACrC,SAAM,OAAO,YAAY,CAAC,YAAY,GAAG;AACzC,aACE;IACE,QAAQ;IACR,WAAW,SAAS;IACpB,UAAU,SAAS,aAAa;IAChC;IACD,EACD,yBAAyB,SAAS,UAAU,aAAa,SAAS,aAAa,CAAC,QAAQ,OAAO,GAChG;AACD,WAAQ,KAAK,SAAS,sBAAsB;;AAG9C,MAAI,CAAC,KAAK,KACR,SAAQ,OAAO,MAAM,iBAAiB;EAOxC,MAAM,aAAa,QAAQ,aAAa,UAAU,OAAO,GAAG,QAAQ,OAAO,KAAK,KAAK;AACrF,MAAI,WACF,KAAI;AACF,SAAM,SAAS,SAAS,WAAW;WAC5B,KAAK;AACZ,SAAM,OAAO,YAAY,CAAC,YAAY,GAAG;AACzC,aACE;IAAE,QAAQ;IAAiB,SAAU,IAAc;IAAS;IAAS;IAAY,EACjF,uCAAuC,WAAW,IAAK,IAAc,UACtE;AACD,WAAQ,KAAK,SAAS,QAAQ;;AAQlC,MAAI;AACF,OAAI,QAAQ,aAAa,SAAS;AAChC,UAAM,OAAO,SAAS,GAAG,QAAQ,MAAM;AACvC,UAAM,OAAO,aAAa,QAAQ;SAElC,OAAM,OAAO,aAAa,QAAQ;WAE7B,KAAK;AACZ,OAAI,WAAY,OAAM,OAAO,WAAW,CAAC,YAAY,GAAG;AACxD,aACE;IAAE,QAAQ;IAAkB,SAAU,IAAc;IAAS;IAAS;IAAa,EACnF,+BAA+B,QAAQ,IAAK,IAAc,UAC3D;AACD,WAAQ,KAAK,SAAS,QAAQ;;EAQhC,IAAI,eAA8B;AAClC,MAAI;GACF,MAAM,EAAE,WAAW,MAAM,UAAU,SAAS,CAAC,YAAY,EAAE;IACzD,SAAS;IACT,aAAa;IACd,CAAC;AACF,OAAI,CAAC,OAAO,MAAM,CAAE,gBAAe;WAC5B,KAAK;AACZ,kBAAgB,IAAc;;AAGhC,MAAI,cAAc;GAChB,IAAI,gBAA+B;GAGnC,IAAI,eAA8B;AAClC,OAAI;AACF,QAAI,QAAQ,aAAa,SAAS;AAChC,SAAI;AACF,YAAM,OAAO,QAAQ;cACd,KAAK;AACZ,qBAAe,qCAAqC,QAAQ,mCAAmC,QAAQ,eAAe,QAAQ;AAC9H,YAAM;;AAER,WAAM,OAAO,GAAG,QAAQ,OAAO,QAAQ;eAC9B,WACT,OAAM,OAAO,YAAY,QAAQ;YAE5B,KAAK;AACZ,oBAAiB,IAAc;AAC/B,QAAI,CAAC,aACH,gBACE,QAAQ,aAAa,UACjB,UAAU,QAAQ,eAAe,QAAQ,oCACzC,UAAU,WAAW,WAAW,QAAQ;;AAGlD,aACE;IACE,QAAQ;IACR,SAAS;IACT;IACA,GAAI,gBAAgB;KAAE;KAAe;KAAY,GAAG,EAAE,YAAY,MAAM;IACzE,EACD,gBACI,2CAA2C,aAAa,0BAA0B,cAAc,IAAI,iBACpG,2CAA2C,aAAa,gCAC7D;AACD,WAAQ,KAAK,SAAS,uBAAuB;;AAG/C,MAAI,WAAY,OAAM,OAAO,WAAW,CAAC,YAAY,GAAG;AAExD,OACE;GACE,IAAI;GACJ,QAAQ;GACR,MAAM;GACN,IAAI;GACJ,aAAa;GACb,aAAa,QAAQ,QAAQ;GAC9B,EACD,mBAAmB,QAAQ,KAAK,SACjC;;CAEJ,CAAC;;;AC5TF,MAAa,2BAA2B,OAAU,KAAK;AAQvD,eAAsB,YAA8C;CAClE,IAAI;AACJ,KAAI;AACF,QAAM,MAAM,SAAS,kBAAkB,EAAE,OAAO;SAC1C;AACN,SAAO;;CAET,IAAI;AACJ,KAAI;AACF,WAAS,KAAK,MAAM,IAAI;SAClB;AACN,SAAO;;AAET,KAAI,CAAC,UAAU,OAAO,WAAW,SAAU,QAAO;CAClD,MAAM,MAAM;AACZ,KAAI,OAAO,IAAI,kBAAkB,SAAU,QAAO;AAGlD,KAAI,IAAI,cAAc,KAAA,KAAa,OAAO,IAAI,cAAc,SAAU,QAAO;AAC7E,KAAI,IAAI,SAAS,KAAA,KAAa,OAAO,IAAI,SAAS,SAAU,QAAO;AAMnE,QALiC;EAC/B,eAAe,IAAI;EACnB,GAAI,IAAI,cAAc,KAAA,IAAY,EAAE,WAAW,IAAI,WAAqB,GAAG,EAAE;EAC7E,GAAI,IAAI,SAAS,KAAA,IAAY,EAAE,MAAM,IAAI,MAAgB,GAAG,EAAE;EAC/D;;AAIH,eAAsB,WAAW,OAAwC;CACvE,MAAM,OAAO,kBAAkB;CAC/B,MAAM,MAAM,QAAQ,KAAK;AACzB,OAAM,MAAM,KAAK,EAAE,WAAW,MAAM,CAAC;AAIhC,qBAAoB,KAAK,KAAK;CAUnC,MAAM,MAAM,GAAG,KAAK,GAAG,QAAQ,IAAI,GAAG,KAAK,KAAK,CAAC,GAAG,KAAK,QAAQ,CAAC,SAAS,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC;AAC5F,KAAI;AAIF,QAAM,UAAU,KAAK,KAAK,UAAU,OAAO,MAAM,EAAE,EAAE,EAAE,MAAM,KAAO,CAAC;AACrE,QAAM,OAAO,KAAK,KAAK;UAChB,KAAK;AAEZ,QAAM,OAAO,IAAI,CAAC,YAAY,GAAG;AACjC,QAAM;;;AAIV,MAAM,kBAAkB,QAAc,KAAK;AAM3C,MAAM,qBAAqB;AAE3B,eAAsB,oBAAoB,KAAa,UAAiC;CACtF,IAAI;AACJ,KAAI;AACF,YAAU,MAAM,QAAQ,IAAI;SACtB;AACN;;CAEF,MAAM,SAAS,KAAK,KAAK,GAAG;CAC5B,MAAM,WAAW,SAAS,MAAM,IAAI,SAAS,EAAE;AAC/C,OAAM,QAAQ,IACZ,QAAQ,IAAI,OAAO,SAAS;AAG1B,MAAI,CAAC,KAAK,WAAW,GAAG,SAAS,GAAG,CAAE;AACtC,MAAI,CAAC,mBAAmB,KAAK,KAAK,CAAE;EACpC,MAAM,IAAI,KAAK,KAAK,KAAK;AACzB,MAAI;AAEF,QADW,MAAM,KAAK,EAAE,EACjB,UAAU,OAAQ,OAAM,OAAO,EAAE;UAClC;GAGR,CACH;;;AAIH,SAAgB,cACd,OACA,MAAc,KAAK,KAAK,EACxB,aAAqB,0BACZ;AACT,KAAI,CAAC,MAAO,QAAO;CACnB,MAAM,OAAO,KAAK,MAAM,MAAM,cAAc;AAC5C,KAAI,CAAC,OAAO,SAAS,KAAK,CAAE,QAAO;AAInC,KAAI,MAAM,KAAM,QAAO;AACvB,QAAO,MAAM,QAAQ;;;;;;;AAevB,eAAsB,oBACpB,OAA2B,EAAE,EACK;CAClC,MAAM,MAAM,KAAK,OAAO,QAAQ;CAChC,MAAM,QAAQ,KAAK,SAAS,QAAQ,QAAQ,OAAO,MAAM;CACzD,MAAM,MAAM,KAAK,OAAO,KAAK,KAAK;CAClC,MAAM,aAAa,KAAK,cAAA;CAKxB,MAAM,SAAS,IAAI;AACnB,KAAI,UAAU,WAAW,OAAO,OAAO,aAAa,KAAK,QAAS,QAAO;AAKzE,KAAI,CAAC,MAAO,QAAO;CAEnB,MAAM,QAAQ,MAAM,WAAW;AAC/B,KAAI,CAAC,cAAc,OAAO,KAAK,WAAW,CAAE,QAAO;CAUnD,MAAM,SAAS,IAAI,KAAK,IAAI,CAAC,aAAa;CAC1C,MAAM,cAAgC;EACpC,eAAe;EACf,GAAI,OAAO,cAAc,KAAA,IAAY,EAAE,WAAW,MAAM,WAAW,GAAG,EAAE;EACxE,GAAI,OAAO,SAAS,KAAA,IAAY,EAAE,MAAM,MAAM,MAAM,GAAG,EAAE;EAC1D;AACD,OAAM,WAAW,YAAY,CAAC,YAAY,GAIxC;CAEF,MAAM,eAAe,OAAO;CAC5B,IAAI,QAA0B;AAC9B,KAAI;EACF,MAAM,SAAS,MAAM,8BAA8B,aAAa;AAChE,MAAI,OAAO,WAAW,eAIpB,SAAQ;GACN,eAAe;GACf,GAAI,OAAO,cAAc,KAAA,IAAY,EAAE,WAAW,MAAM,WAAW,GAAG,EAAE;GACxE,GAAI,OAAO,SAAS,KAAA,IAChB,EAAE,MAAM,OAAO,MAAM,GACrB,OAAO,SAAS,KAAA,IACd,EAAE,MAAM,MAAM,MAAM,GACpB,EAAE;GACT;MAED,SAAQ;GACN,eAAe;GACf,WAAW,OAAO,QAAQ;GAC1B,GAAI,OAAO,SAAS,KAAA,IAAY,EAAE,MAAM,OAAO,MAAM,GAAG,EAAE;GAC3D;AAEH,QAAM,WAAW,MAAM,CAAC,YAAY,GAElC;SACI;AAUR,iBAAgB,OAAO,IAAI;AAC3B,QAAO;;AAGT,SAAS,gBAAgB,OAAyB,KAA8B;AAC9E,KAAI,CAAC,MAAM,UAAW;AAKtB,KAAI,QAAQ,WAAW,YAAY,CAAE;CACrC,MAAM,SAAS,eAAe,MAAM,UAAU;AAC9C,KAAI,CAAC,OAAQ;AACb,KAAI,cAAc,QAAQ,QAAQ,IAAI,EAAG;CAEzC,MAAM,MAAM,IAAI,WAAW,KAAK;CAChC,MAAM,QAAQ,IAAI,WAAW,KAAK;AAElC,SAAQ,OAAO,MACb,KAAK,IAAI,SAAS,OAAO,mDAAmD,MAAM,IACnF;;;;AClPH,eAAe,2BAGZ;CACD,MAAM,SAAS,MAAM,0BAA0B,CAAC,YAAY,KAAK;AACjE,KAAI,CAAC,OAAQ,QAAO;EAAE,QAAQ;EAAQ,OAAO;EAAM;AACnD,QAAO;EAAE,QAAQ,OAAO;EAAM,OAAO,OAAO;EAAO;;AAGrD,SAAS,kBAAkB,MAGhB;AACT,KAAI,KAAK,WAAW,OAAQ,QAAO;AACnC,KAAI,KAAK,WAAW,MAClB,QAAO,mBAAmB,KAAK,QAAQ,MAAM,KAAK,UAAU,GAAG;AAEjE,QAAO,WAAW,KAAK,QAAQ,KAAK,KAAK,MAAM,KAAK;;AA6BtD,eAAe,yBAAyB,MAA8B;AACpE,KAAI,KAAM;CACV,MAAM,YAAY;AAClB,OAAM,QAAQ,KAAK,CACjB,qBAAqB,CAAC,YAAY,KAAK,EACvC,IAAI,SAAe,YAAY;EAC7B,MAAM,IAAI,iBAAiB,QAAQ,KAAK,EAAE,UAAU;AACpD,MAAI,OAAO,EAAE,UAAU,WAAY,GAAE,OAAO;GAC5C,CACH,CAAC;;AAGJ,MAAa,gBAAgB,cAAc;CACzC,MAAM;EACJ,MAAM;EACN,aAAa;EACd;CACD,MAAM;EACJ,MAAM;GACJ,MAAM;GACN,aAAa;GACb,SAAS;GACV;EACD,SAAS;GACP,MAAM;GACN,aAAa;GACb,SAAS;GACV;EACF;CACD,MAAM,IAAI,EAAE,QAAQ;EAClB,MAAM,UAAU,MAAM,aAAa;EACnC,MAAM,OAAO,MAAM,0BAA0B;AAE7C,MAAI,CAAC,SAAS;AACZ,OAAI,KAAK,KACP,SAAQ,OAAO,MACb,GAAG,KAAK,UAAU;IAChB,IAAI;IACJ,eAAe;IACf,kBAAkB,KAAK;IACvB,GAAI,KAAK,QAAQ,EAAE,iBAAiB,KAAK,OAAO,GAAG,EAAE;IACtD,CAAC,CAAC,IACJ;QACI;AACL,YAAQ,OAAO,MAAM,yDAAyD;AAC9E,YAAQ,OAAO,MAAM,yBAAyB,2BAA2B,CAAC,IAAI;AAC9E,YAAQ,OAAO,MAAM,gBAAgB,kBAAkB,KAAK,CAAC,IAAI;;AAEnE,UAAO,eAAe,SAAS,iBAAiB;;AAGlD,MAAI,KAAK,SAAS;AAChB,OAAI,KAAK,MAAM;AACb,YAAQ,OAAO,MACb,GAAG,KAAK,UAAU;KAChB,IAAI;KACJ,eAAe;KACf,QAAQ;KACR,MAAM,QAAQ;KACd,YAAY,QAAQ;KACpB,kBAAkB,KAAK;KACvB,GAAI,KAAK,QAAQ,EAAE,iBAAiB,KAAK,OAAO,GAAG,EAAE;KACtD,CAAC,CAAC,IACJ;AACD,WAAO,eAAe,SAAS,GAAG;;GAEpC,MAAM,QAAQ,QAAQ,KAAK,cACvB,GAAG,QAAQ,KAAK,YAAY,IAAI,QAAQ,KAAK,MAAM,KACnD,QAAQ,KAAK;AACjB,WAAQ,OAAO,MAAM,gBAAgB,MAAM,aAAa;AACxD,WAAQ,OAAO,MAAM,gBAAgB,kBAAkB,KAAK,CAAC,IAAI;AACjE,WAAQ,OAAO,MAAM,qBAAqB,QAAQ,WAAW,IAAI;AACjE,SAAM,yBAAyB,KAAK,KAAK;AACzC,UAAO,eAAe,SAAS,GAAG;;AAGpC,MAAI;GACF,MAAM,OAAO,MAAM,2BAA2B,QAAQ,QAAQ;AAC9D,OAAI,KAAK,MAAM;AACb,YAAQ,OAAO,MACb,GAAG,KAAK,UAAU;KAChB,IAAI;KACJ,eAAe;KACf,QAAQ;KACR,MAAM;MACJ,IAAI,OAAO,KAAK,GAAG;MACnB,WAAW,KAAK;MAChB,MAAM,KAAK;MACX,OAAO,KAAK;MACZ,MAAM,KAAK;MACZ;KACD,YAAY,KAAK,WAAW,KAAK,OAAO;MACtC,aAAa,EAAE;MACf,eAAe,EAAE;MACjB,MAAM,EAAE;MACT,EAAE;KACH,YAAY,QAAQ;KACpB,kBAAkB,KAAK;KACvB,GAAI,KAAK,QAAQ,EAAE,iBAAiB,KAAK,OAAO,GAAG,EAAE;KACtD,CAAC,CAAC,IACJ;AACD,WAAO,eAAe,SAAS,GAAG;;AAEpC,WAAQ,OAAO,MAAM,gBAAgB,KAAK,KAAK,IAAI,KAAK,MAAM,KAAK,KAAK,KAAK,KAAK;AAClF,WAAQ,OAAO,MAAM,gBAAgB,kBAAkB,KAAK,CAAC,IAAI;AACjE,OAAI,KAAK,WAAW,SAAS,GAAG;AAC9B,YAAQ,OAAO,MAAM,gBAAgB;AACrC,SAAK,MAAM,KAAK,KAAK,WACnB,SAAQ,OAAO,MAAM,OAAO,EAAE,cAAc,OAAO,EAAE,YAAY,IAAI,EAAE,KAAK,KAAK;;AAGrF,SAAM,yBAAyB,KAAK,KAAK;AACzC,UAAO,eAAe,SAAS,GAAG;WAC3B,KAAK;AACZ,OAAI,eAAe,gBAAgB,IAAI,aAAa;AAClD,QAAI,KAAK,KACP,SAAQ,OAAO,MACb,GAAG,KAAK,UAAU;KAChB,IAAI;KACJ,eAAe;KACf,QAAQ;KACR,WAAW,IAAI;KACf,kBAAkB,KAAK;KACvB,GAAI,KAAK,QAAQ,EAAE,iBAAiB,KAAK,OAAO,GAAG,EAAE;KACtD,CAAC,CAAC,IACJ;SACI;AACL,aAAQ,OAAO,MAAM,yDAAyD;AAC9E,aAAQ,OAAO,MAAM,gBAAgB,kBAAkB,KAAK,CAAC,IAAI;;AAEnE,WAAO,eAAe,SAAS,iBAAiB;;AAElD,OAAI,eAAe,cAAc;AAK/B,QAAI,KAAK,KACP,SAAQ,OAAO,MACb,GAAG,KAAK,UAAU;KAAE,IAAI;KAAO,QAAQ;KAAiB,SAAS,IAAI;KAAS,CAAC,CAAC,IACjF;QAED,SAAQ,OAAO,MACb,2CAA2C,IAAI,QAAQ,6DACxD;AAEH,WAAO,eAAe,SAAS,aAAa;;AAE9C,OAAI,KAAK,KACP,SAAQ,OAAO,MACb,GAAG,KAAK,UAAU;IAAE,IAAI;IAAO,QAAQ;IAAa,SAAU,IAAc;IAAS,CAAC,CAAC,IACxF;OAED,SAAQ,OAAO,MAAM,qBAAsB,IAAc,QAAQ,IAAI;AAEvE,UAAO,eAAe,SAAS,SAAS;;;CAG7C,CAAC;;;AC1HF,SAAS,aAAa,GAAoB;AACxC,KAAI,MAAM,KAAM,QAAO;AACvB,KAAI,OAAO,MAAM,YAAY,OAAO,MAAM,YAAY,OAAO,MAAM,UAAW,QAAO,OAAO,EAAE;AAC9F,QAAO,KAAK,UAAU,EAAE;;AAG1B,MAAM,YAAY,cAAc;CAC9B,MAAM;EACJ,MAAM;EACN,aAAa;EACd;CACD,MAAM,EACJ,MAAM;EAAE,MAAM;EAAW,aAAa;EAAyC,SAAS;EAAO,EAChG;CACD,MAAM,IAAI,EAAE,QAAQ;EAClB,MAAM,UAAU,MAAM,aAAa;AACnC,MAAI,CAAC,SAAS;AACZ,wBAAqB,KAAK,KAAK;AAC/B,UAAO,eAAe,SAAS,iBAAiB;;AAElD,MAAI;GACF,MAAM,OAAO,MAAM,2BAA2B,QAAQ,QAAQ;GAC9D,MAAM,UAAU,QAAQ;AACxB,OAAI,KAAK,MAAM;AAOb,aAAS;KAAE,IAAI;KAAM,YANF,KAAK,WAAW,KAAK,OAAO;MAC7C,aAAa,EAAE;MACf,eAAe,EAAE;MACjB,MAAM,EAAE;MACR,SAAS,EAAE,gBAAgB;MAC5B,EAAE;KAC8B,CAAC;AAClC,WAAO,eAAe,SAAS,GAAG;;AAEpC,OAAI,KAAK,WAAW,WAAW,GAAG;AAChC,YAAQ,OAAO,MAAM,mBAAmB;AACxC,WAAO,eAAe,SAAS,GAAG;;AAEpC,QAAK,MAAM,KAAK,KAAK,YAAY;IAC/B,MAAM,SAAS,EAAE,gBAAgB,UAAU,OAAO;AAClD,YAAQ,OAAO,MAAM,GAAG,SAAS,EAAE,YAAY,IAAI,EAAE,cAAc,KAAK,EAAE,KAAK,KAAK;;AAEtF,OAAI,YAAY,KAAA,EACd,SAAQ,OAAO,MAAM,2DAA2D;AAElF,UAAO,eAAe,SAAS,GAAG;WAC3B,KAAK;AACZ,UAAO,qBAAqB,KAAK,MAAM,IAAI;;;CAGhD,CAAC;AAEF,MAAM,aAAa,cAAc;CAC/B,MAAM;EACJ,MAAM;EACN,aAAa;EACd;CACD,MAAM;EACJ,IAAI;GAAE,MAAM;GAAc,aAAa;GAAgB,UAAU;GAAM;EACvE,MAAM;GAAE,MAAM;GAAW,aAAa;GAAyC,SAAS;GAAO;EAChG;CACD,MAAM,IAAI,EAAE,QAAQ;EAClB,MAAM,MAAM,OAAO,KAAK,GAAG;EAC3B,MAAM,SAASI,mBAAiB,IAAI;AACpC,MAAI,WAAW,MAAM;GACnB,MAAM,UAAU,gDAAgD,IAAI;AACpE,OAAI,KAAK,KACP,UAAS;IAAE,IAAI;IAAO,QAAQ;IAAc;IAAS,CAAC;OAEtD,SAAQ,OAAO,MAAM,GAAG,QAAQ,IAAI;AAEtC,UAAO,eAAe,SAAS,MAAM;;EAGvC,MAAM,UAAU,MAAM,aAAa;AACnC,MAAI,CAAC,SAAS;AACZ,wBAAqB,KAAK,KAAK;AAC/B,UAAO,eAAe,SAAS,iBAAiB;;AAOlD,MAAI;GAEF,MAAM,SADO,MAAM,2BAA2B,QAAQ,QAAQ,EAC3C,WAAW,MAAM,MAAM,EAAE,gBAAgB,OAAO;AACnE,OAAI,CAAC,OAAO;AACV,QAAI,KAAK,KACP,UAAS;KAAE,IAAI;KAAO,QAAQ;KAAa,aAAa;KAAQ,CAAC;QAEjE,SAAQ,OAAO,MACb,aAAa,OAAO,iGACrB;AAEH,WAAO,eAAe,SAAS,MAAM;;AAUvC,OADgB,MAAM,sBAAsB,OAAO,KACnC,MAAM;AACpB,yBAAqB,KAAK,KAAK;AAC/B,WAAO,eAAe,SAAS,iBAAiB;;AAElD,OAAI,KAAK,KACP,UAAS;IACP,IAAI;IACJ,aAAa,MAAM;IACnB,eAAe,MAAM;IACtB,CAAC;OAEF,SAAQ,OAAO,MAAM,mBAAmB,MAAM,YAAY,IAAI,MAAM,cAAc,MAAM;AAE1F,UAAO,eAAe,SAAS,GAAG;WAC3B,KAAK;AACZ,UAAO,qBAAqB,KAAK,MAAM,IAAI;;;CAGhD,CAAC;AAEF,MAAM,cAAc,cAAc;CAChC,MAAM;EACJ,MAAM;EACN,aAAa;EACd;CACD,MAAM;EACJ,WAAW;GACT,MAAM;GACN,aAAa;GACd;EACD,MAAM;GAAE,MAAM;GAAW,aAAa;GAAyC,SAAS;GAAO;EAChG;CACD,MAAM,IAAI,EAAE,QAAQ;EAClB,MAAM,MAAM,MAAM,wBAAwB,KAAK;AAC/C,MAAI,CAAC,IAAK;EACV,MAAM,EAAE,SAAS,gBAAgB;AACjC,qBAAmB,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;AAE5C,MAAI;GACF,MAAM,SAAS,MAAM,qBAAqB,aAAa,QAAQ,QAAQ;AACvE,OAAI,KAAK,MAAM;AACb,aAAS;KACP,IAAI;KACJ,aAAa,OAAO;KACpB,eAAe,OAAO;KACtB,OAAO,OAAO,SAAS,EAAE;KAC1B,CAAC;AACF,WAAO,eAAe,SAAS,GAAG;;AAEpC,WAAQ,OAAO,MAAM,aAAa,OAAO,YAAY,IAAI,OAAO,cAAc,IAAI;AAClF,OAAI,OAAO,MACT,MAAK,MAAM,CAAC,GAAG,MAAM,OAAO,QAAQ,OAAO,MAAM,CAC/C,SAAQ,OAAO,MAAM,KAAK,EAAE,IAAI,aAAa,EAAE,CAAC,IAAI;AAGxD,UAAO,eAAe,SAAS,GAAG;WAC3B,KAAK;AACZ,UAAO,qBAAqB,KAAK,MAAM,IAAI;;;CAGhD,CAAC;AAEF,MAAM,iBAAiB,cAAc;CACnC,MAAM;EACJ,MAAM;EACN,aAAa;EACd;CACD,MAAM;EACJ,WAAW;GACT,MAAM;GACN,aAAa;GACd;EACD,MAAM;GAAE,MAAM;GAAW,aAAa;GAAyC,SAAS;GAAO;EAChG;CACD,MAAM,IAAI,EAAE,QAAQ;EAClB,MAAM,MAAM,MAAM,wBAAwB,KAAK;AAC/C,MAAI,CAAC,IAAK;EACV,MAAM,EAAE,SAAS,gBAAgB;AACjC,qBAAmB,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;AAE5C,MAAI;GACF,MAAM,QAAQ,MAAM,sBAAsB,aAAa,QAAQ,QAAQ;AACvE,OAAI,KAAK,MAAM;AACb,aAAS;KACP,IAAI;KACJ;KACA,YAAY,MAAM;KAClB,cAAc,MAAM;KACpB,eAAe,MAAM;KACrB,SAAS,MAAM;KAChB,CAAC;AACF,WAAO,eAAe,SAAS,GAAG;;AAEpC,WAAQ,OAAO,MAAM,aAAa,YAAY,aAAa;AAC3D,WAAQ,OAAO,MAAM,iBAAiB,MAAM,WAAW,IAAI;AAC3D,WAAQ,OAAO,MAAM,mBAAmB,MAAM,gBAAgB,OAAO,IAAI;AACzE,OAAI,MAAM,cACR,SAAQ,OAAO,MAAM,oBAAoB,MAAM,cAAc,IAAI;AAEnE,OAAI,MAAM,SAAS;AACjB,YAAQ,OAAO,MAAM,eAAe;AACpC,SAAK,MAAM,CAAC,GAAG,MAAM,OAAO,QAAQ,MAAM,QAAQ,CAChD,SAAQ,OAAO,MAAM,OAAO,EAAE,IAAI,aAAa,EAAE,CAAC,IAAI;;AAG1D,UAAO,eAAe,SAAS,GAAG;WAC3B,KAAK;AACZ,UAAO,qBAAqB,KAAK,MAAM,IAAI;;;CAGhD,CAAC;AAEF,SAAS,gBAAgB,MAA6B;AAOpD,QAAO,KAFK,KAAK,WAAW,aAAa,cAC7B,KAAK,WAAW,cAAc,GACpB,IAAI,KAAK,MAAM,QAAQ,KAAK,YAAY;;AAchE,MAAa,cAA4D;CACvE,YAAY,CAAC,kCAAkC,uCAAuC;CACtF,eAAe;EAAC;EAAgB;EAAc;EAAwB;CACtE,sBAAsB,CAAC,sCAAsC;CAC7D,KAAK,CAAC,yBAAyB;CAC/B,KAAK,CAAC,wBAAwB,aAAa;CAC5C;AAED,SAAgB,UAAU,MAA4C;AACpE,QAAO,YAAY,SAAS,EAAE;;AAGhC,SAAgB,iBAAiB,MAAyB,QAAyB;CAKjF,MAAM,SAAS,UAAU,KAAK;AAC9B,KAAI,OAAO,WAAW,EAAG,QAAO;CAIhC,MAAM,WAAW,CAAC,UAAU,QAAQ,OAAO,SAAS,CAAC,QAAQ,IAAI;CACjE,MAAM,SAAS,WAAW,aAAa;CACvC,MAAM,QAAQ,WAAW,YAAY;AACrC,QAAO,OAAO,OAAO,qBAAqB,OAAO,KAAK,KAAK,GAAG,MAAM;;AAGtE,MAAM,mBAAmB,cAAc;CACrC,MAAM;EACJ,MAAM;EACN,aACE;EACH;CACD,MAAM;EACJ,MAAM;GACJ,MAAM;GACN,aAAa,2BAA2B,qBAAqB,KAAK,MAAM,CAAC;GAC1E;EACD,WAAW;GACT,MAAM;GACN,aAAa;GACd;EACD,MAAM;GAAE,MAAM;GAAW,aAAa;GAAyC,SAAS;GAAO;EAChG;CACD,MAAM,IAAI,EAAE,QAAQ;EAClB,MAAM,MAAM,MAAM,wBAAwB,KAAK;AAC/C,MAAI,CAAC,IAAK;EACV,MAAM,EAAE,SAAS,gBAAgB;EAEjC,MAAM,sBAAoD;AACxD,OAAI,CAAC,KAAK,KAAM,QAAO;GACvB,MAAM,MAAM,OAAO,KAAK,KAAK,CAAC,aAAa;AAC3C,OAAK,qBAA2C,SAAS,IAAI,CAC3D,QAAO,CAAC,IAAyB;AAEnC,UAAO,EAAE;MACP;AACJ,MAAI,aAAa,WAAW,GAAG;GAC7B,MAAM,UAAU,0BAA0B,qBAAqB,KAAK,KAAK;AACzE,OAAI,KAAK,KACP,UAAS;IAAE,IAAI;IAAO,QAAQ;IAAgB,SAAS,CAAC,GAAG,qBAAqB;IAAE,CAAC;OAEnF,SAAQ,OAAO,MAAM,GAAG,QAAQ,IAAI;AAEtC,UAAO,eAAe,SAAS,MAAM;;AAEvC,qBAAmB,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;AAE5C,MAAI;GAKF,MAAM,UAAU,MAAM,QAAQ,IAC5B,aAAa,IACX,OAAO,MAAM,CAAC,GAAG,MAAM,oBAAoB,aAAa,GAAG,QAAQ,QAAQ,CAAC,CAC7E,CACF;GAOD,MAAM,UACJ,MACA,UACwE;IACxE,MAAM,SAAS,UAAU,KAAK;AAC9B,WAAO,MAAM,KAAK,OAAO;KAAE,GAAG;KAAG;KAAQ,EAAE;;AAG7C,OAAI,aAAa,WAAW,GAAG;IAC7B,MAAM,CAAC,MAAM,SAAS,QAAQ;AAC9B,QAAI,KAAK,MAAM;AACb,cAAS;MAAE,IAAI;MAAM;MAAa;MAAM,OAAO,OAAO,MAAM,MAAM;MAAE,CAAC;AACrE,YAAO,eAAe,SAAS,GAAG;;AAEpC,YAAQ,OAAO,MAAM,aAAa,YAAY,UAAU,KAAK,MAAM;AACnE,QAAI,MAAM,WAAW,EACnB,SAAQ,OAAO,MAAM,0BAA0B;QAE/C,MAAK,MAAM,KAAK,OAAO;AACrB,aAAQ,OAAO,MAAM,gBAAgB,EAAE,CAAC;AACxC,aAAQ,OAAO,MAAM,iBAAiB,MAAM,EAAE,SAAS,CAAC;;AAG5D,WAAO,eAAe,SAAS,GAAG;;GAIpC,MAAM,SAGF,EAAE;AACN,QAAK,MAAM,CAAC,GAAG,UAAU,QAAS,QAAO,KAAK,OAAO,GAAG,MAAM;AAC9D,OAAI,KAAK,MAAM;AACb,aAAS;KAAE,IAAI;KAAM;KAAa;KAAQ,CAAC;AAC3C,WAAO,eAAe,SAAS,GAAG;;AAEpC,QAAK,MAAM,CAAC,MAAM,UAAU,SAAS;AACnC,YAAQ,OAAO,MAAM,MAAM,KAAK,KAAK;AACrC,QAAI,MAAM,WAAW,EACnB,SAAQ,OAAO,MAAM,0BAA0B;QAE/C,MAAK,MAAM,KAAK,OAAO;AACrB,aAAQ,OAAO,MAAM,gBAAgB,EAAE,CAAC;AACxC,aAAQ,OAAO,MAAM,iBAAiB,MAAM,EAAE,SAAS,CAAC;;;AAI9D,UAAO,eAAe,SAAS,GAAG;WAC3B,KAAK;AACZ,UAAO,qBAAqB,KAAK,MAAM,IAAI;;;CAGhD,CAAC;AAsBF,SAAgB,mBAAmB,KAAsB;AAIvD,KAAI,eAAe,aAAc,QAAO,IAAI;AAC5C,KAAI,eAAe,aAAc,QAAO,kBAAkB,IAAI;AAC9D,KAAI,eAAe,MAAO,QAAO,IAAI;AACrC,QAAO,OAAO,IAAI;;AAsBpB,SAAgB,kBAAkB,OAGV;CACtB,MAAM,gBAAgB,MAAM,WAAW,SAAS;AAChD,KAAI,CAAC,iBAAiB,CAAC,MAAM,IAC3B,QAAO;EACL,IAAI;EACJ,QAAQ;EACR,SAAS;EACV;AAEH,KAAI,iBAAiB,MAAM,IACzB,QAAO;EACL,IAAI;EACJ,QAAQ;EACR,SAAS;EACV;AAEH,KAAI,MAAM,IACR,QAAO;EAAE,IAAI;EAAM,OAAO;EAAsB;CAElD,MAAM,QAAQ,MAAM,WAAW,aAAa;AAC5C,KAAI,CAAE,qBAA2C,SAAS,MAAM,CAC9D,QAAO;EACL,IAAI;EACJ,QAAQ;EACR,OAAO,MAAM;EACb,SAAS,CAAC,GAAG,qBAAqB;EAClC,SAAS,wBAAwB,MAAM,WAAW,aAAa,qBAAqB,KAAK,KAAK,CAAC;EAChG;AAEH,QAAO;EAAE,IAAI;EAAM,OAAO,CAAC,MAA2B;EAAE;;AAmB1D,eAAsB,oBACpB,SACA,QAIuB;CACvB,MAAM,SAAuB,EAAE;CAC/B,MAAM,YAA0B,EAAE;CAClC,MAAM,SAAuB,EAAE;AAE/B,MAAK,MAAM,EAAE,MAAM,WAAW,SAAS;AACrC,OAAK,MAAM,KAAK,MACd,KAAI,EAAE,SACJ,WAAU,KAAK;GAAE,SAAS,EAAE;GAAS,YAAY,EAAE;GAAY;GAAM,OAAO,EAAE;GAAO,CAAC;EAG1F,MAAM,UAAU,MAAM,QAAQ,MAAM,CAAC,EAAE,SAAS;AAChD,MAAI,QAAQ,WAAW,EAAG;AAC1B,MAAI;AACF,SAAM,OACJ,MACA,QAAQ,KAAK,OAAO;IAAE,SAAS,EAAE;IAAS,YAAY,EAAE;IAAY,EAAE,CACvE;AACD,QAAK,MAAM,KAAK,QACd,QAAO,KAAK;IAAE,SAAS,EAAE;IAAS,YAAY,EAAE;IAAY;IAAM,OAAO,EAAE;IAAO,CAAC;WAE9E,KAAK;GACZ,MAAM,UAAU,mBAAmB,IAAI;AACvC,QAAK,MAAM,KAAK,QACd,QAAO,KAAK;IAAE,SAAS,EAAE;IAAS,YAAY,EAAE;IAAY;IAAM;IAAS,CAAC;;;AAIlF,QAAO;EAAE;EAAQ;EAAW;EAAQ;;AAmQtC,MAAa,mBAAmB,cAAc;CAC5C,MAAM;EACJ,MAAM;EACN,aAAa;EACd;CACD,aAAa;EACX,IAAI;EACJ,KAAK;EACL,MAAM;EACN,SAAS;EACT,OA/IiB,cAAc;GACjC,MAAM;IACJ,MAAM;IACN,aACE;IACH;GAaD,MAAM;IACJ,MAAM;KACJ,MAAM;KACN,aAAa;KACd;IACD,WAAW;KAAE,MAAM;KAAU,aAAa;KAAyC;IACnF,MAAM;KAAE,MAAM;KAAW,aAAa;KAAyC,SAAS;KAAO;IAChG;GACD,aAAa;IACX,MAAM;IACN,OAvJsB,cAAc;KACtC,MAAM;MACJ,MAAM;MACN,aACE;MACH;KACD,MAAM;MACJ,MAAM;OACJ,MAAM;OACN,aAAa,qDAAqD,qBAAqB,KAAK,MAAM,CAAC;OACnG,UAAU;OACX;MACD,KAAK;OACH,MAAM;OACN,aACE;OACF,SAAS;OACV;MACD,WAAW;OACT,MAAM;OACN,aAAa;OACd;MACD,MAAM;OAAE,MAAM;OAAW,aAAa;OAAyC,SAAS;OAAO;MAChG;KACD,MAAM,IAAI,EAAE,QAAQ;MAClB,MAAM,aAAa,kBAAkB;OACnC,YAAY,KAAK,SAAS,KAAA,IAAY,OAAO,KAAK,KAAK,GAAG;OAC1D,KAAK,QAAQ,KAAK,IAAI;OACvB,CAAC;AACF,UAAI,CAAC,WAAW,IAAI;AAClB,WAAI,KAAK,KACP,KAAI,WAAW,WAAW,oBACxB,UAAS;QACP,IAAI;QACJ,QAAQ;QACR,OAAO,WAAW;QAClB,SAAS,WAAW;QACrB,CAAC;WAEF,UAAS;QAAE,IAAI;QAAO,QAAQ,WAAW;QAAQ,SAAS,WAAW;QAAS,CAAC;WAGjF,SAAQ,OAAO,MAAM,GAAG,WAAW,QAAQ,IAAI;AAEjD,cAAO,eAAe,SAAS,MAAM;;MAGvC,MAAM,MAAM,MAAM,wBAAwB,KAAK;AAC/C,UAAI,CAAC,IAAK;MACV,MAAM,EAAE,SAAS,gBAAgB;AACjC,yBAAmB,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;AAE5C,UAAI;OAaF,MAAM,EAAE,QAAQ,WAAW,WAAW,MAAM,oBAPD,MAAM,QAAQ,IACvD,WAAW,MAAM,IAAI,OAAO,UAAU;QACpC;QACA,OAAO,MAAM,oBAAoB,aAAa,MAAM,QAAQ,QAAQ;QACrE,EAAE,CACJ,EAIC,OAAO,OAAO,YAAY;AACxB,cAAM,oBAAoB,aAAa,SAAS,QAAQ,QAAQ;SAEnE;OAED,MAAM,UAAU,OAAO,SAAS;AAChC,WAAI,KAAK,KACP,UAAS;QACP,IAAI,CAAC;QACL;QACA;QACA;QACA;QACA;QACD,CAAC;gBACO,OAAO,WAAW,KAAK,OAAO,WAAW,EAClD,SAAQ,OAAO,MACb,UAAU,WAAW,IACjB,6BACA,mBAAmB,UAAU,OAAO,6BACzC;YACI;AACL,YAAI,OAAO,SAAS,GAAG;SAErB,MAAM,yBAAS,IAAI,KAAsC;AACzD,cAAK,MAAM,KAAK,QAAQ;UACtB,MAAM,MAAM,OAAO,IAAI,EAAE,KAAK,IAAI,EAAE;AACpC,cAAI,KAAK,EAAE;AACX,iBAAO,IAAI,EAAE,MAAM,IAAI;;AAEzB,cAAK,MAAM,CAAC,MAAM,UAAU,QAAQ;UAClC,MAAM,SAAS,UAAU,KAAK;UAC9B,MAAM,aAAa,OAAO,SAAS,IAAI,eAAe,OAAO,KAAK,KAAK,CAAC,KAAK;AAC7E,kBAAQ,OAAO,MAAM,eAAe,MAAM,OAAO,cAAc,OAAO,WAAW,IAAI;AACrF,eAAK,MAAM,KAAK,MACd,SAAQ,OAAO,MAAM,SAAS,EAAE,MAAM,IAAI;;;AAIhD,YAAI,UAAU,SAAS,EACrB,SAAQ,OAAO,MAAM,YAAY,UAAU,OAAO,4BAA4B;AAEhF,YAAI,OAAO,SAAS,GAAG;AACrB,iBAAQ,OAAO,MAAM,KAAK,OAAO,OAAO,oBAAoB;AAC5D,cAAK,MAAM,KAAK,OACd,SAAQ,OAAO,MAAM,UAAU,EAAE,KAAK,YAAY,EAAE,QAAQ,IAAI,EAAE,QAAQ,IAAI;;;AAIpF,cAAO,eAAe,UAAU,SAAS,UAAU,SAAS,GAAG;eACxD,KAAK;AACZ,cAAO,qBAAqB,KAAK,MAAM,IAAI;;;KAGhD,CAAC;IA+BC;GAGD,SAAS;GACV,CAAC;EA+GE,UArBoB,cAAc;GACpC,MAAM;IACJ,MAAM;IACN,aAAa;IACd;GACD,aAAa,EACX,IA9FsB,cAAc;IACtC,MAAM;KACJ,MAAM;KACN,aAAa;KACd;IACD,MAAM;KACJ,WAAW;MACT,MAAM;MACN,aAAa;MACd;KACD,UAAU;MACR,MAAM;MACN,aAAa;MACd;KACD,QAAQ;MAAE,MAAM;MAAU,aAAa;MAAmD;KAC1F,MAAM;MAAE,MAAM;MAAU,aAAa;MAA4B,SAAS;MAAK;KAC/E,MAAM;MAAE,MAAM;MAAW,aAAa;MAAyC,SAAS;MAAO;KAChG;IACD,MAAM,IAAI,EAAE,QAAQ;KAClB,MAAM,MAAM,MAAM,wBAAwB,KAAK;AAC/C,SAAI,CAAC,IAAK;KACV,MAAM,EAAE,SAAS,gBAAgB;KAEjC,MAAM,UAAU,OAAO,KAAK,KAAK;KACjC,MAAM,UAAU,OAAO,QAAQ;AAC/B,SAAI,CAAC,OAAO,SAAS,QAAQ,IAAI,CAAC,OAAO,UAAU,QAAQ,IAAI,UAAU,GAAG;MAC1E,MAAM,UAAU,8CAA8C,KAAK,UAAU,QAAQ,CAAC;AACtF,UAAI,KAAK,KAAM,UAAS;OAAE,IAAI;OAAO,QAAQ;OAAgB;OAAS,CAAC;UAClE,SAAQ,OAAO,MAAM,GAAG,QAAQ,IAAI;AACzC,aAAO,eAAe,SAAS,MAAM;;AAEvC,wBAAmB,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;AAE5C,SAAI;MACF,MAAM,OAAO,MAAM,uBACjB;OACE;OACA,GAAI,KAAK,aAAa,KAAA,IAAY,EAAE,UAAU,OAAO,KAAK,SAAS,EAAE,GAAG,EAAE;OAC1E,GAAI,KAAK,WAAW,KAAA,IAAY,EAAE,QAAQ,OAAO,KAAK,OAAO,EAAE,GAAG,EAAE;OACpE,MAAM;OACP,EACD,QAAQ,QACT;MACD,MAAM,WAAW,KAAK,aAAa,KAAA,IAAY,OAAO,KAAK,SAAS,GAAG;AACvE,UAAI,KAAK,MAAM;AACb,gBAAS;QACP,IAAI;QACJ;QACA;QACA,UAAU,KAAK;QACf,WAAW,KAAK;QAChB,aAAa,KAAK;QACnB,CAAC;AACF,cAAO,eAAe,SAAS,GAAG;;AAEpC,UAAI,KAAK,SAAS,WAAW,GAAG;AAC9B,eAAQ,OAAO,MACb,aAAa,YAAY,IAAI,SAAS,yBAAyB,KAAK,YAAY,IACjF;AACD,cAAO,eAAe,SAAS,GAAG;;AAEpC,cAAQ,OAAO,MACb,aAAa,YAAY,IAAI,SAAS,KAAK,KAAK,SAAS,OAAO,oBAAoB,KAAK,YAAY,MAAM,KAAK,UAAU,IAC3H;AACD,WAAK,MAAM,KAAK,KAAK,UAAU;OAC7B,MAAM,KACJ,OAAO,EAAE,OAAO,YAAY,OAAO,EAAE,OAAO,WACxC,EAAE,KACF,OAAO,EAAE,cAAc,YAAY,OAAO,EAAE,cAAc,WACxD,EAAE,YACF;OACR,MAAM,OACJ,OAAO,EAAE,SAAS,WAAW,EAAE,OAAO,OAAO,EAAE,UAAU,WAAW,EAAE,QAAQ;OAChF,MAAM,YACJ,OAAO,EAAE,cAAc,WACnB,OAAO,EAAE,UAAU,GACnB,OAAO,EAAE,UAAU,WACjB,OAAO,EAAE,MAAM,GACf;AACR,eAAQ,OAAO,MAAM,GAAG,GAAG,IAAI,KAAK,IAAI,UAAU,IAAI;;AAExD,aAAO,eAAe,SAAS,GAAG;cAC3B,KAAK;AACZ,aAAO,qBAAqB,KAAK,MAAM,IAAI;;;IAGhD,CAAC,EASC;GACF,CAAC;EAcC;CACF,CAAC;;;AC51BF,MAAM,OAAO,cAAc;CACzB,MAAM;EACJ,MAAM;EACN,SAAS;EACT,aACE;EACH;CACD,aAAa;EACX,QAAQ;EACR,OAAO;EACP,QAAQ;EACR,MAAM;EACN,SAAS;EACT,WAAW;EACX,KAAK;EACL,SAAS;EACT,MAAM;EACN,SAAS;EACT,IAAI;EACJ,YAAY;EACb;CACF,CAAC;AAEF,8BAA8B,CAAC,YAAY,GAEzC;AAEF,QAAQ,KAAK"}