@ait-co/console-cli 0.1.15 → 0.1.17

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","parsePositiveInt","parseYaml","lsCommand","showCommand","categoriesCommand","BASE","lsCommand","BASE","winPath","fsConstants","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/commands/_shared.ts","../src/config/ait-bundle.ts","../src/commands/app-deploy.ts","../src/config/app-manifest.ts","../src/config/image-validator.ts","../src/commands/register-payload.ts","../src/commands/register.ts","../src/commands/app.ts","../src/commands/completion.ts","../src/api/api-keys.ts","../src/commands/keys.ts","../src/api/me.ts","../src/cdp.ts","../src/chrome.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/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// GET /workspaces/:wid/mini-app/:aid/certs → array of cert records.\n//\n// Observed empty case on app 29405 (2026-04-23): `[]`. Per-record shape\n// not yet observed; passed through opaquely. The console UI exposes\n// \"mTLS 인증서\" for generating client certificates, so a real app\n// will eventually populate this and we can pin fields (probably\n// `certId` / `commonName` / `createdAt` / expiry).\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// --- 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 umbrella `.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} 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// 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 * **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.\n */\nexport async function readSession(): Promise<Session | null> {\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// 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 async function writeSession(session: Session): Promise<void> {\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 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","import { NetworkError, TossApiError } from '../api/http.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// Auth / network / API failure shapes are identical across every command:\n// { ok: true, authenticated: false } (exit 10), { ok: false,\n// reason: 'network-error', message } (exit 11), { ok: false,\n// reason: 'api-error', message } (exit 17). See any per-command\n// `--json contract` block (e.g. `commands/workspace.ts`) for the full\n// exit-code legend plus the success-shape specific to that command —\n// those per-command blocks are the source of truth for success payloads.\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 emitApiError(json, err.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 from `--workspace <id>` or the persisted selection, and handles the\n * three common failure branches (`no session`, `invalid id`, `no workspace\n * selected`). On success, the caller gets the session + resolved id back.\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<{ session: Session; workspaceId: number } | null> {\n const session = await readSession();\n if (!session) {\n emitNotAuthenticated(args.json);\n await exitAfterFlush(ExitCode.NotAuthenticated);\n return null;\n }\n\n let workspaceId: 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 workspaceId = parsed;\n } else {\n workspaceId = session.currentWorkspaceId;\n }\n\n if (workspaceId === undefined) {\n if (args.json) emitJson({ ok: false, reason: 'no-workspace-selected' });\n else {\n process.stderr.write(\n 'No workspace selected. Pass `--workspace <id>` or run `aitcc workspace use <id>`.\\n',\n );\n }\n await exitAfterFlush(ExitCode.Usage);\n return null;\n }\n\n return { session, workspaceId };\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","import { readFile } from 'node:fs/promises';\nimport { unzipSync } from 'fflate';\n\n// An .ait bundle is a zip whose root contains `app.json`. The toolchain\n// that packs the bundle embeds the deployment id at\n// `_metadata.deploymentId`, and `aitcc app bundles upload` needs that id\n// to drive the 3-step init → PUT → complete dance. Reading it here keeps\n// zip-cracking out of the API layer (`src/api/mini-apps.ts` stays\n// transport-only) and gives us a unit-testable seam.\n\nexport type AitBundleErrorReason =\n | 'file-unreadable'\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 interface AitBundleInfo {\n readonly deploymentId: string;\n readonly bytes: Uint8Array;\n}\n\n/**\n * Read the `.ait` at `path`, extract `app.json`, and pull out\n * `_metadata.deploymentId`. Returns both the id and the raw bytes so the\n * caller can forward them to the S3 upload without re-reading the file.\n *\n * Errors are all surfaced as `AitBundleError` with a structured `reason`\n * so the command layer can render a typed `--json` failure.\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 = deploymentIdFromBundleBytes(bytes, path);\n return { deploymentId, bytes };\n}\n\n/**\n * Pure helper split out so tests can feed raw zip bytes without a tmp\n * file. Throws `AitBundleError` on any parse failure.\n */\nexport function deploymentIdFromBundleBytes(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 is synchronous but operates in-memory\n // on a Uint8Array, which is fine for `.ait` bundles (tens of MB\n // worst case; zip central directory is at the tail so partial\n // reads would need an async streaming parser we'd then have to\n // maintain — not worth the complexity for current bundle sizes).\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 { 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, readAitBundle } from '../config/ait-bundle.js';\nimport { ExitCode } from '../exit.js';\nimport { exitAfterFlush } from '../flush.js';\nimport { emitFailureFromError, emitJson, resolveWorkspaceContext } 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// 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// bytes, 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?: (\n path: string,\n ) => Promise<{ readonly deploymentId: string; readonly bytes: Uint8Array }>;\n}\n\nfunction parseAppIdStrict(raw: string): number | null {\n if (raw === '') return 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\nexport async function runDeploy(args: DeployArgs, deps: DeployDeps = {}): Promise<void> {\n // 1. Validate flag shape before loading the session so bad invocations\n // fail fast without the Chrome-spawn detour in case the user is not\n // logged in. Matches `app bundles upload`'s early-exit pattern.\n if (typeof args.app !== 'string' || args.app === '') {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'missing-app-id',\n message: '--app <id> is required',\n });\n } else {\n process.stderr.write('app deploy: --app <id> is required.\\n');\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n const appId = parseAppIdStrict(args.app);\n if (appId === 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: { readonly deploymentId: string; readonly bytes: Uint8Array };\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 (loads session + checks auth). In dry-run we\n // still do this because the `--json` plan includes `workspaceId`\n // and the agent-plugin parses that field unconditionally.\n const ctx = await resolveWorkspaceContext(args);\n if (!ctx) return;\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 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 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 if (json) {\n emitJson({\n ok: false,\n reason: 'api-error',\n status: err.status,\n ...(err.errorCode !== undefined ? { errorCode: err.errorCode } : {}),\n message: err.message,\n ...progress,\n });\n } else {\n process.stderr.write(`Unexpected error: ${err.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 { 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.app.yaml', 'aitcc.app.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.app.yaml` then `aitcc.app.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.\nconst 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\nfunction isValidEmail(v: string): boolean {\n return EMAIL_REGEX.test(v.toLowerCase());\n}\n\n// Dog-food #23 (2026-04-22) HTTP 400 errorCode=4000:\n// \"앱 영문 이름은 영어, 숫자, 공백, 콜론(:)만 사용 가능해요\".\nconst TITLE_EN_REGEX = /^[A-Za-z0-9 :]+$/;\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 = 500;\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 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 colons (got \"${titleEn}\")`,\n 'titleEn',\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 > 20) {\n throw new ManifestError(\n 'invalid-config',\n `subtitle must be 20 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","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 umbrella `.playwright-mcp/`). Dog-food\n// task #23 captures the first real network exchange and will either\n// confirm 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 { ExitCode } from '../exit.js';\nimport { exitAfterFlush } from '../flush.js';\nimport { emitFailureFromError, emitJson, resolveWorkspaceContext } 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 } exit 0\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\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 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 emitSuccess(json: boolean, workspaceId: number, result: CreateMiniAppResult): void {\n if (json) {\n emitJson({\n ok: true,\n workspaceId,\n appId: result.miniAppId ?? null,\n reviewState: result.reviewState ?? null,\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 }\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","import { 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 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 resolveWorkspaceContext,\n} from './_shared.js';\nimport { runDeploy } from './app-deploy.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, apps: [{id, name, reviewState?, extra}] } 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 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\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 if (args.json) {\n const joined = apps.map((app) => {\n const entry = findReviewEntry(review.miniApps, app.id);\n const reviewState = reviewStateFor(entry);\n return {\n id: app.id,\n name: app.name ?? null,\n ...(reviewState !== undefined ? { reviewState } : {}),\n extra: app.extra,\n };\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 for (const app of apps) {\n const entry = findReviewEntry(review.miniApps, app.id);\n const reviewState = reviewStateFor(entry) ?? '-';\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 process.stdout.write(`${app.id}\\t${name}\\t${reviewState}\\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: {...} } 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// `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.\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\nfunction parseAppId(raw: string | undefined): number | null {\n if (raw === undefined || raw === '') return null;\n const n = Number(raw);\n if (!Number.isFinite(n) || !Number.isInteger(n) || n <= 0) return null;\n return n;\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: 'Mini-app ID (the numeric `appId` from `app ls` or `app register`).',\n required: true,\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 json: { type: 'boolean', description: 'Emit machine-readable JSON to stdout.', default: false },\n },\n async run({ args }) {\n const appId = parseAppId(args.id);\n if (appId === null) {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'invalid-id',\n message: `app id must be a positive integer (got ${JSON.stringify(args.id)})`,\n });\n } else {\n process.stderr.write(`app show: invalid id ${JSON.stringify(args.id)}\\n`);\n }\n return exitAfterFlush(ExitCode.Usage);\n }\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 const ctx = await resolveWorkspaceContext(args);\n if (!ctx) return;\n const { session, workspaceId } = ctx;\n\n try {\n const envelope = await fetchMiniAppWithDraft(workspaceId, appId, session.cookies);\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 });\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 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\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}\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 return { state, approvalType, rejectedMessage, hasCurrent, hasDraft };\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.',\n required: true,\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 appId = parseAppId(args.id);\n if (appId === null) {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'invalid-id',\n message: `app id must be a positive integer (got ${JSON.stringify(args.id)})`,\n });\n } else {\n process.stderr.write(`app status: invalid id ${JSON.stringify(args.id)}\\n`);\n }\n return exitAfterFlush(ExitCode.Usage);\n }\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 resolveWorkspaceContext(args);\n if (!ctx) return;\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.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: { type: 'positional', description: 'Mini-app ID.', required: true },\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 appId = parseAppId(args.id);\n if (appId === null) {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'invalid-id',\n message: `app id must be a positive integer (got ${JSON.stringify(args.id)})`,\n });\n } else {\n process.stderr.write(`app ratings: invalid id ${JSON.stringify(args.id)}\\n`);\n }\n return exitAfterFlush(ExitCode.Usage);\n }\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 resolveWorkspaceContext(args);\n if (!ctx) return;\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: { type: 'positional', description: 'Mini-app ID.', required: true },\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 appId = parseAppId(args.id);\n if (appId === null) {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'invalid-id',\n message: `app id must be a positive integer (got ${JSON.stringify(args.id)})`,\n });\n } else {\n process.stderr.write(`app reports: invalid id ${JSON.stringify(args.id)}\\n`);\n }\n return exitAfterFlush(ExitCode.Usage);\n }\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 resolveWorkspaceContext(args);\n if (!ctx) return;\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: { type: 'positional', description: 'Mini-app ID.', required: true },\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 appId = parseAppId(args.id);\n if (appId === null) {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'invalid-id',\n message: `app id must be a positive integer (got ${JSON.stringify(args.id)})`,\n });\n } else {\n process.stderr.write(`app bundles ls: invalid id ${JSON.stringify(args.id)}\\n`);\n }\n return exitAfterFlush(ExitCode.Usage);\n }\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 resolveWorkspaceContext(args);\n if (!ctx) return;\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: { type: 'positional', description: 'Mini-app ID.', required: true },\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 appId = parseAppId(args.id);\n if (appId === null) {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'invalid-id',\n message: `app id must be a positive integer (got ${JSON.stringify(args.id)})`,\n });\n } else {\n process.stderr.write(`app bundles deployed: invalid id ${JSON.stringify(args.id)}\\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\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 id: { type: 'positional', description: 'Mini-app ID.', required: true },\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 appId = parseAppId(args.id);\n if (appId === null) {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'invalid-id',\n message: `app id must be a positive integer (got ${JSON.stringify(args.id)})`,\n });\n } else {\n process.stderr.write(`app bundles upload: invalid id ${JSON.stringify(args.id)}\\n`);\n }\n return exitAfterFlush(ExitCode.Usage);\n }\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 resolveWorkspaceContext(args);\n if (!ctx) return;\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: { type: 'positional', description: 'Mini-app ID.', required: true },\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 appId = parseAppId(args.id);\n if (appId === null) {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'invalid-id',\n message: `app id must be a positive integer (got ${JSON.stringify(args.id)})`,\n });\n } else {\n process.stderr.write(`app bundles review: invalid id ${JSON.stringify(args.id)}\\n`);\n }\n return exitAfterFlush(ExitCode.Usage);\n }\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 resolveWorkspaceContext(args);\n if (!ctx) return;\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: { type: 'positional', description: 'Mini-app ID.', required: true },\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 appId = parseAppId(args.id);\n if (appId === null) {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'invalid-id',\n message: `app id must be a positive integer (got ${JSON.stringify(args.id)})`,\n });\n } else {\n process.stderr.write(`app bundles release: invalid id ${JSON.stringify(args.id)}\\n`);\n }\n return exitAfterFlush(ExitCode.Usage);\n }\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 resolveWorkspaceContext(args);\n if (!ctx) return;\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: { type: 'positional', description: 'Mini-app ID.', required: true },\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 appId = parseAppId(args.id);\n if (appId === null) {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'invalid-id',\n message: `app id must be a positive integer (got ${JSON.stringify(args.id)})`,\n });\n } else {\n process.stderr.write(`app bundles test-push: invalid id ${JSON.stringify(args.id)}\\n`);\n }\n return exitAfterFlush(ExitCode.Usage);\n }\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 resolveWorkspaceContext(args);\n if (!ctx) return;\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: { type: 'positional', description: 'Mini-app ID.', required: true },\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 appId = parseAppId(args.id);\n if (appId === null) {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'invalid-id',\n message: `app id must be a positive integer (got ${JSON.stringify(args.id)})`,\n });\n } else {\n process.stderr.write(`app bundles test-links: invalid id ${JSON.stringify(args.id)}\\n`);\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n const ctx = await resolveWorkspaceContext(args);\n if (!ctx) return;\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: { type: 'positional', description: 'Mini-app ID.', required: true },\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 appId = parseAppId(args.id);\n if (appId === null) {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'invalid-id',\n message: `app id must be a positive integer (got ${JSON.stringify(args.id)})`,\n });\n } else {\n process.stderr.write(`app certs ls: invalid id ${JSON.stringify(args.id)}\\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\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\nconst certsCommand = defineCommand({\n meta: {\n name: 'certs',\n description: 'Inspect mTLS certificates for a mini-app.',\n },\n subCommands: {\n ls: certsLsCommand,\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: { type: 'positional', description: 'Mini-app ID.', required: true },\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 appId = parseAppId(args.id);\n if (appId === null) {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'invalid-id',\n message: `app id must be a positive integer (got ${JSON.stringify(args.id)})`,\n });\n } else {\n process.stderr.write(`app metrics: invalid id ${JSON.stringify(args.id)}\\n`);\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n\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 resolveWorkspaceContext(args);\n if (!ctx) return;\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: { type: 'positional', description: 'Mini-app ID.', required: true },\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 appId = parseAppId(args.id);\n if (appId === null) {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'invalid-id',\n message: `app id must be a positive integer (got ${JSON.stringify(args.id)})`,\n });\n } else {\n process.stderr.write(`app share-rewards ls: invalid id ${JSON.stringify(args.id)}\\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\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: { type: 'positional', description: 'Mini-app ID.', required: true },\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 appId = parseAppId(args.id);\n if (appId === null) {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'invalid-id',\n message: `app id must be a positive integer (got ${JSON.stringify(args.id)})`,\n });\n } else {\n process.stderr.write(`app messages ls: invalid id ${JSON.stringify(args.id)}\\n`);\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n\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 resolveWorkspaceContext(args);\n if (!ctx) return;\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: { type: 'positional', description: 'Mini-app ID.', required: true },\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 appId = parseAppId(args.id);\n if (appId === null) {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'invalid-id',\n message: `app id must be a positive integer (got ${JSON.stringify(args.id)})`,\n });\n } else {\n process.stderr.write(`app events ls: invalid id ${JSON.stringify(args.id)}\\n`);\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n\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 resolveWorkspaceContext(args);\n if (!ctx) return;\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: { type: 'positional', description: 'Mini-app ID.', required: true },\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 appId = parseAppId(args.id);\n if (appId === null) {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'invalid-id',\n message: `app id must be a positive integer (got ${JSON.stringify(args.id)})`,\n });\n } else {\n process.stderr.write(`app templates ls: invalid id ${JSON.stringify(args.id)}\\n`);\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n\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 resolveWorkspaceContext(args);\n if (!ctx) return;\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: { type: 'positional', description: 'Mini-app ID.', required: true },\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 appId = parseAppId(args.id);\n if (appId === null) {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'invalid-id',\n message: `app id must be a positive integer (got ${JSON.stringify(args.id)})`,\n });\n } else {\n process.stderr.write(`app service-status: invalid id ${JSON.stringify(args.id)}\\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\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 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:\n 'Path to the app manifest. Defaults to `./aitcc.app.yaml`, then `./aitcc.app.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: 'Mini-app ID. Required — no top-level \"selected app\" concept yet.',\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 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 { 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 '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 '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 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// GET /workspaces/:id/api-keys — returns an array of console API keys used\n// for deploy automation. Our confirmed workspaces have zero keys (the UI\n// shows a \"발급받기\" CTA when the list is empty), so the entry shape is\n// unconfirmed. We normalise `id`/`name` across a few plausible spellings\n// and stash everything else under `extra`, matching the mini-app pattern.\n//\n// `keys create` is a deliberate follow-up — once an issued key lands we can\n// tighten this client against the real shape. See TODO.md's Medium list.\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 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 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 {\n id: _id,\n apiKeyId: _aid,\n keyId: _kid,\n name: _n,\n apiKeyName: _an,\n keyName: _kn,\n description: _d,\n ...extra\n } = rec;\n return { id: rawId, name, extra };\n}\n","import { defineCommand } from 'citty';\nimport { fetchApiKeys } from '../api/api-keys.js';\nimport { ExitCode } from '../exit.js';\nimport { exitAfterFlush } from '../flush.js';\nimport { emitFailureFromError, emitJson, resolveWorkspaceContext } from './_shared.js';\n\n// --json contract (consumed by agent-plugin):\n//\n// keys ls [--workspace <id>]:\n// { ok: true, workspaceId, keys: [{id, name, 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// 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. We only list here; `keys create` is a follow-up\n// (the management UI 404s until an initial key is issued, so we don't\n// know the creation/rotation endpoint yet).\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\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) => ({ id: k.id, name: k.name ?? null, extra: k.extra })),\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: issue a key from the console UI (API 키 → 발급받기) to enable deploy automation.\\n',\n );\n return exitAfterFlush(ExitCode.Ok);\n }\n process.stdout.write(`${keys.length} API key(s) in workspace ${workspaceId}:\\n`);\n for (const k of keys) {\n const name = k.name ?? '(unnamed)';\n process.stdout.write(`${k.id}\\t${name}\\n`);\n }\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\nexport const keysCommand = defineCommand({\n meta: {\n name: 'keys',\n description: 'Inspect console API keys used for deploy automation.',\n },\n subCommands: {\n ls: lsCommand,\n },\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","// 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\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 // 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 options.initialUrl,\n ];\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","import { defineCommand } from 'citty';\nimport { type FetchLike, TossApiError } from '../api/http.js';\nimport { fetchConsoleMemberUserInfo } from '../api/me.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 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\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\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 },\n async run({ args }) {\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 // 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 // Launch Chrome.\n const launched = await launchChrome({\n initialUrl: authorizeUrl,\n endpointTimeoutMs,\n }).catch((err: Error) => err);\n if (launched instanceof ChromeNotFoundError) {\n emitError({ reason: 'chrome-not-found', candidates: launched.candidates }, launched.message);\n return exitAfterFlush(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 exitAfterFlush(ExitCode.LoginBrowserFailed);\n }\n if (launched instanceof Error) {\n // An unexpected Error type — keep enough context to diagnose later.\n emitError(\n {\n reason: 'chrome-launch-failed',\n errorName: launched.name,\n message: launched.message,\n },\n `Failed to launch browser (${launched.name}): ${launched.message}`,\n );\n return exitAfterFlush(ExitCode.LoginBrowserFailed);\n }\n\n process.stderr.write(\n 'Opened a browser window — complete the sign-in there. The CLI will capture the session automatically.\\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. A `try/finally`\n // wrapper is *not* safe here — the enclosing async function's finally\n // races `process.exit` and may skip the SIGTERM + rm -rf.\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 const exitWith = async (code: number): Promise<never> => {\n await disposeAll();\n return exitAfterFlush(code);\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 return exitWith(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 return exitWith(ExitCode.LoginBrowserFailed);\n }\n\n const landing = await waitForLanding(client, attached.sessionId, timeoutMs);\n if (landing === 'timeout') {\n emitError({ reason: 'login-timeout', timeoutSec }, `Login timed out after ${timeoutSec}s.`);\n return exitWith(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 return exitWith(ExitCode.LoginBrowserFailed);\n }\n\n // Pull all cookies across all origins the browser has collected.\n // `Network.getAllCookies` requires a target session (it isn't exposed\n // on the browser-level endpoint), so we route through the attached\n // page. The returned set still spans every origin the browser has\n // stored cookies for (business.toss.im, business-accounts.toss.im,\n // apps-in-toss.toss.im), not just the current page.\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 return exitWith(ExitCode.LoginCookieCaptureFailed);\n }\n\n // Resolve identity via the console member info endpoint. This also\n // doubles as a liveness check — a session that can't call /me means\n // we captured cookies too early (before the auth-code exchange\n // completed). If so, we wait briefly and retry once.\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 return exitWith(authFailed ? ExitCode.LoginCookieCaptureFailed : ExitCode.ApiError);\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 return exitWith(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 })}\\n`,\n );\n } else {\n process.stdout.write(`Logged in as ${user.name} <${user.email}>\\n`);\n }\n return exitWith(ExitCode.Ok);\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 { emitFailureFromError, emitJson, resolveWorkspaceContext } 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\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\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","// 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 { chmod, rename, writeFile } from 'node:fs/promises';\nimport { basename, dirname } from 'node:path';\nimport { defineCommand } from 'citty';\nimport { ExitCode } from '../exit.js';\nimport { fetchLatestRelease, versionFromTag } from '../github.js';\nimport { detectPlatform } from '../platform.js';\nimport { compareSemver } from '../semver.js';\nimport { VERSION } from '../version.js';\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\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 // 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 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 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, readFile, rename, unlink, writeFile } from 'node:fs/promises';\nimport { dirname } 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 await mkdir(dirname(path), { recursive: true });\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\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","import { defineCommand } from 'citty';\nimport { fetchConsoleMemberUserInfo } from '../api/me.js';\nimport {\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} 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 | --all] [--workspace <id>]:\n// { ok: true, workspaceId, type, terms: WorkspaceTerm[] } exit 0 (single type)\n// { ok: true, workspaceId, byType: { TYPE: WorkspaceTerm[] } } exit 0 (--all)\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// 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 session = await readSession();\n if (!session) {\n emitNotAuthenticated(args.json);\n return exitAfterFlush(ExitCode.NotAuthenticated);\n }\n\n let workspaceId: 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) {\n emitJson({ ok: false, reason: 'invalid-id', message });\n } else {\n process.stderr.write(`${message}\\n`);\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n workspaceId = parsed;\n } else {\n workspaceId = session.currentWorkspaceId;\n }\n\n if (workspaceId === undefined) {\n if (args.json) {\n emitJson({ ok: false, reason: 'no-workspace-selected' });\n } else {\n process.stderr.write(\n 'No workspace selected. Pass `--workspace <id>` or run `aitcc workspace use <id>`.\\n',\n );\n }\n return exitAfterFlush(ExitCode.Usage);\n }\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\n// Shared helper for any workspace subcommand that takes an optional\n// --workspace flag and falls back to the selected workspace. Returns\n// either a numeric workspace id or emits the appropriate JSON/stderr\n// failure and returns null — callers should exit(Usage) on null.\nasync function resolveWorkspaceArg(\n args: { workspace?: unknown; json: boolean },\n selected: number | undefined,\n): Promise<number | null> {\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) {\n emitJson({ ok: false, reason: 'invalid-id', message });\n } else {\n process.stderr.write(`${message}\\n`);\n }\n return null;\n }\n return parsed;\n }\n if (selected === undefined) {\n if (args.json) {\n emitJson({ ok: false, reason: 'no-workspace-selected' });\n } else {\n process.stderr.write(\n 'No workspace selected. Pass `--workspace <id>` or run `aitcc workspace use <id>`.\\n',\n );\n }\n return null;\n }\n return selected;\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 session = await readSession();\n if (!session) {\n emitNotAuthenticated(args.json);\n return exitAfterFlush(ExitCode.NotAuthenticated);\n }\n const workspaceId = await resolveWorkspaceArg(args, session.currentWorkspaceId);\n if (workspaceId === null) return exitAfterFlush(ExitCode.Usage);\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\nconst termsCommand = defineCommand({\n meta: {\n name: 'terms',\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 session = await readSession();\n if (!session) {\n emitNotAuthenticated(args.json);\n return exitAfterFlush(ExitCode.NotAuthenticated);\n }\n const workspaceId = await resolveWorkspaceArg(args, session.currentWorkspaceId);\n if (workspaceId === null) return exitAfterFlush(ExitCode.Usage);\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\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 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 });\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) process.stdout.write(formatTermLines(t));\n }\n return exitAfterFlush(ExitCode.Ok);\n }\n\n // --all path\n const byType: Record<string, readonly WorkspaceTerm[]> = {};\n for (const [t, terms] of results) byType[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) process.stdout.write(formatTermLines(t));\n }\n }\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\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 session = await readSession();\n if (!session) {\n emitNotAuthenticated(args.json);\n return exitAfterFlush(ExitCode.NotAuthenticated);\n }\n const workspaceId = await resolveWorkspaceArg(args, session.currentWorkspaceId);\n if (workspaceId === null) return exitAfterFlush(ExitCode.Usage);\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\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 { 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 { 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 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\nrunMain(main);\n"],"mappings":"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"}
1
+ {"version":3,"file":"cli.mjs","names":["BASE","parsePositiveInt","parseYaml","lsCommand","showCommand","categoriesCommand","BASE","lsCommand","BASE","winPath","fsConstants","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/commands/_shared.ts","../src/config/ait-bundle.ts","../src/commands/app-deploy.ts","../src/config/app-manifest.ts","../src/config/image-validator.ts","../src/commands/register-payload.ts","../src/commands/register.ts","../src/commands/app.ts","../src/commands/completion.ts","../src/api/api-keys.ts","../src/commands/keys.ts","../src/api/me.ts","../src/cdp.ts","../src/chrome.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// GET /workspaces/:wid/mini-app/:aid/certs → array of cert records.\n//\n// Observed empty case on app 29405 (2026-04-23): `[]`. Per-record shape\n// not yet observed; passed through opaquely. The console UI exposes\n// \"mTLS 인증서\" for generating client certificates, so a real app\n// will eventually populate this and we can pin fields (probably\n// `certId` / `commonName` / `createdAt` / expiry).\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// --- 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 umbrella `.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} 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// 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 * **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.\n */\nexport async function readSession(): Promise<Session | null> {\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// 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 async function writeSession(session: Session): Promise<void> {\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 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","import { NetworkError, TossApiError } from '../api/http.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// Auth / network / API failure shapes are identical across every command:\n// { ok: true, authenticated: false } (exit 10), { ok: false,\n// reason: 'network-error', message } (exit 11), { ok: false,\n// reason: 'api-error', message } (exit 17). See any per-command\n// `--json contract` block (e.g. `commands/workspace.ts`) for the full\n// exit-code legend plus the success-shape specific to that command —\n// those per-command blocks are the source of truth for success payloads.\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 emitApiError(json, err.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 from `--workspace <id>` or the persisted selection, and handles the\n * three common failure branches (`no session`, `invalid id`, `no workspace\n * selected`). On success, the caller gets the session + resolved id back.\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<{ session: Session; workspaceId: number } | null> {\n const session = await readSession();\n if (!session) {\n emitNotAuthenticated(args.json);\n await exitAfterFlush(ExitCode.NotAuthenticated);\n return null;\n }\n\n let workspaceId: 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 workspaceId = parsed;\n } else {\n workspaceId = session.currentWorkspaceId;\n }\n\n if (workspaceId === undefined) {\n if (args.json) emitJson({ ok: false, reason: 'no-workspace-selected' });\n else {\n process.stderr.write(\n 'No workspace selected. Pass `--workspace <id>` or run `aitcc workspace use <id>`.\\n',\n );\n }\n await exitAfterFlush(ExitCode.Usage);\n return null;\n }\n\n return { session, workspaceId };\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","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 { 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 { emitFailureFromError, emitJson, resolveWorkspaceContext } 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\nfunction parseAppIdStrict(raw: string): number | null {\n if (raw === '') return 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\nexport async function runDeploy(args: DeployArgs, deps: DeployDeps = {}): Promise<void> {\n // 1. Validate flag shape before loading the session so bad invocations\n // fail fast without the Chrome-spawn detour in case the user is not\n // logged in. Matches `app bundles upload`'s early-exit pattern.\n if (typeof args.app !== 'string' || args.app === '') {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'missing-app-id',\n message: '--app <id> is required',\n });\n } else {\n process.stderr.write('app deploy: --app <id> is required.\\n');\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n const appId = parseAppIdStrict(args.app);\n if (appId === 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 (loads session + checks auth). In dry-run we\n // still do this because the `--json` plan includes `workspaceId`\n // and the agent-plugin parses that field unconditionally.\n const ctx = await resolveWorkspaceContext(args);\n if (!ctx) return;\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 if (json) {\n emitJson({\n ok: false,\n reason: 'api-error',\n status: err.status,\n ...(err.errorCode !== undefined ? { errorCode: err.errorCode } : {}),\n message: err.message,\n ...progress,\n });\n } else {\n process.stderr.write(`Unexpected error: ${err.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 { 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.app.yaml', 'aitcc.app.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.app.yaml` then `aitcc.app.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.\nconst 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\nfunction isValidEmail(v: string): boolean {\n return EMAIL_REGEX.test(v.toLowerCase());\n}\n\n// Dog-food #23 (2026-04-22) HTTP 400 errorCode=4000:\n// \"앱 영문 이름은 영어, 숫자, 공백, 콜론(:)만 사용 가능해요\".\nconst TITLE_EN_REGEX = /^[A-Za-z0-9 :]+$/;\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 = 500;\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 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 colons (got \"${titleEn}\")`,\n 'titleEn',\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 > 20) {\n throw new ManifestError(\n 'invalid-config',\n `subtitle must be 20 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","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 umbrella `.playwright-mcp/`). Dog-food\n// task #23 captures the first real network exchange and will either\n// confirm 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 { ExitCode } from '../exit.js';\nimport { exitAfterFlush } from '../flush.js';\nimport { emitFailureFromError, emitJson, resolveWorkspaceContext } 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 } exit 0\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\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 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 emitSuccess(json: boolean, workspaceId: number, result: CreateMiniAppResult): void {\n if (json) {\n emitJson({\n ok: true,\n workspaceId,\n appId: result.miniAppId ?? null,\n reviewState: result.reviewState ?? null,\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 }\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","import { 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 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 resolveWorkspaceContext,\n} from './_shared.js';\nimport { runDeploy } from './app-deploy.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, apps: [{id, name, reviewState?, extra}] } 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 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\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 if (args.json) {\n const joined = apps.map((app) => {\n const entry = findReviewEntry(review.miniApps, app.id);\n const reviewState = reviewStateFor(entry);\n return {\n id: app.id,\n name: app.name ?? null,\n ...(reviewState !== undefined ? { reviewState } : {}),\n extra: app.extra,\n };\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 for (const app of apps) {\n const entry = findReviewEntry(review.miniApps, app.id);\n const reviewState = reviewStateFor(entry) ?? '-';\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 process.stdout.write(`${app.id}\\t${name}\\t${reviewState}\\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: {...} } 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// `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.\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\nfunction parseAppId(raw: string | undefined): number | null {\n if (raw === undefined || raw === '') return null;\n const n = Number(raw);\n if (!Number.isFinite(n) || !Number.isInteger(n) || n <= 0) return null;\n return n;\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: 'Mini-app ID (the numeric `appId` from `app ls` or `app register`).',\n required: true,\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 json: { type: 'boolean', description: 'Emit machine-readable JSON to stdout.', default: false },\n },\n async run({ args }) {\n const appId = parseAppId(args.id);\n if (appId === null) {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'invalid-id',\n message: `app id must be a positive integer (got ${JSON.stringify(args.id)})`,\n });\n } else {\n process.stderr.write(`app show: invalid id ${JSON.stringify(args.id)}\\n`);\n }\n return exitAfterFlush(ExitCode.Usage);\n }\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 const ctx = await resolveWorkspaceContext(args);\n if (!ctx) return;\n const { session, workspaceId } = ctx;\n\n try {\n const envelope = await fetchMiniAppWithDraft(workspaceId, appId, session.cookies);\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 });\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 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\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}\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 return { state, approvalType, rejectedMessage, hasCurrent, hasDraft };\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.',\n required: true,\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 appId = parseAppId(args.id);\n if (appId === null) {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'invalid-id',\n message: `app id must be a positive integer (got ${JSON.stringify(args.id)})`,\n });\n } else {\n process.stderr.write(`app status: invalid id ${JSON.stringify(args.id)}\\n`);\n }\n return exitAfterFlush(ExitCode.Usage);\n }\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 resolveWorkspaceContext(args);\n if (!ctx) return;\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.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: { type: 'positional', description: 'Mini-app ID.', required: true },\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 appId = parseAppId(args.id);\n if (appId === null) {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'invalid-id',\n message: `app id must be a positive integer (got ${JSON.stringify(args.id)})`,\n });\n } else {\n process.stderr.write(`app ratings: invalid id ${JSON.stringify(args.id)}\\n`);\n }\n return exitAfterFlush(ExitCode.Usage);\n }\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 resolveWorkspaceContext(args);\n if (!ctx) return;\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: { type: 'positional', description: 'Mini-app ID.', required: true },\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 appId = parseAppId(args.id);\n if (appId === null) {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'invalid-id',\n message: `app id must be a positive integer (got ${JSON.stringify(args.id)})`,\n });\n } else {\n process.stderr.write(`app reports: invalid id ${JSON.stringify(args.id)}\\n`);\n }\n return exitAfterFlush(ExitCode.Usage);\n }\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 resolveWorkspaceContext(args);\n if (!ctx) return;\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: { type: 'positional', description: 'Mini-app ID.', required: true },\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 appId = parseAppId(args.id);\n if (appId === null) {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'invalid-id',\n message: `app id must be a positive integer (got ${JSON.stringify(args.id)})`,\n });\n } else {\n process.stderr.write(`app bundles ls: invalid id ${JSON.stringify(args.id)}\\n`);\n }\n return exitAfterFlush(ExitCode.Usage);\n }\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 resolveWorkspaceContext(args);\n if (!ctx) return;\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: { type: 'positional', description: 'Mini-app ID.', required: true },\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 appId = parseAppId(args.id);\n if (appId === null) {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'invalid-id',\n message: `app id must be a positive integer (got ${JSON.stringify(args.id)})`,\n });\n } else {\n process.stderr.write(`app bundles deployed: invalid id ${JSON.stringify(args.id)}\\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\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 id: { type: 'positional', description: 'Mini-app ID.', required: true },\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 appId = parseAppId(args.id);\n if (appId === null) {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'invalid-id',\n message: `app id must be a positive integer (got ${JSON.stringify(args.id)})`,\n });\n } else {\n process.stderr.write(`app bundles upload: invalid id ${JSON.stringify(args.id)}\\n`);\n }\n return exitAfterFlush(ExitCode.Usage);\n }\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 resolveWorkspaceContext(args);\n if (!ctx) return;\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: { type: 'positional', description: 'Mini-app ID.', required: true },\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 appId = parseAppId(args.id);\n if (appId === null) {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'invalid-id',\n message: `app id must be a positive integer (got ${JSON.stringify(args.id)})`,\n });\n } else {\n process.stderr.write(`app bundles review: invalid id ${JSON.stringify(args.id)}\\n`);\n }\n return exitAfterFlush(ExitCode.Usage);\n }\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 resolveWorkspaceContext(args);\n if (!ctx) return;\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: { type: 'positional', description: 'Mini-app ID.', required: true },\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 appId = parseAppId(args.id);\n if (appId === null) {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'invalid-id',\n message: `app id must be a positive integer (got ${JSON.stringify(args.id)})`,\n });\n } else {\n process.stderr.write(`app bundles release: invalid id ${JSON.stringify(args.id)}\\n`);\n }\n return exitAfterFlush(ExitCode.Usage);\n }\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 resolveWorkspaceContext(args);\n if (!ctx) return;\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: { type: 'positional', description: 'Mini-app ID.', required: true },\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 appId = parseAppId(args.id);\n if (appId === null) {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'invalid-id',\n message: `app id must be a positive integer (got ${JSON.stringify(args.id)})`,\n });\n } else {\n process.stderr.write(`app bundles test-push: invalid id ${JSON.stringify(args.id)}\\n`);\n }\n return exitAfterFlush(ExitCode.Usage);\n }\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 resolveWorkspaceContext(args);\n if (!ctx) return;\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: { type: 'positional', description: 'Mini-app ID.', required: true },\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 appId = parseAppId(args.id);\n if (appId === null) {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'invalid-id',\n message: `app id must be a positive integer (got ${JSON.stringify(args.id)})`,\n });\n } else {\n process.stderr.write(`app bundles test-links: invalid id ${JSON.stringify(args.id)}\\n`);\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n const ctx = await resolveWorkspaceContext(args);\n if (!ctx) return;\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: { type: 'positional', description: 'Mini-app ID.', required: true },\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 appId = parseAppId(args.id);\n if (appId === null) {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'invalid-id',\n message: `app id must be a positive integer (got ${JSON.stringify(args.id)})`,\n });\n } else {\n process.stderr.write(`app certs ls: invalid id ${JSON.stringify(args.id)}\\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\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\nconst certsCommand = defineCommand({\n meta: {\n name: 'certs',\n description: 'Inspect mTLS certificates for a mini-app.',\n },\n subCommands: {\n ls: certsLsCommand,\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: { type: 'positional', description: 'Mini-app ID.', required: true },\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 appId = parseAppId(args.id);\n if (appId === null) {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'invalid-id',\n message: `app id must be a positive integer (got ${JSON.stringify(args.id)})`,\n });\n } else {\n process.stderr.write(`app metrics: invalid id ${JSON.stringify(args.id)}\\n`);\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n\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 resolveWorkspaceContext(args);\n if (!ctx) return;\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: { type: 'positional', description: 'Mini-app ID.', required: true },\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 appId = parseAppId(args.id);\n if (appId === null) {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'invalid-id',\n message: `app id must be a positive integer (got ${JSON.stringify(args.id)})`,\n });\n } else {\n process.stderr.write(`app share-rewards ls: invalid id ${JSON.stringify(args.id)}\\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\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: { type: 'positional', description: 'Mini-app ID.', required: true },\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 appId = parseAppId(args.id);\n if (appId === null) {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'invalid-id',\n message: `app id must be a positive integer (got ${JSON.stringify(args.id)})`,\n });\n } else {\n process.stderr.write(`app messages ls: invalid id ${JSON.stringify(args.id)}\\n`);\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n\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 resolveWorkspaceContext(args);\n if (!ctx) return;\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: { type: 'positional', description: 'Mini-app ID.', required: true },\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 appId = parseAppId(args.id);\n if (appId === null) {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'invalid-id',\n message: `app id must be a positive integer (got ${JSON.stringify(args.id)})`,\n });\n } else {\n process.stderr.write(`app events ls: invalid id ${JSON.stringify(args.id)}\\n`);\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n\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 resolveWorkspaceContext(args);\n if (!ctx) return;\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: { type: 'positional', description: 'Mini-app ID.', required: true },\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 appId = parseAppId(args.id);\n if (appId === null) {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'invalid-id',\n message: `app id must be a positive integer (got ${JSON.stringify(args.id)})`,\n });\n } else {\n process.stderr.write(`app templates ls: invalid id ${JSON.stringify(args.id)}\\n`);\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n\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 resolveWorkspaceContext(args);\n if (!ctx) return;\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: { type: 'positional', description: 'Mini-app ID.', required: true },\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 appId = parseAppId(args.id);\n if (appId === null) {\n if (args.json) {\n emitJson({\n ok: false,\n reason: 'invalid-id',\n message: `app id must be a positive integer (got ${JSON.stringify(args.id)})`,\n });\n } else {\n process.stderr.write(`app service-status: invalid id ${JSON.stringify(args.id)}\\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\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 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:\n 'Path to the app manifest. Defaults to `./aitcc.app.yaml`, then `./aitcc.app.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: 'Mini-app ID. Required — no top-level \"selected app\" concept yet.',\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 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 { 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 '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 '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 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// GET /workspaces/:id/api-keys — returns an array of console API keys used\n// for deploy automation. Our confirmed workspaces have zero keys (the UI\n// shows a \"발급받기\" CTA when the list is empty), so the entry shape is\n// unconfirmed. We normalise `id`/`name` across a few plausible spellings\n// and stash everything else under `extra`, matching the mini-app pattern.\n//\n// `keys create` is a deliberate follow-up — once an issued key lands we can\n// tighten this client against the real shape. See TODO.md's Medium list.\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 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 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 {\n id: _id,\n apiKeyId: _aid,\n keyId: _kid,\n name: _n,\n apiKeyName: _an,\n keyName: _kn,\n description: _d,\n ...extra\n } = rec;\n return { id: rawId, name, extra };\n}\n","import { defineCommand } from 'citty';\nimport { fetchApiKeys } from '../api/api-keys.js';\nimport { ExitCode } from '../exit.js';\nimport { exitAfterFlush } from '../flush.js';\nimport { emitFailureFromError, emitJson, resolveWorkspaceContext } from './_shared.js';\n\n// --json contract (consumed by agent-plugin):\n//\n// keys ls [--workspace <id>]:\n// { ok: true, workspaceId, keys: [{id, name, 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// 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. We only list here; `keys create` is a follow-up\n// (the management UI 404s until an initial key is issued, so we don't\n// know the creation/rotation endpoint yet).\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\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) => ({ id: k.id, name: k.name ?? null, extra: k.extra })),\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: issue a key from the console UI (API 키 → 발급받기) to enable deploy automation.\\n',\n );\n return exitAfterFlush(ExitCode.Ok);\n }\n process.stdout.write(`${keys.length} API key(s) in workspace ${workspaceId}:\\n`);\n for (const k of keys) {\n const name = k.name ?? '(unnamed)';\n process.stdout.write(`${k.id}\\t${name}\\n`);\n }\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\n});\n\nexport const keysCommand = defineCommand({\n meta: {\n name: 'keys',\n description: 'Inspect console API keys used for deploy automation.',\n },\n subCommands: {\n ls: lsCommand,\n },\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","// 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\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 // 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 options.initialUrl,\n ];\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","import { defineCommand } from 'citty';\nimport { type FetchLike, TossApiError } from '../api/http.js';\nimport { fetchConsoleMemberUserInfo } from '../api/me.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 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\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\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 },\n async run({ args }) {\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 // 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 // Launch Chrome.\n const launched = await launchChrome({\n initialUrl: authorizeUrl,\n endpointTimeoutMs,\n }).catch((err: Error) => err);\n if (launched instanceof ChromeNotFoundError) {\n emitError({ reason: 'chrome-not-found', candidates: launched.candidates }, launched.message);\n return exitAfterFlush(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 exitAfterFlush(ExitCode.LoginBrowserFailed);\n }\n if (launched instanceof Error) {\n // An unexpected Error type — keep enough context to diagnose later.\n emitError(\n {\n reason: 'chrome-launch-failed',\n errorName: launched.name,\n message: launched.message,\n },\n `Failed to launch browser (${launched.name}): ${launched.message}`,\n );\n return exitAfterFlush(ExitCode.LoginBrowserFailed);\n }\n\n process.stderr.write(\n 'Opened a browser window — complete the sign-in there. The CLI will capture the session automatically.\\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. A `try/finally`\n // wrapper is *not* safe here — the enclosing async function's finally\n // races `process.exit` and may skip the SIGTERM + rm -rf.\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 const exitWith = async (code: number): Promise<never> => {\n await disposeAll();\n return exitAfterFlush(code);\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 return exitWith(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 return exitWith(ExitCode.LoginBrowserFailed);\n }\n\n const landing = await waitForLanding(client, attached.sessionId, timeoutMs);\n if (landing === 'timeout') {\n emitError({ reason: 'login-timeout', timeoutSec }, `Login timed out after ${timeoutSec}s.`);\n return exitWith(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 return exitWith(ExitCode.LoginBrowserFailed);\n }\n\n // Pull all cookies across all origins the browser has collected.\n // `Network.getAllCookies` requires a target session (it isn't exposed\n // on the browser-level endpoint), so we route through the attached\n // page. The returned set still spans every origin the browser has\n // stored cookies for (business.toss.im, business-accounts.toss.im,\n // apps-in-toss.toss.im), not just the current page.\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 return exitWith(ExitCode.LoginCookieCaptureFailed);\n }\n\n // Resolve identity via the console member info endpoint. This also\n // doubles as a liveness check — a session that can't call /me means\n // we captured cookies too early (before the auth-code exchange\n // completed). If so, we wait briefly and retry once.\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 return exitWith(authFailed ? ExitCode.LoginCookieCaptureFailed : ExitCode.ApiError);\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 return exitWith(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 })}\\n`,\n );\n } else {\n process.stdout.write(`Logged in as ${user.name} <${user.email}>\\n`);\n }\n return exitWith(ExitCode.Ok);\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 { emitFailureFromError, emitJson, resolveWorkspaceContext } 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\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 { chmod, rename, unlink, writeFile } from 'node:fs/promises';\nimport { basename, dirname } from 'node:path';\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\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\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 // 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 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 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, readFile, rename, unlink, writeFile } from 'node:fs/promises';\nimport { dirname } 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 await mkdir(dirname(path), { recursive: true });\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\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","import { defineCommand } from 'citty';\nimport { fetchConsoleMemberUserInfo } from '../api/me.js';\nimport {\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} 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 | --all] [--workspace <id>]:\n// { ok: true, workspaceId, type, terms: WorkspaceTerm[] } exit 0 (single type)\n// { ok: true, workspaceId, byType: { TYPE: WorkspaceTerm[] } } exit 0 (--all)\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// 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 session = await readSession();\n if (!session) {\n emitNotAuthenticated(args.json);\n return exitAfterFlush(ExitCode.NotAuthenticated);\n }\n\n let workspaceId: 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) {\n emitJson({ ok: false, reason: 'invalid-id', message });\n } else {\n process.stderr.write(`${message}\\n`);\n }\n return exitAfterFlush(ExitCode.Usage);\n }\n workspaceId = parsed;\n } else {\n workspaceId = session.currentWorkspaceId;\n }\n\n if (workspaceId === undefined) {\n if (args.json) {\n emitJson({ ok: false, reason: 'no-workspace-selected' });\n } else {\n process.stderr.write(\n 'No workspace selected. Pass `--workspace <id>` or run `aitcc workspace use <id>`.\\n',\n );\n }\n return exitAfterFlush(ExitCode.Usage);\n }\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\n// Shared helper for any workspace subcommand that takes an optional\n// --workspace flag and falls back to the selected workspace. Returns\n// either a numeric workspace id or emits the appropriate JSON/stderr\n// failure and returns null — callers should exit(Usage) on null.\nasync function resolveWorkspaceArg(\n args: { workspace?: unknown; json: boolean },\n selected: number | undefined,\n): Promise<number | null> {\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) {\n emitJson({ ok: false, reason: 'invalid-id', message });\n } else {\n process.stderr.write(`${message}\\n`);\n }\n return null;\n }\n return parsed;\n }\n if (selected === undefined) {\n if (args.json) {\n emitJson({ ok: false, reason: 'no-workspace-selected' });\n } else {\n process.stderr.write(\n 'No workspace selected. Pass `--workspace <id>` or run `aitcc workspace use <id>`.\\n',\n );\n }\n return null;\n }\n return selected;\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 session = await readSession();\n if (!session) {\n emitNotAuthenticated(args.json);\n return exitAfterFlush(ExitCode.NotAuthenticated);\n }\n const workspaceId = await resolveWorkspaceArg(args, session.currentWorkspaceId);\n if (workspaceId === null) return exitAfterFlush(ExitCode.Usage);\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\nconst termsCommand = defineCommand({\n meta: {\n name: 'terms',\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 session = await readSession();\n if (!session) {\n emitNotAuthenticated(args.json);\n return exitAfterFlush(ExitCode.NotAuthenticated);\n }\n const workspaceId = await resolveWorkspaceArg(args, session.currentWorkspaceId);\n if (workspaceId === null) return exitAfterFlush(ExitCode.Usage);\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\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 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 });\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) process.stdout.write(formatTermLines(t));\n }\n return exitAfterFlush(ExitCode.Ok);\n }\n\n // --all path\n const byType: Record<string, readonly WorkspaceTerm[]> = {};\n for (const [t, terms] of results) byType[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) process.stdout.write(formatTermLines(t));\n }\n }\n return exitAfterFlush(ExitCode.Ok);\n } catch (err) {\n return emitFailureFromError(args.json, err);\n }\n },\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 session = await readSession();\n if (!session) {\n emitNotAuthenticated(args.json);\n return exitAfterFlush(ExitCode.NotAuthenticated);\n }\n const workspaceId = await resolveWorkspaceArg(args, session.currentWorkspaceId);\n if (workspaceId === null) return exitAfterFlush(ExitCode.Usage);\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\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 { 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 { 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 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\nrunMain(main);\n"],"mappings":"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"}