@hcmai/sdk 0.3.4 → 0.3.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +276 -2
- package/dist/index.js +549 -28
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/http/trace.ts","../src/http/client.ts","../src/http/interceptors.ts","../src/package-version.ts","../src/auth/token-store.ts","../src/config/paths.ts","../src/auth/principal.ts","../src/auth/password.ts","../src/auth/refresh.ts","../src/config/profile.ts","../src/config/global-config.ts","../src/config/env.ts","../src/errors/codes.ts","../src/errors/cli-error.ts","../src/config/identity.ts","../src/config/migration.ts","../src/config/resolve.ts","../src/models/query.ts","../src/models/action.ts","../src/meta/describe.ts","../src/models/create.ts","../src/models/update.ts","../src/models/remove.ts","../src/import/fields.ts","../src/import/placeholder.ts","../src/import/parse.ts","../src/import/runner.ts","../src/client.ts","../src/documents.ts","../src/output/json.ts","../src/output/yaml.ts","../src/output/table.ts","../src/chat/display-width.ts","../src/output/index.ts","../src/auth/client-credentials.ts","../src/auth/pat.ts","../src/auth/pairing.ts","../src/auth/change-password.ts","../src/auth/admin-reset-password.ts","../src/chat/ws-client.ts","../src/chat/stream-iterator.ts","../src/chat/control-channel.ts","../src/chat/delegation-constants.ts","../src/chat/v4-reducer.ts","../src/chat/one-shot.ts","../src/chat/conversation-state.ts","../src/chat/conversation.ts","../src/chat/resume-summary.ts","../src/chat/conversation-list.ts","../src/mini/types.ts","../src/mini/workspace-files.ts","../src/mini/project.ts","../src/mini/templates.ts","../src/cache.ts","../src/tenant.ts","../src/meta/tenant-meta.ts","../src/skills.ts","../src/settings.ts"],"sourcesContent":["import { randomBytes } from 'node:crypto';\n\nexport interface ParsedTraceparent {\n version: string;\n traceId: string;\n spanId: string;\n flags: string;\n}\n\nexport function newTraceparent(): string {\n const traceId = randomBytes(16).toString('hex');\n const spanId = randomBytes(8).toString('hex');\n return `00-${traceId}-${spanId}-01`;\n}\n\nexport function parseTraceparent(value: string): ParsedTraceparent | null {\n const m = /^([0-9a-f]{2})-([0-9a-f]{32})-([0-9a-f]{16})-([0-9a-f]{2})$/.exec(value);\n if (!m) return null;\n return { version: m[1]!, traceId: m[2]!, spanId: m[3]!, flags: m[4]! };\n}\n","import axios, { type AxiosInstance } from 'axios';\nimport { attachAuth, attachTrace, attachRetry } from './interceptors';\nimport { SDK_VERSION } from '../package-version';\n\nexport interface HttpClientOptions {\n endpoint: string;\n getAccessToken: () => Promise<string | null>;\n onRefresh: () => Promise<void>;\n timeoutMs?: number;\n userAgent?: string;\n}\n\nexport function createHttpClient(opts: HttpClientOptions): AxiosInstance {\n const client = axios.create({\n baseURL: opts.endpoint,\n timeout: opts.timeoutMs ?? 60_000,\n headers: { 'User-Agent': opts.userAgent ?? `hcmai-sdk/${SDK_VERSION}` },\n });\n attachTrace(client);\n attachAuth(client, opts.getAccessToken);\n attachRetry(client, opts.onRefresh);\n return client;\n}\n","import type { AxiosInstance, InternalAxiosRequestConfig } from 'axios';\nimport { newTraceparent } from './trace';\n\ndeclare module 'axios' {\n export interface AxiosRequestConfig {\n /**\n * 该请求**不参与** 401 自动 refresh + retry。\n *\n * 🔴 续期请求自己必须打上它:`attachRetry` 在 401 上调 `onRefresh()`,而\n * onRefresh 最终会发一个续期请求;那个请求的 config 是新的、没有 `__retried`\n * 标记,于是它的 401 又触发一次 onRefresh —— 每层发一个新请求,**无界递归**。\n * 见 `auth/refresh.ts` 的 slidePasswordSession。\n */\n __skipAuthRetry?: boolean;\n }\n}\n\nexport function attachTrace(client: AxiosInstance): void {\n client.interceptors.request.use((cfg: InternalAxiosRequestConfig) => {\n cfg.headers.set('traceparent', newTraceparent());\n return cfg;\n });\n}\n\nexport function attachAuth(client: AxiosInstance, getToken: () => Promise<string | null>): void {\n client.interceptors.request.use(async (cfg) => {\n if (!cfg.headers.has('Authorization')) {\n const tok = await getToken();\n if (tok) cfg.headers.set('Authorization', `Bearer ${tok}`);\n }\n return cfg;\n });\n}\n\nexport function attachRetry(client: AxiosInstance, onRefresh: () => Promise<void>): void {\n client.interceptors.response.use(\n (resp) => resp,\n async (error) => {\n const status = error.response?.status;\n const cfg = error.config as (InternalAxiosRequestConfig & { __retried?: boolean }) | undefined;\n if (!cfg) throw error;\n\n // 401 单次自动 refresh + retry。__skipAuthRetry 的请求(续期请求自己)直接放行,\n // 否则 401 → onRefresh → 续期请求 → 401 会无界递归。\n if (status === 401 && !cfg.__retried && !cfg.__skipAuthRetry) {\n cfg.__retried = true;\n try {\n await onRefresh();\n } catch {\n throw error;\n }\n // 关键:删除过期 Authorization,让 attachAuth 重新取刷新后的 token\n cfg.headers.delete('Authorization');\n return client.request(cfg);\n }\n\n // 5xx 指数退避 retry 2 次\n if (status && status >= 500 && status < 600) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- 在 config 上挂 retry 计数标记\n const attempts = (cfg as any).__attempts ?? 0;\n if (attempts < 2) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- 在 config 上挂 retry 计数标记\n (cfg as any).__attempts = attempts + 1;\n const delay = 500 * Math.pow(3, attempts);\n await new Promise((r) => setTimeout(r, delay));\n // 防御性:若 token 临过期,让 attachAuth 重新取\n cfg.headers.delete('Authorization');\n return client.request(cfg);\n }\n }\n throw error;\n },\n );\n}\n","import { readFileSync } from 'node:fs';\n\ninterface PackageManifest {\n version: string;\n}\n\nconst manifest = JSON.parse(\n readFileSync(new URL('../package.json', import.meta.url), 'utf8'),\n) as PackageManifest;\n\nexport const SDK_VERSION = manifest.version;\n","import * as fs from 'node:fs/promises';\nimport type * as Keytar from 'keytar';\nimport { credentialsFile, profileDir, identityDir, identityMetaFile } from '../config/paths';\n\n/**\n * 凭据的来路。\n *\n * 🔴 **来路 ≠ 语义**:`password` 与 `pairing` 是两条完全不同的取得方式,但换来的是\n * **同一种东西**——`TokenManager.createToken` 铸的服务端会话 token(滑动续期、\n * 可 `/api/auth/refresh`、可 `/api/auth/logout` 单杀)。而 `pat` 与 `client_credentials`\n * 不是。判「能不能续期 / 该不该请服务端吊销」必须走 {@link isServerSlidingSession},\n * 不要拿 `=== 'password'` 去比——见该函数的说明。\n */\nexport type AuthScheme = 'client_credentials' | 'pat' | 'password' | 'pairing';\n\n/**\n * 这条凭据是不是「服务端会话 token」——即后端在每个请求上滑动续期、且\n * `/api/auth/logout` 能单条吊销的那一类。\n *\n * 🔴 **存在的理由是防一类静默退化**:全仓有三处要问这个问题——`refreshToken` 的\n * scheme 分支、`ensureSessionFresh` 的早退、`logout` 的 `revokeOnServer`。散着写成三个\n * 字面量比较,下次加第五轨时必然漏掉其中一处,而漏掉的后果**没有任何报错**:\n * - 漏在 refresh 分支 → 落到 default 抛「unknown auth scheme」,会话再也续不上\n * - 漏在 ensureSessionFresh → 不再主动续期,退回「每天重登」\n * - 漏在 revokeOnServer → **`logout` 静默跳过服务端吊销**,而 ADR-308 把\n * 「用完跑 hcm logout」写成了顾问在客户主机上那一行**唯一可操作的缓解**\n *\n * 所以这里收敛成一个谓词,三处都调它。\n */\nexport function isServerSlidingSession(scheme: AuthScheme): boolean {\n return scheme === 'password' || scheme === 'pairing';\n}\n\nexport interface TokenRecord {\n scheme: AuthScheme;\n accessTokenRef: string; // \"keychain:<svc>/<acct>\" | \"file:<path>\" | \"plain:<token>\"\n refreshTokenRef?: string;\n expiresAt: string; // ISO 8601\n scopes: string[];\n /**\n * 签发时服务端告知的 TTL(秒)。**续期后本地 expiresAt 只能靠它重算**:后端\n * `POST /api/auth/refresh` 的响应体是 `{\"message\":\"token刷新成功\"}` —— 不回新的\n * 过期时间,而它重设的 Redis TTL 用的正是签发时那个全局配置值\n * (`SecurityProperties.token.expiration`,缺省 24h)。不存这个数,续期成功之后就只能\n * 猜一个 TTL 写回本地,猜小了照样早退、猜大了会放过一个已死的 token。\n *\n * 🔴 缺省(旧记录升级上来)时按 DEFAULT_SESSION_TTL_SECONDS 兜底,见 refresh.ts。\n */\n ttlSeconds?: number;\n}\n\nconst SERVICE_PREFIX = 'hcm-cli';\ntype KeytarModule = Pick<typeof Keytar, 'deletePassword' | 'getPassword' | 'setPassword'>;\n\ninterface TokenStoreInternal {\n __identity?: boolean;\n}\n\nexport class TokenStore {\n constructor(private profile: string) {}\n\n /**\n * Factory: build an identity-scoped TokenStore. Stores meta at\n * `identities/<name>/credentials.json` and uses keychain account prefix\n * `identity-<name>` to isolate from legacy profile-scoped stores.\n *\n * Used by T13 login when writing identity meta and by T15 business\n * commands when reading the active identity's token.\n */\n static forIdentity(name: string): TokenStore {\n const ts = new TokenStore(name);\n // Freeze the sentinel so Object.assign / shallow merges can't silently flip\n // an identity store back to profile mode. A subclass `IdentityTokenStore extends\n // TokenStore` would be type-safer, but the sentinel keeps the public API one-class.\n Object.defineProperty(ts, '__identity', { value: true, writable: false, configurable: false });\n return ts;\n }\n\n private get isIdentity(): boolean {\n // Strict === true: defends against accidental non-boolean assignment via cast hole.\n return (this as TokenStoreInternal).__identity === true;\n }\n\n private metaFile(): string {\n return this.isIdentity ? identityMetaFile(this.profile) : credentialsFile(this.profile);\n }\n\n private dataDir(): string {\n return this.isIdentity ? identityDir(this.profile) : profileDir(this.profile);\n }\n\n private keychainAccountPrefix(): string {\n return this.isIdentity ? `identity-${this.profile}` : this.profile;\n }\n\n async writeMeta(record: TokenRecord): Promise<void> {\n await fs.mkdir(this.dataDir(), { recursive: true });\n await fs.writeFile(this.metaFile(), JSON.stringify(record, null, 2), { mode: 0o600 });\n }\n\n async readMeta(): Promise<TokenRecord | null> {\n try {\n const raw = await fs.readFile(this.metaFile(), 'utf-8');\n return JSON.parse(raw) as TokenRecord;\n } catch (e) {\n if ((e as NodeJS.ErrnoException).code === 'ENOENT') return null;\n throw e;\n }\n }\n\n async setAccessToken(token: string): Promise<string> {\n if (await this.canUseKeychain()) {\n const keytar = await loadKeytar();\n if (!keytar) return this.writeTokenFile('access', token);\n const account = `${this.keychainAccountPrefix()}/access`;\n await keytar.setPassword(SERVICE_PREFIX, account, token);\n return `keychain:${SERVICE_PREFIX}/${account}`;\n }\n // fallback: 0600 文件\n return this.writeTokenFile('access', token);\n }\n\n async setRefreshToken(token: string): Promise<string> {\n if (await this.canUseKeychain()) {\n const keytar = await loadKeytar();\n if (!keytar) return this.writeTokenFile('refresh', token);\n const account = `${this.keychainAccountPrefix()}/refresh`;\n await keytar.setPassword(SERVICE_PREFIX, account, token);\n return `keychain:${SERVICE_PREFIX}/${account}`;\n }\n return this.writeTokenFile('refresh', token);\n }\n\n async getAccessToken(): Promise<string | null> {\n // 1. env override\n if (process.env.HCM_PAT) return process.env.HCM_PAT;\n if (process.env.HCM_ACCESS_TOKEN) return process.env.HCM_ACCESS_TOKEN;\n // 2. read meta + resolve ref\n const meta = await this.readMeta();\n if (!meta) return null;\n return this.resolveRef(meta.accessTokenRef);\n }\n\n async getRefreshToken(): Promise<string | null> {\n const meta = await this.readMeta();\n if (!meta?.refreshTokenRef) return null;\n return this.resolveRef(meta.refreshTokenRef);\n }\n\n async clear(): Promise<void> {\n const meta = await this.readMeta();\n if (!meta) return;\n // accessTokenRef may be absent on stub identities (created before first login)\n if (meta.accessTokenRef?.startsWith('keychain:')) {\n const account = meta.accessTokenRef\n .slice('keychain:'.length)\n .split('/')\n .slice(1)\n .join('/');\n const keytar = await loadKeytar();\n if (keytar) await keytar.deletePassword(SERVICE_PREFIX, account);\n } else if (meta.accessTokenRef?.startsWith('file:')) {\n await fs.rm(meta.accessTokenRef.slice('file:'.length), { force: true });\n }\n if (meta.refreshTokenRef?.startsWith('keychain:')) {\n const account = meta.refreshTokenRef\n .slice('keychain:'.length)\n .split('/')\n .slice(1)\n .join('/');\n const keytar = await loadKeytar();\n if (keytar) await keytar.deletePassword(SERVICE_PREFIX, account);\n } else if (meta.refreshTokenRef?.startsWith('file:')) {\n await fs.rm(meta.refreshTokenRef.slice('file:'.length), { force: true });\n }\n // Profile-scoped stores own the meta file (legacy logout) → delete it.\n // Identity-scoped stores share the meta file with deleteIdentity (T11\n // cascade ownership boundary) — leave it for deleteIdentity to clean up.\n if (!this.isIdentity) {\n await fs.rm(this.metaFile(), { force: true });\n }\n }\n\n private async resolveRef(ref: string): Promise<string | null> {\n if (ref.startsWith('keychain:')) {\n const account = ref.slice('keychain:'.length).split('/').slice(1).join('/');\n const keytar = await loadKeytar();\n if (!keytar) return null;\n return keytar.getPassword(SERVICE_PREFIX, account);\n }\n if (ref.startsWith('file:')) {\n try {\n return await fs.readFile(ref.slice('file:'.length), 'utf-8');\n } catch (e) {\n if ((e as NodeJS.ErrnoException).code === 'ENOENT') return null;\n throw e;\n }\n }\n if (ref.startsWith('plain:')) return ref.slice('plain:'.length);\n return null;\n }\n\n private async canUseKeychain(): Promise<boolean> {\n const keytar = await loadKeytar();\n if (!keytar) return false;\n try {\n // sanity probe\n await keytar.getPassword(SERVICE_PREFIX, '__probe__');\n return true;\n } catch {\n return false;\n }\n }\n\n private async writeTokenFile(kind: 'access' | 'refresh', token: string): Promise<string> {\n const file = `${this.metaFile()}.${kind}`;\n await fs.mkdir(this.dataDir(), { recursive: true });\n await fs.writeFile(file, token, { mode: 0o600 });\n if (kind === 'access') {\n // eslint-disable-next-line no-console\n console.warn(`[hcm] WARNING: OS keychain unavailable. Token saved to ${file} (0600).`);\n }\n return `file:${file}`;\n }\n}\n\nasync function loadKeytar(): Promise<KeytarModule | null> {\n try {\n const mod = (await import('keytar')) as unknown as KeytarModule & {\n default?: KeytarModule;\n };\n return mod.default ?? mod;\n } catch {\n return null;\n }\n}\n","import * as path from 'node:path';\nimport * as os from 'node:os';\n\nexport function hcmConfigDir(): string {\n const xdg = process.env.XDG_CONFIG_HOME;\n if (xdg) return path.join(xdg, 'hcm');\n const home = process.env.HOME ?? os.homedir();\n return path.join(home, '.config', 'hcm');\n}\n\nexport function profileDir(profile: string): string {\n return path.join(hcmConfigDir(), profile);\n}\n\nexport function profileFile(profile: string): string {\n return path.join(profileDir(profile), 'profile.yml');\n}\n\nexport function credentialsFile(profile: string): string {\n return path.join(profileDir(profile), 'credentials.json');\n}\n\nexport function globalConfigFile(): string {\n return path.join(hcmConfigDir(), 'config.yml');\n}\n\nexport function conversationStateFile(profile: string): string {\n return path.join(profileDir(profile), 'last-conversation.json');\n}\n\nexport function replHistoryFile(profile: string): string {\n return path.join(profileDir(profile), 'repl-history');\n}\n\nexport function envsDir(): string {\n return path.join(hcmConfigDir(), 'envs');\n}\n\nexport function envDir(name: string): string {\n return path.join(envsDir(), name);\n}\n\nexport function envFile(name: string): string {\n return path.join(envDir(name), 'env.yml');\n}\n\nexport function identitiesDir(): string {\n return path.join(hcmConfigDir(), 'identities');\n}\n\nexport function identityDir(name: string): string {\n return path.join(identitiesDir(), name);\n}\n\nexport function identityMetaFile(name: string): string {\n return path.join(identityDir(name), 'credentials.json');\n}\n\nexport function archiveDir(): string {\n return path.join(hcmConfigDir(), '_archive');\n}\n","import type { TokenRecord } from './token-store';\n\n/**\n * **服务端认定的 token 归属**——与\"调用方以为的归属\"是两回事,两者不一致时\n * 一切看起来都正常,而你在对着另一个租户干活。\n *\n * 这正是 fde-kit 那边 Ruling 30 防的同一件事:`hcm login --tenant 1` 传进去的是\n * 「我要的租户」,服务端回的 `user.tenantId` 才是「token 实际归属的租户」。此前\n * login 成功那行只回显前者,于是登错租户是静默的。\n */\nexport interface Principal {\n /** 服务端认定的租户。ADR-177 之后新租户是 UUID,别按 int 处理。 */\n tenantId?: string;\n username?: string;\n roles?: string[];\n}\n\n/** 登录helper 的统一返回:凭据元数据 + 服务端认定的归属(拿不到时 undefined)。 */\nexport interface LoginOutcome {\n record: TokenRecord;\n /**\n * 🔴 `undefined` = **服务端没在响应里给身份**,与「给了但字段为空」是两个状态。\n * 前者的下一步是问服务端为什么不给,后者的下一步是看那个账号本身。合并之后\n * 回显只能给出一句放之四海而皆准的废话。\n */\n principal?: Principal;\n}\n\n/**\n * 从服务端响应里的 user/userInfo 对象抽出 Principal。\n *\n * 两个来源字段名一致但形状不同,所以这里只认键、不认容器:\n * - `POST /api/auth/login` → `LoginResponse.user`(Map,含 id/username/tenantId/roles)\n * - `GET /api/auth/info` → `UserInfoResponse`(含 userId/username/tenantId/roles)\n *\n * 全部字段都当可选处理:任何一个缺失都不该让登录失败——登录已经成功了,\n * 这里只是回显。\n */\nexport function toPrincipal(raw: unknown): Principal | undefined {\n if (!raw || typeof raw !== 'object') return undefined;\n const o = raw as Record<string, unknown>;\n const principal: Principal = {};\n // tenantId 可能是 number(旧租户 id=1) 或 string(UUID),统一成 string 展示。\n if (o.tenantId !== undefined && o.tenantId !== null) principal.tenantId = String(o.tenantId);\n if (typeof o.username === 'string' && o.username) principal.username = o.username;\n if (Array.isArray(o.roles)) principal.roles = o.roles.map(String);\n return Object.keys(principal).length > 0 ? principal : undefined;\n}\n\n/** 回显用的一行摘要。`principal` 为空时明说「服务端未回身份」,不静默略过。 */\nexport function describePrincipal(principal: Principal | undefined): string {\n if (!principal) return 'server-identity=<未回身份>';\n const parts: string[] = [];\n parts.push(`tenant=${principal.tenantId ?? '<未回>'}`);\n parts.push(`user=${principal.username ?? '<未回>'}`);\n if (principal.roles?.length) parts.push(`roles=${principal.roles.join(',')}`);\n return parts.join(' ');\n}\n","import type { AxiosInstance } from 'axios';\nimport { TokenStore, type TokenRecord } from './token-store';\nimport { toPrincipal, type LoginOutcome } from './principal';\n\nexport interface PasswordLoginInput {\n endpoint: string;\n tenantId: string;\n username: string;\n password: string;\n profile: string;\n httpClient: AxiosInstance;\n}\n\n/** 后端 `SecurityProperties.token.expiration` 的缺省值(24h),仅在服务端没回 expiresIn 时兜底。 */\nexport const DEFAULT_SESSION_TTL_SECONDS = 24 * 60 * 60;\n\nexport async function loginPassword(input: PasswordLoginInput): Promise<LoginOutcome> {\n const resp = await input.httpClient.post(\n `${input.endpoint}/api/auth/login`,\n {\n tenantId: input.tenantId,\n username: input.username,\n password: input.password,\n },\n { headers: { 'Content-Type': 'application/json' } },\n );\n const { token, expiresIn, user } = resp.data;\n if (!token) {\n throw new Error('Login response missing token');\n }\n\n const ttlSeconds = expiresIn ?? DEFAULT_SESSION_TTL_SECONDS;\n const expiresAt = new Date(Date.now() + ttlSeconds * 1000).toISOString();\n\n const store = new TokenStore(input.profile);\n const accessTokenRef = await store.setAccessToken(token);\n\n const record: TokenRecord = {\n scheme: 'password',\n accessTokenRef,\n // 口令会话**没有** refresh token —— 后端 `/api/auth/login` 只回 token。\n // 它的续期语义是「同一个 token 被服务端重设 TTL」,见 refresh.ts。\n refreshTokenRef: undefined,\n expiresAt,\n scopes: ['*'],\n ttlSeconds,\n };\n await store.writeMeta(record);\n return { record, principal: toPrincipal(user) };\n}\n","import type { AxiosInstance } from 'axios';\nimport { TokenStore, isServerSlidingSession, type TokenRecord } from './token-store';\nimport { DEFAULT_SESSION_TTL_SECONDS } from './password';\n\nexport interface RefreshInput {\n endpoint: string;\n /**\n * Identity name (preferred). When set, uses TokenStore.forIdentity(identity).\n * Required for sessions established via HcmClient.fromAuthContext.\n */\n identity?: string;\n /**\n * @deprecated Profile name (legacy). Used when identity is not provided —\n * for backward compat with fromConfig callers and tests pre-T15.\n */\n profile?: string;\n httpClient: AxiosInstance;\n}\n\n/**\n * 本地在「还剩这么多」时就主动去续期。\n *\n * 🔴 这个值**不需要、也不应该**去对齐服务端的 `SecurityProperties.token.refreshThreshold`\n * (缺省 2h):那是服务端**自动**续期的触发线,而我们打的 `/api/auth/refresh` 是\n * **无条件**续期(只要 Redis key 还在就重设 TTL),跟阈值无关。抄一份服务端常量到这里\n * 只会多一个会漂的载体。\n */\nexport const PROACTIVE_REFRESH_MARGIN_SECONDS = 60 * 60;\n\n/**\n * 续期。**三种 scheme 是三套完全不同的服务端语义,不是一个端点能覆盖的。**\n *\n * 🔴 此前这里写成了一条,于是三条全错,而且错得不会被发现——口令会话在\n * 「取 refresh token」那一步就抛了,请求根本没发出去,后面那段对不上的协议\n * 因此永远跑不到,测试也只覆盖了这些提前抛出的错误路径。三条的真实形态:\n *\n * - **password**:`POST /api/auth/refresh`,认 **Authorization 头**,服务端把\n * 同一个 token 的 Redis TTL 重设回 expiration,响应体只有\n * `{\"message\":\"token刷新成功\"}`——**没有 access_token**。旧实现往 body 塞\n * `{refresh_token}` 再读 `resp.data.access_token`,两头都对不上;而口令登录\n * 压根拿不到 refresh token,连这个请求都发不出去。\n * - **client_credentials**:走 `POST /api/oauth/token` + `grant_type=refresh_token`,\n * 且后端在进 switch 之前就强制校验 `client_id` + `client_secret`。本 CLI\n * **不保存 client_secret**(有意为之:那是长期凭据) ⇒ 这条路在当前设计下\n * 不可能执行成功。所以这里**明说要重登**,而不是发一个注定 400 的请求\n * 再把它包装成一句看不懂的错误。\n * - **pat**:本来就不可续期,由后端管失效。\n */\nexport async function refreshToken(input: RefreshInput): Promise<TokenRecord> {\n if (!input.identity && !input.profile) {\n throw new Error('refreshToken: either identity or profile required');\n }\n const store = input.identity\n ? TokenStore.forIdentity(input.identity)\n : new TokenStore(input.profile!);\n const meta = await store.readMeta();\n if (!meta) throw new Error('no credentials to refresh');\n\n switch (meta.scheme) {\n case 'pat':\n throw new Error('PAT cannot be refreshed; re-run `hcm login --pat-stdin`');\n case 'client_credentials':\n throw new Error(\n 'client_credentials 会话无法自动续期:刷新需要 client_secret,而 CLI 不保存它。' +\n '重新执行 `hcm login --client-id <id>`',\n );\n case 'password':\n case 'pairing':\n // 配对码换来的就是 TokenManager 铸的普通会话 token,与口令登录**同一种东西**,\n // 续期语义完全一致(ADR-308)。\n return slidePasswordSession(store, meta, input);\n default: {\n // scheme 是联合类型,走到这里说明磁盘上的记录来自更新版本的 CLI。\n const unknown: never = meta.scheme;\n throw new Error(`unknown auth scheme in stored credentials: ${String(unknown)}`);\n }\n }\n}\n\n/**\n * 口令会话续期:打一次 `/api/auth/refresh`,成功后把本地过期时间往后推 ttlSeconds。\n *\n * 🔴 `__skipAuthRetry` 不是可选的防御性代码:`attachRetry` 在 401 上会调\n * `onRefresh()`,而 onRefresh 最终就走到这里。如果这个请求自己也 401(token 真的\n * 死了),它的 config 是**新的**、没有 `__retried` 标记,于是又触发一次 onRefresh —— \n * 每一层都发一个新请求,**无界递归**。此前这条路被提前抛出的错误挡着从没跑到过,\n * 一旦让它真发请求,递归就成立了。\n */\nasync function slidePasswordSession(\n store: TokenStore,\n meta: TokenRecord,\n input: RefreshInput,\n): Promise<TokenRecord> {\n const accessToken = await store.getAccessToken();\n if (!accessToken) {\n throw new Error('no access token to refresh; re-run `hcm login`');\n }\n\n await input.httpClient.post(`${input.endpoint}/api/auth/refresh`, undefined, {\n headers: { Authorization: `Bearer ${accessToken}` },\n // 见上:阻断 401 → onRefresh → 本函数 → 401 的递归。\n __skipAuthRetry: true,\n });\n\n // 服务端不回新的过期时间,重设的 TTL 就是签发时那个值。\n const ttlSeconds = meta.ttlSeconds ?? DEFAULT_SESSION_TTL_SECONDS;\n const record: TokenRecord = {\n ...meta,\n // token 本身没变,accessTokenRef / scopes 原样保留。\n expiresAt: new Date(Date.now() + ttlSeconds * 1000).toISOString(),\n ttlSeconds,\n };\n await store.writeMeta(record);\n return record;\n}\n\nexport function needsRefresh(meta: TokenRecord, marginSec = 60): boolean {\n const expiresAtMs = new Date(meta.expiresAt).getTime();\n return Date.now() + marginSec * 1000 > expiresAtMs;\n}\n\n/**\n * 命令开跑前把会话续上——**这才是\"登录一次基本不用再输密码\"落地的那一处**。\n *\n * 背景:后端 `TokenAuthenticationFilter` 在每个带 token 的请求上都会滑动续期\n * (剩余 <= refreshThreshold 时把 Redis TTL 重设回 24h),也就是说**只要你在用,\n * 服务端的会话就不会死**。而 CLI 这边的 `expiresAt` 是登录那一刻算死的静态值、\n * 之后再没更新过,`ensureActiveAuth` 拿它一比就抛 TOKEN_EXPIRED —— 结果是\n * **CLI 在拒绝服务端明明还认的 token**。\n *\n * 这里做的事:本地剩余时间进入窗口时主动打一次无条件续期端点,并把服务端那次\n * 续期的结果**写回本地**,让本地的过期判断重新变成真的。\n *\n * 返回是否真的续了。**任何失败都不抛**:续期是尽力而为,失败时后面的请求自己会\n * 401 并给出该给的引导;为了一次续期失败就让整条命令挂掉是本末倒置。\n */\nexport async function ensureSessionFresh(input: RefreshInput): Promise<boolean> {\n try {\n const store = input.identity\n ? TokenStore.forIdentity(input.identity)\n : new TokenStore(input.profile!);\n const meta = await store.readMeta();\n // 只有服务端会话 token 是「服务端滑动同一个 token」这套语义。PAT 由后端管失效,\n // client_credentials 拿不到 secret 续不了——两者在这里都该原样放过。\n // 🔴 走谓词而不是 `=== 'password'`:见 isServerSlidingSession 的说明。\n if (!meta || !isServerSlidingSession(meta.scheme)) return false;\n if (!needsRefresh(meta, PROACTIVE_REFRESH_MARGIN_SECONDS)) return false;\n await refreshToken(input);\n return true;\n } catch {\n return false;\n }\n}\n","import * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport yaml from 'js-yaml';\nimport { hcmConfigDir, profileDir, profileFile } from './paths';\n\nexport interface ProfileConfig {\n endpoint: string;\n tenantId: string;\n defaultAgent?: string;\n displayName?: string;\n}\n\nexport async function loadProfile(profile: string): Promise<ProfileConfig> {\n const file = profileFile(profile);\n let content: string;\n try {\n content = await fs.readFile(file, 'utf-8');\n } catch (e) {\n if ((e as NodeJS.ErrnoException).code === 'ENOENT') {\n throw new Error(`profile '${profile}' not found at ${file}`);\n }\n throw e;\n }\n return yaml.load(content) as ProfileConfig;\n}\n\nexport async function saveProfile(profile: string, cfg: ProfileConfig): Promise<void> {\n await fs.mkdir(profileDir(profile), { recursive: true });\n await fs.writeFile(profileFile(profile), yaml.dump(cfg), 'utf-8');\n}\n\nexport async function listProfiles(): Promise<string[]> {\n const root = hcmConfigDir();\n try {\n const entries = await fs.readdir(root, { withFileTypes: true });\n const result: string[] = [];\n for (const e of entries) {\n if (e.isDirectory()) {\n try {\n await fs.access(path.join(root, e.name, 'profile.yml'));\n result.push(e.name);\n } catch {\n /* skip */\n }\n }\n }\n return result;\n } catch (e) {\n if ((e as NodeJS.ErrnoException).code === 'ENOENT') return [];\n throw e;\n }\n}\n","import * as fs from 'node:fs/promises';\nimport yaml from 'js-yaml';\nimport { globalConfigFile, hcmConfigDir } from './paths';\n\nexport interface GlobalConfig {\n /** Canonical: name of the active environment. */\n activeEnv: string;\n /**\n * @deprecated Use `activeEnv`. Kept in sync with `activeEnv` for backward compat\n * while in-tree callers migrate. Will be removed at end of Phase 2.\n */\n activeProfile?: string;\n defaults?: {\n output?: 'table' | 'json' | 'yaml';\n timeout?: string;\n };\n}\n\ninterface RawShape {\n activeProfile?: string;\n activeEnv?: string;\n defaults?: GlobalConfig['defaults'];\n}\n\nconst DEFAULT_ENV = 'default';\nconst DEFAULT_DEFAULTS = { output: 'table' as const, timeout: '60s' };\n\nexport async function loadGlobalConfig(): Promise<GlobalConfig> {\n let raw: RawShape = {};\n try {\n const content = await fs.readFile(globalConfigFile(), 'utf-8');\n raw = (yaml.load(content) as RawShape) ?? {};\n } catch (e) {\n if ((e as NodeJS.ErrnoException).code !== 'ENOENT') throw e;\n }\n const activeEnv = raw.activeEnv ?? raw.activeProfile ?? DEFAULT_ENV;\n return {\n activeEnv,\n activeProfile: activeEnv, // dual-write alias\n defaults: { ...DEFAULT_DEFAULTS, ...raw.defaults },\n };\n}\n\nexport async function saveGlobalConfig(cfg: GlobalConfig): Promise<void> {\n await fs.mkdir(hcmConfigDir(), { recursive: true });\n // Canonical wins. Legacy callers that still mutate `activeProfile` must be\n // migrated to assign `activeEnv` instead — done in T5 for the 3 mutation\n // sites (config.ts × 2, login.ts × 1). Read-only callers stay safe via the\n // dual-write alias from `loadGlobalConfig`.\n const activeEnv = cfg.activeEnv ?? cfg.activeProfile ?? DEFAULT_ENV;\n const out = {\n activeEnv,\n activeProfile: activeEnv,\n defaults: cfg.defaults,\n };\n await fs.writeFile(globalConfigFile(), yaml.dump(out), 'utf-8');\n}\n\n/**\n * Resolve active env name. Priority: --env flag > HCM_ENV > HCM_PROFILE (legacy) > global.activeEnv\n */\nexport function resolveActiveEnv(\n args: { envFlag?: string },\n env: NodeJS.ProcessEnv,\n global: GlobalConfig,\n): string {\n return args.envFlag ?? env.HCM_ENV ?? env.HCM_PROFILE ?? global.activeEnv;\n}\n\n/**\n * @deprecated Use `resolveActiveEnv`. Keeps old signature `(args: {flag})` for backward compat.\n * Will be removed at end of Phase 2.\n */\nexport function resolveActiveProfile(\n args: { flag?: string },\n env: NodeJS.ProcessEnv,\n global: GlobalConfig,\n): string {\n return resolveActiveEnv({ envFlag: args.flag }, env, global);\n}\n","import * as fs from 'node:fs/promises';\nimport yaml from 'js-yaml';\nimport { envDir, envFile, envsDir } from './paths';\nimport { CliError, CliErrorCode } from '../errors';\n\nexport interface EnvConfig {\n endpoint: string;\n frontend?: string;\n tenantId?: string;\n defaultIdentity?: string;\n defaultAgent?: string;\n}\n\nexport async function saveEnv(name: string, cfg: EnvConfig): Promise<void> {\n if (!cfg.endpoint) {\n throw new CliError({\n code: CliErrorCode.ENV_ENDPOINT_REQUIRED,\n message: `env '${name}' requires endpoint`,\n });\n }\n await fs.mkdir(envDir(name), { recursive: true });\n await fs.writeFile(envFile(name), yaml.dump(cfg), 'utf-8');\n}\n\nexport async function loadEnv(name: string): Promise<EnvConfig> {\n try {\n const content = await fs.readFile(envFile(name), 'utf-8');\n return yaml.load(content) as EnvConfig;\n } catch (e) {\n if ((e as NodeJS.ErrnoException).code === 'ENOENT') {\n throw new CliError({\n code: CliErrorCode.ENV_NOT_FOUND,\n message: `env '${name}' not found. Run: hcm env list`,\n });\n }\n throw e;\n }\n}\n\nexport async function listEnvs(): Promise<string[]> {\n try {\n const entries = await fs.readdir(envsDir(), { withFileTypes: true });\n const result: string[] = [];\n for (const e of entries) {\n if (!e.isDirectory()) continue;\n try {\n await fs.access(envFile(e.name));\n result.push(e.name);\n } catch {\n /* skip */\n }\n }\n return result;\n } catch (e) {\n if ((e as NodeJS.ErrnoException).code === 'ENOENT') return [];\n throw e;\n }\n}\n\nexport async function deleteEnv(name: string): Promise<void> {\n try {\n await fs.access(envFile(name));\n } catch {\n throw new CliError({\n code: CliErrorCode.ENV_NOT_FOUND,\n message: `env '${name}' not found`,\n });\n }\n await fs.rm(envDir(name), { recursive: true, force: true });\n}\n","export enum ExitCode {\n SUCCESS = 0,\n USAGE_ERROR = 1,\n AUTH_ERROR = 2,\n BUSINESS_ERROR = 3,\n NETWORK_ERROR = 4,\n CONFIG_ERROR = 5,\n SIGINT = 130,\n}\n\nexport enum CliErrorCode {\n // usage (exit 1)\n INVALID_ARGUMENT = 'INVALID_ARGUMENT',\n INVALID_JSON = 'INVALID_JSON',\n MISSING_FLAG = 'MISSING_FLAG',\n\n // auth (exit 2)\n AUTH_FAILED = 'AUTH_FAILED',\n AUTH_EXPIRED = 'AUTH_EXPIRED',\n AUTH_REFRESH_FAILED = 'AUTH_REFRESH_FAILED',\n WS_AUTH_ERROR = 'WS_AUTH_ERROR',\n SCOPE_FORBIDDEN = 'SCOPE_FORBIDDEN',\n\n // business (exit 3)\n BUSINESS_RULE_VIOLATION = 'BUSINESS_RULE_VIOLATION',\n MODEL_NOT_FOUND = 'MODEL_NOT_FOUND',\n ACTION_NOT_FOUND = 'ACTION_NOT_FOUND',\n AGENT_NOT_FOUND = 'AGENT_NOT_FOUND',\n TOOL_TIMEOUT = 'TOOL_TIMEOUT',\n\n // network (exit 4)\n NETWORK_ERROR = 'NETWORK_ERROR',\n TIMEOUT = 'TIMEOUT',\n SERVER_ERROR = 'SERVER_ERROR',\n PROTOCOL_UNSUPPORTED = 'PROTOCOL_UNSUPPORTED',\n\n // config (exit 5)\n PROFILE_NOT_FOUND = 'PROFILE_NOT_FOUND',\n CONFIG_PARSE_ERROR = 'CONFIG_PARSE_ERROR',\n KEYCHAIN_UNAVAILABLE = 'KEYCHAIN_UNAVAILABLE',\n\n // env + identity (exit 5/2)\n ENV_NOT_FOUND = 'ENV_NOT_FOUND',\n ENV_ALREADY_EXISTS = 'ENV_ALREADY_EXISTS',\n ENV_ENDPOINT_REQUIRED = 'ENV_ENDPOINT_REQUIRED',\n IDENTITY_NOT_FOUND = 'IDENTITY_NOT_FOUND',\n IDENTITY_ALREADY_EXISTS = 'IDENTITY_ALREADY_EXISTS',\n IDENTITY_NOT_IN_ENV = 'IDENTITY_NOT_IN_ENV',\n TOKEN_EXPIRED = 'TOKEN_EXPIRED',\n NO_ACTIVE_ENV = 'NO_ACTIVE_ENV',\n}\n\nconst CODE_TO_EXIT: Record<CliErrorCode, ExitCode> = {\n [CliErrorCode.INVALID_ARGUMENT]: ExitCode.USAGE_ERROR,\n [CliErrorCode.INVALID_JSON]: ExitCode.USAGE_ERROR,\n [CliErrorCode.MISSING_FLAG]: ExitCode.USAGE_ERROR,\n [CliErrorCode.AUTH_FAILED]: ExitCode.AUTH_ERROR,\n [CliErrorCode.AUTH_EXPIRED]: ExitCode.AUTH_ERROR,\n [CliErrorCode.AUTH_REFRESH_FAILED]: ExitCode.AUTH_ERROR,\n [CliErrorCode.WS_AUTH_ERROR]: ExitCode.AUTH_ERROR,\n [CliErrorCode.SCOPE_FORBIDDEN]: ExitCode.AUTH_ERROR,\n [CliErrorCode.BUSINESS_RULE_VIOLATION]: ExitCode.BUSINESS_ERROR,\n [CliErrorCode.MODEL_NOT_FOUND]: ExitCode.BUSINESS_ERROR,\n [CliErrorCode.ACTION_NOT_FOUND]: ExitCode.BUSINESS_ERROR,\n [CliErrorCode.AGENT_NOT_FOUND]: ExitCode.BUSINESS_ERROR,\n [CliErrorCode.TOOL_TIMEOUT]: ExitCode.BUSINESS_ERROR,\n [CliErrorCode.NETWORK_ERROR]: ExitCode.NETWORK_ERROR,\n [CliErrorCode.TIMEOUT]: ExitCode.NETWORK_ERROR,\n [CliErrorCode.SERVER_ERROR]: ExitCode.NETWORK_ERROR,\n [CliErrorCode.PROTOCOL_UNSUPPORTED]: ExitCode.NETWORK_ERROR,\n [CliErrorCode.PROFILE_NOT_FOUND]: ExitCode.CONFIG_ERROR,\n [CliErrorCode.CONFIG_PARSE_ERROR]: ExitCode.CONFIG_ERROR,\n [CliErrorCode.KEYCHAIN_UNAVAILABLE]: ExitCode.CONFIG_ERROR,\n [CliErrorCode.ENV_NOT_FOUND]: ExitCode.CONFIG_ERROR,\n [CliErrorCode.ENV_ALREADY_EXISTS]: ExitCode.CONFIG_ERROR,\n [CliErrorCode.ENV_ENDPOINT_REQUIRED]: ExitCode.USAGE_ERROR,\n [CliErrorCode.IDENTITY_NOT_FOUND]: ExitCode.CONFIG_ERROR,\n [CliErrorCode.IDENTITY_ALREADY_EXISTS]: ExitCode.CONFIG_ERROR,\n [CliErrorCode.IDENTITY_NOT_IN_ENV]: ExitCode.CONFIG_ERROR,\n [CliErrorCode.TOKEN_EXPIRED]: ExitCode.AUTH_ERROR,\n [CliErrorCode.NO_ACTIVE_ENV]: ExitCode.CONFIG_ERROR,\n};\n\nexport function exitCodeFor(code: CliErrorCode): ExitCode {\n return CODE_TO_EXIT[code];\n}\n","import { CliErrorCode, exitCodeFor } from './codes';\nimport type { ExitCode } from './codes';\n\nexport interface CliErrorInit {\n code: CliErrorCode;\n message: string;\n context?: Record<string, unknown>;\n traceId?: string;\n profile?: string;\n cause?: unknown;\n}\n\nexport class CliError extends Error {\n readonly code: CliErrorCode;\n readonly exitCode: ExitCode;\n readonly context?: Record<string, unknown>;\n readonly traceId?: string;\n readonly profile?: string;\n override readonly cause?: unknown;\n\n constructor(init: CliErrorInit) {\n super(init.message);\n this.name = 'CliError';\n this.code = init.code;\n this.exitCode = exitCodeFor(init.code);\n this.context = init.context;\n this.traceId = init.traceId;\n this.profile = init.profile;\n this.cause = init.cause;\n }\n\n toFormattedString(): string {\n const parts: string[] = [`${this.code}: ${this.message}`];\n // CliErrorCode 是命令行自己的分类(决定 exit code),后端错误码比它精确得多\n // (FILTER_SYNTAX_ERROR vs 一律降级成的 BUSINESS_RULE_VIOLATION)。两者不一致时都要显示,\n // 只显示前者就是在报一个错的错误码。\n const serverCode = this.context?.responseCode;\n if (typeof serverCode === 'string' && serverCode && serverCode !== this.code) {\n parts.push(`[服务端错误码: ${serverCode}]`);\n }\n const field = this.context?.field;\n if (typeof field === 'string' && field) parts.push(`[字段: ${field}]`);\n if (this.traceId) parts.push(`[traceId: ${this.traceId}]`);\n if (this.profile) parts.push(`[profile: ${this.profile}]`);\n let out = parts.join(' ');\n const nextStep = this.context?.nextStep;\n if (typeof nextStep === 'string' && nextStep) out += `\\n 下一步: ${nextStep}`;\n return out;\n }\n\n toJSON() {\n return {\n code: this.code,\n message: this.message,\n context: this.context,\n traceId: this.traceId,\n };\n }\n}\n\n// axios doesn't export a clean error type usable here; intentional any\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function fromAxiosError(err: any, profile?: string): CliError {\n const status = err.response?.status;\n const traceId = err.response?.headers?.['x-trace-id'];\n\n // 🔴 HCM 的错误信封是嵌套的:{ \"error\": { code, message, field, details, nextStep, retryable } }\n // —— 见后端 CommonErrorResponse / ActionError。以前这里读的是顶层 data.code / data.message,\n // 对真实响应**恒取不到**:消息落回 axios 的 \"Request failed with status code 400\",\n // 错误码一律按 HTTP 状态码降级,连带下面 MODEL_NOT_FOUND / ACTION_NOT_FOUND 两条特判成了死代码。\n // 单测当时全绿,因为它们喂的是平铺形状 —— 围着 bug 建的 fixture 证明不了任何事。\n // 顶层留作回退:少数早期端点确实平铺返回。\n const data = err.response?.data;\n const body = (data?.error ?? data) as Record<string, unknown> | undefined;\n const respCode = body?.code as string | undefined;\n const respMsg = body?.message as string | undefined;\n const respField = body?.field as string | undefined;\n const respDetails = body?.details;\n const respNextStep = body?.nextStep as string | undefined;\n const respRetryable = body?.retryable as boolean | undefined;\n\n let code: CliErrorCode;\n if (status === 401) code = CliErrorCode.AUTH_FAILED;\n else if (status === 403) code = CliErrorCode.SCOPE_FORBIDDEN;\n else if (status === 404 && respCode === 'MODEL_NOT_FOUND') code = CliErrorCode.MODEL_NOT_FOUND;\n else if (status === 404 && respCode === 'ACTION_NOT_FOUND') code = CliErrorCode.ACTION_NOT_FOUND;\n else if (status && status >= 400 && status < 500) code = CliErrorCode.BUSINESS_RULE_VIOLATION;\n else if (status && status >= 500) code = CliErrorCode.SERVER_ERROR;\n else if (err.code === 'ECONNABORTED') code = CliErrorCode.TIMEOUT;\n else code = CliErrorCode.NETWORK_ERROR;\n\n return new CliError({\n code,\n message: respMsg ?? err.message ?? 'request failed',\n context: {\n status,\n responseCode: respCode,\n // 结构化错误的这几件必须原样带出:field 指哪个字段错了、details 机器可读、\n // nextStep 是后端给的路标(ADR-166 终局协议)—— 吞掉它等于让命令行用户自己猜下一步。\n ...(respField ? { field: respField } : {}),\n ...(respDetails !== undefined ? { details: respDetails } : {}),\n ...(respNextStep ? { nextStep: respNextStep } : {}),\n ...(respRetryable !== undefined ? { retryable: respRetryable } : {}),\n },\n traceId,\n profile,\n cause: err,\n });\n}\n","import * as fs from 'node:fs/promises';\nimport { identityDir, identityMetaFile, identitiesDir, envFile } from './paths';\nimport { CliError, CliErrorCode } from '../errors';\nimport type { AuthScheme } from '../auth/token-store';\n\nexport interface IdentityMeta {\n env: string;\n scheme: AuthScheme;\n scopes: string[];\n expiresAt?: string;\n username?: string;\n /**\n * keychain ref same as TokenRecord.accessTokenRef. Populated by TokenStore.forIdentity.\n * Optional here because saveIdentity may be called before token is written.\n */\n accessTokenRef?: string;\n refreshTokenRef?: string;\n /**\n * **服务端认定的租户**(登录响应里的 `user.tenantId` / `/api/auth/info` 的 tenantId),\n * 不是命令行上传进去的那个。两者不一致 = 你在对着另一个租户干活,而一切看起来都正常。\n *\n * 命令执行时优先用它,`EnvConfig.tenantId` 只是登录前的入参与兜底 —— 后者缺省是\n * 写死的 `'1'`,而 ADR-177 之后新租户是 UUID,那个缺省几乎必然是错的。\n */\n tenantId?: string;\n /**\n * 见 `TokenRecord.ttlSeconds`。\n *\n * 🔴 必须在这里也留一份:`identities/<name>/credentials.json` 这**一个文件**有两个\n * 写入方——`TokenStore.forIdentity().writeMeta()` 写 TokenRecord 形状,`saveIdentity()`\n * 写 IdentityMeta 形状,后者最后写、且只写自己声明的字段。不在这里声明,\n * 登录流程末尾这一次 saveIdentity 就会把 ttlSeconds 抹掉,续期时只能去猜 TTL。\n */\n ttlSeconds?: number;\n}\n\nasync function assertEnvExists(env: string): Promise<void> {\n try {\n await fs.access(envFile(env));\n } catch {\n throw new CliError({\n code: CliErrorCode.IDENTITY_NOT_IN_ENV,\n message: `env '${env}' does not exist. Run: hcm env add ${env} --endpoint <url>`,\n });\n }\n}\n\nexport async function saveIdentity(name: string, meta: IdentityMeta): Promise<void> {\n await assertEnvExists(meta.env);\n await fs.mkdir(identityDir(name), { recursive: true });\n await fs.writeFile(identityMetaFile(name), JSON.stringify(meta, null, 2), { mode: 0o600 });\n}\n\nexport async function loadIdentity(name: string): Promise<IdentityMeta> {\n try {\n const raw = await fs.readFile(identityMetaFile(name), 'utf-8');\n return JSON.parse(raw) as IdentityMeta;\n } catch (e) {\n if ((e as NodeJS.ErrnoException).code === 'ENOENT') {\n throw new CliError({\n code: CliErrorCode.IDENTITY_NOT_FOUND,\n message: `identity '${name}' not found. Run: hcm identity list`,\n });\n }\n throw e;\n }\n}\n\nexport async function listIdentities(env?: string): Promise<string[]> {\n try {\n const entries = await fs.readdir(identitiesDir(), { withFileTypes: true });\n const result: string[] = [];\n for (const e of entries) {\n if (!e.isDirectory()) continue;\n try {\n const meta = await loadIdentity(e.name);\n if (!env || meta.env === env) result.push(e.name);\n } catch {\n /* skip broken */\n }\n }\n return result;\n } catch (e) {\n if ((e as NodeJS.ErrnoException).code === 'ENOENT') return [];\n throw e;\n }\n}\n\nexport async function deleteIdentity(name: string): Promise<void> {\n try {\n await fs.access(identityMetaFile(name));\n } catch {\n throw new CliError({\n code: CliErrorCode.IDENTITY_NOT_FOUND,\n message: `identity '${name}' not found`,\n });\n }\n await fs.rm(identityDir(name), { recursive: true, force: true });\n}\n","import * as fs from 'node:fs/promises';\nimport type { Dirent } from 'node:fs';\nimport * as path from 'node:path';\nimport yaml from 'js-yaml';\nimport {\n hcmConfigDir,\n profileDir,\n profileFile,\n credentialsFile,\n archiveDir,\n envFile,\n} from './paths';\nimport { saveEnv, deleteEnv } from './env';\nimport { saveIdentity, deleteIdentity } from './identity';\nimport { loadGlobalConfig, saveGlobalConfig } from './global-config';\n\ninterface LegacyProfile {\n endpoint: string;\n tenantId?: string;\n defaultAgent?: string;\n}\n\ninterface LegacyCredentials {\n scheme: 'password' | 'client_credentials' | 'pat';\n accessTokenRef: string;\n refreshTokenRef?: string;\n expiresAt: string;\n scopes: string[];\n username?: string;\n}\n\nexport interface MigrationSummary {\n migrated: string[];\n failed: string[];\n skipped: string[];\n}\n\nconst RESERVED = new Set(['envs', 'identities', '_archive']);\n\n/**\n * One-shot lazy migration from legacy `<hcm>/<profile>/profile.yml + credentials.json`\n * to canonical `<hcm>/envs/<name>/env.yml + identities/<name>/credentials.json`.\n *\n * Semantics:\n * - Idempotent: profiles already migrated (env.yml exists) are skipped.\n * - Per-profile atomicity: any failure inside migrateOne rolls back the partial env.\n * - Legacy files for failed profiles stay in place so the user can inspect / retry.\n * - On any migration success, the global config.yml is rewritten so activeEnv is\n * the canonical key (loadGlobalConfig already provides fallback read of\n * legacy activeProfile).\n * - Structured stderr log: `[hcm] event=migration_done count=N archive=PATH/`\n * and `[hcm] event=migration_failed profile=NAME reason=\"...\"`.\n */\nexport async function migrateLegacyProfiles(): Promise<MigrationSummary> {\n const summary: MigrationSummary = { migrated: [], failed: [], skipped: [] };\n const root = hcmConfigDir();\n\n let entries: Dirent[];\n try {\n entries = await fs.readdir(root, { withFileTypes: true });\n } catch (e) {\n if ((e as NodeJS.ErrnoException).code === 'ENOENT') return summary;\n throw e;\n }\n\n for (const e of entries) {\n if (!e.isDirectory() || RESERVED.has(e.name)) continue;\n const legacyYml = profileFile(e.name);\n try {\n await fs.access(legacyYml);\n } catch {\n continue; // not a legacy profile dir\n }\n\n // already migrated?\n try {\n await fs.access(envFile(e.name));\n summary.skipped.push(e.name);\n continue;\n } catch {\n /* not yet migrated */\n }\n\n let result: { identityName: string } | null = null;\n try {\n result = await migrateOne(e.name);\n summary.migrated.push(e.name);\n } catch (err) {\n summary.failed.push(e.name);\n // Rollback: env AND identity (identity may exist if archive step failed)\n try {\n await deleteEnv(e.name);\n } catch {\n /* env was not yet created — nothing to roll back */\n }\n if (result === null) {\n // migrateOne threw before returning identityName; try to clean up by\n // computed name using the same inference migrateOne would have used.\n try {\n const creds = JSON.parse(\n await fs.readFile(credentialsFile(e.name), 'utf-8'),\n ) as { username?: string };\n const guessedId = creds?.username ? `${creds.username}@${e.name}` : `default@${e.name}`;\n await deleteIdentity(guessedId);\n } catch {\n /* identity not created or creds unreadable — fine */\n }\n } else {\n try {\n await deleteIdentity(result.identityName);\n } catch {\n /* not created */\n }\n }\n process.stderr.write(\n `[hcm] event=migration_failed profile=${e.name} reason=${JSON.stringify(String((err as Error).message))}\\n`,\n );\n }\n }\n\n // upgrade global config.yml: activeProfile → activeEnv (canonical key)\n if (summary.migrated.length > 0) {\n const g = await loadGlobalConfig(); // 自动兼容读 activeProfile fallback\n await saveGlobalConfig({ activeEnv: g.activeEnv, defaults: g.defaults });\n\n const archive = archiveDir();\n process.stderr.write(\n `[hcm] event=migration_done count=${summary.migrated.length} archive=${archive}/\\n`,\n );\n }\n\n return summary;\n}\n\nasync function migrateOne(name: string): Promise<{ identityName: string }> {\n const legacyProfile = yaml.load(\n await fs.readFile(profileFile(name), 'utf-8'),\n ) as LegacyProfile | null;\n if (!legacyProfile?.endpoint) {\n throw new Error('legacy profile.yml missing endpoint');\n }\n\n // Read credentials.json eagerly so a corrupt file fails BEFORE we touch env.yml.\n // (This way the rollback branch is exercised cleanly and the legacy files stay\n // in place for inspection — matches the \"partial fail\" test contract.)\n let creds: LegacyCredentials | null = null;\n try {\n creds = JSON.parse(\n await fs.readFile(credentialsFile(name), 'utf-8'),\n ) as LegacyCredentials;\n } catch (e) {\n if ((e as NodeJS.ErrnoException).code !== 'ENOENT') {\n throw new Error(`credentials.json unreadable: ${(e as Error).message}`);\n }\n // ENOENT → no credentials, that's fine\n }\n\n const identityName = creds?.username ? `${creds.username}@${name}` : `default@${name}`;\n\n // 1. write env.yml (with defaultIdentity pointer)\n await saveEnv(name, {\n endpoint: legacyProfile.endpoint,\n tenantId: legacyProfile.tenantId,\n defaultAgent: legacyProfile.defaultAgent,\n defaultIdentity: identityName,\n });\n\n // 2. write identity meta\n // - if creds existed: full meta carried over\n // - if creds missing: stub identity (scheme=password, empty scopes) — env's\n // defaultIdentity points here, user must `hcm login` to populate the token\n if (creds) {\n await saveIdentity(identityName, {\n env: name,\n scheme: creds.scheme,\n scopes: creds.scopes,\n expiresAt: creds.expiresAt,\n username: creds.username,\n accessTokenRef: creds.accessTokenRef,\n refreshTokenRef: creds.refreshTokenRef,\n });\n } else {\n // Stub: no credentials yet — set expiresAt in the past so auth-guard treats\n // this as \"expired, force re-login\" rather than \"valid password scheme\".\n // T13 login overwrites this with real meta when user runs `hcm login --env <name>`.\n await saveIdentity(identityName, {\n env: name,\n scheme: 'password',\n scopes: [],\n expiresAt: '1970-01-01T00:00:00Z',\n });\n }\n\n // 3. archive legacy files (only after env + identity are persisted)\n const archiveTarget = path.join(archiveDir(), name);\n await fs.mkdir(archiveTarget, { recursive: true });\n await fs.rename(profileFile(name), path.join(archiveTarget, 'profile.yml.bak'));\n try {\n await fs.rename(\n credentialsFile(name),\n path.join(archiveTarget, 'credentials.json.bak'),\n );\n } catch (e) {\n if ((e as NodeJS.ErrnoException).code !== 'ENOENT') throw e;\n }\n\n // try to remove the now-empty profile dir (may still hold non-migrated artifacts\n // like last-conversation.json or repl-history — keep dir in that case)\n try {\n await fs.rmdir(profileDir(name));\n } catch {\n /* not empty — leave dir for non-migrated artifacts */\n }\n\n return { identityName };\n}\n","import { loadEnv } from './env';\nimport { listIdentities } from './identity';\nimport { CliError, CliErrorCode } from '../errors';\n\n/**\n * Resolve active identity name.\n * Priority: --as flag > HCM_IDENTITY env var > env.defaultIdentity > first identity in env > throw.\n */\nexport async function resolveActiveIdentity(\n args: { asFlag?: string },\n env: NodeJS.ProcessEnv,\n activeEnv: string,\n): Promise<string> {\n if (args.asFlag) return args.asFlag;\n if (env.HCM_IDENTITY) return env.HCM_IDENTITY;\n const cfg = await loadEnv(activeEnv);\n if (cfg.defaultIdentity) return cfg.defaultIdentity;\n const [first] = await listIdentities(activeEnv);\n if (!first) {\n throw new CliError({\n code: CliErrorCode.IDENTITY_NOT_FOUND,\n message: `env '${activeEnv}' has no identity. Run: hcm login --env ${activeEnv}`,\n });\n }\n return first;\n}\n\n// Re-export resolveActiveEnv from global-config for symmetry\nexport { resolveActiveEnv } from './global-config';\n","import type { AxiosInstance } from 'axios';\nimport type { ModelQueryDsl, QueryResult } from './types';\n\ninterface HcmPageResponse<T> {\n content: T[];\n totalElements?: number;\n totalPages?: number;\n size?: number;\n page?: number;\n}\n\nexport async function query<T = Record<string, unknown>>(\n http: AxiosInstance,\n model: string,\n dsl: ModelQueryDsl,\n): Promise<QueryResult<T>> {\n const body: Record<string, unknown> = {};\n const filter = normalizeFilter(dsl.filter);\n if (filter !== undefined) body.filter = filter;\n const orders = normalizeOrders(dsl.sort);\n if (orders !== undefined) body.orders = orders;\n if (dsl.fields !== undefined) body.fields = dsl.fields;\n if (dsl.keyword !== undefined) body.keyword = dsl.keyword;\n\n const size = dsl.limit ?? 20;\n const page = dsl.offset ? Math.floor(dsl.offset / size) : 0;\n body.page = page;\n body.size = size;\n\n const resp = await http.post<HcmPageResponse<T>>(`/api/models/${model}/list/dsl`, body);\n return {\n rows: resp.data.content ?? [],\n total: resp.data.totalElements,\n };\n}\n\nconst OPERATOR_ALIASES: Record<string, string> = {\n $eq: 'eq',\n eq: 'eq',\n '=': 'eq',\n '==': 'eq',\n $ne: 'ne',\n ne: 'ne',\n '!=': 'ne',\n '<>': 'ne',\n $gt: 'gt',\n gt: 'gt',\n '>': 'gt',\n $gte: 'gte',\n gte: 'gte',\n '>=': 'gte',\n $lt: 'lt',\n lt: 'lt',\n '<': 'lt',\n $lte: 'lte',\n lte: 'lte',\n '<=': 'lte',\n $in: 'in',\n in: 'in',\n $nin: 'not_in',\n nin: 'not_in',\n $not_in: 'not_in',\n not_in: 'not_in',\n $like: 'like',\n like: 'like',\n $not_like: 'not_like',\n not_like: 'not_like',\n $contains: 'contains',\n contains: 'contains',\n $startswith: 'startswith',\n startswith: 'startswith',\n $endswith: 'endswith',\n endswith: 'endswith',\n $between: 'between',\n between: 'between',\n $is_null: 'is_null',\n is_null: 'is_null',\n $is_not_null: 'is_not_null',\n is_not_null: 'is_not_null',\n};\n\nfunction normalizeFilter(filter: Record<string, unknown> | undefined): unknown {\n if (filter === undefined) return undefined;\n if (!isPlainObject(filter)) return filter;\n if (isAstFilter(filter)) return filter;\n\n const leaves = Object.entries(filter).flatMap(([field, value]) => filterLeaves(field, value));\n if (leaves.length === 0) return undefined;\n return leaves.length === 1 ? leaves[0] : { and: leaves };\n}\n\nfunction isAstFilter(filter: Record<string, unknown>): boolean {\n return (\n 'and' in filter ||\n 'or' in filter ||\n 'not' in filter ||\n (typeof filter.field === 'string' && typeof filter.op === 'string')\n );\n}\n\nfunction filterLeaves(field: string, value: unknown): Array<Record<string, unknown>> {\n if (isPlainObject(value)) {\n const entries = Object.entries(value);\n if (entries.length > 0 && entries.every(([key]) => isFilterOperator(key))) {\n return entries.map(([key, operatorValue]) => {\n const op = OPERATOR_ALIASES[key];\n if (!op) {\n throw new Error(`Unsupported filter operator \"${key}\" for field \"${field}\"`);\n }\n return isUnaryOperator(op)\n ? { field, op }\n : { field, op, value: operatorValue };\n });\n }\n if (entries.some(([key]) => isFilterOperator(key))) {\n throw new Error(`Unsupported mixed filter value for field \"${field}\"`);\n }\n }\n return [{ field, op: Array.isArray(value) ? 'in' : 'eq', value }];\n}\n\nfunction isFilterOperator(key: string): boolean {\n return Object.prototype.hasOwnProperty.call(OPERATOR_ALIASES, key);\n}\n\nfunction isUnaryOperator(op: string): boolean {\n return op === 'is_null' || op === 'is_not_null';\n}\n\nfunction normalizeOrders(sort: Array<Record<string, unknown>> | undefined): unknown {\n if (sort === undefined) return undefined;\n return sort.map((item) => {\n const field = item.field;\n if (typeof field !== 'string' || field.trim() === '') {\n throw new Error('sort item requires non-empty field');\n }\n const rawDirection =\n typeof item.direction === 'string'\n ? item.direction\n : typeof item.dir === 'string'\n ? item.dir\n : typeof item.order === 'string'\n ? item.order\n : item.desc === true\n ? 'desc'\n : 'asc';\n const direction = rawDirection.toLowerCase();\n if (direction !== 'asc' && direction !== 'desc') {\n throw new Error(`sort direction must be asc or desc; got \"${rawDirection}\"`);\n }\n return { field, direction };\n });\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n","import type { AxiosInstance } from 'axios';\nimport type { ActionHttpMethod, ActionInput, ActionResult, ActionScope } from './types';\n\nfunction normalizeMethod(method?: ActionHttpMethod): ActionHttpMethod {\n return method ?? 'POST';\n}\n\nfunction normalizeScope(input: ActionInput): ActionScope {\n return input.scope ?? (input.id ? 'object' : 'class');\n}\n\nfunction queryParams(input: ActionInput): Record<string, unknown> {\n return {\n ...(input.params ?? {}),\n ...(input.scope === 'class' && input.id ? { id: input.id } : {}),\n ...(input.ids?.length ? { ids: input.ids.join(',') } : {}),\n };\n}\n\nfunction classBody(input: ActionInput): Record<string, unknown> {\n return {\n ...(input.params ?? {}),\n ...(input.id ? { id: input.id } : {}),\n ...(input.ids?.length ? { ids: input.ids } : {}),\n };\n}\n\nfunction objectBody(input: ActionInput): Record<string, unknown> {\n return input.params ?? {};\n}\n\nexport async function action<R = unknown>(\n http: AxiosInstance,\n model: string,\n actionName: string,\n input: ActionInput,\n): Promise<ActionResult<R>> {\n const method = normalizeMethod(input.method);\n const scope = normalizeScope(input);\n if (scope === 'object' && !input.id) {\n throw new Error(`object action ${model}.${actionName} requires id`);\n }\n\n const path = scope === 'object'\n ? `/api/models/${model}/${input.id}/action/${actionName}`\n : `/api/models/${model}/action/${actionName}`;\n\n const resp = await (async () => {\n if (method === 'GET') {\n return http.get(path, { params: queryParams(input) });\n }\n if (method === 'DELETE') {\n return http.delete(path, { params: queryParams(input) });\n }\n if (method === 'PUT') {\n return http.put(path, scope === 'object' ? objectBody(input) : classBody(input));\n }\n return http.post(path, scope === 'object' ? objectBody(input) : classBody(input));\n })();\n return resp.data as ActionResult<R>;\n}\n","import type { AxiosInstance } from 'axios';\n\nexport interface FieldMeta {\n name: string;\n type: string;\n required?: boolean;\n label?: string;\n description?: string;\n enum?: string[];\n relation?: { model: string; field?: string };\n}\n\nexport interface ActionMeta {\n name: string;\n label?: string;\n description?: string;\n params?: Array<{ name: string; type: string; required?: boolean; description?: string }>;\n scope?: string;\n httpMethod?: string;\n readOnly?: boolean;\n paramTypes?: string[];\n paramNames?: string[];\n returnType?: string;\n source?: string;\n}\n\nexport interface RelationMeta {\n key: string;\n via: string;\n label?: string;\n}\n\nexport interface ModelDescription {\n model: string;\n labels: Record<string, string>;\n fields: FieldMeta[];\n actions: ActionMeta[];\n relations: RelationMeta[];\n}\n\nexport interface DescribeOpts {\n verbose?: boolean;\n}\n\ninterface HcmFieldRaw {\n key?: string;\n name?: string;\n type?: string;\n isRequired?: boolean;\n description?: string;\n enum?: string[];\n relation?: { model: string; field?: string };\n}\n\ninterface HcmActionRaw {\n key?: string;\n name?: string;\n label?: string;\n description?: string;\n params?: Array<{ name: string; type: string; required?: boolean; description?: string }>;\n parameters?: Array<{ name: string; type: string; required?: boolean; description?: string }>;\n scope?: string;\n httpMethod?: string;\n readOnly?: boolean;\n paramTypes?: string[];\n paramNames?: string[];\n returnType?: string;\n source?: string;\n}\n\nfunction normalizeActionParams(\n action: HcmActionRaw,\n): Array<{ name: string; type: string; required?: boolean; description?: string }> | undefined {\n if (action.parameters) return action.parameters;\n if (action.params) return action.params;\n if (!action.paramNames?.length && !action.paramTypes?.length) return undefined;\n const size = Math.max(action.paramNames?.length ?? 0, action.paramTypes?.length ?? 0);\n return Array.from({ length: size }, (_, index) => ({\n name: action.paramNames?.[index] ?? `arg${index}`,\n type: action.paramTypes?.[index] ?? 'unknown',\n }));\n}\n\nexport async function describe(\n http: AxiosInstance,\n model: string,\n _opts: DescribeOpts = {},\n): Promise<ModelDescription> {\n const metaResp = await http.get(`/api/models/${model}/meta`);\n const meta = metaResp.data;\n const fields: FieldMeta[] = (meta.fields ?? []).map((f: HcmFieldRaw) => ({\n name: f.key ?? f.name ?? '',\n label: f.name,\n type: f.type ?? 'unknown',\n required: f.isRequired,\n description: f.description,\n enum: f.enum,\n relation: f.relation,\n }));\n const actions: ActionMeta[] = (meta.actions ?? []).map((a: HcmActionRaw) => ({\n name: a.key ?? a.name ?? '',\n label: a.label,\n description: a.description,\n params: normalizeActionParams(a),\n scope: a.scope,\n httpMethod: a.httpMethod,\n readOnly: a.readOnly,\n paramTypes: a.paramTypes,\n paramNames: a.paramNames,\n returnType: a.returnType,\n source: a.source,\n }));\n return {\n model: meta.name ?? model,\n labels: meta.labels ?? {},\n fields,\n actions,\n relations: meta.relations ?? [],\n };\n}\n","import type { AxiosInstance } from 'axios';\n\nexport interface CreateResult {\n id: string;\n [key: string]: unknown;\n}\n\n/** 创建一条 Model 记录,走后端 Model 层完整校验。\n * 注:返回体形态(尤其 id 字段名)见 spec §12.3,实现期对真后端核实。 */\nexport async function create(\n http: AxiosInstance,\n model: string,\n data: Record<string, unknown>,\n): Promise<CreateResult> {\n const resp = await http.post(`/api/models/${model}`, data);\n return resp.data as CreateResult;\n}\n","import type { AxiosInstance } from 'axios';\nimport type { CreateResult } from './create';\n\nexport async function update(\n http: AxiosInstance,\n model: string,\n id: string,\n data: Record<string, unknown>,\n): Promise<CreateResult> {\n const resp = await http.put(`/api/models/${model}/${id}`, data);\n return resp.data as CreateResult;\n}\n","import type { AxiosInstance } from 'axios';\n\nexport interface RemoveResult {\n id: string;\n status: number;\n data?: unknown;\n}\n\nexport async function remove(http: AxiosInstance, model: string, id: string): Promise<RemoveResult> {\n const resp = await http.delete(`/api/models/${model}/${encodeURIComponent(id)}`);\n if (resp.data && typeof resp.data === 'object') {\n return resp.data as RemoveResult;\n }\n return { id, status: resp.status };\n}\n","\n/** snake_case → camelCase(单个 key)。 */\nexport function snakeToCamel(key: string): string {\n return key.replace(/_([a-z0-9])/g, (_m, c: string) => c.toUpperCase());\n}\n\n/** 转换 record 的**顶层** key:snake_case → camelCase。\n * 嵌套对象/数组的值(jsonb / i18n)原样透传,不转内部键名(对齐 schema §4)。 */\nexport function camelizeKeys(obj: Record<string, unknown>): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(obj)) {\n out[snakeToCamel(k)] = v;\n }\n return out;\n}\n","// 整字段值必须是 ${Model:key}(不嵌套、不拼接,对齐 schema §3)\nconst PLACEHOLDER_RE = /^\\$\\{([A-Za-z0-9_]+):([^}]+)\\}$/;\n\nexport class RefStore {\n private map = new Map<string, string>();\n\n private k(model: string, key: string): string {\n return `${model}:${key}`;\n }\n register(model: string, key: string, id: string): void {\n this.map.set(this.k(model, key), id);\n }\n get(model: string, key: string): string | undefined {\n return this.map.get(this.k(model, key));\n }\n snapshot(): Record<string, string> {\n return Object.fromEntries(this.map);\n }\n}\n\nexport interface ParsedPlaceholder {\n model: string;\n key: string;\n}\n\n/** 若 value 恰好是一个 ${Model:key} 占位符,返回其 model/key;否则 null。 */\nexport function parsePlaceholder(value: unknown): ParsedPlaceholder | null {\n if (typeof value !== 'string') return null;\n const m = PLACEHOLDER_RE.exec(value);\n if (!m) return null;\n const [, model, key] = m;\n if (model === undefined || key === undefined) return null;\n return { model, key };\n}\n\n/** 用 store 替换 record 顶层的 ${Model:key} 占位符。\n * 返回 { resolved, unresolved };unresolved 是 \"字段名=${Model:key}\" 列表,原值保留便于报错。 */\nexport function resolveRefs(\n record: Record<string, unknown>,\n store: RefStore,\n): { resolved: Record<string, unknown>; unresolved: string[] } {\n const resolved: Record<string, unknown> = {};\n const unresolved: string[] = [];\n for (const [field, value] of Object.entries(record)) {\n const ph = parsePlaceholder(value);\n if (!ph) {\n resolved[field] = value;\n continue;\n }\n const id = store.get(ph.model, ph.key);\n if (id === undefined) {\n unresolved.push(`${field}=\\${${ph.model}:${ph.key}}`);\n resolved[field] = value;\n } else {\n resolved[field] = id;\n }\n }\n return { resolved, unresolved };\n}\n","import yaml, { JSON_SCHEMA } from 'js-yaml';\nimport { CliError, CliErrorCode } from '../errors';\nimport type { Fixture } from './types';\n\nconst REQUIRED = ['model', 'key_field', 'count', 'items'] as const;\n\n/** 解析 + 校验单个 data fixture(对齐 spec §10 校验项 1–4)。 */\nexport function parseFixture(yamlText: string, sourcePath?: string): Fixture {\n const where = sourcePath ? ` (${sourcePath})` : '';\n const fail = (message: string): never => {\n throw new CliError({ code: CliErrorCode.INVALID_ARGUMENT, message: `import: ${message}${where}` });\n };\n\n // 注入数据视为不可信:用 JSON_SCHEMA(只认 JSON 可表达的类型,拒绝 !!js/!!python 等自定义 tag),\n // 比 js-yaml v4 默认 DEFAULT_SCHEMA 更严;也避免无引号日期被解析成 Date 对象。\n let parsed: unknown;\n try {\n parsed = yaml.load(yamlText, { schema: JSON_SCHEMA });\n } catch (e) {\n return fail(`YAML 解析失败: ${(e as Error).message}`);\n }\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n return fail('fixture 为空或不是 mapping');\n }\n const doc = parsed as Record<string, unknown>;\n for (const f of REQUIRED) {\n if (doc[f] === undefined) return fail(`fixture 缺必需字段 \"${f}\"`);\n }\n if (!Array.isArray(doc.items)) return fail('\"items\" 必须是 list');\n if (!Number.isInteger(doc.count)) return fail('\"count\" 必须是整数');\n if (doc.items.length !== doc.count) {\n return fail(`count mismatch: count=${doc.count} 但 items 有 ${doc.items.length}`);\n }\n\n const keyField = doc.key_field as string;\n const seen = new Set<string>();\n for (const item of doc.items as Array<Record<string, unknown>>) {\n const k = item[keyField];\n if (k === undefined || k === null || k === '') return fail(`item 缺 key_field \"${keyField}\"`);\n if (seen.has(String(k))) return fail(`duplicate key \"${k}\" in ${String(doc.model)}`);\n seen.add(String(k));\n }\n return { ...(doc as unknown as Fixture), sourcePath };\n}\n","import { camelizeKeys } from './fields';\nimport { RefStore, resolveRefs } from './placeholder';\nimport type { Fixture, ImportOptions, ImportResult, RowResult } from './types';\n\n/** runner 只依赖 client 的这三个方法,便于单测 stub。 */\nexport interface ImportClient {\n findIdByKey(model: string, keyField: string, keyValue: string): Promise<string | null>;\n create(model: string, data: Record<string, unknown>): Promise<{ id: string }>;\n update(model: string, id: string, data: Record<string, unknown>): Promise<unknown>;\n}\n\nexport async function runImport(\n client: ImportClient,\n fixtures: Fixture[],\n opts: ImportOptions = {},\n): Promise<ImportResult> {\n const store = new RefStore();\n const rows: RowResult[] = [];\n const tally = { created: 0, updated: 0, skipped: 0, failed: 0 };\n const result = (): ImportResult => ({ ...tally, rows, refs: store.snapshot() });\n\n for (const fx of fixtures) {\n for (const item of fx.items) {\n const key = String(item[fx.key_field]);\n const { resolved, unresolved } = resolveRefs(item, store);\n\n if (unresolved.length > 0) {\n rows.push({ model: fx.model, key, status: 'failed', error: `unresolved placeholder(s): ${unresolved.join(', ')}` });\n tally.failed++;\n if (opts.onError === 'stop') return result();\n continue;\n }\n\n if (opts.dryRun) {\n // 纯静态:不写后端;注册假 id 让后续跨文件占位符仍能解析\n store.register(fx.model, key, `dryrun-${fx.model}-${key}`);\n rows.push({ model: fx.model, key, status: 'skipped' });\n tally.skipped++;\n continue;\n }\n\n const payload = camelizeKeys(resolved);\n try {\n const existingId = await client.findIdByKey(fx.model, fx.key_field, key);\n if (existingId && !opts.updateOnly) {\n store.register(fx.model, key, existingId);\n rows.push({ model: fx.model, key, status: 'skipped', id: existingId });\n tally.skipped++;\n } else if (existingId && opts.updateOnly) {\n await client.update(fx.model, existingId, payload);\n store.register(fx.model, key, existingId);\n rows.push({ model: fx.model, key, status: 'updated', id: existingId });\n tally.updated++;\n } else {\n const created = await client.create(fx.model, payload);\n store.register(fx.model, key, created.id);\n rows.push({ model: fx.model, key, status: 'created', id: created.id });\n tally.created++;\n }\n } catch (e: unknown) {\n const err = e as { response?: { headers?: Record<string, string>; data?: { message?: string } }; message?: string };\n rows.push({\n model: fx.model, key, status: 'failed',\n error: err.response?.data?.message ?? err.message ?? 'write failed',\n traceId: err.response?.headers?.['x-trace-id'],\n });\n tally.failed++;\n if (opts.onError === 'stop') return result();\n }\n }\n }\n return result();\n}\n","import type { AxiosInstance } from 'axios';\nimport { createHttpClient } from './http';\nimport { TokenStore } from './auth/token-store';\nimport { refreshToken, needsRefresh } from './auth/refresh';\nimport { loadProfile, loadGlobalConfig, type ProfileConfig } from './config';\nimport { query as modelQuery } from './models/query';\nimport { action as modelAction } from './models/action';\nimport { describe as describeModel } from './meta/describe';\nimport type { ModelQueryDsl, QueryResult, ActionInput, ActionResult } from './models/types';\nimport type { ModelDescription, DescribeOpts } from './meta/describe';\nimport {\n create as modelCreate,\n update as modelUpdate,\n remove as modelRemove,\n type CreateResult,\n type RemoveResult,\n} from './models';\nimport { runImport, type ImportClient } from './import';\nimport type { Fixture, ImportOptions, ImportResult } from './import';\n\nexport interface HcmClientOpts {\n endpoint: string;\n tenantId: string;\n accessToken?: string; // 显式构造时传\n scheme?: 'client_credentials' | 'pat';\n profile?: string;\n timeoutMs?: number;\n}\n\n/**\n * Structural type representing the resolved auth context that\n * `HcmClient.fromAuthContext` consumes. Kept as a structural interface\n * (rather than importing `AuthContext` from `@hcmai/cli`) to avoid a circular\n * package dependency — the CLI's `AuthContext` is assignable to this shape.\n */\nexport interface ClientAuthContext {\n env: string;\n endpoint: string;\n tenantId: string;\n identity: string;\n}\n\nexport class HcmClient {\n readonly profile: string;\n readonly endpoint: string;\n readonly tenantId: string;\n private http: AxiosInstance;\n private tokenStore: TokenStore;\n private explicitToken?: string;\n\n constructor(opts: HcmClientOpts) {\n this.profile = opts.profile ?? 'default';\n this.endpoint = opts.endpoint;\n this.tenantId = opts.tenantId;\n this.tokenStore = new TokenStore(this.profile);\n this.explicitToken = opts.accessToken;\n this.http = createHttpClient({\n endpoint: opts.endpoint,\n timeoutMs: opts.timeoutMs,\n getAccessToken: () => this.getAccessToken(),\n onRefresh: () => this.handleRefresh(),\n });\n }\n\n /**\n * Build a client from a resolved env + identity context (T15).\n *\n * Uses `TokenStore.forIdentity(ctx.identity)` so the token is read from\n * the identity-scoped store (written by `hcm login --env <env> --name <id>`),\n * not the legacy profile-scoped credentials.json.\n *\n * The `profile` field is kept = `ctx.env` for downstream callers that still\n * read `client.profile` (e.g. workspace context dump). Phase 2 will rename\n * the field to `env`.\n *\n * Refresh path: fromAuthContext binds `this.identity` so handleRefresh\n * routes refresh through TokenStore.forIdentity — works for\n * client_credentials sessions (1h TTL) post-T8 migration.\n */\n /**\n * Identity name for refresh path (set by fromAuthContext). When set,\n * handleRefresh routes through TokenStore.forIdentity rather than the\n * profile-scoped legacy store — critical for client_credentials sessions\n * (~1h token TTL) on post-T8-migration installs.\n */\n private identity?: string;\n\n static fromAuthContext(ctx: ClientAuthContext): HcmClient {\n const c = new HcmClient({\n endpoint: ctx.endpoint,\n tenantId: ctx.tenantId,\n profile: ctx.env, // alias env→profile for downstream readers\n });\n (c as unknown as { tokenStore: TokenStore }).tokenStore = TokenStore.forIdentity(ctx.identity);\n (c as unknown as { identity: string }).identity = ctx.identity;\n return c;\n }\n\n /**\n * @deprecated Use `HcmClient.fromAuthContext(ctx)` together with the CLI\n * helper `getActiveAuthContextFromCli(args)`. WILL THROW on post-T8\n * installs where `~/.config/hcm/<profile>/profile.yml` was migrated to\n * `envs/<name>/env.yml` (loadProfile reads the legacy path only).\n * Kept only for dead-code paths (mini.ts) and pre-migration users.\n */\n static async fromConfig(args: { profile?: string }): Promise<HcmClient> {\n const global = await loadGlobalConfig();\n const profile = args.profile ?? process.env.HCM_PROFILE ?? global.activeEnv;\n const cfg: ProfileConfig = await loadProfile(profile);\n return new HcmClient({\n endpoint: cfg.endpoint,\n tenantId: cfg.tenantId,\n profile,\n });\n }\n\n query<T = Record<string, unknown>>(model: string, dsl: ModelQueryDsl): Promise<QueryResult<T>> {\n return modelQuery<T>(this.http, model, dsl);\n }\n\n action<R = unknown>(model: string, name: string, input: ActionInput): Promise<ActionResult<R>> {\n return modelAction<R>(this.http, model, name, input);\n }\n\n describe(model: string, opts?: DescribeOpts): Promise<ModelDescription> {\n return describeModel(this.http, model, opts);\n }\n\n create(model: string, data: Record<string, unknown>): Promise<CreateResult> {\n return modelCreate(this.http, model, data);\n }\n\n update(model: string, id: string, data: Record<string, unknown>): Promise<CreateResult> {\n return modelUpdate(this.http, model, id, data);\n }\n\n remove(model: string, id: string): Promise<RemoveResult> {\n return modelRemove(this.http, model, id);\n }\n\n /** 按业务键查已存在记录的 id(幂等用);命中返回 id,否则 null。 */\n async findIdByKey(model: string, keyField: string, keyValue: string): Promise<string | null> {\n const res = await this.query(model, { filter: { [keyField]: keyValue }, limit: 1 });\n const row = res.rows[0] as { id?: string } | undefined;\n return row?.id ?? null;\n }\n\n importPackage(fixtures: Fixture[], opts?: ImportOptions): Promise<ImportResult> {\n return runImport(this as ImportClient, fixtures, opts);\n }\n\n raw(): AxiosInstance {\n return this.http;\n }\n\n async close(): Promise<void> {\n /* nothing for REST; chat module will override */\n }\n\n async getAccessToken(): Promise<string | null> {\n if (this.explicitToken) return this.explicitToken;\n return this.tokenStore.getAccessToken();\n }\n\n async handleRefresh(): Promise<void> {\n if (this.explicitToken) {\n // 显式 token 模式不能 refresh\n throw new Error('explicit token expired; pass a new accessToken');\n }\n const meta = await this.tokenStore.readMeta();\n if (!meta) throw new Error('no credentials');\n if (meta.scheme === 'pat') throw new Error('PAT cannot refresh; re-run login');\n if (needsRefresh(meta, 5)) {\n // Route refresh through identity-scoped store when bound via\n // fromAuthContext (T15 follow-up #3 — fixes client_credentials sessions\n // breaking ~1h in post-T8 installs).\n const refreshArgs = this.identity\n ? { endpoint: this.endpoint, identity: this.identity, httpClient: this.http }\n : { endpoint: this.endpoint, profile: this.profile, httpClient: this.http };\n await refreshToken(refreshArgs);\n }\n }\n}\n","import * as fs from 'node:fs';\nimport * as fsp from 'node:fs/promises';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\nimport type { AxiosInstance } from 'axios';\n\n/**\n * 会话附件元数据——与后端 `ConversationWebSocketHandler.parseAttachments` 形态一致\n * (message.create payload.attachments 元素;#1849 后随 USER_MESSAGE 入 journal 单源)。\n */\nexport interface AttachmentMeta {\n documentId: string;\n fileName: string;\n mimeType: string;\n fileSize: number;\n}\n\nconst MIME_BY_EXT: Record<string, string> = {\n '.pdf': 'application/pdf',\n '.docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',\n '.doc': 'application/msword',\n '.xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',\n '.csv': 'text/csv',\n '.txt': 'text/plain',\n '.md': 'text/markdown',\n '.png': 'image/png',\n '.jpg': 'image/jpeg',\n '.jpeg': 'image/jpeg',\n '.gif': 'image/gif',\n '.webp': 'image/webp',\n};\n\nexport function guessMimeType(filePath: string): string {\n return MIME_BY_EXT[path.extname(filePath).toLowerCase()] ?? 'application/octet-stream';\n}\n\n/**\n * 聊天附件的存储分类 —— `hcm chat` 走这一类,与 web ChatInput 同端点同分类。\n *\n * <p>⚠️ 它**不是**通用默认值:`storageCategory` 是服务端的一份**数据**(可配置的\n * 分类表),不是枚举常量。分类不存在时服务端返回 404 `category not found`。\n */\nexport const CHAT_ATTACHMENT_CATEGORY = 'ai-conversation';\n\n/**\n * 上传本地文件到 HCM 文档存储,返回可直接携带的附件元数据。\n * 类型限制跟随服务端校验。\n *\n * @param storageCategory 存储分类。**必须是目标环境里真实存在的分类**——\n * 它是服务端数据而非枚举,不同环境的分类表可以不同。\n * 聊天附件传 {@link CHAT_ATTACHMENT_CATEGORY};报表模板一类传 `flex-report`。\n *\n * <p>🔴 此前这里硬编码 `'ai-conversation'`,于是 `hcm upload <任意文件>` 在\n * 没有该分类的环境上**一律失败**,而错误被包成\n * `BUSINESS_RULE_VIOLATION: Request failed with status code 404`——看不出真因是\n * 「分类不存在」。2026-08-26 按交付技能跑弹性报表端到端时,Step 4「上传模板」\n * 卡在这里;模板是报表的必需件,这条不通整条链就断了。\n */\nexport async function uploadDocument(\n http: AxiosInstance,\n filePath: string,\n storageCategory: string = CHAT_ATTACHMENT_CATEGORY,\n): Promise<AttachmentMeta> {\n const abs = expandHome(filePath);\n const buf = await fsp.readFile(abs);\n const fileName = path.basename(abs);\n const form = new FormData();\n form.append('file', new Blob([buf], { type: guessMimeType(abs) }), fileName);\n form.append('storageCategory', storageCategory);\n form.append('allowEmptyFormId', 'true');\n const resp = await http.post('/api/documents/upload', form);\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const meta: any = resp.data?.data ?? resp.data;\n return {\n documentId: String(meta.documentId ?? meta.id),\n fileName: String(meta.fileName ?? fileName),\n mimeType: String(meta.contentType ?? meta.mimeType ?? guessMimeType(abs)),\n fileSize: Number(meta.fileSize ?? meta.size ?? buf.length),\n };\n}\n\n/** 默认产物落盘目录(对标 Claude Code 工具产物本地直接访问)。 */\nexport function defaultDownloadDir(): string {\n return path.join(os.homedir(), 'Downloads', 'hcm');\n}\n\n/**\n * 把后端相对下载链接(`/api/documents/download/{id}?...sig=`)拼成可点的绝对 URL,\n * 供终端渲染——签名 URL 自洽免 Bearer,点开即由浏览器打开(ADR-140)。\n * apiBase 为 CLI 连接的后端地址(`HcmClient.endpoint`);无 apiBase 或已是绝对 URL 时原样返回。\n */\nexport function absoluteDownloadUrl(apiBase: string | undefined, url: string): string {\n if (!apiBase || !url.startsWith('/')) return url;\n return apiBase.replace(/\\/+$/, '') + url;\n}\n\n/**\n * 认证下载文档到本地(`/api/documents/download` 需 Bearer,浏览器裸 GET 恒 401 的同款教训)。\n * destPath 为目录或省略时落 defaultDownloadDir,重名自动加序号。返回写入的绝对路径。\n */\nexport async function downloadDocument(\n http: AxiosInstance,\n ref: { documentId?: string; downloadUrl?: string; fileName?: string },\n destPath?: string,\n): Promise<string> {\n const url = ref.downloadUrl ?? `/api/documents/download/${ref.documentId}`;\n const resp = await http.get(url, { responseType: 'arraybuffer' });\n const headerName = parseFilenameFromDisposition(resp.headers?.['content-disposition']);\n // 🔴 路径穿越收口:fileName 来自服务端(content-disposition)/ 工具 result(LLM 可控),\n // safeBasename 剥掉所有目录段——杜绝 \"../../../.ssh/authorized_keys\" 类静默任意文件写\n // (auto-download 默认开,恶意工具 result 否则可写任意路径)。\n const rawName = ref.fileName ?? headerName ?? `${ref.documentId ?? 'download'}.bin`;\n\n // 目标目录与文件名解析:destPath 为目录/省略 → 落该目录 + 服务端 fileName;\n // destPath 指向文件 → 落其父目录 + destPath 的 basename(用户显式命名)。\n let dir: string;\n let nameSource: string;\n const expanded = destPath ? expandHome(destPath) : undefined;\n if (expanded && (expanded.endsWith(path.sep) || isExistingDir(expanded))) {\n dir = expanded;\n nameSource = rawName;\n } else if (expanded) {\n dir = path.dirname(expanded);\n nameSource = path.basename(expanded);\n } else {\n dir = defaultDownloadDir();\n nameSource = rawName;\n }\n const finalName = safeBasename(nameSource, ref.documentId);\n let target = path.join(dir, finalName);\n // 纵深防御:归属断言——target 必须在 dir 内(safeBasename 后理应恒真,留作回潮保险)。\n const resolvedDir = path.resolve(dir);\n if (path.resolve(target) !== path.join(resolvedDir, finalName)) {\n throw new Error(`下载路径越界:${target} 不在 ${resolvedDir} 内`);\n }\n await fsp.mkdir(dir, { recursive: true });\n target = dedupePath(target);\n await fsp.writeFile(target, Buffer.from(resp.data as ArrayBuffer));\n return target;\n}\n\nfunction expandHome(p: string): string {\n return p.startsWith('~') ? path.join(os.homedir(), p.slice(1)) : path.resolve(p);\n}\n\n/**\n * 文件名净化:剥所有目录段(path.basename 处理 / 与 \\\\)+ 拒 .. / 空 / 纯点。\n * 退化时回落 documentId 或 'download',绝不返回可逃出目标目录的串。\n */\nfunction safeBasename(rawName: string, documentId?: string): string {\n // 先归一反斜杠(防 Windows 风格 ..\\\\ 在 posix path.basename 下漏网)。\n const base = path.basename(rawName.replace(/\\\\/g, '/'));\n if (!base || base === '.' || base === '..' || base.includes('/')) {\n return documentId ? `${documentId}.bin` : 'download.bin';\n }\n return base;\n}\n\nfunction isExistingDir(p: string): boolean {\n try {\n return fs.statSync(p).isDirectory();\n } catch {\n return false;\n }\n}\n\n/** 重名文件加序号:report.xlsx → report (1).xlsx。 */\nfunction dedupePath(target: string): string {\n if (!fs.existsSync(target)) return target;\n const dir = path.dirname(target);\n const ext = path.extname(target);\n const base = path.basename(target, ext);\n for (let i = 1; ; i++) {\n const candidate = path.join(dir, `${base} (${i})${ext}`);\n if (!fs.existsSync(candidate)) return candidate;\n }\n}\n\nfunction parseFilenameFromDisposition(disposition?: string): string | undefined {\n if (!disposition) return undefined;\n const star = /filename\\*=UTF-8''([^;]+)/i.exec(disposition);\n if (star?.[1]) {\n try {\n return decodeURIComponent(star[1]);\n } catch {\n /* fallthrough */\n }\n }\n const plain = /filename=\"?([^\";]+)\"?/i.exec(disposition);\n return plain?.[1];\n}\n","export function toJson(value: unknown): string {\n return JSON.stringify(value, null, 2);\n}\n","import yaml from 'js-yaml';\n\nexport function toYaml(value: unknown): string {\n return yaml.dump(value, { indent: 2, lineWidth: -1 });\n}\n","import Table from 'cli-table3';\nimport { displayWidth, truncateDisplay } from '../chat/display-width';\n\nconst MAX_COL_WIDTH = 40;\n\n/**\n * 盒式文本表格。列宽**按内容自适应**、以 {@link MAX_COL_WIDTH} 为上限——短列(SEQ/ROUND\n * 等)不再被强制撑到 40 列(旧 `colWidths.map(()=>40)` 的 BUG:7 列表 ≈ 290 列宽、糊成一片)。\n * 宽度与截断均按**终端显示宽度**(CJK=2 列)计,避免中文单元格溢出错位。\n */\nexport function toTable(rows: Record<string, unknown>[]): string {\n if (rows.length === 0) return '(no results)';\n const cols = Object.keys(rows[0]!);\n const heads = cols.map((c) => c.toUpperCase());\n const cells = rows.map((r) => cols.map((c) => formatCell(r[c])));\n\n // 每列内容宽 = min(上限, max(表头, 各单元格显示宽))。\n const contentWidths = cols.map((_, i) =>\n Math.min(\n MAX_COL_WIDTH,\n Math.max(displayWidth(heads[i]!), ...cells.map((row) => displayWidth(row[i]!))),\n ),\n );\n\n const table = new Table({\n head: heads,\n style: { head: ['cyan'] },\n // +2:cli-table3 的 colWidths 含左右各 1 列 padding。\n colWidths: contentWidths.map((w) => w + 2),\n wordWrap: true,\n });\n for (const row of cells) table.push(row);\n return table.toString();\n}\n\nfunction formatCell(v: unknown): string {\n if (v === null || v === undefined) return '';\n const s = typeof v === 'object' ? JSON.stringify(v) : String(v);\n return truncateDisplay(s, MAX_COL_WIDTH);\n}\n","/**\n * 终端显示宽度(east-asian-width 近似,零依赖):CJK 表意/全角标点/假名/谚文按 2 列,\n * 其余按 1 列。argsPreview 等终端单行截断必须按显示宽度——按字符数截 120 的中文实际\n * 占 240 列必折行(2026-06-11 实测工具行折行根因)。\n */\nfunction charDisplayWidth(cp: number): number {\n return (cp >= 0x1100 && cp <= 0x115f) || // Hangul Jamo\n (cp >= 0x2e80 && cp <= 0xa4cf) || // CJK 部首/注音/汉字/假名\n (cp >= 0xac00 && cp <= 0xd7a3) || // Hangul 音节\n (cp >= 0xf900 && cp <= 0xfaff) || // CJK 兼容表意\n (cp >= 0xfe30 && cp <= 0xfe4f) || // CJK 兼容形式\n (cp >= 0xff00 && cp <= 0xff60) || // 全角 ASCII/标点\n (cp >= 0xffe0 && cp <= 0xffe6) ||\n (cp >= 0x20000 && cp <= 0x3fffd) // CJK 扩展 B+\n ? 2\n : 1;\n}\n\n/** 字符串的终端显示宽度(列数)。 */\nexport function displayWidth(s: string): number {\n let w = 0;\n for (const ch of s) w += charDisplayWidth(ch.codePointAt(0) as number);\n return w;\n}\n\n/** 按显示宽度截断,超宽时截到 maxWidth-1 列并补「…」;未超宽原样返回。 */\nexport function truncateDisplay(s: string, maxWidth: number): string {\n if (displayWidth(s) <= maxWidth) return s;\n let w = 0;\n let out = '';\n for (const ch of s) {\n const cw = charDisplayWidth(ch.codePointAt(0) as number);\n if (w + cw > maxWidth - 1) break;\n w += cw;\n out += ch;\n }\n return out + '…';\n}\n","import { toJson } from './json';\nimport { toYaml } from './yaml';\nimport { toTable } from './table';\n\nexport type OutputFormat = 'table' | 'json' | 'yaml';\n\nexport interface FormatOptions {\n format: OutputFormat;\n}\n\nexport function formatRows(rows: Record<string, unknown>[], opts: FormatOptions): string {\n switch (opts.format) {\n case 'json':\n return toJson(rows);\n case 'yaml':\n return toYaml(rows);\n case 'table':\n return toTable(rows);\n }\n}\n\nexport function formatObject(obj: unknown, opts: FormatOptions): string {\n switch (opts.format) {\n case 'json':\n return toJson(obj);\n case 'yaml':\n return toYaml(obj);\n case 'table':\n // 单对象 fallback 到 json\n return toJson(obj);\n }\n}\n\nexport { toJson, toYaml, toTable };\n","import type { AxiosInstance } from 'axios';\nimport { TokenStore, type TokenRecord } from './token-store';\nimport { type LoginOutcome } from './principal';\n\nexport interface ClientCredentialsInput {\n endpoint: string;\n tenantId: string;\n clientId: string;\n clientSecret: string;\n profile: string;\n httpClient: AxiosInstance;\n}\n\nexport async function loginClientCredentials(\n input: ClientCredentialsInput,\n): Promise<LoginOutcome> {\n const resp = await input.httpClient.post(\n `${input.endpoint}/api/oauth/token`,\n {\n grant_type: 'client_credentials',\n client_id: input.clientId,\n client_secret: input.clientSecret,\n },\n { headers: { 'Content-Type': 'application/json' } },\n );\n const { access_token, refresh_token, expires_in, scope } = resp.data;\n if (!access_token) {\n throw new Error('OAuth response missing access_token');\n }\n\n const ttlSeconds = expires_in ?? 3600;\n const expiresAt = new Date(Date.now() + ttlSeconds * 1000).toISOString();\n const scopes = (scope ?? '').split(/\\s+/).filter(Boolean);\n\n const store = new TokenStore(input.profile);\n const accessTokenRef = await store.setAccessToken(access_token);\n const refreshTokenRef = refresh_token ? await store.setRefreshToken(refresh_token) : undefined;\n\n const record: TokenRecord = {\n scheme: 'client_credentials',\n accessTokenRef,\n refreshTokenRef,\n expiresAt,\n scopes,\n ttlSeconds,\n };\n await store.writeMeta(record);\n // client_credentials 是机器身份,服务端不回 user —— principal 恒为 undefined,\n // 这与「口令登录拿不到身份」是两件事,别在回显上合并。\n return { record };\n}\n","import type { AxiosInstance } from 'axios';\nimport { TokenStore, type TokenRecord } from './token-store';\nimport { toPrincipal, type LoginOutcome } from './principal';\n\nexport interface PatLoginInput {\n endpoint: string;\n tenantId: string;\n pat: string;\n profile: string;\n httpClient: AxiosInstance;\n}\n\nexport async function loginPat(input: PatLoginInput): Promise<LoginOutcome> {\n // 验证 PAT 有效 + 拿用户/scope 信息\n const resp = await input.httpClient.get(`${input.endpoint}/api/auth/info`, {\n headers: { Authorization: `Bearer ${input.pat}` },\n });\n const scopes: string[] = resp.data?.scopes ?? [];\n // `/api/auth/info` 回的是 UserInfoResponse(userId/username/tenantId/roles) ——\n // PAT 到底挂在谁头上,只有这里说得出来。\n\n const store = new TokenStore(input.profile);\n const accessTokenRef = await store.setAccessToken(input.pat);\n\n // PAT 没有 refresh,过期时间设为 1 年(PAT 由后端管失效)\n const expiresAt = new Date(Date.now() + 365 * 24 * 3600 * 1000).toISOString();\n\n const record: TokenRecord = {\n scheme: 'pat',\n accessTokenRef,\n expiresAt,\n scopes,\n };\n await store.writeMeta(record);\n return { record, principal: toPrincipal(resp.data) };\n}\n","import type { AxiosInstance } from 'axios';\nimport { TokenStore, type TokenRecord } from './token-store';\nimport { toPrincipal, type LoginOutcome } from './principal';\nimport { DEFAULT_SESSION_TTL_SECONDS } from './password';\n\nexport interface PairingLoginInput {\n endpoint: string;\n /** 页面上复制来的配对码。大小写与连字符由服务端归一化,这里原样传。 */\n code: string;\n profile: string;\n httpClient: AxiosInstance;\n}\n\n/**\n * 配对码登录(ADR-308 第四轨)。\n *\n * 解的是「没有任何一方知道后端地址与 tenantId」:CLI 只有磁盘上人手填的值,用户知道的\n * 是浏览器地址栏那一串,后端全树无 frontend URL 配置。唯一同时知道这两件事的是那个\n * 已登录的页面——所以地址由命令行位置参数带进来(人从页面复制整行),租户由服务端\n * 在响应里给出,**不再有 `'1'` 这个猜测**。\n *\n * 🔴 `scheme` 记 `'pairing'` 而不是 `'password'`:如实记录来路(审计与回显需要)。\n * 但它换来的是 `TokenManager.createToken` 铸的**普通会话 token**,续期/吊销语义与口令\n * 登录完全一致——这一点由 `isServerSlidingSession` 谓词保证,别在别处写\n * `=== 'password'` 的比较。\n */\nexport async function loginPairing(input: PairingLoginInput): Promise<LoginOutcome> {\n const resp = await input.httpClient.post(\n `${input.endpoint}/api/auth/cli/exchange`,\n { code: input.code },\n { headers: { 'Content-Type': 'application/json' } },\n );\n // 响应刻意与 /api/auth/login 同形(LoginResponse),所以这里能与口令轨共用解析。\n const { token, expiresIn, user } = resp.data;\n if (!token) {\n throw new Error('Pairing exchange response missing token');\n }\n\n const ttlSeconds = expiresIn ?? DEFAULT_SESSION_TTL_SECONDS;\n const expiresAt = new Date(Date.now() + ttlSeconds * 1000).toISOString();\n\n const store = new TokenStore(input.profile);\n const accessTokenRef = await store.setAccessToken(token);\n\n const record: TokenRecord = {\n scheme: 'pairing',\n accessTokenRef,\n // 与口令会话同样没有 refresh token:续期语义是「同一个 token 被服务端重设 TTL」。\n refreshTokenRef: undefined,\n expiresAt,\n scopes: ['*'],\n ttlSeconds,\n };\n await store.writeMeta(record);\n return { record, principal: toPrincipal(user) };\n}\n\n/**\n * 从用户粘贴的地址里取出 origin。\n *\n * 人复制的是**浏览器地址栏那一串**,带路径、带查询参数都正常——那正是他唯一确定知道的\n * 东西。要求他先手工削成 origin,等于把「知道后端地址」这个门槛又搬了回来。\n */\nexport function toOrigin(raw: string): string {\n const withScheme = /^https?:\\/\\//i.test(raw) ? raw : `https://${raw}`;\n const url = new URL(withScheme);\n return url.origin;\n}\n\n/**\n * 由 origin 推一个 env 名:`https://hcm.custa.com` → `custa`。\n *\n * 取的是主域左边那一段而不是整个 hostname:`hcm.custa.com` 与 `hcm-test.custa.com`\n * 会推出不同的名字,而 `hcm.custa.com` 与 `www.custa.com` 也不会撞。\n * 推不出来时落到 `default`,由调用方处理重名。\n */\nexport function inferEnvName(origin: string): string {\n const host = new URL(origin).hostname;\n if (/^\\d+(\\.\\d+)*$/.test(host) || host === 'localhost') return 'local';\n const parts = host.split('.').filter(Boolean);\n if (parts.length === 0) return 'default';\n // hcm.custa.com → custa(倒数第二段);custa.com → custa;单段 → 该段\n const candidate = parts.length >= 2 ? parts[parts.length - 2] : parts[0];\n return candidate || 'default';\n}\n","import type { AxiosInstance } from 'axios';\n\n/**\n * 首登强制改密(fresh tenant 的必经一步)。\n *\n * <p>后端契约(AuthController):\n * <ul>\n * <li>用初始口令 `POST /api/auth/login` → **403**,body 是\n * `{error:{code:\"AUTH_PASSWORD_CHANGE_REQUIRED\", details:{changePasswordToken, expiresInSeconds}}}`\n * —— 那个临时 token **只活 300 秒**;</li>\n * <li>拿它作 Bearer 调 `POST /api/auth/change-password`,body `{oldPassword,newPassword,confirmPassword}`;</li>\n * <li>改完用新口令重新 login 才拿到正式 token。</li>\n * </ul>\n *\n * <p>🔴 这是 `hcm tenant bootstrap` 之后的**硬阻塞**:不走这一步,新建的租户登不进去。\n * 所以 `hcm login` 自动识别并接管,而不是让顾问自己去拼两条 curl。\n */\n\nexport const PASSWORD_CHANGE_REQUIRED = 'AUTH_PASSWORD_CHANGE_REQUIRED';\n\nexport interface PasswordChangeChallenge {\n changePasswordToken: string;\n expiresInSeconds?: number;\n}\n\n/**\n * 从一个失败的 login 响应里识别「强制改密」挑战。\n *\n * 不是该挑战(网络错、口令错、账号锁定…)一律返回 null,由调用方按原错误处理——\n * 判据只认后端的错误码,不靠 HTTP 状态猜(423 账号锁定同样是 4xx,不能混)。\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function detectPasswordChangeChallenge(err: any): PasswordChangeChallenge | null {\n const body = err?.response?.data;\n const error = body?.error ?? body;\n if (error?.code !== PASSWORD_CHANGE_REQUIRED) return null;\n const token = error?.details?.changePasswordToken;\n if (!token) return null;\n return {\n changePasswordToken: String(token),\n expiresInSeconds: Number(error?.details?.expiresInSeconds) || undefined,\n };\n}\n\nexport interface ChangePasswordInput {\n endpoint: string;\n /** 登录挑战里拿到的临时 token(300 秒有效) */\n changePasswordToken: string;\n oldPassword: string;\n newPassword: string;\n httpClient: AxiosInstance;\n}\n\nexport async function changePassword(input: ChangePasswordInput): Promise<void> {\n await input.httpClient.post(\n `${input.endpoint}/api/auth/change-password`,\n {\n oldPassword: input.oldPassword,\n newPassword: input.newPassword,\n confirmPassword: input.newPassword,\n },\n {\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${input.changePasswordToken}`,\n },\n },\n );\n}\n","import type { AxiosInstance } from 'axios';\n\nexport interface AdminResetPasswordRequest {\n userId?: string;\n username?: string;\n newPassword: string;\n confirmPassword: string;\n forceChangeOnNextLogin: boolean;\n}\n\nexport interface AdminResetPasswordResult {\n status: 'PASSWORD_RESET';\n userId: string;\n forceChangeOnNextLogin: boolean;\n credentialVersion: number;\n sessionsRevoked: boolean;\n removedLoginAttemptKeys: number;\n}\n\n/**\n * 管理员重置租户内用户的本地登录口令。\n *\n * 该端点属于专用凭据流:密码只存在于 HTTPS 请求体,不经过 Model 元数据、\n * Action 参数、日志或返回值。调用方仍须避免把密码放进 argv。\n */\nexport async function adminResetPassword(\n http: AxiosInstance,\n input: AdminResetPasswordRequest,\n): Promise<AdminResetPasswordResult> {\n const response = await http.post('/api/admin/users/password-reset', input);\n const payload = response.data?.data ?? response.data;\n return payload as AdminResetPasswordResult;\n}\n","import WebSocket from 'ws';\nimport { CliError, CliErrorCode } from '../errors';\nimport type {\n StreamCommandEnvelope,\n StreamEventEnvelope,\n ConnectionMessage,\n ControlChannelEnvelope,\n} from './protocol';\n\nexport type MessageClass = 'control' | 'event' | 'connection';\n\n/** WS 入站消息三分:控制通道(channel='control')优先于流事件(streamId+seq),其余为连接层。 */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function classifyMessage(parsed: any): MessageClass {\n if (parsed?.channel === 'control') return 'control';\n if (parsed?.streamId && typeof parsed.seq === 'number') return 'event';\n return 'connection';\n}\n\nexport interface WsClientOpts {\n endpoint: string; // wss://host/ws/conversation\n getAccessToken: () => Promise<string | null>;\n onTokenExpiring?: () => Promise<void>; // 后台 refresh\n heartbeatMs?: number;\n}\n\ntype Handler =\n | { kind: 'event'; fn: (ev: StreamEventEnvelope) => void }\n | { kind: 'connection'; fn: (msg: ConnectionMessage) => void }\n | { kind: 'control'; fn: (ev: ControlChannelEnvelope) => void };\n\nexport class WsClient {\n private ws?: WebSocket;\n private handlers: Handler[] = [];\n private heartbeatTimer?: NodeJS.Timeout;\n private ready = false;\n private sessionId?: string;\n /** 流进行中断连的持久订阅者(iterator 等终态收口者)。RISK-1:auth 后无持久\n * close handler,断连时上层 await 永久挂死。这里让消费方能感知 socket 死。 */\n private disconnectHandlers = new Set<() => void>();\n /** 由 close()/ensureConnected 主动关闭 → 不广播 disconnect(非异常断连)。 */\n private intentionalClose = false;\n\n constructor(private opts: WsClientOpts) {}\n\n /**\n * 订阅「流进行中 socket 异常断连」(后端重启 / 网络闪断)。主动 close() 不触发。\n * iterator 订阅它 → 断连时 finish()(incomplete 终态),杜绝 await consume 永久挂死。\n */\n onDisconnect(fn: () => void): () => void {\n this.disconnectHandlers.add(fn);\n return () => {\n this.disconnectHandlers.delete(fn);\n };\n }\n\n private notifyDisconnect(): void {\n if (this.intentionalClose) return;\n this.ready = false;\n for (const fn of [...this.disconnectHandlers]) {\n try {\n fn();\n } catch {\n /* 单个订阅者抛错不影响其他 */\n }\n }\n }\n\n async connect(): Promise<void> {\n const tok = await this.opts.getAccessToken();\n if (!tok) {\n throw new CliError({\n code: CliErrorCode.AUTH_FAILED,\n message: 'no access token; run `hcm login`',\n });\n }\n this.ws = new WebSocket(this.opts.endpoint);\n\n return new Promise<void>((resolve, reject) => {\n const settle = (fn: () => void) => {\n off();\n fn();\n };\n const off = () => {\n this.ws?.removeListener('open', onOpen);\n this.ws?.removeListener('message', onMessage);\n this.ws?.removeListener('error', onError);\n this.ws?.removeListener('close', onClose);\n };\n const onOpen = () => {\n this.ws?.send(JSON.stringify({ type: 'auth', token: tok }));\n };\n const onMessage = (data: WebSocket.RawData) => {\n const text = data.toString();\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n let parsed: any;\n try {\n parsed = JSON.parse(text);\n } catch {\n return;\n }\n\n // 三分:控制通道 vs 流事件 vs 连接层\n switch (classifyMessage(parsed)) {\n case 'control':\n this.dispatchControl(parsed as ControlChannelEnvelope);\n return;\n case 'event':\n this.dispatchEvent(parsed as StreamEventEnvelope);\n return;\n case 'connection':\n break;\n }\n const msg = parsed as ConnectionMessage;\n this.dispatchConnection(msg);\n\n if (msg.type === 'auth_success') {\n this.ready = true;\n this.intentionalClose = false;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n this.sessionId = (msg as any).sessionId;\n this.startHeartbeat();\n settle(resolve);\n // 装上持久 listener,接管后续流事件 / 连接层消息\n this.ws!.on('message', this.persistentOnMessage);\n // RISK-1:持久 close/error listener——auth 后 settle 的 off() 摘掉了建连期\n // 的 onClose,此后无人感知 socket 死。流进行中异常断连时广播 disconnect,\n // 让 iterator finish() 收口,杜绝 await consume 永久挂死。\n this.ws!.on('close', this.persistentOnClose);\n this.ws!.on('error', this.persistentOnClose);\n } else if (msg.type === 'auth_error') {\n settle(() =>\n reject(\n new CliError({\n code: CliErrorCode.WS_AUTH_ERROR,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n message: `${(msg as any).code}: ${(msg as any).message}`,\n }),\n ),\n );\n } else if (msg.type === 'token_expiring' && this.opts.onTokenExpiring) {\n void this.opts.onTokenExpiring();\n }\n };\n const onError = (err: Error) =>\n settle(() =>\n reject(new CliError({ code: CliErrorCode.NETWORK_ERROR, message: err.message })),\n );\n const onClose = () =>\n settle(() =>\n reject(\n new CliError({ code: CliErrorCode.NETWORK_ERROR, message: 'WS closed before auth' }),\n ),\n );\n this.ws!.on('open', onOpen);\n this.ws!.on('message', onMessage);\n this.ws!.on('error', onError);\n this.ws!.on('close', onClose);\n });\n }\n\n send<P>(cmd: StreamCommandEnvelope<P>): void {\n if (!this.ready || !this.ws) {\n throw new CliError({ code: CliErrorCode.NETWORK_ERROR, message: 'WS not ready' });\n }\n this.ws.send(JSON.stringify(cmd));\n }\n\n onEvent(fn: (ev: StreamEventEnvelope) => void): () => void {\n const h: Handler = { kind: 'event', fn };\n this.handlers.push(h);\n return () => {\n this.handlers = this.handlers.filter((x) => x !== h);\n };\n }\n\n onConnection(fn: (msg: ConnectionMessage) => void): () => void {\n const h: Handler = { kind: 'connection', fn };\n this.handlers.push(h);\n return () => {\n this.handlers = this.handlers.filter((x) => x !== h);\n };\n }\n\n onControl(fn: (ev: ControlChannelEnvelope) => void): () => void {\n const h: Handler = { kind: 'control', fn };\n this.handlers.push(h);\n return () => {\n this.handlers = this.handlers.filter((x) => x !== h);\n };\n }\n\n isReady(): boolean {\n return this.ready;\n }\n\n /**\n * 连接已断(后端重启 / 网络闪断 / 心跳静默失败后 socket 已死)时重建连接。\n * REPL 长开场景每轮发消息前调用——旧连接死掉不应让用户重启 CLI(2026-06-12 用户\n * 实测:后端重启后 REPL 下一条消息报错)。已连接且 socket OPEN 时为 no-op。\n */\n async ensureConnected(): Promise<void> {\n if (this.ready && this.ws && this.ws.readyState === WebSocket.OPEN) return;\n await this.close();\n await this.connect();\n }\n\n getSessionId(): string | undefined {\n return this.sessionId;\n }\n\n async close(): Promise<void> {\n if (this.heartbeatTimer) clearInterval(this.heartbeatTimer);\n this.ready = false;\n this.intentionalClose = true; // 主动关 → persistentOnClose 不广播 disconnect\n if (this.ws) {\n this.ws.removeListener('message', this.persistentOnMessage);\n this.ws.removeListener('close', this.persistentOnClose);\n this.ws.removeListener('error', this.persistentOnClose);\n if (this.ws.readyState === WebSocket.OPEN) this.ws.close();\n }\n }\n\n /** 流进行中 socket 异常断连(非主动 close)→ 广播 disconnect 让消费方收口。 */\n private persistentOnClose = (): void => {\n this.notifyDisconnect();\n };\n\n private persistentOnMessage = (data: WebSocket.RawData): void => {\n const text = data.toString();\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n let parsed: any;\n try {\n parsed = JSON.parse(text);\n } catch {\n return;\n }\n switch (classifyMessage(parsed)) {\n case 'control':\n this.dispatchControl(parsed as ControlChannelEnvelope);\n return;\n case 'event':\n this.dispatchEvent(parsed as StreamEventEnvelope);\n return;\n case 'connection':\n break;\n }\n const msg = parsed as ConnectionMessage;\n this.dispatchConnection(msg);\n if (msg.type === 'token_expiring' && this.opts.onTokenExpiring) {\n void this.opts.onTokenExpiring();\n }\n };\n\n private startHeartbeat(): void {\n const ms = this.opts.heartbeatMs ?? 30_000;\n this.heartbeatTimer = setInterval(() => {\n try {\n this.ws?.send(JSON.stringify({ type: 'ping' }));\n } catch {\n /* swallow heartbeat send errors */\n }\n }, ms);\n }\n\n private dispatchEvent(ev: StreamEventEnvelope): void {\n for (const h of this.handlers) if (h.kind === 'event') h.fn(ev);\n }\n\n private dispatchConnection(msg: ConnectionMessage): void {\n for (const h of this.handlers) if (h.kind === 'connection') h.fn(msg);\n }\n\n private dispatchControl(ev: ControlChannelEnvelope): void {\n for (const h of this.handlers) if (h.kind === 'control') h.fn(ev);\n }\n}\n","import type { AttachmentMeta } from '../documents';\nimport { randomUUID } from 'node:crypto';\nimport type { WsClient } from './ws-client';\nimport type {\n StreamEventEnvelope,\n StreamCommandEnvelope,\n MessageCreatePayload,\n ControlChannelEnvelope,\n} from './protocol';\nimport { buildInterrupt, type StreamCommand } from './control-channel';\n\nexport interface SendMessageOpts {\n conversationId?: string; // 不传 = 新建\n agentKey?: string;\n agentId?: string; // direct-ops mode: bypass conversation→agent lookup\n pageContext?: Record<string, unknown>;\n /**\n * confirm/ask 决策回发后继续消费 resume 流到真终态(Task B3)。\n *\n * v4 协议:confirm 触发时原流以 ROOT message.stop 终态(HALT 语义);决策回发后后端\n * resume 续推事件。开启本开关后终态规则变为:\n * - ROOT message.stop 且无未决交互 → 终态(含全程无交互的普通流)\n * - ROOT message.stop 且有未决交互 → hold(HALT / 串联 confirm 的中间 stop)\n * - `control.interaction.resolved(phase=TERMINAL)` 清空未决集且曾 hold 过 ROOT stop\n * → 终态兜底(TERMINAL 是 InteractionSession 生命周期事件,必达;防 resume 段缺\n * ROOT message.stop 的后端回归挂死 CLI)\n * 仅在调用方会回发决策(--on-confirm / --on-ask / 交互式确认)时开启;默认关闭,\n * 行为与历史完全一致。\n */\n holdUntilInteractionTerminal?: boolean;\n /**\n * 重新生成模式(ADR-089):不发 message.create,改发 `response.regenerate`\n * (payload {fromSeq})。fromSeq = 被重生成 assistant 段起点的 journal seq\n * (LLM_DONE;timeline chat-text item 的 seq 即它)。后端 append SEGMENT_SUPERSEDED\n * marker 后用原 user message 重跑 loop,新流事件 requestId 回显本命令 commandId\n * ——iterator 过滤与终态规则完全复用。必须配合已存在的 conversationId。\n */\n regenerateFromSeq?: number;\n /** 附件元数据(issue #64):已上传文档随消息发送,后端 parseAttachments 同形态。 */\n attachments?: AttachmentMeta[];\n}\n\nexport interface MessageStreamHandle {\n conversationId: string;\n commandId: string;\n events: AsyncIterable<StreamEventEnvelope>;\n /**\n * 客户端中断当前流(ADR-085)。\n * 发送 `stream.cancel` command 到后端;后端会回 `response.interrupted` 作 iterator 终态。\n * 调用方仍需等 iterator 走完才能确认终止。\n */\n cancel(reason?: string): void;\n /**\n * 订阅本 conversation 的带外控制通道事件(control.interaction.request / resolved)。\n * 仅转发 `ev.conversationId === conversationId` 的事件;返回取消订阅函数。\n */\n onControl(fn: (ev: ControlChannelEnvelope) => void): () => void;\n /**\n * 回发 client→server 控制命令(approval.confirm / approval.reject / interaction.answer)。\n * 由 control-channel 的 build* 构造,透传到 ws。\n */\n sendCommand(cmd: StreamCommand): void;\n}\n\nexport async function sendMessageAndStream(\n ws: WsClient,\n prompt: string,\n opts: SendMessageOpts = {},\n): Promise<MessageStreamHandle> {\n const conversationId = opts.conversationId ?? randomUUID();\n const commandId = randomUUID();\n const queue: StreamEventEnvelope[] = [];\n const waiters: Array<(ev: IteratorResult<StreamEventEnvelope>) => void> = [];\n let done = false;\n const seenEventIds = new Set<string>();\n\n // B3 · hold 模式状态:未决交互集(值 = 是否已收到 RESUMED 受理)+ 是否 hold 过\n // ROOT stop(HALT 已发生的标记)。B4 升级为 Map:接管链语义需要区分「已 RESUMED」。\n const hold = !!opts.holdUntilInteractionTerminal;\n const pendingInteractions = new Map<string, { resumed: boolean }>();\n let sawHeldRootStop = false;\n let offControlInternal: (() => void) | undefined;\n\n const finish = (): void => {\n if (done) return;\n done = true;\n while (waiters.length) {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion, @typescript-eslint/no-explicit-any\n waiters.shift()!({ value: undefined as any, done: true });\n }\n off();\n offControlInternal?.();\n offDisconnect?.();\n };\n\n // RISK-1:流进行中 socket 异常断连(后端重启 / 网络闪断)→ finish()(incomplete 终态),\n // 杜绝 await consume 永久挂死。queue 中已收事件在 finish 前被消费方吐尽,断后置 done。\n // onDisconnect 可选(旧 WsClient / 测试 stub 无此方法时跳过,不破坏向后兼容)。\n const offDisconnect = ws.onDisconnect?.(() => finish());\n\n const off = ws.onEvent((ev) => {\n if (done) return;\n if (ev.requestId !== commandId) return;\n // v4 去重必须按 eventId,不能按 seq 单调(旧 v3 逻辑)。v4 的 ephemeral 事件\n // (LlmDelta text/thinking 等)共享上一个 material 的 seq(不递增),seq<=lastSeq\n // 会把 text delta + 终态事件全部误杀 → 空白回答 + 等不到 response.completed 而 timeout。\n if (ev.eventId && seenEventIds.has(ev.eventId)) return;\n if (ev.eventId) seenEventIds.add(ev.eventId);\n if (waiters.length) {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n waiters.shift()!({ value: ev, done: false });\n } else {\n queue.push(ev);\n }\n\n // v3 残余终态(completed/failed/interrupted)只在老路径出现,老路径无 resume → 一律立即终态。\n if (\n ev.type === 'response.completed' ||\n ev.type === 'response.failed' ||\n ev.type === 'response.interrupted'\n ) {\n finish();\n return;\n }\n // v4 原生终态:ROOT message.stop(parentToolUseId 空)= turn 结束,与 Web v4 同。\n // 某些路径(如 hcm_respond_directly 短路)后端不发 v3 兼容残余 response.completed,\n // 不以 message.stop 兜底则 iterator 永不结束而 timeout。子流 message.stop(parentToolUseId 非空)不算终态。\n if (\n ev.type === 'message.stop' &&\n !(ev.payload as { parentToolUseId?: string } | undefined)?.parentToolUseId\n ) {\n if (!hold || pendingInteractions.size === 0) {\n finish();\n } else {\n // HALT(或串联 confirm 的中间 stop):有未决交互 → 不终态,等 resume / TERMINAL。\n sawHeldRootStop = true;\n }\n }\n });\n\n // hold 模式:内部订阅 control 通道维护未决交互集。TERMINAL resolved 清空 pending 且\n // 曾 hold 过 ROOT stop → 终态兜底(resume 段内容此刻已推完,queue 中事件仍会被吐尽)。\n if (hold) {\n offControlInternal = ws.onControl((cev) => {\n if (done || cev.conversationId !== conversationId) return;\n const p = cev.payload as { interactionId?: string; phase?: string } | undefined;\n if (!p?.interactionId) return;\n if (cev.type === 'control.interaction.request') {\n // B4 · 接管链语义(bay4 真流实测):interaction 被嵌套 confirm「接管」后\n // (前序已 RESUMED、其 resume 轮又 HALTED 交给新 confirm),后端按设计\n // 不再为前序发 TERMINAL(HALTED 分支不发)。新 request 到达 = 已 RESUMED\n // 的前序终局由新 interaction 接管 → 从 pending 清除,否则链式 confirm 下\n // 终态条件 pending==0 永不满足而挂死。未 RESUMED 的并存 interaction\n // (用例⑤)各有自己的 TERMINAL,不在清理范围。\n for (const [id, st] of pendingInteractions) {\n if (st.resumed) pendingInteractions.delete(id);\n }\n pendingInteractions.set(p.interactionId, { resumed: false });\n } else if (cev.type === 'control.interaction.resolved') {\n if (p.phase === 'RESUMED') {\n const st = pendingInteractions.get(p.interactionId);\n if (st) st.resumed = true;\n } else if (p.phase === 'TERMINAL') {\n pendingInteractions.delete(p.interactionId);\n if (pendingInteractions.size === 0 && sawHeldRootStop) finish();\n }\n }\n });\n }\n\n const cmd: StreamCommandEnvelope<MessageCreatePayload | { fromSeq: number }> =\n opts.regenerateFromSeq != null\n ? {\n // regenerate:同 conversation 重跑被废弃段,事件 requestId 回显本 commandId。\n protocolVersion: '4.0',\n commandId,\n conversationId,\n type: 'response.regenerate',\n timestamp: Date.now(),\n payload: { fromSeq: opts.regenerateFromSeq },\n }\n : {\n // v4 reference client:后端 ADR-118 起无条件推 v4,此字段是诚实声明(不改变后端行为)。\n protocolVersion: '4.0',\n commandId,\n conversationId,\n type: 'message.create',\n timestamp: Date.now(),\n payload: {\n content: prompt,\n agentKey: opts.agentKey,\n directOpsAgentId: opts.agentId,\n pageContext: opts.pageContext,\n attachments: opts.attachments?.length ? opts.attachments : undefined,\n },\n };\n ws.send(cmd);\n\n const events: AsyncIterable<StreamEventEnvelope> = {\n [Symbol.asyncIterator]: () => ({\n next: () =>\n new Promise((resolve) => {\n if (queue.length) {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n resolve({ value: queue.shift()!, done: false });\n } else if (done) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n resolve({ value: undefined as any, done: true });\n } else {\n waiters.push(resolve);\n }\n }),\n }),\n };\n\n const cancel = (_reason: string = 'USER_CANCEL'): void => {\n try {\n // response.interrupt — 后端唯一认的打断命令(StreamCommandType.RESPONSE_INTERRUPT)。\n // 🔴 旧版发 v3.2 的 stream.cancel:后端命令枚举里没有它——cancel 一直是空操作\n // (Esc/Ctrl+C 打断踢空,2026-06-12 用户终验钉出)。统一走 buildInterrupt,消除\n // 与 control-channel 手搓 envelope 的 protocolVersion 不一致(白盒审计 RISK 文档债)。\n ws.send(buildInterrupt(conversationId));\n } catch {\n /* ws 已关闭, 忽略 */\n }\n };\n\n const onControl = (fn: (ev: ControlChannelEnvelope) => void): (() => void) =>\n ws.onControl((ev) => {\n if (ev.conversationId === conversationId) fn(ev);\n });\n\n // 回发命令(approval.* / interaction.answer)的 type 已纳入 StreamCommandType、protocolVersion\n // 收窄为 '3.0',StreamCommand 结构兼容 StreamCommandEnvelope,无需 as 转换。\n const sendCommand = (command: StreamCommand): void => ws.send(command);\n\n return { conversationId, commandId, events, cancel, onControl, sendCommand };\n}\n","/**\n * control-channel — 控制通道(channel='control')解析 + 回发命令构造。\n * 回发走 StreamCommandType(已坐实三方一致:后端 ConversationWebSocketHandler.dispatch /\n * 前端 useAIChat / 协议文档 stream-protocol.md §4),不用死枚举 control.interaction.decision。\n */\nimport { randomUUID } from 'node:crypto';\n\nexport interface InteractionRequest {\n kind: 'CONFIRM' | 'ASK';\n interactionId: string;\n summary: string;\n originAgentPath: string;\n ttlSeconds: number;\n // CONFIRM 专属\n riskLevel?: string;\n toolName?: string;\n argsPreview?: Record<string, unknown>;\n // ASK 专属\n question?: string;\n choices?: string[];\n}\n\nexport interface InteractionResolved {\n interactionId: string;\n phase: 'RESUMED' | 'TERMINAL' | string;\n}\n\nexport interface StreamCommand {\n protocolVersion: '3.0';\n commandId: string;\n conversationId: string;\n type:\n | 'approval.confirm'\n | 'approval.reject'\n | 'interaction.answer'\n // 流中途控制(P0 S-interrupt / S-steer):cooperative cancel / 注入下一 round SystemMessage\n | 'response.interrupt'\n | 'response.steer';\n timestamp: number;\n payload: Record<string, unknown>;\n}\n\nexport function parseInteractionRequest(payload: Record<string, unknown>): InteractionRequest {\n return {\n kind: payload.type as 'CONFIRM' | 'ASK',\n interactionId: payload.interactionId as string,\n summary: (payload.summary as string) || '',\n originAgentPath: (payload.originAgentPath as string) || '',\n ttlSeconds: (payload.ttlSeconds as number) || 0,\n riskLevel: payload.riskLevel as string | undefined,\n toolName: payload.toolName as string | undefined,\n argsPreview: payload.argsPreview as Record<string, unknown> | undefined,\n question: payload.question as string | undefined,\n choices: payload.choices as string[] | undefined,\n };\n}\n\nexport function parseInteractionResolved(payload: Record<string, unknown>): InteractionResolved {\n return {\n interactionId: payload.interactionId as string,\n phase: (payload.phase as string) || '',\n };\n}\n\n// timestamp 由调用点用 Date.now() 注入;测试只校验 type/conversationId/payload,不校验具体时间戳。\nfunction envelope(\n type: StreamCommand['type'],\n conversationId: string,\n payload: Record<string, unknown>,\n): StreamCommand {\n return { protocolVersion: '3.0', commandId: randomUUID(), conversationId, type, timestamp: Date.now(), payload };\n}\n\nexport function buildConfirm(confirmationId: string, conversationId: string): StreamCommand {\n return envelope('approval.confirm', conversationId, { confirmationId });\n}\n\nexport function buildReject(confirmationId: string, conversationId: string, reason?: string): StreamCommand {\n return envelope('approval.reject', conversationId, reason ? { confirmationId, reason } : { confirmationId });\n}\n\nexport function buildAnswer(\n interactionId: string,\n conversationId: string,\n answer: string,\n choiceIndex?: number,\n): StreamCommand {\n return envelope('interaction.answer', conversationId, { interactionId, answer, choiceIndex: choiceIndex ?? null });\n}\n\n/**\n * 流中途打断(cooperative cancel)。后端收到后停止当前 round,流以 ROOT message.stop\n * (payload.stopReason==\"interrupted\")终结。payload 为空对象 {}(后端契约已核实)。\n */\nexport function buildInterrupt(conversationId: string): StreamCommand {\n return envelope('response.interrupt', conversationId, {});\n}\n\n/**\n * 流中途引导(steer)。后端入队 content,下一 round 注入 SystemMessage;无任何 ack 事件\n * (v3 ack 已退役),流正常完成。payload 为 {content}(后端契约已核实)。\n */\nexport function buildSteer(content: string, conversationId: string): StreamCommand {\n return envelope('response.steer', conversationId, { content });\n}\n","/** 委派类工具名(对齐 Web constants.ts::isDelegationToolName)。hcm_handoff 为历史别名。 */\nconst DELEGATION_TOOLS = new Set(['hcm_delegate', 'hcm_handoff']);\nexport function isDelegationToolName(name: string): boolean {\n return DELEGATION_TOOLS.has(name);\n}\n","/**\n * v4-reducer — 复刻 hcm_nx_cloud useChatItemsStreamV4.ts::createV4Reducer 的 fold 状态机,\n * 但输出端换成 CLI 流式语义事件(V4RenderSink)。终端是 append-only 流,不能像 Web 那样\n * 回填 item,故 tool 行等 block.stop(args 齐)才发 onToolStarted。\n * 单层委派模型(与 Web 同限制):currentParentToolUseId 单值;多级嵌套靠 renderer depth counter。\n */\nimport { isDelegationToolName } from './delegation-constants';\nimport { truncateDisplay } from './display-width';\n\nexport interface V4RenderSink {\n onText(text: string, parentToolUseId: string | null): void;\n onThinkingDelta(text: string, parentToolUseId: string | null): void;\n onThinkingDone(parentToolUseId: string | null): void;\n onToolStarted(callId: string, toolName: string, argsPreview: string, parentToolUseId: string | null): void;\n /**\n * @param nextSteps G5 · 工具失败时从 result envelope 解析出的 ADR-058 路标(`error.nextSteps`\n * 数组 / 顶层 `next_step`)。成功或无路标时 undefined。供 host 在失败行下透出下一步指引。\n * @param toolName #1930 · 工具名。优先取 block.start 注册值;缓存短路 no-op 等无 block.start\n * 的事件由 reducer 从 result 文本兜底解析(如「hcm_describe_tool (缓存命中)」),避免 host\n * 裸显示 `?`。仍解析不出时 undefined,host 自行兜底。\n */\n onToolResult(\n callId: string,\n success: boolean,\n durMs: number,\n childConversationId: string | null,\n parentToolUseId: string | null,\n nextSteps?: string[],\n toolName?: string,\n ): void;\n onContentPart(part: Record<string, unknown>, parentToolUseId: string | null): void;\n /**\n * 用户中途纠偏在轮界被真正注入(v4 `steering.applied` 顶层事件,W1a-2)。\n * 仅注入时到达——客户端禁止在发 response.steer 时乐观本地显示「已纠偏」。\n */\n onSteeringApplied(contents: string[], round: number): void;\n onDelegationStart(agentKey: string, parentToolUseId: string): void;\n /**\n * 委派收口。由 hcm_delegate 自身的 tool.result 驱动(后端时序保证:子流 message.stop\n * 先到、父 tool.result 紧随其后,见 WebSocketRuntimeEventProjector.finishChildStreamIfAny)\n * ——因此能携带委派整体 success / 耗时 / 失败路标(2026-06-11 review C1/I1:旧版由子\n * message.stop 驱动拿不到 success,失败委派被静默渲染成功)。\n *\n * @param nextSteps 委派失败时的 ADR-058 路标;成功为 undefined。\n */\n onDelegationEnd(parentToolUseId: string, success: boolean, durMs: number, nextSteps?: string[]): void;\n /**\n * 委派「暂停待应答」(非失败):specialist 在子流内调 hcm_ask_user / 触发风险确认时,\n * 后端按 HALT 语义给 hcm_delegate 的 tool.result 发 transport `success=false`,但其\n * envelope 实为 `ok:true` + `INTERACTION_REQUIRED`/`CONFIRMATION_REQUIRED` 标记\n * (后端 InteractionSuspendMiddleware / RiskConfirmationMiddleware)。这不是失败——\n * 用户应答后委派会 RESUME(同 parentToolUseId 再来一段 message.start)续跑到真终态。\n *\n * 消费端据此:保留委派块不收口、累计本段耗时,等 RESUME 续同一块——避免把暂停\n * 渲染成红色「委派失败」、并避免暂停→续跑被计为两次委派(CLI append-only 无法回填)。\n * @param durMs 暂停前本段执行耗时(累计进委派块总耗时)。\n */\n onDelegationPaused(parentToolUseId: string, durMs: number): void;\n onTerminal(stopReason: string, tokens: number): void;\n}\n\n/**\n * 判定一条 hcm_delegate 的 tool.result(transport `success=false`)实为「暂停待应答」而非失败。\n *\n * 后端 wire 接缝不一致:HALT 时 executor 结果 `success=true`+`haltConversation`,但投影到\n * delegate 的 tool.result `success` 字段为 false;其 resultData envelope 仍诚实保留 `ok:true`\n * + summary/data 带 `INTERACTION_REQUIRED`(ASK)/`CONFIRMATION_REQUIRED`(CONFIRM)标记。\n * 以「envelope ok===true 且含交互标记」为准绳——纯结构信号,不误伤真失败(真失败 ok:false)。\n */\nexport function detectDelegationPause(resultData: unknown): boolean {\n if (!resultData || typeof resultData !== 'object') return false;\n const rd = resultData as Record<string, unknown>;\n let env: Record<string, unknown> | null = null;\n const raw = rd.result;\n if (typeof raw === 'string') {\n try {\n const o = JSON.parse(raw);\n env = o && typeof o === 'object' ? (o as Record<string, unknown>) : null;\n } catch {\n env = null;\n }\n } else if (raw && typeof raw === 'object') {\n env = raw as Record<string, unknown>;\n } else {\n env = rd;\n }\n if (!env || env.ok !== true) return false;\n const text = [env.summary, env.data].filter((x) => typeof x === 'string').join(' ');\n return text.includes('INTERACTION_REQUIRED') || text.includes('CONFIRMATION_REQUIRED');\n}\n\n/**\n * #1930 · 取 tool.result 的原始结果文本(用于无 envelope 的纯字符串结果分类)。\n *\n * wire 形态 {@code resultData = { result: \"<string>\" }}(WS ToolResultPayload);也兼容\n * resultData 本身即字符串。非字符串结果(结构化对象)返回空串——这些走正常 envelope 路径,\n * 不属本函数关心的「控制信号 / no-op 短路」纯文本类。\n */\nfunction resultText(resultData: unknown): string {\n if (typeof resultData === 'string') return resultData;\n if (resultData && typeof resultData === 'object') {\n const raw = (resultData as Record<string, unknown>).result;\n if (typeof raw === 'string') return raw;\n }\n return '';\n}\n\n/**\n * #1930 Bug B · 判定一条**直接工具**(非委派)的 tool.result 实为「待确认 / 待应答」控制信号,\n * 而非工具执行结果。\n *\n * 后端写动作命中确认门 / 交互门时,会先发一条 transport `success=false` 的 TOOL_COMPLETED,其\n * result 是纯文本前缀 {@code \"CONFIRMATION_REQUIRED: ...\"} / {@code \"INTERACTION_REQUIRED: ...\"}\n * (非 JSON envelope),同时另发一条 {@code control.interaction.request} 让 host 弹确认卡。该 tool.result\n * 对展示流是冗余的——若按普通工具渲染会变成红色 `⚙ ? ✗ 0ms` 假失败并计入失败统计(实测用户困惑)。\n *\n * 以「结果文本以这两个标记**起始**」为准绳(强协议信号,正常工具结果不会以此开头),故 host 可安全\n * 抑制该行,把待确认态交给 confirm 卡承载。委派场景的同类暂停由 {@link detectDelegationPause} 处理,\n * 二者互不重叠(委派结果是 envelope,直接工具是纯文本前缀)。\n */\nexport function detectDirectInteractionPending(resultData: unknown): boolean {\n const text = resultText(resultData).trimStart();\n return text.startsWith('CONFIRMATION_REQUIRED') || text.startsWith('INTERACTION_REQUIRED');\n}\n\n/**\n * #1930 Bug A · 为无 block.start 注册名的 tool.result 从结果文本兜底解析有意义的展示名。\n *\n * 当前唯一已知来源:ADR-095 catalog 缓存短路 no-op——后端直接发 TOOL_COMPLETED(无 TOOL_STARTED),\n * result 为 {@code \"[ADR-095 catalog cache hit] 工具 <toolName> 已在本 sub-session ...\"}。解析出工具名\n * 时返回 {@code \"<toolName> (缓存命中)\"},解析不出则返回 {@code \"缓存命中\"}。其他结果返回 undefined\n * (host 保留原 `?` 兜底给真未知事件,不强行编造名字)。\n */\nexport function deriveNamelessToolLabel(resultData: unknown): string | undefined {\n const text = resultText(resultData);\n if (text.includes('catalog cache hit')) {\n const m = text.match(/工具\\s+(\\w+)/);\n return m ? `${m[1]} (缓存命中)` : '缓存命中';\n }\n return undefined;\n}\n\n/**\n * #1949 · 从确认门 / 交互门占位结果文本解析被挂起的工具名。\n *\n * 后端 {@code RiskConfirmationMiddleware} 挂起时发的占位 TOOL_COMPLETED,其 result 为纯文本\n * {@code \"CONFIRMATION_REQUIRED: 等待用户确认 tool=<name>, confirmationId=..., scopeKey=...\"}\n * (INTERACTION_REQUIRED 亦可能带 {@code tool=})。用户确认后被 resume 的工具,其真结果\n * {@code tool.result} 可能先于(或缺失)resume 段 {@code block.start} 抵达消费端 → {@code toolNameByCallId}\n * 无注册名 → 裸显示 `?`(#1949)。这里在抑制占位结果时解析出工具名,供 resume 真结果按 callId 兜底取名。\n * 解析不出返回 undefined。\n */\nexport function parseConfirmToolName(resultData: unknown): string | undefined {\n const m = resultText(resultData).match(/\\btool=([^,\\s]+)/);\n return m ? m[1] : undefined;\n}\n\n/**\n * G5 · 从 tool.result 的 resultData 防御性提取 ADR-058 `next_step` 路标。\n *\n * 后端 wire:`resultData = { result: \"<envelope JSON 字符串>\" }`,失败 envelope 形如\n * `{ ok:false, error:{ code, message, nextSteps:[...] } }`;部分执行器平铺顶层 `next_step`\n * (可为 string / string[] / {message,...} map)。这里兼容多形态,任一解析失败都静默返回\n * undefined(非 JSON 纯文本 result 不炸)。\n */\nexport function extractNextSteps(resultData: unknown): string[] | undefined {\n if (!resultData || typeof resultData !== 'object') return undefined;\n const rd = resultData as Record<string, unknown>;\n // wire 形态:{ result: \"<json string>\" };也兼容 resultData 本身即结构化对象。\n let env: Record<string, unknown> | null = null;\n const raw = rd.result;\n if (typeof raw === 'string') {\n try {\n const o = JSON.parse(raw);\n env = o && typeof o === 'object' ? (o as Record<string, unknown>) : null;\n } catch {\n env = null;\n }\n } else if (raw && typeof raw === 'object') {\n env = raw as Record<string, unknown>;\n } else {\n env = rd;\n }\n if (!env) return undefined;\n\n const collect = (v: unknown): string[] => {\n if (Array.isArray(v)) return v.map((x) => String(x)).filter((s) => s.length > 0);\n if (typeof v === 'string' && v.length > 0) return [v];\n if (v && typeof v === 'object') {\n const m = (v as Record<string, unknown>).message;\n if (typeof m === 'string' && m.length > 0) return [m];\n }\n return [];\n };\n\n const error = env.error;\n const fromError =\n error && typeof error === 'object'\n ? collect((error as Record<string, unknown>).nextSteps).concat(\n collect((error as Record<string, unknown>).next_step),\n )\n : [];\n const fromTop = collect(env.nextSteps).concat(collect(env.next_step));\n const all = [...fromError, ...fromTop];\n return all.length > 0 ? all : undefined;\n}\n\ninterface V4BlockMeta {\n blockType: string;\n toolName?: string;\n callId?: string;\n parentToolUseId: string | null;\n jsonBuffer: string;\n}\n\nexport function createV4Reducer(sink: V4RenderSink) {\n const blockMap = new Map<number, V4BlockMeta>();\n const toolNameByCallId = new Map<string, string>();\n // #1949 · 确认门通过后被 resume 的直接工具:其 resume 段 block.start 可能未抵达消费端(只到\n // tool.result)→ toolNameByCallId 无注册名 → 裸显示 `?`。挂起时占位结果文本带 `tool=<name>`,\n // 在抑制该占位结果时按 callId 暂存,供 resume 真结果兜底取名(见 tool.result case)。\n const confirmToolNameByCallId = new Map<string, string>();\n // 嵌套委派归属栈(2026-06-11 review I2):旧单值 currentParent 在 d1 子流内再委派 d2 时,\n // d2 的 message.stop 会把归属误归零 → d1 剩余子流文本被当主流程平铺。栈语义:push on\n // 子 message.start,pop on 子 message.stop,currentParent 恒为栈顶。\n const parentStack: string[] = [];\n let currentParent: string | null = null;\n\n const parentOf = (payloadParent?: unknown): string | null =>\n (typeof payloadParent === 'string' && payloadParent) ? payloadParent : currentParent;\n\n // 按显示宽度截断(CJK=2 列):单 value ≤40 列防 reasoning 类长参数霸行,总预览 ≤96 列\n // 保证「⚙ name preview ✓ Xms」在 120 列内单行收口(旧按字符数 slice(120) 中文实占 240 列必折行)。\n function buildArgsPreview(args: Record<string, unknown>): string {\n const preview = Object.entries(args)\n .map(([k, v]) => {\n const raw = typeof v === 'object' ? JSON.stringify(v) : String(v);\n return `${k}=${truncateDisplay(raw, 40)}`;\n })\n .join(' · ');\n return truncateDisplay(preview, 96);\n }\n function parseJson(s: string): Record<string, unknown> | null {\n try { const o = JSON.parse(s); return o && typeof o === 'object' ? o : null; } catch { return null; }\n }\n\n function handle(type: string, payload: Record<string, unknown>): boolean {\n switch (type) {\n case 'message.start': {\n const p = payload.parentToolUseId;\n if (typeof p === 'string' && p) {\n parentStack.push(p);\n currentParent = p;\n sink.onDelegationStart((payload.agentKey as string) || 'specialist', p);\n } else {\n // ROOT message:归属复位(resume 等场景防御性清栈)。\n parentStack.length = 0;\n currentParent = null;\n }\n return true;\n }\n case 'block.start': {\n const index = payload.index as number;\n const blockType = (payload.blockType as string) || 'text';\n const toolName = (payload.toolName as string) || undefined;\n const callId = (payload.callId as string) || undefined;\n blockMap.set(index, { blockType, toolName, callId, parentToolUseId: parentOf(payload.parentToolUseId), jsonBuffer: '' });\n if (blockType === 'tool_use' && callId && toolName) toolNameByCallId.set(callId, toolName);\n // CLI:tool 行等 block.stop(args 齐)才发 onToolStarted;此处不输出。\n return true;\n }\n case 'block.delta': {\n const index = payload.index as number;\n const meta = blockMap.get(index);\n const parent = meta?.parentToolUseId ?? currentParent;\n switch (payload.deltaType) {\n case 'text_delta': sink.onText((payload.text as string) || '', parent); return true;\n case 'thinking_delta': sink.onThinkingDelta((payload.text as string) || '', parent); return true;\n case 'input_json_delta': if (meta) meta.jsonBuffer += (payload.partialJson as string) || ''; return true;\n case 'content_part': if (meta) meta.jsonBuffer += (payload.partialJson as string) || ''; return true;\n default: return true;\n }\n }\n case 'block.stop': {\n const index = payload.index as number;\n const meta = blockMap.get(index);\n blockMap.delete(index);\n if (!meta) return true;\n if (meta.blockType === 'thinking') { sink.onThinkingDone(meta.parentToolUseId); return true; }\n if (meta.blockType === 'tool_use' && meta.callId) {\n const args = meta.jsonBuffer ? parseJson(meta.jsonBuffer) ?? {} : {};\n if (meta.toolName && isDelegationToolName(meta.toolName)) return true; // delegation 由 message.start / tool.result 渲染\n sink.onToolStarted(meta.callId, meta.toolName || '?', buildArgsPreview(args), meta.parentToolUseId);\n return true;\n }\n if (meta.blockType === 'content_part' && meta.jsonBuffer) {\n const part = parseJson(meta.jsonBuffer)?.part;\n if (part && typeof part === 'object') {\n const p = part as Record<string, unknown>;\n // fileDownload content_part({fileName, downloadUrl})→ 渲染为可点的绝对签名链接,\n // 点击由浏览器打开(ADR-140,免 Bearer);CLI 不再自动落盘(用户要求点击打开即可)。\n sink.onContentPart(p, meta.parentToolUseId);\n }\n return true;\n }\n return true;\n }\n case 'tool.result': {\n const callId = payload.callId as string;\n if (!callId) return true;\n const toolName = toolNameByCallId.get(callId);\n const success = payload.success !== false;\n // G5:失败才解析 next_step 路标(成功路径不解析,省开销)。\n const nextSteps = success ? undefined : extractNextSteps(payload.resultData);\n if (toolName && isDelegationToolName(toolName)) {\n // 委派收口在此驱动(review C1/I1):本事件带委派整体 success/durMs/失败路标,\n // 失败委派不再被静默渲染成功。子流 message.stop 仅做归属弹栈(见 message.stop case)。\n const durMs = (payload.executionTimeMs as number) || 0;\n // transport success=false 但实为「暂停待应答」(envelope ok:true + 交互标记)→ 非失败,\n // 走 onDelegationPaused 保留委派块等 RESUME 续跑;真失败仍走 onDelegationEnd。\n if (!success && detectDelegationPause(payload.resultData)) {\n // 🔴 不 delete toolNameByCallId[callId]:RESUME 后的「真收口」tool.result(同 callId)\n // 仍须被识别为委派——否则它退化成普通 onToolResult,渲染成神秘「⚙ ? ✓ 0ms」工具行。\n sink.onDelegationPaused(callId, durMs);\n } else {\n toolNameByCallId.delete(callId);\n sink.onDelegationEnd(callId, success, durMs, nextSteps);\n }\n return true;\n }\n toolNameByCallId.delete(callId);\n // #1930 Bug B:直接工具的「待确认 / 待应答」控制信号(CONFIRMATION_REQUIRED /\n // INTERACTION_REQUIRED 纯文本前缀)不是工具结果——它对应一条 control.interaction.request,\n // 由 confirm 卡承载可见待确认态。这里抑制,避免渲染成红色 `⚙ ? ✗ 0ms` 假失败 + 计入失败统计。\n if (detectDirectInteractionPending(payload.resultData)) {\n // #1949 · 暂存被挂起工具名,供其被 resume 后的真结果兜底取名(resume 段 block.start\n // 可能未抵达消费端 → 否则 resume 工具行裸显示 `?`)。\n const confirmName = parseConfirmToolName(payload.resultData);\n if (confirmName) confirmToolNameByCallId.set(callId, confirmName);\n return true;\n }\n // #1930 Bug A + #1949:名字优先取 block.start 注册值;缺失时取确认门暂存名(resume 直接工具);\n // 再退回结果文本兜底(缓存短路 no-op);都没有才让 host 显示 `?`。\n const resolvedToolName =\n toolName ?? confirmToolNameByCallId.get(callId) ?? deriveNamelessToolLabel(payload.resultData);\n confirmToolNameByCallId.delete(callId);\n sink.onToolResult(\n callId,\n success,\n (payload.executionTimeMs as number) || 0,\n (payload.childConversationId as string) || null,\n parentOf(payload.parentToolUseId),\n nextSteps,\n resolvedToolName,\n );\n return true;\n }\n case 'steering.applied': {\n const contents = Array.isArray(payload.contents)\n ? (payload.contents as unknown[]).map((c) => String(c)).filter((c) => c.length > 0)\n : [];\n if (contents.length > 0) {\n sink.onSteeringApplied(contents, (payload.round as number) ?? 0);\n }\n return true;\n }\n case 'message.stop': {\n const p = payload.parentToolUseId;\n if (typeof p === 'string' && p) {\n // 子流结束:仅弹栈回外层归属(嵌套委派 d1>d2 时 d2 结束回 d1)。收口渲染由\n // 紧随其后的 delegation tool.result 驱动(带 success,见 tool.result case)。\n const idx = parentStack.lastIndexOf(p);\n if (idx >= 0) parentStack.splice(idx, 1);\n currentParent = parentStack[parentStack.length - 1] ?? null;\n return true;\n }\n parentStack.length = 0;\n currentParent = null;\n blockMap.clear();\n sink.onTerminal((payload.stopReason as string) || 'end_turn', (payload.totalTokens as number) || 0);\n return true;\n }\n default: return false;\n }\n }\n\n return { handle };\n}\n","import type { WsClient } from './ws-client';\nimport { sendMessageAndStream, type SendMessageOpts } from './stream-iterator';\nimport type { StreamEventEnvelope, ResponseFailedPayload } from './protocol';\nimport { createV4Reducer, type V4RenderSink } from './v4-reducer';\nimport { CliError, CliErrorCode } from '../errors';\n\nexport interface OneShotToolCall {\n /** 工具名;委派为 'hcm_delegate'。无法解析时 '?'。 */\n name: string;\n /** 参数预览(reducer 已按显示宽度截断);委派时为目标 agentKey。 */\n argsPreview: string;\n success: boolean;\n elapsedMs: number;\n /** 委派调用标记。 */\n isDelegation?: boolean;\n /** 归属父 tool_use(顶层直接工具为 null)。 */\n parentToolUseId: string | null;\n}\n\nexport interface OneShotResult {\n conversationId: string;\n streamId: string;\n traceId: string | null;\n /** 最终助手回答正文(ROOT 段文本;委派 specialist 的中间文本不计入)。 */\n content: string;\n toolCalls: OneShotToolCall[];\n elapsedMs: number;\n events: StreamEventEnvelope[];\n /** 终态原因(v4 message.stop.stopReason);未见终态时 undefined。 */\n stopReason?: string;\n /** 本轮总 token(v4 message.stop.totalTokens)。 */\n tokens?: number;\n}\n\nexport interface OneShotOpts extends SendMessageOpts {\n collectEvents?: boolean;\n}\n\n/**\n * 非流式单发:消费 v4 事件流并聚合出最终结果。\n *\n * 🔴 与流式路径同源:把事件喂进 {@link createV4Reducer}(与 CLI streaming、Web 端 fold\n * 状态机一致的 v4 语义层),用一个 headless sink 累加 ROOT 文本 + 收集工具调用——而非\n * 手搓第二套聚合。历史 bug:旧实现监听已废弃的 v3 事件名(response.part.delta /\n * response.completed / response.tool.*),后端早改发 v4(block.delta:text_delta /\n * message.stop)→ switch 全不命中 → content 恒空(--no-stream / --output json 拿到空答案)。\n * 语义层复用见 ADR-129 D2:只换「画」的层,聚合逻辑不重复实现。\n *\n * 终态由 stream-iterator 在 ROOT message.stop(或断连)关闭 async iterator 驱动,本函数\n * 无需自判终止。v3 残余 `response.failed`(仅老路径出现)仍显式抛 CliError 保留旧语义。\n */\nexport async function oneShot(\n ws: WsClient,\n prompt: string,\n opts: OneShotOpts = {},\n): Promise<OneShotResult> {\n const t0 = Date.now();\n const { conversationId, events } = await sendMessageAndStream(ws, prompt, opts);\n\n let content = '';\n let streamId = '';\n let traceId: string | null = null;\n let stopReason: string | undefined;\n let tokens: number | undefined;\n const toolCalls: OneShotToolCall[] = [];\n const collected: StreamEventEnvelope[] = [];\n\n // reducer sink 无 block.start→tool.result 的 callId 内联参数,故按 callId 暂存预览 /\n // 委派 agentKey,待 result / delegationEnd 到达时回填。\n const argsPreviewByCallId = new Map<string, string>();\n const agentKeyByCallId = new Map<string, string>();\n\n const sink: V4RenderSink = {\n // ROOT 段文本才是最终回答;specialist 子流文本(parent 非空)是中间产物,不计入 content。\n onText(text, parentToolUseId) {\n if (parentToolUseId === null) content += text;\n },\n onThinkingDelta() {},\n onThinkingDone() {},\n onToolStarted(callId, _toolName, argsPreview) {\n argsPreviewByCallId.set(callId, argsPreview);\n },\n onToolResult(callId, success, durMs, _childConversationId, parentToolUseId, _nextSteps, toolName) {\n toolCalls.push({\n name: toolName ?? '?',\n argsPreview: argsPreviewByCallId.get(callId) ?? '',\n success,\n elapsedMs: durMs,\n parentToolUseId,\n });\n argsPreviewByCallId.delete(callId);\n },\n onContentPart() {},\n onSteeringApplied() {},\n onDelegationStart(agentKey, parentToolUseId) {\n agentKeyByCallId.set(parentToolUseId, agentKey);\n },\n onDelegationEnd(callId, success, durMs) {\n toolCalls.push({\n name: 'hcm_delegate',\n argsPreview: agentKeyByCallId.get(callId) ?? '',\n success,\n elapsedMs: durMs,\n isDelegation: true,\n parentToolUseId: null,\n });\n agentKeyByCallId.delete(callId);\n },\n onDelegationPaused() {},\n onTerminal(reason, tk) {\n stopReason = reason;\n tokens = tk;\n },\n };\n\n const reducer = createV4Reducer(sink);\n\n for await (const ev of events) {\n if (opts.collectEvents) collected.push(ev);\n if (!streamId) streamId = ev.streamId;\n if (!traceId) traceId = ev.traceId;\n\n // v3 残余终态:仅老后端路径出现,v4 无此事件。保留旧语义——失败即抛。\n if (ev.type === 'response.failed') {\n throw new CliError({\n code: CliErrorCode.BUSINESS_RULE_VIOLATION,\n message: (ev.payload as ResponseFailedPayload).message,\n traceId: traceId ?? undefined,\n });\n }\n\n reducer.handle(ev.type, ev.payload as Record<string, unknown>);\n }\n\n return {\n conversationId,\n streamId,\n traceId,\n content,\n toolCalls,\n elapsedMs: Date.now() - t0,\n events: collected,\n stopReason,\n tokens,\n };\n}\n","import * as fs from 'node:fs/promises';\nimport { conversationStateFile, profileDir } from '../config/paths';\n\nexport interface ConversationState {\n lastConversationId?: string;\n lastStreamId?: string;\n lastTraceId?: string;\n lastAgentKey?: string;\n updatedAt: string;\n}\n\nexport async function loadConversationState(profile: string): Promise<ConversationState | null> {\n try {\n const raw = await fs.readFile(conversationStateFile(profile), 'utf-8');\n return JSON.parse(raw) as ConversationState;\n } catch (e) {\n if ((e as NodeJS.ErrnoException).code === 'ENOENT') return null;\n throw e;\n }\n}\n\nexport async function saveConversationState(\n profile: string,\n state: ConversationState,\n): Promise<void> {\n await fs.mkdir(profileDir(profile), { recursive: true });\n await fs.writeFile(\n conversationStateFile(profile),\n JSON.stringify({ ...state, updatedAt: new Date().toISOString() }, null, 2),\n );\n}\n","import type { AxiosInstance } from 'axios';\n\n/** Default analyst agent id (seeded in HCM at tenant=__SYSTEM__). */\nexport const DEFAULT_AGENT_ID = '019cca00-0000-7000-0000-000000000001';\n\nexport interface CreatedConversation {\n id: string;\n agentId: string;\n title?: string;\n}\n\n/**\n * Create a new conversation in HCM. Required before sending any /ws/conversation\n * message — the WS handler looks up conversation→agentId in DB.\n */\nexport async function createConversation(\n http: AxiosInstance,\n agentId: string = DEFAULT_AGENT_ID,\n): Promise<CreatedConversation> {\n const resp = await http.post('/api/models/ConversationModel', { agentId });\n return {\n id: resp.data.id,\n agentId: resp.data.agentId,\n title: resp.data.title,\n };\n}\n","import type { AxiosInstance } from 'axios';\nimport { truncateDisplay } from './display-width';\n\n/**\n * 续会话回放(CLI 做透轮刀⑤,对标 Claude Code resume 的上下文可见性)。\n *\n * hcm chat 只有显式 --resume 续接 conversation 时才回放历史;否则默认开新会话。\n * 本模块拉\n * timeline(GET /api/models/ConversationModel/{id}/action/timeline,与 Web 回放\n * 同源)折出轻量摘要:最近 N 轮 user/assistant 文本 + 更早轮次计数。\n */\n\n/** timeline 条目(后端 TimelineItemDto 的 CLI 侧投影,只取回放需要的字段)。 */\nexport interface TimelineItemLite {\n kind: string;\n seq: number;\n /** user-msg / steering 的原文。 */\n text?: string | null;\n /** chat-text 的正文。 */\n content?: string | null;\n /** user-msg 附件元数据(#1849 journal 单源透传;issue #64 回放 📎)。 */\n attachments?: Array<{ fileName?: string }> | null;\n}\n\nexport interface ResumeTurn {\n role: 'user' | 'assistant';\n text: string;\n}\n\nexport interface ResumeSummary {\n conversationId: string;\n /** 会话总轮数(user-msg 条目数)。 */\n totalRounds: number;\n /** 最近 maxRounds 轮的 user/assistant 文本(已按显示宽度截断为单行摘要)。 */\n recent: ResumeTurn[];\n}\n\nfunction normalizeTimelineItems(body: unknown): TimelineItemLite[] | null {\n if (Array.isArray(body)) return body as TimelineItemLite[];\n if (\n body\n && typeof body === 'object'\n && Array.isArray((body as { data?: unknown }).data)\n ) {\n return (body as { data: TimelineItemLite[] }).data;\n }\n return null;\n}\n\n/**\n * 拉取 conversation timeline。任何失败(网络/404/结构异常)返回 null——\n * 回放是体验增强,绝不阻断聊天主流程。\n */\nexport async function fetchConversationTimeline(\n http: AxiosInstance,\n conversationId: string,\n): Promise<TimelineItemLite[] | null> {\n try {\n const resp = await http.get(\n `/api/models/ConversationModel/${conversationId}/action/timeline`);\n return normalizeTimelineItems(resp.data);\n } catch {\n return null;\n }\n}\n\n/**\n * 严格拉取 conversation timeline。用于 CLI 显式 inspect 场景:网络/404/结构异常都应暴露,\n * 不能像 resume 体验增强那样静默吞掉。\n */\nexport async function fetchConversationTimelineStrict(\n http: AxiosInstance,\n conversationId: string,\n): Promise<TimelineItemLite[]> {\n const resp = await http.get(\n `/api/models/ConversationModel/${conversationId}/action/timeline`);\n const items = normalizeTimelineItems(resp.data);\n if (!items) {\n throw new Error(`Conversation timeline response is not an array: ${conversationId}`);\n }\n return items;\n}\n\n/**\n * timeline → 续会话摘要。取最近 maxRounds 轮:每轮 = user-msg 原文 + 该轮最后一条\n * chat-text 正文(中间 tools/delegation/steering 等过程条目不进摘要——回顾要的是\n * 「聊到哪了」不是过程重放)。文本截为单行(换行折叠 + 显示宽度截断)。\n */\nexport function buildResumeSummary(\n conversationId: string,\n items: TimelineItemLite[],\n maxRounds: number = 2,\n maxWidth: number = 76,\n): ResumeSummary | null {\n if (!Array.isArray(items) || items.length === 0) return null;\n // 按 user-msg 切轮:每个 user-msg 开启一轮,轮内最后一条 chat-text 是该轮 assistant 收尾。\n interface Round { user: string; assistant: string | null }\n const rounds: Round[] = [];\n for (const it of items) {\n if (it.kind === 'user-msg' && it.text && it.text.trim()) {\n const atts = (it.attachments ?? []).map((a) => a?.fileName).filter(Boolean);\n const suffix = atts.length ? ` 📎 ${atts.join(' · ')}` : '';\n rounds.push({ user: it.text + suffix, assistant: null });\n } else if (it.kind === 'chat-text' && it.content && it.content.trim() && rounds.length > 0) {\n const last = rounds[rounds.length - 1];\n if (last) last.assistant = it.content;\n }\n }\n if (rounds.length === 0) return null;\n\n const oneLine = (s: string): string =>\n truncateDisplay(s.replace(/\\s*\\n\\s*/g, ' ').trim(), maxWidth);\n\n const recent: ResumeTurn[] = [];\n for (const r of rounds.slice(-maxRounds)) {\n recent.push({ role: 'user', text: oneLine(r.user) });\n if (r.assistant) recent.push({ role: 'assistant', text: oneLine(r.assistant) });\n }\n return { conversationId, totalRounds: rounds.length, recent };\n}\n\nexport function findLastAssistantSeq(items: TimelineItemLite[] | null | undefined): number | null {\n const last = [...(items ?? [])].reverse()\n .find((it) => it.kind === 'chat-text' && Number.isInteger(it.seq));\n return last?.seq ?? null;\n}\n","import type { AxiosInstance } from 'axios';\nimport { query } from '../models/query';\n\nexport interface ConversationListItem {\n id?: string;\n title?: string | null;\n summary?: string | null;\n agentId?: string | null;\n messageCount?: number | null;\n totalTokens?: number | null;\n lastActiveAt?: string | null;\n lastMessagePreview?: string | null;\n createTime?: string | null;\n updateTime?: string | null;\n}\n\nexport interface ConversationListResult {\n rows: ConversationListItem[];\n total?: number;\n}\n\nexport async function fetchRecentConversations(\n http: AxiosInstance,\n limit: number = 10,\n): Promise<ConversationListResult> {\n const result = await query<ConversationListItem>(http, 'ConversationModel', {\n filter: { origin: 'USER_CHAT', archived: false },\n fields: [\n 'id',\n 'title',\n 'summary',\n 'agentId',\n 'messageCount',\n 'totalTokens',\n 'lastActiveAt',\n 'lastMessagePreview',\n 'createTime',\n 'updateTime',\n ],\n sort: [{ field: 'lastActiveAt', direction: 'desc' }],\n limit,\n });\n return { rows: result.rows, total: result.total };\n}\n","export const MINI_CONTEXT_FILE = 'hcm.context.json';\nexport const MINI_STATE_DIR = '.hcm';\nexport const MINI_PULL_MANIFEST_FILE = 'mini-manifest.json';\nexport const MINI_REMOTE_ROOT = 'mini_apps';\nexport const MINI_CONTEXT_KIND = 'hcm-mini-app';\nexport const MINI_CONTEXT_VERSION = 1;\n\nexport type MiniSurface = 'embedded' | 'standalone';\nexport type MiniTemplateKind = 'dashboard' | 'multi-page' | 'writable-workbench';\n\nexport interface WorkspaceFileItem {\n id: string;\n workspaceKey: string;\n relativePath: string;\n fileType?: string;\n fileSize?: number;\n lastModified?: number | string | null;\n}\n\nexport interface WorkspaceFileContent {\n workspaceKey: string;\n relativePath: string;\n content: string;\n fileType?: string;\n}\n\nexport interface WorkspaceFileListResult {\n data: WorkspaceFileItem[];\n total: number;\n}\n\nexport interface MiniProjectContext {\n kind: typeof MINI_CONTEXT_KIND;\n version: typeof MINI_CONTEXT_VERSION;\n appCode: string;\n workspaceKey: string;\n remotePrefix: string;\n tenantId?: string;\n endpoint?: string;\n profile?: string;\n updatedAt: string;\n}\n\nexport interface MiniFileSnapshot {\n localPath: string;\n remotePath: string;\n fileId?: string;\n sha256: string;\n size: number;\n lastModified?: number | string | null;\n}\n\nexport interface MiniPullManifest {\n version: typeof MINI_CONTEXT_VERSION;\n appCode: string;\n workspaceKey: string;\n remotePrefix: string;\n profile?: string;\n pulledAt: string;\n files: MiniFileSnapshot[];\n}\n\nexport interface MiniLocalFile {\n localPath: string;\n absolutePath: string;\n content: string;\n sha256: string;\n size: number;\n}\n\nexport type MiniLocalStatus = 'added' | 'modified' | 'deleted' | 'unchanged';\n\nexport interface MiniStatusEntry {\n localPath: string;\n remotePath: string;\n status: MiniLocalStatus;\n fileId?: string;\n sha256?: string;\n baseSha256?: string;\n}\n\nexport interface MiniValidationResult {\n valid: boolean;\n appCode?: string;\n workspaceKey?: string;\n errors: string[];\n warnings: string[];\n}\n\nexport interface MiniPullResult {\n appCode: string;\n workspaceKey: string;\n targetDir: string;\n files: MiniFileSnapshot[];\n}\n\nexport type MiniPushOperation = 'create' | 'update' | 'delete' | 'skip';\n\nexport interface MiniPushPlanItem {\n operation: MiniPushOperation;\n localPath: string;\n remotePath: string;\n fileId?: string;\n reason?: string;\n}\n\nexport interface MiniPushConflict {\n localPath: string;\n remotePath: string;\n reason: string;\n}\n\nexport interface MiniPushResult {\n appCode: string;\n workspaceKey: string;\n dryRun: boolean;\n operations: MiniPushPlanItem[];\n conflicts: MiniPushConflict[];\n}\n\nexport interface MiniPreviewTarget {\n key: 'bare' | 'standalone' | 'embedded';\n name: string;\n url: string;\n}\n\nexport type MiniSmokeTargetKind = 'preview' | 'manifest' | 'asset';\n\nexport interface MiniSmokeTargetResult {\n kind: MiniSmokeTargetKind;\n surface?: MiniPreviewTarget['key'];\n url: string;\n status: number | null;\n ok: boolean;\n error?: string;\n}\n\nexport interface MiniSmokeResult {\n ok: boolean;\n checked: number;\n passed: number;\n failed: number;\n targets: MiniSmokeTargetResult[];\n}\n\nexport type MiniVerifyNextAction =\n | 'push'\n | 'fix-validation'\n | 'resolve-conflict'\n | 'inspect-status'\n | 'inspect-preview';\n\nexport interface MiniVerifyPlan {\n operations: MiniPushPlanItem[];\n conflicts: MiniPushConflict[];\n}\n\nexport interface MiniVerifyReport {\n ok: boolean;\n validate: MiniValidationResult;\n status: MiniStatusEntry[];\n plan: MiniVerifyPlan | null;\n planSkipped?: string;\n preview: MiniPreviewTarget[];\n smoke?: MiniSmokeResult | null;\n}\n\nexport interface MiniVerifySummary {\n validate: {\n ok: boolean;\n errors: number;\n warnings: number;\n appCode?: string;\n workspaceKey?: string;\n };\n status: {\n total: number;\n changed: number;\n added: number;\n modified: number;\n deleted: number;\n unchanged: number;\n };\n dryRun: {\n ok: boolean;\n skipped: boolean;\n skippedReason?: string;\n operations: number;\n creates: number;\n updates: number;\n deletes: number;\n skips: number;\n conflicts: number;\n };\n preview: {\n ok: boolean;\n count: number;\n surfaces: MiniPreviewTarget['key'][];\n };\n}\n\nexport interface MiniVerifyChangedFile {\n path: string;\n status: MiniLocalStatus;\n remotePath: string;\n}\n\nexport interface MiniVerifyPreviewUrl {\n surface: MiniPreviewTarget['key'];\n name: string;\n url: string;\n}\n\nexport interface MiniVerifyBrowserAcceptance {\n recommendedUrl?: string;\n expectedTexts: string[];\n expectedNetwork: string[];\n console: {\n failOn: Array<'error' | 'warn'>;\n };\n}\n\nexport interface MiniVerifyAgentReport {\n ok: boolean;\n safeToPush: boolean;\n nextAction: MiniVerifyNextAction;\n summary: MiniVerifySummary;\n changedFiles: MiniVerifyChangedFile[];\n conflicts: MiniPushConflict[];\n previewUrls: MiniVerifyPreviewUrl[];\n browserAcceptance: MiniVerifyBrowserAcceptance;\n smoke: MiniSmokeResult | null;\n}\n","import type { AxiosInstance } from 'axios';\nimport type { WorkspaceFileContent, WorkspaceFileItem, WorkspaceFileListResult } from './types';\n\nconst MODEL = 'TenantExtensionWorkspaceFile';\n\nexport async function listWorkspaceFiles(\n http: AxiosInstance,\n workspaceKey: string,\n options: { pageSize?: number } = {},\n): Promise<WorkspaceFileListResult> {\n const resp = await http.get(`/api/models/${MODEL}`, {\n params: {\n state: 'workspace',\n filter: `workspaceKey:eq:${workspaceKey}`,\n page: 0,\n size: options.pageSize ?? 1000,\n order: 'relativePath:asc',\n },\n });\n const content = Array.isArray(resp.data?.content) ? resp.data.content : [];\n return {\n data: content.map(toWorkspaceFileItem),\n total: numberOr(resp.data?.totalElements, content.length),\n };\n}\n\nexport async function loadWorkspaceFileContent(\n http: AxiosInstance,\n fileId: string,\n): Promise<WorkspaceFileContent> {\n const resp = await http.get(`/api/models/${MODEL}/${encodeURIComponent(fileId)}/action/load-content`);\n return toWorkspaceFileContent(unwrapActionData(resp.data));\n}\n\nexport async function saveWorkspaceFileContent(\n http: AxiosInstance,\n fileId: string,\n content: string,\n): Promise<WorkspaceFileItem> {\n const resp = await http.post(\n `/api/models/${MODEL}/${encodeURIComponent(fileId)}/action/save-content`,\n { form: { content } },\n );\n return toWorkspaceFileItem(unwrapActionData(resp.data));\n}\n\nexport async function createWorkspaceFile(\n http: AxiosInstance,\n workspaceKey: string,\n relativePath: string,\n content: string,\n): Promise<WorkspaceFileItem> {\n const resp = await http.post(`/api/models/${MODEL}`, {\n workspaceKey,\n relativePath,\n content,\n });\n return toWorkspaceFileItem(resp.data);\n}\n\nexport async function deleteWorkspaceFile(http: AxiosInstance, fileId: string): Promise<void> {\n await http.delete(`/api/models/${MODEL}/${encodeURIComponent(fileId)}`);\n}\n\nfunction unwrapActionData(value: unknown): unknown {\n if (value && typeof value === 'object' && 'data' in value) {\n return (value as { data?: unknown }).data;\n }\n return value;\n}\n\nfunction toWorkspaceFileItem(value: unknown): WorkspaceFileItem {\n const obj = asRecord(value);\n return {\n id: stringOr(obj.id),\n workspaceKey: stringOr(obj.workspaceKey),\n relativePath: stringOr(obj.relativePath),\n fileType: stringOrUndefined(obj.fileType),\n fileSize: numberOr(obj.fileSize, 0),\n lastModified: primitiveOrNull(obj.lastModified),\n };\n}\n\nfunction toWorkspaceFileContent(value: unknown): WorkspaceFileContent {\n const obj = asRecord(value);\n return {\n workspaceKey: stringOr(obj.workspaceKey),\n relativePath: stringOr(obj.relativePath),\n content: stringOr(obj.content),\n fileType: stringOrUndefined(obj.fileType),\n };\n}\n\nfunction asRecord(value: unknown): Record<string, unknown> {\n return value && typeof value === 'object' ? (value as Record<string, unknown>) : {};\n}\n\nfunction stringOr(value: unknown): string {\n return typeof value === 'string' ? value : value == null ? '' : String(value);\n}\n\nfunction stringOrUndefined(value: unknown): string | undefined {\n return value == null ? undefined : stringOr(value);\n}\n\nfunction numberOr(value: unknown, fallback: number): number {\n return typeof value === 'number' && Number.isFinite(value) ? value : fallback;\n}\n\nfunction primitiveOrNull(value: unknown): number | string | null {\n if (typeof value === 'number' || typeof value === 'string') return value;\n return null;\n}\n","import * as crypto from 'node:crypto';\nimport type { Dirent } from 'node:fs';\nimport * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport type { AxiosInstance } from 'axios';\nimport { CliError, CliErrorCode } from '../errors';\nimport {\n createWorkspaceFile,\n deleteWorkspaceFile,\n listWorkspaceFiles,\n loadWorkspaceFileContent,\n saveWorkspaceFileContent,\n} from './workspace-files';\nimport {\n MINI_CONTEXT_FILE,\n MINI_CONTEXT_KIND,\n MINI_CONTEXT_VERSION,\n MINI_PULL_MANIFEST_FILE,\n MINI_REMOTE_ROOT,\n MINI_STATE_DIR,\n type MiniFileSnapshot,\n type MiniPushOperation,\n type MiniLocalFile,\n type MiniPreviewTarget,\n type MiniProjectContext,\n type MiniPullManifest,\n type MiniPullResult,\n type MiniSmokeResult,\n type MiniSmokeTargetResult,\n type MiniVerifyAgentReport,\n type MiniVerifyBrowserAcceptance,\n type MiniVerifyChangedFile,\n type MiniVerifyNextAction,\n type MiniVerifyPreviewUrl,\n type MiniVerifyReport,\n type MiniPushConflict,\n type MiniPushPlanItem,\n type MiniPushResult,\n type MiniStatusEntry,\n type MiniSurface,\n type MiniTemplateKind,\n type MiniValidationResult,\n type WorkspaceFileContent,\n type WorkspaceFileItem,\n} from './types';\nimport {\n buildMiniAppTemplateFiles as buildMiniAppTemplateFileSet,\n defaultMiniRequiredScopes,\n} from './templates';\n\nconst MINI_APP_CODE_PATTERN = /^[a-z][a-z0-9-]*$/;\nconst SCOPE_PATTERN = /^[A-Z][A-Za-z0-9_]*\\.[a-z][A-Za-z0-9]*$/;\nconst SEMVER_PATTERN =\n /^(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)(-[0-9A-Za-z.-]+)?(\\+[0-9A-Za-z.-]+)?$/;\nconst TOP_LEVEL_MANIFEST_KEYS = new Set([\n 'schemaVersion',\n 'code',\n 'name',\n 'version',\n 'sdkVersion',\n 'surfaces',\n 'defaultSurface',\n 'entry',\n 'bundleObjectRef',\n 'requiredScopes',\n]);\nconst ENTRY_KEYS = new Set(['html', 'css', 'js']);\nconst EXCLUDED_DIRS = new Set(['.git', MINI_STATE_DIR, 'node_modules', 'dist']);\nconst MINI_SDK_SCOPE_METHODS: Record<string, string> = {\n query: 'list',\n get: 'get',\n create: 'create',\n update: 'update',\n remove: 'delete',\n};\nconst MINI_MODEL_CONST_PATTERN = /\\bconst\\s+([A-Z][A-Za-z0-9_]*)\\s*=\\s*['\"]([A-Z][A-Za-z0-9_]*)['\"]\\s*;/g;\nconst MINI_SDK_MODEL_CALL_PATTERN =\n /\\bhcm\\.(query|get|create|update|remove)\\s*\\(\\s*\\{[\\s\\S]*?\\bmodel\\s*:\\s*(?:['\"]([A-Z][A-Za-z0-9_]*)['\"]|([A-Z][A-Za-z0-9_]*))/g;\nconst MINI_SDK_ACTION_CALL_PATTERN =\n /\\bhcm\\.action\\s*\\(\\s*\\{[\\s\\S]*?\\bmodel\\s*:\\s*(?:['\"]([A-Z][A-Za-z0-9_]*)['\"]|([A-Z][A-Za-z0-9_]*))[\\s\\S]*?\\baction\\s*:\\s*['\"]([a-z][A-Za-z0-9]*)['\"]/g;\n\nexport interface MiniInitArgs {\n appCode: string;\n workspaceKey: string;\n targetDir: string;\n name?: string;\n defaultSurface?: MiniSurface;\n templateKind?: MiniTemplateKind;\n writableModel?: string;\n writableFields?: string[] | null;\n writableDefaults?: Record<string, unknown>;\n writableTodayDefaultFields?: string[];\n requiredScopes?: string[];\n tenantId?: string;\n endpoint?: string;\n profile?: string;\n force?: boolean;\n}\n\nexport interface MiniPullArgs {\n http: AxiosInstance;\n appCode: string;\n workspaceKey: string;\n targetDir: string;\n tenantId?: string;\n endpoint?: string;\n profile?: string;\n force?: boolean;\n}\n\nexport interface MiniPushArgs {\n http: AxiosInstance;\n rootDir: string;\n dryRun?: boolean;\n}\n\nexport async function initMiniAppProject(args: MiniInitArgs): Promise<MiniPullResult> {\n const appCode = normalizeAppCode(args.appCode);\n const workspaceKey = normalizeWorkspaceKey(args.workspaceKey);\n const targetDir = path.resolve(args.targetDir);\n const remotePrefix = miniRemotePrefix(appCode);\n const templateKind = args.templateKind ?? 'dashboard';\n const files = buildMiniAppTemplateFiles({\n appCode,\n name: args.name,\n defaultSurface: args.defaultSurface ?? 'embedded',\n templateKind,\n writableModel: args.writableModel,\n writableFields: args.writableFields,\n writableDefaults: args.writableDefaults,\n writableTodayDefaultFields: args.writableTodayDefaultFields,\n requiredScopes: args.requiredScopes?.length\n ? args.requiredScopes\n : defaultMiniRequiredScopes(templateKind, { model: args.writableModel, fields: args.writableFields }),\n tenantId: args.tenantId ?? '1',\n });\n\n await ensureTargetWritable(\n targetDir,\n files.map((file) => file.localPath).concat([MINI_CONTEXT_FILE]),\n !!args.force,\n );\n await fs.mkdir(targetDir, { recursive: true });\n for (const file of files) {\n await writeText(path.join(targetDir, file.localPath), file.content);\n }\n\n const context: MiniProjectContext = {\n kind: MINI_CONTEXT_KIND,\n version: MINI_CONTEXT_VERSION,\n appCode,\n workspaceKey,\n remotePrefix,\n tenantId: args.tenantId,\n endpoint: args.endpoint,\n profile: args.profile,\n updatedAt: new Date().toISOString(),\n };\n await writeJson(path.join(targetDir, MINI_CONTEXT_FILE), context);\n\n const localFiles = await readLocalMiniFiles(targetDir);\n await writeMiniState(targetDir, context, [], new Map());\n return {\n appCode,\n workspaceKey,\n targetDir,\n files: localFiles.map((file) => toSnapshot(context, file)),\n };\n}\n\nexport async function pullMiniAppProject(args: MiniPullArgs): Promise<MiniPullResult> {\n const appCode = normalizeAppCode(args.appCode);\n const workspaceKey = normalizeWorkspaceKey(args.workspaceKey);\n const targetDir = path.resolve(args.targetDir);\n const remotePrefix = miniRemotePrefix(appCode);\n const remoteFiles = await loadRemoteMiniAppFiles(args.http, workspaceKey, appCode);\n if (!remoteFiles.length) {\n throw new CliError({\n code: CliErrorCode.BUSINESS_RULE_VIOLATION,\n message: `mini app '${appCode}' not found in workspace '${workspaceKey}'`,\n });\n }\n\n const pullFiles = remoteFiles\n .filter((file) => !isDirectoryMarkerPath(file.item.relativePath))\n .map((file) => ({\n localPath: localPathFromRemote(remotePrefix, file.item.relativePath),\n content: file.content.content,\n remote: file.item,\n }));\n await ensureTargetWritable(\n targetDir,\n pullFiles.map((file) => file.localPath).concat([MINI_CONTEXT_FILE]),\n !!args.force,\n );\n await fs.mkdir(targetDir, { recursive: true });\n for (const file of pullFiles) {\n await writeText(path.join(targetDir, file.localPath), file.content);\n }\n\n const context: MiniProjectContext = {\n kind: MINI_CONTEXT_KIND,\n version: MINI_CONTEXT_VERSION,\n appCode,\n workspaceKey,\n remotePrefix,\n tenantId: args.tenantId,\n endpoint: args.endpoint,\n profile: args.profile,\n updatedAt: new Date().toISOString(),\n };\n await writeJson(path.join(targetDir, MINI_CONTEXT_FILE), context);\n\n const localFiles = await readLocalMiniFiles(targetDir);\n const remoteByLocalPath = new Map(\n pullFiles.map((file) => [file.localPath, file.remote] as const),\n );\n await writeMiniState(targetDir, context, localFiles, remoteByLocalPath);\n const manifest = await readPullManifest(targetDir);\n return {\n appCode,\n workspaceKey,\n targetDir,\n files: manifest.files,\n };\n}\n\nexport async function getMiniStatus(rootDir: string): Promise<MiniStatusEntry[]> {\n const root = path.resolve(rootDir);\n const context = await readMiniContext(root);\n const manifest = await readPullManifest(root);\n const localFiles = await readLocalMiniFiles(root);\n return computeStatusEntries(context, manifest, localFiles);\n}\n\nexport async function validateMiniProject(rootDir: string): Promise<MiniValidationResult> {\n const root = path.resolve(rootDir);\n const errors: string[] = [];\n const warnings: string[] = [];\n let context: MiniProjectContext | null = null;\n\n try {\n context = await readMiniContext(root);\n } catch (e) {\n errors.push((e as Error).message);\n }\n\n const manifestPath = path.join(root, 'manifest.json');\n let manifest: Record<string, unknown> | null = null;\n try {\n manifest = JSON.parse(await fs.readFile(manifestPath, 'utf-8')) as Record<string, unknown>;\n } catch (e) {\n if ((e as NodeJS.ErrnoException).code === 'ENOENT') {\n errors.push('manifest.json is required');\n } else {\n errors.push(`manifest.json is not valid JSON: ${(e as Error).message}`);\n }\n }\n\n if (context) {\n if (!MINI_APP_CODE_PATTERN.test(context.appCode)) {\n errors.push('appCode must match ^[a-z][a-z0-9-]*$');\n }\n if (context.remotePrefix !== miniRemotePrefix(context.appCode)) {\n errors.push(`context remotePrefix must be ${miniRemotePrefix(context.appCode)}`);\n }\n }\n\n if (manifest) {\n await validateManifestObject(manifest, context, root, errors, warnings);\n }\n\n return {\n valid: errors.length === 0,\n appCode: context?.appCode,\n workspaceKey: context?.workspaceKey,\n errors,\n warnings,\n };\n}\n\nexport async function pushMiniAppProject(args: MiniPushArgs): Promise<MiniPushResult> {\n const root = path.resolve(args.rootDir);\n const validation = await validateMiniProject(root);\n if (!validation.valid) {\n throw new CliError({\n code: CliErrorCode.INVALID_ARGUMENT,\n message: `mini project validation failed: ${validation.errors.join('; ')}`,\n });\n }\n\n const context = await readMiniContext(root);\n const manifest = await readPullManifest(root);\n const localFiles = await readLocalMiniFiles(root);\n const remoteFiles = await loadRemoteMiniAppFiles(args.http, context.workspaceKey, context.appCode);\n const plan = buildPushPlan(context, manifest, localFiles, remoteFiles);\n\n const result: MiniPushResult = {\n appCode: context.appCode,\n workspaceKey: context.workspaceKey,\n dryRun: !!args.dryRun,\n operations: plan.operations,\n conflicts: plan.conflicts,\n };\n if (plan.conflicts.length || args.dryRun) {\n return result;\n }\n\n const localByPath = new Map(localFiles.map((file) => [file.localPath, file] as const));\n for (const operation of plan.operations) {\n if (operation.operation === 'skip') continue;\n if (operation.operation === 'delete') {\n if (operation.fileId) await deleteWorkspaceFile(args.http, operation.fileId);\n continue;\n }\n const localFile = localByPath.get(operation.localPath);\n if (!localFile) continue;\n if (operation.operation === 'create') {\n await createWorkspaceFile(\n args.http,\n context.workspaceKey,\n operation.remotePath,\n localFile.content,\n );\n }\n if (operation.operation === 'update' && operation.fileId) {\n await saveWorkspaceFileContent(args.http, operation.fileId, localFile.content);\n }\n }\n\n const latestRemoteFiles = await listWorkspaceFiles(args.http, context.workspaceKey, {\n pageSize: 5000,\n });\n const remoteByLocalPath = new Map<string, WorkspaceFileItem>();\n for (const remote of latestRemoteFiles.data) {\n if (!remote.relativePath.startsWith(context.remotePrefix + '/')) continue;\n remoteByLocalPath.set(localPathFromRemote(context.remotePrefix, remote.relativePath), remote);\n }\n await writeMiniState(root, context, localFiles, remoteByLocalPath);\n return result;\n}\n\nexport async function buildMiniPreviewTargets(\n rootDir: string,\n frontendBaseUrl: string,\n): Promise<MiniPreviewTarget[]> {\n const context = await readMiniContext(path.resolve(rootDir));\n const app = encodeURIComponent(context.appCode);\n const workspace = encodeURIComponent(context.workspaceKey);\n const base = frontendBaseUrl.replace(/\\/+$/, '');\n return [\n {\n key: 'bare',\n name: 'bare runtime',\n url: `${base}/mini.html?app=${app}&workspaceKey=${workspace}`,\n },\n {\n key: 'standalone',\n name: 'standalone page',\n url: `${base}/mini/${app}?workspaceKey=${workspace}`,\n },\n {\n key: 'embedded',\n name: 'embedded page',\n url: `${base}/custom/${app}?workspaceKey=${workspace}`,\n },\n ];\n}\n\nexport interface MiniSmokeFetchResponse {\n status: number;\n}\n\nexport type MiniSmokeFetch = (\n url: string,\n init?: { headers?: Record<string, string>; method: 'GET'; signal?: AbortSignal },\n) => Promise<MiniSmokeFetchResponse>;\n\nexport interface MiniSmokeCheckArgs {\n rootDir: string;\n frontendBaseUrl: string;\n previewTargets: MiniPreviewTarget[];\n fetcher?: MiniSmokeFetch;\n accessToken?: string | null;\n timeoutMs?: number;\n}\n\nexport async function runMiniSmokeChecks(args: MiniSmokeCheckArgs): Promise<MiniSmokeResult> {\n const context = await readMiniContext(path.resolve(args.rootDir));\n const targets = buildMiniSmokeTargets(context, args.frontendBaseUrl, args.previewTargets);\n const results = await Promise.all(\n targets.map((target) =>\n checkMiniSmokeTarget(\n target,\n args.fetcher ?? defaultSmokeFetch,\n args.timeoutMs ?? 5000,\n args.accessToken,\n context.workspaceKey,\n ),\n ),\n );\n const passed = results.filter((target) => target.ok).length;\n const failed = results.length - passed;\n return {\n ok: failed === 0,\n checked: results.length,\n passed,\n failed,\n targets: results,\n };\n}\n\nfunction buildMiniSmokeTargets(\n context: MiniProjectContext,\n frontendBaseUrl: string,\n previewTargets: MiniPreviewTarget[],\n): MiniSmokeTargetResult[] {\n const base = frontendBaseUrl.replace(/\\/+$/, '');\n const app = encodeURIComponent(context.appCode);\n return [\n ...previewTargets.map(\n (target): MiniSmokeTargetResult => ({\n kind: 'preview',\n surface: target.key,\n url: target.url,\n status: null,\n ok: false,\n }),\n ),\n {\n kind: 'manifest',\n url: `${base}/api/mini-apps/${app}`,\n status: null,\n ok: false,\n },\n ...['index.html', 'index.css', 'index.js'].map(\n (file): MiniSmokeTargetResult => ({\n kind: 'asset',\n url: `${base}/static/mini-apps/${app}/${file}`,\n status: null,\n ok: false,\n }),\n ),\n ];\n}\n\nasync function checkMiniSmokeTarget(\n target: MiniSmokeTargetResult,\n fetcher: MiniSmokeFetch,\n timeoutMs: number,\n accessToken?: string | null,\n workspaceKey?: string | null,\n): Promise<MiniSmokeTargetResult> {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), timeoutMs);\n try {\n const headers =\n target.kind !== 'preview'\n ? {\n ...(accessToken ? { Authorization: `Bearer ${accessToken}` } : {}),\n ...(workspaceKey ? { 'X-Workspace-Key': workspaceKey } : {}),\n }\n : undefined;\n const response = await fetcher(target.url, {\n ...(headers ? { headers } : {}),\n method: 'GET',\n signal: controller.signal,\n });\n const ok = response.status >= 200 && response.status < 400;\n return {\n ...target,\n status: response.status,\n ok,\n error: ok ? undefined : `HTTP ${response.status}`,\n };\n } catch (e) {\n return {\n ...target,\n status: null,\n ok: false,\n error: (e as Error).message || 'request failed',\n };\n } finally {\n clearTimeout(timeout);\n }\n}\n\nasync function defaultSmokeFetch(\n url: string,\n init?: { headers?: Record<string, string>; method: 'GET'; signal?: AbortSignal },\n): Promise<MiniSmokeFetchResponse> {\n if (typeof fetch !== 'function') throw new Error('fetch is not available');\n return fetch(url, init);\n}\n\nexport function buildMiniVerifyAgentReport(report: MiniVerifyReport): MiniVerifyAgentReport {\n const statusCounts = countStatuses(report.status);\n const operationCounts = countOperations(report.plan?.operations ?? []);\n const conflicts = report.plan?.conflicts ?? [];\n const dryRunOk = !!report.plan && conflicts.length === 0;\n const smoke = report.smoke ?? null;\n const smokeOk = !smoke || smoke.ok;\n const previewUrls = report.preview.map(\n (target): MiniVerifyPreviewUrl => ({\n surface: target.key,\n name: target.name,\n url: target.url,\n }),\n );\n const safeToPush = report.validate.valid && dryRunOk && previewUrls.length > 0 && smokeOk;\n const nextAction = chooseMiniVerifyNextAction(report, previewUrls, smoke);\n\n return {\n ok: report.ok && smokeOk,\n safeToPush,\n nextAction,\n summary: {\n validate: {\n ok: report.validate.valid,\n errors: report.validate.errors.length,\n warnings: report.validate.warnings.length,\n appCode: report.validate.appCode,\n workspaceKey: report.validate.workspaceKey,\n },\n status: {\n total: report.status.length,\n changed: statusCounts.added + statusCounts.modified + statusCounts.deleted,\n added: statusCounts.added,\n modified: statusCounts.modified,\n deleted: statusCounts.deleted,\n unchanged: statusCounts.unchanged,\n },\n dryRun: {\n ok: dryRunOk,\n skipped: !report.plan,\n skippedReason: report.plan ? undefined : report.planSkipped,\n operations: operationCounts.create + operationCounts.update + operationCounts.delete,\n creates: operationCounts.create,\n updates: operationCounts.update,\n deletes: operationCounts.delete,\n skips: operationCounts.skip,\n conflicts: conflicts.length,\n },\n preview: {\n ok: previewUrls.length > 0,\n count: previewUrls.length,\n surfaces: previewUrls.map((target) => target.surface),\n },\n },\n changedFiles: report.status\n .filter((entry) => entry.status !== 'unchanged')\n .map(\n (entry): MiniVerifyChangedFile => ({\n path: entry.localPath,\n status: entry.status,\n remotePath: entry.remotePath,\n }),\n ),\n conflicts,\n previewUrls,\n browserAcceptance: buildMiniVerifyBrowserAcceptance(report, previewUrls),\n smoke,\n };\n}\n\nfunction buildMiniVerifyBrowserAcceptance(\n report: MiniVerifyReport,\n previewUrls: MiniVerifyPreviewUrl[],\n): MiniVerifyBrowserAcceptance {\n const recommendedUrl = preferredBrowserPreview(previewUrls)?.url;\n const appCode = report.validate.appCode;\n return {\n recommendedUrl,\n expectedTexts: appCode ? [appCode] : [],\n expectedNetwork: expectedBrowserNetwork(report, recommendedUrl),\n console: {\n failOn: ['error', 'warn'],\n },\n };\n}\n\nfunction preferredBrowserPreview(\n previewUrls: MiniVerifyPreviewUrl[],\n): MiniVerifyPreviewUrl | undefined {\n return (\n previewUrls.find((target) => target.surface === 'standalone') ??\n previewUrls.find((target) => target.surface === 'bare') ??\n previewUrls[0]\n );\n}\n\nfunction expectedBrowserNetwork(report: MiniVerifyReport, recommendedUrl?: string): string[] {\n const smokeUrls = report.smoke?.targets\n .filter((target) => target.kind !== 'preview')\n .map((target) => target.url);\n if (smokeUrls?.length) return Array.from(new Set(smokeUrls));\n\n const appCode = report.validate.appCode;\n if (!recommendedUrl || !appCode) return [];\n try {\n const origin = new URL(recommendedUrl).origin;\n return [\n `${origin}/api/mini-apps/${appCode}`,\n `${origin}/static/mini-apps/${appCode}/index.html`,\n `${origin}/static/mini-apps/${appCode}/index.css`,\n `${origin}/static/mini-apps/${appCode}/index.js`,\n ];\n } catch {\n return [];\n }\n}\n\nfunction chooseMiniVerifyNextAction(\n report: MiniVerifyReport,\n previewUrls: MiniVerifyPreviewUrl[],\n smoke: MiniSmokeResult | null,\n): MiniVerifyNextAction {\n if (smoke && !smoke.ok) return 'inspect-preview';\n if (!report.validate.valid) return 'fix-validation';\n if (!report.plan) return 'inspect-status';\n if (report.plan.conflicts.length) return 'resolve-conflict';\n if (!previewUrls.length) return 'inspect-preview';\n return 'push';\n}\n\nexport function formatMiniVerifyReport(report: MiniVerifyReport): string {\n const totalSteps = report.smoke ? 5 : 4;\n const lines: string[] = [];\n lines.push(`verify: ${report.ok ? 'OK' : 'FAIL'}`);\n lines.push('');\n\n // 1. validate\n lines.push(`[1/${totalSteps}] validate: ${report.validate.valid ? '✓ pass' : '✗ fail'}`);\n if (report.validate.errors.length) {\n for (const err of report.validate.errors) lines.push(` error: ${err}`);\n }\n if (report.validate.warnings.length) {\n for (const warn of report.validate.warnings) lines.push(` warn: ${warn}`);\n }\n\n // 2. status\n const statusBucket = { added: 0, modified: 0, deleted: 0, unchanged: 0 };\n for (const row of report.status) statusBucket[row.status] += 1;\n lines.push(\n `[2/${totalSteps}] status: ${statusBucket.added} added, ${statusBucket.modified} modified, ${statusBucket.deleted} deleted, ${statusBucket.unchanged} unchanged`,\n );\n for (const row of report.status) {\n if (row.status === 'unchanged') continue;\n lines.push(` ${row.status.padEnd(9)} ${row.localPath}`);\n }\n\n // 3. push dry-run\n if (report.plan) {\n if (report.plan.conflicts.length) {\n lines.push(`[3/${totalSteps}] dry-run: ✗ ${report.plan.conflicts.length} conflict(s)`);\n for (const c of report.plan.conflicts) lines.push(` ${c.localPath}: ${c.reason}`);\n } else {\n const opCount = report.plan.operations.filter((op) => op.operation !== 'skip').length;\n lines.push(`[3/${totalSteps}] dry-run: ✓ ${opCount} operation(s) planned (no remote writes)`);\n for (const op of report.plan.operations) {\n if (op.operation === 'skip') continue;\n lines.push(` ${op.operation.padEnd(7)} ${op.localPath}`);\n }\n }\n } else {\n lines.push(`[3/${totalSteps}] dry-run: - skipped (${report.planSkipped ?? 'unknown reason'})`);\n }\n\n // 4. preview URLs\n if (report.preview.length) {\n lines.push(`[4/${totalSteps}] preview:`);\n for (const target of report.preview) lines.push(` ${target.key.padEnd(11)} ${target.url}`);\n } else {\n lines.push(`[4/${totalSteps}] preview: - (no targets)`);\n }\n\n // 5. smoke HTTP checks\n if (report.smoke) {\n const marker = report.smoke.ok ? '✓' : '✗';\n lines.push(\n `[5/5] smoke: ${marker} ${report.smoke.passed}/${report.smoke.checked} passed`,\n );\n for (const target of report.smoke.targets) {\n const targetMarker = target.ok ? '✓' : '✗';\n const label = target.surface ? `${target.kind}:${target.surface}` : target.kind;\n const status = target.status ?? '-';\n const error = target.error ? ` (${target.error})` : '';\n lines.push(` ${targetMarker} ${label.padEnd(18)} ${status} ${target.url}${error}`);\n }\n }\n\n return lines.join('\\n');\n}\n\nfunction countStatuses(status: MiniStatusEntry[]): Record<MiniStatusEntry['status'], number> {\n const counts: Record<MiniStatusEntry['status'], number> = {\n added: 0,\n modified: 0,\n deleted: 0,\n unchanged: 0,\n };\n for (const entry of status) counts[entry.status] += 1;\n return counts;\n}\n\nfunction countOperations(operations: MiniPushPlanItem[]): Record<MiniPushOperation, number> {\n const counts: Record<MiniPushOperation, number> = {\n create: 0,\n update: 0,\n delete: 0,\n skip: 0,\n };\n for (const operation of operations) counts[operation.operation] += 1;\n return counts;\n}\n\nexport async function readMiniContext(rootDir: string): Promise<MiniProjectContext> {\n const file = path.join(rootDir, MINI_CONTEXT_FILE);\n let parsed: unknown;\n try {\n parsed = JSON.parse(await fs.readFile(file, 'utf-8'));\n } catch (e) {\n if ((e as NodeJS.ErrnoException).code === 'ENOENT') {\n throw new CliError({\n code: CliErrorCode.CONFIG_PARSE_ERROR,\n message: `${MINI_CONTEXT_FILE} not found; run hcm workspace add mini <appCode> or hcm workspace pull first`,\n });\n }\n throw new CliError({\n code: CliErrorCode.CONFIG_PARSE_ERROR,\n message: `${MINI_CONTEXT_FILE} is invalid: ${(e as Error).message}`,\n });\n }\n const obj = asRecord(parsed);\n if (obj.kind !== MINI_CONTEXT_KIND || obj.version !== MINI_CONTEXT_VERSION) {\n throw new CliError({\n code: CliErrorCode.CONFIG_PARSE_ERROR,\n message: `${MINI_CONTEXT_FILE} is not an HCM mini app context`,\n });\n }\n return {\n kind: MINI_CONTEXT_KIND,\n version: MINI_CONTEXT_VERSION,\n appCode: normalizeAppCode(stringRequired(obj.appCode, 'appCode')),\n workspaceKey: normalizeWorkspaceKey(stringRequired(obj.workspaceKey, 'workspaceKey')),\n remotePrefix: stringRequired(obj.remotePrefix, 'remotePrefix'),\n tenantId: stringOptional(obj.tenantId),\n endpoint: stringOptional(obj.endpoint),\n profile: stringOptional(obj.profile),\n updatedAt: stringOptional(obj.updatedAt) ?? new Date(0).toISOString(),\n };\n}\n\nexport async function readPullManifest(rootDir: string): Promise<MiniPullManifest> {\n const file = path.join(rootDir, MINI_STATE_DIR, MINI_PULL_MANIFEST_FILE);\n try {\n const parsed = JSON.parse(await fs.readFile(file, 'utf-8')) as MiniPullManifest;\n return {\n version: MINI_CONTEXT_VERSION,\n appCode: parsed.appCode ? normalizeAppCode(parsed.appCode) : '',\n workspaceKey: parsed.workspaceKey ? normalizeWorkspaceKey(parsed.workspaceKey) : '',\n remotePrefix: parsed.remotePrefix ?? '',\n profile: parsed.profile,\n pulledAt: parsed.pulledAt,\n files: Array.isArray(parsed.files) ? parsed.files : [],\n };\n } catch (e) {\n if ((e as NodeJS.ErrnoException).code === 'ENOENT') {\n return {\n version: MINI_CONTEXT_VERSION,\n appCode: '',\n workspaceKey: '',\n remotePrefix: '',\n pulledAt: new Date(0).toISOString(),\n files: [],\n };\n }\n throw e;\n }\n}\n\nasync function validateManifestObject(\n manifest: Record<string, unknown>,\n context: MiniProjectContext | null,\n root: string,\n errors: string[],\n warnings: string[],\n): Promise<void> {\n for (const key of Object.keys(manifest)) {\n if (!TOP_LEVEL_MANIFEST_KEYS.has(key)) errors.push(`manifest has unsupported key: ${key}`);\n }\n requireString(manifest, 'schemaVersion', errors, (value) => value === '1.0');\n requireString(manifest, 'code', errors, (value) => /^[a-zA-Z0-9-]+$/.test(value));\n requireString(manifest, 'name', errors, (value) => value.trim().length > 0);\n requireString(manifest, 'version', errors, (value) => SEMVER_PATTERN.test(value));\n requireString(manifest, 'sdkVersion', errors, (value) => SEMVER_PATTERN.test(value));\n\n const code = typeof manifest.code === 'string' ? manifest.code : '';\n if (context && code && code !== context.appCode) {\n errors.push(`manifest code must equal context appCode: ${context.appCode}`);\n }\n const requiredScopes = asStringArray(manifest.requiredScopes);\n if (!requiredScopes.length) {\n errors.push('manifest requiredScopes must contain at least one scope');\n }\n for (const scope of requiredScopes) {\n if (!SCOPE_PATTERN.test(scope)) {\n errors.push(`manifest requiredScopes has invalid scope: ${scope}`);\n }\n }\n\n const surfaces = asStringArray(manifest.surfaces);\n const defaultSurface = typeof manifest.defaultSurface === 'string' ? manifest.defaultSurface : '';\n if (surfaces.length) {\n for (const surface of surfaces) {\n if (surface !== 'embedded' && surface !== 'standalone') {\n errors.push(`manifest surfaces has invalid value: ${surface}`);\n }\n }\n if (new Set(surfaces).size !== surfaces.length) {\n errors.push('manifest surfaces must be unique');\n }\n } else {\n warnings.push('manifest surfaces is omitted; backend default may apply');\n }\n if (defaultSurface && surfaces.length && !surfaces.includes(defaultSurface)) {\n errors.push('manifest defaultSurface must be declared in surfaces');\n }\n\n const entry = asRecord(manifest.entry);\n if (!Object.keys(entry).length) {\n errors.push('manifest entry is required');\n }\n for (const key of Object.keys(entry)) {\n if (!ENTRY_KEYS.has(key)) errors.push(`manifest entry has unsupported key: ${key}`);\n }\n const html = stringOptional(entry.html);\n const js = stringOptional(entry.js);\n const css = stringOptional(entry.css);\n if (!html && !js) {\n errors.push('manifest entry must define html or js');\n }\n await validateEntryPath(root, 'entry.html', html, '.html', errors);\n await validateEntryPath(root, 'entry.js', js, '.js', errors);\n await validateEntryPath(root, 'entry.css', css, '.css', errors);\n await validateEntryRequiredScopes(root, js, requiredScopes, warnings);\n\n const bundleObjectRef = asRecord(manifest.bundleObjectRef);\n const pathSpec = asRecord(bundleObjectRef.pathSpec);\n const pathSegment = stringOptional(pathSpec.pathSegment);\n if (context && pathSegment && pathSegment !== miniRemotePrefix(context.appCode)) {\n errors.push(\n `manifest bundleObjectRef.pathSpec.pathSegment must be ${miniRemotePrefix(context.appCode)}`,\n );\n }\n}\n\nasync function validateEntryPath(\n root: string,\n field: string,\n value: string | undefined,\n suffix: string,\n errors: string[],\n): Promise<void> {\n if (!value) return;\n if (!value.endsWith(suffix)) errors.push(`${field} must end with ${suffix}`);\n try {\n normalizeRelativePath(value);\n } catch (e) {\n errors.push(`${field} is invalid: ${(e as Error).message}`);\n return;\n }\n try {\n await fs.access(path.join(root, value));\n } catch {\n errors.push(`${field} target file does not exist: ${value}`);\n }\n}\n\nasync function validateEntryRequiredScopes(\n root: string,\n jsPath: string | undefined,\n requiredScopes: string[],\n warnings: string[],\n): Promise<void> {\n if (!jsPath) return;\n const absolutePath = path.resolve(root, jsPath);\n if (!absolutePath.startsWith(root + path.sep)) return;\n let source = '';\n try {\n source = await fs.readFile(absolutePath, 'utf-8');\n } catch {\n return;\n }\n\n const declaredScopes = new Set(requiredScopes);\n for (const scope of inferMiniSdkRequiredScopes(source)) {\n if (!declaredScopes.has(scope)) {\n warnings.push(`manifest requiredScopes may be missing ${scope} for ${jsPath} SDK usage`);\n }\n }\n}\n\nfunction inferMiniSdkRequiredScopes(source: string): string[] {\n const constants = readMiniModelConstants(source);\n const scopes = new Set<string>();\n let match: RegExpExecArray | null;\n\n MINI_SDK_MODEL_CALL_PATTERN.lastIndex = 0;\n while ((match = MINI_SDK_MODEL_CALL_PATTERN.exec(source))) {\n const method = match[1];\n if (!method) continue;\n const model = match[2] ?? constants.get(match[3] ?? '');\n const action = MINI_SDK_SCOPE_METHODS[method];\n if (model && action) scopes.add(`${model}.${action}`);\n }\n\n MINI_SDK_ACTION_CALL_PATTERN.lastIndex = 0;\n while ((match = MINI_SDK_ACTION_CALL_PATTERN.exec(source))) {\n const model = match[1] ?? constants.get(match[2] ?? '');\n const action = match[3];\n if (model && action) scopes.add(`${model}.${action}`);\n }\n\n return Array.from(scopes);\n}\n\nfunction readMiniModelConstants(source: string): Map<string, string> {\n const constants = new Map<string, string>();\n let match: RegExpExecArray | null;\n MINI_MODEL_CONST_PATTERN.lastIndex = 0;\n while ((match = MINI_MODEL_CONST_PATTERN.exec(source))) {\n if (!match[1] || !match[2]) continue;\n constants.set(match[1], match[2]);\n }\n return constants;\n}\n\nfunction buildPushPlan(\n context: MiniProjectContext,\n manifest: MiniPullManifest,\n localFiles: MiniLocalFile[],\n remoteFiles: Array<{ item: WorkspaceFileItem; content: WorkspaceFileContent }>,\n): { operations: MiniPushPlanItem[]; conflicts: MiniPushConflict[] } {\n const status = computeStatusEntries(context, manifest, localFiles);\n const baseByLocal = new Map(manifest.files.map((file) => [file.localPath, file] as const));\n const remoteByLocal = new Map(\n remoteFiles.map((file) => [\n localPathFromRemote(context.remotePrefix, file.item.relativePath),\n file,\n ] as const),\n );\n const operations: MiniPushPlanItem[] = [];\n const conflicts: MiniPushConflict[] = [];\n\n for (const entry of status) {\n const remote = remoteByLocal.get(entry.localPath);\n const base = baseByLocal.get(entry.localPath);\n const remoteSha = remote ? sha256(remote.content.content) : undefined;\n if (entry.status === 'added') {\n if (remote) {\n conflicts.push(conflict(entry, 'remote file already exists but was not in local snapshot'));\n } else {\n operations.push({ operation: 'create', localPath: entry.localPath, remotePath: entry.remotePath });\n }\n continue;\n }\n if (entry.status === 'modified') {\n if (!remote) {\n conflicts.push(conflict(entry, 'remote file was deleted after last pull'));\n } else if (base && remoteSha !== base.sha256) {\n conflicts.push(conflict(entry, 'remote file changed after last pull'));\n } else {\n operations.push({\n operation: 'update',\n localPath: entry.localPath,\n remotePath: entry.remotePath,\n fileId: remote.item.id,\n });\n }\n continue;\n }\n if (entry.status === 'deleted') {\n if (!remote) {\n operations.push({\n operation: 'skip',\n localPath: entry.localPath,\n remotePath: entry.remotePath,\n reason: 'already deleted remotely',\n });\n } else if (base && remoteSha !== base.sha256) {\n conflicts.push(conflict(entry, 'remote file changed after last pull'));\n } else {\n operations.push({\n operation: 'delete',\n localPath: entry.localPath,\n remotePath: entry.remotePath,\n fileId: remote.item.id,\n });\n }\n continue;\n }\n operations.push({\n operation: 'skip',\n localPath: entry.localPath,\n remotePath: entry.remotePath,\n fileId: remote?.item.id,\n reason: 'unchanged',\n });\n }\n return { operations, conflicts };\n}\n\nfunction computeStatusEntries(\n context: MiniProjectContext,\n manifest: MiniPullManifest,\n localFiles: MiniLocalFile[],\n): MiniStatusEntry[] {\n const baseByLocal = new Map(manifest.files.map((file) => [file.localPath, file] as const));\n const localByPath = new Map(localFiles.map((file) => [file.localPath, file] as const));\n const paths = [...new Set([...baseByLocal.keys(), ...localByPath.keys()])].sort();\n return paths.map((localPath) => {\n const base = baseByLocal.get(localPath);\n const local = localByPath.get(localPath);\n const remotePath = base?.remotePath ?? remotePathFromLocal(context.appCode, localPath);\n if (!base && local) {\n return { localPath, remotePath, status: 'added', sha256: local.sha256 };\n }\n if (base && !local) {\n return {\n localPath,\n remotePath,\n status: 'deleted',\n fileId: base.fileId,\n baseSha256: base.sha256,\n };\n }\n if (base && local && base.sha256 !== local.sha256) {\n return {\n localPath,\n remotePath,\n status: 'modified',\n fileId: base.fileId,\n sha256: local.sha256,\n baseSha256: base.sha256,\n };\n }\n return {\n localPath,\n remotePath,\n status: 'unchanged',\n fileId: base?.fileId,\n sha256: local?.sha256,\n baseSha256: base?.sha256,\n };\n });\n}\n\nasync function loadRemoteMiniAppFiles(\n http: AxiosInstance,\n workspaceKey: string,\n appCode: string,\n): Promise<Array<{ item: WorkspaceFileItem; content: WorkspaceFileContent }>> {\n const prefix = miniRemotePrefix(appCode) + '/';\n const list = await listWorkspaceFiles(http, workspaceKey, { pageSize: 5000 });\n const result: Array<{ item: WorkspaceFileItem; content: WorkspaceFileContent }> = [];\n for (const item of list.data) {\n if (!item.relativePath.startsWith(prefix) || isDirectoryMarkerPath(item.relativePath)) continue;\n const content = await loadWorkspaceFileContent(http, item.id);\n result.push({ item, content });\n }\n return result;\n}\n\nasync function readLocalMiniFiles(root: string): Promise<MiniLocalFile[]> {\n const files: MiniLocalFile[] = [];\n await walk(root, '', files);\n files.sort((a, b) => a.localPath.localeCompare(b.localPath));\n return files;\n}\n\nasync function walk(root: string, relativeDir: string, files: MiniLocalFile[]): Promise<void> {\n const absoluteDir = path.join(root, relativeDir);\n let entries: Dirent[];\n try {\n entries = await fs.readdir(absoluteDir, { withFileTypes: true });\n } catch (e) {\n if ((e as NodeJS.ErrnoException).code === 'ENOENT') return;\n throw e;\n }\n for (const entry of entries) {\n if (entry.name === MINI_CONTEXT_FILE) continue;\n if (entry.isDirectory() && EXCLUDED_DIRS.has(entry.name)) continue;\n const localPath = normalizeRelativePath(path.posix.join(toPosix(relativeDir), entry.name));\n const absolutePath = path.join(root, localPath);\n if (entry.isDirectory()) {\n await walk(root, localPath, files);\n } else if (entry.isFile()) {\n const content = await fs.readFile(absolutePath, 'utf-8');\n files.push({\n localPath,\n absolutePath,\n content,\n sha256: sha256(content),\n size: Buffer.byteLength(content, 'utf-8'),\n });\n }\n }\n}\n\nasync function writeMiniState(\n root: string,\n context: MiniProjectContext,\n files: MiniLocalFile[],\n remoteByLocalPath: Map<string, WorkspaceFileItem>,\n): Promise<void> {\n const stateDir = path.join(root, MINI_STATE_DIR);\n const baseDir = path.join(stateDir, 'base');\n await fs.rm(baseDir, { recursive: true, force: true });\n await fs.mkdir(baseDir, { recursive: true });\n const snapshots: MiniFileSnapshot[] = [];\n for (const file of files) {\n await writeText(path.join(baseDir, file.localPath), file.content);\n snapshots.push(toSnapshot(context, file, remoteByLocalPath.get(file.localPath)));\n }\n await writeJson(path.join(stateDir, MINI_PULL_MANIFEST_FILE), {\n version: MINI_CONTEXT_VERSION,\n appCode: context.appCode,\n workspaceKey: context.workspaceKey,\n remotePrefix: context.remotePrefix,\n profile: context.profile,\n pulledAt: new Date().toISOString(),\n files: snapshots,\n } satisfies MiniPullManifest);\n}\n\nfunction toSnapshot(\n context: MiniProjectContext,\n file: MiniLocalFile,\n remote?: WorkspaceFileItem,\n): MiniFileSnapshot {\n return {\n localPath: file.localPath,\n remotePath: remote?.relativePath ?? remotePathFromLocal(context.appCode, file.localPath),\n fileId: remote?.id,\n sha256: file.sha256,\n size: file.size,\n lastModified: remote?.lastModified,\n };\n}\n\nfunction buildMiniAppTemplateFiles(args: {\n appCode: string;\n name?: string;\n defaultSurface: MiniSurface;\n templateKind?: MiniTemplateKind;\n writableModel?: string;\n writableFields?: string[] | null;\n writableDefaults?: Record<string, unknown>;\n writableTodayDefaultFields?: string[];\n requiredScopes: string[];\n tenantId: string;\n}): Array<{ localPath: string; content: string }> {\n return buildMiniAppTemplateFileSet(args);\n}\n\nasync function ensureTargetWritable(\n targetDir: string,\n relativePaths: string[],\n force: boolean,\n): Promise<void> {\n if (force) return;\n const existing: string[] = [];\n for (const relativePath of relativePaths) {\n try {\n await fs.access(path.join(targetDir, relativePath));\n existing.push(relativePath);\n } catch {\n /* file does not exist */\n }\n }\n if (existing.length) {\n throw new CliError({\n code: CliErrorCode.INVALID_ARGUMENT,\n message: `target contains existing files: ${existing.join(', ')} (use --force to overwrite)`,\n });\n }\n}\n\nfunction validateRequiredPath(value: string, fieldName: string): string {\n if (!value || !value.trim()) {\n throw new CliError({ code: CliErrorCode.MISSING_FLAG, message: `${fieldName} is required` });\n }\n return value.trim();\n}\n\nfunction normalizeAppCode(value: string): string {\n const normalized = validateRequiredPath(value, 'appCode');\n if (!MINI_APP_CODE_PATTERN.test(normalized)) {\n throw new CliError({\n code: CliErrorCode.INVALID_ARGUMENT,\n message: 'appCode must match ^[a-z][a-z0-9-]*$',\n });\n }\n return normalized;\n}\n\nfunction normalizeWorkspaceKey(value: string): string {\n const normalized = validateRequiredPath(value, 'workspaceKey');\n if (!/^[A-Za-z0-9._-]+$/.test(normalized)) {\n throw new CliError({\n code: CliErrorCode.INVALID_ARGUMENT,\n message: 'workspaceKey only supports letters, numbers, dot, underscore and dash',\n });\n }\n return normalized;\n}\n\nfunction normalizeRelativePath(value: string): string {\n const normalized = toPosix(value).replace(/^\\/+/, '').replace(/\\/+/g, '/');\n if (!normalized || normalized === '.' || normalized.includes('\\0')) {\n throw new Error('path is empty');\n }\n for (const segment of normalized.split('/')) {\n if (!segment || segment === '.' || segment === '..') {\n throw new Error(`unsafe path segment: ${segment}`);\n }\n }\n return normalized;\n}\n\nfunction miniRemotePrefix(appCode: string): string {\n return `${MINI_REMOTE_ROOT}/${normalizeAppCode(appCode)}`;\n}\n\nfunction remotePathFromLocal(appCode: string, localPath: string): string {\n return `${miniRemotePrefix(appCode)}/${normalizeRelativePath(localPath)}`;\n}\n\nfunction localPathFromRemote(remotePrefix: string, remotePath: string): string {\n const prefix = remotePrefix + '/';\n if (!remotePath.startsWith(prefix)) {\n throw new CliError({\n code: CliErrorCode.INVALID_ARGUMENT,\n message: `remote path is outside mini app prefix: ${remotePath}`,\n });\n }\n return normalizeRelativePath(remotePath.slice(prefix.length));\n}\n\nfunction isDirectoryMarkerPath(relativePath: string): boolean {\n return relativePath.endsWith('/.keep') || relativePath === '.keep';\n}\n\nfunction sha256(content: string): string {\n return crypto.createHash('sha256').update(content, 'utf-8').digest('hex');\n}\n\nasync function writeText(file: string, content: string): Promise<void> {\n await fs.mkdir(path.dirname(file), { recursive: true });\n await fs.writeFile(file, content, 'utf-8');\n}\n\nasync function writeJson(file: string, value: unknown): Promise<void> {\n await writeText(file, JSON.stringify(value, null, 2) + '\\n');\n}\n\nfunction conflict(entry: MiniStatusEntry, reason: string): MiniPushConflict {\n return {\n localPath: entry.localPath,\n remotePath: entry.remotePath,\n reason,\n };\n}\n\nfunction asRecord(value: unknown): Record<string, unknown> {\n return value && typeof value === 'object' ? (value as Record<string, unknown>) : {};\n}\n\nfunction asStringArray(value: unknown): string[] {\n return Array.isArray(value) ? value.filter((item): item is string => typeof item === 'string') : [];\n}\n\nfunction requireString(\n obj: Record<string, unknown>,\n key: string,\n errors: string[],\n predicate?: (value: string) => boolean,\n): void {\n const value = obj[key];\n if (typeof value !== 'string' || !value.trim()) {\n errors.push(`manifest ${key} is required`);\n return;\n }\n if (predicate && !predicate(value)) {\n errors.push(`manifest ${key} is invalid`);\n }\n}\n\nfunction stringRequired(value: unknown, fieldName: string): string {\n if (typeof value !== 'string' || !value.trim()) {\n throw new CliError({\n code: CliErrorCode.CONFIG_PARSE_ERROR,\n message: `${MINI_CONTEXT_FILE} missing ${fieldName}`,\n });\n }\n return value;\n}\n\nfunction stringOptional(value: unknown): string | undefined {\n return typeof value === 'string' && value.trim() ? value : undefined;\n}\n\nfunction toPosix(value: string): string {\n return value.replace(/\\\\/g, '/');\n}\n","import type { FieldMeta, ModelDescription } from '../meta/describe';\nimport type { MiniSurface, MiniTemplateKind } from './types';\nimport { SDK_VERSION } from '../package-version';\n\nexport interface MiniTemplateArgs {\n appCode: string;\n name?: string;\n defaultSurface: MiniSurface;\n requiredScopes: string[];\n tenantId: string;\n templateKind?: MiniTemplateKind;\n writableModel?: string;\n writableFields?: string[] | null;\n writableDefaults?: Record<string, unknown>;\n writableTodayDefaultFields?: string[];\n}\n\nexport interface MiniWritableTemplateConfig {\n model?: string;\n fields?: string[] | null;\n defaults?: Record<string, unknown>;\n todayDefaultFields?: string[];\n}\n\ninterface MiniReadTemplateConfig {\n model: string;\n modelLabel: string;\n}\n\nconst DEFAULT_MULTI_PAGE_MODEL = 'Employee';\nconst DEFAULT_WRITABLE_MODEL = 'OrgUnit';\n// OrgUnit 的编号字段是 `code`(CLAUDE.md 字段命名规范:编号/编码一律 code)。\n// 后端 OrgUnit.yml 里 unitCode 仅作 AI synonym 存在,不是可写字段——\n// 曾误用 unitCode 导致 writable-workbench 模板生成绑定到不存在字段的表单。\nconst DEFAULT_WRITABLE_FIELDS = ['code', 'name', 'unitType', 'status', 'effecDate', 'expireDate'];\nconst DEFAULT_WRITABLE_DEFAULTS: Record<string, unknown> = {\n unitType: 'DEPT',\n status: 'ACTIVE',\n expireDate: '9999-12-31',\n};\nconst DEFAULT_WRITABLE_TODAY_FIELDS = ['effecDate'];\nconst SYSTEM_FIELD_NAMES = new Set([\n 'id',\n 'tenantId',\n 'createdAt',\n 'createdBy',\n 'updatedAt',\n 'updatedBy',\n 'deletedAt',\n 'deletedBy',\n 'version',\n]);\nconst PREFERRED_WRITABLE_FIELDS = [\n 'code',\n 'name',\n 'unitType',\n 'status',\n 'effecDate',\n 'effectiveDate',\n 'startDate',\n 'expireDate',\n 'endDate',\n 'description',\n 'remark',\n 'notes',\n];\nconst MAX_AUTO_WRITABLE_FIELDS = 10;\nconst REFERENCE_FIELD_MODELS: Record<string, string> = {\n employeeId: 'Employee',\n managerId: 'Employee',\n orgUnitId: 'OrgUnit',\n organizationId: 'OrgUnit',\n positionId: 'Position',\n reportToId: 'Position',\n jobId: 'Job',\n jobGradeId: 'JobGrade',\n jobFamilyId: 'JobFamily',\n};\n\nexport function defaultMiniRequiredScopes(\n templateKind: MiniTemplateKind = 'dashboard',\n writableConfig: MiniWritableTemplateConfig = {},\n): string[] {\n if (templateKind === 'writable-workbench') {\n const model = normalizeWritableModel(writableConfig.model);\n return unique([\n `${model}.list`,\n `${model}.get`,\n `${model}.create`,\n `${model}.update`,\n `${model}.delete`,\n ...referenceScopesForWritableFields(writableConfig.fields),\n ]);\n }\n if (templateKind === 'multi-page') {\n const model = normalizeReadModel(writableConfig.model);\n return [`${model}.list`, `${model}.get`];\n }\n return ['Employee.list'];\n}\n\nexport function buildMiniAppTemplateFiles(args: MiniTemplateArgs): Array<{ localPath: string; content: string }> {\n const templateKind = args.templateKind ?? 'dashboard';\n const displayName = args.name?.trim() || args.appCode;\n const writableConfig = buildWritableTemplateConfig(args);\n const readConfig = buildReadTemplateConfig(args);\n const manifest = {\n schemaVersion: '1.0',\n code: args.appCode,\n name: displayName,\n version: SDK_VERSION,\n sdkVersion: SDK_VERSION,\n surfaces: ['embedded', 'standalone'],\n defaultSurface: args.defaultSurface,\n entry: { html: 'index.html', css: 'index.css', js: 'index.js' },\n bundleObjectRef: {\n tenantId: args.tenantId,\n storageType: 'LOCAL',\n pathSpec: { pathSegment: `mini_apps/${args.appCode}`, fileName: '' },\n },\n requiredScopes: args.requiredScopes,\n };\n\n return [\n { localPath: 'manifest.json', content: JSON.stringify(manifest, null, 2) + '\\n' },\n {\n localPath: 'index.html',\n content: templateHtml(templateKind, args.appCode, displayName, writableConfig),\n },\n {\n localPath: 'index.css',\n content: templateCss(templateKind),\n },\n {\n localPath: 'index.js',\n content: templateJs(templateKind, args.appCode, displayName, writableConfig, readConfig),\n },\n ];\n}\n\nfunction templateHtml(\n templateKind: MiniTemplateKind,\n appCode: string,\n displayName: string,\n writableConfig: ReturnType<typeof buildWritableTemplateConfig>,\n): string {\n if (templateKind === 'writable-workbench') return writableHtml(appCode, displayName, writableConfig);\n if (templateKind === 'multi-page') return multiPageHtml(appCode, displayName);\n return dashboardHtml(appCode, displayName);\n}\n\nfunction templateCss(templateKind: MiniTemplateKind): string {\n if (templateKind === 'writable-workbench') return writableCss();\n if (templateKind === 'multi-page') return multiPageCss();\n return dashboardCss();\n}\n\nfunction templateJs(\n templateKind: MiniTemplateKind,\n appCode: string,\n displayName: string,\n writableConfig: ReturnType<typeof buildWritableTemplateConfig>,\n readConfig: MiniReadTemplateConfig,\n): string {\n if (templateKind === 'writable-workbench') return writableJs(writableConfig);\n if (templateKind === 'multi-page') return multiPageJs(appCode, displayName, readConfig);\n return dashboardJs();\n}\n\nfunction buildReadTemplateConfig(args: MiniTemplateArgs): MiniReadTemplateConfig {\n const model = normalizeReadModel(args.writableModel);\n return {\n model,\n modelLabel: modelDisplayLabel(model),\n };\n}\n\nfunction buildWritableTemplateConfig(args: MiniTemplateArgs): Required<MiniWritableTemplateConfig> & { modelLabel: string } {\n const model = normalizeWritableModel(args.writableModel);\n const fields =\n args.writableFields === undefined\n ? model === DEFAULT_WRITABLE_MODEL\n ? [...DEFAULT_WRITABLE_FIELDS]\n : null\n : args.writableFields;\n const defaults =\n args.writableDefaults !== undefined\n ? args.writableDefaults\n : model === DEFAULT_WRITABLE_MODEL\n ? { ...DEFAULT_WRITABLE_DEFAULTS }\n : {};\n const todayDefaultFields =\n args.writableTodayDefaultFields !== undefined\n ? args.writableTodayDefaultFields\n : model === DEFAULT_WRITABLE_MODEL\n ? [...DEFAULT_WRITABLE_TODAY_FIELDS]\n : [];\n return {\n model,\n fields,\n defaults,\n todayDefaultFields,\n modelLabel: modelDisplayLabel(model),\n };\n}\n\nexport function inferMiniWritableTemplateOptionsFromModel(\n description: ModelDescription,\n): { fields: string[]; defaults: Record<string, unknown>; todayDefaultFields: string[] } {\n const writableFields = inferWritableFields(description.fields);\n const writableFieldSet = new Set(writableFields);\n return {\n fields: writableFields,\n defaults: inferWritableDefaults(description.fields, writableFieldSet),\n todayDefaultFields: inferTodayDefaultFields(description.fields, writableFieldSet),\n };\n}\n\nfunction inferWritableFields(fields: FieldMeta[]): string[] {\n const editable = fields.map((field) => field.name).filter((name) => name && !SYSTEM_FIELD_NAMES.has(name));\n const editableSet = new Set(editable);\n const required = fields\n .filter((field) => field.required === true && editableSet.has(field.name))\n .map((field) => field.name);\n const preferred = PREFERRED_WRITABLE_FIELDS.filter((name) => editableSet.has(name));\n const inferred = unique([...required, ...preferred]);\n return (inferred.length ? inferred : editable).slice(0, MAX_AUTO_WRITABLE_FIELDS);\n}\n\nfunction inferWritableDefaults(fields: FieldMeta[], writableFieldSet: Set<string>): Record<string, unknown> {\n const defaults: Record<string, unknown> = {};\n if (hasWritableField(fields, writableFieldSet, 'unitType')) defaults.unitType = 'DEPT';\n if (hasWritableField(fields, writableFieldSet, 'status')) defaults.status = 'ACTIVE';\n if (hasWritableField(fields, writableFieldSet, 'expireDate')) defaults.expireDate = '9999-12-31';\n if (hasWritableField(fields, writableFieldSet, 'endDate')) defaults.endDate = '9999-12-31';\n return defaults;\n}\n\nfunction inferTodayDefaultFields(fields: FieldMeta[], writableFieldSet: Set<string>): string[] {\n return ['effecDate', 'effectiveDate', 'startDate'].filter((name) => hasWritableField(fields, writableFieldSet, name));\n}\n\nfunction hasWritableField(fields: FieldMeta[], writableFieldSet: Set<string>, name: string): boolean {\n return writableFieldSet.has(name) && fields.some((field) => field.name === name);\n}\n\nfunction unique(values: string[]): string[] {\n return Array.from(new Set(values));\n}\n\nfunction referenceScopesForWritableFields(fields?: string[] | null): string[] {\n if (!fields?.length) return [];\n return unique(\n fields.flatMap((field) => {\n const model = REFERENCE_FIELD_MODELS[field];\n return model ? [`${model}.list`, `${model}.get`] : [];\n }),\n );\n}\n\nfunction normalizeWritableModel(model?: string): string {\n return model?.trim() || DEFAULT_WRITABLE_MODEL;\n}\n\nfunction normalizeReadModel(model?: string): string {\n return model?.trim() || DEFAULT_MULTI_PAGE_MODEL;\n}\n\nfunction modelDisplayLabel(model: string): string {\n const labels: Record<string, string> = {\n Employee: '员工',\n OrgUnit: '组织单元',\n Position: '岗位',\n };\n return labels[model] ?? `${model} 记录`;\n}\n\nfunction escapeHtml(value: string): string {\n return value\n .replaceAll('&', '&')\n .replaceAll('<', '<')\n .replaceAll('>', '>')\n .replaceAll('\"', '"')\n .replaceAll(\"'\", ''');\n}\n\nfunction dashboardHtml(appCode: string, displayName: string): string {\n return `<section class=\"hcm-mini-app\" data-mini-app=\"${appCode}\">\n <header class=\"hcm-mini-app__header\">\n <p class=\"hcm-mini-app__eyebrow\">HCM Mini App</p>\n <h1>${displayName}</h1>\n </header>\n <main class=\"hcm-mini-app__body\">\n <span data-field=\"status\">Ready</span>\n </main>\n</section>\n`;\n}\n\nfunction dashboardCss(): string {\n return `.hcm-mini-app {\n min-height: 100%;\n padding: 24px;\n color: #1f2933;\n background: #f6f8fb;\n font-family: Inter, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n}\n\n.hcm-mini-app__header {\n margin-bottom: 16px;\n}\n\n.hcm-mini-app__eyebrow {\n margin: 0 0 4px;\n color: #52606d;\n font-size: 12px;\n}\n\n.hcm-mini-app h1 {\n margin: 0;\n font-size: 24px;\n line-height: 1.25;\n}\n`;\n}\n\nfunction dashboardJs(): string {\n return `export async function mount(container, hcm) {\n const status = container.querySelector('[data-field=\"status\"]');\n if (status) {\n status.textContent = \"Tenant \" + hcm.context.tenantId;\n }\n}\n\nexport function unmount(container) {\n container.innerHTML = \"\";\n}\n`;\n}\n\nfunction multiPageHtml(appCode: string, displayName: string): string {\n return `<section class=\"mini-routed-app\" data-mini-app=\"${appCode}\">\n <header class=\"mini-routed-app__header\">\n <div>\n <p class=\"mini-routed-app__eyebrow\">Tenant <span data-field=\"tenant-id\">-</span></p>\n <h1>${escapeHtml(displayName)}</h1>\n </div>\n <span class=\"mini-routed-app__status\" data-field=\"status\">准备就绪</span>\n </header>\n\n <nav class=\"mini-routed-app__nav\" data-field=\"nav\" aria-label=\"轻应用页面\"></nav>\n <main class=\"mini-routed-app__view\" data-field=\"view\"></main>\n <pre class=\"mini-routed-app__error\" data-field=\"error\" hidden></pre>\n</section>\n`;\n}\n\nfunction multiPageCss(): string {\n return `.mini-routed-app {\n min-height: 100%;\n padding: 24px;\n color: #1f2933;\n background: #f6f8fb;\n font-family: Inter, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n}\n\n.mini-routed-app__header {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n gap: 16px;\n margin-bottom: 16px;\n}\n\n.mini-routed-app__eyebrow,\n.mini-routed-app__status {\n margin: 0;\n color: #52606d;\n font-size: 12px;\n}\n\n.mini-routed-app h1 {\n margin: 4px 0 0;\n font-size: 24px;\n line-height: 1.2;\n}\n\n.mini-routed-app__nav {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n margin-bottom: 14px;\n}\n\n.mini-routed-app__nav a {\n border: 1px solid #d9e2ec;\n border-radius: 6px;\n padding: 7px 12px;\n color: #52606d;\n background: #fff;\n text-decoration: none;\n}\n\n.mini-routed-app__nav a[aria-current=\"page\"] {\n border-color: #2f80ed;\n color: #fff;\n background: #2f80ed;\n}\n\n.mini-routed-app__view {\n min-height: 320px;\n border: 1px solid #d9e2ec;\n border-radius: 8px;\n padding: 18px;\n background: #fff;\n}\n\n.mini-routed-app__toolbar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n margin-bottom: 14px;\n}\n\n.mini-routed-app__grid {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));\n gap: 12px;\n}\n\n.mini-routed-app__metric,\n.mini-routed-app__section {\n border: 1px solid #e5edf5;\n border-radius: 8px;\n padding: 14px;\n background: #fbfdff;\n}\n\n.mini-routed-app__metric span,\n.mini-routed-app__section span {\n display: block;\n color: #8a94a6;\n font-size: 12px;\n}\n\n.mini-routed-app__metric strong {\n display: block;\n margin-top: 6px;\n font-size: 24px;\n}\n\n.mini-routed-app__list {\n display: grid;\n gap: 8px;\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.mini-routed-app__list button,\n.mini-routed-app__button {\n height: 34px;\n border: 1px solid #2f80ed;\n border-radius: 6px;\n padding: 0 12px;\n color: #fff;\n background: #2f80ed;\n cursor: pointer;\n}\n\n.mini-routed-app__list button {\n display: flex;\n width: 100%;\n align-items: center;\n justify-content: space-between;\n border-color: #d9e2ec;\n color: #1f2933;\n background: #f8fafc;\n text-align: left;\n}\n\n.mini-routed-app__muted {\n color: #8a94a6;\n}\n\n.mini-routed-app__error {\n margin: 14px 0 0;\n border: 1px solid #fecaca;\n border-radius: 6px;\n padding: 10px;\n color: #b91c1c;\n background: #fff1f2;\n white-space: pre-wrap;\n}\n\n@media (max-width: 720px) {\n .mini-routed-app {\n padding: 16px;\n }\n\n .mini-routed-app__header,\n .mini-routed-app__toolbar {\n display: grid;\n }\n}\n`;\n}\n\nfunction multiPageJs(appCode: string, displayName: string, config: MiniReadTemplateConfig): string {\n return `const APP_CODE = ${JSON.stringify(appCode)};\nconst APP_NAME = ${JSON.stringify(displayName)};\nconst DATA_MODEL = ${JSON.stringify(config.model)};\nconst MODEL_LABEL = ${JSON.stringify(config.modelLabel)};\nconst ROUTES = [\n { path: \"/\", label: \"总览\" },\n { path: \"/records\", label: MODEL_LABEL },\n { path: \"/settings\", label: \"设置\" },\n];\n\nconst state = {\n records: [],\n loaded: false,\n details: new Map(),\n unsubscribe: null,\n};\n\nconst textOf = (value, fallback = \"-\") => String(value ?? fallback);\nconst setText = (container, selector, value) => {\n const node = container.querySelector(selector);\n if (node) node.textContent = textOf(value);\n};\nconst setStatus = (container, value) => setText(container, '[data-field=\"status\"]', value);\nconst showError = (container, error) => {\n const node = container.querySelector('[data-field=\"error\"]');\n if (!node) return;\n if (!error) {\n node.hidden = true;\n node.textContent = \"\";\n return;\n }\n node.hidden = false;\n node.textContent = [error.code || \"REQUEST_FAILED\", error.message || \"请求失败\", error.traceId ? \"traceId: \" + error.traceId : \"\"].filter(Boolean).join(\"\\\\n\");\n};\nconst viewOf = (container) => container.querySelector('[data-field=\"view\"]');\nconst createNode = (tag, className, text) => {\n const node = document.createElement(tag);\n if (className) node.className = className;\n if (text !== undefined) node.textContent = text;\n return node;\n};\nconst recordName = (record) => record.name || record.employeeName || record.code || record.unitCode || record.positionCode || record.employeeNo || record.id || \"未命名\" + MODEL_LABEL;\n\nconst renderNav = (container, hcm) => {\n const nav = container.querySelector('[data-field=\"nav\"]');\n if (!nav) return;\n const current = hcm.router.current().path;\n nav.innerHTML = \"\";\n for (const route of ROUTES) {\n const link = document.createElement(\"a\");\n link.href = hcm.router.link(route.path);\n link.dataset.route = route.path;\n link.textContent = route.label;\n const active = route.path === \"/\" ? current === \"/\" : current === route.path || current.startsWith(route.path + \"/\");\n if (active) link.setAttribute(\"aria-current\", \"page\");\n nav.appendChild(link);\n }\n};\n\nconst loadRecords = async (container, hcm) => {\n if (state.loaded) return;\n setStatus(container, \"加载中\");\n const result = await hcm.query({ model: DATA_MODEL, page: 0, size: 10 });\n state.records = result.rows || [];\n state.loaded = true;\n setStatus(container, \"已更新\");\n};\n\nconst loadDetail = async (hcm, id) => {\n if (!state.details.has(id)) {\n state.details.set(id, await hcm.get({ model: DATA_MODEL, id }));\n }\n return state.details.get(id);\n};\n\nconst renderOverview = (container, hcm) => {\n const view = viewOf(container);\n if (!view) return;\n view.innerHTML = \"\";\n const title = createNode(\"div\", \"mini-routed-app__toolbar\");\n const heading = createNode(\"h2\", \"\", \"总览\");\n const action = createNode(\"button\", \"mini-routed-app__button\", \"查看\" + MODEL_LABEL);\n action.type = \"button\";\n action.dataset.route = \"/records\";\n title.appendChild(heading);\n title.appendChild(action);\n\n const grid = createNode(\"div\", \"mini-routed-app__grid\");\n const metrics = [\n [\"应用\", APP_NAME],\n [\"数据模型\", DATA_MODEL],\n [\"当前租户\", hcm.context.tenantId],\n ];\n for (const [label, value] of metrics) {\n const card = createNode(\"div\", \"mini-routed-app__metric\");\n card.appendChild(createNode(\"span\", \"\", label));\n card.appendChild(createNode(\"strong\", \"\", value));\n grid.appendChild(card);\n }\n view.appendChild(title);\n view.appendChild(grid);\n};\n\nconst renderRecords = async (container, hcm) => {\n await loadRecords(container, hcm);\n const view = viewOf(container);\n if (!view) return;\n view.innerHTML = \"\";\n const toolbar = createNode(\"div\", \"mini-routed-app__toolbar\");\n toolbar.appendChild(createNode(\"h2\", \"\", MODEL_LABEL));\n toolbar.appendChild(createNode(\"span\", \"mini-routed-app__muted\", \"共 \" + state.records.length + \" 条\"));\n const list = createNode(\"ul\", \"mini-routed-app__list\");\n for (const record of state.records) {\n const item = document.createElement(\"li\");\n const button = document.createElement(\"button\");\n button.type = \"button\";\n button.dataset.recordId = textOf(record.id, \"\");\n button.appendChild(createNode(\"strong\", \"\", recordName(record)));\n button.appendChild(createNode(\"span\", \"mini-routed-app__muted\", record.status || record.employeeNo || record.code || \"详情\"));\n item.appendChild(button);\n list.appendChild(item);\n }\n if (!state.records.length) {\n list.appendChild(createNode(\"li\", \"mini-routed-app__muted\", \"暂无\" + MODEL_LABEL + \"记录\"));\n }\n view.appendChild(toolbar);\n view.appendChild(list);\n};\n\nconst renderRecordDetail = async (container, hcm, id) => {\n const record = await loadDetail(hcm, id);\n const view = viewOf(container);\n if (!view) return;\n view.innerHTML = \"\";\n const toolbar = createNode(\"div\", \"mini-routed-app__toolbar\");\n const title = createNode(\"h2\", \"\", recordName(record));\n const back = createNode(\"button\", \"mini-routed-app__button\", \"返回列表\");\n back.type = \"button\";\n back.dataset.route = \"/records\";\n toolbar.appendChild(title);\n toolbar.appendChild(back);\n\n const grid = createNode(\"div\", \"mini-routed-app__grid\");\n const fields = [\n [\"ID\", record.id],\n [\"编号\", record.employeeNo || record.code],\n [\"状态\", record.status],\n [\"邮箱\", record.email],\n ];\n for (const [label, value] of fields) {\n const card = createNode(\"div\", \"mini-routed-app__section\");\n card.appendChild(createNode(\"span\", \"\", label));\n card.appendChild(createNode(\"strong\", \"\", textOf(value)));\n grid.appendChild(card);\n }\n view.appendChild(toolbar);\n view.appendChild(grid);\n};\n\nconst renderSettings = (container, hcm) => {\n const view = viewOf(container);\n if (!view) return;\n view.innerHTML = \"\";\n const section = createNode(\"div\", \"mini-routed-app__section\");\n section.appendChild(createNode(\"h2\", \"\", \"设置\"));\n section.appendChild(createNode(\"span\", \"\", \"应用编码\"));\n section.appendChild(createNode(\"strong\", \"\", APP_CODE));\n section.appendChild(createNode(\"span\", \"\", \"当前路由\"));\n section.appendChild(createNode(\"strong\", \"\", hcm.router.current().path));\n view.appendChild(section);\n};\n\nconst renderRoute = async (container, hcm) => {\n showError(container, null);\n renderNav(container, hcm);\n const detailRoute = hcm.router.match(\"/records/:id\");\n if (detailRoute) {\n await renderRecordDetail(container, hcm, detailRoute.params.id);\n return;\n }\n if (hcm.router.match(\"/records\")) {\n await renderRecords(container, hcm);\n return;\n }\n if (hcm.router.match(\"/settings\")) {\n renderSettings(container, hcm);\n return;\n }\n if (hcm.router.current().path !== \"/\") {\n hcm.router.replace(\"/\");\n return;\n }\n renderOverview(container, hcm);\n};\n\nexport async function mount(container, hcm) {\n setText(container, '[data-field=\"tenant-id\"]', hcm.context.tenantId);\n const onClick = (event) => {\n const routeTarget = event.target instanceof Element ? event.target.closest(\"[data-route]\") : null;\n if (routeTarget?.dataset.route) {\n event.preventDefault();\n hcm.router.push(routeTarget.dataset.route);\n return;\n }\n const recordTarget = event.target instanceof Element ? event.target.closest(\"[data-record-id]\") : null;\n if (recordTarget?.dataset.recordId) {\n hcm.router.push(\"/records/\" + encodeURIComponent(recordTarget.dataset.recordId));\n }\n };\n container.addEventListener(\"click\", onClick);\n state.unsubscribe = hcm.router.subscribe(() => void renderRoute(container, hcm).catch((error) => showError(container, error)));\n await renderRoute(container, hcm);\n container.__miniCleanup = () => {\n container.removeEventListener(\"click\", onClick);\n state.unsubscribe?.();\n };\n}\n\nexport function unmount(container) {\n container.__miniCleanup?.();\n container.innerHTML = \"\";\n}\n`;\n}\n\nfunction writableHtml(appCode: string, displayName: string, config: ReturnType<typeof buildWritableTemplateConfig>): string {\n const modelLabel = escapeHtml(config.modelLabel);\n return `<section class=\"mini-workbench\" data-mini-app=\"${appCode}\">\n <header class=\"mini-workbench__header\">\n <div>\n <p class=\"mini-workbench__eyebrow\">Tenant <span data-field=\"tenant-id\">-</span></p>\n <h1>${escapeHtml(displayName)}</h1>\n </div>\n <span class=\"mini-workbench__status\" data-field=\"status\">准备就绪</span>\n </header>\n\n <div class=\"mini-workbench__layout\">\n <aside class=\"mini-workbench__panel\">\n <div class=\"mini-workbench__panel-head\">\n <strong>${modelLabel}</strong>\n <div class=\"mini-workbench__actions\">\n <button type=\"button\" data-action=\"new-record\">新建</button>\n <button type=\"button\" data-action=\"refresh\">刷新</button>\n </div>\n </div>\n <div class=\"mini-workbench__list\" data-field=\"record-list\"></div>\n </aside>\n\n <main class=\"mini-workbench__panel\">\n <div class=\"mini-workbench__panel-head\">\n <strong data-field=\"editor-title\">选择${modelLabel}</strong>\n <div class=\"mini-workbench__actions\">\n <button type=\"button\" data-action=\"delete-record\" hidden>删除</button>\n <button type=\"button\" data-action=\"back\">返回列表</button>\n </div>\n </div>\n <div class=\"mini-workbench__empty\" data-field=\"editor-empty\">\n <strong>选择一条${modelLabel}</strong>\n <span>从左侧列表打开记录,或点击“新建”。</span>\n </div>\n <form class=\"mini-workbench__form\" data-field=\"editor-form\" hidden>\n <div class=\"mini-workbench__fields\" data-field=\"editor-fields\"></div>\n <button type=\"submit\">保存</button>\n </form>\n <pre class=\"mini-workbench__error\" data-field=\"error\" hidden></pre>\n </main>\n </div>\n</section>\n`;\n}\n\nfunction writableCss(): string {\n return `.mini-workbench {\n min-height: 100%;\n padding: 24px;\n color: #1f2933;\n background: #f6f8fb;\n font-family: Inter, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n}\n\n.mini-workbench__header,\n.mini-workbench__panel-head {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n gap: 16px;\n}\n\n.mini-workbench__header {\n margin-bottom: 18px;\n}\n\n.mini-workbench__eyebrow,\n.mini-workbench__status {\n margin: 0;\n color: #52606d;\n font-size: 12px;\n}\n\n.mini-workbench h1 {\n margin: 4px 0 0;\n font-size: 24px;\n line-height: 1.2;\n}\n\n.mini-workbench__layout {\n display: grid;\n grid-template-columns: minmax(240px, 320px) minmax(0, 1fr);\n gap: 14px;\n}\n\n.mini-workbench__panel {\n min-height: 320px;\n border: 1px solid #d9e2ec;\n border-radius: 8px;\n padding: 16px;\n background: #fff;\n}\n\n.mini-workbench__actions,\n.mini-workbench__form,\n.mini-workbench__fields {\n display: grid;\n gap: 10px;\n}\n\n.mini-workbench__form[hidden],\n.mini-workbench__empty[hidden] {\n display: none;\n}\n\n.mini-workbench__actions {\n grid-auto-flow: column;\n align-items: center;\n}\n\n.mini-workbench button {\n height: 32px;\n border: 1px solid #2f80ed;\n border-radius: 6px;\n padding: 0 12px;\n color: #fff;\n background: #2f80ed;\n cursor: pointer;\n}\n\n.mini-workbench__list {\n display: grid;\n gap: 8px;\n}\n\n.mini-workbench__empty {\n display: grid;\n gap: 8px;\n min-height: 220px;\n place-content: center;\n color: #8a94a6;\n text-align: center;\n}\n\n.mini-workbench__empty strong {\n color: #52606d;\n font-size: 15px;\n}\n\n.mini-workbench__list button {\n width: 100%;\n border-color: #d9e2ec;\n color: #1f2933;\n background: #f8fafc;\n text-align: left;\n}\n\n.mini-workbench__field {\n display: grid;\n gap: 6px;\n color: #52606d;\n font-size: 13px;\n}\n\n.mini-workbench__field input,\n.mini-workbench__field select,\n.mini-workbench__field textarea {\n min-height: 36px;\n border: 1px solid #cbd5e1;\n border-radius: 6px;\n padding: 0 10px;\n color: #1f2933;\n background: #fff;\n}\n\n.mini-workbench__field textarea {\n min-height: 72px;\n padding: 8px 10px;\n}\n\n.mini-workbench__field--checkbox {\n grid-template-columns: auto 1fr;\n align-items: center;\n}\n\n.mini-workbench__field--checkbox input {\n min-height: auto;\n}\n\n.mini-workbench__field-hint {\n color: #8a94a6;\n font-size: 12px;\n}\n\n.mini-workbench__reference {\n display: grid;\n gap: 6px;\n position: relative;\n}\n\n.mini-workbench__reference-row {\n display: grid;\n grid-template-columns: minmax(0, 1fr) auto;\n gap: 8px;\n}\n\n.mini-workbench__reference-results {\n display: grid;\n gap: 4px;\n max-height: 180px;\n overflow: auto;\n border: 1px solid #d9e2ec;\n border-radius: 6px;\n padding: 6px;\n background: #fff;\n}\n\n.mini-workbench__reference-results[hidden] {\n display: none;\n}\n\n.mini-workbench__reference-results button {\n width: 100%;\n border-color: #d9e2ec;\n color: #1f2933;\n background: #f8fafc;\n text-align: left;\n}\n\n.mini-workbench__required {\n color: #dc2626;\n}\n\n.mini-workbench__error {\n margin: 14px 0 0;\n border: 1px solid #fecaca;\n border-radius: 6px;\n padding: 10px;\n color: #b91c1c;\n background: #fff1f2;\n white-space: pre-wrap;\n}\n\n@media (max-width: 720px) {\n .mini-workbench__layout {\n grid-template-columns: 1fr;\n }\n}\n`;\n}\n\nfunction writableJs(config: ReturnType<typeof buildWritableTemplateConfig>): string {\n const model = JSON.stringify(config.model);\n const modelLabel = JSON.stringify(config.modelLabel);\n const fields = config.fields?.length ? JSON.stringify(config.fields) : 'null';\n const defaults = JSON.stringify(config.defaults, null, 2);\n const todayDefaults = JSON.stringify(config.todayDefaultFields);\n return `const MODEL = ${model};\nconst MODEL_LABEL = ${modelLabel};\nconst ROUTE_BASE = \"/records\";\nconst FORM_FIELDS = ${fields};\nconst CREATE_DEFAULTS = ${defaults};\nconst TODAY_DEFAULT_FIELDS = ${todayDefaults};\nconst state = {\n records: [],\n activeId: \"\",\n mode: \"list\",\n meta: null,\n unsubscribe: null,\n};\nconst textOf = (value, fallback = \"-\") => String(value ?? fallback);\nconst today = () => new Date().toISOString().slice(0, 10);\nconst formDefaults = () => {\n const defaults = { ...CREATE_DEFAULTS };\n for (const key of TODAY_DEFAULT_FIELDS) defaults[key] = today();\n return defaults;\n};\nconst formOptions = (defaults = {}) => {\n const options = {};\n if (Array.isArray(FORM_FIELDS) && FORM_FIELDS.length) options.include = FORM_FIELDS;\n if (defaults && Object.keys(defaults).length) options.defaults = defaults;\n return options;\n};\nconst setText = (container, selector, value) => {\n const node = container.querySelector(selector);\n if (node) node.textContent = textOf(value);\n};\nconst setStatus = (container, value) => setText(container, '[data-field=\"status\"]', value);\nconst setDeleteVisible = (container, visible) => {\n const button = container.querySelector('[data-action=\"delete-record\"]');\n if (button) button.hidden = !visible;\n};\nconst setEditorVisible = (container, visible) => {\n const form = container.querySelector('[data-field=\"editor-form\"]');\n const empty = container.querySelector('[data-field=\"editor-empty\"]');\n if (form) form.hidden = !visible;\n if (empty) empty.hidden = visible;\n};\nconst showError = (container, error) => {\n const node = container.querySelector('[data-field=\"error\"]');\n if (!node) return;\n if (!error) {\n node.hidden = true;\n node.textContent = \"\";\n return;\n }\n node.hidden = false;\n node.textContent = [error.code || \"REQUEST_FAILED\", error.message || \"请求失败\", error.traceId ? \"traceId: \" + error.traceId : \"\"].filter(Boolean).join(\"\\\\n\");\n};\nconst ensureMeta = async (hcm) => {\n if (!state.meta) state.meta = await hcm.describe({ model: MODEL, type: \"info\" });\n return state.meta;\n};\nconst queryReferenceRecords = async (hcm, field, keyword) => {\n const model = field.reference?.model;\n if (!model) return [];\n if (hcm.reference?.search) {\n return (await hcm.reference.search({ model, field: field.reference?.field, keyword, size: 8 })).rows;\n }\n const query = { model, page: 0, size: 8 };\n const trimmed = keyword.trim();\n if (trimmed) {\n query.filter = { [field.reference?.field || \"name\"]: { $contains: trimmed } };\n }\n try {\n return (await hcm.query(query)).rows;\n } catch (error) {\n if (!trimmed) throw error;\n return (await hcm.query({ model, page: 0, size: 8 })).rows;\n }\n};\nconst referenceLabel = (hcm, record) => hcm.reference?.label ? hcm.reference.label(record) : record?.name || record?.code || record?.unitCode || record?.positionCode || record?.employeeNo || record?.id || \"未命名记录\";\nconst recordName = (record) => record?.name || record?.employeeName || record?.code || record?.unitCode || record?.positionCode || record?.employeeNo || record?.id || \"未命名\" + MODEL_LABEL;\nconst hydrateReferenceLabel = async (hcm, field, searchInput, value) => {\n const id = value === undefined || value === null ? \"\" : String(value).trim();\n if (!id || !field.reference?.model || !hcm.reference?.labelFor) return;\n const label = await hcm.reference.labelFor({ model: field.reference.model, id });\n if (searchInput.value === id) searchInput.value = label || id;\n};\nconst applyReferenceRecord = (hcm, hiddenInput, searchInput, record) => {\n hiddenInput.value = record?.id ?? \"\";\n searchInput.value = referenceLabel(hcm, record);\n};\nconst isUnsupportedMiniUi = (error) => error?.code === \"MINI_UI_UNSUPPORTED\" || error?.name === \"MiniUiUnsupportedError\";\nconst pickReferenceRecord = async (hcm, field) => {\n const model = field.reference?.model;\n if (!model || !hcm.ui?.pickModel) return null;\n try {\n const result = await hcm.ui.pickModel({\n model,\n multiple: false,\n title: \"选择\" + field.label,\n });\n return result.confirmed ? result.record : null;\n } catch (error) {\n if (isUnsupportedMiniUi(error)) return null;\n throw error;\n }\n};\nconst renderReferenceOptions = (hcm, box, hiddenInput, searchInput, records) => {\n box.innerHTML = \"\";\n if (!records.length) {\n box.textContent = \"暂无可选记录\";\n box.hidden = false;\n return;\n }\n for (const record of records) {\n const button = document.createElement(\"button\");\n button.type = \"button\";\n button.textContent = referenceLabel(hcm, record);\n button.addEventListener(\"click\", () => {\n applyReferenceRecord(hcm, hiddenInput, searchInput, record);\n box.hidden = true;\n });\n box.appendChild(button);\n }\n box.hidden = false;\n};\nconst renderReferenceControl = (field, value, hcm) => {\n const wrapper = document.createElement(\"div\");\n wrapper.className = \"mini-workbench__reference\";\n const hidden = document.createElement(\"input\");\n hidden.type = \"hidden\";\n hidden.name = field.key;\n hidden.dataset.formKey = field.key;\n hidden.value = value ?? \"\";\n const search = document.createElement(\"input\");\n search.type = \"search\";\n search.name = field.key + \"__search\";\n search.autocomplete = \"off\";\n search.placeholder = field.reference?.model ? \"搜索 \" + field.reference.model : \"搜索记录\";\n search.value = value ?? \"\";\n void hydrateReferenceLabel(hcm, field, search, value);\n const picker = document.createElement(\"button\");\n picker.type = \"button\";\n picker.textContent = \"选择\";\n const results = document.createElement(\"div\");\n results.className = \"mini-workbench__reference-results\";\n results.hidden = true;\n let requestId = 0;\n const searchRecords = async () => {\n const current = ++requestId;\n results.hidden = false;\n results.textContent = \"搜索中...\";\n try {\n const records = await queryReferenceRecords(hcm, field, search.value);\n if (current === requestId) renderReferenceOptions(hcm, results, hidden, search, records);\n } catch (error) {\n if (current === requestId) {\n results.textContent = \"搜索失败,可直接填写记录 ID\";\n }\n }\n };\n search.addEventListener(\"focus\", () => void searchRecords());\n search.addEventListener(\"input\", () => {\n hidden.value = search.value.trim();\n void searchRecords();\n });\n picker.addEventListener(\"click\", () => {\n void (async () => {\n results.hidden = true;\n const record = await pickReferenceRecord(hcm, field);\n if (record) {\n applyReferenceRecord(hcm, hidden, search, record);\n return;\n }\n search.focus();\n await searchRecords();\n })().catch((error) => {\n results.hidden = false;\n results.textContent = error?.message || \"选择失败,可直接搜索或填写记录 ID\";\n });\n });\n const row = document.createElement(\"div\");\n row.className = \"mini-workbench__reference-row\";\n row.appendChild(search);\n row.appendChild(picker);\n wrapper.appendChild(hidden);\n wrapper.appendChild(row);\n wrapper.appendChild(results);\n return wrapper;\n};\nconst renderFormControl = (field, value, hcm) => {\n const key = field.key;\n if (field.inputType === \"select\" && field.options?.length) {\n const select = document.createElement(\"select\");\n select.name = key;\n select.dataset.formKey = key;\n for (const option of field.options) {\n const item = document.createElement(\"option\");\n item.value = String(option.value);\n item.textContent = option.label;\n item.disabled = option.disabled === true;\n if (String(option.value) === String(value ?? \"\")) item.selected = true;\n select.appendChild(item);\n }\n return select;\n }\n if (field.inputType === \"textarea\") {\n const textarea = document.createElement(\"textarea\");\n textarea.name = key;\n textarea.dataset.formKey = key;\n textarea.value = value ?? \"\";\n return textarea;\n }\n if (field.inputType === \"reference\") {\n return renderReferenceControl(field, value, hcm);\n }\n const input = document.createElement(\"input\");\n input.name = key;\n input.dataset.formKey = key;\n input.autocomplete = \"off\";\n input.type =\n field.inputType === \"number\" ? \"number\" :\n field.inputType === \"date\" ? \"date\" :\n field.inputType === \"datetime\" ? \"datetime-local\" :\n field.inputType === \"checkbox\" ? \"checkbox\" : \"text\";\n if (field.inputType === \"checkbox\") {\n input.checked = value === true || value === \"true\";\n } else {\n input.value = value ?? \"\";\n }\n return input;\n};\nconst renderForm = async (container, hcm, values = {}) => {\n const meta = await ensureMeta(hcm);\n const defaults = formDefaults();\n const fields = hcm.form.fields(meta, formOptions());\n const fieldBox = container.querySelector('[data-field=\"editor-fields\"]');\n if (!fieldBox) return;\n fieldBox.innerHTML = \"\";\n for (const field of fields) {\n const value = values[field.key] ?? defaults[field.key] ?? field.defaultValue ?? \"\";\n const label = document.createElement(\"label\");\n label.className = \"mini-workbench__field\" + (field.inputType === \"checkbox\" ? \" mini-workbench__field--checkbox\" : \"\");\n const caption = document.createElement(\"span\");\n caption.textContent = field.label;\n if (field.required) {\n const mark = document.createElement(\"strong\");\n mark.className = \"mini-workbench__required\";\n mark.textContent = \" *\";\n caption.appendChild(mark);\n }\n label.appendChild(caption);\n label.appendChild(renderFormControl(field, value, hcm));\n if (field.inputType === \"reference\" && field.reference?.model) {\n const hint = document.createElement(\"small\");\n hint.className = \"mini-workbench__field-hint\";\n hint.textContent = \"关联模型:\" + field.reference.model + \",填写记录 ID\";\n label.appendChild(hint);\n }\n fieldBox.appendChild(label);\n }\n};\nconst readFormValues = (container) => {\n const values = {};\n for (const input of container.querySelectorAll(\"[data-form-key]\")) {\n const key = input.dataset.formKey;\n if (input.type === \"checkbox\") {\n values[key] = input.checked;\n continue;\n }\n const value = input.value?.trim();\n if (value === \"\") continue;\n values[key] = value;\n }\n return values;\n};\nconst renderList = (container, hcm) => {\n const list = container.querySelector('[data-field=\"record-list\"]');\n if (!list) return;\n list.innerHTML = \"\";\n for (const item of state.records) {\n const button = document.createElement(\"button\");\n button.type = \"button\";\n button.dataset.action = \"open-record\";\n button.dataset.id = textOf(item.id, \"\");\n button.textContent = recordName(item);\n list.appendChild(button);\n }\n if (!state.records.length) list.textContent = \"暂无\" + MODEL_LABEL;\n if (hcm.router.current().path === \"/\") hcm.router.replace(ROUTE_BASE);\n};\nconst loadRecords = async (container, hcm) => {\n setStatus(container, \"加载中\");\n showError(container, null);\n const result = await hcm.query({ model: MODEL, page: 0, size: 50 });\n state.records = result.rows;\n renderList(container, hcm);\n setStatus(container, \"已更新\");\n};\nconst openEditor = async (container, hcm, id) => {\n state.activeId = id;\n state.mode = \"edit\";\n setEditorVisible(container, true);\n setStatus(container, \"读取详情\");\n showError(container, null);\n const record = await hcm.get({ model: MODEL, id });\n setText(container, '[data-field=\"editor-title\"]', recordName(record));\n await renderForm(container, hcm, record);\n setDeleteVisible(container, true);\n setStatus(container, \"可编辑\");\n};\nconst openNew = async (container, hcm) => {\n state.activeId = \"\";\n state.mode = \"new\";\n setEditorVisible(container, true);\n showError(container, null);\n setText(container, '[data-field=\"editor-title\"]', \"新建\" + MODEL_LABEL);\n await renderForm(container, hcm, formDefaults());\n setDeleteVisible(container, false);\n setStatus(container, \"可创建\");\n};\nconst renderRoute = async (container, hcm) => {\n if (hcm.router.match(ROUTE_BASE + \"/new\")) {\n await openNew(container, hcm);\n return;\n }\n const route = hcm.router.match(ROUTE_BASE + \"/:id\");\n if (route) {\n await openEditor(container, hcm, route.params.id);\n return;\n }\n state.activeId = \"\";\n state.mode = \"list\";\n setEditorVisible(container, false);\n setText(container, '[data-field=\"editor-title\"]', \"选择\" + MODEL_LABEL);\n const fieldBox = container.querySelector('[data-field=\"editor-fields\"]');\n if (fieldBox) fieldBox.innerHTML = \"\";\n setDeleteVisible(container, false);\n renderList(container, hcm);\n};\nconst saveActive = async (container, hcm) => {\n if (state.mode !== \"new\" && !state.activeId) return;\n const meta = await ensureMeta(hcm);\n const validation = hcm.form.validate(meta, readFormValues(container), {\n ...formOptions(),\n defaults: state.mode === \"new\" ? formDefaults() : {},\n });\n if (!validation.valid) {\n showError(container, { code: \"FORM_INVALID\", message: validation.errors.map((item) => item.message).join(\"\\\\n\") });\n return;\n }\n setStatus(container, state.mode === \"new\" ? \"创建中\" : \"保存中\");\n showError(container, null);\n if (state.mode === \"new\") {\n const created = await hcm.create({ model: MODEL, data: validation.payload });\n await loadRecords(container, hcm);\n hcm.router.push(created?.id ? ROUTE_BASE + \"/\" + encodeURIComponent(created.id) : ROUTE_BASE);\n setStatus(container, \"已创建\");\n return;\n }\n await hcm.update({ model: MODEL, id: state.activeId, data: validation.payload });\n await loadRecords(container, hcm);\n hcm.router.push(ROUTE_BASE + \"/\" + encodeURIComponent(state.activeId));\n setStatus(container, \"已保存\");\n};\nconst deleteActive = async (container, hcm) => {\n if (!state.activeId) return;\n const title = container.querySelector('[data-field=\"editor-title\"]')?.textContent || state.activeId;\n const confirmed = typeof window === \"undefined\" || window.confirm(\"确定删除\" + MODEL_LABEL + \" \" + title + \"?\");\n if (!confirmed) return;\n setStatus(container, \"删除中\");\n showError(container, null);\n await hcm.remove({ model: MODEL, id: state.activeId });\n state.activeId = \"\";\n state.mode = \"list\";\n await loadRecords(container, hcm);\n hcm.router.push(ROUTE_BASE);\n setStatus(container, \"已删除\");\n};\nexport async function mount(container, hcm) {\n setText(container, '[data-field=\"tenant-id\"]', hcm.context.tenantId);\n const onClick = (event) => {\n const target = event.target instanceof Element ? event.target.closest(\"[data-action]\") : null;\n if (!target) return;\n if (target.dataset.action === \"refresh\") void loadRecords(container, hcm).catch((error) => showError(container, error));\n if (target.dataset.action === \"new-record\") hcm.router.push(ROUTE_BASE + \"/new\");\n if (target.dataset.action === \"open-record\" && target.dataset.id) hcm.router.push(ROUTE_BASE + \"/\" + encodeURIComponent(target.dataset.id));\n if (target.dataset.action === \"back\") hcm.router.push(ROUTE_BASE);\n if (target.dataset.action === \"delete-record\") {\n void deleteActive(container, hcm).catch((error) => {\n setStatus(container, \"删除失败\");\n showError(container, error);\n });\n }\n };\n const onSubmit = (event) => {\n event.preventDefault();\n void saveActive(container, hcm).catch((error) => {\n setStatus(container, \"保存失败\");\n showError(container, error);\n });\n };\n container.addEventListener(\"click\", onClick);\n container.querySelector('[data-field=\"editor-form\"]')?.addEventListener(\"submit\", onSubmit);\n state.unsubscribe = hcm.router.subscribe(() => void renderRoute(container, hcm).catch((error) => showError(container, error)));\n await ensureMeta(hcm);\n await loadRecords(container, hcm);\n await renderRoute(container, hcm);\n container.__miniCleanup = () => {\n container.removeEventListener(\"click\", onClick);\n container.querySelector('[data-field=\"editor-form\"]')?.removeEventListener(\"submit\", onSubmit);\n state.unsubscribe?.();\n };\n}\n\nexport function unmount(container) {\n container.__miniCleanup?.();\n container.innerHTML = \"\";\n}\n`;\n}\n","import type { AxiosInstance } from 'axios';\n\n/**\n * 模型缓存运维(两级缓存:Caffeine 本地 + Redis 远程)。\n *\n * 后端契约(CommonModelQueryCacheController):\n * - `GET /api/models/{model}/cache` 统计\n * - `DELETE /api/models/{model}/cache` 清全部实体缓存\n * - `DELETE /api/models/{model}/cache/{id}` 清单条\n * - `DELETE /api/models/{model}/meta/cache` 清元数据缓存(Code 级 YAML + Action 定义)\n *\n * 🔴 缓存必须走 API 管理,不直接操作 Redis —— 保证一致性、审计与业务规则生效。\n */\n\nexport interface CacheStats {\n modelKey: string;\n [k: string]: unknown;\n}\n\nexport interface CacheClearResult {\n modelKey: string;\n clearedCount?: number;\n message?: string;\n [k: string]: unknown;\n}\n\nexport async function getCacheStats(http: AxiosInstance, model: string): Promise<CacheStats> {\n const resp = await http.get(`/api/models/${model}/cache`);\n return (resp.data?.data ?? resp.data) as CacheStats;\n}\n\nexport async function clearModelCache(\n http: AxiosInstance,\n model: string,\n id?: string,\n): Promise<CacheClearResult> {\n const url = id ? `/api/models/${model}/cache/${id}` : `/api/models/${model}/cache`;\n const resp = await http.delete(url);\n return (resp.data?.data ?? resp.data) as CacheClearResult;\n}\n\nexport interface ClearMetaCacheOpts {\n /** 元数据类型(list / info / view / filter / page…),省略 = 全清 */\n type?: string;\n /** 状态变体(如 self),配合 type 使用 */\n state?: string;\n}\n\nexport async function clearMetaCache(\n http: AxiosInstance,\n model: string,\n opts: ClearMetaCacheOpts = {},\n): Promise<CacheClearResult> {\n const params: Record<string, string> = {};\n if (opts.type) params.type = opts.type;\n if (opts.state) params.state = opts.state;\n const resp = await http.delete(`/api/models/${model}/meta/cache`, { params });\n return (resp.data?.data ?? resp.data) as CacheClearResult;\n}\n","import type { AxiosInstance } from 'axios';\n\n/**\n * 租户开通(bootstrap)—— 交付旅程第一步。\n *\n * 后端契约(TenantBootstrapController):\n * - `POST /api/bootstrap/tenant`,header `X-Bootstrap-Key: <secret>`\n * - body `{ tenantId, adminUsername, adminPassword, tenantName? }`\n *\n * 🔴 该端点不走普通 Bearer 认证,用独立的 bootstrap key —— 它要在「还没有任何\n * 租户和用户」的时刻可用。key 由部署方持有,绝不写进 skill 正文或提交进仓库。\n */\n\nexport interface BootstrapTenantRequest {\n tenantId: string;\n adminUsername: string;\n adminPassword: string;\n tenantName?: string;\n}\n\nexport interface BootstrapReconcileResult {\n component?: string;\n version?: number;\n status?: string;\n [k: string]: unknown;\n}\n\nexport interface BootstrapTenantResult {\n success: boolean;\n tenantId: string;\n adminUserId?: string;\n message?: string;\n results?: BootstrapReconcileResult[];\n}\n\nexport async function bootstrapTenant(\n http: AxiosInstance,\n bootstrapKey: string,\n req: BootstrapTenantRequest,\n): Promise<BootstrapTenantResult> {\n const resp = await http.post('/api/bootstrap/tenant', req, {\n headers: { 'X-Bootstrap-Key': bootstrapKey },\n });\n return (resp.data?.data ?? resp.data) as BootstrapTenantResult;\n}\n","import type { AxiosInstance } from 'axios';\n\n/**\n * 租户元数据读写(TenantMetaController,`/api/tenant-meta`)。\n *\n * 统一走 query-param 形态(`?path=`)而非路径通配 `/meta/**`:meta 路径含斜杠与点,\n * query param 避免 URL 段解析歧义与路径穿越面。\n *\n * 后端契约:\n * - `GET /api/tenant-meta/list?keyword=&type=&page=&pageSize=`\n * - `GET /api/tenant-meta/meta?path=<metaName>`\n * - `PUT /api/tenant-meta/meta?path=<metaName>` body `{ content: \"<原文>\" }`\n * - `DELETE /api/tenant-meta/meta?path=<metaName>`\n *\n * 🔴 写入需 config-admin 身份,且 source 必须可写(租户开发空间绑定态)——\n * 后端 `assertConfigAdmin` / `assertWritableSource` 会拒绝越权与只读源。\n */\n\nexport interface TenantMetaEntry {\n metaName?: string;\n name?: string;\n type?: string;\n lastModified?: string;\n size?: number;\n [k: string]: unknown;\n}\n\nexport interface ListTenantMetaOpts {\n keyword?: string;\n /** yml / properties / all(默认 all) */\n type?: string;\n page?: number;\n pageSize?: number;\n}\n\nexport async function listTenantMeta(\n http: AxiosInstance,\n opts: ListTenantMetaOpts = {},\n): Promise<{ items: TenantMetaEntry[]; total?: number; raw: unknown }> {\n const resp = await http.get('/api/tenant-meta/list', { params: opts });\n const payload = resp.data?.data ?? resp.data;\n const items = (payload?.items ?? payload?.list ?? payload?.records ?? []) as TenantMetaEntry[];\n return { items, total: payload?.total, raw: payload };\n}\n\n/** 读单份 meta 原文。返回 `content` 字符串(后端包在信封里时自动解包)。 */\nexport async function getTenantMeta(http: AxiosInstance, path: string): Promise<string> {\n const resp = await http.get('/api/tenant-meta/meta', { params: { path } });\n const payload = resp.data?.data ?? resp.data;\n if (typeof payload === 'string') return payload;\n return String(payload?.content ?? '');\n}\n\nexport interface SaveMetaResult {\n success?: boolean;\n message?: string;\n [k: string]: unknown;\n}\n\nexport async function saveTenantMeta(\n http: AxiosInstance,\n path: string,\n content: string,\n): Promise<SaveMetaResult> {\n const resp = await http.put('/api/tenant-meta/meta', { content }, { params: { path } });\n return (resp.data?.data ?? resp.data) as SaveMetaResult;\n}\n\nexport async function deleteTenantMeta(\n http: AxiosInstance,\n path: string,\n): Promise<SaveMetaResult> {\n const resp = await http.delete('/api/tenant-meta/meta', { params: { path } });\n return (resp.data?.data ?? resp.data) as SaveMetaResult;\n}\n","import type { AxiosInstance } from 'axios';\nimport yaml, { JSON_SCHEMA } from 'js-yaml';\nimport { CliError } from './errors/cli-error';\nimport { CliErrorCode } from './errors/codes';\n\n/**\n * 睿戎 Chiron 交付技能库客户端。\n *\n * <p>🔴 这些端点**免认证**——交付顾问接手的是「还没有租户、还没有账号」的空环境,\n * 必须先装技能才能建租户。所以本模块只需要 endpoint,不碰 identity/token。\n *\n * 后端契约(ChironController):\n * - `GET {base}/skills.json` 目录 manifest\n * - `GET {base}/skills/{id}.md` 技能原文\n * - `GET {base}/skills/{id}/references/{path}` 技能自带的参考文件\n * - `GET {base}/skills/{id}/files?path=...` 完整 Skill 目录中的原始文件\n *\n * 🔴 `{base}` 是 `'/api'` 或 `''`,由 {@link resolveChironBase} 探出来。真实部署只把\n * `/api/**` 路由给后端,根路径归前端 SPA——`GET /skills.json` 在客户环境上拿回来的是\n * 一段 HTML。而老版本后端只有根路径。两种都要能连上,所以先探再用,不写死。\n */\n\nexport interface ChironSkillFile {\n path: string;\n bytes: number;\n sha256: string;\n}\n\nexport interface ChironSkill {\n id: string;\n title: string;\n category: string;\n /** cli = 动作面已全改写为 hcm 命令;curl = 仍为原始 HTTP 形态 */\n actionSurface: 'cli' | 'curl';\n description: string;\n /** 交付生命周期阶段(多值)——一个技能天然跨阶段 */\n stages: string[];\n /** 安装本技能前必须同时安装的其他 Chiron 技能 id;旧服务端缺省时视为空。 */\n requires?: string[];\n bytes: number;\n /** 新服务端提供完整 Skill 目录;旧服务端缺省时只消费 SKILL.md。 */\n files?: ChironSkillFile[];\n markdownUrl: string;\n /**\n * 技能自带的参考文件相对路径(相对 `<skill>/references/`)。SKILL.md 正文用\n * `[x](references/x.md)` 引它们——不跟着装下来就是一堆死链。旧服务端缺省时视为空。\n */\n references?: string[];\n}\n\nexport interface ChironStage {\n key: string;\n name: string;\n description: string;\n /** 该阶段下的技能数。0 = 交付路径上的覆盖缺口 */\n skillCount: number;\n}\n\nexport interface ChironCategory {\n key: string;\n name: string;\n description: string;\n skills: ChironSkill[];\n}\n\nexport interface ChironManifest {\n name: string;\n title: string;\n tagline: string;\n skillCount: number;\n stages: ChironStage[];\n categories: ChironCategory[];\n}\n\n/** 技能库端点的前缀候选,按优先级排列。`/api` 在前:它是真实部署下唯一到得了后端的那条。 */\nconst CHIRON_BASE_CANDIDATES = ['/api', ''] as const;\n\nfunction looksLikeManifest(data: unknown): data is ChironManifest {\n if (!data || typeof data !== 'object') return false;\n const m = data as Record<string, unknown>;\n return m.name === 'chiron' && Array.isArray(m.categories);\n}\n\n/**\n * 探出本环境技能库的前缀。\n *\n * 为什么不能写死任何一个:\n * - 真实部署(反代把 `/api/**` 给后端、其余给前端)下根路径返回的是前端 SPA 的 HTML,\n * HTTP 200 但根本不是 manifest —— 所以判据必须看**内容**,不能看状态码;\n * - 还没升级到带 `/api` 别名那一版的后端,只有根路径可用。\n *\n * 两条都探不到时抛出可执行的错误,而不是把一段 HTML 当目录解析出满屏乱码。\n */\nexport async function resolveChironBase(http: AxiosInstance): Promise<string> {\n const tried: string[] = [];\n for (const base of CHIRON_BASE_CANDIDATES) {\n const url = `${base}/skills.json`;\n tried.push(url);\n try {\n const resp = await http.get(url);\n const data = resp.data?.data ?? resp.data;\n if (looksLikeManifest(data)) return base;\n } catch {\n // 401 / 404 / 网络错误都只说明这条前缀不通,继续探下一条\n }\n }\n throw new CliError({\n code: CliErrorCode.INVALID_ARGUMENT,\n message:\n `这个环境上找不到睿戎技能库(试过 ${tried.join(' 和 ')})。`\n + '常见原因:① 后端版本还没有 /api/skills 端点,升级后端即可;'\n + '② endpoint 填的是前端地址且未反代到后端;③ 地址或网络不通。',\n });\n}\n\nexport async function fetchSkillCatalog(http: AxiosInstance, base: string): Promise<ChironManifest> {\n const resp = await http.get(`${base}/skills.json`);\n return (resp.data?.data ?? resp.data) as ChironManifest;\n}\n\nexport async function fetchSkillMarkdown(http: AxiosInstance, id: string, base: string): Promise<string> {\n const resp = await http.get(`${base}/skills/${encodeURIComponent(id)}.md`, {\n responseType: 'text',\n transformResponse: [(d) => d],\n });\n return String(resp.data ?? '');\n}\n\n/** 取完整 Skill 目录中的任意原始文件;`relativePath` 始终按 query 参数传递。 */\nexport async function fetchSkillFile(\n http: AxiosInstance,\n id: string,\n relativePath: string,\n base: string,\n): Promise<Buffer> {\n assertSafeSkillId(id);\n assertSafeSkillFilePath(relativePath);\n const resp = await http.get(`${base}/skills/${encodeURIComponent(id)}/files`, {\n params: { path: relativePath },\n responseType: 'arraybuffer',\n transformResponse: [(data) => data],\n });\n return Buffer.from(resp.data ?? []);\n}\n\n/** 取技能自带的参考文件原文。`relativePath` 相对 `<skill>/references/`。 */\nexport async function fetchSkillReference(\n http: AxiosInstance,\n id: string,\n relativePath: string,\n base: string,\n): Promise<string> {\n assertSafeReferencePath(relativePath);\n const encoded = relativePath.split('/').map(encodeURIComponent).join('/');\n const resp = await http.get(`${base}/skills/${encodeURIComponent(id)}/references/${encoded}`, {\n responseType: 'text',\n transformResponse: [(d) => d],\n });\n return String(resp.data ?? '');\n}\n\n/**\n * 参考文件相对路径约束——与后端 `ChironCatalogService.SAFE_REFERENCE_PATH` 同形。\n *\n * 🔴 服务端返回什么,本地就按什么建目录,所以这里是**写文件前**的闸:`..` 段会让\n * 安装写出技能目录之外。不要因为「服务端已经校验过」就省掉——CLI 可以指向任何 endpoint。\n */\nexport function assertSafeReferencePath(relativePath: string): void {\n const ok = /^[A-Za-z0-9_][A-Za-z0-9._-]{0,63}(\\/[A-Za-z0-9_][A-Za-z0-9._-]{0,63}){0,2}$/\n .test(relativePath);\n if (!ok) {\n throw invalid(`参考文件路径不合法:${JSON.stringify(relativePath)}`);\n }\n}\n\n/** 完整 Skill 目录相对路径约束;允许有限深度之外的目录,但拒绝任何穿越和反斜线。 */\nexport function assertSafeSkillFilePath(relativePath: string): void {\n const segments = relativePath?.split('/') ?? [];\n if (\n !relativePath ||\n relativePath.length > 512 ||\n relativePath.startsWith('/') ||\n relativePath.includes('\\\\') ||\n segments.some(\n (segment) =>\n !segment ||\n segment === '.' ||\n segment === '..' ||\n !/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(segment),\n )\n ) {\n throw invalid(`非法 Skill 文件路径: \"${relativePath}\"`);\n }\n}\n\n/** 归一 references 字段:旧服务端缺省时视为空,逐条过白名单。 */\nexport function normalizeReferences(raw: unknown, skillId: string): string[] {\n if (raw === undefined || raw === null) return [];\n if (!Array.isArray(raw)) throw invalid(`技能 ${skillId} 的 references 只能是字符串数组`);\n const out: string[] = [];\n for (const value of raw) {\n if (typeof value !== 'string' || !value.trim()) {\n throw invalid(`技能 ${skillId} 的 references 只能是非空字符串数组`);\n }\n const rel = value.trim();\n assertSafeReferencePath(rel);\n if (!out.includes(rel)) out.push(rel);\n }\n return out;\n}\n\nexport function flattenSkills(manifest: ChironManifest): ChironSkill[] {\n return (manifest.categories ?? []).flatMap((c) => c.skills ?? []);\n}\n\n/**\n * 计算技能安装闭包,返回稳定的「依赖在前、目标在后」顺序。\n *\n * 这是纯函数:远程安装与测试共用,不在这里发请求或写文件。旧目录没有 requires 时\n * 自然退化为只安装目标技能。\n */\nexport function resolveSkillInstallOrder(manifest: ChironManifest, targetId: string): ChironSkill[] {\n const byId = new Map<string, ChironSkill>();\n for (const skill of flattenSkills(manifest)) {\n if (byId.has(skill.id)) {\n throw invalid(`技能目录存在重复 id: ${skill.id}`);\n }\n byId.set(skill.id, skill);\n }\n if (!byId.has(targetId)) {\n throw invalid(`技能目录中不存在 ${targetId}`);\n }\n\n const state = new Map<string, 'visiting' | 'done'>();\n const path: string[] = [];\n const order: ChironSkill[] = [];\n\n const visit = (id: string, requiredBy?: string): void => {\n const current = state.get(id);\n if (current === 'done') return;\n if (current === 'visiting') {\n const cycleStart = path.indexOf(id);\n const cycle = [...path.slice(Math.max(0, cycleStart)), id];\n throw invalid(`技能依赖存在环: ${cycle.join(' -> ')}`);\n }\n\n const skill = byId.get(id);\n if (!skill) {\n throw invalid(`技能 ${requiredBy ?? targetId} 依赖不存在: ${id}`);\n }\n assertSafeSkillId(skill.id);\n state.set(id, 'visiting');\n path.push(id);\n for (const required of normalizeRequirements(skill.requires, skill.id)) {\n visit(required, skill.id);\n }\n path.pop();\n state.set(id, 'done');\n order.push(skill);\n };\n\n visit(targetId);\n return order;\n}\n\n/**\n * 全目录的安装顺序:逐个技能求依赖闭包后合并去重,依赖仍排在被依赖者之前。\n *\n * `install --all` 用它。顾问不必知道谁依赖谁,也不该为了装全而写 shell 循环——\n * 那个循环在 zsh 下 `for id in $(...)` 不分词,会一个都不装却退出码 0。\n */\nexport function resolveAllSkillsInstallOrder(manifest: ChironManifest): ChironSkill[] {\n const order = new Map<string, ChironSkill>();\n const all = flattenSkills(manifest).map((skill) => skill.id).sort();\n for (const id of all) {\n for (const skill of resolveSkillInstallOrder(manifest, id)) {\n if (!order.has(skill.id)) order.set(skill.id, skill);\n }\n }\n return [...order.values()];\n}\n\n/** 读 SKILL.md 的 frontmatter;没有 frontmatter 返回空对象,YAML 非法则报错。 */\nexport function parseSkillFrontmatter(markdown: string, skillId: string): Record<string, unknown> {\n const match = /^---\\r?\\n([\\s\\S]*?)\\r?\\n---(?:\\r?\\n|$)/.exec(markdown);\n if (!match) return {};\n\n let frontmatter: unknown;\n try {\n frontmatter = yaml.load(match[1] ?? '', { schema: JSON_SCHEMA });\n } catch (cause) {\n throw new CliError({\n code: CliErrorCode.INVALID_ARGUMENT,\n message: `技能 ${skillId} 的 frontmatter 不是合法 YAML`,\n cause,\n });\n }\n if (!frontmatter || typeof frontmatter !== 'object' || Array.isArray(frontmatter)) return {};\n return frontmatter as Record<string, unknown>;\n}\n\n/** 从一份 SKILL.md 读取本地安装所需的 requires,不接受正文中的近似语法。 */\nexport function parseSkillRequirements(markdown: string, skillId: string): string[] {\n return normalizeRequirements(parseSkillFrontmatter(markdown, skillId).requires, skillId);\n}\n\n/** Chiron 与公开端点共用的小写安全 id 约束。 */\nexport function assertSafeSkillId(id: string): void {\n if (!/^[a-z0-9][a-z0-9-]{0,63}$/.test(id)) {\n throw invalid(`技能 id 只能是小写字母/数字/连字符,收到 \"${id}\"`);\n }\n}\n\nfunction normalizeRequirements(raw: unknown, skillId: string): string[] {\n if (raw === undefined || raw === null) return [];\n const values = Array.isArray(raw) ? raw : [raw];\n const normalized: string[] = [];\n for (const value of values) {\n if (typeof value !== 'string' || !value.trim()) {\n throw invalid(`技能 ${skillId} 的 requires 只能是非空技能 id 数组`);\n }\n const id = value.trim();\n assertSafeSkillId(id);\n if (!normalized.includes(id)) normalized.push(id);\n }\n return normalized;\n}\n\nfunction invalid(message: string): CliError {\n return new CliError({ code: CliErrorCode.INVALID_ARGUMENT, message });\n}\n\n/** 按交付生命周期阶段筛选。 */\nexport function filterByStage(skills: ChironSkill[], stage?: string): ChironSkill[] {\n if (!stage) return skills;\n return skills.filter((s) => (s.stages ?? []).includes(stage));\n}\n\n/** 关键词匹配 id / 标题 / 描述 / 分类,全部小写包含。 */\nexport function matchSkills(skills: ChironSkill[], keyword?: string): ChironSkill[] {\n if (!keyword) return skills;\n const k = keyword.toLowerCase();\n return skills.filter((s) =>\n [s.id, s.title, s.description, s.category].some((f) => String(f ?? '').toLowerCase().includes(k)),\n );\n}\n","import type { AxiosInstance } from 'axios';\n\n/**\n * 租户系统参数(配置中心)。\n *\n * <p>后端契约(TenantSettingDomainController):\n * <ul>\n * <li>`GET /api/tenant-settings/domains/{domain}` 读某域(定义 + 租户覆盖值合并,缺失回退默认值)</li>\n * <li>`PATCH /api/tenant-settings/domains/{domain}` 写,body `{items:[{namespace,settingKey,value}]}`</li>\n * <li>`DELETE /api/tenant-settings/domains/{domain}/items/{ns}/{key}?revision=N` 清除租户覆盖</li>\n * </ul>\n *\n * <p>🔴 **不要走标准 Model 的 PUT 改这些值**:那条路直接写库,绕过定义校验、\n * 乐观锁与历史留痕——改坏 namespace/valueType 会让记录不再匹配任何定义。\n * 后端已从该 Model 的权限里移除 update action,这里也只暴露上面三条受控路径。\n */\n\nexport interface SettingItem {\n namespace: string;\n settingKey: string;\n value?: unknown;\n valueType?: string;\n defaultValue?: unknown;\n revision?: number;\n label?: string;\n [k: string]: unknown;\n}\n\nexport async function getSettingDomain(\n http: AxiosInstance,\n domain: string,\n): Promise<{ items: SettingItem[]; raw: unknown }> {\n const resp = await http.get(`/api/tenant-settings/domains/${encodeURIComponent(domain)}`);\n const payload = resp.data?.data ?? resp.data;\n const items = (payload?.items ?? payload ?? []) as SettingItem[];\n return { items: Array.isArray(items) ? items : [], raw: payload };\n}\n\nexport interface SettingWrite {\n namespace: string;\n settingKey: string;\n value: unknown;\n}\n\nexport async function patchSettingDomain(\n http: AxiosInstance,\n domain: string,\n items: SettingWrite[],\n): Promise<unknown> {\n const resp = await http.patch(`/api/tenant-settings/domains/${encodeURIComponent(domain)}`, { items });\n return resp.data?.data ?? resp.data;\n}\n\nexport async function resetSettingItem(\n http: AxiosInstance,\n domain: string,\n namespace: string,\n settingKey: string,\n revision?: number,\n): Promise<unknown> {\n const url =\n `/api/tenant-settings/domains/${encodeURIComponent(domain)}` +\n `/items/${encodeURIComponent(namespace)}/${encodeURIComponent(settingKey)}`;\n const resp = await http.delete(url, {\n params: revision === undefined ? {} : { revision },\n });\n return resp.data?.data ?? resp.data;\n}\n\n/**\n * 解析 `namespace.settingKey=value` 形态的 --set 参数。\n *\n * settingKey 取最后一段,其余是 namespace —— namespace 本身含点\n * (如 `security.password`),所以从右边切,不能 split('.') 取前两段。\n */\nexport function parseSettingAssignment(raw: string): SettingWrite {\n const eq = raw.indexOf('=');\n if (eq <= 0) throw new Error(`--set 需要 namespace.key=value 形态,收到 \"${raw}\"`);\n const path = raw.slice(0, eq).trim();\n const value = raw.slice(eq + 1);\n const dot = path.lastIndexOf('.');\n if (dot <= 0) throw new Error(`--set 的键需含 namespace,如 security.password.passwordPolicy=complex,收到 \"${path}\"`);\n return { namespace: path.slice(0, dot), settingKey: path.slice(dot + 1), value };\n}\n"],"mappings":";AAAA,SAAS,mBAAmB;AASrB,SAAS,iBAAyB;AACvC,QAAM,UAAU,YAAY,EAAE,EAAE,SAAS,KAAK;AAC9C,QAAM,SAAS,YAAY,CAAC,EAAE,SAAS,KAAK;AAC5C,SAAO,MAAM,OAAO,IAAI,MAAM;AAChC;;;ACbA,OAAO,WAAmC;;;ACiBnC,SAAS,YAAY,QAA6B;AACvD,SAAO,aAAa,QAAQ,IAAI,CAAC,QAAoC;AACnE,QAAI,QAAQ,IAAI,eAAe,eAAe,CAAC;AAC/C,WAAO;AAAA,EACT,CAAC;AACH;AAEO,SAAS,WAAW,QAAuB,UAA8C;AAC9F,SAAO,aAAa,QAAQ,IAAI,OAAO,QAAQ;AAC7C,QAAI,CAAC,IAAI,QAAQ,IAAI,eAAe,GAAG;AACrC,YAAM,MAAM,MAAM,SAAS;AAC3B,UAAI,IAAK,KAAI,QAAQ,IAAI,iBAAiB,UAAU,GAAG,EAAE;AAAA,IAC3D;AACA,WAAO;AAAA,EACT,CAAC;AACH;AAEO,SAAS,YAAY,QAAuB,WAAsC;AACvF,SAAO,aAAa,SAAS;AAAA,IAC3B,CAAC,SAAS;AAAA,IACV,OAAO,UAAU;AACf,YAAM,SAAS,MAAM,UAAU;AAC/B,YAAM,MAAM,MAAM;AAClB,UAAI,CAAC,IAAK,OAAM;AAIhB,UAAI,WAAW,OAAO,CAAC,IAAI,aAAa,CAAC,IAAI,iBAAiB;AAC5D,YAAI,YAAY;AAChB,YAAI;AACF,gBAAM,UAAU;AAAA,QAClB,QAAQ;AACN,gBAAM;AAAA,QACR;AAEA,YAAI,QAAQ,OAAO,eAAe;AAClC,eAAO,OAAO,QAAQ,GAAG;AAAA,MAC3B;AAGA,UAAI,UAAU,UAAU,OAAO,SAAS,KAAK;AAE3C,cAAM,WAAY,IAAY,cAAc;AAC5C,YAAI,WAAW,GAAG;AAEhB,UAAC,IAAY,aAAa,WAAW;AACrC,gBAAM,QAAQ,MAAM,KAAK,IAAI,GAAG,QAAQ;AACxC,gBAAM,IAAI,QAAQ,CAAC,MAAM,WAAW,GAAG,KAAK,CAAC;AAE7C,cAAI,QAAQ,OAAO,eAAe;AAClC,iBAAO,OAAO,QAAQ,GAAG;AAAA,QAC3B;AAAA,MACF;AACA,YAAM;AAAA,IACR;AAAA,EACF;AACF;;;ACzEA,SAAS,oBAAoB;AAM7B,IAAM,WAAW,KAAK;AAAA,EACpB,aAAa,IAAI,IAAI,mBAAmB,YAAY,GAAG,GAAG,MAAM;AAClE;AAEO,IAAM,cAAc,SAAS;;;AFE7B,SAAS,iBAAiB,MAAwC;AACvE,QAAM,SAAS,MAAM,OAAO;AAAA,IAC1B,SAAS,KAAK;AAAA,IACd,SAAS,KAAK,aAAa;AAAA,IAC3B,SAAS,EAAE,cAAc,KAAK,aAAa,aAAa,WAAW,GAAG;AAAA,EACxE,CAAC;AACD,cAAY,MAAM;AAClB,aAAW,QAAQ,KAAK,cAAc;AACtC,cAAY,QAAQ,KAAK,SAAS;AAClC,SAAO;AACT;;;AGtBA,YAAY,QAAQ;;;ACApB,YAAY,UAAU;AACtB,YAAY,QAAQ;AAEb,SAAS,eAAuB;AACrC,QAAM,MAAM,QAAQ,IAAI;AACxB,MAAI,IAAK,QAAY,UAAK,KAAK,KAAK;AACpC,QAAM,OAAO,QAAQ,IAAI,QAAW,WAAQ;AAC5C,SAAY,UAAK,MAAM,WAAW,KAAK;AACzC;AAEO,SAAS,WAAW,SAAyB;AAClD,SAAY,UAAK,aAAa,GAAG,OAAO;AAC1C;AAEO,SAAS,YAAY,SAAyB;AACnD,SAAY,UAAK,WAAW,OAAO,GAAG,aAAa;AACrD;AAEO,SAAS,gBAAgB,SAAyB;AACvD,SAAY,UAAK,WAAW,OAAO,GAAG,kBAAkB;AAC1D;AAEO,SAAS,mBAA2B;AACzC,SAAY,UAAK,aAAa,GAAG,YAAY;AAC/C;AAEO,SAAS,sBAAsB,SAAyB;AAC7D,SAAY,UAAK,WAAW,OAAO,GAAG,wBAAwB;AAChE;AAEO,SAAS,gBAAgB,SAAyB;AACvD,SAAY,UAAK,WAAW,OAAO,GAAG,cAAc;AACtD;AAEO,SAAS,UAAkB;AAChC,SAAY,UAAK,aAAa,GAAG,MAAM;AACzC;AAEO,SAAS,OAAO,MAAsB;AAC3C,SAAY,UAAK,QAAQ,GAAG,IAAI;AAClC;AAEO,SAAS,QAAQ,MAAsB;AAC5C,SAAY,UAAK,OAAO,IAAI,GAAG,SAAS;AAC1C;AAEO,SAAS,gBAAwB;AACtC,SAAY,UAAK,aAAa,GAAG,YAAY;AAC/C;AAEO,SAAS,YAAY,MAAsB;AAChD,SAAY,UAAK,cAAc,GAAG,IAAI;AACxC;AAEO,SAAS,iBAAiB,MAAsB;AACrD,SAAY,UAAK,YAAY,IAAI,GAAG,kBAAkB;AACxD;AAEO,SAAS,aAAqB;AACnC,SAAY,UAAK,aAAa,GAAG,UAAU;AAC7C;;;AD/BO,SAAS,uBAAuB,QAA6B;AAClE,SAAO,WAAW,cAAc,WAAW;AAC7C;AAoBA,IAAM,iBAAiB;AAOhB,IAAM,aAAN,MAAM,YAAW;AAAA,EACtB,YAAoB,SAAiB;AAAjB;AAAA,EAAkB;AAAA,EAAlB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUpB,OAAO,YAAY,MAA0B;AAC3C,UAAM,KAAK,IAAI,YAAW,IAAI;AAI9B,WAAO,eAAe,IAAI,cAAc,EAAE,OAAO,MAAM,UAAU,OAAO,cAAc,MAAM,CAAC;AAC7F,WAAO;AAAA,EACT;AAAA,EAEA,IAAY,aAAsB;AAEhC,WAAQ,KAA4B,eAAe;AAAA,EACrD;AAAA,EAEQ,WAAmB;AACzB,WAAO,KAAK,aAAa,iBAAiB,KAAK,OAAO,IAAI,gBAAgB,KAAK,OAAO;AAAA,EACxF;AAAA,EAEQ,UAAkB;AACxB,WAAO,KAAK,aAAa,YAAY,KAAK,OAAO,IAAI,WAAW,KAAK,OAAO;AAAA,EAC9E;AAAA,EAEQ,wBAAgC;AACtC,WAAO,KAAK,aAAa,YAAY,KAAK,OAAO,KAAK,KAAK;AAAA,EAC7D;AAAA,EAEA,MAAM,UAAU,QAAoC;AAClD,UAAS,SAAM,KAAK,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;AAClD,UAAS,aAAU,KAAK,SAAS,GAAG,KAAK,UAAU,QAAQ,MAAM,CAAC,GAAG,EAAE,MAAM,IAAM,CAAC;AAAA,EACtF;AAAA,EAEA,MAAM,WAAwC;AAC5C,QAAI;AACF,YAAM,MAAM,MAAS,YAAS,KAAK,SAAS,GAAG,OAAO;AACtD,aAAO,KAAK,MAAM,GAAG;AAAA,IACvB,SAAS,GAAG;AACV,UAAK,EAA4B,SAAS,SAAU,QAAO;AAC3D,YAAM;AAAA,IACR;AAAA,EACF;AAAA,EAEA,MAAM,eAAe,OAAgC;AACnD,QAAI,MAAM,KAAK,eAAe,GAAG;AAC/B,YAAM,SAAS,MAAM,WAAW;AAChC,UAAI,CAAC,OAAQ,QAAO,KAAK,eAAe,UAAU,KAAK;AACvD,YAAM,UAAU,GAAG,KAAK,sBAAsB,CAAC;AAC/C,YAAM,OAAO,YAAY,gBAAgB,SAAS,KAAK;AACvD,aAAO,YAAY,cAAc,IAAI,OAAO;AAAA,IAC9C;AAEA,WAAO,KAAK,eAAe,UAAU,KAAK;AAAA,EAC5C;AAAA,EAEA,MAAM,gBAAgB,OAAgC;AACpD,QAAI,MAAM,KAAK,eAAe,GAAG;AAC/B,YAAM,SAAS,MAAM,WAAW;AAChC,UAAI,CAAC,OAAQ,QAAO,KAAK,eAAe,WAAW,KAAK;AACxD,YAAM,UAAU,GAAG,KAAK,sBAAsB,CAAC;AAC/C,YAAM,OAAO,YAAY,gBAAgB,SAAS,KAAK;AACvD,aAAO,YAAY,cAAc,IAAI,OAAO;AAAA,IAC9C;AACA,WAAO,KAAK,eAAe,WAAW,KAAK;AAAA,EAC7C;AAAA,EAEA,MAAM,iBAAyC;AAE7C,QAAI,QAAQ,IAAI,QAAS,QAAO,QAAQ,IAAI;AAC5C,QAAI,QAAQ,IAAI,iBAAkB,QAAO,QAAQ,IAAI;AAErD,UAAM,OAAO,MAAM,KAAK,SAAS;AACjC,QAAI,CAAC,KAAM,QAAO;AAClB,WAAO,KAAK,WAAW,KAAK,cAAc;AAAA,EAC5C;AAAA,EAEA,MAAM,kBAA0C;AAC9C,UAAM,OAAO,MAAM,KAAK,SAAS;AACjC,QAAI,CAAC,MAAM,gBAAiB,QAAO;AACnC,WAAO,KAAK,WAAW,KAAK,eAAe;AAAA,EAC7C;AAAA,EAEA,MAAM,QAAuB;AAC3B,UAAM,OAAO,MAAM,KAAK,SAAS;AACjC,QAAI,CAAC,KAAM;AAEX,QAAI,KAAK,gBAAgB,WAAW,WAAW,GAAG;AAChD,YAAM,UAAU,KAAK,eAClB,MAAM,YAAY,MAAM,EACxB,MAAM,GAAG,EACT,MAAM,CAAC,EACP,KAAK,GAAG;AACX,YAAM,SAAS,MAAM,WAAW;AAChC,UAAI,OAAQ,OAAM,OAAO,eAAe,gBAAgB,OAAO;AAAA,IACjE,WAAW,KAAK,gBAAgB,WAAW,OAAO,GAAG;AACnD,YAAS,MAAG,KAAK,eAAe,MAAM,QAAQ,MAAM,GAAG,EAAE,OAAO,KAAK,CAAC;AAAA,IACxE;AACA,QAAI,KAAK,iBAAiB,WAAW,WAAW,GAAG;AACjD,YAAM,UAAU,KAAK,gBAClB,MAAM,YAAY,MAAM,EACxB,MAAM,GAAG,EACT,MAAM,CAAC,EACP,KAAK,GAAG;AACX,YAAM,SAAS,MAAM,WAAW;AAChC,UAAI,OAAQ,OAAM,OAAO,eAAe,gBAAgB,OAAO;AAAA,IACjE,WAAW,KAAK,iBAAiB,WAAW,OAAO,GAAG;AACpD,YAAS,MAAG,KAAK,gBAAgB,MAAM,QAAQ,MAAM,GAAG,EAAE,OAAO,KAAK,CAAC;AAAA,IACzE;AAIA,QAAI,CAAC,KAAK,YAAY;AACpB,YAAS,MAAG,KAAK,SAAS,GAAG,EAAE,OAAO,KAAK,CAAC;AAAA,IAC9C;AAAA,EACF;AAAA,EAEA,MAAc,WAAW,KAAqC;AAC5D,QAAI,IAAI,WAAW,WAAW,GAAG;AAC/B,YAAM,UAAU,IAAI,MAAM,YAAY,MAAM,EAAE,MAAM,GAAG,EAAE,MAAM,CAAC,EAAE,KAAK,GAAG;AAC1E,YAAM,SAAS,MAAM,WAAW;AAChC,UAAI,CAAC,OAAQ,QAAO;AACpB,aAAO,OAAO,YAAY,gBAAgB,OAAO;AAAA,IACnD;AACA,QAAI,IAAI,WAAW,OAAO,GAAG;AAC3B,UAAI;AACF,eAAO,MAAS,YAAS,IAAI,MAAM,QAAQ,MAAM,GAAG,OAAO;AAAA,MAC7D,SAAS,GAAG;AACV,YAAK,EAA4B,SAAS,SAAU,QAAO;AAC3D,cAAM;AAAA,MACR;AAAA,IACF;AACA,QAAI,IAAI,WAAW,QAAQ,EAAG,QAAO,IAAI,MAAM,SAAS,MAAM;AAC9D,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,iBAAmC;AAC/C,UAAM,SAAS,MAAM,WAAW;AAChC,QAAI,CAAC,OAAQ,QAAO;AACpB,QAAI;AAEF,YAAM,OAAO,YAAY,gBAAgB,WAAW;AACpD,aAAO;AAAA,IACT,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEA,MAAc,eAAe,MAA4B,OAAgC;AACvF,UAAM,OAAO,GAAG,KAAK,SAAS,CAAC,IAAI,IAAI;AACvC,UAAS,SAAM,KAAK,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;AAClD,UAAS,aAAU,MAAM,OAAO,EAAE,MAAM,IAAM,CAAC;AAC/C,QAAI,SAAS,UAAU;AAErB,cAAQ,KAAK,0DAA0D,IAAI,UAAU;AAAA,IACvF;AACA,WAAO,QAAQ,IAAI;AAAA,EACrB;AACF;AAEA,eAAe,aAA2C;AACxD,MAAI;AACF,UAAM,MAAO,MAAM,OAAO,QAAQ;AAGlC,WAAO,IAAI,WAAW;AAAA,EACxB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;;;AErMO,SAAS,YAAY,KAAqC;AAC/D,MAAI,CAAC,OAAO,OAAO,QAAQ,SAAU,QAAO;AAC5C,QAAM,IAAI;AACV,QAAM,YAAuB,CAAC;AAE9B,MAAI,EAAE,aAAa,UAAa,EAAE,aAAa,KAAM,WAAU,WAAW,OAAO,EAAE,QAAQ;AAC3F,MAAI,OAAO,EAAE,aAAa,YAAY,EAAE,SAAU,WAAU,WAAW,EAAE;AACzE,MAAI,MAAM,QAAQ,EAAE,KAAK,EAAG,WAAU,QAAQ,EAAE,MAAM,IAAI,MAAM;AAChE,SAAO,OAAO,KAAK,SAAS,EAAE,SAAS,IAAI,YAAY;AACzD;AAGO,SAAS,kBAAkB,WAA0C;AAC1E,MAAI,CAAC,UAAW,QAAO;AACvB,QAAM,QAAkB,CAAC;AACzB,QAAM,KAAK,UAAU,UAAU,YAAY,gBAAM,EAAE;AACnD,QAAM,KAAK,QAAQ,UAAU,YAAY,gBAAM,EAAE;AACjD,MAAI,UAAU,OAAO,OAAQ,OAAM,KAAK,SAAS,UAAU,MAAM,KAAK,GAAG,CAAC,EAAE;AAC5E,SAAO,MAAM,KAAK,GAAG;AACvB;;;AC3CO,IAAM,8BAA8B,KAAK,KAAK;AAErD,eAAsB,cAAc,OAAkD;AACpF,QAAM,OAAO,MAAM,MAAM,WAAW;AAAA,IAClC,GAAG,MAAM,QAAQ;AAAA,IACjB;AAAA,MACE,UAAU,MAAM;AAAA,MAChB,UAAU,MAAM;AAAA,MAChB,UAAU,MAAM;AAAA,IAClB;AAAA,IACA,EAAE,SAAS,EAAE,gBAAgB,mBAAmB,EAAE;AAAA,EACpD;AACA,QAAM,EAAE,OAAO,WAAW,KAAK,IAAI,KAAK;AACxC,MAAI,CAAC,OAAO;AACV,UAAM,IAAI,MAAM,8BAA8B;AAAA,EAChD;AAEA,QAAM,aAAa,aAAa;AAChC,QAAM,YAAY,IAAI,KAAK,KAAK,IAAI,IAAI,aAAa,GAAI,EAAE,YAAY;AAEvE,QAAM,QAAQ,IAAI,WAAW,MAAM,OAAO;AAC1C,QAAM,iBAAiB,MAAM,MAAM,eAAe,KAAK;AAEvD,QAAM,SAAsB;AAAA,IAC1B,QAAQ;AAAA,IACR;AAAA;AAAA;AAAA,IAGA,iBAAiB;AAAA,IACjB;AAAA,IACA,QAAQ,CAAC,GAAG;AAAA,IACZ;AAAA,EACF;AACA,QAAM,MAAM,UAAU,MAAM;AAC5B,SAAO,EAAE,QAAQ,WAAW,YAAY,IAAI,EAAE;AAChD;;;ACtBO,IAAM,mCAAmC,KAAK;AAqBrD,eAAsB,aAAa,OAA2C;AAC5E,MAAI,CAAC,MAAM,YAAY,CAAC,MAAM,SAAS;AACrC,UAAM,IAAI,MAAM,mDAAmD;AAAA,EACrE;AACA,QAAM,QAAQ,MAAM,WAChB,WAAW,YAAY,MAAM,QAAQ,IACrC,IAAI,WAAW,MAAM,OAAQ;AACjC,QAAM,OAAO,MAAM,MAAM,SAAS;AAClC,MAAI,CAAC,KAAM,OAAM,IAAI,MAAM,2BAA2B;AAEtD,UAAQ,KAAK,QAAQ;AAAA,IACnB,KAAK;AACH,YAAM,IAAI,MAAM,yDAAyD;AAAA,IAC3E,KAAK;AACH,YAAM,IAAI;AAAA,QACR;AAAA,MAEF;AAAA,IACF,KAAK;AAAA,IACL,KAAK;AAGH,aAAO,qBAAqB,OAAO,MAAM,KAAK;AAAA,IAChD,SAAS;AAEP,YAAM,UAAiB,KAAK;AAC5B,YAAM,IAAI,MAAM,8CAA8C,OAAO,OAAO,CAAC,EAAE;AAAA,IACjF;AAAA,EACF;AACF;AAWA,eAAe,qBACb,OACA,MACA,OACsB;AACtB,QAAM,cAAc,MAAM,MAAM,eAAe;AAC/C,MAAI,CAAC,aAAa;AAChB,UAAM,IAAI,MAAM,gDAAgD;AAAA,EAClE;AAEA,QAAM,MAAM,WAAW,KAAK,GAAG,MAAM,QAAQ,qBAAqB,QAAW;AAAA,IAC3E,SAAS,EAAE,eAAe,UAAU,WAAW,GAAG;AAAA;AAAA,IAElD,iBAAiB;AAAA,EACnB,CAAC;AAGD,QAAM,aAAa,KAAK,cAAc;AACtC,QAAM,SAAsB;AAAA,IAC1B,GAAG;AAAA;AAAA,IAEH,WAAW,IAAI,KAAK,KAAK,IAAI,IAAI,aAAa,GAAI,EAAE,YAAY;AAAA,IAChE;AAAA,EACF;AACA,QAAM,MAAM,UAAU,MAAM;AAC5B,SAAO;AACT;AAEO,SAAS,aAAa,MAAmB,YAAY,IAAa;AACvE,QAAM,cAAc,IAAI,KAAK,KAAK,SAAS,EAAE,QAAQ;AACrD,SAAO,KAAK,IAAI,IAAI,YAAY,MAAO;AACzC;AAiBA,eAAsB,mBAAmB,OAAuC;AAC9E,MAAI;AACF,UAAM,QAAQ,MAAM,WAChB,WAAW,YAAY,MAAM,QAAQ,IACrC,IAAI,WAAW,MAAM,OAAQ;AACjC,UAAM,OAAO,MAAM,MAAM,SAAS;AAIlC,QAAI,CAAC,QAAQ,CAAC,uBAAuB,KAAK,MAAM,EAAG,QAAO;AAC1D,QAAI,CAAC,aAAa,MAAM,gCAAgC,EAAG,QAAO;AAClE,UAAM,aAAa,KAAK;AACxB,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;;;ACxJA,YAAYA,SAAQ;AACpB,YAAYC,WAAU;AACtB,OAAO,UAAU;AAUjB,eAAsB,YAAY,SAAyC;AACzE,QAAM,OAAO,YAAY,OAAO;AAChC,MAAI;AACJ,MAAI;AACF,cAAU,MAAS,aAAS,MAAM,OAAO;AAAA,EAC3C,SAAS,GAAG;AACV,QAAK,EAA4B,SAAS,UAAU;AAClD,YAAM,IAAI,MAAM,YAAY,OAAO,kBAAkB,IAAI,EAAE;AAAA,IAC7D;AACA,UAAM;AAAA,EACR;AACA,SAAO,KAAK,KAAK,OAAO;AAC1B;AAEA,eAAsB,YAAY,SAAiB,KAAmC;AACpF,QAAS,UAAM,WAAW,OAAO,GAAG,EAAE,WAAW,KAAK,CAAC;AACvD,QAAS,cAAU,YAAY,OAAO,GAAG,KAAK,KAAK,GAAG,GAAG,OAAO;AAClE;AAEA,eAAsB,eAAkC;AACtD,QAAM,OAAO,aAAa;AAC1B,MAAI;AACF,UAAM,UAAU,MAAS,YAAQ,MAAM,EAAE,eAAe,KAAK,CAAC;AAC9D,UAAM,SAAmB,CAAC;AAC1B,eAAW,KAAK,SAAS;AACvB,UAAI,EAAE,YAAY,GAAG;AACnB,YAAI;AACF,gBAAS,WAAY,WAAK,MAAM,EAAE,MAAM,aAAa,CAAC;AACtD,iBAAO,KAAK,EAAE,IAAI;AAAA,QACpB,QAAQ;AAAA,QAER;AAAA,MACF;AAAA,IACF;AACA,WAAO;AAAA,EACT,SAAS,GAAG;AACV,QAAK,EAA4B,SAAS,SAAU,QAAO,CAAC;AAC5D,UAAM;AAAA,EACR;AACF;;;ACnDA,YAAYC,SAAQ;AACpB,OAAOC,WAAU;AAuBjB,IAAM,cAAc;AACpB,IAAM,mBAAmB,EAAE,QAAQ,SAAkB,SAAS,MAAM;AAEpE,eAAsB,mBAA0C;AAC9D,MAAI,MAAgB,CAAC;AACrB,MAAI;AACF,UAAM,UAAU,MAAS,aAAS,iBAAiB,GAAG,OAAO;AAC7D,UAAOC,MAAK,KAAK,OAAO,KAAkB,CAAC;AAAA,EAC7C,SAAS,GAAG;AACV,QAAK,EAA4B,SAAS,SAAU,OAAM;AAAA,EAC5D;AACA,QAAM,YAAY,IAAI,aAAa,IAAI,iBAAiB;AACxD,SAAO;AAAA,IACL;AAAA,IACA,eAAe;AAAA;AAAA,IACf,UAAU,EAAE,GAAG,kBAAkB,GAAG,IAAI,SAAS;AAAA,EACnD;AACF;AAEA,eAAsB,iBAAiB,KAAkC;AACvE,QAAS,UAAM,aAAa,GAAG,EAAE,WAAW,KAAK,CAAC;AAKlD,QAAM,YAAY,IAAI,aAAa,IAAI,iBAAiB;AACxD,QAAM,MAAM;AAAA,IACV;AAAA,IACA,eAAe;AAAA,IACf,UAAU,IAAI;AAAA,EAChB;AACA,QAAS,cAAU,iBAAiB,GAAGA,MAAK,KAAK,GAAG,GAAG,OAAO;AAChE;AAKO,SAAS,iBACd,MACA,KACA,QACQ;AACR,SAAO,KAAK,WAAW,IAAI,WAAW,IAAI,eAAe,OAAO;AAClE;AAMO,SAAS,qBACd,MACA,KACA,QACQ;AACR,SAAO,iBAAiB,EAAE,SAAS,KAAK,KAAK,GAAG,KAAK,MAAM;AAC7D;;;AC/EA,YAAYC,SAAQ;AACpB,OAAOC,WAAU;;;ACDV,IAAK,WAAL,kBAAKC,cAAL;AACL,EAAAA,oBAAA,aAAU,KAAV;AACA,EAAAA,oBAAA,iBAAc,KAAd;AACA,EAAAA,oBAAA,gBAAa,KAAb;AACA,EAAAA,oBAAA,oBAAiB,KAAjB;AACA,EAAAA,oBAAA,mBAAgB,KAAhB;AACA,EAAAA,oBAAA,kBAAe,KAAf;AACA,EAAAA,oBAAA,YAAS,OAAT;AAPU,SAAAA;AAAA,GAAA;AAUL,IAAK,eAAL,kBAAKC,kBAAL;AAEL,EAAAA,cAAA,sBAAmB;AACnB,EAAAA,cAAA,kBAAe;AACf,EAAAA,cAAA,kBAAe;AAGf,EAAAA,cAAA,iBAAc;AACd,EAAAA,cAAA,kBAAe;AACf,EAAAA,cAAA,yBAAsB;AACtB,EAAAA,cAAA,mBAAgB;AAChB,EAAAA,cAAA,qBAAkB;AAGlB,EAAAA,cAAA,6BAA0B;AAC1B,EAAAA,cAAA,qBAAkB;AAClB,EAAAA,cAAA,sBAAmB;AACnB,EAAAA,cAAA,qBAAkB;AAClB,EAAAA,cAAA,kBAAe;AAGf,EAAAA,cAAA,mBAAgB;AAChB,EAAAA,cAAA,aAAU;AACV,EAAAA,cAAA,kBAAe;AACf,EAAAA,cAAA,0BAAuB;AAGvB,EAAAA,cAAA,uBAAoB;AACpB,EAAAA,cAAA,wBAAqB;AACrB,EAAAA,cAAA,0BAAuB;AAGvB,EAAAA,cAAA,mBAAgB;AAChB,EAAAA,cAAA,wBAAqB;AACrB,EAAAA,cAAA,2BAAwB;AACxB,EAAAA,cAAA,wBAAqB;AACrB,EAAAA,cAAA,6BAA0B;AAC1B,EAAAA,cAAA,yBAAsB;AACtB,EAAAA,cAAA,mBAAgB;AAChB,EAAAA,cAAA,mBAAgB;AAvCN,SAAAA;AAAA,GAAA;AA0CZ,IAAM,eAA+C;AAAA,EACnD,CAAC,yCAA6B,GAAG;AAAA,EACjC,CAAC,iCAAyB,GAAG;AAAA,EAC7B,CAAC,iCAAyB,GAAG;AAAA,EAC7B,CAAC,+BAAwB,GAAG;AAAA,EAC5B,CAAC,iCAAyB,GAAG;AAAA,EAC7B,CAAC,+CAAgC,GAAG;AAAA,EACpC,CAAC,mCAA0B,GAAG;AAAA,EAC9B,CAAC,uCAA4B,GAAG;AAAA,EAChC,CAAC,uDAAoC,GAAG;AAAA,EACxC,CAAC,uCAA4B,GAAG;AAAA,EAChC,CAAC,yCAA6B,GAAG;AAAA,EACjC,CAAC,uCAA4B,GAAG;AAAA,EAChC,CAAC,iCAAyB,GAAG;AAAA,EAC7B,CAAC,mCAA0B,GAAG;AAAA,EAC9B,CAAC,uBAAoB,GAAG;AAAA,EACxB,CAAC,iCAAyB,GAAG;AAAA,EAC7B,CAAC,iDAAiC,GAAG;AAAA,EACrC,CAAC,2CAA8B,GAAG;AAAA,EAClC,CAAC,6CAA+B,GAAG;AAAA,EACnC,CAAC,iDAAiC,GAAG;AAAA,EACrC,CAAC,mCAA0B,GAAG;AAAA,EAC9B,CAAC,6CAA+B,GAAG;AAAA,EACnC,CAAC,mDAAkC,GAAG;AAAA,EACtC,CAAC,6CAA+B,GAAG;AAAA,EACnC,CAAC,uDAAoC,GAAG;AAAA,EACxC,CAAC,+CAAgC,GAAG;AAAA,EACpC,CAAC,mCAA0B,GAAG;AAAA,EAC9B,CAAC,mCAA0B,GAAG;AAChC;AAEO,SAAS,YAAY,MAA8B;AACxD,SAAO,aAAa,IAAI;AAC1B;;;ACzEO,IAAM,WAAN,cAAuB,MAAM;AAAA,EACzB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACS;AAAA,EAElB,YAAY,MAAoB;AAC9B,UAAM,KAAK,OAAO;AAClB,SAAK,OAAO;AACZ,SAAK,OAAO,KAAK;AACjB,SAAK,WAAW,YAAY,KAAK,IAAI;AACrC,SAAK,UAAU,KAAK;AACpB,SAAK,UAAU,KAAK;AACpB,SAAK,UAAU,KAAK;AACpB,SAAK,QAAQ,KAAK;AAAA,EACpB;AAAA,EAEA,oBAA4B;AAC1B,UAAM,QAAkB,CAAC,GAAG,KAAK,IAAI,KAAK,KAAK,OAAO,EAAE;AAIxD,UAAM,aAAa,KAAK,SAAS;AACjC,QAAI,OAAO,eAAe,YAAY,cAAc,eAAe,KAAK,MAAM;AAC5E,YAAM,KAAK,0CAAY,UAAU,GAAG;AAAA,IACtC;AACA,UAAM,QAAQ,KAAK,SAAS;AAC5B,QAAI,OAAO,UAAU,YAAY,MAAO,OAAM,KAAK,kBAAQ,KAAK,GAAG;AACnE,QAAI,KAAK,QAAS,OAAM,KAAK,aAAa,KAAK,OAAO,GAAG;AACzD,QAAI,KAAK,QAAS,OAAM,KAAK,aAAa,KAAK,OAAO,GAAG;AACzD,QAAI,MAAM,MAAM,KAAK,GAAG;AACxB,UAAM,WAAW,KAAK,SAAS;AAC/B,QAAI,OAAO,aAAa,YAAY,SAAU,QAAO;AAAA,wBAAY,QAAQ;AACzE,WAAO;AAAA,EACT;AAAA,EAEA,SAAS;AACP,WAAO;AAAA,MACL,MAAM,KAAK;AAAA,MACX,SAAS,KAAK;AAAA,MACd,SAAS,KAAK;AAAA,MACd,SAAS,KAAK;AAAA,IAChB;AAAA,EACF;AACF;AAIO,SAAS,eAAe,KAAU,SAA4B;AACnE,QAAM,SAAS,IAAI,UAAU;AAC7B,QAAM,UAAU,IAAI,UAAU,UAAU,YAAY;AAQpD,QAAM,OAAO,IAAI,UAAU;AAC3B,QAAM,OAAQ,MAAM,SAAS;AAC7B,QAAM,WAAW,MAAM;AACvB,QAAM,UAAU,MAAM;AACtB,QAAM,YAAY,MAAM;AACxB,QAAM,cAAc,MAAM;AAC1B,QAAM,eAAe,MAAM;AAC3B,QAAM,gBAAgB,MAAM;AAE5B,MAAI;AACJ,MAAI,WAAW,IAAK;AAAA,WACX,WAAW,IAAK;AAAA,WAChB,WAAW,OAAO,aAAa,kBAAmB;AAAA,WAClD,WAAW,OAAO,aAAa,mBAAoB;AAAA,WACnD,UAAU,UAAU,OAAO,SAAS,IAAK;AAAA,WACzC,UAAU,UAAU,IAAK;AAAA,WACzB,IAAI,SAAS,eAAgB;AAAA,MACjC;AAEL,SAAO,IAAI,SAAS;AAAA,IAClB;AAAA,IACA,SAAS,WAAW,IAAI,WAAW;AAAA,IACnC,SAAS;AAAA,MACP;AAAA,MACA,cAAc;AAAA;AAAA;AAAA,MAGd,GAAI,YAAY,EAAE,OAAO,UAAU,IAAI,CAAC;AAAA,MACxC,GAAI,gBAAgB,SAAY,EAAE,SAAS,YAAY,IAAI,CAAC;AAAA,MAC5D,GAAI,eAAe,EAAE,UAAU,aAAa,IAAI,CAAC;AAAA,MACjD,GAAI,kBAAkB,SAAY,EAAE,WAAW,cAAc,IAAI,CAAC;AAAA,IACpE;AAAA,IACA;AAAA,IACA;AAAA,IACA,OAAO;AAAA,EACT,CAAC;AACH;;;AF/FA,eAAsB,QAAQ,MAAc,KAA+B;AACzE,MAAI,CAAC,IAAI,UAAU;AACjB,UAAM,IAAI,SAAS;AAAA,MACjB;AAAA,MACA,SAAS,QAAQ,IAAI;AAAA,IACvB,CAAC;AAAA,EACH;AACA,QAAS,UAAM,OAAO,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AAChD,QAAS,cAAU,QAAQ,IAAI,GAAGC,MAAK,KAAK,GAAG,GAAG,OAAO;AAC3D;AAEA,eAAsB,QAAQ,MAAkC;AAC9D,MAAI;AACF,UAAM,UAAU,MAAS,aAAS,QAAQ,IAAI,GAAG,OAAO;AACxD,WAAOA,MAAK,KAAK,OAAO;AAAA,EAC1B,SAAS,GAAG;AACV,QAAK,EAA4B,SAAS,UAAU;AAClD,YAAM,IAAI,SAAS;AAAA,QACjB;AAAA,QACA,SAAS,QAAQ,IAAI;AAAA,MACvB,CAAC;AAAA,IACH;AACA,UAAM;AAAA,EACR;AACF;AAEA,eAAsB,WAA8B;AAClD,MAAI;AACF,UAAM,UAAU,MAAS,YAAQ,QAAQ,GAAG,EAAE,eAAe,KAAK,CAAC;AACnE,UAAM,SAAmB,CAAC;AAC1B,eAAW,KAAK,SAAS;AACvB,UAAI,CAAC,EAAE,YAAY,EAAG;AACtB,UAAI;AACF,cAAS,WAAO,QAAQ,EAAE,IAAI,CAAC;AAC/B,eAAO,KAAK,EAAE,IAAI;AAAA,MACpB,QAAQ;AAAA,MAER;AAAA,IACF;AACA,WAAO;AAAA,EACT,SAAS,GAAG;AACV,QAAK,EAA4B,SAAS,SAAU,QAAO,CAAC;AAC5D,UAAM;AAAA,EACR;AACF;AAEA,eAAsB,UAAU,MAA6B;AAC3D,MAAI;AACF,UAAS,WAAO,QAAQ,IAAI,CAAC;AAAA,EAC/B,QAAQ;AACN,UAAM,IAAI,SAAS;AAAA,MACjB;AAAA,MACA,SAAS,QAAQ,IAAI;AAAA,IACvB,CAAC;AAAA,EACH;AACA,QAAS,OAAG,OAAO,IAAI,GAAG,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AAC5D;;;AGrEA,YAAYC,SAAQ;AAoCpB,eAAe,gBAAgB,KAA4B;AACzD,MAAI;AACF,UAAS,WAAO,QAAQ,GAAG,CAAC;AAAA,EAC9B,QAAQ;AACN,UAAM,IAAI,SAAS;AAAA,MACjB;AAAA,MACA,SAAS,QAAQ,GAAG,sCAAsC,GAAG;AAAA,IAC/D,CAAC;AAAA,EACH;AACF;AAEA,eAAsB,aAAa,MAAc,MAAmC;AAClF,QAAM,gBAAgB,KAAK,GAAG;AAC9B,QAAS,UAAM,YAAY,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AACrD,QAAS,cAAU,iBAAiB,IAAI,GAAG,KAAK,UAAU,MAAM,MAAM,CAAC,GAAG,EAAE,MAAM,IAAM,CAAC;AAC3F;AAEA,eAAsB,aAAa,MAAqC;AACtE,MAAI;AACF,UAAM,MAAM,MAAS,aAAS,iBAAiB,IAAI,GAAG,OAAO;AAC7D,WAAO,KAAK,MAAM,GAAG;AAAA,EACvB,SAAS,GAAG;AACV,QAAK,EAA4B,SAAS,UAAU;AAClD,YAAM,IAAI,SAAS;AAAA,QACjB;AAAA,QACA,SAAS,aAAa,IAAI;AAAA,MAC5B,CAAC;AAAA,IACH;AACA,UAAM;AAAA,EACR;AACF;AAEA,eAAsB,eAAe,KAAiC;AACpE,MAAI;AACF,UAAM,UAAU,MAAS,YAAQ,cAAc,GAAG,EAAE,eAAe,KAAK,CAAC;AACzE,UAAM,SAAmB,CAAC;AAC1B,eAAW,KAAK,SAAS;AACvB,UAAI,CAAC,EAAE,YAAY,EAAG;AACtB,UAAI;AACF,cAAM,OAAO,MAAM,aAAa,EAAE,IAAI;AACtC,YAAI,CAAC,OAAO,KAAK,QAAQ,IAAK,QAAO,KAAK,EAAE,IAAI;AAAA,MAClD,QAAQ;AAAA,MAER;AAAA,IACF;AACA,WAAO;AAAA,EACT,SAAS,GAAG;AACV,QAAK,EAA4B,SAAS,SAAU,QAAO,CAAC;AAC5D,UAAM;AAAA,EACR;AACF;AAEA,eAAsB,eAAe,MAA6B;AAChE,MAAI;AACF,UAAS,WAAO,iBAAiB,IAAI,CAAC;AAAA,EACxC,QAAQ;AACN,UAAM,IAAI,SAAS;AAAA,MACjB;AAAA,MACA,SAAS,aAAa,IAAI;AAAA,IAC5B,CAAC;AAAA,EACH;AACA,QAAS,OAAG,YAAY,IAAI,GAAG,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AACjE;;;AClGA,YAAYC,SAAQ;AAEpB,YAAYC,WAAU;AACtB,OAAOC,WAAU;AAkCjB,IAAM,WAAW,oBAAI,IAAI,CAAC,QAAQ,cAAc,UAAU,CAAC;AAgB3D,eAAsB,wBAAmD;AACvE,QAAM,UAA4B,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAC,GAAG,SAAS,CAAC,EAAE;AAC1E,QAAM,OAAO,aAAa;AAE1B,MAAI;AACJ,MAAI;AACF,cAAU,MAAS,YAAQ,MAAM,EAAE,eAAe,KAAK,CAAC;AAAA,EAC1D,SAAS,GAAG;AACV,QAAK,EAA4B,SAAS,SAAU,QAAO;AAC3D,UAAM;AAAA,EACR;AAEA,aAAW,KAAK,SAAS;AACvB,QAAI,CAAC,EAAE,YAAY,KAAK,SAAS,IAAI,EAAE,IAAI,EAAG;AAC9C,UAAM,YAAY,YAAY,EAAE,IAAI;AACpC,QAAI;AACF,YAAS,WAAO,SAAS;AAAA,IAC3B,QAAQ;AACN;AAAA,IACF;AAGA,QAAI;AACF,YAAS,WAAO,QAAQ,EAAE,IAAI,CAAC;AAC/B,cAAQ,QAAQ,KAAK,EAAE,IAAI;AAC3B;AAAA,IACF,QAAQ;AAAA,IAER;AAEA,QAAI,SAA0C;AAC9C,QAAI;AACF,eAAS,MAAM,WAAW,EAAE,IAAI;AAChC,cAAQ,SAAS,KAAK,EAAE,IAAI;AAAA,IAC9B,SAAS,KAAK;AACZ,cAAQ,OAAO,KAAK,EAAE,IAAI;AAE1B,UAAI;AACF,cAAM,UAAU,EAAE,IAAI;AAAA,MACxB,QAAQ;AAAA,MAER;AACA,UAAI,WAAW,MAAM;AAGnB,YAAI;AACF,gBAAM,QAAQ,KAAK;AAAA,YACjB,MAAS,aAAS,gBAAgB,EAAE,IAAI,GAAG,OAAO;AAAA,UACpD;AACA,gBAAM,YAAY,OAAO,WAAW,GAAG,MAAM,QAAQ,IAAI,EAAE,IAAI,KAAK,WAAW,EAAE,IAAI;AACrF,gBAAM,eAAe,SAAS;AAAA,QAChC,QAAQ;AAAA,QAER;AAAA,MACF,OAAO;AACL,YAAI;AACF,gBAAM,eAAe,OAAO,YAAY;AAAA,QAC1C,QAAQ;AAAA,QAER;AAAA,MACF;AACA,cAAQ,OAAO;AAAA,QACb,wCAAwC,EAAE,IAAI,WAAW,KAAK,UAAU,OAAQ,IAAc,OAAO,CAAC,CAAC;AAAA;AAAA,MACzG;AAAA,IACF;AAAA,EACF;AAGA,MAAI,QAAQ,SAAS,SAAS,GAAG;AAC/B,UAAM,IAAI,MAAM,iBAAiB;AACjC,UAAM,iBAAiB,EAAE,WAAW,EAAE,WAAW,UAAU,EAAE,SAAS,CAAC;AAEvE,UAAM,UAAU,WAAW;AAC3B,YAAQ,OAAO;AAAA,MACb,oCAAoC,QAAQ,SAAS,MAAM,YAAY,OAAO;AAAA;AAAA,IAChF;AAAA,EACF;AAEA,SAAO;AACT;AAEA,eAAe,WAAW,MAAiD;AACzE,QAAM,gBAAgBC,MAAK;AAAA,IACzB,MAAS,aAAS,YAAY,IAAI,GAAG,OAAO;AAAA,EAC9C;AACA,MAAI,CAAC,eAAe,UAAU;AAC5B,UAAM,IAAI,MAAM,qCAAqC;AAAA,EACvD;AAKA,MAAI,QAAkC;AACtC,MAAI;AACF,YAAQ,KAAK;AAAA,MACX,MAAS,aAAS,gBAAgB,IAAI,GAAG,OAAO;AAAA,IAClD;AAAA,EACF,SAAS,GAAG;AACV,QAAK,EAA4B,SAAS,UAAU;AAClD,YAAM,IAAI,MAAM,gCAAiC,EAAY,OAAO,EAAE;AAAA,IACxE;AAAA,EAEF;AAEA,QAAM,eAAe,OAAO,WAAW,GAAG,MAAM,QAAQ,IAAI,IAAI,KAAK,WAAW,IAAI;AAGpF,QAAM,QAAQ,MAAM;AAAA,IAClB,UAAU,cAAc;AAAA,IACxB,UAAU,cAAc;AAAA,IACxB,cAAc,cAAc;AAAA,IAC5B,iBAAiB;AAAA,EACnB,CAAC;AAMD,MAAI,OAAO;AACT,UAAM,aAAa,cAAc;AAAA,MAC/B,KAAK;AAAA,MACL,QAAQ,MAAM;AAAA,MACd,QAAQ,MAAM;AAAA,MACd,WAAW,MAAM;AAAA,MACjB,UAAU,MAAM;AAAA,MAChB,gBAAgB,MAAM;AAAA,MACtB,iBAAiB,MAAM;AAAA,IACzB,CAAC;AAAA,EACH,OAAO;AAIL,UAAM,aAAa,cAAc;AAAA,MAC/B,KAAK;AAAA,MACL,QAAQ;AAAA,MACR,QAAQ,CAAC;AAAA,MACT,WAAW;AAAA,IACb,CAAC;AAAA,EACH;AAGA,QAAM,gBAAqB,WAAK,WAAW,GAAG,IAAI;AAClD,QAAS,UAAM,eAAe,EAAE,WAAW,KAAK,CAAC;AACjD,QAAS,WAAO,YAAY,IAAI,GAAQ,WAAK,eAAe,iBAAiB,CAAC;AAC9E,MAAI;AACF,UAAS;AAAA,MACP,gBAAgB,IAAI;AAAA,MACf,WAAK,eAAe,sBAAsB;AAAA,IACjD;AAAA,EACF,SAAS,GAAG;AACV,QAAK,EAA4B,SAAS,SAAU,OAAM;AAAA,EAC5D;AAIA,MAAI;AACF,UAAS,UAAM,WAAW,IAAI,CAAC;AAAA,EACjC,QAAQ;AAAA,EAER;AAEA,SAAO,EAAE,aAAa;AACxB;;;AC/MA,eAAsB,sBACpB,MACA,KACA,WACiB;AACjB,MAAI,KAAK,OAAQ,QAAO,KAAK;AAC7B,MAAI,IAAI,aAAc,QAAO,IAAI;AACjC,QAAM,MAAM,MAAM,QAAQ,SAAS;AACnC,MAAI,IAAI,gBAAiB,QAAO,IAAI;AACpC,QAAM,CAAC,KAAK,IAAI,MAAM,eAAe,SAAS;AAC9C,MAAI,CAAC,OAAO;AACV,UAAM,IAAI,SAAS;AAAA,MACjB;AAAA,MACA,SAAS,QAAQ,SAAS,2CAA2C,SAAS;AAAA,IAChF,CAAC;AAAA,EACH;AACA,SAAO;AACT;;;ACdA,eAAsB,MACpB,MACA,OACA,KACyB;AACzB,QAAM,OAAgC,CAAC;AACvC,QAAM,SAAS,gBAAgB,IAAI,MAAM;AACzC,MAAI,WAAW,OAAW,MAAK,SAAS;AACxC,QAAM,SAAS,gBAAgB,IAAI,IAAI;AACvC,MAAI,WAAW,OAAW,MAAK,SAAS;AACxC,MAAI,IAAI,WAAW,OAAW,MAAK,SAAS,IAAI;AAChD,MAAI,IAAI,YAAY,OAAW,MAAK,UAAU,IAAI;AAElD,QAAM,OAAO,IAAI,SAAS;AAC1B,QAAM,OAAO,IAAI,SAAS,KAAK,MAAM,IAAI,SAAS,IAAI,IAAI;AAC1D,OAAK,OAAO;AACZ,OAAK,OAAO;AAEZ,QAAM,OAAO,MAAM,KAAK,KAAyB,eAAe,KAAK,aAAa,IAAI;AACtF,SAAO;AAAA,IACL,MAAM,KAAK,KAAK,WAAW,CAAC;AAAA,IAC5B,OAAO,KAAK,KAAK;AAAA,EACnB;AACF;AAEA,IAAM,mBAA2C;AAAA,EAC/C,KAAK;AAAA,EACL,IAAI;AAAA,EACJ,KAAK;AAAA,EACL,MAAM;AAAA,EACN,KAAK;AAAA,EACL,IAAI;AAAA,EACJ,MAAM;AAAA,EACN,MAAM;AAAA,EACN,KAAK;AAAA,EACL,IAAI;AAAA,EACJ,KAAK;AAAA,EACL,MAAM;AAAA,EACN,KAAK;AAAA,EACL,MAAM;AAAA,EACN,KAAK;AAAA,EACL,IAAI;AAAA,EACJ,KAAK;AAAA,EACL,MAAM;AAAA,EACN,KAAK;AAAA,EACL,MAAM;AAAA,EACN,KAAK;AAAA,EACL,IAAI;AAAA,EACJ,MAAM;AAAA,EACN,KAAK;AAAA,EACL,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,MAAM;AAAA,EACN,WAAW;AAAA,EACX,UAAU;AAAA,EACV,WAAW;AAAA,EACX,UAAU;AAAA,EACV,aAAa;AAAA,EACb,YAAY;AAAA,EACZ,WAAW;AAAA,EACX,UAAU;AAAA,EACV,UAAU;AAAA,EACV,SAAS;AAAA,EACT,UAAU;AAAA,EACV,SAAS;AAAA,EACT,cAAc;AAAA,EACd,aAAa;AACf;AAEA,SAAS,gBAAgB,QAAsD;AAC7E,MAAI,WAAW,OAAW,QAAO;AACjC,MAAI,CAAC,cAAc,MAAM,EAAG,QAAO;AACnC,MAAI,YAAY,MAAM,EAAG,QAAO;AAEhC,QAAM,SAAS,OAAO,QAAQ,MAAM,EAAE,QAAQ,CAAC,CAAC,OAAO,KAAK,MAAM,aAAa,OAAO,KAAK,CAAC;AAC5F,MAAI,OAAO,WAAW,EAAG,QAAO;AAChC,SAAO,OAAO,WAAW,IAAI,OAAO,CAAC,IAAI,EAAE,KAAK,OAAO;AACzD;AAEA,SAAS,YAAY,QAA0C;AAC7D,SACE,SAAS,UACT,QAAQ,UACR,SAAS,UACR,OAAO,OAAO,UAAU,YAAY,OAAO,OAAO,OAAO;AAE9D;AAEA,SAAS,aAAa,OAAe,OAAgD;AACnF,MAAI,cAAc,KAAK,GAAG;AACxB,UAAM,UAAU,OAAO,QAAQ,KAAK;AACpC,QAAI,QAAQ,SAAS,KAAK,QAAQ,MAAM,CAAC,CAAC,GAAG,MAAM,iBAAiB,GAAG,CAAC,GAAG;AACzE,aAAO,QAAQ,IAAI,CAAC,CAAC,KAAK,aAAa,MAAM;AAC3C,cAAM,KAAK,iBAAiB,GAAG;AAC/B,YAAI,CAAC,IAAI;AACP,gBAAM,IAAI,MAAM,gCAAgC,GAAG,gBAAgB,KAAK,GAAG;AAAA,QAC7E;AACA,eAAO,gBAAgB,EAAE,IACrB,EAAE,OAAO,GAAG,IACZ,EAAE,OAAO,IAAI,OAAO,cAAc;AAAA,MACxC,CAAC;AAAA,IACH;AACA,QAAI,QAAQ,KAAK,CAAC,CAAC,GAAG,MAAM,iBAAiB,GAAG,CAAC,GAAG;AAClD,YAAM,IAAI,MAAM,6CAA6C,KAAK,GAAG;AAAA,IACvE;AAAA,EACF;AACA,SAAO,CAAC,EAAE,OAAO,IAAI,MAAM,QAAQ,KAAK,IAAI,OAAO,MAAM,MAAM,CAAC;AAClE;AAEA,SAAS,iBAAiB,KAAsB;AAC9C,SAAO,OAAO,UAAU,eAAe,KAAK,kBAAkB,GAAG;AACnE;AAEA,SAAS,gBAAgB,IAAqB;AAC5C,SAAO,OAAO,aAAa,OAAO;AACpC;AAEA,SAAS,gBAAgB,MAA2D;AAClF,MAAI,SAAS,OAAW,QAAO;AAC/B,SAAO,KAAK,IAAI,CAAC,SAAS;AACxB,UAAM,QAAQ,KAAK;AACnB,QAAI,OAAO,UAAU,YAAY,MAAM,KAAK,MAAM,IAAI;AACpD,YAAM,IAAI,MAAM,oCAAoC;AAAA,IACtD;AACA,UAAM,eACJ,OAAO,KAAK,cAAc,WACtB,KAAK,YACL,OAAO,KAAK,QAAQ,WAClB,KAAK,MACL,OAAO,KAAK,UAAU,WACpB,KAAK,QACL,KAAK,SAAS,OACZ,SACA;AACZ,UAAM,YAAY,aAAa,YAAY;AAC3C,QAAI,cAAc,SAAS,cAAc,QAAQ;AAC/C,YAAM,IAAI,MAAM,4CAA4C,YAAY,GAAG;AAAA,IAC7E;AACA,WAAO,EAAE,OAAO,UAAU;AAAA,EAC5B,CAAC;AACH;AAEA,SAAS,cAAc,OAAkD;AACvE,SAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AAC5E;;;ACzJA,SAAS,gBAAgB,QAA6C;AACpE,SAAO,UAAU;AACnB;AAEA,SAAS,eAAe,OAAiC;AACvD,SAAO,MAAM,UAAU,MAAM,KAAK,WAAW;AAC/C;AAEA,SAAS,YAAY,OAA6C;AAChE,SAAO;AAAA,IACL,GAAI,MAAM,UAAU,CAAC;AAAA,IACrB,GAAI,MAAM,UAAU,WAAW,MAAM,KAAK,EAAE,IAAI,MAAM,GAAG,IAAI,CAAC;AAAA,IAC9D,GAAI,MAAM,KAAK,SAAS,EAAE,KAAK,MAAM,IAAI,KAAK,GAAG,EAAE,IAAI,CAAC;AAAA,EAC1D;AACF;AAEA,SAAS,UAAU,OAA6C;AAC9D,SAAO;AAAA,IACL,GAAI,MAAM,UAAU,CAAC;AAAA,IACrB,GAAI,MAAM,KAAK,EAAE,IAAI,MAAM,GAAG,IAAI,CAAC;AAAA,IACnC,GAAI,MAAM,KAAK,SAAS,EAAE,KAAK,MAAM,IAAI,IAAI,CAAC;AAAA,EAChD;AACF;AAEA,SAAS,WAAW,OAA6C;AAC/D,SAAO,MAAM,UAAU,CAAC;AAC1B;AAEA,eAAsB,OACpB,MACA,OACA,YACA,OAC0B;AAC1B,QAAM,SAAS,gBAAgB,MAAM,MAAM;AAC3C,QAAM,QAAQ,eAAe,KAAK;AAClC,MAAI,UAAU,YAAY,CAAC,MAAM,IAAI;AACnC,UAAM,IAAI,MAAM,iBAAiB,KAAK,IAAI,UAAU,cAAc;AAAA,EACpE;AAEA,QAAMC,QAAO,UAAU,WACnB,eAAe,KAAK,IAAI,MAAM,EAAE,WAAW,UAAU,KACrD,eAAe,KAAK,WAAW,UAAU;AAE7C,QAAM,OAAO,OAAO,YAAY;AAC9B,QAAI,WAAW,OAAO;AACpB,aAAO,KAAK,IAAIA,OAAM,EAAE,QAAQ,YAAY,KAAK,EAAE,CAAC;AAAA,IACtD;AACA,QAAI,WAAW,UAAU;AACvB,aAAO,KAAK,OAAOA,OAAM,EAAE,QAAQ,YAAY,KAAK,EAAE,CAAC;AAAA,IACzD;AACA,QAAI,WAAW,OAAO;AACpB,aAAO,KAAK,IAAIA,OAAM,UAAU,WAAW,WAAW,KAAK,IAAI,UAAU,KAAK,CAAC;AAAA,IACjF;AACA,WAAO,KAAK,KAAKA,OAAM,UAAU,WAAW,WAAW,KAAK,IAAI,UAAU,KAAK,CAAC;AAAA,EAClF,GAAG;AACH,SAAO,KAAK;AACd;;;ACUA,SAAS,sBACPC,SAC6F;AAC7F,MAAIA,QAAO,WAAY,QAAOA,QAAO;AACrC,MAAIA,QAAO,OAAQ,QAAOA,QAAO;AACjC,MAAI,CAACA,QAAO,YAAY,UAAU,CAACA,QAAO,YAAY,OAAQ,QAAO;AACrE,QAAM,OAAO,KAAK,IAAIA,QAAO,YAAY,UAAU,GAAGA,QAAO,YAAY,UAAU,CAAC;AACpF,SAAO,MAAM,KAAK,EAAE,QAAQ,KAAK,GAAG,CAAC,GAAG,WAAW;AAAA,IACjD,MAAMA,QAAO,aAAa,KAAK,KAAK,MAAM,KAAK;AAAA,IAC/C,MAAMA,QAAO,aAAa,KAAK,KAAK;AAAA,EACtC,EAAE;AACJ;AAEA,eAAsB,SACpB,MACA,OACA,QAAsB,CAAC,GACI;AAC3B,QAAM,WAAW,MAAM,KAAK,IAAI,eAAe,KAAK,OAAO;AAC3D,QAAM,OAAO,SAAS;AACtB,QAAM,UAAuB,KAAK,UAAU,CAAC,GAAG,IAAI,CAAC,OAAoB;AAAA,IACvE,MAAM,EAAE,OAAO,EAAE,QAAQ;AAAA,IACzB,OAAO,EAAE;AAAA,IACT,MAAM,EAAE,QAAQ;AAAA,IAChB,UAAU,EAAE;AAAA,IACZ,aAAa,EAAE;AAAA,IACf,MAAM,EAAE;AAAA,IACR,UAAU,EAAE;AAAA,EACd,EAAE;AACF,QAAM,WAAyB,KAAK,WAAW,CAAC,GAAG,IAAI,CAAC,OAAqB;AAAA,IAC3E,MAAM,EAAE,OAAO,EAAE,QAAQ;AAAA,IACzB,OAAO,EAAE;AAAA,IACT,aAAa,EAAE;AAAA,IACf,QAAQ,sBAAsB,CAAC;AAAA,IAC/B,OAAO,EAAE;AAAA,IACT,YAAY,EAAE;AAAA,IACd,UAAU,EAAE;AAAA,IACZ,YAAY,EAAE;AAAA,IACd,YAAY,EAAE;AAAA,IACd,YAAY,EAAE;AAAA,IACd,QAAQ,EAAE;AAAA,EACZ,EAAE;AACF,SAAO;AAAA,IACL,OAAO,KAAK,QAAQ;AAAA,IACpB,QAAQ,KAAK,UAAU,CAAC;AAAA,IACxB;AAAA,IACA;AAAA,IACA,WAAW,KAAK,aAAa,CAAC;AAAA,EAChC;AACF;;;AC9GA,eAAsB,OACpB,MACA,OACA,MACuB;AACvB,QAAM,OAAO,MAAM,KAAK,KAAK,eAAe,KAAK,IAAI,IAAI;AACzD,SAAO,KAAK;AACd;;;ACbA,eAAsB,OACpB,MACA,OACA,IACA,MACuB;AACvB,QAAM,OAAO,MAAM,KAAK,IAAI,eAAe,KAAK,IAAI,EAAE,IAAI,IAAI;AAC9D,SAAO,KAAK;AACd;;;ACHA,eAAsB,OAAO,MAAqB,OAAe,IAAmC;AAClG,QAAM,OAAO,MAAM,KAAK,OAAO,eAAe,KAAK,IAAI,mBAAmB,EAAE,CAAC,EAAE;AAC/E,MAAI,KAAK,QAAQ,OAAO,KAAK,SAAS,UAAU;AAC9C,WAAO,KAAK;AAAA,EACd;AACA,SAAO,EAAE,IAAI,QAAQ,KAAK,OAAO;AACnC;;;ACZO,SAAS,aAAa,KAAqB;AAChD,SAAO,IAAI,QAAQ,gBAAgB,CAAC,IAAI,MAAc,EAAE,YAAY,CAAC;AACvE;AAIO,SAAS,aAAa,KAAuD;AAClF,QAAM,MAA+B,CAAC;AACtC,aAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,GAAG,GAAG;AACxC,QAAI,aAAa,CAAC,CAAC,IAAI;AAAA,EACzB;AACA,SAAO;AACT;;;ACbA,IAAM,iBAAiB;AAEhB,IAAM,WAAN,MAAe;AAAA,EACZ,MAAM,oBAAI,IAAoB;AAAA,EAE9B,EAAE,OAAe,KAAqB;AAC5C,WAAO,GAAG,KAAK,IAAI,GAAG;AAAA,EACxB;AAAA,EACA,SAAS,OAAe,KAAa,IAAkB;AACrD,SAAK,IAAI,IAAI,KAAK,EAAE,OAAO,GAAG,GAAG,EAAE;AAAA,EACrC;AAAA,EACA,IAAI,OAAe,KAAiC;AAClD,WAAO,KAAK,IAAI,IAAI,KAAK,EAAE,OAAO,GAAG,CAAC;AAAA,EACxC;AAAA,EACA,WAAmC;AACjC,WAAO,OAAO,YAAY,KAAK,GAAG;AAAA,EACpC;AACF;AAQO,SAAS,iBAAiB,OAA0C;AACzE,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,QAAM,IAAI,eAAe,KAAK,KAAK;AACnC,MAAI,CAAC,EAAG,QAAO;AACf,QAAM,CAAC,EAAE,OAAO,GAAG,IAAI;AACvB,MAAI,UAAU,UAAa,QAAQ,OAAW,QAAO;AACrD,SAAO,EAAE,OAAO,IAAI;AACtB;AAIO,SAAS,YACd,QACA,OAC6D;AAC7D,QAAM,WAAoC,CAAC;AAC3C,QAAM,aAAuB,CAAC;AAC9B,aAAW,CAAC,OAAO,KAAK,KAAK,OAAO,QAAQ,MAAM,GAAG;AACnD,UAAM,KAAK,iBAAiB,KAAK;AACjC,QAAI,CAAC,IAAI;AACP,eAAS,KAAK,IAAI;AAClB;AAAA,IACF;AACA,UAAM,KAAK,MAAM,IAAI,GAAG,OAAO,GAAG,GAAG;AACrC,QAAI,OAAO,QAAW;AACpB,iBAAW,KAAK,GAAG,KAAK,OAAO,GAAG,KAAK,IAAI,GAAG,GAAG,GAAG;AACpD,eAAS,KAAK,IAAI;AAAA,IACpB,OAAO;AACL,eAAS,KAAK,IAAI;AAAA,IACpB;AAAA,EACF;AACA,SAAO,EAAE,UAAU,WAAW;AAChC;;;AC1DA,OAAOC,SAAQ,mBAAmB;AAIlC,IAAM,WAAW,CAAC,SAAS,aAAa,SAAS,OAAO;AAGjD,SAAS,aAAa,UAAkB,YAA8B;AAC3E,QAAM,QAAQ,aAAa,KAAK,UAAU,MAAM;AAChD,QAAM,OAAO,CAAC,YAA2B;AACvC,UAAM,IAAI,SAAS,EAAE,iDAAqC,SAAS,WAAW,OAAO,GAAG,KAAK,GAAG,CAAC;AAAA,EACnG;AAIA,MAAI;AACJ,MAAI;AACF,aAASC,MAAK,KAAK,UAAU,EAAE,QAAQ,YAAY,CAAC;AAAA,EACtD,SAAS,GAAG;AACV,WAAO,KAAK,kCAAe,EAAY,OAAO,EAAE;AAAA,EAClD;AACA,MAAI,CAAC,UAAU,OAAO,WAAW,YAAY,MAAM,QAAQ,MAAM,GAAG;AAClE,WAAO,KAAK,gDAAuB;AAAA,EACrC;AACA,QAAM,MAAM;AACZ,aAAW,KAAK,UAAU;AACxB,QAAI,IAAI,CAAC,MAAM,OAAW,QAAO,KAAK,2CAAkB,CAAC,GAAG;AAAA,EAC9D;AACA,MAAI,CAAC,MAAM,QAAQ,IAAI,KAAK,EAAG,QAAO,KAAK,iCAAkB;AAC7D,MAAI,CAAC,OAAO,UAAU,IAAI,KAAK,EAAG,QAAO,KAAK,wCAAe;AAC7D,MAAI,IAAI,MAAM,WAAW,IAAI,OAAO;AAClC,WAAO,KAAK,yBAAyB,IAAI,KAAK,wBAAc,IAAI,MAAM,MAAM,EAAE;AAAA,EAChF;AAEA,QAAM,WAAW,IAAI;AACrB,QAAM,OAAO,oBAAI,IAAY;AAC7B,aAAW,QAAQ,IAAI,OAAyC;AAC9D,UAAM,IAAI,KAAK,QAAQ;AACvB,QAAI,MAAM,UAAa,MAAM,QAAQ,MAAM,GAAI,QAAO,KAAK,0BAAqB,QAAQ,GAAG;AAC3F,QAAI,KAAK,IAAI,OAAO,CAAC,CAAC,EAAG,QAAO,KAAK,kBAAkB,CAAC,QAAQ,OAAO,IAAI,KAAK,CAAC,EAAE;AACnF,SAAK,IAAI,OAAO,CAAC,CAAC;AAAA,EACpB;AACA,SAAO,EAAE,GAAI,KAA4B,WAAW;AACtD;;;AChCA,eAAsB,UACpB,QACA,UACA,OAAsB,CAAC,GACA;AACvB,QAAM,QAAQ,IAAI,SAAS;AAC3B,QAAM,OAAoB,CAAC;AAC3B,QAAM,QAAQ,EAAE,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,EAAE;AAC9D,QAAM,SAAS,OAAqB,EAAE,GAAG,OAAO,MAAM,MAAM,MAAM,SAAS,EAAE;AAE7E,aAAW,MAAM,UAAU;AACzB,eAAW,QAAQ,GAAG,OAAO;AAC3B,YAAM,MAAM,OAAO,KAAK,GAAG,SAAS,CAAC;AACrC,YAAM,EAAE,UAAU,WAAW,IAAI,YAAY,MAAM,KAAK;AAExD,UAAI,WAAW,SAAS,GAAG;AACzB,aAAK,KAAK,EAAE,OAAO,GAAG,OAAO,KAAK,QAAQ,UAAU,OAAO,8BAA8B,WAAW,KAAK,IAAI,CAAC,GAAG,CAAC;AAClH,cAAM;AACN,YAAI,KAAK,YAAY,OAAQ,QAAO,OAAO;AAC3C;AAAA,MACF;AAEA,UAAI,KAAK,QAAQ;AAEf,cAAM,SAAS,GAAG,OAAO,KAAK,UAAU,GAAG,KAAK,IAAI,GAAG,EAAE;AACzD,aAAK,KAAK,EAAE,OAAO,GAAG,OAAO,KAAK,QAAQ,UAAU,CAAC;AACrD,cAAM;AACN;AAAA,MACF;AAEA,YAAM,UAAU,aAAa,QAAQ;AACrC,UAAI;AACF,cAAM,aAAa,MAAM,OAAO,YAAY,GAAG,OAAO,GAAG,WAAW,GAAG;AACvE,YAAI,cAAc,CAAC,KAAK,YAAY;AAClC,gBAAM,SAAS,GAAG,OAAO,KAAK,UAAU;AACxC,eAAK,KAAK,EAAE,OAAO,GAAG,OAAO,KAAK,QAAQ,WAAW,IAAI,WAAW,CAAC;AACrE,gBAAM;AAAA,QACR,WAAW,cAAc,KAAK,YAAY;AACxC,gBAAM,OAAO,OAAO,GAAG,OAAO,YAAY,OAAO;AACjD,gBAAM,SAAS,GAAG,OAAO,KAAK,UAAU;AACxC,eAAK,KAAK,EAAE,OAAO,GAAG,OAAO,KAAK,QAAQ,WAAW,IAAI,WAAW,CAAC;AACrE,gBAAM;AAAA,QACR,OAAO;AACL,gBAAM,UAAU,MAAM,OAAO,OAAO,GAAG,OAAO,OAAO;AACrD,gBAAM,SAAS,GAAG,OAAO,KAAK,QAAQ,EAAE;AACxC,eAAK,KAAK,EAAE,OAAO,GAAG,OAAO,KAAK,QAAQ,WAAW,IAAI,QAAQ,GAAG,CAAC;AACrE,gBAAM;AAAA,QACR;AAAA,MACF,SAAS,GAAY;AACnB,cAAM,MAAM;AACZ,aAAK,KAAK;AAAA,UACR,OAAO,GAAG;AAAA,UAAO;AAAA,UAAK,QAAQ;AAAA,UAC9B,OAAO,IAAI,UAAU,MAAM,WAAW,IAAI,WAAW;AAAA,UACrD,SAAS,IAAI,UAAU,UAAU,YAAY;AAAA,QAC/C,CAAC;AACD,cAAM;AACN,YAAI,KAAK,YAAY,OAAQ,QAAO,OAAO;AAAA,MAC7C;AAAA,IACF;AAAA,EACF;AACA,SAAO,OAAO;AAChB;;;AC9BO,IAAM,YAAN,MAAM,WAAU;AAAA,EACZ;AAAA,EACA;AAAA,EACA;AAAA,EACD;AAAA,EACA;AAAA,EACA;AAAA,EAER,YAAY,MAAqB;AAC/B,SAAK,UAAU,KAAK,WAAW;AAC/B,SAAK,WAAW,KAAK;AACrB,SAAK,WAAW,KAAK;AACrB,SAAK,aAAa,IAAI,WAAW,KAAK,OAAO;AAC7C,SAAK,gBAAgB,KAAK;AAC1B,SAAK,OAAO,iBAAiB;AAAA,MAC3B,UAAU,KAAK;AAAA,MACf,WAAW,KAAK;AAAA,MAChB,gBAAgB,MAAM,KAAK,eAAe;AAAA,MAC1C,WAAW,MAAM,KAAK,cAAc;AAAA,IACtC,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAuBQ;AAAA,EAER,OAAO,gBAAgB,KAAmC;AACxD,UAAM,IAAI,IAAI,WAAU;AAAA,MACtB,UAAU,IAAI;AAAA,MACd,UAAU,IAAI;AAAA,MACd,SAAS,IAAI;AAAA;AAAA,IACf,CAAC;AACD,IAAC,EAA4C,aAAa,WAAW,YAAY,IAAI,QAAQ;AAC7F,IAAC,EAAsC,WAAW,IAAI;AACtD,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,aAAa,WAAW,MAAgD;AACtE,UAAM,SAAS,MAAM,iBAAiB;AACtC,UAAM,UAAU,KAAK,WAAW,QAAQ,IAAI,eAAe,OAAO;AAClE,UAAM,MAAqB,MAAM,YAAY,OAAO;AACpD,WAAO,IAAI,WAAU;AAAA,MACnB,UAAU,IAAI;AAAA,MACd,UAAU,IAAI;AAAA,MACd;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,MAAmC,OAAe,KAA6C;AAC7F,WAAO,MAAc,KAAK,MAAM,OAAO,GAAG;AAAA,EAC5C;AAAA,EAEA,OAAoB,OAAe,MAAc,OAA8C;AAC7F,WAAO,OAAe,KAAK,MAAM,OAAO,MAAM,KAAK;AAAA,EACrD;AAAA,EAEA,SAAS,OAAe,MAAgD;AACtE,WAAO,SAAc,KAAK,MAAM,OAAO,IAAI;AAAA,EAC7C;AAAA,EAEA,OAAO,OAAe,MAAsD;AAC1E,WAAO,OAAY,KAAK,MAAM,OAAO,IAAI;AAAA,EAC3C;AAAA,EAEA,OAAO,OAAe,IAAY,MAAsD;AACtF,WAAO,OAAY,KAAK,MAAM,OAAO,IAAI,IAAI;AAAA,EAC/C;AAAA,EAEA,OAAO,OAAe,IAAmC;AACvD,WAAO,OAAY,KAAK,MAAM,OAAO,EAAE;AAAA,EACzC;AAAA;AAAA,EAGA,MAAM,YAAY,OAAe,UAAkB,UAA0C;AAC3F,UAAM,MAAM,MAAM,KAAK,MAAM,OAAO,EAAE,QAAQ,EAAE,CAAC,QAAQ,GAAG,SAAS,GAAG,OAAO,EAAE,CAAC;AAClF,UAAM,MAAM,IAAI,KAAK,CAAC;AACtB,WAAO,KAAK,MAAM;AAAA,EACpB;AAAA,EAEA,cAAc,UAAqB,MAA6C;AAC9E,WAAO,UAAU,MAAsB,UAAU,IAAI;AAAA,EACvD;AAAA,EAEA,MAAqB;AACnB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,MAAM,QAAuB;AAAA,EAE7B;AAAA,EAEA,MAAM,iBAAyC;AAC7C,QAAI,KAAK,cAAe,QAAO,KAAK;AACpC,WAAO,KAAK,WAAW,eAAe;AAAA,EACxC;AAAA,EAEA,MAAM,gBAA+B;AACnC,QAAI,KAAK,eAAe;AAEtB,YAAM,IAAI,MAAM,gDAAgD;AAAA,IAClE;AACA,UAAM,OAAO,MAAM,KAAK,WAAW,SAAS;AAC5C,QAAI,CAAC,KAAM,OAAM,IAAI,MAAM,gBAAgB;AAC3C,QAAI,KAAK,WAAW,MAAO,OAAM,IAAI,MAAM,kCAAkC;AAC7E,QAAI,aAAa,MAAM,CAAC,GAAG;AAIzB,YAAM,cAAc,KAAK,WACrB,EAAE,UAAU,KAAK,UAAU,UAAU,KAAK,UAAU,YAAY,KAAK,KAAK,IAC1E,EAAE,UAAU,KAAK,UAAU,SAAS,KAAK,SAAS,YAAY,KAAK,KAAK;AAC5E,YAAM,aAAa,WAAW;AAAA,IAChC;AAAA,EACF;AACF;;;ACtLA,YAAYC,SAAQ;AACpB,YAAY,SAAS;AACrB,YAAYC,SAAQ;AACpB,YAAYC,WAAU;AActB,IAAM,cAAsC;AAAA,EAC1C,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,SAAS;AACX;AAEO,SAAS,cAAc,UAA0B;AACtD,SAAO,YAAiB,cAAQ,QAAQ,EAAE,YAAY,CAAC,KAAK;AAC9D;AAQO,IAAM,2BAA2B;AAgBxC,eAAsB,eACpB,MACA,UACA,kBAA0B,0BACD;AACzB,QAAM,MAAM,WAAW,QAAQ;AAC/B,QAAM,MAAM,MAAU,aAAS,GAAG;AAClC,QAAM,WAAgB,eAAS,GAAG;AAClC,QAAM,OAAO,IAAI,SAAS;AAC1B,OAAK,OAAO,QAAQ,IAAI,KAAK,CAAC,GAAG,GAAG,EAAE,MAAM,cAAc,GAAG,EAAE,CAAC,GAAG,QAAQ;AAC3E,OAAK,OAAO,mBAAmB,eAAe;AAC9C,OAAK,OAAO,oBAAoB,MAAM;AACtC,QAAM,OAAO,MAAM,KAAK,KAAK,yBAAyB,IAAI;AAE1D,QAAM,OAAY,KAAK,MAAM,QAAQ,KAAK;AAC1C,SAAO;AAAA,IACL,YAAY,OAAO,KAAK,cAAc,KAAK,EAAE;AAAA,IAC7C,UAAU,OAAO,KAAK,YAAY,QAAQ;AAAA,IAC1C,UAAU,OAAO,KAAK,eAAe,KAAK,YAAY,cAAc,GAAG,CAAC;AAAA,IACxE,UAAU,OAAO,KAAK,YAAY,KAAK,QAAQ,IAAI,MAAM;AAAA,EAC3D;AACF;AAGO,SAAS,qBAA6B;AAC3C,SAAY,WAAQ,YAAQ,GAAG,aAAa,KAAK;AACnD;AAOO,SAAS,oBAAoB,SAA6B,KAAqB;AACpF,MAAI,CAAC,WAAW,CAAC,IAAI,WAAW,GAAG,EAAG,QAAO;AAC7C,SAAO,QAAQ,QAAQ,QAAQ,EAAE,IAAI;AACvC;AAMA,eAAsB,iBACpB,MACA,KACA,UACiB;AACjB,QAAM,MAAM,IAAI,eAAe,2BAA2B,IAAI,UAAU;AACxE,QAAM,OAAO,MAAM,KAAK,IAAI,KAAK,EAAE,cAAc,cAAc,CAAC;AAChE,QAAM,aAAa,6BAA6B,KAAK,UAAU,qBAAqB,CAAC;AAIrF,QAAM,UAAU,IAAI,YAAY,cAAc,GAAG,IAAI,cAAc,UAAU;AAI7E,MAAI;AACJ,MAAI;AACJ,QAAM,WAAW,WAAW,WAAW,QAAQ,IAAI;AACnD,MAAI,aAAa,SAAS,SAAc,SAAG,KAAK,cAAc,QAAQ,IAAI;AACxE,UAAM;AACN,iBAAa;AAAA,EACf,WAAW,UAAU;AACnB,UAAW,cAAQ,QAAQ;AAC3B,iBAAkB,eAAS,QAAQ;AAAA,EACrC,OAAO;AACL,UAAM,mBAAmB;AACzB,iBAAa;AAAA,EACf;AACA,QAAM,YAAY,aAAa,YAAY,IAAI,UAAU;AACzD,MAAI,SAAc,WAAK,KAAK,SAAS;AAErC,QAAM,cAAmB,cAAQ,GAAG;AACpC,MAAS,cAAQ,MAAM,MAAW,WAAK,aAAa,SAAS,GAAG;AAC9D,UAAM,IAAI,MAAM,6CAAU,MAAM,iBAAO,WAAW,SAAI;AAAA,EACxD;AACA,QAAU,UAAM,KAAK,EAAE,WAAW,KAAK,CAAC;AACxC,WAAS,WAAW,MAAM;AAC1B,QAAU,cAAU,QAAQ,OAAO,KAAK,KAAK,IAAmB,CAAC;AACjE,SAAO;AACT;AAEA,SAAS,WAAW,GAAmB;AACrC,SAAO,EAAE,WAAW,GAAG,IAAS,WAAQ,YAAQ,GAAG,EAAE,MAAM,CAAC,CAAC,IAAS,cAAQ,CAAC;AACjF;AAMA,SAAS,aAAa,SAAiB,YAA6B;AAElE,QAAM,OAAY,eAAS,QAAQ,QAAQ,OAAO,GAAG,CAAC;AACtD,MAAI,CAAC,QAAQ,SAAS,OAAO,SAAS,QAAQ,KAAK,SAAS,GAAG,GAAG;AAChE,WAAO,aAAa,GAAG,UAAU,SAAS;AAAA,EAC5C;AACA,SAAO;AACT;AAEA,SAAS,cAAc,GAAoB;AACzC,MAAI;AACF,WAAU,aAAS,CAAC,EAAE,YAAY;AAAA,EACpC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGA,SAAS,WAAW,QAAwB;AAC1C,MAAI,CAAI,eAAW,MAAM,EAAG,QAAO;AACnC,QAAM,MAAW,cAAQ,MAAM;AAC/B,QAAM,MAAW,cAAQ,MAAM;AAC/B,QAAM,OAAY,eAAS,QAAQ,GAAG;AACtC,WAAS,IAAI,KAAK,KAAK;AACrB,UAAM,YAAiB,WAAK,KAAK,GAAG,IAAI,KAAK,CAAC,IAAI,GAAG,EAAE;AACvD,QAAI,CAAI,eAAW,SAAS,EAAG,QAAO;AAAA,EACxC;AACF;AAEA,SAAS,6BAA6B,aAA0C;AAC9E,MAAI,CAAC,YAAa,QAAO;AACzB,QAAM,OAAO,6BAA6B,KAAK,WAAW;AAC1D,MAAI,OAAO,CAAC,GAAG;AACb,QAAI;AACF,aAAO,mBAAmB,KAAK,CAAC,CAAC;AAAA,IACnC,QAAQ;AAAA,IAER;AAAA,EACF;AACA,QAAM,QAAQ,yBAAyB,KAAK,WAAW;AACvD,SAAO,QAAQ,CAAC;AAClB;;;AC9LO,SAAS,OAAO,OAAwB;AAC7C,SAAO,KAAK,UAAU,OAAO,MAAM,CAAC;AACtC;;;ACFA,OAAOC,WAAU;AAEV,SAAS,OAAO,OAAwB;AAC7C,SAAOA,MAAK,KAAK,OAAO,EAAE,QAAQ,GAAG,WAAW,GAAG,CAAC;AACtD;;;ACJA,OAAO,WAAW;;;ACKlB,SAAS,iBAAiB,IAAoB;AAC5C,SAAQ,MAAM,QAAU,MAAM;AAAA,EAC3B,MAAM,SAAU,MAAM;AAAA,EACtB,MAAM,SAAU,MAAM;AAAA,EACtB,MAAM,SAAU,MAAM;AAAA,EACtB,MAAM,SAAU,MAAM;AAAA,EACtB,MAAM,SAAU,MAAM;AAAA,EACtB,MAAM,SAAU,MAAM,SACtB,MAAM,UAAW,MAAM,SACtB,IACA;AACN;AAGO,SAAS,aAAa,GAAmB;AAC9C,MAAI,IAAI;AACR,aAAW,MAAM,EAAG,MAAK,iBAAiB,GAAG,YAAY,CAAC,CAAW;AACrE,SAAO;AACT;AAGO,SAAS,gBAAgB,GAAW,UAA0B;AACnE,MAAI,aAAa,CAAC,KAAK,SAAU,QAAO;AACxC,MAAI,IAAI;AACR,MAAI,MAAM;AACV,aAAW,MAAM,GAAG;AAClB,UAAM,KAAK,iBAAiB,GAAG,YAAY,CAAC,CAAW;AACvD,QAAI,IAAI,KAAK,WAAW,EAAG;AAC3B,SAAK;AACL,WAAO;AAAA,EACT;AACA,SAAO,MAAM;AACf;;;ADlCA,IAAM,gBAAgB;AAOf,SAAS,QAAQ,MAAyC;AAC/D,MAAI,KAAK,WAAW,EAAG,QAAO;AAC9B,QAAM,OAAO,OAAO,KAAK,KAAK,CAAC,CAAE;AACjC,QAAM,QAAQ,KAAK,IAAI,CAAC,MAAM,EAAE,YAAY,CAAC;AAC7C,QAAM,QAAQ,KAAK,IAAI,CAAC,MAAM,KAAK,IAAI,CAAC,MAAM,WAAW,EAAE,CAAC,CAAC,CAAC,CAAC;AAG/D,QAAM,gBAAgB,KAAK;AAAA,IAAI,CAAC,GAAG,MACjC,KAAK;AAAA,MACH;AAAA,MACA,KAAK,IAAI,aAAa,MAAM,CAAC,CAAE,GAAG,GAAG,MAAM,IAAI,CAAC,QAAQ,aAAa,IAAI,CAAC,CAAE,CAAC,CAAC;AAAA,IAChF;AAAA,EACF;AAEA,QAAM,QAAQ,IAAI,MAAM;AAAA,IACtB,MAAM;AAAA,IACN,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE;AAAA;AAAA,IAExB,WAAW,cAAc,IAAI,CAAC,MAAM,IAAI,CAAC;AAAA,IACzC,UAAU;AAAA,EACZ,CAAC;AACD,aAAW,OAAO,MAAO,OAAM,KAAK,GAAG;AACvC,SAAO,MAAM,SAAS;AACxB;AAEA,SAAS,WAAW,GAAoB;AACtC,MAAI,MAAM,QAAQ,MAAM,OAAW,QAAO;AAC1C,QAAM,IAAI,OAAO,MAAM,WAAW,KAAK,UAAU,CAAC,IAAI,OAAO,CAAC;AAC9D,SAAO,gBAAgB,GAAG,aAAa;AACzC;;;AE7BO,SAAS,WAAW,MAAiC,MAA6B;AACvF,UAAQ,KAAK,QAAQ;AAAA,IACnB,KAAK;AACH,aAAO,OAAO,IAAI;AAAA,IACpB,KAAK;AACH,aAAO,OAAO,IAAI;AAAA,IACpB,KAAK;AACH,aAAO,QAAQ,IAAI;AAAA,EACvB;AACF;AAEO,SAAS,aAAa,KAAc,MAA6B;AACtE,UAAQ,KAAK,QAAQ;AAAA,IACnB,KAAK;AACH,aAAO,OAAO,GAAG;AAAA,IACnB,KAAK;AACH,aAAO,OAAO,GAAG;AAAA,IACnB,KAAK;AAEH,aAAO,OAAO,GAAG;AAAA,EACrB;AACF;;;AClBA,eAAsB,uBACpB,OACuB;AACvB,QAAM,OAAO,MAAM,MAAM,WAAW;AAAA,IAClC,GAAG,MAAM,QAAQ;AAAA,IACjB;AAAA,MACE,YAAY;AAAA,MACZ,WAAW,MAAM;AAAA,MACjB,eAAe,MAAM;AAAA,IACvB;AAAA,IACA,EAAE,SAAS,EAAE,gBAAgB,mBAAmB,EAAE;AAAA,EACpD;AACA,QAAM,EAAE,cAAc,eAAe,YAAY,MAAM,IAAI,KAAK;AAChE,MAAI,CAAC,cAAc;AACjB,UAAM,IAAI,MAAM,qCAAqC;AAAA,EACvD;AAEA,QAAM,aAAa,cAAc;AACjC,QAAM,YAAY,IAAI,KAAK,KAAK,IAAI,IAAI,aAAa,GAAI,EAAE,YAAY;AACvE,QAAM,UAAU,SAAS,IAAI,MAAM,KAAK,EAAE,OAAO,OAAO;AAExD,QAAM,QAAQ,IAAI,WAAW,MAAM,OAAO;AAC1C,QAAM,iBAAiB,MAAM,MAAM,eAAe,YAAY;AAC9D,QAAM,kBAAkB,gBAAgB,MAAM,MAAM,gBAAgB,aAAa,IAAI;AAErF,QAAM,SAAsB;AAAA,IAC1B,QAAQ;AAAA,IACR;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACA,QAAM,MAAM,UAAU,MAAM;AAG5B,SAAO,EAAE,OAAO;AAClB;;;ACtCA,eAAsB,SAAS,OAA6C;AAE1E,QAAM,OAAO,MAAM,MAAM,WAAW,IAAI,GAAG,MAAM,QAAQ,kBAAkB;AAAA,IACzE,SAAS,EAAE,eAAe,UAAU,MAAM,GAAG,GAAG;AAAA,EAClD,CAAC;AACD,QAAM,SAAmB,KAAK,MAAM,UAAU,CAAC;AAI/C,QAAM,QAAQ,IAAI,WAAW,MAAM,OAAO;AAC1C,QAAM,iBAAiB,MAAM,MAAM,eAAe,MAAM,GAAG;AAG3D,QAAM,YAAY,IAAI,KAAK,KAAK,IAAI,IAAI,MAAM,KAAK,OAAO,GAAI,EAAE,YAAY;AAE5E,QAAM,SAAsB;AAAA,IAC1B,QAAQ;AAAA,IACR;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACA,QAAM,MAAM,UAAU,MAAM;AAC5B,SAAO,EAAE,QAAQ,WAAW,YAAY,KAAK,IAAI,EAAE;AACrD;;;ACTA,eAAsB,aAAa,OAAiD;AAClF,QAAM,OAAO,MAAM,MAAM,WAAW;AAAA,IAClC,GAAG,MAAM,QAAQ;AAAA,IACjB,EAAE,MAAM,MAAM,KAAK;AAAA,IACnB,EAAE,SAAS,EAAE,gBAAgB,mBAAmB,EAAE;AAAA,EACpD;AAEA,QAAM,EAAE,OAAO,WAAW,KAAK,IAAI,KAAK;AACxC,MAAI,CAAC,OAAO;AACV,UAAM,IAAI,MAAM,yCAAyC;AAAA,EAC3D;AAEA,QAAM,aAAa,aAAa;AAChC,QAAM,YAAY,IAAI,KAAK,KAAK,IAAI,IAAI,aAAa,GAAI,EAAE,YAAY;AAEvE,QAAM,QAAQ,IAAI,WAAW,MAAM,OAAO;AAC1C,QAAM,iBAAiB,MAAM,MAAM,eAAe,KAAK;AAEvD,QAAM,SAAsB;AAAA,IAC1B,QAAQ;AAAA,IACR;AAAA;AAAA,IAEA,iBAAiB;AAAA,IACjB;AAAA,IACA,QAAQ,CAAC,GAAG;AAAA,IACZ;AAAA,EACF;AACA,QAAM,MAAM,UAAU,MAAM;AAC5B,SAAO,EAAE,QAAQ,WAAW,YAAY,IAAI,EAAE;AAChD;AAQO,SAAS,SAAS,KAAqB;AAC5C,QAAM,aAAa,gBAAgB,KAAK,GAAG,IAAI,MAAM,WAAW,GAAG;AACnE,QAAM,MAAM,IAAI,IAAI,UAAU;AAC9B,SAAO,IAAI;AACb;AASO,SAAS,aAAa,QAAwB;AACnD,QAAM,OAAO,IAAI,IAAI,MAAM,EAAE;AAC7B,MAAI,gBAAgB,KAAK,IAAI,KAAK,SAAS,YAAa,QAAO;AAC/D,QAAM,QAAQ,KAAK,MAAM,GAAG,EAAE,OAAO,OAAO;AAC5C,MAAI,MAAM,WAAW,EAAG,QAAO;AAE/B,QAAM,YAAY,MAAM,UAAU,IAAI,MAAM,MAAM,SAAS,CAAC,IAAI,MAAM,CAAC;AACvE,SAAO,aAAa;AACtB;;;AClEO,IAAM,2BAA2B;AAcjC,SAAS,8BAA8B,KAA0C;AACtF,QAAM,OAAO,KAAK,UAAU;AAC5B,QAAM,QAAQ,MAAM,SAAS;AAC7B,MAAI,OAAO,SAAS,yBAA0B,QAAO;AACrD,QAAM,QAAQ,OAAO,SAAS;AAC9B,MAAI,CAAC,MAAO,QAAO;AACnB,SAAO;AAAA,IACL,qBAAqB,OAAO,KAAK;AAAA,IACjC,kBAAkB,OAAO,OAAO,SAAS,gBAAgB,KAAK;AAAA,EAChE;AACF;AAWA,eAAsB,eAAe,OAA2C;AAC9E,QAAM,MAAM,WAAW;AAAA,IACrB,GAAG,MAAM,QAAQ;AAAA,IACjB;AAAA,MACE,aAAa,MAAM;AAAA,MACnB,aAAa,MAAM;AAAA,MACnB,iBAAiB,MAAM;AAAA,IACzB;AAAA,IACA;AAAA,MACE,SAAS;AAAA,QACP,gBAAgB;AAAA,QAChB,eAAe,UAAU,MAAM,mBAAmB;AAAA,MACpD;AAAA,IACF;AAAA,EACF;AACF;;;AC3CA,eAAsB,mBACpB,MACA,OACmC;AACnC,QAAM,WAAW,MAAM,KAAK,KAAK,mCAAmC,KAAK;AACzE,QAAM,UAAU,SAAS,MAAM,QAAQ,SAAS;AAChD,SAAO;AACT;;;AChCA,OAAO,eAAe;AAaf,SAAS,gBAAgB,QAA2B;AACzD,MAAI,QAAQ,YAAY,UAAW,QAAO;AAC1C,MAAI,QAAQ,YAAY,OAAO,OAAO,QAAQ,SAAU,QAAO;AAC/D,SAAO;AACT;AAcO,IAAM,WAAN,MAAe;AAAA,EAYpB,YAAoB,MAAoB;AAApB;AAAA,EAAqB;AAAA,EAArB;AAAA,EAXZ;AAAA,EACA,WAAsB,CAAC;AAAA,EACvB;AAAA,EACA,QAAQ;AAAA,EACR;AAAA;AAAA;AAAA,EAGA,qBAAqB,oBAAI,IAAgB;AAAA;AAAA,EAEzC,mBAAmB;AAAA;AAAA;AAAA;AAAA;AAAA,EAQ3B,aAAa,IAA4B;AACvC,SAAK,mBAAmB,IAAI,EAAE;AAC9B,WAAO,MAAM;AACX,WAAK,mBAAmB,OAAO,EAAE;AAAA,IACnC;AAAA,EACF;AAAA,EAEQ,mBAAyB;AAC/B,QAAI,KAAK,iBAAkB;AAC3B,SAAK,QAAQ;AACb,eAAW,MAAM,CAAC,GAAG,KAAK,kBAAkB,GAAG;AAC7C,UAAI;AACF,WAAG;AAAA,MACL,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,UAAyB;AAC7B,UAAM,MAAM,MAAM,KAAK,KAAK,eAAe;AAC3C,QAAI,CAAC,KAAK;AACR,YAAM,IAAI,SAAS;AAAA,QACjB;AAAA,QACA,SAAS;AAAA,MACX,CAAC;AAAA,IACH;AACA,SAAK,KAAK,IAAI,UAAU,KAAK,KAAK,QAAQ;AAE1C,WAAO,IAAI,QAAc,CAACC,UAAS,WAAW;AAC5C,YAAM,SAAS,CAAC,OAAmB;AACjC,YAAI;AACJ,WAAG;AAAA,MACL;AACA,YAAM,MAAM,MAAM;AAChB,aAAK,IAAI,eAAe,QAAQ,MAAM;AACtC,aAAK,IAAI,eAAe,WAAW,SAAS;AAC5C,aAAK,IAAI,eAAe,SAAS,OAAO;AACxC,aAAK,IAAI,eAAe,SAAS,OAAO;AAAA,MAC1C;AACA,YAAM,SAAS,MAAM;AACnB,aAAK,IAAI,KAAK,KAAK,UAAU,EAAE,MAAM,QAAQ,OAAO,IAAI,CAAC,CAAC;AAAA,MAC5D;AACA,YAAM,YAAY,CAAC,SAA4B;AAC7C,cAAM,OAAO,KAAK,SAAS;AAE3B,YAAI;AACJ,YAAI;AACF,mBAAS,KAAK,MAAM,IAAI;AAAA,QAC1B,QAAQ;AACN;AAAA,QACF;AAGA,gBAAQ,gBAAgB,MAAM,GAAG;AAAA,UAC/B,KAAK;AACH,iBAAK,gBAAgB,MAAgC;AACrD;AAAA,UACF,KAAK;AACH,iBAAK,cAAc,MAA6B;AAChD;AAAA,UACF,KAAK;AACH;AAAA,QACJ;AACA,cAAM,MAAM;AACZ,aAAK,mBAAmB,GAAG;AAE3B,YAAI,IAAI,SAAS,gBAAgB;AAC/B,eAAK,QAAQ;AACb,eAAK,mBAAmB;AAExB,eAAK,YAAa,IAAY;AAC9B,eAAK,eAAe;AACpB,iBAAOA,QAAO;AAEd,eAAK,GAAI,GAAG,WAAW,KAAK,mBAAmB;AAI/C,eAAK,GAAI,GAAG,SAAS,KAAK,iBAAiB;AAC3C,eAAK,GAAI,GAAG,SAAS,KAAK,iBAAiB;AAAA,QAC7C,WAAW,IAAI,SAAS,cAAc;AACpC;AAAA,YAAO,MACL;AAAA,cACE,IAAI,SAAS;AAAA,gBACX;AAAA;AAAA,gBAEA,SAAS,GAAI,IAAY,IAAI,KAAM,IAAY,OAAO;AAAA,cACxD,CAAC;AAAA,YACH;AAAA,UACF;AAAA,QACF,WAAW,IAAI,SAAS,oBAAoB,KAAK,KAAK,iBAAiB;AACrE,eAAK,KAAK,KAAK,gBAAgB;AAAA,QACjC;AAAA,MACF;AACA,YAAM,UAAU,CAAC,QACf;AAAA,QAAO,MACL,OAAO,IAAI,SAAS,EAAE,2CAAkC,SAAS,IAAI,QAAQ,CAAC,CAAC;AAAA,MACjF;AACF,YAAM,UAAU,MACd;AAAA,QAAO,MACL;AAAA,UACE,IAAI,SAAS,EAAE,2CAAkC,SAAS,wBAAwB,CAAC;AAAA,QACrF;AAAA,MACF;AACF,WAAK,GAAI,GAAG,QAAQ,MAAM;AAC1B,WAAK,GAAI,GAAG,WAAW,SAAS;AAChC,WAAK,GAAI,GAAG,SAAS,OAAO;AAC5B,WAAK,GAAI,GAAG,SAAS,OAAO;AAAA,IAC9B,CAAC;AAAA,EACH;AAAA,EAEA,KAAQ,KAAqC;AAC3C,QAAI,CAAC,KAAK,SAAS,CAAC,KAAK,IAAI;AAC3B,YAAM,IAAI,SAAS,EAAE,2CAAkC,SAAS,eAAe,CAAC;AAAA,IAClF;AACA,SAAK,GAAG,KAAK,KAAK,UAAU,GAAG,CAAC;AAAA,EAClC;AAAA,EAEA,QAAQ,IAAmD;AACzD,UAAM,IAAa,EAAE,MAAM,SAAS,GAAG;AACvC,SAAK,SAAS,KAAK,CAAC;AACpB,WAAO,MAAM;AACX,WAAK,WAAW,KAAK,SAAS,OAAO,CAAC,MAAM,MAAM,CAAC;AAAA,IACrD;AAAA,EACF;AAAA,EAEA,aAAa,IAAkD;AAC7D,UAAM,IAAa,EAAE,MAAM,cAAc,GAAG;AAC5C,SAAK,SAAS,KAAK,CAAC;AACpB,WAAO,MAAM;AACX,WAAK,WAAW,KAAK,SAAS,OAAO,CAAC,MAAM,MAAM,CAAC;AAAA,IACrD;AAAA,EACF;AAAA,EAEA,UAAU,IAAsD;AAC9D,UAAM,IAAa,EAAE,MAAM,WAAW,GAAG;AACzC,SAAK,SAAS,KAAK,CAAC;AACpB,WAAO,MAAM;AACX,WAAK,WAAW,KAAK,SAAS,OAAO,CAAC,MAAM,MAAM,CAAC;AAAA,IACrD;AAAA,EACF;AAAA,EAEA,UAAmB;AACjB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,kBAAiC;AACrC,QAAI,KAAK,SAAS,KAAK,MAAM,KAAK,GAAG,eAAe,UAAU,KAAM;AACpE,UAAM,KAAK,MAAM;AACjB,UAAM,KAAK,QAAQ;AAAA,EACrB;AAAA,EAEA,eAAmC;AACjC,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,MAAM,QAAuB;AAC3B,QAAI,KAAK,eAAgB,eAAc,KAAK,cAAc;AAC1D,SAAK,QAAQ;AACb,SAAK,mBAAmB;AACxB,QAAI,KAAK,IAAI;AACX,WAAK,GAAG,eAAe,WAAW,KAAK,mBAAmB;AAC1D,WAAK,GAAG,eAAe,SAAS,KAAK,iBAAiB;AACtD,WAAK,GAAG,eAAe,SAAS,KAAK,iBAAiB;AACtD,UAAI,KAAK,GAAG,eAAe,UAAU,KAAM,MAAK,GAAG,MAAM;AAAA,IAC3D;AAAA,EACF;AAAA;AAAA,EAGQ,oBAAoB,MAAY;AACtC,SAAK,iBAAiB;AAAA,EACxB;AAAA,EAEQ,sBAAsB,CAAC,SAAkC;AAC/D,UAAM,OAAO,KAAK,SAAS;AAE3B,QAAI;AACJ,QAAI;AACF,eAAS,KAAK,MAAM,IAAI;AAAA,IAC1B,QAAQ;AACN;AAAA,IACF;AACA,YAAQ,gBAAgB,MAAM,GAAG;AAAA,MAC/B,KAAK;AACH,aAAK,gBAAgB,MAAgC;AACrD;AAAA,MACF,KAAK;AACH,aAAK,cAAc,MAA6B;AAChD;AAAA,MACF,KAAK;AACH;AAAA,IACJ;AACA,UAAM,MAAM;AACZ,SAAK,mBAAmB,GAAG;AAC3B,QAAI,IAAI,SAAS,oBAAoB,KAAK,KAAK,iBAAiB;AAC9D,WAAK,KAAK,KAAK,gBAAgB;AAAA,IACjC;AAAA,EACF;AAAA,EAEQ,iBAAuB;AAC7B,UAAM,KAAK,KAAK,KAAK,eAAe;AACpC,SAAK,iBAAiB,YAAY,MAAM;AACtC,UAAI;AACF,aAAK,IAAI,KAAK,KAAK,UAAU,EAAE,MAAM,OAAO,CAAC,CAAC;AAAA,MAChD,QAAQ;AAAA,MAER;AAAA,IACF,GAAG,EAAE;AAAA,EACP;AAAA,EAEQ,cAAc,IAA+B;AACnD,eAAW,KAAK,KAAK,SAAU,KAAI,EAAE,SAAS,QAAS,GAAE,GAAG,EAAE;AAAA,EAChE;AAAA,EAEQ,mBAAmB,KAA8B;AACvD,eAAW,KAAK,KAAK,SAAU,KAAI,EAAE,SAAS,aAAc,GAAE,GAAG,GAAG;AAAA,EACtE;AAAA,EAEQ,gBAAgB,IAAkC;AACxD,eAAW,KAAK,KAAK,SAAU,KAAI,EAAE,SAAS,UAAW,GAAE,GAAG,EAAE;AAAA,EAClE;AACF;;;ACnRA,SAAS,cAAAC,mBAAkB;;;ACI3B,SAAS,kBAAkB;AAqCpB,SAAS,wBAAwB,SAAsD;AAC5F,SAAO;AAAA,IACL,MAAM,QAAQ;AAAA,IACd,eAAe,QAAQ;AAAA,IACvB,SAAU,QAAQ,WAAsB;AAAA,IACxC,iBAAkB,QAAQ,mBAA8B;AAAA,IACxD,YAAa,QAAQ,cAAyB;AAAA,IAC9C,WAAW,QAAQ;AAAA,IACnB,UAAU,QAAQ;AAAA,IAClB,aAAa,QAAQ;AAAA,IACrB,UAAU,QAAQ;AAAA,IAClB,SAAS,QAAQ;AAAA,EACnB;AACF;AAEO,SAAS,yBAAyB,SAAuD;AAC9F,SAAO;AAAA,IACL,eAAe,QAAQ;AAAA,IACvB,OAAQ,QAAQ,SAAoB;AAAA,EACtC;AACF;AAGA,SAAS,SACP,MACA,gBACA,SACe;AACf,SAAO,EAAE,iBAAiB,OAAO,WAAW,WAAW,GAAG,gBAAgB,MAAM,WAAW,KAAK,IAAI,GAAG,QAAQ;AACjH;AAEO,SAAS,aAAa,gBAAwB,gBAAuC;AAC1F,SAAO,SAAS,oBAAoB,gBAAgB,EAAE,eAAe,CAAC;AACxE;AAEO,SAAS,YAAY,gBAAwB,gBAAwB,QAAgC;AAC1G,SAAO,SAAS,mBAAmB,gBAAgB,SAAS,EAAE,gBAAgB,OAAO,IAAI,EAAE,eAAe,CAAC;AAC7G;AAEO,SAAS,YACd,eACA,gBACA,QACA,aACe;AACf,SAAO,SAAS,sBAAsB,gBAAgB,EAAE,eAAe,QAAQ,aAAa,eAAe,KAAK,CAAC;AACnH;AAMO,SAAS,eAAe,gBAAuC;AACpE,SAAO,SAAS,sBAAsB,gBAAgB,CAAC,CAAC;AAC1D;AAMO,SAAS,WAAW,SAAiB,gBAAuC;AACjF,SAAO,SAAS,kBAAkB,gBAAgB,EAAE,QAAQ,CAAC;AAC/D;;;ADxCA,eAAsB,qBACpB,IACA,QACA,OAAwB,CAAC,GACK;AAC9B,QAAM,iBAAiB,KAAK,kBAAkBC,YAAW;AACzD,QAAM,YAAYA,YAAW;AAC7B,QAAM,QAA+B,CAAC;AACtC,QAAM,UAAoE,CAAC;AAC3E,MAAI,OAAO;AACX,QAAM,eAAe,oBAAI,IAAY;AAIrC,QAAM,OAAO,CAAC,CAAC,KAAK;AACpB,QAAM,sBAAsB,oBAAI,IAAkC;AAClE,MAAI,kBAAkB;AACtB,MAAI;AAEJ,QAAM,SAAS,MAAY;AACzB,QAAI,KAAM;AACV,WAAO;AACP,WAAO,QAAQ,QAAQ;AAErB,cAAQ,MAAM,EAAG,EAAE,OAAO,QAAkB,MAAM,KAAK,CAAC;AAAA,IAC1D;AACA,QAAI;AACJ,yBAAqB;AACrB,oBAAgB;AAAA,EAClB;AAKA,QAAM,gBAAgB,GAAG,eAAe,MAAM,OAAO,CAAC;AAEtD,QAAM,MAAM,GAAG,QAAQ,CAAC,OAAO;AAC7B,QAAI,KAAM;AACV,QAAI,GAAG,cAAc,UAAW;AAIhC,QAAI,GAAG,WAAW,aAAa,IAAI,GAAG,OAAO,EAAG;AAChD,QAAI,GAAG,QAAS,cAAa,IAAI,GAAG,OAAO;AAC3C,QAAI,QAAQ,QAAQ;AAElB,cAAQ,MAAM,EAAG,EAAE,OAAO,IAAI,MAAM,MAAM,CAAC;AAAA,IAC7C,OAAO;AACL,YAAM,KAAK,EAAE;AAAA,IACf;AAGA,QACE,GAAG,SAAS,wBACZ,GAAG,SAAS,qBACZ,GAAG,SAAS,wBACZ;AACA,aAAO;AACP;AAAA,IACF;AAIA,QACE,GAAG,SAAS,kBACZ,CAAE,GAAG,SAAsD,iBAC3D;AACA,UAAI,CAAC,QAAQ,oBAAoB,SAAS,GAAG;AAC3C,eAAO;AAAA,MACT,OAAO;AAEL,0BAAkB;AAAA,MACpB;AAAA,IACF;AAAA,EACF,CAAC;AAID,MAAI,MAAM;AACR,yBAAqB,GAAG,UAAU,CAAC,QAAQ;AACzC,UAAI,QAAQ,IAAI,mBAAmB,eAAgB;AACnD,YAAM,IAAI,IAAI;AACd,UAAI,CAAC,GAAG,cAAe;AACvB,UAAI,IAAI,SAAS,+BAA+B;AAO9C,mBAAW,CAAC,IAAI,EAAE,KAAK,qBAAqB;AAC1C,cAAI,GAAG,QAAS,qBAAoB,OAAO,EAAE;AAAA,QAC/C;AACA,4BAAoB,IAAI,EAAE,eAAe,EAAE,SAAS,MAAM,CAAC;AAAA,MAC7D,WAAW,IAAI,SAAS,gCAAgC;AACtD,YAAI,EAAE,UAAU,WAAW;AACzB,gBAAM,KAAK,oBAAoB,IAAI,EAAE,aAAa;AAClD,cAAI,GAAI,IAAG,UAAU;AAAA,QACvB,WAAW,EAAE,UAAU,YAAY;AACjC,8BAAoB,OAAO,EAAE,aAAa;AAC1C,cAAI,oBAAoB,SAAS,KAAK,gBAAiB,QAAO;AAAA,QAChE;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH;AAEA,QAAM,MACJ,KAAK,qBAAqB,OACtB;AAAA;AAAA,IAEE,iBAAiB;AAAA,IACjB;AAAA,IACA;AAAA,IACA,MAAM;AAAA,IACN,WAAW,KAAK,IAAI;AAAA,IACpB,SAAS,EAAE,SAAS,KAAK,kBAAkB;AAAA,EAC7C,IACA;AAAA;AAAA,IAEE,iBAAiB;AAAA,IACjB;AAAA,IACA;AAAA,IACA,MAAM;AAAA,IACN,WAAW,KAAK,IAAI;AAAA,IACpB,SAAS;AAAA,MACP,SAAS;AAAA,MACT,UAAU,KAAK;AAAA,MACf,kBAAkB,KAAK;AAAA,MACvB,aAAa,KAAK;AAAA,MAClB,aAAa,KAAK,aAAa,SAAS,KAAK,cAAc;AAAA,IAC7D;AAAA,EACF;AACN,KAAG,KAAK,GAAG;AAEX,QAAM,SAA6C;AAAA,IACjD,CAAC,OAAO,aAAa,GAAG,OAAO;AAAA,MAC7B,MAAM,MACJ,IAAI,QAAQ,CAACC,aAAY;AACvB,YAAI,MAAM,QAAQ;AAEhB,UAAAA,SAAQ,EAAE,OAAO,MAAM,MAAM,GAAI,MAAM,MAAM,CAAC;AAAA,QAChD,WAAW,MAAM;AAEf,UAAAA,SAAQ,EAAE,OAAO,QAAkB,MAAM,KAAK,CAAC;AAAA,QACjD,OAAO;AACL,kBAAQ,KAAKA,QAAO;AAAA,QACtB;AAAA,MACF,CAAC;AAAA,IACL;AAAA,EACF;AAEA,QAAM,SAAS,CAAC,UAAkB,kBAAwB;AACxD,QAAI;AAKF,SAAG,KAAK,eAAe,cAAc,CAAC;AAAA,IACxC,QAAQ;AAAA,IAER;AAAA,EACF;AAEA,QAAM,YAAY,CAAC,OACjB,GAAG,UAAU,CAAC,OAAO;AACnB,QAAI,GAAG,mBAAmB,eAAgB,IAAG,EAAE;AAAA,EACjD,CAAC;AAIH,QAAM,cAAc,CAAC,YAAiC,GAAG,KAAK,OAAO;AAErE,SAAO,EAAE,gBAAgB,WAAW,QAAQ,QAAQ,WAAW,YAAY;AAC7E;;;AE5OA,IAAM,mBAAmB,oBAAI,IAAI,CAAC,gBAAgB,aAAa,CAAC;AACzD,SAAS,qBAAqB,MAAuB;AAC1D,SAAO,iBAAiB,IAAI,IAAI;AAClC;;;ACiEO,SAAS,sBAAsB,YAA8B;AAClE,MAAI,CAAC,cAAc,OAAO,eAAe,SAAU,QAAO;AAC1D,QAAM,KAAK;AACX,MAAI,MAAsC;AAC1C,QAAM,MAAM,GAAG;AACf,MAAI,OAAO,QAAQ,UAAU;AAC3B,QAAI;AACF,YAAM,IAAI,KAAK,MAAM,GAAG;AACxB,YAAM,KAAK,OAAO,MAAM,WAAY,IAAgC;AAAA,IACtE,QAAQ;AACN,YAAM;AAAA,IACR;AAAA,EACF,WAAW,OAAO,OAAO,QAAQ,UAAU;AACzC,UAAM;AAAA,EACR,OAAO;AACL,UAAM;AAAA,EACR;AACA,MAAI,CAAC,OAAO,IAAI,OAAO,KAAM,QAAO;AACpC,QAAM,OAAO,CAAC,IAAI,SAAS,IAAI,IAAI,EAAE,OAAO,CAAC,MAAM,OAAO,MAAM,QAAQ,EAAE,KAAK,GAAG;AAClF,SAAO,KAAK,SAAS,sBAAsB,KAAK,KAAK,SAAS,uBAAuB;AACvF;AASA,SAAS,WAAW,YAA6B;AAC/C,MAAI,OAAO,eAAe,SAAU,QAAO;AAC3C,MAAI,cAAc,OAAO,eAAe,UAAU;AAChD,UAAM,MAAO,WAAuC;AACpD,QAAI,OAAO,QAAQ,SAAU,QAAO;AAAA,EACtC;AACA,SAAO;AACT;AAeO,SAAS,+BAA+B,YAA8B;AAC3E,QAAM,OAAO,WAAW,UAAU,EAAE,UAAU;AAC9C,SAAO,KAAK,WAAW,uBAAuB,KAAK,KAAK,WAAW,sBAAsB;AAC3F;AAUO,SAAS,wBAAwB,YAAyC;AAC/E,QAAM,OAAO,WAAW,UAAU;AAClC,MAAI,KAAK,SAAS,mBAAmB,GAAG;AACtC,UAAM,IAAI,KAAK,MAAM,YAAY;AACjC,WAAO,IAAI,GAAG,EAAE,CAAC,CAAC,gCAAY;AAAA,EAChC;AACA,SAAO;AACT;AAYO,SAAS,qBAAqB,YAAyC;AAC5E,QAAM,IAAI,WAAW,UAAU,EAAE,MAAM,kBAAkB;AACzD,SAAO,IAAI,EAAE,CAAC,IAAI;AACpB;AAUO,SAAS,iBAAiB,YAA2C;AAC1E,MAAI,CAAC,cAAc,OAAO,eAAe,SAAU,QAAO;AAC1D,QAAM,KAAK;AAEX,MAAI,MAAsC;AAC1C,QAAM,MAAM,GAAG;AACf,MAAI,OAAO,QAAQ,UAAU;AAC3B,QAAI;AACF,YAAM,IAAI,KAAK,MAAM,GAAG;AACxB,YAAM,KAAK,OAAO,MAAM,WAAY,IAAgC;AAAA,IACtE,QAAQ;AACN,YAAM;AAAA,IACR;AAAA,EACF,WAAW,OAAO,OAAO,QAAQ,UAAU;AACzC,UAAM;AAAA,EACR,OAAO;AACL,UAAM;AAAA,EACR;AACA,MAAI,CAAC,IAAK,QAAO;AAEjB,QAAM,UAAU,CAAC,MAAyB;AACxC,QAAI,MAAM,QAAQ,CAAC,EAAG,QAAO,EAAE,IAAI,CAAC,MAAM,OAAO,CAAC,CAAC,EAAE,OAAO,CAAC,MAAM,EAAE,SAAS,CAAC;AAC/E,QAAI,OAAO,MAAM,YAAY,EAAE,SAAS,EAAG,QAAO,CAAC,CAAC;AACpD,QAAI,KAAK,OAAO,MAAM,UAAU;AAC9B,YAAM,IAAK,EAA8B;AACzC,UAAI,OAAO,MAAM,YAAY,EAAE,SAAS,EAAG,QAAO,CAAC,CAAC;AAAA,IACtD;AACA,WAAO,CAAC;AAAA,EACV;AAEA,QAAM,QAAQ,IAAI;AAClB,QAAM,YACJ,SAAS,OAAO,UAAU,WACtB,QAAS,MAAkC,SAAS,EAAE;AAAA,IACpD,QAAS,MAAkC,SAAS;AAAA,EACtD,IACA,CAAC;AACP,QAAM,UAAU,QAAQ,IAAI,SAAS,EAAE,OAAO,QAAQ,IAAI,SAAS,CAAC;AACpE,QAAM,MAAM,CAAC,GAAG,WAAW,GAAG,OAAO;AACrC,SAAO,IAAI,SAAS,IAAI,MAAM;AAChC;AAUO,SAAS,gBAAgB,MAAoB;AAClD,QAAM,WAAW,oBAAI,IAAyB;AAC9C,QAAM,mBAAmB,oBAAI,IAAoB;AAIjD,QAAM,0BAA0B,oBAAI,IAAoB;AAIxD,QAAM,cAAwB,CAAC;AAC/B,MAAI,gBAA+B;AAEnC,QAAM,WAAW,CAAC,kBACf,OAAO,kBAAkB,YAAY,gBAAiB,gBAAgB;AAIzE,WAAS,iBAAiB,MAAuC;AAC/D,UAAM,UAAU,OAAO,QAAQ,IAAI,EAChC,IAAI,CAAC,CAAC,GAAG,CAAC,MAAM;AACf,YAAM,MAAM,OAAO,MAAM,WAAW,KAAK,UAAU,CAAC,IAAI,OAAO,CAAC;AAChE,aAAO,GAAG,CAAC,IAAI,gBAAgB,KAAK,EAAE,CAAC;AAAA,IACzC,CAAC,EACA,KAAK,QAAK;AACb,WAAO,gBAAgB,SAAS,EAAE;AAAA,EACpC;AACA,WAAS,UAAU,GAA2C;AAC5D,QAAI;AAAE,YAAM,IAAI,KAAK,MAAM,CAAC;AAAG,aAAO,KAAK,OAAO,MAAM,WAAW,IAAI;AAAA,IAAM,QAAQ;AAAE,aAAO;AAAA,IAAM;AAAA,EACtG;AAEA,WAAS,OAAO,MAAc,SAA2C;AACvE,YAAQ,MAAM;AAAA,MACZ,KAAK,iBAAiB;AACpB,cAAM,IAAI,QAAQ;AAClB,YAAI,OAAO,MAAM,YAAY,GAAG;AAC9B,sBAAY,KAAK,CAAC;AAClB,0BAAgB;AAChB,eAAK,kBAAmB,QAAQ,YAAuB,cAAc,CAAC;AAAA,QACxE,OAAO;AAEL,sBAAY,SAAS;AACrB,0BAAgB;AAAA,QAClB;AACA,eAAO;AAAA,MACT;AAAA,MACA,KAAK,eAAe;AAClB,cAAM,QAAQ,QAAQ;AACtB,cAAM,YAAa,QAAQ,aAAwB;AACnD,cAAM,WAAY,QAAQ,YAAuB;AACjD,cAAM,SAAU,QAAQ,UAAqB;AAC7C,iBAAS,IAAI,OAAO,EAAE,WAAW,UAAU,QAAQ,iBAAiB,SAAS,QAAQ,eAAe,GAAG,YAAY,GAAG,CAAC;AACvH,YAAI,cAAc,cAAc,UAAU,SAAU,kBAAiB,IAAI,QAAQ,QAAQ;AAEzF,eAAO;AAAA,MACT;AAAA,MACA,KAAK,eAAe;AAClB,cAAM,QAAQ,QAAQ;AACtB,cAAM,OAAO,SAAS,IAAI,KAAK;AAC/B,cAAM,SAAS,MAAM,mBAAmB;AACxC,gBAAQ,QAAQ,WAAW;AAAA,UACzB,KAAK;AAAc,iBAAK,OAAQ,QAAQ,QAAmB,IAAI,MAAM;AAAG,mBAAO;AAAA,UAC/E,KAAK;AAAkB,iBAAK,gBAAiB,QAAQ,QAAmB,IAAI,MAAM;AAAG,mBAAO;AAAA,UAC5F,KAAK;AAAoB,gBAAI,KAAM,MAAK,cAAe,QAAQ,eAA0B;AAAI,mBAAO;AAAA,UACpG,KAAK;AAAgB,gBAAI,KAAM,MAAK,cAAe,QAAQ,eAA0B;AAAI,mBAAO;AAAA,UAChG;AAAS,mBAAO;AAAA,QAClB;AAAA,MACF;AAAA,MACA,KAAK,cAAc;AACjB,cAAM,QAAQ,QAAQ;AACtB,cAAM,OAAO,SAAS,IAAI,KAAK;AAC/B,iBAAS,OAAO,KAAK;AACrB,YAAI,CAAC,KAAM,QAAO;AAClB,YAAI,KAAK,cAAc,YAAY;AAAE,eAAK,eAAe,KAAK,eAAe;AAAG,iBAAO;AAAA,QAAM;AAC7F,YAAI,KAAK,cAAc,cAAc,KAAK,QAAQ;AAChD,gBAAM,OAAO,KAAK,aAAa,UAAU,KAAK,UAAU,KAAK,CAAC,IAAI,CAAC;AACnE,cAAI,KAAK,YAAY,qBAAqB,KAAK,QAAQ,EAAG,QAAO;AACjE,eAAK,cAAc,KAAK,QAAQ,KAAK,YAAY,KAAK,iBAAiB,IAAI,GAAG,KAAK,eAAe;AAClG,iBAAO;AAAA,QACT;AACA,YAAI,KAAK,cAAc,kBAAkB,KAAK,YAAY;AACxD,gBAAM,OAAO,UAAU,KAAK,UAAU,GAAG;AACzC,cAAI,QAAQ,OAAO,SAAS,UAAU;AACpC,kBAAM,IAAI;AAGV,iBAAK,cAAc,GAAG,KAAK,eAAe;AAAA,UAC5C;AACA,iBAAO;AAAA,QACT;AACA,eAAO;AAAA,MACT;AAAA,MACA,KAAK,eAAe;AAClB,cAAM,SAAS,QAAQ;AACvB,YAAI,CAAC,OAAQ,QAAO;AACpB,cAAM,WAAW,iBAAiB,IAAI,MAAM;AAC5C,cAAM,UAAU,QAAQ,YAAY;AAEpC,cAAM,YAAY,UAAU,SAAY,iBAAiB,QAAQ,UAAU;AAC3E,YAAI,YAAY,qBAAqB,QAAQ,GAAG;AAG9C,gBAAM,QAAS,QAAQ,mBAA8B;AAGrD,cAAI,CAAC,WAAW,sBAAsB,QAAQ,UAAU,GAAG;AAGzD,iBAAK,mBAAmB,QAAQ,KAAK;AAAA,UACvC,OAAO;AACL,6BAAiB,OAAO,MAAM;AAC9B,iBAAK,gBAAgB,QAAQ,SAAS,OAAO,SAAS;AAAA,UACxD;AACA,iBAAO;AAAA,QACT;AACA,yBAAiB,OAAO,MAAM;AAI9B,YAAI,+BAA+B,QAAQ,UAAU,GAAG;AAGtD,gBAAM,cAAc,qBAAqB,QAAQ,UAAU;AAC3D,cAAI,YAAa,yBAAwB,IAAI,QAAQ,WAAW;AAChE,iBAAO;AAAA,QACT;AAGA,cAAM,mBACJ,YAAY,wBAAwB,IAAI,MAAM,KAAK,wBAAwB,QAAQ,UAAU;AAC/F,gCAAwB,OAAO,MAAM;AACrC,aAAK;AAAA,UACH;AAAA,UACA;AAAA,UACC,QAAQ,mBAA8B;AAAA,UACtC,QAAQ,uBAAkC;AAAA,UAC3C,SAAS,QAAQ,eAAe;AAAA,UAChC;AAAA,UACA;AAAA,QACF;AACA,eAAO;AAAA,MACT;AAAA,MACA,KAAK,oBAAoB;AACvB,cAAM,WAAW,MAAM,QAAQ,QAAQ,QAAQ,IAC1C,QAAQ,SAAuB,IAAI,CAAC,MAAM,OAAO,CAAC,CAAC,EAAE,OAAO,CAAC,MAAM,EAAE,SAAS,CAAC,IAChF,CAAC;AACL,YAAI,SAAS,SAAS,GAAG;AACvB,eAAK,kBAAkB,UAAW,QAAQ,SAAoB,CAAC;AAAA,QACjE;AACA,eAAO;AAAA,MACT;AAAA,MACA,KAAK,gBAAgB;AACnB,cAAM,IAAI,QAAQ;AAClB,YAAI,OAAO,MAAM,YAAY,GAAG;AAG9B,gBAAM,MAAM,YAAY,YAAY,CAAC;AACrC,cAAI,OAAO,EAAG,aAAY,OAAO,KAAK,CAAC;AACvC,0BAAgB,YAAY,YAAY,SAAS,CAAC,KAAK;AACvD,iBAAO;AAAA,QACT;AACA,oBAAY,SAAS;AACrB,wBAAgB;AAChB,iBAAS,MAAM;AACf,aAAK,WAAY,QAAQ,cAAyB,YAAa,QAAQ,eAA0B,CAAC;AAClG,eAAO;AAAA,MACT;AAAA,MACA;AAAS,eAAO;AAAA,IAClB;AAAA,EACF;AAEA,SAAO,EAAE,OAAO;AAClB;;;AChVA,eAAsB,QACpB,IACA,QACA,OAAoB,CAAC,GACG;AACxB,QAAM,KAAK,KAAK,IAAI;AACpB,QAAM,EAAE,gBAAgB,OAAO,IAAI,MAAM,qBAAqB,IAAI,QAAQ,IAAI;AAE9E,MAAI,UAAU;AACd,MAAI,WAAW;AACf,MAAI,UAAyB;AAC7B,MAAI;AACJ,MAAI;AACJ,QAAM,YAA+B,CAAC;AACtC,QAAM,YAAmC,CAAC;AAI1C,QAAM,sBAAsB,oBAAI,IAAoB;AACpD,QAAM,mBAAmB,oBAAI,IAAoB;AAEjD,QAAM,OAAqB;AAAA;AAAA,IAEzB,OAAO,MAAM,iBAAiB;AAC5B,UAAI,oBAAoB,KAAM,YAAW;AAAA,IAC3C;AAAA,IACA,kBAAkB;AAAA,IAAC;AAAA,IACnB,iBAAiB;AAAA,IAAC;AAAA,IAClB,cAAc,QAAQ,WAAW,aAAa;AAC5C,0BAAoB,IAAI,QAAQ,WAAW;AAAA,IAC7C;AAAA,IACA,aAAa,QAAQ,SAAS,OAAO,sBAAsB,iBAAiB,YAAY,UAAU;AAChG,gBAAU,KAAK;AAAA,QACb,MAAM,YAAY;AAAA,QAClB,aAAa,oBAAoB,IAAI,MAAM,KAAK;AAAA,QAChD;AAAA,QACA,WAAW;AAAA,QACX;AAAA,MACF,CAAC;AACD,0BAAoB,OAAO,MAAM;AAAA,IACnC;AAAA,IACA,gBAAgB;AAAA,IAAC;AAAA,IACjB,oBAAoB;AAAA,IAAC;AAAA,IACrB,kBAAkB,UAAU,iBAAiB;AAC3C,uBAAiB,IAAI,iBAAiB,QAAQ;AAAA,IAChD;AAAA,IACA,gBAAgB,QAAQ,SAAS,OAAO;AACtC,gBAAU,KAAK;AAAA,QACb,MAAM;AAAA,QACN,aAAa,iBAAiB,IAAI,MAAM,KAAK;AAAA,QAC7C;AAAA,QACA,WAAW;AAAA,QACX,cAAc;AAAA,QACd,iBAAiB;AAAA,MACnB,CAAC;AACD,uBAAiB,OAAO,MAAM;AAAA,IAChC;AAAA,IACA,qBAAqB;AAAA,IAAC;AAAA,IACtB,WAAW,QAAQ,IAAI;AACrB,mBAAa;AACb,eAAS;AAAA,IACX;AAAA,EACF;AAEA,QAAM,UAAU,gBAAgB,IAAI;AAEpC,mBAAiB,MAAM,QAAQ;AAC7B,QAAI,KAAK,cAAe,WAAU,KAAK,EAAE;AACzC,QAAI,CAAC,SAAU,YAAW,GAAG;AAC7B,QAAI,CAAC,QAAS,WAAU,GAAG;AAG3B,QAAI,GAAG,SAAS,mBAAmB;AACjC,YAAM,IAAI,SAAS;AAAA,QACjB;AAAA,QACA,SAAU,GAAG,QAAkC;AAAA,QAC/C,SAAS,WAAW;AAAA,MACtB,CAAC;AAAA,IACH;AAEA,YAAQ,OAAO,GAAG,MAAM,GAAG,OAAkC;AAAA,EAC/D;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,WAAW,KAAK,IAAI,IAAI;AAAA,IACxB,QAAQ;AAAA,IACR;AAAA,IACA;AAAA,EACF;AACF;;;ACjJA,YAAYC,SAAQ;AAWpB,eAAsB,sBAAsB,SAAoD;AAC9F,MAAI;AACF,UAAM,MAAM,MAAS,aAAS,sBAAsB,OAAO,GAAG,OAAO;AACrE,WAAO,KAAK,MAAM,GAAG;AAAA,EACvB,SAAS,GAAG;AACV,QAAK,EAA4B,SAAS,SAAU,QAAO;AAC3D,UAAM;AAAA,EACR;AACF;AAEA,eAAsB,sBACpB,SACA,OACe;AACf,QAAS,UAAM,WAAW,OAAO,GAAG,EAAE,WAAW,KAAK,CAAC;AACvD,QAAS;AAAA,IACP,sBAAsB,OAAO;AAAA,IAC7B,KAAK,UAAU,EAAE,GAAG,OAAO,YAAW,oBAAI,KAAK,GAAE,YAAY,EAAE,GAAG,MAAM,CAAC;AAAA,EAC3E;AACF;;;AC3BO,IAAM,mBAAmB;AAYhC,eAAsB,mBACpB,MACA,UAAkB,kBACY;AAC9B,QAAM,OAAO,MAAM,KAAK,KAAK,iCAAiC,EAAE,QAAQ,CAAC;AACzE,SAAO;AAAA,IACL,IAAI,KAAK,KAAK;AAAA,IACd,SAAS,KAAK,KAAK;AAAA,IACnB,OAAO,KAAK,KAAK;AAAA,EACnB;AACF;;;ACYA,SAAS,uBAAuB,MAA0C;AACxE,MAAI,MAAM,QAAQ,IAAI,EAAG,QAAO;AAChC,MACE,QACG,OAAO,SAAS,YAChB,MAAM,QAAS,KAA4B,IAAI,GAClD;AACA,WAAQ,KAAsC;AAAA,EAChD;AACA,SAAO;AACT;AAMA,eAAsB,0BACpB,MACA,gBACoC;AACpC,MAAI;AACF,UAAM,OAAO,MAAM,KAAK;AAAA,MACtB,iCAAiC,cAAc;AAAA,IAAkB;AACnE,WAAO,uBAAuB,KAAK,IAAI;AAAA,EACzC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAMA,eAAsB,gCACpB,MACA,gBAC6B;AAC7B,QAAM,OAAO,MAAM,KAAK;AAAA,IACtB,iCAAiC,cAAc;AAAA,EAAkB;AACnE,QAAM,QAAQ,uBAAuB,KAAK,IAAI;AAC9C,MAAI,CAAC,OAAO;AACV,UAAM,IAAI,MAAM,mDAAmD,cAAc,EAAE;AAAA,EACrF;AACA,SAAO;AACT;AAOO,SAAS,mBACd,gBACA,OACA,YAAoB,GACpB,WAAmB,IACG;AACtB,MAAI,CAAC,MAAM,QAAQ,KAAK,KAAK,MAAM,WAAW,EAAG,QAAO;AAGxD,QAAM,SAAkB,CAAC;AACzB,aAAW,MAAM,OAAO;AACtB,QAAI,GAAG,SAAS,cAAc,GAAG,QAAQ,GAAG,KAAK,KAAK,GAAG;AACvD,YAAM,QAAQ,GAAG,eAAe,CAAC,GAAG,IAAI,CAAC,MAAM,GAAG,QAAQ,EAAE,OAAO,OAAO;AAC1E,YAAM,SAAS,KAAK,SAAS,cAAO,KAAK,KAAK,QAAK,CAAC,KAAK;AACzD,aAAO,KAAK,EAAE,MAAM,GAAG,OAAO,QAAQ,WAAW,KAAK,CAAC;AAAA,IACzD,WAAW,GAAG,SAAS,eAAe,GAAG,WAAW,GAAG,QAAQ,KAAK,KAAK,OAAO,SAAS,GAAG;AAC1F,YAAM,OAAO,OAAO,OAAO,SAAS,CAAC;AACrC,UAAI,KAAM,MAAK,YAAY,GAAG;AAAA,IAChC;AAAA,EACF;AACA,MAAI,OAAO,WAAW,EAAG,QAAO;AAEhC,QAAM,UAAU,CAAC,MACf,gBAAgB,EAAE,QAAQ,aAAa,GAAG,EAAE,KAAK,GAAG,QAAQ;AAE9D,QAAM,SAAuB,CAAC;AAC9B,aAAW,KAAK,OAAO,MAAM,CAAC,SAAS,GAAG;AACxC,WAAO,KAAK,EAAE,MAAM,QAAQ,MAAM,QAAQ,EAAE,IAAI,EAAE,CAAC;AACnD,QAAI,EAAE,UAAW,QAAO,KAAK,EAAE,MAAM,aAAa,MAAM,QAAQ,EAAE,SAAS,EAAE,CAAC;AAAA,EAChF;AACA,SAAO,EAAE,gBAAgB,aAAa,OAAO,QAAQ,OAAO;AAC9D;AAEO,SAAS,qBAAqB,OAA6D;AAChG,QAAM,OAAO,CAAC,GAAI,SAAS,CAAC,CAAE,EAAE,QAAQ,EACrC,KAAK,CAAC,OAAO,GAAG,SAAS,eAAe,OAAO,UAAU,GAAG,GAAG,CAAC;AACnE,SAAO,MAAM,OAAO;AACtB;;;ACxGA,eAAsB,yBACpB,MACA,QAAgB,IACiB;AACjC,QAAM,SAAS,MAAM,MAA4B,MAAM,qBAAqB;AAAA,IAC1E,QAAQ,EAAE,QAAQ,aAAa,UAAU,MAAM;AAAA,IAC/C,QAAQ;AAAA,MACN;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,MAAM,CAAC,EAAE,OAAO,gBAAgB,WAAW,OAAO,CAAC;AAAA,IACnD;AAAA,EACF,CAAC;AACD,SAAO,EAAE,MAAM,OAAO,MAAM,OAAO,OAAO,MAAM;AAClD;;;AC3CO,IAAM,oBAAoB;AAC1B,IAAM,iBAAiB;AACvB,IAAM,0BAA0B;AAChC,IAAM,mBAAmB;AACzB,IAAM,oBAAoB;AAC1B,IAAM,uBAAuB;;;ACFpC,IAAM,QAAQ;AAEd,eAAsB,mBACpB,MACA,cACA,UAAiC,CAAC,GACA;AAClC,QAAM,OAAO,MAAM,KAAK,IAAI,eAAe,KAAK,IAAI;AAAA,IAClD,QAAQ;AAAA,MACN,OAAO;AAAA,MACP,QAAQ,mBAAmB,YAAY;AAAA,MACvC,MAAM;AAAA,MACN,MAAM,QAAQ,YAAY;AAAA,MAC1B,OAAO;AAAA,IACT;AAAA,EACF,CAAC;AACD,QAAM,UAAU,MAAM,QAAQ,KAAK,MAAM,OAAO,IAAI,KAAK,KAAK,UAAU,CAAC;AACzE,SAAO;AAAA,IACL,MAAM,QAAQ,IAAI,mBAAmB;AAAA,IACrC,OAAO,SAAS,KAAK,MAAM,eAAe,QAAQ,MAAM;AAAA,EAC1D;AACF;AAEA,eAAsB,yBACpB,MACA,QAC+B;AAC/B,QAAM,OAAO,MAAM,KAAK,IAAI,eAAe,KAAK,IAAI,mBAAmB,MAAM,CAAC,sBAAsB;AACpG,SAAO,uBAAuB,iBAAiB,KAAK,IAAI,CAAC;AAC3D;AAEA,eAAsB,yBACpB,MACA,QACA,SAC4B;AAC5B,QAAM,OAAO,MAAM,KAAK;AAAA,IACtB,eAAe,KAAK,IAAI,mBAAmB,MAAM,CAAC;AAAA,IAClD,EAAE,MAAM,EAAE,QAAQ,EAAE;AAAA,EACtB;AACA,SAAO,oBAAoB,iBAAiB,KAAK,IAAI,CAAC;AACxD;AAEA,eAAsB,oBACpB,MACA,cACA,cACA,SAC4B;AAC5B,QAAM,OAAO,MAAM,KAAK,KAAK,eAAe,KAAK,IAAI;AAAA,IACnD;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AACD,SAAO,oBAAoB,KAAK,IAAI;AACtC;AAEA,eAAsB,oBAAoB,MAAqB,QAA+B;AAC5F,QAAM,KAAK,OAAO,eAAe,KAAK,IAAI,mBAAmB,MAAM,CAAC,EAAE;AACxE;AAEA,SAAS,iBAAiB,OAAyB;AACjD,MAAI,SAAS,OAAO,UAAU,YAAY,UAAU,OAAO;AACzD,WAAQ,MAA6B;AAAA,EACvC;AACA,SAAO;AACT;AAEA,SAAS,oBAAoB,OAAmC;AAC9D,QAAM,MAAM,SAAS,KAAK;AAC1B,SAAO;AAAA,IACL,IAAI,SAAS,IAAI,EAAE;AAAA,IACnB,cAAc,SAAS,IAAI,YAAY;AAAA,IACvC,cAAc,SAAS,IAAI,YAAY;AAAA,IACvC,UAAU,kBAAkB,IAAI,QAAQ;AAAA,IACxC,UAAU,SAAS,IAAI,UAAU,CAAC;AAAA,IAClC,cAAc,gBAAgB,IAAI,YAAY;AAAA,EAChD;AACF;AAEA,SAAS,uBAAuB,OAAsC;AACpE,QAAM,MAAM,SAAS,KAAK;AAC1B,SAAO;AAAA,IACL,cAAc,SAAS,IAAI,YAAY;AAAA,IACvC,cAAc,SAAS,IAAI,YAAY;AAAA,IACvC,SAAS,SAAS,IAAI,OAAO;AAAA,IAC7B,UAAU,kBAAkB,IAAI,QAAQ;AAAA,EAC1C;AACF;AAEA,SAAS,SAAS,OAAyC;AACzD,SAAO,SAAS,OAAO,UAAU,WAAY,QAAoC,CAAC;AACpF;AAEA,SAAS,SAAS,OAAwB;AACxC,SAAO,OAAO,UAAU,WAAW,QAAQ,SAAS,OAAO,KAAK,OAAO,KAAK;AAC9E;AAEA,SAAS,kBAAkB,OAAoC;AAC7D,SAAO,SAAS,OAAO,SAAY,SAAS,KAAK;AACnD;AAEA,SAAS,SAAS,OAAgB,UAA0B;AAC1D,SAAO,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK,IAAI,QAAQ;AACvE;AAEA,SAAS,gBAAgB,OAAwC;AAC/D,MAAI,OAAO,UAAU,YAAY,OAAO,UAAU,SAAU,QAAO;AACnE,SAAO;AACT;;;AChHA,YAAY,YAAY;AAExB,YAAYC,SAAQ;AACpB,YAAYC,WAAU;;;AC0BtB,IAAM,2BAA2B;AACjC,IAAM,yBAAyB;AAI/B,IAAM,0BAA0B,CAAC,QAAQ,QAAQ,YAAY,UAAU,aAAa,YAAY;AAChG,IAAM,4BAAqD;AAAA,EACzD,UAAU;AAAA,EACV,QAAQ;AAAA,EACR,YAAY;AACd;AACA,IAAM,gCAAgC,CAAC,WAAW;AAClD,IAAM,qBAAqB,oBAAI,IAAI;AAAA,EACjC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AACD,IAAM,4BAA4B;AAAA,EAChC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AACA,IAAM,2BAA2B;AACjC,IAAM,yBAAiD;AAAA,EACrD,YAAY;AAAA,EACZ,WAAW;AAAA,EACX,WAAW;AAAA,EACX,gBAAgB;AAAA,EAChB,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,aAAa;AACf;AAEO,SAAS,0BACd,eAAiC,aACjC,iBAA6C,CAAC,GACpC;AACV,MAAI,iBAAiB,sBAAsB;AACzC,UAAM,QAAQ,uBAAuB,eAAe,KAAK;AACzD,WAAO,OAAO;AAAA,MACZ,GAAG,KAAK;AAAA,MACR,GAAG,KAAK;AAAA,MACR,GAAG,KAAK;AAAA,MACR,GAAG,KAAK;AAAA,MACR,GAAG,KAAK;AAAA,MACR,GAAG,iCAAiC,eAAe,MAAM;AAAA,IAC3D,CAAC;AAAA,EACH;AACA,MAAI,iBAAiB,cAAc;AACjC,UAAM,QAAQ,mBAAmB,eAAe,KAAK;AACrD,WAAO,CAAC,GAAG,KAAK,SAAS,GAAG,KAAK,MAAM;AAAA,EACzC;AACA,SAAO,CAAC,eAAe;AACzB;AAEO,SAAS,0BAA0B,MAAuE;AAC/G,QAAM,eAAe,KAAK,gBAAgB;AAC1C,QAAM,cAAc,KAAK,MAAM,KAAK,KAAK,KAAK;AAC9C,QAAM,iBAAiB,4BAA4B,IAAI;AACvD,QAAM,aAAa,wBAAwB,IAAI;AAC/C,QAAMC,YAAW;AAAA,IACf,eAAe;AAAA,IACf,MAAM,KAAK;AAAA,IACX,MAAM;AAAA,IACN,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,UAAU,CAAC,YAAY,YAAY;AAAA,IACnC,gBAAgB,KAAK;AAAA,IACrB,OAAO,EAAE,MAAM,cAAc,KAAK,aAAa,IAAI,WAAW;AAAA,IAC9D,iBAAiB;AAAA,MACf,UAAU,KAAK;AAAA,MACf,aAAa;AAAA,MACb,UAAU,EAAE,aAAa,aAAa,KAAK,OAAO,IAAI,UAAU,GAAG;AAAA,IACrE;AAAA,IACA,gBAAgB,KAAK;AAAA,EACvB;AAEA,SAAO;AAAA,IACL,EAAE,WAAW,iBAAiB,SAAS,KAAK,UAAUA,WAAU,MAAM,CAAC,IAAI,KAAK;AAAA,IAChF;AAAA,MACE,WAAW;AAAA,MACX,SAAS,aAAa,cAAc,KAAK,SAAS,aAAa,cAAc;AAAA,IAC/E;AAAA,IACA;AAAA,MACE,WAAW;AAAA,MACX,SAAS,YAAY,YAAY;AAAA,IACnC;AAAA,IACA;AAAA,MACE,WAAW;AAAA,MACX,SAAS,WAAW,cAAc,KAAK,SAAS,aAAa,gBAAgB,UAAU;AAAA,IACzF;AAAA,EACF;AACF;AAEA,SAAS,aACP,cACA,SACA,aACA,gBACQ;AACR,MAAI,iBAAiB,qBAAsB,QAAO,aAAa,SAAS,aAAa,cAAc;AACnG,MAAI,iBAAiB,aAAc,QAAO,cAAc,SAAS,WAAW;AAC5E,SAAO,cAAc,SAAS,WAAW;AAC3C;AAEA,SAAS,YAAY,cAAwC;AAC3D,MAAI,iBAAiB,qBAAsB,QAAO,YAAY;AAC9D,MAAI,iBAAiB,aAAc,QAAO,aAAa;AACvD,SAAO,aAAa;AACtB;AAEA,SAAS,WACP,cACA,SACA,aACA,gBACA,YACQ;AACR,MAAI,iBAAiB,qBAAsB,QAAO,WAAW,cAAc;AAC3E,MAAI,iBAAiB,aAAc,QAAO,YAAY,SAAS,aAAa,UAAU;AACtF,SAAO,YAAY;AACrB;AAEA,SAAS,wBAAwB,MAAgD;AAC/E,QAAM,QAAQ,mBAAmB,KAAK,aAAa;AACnD,SAAO;AAAA,IACL;AAAA,IACA,YAAY,kBAAkB,KAAK;AAAA,EACrC;AACF;AAEA,SAAS,4BAA4B,MAAuF;AAC1H,QAAM,QAAQ,uBAAuB,KAAK,aAAa;AACvD,QAAM,SACJ,KAAK,mBAAmB,SACpB,UAAU,yBACR,CAAC,GAAG,uBAAuB,IAC3B,OACF,KAAK;AACX,QAAM,WACJ,KAAK,qBAAqB,SACtB,KAAK,mBACL,UAAU,yBACR,EAAE,GAAG,0BAA0B,IAC/B,CAAC;AACT,QAAM,qBACJ,KAAK,+BAA+B,SAChC,KAAK,6BACL,UAAU,yBACR,CAAC,GAAG,6BAA6B,IACjC,CAAC;AACT,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,YAAY,kBAAkB,KAAK;AAAA,EACrC;AACF;AAEO,SAAS,0CACd,aACuF;AACvF,QAAM,iBAAiB,oBAAoB,YAAY,MAAM;AAC7D,QAAM,mBAAmB,IAAI,IAAI,cAAc;AAC/C,SAAO;AAAA,IACL,QAAQ;AAAA,IACR,UAAU,sBAAsB,YAAY,QAAQ,gBAAgB;AAAA,IACpE,oBAAoB,wBAAwB,YAAY,QAAQ,gBAAgB;AAAA,EAClF;AACF;AAEA,SAAS,oBAAoB,QAA+B;AAC1D,QAAM,WAAW,OAAO,IAAI,CAAC,UAAU,MAAM,IAAI,EAAE,OAAO,CAAC,SAAS,QAAQ,CAAC,mBAAmB,IAAI,IAAI,CAAC;AACzG,QAAM,cAAc,IAAI,IAAI,QAAQ;AACpC,QAAM,WAAW,OACd,OAAO,CAAC,UAAU,MAAM,aAAa,QAAQ,YAAY,IAAI,MAAM,IAAI,CAAC,EACxE,IAAI,CAAC,UAAU,MAAM,IAAI;AAC5B,QAAM,YAAY,0BAA0B,OAAO,CAAC,SAAS,YAAY,IAAI,IAAI,CAAC;AAClF,QAAM,WAAW,OAAO,CAAC,GAAG,UAAU,GAAG,SAAS,CAAC;AACnD,UAAQ,SAAS,SAAS,WAAW,UAAU,MAAM,GAAG,wBAAwB;AAClF;AAEA,SAAS,sBAAsB,QAAqB,kBAAwD;AAC1G,QAAM,WAAoC,CAAC;AAC3C,MAAI,iBAAiB,QAAQ,kBAAkB,UAAU,EAAG,UAAS,WAAW;AAChF,MAAI,iBAAiB,QAAQ,kBAAkB,QAAQ,EAAG,UAAS,SAAS;AAC5E,MAAI,iBAAiB,QAAQ,kBAAkB,YAAY,EAAG,UAAS,aAAa;AACpF,MAAI,iBAAiB,QAAQ,kBAAkB,SAAS,EAAG,UAAS,UAAU;AAC9E,SAAO;AACT;AAEA,SAAS,wBAAwB,QAAqB,kBAAyC;AAC7F,SAAO,CAAC,aAAa,iBAAiB,WAAW,EAAE,OAAO,CAAC,SAAS,iBAAiB,QAAQ,kBAAkB,IAAI,CAAC;AACtH;AAEA,SAAS,iBAAiB,QAAqB,kBAA+B,MAAuB;AACnG,SAAO,iBAAiB,IAAI,IAAI,KAAK,OAAO,KAAK,CAAC,UAAU,MAAM,SAAS,IAAI;AACjF;AAEA,SAAS,OAAO,QAA4B;AAC1C,SAAO,MAAM,KAAK,IAAI,IAAI,MAAM,CAAC;AACnC;AAEA,SAAS,iCAAiC,QAAoC;AAC5E,MAAI,CAAC,QAAQ,OAAQ,QAAO,CAAC;AAC7B,SAAO;AAAA,IACL,OAAO,QAAQ,CAAC,UAAU;AACxB,YAAM,QAAQ,uBAAuB,KAAK;AAC1C,aAAO,QAAQ,CAAC,GAAG,KAAK,SAAS,GAAG,KAAK,MAAM,IAAI,CAAC;AAAA,IACtD,CAAC;AAAA,EACH;AACF;AAEA,SAAS,uBAAuB,OAAwB;AACtD,SAAO,OAAO,KAAK,KAAK;AAC1B;AAEA,SAAS,mBAAmB,OAAwB;AAClD,SAAO,OAAO,KAAK,KAAK;AAC1B;AAEA,SAAS,kBAAkB,OAAuB;AAChD,QAAM,SAAiC;AAAA,IACrC,UAAU;AAAA,IACV,SAAS;AAAA,IACT,UAAU;AAAA,EACZ;AACA,SAAO,OAAO,KAAK,KAAK,GAAG,KAAK;AAClC;AAEA,SAAS,WAAW,OAAuB;AACzC,SAAO,MACJ,WAAW,KAAK,OAAO,EACvB,WAAW,KAAK,MAAM,EACtB,WAAW,KAAK,MAAM,EACtB,WAAW,KAAK,QAAQ,EACxB,WAAW,KAAK,OAAO;AAC5B;AAEA,SAAS,cAAc,SAAiB,aAA6B;AACnE,SAAO,gDAAgD,OAAO;AAAA;AAAA;AAAA,UAGtD,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAOrB;AAEA,SAAS,eAAuB;AAC9B,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAwBT;AAEA,SAAS,cAAsB;AAC7B,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAWT;AAEA,SAAS,cAAc,SAAiB,aAA6B;AACnE,SAAO,mDAAmD,OAAO;AAAA;AAAA;AAAA;AAAA,YAIvD,WAAW,WAAW,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAUnC;AAEA,SAAS,eAAuB;AAC9B,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAqJT;AAEA,SAAS,YAAY,SAAiB,aAAqB,QAAwC;AACjG,SAAO,oBAAoB,KAAK,UAAU,OAAO,CAAC;AAAA,mBACjC,KAAK,UAAU,WAAW,CAAC;AAAA,qBACzB,KAAK,UAAU,OAAO,KAAK,CAAC;AAAA,sBAC3B,KAAK,UAAU,OAAO,UAAU,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA4NvD;AAEA,SAAS,aAAa,SAAiB,aAAqB,QAAgE;AAC1H,QAAM,aAAa,WAAW,OAAO,UAAU;AAC/C,SAAO,kDAAkD,OAAO;AAAA;AAAA;AAAA;AAAA,YAItD,WAAW,WAAW,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,kBAQjB,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wDAWkB,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,0CAOlC,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAYhC;AAEA,SAAS,cAAsB;AAC7B,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAiMT;AAEA,SAAS,WAAW,QAAgE;AAClF,QAAM,QAAQ,KAAK,UAAU,OAAO,KAAK;AACzC,QAAM,aAAa,KAAK,UAAU,OAAO,UAAU;AACnD,QAAM,SAAS,OAAO,QAAQ,SAAS,KAAK,UAAU,OAAO,MAAM,IAAI;AACvE,QAAM,WAAW,KAAK,UAAU,OAAO,UAAU,MAAM,CAAC;AACxD,QAAM,gBAAgB,KAAK,UAAU,OAAO,kBAAkB;AAC9D,SAAO,iBAAiB,KAAK;AAAA,sBACT,UAAU;AAAA;AAAA,sBAEV,MAAM;AAAA,0BACF,QAAQ;AAAA,+BACH,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA0Z5C;;;ADp0CA,IAAM,wBAAwB;AAC9B,IAAM,gBAAgB;AACtB,IAAM,iBACJ;AACF,IAAM,0BAA0B,oBAAI,IAAI;AAAA,EACtC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AACD,IAAM,aAAa,oBAAI,IAAI,CAAC,QAAQ,OAAO,IAAI,CAAC;AAChD,IAAM,gBAAgB,oBAAI,IAAI,CAAC,QAAQ,gBAAgB,gBAAgB,MAAM,CAAC;AAC9E,IAAM,yBAAiD;AAAA,EACrD,OAAO;AAAA,EACP,KAAK;AAAA,EACL,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AACV;AACA,IAAM,2BAA2B;AACjC,IAAM,8BACJ;AACF,IAAM,+BACJ;AAqCF,eAAsB,mBAAmB,MAA6C;AACpF,QAAM,UAAU,iBAAiB,KAAK,OAAO;AAC7C,QAAM,eAAe,sBAAsB,KAAK,YAAY;AAC5D,QAAM,YAAiB,cAAQ,KAAK,SAAS;AAC7C,QAAM,eAAe,iBAAiB,OAAO;AAC7C,QAAM,eAAe,KAAK,gBAAgB;AAC1C,QAAM,QAAQC,2BAA0B;AAAA,IACtC;AAAA,IACA,MAAM,KAAK;AAAA,IACX,gBAAgB,KAAK,kBAAkB;AAAA,IACvC;AAAA,IACA,eAAe,KAAK;AAAA,IACpB,gBAAgB,KAAK;AAAA,IACrB,kBAAkB,KAAK;AAAA,IACvB,4BAA4B,KAAK;AAAA,IACjC,gBAAgB,KAAK,gBAAgB,SACjC,KAAK,iBACL,0BAA0B,cAAc,EAAE,OAAO,KAAK,eAAe,QAAQ,KAAK,eAAe,CAAC;AAAA,IACtG,UAAU,KAAK,YAAY;AAAA,EAC7B,CAAC;AAED,QAAM;AAAA,IACJ;AAAA,IACA,MAAM,IAAI,CAAC,SAAS,KAAK,SAAS,EAAE,OAAO,CAAC,iBAAiB,CAAC;AAAA,IAC9D,CAAC,CAAC,KAAK;AAAA,EACT;AACA,QAAS,UAAM,WAAW,EAAE,WAAW,KAAK,CAAC;AAC7C,aAAW,QAAQ,OAAO;AACxB,UAAM,UAAe,WAAK,WAAW,KAAK,SAAS,GAAG,KAAK,OAAO;AAAA,EACpE;AAEA,QAAM,UAA8B;AAAA,IAClC,MAAM;AAAA,IACN,SAAS;AAAA,IACT;AAAA,IACA;AAAA,IACA;AAAA,IACA,UAAU,KAAK;AAAA,IACf,UAAU,KAAK;AAAA,IACf,SAAS,KAAK;AAAA,IACd,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,EACpC;AACA,QAAM,UAAe,WAAK,WAAW,iBAAiB,GAAG,OAAO;AAEhE,QAAM,aAAa,MAAM,mBAAmB,SAAS;AACrD,QAAM,eAAe,WAAW,SAAS,CAAC,GAAG,oBAAI,IAAI,CAAC;AACtD,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,OAAO,WAAW,IAAI,CAAC,SAAS,WAAW,SAAS,IAAI,CAAC;AAAA,EAC3D;AACF;AAEA,eAAsB,mBAAmB,MAA6C;AACpF,QAAM,UAAU,iBAAiB,KAAK,OAAO;AAC7C,QAAM,eAAe,sBAAsB,KAAK,YAAY;AAC5D,QAAM,YAAiB,cAAQ,KAAK,SAAS;AAC7C,QAAM,eAAe,iBAAiB,OAAO;AAC7C,QAAM,cAAc,MAAM,uBAAuB,KAAK,MAAM,cAAc,OAAO;AACjF,MAAI,CAAC,YAAY,QAAQ;AACvB,UAAM,IAAI,SAAS;AAAA,MACjB;AAAA,MACA,SAAS,aAAa,OAAO,6BAA6B,YAAY;AAAA,IACxE,CAAC;AAAA,EACH;AAEA,QAAM,YAAY,YACf,OAAO,CAAC,SAAS,CAAC,sBAAsB,KAAK,KAAK,YAAY,CAAC,EAC/D,IAAI,CAAC,UAAU;AAAA,IACd,WAAW,oBAAoB,cAAc,KAAK,KAAK,YAAY;AAAA,IACnE,SAAS,KAAK,QAAQ;AAAA,IACtB,QAAQ,KAAK;AAAA,EACf,EAAE;AACJ,QAAM;AAAA,IACJ;AAAA,IACA,UAAU,IAAI,CAAC,SAAS,KAAK,SAAS,EAAE,OAAO,CAAC,iBAAiB,CAAC;AAAA,IAClE,CAAC,CAAC,KAAK;AAAA,EACT;AACA,QAAS,UAAM,WAAW,EAAE,WAAW,KAAK,CAAC;AAC7C,aAAW,QAAQ,WAAW;AAC5B,UAAM,UAAe,WAAK,WAAW,KAAK,SAAS,GAAG,KAAK,OAAO;AAAA,EACpE;AAEA,QAAM,UAA8B;AAAA,IAClC,MAAM;AAAA,IACN,SAAS;AAAA,IACT;AAAA,IACA;AAAA,IACA;AAAA,IACA,UAAU,KAAK;AAAA,IACf,UAAU,KAAK;AAAA,IACf,SAAS,KAAK;AAAA,IACd,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,EACpC;AACA,QAAM,UAAe,WAAK,WAAW,iBAAiB,GAAG,OAAO;AAEhE,QAAM,aAAa,MAAM,mBAAmB,SAAS;AACrD,QAAM,oBAAoB,IAAI;AAAA,IAC5B,UAAU,IAAI,CAAC,SAAS,CAAC,KAAK,WAAW,KAAK,MAAM,CAAU;AAAA,EAChE;AACA,QAAM,eAAe,WAAW,SAAS,YAAY,iBAAiB;AACtE,QAAMC,YAAW,MAAM,iBAAiB,SAAS;AACjD,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,OAAOA,UAAS;AAAA,EAClB;AACF;AAEA,eAAsB,cAAc,SAA6C;AAC/E,QAAM,OAAY,cAAQ,OAAO;AACjC,QAAM,UAAU,MAAM,gBAAgB,IAAI;AAC1C,QAAMA,YAAW,MAAM,iBAAiB,IAAI;AAC5C,QAAM,aAAa,MAAM,mBAAmB,IAAI;AAChD,SAAO,qBAAqB,SAASA,WAAU,UAAU;AAC3D;AAEA,eAAsB,oBAAoB,SAAgD;AACxF,QAAM,OAAY,cAAQ,OAAO;AACjC,QAAM,SAAmB,CAAC;AAC1B,QAAM,WAAqB,CAAC;AAC5B,MAAI,UAAqC;AAEzC,MAAI;AACF,cAAU,MAAM,gBAAgB,IAAI;AAAA,EACtC,SAAS,GAAG;AACV,WAAO,KAAM,EAAY,OAAO;AAAA,EAClC;AAEA,QAAM,eAAoB,WAAK,MAAM,eAAe;AACpD,MAAIA,YAA2C;AAC/C,MAAI;AACF,IAAAA,YAAW,KAAK,MAAM,MAAS,aAAS,cAAc,OAAO,CAAC;AAAA,EAChE,SAAS,GAAG;AACV,QAAK,EAA4B,SAAS,UAAU;AAClD,aAAO,KAAK,2BAA2B;AAAA,IACzC,OAAO;AACL,aAAO,KAAK,oCAAqC,EAAY,OAAO,EAAE;AAAA,IACxE;AAAA,EACF;AAEA,MAAI,SAAS;AACX,QAAI,CAAC,sBAAsB,KAAK,QAAQ,OAAO,GAAG;AAChD,aAAO,KAAK,sCAAsC;AAAA,IACpD;AACA,QAAI,QAAQ,iBAAiB,iBAAiB,QAAQ,OAAO,GAAG;AAC9D,aAAO,KAAK,gCAAgC,iBAAiB,QAAQ,OAAO,CAAC,EAAE;AAAA,IACjF;AAAA,EACF;AAEA,MAAIA,WAAU;AACZ,UAAM,uBAAuBA,WAAU,SAAS,MAAM,QAAQ,QAAQ;AAAA,EACxE;AAEA,SAAO;AAAA,IACL,OAAO,OAAO,WAAW;AAAA,IACzB,SAAS,SAAS;AAAA,IAClB,cAAc,SAAS;AAAA,IACvB;AAAA,IACA;AAAA,EACF;AACF;AAEA,eAAsB,mBAAmB,MAA6C;AACpF,QAAM,OAAY,cAAQ,KAAK,OAAO;AACtC,QAAM,aAAa,MAAM,oBAAoB,IAAI;AACjD,MAAI,CAAC,WAAW,OAAO;AACrB,UAAM,IAAI,SAAS;AAAA,MACjB;AAAA,MACA,SAAS,mCAAmC,WAAW,OAAO,KAAK,IAAI,CAAC;AAAA,IAC1E,CAAC;AAAA,EACH;AAEA,QAAM,UAAU,MAAM,gBAAgB,IAAI;AAC1C,QAAMA,YAAW,MAAM,iBAAiB,IAAI;AAC5C,QAAM,aAAa,MAAM,mBAAmB,IAAI;AAChD,QAAM,cAAc,MAAM,uBAAuB,KAAK,MAAM,QAAQ,cAAc,QAAQ,OAAO;AACjG,QAAM,OAAO,cAAc,SAASA,WAAU,YAAY,WAAW;AAErE,QAAM,SAAyB;AAAA,IAC7B,SAAS,QAAQ;AAAA,IACjB,cAAc,QAAQ;AAAA,IACtB,QAAQ,CAAC,CAAC,KAAK;AAAA,IACf,YAAY,KAAK;AAAA,IACjB,WAAW,KAAK;AAAA,EAClB;AACA,MAAI,KAAK,UAAU,UAAU,KAAK,QAAQ;AACxC,WAAO;AAAA,EACT;AAEA,QAAM,cAAc,IAAI,IAAI,WAAW,IAAI,CAAC,SAAS,CAAC,KAAK,WAAW,IAAI,CAAU,CAAC;AACrF,aAAW,aAAa,KAAK,YAAY;AACvC,QAAI,UAAU,cAAc,OAAQ;AACpC,QAAI,UAAU,cAAc,UAAU;AACpC,UAAI,UAAU,OAAQ,OAAM,oBAAoB,KAAK,MAAM,UAAU,MAAM;AAC3E;AAAA,IACF;AACA,UAAM,YAAY,YAAY,IAAI,UAAU,SAAS;AACrD,QAAI,CAAC,UAAW;AAChB,QAAI,UAAU,cAAc,UAAU;AACpC,YAAM;AAAA,QACJ,KAAK;AAAA,QACL,QAAQ;AAAA,QACR,UAAU;AAAA,QACV,UAAU;AAAA,MACZ;AAAA,IACF;AACA,QAAI,UAAU,cAAc,YAAY,UAAU,QAAQ;AACxD,YAAM,yBAAyB,KAAK,MAAM,UAAU,QAAQ,UAAU,OAAO;AAAA,IAC/E;AAAA,EACF;AAEA,QAAM,oBAAoB,MAAM,mBAAmB,KAAK,MAAM,QAAQ,cAAc;AAAA,IAClF,UAAU;AAAA,EACZ,CAAC;AACD,QAAM,oBAAoB,oBAAI,IAA+B;AAC7D,aAAW,UAAU,kBAAkB,MAAM;AAC3C,QAAI,CAAC,OAAO,aAAa,WAAW,QAAQ,eAAe,GAAG,EAAG;AACjE,sBAAkB,IAAI,oBAAoB,QAAQ,cAAc,OAAO,YAAY,GAAG,MAAM;AAAA,EAC9F;AACA,QAAM,eAAe,MAAM,SAAS,YAAY,iBAAiB;AACjE,SAAO;AACT;AAEA,eAAsB,wBACpB,SACA,iBAC8B;AAC9B,QAAM,UAAU,MAAM,gBAAqB,cAAQ,OAAO,CAAC;AAC3D,QAAM,MAAM,mBAAmB,QAAQ,OAAO;AAC9C,QAAM,YAAY,mBAAmB,QAAQ,YAAY;AACzD,QAAM,OAAO,gBAAgB,QAAQ,QAAQ,EAAE;AAC/C,SAAO;AAAA,IACL;AAAA,MACE,KAAK;AAAA,MACL,MAAM;AAAA,MACN,KAAK,GAAG,IAAI,kBAAkB,GAAG,iBAAiB,SAAS;AAAA,IAC7D;AAAA,IACA;AAAA,MACE,KAAK;AAAA,MACL,MAAM;AAAA,MACN,KAAK,GAAG,IAAI,SAAS,GAAG,iBAAiB,SAAS;AAAA,IACpD;AAAA,IACA;AAAA,MACE,KAAK;AAAA,MACL,MAAM;AAAA,MACN,KAAK,GAAG,IAAI,WAAW,GAAG,iBAAiB,SAAS;AAAA,IACtD;AAAA,EACF;AACF;AAoBA,eAAsB,mBAAmB,MAAoD;AAC3F,QAAM,UAAU,MAAM,gBAAqB,cAAQ,KAAK,OAAO,CAAC;AAChE,QAAM,UAAU,sBAAsB,SAAS,KAAK,iBAAiB,KAAK,cAAc;AACxF,QAAM,UAAU,MAAM,QAAQ;AAAA,IAC5B,QAAQ;AAAA,MAAI,CAAC,WACX;AAAA,QACE;AAAA,QACA,KAAK,WAAW;AAAA,QAChB,KAAK,aAAa;AAAA,QAClB,KAAK;AAAA,QACL,QAAQ;AAAA,MACV;AAAA,IACF;AAAA,EACF;AACA,QAAM,SAAS,QAAQ,OAAO,CAAC,WAAW,OAAO,EAAE,EAAE;AACrD,QAAM,SAAS,QAAQ,SAAS;AAChC,SAAO;AAAA,IACL,IAAI,WAAW;AAAA,IACf,SAAS,QAAQ;AAAA,IACjB;AAAA,IACA;AAAA,IACA,SAAS;AAAA,EACX;AACF;AAEA,SAAS,sBACP,SACA,iBACA,gBACyB;AACzB,QAAM,OAAO,gBAAgB,QAAQ,QAAQ,EAAE;AAC/C,QAAM,MAAM,mBAAmB,QAAQ,OAAO;AAC9C,SAAO;AAAA,IACL,GAAG,eAAe;AAAA,MAChB,CAAC,YAAmC;AAAA,QAClC,MAAM;AAAA,QACN,SAAS,OAAO;AAAA,QAChB,KAAK,OAAO;AAAA,QACZ,QAAQ;AAAA,QACR,IAAI;AAAA,MACN;AAAA,IACF;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,KAAK,GAAG,IAAI,kBAAkB,GAAG;AAAA,MACjC,QAAQ;AAAA,MACR,IAAI;AAAA,IACN;AAAA,IACA,GAAG,CAAC,cAAc,aAAa,UAAU,EAAE;AAAA,MACzC,CAAC,UAAiC;AAAA,QAChC,MAAM;AAAA,QACN,KAAK,GAAG,IAAI,qBAAqB,GAAG,IAAI,IAAI;AAAA,QAC5C,QAAQ;AAAA,QACR,IAAI;AAAA,MACN;AAAA,IACF;AAAA,EACF;AACF;AAEA,eAAe,qBACb,QACA,SACA,WACA,aACA,cACgC;AAChC,QAAM,aAAa,IAAI,gBAAgB;AACvC,QAAM,UAAU,WAAW,MAAM,WAAW,MAAM,GAAG,SAAS;AAC9D,MAAI;AACF,UAAM,UACJ,OAAO,SAAS,YACZ;AAAA,MACE,GAAI,cAAc,EAAE,eAAe,UAAU,WAAW,GAAG,IAAI,CAAC;AAAA,MAChE,GAAI,eAAe,EAAE,mBAAmB,aAAa,IAAI,CAAC;AAAA,IAC5D,IACA;AACN,UAAM,WAAW,MAAM,QAAQ,OAAO,KAAK;AAAA,MACzC,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC;AAAA,MAC7B,QAAQ;AAAA,MACR,QAAQ,WAAW;AAAA,IACrB,CAAC;AACD,UAAM,KAAK,SAAS,UAAU,OAAO,SAAS,SAAS;AACvD,WAAO;AAAA,MACL,GAAG;AAAA,MACH,QAAQ,SAAS;AAAA,MACjB;AAAA,MACA,OAAO,KAAK,SAAY,QAAQ,SAAS,MAAM;AAAA,IACjD;AAAA,EACF,SAAS,GAAG;AACV,WAAO;AAAA,MACL,GAAG;AAAA,MACH,QAAQ;AAAA,MACR,IAAI;AAAA,MACJ,OAAQ,EAAY,WAAW;AAAA,IACjC;AAAA,EACF,UAAE;AACA,iBAAa,OAAO;AAAA,EACtB;AACF;AAEA,eAAe,kBACb,KACA,MACiC;AACjC,MAAI,OAAO,UAAU,WAAY,OAAM,IAAI,MAAM,wBAAwB;AACzE,SAAO,MAAM,KAAK,IAAI;AACxB;AAEO,SAAS,2BAA2B,QAAiD;AAC1F,QAAM,eAAe,cAAc,OAAO,MAAM;AAChD,QAAM,kBAAkB,gBAAgB,OAAO,MAAM,cAAc,CAAC,CAAC;AACrE,QAAM,YAAY,OAAO,MAAM,aAAa,CAAC;AAC7C,QAAM,WAAW,CAAC,CAAC,OAAO,QAAQ,UAAU,WAAW;AACvD,QAAM,QAAQ,OAAO,SAAS;AAC9B,QAAM,UAAU,CAAC,SAAS,MAAM;AAChC,QAAM,cAAc,OAAO,QAAQ;AAAA,IACjC,CAAC,YAAkC;AAAA,MACjC,SAAS,OAAO;AAAA,MAChB,MAAM,OAAO;AAAA,MACb,KAAK,OAAO;AAAA,IACd;AAAA,EACF;AACA,QAAM,aAAa,OAAO,SAAS,SAAS,YAAY,YAAY,SAAS,KAAK;AAClF,QAAM,aAAa,2BAA2B,QAAQ,aAAa,KAAK;AAExE,SAAO;AAAA,IACL,IAAI,OAAO,MAAM;AAAA,IACjB;AAAA,IACA;AAAA,IACA,SAAS;AAAA,MACP,UAAU;AAAA,QACR,IAAI,OAAO,SAAS;AAAA,QACpB,QAAQ,OAAO,SAAS,OAAO;AAAA,QAC/B,UAAU,OAAO,SAAS,SAAS;AAAA,QACnC,SAAS,OAAO,SAAS;AAAA,QACzB,cAAc,OAAO,SAAS;AAAA,MAChC;AAAA,MACA,QAAQ;AAAA,QACN,OAAO,OAAO,OAAO;AAAA,QACrB,SAAS,aAAa,QAAQ,aAAa,WAAW,aAAa;AAAA,QACnE,OAAO,aAAa;AAAA,QACpB,UAAU,aAAa;AAAA,QACvB,SAAS,aAAa;AAAA,QACtB,WAAW,aAAa;AAAA,MAC1B;AAAA,MACA,QAAQ;AAAA,QACN,IAAI;AAAA,QACJ,SAAS,CAAC,OAAO;AAAA,QACjB,eAAe,OAAO,OAAO,SAAY,OAAO;AAAA,QAChD,YAAY,gBAAgB,SAAS,gBAAgB,SAAS,gBAAgB;AAAA,QAC9E,SAAS,gBAAgB;AAAA,QACzB,SAAS,gBAAgB;AAAA,QACzB,SAAS,gBAAgB;AAAA,QACzB,OAAO,gBAAgB;AAAA,QACvB,WAAW,UAAU;AAAA,MACvB;AAAA,MACA,SAAS;AAAA,QACP,IAAI,YAAY,SAAS;AAAA,QACzB,OAAO,YAAY;AAAA,QACnB,UAAU,YAAY,IAAI,CAAC,WAAW,OAAO,OAAO;AAAA,MACtD;AAAA,IACF;AAAA,IACA,cAAc,OAAO,OAClB,OAAO,CAAC,UAAU,MAAM,WAAW,WAAW,EAC9C;AAAA,MACC,CAAC,WAAkC;AAAA,QACjC,MAAM,MAAM;AAAA,QACZ,QAAQ,MAAM;AAAA,QACd,YAAY,MAAM;AAAA,MACpB;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA,mBAAmB,iCAAiC,QAAQ,WAAW;AAAA,IACvE;AAAA,EACF;AACF;AAEA,SAAS,iCACP,QACA,aAC6B;AAC7B,QAAM,iBAAiB,wBAAwB,WAAW,GAAG;AAC7D,QAAM,UAAU,OAAO,SAAS;AAChC,SAAO;AAAA,IACL;AAAA,IACA,eAAe,UAAU,CAAC,OAAO,IAAI,CAAC;AAAA,IACtC,iBAAiB,uBAAuB,QAAQ,cAAc;AAAA,IAC9D,SAAS;AAAA,MACP,QAAQ,CAAC,SAAS,MAAM;AAAA,IAC1B;AAAA,EACF;AACF;AAEA,SAAS,wBACP,aACkC;AAClC,SACE,YAAY,KAAK,CAAC,WAAW,OAAO,YAAY,YAAY,KAC5D,YAAY,KAAK,CAAC,WAAW,OAAO,YAAY,MAAM,KACtD,YAAY,CAAC;AAEjB;AAEA,SAAS,uBAAuB,QAA0B,gBAAmC;AAC3F,QAAM,YAAY,OAAO,OAAO,QAC7B,OAAO,CAAC,WAAW,OAAO,SAAS,SAAS,EAC5C,IAAI,CAAC,WAAW,OAAO,GAAG;AAC7B,MAAI,WAAW,OAAQ,QAAO,MAAM,KAAK,IAAI,IAAI,SAAS,CAAC;AAE3D,QAAM,UAAU,OAAO,SAAS;AAChC,MAAI,CAAC,kBAAkB,CAAC,QAAS,QAAO,CAAC;AACzC,MAAI;AACF,UAAM,SAAS,IAAI,IAAI,cAAc,EAAE;AACvC,WAAO;AAAA,MACL,GAAG,MAAM,kBAAkB,OAAO;AAAA,MAClC,GAAG,MAAM,qBAAqB,OAAO;AAAA,MACrC,GAAG,MAAM,qBAAqB,OAAO;AAAA,MACrC,GAAG,MAAM,qBAAqB,OAAO;AAAA,IACvC;AAAA,EACF,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;AAEA,SAAS,2BACP,QACA,aACA,OACsB;AACtB,MAAI,SAAS,CAAC,MAAM,GAAI,QAAO;AAC/B,MAAI,CAAC,OAAO,SAAS,MAAO,QAAO;AACnC,MAAI,CAAC,OAAO,KAAM,QAAO;AACzB,MAAI,OAAO,KAAK,UAAU,OAAQ,QAAO;AACzC,MAAI,CAAC,YAAY,OAAQ,QAAO;AAChC,SAAO;AACT;AAEO,SAAS,uBAAuB,QAAkC;AACvE,QAAM,aAAa,OAAO,QAAQ,IAAI;AACtC,QAAM,QAAkB,CAAC;AACzB,QAAM,KAAK,WAAW,OAAO,KAAK,OAAO,MAAM,EAAE;AACjD,QAAM,KAAK,EAAE;AAGb,QAAM,KAAK,MAAM,UAAU,eAAe,OAAO,SAAS,QAAQ,gBAAW,aAAQ,EAAE;AACvF,MAAI,OAAO,SAAS,OAAO,QAAQ;AACjC,eAAW,OAAO,OAAO,SAAS,OAAQ,OAAM,KAAK,kBAAkB,GAAG,EAAE;AAAA,EAC9E;AACA,MAAI,OAAO,SAAS,SAAS,QAAQ;AACnC,eAAW,QAAQ,OAAO,SAAS,SAAU,OAAM,KAAK,kBAAkB,IAAI,EAAE;AAAA,EAClF;AAGA,QAAM,eAAe,EAAE,OAAO,GAAG,UAAU,GAAG,SAAS,GAAG,WAAW,EAAE;AACvE,aAAW,OAAO,OAAO,OAAQ,cAAa,IAAI,MAAM,KAAK;AAC7D,QAAM;AAAA,IACJ,MAAM,UAAU,eAAe,aAAa,KAAK,WAAW,aAAa,QAAQ,cAAc,aAAa,OAAO,aAAa,aAAa,SAAS;AAAA,EACxJ;AACA,aAAW,OAAO,OAAO,QAAQ;AAC/B,QAAI,IAAI,WAAW,YAAa;AAChC,UAAM,KAAK,WAAW,IAAI,OAAO,OAAO,CAAC,CAAC,IAAI,IAAI,SAAS,EAAE;AAAA,EAC/D;AAGA,MAAI,OAAO,MAAM;AACf,QAAI,OAAO,KAAK,UAAU,QAAQ;AAChC,YAAM,KAAK,MAAM,UAAU,sBAAiB,OAAO,KAAK,UAAU,MAAM,cAAc;AACtF,iBAAW,KAAK,OAAO,KAAK,UAAW,OAAM,KAAK,WAAW,EAAE,SAAS,KAAK,EAAE,MAAM,EAAE;AAAA,IACzF,OAAO;AACL,YAAM,UAAU,OAAO,KAAK,WAAW,OAAO,CAAC,OAAO,GAAG,cAAc,MAAM,EAAE;AAC/E,YAAM,KAAK,MAAM,UAAU,sBAAiB,OAAO,0CAA0C;AAC7F,iBAAW,MAAM,OAAO,KAAK,YAAY;AACvC,YAAI,GAAG,cAAc,OAAQ;AAC7B,cAAM,KAAK,WAAW,GAAG,UAAU,OAAO,CAAC,CAAC,IAAI,GAAG,SAAS,EAAE;AAAA,MAChE;AAAA,IACF;AAAA,EACF,OAAO;AACL,UAAM,KAAK,MAAM,UAAU,0BAA0B,OAAO,eAAe,gBAAgB,GAAG;AAAA,EAChG;AAGA,MAAI,OAAO,QAAQ,QAAQ;AACzB,UAAM,KAAK,MAAM,UAAU,YAAY;AACvC,eAAW,UAAU,OAAO,QAAS,OAAM,KAAK,WAAW,OAAO,IAAI,OAAO,EAAE,CAAC,IAAI,OAAO,GAAG,EAAE;AAAA,EAClG,OAAO;AACL,UAAM,KAAK,MAAM,UAAU,4BAA4B;AAAA,EACzD;AAGA,MAAI,OAAO,OAAO;AAChB,UAAM,SAAS,OAAO,MAAM,KAAK,WAAM;AACvC,UAAM;AAAA,MACJ,mBAAmB,MAAM,IAAI,OAAO,MAAM,MAAM,IAAI,OAAO,MAAM,OAAO;AAAA,IAC1E;AACA,eAAW,UAAU,OAAO,MAAM,SAAS;AACzC,YAAM,eAAe,OAAO,KAAK,WAAM;AACvC,YAAM,QAAQ,OAAO,UAAU,GAAG,OAAO,IAAI,IAAI,OAAO,OAAO,KAAK,OAAO;AAC3E,YAAM,SAAS,OAAO,UAAU;AAChC,YAAM,QAAQ,OAAO,QAAQ,KAAK,OAAO,KAAK,MAAM;AACpD,YAAM,KAAK,WAAW,YAAY,IAAI,MAAM,OAAO,EAAE,CAAC,IAAI,MAAM,IAAI,OAAO,GAAG,GAAG,KAAK,EAAE;AAAA,IAC1F;AAAA,EACF;AAEA,SAAO,MAAM,KAAK,IAAI;AACxB;AAEA,SAAS,cAAc,QAAsE;AAC3F,QAAM,SAAoD;AAAA,IACxD,OAAO;AAAA,IACP,UAAU;AAAA,IACV,SAAS;AAAA,IACT,WAAW;AAAA,EACb;AACA,aAAW,SAAS,OAAQ,QAAO,MAAM,MAAM,KAAK;AACpD,SAAO;AACT;AAEA,SAAS,gBAAgB,YAAmE;AAC1F,QAAM,SAA4C;AAAA,IAChD,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,MAAM;AAAA,EACR;AACA,aAAW,aAAa,WAAY,QAAO,UAAU,SAAS,KAAK;AACnE,SAAO;AACT;AAEA,eAAsB,gBAAgB,SAA8C;AAClF,QAAM,OAAY,WAAK,SAAS,iBAAiB;AACjD,MAAI;AACJ,MAAI;AACF,aAAS,KAAK,MAAM,MAAS,aAAS,MAAM,OAAO,CAAC;AAAA,EACtD,SAAS,GAAG;AACV,QAAK,EAA4B,SAAS,UAAU;AAClD,YAAM,IAAI,SAAS;AAAA,QACjB;AAAA,QACA,SAAS,GAAG,iBAAiB;AAAA,MAC/B,CAAC;AAAA,IACH;AACA,UAAM,IAAI,SAAS;AAAA,MACjB;AAAA,MACA,SAAS,GAAG,iBAAiB,gBAAiB,EAAY,OAAO;AAAA,IACnE,CAAC;AAAA,EACH;AACA,QAAM,MAAMC,UAAS,MAAM;AAC3B,MAAI,IAAI,SAAS,qBAAqB,IAAI,YAAY,sBAAsB;AAC1E,UAAM,IAAI,SAAS;AAAA,MACjB;AAAA,MACA,SAAS,GAAG,iBAAiB;AAAA,IAC/B,CAAC;AAAA,EACH;AACA,SAAO;AAAA,IACL,MAAM;AAAA,IACN,SAAS;AAAA,IACT,SAAS,iBAAiB,eAAe,IAAI,SAAS,SAAS,CAAC;AAAA,IAChE,cAAc,sBAAsB,eAAe,IAAI,cAAc,cAAc,CAAC;AAAA,IACpF,cAAc,eAAe,IAAI,cAAc,cAAc;AAAA,IAC7D,UAAU,eAAe,IAAI,QAAQ;AAAA,IACrC,UAAU,eAAe,IAAI,QAAQ;AAAA,IACrC,SAAS,eAAe,IAAI,OAAO;AAAA,IACnC,WAAW,eAAe,IAAI,SAAS,MAAK,oBAAI,KAAK,CAAC,GAAE,YAAY;AAAA,EACtE;AACF;AAEA,eAAsB,iBAAiB,SAA4C;AACjF,QAAM,OAAY,WAAK,SAAS,gBAAgB,uBAAuB;AACvE,MAAI;AACF,UAAM,SAAS,KAAK,MAAM,MAAS,aAAS,MAAM,OAAO,CAAC;AAC1D,WAAO;AAAA,MACL,SAAS;AAAA,MACT,SAAS,OAAO,UAAU,iBAAiB,OAAO,OAAO,IAAI;AAAA,MAC7D,cAAc,OAAO,eAAe,sBAAsB,OAAO,YAAY,IAAI;AAAA,MACjF,cAAc,OAAO,gBAAgB;AAAA,MACrC,SAAS,OAAO;AAAA,MAChB,UAAU,OAAO;AAAA,MACjB,OAAO,MAAM,QAAQ,OAAO,KAAK,IAAI,OAAO,QAAQ,CAAC;AAAA,IACvD;AAAA,EACF,SAAS,GAAG;AACV,QAAK,EAA4B,SAAS,UAAU;AAClD,aAAO;AAAA,QACL,SAAS;AAAA,QACT,SAAS;AAAA,QACT,cAAc;AAAA,QACd,cAAc;AAAA,QACd,WAAU,oBAAI,KAAK,CAAC,GAAE,YAAY;AAAA,QAClC,OAAO,CAAC;AAAA,MACV;AAAA,IACF;AACA,UAAM;AAAA,EACR;AACF;AAEA,eAAe,uBACbD,WACA,SACA,MACA,QACA,UACe;AACf,aAAW,OAAO,OAAO,KAAKA,SAAQ,GAAG;AACvC,QAAI,CAAC,wBAAwB,IAAI,GAAG,EAAG,QAAO,KAAK,iCAAiC,GAAG,EAAE;AAAA,EAC3F;AACA,gBAAcA,WAAU,iBAAiB,QAAQ,CAAC,UAAU,UAAU,KAAK;AAC3E,gBAAcA,WAAU,QAAQ,QAAQ,CAAC,UAAU,kBAAkB,KAAK,KAAK,CAAC;AAChF,gBAAcA,WAAU,QAAQ,QAAQ,CAAC,UAAU,MAAM,KAAK,EAAE,SAAS,CAAC;AAC1E,gBAAcA,WAAU,WAAW,QAAQ,CAAC,UAAU,eAAe,KAAK,KAAK,CAAC;AAChF,gBAAcA,WAAU,cAAc,QAAQ,CAAC,UAAU,eAAe,KAAK,KAAK,CAAC;AAEnF,QAAM,OAAO,OAAOA,UAAS,SAAS,WAAWA,UAAS,OAAO;AACjE,MAAI,WAAW,QAAQ,SAAS,QAAQ,SAAS;AAC/C,WAAO,KAAK,6CAA6C,QAAQ,OAAO,EAAE;AAAA,EAC5E;AACA,QAAM,iBAAiB,cAAcA,UAAS,cAAc;AAC5D,MAAI,CAAC,eAAe,QAAQ;AAC1B,WAAO,KAAK,yDAAyD;AAAA,EACvE;AACA,aAAW,SAAS,gBAAgB;AAClC,QAAI,CAAC,cAAc,KAAK,KAAK,GAAG;AAC9B,aAAO,KAAK,8CAA8C,KAAK,EAAE;AAAA,IACnE;AAAA,EACF;AAEA,QAAM,WAAW,cAAcA,UAAS,QAAQ;AAChD,QAAM,iBAAiB,OAAOA,UAAS,mBAAmB,WAAWA,UAAS,iBAAiB;AAC/F,MAAI,SAAS,QAAQ;AACnB,eAAW,WAAW,UAAU;AAC9B,UAAI,YAAY,cAAc,YAAY,cAAc;AACtD,eAAO,KAAK,wCAAwC,OAAO,EAAE;AAAA,MAC/D;AAAA,IACF;AACA,QAAI,IAAI,IAAI,QAAQ,EAAE,SAAS,SAAS,QAAQ;AAC9C,aAAO,KAAK,kCAAkC;AAAA,IAChD;AAAA,EACF,OAAO;AACL,aAAS,KAAK,yDAAyD;AAAA,EACzE;AACA,MAAI,kBAAkB,SAAS,UAAU,CAAC,SAAS,SAAS,cAAc,GAAG;AAC3E,WAAO,KAAK,sDAAsD;AAAA,EACpE;AAEA,QAAM,QAAQC,UAASD,UAAS,KAAK;AACrC,MAAI,CAAC,OAAO,KAAK,KAAK,EAAE,QAAQ;AAC9B,WAAO,KAAK,4BAA4B;AAAA,EAC1C;AACA,aAAW,OAAO,OAAO,KAAK,KAAK,GAAG;AACpC,QAAI,CAAC,WAAW,IAAI,GAAG,EAAG,QAAO,KAAK,uCAAuC,GAAG,EAAE;AAAA,EACpF;AACA,QAAM,OAAO,eAAe,MAAM,IAAI;AACtC,QAAM,KAAK,eAAe,MAAM,EAAE;AAClC,QAAM,MAAM,eAAe,MAAM,GAAG;AACpC,MAAI,CAAC,QAAQ,CAAC,IAAI;AAChB,WAAO,KAAK,uCAAuC;AAAA,EACrD;AACA,QAAM,kBAAkB,MAAM,cAAc,MAAM,SAAS,MAAM;AACjE,QAAM,kBAAkB,MAAM,YAAY,IAAI,OAAO,MAAM;AAC3D,QAAM,kBAAkB,MAAM,aAAa,KAAK,QAAQ,MAAM;AAC9D,QAAM,4BAA4B,MAAM,IAAI,gBAAgB,QAAQ;AAEpE,QAAM,kBAAkBC,UAASD,UAAS,eAAe;AACzD,QAAM,WAAWC,UAAS,gBAAgB,QAAQ;AAClD,QAAM,cAAc,eAAe,SAAS,WAAW;AACvD,MAAI,WAAW,eAAe,gBAAgB,iBAAiB,QAAQ,OAAO,GAAG;AAC/E,WAAO;AAAA,MACL,yDAAyD,iBAAiB,QAAQ,OAAO,CAAC;AAAA,IAC5F;AAAA,EACF;AACF;AAEA,eAAe,kBACb,MACA,OACA,OACA,QACA,QACe;AACf,MAAI,CAAC,MAAO;AACZ,MAAI,CAAC,MAAM,SAAS,MAAM,EAAG,QAAO,KAAK,GAAG,KAAK,kBAAkB,MAAM,EAAE;AAC3E,MAAI;AACF,0BAAsB,KAAK;AAAA,EAC7B,SAAS,GAAG;AACV,WAAO,KAAK,GAAG,KAAK,gBAAiB,EAAY,OAAO,EAAE;AAC1D;AAAA,EACF;AACA,MAAI;AACF,UAAS,WAAY,WAAK,MAAM,KAAK,CAAC;AAAA,EACxC,QAAQ;AACN,WAAO,KAAK,GAAG,KAAK,gCAAgC,KAAK,EAAE;AAAA,EAC7D;AACF;AAEA,eAAe,4BACb,MACA,QACA,gBACA,UACe;AACf,MAAI,CAAC,OAAQ;AACb,QAAM,eAAoB,cAAQ,MAAM,MAAM;AAC9C,MAAI,CAAC,aAAa,WAAW,OAAY,SAAG,EAAG;AAC/C,MAAI,SAAS;AACb,MAAI;AACF,aAAS,MAAS,aAAS,cAAc,OAAO;AAAA,EAClD,QAAQ;AACN;AAAA,EACF;AAEA,QAAM,iBAAiB,IAAI,IAAI,cAAc;AAC7C,aAAW,SAAS,2BAA2B,MAAM,GAAG;AACtD,QAAI,CAAC,eAAe,IAAI,KAAK,GAAG;AAC9B,eAAS,KAAK,0CAA0C,KAAK,QAAQ,MAAM,YAAY;AAAA,IACzF;AAAA,EACF;AACF;AAEA,SAAS,2BAA2B,QAA0B;AAC5D,QAAM,YAAY,uBAAuB,MAAM;AAC/C,QAAM,SAAS,oBAAI,IAAY;AAC/B,MAAI;AAEJ,8BAA4B,YAAY;AACxC,SAAQ,QAAQ,4BAA4B,KAAK,MAAM,GAAI;AACzD,UAAM,SAAS,MAAM,CAAC;AACtB,QAAI,CAAC,OAAQ;AACb,UAAM,QAAQ,MAAM,CAAC,KAAK,UAAU,IAAI,MAAM,CAAC,KAAK,EAAE;AACtD,UAAMC,UAAS,uBAAuB,MAAM;AAC5C,QAAI,SAASA,QAAQ,QAAO,IAAI,GAAG,KAAK,IAAIA,OAAM,EAAE;AAAA,EACtD;AAEA,+BAA6B,YAAY;AACzC,SAAQ,QAAQ,6BAA6B,KAAK,MAAM,GAAI;AAC1D,UAAM,QAAQ,MAAM,CAAC,KAAK,UAAU,IAAI,MAAM,CAAC,KAAK,EAAE;AACtD,UAAMA,UAAS,MAAM,CAAC;AACtB,QAAI,SAASA,QAAQ,QAAO,IAAI,GAAG,KAAK,IAAIA,OAAM,EAAE;AAAA,EACtD;AAEA,SAAO,MAAM,KAAK,MAAM;AAC1B;AAEA,SAAS,uBAAuB,QAAqC;AACnE,QAAM,YAAY,oBAAI,IAAoB;AAC1C,MAAI;AACJ,2BAAyB,YAAY;AACrC,SAAQ,QAAQ,yBAAyB,KAAK,MAAM,GAAI;AACtD,QAAI,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,EAAG;AAC5B,cAAU,IAAI,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC;AAAA,EAClC;AACA,SAAO;AACT;AAEA,SAAS,cACP,SACAF,WACA,YACA,aACmE;AACnE,QAAM,SAAS,qBAAqB,SAASA,WAAU,UAAU;AACjE,QAAM,cAAc,IAAI,IAAIA,UAAS,MAAM,IAAI,CAAC,SAAS,CAAC,KAAK,WAAW,IAAI,CAAU,CAAC;AACzF,QAAM,gBAAgB,IAAI;AAAA,IACxB,YAAY,IAAI,CAAC,SAAS;AAAA,MACxB,oBAAoB,QAAQ,cAAc,KAAK,KAAK,YAAY;AAAA,MAChE;AAAA,IACF,CAAU;AAAA,EACZ;AACA,QAAM,aAAiC,CAAC;AACxC,QAAM,YAAgC,CAAC;AAEvC,aAAW,SAAS,QAAQ;AAC1B,UAAM,SAAS,cAAc,IAAI,MAAM,SAAS;AAChD,UAAM,OAAO,YAAY,IAAI,MAAM,SAAS;AAC5C,UAAM,YAAY,SAAS,OAAO,OAAO,QAAQ,OAAO,IAAI;AAC5D,QAAI,MAAM,WAAW,SAAS;AAC5B,UAAI,QAAQ;AACV,kBAAU,KAAK,SAAS,OAAO,0DAA0D,CAAC;AAAA,MAC5F,OAAO;AACL,mBAAW,KAAK,EAAE,WAAW,UAAU,WAAW,MAAM,WAAW,YAAY,MAAM,WAAW,CAAC;AAAA,MACnG;AACA;AAAA,IACF;AACA,QAAI,MAAM,WAAW,YAAY;AAC/B,UAAI,CAAC,QAAQ;AACX,kBAAU,KAAK,SAAS,OAAO,yCAAyC,CAAC;AAAA,MAC3E,WAAW,QAAQ,cAAc,KAAK,QAAQ;AAC5C,kBAAU,KAAK,SAAS,OAAO,qCAAqC,CAAC;AAAA,MACvE,OAAO;AACL,mBAAW,KAAK;AAAA,UACd,WAAW;AAAA,UACX,WAAW,MAAM;AAAA,UACjB,YAAY,MAAM;AAAA,UAClB,QAAQ,OAAO,KAAK;AAAA,QACtB,CAAC;AAAA,MACH;AACA;AAAA,IACF;AACA,QAAI,MAAM,WAAW,WAAW;AAC9B,UAAI,CAAC,QAAQ;AACX,mBAAW,KAAK;AAAA,UACd,WAAW;AAAA,UACX,WAAW,MAAM;AAAA,UACjB,YAAY,MAAM;AAAA,UAClB,QAAQ;AAAA,QACV,CAAC;AAAA,MACH,WAAW,QAAQ,cAAc,KAAK,QAAQ;AAC5C,kBAAU,KAAK,SAAS,OAAO,qCAAqC,CAAC;AAAA,MACvE,OAAO;AACL,mBAAW,KAAK;AAAA,UACd,WAAW;AAAA,UACX,WAAW,MAAM;AAAA,UACjB,YAAY,MAAM;AAAA,UAClB,QAAQ,OAAO,KAAK;AAAA,QACtB,CAAC;AAAA,MACH;AACA;AAAA,IACF;AACA,eAAW,KAAK;AAAA,MACd,WAAW;AAAA,MACX,WAAW,MAAM;AAAA,MACjB,YAAY,MAAM;AAAA,MAClB,QAAQ,QAAQ,KAAK;AAAA,MACrB,QAAQ;AAAA,IACV,CAAC;AAAA,EACH;AACA,SAAO,EAAE,YAAY,UAAU;AACjC;AAEA,SAAS,qBACP,SACAA,WACA,YACmB;AACnB,QAAM,cAAc,IAAI,IAAIA,UAAS,MAAM,IAAI,CAAC,SAAS,CAAC,KAAK,WAAW,IAAI,CAAU,CAAC;AACzF,QAAM,cAAc,IAAI,IAAI,WAAW,IAAI,CAAC,SAAS,CAAC,KAAK,WAAW,IAAI,CAAU,CAAC;AACrF,QAAM,QAAQ,CAAC,GAAG,oBAAI,IAAI,CAAC,GAAG,YAAY,KAAK,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK;AAChF,SAAO,MAAM,IAAI,CAAC,cAAc;AAC9B,UAAM,OAAO,YAAY,IAAI,SAAS;AACtC,UAAM,QAAQ,YAAY,IAAI,SAAS;AACvC,UAAM,aAAa,MAAM,cAAc,oBAAoB,QAAQ,SAAS,SAAS;AACrF,QAAI,CAAC,QAAQ,OAAO;AAClB,aAAO,EAAE,WAAW,YAAY,QAAQ,SAAS,QAAQ,MAAM,OAAO;AAAA,IACxE;AACA,QAAI,QAAQ,CAAC,OAAO;AAClB,aAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA,QAAQ;AAAA,QACR,QAAQ,KAAK;AAAA,QACb,YAAY,KAAK;AAAA,MACnB;AAAA,IACF;AACA,QAAI,QAAQ,SAAS,KAAK,WAAW,MAAM,QAAQ;AACjD,aAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA,QAAQ;AAAA,QACR,QAAQ,KAAK;AAAA,QACb,QAAQ,MAAM;AAAA,QACd,YAAY,KAAK;AAAA,MACnB;AAAA,IACF;AACA,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA,QAAQ;AAAA,MACR,QAAQ,MAAM;AAAA,MACd,QAAQ,OAAO;AAAA,MACf,YAAY,MAAM;AAAA,IACpB;AAAA,EACF,CAAC;AACH;AAEA,eAAe,uBACb,MACA,cACA,SAC4E;AAC5E,QAAM,SAAS,iBAAiB,OAAO,IAAI;AAC3C,QAAM,OAAO,MAAM,mBAAmB,MAAM,cAAc,EAAE,UAAU,IAAK,CAAC;AAC5E,QAAM,SAA4E,CAAC;AACnF,aAAW,QAAQ,KAAK,MAAM;AAC5B,QAAI,CAAC,KAAK,aAAa,WAAW,MAAM,KAAK,sBAAsB,KAAK,YAAY,EAAG;AACvF,UAAM,UAAU,MAAM,yBAAyB,MAAM,KAAK,EAAE;AAC5D,WAAO,KAAK,EAAE,MAAM,QAAQ,CAAC;AAAA,EAC/B;AACA,SAAO;AACT;AAEA,eAAe,mBAAmB,MAAwC;AACxE,QAAM,QAAyB,CAAC;AAChC,QAAM,KAAK,MAAM,IAAI,KAAK;AAC1B,QAAM,KAAK,CAAC,GAAG,MAAM,EAAE,UAAU,cAAc,EAAE,SAAS,CAAC;AAC3D,SAAO;AACT;AAEA,eAAe,KAAK,MAAc,aAAqB,OAAuC;AAC5F,QAAM,cAAmB,WAAK,MAAM,WAAW;AAC/C,MAAI;AACJ,MAAI;AACF,cAAU,MAAS,YAAQ,aAAa,EAAE,eAAe,KAAK,CAAC;AAAA,EACjE,SAAS,GAAG;AACV,QAAK,EAA4B,SAAS,SAAU;AACpD,UAAM;AAAA,EACR;AACA,aAAW,SAAS,SAAS;AAC3B,QAAI,MAAM,SAAS,kBAAmB;AACtC,QAAI,MAAM,YAAY,KAAK,cAAc,IAAI,MAAM,IAAI,EAAG;AAC1D,UAAM,YAAY,sBAA2B,YAAM,KAAK,QAAQ,WAAW,GAAG,MAAM,IAAI,CAAC;AACzF,UAAM,eAAoB,WAAK,MAAM,SAAS;AAC9C,QAAI,MAAM,YAAY,GAAG;AACvB,YAAM,KAAK,MAAM,WAAW,KAAK;AAAA,IACnC,WAAW,MAAM,OAAO,GAAG;AACzB,YAAM,UAAU,MAAS,aAAS,cAAc,OAAO;AACvD,YAAM,KAAK;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,QACA,QAAQ,OAAO,OAAO;AAAA,QACtB,MAAM,OAAO,WAAW,SAAS,OAAO;AAAA,MAC1C,CAAC;AAAA,IACH;AAAA,EACF;AACF;AAEA,eAAe,eACb,MACA,SACA,OACA,mBACe;AACf,QAAM,WAAgB,WAAK,MAAM,cAAc;AAC/C,QAAM,UAAe,WAAK,UAAU,MAAM;AAC1C,QAAS,OAAG,SAAS,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AACrD,QAAS,UAAM,SAAS,EAAE,WAAW,KAAK,CAAC;AAC3C,QAAM,YAAgC,CAAC;AACvC,aAAW,QAAQ,OAAO;AACxB,UAAM,UAAe,WAAK,SAAS,KAAK,SAAS,GAAG,KAAK,OAAO;AAChE,cAAU,KAAK,WAAW,SAAS,MAAM,kBAAkB,IAAI,KAAK,SAAS,CAAC,CAAC;AAAA,EACjF;AACA,QAAM,UAAe,WAAK,UAAU,uBAAuB,GAAG;AAAA,IAC5D,SAAS;AAAA,IACT,SAAS,QAAQ;AAAA,IACjB,cAAc,QAAQ;AAAA,IACtB,cAAc,QAAQ;AAAA,IACtB,SAAS,QAAQ;AAAA,IACjB,WAAU,oBAAI,KAAK,GAAE,YAAY;AAAA,IACjC,OAAO;AAAA,EACT,CAA4B;AAC9B;AAEA,SAAS,WACP,SACA,MACA,QACkB;AAClB,SAAO;AAAA,IACL,WAAW,KAAK;AAAA,IAChB,YAAY,QAAQ,gBAAgB,oBAAoB,QAAQ,SAAS,KAAK,SAAS;AAAA,IACvF,QAAQ,QAAQ;AAAA,IAChB,QAAQ,KAAK;AAAA,IACb,MAAM,KAAK;AAAA,IACX,cAAc,QAAQ;AAAA,EACxB;AACF;AAEA,SAASD,2BAA0B,MAWe;AAChD,SAAO,0BAA4B,IAAI;AACzC;AAEA,eAAe,qBACb,WACA,eACA,OACe;AACf,MAAI,MAAO;AACX,QAAM,WAAqB,CAAC;AAC5B,aAAW,gBAAgB,eAAe;AACxC,QAAI;AACF,YAAS,WAAY,WAAK,WAAW,YAAY,CAAC;AAClD,eAAS,KAAK,YAAY;AAAA,IAC5B,QAAQ;AAAA,IAER;AAAA,EACF;AACA,MAAI,SAAS,QAAQ;AACnB,UAAM,IAAI,SAAS;AAAA,MACjB;AAAA,MACA,SAAS,mCAAmC,SAAS,KAAK,IAAI,CAAC;AAAA,IACjE,CAAC;AAAA,EACH;AACF;AAEA,SAAS,qBAAqB,OAAe,WAA2B;AACtE,MAAI,CAAC,SAAS,CAAC,MAAM,KAAK,GAAG;AAC3B,UAAM,IAAI,SAAS,EAAE,yCAAiC,SAAS,GAAG,SAAS,eAAe,CAAC;AAAA,EAC7F;AACA,SAAO,MAAM,KAAK;AACpB;AAEA,SAAS,iBAAiB,OAAuB;AAC/C,QAAM,aAAa,qBAAqB,OAAO,SAAS;AACxD,MAAI,CAAC,sBAAsB,KAAK,UAAU,GAAG;AAC3C,UAAM,IAAI,SAAS;AAAA,MACjB;AAAA,MACA,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AACA,SAAO;AACT;AAEA,SAAS,sBAAsB,OAAuB;AACpD,QAAM,aAAa,qBAAqB,OAAO,cAAc;AAC7D,MAAI,CAAC,oBAAoB,KAAK,UAAU,GAAG;AACzC,UAAM,IAAI,SAAS;AAAA,MACjB;AAAA,MACA,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AACA,SAAO;AACT;AAEA,SAAS,sBAAsB,OAAuB;AACpD,QAAM,aAAa,QAAQ,KAAK,EAAE,QAAQ,QAAQ,EAAE,EAAE,QAAQ,QAAQ,GAAG;AACzE,MAAI,CAAC,cAAc,eAAe,OAAO,WAAW,SAAS,IAAI,GAAG;AAClE,UAAM,IAAI,MAAM,eAAe;AAAA,EACjC;AACA,aAAW,WAAW,WAAW,MAAM,GAAG,GAAG;AAC3C,QAAI,CAAC,WAAW,YAAY,OAAO,YAAY,MAAM;AACnD,YAAM,IAAI,MAAM,wBAAwB,OAAO,EAAE;AAAA,IACnD;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,iBAAiB,SAAyB;AACjD,SAAO,GAAG,gBAAgB,IAAI,iBAAiB,OAAO,CAAC;AACzD;AAEA,SAAS,oBAAoB,SAAiB,WAA2B;AACvE,SAAO,GAAG,iBAAiB,OAAO,CAAC,IAAI,sBAAsB,SAAS,CAAC;AACzE;AAEA,SAAS,oBAAoB,cAAsB,YAA4B;AAC7E,QAAM,SAAS,eAAe;AAC9B,MAAI,CAAC,WAAW,WAAW,MAAM,GAAG;AAClC,UAAM,IAAI,SAAS;AAAA,MACjB;AAAA,MACA,SAAS,2CAA2C,UAAU;AAAA,IAChE,CAAC;AAAA,EACH;AACA,SAAO,sBAAsB,WAAW,MAAM,OAAO,MAAM,CAAC;AAC9D;AAEA,SAAS,sBAAsB,cAA+B;AAC5D,SAAO,aAAa,SAAS,QAAQ,KAAK,iBAAiB;AAC7D;AAEA,SAAS,OAAO,SAAyB;AACvC,SAAc,kBAAW,QAAQ,EAAE,OAAO,SAAS,OAAO,EAAE,OAAO,KAAK;AAC1E;AAEA,eAAe,UAAU,MAAc,SAAgC;AACrE,QAAS,UAAW,cAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AACtD,QAAS,cAAU,MAAM,SAAS,OAAO;AAC3C;AAEA,eAAe,UAAU,MAAc,OAA+B;AACpE,QAAM,UAAU,MAAM,KAAK,UAAU,OAAO,MAAM,CAAC,IAAI,IAAI;AAC7D;AAEA,SAAS,SAAS,OAAwB,QAAkC;AAC1E,SAAO;AAAA,IACL,WAAW,MAAM;AAAA,IACjB,YAAY,MAAM;AAAA,IAClB;AAAA,EACF;AACF;AAEA,SAASE,UAAS,OAAyC;AACzD,SAAO,SAAS,OAAO,UAAU,WAAY,QAAoC,CAAC;AACpF;AAEA,SAAS,cAAc,OAA0B;AAC/C,SAAO,MAAM,QAAQ,KAAK,IAAI,MAAM,OAAO,CAAC,SAAyB,OAAO,SAAS,QAAQ,IAAI,CAAC;AACpG;AAEA,SAAS,cACP,KACA,KACA,QACA,WACM;AACN,QAAM,QAAQ,IAAI,GAAG;AACrB,MAAI,OAAO,UAAU,YAAY,CAAC,MAAM,KAAK,GAAG;AAC9C,WAAO,KAAK,YAAY,GAAG,cAAc;AACzC;AAAA,EACF;AACA,MAAI,aAAa,CAAC,UAAU,KAAK,GAAG;AAClC,WAAO,KAAK,YAAY,GAAG,aAAa;AAAA,EAC1C;AACF;AAEA,SAAS,eAAe,OAAgB,WAA2B;AACjE,MAAI,OAAO,UAAU,YAAY,CAAC,MAAM,KAAK,GAAG;AAC9C,UAAM,IAAI,SAAS;AAAA,MACjB;AAAA,MACA,SAAS,GAAG,iBAAiB,YAAY,SAAS;AAAA,IACpD,CAAC;AAAA,EACH;AACA,SAAO;AACT;AAEA,SAAS,eAAe,OAAoC;AAC1D,SAAO,OAAO,UAAU,YAAY,MAAM,KAAK,IAAI,QAAQ;AAC7D;AAEA,SAAS,QAAQ,OAAuB;AACtC,SAAO,MAAM,QAAQ,OAAO,GAAG;AACjC;;;AEzwCA,eAAsB,cAAc,MAAqB,OAAoC;AAC3F,QAAM,OAAO,MAAM,KAAK,IAAI,eAAe,KAAK,QAAQ;AACxD,SAAQ,KAAK,MAAM,QAAQ,KAAK;AAClC;AAEA,eAAsB,gBACpB,MACA,OACA,IAC2B;AAC3B,QAAM,MAAM,KAAK,eAAe,KAAK,UAAU,EAAE,KAAK,eAAe,KAAK;AAC1E,QAAM,OAAO,MAAM,KAAK,OAAO,GAAG;AAClC,SAAQ,KAAK,MAAM,QAAQ,KAAK;AAClC;AASA,eAAsB,eACpB,MACA,OACA,OAA2B,CAAC,GACD;AAC3B,QAAM,SAAiC,CAAC;AACxC,MAAI,KAAK,KAAM,QAAO,OAAO,KAAK;AAClC,MAAI,KAAK,MAAO,QAAO,QAAQ,KAAK;AACpC,QAAM,OAAO,MAAM,KAAK,OAAO,eAAe,KAAK,eAAe,EAAE,OAAO,CAAC;AAC5E,SAAQ,KAAK,MAAM,QAAQ,KAAK;AAClC;;;ACvBA,eAAsB,gBACpB,MACA,cACA,KACgC;AAChC,QAAM,OAAO,MAAM,KAAK,KAAK,yBAAyB,KAAK;AAAA,IACzD,SAAS,EAAE,mBAAmB,aAAa;AAAA,EAC7C,CAAC;AACD,SAAQ,KAAK,MAAM,QAAQ,KAAK;AAClC;;;ACTA,eAAsB,eACpB,MACA,OAA2B,CAAC,GACyC;AACrE,QAAM,OAAO,MAAM,KAAK,IAAI,yBAAyB,EAAE,QAAQ,KAAK,CAAC;AACrE,QAAM,UAAU,KAAK,MAAM,QAAQ,KAAK;AACxC,QAAM,QAAS,SAAS,SAAS,SAAS,QAAQ,SAAS,WAAW,CAAC;AACvE,SAAO,EAAE,OAAO,OAAO,SAAS,OAAO,KAAK,QAAQ;AACtD;AAGA,eAAsB,cAAc,MAAqBE,OAA+B;AACtF,QAAM,OAAO,MAAM,KAAK,IAAI,yBAAyB,EAAE,QAAQ,EAAE,MAAAA,MAAK,EAAE,CAAC;AACzE,QAAM,UAAU,KAAK,MAAM,QAAQ,KAAK;AACxC,MAAI,OAAO,YAAY,SAAU,QAAO;AACxC,SAAO,OAAO,SAAS,WAAW,EAAE;AACtC;AAQA,eAAsB,eACpB,MACAA,OACA,SACyB;AACzB,QAAM,OAAO,MAAM,KAAK,IAAI,yBAAyB,EAAE,QAAQ,GAAG,EAAE,QAAQ,EAAE,MAAAA,MAAK,EAAE,CAAC;AACtF,SAAQ,KAAK,MAAM,QAAQ,KAAK;AAClC;AAEA,eAAsB,iBACpB,MACAA,OACyB;AACzB,QAAM,OAAO,MAAM,KAAK,OAAO,yBAAyB,EAAE,QAAQ,EAAE,MAAAA,MAAK,EAAE,CAAC;AAC5E,SAAQ,KAAK,MAAM,QAAQ,KAAK;AAClC;;;ACzEA,OAAOC,SAAQ,eAAAC,oBAAmB;AA0ElC,IAAM,yBAAyB,CAAC,QAAQ,EAAE;AAE1C,SAAS,kBAAkB,MAAuC;AAChE,MAAI,CAAC,QAAQ,OAAO,SAAS,SAAU,QAAO;AAC9C,QAAM,IAAI;AACV,SAAO,EAAE,SAAS,YAAY,MAAM,QAAQ,EAAE,UAAU;AAC1D;AAYA,eAAsB,kBAAkB,MAAsC;AAC5E,QAAM,QAAkB,CAAC;AACzB,aAAW,QAAQ,wBAAwB;AACzC,UAAM,MAAM,GAAG,IAAI;AACnB,UAAM,KAAK,GAAG;AACd,QAAI;AACF,YAAM,OAAO,MAAM,KAAK,IAAI,GAAG;AAC/B,YAAM,OAAO,KAAK,MAAM,QAAQ,KAAK;AACrC,UAAI,kBAAkB,IAAI,EAAG,QAAO;AAAA,IACtC,QAAQ;AAAA,IAER;AAAA,EACF;AACA,QAAM,IAAI,SAAS;AAAA,IACjB;AAAA,IACA,SACE,oGAAoB,MAAM,KAAK,UAAK,CAAC;AAAA,EAGzC,CAAC;AACH;AAEA,eAAsB,kBAAkB,MAAqB,MAAuC;AAClG,QAAM,OAAO,MAAM,KAAK,IAAI,GAAG,IAAI,cAAc;AACjD,SAAQ,KAAK,MAAM,QAAQ,KAAK;AAClC;AAEA,eAAsB,mBAAmB,MAAqB,IAAY,MAA+B;AACvG,QAAM,OAAO,MAAM,KAAK,IAAI,GAAG,IAAI,WAAW,mBAAmB,EAAE,CAAC,OAAO;AAAA,IACzE,cAAc;AAAA,IACd,mBAAmB,CAAC,CAAC,MAAM,CAAC;AAAA,EAC9B,CAAC;AACD,SAAO,OAAO,KAAK,QAAQ,EAAE;AAC/B;AAGA,eAAsB,eACpB,MACA,IACA,cACA,MACiB;AACjB,oBAAkB,EAAE;AACpB,0BAAwB,YAAY;AACpC,QAAM,OAAO,MAAM,KAAK,IAAI,GAAG,IAAI,WAAW,mBAAmB,EAAE,CAAC,UAAU;AAAA,IAC5E,QAAQ,EAAE,MAAM,aAAa;AAAA,IAC7B,cAAc;AAAA,IACd,mBAAmB,CAAC,CAAC,SAAS,IAAI;AAAA,EACpC,CAAC;AACD,SAAO,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC;AACpC;AAGA,eAAsB,oBACpB,MACA,IACA,cACA,MACiB;AACjB,0BAAwB,YAAY;AACpC,QAAM,UAAU,aAAa,MAAM,GAAG,EAAE,IAAI,kBAAkB,EAAE,KAAK,GAAG;AACxE,QAAM,OAAO,MAAM,KAAK,IAAI,GAAG,IAAI,WAAW,mBAAmB,EAAE,CAAC,eAAe,OAAO,IAAI;AAAA,IAC5F,cAAc;AAAA,IACd,mBAAmB,CAAC,CAAC,MAAM,CAAC;AAAA,EAC9B,CAAC;AACD,SAAO,OAAO,KAAK,QAAQ,EAAE;AAC/B;AAQO,SAAS,wBAAwB,cAA4B;AAClE,QAAM,KAAK,8EACR,KAAK,YAAY;AACpB,MAAI,CAAC,IAAI;AACP,UAAM,QAAQ,+DAAa,KAAK,UAAU,YAAY,CAAC,EAAE;AAAA,EAC3D;AACF;AAGO,SAAS,wBAAwB,cAA4B;AAClE,QAAM,WAAW,cAAc,MAAM,GAAG,KAAK,CAAC;AAC9C,MACE,CAAC,gBACD,aAAa,SAAS,OACtB,aAAa,WAAW,GAAG,KAC3B,aAAa,SAAS,IAAI,KAC1B,SAAS;AAAA,IACP,CAAC,YACC,CAAC,WACD,YAAY,OACZ,YAAY,QACZ,CAAC,qCAAqC,KAAK,OAAO;AAAA,EACtD,GACA;AACA,UAAM,QAAQ,iDAAmB,YAAY,GAAG;AAAA,EAClD;AACF;AAGO,SAAS,oBAAoB,KAAc,SAA2B;AAC3E,MAAI,QAAQ,UAAa,QAAQ,KAAM,QAAO,CAAC;AAC/C,MAAI,CAAC,MAAM,QAAQ,GAAG,EAAG,OAAM,QAAQ,gBAAM,OAAO,qEAAwB;AAC5E,QAAM,MAAgB,CAAC;AACvB,aAAW,SAAS,KAAK;AACvB,QAAI,OAAO,UAAU,YAAY,CAAC,MAAM,KAAK,GAAG;AAC9C,YAAM,QAAQ,gBAAM,OAAO,iFAA0B;AAAA,IACvD;AACA,UAAM,MAAM,MAAM,KAAK;AACvB,4BAAwB,GAAG;AAC3B,QAAI,CAAC,IAAI,SAAS,GAAG,EAAG,KAAI,KAAK,GAAG;AAAA,EACtC;AACA,SAAO;AACT;AAEO,SAAS,cAAcC,WAAyC;AACrE,UAAQA,UAAS,cAAc,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;AAClE;AAQO,SAAS,yBAAyBA,WAA0B,UAAiC;AAClG,QAAM,OAAO,oBAAI,IAAyB;AAC1C,aAAW,SAAS,cAAcA,SAAQ,GAAG;AAC3C,QAAI,KAAK,IAAI,MAAM,EAAE,GAAG;AACtB,YAAM,QAAQ,wDAAgB,MAAM,EAAE,EAAE;AAAA,IAC1C;AACA,SAAK,IAAI,MAAM,IAAI,KAAK;AAAA,EAC1B;AACA,MAAI,CAAC,KAAK,IAAI,QAAQ,GAAG;AACvB,UAAM,QAAQ,oDAAY,QAAQ,EAAE;AAAA,EACtC;AAEA,QAAM,QAAQ,oBAAI,IAAiC;AACnD,QAAMC,QAAiB,CAAC;AACxB,QAAM,QAAuB,CAAC;AAE9B,QAAM,QAAQ,CAAC,IAAY,eAA8B;AACvD,UAAM,UAAU,MAAM,IAAI,EAAE;AAC5B,QAAI,YAAY,OAAQ;AACxB,QAAI,YAAY,YAAY;AAC1B,YAAM,aAAaA,MAAK,QAAQ,EAAE;AAClC,YAAM,QAAQ,CAAC,GAAGA,MAAK,MAAM,KAAK,IAAI,GAAG,UAAU,CAAC,GAAG,EAAE;AACzD,YAAM,QAAQ,+CAAY,MAAM,KAAK,MAAM,CAAC,EAAE;AAAA,IAChD;AAEA,UAAM,QAAQ,KAAK,IAAI,EAAE;AACzB,QAAI,CAAC,OAAO;AACV,YAAM,QAAQ,gBAAM,cAAc,QAAQ,oCAAW,EAAE,EAAE;AAAA,IAC3D;AACA,sBAAkB,MAAM,EAAE;AAC1B,UAAM,IAAI,IAAI,UAAU;AACxB,IAAAA,MAAK,KAAK,EAAE;AACZ,eAAW,YAAY,sBAAsB,MAAM,UAAU,MAAM,EAAE,GAAG;AACtE,YAAM,UAAU,MAAM,EAAE;AAAA,IAC1B;AACA,IAAAA,MAAK,IAAI;AACT,UAAM,IAAI,IAAI,MAAM;AACpB,UAAM,KAAK,KAAK;AAAA,EAClB;AAEA,QAAM,QAAQ;AACd,SAAO;AACT;AAQO,SAAS,6BAA6BD,WAAyC;AACpF,QAAM,QAAQ,oBAAI,IAAyB;AAC3C,QAAM,MAAM,cAAcA,SAAQ,EAAE,IAAI,CAAC,UAAU,MAAM,EAAE,EAAE,KAAK;AAClE,aAAW,MAAM,KAAK;AACpB,eAAW,SAAS,yBAAyBA,WAAU,EAAE,GAAG;AAC1D,UAAI,CAAC,MAAM,IAAI,MAAM,EAAE,EAAG,OAAM,IAAI,MAAM,IAAI,KAAK;AAAA,IACrD;AAAA,EACF;AACA,SAAO,CAAC,GAAG,MAAM,OAAO,CAAC;AAC3B;AAGO,SAAS,sBAAsB,UAAkB,SAA0C;AAChG,QAAM,QAAQ,yCAAyC,KAAK,QAAQ;AACpE,MAAI,CAAC,MAAO,QAAO,CAAC;AAEpB,MAAI;AACJ,MAAI;AACF,kBAAcE,MAAK,KAAK,MAAM,CAAC,KAAK,IAAI,EAAE,QAAQC,aAAY,CAAC;AAAA,EACjE,SAAS,OAAO;AACd,UAAM,IAAI,SAAS;AAAA,MACjB;AAAA,MACA,SAAS,gBAAM,OAAO;AAAA,MACtB;AAAA,IACF,CAAC;AAAA,EACH;AACA,MAAI,CAAC,eAAe,OAAO,gBAAgB,YAAY,MAAM,QAAQ,WAAW,EAAG,QAAO,CAAC;AAC3F,SAAO;AACT;AAGO,SAAS,uBAAuB,UAAkB,SAA2B;AAClF,SAAO,sBAAsB,sBAAsB,UAAU,OAAO,EAAE,UAAU,OAAO;AACzF;AAGO,SAAS,kBAAkB,IAAkB;AAClD,MAAI,CAAC,4BAA4B,KAAK,EAAE,GAAG;AACzC,UAAM,QAAQ,iHAA4B,EAAE,GAAG;AAAA,EACjD;AACF;AAEA,SAAS,sBAAsB,KAAc,SAA2B;AACtE,MAAI,QAAQ,UAAa,QAAQ,KAAM,QAAO,CAAC;AAC/C,QAAM,SAAS,MAAM,QAAQ,GAAG,IAAI,MAAM,CAAC,GAAG;AAC9C,QAAM,aAAuB,CAAC;AAC9B,aAAW,SAAS,QAAQ;AAC1B,QAAI,OAAO,UAAU,YAAY,CAAC,MAAM,KAAK,GAAG;AAC9C,YAAM,QAAQ,gBAAM,OAAO,6EAA2B;AAAA,IACxD;AACA,UAAM,KAAK,MAAM,KAAK;AACtB,sBAAkB,EAAE;AACpB,QAAI,CAAC,WAAW,SAAS,EAAE,EAAG,YAAW,KAAK,EAAE;AAAA,EAClD;AACA,SAAO;AACT;AAEA,SAAS,QAAQ,SAA2B;AAC1C,SAAO,IAAI,SAAS,EAAE,iDAAqC,QAAQ,CAAC;AACtE;AAGO,SAAS,cAAc,QAAuB,OAA+B;AAClF,MAAI,CAAC,MAAO,QAAO;AACnB,SAAO,OAAO,OAAO,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,SAAS,KAAK,CAAC;AAC9D;AAGO,SAAS,YAAY,QAAuB,SAAiC;AAClF,MAAI,CAAC,QAAS,QAAO;AACrB,QAAM,IAAI,QAAQ,YAAY;AAC9B,SAAO,OAAO;AAAA,IAAO,CAAC,MACpB,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,KAAK,CAAC,MAAM,OAAO,KAAK,EAAE,EAAE,YAAY,EAAE,SAAS,CAAC,CAAC;AAAA,EAClG;AACF;;;AC7TA,eAAsB,iBACpB,MACA,QACiD;AACjD,QAAM,OAAO,MAAM,KAAK,IAAI,gCAAgC,mBAAmB,MAAM,CAAC,EAAE;AACxF,QAAM,UAAU,KAAK,MAAM,QAAQ,KAAK;AACxC,QAAM,QAAS,SAAS,SAAS,WAAW,CAAC;AAC7C,SAAO,EAAE,OAAO,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,GAAG,KAAK,QAAQ;AAClE;AAQA,eAAsB,mBACpB,MACA,QACA,OACkB;AAClB,QAAM,OAAO,MAAM,KAAK,MAAM,gCAAgC,mBAAmB,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC;AACrG,SAAO,KAAK,MAAM,QAAQ,KAAK;AACjC;AAEA,eAAsB,iBACpB,MACA,QACA,WACA,YACA,UACkB;AAClB,QAAM,MACJ,gCAAgC,mBAAmB,MAAM,CAAC,UAChD,mBAAmB,SAAS,CAAC,IAAI,mBAAmB,UAAU,CAAC;AAC3E,QAAM,OAAO,MAAM,KAAK,OAAO,KAAK;AAAA,IAClC,QAAQ,aAAa,SAAY,CAAC,IAAI,EAAE,SAAS;AAAA,EACnD,CAAC;AACD,SAAO,KAAK,MAAM,QAAQ,KAAK;AACjC;AAQO,SAAS,uBAAuB,KAA2B;AAChE,QAAM,KAAK,IAAI,QAAQ,GAAG;AAC1B,MAAI,MAAM,EAAG,OAAM,IAAI,MAAM,0EAAuC,GAAG,GAAG;AAC1E,QAAMC,QAAO,IAAI,MAAM,GAAG,EAAE,EAAE,KAAK;AACnC,QAAM,QAAQ,IAAI,MAAM,KAAK,CAAC;AAC9B,QAAM,MAAMA,MAAK,YAAY,GAAG;AAChC,MAAI,OAAO,EAAG,OAAM,IAAI,MAAM,oHAAuEA,KAAI,GAAG;AAC5G,SAAO,EAAE,WAAWA,MAAK,MAAM,GAAG,GAAG,GAAG,YAAYA,MAAK,MAAM,MAAM,CAAC,GAAG,MAAM;AACjF;","names":["fs","path","fs","yaml","yaml","fs","yaml","ExitCode","CliErrorCode","yaml","fs","fs","path","yaml","yaml","path","action","yaml","yaml","fs","os","path","yaml","resolve","randomUUID","randomUUID","resolve","fs","fs","path","manifest","buildMiniAppTemplateFiles","manifest","asRecord","action","path","yaml","JSON_SCHEMA","manifest","path","yaml","JSON_SCHEMA","path"]}
|
|
1
|
+
{"version":3,"sources":["../src/http/trace.ts","../src/http/client.ts","../src/http/interceptors.ts","../src/package-version.ts","../src/auth/token-store.ts","../src/config/paths.ts","../src/auth/principal.ts","../src/auth/password.ts","../src/auth/refresh.ts","../src/config/profile.ts","../src/config/global-config.ts","../src/config/env.ts","../src/errors/codes.ts","../src/errors/cli-error.ts","../src/config/identity.ts","../src/config/migration.ts","../src/config/resolve.ts","../src/models/query.ts","../src/models/action.ts","../src/meta/describe.ts","../src/models/create.ts","../src/models/update.ts","../src/models/remove.ts","../src/import/fields.ts","../src/import/placeholder.ts","../src/import/parse.ts","../src/import/runner.ts","../src/client.ts","../src/documents.ts","../src/output/json.ts","../src/output/yaml.ts","../src/output/table.ts","../src/chat/display-width.ts","../src/output/index.ts","../src/auth/client-credentials.ts","../src/auth/pat.ts","../src/auth/pairing.ts","../src/auth/change-password.ts","../src/auth/admin-reset-password.ts","../src/chat/ws-client.ts","../src/chat/stream-iterator.ts","../src/chat/control-channel.ts","../src/chat/delegation-constants.ts","../src/chat/v4-reducer.ts","../src/chat/one-shot.ts","../src/chat/conversation-state.ts","../src/chat/conversation.ts","../src/chat/resume-summary.ts","../src/chat/conversation-list.ts","../src/mini/types.ts","../src/mini/workspace-files.ts","../src/mini/project.ts","../src/mini/templates.ts","../src/cache.ts","../src/tenant.ts","../src/meta/tenant-meta.ts","../src/skills.ts","../src/settings.ts","../src/fde/layout.ts","../src/fde/fingerprint.ts","../src/fde/doctor.ts","../src/fde/probe.ts"],"sourcesContent":["import { randomBytes } from 'node:crypto';\n\nexport interface ParsedTraceparent {\n version: string;\n traceId: string;\n spanId: string;\n flags: string;\n}\n\nexport function newTraceparent(): string {\n const traceId = randomBytes(16).toString('hex');\n const spanId = randomBytes(8).toString('hex');\n return `00-${traceId}-${spanId}-01`;\n}\n\nexport function parseTraceparent(value: string): ParsedTraceparent | null {\n const m = /^([0-9a-f]{2})-([0-9a-f]{32})-([0-9a-f]{16})-([0-9a-f]{2})$/.exec(value);\n if (!m) return null;\n return { version: m[1]!, traceId: m[2]!, spanId: m[3]!, flags: m[4]! };\n}\n","import axios, { type AxiosInstance } from 'axios';\nimport { attachAuth, attachTrace, attachRetry } from './interceptors';\nimport { SDK_VERSION } from '../package-version';\n\nexport interface HttpClientOptions {\n endpoint: string;\n getAccessToken: () => Promise<string | null>;\n onRefresh: () => Promise<void>;\n timeoutMs?: number;\n userAgent?: string;\n}\n\nexport function createHttpClient(opts: HttpClientOptions): AxiosInstance {\n const client = axios.create({\n baseURL: opts.endpoint,\n timeout: opts.timeoutMs ?? 60_000,\n headers: { 'User-Agent': opts.userAgent ?? `hcmai-sdk/${SDK_VERSION}` },\n });\n attachTrace(client);\n attachAuth(client, opts.getAccessToken);\n attachRetry(client, opts.onRefresh);\n return client;\n}\n","import type { AxiosInstance, InternalAxiosRequestConfig } from 'axios';\nimport { newTraceparent } from './trace';\n\ndeclare module 'axios' {\n export interface AxiosRequestConfig {\n /**\n * 该请求**不参与** 401 自动 refresh + retry。\n *\n * 🔴 续期请求自己必须打上它:`attachRetry` 在 401 上调 `onRefresh()`,而\n * onRefresh 最终会发一个续期请求;那个请求的 config 是新的、没有 `__retried`\n * 标记,于是它的 401 又触发一次 onRefresh —— 每层发一个新请求,**无界递归**。\n * 见 `auth/refresh.ts` 的 slidePasswordSession。\n */\n __skipAuthRetry?: boolean;\n }\n}\n\nexport function attachTrace(client: AxiosInstance): void {\n client.interceptors.request.use((cfg: InternalAxiosRequestConfig) => {\n cfg.headers.set('traceparent', newTraceparent());\n return cfg;\n });\n}\n\nexport function attachAuth(client: AxiosInstance, getToken: () => Promise<string | null>): void {\n client.interceptors.request.use(async (cfg) => {\n if (!cfg.headers.has('Authorization')) {\n const tok = await getToken();\n if (tok) cfg.headers.set('Authorization', `Bearer ${tok}`);\n }\n return cfg;\n });\n}\n\nexport function attachRetry(client: AxiosInstance, onRefresh: () => Promise<void>): void {\n client.interceptors.response.use(\n (resp) => resp,\n async (error) => {\n const status = error.response?.status;\n const cfg = error.config as (InternalAxiosRequestConfig & { __retried?: boolean }) | undefined;\n if (!cfg) throw error;\n\n // 401 单次自动 refresh + retry。__skipAuthRetry 的请求(续期请求自己)直接放行,\n // 否则 401 → onRefresh → 续期请求 → 401 会无界递归。\n if (status === 401 && !cfg.__retried && !cfg.__skipAuthRetry) {\n cfg.__retried = true;\n try {\n await onRefresh();\n } catch {\n throw error;\n }\n // 关键:删除过期 Authorization,让 attachAuth 重新取刷新后的 token\n cfg.headers.delete('Authorization');\n return client.request(cfg);\n }\n\n // 5xx 指数退避 retry 2 次\n if (status && status >= 500 && status < 600) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- 在 config 上挂 retry 计数标记\n const attempts = (cfg as any).__attempts ?? 0;\n if (attempts < 2) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- 在 config 上挂 retry 计数标记\n (cfg as any).__attempts = attempts + 1;\n const delay = 500 * Math.pow(3, attempts);\n await new Promise((r) => setTimeout(r, delay));\n // 防御性:若 token 临过期,让 attachAuth 重新取\n cfg.headers.delete('Authorization');\n return client.request(cfg);\n }\n }\n throw error;\n },\n );\n}\n","import { readFileSync } from 'node:fs';\n\ninterface PackageManifest {\n version: string;\n}\n\nconst manifest = JSON.parse(\n readFileSync(new URL('../package.json', import.meta.url), 'utf8'),\n) as PackageManifest;\n\nexport const SDK_VERSION = manifest.version;\n","import * as fs from 'node:fs/promises';\nimport type * as Keytar from 'keytar';\nimport { credentialsFile, profileDir, identityDir, identityMetaFile } from '../config/paths';\n\n/**\n * 凭据的来路。\n *\n * 🔴 **来路 ≠ 语义**:`password` 与 `pairing` 是两条完全不同的取得方式,但换来的是\n * **同一种东西**——`TokenManager.createToken` 铸的服务端会话 token(滑动续期、\n * 可 `/api/auth/refresh`、可 `/api/auth/logout` 单杀)。而 `pat` 与 `client_credentials`\n * 不是。判「能不能续期 / 该不该请服务端吊销」必须走 {@link isServerSlidingSession},\n * 不要拿 `=== 'password'` 去比——见该函数的说明。\n */\nexport type AuthScheme = 'client_credentials' | 'pat' | 'password' | 'pairing';\n\n/**\n * 这条凭据是不是「服务端会话 token」——即后端在每个请求上滑动续期、且\n * `/api/auth/logout` 能单条吊销的那一类。\n *\n * 🔴 **存在的理由是防一类静默退化**:全仓有三处要问这个问题——`refreshToken` 的\n * scheme 分支、`ensureSessionFresh` 的早退、`logout` 的 `revokeOnServer`。散着写成三个\n * 字面量比较,下次加第五轨时必然漏掉其中一处,而漏掉的后果**没有任何报错**:\n * - 漏在 refresh 分支 → 落到 default 抛「unknown auth scheme」,会话再也续不上\n * - 漏在 ensureSessionFresh → 不再主动续期,退回「每天重登」\n * - 漏在 revokeOnServer → **`logout` 静默跳过服务端吊销**,而 ADR-308 把\n * 「用完跑 hcm logout」写成了顾问在客户主机上那一行**唯一可操作的缓解**\n *\n * 所以这里收敛成一个谓词,三处都调它。\n */\nexport function isServerSlidingSession(scheme: AuthScheme): boolean {\n return scheme === 'password' || scheme === 'pairing';\n}\n\nexport interface TokenRecord {\n scheme: AuthScheme;\n accessTokenRef: string; // \"keychain:<svc>/<acct>\" | \"file:<path>\" | \"plain:<token>\"\n refreshTokenRef?: string;\n expiresAt: string; // ISO 8601\n scopes: string[];\n /**\n * 签发时服务端告知的 TTL(秒)。**续期后本地 expiresAt 只能靠它重算**:后端\n * `POST /api/auth/refresh` 的响应体是 `{\"message\":\"token刷新成功\"}` —— 不回新的\n * 过期时间,而它重设的 Redis TTL 用的正是签发时那个全局配置值\n * (`SecurityProperties.token.expiration`,缺省 24h)。不存这个数,续期成功之后就只能\n * 猜一个 TTL 写回本地,猜小了照样早退、猜大了会放过一个已死的 token。\n *\n * 🔴 缺省(旧记录升级上来)时按 DEFAULT_SESSION_TTL_SECONDS 兜底,见 refresh.ts。\n */\n ttlSeconds?: number;\n}\n\nconst SERVICE_PREFIX = 'hcm-cli';\ntype KeytarModule = Pick<typeof Keytar, 'deletePassword' | 'getPassword' | 'setPassword'>;\n\ninterface TokenStoreInternal {\n __identity?: boolean;\n}\n\nexport class TokenStore {\n constructor(private profile: string) {}\n\n /**\n * Factory: build an identity-scoped TokenStore. Stores meta at\n * `identities/<name>/credentials.json` and uses keychain account prefix\n * `identity-<name>` to isolate from legacy profile-scoped stores.\n *\n * Used by T13 login when writing identity meta and by T15 business\n * commands when reading the active identity's token.\n */\n static forIdentity(name: string): TokenStore {\n const ts = new TokenStore(name);\n // Freeze the sentinel so Object.assign / shallow merges can't silently flip\n // an identity store back to profile mode. A subclass `IdentityTokenStore extends\n // TokenStore` would be type-safer, but the sentinel keeps the public API one-class.\n Object.defineProperty(ts, '__identity', { value: true, writable: false, configurable: false });\n return ts;\n }\n\n private get isIdentity(): boolean {\n // Strict === true: defends against accidental non-boolean assignment via cast hole.\n return (this as TokenStoreInternal).__identity === true;\n }\n\n private metaFile(): string {\n return this.isIdentity ? identityMetaFile(this.profile) : credentialsFile(this.profile);\n }\n\n private dataDir(): string {\n return this.isIdentity ? identityDir(this.profile) : profileDir(this.profile);\n }\n\n private keychainAccountPrefix(): string {\n return this.isIdentity ? `identity-${this.profile}` : this.profile;\n }\n\n async writeMeta(record: TokenRecord): Promise<void> {\n await fs.mkdir(this.dataDir(), { recursive: true });\n await fs.writeFile(this.metaFile(), JSON.stringify(record, null, 2), { mode: 0o600 });\n }\n\n async readMeta(): Promise<TokenRecord | null> {\n try {\n const raw = await fs.readFile(this.metaFile(), 'utf-8');\n return JSON.parse(raw) as TokenRecord;\n } catch (e) {\n if ((e as NodeJS.ErrnoException).code === 'ENOENT') return null;\n throw e;\n }\n }\n\n async setAccessToken(token: string): Promise<string> {\n if (await this.canUseKeychain()) {\n const keytar = await loadKeytar();\n if (!keytar) return this.writeTokenFile('access', token);\n const account = `${this.keychainAccountPrefix()}/access`;\n await keytar.setPassword(SERVICE_PREFIX, account, token);\n return `keychain:${SERVICE_PREFIX}/${account}`;\n }\n // fallback: 0600 文件\n return this.writeTokenFile('access', token);\n }\n\n async setRefreshToken(token: string): Promise<string> {\n if (await this.canUseKeychain()) {\n const keytar = await loadKeytar();\n if (!keytar) return this.writeTokenFile('refresh', token);\n const account = `${this.keychainAccountPrefix()}/refresh`;\n await keytar.setPassword(SERVICE_PREFIX, account, token);\n return `keychain:${SERVICE_PREFIX}/${account}`;\n }\n return this.writeTokenFile('refresh', token);\n }\n\n async getAccessToken(): Promise<string | null> {\n // 1. env override\n if (process.env.HCM_PAT) return process.env.HCM_PAT;\n if (process.env.HCM_ACCESS_TOKEN) return process.env.HCM_ACCESS_TOKEN;\n // 2. read meta + resolve ref\n const meta = await this.readMeta();\n if (!meta) return null;\n return this.resolveRef(meta.accessTokenRef);\n }\n\n async getRefreshToken(): Promise<string | null> {\n const meta = await this.readMeta();\n if (!meta?.refreshTokenRef) return null;\n return this.resolveRef(meta.refreshTokenRef);\n }\n\n async clear(): Promise<void> {\n const meta = await this.readMeta();\n if (!meta) return;\n // accessTokenRef may be absent on stub identities (created before first login)\n if (meta.accessTokenRef?.startsWith('keychain:')) {\n const account = meta.accessTokenRef\n .slice('keychain:'.length)\n .split('/')\n .slice(1)\n .join('/');\n const keytar = await loadKeytar();\n if (keytar) await keytar.deletePassword(SERVICE_PREFIX, account);\n } else if (meta.accessTokenRef?.startsWith('file:')) {\n await fs.rm(meta.accessTokenRef.slice('file:'.length), { force: true });\n }\n if (meta.refreshTokenRef?.startsWith('keychain:')) {\n const account = meta.refreshTokenRef\n .slice('keychain:'.length)\n .split('/')\n .slice(1)\n .join('/');\n const keytar = await loadKeytar();\n if (keytar) await keytar.deletePassword(SERVICE_PREFIX, account);\n } else if (meta.refreshTokenRef?.startsWith('file:')) {\n await fs.rm(meta.refreshTokenRef.slice('file:'.length), { force: true });\n }\n // Profile-scoped stores own the meta file (legacy logout) → delete it.\n // Identity-scoped stores share the meta file with deleteIdentity (T11\n // cascade ownership boundary) — leave it for deleteIdentity to clean up.\n if (!this.isIdentity) {\n await fs.rm(this.metaFile(), { force: true });\n }\n }\n\n private async resolveRef(ref: string): Promise<string | null> {\n if (ref.startsWith('keychain:')) {\n const account = ref.slice('keychain:'.length).split('/').slice(1).join('/');\n const keytar = await loadKeytar();\n if (!keytar) return null;\n return keytar.getPassword(SERVICE_PREFIX, account);\n }\n if (ref.startsWith('file:')) {\n try {\n return await fs.readFile(ref.slice('file:'.length), 'utf-8');\n } catch (e) {\n if ((e as NodeJS.ErrnoException).code === 'ENOENT') return null;\n throw e;\n }\n }\n if (ref.startsWith('plain:')) return ref.slice('plain:'.length);\n return null;\n }\n\n private async canUseKeychain(): Promise<boolean> {\n const keytar = await loadKeytar();\n if (!keytar) return false;\n try {\n // sanity probe\n await keytar.getPassword(SERVICE_PREFIX, '__probe__');\n return true;\n } catch {\n return false;\n }\n }\n\n private async writeTokenFile(kind: 'access' | 'refresh', token: string): Promise<string> {\n const file = `${this.metaFile()}.${kind}`;\n await fs.mkdir(this.dataDir(), { recursive: true });\n await fs.writeFile(file, token, { mode: 0o600 });\n if (kind === 'access') {\n // eslint-disable-next-line no-console\n console.warn(`[hcm] WARNING: OS keychain unavailable. Token saved to ${file} (0600).`);\n }\n return `file:${file}`;\n }\n}\n\nasync function loadKeytar(): Promise<KeytarModule | null> {\n try {\n const mod = (await import('keytar')) as unknown as KeytarModule & {\n default?: KeytarModule;\n };\n return mod.default ?? mod;\n } catch {\n return null;\n }\n}\n","import * as path from 'node:path';\nimport * as os from 'node:os';\n\nexport function hcmConfigDir(): string {\n const xdg = process.env.XDG_CONFIG_HOME;\n if (xdg) return path.join(xdg, 'hcm');\n const home = process.env.HOME ?? os.homedir();\n return path.join(home, '.config', 'hcm');\n}\n\nexport function profileDir(profile: string): string {\n return path.join(hcmConfigDir(), profile);\n}\n\nexport function profileFile(profile: string): string {\n return path.join(profileDir(profile), 'profile.yml');\n}\n\nexport function credentialsFile(profile: string): string {\n return path.join(profileDir(profile), 'credentials.json');\n}\n\nexport function globalConfigFile(): string {\n return path.join(hcmConfigDir(), 'config.yml');\n}\n\nexport function conversationStateFile(profile: string): string {\n return path.join(profileDir(profile), 'last-conversation.json');\n}\n\nexport function replHistoryFile(profile: string): string {\n return path.join(profileDir(profile), 'repl-history');\n}\n\nexport function envsDir(): string {\n return path.join(hcmConfigDir(), 'envs');\n}\n\nexport function envDir(name: string): string {\n return path.join(envsDir(), name);\n}\n\nexport function envFile(name: string): string {\n return path.join(envDir(name), 'env.yml');\n}\n\nexport function identitiesDir(): string {\n return path.join(hcmConfigDir(), 'identities');\n}\n\nexport function identityDir(name: string): string {\n return path.join(identitiesDir(), name);\n}\n\nexport function identityMetaFile(name: string): string {\n return path.join(identityDir(name), 'credentials.json');\n}\n\nexport function archiveDir(): string {\n return path.join(hcmConfigDir(), '_archive');\n}\n","import type { TokenRecord } from './token-store';\n\n/**\n * **服务端认定的 token 归属**——与\"调用方以为的归属\"是两回事,两者不一致时\n * 一切看起来都正常,而你在对着另一个租户干活。\n *\n * 这正是 fde-kit 那边 Ruling 30 防的同一件事:`hcm login --tenant 1` 传进去的是\n * 「我要的租户」,服务端回的 `user.tenantId` 才是「token 实际归属的租户」。此前\n * login 成功那行只回显前者,于是登错租户是静默的。\n */\nexport interface Principal {\n /** 服务端认定的租户。ADR-177 之后新租户是 UUID,别按 int 处理。 */\n tenantId?: string;\n username?: string;\n roles?: string[];\n}\n\n/** 登录helper 的统一返回:凭据元数据 + 服务端认定的归属(拿不到时 undefined)。 */\nexport interface LoginOutcome {\n record: TokenRecord;\n /**\n * 🔴 `undefined` = **服务端没在响应里给身份**,与「给了但字段为空」是两个状态。\n * 前者的下一步是问服务端为什么不给,后者的下一步是看那个账号本身。合并之后\n * 回显只能给出一句放之四海而皆准的废话。\n */\n principal?: Principal;\n}\n\n/**\n * 从服务端响应里的 user/userInfo 对象抽出 Principal。\n *\n * 两个来源字段名一致但形状不同,所以这里只认键、不认容器:\n * - `POST /api/auth/login` → `LoginResponse.user`(Map,含 id/username/tenantId/roles)\n * - `GET /api/auth/info` → `UserInfoResponse`(含 userId/username/tenantId/roles)\n *\n * 全部字段都当可选处理:任何一个缺失都不该让登录失败——登录已经成功了,\n * 这里只是回显。\n */\nexport function toPrincipal(raw: unknown): Principal | undefined {\n if (!raw || typeof raw !== 'object') return undefined;\n const o = raw as Record<string, unknown>;\n const principal: Principal = {};\n // tenantId 可能是 number(旧租户 id=1) 或 string(UUID),统一成 string 展示。\n if (o.tenantId !== undefined && o.tenantId !== null) principal.tenantId = String(o.tenantId);\n if (typeof o.username === 'string' && o.username) principal.username = o.username;\n if (Array.isArray(o.roles)) principal.roles = o.roles.map(String);\n return Object.keys(principal).length > 0 ? principal : undefined;\n}\n\n/** 回显用的一行摘要。`principal` 为空时明说「服务端未回身份」,不静默略过。 */\nexport function describePrincipal(principal: Principal | undefined): string {\n if (!principal) return 'server-identity=<未回身份>';\n const parts: string[] = [];\n parts.push(`tenant=${principal.tenantId ?? '<未回>'}`);\n parts.push(`user=${principal.username ?? '<未回>'}`);\n if (principal.roles?.length) parts.push(`roles=${principal.roles.join(',')}`);\n return parts.join(' ');\n}\n","import type { AxiosInstance } from 'axios';\nimport { TokenStore, type TokenRecord } from './token-store';\nimport { toPrincipal, type LoginOutcome } from './principal';\n\nexport interface PasswordLoginInput {\n endpoint: string;\n tenantId: string;\n username: string;\n password: string;\n profile: string;\n httpClient: AxiosInstance;\n}\n\n/** 后端 `SecurityProperties.token.expiration` 的缺省值(24h),仅在服务端没回 expiresIn 时兜底。 */\nexport const DEFAULT_SESSION_TTL_SECONDS = 24 * 60 * 60;\n\nexport async function loginPassword(input: PasswordLoginInput): Promise<LoginOutcome> {\n const resp = await input.httpClient.post(\n `${input.endpoint}/api/auth/login`,\n {\n tenantId: input.tenantId,\n username: input.username,\n password: input.password,\n },\n { headers: { 'Content-Type': 'application/json' } },\n );\n const { token, expiresIn, user } = resp.data;\n if (!token) {\n throw new Error('Login response missing token');\n }\n\n const ttlSeconds = expiresIn ?? DEFAULT_SESSION_TTL_SECONDS;\n const expiresAt = new Date(Date.now() + ttlSeconds * 1000).toISOString();\n\n const store = new TokenStore(input.profile);\n const accessTokenRef = await store.setAccessToken(token);\n\n const record: TokenRecord = {\n scheme: 'password',\n accessTokenRef,\n // 口令会话**没有** refresh token —— 后端 `/api/auth/login` 只回 token。\n // 它的续期语义是「同一个 token 被服务端重设 TTL」,见 refresh.ts。\n refreshTokenRef: undefined,\n expiresAt,\n scopes: ['*'],\n ttlSeconds,\n };\n await store.writeMeta(record);\n return { record, principal: toPrincipal(user) };\n}\n","import type { AxiosInstance } from 'axios';\nimport { TokenStore, isServerSlidingSession, type TokenRecord } from './token-store';\nimport { DEFAULT_SESSION_TTL_SECONDS } from './password';\n\nexport interface RefreshInput {\n endpoint: string;\n /**\n * Identity name (preferred). When set, uses TokenStore.forIdentity(identity).\n * Required for sessions established via HcmClient.fromAuthContext.\n */\n identity?: string;\n /**\n * @deprecated Profile name (legacy). Used when identity is not provided —\n * for backward compat with fromConfig callers and tests pre-T15.\n */\n profile?: string;\n httpClient: AxiosInstance;\n}\n\n/**\n * 本地在「还剩这么多」时就主动去续期。\n *\n * 🔴 这个值**不需要、也不应该**去对齐服务端的 `SecurityProperties.token.refreshThreshold`\n * (缺省 2h):那是服务端**自动**续期的触发线,而我们打的 `/api/auth/refresh` 是\n * **无条件**续期(只要 Redis key 还在就重设 TTL),跟阈值无关。抄一份服务端常量到这里\n * 只会多一个会漂的载体。\n */\nexport const PROACTIVE_REFRESH_MARGIN_SECONDS = 60 * 60;\n\n/**\n * 续期。**三种 scheme 是三套完全不同的服务端语义,不是一个端点能覆盖的。**\n *\n * 🔴 此前这里写成了一条,于是三条全错,而且错得不会被发现——口令会话在\n * 「取 refresh token」那一步就抛了,请求根本没发出去,后面那段对不上的协议\n * 因此永远跑不到,测试也只覆盖了这些提前抛出的错误路径。三条的真实形态:\n *\n * - **password**:`POST /api/auth/refresh`,认 **Authorization 头**,服务端把\n * 同一个 token 的 Redis TTL 重设回 expiration,响应体只有\n * `{\"message\":\"token刷新成功\"}`——**没有 access_token**。旧实现往 body 塞\n * `{refresh_token}` 再读 `resp.data.access_token`,两头都对不上;而口令登录\n * 压根拿不到 refresh token,连这个请求都发不出去。\n * - **client_credentials**:走 `POST /api/oauth/token` + `grant_type=refresh_token`,\n * 且后端在进 switch 之前就强制校验 `client_id` + `client_secret`。本 CLI\n * **不保存 client_secret**(有意为之:那是长期凭据) ⇒ 这条路在当前设计下\n * 不可能执行成功。所以这里**明说要重登**,而不是发一个注定 400 的请求\n * 再把它包装成一句看不懂的错误。\n * - **pat**:本来就不可续期,由后端管失效。\n */\nexport async function refreshToken(input: RefreshInput): Promise<TokenRecord> {\n if (!input.identity && !input.profile) {\n throw new Error('refreshToken: either identity or profile required');\n }\n const store = input.identity\n ? TokenStore.forIdentity(input.identity)\n : new TokenStore(input.profile!);\n const meta = await store.readMeta();\n if (!meta) throw new Error('no credentials to refresh');\n\n switch (meta.scheme) {\n case 'pat':\n throw new Error('PAT cannot be refreshed; re-run `hcm login --pat-stdin`');\n case 'client_credentials':\n throw new Error(\n 'client_credentials 会话无法自动续期:刷新需要 client_secret,而 CLI 不保存它。' +\n '重新执行 `hcm login --client-id <id>`',\n );\n case 'password':\n case 'pairing':\n // 配对码换来的就是 TokenManager 铸的普通会话 token,与口令登录**同一种东西**,\n // 续期语义完全一致(ADR-308)。\n return slidePasswordSession(store, meta, input);\n default: {\n // scheme 是联合类型,走到这里说明磁盘上的记录来自更新版本的 CLI。\n const unknown: never = meta.scheme;\n throw new Error(`unknown auth scheme in stored credentials: ${String(unknown)}`);\n }\n }\n}\n\n/**\n * 口令会话续期:打一次 `/api/auth/refresh`,成功后把本地过期时间往后推 ttlSeconds。\n *\n * 🔴 `__skipAuthRetry` 不是可选的防御性代码:`attachRetry` 在 401 上会调\n * `onRefresh()`,而 onRefresh 最终就走到这里。如果这个请求自己也 401(token 真的\n * 死了),它的 config 是**新的**、没有 `__retried` 标记,于是又触发一次 onRefresh —— \n * 每一层都发一个新请求,**无界递归**。此前这条路被提前抛出的错误挡着从没跑到过,\n * 一旦让它真发请求,递归就成立了。\n */\nasync function slidePasswordSession(\n store: TokenStore,\n meta: TokenRecord,\n input: RefreshInput,\n): Promise<TokenRecord> {\n const accessToken = await store.getAccessToken();\n if (!accessToken) {\n throw new Error('no access token to refresh; re-run `hcm login`');\n }\n\n await input.httpClient.post(`${input.endpoint}/api/auth/refresh`, undefined, {\n headers: { Authorization: `Bearer ${accessToken}` },\n // 见上:阻断 401 → onRefresh → 本函数 → 401 的递归。\n __skipAuthRetry: true,\n });\n\n // 服务端不回新的过期时间,重设的 TTL 就是签发时那个值。\n const ttlSeconds = meta.ttlSeconds ?? DEFAULT_SESSION_TTL_SECONDS;\n const record: TokenRecord = {\n ...meta,\n // token 本身没变,accessTokenRef / scopes 原样保留。\n expiresAt: new Date(Date.now() + ttlSeconds * 1000).toISOString(),\n ttlSeconds,\n };\n await store.writeMeta(record);\n return record;\n}\n\nexport function needsRefresh(meta: TokenRecord, marginSec = 60): boolean {\n const expiresAtMs = new Date(meta.expiresAt).getTime();\n return Date.now() + marginSec * 1000 > expiresAtMs;\n}\n\n/**\n * 命令开跑前把会话续上——**这才是\"登录一次基本不用再输密码\"落地的那一处**。\n *\n * 背景:后端 `TokenAuthenticationFilter` 在每个带 token 的请求上都会滑动续期\n * (剩余 <= refreshThreshold 时把 Redis TTL 重设回 24h),也就是说**只要你在用,\n * 服务端的会话就不会死**。而 CLI 这边的 `expiresAt` 是登录那一刻算死的静态值、\n * 之后再没更新过,`ensureActiveAuth` 拿它一比就抛 TOKEN_EXPIRED —— 结果是\n * **CLI 在拒绝服务端明明还认的 token**。\n *\n * 这里做的事:本地剩余时间进入窗口时主动打一次无条件续期端点,并把服务端那次\n * 续期的结果**写回本地**,让本地的过期判断重新变成真的。\n *\n * 返回是否真的续了。**任何失败都不抛**:续期是尽力而为,失败时后面的请求自己会\n * 401 并给出该给的引导;为了一次续期失败就让整条命令挂掉是本末倒置。\n */\nexport async function ensureSessionFresh(input: RefreshInput): Promise<boolean> {\n try {\n const store = input.identity\n ? TokenStore.forIdentity(input.identity)\n : new TokenStore(input.profile!);\n const meta = await store.readMeta();\n // 只有服务端会话 token 是「服务端滑动同一个 token」这套语义。PAT 由后端管失效,\n // client_credentials 拿不到 secret 续不了——两者在这里都该原样放过。\n // 🔴 走谓词而不是 `=== 'password'`:见 isServerSlidingSession 的说明。\n if (!meta || !isServerSlidingSession(meta.scheme)) return false;\n if (!needsRefresh(meta, PROACTIVE_REFRESH_MARGIN_SECONDS)) return false;\n await refreshToken(input);\n return true;\n } catch {\n return false;\n }\n}\n","import * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport yaml from 'js-yaml';\nimport { hcmConfigDir, profileDir, profileFile } from './paths';\n\nexport interface ProfileConfig {\n endpoint: string;\n tenantId: string;\n defaultAgent?: string;\n displayName?: string;\n}\n\nexport async function loadProfile(profile: string): Promise<ProfileConfig> {\n const file = profileFile(profile);\n let content: string;\n try {\n content = await fs.readFile(file, 'utf-8');\n } catch (e) {\n if ((e as NodeJS.ErrnoException).code === 'ENOENT') {\n throw new Error(`profile '${profile}' not found at ${file}`);\n }\n throw e;\n }\n return yaml.load(content) as ProfileConfig;\n}\n\nexport async function saveProfile(profile: string, cfg: ProfileConfig): Promise<void> {\n await fs.mkdir(profileDir(profile), { recursive: true });\n await fs.writeFile(profileFile(profile), yaml.dump(cfg), 'utf-8');\n}\n\nexport async function listProfiles(): Promise<string[]> {\n const root = hcmConfigDir();\n try {\n const entries = await fs.readdir(root, { withFileTypes: true });\n const result: string[] = [];\n for (const e of entries) {\n if (e.isDirectory()) {\n try {\n await fs.access(path.join(root, e.name, 'profile.yml'));\n result.push(e.name);\n } catch {\n /* skip */\n }\n }\n }\n return result;\n } catch (e) {\n if ((e as NodeJS.ErrnoException).code === 'ENOENT') return [];\n throw e;\n }\n}\n","import * as fs from 'node:fs/promises';\nimport yaml from 'js-yaml';\nimport { globalConfigFile, hcmConfigDir } from './paths';\n\nexport interface GlobalConfig {\n /** Canonical: name of the active environment. */\n activeEnv: string;\n /**\n * @deprecated Use `activeEnv`. Kept in sync with `activeEnv` for backward compat\n * while in-tree callers migrate. Will be removed at end of Phase 2.\n */\n activeProfile?: string;\n defaults?: {\n output?: 'table' | 'json' | 'yaml';\n timeout?: string;\n };\n}\n\ninterface RawShape {\n activeProfile?: string;\n activeEnv?: string;\n defaults?: GlobalConfig['defaults'];\n}\n\nconst DEFAULT_ENV = 'default';\nconst DEFAULT_DEFAULTS = { output: 'table' as const, timeout: '60s' };\n\nexport async function loadGlobalConfig(): Promise<GlobalConfig> {\n let raw: RawShape = {};\n try {\n const content = await fs.readFile(globalConfigFile(), 'utf-8');\n raw = (yaml.load(content) as RawShape) ?? {};\n } catch (e) {\n if ((e as NodeJS.ErrnoException).code !== 'ENOENT') throw e;\n }\n const activeEnv = raw.activeEnv ?? raw.activeProfile ?? DEFAULT_ENV;\n return {\n activeEnv,\n activeProfile: activeEnv, // dual-write alias\n defaults: { ...DEFAULT_DEFAULTS, ...raw.defaults },\n };\n}\n\nexport async function saveGlobalConfig(cfg: GlobalConfig): Promise<void> {\n await fs.mkdir(hcmConfigDir(), { recursive: true });\n // Canonical wins. Legacy callers that still mutate `activeProfile` must be\n // migrated to assign `activeEnv` instead — done in T5 for the 3 mutation\n // sites (config.ts × 2, login.ts × 1). Read-only callers stay safe via the\n // dual-write alias from `loadGlobalConfig`.\n const activeEnv = cfg.activeEnv ?? cfg.activeProfile ?? DEFAULT_ENV;\n const out = {\n activeEnv,\n activeProfile: activeEnv,\n defaults: cfg.defaults,\n };\n await fs.writeFile(globalConfigFile(), yaml.dump(out), 'utf-8');\n}\n\n/**\n * Resolve active env name. Priority: --env flag > HCM_ENV > HCM_PROFILE (legacy) > global.activeEnv\n */\nexport function resolveActiveEnv(\n args: { envFlag?: string },\n env: NodeJS.ProcessEnv,\n global: GlobalConfig,\n): string {\n return args.envFlag ?? env.HCM_ENV ?? env.HCM_PROFILE ?? global.activeEnv;\n}\n\n/**\n * @deprecated Use `resolveActiveEnv`. Keeps old signature `(args: {flag})` for backward compat.\n * Will be removed at end of Phase 2.\n */\nexport function resolveActiveProfile(\n args: { flag?: string },\n env: NodeJS.ProcessEnv,\n global: GlobalConfig,\n): string {\n return resolveActiveEnv({ envFlag: args.flag }, env, global);\n}\n","import * as fs from 'node:fs/promises';\nimport yaml from 'js-yaml';\nimport { envDir, envFile, envsDir } from './paths';\nimport { CliError, CliErrorCode } from '../errors';\n\nexport interface EnvConfig {\n endpoint: string;\n frontend?: string;\n tenantId?: string;\n defaultIdentity?: string;\n defaultAgent?: string;\n}\n\nexport async function saveEnv(name: string, cfg: EnvConfig): Promise<void> {\n if (!cfg.endpoint) {\n throw new CliError({\n code: CliErrorCode.ENV_ENDPOINT_REQUIRED,\n message: `env '${name}' requires endpoint`,\n });\n }\n await fs.mkdir(envDir(name), { recursive: true });\n await fs.writeFile(envFile(name), yaml.dump(cfg), 'utf-8');\n}\n\nexport async function loadEnv(name: string): Promise<EnvConfig> {\n try {\n const content = await fs.readFile(envFile(name), 'utf-8');\n return yaml.load(content) as EnvConfig;\n } catch (e) {\n if ((e as NodeJS.ErrnoException).code === 'ENOENT') {\n throw new CliError({\n code: CliErrorCode.ENV_NOT_FOUND,\n message: `env '${name}' not found. Run: hcm env list`,\n });\n }\n throw e;\n }\n}\n\nexport async function listEnvs(): Promise<string[]> {\n try {\n const entries = await fs.readdir(envsDir(), { withFileTypes: true });\n const result: string[] = [];\n for (const e of entries) {\n if (!e.isDirectory()) continue;\n try {\n await fs.access(envFile(e.name));\n result.push(e.name);\n } catch {\n /* skip */\n }\n }\n return result;\n } catch (e) {\n if ((e as NodeJS.ErrnoException).code === 'ENOENT') return [];\n throw e;\n }\n}\n\nexport async function deleteEnv(name: string): Promise<void> {\n try {\n await fs.access(envFile(name));\n } catch {\n throw new CliError({\n code: CliErrorCode.ENV_NOT_FOUND,\n message: `env '${name}' not found`,\n });\n }\n await fs.rm(envDir(name), { recursive: true, force: true });\n}\n","export enum ExitCode {\n SUCCESS = 0,\n USAGE_ERROR = 1,\n AUTH_ERROR = 2,\n BUSINESS_ERROR = 3,\n NETWORK_ERROR = 4,\n CONFIG_ERROR = 5,\n SIGINT = 130,\n}\n\nexport enum CliErrorCode {\n // usage (exit 1)\n INVALID_ARGUMENT = 'INVALID_ARGUMENT',\n INVALID_JSON = 'INVALID_JSON',\n MISSING_FLAG = 'MISSING_FLAG',\n\n // auth (exit 2)\n AUTH_FAILED = 'AUTH_FAILED',\n AUTH_EXPIRED = 'AUTH_EXPIRED',\n AUTH_REFRESH_FAILED = 'AUTH_REFRESH_FAILED',\n WS_AUTH_ERROR = 'WS_AUTH_ERROR',\n SCOPE_FORBIDDEN = 'SCOPE_FORBIDDEN',\n\n // business (exit 3)\n BUSINESS_RULE_VIOLATION = 'BUSINESS_RULE_VIOLATION',\n MODEL_NOT_FOUND = 'MODEL_NOT_FOUND',\n ACTION_NOT_FOUND = 'ACTION_NOT_FOUND',\n AGENT_NOT_FOUND = 'AGENT_NOT_FOUND',\n TOOL_TIMEOUT = 'TOOL_TIMEOUT',\n\n // network (exit 4)\n NETWORK_ERROR = 'NETWORK_ERROR',\n TIMEOUT = 'TIMEOUT',\n SERVER_ERROR = 'SERVER_ERROR',\n PROTOCOL_UNSUPPORTED = 'PROTOCOL_UNSUPPORTED',\n\n // config (exit 5)\n PROFILE_NOT_FOUND = 'PROFILE_NOT_FOUND',\n CONFIG_PARSE_ERROR = 'CONFIG_PARSE_ERROR',\n KEYCHAIN_UNAVAILABLE = 'KEYCHAIN_UNAVAILABLE',\n\n // env + identity (exit 5/2)\n ENV_NOT_FOUND = 'ENV_NOT_FOUND',\n ENV_ALREADY_EXISTS = 'ENV_ALREADY_EXISTS',\n ENV_ENDPOINT_REQUIRED = 'ENV_ENDPOINT_REQUIRED',\n IDENTITY_NOT_FOUND = 'IDENTITY_NOT_FOUND',\n IDENTITY_ALREADY_EXISTS = 'IDENTITY_ALREADY_EXISTS',\n IDENTITY_NOT_IN_ENV = 'IDENTITY_NOT_IN_ENV',\n TOKEN_EXPIRED = 'TOKEN_EXPIRED',\n NO_ACTIVE_ENV = 'NO_ACTIVE_ENV',\n\n // skills(exit 1/3/4)\n /** 同一个 id 在 CLI 自带与睿戎目录里同时存在。确定性拒绝,不静默取其一。 */\n SKILL_ID_CONFLICT = 'SKILL_ID_CONFLICT',\n /** 装在这份 CLI 里的技能资源读不到(dist/skills 缺失或损坏)。安装损坏,硬失败。 */\n SKILL_SOURCE_UNREACHABLE = 'SKILL_SOURCE_UNREACHABLE',\n /** `skills sync` 的锁文件此刻只记得下睿戎来源的出处;自带技能没有它要的三个字段。 */\n SYNC_SOURCE_UNSUPPORTED = 'SYNC_SOURCE_UNSUPPORTED',\n}\n\nconst CODE_TO_EXIT: Record<CliErrorCode, ExitCode> = {\n [CliErrorCode.INVALID_ARGUMENT]: ExitCode.USAGE_ERROR,\n [CliErrorCode.INVALID_JSON]: ExitCode.USAGE_ERROR,\n [CliErrorCode.MISSING_FLAG]: ExitCode.USAGE_ERROR,\n [CliErrorCode.AUTH_FAILED]: ExitCode.AUTH_ERROR,\n [CliErrorCode.AUTH_EXPIRED]: ExitCode.AUTH_ERROR,\n [CliErrorCode.AUTH_REFRESH_FAILED]: ExitCode.AUTH_ERROR,\n [CliErrorCode.WS_AUTH_ERROR]: ExitCode.AUTH_ERROR,\n [CliErrorCode.SCOPE_FORBIDDEN]: ExitCode.AUTH_ERROR,\n [CliErrorCode.BUSINESS_RULE_VIOLATION]: ExitCode.BUSINESS_ERROR,\n [CliErrorCode.MODEL_NOT_FOUND]: ExitCode.BUSINESS_ERROR,\n [CliErrorCode.ACTION_NOT_FOUND]: ExitCode.BUSINESS_ERROR,\n [CliErrorCode.AGENT_NOT_FOUND]: ExitCode.BUSINESS_ERROR,\n [CliErrorCode.TOOL_TIMEOUT]: ExitCode.BUSINESS_ERROR,\n [CliErrorCode.NETWORK_ERROR]: ExitCode.NETWORK_ERROR,\n [CliErrorCode.TIMEOUT]: ExitCode.NETWORK_ERROR,\n [CliErrorCode.SERVER_ERROR]: ExitCode.NETWORK_ERROR,\n [CliErrorCode.PROTOCOL_UNSUPPORTED]: ExitCode.NETWORK_ERROR,\n [CliErrorCode.PROFILE_NOT_FOUND]: ExitCode.CONFIG_ERROR,\n [CliErrorCode.CONFIG_PARSE_ERROR]: ExitCode.CONFIG_ERROR,\n [CliErrorCode.KEYCHAIN_UNAVAILABLE]: ExitCode.CONFIG_ERROR,\n [CliErrorCode.ENV_NOT_FOUND]: ExitCode.CONFIG_ERROR,\n [CliErrorCode.ENV_ALREADY_EXISTS]: ExitCode.CONFIG_ERROR,\n [CliErrorCode.ENV_ENDPOINT_REQUIRED]: ExitCode.USAGE_ERROR,\n [CliErrorCode.IDENTITY_NOT_FOUND]: ExitCode.CONFIG_ERROR,\n [CliErrorCode.IDENTITY_ALREADY_EXISTS]: ExitCode.CONFIG_ERROR,\n [CliErrorCode.IDENTITY_NOT_IN_ENV]: ExitCode.CONFIG_ERROR,\n [CliErrorCode.TOKEN_EXPIRED]: ExitCode.AUTH_ERROR,\n [CliErrorCode.NO_ACTIVE_ENV]: ExitCode.CONFIG_ERROR,\n [CliErrorCode.SKILL_ID_CONFLICT]: ExitCode.BUSINESS_ERROR,\n // 🔴 装坏了不是网络问题:报 network 会让顾问先去查 VPN。\n [CliErrorCode.SKILL_SOURCE_UNREACHABLE]: ExitCode.CONFIG_ERROR,\n [CliErrorCode.SYNC_SOURCE_UNSUPPORTED]: ExitCode.USAGE_ERROR,\n};\n\n/**\n * 🔴 **兜底不是防御性编程,是防「静默退 0」**。\n *\n * `CODE_TO_EXIT` 是 `Record<CliErrorCode, ExitCode>`,编译期漏一条会红,\n * 所以这一句在类型正确的构建里永远走不到。它防的是**构建不同步**:\n * CLI 的 dist 认识一个新错误码、而 sdk 的 dist 还是旧的时候,\n * 这里会取到 `undefined` ⇒ `process.exit(undefined)` ⇒ **报了错却退 0**。\n * 实测撞到过一次(只跑了 `npm run build -w @hcmai/cli`,没重建 sdk):\n * 终端上明明打印了 Error,脚本里 `$?` 是 0——上游调用方会当成成功。\n */\nexport function exitCodeFor(code: CliErrorCode): ExitCode {\n return CODE_TO_EXIT[code] ?? ExitCode.USAGE_ERROR;\n}\n","import { CliErrorCode, exitCodeFor } from './codes';\nimport type { ExitCode } from './codes';\n\nexport interface CliErrorInit {\n code: CliErrorCode;\n message: string;\n context?: Record<string, unknown>;\n traceId?: string;\n profile?: string;\n cause?: unknown;\n}\n\nexport class CliError extends Error {\n readonly code: CliErrorCode;\n readonly exitCode: ExitCode;\n readonly context?: Record<string, unknown>;\n readonly traceId?: string;\n readonly profile?: string;\n override readonly cause?: unknown;\n\n constructor(init: CliErrorInit) {\n super(init.message);\n this.name = 'CliError';\n this.code = init.code;\n this.exitCode = exitCodeFor(init.code);\n this.context = init.context;\n this.traceId = init.traceId;\n this.profile = init.profile;\n this.cause = init.cause;\n }\n\n toFormattedString(): string {\n const parts: string[] = [`${this.code}: ${this.message}`];\n // CliErrorCode 是命令行自己的分类(决定 exit code),后端错误码比它精确得多\n // (FILTER_SYNTAX_ERROR vs 一律降级成的 BUSINESS_RULE_VIOLATION)。两者不一致时都要显示,\n // 只显示前者就是在报一个错的错误码。\n const serverCode = this.context?.responseCode;\n if (typeof serverCode === 'string' && serverCode && serverCode !== this.code) {\n parts.push(`[服务端错误码: ${serverCode}]`);\n }\n const field = this.context?.field;\n if (typeof field === 'string' && field) parts.push(`[字段: ${field}]`);\n if (this.traceId) parts.push(`[traceId: ${this.traceId}]`);\n if (this.profile) parts.push(`[profile: ${this.profile}]`);\n let out = parts.join(' ');\n // 🔴 details 必须显示。后端把 BusinessException 的 description 放进 details\n // (CommonErrorResponse:data 为空时 details 就是 description),那句话往往是**唯一**\n // 说清「哪里错了、该改成什么」的文本,而 message 常常是错误码的静态措辞。\n // fromAxiosError 早就把它解析进 context 了,却一直没渲染出来——2026-08-29 顾问仿真\n // 实测:静态 JSON 数据源传了标量,服务端回的是「json 数据源 data 字段必须是 List 或\n // Map」,命令行只显示 BUSINESS_RULE_VIOLATION,Agent 无从自纠,整轮验收被迫中止。\n const details = this.context?.details;\n const detailText = formatDetails(details);\n if (detailText) out += `\\n 详情: ${detailText}`;\n const nextStep = this.context?.nextStep;\n if (typeof nextStep === 'string' && nextStep) out += `\\n 下一步: ${nextStep}`;\n return out;\n }\n\n toJSON() {\n return {\n code: this.code,\n message: this.message,\n context: this.context,\n traceId: this.traceId,\n };\n }\n}\n\n// axios doesn't export a clean error type usable here; intentional any\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function fromAxiosError(err: any, profile?: string): CliError {\n const status = err.response?.status;\n const traceId = err.response?.headers?.['x-trace-id'];\n\n // 🔴 HCM 的错误信封是嵌套的:{ \"error\": { code, message, field, details, nextStep, retryable } }\n // —— 见后端 CommonErrorResponse / ActionError。以前这里读的是顶层 data.code / data.message,\n // 对真实响应**恒取不到**:消息落回 axios 的 \"Request failed with status code 400\",\n // 错误码一律按 HTTP 状态码降级,连带下面 MODEL_NOT_FOUND / ACTION_NOT_FOUND 两条特判成了死代码。\n // 单测当时全绿,因为它们喂的是平铺形状 —— 围着 bug 建的 fixture 证明不了任何事。\n // 顶层留作回退:少数早期端点确实平铺返回。\n const data = err.response?.data;\n const body = (data?.error ?? data) as Record<string, unknown> | undefined;\n const respCode = body?.code as string | undefined;\n const respMsg = body?.message as string | undefined;\n const respField = body?.field as string | undefined;\n const respDetails = body?.details;\n const respNextStep = body?.nextStep as string | undefined;\n const respRetryable = body?.retryable as boolean | undefined;\n\n let code: CliErrorCode;\n if (status === 401) code = CliErrorCode.AUTH_FAILED;\n else if (status === 403) code = CliErrorCode.SCOPE_FORBIDDEN;\n else if (status === 404 && respCode === 'MODEL_NOT_FOUND') code = CliErrorCode.MODEL_NOT_FOUND;\n else if (status === 404 && respCode === 'ACTION_NOT_FOUND') code = CliErrorCode.ACTION_NOT_FOUND;\n else if (status && status >= 400 && status < 500) code = CliErrorCode.BUSINESS_RULE_VIOLATION;\n else if (status && status >= 500) code = CliErrorCode.SERVER_ERROR;\n else if (err.code === 'ECONNABORTED') code = CliErrorCode.TIMEOUT;\n else code = CliErrorCode.NETWORK_ERROR;\n\n return new CliError({\n code,\n message: respMsg ?? err.message ?? 'request failed',\n context: {\n status,\n responseCode: respCode,\n // 结构化错误的这几件必须原样带出:field 指哪个字段错了、details 机器可读、\n // nextStep 是后端给的路标(ADR-166 终局协议)—— 吞掉它等于让命令行用户自己猜下一步。\n ...(respField ? { field: respField } : {}),\n ...(respDetails !== undefined ? { details: respDetails } : {}),\n ...(respNextStep ? { nextStep: respNextStep } : {}),\n ...(respRetryable !== undefined ? { retryable: respRetryable } : {}),\n },\n traceId,\n profile,\n cause: err,\n });\n}\n\n\n/**\n * details 可能是字符串(后端 description 回退路径),也可能是结构化 payload。\n * 字符串原样显示;结构化的紧凑序列化,超长截断——宁可截断也不要整段不显示。\n */\nfunction formatDetails(details: unknown): string | undefined {\n if (details === undefined || details === null) return undefined;\n if (typeof details === 'string') return details.trim() || undefined;\n if (typeof details === 'number' || typeof details === 'boolean') return String(details);\n let text: string;\n try {\n text = JSON.stringify(details);\n } catch {\n return undefined;\n }\n if (!text || text === '{}' || text === '[]') return undefined;\n return text.length > 800 ? `${text.slice(0, 800)}…(已截断)` : text;\n}\n","import * as fs from 'node:fs/promises';\nimport { identityDir, identityMetaFile, identitiesDir, envFile } from './paths';\nimport { CliError, CliErrorCode } from '../errors';\nimport type { AuthScheme } from '../auth/token-store';\n\nexport interface IdentityMeta {\n env: string;\n scheme: AuthScheme;\n scopes: string[];\n expiresAt?: string;\n username?: string;\n /**\n * keychain ref same as TokenRecord.accessTokenRef. Populated by TokenStore.forIdentity.\n * Optional here because saveIdentity may be called before token is written.\n */\n accessTokenRef?: string;\n refreshTokenRef?: string;\n /**\n * **服务端认定的租户**(登录响应里的 `user.tenantId` / `/api/auth/info` 的 tenantId),\n * 不是命令行上传进去的那个。两者不一致 = 你在对着另一个租户干活,而一切看起来都正常。\n *\n * 命令执行时优先用它,`EnvConfig.tenantId` 只是登录前的入参与兜底 —— 后者缺省是\n * 写死的 `'1'`,而 ADR-177 之后新租户是 UUID,那个缺省几乎必然是错的。\n */\n tenantId?: string;\n /**\n * 见 `TokenRecord.ttlSeconds`。\n *\n * 🔴 必须在这里也留一份:`identities/<name>/credentials.json` 这**一个文件**有两个\n * 写入方——`TokenStore.forIdentity().writeMeta()` 写 TokenRecord 形状,`saveIdentity()`\n * 写 IdentityMeta 形状,后者最后写、且只写自己声明的字段。不在这里声明,\n * 登录流程末尾这一次 saveIdentity 就会把 ttlSeconds 抹掉,续期时只能去猜 TTL。\n */\n ttlSeconds?: number;\n}\n\nasync function assertEnvExists(env: string): Promise<void> {\n try {\n await fs.access(envFile(env));\n } catch {\n throw new CliError({\n code: CliErrorCode.IDENTITY_NOT_IN_ENV,\n message: `env '${env}' does not exist. Run: hcm env add ${env} --endpoint <url>`,\n });\n }\n}\n\nexport async function saveIdentity(name: string, meta: IdentityMeta): Promise<void> {\n await assertEnvExists(meta.env);\n await fs.mkdir(identityDir(name), { recursive: true });\n await fs.writeFile(identityMetaFile(name), JSON.stringify(meta, null, 2), { mode: 0o600 });\n}\n\nexport async function loadIdentity(name: string): Promise<IdentityMeta> {\n try {\n const raw = await fs.readFile(identityMetaFile(name), 'utf-8');\n return JSON.parse(raw) as IdentityMeta;\n } catch (e) {\n if ((e as NodeJS.ErrnoException).code === 'ENOENT') {\n throw new CliError({\n code: CliErrorCode.IDENTITY_NOT_FOUND,\n message: `identity '${name}' not found. Run: hcm identity list`,\n });\n }\n throw e;\n }\n}\n\nexport async function listIdentities(env?: string): Promise<string[]> {\n try {\n const entries = await fs.readdir(identitiesDir(), { withFileTypes: true });\n const result: string[] = [];\n for (const e of entries) {\n if (!e.isDirectory()) continue;\n try {\n const meta = await loadIdentity(e.name);\n if (!env || meta.env === env) result.push(e.name);\n } catch {\n /* skip broken */\n }\n }\n return result;\n } catch (e) {\n if ((e as NodeJS.ErrnoException).code === 'ENOENT') return [];\n throw e;\n }\n}\n\nexport async function deleteIdentity(name: string): Promise<void> {\n try {\n await fs.access(identityMetaFile(name));\n } catch {\n throw new CliError({\n code: CliErrorCode.IDENTITY_NOT_FOUND,\n message: `identity '${name}' not found`,\n });\n }\n await fs.rm(identityDir(name), { recursive: true, force: true });\n}\n","import * as fs from 'node:fs/promises';\nimport type { Dirent } from 'node:fs';\nimport * as path from 'node:path';\nimport yaml from 'js-yaml';\nimport {\n hcmConfigDir,\n profileDir,\n profileFile,\n credentialsFile,\n archiveDir,\n envFile,\n} from './paths';\nimport { saveEnv, deleteEnv } from './env';\nimport { saveIdentity, deleteIdentity } from './identity';\nimport { loadGlobalConfig, saveGlobalConfig } from './global-config';\n\ninterface LegacyProfile {\n endpoint: string;\n tenantId?: string;\n defaultAgent?: string;\n}\n\ninterface LegacyCredentials {\n scheme: 'password' | 'client_credentials' | 'pat';\n accessTokenRef: string;\n refreshTokenRef?: string;\n expiresAt: string;\n scopes: string[];\n username?: string;\n}\n\nexport interface MigrationSummary {\n migrated: string[];\n failed: string[];\n skipped: string[];\n}\n\nconst RESERVED = new Set(['envs', 'identities', '_archive']);\n\n/**\n * One-shot lazy migration from legacy `<hcm>/<profile>/profile.yml + credentials.json`\n * to canonical `<hcm>/envs/<name>/env.yml + identities/<name>/credentials.json`.\n *\n * Semantics:\n * - Idempotent: profiles already migrated (env.yml exists) are skipped.\n * - Per-profile atomicity: any failure inside migrateOne rolls back the partial env.\n * - Legacy files for failed profiles stay in place so the user can inspect / retry.\n * - On any migration success, the global config.yml is rewritten so activeEnv is\n * the canonical key (loadGlobalConfig already provides fallback read of\n * legacy activeProfile).\n * - Structured stderr log: `[hcm] event=migration_done count=N archive=PATH/`\n * and `[hcm] event=migration_failed profile=NAME reason=\"...\"`.\n */\nexport async function migrateLegacyProfiles(): Promise<MigrationSummary> {\n const summary: MigrationSummary = { migrated: [], failed: [], skipped: [] };\n const root = hcmConfigDir();\n\n let entries: Dirent[];\n try {\n entries = await fs.readdir(root, { withFileTypes: true });\n } catch (e) {\n if ((e as NodeJS.ErrnoException).code === 'ENOENT') return summary;\n throw e;\n }\n\n for (const e of entries) {\n if (!e.isDirectory() || RESERVED.has(e.name)) continue;\n const legacyYml = profileFile(e.name);\n try {\n await fs.access(legacyYml);\n } catch {\n continue; // not a legacy profile dir\n }\n\n // already migrated?\n try {\n await fs.access(envFile(e.name));\n summary.skipped.push(e.name);\n continue;\n } catch {\n /* not yet migrated */\n }\n\n let result: { identityName: string } | null = null;\n try {\n result = await migrateOne(e.name);\n summary.migrated.push(e.name);\n } catch (err) {\n summary.failed.push(e.name);\n // Rollback: env AND identity (identity may exist if archive step failed)\n try {\n await deleteEnv(e.name);\n } catch {\n /* env was not yet created — nothing to roll back */\n }\n if (result === null) {\n // migrateOne threw before returning identityName; try to clean up by\n // computed name using the same inference migrateOne would have used.\n try {\n const creds = JSON.parse(\n await fs.readFile(credentialsFile(e.name), 'utf-8'),\n ) as { username?: string };\n const guessedId = creds?.username ? `${creds.username}@${e.name}` : `default@${e.name}`;\n await deleteIdentity(guessedId);\n } catch {\n /* identity not created or creds unreadable — fine */\n }\n } else {\n try {\n await deleteIdentity(result.identityName);\n } catch {\n /* not created */\n }\n }\n process.stderr.write(\n `[hcm] event=migration_failed profile=${e.name} reason=${JSON.stringify(String((err as Error).message))}\\n`,\n );\n }\n }\n\n // upgrade global config.yml: activeProfile → activeEnv (canonical key)\n if (summary.migrated.length > 0) {\n const g = await loadGlobalConfig(); // 自动兼容读 activeProfile fallback\n await saveGlobalConfig({ activeEnv: g.activeEnv, defaults: g.defaults });\n\n const archive = archiveDir();\n process.stderr.write(\n `[hcm] event=migration_done count=${summary.migrated.length} archive=${archive}/\\n`,\n );\n }\n\n return summary;\n}\n\nasync function migrateOne(name: string): Promise<{ identityName: string }> {\n const legacyProfile = yaml.load(\n await fs.readFile(profileFile(name), 'utf-8'),\n ) as LegacyProfile | null;\n if (!legacyProfile?.endpoint) {\n throw new Error('legacy profile.yml missing endpoint');\n }\n\n // Read credentials.json eagerly so a corrupt file fails BEFORE we touch env.yml.\n // (This way the rollback branch is exercised cleanly and the legacy files stay\n // in place for inspection — matches the \"partial fail\" test contract.)\n let creds: LegacyCredentials | null = null;\n try {\n creds = JSON.parse(\n await fs.readFile(credentialsFile(name), 'utf-8'),\n ) as LegacyCredentials;\n } catch (e) {\n if ((e as NodeJS.ErrnoException).code !== 'ENOENT') {\n throw new Error(`credentials.json unreadable: ${(e as Error).message}`);\n }\n // ENOENT → no credentials, that's fine\n }\n\n const identityName = creds?.username ? `${creds.username}@${name}` : `default@${name}`;\n\n // 1. write env.yml (with defaultIdentity pointer)\n await saveEnv(name, {\n endpoint: legacyProfile.endpoint,\n tenantId: legacyProfile.tenantId,\n defaultAgent: legacyProfile.defaultAgent,\n defaultIdentity: identityName,\n });\n\n // 2. write identity meta\n // - if creds existed: full meta carried over\n // - if creds missing: stub identity (scheme=password, empty scopes) — env's\n // defaultIdentity points here, user must `hcm login` to populate the token\n if (creds) {\n await saveIdentity(identityName, {\n env: name,\n scheme: creds.scheme,\n scopes: creds.scopes,\n expiresAt: creds.expiresAt,\n username: creds.username,\n accessTokenRef: creds.accessTokenRef,\n refreshTokenRef: creds.refreshTokenRef,\n });\n } else {\n // Stub: no credentials yet — set expiresAt in the past so auth-guard treats\n // this as \"expired, force re-login\" rather than \"valid password scheme\".\n // T13 login overwrites this with real meta when user runs `hcm login --env <name>`.\n await saveIdentity(identityName, {\n env: name,\n scheme: 'password',\n scopes: [],\n expiresAt: '1970-01-01T00:00:00Z',\n });\n }\n\n // 3. archive legacy files (only after env + identity are persisted)\n const archiveTarget = path.join(archiveDir(), name);\n await fs.mkdir(archiveTarget, { recursive: true });\n await fs.rename(profileFile(name), path.join(archiveTarget, 'profile.yml.bak'));\n try {\n await fs.rename(\n credentialsFile(name),\n path.join(archiveTarget, 'credentials.json.bak'),\n );\n } catch (e) {\n if ((e as NodeJS.ErrnoException).code !== 'ENOENT') throw e;\n }\n\n // try to remove the now-empty profile dir (may still hold non-migrated artifacts\n // like last-conversation.json or repl-history — keep dir in that case)\n try {\n await fs.rmdir(profileDir(name));\n } catch {\n /* not empty — leave dir for non-migrated artifacts */\n }\n\n return { identityName };\n}\n","import { loadEnv } from './env';\nimport { listIdentities } from './identity';\nimport { CliError, CliErrorCode } from '../errors';\n\n/**\n * Resolve active identity name.\n * Priority: --as flag > HCM_IDENTITY env var > env.defaultIdentity > first identity in env > throw.\n */\nexport async function resolveActiveIdentity(\n args: { asFlag?: string },\n env: NodeJS.ProcessEnv,\n activeEnv: string,\n): Promise<string> {\n if (args.asFlag) return args.asFlag;\n if (env.HCM_IDENTITY) return env.HCM_IDENTITY;\n const cfg = await loadEnv(activeEnv);\n if (cfg.defaultIdentity) return cfg.defaultIdentity;\n const [first] = await listIdentities(activeEnv);\n if (!first) {\n throw new CliError({\n code: CliErrorCode.IDENTITY_NOT_FOUND,\n message: `env '${activeEnv}' has no identity. Run: hcm login --env ${activeEnv}`,\n });\n }\n return first;\n}\n\n// Re-export resolveActiveEnv from global-config for symmetry\nexport { resolveActiveEnv } from './global-config';\n","import type { AxiosInstance } from 'axios';\nimport type { ModelQueryDsl, QueryResult } from './types';\n\ninterface HcmPageResponse<T> {\n content: T[];\n totalElements?: number;\n totalPages?: number;\n size?: number;\n page?: number;\n}\n\nexport async function query<T = Record<string, unknown>>(\n http: AxiosInstance,\n model: string,\n dsl: ModelQueryDsl,\n): Promise<QueryResult<T>> {\n const body: Record<string, unknown> = {};\n const filter = normalizeFilter(dsl.filter);\n if (filter !== undefined) body.filter = filter;\n const orders = normalizeOrders(dsl.sort);\n if (orders !== undefined) body.orders = orders;\n if (dsl.fields !== undefined) body.fields = dsl.fields;\n if (dsl.keyword !== undefined) body.keyword = dsl.keyword;\n\n const size = dsl.limit ?? 20;\n const page = dsl.offset ? Math.floor(dsl.offset / size) : 0;\n body.page = page;\n body.size = size;\n\n const resp = await http.post<HcmPageResponse<T>>(`/api/models/${model}/list/dsl`, body);\n return {\n rows: resp.data.content ?? [],\n total: resp.data.totalElements,\n };\n}\n\nconst OPERATOR_ALIASES: Record<string, string> = {\n $eq: 'eq',\n eq: 'eq',\n '=': 'eq',\n '==': 'eq',\n $ne: 'ne',\n ne: 'ne',\n '!=': 'ne',\n '<>': 'ne',\n $gt: 'gt',\n gt: 'gt',\n '>': 'gt',\n $gte: 'gte',\n gte: 'gte',\n '>=': 'gte',\n $lt: 'lt',\n lt: 'lt',\n '<': 'lt',\n $lte: 'lte',\n lte: 'lte',\n '<=': 'lte',\n $in: 'in',\n in: 'in',\n $nin: 'not_in',\n nin: 'not_in',\n $not_in: 'not_in',\n not_in: 'not_in',\n $like: 'like',\n like: 'like',\n $not_like: 'not_like',\n not_like: 'not_like',\n $contains: 'contains',\n contains: 'contains',\n $startswith: 'startswith',\n startswith: 'startswith',\n $endswith: 'endswith',\n endswith: 'endswith',\n $between: 'between',\n between: 'between',\n $is_null: 'is_null',\n is_null: 'is_null',\n $is_not_null: 'is_not_null',\n is_not_null: 'is_not_null',\n};\n\nfunction normalizeFilter(filter: Record<string, unknown> | undefined): unknown {\n if (filter === undefined) return undefined;\n if (!isPlainObject(filter)) return filter;\n if (isAstFilter(filter)) return filter;\n\n const leaves = Object.entries(filter).flatMap(([field, value]) => filterLeaves(field, value));\n if (leaves.length === 0) return undefined;\n return leaves.length === 1 ? leaves[0] : { and: leaves };\n}\n\nfunction isAstFilter(filter: Record<string, unknown>): boolean {\n return (\n 'and' in filter ||\n 'or' in filter ||\n 'not' in filter ||\n (typeof filter.field === 'string' && typeof filter.op === 'string')\n );\n}\n\nfunction filterLeaves(field: string, value: unknown): Array<Record<string, unknown>> {\n if (isPlainObject(value)) {\n const entries = Object.entries(value);\n if (entries.length > 0 && entries.every(([key]) => isFilterOperator(key))) {\n return entries.map(([key, operatorValue]) => {\n const op = OPERATOR_ALIASES[key];\n if (!op) {\n throw new Error(`Unsupported filter operator \"${key}\" for field \"${field}\"`);\n }\n return isUnaryOperator(op)\n ? { field, op }\n : { field, op, value: operatorValue };\n });\n }\n if (entries.some(([key]) => isFilterOperator(key))) {\n throw new Error(`Unsupported mixed filter value for field \"${field}\"`);\n }\n }\n return [{ field, op: Array.isArray(value) ? 'in' : 'eq', value }];\n}\n\nfunction isFilterOperator(key: string): boolean {\n return Object.prototype.hasOwnProperty.call(OPERATOR_ALIASES, key);\n}\n\nfunction isUnaryOperator(op: string): boolean {\n return op === 'is_null' || op === 'is_not_null';\n}\n\nfunction normalizeOrders(sort: Array<Record<string, unknown>> | undefined): unknown {\n if (sort === undefined) return undefined;\n return sort.map((item) => {\n const field = item.field;\n if (typeof field !== 'string' || field.trim() === '') {\n throw new Error('sort item requires non-empty field');\n }\n const rawDirection =\n typeof item.direction === 'string'\n ? item.direction\n : typeof item.dir === 'string'\n ? item.dir\n : typeof item.order === 'string'\n ? item.order\n : item.desc === true\n ? 'desc'\n : 'asc';\n const direction = rawDirection.toLowerCase();\n if (direction !== 'asc' && direction !== 'desc') {\n throw new Error(`sort direction must be asc or desc; got \"${rawDirection}\"`);\n }\n return { field, direction };\n });\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n","import type { AxiosInstance } from 'axios';\nimport type { ActionHttpMethod, ActionInput, ActionResult, ActionScope } from './types';\n\nfunction normalizeMethod(method?: ActionHttpMethod): ActionHttpMethod {\n return method ?? 'POST';\n}\n\nfunction normalizeScope(input: ActionInput): ActionScope {\n return input.scope ?? (input.id ? 'object' : 'class');\n}\n\nfunction queryParams(input: ActionInput): Record<string, unknown> {\n return {\n ...(input.params ?? {}),\n ...(input.scope === 'class' && input.id ? { id: input.id } : {}),\n ...(input.ids?.length ? { ids: input.ids.join(',') } : {}),\n };\n}\n\nfunction classBody(input: ActionInput): Record<string, unknown> {\n return {\n ...(input.params ?? {}),\n ...(input.id ? { id: input.id } : {}),\n ...(input.ids?.length ? { ids: input.ids } : {}),\n };\n}\n\nfunction objectBody(input: ActionInput): Record<string, unknown> {\n return input.params ?? {};\n}\n\nexport async function action<R = unknown>(\n http: AxiosInstance,\n model: string,\n actionName: string,\n input: ActionInput,\n): Promise<ActionResult<R>> {\n const method = normalizeMethod(input.method);\n const scope = normalizeScope(input);\n if (scope === 'object' && !input.id) {\n throw new Error(`object action ${model}.${actionName} requires id`);\n }\n\n const path = scope === 'object'\n ? `/api/models/${model}/${input.id}/action/${actionName}`\n : `/api/models/${model}/action/${actionName}`;\n\n const resp = await (async () => {\n if (method === 'GET') {\n return http.get(path, { params: queryParams(input) });\n }\n if (method === 'DELETE') {\n return http.delete(path, { params: queryParams(input) });\n }\n if (method === 'PUT') {\n return http.put(path, scope === 'object' ? objectBody(input) : classBody(input));\n }\n return http.post(path, scope === 'object' ? objectBody(input) : classBody(input));\n })();\n return resp.data as ActionResult<R>;\n}\n","import type { AxiosInstance } from 'axios';\n\nexport interface FieldMeta {\n name: string;\n type: string;\n required?: boolean;\n label?: string;\n description?: string;\n enum?: string[];\n relation?: { model: string; field?: string };\n}\n\nexport interface ActionMeta {\n name: string;\n label?: string;\n description?: string;\n params?: Array<{ name: string; type: string; required?: boolean; description?: string }>;\n scope?: string;\n httpMethod?: string;\n readOnly?: boolean;\n paramTypes?: string[];\n paramNames?: string[];\n returnType?: string;\n source?: string;\n}\n\nexport interface RelationMeta {\n key: string;\n via: string;\n label?: string;\n}\n\nexport interface ModelDescription {\n model: string;\n labels: Record<string, string>;\n fields: FieldMeta[];\n actions: ActionMeta[];\n relations: RelationMeta[];\n}\n\nexport interface DescribeOpts {\n verbose?: boolean;\n}\n\ninterface HcmFieldRaw {\n key?: string;\n name?: string;\n type?: string;\n isRequired?: boolean;\n description?: string;\n enum?: string[];\n relation?: { model: string; field?: string };\n}\n\ninterface HcmActionRaw {\n key?: string;\n name?: string;\n label?: string;\n description?: string;\n params?: Array<{ name: string; type: string; required?: boolean; description?: string }>;\n parameters?: Array<{ name: string; type: string; required?: boolean; description?: string }>;\n scope?: string;\n httpMethod?: string;\n readOnly?: boolean;\n paramTypes?: string[];\n paramNames?: string[];\n returnType?: string;\n source?: string;\n}\n\nfunction normalizeActionParams(\n action: HcmActionRaw,\n): Array<{ name: string; type: string; required?: boolean; description?: string }> | undefined {\n if (action.parameters) return action.parameters;\n if (action.params) return action.params;\n if (!action.paramNames?.length && !action.paramTypes?.length) return undefined;\n const size = Math.max(action.paramNames?.length ?? 0, action.paramTypes?.length ?? 0);\n return Array.from({ length: size }, (_, index) => ({\n name: action.paramNames?.[index] ?? `arg${index}`,\n type: action.paramTypes?.[index] ?? 'unknown',\n }));\n}\n\nexport async function describe(\n http: AxiosInstance,\n model: string,\n _opts: DescribeOpts = {},\n): Promise<ModelDescription> {\n const metaResp = await http.get(`/api/models/${model}/meta`);\n const meta = metaResp.data;\n const fields: FieldMeta[] = (meta.fields ?? []).map((f: HcmFieldRaw) => ({\n name: f.key ?? f.name ?? '',\n label: f.name,\n type: f.type ?? 'unknown',\n required: f.isRequired,\n description: f.description,\n enum: f.enum,\n relation: f.relation,\n }));\n const actions: ActionMeta[] = (meta.actions ?? []).map((a: HcmActionRaw) => ({\n name: a.key ?? a.name ?? '',\n label: a.label,\n description: a.description,\n params: normalizeActionParams(a),\n scope: a.scope,\n httpMethod: a.httpMethod,\n readOnly: a.readOnly,\n paramTypes: a.paramTypes,\n paramNames: a.paramNames,\n returnType: a.returnType,\n source: a.source,\n }));\n return {\n model: meta.name ?? model,\n labels: meta.labels ?? {},\n fields,\n actions,\n relations: meta.relations ?? [],\n };\n}\n","import type { AxiosInstance } from 'axios';\n\nexport interface CreateResult {\n id: string;\n [key: string]: unknown;\n}\n\n/** 创建一条 Model 记录,走后端 Model 层完整校验。\n * 注:返回体形态(尤其 id 字段名)见 spec §12.3,实现期对真后端核实。 */\nexport async function create(\n http: AxiosInstance,\n model: string,\n data: Record<string, unknown>,\n): Promise<CreateResult> {\n const resp = await http.post(`/api/models/${model}`, data);\n return resp.data as CreateResult;\n}\n","import type { AxiosInstance } from 'axios';\nimport type { CreateResult } from './create';\n\nexport async function update(\n http: AxiosInstance,\n model: string,\n id: string,\n data: Record<string, unknown>,\n): Promise<CreateResult> {\n const resp = await http.put(`/api/models/${model}/${id}`, data);\n return resp.data as CreateResult;\n}\n","import type { AxiosInstance } from 'axios';\n\nexport interface RemoveResult {\n id: string;\n status: number;\n data?: unknown;\n}\n\nexport async function remove(http: AxiosInstance, model: string, id: string): Promise<RemoveResult> {\n const resp = await http.delete(`/api/models/${model}/${encodeURIComponent(id)}`);\n if (resp.data && typeof resp.data === 'object') {\n return resp.data as RemoveResult;\n }\n return { id, status: resp.status };\n}\n","\n/** snake_case → camelCase(单个 key)。 */\nexport function snakeToCamel(key: string): string {\n return key.replace(/_([a-z0-9])/g, (_m, c: string) => c.toUpperCase());\n}\n\n/** 转换 record 的**顶层** key:snake_case → camelCase。\n * 嵌套对象/数组的值(jsonb / i18n)原样透传,不转内部键名(对齐 schema §4)。 */\nexport function camelizeKeys(obj: Record<string, unknown>): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(obj)) {\n out[snakeToCamel(k)] = v;\n }\n return out;\n}\n","// 整字段值必须是 ${Model:key}(不嵌套、不拼接,对齐 schema §3)\nconst PLACEHOLDER_RE = /^\\$\\{([A-Za-z0-9_]+):([^}]+)\\}$/;\n\nexport class RefStore {\n private map = new Map<string, string>();\n\n private k(model: string, key: string): string {\n return `${model}:${key}`;\n }\n register(model: string, key: string, id: string): void {\n this.map.set(this.k(model, key), id);\n }\n get(model: string, key: string): string | undefined {\n return this.map.get(this.k(model, key));\n }\n snapshot(): Record<string, string> {\n return Object.fromEntries(this.map);\n }\n}\n\nexport interface ParsedPlaceholder {\n model: string;\n key: string;\n}\n\n/** 若 value 恰好是一个 ${Model:key} 占位符,返回其 model/key;否则 null。 */\nexport function parsePlaceholder(value: unknown): ParsedPlaceholder | null {\n if (typeof value !== 'string') return null;\n const m = PLACEHOLDER_RE.exec(value);\n if (!m) return null;\n const [, model, key] = m;\n if (model === undefined || key === undefined) return null;\n return { model, key };\n}\n\n/** 用 store 替换 record 顶层的 ${Model:key} 占位符。\n * 返回 { resolved, unresolved };unresolved 是 \"字段名=${Model:key}\" 列表,原值保留便于报错。 */\nexport function resolveRefs(\n record: Record<string, unknown>,\n store: RefStore,\n): { resolved: Record<string, unknown>; unresolved: string[] } {\n const resolved: Record<string, unknown> = {};\n const unresolved: string[] = [];\n for (const [field, value] of Object.entries(record)) {\n const ph = parsePlaceholder(value);\n if (!ph) {\n resolved[field] = value;\n continue;\n }\n const id = store.get(ph.model, ph.key);\n if (id === undefined) {\n unresolved.push(`${field}=\\${${ph.model}:${ph.key}}`);\n resolved[field] = value;\n } else {\n resolved[field] = id;\n }\n }\n return { resolved, unresolved };\n}\n","import yaml, { JSON_SCHEMA } from 'js-yaml';\nimport { CliError, CliErrorCode } from '../errors';\nimport type { Fixture } from './types';\n\nconst REQUIRED = ['model', 'key_field', 'count', 'items'] as const;\n\n/** 解析 + 校验单个 data fixture(对齐 spec §10 校验项 1–4)。 */\nexport function parseFixture(yamlText: string, sourcePath?: string): Fixture {\n const where = sourcePath ? ` (${sourcePath})` : '';\n const fail = (message: string): never => {\n throw new CliError({ code: CliErrorCode.INVALID_ARGUMENT, message: `import: ${message}${where}` });\n };\n\n // 注入数据视为不可信:用 JSON_SCHEMA(只认 JSON 可表达的类型,拒绝 !!js/!!python 等自定义 tag),\n // 比 js-yaml v4 默认 DEFAULT_SCHEMA 更严;也避免无引号日期被解析成 Date 对象。\n let parsed: unknown;\n try {\n parsed = yaml.load(yamlText, { schema: JSON_SCHEMA });\n } catch (e) {\n return fail(`YAML 解析失败: ${(e as Error).message}`);\n }\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n return fail('fixture 为空或不是 mapping');\n }\n const doc = parsed as Record<string, unknown>;\n for (const f of REQUIRED) {\n if (doc[f] === undefined) return fail(`fixture 缺必需字段 \"${f}\"`);\n }\n if (!Array.isArray(doc.items)) return fail('\"items\" 必须是 list');\n if (!Number.isInteger(doc.count)) return fail('\"count\" 必须是整数');\n if (doc.items.length !== doc.count) {\n return fail(`count mismatch: count=${doc.count} 但 items 有 ${doc.items.length}`);\n }\n\n const keyField = doc.key_field as string;\n const seen = new Set<string>();\n for (const item of doc.items as Array<Record<string, unknown>>) {\n const k = item[keyField];\n if (k === undefined || k === null || k === '') return fail(`item 缺 key_field \"${keyField}\"`);\n if (seen.has(String(k))) return fail(`duplicate key \"${k}\" in ${String(doc.model)}`);\n seen.add(String(k));\n }\n return { ...(doc as unknown as Fixture), sourcePath };\n}\n","import { camelizeKeys } from './fields';\nimport { RefStore, resolveRefs } from './placeholder';\nimport type { Fixture, ImportOptions, ImportResult, RowResult } from './types';\n\n/** runner 只依赖 client 的这三个方法,便于单测 stub。 */\nexport interface ImportClient {\n findIdByKey(model: string, keyField: string, keyValue: string): Promise<string | null>;\n create(model: string, data: Record<string, unknown>): Promise<{ id: string }>;\n update(model: string, id: string, data: Record<string, unknown>): Promise<unknown>;\n}\n\nexport async function runImport(\n client: ImportClient,\n fixtures: Fixture[],\n opts: ImportOptions = {},\n): Promise<ImportResult> {\n const store = new RefStore();\n const rows: RowResult[] = [];\n const tally = { created: 0, updated: 0, skipped: 0, failed: 0 };\n const result = (): ImportResult => ({ ...tally, rows, refs: store.snapshot() });\n\n for (const fx of fixtures) {\n for (const item of fx.items) {\n const key = String(item[fx.key_field]);\n const { resolved, unresolved } = resolveRefs(item, store);\n\n if (unresolved.length > 0) {\n rows.push({ model: fx.model, key, status: 'failed', error: `unresolved placeholder(s): ${unresolved.join(', ')}` });\n tally.failed++;\n if (opts.onError === 'stop') return result();\n continue;\n }\n\n if (opts.dryRun) {\n // 纯静态:不写后端;注册假 id 让后续跨文件占位符仍能解析\n store.register(fx.model, key, `dryrun-${fx.model}-${key}`);\n rows.push({ model: fx.model, key, status: 'skipped' });\n tally.skipped++;\n continue;\n }\n\n const payload = camelizeKeys(resolved);\n try {\n const existingId = await client.findIdByKey(fx.model, fx.key_field, key);\n if (existingId && !opts.updateOnly) {\n store.register(fx.model, key, existingId);\n rows.push({ model: fx.model, key, status: 'skipped', id: existingId });\n tally.skipped++;\n } else if (existingId && opts.updateOnly) {\n await client.update(fx.model, existingId, payload);\n store.register(fx.model, key, existingId);\n rows.push({ model: fx.model, key, status: 'updated', id: existingId });\n tally.updated++;\n } else {\n const created = await client.create(fx.model, payload);\n store.register(fx.model, key, created.id);\n rows.push({ model: fx.model, key, status: 'created', id: created.id });\n tally.created++;\n }\n } catch (e: unknown) {\n const err = e as { response?: { headers?: Record<string, string>; data?: { message?: string } }; message?: string };\n rows.push({\n model: fx.model, key, status: 'failed',\n error: err.response?.data?.message ?? err.message ?? 'write failed',\n traceId: err.response?.headers?.['x-trace-id'],\n });\n tally.failed++;\n if (opts.onError === 'stop') return result();\n }\n }\n }\n return result();\n}\n","import type { AxiosInstance } from 'axios';\nimport { createHttpClient } from './http';\nimport { TokenStore } from './auth/token-store';\nimport { refreshToken, needsRefresh } from './auth/refresh';\nimport { loadProfile, loadGlobalConfig, type ProfileConfig } from './config';\nimport { query as modelQuery } from './models/query';\nimport { action as modelAction } from './models/action';\nimport { describe as describeModel } from './meta/describe';\nimport type { ModelQueryDsl, QueryResult, ActionInput, ActionResult } from './models/types';\nimport type { ModelDescription, DescribeOpts } from './meta/describe';\nimport {\n create as modelCreate,\n update as modelUpdate,\n remove as modelRemove,\n type CreateResult,\n type RemoveResult,\n} from './models';\nimport { runImport, type ImportClient } from './import';\nimport type { Fixture, ImportOptions, ImportResult } from './import';\n\nexport interface HcmClientOpts {\n endpoint: string;\n tenantId: string;\n accessToken?: string; // 显式构造时传\n scheme?: 'client_credentials' | 'pat';\n profile?: string;\n timeoutMs?: number;\n}\n\n/**\n * Structural type representing the resolved auth context that\n * `HcmClient.fromAuthContext` consumes. Kept as a structural interface\n * (rather than importing `AuthContext` from `@hcmai/cli`) to avoid a circular\n * package dependency — the CLI's `AuthContext` is assignable to this shape.\n */\nexport interface ClientAuthContext {\n env: string;\n endpoint: string;\n tenantId: string;\n identity: string;\n}\n\nexport class HcmClient {\n readonly profile: string;\n readonly endpoint: string;\n readonly tenantId: string;\n private http: AxiosInstance;\n private tokenStore: TokenStore;\n private explicitToken?: string;\n\n constructor(opts: HcmClientOpts) {\n this.profile = opts.profile ?? 'default';\n this.endpoint = opts.endpoint;\n this.tenantId = opts.tenantId;\n this.tokenStore = new TokenStore(this.profile);\n this.explicitToken = opts.accessToken;\n this.http = createHttpClient({\n endpoint: opts.endpoint,\n timeoutMs: opts.timeoutMs,\n getAccessToken: () => this.getAccessToken(),\n onRefresh: () => this.handleRefresh(),\n });\n }\n\n /**\n * Build a client from a resolved env + identity context (T15).\n *\n * Uses `TokenStore.forIdentity(ctx.identity)` so the token is read from\n * the identity-scoped store (written by `hcm login --env <env> --name <id>`),\n * not the legacy profile-scoped credentials.json.\n *\n * The `profile` field is kept = `ctx.env` for downstream callers that still\n * read `client.profile` (e.g. workspace context dump). Phase 2 will rename\n * the field to `env`.\n *\n * Refresh path: fromAuthContext binds `this.identity` so handleRefresh\n * routes refresh through TokenStore.forIdentity — works for\n * client_credentials sessions (1h TTL) post-T8 migration.\n */\n /**\n * Identity name for refresh path (set by fromAuthContext). When set,\n * handleRefresh routes through TokenStore.forIdentity rather than the\n * profile-scoped legacy store — critical for client_credentials sessions\n * (~1h token TTL) on post-T8-migration installs.\n */\n private identity?: string;\n\n static fromAuthContext(ctx: ClientAuthContext): HcmClient {\n const c = new HcmClient({\n endpoint: ctx.endpoint,\n tenantId: ctx.tenantId,\n profile: ctx.env, // alias env→profile for downstream readers\n });\n (c as unknown as { tokenStore: TokenStore }).tokenStore = TokenStore.forIdentity(ctx.identity);\n (c as unknown as { identity: string }).identity = ctx.identity;\n return c;\n }\n\n /**\n * @deprecated Use `HcmClient.fromAuthContext(ctx)` together with the CLI\n * helper `getActiveAuthContextFromCli(args)`. WILL THROW on post-T8\n * installs where `~/.config/hcm/<profile>/profile.yml` was migrated to\n * `envs/<name>/env.yml` (loadProfile reads the legacy path only).\n * Kept only for dead-code paths (mini.ts) and pre-migration users.\n */\n static async fromConfig(args: { profile?: string }): Promise<HcmClient> {\n const global = await loadGlobalConfig();\n const profile = args.profile ?? process.env.HCM_PROFILE ?? global.activeEnv;\n const cfg: ProfileConfig = await loadProfile(profile);\n return new HcmClient({\n endpoint: cfg.endpoint,\n tenantId: cfg.tenantId,\n profile,\n });\n }\n\n query<T = Record<string, unknown>>(model: string, dsl: ModelQueryDsl): Promise<QueryResult<T>> {\n return modelQuery<T>(this.http, model, dsl);\n }\n\n action<R = unknown>(model: string, name: string, input: ActionInput): Promise<ActionResult<R>> {\n return modelAction<R>(this.http, model, name, input);\n }\n\n describe(model: string, opts?: DescribeOpts): Promise<ModelDescription> {\n return describeModel(this.http, model, opts);\n }\n\n create(model: string, data: Record<string, unknown>): Promise<CreateResult> {\n return modelCreate(this.http, model, data);\n }\n\n update(model: string, id: string, data: Record<string, unknown>): Promise<CreateResult> {\n return modelUpdate(this.http, model, id, data);\n }\n\n remove(model: string, id: string): Promise<RemoveResult> {\n return modelRemove(this.http, model, id);\n }\n\n /** 按业务键查已存在记录的 id(幂等用);命中返回 id,否则 null。 */\n async findIdByKey(model: string, keyField: string, keyValue: string): Promise<string | null> {\n const res = await this.query(model, { filter: { [keyField]: keyValue }, limit: 1 });\n const row = res.rows[0] as { id?: string } | undefined;\n return row?.id ?? null;\n }\n\n importPackage(fixtures: Fixture[], opts?: ImportOptions): Promise<ImportResult> {\n return runImport(this as ImportClient, fixtures, opts);\n }\n\n raw(): AxiosInstance {\n return this.http;\n }\n\n async close(): Promise<void> {\n /* nothing for REST; chat module will override */\n }\n\n async getAccessToken(): Promise<string | null> {\n if (this.explicitToken) return this.explicitToken;\n return this.tokenStore.getAccessToken();\n }\n\n async handleRefresh(): Promise<void> {\n if (this.explicitToken) {\n // 显式 token 模式不能 refresh\n throw new Error('explicit token expired; pass a new accessToken');\n }\n const meta = await this.tokenStore.readMeta();\n if (!meta) throw new Error('no credentials');\n if (meta.scheme === 'pat') throw new Error('PAT cannot refresh; re-run login');\n if (needsRefresh(meta, 5)) {\n // Route refresh through identity-scoped store when bound via\n // fromAuthContext (T15 follow-up #3 — fixes client_credentials sessions\n // breaking ~1h in post-T8 installs).\n const refreshArgs = this.identity\n ? { endpoint: this.endpoint, identity: this.identity, httpClient: this.http }\n : { endpoint: this.endpoint, profile: this.profile, httpClient: this.http };\n await refreshToken(refreshArgs);\n }\n }\n}\n","import * as fs from 'node:fs';\nimport * as fsp from 'node:fs/promises';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\nimport type { AxiosInstance } from 'axios';\n\n/**\n * 会话附件元数据——与后端 `ConversationWebSocketHandler.parseAttachments` 形态一致\n * (message.create payload.attachments 元素;#1849 后随 USER_MESSAGE 入 journal 单源)。\n */\nexport interface AttachmentMeta {\n documentId: string;\n fileName: string;\n mimeType: string;\n fileSize: number;\n}\n\nconst MIME_BY_EXT: Record<string, string> = {\n '.pdf': 'application/pdf',\n '.docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',\n '.doc': 'application/msword',\n '.xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',\n '.csv': 'text/csv',\n '.txt': 'text/plain',\n '.md': 'text/markdown',\n '.png': 'image/png',\n '.jpg': 'image/jpeg',\n '.jpeg': 'image/jpeg',\n '.gif': 'image/gif',\n '.webp': 'image/webp',\n};\n\nexport function guessMimeType(filePath: string): string {\n return MIME_BY_EXT[path.extname(filePath).toLowerCase()] ?? 'application/octet-stream';\n}\n\n/**\n * 聊天附件的存储分类 —— `hcm chat` 走这一类,与 web ChatInput 同端点同分类。\n *\n * <p>⚠️ 它**不是**通用默认值:`storageCategory` 是服务端的一份**数据**(可配置的\n * 分类表),不是枚举常量。分类不存在时服务端返回 404 `category not found`。\n */\nexport const CHAT_ATTACHMENT_CATEGORY = 'ai-conversation';\n\n/**\n * 上传本地文件到 HCM 文档存储,返回可直接携带的附件元数据。\n * 类型限制跟随服务端校验。\n *\n * @param storageCategory 存储分类。**必须是目标环境里真实存在的分类**——\n * 它是服务端数据而非枚举,不同环境的分类表可以不同。\n * 聊天附件传 {@link CHAT_ATTACHMENT_CATEGORY};报表模板一类传 `flex-report`。\n *\n * <p>🔴 此前这里硬编码 `'ai-conversation'`,于是 `hcm upload <任意文件>` 在\n * 没有该分类的环境上**一律失败**,而错误被包成\n * `BUSINESS_RULE_VIOLATION: Request failed with status code 404`——看不出真因是\n * 「分类不存在」。2026-08-26 按交付技能跑弹性报表端到端时,Step 4「上传模板」\n * 卡在这里;模板是报表的必需件,这条不通整条链就断了。\n */\nexport async function uploadDocument(\n http: AxiosInstance,\n filePath: string,\n storageCategory: string = CHAT_ATTACHMENT_CATEGORY,\n): Promise<AttachmentMeta> {\n const abs = expandHome(filePath);\n const buf = await fsp.readFile(abs);\n const fileName = path.basename(abs);\n const form = new FormData();\n form.append('file', new Blob([buf], { type: guessMimeType(abs) }), fileName);\n form.append('storageCategory', storageCategory);\n form.append('allowEmptyFormId', 'true');\n const resp = await http.post('/api/documents/upload', form);\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const meta: any = resp.data?.data ?? resp.data;\n return {\n documentId: String(meta.documentId ?? meta.id),\n fileName: String(meta.fileName ?? fileName),\n mimeType: String(meta.contentType ?? meta.mimeType ?? guessMimeType(abs)),\n fileSize: Number(meta.fileSize ?? meta.size ?? buf.length),\n };\n}\n\n/** 默认产物落盘目录(对标 Claude Code 工具产物本地直接访问)。 */\nexport function defaultDownloadDir(): string {\n return path.join(os.homedir(), 'Downloads', 'hcm');\n}\n\n/**\n * 把后端相对下载链接(`/api/documents/download/{id}?...sig=`)拼成可点的绝对 URL,\n * 供终端渲染——签名 URL 自洽免 Bearer,点开即由浏览器打开(ADR-140)。\n * apiBase 为 CLI 连接的后端地址(`HcmClient.endpoint`);无 apiBase 或已是绝对 URL 时原样返回。\n */\nexport function absoluteDownloadUrl(apiBase: string | undefined, url: string): string {\n if (!apiBase || !url.startsWith('/')) return url;\n return apiBase.replace(/\\/+$/, '') + url;\n}\n\n/**\n * 认证下载文档到本地(`/api/documents/download` 需 Bearer,浏览器裸 GET 恒 401 的同款教训)。\n * destPath 为目录或省略时落 defaultDownloadDir,重名自动加序号。返回写入的绝对路径。\n */\nexport async function downloadDocument(\n http: AxiosInstance,\n ref: { documentId?: string; downloadUrl?: string; fileName?: string },\n destPath?: string,\n): Promise<string> {\n const url = ref.downloadUrl ?? `/api/documents/download/${ref.documentId}`;\n const resp = await http.get(url, { responseType: 'arraybuffer' });\n const headerName = parseFilenameFromDisposition(resp.headers?.['content-disposition']);\n // 🔴 路径穿越收口:fileName 来自服务端(content-disposition)/ 工具 result(LLM 可控),\n // safeBasename 剥掉所有目录段——杜绝 \"../../../.ssh/authorized_keys\" 类静默任意文件写\n // (auto-download 默认开,恶意工具 result 否则可写任意路径)。\n const rawName = ref.fileName ?? headerName ?? `${ref.documentId ?? 'download'}.bin`;\n\n // 目标目录与文件名解析:destPath 为目录/省略 → 落该目录 + 服务端 fileName;\n // destPath 指向文件 → 落其父目录 + destPath 的 basename(用户显式命名)。\n let dir: string;\n let nameSource: string;\n const expanded = destPath ? expandHome(destPath) : undefined;\n if (expanded && (expanded.endsWith(path.sep) || isExistingDir(expanded))) {\n dir = expanded;\n nameSource = rawName;\n } else if (expanded) {\n dir = path.dirname(expanded);\n nameSource = path.basename(expanded);\n } else {\n dir = defaultDownloadDir();\n nameSource = rawName;\n }\n const finalName = safeBasename(nameSource, ref.documentId);\n let target = path.join(dir, finalName);\n // 纵深防御:归属断言——target 必须在 dir 内(safeBasename 后理应恒真,留作回潮保险)。\n const resolvedDir = path.resolve(dir);\n if (path.resolve(target) !== path.join(resolvedDir, finalName)) {\n throw new Error(`下载路径越界:${target} 不在 ${resolvedDir} 内`);\n }\n await fsp.mkdir(dir, { recursive: true });\n target = dedupePath(target);\n await fsp.writeFile(target, Buffer.from(resp.data as ArrayBuffer));\n return target;\n}\n\nfunction expandHome(p: string): string {\n return p.startsWith('~') ? path.join(os.homedir(), p.slice(1)) : path.resolve(p);\n}\n\n/**\n * 文件名净化:剥所有目录段(path.basename 处理 / 与 \\\\)+ 拒 .. / 空 / 纯点。\n * 退化时回落 documentId 或 'download',绝不返回可逃出目标目录的串。\n */\nfunction safeBasename(rawName: string, documentId?: string): string {\n // 先归一反斜杠(防 Windows 风格 ..\\\\ 在 posix path.basename 下漏网)。\n const base = path.basename(rawName.replace(/\\\\/g, '/'));\n if (!base || base === '.' || base === '..' || base.includes('/')) {\n return documentId ? `${documentId}.bin` : 'download.bin';\n }\n return base;\n}\n\nfunction isExistingDir(p: string): boolean {\n try {\n return fs.statSync(p).isDirectory();\n } catch {\n return false;\n }\n}\n\n/** 重名文件加序号:report.xlsx → report (1).xlsx。 */\nfunction dedupePath(target: string): string {\n if (!fs.existsSync(target)) return target;\n const dir = path.dirname(target);\n const ext = path.extname(target);\n const base = path.basename(target, ext);\n for (let i = 1; ; i++) {\n const candidate = path.join(dir, `${base} (${i})${ext}`);\n if (!fs.existsSync(candidate)) return candidate;\n }\n}\n\nfunction parseFilenameFromDisposition(disposition?: string): string | undefined {\n if (!disposition) return undefined;\n const star = /filename\\*=UTF-8''([^;]+)/i.exec(disposition);\n if (star?.[1]) {\n try {\n return decodeURIComponent(star[1]);\n } catch {\n /* fallthrough */\n }\n }\n const plain = /filename=\"?([^\";]+)\"?/i.exec(disposition);\n return plain?.[1];\n}\n","export function toJson(value: unknown): string {\n return JSON.stringify(value, null, 2);\n}\n","import yaml from 'js-yaml';\n\nexport function toYaml(value: unknown): string {\n return yaml.dump(value, { indent: 2, lineWidth: -1 });\n}\n","import Table from 'cli-table3';\nimport { displayWidth, truncateDisplay } from '../chat/display-width';\n\nconst MAX_COL_WIDTH = 40;\n\n/**\n * 盒式文本表格。列宽**按内容自适应**、以 {@link MAX_COL_WIDTH} 为上限——短列(SEQ/ROUND\n * 等)不再被强制撑到 40 列(旧 `colWidths.map(()=>40)` 的 BUG:7 列表 ≈ 290 列宽、糊成一片)。\n * 宽度与截断均按**终端显示宽度**(CJK=2 列)计,避免中文单元格溢出错位。\n */\nexport function toTable(rows: Record<string, unknown>[]): string {\n if (rows.length === 0) return '(no results)';\n const cols = Object.keys(rows[0]!);\n const heads = cols.map((c) => c.toUpperCase());\n const cells = rows.map((r) => cols.map((c) => formatCell(r[c])));\n\n // 每列内容宽 = min(上限, max(表头, 各单元格显示宽))。\n const contentWidths = cols.map((_, i) =>\n Math.min(\n MAX_COL_WIDTH,\n Math.max(displayWidth(heads[i]!), ...cells.map((row) => displayWidth(row[i]!))),\n ),\n );\n\n const table = new Table({\n head: heads,\n style: { head: ['cyan'] },\n // +2:cli-table3 的 colWidths 含左右各 1 列 padding。\n colWidths: contentWidths.map((w) => w + 2),\n wordWrap: true,\n });\n for (const row of cells) table.push(row);\n return table.toString();\n}\n\nfunction formatCell(v: unknown): string {\n if (v === null || v === undefined) return '';\n const s = typeof v === 'object' ? JSON.stringify(v) : String(v);\n return truncateDisplay(s, MAX_COL_WIDTH);\n}\n","/**\n * 终端显示宽度(east-asian-width 近似,零依赖):CJK 表意/全角标点/假名/谚文按 2 列,\n * 其余按 1 列。argsPreview 等终端单行截断必须按显示宽度——按字符数截 120 的中文实际\n * 占 240 列必折行(2026-06-11 实测工具行折行根因)。\n */\nfunction charDisplayWidth(cp: number): number {\n return (cp >= 0x1100 && cp <= 0x115f) || // Hangul Jamo\n (cp >= 0x2e80 && cp <= 0xa4cf) || // CJK 部首/注音/汉字/假名\n (cp >= 0xac00 && cp <= 0xd7a3) || // Hangul 音节\n (cp >= 0xf900 && cp <= 0xfaff) || // CJK 兼容表意\n (cp >= 0xfe30 && cp <= 0xfe4f) || // CJK 兼容形式\n (cp >= 0xff00 && cp <= 0xff60) || // 全角 ASCII/标点\n (cp >= 0xffe0 && cp <= 0xffe6) ||\n (cp >= 0x20000 && cp <= 0x3fffd) // CJK 扩展 B+\n ? 2\n : 1;\n}\n\n/** 字符串的终端显示宽度(列数)。 */\nexport function displayWidth(s: string): number {\n let w = 0;\n for (const ch of s) w += charDisplayWidth(ch.codePointAt(0) as number);\n return w;\n}\n\n/** 按显示宽度截断,超宽时截到 maxWidth-1 列并补「…」;未超宽原样返回。 */\nexport function truncateDisplay(s: string, maxWidth: number): string {\n if (displayWidth(s) <= maxWidth) return s;\n let w = 0;\n let out = '';\n for (const ch of s) {\n const cw = charDisplayWidth(ch.codePointAt(0) as number);\n if (w + cw > maxWidth - 1) break;\n w += cw;\n out += ch;\n }\n return out + '…';\n}\n","import { toJson } from './json';\nimport { toYaml } from './yaml';\nimport { toTable } from './table';\n\nexport type OutputFormat = 'table' | 'json' | 'yaml';\n\nexport interface FormatOptions {\n format: OutputFormat;\n}\n\nexport function formatRows(rows: Record<string, unknown>[], opts: FormatOptions): string {\n switch (opts.format) {\n case 'json':\n return toJson(rows);\n case 'yaml':\n return toYaml(rows);\n case 'table':\n return toTable(rows);\n }\n}\n\nexport function formatObject(obj: unknown, opts: FormatOptions): string {\n switch (opts.format) {\n case 'json':\n return toJson(obj);\n case 'yaml':\n return toYaml(obj);\n case 'table':\n // 单对象 fallback 到 json\n return toJson(obj);\n }\n}\n\nexport { toJson, toYaml, toTable };\n","import type { AxiosInstance } from 'axios';\nimport { TokenStore, type TokenRecord } from './token-store';\nimport { type LoginOutcome } from './principal';\n\nexport interface ClientCredentialsInput {\n endpoint: string;\n tenantId: string;\n clientId: string;\n clientSecret: string;\n profile: string;\n httpClient: AxiosInstance;\n}\n\nexport async function loginClientCredentials(\n input: ClientCredentialsInput,\n): Promise<LoginOutcome> {\n const resp = await input.httpClient.post(\n `${input.endpoint}/api/oauth/token`,\n {\n grant_type: 'client_credentials',\n client_id: input.clientId,\n client_secret: input.clientSecret,\n },\n { headers: { 'Content-Type': 'application/json' } },\n );\n const { access_token, refresh_token, expires_in, scope } = resp.data;\n if (!access_token) {\n throw new Error('OAuth response missing access_token');\n }\n\n const ttlSeconds = expires_in ?? 3600;\n const expiresAt = new Date(Date.now() + ttlSeconds * 1000).toISOString();\n const scopes = (scope ?? '').split(/\\s+/).filter(Boolean);\n\n const store = new TokenStore(input.profile);\n const accessTokenRef = await store.setAccessToken(access_token);\n const refreshTokenRef = refresh_token ? await store.setRefreshToken(refresh_token) : undefined;\n\n const record: TokenRecord = {\n scheme: 'client_credentials',\n accessTokenRef,\n refreshTokenRef,\n expiresAt,\n scopes,\n ttlSeconds,\n };\n await store.writeMeta(record);\n // client_credentials 是机器身份,服务端不回 user —— principal 恒为 undefined,\n // 这与「口令登录拿不到身份」是两件事,别在回显上合并。\n return { record };\n}\n","import type { AxiosInstance } from 'axios';\nimport { TokenStore, type TokenRecord } from './token-store';\nimport { toPrincipal, type LoginOutcome } from './principal';\n\nexport interface PatLoginInput {\n endpoint: string;\n tenantId: string;\n pat: string;\n profile: string;\n httpClient: AxiosInstance;\n}\n\nexport async function loginPat(input: PatLoginInput): Promise<LoginOutcome> {\n // 验证 PAT 有效 + 拿用户/scope 信息\n const resp = await input.httpClient.get(`${input.endpoint}/api/auth/info`, {\n headers: { Authorization: `Bearer ${input.pat}` },\n });\n const scopes: string[] = resp.data?.scopes ?? [];\n // `/api/auth/info` 回的是 UserInfoResponse(userId/username/tenantId/roles) ——\n // PAT 到底挂在谁头上,只有这里说得出来。\n\n const store = new TokenStore(input.profile);\n const accessTokenRef = await store.setAccessToken(input.pat);\n\n // PAT 没有 refresh,过期时间设为 1 年(PAT 由后端管失效)\n const expiresAt = new Date(Date.now() + 365 * 24 * 3600 * 1000).toISOString();\n\n const record: TokenRecord = {\n scheme: 'pat',\n accessTokenRef,\n expiresAt,\n scopes,\n };\n await store.writeMeta(record);\n return { record, principal: toPrincipal(resp.data) };\n}\n","import type { AxiosInstance } from 'axios';\nimport { TokenStore, type TokenRecord } from './token-store';\nimport { toPrincipal, type LoginOutcome } from './principal';\nimport { DEFAULT_SESSION_TTL_SECONDS } from './password';\n\nexport interface PairingLoginInput {\n endpoint: string;\n /** 页面上复制来的配对码。大小写与连字符由服务端归一化,这里原样传。 */\n code: string;\n profile: string;\n httpClient: AxiosInstance;\n}\n\n/**\n * 配对码登录(ADR-308 第四轨)。\n *\n * 解的是「没有任何一方知道后端地址与 tenantId」:CLI 只有磁盘上人手填的值,用户知道的\n * 是浏览器地址栏那一串,后端全树无 frontend URL 配置。唯一同时知道这两件事的是那个\n * 已登录的页面——所以地址由命令行位置参数带进来(人从页面复制整行),租户由服务端\n * 在响应里给出,**不再有 `'1'` 这个猜测**。\n *\n * 🔴 `scheme` 记 `'pairing'` 而不是 `'password'`:如实记录来路(审计与回显需要)。\n * 但它换来的是 `TokenManager.createToken` 铸的**普通会话 token**,续期/吊销语义与口令\n * 登录完全一致——这一点由 `isServerSlidingSession` 谓词保证,别在别处写\n * `=== 'password'` 的比较。\n */\nexport async function loginPairing(input: PairingLoginInput): Promise<LoginOutcome> {\n const resp = await input.httpClient.post(\n `${input.endpoint}/api/auth/cli/exchange`,\n { code: input.code },\n { headers: { 'Content-Type': 'application/json' } },\n );\n // 响应刻意与 /api/auth/login 同形(LoginResponse),所以这里能与口令轨共用解析。\n const { token, expiresIn, user } = resp.data;\n if (!token) {\n throw new Error('Pairing exchange response missing token');\n }\n\n const ttlSeconds = expiresIn ?? DEFAULT_SESSION_TTL_SECONDS;\n const expiresAt = new Date(Date.now() + ttlSeconds * 1000).toISOString();\n\n const store = new TokenStore(input.profile);\n const accessTokenRef = await store.setAccessToken(token);\n\n const record: TokenRecord = {\n scheme: 'pairing',\n accessTokenRef,\n // 与口令会话同样没有 refresh token:续期语义是「同一个 token 被服务端重设 TTL」。\n refreshTokenRef: undefined,\n expiresAt,\n scopes: ['*'],\n ttlSeconds,\n };\n await store.writeMeta(record);\n return { record, principal: toPrincipal(user) };\n}\n\n/**\n * 从用户粘贴的地址里取出 origin。\n *\n * 人复制的是**浏览器地址栏那一串**,带路径、带查询参数都正常——那正是他唯一确定知道的\n * 东西。要求他先手工削成 origin,等于把「知道后端地址」这个门槛又搬了回来。\n */\nexport function toOrigin(raw: string): string {\n const withScheme = /^https?:\\/\\//i.test(raw) ? raw : `https://${raw}`;\n const url = new URL(withScheme);\n return url.origin;\n}\n\n/**\n * 由 origin 推一个 env 名:`https://hcm.custa.com` → `custa`。\n *\n * 取的是主域左边那一段而不是整个 hostname:`hcm.custa.com` 与 `hcm-test.custa.com`\n * 会推出不同的名字,而 `hcm.custa.com` 与 `www.custa.com` 也不会撞。\n * 推不出来时落到 `default`,由调用方处理重名。\n */\nexport function inferEnvName(origin: string): string {\n const host = new URL(origin).hostname;\n if (/^\\d+(\\.\\d+)*$/.test(host) || host === 'localhost') return 'local';\n const parts = host.split('.').filter(Boolean);\n if (parts.length === 0) return 'default';\n // hcm.custa.com → custa(倒数第二段);custa.com → custa;单段 → 该段\n const candidate = parts.length >= 2 ? parts[parts.length - 2] : parts[0];\n return candidate || 'default';\n}\n","import type { AxiosInstance } from 'axios';\n\n/**\n * 首登强制改密(fresh tenant 的必经一步)。\n *\n * <p>后端契约(AuthController):\n * <ul>\n * <li>用初始口令 `POST /api/auth/login` → **403**,body 是\n * `{error:{code:\"AUTH_PASSWORD_CHANGE_REQUIRED\", details:{changePasswordToken, expiresInSeconds}}}`\n * —— 那个临时 token **只活 300 秒**;</li>\n * <li>拿它作 Bearer 调 `POST /api/auth/change-password`,body `{oldPassword,newPassword,confirmPassword}`;</li>\n * <li>改完用新口令重新 login 才拿到正式 token。</li>\n * </ul>\n *\n * <p>🔴 这是 `hcm tenant bootstrap` 之后的**硬阻塞**:不走这一步,新建的租户登不进去。\n * 所以 `hcm login` 自动识别并接管,而不是让顾问自己去拼两条 curl。\n */\n\nexport const PASSWORD_CHANGE_REQUIRED = 'AUTH_PASSWORD_CHANGE_REQUIRED';\n\nexport interface PasswordChangeChallenge {\n changePasswordToken: string;\n expiresInSeconds?: number;\n}\n\n/**\n * 从一个失败的 login 响应里识别「强制改密」挑战。\n *\n * 不是该挑战(网络错、口令错、账号锁定…)一律返回 null,由调用方按原错误处理——\n * 判据只认后端的错误码,不靠 HTTP 状态猜(423 账号锁定同样是 4xx,不能混)。\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function detectPasswordChangeChallenge(err: any): PasswordChangeChallenge | null {\n const body = err?.response?.data;\n const error = body?.error ?? body;\n if (error?.code !== PASSWORD_CHANGE_REQUIRED) return null;\n const token = error?.details?.changePasswordToken;\n if (!token) return null;\n return {\n changePasswordToken: String(token),\n expiresInSeconds: Number(error?.details?.expiresInSeconds) || undefined,\n };\n}\n\nexport interface ChangePasswordInput {\n endpoint: string;\n /** 登录挑战里拿到的临时 token(300 秒有效) */\n changePasswordToken: string;\n oldPassword: string;\n newPassword: string;\n httpClient: AxiosInstance;\n}\n\nexport async function changePassword(input: ChangePasswordInput): Promise<void> {\n await input.httpClient.post(\n `${input.endpoint}/api/auth/change-password`,\n {\n oldPassword: input.oldPassword,\n newPassword: input.newPassword,\n confirmPassword: input.newPassword,\n },\n {\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${input.changePasswordToken}`,\n },\n },\n );\n}\n","import type { AxiosInstance } from 'axios';\n\nexport interface AdminResetPasswordRequest {\n userId?: string;\n username?: string;\n newPassword: string;\n confirmPassword: string;\n forceChangeOnNextLogin: boolean;\n}\n\nexport interface AdminResetPasswordResult {\n status: 'PASSWORD_RESET';\n userId: string;\n forceChangeOnNextLogin: boolean;\n credentialVersion: number;\n sessionsRevoked: boolean;\n removedLoginAttemptKeys: number;\n}\n\n/**\n * 管理员重置租户内用户的本地登录口令。\n *\n * 该端点属于专用凭据流:密码只存在于 HTTPS 请求体,不经过 Model 元数据、\n * Action 参数、日志或返回值。调用方仍须避免把密码放进 argv。\n */\nexport async function adminResetPassword(\n http: AxiosInstance,\n input: AdminResetPasswordRequest,\n): Promise<AdminResetPasswordResult> {\n const response = await http.post('/api/admin/users/password-reset', input);\n const payload = response.data?.data ?? response.data;\n return payload as AdminResetPasswordResult;\n}\n","import WebSocket from 'ws';\nimport { CliError, CliErrorCode } from '../errors';\nimport type {\n StreamCommandEnvelope,\n StreamEventEnvelope,\n ConnectionMessage,\n ControlChannelEnvelope,\n} from './protocol';\n\nexport type MessageClass = 'control' | 'event' | 'connection';\n\n/** WS 入站消息三分:控制通道(channel='control')优先于流事件(streamId+seq),其余为连接层。 */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function classifyMessage(parsed: any): MessageClass {\n if (parsed?.channel === 'control') return 'control';\n if (parsed?.streamId && typeof parsed.seq === 'number') return 'event';\n return 'connection';\n}\n\nexport interface WsClientOpts {\n endpoint: string; // wss://host/ws/conversation\n getAccessToken: () => Promise<string | null>;\n onTokenExpiring?: () => Promise<void>; // 后台 refresh\n heartbeatMs?: number;\n}\n\ntype Handler =\n | { kind: 'event'; fn: (ev: StreamEventEnvelope) => void }\n | { kind: 'connection'; fn: (msg: ConnectionMessage) => void }\n | { kind: 'control'; fn: (ev: ControlChannelEnvelope) => void };\n\nexport class WsClient {\n private ws?: WebSocket;\n private handlers: Handler[] = [];\n private heartbeatTimer?: NodeJS.Timeout;\n private ready = false;\n private sessionId?: string;\n /** 流进行中断连的持久订阅者(iterator 等终态收口者)。RISK-1:auth 后无持久\n * close handler,断连时上层 await 永久挂死。这里让消费方能感知 socket 死。 */\n private disconnectHandlers = new Set<() => void>();\n /** 由 close()/ensureConnected 主动关闭 → 不广播 disconnect(非异常断连)。 */\n private intentionalClose = false;\n\n constructor(private opts: WsClientOpts) {}\n\n /**\n * 订阅「流进行中 socket 异常断连」(后端重启 / 网络闪断)。主动 close() 不触发。\n * iterator 订阅它 → 断连时 finish()(incomplete 终态),杜绝 await consume 永久挂死。\n */\n onDisconnect(fn: () => void): () => void {\n this.disconnectHandlers.add(fn);\n return () => {\n this.disconnectHandlers.delete(fn);\n };\n }\n\n private notifyDisconnect(): void {\n if (this.intentionalClose) return;\n this.ready = false;\n for (const fn of [...this.disconnectHandlers]) {\n try {\n fn();\n } catch {\n /* 单个订阅者抛错不影响其他 */\n }\n }\n }\n\n async connect(): Promise<void> {\n const tok = await this.opts.getAccessToken();\n if (!tok) {\n throw new CliError({\n code: CliErrorCode.AUTH_FAILED,\n message: 'no access token; run `hcm login`',\n });\n }\n this.ws = new WebSocket(this.opts.endpoint);\n\n return new Promise<void>((resolve, reject) => {\n const settle = (fn: () => void) => {\n off();\n fn();\n };\n const off = () => {\n this.ws?.removeListener('open', onOpen);\n this.ws?.removeListener('message', onMessage);\n this.ws?.removeListener('error', onError);\n this.ws?.removeListener('close', onClose);\n };\n const onOpen = () => {\n this.ws?.send(JSON.stringify({ type: 'auth', token: tok }));\n };\n const onMessage = (data: WebSocket.RawData) => {\n const text = data.toString();\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n let parsed: any;\n try {\n parsed = JSON.parse(text);\n } catch {\n return;\n }\n\n // 三分:控制通道 vs 流事件 vs 连接层\n switch (classifyMessage(parsed)) {\n case 'control':\n this.dispatchControl(parsed as ControlChannelEnvelope);\n return;\n case 'event':\n this.dispatchEvent(parsed as StreamEventEnvelope);\n return;\n case 'connection':\n break;\n }\n const msg = parsed as ConnectionMessage;\n this.dispatchConnection(msg);\n\n if (msg.type === 'auth_success') {\n this.ready = true;\n this.intentionalClose = false;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n this.sessionId = (msg as any).sessionId;\n this.startHeartbeat();\n settle(resolve);\n // 装上持久 listener,接管后续流事件 / 连接层消息\n this.ws!.on('message', this.persistentOnMessage);\n // RISK-1:持久 close/error listener——auth 后 settle 的 off() 摘掉了建连期\n // 的 onClose,此后无人感知 socket 死。流进行中异常断连时广播 disconnect,\n // 让 iterator finish() 收口,杜绝 await consume 永久挂死。\n this.ws!.on('close', this.persistentOnClose);\n this.ws!.on('error', this.persistentOnClose);\n } else if (msg.type === 'auth_error') {\n settle(() =>\n reject(\n new CliError({\n code: CliErrorCode.WS_AUTH_ERROR,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n message: `${(msg as any).code}: ${(msg as any).message}`,\n }),\n ),\n );\n } else if (msg.type === 'token_expiring' && this.opts.onTokenExpiring) {\n void this.opts.onTokenExpiring();\n }\n };\n const onError = (err: Error) =>\n settle(() =>\n reject(new CliError({ code: CliErrorCode.NETWORK_ERROR, message: err.message })),\n );\n const onClose = () =>\n settle(() =>\n reject(\n new CliError({ code: CliErrorCode.NETWORK_ERROR, message: 'WS closed before auth' }),\n ),\n );\n this.ws!.on('open', onOpen);\n this.ws!.on('message', onMessage);\n this.ws!.on('error', onError);\n this.ws!.on('close', onClose);\n });\n }\n\n send<P>(cmd: StreamCommandEnvelope<P>): void {\n if (!this.ready || !this.ws) {\n throw new CliError({ code: CliErrorCode.NETWORK_ERROR, message: 'WS not ready' });\n }\n this.ws.send(JSON.stringify(cmd));\n }\n\n onEvent(fn: (ev: StreamEventEnvelope) => void): () => void {\n const h: Handler = { kind: 'event', fn };\n this.handlers.push(h);\n return () => {\n this.handlers = this.handlers.filter((x) => x !== h);\n };\n }\n\n onConnection(fn: (msg: ConnectionMessage) => void): () => void {\n const h: Handler = { kind: 'connection', fn };\n this.handlers.push(h);\n return () => {\n this.handlers = this.handlers.filter((x) => x !== h);\n };\n }\n\n onControl(fn: (ev: ControlChannelEnvelope) => void): () => void {\n const h: Handler = { kind: 'control', fn };\n this.handlers.push(h);\n return () => {\n this.handlers = this.handlers.filter((x) => x !== h);\n };\n }\n\n isReady(): boolean {\n return this.ready;\n }\n\n /**\n * 连接已断(后端重启 / 网络闪断 / 心跳静默失败后 socket 已死)时重建连接。\n * REPL 长开场景每轮发消息前调用——旧连接死掉不应让用户重启 CLI(2026-06-12 用户\n * 实测:后端重启后 REPL 下一条消息报错)。已连接且 socket OPEN 时为 no-op。\n */\n async ensureConnected(): Promise<void> {\n if (this.ready && this.ws && this.ws.readyState === WebSocket.OPEN) return;\n await this.close();\n await this.connect();\n }\n\n getSessionId(): string | undefined {\n return this.sessionId;\n }\n\n async close(): Promise<void> {\n if (this.heartbeatTimer) clearInterval(this.heartbeatTimer);\n this.ready = false;\n this.intentionalClose = true; // 主动关 → persistentOnClose 不广播 disconnect\n if (this.ws) {\n this.ws.removeListener('message', this.persistentOnMessage);\n this.ws.removeListener('close', this.persistentOnClose);\n this.ws.removeListener('error', this.persistentOnClose);\n if (this.ws.readyState === WebSocket.OPEN) this.ws.close();\n }\n }\n\n /** 流进行中 socket 异常断连(非主动 close)→ 广播 disconnect 让消费方收口。 */\n private persistentOnClose = (): void => {\n this.notifyDisconnect();\n };\n\n private persistentOnMessage = (data: WebSocket.RawData): void => {\n const text = data.toString();\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n let parsed: any;\n try {\n parsed = JSON.parse(text);\n } catch {\n return;\n }\n switch (classifyMessage(parsed)) {\n case 'control':\n this.dispatchControl(parsed as ControlChannelEnvelope);\n return;\n case 'event':\n this.dispatchEvent(parsed as StreamEventEnvelope);\n return;\n case 'connection':\n break;\n }\n const msg = parsed as ConnectionMessage;\n this.dispatchConnection(msg);\n if (msg.type === 'token_expiring' && this.opts.onTokenExpiring) {\n void this.opts.onTokenExpiring();\n }\n };\n\n private startHeartbeat(): void {\n const ms = this.opts.heartbeatMs ?? 30_000;\n this.heartbeatTimer = setInterval(() => {\n try {\n this.ws?.send(JSON.stringify({ type: 'ping' }));\n } catch {\n /* swallow heartbeat send errors */\n }\n }, ms);\n }\n\n private dispatchEvent(ev: StreamEventEnvelope): void {\n for (const h of this.handlers) if (h.kind === 'event') h.fn(ev);\n }\n\n private dispatchConnection(msg: ConnectionMessage): void {\n for (const h of this.handlers) if (h.kind === 'connection') h.fn(msg);\n }\n\n private dispatchControl(ev: ControlChannelEnvelope): void {\n for (const h of this.handlers) if (h.kind === 'control') h.fn(ev);\n }\n}\n","import type { AttachmentMeta } from '../documents';\nimport { randomUUID } from 'node:crypto';\nimport type { WsClient } from './ws-client';\nimport type {\n StreamEventEnvelope,\n StreamCommandEnvelope,\n MessageCreatePayload,\n ControlChannelEnvelope,\n} from './protocol';\nimport { buildInterrupt, type StreamCommand } from './control-channel';\n\nexport interface SendMessageOpts {\n conversationId?: string; // 不传 = 新建\n agentKey?: string;\n agentId?: string; // direct-ops mode: bypass conversation→agent lookup\n pageContext?: Record<string, unknown>;\n /**\n * confirm/ask 决策回发后继续消费 resume 流到真终态(Task B3)。\n *\n * v4 协议:confirm 触发时原流以 ROOT message.stop 终态(HALT 语义);决策回发后后端\n * resume 续推事件。开启本开关后终态规则变为:\n * - ROOT message.stop 且无未决交互 → 终态(含全程无交互的普通流)\n * - ROOT message.stop 且有未决交互 → hold(HALT / 串联 confirm 的中间 stop)\n * - `control.interaction.resolved(phase=TERMINAL)` 清空未决集且曾 hold 过 ROOT stop\n * → 终态兜底(TERMINAL 是 InteractionSession 生命周期事件,必达;防 resume 段缺\n * ROOT message.stop 的后端回归挂死 CLI)\n * 仅在调用方会回发决策(--on-confirm / --on-ask / 交互式确认)时开启;默认关闭,\n * 行为与历史完全一致。\n */\n holdUntilInteractionTerminal?: boolean;\n /**\n * 重新生成模式(ADR-089):不发 message.create,改发 `response.regenerate`\n * (payload {fromSeq})。fromSeq = 被重生成 assistant 段起点的 journal seq\n * (LLM_DONE;timeline chat-text item 的 seq 即它)。后端 append SEGMENT_SUPERSEDED\n * marker 后用原 user message 重跑 loop,新流事件 requestId 回显本命令 commandId\n * ——iterator 过滤与终态规则完全复用。必须配合已存在的 conversationId。\n */\n regenerateFromSeq?: number;\n /** 附件元数据(issue #64):已上传文档随消息发送,后端 parseAttachments 同形态。 */\n attachments?: AttachmentMeta[];\n}\n\nexport interface MessageStreamHandle {\n conversationId: string;\n commandId: string;\n events: AsyncIterable<StreamEventEnvelope>;\n /**\n * 客户端中断当前流(ADR-085)。\n * 发送 `stream.cancel` command 到后端;后端会回 `response.interrupted` 作 iterator 终态。\n * 调用方仍需等 iterator 走完才能确认终止。\n */\n cancel(reason?: string): void;\n /**\n * 订阅本 conversation 的带外控制通道事件(control.interaction.request / resolved)。\n * 仅转发 `ev.conversationId === conversationId` 的事件;返回取消订阅函数。\n */\n onControl(fn: (ev: ControlChannelEnvelope) => void): () => void;\n /**\n * 回发 client→server 控制命令(approval.confirm / approval.reject / interaction.answer)。\n * 由 control-channel 的 build* 构造,透传到 ws。\n */\n sendCommand(cmd: StreamCommand): void;\n}\n\nexport async function sendMessageAndStream(\n ws: WsClient,\n prompt: string,\n opts: SendMessageOpts = {},\n): Promise<MessageStreamHandle> {\n const conversationId = opts.conversationId ?? randomUUID();\n const commandId = randomUUID();\n const queue: StreamEventEnvelope[] = [];\n const waiters: Array<(ev: IteratorResult<StreamEventEnvelope>) => void> = [];\n let done = false;\n const seenEventIds = new Set<string>();\n\n // B3 · hold 模式状态:未决交互集(值 = 是否已收到 RESUMED 受理)+ 是否 hold 过\n // ROOT stop(HALT 已发生的标记)。B4 升级为 Map:接管链语义需要区分「已 RESUMED」。\n const hold = !!opts.holdUntilInteractionTerminal;\n const pendingInteractions = new Map<string, { resumed: boolean }>();\n let sawHeldRootStop = false;\n let offControlInternal: (() => void) | undefined;\n\n const finish = (): void => {\n if (done) return;\n done = true;\n while (waiters.length) {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion, @typescript-eslint/no-explicit-any\n waiters.shift()!({ value: undefined as any, done: true });\n }\n off();\n offControlInternal?.();\n offDisconnect?.();\n };\n\n // RISK-1:流进行中 socket 异常断连(后端重启 / 网络闪断)→ finish()(incomplete 终态),\n // 杜绝 await consume 永久挂死。queue 中已收事件在 finish 前被消费方吐尽,断后置 done。\n // onDisconnect 可选(旧 WsClient / 测试 stub 无此方法时跳过,不破坏向后兼容)。\n const offDisconnect = ws.onDisconnect?.(() => finish());\n\n const off = ws.onEvent((ev) => {\n if (done) return;\n if (ev.requestId !== commandId) return;\n // v4 去重必须按 eventId,不能按 seq 单调(旧 v3 逻辑)。v4 的 ephemeral 事件\n // (LlmDelta text/thinking 等)共享上一个 material 的 seq(不递增),seq<=lastSeq\n // 会把 text delta + 终态事件全部误杀 → 空白回答 + 等不到 response.completed 而 timeout。\n if (ev.eventId && seenEventIds.has(ev.eventId)) return;\n if (ev.eventId) seenEventIds.add(ev.eventId);\n if (waiters.length) {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n waiters.shift()!({ value: ev, done: false });\n } else {\n queue.push(ev);\n }\n\n // v3 残余终态(completed/failed/interrupted)只在老路径出现,老路径无 resume → 一律立即终态。\n if (\n ev.type === 'response.completed' ||\n ev.type === 'response.failed' ||\n ev.type === 'response.interrupted'\n ) {\n finish();\n return;\n }\n // v4 原生终态:ROOT message.stop(parentToolUseId 空)= turn 结束,与 Web v4 同。\n // 某些路径(如 hcm_respond_directly 短路)后端不发 v3 兼容残余 response.completed,\n // 不以 message.stop 兜底则 iterator 永不结束而 timeout。子流 message.stop(parentToolUseId 非空)不算终态。\n if (\n ev.type === 'message.stop' &&\n !(ev.payload as { parentToolUseId?: string } | undefined)?.parentToolUseId\n ) {\n if (!hold || pendingInteractions.size === 0) {\n finish();\n } else {\n // HALT(或串联 confirm 的中间 stop):有未决交互 → 不终态,等 resume / TERMINAL。\n sawHeldRootStop = true;\n }\n }\n });\n\n // hold 模式:内部订阅 control 通道维护未决交互集。TERMINAL resolved 清空 pending 且\n // 曾 hold 过 ROOT stop → 终态兜底(resume 段内容此刻已推完,queue 中事件仍会被吐尽)。\n if (hold) {\n offControlInternal = ws.onControl((cev) => {\n if (done || cev.conversationId !== conversationId) return;\n const p = cev.payload as { interactionId?: string; phase?: string } | undefined;\n if (!p?.interactionId) return;\n if (cev.type === 'control.interaction.request') {\n // B4 · 接管链语义(bay4 真流实测):interaction 被嵌套 confirm「接管」后\n // (前序已 RESUMED、其 resume 轮又 HALTED 交给新 confirm),后端按设计\n // 不再为前序发 TERMINAL(HALTED 分支不发)。新 request 到达 = 已 RESUMED\n // 的前序终局由新 interaction 接管 → 从 pending 清除,否则链式 confirm 下\n // 终态条件 pending==0 永不满足而挂死。未 RESUMED 的并存 interaction\n // (用例⑤)各有自己的 TERMINAL,不在清理范围。\n for (const [id, st] of pendingInteractions) {\n if (st.resumed) pendingInteractions.delete(id);\n }\n pendingInteractions.set(p.interactionId, { resumed: false });\n } else if (cev.type === 'control.interaction.resolved') {\n if (p.phase === 'RESUMED') {\n const st = pendingInteractions.get(p.interactionId);\n if (st) st.resumed = true;\n } else if (p.phase === 'TERMINAL') {\n pendingInteractions.delete(p.interactionId);\n if (pendingInteractions.size === 0 && sawHeldRootStop) finish();\n }\n }\n });\n }\n\n const cmd: StreamCommandEnvelope<MessageCreatePayload | { fromSeq: number }> =\n opts.regenerateFromSeq != null\n ? {\n // regenerate:同 conversation 重跑被废弃段,事件 requestId 回显本 commandId。\n protocolVersion: '4.0',\n commandId,\n conversationId,\n type: 'response.regenerate',\n timestamp: Date.now(),\n payload: { fromSeq: opts.regenerateFromSeq },\n }\n : {\n // v4 reference client:后端 ADR-118 起无条件推 v4,此字段是诚实声明(不改变后端行为)。\n protocolVersion: '4.0',\n commandId,\n conversationId,\n type: 'message.create',\n timestamp: Date.now(),\n payload: {\n content: prompt,\n agentKey: opts.agentKey,\n directOpsAgentId: opts.agentId,\n pageContext: opts.pageContext,\n attachments: opts.attachments?.length ? opts.attachments : undefined,\n },\n };\n ws.send(cmd);\n\n const events: AsyncIterable<StreamEventEnvelope> = {\n [Symbol.asyncIterator]: () => ({\n next: () =>\n new Promise((resolve) => {\n if (queue.length) {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n resolve({ value: queue.shift()!, done: false });\n } else if (done) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n resolve({ value: undefined as any, done: true });\n } else {\n waiters.push(resolve);\n }\n }),\n }),\n };\n\n const cancel = (_reason: string = 'USER_CANCEL'): void => {\n try {\n // response.interrupt — 后端唯一认的打断命令(StreamCommandType.RESPONSE_INTERRUPT)。\n // 🔴 旧版发 v3.2 的 stream.cancel:后端命令枚举里没有它——cancel 一直是空操作\n // (Esc/Ctrl+C 打断踢空,2026-06-12 用户终验钉出)。统一走 buildInterrupt,消除\n // 与 control-channel 手搓 envelope 的 protocolVersion 不一致(白盒审计 RISK 文档债)。\n ws.send(buildInterrupt(conversationId));\n } catch {\n /* ws 已关闭, 忽略 */\n }\n };\n\n const onControl = (fn: (ev: ControlChannelEnvelope) => void): (() => void) =>\n ws.onControl((ev) => {\n if (ev.conversationId === conversationId) fn(ev);\n });\n\n // 回发命令(approval.* / interaction.answer)的 type 已纳入 StreamCommandType、protocolVersion\n // 收窄为 '3.0',StreamCommand 结构兼容 StreamCommandEnvelope,无需 as 转换。\n const sendCommand = (command: StreamCommand): void => ws.send(command);\n\n return { conversationId, commandId, events, cancel, onControl, sendCommand };\n}\n","/**\n * control-channel — 控制通道(channel='control')解析 + 回发命令构造。\n * 回发走 StreamCommandType(已坐实三方一致:后端 ConversationWebSocketHandler.dispatch /\n * 前端 useAIChat / 协议文档 stream-protocol.md §4),不用死枚举 control.interaction.decision。\n */\nimport { randomUUID } from 'node:crypto';\n\nexport interface InteractionRequest {\n kind: 'CONFIRM' | 'ASK';\n interactionId: string;\n summary: string;\n originAgentPath: string;\n ttlSeconds: number;\n // CONFIRM 专属\n riskLevel?: string;\n toolName?: string;\n argsPreview?: Record<string, unknown>;\n // ASK 专属\n question?: string;\n choices?: string[];\n}\n\nexport interface InteractionResolved {\n interactionId: string;\n phase: 'RESUMED' | 'TERMINAL' | string;\n}\n\nexport interface StreamCommand {\n protocolVersion: '3.0';\n commandId: string;\n conversationId: string;\n type:\n | 'approval.confirm'\n | 'approval.reject'\n | 'interaction.answer'\n // 流中途控制(P0 S-interrupt / S-steer):cooperative cancel / 注入下一 round SystemMessage\n | 'response.interrupt'\n | 'response.steer';\n timestamp: number;\n payload: Record<string, unknown>;\n}\n\nexport function parseInteractionRequest(payload: Record<string, unknown>): InteractionRequest {\n return {\n kind: payload.type as 'CONFIRM' | 'ASK',\n interactionId: payload.interactionId as string,\n summary: (payload.summary as string) || '',\n originAgentPath: (payload.originAgentPath as string) || '',\n ttlSeconds: (payload.ttlSeconds as number) || 0,\n riskLevel: payload.riskLevel as string | undefined,\n toolName: payload.toolName as string | undefined,\n argsPreview: payload.argsPreview as Record<string, unknown> | undefined,\n question: payload.question as string | undefined,\n choices: payload.choices as string[] | undefined,\n };\n}\n\nexport function parseInteractionResolved(payload: Record<string, unknown>): InteractionResolved {\n return {\n interactionId: payload.interactionId as string,\n phase: (payload.phase as string) || '',\n };\n}\n\n// timestamp 由调用点用 Date.now() 注入;测试只校验 type/conversationId/payload,不校验具体时间戳。\nfunction envelope(\n type: StreamCommand['type'],\n conversationId: string,\n payload: Record<string, unknown>,\n): StreamCommand {\n return { protocolVersion: '3.0', commandId: randomUUID(), conversationId, type, timestamp: Date.now(), payload };\n}\n\nexport function buildConfirm(confirmationId: string, conversationId: string): StreamCommand {\n return envelope('approval.confirm', conversationId, { confirmationId });\n}\n\nexport function buildReject(confirmationId: string, conversationId: string, reason?: string): StreamCommand {\n return envelope('approval.reject', conversationId, reason ? { confirmationId, reason } : { confirmationId });\n}\n\nexport function buildAnswer(\n interactionId: string,\n conversationId: string,\n answer: string,\n choiceIndex?: number,\n): StreamCommand {\n return envelope('interaction.answer', conversationId, { interactionId, answer, choiceIndex: choiceIndex ?? null });\n}\n\n/**\n * 流中途打断(cooperative cancel)。后端收到后停止当前 round,流以 ROOT message.stop\n * (payload.stopReason==\"interrupted\")终结。payload 为空对象 {}(后端契约已核实)。\n */\nexport function buildInterrupt(conversationId: string): StreamCommand {\n return envelope('response.interrupt', conversationId, {});\n}\n\n/**\n * 流中途引导(steer)。后端入队 content,下一 round 注入 SystemMessage;无任何 ack 事件\n * (v3 ack 已退役),流正常完成。payload 为 {content}(后端契约已核实)。\n */\nexport function buildSteer(content: string, conversationId: string): StreamCommand {\n return envelope('response.steer', conversationId, { content });\n}\n","/** 委派类工具名(对齐 Web constants.ts::isDelegationToolName)。hcm_handoff 为历史别名。 */\nconst DELEGATION_TOOLS = new Set(['hcm_delegate', 'hcm_handoff']);\nexport function isDelegationToolName(name: string): boolean {\n return DELEGATION_TOOLS.has(name);\n}\n","/**\n * v4-reducer — 复刻 hcm_nx_cloud useChatItemsStreamV4.ts::createV4Reducer 的 fold 状态机,\n * 但输出端换成 CLI 流式语义事件(V4RenderSink)。终端是 append-only 流,不能像 Web 那样\n * 回填 item,故 tool 行等 block.stop(args 齐)才发 onToolStarted。\n * 单层委派模型(与 Web 同限制):currentParentToolUseId 单值;多级嵌套靠 renderer depth counter。\n */\nimport { isDelegationToolName } from './delegation-constants';\nimport { truncateDisplay } from './display-width';\n\nexport interface V4RenderSink {\n onText(text: string, parentToolUseId: string | null): void;\n onThinkingDelta(text: string, parentToolUseId: string | null): void;\n onThinkingDone(parentToolUseId: string | null): void;\n onToolStarted(callId: string, toolName: string, argsPreview: string, parentToolUseId: string | null): void;\n /**\n * @param nextSteps G5 · 工具失败时从 result envelope 解析出的 ADR-058 路标(`error.nextSteps`\n * 数组 / 顶层 `next_step`)。成功或无路标时 undefined。供 host 在失败行下透出下一步指引。\n * @param toolName #1930 · 工具名。优先取 block.start 注册值;缓存短路 no-op 等无 block.start\n * 的事件由 reducer 从 result 文本兜底解析(如「hcm_describe_tool (缓存命中)」),避免 host\n * 裸显示 `?`。仍解析不出时 undefined,host 自行兜底。\n */\n onToolResult(\n callId: string,\n success: boolean,\n durMs: number,\n childConversationId: string | null,\n parentToolUseId: string | null,\n nextSteps?: string[],\n toolName?: string,\n ): void;\n onContentPart(part: Record<string, unknown>, parentToolUseId: string | null): void;\n /**\n * 用户中途纠偏在轮界被真正注入(v4 `steering.applied` 顶层事件,W1a-2)。\n * 仅注入时到达——客户端禁止在发 response.steer 时乐观本地显示「已纠偏」。\n */\n onSteeringApplied(contents: string[], round: number): void;\n onDelegationStart(agentKey: string, parentToolUseId: string): void;\n /**\n * 委派收口。由 hcm_delegate 自身的 tool.result 驱动(后端时序保证:子流 message.stop\n * 先到、父 tool.result 紧随其后,见 WebSocketRuntimeEventProjector.finishChildStreamIfAny)\n * ——因此能携带委派整体 success / 耗时 / 失败路标(2026-06-11 review C1/I1:旧版由子\n * message.stop 驱动拿不到 success,失败委派被静默渲染成功)。\n *\n * @param nextSteps 委派失败时的 ADR-058 路标;成功为 undefined。\n */\n onDelegationEnd(parentToolUseId: string, success: boolean, durMs: number, nextSteps?: string[]): void;\n /**\n * 委派「暂停待应答」(非失败):specialist 在子流内调 hcm_ask_user / 触发风险确认时,\n * 后端按 HALT 语义给 hcm_delegate 的 tool.result 发 transport `success=false`,但其\n * envelope 实为 `ok:true` + `INTERACTION_REQUIRED`/`CONFIRMATION_REQUIRED` 标记\n * (后端 InteractionSuspendMiddleware / RiskConfirmationMiddleware)。这不是失败——\n * 用户应答后委派会 RESUME(同 parentToolUseId 再来一段 message.start)续跑到真终态。\n *\n * 消费端据此:保留委派块不收口、累计本段耗时,等 RESUME 续同一块——避免把暂停\n * 渲染成红色「委派失败」、并避免暂停→续跑被计为两次委派(CLI append-only 无法回填)。\n * @param durMs 暂停前本段执行耗时(累计进委派块总耗时)。\n */\n onDelegationPaused(parentToolUseId: string, durMs: number): void;\n onTerminal(stopReason: string, tokens: number): void;\n}\n\n/**\n * 判定一条 hcm_delegate 的 tool.result(transport `success=false`)实为「暂停待应答」而非失败。\n *\n * 后端 wire 接缝不一致:HALT 时 executor 结果 `success=true`+`haltConversation`,但投影到\n * delegate 的 tool.result `success` 字段为 false;其 resultData envelope 仍诚实保留 `ok:true`\n * + summary/data 带 `INTERACTION_REQUIRED`(ASK)/`CONFIRMATION_REQUIRED`(CONFIRM)标记。\n * 以「envelope ok===true 且含交互标记」为准绳——纯结构信号,不误伤真失败(真失败 ok:false)。\n */\nexport function detectDelegationPause(resultData: unknown): boolean {\n if (!resultData || typeof resultData !== 'object') return false;\n const rd = resultData as Record<string, unknown>;\n let env: Record<string, unknown> | null = null;\n const raw = rd.result;\n if (typeof raw === 'string') {\n try {\n const o = JSON.parse(raw);\n env = o && typeof o === 'object' ? (o as Record<string, unknown>) : null;\n } catch {\n env = null;\n }\n } else if (raw && typeof raw === 'object') {\n env = raw as Record<string, unknown>;\n } else {\n env = rd;\n }\n if (!env || env.ok !== true) return false;\n const text = [env.summary, env.data].filter((x) => typeof x === 'string').join(' ');\n return text.includes('INTERACTION_REQUIRED') || text.includes('CONFIRMATION_REQUIRED');\n}\n\n/**\n * #1930 · 取 tool.result 的原始结果文本(用于无 envelope 的纯字符串结果分类)。\n *\n * wire 形态 {@code resultData = { result: \"<string>\" }}(WS ToolResultPayload);也兼容\n * resultData 本身即字符串。非字符串结果(结构化对象)返回空串——这些走正常 envelope 路径,\n * 不属本函数关心的「控制信号 / no-op 短路」纯文本类。\n */\nfunction resultText(resultData: unknown): string {\n if (typeof resultData === 'string') return resultData;\n if (resultData && typeof resultData === 'object') {\n const raw = (resultData as Record<string, unknown>).result;\n if (typeof raw === 'string') return raw;\n }\n return '';\n}\n\n/**\n * #1930 Bug B · 判定一条**直接工具**(非委派)的 tool.result 实为「待确认 / 待应答」控制信号,\n * 而非工具执行结果。\n *\n * 后端写动作命中确认门 / 交互门时,会先发一条 transport `success=false` 的 TOOL_COMPLETED,其\n * result 是纯文本前缀 {@code \"CONFIRMATION_REQUIRED: ...\"} / {@code \"INTERACTION_REQUIRED: ...\"}\n * (非 JSON envelope),同时另发一条 {@code control.interaction.request} 让 host 弹确认卡。该 tool.result\n * 对展示流是冗余的——若按普通工具渲染会变成红色 `⚙ ? ✗ 0ms` 假失败并计入失败统计(实测用户困惑)。\n *\n * 以「结果文本以这两个标记**起始**」为准绳(强协议信号,正常工具结果不会以此开头),故 host 可安全\n * 抑制该行,把待确认态交给 confirm 卡承载。委派场景的同类暂停由 {@link detectDelegationPause} 处理,\n * 二者互不重叠(委派结果是 envelope,直接工具是纯文本前缀)。\n */\nexport function detectDirectInteractionPending(resultData: unknown): boolean {\n const text = resultText(resultData).trimStart();\n return text.startsWith('CONFIRMATION_REQUIRED') || text.startsWith('INTERACTION_REQUIRED');\n}\n\n/**\n * #1930 Bug A · 为无 block.start 注册名的 tool.result 从结果文本兜底解析有意义的展示名。\n *\n * 当前唯一已知来源:ADR-095 catalog 缓存短路 no-op——后端直接发 TOOL_COMPLETED(无 TOOL_STARTED),\n * result 为 {@code \"[ADR-095 catalog cache hit] 工具 <toolName> 已在本 sub-session ...\"}。解析出工具名\n * 时返回 {@code \"<toolName> (缓存命中)\"},解析不出则返回 {@code \"缓存命中\"}。其他结果返回 undefined\n * (host 保留原 `?` 兜底给真未知事件,不强行编造名字)。\n */\nexport function deriveNamelessToolLabel(resultData: unknown): string | undefined {\n const text = resultText(resultData);\n if (text.includes('catalog cache hit')) {\n const m = text.match(/工具\\s+(\\w+)/);\n return m ? `${m[1]} (缓存命中)` : '缓存命中';\n }\n return undefined;\n}\n\n/**\n * #1949 · 从确认门 / 交互门占位结果文本解析被挂起的工具名。\n *\n * 后端 {@code RiskConfirmationMiddleware} 挂起时发的占位 TOOL_COMPLETED,其 result 为纯文本\n * {@code \"CONFIRMATION_REQUIRED: 等待用户确认 tool=<name>, confirmationId=..., scopeKey=...\"}\n * (INTERACTION_REQUIRED 亦可能带 {@code tool=})。用户确认后被 resume 的工具,其真结果\n * {@code tool.result} 可能先于(或缺失)resume 段 {@code block.start} 抵达消费端 → {@code toolNameByCallId}\n * 无注册名 → 裸显示 `?`(#1949)。这里在抑制占位结果时解析出工具名,供 resume 真结果按 callId 兜底取名。\n * 解析不出返回 undefined。\n */\nexport function parseConfirmToolName(resultData: unknown): string | undefined {\n const m = resultText(resultData).match(/\\btool=([^,\\s]+)/);\n return m ? m[1] : undefined;\n}\n\n/**\n * G5 · 从 tool.result 的 resultData 防御性提取 ADR-058 `next_step` 路标。\n *\n * 后端 wire:`resultData = { result: \"<envelope JSON 字符串>\" }`,失败 envelope 形如\n * `{ ok:false, error:{ code, message, nextSteps:[...] } }`;部分执行器平铺顶层 `next_step`\n * (可为 string / string[] / {message,...} map)。这里兼容多形态,任一解析失败都静默返回\n * undefined(非 JSON 纯文本 result 不炸)。\n */\nexport function extractNextSteps(resultData: unknown): string[] | undefined {\n if (!resultData || typeof resultData !== 'object') return undefined;\n const rd = resultData as Record<string, unknown>;\n // wire 形态:{ result: \"<json string>\" };也兼容 resultData 本身即结构化对象。\n let env: Record<string, unknown> | null = null;\n const raw = rd.result;\n if (typeof raw === 'string') {\n try {\n const o = JSON.parse(raw);\n env = o && typeof o === 'object' ? (o as Record<string, unknown>) : null;\n } catch {\n env = null;\n }\n } else if (raw && typeof raw === 'object') {\n env = raw as Record<string, unknown>;\n } else {\n env = rd;\n }\n if (!env) return undefined;\n\n const collect = (v: unknown): string[] => {\n if (Array.isArray(v)) return v.map((x) => String(x)).filter((s) => s.length > 0);\n if (typeof v === 'string' && v.length > 0) return [v];\n if (v && typeof v === 'object') {\n const m = (v as Record<string, unknown>).message;\n if (typeof m === 'string' && m.length > 0) return [m];\n }\n return [];\n };\n\n const error = env.error;\n const fromError =\n error && typeof error === 'object'\n ? collect((error as Record<string, unknown>).nextSteps).concat(\n collect((error as Record<string, unknown>).next_step),\n )\n : [];\n const fromTop = collect(env.nextSteps).concat(collect(env.next_step));\n const all = [...fromError, ...fromTop];\n return all.length > 0 ? all : undefined;\n}\n\ninterface V4BlockMeta {\n blockType: string;\n toolName?: string;\n callId?: string;\n parentToolUseId: string | null;\n jsonBuffer: string;\n}\n\nexport function createV4Reducer(sink: V4RenderSink) {\n const blockMap = new Map<number, V4BlockMeta>();\n const toolNameByCallId = new Map<string, string>();\n // #1949 · 确认门通过后被 resume 的直接工具:其 resume 段 block.start 可能未抵达消费端(只到\n // tool.result)→ toolNameByCallId 无注册名 → 裸显示 `?`。挂起时占位结果文本带 `tool=<name>`,\n // 在抑制该占位结果时按 callId 暂存,供 resume 真结果兜底取名(见 tool.result case)。\n const confirmToolNameByCallId = new Map<string, string>();\n // 嵌套委派归属栈(2026-06-11 review I2):旧单值 currentParent 在 d1 子流内再委派 d2 时,\n // d2 的 message.stop 会把归属误归零 → d1 剩余子流文本被当主流程平铺。栈语义:push on\n // 子 message.start,pop on 子 message.stop,currentParent 恒为栈顶。\n const parentStack: string[] = [];\n let currentParent: string | null = null;\n\n const parentOf = (payloadParent?: unknown): string | null =>\n (typeof payloadParent === 'string' && payloadParent) ? payloadParent : currentParent;\n\n // 按显示宽度截断(CJK=2 列):单 value ≤40 列防 reasoning 类长参数霸行,总预览 ≤96 列\n // 保证「⚙ name preview ✓ Xms」在 120 列内单行收口(旧按字符数 slice(120) 中文实占 240 列必折行)。\n function buildArgsPreview(args: Record<string, unknown>): string {\n const preview = Object.entries(args)\n .map(([k, v]) => {\n const raw = typeof v === 'object' ? JSON.stringify(v) : String(v);\n return `${k}=${truncateDisplay(raw, 40)}`;\n })\n .join(' · ');\n return truncateDisplay(preview, 96);\n }\n function parseJson(s: string): Record<string, unknown> | null {\n try { const o = JSON.parse(s); return o && typeof o === 'object' ? o : null; } catch { return null; }\n }\n\n function handle(type: string, payload: Record<string, unknown>): boolean {\n switch (type) {\n case 'message.start': {\n const p = payload.parentToolUseId;\n if (typeof p === 'string' && p) {\n parentStack.push(p);\n currentParent = p;\n sink.onDelegationStart((payload.agentKey as string) || 'specialist', p);\n } else {\n // ROOT message:归属复位(resume 等场景防御性清栈)。\n parentStack.length = 0;\n currentParent = null;\n }\n return true;\n }\n case 'block.start': {\n const index = payload.index as number;\n const blockType = (payload.blockType as string) || 'text';\n const toolName = (payload.toolName as string) || undefined;\n const callId = (payload.callId as string) || undefined;\n blockMap.set(index, { blockType, toolName, callId, parentToolUseId: parentOf(payload.parentToolUseId), jsonBuffer: '' });\n if (blockType === 'tool_use' && callId && toolName) toolNameByCallId.set(callId, toolName);\n // CLI:tool 行等 block.stop(args 齐)才发 onToolStarted;此处不输出。\n return true;\n }\n case 'block.delta': {\n const index = payload.index as number;\n const meta = blockMap.get(index);\n const parent = meta?.parentToolUseId ?? currentParent;\n switch (payload.deltaType) {\n case 'text_delta': sink.onText((payload.text as string) || '', parent); return true;\n case 'thinking_delta': sink.onThinkingDelta((payload.text as string) || '', parent); return true;\n case 'input_json_delta': if (meta) meta.jsonBuffer += (payload.partialJson as string) || ''; return true;\n case 'content_part': if (meta) meta.jsonBuffer += (payload.partialJson as string) || ''; return true;\n default: return true;\n }\n }\n case 'block.stop': {\n const index = payload.index as number;\n const meta = blockMap.get(index);\n blockMap.delete(index);\n if (!meta) return true;\n if (meta.blockType === 'thinking') { sink.onThinkingDone(meta.parentToolUseId); return true; }\n if (meta.blockType === 'tool_use' && meta.callId) {\n const args = meta.jsonBuffer ? parseJson(meta.jsonBuffer) ?? {} : {};\n if (meta.toolName && isDelegationToolName(meta.toolName)) return true; // delegation 由 message.start / tool.result 渲染\n sink.onToolStarted(meta.callId, meta.toolName || '?', buildArgsPreview(args), meta.parentToolUseId);\n return true;\n }\n if (meta.blockType === 'content_part' && meta.jsonBuffer) {\n const part = parseJson(meta.jsonBuffer)?.part;\n if (part && typeof part === 'object') {\n const p = part as Record<string, unknown>;\n // fileDownload content_part({fileName, downloadUrl})→ 渲染为可点的绝对签名链接,\n // 点击由浏览器打开(ADR-140,免 Bearer);CLI 不再自动落盘(用户要求点击打开即可)。\n sink.onContentPart(p, meta.parentToolUseId);\n }\n return true;\n }\n return true;\n }\n case 'tool.result': {\n const callId = payload.callId as string;\n if (!callId) return true;\n const toolName = toolNameByCallId.get(callId);\n const success = payload.success !== false;\n // G5:失败才解析 next_step 路标(成功路径不解析,省开销)。\n const nextSteps = success ? undefined : extractNextSteps(payload.resultData);\n if (toolName && isDelegationToolName(toolName)) {\n // 委派收口在此驱动(review C1/I1):本事件带委派整体 success/durMs/失败路标,\n // 失败委派不再被静默渲染成功。子流 message.stop 仅做归属弹栈(见 message.stop case)。\n const durMs = (payload.executionTimeMs as number) || 0;\n // transport success=false 但实为「暂停待应答」(envelope ok:true + 交互标记)→ 非失败,\n // 走 onDelegationPaused 保留委派块等 RESUME 续跑;真失败仍走 onDelegationEnd。\n if (!success && detectDelegationPause(payload.resultData)) {\n // 🔴 不 delete toolNameByCallId[callId]:RESUME 后的「真收口」tool.result(同 callId)\n // 仍须被识别为委派——否则它退化成普通 onToolResult,渲染成神秘「⚙ ? ✓ 0ms」工具行。\n sink.onDelegationPaused(callId, durMs);\n } else {\n toolNameByCallId.delete(callId);\n sink.onDelegationEnd(callId, success, durMs, nextSteps);\n }\n return true;\n }\n toolNameByCallId.delete(callId);\n // #1930 Bug B:直接工具的「待确认 / 待应答」控制信号(CONFIRMATION_REQUIRED /\n // INTERACTION_REQUIRED 纯文本前缀)不是工具结果——它对应一条 control.interaction.request,\n // 由 confirm 卡承载可见待确认态。这里抑制,避免渲染成红色 `⚙ ? ✗ 0ms` 假失败 + 计入失败统计。\n if (detectDirectInteractionPending(payload.resultData)) {\n // #1949 · 暂存被挂起工具名,供其被 resume 后的真结果兜底取名(resume 段 block.start\n // 可能未抵达消费端 → 否则 resume 工具行裸显示 `?`)。\n const confirmName = parseConfirmToolName(payload.resultData);\n if (confirmName) confirmToolNameByCallId.set(callId, confirmName);\n return true;\n }\n // #1930 Bug A + #1949:名字优先取 block.start 注册值;缺失时取确认门暂存名(resume 直接工具);\n // 再退回结果文本兜底(缓存短路 no-op);都没有才让 host 显示 `?`。\n const resolvedToolName =\n toolName ?? confirmToolNameByCallId.get(callId) ?? deriveNamelessToolLabel(payload.resultData);\n confirmToolNameByCallId.delete(callId);\n sink.onToolResult(\n callId,\n success,\n (payload.executionTimeMs as number) || 0,\n (payload.childConversationId as string) || null,\n parentOf(payload.parentToolUseId),\n nextSteps,\n resolvedToolName,\n );\n return true;\n }\n case 'steering.applied': {\n const contents = Array.isArray(payload.contents)\n ? (payload.contents as unknown[]).map((c) => String(c)).filter((c) => c.length > 0)\n : [];\n if (contents.length > 0) {\n sink.onSteeringApplied(contents, (payload.round as number) ?? 0);\n }\n return true;\n }\n case 'message.stop': {\n const p = payload.parentToolUseId;\n if (typeof p === 'string' && p) {\n // 子流结束:仅弹栈回外层归属(嵌套委派 d1>d2 时 d2 结束回 d1)。收口渲染由\n // 紧随其后的 delegation tool.result 驱动(带 success,见 tool.result case)。\n const idx = parentStack.lastIndexOf(p);\n if (idx >= 0) parentStack.splice(idx, 1);\n currentParent = parentStack[parentStack.length - 1] ?? null;\n return true;\n }\n parentStack.length = 0;\n currentParent = null;\n blockMap.clear();\n sink.onTerminal((payload.stopReason as string) || 'end_turn', (payload.totalTokens as number) || 0);\n return true;\n }\n default: return false;\n }\n }\n\n return { handle };\n}\n","import type { WsClient } from './ws-client';\nimport { sendMessageAndStream, type SendMessageOpts } from './stream-iterator';\nimport type { StreamEventEnvelope, ResponseFailedPayload } from './protocol';\nimport { createV4Reducer, type V4RenderSink } from './v4-reducer';\nimport { CliError, CliErrorCode } from '../errors';\n\nexport interface OneShotToolCall {\n /** 工具名;委派为 'hcm_delegate'。无法解析时 '?'。 */\n name: string;\n /** 参数预览(reducer 已按显示宽度截断);委派时为目标 agentKey。 */\n argsPreview: string;\n success: boolean;\n elapsedMs: number;\n /** 委派调用标记。 */\n isDelegation?: boolean;\n /** 归属父 tool_use(顶层直接工具为 null)。 */\n parentToolUseId: string | null;\n}\n\nexport interface OneShotResult {\n conversationId: string;\n streamId: string;\n traceId: string | null;\n /** 最终助手回答正文(ROOT 段文本;委派 specialist 的中间文本不计入)。 */\n content: string;\n toolCalls: OneShotToolCall[];\n elapsedMs: number;\n events: StreamEventEnvelope[];\n /** 终态原因(v4 message.stop.stopReason);未见终态时 undefined。 */\n stopReason?: string;\n /** 本轮总 token(v4 message.stop.totalTokens)。 */\n tokens?: number;\n}\n\nexport interface OneShotOpts extends SendMessageOpts {\n collectEvents?: boolean;\n}\n\n/**\n * 非流式单发:消费 v4 事件流并聚合出最终结果。\n *\n * 🔴 与流式路径同源:把事件喂进 {@link createV4Reducer}(与 CLI streaming、Web 端 fold\n * 状态机一致的 v4 语义层),用一个 headless sink 累加 ROOT 文本 + 收集工具调用——而非\n * 手搓第二套聚合。历史 bug:旧实现监听已废弃的 v3 事件名(response.part.delta /\n * response.completed / response.tool.*),后端早改发 v4(block.delta:text_delta /\n * message.stop)→ switch 全不命中 → content 恒空(--no-stream / --output json 拿到空答案)。\n * 语义层复用见 ADR-129 D2:只换「画」的层,聚合逻辑不重复实现。\n *\n * 终态由 stream-iterator 在 ROOT message.stop(或断连)关闭 async iterator 驱动,本函数\n * 无需自判终止。v3 残余 `response.failed`(仅老路径出现)仍显式抛 CliError 保留旧语义。\n */\nexport async function oneShot(\n ws: WsClient,\n prompt: string,\n opts: OneShotOpts = {},\n): Promise<OneShotResult> {\n const t0 = Date.now();\n const { conversationId, events } = await sendMessageAndStream(ws, prompt, opts);\n\n let content = '';\n let streamId = '';\n let traceId: string | null = null;\n let stopReason: string | undefined;\n let tokens: number | undefined;\n const toolCalls: OneShotToolCall[] = [];\n const collected: StreamEventEnvelope[] = [];\n\n // reducer sink 无 block.start→tool.result 的 callId 内联参数,故按 callId 暂存预览 /\n // 委派 agentKey,待 result / delegationEnd 到达时回填。\n const argsPreviewByCallId = new Map<string, string>();\n const agentKeyByCallId = new Map<string, string>();\n\n const sink: V4RenderSink = {\n // ROOT 段文本才是最终回答;specialist 子流文本(parent 非空)是中间产物,不计入 content。\n onText(text, parentToolUseId) {\n if (parentToolUseId === null) content += text;\n },\n onThinkingDelta() {},\n onThinkingDone() {},\n onToolStarted(callId, _toolName, argsPreview) {\n argsPreviewByCallId.set(callId, argsPreview);\n },\n onToolResult(callId, success, durMs, _childConversationId, parentToolUseId, _nextSteps, toolName) {\n toolCalls.push({\n name: toolName ?? '?',\n argsPreview: argsPreviewByCallId.get(callId) ?? '',\n success,\n elapsedMs: durMs,\n parentToolUseId,\n });\n argsPreviewByCallId.delete(callId);\n },\n onContentPart() {},\n onSteeringApplied() {},\n onDelegationStart(agentKey, parentToolUseId) {\n agentKeyByCallId.set(parentToolUseId, agentKey);\n },\n onDelegationEnd(callId, success, durMs) {\n toolCalls.push({\n name: 'hcm_delegate',\n argsPreview: agentKeyByCallId.get(callId) ?? '',\n success,\n elapsedMs: durMs,\n isDelegation: true,\n parentToolUseId: null,\n });\n agentKeyByCallId.delete(callId);\n },\n onDelegationPaused() {},\n onTerminal(reason, tk) {\n stopReason = reason;\n tokens = tk;\n },\n };\n\n const reducer = createV4Reducer(sink);\n\n for await (const ev of events) {\n if (opts.collectEvents) collected.push(ev);\n if (!streamId) streamId = ev.streamId;\n if (!traceId) traceId = ev.traceId;\n\n // v3 残余终态:仅老后端路径出现,v4 无此事件。保留旧语义——失败即抛。\n if (ev.type === 'response.failed') {\n throw new CliError({\n code: CliErrorCode.BUSINESS_RULE_VIOLATION,\n message: (ev.payload as ResponseFailedPayload).message,\n traceId: traceId ?? undefined,\n });\n }\n\n reducer.handle(ev.type, ev.payload as Record<string, unknown>);\n }\n\n return {\n conversationId,\n streamId,\n traceId,\n content,\n toolCalls,\n elapsedMs: Date.now() - t0,\n events: collected,\n stopReason,\n tokens,\n };\n}\n","import * as fs from 'node:fs/promises';\nimport { conversationStateFile, profileDir } from '../config/paths';\n\nexport interface ConversationState {\n lastConversationId?: string;\n lastStreamId?: string;\n lastTraceId?: string;\n lastAgentKey?: string;\n updatedAt: string;\n}\n\nexport async function loadConversationState(profile: string): Promise<ConversationState | null> {\n try {\n const raw = await fs.readFile(conversationStateFile(profile), 'utf-8');\n return JSON.parse(raw) as ConversationState;\n } catch (e) {\n if ((e as NodeJS.ErrnoException).code === 'ENOENT') return null;\n throw e;\n }\n}\n\nexport async function saveConversationState(\n profile: string,\n state: ConversationState,\n): Promise<void> {\n await fs.mkdir(profileDir(profile), { recursive: true });\n await fs.writeFile(\n conversationStateFile(profile),\n JSON.stringify({ ...state, updatedAt: new Date().toISOString() }, null, 2),\n );\n}\n","import type { AxiosInstance } from 'axios';\n\n/** Default analyst agent id (seeded in HCM at tenant=__SYSTEM__). */\nexport const DEFAULT_AGENT_ID = '019cca00-0000-7000-0000-000000000001';\n\nexport interface CreatedConversation {\n id: string;\n agentId: string;\n title?: string;\n}\n\n/**\n * Create a new conversation in HCM. Required before sending any /ws/conversation\n * message — the WS handler looks up conversation→agentId in DB.\n */\nexport async function createConversation(\n http: AxiosInstance,\n agentId: string = DEFAULT_AGENT_ID,\n): Promise<CreatedConversation> {\n const resp = await http.post('/api/models/ConversationModel', { agentId });\n return {\n id: resp.data.id,\n agentId: resp.data.agentId,\n title: resp.data.title,\n };\n}\n","import type { AxiosInstance } from 'axios';\nimport { truncateDisplay } from './display-width';\n\n/**\n * 续会话回放(CLI 做透轮刀⑤,对标 Claude Code resume 的上下文可见性)。\n *\n * hcm chat 只有显式 --resume 续接 conversation 时才回放历史;否则默认开新会话。\n * 本模块拉\n * timeline(GET /api/models/ConversationModel/{id}/action/timeline,与 Web 回放\n * 同源)折出轻量摘要:最近 N 轮 user/assistant 文本 + 更早轮次计数。\n */\n\n/** timeline 条目(后端 TimelineItemDto 的 CLI 侧投影,只取回放需要的字段)。 */\nexport interface TimelineItemLite {\n kind: string;\n seq: number;\n /** user-msg / steering 的原文。 */\n text?: string | null;\n /** chat-text 的正文。 */\n content?: string | null;\n /** user-msg 附件元数据(#1849 journal 单源透传;issue #64 回放 📎)。 */\n attachments?: Array<{ fileName?: string }> | null;\n}\n\nexport interface ResumeTurn {\n role: 'user' | 'assistant';\n text: string;\n}\n\nexport interface ResumeSummary {\n conversationId: string;\n /** 会话总轮数(user-msg 条目数)。 */\n totalRounds: number;\n /** 最近 maxRounds 轮的 user/assistant 文本(已按显示宽度截断为单行摘要)。 */\n recent: ResumeTurn[];\n}\n\nfunction normalizeTimelineItems(body: unknown): TimelineItemLite[] | null {\n if (Array.isArray(body)) return body as TimelineItemLite[];\n if (\n body\n && typeof body === 'object'\n && Array.isArray((body as { data?: unknown }).data)\n ) {\n return (body as { data: TimelineItemLite[] }).data;\n }\n return null;\n}\n\n/**\n * 拉取 conversation timeline。任何失败(网络/404/结构异常)返回 null——\n * 回放是体验增强,绝不阻断聊天主流程。\n */\nexport async function fetchConversationTimeline(\n http: AxiosInstance,\n conversationId: string,\n): Promise<TimelineItemLite[] | null> {\n try {\n const resp = await http.get(\n `/api/models/ConversationModel/${conversationId}/action/timeline`);\n return normalizeTimelineItems(resp.data);\n } catch {\n return null;\n }\n}\n\n/**\n * 严格拉取 conversation timeline。用于 CLI 显式 inspect 场景:网络/404/结构异常都应暴露,\n * 不能像 resume 体验增强那样静默吞掉。\n */\nexport async function fetchConversationTimelineStrict(\n http: AxiosInstance,\n conversationId: string,\n): Promise<TimelineItemLite[]> {\n const resp = await http.get(\n `/api/models/ConversationModel/${conversationId}/action/timeline`);\n const items = normalizeTimelineItems(resp.data);\n if (!items) {\n throw new Error(`Conversation timeline response is not an array: ${conversationId}`);\n }\n return items;\n}\n\n/**\n * timeline → 续会话摘要。取最近 maxRounds 轮:每轮 = user-msg 原文 + 该轮最后一条\n * chat-text 正文(中间 tools/delegation/steering 等过程条目不进摘要——回顾要的是\n * 「聊到哪了」不是过程重放)。文本截为单行(换行折叠 + 显示宽度截断)。\n */\nexport function buildResumeSummary(\n conversationId: string,\n items: TimelineItemLite[],\n maxRounds: number = 2,\n maxWidth: number = 76,\n): ResumeSummary | null {\n if (!Array.isArray(items) || items.length === 0) return null;\n // 按 user-msg 切轮:每个 user-msg 开启一轮,轮内最后一条 chat-text 是该轮 assistant 收尾。\n interface Round { user: string; assistant: string | null }\n const rounds: Round[] = [];\n for (const it of items) {\n if (it.kind === 'user-msg' && it.text && it.text.trim()) {\n const atts = (it.attachments ?? []).map((a) => a?.fileName).filter(Boolean);\n const suffix = atts.length ? ` 📎 ${atts.join(' · ')}` : '';\n rounds.push({ user: it.text + suffix, assistant: null });\n } else if (it.kind === 'chat-text' && it.content && it.content.trim() && rounds.length > 0) {\n const last = rounds[rounds.length - 1];\n if (last) last.assistant = it.content;\n }\n }\n if (rounds.length === 0) return null;\n\n const oneLine = (s: string): string =>\n truncateDisplay(s.replace(/\\s*\\n\\s*/g, ' ').trim(), maxWidth);\n\n const recent: ResumeTurn[] = [];\n for (const r of rounds.slice(-maxRounds)) {\n recent.push({ role: 'user', text: oneLine(r.user) });\n if (r.assistant) recent.push({ role: 'assistant', text: oneLine(r.assistant) });\n }\n return { conversationId, totalRounds: rounds.length, recent };\n}\n\nexport function findLastAssistantSeq(items: TimelineItemLite[] | null | undefined): number | null {\n const last = [...(items ?? [])].reverse()\n .find((it) => it.kind === 'chat-text' && Number.isInteger(it.seq));\n return last?.seq ?? null;\n}\n","import type { AxiosInstance } from 'axios';\nimport { query } from '../models/query';\n\nexport interface ConversationListItem {\n id?: string;\n title?: string | null;\n summary?: string | null;\n agentId?: string | null;\n messageCount?: number | null;\n totalTokens?: number | null;\n lastActiveAt?: string | null;\n lastMessagePreview?: string | null;\n createTime?: string | null;\n updateTime?: string | null;\n}\n\nexport interface ConversationListResult {\n rows: ConversationListItem[];\n total?: number;\n}\n\nexport async function fetchRecentConversations(\n http: AxiosInstance,\n limit: number = 10,\n): Promise<ConversationListResult> {\n const result = await query<ConversationListItem>(http, 'ConversationModel', {\n filter: { origin: 'USER_CHAT', archived: false },\n fields: [\n 'id',\n 'title',\n 'summary',\n 'agentId',\n 'messageCount',\n 'totalTokens',\n 'lastActiveAt',\n 'lastMessagePreview',\n 'createTime',\n 'updateTime',\n ],\n sort: [{ field: 'lastActiveAt', direction: 'desc' }],\n limit,\n });\n return { rows: result.rows, total: result.total };\n}\n","export const MINI_CONTEXT_FILE = 'hcm.context.json';\nexport const MINI_STATE_DIR = '.hcm';\nexport const MINI_PULL_MANIFEST_FILE = 'mini-manifest.json';\nexport const MINI_REMOTE_ROOT = 'mini_apps';\nexport const MINI_CONTEXT_KIND = 'hcm-mini-app';\nexport const MINI_CONTEXT_VERSION = 1;\n\nexport type MiniSurface = 'embedded' | 'standalone';\nexport type MiniTemplateKind = 'dashboard' | 'multi-page' | 'writable-workbench';\n\nexport interface WorkspaceFileItem {\n id: string;\n workspaceKey: string;\n relativePath: string;\n fileType?: string;\n fileSize?: number;\n lastModified?: number | string | null;\n}\n\nexport interface WorkspaceFileContent {\n workspaceKey: string;\n relativePath: string;\n content: string;\n fileType?: string;\n}\n\nexport interface WorkspaceFileListResult {\n data: WorkspaceFileItem[];\n total: number;\n}\n\nexport interface MiniProjectContext {\n kind: typeof MINI_CONTEXT_KIND;\n version: typeof MINI_CONTEXT_VERSION;\n appCode: string;\n workspaceKey: string;\n remotePrefix: string;\n tenantId?: string;\n endpoint?: string;\n profile?: string;\n updatedAt: string;\n}\n\nexport interface MiniFileSnapshot {\n localPath: string;\n remotePath: string;\n fileId?: string;\n sha256: string;\n size: number;\n lastModified?: number | string | null;\n}\n\nexport interface MiniPullManifest {\n version: typeof MINI_CONTEXT_VERSION;\n appCode: string;\n workspaceKey: string;\n remotePrefix: string;\n profile?: string;\n pulledAt: string;\n files: MiniFileSnapshot[];\n}\n\nexport interface MiniLocalFile {\n localPath: string;\n absolutePath: string;\n content: string;\n sha256: string;\n size: number;\n}\n\nexport type MiniLocalStatus = 'added' | 'modified' | 'deleted' | 'unchanged';\n\nexport interface MiniStatusEntry {\n localPath: string;\n remotePath: string;\n status: MiniLocalStatus;\n fileId?: string;\n sha256?: string;\n baseSha256?: string;\n}\n\nexport interface MiniValidationResult {\n valid: boolean;\n appCode?: string;\n workspaceKey?: string;\n errors: string[];\n warnings: string[];\n}\n\nexport interface MiniPullResult {\n appCode: string;\n workspaceKey: string;\n targetDir: string;\n files: MiniFileSnapshot[];\n}\n\nexport type MiniPushOperation = 'create' | 'update' | 'delete' | 'skip';\n\nexport interface MiniPushPlanItem {\n operation: MiniPushOperation;\n localPath: string;\n remotePath: string;\n fileId?: string;\n reason?: string;\n}\n\nexport interface MiniPushConflict {\n localPath: string;\n remotePath: string;\n reason: string;\n}\n\nexport interface MiniPushResult {\n appCode: string;\n workspaceKey: string;\n dryRun: boolean;\n operations: MiniPushPlanItem[];\n conflicts: MiniPushConflict[];\n}\n\nexport interface MiniPreviewTarget {\n key: 'bare' | 'standalone' | 'embedded';\n name: string;\n url: string;\n}\n\nexport type MiniSmokeTargetKind = 'preview' | 'manifest' | 'asset';\n\nexport interface MiniSmokeTargetResult {\n kind: MiniSmokeTargetKind;\n surface?: MiniPreviewTarget['key'];\n url: string;\n status: number | null;\n ok: boolean;\n error?: string;\n}\n\nexport interface MiniSmokeResult {\n ok: boolean;\n checked: number;\n passed: number;\n failed: number;\n targets: MiniSmokeTargetResult[];\n}\n\nexport type MiniVerifyNextAction =\n | 'push'\n | 'fix-validation'\n | 'resolve-conflict'\n | 'inspect-status'\n | 'inspect-preview';\n\nexport interface MiniVerifyPlan {\n operations: MiniPushPlanItem[];\n conflicts: MiniPushConflict[];\n}\n\nexport interface MiniVerifyReport {\n ok: boolean;\n validate: MiniValidationResult;\n status: MiniStatusEntry[];\n plan: MiniVerifyPlan | null;\n planSkipped?: string;\n preview: MiniPreviewTarget[];\n smoke?: MiniSmokeResult | null;\n}\n\nexport interface MiniVerifySummary {\n validate: {\n ok: boolean;\n errors: number;\n warnings: number;\n appCode?: string;\n workspaceKey?: string;\n };\n status: {\n total: number;\n changed: number;\n added: number;\n modified: number;\n deleted: number;\n unchanged: number;\n };\n dryRun: {\n ok: boolean;\n skipped: boolean;\n skippedReason?: string;\n operations: number;\n creates: number;\n updates: number;\n deletes: number;\n skips: number;\n conflicts: number;\n };\n preview: {\n ok: boolean;\n count: number;\n surfaces: MiniPreviewTarget['key'][];\n };\n}\n\nexport interface MiniVerifyChangedFile {\n path: string;\n status: MiniLocalStatus;\n remotePath: string;\n}\n\nexport interface MiniVerifyPreviewUrl {\n surface: MiniPreviewTarget['key'];\n name: string;\n url: string;\n}\n\nexport interface MiniVerifyBrowserAcceptance {\n recommendedUrl?: string;\n expectedTexts: string[];\n expectedNetwork: string[];\n console: {\n failOn: Array<'error' | 'warn'>;\n };\n}\n\nexport interface MiniVerifyAgentReport {\n ok: boolean;\n safeToPush: boolean;\n nextAction: MiniVerifyNextAction;\n summary: MiniVerifySummary;\n changedFiles: MiniVerifyChangedFile[];\n conflicts: MiniPushConflict[];\n previewUrls: MiniVerifyPreviewUrl[];\n browserAcceptance: MiniVerifyBrowserAcceptance;\n smoke: MiniSmokeResult | null;\n}\n","import type { AxiosInstance } from 'axios';\nimport type { WorkspaceFileContent, WorkspaceFileItem, WorkspaceFileListResult } from './types';\n\nconst MODEL = 'TenantExtensionWorkspaceFile';\n\nexport async function listWorkspaceFiles(\n http: AxiosInstance,\n workspaceKey: string,\n options: { pageSize?: number } = {},\n): Promise<WorkspaceFileListResult> {\n const resp = await http.get(`/api/models/${MODEL}`, {\n params: {\n state: 'workspace',\n filter: `workspaceKey:eq:${workspaceKey}`,\n page: 0,\n size: options.pageSize ?? 1000,\n order: 'relativePath:asc',\n },\n });\n const content = Array.isArray(resp.data?.content) ? resp.data.content : [];\n return {\n data: content.map(toWorkspaceFileItem),\n total: numberOr(resp.data?.totalElements, content.length),\n };\n}\n\nexport async function loadWorkspaceFileContent(\n http: AxiosInstance,\n fileId: string,\n): Promise<WorkspaceFileContent> {\n const resp = await http.get(`/api/models/${MODEL}/${encodeURIComponent(fileId)}/action/load-content`);\n return toWorkspaceFileContent(unwrapActionData(resp.data));\n}\n\nexport async function saveWorkspaceFileContent(\n http: AxiosInstance,\n fileId: string,\n content: string,\n): Promise<WorkspaceFileItem> {\n const resp = await http.post(\n `/api/models/${MODEL}/${encodeURIComponent(fileId)}/action/save-content`,\n { form: { content } },\n );\n return toWorkspaceFileItem(unwrapActionData(resp.data));\n}\n\nexport async function createWorkspaceFile(\n http: AxiosInstance,\n workspaceKey: string,\n relativePath: string,\n content: string,\n): Promise<WorkspaceFileItem> {\n const resp = await http.post(`/api/models/${MODEL}`, {\n workspaceKey,\n relativePath,\n content,\n });\n return toWorkspaceFileItem(resp.data);\n}\n\nexport async function deleteWorkspaceFile(http: AxiosInstance, fileId: string): Promise<void> {\n await http.delete(`/api/models/${MODEL}/${encodeURIComponent(fileId)}`);\n}\n\nfunction unwrapActionData(value: unknown): unknown {\n if (value && typeof value === 'object' && 'data' in value) {\n return (value as { data?: unknown }).data;\n }\n return value;\n}\n\nfunction toWorkspaceFileItem(value: unknown): WorkspaceFileItem {\n const obj = asRecord(value);\n return {\n id: stringOr(obj.id),\n workspaceKey: stringOr(obj.workspaceKey),\n relativePath: stringOr(obj.relativePath),\n fileType: stringOrUndefined(obj.fileType),\n fileSize: numberOr(obj.fileSize, 0),\n lastModified: primitiveOrNull(obj.lastModified),\n };\n}\n\nfunction toWorkspaceFileContent(value: unknown): WorkspaceFileContent {\n const obj = asRecord(value);\n return {\n workspaceKey: stringOr(obj.workspaceKey),\n relativePath: stringOr(obj.relativePath),\n content: stringOr(obj.content),\n fileType: stringOrUndefined(obj.fileType),\n };\n}\n\nfunction asRecord(value: unknown): Record<string, unknown> {\n return value && typeof value === 'object' ? (value as Record<string, unknown>) : {};\n}\n\nfunction stringOr(value: unknown): string {\n return typeof value === 'string' ? value : value == null ? '' : String(value);\n}\n\nfunction stringOrUndefined(value: unknown): string | undefined {\n return value == null ? undefined : stringOr(value);\n}\n\nfunction numberOr(value: unknown, fallback: number): number {\n return typeof value === 'number' && Number.isFinite(value) ? value : fallback;\n}\n\nfunction primitiveOrNull(value: unknown): number | string | null {\n if (typeof value === 'number' || typeof value === 'string') return value;\n return null;\n}\n","import * as crypto from 'node:crypto';\nimport type { Dirent } from 'node:fs';\nimport * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport type { AxiosInstance } from 'axios';\nimport { CliError, CliErrorCode } from '../errors';\nimport {\n createWorkspaceFile,\n deleteWorkspaceFile,\n listWorkspaceFiles,\n loadWorkspaceFileContent,\n saveWorkspaceFileContent,\n} from './workspace-files';\nimport {\n MINI_CONTEXT_FILE,\n MINI_CONTEXT_KIND,\n MINI_CONTEXT_VERSION,\n MINI_PULL_MANIFEST_FILE,\n MINI_REMOTE_ROOT,\n MINI_STATE_DIR,\n type MiniFileSnapshot,\n type MiniPushOperation,\n type MiniLocalFile,\n type MiniPreviewTarget,\n type MiniProjectContext,\n type MiniPullManifest,\n type MiniPullResult,\n type MiniSmokeResult,\n type MiniSmokeTargetResult,\n type MiniVerifyAgentReport,\n type MiniVerifyBrowserAcceptance,\n type MiniVerifyChangedFile,\n type MiniVerifyNextAction,\n type MiniVerifyPreviewUrl,\n type MiniVerifyReport,\n type MiniPushConflict,\n type MiniPushPlanItem,\n type MiniPushResult,\n type MiniStatusEntry,\n type MiniSurface,\n type MiniTemplateKind,\n type MiniValidationResult,\n type WorkspaceFileContent,\n type WorkspaceFileItem,\n} from './types';\nimport {\n buildMiniAppTemplateFiles as buildMiniAppTemplateFileSet,\n defaultMiniRequiredScopes,\n} from './templates';\n\nconst MINI_APP_CODE_PATTERN = /^[a-z][a-z0-9-]*$/;\nconst SCOPE_PATTERN = /^[A-Z][A-Za-z0-9_]*\\.[a-z][A-Za-z0-9]*$/;\nconst SEMVER_PATTERN =\n /^(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)(-[0-9A-Za-z.-]+)?(\\+[0-9A-Za-z.-]+)?$/;\nconst TOP_LEVEL_MANIFEST_KEYS = new Set([\n 'schemaVersion',\n 'code',\n 'name',\n 'version',\n 'sdkVersion',\n 'surfaces',\n 'defaultSurface',\n 'entry',\n 'bundleObjectRef',\n 'requiredScopes',\n]);\nconst ENTRY_KEYS = new Set(['html', 'css', 'js']);\nconst EXCLUDED_DIRS = new Set(['.git', MINI_STATE_DIR, 'node_modules', 'dist']);\nconst MINI_SDK_SCOPE_METHODS: Record<string, string> = {\n query: 'list',\n get: 'get',\n create: 'create',\n update: 'update',\n remove: 'delete',\n};\nconst MINI_MODEL_CONST_PATTERN = /\\bconst\\s+([A-Z][A-Za-z0-9_]*)\\s*=\\s*['\"]([A-Z][A-Za-z0-9_]*)['\"]\\s*;/g;\nconst MINI_SDK_MODEL_CALL_PATTERN =\n /\\bhcm\\.(query|get|create|update|remove)\\s*\\(\\s*\\{[\\s\\S]*?\\bmodel\\s*:\\s*(?:['\"]([A-Z][A-Za-z0-9_]*)['\"]|([A-Z][A-Za-z0-9_]*))/g;\nconst MINI_SDK_ACTION_CALL_PATTERN =\n /\\bhcm\\.action\\s*\\(\\s*\\{[\\s\\S]*?\\bmodel\\s*:\\s*(?:['\"]([A-Z][A-Za-z0-9_]*)['\"]|([A-Z][A-Za-z0-9_]*))[\\s\\S]*?\\baction\\s*:\\s*['\"]([a-z][A-Za-z0-9]*)['\"]/g;\n\nexport interface MiniInitArgs {\n appCode: string;\n workspaceKey: string;\n targetDir: string;\n name?: string;\n defaultSurface?: MiniSurface;\n templateKind?: MiniTemplateKind;\n writableModel?: string;\n writableFields?: string[] | null;\n writableDefaults?: Record<string, unknown>;\n writableTodayDefaultFields?: string[];\n requiredScopes?: string[];\n tenantId?: string;\n endpoint?: string;\n profile?: string;\n force?: boolean;\n}\n\nexport interface MiniPullArgs {\n http: AxiosInstance;\n appCode: string;\n workspaceKey: string;\n targetDir: string;\n tenantId?: string;\n endpoint?: string;\n profile?: string;\n force?: boolean;\n}\n\nexport interface MiniPushArgs {\n http: AxiosInstance;\n rootDir: string;\n dryRun?: boolean;\n}\n\nexport async function initMiniAppProject(args: MiniInitArgs): Promise<MiniPullResult> {\n const appCode = normalizeAppCode(args.appCode);\n const workspaceKey = normalizeWorkspaceKey(args.workspaceKey);\n const targetDir = path.resolve(args.targetDir);\n const remotePrefix = miniRemotePrefix(appCode);\n const templateKind = args.templateKind ?? 'dashboard';\n const files = buildMiniAppTemplateFiles({\n appCode,\n name: args.name,\n defaultSurface: args.defaultSurface ?? 'embedded',\n templateKind,\n writableModel: args.writableModel,\n writableFields: args.writableFields,\n writableDefaults: args.writableDefaults,\n writableTodayDefaultFields: args.writableTodayDefaultFields,\n requiredScopes: args.requiredScopes?.length\n ? args.requiredScopes\n : defaultMiniRequiredScopes(templateKind, { model: args.writableModel, fields: args.writableFields }),\n tenantId: args.tenantId ?? '1',\n });\n\n await ensureTargetWritable(\n targetDir,\n files.map((file) => file.localPath).concat([MINI_CONTEXT_FILE]),\n !!args.force,\n );\n await fs.mkdir(targetDir, { recursive: true });\n for (const file of files) {\n await writeText(path.join(targetDir, file.localPath), file.content);\n }\n\n const context: MiniProjectContext = {\n kind: MINI_CONTEXT_KIND,\n version: MINI_CONTEXT_VERSION,\n appCode,\n workspaceKey,\n remotePrefix,\n tenantId: args.tenantId,\n endpoint: args.endpoint,\n profile: args.profile,\n updatedAt: new Date().toISOString(),\n };\n await writeJson(path.join(targetDir, MINI_CONTEXT_FILE), context);\n\n const localFiles = await readLocalMiniFiles(targetDir);\n await writeMiniState(targetDir, context, [], new Map());\n return {\n appCode,\n workspaceKey,\n targetDir,\n files: localFiles.map((file) => toSnapshot(context, file)),\n };\n}\n\nexport async function pullMiniAppProject(args: MiniPullArgs): Promise<MiniPullResult> {\n const appCode = normalizeAppCode(args.appCode);\n const workspaceKey = normalizeWorkspaceKey(args.workspaceKey);\n const targetDir = path.resolve(args.targetDir);\n const remotePrefix = miniRemotePrefix(appCode);\n const remoteFiles = await loadRemoteMiniAppFiles(args.http, workspaceKey, appCode);\n if (!remoteFiles.length) {\n throw new CliError({\n code: CliErrorCode.BUSINESS_RULE_VIOLATION,\n message: `mini app '${appCode}' not found in workspace '${workspaceKey}'`,\n });\n }\n\n const pullFiles = remoteFiles\n .filter((file) => !isDirectoryMarkerPath(file.item.relativePath))\n .map((file) => ({\n localPath: localPathFromRemote(remotePrefix, file.item.relativePath),\n content: file.content.content,\n remote: file.item,\n }));\n await ensureTargetWritable(\n targetDir,\n pullFiles.map((file) => file.localPath).concat([MINI_CONTEXT_FILE]),\n !!args.force,\n );\n await fs.mkdir(targetDir, { recursive: true });\n for (const file of pullFiles) {\n await writeText(path.join(targetDir, file.localPath), file.content);\n }\n\n const context: MiniProjectContext = {\n kind: MINI_CONTEXT_KIND,\n version: MINI_CONTEXT_VERSION,\n appCode,\n workspaceKey,\n remotePrefix,\n tenantId: args.tenantId,\n endpoint: args.endpoint,\n profile: args.profile,\n updatedAt: new Date().toISOString(),\n };\n await writeJson(path.join(targetDir, MINI_CONTEXT_FILE), context);\n\n const localFiles = await readLocalMiniFiles(targetDir);\n const remoteByLocalPath = new Map(\n pullFiles.map((file) => [file.localPath, file.remote] as const),\n );\n await writeMiniState(targetDir, context, localFiles, remoteByLocalPath);\n const manifest = await readPullManifest(targetDir);\n return {\n appCode,\n workspaceKey,\n targetDir,\n files: manifest.files,\n };\n}\n\nexport async function getMiniStatus(rootDir: string): Promise<MiniStatusEntry[]> {\n const root = path.resolve(rootDir);\n const context = await readMiniContext(root);\n const manifest = await readPullManifest(root);\n const localFiles = await readLocalMiniFiles(root);\n return computeStatusEntries(context, manifest, localFiles);\n}\n\nexport async function validateMiniProject(rootDir: string): Promise<MiniValidationResult> {\n const root = path.resolve(rootDir);\n const errors: string[] = [];\n const warnings: string[] = [];\n let context: MiniProjectContext | null = null;\n\n try {\n context = await readMiniContext(root);\n } catch (e) {\n errors.push((e as Error).message);\n }\n\n const manifestPath = path.join(root, 'manifest.json');\n let manifest: Record<string, unknown> | null = null;\n try {\n manifest = JSON.parse(await fs.readFile(manifestPath, 'utf-8')) as Record<string, unknown>;\n } catch (e) {\n if ((e as NodeJS.ErrnoException).code === 'ENOENT') {\n errors.push('manifest.json is required');\n } else {\n errors.push(`manifest.json is not valid JSON: ${(e as Error).message}`);\n }\n }\n\n if (context) {\n if (!MINI_APP_CODE_PATTERN.test(context.appCode)) {\n errors.push('appCode must match ^[a-z][a-z0-9-]*$');\n }\n if (context.remotePrefix !== miniRemotePrefix(context.appCode)) {\n errors.push(`context remotePrefix must be ${miniRemotePrefix(context.appCode)}`);\n }\n }\n\n if (manifest) {\n await validateManifestObject(manifest, context, root, errors, warnings);\n }\n\n return {\n valid: errors.length === 0,\n appCode: context?.appCode,\n workspaceKey: context?.workspaceKey,\n errors,\n warnings,\n };\n}\n\nexport async function pushMiniAppProject(args: MiniPushArgs): Promise<MiniPushResult> {\n const root = path.resolve(args.rootDir);\n const validation = await validateMiniProject(root);\n if (!validation.valid) {\n throw new CliError({\n code: CliErrorCode.INVALID_ARGUMENT,\n message: `mini project validation failed: ${validation.errors.join('; ')}`,\n });\n }\n\n const context = await readMiniContext(root);\n const manifest = await readPullManifest(root);\n const localFiles = await readLocalMiniFiles(root);\n const remoteFiles = await loadRemoteMiniAppFiles(args.http, context.workspaceKey, context.appCode);\n const plan = buildPushPlan(context, manifest, localFiles, remoteFiles);\n\n const result: MiniPushResult = {\n appCode: context.appCode,\n workspaceKey: context.workspaceKey,\n dryRun: !!args.dryRun,\n operations: plan.operations,\n conflicts: plan.conflicts,\n };\n if (plan.conflicts.length || args.dryRun) {\n return result;\n }\n\n const localByPath = new Map(localFiles.map((file) => [file.localPath, file] as const));\n for (const operation of plan.operations) {\n if (operation.operation === 'skip') continue;\n if (operation.operation === 'delete') {\n if (operation.fileId) await deleteWorkspaceFile(args.http, operation.fileId);\n continue;\n }\n const localFile = localByPath.get(operation.localPath);\n if (!localFile) continue;\n if (operation.operation === 'create') {\n await createWorkspaceFile(\n args.http,\n context.workspaceKey,\n operation.remotePath,\n localFile.content,\n );\n }\n if (operation.operation === 'update' && operation.fileId) {\n await saveWorkspaceFileContent(args.http, operation.fileId, localFile.content);\n }\n }\n\n const latestRemoteFiles = await listWorkspaceFiles(args.http, context.workspaceKey, {\n pageSize: 5000,\n });\n const remoteByLocalPath = new Map<string, WorkspaceFileItem>();\n for (const remote of latestRemoteFiles.data) {\n if (!remote.relativePath.startsWith(context.remotePrefix + '/')) continue;\n remoteByLocalPath.set(localPathFromRemote(context.remotePrefix, remote.relativePath), remote);\n }\n await writeMiniState(root, context, localFiles, remoteByLocalPath);\n return result;\n}\n\nexport async function buildMiniPreviewTargets(\n rootDir: string,\n frontendBaseUrl: string,\n): Promise<MiniPreviewTarget[]> {\n const context = await readMiniContext(path.resolve(rootDir));\n const app = encodeURIComponent(context.appCode);\n const workspace = encodeURIComponent(context.workspaceKey);\n const base = frontendBaseUrl.replace(/\\/+$/, '');\n return [\n {\n key: 'bare',\n name: 'bare runtime',\n url: `${base}/mini.html?app=${app}&workspaceKey=${workspace}`,\n },\n {\n key: 'standalone',\n name: 'standalone page',\n url: `${base}/mini/${app}?workspaceKey=${workspace}`,\n },\n {\n key: 'embedded',\n name: 'embedded page',\n url: `${base}/custom/${app}?workspaceKey=${workspace}`,\n },\n ];\n}\n\nexport interface MiniSmokeFetchResponse {\n status: number;\n}\n\nexport type MiniSmokeFetch = (\n url: string,\n init?: { headers?: Record<string, string>; method: 'GET'; signal?: AbortSignal },\n) => Promise<MiniSmokeFetchResponse>;\n\nexport interface MiniSmokeCheckArgs {\n rootDir: string;\n frontendBaseUrl: string;\n previewTargets: MiniPreviewTarget[];\n fetcher?: MiniSmokeFetch;\n accessToken?: string | null;\n timeoutMs?: number;\n}\n\nexport async function runMiniSmokeChecks(args: MiniSmokeCheckArgs): Promise<MiniSmokeResult> {\n const context = await readMiniContext(path.resolve(args.rootDir));\n const targets = buildMiniSmokeTargets(context, args.frontendBaseUrl, args.previewTargets);\n const results = await Promise.all(\n targets.map((target) =>\n checkMiniSmokeTarget(\n target,\n args.fetcher ?? defaultSmokeFetch,\n args.timeoutMs ?? 5000,\n args.accessToken,\n context.workspaceKey,\n ),\n ),\n );\n const passed = results.filter((target) => target.ok).length;\n const failed = results.length - passed;\n return {\n ok: failed === 0,\n checked: results.length,\n passed,\n failed,\n targets: results,\n };\n}\n\nfunction buildMiniSmokeTargets(\n context: MiniProjectContext,\n frontendBaseUrl: string,\n previewTargets: MiniPreviewTarget[],\n): MiniSmokeTargetResult[] {\n const base = frontendBaseUrl.replace(/\\/+$/, '');\n const app = encodeURIComponent(context.appCode);\n return [\n ...previewTargets.map(\n (target): MiniSmokeTargetResult => ({\n kind: 'preview',\n surface: target.key,\n url: target.url,\n status: null,\n ok: false,\n }),\n ),\n {\n kind: 'manifest',\n url: `${base}/api/mini-apps/${app}`,\n status: null,\n ok: false,\n },\n ...['index.html', 'index.css', 'index.js'].map(\n (file): MiniSmokeTargetResult => ({\n kind: 'asset',\n url: `${base}/static/mini-apps/${app}/${file}`,\n status: null,\n ok: false,\n }),\n ),\n ];\n}\n\nasync function checkMiniSmokeTarget(\n target: MiniSmokeTargetResult,\n fetcher: MiniSmokeFetch,\n timeoutMs: number,\n accessToken?: string | null,\n workspaceKey?: string | null,\n): Promise<MiniSmokeTargetResult> {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), timeoutMs);\n try {\n const headers =\n target.kind !== 'preview'\n ? {\n ...(accessToken ? { Authorization: `Bearer ${accessToken}` } : {}),\n ...(workspaceKey ? { 'X-Workspace-Key': workspaceKey } : {}),\n }\n : undefined;\n const response = await fetcher(target.url, {\n ...(headers ? { headers } : {}),\n method: 'GET',\n signal: controller.signal,\n });\n const ok = response.status >= 200 && response.status < 400;\n return {\n ...target,\n status: response.status,\n ok,\n error: ok ? undefined : `HTTP ${response.status}`,\n };\n } catch (e) {\n return {\n ...target,\n status: null,\n ok: false,\n error: (e as Error).message || 'request failed',\n };\n } finally {\n clearTimeout(timeout);\n }\n}\n\nasync function defaultSmokeFetch(\n url: string,\n init?: { headers?: Record<string, string>; method: 'GET'; signal?: AbortSignal },\n): Promise<MiniSmokeFetchResponse> {\n if (typeof fetch !== 'function') throw new Error('fetch is not available');\n return fetch(url, init);\n}\n\nexport function buildMiniVerifyAgentReport(report: MiniVerifyReport): MiniVerifyAgentReport {\n const statusCounts = countStatuses(report.status);\n const operationCounts = countOperations(report.plan?.operations ?? []);\n const conflicts = report.plan?.conflicts ?? [];\n const dryRunOk = !!report.plan && conflicts.length === 0;\n const smoke = report.smoke ?? null;\n const smokeOk = !smoke || smoke.ok;\n const previewUrls = report.preview.map(\n (target): MiniVerifyPreviewUrl => ({\n surface: target.key,\n name: target.name,\n url: target.url,\n }),\n );\n const safeToPush = report.validate.valid && dryRunOk && previewUrls.length > 0 && smokeOk;\n const nextAction = chooseMiniVerifyNextAction(report, previewUrls, smoke);\n\n return {\n ok: report.ok && smokeOk,\n safeToPush,\n nextAction,\n summary: {\n validate: {\n ok: report.validate.valid,\n errors: report.validate.errors.length,\n warnings: report.validate.warnings.length,\n appCode: report.validate.appCode,\n workspaceKey: report.validate.workspaceKey,\n },\n status: {\n total: report.status.length,\n changed: statusCounts.added + statusCounts.modified + statusCounts.deleted,\n added: statusCounts.added,\n modified: statusCounts.modified,\n deleted: statusCounts.deleted,\n unchanged: statusCounts.unchanged,\n },\n dryRun: {\n ok: dryRunOk,\n skipped: !report.plan,\n skippedReason: report.plan ? undefined : report.planSkipped,\n operations: operationCounts.create + operationCounts.update + operationCounts.delete,\n creates: operationCounts.create,\n updates: operationCounts.update,\n deletes: operationCounts.delete,\n skips: operationCounts.skip,\n conflicts: conflicts.length,\n },\n preview: {\n ok: previewUrls.length > 0,\n count: previewUrls.length,\n surfaces: previewUrls.map((target) => target.surface),\n },\n },\n changedFiles: report.status\n .filter((entry) => entry.status !== 'unchanged')\n .map(\n (entry): MiniVerifyChangedFile => ({\n path: entry.localPath,\n status: entry.status,\n remotePath: entry.remotePath,\n }),\n ),\n conflicts,\n previewUrls,\n browserAcceptance: buildMiniVerifyBrowserAcceptance(report, previewUrls),\n smoke,\n };\n}\n\nfunction buildMiniVerifyBrowserAcceptance(\n report: MiniVerifyReport,\n previewUrls: MiniVerifyPreviewUrl[],\n): MiniVerifyBrowserAcceptance {\n const recommendedUrl = preferredBrowserPreview(previewUrls)?.url;\n const appCode = report.validate.appCode;\n return {\n recommendedUrl,\n expectedTexts: appCode ? [appCode] : [],\n expectedNetwork: expectedBrowserNetwork(report, recommendedUrl),\n console: {\n failOn: ['error', 'warn'],\n },\n };\n}\n\nfunction preferredBrowserPreview(\n previewUrls: MiniVerifyPreviewUrl[],\n): MiniVerifyPreviewUrl | undefined {\n return (\n previewUrls.find((target) => target.surface === 'standalone') ??\n previewUrls.find((target) => target.surface === 'bare') ??\n previewUrls[0]\n );\n}\n\nfunction expectedBrowserNetwork(report: MiniVerifyReport, recommendedUrl?: string): string[] {\n const smokeUrls = report.smoke?.targets\n .filter((target) => target.kind !== 'preview')\n .map((target) => target.url);\n if (smokeUrls?.length) return Array.from(new Set(smokeUrls));\n\n const appCode = report.validate.appCode;\n if (!recommendedUrl || !appCode) return [];\n try {\n const origin = new URL(recommendedUrl).origin;\n return [\n `${origin}/api/mini-apps/${appCode}`,\n `${origin}/static/mini-apps/${appCode}/index.html`,\n `${origin}/static/mini-apps/${appCode}/index.css`,\n `${origin}/static/mini-apps/${appCode}/index.js`,\n ];\n } catch {\n return [];\n }\n}\n\nfunction chooseMiniVerifyNextAction(\n report: MiniVerifyReport,\n previewUrls: MiniVerifyPreviewUrl[],\n smoke: MiniSmokeResult | null,\n): MiniVerifyNextAction {\n if (smoke && !smoke.ok) return 'inspect-preview';\n if (!report.validate.valid) return 'fix-validation';\n if (!report.plan) return 'inspect-status';\n if (report.plan.conflicts.length) return 'resolve-conflict';\n if (!previewUrls.length) return 'inspect-preview';\n return 'push';\n}\n\nexport function formatMiniVerifyReport(report: MiniVerifyReport): string {\n const totalSteps = report.smoke ? 5 : 4;\n const lines: string[] = [];\n lines.push(`verify: ${report.ok ? 'OK' : 'FAIL'}`);\n lines.push('');\n\n // 1. validate\n lines.push(`[1/${totalSteps}] validate: ${report.validate.valid ? '✓ pass' : '✗ fail'}`);\n if (report.validate.errors.length) {\n for (const err of report.validate.errors) lines.push(` error: ${err}`);\n }\n if (report.validate.warnings.length) {\n for (const warn of report.validate.warnings) lines.push(` warn: ${warn}`);\n }\n\n // 2. status\n const statusBucket = { added: 0, modified: 0, deleted: 0, unchanged: 0 };\n for (const row of report.status) statusBucket[row.status] += 1;\n lines.push(\n `[2/${totalSteps}] status: ${statusBucket.added} added, ${statusBucket.modified} modified, ${statusBucket.deleted} deleted, ${statusBucket.unchanged} unchanged`,\n );\n for (const row of report.status) {\n if (row.status === 'unchanged') continue;\n lines.push(` ${row.status.padEnd(9)} ${row.localPath}`);\n }\n\n // 3. push dry-run\n if (report.plan) {\n if (report.plan.conflicts.length) {\n lines.push(`[3/${totalSteps}] dry-run: ✗ ${report.plan.conflicts.length} conflict(s)`);\n for (const c of report.plan.conflicts) lines.push(` ${c.localPath}: ${c.reason}`);\n } else {\n const opCount = report.plan.operations.filter((op) => op.operation !== 'skip').length;\n lines.push(`[3/${totalSteps}] dry-run: ✓ ${opCount} operation(s) planned (no remote writes)`);\n for (const op of report.plan.operations) {\n if (op.operation === 'skip') continue;\n lines.push(` ${op.operation.padEnd(7)} ${op.localPath}`);\n }\n }\n } else {\n lines.push(`[3/${totalSteps}] dry-run: - skipped (${report.planSkipped ?? 'unknown reason'})`);\n }\n\n // 4. preview URLs\n if (report.preview.length) {\n lines.push(`[4/${totalSteps}] preview:`);\n for (const target of report.preview) lines.push(` ${target.key.padEnd(11)} ${target.url}`);\n } else {\n lines.push(`[4/${totalSteps}] preview: - (no targets)`);\n }\n\n // 5. smoke HTTP checks\n if (report.smoke) {\n const marker = report.smoke.ok ? '✓' : '✗';\n lines.push(\n `[5/5] smoke: ${marker} ${report.smoke.passed}/${report.smoke.checked} passed`,\n );\n for (const target of report.smoke.targets) {\n const targetMarker = target.ok ? '✓' : '✗';\n const label = target.surface ? `${target.kind}:${target.surface}` : target.kind;\n const status = target.status ?? '-';\n const error = target.error ? ` (${target.error})` : '';\n lines.push(` ${targetMarker} ${label.padEnd(18)} ${status} ${target.url}${error}`);\n }\n }\n\n return lines.join('\\n');\n}\n\nfunction countStatuses(status: MiniStatusEntry[]): Record<MiniStatusEntry['status'], number> {\n const counts: Record<MiniStatusEntry['status'], number> = {\n added: 0,\n modified: 0,\n deleted: 0,\n unchanged: 0,\n };\n for (const entry of status) counts[entry.status] += 1;\n return counts;\n}\n\nfunction countOperations(operations: MiniPushPlanItem[]): Record<MiniPushOperation, number> {\n const counts: Record<MiniPushOperation, number> = {\n create: 0,\n update: 0,\n delete: 0,\n skip: 0,\n };\n for (const operation of operations) counts[operation.operation] += 1;\n return counts;\n}\n\nexport async function readMiniContext(rootDir: string): Promise<MiniProjectContext> {\n const file = path.join(rootDir, MINI_CONTEXT_FILE);\n let parsed: unknown;\n try {\n parsed = JSON.parse(await fs.readFile(file, 'utf-8'));\n } catch (e) {\n if ((e as NodeJS.ErrnoException).code === 'ENOENT') {\n throw new CliError({\n code: CliErrorCode.CONFIG_PARSE_ERROR,\n message: `${MINI_CONTEXT_FILE} not found; run hcm workspace add mini <appCode> or hcm workspace pull first`,\n });\n }\n throw new CliError({\n code: CliErrorCode.CONFIG_PARSE_ERROR,\n message: `${MINI_CONTEXT_FILE} is invalid: ${(e as Error).message}`,\n });\n }\n const obj = asRecord(parsed);\n if (obj.kind !== MINI_CONTEXT_KIND || obj.version !== MINI_CONTEXT_VERSION) {\n throw new CliError({\n code: CliErrorCode.CONFIG_PARSE_ERROR,\n message: `${MINI_CONTEXT_FILE} is not an HCM mini app context`,\n });\n }\n return {\n kind: MINI_CONTEXT_KIND,\n version: MINI_CONTEXT_VERSION,\n appCode: normalizeAppCode(stringRequired(obj.appCode, 'appCode')),\n workspaceKey: normalizeWorkspaceKey(stringRequired(obj.workspaceKey, 'workspaceKey')),\n remotePrefix: stringRequired(obj.remotePrefix, 'remotePrefix'),\n tenantId: stringOptional(obj.tenantId),\n endpoint: stringOptional(obj.endpoint),\n profile: stringOptional(obj.profile),\n updatedAt: stringOptional(obj.updatedAt) ?? new Date(0).toISOString(),\n };\n}\n\nexport async function readPullManifest(rootDir: string): Promise<MiniPullManifest> {\n const file = path.join(rootDir, MINI_STATE_DIR, MINI_PULL_MANIFEST_FILE);\n try {\n const parsed = JSON.parse(await fs.readFile(file, 'utf-8')) as MiniPullManifest;\n return {\n version: MINI_CONTEXT_VERSION,\n appCode: parsed.appCode ? normalizeAppCode(parsed.appCode) : '',\n workspaceKey: parsed.workspaceKey ? normalizeWorkspaceKey(parsed.workspaceKey) : '',\n remotePrefix: parsed.remotePrefix ?? '',\n profile: parsed.profile,\n pulledAt: parsed.pulledAt,\n files: Array.isArray(parsed.files) ? parsed.files : [],\n };\n } catch (e) {\n if ((e as NodeJS.ErrnoException).code === 'ENOENT') {\n return {\n version: MINI_CONTEXT_VERSION,\n appCode: '',\n workspaceKey: '',\n remotePrefix: '',\n pulledAt: new Date(0).toISOString(),\n files: [],\n };\n }\n throw e;\n }\n}\n\nasync function validateManifestObject(\n manifest: Record<string, unknown>,\n context: MiniProjectContext | null,\n root: string,\n errors: string[],\n warnings: string[],\n): Promise<void> {\n for (const key of Object.keys(manifest)) {\n if (!TOP_LEVEL_MANIFEST_KEYS.has(key)) errors.push(`manifest has unsupported key: ${key}`);\n }\n requireString(manifest, 'schemaVersion', errors, (value) => value === '1.0');\n requireString(manifest, 'code', errors, (value) => /^[a-zA-Z0-9-]+$/.test(value));\n requireString(manifest, 'name', errors, (value) => value.trim().length > 0);\n requireString(manifest, 'version', errors, (value) => SEMVER_PATTERN.test(value));\n requireString(manifest, 'sdkVersion', errors, (value) => SEMVER_PATTERN.test(value));\n\n const code = typeof manifest.code === 'string' ? manifest.code : '';\n if (context && code && code !== context.appCode) {\n errors.push(`manifest code must equal context appCode: ${context.appCode}`);\n }\n const requiredScopes = asStringArray(manifest.requiredScopes);\n if (!requiredScopes.length) {\n errors.push('manifest requiredScopes must contain at least one scope');\n }\n for (const scope of requiredScopes) {\n if (!SCOPE_PATTERN.test(scope)) {\n errors.push(`manifest requiredScopes has invalid scope: ${scope}`);\n }\n }\n\n const surfaces = asStringArray(manifest.surfaces);\n const defaultSurface = typeof manifest.defaultSurface === 'string' ? manifest.defaultSurface : '';\n if (surfaces.length) {\n for (const surface of surfaces) {\n if (surface !== 'embedded' && surface !== 'standalone') {\n errors.push(`manifest surfaces has invalid value: ${surface}`);\n }\n }\n if (new Set(surfaces).size !== surfaces.length) {\n errors.push('manifest surfaces must be unique');\n }\n } else {\n warnings.push('manifest surfaces is omitted; backend default may apply');\n }\n if (defaultSurface && surfaces.length && !surfaces.includes(defaultSurface)) {\n errors.push('manifest defaultSurface must be declared in surfaces');\n }\n\n const entry = asRecord(manifest.entry);\n if (!Object.keys(entry).length) {\n errors.push('manifest entry is required');\n }\n for (const key of Object.keys(entry)) {\n if (!ENTRY_KEYS.has(key)) errors.push(`manifest entry has unsupported key: ${key}`);\n }\n const html = stringOptional(entry.html);\n const js = stringOptional(entry.js);\n const css = stringOptional(entry.css);\n if (!html && !js) {\n errors.push('manifest entry must define html or js');\n }\n await validateEntryPath(root, 'entry.html', html, '.html', errors);\n await validateEntryPath(root, 'entry.js', js, '.js', errors);\n await validateEntryPath(root, 'entry.css', css, '.css', errors);\n await validateEntryRequiredScopes(root, js, requiredScopes, warnings);\n\n const bundleObjectRef = asRecord(manifest.bundleObjectRef);\n const pathSpec = asRecord(bundleObjectRef.pathSpec);\n const pathSegment = stringOptional(pathSpec.pathSegment);\n if (context && pathSegment && pathSegment !== miniRemotePrefix(context.appCode)) {\n errors.push(\n `manifest bundleObjectRef.pathSpec.pathSegment must be ${miniRemotePrefix(context.appCode)}`,\n );\n }\n}\n\nasync function validateEntryPath(\n root: string,\n field: string,\n value: string | undefined,\n suffix: string,\n errors: string[],\n): Promise<void> {\n if (!value) return;\n if (!value.endsWith(suffix)) errors.push(`${field} must end with ${suffix}`);\n try {\n normalizeRelativePath(value);\n } catch (e) {\n errors.push(`${field} is invalid: ${(e as Error).message}`);\n return;\n }\n try {\n await fs.access(path.join(root, value));\n } catch {\n errors.push(`${field} target file does not exist: ${value}`);\n }\n}\n\nasync function validateEntryRequiredScopes(\n root: string,\n jsPath: string | undefined,\n requiredScopes: string[],\n warnings: string[],\n): Promise<void> {\n if (!jsPath) return;\n const absolutePath = path.resolve(root, jsPath);\n if (!absolutePath.startsWith(root + path.sep)) return;\n let source = '';\n try {\n source = await fs.readFile(absolutePath, 'utf-8');\n } catch {\n return;\n }\n\n const declaredScopes = new Set(requiredScopes);\n for (const scope of inferMiniSdkRequiredScopes(source)) {\n if (!declaredScopes.has(scope)) {\n warnings.push(`manifest requiredScopes may be missing ${scope} for ${jsPath} SDK usage`);\n }\n }\n}\n\nfunction inferMiniSdkRequiredScopes(source: string): string[] {\n const constants = readMiniModelConstants(source);\n const scopes = new Set<string>();\n let match: RegExpExecArray | null;\n\n MINI_SDK_MODEL_CALL_PATTERN.lastIndex = 0;\n while ((match = MINI_SDK_MODEL_CALL_PATTERN.exec(source))) {\n const method = match[1];\n if (!method) continue;\n const model = match[2] ?? constants.get(match[3] ?? '');\n const action = MINI_SDK_SCOPE_METHODS[method];\n if (model && action) scopes.add(`${model}.${action}`);\n }\n\n MINI_SDK_ACTION_CALL_PATTERN.lastIndex = 0;\n while ((match = MINI_SDK_ACTION_CALL_PATTERN.exec(source))) {\n const model = match[1] ?? constants.get(match[2] ?? '');\n const action = match[3];\n if (model && action) scopes.add(`${model}.${action}`);\n }\n\n return Array.from(scopes);\n}\n\nfunction readMiniModelConstants(source: string): Map<string, string> {\n const constants = new Map<string, string>();\n let match: RegExpExecArray | null;\n MINI_MODEL_CONST_PATTERN.lastIndex = 0;\n while ((match = MINI_MODEL_CONST_PATTERN.exec(source))) {\n if (!match[1] || !match[2]) continue;\n constants.set(match[1], match[2]);\n }\n return constants;\n}\n\nfunction buildPushPlan(\n context: MiniProjectContext,\n manifest: MiniPullManifest,\n localFiles: MiniLocalFile[],\n remoteFiles: Array<{ item: WorkspaceFileItem; content: WorkspaceFileContent }>,\n): { operations: MiniPushPlanItem[]; conflicts: MiniPushConflict[] } {\n const status = computeStatusEntries(context, manifest, localFiles);\n const baseByLocal = new Map(manifest.files.map((file) => [file.localPath, file] as const));\n const remoteByLocal = new Map(\n remoteFiles.map((file) => [\n localPathFromRemote(context.remotePrefix, file.item.relativePath),\n file,\n ] as const),\n );\n const operations: MiniPushPlanItem[] = [];\n const conflicts: MiniPushConflict[] = [];\n\n for (const entry of status) {\n const remote = remoteByLocal.get(entry.localPath);\n const base = baseByLocal.get(entry.localPath);\n const remoteSha = remote ? sha256(remote.content.content) : undefined;\n if (entry.status === 'added') {\n if (remote) {\n conflicts.push(conflict(entry, 'remote file already exists but was not in local snapshot'));\n } else {\n operations.push({ operation: 'create', localPath: entry.localPath, remotePath: entry.remotePath });\n }\n continue;\n }\n if (entry.status === 'modified') {\n if (!remote) {\n conflicts.push(conflict(entry, 'remote file was deleted after last pull'));\n } else if (base && remoteSha !== base.sha256) {\n conflicts.push(conflict(entry, 'remote file changed after last pull'));\n } else {\n operations.push({\n operation: 'update',\n localPath: entry.localPath,\n remotePath: entry.remotePath,\n fileId: remote.item.id,\n });\n }\n continue;\n }\n if (entry.status === 'deleted') {\n if (!remote) {\n operations.push({\n operation: 'skip',\n localPath: entry.localPath,\n remotePath: entry.remotePath,\n reason: 'already deleted remotely',\n });\n } else if (base && remoteSha !== base.sha256) {\n conflicts.push(conflict(entry, 'remote file changed after last pull'));\n } else {\n operations.push({\n operation: 'delete',\n localPath: entry.localPath,\n remotePath: entry.remotePath,\n fileId: remote.item.id,\n });\n }\n continue;\n }\n operations.push({\n operation: 'skip',\n localPath: entry.localPath,\n remotePath: entry.remotePath,\n fileId: remote?.item.id,\n reason: 'unchanged',\n });\n }\n return { operations, conflicts };\n}\n\nfunction computeStatusEntries(\n context: MiniProjectContext,\n manifest: MiniPullManifest,\n localFiles: MiniLocalFile[],\n): MiniStatusEntry[] {\n const baseByLocal = new Map(manifest.files.map((file) => [file.localPath, file] as const));\n const localByPath = new Map(localFiles.map((file) => [file.localPath, file] as const));\n const paths = [...new Set([...baseByLocal.keys(), ...localByPath.keys()])].sort();\n return paths.map((localPath) => {\n const base = baseByLocal.get(localPath);\n const local = localByPath.get(localPath);\n const remotePath = base?.remotePath ?? remotePathFromLocal(context.appCode, localPath);\n if (!base && local) {\n return { localPath, remotePath, status: 'added', sha256: local.sha256 };\n }\n if (base && !local) {\n return {\n localPath,\n remotePath,\n status: 'deleted',\n fileId: base.fileId,\n baseSha256: base.sha256,\n };\n }\n if (base && local && base.sha256 !== local.sha256) {\n return {\n localPath,\n remotePath,\n status: 'modified',\n fileId: base.fileId,\n sha256: local.sha256,\n baseSha256: base.sha256,\n };\n }\n return {\n localPath,\n remotePath,\n status: 'unchanged',\n fileId: base?.fileId,\n sha256: local?.sha256,\n baseSha256: base?.sha256,\n };\n });\n}\n\nasync function loadRemoteMiniAppFiles(\n http: AxiosInstance,\n workspaceKey: string,\n appCode: string,\n): Promise<Array<{ item: WorkspaceFileItem; content: WorkspaceFileContent }>> {\n const prefix = miniRemotePrefix(appCode) + '/';\n const list = await listWorkspaceFiles(http, workspaceKey, { pageSize: 5000 });\n const result: Array<{ item: WorkspaceFileItem; content: WorkspaceFileContent }> = [];\n for (const item of list.data) {\n if (!item.relativePath.startsWith(prefix) || isDirectoryMarkerPath(item.relativePath)) continue;\n const content = await loadWorkspaceFileContent(http, item.id);\n result.push({ item, content });\n }\n return result;\n}\n\nasync function readLocalMiniFiles(root: string): Promise<MiniLocalFile[]> {\n const files: MiniLocalFile[] = [];\n await walk(root, '', files);\n files.sort((a, b) => a.localPath.localeCompare(b.localPath));\n return files;\n}\n\nasync function walk(root: string, relativeDir: string, files: MiniLocalFile[]): Promise<void> {\n const absoluteDir = path.join(root, relativeDir);\n let entries: Dirent[];\n try {\n entries = await fs.readdir(absoluteDir, { withFileTypes: true });\n } catch (e) {\n if ((e as NodeJS.ErrnoException).code === 'ENOENT') return;\n throw e;\n }\n for (const entry of entries) {\n if (entry.name === MINI_CONTEXT_FILE) continue;\n if (entry.isDirectory() && EXCLUDED_DIRS.has(entry.name)) continue;\n const localPath = normalizeRelativePath(path.posix.join(toPosix(relativeDir), entry.name));\n const absolutePath = path.join(root, localPath);\n if (entry.isDirectory()) {\n await walk(root, localPath, files);\n } else if (entry.isFile()) {\n const content = await fs.readFile(absolutePath, 'utf-8');\n files.push({\n localPath,\n absolutePath,\n content,\n sha256: sha256(content),\n size: Buffer.byteLength(content, 'utf-8'),\n });\n }\n }\n}\n\nasync function writeMiniState(\n root: string,\n context: MiniProjectContext,\n files: MiniLocalFile[],\n remoteByLocalPath: Map<string, WorkspaceFileItem>,\n): Promise<void> {\n const stateDir = path.join(root, MINI_STATE_DIR);\n const baseDir = path.join(stateDir, 'base');\n await fs.rm(baseDir, { recursive: true, force: true });\n await fs.mkdir(baseDir, { recursive: true });\n const snapshots: MiniFileSnapshot[] = [];\n for (const file of files) {\n await writeText(path.join(baseDir, file.localPath), file.content);\n snapshots.push(toSnapshot(context, file, remoteByLocalPath.get(file.localPath)));\n }\n await writeJson(path.join(stateDir, MINI_PULL_MANIFEST_FILE), {\n version: MINI_CONTEXT_VERSION,\n appCode: context.appCode,\n workspaceKey: context.workspaceKey,\n remotePrefix: context.remotePrefix,\n profile: context.profile,\n pulledAt: new Date().toISOString(),\n files: snapshots,\n } satisfies MiniPullManifest);\n}\n\nfunction toSnapshot(\n context: MiniProjectContext,\n file: MiniLocalFile,\n remote?: WorkspaceFileItem,\n): MiniFileSnapshot {\n return {\n localPath: file.localPath,\n remotePath: remote?.relativePath ?? remotePathFromLocal(context.appCode, file.localPath),\n fileId: remote?.id,\n sha256: file.sha256,\n size: file.size,\n lastModified: remote?.lastModified,\n };\n}\n\nfunction buildMiniAppTemplateFiles(args: {\n appCode: string;\n name?: string;\n defaultSurface: MiniSurface;\n templateKind?: MiniTemplateKind;\n writableModel?: string;\n writableFields?: string[] | null;\n writableDefaults?: Record<string, unknown>;\n writableTodayDefaultFields?: string[];\n requiredScopes: string[];\n tenantId: string;\n}): Array<{ localPath: string; content: string }> {\n return buildMiniAppTemplateFileSet(args);\n}\n\nasync function ensureTargetWritable(\n targetDir: string,\n relativePaths: string[],\n force: boolean,\n): Promise<void> {\n if (force) return;\n const existing: string[] = [];\n for (const relativePath of relativePaths) {\n try {\n await fs.access(path.join(targetDir, relativePath));\n existing.push(relativePath);\n } catch {\n /* file does not exist */\n }\n }\n if (existing.length) {\n throw new CliError({\n code: CliErrorCode.INVALID_ARGUMENT,\n message: `target contains existing files: ${existing.join(', ')} (use --force to overwrite)`,\n });\n }\n}\n\nfunction validateRequiredPath(value: string, fieldName: string): string {\n if (!value || !value.trim()) {\n throw new CliError({ code: CliErrorCode.MISSING_FLAG, message: `${fieldName} is required` });\n }\n return value.trim();\n}\n\nfunction normalizeAppCode(value: string): string {\n const normalized = validateRequiredPath(value, 'appCode');\n if (!MINI_APP_CODE_PATTERN.test(normalized)) {\n throw new CliError({\n code: CliErrorCode.INVALID_ARGUMENT,\n message: 'appCode must match ^[a-z][a-z0-9-]*$',\n });\n }\n return normalized;\n}\n\nfunction normalizeWorkspaceKey(value: string): string {\n const normalized = validateRequiredPath(value, 'workspaceKey');\n if (!/^[A-Za-z0-9._-]+$/.test(normalized)) {\n throw new CliError({\n code: CliErrorCode.INVALID_ARGUMENT,\n message: 'workspaceKey only supports letters, numbers, dot, underscore and dash',\n });\n }\n return normalized;\n}\n\nfunction normalizeRelativePath(value: string): string {\n const normalized = toPosix(value).replace(/^\\/+/, '').replace(/\\/+/g, '/');\n if (!normalized || normalized === '.' || normalized.includes('\\0')) {\n throw new Error('path is empty');\n }\n for (const segment of normalized.split('/')) {\n if (!segment || segment === '.' || segment === '..') {\n throw new Error(`unsafe path segment: ${segment}`);\n }\n }\n return normalized;\n}\n\nfunction miniRemotePrefix(appCode: string): string {\n return `${MINI_REMOTE_ROOT}/${normalizeAppCode(appCode)}`;\n}\n\nfunction remotePathFromLocal(appCode: string, localPath: string): string {\n return `${miniRemotePrefix(appCode)}/${normalizeRelativePath(localPath)}`;\n}\n\nfunction localPathFromRemote(remotePrefix: string, remotePath: string): string {\n const prefix = remotePrefix + '/';\n if (!remotePath.startsWith(prefix)) {\n throw new CliError({\n code: CliErrorCode.INVALID_ARGUMENT,\n message: `remote path is outside mini app prefix: ${remotePath}`,\n });\n }\n return normalizeRelativePath(remotePath.slice(prefix.length));\n}\n\nfunction isDirectoryMarkerPath(relativePath: string): boolean {\n return relativePath.endsWith('/.keep') || relativePath === '.keep';\n}\n\nfunction sha256(content: string): string {\n return crypto.createHash('sha256').update(content, 'utf-8').digest('hex');\n}\n\nasync function writeText(file: string, content: string): Promise<void> {\n await fs.mkdir(path.dirname(file), { recursive: true });\n await fs.writeFile(file, content, 'utf-8');\n}\n\nasync function writeJson(file: string, value: unknown): Promise<void> {\n await writeText(file, JSON.stringify(value, null, 2) + '\\n');\n}\n\nfunction conflict(entry: MiniStatusEntry, reason: string): MiniPushConflict {\n return {\n localPath: entry.localPath,\n remotePath: entry.remotePath,\n reason,\n };\n}\n\nfunction asRecord(value: unknown): Record<string, unknown> {\n return value && typeof value === 'object' ? (value as Record<string, unknown>) : {};\n}\n\nfunction asStringArray(value: unknown): string[] {\n return Array.isArray(value) ? value.filter((item): item is string => typeof item === 'string') : [];\n}\n\nfunction requireString(\n obj: Record<string, unknown>,\n key: string,\n errors: string[],\n predicate?: (value: string) => boolean,\n): void {\n const value = obj[key];\n if (typeof value !== 'string' || !value.trim()) {\n errors.push(`manifest ${key} is required`);\n return;\n }\n if (predicate && !predicate(value)) {\n errors.push(`manifest ${key} is invalid`);\n }\n}\n\nfunction stringRequired(value: unknown, fieldName: string): string {\n if (typeof value !== 'string' || !value.trim()) {\n throw new CliError({\n code: CliErrorCode.CONFIG_PARSE_ERROR,\n message: `${MINI_CONTEXT_FILE} missing ${fieldName}`,\n });\n }\n return value;\n}\n\nfunction stringOptional(value: unknown): string | undefined {\n return typeof value === 'string' && value.trim() ? value : undefined;\n}\n\nfunction toPosix(value: string): string {\n return value.replace(/\\\\/g, '/');\n}\n","import type { FieldMeta, ModelDescription } from '../meta/describe';\nimport type { MiniSurface, MiniTemplateKind } from './types';\nimport { SDK_VERSION } from '../package-version';\n\nexport interface MiniTemplateArgs {\n appCode: string;\n name?: string;\n defaultSurface: MiniSurface;\n requiredScopes: string[];\n tenantId: string;\n templateKind?: MiniTemplateKind;\n writableModel?: string;\n writableFields?: string[] | null;\n writableDefaults?: Record<string, unknown>;\n writableTodayDefaultFields?: string[];\n}\n\nexport interface MiniWritableTemplateConfig {\n model?: string;\n fields?: string[] | null;\n defaults?: Record<string, unknown>;\n todayDefaultFields?: string[];\n}\n\ninterface MiniReadTemplateConfig {\n model: string;\n modelLabel: string;\n}\n\nconst DEFAULT_MULTI_PAGE_MODEL = 'Employee';\nconst DEFAULT_WRITABLE_MODEL = 'OrgUnit';\n// OrgUnit 的编号字段是 `code`(CLAUDE.md 字段命名规范:编号/编码一律 code)。\n// 后端 OrgUnit.yml 里 unitCode 仅作 AI synonym 存在,不是可写字段——\n// 曾误用 unitCode 导致 writable-workbench 模板生成绑定到不存在字段的表单。\nconst DEFAULT_WRITABLE_FIELDS = ['code', 'name', 'unitType', 'status', 'effecDate', 'expireDate'];\nconst DEFAULT_WRITABLE_DEFAULTS: Record<string, unknown> = {\n unitType: 'DEPT',\n status: 'ACTIVE',\n expireDate: '9999-12-31',\n};\nconst DEFAULT_WRITABLE_TODAY_FIELDS = ['effecDate'];\nconst SYSTEM_FIELD_NAMES = new Set([\n 'id',\n 'tenantId',\n 'createdAt',\n 'createdBy',\n 'updatedAt',\n 'updatedBy',\n 'deletedAt',\n 'deletedBy',\n 'version',\n]);\nconst PREFERRED_WRITABLE_FIELDS = [\n 'code',\n 'name',\n 'unitType',\n 'status',\n 'effecDate',\n 'effectiveDate',\n 'startDate',\n 'expireDate',\n 'endDate',\n 'description',\n 'remark',\n 'notes',\n];\nconst MAX_AUTO_WRITABLE_FIELDS = 10;\nconst REFERENCE_FIELD_MODELS: Record<string, string> = {\n employeeId: 'Employee',\n managerId: 'Employee',\n orgUnitId: 'OrgUnit',\n organizationId: 'OrgUnit',\n positionId: 'Position',\n reportToId: 'Position',\n jobId: 'Job',\n jobGradeId: 'JobGrade',\n jobFamilyId: 'JobFamily',\n};\n\nexport function defaultMiniRequiredScopes(\n templateKind: MiniTemplateKind = 'dashboard',\n writableConfig: MiniWritableTemplateConfig = {},\n): string[] {\n if (templateKind === 'writable-workbench') {\n const model = normalizeWritableModel(writableConfig.model);\n return unique([\n `${model}.list`,\n `${model}.get`,\n `${model}.create`,\n `${model}.update`,\n `${model}.delete`,\n ...referenceScopesForWritableFields(writableConfig.fields),\n ]);\n }\n if (templateKind === 'multi-page') {\n const model = normalizeReadModel(writableConfig.model);\n return [`${model}.list`, `${model}.get`];\n }\n return ['Employee.list'];\n}\n\nexport function buildMiniAppTemplateFiles(args: MiniTemplateArgs): Array<{ localPath: string; content: string }> {\n const templateKind = args.templateKind ?? 'dashboard';\n const displayName = args.name?.trim() || args.appCode;\n const writableConfig = buildWritableTemplateConfig(args);\n const readConfig = buildReadTemplateConfig(args);\n const manifest = {\n schemaVersion: '1.0',\n code: args.appCode,\n name: displayName,\n version: SDK_VERSION,\n sdkVersion: SDK_VERSION,\n surfaces: ['embedded', 'standalone'],\n defaultSurface: args.defaultSurface,\n entry: { html: 'index.html', css: 'index.css', js: 'index.js' },\n bundleObjectRef: {\n tenantId: args.tenantId,\n storageType: 'LOCAL',\n pathSpec: { pathSegment: `mini_apps/${args.appCode}`, fileName: '' },\n },\n requiredScopes: args.requiredScopes,\n };\n\n return [\n { localPath: 'manifest.json', content: JSON.stringify(manifest, null, 2) + '\\n' },\n {\n localPath: 'index.html',\n content: templateHtml(templateKind, args.appCode, displayName, writableConfig),\n },\n {\n localPath: 'index.css',\n content: templateCss(templateKind),\n },\n {\n localPath: 'index.js',\n content: templateJs(templateKind, args.appCode, displayName, writableConfig, readConfig),\n },\n ];\n}\n\nfunction templateHtml(\n templateKind: MiniTemplateKind,\n appCode: string,\n displayName: string,\n writableConfig: ReturnType<typeof buildWritableTemplateConfig>,\n): string {\n if (templateKind === 'writable-workbench') return writableHtml(appCode, displayName, writableConfig);\n if (templateKind === 'multi-page') return multiPageHtml(appCode, displayName);\n return dashboardHtml(appCode, displayName);\n}\n\nfunction templateCss(templateKind: MiniTemplateKind): string {\n if (templateKind === 'writable-workbench') return writableCss();\n if (templateKind === 'multi-page') return multiPageCss();\n return dashboardCss();\n}\n\nfunction templateJs(\n templateKind: MiniTemplateKind,\n appCode: string,\n displayName: string,\n writableConfig: ReturnType<typeof buildWritableTemplateConfig>,\n readConfig: MiniReadTemplateConfig,\n): string {\n if (templateKind === 'writable-workbench') return writableJs(writableConfig);\n if (templateKind === 'multi-page') return multiPageJs(appCode, displayName, readConfig);\n return dashboardJs();\n}\n\nfunction buildReadTemplateConfig(args: MiniTemplateArgs): MiniReadTemplateConfig {\n const model = normalizeReadModel(args.writableModel);\n return {\n model,\n modelLabel: modelDisplayLabel(model),\n };\n}\n\nfunction buildWritableTemplateConfig(args: MiniTemplateArgs): Required<MiniWritableTemplateConfig> & { modelLabel: string } {\n const model = normalizeWritableModel(args.writableModel);\n const fields =\n args.writableFields === undefined\n ? model === DEFAULT_WRITABLE_MODEL\n ? [...DEFAULT_WRITABLE_FIELDS]\n : null\n : args.writableFields;\n const defaults =\n args.writableDefaults !== undefined\n ? args.writableDefaults\n : model === DEFAULT_WRITABLE_MODEL\n ? { ...DEFAULT_WRITABLE_DEFAULTS }\n : {};\n const todayDefaultFields =\n args.writableTodayDefaultFields !== undefined\n ? args.writableTodayDefaultFields\n : model === DEFAULT_WRITABLE_MODEL\n ? [...DEFAULT_WRITABLE_TODAY_FIELDS]\n : [];\n return {\n model,\n fields,\n defaults,\n todayDefaultFields,\n modelLabel: modelDisplayLabel(model),\n };\n}\n\nexport function inferMiniWritableTemplateOptionsFromModel(\n description: ModelDescription,\n): { fields: string[]; defaults: Record<string, unknown>; todayDefaultFields: string[] } {\n const writableFields = inferWritableFields(description.fields);\n const writableFieldSet = new Set(writableFields);\n return {\n fields: writableFields,\n defaults: inferWritableDefaults(description.fields, writableFieldSet),\n todayDefaultFields: inferTodayDefaultFields(description.fields, writableFieldSet),\n };\n}\n\nfunction inferWritableFields(fields: FieldMeta[]): string[] {\n const editable = fields.map((field) => field.name).filter((name) => name && !SYSTEM_FIELD_NAMES.has(name));\n const editableSet = new Set(editable);\n const required = fields\n .filter((field) => field.required === true && editableSet.has(field.name))\n .map((field) => field.name);\n const preferred = PREFERRED_WRITABLE_FIELDS.filter((name) => editableSet.has(name));\n const inferred = unique([...required, ...preferred]);\n return (inferred.length ? inferred : editable).slice(0, MAX_AUTO_WRITABLE_FIELDS);\n}\n\nfunction inferWritableDefaults(fields: FieldMeta[], writableFieldSet: Set<string>): Record<string, unknown> {\n const defaults: Record<string, unknown> = {};\n if (hasWritableField(fields, writableFieldSet, 'unitType')) defaults.unitType = 'DEPT';\n if (hasWritableField(fields, writableFieldSet, 'status')) defaults.status = 'ACTIVE';\n if (hasWritableField(fields, writableFieldSet, 'expireDate')) defaults.expireDate = '9999-12-31';\n if (hasWritableField(fields, writableFieldSet, 'endDate')) defaults.endDate = '9999-12-31';\n return defaults;\n}\n\nfunction inferTodayDefaultFields(fields: FieldMeta[], writableFieldSet: Set<string>): string[] {\n return ['effecDate', 'effectiveDate', 'startDate'].filter((name) => hasWritableField(fields, writableFieldSet, name));\n}\n\nfunction hasWritableField(fields: FieldMeta[], writableFieldSet: Set<string>, name: string): boolean {\n return writableFieldSet.has(name) && fields.some((field) => field.name === name);\n}\n\nfunction unique(values: string[]): string[] {\n return Array.from(new Set(values));\n}\n\nfunction referenceScopesForWritableFields(fields?: string[] | null): string[] {\n if (!fields?.length) return [];\n return unique(\n fields.flatMap((field) => {\n const model = REFERENCE_FIELD_MODELS[field];\n return model ? [`${model}.list`, `${model}.get`] : [];\n }),\n );\n}\n\nfunction normalizeWritableModel(model?: string): string {\n return model?.trim() || DEFAULT_WRITABLE_MODEL;\n}\n\nfunction normalizeReadModel(model?: string): string {\n return model?.trim() || DEFAULT_MULTI_PAGE_MODEL;\n}\n\nfunction modelDisplayLabel(model: string): string {\n const labels: Record<string, string> = {\n Employee: '员工',\n OrgUnit: '组织单元',\n Position: '岗位',\n };\n return labels[model] ?? `${model} 记录`;\n}\n\nfunction escapeHtml(value: string): string {\n return value\n .replaceAll('&', '&')\n .replaceAll('<', '<')\n .replaceAll('>', '>')\n .replaceAll('\"', '"')\n .replaceAll(\"'\", ''');\n}\n\nfunction dashboardHtml(appCode: string, displayName: string): string {\n return `<section class=\"hcm-mini-app\" data-mini-app=\"${appCode}\">\n <header class=\"hcm-mini-app__header\">\n <p class=\"hcm-mini-app__eyebrow\">HCM Mini App</p>\n <h1>${displayName}</h1>\n </header>\n <main class=\"hcm-mini-app__body\">\n <span data-field=\"status\">Ready</span>\n </main>\n</section>\n`;\n}\n\nfunction dashboardCss(): string {\n return `.hcm-mini-app {\n min-height: 100%;\n padding: 24px;\n color: #1f2933;\n background: #f6f8fb;\n font-family: Inter, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n}\n\n.hcm-mini-app__header {\n margin-bottom: 16px;\n}\n\n.hcm-mini-app__eyebrow {\n margin: 0 0 4px;\n color: #52606d;\n font-size: 12px;\n}\n\n.hcm-mini-app h1 {\n margin: 0;\n font-size: 24px;\n line-height: 1.25;\n}\n`;\n}\n\nfunction dashboardJs(): string {\n return `export async function mount(container, hcm) {\n const status = container.querySelector('[data-field=\"status\"]');\n if (status) {\n status.textContent = \"Tenant \" + hcm.context.tenantId;\n }\n}\n\nexport function unmount(container) {\n container.innerHTML = \"\";\n}\n`;\n}\n\nfunction multiPageHtml(appCode: string, displayName: string): string {\n return `<section class=\"mini-routed-app\" data-mini-app=\"${appCode}\">\n <header class=\"mini-routed-app__header\">\n <div>\n <p class=\"mini-routed-app__eyebrow\">Tenant <span data-field=\"tenant-id\">-</span></p>\n <h1>${escapeHtml(displayName)}</h1>\n </div>\n <span class=\"mini-routed-app__status\" data-field=\"status\">准备就绪</span>\n </header>\n\n <nav class=\"mini-routed-app__nav\" data-field=\"nav\" aria-label=\"轻应用页面\"></nav>\n <main class=\"mini-routed-app__view\" data-field=\"view\"></main>\n <pre class=\"mini-routed-app__error\" data-field=\"error\" hidden></pre>\n</section>\n`;\n}\n\nfunction multiPageCss(): string {\n return `.mini-routed-app {\n min-height: 100%;\n padding: 24px;\n color: #1f2933;\n background: #f6f8fb;\n font-family: Inter, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n}\n\n.mini-routed-app__header {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n gap: 16px;\n margin-bottom: 16px;\n}\n\n.mini-routed-app__eyebrow,\n.mini-routed-app__status {\n margin: 0;\n color: #52606d;\n font-size: 12px;\n}\n\n.mini-routed-app h1 {\n margin: 4px 0 0;\n font-size: 24px;\n line-height: 1.2;\n}\n\n.mini-routed-app__nav {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n margin-bottom: 14px;\n}\n\n.mini-routed-app__nav a {\n border: 1px solid #d9e2ec;\n border-radius: 6px;\n padding: 7px 12px;\n color: #52606d;\n background: #fff;\n text-decoration: none;\n}\n\n.mini-routed-app__nav a[aria-current=\"page\"] {\n border-color: #2f80ed;\n color: #fff;\n background: #2f80ed;\n}\n\n.mini-routed-app__view {\n min-height: 320px;\n border: 1px solid #d9e2ec;\n border-radius: 8px;\n padding: 18px;\n background: #fff;\n}\n\n.mini-routed-app__toolbar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n margin-bottom: 14px;\n}\n\n.mini-routed-app__grid {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));\n gap: 12px;\n}\n\n.mini-routed-app__metric,\n.mini-routed-app__section {\n border: 1px solid #e5edf5;\n border-radius: 8px;\n padding: 14px;\n background: #fbfdff;\n}\n\n.mini-routed-app__metric span,\n.mini-routed-app__section span {\n display: block;\n color: #8a94a6;\n font-size: 12px;\n}\n\n.mini-routed-app__metric strong {\n display: block;\n margin-top: 6px;\n font-size: 24px;\n}\n\n.mini-routed-app__list {\n display: grid;\n gap: 8px;\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.mini-routed-app__list button,\n.mini-routed-app__button {\n height: 34px;\n border: 1px solid #2f80ed;\n border-radius: 6px;\n padding: 0 12px;\n color: #fff;\n background: #2f80ed;\n cursor: pointer;\n}\n\n.mini-routed-app__list button {\n display: flex;\n width: 100%;\n align-items: center;\n justify-content: space-between;\n border-color: #d9e2ec;\n color: #1f2933;\n background: #f8fafc;\n text-align: left;\n}\n\n.mini-routed-app__muted {\n color: #8a94a6;\n}\n\n.mini-routed-app__error {\n margin: 14px 0 0;\n border: 1px solid #fecaca;\n border-radius: 6px;\n padding: 10px;\n color: #b91c1c;\n background: #fff1f2;\n white-space: pre-wrap;\n}\n\n@media (max-width: 720px) {\n .mini-routed-app {\n padding: 16px;\n }\n\n .mini-routed-app__header,\n .mini-routed-app__toolbar {\n display: grid;\n }\n}\n`;\n}\n\nfunction multiPageJs(appCode: string, displayName: string, config: MiniReadTemplateConfig): string {\n return `const APP_CODE = ${JSON.stringify(appCode)};\nconst APP_NAME = ${JSON.stringify(displayName)};\nconst DATA_MODEL = ${JSON.stringify(config.model)};\nconst MODEL_LABEL = ${JSON.stringify(config.modelLabel)};\nconst ROUTES = [\n { path: \"/\", label: \"总览\" },\n { path: \"/records\", label: MODEL_LABEL },\n { path: \"/settings\", label: \"设置\" },\n];\n\nconst state = {\n records: [],\n loaded: false,\n details: new Map(),\n unsubscribe: null,\n};\n\nconst textOf = (value, fallback = \"-\") => String(value ?? fallback);\nconst setText = (container, selector, value) => {\n const node = container.querySelector(selector);\n if (node) node.textContent = textOf(value);\n};\nconst setStatus = (container, value) => setText(container, '[data-field=\"status\"]', value);\nconst showError = (container, error) => {\n const node = container.querySelector('[data-field=\"error\"]');\n if (!node) return;\n if (!error) {\n node.hidden = true;\n node.textContent = \"\";\n return;\n }\n node.hidden = false;\n node.textContent = [error.code || \"REQUEST_FAILED\", error.message || \"请求失败\", error.traceId ? \"traceId: \" + error.traceId : \"\"].filter(Boolean).join(\"\\\\n\");\n};\nconst viewOf = (container) => container.querySelector('[data-field=\"view\"]');\nconst createNode = (tag, className, text) => {\n const node = document.createElement(tag);\n if (className) node.className = className;\n if (text !== undefined) node.textContent = text;\n return node;\n};\nconst recordName = (record) => record.name || record.employeeName || record.code || record.unitCode || record.positionCode || record.employeeNo || record.id || \"未命名\" + MODEL_LABEL;\n\nconst renderNav = (container, hcm) => {\n const nav = container.querySelector('[data-field=\"nav\"]');\n if (!nav) return;\n const current = hcm.router.current().path;\n nav.innerHTML = \"\";\n for (const route of ROUTES) {\n const link = document.createElement(\"a\");\n link.href = hcm.router.link(route.path);\n link.dataset.route = route.path;\n link.textContent = route.label;\n const active = route.path === \"/\" ? current === \"/\" : current === route.path || current.startsWith(route.path + \"/\");\n if (active) link.setAttribute(\"aria-current\", \"page\");\n nav.appendChild(link);\n }\n};\n\nconst loadRecords = async (container, hcm) => {\n if (state.loaded) return;\n setStatus(container, \"加载中\");\n const result = await hcm.query({ model: DATA_MODEL, page: 0, size: 10 });\n state.records = result.rows || [];\n state.loaded = true;\n setStatus(container, \"已更新\");\n};\n\nconst loadDetail = async (hcm, id) => {\n if (!state.details.has(id)) {\n state.details.set(id, await hcm.get({ model: DATA_MODEL, id }));\n }\n return state.details.get(id);\n};\n\nconst renderOverview = (container, hcm) => {\n const view = viewOf(container);\n if (!view) return;\n view.innerHTML = \"\";\n const title = createNode(\"div\", \"mini-routed-app__toolbar\");\n const heading = createNode(\"h2\", \"\", \"总览\");\n const action = createNode(\"button\", \"mini-routed-app__button\", \"查看\" + MODEL_LABEL);\n action.type = \"button\";\n action.dataset.route = \"/records\";\n title.appendChild(heading);\n title.appendChild(action);\n\n const grid = createNode(\"div\", \"mini-routed-app__grid\");\n const metrics = [\n [\"应用\", APP_NAME],\n [\"数据模型\", DATA_MODEL],\n [\"当前租户\", hcm.context.tenantId],\n ];\n for (const [label, value] of metrics) {\n const card = createNode(\"div\", \"mini-routed-app__metric\");\n card.appendChild(createNode(\"span\", \"\", label));\n card.appendChild(createNode(\"strong\", \"\", value));\n grid.appendChild(card);\n }\n view.appendChild(title);\n view.appendChild(grid);\n};\n\nconst renderRecords = async (container, hcm) => {\n await loadRecords(container, hcm);\n const view = viewOf(container);\n if (!view) return;\n view.innerHTML = \"\";\n const toolbar = createNode(\"div\", \"mini-routed-app__toolbar\");\n toolbar.appendChild(createNode(\"h2\", \"\", MODEL_LABEL));\n toolbar.appendChild(createNode(\"span\", \"mini-routed-app__muted\", \"共 \" + state.records.length + \" 条\"));\n const list = createNode(\"ul\", \"mini-routed-app__list\");\n for (const record of state.records) {\n const item = document.createElement(\"li\");\n const button = document.createElement(\"button\");\n button.type = \"button\";\n button.dataset.recordId = textOf(record.id, \"\");\n button.appendChild(createNode(\"strong\", \"\", recordName(record)));\n button.appendChild(createNode(\"span\", \"mini-routed-app__muted\", record.status || record.employeeNo || record.code || \"详情\"));\n item.appendChild(button);\n list.appendChild(item);\n }\n if (!state.records.length) {\n list.appendChild(createNode(\"li\", \"mini-routed-app__muted\", \"暂无\" + MODEL_LABEL + \"记录\"));\n }\n view.appendChild(toolbar);\n view.appendChild(list);\n};\n\nconst renderRecordDetail = async (container, hcm, id) => {\n const record = await loadDetail(hcm, id);\n const view = viewOf(container);\n if (!view) return;\n view.innerHTML = \"\";\n const toolbar = createNode(\"div\", \"mini-routed-app__toolbar\");\n const title = createNode(\"h2\", \"\", recordName(record));\n const back = createNode(\"button\", \"mini-routed-app__button\", \"返回列表\");\n back.type = \"button\";\n back.dataset.route = \"/records\";\n toolbar.appendChild(title);\n toolbar.appendChild(back);\n\n const grid = createNode(\"div\", \"mini-routed-app__grid\");\n const fields = [\n [\"ID\", record.id],\n [\"编号\", record.employeeNo || record.code],\n [\"状态\", record.status],\n [\"邮箱\", record.email],\n ];\n for (const [label, value] of fields) {\n const card = createNode(\"div\", \"mini-routed-app__section\");\n card.appendChild(createNode(\"span\", \"\", label));\n card.appendChild(createNode(\"strong\", \"\", textOf(value)));\n grid.appendChild(card);\n }\n view.appendChild(toolbar);\n view.appendChild(grid);\n};\n\nconst renderSettings = (container, hcm) => {\n const view = viewOf(container);\n if (!view) return;\n view.innerHTML = \"\";\n const section = createNode(\"div\", \"mini-routed-app__section\");\n section.appendChild(createNode(\"h2\", \"\", \"设置\"));\n section.appendChild(createNode(\"span\", \"\", \"应用编码\"));\n section.appendChild(createNode(\"strong\", \"\", APP_CODE));\n section.appendChild(createNode(\"span\", \"\", \"当前路由\"));\n section.appendChild(createNode(\"strong\", \"\", hcm.router.current().path));\n view.appendChild(section);\n};\n\nconst renderRoute = async (container, hcm) => {\n showError(container, null);\n renderNav(container, hcm);\n const detailRoute = hcm.router.match(\"/records/:id\");\n if (detailRoute) {\n await renderRecordDetail(container, hcm, detailRoute.params.id);\n return;\n }\n if (hcm.router.match(\"/records\")) {\n await renderRecords(container, hcm);\n return;\n }\n if (hcm.router.match(\"/settings\")) {\n renderSettings(container, hcm);\n return;\n }\n if (hcm.router.current().path !== \"/\") {\n hcm.router.replace(\"/\");\n return;\n }\n renderOverview(container, hcm);\n};\n\nexport async function mount(container, hcm) {\n setText(container, '[data-field=\"tenant-id\"]', hcm.context.tenantId);\n const onClick = (event) => {\n const routeTarget = event.target instanceof Element ? event.target.closest(\"[data-route]\") : null;\n if (routeTarget?.dataset.route) {\n event.preventDefault();\n hcm.router.push(routeTarget.dataset.route);\n return;\n }\n const recordTarget = event.target instanceof Element ? event.target.closest(\"[data-record-id]\") : null;\n if (recordTarget?.dataset.recordId) {\n hcm.router.push(\"/records/\" + encodeURIComponent(recordTarget.dataset.recordId));\n }\n };\n container.addEventListener(\"click\", onClick);\n state.unsubscribe = hcm.router.subscribe(() => void renderRoute(container, hcm).catch((error) => showError(container, error)));\n await renderRoute(container, hcm);\n container.__miniCleanup = () => {\n container.removeEventListener(\"click\", onClick);\n state.unsubscribe?.();\n };\n}\n\nexport function unmount(container) {\n container.__miniCleanup?.();\n container.innerHTML = \"\";\n}\n`;\n}\n\nfunction writableHtml(appCode: string, displayName: string, config: ReturnType<typeof buildWritableTemplateConfig>): string {\n const modelLabel = escapeHtml(config.modelLabel);\n return `<section class=\"mini-workbench\" data-mini-app=\"${appCode}\">\n <header class=\"mini-workbench__header\">\n <div>\n <p class=\"mini-workbench__eyebrow\">Tenant <span data-field=\"tenant-id\">-</span></p>\n <h1>${escapeHtml(displayName)}</h1>\n </div>\n <span class=\"mini-workbench__status\" data-field=\"status\">准备就绪</span>\n </header>\n\n <div class=\"mini-workbench__layout\">\n <aside class=\"mini-workbench__panel\">\n <div class=\"mini-workbench__panel-head\">\n <strong>${modelLabel}</strong>\n <div class=\"mini-workbench__actions\">\n <button type=\"button\" data-action=\"new-record\">新建</button>\n <button type=\"button\" data-action=\"refresh\">刷新</button>\n </div>\n </div>\n <div class=\"mini-workbench__list\" data-field=\"record-list\"></div>\n </aside>\n\n <main class=\"mini-workbench__panel\">\n <div class=\"mini-workbench__panel-head\">\n <strong data-field=\"editor-title\">选择${modelLabel}</strong>\n <div class=\"mini-workbench__actions\">\n <button type=\"button\" data-action=\"delete-record\" hidden>删除</button>\n <button type=\"button\" data-action=\"back\">返回列表</button>\n </div>\n </div>\n <div class=\"mini-workbench__empty\" data-field=\"editor-empty\">\n <strong>选择一条${modelLabel}</strong>\n <span>从左侧列表打开记录,或点击“新建”。</span>\n </div>\n <form class=\"mini-workbench__form\" data-field=\"editor-form\" hidden>\n <div class=\"mini-workbench__fields\" data-field=\"editor-fields\"></div>\n <button type=\"submit\">保存</button>\n </form>\n <pre class=\"mini-workbench__error\" data-field=\"error\" hidden></pre>\n </main>\n </div>\n</section>\n`;\n}\n\nfunction writableCss(): string {\n return `.mini-workbench {\n min-height: 100%;\n padding: 24px;\n color: #1f2933;\n background: #f6f8fb;\n font-family: Inter, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n}\n\n.mini-workbench__header,\n.mini-workbench__panel-head {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n gap: 16px;\n}\n\n.mini-workbench__header {\n margin-bottom: 18px;\n}\n\n.mini-workbench__eyebrow,\n.mini-workbench__status {\n margin: 0;\n color: #52606d;\n font-size: 12px;\n}\n\n.mini-workbench h1 {\n margin: 4px 0 0;\n font-size: 24px;\n line-height: 1.2;\n}\n\n.mini-workbench__layout {\n display: grid;\n grid-template-columns: minmax(240px, 320px) minmax(0, 1fr);\n gap: 14px;\n}\n\n.mini-workbench__panel {\n min-height: 320px;\n border: 1px solid #d9e2ec;\n border-radius: 8px;\n padding: 16px;\n background: #fff;\n}\n\n.mini-workbench__actions,\n.mini-workbench__form,\n.mini-workbench__fields {\n display: grid;\n gap: 10px;\n}\n\n.mini-workbench__form[hidden],\n.mini-workbench__empty[hidden] {\n display: none;\n}\n\n.mini-workbench__actions {\n grid-auto-flow: column;\n align-items: center;\n}\n\n.mini-workbench button {\n height: 32px;\n border: 1px solid #2f80ed;\n border-radius: 6px;\n padding: 0 12px;\n color: #fff;\n background: #2f80ed;\n cursor: pointer;\n}\n\n.mini-workbench__list {\n display: grid;\n gap: 8px;\n}\n\n.mini-workbench__empty {\n display: grid;\n gap: 8px;\n min-height: 220px;\n place-content: center;\n color: #8a94a6;\n text-align: center;\n}\n\n.mini-workbench__empty strong {\n color: #52606d;\n font-size: 15px;\n}\n\n.mini-workbench__list button {\n width: 100%;\n border-color: #d9e2ec;\n color: #1f2933;\n background: #f8fafc;\n text-align: left;\n}\n\n.mini-workbench__field {\n display: grid;\n gap: 6px;\n color: #52606d;\n font-size: 13px;\n}\n\n.mini-workbench__field input,\n.mini-workbench__field select,\n.mini-workbench__field textarea {\n min-height: 36px;\n border: 1px solid #cbd5e1;\n border-radius: 6px;\n padding: 0 10px;\n color: #1f2933;\n background: #fff;\n}\n\n.mini-workbench__field textarea {\n min-height: 72px;\n padding: 8px 10px;\n}\n\n.mini-workbench__field--checkbox {\n grid-template-columns: auto 1fr;\n align-items: center;\n}\n\n.mini-workbench__field--checkbox input {\n min-height: auto;\n}\n\n.mini-workbench__field-hint {\n color: #8a94a6;\n font-size: 12px;\n}\n\n.mini-workbench__reference {\n display: grid;\n gap: 6px;\n position: relative;\n}\n\n.mini-workbench__reference-row {\n display: grid;\n grid-template-columns: minmax(0, 1fr) auto;\n gap: 8px;\n}\n\n.mini-workbench__reference-results {\n display: grid;\n gap: 4px;\n max-height: 180px;\n overflow: auto;\n border: 1px solid #d9e2ec;\n border-radius: 6px;\n padding: 6px;\n background: #fff;\n}\n\n.mini-workbench__reference-results[hidden] {\n display: none;\n}\n\n.mini-workbench__reference-results button {\n width: 100%;\n border-color: #d9e2ec;\n color: #1f2933;\n background: #f8fafc;\n text-align: left;\n}\n\n.mini-workbench__required {\n color: #dc2626;\n}\n\n.mini-workbench__error {\n margin: 14px 0 0;\n border: 1px solid #fecaca;\n border-radius: 6px;\n padding: 10px;\n color: #b91c1c;\n background: #fff1f2;\n white-space: pre-wrap;\n}\n\n@media (max-width: 720px) {\n .mini-workbench__layout {\n grid-template-columns: 1fr;\n }\n}\n`;\n}\n\nfunction writableJs(config: ReturnType<typeof buildWritableTemplateConfig>): string {\n const model = JSON.stringify(config.model);\n const modelLabel = JSON.stringify(config.modelLabel);\n const fields = config.fields?.length ? JSON.stringify(config.fields) : 'null';\n const defaults = JSON.stringify(config.defaults, null, 2);\n const todayDefaults = JSON.stringify(config.todayDefaultFields);\n return `const MODEL = ${model};\nconst MODEL_LABEL = ${modelLabel};\nconst ROUTE_BASE = \"/records\";\nconst FORM_FIELDS = ${fields};\nconst CREATE_DEFAULTS = ${defaults};\nconst TODAY_DEFAULT_FIELDS = ${todayDefaults};\nconst state = {\n records: [],\n activeId: \"\",\n mode: \"list\",\n meta: null,\n unsubscribe: null,\n};\nconst textOf = (value, fallback = \"-\") => String(value ?? fallback);\nconst today = () => new Date().toISOString().slice(0, 10);\nconst formDefaults = () => {\n const defaults = { ...CREATE_DEFAULTS };\n for (const key of TODAY_DEFAULT_FIELDS) defaults[key] = today();\n return defaults;\n};\nconst formOptions = (defaults = {}) => {\n const options = {};\n if (Array.isArray(FORM_FIELDS) && FORM_FIELDS.length) options.include = FORM_FIELDS;\n if (defaults && Object.keys(defaults).length) options.defaults = defaults;\n return options;\n};\nconst setText = (container, selector, value) => {\n const node = container.querySelector(selector);\n if (node) node.textContent = textOf(value);\n};\nconst setStatus = (container, value) => setText(container, '[data-field=\"status\"]', value);\nconst setDeleteVisible = (container, visible) => {\n const button = container.querySelector('[data-action=\"delete-record\"]');\n if (button) button.hidden = !visible;\n};\nconst setEditorVisible = (container, visible) => {\n const form = container.querySelector('[data-field=\"editor-form\"]');\n const empty = container.querySelector('[data-field=\"editor-empty\"]');\n if (form) form.hidden = !visible;\n if (empty) empty.hidden = visible;\n};\nconst showError = (container, error) => {\n const node = container.querySelector('[data-field=\"error\"]');\n if (!node) return;\n if (!error) {\n node.hidden = true;\n node.textContent = \"\";\n return;\n }\n node.hidden = false;\n node.textContent = [error.code || \"REQUEST_FAILED\", error.message || \"请求失败\", error.traceId ? \"traceId: \" + error.traceId : \"\"].filter(Boolean).join(\"\\\\n\");\n};\nconst ensureMeta = async (hcm) => {\n if (!state.meta) state.meta = await hcm.describe({ model: MODEL, type: \"info\" });\n return state.meta;\n};\nconst queryReferenceRecords = async (hcm, field, keyword) => {\n const model = field.reference?.model;\n if (!model) return [];\n if (hcm.reference?.search) {\n return (await hcm.reference.search({ model, field: field.reference?.field, keyword, size: 8 })).rows;\n }\n const query = { model, page: 0, size: 8 };\n const trimmed = keyword.trim();\n if (trimmed) {\n query.filter = { [field.reference?.field || \"name\"]: { $contains: trimmed } };\n }\n try {\n return (await hcm.query(query)).rows;\n } catch (error) {\n if (!trimmed) throw error;\n return (await hcm.query({ model, page: 0, size: 8 })).rows;\n }\n};\nconst referenceLabel = (hcm, record) => hcm.reference?.label ? hcm.reference.label(record) : record?.name || record?.code || record?.unitCode || record?.positionCode || record?.employeeNo || record?.id || \"未命名记录\";\nconst recordName = (record) => record?.name || record?.employeeName || record?.code || record?.unitCode || record?.positionCode || record?.employeeNo || record?.id || \"未命名\" + MODEL_LABEL;\nconst hydrateReferenceLabel = async (hcm, field, searchInput, value) => {\n const id = value === undefined || value === null ? \"\" : String(value).trim();\n if (!id || !field.reference?.model || !hcm.reference?.labelFor) return;\n const label = await hcm.reference.labelFor({ model: field.reference.model, id });\n if (searchInput.value === id) searchInput.value = label || id;\n};\nconst applyReferenceRecord = (hcm, hiddenInput, searchInput, record) => {\n hiddenInput.value = record?.id ?? \"\";\n searchInput.value = referenceLabel(hcm, record);\n};\nconst isUnsupportedMiniUi = (error) => error?.code === \"MINI_UI_UNSUPPORTED\" || error?.name === \"MiniUiUnsupportedError\";\nconst pickReferenceRecord = async (hcm, field) => {\n const model = field.reference?.model;\n if (!model || !hcm.ui?.pickModel) return null;\n try {\n const result = await hcm.ui.pickModel({\n model,\n multiple: false,\n title: \"选择\" + field.label,\n });\n return result.confirmed ? result.record : null;\n } catch (error) {\n if (isUnsupportedMiniUi(error)) return null;\n throw error;\n }\n};\nconst renderReferenceOptions = (hcm, box, hiddenInput, searchInput, records) => {\n box.innerHTML = \"\";\n if (!records.length) {\n box.textContent = \"暂无可选记录\";\n box.hidden = false;\n return;\n }\n for (const record of records) {\n const button = document.createElement(\"button\");\n button.type = \"button\";\n button.textContent = referenceLabel(hcm, record);\n button.addEventListener(\"click\", () => {\n applyReferenceRecord(hcm, hiddenInput, searchInput, record);\n box.hidden = true;\n });\n box.appendChild(button);\n }\n box.hidden = false;\n};\nconst renderReferenceControl = (field, value, hcm) => {\n const wrapper = document.createElement(\"div\");\n wrapper.className = \"mini-workbench__reference\";\n const hidden = document.createElement(\"input\");\n hidden.type = \"hidden\";\n hidden.name = field.key;\n hidden.dataset.formKey = field.key;\n hidden.value = value ?? \"\";\n const search = document.createElement(\"input\");\n search.type = \"search\";\n search.name = field.key + \"__search\";\n search.autocomplete = \"off\";\n search.placeholder = field.reference?.model ? \"搜索 \" + field.reference.model : \"搜索记录\";\n search.value = value ?? \"\";\n void hydrateReferenceLabel(hcm, field, search, value);\n const picker = document.createElement(\"button\");\n picker.type = \"button\";\n picker.textContent = \"选择\";\n const results = document.createElement(\"div\");\n results.className = \"mini-workbench__reference-results\";\n results.hidden = true;\n let requestId = 0;\n const searchRecords = async () => {\n const current = ++requestId;\n results.hidden = false;\n results.textContent = \"搜索中...\";\n try {\n const records = await queryReferenceRecords(hcm, field, search.value);\n if (current === requestId) renderReferenceOptions(hcm, results, hidden, search, records);\n } catch (error) {\n if (current === requestId) {\n results.textContent = \"搜索失败,可直接填写记录 ID\";\n }\n }\n };\n search.addEventListener(\"focus\", () => void searchRecords());\n search.addEventListener(\"input\", () => {\n hidden.value = search.value.trim();\n void searchRecords();\n });\n picker.addEventListener(\"click\", () => {\n void (async () => {\n results.hidden = true;\n const record = await pickReferenceRecord(hcm, field);\n if (record) {\n applyReferenceRecord(hcm, hidden, search, record);\n return;\n }\n search.focus();\n await searchRecords();\n })().catch((error) => {\n results.hidden = false;\n results.textContent = error?.message || \"选择失败,可直接搜索或填写记录 ID\";\n });\n });\n const row = document.createElement(\"div\");\n row.className = \"mini-workbench__reference-row\";\n row.appendChild(search);\n row.appendChild(picker);\n wrapper.appendChild(hidden);\n wrapper.appendChild(row);\n wrapper.appendChild(results);\n return wrapper;\n};\nconst renderFormControl = (field, value, hcm) => {\n const key = field.key;\n if (field.inputType === \"select\" && field.options?.length) {\n const select = document.createElement(\"select\");\n select.name = key;\n select.dataset.formKey = key;\n for (const option of field.options) {\n const item = document.createElement(\"option\");\n item.value = String(option.value);\n item.textContent = option.label;\n item.disabled = option.disabled === true;\n if (String(option.value) === String(value ?? \"\")) item.selected = true;\n select.appendChild(item);\n }\n return select;\n }\n if (field.inputType === \"textarea\") {\n const textarea = document.createElement(\"textarea\");\n textarea.name = key;\n textarea.dataset.formKey = key;\n textarea.value = value ?? \"\";\n return textarea;\n }\n if (field.inputType === \"reference\") {\n return renderReferenceControl(field, value, hcm);\n }\n const input = document.createElement(\"input\");\n input.name = key;\n input.dataset.formKey = key;\n input.autocomplete = \"off\";\n input.type =\n field.inputType === \"number\" ? \"number\" :\n field.inputType === \"date\" ? \"date\" :\n field.inputType === \"datetime\" ? \"datetime-local\" :\n field.inputType === \"checkbox\" ? \"checkbox\" : \"text\";\n if (field.inputType === \"checkbox\") {\n input.checked = value === true || value === \"true\";\n } else {\n input.value = value ?? \"\";\n }\n return input;\n};\nconst renderForm = async (container, hcm, values = {}) => {\n const meta = await ensureMeta(hcm);\n const defaults = formDefaults();\n const fields = hcm.form.fields(meta, formOptions());\n const fieldBox = container.querySelector('[data-field=\"editor-fields\"]');\n if (!fieldBox) return;\n fieldBox.innerHTML = \"\";\n for (const field of fields) {\n const value = values[field.key] ?? defaults[field.key] ?? field.defaultValue ?? \"\";\n const label = document.createElement(\"label\");\n label.className = \"mini-workbench__field\" + (field.inputType === \"checkbox\" ? \" mini-workbench__field--checkbox\" : \"\");\n const caption = document.createElement(\"span\");\n caption.textContent = field.label;\n if (field.required) {\n const mark = document.createElement(\"strong\");\n mark.className = \"mini-workbench__required\";\n mark.textContent = \" *\";\n caption.appendChild(mark);\n }\n label.appendChild(caption);\n label.appendChild(renderFormControl(field, value, hcm));\n if (field.inputType === \"reference\" && field.reference?.model) {\n const hint = document.createElement(\"small\");\n hint.className = \"mini-workbench__field-hint\";\n hint.textContent = \"关联模型:\" + field.reference.model + \",填写记录 ID\";\n label.appendChild(hint);\n }\n fieldBox.appendChild(label);\n }\n};\nconst readFormValues = (container) => {\n const values = {};\n for (const input of container.querySelectorAll(\"[data-form-key]\")) {\n const key = input.dataset.formKey;\n if (input.type === \"checkbox\") {\n values[key] = input.checked;\n continue;\n }\n const value = input.value?.trim();\n if (value === \"\") continue;\n values[key] = value;\n }\n return values;\n};\nconst renderList = (container, hcm) => {\n const list = container.querySelector('[data-field=\"record-list\"]');\n if (!list) return;\n list.innerHTML = \"\";\n for (const item of state.records) {\n const button = document.createElement(\"button\");\n button.type = \"button\";\n button.dataset.action = \"open-record\";\n button.dataset.id = textOf(item.id, \"\");\n button.textContent = recordName(item);\n list.appendChild(button);\n }\n if (!state.records.length) list.textContent = \"暂无\" + MODEL_LABEL;\n if (hcm.router.current().path === \"/\") hcm.router.replace(ROUTE_BASE);\n};\nconst loadRecords = async (container, hcm) => {\n setStatus(container, \"加载中\");\n showError(container, null);\n const result = await hcm.query({ model: MODEL, page: 0, size: 50 });\n state.records = result.rows;\n renderList(container, hcm);\n setStatus(container, \"已更新\");\n};\nconst openEditor = async (container, hcm, id) => {\n state.activeId = id;\n state.mode = \"edit\";\n setEditorVisible(container, true);\n setStatus(container, \"读取详情\");\n showError(container, null);\n const record = await hcm.get({ model: MODEL, id });\n setText(container, '[data-field=\"editor-title\"]', recordName(record));\n await renderForm(container, hcm, record);\n setDeleteVisible(container, true);\n setStatus(container, \"可编辑\");\n};\nconst openNew = async (container, hcm) => {\n state.activeId = \"\";\n state.mode = \"new\";\n setEditorVisible(container, true);\n showError(container, null);\n setText(container, '[data-field=\"editor-title\"]', \"新建\" + MODEL_LABEL);\n await renderForm(container, hcm, formDefaults());\n setDeleteVisible(container, false);\n setStatus(container, \"可创建\");\n};\nconst renderRoute = async (container, hcm) => {\n if (hcm.router.match(ROUTE_BASE + \"/new\")) {\n await openNew(container, hcm);\n return;\n }\n const route = hcm.router.match(ROUTE_BASE + \"/:id\");\n if (route) {\n await openEditor(container, hcm, route.params.id);\n return;\n }\n state.activeId = \"\";\n state.mode = \"list\";\n setEditorVisible(container, false);\n setText(container, '[data-field=\"editor-title\"]', \"选择\" + MODEL_LABEL);\n const fieldBox = container.querySelector('[data-field=\"editor-fields\"]');\n if (fieldBox) fieldBox.innerHTML = \"\";\n setDeleteVisible(container, false);\n renderList(container, hcm);\n};\nconst saveActive = async (container, hcm) => {\n if (state.mode !== \"new\" && !state.activeId) return;\n const meta = await ensureMeta(hcm);\n const validation = hcm.form.validate(meta, readFormValues(container), {\n ...formOptions(),\n defaults: state.mode === \"new\" ? formDefaults() : {},\n });\n if (!validation.valid) {\n showError(container, { code: \"FORM_INVALID\", message: validation.errors.map((item) => item.message).join(\"\\\\n\") });\n return;\n }\n setStatus(container, state.mode === \"new\" ? \"创建中\" : \"保存中\");\n showError(container, null);\n if (state.mode === \"new\") {\n const created = await hcm.create({ model: MODEL, data: validation.payload });\n await loadRecords(container, hcm);\n hcm.router.push(created?.id ? ROUTE_BASE + \"/\" + encodeURIComponent(created.id) : ROUTE_BASE);\n setStatus(container, \"已创建\");\n return;\n }\n await hcm.update({ model: MODEL, id: state.activeId, data: validation.payload });\n await loadRecords(container, hcm);\n hcm.router.push(ROUTE_BASE + \"/\" + encodeURIComponent(state.activeId));\n setStatus(container, \"已保存\");\n};\nconst deleteActive = async (container, hcm) => {\n if (!state.activeId) return;\n const title = container.querySelector('[data-field=\"editor-title\"]')?.textContent || state.activeId;\n const confirmed = typeof window === \"undefined\" || window.confirm(\"确定删除\" + MODEL_LABEL + \" \" + title + \"?\");\n if (!confirmed) return;\n setStatus(container, \"删除中\");\n showError(container, null);\n await hcm.remove({ model: MODEL, id: state.activeId });\n state.activeId = \"\";\n state.mode = \"list\";\n await loadRecords(container, hcm);\n hcm.router.push(ROUTE_BASE);\n setStatus(container, \"已删除\");\n};\nexport async function mount(container, hcm) {\n setText(container, '[data-field=\"tenant-id\"]', hcm.context.tenantId);\n const onClick = (event) => {\n const target = event.target instanceof Element ? event.target.closest(\"[data-action]\") : null;\n if (!target) return;\n if (target.dataset.action === \"refresh\") void loadRecords(container, hcm).catch((error) => showError(container, error));\n if (target.dataset.action === \"new-record\") hcm.router.push(ROUTE_BASE + \"/new\");\n if (target.dataset.action === \"open-record\" && target.dataset.id) hcm.router.push(ROUTE_BASE + \"/\" + encodeURIComponent(target.dataset.id));\n if (target.dataset.action === \"back\") hcm.router.push(ROUTE_BASE);\n if (target.dataset.action === \"delete-record\") {\n void deleteActive(container, hcm).catch((error) => {\n setStatus(container, \"删除失败\");\n showError(container, error);\n });\n }\n };\n const onSubmit = (event) => {\n event.preventDefault();\n void saveActive(container, hcm).catch((error) => {\n setStatus(container, \"保存失败\");\n showError(container, error);\n });\n };\n container.addEventListener(\"click\", onClick);\n container.querySelector('[data-field=\"editor-form\"]')?.addEventListener(\"submit\", onSubmit);\n state.unsubscribe = hcm.router.subscribe(() => void renderRoute(container, hcm).catch((error) => showError(container, error)));\n await ensureMeta(hcm);\n await loadRecords(container, hcm);\n await renderRoute(container, hcm);\n container.__miniCleanup = () => {\n container.removeEventListener(\"click\", onClick);\n container.querySelector('[data-field=\"editor-form\"]')?.removeEventListener(\"submit\", onSubmit);\n state.unsubscribe?.();\n };\n}\n\nexport function unmount(container) {\n container.__miniCleanup?.();\n container.innerHTML = \"\";\n}\n`;\n}\n","import type { AxiosInstance } from 'axios';\n\n/**\n * 模型缓存运维(两级缓存:Caffeine 本地 + Redis 远程)。\n *\n * 后端契约(CommonModelQueryCacheController):\n * - `GET /api/models/{model}/cache` 统计\n * - `DELETE /api/models/{model}/cache` 清全部实体缓存\n * - `DELETE /api/models/{model}/cache/{id}` 清单条\n * - `DELETE /api/models/{model}/meta/cache` 清元数据缓存(Code 级 YAML + Action 定义)\n *\n * 🔴 缓存必须走 API 管理,不直接操作 Redis —— 保证一致性、审计与业务规则生效。\n */\n\nexport interface CacheStats {\n modelKey: string;\n [k: string]: unknown;\n}\n\nexport interface CacheClearResult {\n modelKey: string;\n clearedCount?: number;\n message?: string;\n [k: string]: unknown;\n}\n\nexport async function getCacheStats(http: AxiosInstance, model: string): Promise<CacheStats> {\n const resp = await http.get(`/api/models/${model}/cache`);\n return (resp.data?.data ?? resp.data) as CacheStats;\n}\n\nexport async function clearModelCache(\n http: AxiosInstance,\n model: string,\n id?: string,\n): Promise<CacheClearResult> {\n const url = id ? `/api/models/${model}/cache/${id}` : `/api/models/${model}/cache`;\n const resp = await http.delete(url);\n return (resp.data?.data ?? resp.data) as CacheClearResult;\n}\n\nexport interface ClearMetaCacheOpts {\n /** 元数据类型(list / info / view / filter / page…),省略 = 全清 */\n type?: string;\n /** 状态变体(如 self),配合 type 使用 */\n state?: string;\n}\n\nexport async function clearMetaCache(\n http: AxiosInstance,\n model: string,\n opts: ClearMetaCacheOpts = {},\n): Promise<CacheClearResult> {\n const params: Record<string, string> = {};\n if (opts.type) params.type = opts.type;\n if (opts.state) params.state = opts.state;\n const resp = await http.delete(`/api/models/${model}/meta/cache`, { params });\n return (resp.data?.data ?? resp.data) as CacheClearResult;\n}\n","import type { AxiosInstance } from 'axios';\n\n/**\n * 租户开通(bootstrap)—— 交付旅程第一步。\n *\n * 后端契约(TenantBootstrapController):\n * - `POST /api/bootstrap/tenant`,header `X-Bootstrap-Key: <secret>`\n * - body `{ tenantId, adminUsername, adminPassword, tenantName? }`\n *\n * 🔴 该端点不走普通 Bearer 认证,用独立的 bootstrap key —— 它要在「还没有任何\n * 租户和用户」的时刻可用。key 由部署方持有,绝不写进 skill 正文或提交进仓库。\n */\n\nexport interface BootstrapTenantRequest {\n tenantId: string;\n adminUsername: string;\n adminPassword: string;\n tenantName?: string;\n}\n\nexport interface BootstrapReconcileResult {\n component?: string;\n version?: number;\n status?: string;\n [k: string]: unknown;\n}\n\nexport interface BootstrapTenantResult {\n success: boolean;\n tenantId: string;\n adminUserId?: string;\n message?: string;\n results?: BootstrapReconcileResult[];\n}\n\nexport async function bootstrapTenant(\n http: AxiosInstance,\n bootstrapKey: string,\n req: BootstrapTenantRequest,\n): Promise<BootstrapTenantResult> {\n const resp = await http.post('/api/bootstrap/tenant', req, {\n headers: { 'X-Bootstrap-Key': bootstrapKey },\n });\n return (resp.data?.data ?? resp.data) as BootstrapTenantResult;\n}\n","import type { AxiosInstance } from 'axios';\n\n/**\n * 租户元数据读写(TenantMetaController,`/api/tenant-meta`)。\n *\n * 统一走 query-param 形态(`?path=`)而非路径通配 `/meta/**`:meta 路径含斜杠与点,\n * query param 避免 URL 段解析歧义与路径穿越面。\n *\n * 后端契约:\n * - `GET /api/tenant-meta/list?keyword=&type=&page=&pageSize=`\n * - `GET /api/tenant-meta/meta?path=<metaName>`\n * - `PUT /api/tenant-meta/meta?path=<metaName>` body `{ content: \"<原文>\" }`\n * - `DELETE /api/tenant-meta/meta?path=<metaName>`\n *\n * 🔴 写入需 config-admin 身份,且 source 必须可写(租户开发空间绑定态)——\n * 后端 `assertConfigAdmin` / `assertWritableSource` 会拒绝越权与只读源。\n */\n\nexport interface TenantMetaEntry {\n metaName?: string;\n name?: string;\n type?: string;\n lastModified?: string;\n size?: number;\n [k: string]: unknown;\n}\n\nexport interface ListTenantMetaOpts {\n keyword?: string;\n /** yml / properties / all(默认 all) */\n type?: string;\n page?: number;\n pageSize?: number;\n}\n\nexport async function listTenantMeta(\n http: AxiosInstance,\n opts: ListTenantMetaOpts = {},\n): Promise<{ items: TenantMetaEntry[]; total?: number; raw: unknown }> {\n const resp = await http.get('/api/tenant-meta/list', { params: opts });\n const payload = resp.data?.data ?? resp.data;\n const items = (payload?.items ?? payload?.list ?? payload?.records ?? []) as TenantMetaEntry[];\n return { items, total: payload?.total, raw: payload };\n}\n\n/** 读单份 meta 原文。返回 `content` 字符串(后端包在信封里时自动解包)。 */\nexport async function getTenantMeta(http: AxiosInstance, path: string): Promise<string> {\n const resp = await http.get('/api/tenant-meta/meta', { params: { path } });\n const payload = resp.data?.data ?? resp.data;\n if (typeof payload === 'string') return payload;\n return String(payload?.content ?? '');\n}\n\nexport interface SaveMetaResult {\n success?: boolean;\n message?: string;\n [k: string]: unknown;\n}\n\nexport async function saveTenantMeta(\n http: AxiosInstance,\n path: string,\n content: string,\n): Promise<SaveMetaResult> {\n const resp = await http.put('/api/tenant-meta/meta', { content }, { params: { path } });\n return (resp.data?.data ?? resp.data) as SaveMetaResult;\n}\n\nexport async function deleteTenantMeta(\n http: AxiosInstance,\n path: string,\n): Promise<SaveMetaResult> {\n const resp = await http.delete('/api/tenant-meta/meta', { params: { path } });\n return (resp.data?.data ?? resp.data) as SaveMetaResult;\n}\n","import type { AxiosInstance } from 'axios';\nimport yaml, { JSON_SCHEMA } from 'js-yaml';\nimport { CliError } from './errors/cli-error';\nimport { CliErrorCode } from './errors/codes';\n\n/**\n * 睿戎 Chiron 交付技能库客户端。\n *\n * <p>🔴 这些端点**免认证**——交付顾问接手的是「还没有租户、还没有账号」的空环境,\n * 必须先装技能才能建租户。所以本模块只需要 endpoint,不碰 identity/token。\n *\n * 后端契约(ChironController):\n * - `GET {base}/skills.json` 目录 manifest\n * - `GET {base}/skills/{id}.md` 技能原文\n * - `GET {base}/skills/{id}/references/{path}` 技能自带的参考文件\n * - `GET {base}/skills/{id}/files?path=...` 完整 Skill 目录中的原始文件\n *\n * 🔴 `{base}` 是 `'/api'` 或 `''`,由 {@link resolveChironBase} 探出来。真实部署只把\n * `/api/**` 路由给后端,根路径归前端 SPA——`GET /skills.json` 在客户环境上拿回来的是\n * 一段 HTML。而老版本后端只有根路径。两种都要能连上,所以先探再用,不写死。\n */\n\nexport interface ChironSkillFile {\n path: string;\n bytes: number;\n sha256: string;\n}\n\nexport interface ChironSkill {\n id: string;\n title: string;\n category: string;\n /** cli = 动作面已全改写为 hcm 命令;curl = 仍为原始 HTTP 形态 */\n actionSurface: 'cli' | 'curl';\n description: string;\n /** 交付生命周期阶段(多值)——一个技能天然跨阶段 */\n stages: string[];\n /** 安装本技能前必须同时安装的其他 Chiron 技能 id;旧服务端缺省时视为空。 */\n requires?: string[];\n bytes: number;\n /** 新服务端提供完整 Skill 目录;旧服务端缺省时只消费 SKILL.md。 */\n files?: ChironSkillFile[];\n markdownUrl: string;\n /**\n * 技能自带的参考文件相对路径(相对 `<skill>/references/`)。SKILL.md 正文用\n * `[x](references/x.md)` 引它们——不跟着装下来就是一堆死链。旧服务端缺省时视为空。\n */\n references?: string[];\n}\n\nexport interface ChironStage {\n key: string;\n name: string;\n description: string;\n /** 该阶段下的技能数。0 = 交付路径上的覆盖缺口 */\n skillCount: number;\n}\n\nexport interface ChironCategory {\n key: string;\n name: string;\n description: string;\n skills: ChironSkill[];\n}\n\nexport interface ChironManifest {\n name: string;\n title: string;\n tagline: string;\n skillCount: number;\n stages: ChironStage[];\n categories: ChironCategory[];\n}\n\n/** 技能库端点的前缀候选,按优先级排列。`/api` 在前:它是真实部署下唯一到得了后端的那条。 */\nconst CHIRON_BASE_CANDIDATES = ['/api', ''] as const;\n\nfunction looksLikeManifest(data: unknown): data is ChironManifest {\n if (!data || typeof data !== 'object') return false;\n const m = data as Record<string, unknown>;\n return m.name === 'chiron' && Array.isArray(m.categories);\n}\n\n/**\n * 探出本环境技能库的前缀。\n *\n * 为什么不能写死任何一个:\n * - 真实部署(反代把 `/api/**` 给后端、其余给前端)下根路径返回的是前端 SPA 的 HTML,\n * HTTP 200 但根本不是 manifest —— 所以判据必须看**内容**,不能看状态码;\n * - 还没升级到带 `/api` 别名那一版的后端,只有根路径可用。\n *\n * 两条都探不到时抛出可执行的错误,而不是把一段 HTML 当目录解析出满屏乱码。\n */\nexport async function resolveChironBase(http: AxiosInstance): Promise<string> {\n const tried: string[] = [];\n for (const base of CHIRON_BASE_CANDIDATES) {\n const url = `${base}/skills.json`;\n tried.push(url);\n try {\n const resp = await http.get(url);\n const data = resp.data?.data ?? resp.data;\n if (looksLikeManifest(data)) return base;\n } catch {\n // 401 / 404 / 网络错误都只说明这条前缀不通,继续探下一条\n }\n }\n throw new CliError({\n code: CliErrorCode.INVALID_ARGUMENT,\n message:\n `这个环境上找不到睿戎技能库(试过 ${tried.join(' 和 ')})。`\n + '常见原因:① 后端版本还没有 /api/skills 端点,升级后端即可;'\n + '② endpoint 填的是前端地址且未反代到后端;③ 地址或网络不通。',\n });\n}\n\nexport async function fetchSkillCatalog(http: AxiosInstance, base: string): Promise<ChironManifest> {\n const resp = await http.get(`${base}/skills.json`);\n return (resp.data?.data ?? resp.data) as ChironManifest;\n}\n\nexport async function fetchSkillMarkdown(http: AxiosInstance, id: string, base: string): Promise<string> {\n const resp = await http.get(`${base}/skills/${encodeURIComponent(id)}.md`, {\n responseType: 'text',\n transformResponse: [(d) => d],\n });\n return String(resp.data ?? '');\n}\n\n/** 取完整 Skill 目录中的任意原始文件;`relativePath` 始终按 query 参数传递。 */\nexport async function fetchSkillFile(\n http: AxiosInstance,\n id: string,\n relativePath: string,\n base: string,\n): Promise<Buffer> {\n assertSafeSkillId(id);\n assertSafeSkillFilePath(relativePath);\n const resp = await http.get(`${base}/skills/${encodeURIComponent(id)}/files`, {\n params: { path: relativePath },\n responseType: 'arraybuffer',\n transformResponse: [(data) => data],\n });\n return Buffer.from(resp.data ?? []);\n}\n\n/** 取技能自带的参考文件原文。`relativePath` 相对 `<skill>/references/`。 */\nexport async function fetchSkillReference(\n http: AxiosInstance,\n id: string,\n relativePath: string,\n base: string,\n): Promise<string> {\n assertSafeReferencePath(relativePath);\n const encoded = relativePath.split('/').map(encodeURIComponent).join('/');\n const resp = await http.get(`${base}/skills/${encodeURIComponent(id)}/references/${encoded}`, {\n responseType: 'text',\n transformResponse: [(d) => d],\n });\n return String(resp.data ?? '');\n}\n\n/**\n * 参考文件相对路径约束——与后端 `ChironCatalogService.SAFE_REFERENCE_PATH` 同形。\n *\n * 🔴 服务端返回什么,本地就按什么建目录,所以这里是**写文件前**的闸:`..` 段会让\n * 安装写出技能目录之外。不要因为「服务端已经校验过」就省掉——CLI 可以指向任何 endpoint。\n */\nexport function assertSafeReferencePath(relativePath: string): void {\n const ok = /^[A-Za-z0-9_][A-Za-z0-9._-]{0,63}(\\/[A-Za-z0-9_][A-Za-z0-9._-]{0,63}){0,2}$/\n .test(relativePath);\n if (!ok) {\n throw invalid(`参考文件路径不合法:${JSON.stringify(relativePath)}`);\n }\n}\n\n/** 完整 Skill 目录相对路径约束;允许有限深度之外的目录,但拒绝任何穿越和反斜线。 */\nexport function assertSafeSkillFilePath(relativePath: string): void {\n const segments = relativePath?.split('/') ?? [];\n if (\n !relativePath ||\n relativePath.length > 512 ||\n relativePath.startsWith('/') ||\n relativePath.includes('\\\\') ||\n segments.some(\n (segment) =>\n !segment ||\n segment === '.' ||\n segment === '..' ||\n !/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(segment),\n )\n ) {\n throw invalid(`非法 Skill 文件路径: \"${relativePath}\"`);\n }\n}\n\n/** 归一 references 字段:旧服务端缺省时视为空,逐条过白名单。 */\nexport function normalizeReferences(raw: unknown, skillId: string): string[] {\n if (raw === undefined || raw === null) return [];\n if (!Array.isArray(raw)) throw invalid(`技能 ${skillId} 的 references 只能是字符串数组`);\n const out: string[] = [];\n for (const value of raw) {\n if (typeof value !== 'string' || !value.trim()) {\n throw invalid(`技能 ${skillId} 的 references 只能是非空字符串数组`);\n }\n const rel = value.trim();\n assertSafeReferencePath(rel);\n if (!out.includes(rel)) out.push(rel);\n }\n return out;\n}\n\nexport function flattenSkills(manifest: ChironManifest): ChironSkill[] {\n return (manifest.categories ?? []).flatMap((c) => c.skills ?? []);\n}\n\n/**\n * 计算技能安装闭包,返回稳定的「依赖在前、目标在后」顺序。\n *\n * 这是纯函数:远程安装与测试共用,不在这里发请求或写文件。旧目录没有 requires 时\n * 自然退化为只安装目标技能。\n */\nexport function resolveSkillInstallOrder(manifest: ChironManifest, targetId: string): ChironSkill[] {\n const byId = new Map<string, ChironSkill>();\n for (const skill of flattenSkills(manifest)) {\n if (byId.has(skill.id)) {\n throw invalid(`技能目录存在重复 id: ${skill.id}`);\n }\n byId.set(skill.id, skill);\n }\n if (!byId.has(targetId)) {\n throw invalid(`技能目录中不存在 ${targetId}`);\n }\n\n const state = new Map<string, 'visiting' | 'done'>();\n const path: string[] = [];\n const order: ChironSkill[] = [];\n\n const visit = (id: string, requiredBy?: string): void => {\n const current = state.get(id);\n if (current === 'done') return;\n if (current === 'visiting') {\n const cycleStart = path.indexOf(id);\n const cycle = [...path.slice(Math.max(0, cycleStart)), id];\n throw invalid(`技能依赖存在环: ${cycle.join(' -> ')}`);\n }\n\n const skill = byId.get(id);\n if (!skill) {\n throw invalid(`技能 ${requiredBy ?? targetId} 依赖不存在: ${id}`);\n }\n assertSafeSkillId(skill.id);\n state.set(id, 'visiting');\n path.push(id);\n for (const required of normalizeRequirements(skill.requires, skill.id)) {\n visit(required, skill.id);\n }\n path.pop();\n state.set(id, 'done');\n order.push(skill);\n };\n\n visit(targetId);\n return order;\n}\n\n/**\n * 全目录的安装顺序:逐个技能求依赖闭包后合并去重,依赖仍排在被依赖者之前。\n *\n * `install --all` 用它。顾问不必知道谁依赖谁,也不该为了装全而写 shell 循环——\n * 那个循环在 zsh 下 `for id in $(...)` 不分词,会一个都不装却退出码 0。\n */\nexport function resolveAllSkillsInstallOrder(manifest: ChironManifest): ChironSkill[] {\n const order = new Map<string, ChironSkill>();\n const all = flattenSkills(manifest).map((skill) => skill.id).sort();\n for (const id of all) {\n for (const skill of resolveSkillInstallOrder(manifest, id)) {\n if (!order.has(skill.id)) order.set(skill.id, skill);\n }\n }\n return [...order.values()];\n}\n\n/** 读 SKILL.md 的 frontmatter;没有 frontmatter 返回空对象,YAML 非法则报错。 */\nexport function parseSkillFrontmatter(markdown: string, skillId: string): Record<string, unknown> {\n const match = /^---\\r?\\n([\\s\\S]*?)\\r?\\n---(?:\\r?\\n|$)/.exec(markdown);\n if (!match) return {};\n\n let frontmatter: unknown;\n try {\n frontmatter = yaml.load(match[1] ?? '', { schema: JSON_SCHEMA });\n } catch (cause) {\n throw new CliError({\n code: CliErrorCode.INVALID_ARGUMENT,\n message: `技能 ${skillId} 的 frontmatter 不是合法 YAML`,\n cause,\n });\n }\n if (!frontmatter || typeof frontmatter !== 'object' || Array.isArray(frontmatter)) return {};\n return frontmatter as Record<string, unknown>;\n}\n\n/** 从一份 SKILL.md 读取本地安装所需的 requires,不接受正文中的近似语法。 */\nexport function parseSkillRequirements(markdown: string, skillId: string): string[] {\n return normalizeRequirements(parseSkillFrontmatter(markdown, skillId).requires, skillId);\n}\n\n/** Chiron 与公开端点共用的小写安全 id 约束。 */\nexport function assertSafeSkillId(id: string): void {\n if (!/^[a-z0-9][a-z0-9-]{0,63}$/.test(id)) {\n throw invalid(`技能 id 只能是小写字母/数字/连字符,收到 \"${id}\"`);\n }\n}\n\nfunction normalizeRequirements(raw: unknown, skillId: string): string[] {\n if (raw === undefined || raw === null) return [];\n const values = Array.isArray(raw) ? raw : [raw];\n const normalized: string[] = [];\n for (const value of values) {\n if (typeof value !== 'string' || !value.trim()) {\n throw invalid(`技能 ${skillId} 的 requires 只能是非空技能 id 数组`);\n }\n const id = value.trim();\n assertSafeSkillId(id);\n if (!normalized.includes(id)) normalized.push(id);\n }\n return normalized;\n}\n\nfunction invalid(message: string): CliError {\n return new CliError({ code: CliErrorCode.INVALID_ARGUMENT, message });\n}\n\n/** 按交付生命周期阶段筛选。 */\nexport function filterByStage(skills: ChironSkill[], stage?: string): ChironSkill[] {\n if (!stage) return skills;\n return skills.filter((s) => (s.stages ?? []).includes(stage));\n}\n\n/** 关键词匹配 id / 标题 / 描述 / 分类,全部小写包含。 */\nexport function matchSkills(skills: ChironSkill[], keyword?: string): ChironSkill[] {\n if (!keyword) return skills;\n const k = keyword.toLowerCase();\n return skills.filter((s) =>\n [s.id, s.title, s.description, s.category].some((f) => String(f ?? '').toLowerCase().includes(k)),\n );\n}\n","import type { AxiosInstance } from 'axios';\n\n/**\n * 租户系统参数(配置中心)。\n *\n * <p>后端契约(TenantSettingDomainController):\n * <ul>\n * <li>`GET /api/tenant-settings/domains/{domain}` 读某域(定义 + 租户覆盖值合并,缺失回退默认值)</li>\n * <li>`PATCH /api/tenant-settings/domains/{domain}` 写,body `{items:[{namespace,settingKey,value}]}`</li>\n * <li>`DELETE /api/tenant-settings/domains/{domain}/items/{ns}/{key}?revision=N` 清除租户覆盖</li>\n * </ul>\n *\n * <p>🔴 **不要走标准 Model 的 PUT 改这些值**:那条路直接写库,绕过定义校验、\n * 乐观锁与历史留痕——改坏 namespace/valueType 会让记录不再匹配任何定义。\n * 后端已从该 Model 的权限里移除 update action,这里也只暴露上面三条受控路径。\n */\n\nexport interface SettingItem {\n namespace: string;\n settingKey: string;\n value?: unknown;\n valueType?: string;\n defaultValue?: unknown;\n revision?: number;\n label?: string;\n [k: string]: unknown;\n}\n\nexport async function getSettingDomain(\n http: AxiosInstance,\n domain: string,\n): Promise<{ items: SettingItem[]; raw: unknown }> {\n const resp = await http.get(`/api/tenant-settings/domains/${encodeURIComponent(domain)}`);\n const payload = resp.data?.data ?? resp.data;\n const items = (payload?.items ?? payload ?? []) as SettingItem[];\n return { items: Array.isArray(items) ? items : [], raw: payload };\n}\n\nexport interface SettingWrite {\n namespace: string;\n settingKey: string;\n value: unknown;\n}\n\nexport async function patchSettingDomain(\n http: AxiosInstance,\n domain: string,\n items: SettingWrite[],\n): Promise<unknown> {\n const resp = await http.patch(`/api/tenant-settings/domains/${encodeURIComponent(domain)}`, { items });\n return resp.data?.data ?? resp.data;\n}\n\nexport async function resetSettingItem(\n http: AxiosInstance,\n domain: string,\n namespace: string,\n settingKey: string,\n revision?: number,\n): Promise<unknown> {\n const url =\n `/api/tenant-settings/domains/${encodeURIComponent(domain)}` +\n `/items/${encodeURIComponent(namespace)}/${encodeURIComponent(settingKey)}`;\n const resp = await http.delete(url, {\n params: revision === undefined ? {} : { revision },\n });\n return resp.data?.data ?? resp.data;\n}\n\n/**\n * 解析 `namespace.settingKey=value` 形态的 --set 参数。\n *\n * settingKey 取最后一段,其余是 namespace —— namespace 本身含点\n * (如 `security.password`),所以从右边切,不能 split('.') 取前两段。\n */\nexport function parseSettingAssignment(raw: string): SettingWrite {\n const eq = raw.indexOf('=');\n if (eq <= 0) throw new Error(`--set 需要 namespace.key=value 形态,收到 \"${raw}\"`);\n const path = raw.slice(0, eq).trim();\n const value = raw.slice(eq + 1);\n const dot = path.lastIndexOf('.');\n if (dot <= 0) throw new Error(`--set 的键需含 namespace,如 security.password.passwordPolicy=complex,收到 \"${path}\"`);\n return { namespace: path.slice(0, dot), settingKey: path.slice(dot + 1), value };\n}\n","/**\n * FDE 侦察产物的目录布局与路径遏制。\n *\n * 产物按**爬取身份**分叉:`<root>/recon/<tenantId>/<sourceKey>/…`。\n * 同一个租户、同一条命令,两个 FDE 顾问绑在不同 workspace 上时看到的元数据是不同的——\n * 把两份产物混在一个目录里,后来者不是「挤在一起」,而是**静默覆盖**前一个人的\n * `models.json` / `meta/*.json`,而 `fingerprint.json` 里只剩最后写入者的 `extensionSource`。\n *\n * 移植自 `hcm_nx_core` 的 `tools/fde-kit/hcmnx_kit/layout.py`(ADR-319 刀 6b)。\n * 行为基线是 `docs/superpowers/specs/2026-08-28-fde-kit-behavior-contract.md` §1.3 的 19 条 P。\n */\nimport { createHash } from 'node:crypto';\nimport { mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport * as path from 'node:path';\n\nconst UNSAFE = /[^A-Za-z0-9._-]/g;\n\n/** 把不安全字符换成下划线。**不保证单射**,只用于人眼可读的那一半。 */\nfunction slug(value: string): string {\n return (value || 'unknown').replace(UNSAFE, '_');\n}\n\n/**\n * sourceKey 实测形如 `ws:poc-cnpc`;冒号等字符不能直接进路径。\n *\n * 🔴 纯字符替换做不到单射(`ws:alice` 与 `ws_alice` 会撞进同一个目录),而不同 sourceKey\n * 的产物不可互换——撞目录不是「挤在一起」,是后跑的 FDE 静默覆盖前一个 FDE 的\n * models.json / meta/*.json。所以在可读 slug 后面缀上**原始键**的短摘要:\n * slug 用来给人看,后缀用来保证身份。\n */\nexport function safeSegment(value: string): string {\n const raw = value || 'unknown';\n const digest = createHash('sha256').update(raw, 'utf8').digest('hex').slice(0, 10);\n return `${slug(raw)}-${digest}`;\n}\n\n/**\n * 租户段的归一。\n *\n * 🔴 **它就是 `safeSegment`,不是另一个归一函数。** 保留独立函数名只为让调用点读起来\n * 有语义(也给命令层一个公开入口,两端才不会各自拼路径),实现必须**原样委派**,\n * 绝不允许两个归一算法并存。\n *\n * ⚠️ 不要改成「校验 tenantId 字符集、非法即拒」:那会在某个 tenantId 真含特殊字符的\n * 环境上**硬拒一个合法环境**,把可用性押在我们对 tenantId 取值范围的猜测上。\n * 摘要后缀不拒绝任何输入,且无条件单射。\n */\nexport function tenantSegment(value: string): string {\n return safeSegment(value);\n}\n\n/**\n * `p` 解析后是否落在 `container` 之内(含相等)。\n *\n * 🔴 **判分量,不判前缀**。写成 `rel.startsWith('..')` 会误杀两类合法路径,\n * 且两类都真实存在于本模块:\n * · `..foo.json` / `...json` —— 合法文件名(契约 §1.3「仅以点开头的文件名不被误杀」);\n * · `..-<摘要>/` —— `safeSegment('..')` 的产物,租户 id 恰好是 `..` 时的正常目录名。\n * 移植时这一格栽过一次:三条用例同时红,根因都在这个前缀判据上。\n */\nfunction isInside(p: string, container: string): boolean {\n const rel = path.relative(container, p);\n if (rel === '') return true;\n if (path.isAbsolute(rel)) return false;\n return !rel.split(path.sep).includes('..');\n}\n\n/**\n * 判 `rel` 里有没有 `..` **分量**。\n *\n * 🔴 判分量而不是字符串 `includes`:`..foo.json` / `...json` 是合法文件名,\n * 字符串判据会把它们误杀。\n *\n * 🔴 `/` **和** `\\` 都当分隔符:`valuedomain/..\\evil.json`、`valuedomain\\..\\evil.json`\n * 这两种形态也必须判得出 `..` 分量。只按 POSIX 分隔符切对它们恒返 false,是个盲区。\n * 收益不是「支持 Windows」,是**多认一种分隔符、少一个盲区**。\n */\nfunction hasDotDotComponent(rel: string): boolean {\n return rel.split(/[/\\\\]/).includes('..');\n}\n\nexport class ReconLayout {\n readonly root: string;\n readonly tenantId: string;\n readonly sourceKey: string;\n readonly dir: string;\n\n constructor(root: string, tenantId: string, sourceKey: string) {\n this.root = root;\n this.tenantId = String(tenantId);\n this.sourceKey = sourceKey;\n // 🔴 产物按爬取身份分叉:不同 sourceKey 的产物不可互换。\n // 两段都走 `safeSegment`——单射由摘要后缀无条件保证。\n this.dir = path.join(\n this.root,\n 'recon',\n tenantSegment(this.tenantId),\n safeSegment(sourceKey),\n );\n // 🔴 容器断言**不是装饰品**:它守的前提是「**归一函数没被改弱**」。\n // 把 `safeSegment` 退回纯字符替换,`--tenant \"..\"` 立刻让产物根跳出 recon/:\n // `<out>/recon/../active-xxx` 规范化后是 `<out>/active-xxx`。\n // `resolvePath` 的闸拦不住这一步——那道闸只比 `this.dir`,而 `this.dir` 自己已经偏了。\n const container = path.resolve(this.root, 'recon');\n if (!isInside(path.resolve(this.dir), container)) {\n throw new LayoutContainmentError(\n `租户段越界,拒绝构造产物目录:tenantId=${JSON.stringify(tenantId)}(产物根 ${container})`,\n );\n }\n }\n\n /**\n * 把相对路径解析成绝对路径,并过两道**互相印证**的遏制闸。\n *\n * 🔴 `rel` 里会嵌入**现爬的 modelKey**:`meta/<k>.model.json`、`valuedomain/<k>.json`、\n * `mapping/<k>.json`。服务端说什么,这里就拼什么。\n *\n * 实测分档(沿用 Python 侧 2026-08-16 的实跑结论):\n * `../evil` → 规范化后**仍在产物根内**,写成 `<产物根>/evil.json`。第二道闸**拦不住**。\n * `../models` → 同上,**顶掉 recon 自己的模型名册**,写入方拿到「成功」,产物里没有留痕。\n * `../../evil` → 真的逃出产物根 ⇒ 第二道闸拦得住。\n * `/etc/passwd` → 前导 `/` 被路径规范化吃掉,落在 `valuedomain/etc/passwd.json`。\n * ⇒ 真正的危害不是「逃出产物根」(那一档本来就拦得住),是「**逃出预期子目录、\n * 静默顶掉同级产物**」——`models.json` / `fingerprint.json` / `gates.json` 全在射程内。\n *\n * ⚠️ 拒绝而不是消毒:消毒会让 `meta/<modelKey>.json` 不再能按 modelKey grep 回去。\n */\n resolvePath(rel: string, createParents = true): string {\n const p = path.join(this.dir, rel);\n // 第一道闸:守「逃出预期子目录」。\n if (hasDotDotComponent(rel)) {\n throw new LayoutContainmentError(\n `产物相对路径含 \\`..\\` 分量,拒绝写入:${JSON.stringify(rel)}` +\n `(会越出预期子目录、静默顶掉同级产物;产物根 ${this.dir})`,\n );\n }\n // 第二道闸:守「逃出产物根」。\n if (!isInside(path.resolve(p), path.resolve(this.dir))) {\n throw new LayoutContainmentError(\n `产物相对路径越界,拒绝写入:${JSON.stringify(rel)}(产物根 ${this.dir})`,\n );\n }\n if (createParents) {\n // 🔴 读路径传 createParents=false:读一个不存在的产物不该顺手建出目录,\n // 否则一个空目录会冒充「这里爬过」——那是必须分开的两件事。\n mkdirSync(path.dirname(p), { recursive: true });\n }\n // 🔴 返回未 realpath 的 p:macOS 上 /var → /private/var 的符号链接会让\n // 调用方拿 this.dir 拼出来的路径断言全部错位。\n return p;\n }\n\n writeJson(rel: string, obj: unknown): string {\n const p = this.resolvePath(rel);\n // JSON.stringify 默认不转义非 ASCII——中文逐字落盘,本地 grep 才找得到。\n writeFileSync(p, JSON.stringify(obj, null, 2), 'utf8');\n return p;\n }\n\n writeJsonl(rel: string, rows: Iterable<unknown>): string {\n const p = this.resolvePath(rel);\n let body = '';\n for (const row of rows) body += `${JSON.stringify(row)}\\n`;\n writeFileSync(p, body, 'utf8');\n return p;\n }\n\n writeText(rel: string, text: string): string {\n const p = this.resolvePath(rel);\n writeFileSync(p, text, 'utf8');\n return p;\n }\n\n /**\n * 逐行读一份 JSONL 产物。\n *\n * 🔴 与 `readJson` 同走那两道越界闸——同一个收口点不能一半有闸一半没有。\n * 🔴 **跳过空行**(末尾换行是 `writeJsonl` 必然产生的),但**不跳过坏行**:\n * 坏行原样抛,因为「这一行读不出来」和「这个产物没有这一行」是两件事。\n */\n readJsonlRows(rel: string): unknown[] {\n const body = readFileSync(this.resolvePath(rel, false), 'utf8');\n const rows: unknown[] = [];\n for (const line of body.split('\\n')) {\n const trimmed = line.trim();\n if (trimmed) rows.push(JSON.parse(trimmed));\n }\n return rows;\n }\n\n readJson(rel: string): unknown {\n // 走 resolvePath:读路径同样会嵌入现爬的 modelKey,越界读同样要拒绝。\n return JSON.parse(readFileSync(this.resolvePath(rel, false), 'utf8'));\n }\n}\n\n/**\n * 路径遏制被触发。\n *\n * 独立于 `CliError`:它是**内部不变量**被破坏,不是顾问能修的用法错误。\n * 命令层负责把它翻成一句人话 + 退出码,见 `packages/cli/src/commands/delivery-doctor.ts`。\n */\nexport class LayoutContainmentError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'LayoutContainmentError';\n }\n}\n","/**\n * 漂移握手四元组。\n *\n * 回答的是一个问题:**「我手上这份基线,还配得上眼前这个环境吗?」**\n *\n * 三个分量作门(`GATE_COMPONENTS`),第四个(backendVersion)只留档——实测 on-prem 恒返\n * \"unknown\",拿它作门只会得到一个恒等式。\n *\n * 移植自 `hcm_nx_core` 的 `tools/fde-kit/hcmnx_kit/fingerprint.py`(ADR-319 刀 6b)。\n * 行为基线是行为契约清单 §1.2 的 30 条 P。\n */\nimport { createHash } from 'node:crypto';\nimport type { Outcome, ReconProbe } from './probe';\n\n/** 🔴 只有这三个分量作门。backendVersion 仅记录——实测它在 on-prem 恒返 \"unknown\"。 */\nexport const GATE_COMPONENTS = [\n 'tenantMetaManifestDigest',\n 'extensionSource',\n 'modelListHash',\n] as const;\n\nexport const BLIND_SPOTS =\n '产品升级:manifest 只读租户 workspace 的 metas/ 目录,' +\n '产品升级改的是安装包内 _metas/,四元组不会变——该场景的防线只有行为探针一条。' +\n ' | 同字节数内容替换:清单只给 name/size/lastModified,' +\n '把标签「部门」改成「机构」在 UTF-8 下恰好都是 6 字节,摘要不变。' +\n '⇒ 字段名叫 ManifestDigest 而非 Fingerprint,名字必须说实话。';\n\nexport const MODELS_PATH = '/api/system/models';\nexport const MANIFEST_PATH = '/api/tenant-meta/list';\nexport const VERSION_PATH = '/api/version/backend';\nexport const META_PATH = '/api/models/{model}/meta';\n\nexport const EXTENSION_SOURCE_HEADER = 'x-hcm-extension-source';\n\n/**\n * 探针上限。取不到就取不到,**不许**为了拿到一个值把 761 个模型全试一遍——\n * 那会让 doctor 从「几秒」变成「几分钟」,而 doctor 存在的意义就是**快速**告诉顾问他站在哪儿。\n *\n * 🔴 为什么是 50 而不是 20:「20 够不够」这个问题,原工位实测第 1 个 key 就命中,\n * 但**这不能证明任何别的环境**;若某环境排序靠前的 key 恰好全是隐藏型(实测有 19 个),\n * 探针会空手而归并阻断门,而 key 21 可能就带着这个头。\n * ⇒ 正确的应对**不是**把 20 换成另一个同样证明不了的数字,而是:\n * ① 放宽到显然够用又不至于拖慢 doctor(命中即停,坏情况 50 × ~50ms ≈ 2.5s);\n * ② 把失败讲清楚(见 attempted/answered)。判据从「猜一个够用的数」换成「失败时说得出为什么」。\n */\nexport const MAX_SOURCE_PROBES = 50;\n\n/**\n * 本模块的全部前提是「打一个不认识的环境」。一个把 totalPages 报成 2**31 的\n * 服务端会让顾问的笔记本原地转圈。200 页 × 200 行 = 4 万行,远超真实 workspace。\n */\nexport const MAX_MANIFEST_PAGES = 200;\n\n/**\n * 🔴 只有**门相关**端点的取数失败才阻断。backendVersion 是明文钉死的「仅记录、不作门」\n * 分量——让它的取数失败从 incomplete: 侧门变成门,等于在任何缺 `/api/version/backend`\n * 的老构建上把门恒推成「不匹配」,而门被常态化绕过(`--force-stale`)正是要防的事。\n */\nconst GATE_SOURCES = [MODELS_PATH, MANIFEST_PATH, META_PATH];\n\nexport interface ModelEntry {\n modelKey?: string;\n type?: string;\n [k: string]: unknown;\n}\n\n/**\n * `/api/system/models` 的响应体 → **可用的**模型条目。\n *\n * 🔴 **这是唯一一道门**。原先 doctor / computeFingerprint / 布局解析三个点各写各的过滤,\n * 第三个漏了——而它跑在**最前面**,客户网关一改清单形状,recon 就在第一步崩掉,\n * 而 FDE 手上没有源码、没有安装包、也没有第二份基线可对照。\n *\n * 两种敌意形状都在这里挡掉,调用方不必(也不许)自己判:\n * ① `body` 整体不是对象——服务端**声明**返 object,但网关扒掉 envelope 直接返数组是真会发生的;\n * ② `models[]` 里混进非对象元素——部分序列化失败 / 拼接出错。\n *\n * ⚠️ **降级不是静默**:这里只负责「不崩」,「有没有掉东西」由调用方各自留痕。\n */\nexport function modelEntries(body: unknown): ModelEntry[] {\n if (!isPlainObject(body)) return [];\n const raw = (body as Record<string, unknown>).models;\n if (!Array.isArray(raw)) return [];\n return raw.filter(isPlainObject) as ModelEntry[];\n}\n\n/**\n * `/api/system/models` 的响应体 → 探针用的 modelKey 列表(去空串 + 排序)。\n *\n * 🔴 空串过滤不能省:一条缺 `modelKey` 的记录会让第一发打到 `/api/models//meta`,\n * 白烧一发**有限的**探针配额。\n * 🔴 排序 = 探针确定性:同一个环境两次 recon 必须探同一个模型,否则 `extensionSourceProbe`\n * 会无端漂移,看上去像环境变了。\n */\nexport function modelKeys(body: unknown): string[] {\n return modelEntries(body)\n .map((m) => (typeof m.modelKey === 'string' ? m.modelKey : ''))\n .filter((k) => k !== '')\n .sort();\n}\n\nexport interface ProbeResult {\n sourceKey: string;\n probeModelKey: string;\n attempted: number;\n answered: number;\n}\n\n/**\n * 探 `X-HCM-Extension-Source`。\n *\n * 🔴 **这个头只挂在 `GET /api/models/{model}/meta` 上,`/api/system/models` 上没有**。\n * 读错端点的后果不是「偶尔取不到」,是**每一个真实环境上恒为 unknown**:三个门分量残掉\n * 一个 ⇒ `compareFingerprint` 恒返 `incomplete:` ⇒ 门被 `--force-stale` 常态化绕过。\n *\n * 🔴 `answered` 不是凑数的统计量,它是「unknown 到底是哪种 unknown」的唯一判据。\n * 只返回 `(sourceKey, probeModelKey)` 时,「一个模型都没答应」与「答应了但没回这个头」\n * **都**返回空串、**都**写同一条失败条目——顾问看到 `unknown` 照样不知道该查连通/权限还是查反代。\n * **一个自己都分辨不了的分辨承诺比不承诺更糟**,它让人以为这件事已经处理了。\n */\nexport async function probeExtensionSource(\n probe: ReconProbe,\n keys: string[],\n): Promise<ProbeResult> {\n let attempted = 0;\n let answered = 0;\n for (const key of [...keys].sort().slice(0, MAX_SOURCE_PROBES)) {\n attempted += 1;\n const out = await probe.get(META_PATH.replace('{model}', key));\n // meta 404 是常态(隐藏型模型,实测有 19 个),换下一个\n if (out.error !== 'ok') continue;\n answered += 1;\n const value = out.headers[EXTENSION_SOURCE_HEADER];\n if (value) return { sourceKey: value, probeModelKey: key, attempted, answered };\n }\n return { sourceKey: '', probeModelKey: '', attempted, answered };\n}\n\n/**\n * 分页取全量租户元数据清单。page 从 0 开始,分页参数名是 `pageSize`(不是 `size`)。\n *\n * 🔴 一旦某页没取回来就把端点记进 failures:清单为空(真的没有 overlay 文件)\n * 与清单没爬到(401/网关截断)会算出**同一个** sha256(''),产物里必须有一处把这两件事分开,\n * 否则「爬过且为空」和「没爬」在磁盘上无法区分。\n */\nasync function fetchManifestRows(\n probe: ReconProbe,\n failures: string[],\n): Promise<Record<string, unknown>[]> {\n const rows: Record<string, unknown>[] = [];\n let page = 0;\n const pageSize = 200;\n for (;;) {\n const out = await probe.get(MANIFEST_PATH, { page, pageSize });\n if (out.error !== 'ok' || !isPlainObject(out.body)) {\n failures.push(MANIFEST_PATH);\n break;\n }\n const body = out.body as Record<string, unknown>;\n const chunk = body.data ?? [];\n if (!Array.isArray(chunk)) {\n failures.push(MANIFEST_PATH);\n break;\n }\n rows.push(...(chunk.filter(isPlainObject) as Record<string, unknown>[]));\n const totalPages = typeof body.totalPages === 'number' ? body.totalPages : 0;\n page += 1;\n if (page >= totalPages || chunk.length === 0) break;\n if (page >= MAX_MANIFEST_PAGES) {\n // 🔴 截断必须留痕,否则又是一个「截断了但看不出来」:少了后面几页的清单,\n // 摘要照样算得出来,看上去和完整清单没两样。\n failures.push(`${MANIFEST_PATH}#page-cap`);\n break;\n }\n }\n return rows;\n}\n\nfunction manifestDigest(rows: Record<string, unknown>[]): string {\n // 对**全量清单**求摘要:集合成员变化必然改变摘要(高水位做不到这点——\n // 删掉一个非最新的文件、或恢复一个旧时间戳文件,高水位纹丝不动)。\n const lines = rows\n .map((r) => `${r.name}|${r.size}|${r.lastModified}`)\n .sort();\n return sha256(lines.join('\\n'));\n}\n\nexport interface Fingerprint {\n tenantMetaManifestDigest: string;\n manifestEntryCount: number;\n extensionSource: string;\n extensionSourceProbe: string;\n extensionSourceProbeAttempted: number;\n extensionSourceProbeAnswered: number;\n modelListHash: string;\n modelCount: number;\n backendVersion: unknown;\n capabilities: string;\n createdAt: string;\n ttlDays: number;\n coverageNotes: {\n gateComponents: string[];\n blindSpots: string;\n fetchFailures: string[];\n };\n}\n\nexport async function computeFingerprint(\n probe: ReconProbe,\n opts: { ttlDays?: number; now?: () => Date } = {},\n): Promise<Fingerprint> {\n const ttlDays = opts.ttlDays ?? 30;\n const now = opts.now ?? (() => new Date());\n const failures: string[] = [];\n\n const modelsOut = await probe.get(MODELS_PATH);\n // 🔴 body 声明是 object,服务端完全可能返回 array/string——直接取属性会在客户现场炸。\n // 判成功一律 error === 'ok',再核类型。\n const modelsOk = modelsOut.error === 'ok' && isPlainObject(modelsOut.body);\n if (!modelsOk) failures.push(MODELS_PATH);\n\n const models = modelsOk ? modelEntries(modelsOut.body) : [];\n const keys = modelsOk ? modelKeys(modelsOut.body) : [];\n\n if (modelsOut.error === 'ok' && keys.length === 0) {\n // 🔴 请求成功但**一个可用 modelKey 都没有** ⇒ modelListHash 会算出 sha256('')\n // ——一个语法完整、语义为空的值——而 fetchFailures 若保持为空,门就拿它当\n // 「爬到了,就是这个值」放行。实测该环境有 761 个模型,**零可用 key 只可能是出了事**\n // (键名读错 / 权限 / 网关改写),不可能是「这个环境真的没有模型」。\n failures.push(`${MODELS_PATH}#empty-model-list`);\n }\n\n const p = await probeExtensionSource(probe, keys);\n let sourceKey = p.sourceKey;\n if (!sourceKey) {\n // 🔴 两种 unknown 的处置**相反**,所以标记必须不同。\n // answered === 0 ⇒ 没有任何模型答应 ⇒ 查连通/权限/模型清单是不是空的,**与反代无关**;\n // answered > 0 ⇒ 答应了但没回这个头 ⇒ 查反代是不是把 X-HCM-* 响应头剥了。\n failures.push(\n p.answered > 0\n ? `${META_PATH}#X-HCM-Extension-Source`\n : `${META_PATH}#no-meta-answered`,\n );\n sourceKey = 'unknown';\n }\n\n // 🔴 吃 type 而不只是 modelKey:一个模型从 DATA 变 JSON 时 meta 形状全变,\n // 只哈希 modelKey 的门对此完全无感。\n // 与上面探针的 keys 分开:那份**过滤过空串**(探针配额有限),\n // 这份是**全量**(哈希要如实反映清单,一条缺 modelKey 的记录本身就是漂移信号)。\n const hashLines = models.map((m) => `${m.modelKey ?? ''}|${m.type ?? ''}`).sort();\n const modelHash = sha256(hashLines.join('\\n'));\n\n const rows = await fetchManifestRows(probe, failures);\n\n const versionOut = await probe.get(VERSION_PATH);\n let backendVersion: unknown;\n if (versionOut.error === 'ok' && versionOut.body !== null && versionOut.body !== undefined) {\n backendVersion = versionOut.body;\n } else {\n failures.push(VERSION_PATH);\n backendVersion = { version: 'unavailable' };\n }\n\n return {\n tenantMetaManifestDigest: manifestDigest(rows),\n manifestEntryCount: rows.length,\n extensionSource: sourceKey,\n // 🔴 记下**是从哪个模型探到的**:让「这个值是谁给的」在产物里留痕。\n extensionSourceProbe: p.probeModelKey,\n // 🔴 没有这两个数,`extensionSource: \"unknown\"` 就是一个**不可追问的结论**。\n extensionSourceProbeAttempted: p.attempted,\n extensionSourceProbeAnswered: p.answered,\n modelListHash: modelHash,\n modelCount: models.length,\n backendVersion,\n capabilities: probe.capabilities,\n createdAt: `${now().toISOString().slice(0, 19)}Z`,\n ttlDays,\n coverageNotes: {\n gateComponents: [...GATE_COMPONENTS],\n blindSpots: BLIND_SPOTS,\n // 空数组 = 三个端点都真取到了;非空 = 这份四元组是**残的**,\n // 上面那些 0 和空摘要不代表环境真的空。\n fetchFailures: failures,\n },\n };\n}\n\n/**\n * 比对两份四元组,回**差异清单**(空 = 同源)。\n *\n * 🔴 某个分量没爬到时,它和「爬到了而且相等」在值上同形。两边都没爬到 → 三个分量全等\n * → 门放行一份根本没验过的基线。所以残缺本身也要进差异清单。\n */\nexport function compareFingerprint(\n recorded: Record<string, unknown>,\n current: Record<string, unknown>,\n): string[] {\n const diffs: string[] = GATE_COMPONENTS.filter((c) => recorded[c] !== current[c]);\n\n // capabilities 不进 GATE_COMPONENTS(它不是环境属性,是**爬法**属性),但仍然上报:\n // 两份基线若用不同 capabilities 爬出,那是两种爬法,值得在握手里说一声。\n if (recorded.capabilities !== current.capabilities) diffs.push('capabilities');\n\n for (const [fp, tag] of [\n [recorded, 'recorded'],\n [current, 'current'],\n ] as const) {\n const notes = fp.coverageNotes;\n // 🔴 **`coverageNotes` 整个缺失 ≠ 没有失败**。手改过的、或旧版产出的指纹没有这一节,\n // 此时「有没有取数失败」是**未知**,不是「否」。把未知读成否 ⇒ 放行一份从未验过\n // 残缺与否的基线——「爬过且没失败」与「根本没这个字段」不许同形。\n if (!isPlainObject(notes) || !('fetchFailures' in (notes as object))) {\n diffs.push(`incomplete:${tag}:coverageNotes-missing`);\n continue;\n }\n const raw = (notes as Record<string, unknown>).fetchFailures;\n const list = Array.isArray(raw) ? raw : [];\n // 🔴 报**是什么**残了,不只报**哪一侧**残了。只报侧别会把唯一可行动的信息丢掉:\n // 顾问拿到 `incomplete:recorded` 只知道「有问题」,不知道该查反代(剥了头)\n // 还是查网关分页(#page-cap)。侧别继续留着——只有 recorded 残 ⇒ 旧基线不可信,\n // 重跑 recon;只有 current 残 ⇒ 这次爬取本身有问题,先修环境。\n const gateFailures = [...new Set(list.map(String))]\n .filter((x) => GATE_SOURCES.some((s) => x.startsWith(s)))\n .sort();\n for (const f of gateFailures) diffs.push(`incomplete:${tag}:${f}`);\n }\n return diffs;\n}\n\nfunction sha256(s: string): string {\n return createHash('sha256').update(s, 'utf8').digest('hex');\n}\n\nfunction isPlainObject(v: unknown): v is Record<string, unknown> {\n return typeof v === 'object' && v !== null && !Array.isArray(v);\n}\n\nexport type { Outcome, ReconProbe };\n","/**\n * 顾问到现场敲的第一条命令。\n *\n * 回答四个问题,一条锚点一个:**我连上了吗 / 我是谁 / 我有没有 configAdmin / 我站在哪一层**。\n *\n * 🔴 四条锚点**恒存在**。失败时少发一条,顾问看到的是「三条都绿」——他会以为都查过了,\n * 而实际上有一层根本没被体检到。少一条 ≠ 那一层没问题,少一条 = **那一层没被问过**。\n *\n * 移植自 `hcm_nx_core` 的 `tools/fde-kit/hcmnx_kit/doctor.py`(ADR-319 刀 6b)。\n * 行为基线是行为契约清单 §1.4 的 19 条 P。\n */\nimport {\n MANIFEST_PATH,\n MODELS_PATH,\n modelKeys,\n probeExtensionSource,\n} from './fingerprint';\nimport { toPrincipal } from '../auth/principal';\nimport type { ReconProbe } from './probe';\n\nexport type DoctorAnchor = 'connectivity' | 'identity' | 'config-admin' | 'layer';\n\n/** 四条锚点的顺序是固定的:连通 → 我是谁 → 有没有权限 → 站在哪一层。 */\nexport const DOCTOR_ANCHORS: readonly DoctorAnchor[] = [\n 'connectivity',\n 'identity',\n 'config-admin',\n 'layer',\n] as const;\n\nexport interface DoctorCheck {\n anchor: DoctorAnchor;\n ok: boolean;\n detail: string;\n}\n\nexport interface DoctorReport {\n ok: boolean;\n checks: DoctorCheck[];\n}\n\n/**\n * 登录身份。`null` 表示**本工具没有经手登录**——那与「登录了但服务端没给 user」\n * 是两种状态,处置不同,不许合并。\n */\nexport interface ReconIdentity {\n /** token 实际归属的租户(服务端登录响应里的 `user.tenantId`)。 */\n tenantId?: string;\n /** 顾问**要的**租户(命令行给的)。 */\n requestedTenantId?: string;\n username?: string;\n roles?: string[];\n}\n\nexport async function runDoctor(\n probe: ReconProbe,\n identity: ReconIdentity | null,\n): Promise<DoctorReport> {\n const checks: DoctorCheck[] = [];\n\n // ── 锚点 1:连通 ────────────────────────────────────────────────\n const modelsOut = await probe.get(MODELS_PATH);\n const connected = modelsOut.error === 'ok';\n // 🔴 body 声明是 object:服务端完全可能返回 array/string,直接取属性会在客户现场炸——\n // doctor 是第一条命令,它自己不许以裸异常收场。\n const modelsBody = isObj(modelsOut.body) ? modelsOut.body : {};\n checks.push({\n anchor: 'connectivity',\n ok: connected,\n detail: connected\n ? `模型清单可达,共 ${modelsBody.totalCount ?? 0} 个模型`\n : `不可达:${modelsOut.error}(status=${modelsOut.status ?? '?'})`,\n });\n\n // 🔴 从 **meta** 端点探。`/api/system/models` 的响应里**没有**这个头。\n // 读错端点会让 layer 锚点在**每一个**真实环境上报「未回显」,\n // 而单测因为假路由把头挂错了地方而恒绿。\n // 🔴 形状判断 / 空串过滤 / 排序,全在 `modelKeys` 这**唯一一道门**里。\n const keys = modelKeys(modelsOut.body);\n const p = await probeExtensionSource(probe, keys);\n\n // ── 锚点 2:我是谁 ──────────────────────────────────────────────\n // 🔴 `identity` 必须有**自己的**判据。把它的 ok 直接复用 connectivity 的布尔,\n // 「四条锚点」就只有三条真判据,第四条是装饰品。\n // 真判据是:**token 归属的租户 vs 顾问要的租户**。顾问敲错一个租户号,\n // 爬出来的基线**看上去完全正常**,而里面是另一个客户的 schema。\n checks.push({ anchor: 'identity', ...judgeIdentity(identity) });\n\n // ── 锚点 3:有没有 configAdmin ──────────────────────────────────\n const manifestOut = await probe.get(MANIFEST_PATH, { page: 0, pageSize: 1 });\n // 🔴 403 = 没权限(判红);total=0 = 有权限且确实是空的(判绿)。两者不许混——\n // 混判会把一个完全正常的克隆环境(带得走 workspace 绑定、带不走 workspace 文件)\n // 判成权限不足,顾问会去要一个他其实已经有的权限。\n const caOk = manifestOut.error === 'ok';\n const manifestBody = isObj(manifestOut.body) ? manifestOut.body : {};\n const total = caOk ? (manifestBody.total ?? '?') : '?';\n let caDetail: string;\n if (caOk) {\n caDetail =\n `租户 meta 清单可读,共 ${total} 条(0 条是合法状态:` +\n '克隆库带得走 workspace 绑定,带不走 workspace 文件)';\n } else if (manifestOut.status === 401 || manifestOut.status === 403) {\n // 🔴 不可读时**分开两种成因**,它们的下一步完全不同。压进同一句「不可读:{error}」,\n // 顾问只知道「不可读」,不知道该去找客户管理员要角色、还是去找网络组。\n // ⚠️ 压一起时两条 detail 仍然**字面不相等**,所以「两条不相等」这个判据抓不住它——\n // 判据必须落在**下一步指向谁**上。\n caDetail =\n `HTTP ${manifestOut.status}:当前账号**没有 configAdmin 权限**。` +\n '找客户管理员加角色;这不是网络问题';\n } else {\n caDetail =\n `不可读:${manifestOut.error}(status=${manifestOut.status ?? '?'})。` +\n '先排连通/网关,再谈权限。缺 configAdmin 时主锚点算不出来';\n }\n checks.push({ anchor: 'config-admin', ok: caOk, detail: caDetail });\n\n // ── 锚点 4:我站在哪一层 ────────────────────────────────────────\n checks.push({\n anchor: 'layer',\n ok: Boolean(p.sourceKey),\n detail: layerDetail(p, connected),\n });\n\n return { ok: checks.every((c) => c.ok), checks };\n}\n\nfunction judgeIdentity(ident: ReconIdentity | null): { ok: boolean; detail: string } {\n if (ident === null) {\n // 「本工具没有经手登录」≠「登录了但没给 user」。fail-closed:核不了就算没核过。\n return { ok: false, detail: '未经本工具登录,无法核对 token 归属的租户' };\n }\n const want = String(ident.requestedTenantId ?? '').trim();\n const got = String(ident.tenantId ?? '').trim();\n if (!want) {\n // 🔴 命令层忘填 `requestedTenantId` 的形态。落进下面的「不一致」分支会渲染成\n // 「token 归属租户 13,而你要的是 」——一句半截话,且**用一个工具接线缺陷去指控环境**:\n // 顾问会跑去查客户的租户配置,而问题在我们这边。\n return {\n ok: false,\n detail:\n '本工具没有记下「你要的租户」(命令层未填 requestedTenantId),' +\n `无法与 token 归属的租户 ${got || '(未知)'} 核对 —— ` +\n '这是工具接线问题,不是环境问题',\n };\n }\n if (!got) {\n return {\n ok: false,\n detail: `服务端登录响应里没有 user.tenantId,无法核对(你要的是 ${want})`,\n };\n }\n if (want !== got) {\n return {\n ok: false,\n detail:\n `🔴 **token 归属租户 ${got},而你要的是 ${want}** —— ` +\n '你正在爬另一个租户,先停下来。这一对身份会随身份段落进产物索引,' +\n '所以爬完之后在产物里也对得出来;但**落盘路径**上那个租户段取自你给的 ' +\n '`--tenant` —— 目录名照旧是你要的那个号。',\n };\n }\n return {\n ok: true,\n detail:\n `租户 ${got} 核对一致;登录身份 ${ident.username || '(未知)'},` +\n `角色 ${JSON.stringify(ident.roles ?? [])}`,\n };\n}\n\nfunction layerDetail(\n p: { sourceKey: string; probeModelKey: string; attempted: number; answered: number },\n connected: boolean,\n): string {\n if (p.sourceKey) {\n // 🔴 `probeModelKey` 不许算出来又不用:它答的是「在哪个模型上探到的」——\n // 顾问复现时要拿它去打同一个端点。\n return (\n `你站在 \\`${p.sourceKey}\\` 这一层(探自 \\`${p.probeModelKey}\\` 的 meta)。` +\n '不同 sourceKey 的 recon 产物不可互换 —— 两个 FDE 同一条命令会得到两份不同的事实,' +\n '那不是陈旧,是站在不同的层上'\n );\n }\n // 🔴 **几种** unknown 给**不同的下一步**。一句笼统的「无法确定所在层」\n // 会让顾问在一台陌生机器上无从下手。\n if (p.attempted === 0) {\n // 🔴 `attempted === 0` 必须单独说:没有 key 可试时渲染成「试了 0 个模型,一个都没答应」\n // 是把**没试**说成**试了没人应**。而 attempted === 0 本身还有两种成因,说法也不能合:\n // 清单**没取到**(去看 connectivity)vs 清单取到了但**一个可用 modelKey 都没有**\n // (connectivity 是绿的,把人支过去是浪费)。\n return connected\n ? '模型清单取到了,但里面**一个可用 modelKey 都没有**,' +\n '**一个模型都没得试** ⇒ 这不是层的问题:查清单的 modelKey 键名是不是被网关' +\n '改写、或当前账号看不到任何模型'\n : '模型清单没取到,**一个模型都没得试** ⇒ 先看上面 connectivity 那条';\n }\n if (p.answered > 0) {\n return (\n `试了 ${p.attempted} 个模型的 meta,${p.answered} 个答应了,` +\n '但都没回显 X-HCM-Extension-Source ⇒ 先查反代是不是把 X-HCM-* 响应头剥了'\n );\n }\n return (\n `试了 ${p.attempted} 个模型的 meta,**一个都没答应** ⇒ 先查连通与权限,` +\n '再看模型清单是不是空的。⚠️ 本工具只知道「一个都没答应」,' +\n '**分不出**是连通、权限还是中间有人挡了 —— 上一档(答应了但没回显 X-HCM-* 头)' +\n '才指得动反代,这一档指不动任何一方'\n );\n}\n\nfunction isObj(v: unknown): v is Record<string, unknown> {\n return typeof v === 'object' && v !== null && !Array.isArray(v);\n}\n\n/** `GET /api/auth/info` —— 服务端认定的 token 归属从这里来。 */\nexport const AUTH_INFO_PATH = '/api/auth/info';\n\n/**\n * 现打一次 `/api/auth/info`,拼出 identity 锚点要的那一对身份。\n *\n * 🔴 **复用 `toPrincipal`,不另写解析**:那个 helper 已经处理了两个来源\n * (`POST /api/auth/login` 的 `user` 与 `GET /api/auth/info` 本体)字段名一致但容器\n * 不同、以及 tenantId 可能是 number 也可能是 UUID 这两件事。再写一份就是第二套口径。\n *\n * 🔴 取不到时回 `null` 而不是空对象:**核不了 ≠ 核过了没问题**。\n * 空对象会落进「服务端没给 user.tenantId」那一档,把一次「问都没问成」\n * 说成「问了,服务端没答」——两者的下一步不同。\n */\nexport async function fetchReconIdentity(\n probe: ReconProbe,\n requestedTenantId: string,\n): Promise<ReconIdentity | null> {\n const out = await probe.get(AUTH_INFO_PATH);\n if (out.error !== 'ok') return null;\n const principal = toPrincipal(out.body);\n if (!principal) return { requestedTenantId };\n return {\n requestedTenantId,\n tenantId: principal.tenantId,\n username: principal.username,\n roles: principal.roles,\n };\n}\n","/**\n * 侦察层对 HTTP 的**窄端口**与**响应分类**。\n *\n * 🔴 为什么是端口而不是搬一份客户端过来(ADR-319 刀 6b 明令):\n * CLI 已经有认证 / endpoint 解析 / token 刷新 / 拦截器这一整条管道\n * (`packages/core/src/client.ts` + `src/http/`)。侦察层再造一份,就是\n * 「集成 reference path 必复用 helper」那条铁律的反面。\n *\n * 但**响应分类**不在那条管道里,而它是侦察层的命根子:顾问打的是一个陌生环境,\n * 「没权限」「没这个端点」「网关吐了 HTML」「连不上」的下一步完全不同。\n * 把它们压成一个布尔,产物里就再也分不出来了。所以这一层必须自己有。\n *\n * ⚠️ `error` 用字符串而不是布尔:要区分的不是「成没成」,而是「没成是哪一种」。\n * 判成功一律写 `error === 'ok'`,别写 `!error`。\n */\nimport type { AxiosInstance } from 'axios';\n\n/** 响应分类的完整取值域。 */\nexport type OutcomeError =\n | 'ok'\n | 'unauthorized'\n | 'forbidden'\n | 'not-found'\n | 'server-error'\n | 'bad-response'\n | 'network';\n\nexport interface Outcome {\n /** 网络层没走通时是 `-1`——它不是 HTTP 状态,别拿去和 4xx 比。 */\n status: number;\n /** 🔴 是 unknown 不是对象:服务端完全可能返回 array / string / number。 */\n body: unknown;\n error: OutcomeError;\n /**\n * 本次响应的头,键一律小写。\n *\n * 🔴 挂在 Outcome 上而不是客户端实例上(Python 侧是 `client.last_headers`):\n * 那种可变字段有「串味」风险——上一次成功的头留到这一次失败时被读到,\n * 而 `probeExtensionSource` 恰恰是紧跟每次 GET 读头。挂在返回值上让串味**结构上不可能**,\n * 契约里那几条「last_headers 必须清空」于是从「要测的行为」变成「构造上成立」。\n */\n headers: Record<string, string>;\n}\n\nexport interface ReconProbe {\n get(path: string, params?: Record<string, unknown>): Promise<Outcome>;\n post?(path: string, body: unknown): Promise<Outcome>;\n getMany?(paths: string[]): Promise<Record<string, Outcome>>;\n postMany?(jobs: Array<{ path: string; body: unknown }>): Promise<Record<string, Outcome>>;\n /** 爬取时声明的 capability 串——它是**爬法**属性,不是环境属性。 */\n readonly capabilities: string;\n}\n\nconst STATUS_CLASS: Record<number, OutcomeError> = {\n 401: 'unauthorized',\n 403: 'forbidden',\n 404: 'not-found',\n};\n\n/**\n * 状态码 → 分类。\n *\n * 🔴 整个 2xx 段都是成功。只认 200 会害得 201/202/206 被归成 `bad-response`——\n * 一个正常的「已创建 / 处理中 / 分段内容」响应被记成失败。\n */\nexport function classifyStatus(status: number): OutcomeError {\n if (status >= 200 && status < 300) return 'ok';\n if (STATUS_CLASS[status]) return STATUS_CLASS[status];\n if (status >= 500) return 'server-error';\n return 'bad-response';\n}\n\n/** 能力头:声明本次爬取用的 capability 串。 */\nexport const CAPABILITIES_HEADER = 'x-hcm-capabilities';\n\n/**\n * 把 `params` urlencode 到 URL 上。\n *\n * 🔴 三个坑各栽过一次:中文必须 urlencode;path 已带 query 时要用 `&` 续接\n * 而不是再来一个 `?`;base 末尾的 `/` 不能和 path 前导的 `/` 拼成 `//`。\n */\nexport function buildUrl(path: string, params?: Record<string, unknown>): string {\n const entries = Object.entries(params ?? {}).filter(([, v]) => v !== undefined && v !== null);\n if (entries.length === 0) return path;\n const qs = entries\n .map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`)\n .join('&');\n return `${path}${path.includes('?') ? '&' : '?'}${qs}`;\n}\n\nfunction lowerHeaders(raw: unknown): Record<string, string> {\n const out: Record<string, string> = {};\n if (raw && typeof raw === 'object') {\n for (const [k, v] of Object.entries(raw as Record<string, unknown>)) {\n out[k.toLowerCase()] = String(v);\n }\n }\n return out;\n}\n\n/**\n * 把一次 axios 调用的结果归类成 `Outcome`。\n *\n * 🔴 空体的两档不能合:\n * · 2xx 且空体 ⇒ `bad-response`。静默把空当成结果,正是最怕的丢数据形态。\n * 例外是 204 No Content——它**声明**了没有内容,是 `ok` + `body=null`。\n * · 非 2xx 且空体 ⇒ **保留状态分类**,不降级成 `bad-response`。\n * 500 空体仍是 `server-error`;降级会让顾问去查自己请求写错没有,而真相在服务端。\n *\n * 🔴 非 2xx 但**有** body 时 body 要留着:实测反代对大 DSL 查询吐 `504 + HTML 网关超时页`,\n * 顾问拿到 `bad-response` 会去查自己 DSL 键写错没有,而真相是查询超时。\n * 但 HTML 这种非 JSON 体留着也没用且会污染产物 ⇒ 保留分类、body 置空。\n */\nfunction toOutcome(status: number, data: unknown, headers: Record<string, string>): Outcome {\n const cls = classifyStatus(status);\n if (status === 204) return { status, body: null, error: 'ok', headers };\n const empty =\n data === undefined ||\n data === null ||\n (typeof data === 'string' && data.trim() === '');\n if (empty) {\n return { status, body: null, error: cls === 'ok' ? 'bad-response' : cls, headers };\n }\n if (typeof data === 'string') {\n // 字符串体:JSON 解析得出来就当结构化体,解析不出(HTML 错误页等)就置空但保留分类。\n try {\n return { status, body: JSON.parse(data), error: cls, headers };\n } catch {\n return { status, body: null, error: cls === 'ok' ? 'bad-response' : cls, headers };\n }\n }\n return { status, body: data, error: cls, headers };\n}\n\n/** 把既有 axios 管道(已带认证与刷新)适配成侦察层的窄端口。 */\nexport function httpProbe(http: AxiosInstance, capabilities: string): Required<ReconProbe> {\n async function once(\n fn: () => Promise<{ status: number; data: unknown; headers?: unknown }>,\n ): Promise<Outcome> {\n try {\n const res = await fn();\n return toOutcome(res.status, res.data, lowerHeaders(res.headers));\n } catch (e) {\n // 🔴 URL 本身非法之类的**本地**错误单独落 bad-response:归进 network\n // 会让顾问白查一遍 VPN。\n const msg = e instanceof Error ? e.message : String(e);\n const local = /Invalid URL|ERR_INVALID_URL|ERR_UNESCAPED_CHARACTERS/i.test(msg);\n return { status: -1, body: null, error: local ? 'bad-response' : 'network', headers: {} };\n }\n }\n\n const headers = { [CAPABILITIES_HEADER]: capabilities };\n\n const probe: Required<ReconProbe> = {\n capabilities,\n get(path, params) {\n return once(() =>\n http.get(buildUrl(path, params), { headers, validateStatus: () => true }),\n );\n },\n post(path, body) {\n return once(() =>\n http.post(path, body, {\n headers: { ...headers, 'content-type': 'application/json' },\n validateStatus: () => true,\n }),\n );\n },\n /**\n * 🔴 **按原始路径回填、输入全覆盖**:调用方拿 `out[path]` 取结果,\n * 少一个键就会读成 `undefined` 然后当成「没这个东西」。\n * 🔴 一路炸不许拖垮整批——逐路径独立归类。\n */\n async getMany(paths) {\n const pairs = await Promise.all(\n paths.map(async (p): Promise<[string, Outcome]> => [\n p,\n await probe.get(p).catch(() => networkOutcome()),\n ]),\n );\n return Object.fromEntries(pairs);\n },\n async postMany(jobs) {\n const pairs = await Promise.all(\n jobs.map(async (j): Promise<[string, Outcome]> => [\n j.path,\n await probe.post(j.path, j.body).catch(() => networkOutcome()),\n ]),\n );\n return Object.fromEntries(pairs);\n },\n };\n return probe;\n}\n\nfunction networkOutcome(): Outcome {\n return { status: -1, body: null, error: 'network', headers: {} };\n}\n\nexport { toOutcome as classifyResponse };\n"],"mappings":"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AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;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","names":["fs","path","fs","yaml","yaml","fs","yaml","ExitCode","CliErrorCode","yaml","fs","fs","path","yaml","yaml","path","action","yaml","yaml","fs","os","path","yaml","resolve","randomUUID","randomUUID","resolve","fs","fs","path","manifest","buildMiniAppTemplateFiles","manifest","asRecord","action","path","yaml","JSON_SCHEMA","manifest","path","yaml","JSON_SCHEMA","path","createHash","readFileSync","path","createHash","isPlainObject","sha256","path"]}
|