@hcmai/sdk 0.1.0
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/LICENSE +201 -0
- package/README.md +17 -0
- package/dist/index.d.ts +1775 -0
- package/dist/index.js +5208 -0
- package/dist/index.js.map +1 -0
- package/package.json +54 -0
|
@@ -0,0 +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/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 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}\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}\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: a.params,\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 文档存储(web ChatInput 同端点同 storageCategory),\n * 返回 message.create 可直接携带的附件元数据。类型限制跟随服务端校验。\n */\nexport async function uploadDocument(http: AxiosInstance, filePath: string): 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', 'ai-conversation');\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 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';\n\n/**\n * 睿戎 Chiron 交付技能库客户端。\n *\n * <p>🔴 这些端点**免认证**——交付顾问接手的是「还没有租户、还没有账号」的空环境,\n * 必须先装技能才能建租户。所以本模块只需要 endpoint,不碰 identity/token。\n *\n * 后端契约(ChironController):\n * - `GET /skills.json` 目录 manifest\n * - `GET /skills/{id}.md` 技能原文\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 bytes: number;\n markdownUrl: 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\nexport async function fetchSkillCatalog(http: AxiosInstance): Promise<ChironManifest> {\n const resp = await http.get('/skills.json');\n return (resp.data?.data ?? resp.data) as ChironManifest;\n}\n\nexport async function fetchSkillMarkdown(http: AxiosInstance, id: string): Promise<string> {\n const resp = await http.get(`/skills/${encodeURIComponent(id)}.md`, {\n responseType: 'text',\n transformResponse: [(d) => d],\n });\n return String(resp.data ?? '');\n}\n\nexport function flattenSkills(manifest: ChironManifest): ChironSkill[] {\n return (manifest.categories ?? []).flatMap((c) => c.skills ?? []);\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":"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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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;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;;;ACxBA,eAAsB,kBAAkB,MAA8C;AACpF,QAAM,OAAO,MAAM,KAAK,IAAI,cAAc;AAC1C,SAAQ,KAAK,MAAM,QAAQ,KAAK;AAClC;AAEA,eAAsB,mBAAmB,MAAqB,IAA6B;AACzF,QAAM,OAAO,MAAM,KAAK,IAAI,WAAW,mBAAmB,EAAE,CAAC,OAAO;AAAA,IAClE,cAAc;AAAA,IACd,mBAAmB,CAAC,CAAC,MAAM,CAAC;AAAA,EAC9B,CAAC;AACD,SAAO,OAAO,KAAK,QAAQ,EAAE;AAC/B;AAEO,SAAS,cAAcC,WAAyC;AACrE,UAAQA,UAAS,cAAc,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;AAClE;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;;;ACpDA,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","yaml","yaml","fs","os","path","yaml","resolve","randomUUID","randomUUID","resolve","fs","fs","path","manifest","buildMiniAppTemplateFiles","manifest","asRecord","action","path","manifest","path"]}
|