dskcode 0.1.21 → 0.1.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cli/index.tsx","../src/config/loader.ts","../src/cli/middleware.ts","../src/cli/help.ts","../src/cli/api-key-setup.ts","../src/cli/skill-import.ts","../src/ui/RenderScope.tsx","../src/ui/Spinner.tsx","../src/ui/StatusMessage.tsx","../src/ui/DskcodeSplash.tsx","../src/ui/ChatSession.tsx","../src/ui/useDoubleCtrlC.ts","../src/ui/AssistantMessage.tsx","../src/provider/cost-tracker.ts","../src/ui/ToolCallBlock.tsx","../src/agent/message-builder.ts","../src/ui/DiffPreview.tsx","../src/ui/SkillSelector.tsx","../src/ui/FileSelector.tsx","../src/provider/registry.ts","../src/agent/extra-prompt.ts","../src/agent/system-prompt.ts","../src/tool/registry.ts","../src/tool/types.ts","../src/agent/index.ts","../src/tool/sandbox.ts","../src/tool/diff.ts","../src/tool/eol.ts","../src/tool/builtins/read-file.ts","../src/tool/builtins/write-file.ts","../src/tool/builtins/edit-file.ts","../src/tool/builtins/multi-edit.ts","../src/tool/builtins/delete-range.ts","../src/tool/builtins/bash.ts","../src/tool/builtins/glob.ts","../src/tool/builtins/grep.ts","../src/tool/builtins/ls.ts","../src/tool/builtins/fetch.ts","../src/tool/builtins/index.ts","../src/utils/gradient.ts","../src/ui/GamePicker.tsx","../src/game/index.ts","../src/game/brick-breaker/index.tsx","../src/game/coder-check/index.tsx","../src/game/snake/engine.ts","../src/game/snake/renderer.ts","../src/game/snake/input.ts","../src/game/snake/index.ts","../src/game/registry.ts","../src/stock/StockList.tsx","../src/utils/scan-files.ts","../src/cli/exit-codes.ts","../src/index.ts"],"sourcesContent":["import { Command } from \"commander\";\r\nimport { loadConfigMiddleware } from \"./middleware.js\";\r\nimport type { DskcodeContext } from \"./middleware.js\";\r\nimport { customHelp } from \"./help.js\";\r\nimport { hasApiKey, promptForApiKey } from \"./api-key-setup.js\";\r\nimport { promptImportClaudeSkills, countDskcodeSkills, countProjectLocalSkills, getAllSkills } from \"./skill-import.js\";\r\nimport { saveApiKey, loadAndValidate, saveStockConfig } from \"../config/index.js\";\r\nimport { readFile } from \"node:fs/promises\";\r\nimport { join } from \"node:path\";\r\nimport { renderApp, ChatSession } from \"../ui/index.js\";\r\nimport { initGames } from \"../game/registry.js\";\r\nimport { listGames, getGame } from \"../game/index.js\";\r\nimport type { Game } from \"../game/index.js\";\r\nimport { GamePicker } from \"../ui/GamePicker.js\";\r\nimport { render } from \"ink\";\r\nimport chalk from \"chalk\";\r\nimport { StockList } from \"../stock/index.js\";\r\nimport { CostTracker } from \"../provider/cost-tracker.js\";\r\nimport { scanProjectFiles } from \"../utils/scan-files.js\";\r\n\r\nconst SUBCOMMANDS = [\"chat\", \"run\", \"setup\", \"init\", \"completion\", \"game\", \"stock\"];\r\n\r\nexport function createCli(): Command {\r\n const program = new Command();\r\n program.exitOverride();\r\n\r\n program\r\n .name(\"dskcode\")\r\n .description(\"基于 DeepSeek 的 AI 编程助手终端工具\")\r\n .version(\"0.0.0\", \"-V, --version\", \"显示版本号\")\r\n .option(\"--verbose\", \"开启详细日志输出\")\r\n .option(\"--config <path>\", \"指定配置文件路径\");\r\n\r\n program.helpInformation = () => customHelp(program);\r\n\r\n program.hook(\"preAction\", async (thisCommand, actionCommand) => {\r\n const ctx = await loadConfigMiddleware.call(thisCommand);\r\n (actionCommand as unknown as Record<string, unknown>).dskcodeCtx = ctx;\r\n });\r\n\r\n // chat — 交互式对话\r\n program\r\n .command(\"chat\")\r\n .description(\"启动交互式对话会话\")\r\n .action(async function () {\r\n if (!process.stdin.isTTY) {\r\n console.error(\"dskcode chat 需要交互式终端。如需执行一次性任务,请使用 dskcode run。\");\r\n process.exit(1);\r\n }\r\n\r\n let ctx = (this as unknown as Record<string, unknown>).dskcodeCtx as DskcodeContext | undefined;\r\n\r\n // 检查 API Key,如果没有则交互式输入\r\n if (ctx && !hasApiKey(ctx.config.providers)) {\r\n const key = await promptForApiKey();\r\n if (!key) process.exit(1);\r\n\r\n // 保存到全局配置\r\n const savedPath = await saveApiKey(key);\r\n console.log(` ${chalk.green(\"✔\")} API Key 已保存到 ${chalk.dim(savedPath)}\\n`);\r\n\r\n // 重新加载配置,使新 Key 生效\r\n const result = await loadAndValidate();\r\n ctx = { ...ctx, config: result.config };\r\n }\r\n\r\n // API Key 检查之后:检测项目本地 skill 和 Claude Code skill\r\n // 如果 .dskcode/skill 下有 skill,则跳过导入 Claude Code skill\r\n await promptImportClaudeSkills(process.cwd());\r\n\r\n // 从配置创建 CostTracker\r\n const costTracker = new CostTracker({\r\n budgetLimit: ctx?.config.budgetLimit ?? 0,\r\n tokenBudgetLimit: ctx?.config.tokenBudgetLimit ?? 0,\r\n });\r\n\r\n startChat(ctx, costTracker);\r\n });\r\n\r\n async function startChat(\r\n ctx: DskcodeContext | undefined,\r\n costTracker: CostTracker,\r\n ) {\r\n // 统计 skill 数量并获取详情列表,以及扫描项目文件\r\n const [globalSkillCount, localSkillCount, skills, files] = await Promise.all([\r\n countDskcodeSkills(),\r\n countProjectLocalSkills(process.cwd()),\r\n getAllSkills(process.cwd()),\r\n scanProjectFiles(process.cwd()),\r\n ]);\r\n const skillCount = globalSkillCount + localSkillCount;\r\n // 从配置中提取默认 Provider 的 apiKey 和 baseUrl\r\n const defaultProvider = ctx?.config.providers.find(\r\n (p) => p.name === (ctx?.config.defaultProvider ?? \"deepseek\"),\r\n );\r\n const model = defaultProvider?.model ?? \"deepseek-v4-flash\";\r\n const chatApp = renderApp(\r\n <ChatSession\r\n skillCount={skillCount}\r\n skills={skills}\r\n files={files}\r\n toolCount={ctx?.config.tools.length ?? 0}\r\n verbose={ctx?.verbose ?? false}\r\n apiKey={defaultProvider?.apiKey}\r\n baseUrl={defaultProvider?.baseUrl ?? \"https://api.deepseek.com\"}\r\n costTracker={costTracker}\r\n model={model}\r\n onLaunchGame={() => {\r\n chatApp.unmount();\r\n setImmediate(() => {\r\n initGames();\r\n const games = listGames();\r\n const { unmount } = render(\r\n <GamePicker\r\n games={games}\r\n onSelect={async (game: Game) => {\r\n unmount();\r\n await game.play();\r\n // 游戏结束后返回对话\r\n startChat(ctx, costTracker);\r\n }}\r\n onBackToChat={() => {\r\n unmount();\r\n setImmediate(() => startChat(ctx, costTracker));\r\n }}\r\n />,\r\n { exitOnCtrlC: false },\r\n );\r\n });\r\n }}\r\n onLaunchStock={() => {\r\n chatApp.unmount();\r\n setImmediate(() => {\r\n // 使用配置中的自选股列表或兜底默认值\r\n const defaultStockCodes = ctx?.config.stock?.symbols?.map((s) => s.code)\r\n ?? [\"sh000001\", \"sz399006\", \"sh601688\"];\r\n const stockApp = renderApp(\r\n <StockList\r\n codes={defaultStockCodes}\r\n onBackToChat={() => {\r\n stockApp.unmount();\r\n setImmediate(() => startChat(ctx, costTracker));\r\n }}\r\n onExit={() => process.exit(0)}\r\n />,\r\n );\r\n });\r\n }}\r\n />,\r\n );\r\n }\r\n\r\n // run\r\n program\r\n .command(\"run\")\r\n .description(\"执行一次性任务\")\r\n .argument(\"[prompt...]\", \"任务描述\")\r\n .option(\"--model <name>\", \"指定使用的模型\")\r\n .action(async function (_prompt: string[]) {\r\n console.log(\"dskcode run — 待实现(第07章)\");\r\n });\r\n\r\n // setup\r\n program\r\n .command(\"setup\")\r\n .description(\"运行配置向导\")\r\n .option(\"--export\", \"以 JSON 格式导出配置\")\r\n .option(\"--test\", \"测试 API Key 连通性\")\r\n .action(async function () {\r\n console.log(\"dskcode setup — 待实现(第14章)\");\r\n });\r\n\r\n // init\r\n program\r\n .command(\"init\")\r\n .description(\"在当前项目下生成项目记忆文件(AGENTS.md)\")\r\n .action(async function () {\r\n console.log(\"dskcode init — 待实现(第11章)\");\r\n });\r\n\r\n // completion\r\n program\r\n .command(\"completion\")\r\n .description(\"输出 shell 自动补全配置说明(bash/zsh)\")\r\n .argument(\"[shell]\", \"shell 类型\", /^(bash|zsh)$/i)\r\n .action(async function (shell?: string) {\r\n if (!shell) {\r\n console.log(\"请指定 shell 类型:dskcode completion bash 或 dskcode completion zsh\");\r\n return;\r\n }\r\n if (shell === \"bash\") {\r\n console.log(`# dskcode bash 自动补全\r\n_dskcode_completion() {\r\n local cur=\\${COMP_WORDS[COMP_CWORD]}\r\n if [[ \\${COMP_CWORD} -eq 1 ]]; then\r\n COMPREPLY=( $(compgen -W \"${SUBCOMMANDS.join(\" \")}\" -- \"\\${cur}\") )\r\n return 0\r\n fi\r\n COMPREPLY=( $(compgen -W \"--verbose --config --model\" -- \"\\${cur}\") )\r\n}\r\ncomplete -F _dskcode_completion dskcode`);\r\n } else {\r\n console.log(`# dskcode zsh 自动补全\r\n_dskcode_completion() {\r\n local -a commands\r\n commands=(\r\n \"chat:启动交互式对话会话\"\r\n \"run:执行一次性任务\"\r\n \"setup:运行配置向导\"\r\n \"init:生成项目记忆文件\"\r\n \"completion:输出 shell 自动补全说明\"\r\n \"game:内置小游戏\"\r\n \"stock:查看自选股实时行情\"\r\n )\r\n _describe 'dskcode commands' commands\r\n}\r\ncompdef _dskcode_completion dskcode`);\r\n }\r\n });\r\n\r\n // stock — 股票行情\r\n program\r\n .command(\"stock\")\r\n .description(\"查看自选股实时行情\")\r\n .argument(\"[codes...]\", \"股票代码(空格分隔),如 513090 600519\")\r\n .action(async function (codes: string[]) {\r\n const ctx = (this as unknown as Record<string, unknown>).dskcodeCtx as DskcodeContext | undefined;\r\n\r\n // 检查用户全局配置文件是否已有自选股配置;没有则自动创建\r\n const home = process.env.HOME ?? process.env.USERPROFILE ?? \"~\";\r\n const globalConfigPath = join(home, \".dskcode\", \"settings.json\");\r\n let globalConfigHasStock = false;\r\n try {\r\n const raw = await readFile(globalConfigPath, \"utf-8\");\r\n const parsed = JSON.parse(raw) as Record<string, unknown>;\r\n const stock = parsed.stock as Record<string, unknown> | undefined;\r\n globalConfigHasStock = Array.isArray(stock?.symbols) && (stock!.symbols as unknown[]).length > 0;\r\n } catch {\r\n // 文件不存在 — 需要创建\r\n }\r\n\r\n if (!globalConfigHasStock) {\r\n const defaultSymbols = [\r\n { code: \"sh000001\" },\r\n { code: \"sz399300\" },\r\n { code: \"sh601899\" },\r\n ];\r\n const savedPath = await saveStockConfig(defaultSymbols);\r\n console.log(`${chalk.green(\"✔\")} 已生成自选股配置: ${chalk.dim(savedPath)}`);\r\n console.log(`${chalk.dim(\" 提示: 可编辑上述文件自定义自选股列表\")}\\n`);\r\n }\r\n\r\n // 加载配置(包含可能刚写入的自选股)\r\n const freshResult = await loadAndValidate();\r\n const codeList = codes && codes.length > 0\r\n ? codes\r\n : freshResult.config.stock?.symbols?.map((s) => s.code)\r\n ?? [\"sh000001\", \"sz399006\", \"sh601688\"];\r\n\r\n const app = renderApp(\r\n <StockList\r\n codes={codeList}\r\n onExit={() => process.exit(0)}\r\n />,\r\n );\r\n\r\n await app.waitUntilExit;\r\n });\r\n\r\n // game — 游戏模式\r\n initGames();\r\n\r\n program\r\n .command(\"game\")\r\n .description(\"启动内置小游戏\")\r\n .argument(\"[name]\", \"游戏名称,不指定则显示交互式游戏列表\")\r\n .action(async function (name?: string) {\r\n if (name) {\r\n const game = getGame(name);\r\n if (!game) {\r\n console.error(`未找到游戏 \"${name}\"。使用 dskcode game 查看可用游戏列表。`);\r\n process.exit(1);\r\n }\r\n console.log(`正在启动: ${game.name} — ${game.description}\\n`);\r\n await game.play();\r\n } else {\r\n const games = listGames();\r\n if (games.length === 0) {\r\n console.log(\"暂无可用游戏。\");\r\n return;\r\n }\r\n\r\n const selectedGame = await new Promise<Game | null>((resolve) => {\r\n const { unmount } = render(\r\n <GamePicker\r\n games={games}\r\n onSelect={(game) => {\r\n unmount();\r\n resolve(game);\r\n }}\r\n onExit={() => {\r\n unmount();\r\n resolve(null);\r\n }}\r\n />,\r\n { exitOnCtrlC: false },\r\n );\r\n });\r\n\r\n if (selectedGame) {\r\n console.log(`\\n 启动游戏: ${chalk.green(selectedGame.name)}\\n`);\r\n await selectedGame.play();\r\n }\r\n }\r\n });\r\n\r\n return program;\r\n}\r\n","import { existsSync, watch } from \"node:fs\";\r\nimport { mkdir, readFile, writeFile } from \"node:fs/promises\";\r\nimport { join } from \"node:path\";\r\nimport type { Config, ProviderConfig, ToolConfig, PluginConfig, StockConfig } from \"./types.js\";\r\n\r\n// ---------------------------------------------------------------------------\r\n// 出厂默认配置\r\n// ---------------------------------------------------------------------------\r\n\r\nexport const defaultConfig: Config = {\r\n defaultProvider: \"deepseek\",\r\n maxTokens: 8192,\r\n temperature: 0.7,\r\n maxToolRounds: 20,\r\n providers: [\r\n {\r\n name: \"deepseek\",\r\n baseUrl: \"https://api.deepseek.com\",\r\n model: \"deepseek-v4-flash\",\r\n },\r\n ],\r\n tools: [\r\n { name: \"read_file\", enabled: true },\r\n { name: \"write_file\", enabled: true },\r\n { name: \"edit_file\", enabled: true },\r\n { name: \"bash\", enabled: true },\r\n { name: \"glob\", enabled: true },\r\n { name: \"grep\", enabled: true },\r\n { name: \"ls\", enabled: true },\r\n { name: \"fetch\", enabled: true },\r\n ],\r\n plugins: [],\r\n stock: {\r\n symbols: [\r\n { code: \"sh000001\" },\r\n { code: \"sz399300\" },\r\n { code: \"sh601899\" },\r\n ],\r\n },\r\n};\r\n\r\n// ---------------------------------------------------------------------------\r\n// 配置文件路径解析\r\n// ---------------------------------------------------------------------------\r\n\r\n/** 判断一个字符串是否为合法的 URL(用于区分 local path 和 url)。\r\n * 目前暂未使用,保留以备 plugin 配置 URL 校验时使用。\r\n */\r\nfunction _isUrl(s: string): boolean {\r\n try {\r\n new URL(s);\r\n return true;\r\n } catch {\r\n return false;\r\n }\r\n}\r\n\r\n/**\r\n * 返回候选配置文件路径列表。\r\n * 若传入了 --config 路径,则只使用该路径;\r\n * 否则依次检查用户全局目录和项目本地目录。\r\n */\r\nfunction resolveConfigFiles(configPath?: string): string[] {\r\n if (configPath) {\r\n return [configPath];\r\n }\r\n\r\n const home = process.env.HOME ?? process.env.USERPROFILE ?? \"~\";\r\n return [\r\n join(home, \".dskcode\", \"settings.json\"),\r\n join(process.cwd(), \".dskcode\", \"settings.json\"),\r\n ];\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 深度合并\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * 将较高优先级的配置 overlay 合并到 base 之上。\r\n *\r\n * 合并规则:\r\n * - 标量字段(string / number / boolean):覆盖\r\n * - 数组字段(providers / tools / plugins):直接替换,不合并\r\n */\r\nfunction mergeConfig(base: Config, overlay: Partial<Config>): Config {\r\n const result: Config = { ...base };\r\n\r\n if (overlay.defaultProvider !== undefined) {\r\n result.defaultProvider = overlay.defaultProvider;\r\n }\r\n if (overlay.verbose !== undefined) {\r\n result.verbose = overlay.verbose;\r\n }\r\n if (overlay.maxTokens !== undefined) {\r\n result.maxTokens = overlay.maxTokens;\r\n }\r\n if (overlay.temperature !== undefined) {\r\n result.temperature = overlay.temperature;\r\n }\r\n if (overlay.maxToolRounds !== undefined) {\r\n result.maxToolRounds = overlay.maxToolRounds;\r\n }\r\n if (overlay.budgetLimit !== undefined) {\r\n result.budgetLimit = overlay.budgetLimit;\r\n }\r\n if (overlay.tokenBudgetLimit !== undefined) {\r\n result.tokenBudgetLimit = overlay.tokenBudgetLimit;\r\n }\r\n if (overlay.providers !== undefined) {\r\n result.providers = overlay.providers as ProviderConfig[];\r\n }\r\n if (overlay.tools !== undefined) {\r\n result.tools = overlay.tools as ToolConfig[];\r\n }\r\n if (overlay.plugins !== undefined) {\r\n result.plugins = overlay.plugins as PluginConfig[];\r\n }\r\n if (overlay.stock !== undefined) {\r\n result.stock = overlay.stock as StockConfig;\r\n }\r\n\r\n return result;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 环境变量解析\r\n// ---------------------------------------------------------------------------\r\n\r\n/** 环境变量前缀 */\r\nconst ENV_PREFIX = \"DSKCODE_\";\r\n\r\n/** 支持的环境变量映射表 */\r\nconst ENV_MAP: Record<string, keyof Config> = {\r\n [`${ENV_PREFIX}DEFAULT_PROVIDER`]: \"defaultProvider\",\r\n [`${ENV_PREFIX}VERBOSE`]: \"verbose\",\r\n [`${ENV_PREFIX}MAX_TOKENS`]: \"maxTokens\",\r\n [`${ENV_PREFIX}TEMPERATURE`]: \"temperature\",\r\n [`${ENV_PREFIX}MAX_TOOL_ROUNDS`]: \"maxToolRounds\",\r\n [`${ENV_PREFIX}BUDGET_LIMIT`]: \"budgetLimit\",\r\n [`${ENV_PREFIX}TOKEN_BUDGET_LIMIT`]: \"tokenBudgetLimit\",\r\n};\r\n\r\n/**\r\n * 将环境变量中读取的值覆盖到配置上。\r\n * 环境变量的优先级高于 TOML 文件,但低于 CLI flag。\r\n * 返回一个新的配置对象,不修改原始配置。\r\n */\r\nfunction applyEnvVars(config: Config): Config {\r\n // 浅拷贝以避免修改原始对象\r\n const result: Config = { ...config, providers: [...config.providers] };\r\n\r\n // 1. DSKCODE_* 前缀的环境变量\r\n for (const [envKey, configKey] of Object.entries(ENV_MAP)) {\r\n const raw = process.env[envKey];\r\n if (raw === undefined) continue;\r\n\r\n switch (configKey) {\r\n case \"verbose\":\r\n case \"defaultProvider\": {\r\n (result as unknown as Record<string, unknown>)[configKey] = raw;\r\n break;\r\n }\r\n case \"maxTokens\":\r\n case \"maxToolRounds\":\r\n case \"budgetLimit\":\r\n case \"tokenBudgetLimit\": {\r\n const n = Number(raw);\r\n if (Number.isFinite(n) && n >= 0) {\r\n (result as unknown as Record<string, unknown>)[configKey] = n;\r\n }\r\n break;\r\n }\r\n case \"temperature\": {\r\n const n = Number(raw);\r\n if (Number.isFinite(n) && n >= 0 && n <= 2) {\r\n (result as unknown as Record<string, unknown>)[configKey] = n;\r\n }\r\n break;\r\n }\r\n }\r\n }\r\n\r\n // 2. DEEPSEEK_API_KEY — 注入到名为 deepseek 的 provider\r\n const apiKey = process.env.DEEPSEEK_API_KEY;\r\n if (apiKey) {\r\n const deepseekIdx = result.providers.findIndex((p) => p.name === \"deepseek\");\r\n if (deepseekIdx !== -1) {\r\n const existing = result.providers[deepseekIdx]!;\r\n if (!existing.apiKey) {\r\n result.providers[deepseekIdx] = { ...existing, apiKey };\r\n }\r\n } else {\r\n // 如果没有 deepseek provider,自动创建一个\r\n result.providers.unshift({\r\n name: \"deepseek\",\r\n baseUrl: \"https://api.deepseek.com\",\r\n model: \"deepseek-v4-flash\",\r\n apiKey,\r\n });\r\n }\r\n }\r\n\r\n return result;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// CLI flag 覆盖\r\n// ---------------------------------------------------------------------------\r\n\r\nexport interface CliFlags {\r\n verbose?: boolean;\r\n model?: string;\r\n maxTokens?: number;\r\n temperature?: number;\r\n budgetLimit?: number;\r\n tokenBudgetLimit?: number;\r\n}\r\n\r\n/**\r\n * 将 CLI flag 中的值覆盖到配置上。\r\n * CLI flag 的优先级最高。\r\n * 返回一个新的配置对象,不修改原始配置。\r\n */\r\nexport function applyCliOverrides(config: Config, flags: CliFlags): Config {\r\n const result: Config = { ...config, providers: [...config.providers] };\r\n\r\n if (flags.verbose !== undefined) {\r\n result.verbose = flags.verbose;\r\n }\r\n if (flags.model !== undefined) {\r\n // 将 --model 的值映射为标准 model 名称\r\n // 如果用户指定了 --model,覆盖 defaultProvider 中配置的 model\r\n const providerIdx = result.providers.findIndex(\r\n (p) => p.name === result.defaultProvider,\r\n );\r\n if (providerIdx !== -1) {\r\n result.providers[providerIdx] = {\r\n ...result.providers[providerIdx]!,\r\n model: flags.model,\r\n };\r\n }\r\n }\r\n if (flags.maxTokens !== undefined && flags.maxTokens > 0) {\r\n result.maxTokens = flags.maxTokens;\r\n }\r\n if (\r\n flags.temperature !== undefined &&\r\n flags.temperature >= 0 &&\r\n flags.temperature <= 2\r\n ) {\r\n result.temperature = flags.temperature;\r\n }\r\n if (flags.budgetLimit !== undefined && flags.budgetLimit >= 0) {\r\n result.budgetLimit = flags.budgetLimit;\r\n }\r\n if (flags.tokenBudgetLimit !== undefined && flags.tokenBudgetLimit >= 0) {\r\n result.tokenBudgetLimit = flags.tokenBudgetLimit;\r\n }\r\n return result;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 配置校验\r\n// ---------------------------------------------------------------------------\r\n\r\nexport interface ConfigError {\r\n field: string;\r\n message: string;\r\n}\r\n\r\n/**\r\n * 校验配置的合法性,返回错误列表。\r\n * 返回空数组表示配置合法。\r\n */\r\nexport function validateConfig(config: Config): ConfigError[] {\r\n const errors: ConfigError[] = [];\r\n\r\n // 1. 至少需要一个 Provider\r\n if (!config.providers || config.providers.length === 0) {\r\n errors.push({\r\n field: \"providers\",\r\n message: \"至少需要配置一个 Provider。请通过配置文件或 DEEPSEEK_API_KEY 环境变量设置。\",\r\n });\r\n }\r\n\r\n // 2. 每个 Provider 必须有 name 和 model,且 model 必须受支持\r\n const SUPPORTED_MODELS = [\"deepseek-v4-flash\", \"deepseek-v4-pro\"];\r\n for (let i = 0; i < config.providers.length; i++) {\r\n const p = config.providers[i]!;\r\n if (!p.name) {\r\n errors.push({\r\n field: `providers[${i}].name`,\r\n message: `第 ${i + 1} 个 Provider 缺少 name 字段。`,\r\n });\r\n }\r\n if (!p.model) {\r\n errors.push({\r\n field: `providers[${i}].model`,\r\n message: `Provider \"${p.name || i}\" 缺少 model 字段。`,\r\n });\r\n } else if (!SUPPORTED_MODELS.includes(p.model)) {\r\n errors.push({\r\n field: `providers[${i}].model`,\r\n message: `Provider \"${p.name || i}\" 的 model \"${p.model}\" 不受支持。dskcode 仅支持: ${SUPPORTED_MODELS.join(\", \")}`,\r\n });\r\n }\r\n }\r\n\r\n // 3. defaultProvider 必须存在于 providers 列表中\r\n if (config.defaultProvider) {\r\n const exists = config.providers.some(\r\n (p) => p.name === config.defaultProvider,\r\n );\r\n if (!exists) {\r\n errors.push({\r\n field: \"defaultProvider\",\r\n message: `默认 Provider \"${config.defaultProvider}\" 未在 providers 中定义。`,\r\n });\r\n }\r\n }\r\n\r\n // 4. temperature 范围校验\r\n if (\r\n config.temperature !== undefined &&\r\n (config.temperature < 0 || config.temperature > 2)\r\n ) {\r\n errors.push({\r\n field: \"temperature\",\r\n message: \"temperature 必须在 0.0 ~ 2.0 之间。\",\r\n });\r\n }\r\n\r\n // 5. maxTokens 范围校验\r\n if (config.maxTokens !== undefined && config.maxTokens < 1) {\r\n errors.push({\r\n field: \"maxTokens\",\r\n message: \"maxTokens 必须大于等于 1。\",\r\n });\r\n }\r\n\r\n // 6. maxToolRounds 范围校验\r\n if (config.maxToolRounds !== undefined && config.maxToolRounds < 1) {\r\n errors.push({\r\n field: \"maxToolRounds\",\r\n message: \"maxToolRounds 必须大于等于 1。\",\r\n });\r\n }\r\n\r\n // 7. budgetLimit 范围校验\r\n if (config.budgetLimit !== undefined && config.budgetLimit < 0) {\r\n errors.push({\r\n field: \"budgetLimit\",\r\n message: \"budgetLimit 必须大于等于 0(0 表示不限制)。\",\r\n });\r\n }\r\n\r\n // 8. tokenBudgetLimit 范围校验\r\n if (config.tokenBudgetLimit !== undefined && config.tokenBudgetLimit < 0) {\r\n errors.push({\r\n field: \"tokenBudgetLimit\",\r\n message: \"tokenBudgetLimit 必须大于等于 0(0 表示不限制)。\",\r\n });\r\n }\r\n\r\n return errors;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 核心加载流程\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * 从多级配置源加载并合并配置。\r\n *\r\n * 解析顺序(后加载的优先级更高):\r\n * 1. 内置默认值 —— defaultConfig\r\n * 2. 用户全局 —— ~/.dskcode/settings.json\r\n * 3. 项目本地 —— .dskcode/settings.json(或通过 --config 指定的路径)\r\n * 4. 环境变量 —— DEEPSEEK_API_KEY、DSKCODE_* 等\r\n * 5. CLI flag —— 由调用方通过 applyCliOverrides() 单独注入\r\n */\r\nexport async function loadConfig(configPath?: string): Promise<Config> {\r\n const filePaths = resolveConfigFiles(configPath);\r\n\r\n let config: Config = structuredClone(defaultConfig);\r\n\r\n // 1-3. 依次加载 JSON 配置文件\r\n for (const filePath of filePaths) {\r\n try {\r\n const raw = await readFile(filePath, \"utf-8\");\r\n const parsed = JSON.parse(raw) as Partial<Config>;\r\n config = mergeConfig(config, parsed);\r\n } catch {\r\n // 文件不存在或权限不足 — 静默跳过\r\n }\r\n }\r\n\r\n // 4. 环境变量覆盖\r\n config = applyEnvVars(config);\r\n\r\n return config;\r\n}\r\n\r\n/**\r\n * 加载配置并同时执行校验。\r\n * 校验错误不会 throw,而是通过返回值中的 errors 字段返回,\r\n * 由调用方决定如何处理(例如在 middleware 中输出警告)。\r\n */\r\nexport async function loadAndValidate(\r\n configPath?: string,\r\n): Promise<{ config: Config; errors: ConfigError[] }> {\r\n const config = await loadConfig(configPath);\r\n const errors = validateConfig(config);\r\n return { config, errors };\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 配置热加载(Watch 模式)\r\n// ---------------------------------------------------------------------------\r\n\r\nexport type ConfigChangeCallback = (config: Config) => void;\r\n\r\n/**\r\n * 监听配置文件变更,在文件被修改时重新加载配置并调用回调。\r\n *\r\n * @param callback 配置变更后的回调函数\r\n * @param configPath 可选,指定配置文件路径(对应 --config flag)\r\n * @returns 一个 unwatch 函数,调用后可停止监听\r\n */\r\nexport function watchConfig(\r\n callback: ConfigChangeCallback,\r\n configPath?: string,\r\n): () => void {\r\n const filePaths = resolveConfigFiles(configPath).filter((fp) => existsSync(fp));\r\n\r\n // 如果一个文件都不存在,则监听项目本地的 .dskcode/settings.json(即使还没创建)\r\n if (filePaths.length === 0) {\r\n filePaths.push(join(process.cwd(), \".dskcode\", \"settings.json\"));\r\n }\r\n\r\n const watchers: ReturnType<typeof watch>[] = [];\r\n let debounceTimer: ReturnType<typeof setTimeout> | undefined;\r\n\r\n for (const filePath of filePaths) {\r\n try {\r\n const watcher = watch(filePath, (eventType) => {\r\n if (eventType !== \"change\") return;\r\n\r\n // 防抖:多次连续变更只触发一次\r\n if (debounceTimer) clearTimeout(debounceTimer);\r\n debounceTimer = setTimeout(() => {\r\n void (async () => {\r\n try {\r\n const config = await loadConfig(configPath);\r\n callback(config);\r\n } catch {\r\n // 重载失败时不回调,等待下一次变更\r\n }\r\n })();\r\n }, 300);\r\n });\r\n\r\n watchers.push(watcher);\r\n } catch {\r\n // 无法监听的文件(例如还不存在)— 跳过\r\n }\r\n }\r\n\r\n return () => {\r\n if (debounceTimer) clearTimeout(debounceTimer);\r\n for (const w of watchers) {\r\n w.close();\r\n }\r\n };\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// API Key 持久化\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * 将 API Key 保存到用户全局配置 ~/.dskcode/settings.json。\r\n * 如果文件已存在,合并写入;不存在则新建。\r\n * 返回保存的文件路径。\r\n */\r\nexport async function saveApiKey(apiKey: string): Promise<string> {\r\n const home = process.env.HOME ?? process.env.USERPROFILE ?? \"~\";\r\n const configDir = join(home, \".dskcode\");\r\n const configFile = join(configDir, \"settings.json\");\r\n\r\n // 确保目录存在\r\n await mkdir(configDir, { recursive: true });\r\n\r\n // 读取现有配置,或从默认配置开始\r\n let configData: Record<string, unknown>;\r\n try {\r\n const raw = await readFile(configFile, \"utf-8\");\r\n configData = JSON.parse(raw);\r\n } catch {\r\n // 文件不存在,用内置默认值填充(tools、plugins 等都会写入)\r\n configData = structuredClone(defaultConfig) as unknown as Record<string, unknown>;\r\n }\r\n\r\n // 更新或创建 deepseek provider\r\n const providers = (configData.providers as Record<string, unknown>[]) ?? [];\r\n const existing = providers.find((p) => p.name === \"deepseek\");\r\n\r\n if (existing) {\r\n existing.apiKey = apiKey;\r\n } else {\r\n providers.push({\r\n name: \"deepseek\",\r\n apiKey,\r\n baseUrl: \"https://api.deepseek.com\",\r\n model: \"deepseek-v4-flash\",\r\n });\r\n }\r\n\r\n configData.providers = providers;\r\n\r\n // 写回文件\r\n await writeFile(configFile, JSON.stringify(configData, null, 2), \"utf-8\");\r\n\r\n return configFile;\r\n}\r\n\r\n/**\r\n * 将模型偏好保存到用户全局配置 ~/.dskcode/settings.json。\r\n * 只更新 defaultProvider 的 model 字段,不覆盖其他配置。\r\n * 返回保存的文件路径。\r\n */\r\nexport async function saveModelConfig(model: string): Promise<string> {\r\n const home = process.env.HOME ?? process.env.USERPROFILE ?? \"~\";\r\n const configDir = join(home, \".dskcode\");\r\n const configFile = join(configDir, \"settings.json\");\r\n\r\n // 确保目录存在\r\n await mkdir(configDir, { recursive: true });\r\n\r\n // 读取现有配置\r\n let configData: Record<string, unknown>;\r\n try {\r\n const raw = await readFile(configFile, \"utf-8\");\r\n configData = JSON.parse(raw);\r\n } catch {\r\n configData = structuredClone(defaultConfig) as unknown as Record<string, unknown>;\r\n }\r\n\r\n // 更新 defaultProvider 的 model\r\n const providers = (configData.providers as Record<string, unknown>[]) ?? [];\r\n const defaultProviderName = (configData.defaultProvider as string) ?? \"deepseek\";\r\n const existing = providers.find((p) => p.name === defaultProviderName);\r\n\r\n if (existing) {\r\n existing.model = model;\r\n } else {\r\n // 没有 default provider,创建一个\r\n providers.push({\r\n name: defaultProviderName,\r\n baseUrl: \"https://api.deepseek.com\",\r\n model,\r\n });\r\n }\r\n\r\n configData.providers = providers;\r\n\r\n // 写回文件\r\n await writeFile(configFile, JSON.stringify(configData, null, 2), \"utf-8\");\r\n\r\n return configFile;\r\n}\r\n\r\n/**\r\n * 将自选股配置保存到用户全局配置 ~/.dskcode/settings.json。\r\n * 如果文件已存在,合并写入;不存在则新建。\r\n * 返回保存的文件路径。\r\n */\r\nexport async function saveStockConfig(symbols: StockConfig[\"symbols\"]): Promise<string> {\r\n const home = process.env.HOME ?? process.env.USERPROFILE ?? \"~\";\r\n const configDir = join(home, \".dskcode\");\r\n const configFile = join(configDir, \"settings.json\");\r\n\r\n // 确保目录存在\r\n await mkdir(configDir, { recursive: true });\r\n\r\n // 读取现有配置,或从默认配置开始\r\n let configData: Record<string, unknown>;\r\n try {\r\n const raw = await readFile(configFile, \"utf-8\");\r\n configData = JSON.parse(raw);\r\n } catch {\r\n configData = structuredClone(defaultConfig) as unknown as Record<string, unknown>;\r\n }\r\n\r\n // 写入自选股配置\r\n configData.stock = { symbols };\r\n\r\n // 写回文件\r\n await writeFile(configFile, JSON.stringify(configData, null, 2), \"utf-8\");\r\n\r\n return configFile;\r\n}\r\n","import type { Command } from \"commander\";\r\nimport {\r\n loadAndValidate,\r\n applyCliOverrides,\r\n defaultConfig,\r\n} from \"../config/index.js\";\r\nimport type { Config } from \"../config/index.js\";\r\n\r\n/**\r\n * dskcode 运行时上下文。\r\n * 通过 commander 的 preAction hook 注入到每个命令中。\r\n */\r\nexport interface DskcodeContext {\r\n config: Config;\r\n verbose: boolean;\r\n}\r\n\r\n/**\r\n * 在 preAction hook 中加载配置并构造上下文。\r\n *\r\n * 完整的配置解析流水线:\r\n * 1. 内置默认值 —— defaultConfig\r\n * 2. 用户全局 —— ~/.dskcode/settings.json\r\n * 3. 项目本地 —— .dskcode/settings.json(或 --config 指定的路径)\r\n * 4. 环境变量 —— DEEPSEEK_API_KEY / DSKCODE_*\r\n * 5. CLI flag —— --verbose / --model 等\r\n */\r\nexport async function loadConfigMiddleware(\r\n this: Command,\r\n): Promise<DskcodeContext> {\r\n const opts = this.optsWithGlobals() as {\r\n verbose?: boolean;\r\n config?: string;\r\n model?: string;\r\n };\r\n const verbose = opts.verbose ?? false;\r\n\r\n // 1-4. 加载 TOML 文件 + 环境变量\r\n let config: Config;\r\n const errors: string[] = [];\r\n try {\r\n const result = await loadAndValidate(opts.config);\r\n config = result.config;\r\n if (result.errors.length > 0) {\r\n for (const e of result.errors) {\r\n errors.push(e.message);\r\n }\r\n }\r\n } catch {\r\n config = structuredClone(defaultConfig);\r\n }\r\n\r\n // 5. CLI flag 覆盖(优先级最高)\r\n config = applyCliOverrides(config, {\r\n verbose,\r\n model: opts.model,\r\n });\r\n\r\n // 校验错误输出(不阻断执行)\r\n if (errors.length > 0 && verbose) {\r\n for (const msg of errors) {\r\n console.error(` ⚠ ${msg}`);\r\n }\r\n }\r\n\r\n return { config, verbose };\r\n}\r\n","import type { Command } from \"commander\";\r\nimport chalk from \"chalk\";\r\n\r\nexport function customHelp(program: Command): string {\r\n const lines: string[] = [];\r\n\r\n lines.push(\"\");\r\n lines.push(chalk.bold(\"用法:\"));\r\n lines.push(` ${chalk.cyan(\"dskcode\")} ${chalk.dim(\"[global-options]\")} ${chalk.green(\"<command>\")} ${chalk.dim(\"[options]\")}`);\r\n lines.push(\"\");\r\n\r\n const globalOpts = program.options.filter(\r\n (o) => o.long !== \"--help\" && o.long !== \"--version\" && o.long !== \"--config\",\r\n );\r\n if (globalOpts.length > 0) {\r\n lines.push(chalk.bold(\"全局选项:\"));\r\n for (const opt of globalOpts) {\r\n const flags = [opt.short, opt.long].filter(Boolean).join(\", \");\r\n lines.push(` ${chalk.cyan(flags.padEnd(24))} ${opt.description ?? \"\"}`);\r\n }\r\n lines.push(\"\");\r\n }\r\n\r\n lines.push(chalk.bold(\"内置选项:\"));\r\n for (const flag of [\"-h, --help\", \"-V, --version\"]) {\r\n const opt = program.options.find(\r\n (o) => o.long === (flag.includes(\"help\") ? \"--help\" : \"--version\"),\r\n );\r\n if (opt) {\r\n lines.push(` ${chalk.cyan(flag.padEnd(24))} ${opt.description ?? \"\"}`);\r\n }\r\n }\r\n lines.push(\"\");\r\n\r\n const cmds = program.commands.filter((c) => !c.name().startsWith(\"help\"));\r\n if (cmds.length > 0) {\r\n lines.push(chalk.bold(\"命令:\"));\r\n for (const cmd of cmds) {\r\n lines.push(` ${chalk.green(cmd.name().padEnd(24))} ${cmd.description()}`);\r\n }\r\n lines.push(\"\");\r\n }\r\n\r\n lines.push(chalk.bold(\"示例:\"));\r\n lines.push(` ${chalk.dim(\"# 启动交互式对话\")}`);\r\n lines.push(\" dskcode chat\");\r\n lines.push(` ${chalk.dim(\"# 让 AI 执行一个任务\")}`);\r\n lines.push(\" dskcode run 修改所有 TODO 注释\");\r\n lines.push(` ${chalk.dim(\"# 运行配置向导\")}`);\r\n lines.push(\" dskcode setup\");\r\n lines.push(` ${chalk.dim(\"# 生成 shell 自动补全\")}`);\r\n lines.push(\" dskcode completion\");\r\n lines.push(` ${chalk.dim(\"# 查看自选股行情\")}`);\r\n lines.push(\" dskcode stock\");\r\n lines.push(` ${chalk.dim(\"# 查看指定股票行情\")}`);\r\n lines.push(\" dskcode stock sh513090 sz000001\");\r\n lines.push(\"\");\r\n\r\n return lines.join(\"\\n\");\r\n}\r\n","import { createInterface } from \"node:readline\";\r\nimport chalk from \"chalk\";\r\n\r\n/**\r\n * 检测是否有可用的 API Key。\r\n * 遍历所有 provider 检查是否配置了 apiKey,同时检查 DEEPSEEK_API_KEY 环境变量。\r\n */\r\nexport function hasApiKey(providers: { apiKey?: string }[]): boolean {\r\n if (providers.some((p) => p.apiKey)) return true;\r\n if (process.env.DEEPSEEK_API_KEY) return true;\r\n return false;\r\n}\r\n\r\n/**\r\n * API Key 最小长度校验值。\r\n * DeepSeek 的 Key 通常以 sk- 开头,长度远超 10 位。\r\n */\r\nconst MIN_API_KEY_LENGTH = 10;\r\n\r\n/**\r\n * 交互式提示用户输入 DeepSeek API Key。\r\n * 使用 Node readline 的 password 模式(输入不可见)。\r\n * 返回用户输入的 Key,如果用户取消则返回 null。\r\n */\r\nexport async function promptForApiKey(): Promise<string | null> {\r\n console.log(chalk.yellow(\"\\n ⚠ 未检测到 API Key 配置\"));\r\n console.log(chalk.dim(\" 你可以通过以下任一方式配置:\"));\r\n console.log(chalk.dim(\" · 环境变量: export DEEPSEEK_API_KEY=sk-xxx\"));\r\n console.log(chalk.dim(\" · 配置文件: ~/.dskcode/settings.json\"));\r\n console.log(chalk.dim(\" · 下面直接输入,自动保存到全局配置\\n\"));\r\n\r\n const rl = createInterface({\r\n input: process.stdin,\r\n output: process.stdout,\r\n });\r\n\r\n return new Promise<string | null>((resolve) => {\r\n let resolved = false;\r\n\r\n const cleanup = () => {\r\n if (resolved) return;\r\n resolved = true;\r\n process.stdin.removeListener(\"keypress\", onKeypress);\r\n rl.close();\r\n };\r\n\r\n const onKeypress = (_: unknown, key: { ctrl?: boolean; name?: string }) => {\r\n if (key.ctrl && key.name === \"c\") {\r\n cleanup();\r\n resolve(null);\r\n }\r\n };\r\n\r\n process.stdin.on(\"keypress\", onKeypress);\r\n\r\n rl.question(\r\n ` ${chalk.cyan(\"🔑\")} ${chalk.bold(\"请输入你的 DeepSeek API Key:\")} `,\r\n (answer) => {\r\n cleanup();\r\n const trimmed = answer.trim();\r\n if (!trimmed) {\r\n console.log(chalk.red(\" ✖ API Key 不能为空\"));\r\n resolve(null);\r\n return;\r\n }\r\n if (trimmed.length < MIN_API_KEY_LENGTH) {\r\n console.log(chalk.red(\" ✖ API Key 格式不正确,长度至少 10 位\"));\r\n resolve(null);\r\n return;\r\n }\r\n resolve(trimmed);\r\n },\r\n );\r\n });\r\n}\r\n","import { createInterface } from \"node:readline\";\r\nimport { existsSync, statSync, lstatSync } from \"node:fs\";\r\nimport { mkdir, readdir, cp, access, realpath, readFile } from \"node:fs/promises\";\r\nimport { join } from \"node:path\";\r\nimport chalk from \"chalk\";\r\n\r\n/**\r\n * Claude Code 全局 skills 目录路径(~/.claude/skills)。\r\n * 跨平台兼容:优先用 HOME,Windows 下回退到 USERPROFILE。\r\n */\r\nfunction getClaudeSkillsDir(): string {\r\n const home = process.env.HOME ?? process.env.USERPROFILE ?? \"~\";\r\n return join(home, \".claude\", \"skills\");\r\n}\r\n\r\n/**\r\n * dskcode 全局 skills 目录路径(~/.dskcode/skills)。\r\n */\r\nfunction getDskcodeSkillsDir(): string {\r\n const home = process.env.HOME ?? process.env.USERPROFILE ?? \"~\";\r\n return join(home, \".dskcode\", \"skills\");\r\n}\r\n\r\n/**\r\n * 判断某个目录是否为一个有效的 skill(包含 SKILL.md)。\r\n */\r\nasync function isSkillDir(dir: string): Promise<boolean> {\r\n try {\r\n await access(join(dir, \"SKILL.md\"));\r\n return true;\r\n } catch {\r\n return false;\r\n }\r\n}\r\n\r\n/**\r\n * 列出 Claude Code skills 目录下所有有效的 skill 名称(子目录包含 SKILL.md)。\r\n * 找不到目录返回空数组。\r\n */\r\nexport async function listClaudeSkills(): Promise<string[]> {\r\n const claudeDir = getClaudeSkillsDir();\r\n if (!existsSync(claudeDir)) return [];\r\n\r\n let entries: string[];\r\n try {\r\n entries = await readdir(claudeDir);\r\n } catch {\r\n return [];\r\n }\r\n\r\n const skills: string[] = [];\r\n for (const name of entries) {\r\n const full = join(claudeDir, name);\r\n const stat = statSync(full);\r\n if (stat.isDirectory() && (await isSkillDir(full))) {\r\n skills.push(name);\r\n }\r\n }\r\n return skills;\r\n}\r\n\r\n/**\r\n * 是否存在可导入的 Claude skills。\r\n */\r\nexport async function hasClaudeSkills(): Promise<boolean> {\r\n const skills = await listClaudeSkills();\r\n return skills.length > 0;\r\n}\r\n\r\n/**\r\n * 交互式询问用户是否确认。默认接受回车视为\"否\"。\r\n * 返回 true 表示用户确认。\r\n */\r\nfunction askConfirm(question: string): Promise<boolean> {\r\n const rl = createInterface({\r\n input: process.stdin,\r\n output: process.stdout,\r\n });\r\n\r\n return new Promise<boolean>((resolve) => {\r\n let resolved = false;\r\n\r\n const cleanup = () => {\r\n if (resolved) return;\r\n resolved = true;\r\n process.stdin.removeListener(\"keypress\", onKeypress);\r\n rl.close();\r\n };\r\n\r\n const onKeypress = (_: unknown, key: { ctrl?: boolean; name?: string }) => {\r\n if (key.ctrl && key.name === \"c\") {\r\n cleanup();\r\n resolve(false);\r\n }\r\n };\r\n\r\n process.stdin.on(\"keypress\", onKeypress);\r\n\r\n rl.question(question, (answer) => {\r\n cleanup();\r\n const trimmed = answer.trim().toLowerCase();\r\n // 默认回车 = 否;明确输入 y / yes 才算确认\r\n resolve(trimmed === \"y\" || trimmed === \"yes\");\r\n });\r\n });\r\n}\r\n\r\n/**\r\n * 将 Claude skills 目录复制到 ~/.dskcode/skills。\r\n * 已存在的同名 skill 跳过,避免覆盖用户自定义修改。\r\n * 返回 { imported, skipped } 两个数组。\r\n */\r\nexport async function importClaudeSkills(\r\n skillNames: string[],\r\n): Promise<{ imported: string[]; skipped: string[] }> {\r\n const claudeDir = getClaudeSkillsDir();\r\n const dskcodeDir = getDskcodeSkillsDir();\r\n\r\n await mkdir(dskcodeDir, { recursive: true });\r\n\r\n const imported: string[] = [];\r\n const skipped: string[] = [];\r\n\r\n for (const name of skillNames) {\r\n const src = join(claudeDir, name);\r\n const dest = join(dskcodeDir, name);\r\n\r\n if (existsSync(dest)) {\r\n skipped.push(name);\r\n continue;\r\n }\r\n\r\n // 递归复制整个 skill 目录,保留原始结构(SKILL.md、reference 等)。\r\n // 两个解引用手段叠加:\r\n // 1) realpath() 解决 source 顶层是 symlink 的情况(cp 的 dereference 只处理源目录内部 symlink)\r\n // 2) cp 的 dereference: true 解决源目录内部遇到的 symlink\r\n // 这样可以避开 Windows 上非管理员无法创建 symlink 的 EPERM 错误\r\n // (Claude Code 的 skill 目录往往是 symlink 指向 ~/.agents/skills)。\r\n const realSrc = lstatSync(src).isSymbolicLink() ? await realpath(src) : src;\r\n await cp(realSrc, dest, { recursive: true, dereference: true });\r\n imported.push(name);\r\n }\r\n\r\n return { imported, skipped };\r\n}\r\n\r\n/**\r\n * 统计全局 ~/.dskcode/skills 下的有效 skill 数量。\r\n */\r\nexport async function countDskcodeSkills(): Promise<number> {\r\n const dskcodeDir = getDskcodeSkillsDir();\r\n if (!existsSync(dskcodeDir)) return 0;\r\n\r\n let entries: string[];\r\n try {\r\n entries = await readdir(dskcodeDir);\r\n } catch {\r\n return 0;\r\n }\r\n\r\n let count = 0;\r\n for (const name of entries) {\r\n const full = join(dskcodeDir, name);\r\n if (statSync(full).isDirectory() && (await isSkillDir(full))) {\r\n count++;\r\n }\r\n }\r\n return count;\r\n}\r\n\r\n/**\r\n * 项目本地 skill 目录路径({cwd}/.dskcode/skill)。\r\n */\r\nexport function getProjectSkillDir(cwd: string): string {\r\n return join(cwd, \".dskcode\", \"skill\");\r\n}\r\n\r\n/**\r\n * 检测项目本地 .dskcode/skill 目录下是否存在有效 skill。\r\n */\r\nexport async function hasProjectLocalSkills(cwd: string): Promise<boolean> {\r\n const count = await countProjectLocalSkills(cwd);\r\n return count > 0;\r\n}\r\n\r\n/**\r\n * 统计项目本地 .dskcode/skill 下的有效 skill 数量。\r\n */\r\nexport async function countProjectLocalSkills(cwd: string): Promise<number> {\r\n const skillDir = getProjectSkillDir(cwd);\r\n if (!existsSync(skillDir)) return 0;\r\n\r\n let entries: string[];\r\n try {\r\n entries = await readdir(skillDir);\r\n } catch {\r\n return 0;\r\n }\r\n\r\n let count = 0;\r\n for (const name of entries) {\r\n const full = join(skillDir, name);\r\n const stat = statSync(full);\r\n if (stat.isDirectory() && (await isSkillDir(full))) {\r\n count++;\r\n }\r\n }\r\n return count;\r\n}\r\n\r\n/**\r\n * 检测全局 ~/.dskcode/skills 目录下是否存在有效 skill。\r\n */\r\nexport async function hasDskcodeSkills(): Promise<boolean> {\r\n const dskcodeDir = getDskcodeSkillsDir();\r\n if (!existsSync(dskcodeDir)) return false;\r\n\r\n let entries: string[];\r\n try {\r\n entries = await readdir(dskcodeDir);\r\n } catch {\r\n return false;\r\n }\r\n\r\n for (const name of entries) {\r\n const full = join(dskcodeDir, name);\r\n const stat = statSync(full);\r\n if (stat.isDirectory() && (await isSkillDir(full))) {\r\n return true;\r\n }\r\n }\r\n return false;\r\n}\r\n\r\n/**\r\n * 主流程:检测已有 skill 并决定是否提示导入 Claude Code skill。\r\n * 通常在 chat 命令的 API Key 检查之后调用。\r\n *\r\n * 规则(按优先级):\r\n * 1. 如果项目本地 .dskcode/skill 下有 skill,跳过导入\r\n * 2. 如果全局 ~/.dskcode/skills 下有 skill(已导入过),跳过导入\r\n * 3. 否则检测 Claude Code 并询问是否导入\r\n */\r\nexport async function promptImportClaudeSkills(cwd?: string): Promise<void> {\r\n // 检查项目本地 .dskcode/skill\r\n if (cwd) {\r\n const hasLocal = await hasProjectLocalSkills(cwd);\r\n if (hasLocal) {\r\n console.log(chalk.dim(\" 检测到项目本地 .dskcode/skill,跳过导入 Claude Code skill\\n\"));\r\n return;\r\n }\r\n }\r\n\r\n // 检查全局 ~/.dskcode/skills(已经导入过的无需再提示)\r\n const hasGlobal = await hasDskcodeSkills();\r\n if (hasGlobal) {\r\n return;\r\n }\r\n\r\n const skills = await listClaudeSkills();\r\n if (skills.length === 0) return;\r\n\r\n console.log(chalk.cyan(`\\n ✦ 检测到你在 Claude Code 中安装了 ${String(skills.length)} 个 skill:`));\r\n // 每行最多展示 5 个,避免列表过长\r\n const preview = skills.slice(0, 5).map((s) => chalk.dim(` · ${s}`)).join(\"\\n\");\r\n console.log(preview);\r\n if (skills.length > 5) {\r\n console.log(chalk.dim(` · ...等 ${String(skills.length)} 个`));\r\n }\r\n console.log(chalk.dim(` 源目录: ${getClaudeSkillsDir()}`));\r\n console.log(chalk.dim(` 目标目录: ${getDskcodeSkillsDir()}\\n`));\r\n\r\n const confirmed = await askConfirm(\r\n ` ${chalk.cyan(\"📦\")} 是否将这些 skill 导入到 dskcode?${chalk.dim(\"[y/N] \")} `,\r\n );\r\n\r\n if (!confirmed) {\r\n console.log(chalk.dim(\" 已跳过 skill 导入\\n\"));\r\n return;\r\n }\r\n\r\n try {\r\n const { imported, skipped } = await importClaudeSkills(skills);\r\n if (imported.length > 0) {\r\n console.log(\r\n chalk.green(` ✔ 已导入 ${String(imported.length)} 个 skill 到 ~/.dskcode/skills`),\r\n );\r\n }\r\n if (skipped.length > 0) {\r\n console.log(\r\n chalk.yellow(` ⚠ ${String(skipped.length)} 个 skill 已存在,已跳过:${skipped.join(\", \")}`),\r\n );\r\n }\r\n console.log(\"\");\r\n } catch (err) {\r\n console.log(\r\n chalk.red(` ✖ 导入 skill 失败: ${err instanceof Error ? err.message : String(err)}`),\r\n );\r\n console.log(chalk.dim(\" 你可以稍后手动复制 ~/.claude/skills 到 ~/.dskcode/skills\\n\"));\r\n }\r\n}\r\n\r\n/** 单个 skill 的展示信息 */\r\nexport interface SkillInfo {\r\n name: string;\r\n description: string;\r\n}\r\n\r\n/**\r\n * 列出全局 ~/.dskcode/skills 下所有有效的 skill 名称。\r\n */\r\nexport async function listDskcodeSkills(): Promise<string[]> {\r\n const dskcodeDir = getDskcodeSkillsDir();\r\n if (!existsSync(dskcodeDir)) return [];\r\n\r\n let entries: string[];\r\n try {\r\n entries = await readdir(dskcodeDir);\r\n } catch {\r\n return [];\r\n }\r\n\r\n const skills: string[] = [];\r\n for (const name of entries) {\r\n const full = join(dskcodeDir, name);\r\n if (statSync(full).isDirectory() && (await isSkillDir(full))) {\r\n skills.push(name);\r\n }\r\n }\r\n return skills;\r\n}\r\n\r\n/**\r\n * 列出项目本地 .dskcode/skill 下所有有效的 skill 名称。\r\n */\r\nexport async function listProjectSkills(cwd: string): Promise<string[]> {\r\n const skillDir = getProjectSkillDir(cwd);\r\n if (!existsSync(skillDir)) return [];\r\n\r\n let entries: string[];\r\n try {\r\n entries = await readdir(skillDir);\r\n } catch {\r\n return [];\r\n }\r\n\r\n const skills: string[] = [];\r\n for (const name of entries) {\r\n const full = join(skillDir, name);\r\n if (statSync(full).isDirectory() && (await isSkillDir(full))) {\r\n skills.push(name);\r\n }\r\n }\r\n return skills;\r\n}\r\n\r\n/**\r\n * 解析 SKILL.md 的 YAML front matter,获取 skill 名称和描述。\r\n * 解析失败时返回 null。\r\n */\r\nasync function readSkillInfo(skillDir: string): Promise<SkillInfo | null> {\r\n try {\r\n const content = await readFile(join(skillDir, \"SKILL.md\"), \"utf-8\");\r\n const match = content.match(/^---\\n([\\s\\S]*?)\\n---/);\r\n if (!match) return null;\r\n\r\n const frontMatter = match[1]!;\r\n const nameMatch = frontMatter.match(/^name:\\s*(.+)$/m);\r\n const descMatch = frontMatter.match(/^description:\\s*(.+)$/m);\r\n\r\n return {\r\n name: nameMatch?.[1]?.trim() ?? \"\",\r\n description: descMatch?.[1]?.trim() ?? \"\",\r\n };\r\n } catch {\r\n return null;\r\n }\r\n}\r\n\r\n/**\r\n * 获取所有可用 skill 的详细信息(全局 + 项目本地)。\r\n * 返回 SkillInfo 列表,解析失败时仍使用目录名作为 name。\r\n */\r\nexport async function getAllSkills(cwd: string): Promise<SkillInfo[]> {\r\n const [globalNames, localNames] = await Promise.all([\r\n listDskcodeSkills(),\r\n listProjectSkills(cwd),\r\n ]);\r\n\r\n // 合并去重,项目本地优先\r\n const seen = new Set<string>();\r\n const allNames: string[] = [];\r\n for (const name of [...localNames, ...globalNames]) {\r\n if (!seen.has(name)) {\r\n seen.add(name);\r\n allNames.push(name);\r\n }\r\n }\r\n\r\n const dskcodeDir = getDskcodeSkillsDir();\r\n const projectDir = getProjectSkillDir(cwd);\r\n\r\n const results: SkillInfo[] = [];\r\n for (const name of allNames) {\r\n // 优先读项目本地的 SKILL.md,再读全局\r\n const localPath = join(projectDir, name);\r\n let info: SkillInfo | null = null;\r\n if (existsSync(localPath)) {\r\n info = await readSkillInfo(localPath);\r\n }\r\n if (!info) {\r\n const globalPath = join(dskcodeDir, name);\r\n if (existsSync(globalPath)) {\r\n info = await readSkillInfo(globalPath);\r\n }\r\n }\r\n results.push(info ?? { name, description: \"\" });\r\n }\r\n\r\n return results;\r\n}\r\n\r\n// 暴露路径获取函数,便于测试\r\nexport { getClaudeSkillsDir, getDskcodeSkillsDir };","import { render } from \"ink\";\r\nimport type { ReactNode } from \"react\";\r\n\r\nexport interface RenderScopeHandle {\r\n waitUntilExit: Promise<unknown>;\r\n unmount: () => void;\r\n clear: () => void;\r\n}\r\n\r\nexport function renderApp(node: ReactNode): RenderScopeHandle {\r\n // exitOnCtrlC: false — 让组件自行处理 Ctrl+C(双击退出)\r\n const { waitUntilExit, clear, unmount } = render(node, { exitOnCtrlC: false });\r\n return { waitUntilExit: waitUntilExit(), clear, unmount };\r\n}\r\n\r\nexport async function unmountApp(handle: RenderScopeHandle): Promise<void> {\r\n handle.unmount();\r\n await new Promise((resolve) => setTimeout(resolve, 50));\r\n}\r\n","import { Text } from \"ink\";\r\nimport InkSpinner from \"ink-spinner\";\r\n\r\ninterface SpinnerProps {\r\n type?: \"dots\" | \"line\" | \"bouncingBar\" | \"aesthetic\";\r\n label?: string;\r\n}\r\n\r\nexport function Spinner({ type = \"dots\", label }: SpinnerProps) {\r\n return (\r\n <Text>\r\n <Text color=\"cyan\">\r\n <InkSpinner type={type} />\r\n </Text>\r\n {label ? <Text> {label}</Text> : null}\r\n </Text>\r\n );\r\n}\r\n","import { Box, Text } from \"ink\";\r\n\r\ntype MessageType = \"info\" | \"success\" | \"warning\" | \"error\";\r\n\r\ninterface StatusMessageProps {\r\n type?: MessageType;\r\n label: string;\r\n detail?: string;\r\n}\r\n\r\nconst STYLES: Record<MessageType, { color: string; icon: string }> = {\r\n info: { color: \"cyan\", icon: \"ℹ\" },\r\n success: { color: \"green\", icon: \"✔\" },\r\n warning: { color: \"yellow\", icon: \"⚠\" },\r\n error: { color: \"red\", icon: \"✖\" },\r\n};\r\n\r\nexport function StatusMessage({ type = \"info\", label, detail }: StatusMessageProps) {\r\n const { color, icon } = STYLES[type];\r\n return (\r\n <Box>\r\n <Text color={color}>\r\n {icon} {label}\r\n </Text>\r\n {detail ? <Text dimColor>: {detail}</Text> : null}\r\n </Box>\r\n );\r\n}\r\n","import { Box, Text } from \"ink\";\r\nimport { useEffect, useState } from \"react\";\r\n\r\n/** 赛博朋克调色板 — 多处组件复用 */\r\nexport const CYBER_PALETTE = [\"#00ffff\", \"#ff00ff\", \"#00ff41\", \"#ff1493\", \"#8b00ff\"] as const;\r\n\r\n/** ASCII LOGO 行 — 多处组件复用 */\r\nexport const LOGO_LINES = [\r\n \" ██████╗ ███████╗██╗ ██╗\",\r\n \" ██╔══██╗██╔════╝██║ ██╔╝\",\r\n \" ██║ ██║███████╗█████╔╝ \",\r\n \" ██║ ██║╚════██║██╔═██╗ \",\r\n \" ██████╔╝███████║██║ ██╗\",\r\n \" ╚═════╝ ╚══════╝╚═╝ ╚═╝\",\r\n] as const;\r\n\r\nexport function DskcodeSplash() {\r\n const [offset, setOffset] = useState(0);\r\n\r\n useEffect(() => {\r\n const timer = setInterval(() => {\r\n setOffset((prev) => (prev + 1) % CYBER_PALETTE.length);\r\n }, 500);\r\n return () => clearInterval(timer);\r\n }, []);\r\n\r\n return (\r\n <Box flexDirection=\"column\" paddingLeft={1}>\r\n {LOGO_LINES.map((line, i) => {\r\n const colorIndex = (i + offset) % CYBER_PALETTE.length;\r\n return (\r\n <Box key={i}>\r\n <Text bold color={CYBER_PALETTE[colorIndex]}>\r\n {line}\r\n </Text>\r\n </Box>\r\n );\r\n })}\r\n </Box>\r\n );\r\n}\r\n","// ---------------------------------------------------------------------------\r\n// 交互式聊天会话组件 — 接入 Agent 主循环,实现流式对话\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { Box, Text, useInput, Static } from \"ink\";\r\nimport TextInput from \"ink-text-input\";\r\nimport { useEffect, useState, useCallback, useRef } from \"react\";\r\nimport { useDoubleCtrlC } from \"./useDoubleCtrlC.js\";\r\nimport { CYBER_PALETTE, LOGO_LINES } from \"./DskcodeSplash.js\";\r\nimport { Spinner } from \"./Spinner.js\";\r\nimport { AssistantMessage } from \"./AssistantMessage.js\";\r\nimport { DiffPreview } from \"./DiffPreview.js\";\r\nimport { SkillSelector } from \"./SkillSelector.js\";\r\nimport { FileSelector } from \"./FileSelector.js\";\r\nimport type { SkillInfo } from \"../cli/skill-import.js\";\r\nimport { CostTracker } from \"../provider/cost-tracker.js\";\r\nimport type { ProviderToolCall, UsageInfo } from \"../provider/index.js\";\r\nimport type { FileDiff } from \"../tool/types.js\";\r\nimport { createProvider } from \"../provider/index.js\";\r\nimport { Session } from \"../agent/index.js\";\r\nimport type { AgentEvent } from \"../agent/types.js\";\r\nimport { builtinTools } from \"../tool/index.js\";\r\nimport { ToolRegistry } from \"../tool/registry.js\";\r\nimport {\r\n IDLE_GRADIENT_STOPS,\r\n STREAMING_GRADIENT_STOPS,\r\n CMD_TIP_GRADIENT_STOPS,\r\n GRADIENT_ANIMATION,\r\n getGradientColors,\r\n} from \"../utils/gradient.js\";\r\nimport { SUPPORTED_MODELS } from \"../provider/models.js\";\r\nimport { saveModelConfig } from \"../config/loader.js\";\r\n\r\n/** 命令处理结果的类型,支持文本响应和动作跳转 */\r\nexport type CommandAction =\r\n | { kind: \"text\"; content: string }\r\n | { kind: \"exit\" }\r\n | { kind: \"clear\" }\r\n | { kind: \"navigate\"; target: \"game\" | \"stock\" };\r\n\r\nexport interface ChatCommand {\r\n desc: string;\r\n handler: () => CommandAction;\r\n}\r\n\r\n/** 命令注册表,支持动态注册新命令 */\r\nconst commandRegistry = new Map<string, ChatCommand>();\r\n\r\n/** 注册一个命令 */\r\nexport function registerCommand(name: string, cmd: ChatCommand): void {\r\n commandRegistry.set(name, cmd);\r\n}\r\n\r\n/** 获取所有已注册命令(用于 /help 生成帮助文本) */\r\nfunction getRegisteredCommands(): Map<string, ChatCommand> {\r\n return commandRegistry;\r\n}\r\n\r\n// 注册内置命令\r\nregisterCommand(\"/exit\", { desc: \"退出对话\", handler: () => ({ kind: \"exit\" }) });\r\nregisterCommand(\"/quit\", { desc: \"退出对话\", handler: () => ({ kind: \"exit\" }) });\r\nregisterCommand(\"/help\", {\r\n desc: \"显示帮助信息\",\r\n handler: () => {\r\n const commands = getRegisteredCommands();\r\n const lines = [\"可用命令:\"];\r\n for (const [name, cmd] of commands) {\r\n lines.push(` ${name.padEnd(16)}${cmd.desc}`);\r\n }\r\n return { kind: \"text\", content: lines.join(\"\\n\") };\r\n },\r\n});\r\nregisterCommand(\"/clear\", { desc: \"清空对话历史\", handler: () => ({ kind: \"clear\" }) });\r\nregisterCommand(\"/version\", { desc: \"显示版本信息\", handler: () => ({ kind: \"text\", content: \"dskcode v0.1.10\" }) });\r\nregisterCommand(\"/model\", { desc: \"切换模型\", handler: () => ({ kind: \"text\", content: \"请直接输入 /model 进入选择界面\" }) });\r\nregisterCommand(\"/game\", { desc: \"启动游戏\", handler: () => ({ kind: \"navigate\", target: \"game\" }) });\r\nregisterCommand(\"/stock\", { desc: \"查看股票行情\", handler: () => ({ kind: \"navigate\", target: \"stock\" }) });\r\n\r\n/** 流式输出时,输入框随机展示的占位符列表 */\r\nconst STREAMING_PLACEHOLDERS = [\r\n \"让子弹飞一会儿...\",\r\n \"马上就好...\",\r\n \"正在憋大招...\",\r\n \"稍等一下下~\",\r\n \"码字中...\",\r\n \"脑子转得飞快...\",\r\n];\r\n\r\n/** 空闲时输入框随机展示的占位符列表 */\r\nconst IDLE_PLACEHOLDERS = [\r\n \"想干啥?直接说~\",\r\n \"来吧,吩咐点啥\",\r\n \"随时待命...\",\r\n \"戳这里开聊...\",\r\n \"等你开口...\",\r\n \"尽管使唤~\",\r\n];\r\n\r\nfunction pickRandom<T>(arr: readonly T[]): T {\r\n return arr[Math.floor(Math.random() * arr.length)]!;\r\n}\r\n\r\n/** 单条已完成的助手消息 */\r\ninterface CompletedAssistant {\r\n content: string;\r\n toolCalls?: ProviderToolCall[];\r\n usage?: UsageInfo;\r\n elapsed?: number;\r\n cost?: number;\r\n model?: string;\r\n}\r\n\r\n/** 单条显示消息 */\r\ninterface DisplayMessage {\r\n role: \"user\" | \"assistant\" | \"tool\";\r\n content: string;\r\n /** 已完成的助手消息详情(仅在 role=assistant 时) */\r\n assistantDetail?: CompletedAssistant;\r\n /** 文件变更 diff(仅在 role=tool 且有 diff 时) */\r\n diff?: FileDiff;\r\n}\r\n\r\ninterface ChatSessionProps {\r\n skillCount: number;\r\n /** 可用 skill 详情列表(用于 / 输入时展示) */\r\n skills?: SkillInfo[];\r\n /** 项目源码文件路径列表(用于 @ 输入时展示) */\r\n files?: string[];\r\n toolCount: number;\r\n verbose: boolean;\r\n apiKey?: string;\r\n baseUrl?: string;\r\n costTracker?: CostTracker;\r\n model?: string;\r\n onLaunchGame?: () => void;\r\n onLaunchStock?: () => void;\r\n}\r\n\r\nexport function ChatSession({\r\n skillCount,\r\n skills = [],\r\n files = [],\r\n toolCount,\r\n verbose,\r\n apiKey,\r\n baseUrl,\r\n costTracker: externalCostTracker,\r\n model = \"deepseek-v4-flash\",\r\n onLaunchGame,\r\n onLaunchStock,\r\n}: ChatSessionProps) {\r\n const termWidth = typeof process.stdout.columns === \"number\" ? process.stdout.columns : 80;\r\n const dividerWidth = Math.max(termWidth - 2, 1);\r\n\r\n const [offset, setOffset] = useState(0);\r\n const [displayMessages, setDisplayMessages] = useState<DisplayMessage[]>([]);\r\n const [staticKey, setStaticKey] = useState(0);\r\n const [input, setInput] = useState(\"\");\r\n const [balance, setBalance] = useState<number | null>(null);\r\n const [balanceLoading, setBalanceLoading] = useState(false);\r\n const [todayCost, setTodayCost] = useState<number | null>(null);\r\n\r\n // 流式状态\r\n const [isStreaming, setIsStreaming] = useState(false);\r\n const [streamingPlaceholder, setStreamingPlaceholder] = useState(\"\");\r\n const [idlePlaceholder, setIdlePlaceholder] = useState(() => pickRandom(IDLE_PLACEHOLDERS));\r\n const [gradientColors, setGradientColors] = useState<string[]>([]);\r\n const gradientPhaseRef = useRef(0);\r\n const [streamingGradientColors, setStreamingGradientColors] = useState<string[]>([]);\r\n const streamingPhaseRef = useRef(0);\r\n const [currentContent, setCurrentContent] = useState(\"\");\r\n const [currentToolCalls, setCurrentToolCalls] = useState<ProviderToolCall[]>([]);\r\n const [currentUsage, setCurrentUsage] = useState<UsageInfo | undefined>(undefined);\r\n const [currentElapsed, setCurrentElapsed] = useState<number | undefined>(undefined);\r\n const [currentCost, setCurrentCost] = useState<number | undefined>(undefined);\r\n const [activeModel, setActiveModel] = useState<ModelId>(model as ModelId);\r\n const [streamingModel, setStreamingModel] = useState<string | undefined>(undefined);\r\n const [streamError, setStreamError] = useState<string | undefined>(undefined);\r\n\r\n // 命令提示轮播索引\r\n const cmdTips = Array.from(getRegisteredCommands())\r\n .filter(([name]) => name !== \"/exit\" && name !== \"/quit\")\r\n .map(([name, cmd]) => ({ name, desc: cmd.desc }));\r\n const [cmdTipIndex, setCmdTipIndex] = useState(0);\r\n const [cmdTipGradientColors, setCmdTipGradientColors] = useState<string[]>([]);\r\n const cmdTipPhaseRef = useRef(0);\r\n\r\n // skill 选择索引(用于上下键导航)\r\n const [skillSelectIndex, setSkillSelectIndex] = useState(0);\r\n // 文件选择索引\r\n const [fileSelectIndex, setFileSelectIndex] = useState(0);\r\n // 输入框 key(补全时递增,强制 TextInput 重挂载以重置光标到末尾)\r\n const [inputKey, setInputKey] = useState(0);\r\n\r\n // 模型选择模式\r\n const [selectingModel, setSelectingModel] = useState(false);\r\n const [modelSelectIndex, setModelSelectIndex] = useState(0);\r\n const modelOptions: ModelId[] = [\"deepseek-v4-flash\", \"deepseek-v4-pro\"];\r\n\r\n // Session 引用(保持跨渲染稳定)\r\n const sessionRef = useRef<Session | null>(null);\r\n const abortRef = useRef<AbortController | null>(null);\r\n\r\n // 用 ref 跟踪流式内容的最新值,以便 finally 块能获取非闭包过期的值\r\n const currentContentRef = useRef(\"\");\r\n const currentToolCallsRef = useRef<ProviderToolCall[]>([]);\r\n const currentUsageRef = useRef<UsageInfo | undefined>(undefined);\r\n const currentElapsedRef = useRef<number | undefined>(undefined);\r\n const currentCostRef = useRef<number | undefined>(undefined);\r\n const currentModelRef = useRef<string | undefined>(undefined);\r\n const streamErrorRef = useRef<string | undefined>(undefined);\r\n\r\n // 输入变更时重置选择索引\r\n useEffect(() => {\r\n setSkillSelectIndex(0);\r\n setFileSelectIndex(0);\r\n }, [input]);\r\n\r\n // 获取当前输入匹配的 skill 列表\r\n const getFilteredSkills = useCallback(\r\n (value: string) => {\r\n const match = value.match(/(?:^|\\s)\\/([^/]*)$/);\r\n if (!match) return [];\r\n const q = match[1]!.toLowerCase().trim();\r\n if (!q) {\r\n if (value.startsWith(\"/\")) return skills.slice(0, 3);\r\n return [];\r\n }\r\n const matched = skills.filter((s) => s.name.toLowerCase().includes(q)).slice(0, 3);\r\n // 精确匹配时表示已补全完成,不显示列表\r\n if (matched.some((s) => s.name.toLowerCase() === q)) return [];\r\n return matched;\r\n },\r\n [skills],\r\n );\r\n\r\n // 获取当前输入匹配的文件列表\r\n const getFilteredFiles = useCallback(\r\n (value: string) => {\r\n const match = value.match(/(?:^|\\s)@([^@]*)$/);\r\n if (!match) return [];\r\n const q = match[1]!.toLowerCase().trim();\r\n // @ 在开头且无后续内容时,展示前 5 个文件作为提示\r\n if (!q) {\r\n if (value.startsWith(\"@\")) return files.slice(0, 5);\r\n return [];\r\n }\r\n const matched = files.filter((f) => f.toLowerCase().includes(q)).slice(0, 5);\r\n // 精确匹配时表示已补全完成,不显示列表\r\n if (matched.some((f) => f.toLowerCase() === q)) return [];\r\n return matched;\r\n },\r\n [files],\r\n );\r\n\r\n const { doubleCtrlC, handleCtrlC } = useDoubleCtrlC(() => {\r\n // 双击 Ctrl+C 退出进程\r\n process.exit(0);\r\n });\r\n\r\n // 捕获 Ctrl+C 和渐变占位符状态下的字符输入\r\n useInput(\r\n useCallback(\r\n (_input, key) => {\r\n // 模型选择模式\r\n if (selectingModel) {\r\n if (key.upArrow) {\r\n setModelSelectIndex((prev) => (prev - 1 + modelOptions.length) % modelOptions.length);\r\n } else if (key.downArrow) {\r\n setModelSelectIndex((prev) => (prev + 1) % modelOptions.length);\r\n } else if (key.return) {\r\n const selected = modelOptions[modelSelectIndex]!;\r\n if (selected === activeModel) {\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"assistant\", content: `已经在使用 ${SUPPORTED_MODELS[selected].displayName}` },\r\n ]);\r\n } else {\r\n // 切换模型:持久化到配置 + 重置 Session\r\n setActiveModel(selected);\r\n sessionRef.current?.reset();\r\n saveModelConfig(selected).catch(() => {\r\n // 持久化失败静默处理,不影响当前会话\r\n });\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"assistant\", content: `模型已切换为 ${SUPPORTED_MODELS[selected].displayName}(${selected})` },\r\n ]);\r\n }\r\n setSelectingModel(false);\r\n } else if (key.escape) {\r\n setSelectingModel(false);\r\n }\r\n return;\r\n }\r\n\r\n // 文件 @ 选择模式 / skill 选择模式\r\n // 优先 @(文件匹配),没有文件匹配再走 skill 匹配\r\n const fileList = getFilteredFiles(input);\r\n const skillList = fileList.length === 0 ? getFilteredSkills(input) : [];\r\n\r\n if (fileList.length > 0) {\r\n if (key.upArrow) {\r\n setFileSelectIndex((prev) => (prev - 1 + fileList.length) % fileList.length);\r\n return;\r\n }\r\n if (key.downArrow) {\r\n setFileSelectIndex((prev) => (prev + 1) % fileList.length);\r\n return;\r\n }\r\n // Tab 补全选中的文件\r\n if (key.tab) {\r\n const selected = fileList[fileSelectIndex];\r\n if (selected) {\r\n const atIdx = input.lastIndexOf(\"@\");\r\n if (atIdx >= 0) {\r\n setInput(input.slice(0, atIdx) + \"@\" + selected + \" \");\r\n setInputKey((k) => k + 1);\r\n }\r\n }\r\n return;\r\n }\r\n }\r\n\r\n if (skillList.length > 0) {\r\n if (key.upArrow) {\r\n setSkillSelectIndex((prev) => (prev - 1 + skillList.length) % skillList.length);\r\n return;\r\n }\r\n if (key.downArrow) {\r\n setSkillSelectIndex((prev) => (prev + 1) % skillList.length);\r\n return;\r\n }\r\n // Tab 补全选中的 skill(补全后光标自动移至末尾)\r\n if (key.tab) {\r\n const selected = skillList[skillSelectIndex];\r\n if (selected) {\r\n // 只替换最后一个 / 之后的部分,保留之前输入的内容\r\n // 用 lastIndexOf 定位 / 的精确位置,确保前置空格不被吃掉\r\n const slashIdx = input.lastIndexOf(\"/\");\r\n if (slashIdx >= 0) {\r\n // 补全后加空格分隔,方便继续输入下一个 skill 或问题\r\n setInput(input.slice(0, slashIdx) + \"/\" + selected.name + \" \");\r\n setInputKey((k) => k + 1);\r\n }\r\n }\r\n return;\r\n }\r\n }\r\n\r\n if (key.ctrl && _input === \"c\") {\r\n if (isStreaming) {\r\n // 流式输出中,取消当前请求\r\n abortRef.current?.abort();\r\n } else {\r\n handleCtrlC();\r\n }\r\n return;\r\n }\r\n\r\n // 渐变占位符显示时(TextInput 未渲染),将按键字符加入 input 触发切换\r\n if (!input && !isStreaming && _input) {\r\n setInput(_input);\r\n }\r\n },\r\n [selectingModel, modelSelectIndex, modelOptions, activeModel, isStreaming, handleCtrlC, input, skills, skillSelectIndex, fileSelectIndex, getFilteredSkills, getFilteredFiles]\r\n ),\r\n );\r\n\r\n // 命令提示轮播 — 每 5 秒切换下一条\r\n useEffect(() => {\r\n if (cmdTips.length <= 1) return;\r\n const timer = setInterval(() => {\r\n setCmdTipIndex((prev) => (prev + 1) % cmdTips.length);\r\n }, 5000);\r\n return () => clearInterval(timer);\r\n }, [cmdTips.length]);\r\n\r\n // 命令提示条渐变动画(反向流动:1 - phase)\r\n useEffect(() => {\r\n const tip = cmdTips[cmdTipIndex % cmdTips.length];\r\n if (!tip) {\r\n setCmdTipGradientColors([]);\r\n return;\r\n }\r\n const text = `${tip.name} ${tip.desc}`;\r\n cmdTipPhaseRef.current = 0;\r\n setCmdTipGradientColors(getGradientColors(text, 1, CMD_TIP_GRADIENT_STOPS));\r\n\r\n const interval = setInterval(() => {\r\n cmdTipPhaseRef.current = (cmdTipPhaseRef.current + GRADIENT_ANIMATION.cmdTipPhaseStep) % 1;\r\n // 反向 phase 使色彩从左到右流动\r\n setCmdTipGradientColors(getGradientColors(text, 1 - cmdTipPhaseRef.current, CMD_TIP_GRADIENT_STOPS));\r\n }, GRADIENT_ANIMATION.cmdTipInterval);\r\n\r\n return () => clearInterval(interval);\r\n }, [cmdTipIndex, cmdTips.length]);\r\n\r\n // Logo 色彩动画\r\n useEffect(() => {\r\n const timer = setInterval(() => {\r\n setOffset((prev) => (prev + 1) % CYBER_PALETTE.length);\r\n }, 500);\r\n return () => clearInterval(timer);\r\n }, []);\r\n\r\n // 初始化 Session(当模型切换时重建)\r\n useEffect(() => {\r\n if (!apiKey || !baseUrl) return;\r\n\r\n const provider = createProvider({\r\n name: \"deepseek\",\r\n apiKey,\r\n baseUrl,\r\n model: activeModel,\r\n });\r\n\r\n const tracker = externalCostTracker ?? new CostTracker();\r\n // 注册所有内置工具\r\n const toolRegistry = new ToolRegistry();\r\n toolRegistry.registerAll(builtinTools);\r\n const session = new Session(provider, toolRegistry, tracker, {\r\n cwd: process.cwd(),\r\n });\r\n sessionRef.current = session;\r\n\r\n return () => {\r\n sessionRef.current = null;\r\n };\r\n }, [apiKey, baseUrl, activeModel, externalCostTracker]);\r\n\r\n // 查询余额\r\n useEffect(() => {\r\n if (!apiKey || !baseUrl) return;\r\n let cancelled = false;\r\n setBalanceLoading(true);\r\n import(\"../provider/deepseek.js\").then(({ DeepSeekProvider }) => {\r\n const provider = new DeepSeekProvider({\r\n apiKey,\r\n baseUrl,\r\n model: \"deepseek-v4-flash\",\r\n });\r\n return provider.getBalance();\r\n }).then((result) => {\r\n if (cancelled) return;\r\n const cny = result.balances.find((b) => b.currency === \"CNY\");\r\n if (cny) {\r\n setBalance(cny.totalBalance);\r\n }\r\n }).catch(() => {\r\n // 查询失败静默处理,不影响主流程\r\n }).finally(() => {\r\n if (!cancelled) setBalanceLoading(false);\r\n });\r\n return () => { cancelled = true; };\r\n }, [apiKey, baseUrl]);\r\n\r\n // 加载今日消耗历史数据,并定时刷新\r\n useEffect(() => {\r\n if (!externalCostTracker) return;\r\n let cancelled = false;\r\n let timer: ReturnType<typeof setInterval> | undefined;\r\n\r\n const refresh = () => {\r\n setTodayCost(externalCostTracker.todayTotalCost);\r\n };\r\n\r\n externalCostTracker.load().then(() => {\r\n if (cancelled) return;\r\n refresh();\r\n timer = setInterval(refresh, 5000);\r\n }).catch(() => {\r\n // 加载失败静默处理\r\n });\r\n\r\n return () => {\r\n cancelled = true;\r\n if (timer) clearInterval(timer);\r\n };\r\n }, [externalCostTracker]);\r\n\r\n // 空闲占位符渐变动画\r\n useEffect(() => {\r\n if (isStreaming || !idlePlaceholder) {\r\n setGradientColors([]);\r\n return;\r\n }\r\n\r\n gradientPhaseRef.current = 0;\r\n setGradientColors(getGradientColors(idlePlaceholder, 1, IDLE_GRADIENT_STOPS));\r\n\r\n const interval = setInterval(() => {\r\n gradientPhaseRef.current = (gradientPhaseRef.current + GRADIENT_ANIMATION.idlePhaseStep) % 1;\r\n setGradientColors(getGradientColors(idlePlaceholder, 1 - gradientPhaseRef.current, IDLE_GRADIENT_STOPS));\r\n }, GRADIENT_ANIMATION.idleInterval);\r\n\r\n return () => clearInterval(interval);\r\n }, [isStreaming, idlePlaceholder]);\r\n\r\n // 流式占位符渐变动画\r\n useEffect(() => {\r\n if (!isStreaming || !streamingPlaceholder) {\r\n setStreamingGradientColors([]);\r\n return;\r\n }\r\n\r\n streamingPhaseRef.current = 0;\r\n setStreamingGradientColors(getGradientColors(streamingPlaceholder, 1, STREAMING_GRADIENT_STOPS));\r\n\r\n const interval = setInterval(() => {\r\n streamingPhaseRef.current = (streamingPhaseRef.current + GRADIENT_ANIMATION.streamingPhaseStep) % 1;\r\n setStreamingGradientColors(getGradientColors(streamingPlaceholder, 1 - streamingPhaseRef.current, STREAMING_GRADIENT_STOPS));\r\n }, GRADIENT_ANIMATION.streamingInterval);\r\n\r\n return () => clearInterval(interval);\r\n }, [isStreaming, streamingPlaceholder]);\r\n\r\n /** 处理用户输入 */\r\n const handleSubmit = useCallback(async (value: string) => {\r\n const trimmed = value.trim();\r\n if (!trimmed) return;\r\n\r\n // 处理斜杠命令\r\n if (trimmed.startsWith(\"/\") && trimmed.length > 1) {\r\n // /model 命令:进入模型选择模式\r\n if (trimmed.toLowerCase() === \"/model\") {\r\n // 默认高亮当前模型\r\n const curIdx = modelOptions.indexOf(activeModel);\r\n setModelSelectIndex(curIdx >= 0 ? curIdx : 0);\r\n setSelectingModel(true);\r\n setInput(\"\");\r\n return;\r\n }\r\n\r\n const cmd = commandRegistry.get(trimmed.toLowerCase());\r\n if (cmd) {\r\n const result = cmd.handler();\r\n\r\n switch (result.kind) {\r\n case \"exit\":\r\n process.exit(0);\r\n return;\r\n case \"clear\":\r\n // 清空所有会话状态,回到初始界面\r\n setDisplayMessages([]);\r\n setStaticKey((prev) => prev + 1);\r\n setInput(\"\");\r\n setStreamError(undefined);\r\n // 先清屏再清滚动区,确保终端完全重置\r\n process.stdout.write(\"\\x1b[2J\\x1b[H\\x1b[3J\");\r\n // 重置 Session 历史\r\n sessionRef.current?.reset();\r\n return;\r\n case \"navigate\":\r\n setInput(\"\");\r\n if (result.target === \"game\") {\r\n onLaunchGame?.();\r\n } else if (result.target === \"stock\") {\r\n onLaunchStock?.();\r\n }\r\n return;\r\n case \"text\":\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"user\", content: trimmed },\r\n { role: \"assistant\", content: result.content },\r\n ]);\r\n setInput(\"\");\r\n return;\r\n }\r\n }\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"user\", content: trimmed },\r\n { role: \"assistant\", content: `未知命令:${trimmed}。输入 /help 查看。` },\r\n ]);\r\n setInput(\"\");\r\n return;\r\n }\r\n\r\n // 检查 Session 是否就绪\r\n if (!sessionRef.current) {\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"user\", content: trimmed },\r\n { role: \"assistant\", content: \"⚠ 无法连接到 Provider。请检查 API Key 和网络配置。\" },\r\n ]);\r\n setInput(\"\");\r\n return;\r\n }\r\n\r\n // ---- 正常对话:接入 Agent 流式主循环 ----\r\n\r\n // 追加用户消息到显示列表\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"user\", content: trimmed },\r\n ]);\r\n\r\n // 进入流式状态\r\n setInput(\"\");\r\n // 重置流式状态\r\n setIsStreaming(true);\r\n setStreamingPlaceholder(pickRandom(STREAMING_PLACEHOLDERS));\r\n setCurrentContent(\"\");\r\n setCurrentToolCalls([]);\r\n setCurrentUsage(undefined);\r\n setCurrentElapsed(undefined);\r\n setCurrentCost(undefined);\r\n setStreamingModel(undefined);\r\n setStreamError(undefined);\r\n // 同步重置 ref\r\n currentContentRef.current = \"\";\r\n currentToolCallsRef.current = [];\r\n currentUsageRef.current = undefined;\r\n currentElapsedRef.current = undefined;\r\n currentCostRef.current = undefined;\r\n currentModelRef.current = undefined;\r\n streamErrorRef.current = undefined;\r\n\r\n const session = sessionRef.current;\r\n const abortController = new AbortController();\r\n abortRef.current = abortController;\r\n\r\n try {\r\n for await (const event of session!.chat(trimmed)) {\r\n // 如果请求被取消,跳过后续事件\r\n if (abortController.signal.aborted) break;\r\n\r\n switch (event.type) {\r\n case \"text_delta\":\r\n setCurrentContent((prev) => {\r\n const next = prev + event.content;\r\n currentContentRef.current = next;\r\n return next;\r\n });\r\n break;\r\n\r\n case \"tool_calls\":\r\n setCurrentToolCalls((prev) => {\r\n const next = [...prev, ...event.calls];\r\n currentToolCallsRef.current = next;\r\n return next;\r\n });\r\n break;\r\n\r\n case \"tool_result\":\r\n // 工具执行完成 — 重置流式状态,准备接收模型的新一轮回复\r\n // 这是因为 Agent 循环会在工具执行后再次调用模型\r\n setCurrentContent(\"\");\r\n currentContentRef.current = \"\";\r\n setCurrentToolCalls([]);\r\n currentToolCallsRef.current = [];\r\n // 将工具结果追加为一条用户可见的消息\r\n // 优先使用工具提供的 summary(一行简短摘要),避免在 UI 中撑出大段文件内容\r\n const r = event.result;\r\n const line = r.success\r\n ? r.summary ?? `✅ ${event.name}: ${r.data.slice(0, 500)}${r.data.length > 500 ? \"...\" : \"\"}`\r\n : `❌ ${event.name}: ${r.error ?? \"执行失败\"}`;\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n {\r\n role: \"tool\" as const,\r\n content: line,\r\n diff: r.diff,\r\n },\r\n ]);\r\n break;\r\n\r\n case \"usage\":\r\n setCurrentUsage(event.usage);\r\n setStreamingModel(event.model);\r\n currentUsageRef.current = event.usage;\r\n currentModelRef.current = event.model;\r\n // 使用量来自最后一个事件,费率已知后可同步计算\r\n break;\r\n\r\n case \"done\":\r\n setCurrentElapsed(event.elapsed);\r\n currentElapsedRef.current = event.elapsed;\r\n break;\r\n\r\n case \"error\":\r\n setStreamError(event.error.message);\r\n streamErrorRef.current = event.error.message;\r\n break;\r\n }\r\n }\r\n } catch (err: unknown) {\r\n const msg = err instanceof Error ? err.message : String(err);\r\n setStreamError(msg);\r\n streamErrorRef.current = msg;\r\n } finally {\r\n setIsStreaming(false);\r\n setIdlePlaceholder(pickRandom(IDLE_PLACEHOLDERS));\r\n abortRef.current = null;\r\n\r\n // 流式结束后,用 ref 拿到最新值,直接追加完成的助手消息\r\n const finContent = currentContentRef.current;\r\n const finToolCalls = currentToolCallsRef.current.length > 0 ? currentToolCallsRef.current : undefined;\r\n const finStreamError = streamErrorRef.current;\r\n\r\n if (finContent || finToolCalls || finStreamError) {\r\n const completed: CompletedAssistant = {\r\n content: finStreamError ? `⚠ 请求出错:${finStreamError}` : (finContent || \"\"),\r\n toolCalls: finToolCalls,\r\n usage: currentUsageRef.current,\r\n elapsed: currentElapsedRef.current,\r\n cost: currentCostRef.current,\r\n model: currentModelRef.current,\r\n };\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n {\r\n role: \"assistant\" as const,\r\n content: completed.content,\r\n assistantDetail: completed,\r\n },\r\n ]);\r\n }\r\n }\r\n }, [onLaunchGame, onLaunchStock, currentContent, currentToolCalls, skills, skillSelectIndex, getFilteredSkills]);\r\n\r\n // 从 costTracker 更新今日消耗(每次流式结束后刷新)\r\n useEffect(() => {\r\n if (!isStreaming && externalCostTracker) {\r\n setTodayCost(externalCostTracker.todayTotalCost);\r\n }\r\n }, [isStreaming, externalCostTracker]);\r\n\r\n // 从 usage 计算 cost(需要在 usage + model 都就绪后)\r\n useEffect(() => {\r\n if (currentUsage && streamingModel && !currentCost) {\r\n import(\"../provider/models.js\").then(({ calculateCost }) => {\r\n const cost = calculateCost(currentUsage, streamingModel as ModelId);\r\n setCurrentCost(cost.totalCost);\r\n currentCostRef.current = cost.totalCost;\r\n });\r\n }\r\n }, [currentUsage, streamingModel, currentCost]);\r\n\r\n return (\r\n <Box flexDirection=\"column\" paddingLeft={1} paddingRight={1}>\r\n {/* Logo + 状态栏 + 余额 — 三栏布局 */}\r\n <Box flexDirection=\"row\" marginBottom={1}>\r\n {/* Logo */}\r\n <Box flexDirection=\"column\" marginRight={4}>\r\n {LOGO_LINES.map((line, i) => {\r\n const colorIndex = (i + offset) % CYBER_PALETTE.length;\r\n return (\r\n <Box key={i}>\r\n <Text bold color={CYBER_PALETTE[colorIndex]}>\r\n {line}\r\n </Text>\r\n </Box>\r\n );\r\n })}\r\n </Box>\r\n\r\n {/* 状态信息 */}\r\n <Box flexDirection=\"column\" justifyContent=\"center\">\r\n <Text color=\"#00ff41\">{\" ✔ \"}已就绪 {skillCount} 个 Skill</Text>\r\n <Text color=\"#00ffff\">{\" ℹ \"}已就绪 {toolCount} 个工具</Text>\r\n <Text color=\"#00ffff\">{\" 🔧 模型 \"}{SUPPORTED_MODELS[activeModel]?.displayName ?? activeModel}</Text>\r\n {/* 命令提示轮播 — 每 5 秒切换下一条,渐变动画 */}\r\n {cmdTips.length > 0 && (() => {\r\n const tip = cmdTips[cmdTipIndex % cmdTips.length];\r\n if (!tip) return null;\r\n const text = `${tip.name} ${tip.desc}`;\r\n return (\r\n <Text>\r\n <Text color=\"#808080\">{\" 💡 \"}</Text>\r\n {cmdTipGradientColors.length > 0\r\n ? text.split(\"\").map((ch, i) => (\r\n <Text key={i} color={cmdTipGradientColors[i] || undefined}>{ch}</Text>\r\n ))\r\n : <Text color=\"#808080\">{text}</Text>}\r\n </Text>\r\n );\r\n })()}\r\n {verbose ? <Text color=\"#ff1493\">{\" ⚡ Verbose\"}</Text> : null}\r\n </Box>\r\n\r\n {/* 右侧余额 + 今日消耗 */}\r\n <Box flexGrow={1} flexDirection=\"column\" justifyContent=\"center\" alignItems=\"flex-end\">\r\n {balanceLoading && balance === null ? (\r\n <Text color=\"yellow\">{\" ⏳ 查询余额...\"}</Text>\r\n ) : balance !== null ? (\r\n <Box flexDirection=\"row\">\r\n <Text color=\"yellow\">{\"💰 \"}</Text>\r\n <Text color=\"yellow\">{\"余额 ¥\"}{balance.toFixed(2)}</Text>\r\n </Box>\r\n ) : null}\r\n {todayCost !== null ? (\r\n <Box flexDirection=\"row\">\r\n <Text color=\"cyan\">{\"📊 \"}</Text>\r\n <Text color=\"cyan\">{\"今日 ¥\"}{todayCost.toFixed(2)}</Text>\r\n </Box>\r\n ) : null}\r\n </Box>\r\n </Box>\r\n\r\n {/* 消息列表 - 已完成的消息用 Static 固定,避免重绘时丢失滚动位置 */}\r\n <Box flexDirection=\"column\" marginTop={1}>\r\n <Static key={staticKey} items={displayMessages}>\r\n {(msg, i) => {\r\n if (msg.role === \"user\") {\r\n return (\r\n <Box key={i} marginTop={1}>\r\n <Box width={4} flexShrink={0}>\r\n <Text bold color=\"#00ff41\">{\"👤\"}</Text>\r\n </Box>\r\n <Box flexGrow={1}>\r\n <Text wrap=\"wrap\">{msg.content}</Text>\r\n </Box>\r\n </Box>\r\n );\r\n }\r\n\r\n // 工具消息 — 显示文本内容 + Diff 预览\r\n if (msg.role === \"tool\") {\r\n return (\r\n <Box key={i} marginTop={1} flexDirection=\"column\">\r\n <Box flexDirection=\"row\">\r\n <Box width={4} flexShrink={0}>\r\n <Text bold color=\"#f59e0b\">{\"🔧\"}</Text>\r\n </Box>\r\n <Box flexGrow={1}>\r\n <Text wrap=\"wrap\">{msg.content}</Text>\r\n </Box>\r\n </Box>\r\n {msg.diff && <DiffPreview diff={msg.diff} />}\r\n </Box>\r\n );\r\n }\r\n\r\n // 已完成的助手消息\r\n const detail = msg.assistantDetail;\r\n return (\r\n <AssistantMessage\r\n key={i}\r\n content={msg.content}\r\n toolCalls={detail?.toolCalls}\r\n isStreaming={false}\r\n usage={detail?.usage}\r\n elapsed={detail?.elapsed}\r\n cost={detail?.cost}\r\n model={detail?.model}\r\n />\r\n );\r\n }}\r\n </Static>\r\n\r\n {/* 正在流式输出的助手消息 */}\r\n {isStreaming && (\r\n <AssistantMessage\r\n content={currentContent}\r\n toolCalls={currentToolCalls.length > 0 ? currentToolCalls : undefined}\r\n isStreaming={true}\r\n />\r\n )}\r\n\r\n {/* 思考中 Spinner */}\r\n {isStreaming && !currentContent && currentToolCalls.length === 0 && (\r\n <Box marginTop={1} marginLeft={4}>\r\n <Spinner type=\"dots\" label=\"思考中...\" />\r\n </Box>\r\n )}\r\n\r\n {/* 错误信息(流式结束后显示) */}\r\n {!isStreaming && streamError && (\r\n <Box marginTop={1} marginLeft={3}>\r\n <Text color=\"red\">⚠ {streamError}</Text>\r\n </Box>\r\n )}\r\n </Box>\r\n\r\n {/* 输入区 */}\r\n {selectingModel ? (\r\n <Box marginTop={1} flexDirection=\"column\">\r\n <Text color=\"#00ffff\" dimColor>{\"─\".repeat(dividerWidth)}</Text>\r\n <Box flexDirection=\"column\" marginTop={1}>\r\n <Text bold color=\"#00ffff\">选择模型:</Text>\r\n {modelOptions.map((id, i) => {\r\n const meta = SUPPORTED_MODELS[id];\r\n const isCurrent = id === activeModel;\r\n const isSelected = i === modelSelectIndex;\r\n const marker = isSelected ? \" > \" : \" \";\r\n const suffix = isCurrent ? \" (当前)\" : \"\";\r\n return (\r\n <Box key={id}>\r\n <Text\r\n color={isSelected ? \"#00ff41\" : undefined}\r\n bold={isSelected}\r\n >\r\n {marker}{meta.displayName}{suffix}\r\n </Text>\r\n {isSelected && <Text color=\"#808080\"> — {id}</Text>}\r\n </Box>\r\n );\r\n })}\r\n <Box marginTop={1}>\r\n <Text color=\"#808080\" dimColor>↑↓ 选择 · Enter 确认 · Esc 取消</Text>\r\n </Box>\r\n </Box>\r\n <Text color=\"#00ffff\" dimColor>{\"─\".repeat(dividerWidth)}</Text>\r\n </Box>\r\n ) : (\r\n <>\r\n <Box marginTop={1}>\r\n <Text color=\"#00ffff\" dimColor>{\"─\".repeat(dividerWidth)}</Text>\r\n </Box>\r\n <Box>\r\n <Box width={4} flexShrink={0}>\r\n <Text bold color=\"#00ff41\">{\"⚡\"}</Text>\r\n </Box>\r\n <Box flexGrow={1}>\r\n {!input && !isStreaming && idlePlaceholder && gradientColors.length > 0 ? (\r\n <Text>\r\n {idlePlaceholder.split(\"\").map((ch, i) => (\r\n <Text key={i} color={gradientColors[i] ?? undefined}>{ch}</Text>\r\n ))}\r\n </Text>\r\n ) : !input && isStreaming && streamingPlaceholder && streamingGradientColors.length > 0 ? (\r\n <Text>\r\n {streamingPlaceholder.split(\"\").map((ch, i) => (\r\n <Text key={i} color={streamingGradientColors[i] ?? undefined}>{ch}</Text>\r\n ))}\r\n </Text>\r\n ) : (\r\n <TextInput\r\n key={inputKey}\r\n value={input}\r\n onChange={setInput}\r\n onSubmit={handleSubmit}\r\n placeholder=\"\"\r\n />\r\n )}\r\n </Box>\r\n </Box>\r\n <Box>\r\n <Text color=\"#00ffff\" dimColor>{\"─\".repeat(dividerWidth)}</Text>\r\n </Box>\r\n\r\n {/* 用户输入 / 时显示 skill 列表 */}\r\n <SkillSelector skills={skills} input={input} selectedIndex={skillSelectIndex} />\r\n {/* 用户输入 @ 时显示文件列表 */}\r\n <FileSelector files={files} input={input} selectedIndex={fileSelectIndex} />\r\n </>\r\n )}\r\n\r\n {/* 双击 Ctrl+C 退出提示 */}\r\n {doubleCtrlC && !isStreaming && (\r\n <Box marginTop={1}>\r\n <Text color=\"#ff1493\" bold>\r\n {\" ⚠ 再按一次 Ctrl+C 退出 dskcode\"}\r\n </Text>\r\n </Box>\r\n )}\r\n\r\n {/* 流式中 Ctrl+C 取消提示 */}\r\n {isStreaming && (\r\n <Box marginTop={1}>\r\n <Text color=\"yellow\" dimColor>\r\n {\" 提示:按 Ctrl+C 取消当前请求\"}\r\n </Text>\r\n </Box>\r\n )}\r\n </Box>\r\n );\r\n}\r\n","import { useState, useCallback, useEffect, useRef } from \"react\";\r\n\r\nconst CTRL_C_TIMEOUT_MS = 1500;\r\n\r\n/**\r\n * 提供\"双击 Ctrl+C 才退出\"的交互逻辑。\r\n *\r\n * - 第一次 Ctrl+C:设置提示状态,1.5 秒后自动重置\r\n * - 第二次 Ctrl+C(1.5 秒内):执行 onExit 回调\r\n *\r\n * 返回 { doubleCtrlC, handleCtrlC },组件通过 useInput 捕获\r\n * Ctrl+C 后调用 handleCtrlC,并根据 doubleCtrlC 显示提示。\r\n */\r\nexport function useDoubleCtrlC(onExit: () => void) {\r\n const [doubleCtrlC, setDoubleCtrlC] = useState(false);\r\n const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\r\n const onExitRef = useRef(onExit);\r\n onExitRef.current = onExit;\r\n\r\n // 组件卸载时清理定时器\r\n useEffect(() => {\r\n return () => {\r\n if (timerRef.current) clearTimeout(timerRef.current);\r\n };\r\n }, []);\r\n\r\n const handleCtrlC = useCallback(() => {\r\n if (doubleCtrlC) {\r\n // 第二次 Ctrl+C → 退出\r\n onExitRef.current();\r\n return;\r\n }\r\n\r\n // 第一次 Ctrl+C → 显示提示,启动倒计时重置\r\n setDoubleCtrlC(true);\r\n if (timerRef.current) clearTimeout(timerRef.current);\r\n timerRef.current = setTimeout(() => {\r\n setDoubleCtrlC(false);\r\n timerRef.current = null;\r\n }, CTRL_C_TIMEOUT_MS);\r\n }, [doubleCtrlC]);\r\n\r\n return { doubleCtrlC, handleCtrlC };\r\n}","// ---------------------------------------------------------------------------\r\n// 助手消息组件 — 渲染单条 assistant 回复(流式文本 + 工具调用 + 成本行)\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { Box, Text } from \"ink\";\r\nimport type { ProviderToolCall, UsageInfo } from \"../provider/index.js\";\r\nimport { formatMoney, formatCallCostLine } from \"../provider/cost-tracker.js\";\r\nimport { ToolCallBlock } from \"./ToolCallBlock.js\";\r\nimport { formatUsageSummary } from \"../agent/message-builder.js\";\r\n\r\ninterface AssistantMessageProps {\r\n /** 助手回复的文本内容 */\r\n content: string;\r\n /** 工具调用列表 */\r\n toolCalls?: ProviderToolCall[];\r\n /** 是否正在流式输出中 */\r\n isStreaming?: boolean;\r\n /** Token 使用统计 */\r\n usage?: UsageInfo;\r\n /** 本次调用耗时(毫秒) */\r\n elapsed?: number;\r\n /** 本次调用费用 */\r\n cost?: number;\r\n /** 使用的模型标识 */\r\n model?: string;\r\n}\r\n\r\n/** 格式化毫秒为人类可读的时间 */\r\nfunction formatElapsed(ms: number): string {\r\n if (ms < 1000) return `${ms}ms`;\r\n const seconds = (ms / 1000).toFixed(1);\r\n return `${seconds}s`;\r\n}\r\n\r\n/**\r\n * 渲染单条助手回复。\r\n *\r\n * 显示结构:\r\n * 🤖 流式文本内容...\r\n * 📦 tool_name ────\r\n * 参数摘要\r\n * ⏳ 等待执行\r\n * ────────────────────────────\r\n * 💰 本次 ¥0.0012 │ 🕐 3.2s │ 📦 1.2k tokens\r\n */\r\nexport function AssistantMessage({\r\n content,\r\n toolCalls,\r\n isStreaming = false,\r\n usage,\r\n elapsed,\r\n cost,\r\n model,\r\n}: AssistantMessageProps) {\r\n // 内容为空且无工具调用时不渲染\r\n if (!content && (!toolCalls || toolCalls.length === 0) && !isStreaming) {\r\n return null;\r\n }\r\n\r\n return (\r\n <Box flexDirection=\"column\" marginTop={1}>\r\n {/* 助手标识 + 内容 */}\r\n <Box flexDirection=\"row\">\r\n <Box width={4} flexShrink={0}>\r\n <Text bold color=\"#ff00ff\">{\"🤖\"}</Text>\r\n </Box>\r\n <Box flexGrow={1} flexDirection=\"column\">\r\n {/* 文本内容 */}\r\n {content && (\r\n <Text wrap=\"wrap\">{content}</Text>\r\n )}\r\n {/* 流式输出时的光标 */}\r\n {isStreaming && !content && (\r\n <Text color=\"#888888\">...</Text>\r\n )}\r\n </Box>\r\n </Box>\r\n\r\n {/* 工具调用 */}\r\n {toolCalls && toolCalls.length > 0 && (\r\n <Box flexDirection=\"column\">\r\n {toolCalls.map((tc, i) => (\r\n <ToolCallBlock key={tc.id ?? i} call={tc} />\r\n ))}\r\n </Box>\r\n )}\r\n\r\n {/* 成本/耗时摘要行(仅在流式结束后显示) */}\r\n {!isStreaming && (usage || elapsed !== undefined) && (\r\n <Box flexDirection=\"column\" marginTop={1} marginLeft={3}>\r\n <Text color=\"#555555\">{\"─\".repeat(36)}</Text>\r\n <Box flexDirection=\"row\" gap={2}>\r\n {cost !== undefined && cost > 0 && (\r\n <Text color=\"yellow\">\r\n 💰 本次 {formatMoney(cost)}\r\n </Text>\r\n )}\r\n {elapsed !== undefined && (\r\n <Text color=\"cyan\">\r\n 🕐 {formatElapsed(elapsed)}\r\n </Text>\r\n )}\r\n {usage && (\r\n <Text color=\"#888888\">\r\n {formatUsageSummary(usage)}\r\n </Text>\r\n )}\r\n </Box>\r\n </Box>\r\n )}\r\n </Box>\r\n );\r\n}","// ---------------------------------------------------------------------------\r\n// 成本追踪器 — 会话级 / 日级 / 历史级三层成本统计与持久化\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { mkdir, readFile, writeFile } from \"node:fs/promises\";\r\nimport { join } from \"node:path\";\r\nimport type { UsageInfo, CostInfo, ModelId } from \"./types.js\";\r\nimport { calculateCost } from \"./models.js\";\r\n\r\n// ---------------------------------------------------------------------------\r\n// 类型定义\r\n// ---------------------------------------------------------------------------\r\n\r\n/** 单次 API 调用的成本记录 */\r\nexport interface CostRecord {\r\n /** 记录时间戳(ISO 8601) */\r\n timestamp: string;\r\n /** 使用的模型 */\r\n model: ModelId;\r\n /** Token 使用量 */\r\n usage: UsageInfo;\r\n /** 费用明细(单位:元) */\r\n cost: CostInfo;\r\n}\r\n\r\n/** 单个会话的成本汇总 */\r\nexport interface SessionCostSummary {\r\n /** 会话 ID */\r\n sessionId: string;\r\n /** 会话开始时间(ISO 8601) */\r\n startedAt: string;\r\n /** 总输入 token 数 */\r\n totalPromptTokens: number;\r\n /** 总输出 token 数 */\r\n totalCompletionTokens: number;\r\n /** 总缓存命中 token 数 */\r\n totalCachedTokens: number;\r\n /** 总费用(元) */\r\n totalCost: number;\r\n /** 每次调用的详细记录 */\r\n records: CostRecord[];\r\n}\r\n\r\n/** 一天的成本汇总 */\r\nexport interface DailyCostSummary {\r\n /** 日期(YYYY-MM-DD 格式) */\r\n date: string;\r\n /** 当日总输入 token 数 */\r\n totalPromptTokens: number;\r\n /** 当日总输出 token 数 */\r\n totalCompletionTokens: number;\r\n /** 当日总缓存命中 token 数 */\r\n totalCachedTokens: number;\r\n /** 当日总费用(元) */\r\n totalCost: number;\r\n /** 当日 API 调用次数 */\r\n totalCalls: number;\r\n /** 按模型分类的明细 */\r\n byModel: Record<string, ModelCostSummary>;\r\n}\r\n\r\n/** 按模型分类的成本汇总 */\r\nexport interface ModelCostSummary {\r\n /** 模型标识 */\r\n model: ModelId;\r\n /** 该模型的总输入 token 数 */\r\n totalPromptTokens: number;\r\n /** 该模型的总输出 token 数 */\r\n totalCompletionTokens: number;\r\n /** 该模型的总缓存命中 token 数 */\r\n totalCachedTokens: number;\r\n /** 该模型的总费用(元) */\r\n totalCost: number;\r\n /** 该模型的 API 调用次数 */\r\n totalCalls: number;\r\n}\r\n\r\n/** 持久化文件格式 */\r\nexport interface CostStore {\r\n /** 版本号,便于未来格式迁移 */\r\n version: 1;\r\n /** 按日期索引的汇总数据 */\r\n daily: Record<string, DailyCostSummary>;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// CostTracker 配置\r\n// ---------------------------------------------------------------------------\r\n\r\nexport interface CostTrackerOptions {\r\n /** 成本持久化目录,默认 ~/.dskcode/costs */\r\n costDir?: string;\r\n /** 预算上限(元),超过后触发回调,0 表示不限制 */\r\n budgetLimit?: number;\r\n /** Token 预算上限,超过后触发回调,0 表示不限制 */\r\n tokenBudgetLimit?: number;\r\n /** 预算超限回调 */\r\n onBudgetExceeded?: (tracker: CostTracker) => void;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// CostTracker 类\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * 成本追踪器 — 管理三层成本统计:\r\n *\r\n * 1. **会话级**(内存):当前会话内的累计 token 与费用\r\n * 2. **日级**(内存 + 持久化):按日聚合,支持\"今日消耗\"查询\r\n * 3. **历史级**(持久化):跨日的历史汇总查询\r\n *\r\n * 使用方式:\r\n * ```ts\r\n * const tracker = new CostTracker({ budgetLimit: 10 });\r\n * tracker.record(usage, 'deepseek-v4-flash');\r\n * console.log(tracker.todaySummary());\r\n * ```\r\n */\r\nexport class CostTracker {\r\n readonly #costDir: string;\r\n readonly #budgetLimit: number;\r\n readonly #tokenBudgetLimit: number;\r\n readonly #onBudgetExceeded?: (tracker: CostTracker) => void;\r\n\r\n // 会话级统计(内存)\r\n #sessionId: string;\r\n #sessionStartedAt: string;\r\n readonly #sessionRecords: CostRecord[] = [];\r\n\r\n // 日级统计(内存缓存,启动时从磁盘恢复)\r\n #todayDate: string;\r\n #todaySummary: DailyCostSummary;\r\n\r\n // 持久化标记\r\n #dirty = false;\r\n #flushInProgress = false;\r\n\r\n constructor(options: CostTrackerOptions = {}) {\r\n const home = process.env.HOME ?? process.env.USERPROFILE ?? \"~\";\r\n this.#costDir = options.costDir ?? join(home, \".dskcode\", \"costs\");\r\n this.#budgetLimit = options.budgetLimit ?? 0;\r\n this.#tokenBudgetLimit = options.tokenBudgetLimit ?? 0;\r\n this.#onBudgetExceeded = options.onBudgetExceeded;\r\n\r\n // 初始化会话\r\n this.#sessionId = generateSessionId();\r\n this.#sessionStartedAt = new Date().toISOString();\r\n\r\n // 初始化日级统计\r\n const today = getTodayStr();\r\n this.#todayDate = today;\r\n this.#todaySummary = createEmptyDailySummary(today);\r\n }\r\n\r\n // -----------------------------------------------------------------------\r\n // 核心方法\r\n // -----------------------------------------------------------------------\r\n\r\n /**\r\n * 记录一次 API 调用的 Token 使用量和成本。\r\n * 自动计算费用,累加到会话级和日级统计。\r\n */\r\n record(usage: UsageInfo, model: ModelId): CostInfo {\r\n const cost = calculateCost(usage, model);\r\n const record: CostRecord = {\r\n timestamp: new Date().toISOString(),\r\n model,\r\n usage: { ...usage },\r\n cost: { ...cost },\r\n };\r\n\r\n // 1. 会话级累加\r\n this.#sessionRecords.push(record);\r\n\r\n // 2. 日级累加(自动处理日期变更)\r\n this.#ensureTodayBucket();\r\n this.#addToDaily(record);\r\n\r\n // 3. 标记需要持久化\r\n this.#dirty = true;\r\n\r\n // 4. 预算检查\r\n this.#checkBudget();\r\n\r\n return cost;\r\n }\r\n\r\n // -----------------------------------------------------------------------\r\n // 会话级查询\r\n // -----------------------------------------------------------------------\r\n\r\n /** 当前会话 ID */\r\n get sessionId(): string {\r\n return this.#sessionId;\r\n }\r\n\r\n /** 当前会话的所有成本记录 */\r\n get records(): readonly CostRecord[] {\r\n return this.#sessionRecords;\r\n }\r\n\r\n /** 当前会话累计成本汇总 */\r\n get sessionSummary(): SessionCostSummary {\r\n let totalPromptTokens = 0;\r\n let totalCompletionTokens = 0;\r\n let totalCachedTokens = 0;\r\n let totalCost = 0;\r\n\r\n for (const r of this.#sessionRecords) {\r\n totalPromptTokens += r.usage.promptTokens;\r\n totalCompletionTokens += r.usage.completionTokens;\r\n totalCachedTokens += r.usage.cachedPromptTokens ?? 0;\r\n totalCost += r.cost.totalCost;\r\n }\r\n\r\n return {\r\n sessionId: this.#sessionId,\r\n startedAt: this.#sessionStartedAt,\r\n totalPromptTokens,\r\n totalCompletionTokens,\r\n totalCachedTokens,\r\n totalCost,\r\n records: [...this.#sessionRecords],\r\n };\r\n }\r\n\r\n /** 当前会话总费用(元) */\r\n get sessionTotalCost(): number {\r\n return this.#sessionRecords.reduce((sum, r) => sum + r.cost.totalCost, 0);\r\n }\r\n\r\n /** 当前会话总 prompt token 数 */\r\n get sessionPromptTokens(): number {\r\n return this.#sessionRecords.reduce((sum, r) => sum + r.usage.promptTokens, 0);\r\n }\r\n\r\n /** 当前会话总 completion token 数 */\r\n get sessionCompletionTokens(): number {\r\n return this.#sessionRecords.reduce((sum, r) => sum + r.usage.completionTokens, 0);\r\n }\r\n\r\n /** 当前会话 API 调用次数 */\r\n get sessionCallCount(): number {\r\n return this.#sessionRecords.length;\r\n }\r\n\r\n // -----------------------------------------------------------------------\r\n // 日级查询\r\n // -----------------------------------------------------------------------\r\n\r\n /** 今日成本汇总 */\r\n get todaySummary(): DailyCostSummary {\r\n this.#ensureTodayBucket();\r\n return { ...this.#todaySummary };\r\n }\r\n\r\n /** 今日总费用(元) */\r\n get todayTotalCost(): number {\r\n this.#ensureTodayBucket();\r\n return this.#todaySummary.totalCost;\r\n }\r\n\r\n /** 今日 API 调用次数 */\r\n get todayCallCount(): number {\r\n this.#ensureTodayBucket();\r\n return this.#todaySummary.totalCalls;\r\n }\r\n\r\n // -----------------------------------------------------------------------\r\n // 预算检查\r\n // -----------------------------------------------------------------------\r\n\r\n /** 是否已超出预算 */\r\n get isBudgetExceeded(): boolean {\r\n if (this.#budgetLimit > 0 && this.todayTotalCost >= this.#budgetLimit) {\r\n return true;\r\n }\r\n if (this.#tokenBudgetLimit > 0) {\r\n const totalTokens =\r\n this.#todaySummary.totalPromptTokens +\r\n this.#todaySummary.totalCompletionTokens;\r\n if (totalTokens >= this.#tokenBudgetLimit) return true;\r\n }\r\n return false;\r\n }\r\n\r\n /** 剩余预算(元),无限制时返回 Infinity */\r\n get remainingBudget(): number {\r\n if (this.#budgetLimit <= 0) return Infinity;\r\n return Math.max(0, this.#budgetLimit - this.todayTotalCost);\r\n }\r\n\r\n // -----------------------------------------------------------------------\r\n // 持久化\r\n // -----------------------------------------------------------------------\r\n\r\n /**\r\n * 从磁盘加载历史成本数据。\r\n * 启动时调用,用于恢复今日和历史数据。\r\n */\r\n async load(): Promise<void> {\r\n const store = await this.#loadStore();\r\n const today = getTodayStr();\r\n\r\n if (store.daily[today]) {\r\n this.#todaySummary = store.daily[today];\r\n this.#todayDate = today;\r\n }\r\n }\r\n\r\n /**\r\n * 将脏数据持久化到磁盘。\r\n * 通常在每次 record() 后异步调用,或在会话结束时主动调用。\r\n */\r\n async flush(): Promise<void> {\r\n if (!this.#dirty || this.#flushInProgress) return;\r\n this.#flushInProgress = true;\r\n this.#dirty = false;\r\n\r\n try {\r\n await this.#saveStore();\r\n } finally {\r\n this.#flushInProgress = false;\r\n }\r\n }\r\n\r\n /**\r\n * 查询指定日期范围的成本汇总。\r\n * @param startDate 起始日期(YYYY-MM-DD),包含\r\n * @param endDate 截止日期(YYYY-MM-DD),包含,默认同 startDate\r\n */\r\n async queryRange(\r\n startDate: string,\r\n endDate?: string,\r\n ): Promise<DailyCostSummary[]> {\r\n const store = await this.#loadStore();\r\n const end = endDate ?? startDate;\r\n const result: DailyCostSummary[] = [];\r\n\r\n for (const [date, summary] of Object.entries(store.daily)) {\r\n if (date >= startDate && date <= end) {\r\n result.push(summary);\r\n }\r\n }\r\n\r\n result.sort((a, b) => a.date.localeCompare(b.date));\r\n return result;\r\n }\r\n\r\n /** 重置会话(开始新会话,不影响日级累计) */\r\n resetSession(): void {\r\n this.#sessionId = generateSessionId();\r\n this.#sessionStartedAt = new Date().toISOString();\r\n this.#sessionRecords.length = 0;\r\n }\r\n\r\n // -----------------------------------------------------------------------\r\n // 内部方法\r\n // -----------------------------------------------------------------------\r\n\r\n /** 确保今日的统计桶存在(处理跨日情况) */\r\n #ensureTodayBucket(): void {\r\n const today = getTodayStr();\r\n if (today !== this.#todayDate) {\r\n // 日期变更:将旧日数据持久化后切换到新日\r\n this.#todayDate = today;\r\n this.#todaySummary = createEmptyDailySummary(today);\r\n this.#dirty = true;\r\n }\r\n }\r\n\r\n /** 将一条记录累加到日级汇总 */\r\n #addToDaily(record: CostRecord): void {\r\n this.#todaySummary.totalPromptTokens += record.usage.promptTokens;\r\n this.#todaySummary.totalCompletionTokens += record.usage.completionTokens;\r\n this.#todaySummary.totalCachedTokens += record.usage.cachedPromptTokens ?? 0;\r\n this.#todaySummary.totalCost += record.cost.totalCost;\r\n this.#todaySummary.totalCalls += 1;\r\n\r\n // 按模型分类累加\r\n const modelKey = record.model;\r\n if (!this.#todaySummary.byModel[modelKey]) {\r\n this.#todaySummary.byModel[modelKey] = {\r\n model: record.model,\r\n totalPromptTokens: 0,\r\n totalCompletionTokens: 0,\r\n totalCachedTokens: 0,\r\n totalCost: 0,\r\n totalCalls: 0,\r\n };\r\n }\r\n\r\n const modelSummary = this.#todaySummary.byModel[modelKey];\r\n modelSummary.totalPromptTokens += record.usage.promptTokens;\r\n modelSummary.totalCompletionTokens += record.usage.completionTokens;\r\n modelSummary.totalCachedTokens += record.usage.cachedPromptTokens ?? 0;\r\n modelSummary.totalCost += record.cost.totalCost;\r\n modelSummary.totalCalls += 1;\r\n }\r\n\r\n /** 预算超限检查 */\r\n #checkBudget(): void {\r\n if (!this.isBudgetExceeded) return;\r\n this.#onBudgetExceeded?.(this);\r\n }\r\n\r\n /** 从磁盘加载成本存储 */\r\n async #loadStore(): Promise<CostStore> {\r\n const filePath = join(this.#costDir, \"history.json\");\r\n try {\r\n const raw = await readFile(filePath, \"utf-8\");\r\n return JSON.parse(raw) as CostStore;\r\n } catch {\r\n return { version: 1, daily: {} };\r\n }\r\n }\r\n\r\n /** 保存成本存储到磁盘 */\r\n async #saveStore(): Promise<void> {\r\n const store = await this.#loadStore();\r\n\r\n // 合并今日数据到存储中\r\n store.daily[this.#todayDate] = this.#todaySummary;\r\n\r\n // 保留最近 90 天的数据(避免文件过大)\r\n const cutoffDate = getDateNDaysAgo(90);\r\n const datesToRemove = Object.keys(store.daily).filter(\r\n (date) => date < cutoffDate,\r\n );\r\n for (const date of datesToRemove) {\r\n // eslint-disable-next-line @typescript-eslint/no-dynamic-delete\r\n delete store.daily[date];\r\n }\r\n\r\n // 确保目录存在\r\n await mkdir(this.#costDir, { recursive: true });\r\n\r\n const filePath = join(this.#costDir, \"history.json\");\r\n await writeFile(filePath, JSON.stringify(store, null, 2), \"utf-8\");\r\n }\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 工具函数\r\n// ---------------------------------------------------------------------------\r\n\r\n/** 生成会话 ID:时间戳 + 随机后缀 */\r\nfunction generateSessionId(): string {\r\n const ts = Date.now().toString(36);\r\n const rand = Math.random().toString(36).slice(2, 8);\r\n return `${ts}-${rand}`;\r\n}\r\n\r\n/** 获取今日日期字符串(YYYY-MM-DD) */\r\nfunction getTodayStr(): string {\r\n return new Date().toISOString().slice(0, 10);\r\n}\r\n\r\n/** 获取 N 天前的日期字符串 */\r\nfunction getDateNDaysAgo(n: number): string {\r\n const d = new Date();\r\n d.setDate(d.getDate() - n);\r\n return d.toISOString().slice(0, 10);\r\n}\r\n\r\n/** 创建空的日级成本汇总 */\r\nfunction createEmptyDailySummary(date: string): DailyCostSummary {\r\n return {\r\n date,\r\n totalPromptTokens: 0,\r\n totalCompletionTokens: 0,\r\n totalCachedTokens: 0,\r\n totalCost: 0,\r\n totalCalls: 0,\r\n byModel: {},\r\n };\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 格式化工具\r\n// ---------------------------------------------------------------------------\r\n\r\n/** 格式化费用金额(保留4位小数) */\r\nexport function formatMoney(yuan: number): string {\r\n return `¥${yuan.toFixed(4)}`;\r\n}\r\n\r\n/** 格式化 token 数量(带千分位) */\r\nexport function formatTokens(tokens: number): string {\r\n return tokens.toLocaleString(\"en-US\");\r\n}\r\n\r\n/** 格式化缓存命中率百分比 */\r\nexport function formatCacheHitRate(cached: number, total: number): string {\r\n if (total === 0) return \"0.0%\";\r\n return `${((cached / total) * 100).toFixed(1)}%`;\r\n}\r\n\r\n/** 生成今日消耗报告的终端友好文本 */\r\nexport function formatTodayReport(summary: DailyCostSummary): string {\r\n const lines: string[] = [];\r\n\r\n lines.push(`📊 今日消耗报告 (${summary.date})`);\r\n lines.push(\"─\".repeat(40));\r\n lines.push(` 💰 总费用: ${formatMoney(summary.totalCost)}`);\r\n lines.push(` 📞 调用次数: ${String(summary.totalCalls)} 次`);\r\n lines.push(` 📥 输入 Token: ${formatTokens(summary.totalPromptTokens)}`);\r\n lines.push(` 📤 输出 Token: ${formatTokens(summary.totalCompletionTokens)}`);\r\n lines.push(\r\n ` 🗄️ 缓存命中: ${formatTokens(summary.totalCachedTokens)} (${formatCacheHitRate(summary.totalCachedTokens, summary.totalPromptTokens)})`,\r\n );\r\n lines.push(\"\");\r\n\r\n // 按模型分类明细\r\n const models = Object.values(summary.byModel);\r\n if (models.length > 0) {\r\n lines.push(\"📈 按模型分类:\");\r\n for (const m of models) {\r\n lines.push(` ─ ${m.model} ─`);\r\n lines.push(` 费用: ${formatMoney(m.totalCost)} | 调用: ${String(m.totalCalls)} 次`);\r\n lines.push(\r\n ` 输入: ${formatTokens(m.totalPromptTokens)} | 输出: ${formatTokens(m.totalCompletionTokens)}`,\r\n );\r\n lines.push(\r\n ` 缓存命中: ${formatTokens(m.totalCachedTokens)} (${formatCacheHitRate(m.totalCachedTokens, m.totalPromptTokens)})`,\r\n );\r\n }\r\n }\r\n\r\n return lines.join(\"\\n\");\r\n}\r\n\r\n/** 生成会话成本摘要的单行文本 */\r\nexport function formatSessionCostLine(summary: SessionCostSummary): string {\r\n const cacheRate = formatCacheHitRate(\r\n summary.totalCachedTokens,\r\n summary.totalPromptTokens,\r\n );\r\n return (\r\n `💰 ¥${summary.totalCost.toFixed(4)} | ` +\r\n `📥${formatTokens(summary.totalPromptTokens)} ` +\r\n `📤${formatTokens(summary.totalCompletionTokens)} ` +\r\n `🗄️${cacheRate} | ` +\r\n `📞${String(summary.records.length)}次`\r\n );\r\n}\r\n\r\n/** 生成单次调用的成本单行文本 */\r\nexport function formatCallCostLine(record: CostRecord): string {\r\n const cacheRate = formatCacheHitRate(\r\n record.usage.cachedPromptTokens ?? 0,\r\n record.usage.promptTokens,\r\n );\r\n return (\r\n `≈¥${record.cost.totalCost.toFixed(4)} ` +\r\n `(📥${String(record.usage.promptTokens)} 📤${String(record.usage.completionTokens)} 🗄️${cacheRate})`\r\n );\r\n}","// ---------------------------------------------------------------------------\r\n// 工具调用块组件 — 在终端中渲染单个工具调用\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { Box, Text } from \"ink\";\r\nimport type { ProviderToolCall } from \"../provider/index.js\";\r\n\r\ninterface ToolCallBlockProps {\r\n call: ProviderToolCall;\r\n /** 是否显示\"等待执行\"提示(默认 true) */\r\n showPendingHint?: boolean;\r\n}\r\n\r\n/**\r\n * 格式化工具调用参数的摘要。\r\n * 短参数直接展示,长参数截断。\r\n */\r\nfunction formatArgsSummary(args: string): string {\r\n try {\r\n const parsed = JSON.parse(args) as Record<string, unknown>;\r\n const lines = Object.entries(parsed).map(([key, value]) => {\r\n const val = String(value);\r\n const truncated = val.length > 80 ? val.slice(0, 77) + \"...\" : val;\r\n return ` ${key}: ${truncated}`;\r\n });\r\n return lines.join(\"\\n\");\r\n } catch {\r\n // 无法解析的 JSON,直接截断展示\r\n const truncated = args.length > 120 ? args.slice(0, 117) + \"...\" : args;\r\n return ` ${truncated}`;\r\n }\r\n}\r\n\r\n/**\r\n * 渲染单个工具调用信息块。\r\n *\r\n * 显示效果:\r\n * 📦 read_file ─────────────────\r\n * 📂 path: src/provider/types.ts\r\n * ⏳ 等待执行\r\n */\r\nexport function ToolCallBlock({ call, showPendingHint = true }: ToolCallBlockProps) {\r\n const argsDisplay = formatArgsSummary(call.arguments);\r\n\r\n return (\r\n <Box flexDirection=\"column\" marginLeft={3} marginTop={1}>\r\n <Box>\r\n <Text color=\"#00ffff\" bold>📦 {call.name}</Text>\r\n <Text color=\"#555555\"> {\"─\".repeat(Math.max(1, 30 - call.name.length))}</Text>\r\n </Box>\r\n <Box flexDirection=\"column\">\r\n <Text color=\"#888888\">{argsDisplay}</Text>\r\n </Box>\r\n {showPendingHint && (\r\n <Box>\r\n <Text color=\"yellow\">⏳ 等待执行</Text>\r\n </Box>\r\n )}\r\n </Box>\r\n );\r\n}","// ---------------------------------------------------------------------------\r\n// 消息组装 — 将历史消息与系统提示词合并,支持上下文裁剪\r\n// ---------------------------------------------------------------------------\r\n\r\nimport type { ChatMessage, UsageInfo } from \"../provider/index.js\";\r\nimport type { ModelId } from \"../provider/types.js\";\r\nimport { getModelMeta, estimateTokens } from \"../provider/models.js\";\r\n\r\n/** 上下文裁剪策略 */\r\nexport interface TrimOptions {\r\n /** 模型标识,用于获取上下文窗口大小 */\r\n model: ModelId;\r\n /** 为模型输出预留的 token 数 */\r\n reservedForOutput: number;\r\n /** 系统提示词(始终保留) */\r\n systemPrompt: string;\r\n /** 最近 N 轮对话强制保留(不参与裁剪) */\r\n preserveRecentRounds: number;\r\n}\r\n\r\n/**\r\n * 估算消息的 token 数。\r\n * 简单估算:content 长度 × 0.3 + 角色开销 ~10 tokens\r\n */\r\nfunction estimateMessageTokens(msg: ChatMessage): number {\r\n let text = msg.content;\r\n // 工具调用的参数也算\r\n if (msg.toolCalls) {\r\n for (const tc of msg.toolCalls) {\r\n text += tc.name + tc.arguments;\r\n }\r\n }\r\n return estimateTokens(text) + 10;\r\n}\r\n\r\n/**\r\n * 将消息历史划分为不可分割的「回合」单元。\r\n *\r\n * 一个回合的定义:\r\n * - 以一条 user 消息开头\r\n * - 后续连续的 assistant / tool 附属属于该回合,直到下一条 user 出现\r\n * - 特别地,assistant 携带 tool_calls 时,其后紧跟的若干 tool 消息必须与该 assistant 绑定\r\n * (DeepSeek/OpenAI 兼容 API 要求 tool_calls 与对应的 tool 结果消息一一配对,\r\n * 否则返回 400 错误)。因此裁剪时不能把这对拆开。\r\n *\r\n * @returns 回合数组;每个回合是一个非空消息数组,原顺序保留\r\n */\r\nfunction groupIntoTurns(messages: ChatMessage[]): ChatMessage[][] {\r\n const turns: ChatMessage[][] = [];\r\n let current: ChatMessage[] | null = null;\r\n\r\n for (const msg of messages) {\r\n if (msg.role === \"user\") {\r\n // 新回合开始\r\n if (current) turns.push(current);\r\n current = [msg];\r\n } else {\r\n // assistant / tool:归属当前回合;若无当前回合(历史以非 user 开头)\r\n // 则单独为保证不丢失而新建一个虚拟回合\r\n if (!current) current = [];\r\n current.push(msg);\r\n }\r\n }\r\n if (current && current.length > 0) turns.push(current);\r\n return turns;\r\n}\r\n\r\n/**\r\n * 估算一个回合内所有消息的总 token 数。\r\n */\r\nfunction estimateTurnTokens(turn: ChatMessage[]): number {\r\n let sum = 0;\r\n for (const msg of turn) sum += estimateMessageTokens(msg);\r\n return sum;\r\n}\r\n\r\n/**\r\n * 裁剪消息历史以适应上下文窗口,保证工具调用回合不被拆散。\r\n *\r\n * 策略:\r\n * 1. 系统提示词始终保留,不计入裁剪范围\r\n * 2. 将历史按「回合」分组(user 起头 → 直到下一个 user 之前的所有 assistant/tool)\r\n * 3. 最近的 `preserveRecentRounds` 个回合整体强制保留\r\n * 4. 若保留区超限,从最前的回合整组丢弃,直到不超限\r\n * 5. 否则,从最近向 earliest 后填更早的整回合,装不下就停\r\n *\r\n * 关键不变量:任何含 toolCalls 的 assistant 与其后续的 tool 结果消息\r\n * 始终在同一回合里,裁剪不会拆开它们。\r\n *\r\n * @param messages 原始消息历史(不含 system prompt)\r\n * @param opts 裁剪选项\r\n * @returns [裁剪后的消息数组, 是否发生了裁剪]\r\n */\r\nexport function trimMessages(\r\n messages: ChatMessage[],\r\n opts: TrimOptions,\r\n): [ChatMessage[], boolean] {\r\n const meta = getModelMeta(opts.model);\r\n const maxInputTokens = meta.contextWindow - opts.reservedForOutput;\r\n\r\n // 系统提示词 token 估算\r\n const systemTokens = estimateTokens(opts.systemPrompt);\r\n let remaining = maxInputTokens - systemTokens;\r\n\r\n const turns = groupIntoTurns(messages);\r\n\r\n // 保留最近 N 个回合\r\n const preservedTurns: ChatMessage[][] = [];\r\n let roundsPreserved = 0;\r\n for (let i = turns.length - 1; i >= 0 && roundsPreserved < opts.preserveRecentRounds; i--) {\r\n preservedTurns.unshift(turns[i]!);\r\n roundsPreserved++;\r\n }\r\n\r\n const preserved: ChatMessage[] = preservedTurns.flat();\r\n for (const msg of preserved) {\r\n remaining -= estimateMessageTokens(msg);\r\n }\r\n\r\n // 保留区本身超限:从最前的回合整组丢\r\n if (remaining < 0) {\r\n while (preservedTurns.length > 1 && remaining < 0) {\r\n const dropped = preservedTurns.shift()!;\r\n remaining += estimateTurnTokens(dropped);\r\n }\r\n return [preservedTurns.flat(), true];\r\n }\r\n\r\n // 向 earliest 填更早的回合\r\n const olderTurns = turns.slice(0, turns.length - preservedTurns.length);\r\n const keptTurns: ChatMessage[][] = [];\r\n\r\n for (let i = olderTurns.length - 1; i >= 0; i--) {\r\n const cost = estimateTurnTokens(olderTurns[i]!);\r\n if (remaining - cost < 0) break;\r\n remaining -= cost;\r\n keptTurns.unshift(olderTurns[i]!);\r\n }\r\n\r\n const result = [...keptTurns, ...preservedTurns].flat();\r\n const trimmed = result.length < messages.length;\r\n return [result, trimmed];\r\n}\r\n\r\n/**\r\n * 构建完整的 API 请求消息数组。\r\n *\r\n * 在消息历史前面插入 system prompt,返回可直接传给 provider.chat() 的消息数组。\r\n */\r\nexport function buildApiMessages(\r\n systemPrompt: string,\r\n history: ChatMessage[],\r\n): ChatMessage[] {\r\n return [\r\n { role: \"system\", content: systemPrompt },\r\n ...history,\r\n ];\r\n}\r\n\r\n/**\r\n * 将 Token 使用量格式化为人类可读的摘要字符串。\r\n */\r\nexport function formatUsageSummary(usage: UsageInfo): string {\r\n const prompt = usage.promptTokens.toLocaleString();\r\n const completion = usage.completionTokens.toLocaleString();\r\n const total = (usage.promptTokens + usage.completionTokens).toLocaleString();\r\n\r\n let summary = `📥 ${prompt} + 📤 ${completion} = 📦 ${total} tokens`;\r\n\r\n if (usage.cachedPromptTokens && usage.cachedPromptTokens > 0) {\r\n const cacheRate = ((usage.cachedPromptTokens / usage.promptTokens) * 100).toFixed(1);\r\n summary += ` │ 🗄️ 缓存命中 ${cacheRate}%`;\r\n }\r\n\r\n return summary;\r\n}","// ---------------------------------------------------------------------------\r\n// Diff 预览组件 — 终端中渲染带色彩的 unified diff\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { Box, Text } from \"ink\";\r\nimport type { FileDiff } from \"../tool/types.js\";\r\n\r\ninterface DiffPreviewProps {\r\n /** FileDiff 对象 */\r\n diff: FileDiff;\r\n}\r\n\r\n/**\r\n * 解析 unified diff 文本,按行着色渲染。\r\n *\r\n * 颜色方案:\r\n * - `@@ ... @@` → 青色(hunk 头)\r\n * - `+` 添加行 → 绿色\r\n * - `-` 删除行 → 红色\r\n * - ` ` 上下文行 → 灰色\r\n *\r\n * 注意:文件头“--- a/...” / “+++ b/...”由摘要行展示,不重复渲染。\r\n */\r\nexport function DiffPreview({ diff }: DiffPreviewProps) {\r\n const { patch, additions, deletions, existedBefore, filePath } = diff;\r\n\r\n // 无变更\r\n if (!patch || patch.length === 0) {\r\n return null;\r\n }\r\n\r\n const lines = patch.split(\"\\n\");\r\n const fileName = filePath.replace(/\\\\/g, \"/\").split(\"/\").pop() ?? filePath;\r\n\r\n return (\r\n <Box flexDirection=\"column\" marginTop={1}>\r\n {/* 摘要行 */}\r\n <Box flexDirection=\"row\" gap={1}>\r\n <Text bold color=\"#00ffff\">\r\n 📝 {existedBefore ? \"修改\" : \"新建\"}:\r\n </Text>\r\n <Text bold>{fileName}</Text>\r\n <Text color=\"#555555\">\r\n (+{additions} -{deletions})\r\n </Text>\r\n </Box>\r\n\r\n {/* Diff 内容(跳过文件头两行“--- a/x” / \"+++ b/x\",避免与摘要行重复展示文件名) */}\r\n <Box flexDirection=\"column\" marginLeft={2}>\r\n {lines\r\n .filter((line) => !line.startsWith(\"---\") && !line.startsWith(\"+++\"))\r\n .map((line, i) => (\r\n <DiffLine key={i} line={line} />\r\n ))}\r\n </Box>\r\n </Box>\r\n );\r\n}\r\n\r\n/** 渲染单行 diff 内容 */\r\nfunction DiffLine({ line }: { line: string }) {\r\n // 文件头行(如 \"--- a/path\" / \"+++ b/path\" / \"--- /dev/null\")\r\n // 摘要行已展示文件名与新建/修改状态,无需重复渲染\r\n if (line.startsWith(\"---\") || line.startsWith(\"+++\")) {\r\n return null;\r\n }\r\n\r\n // Hunk 头行 @@ -x,y +a,b @@\r\n if (line.startsWith(\"@@\")) {\r\n // 将 @@ 标记和行号范围分别着色\r\n return <Text color=\"#00cccc\">{line}</Text>;\r\n }\r\n\r\n // 添加行\r\n if (line.startsWith(\"+\")) {\r\n return <Text color=\"#22c55e\">{line}</Text>;\r\n }\r\n\r\n // 删除行\r\n if (line.startsWith(\"-\")) {\r\n return <Text color=\"#ef4444\">{line}</Text>;\r\n }\r\n\r\n // 上下文行(空格开头或空行)\r\n return <Text color=\"#6b7280\">{line}</Text>;\r\n}","// ---------------------------------------------------------------------------\r\n// SkillSelector — 用户输入 / 时渲染的 skill 列表组件\r\n// 通过 selectedIndex 高亮当前选中项,按 Tab 可补全 skill 名称\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { Box, Text } from \"ink\";\r\nimport type { SkillInfo } from \"../cli/skill-import.js\";\r\n\r\nconst HIGHLIGHT_COLOR = \"#00bfff\";\r\n\r\ninterface SkillSelectorProps {\r\n /** 所有可用 skill 列表 */\r\n skills: SkillInfo[];\r\n /** 当前输入框文本 */\r\n input: string;\r\n /** 当前选中的 skill 索引 */\r\n selectedIndex: number;\r\n}\r\n\r\nexport function SkillSelector({ skills, input, selectedIndex }: SkillSelectorProps) {\r\n // 匹配规则:/ 必须在输入开头,或者前面有空格\r\n const match = input.match(/(?:^|\\s)\\/([^/]*)$/);\r\n if (!match) return null;\r\n\r\n const query = match[1]!.toLowerCase().trim();\r\n if (skills.length === 0) return null;\r\n\r\n // 斜杠在开头且无后续内容时,展示全部 skill 作为提示\r\n const matched = !query && input.startsWith(\"/\")\r\n ? skills.slice(0, 3)\r\n : skills\r\n .filter((s) => s.name.toLowerCase().includes(query))\r\n .slice(0, 3);\r\n\r\n // 精确匹配时表示已补全完成,不显示列表\r\n if (query && matched.length > 0 && matched.some((s) => s.name.toLowerCase() === query)) return null;\r\n\r\n if (matched.length === 0) return null;\r\n\r\n return (\r\n <Box flexDirection=\"column\" marginLeft={4} marginTop={1}>\r\n <Text color=\"#808080\" dimColor>\r\n 支持的 Skill:\r\n </Text>\r\n {matched.map((skill, i) => (\r\n <Box key={skill.name}>\r\n <Text color={i === selectedIndex ? HIGHLIGHT_COLOR : \"#808080\"}>\r\n {i === selectedIndex ? \" › \" : \" \"}{skill.name}\r\n </Text>\r\n </Box>\r\n ))}\r\n <Box marginTop={1}>\r\n <Text color=\"#808080\" dimColor>\r\n ↑↓ 选择 · Tab 补全\r\n </Text>\r\n </Box>\r\n </Box>\r\n );\r\n}\r\n","// ---------------------------------------------------------------------------\r\n// FileSelector — 用户输入 @ 时渲染的文件列表组件\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { Box, Text } from \"ink\";\r\n\r\ninterface FileSelectorProps {\r\n /** 所有项目文件路径列表(相对于 CWD) */\r\n files: string[];\r\n /** 当前输入框文本 */\r\n input: string;\r\n /** 当前选中的文件索引 */\r\n selectedIndex: number;\r\n}\r\n\r\nconst HIGHLIGHT_COLOR = \"#00ff41\";\r\n\r\nexport function FileSelector({ files, input, selectedIndex }: FileSelectorProps) {\r\n // 匹配规则:@ 必须在输入开头,或者前面有空格\r\n const match = input.match(/(?:^|\\s)@([^@]*)$/);\r\n if (!match) return null;\r\n\r\n const query = match[1]!.toLowerCase().trim();\r\n if (files.length === 0) return null;\r\n\r\n // @ 在开头且无后续内容时,展示前 5 个文件作为提示\r\n const matched = !query && input.startsWith(\"@\")\r\n ? files.slice(0, 5)\r\n : files\r\n .filter((f) => f.toLowerCase().includes(query))\r\n .slice(0, 5);\r\n\r\n // 精确匹配时表示已补全完成,不显示列表\r\n if (query && matched.some((f) => f.toLowerCase() === query)) return null;\r\n\r\n if (matched.length === 0) return null;\r\n\r\n return (\r\n <Box flexDirection=\"column\" marginLeft={4} marginTop={1}>\r\n <Text color=\"#808080\" dimColor>\r\n 项目文件:\r\n </Text>\r\n {matched.map((file, i) => (\r\n <Box key={file}>\r\n <Text color={i === selectedIndex ? HIGHLIGHT_COLOR : \"#808080\"}>\r\n {i === selectedIndex ? \" › \" : \" \"}{file}\r\n </Text>\r\n </Box>\r\n ))}\r\n <Box marginTop={1}>\r\n <Text color=\"#808080\" dimColor>\r\n ↑↓ 选择 · Tab 补全\r\n </Text>\r\n </Box>\r\n </Box>\r\n );\r\n}\r\n","// ---------------------------------------------------------------------------\r\n// Provider 注册表 — 工厂模式管理 Provider 实例\r\n// ---------------------------------------------------------------------------\r\n\r\nimport type { Provider, ModelId } from \"./types.js\";\r\nimport { isSupportedModel } from \"./models.js\";\r\nimport { ModelNotSupportedError } from \"./errors.js\";\r\nimport { DeepSeekProvider } from \"./deepseek.js\";\r\n\r\n/** Provider 工厂函数类型 */\r\nexport type ProviderFactory = (config: {\r\n apiKey: string;\r\n baseUrl: string;\r\n model: string;\r\n}) => Provider;\r\n\r\n/**\r\n * Provider 注册表 — 工厂模式管理 Provider 实例。\r\n *\r\n * 使用方式:\r\n * ```ts\r\n * const provider = registry.get(\"deepseek\", { apiKey, baseUrl, model });\r\n * for await (const chunk of provider.chat(messages)) { ... }\r\n * ```\r\n */\r\nexport class ProviderRegistry {\r\n readonly #factories = new Map<string, ProviderFactory>();\r\n readonly #instances = new Map<string, Provider>();\r\n\r\n /** 注册一个 Provider 工厂 */\r\n register(name: string, factory: ProviderFactory): void {\r\n this.#factories.set(name, factory);\r\n }\r\n\r\n /**\r\n * 获取或创建一个 Provider 实例(单例)。\r\n *\r\n * 相同的 name + baseUrl + model 组合会复用同一实例。\r\n * 如果 model 不在支持列表中,抛出 ModelNotSupportedError。\r\n */\r\n get(\r\n name: string,\r\n config: { apiKey: string; baseUrl: string; model: string },\r\n ): Provider {\r\n if (!isSupportedModel(config.model)) {\r\n throw new ModelNotSupportedError(config.model);\r\n }\r\n\r\n // 缓存键 = name:baseUrl:model(不同配置会创建不同实例)\r\n const cacheKey = `${name}:${config.baseUrl}:${config.model}`;\r\n\r\n const cached = this.#instances.get(cacheKey);\r\n if (cached) return cached;\r\n\r\n const factory = this.#factories.get(name);\r\n if (!factory) {\r\n const available = [...this.#factories.keys()].join(\", \");\r\n throw new Error(`未注册的 Provider: \"${name}\"。可用: ${available}`);\r\n }\r\n\r\n const instance = factory(config);\r\n this.#instances.set(cacheKey, instance);\r\n return instance;\r\n }\r\n\r\n /** 列出已注册的 Provider 名称 */\r\n list(): string[] {\r\n return [...this.#factories.keys()];\r\n }\r\n\r\n /** 清除缓存的实例(用于配置热重载) */\r\n clear(): void {\r\n this.#instances.clear();\r\n }\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 全局默认注册表 + 预注册内置 Provider\r\n// ---------------------------------------------------------------------------\r\n\r\n/** 全局默认 Provider 注册表 */\r\nexport const defaultRegistry = new ProviderRegistry();\r\n\r\ndefaultRegistry.register(\"deepseek\", (config) => {\r\n // 模型合法性已在 registry.get() 中通过 isSupportedModel 校验\r\n return new DeepSeekProvider({\r\n apiKey: config.apiKey,\r\n baseUrl: config.baseUrl,\r\n model: config.model as ModelId,\r\n });\r\n});\r\n\r\n/**\r\n * 从配置快速创建 Provider 实例的快捷方式。\r\n *\r\n * 内部委托给 defaultRegistry,会在注册表中校验模型并缓存实例。\r\n */\r\nexport function createProvider(config: {\r\n name: string;\r\n apiKey: string;\r\n baseUrl: string;\r\n model: string;\r\n}): Provider {\r\n return defaultRegistry.get(config.name, config);\r\n}","// ---------------------------------------------------------------------------\r\n// 额外提示词 — 终端输出格式化约束\r\n//\r\n// 此文件与 system-prompt.ts 分离,方便编辑和调整提示词内容,\r\n// 无需修改业务逻辑代码。\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * 终端输出格式化约束。\r\n *\r\n * 由于 dskcode 运行在终端中,AI 的回复会直接渲染在命令行界面,\r\n * 不支持 Markdown 渲染。因此需要约束模型避免使用 Markdown 符号。\r\n */\r\nexport const EXTRA_PROMPT = [\r\n \"【终端输出约束】\",\r\n \"\",\r\n \"你的回复将直接渲染在用户终端的命令行界面中,不支持 Markdown 渲染。\",\r\n \"请严格遵守以下格式规则。\",\r\n \"\",\r\n \"一、禁止使用的符号\",\r\n ' - 不要使用 Markdown 标题符号(单独或连续的井号)',\r\n ' - 不要使用粗体或斜体符号(星号、下划线)',\r\n \" - 不要使用代码块标记(连续三个反引号)\",\r\n \" - 不要使用块引用符号(大于号)\",\r\n \" - 不要使用分隔线(连续三个减号或星号)\",\r\n \" - 不要使用行内代码标记(单个反引号)\",\r\n \"\",\r\n \"二、推荐的组织方式\",\r\n ' - 用纯文字描述层级,例如\"第一点\"、\"第二点\"替代标题符号',\r\n \" - 用 emoji 符号做视觉标记(如 📌 📦 📝 ⚠️ 等)\",\r\n \" - 用数字序号(1. 2. 3.)组织列表\",\r\n \" - 代码示例用缩进(每行前加 4 个空格)替代代码块标记\",\r\n \" - 文件路径、命令、变量名直接书写,不要用任何符号包裹\",\r\n \"\",\r\n \"三、正确格式示例\",\r\n \"\",\r\n \" 第一点:优化数据库查询\",\r\n \" 1. 添加索引到 user_id 字段\",\r\n \" 2. 用连接池替代短连接\",\r\n \"\",\r\n \" 代码示例:\",\r\n \" const pool = new Pool({ max: 20 });\",\r\n ' await pool.query(\"SELECT * FROM users\");',\r\n \"\",\r\n \" 输出提示:\",\r\n \" 📌 优化前:查询耗时 3.2 秒\",\r\n \"\",\r\n \"四、错误格式示例(禁止这样写)\",\r\n \"\",\r\n \" 井号串 优化数据库查询(这是错误的)\",\r\n \" 星号 添加索引到 user_id 字段(这是错误的)\",\r\n \" 三个反引号开始或结束(这是错误的)\",\r\n \"\",\r\n \"注意:上述示例中的错误写法仅作为说明,你的回复中不要出现这些格式。\",\r\n].join(\"\\n\");\r\n","// ---------------------------------------------------------------------------\r\n// 系统提示词构建 — 动态注入模型信息、工具描述、时间、项目上下文\r\n// ---------------------------------------------------------------------------\r\n\r\nimport type { SystemPromptOptions } from \"./types.js\";\r\nimport { EXTRA_PROMPT } from \"./extra-prompt.js\";\r\n\r\n/**\r\n * 构建 Agent 的系统提示词。\r\n *\r\n * 系统提示词按如下顺序组装:\r\n * 1. 角色定义 — dskcode 是什么\r\n * 2. 模型信息 — 当前使用的模型\r\n * 3. 时间上下文 — 当前日期时间\r\n * 4. 工具描述 — 可用工具列表(如有)\r\n * 5. 项目上下文 — AGENTS.md 内容(如有)\r\n * 6. 行为约束 — 回答风格、安全边界\r\n * 7. 终端输出约束 — 禁止 Markdown 符号(纯终端渲染)\r\n */\r\nexport function buildSystemPrompt(opts: SystemPromptOptions): string {\r\n const sections: string[] = [];\r\n\r\n // 1. 角色定义\r\n sections.push(`你是 dskcode,一个基于 DeepSeek 的 AI 编程助手,运行在用户终端中。你的职责是帮助开发者编写、理解、调试和重构代码。\r\n\r\n## 核心原则\r\n- 回答使用中文,代码标识符保持英文\r\n- 提供准确、实用、可操作的建议\r\n- 对不确定的内容明确标注,不编造事实\r\n- 代码示例保持简洁,附带必要的注释`);\r\n\r\n // 2. 模型信息\r\n sections.push(`## 当前模型\r\n- 模型:${opts.model}`);\r\n\r\n // 3. 时间上下文\r\n const now = new Date();\r\n const dateStr = now.toLocaleDateString(\"zh-CN\", {\r\n year: \"numeric\",\r\n month: \"long\",\r\n day: \"numeric\",\r\n weekday: \"long\",\r\n });\r\n const timeStr = now.toLocaleTimeString(\"zh-CN\", {\r\n hour: \"2-digit\",\r\n minute: \"2-digit\",\r\n });\r\n sections.push(`## 时间上下文\r\n- 当前日期:${dateStr}\r\n- 当前时间:${timeStr}\r\n- 工作目录:${opts.cwd}`);\r\n\r\n // 4. 工具描述\r\n if (opts.tools && opts.tools.length > 0) {\r\n const toolLines = opts.tools\r\n .map((t) => {\r\n const paramInfo = t.parameters\r\n ? Object.keys((t.parameters as Record<string, unknown>).properties ?? {})\r\n .join(\", \")\r\n : \"\";\r\n return `- **${t.name}**:${t.description}${paramInfo ? `(参数:${paramInfo})` : \"\"}`;\r\n })\r\n .join(\"\\n\");\r\n sections.push(`## 可用工具\r\n\r\n你可以通过工具调用执行操作。当前可用的工具:\r\n\r\n${toolLines}\r\n\r\n调用工具时,请使用标准 function_call 格式。工具将由系统执行并返回结果。`);\r\n }\r\n\r\n // 5. 项目上下文\r\n if (opts.projectContext) {\r\n sections.push(`## 项目上下文\r\n\r\n${opts.projectContext}`);\r\n }\r\n\r\n // 6. 行为约束\r\n sections.push(`## 行为约束\r\n- 如果用户请求不明确,请主动询问澄清\r\n- 涉及文件操作时,先确认文件路径\r\n- 不执行可能造成不可逆损害的操作(如 rm -rf)除非用户明确确认\r\n- 当工具调用返回错误时,分析原因并尝试修复`);\r\n\r\n // 7. 终端输出约束(来自 extra-prompt.ts)\r\n sections.push(EXTRA_PROMPT);\r\n\r\n return sections.join(\"\\n\\n\");\r\n}","// ---------------------------------------------------------------------------\r\n// 工具注册表 — 注册、查找、列出、过滤工具\r\n// ---------------------------------------------------------------------------\r\n\r\nimport type { Tool, ToolContext, ToolResult } from \"./types.js\";\r\n\r\n/** 工具注册表配置 */\r\nexport interface ToolRegistryOptions {\r\n /** 工具名称黑名单,这些工具不会被 list() 返回 */\r\n disabledTools?: string[];\r\n}\r\n\r\n/**\r\n * ToolRegistry — 管理所有已注册的工具。\r\n *\r\n * 设计要点:\r\n * - 注册时检查名称唯一性,防止重复注册\r\n * - 支持 disable/enable 工具(基于配置项过滤)\r\n * - get() 返回 undefined 而非抛异常,调用方自行处理\r\n */\r\nexport class ToolRegistry {\r\n readonly #tools = new Map<string, Tool>();\r\n readonly #disabledNames: Set<string>;\r\n\r\n constructor(opts?: ToolRegistryOptions) {\r\n this.#disabledNames = new Set(opts?.disabledTools ?? []);\r\n }\r\n\r\n /**\r\n * 注册一个工具。\r\n * 如果同名工具已存在则抛出错误。\r\n */\r\n register(tool: Tool): this {\r\n if (this.#tools.has(tool.name)) {\r\n throw new Error(`工具 \"${tool.name}\" 已注册,不能重复注册`);\r\n }\r\n this.#tools.set(tool.name, tool);\r\n return this;\r\n }\r\n\r\n /**\r\n * 批量注册工具。\r\n */\r\n registerAll(tools: Tool[]): this {\r\n for (const tool of tools) {\r\n this.register(tool);\r\n }\r\n return this;\r\n }\r\n\r\n /**\r\n * 注销一个工具。\r\n */\r\n unregister(name: string): boolean {\r\n return this.#tools.delete(name);\r\n }\r\n\r\n /**\r\n * 按名称获取工具。\r\n * 如果工具被禁用或不存在,返回 undefined。\r\n */\r\n get(name: string): Tool | undefined {\r\n if (this.#disabledNames.has(name)) return undefined;\r\n return this.#tools.get(name);\r\n }\r\n\r\n /**\r\n * 获取所有启用的工具列表。\r\n */\r\n list(): Tool[] {\r\n const result: Tool[] = [];\r\n for (const [name, tool] of this.#tools) {\r\n if (!this.#disabledNames.has(name)) {\r\n result.push(tool);\r\n }\r\n }\r\n return result;\r\n }\r\n\r\n /**\r\n * 获取所有已注册的工具名称(含禁用的)。\r\n */\r\n names(): string[] {\r\n return [...this.#tools.keys()];\r\n }\r\n\r\n /**\r\n * 检查工具是否已注册(含禁用的)。\r\n */\r\n has(name: string): boolean {\r\n return this.#tools.has(name);\r\n }\r\n\r\n /**\r\n * 检查工具是否已启用。\r\n */\r\n isEnabled(name: string): boolean {\r\n return this.#tools.has(name) && !this.#disabledNames.has(name);\r\n }\r\n\r\n /**\r\n * 禁用一个工具。\r\n */\r\n disable(name: string): void {\r\n this.#disabledNames.add(name);\r\n }\r\n\r\n /**\r\n * 启用一个之前被禁用的工具。\r\n */\r\n enable(name: string): void {\r\n this.#disabledNames.delete(name);\r\n }\r\n\r\n /**\r\n * 执行指定工具。\r\n *\r\n * @returns 工具执行结果;如果工具不存在或被禁用,返回失败结果\r\n */\r\n async execute(name: string, args: unknown, ctx: ToolContext): Promise<ToolResult> {\r\n const tool = this.get(name);\r\n if (!tool) {\r\n return {\r\n success: false,\r\n data: `工具 \"${name}\" 不存在或已被禁用`,\r\n error: \"TOOL_NOT_FOUND\",\r\n };\r\n }\r\n\r\n try {\r\n return await tool.execute(args, ctx);\r\n } catch (err: unknown) {\r\n const message = err instanceof Error ? err.message : String(err);\r\n return {\r\n success: false,\r\n data: `工具 \"${name}\" 执行异常:${message}`,\r\n error: \"EXECUTION_ERROR\",\r\n };\r\n }\r\n }\r\n}","// ---------------------------------------------------------------------------\r\n// 工具系统核心类型定义\r\n// ---------------------------------------------------------------------------\r\n\r\n/** 工具参数的 JSON Schema 表示 */\r\nexport interface JSONSchema {\r\n type: \"object\";\r\n properties?: Record<string, unknown>;\r\n required?: string[];\r\n additionalProperties?: boolean;\r\n}\r\n\r\n/** 每次工具执行时传入的上下文 */\r\nexport interface ToolContext {\r\n /** 当前工作目录,用于路径约束和相对路径解析 */\r\n cwd: string;\r\n /** 中止信号,用于取消正在执行的工具 */\r\n signal?: AbortSignal;\r\n /** 命令执行超时(毫秒),默认 30000 */\r\n timeout?: number;\r\n /**\r\n * 写操作的允许根目录列表(绝对路径)。\r\n * 非空时,写工具必须确保目标路径在其中一个根下;\r\n * 为空数组表示不限制(保持兼容)。\r\n */\r\n writeRoots?: string[];\r\n}\r\n\r\n/** 文件变更的 diff 信息 */\r\nexport interface FileDiff {\r\n /** 文件路径(绝对路径) */\r\n filePath: string;\r\n /** unified diff 文本 */\r\n patch: string;\r\n /** 变更前是否存在(新建文件时为 false) */\r\n existedBefore: boolean;\r\n /** 变更统计:新增行数 */\r\n additions: number;\r\n /** 变更统计:删除行数 */\r\n deletions: number;\r\n}\r\n\r\n/** 工具执行返回的结果 */\r\nexport interface ToolResult {\r\n /** 是否执行成功 */\r\n success: boolean;\r\n /** 结果内容(成功时为输出,失败时为错误信息) */\r\n data: string;\r\n /** 错误详情(仅在 success=false 时有意义) */\r\n error?: string;\r\n /** 文件变更 diff(仅文件修改工具携带) */\r\n diff?: FileDiff;\r\n /**\r\n * 给 UI 展示的简短摘要(一行)。\r\n * 与 data 区别:data 喂给 LLM 保留完整内容,summary 仅做一行回显。\r\n * 未设置时 UI 自动降级到 data 的前 500 字符。\r\n */\r\n summary?: string;\r\n}\r\n\r\n/**\r\n * Tool 接口 — 每个内置工具或插件适配的工具都需要实现此接口。\r\n *\r\n * 设计要点:\r\n * - ReadOnly() 标志控制并行执行:读工具返回 true,写工具返回 false。\r\n * 代理在 executeBatch 中判断:全部 ReadOnly 的可并行执行,否则串行。\r\n * - 写工具(write_file/edit_file/delete_range等)返回 false,\r\n * 读工具(read_file/grep/ls/glob/fetch)返回 true。\r\n */\r\nexport interface Tool {\r\n /** 工具名称,全局唯一标识符 */\r\n readonly name: string;\r\n /** 工具描述,供模型理解工具的功能和用法 */\r\n readonly description: string;\r\n /** 参数的 JSON Schema 定义,供模型理解输入格式 */\r\n readonly parameters: JSONSchema;\r\n /**\r\n * 工具是否为只读。\r\n * true = 无副作用的读操作,可并行执行;\r\n * false = 有写操作,需串行执行。\r\n */\r\n readonly readOnly: boolean;\r\n /** 执行工具逻辑 */\r\n execute(args: unknown, ctx: ToolContext): Promise<ToolResult>;\r\n}\r\n\r\n/**\r\n * Previewer 可选接口 — 写工具可实现此接口。\r\n *\r\n * 给定相同的参数,计算文件变更的 diff 信息,而不实际写入磁盘。\r\n * 用于:\r\n * - 审批前预览变更\r\n * - 检查点快照\r\n * - 在不触盘的情况下展示改动\r\n */\r\nexport interface Previewer {\r\n /**\r\n * 预览文件变更。\r\n * @param args 与 execute 相同的参数\r\n * @returns 文件变更的 diff 信息\r\n */\r\n preview(args: unknown, ctx: ToolContext): Promise<FileDiff>;\r\n}\r\n\r\n/**\r\n * Gate 权限门接口 — 控制工具执行前是否需要审批。\r\n *\r\n * 当前预留接口,默认实现为 always-allow。\r\n * 未来可接入交互式审批或策略引擎。\r\n */\r\nexport interface Gate {\r\n /**\r\n * 检查工具调用是否允许执行。\r\n * @param toolName 工具名称\r\n * @param args 工具参数\r\n * @returns true = 允许执行,false = 拒绝执行\r\n */\r\n check(toolName: string, args: unknown): boolean | Promise<boolean>;\r\n}\r\n\r\n/**\r\n * AlwaysAllowGate — 默认权限门,所有工具调用默认放行。\r\n */\r\nexport class AlwaysAllowGate implements Gate {\r\n check(_toolName: string, _args: unknown): boolean {\r\n return true;\r\n }\r\n}\r\n\r\n/** 工具调用记录(用于风暴检测等) */\r\nexport interface ToolCallRecord {\r\n /** 工具名称 */\r\n name: string;\r\n /** 执行是否成功 */\r\n success: boolean;\r\n /** 退出时是否发生错误 */\r\n error?: string;\r\n /** 执行时间戳 */\r\n timestamp: number;\r\n}\r\n","// ---------------------------------------------------------------------------\r\n// Agent 会话 — 消息编排、流式 LLM 调用、工具执行循环、成本追踪\r\n// ---------------------------------------------------------------------------\r\n\r\nimport type {\r\n ChatMessage,\r\n Provider,\r\n ProviderToolCall,\r\n UsageInfo,\r\n ModelId,\r\n ToolDefinition,\r\n} from \"../provider/index.js\";\r\nimport { CostTracker } from \"../provider/index.js\";\r\nimport type { Tool } from \"../tool/index.js\";\r\nimport type { ToolContext } from \"../tool/index.js\";\r\nimport type { AgentEvent, SystemPromptOptions } from \"./types.js\";\r\nimport { buildSystemPrompt } from \"./system-prompt.js\";\r\nimport { trimMessages, buildApiMessages } from \"./message-builder.js\";\r\nimport { ToolRegistry } from \"../tool/registry.js\";\r\nimport type { Gate, Previewer, ToolCallRecord, ToolResult } from \"../tool/types.js\";\r\nimport { AlwaysAllowGate } from \"../tool/types.js\";\r\n\r\n/** Session 构造选项 */\r\nexport interface SessionOptions {\r\n /** 当前工作目录(注入到 system prompt) */\r\n cwd?: string;\r\n /** 最大工具调用轮次(防止无限循环),默认 20 */\r\n maxToolRounds?: number;\r\n /** 为模型输出预留的 token 数,默认 4096 */\r\n reservedForOutput?: number;\r\n /** 强制保留最近 N 轮对话(不参与上下文裁剪),默认 10 */\r\n preserveRecentRounds?: number;\r\n /** 项目上下文(AGENTS.md 内容),可选 */\r\n projectContext?: string;\r\n /** Gate 权限门 — 控制工具执行前的审批,默认 AlwaysAllowGate */\r\n gate?: Gate;\r\n /** 写目录的白名单(用于 confine 路径安全),默认 [cwd] */\r\n writeRoots?: string[];\r\n}\r\n\r\n/**\r\n * Session 表示一个 Agent 会话 — 与 LLM 的一次完整对话,\r\n * 包含消息历史、流式输出、工具执行、成本追踪。\r\n */\r\nexport class Session {\r\n readonly #messages: ChatMessage[] = [];\r\n readonly #provider: Provider;\r\n readonly #toolRegistry: ToolRegistry;\r\n readonly #costTracker: CostTracker;\r\n readonly #options: Required<\r\n Pick<SessionOptions, \"cwd\" | \"maxToolRounds\" | \"reservedForOutput\" | \"preserveRecentRounds\">\r\n > & { projectContext?: string; gate: Gate; writeRoots: string[] };\r\n readonly #abortController = new AbortController();\r\n\r\n // 风暴检测:记录每轮的工具调用错误\r\n #stormRecords: ToolCallRecord[] = [];\r\n\r\n constructor(\r\n provider: Provider,\r\n tools: Tool[] | ToolRegistry = [],\r\n costTracker?: CostTracker,\r\n options?: SessionOptions,\r\n ) {\r\n this.#provider = provider;\r\n // 兼容 Tool[] 和 ToolRegistry 两种入参\r\n if (tools instanceof ToolRegistry) {\r\n this.#toolRegistry = tools;\r\n } else {\r\n this.#toolRegistry = new ToolRegistry();\r\n this.#toolRegistry.registerAll(tools);\r\n }\r\n this.#costTracker = costTracker ?? new CostTracker();\r\n this.#options = {\r\n cwd: options?.cwd ?? process.cwd(),\r\n maxToolRounds: options?.maxToolRounds ?? 20,\r\n reservedForOutput: options?.reservedForOutput ?? 4096,\r\n preserveRecentRounds: options?.preserveRecentRounds ?? 10,\r\n projectContext: options?.projectContext,\r\n gate: options?.gate ?? new AlwaysAllowGate(),\r\n writeRoots: options?.writeRoots ?? [options?.cwd ?? process.cwd()],\r\n };\r\n }\r\n\r\n // -------------------------------------------------------------------------\r\n // 公共只读属性\r\n // -------------------------------------------------------------------------\r\n\r\n get messages(): readonly ChatMessage[] {\r\n return this.#messages;\r\n }\r\n\r\n get accumulatedCost(): number {\r\n return this.#costTracker.sessionTotalCost;\r\n }\r\n\r\n get costTracker(): CostTracker {\r\n return this.#costTracker;\r\n }\r\n\r\n get model(): string {\r\n return this.#provider.model();\r\n }\r\n\r\n /** 获取工具注册表(只读视图) */\r\n get toolRegistry(): ToolRegistry {\r\n return this.#toolRegistry;\r\n }\r\n\r\n // -------------------------------------------------------------------------\r\n // 流式对话 — Agent 主循环\r\n // -------------------------------------------------------------------------\r\n\r\n /**\r\n * 执行一轮用户对话,以 AsyncGenerator 形式逐步 yield 事件。\r\n *\r\n * 主循环流程:\r\n * 1. 追加用户消息\r\n * 2. 进入 Agent 循环(最多 maxToolRounds 轮)\r\n * a. 构建消息 → 裁剪 → 调用 Provider 流式接口\r\n * b. 解析响应:文本增量、工具调用、使用量\r\n * c. 如果有工具调用 → 执行工具 → 追加结果 → 继续循环\r\n * d. 如果没有工具调用 → 退出循环\r\n * 3. yield done 事件\r\n */\r\n async *chat(userInput: string): AsyncGenerator<AgentEvent> {\r\n // 1. 追加用户消息\r\n this.#messages.push({ role: \"user\", content: userInput });\r\n\r\n // 2. 进入 Agent 循环\r\n const startTime = Date.now();\r\n let toolRounds = 0;\r\n\r\n try {\r\n while (toolRounds < this.#options.maxToolRounds) {\r\n // a. 构建消息\r\n const systemPrompt = this.#buildSystemPrompt();\r\n const [trimmed] = trimMessages(\r\n [...this.#messages],\r\n {\r\n model: this.#provider.model() as ModelId,\r\n reservedForOutput: this.#options.reservedForOutput,\r\n systemPrompt,\r\n preserveRecentRounds: this.#options.preserveRecentRounds,\r\n },\r\n );\r\n const apiMessages = buildApiMessages(systemPrompt, trimmed);\r\n\r\n // b. 调用 Provider 流式接口\r\n const toolDefs = this.#buildToolDefinitions();\r\n const stream = this.#provider.chat(apiMessages, {\r\n signal: this.#abortController.signal,\r\n tools: toolDefs.length > 0 ? toolDefs : undefined,\r\n });\r\n\r\n // c. 逐步解析流式响应\r\n let accumulatedText = \"\";\r\n let lastUsage: UsageInfo | undefined;\r\n let lastToolCalls: ProviderToolCall[] | undefined;\r\n let lastFinishReason: string | null = null;\r\n\r\n for await (const chunk of stream) {\r\n if (chunk.content) {\r\n accumulatedText += chunk.content;\r\n yield { type: \"text_delta\", content: chunk.content };\r\n }\r\n\r\n if (chunk.toolCalls && chunk.toolCalls.length > 0) {\r\n lastToolCalls = chunk.toolCalls;\r\n }\r\n\r\n if (chunk.usage) {\r\n lastUsage = chunk.usage;\r\n }\r\n\r\n if (chunk.finishReason) {\r\n lastFinishReason = chunk.finishReason;\r\n }\r\n }\r\n\r\n // d. 记录使用量与成本\r\n if (lastUsage) {\r\n const modelId = this.#provider.model() as ModelId;\r\n this.#costTracker.record(lastUsage, modelId);\r\n yield { type: \"usage\", usage: lastUsage, model: modelId };\r\n }\r\n\r\n // e. 追加助手消息到历史\r\n const assistantMsg: ChatMessage = {\r\n role: \"assistant\",\r\n content: accumulatedText,\r\n };\r\n if (lastToolCalls && lastToolCalls.length > 0) {\r\n assistantMsg.toolCalls = lastToolCalls;\r\n }\r\n this.#messages.push(assistantMsg);\r\n\r\n // f. 如果有工具调用,执行工具并继续循环\r\n if (lastToolCalls && lastToolCalls.length > 0) {\r\n yield { type: \"tool_calls\", calls: lastToolCalls };\r\n\r\n // 风暴检测:检查上一个回合是否同一工具同一错误重复\r\n const stormBroken = this.#checkStormBreak(lastToolCalls);\r\n if (stormBroken) {\r\n const stormMsg = \"\\n⚠️ 同一工具重复出错,已强制切换策略\\n\";\r\n yield { type: \"text_delta\", content: stormMsg };\r\n // 清除 assistant 消息中的 toolCalls,风暴中断不再执行这些调用\r\n assistantMsg.toolCalls = undefined;\r\n // 将风暴信息追加到助手消息文本中\r\n assistantMsg.content += stormMsg;\r\n // 重置风暴计数\r\n this.#stormRecords = [];\r\n toolRounds++;\r\n continue;\r\n }\r\n\r\n // 执行工具调用批次\r\n const results = await this.#executeBatch(lastToolCalls);\r\n this.#stormRecords = results.records;\r\n\r\n for (const item of results.items) {\r\n yield { type: \"tool_result\", name: item.name, result: item.result };\r\n\r\n // 构建 tool 消息内容\r\n let toolContent = item.result.data;\r\n if (item.result.diff && item.result.diff.patch) {\r\n toolContent += `\\n\\n${item.result.diff.patch}`;\r\n }\r\n\r\n this.#messages.push({\r\n role: \"tool\",\r\n content: toolContent,\r\n toolCallId: item.callId,\r\n name: item.name,\r\n });\r\n }\r\n\r\n toolRounds++;\r\n continue;\r\n }\r\n\r\n // 没有工具调用,退出循环\r\n break;\r\n }\r\n } catch (err: unknown) {\r\n if (err instanceof DOMException && err.name === \"AbortError\") {\r\n return;\r\n }\r\n if (err instanceof Error && err.name === \"AbortError\") {\r\n return;\r\n }\r\n\r\n yield {\r\n type: \"error\",\r\n error: err instanceof Error ? err : new Error(String(err)),\r\n };\r\n return;\r\n }\r\n\r\n const elapsed = Date.now() - startTime;\r\n yield { type: \"done\", elapsed };\r\n }\r\n\r\n // -------------------------------------------------------------------------\r\n // 工具执行 — 批量、并行/串行、Gate、风暴检测\r\n // -------------------------------------------------------------------------\r\n\r\n /**\r\n * 执行一批工具调用。\r\n *\r\n * 并行策略:\r\n * - 如果这批工具全部是 ReadOnly 的,并行执行(最多 8 并发)\r\n * - 否则按顺序串行执行,保证写/读顺序\r\n */\r\n async #executeBatch(calls: ProviderToolCall[]): Promise<{ items: Array<{ name: string; callId: string; result: ToolResult }>; records: ToolCallRecord[] }> {\r\n const toolCtx: ToolContext = {\r\n cwd: this.#options.cwd,\r\n signal: this.#abortController.signal,\r\n writeRoots: this.#options.writeRoots,\r\n };\r\n\r\n // 判断是否能并行:全部 ReadOnly\r\n const allReadOnly = calls.every((tc) => {\r\n const tool = this.#toolRegistry.get(tc.name);\r\n return tool?.readOnly === true;\r\n });\r\n\r\n if (allReadOnly && calls.length > 1) {\r\n // 并行执行 ReadOnly 工具\r\n const MAX_PARALLEL = 8;\r\n const items: Array<{ name: string; callId: string; result: ToolResult }> = [];\r\n const records: ToolCallRecord[] = [];\r\n\r\n // 分批并行,每批最多 MAX_PARALLEL 个\r\n for (let i = 0; i < calls.length; i += MAX_PARALLEL) {\r\n const batch = calls.slice(i, i + MAX_PARALLEL);\r\n const promises = batch.map((tc) => this.#executeOne(tc, toolCtx));\r\n const batchResults = await Promise.all(promises);\r\n for (const r of batchResults) {\r\n items.push(r.item);\r\n records.push(r.record);\r\n }\r\n }\r\n\r\n return { items, records };\r\n }\r\n\r\n // 串行执行\r\n const items: Array<{ name: string; callId: string; result: ToolResult }> = [];\r\n const records: ToolCallRecord[] = [];\r\n for (const tc of calls) {\r\n const r = await this.#executeOne(tc, toolCtx);\r\n items.push(r.item);\r\n records.push(r.record);\r\n }\r\n\r\n return { items, records };\r\n }\r\n\r\n /**\r\n * 执行单个工具调用,包含 Gate 检查和预览。\r\n */\r\n async #executeOne(\r\n tc: ProviderToolCall,\r\n ctx: ToolContext,\r\n ): Promise<{ item: { name: string; callId: string; result: ToolResult }; record: ToolCallRecord }> {\r\n const toolName = tc.name;\r\n const timestamp = Date.now();\r\n\r\n // 1. 查找工具\r\n const tool = this.#toolRegistry.get(toolName);\r\n if (!tool) {\r\n const errMsg = `工具 \"${toolName}\" 不存在或已被禁用`;\r\n return {\r\n item: { name: toolName, callId: tc.id, result: { success: false, data: errMsg, error: \"TOOL_NOT_FOUND\" } },\r\n record: { name: toolName, success: false, error: \"TOOL_NOT_FOUND\", timestamp },\r\n };\r\n }\r\n\r\n // 2. 解析参数\r\n let toolArgs: unknown;\r\n try {\r\n toolArgs = tc.arguments ? JSON.parse(tc.arguments) : {};\r\n } catch {\r\n toolArgs = {};\r\n }\r\n\r\n // 3. Gate 检查(权限门)\r\n const gateResult = await this.#options.gate.check(toolName, toolArgs);\r\n if (!gateResult) {\r\n const errMsg = `工具 \"${toolName}\" 被权限门拒绝`;\r\n return {\r\n item: { name: toolName, callId: tc.id, result: { success: false, data: errMsg, error: \"GATE_DENIED\" } },\r\n record: { name: toolName, success: false, error: \"GATE_DENIED\", timestamp },\r\n };\r\n }\r\n\r\n // 4. Previewer 预览(仅写工具,不触盘)\r\n if (!tool.readOnly) {\r\n const maybePreviewer = tool as unknown as { preview?: Previewer[\"preview\"] };\r\n if (typeof maybePreviewer.preview === \"function\") {\r\n try {\r\n await maybePreviewer.preview(toolArgs, ctx);\r\n } catch {\r\n // 预览失败不影响执行\r\n }\r\n }\r\n }\r\n\r\n // 5. 执行工具\r\n try {\r\n const result = await tool.execute(toolArgs, ctx);\r\n return {\r\n item: { name: toolName, callId: tc.id, result },\r\n record: {\r\n name: toolName,\r\n success: result.success,\r\n error: result.error,\r\n timestamp,\r\n },\r\n };\r\n } catch (err: unknown) {\r\n const message = err instanceof Error ? err.message : String(err);\r\n const errorResult = { success: false, data: `工具 \"${toolName}\" 执行异常:${message}`, error: \"EXECUTION_ERROR\" };\r\n return {\r\n item: { name: toolName, callId: tc.id, result: errorResult },\r\n record: { name: toolName, success: false, error: \"EXECUTION_ERROR\", timestamp },\r\n };\r\n }\r\n }\r\n\r\n // -------------------------------------------------------------------------\r\n // 风暴检测 — 同一工具同一错误连续 3 次 → 强制换策略\r\n // -------------------------------------------------------------------------\r\n\r\n /**\r\n * 连续 3 次同一工具同一错误 → 触发风暴中断。\r\n */\r\n #checkStormBreak(currentCalls: ProviderToolCall[]): boolean {\r\n if (this.#stormRecords.length < 3) return false;\r\n\r\n // 检查当前调用中是否有工具名与最近 3 次错误记录匹配\r\n const recentErrors = this.#stormRecords.slice(-3);\r\n if (recentErrors.length < 3) return false;\r\n\r\n // 检查最近 3 次是否同一工具同一错误\r\n const first = recentErrors[0] as ToolCallRecord;\r\n const allSame = recentErrors.every(\r\n (r) => r.name === first.name && r.error === first.error && !r.success,\r\n );\r\n\r\n if (!allSame) return false;\r\n\r\n // 检查当前调用是否还包含这个工具\r\n return currentCalls.some((tc) => tc.name === first.name);\r\n }\r\n\r\n // -------------------------------------------------------------------------\r\n // 会话管理\r\n // -------------------------------------------------------------------------\r\n\r\n /** 取消正在进行的流式请求 */\r\n abort(): void {\r\n this.#abortController.abort();\r\n }\r\n\r\n /** 重置会话历史(保留 provider/tools 配置,重置成本追踪) */\r\n reset(): void {\r\n this.#messages.length = 0;\r\n this.#costTracker.resetSession();\r\n this.#stormRecords = [];\r\n }\r\n\r\n // -------------------------------------------------------------------------\r\n // 内部方法\r\n // -------------------------------------------------------------------------\r\n\r\n /** 构建系统提示词 */\r\n #buildSystemPrompt(): string {\r\n const enabledTools = this.#toolRegistry.list();\r\n const toolDescs = enabledTools.map((t) => ({\r\n name: t.name,\r\n description: t.description,\r\n parameters: t.parameters as unknown as Record<string, unknown>,\r\n }));\r\n\r\n const opts: SystemPromptOptions = {\r\n model: this.#provider.model(),\r\n tools: toolDescs.length > 0 ? toolDescs : undefined,\r\n projectContext: this.#options.projectContext ?? undefined,\r\n cwd: this.#options.cwd,\r\n };\r\n\r\n return buildSystemPrompt(opts);\r\n }\r\n\r\n /** 将注册的工具转为 ToolDefinition 格式(预留给 function calling) */\r\n #buildToolDefinitions(): ToolDefinition[] {\r\n return this.#toolRegistry.list().map((t) => ({\r\n type: \"function\" as const,\r\n function: {\r\n name: t.name,\r\n description: t.description,\r\n parameters: t.parameters as unknown as Record<string, unknown>,\r\n },\r\n }));\r\n }\r\n}\r\n","// ---------------------------------------------------------------------------\r\n// 工具执行沙箱 — 路径约束、路径安全、超时控制、输出截断\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { resolve, relative, isAbsolute } from \"node:path\";\r\nimport { realpath } from \"node:fs/promises\";\r\nimport { spawn } from \"node:child_process\";\r\nimport process from \"node:process\";\r\n\r\n/** 沙箱默认配置 */\r\nconst DEFAULT_TIMEOUT_MS = 30_000;\r\nconst DEFAULT_MAX_OUTPUT_LENGTH = 50_000;\r\nconst DEFAULT_MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB\r\n\r\n/** 是否为 Windows 平台 */\r\nconst isWindows = process.platform === \"win32\";\r\n\r\n// ---------------------------------------------------------------------------\r\n// 路径安全 — confine / resolveIn / realPath\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * 将路径解析为绝对路径。\r\n * 相对路径基于 cwd 解析,绝对路径原样返回。\r\n */\r\nexport function resolvePath(inputPath: string, cwd: string): string {\r\n const resolved = isAbsolute(inputPath) ? inputPath : resolve(cwd, inputPath);\r\n return resolve(resolved);\r\n}\r\n\r\n/**\r\n * 获取路径的真实路径(解析符号链接)。\r\n * 如果路径不存在,则对其父目录逐级解析,尽可能获取真实路径。\r\n */\r\nasync function realPath(target: string): Promise<string> {\r\n try {\r\n return await realpath(target);\r\n } catch {\r\n // 路径可能还不存在(新建文件),尝试解析父目录\r\n const parent = resolve(target, \"..\");\r\n try {\r\n const realParent = await realpath(parent);\r\n return resolve(realParent, relative(parent, target));\r\n } catch {\r\n // 父目录也不存在,返回原路径\r\n return resolve(target);\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * 检查目标路径是否在允许的根目录范围内。\r\n *\r\n * @param allowedRoots 允许的根目录列表(绝对路径)\r\n * @param target 要检查的目标路径(绝对路径)\r\n * @returns 如果目标在范围内返回 true,否则返回 false 及错误信息\r\n */\r\nexport async function confine(\r\n allowedRoots: string[],\r\n target: string,\r\n): Promise<{ ok: true } | { ok: false; error: string }> {\r\n if (allowedRoots.length === 0) {\r\n return { ok: true }; // 没有限制,默认允许\r\n }\r\n\r\n const realTarget = await realPath(target);\r\n\r\n for (const root of allowedRoots) {\r\n const realRoot = await realPath(root);\r\n // 检查 target 是否在 root 之下\r\n const rel = relative(realRoot, realTarget);\r\n if (!rel.startsWith(\"..\") && rel !== \"\" && !rel.startsWith(\"/\") && !rel.startsWith(\"\\\\\")) {\r\n return { ok: true };\r\n }\r\n // 允许 target 恰好等于 root\r\n if (realTarget === realRoot) {\r\n return { ok: true };\r\n }\r\n }\r\n\r\n return {\r\n ok: false,\r\n error: `路径 \"${target}\" 不在允许的写入范围内 ${allowedRoots.join(\", \")}`,\r\n };\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 输出截断\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * 截断过长的输出内容。\r\n */\r\nexport function truncateOutput(content: string, maxLength = DEFAULT_MAX_OUTPUT_LENGTH): string {\r\n if (content.length <= maxLength) return content;\r\n const truncated = content.slice(0, maxLength);\r\n return `${truncated}\\n\\n... [输出过长,已截断,共 ${String(content.length)} 字符]`;\r\n}\r\n\r\n/**\r\n * 获取默认超时时间。\r\n */\r\nexport function getDefaultTimeout(): number {\r\n return DEFAULT_TIMEOUT_MS;\r\n}\r\n\r\n/**\r\n * 获取默认最大文件大小。\r\n */\r\nexport function getDefaultMaxFileSize(): number {\r\n return DEFAULT_MAX_FILE_SIZE;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 超时控制\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * 创建一个带超时的 AbortController。\r\n */\r\nexport function createTimeoutSignal(signal?: AbortSignal, timeoutMs = DEFAULT_TIMEOUT_MS): AbortController {\r\n const controller = new AbortController();\r\n\r\n // 外部信号触发时,联动中止\r\n if (signal) {\r\n signal.addEventListener(\"abort\", () => {\r\n controller.abort();\r\n }, { once: true });\r\n }\r\n\r\n // 超时自动中止\r\n const timer = setTimeout(() => {\r\n controller.abort();\r\n }, timeoutMs);\r\n controller.signal.addEventListener(\"abort\", () => {\r\n clearTimeout(timer);\r\n }, { once: true });\r\n\r\n return controller;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Shell 命令执行\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * 执行 shell 命令的通用封装。\r\n */\r\nexport async function execCommand(\r\n command: string,\r\n args: string[],\r\n cwd: string,\r\n timeoutMs = DEFAULT_TIMEOUT_MS,\r\n signal?: AbortSignal,\r\n /** 如果为 true,表示 command 已经是 shell 程序(如 cmd/sh),不需要再包装 */\r\n isShellCommand?: boolean,\r\n): Promise<{ stdout: string; stderr: string; exitCode: number | null }> {\r\n return new Promise((resolve) => {\r\n let spawnCmd: string;\r\n let spawnArgs: string[];\r\n let useShell: boolean;\r\n\r\n if (isShellCommand) {\r\n spawnCmd = command;\r\n spawnArgs = args;\r\n useShell = false;\r\n } else {\r\n useShell = !isWindows;\r\n spawnCmd = isWindows ? \"cmd\" : command;\r\n spawnArgs = isWindows\r\n ? [\"/c\", command, ...args]\r\n : args;\r\n }\r\n\r\n const child = spawn(spawnCmd, spawnArgs, {\r\n cwd,\r\n shell: useShell,\r\n env: { ...process.env },\r\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\r\n });\r\n\r\n let stdout = \"\";\r\n let stderr = \"\";\r\n\r\n child.stdout.on(\"data\", (data: Buffer) => {\r\n stdout += data.toString();\r\n });\r\n\r\n child.stderr.on(\"data\", (data: Buffer) => {\r\n stderr += data.toString();\r\n });\r\n\r\n const timeout = setTimeout(() => {\r\n child.kill(\"SIGTERM\");\r\n // 给 5 秒优雅退出时间\r\n setTimeout(() => {\r\n child.kill(\"SIGKILL\");\r\n }, 5000);\r\n }, timeoutMs);\r\n\r\n // 外部中止信号\r\n if (signal) {\r\n if (signal.aborted) {\r\n child.kill(\"SIGTERM\");\r\n }\r\n signal.addEventListener(\"abort\", () => {\r\n child.kill(\"SIGTERM\");\r\n }, { once: true });\r\n }\r\n\r\n child.on(\"close\", (code) => {\r\n clearTimeout(timeout);\r\n resolve({ stdout, stderr, exitCode: code });\r\n });\r\n\r\n child.on(\"error\", (err) => {\r\n clearTimeout(timeout);\r\n stderr += `\\n进程启动失败:${err.message}`;\r\n resolve({ stdout, stderr, exitCode: null });\r\n });\r\n });\r\n}\r\n","// ---------------------------------------------------------------------------\r\n// 文件差异计算 — Myers O(ND) 算法 + unified patch 生成\r\n//\r\n// Myers 算法与 Git 的 diff 引擎同款,产生最短编辑脚本(SES)。\r\n// 时间复杂度 O((N+M)D),空间复杂度 O(N+M),其中 D 为编辑距离。\r\n//\r\n// 实现方式:非递归的 Myers 差分算法。\r\n// 1. 正向搜索:找到从 (0,0) 到 (N,M) 的最短编辑路径\r\n// 2. 回溯路径:从 (N,M) 回溯到 (0,0),生成 DiffLine 序列\r\n// ---------------------------------------------------------------------------\r\n\r\nimport type { FileDiff } from \"./types.js\";\r\n\r\n// ---------------------------------------------------------------------------\r\n// 内部类型\r\n// ---------------------------------------------------------------------------\r\n\r\n/** 单行 diff 操作类型 */\r\ntype Op = \"equal\" | \"add\" | \"remove\";\r\n\r\n/** 一行 diff 记录 */\r\ninterface DiffLine {\r\n op: Op;\r\n line: string;\r\n}\r\n\r\n/** unified diff 中的一个 hunk */\r\ninterface DiffHunk {\r\n oldStart: number;\r\n oldCount: number;\r\n newStart: number;\r\n newCount: number;\r\n lines: DiffLine[];\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// EOL(行尾风格)检测\r\n// ---------------------------------------------------------------------------\r\n\r\ntype EOFStyle = \"lf\" | \"crlf\" | \"no-eol\";\r\n\r\ninterface SplitResult {\r\n lines: string[];\r\n eol: string;\r\n eofStyle: EOFStyle;\r\n}\r\n\r\nfunction splitLines(text: string): SplitResult {\r\n if (text.length === 0) {\r\n return { lines: [], eol: \"\\n\", eofStyle: \"no-eol\" };\r\n }\r\n\r\n const crlfIdx = text.indexOf(\"\\r\\n\");\r\n const lfIdx = text.indexOf(\"\\n\");\r\n let eol: string;\r\n if (crlfIdx !== -1 && (lfIdx === -1 || crlfIdx <= lfIdx)) {\r\n eol = \"\\r\\n\";\r\n } else if (lfIdx !== -1) {\r\n eol = \"\\n\";\r\n } else {\r\n eol = \"\\n\";\r\n }\r\n\r\n const lines = text.split(eol);\r\n\r\n let eofStyle: EOFStyle;\r\n if (text.endsWith(eol)) {\r\n if (lines.length > 0 && lines[lines.length - 1] === \"\") {\r\n lines.pop();\r\n }\r\n eofStyle = \"lf\";\r\n } else {\r\n eofStyle = \"no-eol\";\r\n }\r\n\r\n return { lines, eol, eofStyle };\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Myers 非递归差分算法\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * 计算两个行数组之间的最短编辑脚本(SES)。\r\n *\r\n * 使用非递归 Myers 算法:\r\n * 1. 正向搜索 V 数组,记录每步的进度\r\n * 2. 到达终点后回溯路径生成 DiffLine 列表\r\n */\r\nfunction computeLineDiff(oldLines: string[], newLines: string[]): DiffLine[] {\r\n const N = oldLines.length;\r\n const M = newLines.length;\r\n\r\n if (N === 0 && M === 0) return [];\r\n if (N === 0) return newLines.map((line) => ({ op: \"add\" as Op, line }));\r\n if (M === 0) return oldLines.map((line) => ({ op: \"remove\" as Op, line }));\r\n\r\n // 存储每一步的 V 快照,用于回溯\r\n const trace: Map<number, number>[] = [];\r\n\r\n const maxD = N + M;\r\n // V[k] = x position;初始化为 -1 表示未访问\r\n const V: number[] = new Array(2 * maxD + 2).fill(-1);\r\n // k 的偏移量,使索引非负\r\n const offset = maxD;\r\n\r\n V[offset + 1] = 0;\r\n\r\n // 正向搜索:从 d=0 开始,逐层扩展\r\n outer:\r\n for (let d = 0; d <= maxD; d++) {\r\n // 保存当前层的 V 快照\r\n const snapshot = new Map<number, number>();\r\n\r\n const kStart = -d;\r\n const kEnd = d;\r\n\r\n for (let k = kStart; k <= kEnd; k += 2) {\r\n const ki = k + offset;\r\n let x: number;\r\n\r\n if (k === -d || (k !== d && V[ki - 1]! < V[ki + 1]!)) {\r\n x = V[ki + 1]!;\r\n } else {\r\n x = V[ki - 1]! + 1;\r\n }\r\n\r\n let y = x - k;\r\n\r\n // 沿着对角线延伸\r\n while (x < N && y < M && oldLines[x] === newLines[y]) {\r\n x++;\r\n y++;\r\n }\r\n\r\n V[ki] = x;\r\n snapshot.set(k, x);\r\n\r\n // 到达终点\r\n if (x >= N && y >= M) {\r\n trace.push(snapshot);\r\n // 回溯路径\r\n return backtrack(trace, N, M, oldLines, newLines, offset);\r\n }\r\n }\r\n\r\n trace.push(snapshot);\r\n }\r\n\r\n // 不应该到达这里(总有解)\r\n return oldLines.map((line) => ({ op: \"remove\" as Op, line }))\r\n .concat(newLines.map((line) => ({ op: \"add\" as Op, line })));\r\n}\r\n\r\n/**\r\n * 从 trace 中回溯最短编辑路径,生成 DiffLine 列表。\r\n *\r\n * trace 存储了每一层 (d) 的 V 快照。\r\n * 从终点 (N, M) 开始,逆着路径逐层回溯到 (0, 0)。\r\n */\r\nfunction backtrack(\r\n trace: Map<number, number>[],\r\n N: number,\r\n M: number,\r\n oldLines: string[],\r\n newLines: string[],\r\n offset: number,\r\n): DiffLine[] {\r\n const result: DiffLine[] = [];\r\n\r\n let x = N;\r\n let y = M;\r\n\r\n // 从最后一层回溯到第 1 层(第 0 层只有初始蛇行,没有实际移动)\r\n for (let d = trace.length - 1; d >= 1; d--) {\r\n const k = x - y;\r\n\r\n // 从 trace[d-1] 中查找上一步的 x 位置\r\n // 注意:trace[d] 存的是本层结果,前一层的数据在 trace[d-1] 中\r\n const prevSnapshot = d > 0 ? (trace[d - 1] as Map<number, number>) : new Map();\r\n const prevKDown = prevSnapshot.get(k - 1);\r\n const prevKUp = prevSnapshot.get(k + 1);\r\n\r\n let prevK: number;\r\n if (k === -d) {\r\n // 只能在 k+1 方向\r\n prevK = k + 1;\r\n } else if (k === d) {\r\n // 只能在 k-1 方向\r\n prevK = k - 1;\r\n } else if ((prevKDown ?? -1) < (prevKUp ?? -1)) {\r\n // k+1 方向的 V 值更大,从那里过来(删除)\r\n prevK = k + 1;\r\n } else {\r\n // k-1 方向的 V 值更大,从那里过来(添加)\r\n prevK = k - 1;\r\n }\r\n\r\n const prevX = prevSnapshot.get(prevK) ?? 0;\r\n const prevY = prevX - prevK;\r\n\r\n // ===== 从 (prevX, prevY) 到 (x, y) 的回溯路径 =====\r\n\r\n if (prevK === k - 1) {\r\n // prevK = k-1 → V[k-1] 被使用 → x = V[k-1] + 1 → 垂直移动(删除)\r\n // 路径: (prevX, prevY) → (prevX+1, prevY) [删除] → 蛇行 [(prevX+2, prevY+1), ...]\r\n // 回溯: 先走蛇行(相等行), 再走删除行\r\n\r\n // 第一步:蛇行(相等行),从 (x,y) 回到 (prevX+1, prevY)\r\n while (x > prevX + 1 && y > prevY) {\r\n x--;\r\n y--;\r\n result.unshift({ op: \"equal\", line: oldLines[x] as string });\r\n }\r\n\r\n // 第二步:删除行\r\n if (x > prevX) {\r\n x--;\r\n result.unshift({ op: \"remove\", line: oldLines[x] as string });\r\n }\r\n } else {\r\n // prevK = k+1 → V[k+1] 被使用 → x = V[k+1] → 水平移动(添加)\r\n // 路径: (prevX, prevY) → (prevX, prevY+1) [添加] → 蛇行 [(prevX+1, prevY+2), ...]\r\n // 回溯: 先走蛇行(相等行), 再走添加行\r\n\r\n // 第一步:蛇行(相等行),从 (x,y) 回到 (prevX, prevY+1)\r\n while (x > prevX && y > prevY + 1) {\r\n x--;\r\n y--;\r\n result.unshift({ op: \"equal\", line: oldLines[x] as string });\r\n }\r\n\r\n // 第二步:添加行\r\n if (y > prevY) {\r\n y--;\r\n result.unshift({ op: \"add\", line: newLines[y] as string });\r\n }\r\n }\r\n }\r\n\r\n // 处理初始蛇行:从 (0, 0) 到当前 (x, y) 的相等行\r\n // 逆向(从 (x, y) 到 (1, 1) 到 (0, 0) 回溯)\r\n while (x > 0 && y > 0) {\r\n x--;\r\n y--;\r\n result.unshift({ op: \"equal\", line: oldLines[x] as string });\r\n }\r\n\r\n return result;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Hunk 分组\r\n// ---------------------------------------------------------------------------\r\n\r\n/** 上下文行数(unified diff 标准 3 行) */\r\nconst CONTEXT_LINES = 3;\r\n\r\n/**\r\n * 将 diff 行列表分组为 hunks。\r\n */\r\nfunction groupIntoHunks(diffLines: DiffLine[]): DiffHunk[] {\r\n if (diffLines.length === 0) return [];\r\n\r\n // 找出所有变更行的索引\r\n const changeIndices: number[] = [];\r\n for (let i = 0; i < diffLines.length; i++) {\r\n const line = diffLines[i] as DiffLine;\r\n if (line.op !== \"equal\") {\r\n changeIndices.push(i);\r\n }\r\n }\r\n\r\n if (changeIndices.length === 0) return [];\r\n\r\n // 将相近的变更行分组\r\n const groups: number[][] = [[changeIndices[0] as number]];\r\n\r\n for (let idx = 1; idx < changeIndices.length; idx++) {\r\n const lastGroup = groups[groups.length - 1] as number[];\r\n const prevIdx = lastGroup[lastGroup.length - 1] as number;\r\n const currIdx = changeIndices[idx] as number;\r\n\r\n let gap = 0;\r\n for (let k = prevIdx + 1; k < currIdx; k++) {\r\n const line = diffLines[k] as DiffLine;\r\n if (line.op === \"equal\") gap++;\r\n }\r\n\r\n if (gap <= 2 * CONTEXT_LINES) {\r\n lastGroup.push(currIdx);\r\n } else {\r\n groups.push([currIdx]);\r\n }\r\n }\r\n\r\n // 为每个分组生成 hunk\r\n const hunks: DiffHunk[] = [];\r\n\r\n for (const group of groups) {\r\n const firstChangeIdx = group[0] as number;\r\n const lastChangeIdx = group[group.length - 1] as number;\r\n\r\n const startIdx = Math.max(0, firstChangeIdx - CONTEXT_LINES);\r\n const endIdx = Math.min(diffLines.length - 1, lastChangeIdx + CONTEXT_LINES);\r\n\r\n const lines = diffLines.slice(startIdx, endIdx + 1);\r\n\r\n let oldLine = 0;\r\n let newLine = 0;\r\n for (let i = 0; i < startIdx; i++) {\r\n const line = diffLines[i] as DiffLine;\r\n if (line.op === \"equal\" || line.op === \"remove\") oldLine++;\r\n if (line.op === \"equal\" || line.op === \"add\") newLine++;\r\n }\r\n\r\n let oldCount = 0;\r\n let newCount = 0;\r\n for (const diffLine of lines) {\r\n if (diffLine.op === \"equal\" || diffLine.op === \"remove\") oldCount++;\r\n if (diffLine.op === \"equal\" || diffLine.op === \"add\") newCount++;\r\n }\r\n\r\n hunks.push({\r\n oldStart: oldLine + 1,\r\n oldCount,\r\n newStart: newLine + 1,\r\n newCount,\r\n lines,\r\n });\r\n }\r\n\r\n return hunks;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Unified diff 格式化\r\n// ---------------------------------------------------------------------------\r\n\r\nfunction formatUnifiedDiff(hunks: DiffHunk[], filePath: string): string {\r\n if (hunks.length === 0) return \"\";\r\n\r\n const parts: string[] = [];\r\n const fileName = extractFileName(filePath);\r\n\r\n parts.push(`--- a/${fileName}`);\r\n parts.push(`+++ b/${fileName}`);\r\n\r\n for (const hunk of hunks) {\r\n parts.push(\r\n `@@ -${String(hunk.oldStart)},${String(hunk.oldCount)} +${String(hunk.newStart)},${String(hunk.newCount)} @@`,\r\n );\r\n\r\n for (const diffLine of hunk.lines) {\r\n switch (diffLine.op) {\r\n case \"equal\":\r\n parts.push(` ${diffLine.line}`);\r\n break;\r\n case \"remove\":\r\n parts.push(`-${diffLine.line}`);\r\n break;\r\n case \"add\":\r\n parts.push(`+${diffLine.line}`);\r\n break;\r\n }\r\n }\r\n }\r\n\r\n return parts.join(\"\\n\");\r\n}\r\n\r\nfunction formatNewFileDiff(lines: string[], filePath: string): string {\r\n const fileName = extractFileName(filePath);\r\n const parts: string[] = [];\r\n\r\n parts.push(`--- /dev/null`);\r\n parts.push(`+++ b/${fileName}`);\r\n parts.push(`@@ -0,0 +1,${String(lines.length)} @@`);\r\n\r\n for (const line of lines) {\r\n parts.push(`+${line}`);\r\n }\r\n\r\n return parts.join(\"\\n\");\r\n}\r\n\r\nfunction formatDeletedFileDiff(lines: string[], filePath: string): string {\r\n const fileName = extractFileName(filePath);\r\n const parts: string[] = [];\r\n\r\n parts.push(`--- a/${fileName}`);\r\n parts.push(`+++ /dev/null`);\r\n parts.push(`@@ -1,${String(lines.length)} +0,0 @@`);\r\n\r\n for (const line of lines) {\r\n parts.push(`-${line}`);\r\n }\r\n\r\n return parts.join(\"\\n\");\r\n}\r\n\r\nfunction extractFileName(filePath: string): string {\r\n const normalized = filePath.replace(/\\\\/g, \"/\");\r\n return normalized.split(\"/\").pop() ?? filePath;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 公共 API\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * 计算两个文本之间的 unified diff,返回 FileDiff 对象。\r\n *\r\n * 使用 Myers O(ND) 算法产生最短编辑脚本,与 Git 的 diff 引擎同款。\r\n * 自动检测并保留原始文件的行尾风格(LF / CRLF)。\r\n */\r\nexport function computeFileDiff(\r\n oldContent: string,\r\n newContent: string,\r\n filePath: string,\r\n): FileDiff {\r\n if (oldContent === newContent) {\r\n return {\r\n filePath,\r\n patch: \"\",\r\n existedBefore: oldContent.length > 0,\r\n additions: 0,\r\n deletions: 0,\r\n };\r\n }\r\n\r\n if (oldContent.length === 0) {\r\n const split = splitLines(newContent);\r\n const patch = formatNewFileDiff(split.lines, filePath);\r\n return {\r\n filePath,\r\n patch,\r\n existedBefore: false,\r\n additions: split.lines.length,\r\n deletions: 0,\r\n };\r\n }\r\n\r\n if (newContent.length === 0) {\r\n const split = splitLines(oldContent);\r\n const patch = formatDeletedFileDiff(split.lines, filePath);\r\n return {\r\n filePath,\r\n patch,\r\n existedBefore: true,\r\n additions: 0,\r\n deletions: split.lines.length,\r\n };\r\n }\r\n\r\n const oldSplit = splitLines(oldContent);\r\n const newSplit = splitLines(newContent);\r\n\r\n const diffLines = computeLineDiff(oldSplit.lines, newSplit.lines);\r\n\r\n let additions = 0;\r\n let deletions = 0;\r\n for (const line of diffLines) {\r\n if (line.op === \"add\") additions++;\r\n if (line.op === \"remove\") deletions++;\r\n }\r\n\r\n const hunks = groupIntoHunks(diffLines);\r\n const patch = formatUnifiedDiff(hunks, filePath);\r\n\r\n return {\r\n filePath,\r\n patch,\r\n existedBefore: true,\r\n additions,\r\n deletions,\r\n };\r\n}\r\n\r\n/**\r\n * 将文件变更应用于原内容,返回新的文件内容。\r\n */\r\nexport function applyChange(\r\n kind: \"edit\" | \"create\" | \"delete\",\r\n oldContent: string,\r\n newContent: string,\r\n): string {\r\n if (kind === \"delete\") return \"\";\r\n if (kind === \"create\") return newContent;\r\n return newContent;\r\n}\r\n","// ---------------------------------------------------------------------------\r\n// 行尾(EOL)风格检测与保留 — 写工具统一使用此模块\r\n// ---------------------------------------------------------------------------\r\n//\r\n// 解决问题:原文件为 CRLF(Windows 项目常态)时,写工具若直接 writeFile\r\n// 新内容(往往以 LF 编码),会导致整文件 EOL 翻转,git diff 全文件飘红,\r\n// AI 看到大量\"假改动\"后陷入修复循环。此模块提供公共的 EOL 检测与落盘封装。\r\n//\r\n// 设计要点:\r\n// - 检测策略与 diff.ts 的 splitLines 保持一致:首个 CRLF 早于首个 LF → CRLF\r\n// - 对原内容做 EOL 探测,按探测结果统一新内容的行尾\r\n// - 保留原文件是否以行尾符结尾(trailing newline)的特征\r\n\r\nimport { writeFile } from \"node:fs/promises\";\r\n\r\n/**\r\n * 检测文本的行尾风格。\r\n *\r\n * 与 diff.ts 的 splitLines 探测策略一致:\r\n * - 找到最早出现的换行符位置;若最早的是 \\r\\n 则判为 CRLF,否则 LF\r\n *\r\n * @returns 行尾字符串:\"\\r\\n\" 或 \"\\n\";空文本默认 \"\\n\"\r\n */\r\nexport function detectEol(text: string): string {\r\n if (text.length === 0) return \"\\n\";\r\n\r\n const crlfIdx = text.indexOf(\"\\r\\n\");\r\n const lfIdx = text.indexOf(\"\\n\");\r\n\r\n if (crlfIdx !== -1 && (lfIdx === -1 || crlfIdx <= lfIdx)) {\r\n return \"\\r\\n\";\r\n }\r\n return \"\\n\";\r\n}\r\n\r\n/**\r\n * 检测文本是否以行尾符结尾(trailing newline)。\r\n */\r\nexport function hasTrailingNewline(text: string): boolean {\r\n return text.endsWith(\"\\n\") || text.endsWith(\"\\r\\n\");\r\n}\r\n\r\n/**\r\n * 按给定 EOL 规整文本,并保留与原内容一致的\"是否以行尾结尾\"特征。\r\n *\r\n * 规整步骤:\r\n * 1. 先统一拆成行片段(兼容现有 \\r\\n 与 \\n 混合)\r\n * 2. 用目标 EOL 重新拼接\r\n * 3. 若原内容以行尾结尾但规整后没有,补一个;反之不补\r\n *\r\n * 用于写入前对 newContent 做归一化,保证不在 EOL 上产生噪声 diff。\r\n *\r\n * @param originalContent 原文件内容(用于探测 EOL 风格与尾行尾特征)\r\n * @param newContent 待写入的新内容\r\n * @returns 应实际落盘的内容\r\n */\r\nexport function normalizeEol(originalContent: string, newContent: string): string {\r\n const targetEol = detectEol(originalContent);\r\n\r\n // 拆行:兼容混合 \\r\\n / \\n;先把 \\r\\n 统一成 \\n 再按 \\n 拆\r\n const lines = newContent.replace(/\\r\\n/g, \"\\n\").split(\"\\n\");\r\n\r\n // 判断原内容是否以行尾符结尾\r\n const originalHasTrailing = hasTrailingNewline(originalContent);\r\n\r\n // newContent 经 replace+split 后,行尾结尾会在末尾多一个空行元素\r\n // 如 \"a\\nb\\n\" → [\"a\",\"b\",\"\"],\"a\\nb\" → [\"a\",\"b\"]\r\n const newHasTrailing = lines.length > 0 && lines[lines.length - 1] === \"\";\r\n\r\n // 若新内容原本就带尾行尾,split 出的末尾空字符串保留即可\r\n // 若新内容不带尾行尾,但原内容带,则补一个\r\n let result = lines.join(targetEol);\r\n if (originalHasTrailing && !newHasTrailing) {\r\n result += targetEol;\r\n }\r\n // 若新内容带尾行尾但原内容不带:去掉末尾 EOL\r\n if (!originalHasTrailing && newHasTrailing) {\r\n // result 此时末尾恰有一个 targetEol,截悼\r\n if (result.endsWith(targetEol)) {\r\n result = result.slice(0, -targetEol.length);\r\n }\r\n }\r\n\r\n return result;\r\n}\r\n\r\n/**\r\n * 写文件并保留原文件的 EOL 风格与尾行尾特征。\r\n *\r\n * - 原内容存在时:按原 EOL 归一化新内容后写入\r\n * - 原内容不存在(新建文件):LF + 保留新内容自带的尾行尾特征\r\n *\r\n * 所有写工具(write_file / edit_file / multi_edit / delete_range)应统一走此函数,\r\n * 避免 EOL 翻转造成的全文件 diff 风暴。\r\n *\r\n * @param filePath 目标文件绝对路径\r\n * @param originalContent 原文件内容;文件不存在时传空字符串\r\n * @param newContent 新内容\r\n */\r\nexport async function writeFileWithEol(\r\n filePath: string,\r\n originalContent: string,\r\n newContent: string,\r\n): Promise<void> {\r\n const content =\r\n originalContent.length > 0\r\n ? normalizeEol(originalContent, newContent)\r\n : newContent;\r\n await writeFile(filePath, content, \"utf-8\");\r\n}","// ---------------------------------------------------------------------------\r\n// read_file 工具 — 读取指定路径的文件内容\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { readFile, stat } from \"node:fs/promises\";\r\nimport { open } from \"node:fs/promises\";\r\nimport { relative } from \"node:path\";\r\nimport type { Tool, ToolContext, ToolResult, JSONSchema } from \"../types.js\";\r\nimport { resolvePath, truncateOutput } from \"../sandbox.js\";\r\n\r\n/** read_file 工具的参数格式 */\r\ninterface ReadFileArgs {\r\n /** 文件路径(相对于 cwd 或绝对路径) */\r\n path: string;\r\n /** 起始行号(1-based),默认 1 */\r\n startLine?: number;\r\n /** 结束行号(1-based,包含),默认到文件末尾 */\r\n endLine?: number;\r\n}\r\n\r\n/** read_file 工具的参数 JSON Schema */\r\nconst readFileSchema: JSONSchema = {\r\n type: \"object\",\r\n properties: {\r\n path: {\r\n type: \"string\",\r\n description: \"文件路径(相对于当前工作目录或绝对路径)\",\r\n },\r\n startLine: {\r\n type: \"number\",\r\n description: \"起始行号(从 1 开始),默认为 1\",\r\n },\r\n endLine: {\r\n type: \"number\",\r\n description: \"结束行号(包含),默认到文件末尾\",\r\n },\r\n },\r\n required: [\"path\"],\r\n additionalProperties: false,\r\n};\r\n\r\n/** 读取前 N 字节检测是否为二进制文件 */\r\nasync function checkBinary(filePath: string): Promise<boolean> {\r\n const fileHandle = await open(filePath, \"r\");\r\n try {\r\n const buffer = Buffer.alloc(8192); // 8KB\r\n const { bytesRead } = await fileHandle.read(buffer, 0, 8192, 0);\r\n return buffer.subarray(0, bytesRead).includes(0); // NUL 字节 → 二进制\r\n } finally {\r\n await fileHandle.close();\r\n }\r\n}\r\n\r\n/**\r\n * read_file 工具 — 读取文件内容,支持行号范围选择。\r\n *\r\n * 功能:\r\n * - 按行号范围读取部分内容\r\n * - 自动添加行号前缀(→ 分隔)\r\n * - 文件大小限制(默认 10MB)\r\n * - 输出长度截断(默认 50K 字符)\r\n * - 二进制检测(阻止读取二进制文件)\r\n */\r\nexport const readFileTool: Tool = {\r\n name: \"read_file\",\r\n description:\r\n \"读取指定路径的文件内容。支持行号范围选择,输出带行号。适用于查看源代码、配置文件等文本文件。自动拒绝二进制文件。\",\r\n parameters: readFileSchema,\r\n readOnly: true,\r\n\r\n async execute(args: unknown, ctx: ToolContext): Promise<ToolResult> {\r\n const params = args as ReadFileArgs;\r\n if (!params?.path || typeof params.path !== \"string\") {\r\n return { success: false, data: \"缺少必要参数 path\", error: \"INVALID_ARGS\" };\r\n }\r\n\r\n const filePath = resolvePath(params.path, ctx.cwd);\r\n\r\n try {\r\n // 检查文件大小\r\n const fileStat = await stat(filePath);\r\n const maxSize = 10 * 1024 * 1024; // 10MB\r\n if (fileStat.size > maxSize) {\r\n return {\r\n success: false,\r\n data: `文件过大(${(fileStat.size / 1024 / 1024).toFixed(1)}MB),超过 10MB 限制`,\r\n error: \"FILE_TOO_LARGE\",\r\n };\r\n }\r\n\r\n // 检查是否为目录\r\n if (fileStat.isDirectory()) {\r\n return {\r\n success: false,\r\n data: `\"${filePath}\" 是一个目录,请使用 ls 工具查看目录内容`,\r\n error: \"IS_DIRECTORY\",\r\n };\r\n }\r\n\r\n // 二进制检测:扫描前 8KB 看是否包含 NUL 字节\r\n if (fileStat.size > 0) {\r\n const isBin = await checkBinary(filePath);\r\n if (isBin) {\r\n return {\r\n success: false,\r\n data: `\"${filePath}\" 看起来是二进制文件,不支持读取`,\r\n error: \"BINARY_FILE\",\r\n };\r\n }\r\n }\r\n\r\n const content = await readFile(filePath, \"utf-8\");\r\n const lines = content.split(\"\\n\");\r\n\r\n // 如果是空文件,最后一项为空行,pop 掉\r\n if (lines.length > 0 && lines[lines.length - 1] === \"\" && content.endsWith(\"\\n\")) {\r\n lines.pop();\r\n }\r\n\r\n // 行号范围处理(1-based → 0-based)\r\n const startLine = Math.max(1, params.startLine ?? 1) - 1;\r\n const endLine = params.endLine\r\n ? Math.min(params.endLine, lines.length)\r\n : lines.length;\r\n const selectedLines = lines.slice(startLine, endLine);\r\n\r\n // 添加行号前缀(右对齐 + →)\r\n const lineNumWidth = String(endLine).length;\r\n const result = selectedLines\r\n .map((line, i) => {\r\n const lineNum = String(startLine + i + 1).padStart(lineNumWidth, \" \");\r\n return `${lineNum}→${line}`;\r\n })\r\n .join(\"\\n\");\r\n\r\n // 尾部提示:告知剩余行数\r\n const remaining = lines.length - endLine;\r\n const tailHint = remaining > 0\r\n ? `\\n\\n[还有 ${remaining} 行;使用 startLine=${endLine + 1} 继续查看]`\r\n : \"\";\r\n\r\n // UI 摘要:仅显示路径与行数范围,避免把文件内容塞进 UI\r\n const relPath = relative(ctx.cwd, filePath).replace(/\\\\/g, \"/\");\r\n const rangeLabel = (startLine > 0 || endLine < lines.length)\r\n ? `第 ${startLine + 1}-${endLine} 行`\r\n : `${lines.length} 行`;\r\n\r\n return {\r\n success: true,\r\n data: truncateOutput(result) + tailHint,\r\n summary: `📖 ${relPath}(${rangeLabel})`,\r\n };\r\n } catch (err: unknown) {\r\n const message = err instanceof Error ? err.message : String(err);\r\n return {\r\n success: false,\r\n data: `读取文件失败:${message}`,\r\n error: \"READ_ERROR\",\r\n };\r\n }\r\n },\r\n};\r\n","// ---------------------------------------------------------------------------\r\n// write_file 工具 — 创建或覆盖文件\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { mkdir, readFile } from \"node:fs/promises\";\r\nimport { dirname, relative, basename } from \"node:path\";\r\nimport type { Tool, ToolContext, ToolResult, JSONSchema } from \"../types.js\";\r\nimport { resolvePath, confine } from \"../sandbox.js\";\r\nimport { computeFileDiff } from \"../diff.js\";\r\nimport { writeFileWithEol } from \"../eol.js\";\r\n\r\n/** write_file 工具的参数格式 */\r\ninterface WriteFileArgs {\r\n /** 文件路径(相对于 cwd 或绝对路径) */\r\n path: string;\r\n /** 要写入的文件内容 */\r\n content: string;\r\n}\r\n\r\n/** write_file 工具的参数 JSON Schema */\r\nconst writeFileSchema: JSONSchema = {\r\n type: \"object\",\r\n properties: {\r\n path: {\r\n type: \"string\",\r\n description: \"文件路径(相对于当前工作目录或绝对路径)\",\r\n },\r\n content: {\r\n type: \"string\",\r\n description: \"要写入的文件内容\",\r\n },\r\n },\r\n required: [\"path\", \"content\"],\r\n additionalProperties: false,\r\n};\r\n\r\n/**\r\n * write_file 工具 — 创建或覆盖文件。\r\n *\r\n * 功能:\r\n * - 自动创建中间目录\r\n * - 返回写入的行数和字节数\r\n */\r\nexport const writeFileTool: Tool = {\r\n name: \"write_file\",\r\n description:\r\n \"创建或覆盖文件。如果父目录不存在会自动创建。适用于创建新文件或完全替换文件内容。请勿用此工具创建 _temp_、_debug_ 等用于诊断/调试的临时文件——如需诊断请改用 bash 工具内联脚本(node -e)。\",\r\n parameters: writeFileSchema,\r\n readOnly: false,\r\n\r\n async execute(args: unknown, ctx: ToolContext): Promise<ToolResult> {\r\n const params = args as WriteFileArgs;\r\n if (!params?.path || typeof params.path !== \"string\") {\r\n return { success: false, data: \"缺少必要参数 path\", error: \"INVALID_ARGS\" };\r\n }\r\n if (params.content === undefined || params.content === null) {\r\n return { success: false, data: \"缺少必要参数 content\", error: \"INVALID_ARGS\" };\r\n }\r\n\r\n const filePath = resolvePath(params.path, ctx.cwd);\r\n\r\n try {\r\n // 写入范围安全检查:确保目标在允许根目录内\r\n if (ctx.writeRoots && ctx.writeRoots.length > 0) {\r\n const conf = await confine(ctx.writeRoots, filePath);\r\n if (!conf.ok) {\r\n return { success: false, data: conf.error, error: \"OUTSIDE_WRITE_ROOTS\" };\r\n }\r\n }\r\n\r\n // 写入前快照旧内容(文件可能不存在)\r\n let oldContent = \"\";\r\n let existedBefore = false;\r\n try {\r\n oldContent = await readFile(filePath, \"utf-8\");\r\n existedBefore = true;\r\n } catch {\r\n // 文件不存在,这是新建文件\r\n }\r\n\r\n // 确保父目录存在\r\n await mkdir(dirname(filePath), { recursive: true });\r\n\r\n const content = String(params.content);\r\n // 按原文件 EOL 风格落盘,避免 CRLF/LF 翻转造成噪声 diff\r\n await writeFileWithEol(filePath, oldContent, content);\r\n\r\n // 计算文件变更 diff\r\n const diff = computeFileDiff(oldContent, content, filePath);\r\n diff.existedBefore = existedBefore;\r\n\r\n const lineCount = content.split(\"\\n\").length;\r\n const byteSize = Buffer.byteLength(content, \"utf-8\");\r\n\r\n // 构建包含 diff 摘要的返回信息\r\n const action = existedBefore ? \"已修改\" : \"已创建\";\r\n const diffSummary = existedBefore\r\n ? `,+${diff.additions} -${diff.deletions}`\r\n : `,+${diff.additions} 行(新建)`;\r\n\r\n // UI 一行摘要:状态 + 文件名 + 增删行数,避免暴露完整路径之外的信息\r\n const fileName = basename(filePath);\r\n const summary = existedBefore\r\n ? `📝 修改: ${fileName} (+${diff.additions} -${diff.deletions})`\r\n : `📝 新建: ${fileName} (+${diff.additions} 行)`;\r\n\r\n return {\r\n success: true,\r\n data: `文件${action}:${relative(ctx.cwd, filePath).replace(/\\\\/g, \"/\")}(${lineCount} 行,${byteSize} 字节${diffSummary})`,\r\n summary,\r\n diff,\r\n };\r\n } catch (err: unknown) {\r\n const message = err instanceof Error ? err.message : String(err);\r\n return {\r\n success: false,\r\n data: `写入文件失败:${message}`,\r\n error: \"WRITE_ERROR\",\r\n };\r\n }\r\n },\r\n};","// ---------------------------------------------------------------------------\r\n// edit_file 工具 — 精确字符串替换编辑文件\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { readFile } from \"node:fs/promises\";\r\nimport { basename } from \"node:path\";\r\nimport type { Tool, ToolContext, ToolResult, JSONSchema } from \"../types.js\";\r\nimport { resolvePath, confine } from \"../sandbox.js\";\r\nimport { computeFileDiff } from \"../diff.js\";\r\nimport { writeFileWithEol } from \"../eol.js\";\r\n\r\n/** edit_file 工具的参数格式 */\r\ninterface EditFileArgs {\r\n /** 文件路径(相对于 cwd 或绝对路径) */\r\n path: string;\r\n /** 要查找的原始文本(精确匹配) */\r\n old_text: string;\r\n /** 替换后的新文本 */\r\n new_text: string;\r\n}\r\n\r\n/** edit_file 工具的参数 JSON Schema */\r\nconst editFileSchema: JSONSchema = {\r\n type: \"object\",\r\n properties: {\r\n path: {\r\n type: \"string\",\r\n description: \"文件路径(相对于当前工作目录或绝对路径)\",\r\n },\r\n old_text: {\r\n type: \"string\",\r\n description: \"要查找的原始文本(精确匹配)\",\r\n },\r\n new_text: {\r\n type: \"string\",\r\n description: \"替换后的新文本\",\r\n },\r\n },\r\n required: [\"path\", \"old_text\", \"new_text\"],\r\n additionalProperties: false,\r\n};\r\n\r\n/**\r\n * edit_file 工具 — 对文件进行精确字符串替换。\r\n *\r\n * 功能:\r\n * - 精确匹配 old_text 并替换为 new_text\r\n * - 如果 old_text 在文件中出现多次则报错(避免误改)\r\n * - 如果 old_text 未找到则报错\r\n * - 返回替换前后的上下文信息\r\n */\r\nexport const editFileTool: Tool = {\r\n name: \"edit_file\",\r\n description:\r\n \"对文件进行精确字符串替换。查找文件中的 old_text 并替换为 new_text。如果 old_text 出现多次或未找到则报错。适用于小范围精确修改。\",\r\n parameters: editFileSchema,\r\n readOnly: false,\r\n\r\n async execute(args: unknown, ctx: ToolContext): Promise<ToolResult> {\r\n const params = args as EditFileArgs;\r\n if (!params?.path || typeof params.path !== \"string\") {\r\n return { success: false, data: \"缺少必要参数 path\", error: \"INVALID_ARGS\" };\r\n }\r\n if (typeof params.old_text !== \"string\") {\r\n return { success: false, data: \"缺少必要参数 old_text\", error: \"INVALID_ARGS\" };\r\n }\r\n if (typeof params.new_text !== \"string\") {\r\n return { success: false, data: \"缺少必要参数 new_text\", error: \"INVALID_ARGS\" };\r\n }\r\n\r\n const filePath = resolvePath(params.path, ctx.cwd);\r\n\r\n // 写入范围安全检查:写之前先拦,避免在只读路径上浪费时间\r\n if (ctx.writeRoots && ctx.writeRoots.length > 0) {\r\n const conf = await confine(ctx.writeRoots, filePath);\r\n if (!conf.ok) {\r\n return { success: false, data: conf.error, error: \"OUTSIDE_WRITE_ROOTS\" };\r\n }\r\n }\r\n\r\n try {\r\n const content = await readFile(filePath, \"utf-8\");\r\n\r\n // 检查 old_text 是否存在\r\n const firstIndex = content.indexOf(params.old_text);\r\n if (firstIndex === -1) {\r\n return {\r\n success: false,\r\n data: `未找到要替换的文本。请确认 old_text 与文件内容完全一致(包括缩进和空格)。`,\r\n error: \"TEXT_NOT_FOUND\",\r\n };\r\n }\r\n\r\n // 检查是否出现多次\r\n const secondIndex = content.indexOf(params.old_text, firstIndex + 1);\r\n if (secondIndex !== -1) {\r\n return {\r\n success: false,\r\n data: `要替换的文本在文件中出现多次,请提供更多上下文以精确定位。`,\r\n error: \"TEXT_MULTIPLE_MATCHES\",\r\n };\r\n }\r\n\r\n // 执行替换\r\n const newContent = content.replace(params.old_text, params.new_text);\r\n // 按原文件 EOL 风格落盘,避免 CRLF/LF 翻转\r\n await writeFileWithEol(filePath, content, newContent);\r\n\r\n // 计算文件变更 diff\r\n const diff = computeFileDiff(content, newContent, filePath);\r\n diff.existedBefore = true;\r\n\r\n // 计算替换位置的行号\r\n const beforeText = content.slice(0, firstIndex);\r\n const startLine = beforeText.split(\"\\n\").length;\r\n const oldLines = params.old_text.split(\"\\n\").length;\r\n const newLines = params.new_text.split(\"\\n\").length;\r\n\r\n // 构建包含 diff 摘要的返回信息\r\n const diffSummary = `+${diff.additions} -${diff.deletions}`;\r\n\r\n const summary = `📝 修改: ${basename(filePath)} (+${diff.additions} -${diff.deletions})`;\r\n\r\n return {\r\n success: true,\r\n data: `文件已编辑:${filePath}\\n替换位置:第 ${startLine} 行\\n${oldLines} 行 → ${newLines} 行\\n变更:${diffSummary}`,\r\n summary,\r\n diff,\r\n };\r\n } catch (err: unknown) {\r\n const message = err instanceof Error ? err.message : String(err);\r\n return {\r\n success: false,\r\n data: `编辑文件失败:${message}`,\r\n error: \"EDIT_ERROR\",\r\n };\r\n }\r\n },\r\n};","// ---------------------------------------------------------------------------\r\n// multi_edit 工具 — 精确字符串批量替换编辑文件(原子性)\r\n//\r\n// 与 edit_file 不同之处:\r\n// - 支持多个替换步骤,在一个请求中原子完成\r\n// - 支持 replace_all 标志,允许替换文件中所有匹配项\r\n// - 所有步骤在内存中应用,要么全部成功(一次写入),要么全部失败\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { readFile } from \"node:fs/promises\";\r\nimport { basename } from \"node:path\";\r\nimport type { Tool, ToolContext, ToolResult, JSONSchema } from \"../types.js\";\r\nimport { resolvePath, confine } from \"../sandbox.js\";\r\nimport { computeFileDiff } from \"../diff.js\";\r\nimport { writeFileWithEol } from \"../eol.js\";\r\n\r\n/** 单个编辑步骤 */\r\ninterface EditStep {\r\n /** 要查找的原始文本(精确匹配) */\r\n oldText: string;\r\n /** 替换后的新文本 */\r\n newText: string;\r\n /** 是否替换文件中所有匹配项,默认 false */\r\n replaceAll?: boolean;\r\n}\r\n\r\n/** multi_edit 工具的参数格式 */\r\ninterface MultiEditArgs {\r\n /** 文件路径(相对于 cwd 或绝对路径) */\r\n path: string;\r\n /** 有序编辑步骤数组 */\r\n edits: EditStep[];\r\n}\r\n\r\n/** multi_edit 工具的参数 JSON Schema */\r\nconst multiEditSchema: JSONSchema = {\r\n type: \"object\",\r\n properties: {\r\n path: {\r\n type: \"string\",\r\n description: \"文件路径(相对于当前工作目录或绝对路径)\",\r\n },\r\n edits: {\r\n type: \"array\",\r\n description: \"有序的编辑步骤列表。每个步骤包含 oldText(精确匹配)、newText(替换内容)和可选的 replaceAll(是否替换所有匹配项)\",\r\n items: {\r\n type: \"object\",\r\n properties: {\r\n oldText: { type: \"string\", description: \"要查找的原始文本(精确匹配)\" },\r\n newText: { type: \"string\", description: \"替换后的新文本\" },\r\n replaceAll: { type: \"boolean\", description: \"是否替换所有匹配项,默认 false\" },\r\n },\r\n required: [\"oldText\", \"newText\"],\r\n additionalProperties: false,\r\n },\r\n },\r\n },\r\n required: [\"path\", \"edits\"],\r\n additionalProperties: false,\r\n};\r\n\r\n/**\r\n * multi_edit 工具 — 对文件进行原子批量替换编辑。\r\n *\r\n * 设计要点:\r\n * - 读文件到内存 → 逐个应用所有编辑步骤 → 全部成功才写入磁盘\r\n * - 任一步骤失败(文本不存在、不唯一等)则返回错误,文件不变\r\n * - 支持 replace_all 标志做全局替换\r\n * - 返回汇总后的 diff\r\n */\r\nexport const multiEditTool: Tool = {\r\n name: \"multi_edit\",\r\n description:\r\n \"对文件进行原子批量替换编辑。在一个请求中执行多个精确替换,任一失败则全部回滚。支持 replaceAll 参数做全局替换。适用于需要同时修改文件多处的场景。\",\r\n parameters: multiEditSchema,\r\n readOnly: false,\r\n\r\n async execute(args: unknown, ctx: ToolContext): Promise<ToolResult> {\r\n const params = args as MultiEditArgs;\r\n if (!params?.path || typeof params.path !== \"string\") {\r\n return { success: false, data: \"缺少必要参数 path\", error: \"INVALID_ARGS\" };\r\n }\r\n if (!Array.isArray(params.edits) || params.edits.length === 0) {\r\n return { success: false, data: \"缺少必要参数 edits(非空数组)\", error: \"INVALID_ARGS\" };\r\n }\r\n\r\n const filePath = resolvePath(params.path, ctx.cwd);\r\n\r\n // 写入范围安全检查\r\n if (ctx.writeRoots && ctx.writeRoots.length > 0) {\r\n const conf = await confine(ctx.writeRoots, filePath);\r\n if (!conf.ok) {\r\n return { success: false, data: conf.error, error: \"OUTSIDE_WRITE_ROOTS\" };\r\n }\r\n }\r\n\r\n try {\r\n const originalContent = await readFile(filePath, \"utf-8\");\r\n let currentContent = originalContent;\r\n\r\n // 逐个应用编辑步骤\r\n for (let idx = 0; idx < params.edits.length; idx++) {\r\n const step = params.edits[idx] as EditStep;\r\n if (typeof step.oldText !== \"string\") {\r\n return {\r\n success: false,\r\n data: `第 ${idx + 1} 步:缺少有效的 oldText`,\r\n error: \"INVALID_STEP_ARGS\",\r\n };\r\n }\r\n\r\n if (step.replaceAll) {\r\n // 全局替换 — 统计替换次数\r\n let count = 0;\r\n let pos = -1;\r\n while ((pos = currentContent.indexOf(step.oldText, pos + 1)) !== -1) {\r\n count++;\r\n }\r\n\r\n if (count === 0) {\r\n return {\r\n success: false,\r\n data: `第 ${idx + 1} 步:未找到要替换的文本,请确认 oldText 与文件内容完全一致`,\r\n error: \"TEXT_NOT_FOUND\",\r\n };\r\n }\r\n\r\n currentContent = currentContent.split(step.oldText).join(step.newText);\r\n } else {\r\n // 精确单次替换 — 检查唯一性\r\n const firstIdx = currentContent.indexOf(step.oldText);\r\n if (firstIdx === -1) {\r\n return {\r\n success: false,\r\n data: `第 ${idx + 1} 步:未找到要替换的文本。请确认 oldText 与文件内容完全一致(包括缩进和空格)。`,\r\n error: \"TEXT_NOT_FOUND\",\r\n };\r\n }\r\n\r\n const secondIdx = currentContent.indexOf(step.oldText, firstIdx + 1);\r\n if (secondIdx !== -1) {\r\n return {\r\n success: false,\r\n data: `第 ${idx + 1} 步:要替换的文本在文件中出现多次,请提供更多上下文以精确定位,或设置 replaceAll=true`,\r\n error: \"TEXT_MULTIPLE_MATCHES\",\r\n };\r\n }\r\n\r\n currentContent = currentContent.replace(step.oldText, step.newText);\r\n }\r\n }\r\n\r\n // 全部步骤成功 → 一次性写入磁盘(保留原文件 EOL)\r\n await writeFileWithEol(filePath, originalContent, currentContent);\r\n\r\n // 计算总体 diff\r\n const diff = computeFileDiff(originalContent, currentContent, filePath);\r\n diff.existedBefore = true;\r\n\r\n return {\r\n success: true,\r\n data: `文件已编辑:${filePath}\\n共执行 ${params.edits.length} 步替换\\n变更:+${diff.additions} -${diff.deletions}`,\r\n summary: `📝 修改: ${basename(filePath)} (${params.edits.length} 步, +${diff.additions} -${diff.deletions})`,\r\n diff,\r\n };\r\n } catch (err: unknown) {\r\n const message = err instanceof Error ? err.message : String(err);\r\n return {\r\n success: false,\r\n data: `批量编辑失败:${message}`,\r\n error: \"MULTI_EDIT_ERROR\",\r\n };\r\n }\r\n },\r\n};\r\n","// ---------------------------------------------------------------------------\r\n// delete_range 工具 — 按行锚点删除文件中的行范围\r\n//\r\n// 通过两个唯一的行内容锚点(start_anchor / end_anchor)定位删除范围,\r\n// 要求每个锚点在文件中恰好出现一次,避免模糊删除。\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { readFile } from \"node:fs/promises\";\r\nimport { basename } from \"node:path\";\r\nimport type { Tool, ToolContext, ToolResult, JSONSchema } from \"../types.js\";\r\nimport { resolvePath, confine } from \"../sandbox.js\";\r\nimport { computeFileDiff } from \"../diff.js\";\r\nimport { writeFileWithEol } from \"../eol.js\";\r\n\r\n/** delete_range 工具的参数格式 */\r\ninterface DeleteRangeArgs {\r\n /** 文件路径(相对于 cwd 或绝对路径) */\r\n path: string;\r\n /** 删除范围的起始标记行(必须唯一) */\r\n startAnchor: string;\r\n /** 删除范围的结束标记行(必须唯一,须在 start_anchor 之后) */\r\n endAnchor: string;\r\n /** 是否包含锚点行本身,默认 false(只删除中间内容) */\r\n inclusive?: boolean;\r\n}\r\n\r\n/** delete_range 工具的参数 JSON Schema */\r\nconst deleteRangeSchema: JSONSchema = {\r\n type: \"object\",\r\n properties: {\r\n path: {\r\n type: \"string\",\r\n description: \"文件路径(相对于当前工作目录或绝对路径)\",\r\n },\r\n startAnchor: {\r\n type: \"string\",\r\n description: \"删除范围的起始标记行内容(必须在文件中唯一,精确匹配整行)\",\r\n },\r\n endAnchor: {\r\n type: \"string\",\r\n description: \"删除范围的结束标记行内容(必须在文件中唯一,精确匹配整行,须在 start_anchor 之后)\",\r\n },\r\n inclusive: {\r\n type: \"boolean\",\r\n description: \"是否包含锚点行本身,默认 false(只删除两行之间的内容)\",\r\n },\r\n },\r\n required: [\"path\", \"startAnchor\", \"endAnchor\"],\r\n additionalProperties: false,\r\n};\r\n\r\n/**\r\n * 在行列表中查找唯一匹配的行号。\r\n * 要求行内容精确匹配,且只能匹配一次。\r\n *\r\n * @returns 匹配的行号(0-based),如果不存在或不唯一则返回错误\r\n */\r\nfunction findUniqueLine(lines: string[], anchor: string, label: string): { line: number } | { error: string } {\r\n const matches: number[] = [];\r\n for (let i = 0; i < lines.length; i++) {\r\n if ((lines[i] as string) === anchor) {\r\n matches.push(i);\r\n }\r\n }\r\n\r\n if (matches.length === 0) {\r\n return { error: `未找到 \"${label}\" 锚点行,请确认内容完全一致` };\r\n }\r\n if (matches.length > 1) {\r\n return { error: `\"${label}\" 锚点行在文件中出现 ${matches.length} 次,请使用更唯一的行内容` };\r\n }\r\n\r\n return { line: matches[0] as number };\r\n}\r\n\r\n/**\r\n * delete_range 工具 — 删除文件中两个锚点行之间的内容。\r\n *\r\n * 设计要点:\r\n * - 通过两个唯一行锚点定位范围\r\n * - 两个锚点必须在文件中恰好出现一次\r\n * - end_anchor 必须在 start_anchor 之后\r\n * - 可选择是否包含锚点行\r\n */\r\nexport const deleteRangeTool: Tool = {\r\n name: \"delete_range\",\r\n description:\r\n \"删除文件中两个锚点行之间的内容。通过两个唯一的行内容精确定位范围。适用于删除方法、代码块、配置段等。\",\r\n parameters: deleteRangeSchema,\r\n readOnly: false,\r\n\r\n async execute(args: unknown, ctx: ToolContext): Promise<ToolResult> {\r\n const params = args as DeleteRangeArgs;\r\n if (!params?.path || typeof params.path !== \"string\") {\r\n return { success: false, data: \"缺少必要参数 path\", error: \"INVALID_ARGS\" };\r\n }\r\n if (typeof params.startAnchor !== \"string\") {\r\n return { success: false, data: \"缺少有效参数 startAnchor\", error: \"INVALID_ARGS\" };\r\n }\r\n if (typeof params.endAnchor !== \"string\") {\r\n return { success: false, data: \"缺少有效参数 endAnchor\", error: \"INVALID_ARGS\" };\r\n }\r\n\r\n const filePath = resolvePath(params.path, ctx.cwd);\r\n const inclusive = params.inclusive ?? false;\r\n\r\n // 写入范围安全检查\r\n if (ctx.writeRoots && ctx.writeRoots.length > 0) {\r\n const conf = await confine(ctx.writeRoots, filePath);\r\n if (!conf.ok) {\r\n return { success: false, data: conf.error, error: \"OUTSIDE_WRITE_ROOTS\" };\r\n }\r\n }\r\n\r\n try {\r\n const content = await readFile(filePath, \"utf-8\");\r\n const lines = content.split(\"\\n\");\r\n\r\n // 查找锚点\r\n const startResult = findUniqueLine(lines, params.startAnchor, \"start_anchor\");\r\n if (\"error\" in startResult) {\r\n return { success: false, data: startResult.error, error: \"ANCHOR_NOT_FOUND\" };\r\n }\r\n\r\n const endResult = findUniqueLine(lines, params.endAnchor, \"end_anchor\");\r\n if (\"error\" in endResult) {\r\n return { success: false, data: endResult.error, error: \"ANCHOR_NOT_FOUND\" };\r\n }\r\n\r\n const startLine = startResult.line;\r\n const endLine = endResult.line;\r\n\r\n // 验证顺序\r\n if (startLine >= endLine) {\r\n return {\r\n success: false,\r\n data: \"end_anchor 必须在 start_anchor 之后\",\r\n error: \"ANCHOR_ORDER_ERROR\",\r\n };\r\n }\r\n\r\n // 计算删除范围(0-based,含头含尾 slice)\r\n const rangeStart = inclusive ? startLine : startLine + 1;\r\n const rangeEnd = inclusive ? endLine : endLine - 1;\r\n\r\n if (rangeStart > rangeEnd) {\r\n return {\r\n success: false,\r\n data: \"删除范围为空(两行锚点相邻且 inclusive=false)\",\r\n error: \"EMPTY_RANGE\",\r\n };\r\n }\r\n\r\n // 执行删除(用与原文件一致的 EOL 拼接,交给 writeFileWithEol 统一处理)\r\n const newLines = [...lines.slice(0, rangeStart), ...lines.slice(rangeEnd + 1)];\r\n const newContent = newLines.join(\"\\n\");\r\n\r\n await writeFileWithEol(filePath, content, newContent);\r\n\r\n const diff = computeFileDiff(content, newContent, filePath);\r\n diff.existedBefore = true;\r\n\r\n const deletedLines = rangeEnd - rangeStart + 1;\r\n\r\n const summary = `📝 修改: ${basename(filePath)} (删 ${deletedLines} 行, +${diff.additions} -${diff.deletions})`;\r\n\r\n return {\r\n success: true,\r\n data: `文件已编辑:${filePath}\\n删除行范围:第 ${rangeStart + 1} 行 ~ 第 ${rangeEnd + 1} 行(共 ${deletedLines} 行)\\n变更:+${diff.additions} -${diff.deletions}`,\r\n summary,\r\n diff,\r\n };\r\n } catch (err: unknown) {\r\n const message = err instanceof Error ? err.message : String(err);\r\n return {\r\n success: false,\r\n data: `删除范围失败:${message}`,\r\n error: \"DELETE_RANGE_ERROR\",\r\n };\r\n }\r\n },\r\n};\r\n","// ---------------------------------------------------------------------------\r\n// bash 工具 — 执行 shell 命令\r\n// ---------------------------------------------------------------------------\r\n\r\nimport process from \"node:process\";\r\nimport type { Tool, ToolContext, ToolResult, JSONSchema } from \"../types.js\";\r\nimport { execCommand, truncateOutput, getDefaultTimeout } from \"../sandbox.js\";\r\n\r\n/** 是否为 Windows 平台 */\r\nconst isWindows = process.platform === \"win32\";\r\n\r\n/** bash 工具的参数格式 */\r\ninterface BashArgs {\r\n /** 要执行的命令 */\r\n command: string;\r\n /** 执行超时时间(毫秒),默认 30000 */\r\n timeout?: number;\r\n}\r\n\r\n/** bash 工具的参数 JSON Schema */\r\nconst bashSchema: JSONSchema = {\r\n type: \"object\",\r\n properties: {\r\n command: {\r\n type: \"string\",\r\n description: \"要执行的 shell 命令\",\r\n },\r\n timeout: {\r\n type: \"number\",\r\n description: \"执行超时时间(毫秒),默认 30000\",\r\n },\r\n },\r\n required: [\"command\"],\r\n additionalProperties: false,\r\n};\r\n\r\n/**\r\n * bash 工具 — 在 shell 中执行命令。\r\n *\r\n * 功能:\r\n * - 支持任意 shell 命令\r\n * - 超时控制(默认 30 秒)\r\n * - 输出截断(默认 50K 字符)\r\n * - 返回标准输出、标准错误和退出码\r\n * - 支持外部中止信号\r\n */\r\nexport const bashTool: Tool = {\r\n name: \"bash\",\r\n description:\r\n \"在 shell 中执行命令。返回标准输出、标准错误和退出码。支持超时控制和信号中止。适用于运行构建、测试、Git 操作等命令。\",\r\n parameters: bashSchema,\r\n readOnly: false, // bash 有副作用,不能并行执行\r\n\r\n async execute(args: unknown, ctx: ToolContext): Promise<ToolResult> {\r\n const params = args as BashArgs;\r\n if (!params?.command || typeof params.command !== \"string\") {\r\n return { success: false, data: \"缺少必要参数 command\", error: \"INVALID_ARGS\" };\r\n }\r\n\r\n const timeout = params.timeout ?? ctx.timeout ?? getDefaultTimeout();\r\n\r\n try {\r\n // Windows 使用 cmd /c 执行,Unix 使用 sh -c 执行\r\n // isShellCommand=true 告知 execCommand 不需要再包装 shell\r\n const shellCommand = isWindows ? \"cmd\" : \"sh\";\r\n const shellArgs = isWindows ? [\"/c\", params.command] : [\"-c\", params.command];\r\n\r\n const result = await execCommand(\r\n shellCommand,\r\n shellArgs,\r\n ctx.cwd,\r\n timeout,\r\n ctx.signal,\r\n true, // isShellCommand — 已指定 shell 程序,不需要二次包装\r\n );\r\n\r\n // 组装输出\r\n const parts: string[] = [];\r\n if (result.stdout) {\r\n parts.push(truncateOutput(result.stdout));\r\n }\r\n if (result.stderr) {\r\n parts.push(`[stderr]\\n${truncateOutput(result.stderr)}`);\r\n }\r\n\r\n const success = result.exitCode === 0;\r\n const output = parts.length > 0 ? parts.join(\"\\n\") : \"(无输出)\";\r\n\r\n // UI 摘要:命令本身截断到 60 字符,避免长命令撑屏\r\n const cmdPreview = params.command.length > 60\r\n ? params.command.slice(0, 57) + \"...\"\r\n : params.command;\r\n const summary = `🔧 $ ${cmdPreview}(exit ${result.exitCode ?? \"未知\"})`;\r\n\r\n return {\r\n success,\r\n data: `${output}\\n[退出码: ${result.exitCode ?? \"未知\"}]`,\r\n summary,\r\n error: success ? undefined : `EXIT_CODE_${result.exitCode ?? \"UNKNOWN\"}`,\r\n };\r\n } catch (err: unknown) {\r\n const message = err instanceof Error ? err.message : String(err);\r\n return {\r\n success: false,\r\n data: `命令执行失败:${message}`,\r\n error: \"EXECUTION_ERROR\",\r\n };\r\n }\r\n },\r\n};","// ---------------------------------------------------------------------------\r\n// glob 工具 — 文件搜索(模式匹配)\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { readdir, stat } from \"node:fs/promises\";\r\nimport { join, relative, isAbsolute } from \"node:path\";\r\nimport type { Tool, ToolContext, ToolResult, JSONSchema } from \"../types.js\";\r\nimport { truncateOutput } from \"../sandbox.js\";\r\n\r\n/** glob 工具的参数格式 */\r\ninterface GlobArgs {\r\n /** 搜索模式(支持 * 和 ** 通配符) */\r\n pattern: string;\r\n /** 搜索的起始目录,默认为 cwd */\r\n directory?: string;\r\n}\r\n\r\n/** glob 工具的参数 JSON Schema */\r\nconst globSchema: JSONSchema = {\r\n type: \"object\",\r\n properties: {\r\n pattern: {\r\n type: \"string\",\r\n description: \"搜索模式(支持 * 和 ** 通配符,如 **/*.ts、src/**/*.test.ts)\",\r\n },\r\n directory: {\r\n type: \"string\",\r\n description: \"搜索的起始目录,默认为当前工作目录\",\r\n },\r\n },\r\n required: [\"pattern\"],\r\n additionalProperties: false,\r\n};\r\n\r\n/**\r\n * 将 glob 模式转换为正则表达式。\r\n *\r\n * 支持:\r\n * - `*` 匹配除路径分隔符外的任意字符\r\n * - `**` 匹配任意路径(包含分隔符)\r\n * - `?` 匹配单个字符\r\n * - `{a,b}` 匹配 a 或 b\r\n * - `[abc]` 字符类\r\n */\r\nfunction globToRegex(pattern: string): RegExp {\r\n let regexStr = pattern;\r\n // 先处理 **(必须在 * 之前处理)\r\n // **/ 表示零或多个目录段,** 单独使用表示匹配任意路径\r\n regexStr = regexStr.replace(/\\*\\*\\//g, \"<<GLOBSTAR_SLASH>>\");\r\n regexStr = regexStr.replace(/\\*\\*/g, \"<<GLOBSTAR>>\");\r\n // 将 glob 的 ? 通配符转换为占位符(必须在转义和通配符替换之前)\r\n regexStr = regexStr.replace(/\\?/g, \"<<QUESTION>>\");\r\n // 转义正则特殊字符(除了我们的占位符)\r\n regexStr = regexStr.replace(/[.+^${}()|[\\]\\\\]/g, \"\\\\$&\");\r\n // 处理通配符\r\n regexStr = regexStr.replace(/\\*/g, \"[^/]*\"); // * 匹配非路径分隔符\r\n regexStr = regexStr.replace(/<<GLOBSTAR_SLASH>>/g, \"(.*/)?\"); // **/ 匹配零或多个目录段\r\n regexStr = regexStr.replace(/<<GLOBSTAR>>/g, \".*\"); // ** 匹配任意路径\r\n regexStr = regexStr.replace(/<<QUESTION>>/g, \"[^/]\"); // ? 匹配单个非路径分隔符字符\r\n return new RegExp(`^${regexStr}$`, \"i\");\r\n}\r\n\r\n/**\r\n * 递归遍历目录收集文件路径。\r\n */\r\nasync function walkDir(dir: string, baseDir: string): Promise<string[]> {\r\n const results: string[] = [];\r\n let entries;\r\n\r\n try {\r\n entries = await readdir(dir, { withFileTypes: true });\r\n } catch {\r\n return results;\r\n }\r\n\r\n for (const entry of entries) {\r\n // 跳过 node_modules 和 .git 目录\r\n if (entry.isDirectory() && (entry.name === \"node_modules\" || entry.name === \".git\")) {\r\n continue;\r\n }\r\n\r\n const fullPath = join(dir, entry.name);\r\n const relPath = relative(baseDir, fullPath);\r\n\r\n if (entry.isDirectory()) {\r\n results.push(...await walkDir(fullPath, baseDir));\r\n } else {\r\n results.push(relPath);\r\n }\r\n }\r\n\r\n return results;\r\n}\r\n\r\n/**\r\n * glob 工具 — 按模式搜索文件路径。\r\n *\r\n * 功能:\r\n * - 支持 * 和 ** 通配符\r\n * - 自动跳过 node_modules 和 .git 目录\r\n * - 返回相对于搜索目录的路径列表\r\n */\r\nexport const globTool: Tool = {\r\n name: \"glob\",\r\n description:\r\n \"按模式搜索文件路径。支持 *(匹配文件名部分)和 **(匹配多层目录)通配符。自动跳过 node_modules 和 .git 目录。\",\r\n parameters: globSchema,\r\n readOnly: true,\r\n\r\n async execute(args: unknown, ctx: ToolContext): Promise<ToolResult> {\r\n const params = args as GlobArgs;\r\n if (!params?.pattern || typeof params.pattern !== \"string\") {\r\n return { success: false, data: \"缺少必要参数 pattern\", error: \"INVALID_ARGS\" };\r\n }\r\n\r\n const searchDir = params.directory\r\n ? (isAbsolute(params.directory) ? params.directory : join(ctx.cwd, params.directory))\r\n : ctx.cwd;\r\n const regex = globToRegex(params.pattern);\r\n\r\n try {\r\n // 检查搜索目录是否存在\r\n const dirStat = await stat(searchDir);\r\n if (!dirStat.isDirectory()) {\r\n return { success: false, data: `路径不是目录:${searchDir}`, error: \"NOT_DIRECTORY\" };\r\n }\r\n\r\n const allFiles = await walkDir(searchDir, searchDir);\r\n const matched = allFiles.filter((f) => regex.test(f));\r\n\r\n if (matched.length === 0) {\r\n return {\r\n success: true,\r\n data: `未找到匹配 \"${params.pattern}\" 的文件`,\r\n summary: `${params.pattern} → 0 个文件`,\r\n };\r\n }\r\n\r\n // 限制返回数量\r\n const limit = 200;\r\n const limited = matched.slice(0, limit);\r\n const output = limited.join(\"\\n\");\r\n const suffix = matched.length > limit ? `\\n\\n... 共 ${matched.length} 个文件,只显示前 ${limit} 个` : \"\";\r\n\r\n return {\r\n success: true,\r\n data: truncateOutput(output + suffix),\r\n summary: `${params.pattern} → ${matched.length} 个文件`,\r\n };\r\n } catch (err: unknown) {\r\n const message = err instanceof Error ? err.message : String(err);\r\n return {\r\n success: false,\r\n data: `文件搜索失败:${message}`,\r\n error: \"GLOB_ERROR\",\r\n };\r\n }\r\n },\r\n};","// ---------------------------------------------------------------------------\r\n// grep 工具 — 文件内容正则搜索\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { readdir, readFile, stat } from \"node:fs/promises\";\r\nimport { join, relative, isAbsolute } from \"node:path\";\r\nimport type { Tool, ToolContext, ToolResult, JSONSchema } from \"../types.js\";\r\nimport { truncateOutput } from \"../sandbox.js\";\r\n\r\n/** grep 工具的参数格式 */\r\ninterface GrepArgs {\r\n /** 正则表达式模式 */\r\n pattern: string;\r\n /** 搜索的目录路径,默认为 cwd */\r\n directory?: string;\r\n /** 文件扩展名过滤(如 \"ts\"、\"json\"),不含点号 */\r\n include?: string;\r\n /** 是否大小写敏感,默认 false */\r\n case_sensitive?: boolean;\r\n /** 最大搜索文件数,默认 200 */\r\n max_files?: number;\r\n}\r\n\r\n/** grep 工具的参数 JSON Schema */\r\nconst grepSchema: JSONSchema = {\r\n type: \"object\",\r\n properties: {\r\n pattern: {\r\n type: \"string\",\r\n description: \"正则表达式搜索模式\",\r\n },\r\n directory: {\r\n type: \"string\",\r\n description: \"搜索的目录路径,默认为当前工作目录\",\r\n },\r\n include: {\r\n type: \"string\",\r\n description: \"文件扩展名过滤(如 ts、json),不含点号\",\r\n },\r\n case_sensitive: {\r\n type: \"boolean\",\r\n description: \"是否大小写敏感,默认 false\",\r\n },\r\n max_files: {\r\n type: \"number\",\r\n description: \"最大搜索文件数,默认 200\",\r\n },\r\n },\r\n required: [\"pattern\"],\r\n additionalProperties: false,\r\n};\r\n\r\n/** 单个匹配结果 */\r\ninterface GrepMatch {\r\n /** 相对文件路径 */\r\n file: string;\r\n /** 行号(1-based) */\r\n line: number;\r\n /** 匹配行内容 */\r\n content: string;\r\n}\r\n\r\n/**\r\n * 递归遍历目录收集文件路径。\r\n */\r\nasync function collectFiles(\r\n dir: string,\r\n baseDir: string,\r\n extension?: string,\r\n maxFiles = 200,\r\n): Promise<string[]> {\r\n const results: string[] = [];\r\n let entries;\r\n\r\n try {\r\n entries = await readdir(dir, { withFileTypes: true });\r\n } catch {\r\n return results;\r\n }\r\n\r\n for (const entry of entries) {\r\n if (results.length >= maxFiles) break;\r\n\r\n // 跳过常见忽略目录\r\n if (entry.isDirectory() && (entry.name === \"node_modules\" || entry.name === \".git\" || entry.name === \"dist\")) {\r\n continue;\r\n }\r\n\r\n const fullPath = join(dir, entry.name);\r\n\r\n if (entry.isDirectory()) {\r\n results.push(...await collectFiles(fullPath, baseDir, extension, maxFiles - results.length));\r\n } else {\r\n // 如果指定了扩展名过滤,只匹配对应扩展名的文件\r\n if (extension && !entry.name.endsWith(`.${extension}`)) {\r\n continue;\r\n }\r\n results.push(fullPath);\r\n }\r\n }\r\n\r\n return results;\r\n}\r\n\r\n/**\r\n * grep 工具 — 在文件内容中搜索正则表达式匹配。\r\n *\r\n * 功能:\r\n * - 正则表达式搜索\r\n * - 支持大小写敏感/不敏感\r\n * - 支持文件扩展名过滤\r\n * - 返回匹配行的文件路径、行号和内容\r\n * - 自动跳过 node_modules、.git、dist 目录\r\n */\r\nexport const grepTool: Tool = {\r\n name: \"grep\",\r\n description:\r\n \"在文件内容中搜索正则表达式。返回匹配行的文件路径、行号和内容。支持大小写敏感、文件扩展名过滤。\",\r\n parameters: grepSchema,\r\n readOnly: true,\r\n\r\n async execute(args: unknown, ctx: ToolContext): Promise<ToolResult> {\r\n const params = args as GrepArgs;\r\n if (!params?.pattern || typeof params.pattern !== \"string\") {\r\n return { success: false, data: \"缺少必要参数 pattern\", error: \"INVALID_ARGS\" };\r\n }\r\n\r\n const searchDir = params.directory\r\n ? (isAbsolute(params.directory) ? params.directory : join(ctx.cwd, params.directory))\r\n : ctx.cwd;\r\n const maxFiles = params.max_files ?? 200;\r\n\r\n try {\r\n // 编译正则表达式\r\n const flags = params.case_sensitive ? \"g\" : \"gi\";\r\n const regex = new RegExp(params.pattern, flags);\r\n\r\n // 检查搜索目录\r\n const dirStat = await stat(searchDir);\r\n if (!dirStat.isDirectory()) {\r\n return { success: false, data: `路径不是目录:${searchDir}`, error: \"NOT_DIRECTORY\" };\r\n }\r\n\r\n // 收集文件并搜索\r\n const files = await collectFiles(searchDir, searchDir, params.include, maxFiles);\r\n const matches: GrepMatch[] = [];\r\n\r\n for (const filePath of files) {\r\n try {\r\n const content = await readFile(filePath, \"utf-8\");\r\n const lines = content.split(\"\\n\");\r\n const relPath = relative(searchDir, filePath);\r\n\r\n for (let i = 0; i < lines.length; i++) {\r\n if (regex.test(lines[i]!)) {\r\n // 重置 lastIndex(因为用了 g 标志)\r\n regex.lastIndex = 0;\r\n matches.push({\r\n file: relPath,\r\n line: i + 1,\r\n content: lines[i]!,\r\n });\r\n if (matches.length >= 500) break;\r\n }\r\n }\r\n\r\n if (matches.length >= 500) break;\r\n } catch {\r\n // 跳过无法读取的文件(如二进制文件)\r\n continue;\r\n }\r\n }\r\n\r\n if (matches.length === 0) {\r\n return {\r\n success: true,\r\n data: `未找到匹配 \"${params.pattern}\" 的内容`,\r\n summary: `🔍 \"${params.pattern}\" → 0 条命中`,\r\n };\r\n }\r\n\r\n // 格式化输出\r\n const output = matches\r\n .map((m) => `${m.file}:${m.line}: ${m.content}`)\r\n .join(\"\\n\");\r\n\r\n // 按文件汇总,UI 摘要不暴露具体行内容\r\n const fileSet = new Set(matches.map((m) => m.file));\r\n const summary = `🔍 \"${params.pattern}\" → ${matches.length} 条命中 / ${fileSet.size} 个文件`;\r\n\r\n return {\r\n success: true,\r\n data: truncateOutput(output),\r\n summary,\r\n };\r\n } catch (err: unknown) {\r\n const message = err instanceof Error ? err.message : String(err);\r\n return {\r\n success: false,\r\n data: `内容搜索失败:${message}`,\r\n error: \"GREP_ERROR\",\r\n };\r\n }\r\n },\r\n};","// ---------------------------------------------------------------------------\r\n// ls 工具 — 列出目录内容\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { readdir, stat } from \"node:fs/promises\";\r\nimport { join, relative } from \"node:path\";\r\nimport type { Tool, ToolContext, ToolResult, JSONSchema } from \"../types.js\";\r\nimport { resolvePath, truncateOutput } from \"../sandbox.js\";\r\n\r\n/** ls 工具的参数格式 */\r\ninterface LsArgs {\r\n /** 目录路径,默认为 cwd */\r\n path?: string;\r\n /** 是否显示隐藏文件,默认 false */\r\n all?: boolean;\r\n}\r\n\r\n/** 目录项类型标记 */\r\ntype EntryType = \"FILE\" | \"DIR\" | \"LINK\";\r\n\r\n/** ls 工具的参数 JSON Schema */\r\nconst lsSchema: JSONSchema = {\r\n type: \"object\",\r\n properties: {\r\n path: {\r\n type: \"string\",\r\n description: \"目录路径(相对于当前工作目录或绝对路径),默认为当前目录\",\r\n },\r\n all: {\r\n type: \"boolean\",\r\n description: \"是否显示隐藏文件(以 . 开头的文件),默认 false\",\r\n },\r\n },\r\n required: [],\r\n additionalProperties: false,\r\n};\r\n\r\n/**\r\n * ls 工具 — 列出目录内容。\r\n *\r\n * 功能:\r\n * - 显示文件/目录类型标记\r\n * - 显示文件大小\r\n * - 可选显示隐藏文件\r\n * - 自动跳过无权访问的目录\r\n */\r\nexport const lsTool: Tool = {\r\n name: \"ls\",\r\n description:\r\n \"列出目录内容。显示条目类型(文件/目录/链接)和大小。可选择是否显示隐藏文件。\",\r\n parameters: lsSchema,\r\n readOnly: true,\r\n\r\n async execute(args: unknown, ctx: ToolContext): Promise<ToolResult> {\r\n const params = (args ?? {}) as LsArgs;\r\n const dirPath = params.path ? resolvePath(params.path, ctx.cwd) : ctx.cwd;\r\n const showAll = params.all ?? false;\r\n\r\n try {\r\n const entries = await readdir(dirPath, { withFileTypes: true });\r\n const lines: string[] = [];\r\n\r\n // 按名称排序:目录在前,文件在后\r\n const sorted = [...entries].sort((a, b) => {\r\n if (a.isDirectory() !== b.isDirectory()) {\r\n return a.isDirectory() ? -1 : 1;\r\n }\r\n return a.name.localeCompare(b.name);\r\n });\r\n\r\n for (const entry of sorted) {\r\n // 过滤隐藏文件\r\n if (!showAll && entry.name.startsWith(\".\")) continue;\r\n\r\n const typeMark: EntryType = entry.isDirectory()\r\n ? \"DIR\"\r\n : entry.isSymbolicLink()\r\n ? \"LINK\"\r\n : \"FILE\";\r\n\r\n let sizeStr = \"\";\r\n if (typeMark === \"FILE\") {\r\n try {\r\n const fileStat = await stat(join(dirPath, entry.name));\r\n if (fileStat.size < 1024) {\r\n sizeStr = `${fileStat.size}B`;\r\n } else if (fileStat.size < 1024 * 1024) {\r\n sizeStr = `${(fileStat.size / 1024).toFixed(1)}KB`;\r\n } else {\r\n sizeStr = `${(fileStat.size / 1024 / 1024).toFixed(1)}MB`;\r\n }\r\n } catch {\r\n sizeStr = \"?\";\r\n }\r\n }\r\n\r\n const typeLabel = typeMark === \"DIR\" ? \"📁\" : typeMark === \"LINK\" ? \"🔗\" : \"📄\";\r\n lines.push(`${typeLabel} ${entry.name}${sizeStr ? ` (${sizeStr})` : \"\"}`);\r\n }\r\n\r\n if (lines.length === 0) {\r\n return { success: true, data: \"目录为空\", summary: `📂 ${relative(ctx.cwd, dirPath).replace(/\\\\/g, \"/\")}(空)` };\r\n }\r\n\r\n const relPath = relative(ctx.cwd, dirPath).replace(/\\\\/g, \"/\");\r\n return {\r\n success: true,\r\n data: truncateOutput(`目录:${dirPath}\\n${lines.join(\"\\n\")}`),\r\n summary: `📂 ${relPath}(${lines.length} 项)`,\r\n };\r\n } catch (err: unknown) {\r\n const message = err instanceof Error ? err.message : String(err);\r\n return {\r\n success: false,\r\n data: `列目录失败:${message}`,\r\n error: \"LS_ERROR\",\r\n };\r\n }\r\n },\r\n};","// ---------------------------------------------------------------------------\r\n// fetch 工具 — HTTP 请求\r\n// ---------------------------------------------------------------------------\r\n\r\nimport type { Tool, ToolContext, ToolResult, JSONSchema } from \"../types.js\";\r\nimport { truncateOutput } from \"../sandbox.js\";\r\n\r\n/** fetch 工具的参数格式 */\r\ninterface FetchArgs {\r\n /** 请求 URL */\r\n url: string;\r\n /** 请求方法,默认 GET */\r\n method?: string;\r\n /** 请求头 */\r\n headers?: Record<string, string>;\r\n /** 请求体(POST/PUT 时使用) */\r\n body?: string;\r\n /** 响应最大长度(字符),默认 50000 */\r\n max_length?: number;\r\n}\r\n\r\n/** fetch 工具的参数 JSON Schema */\r\nconst fetchSchema: JSONSchema = {\r\n type: \"object\",\r\n properties: {\r\n url: {\r\n type: \"string\",\r\n description: \"请求的 URL\",\r\n },\r\n method: {\r\n type: \"string\",\r\n description: \"HTTP 方法(GET、POST、PUT、DELETE 等),默认 GET\",\r\n },\r\n headers: {\r\n type: \"object\",\r\n description: \"请求头键值对\",\r\n additionalProperties: { type: \"string\" },\r\n },\r\n body: {\r\n type: \"string\",\r\n description: \"请求体内容(POST/PUT 时使用)\",\r\n },\r\n max_length: {\r\n type: \"number\",\r\n description: \"响应内容最大长度(字符),默认 50000\",\r\n },\r\n },\r\n required: [\"url\"],\r\n additionalProperties: false,\r\n};\r\n\r\n/**\r\n * fetch 工具 — 发起 HTTP 请求并返回响应内容。\r\n *\r\n * 功能:\r\n * - 支持 GET/POST/PUT/DELETE 等 HTTP 方法\r\n * - 自定义请求头和请求体\r\n * - 超时控制(默认 30 秒)\r\n * - 响应大小限制\r\n * - 显示响应状态码和内容类型\r\n */\r\nexport const fetchTool: Tool = {\r\n name: \"fetch\",\r\n description:\r\n \"发起 HTTP 请求并返回响应内容。支持自定义方法和请求头。适用于获取网页内容、API 调用等场景。\",\r\n parameters: fetchSchema,\r\n readOnly: true,\r\n\r\n async execute(args: unknown, ctx: ToolContext): Promise<ToolResult> {\r\n const params = args as FetchArgs;\r\n if (!params?.url || typeof params.url !== \"string\") {\r\n return { success: false, data: \"缺少必要参数 url\", error: \"INVALID_ARGS\" };\r\n }\r\n\r\n const method = (params.method ?? \"GET\").toUpperCase();\r\n const timeout = 30_000;\r\n const maxLength = params.max_length ?? 50_000;\r\n\r\n try {\r\n // 构建请求\r\n const fetchOptions: RequestInit = {\r\n method,\r\n headers: params.headers ?? {},\r\n signal: ctx.signal ?? AbortSignal.timeout(timeout),\r\n };\r\n\r\n if (params.body && (method === \"POST\" || method === \"PUT\" || method === \"PATCH\")) {\r\n (fetchOptions.headers as Record<string, string>)[\"Content-Type\"] ??= \"application/json\";\r\n fetchOptions.body = params.body;\r\n }\r\n\r\n const response = await fetch(params.url, fetchOptions);\r\n\r\n // 收集响应信息\r\n const contentType = response.headers.get(\"content-type\") ?? \"unknown\";\r\n const statusText = `${response.status} ${response.statusText}`;\r\n\r\n // 读取响应体\r\n let body: string;\r\n try {\r\n body = await response.text();\r\n } catch {\r\n body = \"(无法读取响应体)\";\r\n }\r\n\r\n // 截断过长内容\r\n const truncatedBody = truncateOutput(body, maxLength);\r\n\r\n const header = `状态: ${statusText}\\n内容类型: ${contentType}`;\r\n const separator = body.length > 0 ? \"\\n---\\n\" : \"\";\r\n\r\n // UI 摘要:方法 + URL + 状态码,不暴露响应体\r\n const urlPreview = params.url.length > 60\r\n ? params.url.slice(0, 57) + \"...\"\r\n : params.url;\r\n const summary = `🌐 ${method} ${urlPreview} → ${response.status}`;\r\n\r\n return {\r\n success: response.ok,\r\n data: `${header}${separator}${truncatedBody}`,\r\n summary,\r\n error: response.ok ? undefined : `HTTP_${response.status}`,\r\n };\r\n } catch (err: unknown) {\r\n if (err instanceof DOMException && err.name === \"AbortError\") {\r\n return { success: false, data: \"请求被中止\", error: \"ABORTED\" };\r\n }\r\n if (err instanceof TypeError && err.message.includes(\"fetch\")) {\r\n return { success: false, data: `网络错误:${err.message}`, error: \"NETWORK_ERROR\" };\r\n }\r\n const message = err instanceof Error ? err.message : String(err);\r\n return {\r\n success: false,\r\n data: `HTTP 请求失败:${message}`,\r\n error: \"FETCH_ERROR\",\r\n };\r\n }\r\n },\r\n};","// ---------------------------------------------------------------------------\r\n// 内置工具 — 注册所有内置工具的工厂函数\r\n// ---------------------------------------------------------------------------\r\n\r\nimport type { Tool } from \"../types.js\";\r\nimport { readFileTool } from \"./read-file.js\";\r\nimport { writeFileTool } from \"./write-file.js\";\r\nimport { editFileTool } from \"./edit-file.js\";\r\nimport { multiEditTool } from \"./multi-edit.js\";\r\nimport { deleteRangeTool } from \"./delete-range.js\";\r\nimport { bashTool } from \"./bash.js\";\r\nimport { globTool } from \"./glob.js\";\r\nimport { grepTool } from \"./grep.js\";\r\nimport { lsTool } from \"./ls.js\";\r\nimport { fetchTool } from \"./fetch.js\";\r\n\r\n/** 所有内置工具列表 */\r\nexport const builtinTools: Tool[] = [\r\n readFileTool,\r\n writeFileTool,\r\n editFileTool,\r\n multiEditTool,\r\n deleteRangeTool,\r\n bashTool,\r\n globTool,\r\n grepTool,\r\n lsTool,\r\n fetchTool,\r\n];\r\n\r\n/**\r\n * 获取所有内置工具映射表(按名称索引)。\r\n */\r\nexport function getBuiltinToolMap(): Map<string, Tool> {\r\n return new Map(builtinTools.map((t) => [t.name, t]));\r\n}\r\n","// ---------------------------------------------------------------------------\r\n// 渐变色工具 — 用于占位符从左到右的渐变动画\r\n// ---------------------------------------------------------------------------\r\n\r\n/** RGB 色标元组 [R, G, B],每个值 0~255 */\r\nexport type ColorStop = [number, number, number];\r\n\r\n// ---- 预设渐变方案 ----\r\n\r\n/** 空闲占位符渐变:赛博朋克绿 → 青 → 紫 */\r\nexport const IDLE_GRADIENT_STOPS: ColorStop[] = [\r\n [0, 255, 65], // #00ff41\r\n [0, 255, 255], // #00ffff\r\n [189, 147, 249], // #bd93f9\r\n];\r\n\r\n/** 命令提示条渐变:浅柠黄 → 暖黄 → 橙色(从左到右流动) */\r\nexport const CMD_TIP_GRADIENT_STOPS: ColorStop[] = [\r\n [255, 245, 180], // #fff5b4 浅柠黄\r\n [255, 210, 80], // #ffd250 暖黄\r\n [255, 150, 50], // #ff9632 橙色\r\n];\r\n\r\n/** 流式占位符渐变:琥珀 → 焦糖 → 棕红 */\r\nexport const STREAMING_GRADIENT_STOPS: ColorStop[] = [\r\n [255, 191, 0], // #ffbf00 琥珀金\r\n [210, 140, 60], // #d28c3c 焦糖\r\n [175, 70, 40], // #af4628 棕红\r\n];\r\n\r\n// ---- 渐变动画配置 ----\r\n\r\n/** 动画参数:每帧相位增量 & 帧间隔(ms) */\r\nexport const GRADIENT_ANIMATION = {\r\n /** 空闲占位符动画:每帧相位步进(值越大流速越快) */\r\n idlePhaseStep: 0.06,\r\n /** 空闲占位符动画:帧间隔(ms) */\r\n idleInterval: 40,\r\n /** 流式占位符动画:每帧相位步进 */\r\n streamingPhaseStep: 0.05,\r\n /** 流式占位符动画:帧间隔(ms) */\r\n streamingInterval: 40,\r\n /** 命令提示条动画:每帧相位步进 */\r\n cmdTipPhaseStep: 0.05,\r\n /** 命令提示条动画:帧间隔(ms) */\r\n cmdTipInterval: 40,\r\n} as const;\r\n\r\n// ---- 渲染时跳过着色的字符 ----\r\n\r\n/** 不参与渐变着色的字符(空格、标点、emoji 等) */\r\nexport const SKIP_CHARS = new Set([\" \", \"~\", \".\", \"。\", \"!\", \"👇\"]);\r\n\r\n// ---- 核心算法 ----\r\n\r\n/** 根据 t(0~1) 在色标间线性插值,返回 hex 色值 */\r\nexport function lerpStopsToHex(t: number, stops: readonly ColorStop[]): string {\r\n const len = stops.length;\r\n const segment = t * (len - 1);\r\n const idx = Math.min(Math.floor(segment), len - 2);\r\n const frac = segment - idx;\r\n const stop = stops[idx];\r\n const nextStop = stops[idx + 1];\r\n if (!stop || !nextStop) return \"#808080\";\r\n const r = Math.round(stop[0] + (nextStop[0] - stop[0]) * frac);\r\n const g = Math.round(stop[1] + (nextStop[1] - stop[1]) * frac);\r\n const b = Math.round(stop[2] + (nextStop[2] - stop[2]) * frac);\r\n return `#${r.toString(16).padStart(2, \"0\")}${g.toString(16).padStart(2, \"0\")}${b.toString(16).padStart(2, \"0\")}`;\r\n}\r\n\r\n/** 对文本计算逐字渐变色数组,phase 控制色彩偏移位置(0~1) */\r\nexport function getGradientColors(\r\n text: string,\r\n phase: number,\r\n stops: readonly ColorStop[],\r\n): string[] {\r\n const len = text.length;\r\n if (len === 0) return [];\r\n return text.split(\"\").map((ch, i) => {\r\n if (SKIP_CHARS.has(ch)) return \"\";\r\n const t = ((i / (len - 1)) + phase) % 1;\r\n return lerpStopsToHex(t, stops);\r\n });\r\n}","import { Box, Text, useInput } from \"ink\";\r\nimport { useState, useCallback } from \"react\";\r\nimport type { Game } from \"../game/index.js\";\r\nimport { useDoubleCtrlC } from \"../ui/useDoubleCtrlC.js\";\r\n\r\ninterface GamePickerProps {\r\n games: Game[];\r\n onSelect: (game: Game) => void;\r\n onExit?: () => void;\r\n onBackToChat?: () => void;\r\n}\r\n\r\nexport function GamePicker({ games, onSelect, onExit, onBackToChat }: GamePickerProps) {\r\n const [selectedIndex, setSelectedIndex] = useState(0);\r\n\r\n const exitAction = onBackToChat ?? onExit ?? (() => process.exit(0));\r\n const { doubleCtrlC, handleCtrlC } = useDoubleCtrlC(exitAction);\r\n\r\n useInput(\r\n useCallback(\r\n (input, key) => {\r\n // Ctrl+C:双击退出\r\n if (input === \"c\" && key.ctrl) {\r\n handleCtrlC();\r\n return;\r\n }\r\n\r\n if (games.length === 0) return;\r\n if (key.upArrow || input === \"k\") {\r\n setSelectedIndex((prev) => (prev > 0 ? prev - 1 : games.length - 1));\r\n } else if (key.downArrow || input === \"j\") {\r\n setSelectedIndex((prev) => (prev < games.length - 1 ? prev + 1 : 0));\r\n } else if (key.return) {\r\n const game = games[selectedIndex];\r\n if (game) onSelect(game);\r\n } else if (key.escape || input === \"q\") {\r\n if (onBackToChat) onBackToChat();\r\n else onExit?.();\r\n }\r\n },\r\n [games, selectedIndex, onSelect, onExit, onBackToChat, handleCtrlC],\r\n ),\r\n );\r\n\r\n return (\r\n <Box flexDirection=\"column\">\r\n <Box marginBottom={1}>\r\n <Text bold color=\"#00ffff\">\r\n 🎮 游戏列表\r\n </Text>\r\n </Box>\r\n\r\n <Box flexDirection=\"column\">\r\n {games.map((game, index) => {\r\n const isSelected = index === selectedIndex;\r\n return (\r\n <Box key={game.id} flexDirection=\"row\">\r\n <Box width={3} flexShrink={0}>\r\n {isSelected ? (\r\n <Text bold color=\"#00ff41\">\r\n {\"▸ \"}\r\n </Text>\r\n ) : (\r\n <Text>{\" \"}</Text>\r\n )}\r\n </Box>\r\n <Box width={20} flexShrink={0}>\r\n <Text bold color={isSelected ? \"#00ff41\" : \"#ffffff\"}>\r\n {game.name}\r\n </Text>\r\n </Box>\r\n <Box>\r\n <Text color=\"#888888\">{game.description}</Text>\r\n </Box>\r\n </Box>\r\n );\r\n })}\r\n </Box>\r\n\r\n <Box marginTop={1}>\r\n <Text dimColor>\r\n {\" ↑/↓ 选择 Enter 启动 q 返回\"}\r\n </Text>\r\n </Box>\r\n\r\n {/* 双击 Ctrl+C 退出提示 */}\r\n {doubleCtrlC && (\r\n <Box marginTop={1}>\r\n <Text color=\"#ff1493\" bold>\r\n {\" ⚠ 再按一次 Ctrl+C 退出 dskcode\"}\r\n </Text>\r\n </Box>\r\n )}\r\n </Box>\r\n );\r\n}\r\n","export interface Game {\r\n /** 游戏唯一标识 */\r\n id: string;\r\n /** 游戏名称 */\r\n name: string;\r\n /** 简短描述 */\r\n description: string;\r\n /** 启动游戏 */\r\n play: () => Promise<void>;\r\n}\r\n\r\nconst registry = new Map<string, Game>();\r\n\r\nexport function registerGame(game: Game): void {\r\n registry.set(game.id, game);\r\n}\r\n\r\nexport function getGame(id: string): Game | undefined {\r\n return registry.get(id);\r\n}\r\n\r\nexport function listGames(): Game[] {\r\n return Array.from(registry.values());\r\n}\r\n","import { Box, Text, useInput, render } from \"ink\";\r\nimport { useState, useEffect, useRef, useCallback } from \"react\";\r\n\r\nconst GAME_WIDTH = 40;\r\nconst GAME_HEIGHT = 18;\r\nconst PADDLE_WIDTH = 9;\r\nconst BRICK_COLORS = [166, 214, 76, 69];\r\n\r\ninterface Vec2 {\r\n x: number;\r\n y: number;\r\n}\r\n\r\ninterface Brick {\r\n x: number;\r\n y: number;\r\n w: number;\r\n alive: boolean;\r\n}\r\n\r\n// ─── 10 个关卡定义 ──────────────────────────────────────────────\r\n\r\ninterface LevelDef {\r\n rows: number;\r\n cols: number;\r\n bw: number; // 砖块宽度(格数)\r\n desc: string;\r\n // 返回 true 表示该位置有砖块\r\n pattern: (col: number, row: number, rows: number, cols: number) => boolean;\r\n}\r\n\r\nconst LEVELS: LevelDef[] = [\r\n { rows: 4, cols: 8, bw: 3, desc: \"经典 4×8\", pattern: () => true },\r\n { rows: 3, cols: 8, bw: 3, desc: \"轻松 3 层\", pattern: () => true },\r\n { rows: 6, cols: 8, bw: 3, desc: \"厚墙 6 层\", pattern: () => true },\r\n { rows: 4, cols: 8, bw: 3, desc: \"棋盘格\", pattern: (c, r) => (c + r) % 2 === 0 },\r\n { rows: 4, cols: 8, bw: 3, desc: \"金字塔\", pattern: (c, r, _, tc) => c >= r && c < tc - r },\r\n { rows: 4, cols: 8, bw: 3, desc: \"交错排列\", pattern: (c, r) => r % 2 === 0 || (c >= 1 && c <= 6) },\r\n { rows: 4, cols: 8, bw: 3, desc: \"中空边框\", pattern: (c, r, tr, tc) => r === 0 || r === tr - 1 || c === 0 || c === tc - 1 },\r\n { rows: 4, cols: 10, bw: 2, desc: \"密集 10 列\", pattern: () => true },\r\n { rows: 7, cols: 8, bw: 3, desc: \"高墙 7 层\", pattern: () => true },\r\n { rows: 8, cols: 8, bw: 3, desc: \"满屏 8 层\", pattern: () => true },\r\n];\r\n\r\nfunction getLevel(level: number): LevelDef {\r\n return LEVELS[(level - 1) % LEVELS.length] as LevelDef;\r\n}\r\n\r\nfunction createBricks(level: number): Brick[] {\r\n const def = getLevel(level);\r\n const bricks: Brick[] = [];\r\n const gap = 2;\r\n const totalW = def.cols * def.bw + (def.cols - 1) * gap;\r\n const startX = Math.floor((GAME_WIDTH - totalW) / 2);\r\n const step = def.bw + gap;\r\n\r\n for (let row = 0; row < def.rows; row++) {\r\n for (let col = 0; col < def.cols; col++) {\r\n if (def.pattern(col, row, def.rows, def.cols)) {\r\n bricks.push({\r\n x: startX + col * step,\r\n y: 2 + row * 2,\r\n w: def.bw,\r\n alive: true,\r\n });\r\n }\r\n }\r\n }\r\n return bricks;\r\n}\r\n\r\n// ─── 游戏状态 ──────────────────────────────────────────────────\r\n\r\ninterface GameState {\r\n level: number;\r\n bricks: Brick[];\r\n paddleX: number;\r\n ball: Vec2;\r\n ballDir: Vec2;\r\n score: number;\r\n lives: number;\r\n gameOver: boolean;\r\n win: boolean;\r\n paused: boolean;\r\n}\r\n\r\nfunction createInitialState(level: number): GameState {\r\n const def = getLevel(level);\r\n const totalW = def.cols * def.bw + (def.cols - 1) * 2;\r\n const startX = Math.floor((GAME_WIDTH - totalW) / 2);\r\n return {\r\n level,\r\n bricks: createBricks(level),\r\n paddleX: Math.floor(GAME_WIDTH / 2) - Math.floor(PADDLE_WIDTH / 2),\r\n ball: { x: GAME_WIDTH / 2, y: GAME_HEIGHT - 3 },\r\n ballDir: { x: 1, y: -1 },\r\n score: 0,\r\n lives: 3,\r\n gameOver: false,\r\n win: false,\r\n paused: false,\r\n };\r\n}\r\n\r\n// ─── 物理更新 ──────────────────────────────────────────────────\r\n\r\nfunction update(state: GameState): void {\r\n if (state.paused || state.gameOver || state.win) return;\r\n\r\n state.ball.x += state.ballDir.x;\r\n state.ball.y += state.ballDir.y;\r\n\r\n if (state.ball.x <= 0) { state.ball.x = 0; state.ballDir.x = 1; }\r\n if (state.ball.x >= GAME_WIDTH - 1) { state.ball.x = GAME_WIDTH - 1; state.ballDir.x = -1; }\r\n if (state.ball.y <= 0) { state.ball.y = 0; state.ballDir.y = 1; }\r\n\r\n // 挡板碰撞\r\n if (\r\n state.ball.y === GAME_HEIGHT - 1 &&\r\n state.ball.x >= state.paddleX &&\r\n state.ball.x <= state.paddleX + PADDLE_WIDTH\r\n ) {\r\n state.ballDir.y = -1;\r\n const hitPos = (state.ball.x - state.paddleX) / PADDLE_WIDTH;\r\n state.ballDir.x = hitPos < 0.5 ? -1 : 1;\r\n }\r\n\r\n // 出界\r\n if (state.ball.y > GAME_HEIGHT) {\r\n state.lives--;\r\n if (state.lives <= 0) {\r\n state.gameOver = true;\r\n } else {\r\n state.ball = { x: GAME_WIDTH / 2, y: GAME_HEIGHT - 3 };\r\n state.ballDir = { x: 1, y: -1 };\r\n state.paddleX = Math.floor(GAME_WIDTH / 2) - Math.floor(PADDLE_WIDTH / 2);\r\n }\r\n }\r\n\r\n // 砖块碰撞\r\n const hitBrick = state.bricks.find((b) => {\r\n if (!b.alive) return false;\r\n return state.ball.x >= b.x && state.ball.x < b.x + b.w && state.ball.y >= b.y && state.ball.y < b.y + 1;\r\n });\r\n\r\n if (hitBrick) {\r\n hitBrick.alive = false;\r\n state.score += 10;\r\n state.ballDir.y = -state.ballDir.y;\r\n }\r\n\r\n if (state.bricks.every((b) => !b.alive)) state.win = true;\r\n}\r\n\r\n// ─── 画面渲染(含 ANSI 颜色) ─────────────────────────────────\r\n\r\nfunction buildBoard(state: GameState): string {\r\n const lines: string[] = [];\r\n\r\n function brickColorIndex(x: number, y: number): number | undefined {\r\n const row = Math.floor((y - 2) / 2);\r\n if (row >= 0) {\r\n const b = state.bricks.find((br) => br.y === y && br.alive && x >= br.x && x < br.x + br.w);\r\n if (b) return row % BRICK_COLORS.length;\r\n }\r\n return undefined;\r\n }\r\n\r\n for (let y = 0; y < GAME_HEIGHT; y++) {\r\n let line = \"\";\r\n for (let x = 0; x < GAME_WIDTH; x++) {\r\n const isBall = state.ball.x === x && state.ball.y === y;\r\n const isPaddle = y === GAME_HEIGHT - 1 && x >= state.paddleX && x < state.paddleX + PADDLE_WIDTH;\r\n const brickRow = brickColorIndex(x, y);\r\n\r\n if (isBall) {\r\n line += \"\\x1b[97m●\\x1b[0m\";\r\n } else if (isPaddle) {\r\n line += \"\\x1b[94m▄\\x1b[0m\";\r\n } else if (brickRow !== undefined) {\r\n line += `\\x1b[38;5;${BRICK_COLORS[brickRow] as number}m▀\\x1b[0m`;\r\n } else {\r\n line += \" \";\r\n }\r\n }\r\n lines.push(line);\r\n }\r\n\r\n return lines.map((l) => `│${l}│`).join(\"\\n\");\r\n}\r\n\r\n// ─── Ink 组件 ──────────────────────────────────────────────────\r\n\r\ninterface BrickBreakerGameProps {\r\n onExit: () => void;\r\n}\r\n\r\nfunction BrickBreakerGame({ onExit: _onExit }: BrickBreakerGameProps) {\r\n const [initialLevel, setInitialLevel] = useState(1);\r\n const [selectingLevel, setSelectingLevel] = useState(true);\r\n const stateRef = useRef<GameState>(createInitialState(initialLevel));\r\n const [tick, setTick] = useState(0);\r\n const onExitRef = useRef(_onExit);\r\n onExitRef.current = _onExit;\r\n\r\n useEffect(() => {\r\n if (selectingLevel) return;\r\n const interval = setInterval(() => {\r\n update(stateRef.current);\r\n setTick((t) => t + 1);\r\n }, 80);\r\n return () => clearInterval(interval);\r\n }, [selectingLevel]);\r\n\r\n // 重新开始(保留当前关卡)\r\n const restart = useCallback((level?: number) => {\r\n const lv = level ?? stateRef.current.level;\r\n stateRef.current = createInitialState(lv);\r\n setInitialLevel(lv);\r\n setSelectingLevel(false);\r\n setTick(0);\r\n }, []);\r\n\r\n // 选关\r\n const startLevelSelect = useCallback(() => {\r\n setSelectingLevel(true);\r\n }, []);\r\n\r\n useInput(\r\n useCallback((input, key) => {\r\n const s = stateRef.current;\r\n\r\n // 选关模式\r\n if (selectingLevel) {\r\n if (input >= \"1\" && input <= \"9\") {\r\n restart(Number(input));\r\n } else if (input === \"0\") {\r\n restart(10);\r\n } else if (key.escape || input === \"q\") {\r\n onExitRef.current();\r\n }\r\n return;\r\n }\r\n\r\n if (key.leftArrow) {\r\n s.paddleX = Math.max(0, s.paddleX - 1);\r\n setTick((t) => t + 1);\r\n } else if (key.rightArrow) {\r\n s.paddleX = Math.min(GAME_WIDTH - PADDLE_WIDTH, s.paddleX + 1);\r\n setTick((t) => t + 1);\r\n } else if (input === \"p\" || input === \" \") {\r\n s.paused = !s.paused;\r\n } else if (input === \"r\") {\r\n if (s.gameOver || s.win) restart();\r\n } else if (input === \"l\") {\r\n if (s.gameOver || s.win) startLevelSelect();\r\n } else if (input === \"q\" || key.escape) {\r\n onExitRef.current();\r\n }\r\n }, [selectingLevel, restart, startLevelSelect]),\r\n );\r\n\r\n const s = stateRef.current;\r\n const aliveCount = s.bricks.filter((b) => b.alive).length;\r\n const board = buildBoard(s);\r\n const def = getLevel(s.level);\r\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\r\n void tick;\r\n\r\n return (\r\n <Box flexDirection=\"column\">\r\n {/* 状态栏 */}\r\n <Box flexDirection=\"row\">\r\n <Box width={20}>\r\n <Text>\r\n 关卡 {s.level}: <Text color=\"cyan\">{def.desc}</Text>\r\n </Text>\r\n </Box>\r\n <Box width={12}>\r\n <Text>\r\n 分数: <Text color=\"yellow\">{String(s.score).padStart(3, \"0\")}</Text>\r\n </Text>\r\n </Box>\r\n <Box width={12}>\r\n <Text>\r\n 生命: <Text color=\"red\">{\"♥\".repeat(Math.max(0, s.lives))}</Text>\r\n </Text>\r\n </Box>\r\n <Box width={10}>\r\n <Text>\r\n 砖块: <Text color=\"cyan\">{aliveCount}</Text>\r\n </Text>\r\n </Box>\r\n <Box>\r\n <Text color={s.paused ? \"gray\" : \"green\"}>\r\n [{s.paused ? \"暂停\" : \"运行中\"}]\r\n </Text>\r\n </Box>\r\n </Box>\r\n\r\n {/* 游戏画面 */}\r\n <Box flexDirection=\"column\">\r\n <Text>┌{\"─\".repeat(GAME_WIDTH)}┐</Text>\r\n <Text>{selectingLevel ? \" \\x1b[90m选择关卡后开始...\\x1b[0m\" : board}</Text>\r\n <Text>└{\"─\".repeat(GAME_WIDTH)}┘</Text>\r\n </Box>\r\n\r\n {/* 选关界面 */}\r\n {selectingLevel && (\r\n <Box marginTop={1} flexDirection=\"column\">\r\n <Text bold color=\"yellow\">选择关卡</Text>\r\n <Box flexDirection=\"row\" flexWrap=\"wrap\">\r\n {LEVELS.map((lv, i) => (\r\n <Box key={i} width={22}>\r\n <Text>\r\n <Text color={initialLevel === i + 1 ? \"green\" : \"white\"}>\r\n {i + 1 === 10 ? \"0\" : String(i + 1)}\r\n </Text>\r\n . {lv.desc} ({lv.rows}×{lv.cols})\r\n </Text>\r\n </Box>\r\n ))}\r\n </Box>\r\n <Box marginTop={1}>\r\n <Text dimColor>按数字选关 q 退出</Text>\r\n </Box>\r\n </Box>\r\n )}\r\n\r\n {/* 结束/通关信息 */}\r\n {!selectingLevel && (s.gameOver || s.win) && (\r\n <Box marginTop={1}>\r\n <Text bold color={s.gameOver ? \"red\" : \"green\"}>\r\n {s.gameOver ? \"游戏结束!\" : \"恭喜通关!\"}\r\n </Text>\r\n <Text>\r\n {\" 分数: \"}<Text color=\"yellow\">{s.score}</Text>\r\n </Text>\r\n </Box>\r\n )}\r\n\r\n {/* 操作提示 */}\r\n {!selectingLevel && (\r\n <Box marginTop={1}>\r\n {s.gameOver || s.win ? (\r\n <Text dimColor>\r\n {\"← → 移动 r 重开 l 选关 q 退出\"}\r\n </Text>\r\n ) : (\r\n <Text dimColor>\r\n {\"← → 移动 p 暂停 q 退出\"}\r\n </Text>\r\n )}\r\n </Box>\r\n )}\r\n </Box>\r\n );\r\n}\r\n\r\n// ─── 导出 ──────────────────────────────────────────────────────\r\n\r\nexport default {\r\n id: \"brick-breaker\",\r\n name: \"Brick Breaker\",\r\n description: \"经典打砖块游戏,10 个关卡可选!\",\r\n play: async () => {\r\n await new Promise<void>((resolve) => {\r\n const { unmount } = render(\r\n <BrickBreakerGame onExit={() => { unmount(); resolve(); }} />,\r\n );\r\n });\r\n },\r\n};\r\n","import { Box, Text, useInput, render } from \"ink\";\r\nimport { useState, useEffect, useRef, useCallback } from \"react\";\r\n\r\nconst GAME_W = 66;\r\nconst GAME_H = 20; // 6 行分数 + 14 行掉落单词\r\nconst SCORE_H = 6;\r\nconst WORD_H = 14;\r\nconst MAX_WORDS = 10;\r\nconst CYBER_PALETTE = [\"#00ffff\", \"#ff00ff\", \"#00ff41\", \"#ff1493\", \"#8b00ff\"];\r\n\r\n// 3×5 像素大号数字(每个像素 = ██,每数字 6 列)\r\nconst DIGIT_ART: Record<string, string[]> = {\r\n \"0\": [\" ██████ \", \"██ ██\", \"██ ██\", \"██ ██\", \" ██████ \"],\r\n \"1\": [\" ██ \", \" ████ \", \" ██ \", \" ██ \", \" ██████ \"],\r\n \"2\": [\" ██████ \", \" ██ \", \" ██████ \", \" ██ \", \" ██████ \"],\r\n \"3\": [\" ██████ \", \" ██ \", \" ██████ \", \" ██ \", \" ██████ \"],\r\n \"4\": [\"██ ██\", \"██ ██\", \" ██████ \", \" ██ \", \" ██ \"],\r\n \"5\": [\" ██████ \", \" ██ \", \" ██████ \", \" ██ \", \" ██████ \"],\r\n \"6\": [\" ██████ \", \" ██ \", \" ██████ \", \" ██ ██\", \" ██████ \"],\r\n \"7\": [\" ██████ \", \" ██ \", \" ██ \", \" ██ \", \" ██ \"],\r\n \"8\": [\" ██████ \", \"██ ██\", \" ██████ \", \"██ ██\", \" ██████ \"],\r\n \"9\": [\" ██████ \", \"██ ██\", \" ██████ \", \" ██ \", \" ██████ \"],\r\n};\r\n\r\nfunction buildScoreLines(scoreStr: string): string[] {\r\n const lines = [\"\", \"\", \"\", \"\", \"\"];\r\n for (const ch of scoreStr) {\r\n const art = DIGIT_ART[ch] ?? DIGIT_ART[\"0\"]!;\r\n for (let r = 0; r < 5; r++) {\r\n lines[r] += \" \" + (art[r] ?? \"\");\r\n }\r\n }\r\n // 左右各留 6 格间距\r\n const pad = 6;\r\n return lines.map((l) => \" \".repeat(pad) + l);\r\n}\r\n\r\nfunction hexToRgb(hex: string): string {\r\n const r = parseInt(hex.slice(1, 3), 16);\r\n const g = parseInt(hex.slice(3, 5), 16);\r\n const b = parseInt(hex.slice(5, 7), 16);\r\n return `${r};${g};${b}`;\r\n}\r\n\r\n// ─── 200 个常见单词 ────────────────────────────────────────────\r\n\r\nconst WORDS_BANK = [\r\n // ── 框架 & 库 ──\r\n \"react\", \"vue\", \"next\", \"node\", \"axios\", \"express\", \"lodash\", \"jquery\",\r\n \"webpack\", \"vite\", \"babel\", \"eslint\", \"prettier\", \"tailwind\", \"bootstrap\",\r\n \"sass\", \"less\", \"postcss\", \"redux\", \"pinia\", \"vuex\", \"router\",\r\n \"nestjs\", \"socket\", \"graphql\", \"rest\", \"grpc\", \"prisma\", \"typeorm\",\r\n\r\n // ── 前端 ──\r\n \"html\", \"css\", \"jsx\", \"tsx\", \"dom\", \"spa\", \"ssr\", \"csr\", \"pwa\",\r\n \"component\", \"prop\", \"hook\", \"composable\", \"directive\", \"filter\", \"mixin\",\r\n \"template\", \"render\", \"virtual\", \"diff\", \"patch\", \"hydration\",\r\n \"responsive\", \"flexbox\", \"grid\", \"animation\", \"transition\",\r\n\r\n // ── 后端 ──\r\n \"api\", \"route\", \"middleware\", \"controller\", \"service\", \"module\",\r\n \"dto\", \"entity\", \"schema\", \"migration\", \"seeder\", \"factory\",\r\n \"auth\", \"jwt\", \"oauth\", \"session\", \"cookie\", \"token\", \"cors\",\r\n \"cache\", \"redis\", \"mq\", \"rabbit\", \"kafka\", \"nats\",\r\n\r\n // ── 数据库 ──\r\n \"sql\", \"mysql\", \"pg\", \"sqlite\", \"mongo\", \"redis\", \"orm\",\r\n \"table\", \"index\", \"query\", \"join\", \"union\", \"group\", \"order\",\r\n \"where\", \"having\", \"limit\", \"offset\", \"insert\", \"update\", \"delete\",\r\n\r\n // ── DevOps ──\r\n \"docker\", \"nginx\", \"linux\", \"bash\", \"shell\", \"yaml\", \"toml\",\r\n \"ci\", \"cd\", \"deploy\", \"rollback\", \"release\", \"build\", \"test\",\r\n \"lint\", \"format\", \"stage\", \"commit\", \"branch\", \"merge\", \"rebase\",\r\n\r\n // ── 数据结构 & 算法 ──\r\n \"array\", \"stack\", \"queue\", \"tree\", \"graph\", \"list\", \"map\", \"set\",\r\n \"sort\", \"search\", \"filter\", \"reduce\", \"map\", \"async\", \"await\",\r\n \"promise\", \"callback\", \"closure\", \"proxy\", \"reflect\", \"decorator\",\r\n\r\n // ── 常用操作 ──\r\n \"create\", \"read\", \"update\", \"delete\", \"crud\", \"parse\", \"stringify\",\r\n \"encode\", \"decode\", \"transform\", \"validate\", \"format\", \"parse\",\r\n \"upload\", \"download\", \"export\", \"import\", \"backup\", \"restore\",\r\n\r\n // ── 类型 & 变量 ──\r\n \"string\", \"number\", \"boolean\", \"object\", \"array\", \"tuple\", \"enum\",\r\n \"interface\", \"type\", \"class\", \"function\", \"method\", \"property\",\r\n \"public\", \"private\", \"static\", \"readonly\", \"optional\", \"abstract\",\r\n \"const\", \"let\", \"var\", \"void\", \"null\", \"undef\", \"never\", \"any\",\r\n\r\n // ── 补充 ──\r\n \"config\", \"logger\", \"monitor\", \"metric\", \"alert\", \"webhook\",\r\n \"endpoint\", \"payload\", \"header\", \"status\", \"timeout\", \"retry\",\r\n \"fallback\", \"circuit\", \"breaker\", \"throttle\", \"debounce\",\r\n \"scroll\", \"resize\", \"click\", \"hover\", \"focus\", \"blur\",\r\n];\r\n\r\nfunction randomWord(used: Set<string>): string {\r\n let w: string;\r\n do { w = WORDS_BANK[Math.floor(Math.random() * WORDS_BANK.length)] as string; }\r\n while (used.has(w));\r\n return w;\r\n}\r\n\r\n// ─── 游戏状态 ──────────────────────────────────────────────────\r\n\r\ninterface DropWord {\r\n text: string;\r\n row: number;\r\n col: number;\r\n}\r\n\r\ninterface GameState {\r\n words: DropWord[];\r\n score: number;\r\n lives: number;\r\n speed: number;\r\n spawnTimer: number;\r\n gameOver: boolean;\r\n paused: boolean;\r\n typed: string;\r\n target: string | null;\r\n combo: number;\r\n message: string;\r\n messageTimer: number;\r\n usedWords: Set<string>;\r\n}\r\n\r\nfunction createInitialState(): GameState {\r\n return {\r\n words: [],\r\n score: 0,\r\n lives: 3,\r\n speed: 0.3,\r\n spawnTimer: 0,\r\n gameOver: false,\r\n paused: false,\r\n typed: \"\",\r\n target: null,\r\n combo: 0,\r\n message: \"\",\r\n messageTimer: 0,\r\n usedWords: new Set(),\r\n };\r\n}\r\n\r\nfunction pickTarget(s: GameState): string | null {\r\n // 选取最靠左的存活单词作为目标\r\n let best: DropWord | null = null;\r\n for (const w of s.words) {\r\n if (!best || w.col < best.col) best = w;\r\n }\r\n return best?.text ?? null;\r\n}\r\n\r\n// ─── 物理更新 ──────────────────────────────────────────────────\r\n\r\nfunction update(s: GameState): void {\r\n if (s.paused || s.gameOver) return;\r\n\r\n // 生成新单词\r\n s.spawnTimer++;\r\n if (s.spawnTimer >= Math.max(20, 50 - Math.floor(s.speed * 15))) {\r\n s.spawnTimer = 0;\r\n if (s.words.length < MAX_WORDS) {\r\n const used = new Set(s.usedWords);\r\n for (const w of s.words) used.add(w.text);\r\n const text = randomWord(used);\r\n // 找空白行\r\n const usedRows = new Set(s.words.map((w) => w.row));\r\n let row = -1;\r\n for (let r = SCORE_H; r < GAME_H; r++) {\r\n if (!usedRows.has(r)) { row = r; break; }\r\n }\r\n if (row >= 0) {\r\n s.words.push({ text, row, col: GAME_W - 1 });\r\n s.usedWords.add(text);\r\n }\r\n }\r\n }\r\n\r\n // 移动单词\r\n for (const w of s.words) {\r\n w.col -= s.speed;\r\n }\r\n\r\n // 移除超出左边的 + 更新目标\r\n const before = s.words.length;\r\n s.words = s.words.filter((w) => w.col > -w.text.length);\r\n const removed = before - s.words.length;\r\n\r\n // 单词漏掉了 → 扣命\r\n if (removed > 0) {\r\n s.lives -= removed;\r\n s.typed = \"\";\r\n s.target = pickTarget(s);\r\n if (s.lives <= 0) {\r\n s.gameOver = true;\r\n s.words = [];\r\n }\r\n }\r\n\r\n // 更新目标\r\n s.target = pickTarget(s);\r\n\r\n // 消息计时\r\n if (s.messageTimer > 0) {\r\n s.messageTimer--;\r\n if (s.messageTimer <= 0) s.message = \"\";\r\n }\r\n}\r\n\r\n// ─── 画面渲染 ──────────────────────────────────────────────────\r\n\r\nfunction buildGameView(s: GameState, scoreLines: string[], scoreColor: string, message: string): string[] {\r\n const rows: string[] = [];\r\n\r\n for (let y = 0; y < GAME_H; y++) {\r\n let line = \"\";\r\n\r\n // 分数区域(前 6 行)\r\n if (y < SCORE_H) {\r\n if (y < 5) {\r\n // 数字行 — 先补足宽度再套赛博朋克色\r\n const raw = (scoreLines[y] ?? \"\").padEnd(GAME_W);\r\n line = `\\x1b[38;2;${hexToRgb(scoreColor)}m${raw}\\x1b[0m`;\r\n } else if (y === 5) {\r\n // 第 6 行:连击 / 暂停\r\n if (s.combo >= 3) {\r\n const comboText = `${s.combo}连击!`;\r\n const pad = Math.floor((GAME_W - comboText.length) / 2);\r\n const comboColor = CYBER_PALETTE[s.combo % CYBER_PALETTE.length] as string;\r\n const raw = \" \".repeat(pad) + comboText + \" \".repeat(GAME_W - pad - comboText.length);\r\n line = `\\x1b[38;2;${hexToRgb(comboColor)}m${raw}\\x1b[0m`;\r\n } else if (s.paused) {\r\n const pauseText = \"暂停\";\r\n const pad = Math.floor((GAME_W - pauseText.length) / 2);\r\n line = \" \".repeat(pad) + pauseText;\r\n line = line.padEnd(GAME_W);\r\n }\r\n }\r\n // 补全到 GAME_W 宽度\r\n line = line.padEnd(GAME_W);\r\n } else {\r\n // 单词区域\r\n // 第 1 行显示消息(如果有)\r\n if (y === SCORE_H && message) {\r\n const pad = Math.floor((GAME_W - message.length) / 2);\r\n const raw = \" \".repeat(pad) + message + \" \".repeat(GAME_W - pad - message.length);\r\n const msgColor = CYBER_PALETTE[Math.floor(Math.random() * CYBER_PALETTE.length)] as string;\r\n line = `\\x1b[38;2;${hexToRgb(msgColor)}m${raw}\\x1b[0m`;\r\n } else {\r\n for (let x = 0; x < GAME_W; x++) {\r\n const word = s.words.find((w) => {\r\n const charIdx = x - Math.floor(w.col);\r\n return charIdx >= 0 && charIdx < w.text.length && w.row === y;\r\n });\r\n if (word) {\r\n const charIdx = x - Math.floor(word.col);\r\n const ch = word.text[charIdx] as string;\r\n const isTarget = word.text === s.target;\r\n const typedIdx = s.target === word.text ? s.typed.length : 0;\r\n const isTyped = isTarget && charIdx < typedIdx;\r\n if (isTarget) {\r\n line += isTyped ? `\\x1b[92m${ch}\\x1b[0m` : `\\x1b[97m${ch}\\x1b[0m`;\r\n } else {\r\n line += `\\x1b[90m${ch}\\x1b[0m`;\r\n }\r\n } else {\r\n line += \" \";\r\n }\r\n }\r\n }\r\n }\r\n\r\n rows.push(line);\r\n }\r\n return rows;\r\n}\r\n\r\n// ─── Ink 组件 ──────────────────────────────────────────────────\r\n\r\ninterface CoderCheckProps {\r\n onExit: () => void;\r\n}\r\n\r\nfunction CoderCheck({ onExit: _onExit }: CoderCheckProps) {\r\n const stateRef = useRef<GameState>(createInitialState());\r\n const [tick, setTick] = useState(0);\r\n const [colorOffset, setColorOffset] = useState(0);\r\n const onExitRef = useRef(_onExit);\r\n onExitRef.current = _onExit;\r\n\r\n // 赛博朋克颜色动画\r\n useEffect(() => {\r\n const timer = setInterval(() => {\r\n setColorOffset((prev) => (prev + 1) % CYBER_PALETTE.length);\r\n }, 400);\r\n return () => clearInterval(timer);\r\n }, []);\r\n\r\n // 游戏循环\r\n useEffect(() => {\r\n const interval = setInterval(() => {\r\n update(stateRef.current);\r\n setTick((t) => t + 1);\r\n }, 60);\r\n return () => clearInterval(interval);\r\n }, []);\r\n\r\n useInput(\r\n useCallback((input, key) => {\r\n const s = stateRef.current;\r\n if (s.gameOver) {\r\n if (input === \"r\") {\r\n stateRef.current = createInitialState();\r\n setTick(0);\r\n } else if (input === \"q\" || key.escape) {\r\n onExitRef.current();\r\n }\r\n return;\r\n }\r\n\r\n if ((input === \"p\" && key.ctrl) || input === \" \") {\r\n s.paused = !s.paused;\r\n return;\r\n }\r\n if ((input === \"q\" && key.ctrl) || key.escape) {\r\n onExitRef.current();\r\n return;\r\n }\r\n\r\n if (s.paused) return;\r\n\r\n // 打字输入\r\n if (input.length === 1 && input >= \"a\" && input <= \"z\") {\r\n // 选取目标\r\n if (!s.target) {\r\n s.target = pickTarget(s);\r\n }\r\n if (s.target) {\r\n const nextChar = s.target[s.typed.length];\r\n if (nextChar === input) {\r\n s.typed += input;\r\n // 检查是否完成\r\n if (s.typed === s.target) {\r\n // 移除该单词\r\n s.words = s.words.filter((w) => w.text !== s.target);\r\n s.combo++;\r\n // 连击 >= 3 时以当前分数翻倍奖励\r\n const prevScore = s.score;\r\n const basePts = s.target.length;\r\n const comboBonus = s.combo >= 3 ? s.score : 0;\r\n s.score += basePts + comboBonus;\r\n s.speed = s.speed + 0.02;\r\n\r\n // 每 500 分提速\r\n const prevMilestone = Math.floor(prevScore / 500);\r\n const newMilestone = Math.floor(s.score / 500);\r\n if (newMilestone > prevMilestone) {\r\n s.speed += 0.15;\r\n }\r\n\r\n // 超过 99999 分提示通关\r\n if (s.score >= 100000 && prevScore < 100000) {\r\n s.message = \"恭喜通关! 难度最大化!\";\r\n s.messageTimer = 100;\r\n }\r\n s.typed = \"\";\r\n s.target = pickTarget(s);\r\n }\r\n } else {\r\n // 打错字符,连击中断\r\n s.combo = 0;\r\n }\r\n }\r\n }\r\n }, []),\r\n );\r\n\r\n const s = stateRef.current;\r\n const scoreColor = CYBER_PALETTE[colorOffset] as string;\r\n const scoreStr = String(s.score).padStart(5, \"0\");\r\n const scoreLines = buildScoreLines(scoreStr);\r\n const view = buildGameView(s, scoreLines, scoreColor, s.message);\r\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\r\n void tick;\r\n\r\n return (\r\n <Box flexDirection=\"column\" paddingX={1}>\r\n {/* 顶栏:生命 + 速度 */}\r\n <Box flexDirection=\"row\">\r\n <Text>\r\n 生命 <Text color=\"red\">{\"♥\".repeat(Math.max(0, s.lives))}</Text>\r\n {\" \"}速度 <Text color=\"cyan\">Lv.{Math.floor(s.speed * 10)}</Text>\r\n </Text>\r\n </Box>\r\n\r\n {/* 目标单词提示 */}\r\n {!s.gameOver && s.target && (\r\n <Box marginTop={1}>\r\n <Text>\r\n 打字: <Text color=\"green\">{s.typed}</Text>\r\n <Text color=\"white\">{s.target.slice(s.typed.length)}</Text>\r\n </Text>\r\n </Box>\r\n )}\r\n\r\n {/* 游戏画面(含分数) */}\r\n <Box flexDirection=\"column\" marginTop={1}>\r\n <Text>┌{\"─\".repeat(GAME_W)}┐</Text>\r\n {view.map((row, i) => (\r\n <Text key={i}>{`│${row}│`}</Text>\r\n ))}\r\n <Text>└{\"─\".repeat(GAME_W)}┘</Text>\r\n </Box>\r\n\r\n {/* 游戏结束 */}\r\n {s.gameOver && (\r\n <Box marginTop={1}>\r\n <Text bold color=\"red\">\r\n 游戏结束!\r\n </Text>\r\n <Text>\r\n {\" 得分: \"}<Text color=\"yellow\">{s.score}</Text>\r\n {\" r 重开 q 退出\"}\r\n </Text>\r\n </Box>\r\n )}\r\n\r\n {/* 操作提示 */}\r\n <Box marginTop={1}>\r\n <Text dimColor>\r\n {\"打字消除单词 空格/Ctrl+P暂停 Ctrl+Q退出\"}\r\n </Text>\r\n </Box>\r\n </Box>\r\n );\r\n}\r\n\r\n// ─── 导出 ──────────────────────────────────────────────────────\r\n\r\nexport default {\r\n id: \"coder-check\",\r\n name: \"Coder Check\",\r\n description: \"极速打字游戏,输入单词消除它们!\",\r\n play: async () => {\r\n await new Promise<void>((resolve) => {\r\n const { unmount } = render(\r\n <CoderCheck onExit={() => { unmount(); resolve(); }} />,\r\n );\r\n });\r\n },\r\n};\r\n","import type { Direction, GameState, GameConfig, Food, Point } from './types.js';\r\n\r\n// ─── 辅助函数 ───────────────────────────────────────────\r\n\r\nfunction isOpposite(a: Direction, b: Direction): boolean {\r\n return (\r\n (a === 'UP' && b === 'DOWN') ||\r\n (a === 'DOWN' && b === 'UP') ||\r\n (a === 'LEFT' && b === 'RIGHT') ||\r\n (a === 'RIGHT' && b === 'LEFT')\r\n );\r\n}\r\n\r\nfunction samePoint(a: Point, b: Point): boolean {\r\n return a.x === b.x && a.y === b.y;\r\n}\r\n\r\nfunction movePoint(p: Point, dir: Direction): Point {\r\n switch (dir) {\r\n case 'UP': return { x: p.x, y: p.y - 1 };\r\n case 'DOWN': return { x: p.x, y: p.y + 1 };\r\n case 'LEFT': return { x: p.x - 1, y: p.y };\r\n case 'RIGHT': return { x: p.x + 1, y: p.y };\r\n }\r\n}\r\n\r\nfunction wrapPoint(p: Point, config: GameConfig): Point {\r\n return {\r\n x: ((p.x % config.width) + config.width) % config.width,\r\n y: ((p.y % config.height) + config.height) % config.height,\r\n };\r\n}\r\n\r\n// ─── 食物生成 ───────────────────────────────────────────\r\n\r\n/**\r\n * 在蛇身 + 指定排除位置之外的随机空位生成食物。\r\n * 排除位置用于防止普通食物和特殊食物重叠。\r\n */\r\nfunction generateFood(\r\n snake: Point[],\r\n config: GameConfig,\r\n type: 'normal' | 'special',\r\n excludePositions: Point[] = [],\r\n): Food | null {\r\n const occupied = new Set(snake.map(p => `${p.x},${p.y}`));\r\n // 排除位置也加入已占用集合(防止与另一种食物重叠)\r\n for (const p of excludePositions) {\r\n occupied.add(`${p.x},${p.y}`);\r\n }\r\n\r\n const totalCells = config.width * config.height;\r\n if (occupied.size >= totalCells) return null;\r\n\r\n let pos: Point;\r\n do {\r\n pos = {\r\n x: Math.floor(Math.random() * config.width),\r\n y: Math.floor(Math.random() * config.height),\r\n };\r\n } while (occupied.has(`${pos.x},${pos.y}`));\r\n\r\n return {\r\n position: pos,\r\n type,\r\n ...(type === 'special' ? { spawnedAt: Date.now() } : {}),\r\n };\r\n}\r\n\r\n// ─── 状态工厂 ───────────────────────────────────────────\r\n\r\n/** 创建初始游戏状态 */\r\nexport function createInitialState(config: GameConfig): GameState {\r\n const startX = Math.floor(config.width / 2);\r\n const startY = Math.floor(config.height / 2);\r\n\r\n const snake: Point[] = [\r\n { x: startX, y: startY },\r\n { x: startX - 1, y: startY },\r\n { x: startX - 2, y: startY },\r\n ];\r\n\r\n const food = generateFood(snake, config, 'normal')!;\r\n\r\n return {\r\n snake,\r\n direction: 'RIGHT',\r\n nextDirection: 'RIGHT',\r\n food,\r\n specialFood: null,\r\n score: 0,\r\n speedLevel: 1,\r\n isGameOver: false,\r\n isPaused: false,\r\n wallWrap: Math.random() < 0.5,\r\n tickInterval: config.initialSpeed,\r\n };\r\n}\r\n\r\n// ─── 核心逻辑:每帧推进 ─────────────────────────────────\r\n\r\nexport function tick(state: GameState, config: GameConfig): void {\r\n // 1. 应用缓冲方向(防止同帧内 180° 掉头)\r\n state.direction = state.nextDirection;\r\n\r\n // 2. 计算新蛇头位置\r\n const head = state.snake[0]!;\r\n let newHead = movePoint(head, state.direction);\r\n\r\n // 3. 边界处理\r\n const outOfBounds =\r\n newHead.x < 0 ||\r\n newHead.x >= config.width ||\r\n newHead.y < 0 ||\r\n newHead.y >= config.height;\r\n\r\n if (outOfBounds) {\r\n if (state.wallWrap) {\r\n newHead = wrapPoint(newHead, config);\r\n } else {\r\n state.isGameOver = true;\r\n return;\r\n }\r\n }\r\n\r\n // 4. 判断本帧是否会吃到食物(影响自碰撞检测的判定范围)\r\n const ateNormal = samePoint(newHead, state.food.position);\r\n const ateSpecial = state.specialFood !== null && samePoint(newHead, state.specialFood.position);\r\n const willEat = ateNormal || ateSpecial;\r\n\r\n // 自碰撞检测:不吃食物时排除蛇尾(因为蛇尾会移除)\r\n const bodyToCheck = willEat ? state.snake : state.snake.slice(0, -1);\r\n for (const segment of bodyToCheck) {\r\n if (samePoint(newHead, segment)) {\r\n state.isGameOver = true;\r\n return;\r\n }\r\n }\r\n\r\n // 5. 移动蛇\r\n state.snake.unshift(newHead);\r\n\r\n // 6. 处理进食\r\n if (ateNormal) {\r\n state.score += 1;\r\n updateSpeed(state, config);\r\n\r\n // 生成新普通食物时排除特殊食物的位置\r\n const exclude: Point[] = state.specialFood ? [state.specialFood.position] : [];\r\n const newFood = generateFood(state.snake, config, 'normal', exclude);\r\n if (newFood) {\r\n state.food = newFood;\r\n } else {\r\n // 没有空位了(蛇填满了棋盘),宣告胜利\r\n state.isGameOver = true;\r\n return;\r\n }\r\n\r\n // 25% 概率生成特殊食物,排除普通食物的位置\r\n if (state.specialFood === null && Math.random() < 0.25) {\r\n state.specialFood = generateFood(state.snake, config, 'special', [state.food.position]);\r\n }\r\n } else if (ateSpecial) {\r\n state.score += 3;\r\n updateSpeed(state, config);\r\n state.specialFood = null;\r\n } else {\r\n // 没吃到食物,移除蛇尾\r\n state.snake.pop();\r\n }\r\n\r\n // 7. 特殊食物倒计时(5 秒后消失)\r\n if (state.specialFood?.spawnedAt !== undefined) {\r\n if (Date.now() - state.specialFood.spawnedAt > 5000) {\r\n state.specialFood = null;\r\n }\r\n }\r\n}\r\n\r\n// ─── 速度更新 ───────────────────────────────────────────\r\n\r\nfunction updateSpeed(state: GameState, config: GameConfig): void {\r\n const newLevel = Math.floor(state.score / config.scorePerLevel) + 1;\r\n state.speedLevel = newLevel;\r\n state.tickInterval = Math.max(\r\n config.minSpeed,\r\n config.initialSpeed - (newLevel - 1) * config.speedDecrement,\r\n );\r\n}\r\n\r\n// ─── 方向变更(外部调用) ──────────────────────────────\r\n\r\n/**\r\n * 尝试改变方向。\r\n * 检查新方向是否与当前移动方向相反,防止 180° 掉头。\r\n */\r\nexport function changeDirection(state: GameState, newDir: Direction): void {\r\n if (!isOpposite(newDir, state.direction)) {\r\n state.nextDirection = newDir;\r\n }\r\n}\r\n","import chalk from 'chalk';\r\nimport type { GameState, GameConfig, Point } from './types.js';\r\n\r\n// 每个格子在终端中占 2 个字符宽度,让网格看起来更接近正方形\r\nconst CELL_W = 2;\r\nconst EMPTY = ' ';\r\nconst BODY_CHAR = '▓';\r\nconst HEAD_CHAR = '▓';\r\nconst NORMAL_FOOD_CHAR = '◆';\r\nconst SPECIAL_FOOD_CHAR = '★';\r\n\r\n// ─── ANSI 控制序列 ──────────────────────────────────────\r\n\r\nconst CSI = '\\x1b[';\r\nconst CURSOR_HIDE = `${CSI}?25l`;\r\nconst CURSOR_SHOW = `${CSI}?25h`;\r\nconst CURSOR_HOME = `${CSI}H`;\r\n\r\n/** 隐藏终端光标 */\r\nexport function hideCursor(): void {\r\n process.stdout.write(CURSOR_HIDE);\r\n}\r\n\r\n/** 显示终端光标 */\r\nexport function showCursor(): void {\r\n process.stdout.write(CURSOR_SHOW);\r\n}\r\n\r\n/** 清屏并复位光标 */\r\nexport function clearScreen(): void {\r\n process.stdout.write(`${CSI}2J${CURSOR_HOME}`);\r\n}\r\n\r\n// ─── 着色辅助 ───────────────────────────────────────────\r\n\r\nfunction cellColor(char: string, type: 'snake-head' | 'snake-body' | 'normal-food' | 'special-food' | 'empty'): string {\r\n switch (type) {\r\n case 'snake-head': return chalk.greenBright(char);\r\n case 'snake-body': return chalk.green(char);\r\n case 'normal-food': return chalk.red(char);\r\n case 'special-food': return chalk.yellow(char);\r\n case 'empty': return char;\r\n }\r\n}\r\n\r\n// ─── 主渲染函数 ─────────────────────────────────────────\r\n\r\n/**\r\n * 将当前游戏状态渲染到终端。\r\n * 先移动光标到左上角再逐行覆盖,避免闪烁。\r\n */\r\nexport function renderToTerminal(state: GameState, config: GameConfig): void {\r\n const lines = buildRenderLines(state, config);\r\n // 先清屏再回左上角绘制,避免状态切换/终端滚动时旧行残留导致重叠\r\n process.stdout.write(`${CSI}2J${CURSOR_HOME}` + lines.join('\\n'));\r\n}\r\n\r\nfunction buildRenderLines(state: GameState, config: GameConfig): string[] {\r\n const { width: W, height: H } = config;\r\n const lines: string[] = [];\r\n\r\n // ── 构建网格数据 ──\r\n // grid[y][x] 存储该位置要显示的内容类型\r\n const grid: string[][] = Array.from({ length: H }, () => Array(W).fill('empty'));\r\n\r\n // 蛇身(除蛇头外)\r\n for (let i = 1; i < state.snake.length; i++) {\r\n const seg = state.snake[i]!;\r\n if (seg.y >= 0 && seg.y < H && seg.x >= 0 && seg.x < W) {\r\n grid[seg.y]![seg.x] = 'snake-body';\r\n }\r\n }\r\n\r\n // 蛇头(覆盖蛇身标记,确保蛇头颜色优先)\r\n const head = state.snake[0]!;\r\n if (head.y >= 0 && head.y < H && head.x >= 0 && head.x < W) {\r\n grid[head.y]![head.x] = 'snake-head';\r\n }\r\n\r\n // 普通食物\r\n const fp = state.food.position;\r\n if (grid[fp.y]![fp.x] === 'empty') {\r\n grid[fp.y]![fp.x] = 'normal-food';\r\n }\r\n\r\n // 特殊食物\r\n if (state.specialFood) {\r\n const sp = state.specialFood.position;\r\n if (grid[sp.y]![sp.x] === 'empty') {\r\n grid[sp.y]![sp.x] = 'special-food';\r\n }\r\n }\r\n\r\n // ── 顶部边框 ──\r\n lines.push(' ' + chalk.gray('╔' + '══'.repeat(W) + '╗'));\r\n\r\n // ── 每行渲染 ──\r\n for (let y = 0; y < H; y++) {\r\n let row = ' ' + chalk.gray('║');\r\n for (let x = 0; x < W; x++) {\r\n const cellType = grid[y]![x]!;\r\n let char: string;\r\n switch (cellType) {\r\n case 'snake-head':\r\n char = HEAD_CHAR.repeat(CELL_W);\r\n break;\r\n case 'snake-body':\r\n char = BODY_CHAR.repeat(CELL_W);\r\n break;\r\n case 'normal-food':\r\n char = NORMAL_FOOD_CHAR + ' ';\r\n break;\r\n case 'special-food':\r\n char = SPECIAL_FOOD_CHAR + ' ';\r\n break;\r\n default:\r\n char = EMPTY;\r\n }\r\n row += cellColor(char, cellType as any);\r\n }\r\n row += chalk.gray('║');\r\n lines.push(row);\r\n }\r\n\r\n // ── 底部边框 ──\r\n lines.push(' ' + chalk.gray('╚' + '══'.repeat(W) + '╝'));\r\n\r\n // ── HUD 信息 ──\r\n lines.push('');\r\n lines.push(\r\n ` ${chalk.cyan('分数')}: ${state.score} ` +\r\n `${chalk.cyan('长度')}: ${state.snake.length} ` +\r\n `${chalk.cyan('速度')}: Lv.${state.speedLevel} ` +\r\n `${chalk.cyan('边界')}: ${state.wallWrap ? '穿墙' : '撞墙'}`,\r\n );\r\n\r\n // 特殊食物倒计时提示\r\n if (state.specialFood?.spawnedAt !== undefined) {\r\n const remaining = Math.ceil((5000 - (Date.now() - state.specialFood.spawnedAt)) / 1000);\r\n if (remaining > 0) {\r\n lines.push(` ${chalk.yellow(`⭐ 特殊食物 ${remaining} 秒后消失`)}`);\r\n }\r\n }\r\n\r\n // 操作提示\r\n lines.push(` ${chalk.gray('W/A/S/D 或 方向键移动 | P 暂停 | M 切换模式 | Q 退出')}`);\r\n\r\n // 暂停 / 游戏结束提示\r\n if (state.isPaused) {\r\n lines.push('');\r\n lines.push(` ${chalk.yellow('⏸ 已暂停 — 按 P 继续')}`);\r\n }\r\n\r\n if (state.isGameOver) {\r\n lines.push('');\r\n lines.push(` ${chalk.red.bold('💀 游戏结束!')} ${chalk.cyan('最终分数: ' + state.score)}`);\r\n lines.push(` ${chalk.gray('按 R 重新开始 | 按 Q 退出')}`);\r\n }\r\n\r\n return lines;\r\n}\r\n","import * as readline from 'node:readline';\r\nimport type { Direction, GameState } from './types.js';\r\nimport { changeDirection } from './engine.js';\r\n\r\n// ─── 按键 → 方向映射 ───────────────────────────────────\r\n\r\nconst KEY_TO_DIR: Record<string, Direction> = {\r\n up: 'UP',\r\n w: 'UP',\r\n W: 'UP',\r\n down: 'DOWN',\r\n s: 'DOWN',\r\n S: 'DOWN',\r\n left: 'LEFT',\r\n a: 'LEFT',\r\n A: 'LEFT',\r\n right: 'RIGHT',\r\n d: 'RIGHT',\r\n D: 'RIGHT',\r\n};\r\n\r\n// ─── 输入设置 ───────────────────────────────────────────\r\n\r\nexport interface InputContext {\r\n state: GameState;\r\n}\r\n\r\nexport type InputCleanup = () => void;\r\n\r\n/**\r\n * 设置终端键盘输入监听。\r\n * - WASD / 方向键 → 控制方向\r\n * - P → 暂停/继续\r\n * - R → 游戏结束后重新开始\r\n * - Q / Ctrl+C → 退出游戏\r\n *\r\n * @param ctx 可变状态上下文(重启时替换 state 引用)\r\n * @param onQuit 退出回调\r\n * @param onRestart 重新开始回调(应返回新的 GameState)\r\n */\r\nexport function setupInput(\r\n ctx: InputContext,\r\n onQuit: () => void,\r\n onRestart: () => GameState,\r\n): InputCleanup {\r\n readline.emitKeypressEvents(process.stdin);\r\n\r\n if (process.stdin.isTTY) {\r\n process.stdin.setRawMode(true);\r\n }\r\n\r\n const handler = (_: unknown, key: readline.Key) => {\r\n if (!key || !key.name) return;\r\n\r\n const { name, ctrl } = key;\r\n\r\n // Ctrl+C / Ctrl+D → 退出\r\n if (ctrl && (name === 'c' || name === 'd')) {\r\n onQuit();\r\n return;\r\n }\r\n\r\n // Q → 退出\r\n if (name === 'q') {\r\n onQuit();\r\n return;\r\n }\r\n\r\n const state = ctx.state;\r\n\r\n // 游戏结束后 R → 重新开始\r\n if (state.isGameOver) {\r\n if (name === 'r') {\r\n ctx.state = onRestart();\r\n }\r\n return;\r\n }\r\n\r\n // P → 暂停/继续\r\n if (name === 'p') {\r\n state.isPaused = !state.isPaused;\r\n return;\r\n }\r\n\r\n // M → 切换边界模式(穿墙 / 撞墙)\r\n if (name === 'm') {\r\n state.wallWrap = !state.wallWrap;\r\n return;\r\n }\r\n\r\n // 方向控制\r\n const dir = KEY_TO_DIR[name];\r\n if (dir) {\r\n changeDirection(state, dir);\r\n }\r\n };\r\n\r\n process.stdin.on('keypress', handler);\r\n\r\n return () => {\r\n process.stdin.removeListener('keypress', handler);\r\n if (process.stdin.isTTY) {\r\n process.stdin.setRawMode(false);\r\n }\r\n };\r\n}\r\n","import type { Game } from '../index.js';\r\nimport type { GameConfig } from './types.js';\r\nimport { createInitialState, tick } from './engine.js';\r\nimport { renderToTerminal, clearScreen, hideCursor, showCursor } from './renderer.js';\r\nimport { setupInput } from './input.js';\r\nimport type { InputContext } from './input.js';\r\n\r\nconst CONFIG: GameConfig = {\r\n width: 20,\r\n height: 16,\r\n initialSpeed: 180,\r\n minSpeed: 60,\r\n speedDecrement: 10,\r\n scorePerLevel: 5,\r\n};\r\n\r\n/** 游戏结束后或暂停时的循环间隔(不需要高帧率) */\r\nconst IDLE_INTERVAL = 200;\r\n\r\nconst snakeGame: Game = {\r\n id: 'snake',\r\n name: 'Big Snake',\r\n description: '经典贪吃蛇,双食物系统、难度递增、穿墙/撞墙模式',\r\n play: async () => {\r\n const ctx: InputContext = { state: createInitialState(CONFIG) };\r\n\r\n return new Promise<void>((resolve) => {\r\n const cleanupInput = setupInput(\r\n ctx,\r\n () => {\r\n // ── 退出 ──\r\n stop = true;\r\n clearTimeout(timerId);\r\n showCursor();\r\n cleanupInput();\r\n resolve();\r\n },\r\n () => {\r\n // ── 重新开始 ──\r\n return createInitialState(CONFIG);\r\n },\r\n );\r\n\r\n // 初始化终端\r\n clearScreen();\r\n hideCursor();\r\n\r\n let stop = false;\r\n let timerId: ReturnType<typeof setTimeout>;\r\n\r\n function loop(): void {\r\n if (stop) return;\r\n\r\n const s = ctx.state;\r\n\r\n if (!s.isGameOver && !s.isPaused) {\r\n tick(s, CONFIG);\r\n }\r\n\r\n renderToTerminal(s, CONFIG);\r\n\r\n // 游戏进行中 → 用 tick 速度;暂停/结束时用恒定低速,节省 CPU\r\n const interval = s.isGameOver || s.isPaused ? IDLE_INTERVAL : s.tickInterval;\r\n timerId = setTimeout(loop, interval);\r\n }\r\n\r\n timerId = setTimeout(loop, 0);\r\n });\r\n },\r\n};\r\n\r\nexport default snakeGame;\r\n","import { registerGame, listGames } from \"./index.js\";\r\nimport type { Game } from \"./index.js\";\r\nimport brickBreaker from \"./brick-breaker/index.js\";\r\nimport coderCheck from \"./coder-check/index.js\";\r\nimport snake from \"./snake/index.js\";\r\n\r\n/** 在此注册所有游戏 */\r\nexport function initGames(): Game[] {\r\n registerGame(brickBreaker);\r\n registerGame(coderCheck);\r\n registerGame(snake);\r\n return listGames();\r\n}\r\n","import { Box, Text, useInput } from \"ink\";\r\nimport { useState, useCallback, useEffect } from \"react\";\r\nimport asciichart from \"asciichart\";\r\nimport os from \"node:os\";\r\nimport { join } from \"node:path\";\r\nimport type { StockRow } from \"./types.js\";\r\nimport { useDoubleCtrlC } from \"../ui/useDoubleCtrlC.js\";\r\n\r\n// ---------------------------------------------------------------------------\r\n// 自选股配置文件路径(取用户全局 ~/.dskcode/settings.json 的真实绝对路径)\r\n// ---------------------------------------------------------------------------\r\n\r\nconst SETTINGS_PATH = join(os.homedir(), \".dskcode\", \"settings.json\");\r\n\r\n/**\r\n * 将本地绝对路径转换为 file:// URL,兼容 Windows 与 POSIX。\r\n * Windows: C:\\Users\\me\\.dskcode\\settings.json → file:///C:/Users/me/.dskcode/settings.json\r\n */\r\nfunction toFileUrl(p: string): string {\r\n const norm = p.replace(/\\\\/g, \"/\");\r\n if (process.platform === \"win32\") {\r\n return \"file://\" + norm.replace(/^([a-zA-Z]:)/, \"/$1\");\r\n }\r\n return \"file://\" + norm;\r\n}\r\n\r\n/**\r\n * 用 OSC 8 转义序列把 text 包装成可点击的终端超链接。\r\n * 现代终端(Windows Terminal / iTerm2 / kitty / WezTerm 等)点击后会以系统默认程序打开该 URI。\r\n * 不可见宽度的转义对 string-width 透明(strip-ansi 会剥除),不影响布局。\r\n */\r\nfunction osc8Link(uri: string, text: string): string {\r\n return `\\x1b]8;;${uri}\\x1b\\\\${text}\\x1b]8;;\\x1b\\\\`;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 分时数据接口\r\n// ---------------------------------------------------------------------------\r\n\r\n/** API 接口地址模板 */\r\nconst MINUTE_API = \"https://web.ifzq.gtimg.cn/appstock/app/minute/query?code={code}&r=0.1\";\r\n\r\ninterface MinuteResponse {\r\n code: number;\r\n data?: Record<string, {\r\n data?: { data?: string[]; date?: string };\r\n qt?: Record<string, (string | string[])[]>;\r\n }>;\r\n}\r\n\r\n/** 确保股票代码带市场前缀(如 513090 → sh513090) */\r\nfunction normalizeApiCode(code: string): string {\r\n if (code.startsWith(\"sh\") || code.startsWith(\"sz\")) return code;\r\n if (/^60|^68|^51/.test(code)) return \"sh\" + code;\r\n if (/^00|^30|^39/.test(code)) return \"sz\" + code;\r\n return \"sh\" + code;\r\n}\r\n\r\n/**\r\n * 调用分时接口获取某只股票的全天分钟数据。\r\n * 返回 { prices: 每分钟价格数组, quote: 实时行情快照, date: 日期 }\r\n */\r\nasync function fetchStockMinute(code: string): Promise<{\r\n prices: number[];\r\n quote: StockRow | null;\r\n date: string;\r\n} | null> {\r\n const url = MINUTE_API.replace(\"{code}\", normalizeApiCode(code));\r\n try {\r\n const resp = await fetch(url);\r\n const json = (await resp.json()) as MinuteResponse;\r\n if (json.code !== 0) return null;\r\n\r\n const stockKey = normalizeApiCode(code);\r\n const stockData = json.data?.[stockKey];\r\n if (!stockData) return null;\r\n\r\n // 1. 分钟行情 — 用于折线图\r\n const rawMinutes = stockData.data?.data ?? [];\r\n const prices: number[] = [];\r\n for (const line of rawMinutes) {\r\n // 格式: \"HHMM price volume amount\"\r\n const parts = (line as string).split(\" \");\r\n if (parts.length >= 2) {\r\n const p = parseFloat(parts[1]!);\r\n if (!isNaN(p)) prices.push(p);\r\n }\r\n }\r\n\r\n // 2. 实时行情快照 — 用于列表\r\n const qtKey = stockKey;\r\n const qt = stockData.qt?.[qtKey] as string[] | undefined;\r\n let quote: StockRow | null = null;\r\n if (qt && qt.length >= 35) {\r\n quote = {\r\n code,\r\n name: qt[1] ?? \"\",\r\n price: parseFloat(qt[3] ?? \"0\"),\r\n changePercent: parseFloat(qt[32] ?? \"0\"),\r\n changeAmount: parseFloat(qt[31] ?? \"0\"),\r\n high: parseFloat(qt[33] ?? \"0\"),\r\n low: parseFloat(qt[34] ?? \"0\"),\r\n volume: parseInt(qt[6] ?? \"0\", 10),\r\n // 成交额(单位:万元)→ 转为元\r\n amount: parseFloat(qt[37] ?? \"0\") * 10_000,\r\n };\r\n }\r\n\r\n return {\r\n prices,\r\n quote,\r\n date: stockData.data?.date ?? \"\",\r\n };\r\n } catch {\r\n return null;\r\n }\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 缓存\r\n// ---------------------------------------------------------------------------\r\n\r\n/** 缓存各股票的分钟价格数据,key = 股票代码 */\r\nconst minuteCache = new Map<string, number[]>();\r\n\r\nfunction cacheMinute(code: string, prices: number[]): void {\r\n minuteCache.set(code, prices);\r\n}\r\n\r\n/** 仅用于测试环境重置缓存 */\r\nexport function _clearMinuteCache(): void {\r\n minuteCache.clear();\r\n}\r\n\r\nfunction getCachedMinutes(code: string): number[] | undefined {\r\n return minuteCache.get(code);\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 演示兜底数据\r\n// ---------------------------------------------------------------------------\r\n\r\nconst FALLBACK_STOCKS: StockRow[] = [\r\n // 上证指数成交额约 1.56 万亿\r\n { code: \"000001\", name: \"上证指数\", price: 3150.00, changePercent: 0.35, changeAmount: 11.02, high: 3160.00, low: 3140.00, volume: 285430000, amount: 1_560_000_000_000 },\r\n // 紫金矿业成交额约 108.5 亿\r\n { code: \"601899\", name: \"紫金矿业\", price: 18.20, changePercent: 1.85, changeAmount: 0.33, high: 18.45, low: 17.90, volume: 59615384, amount: 10_850_000_000 },\r\n { code: \"399006\", name: \"创业板指\", price: 1820.00, changePercent: -0.52, changeAmount: -9.50, high: 1835.00, low: 1815.00, volume: 98650000, amount: 18_240_000_000 },\r\n];\r\n\r\n// ---------------------------------------------------------------------------\r\n// fetchStocks — 加载列表数据\r\n// ---------------------------------------------------------------------------\r\n\r\nasync function fetchStocks(codes: string[]): Promise<StockRow[]> {\r\n const results = await Promise.all(\r\n codes.map(async (code) => {\r\n const data = await fetchStockMinute(code);\r\n if (data?.quote) {\r\n // 同时缓存分钟数据,进详情就不用再请求了\r\n if (data.prices.length > 0) cacheMinute(code, data.prices);\r\n return data.quote;\r\n }\r\n return null;\r\n }),\r\n );\r\n\r\n // 用真实数据替换,缺失的用兜底\r\n const real: StockRow[] = results.filter((r): r is StockRow => r !== null);\r\n if (real.length > 0) return real;\r\n\r\n // 全都没取到?用兜底数据\r\n return FALLBACK_STOCKS;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 格式化工具\r\n// ---------------------------------------------------------------------------\r\n\r\nfunction formatPrice(p: number): string {\r\n return p >= 100 ? p.toFixed(2) : p.toFixed(3);\r\n}\r\n\r\nfunction formatVolume(v: number): string {\r\n if (v >= 10000) return (v / 10000).toFixed(1) + \"万\";\r\n return v.toLocaleString();\r\n}\r\n\r\n/**\r\n * 成交额格式化(元 → 万/亿/万亿)\r\n * 1 万亿 = 1_0000_0000_0000,1 亿 = 1_0000_0000,1 万 = 1_0000\r\n */\r\nfunction formatAmount(yuan: number): string {\r\n if (yuan >= 1_000_000_000_000) return (yuan / 1_000_000_000_000).toFixed(2) + \"万亿\";\r\n if (yuan >= 100_000_000) return (yuan / 100_000_000).toFixed(2) + \"亿\";\r\n if (yuan >= 10_000) return (yuan / 10_000).toFixed(1) + \"万\";\r\n return yuan.toLocaleString();\r\n}\r\n\r\n/**\r\n * 取最新 maxPoints 个点,用于绘制折线图。\r\n * 分时数据从 09:30 累积到当前时间,取尾部最新的 60 点\r\n * 能展示最近的行情走势,同时适配终端宽度。\r\n */\r\nfunction latestPoints(data: number[], maxPoints = 60): number[] {\r\n if (data.length <= maxPoints) return data;\r\n return data.slice(data.length - maxPoints);\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// StockList 组件\r\n// ---------------------------------------------------------------------------\r\n\r\ninterface StockListProps {\r\n /** 自选股代码列表 */\r\n codes?: string[];\r\n /** 按 q 时退出 */\r\n onExit: () => void;\r\n /** 按 q 时返回上级(从 chat 内跳转时使用) */\r\n onBackToChat?: () => void;\r\n}\r\n\r\nexport function StockList({ codes, onExit, onBackToChat }: StockListProps) {\r\n const [stocks, setStocks] = useState<StockRow[]>([]);\r\n const [selectedIndex, setSelectedIndex] = useState(0);\r\n const [loading, setLoading] = useState(true);\r\n const [lastUpdate, setLastUpdate] = useState<string>(\"\");\r\n const [detailView, setDetailView] = useState<StockRow | null>(null);\r\n const [detailPrices, setDetailPrices] = useState<number[] | null>(null);\r\n const [detailLoading, setDetailLoading] = useState(false);\r\n const [detailCountdown, setDetailCountdown] = useState(10);\r\n const [countdown, setCountdown] = useState(5);\r\n const [currentTime, setCurrentTime] = useState<string>(() =>\r\n new Date().toLocaleTimeString(\"zh-CN\", { hour12: false }),\r\n );\r\n // h 键一键置灰:所有高亮色替换为 dimColor\r\n const [dimMode, setDimMode] = useState(false);\r\n\r\n const { doubleCtrlC, handleCtrlC } = useDoubleCtrlC(onExit);\r\n\r\n // 实时时钟(每秒更新)\r\n useEffect(() => {\r\n const timer = setInterval(() => {\r\n setCurrentTime(new Date().toLocaleTimeString(\"zh-CN\", { hour12: false }));\r\n }, 1000);\r\n return () => clearInterval(timer);\r\n }, []);\r\n\r\n // ---------- 数据加载 ----------\r\n const loadData = useCallback(async () => {\r\n setLoading(true);\r\n try {\r\n const data = await fetchStocks(codes ?? []);\r\n setStocks(data);\r\n setLastUpdate(new Date().toLocaleTimeString());\r\n } catch {\r\n // 保持旧数据\r\n }\r\n setLoading(false);\r\n }, [codes]);\r\n\r\n // 首次加载\r\n useEffect(() => {\r\n loadData();\r\n }, [loadData]);\r\n\r\n // 5 秒自动刷新 + 倒计时\r\n useEffect(() => {\r\n const interval = setInterval(() => {\r\n setCountdown((prev) => {\r\n if (prev <= 1) {\r\n loadData();\r\n return 5;\r\n }\r\n return prev - 1;\r\n });\r\n }, 1000);\r\n return () => clearInterval(interval);\r\n }, [loadData]);\r\n\r\n // ---------- 进入详情时加载分钟数据,每 30s 自动刷新 ----------\r\n useEffect(() => {\r\n if (!detailView) {\r\n setDetailPrices(null);\r\n setDetailLoading(false);\r\n return;\r\n }\r\n\r\n const loadDetail = () => {\r\n // 清除缓存,强制走网络\r\n minuteCache.delete(detailView.code);\r\n fetchStockMinute(detailView.code).then((data) => {\r\n if (data && data.prices.length > 0) {\r\n cacheMinute(detailView.code, data.prices);\r\n setDetailPrices(data.prices);\r\n }\r\n });\r\n };\r\n\r\n // 立即加载\r\n loadDetail();\r\n\r\n // 重置倒计时\r\n setDetailCountdown(10);\r\n\r\n // 每 10 秒刷新\r\n const timer = setInterval(loadDetail, 10000);\r\n return () => clearInterval(timer);\r\n }, [detailView]);\r\n\r\n // 详情页倒计时\r\n useEffect(() => {\r\n if (!detailView) return;\r\n const timer = setInterval(() => {\r\n setDetailCountdown((prev) => (prev > 0 ? prev - 1 : 10));\r\n }, 1000);\r\n return () => clearInterval(timer);\r\n }, [detailView]);\r\n\r\n // ---------- 键盘控制 ----------\r\n useInput(\r\n useCallback(\r\n (input, key) => {\r\n // Ctrl+C:双击退出\r\n if (input === \"c\" && key.ctrl) {\r\n handleCtrlC();\r\n return;\r\n }\r\n\r\n if (detailView) {\r\n // 详情模式下按 Esc/q 返回列表\r\n if (key.escape || input === \"q\" || input === \" \") {\r\n setDetailView(null);\r\n }\r\n return;\r\n }\r\n\r\n if (stocks.length === 0) return;\r\n\r\n if (key.upArrow || input === \"k\") {\r\n setSelectedIndex((prev) => (prev > 0 ? prev - 1 : stocks.length - 1));\r\n } else if (key.downArrow || input === \"j\") {\r\n setSelectedIndex((prev) => (prev < stocks.length - 1 ? prev + 1 : 0));\r\n } else if (key.return) {\r\n const stock = stocks[selectedIndex];\r\n if (stock) setDetailView(stock);\r\n } else if (key.escape || input === \"q\") {\r\n if (onBackToChat) onBackToChat();\r\n else onExit();\r\n } else if (input === \"r\") {\r\n setCountdown(5);\r\n loadData();\r\n } else if (input === \"h\") {\r\n setDimMode((v) => !v);\r\n }\r\n },\r\n [stocks, selectedIndex, detailView, onExit, onBackToChat, loadData, handleCtrlC, setDimMode],\r\n ),\r\n );\r\n\r\n // ---------- 详情视图 ----------\r\n if (detailView) {\r\n if (detailLoading) {\r\n return (\r\n <Box paddingLeft={1}>\r\n <Text dimColor>{\" ⟳ 加载分时数据...\"}</Text>\r\n </Box>\r\n );\r\n }\r\n return renderDetail(detailView, () => setDetailView(null), detailPrices ?? undefined, detailCountdown, currentTime);\r\n }\r\n\r\n // ---------- 列表视图 ----------\r\n // 置灰模式下,所有高亮色统一替换为 dimColor\r\n const cp = (c: string) => dimMode ? { dimColor: true } : { color: c };\r\n return (\r\n <Box flexDirection=\"column\">\r\n {/* 顶部状态栏 */}\r\n <Box marginBottom={1} justifyContent=\"space-between\">\r\n <Text bold {...cp(\"#00ffff\")}>\r\n {\" 📈 自选股监控\"}\r\n </Text>\r\n <Text dimColor>\r\n {\" 🕐 \"}{currentTime}\r\n </Text>\r\n <Text dimColor>\r\n {loading ? \" ⟳ 刷新中...\" : ` ${countdown}s 后自动刷新`}\r\n </Text>\r\n </Box>\r\n\r\n {/* 表头 */}\r\n <Box>\r\n <Box width={3} />\r\n <Box width={9}>\r\n <Text dimColor>代码</Text>\r\n </Box>\r\n <Box width={16}>\r\n <Text dimColor>名称</Text>\r\n </Box>\r\n <Box width={12}>\r\n <Text dimColor>最新价</Text>\r\n </Box>\r\n <Box width={12}>\r\n <Text dimColor>涨跌幅</Text>\r\n </Box>\r\n <Box width={12}>\r\n <Text dimColor>涨跌额</Text>\r\n </Box>\r\n <Box width={12}>\r\n <Text dimColor>最高</Text>\r\n </Box>\r\n <Box width={12}>\r\n <Text dimColor>最低</Text>\r\n </Box>\r\n <Box>\r\n <Text dimColor>成交额</Text>\r\n </Box>\r\n </Box>\r\n\r\n {/* 分隔线 */}\r\n <Box>\r\n <Text dimColor>{\" \" + \"─\".repeat(100)}</Text>\r\n </Box>\r\n\r\n {/* 股票列表 */}\r\n <Box flexDirection=\"column\">\r\n {stocks.map((stock, index) => {\r\n const isSelected = index === selectedIndex;\r\n const isUp = stock.changePercent >= 0;\r\n const color = isUp ? \"#ff1493\" : \"#00ff41\";\r\n\r\n return (\r\n <Box key={stock.code}>\r\n <Box width={3} flexShrink={0}>\r\n {isSelected ? (\r\n <Text bold {...cp(\"#00ffff\")}>\r\n {\"▸ \"}\r\n </Text>\r\n ) : (\r\n <Text>{\" \"}</Text>\r\n )}\r\n </Box>\r\n <Box width={9}>\r\n <Text bold {...cp(isSelected ? \"#00ffff\" : \"#ffffff\")}>\r\n {stock.code}\r\n </Text>\r\n </Box>\r\n <Box width={16}>\r\n <Text {...cp(isSelected ? \"#ffffff\" : \"#cccccc\")}>\r\n {stock.name}\r\n </Text>\r\n </Box>\r\n <Box width={12}>\r\n <Text bold {...cp(color)}>\r\n {formatPrice(stock.price)}\r\n </Text>\r\n </Box>\r\n <Box width={12}>\r\n <Text {...cp(color)}>\r\n {isUp ? \"+\" : \"\"}{stock.changePercent.toFixed(2)}%\r\n </Text>\r\n </Box>\r\n <Box width={12}>\r\n <Text {...cp(color)}>\r\n {isUp ? \"+\" : \"\"}{stock.changeAmount.toFixed(3)}\r\n </Text>\r\n </Box>\r\n <Box width={12}>\r\n <Text {...cp(\"#cccccc\")}>\r\n {formatPrice(stock.high)}\r\n </Text>\r\n </Box>\r\n <Box width={12}>\r\n <Text {...cp(\"#888888\")}>\r\n {formatPrice(stock.low)}\r\n </Text>\r\n </Box>\r\n <Box>\r\n <Text {...cp(\"#888888\")}>\r\n {formatAmount(stock.amount)}\r\n </Text>\r\n </Box>\r\n </Box>\r\n );\r\n })}\r\n </Box>\r\n\r\n {/* 底栏 */}\r\n <Box marginTop={1}>\r\n <Text dimColor>\r\n {` ↑/↓ 选择 Enter 详情 r 手动刷新 h 置灰/恢复 q 返回`}\r\n </Text>\r\n </Box>\r\n <Box>\r\n <Text dimColor>{` 最后更新: ${lastUpdate} 编辑自选股: `}</Text>\r\n <Text {...cp(\"#c792ea\")}>\r\n {osc8Link(toFileUrl(SETTINGS_PATH), SETTINGS_PATH)}\r\n </Text>\r\n </Box>\r\n\r\n {/* 双击 Ctrl+C 退出提示 */}\r\n {doubleCtrlC && (\r\n <Box marginTop={1}>\r\n <Text bold {...cp(\"#ff1493\")}>\r\n {\" ⚠ 再按一次 Ctrl+C 退出 dskcode\"}\r\n </Text>\r\n </Box>\r\n )}\r\n </Box>\r\n );\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 详情视图(独立的渲染函数)\r\n// ---------------------------------------------------------------------------\r\n\r\nfunction renderDetail(\r\n stock: StockRow,\r\n _onBack: () => void,\r\n prices?: number[],\r\n countdown = 10,\r\n currentTime?: string,\r\n) {\r\n const isUp = stock.changePercent >= 0;\r\n const colorCode = isUp ? \"#ff1493\" : \"#00ff41\";\r\n const arrow = isUp ? \"▲\" : \"▼\";\r\n\r\n // 折线图\r\n let chartLines: string[] = [];\r\n if (prices && prices.length > 0) {\r\n const chartColor = isUp ? asciichart.red : asciichart.green;\r\n // 取最新 60 条分时数据展示近期走势\r\n const latest = latestPoints(prices, 60);\r\n let raw = asciichart.plot(latest, {\r\n height: 10,\r\n colors: [chartColor],\r\n });\r\n // 弯角 → 直角,折线更硬朗\r\n raw = raw\r\n .replaceAll(\"╭\", \"┌\")\r\n .replaceAll(\"╮\", \"┐\")\r\n .replaceAll(\"╰\", \"└\")\r\n .replaceAll(\"╯\", \"┘\");\r\n chartLines = raw.split(\"\\n\");\r\n }\r\n\r\n return (\r\n <Box flexDirection=\"column\" paddingLeft={1}>\r\n {/* 标题行 — 左:名称代码 + 实时时间,右:刷新倒计时 */}\r\n <Box marginBottom={1} justifyContent=\"space-between\">\r\n <Box>\r\n <Text bold color=\"#00ffff\">\r\n {\" 📊 \"}{stock.name}{\" \"}\r\n </Text>\r\n <Text dimColor>\r\n {stock.code}\r\n </Text>\r\n {currentTime && (\r\n <Text dimColor>\r\n {\" 🕐 \"}{currentTime}\r\n </Text>\r\n )}\r\n </Box>\r\n <Text dimColor>\r\n {`${countdown}s 后刷新`}\r\n </Text>\r\n </Box>\r\n\r\n {/* 价格摘要 */}\r\n <Box>\r\n <Box width={16}>\r\n <Text bold color=\"#888888\">当前价</Text>\r\n </Box>\r\n <Box>\r\n <Text bold color={colorCode}>\r\n {arrow} {formatPrice(stock.price)}\r\n </Text>\r\n </Box>\r\n </Box>\r\n <Box>\r\n <Box width={16}>\r\n <Text color=\"#888888\">涨跌幅</Text>\r\n </Box>\r\n <Box>\r\n <Text color={colorCode}>\r\n {isUp ? \"+\" : \"\"}{stock.changePercent.toFixed(2)}%\r\n {\" \"}\r\n {isUp ? \"+\" : \"\"}{stock.changeAmount.toFixed(3)}\r\n </Text>\r\n </Box>\r\n </Box>\r\n\r\n {/* 折线图 */}\r\n {chartLines.length > 0 && (\r\n <Box marginTop={1} flexDirection=\"column\">\r\n {chartLines.map((line, i) => (\r\n <Box key={i}>\r\n <Text color={colorCode}>\r\n {line || \" \"}\r\n </Text>\r\n </Box>\r\n ))}\r\n </Box>\r\n )}\r\n\r\n {/* 底部 */}\r\n <Box marginTop={1}>\r\n <Text dimColor>\r\n {\" Space/q 返回列表\"}\r\n </Text>\r\n </Box>\r\n </Box>\r\n );\r\n}\r\n","// ---------------------------------------------------------------------------\r\n// 递归扫描项目源码文件(排除 node_modules 等非源码目录)\r\n// 启动时调用一次,结果缓存为扁平路径数组传给 UI\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { readdir } from \"node:fs/promises\";\r\nimport { join, relative } from \"node:path\";\r\n\r\n/** 需要跳过的目录名(任何层级) */\r\nconst IGNORE_DIRS = new Set([\r\n \"node_modules\",\r\n \".git\",\r\n \".svn\",\r\n \".hg\",\r\n \".dskcode\",\r\n \".claude\",\r\n \"dist\",\r\n \"build\",\r\n \".next\",\r\n \".turbo\",\r\n \".nx\",\r\n \"coverage\",\r\n \".cache\",\r\n \".nyc_output\",\r\n \".vscode\",\r\n \".idea\",\r\n \"__pycache__\",\r\n \".venv\",\r\n \"venv\",\r\n \".tox\",\r\n \"target\",\r\n \"vendor\",\r\n \"bower_components\",\r\n \"jspm_packages\",\r\n]);\r\n\r\n/** 只索引这些源码扩展名 */\r\nconst SOURCE_EXTS = new Set([\r\n \".ts\", \".tsx\", \".js\", \".jsx\", \".mjs\", \".cjs\",\r\n \".vue\", \".svelte\", \".astro\",\r\n \".css\", \".scss\", \".less\",\r\n \".html\",\r\n \".json\",\r\n \".md\",\r\n \".yaml\", \".yml\",\r\n \".toml\",\r\n]);\r\n\r\n/**\r\n * 递归扫描 dir 下的所有文件,返回相对于 baseDir 的路径数组。\r\n *\r\n * 优化手段:\r\n * - readdir withFileTypes 省掉 stat 调用(减少一半 syscall)\r\n * - 同层目录的 readdir 用 Promise.all 并行\r\n * - 跳过以 . 开头的隐藏目录和已知非源码目录\r\n *\r\n * 性能预期(SSD):\r\n * 1,000 文件 → ~15ms\r\n * 10,000 文件 → ~150ms\r\n * 50,000 文件 → ~800ms\r\n */\r\nexport async function scanProjectFiles(\r\n baseDir: string,\r\n dir?: string,\r\n): Promise<string[]> {\r\n const currentDir = dir ?? baseDir;\r\n\r\n let entries;\r\n try {\r\n entries = await readdir(currentDir, { withFileTypes: true });\r\n } catch {\r\n return [];\r\n }\r\n\r\n const files: string[] = [];\r\n const dirs: string[] = [];\r\n\r\n for (const entry of entries) {\r\n const name = entry.name;\r\n if (IGNORE_DIRS.has(name) || name.startsWith(\".\")) continue;\r\n\r\n if (entry.isDirectory()) {\r\n dirs.push(name);\r\n } else if (entry.isFile()) {\r\n const ext = name.slice(name.lastIndexOf(\".\")).toLowerCase();\r\n if (SOURCE_EXTS.has(ext)) {\r\n files.push(relative(baseDir, join(currentDir, name)));\r\n }\r\n }\r\n }\r\n\r\n // 同层子目录并行扫描\r\n const nested = await Promise.all(\r\n dirs.map((d) => scanProjectFiles(baseDir, join(currentDir, d))),\r\n );\r\n\r\n for (const n of nested) {\r\n files.push(...n);\r\n }\r\n\r\n return files;\r\n}\r\n","/** dskcode 退出码规范 */\r\nexport const ExitCode = {\r\n /** 正常执行完成 */\r\n SUCCESS: 0,\r\n /** 通用错误 */\r\n GENERAL_ERROR: 1,\r\n /** 配置错误 */\r\n CONFIG_ERROR: 2,\r\n /** 用户通过 Ctrl+C 中断 */\r\n SIGINT: 130,\r\n} as const;\r\n","#!/usr/bin/env node\r\n\r\nimport { createCli } from \"./cli/index.js\";\r\nimport { ExitCode } from \"./cli/exit-codes.js\";\r\n\r\n/**\r\n * 双击 Ctrl+C 退出:\r\n * 第一次 SIGINT → 提示用户再按一次\r\n * 第二次 SIGINT(1.5 秒内)→ 立即退出\r\n *\r\n * 在 ink 交互模式下,Ctrl+C 由 useInput 捕获处理,\r\n * 此处只处理 ink 未运行时的 SIGINT(如启动阶段)。\r\n */\r\nlet sigintCount = 0;\r\nlet sigintTimer: ReturnType<typeof setTimeout> | null = null;\r\n\r\n/**\r\n * 全局兜底 SIGINT 处理:双击退出。\r\n * ink 交互模式下 Ctrl+C 由 useDoubleCtrlC hook 处理,\r\n * 此处仅处理 ink 未运行时的场景(如启动阶段、异常退出后)。\r\n */\r\nprocess.on(\"SIGINT\", () => {\r\n sigintCount++;\r\n if (sigintCount >= 2) {\r\n process.exit(ExitCode.SIGINT);\r\n }\r\n process.stdout.write(\"\\n ⚠ 再按一次 Ctrl+C 退出 dskcode\\n\");\r\n if (sigintTimer) clearTimeout(sigintTimer);\r\n sigintTimer = setTimeout(() => {\r\n sigintCount = 0;\r\n }, 1500);\r\n});\r\n\r\nconst program = createCli();\r\n\r\ntry {\r\n await program.parseAsync(process.argv);\r\n} catch (err: unknown) {\r\n const error = err as { exitCode?: number; code?: string };\r\n\r\n if (error.code === \"commander.helpDisplayed\" || error.code === \"commander.version\") {\r\n process.exit(error.exitCode ?? ExitCode.SUCCESS);\r\n }\r\n\r\n if (typeof error.exitCode === \"number\") {\r\n process.exit(error.exitCode);\r\n }\r\n\r\n console.error(String(err));\r\n process.exit(ExitCode.GENERAL_ERROR);\r\n}\r\n"],"mappings":";;;;;;;;;;;;;;AAAA,SAAS,eAAe;;;ACAxB,SAAS,YAAY,aAAa;AAClC,SAAS,OAAO,UAAU,iBAAiB;AAC3C,SAAS,YAAY;AAOd,IAAM,gBAAwB;AAAA,EACnC,iBAAiB;AAAA,EACjB,WAAW;AAAA,EACX,aAAa;AAAA,EACb,eAAe;AAAA,EACf,WAAW;AAAA,IACT;AAAA,MACE,MAAM;AAAA,MACN,SAAS;AAAA,MACT,OAAO;AAAA,IACT;AAAA,EACF;AAAA,EACA,OAAO;AAAA,IACL,EAAE,MAAM,aAAa,SAAS,KAAK;AAAA,IACnC,EAAE,MAAM,cAAc,SAAS,KAAK;AAAA,IACpC,EAAE,MAAM,aAAa,SAAS,KAAK;AAAA,IACnC,EAAE,MAAM,QAAQ,SAAS,KAAK;AAAA,IAC9B,EAAE,MAAM,QAAQ,SAAS,KAAK;AAAA,IAC9B,EAAE,MAAM,QAAQ,SAAS,KAAK;AAAA,IAC9B,EAAE,MAAM,MAAM,SAAS,KAAK;AAAA,IAC5B,EAAE,MAAM,SAAS,SAAS,KAAK;AAAA,EACjC;AAAA,EACA,SAAS,CAAC;AAAA,EACV,OAAO;AAAA,IACL,SAAS;AAAA,MACP,EAAE,MAAM,WAAW;AAAA,MACnB,EAAE,MAAM,WAAW;AAAA,MACnB,EAAE,MAAM,WAAW;AAAA,IACrB;AAAA,EACF;AACF;AAuBA,SAAS,mBAAmB,YAA+B;AACzD,MAAI,YAAY;AACd,WAAO,CAAC,UAAU;AAAA,EACpB;AAEA,QAAM,OAAO,QAAQ,IAAI,QAAQ,QAAQ,IAAI,eAAe;AAC5D,SAAO;AAAA,IACL,KAAK,MAAM,YAAY,eAAe;AAAA,IACtC,KAAK,QAAQ,IAAI,GAAG,YAAY,eAAe;AAAA,EACjD;AACF;AAaA,SAAS,YAAY,MAAc,SAAkC;AACnE,QAAM,SAAiB,EAAE,GAAG,KAAK;AAEjC,MAAI,QAAQ,oBAAoB,QAAW;AACzC,WAAO,kBAAkB,QAAQ;AAAA,EACnC;AACA,MAAI,QAAQ,YAAY,QAAW;AACjC,WAAO,UAAU,QAAQ;AAAA,EAC3B;AACA,MAAI,QAAQ,cAAc,QAAW;AACnC,WAAO,YAAY,QAAQ;AAAA,EAC7B;AACA,MAAI,QAAQ,gBAAgB,QAAW;AACrC,WAAO,cAAc,QAAQ;AAAA,EAC/B;AACA,MAAI,QAAQ,kBAAkB,QAAW;AACvC,WAAO,gBAAgB,QAAQ;AAAA,EACjC;AACA,MAAI,QAAQ,gBAAgB,QAAW;AACrC,WAAO,cAAc,QAAQ;AAAA,EAC/B;AACA,MAAI,QAAQ,qBAAqB,QAAW;AAC1C,WAAO,mBAAmB,QAAQ;AAAA,EACpC;AACA,MAAI,QAAQ,cAAc,QAAW;AACnC,WAAO,YAAY,QAAQ;AAAA,EAC7B;AACA,MAAI,QAAQ,UAAU,QAAW;AAC/B,WAAO,QAAQ,QAAQ;AAAA,EACzB;AACA,MAAI,QAAQ,YAAY,QAAW;AACjC,WAAO,UAAU,QAAQ;AAAA,EAC3B;AACA,MAAI,QAAQ,UAAU,QAAW;AAC/B,WAAO,QAAQ,QAAQ;AAAA,EACzB;AAEA,SAAO;AACT;AAOA,IAAM,aAAa;AAGnB,IAAM,UAAwC;AAAA,EAC5C,CAAC,GAAG,UAAU,kBAAkB,GAAG;AAAA,EACnC,CAAC,GAAG,UAAU,SAAS,GAAG;AAAA,EAC1B,CAAC,GAAG,UAAU,YAAY,GAAG;AAAA,EAC7B,CAAC,GAAG,UAAU,aAAa,GAAG;AAAA,EAC9B,CAAC,GAAG,UAAU,iBAAiB,GAAG;AAAA,EAClC,CAAC,GAAG,UAAU,cAAc,GAAG;AAAA,EAC/B,CAAC,GAAG,UAAU,oBAAoB,GAAG;AACvC;AAOA,SAAS,aAAa,QAAwB;AAE5C,QAAM,SAAiB,EAAE,GAAG,QAAQ,WAAW,CAAC,GAAG,OAAO,SAAS,EAAE;AAGrE,aAAW,CAAC,QAAQ,SAAS,KAAK,OAAO,QAAQ,OAAO,GAAG;AACzD,UAAM,MAAM,QAAQ,IAAI,MAAM;AAC9B,QAAI,QAAQ,OAAW;AAEvB,YAAQ,WAAW;AAAA,MACjB,KAAK;AAAA,MACL,KAAK,mBAAmB;AACtB,QAAC,OAA8C,SAAS,IAAI;AAC5D;AAAA,MACF;AAAA,MACA,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK,oBAAoB;AACvB,cAAM,IAAI,OAAO,GAAG;AACpB,YAAI,OAAO,SAAS,CAAC,KAAK,KAAK,GAAG;AAChC,UAAC,OAA8C,SAAS,IAAI;AAAA,QAC9D;AACA;AAAA,MACF;AAAA,MACA,KAAK,eAAe;AAClB,cAAM,IAAI,OAAO,GAAG;AACpB,YAAI,OAAO,SAAS,CAAC,KAAK,KAAK,KAAK,KAAK,GAAG;AAC1C,UAAC,OAA8C,SAAS,IAAI;AAAA,QAC9D;AACA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAGA,QAAM,SAAS,QAAQ,IAAI;AAC3B,MAAI,QAAQ;AACV,UAAM,cAAc,OAAO,UAAU,UAAU,CAAC,MAAM,EAAE,SAAS,UAAU;AAC3E,QAAI,gBAAgB,IAAI;AACtB,YAAM,WAAW,OAAO,UAAU,WAAW;AAC7C,UAAI,CAAC,SAAS,QAAQ;AACpB,eAAO,UAAU,WAAW,IAAI,EAAE,GAAG,UAAU,OAAO;AAAA,MACxD;AAAA,IACF,OAAO;AAEL,aAAO,UAAU,QAAQ;AAAA,QACvB,MAAM;AAAA,QACN,SAAS;AAAA,QACT,OAAO;AAAA,QACP;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO;AACT;AAoBO,SAAS,kBAAkB,QAAgB,OAAyB;AACzE,QAAM,SAAiB,EAAE,GAAG,QAAQ,WAAW,CAAC,GAAG,OAAO,SAAS,EAAE;AAErE,MAAI,MAAM,YAAY,QAAW;AAC/B,WAAO,UAAU,MAAM;AAAA,EACzB;AACA,MAAI,MAAM,UAAU,QAAW;AAG7B,UAAM,cAAc,OAAO,UAAU;AAAA,MACnC,CAAC,MAAM,EAAE,SAAS,OAAO;AAAA,IAC3B;AACA,QAAI,gBAAgB,IAAI;AACtB,aAAO,UAAU,WAAW,IAAI;AAAA,QAC9B,GAAG,OAAO,UAAU,WAAW;AAAA,QAC/B,OAAO,MAAM;AAAA,MACf;AAAA,IACF;AAAA,EACF;AACA,MAAI,MAAM,cAAc,UAAa,MAAM,YAAY,GAAG;AACxD,WAAO,YAAY,MAAM;AAAA,EAC3B;AACA,MACE,MAAM,gBAAgB,UACtB,MAAM,eAAe,KACrB,MAAM,eAAe,GACrB;AACA,WAAO,cAAc,MAAM;AAAA,EAC7B;AACA,MAAI,MAAM,gBAAgB,UAAa,MAAM,eAAe,GAAG;AAC7D,WAAO,cAAc,MAAM;AAAA,EAC7B;AACA,MAAI,MAAM,qBAAqB,UAAa,MAAM,oBAAoB,GAAG;AACvE,WAAO,mBAAmB,MAAM;AAAA,EAClC;AACA,SAAO;AACT;AAeO,SAAS,eAAe,QAA+B;AAC5D,QAAM,SAAwB,CAAC;AAG/B,MAAI,CAAC,OAAO,aAAa,OAAO,UAAU,WAAW,GAAG;AACtD,WAAO,KAAK;AAAA,MACV,OAAO;AAAA,MACP,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AAGA,QAAMA,oBAAmB,CAAC,qBAAqB,iBAAiB;AAChE,WAAS,IAAI,GAAG,IAAI,OAAO,UAAU,QAAQ,KAAK;AAChD,UAAM,IAAI,OAAO,UAAU,CAAC;AAC5B,QAAI,CAAC,EAAE,MAAM;AACX,aAAO,KAAK;AAAA,QACV,OAAO,aAAa,CAAC;AAAA,QACrB,SAAS,UAAK,IAAI,CAAC;AAAA,MACrB,CAAC;AAAA,IACH;AACA,QAAI,CAAC,EAAE,OAAO;AACZ,aAAO,KAAK;AAAA,QACV,OAAO,aAAa,CAAC;AAAA,QACrB,SAAS,aAAa,EAAE,QAAQ,CAAC;AAAA,MACnC,CAAC;AAAA,IACH,WAAW,CAACA,kBAAiB,SAAS,EAAE,KAAK,GAAG;AAC9C,aAAO,KAAK;AAAA,QACV,OAAO,aAAa,CAAC;AAAA,QACrB,SAAS,aAAa,EAAE,QAAQ,CAAC,mBAAc,EAAE,KAAK,+DAAuBA,kBAAiB,KAAK,IAAI,CAAC;AAAA,MAC1G,CAAC;AAAA,IACH;AAAA,EACF;AAGA,MAAI,OAAO,iBAAiB;AAC1B,UAAM,SAAS,OAAO,UAAU;AAAA,MAC9B,CAAC,MAAM,EAAE,SAAS,OAAO;AAAA,IAC3B;AACA,QAAI,CAAC,QAAQ;AACX,aAAO,KAAK;AAAA,QACV,OAAO;AAAA,QACP,SAAS,0BAAgB,OAAO,eAAe;AAAA,MACjD,CAAC;AAAA,IACH;AAAA,EACF;AAGA,MACE,OAAO,gBAAgB,WACtB,OAAO,cAAc,KAAK,OAAO,cAAc,IAChD;AACA,WAAO,KAAK;AAAA,MACV,OAAO;AAAA,MACP,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AAGA,MAAI,OAAO,cAAc,UAAa,OAAO,YAAY,GAAG;AAC1D,WAAO,KAAK;AAAA,MACV,OAAO;AAAA,MACP,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AAGA,MAAI,OAAO,kBAAkB,UAAa,OAAO,gBAAgB,GAAG;AAClE,WAAO,KAAK;AAAA,MACV,OAAO;AAAA,MACP,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AAGA,MAAI,OAAO,gBAAgB,UAAa,OAAO,cAAc,GAAG;AAC9D,WAAO,KAAK;AAAA,MACV,OAAO;AAAA,MACP,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AAGA,MAAI,OAAO,qBAAqB,UAAa,OAAO,mBAAmB,GAAG;AACxE,WAAO,KAAK;AAAA,MACV,OAAO;AAAA,MACP,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AAEA,SAAO;AACT;AAgBA,eAAsB,WAAW,YAAsC;AACrE,QAAM,YAAY,mBAAmB,UAAU;AAE/C,MAAI,SAAiB,gBAAgB,aAAa;AAGlD,aAAW,YAAY,WAAW;AAChC,QAAI;AACF,YAAM,MAAM,MAAM,SAAS,UAAU,OAAO;AAC5C,YAAM,SAAS,KAAK,MAAM,GAAG;AAC7B,eAAS,YAAY,QAAQ,MAAM;AAAA,IACrC,QAAQ;AAAA,IAER;AAAA,EACF;AAGA,WAAS,aAAa,MAAM;AAE5B,SAAO;AACT;AAOA,eAAsB,gBACpB,YACoD;AACpD,QAAM,SAAS,MAAM,WAAW,UAAU;AAC1C,QAAM,SAAS,eAAe,MAAM;AACpC,SAAO,EAAE,QAAQ,OAAO;AAC1B;AAuEA,eAAsB,WAAW,QAAiC;AAChE,QAAM,OAAO,QAAQ,IAAI,QAAQ,QAAQ,IAAI,eAAe;AAC5D,QAAM,YAAY,KAAK,MAAM,UAAU;AACvC,QAAM,aAAa,KAAK,WAAW,eAAe;AAGlD,QAAM,MAAM,WAAW,EAAE,WAAW,KAAK,CAAC;AAG1C,MAAI;AACJ,MAAI;AACF,UAAM,MAAM,MAAM,SAAS,YAAY,OAAO;AAC9C,iBAAa,KAAK,MAAM,GAAG;AAAA,EAC7B,QAAQ;AAEN,iBAAa,gBAAgB,aAAa;AAAA,EAC5C;AAGA,QAAM,YAAa,WAAW,aAA2C,CAAC;AAC1E,QAAM,WAAW,UAAU,KAAK,CAAC,MAAM,EAAE,SAAS,UAAU;AAE5D,MAAI,UAAU;AACZ,aAAS,SAAS;AAAA,EACpB,OAAO;AACL,cAAU,KAAK;AAAA,MACb,MAAM;AAAA,MACN;AAAA,MACA,SAAS;AAAA,MACT,OAAO;AAAA,IACT,CAAC;AAAA,EACH;AAEA,aAAW,YAAY;AAGvB,QAAM,UAAU,YAAY,KAAK,UAAU,YAAY,MAAM,CAAC,GAAG,OAAO;AAExE,SAAO;AACT;AAOA,eAAsB,gBAAgB,OAAgC;AACpE,QAAM,OAAO,QAAQ,IAAI,QAAQ,QAAQ,IAAI,eAAe;AAC5D,QAAM,YAAY,KAAK,MAAM,UAAU;AACvC,QAAM,aAAa,KAAK,WAAW,eAAe;AAGlD,QAAM,MAAM,WAAW,EAAE,WAAW,KAAK,CAAC;AAG1C,MAAI;AACJ,MAAI;AACF,UAAM,MAAM,MAAM,SAAS,YAAY,OAAO;AAC9C,iBAAa,KAAK,MAAM,GAAG;AAAA,EAC7B,QAAQ;AACN,iBAAa,gBAAgB,aAAa;AAAA,EAC5C;AAGA,QAAM,YAAa,WAAW,aAA2C,CAAC;AAC1E,QAAM,sBAAuB,WAAW,mBAA8B;AACtE,QAAM,WAAW,UAAU,KAAK,CAAC,MAAM,EAAE,SAAS,mBAAmB;AAErE,MAAI,UAAU;AACZ,aAAS,QAAQ;AAAA,EACnB,OAAO;AAEL,cAAU,KAAK;AAAA,MACb,MAAM;AAAA,MACN,SAAS;AAAA,MACT;AAAA,IACF,CAAC;AAAA,EACH;AAEA,aAAW,YAAY;AAGvB,QAAM,UAAU,YAAY,KAAK,UAAU,YAAY,MAAM,CAAC,GAAG,OAAO;AAExE,SAAO;AACT;AAOA,eAAsB,gBAAgB,SAAkD;AACtF,QAAM,OAAO,QAAQ,IAAI,QAAQ,QAAQ,IAAI,eAAe;AAC5D,QAAM,YAAY,KAAK,MAAM,UAAU;AACvC,QAAM,aAAa,KAAK,WAAW,eAAe;AAGlD,QAAM,MAAM,WAAW,EAAE,WAAW,KAAK,CAAC;AAG1C,MAAI;AACJ,MAAI;AACF,UAAM,MAAM,MAAM,SAAS,YAAY,OAAO;AAC9C,iBAAa,KAAK,MAAM,GAAG;AAAA,EAC7B,QAAQ;AACN,iBAAa,gBAAgB,aAAa;AAAA,EAC5C;AAGA,aAAW,QAAQ,EAAE,QAAQ;AAG7B,QAAM,UAAU,YAAY,KAAK,UAAU,YAAY,MAAM,CAAC,GAAG,OAAO;AAExE,SAAO;AACT;;;AC/jBA,eAAsB,uBAEK;AACzB,QAAM,OAAO,KAAK,gBAAgB;AAKlC,QAAM,UAAU,KAAK,WAAW;AAGhC,MAAI;AACJ,QAAM,SAAmB,CAAC;AAC1B,MAAI;AACF,UAAM,SAAS,MAAM,gBAAgB,KAAK,MAAM;AAChD,aAAS,OAAO;AAChB,QAAI,OAAO,OAAO,SAAS,GAAG;AAC5B,iBAAW,KAAK,OAAO,QAAQ;AAC7B,eAAO,KAAK,EAAE,OAAO;AAAA,MACvB;AAAA,IACF;AAAA,EACF,QAAQ;AACN,aAAS,gBAAgB,aAAa;AAAA,EACxC;AAGA,WAAS,kBAAkB,QAAQ;AAAA,IACjC;AAAA,IACA,OAAO,KAAK;AAAA,EACd,CAAC;AAGD,MAAI,OAAO,SAAS,KAAK,SAAS;AAChC,eAAW,OAAO,QAAQ;AACxB,cAAQ,MAAM,YAAO,GAAG,EAAE;AAAA,IAC5B;AAAA,EACF;AAEA,SAAO,EAAE,QAAQ,QAAQ;AAC3B;;;ACjEA,OAAO,WAAW;AAEX,SAAS,WAAWC,UAA0B;AACnD,QAAM,QAAkB,CAAC;AAEzB,QAAM,KAAK,EAAE;AACb,QAAM,KAAK,MAAM,KAAK,eAAK,CAAC;AAC5B,QAAM,KAAK,KAAK,MAAM,KAAK,SAAS,CAAC,IAAI,MAAM,IAAI,kBAAkB,CAAC,IAAI,MAAM,MAAM,WAAW,CAAC,IAAI,MAAM,IAAI,WAAW,CAAC,EAAE;AAC9H,QAAM,KAAK,EAAE;AAEb,QAAM,aAAaA,SAAQ,QAAQ;AAAA,IACjC,CAAC,MAAM,EAAE,SAAS,YAAY,EAAE,SAAS,eAAe,EAAE,SAAS;AAAA,EACrE;AACA,MAAI,WAAW,SAAS,GAAG;AACzB,UAAM,KAAK,MAAM,KAAK,2BAAO,CAAC;AAC9B,eAAW,OAAO,YAAY;AAC5B,YAAM,QAAQ,CAAC,IAAI,OAAO,IAAI,IAAI,EAAE,OAAO,OAAO,EAAE,KAAK,IAAI;AAC7D,YAAM,KAAK,KAAK,MAAM,KAAK,MAAM,OAAO,EAAE,CAAC,CAAC,IAAI,IAAI,eAAe,EAAE,EAAE;AAAA,IACzE;AACA,UAAM,KAAK,EAAE;AAAA,EACf;AAEA,QAAM,KAAK,MAAM,KAAK,2BAAO,CAAC;AAC9B,aAAW,QAAQ,CAAC,cAAc,eAAe,GAAG;AAClD,UAAM,MAAMA,SAAQ,QAAQ;AAAA,MAC1B,CAAC,MAAM,EAAE,UAAU,KAAK,SAAS,MAAM,IAAI,WAAW;AAAA,IACxD;AACA,QAAI,KAAK;AACP,YAAM,KAAK,KAAK,MAAM,KAAK,KAAK,OAAO,EAAE,CAAC,CAAC,IAAI,IAAI,eAAe,EAAE,EAAE;AAAA,IACxE;AAAA,EACF;AACA,QAAM,KAAK,EAAE;AAEb,QAAM,OAAOA,SAAQ,SAAS,OAAO,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,WAAW,MAAM,CAAC;AACxE,MAAI,KAAK,SAAS,GAAG;AACnB,UAAM,KAAK,MAAM,KAAK,eAAK,CAAC;AAC5B,eAAW,OAAO,MAAM;AACtB,YAAM,KAAK,KAAK,MAAM,MAAM,IAAI,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC,IAAI,IAAI,YAAY,CAAC,EAAE;AAAA,IAC3E;AACA,UAAM,KAAK,EAAE;AAAA,EACf;AAEA,QAAM,KAAK,MAAM,KAAK,eAAK,CAAC;AAC5B,QAAM,KAAK,KAAK,MAAM,IAAI,8CAAW,CAAC,EAAE;AACxC,QAAM,KAAK,gBAAgB;AAC3B,QAAM,KAAK,KAAK,MAAM,IAAI,kDAAe,CAAC,EAAE;AAC5C,QAAM,KAAK,0DAA4B;AACvC,QAAM,KAAK,KAAK,MAAM,IAAI,wCAAU,CAAC,EAAE;AACvC,QAAM,KAAK,iBAAiB;AAC5B,QAAM,KAAK,KAAK,MAAM,IAAI,+CAAiB,CAAC,EAAE;AAC9C,QAAM,KAAK,sBAAsB;AACjC,QAAM,KAAK,KAAK,MAAM,IAAI,8CAAW,CAAC,EAAE;AACxC,QAAM,KAAK,iBAAiB;AAC5B,QAAM,KAAK,KAAK,MAAM,IAAI,oDAAY,CAAC,EAAE;AACzC,QAAM,KAAK,mCAAmC;AAC9C,QAAM,KAAK,EAAE;AAEb,SAAO,MAAM,KAAK,IAAI;AACxB;;;AC3DA,SAAS,uBAAuB;AAChC,OAAOC,YAAW;AAMX,SAAS,UAAU,WAA2C;AACnE,MAAI,UAAU,KAAK,CAAC,MAAM,EAAE,MAAM,EAAG,QAAO;AAC5C,MAAI,QAAQ,IAAI,iBAAkB,QAAO;AACzC,SAAO;AACT;AAMA,IAAM,qBAAqB;AAO3B,eAAsB,kBAA0C;AAC9D,UAAQ,IAAIA,OAAM,OAAO,0DAAuB,CAAC;AACjD,UAAQ,IAAIA,OAAM,IAAI,wFAAkB,CAAC;AACzC,UAAQ,IAAIA,OAAM,IAAI,mEAA4C,CAAC;AACnE,UAAQ,IAAIA,OAAM,IAAI,6DAAsC,CAAC;AAC7D,UAAQ,IAAIA,OAAM,IAAI,6GAA0B,CAAC;AAEjD,QAAM,KAAK,gBAAgB;AAAA,IACzB,OAAO,QAAQ;AAAA,IACf,QAAQ,QAAQ;AAAA,EAClB,CAAC;AAED,SAAO,IAAI,QAAuB,CAACC,aAAY;AAC7C,QAAI,WAAW;AAEf,UAAM,UAAU,MAAM;AACpB,UAAI,SAAU;AACd,iBAAW;AACX,cAAQ,MAAM,eAAe,YAAY,UAAU;AACnD,SAAG,MAAM;AAAA,IACX;AAEA,UAAM,aAAa,CAAC,GAAY,QAA2C;AACzE,UAAI,IAAI,QAAQ,IAAI,SAAS,KAAK;AAChC,gBAAQ;AACR,QAAAA,SAAQ,IAAI;AAAA,MACd;AAAA,IACF;AAEA,YAAQ,MAAM,GAAG,YAAY,UAAU;AAEvC,OAAG;AAAA,MACD,KAAKD,OAAM,KAAK,WAAI,CAAC,IAAIA,OAAM,KAAK,kDAAyB,CAAC;AAAA,MAC9D,CAAC,WAAW;AACV,gBAAQ;AACR,cAAM,UAAU,OAAO,KAAK;AAC5B,YAAI,CAAC,SAAS;AACZ,kBAAQ,IAAIA,OAAM,IAAI,2CAAkB,CAAC;AACzC,UAAAC,SAAQ,IAAI;AACZ;AAAA,QACF;AACA,YAAI,QAAQ,SAAS,oBAAoB;AACvC,kBAAQ,IAAID,OAAM,IAAI,yFAA6B,CAAC;AACpD,UAAAC,SAAQ,IAAI;AACZ;AAAA,QACF;AACA,QAAAA,SAAQ,OAAO;AAAA,MACjB;AAAA,IACF;AAAA,EACF,CAAC;AACH;;;AC1EA,SAAS,mBAAAC,wBAAuB;AAChC,SAAS,cAAAC,aAAY,UAAU,iBAAiB;AAChD,SAAS,SAAAC,QAAO,SAAS,IAAI,QAAQ,UAAU,YAAAC,iBAAgB;AAC/D,SAAS,QAAAC,aAAY;AACrB,OAAOC,YAAW;AAMlB,SAAS,qBAA6B;AACpC,QAAM,OAAO,QAAQ,IAAI,QAAQ,QAAQ,IAAI,eAAe;AAC5D,SAAOD,MAAK,MAAM,WAAW,QAAQ;AACvC;AAKA,SAAS,sBAA8B;AACrC,QAAM,OAAO,QAAQ,IAAI,QAAQ,QAAQ,IAAI,eAAe;AAC5D,SAAOA,MAAK,MAAM,YAAY,QAAQ;AACxC;AAKA,eAAe,WAAW,KAA+B;AACvD,MAAI;AACF,UAAM,OAAOA,MAAK,KAAK,UAAU,CAAC;AAClC,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAMA,eAAsB,mBAAsC;AAC1D,QAAM,YAAY,mBAAmB;AACrC,MAAI,CAACH,YAAW,SAAS,EAAG,QAAO,CAAC;AAEpC,MAAI;AACJ,MAAI;AACF,cAAU,MAAM,QAAQ,SAAS;AAAA,EACnC,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AAEA,QAAM,SAAmB,CAAC;AAC1B,aAAW,QAAQ,SAAS;AAC1B,UAAM,OAAOG,MAAK,WAAW,IAAI;AACjC,UAAME,QAAO,SAAS,IAAI;AAC1B,QAAIA,MAAK,YAAY,KAAM,MAAM,WAAW,IAAI,GAAI;AAClD,aAAO,KAAK,IAAI;AAAA,IAClB;AAAA,EACF;AACA,SAAO;AACT;AAcA,SAAS,WAAW,UAAoC;AACtD,QAAM,KAAKC,iBAAgB;AAAA,IACzB,OAAO,QAAQ;AAAA,IACf,QAAQ,QAAQ;AAAA,EAClB,CAAC;AAED,SAAO,IAAI,QAAiB,CAACC,aAAY;AACvC,QAAI,WAAW;AAEf,UAAM,UAAU,MAAM;AACpB,UAAI,SAAU;AACd,iBAAW;AACX,cAAQ,MAAM,eAAe,YAAY,UAAU;AACnD,SAAG,MAAM;AAAA,IACX;AAEA,UAAM,aAAa,CAAC,GAAY,QAA2C;AACzE,UAAI,IAAI,QAAQ,IAAI,SAAS,KAAK;AAChC,gBAAQ;AACR,QAAAA,SAAQ,KAAK;AAAA,MACf;AAAA,IACF;AAEA,YAAQ,MAAM,GAAG,YAAY,UAAU;AAEvC,OAAG,SAAS,UAAU,CAAC,WAAW;AAChC,cAAQ;AACR,YAAM,UAAU,OAAO,KAAK,EAAE,YAAY;AAE1C,MAAAA,SAAQ,YAAY,OAAO,YAAY,KAAK;AAAA,IAC9C,CAAC;AAAA,EACH,CAAC;AACH;AAOA,eAAsB,mBACpB,YACoD;AACpD,QAAM,YAAY,mBAAmB;AACrC,QAAM,aAAa,oBAAoB;AAEvC,QAAMC,OAAM,YAAY,EAAE,WAAW,KAAK,CAAC;AAE3C,QAAM,WAAqB,CAAC;AAC5B,QAAM,UAAoB,CAAC;AAE3B,aAAW,QAAQ,YAAY;AAC7B,UAAM,MAAMC,MAAK,WAAW,IAAI;AAChC,UAAM,OAAOA,MAAK,YAAY,IAAI;AAElC,QAAIC,YAAW,IAAI,GAAG;AACpB,cAAQ,KAAK,IAAI;AACjB;AAAA,IACF;AAQA,UAAM,UAAU,UAAU,GAAG,EAAE,eAAe,IAAI,MAAM,SAAS,GAAG,IAAI;AACxE,UAAM,GAAG,SAAS,MAAM,EAAE,WAAW,MAAM,aAAa,KAAK,CAAC;AAC9D,aAAS,KAAK,IAAI;AAAA,EACpB;AAEA,SAAO,EAAE,UAAU,QAAQ;AAC7B;AAKA,eAAsB,qBAAsC;AAC1D,QAAM,aAAa,oBAAoB;AACvC,MAAI,CAACA,YAAW,UAAU,EAAG,QAAO;AAEpC,MAAI;AACJ,MAAI;AACF,cAAU,MAAM,QAAQ,UAAU;AAAA,EACpC,QAAQ;AACN,WAAO;AAAA,EACT;AAEA,MAAI,QAAQ;AACZ,aAAW,QAAQ,SAAS;AAC1B,UAAM,OAAOD,MAAK,YAAY,IAAI;AAClC,QAAI,SAAS,IAAI,EAAE,YAAY,KAAM,MAAM,WAAW,IAAI,GAAI;AAC5D;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAKO,SAAS,mBAAmB,KAAqB;AACtD,SAAOA,MAAK,KAAK,YAAY,OAAO;AACtC;AAKA,eAAsB,sBAAsB,KAA+B;AACzE,QAAM,QAAQ,MAAM,wBAAwB,GAAG;AAC/C,SAAO,QAAQ;AACjB;AAKA,eAAsB,wBAAwB,KAA8B;AAC1E,QAAM,WAAW,mBAAmB,GAAG;AACvC,MAAI,CAACC,YAAW,QAAQ,EAAG,QAAO;AAElC,MAAI;AACJ,MAAI;AACF,cAAU,MAAM,QAAQ,QAAQ;AAAA,EAClC,QAAQ;AACN,WAAO;AAAA,EACT;AAEA,MAAI,QAAQ;AACZ,aAAW,QAAQ,SAAS;AAC1B,UAAM,OAAOD,MAAK,UAAU,IAAI;AAChC,UAAME,QAAO,SAAS,IAAI;AAC1B,QAAIA,MAAK,YAAY,KAAM,MAAM,WAAW,IAAI,GAAI;AAClD;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAKA,eAAsB,mBAAqC;AACzD,QAAM,aAAa,oBAAoB;AACvC,MAAI,CAACD,YAAW,UAAU,EAAG,QAAO;AAEpC,MAAI;AACJ,MAAI;AACF,cAAU,MAAM,QAAQ,UAAU;AAAA,EACpC,QAAQ;AACN,WAAO;AAAA,EACT;AAEA,aAAW,QAAQ,SAAS;AAC1B,UAAM,OAAOD,MAAK,YAAY,IAAI;AAClC,UAAME,QAAO,SAAS,IAAI;AAC1B,QAAIA,MAAK,YAAY,KAAM,MAAM,WAAW,IAAI,GAAI;AAClD,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AAWA,eAAsB,yBAAyB,KAA6B;AAE1E,MAAI,KAAK;AACP,UAAM,WAAW,MAAM,sBAAsB,GAAG;AAChD,QAAI,UAAU;AACZ,cAAQ,IAAIC,OAAM,IAAI,+GAAmD,CAAC;AAC1E;AAAA,IACF;AAAA,EACF;AAGA,QAAM,YAAY,MAAM,iBAAiB;AACzC,MAAI,WAAW;AACb;AAAA,EACF;AAEA,QAAM,SAAS,MAAM,iBAAiB;AACtC,MAAI,OAAO,WAAW,EAAG;AAEzB,UAAQ,IAAIA,OAAM,KAAK;AAAA,+EAAgC,OAAO,OAAO,MAAM,CAAC,qBAAW,CAAC;AAExF,QAAM,UAAU,OAAO,MAAM,GAAG,CAAC,EAAE,IAAI,CAAC,MAAMA,OAAM,IAAI,YAAS,CAAC,EAAE,CAAC,EAAE,KAAK,IAAI;AAChF,UAAQ,IAAI,OAAO;AACnB,MAAI,OAAO,SAAS,GAAG;AACrB,YAAQ,IAAIA,OAAM,IAAI,sBAAc,OAAO,OAAO,MAAM,CAAC,SAAI,CAAC;AAAA,EAChE;AACA,UAAQ,IAAIA,OAAM,IAAI,yBAAU,mBAAmB,CAAC,EAAE,CAAC;AACvD,UAAQ,IAAIA,OAAM,IAAI,+BAAW,oBAAoB,CAAC;AAAA,CAAI,CAAC;AAE3D,QAAM,YAAY,MAAM;AAAA,IACtB,KAAKA,OAAM,KAAK,WAAI,CAAC,yEAA4BA,OAAM,IAAI,QAAQ,CAAC;AAAA,EACtE;AAEA,MAAI,CAAC,WAAW;AACd,YAAQ,IAAIA,OAAM,IAAI,2CAAkB,CAAC;AACzC;AAAA,EACF;AAEA,MAAI;AACF,UAAM,EAAE,UAAU,QAAQ,IAAI,MAAM,mBAAmB,MAAM;AAC7D,QAAI,SAAS,SAAS,GAAG;AACvB,cAAQ;AAAA,QACNA,OAAM,MAAM,+BAAW,OAAO,SAAS,MAAM,CAAC,wCAA8B;AAAA,MAC9E;AAAA,IACF;AACA,QAAI,QAAQ,SAAS,GAAG;AACtB,cAAQ;AAAA,QACNA,OAAM,OAAO,YAAO,OAAO,QAAQ,MAAM,CAAC,iEAAoB,QAAQ,KAAK,IAAI,CAAC,EAAE;AAAA,MACpF;AAAA,IACF;AACA,YAAQ,IAAI,EAAE;AAAA,EAChB,SAAS,KAAK;AACZ,YAAQ;AAAA,MACNA,OAAM,IAAI,6CAAoB,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC,EAAE;AAAA,IAClF;AACA,YAAQ,IAAIA,OAAM,IAAI,sGAAoD,CAAC;AAAA,EAC7E;AACF;AAWA,eAAsB,oBAAuC;AAC3D,QAAM,aAAa,oBAAoB;AACvC,MAAI,CAACF,YAAW,UAAU,EAAG,QAAO,CAAC;AAErC,MAAI;AACJ,MAAI;AACF,cAAU,MAAM,QAAQ,UAAU;AAAA,EACpC,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AAEA,QAAM,SAAmB,CAAC;AAC1B,aAAW,QAAQ,SAAS;AAC1B,UAAM,OAAOD,MAAK,YAAY,IAAI;AAClC,QAAI,SAAS,IAAI,EAAE,YAAY,KAAM,MAAM,WAAW,IAAI,GAAI;AAC5D,aAAO,KAAK,IAAI;AAAA,IAClB;AAAA,EACF;AACA,SAAO;AACT;AAKA,eAAsB,kBAAkB,KAAgC;AACtE,QAAM,WAAW,mBAAmB,GAAG;AACvC,MAAI,CAACC,YAAW,QAAQ,EAAG,QAAO,CAAC;AAEnC,MAAI;AACJ,MAAI;AACF,cAAU,MAAM,QAAQ,QAAQ;AAAA,EAClC,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AAEA,QAAM,SAAmB,CAAC;AAC1B,aAAW,QAAQ,SAAS;AAC1B,UAAM,OAAOD,MAAK,UAAU,IAAI;AAChC,QAAI,SAAS,IAAI,EAAE,YAAY,KAAM,MAAM,WAAW,IAAI,GAAI;AAC5D,aAAO,KAAK,IAAI;AAAA,IAClB;AAAA,EACF;AACA,SAAO;AACT;AAMA,eAAe,cAAc,UAA6C;AACxE,MAAI;AACF,UAAM,UAAU,MAAMI,UAASJ,MAAK,UAAU,UAAU,GAAG,OAAO;AAClE,UAAM,QAAQ,QAAQ,MAAM,uBAAuB;AACnD,QAAI,CAAC,MAAO,QAAO;AAEnB,UAAM,cAAc,MAAM,CAAC;AAC3B,UAAM,YAAY,YAAY,MAAM,iBAAiB;AACrD,UAAM,YAAY,YAAY,MAAM,wBAAwB;AAE5D,WAAO;AAAA,MACL,MAAM,YAAY,CAAC,GAAG,KAAK,KAAK;AAAA,MAChC,aAAa,YAAY,CAAC,GAAG,KAAK,KAAK;AAAA,IACzC;AAAA,EACF,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAMA,eAAsB,aAAa,KAAmC;AACpE,QAAM,CAAC,aAAa,UAAU,IAAI,MAAM,QAAQ,IAAI;AAAA,IAClD,kBAAkB;AAAA,IAClB,kBAAkB,GAAG;AAAA,EACvB,CAAC;AAGD,QAAM,OAAO,oBAAI,IAAY;AAC7B,QAAM,WAAqB,CAAC;AAC5B,aAAW,QAAQ,CAAC,GAAG,YAAY,GAAG,WAAW,GAAG;AAClD,QAAI,CAAC,KAAK,IAAI,IAAI,GAAG;AACnB,WAAK,IAAI,IAAI;AACb,eAAS,KAAK,IAAI;AAAA,IACpB;AAAA,EACF;AAEA,QAAM,aAAa,oBAAoB;AACvC,QAAM,aAAa,mBAAmB,GAAG;AAEzC,QAAM,UAAuB,CAAC;AAC9B,aAAW,QAAQ,UAAU;AAE3B,UAAM,YAAYA,MAAK,YAAY,IAAI;AACvC,QAAI,OAAyB;AAC7B,QAAIC,YAAW,SAAS,GAAG;AACzB,aAAO,MAAM,cAAc,SAAS;AAAA,IACtC;AACA,QAAI,CAAC,MAAM;AACT,YAAM,aAAaD,MAAK,YAAY,IAAI;AACxC,UAAIC,YAAW,UAAU,GAAG;AAC1B,eAAO,MAAM,cAAc,UAAU;AAAA,MACvC;AAAA,IACF;AACA,YAAQ,KAAK,QAAQ,EAAE,MAAM,aAAa,GAAG,CAAC;AAAA,EAChD;AAEA,SAAO;AACT;;;AL7ZA,SAAS,YAAAI,kBAAgB;AACzB,SAAS,QAAAC,aAAY;;;AMRrB,SAAS,cAAc;AAShB,SAAS,UAAU,MAAoC;AAE5D,QAAM,EAAE,eAAe,OAAO,QAAQ,IAAI,OAAO,MAAM,EAAE,aAAa,MAAM,CAAC;AAC7E,SAAO,EAAE,eAAe,cAAc,GAAG,OAAO,QAAQ;AAC1D;;;ACbA,SAAS,YAAY;AACrB,OAAO,gBAAgB;AAWf,cAEO,YAFP;AAJD,SAAS,QAAQ,EAAE,OAAO,QAAQ,MAAM,GAAiB;AAC9D,SACE,qBAAC,QACC;AAAA,wBAAC,QAAK,OAAM,QACV,8BAAC,cAAW,MAAY,GAC1B;AAAA,IACC,QAAQ,qBAAC,QAAK;AAAA;AAAA,MAAE;AAAA,OAAM,IAAU;AAAA,KACnC;AAEJ;;;ACjBA,SAAS,KAAK,QAAAC,aAAY;AAqBpB,iBAAAC,aAAA;;;ACrBN,SAAS,OAAAC,MAAK,QAAAC,aAAY;AAC1B,SAAS,WAAW,gBAAgB;AA+BxB,gBAAAC,YAAA;AA5BL,IAAM,gBAAgB,CAAC,WAAW,WAAW,WAAW,WAAW,SAAS;AAG5E,IAAM,aAAa;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;;;ACVA,SAAS,OAAAC,MAAK,QAAAC,OAAM,UAAU,cAAc;AAC5C,OAAO,eAAe;AACtB,SAAS,aAAAC,YAAW,YAAAC,WAAU,eAAAC,cAAa,UAAAC,eAAc;;;ACNzD,SAAS,YAAAC,WAAU,aAAa,aAAAC,YAAW,cAAc;AAEzD,IAAM,oBAAoB;AAWnB,SAAS,eAAe,QAAoB;AACjD,QAAM,CAAC,aAAa,cAAc,IAAID,UAAS,KAAK;AACpD,QAAM,WAAW,OAA6C,IAAI;AAClE,QAAM,YAAY,OAAO,MAAM;AAC/B,YAAU,UAAU;AAGpB,EAAAC,WAAU,MAAM;AACd,WAAO,MAAM;AACX,UAAI,SAAS,QAAS,cAAa,SAAS,OAAO;AAAA,IACrD;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,QAAM,cAAc,YAAY,MAAM;AACpC,QAAI,aAAa;AAEf,gBAAU,QAAQ;AAClB;AAAA,IACF;AAGA,mBAAe,IAAI;AACnB,QAAI,SAAS,QAAS,cAAa,SAAS,OAAO;AACnD,aAAS,UAAU,WAAW,MAAM;AAClC,qBAAe,KAAK;AACpB,eAAS,UAAU;AAAA,IACrB,GAAG,iBAAiB;AAAA,EACtB,GAAG,CAAC,WAAW,CAAC;AAEhB,SAAO,EAAE,aAAa,YAAY;AACpC;;;ACvCA,SAAS,OAAAC,MAAK,QAAAC,aAAY;;;ACA1B,SAAS,SAAAC,QAAO,YAAAC,WAAU,aAAAC,kBAAiB;AAC3C,SAAS,QAAAC,aAAY;AAiHd,IAAM,cAAN,MAAkB;AAAA,EACd;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGT;AAAA,EACA;AAAA,EACS,kBAAgC,CAAC;AAAA;AAAA,EAG1C;AAAA,EACA;AAAA;AAAA,EAGA,SAAS;AAAA,EACT,mBAAmB;AAAA,EAEnB,YAAY,UAA8B,CAAC,GAAG;AAC5C,UAAM,OAAO,QAAQ,IAAI,QAAQ,QAAQ,IAAI,eAAe;AAC5D,SAAK,WAAW,QAAQ,WAAWC,MAAK,MAAM,YAAY,OAAO;AACjE,SAAK,eAAe,QAAQ,eAAe;AAC3C,SAAK,oBAAoB,QAAQ,oBAAoB;AACrD,SAAK,oBAAoB,QAAQ;AAGjC,SAAK,aAAa,kBAAkB;AACpC,SAAK,qBAAoB,oBAAI,KAAK,GAAE,YAAY;AAGhD,UAAM,QAAQ,YAAY;AAC1B,SAAK,aAAa;AAClB,SAAK,gBAAgB,wBAAwB,KAAK;AAAA,EACpD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,OAAO,OAAkB,OAA0B;AACjD,UAAM,OAAO,cAAc,OAAO,KAAK;AACvC,UAAM,SAAqB;AAAA,MACzB,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,MAClC;AAAA,MACA,OAAO,EAAE,GAAG,MAAM;AAAA,MAClB,MAAM,EAAE,GAAG,KAAK;AAAA,IAClB;AAGA,SAAK,gBAAgB,KAAK,MAAM;AAGhC,SAAK,mBAAmB;AACxB,SAAK,YAAY,MAAM;AAGvB,SAAK,SAAS;AAGd,SAAK,aAAa;AAElB,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,IAAI,YAAoB;AACtB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,IAAI,UAAiC;AACnC,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,IAAI,iBAAqC;AACvC,QAAI,oBAAoB;AACxB,QAAI,wBAAwB;AAC5B,QAAI,oBAAoB;AACxB,QAAI,YAAY;AAEhB,eAAW,KAAK,KAAK,iBAAiB;AACpC,2BAAqB,EAAE,MAAM;AAC7B,+BAAyB,EAAE,MAAM;AACjC,2BAAqB,EAAE,MAAM,sBAAsB;AACnD,mBAAa,EAAE,KAAK;AAAA,IACtB;AAEA,WAAO;AAAA,MACL,WAAW,KAAK;AAAA,MAChB,WAAW,KAAK;AAAA,MAChB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,SAAS,CAAC,GAAG,KAAK,eAAe;AAAA,IACnC;AAAA,EACF;AAAA;AAAA,EAGA,IAAI,mBAA2B;AAC7B,WAAO,KAAK,gBAAgB,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,KAAK,WAAW,CAAC;AAAA,EAC1E;AAAA;AAAA,EAGA,IAAI,sBAA8B;AAChC,WAAO,KAAK,gBAAgB,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,MAAM,cAAc,CAAC;AAAA,EAC9E;AAAA;AAAA,EAGA,IAAI,0BAAkC;AACpC,WAAO,KAAK,gBAAgB,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAAA,EAClF;AAAA;AAAA,EAGA,IAAI,mBAA2B;AAC7B,WAAO,KAAK,gBAAgB;AAAA,EAC9B;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,IAAI,eAAiC;AACnC,SAAK,mBAAmB;AACxB,WAAO,EAAE,GAAG,KAAK,cAAc;AAAA,EACjC;AAAA;AAAA,EAGA,IAAI,iBAAyB;AAC3B,SAAK,mBAAmB;AACxB,WAAO,KAAK,cAAc;AAAA,EAC5B;AAAA;AAAA,EAGA,IAAI,iBAAyB;AAC3B,SAAK,mBAAmB;AACxB,WAAO,KAAK,cAAc;AAAA,EAC5B;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,IAAI,mBAA4B;AAC9B,QAAI,KAAK,eAAe,KAAK,KAAK,kBAAkB,KAAK,cAAc;AACrE,aAAO;AAAA,IACT;AACA,QAAI,KAAK,oBAAoB,GAAG;AAC9B,YAAM,cACJ,KAAK,cAAc,oBACnB,KAAK,cAAc;AACrB,UAAI,eAAe,KAAK,kBAAmB,QAAO;AAAA,IACpD;AACA,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,IAAI,kBAA0B;AAC5B,QAAI,KAAK,gBAAgB,EAAG,QAAO;AACnC,WAAO,KAAK,IAAI,GAAG,KAAK,eAAe,KAAK,cAAc;AAAA,EAC5D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAM,OAAsB;AAC1B,UAAM,QAAQ,MAAM,KAAK,WAAW;AACpC,UAAM,QAAQ,YAAY;AAE1B,QAAI,MAAM,MAAM,KAAK,GAAG;AACtB,WAAK,gBAAgB,MAAM,MAAM,KAAK;AACtC,WAAK,aAAa;AAAA,IACpB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,QAAuB;AAC3B,QAAI,CAAC,KAAK,UAAU,KAAK,iBAAkB;AAC3C,SAAK,mBAAmB;AACxB,SAAK,SAAS;AAEd,QAAI;AACF,YAAM,KAAK,WAAW;AAAA,IACxB,UAAE;AACA,WAAK,mBAAmB;AAAA,IAC1B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,WACJ,WACA,SAC6B;AAC7B,UAAM,QAAQ,MAAM,KAAK,WAAW;AACpC,UAAM,MAAM,WAAW;AACvB,UAAM,SAA6B,CAAC;AAEpC,eAAW,CAAC,MAAM,OAAO,KAAK,OAAO,QAAQ,MAAM,KAAK,GAAG;AACzD,UAAI,QAAQ,aAAa,QAAQ,KAAK;AACpC,eAAO,KAAK,OAAO;AAAA,MACrB;AAAA,IACF;AAEA,WAAO,KAAK,CAAC,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,IAAI,CAAC;AAClD,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,eAAqB;AACnB,SAAK,aAAa,kBAAkB;AACpC,SAAK,qBAAoB,oBAAI,KAAK,GAAE,YAAY;AAChD,SAAK,gBAAgB,SAAS;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,qBAA2B;AACzB,UAAM,QAAQ,YAAY;AAC1B,QAAI,UAAU,KAAK,YAAY;AAE7B,WAAK,aAAa;AAClB,WAAK,gBAAgB,wBAAwB,KAAK;AAClD,WAAK,SAAS;AAAA,IAChB;AAAA,EACF;AAAA;AAAA,EAGA,YAAY,QAA0B;AACpC,SAAK,cAAc,qBAAqB,OAAO,MAAM;AACrD,SAAK,cAAc,yBAAyB,OAAO,MAAM;AACzD,SAAK,cAAc,qBAAqB,OAAO,MAAM,sBAAsB;AAC3E,SAAK,cAAc,aAAa,OAAO,KAAK;AAC5C,SAAK,cAAc,cAAc;AAGjC,UAAM,WAAW,OAAO;AACxB,QAAI,CAAC,KAAK,cAAc,QAAQ,QAAQ,GAAG;AACzC,WAAK,cAAc,QAAQ,QAAQ,IAAI;AAAA,QACrC,OAAO,OAAO;AAAA,QACd,mBAAmB;AAAA,QACnB,uBAAuB;AAAA,QACvB,mBAAmB;AAAA,QACnB,WAAW;AAAA,QACX,YAAY;AAAA,MACd;AAAA,IACF;AAEA,UAAM,eAAe,KAAK,cAAc,QAAQ,QAAQ;AACxD,iBAAa,qBAAqB,OAAO,MAAM;AAC/C,iBAAa,yBAAyB,OAAO,MAAM;AACnD,iBAAa,qBAAqB,OAAO,MAAM,sBAAsB;AACrE,iBAAa,aAAa,OAAO,KAAK;AACtC,iBAAa,cAAc;AAAA,EAC7B;AAAA;AAAA,EAGA,eAAqB;AACnB,QAAI,CAAC,KAAK,iBAAkB;AAC5B,SAAK,oBAAoB,IAAI;AAAA,EAC/B;AAAA;AAAA,EAGA,MAAM,aAAiC;AACrC,UAAM,WAAWA,MAAK,KAAK,UAAU,cAAc;AACnD,QAAI;AACF,YAAM,MAAM,MAAMC,UAAS,UAAU,OAAO;AAC5C,aAAO,KAAK,MAAM,GAAG;AAAA,IACvB,QAAQ;AACN,aAAO,EAAE,SAAS,GAAG,OAAO,CAAC,EAAE;AAAA,IACjC;AAAA,EACF;AAAA;AAAA,EAGA,MAAM,aAA4B;AAChC,UAAM,QAAQ,MAAM,KAAK,WAAW;AAGpC,UAAM,MAAM,KAAK,UAAU,IAAI,KAAK;AAGpC,UAAM,aAAa,gBAAgB,EAAE;AACrC,UAAM,gBAAgB,OAAO,KAAK,MAAM,KAAK,EAAE;AAAA,MAC7C,CAAC,SAAS,OAAO;AAAA,IACnB;AACA,eAAW,QAAQ,eAAe;AAEhC,aAAO,MAAM,MAAM,IAAI;AAAA,IACzB;AAGA,UAAMC,OAAM,KAAK,UAAU,EAAE,WAAW,KAAK,CAAC;AAE9C,UAAM,WAAWF,MAAK,KAAK,UAAU,cAAc;AACnD,UAAMG,WAAU,UAAU,KAAK,UAAU,OAAO,MAAM,CAAC,GAAG,OAAO;AAAA,EACnE;AACF;AAOA,SAAS,oBAA4B;AACnC,QAAM,KAAK,KAAK,IAAI,EAAE,SAAS,EAAE;AACjC,QAAM,OAAO,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,GAAG,CAAC;AAClD,SAAO,GAAG,EAAE,IAAI,IAAI;AACtB;AAGA,SAAS,cAAsB;AAC7B,UAAO,oBAAI,KAAK,GAAE,YAAY,EAAE,MAAM,GAAG,EAAE;AAC7C;AAGA,SAAS,gBAAgB,GAAmB;AAC1C,QAAM,IAAI,oBAAI,KAAK;AACnB,IAAE,QAAQ,EAAE,QAAQ,IAAI,CAAC;AACzB,SAAO,EAAE,YAAY,EAAE,MAAM,GAAG,EAAE;AACpC;AAGA,SAAS,wBAAwB,MAAgC;AAC/D,SAAO;AAAA,IACL;AAAA,IACA,mBAAmB;AAAA,IACnB,uBAAuB;AAAA,IACvB,mBAAmB;AAAA,IACnB,WAAW;AAAA,IACX,YAAY;AAAA,IACZ,SAAS,CAAC;AAAA,EACZ;AACF;AAOO,SAAS,YAAY,MAAsB;AAChD,SAAO,OAAI,KAAK,QAAQ,CAAC,CAAC;AAC5B;;;ACjeA,SAAS,OAAAC,MAAK,QAAAC,aAAY;AA2ClB,SAIA,OAAAC,MAJA,QAAAC,aAAA;AA9BR,SAAS,kBAAkB,MAAsB;AAC/C,MAAI;AACF,UAAM,SAAS,KAAK,MAAM,IAAI;AAC9B,UAAM,QAAQ,OAAO,QAAQ,MAAM,EAAE,IAAI,CAAC,CAAC,KAAK,KAAK,MAAM;AACzD,YAAM,MAAM,OAAO,KAAK;AACxB,YAAM,YAAY,IAAI,SAAS,KAAK,IAAI,MAAM,GAAG,EAAE,IAAI,QAAQ;AAC/D,aAAO,QAAQ,GAAG,KAAK,SAAS;AAAA,IAClC,CAAC;AACD,WAAO,MAAM,KAAK,IAAI;AAAA,EACxB,QAAQ;AAEN,UAAM,YAAY,KAAK,SAAS,MAAM,KAAK,MAAM,GAAG,GAAG,IAAI,QAAQ;AACnE,WAAO,QAAQ,SAAS;AAAA,EAC1B;AACF;AAUO,SAAS,cAAc,EAAE,MAAM,kBAAkB,KAAK,GAAuB;AAClF,QAAM,cAAc,kBAAkB,KAAK,SAAS;AAEpD,SACE,gBAAAA,MAACH,MAAA,EAAI,eAAc,UAAS,YAAY,GAAG,WAAW,GACpD;AAAA,oBAAAG,MAACH,MAAA,EACC;AAAA,sBAAAG,MAACF,OAAA,EAAK,OAAM,WAAU,MAAI,MAAC;AAAA;AAAA,QAAI,KAAK;AAAA,SAAK;AAAA,MACzC,gBAAAE,MAACF,OAAA,EAAK,OAAM,WAAU;AAAA;AAAA,QAAE,SAAI,OAAO,KAAK,IAAI,GAAG,KAAK,KAAK,KAAK,MAAM,CAAC;AAAA,SAAE;AAAA,OACzE;AAAA,IACA,gBAAAC,KAACF,MAAA,EAAI,eAAc,UACjB,0BAAAE,KAACD,OAAA,EAAK,OAAM,WAAW,uBAAY,GACrC;AAAA,IACC,mBACC,gBAAAC,KAACF,MAAA,EACC,0BAAAE,KAACD,OAAA,EAAK,OAAM,UAAS,6CAAM,GAC7B;AAAA,KAEJ;AAEJ;;;ACpCA,SAAS,sBAAsB,KAA0B;AACvD,MAAI,OAAO,IAAI;AAEf,MAAI,IAAI,WAAW;AACjB,eAAW,MAAM,IAAI,WAAW;AAC9B,cAAQ,GAAG,OAAO,GAAG;AAAA,IACvB;AAAA,EACF;AACA,SAAO,eAAe,IAAI,IAAI;AAChC;AAcA,SAAS,eAAe,UAA0C;AAChE,QAAM,QAAyB,CAAC;AAChC,MAAI,UAAgC;AAEpC,aAAW,OAAO,UAAU;AAC1B,QAAI,IAAI,SAAS,QAAQ;AAEvB,UAAI,QAAS,OAAM,KAAK,OAAO;AAC/B,gBAAU,CAAC,GAAG;AAAA,IAChB,OAAO;AAGL,UAAI,CAAC,QAAS,WAAU,CAAC;AACzB,cAAQ,KAAK,GAAG;AAAA,IAClB;AAAA,EACF;AACA,MAAI,WAAW,QAAQ,SAAS,EAAG,OAAM,KAAK,OAAO;AACrD,SAAO;AACT;AAKA,SAAS,mBAAmB,MAA6B;AACvD,MAAI,MAAM;AACV,aAAW,OAAO,KAAM,QAAO,sBAAsB,GAAG;AACxD,SAAO;AACT;AAmBO,SAAS,aACd,UACA,MAC0B;AAC1B,QAAM,OAAO,aAAa,KAAK,KAAK;AACpC,QAAM,iBAAiB,KAAK,gBAAgB,KAAK;AAGjD,QAAM,eAAe,eAAe,KAAK,YAAY;AACrD,MAAI,YAAY,iBAAiB;AAEjC,QAAM,QAAQ,eAAe,QAAQ;AAGrC,QAAM,iBAAkC,CAAC;AACzC,MAAI,kBAAkB;AACtB,WAAS,IAAI,MAAM,SAAS,GAAG,KAAK,KAAK,kBAAkB,KAAK,sBAAsB,KAAK;AACzF,mBAAe,QAAQ,MAAM,CAAC,CAAE;AAChC;AAAA,EACF;AAEA,QAAM,YAA2B,eAAe,KAAK;AACrD,aAAW,OAAO,WAAW;AAC3B,iBAAa,sBAAsB,GAAG;AAAA,EACxC;AAGA,MAAI,YAAY,GAAG;AACjB,WAAO,eAAe,SAAS,KAAK,YAAY,GAAG;AACjD,YAAM,UAAU,eAAe,MAAM;AACrC,mBAAa,mBAAmB,OAAO;AAAA,IACzC;AACA,WAAO,CAAC,eAAe,KAAK,GAAG,IAAI;AAAA,EACrC;AAGA,QAAM,aAAa,MAAM,MAAM,GAAG,MAAM,SAAS,eAAe,MAAM;AACtE,QAAM,YAA6B,CAAC;AAEpC,WAAS,IAAI,WAAW,SAAS,GAAG,KAAK,GAAG,KAAK;AAC/C,UAAM,OAAO,mBAAmB,WAAW,CAAC,CAAE;AAC9C,QAAI,YAAY,OAAO,EAAG;AAC1B,iBAAa;AACb,cAAU,QAAQ,WAAW,CAAC,CAAE;AAAA,EAClC;AAEA,QAAM,SAAS,CAAC,GAAG,WAAW,GAAG,cAAc,EAAE,KAAK;AACtD,QAAM,UAAU,OAAO,SAAS,SAAS;AACzC,SAAO,CAAC,QAAQ,OAAO;AACzB;AAOO,SAAS,iBACd,cACA,SACe;AACf,SAAO;AAAA,IACL,EAAE,MAAM,UAAU,SAAS,aAAa;AAAA,IACxC,GAAG;AAAA,EACL;AACF;AAKO,SAAS,mBAAmB,OAA0B;AAC3D,QAAM,SAAS,MAAM,aAAa,eAAe;AACjD,QAAM,aAAa,MAAM,iBAAiB,eAAe;AACzD,QAAM,SAAS,MAAM,eAAe,MAAM,kBAAkB,eAAe;AAE3E,MAAI,UAAU,aAAM,MAAM,gBAAS,UAAU,gBAAS,KAAK;AAE3D,MAAI,MAAM,sBAAsB,MAAM,qBAAqB,GAAG;AAC5D,UAAM,aAAc,MAAM,qBAAqB,MAAM,eAAgB,KAAK,QAAQ,CAAC;AACnF,eAAW,oDAAe,SAAS;AAAA,EACrC;AAEA,SAAO;AACT;;;AH/GU,gBAAAG,MAEF,QAAAC,aAFE;AApCV,SAAS,cAAc,IAAoB;AACzC,MAAI,KAAK,IAAM,QAAO,GAAG,EAAE;AAC3B,QAAM,WAAW,KAAK,KAAM,QAAQ,CAAC;AACrC,SAAO,GAAG,OAAO;AACnB;AAaO,SAAS,iBAAiB;AAAA,EAC/B;AAAA,EACA;AAAA,EACA,cAAc;AAAA,EACd;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAA0B;AAExB,MAAI,CAAC,YAAY,CAAC,aAAa,UAAU,WAAW,MAAM,CAAC,aAAa;AACtE,WAAO;AAAA,EACT;AAEA,SACE,gBAAAA,MAACC,MAAA,EAAI,eAAc,UAAS,WAAW,GAErC;AAAA,oBAAAD,MAACC,MAAA,EAAI,eAAc,OACjB;AAAA,sBAAAF,KAACE,MAAA,EAAI,OAAO,GAAG,YAAY,GACzB,0BAAAF,KAACG,OAAA,EAAK,MAAI,MAAC,OAAM,WAAW,uBAAK,GACnC;AAAA,MACA,gBAAAF,MAACC,MAAA,EAAI,UAAU,GAAG,eAAc,UAE7B;AAAA,mBACC,gBAAAF,KAACG,OAAA,EAAK,MAAK,QAAQ,mBAAQ;AAAA,QAG5B,eAAe,CAAC,WACf,gBAAAH,KAACG,OAAA,EAAK,OAAM,WAAU,iBAAG;AAAA,SAE7B;AAAA,OACF;AAAA,IAGC,aAAa,UAAU,SAAS,KAC/B,gBAAAH,KAACE,MAAA,EAAI,eAAc,UAChB,oBAAU,IAAI,CAAC,IAAI,MAClB,gBAAAF,KAAC,iBAA+B,MAAM,MAAlB,GAAG,MAAM,CAAa,CAC3C,GACH;AAAA,IAID,CAAC,gBAAgB,SAAS,YAAY,WACrC,gBAAAC,MAACC,MAAA,EAAI,eAAc,UAAS,WAAW,GAAG,YAAY,GACpD;AAAA,sBAAAF,KAACG,OAAA,EAAK,OAAM,WAAW,mBAAI,OAAO,EAAE,GAAE;AAAA,MACtC,gBAAAF,MAACC,MAAA,EAAI,eAAc,OAAM,KAAK,GAC3B;AAAA,iBAAS,UAAa,OAAO,KAC5B,gBAAAD,MAACE,OAAA,EAAK,OAAM,UAAS;AAAA;AAAA,UACZ,YAAY,IAAI;AAAA,WACzB;AAAA,QAED,YAAY,UACX,gBAAAF,MAACE,OAAA,EAAK,OAAM,QAAO;AAAA;AAAA,UACb,cAAc,OAAO;AAAA,WAC3B;AAAA,QAED,SACC,gBAAAH,KAACG,OAAA,EAAK,OAAM,WACT,6BAAmB,KAAK,GAC3B;AAAA,SAEJ;AAAA,OACF;AAAA,KAEJ;AAEJ;;;AI5GA,SAAS,OAAAC,MAAK,QAAAC,aAAY;AAkClB,SAGA,OAAAC,MAHA,QAAAC,aAAA;AAfD,SAAS,YAAY,EAAE,KAAK,GAAqB;AACtD,QAAM,EAAE,OAAO,WAAW,WAAW,eAAe,SAAS,IAAI;AAGjE,MAAI,CAAC,SAAS,MAAM,WAAW,GAAG;AAChC,WAAO;AAAA,EACT;AAEA,QAAM,QAAQ,MAAM,MAAM,IAAI;AAC9B,QAAM,WAAW,SAAS,QAAQ,OAAO,GAAG,EAAE,MAAM,GAAG,EAAE,IAAI,KAAK;AAElE,SACE,gBAAAA,MAACH,MAAA,EAAI,eAAc,UAAS,WAAW,GAErC;AAAA,oBAAAG,MAACH,MAAA,EAAI,eAAc,OAAM,KAAK,GAC5B;AAAA,sBAAAG,MAACF,OAAA,EAAK,MAAI,MAAC,OAAM,WAAU;AAAA;AAAA,QACrB,gBAAgB,iBAAO;AAAA,QAAK;AAAA,SAClC;AAAA,MACA,gBAAAC,KAACD,OAAA,EAAK,MAAI,MAAE,oBAAS;AAAA,MACrB,gBAAAE,MAACF,OAAA,EAAK,OAAM,WAAU;AAAA;AAAA,QACjB;AAAA,QAAU;AAAA,QAAG;AAAA,QAAU;AAAA,SAC5B;AAAA,OACF;AAAA,IAGA,gBAAAC,KAACF,MAAA,EAAI,eAAc,UAAS,YAAY,GACrC,gBACE,OAAO,CAAC,SAAS,CAAC,KAAK,WAAW,KAAK,KAAK,CAAC,KAAK,WAAW,KAAK,CAAC,EACnE,IAAI,CAAC,MAAM,MACV,gBAAAE,KAAC,YAAiB,QAAH,CAAe,CAC/B,GACL;AAAA,KACF;AAEJ;AAGA,SAAS,SAAS,EAAE,KAAK,GAAqB;AAG5C,MAAI,KAAK,WAAW,KAAK,KAAK,KAAK,WAAW,KAAK,GAAG;AACpD,WAAO;AAAA,EACT;AAGA,MAAI,KAAK,WAAW,IAAI,GAAG;AAEzB,WAAO,gBAAAA,KAACD,OAAA,EAAK,OAAM,WAAW,gBAAK;AAAA,EACrC;AAGA,MAAI,KAAK,WAAW,GAAG,GAAG;AACxB,WAAO,gBAAAC,KAACD,OAAA,EAAK,OAAM,WAAW,gBAAK;AAAA,EACrC;AAGA,MAAI,KAAK,WAAW,GAAG,GAAG;AACxB,WAAO,gBAAAC,KAACD,OAAA,EAAK,OAAM,WAAW,gBAAK;AAAA,EACrC;AAGA,SAAO,gBAAAC,KAACD,OAAA,EAAK,OAAM,WAAW,gBAAK;AACrC;;;AChFA,SAAS,OAAAG,MAAK,QAAAC,aAAY;AAoCpB,gBAAAC,MAKI,QAAAC,aALJ;AAjCN,IAAM,kBAAkB;AAWjB,SAAS,cAAc,EAAE,QAAQ,OAAO,cAAc,GAAuB;AAElF,QAAM,QAAQ,MAAM,MAAM,oBAAoB;AAC9C,MAAI,CAAC,MAAO,QAAO;AAEnB,QAAM,QAAQ,MAAM,CAAC,EAAG,YAAY,EAAE,KAAK;AAC3C,MAAI,OAAO,WAAW,EAAG,QAAO;AAGhC,QAAM,UAAU,CAAC,SAAS,MAAM,WAAW,GAAG,IAC1C,OAAO,MAAM,GAAG,CAAC,IACjB,OACG,OAAO,CAAC,MAAM,EAAE,KAAK,YAAY,EAAE,SAAS,KAAK,CAAC,EAClD,MAAM,GAAG,CAAC;AAGjB,MAAI,SAAS,QAAQ,SAAS,KAAK,QAAQ,KAAK,CAAC,MAAM,EAAE,KAAK,YAAY,MAAM,KAAK,EAAG,QAAO;AAE/F,MAAI,QAAQ,WAAW,EAAG,QAAO;AAEjC,SACE,gBAAAA,MAACH,MAAA,EAAI,eAAc,UAAS,YAAY,GAAG,WAAW,GACpD;AAAA,oBAAAE,KAACD,OAAA,EAAK,OAAM,WAAU,UAAQ,MAAC,4CAE/B;AAAA,IACC,QAAQ,IAAI,CAAC,OAAO,MACnB,gBAAAC,KAACF,MAAA,EACC,0BAAAG,MAACF,OAAA,EAAK,OAAO,MAAM,gBAAgB,kBAAkB,WAClD;AAAA,YAAM,gBAAgB,cAAS;AAAA,MAAQ,MAAM;AAAA,OAChD,KAHQ,MAAM,IAIhB,CACD;AAAA,IACD,gBAAAC,KAACF,MAAA,EAAI,WAAW,GACd,0BAAAE,KAACD,OAAA,EAAK,OAAM,WAAU,UAAQ,MAAC,6DAE/B,GACF;AAAA,KACF;AAEJ;;;ACtDA,SAAS,OAAAG,MAAK,QAAAC,aAAY;AAmCpB,gBAAAC,MAKI,QAAAC,aALJ;AAxBN,IAAMC,mBAAkB;AAEjB,SAAS,aAAa,EAAE,OAAO,OAAO,cAAc,GAAsB;AAE/E,QAAM,QAAQ,MAAM,MAAM,mBAAmB;AAC7C,MAAI,CAAC,MAAO,QAAO;AAEnB,QAAM,QAAQ,MAAM,CAAC,EAAG,YAAY,EAAE,KAAK;AAC3C,MAAI,MAAM,WAAW,EAAG,QAAO;AAG/B,QAAM,UAAU,CAAC,SAAS,MAAM,WAAW,GAAG,IAC1C,MAAM,MAAM,GAAG,CAAC,IAChB,MACG,OAAO,CAAC,MAAM,EAAE,YAAY,EAAE,SAAS,KAAK,CAAC,EAC7C,MAAM,GAAG,CAAC;AAGjB,MAAI,SAAS,QAAQ,KAAK,CAAC,MAAM,EAAE,YAAY,MAAM,KAAK,EAAG,QAAO;AAEpE,MAAI,QAAQ,WAAW,EAAG,QAAO;AAEjC,SACE,gBAAAD,MAACH,MAAA,EAAI,eAAc,UAAS,YAAY,GAAG,WAAW,GACpD;AAAA,oBAAAE,KAACD,OAAA,EAAK,OAAM,WAAU,UAAQ,MAAC,4CAE/B;AAAA,IACC,QAAQ,IAAI,CAAC,MAAM,MAClB,gBAAAC,KAACF,MAAA,EACC,0BAAAG,MAACF,OAAA,EAAK,OAAO,MAAM,gBAAgBG,mBAAkB,WAClD;AAAA,YAAM,gBAAgB,cAAS;AAAA,MAAQ;AAAA,OAC1C,KAHQ,IAIV,CACD;AAAA,IACD,gBAAAF,KAACF,MAAA,EAAI,WAAW,GACd,0BAAAE,KAACD,OAAA,EAAK,OAAM,WAAU,UAAQ,MAAC,6DAE/B,GACF;AAAA,KACF;AAEJ;;;AC/BO,IAAM,mBAAN,MAAuB;AAAA,EACnB,aAAa,oBAAI,IAA6B;AAAA,EAC9C,aAAa,oBAAI,IAAsB;AAAA;AAAA,EAGhD,SAAS,MAAc,SAAgC;AACrD,SAAK,WAAW,IAAI,MAAM,OAAO;AAAA,EACnC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,IACE,MACA,QACU;AACV,QAAI,CAAC,iBAAiB,OAAO,KAAK,GAAG;AACnC,YAAM,IAAI,uBAAuB,OAAO,KAAK;AAAA,IAC/C;AAGA,UAAM,WAAW,GAAG,IAAI,IAAI,OAAO,OAAO,IAAI,OAAO,KAAK;AAE1D,UAAM,SAAS,KAAK,WAAW,IAAI,QAAQ;AAC3C,QAAI,OAAQ,QAAO;AAEnB,UAAM,UAAU,KAAK,WAAW,IAAI,IAAI;AACxC,QAAI,CAAC,SAAS;AACZ,YAAM,YAAY,CAAC,GAAG,KAAK,WAAW,KAAK,CAAC,EAAE,KAAK,IAAI;AACvD,YAAM,IAAI,MAAM,uCAAmB,IAAI,wBAAS,SAAS,EAAE;AAAA,IAC7D;AAEA,UAAM,WAAW,QAAQ,MAAM;AAC/B,SAAK,WAAW,IAAI,UAAU,QAAQ;AACtC,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,OAAiB;AACf,WAAO,CAAC,GAAG,KAAK,WAAW,KAAK,CAAC;AAAA,EACnC;AAAA;AAAA,EAGA,QAAc;AACZ,SAAK,WAAW,MAAM;AAAA,EACxB;AACF;AAOO,IAAM,kBAAkB,IAAI,iBAAiB;AAEpD,gBAAgB,SAAS,YAAY,CAAC,WAAW;AAE/C,SAAO,IAAI,iBAAiB;AAAA,IAC1B,QAAQ,OAAO;AAAA,IACf,SAAS,OAAO;AAAA,IAChB,OAAO,OAAO;AAAA,EAChB,CAAC;AACH,CAAC;AAOM,SAAS,eAAe,QAKlB;AACX,SAAO,gBAAgB,IAAI,OAAO,MAAM,MAAM;AAChD;;;AC3FO,IAAM,eAAe;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,EAAE,KAAK,IAAI;;;ACnCJ,SAAS,kBAAkB,MAAmC;AACnE,QAAM,WAAqB,CAAC;AAG5B,WAAS,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mGAMG;AAGjB,WAAS,KAAK;AAAA,sBACT,KAAK,KAAK,EAAE;AAGjB,QAAM,MAAM,oBAAI,KAAK;AACrB,QAAM,UAAU,IAAI,mBAAmB,SAAS;AAAA,IAC9C,MAAM;AAAA,IACN,OAAO;AAAA,IACP,KAAK;AAAA,IACL,SAAS;AAAA,EACX,CAAC;AACD,QAAM,UAAU,IAAI,mBAAmB,SAAS;AAAA,IAC9C,MAAM;AAAA,IACN,QAAQ;AAAA,EACV,CAAC;AACD,WAAS,KAAK;AAAA,kCACP,OAAO;AAAA,kCACP,OAAO;AAAA,kCACP,KAAK,GAAG,EAAE;AAGjB,MAAI,KAAK,SAAS,KAAK,MAAM,SAAS,GAAG;AACvC,UAAM,YAAY,KAAK,MACpB,IAAI,CAAC,MAAM;AACV,YAAM,YAAY,EAAE,aAChB,OAAO,KAAM,EAAE,WAAuC,cAAc,CAAC,CAAC,EACnE,KAAK,IAAI,IACZ;AACJ,aAAO,OAAO,EAAE,IAAI,WAAM,EAAE,WAAW,GAAG,YAAY,2BAAO,SAAS,WAAM,EAAE;AAAA,IAChF,CAAC,EACA,KAAK,IAAI;AACZ,aAAS,KAAK;AAAA;AAAA;AAAA;AAAA,EAIhB,SAAS;AAAA;AAAA,wLAEiC;AAAA,EAC1C;AAGA,MAAI,KAAK,gBAAgB;AACvB,aAAS,KAAK;AAAA;AAAA,EAEhB,KAAK,cAAc,EAAE;AAAA,EACrB;AAGA,WAAS,KAAK;AAAA;AAAA;AAAA;AAAA,2HAIO;AAGrB,WAAS,KAAK,YAAY;AAE1B,SAAO,SAAS,KAAK,MAAM;AAC7B;;;ACtEO,IAAM,eAAN,MAAmB;AAAA,EACf,SAAS,oBAAI,IAAkB;AAAA,EAC/B;AAAA,EAET,YAAY,MAA4B;AACtC,SAAK,iBAAiB,IAAI,IAAI,MAAM,iBAAiB,CAAC,CAAC;AAAA,EACzD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,SAAS,MAAkB;AACzB,QAAI,KAAK,OAAO,IAAI,KAAK,IAAI,GAAG;AAC9B,YAAM,IAAI,MAAM,iBAAO,KAAK,IAAI,gEAAc;AAAA,IAChD;AACA,SAAK,OAAO,IAAI,KAAK,MAAM,IAAI;AAC/B,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,YAAY,OAAqB;AAC/B,eAAW,QAAQ,OAAO;AACxB,WAAK,SAAS,IAAI;AAAA,IACpB;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,WAAW,MAAuB;AAChC,WAAO,KAAK,OAAO,OAAO,IAAI;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,IAAI,MAAgC;AAClC,QAAI,KAAK,eAAe,IAAI,IAAI,EAAG,QAAO;AAC1C,WAAO,KAAK,OAAO,IAAI,IAAI;AAAA,EAC7B;AAAA;AAAA;AAAA;AAAA,EAKA,OAAe;AACb,UAAM,SAAiB,CAAC;AACxB,eAAW,CAAC,MAAM,IAAI,KAAK,KAAK,QAAQ;AACtC,UAAI,CAAC,KAAK,eAAe,IAAI,IAAI,GAAG;AAClC,eAAO,KAAK,IAAI;AAAA,MAClB;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,QAAkB;AAChB,WAAO,CAAC,GAAG,KAAK,OAAO,KAAK,CAAC;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,MAAuB;AACzB,WAAO,KAAK,OAAO,IAAI,IAAI;AAAA,EAC7B;AAAA;AAAA;AAAA;AAAA,EAKA,UAAU,MAAuB;AAC/B,WAAO,KAAK,OAAO,IAAI,IAAI,KAAK,CAAC,KAAK,eAAe,IAAI,IAAI;AAAA,EAC/D;AAAA;AAAA;AAAA;AAAA,EAKA,QAAQ,MAAoB;AAC1B,SAAK,eAAe,IAAI,IAAI;AAAA,EAC9B;AAAA;AAAA;AAAA;AAAA,EAKA,OAAO,MAAoB;AACzB,SAAK,eAAe,OAAO,IAAI;AAAA,EACjC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,QAAQ,MAAc,MAAe,KAAuC;AAChF,UAAM,OAAO,KAAK,IAAI,IAAI;AAC1B,QAAI,CAAC,MAAM;AACT,aAAO;AAAA,QACL,SAAS;AAAA,QACT,MAAM,iBAAO,IAAI;AAAA,QACjB,OAAO;AAAA,MACT;AAAA,IACF;AAEA,QAAI;AACF,aAAO,MAAM,KAAK,QAAQ,MAAM,GAAG;AAAA,IACrC,SAAS,KAAc;AACrB,YAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,aAAO;AAAA,QACL,SAAS;AAAA,QACT,MAAM,iBAAO,IAAI,mCAAU,OAAO;AAAA,QAClC,OAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AACF;;;ACjBO,IAAM,kBAAN,MAAsC;AAAA,EAC3C,MAAM,WAAmB,OAAyB;AAChD,WAAO;AAAA,EACT;AACF;;;ACnFO,IAAM,UAAN,MAAc;AAAA,EACV,YAA2B,CAAC;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAGA,mBAAmB,IAAI,gBAAgB;AAAA;AAAA,EAGhD,gBAAkC,CAAC;AAAA,EAEnC,YACE,UACA,QAA+B,CAAC,GAChC,aACA,SACA;AACA,SAAK,YAAY;AAEjB,QAAI,iBAAiB,cAAc;AACjC,WAAK,gBAAgB;AAAA,IACvB,OAAO;AACL,WAAK,gBAAgB,IAAI,aAAa;AACtC,WAAK,cAAc,YAAY,KAAK;AAAA,IACtC;AACA,SAAK,eAAe,eAAe,IAAI,YAAY;AACnD,SAAK,WAAW;AAAA,MACd,KAAK,SAAS,OAAO,QAAQ,IAAI;AAAA,MACjC,eAAe,SAAS,iBAAiB;AAAA,MACzC,mBAAmB,SAAS,qBAAqB;AAAA,MACjD,sBAAsB,SAAS,wBAAwB;AAAA,MACvD,gBAAgB,SAAS;AAAA,MACzB,MAAM,SAAS,QAAQ,IAAI,gBAAgB;AAAA,MAC3C,YAAY,SAAS,cAAc,CAAC,SAAS,OAAO,QAAQ,IAAI,CAAC;AAAA,IACnE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAMA,IAAI,WAAmC;AACrC,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,kBAA0B;AAC5B,WAAO,KAAK,aAAa;AAAA,EAC3B;AAAA,EAEA,IAAI,cAA2B;AAC7B,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,QAAgB;AAClB,WAAO,KAAK,UAAU,MAAM;AAAA,EAC9B;AAAA;AAAA,EAGA,IAAI,eAA6B;AAC/B,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkBA,OAAO,KAAK,WAA+C;AAEzD,SAAK,UAAU,KAAK,EAAE,MAAM,QAAQ,SAAS,UAAU,CAAC;AAGxD,UAAM,YAAY,KAAK,IAAI;AAC3B,QAAI,aAAa;AAEjB,QAAI;AACF,aAAO,aAAa,KAAK,SAAS,eAAe;AAE/C,cAAM,eAAe,KAAK,mBAAmB;AAC7C,cAAM,CAAC,OAAO,IAAI;AAAA,UAChB,CAAC,GAAG,KAAK,SAAS;AAAA,UAClB;AAAA,YACE,OAAO,KAAK,UAAU,MAAM;AAAA,YAC5B,mBAAmB,KAAK,SAAS;AAAA,YACjC;AAAA,YACA,sBAAsB,KAAK,SAAS;AAAA,UACtC;AAAA,QACF;AACA,cAAM,cAAc,iBAAiB,cAAc,OAAO;AAG1D,cAAM,WAAW,KAAK,sBAAsB;AAC5C,cAAM,SAAS,KAAK,UAAU,KAAK,aAAa;AAAA,UAC9C,QAAQ,KAAK,iBAAiB;AAAA,UAC9B,OAAO,SAAS,SAAS,IAAI,WAAW;AAAA,QAC1C,CAAC;AAGD,YAAI,kBAAkB;AACtB,YAAI;AACJ,YAAI;AACJ,YAAI,mBAAkC;AAEtC,yBAAiB,SAAS,QAAQ;AAChC,cAAI,MAAM,SAAS;AACjB,+BAAmB,MAAM;AACzB,kBAAM,EAAE,MAAM,cAAc,SAAS,MAAM,QAAQ;AAAA,UACrD;AAEA,cAAI,MAAM,aAAa,MAAM,UAAU,SAAS,GAAG;AACjD,4BAAgB,MAAM;AAAA,UACxB;AAEA,cAAI,MAAM,OAAO;AACf,wBAAY,MAAM;AAAA,UACpB;AAEA,cAAI,MAAM,cAAc;AACtB,+BAAmB,MAAM;AAAA,UAC3B;AAAA,QACF;AAGA,YAAI,WAAW;AACb,gBAAM,UAAU,KAAK,UAAU,MAAM;AACrC,eAAK,aAAa,OAAO,WAAW,OAAO;AAC3C,gBAAM,EAAE,MAAM,SAAS,OAAO,WAAW,OAAO,QAAQ;AAAA,QAC1D;AAGA,cAAM,eAA4B;AAAA,UAChC,MAAM;AAAA,UACN,SAAS;AAAA,QACX;AACA,YAAI,iBAAiB,cAAc,SAAS,GAAG;AAC7C,uBAAa,YAAY;AAAA,QAC3B;AACA,aAAK,UAAU,KAAK,YAAY;AAGhC,YAAI,iBAAiB,cAAc,SAAS,GAAG;AAC7C,gBAAM,EAAE,MAAM,cAAc,OAAO,cAAc;AAGjD,gBAAM,cAAc,KAAK,iBAAiB,aAAa;AACvD,cAAI,aAAa;AACf,kBAAM,WAAW;AACjB,kBAAM,EAAE,MAAM,cAAc,SAAS,SAAS;AAE9C,yBAAa,YAAY;AAEzB,yBAAa,WAAW;AAExB,iBAAK,gBAAgB,CAAC;AACtB;AACA;AAAA,UACF;AAGA,gBAAM,UAAU,MAAM,KAAK,cAAc,aAAa;AACtD,eAAK,gBAAgB,QAAQ;AAE7B,qBAAW,QAAQ,QAAQ,OAAO;AAChC,kBAAM,EAAE,MAAM,eAAe,MAAM,KAAK,MAAM,QAAQ,KAAK,OAAO;AAGlE,gBAAI,cAAc,KAAK,OAAO;AAC9B,gBAAI,KAAK,OAAO,QAAQ,KAAK,OAAO,KAAK,OAAO;AAC9C,6BAAe;AAAA;AAAA,EAAO,KAAK,OAAO,KAAK,KAAK;AAAA,YAC9C;AAEA,iBAAK,UAAU,KAAK;AAAA,cAClB,MAAM;AAAA,cACN,SAAS;AAAA,cACT,YAAY,KAAK;AAAA,cACjB,MAAM,KAAK;AAAA,YACb,CAAC;AAAA,UACH;AAEA;AACA;AAAA,QACF;AAGA;AAAA,MACF;AAAA,IACF,SAAS,KAAc;AACrB,UAAI,eAAe,gBAAgB,IAAI,SAAS,cAAc;AAC5D;AAAA,MACF;AACA,UAAI,eAAe,SAAS,IAAI,SAAS,cAAc;AACrD;AAAA,MACF;AAEA,YAAM;AAAA,QACJ,MAAM;AAAA,QACN,OAAO,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC;AAAA,MAC3D;AACA;AAAA,IACF;AAEA,UAAM,UAAU,KAAK,IAAI,IAAI;AAC7B,UAAM,EAAE,MAAM,QAAQ,QAAQ;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,MAAM,cAAc,OAAuI;AACzJ,UAAM,UAAuB;AAAA,MAC3B,KAAK,KAAK,SAAS;AAAA,MACnB,QAAQ,KAAK,iBAAiB;AAAA,MAC9B,YAAY,KAAK,SAAS;AAAA,IAC5B;AAGA,UAAM,cAAc,MAAM,MAAM,CAAC,OAAO;AACtC,YAAM,OAAO,KAAK,cAAc,IAAI,GAAG,IAAI;AAC3C,aAAO,MAAM,aAAa;AAAA,IAC5B,CAAC;AAED,QAAI,eAAe,MAAM,SAAS,GAAG;AAEnC,YAAM,eAAe;AACrB,YAAMI,SAAqE,CAAC;AAC5E,YAAMC,WAA4B,CAAC;AAGnC,eAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK,cAAc;AACnD,cAAM,QAAQ,MAAM,MAAM,GAAG,IAAI,YAAY;AAC7C,cAAM,WAAW,MAAM,IAAI,CAAC,OAAO,KAAK,YAAY,IAAI,OAAO,CAAC;AAChE,cAAM,eAAe,MAAM,QAAQ,IAAI,QAAQ;AAC/C,mBAAW,KAAK,cAAc;AAC5B,UAAAD,OAAM,KAAK,EAAE,IAAI;AACjB,UAAAC,SAAQ,KAAK,EAAE,MAAM;AAAA,QACvB;AAAA,MACF;AAEA,aAAO,EAAE,OAAAD,QAAO,SAAAC,SAAQ;AAAA,IAC1B;AAGA,UAAM,QAAqE,CAAC;AAC5E,UAAM,UAA4B,CAAC;AACnC,eAAW,MAAM,OAAO;AACtB,YAAM,IAAI,MAAM,KAAK,YAAY,IAAI,OAAO;AAC5C,YAAM,KAAK,EAAE,IAAI;AACjB,cAAQ,KAAK,EAAE,MAAM;AAAA,IACvB;AAEA,WAAO,EAAE,OAAO,QAAQ;AAAA,EAC1B;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,YACJ,IACA,KACiG;AACjG,UAAM,WAAW,GAAG;AACpB,UAAM,YAAY,KAAK,IAAI;AAG3B,UAAM,OAAO,KAAK,cAAc,IAAI,QAAQ;AAC5C,QAAI,CAAC,MAAM;AACT,YAAM,SAAS,iBAAO,QAAQ;AAC9B,aAAO;AAAA,QACL,MAAM,EAAE,MAAM,UAAU,QAAQ,GAAG,IAAI,QAAQ,EAAE,SAAS,OAAO,MAAM,QAAQ,OAAO,iBAAiB,EAAE;AAAA,QACzG,QAAQ,EAAE,MAAM,UAAU,SAAS,OAAO,OAAO,kBAAkB,UAAU;AAAA,MAC/E;AAAA,IACF;AAGA,QAAI;AACJ,QAAI;AACF,iBAAW,GAAG,YAAY,KAAK,MAAM,GAAG,SAAS,IAAI,CAAC;AAAA,IACxD,QAAQ;AACN,iBAAW,CAAC;AAAA,IACd;AAGA,UAAM,aAAa,MAAM,KAAK,SAAS,KAAK,MAAM,UAAU,QAAQ;AACpE,QAAI,CAAC,YAAY;AACf,YAAM,SAAS,iBAAO,QAAQ;AAC9B,aAAO;AAAA,QACL,MAAM,EAAE,MAAM,UAAU,QAAQ,GAAG,IAAI,QAAQ,EAAE,SAAS,OAAO,MAAM,QAAQ,OAAO,cAAc,EAAE;AAAA,QACtG,QAAQ,EAAE,MAAM,UAAU,SAAS,OAAO,OAAO,eAAe,UAAU;AAAA,MAC5E;AAAA,IACF;AAGA,QAAI,CAAC,KAAK,UAAU;AAClB,YAAM,iBAAiB;AACvB,UAAI,OAAO,eAAe,YAAY,YAAY;AAChD,YAAI;AACF,gBAAM,eAAe,QAAQ,UAAU,GAAG;AAAA,QAC5C,QAAQ;AAAA,QAER;AAAA,MACF;AAAA,IACF;AAGA,QAAI;AACF,YAAM,SAAS,MAAM,KAAK,QAAQ,UAAU,GAAG;AAC/C,aAAO;AAAA,QACL,MAAM,EAAE,MAAM,UAAU,QAAQ,GAAG,IAAI,OAAO;AAAA,QAC9C,QAAQ;AAAA,UACN,MAAM;AAAA,UACN,SAAS,OAAO;AAAA,UAChB,OAAO,OAAO;AAAA,UACd;AAAA,QACF;AAAA,MACF;AAAA,IACF,SAAS,KAAc;AACrB,YAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,YAAM,cAAc,EAAE,SAAS,OAAO,MAAM,iBAAO,QAAQ,mCAAU,OAAO,IAAI,OAAO,kBAAkB;AACzG,aAAO;AAAA,QACL,MAAM,EAAE,MAAM,UAAU,QAAQ,GAAG,IAAI,QAAQ,YAAY;AAAA,QAC3D,QAAQ,EAAE,MAAM,UAAU,SAAS,OAAO,OAAO,mBAAmB,UAAU;AAAA,MAChF;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,iBAAiB,cAA2C;AAC1D,QAAI,KAAK,cAAc,SAAS,EAAG,QAAO;AAG1C,UAAM,eAAe,KAAK,cAAc,MAAM,EAAE;AAChD,QAAI,aAAa,SAAS,EAAG,QAAO;AAGpC,UAAM,QAAQ,aAAa,CAAC;AAC5B,UAAM,UAAU,aAAa;AAAA,MAC3B,CAAC,MAAM,EAAE,SAAS,MAAM,QAAQ,EAAE,UAAU,MAAM,SAAS,CAAC,EAAE;AAAA,IAChE;AAEA,QAAI,CAAC,QAAS,QAAO;AAGrB,WAAO,aAAa,KAAK,CAAC,OAAO,GAAG,SAAS,MAAM,IAAI;AAAA,EACzD;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,QAAc;AACZ,SAAK,iBAAiB,MAAM;AAAA,EAC9B;AAAA;AAAA,EAGA,QAAc;AACZ,SAAK,UAAU,SAAS;AACxB,SAAK,aAAa,aAAa;AAC/B,SAAK,gBAAgB,CAAC;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,qBAA6B;AAC3B,UAAM,eAAe,KAAK,cAAc,KAAK;AAC7C,UAAM,YAAY,aAAa,IAAI,CAAC,OAAO;AAAA,MACzC,MAAM,EAAE;AAAA,MACR,aAAa,EAAE;AAAA,MACf,YAAY,EAAE;AAAA,IAChB,EAAE;AAEF,UAAM,OAA4B;AAAA,MAChC,OAAO,KAAK,UAAU,MAAM;AAAA,MAC5B,OAAO,UAAU,SAAS,IAAI,YAAY;AAAA,MAC1C,gBAAgB,KAAK,SAAS,kBAAkB;AAAA,MAChD,KAAK,KAAK,SAAS;AAAA,IACrB;AAEA,WAAO,kBAAkB,IAAI;AAAA,EAC/B;AAAA;AAAA,EAGA,wBAA0C;AACxC,WAAO,KAAK,cAAc,KAAK,EAAE,IAAI,CAAC,OAAO;AAAA,MAC3C,MAAM;AAAA,MACN,UAAU;AAAA,QACR,MAAM,EAAE;AAAA,QACR,aAAa,EAAE;AAAA,QACf,YAAY,EAAE;AAAA,MAChB;AAAA,IACF,EAAE;AAAA,EACJ;AACF;;;AC9cA,SAAS,SAAS,UAAU,kBAAkB;AAC9C,SAAS,YAAAC,iBAAgB;AACzB,SAAS,aAAa;AACtB,OAAOC,cAAa;AAGpB,IAAM,qBAAqB;AAC3B,IAAM,4BAA4B;AAClC,IAAM,wBAAwB,KAAK,OAAO;AAG1C,IAAM,YAAYA,SAAQ,aAAa;AAUhC,SAAS,YAAY,WAAmB,KAAqB;AAClE,QAAM,WAAW,WAAW,SAAS,IAAI,YAAY,QAAQ,KAAK,SAAS;AAC3E,SAAO,QAAQ,QAAQ;AACzB;AAMA,eAAe,SAAS,QAAiC;AACvD,MAAI;AACF,WAAO,MAAMD,UAAS,MAAM;AAAA,EAC9B,QAAQ;AAEN,UAAM,SAAS,QAAQ,QAAQ,IAAI;AACnC,QAAI;AACF,YAAM,aAAa,MAAMA,UAAS,MAAM;AACxC,aAAO,QAAQ,YAAY,SAAS,QAAQ,MAAM,CAAC;AAAA,IACrD,QAAQ;AAEN,aAAO,QAAQ,MAAM;AAAA,IACvB;AAAA,EACF;AACF;AASA,eAAsB,QACpB,cACA,QACsD;AACtD,MAAI,aAAa,WAAW,GAAG;AAC7B,WAAO,EAAE,IAAI,KAAK;AAAA,EACpB;AAEA,QAAM,aAAa,MAAM,SAAS,MAAM;AAExC,aAAW,QAAQ,cAAc;AAC/B,UAAM,WAAW,MAAM,SAAS,IAAI;AAEpC,UAAM,MAAM,SAAS,UAAU,UAAU;AACzC,QAAI,CAAC,IAAI,WAAW,IAAI,KAAK,QAAQ,MAAM,CAAC,IAAI,WAAW,GAAG,KAAK,CAAC,IAAI,WAAW,IAAI,GAAG;AACxF,aAAO,EAAE,IAAI,KAAK;AAAA,IACpB;AAEA,QAAI,eAAe,UAAU;AAC3B,aAAO,EAAE,IAAI,KAAK;AAAA,IACpB;AAAA,EACF;AAEA,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,OAAO,iBAAO,MAAM,kEAAgB,aAAa,KAAK,IAAI,CAAC;AAAA,EAC7D;AACF;AASO,SAAS,eAAe,SAAiB,YAAY,2BAAmC;AAC7F,MAAI,QAAQ,UAAU,UAAW,QAAO;AACxC,QAAM,YAAY,QAAQ,MAAM,GAAG,SAAS;AAC5C,SAAO,GAAG,SAAS;AAAA;AAAA,oEAAuB,OAAO,QAAQ,MAAM,CAAC;AAClE;AAKO,SAAS,oBAA4B;AAC1C,SAAO;AACT;AA4CA,eAAsB,YACpB,SACA,MACA,KACA,YAAY,oBACZ,QAEA,gBACsE;AACtE,SAAO,IAAI,QAAQ,CAACE,aAAY;AAC9B,QAAI;AACJ,QAAI;AACJ,QAAI;AAEJ,QAAI,gBAAgB;AAClB,iBAAW;AACX,kBAAY;AACZ,iBAAW;AAAA,IACb,OAAO;AACL,iBAAW,CAAC;AACZ,iBAAW,YAAY,QAAQ;AAC/B,kBAAY,YACR,CAAC,MAAM,SAAS,GAAG,IAAI,IACvB;AAAA,IACN;AAEA,UAAM,QAAQ,MAAM,UAAU,WAAW;AAAA,MACvC;AAAA,MACA,OAAO;AAAA,MACP,KAAK,EAAE,GAAGC,SAAQ,IAAI;AAAA,MACtB,OAAO,CAAC,QAAQ,QAAQ,MAAM;AAAA,IAChC,CAAC;AAED,QAAI,SAAS;AACb,QAAI,SAAS;AAEb,UAAM,OAAO,GAAG,QAAQ,CAAC,SAAiB;AACxC,gBAAU,KAAK,SAAS;AAAA,IAC1B,CAAC;AAED,UAAM,OAAO,GAAG,QAAQ,CAAC,SAAiB;AACxC,gBAAU,KAAK,SAAS;AAAA,IAC1B,CAAC;AAED,UAAM,UAAU,WAAW,MAAM;AAC/B,YAAM,KAAK,SAAS;AAEpB,iBAAW,MAAM;AACf,cAAM,KAAK,SAAS;AAAA,MACtB,GAAG,GAAI;AAAA,IACT,GAAG,SAAS;AAGZ,QAAI,QAAQ;AACV,UAAI,OAAO,SAAS;AAClB,cAAM,KAAK,SAAS;AAAA,MACtB;AACA,aAAO,iBAAiB,SAAS,MAAM;AACrC,cAAM,KAAK,SAAS;AAAA,MACtB,GAAG,EAAE,MAAM,KAAK,CAAC;AAAA,IACnB;AAEA,UAAM,GAAG,SAAS,CAAC,SAAS;AAC1B,mBAAa,OAAO;AACpB,MAAAD,SAAQ,EAAE,QAAQ,QAAQ,UAAU,KAAK,CAAC;AAAA,IAC5C,CAAC;AAED,UAAM,GAAG,SAAS,CAAC,QAAQ;AACzB,mBAAa,OAAO;AACpB,gBAAU;AAAA,4CAAY,IAAI,OAAO;AACjC,MAAAA,SAAQ,EAAE,QAAQ,QAAQ,UAAU,KAAK,CAAC;AAAA,IAC5C,CAAC;AAAA,EACH,CAAC;AACH;;;AC9KA,SAAS,WAAW,MAA2B;AAC7C,MAAI,KAAK,WAAW,GAAG;AACrB,WAAO,EAAE,OAAO,CAAC,GAAG,KAAK,MAAM,UAAU,SAAS;AAAA,EACpD;AAEA,QAAM,UAAU,KAAK,QAAQ,MAAM;AACnC,QAAM,QAAQ,KAAK,QAAQ,IAAI;AAC/B,MAAI;AACJ,MAAI,YAAY,OAAO,UAAU,MAAM,WAAW,QAAQ;AACxD,UAAM;AAAA,EACR,WAAW,UAAU,IAAI;AACvB,UAAM;AAAA,EACR,OAAO;AACL,UAAM;AAAA,EACR;AAEA,QAAM,QAAQ,KAAK,MAAM,GAAG;AAE5B,MAAI;AACJ,MAAI,KAAK,SAAS,GAAG,GAAG;AACtB,QAAI,MAAM,SAAS,KAAK,MAAM,MAAM,SAAS,CAAC,MAAM,IAAI;AACtD,YAAM,IAAI;AAAA,IACZ;AACA,eAAW;AAAA,EACb,OAAO;AACL,eAAW;AAAA,EACb;AAEA,SAAO,EAAE,OAAO,KAAK,SAAS;AAChC;AAaA,SAAS,gBAAgB,UAAoB,UAAgC;AAC3E,QAAM,IAAI,SAAS;AACnB,QAAM,IAAI,SAAS;AAEnB,MAAI,MAAM,KAAK,MAAM,EAAG,QAAO,CAAC;AAChC,MAAI,MAAM,EAAG,QAAO,SAAS,IAAI,CAAC,UAAU,EAAE,IAAI,OAAa,KAAK,EAAE;AACtE,MAAI,MAAM,EAAG,QAAO,SAAS,IAAI,CAAC,UAAU,EAAE,IAAI,UAAgB,KAAK,EAAE;AAGzE,QAAM,QAA+B,CAAC;AAEtC,QAAM,OAAO,IAAI;AAEjB,QAAM,IAAc,IAAI,MAAM,IAAI,OAAO,CAAC,EAAE,KAAK,EAAE;AAEnD,QAAM,SAAS;AAEf,IAAE,SAAS,CAAC,IAAI;AAGhB;AACA,aAAS,IAAI,GAAG,KAAK,MAAM,KAAK;AAE9B,YAAM,WAAW,oBAAI,IAAoB;AAEzC,YAAM,SAAS,CAAC;AAChB,YAAM,OAAO;AAEb,eAAS,IAAI,QAAQ,KAAK,MAAM,KAAK,GAAG;AACtC,cAAM,KAAK,IAAI;AACf,YAAI;AAEJ,YAAI,MAAM,CAAC,KAAM,MAAM,KAAK,EAAE,KAAK,CAAC,IAAK,EAAE,KAAK,CAAC,GAAK;AACpD,cAAI,EAAE,KAAK,CAAC;AAAA,QACd,OAAO;AACL,cAAI,EAAE,KAAK,CAAC,IAAK;AAAA,QACnB;AAEA,YAAI,IAAI,IAAI;AAGZ,eAAO,IAAI,KAAK,IAAI,KAAK,SAAS,CAAC,MAAM,SAAS,CAAC,GAAG;AACpD;AACA;AAAA,QACF;AAEA,UAAE,EAAE,IAAI;AACR,iBAAS,IAAI,GAAG,CAAC;AAGjB,YAAI,KAAK,KAAK,KAAK,GAAG;AACpB,gBAAM,KAAK,QAAQ;AAEnB,iBAAO,UAAU,OAAO,GAAG,GAAG,UAAU,UAAU,MAAM;AAAA,QAC1D;AAAA,MACF;AAEA,YAAM,KAAK,QAAQ;AAAA,IACrB;AAGA,SAAO,SAAS,IAAI,CAAC,UAAU,EAAE,IAAI,UAAgB,KAAK,EAAE,EACzD,OAAO,SAAS,IAAI,CAAC,UAAU,EAAE,IAAI,OAAa,KAAK,EAAE,CAAC;AAC/D;AAQA,SAAS,UACP,OACA,GACA,GACA,UACA,UACA,QACY;AACZ,QAAM,SAAqB,CAAC;AAE5B,MAAI,IAAI;AACR,MAAI,IAAI;AAGR,WAAS,IAAI,MAAM,SAAS,GAAG,KAAK,GAAG,KAAK;AAC1C,UAAM,IAAI,IAAI;AAId,UAAM,eAAe,IAAI,IAAK,MAAM,IAAI,CAAC,IAA4B,oBAAI,IAAI;AAC7E,UAAM,YAAY,aAAa,IAAI,IAAI,CAAC;AACxC,UAAM,UAAU,aAAa,IAAI,IAAI,CAAC;AAEtC,QAAI;AACJ,QAAI,MAAM,CAAC,GAAG;AAEZ,cAAQ,IAAI;AAAA,IACd,WAAW,MAAM,GAAG;AAElB,cAAQ,IAAI;AAAA,IACd,YAAY,aAAa,OAAO,WAAW,KAAK;AAE9C,cAAQ,IAAI;AAAA,IACd,OAAO;AAEL,cAAQ,IAAI;AAAA,IACd;AAEA,UAAM,QAAQ,aAAa,IAAI,KAAK,KAAK;AACzC,UAAM,QAAQ,QAAQ;AAItB,QAAI,UAAU,IAAI,GAAG;AAMnB,aAAO,IAAI,QAAQ,KAAK,IAAI,OAAO;AACjC;AACA;AACA,eAAO,QAAQ,EAAE,IAAI,SAAS,MAAM,SAAS,CAAC,EAAY,CAAC;AAAA,MAC7D;AAGA,UAAI,IAAI,OAAO;AACb;AACA,eAAO,QAAQ,EAAE,IAAI,UAAU,MAAM,SAAS,CAAC,EAAY,CAAC;AAAA,MAC9D;AAAA,IACF,OAAO;AAML,aAAO,IAAI,SAAS,IAAI,QAAQ,GAAG;AACjC;AACA;AACA,eAAO,QAAQ,EAAE,IAAI,SAAS,MAAM,SAAS,CAAC,EAAY,CAAC;AAAA,MAC7D;AAGA,UAAI,IAAI,OAAO;AACb;AACA,eAAO,QAAQ,EAAE,IAAI,OAAO,MAAM,SAAS,CAAC,EAAY,CAAC;AAAA,MAC3D;AAAA,IACF;AAAA,EACF;AAIA,SAAO,IAAI,KAAK,IAAI,GAAG;AACrB;AACA;AACA,WAAO,QAAQ,EAAE,IAAI,SAAS,MAAM,SAAS,CAAC,EAAY,CAAC;AAAA,EAC7D;AAEA,SAAO;AACT;AAOA,IAAM,gBAAgB;AAKtB,SAAS,eAAe,WAAmC;AACzD,MAAI,UAAU,WAAW,EAAG,QAAO,CAAC;AAGpC,QAAM,gBAA0B,CAAC;AACjC,WAAS,IAAI,GAAG,IAAI,UAAU,QAAQ,KAAK;AACzC,UAAM,OAAO,UAAU,CAAC;AACxB,QAAI,KAAK,OAAO,SAAS;AACvB,oBAAc,KAAK,CAAC;AAAA,IACtB;AAAA,EACF;AAEA,MAAI,cAAc,WAAW,EAAG,QAAO,CAAC;AAGxC,QAAM,SAAqB,CAAC,CAAC,cAAc,CAAC,CAAW,CAAC;AAExD,WAAS,MAAM,GAAG,MAAM,cAAc,QAAQ,OAAO;AACnD,UAAM,YAAY,OAAO,OAAO,SAAS,CAAC;AAC1C,UAAM,UAAU,UAAU,UAAU,SAAS,CAAC;AAC9C,UAAM,UAAU,cAAc,GAAG;AAEjC,QAAI,MAAM;AACV,aAAS,IAAI,UAAU,GAAG,IAAI,SAAS,KAAK;AAC1C,YAAM,OAAO,UAAU,CAAC;AACxB,UAAI,KAAK,OAAO,QAAS;AAAA,IAC3B;AAEA,QAAI,OAAO,IAAI,eAAe;AAC5B,gBAAU,KAAK,OAAO;AAAA,IACxB,OAAO;AACL,aAAO,KAAK,CAAC,OAAO,CAAC;AAAA,IACvB;AAAA,EACF;AAGA,QAAM,QAAoB,CAAC;AAE3B,aAAW,SAAS,QAAQ;AAC1B,UAAM,iBAAiB,MAAM,CAAC;AAC9B,UAAM,gBAAgB,MAAM,MAAM,SAAS,CAAC;AAE5C,UAAM,WAAW,KAAK,IAAI,GAAG,iBAAiB,aAAa;AAC3D,UAAM,SAAS,KAAK,IAAI,UAAU,SAAS,GAAG,gBAAgB,aAAa;AAE3E,UAAM,QAAQ,UAAU,MAAM,UAAU,SAAS,CAAC;AAElD,QAAI,UAAU;AACd,QAAI,UAAU;AACd,aAAS,IAAI,GAAG,IAAI,UAAU,KAAK;AACjC,YAAM,OAAO,UAAU,CAAC;AACxB,UAAI,KAAK,OAAO,WAAW,KAAK,OAAO,SAAU;AACjD,UAAI,KAAK,OAAO,WAAW,KAAK,OAAO,MAAO;AAAA,IAChD;AAEA,QAAI,WAAW;AACf,QAAI,WAAW;AACf,eAAW,YAAY,OAAO;AAC5B,UAAI,SAAS,OAAO,WAAW,SAAS,OAAO,SAAU;AACzD,UAAI,SAAS,OAAO,WAAW,SAAS,OAAO,MAAO;AAAA,IACxD;AAEA,UAAM,KAAK;AAAA,MACT,UAAU,UAAU;AAAA,MACpB;AAAA,MACA,UAAU,UAAU;AAAA,MACpB;AAAA,MACA;AAAA,IACF,CAAC;AAAA,EACH;AAEA,SAAO;AACT;AAMA,SAAS,kBAAkB,OAAmB,UAA0B;AACtE,MAAI,MAAM,WAAW,EAAG,QAAO;AAE/B,QAAM,QAAkB,CAAC;AACzB,QAAM,WAAW,gBAAgB,QAAQ;AAEzC,QAAM,KAAK,SAAS,QAAQ,EAAE;AAC9B,QAAM,KAAK,SAAS,QAAQ,EAAE;AAE9B,aAAW,QAAQ,OAAO;AACxB,UAAM;AAAA,MACJ,OAAO,OAAO,KAAK,QAAQ,CAAC,IAAI,OAAO,KAAK,QAAQ,CAAC,KAAK,OAAO,KAAK,QAAQ,CAAC,IAAI,OAAO,KAAK,QAAQ,CAAC;AAAA,IAC1G;AAEA,eAAW,YAAY,KAAK,OAAO;AACjC,cAAQ,SAAS,IAAI;AAAA,QACnB,KAAK;AACH,gBAAM,KAAK,IAAI,SAAS,IAAI,EAAE;AAC9B;AAAA,QACF,KAAK;AACH,gBAAM,KAAK,IAAI,SAAS,IAAI,EAAE;AAC9B;AAAA,QACF,KAAK;AACH,gBAAM,KAAK,IAAI,SAAS,IAAI,EAAE;AAC9B;AAAA,MACJ;AAAA,IACF;AAAA,EACF;AAEA,SAAO,MAAM,KAAK,IAAI;AACxB;AAEA,SAAS,kBAAkB,OAAiB,UAA0B;AACpE,QAAM,WAAW,gBAAgB,QAAQ;AACzC,QAAM,QAAkB,CAAC;AAEzB,QAAM,KAAK,eAAe;AAC1B,QAAM,KAAK,SAAS,QAAQ,EAAE;AAC9B,QAAM,KAAK,cAAc,OAAO,MAAM,MAAM,CAAC,KAAK;AAElD,aAAW,QAAQ,OAAO;AACxB,UAAM,KAAK,IAAI,IAAI,EAAE;AAAA,EACvB;AAEA,SAAO,MAAM,KAAK,IAAI;AACxB;AAEA,SAAS,sBAAsB,OAAiB,UAA0B;AACxE,QAAM,WAAW,gBAAgB,QAAQ;AACzC,QAAM,QAAkB,CAAC;AAEzB,QAAM,KAAK,SAAS,QAAQ,EAAE;AAC9B,QAAM,KAAK,eAAe;AAC1B,QAAM,KAAK,SAAS,OAAO,MAAM,MAAM,CAAC,UAAU;AAElD,aAAW,QAAQ,OAAO;AACxB,UAAM,KAAK,IAAI,IAAI,EAAE;AAAA,EACvB;AAEA,SAAO,MAAM,KAAK,IAAI;AACxB;AAEA,SAAS,gBAAgB,UAA0B;AACjD,QAAM,aAAa,SAAS,QAAQ,OAAO,GAAG;AAC9C,SAAO,WAAW,MAAM,GAAG,EAAE,IAAI,KAAK;AACxC;AAYO,SAAS,gBACd,YACA,YACA,UACU;AACV,MAAI,eAAe,YAAY;AAC7B,WAAO;AAAA,MACL;AAAA,MACA,OAAO;AAAA,MACP,eAAe,WAAW,SAAS;AAAA,MACnC,WAAW;AAAA,MACX,WAAW;AAAA,IACb;AAAA,EACF;AAEA,MAAI,WAAW,WAAW,GAAG;AAC3B,UAAM,QAAQ,WAAW,UAAU;AACnC,UAAME,SAAQ,kBAAkB,MAAM,OAAO,QAAQ;AACrD,WAAO;AAAA,MACL;AAAA,MACA,OAAAA;AAAA,MACA,eAAe;AAAA,MACf,WAAW,MAAM,MAAM;AAAA,MACvB,WAAW;AAAA,IACb;AAAA,EACF;AAEA,MAAI,WAAW,WAAW,GAAG;AAC3B,UAAM,QAAQ,WAAW,UAAU;AACnC,UAAMA,SAAQ,sBAAsB,MAAM,OAAO,QAAQ;AACzD,WAAO;AAAA,MACL;AAAA,MACA,OAAAA;AAAA,MACA,eAAe;AAAA,MACf,WAAW;AAAA,MACX,WAAW,MAAM,MAAM;AAAA,IACzB;AAAA,EACF;AAEA,QAAM,WAAW,WAAW,UAAU;AACtC,QAAM,WAAW,WAAW,UAAU;AAEtC,QAAM,YAAY,gBAAgB,SAAS,OAAO,SAAS,KAAK;AAEhE,MAAI,YAAY;AAChB,MAAI,YAAY;AAChB,aAAW,QAAQ,WAAW;AAC5B,QAAI,KAAK,OAAO,MAAO;AACvB,QAAI,KAAK,OAAO,SAAU;AAAA,EAC5B;AAEA,QAAM,QAAQ,eAAe,SAAS;AACtC,QAAM,QAAQ,kBAAkB,OAAO,QAAQ;AAE/C,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,eAAe;AAAA,IACf;AAAA,IACA;AAAA,EACF;AACF;;;AChdA,SAAS,aAAAC,kBAAiB;AAUnB,SAAS,UAAU,MAAsB;AAC9C,MAAI,KAAK,WAAW,EAAG,QAAO;AAE9B,QAAM,UAAU,KAAK,QAAQ,MAAM;AACnC,QAAM,QAAQ,KAAK,QAAQ,IAAI;AAE/B,MAAI,YAAY,OAAO,UAAU,MAAM,WAAW,QAAQ;AACxD,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAKO,SAAS,mBAAmB,MAAuB;AACxD,SAAO,KAAK,SAAS,IAAI,KAAK,KAAK,SAAS,MAAM;AACpD;AAgBO,SAAS,aAAa,iBAAyB,YAA4B;AAChF,QAAM,YAAY,UAAU,eAAe;AAG3C,QAAM,QAAQ,WAAW,QAAQ,SAAS,IAAI,EAAE,MAAM,IAAI;AAG1D,QAAM,sBAAsB,mBAAmB,eAAe;AAI9D,QAAM,iBAAiB,MAAM,SAAS,KAAK,MAAM,MAAM,SAAS,CAAC,MAAM;AAIvE,MAAI,SAAS,MAAM,KAAK,SAAS;AACjC,MAAI,uBAAuB,CAAC,gBAAgB;AAC1C,cAAU;AAAA,EACZ;AAEA,MAAI,CAAC,uBAAuB,gBAAgB;AAE1C,QAAI,OAAO,SAAS,SAAS,GAAG;AAC9B,eAAS,OAAO,MAAM,GAAG,CAAC,UAAU,MAAM;AAAA,IAC5C;AAAA,EACF;AAEA,SAAO;AACT;AAeA,eAAsB,iBACpB,UACA,iBACA,YACe;AACf,QAAM,UACJ,gBAAgB,SAAS,IACrB,aAAa,iBAAiB,UAAU,IACxC;AACN,QAAMA,WAAU,UAAU,SAAS,OAAO;AAC5C;;;ACzGA,SAAS,YAAAC,WAAU,YAAY;AAC/B,SAAS,YAAY;AACrB,SAAS,YAAAC,iBAAgB;AAezB,IAAM,iBAA6B;AAAA,EACjC,MAAM;AAAA,EACN,YAAY;AAAA,IACV,MAAM;AAAA,MACJ,MAAM;AAAA,MACN,aAAa;AAAA,IACf;AAAA,IACA,WAAW;AAAA,MACT,MAAM;AAAA,MACN,aAAa;AAAA,IACf;AAAA,IACA,SAAS;AAAA,MACP,MAAM;AAAA,MACN,aAAa;AAAA,IACf;AAAA,EACF;AAAA,EACA,UAAU,CAAC,MAAM;AAAA,EACjB,sBAAsB;AACxB;AAGA,eAAe,YAAY,UAAoC;AAC7D,QAAM,aAAa,MAAM,KAAK,UAAU,GAAG;AAC3C,MAAI;AACF,UAAM,SAAS,OAAO,MAAM,IAAI;AAChC,UAAM,EAAE,UAAU,IAAI,MAAM,WAAW,KAAK,QAAQ,GAAG,MAAM,CAAC;AAC9D,WAAO,OAAO,SAAS,GAAG,SAAS,EAAE,SAAS,CAAC;AAAA,EACjD,UAAE;AACA,UAAM,WAAW,MAAM;AAAA,EACzB;AACF;AAYO,IAAM,eAAqB;AAAA,EAChC,MAAM;AAAA,EACN,aACE;AAAA,EACF,YAAY;AAAA,EACZ,UAAU;AAAA,EAEV,MAAM,QAAQ,MAAe,KAAuC;AAClE,UAAM,SAAS;AACf,QAAI,CAAC,QAAQ,QAAQ,OAAO,OAAO,SAAS,UAAU;AACpD,aAAO,EAAE,SAAS,OAAO,MAAM,6CAAe,OAAO,eAAe;AAAA,IACtE;AAEA,UAAM,WAAW,YAAY,OAAO,MAAM,IAAI,GAAG;AAEjD,QAAI;AAEF,YAAM,WAAW,MAAM,KAAK,QAAQ;AACpC,YAAM,UAAU,KAAK,OAAO;AAC5B,UAAI,SAAS,OAAO,SAAS;AAC3B,eAAO;AAAA,UACL,SAAS;AAAA,UACT,MAAM,kCAAS,SAAS,OAAO,OAAO,MAAM,QAAQ,CAAC,CAAC;AAAA,UACtD,OAAO;AAAA,QACT;AAAA,MACF;AAGA,UAAI,SAAS,YAAY,GAAG;AAC1B,eAAO;AAAA,UACL,SAAS;AAAA,UACT,MAAM,IAAI,QAAQ;AAAA,UAClB,OAAO;AAAA,QACT;AAAA,MACF;AAGA,UAAI,SAAS,OAAO,GAAG;AACrB,cAAM,QAAQ,MAAM,YAAY,QAAQ;AACxC,YAAI,OAAO;AACT,iBAAO;AAAA,YACL,SAAS;AAAA,YACT,MAAM,IAAI,QAAQ;AAAA,YAClB,OAAO;AAAA,UACT;AAAA,QACF;AAAA,MACF;AAEA,YAAM,UAAU,MAAMC,UAAS,UAAU,OAAO;AAChD,YAAM,QAAQ,QAAQ,MAAM,IAAI;AAGhC,UAAI,MAAM,SAAS,KAAK,MAAM,MAAM,SAAS,CAAC,MAAM,MAAM,QAAQ,SAAS,IAAI,GAAG;AAChF,cAAM,IAAI;AAAA,MACZ;AAGA,YAAM,YAAY,KAAK,IAAI,GAAG,OAAO,aAAa,CAAC,IAAI;AACvD,YAAM,UAAU,OAAO,UACnB,KAAK,IAAI,OAAO,SAAS,MAAM,MAAM,IACrC,MAAM;AACV,YAAM,gBAAgB,MAAM,MAAM,WAAW,OAAO;AAGpD,YAAM,eAAe,OAAO,OAAO,EAAE;AACrC,YAAM,SAAS,cACZ,IAAI,CAAC,MAAM,MAAM;AAChB,cAAM,UAAU,OAAO,YAAY,IAAI,CAAC,EAAE,SAAS,cAAc,GAAG;AACpE,eAAO,GAAG,OAAO,SAAI,IAAI;AAAA,MAC3B,CAAC,EACA,KAAK,IAAI;AAGZ,YAAM,YAAY,MAAM,SAAS;AACjC,YAAM,WAAW,YAAY,IACzB;AAAA;AAAA,gBAAW,SAAS,uCAAmB,UAAU,CAAC,+BAClD;AAGJ,YAAM,UAAUC,UAAS,IAAI,KAAK,QAAQ,EAAE,QAAQ,OAAO,GAAG;AAC9D,YAAM,aAAc,YAAY,KAAK,UAAU,MAAM,SACjD,UAAK,YAAY,CAAC,IAAI,OAAO,YAC7B,GAAG,MAAM,MAAM;AAEnB,aAAO;AAAA,QACL,SAAS;AAAA,QACT,MAAM,eAAe,MAAM,IAAI;AAAA,QAC/B,SAAS,aAAM,OAAO,SAAI,UAAU;AAAA,MACtC;AAAA,IACF,SAAS,KAAc;AACrB,YAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,aAAO;AAAA,QACL,SAAS;AAAA,QACT,MAAM,6CAAU,OAAO;AAAA,QACvB,OAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AACF;;;AC7JA,SAAS,SAAAC,QAAO,YAAAC,iBAAgB;AAChC,SAAS,SAAS,YAAAC,WAAU,gBAAgB;AAe5C,IAAM,kBAA8B;AAAA,EAClC,MAAM;AAAA,EACN,YAAY;AAAA,IACV,MAAM;AAAA,MACJ,MAAM;AAAA,MACN,aAAa;AAAA,IACf;AAAA,IACA,SAAS;AAAA,MACP,MAAM;AAAA,MACN,aAAa;AAAA,IACf;AAAA,EACF;AAAA,EACA,UAAU,CAAC,QAAQ,SAAS;AAAA,EAC5B,sBAAsB;AACxB;AASO,IAAM,gBAAsB;AAAA,EACjC,MAAM;AAAA,EACN,aACE;AAAA,EACF,YAAY;AAAA,EACZ,UAAU;AAAA,EAEV,MAAM,QAAQ,MAAe,KAAuC;AAClE,UAAM,SAAS;AACf,QAAI,CAAC,QAAQ,QAAQ,OAAO,OAAO,SAAS,UAAU;AACpD,aAAO,EAAE,SAAS,OAAO,MAAM,6CAAe,OAAO,eAAe;AAAA,IACtE;AACA,QAAI,OAAO,YAAY,UAAa,OAAO,YAAY,MAAM;AAC3D,aAAO,EAAE,SAAS,OAAO,MAAM,gDAAkB,OAAO,eAAe;AAAA,IACzE;AAEA,UAAM,WAAW,YAAY,OAAO,MAAM,IAAI,GAAG;AAEjD,QAAI;AAEF,UAAI,IAAI,cAAc,IAAI,WAAW,SAAS,GAAG;AAC/C,cAAM,OAAO,MAAM,QAAQ,IAAI,YAAY,QAAQ;AACnD,YAAI,CAAC,KAAK,IAAI;AACZ,iBAAO,EAAE,SAAS,OAAO,MAAM,KAAK,OAAO,OAAO,sBAAsB;AAAA,QAC1E;AAAA,MACF;AAGA,UAAI,aAAa;AACjB,UAAI,gBAAgB;AACpB,UAAI;AACF,qBAAa,MAAMC,UAAS,UAAU,OAAO;AAC7C,wBAAgB;AAAA,MAClB,QAAQ;AAAA,MAER;AAGA,YAAMC,OAAM,QAAQ,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;AAElD,YAAM,UAAU,OAAO,OAAO,OAAO;AAErC,YAAM,iBAAiB,UAAU,YAAY,OAAO;AAGpD,YAAM,OAAO,gBAAgB,YAAY,SAAS,QAAQ;AAC1D,WAAK,gBAAgB;AAErB,YAAM,YAAY,QAAQ,MAAM,IAAI,EAAE;AACtC,YAAM,WAAW,OAAO,WAAW,SAAS,OAAO;AAGnD,YAAM,SAAS,gBAAgB,uBAAQ;AACvC,YAAM,cAAc,gBAChB,UAAK,KAAK,SAAS,KAAK,KAAK,SAAS,KACtC,UAAK,KAAK,SAAS;AAGvB,YAAM,WAAW,SAAS,QAAQ;AAClC,YAAM,UAAU,gBACZ,2BAAU,QAAQ,MAAM,KAAK,SAAS,KAAK,KAAK,SAAS,MACzD,2BAAU,QAAQ,MAAM,KAAK,SAAS;AAE1C,aAAO;AAAA,QACL,SAAS;AAAA,QACT,MAAM,eAAK,MAAM,SAAIC,UAAS,IAAI,KAAK,QAAQ,EAAE,QAAQ,OAAO,GAAG,CAAC,SAAI,SAAS,gBAAM,QAAQ,gBAAM,WAAW;AAAA,QAChH;AAAA,QACA;AAAA,MACF;AAAA,IACF,SAAS,KAAc;AACrB,YAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,aAAO;AAAA,QACL,SAAS;AAAA,QACT,MAAM,6CAAU,OAAO;AAAA,QACvB,OAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AACF;;;ACrHA,SAAS,YAAAC,iBAAgB;AACzB,SAAS,YAAAC,iBAAgB;AAiBzB,IAAM,iBAA6B;AAAA,EACjC,MAAM;AAAA,EACN,YAAY;AAAA,IACV,MAAM;AAAA,MACJ,MAAM;AAAA,MACN,aAAa;AAAA,IACf;AAAA,IACA,UAAU;AAAA,MACR,MAAM;AAAA,MACN,aAAa;AAAA,IACf;AAAA,IACA,UAAU;AAAA,MACR,MAAM;AAAA,MACN,aAAa;AAAA,IACf;AAAA,EACF;AAAA,EACA,UAAU,CAAC,QAAQ,YAAY,UAAU;AAAA,EACzC,sBAAsB;AACxB;AAWO,IAAM,eAAqB;AAAA,EAChC,MAAM;AAAA,EACN,aACE;AAAA,EACF,YAAY;AAAA,EACZ,UAAU;AAAA,EAEV,MAAM,QAAQ,MAAe,KAAuC;AAClE,UAAM,SAAS;AACf,QAAI,CAAC,QAAQ,QAAQ,OAAO,OAAO,SAAS,UAAU;AACpD,aAAO,EAAE,SAAS,OAAO,MAAM,6CAAe,OAAO,eAAe;AAAA,IACtE;AACA,QAAI,OAAO,OAAO,aAAa,UAAU;AACvC,aAAO,EAAE,SAAS,OAAO,MAAM,iDAAmB,OAAO,eAAe;AAAA,IAC1E;AACA,QAAI,OAAO,OAAO,aAAa,UAAU;AACvC,aAAO,EAAE,SAAS,OAAO,MAAM,iDAAmB,OAAO,eAAe;AAAA,IAC1E;AAEA,UAAM,WAAW,YAAY,OAAO,MAAM,IAAI,GAAG;AAGjD,QAAI,IAAI,cAAc,IAAI,WAAW,SAAS,GAAG;AAC/C,YAAM,OAAO,MAAM,QAAQ,IAAI,YAAY,QAAQ;AACnD,UAAI,CAAC,KAAK,IAAI;AACZ,eAAO,EAAE,SAAS,OAAO,MAAM,KAAK,OAAO,OAAO,sBAAsB;AAAA,MAC1E;AAAA,IACF;AAEA,QAAI;AACF,YAAM,UAAU,MAAMC,UAAS,UAAU,OAAO;AAGhD,YAAM,aAAa,QAAQ,QAAQ,OAAO,QAAQ;AAClD,UAAI,eAAe,IAAI;AACrB,eAAO;AAAA,UACL,SAAS;AAAA,UACT,MAAM;AAAA,UACN,OAAO;AAAA,QACT;AAAA,MACF;AAGA,YAAM,cAAc,QAAQ,QAAQ,OAAO,UAAU,aAAa,CAAC;AACnE,UAAI,gBAAgB,IAAI;AACtB,eAAO;AAAA,UACL,SAAS;AAAA,UACT,MAAM;AAAA,UACN,OAAO;AAAA,QACT;AAAA,MACF;AAGA,YAAM,aAAa,QAAQ,QAAQ,OAAO,UAAU,OAAO,QAAQ;AAEnE,YAAM,iBAAiB,UAAU,SAAS,UAAU;AAGpD,YAAM,OAAO,gBAAgB,SAAS,YAAY,QAAQ;AAC1D,WAAK,gBAAgB;AAGrB,YAAM,aAAa,QAAQ,MAAM,GAAG,UAAU;AAC9C,YAAM,YAAY,WAAW,MAAM,IAAI,EAAE;AACzC,YAAM,WAAW,OAAO,SAAS,MAAM,IAAI,EAAE;AAC7C,YAAM,WAAW,OAAO,SAAS,MAAM,IAAI,EAAE;AAG7C,YAAM,cAAc,IAAI,KAAK,SAAS,KAAK,KAAK,SAAS;AAEzD,YAAM,UAAU,2BAAUC,UAAS,QAAQ,CAAC,MAAM,KAAK,SAAS,KAAK,KAAK,SAAS;AAEnF,aAAO;AAAA,QACL,SAAS;AAAA,QACT,MAAM,uCAAS,QAAQ;AAAA,uCAAY,SAAS;AAAA,EAAO,QAAQ,kBAAQ,QAAQ;AAAA,oBAAU,WAAW;AAAA,QAChG;AAAA,QACA;AAAA,MACF;AAAA,IACF,SAAS,KAAc;AACrB,YAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,aAAO;AAAA,QACL,SAAS;AAAA,QACT,MAAM,6CAAU,OAAO;AAAA,QACvB,OAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AACF;;;ACjIA,SAAS,YAAAC,iBAAgB;AACzB,SAAS,YAAAC,iBAAgB;AAyBzB,IAAM,kBAA8B;AAAA,EAClC,MAAM;AAAA,EACN,YAAY;AAAA,IACV,MAAM;AAAA,MACJ,MAAM;AAAA,MACN,aAAa;AAAA,IACf;AAAA,IACA,OAAO;AAAA,MACL,MAAM;AAAA,MACN,aAAa;AAAA,MACb,OAAO;AAAA,QACL,MAAM;AAAA,QACN,YAAY;AAAA,UACV,SAAS,EAAE,MAAM,UAAU,aAAa,uFAAiB;AAAA,UACzD,SAAS,EAAE,MAAM,UAAU,aAAa,6CAAU;AAAA,UAClD,YAAY,EAAE,MAAM,WAAW,aAAa,iFAAqB;AAAA,QACnE;AAAA,QACA,UAAU,CAAC,WAAW,SAAS;AAAA,QAC/B,sBAAsB;AAAA,MACxB;AAAA,IACF;AAAA,EACF;AAAA,EACA,UAAU,CAAC,QAAQ,OAAO;AAAA,EAC1B,sBAAsB;AACxB;AAWO,IAAM,gBAAsB;AAAA,EACjC,MAAM;AAAA,EACN,aACE;AAAA,EACF,YAAY;AAAA,EACZ,UAAU;AAAA,EAEV,MAAM,QAAQ,MAAe,KAAuC;AAClE,UAAM,SAAS;AACf,QAAI,CAAC,QAAQ,QAAQ,OAAO,OAAO,SAAS,UAAU;AACpD,aAAO,EAAE,SAAS,OAAO,MAAM,6CAAe,OAAO,eAAe;AAAA,IACtE;AACA,QAAI,CAAC,MAAM,QAAQ,OAAO,KAAK,KAAK,OAAO,MAAM,WAAW,GAAG;AAC7D,aAAO,EAAE,SAAS,OAAO,MAAM,kFAAsB,OAAO,eAAe;AAAA,IAC7E;AAEA,UAAM,WAAW,YAAY,OAAO,MAAM,IAAI,GAAG;AAGjD,QAAI,IAAI,cAAc,IAAI,WAAW,SAAS,GAAG;AAC/C,YAAM,OAAO,MAAM,QAAQ,IAAI,YAAY,QAAQ;AACnD,UAAI,CAAC,KAAK,IAAI;AACZ,eAAO,EAAE,SAAS,OAAO,MAAM,KAAK,OAAO,OAAO,sBAAsB;AAAA,MAC1E;AAAA,IACF;AAEA,QAAI;AACF,YAAM,kBAAkB,MAAMC,UAAS,UAAU,OAAO;AACxD,UAAI,iBAAiB;AAGrB,eAAS,MAAM,GAAG,MAAM,OAAO,MAAM,QAAQ,OAAO;AAClD,cAAM,OAAO,OAAO,MAAM,GAAG;AAC7B,YAAI,OAAO,KAAK,YAAY,UAAU;AACpC,iBAAO;AAAA,YACL,SAAS;AAAA,YACT,MAAM,UAAK,MAAM,CAAC;AAAA,YAClB,OAAO;AAAA,UACT;AAAA,QACF;AAEA,YAAI,KAAK,YAAY;AAEnB,cAAI,QAAQ;AACZ,cAAI,MAAM;AACV,kBAAQ,MAAM,eAAe,QAAQ,KAAK,SAAS,MAAM,CAAC,OAAO,IAAI;AACnE;AAAA,UACF;AAEA,cAAI,UAAU,GAAG;AACf,mBAAO;AAAA,cACL,SAAS;AAAA,cACT,MAAM,UAAK,MAAM,CAAC;AAAA,cAClB,OAAO;AAAA,YACT;AAAA,UACF;AAEA,2BAAiB,eAAe,MAAM,KAAK,OAAO,EAAE,KAAK,KAAK,OAAO;AAAA,QACvE,OAAO;AAEL,gBAAM,WAAW,eAAe,QAAQ,KAAK,OAAO;AACpD,cAAI,aAAa,IAAI;AACnB,mBAAO;AAAA,cACL,SAAS;AAAA,cACT,MAAM,UAAK,MAAM,CAAC;AAAA,cAClB,OAAO;AAAA,YACT;AAAA,UACF;AAEA,gBAAM,YAAY,eAAe,QAAQ,KAAK,SAAS,WAAW,CAAC;AACnE,cAAI,cAAc,IAAI;AACpB,mBAAO;AAAA,cACL,SAAS;AAAA,cACT,MAAM,UAAK,MAAM,CAAC;AAAA,cAClB,OAAO;AAAA,YACT;AAAA,UACF;AAEA,2BAAiB,eAAe,QAAQ,KAAK,SAAS,KAAK,OAAO;AAAA,QACpE;AAAA,MACF;AAGA,YAAM,iBAAiB,UAAU,iBAAiB,cAAc;AAGhE,YAAM,OAAO,gBAAgB,iBAAiB,gBAAgB,QAAQ;AACtE,WAAK,gBAAgB;AAErB,aAAO;AAAA,QACL,SAAS;AAAA,QACT,MAAM,uCAAS,QAAQ;AAAA,qBAAS,OAAO,MAAM,MAAM;AAAA,qBAAa,KAAK,SAAS,KAAK,KAAK,SAAS;AAAA,QACjG,SAAS,2BAAUC,UAAS,QAAQ,CAAC,KAAK,OAAO,MAAM,MAAM,aAAQ,KAAK,SAAS,KAAK,KAAK,SAAS;AAAA,QACtG;AAAA,MACF;AAAA,IACF,SAAS,KAAc;AACrB,YAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,aAAO;AAAA,QACL,SAAS;AAAA,QACT,MAAM,6CAAU,OAAO;AAAA,QACvB,OAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AACF;;;ACvKA,SAAS,YAAAC,iBAAgB;AACzB,SAAS,YAAAC,iBAAgB;AAmBzB,IAAM,oBAAgC;AAAA,EACpC,MAAM;AAAA,EACN,YAAY;AAAA,IACV,MAAM;AAAA,MACJ,MAAM;AAAA,MACN,aAAa;AAAA,IACf;AAAA,IACA,aAAa;AAAA,MACX,MAAM;AAAA,MACN,aAAa;AAAA,IACf;AAAA,IACA,WAAW;AAAA,MACT,MAAM;AAAA,MACN,aAAa;AAAA,IACf;AAAA,IACA,WAAW;AAAA,MACT,MAAM;AAAA,MACN,aAAa;AAAA,IACf;AAAA,EACF;AAAA,EACA,UAAU,CAAC,QAAQ,eAAe,WAAW;AAAA,EAC7C,sBAAsB;AACxB;AAQA,SAAS,eAAe,OAAiB,QAAgB,OAAqD;AAC5G,QAAM,UAAoB,CAAC;AAC3B,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,QAAK,MAAM,CAAC,MAAiB,QAAQ;AACnC,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF;AAEA,MAAI,QAAQ,WAAW,GAAG;AACxB,WAAO,EAAE,OAAO,uBAAQ,KAAK,mFAAkB;AAAA,EACjD;AACA,MAAI,QAAQ,SAAS,GAAG;AACtB,WAAO,EAAE,OAAO,IAAI,KAAK,4DAAe,QAAQ,MAAM,4EAAgB;AAAA,EACxE;AAEA,SAAO,EAAE,MAAM,QAAQ,CAAC,EAAY;AACtC;AAWO,IAAM,kBAAwB;AAAA,EACnC,MAAM;AAAA,EACN,aACE;AAAA,EACF,YAAY;AAAA,EACZ,UAAU;AAAA,EAEV,MAAM,QAAQ,MAAe,KAAuC;AAClE,UAAM,SAAS;AACf,QAAI,CAAC,QAAQ,QAAQ,OAAO,OAAO,SAAS,UAAU;AACpD,aAAO,EAAE,SAAS,OAAO,MAAM,6CAAe,OAAO,eAAe;AAAA,IACtE;AACA,QAAI,OAAO,OAAO,gBAAgB,UAAU;AAC1C,aAAO,EAAE,SAAS,OAAO,MAAM,oDAAsB,OAAO,eAAe;AAAA,IAC7E;AACA,QAAI,OAAO,OAAO,cAAc,UAAU;AACxC,aAAO,EAAE,SAAS,OAAO,MAAM,kDAAoB,OAAO,eAAe;AAAA,IAC3E;AAEA,UAAM,WAAW,YAAY,OAAO,MAAM,IAAI,GAAG;AACjD,UAAM,YAAY,OAAO,aAAa;AAGtC,QAAI,IAAI,cAAc,IAAI,WAAW,SAAS,GAAG;AAC/C,YAAM,OAAO,MAAM,QAAQ,IAAI,YAAY,QAAQ;AACnD,UAAI,CAAC,KAAK,IAAI;AACZ,eAAO,EAAE,SAAS,OAAO,MAAM,KAAK,OAAO,OAAO,sBAAsB;AAAA,MAC1E;AAAA,IACF;AAEA,QAAI;AACF,YAAM,UAAU,MAAMC,UAAS,UAAU,OAAO;AAChD,YAAM,QAAQ,QAAQ,MAAM,IAAI;AAGhC,YAAM,cAAc,eAAe,OAAO,OAAO,aAAa,cAAc;AAC5E,UAAI,WAAW,aAAa;AAC1B,eAAO,EAAE,SAAS,OAAO,MAAM,YAAY,OAAO,OAAO,mBAAmB;AAAA,MAC9E;AAEA,YAAM,YAAY,eAAe,OAAO,OAAO,WAAW,YAAY;AACtE,UAAI,WAAW,WAAW;AACxB,eAAO,EAAE,SAAS,OAAO,MAAM,UAAU,OAAO,OAAO,mBAAmB;AAAA,MAC5E;AAEA,YAAM,YAAY,YAAY;AAC9B,YAAM,UAAU,UAAU;AAG1B,UAAI,aAAa,SAAS;AACxB,eAAO;AAAA,UACL,SAAS;AAAA,UACT,MAAM;AAAA,UACN,OAAO;AAAA,QACT;AAAA,MACF;AAGA,YAAM,aAAa,YAAY,YAAY,YAAY;AACvD,YAAM,WAAW,YAAY,UAAU,UAAU;AAEjD,UAAI,aAAa,UAAU;AACzB,eAAO;AAAA,UACL,SAAS;AAAA,UACT,MAAM;AAAA,UACN,OAAO;AAAA,QACT;AAAA,MACF;AAGA,YAAM,WAAW,CAAC,GAAG,MAAM,MAAM,GAAG,UAAU,GAAG,GAAG,MAAM,MAAM,WAAW,CAAC,CAAC;AAC7E,YAAM,aAAa,SAAS,KAAK,IAAI;AAErC,YAAM,iBAAiB,UAAU,SAAS,UAAU;AAEpD,YAAM,OAAO,gBAAgB,SAAS,YAAY,QAAQ;AAC1D,WAAK,gBAAgB;AAErB,YAAM,eAAe,WAAW,aAAa;AAE7C,YAAM,UAAU,2BAAUC,UAAS,QAAQ,CAAC,YAAO,YAAY,aAAQ,KAAK,SAAS,KAAK,KAAK,SAAS;AAExG,aAAO;AAAA,QACL,SAAS;AAAA,QACT,MAAM,uCAAS,QAAQ;AAAA,6CAAa,aAAa,CAAC,oBAAU,WAAW,CAAC,uBAAQ,YAAY;AAAA,qBAAY,KAAK,SAAS,KAAK,KAAK,SAAS;AAAA,QACzI;AAAA,QACA;AAAA,MACF;AAAA,IACF,SAAS,KAAc;AACrB,YAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,aAAO;AAAA,QACL,SAAS;AAAA,QACT,MAAM,6CAAU,OAAO;AAAA,QACvB,OAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AACF;;;ACjLA,OAAOC,cAAa;AAKpB,IAAMC,aAAYC,SAAQ,aAAa;AAWvC,IAAM,aAAyB;AAAA,EAC7B,MAAM;AAAA,EACN,YAAY;AAAA,IACV,SAAS;AAAA,MACP,MAAM;AAAA,MACN,aAAa;AAAA,IACf;AAAA,IACA,SAAS;AAAA,MACP,MAAM;AAAA,MACN,aAAa;AAAA,IACf;AAAA,EACF;AAAA,EACA,UAAU,CAAC,SAAS;AAAA,EACpB,sBAAsB;AACxB;AAYO,IAAM,WAAiB;AAAA,EAC5B,MAAM;AAAA,EACN,aACE;AAAA,EACF,YAAY;AAAA,EACZ,UAAU;AAAA;AAAA,EAEV,MAAM,QAAQ,MAAe,KAAuC;AAClE,UAAM,SAAS;AACf,QAAI,CAAC,QAAQ,WAAW,OAAO,OAAO,YAAY,UAAU;AAC1D,aAAO,EAAE,SAAS,OAAO,MAAM,gDAAkB,OAAO,eAAe;AAAA,IACzE;AAEA,UAAM,UAAU,OAAO,WAAW,IAAI,WAAW,kBAAkB;AAEnE,QAAI;AAGF,YAAM,eAAeD,aAAY,QAAQ;AACzC,YAAM,YAAYA,aAAY,CAAC,MAAM,OAAO,OAAO,IAAI,CAAC,MAAM,OAAO,OAAO;AAE5E,YAAM,SAAS,MAAM;AAAA,QACnB;AAAA,QACA;AAAA,QACA,IAAI;AAAA,QACJ;AAAA,QACA,IAAI;AAAA,QACJ;AAAA;AAAA,MACF;AAGA,YAAM,QAAkB,CAAC;AACzB,UAAI,OAAO,QAAQ;AACjB,cAAM,KAAK,eAAe,OAAO,MAAM,CAAC;AAAA,MAC1C;AACA,UAAI,OAAO,QAAQ;AACjB,cAAM,KAAK;AAAA,EAAa,eAAe,OAAO,MAAM,CAAC,EAAE;AAAA,MACzD;AAEA,YAAM,UAAU,OAAO,aAAa;AACpC,YAAM,SAAS,MAAM,SAAS,IAAI,MAAM,KAAK,IAAI,IAAI;AAGrD,YAAM,aAAa,OAAO,QAAQ,SAAS,KACvC,OAAO,QAAQ,MAAM,GAAG,EAAE,IAAI,QAC9B,OAAO;AACX,YAAM,UAAU,eAAQ,UAAU,cAAS,OAAO,YAAY,cAAI;AAElE,aAAO;AAAA,QACL;AAAA,QACA,MAAM,GAAG,MAAM;AAAA,uBAAW,OAAO,YAAY,cAAI;AAAA,QACjD;AAAA,QACA,OAAO,UAAU,SAAY,aAAa,OAAO,YAAY,SAAS;AAAA,MACxE;AAAA,IACF,SAAS,KAAc;AACrB,YAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,aAAO;AAAA,QACL,SAAS;AAAA,QACT,MAAM,6CAAU,OAAO;AAAA,QACvB,OAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AACF;;;ACzGA,SAAS,WAAAE,UAAS,QAAAC,aAAY;AAC9B,SAAS,QAAAC,OAAM,YAAAC,WAAU,cAAAC,mBAAkB;AAa3C,IAAM,aAAyB;AAAA,EAC7B,MAAM;AAAA,EACN,YAAY;AAAA,IACV,SAAS;AAAA,MACP,MAAM;AAAA,MACN,aAAa;AAAA,IACf;AAAA,IACA,WAAW;AAAA,MACT,MAAM;AAAA,MACN,aAAa;AAAA,IACf;AAAA,EACF;AAAA,EACA,UAAU,CAAC,SAAS;AAAA,EACpB,sBAAsB;AACxB;AAYA,SAAS,YAAY,SAAyB;AAC5C,MAAI,WAAW;AAGf,aAAW,SAAS,QAAQ,WAAW,oBAAoB;AAC3D,aAAW,SAAS,QAAQ,SAAS,cAAc;AAEnD,aAAW,SAAS,QAAQ,OAAO,cAAc;AAEjD,aAAW,SAAS,QAAQ,qBAAqB,MAAM;AAEvD,aAAW,SAAS,QAAQ,OAAO,OAAO;AAC1C,aAAW,SAAS,QAAQ,uBAAuB,QAAQ;AAC3D,aAAW,SAAS,QAAQ,iBAAiB,IAAI;AACjD,aAAW,SAAS,QAAQ,iBAAiB,MAAM;AACnD,SAAO,IAAI,OAAO,IAAI,QAAQ,KAAK,GAAG;AACxC;AAKA,eAAe,QAAQ,KAAa,SAAoC;AACtE,QAAM,UAAoB,CAAC;AAC3B,MAAI;AAEJ,MAAI;AACF,cAAU,MAAMC,SAAQ,KAAK,EAAE,eAAe,KAAK,CAAC;AAAA,EACtD,QAAQ;AACN,WAAO;AAAA,EACT;AAEA,aAAW,SAAS,SAAS;AAE3B,QAAI,MAAM,YAAY,MAAM,MAAM,SAAS,kBAAkB,MAAM,SAAS,SAAS;AACnF;AAAA,IACF;AAEA,UAAM,WAAWC,MAAK,KAAK,MAAM,IAAI;AACrC,UAAM,UAAUC,UAAS,SAAS,QAAQ;AAE1C,QAAI,MAAM,YAAY,GAAG;AACvB,cAAQ,KAAK,GAAG,MAAM,QAAQ,UAAU,OAAO,CAAC;AAAA,IAClD,OAAO;AACL,cAAQ,KAAK,OAAO;AAAA,IACtB;AAAA,EACF;AAEA,SAAO;AACT;AAUO,IAAM,WAAiB;AAAA,EAC5B,MAAM;AAAA,EACN,aACE;AAAA,EACF,YAAY;AAAA,EACZ,UAAU;AAAA,EAEV,MAAM,QAAQ,MAAe,KAAuC;AAClE,UAAM,SAAS;AACf,QAAI,CAAC,QAAQ,WAAW,OAAO,OAAO,YAAY,UAAU;AAC1D,aAAO,EAAE,SAAS,OAAO,MAAM,gDAAkB,OAAO,eAAe;AAAA,IACzE;AAEA,UAAM,YAAY,OAAO,YACpBC,YAAW,OAAO,SAAS,IAAI,OAAO,YAAYF,MAAK,IAAI,KAAK,OAAO,SAAS,IACjF,IAAI;AACR,UAAM,QAAQ,YAAY,OAAO,OAAO;AAExC,QAAI;AAEF,YAAM,UAAU,MAAMG,MAAK,SAAS;AACpC,UAAI,CAAC,QAAQ,YAAY,GAAG;AAC1B,eAAO,EAAE,SAAS,OAAO,MAAM,6CAAU,SAAS,IAAI,OAAO,gBAAgB;AAAA,MAC/E;AAEA,YAAM,WAAW,MAAM,QAAQ,WAAW,SAAS;AACnD,YAAM,UAAU,SAAS,OAAO,CAAC,MAAM,MAAM,KAAK,CAAC,CAAC;AAEpD,UAAI,QAAQ,WAAW,GAAG;AACxB,eAAO;AAAA,UACL,SAAS;AAAA,UACT,MAAM,mCAAU,OAAO,OAAO;AAAA,UAC9B,SAAS,GAAG,OAAO,OAAO;AAAA,QAC5B;AAAA,MACF;AAGA,YAAM,QAAQ;AACd,YAAM,UAAU,QAAQ,MAAM,GAAG,KAAK;AACtC,YAAM,SAAS,QAAQ,KAAK,IAAI;AAChC,YAAM,SAAS,QAAQ,SAAS,QAAQ;AAAA;AAAA,aAAa,QAAQ,MAAM,qDAAa,KAAK,YAAO;AAE5F,aAAO;AAAA,QACL,SAAS;AAAA,QACT,MAAM,eAAe,SAAS,MAAM;AAAA,QACpC,SAAS,GAAG,OAAO,OAAO,WAAM,QAAQ,MAAM;AAAA,MAChD;AAAA,IACF,SAAS,KAAc;AACrB,YAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,aAAO;AAAA,QACL,SAAS;AAAA,QACT,MAAM,6CAAU,OAAO;AAAA,QACvB,OAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AACF;;;AC1JA,SAAS,WAAAC,UAAS,YAAAC,WAAU,QAAAC,aAAY;AACxC,SAAS,QAAAC,OAAM,YAAAC,WAAU,cAAAC,mBAAkB;AAmB3C,IAAM,aAAyB;AAAA,EAC7B,MAAM;AAAA,EACN,YAAY;AAAA,IACV,SAAS;AAAA,MACP,MAAM;AAAA,MACN,aAAa;AAAA,IACf;AAAA,IACA,WAAW;AAAA,MACT,MAAM;AAAA,MACN,aAAa;AAAA,IACf;AAAA,IACA,SAAS;AAAA,MACP,MAAM;AAAA,MACN,aAAa;AAAA,IACf;AAAA,IACA,gBAAgB;AAAA,MACd,MAAM;AAAA,MACN,aAAa;AAAA,IACf;AAAA,IACA,WAAW;AAAA,MACT,MAAM;AAAA,MACN,aAAa;AAAA,IACf;AAAA,EACF;AAAA,EACA,UAAU,CAAC,SAAS;AAAA,EACpB,sBAAsB;AACxB;AAeA,eAAe,aACb,KACA,SACA,WACA,WAAW,KACQ;AACnB,QAAM,UAAoB,CAAC;AAC3B,MAAI;AAEJ,MAAI;AACF,cAAU,MAAMC,SAAQ,KAAK,EAAE,eAAe,KAAK,CAAC;AAAA,EACtD,QAAQ;AACN,WAAO;AAAA,EACT;AAEA,aAAW,SAAS,SAAS;AAC3B,QAAI,QAAQ,UAAU,SAAU;AAGhC,QAAI,MAAM,YAAY,MAAM,MAAM,SAAS,kBAAkB,MAAM,SAAS,UAAU,MAAM,SAAS,SAAS;AAC5G;AAAA,IACF;AAEA,UAAM,WAAWC,MAAK,KAAK,MAAM,IAAI;AAErC,QAAI,MAAM,YAAY,GAAG;AACvB,cAAQ,KAAK,GAAG,MAAM,aAAa,UAAU,SAAS,WAAW,WAAW,QAAQ,MAAM,CAAC;AAAA,IAC7F,OAAO;AAEL,UAAI,aAAa,CAAC,MAAM,KAAK,SAAS,IAAI,SAAS,EAAE,GAAG;AACtD;AAAA,MACF;AACA,cAAQ,KAAK,QAAQ;AAAA,IACvB;AAAA,EACF;AAEA,SAAO;AACT;AAYO,IAAM,WAAiB;AAAA,EAC5B,MAAM;AAAA,EACN,aACE;AAAA,EACF,YAAY;AAAA,EACZ,UAAU;AAAA,EAEV,MAAM,QAAQ,MAAe,KAAuC;AAClE,UAAM,SAAS;AACf,QAAI,CAAC,QAAQ,WAAW,OAAO,OAAO,YAAY,UAAU;AAC1D,aAAO,EAAE,SAAS,OAAO,MAAM,gDAAkB,OAAO,eAAe;AAAA,IACzE;AAEA,UAAM,YAAY,OAAO,YACpBC,YAAW,OAAO,SAAS,IAAI,OAAO,YAAYD,MAAK,IAAI,KAAK,OAAO,SAAS,IACjF,IAAI;AACR,UAAM,WAAW,OAAO,aAAa;AAErC,QAAI;AAEF,YAAM,QAAQ,OAAO,iBAAiB,MAAM;AAC5C,YAAM,QAAQ,IAAI,OAAO,OAAO,SAAS,KAAK;AAG9C,YAAM,UAAU,MAAME,MAAK,SAAS;AACpC,UAAI,CAAC,QAAQ,YAAY,GAAG;AAC1B,eAAO,EAAE,SAAS,OAAO,MAAM,6CAAU,SAAS,IAAI,OAAO,gBAAgB;AAAA,MAC/E;AAGA,YAAM,QAAQ,MAAM,aAAa,WAAW,WAAW,OAAO,SAAS,QAAQ;AAC/E,YAAM,UAAuB,CAAC;AAE9B,iBAAW,YAAY,OAAO;AAC5B,YAAI;AACF,gBAAM,UAAU,MAAMC,UAAS,UAAU,OAAO;AAChD,gBAAM,QAAQ,QAAQ,MAAM,IAAI;AAChC,gBAAM,UAAUC,UAAS,WAAW,QAAQ;AAE5C,mBAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,gBAAI,MAAM,KAAK,MAAM,CAAC,CAAE,GAAG;AAEzB,oBAAM,YAAY;AAClB,sBAAQ,KAAK;AAAA,gBACX,MAAM;AAAA,gBACN,MAAM,IAAI;AAAA,gBACV,SAAS,MAAM,CAAC;AAAA,cAClB,CAAC;AACD,kBAAI,QAAQ,UAAU,IAAK;AAAA,YAC7B;AAAA,UACF;AAEA,cAAI,QAAQ,UAAU,IAAK;AAAA,QAC7B,QAAQ;AAEN;AAAA,QACF;AAAA,MACF;AAEA,UAAI,QAAQ,WAAW,GAAG;AACxB,eAAO;AAAA,UACL,SAAS;AAAA,UACT,MAAM,mCAAU,OAAO,OAAO;AAAA,UAC9B,SAAS,cAAO,OAAO,OAAO;AAAA,QAChC;AAAA,MACF;AAGA,YAAM,SAAS,QACZ,IAAI,CAAC,MAAM,GAAG,EAAE,IAAI,IAAI,EAAE,IAAI,KAAK,EAAE,OAAO,EAAE,EAC9C,KAAK,IAAI;AAGZ,YAAM,UAAU,IAAI,IAAI,QAAQ,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC;AAClD,YAAM,UAAU,cAAO,OAAO,OAAO,YAAO,QAAQ,MAAM,yBAAU,QAAQ,IAAI;AAEhF,aAAO;AAAA,QACL,SAAS;AAAA,QACT,MAAM,eAAe,MAAM;AAAA,QAC3B;AAAA,MACF;AAAA,IACF,SAAS,KAAc;AACrB,YAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,aAAO;AAAA,QACL,SAAS;AAAA,QACT,MAAM,6CAAU,OAAO;AAAA,QACvB,OAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AACF;;;ACxMA,SAAS,WAAAC,UAAS,QAAAC,aAAY;AAC9B,SAAS,QAAAC,OAAM,YAAAC,iBAAgB;AAgB/B,IAAM,WAAuB;AAAA,EAC3B,MAAM;AAAA,EACN,YAAY;AAAA,IACV,MAAM;AAAA,MACJ,MAAM;AAAA,MACN,aAAa;AAAA,IACf;AAAA,IACA,KAAK;AAAA,MACH,MAAM;AAAA,MACN,aAAa;AAAA,IACf;AAAA,EACF;AAAA,EACA,UAAU,CAAC;AAAA,EACX,sBAAsB;AACxB;AAWO,IAAM,SAAe;AAAA,EAC1B,MAAM;AAAA,EACN,aACE;AAAA,EACF,YAAY;AAAA,EACZ,UAAU;AAAA,EAEV,MAAM,QAAQ,MAAe,KAAuC;AAClE,UAAM,SAAU,QAAQ,CAAC;AACzB,UAAM,UAAU,OAAO,OAAO,YAAY,OAAO,MAAM,IAAI,GAAG,IAAI,IAAI;AACtE,UAAM,UAAU,OAAO,OAAO;AAE9B,QAAI;AACF,YAAM,UAAU,MAAMC,SAAQ,SAAS,EAAE,eAAe,KAAK,CAAC;AAC9D,YAAM,QAAkB,CAAC;AAGzB,YAAM,SAAS,CAAC,GAAG,OAAO,EAAE,KAAK,CAAC,GAAG,MAAM;AACzC,YAAI,EAAE,YAAY,MAAM,EAAE,YAAY,GAAG;AACvC,iBAAO,EAAE,YAAY,IAAI,KAAK;AAAA,QAChC;AACA,eAAO,EAAE,KAAK,cAAc,EAAE,IAAI;AAAA,MACpC,CAAC;AAED,iBAAW,SAAS,QAAQ;AAE1B,YAAI,CAAC,WAAW,MAAM,KAAK,WAAW,GAAG,EAAG;AAE5C,cAAM,WAAsB,MAAM,YAAY,IAC1C,QACA,MAAM,eAAe,IACnB,SACA;AAEN,YAAI,UAAU;AACd,YAAI,aAAa,QAAQ;AACvB,cAAI;AACF,kBAAM,WAAW,MAAMC,MAAKC,MAAK,SAAS,MAAM,IAAI,CAAC;AACrD,gBAAI,SAAS,OAAO,MAAM;AACxB,wBAAU,GAAG,SAAS,IAAI;AAAA,YAC5B,WAAW,SAAS,OAAO,OAAO,MAAM;AACtC,wBAAU,IAAI,SAAS,OAAO,MAAM,QAAQ,CAAC,CAAC;AAAA,YAChD,OAAO;AACL,wBAAU,IAAI,SAAS,OAAO,OAAO,MAAM,QAAQ,CAAC,CAAC;AAAA,YACvD;AAAA,UACF,QAAQ;AACN,sBAAU;AAAA,UACZ;AAAA,QACF;AAEA,cAAM,YAAY,aAAa,QAAQ,cAAO,aAAa,SAAS,cAAO;AAC3E,cAAM,KAAK,GAAG,SAAS,IAAI,MAAM,IAAI,GAAG,UAAU,KAAK,OAAO,MAAM,EAAE,EAAE;AAAA,MAC1E;AAEA,UAAI,MAAM,WAAW,GAAG;AACtB,eAAO,EAAE,SAAS,MAAM,MAAM,4BAAQ,SAAS,aAAMC,UAAS,IAAI,KAAK,OAAO,EAAE,QAAQ,OAAO,GAAG,CAAC,qBAAM;AAAA,MAC3G;AAEA,YAAM,UAAUA,UAAS,IAAI,KAAK,OAAO,EAAE,QAAQ,OAAO,GAAG;AAC7D,aAAO;AAAA,QACL,SAAS;AAAA,QACT,MAAM,eAAe,qBAAM,OAAO;AAAA,EAAK,MAAM,KAAK,IAAI,CAAC,EAAE;AAAA,QACzD,SAAS,aAAM,OAAO,SAAI,MAAM,MAAM;AAAA,MACxC;AAAA,IACF,SAAS,KAAc;AACrB,YAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,aAAO;AAAA,QACL,SAAS;AAAA,QACT,MAAM,uCAAS,OAAO;AAAA,QACtB,OAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AACF;;;ACjGA,IAAM,cAA0B;AAAA,EAC9B,MAAM;AAAA,EACN,YAAY;AAAA,IACV,KAAK;AAAA,MACH,MAAM;AAAA,MACN,aAAa;AAAA,IACf;AAAA,IACA,QAAQ;AAAA,MACN,MAAM;AAAA,MACN,aAAa;AAAA,IACf;AAAA,IACA,SAAS;AAAA,MACP,MAAM;AAAA,MACN,aAAa;AAAA,MACb,sBAAsB,EAAE,MAAM,SAAS;AAAA,IACzC;AAAA,IACA,MAAM;AAAA,MACJ,MAAM;AAAA,MACN,aAAa;AAAA,IACf;AAAA,IACA,YAAY;AAAA,MACV,MAAM;AAAA,MACN,aAAa;AAAA,IACf;AAAA,EACF;AAAA,EACA,UAAU,CAAC,KAAK;AAAA,EAChB,sBAAsB;AACxB;AAYO,IAAM,YAAkB;AAAA,EAC7B,MAAM;AAAA,EACN,aACE;AAAA,EACF,YAAY;AAAA,EACZ,UAAU;AAAA,EAEV,MAAM,QAAQ,MAAe,KAAuC;AAClE,UAAM,SAAS;AACf,QAAI,CAAC,QAAQ,OAAO,OAAO,OAAO,QAAQ,UAAU;AAClD,aAAO,EAAE,SAAS,OAAO,MAAM,4CAAc,OAAO,eAAe;AAAA,IACrE;AAEA,UAAM,UAAU,OAAO,UAAU,OAAO,YAAY;AACpD,UAAM,UAAU;AAChB,UAAM,YAAY,OAAO,cAAc;AAEvC,QAAI;AAEF,YAAM,eAA4B;AAAA,QAChC;AAAA,QACA,SAAS,OAAO,WAAW,CAAC;AAAA,QAC5B,QAAQ,IAAI,UAAU,YAAY,QAAQ,OAAO;AAAA,MACnD;AAEA,UAAI,OAAO,SAAS,WAAW,UAAU,WAAW,SAAS,WAAW,UAAU;AAChF,QAAC,aAAa,QAAmC,cAAc,MAAM;AACrE,qBAAa,OAAO,OAAO;AAAA,MAC7B;AAEA,YAAM,WAAW,MAAM,MAAM,OAAO,KAAK,YAAY;AAGrD,YAAM,cAAc,SAAS,QAAQ,IAAI,cAAc,KAAK;AAC5D,YAAM,aAAa,GAAG,SAAS,MAAM,IAAI,SAAS,UAAU;AAG5D,UAAI;AACJ,UAAI;AACF,eAAO,MAAM,SAAS,KAAK;AAAA,MAC7B,QAAQ;AACN,eAAO;AAAA,MACT;AAGA,YAAM,gBAAgB,eAAe,MAAM,SAAS;AAEpD,YAAM,SAAS,iBAAO,UAAU;AAAA,4BAAW,WAAW;AACtD,YAAM,YAAY,KAAK,SAAS,IAAI,YAAY;AAGhD,YAAM,aAAa,OAAO,IAAI,SAAS,KACnC,OAAO,IAAI,MAAM,GAAG,EAAE,IAAI,QAC1B,OAAO;AACX,YAAM,UAAU,aAAM,MAAM,IAAI,UAAU,WAAM,SAAS,MAAM;AAE/D,aAAO;AAAA,QACL,SAAS,SAAS;AAAA,QAClB,MAAM,GAAG,MAAM,GAAG,SAAS,GAAG,aAAa;AAAA,QAC3C;AAAA,QACA,OAAO,SAAS,KAAK,SAAY,QAAQ,SAAS,MAAM;AAAA,MAC1D;AAAA,IACF,SAAS,KAAc;AACrB,UAAI,eAAe,gBAAgB,IAAI,SAAS,cAAc;AAC5D,eAAO,EAAE,SAAS,OAAO,MAAM,kCAAS,OAAO,UAAU;AAAA,MAC3D;AACA,UAAI,eAAe,aAAa,IAAI,QAAQ,SAAS,OAAO,GAAG;AAC7D,eAAO,EAAE,SAAS,OAAO,MAAM,iCAAQ,IAAI,OAAO,IAAI,OAAO,gBAAgB;AAAA,MAC/E;AACA,YAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,aAAO;AAAA,QACL,SAAS;AAAA,QACT,MAAM,sCAAa,OAAO;AAAA,QAC1B,OAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AACF;;;ACzHO,IAAM,eAAuB;AAAA,EAClC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;;;AClBO,IAAM,sBAAmC;AAAA,EAC9C,CAAC,GAAG,KAAK,EAAE;AAAA;AAAA,EACX,CAAC,GAAG,KAAK,GAAG;AAAA;AAAA,EACZ,CAAC,KAAK,KAAK,GAAG;AAAA;AAChB;AAGO,IAAM,yBAAsC;AAAA,EACjD,CAAC,KAAK,KAAK,GAAG;AAAA;AAAA,EACd,CAAC,KAAK,KAAK,EAAE;AAAA;AAAA,EACb,CAAC,KAAK,KAAK,EAAE;AAAA;AACf;AAGO,IAAM,2BAAwC;AAAA,EACnD,CAAC,KAAK,KAAK,CAAC;AAAA;AAAA,EACZ,CAAC,KAAK,KAAK,EAAE;AAAA;AAAA,EACb,CAAC,KAAK,IAAI,EAAE;AAAA;AACd;AAKO,IAAM,qBAAqB;AAAA;AAAA,EAEhC,eAAe;AAAA;AAAA,EAEf,cAAc;AAAA;AAAA,EAEd,oBAAoB;AAAA;AAAA,EAEpB,mBAAmB;AAAA;AAAA,EAEnB,iBAAiB;AAAA;AAAA,EAEjB,gBAAgB;AAClB;AAKO,IAAM,aAAa,oBAAI,IAAI,CAAC,KAAK,KAAK,KAAK,UAAK,UAAK,WAAI,CAAC;AAK1D,SAAS,eAAe,GAAW,OAAqC;AAC7E,QAAM,MAAM,MAAM;AAClB,QAAM,UAAU,KAAK,MAAM;AAC3B,QAAM,MAAM,KAAK,IAAI,KAAK,MAAM,OAAO,GAAG,MAAM,CAAC;AACjD,QAAM,OAAO,UAAU;AACvB,QAAM,OAAO,MAAM,GAAG;AACtB,QAAM,WAAW,MAAM,MAAM,CAAC;AAC9B,MAAI,CAAC,QAAQ,CAAC,SAAU,QAAO;AAC/B,QAAM,IAAI,KAAK,MAAM,KAAK,CAAC,KAAK,SAAS,CAAC,IAAI,KAAK,CAAC,KAAK,IAAI;AAC7D,QAAM,IAAI,KAAK,MAAM,KAAK,CAAC,KAAK,SAAS,CAAC,IAAI,KAAK,CAAC,KAAK,IAAI;AAC7D,QAAM,IAAI,KAAK,MAAM,KAAK,CAAC,KAAK,SAAS,CAAC,IAAI,KAAK,CAAC,KAAK,IAAI;AAC7D,SAAO,IAAI,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,GAAG,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,GAAG,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC;AAChH;AAGO,SAAS,kBACd,MACA,OACA,OACU;AACV,QAAM,MAAM,KAAK;AACjB,MAAI,QAAQ,EAAG,QAAO,CAAC;AACvB,SAAO,KAAK,MAAM,EAAE,EAAE,IAAI,CAAC,IAAI,MAAM;AACnC,QAAI,WAAW,IAAI,EAAE,EAAG,QAAO;AAC/B,UAAM,KAAM,KAAK,MAAM,KAAM,SAAS;AACtC,WAAO,eAAe,GAAG,KAAK;AAAA,EAChC,CAAC;AACH;;;A7B4pBgB,SA6JR,UA7JQ,OAAAC,MAUN,QAAAC,aAVM;AAjsBhB,IAAM,kBAAkB,oBAAI,IAAyB;AAG9C,SAAS,gBAAgB,MAAc,KAAwB;AACpE,kBAAgB,IAAI,MAAM,GAAG;AAC/B;AAGA,SAAS,wBAAkD;AACzD,SAAO;AACT;AAGA,gBAAgB,SAAS,EAAE,MAAM,4BAAQ,SAAS,OAAO,EAAE,MAAM,OAAO,GAAG,CAAC;AAC5E,gBAAgB,SAAS,EAAE,MAAM,4BAAQ,SAAS,OAAO,EAAE,MAAM,OAAO,GAAG,CAAC;AAC5E,gBAAgB,SAAS;AAAA,EACvB,MAAM;AAAA,EACN,SAAS,MAAM;AACb,UAAM,WAAW,sBAAsB;AACvC,UAAM,QAAQ,CAAC,gCAAO;AACtB,eAAW,CAAC,MAAM,GAAG,KAAK,UAAU;AAClC,YAAM,KAAK,KAAK,KAAK,OAAO,EAAE,CAAC,GAAG,IAAI,IAAI,EAAE;AAAA,IAC9C;AACA,WAAO,EAAE,MAAM,QAAQ,SAAS,MAAM,KAAK,IAAI,EAAE;AAAA,EACnD;AACF,CAAC;AACD,gBAAgB,UAAU,EAAE,MAAM,wCAAU,SAAS,OAAO,EAAE,MAAM,QAAQ,GAAG,CAAC;AAChF,gBAAgB,YAAY,EAAE,MAAM,wCAAU,SAAS,OAAO,EAAE,MAAM,QAAQ,SAAS,kBAAkB,GAAG,CAAC;AAC7G,gBAAgB,UAAU,EAAE,MAAM,4BAAQ,SAAS,OAAO,EAAE,MAAM,QAAQ,SAAS,6EAAsB,GAAG,CAAC;AAC7G,gBAAgB,SAAS,EAAE,MAAM,4BAAQ,SAAS,OAAO,EAAE,MAAM,YAAY,QAAQ,OAAO,GAAG,CAAC;AAChG,gBAAgB,UAAU,EAAE,MAAM,wCAAU,SAAS,OAAO,EAAE,MAAM,YAAY,QAAQ,QAAQ,GAAG,CAAC;AAGpG,IAAM,yBAAyB;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAGA,IAAM,oBAAoB;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,SAAS,WAAc,KAAsB;AAC3C,SAAO,IAAI,KAAK,MAAM,KAAK,OAAO,IAAI,IAAI,MAAM,CAAC;AACnD;AAsCO,SAAS,YAAY;AAAA,EAC1B;AAAA,EACA,SAAS,CAAC;AAAA,EACV,QAAQ,CAAC;AAAA,EACT;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,aAAa;AAAA,EACb,QAAQ;AAAA,EACR;AAAA,EACA;AACF,GAAqB;AACnB,QAAM,YAAY,OAAO,QAAQ,OAAO,YAAY,WAAW,QAAQ,OAAO,UAAU;AACxF,QAAM,eAAe,KAAK,IAAI,YAAY,GAAG,CAAC;AAE9C,QAAM,CAAC,QAAQ,SAAS,IAAIC,UAAS,CAAC;AACtC,QAAM,CAAC,iBAAiB,kBAAkB,IAAIA,UAA2B,CAAC,CAAC;AAC3E,QAAM,CAAC,WAAW,YAAY,IAAIA,UAAS,CAAC;AAC5C,QAAM,CAAC,OAAO,QAAQ,IAAIA,UAAS,EAAE;AACrC,QAAM,CAAC,SAAS,UAAU,IAAIA,UAAwB,IAAI;AAC1D,QAAM,CAAC,gBAAgB,iBAAiB,IAAIA,UAAS,KAAK;AAC1D,QAAM,CAAC,WAAW,YAAY,IAAIA,UAAwB,IAAI;AAG9D,QAAM,CAAC,aAAa,cAAc,IAAIA,UAAS,KAAK;AACpD,QAAM,CAAC,sBAAsB,uBAAuB,IAAIA,UAAS,EAAE;AACnE,QAAM,CAAC,iBAAiB,kBAAkB,IAAIA,UAAS,MAAM,WAAW,iBAAiB,CAAC;AAC1F,QAAM,CAAC,gBAAgB,iBAAiB,IAAIA,UAAmB,CAAC,CAAC;AACjE,QAAM,mBAAmBC,QAAO,CAAC;AACjC,QAAM,CAAC,yBAAyB,0BAA0B,IAAID,UAAmB,CAAC,CAAC;AACnF,QAAM,oBAAoBC,QAAO,CAAC;AAClC,QAAM,CAAC,gBAAgB,iBAAiB,IAAID,UAAS,EAAE;AACvD,QAAM,CAAC,kBAAkB,mBAAmB,IAAIA,UAA6B,CAAC,CAAC;AAC/E,QAAM,CAAC,cAAc,eAAe,IAAIA,UAAgC,MAAS;AACjF,QAAM,CAAC,gBAAgB,iBAAiB,IAAIA,UAA6B,MAAS;AAClF,QAAM,CAAC,aAAa,cAAc,IAAIA,UAA6B,MAAS;AAC5E,QAAM,CAAC,aAAa,cAAc,IAAIA,UAAkB,KAAgB;AACxE,QAAM,CAAC,gBAAgB,iBAAiB,IAAIA,UAA6B,MAAS;AAClF,QAAM,CAAC,aAAa,cAAc,IAAIA,UAA6B,MAAS;AAG5E,QAAM,UAAU,MAAM,KAAK,sBAAsB,CAAC,EAC/C,OAAO,CAAC,CAAC,IAAI,MAAM,SAAS,WAAW,SAAS,OAAO,EACvD,IAAI,CAAC,CAAC,MAAM,GAAG,OAAO,EAAE,MAAM,MAAM,IAAI,KAAK,EAAE;AAClD,QAAM,CAAC,aAAa,cAAc,IAAIA,UAAS,CAAC;AAChD,QAAM,CAAC,sBAAsB,uBAAuB,IAAIA,UAAmB,CAAC,CAAC;AAC7E,QAAM,iBAAiBC,QAAO,CAAC;AAG/B,QAAM,CAAC,kBAAkB,mBAAmB,IAAID,UAAS,CAAC;AAE1D,QAAM,CAAC,iBAAiB,kBAAkB,IAAIA,UAAS,CAAC;AAExD,QAAM,CAAC,UAAU,WAAW,IAAIA,UAAS,CAAC;AAG1C,QAAM,CAAC,gBAAgB,iBAAiB,IAAIA,UAAS,KAAK;AAC1D,QAAM,CAAC,kBAAkB,mBAAmB,IAAIA,UAAS,CAAC;AAC1D,QAAM,eAA0B,CAAC,qBAAqB,iBAAiB;AAGvE,QAAM,aAAaC,QAAuB,IAAI;AAC9C,QAAM,WAAWA,QAA+B,IAAI;AAGpD,QAAM,oBAAoBA,QAAO,EAAE;AACnC,QAAM,sBAAsBA,QAA2B,CAAC,CAAC;AACzD,QAAM,kBAAkBA,QAA8B,MAAS;AAC/D,QAAM,oBAAoBA,QAA2B,MAAS;AAC9D,QAAM,iBAAiBA,QAA2B,MAAS;AAC3D,QAAM,kBAAkBA,QAA2B,MAAS;AAC5D,QAAM,iBAAiBA,QAA2B,MAAS;AAG3D,EAAAC,WAAU,MAAM;AACd,wBAAoB,CAAC;AACrB,uBAAmB,CAAC;AAAA,EACtB,GAAG,CAAC,KAAK,CAAC;AAGV,QAAM,oBAAoBC;AAAA,IACxB,CAAC,UAAkB;AACjB,YAAM,QAAQ,MAAM,MAAM,oBAAoB;AAC9C,UAAI,CAAC,MAAO,QAAO,CAAC;AACpB,YAAM,IAAI,MAAM,CAAC,EAAG,YAAY,EAAE,KAAK;AACvC,UAAI,CAAC,GAAG;AACN,YAAI,MAAM,WAAW,GAAG,EAAG,QAAO,OAAO,MAAM,GAAG,CAAC;AACnD,eAAO,CAAC;AAAA,MACV;AACA,YAAM,UAAU,OAAO,OAAO,CAAC,MAAM,EAAE,KAAK,YAAY,EAAE,SAAS,CAAC,CAAC,EAAE,MAAM,GAAG,CAAC;AAEjF,UAAI,QAAQ,KAAK,CAAC,MAAM,EAAE,KAAK,YAAY,MAAM,CAAC,EAAG,QAAO,CAAC;AAC7D,aAAO;AAAA,IACT;AAAA,IACA,CAAC,MAAM;AAAA,EACT;AAGA,QAAM,mBAAmBA;AAAA,IACvB,CAAC,UAAkB;AACjB,YAAM,QAAQ,MAAM,MAAM,mBAAmB;AAC7C,UAAI,CAAC,MAAO,QAAO,CAAC;AACpB,YAAM,IAAI,MAAM,CAAC,EAAG,YAAY,EAAE,KAAK;AAEvC,UAAI,CAAC,GAAG;AACN,YAAI,MAAM,WAAW,GAAG,EAAG,QAAO,MAAM,MAAM,GAAG,CAAC;AAClD,eAAO,CAAC;AAAA,MACV;AACA,YAAM,UAAU,MAAM,OAAO,CAAC,MAAM,EAAE,YAAY,EAAE,SAAS,CAAC,CAAC,EAAE,MAAM,GAAG,CAAC;AAE3E,UAAI,QAAQ,KAAK,CAAC,MAAM,EAAE,YAAY,MAAM,CAAC,EAAG,QAAO,CAAC;AACxD,aAAO;AAAA,IACT;AAAA,IACA,CAAC,KAAK;AAAA,EACR;AAEA,QAAM,EAAE,aAAa,YAAY,IAAI,eAAe,MAAM;AAExD,YAAQ,KAAK,CAAC;AAAA,EAChB,CAAC;AAGD;AAAA,IACEA;AAAA,MACE,CAAC,QAAQ,QAAQ;AAEf,YAAI,gBAAgB;AAClB,cAAI,IAAI,SAAS;AACf,gCAAoB,CAAC,UAAU,OAAO,IAAI,aAAa,UAAU,aAAa,MAAM;AAAA,UACtF,WAAW,IAAI,WAAW;AACxB,gCAAoB,CAAC,UAAU,OAAO,KAAK,aAAa,MAAM;AAAA,UAChE,WAAW,IAAI,QAAQ;AACrB,kBAAM,WAAW,aAAa,gBAAgB;AAC9C,gBAAI,aAAa,aAAa;AAC5B,iCAAmB,CAAC,SAAS;AAAA,gBAC3B,GAAG;AAAA,gBACH,EAAE,MAAM,aAAa,SAAS,kCAAS,iBAAiB,QAAQ,EAAE,WAAW,GAAG;AAAA,cAClF,CAAC;AAAA,YACH,OAAO;AAEL,6BAAe,QAAQ;AACvB,yBAAW,SAAS,MAAM;AAC1B,8BAAgB,QAAQ,EAAE,MAAM,MAAM;AAAA,cAEtC,CAAC;AACD,iCAAmB,CAAC,SAAS;AAAA,gBAC3B,GAAG;AAAA,gBACH,EAAE,MAAM,aAAa,SAAS,wCAAU,iBAAiB,QAAQ,EAAE,WAAW,SAAI,QAAQ,SAAI;AAAA,cAChG,CAAC;AAAA,YACH;AACA,8BAAkB,KAAK;AAAA,UACzB,WAAW,IAAI,QAAQ;AACrB,8BAAkB,KAAK;AAAA,UACzB;AACA;AAAA,QACF;AAIA,cAAM,WAAW,iBAAiB,KAAK;AACvC,cAAM,YAAY,SAAS,WAAW,IAAI,kBAAkB,KAAK,IAAI,CAAC;AAEtE,YAAI,SAAS,SAAS,GAAG;AACvB,cAAI,IAAI,SAAS;AACf,+BAAmB,CAAC,UAAU,OAAO,IAAI,SAAS,UAAU,SAAS,MAAM;AAC3E;AAAA,UACF;AACA,cAAI,IAAI,WAAW;AACjB,+BAAmB,CAAC,UAAU,OAAO,KAAK,SAAS,MAAM;AACzD;AAAA,UACF;AAEA,cAAI,IAAI,KAAK;AACX,kBAAM,WAAW,SAAS,eAAe;AACzC,gBAAI,UAAU;AACZ,oBAAM,QAAQ,MAAM,YAAY,GAAG;AACnC,kBAAI,SAAS,GAAG;AACd,yBAAS,MAAM,MAAM,GAAG,KAAK,IAAI,MAAM,WAAW,GAAG;AACrD,4BAAY,CAAC,MAAM,IAAI,CAAC;AAAA,cAC1B;AAAA,YACF;AACA;AAAA,UACF;AAAA,QACF;AAEA,YAAI,UAAU,SAAS,GAAG;AACxB,cAAI,IAAI,SAAS;AACf,gCAAoB,CAAC,UAAU,OAAO,IAAI,UAAU,UAAU,UAAU,MAAM;AAC9E;AAAA,UACF;AACA,cAAI,IAAI,WAAW;AACjB,gCAAoB,CAAC,UAAU,OAAO,KAAK,UAAU,MAAM;AAC3D;AAAA,UACF;AAEA,cAAI,IAAI,KAAK;AACX,kBAAM,WAAW,UAAU,gBAAgB;AAC3C,gBAAI,UAAU;AAGZ,oBAAM,WAAW,MAAM,YAAY,GAAG;AACtC,kBAAI,YAAY,GAAG;AAEjB,yBAAS,MAAM,MAAM,GAAG,QAAQ,IAAI,MAAM,SAAS,OAAO,GAAG;AAC7D,4BAAY,CAAC,MAAM,IAAI,CAAC;AAAA,cAC1B;AAAA,YACF;AACA;AAAA,UACF;AAAA,QACF;AAEA,YAAI,IAAI,QAAQ,WAAW,KAAK;AAC9B,cAAI,aAAa;AAEf,qBAAS,SAAS,MAAM;AAAA,UAC1B,OAAO;AACL,wBAAY;AAAA,UACd;AACA;AAAA,QACF;AAGA,YAAI,CAAC,SAAS,CAAC,eAAe,QAAQ;AACpC,mBAAS,MAAM;AAAA,QACjB;AAAA,MACF;AAAA,MACA,CAAC,gBAAgB,kBAAkB,cAAc,aAAa,aAAa,aAAa,OAAO,QAAQ,kBAAkB,iBAAiB,mBAAmB,gBAAgB;AAAA,IAC/K;AAAA,EACF;AAGA,EAAAD,WAAU,MAAM;AACd,QAAI,QAAQ,UAAU,EAAG;AACzB,UAAM,QAAQ,YAAY,MAAM;AAC9B,qBAAe,CAAC,UAAU,OAAO,KAAK,QAAQ,MAAM;AAAA,IACtD,GAAG,GAAI;AACP,WAAO,MAAM,cAAc,KAAK;AAAA,EAClC,GAAG,CAAC,QAAQ,MAAM,CAAC;AAGnB,EAAAA,WAAU,MAAM;AACd,UAAM,MAAM,QAAQ,cAAc,QAAQ,MAAM;AAChD,QAAI,CAAC,KAAK;AACR,8BAAwB,CAAC,CAAC;AAC1B;AAAA,IACF;AACA,UAAM,OAAO,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI;AACpC,mBAAe,UAAU;AACzB,4BAAwB,kBAAkB,MAAM,GAAG,sBAAsB,CAAC;AAE1E,UAAM,WAAW,YAAY,MAAM;AACjC,qBAAe,WAAW,eAAe,UAAU,mBAAmB,mBAAmB;AAEzF,8BAAwB,kBAAkB,MAAM,IAAI,eAAe,SAAS,sBAAsB,CAAC;AAAA,IACrG,GAAG,mBAAmB,cAAc;AAEpC,WAAO,MAAM,cAAc,QAAQ;AAAA,EACrC,GAAG,CAAC,aAAa,QAAQ,MAAM,CAAC;AAGhC,EAAAA,WAAU,MAAM;AACd,UAAM,QAAQ,YAAY,MAAM;AAC9B,gBAAU,CAAC,UAAU,OAAO,KAAK,cAAc,MAAM;AAAA,IACvD,GAAG,GAAG;AACN,WAAO,MAAM,cAAc,KAAK;AAAA,EAClC,GAAG,CAAC,CAAC;AAGL,EAAAA,WAAU,MAAM;AACd,QAAI,CAAC,UAAU,CAAC,QAAS;AAEzB,UAAM,WAAW,eAAe;AAAA,MAC9B,MAAM;AAAA,MACN;AAAA,MACA;AAAA,MACA,OAAO;AAAA,IACT,CAAC;AAED,UAAM,UAAU,uBAAuB,IAAI,YAAY;AAEvD,UAAM,eAAe,IAAI,aAAa;AACtC,iBAAa,YAAY,YAAY;AACrC,UAAM,UAAU,IAAI,QAAQ,UAAU,cAAc,SAAS;AAAA,MAC3D,KAAK,QAAQ,IAAI;AAAA,IACnB,CAAC;AACD,eAAW,UAAU;AAErB,WAAO,MAAM;AACX,iBAAW,UAAU;AAAA,IACvB;AAAA,EACF,GAAG,CAAC,QAAQ,SAAS,aAAa,mBAAmB,CAAC;AAGtD,EAAAA,WAAU,MAAM;AACd,QAAI,CAAC,UAAU,CAAC,QAAS;AACzB,QAAI,YAAY;AAChB,sBAAkB,IAAI;AACtB,WAAO,wBAAyB,EAAE,KAAK,CAAC,EAAE,kBAAAE,kBAAiB,MAAM;AAC/D,YAAM,WAAW,IAAIA,kBAAiB;AAAA,QACpC;AAAA,QACA;AAAA,QACA,OAAO;AAAA,MACT,CAAC;AACD,aAAO,SAAS,WAAW;AAAA,IAC7B,CAAC,EAAE,KAAK,CAAC,WAAW;AAClB,UAAI,UAAW;AACf,YAAM,MAAM,OAAO,SAAS,KAAK,CAAC,MAAM,EAAE,aAAa,KAAK;AAC5D,UAAI,KAAK;AACP,mBAAW,IAAI,YAAY;AAAA,MAC7B;AAAA,IACF,CAAC,EAAE,MAAM,MAAM;AAAA,IAEf,CAAC,EAAE,QAAQ,MAAM;AACf,UAAI,CAAC,UAAW,mBAAkB,KAAK;AAAA,IACzC,CAAC;AACD,WAAO,MAAM;AAAE,kBAAY;AAAA,IAAM;AAAA,EACnC,GAAG,CAAC,QAAQ,OAAO,CAAC;AAGpB,EAAAF,WAAU,MAAM;AACd,QAAI,CAAC,oBAAqB;AAC1B,QAAI,YAAY;AAChB,QAAI;AAEJ,UAAM,UAAU,MAAM;AACpB,mBAAa,oBAAoB,cAAc;AAAA,IACjD;AAEA,wBAAoB,KAAK,EAAE,KAAK,MAAM;AACpC,UAAI,UAAW;AACf,cAAQ;AACR,cAAQ,YAAY,SAAS,GAAI;AAAA,IACnC,CAAC,EAAE,MAAM,MAAM;AAAA,IAEf,CAAC;AAED,WAAO,MAAM;AACX,kBAAY;AACZ,UAAI,MAAO,eAAc,KAAK;AAAA,IAChC;AAAA,EACF,GAAG,CAAC,mBAAmB,CAAC;AAGxB,EAAAA,WAAU,MAAM;AACd,QAAI,eAAe,CAAC,iBAAiB;AACnC,wBAAkB,CAAC,CAAC;AACpB;AAAA,IACF;AAEA,qBAAiB,UAAU;AAC3B,sBAAkB,kBAAkB,iBAAiB,GAAG,mBAAmB,CAAC;AAE5E,UAAM,WAAW,YAAY,MAAM;AACjC,uBAAiB,WAAW,iBAAiB,UAAU,mBAAmB,iBAAiB;AAC3F,wBAAkB,kBAAkB,iBAAiB,IAAI,iBAAiB,SAAS,mBAAmB,CAAC;AAAA,IACzG,GAAG,mBAAmB,YAAY;AAElC,WAAO,MAAM,cAAc,QAAQ;AAAA,EACrC,GAAG,CAAC,aAAa,eAAe,CAAC;AAGjC,EAAAA,WAAU,MAAM;AACd,QAAI,CAAC,eAAe,CAAC,sBAAsB;AACzC,iCAA2B,CAAC,CAAC;AAC7B;AAAA,IACF;AAEA,sBAAkB,UAAU;AAC5B,+BAA2B,kBAAkB,sBAAsB,GAAG,wBAAwB,CAAC;AAE/F,UAAM,WAAW,YAAY,MAAM;AACjC,wBAAkB,WAAW,kBAAkB,UAAU,mBAAmB,sBAAsB;AAClG,iCAA2B,kBAAkB,sBAAsB,IAAI,kBAAkB,SAAS,wBAAwB,CAAC;AAAA,IAC7H,GAAG,mBAAmB,iBAAiB;AAEvC,WAAO,MAAM,cAAc,QAAQ;AAAA,EACrC,GAAG,CAAC,aAAa,oBAAoB,CAAC;AAGtC,QAAM,eAAeC,aAAY,OAAO,UAAkB;AACxD,UAAM,UAAU,MAAM,KAAK;AAC3B,QAAI,CAAC,QAAS;AAGd,QAAI,QAAQ,WAAW,GAAG,KAAK,QAAQ,SAAS,GAAG;AAEjD,UAAI,QAAQ,YAAY,MAAM,UAAU;AAEtC,cAAM,SAAS,aAAa,QAAQ,WAAW;AAC/C,4BAAoB,UAAU,IAAI,SAAS,CAAC;AAC5C,0BAAkB,IAAI;AACtB,iBAAS,EAAE;AACX;AAAA,MACF;AAEA,YAAM,MAAM,gBAAgB,IAAI,QAAQ,YAAY,CAAC;AACrD,UAAI,KAAK;AACP,cAAM,SAAS,IAAI,QAAQ;AAE3B,gBAAQ,OAAO,MAAM;AAAA,UACnB,KAAK;AACH,oBAAQ,KAAK,CAAC;AACd;AAAA,UACF,KAAK;AAEH,+BAAmB,CAAC,CAAC;AACrB,yBAAa,CAAC,SAAS,OAAO,CAAC;AAC/B,qBAAS,EAAE;AACX,2BAAe,MAAS;AAExB,oBAAQ,OAAO,MAAM,sBAAsB;AAE3C,uBAAW,SAAS,MAAM;AAC1B;AAAA,UACF,KAAK;AACH,qBAAS,EAAE;AACX,gBAAI,OAAO,WAAW,QAAQ;AAC5B,6BAAe;AAAA,YACjB,WAAW,OAAO,WAAW,SAAS;AACpC,8BAAgB;AAAA,YAClB;AACA;AAAA,UACF,KAAK;AACH,+BAAmB,CAAC,SAAS;AAAA,cAC3B,GAAG;AAAA,cACH,EAAE,MAAM,QAAQ,SAAS,QAAQ;AAAA,cACjC,EAAE,MAAM,aAAa,SAAS,OAAO,QAAQ;AAAA,YAC/C,CAAC;AACD,qBAAS,EAAE;AACX;AAAA,QACJ;AAAA,MACF;AACA,yBAAmB,CAAC,SAAS;AAAA,QAC3B,GAAG;AAAA,QACH,EAAE,MAAM,QAAQ,SAAS,QAAQ;AAAA,QACjC,EAAE,MAAM,aAAa,SAAS,iCAAQ,OAAO,8CAAgB;AAAA,MAC/D,CAAC;AACD,eAAS,EAAE;AACX;AAAA,IACF;AAGA,QAAI,CAAC,WAAW,SAAS;AACvB,yBAAmB,CAAC,SAAS;AAAA,QAC3B,GAAG;AAAA,QACH,EAAE,MAAM,QAAQ,SAAS,QAAQ;AAAA,QACjC,EAAE,MAAM,aAAa,SAAS,sHAAsC;AAAA,MACtE,CAAC;AACD,eAAS,EAAE;AACX;AAAA,IACF;AAKA,uBAAmB,CAAC,SAAS;AAAA,MAC3B,GAAG;AAAA,MACH,EAAE,MAAM,QAAQ,SAAS,QAAQ;AAAA,IACnC,CAAC;AAGD,aAAS,EAAE;AAEX,mBAAe,IAAI;AACnB,4BAAwB,WAAW,sBAAsB,CAAC;AAC1D,sBAAkB,EAAE;AACpB,wBAAoB,CAAC,CAAC;AACtB,oBAAgB,MAAS;AACzB,sBAAkB,MAAS;AAC3B,mBAAe,MAAS;AACxB,sBAAkB,MAAS;AAC3B,mBAAe,MAAS;AAExB,sBAAkB,UAAU;AAC5B,wBAAoB,UAAU,CAAC;AAC/B,oBAAgB,UAAU;AAC1B,sBAAkB,UAAU;AAC5B,mBAAe,UAAU;AACzB,oBAAgB,UAAU;AAC1B,mBAAe,UAAU;AAEzB,UAAM,UAAU,WAAW;AAC3B,UAAM,kBAAkB,IAAI,gBAAgB;AAC5C,aAAS,UAAU;AAEnB,QAAI;AACF,uBAAiB,SAAS,QAAS,KAAK,OAAO,GAAG;AAEhD,YAAI,gBAAgB,OAAO,QAAS;AAEpC,gBAAQ,MAAM,MAAM;AAAA,UAClB,KAAK;AACH,8BAAkB,CAAC,SAAS;AAC1B,oBAAM,OAAO,OAAO,MAAM;AAC1B,gCAAkB,UAAU;AAC5B,qBAAO;AAAA,YACT,CAAC;AACD;AAAA,UAEF,KAAK;AACH,gCAAoB,CAAC,SAAS;AAC5B,oBAAM,OAAO,CAAC,GAAG,MAAM,GAAG,MAAM,KAAK;AACrC,kCAAoB,UAAU;AAC9B,qBAAO;AAAA,YACT,CAAC;AACD;AAAA,UAEF,KAAK;AAGH,8BAAkB,EAAE;AACpB,8BAAkB,UAAU;AAC5B,gCAAoB,CAAC,CAAC;AACtB,gCAAoB,UAAU,CAAC;AAG/B,kBAAM,IAAI,MAAM;AAChB,kBAAM,OAAO,EAAE,UACX,EAAE,WAAW,UAAK,MAAM,IAAI,KAAK,EAAE,KAAK,MAAM,GAAG,GAAG,CAAC,GAAG,EAAE,KAAK,SAAS,MAAM,QAAQ,EAAE,KACxF,UAAK,MAAM,IAAI,KAAK,EAAE,SAAS,0BAAM;AACzC,+BAAmB,CAAC,SAAS;AAAA,cAC3B,GAAG;AAAA,cACH;AAAA,gBACE,MAAM;AAAA,gBACN,SAAS;AAAA,gBACT,MAAM,EAAE;AAAA,cACV;AAAA,YACF,CAAC;AACD;AAAA,UAEF,KAAK;AACH,4BAAgB,MAAM,KAAK;AAC3B,8BAAkB,MAAM,KAAK;AAC7B,4BAAgB,UAAU,MAAM;AAChC,4BAAgB,UAAU,MAAM;AAEhC;AAAA,UAEF,KAAK;AACH,8BAAkB,MAAM,OAAO;AAC/B,8BAAkB,UAAU,MAAM;AAClC;AAAA,UAEF,KAAK;AACH,2BAAe,MAAM,MAAM,OAAO;AAClC,2BAAe,UAAU,MAAM,MAAM;AACrC;AAAA,QACJ;AAAA,MACF;AAAA,IACF,SAAS,KAAc;AACrB,YAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D,qBAAe,GAAG;AAClB,qBAAe,UAAU;AAAA,IAC3B,UAAE;AACA,qBAAe,KAAK;AACpB,yBAAmB,WAAW,iBAAiB,CAAC;AAChD,eAAS,UAAU;AAGnB,YAAM,aAAa,kBAAkB;AACrC,YAAM,eAAe,oBAAoB,QAAQ,SAAS,IAAI,oBAAoB,UAAU;AAC5F,YAAM,iBAAiB,eAAe;AAEtC,UAAI,cAAc,gBAAgB,gBAAgB;AAChD,cAAM,YAAgC;AAAA,UACpC,SAAS,iBAAiB,wCAAU,cAAc,KAAM,cAAc;AAAA,UACtE,WAAW;AAAA,UACX,OAAO,gBAAgB;AAAA,UACvB,SAAS,kBAAkB;AAAA,UAC3B,MAAM,eAAe;AAAA,UACrB,OAAO,gBAAgB;AAAA,QACzB;AACA,2BAAmB,CAAC,SAAS;AAAA,UAC3B,GAAG;AAAA,UACH;AAAA,YACE,MAAM;AAAA,YACN,SAAS,UAAU;AAAA,YACnB,iBAAiB;AAAA,UACnB;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF,GAAG,CAAC,cAAc,eAAe,gBAAgB,kBAAkB,QAAQ,kBAAkB,iBAAiB,CAAC;AAG/G,EAAAD,WAAU,MAAM;AACd,QAAI,CAAC,eAAe,qBAAqB;AACvC,mBAAa,oBAAoB,cAAc;AAAA,IACjD;AAAA,EACF,GAAG,CAAC,aAAa,mBAAmB,CAAC;AAGrC,EAAAA,WAAU,MAAM;AACd,QAAI,gBAAgB,kBAAkB,CAAC,aAAa;AAClD,aAAO,sBAAuB,EAAE,KAAK,CAAC,EAAE,eAAAG,eAAc,MAAM;AAC1D,cAAM,OAAOA,eAAc,cAAc,cAAyB;AAClE,uBAAe,KAAK,SAAS;AAC7B,uBAAe,UAAU,KAAK;AAAA,MAChC,CAAC;AAAA,IACH;AAAA,EACF,GAAG,CAAC,cAAc,gBAAgB,WAAW,CAAC;AAE9C,SACE,gBAAAN,MAACO,MAAA,EAAI,eAAc,UAAS,aAAa,GAAG,cAAc,GAExD;AAAA,oBAAAP,MAACO,MAAA,EAAI,eAAc,OAAM,cAAc,GAErC;AAAA,sBAAAR,KAACQ,MAAA,EAAI,eAAc,UAAS,aAAa,GACtC,qBAAW,IAAI,CAAC,MAAM,MAAM;AAC3B,cAAM,cAAc,IAAI,UAAU,cAAc;AAChD,eACE,gBAAAR,KAACQ,MAAA,EACC,0BAAAR,KAACS,OAAA,EAAK,MAAI,MAAC,OAAO,cAAc,UAAU,GACvC,gBACH,KAHQ,CAIV;AAAA,MAEJ,CAAC,GACH;AAAA,MAGA,gBAAAR,MAACO,MAAA,EAAI,eAAc,UAAS,gBAAe,UACzC;AAAA,wBAAAP,MAACQ,OAAA,EAAK,OAAM,WAAW;AAAA;AAAA,UAAO;AAAA,UAAK;AAAA,UAAW;AAAA,WAAQ;AAAA,QACtD,gBAAAR,MAACQ,OAAA,EAAK,OAAM,WAAW;AAAA;AAAA,UAAO;AAAA,UAAK;AAAA,UAAU;AAAA,WAAI;AAAA,QACjD,gBAAAR,MAACQ,OAAA,EAAK,OAAM,WAAW;AAAA;AAAA,UAAY,iBAAiB,WAAW,GAAG,eAAe;AAAA,WAAY;AAAA,QAE5F,QAAQ,SAAS,MAAM,MAAM;AAC5B,gBAAM,MAAM,QAAQ,cAAc,QAAQ,MAAM;AAChD,cAAI,CAAC,IAAK,QAAO;AACjB,gBAAM,OAAO,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI;AACpC,iBACE,gBAAAR,MAACQ,OAAA,EACC;AAAA,4BAAAT,KAACS,OAAA,EAAK,OAAM,WAAW,0BAAQ;AAAA,YAC9B,qBAAqB,SAAS,IAC3B,KAAK,MAAM,EAAE,EAAE,IAAI,CAAC,IAAI,MACtB,gBAAAT,KAACS,OAAA,EAAa,OAAO,qBAAqB,CAAC,KAAK,QAAY,gBAAjD,CAAoD,CAChE,IACD,gBAAAT,KAACS,OAAA,EAAK,OAAM,WAAW,gBAAK;AAAA,aAClC;AAAA,QAEJ,GAAG;AAAA,QACF,UAAU,gBAAAT,KAACS,OAAA,EAAK,OAAM,WAAW,8BAAc,IAAU;AAAA,SAC5D;AAAA,MAGA,gBAAAR,MAACO,MAAA,EAAI,UAAU,GAAG,eAAc,UAAS,gBAAe,UAAS,YAAW,YACzE;AAAA,0BAAkB,YAAY,OAC7B,gBAAAR,KAACS,OAAA,EAAK,OAAM,UAAU,kDAAc,IAClC,YAAY,OACd,gBAAAR,MAACO,MAAA,EAAI,eAAc,OACjB;AAAA,0BAAAR,KAACS,OAAA,EAAK,OAAM,UAAU,wBAAM;AAAA,UAC5B,gBAAAR,MAACQ,OAAA,EAAK,OAAM,UAAU;AAAA;AAAA,YAAQ,QAAQ,QAAQ,CAAC;AAAA,aAAE;AAAA,WACnD,IACE;AAAA,QACH,cAAc,OACb,gBAAAR,MAACO,MAAA,EAAI,eAAc,OACjB;AAAA,0BAAAR,KAACS,OAAA,EAAK,OAAM,QAAQ,wBAAM;AAAA,UAC1B,gBAAAR,MAACQ,OAAA,EAAK,OAAM,QAAQ;AAAA;AAAA,YAAQ,UAAU,QAAQ,CAAC;AAAA,aAAE;AAAA,WACnD,IACE;AAAA,SACN;AAAA,OACF;AAAA,IAGA,gBAAAR,MAACO,MAAA,EAAI,eAAc,UAAS,WAAW,GACrC;AAAA,sBAAAR,KAAC,UAAuB,OAAO,iBAC5B,WAAC,KAAK,MAAM;AACX,YAAI,IAAI,SAAS,QAAQ;AACvB,iBACE,gBAAAC,MAACO,MAAA,EAAY,WAAW,GACtB;AAAA,4BAAAR,KAACQ,MAAA,EAAI,OAAO,GAAG,YAAY,GACzB,0BAAAR,KAACS,OAAA,EAAK,MAAI,MAAC,OAAM,WAAW,uBAAK,GACnC;AAAA,YACA,gBAAAT,KAACQ,MAAA,EAAI,UAAU,GACb,0BAAAR,KAACS,OAAA,EAAK,MAAK,QAAQ,cAAI,SAAQ,GACjC;AAAA,eANQ,CAOV;AAAA,QAEJ;AAGA,YAAI,IAAI,SAAS,QAAQ;AACvB,iBACE,gBAAAR,MAACO,MAAA,EAAY,WAAW,GAAG,eAAc,UACvC;AAAA,4BAAAP,MAACO,MAAA,EAAI,eAAc,OACjB;AAAA,8BAAAR,KAACQ,MAAA,EAAI,OAAO,GAAG,YAAY,GACzB,0BAAAR,KAACS,OAAA,EAAK,MAAI,MAAC,OAAM,WAAW,uBAAK,GACnC;AAAA,cACA,gBAAAT,KAACQ,MAAA,EAAI,UAAU,GACb,0BAAAR,KAACS,OAAA,EAAK,MAAK,QAAQ,cAAI,SAAQ,GACjC;AAAA,eACF;AAAA,YACC,IAAI,QAAQ,gBAAAT,KAAC,eAAY,MAAM,IAAI,MAAM;AAAA,eATlC,CAUV;AAAA,QAEJ;AAGA,cAAM,SAAS,IAAI;AACnB,eACE,gBAAAA;AAAA,UAAC;AAAA;AAAA,YAEC,SAAS,IAAI;AAAA,YACb,WAAW,QAAQ;AAAA,YACnB,aAAa;AAAA,YACb,OAAO,QAAQ;AAAA,YACf,SAAS,QAAQ;AAAA,YACjB,MAAM,QAAQ;AAAA,YACd,OAAO,QAAQ;AAAA;AAAA,UAPV;AAAA,QAQP;AAAA,MAEJ,KA9CW,SA+Cb;AAAA,MAGC,eACC,gBAAAA;AAAA,QAAC;AAAA;AAAA,UACC,SAAS;AAAA,UACT,WAAW,iBAAiB,SAAS,IAAI,mBAAmB;AAAA,UAC5D,aAAa;AAAA;AAAA,MACf;AAAA,MAID,eAAe,CAAC,kBAAkB,iBAAiB,WAAW,KAC7D,gBAAAA,KAACQ,MAAA,EAAI,WAAW,GAAG,YAAY,GAC7B,0BAAAR,KAAC,WAAQ,MAAK,QAAO,OAAM,yBAAS,GACtC;AAAA,MAID,CAAC,eAAe,eACf,gBAAAA,KAACQ,MAAA,EAAI,WAAW,GAAG,YAAY,GAC7B,0BAAAP,MAACQ,OAAA,EAAK,OAAM,OAAM;AAAA;AAAA,QAAG;AAAA,SAAY,GACnC;AAAA,OAEJ;AAAA,IAGC,iBACC,gBAAAR,MAACO,MAAA,EAAI,WAAW,GAAG,eAAc,UAC/B;AAAA,sBAAAR,KAACS,OAAA,EAAK,OAAM,WAAU,UAAQ,MAAE,mBAAI,OAAO,YAAY,GAAE;AAAA,MACzD,gBAAAR,MAACO,MAAA,EAAI,eAAc,UAAS,WAAW,GACrC;AAAA,wBAAAR,KAACS,OAAA,EAAK,MAAI,MAAC,OAAM,WAAU,4CAAK;AAAA,QAC/B,aAAa,IAAI,CAAC,IAAI,MAAM;AAC3B,gBAAM,OAAO,iBAAiB,EAAE;AAChC,gBAAM,YAAY,OAAO;AACzB,gBAAM,aAAa,MAAM;AACzB,gBAAM,SAAS,aAAa,QAAQ;AACpC,gBAAM,SAAS,YAAY,oBAAU;AACrC,iBACE,gBAAAR,MAACO,MAAA,EACC;AAAA,4BAAAP;AAAA,cAACQ;AAAA,cAAA;AAAA,gBACC,OAAO,aAAa,YAAY;AAAA,gBAChC,MAAM;AAAA,gBAEL;AAAA;AAAA,kBAAQ,KAAK;AAAA,kBAAa;AAAA;AAAA;AAAA,YAC7B;AAAA,YACC,cAAc,gBAAAR,MAACQ,OAAA,EAAK,OAAM,WAAU;AAAA;AAAA,cAAI;AAAA,eAAG;AAAA,eAPpC,EAQV;AAAA,QAEJ,CAAC;AAAA,QACD,gBAAAT,KAACQ,MAAA,EAAI,WAAW,GACd,0BAAAR,KAACS,OAAA,EAAK,OAAM,WAAU,UAAQ,MAAC,qFAAyB,GAC1D;AAAA,SACF;AAAA,MACA,gBAAAT,KAACS,OAAA,EAAK,OAAM,WAAU,UAAQ,MAAE,mBAAI,OAAO,YAAY,GAAE;AAAA,OAC3D,IAEA,gBAAAR,MAAA,YACE;AAAA,sBAAAD,KAACQ,MAAA,EAAI,WAAW,GACd,0BAAAR,KAACS,OAAA,EAAK,OAAM,WAAU,UAAQ,MAAE,mBAAI,OAAO,YAAY,GAAE,GAC3D;AAAA,MACA,gBAAAR,MAACO,MAAA,EACC;AAAA,wBAAAR,KAACQ,MAAA,EAAI,OAAO,GAAG,YAAY,GACzB,0BAAAR,KAACS,OAAA,EAAK,MAAI,MAAC,OAAM,WAAW,oBAAI,GAClC;AAAA,QACA,gBAAAT,KAACQ,MAAA,EAAI,UAAU,GACZ,WAAC,SAAS,CAAC,eAAe,mBAAmB,eAAe,SAAS,IACpE,gBAAAR,KAACS,OAAA,EACE,0BAAgB,MAAM,EAAE,EAAE,IAAI,CAAC,IAAI,MAClC,gBAAAT,KAACS,OAAA,EAAa,OAAO,eAAe,CAAC,KAAK,QAAY,gBAA3C,CAA8C,CAC1D,GACH,IACE,CAAC,SAAS,eAAe,wBAAwB,wBAAwB,SAAS,IACpF,gBAAAT,KAACS,OAAA,EACE,+BAAqB,MAAM,EAAE,EAAE,IAAI,CAAC,IAAI,MACvC,gBAAAT,KAACS,OAAA,EAAa,OAAO,wBAAwB,CAAC,KAAK,QAAY,gBAApD,CAAuD,CACnE,GACH,IAEA,gBAAAT;AAAA,UAAC;AAAA;AAAA,YAEC,OAAO;AAAA,YACP,UAAU;AAAA,YACV,UAAU;AAAA,YACV,aAAY;AAAA;AAAA,UAJP;AAAA,QAKP,GAEJ;AAAA,SACF;AAAA,MACA,gBAAAA,KAACQ,MAAA,EACC,0BAAAR,KAACS,OAAA,EAAK,OAAM,WAAU,UAAQ,MAAE,mBAAI,OAAO,YAAY,GAAE,GAC3D;AAAA,MAGA,gBAAAT,KAAC,iBAAc,QAAgB,OAAc,eAAe,kBAAkB;AAAA,MAE9E,gBAAAA,KAAC,gBAAa,OAAc,OAAc,eAAe,iBAAiB;AAAA,OAC5E;AAAA,IAID,eAAe,CAAC,eACf,gBAAAA,KAACQ,MAAA,EAAI,WAAW,GACd,0BAAAR,KAACS,OAAA,EAAK,OAAM,WAAU,MAAI,MACvB,2EACH,GACF;AAAA,IAID,eACC,gBAAAT,KAACQ,MAAA,EAAI,WAAW,GACd,0BAAAR,KAACS,OAAA,EAAK,OAAM,UAAS,UAAQ,MAC1B,oFACH,GACF;AAAA,KAEJ;AAEJ;;;A8B18BA,SAAS,OAAAC,MAAK,QAAAC,QAAM,YAAAC,iBAAgB;AACpC,SAAS,YAAAC,WAAU,eAAAC,oBAAmB;AA8C9B,gBAAAC,MASI,QAAAC,aATJ;AAnCD,SAAS,WAAW,EAAE,OAAO,UAAU,QAAQ,aAAa,GAAoB;AACrF,QAAM,CAAC,eAAe,gBAAgB,IAAIC,UAAS,CAAC;AAEpD,QAAM,aAAa,gBAAgB,WAAW,MAAM,QAAQ,KAAK,CAAC;AAClE,QAAM,EAAE,aAAa,YAAY,IAAI,eAAe,UAAU;AAE9D,EAAAC;AAAA,IACEC;AAAA,MACE,CAAC,OAAO,QAAQ;AAEd,YAAI,UAAU,OAAO,IAAI,MAAM;AAC7B,sBAAY;AACZ;AAAA,QACF;AAEA,YAAI,MAAM,WAAW,EAAG;AACxB,YAAI,IAAI,WAAW,UAAU,KAAK;AAChC,2BAAiB,CAAC,SAAU,OAAO,IAAI,OAAO,IAAI,MAAM,SAAS,CAAE;AAAA,QACrE,WAAW,IAAI,aAAa,UAAU,KAAK;AACzC,2BAAiB,CAAC,SAAU,OAAO,MAAM,SAAS,IAAI,OAAO,IAAI,CAAE;AAAA,QACrE,WAAW,IAAI,QAAQ;AACrB,gBAAM,OAAO,MAAM,aAAa;AAChC,cAAI,KAAM,UAAS,IAAI;AAAA,QACzB,WAAW,IAAI,UAAU,UAAU,KAAK;AACtC,cAAI,aAAc,cAAa;AAAA,cAC1B,UAAS;AAAA,QAChB;AAAA,MACF;AAAA,MACA,CAAC,OAAO,eAAe,UAAU,QAAQ,cAAc,WAAW;AAAA,IACpE;AAAA,EACF;AAEA,SACE,gBAAAH,MAACI,MAAA,EAAI,eAAc,UACjB;AAAA,oBAAAL,KAACK,MAAA,EAAI,cAAc,GACjB,0BAAAL,KAACM,QAAA,EAAK,MAAI,MAAC,OAAM,WAAU,gDAE3B,GACF;AAAA,IAEA,gBAAAN,KAACK,MAAA,EAAI,eAAc,UAChB,gBAAM,IAAI,CAAC,MAAM,UAAU;AAC1B,YAAM,aAAa,UAAU;AAC7B,aACE,gBAAAJ,MAACI,MAAA,EAAkB,eAAc,OAC/B;AAAA,wBAAAL,KAACK,MAAA,EAAI,OAAO,GAAG,YAAY,GACxB,uBACC,gBAAAL,KAACM,QAAA,EAAK,MAAI,MAAC,OAAM,WACd,qBACH,IAEA,gBAAAN,KAACM,QAAA,EAAM,gBAAK,GAEhB;AAAA,QACA,gBAAAN,KAACK,MAAA,EAAI,OAAO,IAAI,YAAY,GAC1B,0BAAAL,KAACM,QAAA,EAAK,MAAI,MAAC,OAAO,aAAa,YAAY,WACxC,eAAK,MACR,GACF;AAAA,QACA,gBAAAN,KAACK,MAAA,EACC,0BAAAL,KAACM,QAAA,EAAK,OAAM,WAAW,eAAK,aAAY,GAC1C;AAAA,WAjBQ,KAAK,EAkBf;AAAA,IAEJ,CAAC,GACH;AAAA,IAEA,gBAAAN,KAACK,MAAA,EAAI,WAAW,GACd,0BAAAL,KAACM,QAAA,EAAK,UAAQ,MACX,8EACH,GACF;AAAA,IAGC,eACC,gBAAAN,KAACK,MAAA,EAAI,WAAW,GACd,0BAAAL,KAACM,QAAA,EAAK,OAAM,WAAU,MAAI,MACvB,2EACH,GACF;AAAA,KAEJ;AAEJ;;;ACpFA,IAAM,WAAW,oBAAI,IAAkB;AAEhC,SAAS,aAAa,MAAkB;AAC7C,WAAS,IAAI,KAAK,IAAI,IAAI;AAC5B;AAEO,SAAS,QAAQ,IAA8B;AACpD,SAAO,SAAS,IAAI,EAAE;AACxB;AAEO,SAAS,YAAoB;AAClC,SAAO,MAAM,KAAK,SAAS,OAAO,CAAC;AACrC;;;ACvBA,SAAS,OAAAC,OAAK,QAAAC,QAAM,YAAAC,WAAU,UAAAC,eAAc;AAC5C,SAAS,YAAAC,WAAU,aAAAC,YAAW,UAAAC,SAAQ,eAAAC,oBAAmB;AAiR/C,SACgB,OAAAC,OADhB,QAAAC,cAAA;AA/QV,IAAM,aAAa;AACnB,IAAM,cAAc;AACpB,IAAM,eAAe;AACrB,IAAM,eAAe,CAAC,KAAK,KAAK,IAAI,EAAE;AAyBtC,IAAM,SAAqB;AAAA,EACzB,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,MAAM,uBAAgB,SAAS,MAAM,KAAK;AAAA,EACrE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,MAAM,yBAAgB,SAAS,MAAM,KAAK;AAAA,EACrE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,MAAM,yBAAgB,SAAS,MAAM,KAAK;AAAA,EACrE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,MAAM,sBAAiB,SAAS,CAAC,GAAG,OAAO,IAAI,KAAK,MAAM,EAAE;AAAA,EACvF,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,MAAM,sBAAiB,SAAS,CAAC,GAAG,GAAG,GAAG,OAAO,KAAK,KAAK,IAAI,KAAK,EAAE;AAAA,EACjG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,MAAM,4BAAgB,SAAS,CAAC,GAAG,MAAM,IAAI,MAAM,KAAM,KAAK,KAAK,KAAK,EAAG;AAAA,EACtG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,MAAM,4BAAgB,SAAS,CAAC,GAAG,GAAG,IAAI,OAAO,MAAM,KAAK,MAAM,KAAK,KAAK,MAAM,KAAK,MAAM,KAAK,EAAE;AAAA,EAC/H,EAAE,MAAM,GAAG,MAAM,IAAI,IAAI,GAAG,MAAM,0BAAgB,SAAS,MAAM,KAAK;AAAA,EACtE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,MAAM,yBAAgB,SAAS,MAAM,KAAK;AAAA,EACrE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,MAAM,yBAAgB,SAAS,MAAM,KAAK;AACvE;AAEA,SAAS,SAAS,OAAyB;AACzC,SAAO,QAAQ,QAAQ,KAAK,OAAO,MAAM;AAC3C;AAEA,SAAS,aAAa,OAAwB;AAC5C,QAAM,MAAM,SAAS,KAAK;AAC1B,QAAM,SAAkB,CAAC;AACzB,QAAM,MAAM;AACZ,QAAM,SAAS,IAAI,OAAO,IAAI,MAAM,IAAI,OAAO,KAAK;AACpD,QAAM,SAAS,KAAK,OAAO,aAAa,UAAU,CAAC;AACnD,QAAM,OAAO,IAAI,KAAK;AAEtB,WAAS,MAAM,GAAG,MAAM,IAAI,MAAM,OAAO;AACvC,aAAS,MAAM,GAAG,MAAM,IAAI,MAAM,OAAO;AACvC,UAAI,IAAI,QAAQ,KAAK,KAAK,IAAI,MAAM,IAAI,IAAI,GAAG;AAC7C,eAAO,KAAK;AAAA,UACV,GAAG,SAAS,MAAM;AAAA,UAClB,GAAG,IAAI,MAAM;AAAA,UACb,GAAG,IAAI;AAAA,UACP,OAAO;AAAA,QACT,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAiBA,SAAS,mBAAmB,OAA0B;AACpD,QAAM,MAAM,SAAS,KAAK;AAC1B,QAAM,SAAS,IAAI,OAAO,IAAI,MAAM,IAAI,OAAO,KAAK;AACpD,QAAM,SAAS,KAAK,OAAO,aAAa,UAAU,CAAC;AACnD,SAAO;AAAA,IACL;AAAA,IACA,QAAQ,aAAa,KAAK;AAAA,IAC1B,SAAS,KAAK,MAAM,aAAa,CAAC,IAAI,KAAK,MAAM,eAAe,CAAC;AAAA,IACjE,MAAM,EAAE,GAAG,aAAa,GAAG,GAAG,cAAc,EAAE;AAAA,IAC9C,SAAS,EAAE,GAAG,GAAG,GAAG,GAAG;AAAA,IACvB,OAAO;AAAA,IACP,OAAO;AAAA,IACP,UAAU;AAAA,IACV,KAAK;AAAA,IACL,QAAQ;AAAA,EACV;AACF;AAIA,SAAS,OAAO,OAAwB;AACtC,MAAI,MAAM,UAAU,MAAM,YAAY,MAAM,IAAK;AAEjD,QAAM,KAAK,KAAK,MAAM,QAAQ;AAC9B,QAAM,KAAK,KAAK,MAAM,QAAQ;AAE9B,MAAI,MAAM,KAAK,KAAK,GAAG;AAAE,UAAM,KAAK,IAAI;AAAG,UAAM,QAAQ,IAAI;AAAA,EAAG;AAChE,MAAI,MAAM,KAAK,KAAK,aAAa,GAAG;AAAE,UAAM,KAAK,IAAI,aAAa;AAAG,UAAM,QAAQ,IAAI;AAAA,EAAI;AAC3F,MAAI,MAAM,KAAK,KAAK,GAAG;AAAE,UAAM,KAAK,IAAI;AAAG,UAAM,QAAQ,IAAI;AAAA,EAAG;AAGhE,MACE,MAAM,KAAK,MAAM,cAAc,KAC/B,MAAM,KAAK,KAAK,MAAM,WACtB,MAAM,KAAK,KAAK,MAAM,UAAU,cAChC;AACA,UAAM,QAAQ,IAAI;AAClB,UAAM,UAAU,MAAM,KAAK,IAAI,MAAM,WAAW;AAChD,UAAM,QAAQ,IAAI,SAAS,MAAM,KAAK;AAAA,EACxC;AAGA,MAAI,MAAM,KAAK,IAAI,aAAa;AAC9B,UAAM;AACN,QAAI,MAAM,SAAS,GAAG;AACpB,YAAM,WAAW;AAAA,IACnB,OAAO;AACL,YAAM,OAAO,EAAE,GAAG,aAAa,GAAG,GAAG,cAAc,EAAE;AACrD,YAAM,UAAU,EAAE,GAAG,GAAG,GAAG,GAAG;AAC9B,YAAM,UAAU,KAAK,MAAM,aAAa,CAAC,IAAI,KAAK,MAAM,eAAe,CAAC;AAAA,IAC1E;AAAA,EACF;AAGA,QAAM,WAAW,MAAM,OAAO,KAAK,CAAC,MAAM;AACxC,QAAI,CAAC,EAAE,MAAO,QAAO;AACrB,WAAO,MAAM,KAAK,KAAK,EAAE,KAAK,MAAM,KAAK,IAAI,EAAE,IAAI,EAAE,KAAK,MAAM,KAAK,KAAK,EAAE,KAAK,MAAM,KAAK,IAAI,EAAE,IAAI;AAAA,EACxG,CAAC;AAED,MAAI,UAAU;AACZ,aAAS,QAAQ;AACjB,UAAM,SAAS;AACf,UAAM,QAAQ,IAAI,CAAC,MAAM,QAAQ;AAAA,EACnC;AAEA,MAAI,MAAM,OAAO,MAAM,CAAC,MAAM,CAAC,EAAE,KAAK,EAAG,OAAM,MAAM;AACvD;AAIA,SAAS,WAAW,OAA0B;AAC5C,QAAM,QAAkB,CAAC;AAEzB,WAAS,gBAAgB,GAAW,GAA+B;AACjE,UAAM,MAAM,KAAK,OAAO,IAAI,KAAK,CAAC;AAClC,QAAI,OAAO,GAAG;AACZ,YAAM,IAAI,MAAM,OAAO,KAAK,CAAC,OAAO,GAAG,MAAM,KAAK,GAAG,SAAS,KAAK,GAAG,KAAK,IAAI,GAAG,IAAI,GAAG,CAAC;AAC1F,UAAI,EAAG,QAAO,MAAM,aAAa;AAAA,IACnC;AACA,WAAO;AAAA,EACT;AAEA,WAAS,IAAI,GAAG,IAAI,aAAa,KAAK;AACpC,QAAI,OAAO;AACX,aAAS,IAAI,GAAG,IAAI,YAAY,KAAK;AACnC,YAAM,SAAS,MAAM,KAAK,MAAM,KAAK,MAAM,KAAK,MAAM;AACtD,YAAM,WAAW,MAAM,cAAc,KAAK,KAAK,MAAM,WAAW,IAAI,MAAM,UAAU;AACpF,YAAM,WAAW,gBAAgB,GAAG,CAAC;AAErC,UAAI,QAAQ;AACV,gBAAQ;AAAA,MACV,WAAW,UAAU;AACnB,gBAAQ;AAAA,MACV,WAAW,aAAa,QAAW;AACjC,gBAAQ,aAAa,aAAa,QAAQ,CAAW;AAAA,MACvD,OAAO;AACL,gBAAQ;AAAA,MACV;AAAA,IACF;AACA,UAAM,KAAK,IAAI;AAAA,EACjB;AAEA,SAAO,MAAM,IAAI,CAAC,MAAM,SAAI,CAAC,QAAG,EAAE,KAAK,IAAI;AAC7C;AAQA,SAAS,iBAAiB,EAAE,QAAQ,QAAQ,GAA0B;AACpE,QAAM,CAAC,cAAc,eAAe,IAAIL,UAAS,CAAC;AAClD,QAAM,CAAC,gBAAgB,iBAAiB,IAAIA,UAAS,IAAI;AACzD,QAAM,WAAWE,QAAkB,mBAAmB,YAAY,CAAC;AACnE,QAAM,CAACI,OAAM,OAAO,IAAIN,UAAS,CAAC;AAClC,QAAM,YAAYE,QAAO,OAAO;AAChC,YAAU,UAAU;AAEpB,EAAAD,WAAU,MAAM;AACd,QAAI,eAAgB;AACpB,UAAM,WAAW,YAAY,MAAM;AACjC,aAAO,SAAS,OAAO;AACvB,cAAQ,CAAC,MAAM,IAAI,CAAC;AAAA,IACtB,GAAG,EAAE;AACL,WAAO,MAAM,cAAc,QAAQ;AAAA,EACrC,GAAG,CAAC,cAAc,CAAC;AAGnB,QAAM,UAAUE,aAAY,CAAC,UAAmB;AAC9C,UAAM,KAAK,SAAS,SAAS,QAAQ;AACrC,aAAS,UAAU,mBAAmB,EAAE;AACxC,oBAAgB,EAAE;AAClB,sBAAkB,KAAK;AACvB,YAAQ,CAAC;AAAA,EACX,GAAG,CAAC,CAAC;AAGL,QAAM,mBAAmBA,aAAY,MAAM;AACzC,sBAAkB,IAAI;AAAA,EACxB,GAAG,CAAC,CAAC;AAEL,EAAAL;AAAA,IACEK,aAAY,CAAC,OAAO,QAAQ;AAC1B,YAAMI,KAAI,SAAS;AAGnB,UAAI,gBAAgB;AAClB,YAAI,SAAS,OAAO,SAAS,KAAK;AAChC,kBAAQ,OAAO,KAAK,CAAC;AAAA,QACvB,WAAW,UAAU,KAAK;AACxB,kBAAQ,EAAE;AAAA,QACZ,WAAW,IAAI,UAAU,UAAU,KAAK;AACtC,oBAAU,QAAQ;AAAA,QACpB;AACA;AAAA,MACF;AAEA,UAAI,IAAI,WAAW;AACjB,QAAAA,GAAE,UAAU,KAAK,IAAI,GAAGA,GAAE,UAAU,CAAC;AACrC,gBAAQ,CAAC,MAAM,IAAI,CAAC;AAAA,MACtB,WAAW,IAAI,YAAY;AACzB,QAAAA,GAAE,UAAU,KAAK,IAAI,aAAa,cAAcA,GAAE,UAAU,CAAC;AAC7D,gBAAQ,CAAC,MAAM,IAAI,CAAC;AAAA,MACtB,WAAW,UAAU,OAAO,UAAU,KAAK;AACzC,QAAAA,GAAE,SAAS,CAACA,GAAE;AAAA,MAChB,WAAW,UAAU,KAAK;AACxB,YAAIA,GAAE,YAAYA,GAAE,IAAK,SAAQ;AAAA,MACnC,WAAW,UAAU,KAAK;AACxB,YAAIA,GAAE,YAAYA,GAAE,IAAK,kBAAiB;AAAA,MAC5C,WAAW,UAAU,OAAO,IAAI,QAAQ;AACtC,kBAAU,QAAQ;AAAA,MACpB;AAAA,IACF,GAAG,CAAC,gBAAgB,SAAS,gBAAgB,CAAC;AAAA,EAChD;AAEA,QAAM,IAAI,SAAS;AACnB,QAAM,aAAa,EAAE,OAAO,OAAO,CAAC,MAAM,EAAE,KAAK,EAAE;AACnD,QAAM,QAAQ,WAAW,CAAC;AAC1B,QAAM,MAAM,SAAS,EAAE,KAAK;AAE5B,OAAKD;AAEL,SACE,gBAAAD,OAACT,OAAA,EAAI,eAAc,UAEjB;AAAA,oBAAAS,OAACT,OAAA,EAAI,eAAc,OACjB;AAAA,sBAAAQ,MAACR,OAAA,EAAI,OAAO,IACV,0BAAAS,OAACR,QAAA,EAAK;AAAA;AAAA,QACA,EAAE;AAAA,QAAM;AAAA,QAAE,gBAAAO,MAACP,QAAA,EAAK,OAAM,QAAQ,cAAI,MAAK;AAAA,SAC7C,GACF;AAAA,MACA,gBAAAO,MAACR,OAAA,EAAI,OAAO,IACV,0BAAAS,OAACR,QAAA,EAAK;AAAA;AAAA,QACA,gBAAAO,MAACP,QAAA,EAAK,OAAM,UAAU,iBAAO,EAAE,KAAK,EAAE,SAAS,GAAG,GAAG,GAAE;AAAA,SAC7D,GACF;AAAA,MACA,gBAAAO,MAACR,OAAA,EAAI,OAAO,IACV,0BAAAS,OAACR,QAAA,EAAK;AAAA;AAAA,QACA,gBAAAO,MAACP,QAAA,EAAK,OAAM,OAAO,mBAAI,OAAO,KAAK,IAAI,GAAG,EAAE,KAAK,CAAC,GAAE;AAAA,SAC1D,GACF;AAAA,MACA,gBAAAO,MAACR,OAAA,EAAI,OAAO,IACV,0BAAAS,OAACR,QAAA,EAAK;AAAA;AAAA,QACA,gBAAAO,MAACP,QAAA,EAAK,OAAM,QAAQ,sBAAW;AAAA,SACrC,GACF;AAAA,MACA,gBAAAO,MAACR,OAAA,EACC,0BAAAS,OAACR,QAAA,EAAK,OAAO,EAAE,SAAS,SAAS,SAAS;AAAA;AAAA,QACtC,EAAE,SAAS,iBAAO;AAAA,QAAM;AAAA,SAC5B,GACF;AAAA,OACF;AAAA,IAGA,gBAAAQ,OAACT,OAAA,EAAI,eAAc,UACjB;AAAA,sBAAAS,OAACR,QAAA,EAAK;AAAA;AAAA,QAAE,SAAI,OAAO,UAAU;AAAA,QAAE;AAAA,SAAC;AAAA,MAChC,gBAAAO,MAACP,QAAA,EAAM,2BAAiB,mEAAgC,OAAM;AAAA,MAC9D,gBAAAQ,OAACR,QAAA,EAAK;AAAA;AAAA,QAAE,SAAI,OAAO,UAAU;AAAA,QAAE;AAAA,SAAC;AAAA,OAClC;AAAA,IAGC,kBACC,gBAAAQ,OAACT,OAAA,EAAI,WAAW,GAAG,eAAc,UAC/B;AAAA,sBAAAQ,MAACP,QAAA,EAAK,MAAI,MAAC,OAAM,UAAS,sCAAI;AAAA,MAC9B,gBAAAO,MAACR,OAAA,EAAI,eAAc,OAAM,UAAS,QAC/B,iBAAO,IAAI,CAAC,IAAI,MACf,gBAAAQ,MAACR,OAAA,EAAY,OAAO,IAClB,0BAAAS,OAACR,QAAA,EACC;AAAA,wBAAAO,MAACP,QAAA,EAAK,OAAO,iBAAiB,IAAI,IAAI,UAAU,SAC7C,cAAI,MAAM,KAAK,MAAM,OAAO,IAAI,CAAC,GACpC;AAAA,QAAO;AAAA,QACJ,GAAG;AAAA,QAAK;AAAA,QAAG,GAAG;AAAA,QAAK;AAAA,QAAE,GAAG;AAAA,QAAK;AAAA,SAClC,KANQ,CAOV,CACD,GACH;AAAA,MACA,gBAAAO,MAACR,OAAA,EAAI,WAAW,GACd,0BAAAQ,MAACP,QAAA,EAAK,UAAQ,MAAC,4DAAW,GAC5B;AAAA,OACF;AAAA,IAID,CAAC,mBAAmB,EAAE,YAAY,EAAE,QACnC,gBAAAQ,OAACT,OAAA,EAAI,WAAW,GACd;AAAA,sBAAAQ,MAACP,QAAA,EAAK,MAAI,MAAC,OAAO,EAAE,WAAW,QAAQ,SACpC,YAAE,WAAW,mCAAU,kCAC1B;AAAA,MACA,gBAAAQ,OAACR,QAAA,EACE;AAAA;AAAA,QAAS,gBAAAO,MAACP,QAAA,EAAK,OAAM,UAAU,YAAE,OAAM;AAAA,SAC1C;AAAA,OACF;AAAA,IAID,CAAC,kBACA,gBAAAO,MAACR,OAAA,EAAI,WAAW,GACb,YAAE,YAAY,EAAE,MACf,gBAAAQ,MAACP,QAAA,EAAK,UAAQ,MACX,wFACH,IAEA,gBAAAO,MAACP,QAAA,EAAK,UAAQ,MACX,wEACH,GAEJ;AAAA,KAEJ;AAEJ;AAIA,IAAO,wBAAQ;AAAA,EACb,IAAI;AAAA,EACJ,MAAM;AAAA,EACN,aAAa;AAAA,EACb,MAAM,YAAY;AAChB,UAAM,IAAI,QAAc,CAACW,aAAY;AACnC,YAAM,EAAE,QAAQ,IAAIT;AAAA,QAClB,gBAAAK,MAAC,oBAAiB,QAAQ,MAAM;AAAE,kBAAQ;AAAG,UAAAI,SAAQ;AAAA,QAAG,GAAG;AAAA,MAC7D;AAAA,IACF,CAAC;AAAA,EACH;AACF;;;ACpXA,SAAS,OAAAC,OAAK,QAAAC,QAAM,YAAAC,WAAU,UAAAC,eAAc;AAC5C,SAAS,YAAAC,WAAU,aAAAC,YAAW,UAAAC,SAAQ,eAAAC,oBAAmB;AAyY5C,gBAAAC,OACM,QAAAC,cADN;AAvYb,IAAM,SAAS;AACf,IAAM,SAAS;AACf,IAAM,UAAU;AAEhB,IAAM,YAAY;AAClB,IAAMC,iBAAgB,CAAC,WAAW,WAAW,WAAW,WAAW,SAAS;AAG5E,IAAM,YAAsC;AAAA,EAC1C,KAAK,CAAC,0CAAY,gCAAY,gCAAY,gCAAY,wCAAU;AAAA,EAChE,KAAK,CAAC,sBAAY,gCAAY,sBAAY,sBAAY,wCAAU;AAAA,EAChE,KAAK,CAAC,0CAAY,sBAAY,0CAAY,sBAAY,wCAAU;AAAA,EAChE,KAAK,CAAC,0CAAY,sBAAY,0CAAY,sBAAY,wCAAU;AAAA,EAChE,KAAK,CAAC,gCAAY,gCAAY,0CAAY,sBAAY,oBAAU;AAAA,EAChE,KAAK,CAAC,0CAAY,sBAAY,0CAAY,sBAAY,wCAAU;AAAA,EAChE,KAAK,CAAC,0CAAY,sBAAY,0CAAY,gCAAY,wCAAU;AAAA,EAChE,KAAK,CAAC,0CAAY,sBAAY,sBAAY,sBAAY,oBAAU;AAAA,EAChE,KAAK,CAAC,0CAAY,gCAAY,0CAAY,gCAAY,wCAAU;AAAA,EAChE,KAAK,CAAC,0CAAY,gCAAY,0CAAY,sBAAY,wCAAU;AAClE;AAEA,SAAS,gBAAgB,UAA4B;AACnD,QAAM,QAAQ,CAAC,IAAI,IAAI,IAAI,IAAI,EAAE;AACjC,aAAW,MAAM,UAAU;AACzB,UAAM,MAAM,UAAU,EAAE,KAAK,UAAU,GAAG;AAC1C,aAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,YAAM,CAAC,KAAK,OAAO,IAAI,CAAC,KAAK;AAAA,IAC/B;AAAA,EACF;AAEA,QAAM,MAAM;AACZ,SAAO,MAAM,IAAI,CAAC,MAAM,IAAI,OAAO,GAAG,IAAI,CAAC;AAC7C;AAEA,SAAS,SAAS,KAAqB;AACrC,QAAM,IAAI,SAAS,IAAI,MAAM,GAAG,CAAC,GAAG,EAAE;AACtC,QAAM,IAAI,SAAS,IAAI,MAAM,GAAG,CAAC,GAAG,EAAE;AACtC,QAAM,IAAI,SAAS,IAAI,MAAM,GAAG,CAAC,GAAG,EAAE;AACtC,SAAO,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC;AACvB;AAIA,IAAM,aAAa;AAAA;AAAA,EAEjB;AAAA,EAAS;AAAA,EAAO;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAS;AAAA,EAAW;AAAA,EAAU;AAAA,EAC9D;AAAA,EAAW;AAAA,EAAQ;AAAA,EAAS;AAAA,EAAU;AAAA,EAAY;AAAA,EAAY;AAAA,EAC9D;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAW;AAAA,EAAS;AAAA,EAAS;AAAA,EAAQ;AAAA,EACrD;AAAA,EAAU;AAAA,EAAU;AAAA,EAAW;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAU;AAAA;AAAA,EAGzD;AAAA,EAAQ;AAAA,EAAO;AAAA,EAAO;AAAA,EAAO;AAAA,EAAO;AAAA,EAAO;AAAA,EAAO;AAAA,EAAO;AAAA,EACzD;AAAA,EAAa;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAc;AAAA,EAAa;AAAA,EAAU;AAAA,EAClE;AAAA,EAAY;AAAA,EAAU;AAAA,EAAW;AAAA,EAAQ;AAAA,EAAS;AAAA,EAClD;AAAA,EAAc;AAAA,EAAW;AAAA,EAAQ;AAAA,EAAa;AAAA;AAAA,EAG9C;AAAA,EAAO;AAAA,EAAS;AAAA,EAAc;AAAA,EAAc;AAAA,EAAW;AAAA,EACvD;AAAA,EAAO;AAAA,EAAU;AAAA,EAAU;AAAA,EAAa;AAAA,EAAU;AAAA,EAClD;AAAA,EAAQ;AAAA,EAAO;AAAA,EAAS;AAAA,EAAW;AAAA,EAAU;AAAA,EAAS;AAAA,EACtD;AAAA,EAAS;AAAA,EAAS;AAAA,EAAM;AAAA,EAAU;AAAA,EAAS;AAAA;AAAA,EAG3C;AAAA,EAAO;AAAA,EAAS;AAAA,EAAM;AAAA,EAAU;AAAA,EAAS;AAAA,EAAS;AAAA,EAClD;AAAA,EAAS;AAAA,EAAS;AAAA,EAAS;AAAA,EAAQ;AAAA,EAAS;AAAA,EAAS;AAAA,EACrD;AAAA,EAAS;AAAA,EAAU;AAAA,EAAS;AAAA,EAAU;AAAA,EAAU;AAAA,EAAU;AAAA;AAAA,EAG1D;AAAA,EAAU;AAAA,EAAS;AAAA,EAAS;AAAA,EAAQ;AAAA,EAAS;AAAA,EAAQ;AAAA,EACrD;AAAA,EAAM;AAAA,EAAM;AAAA,EAAU;AAAA,EAAY;AAAA,EAAW;AAAA,EAAS;AAAA,EACtD;AAAA,EAAQ;AAAA,EAAU;AAAA,EAAS;AAAA,EAAU;AAAA,EAAU;AAAA,EAAS;AAAA;AAAA,EAGxD;AAAA,EAAS;AAAA,EAAS;AAAA,EAAS;AAAA,EAAQ;AAAA,EAAS;AAAA,EAAQ;AAAA,EAAO;AAAA,EAC3D;AAAA,EAAQ;AAAA,EAAU;AAAA,EAAU;AAAA,EAAU;AAAA,EAAO;AAAA,EAAS;AAAA,EACtD;AAAA,EAAW;AAAA,EAAY;AAAA,EAAW;AAAA,EAAS;AAAA,EAAW;AAAA;AAAA,EAGtD;AAAA,EAAU;AAAA,EAAQ;AAAA,EAAU;AAAA,EAAU;AAAA,EAAQ;AAAA,EAAS;AAAA,EACvD;AAAA,EAAU;AAAA,EAAU;AAAA,EAAa;AAAA,EAAY;AAAA,EAAU;AAAA,EACvD;AAAA,EAAU;AAAA,EAAY;AAAA,EAAU;AAAA,EAAU;AAAA,EAAU;AAAA;AAAA,EAGpD;AAAA,EAAU;AAAA,EAAU;AAAA,EAAW;AAAA,EAAU;AAAA,EAAS;AAAA,EAAS;AAAA,EAC3D;AAAA,EAAa;AAAA,EAAQ;AAAA,EAAS;AAAA,EAAY;AAAA,EAAU;AAAA,EACpD;AAAA,EAAU;AAAA,EAAW;AAAA,EAAU;AAAA,EAAY;AAAA,EAAY;AAAA,EACvD;AAAA,EAAS;AAAA,EAAO;AAAA,EAAO;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAS;AAAA,EAAS;AAAA;AAAA,EAGzD;AAAA,EAAU;AAAA,EAAU;AAAA,EAAW;AAAA,EAAU;AAAA,EAAS;AAAA,EAClD;AAAA,EAAY;AAAA,EAAW;AAAA,EAAU;AAAA,EAAU;AAAA,EAAW;AAAA,EACtD;AAAA,EAAY;AAAA,EAAW;AAAA,EAAW;AAAA,EAAY;AAAA,EAC9C;AAAA,EAAU;AAAA,EAAU;AAAA,EAAS;AAAA,EAAS;AAAA,EAAS;AACjD;AAEA,SAAS,WAAW,MAA2B;AAC7C,MAAI;AACJ,KAAG;AAAE,QAAI,WAAW,KAAK,MAAM,KAAK,OAAO,IAAI,WAAW,MAAM,CAAC;AAAA,EAAa,SACvE,KAAK,IAAI,CAAC;AACjB,SAAO;AACT;AA0BA,SAASC,sBAAgC;AACvC,SAAO;AAAA,IACL,OAAO,CAAC;AAAA,IACR,OAAO;AAAA,IACP,OAAO;AAAA,IACP,OAAO;AAAA,IACP,YAAY;AAAA,IACZ,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,SAAS;AAAA,IACT,cAAc;AAAA,IACd,WAAW,oBAAI,IAAI;AAAA,EACrB;AACF;AAEA,SAAS,WAAW,GAA6B;AAE/C,MAAI,OAAwB;AAC5B,aAAW,KAAK,EAAE,OAAO;AACvB,QAAI,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAK,QAAO;AAAA,EACxC;AACA,SAAO,MAAM,QAAQ;AACvB;AAIA,SAASC,QAAO,GAAoB;AAClC,MAAI,EAAE,UAAU,EAAE,SAAU;AAG5B,IAAE;AACF,MAAI,EAAE,cAAc,KAAK,IAAI,IAAI,KAAK,KAAK,MAAM,EAAE,QAAQ,EAAE,CAAC,GAAG;AAC/D,MAAE,aAAa;AACf,QAAI,EAAE,MAAM,SAAS,WAAW;AAC9B,YAAM,OAAO,IAAI,IAAI,EAAE,SAAS;AAChC,iBAAW,KAAK,EAAE,MAAO,MAAK,IAAI,EAAE,IAAI;AACxC,YAAM,OAAO,WAAW,IAAI;AAE5B,YAAM,WAAW,IAAI,IAAI,EAAE,MAAM,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC;AAClD,UAAI,MAAM;AACV,eAAS,IAAI,SAAS,IAAI,QAAQ,KAAK;AACrC,YAAI,CAAC,SAAS,IAAI,CAAC,GAAG;AAAE,gBAAM;AAAG;AAAA,QAAO;AAAA,MAC1C;AACA,UAAI,OAAO,GAAG;AACZ,UAAE,MAAM,KAAK,EAAE,MAAM,KAAK,KAAK,SAAS,EAAE,CAAC;AAC3C,UAAE,UAAU,IAAI,IAAI;AAAA,MACtB;AAAA,IACF;AAAA,EACF;AAGA,aAAW,KAAK,EAAE,OAAO;AACvB,MAAE,OAAO,EAAE;AAAA,EACb;AAGA,QAAM,SAAS,EAAE,MAAM;AACvB,IAAE,QAAQ,EAAE,MAAM,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,KAAK,MAAM;AACtD,QAAM,UAAU,SAAS,EAAE,MAAM;AAGjC,MAAI,UAAU,GAAG;AACf,MAAE,SAAS;AACX,MAAE,QAAQ;AACV,MAAE,SAAS,WAAW,CAAC;AACvB,QAAI,EAAE,SAAS,GAAG;AAChB,QAAE,WAAW;AACb,QAAE,QAAQ,CAAC;AAAA,IACb;AAAA,EACF;AAGA,IAAE,SAAS,WAAW,CAAC;AAGvB,MAAI,EAAE,eAAe,GAAG;AACtB,MAAE;AACF,QAAI,EAAE,gBAAgB,EAAG,GAAE,UAAU;AAAA,EACvC;AACF;AAIA,SAAS,cAAc,GAAc,YAAsB,YAAoB,SAA2B;AACxG,QAAM,OAAiB,CAAC;AAExB,WAAS,IAAI,GAAG,IAAI,QAAQ,KAAK;AAC/B,QAAI,OAAO;AAGX,QAAI,IAAI,SAAS;AACf,UAAI,IAAI,GAAG;AAET,cAAM,OAAO,WAAW,CAAC,KAAK,IAAI,OAAO,MAAM;AAC/C,eAAO,aAAa,SAAS,UAAU,CAAC,IAAI,GAAG;AAAA,MACjD,WAAW,MAAM,GAAG;AAElB,YAAI,EAAE,SAAS,GAAG;AAChB,gBAAM,YAAY,GAAG,EAAE,KAAK;AAC5B,gBAAM,MAAM,KAAK,OAAO,SAAS,UAAU,UAAU,CAAC;AACtD,gBAAM,aAAaF,eAAc,EAAE,QAAQA,eAAc,MAAM;AAC/D,gBAAM,MAAM,IAAI,OAAO,GAAG,IAAI,YAAY,IAAI,OAAO,SAAS,MAAM,UAAU,MAAM;AACpF,iBAAO,aAAa,SAAS,UAAU,CAAC,IAAI,GAAG;AAAA,QACjD,WAAW,EAAE,QAAQ;AACnB,gBAAM,YAAY;AAClB,gBAAM,MAAM,KAAK,OAAO,SAAS,UAAU,UAAU,CAAC;AACtD,iBAAO,IAAI,OAAO,GAAG,IAAI;AACzB,iBAAO,KAAK,OAAO,MAAM;AAAA,QAC3B;AAAA,MACF;AAEA,aAAO,KAAK,OAAO,MAAM;AAAA,IAC3B,OAAO;AAGL,UAAI,MAAM,WAAW,SAAS;AAC5B,cAAM,MAAM,KAAK,OAAO,SAAS,QAAQ,UAAU,CAAC;AACpD,cAAM,MAAM,IAAI,OAAO,GAAG,IAAI,UAAU,IAAI,OAAO,SAAS,MAAM,QAAQ,MAAM;AAChF,cAAM,WAAWA,eAAc,KAAK,MAAM,KAAK,OAAO,IAAIA,eAAc,MAAM,CAAC;AAC/E,eAAO,aAAa,SAAS,QAAQ,CAAC,IAAI,GAAG;AAAA,MAC/C,OAAO;AACL,iBAAS,IAAI,GAAG,IAAI,QAAQ,KAAK;AAC/B,gBAAM,OAAO,EAAE,MAAM,KAAK,CAAC,MAAM;AAC/B,kBAAM,UAAU,IAAI,KAAK,MAAM,EAAE,GAAG;AACpC,mBAAO,WAAW,KAAK,UAAU,EAAE,KAAK,UAAU,EAAE,QAAQ;AAAA,UAC9D,CAAC;AACD,cAAI,MAAM;AACR,kBAAM,UAAU,IAAI,KAAK,MAAM,KAAK,GAAG;AACvC,kBAAM,KAAK,KAAK,KAAK,OAAO;AAC5B,kBAAM,WAAW,KAAK,SAAS,EAAE;AACjC,kBAAM,WAAW,EAAE,WAAW,KAAK,OAAO,EAAE,MAAM,SAAS;AAC3D,kBAAM,UAAU,YAAY,UAAU;AACtC,gBAAI,UAAU;AACZ,sBAAQ,UAAU,WAAW,EAAE,YAAY,WAAW,EAAE;AAAA,YAC1D,OAAO;AACL,sBAAQ,WAAW,EAAE;AAAA,YACvB;AAAA,UACF,OAAO;AACL,oBAAQ;AAAA,UACV;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,SAAK,KAAK,IAAI;AAAA,EAChB;AACA,SAAO;AACT;AAQA,SAAS,WAAW,EAAE,QAAQ,QAAQ,GAAoB;AACxD,QAAM,WAAWG,QAAkBF,oBAAmB,CAAC;AACvD,QAAM,CAACG,OAAM,OAAO,IAAIC,UAAS,CAAC;AAClC,QAAM,CAAC,aAAa,cAAc,IAAIA,UAAS,CAAC;AAChD,QAAM,YAAYF,QAAO,OAAO;AAChC,YAAU,UAAU;AAGpB,EAAAG,WAAU,MAAM;AACd,UAAM,QAAQ,YAAY,MAAM;AAC9B,qBAAe,CAAC,UAAU,OAAO,KAAKN,eAAc,MAAM;AAAA,IAC5D,GAAG,GAAG;AACN,WAAO,MAAM,cAAc,KAAK;AAAA,EAClC,GAAG,CAAC,CAAC;AAGL,EAAAM,WAAU,MAAM;AACd,UAAM,WAAW,YAAY,MAAM;AACjC,MAAAJ,QAAO,SAAS,OAAO;AACvB,cAAQ,CAAC,MAAM,IAAI,CAAC;AAAA,IACtB,GAAG,EAAE;AACL,WAAO,MAAM,cAAc,QAAQ;AAAA,EACrC,GAAG,CAAC,CAAC;AAEL,EAAAK;AAAA,IACEC,aAAY,CAAC,OAAO,QAAQ;AAC1B,YAAMC,KAAI,SAAS;AACnB,UAAIA,GAAE,UAAU;AACd,YAAI,UAAU,KAAK;AACjB,mBAAS,UAAUR,oBAAmB;AACtC,kBAAQ,CAAC;AAAA,QACX,WAAW,UAAU,OAAO,IAAI,QAAQ;AACtC,oBAAU,QAAQ;AAAA,QACpB;AACA;AAAA,MACF;AAEA,UAAK,UAAU,OAAO,IAAI,QAAS,UAAU,KAAK;AAChD,QAAAQ,GAAE,SAAS,CAACA,GAAE;AACd;AAAA,MACF;AACA,UAAK,UAAU,OAAO,IAAI,QAAS,IAAI,QAAQ;AAC7C,kBAAU,QAAQ;AAClB;AAAA,MACF;AAEA,UAAIA,GAAE,OAAQ;AAGd,UAAI,MAAM,WAAW,KAAK,SAAS,OAAO,SAAS,KAAK;AAEtD,YAAI,CAACA,GAAE,QAAQ;AACb,UAAAA,GAAE,SAAS,WAAWA,EAAC;AAAA,QACzB;AACA,YAAIA,GAAE,QAAQ;AACZ,gBAAM,WAAWA,GAAE,OAAOA,GAAE,MAAM,MAAM;AACxC,cAAI,aAAa,OAAO;AACtB,YAAAA,GAAE,SAAS;AAEX,gBAAIA,GAAE,UAAUA,GAAE,QAAQ;AAExB,cAAAA,GAAE,QAAQA,GAAE,MAAM,OAAO,CAAC,MAAM,EAAE,SAASA,GAAE,MAAM;AACnD,cAAAA,GAAE;AAEF,oBAAM,YAAYA,GAAE;AACpB,oBAAM,UAAUA,GAAE,OAAO;AACzB,oBAAM,aAAaA,GAAE,SAAS,IAAIA,GAAE,QAAQ;AAC5C,cAAAA,GAAE,SAAS,UAAU;AACrB,cAAAA,GAAE,QAAQA,GAAE,QAAQ;AAGpB,oBAAM,gBAAgB,KAAK,MAAM,YAAY,GAAG;AAChD,oBAAM,eAAe,KAAK,MAAMA,GAAE,QAAQ,GAAG;AAC7C,kBAAI,eAAe,eAAe;AAChC,gBAAAA,GAAE,SAAS;AAAA,cACb;AAGA,kBAAIA,GAAE,SAAS,OAAU,YAAY,KAAQ;AAC3C,gBAAAA,GAAE,UAAU;AACZ,gBAAAA,GAAE,eAAe;AAAA,cACnB;AACA,cAAAA,GAAE,QAAQ;AACV,cAAAA,GAAE,SAAS,WAAWA,EAAC;AAAA,YACzB;AAAA,UACF,OAAO;AAEL,YAAAA,GAAE,QAAQ;AAAA,UACZ;AAAA,QACF;AAAA,MACF;AAAA,IACF,GAAG,CAAC,CAAC;AAAA,EACP;AAEA,QAAM,IAAI,SAAS;AACnB,QAAM,aAAaT,eAAc,WAAW;AAC5C,QAAM,WAAW,OAAO,EAAE,KAAK,EAAE,SAAS,GAAG,GAAG;AAChD,QAAM,aAAa,gBAAgB,QAAQ;AAC3C,QAAM,OAAO,cAAc,GAAG,YAAY,YAAY,EAAE,OAAO;AAE/D,OAAKI;AAEL,SACE,gBAAAM,OAACC,OAAA,EAAI,eAAc,UAAS,UAAU,GAEpC;AAAA,oBAAAC,MAACD,OAAA,EAAI,eAAc,OACjB,0BAAAD,OAACG,QAAA,EAAK;AAAA;AAAA,MACD,gBAAAD,MAACC,QAAA,EAAK,OAAM,OAAO,mBAAI,OAAO,KAAK,IAAI,GAAG,EAAE,KAAK,CAAC,GAAE;AAAA,MACtD;AAAA,MAAK;AAAA,MAAG,gBAAAH,OAACG,QAAA,EAAK,OAAM,QAAO;AAAA;AAAA,QAAI,KAAK,MAAM,EAAE,QAAQ,EAAE;AAAA,SAAE;AAAA,OAC3D,GACF;AAAA,IAGC,CAAC,EAAE,YAAY,EAAE,UAChB,gBAAAD,MAACD,OAAA,EAAI,WAAW,GACd,0BAAAD,OAACG,QAAA,EAAK;AAAA;AAAA,MACA,gBAAAD,MAACC,QAAA,EAAK,OAAM,SAAS,YAAE,OAAM;AAAA,MACjC,gBAAAD,MAACC,QAAA,EAAK,OAAM,SAAS,YAAE,OAAO,MAAM,EAAE,MAAM,MAAM,GAAE;AAAA,OACtD,GACF;AAAA,IAIF,gBAAAH,OAACC,OAAA,EAAI,eAAc,UAAS,WAAW,GACrC;AAAA,sBAAAD,OAACG,QAAA,EAAK;AAAA;AAAA,QAAE,SAAI,OAAO,MAAM;AAAA,QAAE;AAAA,SAAC;AAAA,MAC3B,KAAK,IAAI,CAAC,KAAK,MACd,gBAAAD,MAACC,QAAA,EAAc,mBAAI,GAAG,YAAX,CAAe,CAC3B;AAAA,MACD,gBAAAH,OAACG,QAAA,EAAK;AAAA;AAAA,QAAE,SAAI,OAAO,MAAM;AAAA,QAAE;AAAA,SAAC;AAAA,OAC9B;AAAA,IAGC,EAAE,YACD,gBAAAH,OAACC,OAAA,EAAI,WAAW,GACd;AAAA,sBAAAC,MAACC,QAAA,EAAK,MAAI,MAAC,OAAM,OAAM,4CAEvB;AAAA,MACA,gBAAAH,OAACG,QAAA,EACE;AAAA;AAAA,QAAS,gBAAAD,MAACC,QAAA,EAAK,OAAM,UAAU,YAAE,OAAM;AAAA,QACvC;AAAA,SACH;AAAA,OACF;AAAA,IAIF,gBAAAD,MAACD,OAAA,EAAI,WAAW,GACd,0BAAAC,MAACC,QAAA,EAAK,UAAQ,MACX,uGACH,GACF;AAAA,KACF;AAEJ;AAIA,IAAO,sBAAQ;AAAA,EACb,IAAI;AAAA,EACJ,MAAM;AAAA,EACN,aAAa;AAAA,EACb,MAAM,YAAY;AAChB,UAAM,IAAI,QAAc,CAACC,aAAY;AACnC,YAAM,EAAE,QAAQ,IAAIC;AAAA,QAClB,gBAAAH,MAAC,cAAW,QAAQ,MAAM;AAAE,kBAAQ;AAAG,UAAAE,SAAQ;AAAA,QAAG,GAAG;AAAA,MACvD;AAAA,IACF,CAAC;AAAA,EACH;AACF;;;AClcA,SAAS,WAAW,GAAc,GAAuB;AACvD,SACG,MAAM,QAAQ,MAAM,UACpB,MAAM,UAAU,MAAM,QACtB,MAAM,UAAU,MAAM,WACtB,MAAM,WAAW,MAAM;AAE5B;AAEA,SAAS,UAAU,GAAU,GAAmB;AAC9C,SAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE;AAClC;AAEA,SAAS,UAAU,GAAU,KAAuB;AAClD,UAAQ,KAAK;AAAA,IACX,KAAK;AAAS,aAAO,EAAE,GAAG,EAAE,GAAG,GAAG,EAAE,IAAI,EAAE;AAAA,IAC1C,KAAK;AAAS,aAAO,EAAE,GAAG,EAAE,GAAG,GAAG,EAAE,IAAI,EAAE;AAAA,IAC1C,KAAK;AAAS,aAAO,EAAE,GAAG,EAAE,IAAI,GAAG,GAAG,EAAE,EAAE;AAAA,IAC1C,KAAK;AAAS,aAAO,EAAE,GAAG,EAAE,IAAI,GAAG,GAAG,EAAE,EAAE;AAAA,EAC5C;AACF;AAEA,SAAS,UAAU,GAAU,QAA2B;AACtD,SAAO;AAAA,IACL,IAAK,EAAE,IAAI,OAAO,QAAS,OAAO,SAAS,OAAO;AAAA,IAClD,IAAK,EAAE,IAAI,OAAO,SAAU,OAAO,UAAU,OAAO;AAAA,EACtD;AACF;AAQA,SAAS,aACP,OACA,QACA,MACA,mBAA4B,CAAC,GAChB;AACb,QAAM,WAAW,IAAI,IAAI,MAAM,IAAI,OAAK,GAAG,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC;AAExD,aAAW,KAAK,kBAAkB;AAChC,aAAS,IAAI,GAAG,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE;AAAA,EAC9B;AAEA,QAAM,aAAa,OAAO,QAAQ,OAAO;AACzC,MAAI,SAAS,QAAQ,WAAY,QAAO;AAExC,MAAI;AACJ,KAAG;AACD,UAAM;AAAA,MACJ,GAAG,KAAK,MAAM,KAAK,OAAO,IAAI,OAAO,KAAK;AAAA,MAC1C,GAAG,KAAK,MAAM,KAAK,OAAO,IAAI,OAAO,MAAM;AAAA,IAC7C;AAAA,EACF,SAAS,SAAS,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,CAAC,EAAE;AAEzC,SAAO;AAAA,IACL,UAAU;AAAA,IACV;AAAA,IACA,GAAI,SAAS,YAAY,EAAE,WAAW,KAAK,IAAI,EAAE,IAAI,CAAC;AAAA,EACxD;AACF;AAKO,SAASE,oBAAmB,QAA+B;AAChE,QAAM,SAAS,KAAK,MAAM,OAAO,QAAQ,CAAC;AAC1C,QAAM,SAAS,KAAK,MAAM,OAAO,SAAS,CAAC;AAE3C,QAAM,QAAiB;AAAA,IACrB,EAAE,GAAG,QAAQ,GAAG,OAAO;AAAA,IACvB,EAAE,GAAG,SAAS,GAAG,GAAG,OAAO;AAAA,IAC3B,EAAE,GAAG,SAAS,GAAG,GAAG,OAAO;AAAA,EAC7B;AAEA,QAAM,OAAO,aAAa,OAAO,QAAQ,QAAQ;AAEjD,SAAO;AAAA,IACL;AAAA,IACA,WAAW;AAAA,IACX,eAAe;AAAA,IACf;AAAA,IACA,aAAa;AAAA,IACb,OAAO;AAAA,IACP,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,UAAU;AAAA,IACV,UAAU,KAAK,OAAO,IAAI;AAAA,IAC1B,cAAc,OAAO;AAAA,EACvB;AACF;AAIO,SAAS,KAAK,OAAkB,QAA0B;AAE/D,QAAM,YAAY,MAAM;AAGxB,QAAM,OAAO,MAAM,MAAM,CAAC;AAC1B,MAAI,UAAU,UAAU,MAAM,MAAM,SAAS;AAG7C,QAAM,cACJ,QAAQ,IAAI,KACZ,QAAQ,KAAK,OAAO,SACpB,QAAQ,IAAI,KACZ,QAAQ,KAAK,OAAO;AAEtB,MAAI,aAAa;AACf,QAAI,MAAM,UAAU;AAClB,gBAAU,UAAU,SAAS,MAAM;AAAA,IACrC,OAAO;AACL,YAAM,aAAa;AACnB;AAAA,IACF;AAAA,EACF;AAGA,QAAM,YAAY,UAAU,SAAS,MAAM,KAAK,QAAQ;AACxD,QAAM,aAAa,MAAM,gBAAgB,QAAQ,UAAU,SAAS,MAAM,YAAY,QAAQ;AAC9F,QAAM,UAAU,aAAa;AAG7B,QAAM,cAAc,UAAU,MAAM,QAAQ,MAAM,MAAM,MAAM,GAAG,EAAE;AACnE,aAAW,WAAW,aAAa;AACjC,QAAI,UAAU,SAAS,OAAO,GAAG;AAC/B,YAAM,aAAa;AACnB;AAAA,IACF;AAAA,EACF;AAGA,QAAM,MAAM,QAAQ,OAAO;AAG3B,MAAI,WAAW;AACb,UAAM,SAAS;AACf,gBAAY,OAAO,MAAM;AAGzB,UAAM,UAAmB,MAAM,cAAc,CAAC,MAAM,YAAY,QAAQ,IAAI,CAAC;AAC7E,UAAM,UAAU,aAAa,MAAM,OAAO,QAAQ,UAAU,OAAO;AACnE,QAAI,SAAS;AACX,YAAM,OAAO;AAAA,IACf,OAAO;AAEL,YAAM,aAAa;AACnB;AAAA,IACF;AAGA,QAAI,MAAM,gBAAgB,QAAQ,KAAK,OAAO,IAAI,MAAM;AACtD,YAAM,cAAc,aAAa,MAAM,OAAO,QAAQ,WAAW,CAAC,MAAM,KAAK,QAAQ,CAAC;AAAA,IACxF;AAAA,EACF,WAAW,YAAY;AACrB,UAAM,SAAS;AACf,gBAAY,OAAO,MAAM;AACzB,UAAM,cAAc;AAAA,EACtB,OAAO;AAEL,UAAM,MAAM,IAAI;AAAA,EAClB;AAGA,MAAI,MAAM,aAAa,cAAc,QAAW;AAC9C,QAAI,KAAK,IAAI,IAAI,MAAM,YAAY,YAAY,KAAM;AACnD,YAAM,cAAc;AAAA,IACtB;AAAA,EACF;AACF;AAIA,SAAS,YAAY,OAAkB,QAA0B;AAC/D,QAAM,WAAW,KAAK,MAAM,MAAM,QAAQ,OAAO,aAAa,IAAI;AAClE,QAAM,aAAa;AACnB,QAAM,eAAe,KAAK;AAAA,IACxB,OAAO;AAAA,IACP,OAAO,gBAAgB,WAAW,KAAK,OAAO;AAAA,EAChD;AACF;AAQO,SAAS,gBAAgB,OAAkB,QAAyB;AACzE,MAAI,CAAC,WAAW,QAAQ,MAAM,SAAS,GAAG;AACxC,UAAM,gBAAgB;AAAA,EACxB;AACF;;;ACxMA,OAAOC,YAAW;AAIlB,IAAM,SAAS;AACf,IAAM,QAAQ;AACd,IAAM,YAAY;AAClB,IAAM,YAAY;AAClB,IAAM,mBAAmB;AACzB,IAAM,oBAAoB;AAI1B,IAAM,MAAM;AACZ,IAAM,cAAc,GAAG,GAAG;AAC1B,IAAM,cAAc,GAAG,GAAG;AAC1B,IAAM,cAAc,GAAG,GAAG;AAGnB,SAAS,aAAmB;AACjC,UAAQ,OAAO,MAAM,WAAW;AAClC;AAGO,SAAS,aAAmB;AACjC,UAAQ,OAAO,MAAM,WAAW;AAClC;AAGO,SAAS,cAAoB;AAClC,UAAQ,OAAO,MAAM,GAAG,GAAG,KAAK,WAAW,EAAE;AAC/C;AAIA,SAAS,UAAU,MAAc,MAAsF;AACrH,UAAQ,MAAM;AAAA,IACZ,KAAK;AAAe,aAAOA,OAAM,YAAY,IAAI;AAAA,IACjD,KAAK;AAAe,aAAOA,OAAM,MAAM,IAAI;AAAA,IAC3C,KAAK;AAAe,aAAOA,OAAM,IAAI,IAAI;AAAA,IACzC,KAAK;AAAgB,aAAOA,OAAM,OAAO,IAAI;AAAA,IAC7C,KAAK;AAAe,aAAO;AAAA,EAC7B;AACF;AAQO,SAAS,iBAAiB,OAAkB,QAA0B;AAC3E,QAAM,QAAQ,iBAAiB,OAAO,MAAM;AAE5C,UAAQ,OAAO,MAAM,GAAG,GAAG,KAAK,WAAW,KAAK,MAAM,KAAK,IAAI,CAAC;AAClE;AAEA,SAAS,iBAAiB,OAAkB,QAA8B;AACxE,QAAM,EAAE,OAAO,GAAG,QAAQ,EAAE,IAAI;AAChC,QAAM,QAAkB,CAAC;AAIzB,QAAM,OAAmB,MAAM,KAAK,EAAE,QAAQ,EAAE,GAAG,MAAM,MAAM,CAAC,EAAE,KAAK,OAAO,CAAC;AAG/E,WAAS,IAAI,GAAG,IAAI,MAAM,MAAM,QAAQ,KAAK;AAC3C,UAAM,MAAM,MAAM,MAAM,CAAC;AACzB,QAAI,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,IAAI,KAAK,KAAK,IAAI,IAAI,GAAG;AACtD,WAAK,IAAI,CAAC,EAAG,IAAI,CAAC,IAAI;AAAA,IACxB;AAAA,EACF;AAGA,QAAM,OAAO,MAAM,MAAM,CAAC;AAC1B,MAAI,KAAK,KAAK,KAAK,KAAK,IAAI,KAAK,KAAK,KAAK,KAAK,KAAK,IAAI,GAAG;AAC1D,SAAK,KAAK,CAAC,EAAG,KAAK,CAAC,IAAI;AAAA,EAC1B;AAGA,QAAM,KAAK,MAAM,KAAK;AACtB,MAAI,KAAK,GAAG,CAAC,EAAG,GAAG,CAAC,MAAM,SAAS;AACjC,SAAK,GAAG,CAAC,EAAG,GAAG,CAAC,IAAI;AAAA,EACtB;AAGA,MAAI,MAAM,aAAa;AACrB,UAAM,KAAK,MAAM,YAAY;AAC7B,QAAI,KAAK,GAAG,CAAC,EAAG,GAAG,CAAC,MAAM,SAAS;AACjC,WAAK,GAAG,CAAC,EAAG,GAAG,CAAC,IAAI;AAAA,IACtB;AAAA,EACF;AAGA,QAAM,KAAK,OAAOA,OAAM,KAAK,WAAM,eAAK,OAAO,CAAC,IAAI,QAAG,CAAC;AAGxD,WAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,QAAI,MAAM,OAAOA,OAAM,KAAK,QAAG;AAC/B,aAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,YAAM,WAAW,KAAK,CAAC,EAAG,CAAC;AAC3B,UAAI;AACJ,cAAQ,UAAU;AAAA,QAChB,KAAK;AACH,iBAAO,UAAU,OAAO,MAAM;AAC9B;AAAA,QACF,KAAK;AACH,iBAAO,UAAU,OAAO,MAAM;AAC9B;AAAA,QACF,KAAK;AACH,iBAAO,mBAAmB;AAC1B;AAAA,QACF,KAAK;AACH,iBAAO,oBAAoB;AAC3B;AAAA,QACF;AACE,iBAAO;AAAA,MACX;AACA,aAAO,UAAU,MAAM,QAAe;AAAA,IACxC;AACA,WAAOA,OAAM,KAAK,QAAG;AACrB,UAAM,KAAK,GAAG;AAAA,EAChB;AAGA,QAAM,KAAK,OAAOA,OAAM,KAAK,WAAM,eAAK,OAAO,CAAC,IAAI,QAAG,CAAC;AAGxD,QAAM,KAAK,EAAE;AACb,QAAM;AAAA,IACJ,KAAKA,OAAM,KAAK,cAAI,CAAC,KAAK,MAAM,KAAK,KAClCA,OAAM,KAAK,cAAI,CAAC,KAAK,MAAM,MAAM,MAAM,KACvCA,OAAM,KAAK,cAAI,CAAC,QAAQ,MAAM,UAAU,KACxCA,OAAM,KAAK,cAAI,CAAC,KAAK,MAAM,WAAW,iBAAO,cAAI;AAAA,EACtD;AAGA,MAAI,MAAM,aAAa,cAAc,QAAW;AAC9C,UAAM,YAAY,KAAK,MAAM,OAAQ,KAAK,IAAI,IAAI,MAAM,YAAY,cAAc,GAAI;AACtF,QAAI,YAAY,GAAG;AACjB,YAAM,KAAK,KAAKA,OAAM,OAAO,mCAAU,SAAS,2BAAO,CAAC,EAAE;AAAA,IAC5D;AAAA,EACF;AAGA,QAAM,KAAK,KAAKA,OAAM,KAAK,8GAAwC,CAAC,EAAE;AAGtE,MAAI,MAAM,UAAU;AAClB,UAAM,KAAK,EAAE;AACb,UAAM,KAAK,KAAKA,OAAM,OAAO,yDAAiB,CAAC,EAAE;AAAA,EACnD;AAEA,MAAI,MAAM,YAAY;AACpB,UAAM,KAAK,EAAE;AACb,UAAM,KAAK,KAAKA,OAAM,IAAI,KAAK,sCAAW,CAAC,KAAKA,OAAM,KAAK,+BAAW,MAAM,KAAK,CAAC,EAAE;AACpF,UAAM,KAAK,KAAKA,OAAM,KAAK,2DAAmB,CAAC,EAAE;AAAA,EACnD;AAEA,SAAO;AACT;;;AChKA,YAAY,cAAc;AAM1B,IAAM,aAAwC;AAAA,EAC5C,IAAI;AAAA,EACJ,GAAG;AAAA,EACH,GAAG;AAAA,EACH,MAAM;AAAA,EACN,GAAG;AAAA,EACH,GAAG;AAAA,EACH,MAAM;AAAA,EACN,GAAG;AAAA,EACH,GAAG;AAAA,EACH,OAAO;AAAA,EACP,GAAG;AAAA,EACH,GAAG;AACL;AAqBO,SAAS,WACd,KACA,QACA,WACc;AACd,EAAS,4BAAmB,QAAQ,KAAK;AAEzC,MAAI,QAAQ,MAAM,OAAO;AACvB,YAAQ,MAAM,WAAW,IAAI;AAAA,EAC/B;AAEA,QAAM,UAAU,CAAC,GAAY,QAAsB;AACjD,QAAI,CAAC,OAAO,CAAC,IAAI,KAAM;AAEvB,UAAM,EAAE,MAAM,KAAK,IAAI;AAGvB,QAAI,SAAS,SAAS,OAAO,SAAS,MAAM;AAC1C,aAAO;AACP;AAAA,IACF;AAGA,QAAI,SAAS,KAAK;AAChB,aAAO;AACP;AAAA,IACF;AAEA,UAAM,QAAQ,IAAI;AAGlB,QAAI,MAAM,YAAY;AACpB,UAAI,SAAS,KAAK;AAChB,YAAI,QAAQ,UAAU;AAAA,MACxB;AACA;AAAA,IACF;AAGA,QAAI,SAAS,KAAK;AAChB,YAAM,WAAW,CAAC,MAAM;AACxB;AAAA,IACF;AAGA,QAAI,SAAS,KAAK;AAChB,YAAM,WAAW,CAAC,MAAM;AACxB;AAAA,IACF;AAGA,UAAM,MAAM,WAAW,IAAI;AAC3B,QAAI,KAAK;AACP,sBAAgB,OAAO,GAAG;AAAA,IAC5B;AAAA,EACF;AAEA,UAAQ,MAAM,GAAG,YAAY,OAAO;AAEpC,SAAO,MAAM;AACX,YAAQ,MAAM,eAAe,YAAY,OAAO;AAChD,QAAI,QAAQ,MAAM,OAAO;AACvB,cAAQ,MAAM,WAAW,KAAK;AAAA,IAChC;AAAA,EACF;AACF;;;AClGA,IAAM,SAAqB;AAAA,EACzB,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,cAAc;AAAA,EACd,UAAU;AAAA,EACV,gBAAgB;AAAA,EAChB,eAAe;AACjB;AAGA,IAAM,gBAAgB;AAEtB,IAAM,YAAkB;AAAA,EACtB,IAAI;AAAA,EACJ,MAAM;AAAA,EACN,aAAa;AAAA,EACb,MAAM,YAAY;AAChB,UAAM,MAAoB,EAAE,OAAOC,oBAAmB,MAAM,EAAE;AAE9D,WAAO,IAAI,QAAc,CAACC,aAAY;AACpC,YAAM,eAAe;AAAA,QACnB;AAAA,QACA,MAAM;AAEJ,iBAAO;AACP,uBAAa,OAAO;AACpB,qBAAW;AACX,uBAAa;AACb,UAAAA,SAAQ;AAAA,QACV;AAAA,QACA,MAAM;AAEJ,iBAAOD,oBAAmB,MAAM;AAAA,QAClC;AAAA,MACF;AAGA,kBAAY;AACZ,iBAAW;AAEX,UAAI,OAAO;AACX,UAAI;AAEJ,eAAS,OAAa;AACpB,YAAI,KAAM;AAEV,cAAM,IAAI,IAAI;AAEd,YAAI,CAAC,EAAE,cAAc,CAAC,EAAE,UAAU;AAChC,eAAK,GAAG,MAAM;AAAA,QAChB;AAEA,yBAAiB,GAAG,MAAM;AAG1B,cAAM,WAAW,EAAE,cAAc,EAAE,WAAW,gBAAgB,EAAE;AAChE,kBAAU,WAAW,MAAM,QAAQ;AAAA,MACrC;AAEA,gBAAU,WAAW,MAAM,CAAC;AAAA,IAC9B,CAAC;AAAA,EACH;AACF;AAEA,IAAO,gBAAQ;;;AChER,SAAS,YAAoB;AAClC,eAAa,qBAAY;AACzB,eAAa,mBAAU;AACvB,eAAa,aAAK;AAClB,SAAO,UAAU;AACnB;;;AhDEA,SAAS,UAAAE,eAAc;AACvB,OAAOC,YAAW;;;AiDflB,SAAS,OAAAC,OAAK,QAAAC,QAAM,YAAAC,iBAAgB;AACpC,SAAS,YAAAC,WAAU,eAAAC,cAAa,aAAAC,kBAAiB;AACjD,OAAO,gBAAgB;AACvB,OAAO,QAAQ;AACf,SAAS,QAAAC,aAAY;AAyWX,gBAAAC,OAiBF,QAAAC,cAjBE;AAjWV,IAAM,gBAAgBC,MAAK,GAAG,QAAQ,GAAG,YAAY,eAAe;AAMpE,SAAS,UAAU,GAAmB;AACpC,QAAM,OAAO,EAAE,QAAQ,OAAO,GAAG;AACjC,MAAI,QAAQ,aAAa,SAAS;AAChC,WAAO,YAAY,KAAK,QAAQ,gBAAgB,KAAK;AAAA,EACvD;AACA,SAAO,YAAY;AACrB;AAOA,SAAS,SAAS,KAAa,MAAsB;AACnD,SAAO,WAAW,GAAG,SAAS,IAAI;AACpC;AAOA,IAAM,aAAa;AAWnB,SAAS,iBAAiB,MAAsB;AAC9C,MAAI,KAAK,WAAW,IAAI,KAAK,KAAK,WAAW,IAAI,EAAG,QAAO;AAC3D,MAAI,cAAc,KAAK,IAAI,EAAG,QAAO,OAAO;AAC5C,MAAI,cAAc,KAAK,IAAI,EAAG,QAAO,OAAO;AAC5C,SAAO,OAAO;AAChB;AAMA,eAAe,iBAAiB,MAItB;AACR,QAAM,MAAM,WAAW,QAAQ,UAAU,iBAAiB,IAAI,CAAC;AAC/D,MAAI;AACF,UAAM,OAAO,MAAM,MAAM,GAAG;AAC5B,UAAM,OAAQ,MAAM,KAAK,KAAK;AAC9B,QAAI,KAAK,SAAS,EAAG,QAAO;AAE5B,UAAM,WAAW,iBAAiB,IAAI;AACtC,UAAM,YAAY,KAAK,OAAO,QAAQ;AACtC,QAAI,CAAC,UAAW,QAAO;AAGvB,UAAM,aAAa,UAAU,MAAM,QAAQ,CAAC;AAC5C,UAAM,SAAmB,CAAC;AAC1B,eAAW,QAAQ,YAAY;AAE7B,YAAM,QAAS,KAAgB,MAAM,GAAG;AACxC,UAAI,MAAM,UAAU,GAAG;AACrB,cAAM,IAAI,WAAW,MAAM,CAAC,CAAE;AAC9B,YAAI,CAAC,MAAM,CAAC,EAAG,QAAO,KAAK,CAAC;AAAA,MAC9B;AAAA,IACF;AAGA,UAAM,QAAQ;AACd,UAAM,KAAK,UAAU,KAAK,KAAK;AAC/B,QAAI,QAAyB;AAC7B,QAAI,MAAM,GAAG,UAAU,IAAI;AACzB,cAAQ;AAAA,QACN;AAAA,QACA,MAAM,GAAG,CAAC,KAAK;AAAA,QACf,OAAO,WAAW,GAAG,CAAC,KAAK,GAAG;AAAA,QAC9B,eAAe,WAAW,GAAG,EAAE,KAAK,GAAG;AAAA,QACvC,cAAc,WAAW,GAAG,EAAE,KAAK,GAAG;AAAA,QACtC,MAAM,WAAW,GAAG,EAAE,KAAK,GAAG;AAAA,QAC9B,KAAK,WAAW,GAAG,EAAE,KAAK,GAAG;AAAA,QAC7B,QAAQ,SAAS,GAAG,CAAC,KAAK,KAAK,EAAE;AAAA;AAAA,QAEjC,QAAQ,WAAW,GAAG,EAAE,KAAK,GAAG,IAAI;AAAA,MACtC;AAAA,IACF;AAEA,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA,MAAM,UAAU,MAAM,QAAQ;AAAA,IAChC;AAAA,EACF,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAOA,IAAM,cAAc,oBAAI,IAAsB;AAE9C,SAAS,YAAY,MAAc,QAAwB;AACzD,cAAY,IAAI,MAAM,MAAM;AAC9B;AAeA,IAAM,kBAA8B;AAAA;AAAA,EAElC,EAAE,MAAM,UAAU,MAAM,4BAAQ,OAAO,MAAS,eAAe,MAAM,cAAc,OAAO,MAAM,MAAS,KAAK,MAAS,QAAQ,SAAW,QAAQ,OAAkB;AAAA;AAAA,EAEpK,EAAE,MAAM,UAAU,MAAM,4BAAQ,OAAO,MAAO,eAAe,MAAM,cAAc,MAAM,MAAM,OAAO,KAAK,MAAO,QAAQ,UAAU,QAAQ,OAAe;AAAA,EACzJ,EAAE,MAAM,UAAU,MAAM,4BAAQ,OAAO,MAAS,eAAe,OAAO,cAAc,MAAO,MAAM,MAAS,KAAK,MAAS,QAAQ,QAAU,QAAQ,OAAe;AACnK;AAMA,eAAe,YAAY,OAAsC;AAC/D,QAAM,UAAU,MAAM,QAAQ;AAAA,IAC5B,MAAM,IAAI,OAAO,SAAS;AACxB,YAAM,OAAO,MAAM,iBAAiB,IAAI;AACxC,UAAI,MAAM,OAAO;AAEf,YAAI,KAAK,OAAO,SAAS,EAAG,aAAY,MAAM,KAAK,MAAM;AACzD,eAAO,KAAK;AAAA,MACd;AACA,aAAO;AAAA,IACT,CAAC;AAAA,EACH;AAGA,QAAM,OAAmB,QAAQ,OAAO,CAAC,MAAqB,MAAM,IAAI;AACxE,MAAI,KAAK,SAAS,EAAG,QAAO;AAG5B,SAAO;AACT;AAMA,SAAS,YAAY,GAAmB;AACtC,SAAO,KAAK,MAAM,EAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;AAC9C;AAWA,SAAS,aAAa,MAAsB;AAC1C,MAAI,QAAQ,KAAmB,SAAQ,OAAO,MAAmB,QAAQ,CAAC,IAAI;AAC9E,MAAI,QAAQ,IAAa,SAAQ,OAAO,KAAa,QAAQ,CAAC,IAAI;AAClE,MAAI,QAAQ,IAAQ,SAAQ,OAAO,KAAQ,QAAQ,CAAC,IAAI;AACxD,SAAO,KAAK,eAAe;AAC7B;AAOA,SAAS,aAAa,MAAgB,YAAY,IAAc;AAC9D,MAAI,KAAK,UAAU,UAAW,QAAO;AACrC,SAAO,KAAK,MAAM,KAAK,SAAS,SAAS;AAC3C;AAeO,SAAS,UAAU,EAAE,OAAO,QAAQ,aAAa,GAAmB;AACzE,QAAM,CAAC,QAAQ,SAAS,IAAIC,UAAqB,CAAC,CAAC;AACnD,QAAM,CAAC,eAAe,gBAAgB,IAAIA,UAAS,CAAC;AACpD,QAAM,CAAC,SAAS,UAAU,IAAIA,UAAS,IAAI;AAC3C,QAAM,CAAC,YAAY,aAAa,IAAIA,UAAiB,EAAE;AACvD,QAAM,CAAC,YAAY,aAAa,IAAIA,UAA0B,IAAI;AAClE,QAAM,CAAC,cAAc,eAAe,IAAIA,UAA0B,IAAI;AACtE,QAAM,CAAC,eAAe,gBAAgB,IAAIA,UAAS,KAAK;AACxD,QAAM,CAAC,iBAAiB,kBAAkB,IAAIA,UAAS,EAAE;AACzD,QAAM,CAAC,WAAW,YAAY,IAAIA,UAAS,CAAC;AAC5C,QAAM,CAAC,aAAa,cAAc,IAAIA;AAAA,IAAiB,OACrD,oBAAI,KAAK,GAAE,mBAAmB,SAAS,EAAE,QAAQ,MAAM,CAAC;AAAA,EAC1D;AAEA,QAAM,CAAC,SAAS,UAAU,IAAIA,UAAS,KAAK;AAE5C,QAAM,EAAE,aAAa,YAAY,IAAI,eAAe,MAAM;AAG1D,EAAAC,WAAU,MAAM;AACd,UAAM,QAAQ,YAAY,MAAM;AAC9B,sBAAe,oBAAI,KAAK,GAAE,mBAAmB,SAAS,EAAE,QAAQ,MAAM,CAAC,CAAC;AAAA,IAC1E,GAAG,GAAI;AACP,WAAO,MAAM,cAAc,KAAK;AAAA,EAClC,GAAG,CAAC,CAAC;AAGL,QAAM,WAAWC,aAAY,YAAY;AACvC,eAAW,IAAI;AACf,QAAI;AACF,YAAM,OAAO,MAAM,YAAY,SAAS,CAAC,CAAC;AAC1C,gBAAU,IAAI;AACd,qBAAc,oBAAI,KAAK,GAAE,mBAAmB,CAAC;AAAA,IAC/C,QAAQ;AAAA,IAER;AACA,eAAW,KAAK;AAAA,EAClB,GAAG,CAAC,KAAK,CAAC;AAGV,EAAAD,WAAU,MAAM;AACd,aAAS;AAAA,EACX,GAAG,CAAC,QAAQ,CAAC;AAGb,EAAAA,WAAU,MAAM;AACd,UAAM,WAAW,YAAY,MAAM;AACjC,mBAAa,CAAC,SAAS;AACrB,YAAI,QAAQ,GAAG;AACb,mBAAS;AACT,iBAAO;AAAA,QACT;AACA,eAAO,OAAO;AAAA,MAChB,CAAC;AAAA,IACH,GAAG,GAAI;AACP,WAAO,MAAM,cAAc,QAAQ;AAAA,EACrC,GAAG,CAAC,QAAQ,CAAC;AAGb,EAAAA,WAAU,MAAM;AACd,QAAI,CAAC,YAAY;AACf,sBAAgB,IAAI;AACpB,uBAAiB,KAAK;AACtB;AAAA,IACF;AAEA,UAAM,aAAa,MAAM;AAEvB,kBAAY,OAAO,WAAW,IAAI;AAClC,uBAAiB,WAAW,IAAI,EAAE,KAAK,CAAC,SAAS;AAC/C,YAAI,QAAQ,KAAK,OAAO,SAAS,GAAG;AAClC,sBAAY,WAAW,MAAM,KAAK,MAAM;AACxC,0BAAgB,KAAK,MAAM;AAAA,QAC7B;AAAA,MACF,CAAC;AAAA,IACH;AAGA,eAAW;AAGX,uBAAmB,EAAE;AAGrB,UAAM,QAAQ,YAAY,YAAY,GAAK;AAC3C,WAAO,MAAM,cAAc,KAAK;AAAA,EAClC,GAAG,CAAC,UAAU,CAAC;AAGf,EAAAA,WAAU,MAAM;AACd,QAAI,CAAC,WAAY;AACjB,UAAM,QAAQ,YAAY,MAAM;AAC9B,yBAAmB,CAAC,SAAU,OAAO,IAAI,OAAO,IAAI,EAAG;AAAA,IACzD,GAAG,GAAI;AACP,WAAO,MAAM,cAAc,KAAK;AAAA,EAClC,GAAG,CAAC,UAAU,CAAC;AAGf,EAAAE;AAAA,IACED;AAAA,MACE,CAAC,OAAO,QAAQ;AAEd,YAAI,UAAU,OAAO,IAAI,MAAM;AAC7B,sBAAY;AACZ;AAAA,QACF;AAEA,YAAI,YAAY;AAEd,cAAI,IAAI,UAAU,UAAU,OAAO,UAAU,KAAK;AAChD,0BAAc,IAAI;AAAA,UACpB;AACA;AAAA,QACF;AAEA,YAAI,OAAO,WAAW,EAAG;AAEzB,YAAI,IAAI,WAAW,UAAU,KAAK;AAChC,2BAAiB,CAAC,SAAU,OAAO,IAAI,OAAO,IAAI,OAAO,SAAS,CAAE;AAAA,QACtE,WAAW,IAAI,aAAa,UAAU,KAAK;AACzC,2BAAiB,CAAC,SAAU,OAAO,OAAO,SAAS,IAAI,OAAO,IAAI,CAAE;AAAA,QACtE,WAAW,IAAI,QAAQ;AACrB,gBAAM,QAAQ,OAAO,aAAa;AAClC,cAAI,MAAO,eAAc,KAAK;AAAA,QAChC,WAAW,IAAI,UAAU,UAAU,KAAK;AACtC,cAAI,aAAc,cAAa;AAAA,cAC1B,QAAO;AAAA,QACd,WAAW,UAAU,KAAK;AACxB,uBAAa,CAAC;AACd,mBAAS;AAAA,QACX,WAAW,UAAU,KAAK;AACxB,qBAAW,CAAC,MAAM,CAAC,CAAC;AAAA,QACtB;AAAA,MACF;AAAA,MACA,CAAC,QAAQ,eAAe,YAAY,QAAQ,cAAc,UAAU,aAAa,UAAU;AAAA,IAC7F;AAAA,EACF;AAGA,MAAI,YAAY;AACd,QAAI,eAAe;AACjB,aACE,gBAAAE,MAACC,OAAA,EAAI,aAAa,GAChB,0BAAAD,MAACE,QAAA,EAAK,UAAQ,MAAE,8DAAgB,GAClC;AAAA,IAEJ;AACA,WAAO,aAAa,YAAY,MAAM,cAAc,IAAI,GAAG,gBAAgB,QAAW,iBAAiB,WAAW;AAAA,EACpH;AAIA,QAAMC,MAAK,CAAC,MAAc,UAAU,EAAE,UAAU,KAAK,IAAI,EAAE,OAAO,EAAE;AACpE,SACE,gBAAAC,OAACH,OAAA,EAAI,eAAc,UAEjB;AAAA,oBAAAG,OAACH,OAAA,EAAI,cAAc,GAAG,gBAAe,iBACnC;AAAA,sBAAAD,MAACE,QAAA,EAAK,MAAI,MAAE,GAAGC,IAAG,SAAS,GACxB,wDACH;AAAA,MACA,gBAAAC,OAACF,QAAA,EAAK,UAAQ,MACX;AAAA;AAAA,QAAS;AAAA,SACZ;AAAA,MACA,gBAAAF,MAACE,QAAA,EAAK,UAAQ,MACX,oBAAU,mCAAe,KAAK,SAAS,oCAC1C;AAAA,OACF;AAAA,IAGA,gBAAAE,OAACH,OAAA,EACC;AAAA,sBAAAD,MAACC,OAAA,EAAI,OAAO,GAAG;AAAA,MACf,gBAAAD,MAACC,OAAA,EAAI,OAAO,GACV,0BAAAD,MAACE,QAAA,EAAK,UAAQ,MAAC,0BAAE,GACnB;AAAA,MACA,gBAAAF,MAACC,OAAA,EAAI,OAAO,IACV,0BAAAD,MAACE,QAAA,EAAK,UAAQ,MAAC,0BAAE,GACnB;AAAA,MACA,gBAAAF,MAACC,OAAA,EAAI,OAAO,IACV,0BAAAD,MAACE,QAAA,EAAK,UAAQ,MAAC,gCAAG,GACpB;AAAA,MACA,gBAAAF,MAACC,OAAA,EAAI,OAAO,IACV,0BAAAD,MAACE,QAAA,EAAK,UAAQ,MAAC,gCAAG,GACpB;AAAA,MACA,gBAAAF,MAACC,OAAA,EAAI,OAAO,IACV,0BAAAD,MAACE,QAAA,EAAK,UAAQ,MAAC,gCAAG,GACpB;AAAA,MACA,gBAAAF,MAACC,OAAA,EAAI,OAAO,IACV,0BAAAD,MAACE,QAAA,EAAK,UAAQ,MAAC,0BAAE,GACnB;AAAA,MACA,gBAAAF,MAACC,OAAA,EAAI,OAAO,IACV,0BAAAD,MAACE,QAAA,EAAK,UAAQ,MAAC,0BAAE,GACnB;AAAA,MACA,gBAAAF,MAACC,OAAA,EACC,0BAAAD,MAACE,QAAA,EAAK,UAAQ,MAAC,gCAAG,GACpB;AAAA,OACF;AAAA,IAGA,gBAAAF,MAACC,OAAA,EACC,0BAAAD,MAACE,QAAA,EAAK,UAAQ,MAAE,iBAAO,SAAI,OAAO,GAAG,GAAE,GACzC;AAAA,IAGA,gBAAAF,MAACC,OAAA,EAAI,eAAc,UAChB,iBAAO,IAAI,CAAC,OAAO,UAAU;AAC5B,YAAM,aAAa,UAAU;AAC7B,YAAM,OAAO,MAAM,iBAAiB;AACpC,YAAM,QAAQ,OAAO,YAAY;AAEjC,aACE,gBAAAG,OAACH,OAAA,EACC;AAAA,wBAAAD,MAACC,OAAA,EAAI,OAAO,GAAG,YAAY,GACxB,uBACC,gBAAAD,MAACE,QAAA,EAAK,MAAI,MAAE,GAAGC,IAAG,SAAS,GACxB,qBACH,IAEA,gBAAAH,MAACE,QAAA,EAAM,gBAAK,GAEhB;AAAA,QACA,gBAAAF,MAACC,OAAA,EAAI,OAAO,GACV,0BAAAD,MAACE,QAAA,EAAK,MAAI,MAAE,GAAGC,IAAG,aAAa,YAAY,SAAS,GACjD,gBAAM,MACT,GACF;AAAA,QACA,gBAAAH,MAACC,OAAA,EAAI,OAAO,IACV,0BAAAD,MAACE,QAAA,EAAM,GAAGC,IAAG,aAAa,YAAY,SAAS,GAC5C,gBAAM,MACT,GACF;AAAA,QACA,gBAAAH,MAACC,OAAA,EAAI,OAAO,IACV,0BAAAD,MAACE,QAAA,EAAK,MAAI,MAAE,GAAGC,IAAG,KAAK,GACpB,sBAAY,MAAM,KAAK,GAC1B,GACF;AAAA,QACA,gBAAAH,MAACC,OAAA,EAAI,OAAO,IACV,0BAAAG,OAACF,QAAA,EAAM,GAAGC,IAAG,KAAK,GACf;AAAA,iBAAO,MAAM;AAAA,UAAI,MAAM,cAAc,QAAQ,CAAC;AAAA,UAAE;AAAA,WACnD,GACF;AAAA,QACA,gBAAAH,MAACC,OAAA,EAAI,OAAO,IACV,0BAAAG,OAACF,QAAA,EAAM,GAAGC,IAAG,KAAK,GACf;AAAA,iBAAO,MAAM;AAAA,UAAI,MAAM,aAAa,QAAQ,CAAC;AAAA,WAChD,GACF;AAAA,QACA,gBAAAH,MAACC,OAAA,EAAI,OAAO,IACV,0BAAAD,MAACE,QAAA,EAAM,GAAGC,IAAG,SAAS,GACnB,sBAAY,MAAM,IAAI,GACzB,GACF;AAAA,QACA,gBAAAH,MAACC,OAAA,EAAI,OAAO,IACV,0BAAAD,MAACE,QAAA,EAAM,GAAGC,IAAG,SAAS,GACnB,sBAAY,MAAM,GAAG,GACxB,GACF;AAAA,QACA,gBAAAH,MAACC,OAAA,EACC,0BAAAD,MAACE,QAAA,EAAM,GAAGC,IAAG,SAAS,GACnB,uBAAa,MAAM,MAAM,GAC5B,GACF;AAAA,WAjDQ,MAAM,IAkDhB;AAAA,IAEJ,CAAC,GACH;AAAA,IAGA,gBAAAH,MAACC,OAAA,EAAI,WAAW,GACd,0BAAAD,MAACE,QAAA,EAAK,UAAQ,MACX,uIACH,GACF;AAAA,IACA,gBAAAE,OAACH,OAAA,EACC;AAAA,sBAAAD,MAACE,QAAA,EAAK,UAAQ,MAAE,yCAAW,UAAU,sCAAY;AAAA,MACjD,gBAAAF,MAACE,QAAA,EAAM,GAAGC,IAAG,SAAS,GACnB,mBAAS,UAAU,aAAa,GAAG,aAAa,GACnD;AAAA,OACF;AAAA,IAGC,eACC,gBAAAH,MAACC,OAAA,EAAI,WAAW,GACd,0BAAAD,MAACE,QAAA,EAAK,MAAI,MAAE,GAAGC,IAAG,SAAS,GACxB,2EACH,GACF;AAAA,KAEJ;AAEJ;AAMA,SAAS,aACP,OACA,SACA,QACA,YAAY,IACZ,aACA;AACA,QAAM,OAAO,MAAM,iBAAiB;AACpC,QAAM,YAAY,OAAO,YAAY;AACrC,QAAM,QAAQ,OAAO,WAAM;AAG3B,MAAI,aAAuB,CAAC;AAC5B,MAAI,UAAU,OAAO,SAAS,GAAG;AAC/B,UAAM,aAAa,OAAO,WAAW,MAAM,WAAW;AAEtD,UAAM,SAAS,aAAa,QAAQ,EAAE;AACtC,QAAI,MAAM,WAAW,KAAK,QAAQ;AAAA,MAChC,QAAQ;AAAA,MACR,QAAQ,CAAC,UAAU;AAAA,IACrB,CAAC;AAED,UAAM,IACH,WAAW,UAAK,QAAG,EACnB,WAAW,UAAK,QAAG,EACnB,WAAW,UAAK,QAAG,EACnB,WAAW,UAAK,QAAG;AACtB,iBAAa,IAAI,MAAM,IAAI;AAAA,EAC7B;AAEA,SACE,gBAAAC,OAACH,OAAA,EAAI,eAAc,UAAS,aAAa,GAEvC;AAAA,oBAAAG,OAACH,OAAA,EAAI,cAAc,GAAG,gBAAe,iBACnC;AAAA,sBAAAG,OAACH,OAAA,EACC;AAAA,wBAAAG,OAACF,QAAA,EAAK,MAAI,MAAC,OAAM,WACd;AAAA;AAAA,UAAS,MAAM;AAAA,UAAM;AAAA,WACxB;AAAA,QACA,gBAAAF,MAACE,QAAA,EAAK,UAAQ,MACX,gBAAM,MACT;AAAA,QACC,eACC,gBAAAE,OAACF,QAAA,EAAK,UAAQ,MACX;AAAA;AAAA,UAAS;AAAA,WACZ;AAAA,SAEJ;AAAA,MACA,gBAAAF,MAACE,QAAA,EAAK,UAAQ,MACX,aAAG,SAAS,wBACf;AAAA,OACF;AAAA,IAGA,gBAAAE,OAACH,OAAA,EACC;AAAA,sBAAAD,MAACC,OAAA,EAAI,OAAO,IACV,0BAAAD,MAACE,QAAA,EAAK,MAAI,MAAC,OAAM,WAAU,gCAAG,GAChC;AAAA,MACA,gBAAAF,MAACC,OAAA,EACC,0BAAAG,OAACF,QAAA,EAAK,MAAI,MAAC,OAAO,WACf;AAAA;AAAA,QAAM;AAAA,QAAE,YAAY,MAAM,KAAK;AAAA,SAClC,GACF;AAAA,OACF;AAAA,IACA,gBAAAE,OAACH,OAAA,EACC;AAAA,sBAAAD,MAACC,OAAA,EAAI,OAAO,IACV,0BAAAD,MAACE,QAAA,EAAK,OAAM,WAAU,gCAAG,GAC3B;AAAA,MACA,gBAAAF,MAACC,OAAA,EACC,0BAAAG,OAACF,QAAA,EAAK,OAAO,WACV;AAAA,eAAO,MAAM;AAAA,QAAI,MAAM,cAAc,QAAQ,CAAC;AAAA,QAAE;AAAA,QAChD;AAAA,QACA,OAAO,MAAM;AAAA,QAAI,MAAM,aAAa,QAAQ,CAAC;AAAA,SAChD,GACF;AAAA,OACF;AAAA,IAGC,WAAW,SAAS,KACnB,gBAAAF,MAACC,OAAA,EAAI,WAAW,GAAG,eAAc,UAC9B,qBAAW,IAAI,CAAC,MAAM,MACrB,gBAAAD,MAACC,OAAA,EACC,0BAAAD,MAACE,QAAA,EAAK,OAAO,WACV,kBAAQ,KACX,KAHQ,CAIV,CACD,GACH;AAAA,IAIF,gBAAAF,MAACC,OAAA,EAAI,WAAW,GACd,0BAAAD,MAACE,QAAA,EAAK,UAAQ,MACX,gDACH,GACF;AAAA,KACF;AAEJ;;;AChmBA,SAAS,WAAAG,gBAAe;AACxB,SAAS,QAAAC,OAAM,YAAAC,iBAAgB;AAG/B,IAAM,cAAc,oBAAI,IAAI;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAGD,IAAM,cAAc,oBAAI,IAAI;AAAA,EAC1B;AAAA,EAAO;AAAA,EAAQ;AAAA,EAAO;AAAA,EAAQ;AAAA,EAAQ;AAAA,EACtC;AAAA,EAAQ;AAAA,EAAW;AAAA,EACnB;AAAA,EAAQ;AAAA,EAAS;AAAA,EACjB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAAS;AAAA,EACT;AACF,CAAC;AAeD,eAAsB,iBACpB,SACA,KACmB;AACnB,QAAM,aAAa,OAAO;AAE1B,MAAI;AACJ,MAAI;AACF,cAAU,MAAMF,SAAQ,YAAY,EAAE,eAAe,KAAK,CAAC;AAAA,EAC7D,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AAEA,QAAM,QAAkB,CAAC;AACzB,QAAM,OAAiB,CAAC;AAExB,aAAW,SAAS,SAAS;AAC3B,UAAM,OAAO,MAAM;AACnB,QAAI,YAAY,IAAI,IAAI,KAAK,KAAK,WAAW,GAAG,EAAG;AAEnD,QAAI,MAAM,YAAY,GAAG;AACvB,WAAK,KAAK,IAAI;AAAA,IAChB,WAAW,MAAM,OAAO,GAAG;AACzB,YAAM,MAAM,KAAK,MAAM,KAAK,YAAY,GAAG,CAAC,EAAE,YAAY;AAC1D,UAAI,YAAY,IAAI,GAAG,GAAG;AACxB,cAAM,KAAKE,UAAS,SAASD,MAAK,YAAY,IAAI,CAAC,CAAC;AAAA,MACtD;AAAA,IACF;AAAA,EACF;AAGA,QAAM,SAAS,MAAM,QAAQ;AAAA,IAC3B,KAAK,IAAI,CAAC,MAAM,iBAAiB,SAASA,MAAK,YAAY,CAAC,CAAC,CAAC;AAAA,EAChE;AAEA,aAAW,KAAK,QAAQ;AACtB,UAAM,KAAK,GAAG,CAAC;AAAA,EACjB;AAEA,SAAO;AACT;;;AlDYc,gBAAAE,aAAA;AA7Fd,IAAM,cAAc,CAAC,QAAQ,OAAO,SAAS,QAAQ,cAAc,QAAQ,OAAO;AAE3E,SAAS,YAAqB;AACnC,QAAMC,WAAU,IAAI,QAAQ;AAC5B,EAAAA,SAAQ,aAAa;AAErB,EAAAA,SACG,KAAK,SAAS,EACd,YAAY,kFAA2B,EACvC,QAAQ,SAAS,iBAAiB,gCAAO,EACzC,OAAO,aAAa,kDAAU,EAC9B,OAAO,mBAAmB,kDAAU;AAEvC,EAAAA,SAAQ,kBAAkB,MAAM,WAAWA,QAAO;AAElD,EAAAA,SAAQ,KAAK,aAAa,OAAO,aAAa,kBAAkB;AAC9D,UAAM,MAAM,MAAM,qBAAqB,KAAK,WAAW;AACvD,IAAC,cAAqD,aAAa;AAAA,EACrE,CAAC;AAGD,EAAAA,SACG,QAAQ,MAAM,EACd,YAAY,wDAAW,EACvB,OAAO,iBAAkB;AACxB,QAAI,CAAC,QAAQ,MAAM,OAAO;AACxB,cAAQ,MAAM,+JAAiD;AAC/D,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,QAAI,MAAO,KAA4C;AAGvD,QAAI,OAAO,CAAC,UAAU,IAAI,OAAO,SAAS,GAAG;AAC3C,YAAM,MAAM,MAAM,gBAAgB;AAClC,UAAI,CAAC,IAAK,SAAQ,KAAK,CAAC;AAGxB,YAAM,YAAY,MAAM,WAAW,GAAG;AACtC,cAAQ,IAAI,KAAKC,OAAM,MAAM,QAAG,CAAC,qCAAiBA,OAAM,IAAI,SAAS,CAAC;AAAA,CAAI;AAG1E,YAAM,SAAS,MAAM,gBAAgB;AACrC,YAAM,EAAE,GAAG,KAAK,QAAQ,OAAO,OAAO;AAAA,IACxC;AAIA,UAAM,yBAAyB,QAAQ,IAAI,CAAC;AAG9C,UAAM,cAAc,IAAI,YAAY;AAAA,MAClC,aAAa,KAAK,OAAO,eAAe;AAAA,MACxC,kBAAkB,KAAK,OAAO,oBAAoB;AAAA,IACpD,CAAC;AAED,cAAU,KAAK,WAAW;AAAA,EAC5B,CAAC;AAED,iBAAe,UACb,KACA,aACA;AAEA,UAAM,CAAC,kBAAkB,iBAAiB,QAAQ,KAAK,IAAI,MAAM,QAAQ,IAAI;AAAA,MAC3E,mBAAmB;AAAA,MACnB,wBAAwB,QAAQ,IAAI,CAAC;AAAA,MACrC,aAAa,QAAQ,IAAI,CAAC;AAAA,MAC1B,iBAAiB,QAAQ,IAAI,CAAC;AAAA,IAChC,CAAC;AACD,UAAM,aAAa,mBAAmB;AAEtC,UAAM,kBAAkB,KAAK,OAAO,UAAU;AAAA,MAC5C,CAAC,MAAM,EAAE,UAAU,KAAK,OAAO,mBAAmB;AAAA,IACpD;AACA,UAAM,QAAQ,iBAAiB,SAAS;AACxC,UAAM,UAAU;AAAA,MACd,gBAAAF;AAAA,QAAC;AAAA;AAAA,UACC;AAAA,UACA;AAAA,UACA;AAAA,UACA,WAAW,KAAK,OAAO,MAAM,UAAU;AAAA,UACvC,SAAS,KAAK,WAAW;AAAA,UACzB,QAAQ,iBAAiB;AAAA,UACzB,SAAS,iBAAiB,WAAW;AAAA,UACrC;AAAA,UACA;AAAA,UACA,cAAc,MAAM;AAClB,oBAAQ,QAAQ;AAChB,yBAAa,MAAM;AACjB,wBAAU;AACV,oBAAM,QAAQ,UAAU;AACxB,oBAAM,EAAE,QAAQ,IAAIG;AAAA,gBAClB,gBAAAH;AAAA,kBAAC;AAAA;AAAA,oBACC;AAAA,oBACA,UAAU,OAAO,SAAe;AAC9B,8BAAQ;AACR,4BAAM,KAAK,KAAK;AAEhB,gCAAU,KAAK,WAAW;AAAA,oBAC5B;AAAA,oBACA,cAAc,MAAM;AAClB,8BAAQ;AACR,mCAAa,MAAM,UAAU,KAAK,WAAW,CAAC;AAAA,oBAChD;AAAA;AAAA,gBACF;AAAA,gBACA,EAAE,aAAa,MAAM;AAAA,cACvB;AAAA,YACF,CAAC;AAAA,UACH;AAAA,UACA,eAAe,MAAM;AACnB,oBAAQ,QAAQ;AAChB,yBAAa,MAAM;AAEjB,oBAAM,oBAAoB,KAAK,OAAO,OAAO,SAAS,IAAI,CAAC,MAAM,EAAE,IAAI,KAClE,CAAC,YAAY,YAAY,UAAU;AACxC,oBAAM,WAAW;AAAA,gBACf,gBAAAA;AAAA,kBAAC;AAAA;AAAA,oBACC,OAAO;AAAA,oBACP,cAAc,MAAM;AAClB,+BAAS,QAAQ;AACjB,mCAAa,MAAM,UAAU,KAAK,WAAW,CAAC;AAAA,oBAChD;AAAA,oBACA,QAAQ,MAAM,QAAQ,KAAK,CAAC;AAAA;AAAA,gBAC9B;AAAA,cACF;AAAA,YACF,CAAC;AAAA,UACH;AAAA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAGA,EAAAC,SACG,QAAQ,KAAK,EACb,YAAY,4CAAS,EACrB,SAAS,eAAe,0BAAM,EAC9B,OAAO,kBAAkB,4CAAS,EAClC,OAAO,eAAgB,SAAmB;AACzC,YAAQ,IAAI,iEAAyB;AAAA,EACvC,CAAC;AAGH,EAAAA,SACG,QAAQ,OAAO,EACf,YAAY,sCAAQ,EACpB,OAAO,YAAY,kDAAe,EAClC,OAAO,UAAU,yCAAgB,EACjC,OAAO,iBAAkB;AACxB,YAAQ,IAAI,mEAA2B;AAAA,EACzC,CAAC;AAGH,EAAAA,SACG,QAAQ,MAAM,EACd,YAAY,2GAA2B,EACvC,OAAO,iBAAkB;AACxB,YAAQ,IAAI,kEAA0B;AAAA,EACxC,CAAC;AAGH,EAAAA,SACG,QAAQ,YAAY,EACpB,YAAY,yFAA6B,EACzC,SAAS,WAAW,sBAAY,eAAe,EAC/C,OAAO,eAAgB,OAAgB;AACtC,QAAI,CAAC,OAAO;AACV,cAAQ,IAAI,kGAA+D;AAC3E;AAAA,IACF;AACA,QAAI,UAAU,QAAQ;AACpB,cAAQ,IAAI;AAAA;AAAA;AAAA;AAAA,gCAIY,YAAY,KAAK,GAAG,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,wCAKb;AAAA,IAClC,OAAO;AACL,cAAQ,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oCAcgB;AAAA,IAC9B;AAAA,EACF,CAAC;AAGH,EAAAA,SACG,QAAQ,OAAO,EACf,YAAY,wDAAW,EACvB,SAAS,cAAc,wFAA4B,EACnD,OAAO,eAAgB,OAAiB;AACvC,UAAM,MAAO,KAA4C;AAGzD,UAAM,OAAO,QAAQ,IAAI,QAAQ,QAAQ,IAAI,eAAe;AAC5D,UAAM,mBAAmBG,MAAK,MAAM,YAAY,eAAe;AAC/D,QAAI,uBAAuB;AAC3B,QAAI;AACF,YAAM,MAAM,MAAMC,WAAS,kBAAkB,OAAO;AACpD,YAAM,SAAS,KAAK,MAAM,GAAG;AAC7B,YAAM,QAAQ,OAAO;AACrB,6BAAuB,MAAM,QAAQ,OAAO,OAAO,KAAM,MAAO,QAAsB,SAAS;AAAA,IACjG,QAAQ;AAAA,IAER;AAEA,QAAI,CAAC,sBAAsB;AACzB,YAAM,iBAAiB;AAAA,QACrB,EAAE,MAAM,WAAW;AAAA,QACnB,EAAE,MAAM,WAAW;AAAA,QACnB,EAAE,MAAM,WAAW;AAAA,MACrB;AACA,YAAM,YAAY,MAAM,gBAAgB,cAAc;AACtD,cAAQ,IAAI,GAAGH,OAAM,MAAM,QAAG,CAAC,sDAAcA,OAAM,IAAI,SAAS,CAAC,EAAE;AACnE,cAAQ,IAAI,GAAGA,OAAM,IAAI,4GAAuB,CAAC;AAAA,CAAI;AAAA,IACvD;AAGA,UAAM,cAAc,MAAM,gBAAgB;AAC1C,UAAM,WAAW,SAAS,MAAM,SAAS,IACrC,QACA,YAAY,OAAO,OAAO,SAAS,IAAI,CAAC,MAAM,EAAE,IAAI,KACjD,CAAC,YAAY,YAAY,UAAU;AAE1C,UAAM,MAAM;AAAA,MACV,gBAAAF;AAAA,QAAC;AAAA;AAAA,UACC,OAAO;AAAA,UACP,QAAQ,MAAM,QAAQ,KAAK,CAAC;AAAA;AAAA,MAC9B;AAAA,IACF;AAEA,UAAM,IAAI;AAAA,EACZ,CAAC;AAGH,YAAU;AAEV,EAAAC,SACG,QAAQ,MAAM,EACd,YAAY,4CAAS,EACrB,SAAS,UAAU,8GAAoB,EACvC,OAAO,eAAgB,MAAe;AACrC,QAAI,MAAM;AACR,YAAM,OAAO,QAAQ,IAAI;AACzB,UAAI,CAAC,MAAM;AACT,gBAAQ,MAAM,mCAAU,IAAI,yFAA6B;AACzD,gBAAQ,KAAK,CAAC;AAAA,MAChB;AACA,cAAQ,IAAI,6BAAS,KAAK,IAAI,WAAM,KAAK,WAAW;AAAA,CAAI;AACxD,YAAM,KAAK,KAAK;AAAA,IAClB,OAAO;AACL,YAAM,QAAQ,UAAU;AACxB,UAAI,MAAM,WAAW,GAAG;AACtB,gBAAQ,IAAI,4CAAS;AACrB;AAAA,MACF;AAEA,YAAM,eAAe,MAAM,IAAI,QAAqB,CAACK,aAAY;AAC/D,cAAM,EAAE,QAAQ,IAAIH;AAAA,UAClB,gBAAAH;AAAA,YAAC;AAAA;AAAA,cACC;AAAA,cACA,UAAU,CAAC,SAAS;AAClB,wBAAQ;AACR,gBAAAM,SAAQ,IAAI;AAAA,cACd;AAAA,cACA,QAAQ,MAAM;AACZ,wBAAQ;AACR,gBAAAA,SAAQ,IAAI;AAAA,cACd;AAAA;AAAA,UACF;AAAA,UACA,EAAE,aAAa,MAAM;AAAA,QACvB;AAAA,MACF,CAAC;AAED,UAAI,cAAc;AAChB,gBAAQ,IAAI;AAAA,8BAAaJ,OAAM,MAAM,aAAa,IAAI,CAAC;AAAA,CAAI;AAC3D,cAAM,aAAa,KAAK;AAAA,MAC1B;AAAA,IACF;AAAA,EACF,CAAC;AAEH,SAAOD;AACT;;;AmD5TO,IAAM,WAAW;AAAA;AAAA,EAEtB,SAAS;AAAA;AAAA,EAET,eAAe;AAAA;AAAA,EAEf,cAAc;AAAA;AAAA,EAEd,QAAQ;AACV;;;ACGA,IAAI,cAAc;AAClB,IAAI,cAAoD;AAOxD,QAAQ,GAAG,UAAU,MAAM;AACzB;AACA,MAAI,eAAe,GAAG;AACpB,YAAQ,KAAK,SAAS,MAAM;AAAA,EAC9B;AACA,UAAQ,OAAO,MAAM,mEAAgC;AACrD,MAAI,YAAa,cAAa,WAAW;AACzC,gBAAc,WAAW,MAAM;AAC7B,kBAAc;AAAA,EAChB,GAAG,IAAI;AACT,CAAC;AAED,IAAM,UAAU,UAAU;AAE1B,IAAI;AACF,QAAM,QAAQ,WAAW,QAAQ,IAAI;AACvC,SAAS,KAAc;AACrB,QAAM,QAAQ;AAEd,MAAI,MAAM,SAAS,6BAA6B,MAAM,SAAS,qBAAqB;AAClF,YAAQ,KAAK,MAAM,YAAY,SAAS,OAAO;AAAA,EACjD;AAEA,MAAI,OAAO,MAAM,aAAa,UAAU;AACtC,YAAQ,KAAK,MAAM,QAAQ;AAAA,EAC7B;AAEA,UAAQ,MAAM,OAAO,GAAG,CAAC;AACzB,UAAQ,KAAK,SAAS,aAAa;AACrC;","names":["SUPPORTED_MODELS","program","chalk","resolve","createInterface","existsSync","mkdir","readFile","join","chalk","stat","createInterface","resolve","mkdir","join","existsSync","stat","chalk","readFile","readFile","join","Text","jsxs","Box","Text","jsx","Box","Text","useEffect","useState","useCallback","useRef","useState","useEffect","Box","Text","mkdir","readFile","writeFile","join","join","readFile","mkdir","writeFile","Box","Text","jsx","jsxs","jsx","jsxs","Box","Text","Box","Text","jsx","jsxs","Box","Text","jsx","jsxs","Box","Text","jsx","jsxs","HIGHLIGHT_COLOR","items","records","realpath","process","resolve","process","patch","writeFile","readFile","relative","readFile","relative","mkdir","readFile","relative","readFile","mkdir","relative","readFile","basename","readFile","basename","readFile","basename","readFile","basename","readFile","basename","readFile","basename","process","isWindows","process","readdir","stat","join","relative","isAbsolute","readdir","join","relative","isAbsolute","stat","readdir","readFile","stat","join","relative","isAbsolute","readdir","join","isAbsolute","stat","readFile","relative","readdir","stat","join","relative","readdir","stat","join","relative","jsx","jsxs","useState","useRef","useEffect","useCallback","DeepSeekProvider","calculateCost","Box","Text","Box","Text","useInput","useState","useCallback","jsx","jsxs","useState","useInput","useCallback","Box","Text","Box","Text","useInput","render","useState","useEffect","useRef","useCallback","jsx","jsxs","tick","s","resolve","Box","Text","useInput","render","useState","useEffect","useRef","useCallback","jsx","jsxs","CYBER_PALETTE","createInitialState","update","useRef","tick","useState","useEffect","useInput","useCallback","s","jsxs","Box","jsx","Text","resolve","render","createInitialState","chalk","createInitialState","resolve","render","chalk","Box","Text","useInput","useState","useCallback","useEffect","join","jsx","jsxs","join","useState","useEffect","useCallback","useInput","jsx","Box","Text","cp","jsxs","readdir","join","relative","jsx","program","chalk","render","join","readFile","resolve"]}
1
+ {"version":3,"sources":["../src/cli/index.tsx","../src/config/loader.ts","../src/cli/middleware.ts","../src/cli/help.ts","../src/cli/api-key-setup.ts","../src/cli/skill-import.ts","../src/ui/RenderScope.tsx","../src/ui/Spinner.tsx","../src/ui/StatusMessage.tsx","../src/ui/DskcodeSplash.tsx","../src/ui/ChatSession.tsx","../src/ui/useDoubleCtrlC.ts","../src/ui/AssistantMessage.tsx","../src/provider/cost-tracker.ts","../src/ui/ToolCallBlock.tsx","../src/agent/message-builder.ts","../src/ui/HighlightedText.tsx","../src/ui/DiffPreview.tsx","../src/ui/SkillSelector.tsx","../src/ui/FileSelector.tsx","../src/provider/registry.ts","../src/tool/types.ts","../src/agent/system-prompt.ts","../src/agent/prompts/system-prompt.hbs","../src/tool/registry.ts","../src/agent/index.ts","../src/tool/sandbox.ts","../src/tool/diff.ts","../src/tool/eol.ts","../src/tool/builtins/read-file.ts","../src/tool/builtins/write-file.ts","../src/tool/builtins/edit-file.ts","../src/tool/builtins/multi-edit.ts","../src/tool/builtins/delete-range.ts","../src/tool/builtins/bash.ts","../src/tool/builtins/glob.ts","../src/tool/builtins/grep.ts","../src/tool/builtins/ls.ts","../src/tool/builtins/fetch.ts","../src/tool/builtins/index.ts","../src/utils/gradient.ts","../src/ui/GamePicker.tsx","../src/game/index.ts","../src/game/brick-breaker/index.tsx","../src/game/coder-check/index.tsx","../src/game/snake/engine.ts","../src/game/snake/renderer.ts","../src/game/snake/input.ts","../src/game/snake/index.ts","../src/game/registry.ts","../src/stock/StockList.tsx","../src/utils/scan-files.ts","../src/cli/exit-codes.ts","../src/index.ts"],"sourcesContent":["import { Command } from \"commander\";\r\nimport { loadConfigMiddleware } from \"./middleware.js\";\r\nimport type { DskcodeContext } from \"./middleware.js\";\r\nimport { customHelp } from \"./help.js\";\r\nimport { hasApiKey, promptForApiKey } from \"./api-key-setup.js\";\r\nimport { promptImportClaudeSkills, countDskcodeSkills, countProjectLocalSkills, getAllSkills } from \"./skill-import.js\";\r\nimport { saveApiKey, loadAndValidate, saveStockConfig } from \"../config/index.js\";\r\nimport { readFile } from \"node:fs/promises\";\r\nimport { join } from \"node:path\";\r\nimport { renderApp, ChatSession } from \"../ui/index.js\";\r\nimport { initGames } from \"../game/registry.js\";\r\nimport { listGames, getGame } from \"../game/index.js\";\r\nimport type { Game } from \"../game/index.js\";\r\nimport { GamePicker } from \"../ui/GamePicker.js\";\r\nimport { render } from \"ink\";\r\nimport chalk from \"chalk\";\r\nimport { StockList } from \"../stock/index.js\";\r\nimport { CostTracker } from \"../provider/cost-tracker.js\";\r\nimport { scanProjectFiles } from \"../utils/scan-files.js\";\r\n\r\nconst SUBCOMMANDS = [\"chat\", \"run\", \"setup\", \"init\", \"completion\", \"game\", \"stock\"];\r\n\r\nexport function createCli(): Command {\r\n const program = new Command();\r\n program.exitOverride();\r\n\r\n program\r\n .name(\"dskcode\")\r\n .description(\"基于 DeepSeek 的 AI 编程助手终端工具\")\r\n .version(\"0.0.0\", \"-V, --version\", \"显示版本号\")\r\n .option(\"--verbose\", \"开启详细日志输出\")\r\n .option(\"--config <path>\", \"指定配置文件路径\");\r\n\r\n program.helpInformation = () => customHelp(program);\r\n\r\n program.hook(\"preAction\", async (thisCommand, actionCommand) => {\r\n const ctx = await loadConfigMiddleware.call(thisCommand);\r\n (actionCommand as unknown as Record<string, unknown>).dskcodeCtx = ctx;\r\n });\r\n\r\n // chat — 交互式对话\r\n program\r\n .command(\"chat\")\r\n .description(\"启动交互式对话会话\")\r\n .action(async function () {\r\n if (!process.stdin.isTTY) {\r\n console.error(\"dskcode chat 需要交互式终端。如需执行一次性任务,请使用 dskcode run。\");\r\n process.exit(1);\r\n }\r\n\r\n let ctx = (this as unknown as Record<string, unknown>).dskcodeCtx as DskcodeContext | undefined;\r\n\r\n // 检查 API Key,如果没有则交互式输入\r\n if (ctx && !hasApiKey(ctx.config.providers)) {\r\n const key = await promptForApiKey();\r\n if (!key) process.exit(1);\r\n\r\n // 保存到全局配置\r\n const savedPath = await saveApiKey(key);\r\n console.log(` ${chalk.green(\"✔\")} API Key 已保存到 ${chalk.dim(savedPath)}\\n`);\r\n\r\n // 重新加载配置,使新 Key 生效\r\n const result = await loadAndValidate();\r\n ctx = { ...ctx, config: result.config };\r\n }\r\n\r\n // API Key 检查之后:检测项目本地 skill 和 Claude Code skill\r\n // 如果 .dskcode/skill 下有 skill,则跳过导入 Claude Code skill\r\n await promptImportClaudeSkills(process.cwd());\r\n\r\n // 从配置创建 CostTracker\r\n const costTracker = new CostTracker({\r\n budgetLimit: ctx?.config.budgetLimit ?? 0,\r\n tokenBudgetLimit: ctx?.config.tokenBudgetLimit ?? 0,\r\n });\r\n\r\n startChat(ctx, costTracker);\r\n });\r\n\r\n async function startChat(\r\n ctx: DskcodeContext | undefined,\r\n costTracker: CostTracker,\r\n ) {\r\n // 统计 skill 数量并获取详情列表,以及扫描项目文件\r\n const [globalSkillCount, localSkillCount, skills, files] = await Promise.all([\r\n countDskcodeSkills(),\r\n countProjectLocalSkills(process.cwd()),\r\n getAllSkills(process.cwd()),\r\n scanProjectFiles(process.cwd()),\r\n ]);\r\n const skillCount = globalSkillCount + localSkillCount;\r\n // 从配置中提取默认 Provider 的 apiKey 和 baseUrl\r\n const defaultProvider = ctx?.config.providers.find(\r\n (p) => p.name === (ctx?.config.defaultProvider ?? \"deepseek\"),\r\n );\r\n const model = defaultProvider?.model ?? \"deepseek-v4-flash\";\r\n const chatApp = renderApp(\r\n <ChatSession\r\n skillCount={skillCount}\r\n skills={skills}\r\n files={files}\r\n toolCount={ctx?.config.tools.length ?? 0}\r\n verbose={ctx?.verbose ?? false}\r\n apiKey={defaultProvider?.apiKey}\r\n baseUrl={defaultProvider?.baseUrl ?? \"https://api.deepseek.com\"}\r\n costTracker={costTracker}\r\n model={model}\r\n onLaunchGame={() => {\r\n chatApp.unmount();\r\n setImmediate(() => {\r\n initGames();\r\n const games = listGames();\r\n const { unmount } = render(\r\n <GamePicker\r\n games={games}\r\n onSelect={async (game: Game) => {\r\n unmount();\r\n await game.play();\r\n // 游戏结束后返回对话\r\n startChat(ctx, costTracker);\r\n }}\r\n onBackToChat={() => {\r\n unmount();\r\n setImmediate(() => startChat(ctx, costTracker));\r\n }}\r\n />,\r\n { exitOnCtrlC: false },\r\n );\r\n });\r\n }}\r\n onLaunchStock={() => {\r\n chatApp.unmount();\r\n setImmediate(() => {\r\n // 使用配置中的自选股列表或兜底默认值\r\n const defaultStockCodes = ctx?.config.stock?.symbols?.map((s) => s.code)\r\n ?? [\"sh000001\", \"sz399006\", \"sh601688\"];\r\n const stockApp = renderApp(\r\n <StockList\r\n codes={defaultStockCodes}\r\n onBackToChat={() => {\r\n stockApp.unmount();\r\n setImmediate(() => startChat(ctx, costTracker));\r\n }}\r\n onExit={() => process.exit(0)}\r\n />,\r\n );\r\n });\r\n }}\r\n />,\r\n );\r\n }\r\n\r\n // run\r\n program\r\n .command(\"run\")\r\n .description(\"执行一次性任务\")\r\n .argument(\"[prompt...]\", \"任务描述\")\r\n .option(\"--model <name>\", \"指定使用的模型\")\r\n .action(async function (_prompt: string[]) {\r\n console.log(\"dskcode run — 待实现(第07章)\");\r\n });\r\n\r\n // setup\r\n program\r\n .command(\"setup\")\r\n .description(\"运行配置向导\")\r\n .option(\"--export\", \"以 JSON 格式导出配置\")\r\n .option(\"--test\", \"测试 API Key 连通性\")\r\n .action(async function () {\r\n console.log(\"dskcode setup — 待实现(第14章)\");\r\n });\r\n\r\n // init\r\n program\r\n .command(\"init\")\r\n .description(\"在当前项目下生成项目记忆文件(AGENTS.md)\")\r\n .action(async function () {\r\n console.log(\"dskcode init — 待实现(第11章)\");\r\n });\r\n\r\n // completion\r\n program\r\n .command(\"completion\")\r\n .description(\"输出 shell 自动补全配置说明(bash/zsh)\")\r\n .argument(\"[shell]\", \"shell 类型\", /^(bash|zsh)$/i)\r\n .action(async function (shell?: string) {\r\n if (!shell) {\r\n console.log(\"请指定 shell 类型:dskcode completion bash 或 dskcode completion zsh\");\r\n return;\r\n }\r\n if (shell === \"bash\") {\r\n console.log(`# dskcode bash 自动补全\r\n_dskcode_completion() {\r\n local cur=\\${COMP_WORDS[COMP_CWORD]}\r\n if [[ \\${COMP_CWORD} -eq 1 ]]; then\r\n COMPREPLY=( $(compgen -W \"${SUBCOMMANDS.join(\" \")}\" -- \"\\${cur}\") )\r\n return 0\r\n fi\r\n COMPREPLY=( $(compgen -W \"--verbose --config --model\" -- \"\\${cur}\") )\r\n}\r\ncomplete -F _dskcode_completion dskcode`);\r\n } else {\r\n console.log(`# dskcode zsh 自动补全\r\n_dskcode_completion() {\r\n local -a commands\r\n commands=(\r\n \"chat:启动交互式对话会话\"\r\n \"run:执行一次性任务\"\r\n \"setup:运行配置向导\"\r\n \"init:生成项目记忆文件\"\r\n \"completion:输出 shell 自动补全说明\"\r\n \"game:内置小游戏\"\r\n \"stock:查看自选股实时行情\"\r\n )\r\n _describe 'dskcode commands' commands\r\n}\r\ncompdef _dskcode_completion dskcode`);\r\n }\r\n });\r\n\r\n // stock — 股票行情\r\n program\r\n .command(\"stock\")\r\n .description(\"查看自选股实时行情\")\r\n .argument(\"[codes...]\", \"股票代码(空格分隔),如 513090 600519\")\r\n .action(async function (codes: string[]) {\r\n const ctx = (this as unknown as Record<string, unknown>).dskcodeCtx as DskcodeContext | undefined;\r\n\r\n // 检查用户全局配置文件是否已有自选股配置;没有则自动创建\r\n const home = process.env.HOME ?? process.env.USERPROFILE ?? \"~\";\r\n const globalConfigPath = join(home, \".dskcode\", \"settings.json\");\r\n let globalConfigHasStock = false;\r\n try {\r\n const raw = await readFile(globalConfigPath, \"utf-8\");\r\n const parsed = JSON.parse(raw) as Record<string, unknown>;\r\n const stock = parsed.stock as Record<string, unknown> | undefined;\r\n globalConfigHasStock = Array.isArray(stock?.symbols) && (stock!.symbols as unknown[]).length > 0;\r\n } catch {\r\n // 文件不存在 — 需要创建\r\n }\r\n\r\n if (!globalConfigHasStock) {\r\n const defaultSymbols = [\r\n { code: \"sh000001\" },\r\n { code: \"sz399300\" },\r\n { code: \"sh601899\" },\r\n ];\r\n const savedPath = await saveStockConfig(defaultSymbols);\r\n console.log(`${chalk.green(\"✔\")} 已生成自选股配置: ${chalk.dim(savedPath)}`);\r\n console.log(`${chalk.dim(\" 提示: 可编辑上述文件自定义自选股列表\")}\\n`);\r\n }\r\n\r\n // 加载配置(包含可能刚写入的自选股)\r\n const freshResult = await loadAndValidate();\r\n const codeList = codes && codes.length > 0\r\n ? codes\r\n : freshResult.config.stock?.symbols?.map((s) => s.code)\r\n ?? [\"sh000001\", \"sz399006\", \"sh601688\"];\r\n\r\n const app = renderApp(\r\n <StockList\r\n codes={codeList}\r\n onExit={() => process.exit(0)}\r\n />,\r\n );\r\n\r\n await app.waitUntilExit;\r\n });\r\n\r\n // game — 游戏模式\r\n initGames();\r\n\r\n program\r\n .command(\"game\")\r\n .description(\"启动内置小游戏\")\r\n .argument(\"[name]\", \"游戏名称,不指定则显示交互式游戏列表\")\r\n .action(async function (name?: string) {\r\n if (name) {\r\n const game = getGame(name);\r\n if (!game) {\r\n console.error(`未找到游戏 \"${name}\"。使用 dskcode game 查看可用游戏列表。`);\r\n process.exit(1);\r\n }\r\n console.log(`正在启动: ${game.name} — ${game.description}\\n`);\r\n await game.play();\r\n } else {\r\n const games = listGames();\r\n if (games.length === 0) {\r\n console.log(\"暂无可用游戏。\");\r\n return;\r\n }\r\n\r\n const selectedGame = await new Promise<Game | null>((resolve) => {\r\n const { unmount } = render(\r\n <GamePicker\r\n games={games}\r\n onSelect={(game) => {\r\n unmount();\r\n resolve(game);\r\n }}\r\n onExit={() => {\r\n unmount();\r\n resolve(null);\r\n }}\r\n />,\r\n { exitOnCtrlC: false },\r\n );\r\n });\r\n\r\n if (selectedGame) {\r\n console.log(`\\n 启动游戏: ${chalk.green(selectedGame.name)}\\n`);\r\n await selectedGame.play();\r\n }\r\n }\r\n });\r\n\r\n return program;\r\n}\r\n","import { existsSync, watch } from \"node:fs\";\r\nimport { mkdir, readFile, writeFile } from \"node:fs/promises\";\r\nimport { join } from \"node:path\";\r\nimport type { Config, ProviderConfig, ToolConfig, PluginConfig, StockConfig } from \"./types.js\";\r\n\r\n// ---------------------------------------------------------------------------\r\n// 出厂默认配置\r\n// ---------------------------------------------------------------------------\r\n\r\nexport const defaultConfig: Config = {\r\n defaultProvider: \"deepseek\",\r\n maxTokens: 8192,\r\n temperature: 0.7,\r\n maxToolRounds: 20,\r\n providers: [\r\n {\r\n name: \"deepseek\",\r\n baseUrl: \"https://api.deepseek.com\",\r\n model: \"deepseek-v4-flash\",\r\n },\r\n ],\r\n tools: [\r\n { name: \"read_file\", enabled: true },\r\n { name: \"write_file\", enabled: true },\r\n { name: \"edit_file\", enabled: true },\r\n { name: \"bash\", enabled: true },\r\n { name: \"glob\", enabled: true },\r\n { name: \"grep\", enabled: true },\r\n { name: \"ls\", enabled: true },\r\n { name: \"fetch\", enabled: true },\r\n ],\r\n plugins: [],\r\n stock: {\r\n symbols: [\r\n { code: \"sh000001\" },\r\n { code: \"sz399300\" },\r\n { code: \"sh601899\" },\r\n ],\r\n },\r\n};\r\n\r\n// ---------------------------------------------------------------------------\r\n// 配置文件路径解析\r\n// ---------------------------------------------------------------------------\r\n\r\n/** 判断一个字符串是否为合法的 URL(用于区分 local path 和 url)。\r\n * 目前暂未使用,保留以备 plugin 配置 URL 校验时使用。\r\n */\r\nfunction _isUrl(s: string): boolean {\r\n try {\r\n new URL(s);\r\n return true;\r\n } catch {\r\n return false;\r\n }\r\n}\r\n\r\n/**\r\n * 返回候选配置文件路径列表。\r\n * 若传入了 --config 路径,则只使用该路径;\r\n * 否则依次检查用户全局目录和项目本地目录。\r\n */\r\nfunction resolveConfigFiles(configPath?: string): string[] {\r\n if (configPath) {\r\n return [configPath];\r\n }\r\n\r\n const home = process.env.HOME ?? process.env.USERPROFILE ?? \"~\";\r\n return [\r\n join(home, \".dskcode\", \"settings.json\"),\r\n join(process.cwd(), \".dskcode\", \"settings.json\"),\r\n ];\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 深度合并\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * 将较高优先级的配置 overlay 合并到 base 之上。\r\n *\r\n * 合并规则:\r\n * - 标量字段(string / number / boolean):覆盖\r\n * - 数组字段(providers / tools / plugins):直接替换,不合并\r\n */\r\nfunction mergeConfig(base: Config, overlay: Partial<Config>): Config {\r\n const result: Config = { ...base };\r\n\r\n if (overlay.defaultProvider !== undefined) {\r\n result.defaultProvider = overlay.defaultProvider;\r\n }\r\n if (overlay.verbose !== undefined) {\r\n result.verbose = overlay.verbose;\r\n }\r\n if (overlay.maxTokens !== undefined) {\r\n result.maxTokens = overlay.maxTokens;\r\n }\r\n if (overlay.temperature !== undefined) {\r\n result.temperature = overlay.temperature;\r\n }\r\n if (overlay.maxToolRounds !== undefined) {\r\n result.maxToolRounds = overlay.maxToolRounds;\r\n }\r\n if (overlay.budgetLimit !== undefined) {\r\n result.budgetLimit = overlay.budgetLimit;\r\n }\r\n if (overlay.tokenBudgetLimit !== undefined) {\r\n result.tokenBudgetLimit = overlay.tokenBudgetLimit;\r\n }\r\n if (overlay.providers !== undefined) {\r\n result.providers = overlay.providers as ProviderConfig[];\r\n }\r\n if (overlay.tools !== undefined) {\r\n result.tools = overlay.tools as ToolConfig[];\r\n }\r\n if (overlay.plugins !== undefined) {\r\n result.plugins = overlay.plugins as PluginConfig[];\r\n }\r\n if (overlay.stock !== undefined) {\r\n result.stock = overlay.stock as StockConfig;\r\n }\r\n\r\n return result;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 环境变量解析\r\n// ---------------------------------------------------------------------------\r\n\r\n/** 环境变量前缀 */\r\nconst ENV_PREFIX = \"DSKCODE_\";\r\n\r\n/** 支持的环境变量映射表 */\r\nconst ENV_MAP: Record<string, keyof Config> = {\r\n [`${ENV_PREFIX}DEFAULT_PROVIDER`]: \"defaultProvider\",\r\n [`${ENV_PREFIX}VERBOSE`]: \"verbose\",\r\n [`${ENV_PREFIX}MAX_TOKENS`]: \"maxTokens\",\r\n [`${ENV_PREFIX}TEMPERATURE`]: \"temperature\",\r\n [`${ENV_PREFIX}MAX_TOOL_ROUNDS`]: \"maxToolRounds\",\r\n [`${ENV_PREFIX}BUDGET_LIMIT`]: \"budgetLimit\",\r\n [`${ENV_PREFIX}TOKEN_BUDGET_LIMIT`]: \"tokenBudgetLimit\",\r\n};\r\n\r\n/**\r\n * 将环境变量中读取的值覆盖到配置上。\r\n * 环境变量的优先级高于 TOML 文件,但低于 CLI flag。\r\n * 返回一个新的配置对象,不修改原始配置。\r\n */\r\nfunction applyEnvVars(config: Config): Config {\r\n // 浅拷贝以避免修改原始对象\r\n const result: Config = { ...config, providers: [...config.providers] };\r\n\r\n // 1. DSKCODE_* 前缀的环境变量\r\n for (const [envKey, configKey] of Object.entries(ENV_MAP)) {\r\n const raw = process.env[envKey];\r\n if (raw === undefined) continue;\r\n\r\n switch (configKey) {\r\n case \"verbose\":\r\n case \"defaultProvider\": {\r\n (result as unknown as Record<string, unknown>)[configKey] = raw;\r\n break;\r\n }\r\n case \"maxTokens\":\r\n case \"maxToolRounds\":\r\n case \"budgetLimit\":\r\n case \"tokenBudgetLimit\": {\r\n const n = Number(raw);\r\n if (Number.isFinite(n) && n >= 0) {\r\n (result as unknown as Record<string, unknown>)[configKey] = n;\r\n }\r\n break;\r\n }\r\n case \"temperature\": {\r\n const n = Number(raw);\r\n if (Number.isFinite(n) && n >= 0 && n <= 2) {\r\n (result as unknown as Record<string, unknown>)[configKey] = n;\r\n }\r\n break;\r\n }\r\n }\r\n }\r\n\r\n // 2. DEEPSEEK_API_KEY — 注入到名为 deepseek 的 provider\r\n const apiKey = process.env.DEEPSEEK_API_KEY;\r\n if (apiKey) {\r\n const deepseekIdx = result.providers.findIndex((p) => p.name === \"deepseek\");\r\n if (deepseekIdx !== -1) {\r\n const existing = result.providers[deepseekIdx]!;\r\n if (!existing.apiKey) {\r\n result.providers[deepseekIdx] = { ...existing, apiKey };\r\n }\r\n } else {\r\n // 如果没有 deepseek provider,自动创建一个\r\n result.providers.unshift({\r\n name: \"deepseek\",\r\n baseUrl: \"https://api.deepseek.com\",\r\n model: \"deepseek-v4-flash\",\r\n apiKey,\r\n });\r\n }\r\n }\r\n\r\n return result;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// CLI flag 覆盖\r\n// ---------------------------------------------------------------------------\r\n\r\nexport interface CliFlags {\r\n verbose?: boolean;\r\n model?: string;\r\n maxTokens?: number;\r\n temperature?: number;\r\n budgetLimit?: number;\r\n tokenBudgetLimit?: number;\r\n}\r\n\r\n/**\r\n * 将 CLI flag 中的值覆盖到配置上。\r\n * CLI flag 的优先级最高。\r\n * 返回一个新的配置对象,不修改原始配置。\r\n */\r\nexport function applyCliOverrides(config: Config, flags: CliFlags): Config {\r\n const result: Config = { ...config, providers: [...config.providers] };\r\n\r\n if (flags.verbose !== undefined) {\r\n result.verbose = flags.verbose;\r\n }\r\n if (flags.model !== undefined) {\r\n // 将 --model 的值映射为标准 model 名称\r\n // 如果用户指定了 --model,覆盖 defaultProvider 中配置的 model\r\n const providerIdx = result.providers.findIndex(\r\n (p) => p.name === result.defaultProvider,\r\n );\r\n if (providerIdx !== -1) {\r\n result.providers[providerIdx] = {\r\n ...result.providers[providerIdx]!,\r\n model: flags.model,\r\n };\r\n }\r\n }\r\n if (flags.maxTokens !== undefined && flags.maxTokens > 0) {\r\n result.maxTokens = flags.maxTokens;\r\n }\r\n if (\r\n flags.temperature !== undefined &&\r\n flags.temperature >= 0 &&\r\n flags.temperature <= 2\r\n ) {\r\n result.temperature = flags.temperature;\r\n }\r\n if (flags.budgetLimit !== undefined && flags.budgetLimit >= 0) {\r\n result.budgetLimit = flags.budgetLimit;\r\n }\r\n if (flags.tokenBudgetLimit !== undefined && flags.tokenBudgetLimit >= 0) {\r\n result.tokenBudgetLimit = flags.tokenBudgetLimit;\r\n }\r\n return result;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 配置校验\r\n// ---------------------------------------------------------------------------\r\n\r\nexport interface ConfigError {\r\n field: string;\r\n message: string;\r\n}\r\n\r\n/**\r\n * 校验配置的合法性,返回错误列表。\r\n * 返回空数组表示配置合法。\r\n */\r\nexport function validateConfig(config: Config): ConfigError[] {\r\n const errors: ConfigError[] = [];\r\n\r\n // 1. 至少需要一个 Provider\r\n if (!config.providers || config.providers.length === 0) {\r\n errors.push({\r\n field: \"providers\",\r\n message: \"至少需要配置一个 Provider。请通过配置文件或 DEEPSEEK_API_KEY 环境变量设置。\",\r\n });\r\n }\r\n\r\n // 2. 每个 Provider 必须有 name 和 model,且 model 必须受支持\r\n const SUPPORTED_MODELS = [\"deepseek-v4-flash\", \"deepseek-v4-pro\"];\r\n for (let i = 0; i < config.providers.length; i++) {\r\n const p = config.providers[i]!;\r\n if (!p.name) {\r\n errors.push({\r\n field: `providers[${i}].name`,\r\n message: `第 ${i + 1} 个 Provider 缺少 name 字段。`,\r\n });\r\n }\r\n if (!p.model) {\r\n errors.push({\r\n field: `providers[${i}].model`,\r\n message: `Provider \"${p.name || i}\" 缺少 model 字段。`,\r\n });\r\n } else if (!SUPPORTED_MODELS.includes(p.model)) {\r\n errors.push({\r\n field: `providers[${i}].model`,\r\n message: `Provider \"${p.name || i}\" 的 model \"${p.model}\" 不受支持。dskcode 仅支持: ${SUPPORTED_MODELS.join(\", \")}`,\r\n });\r\n }\r\n }\r\n\r\n // 3. defaultProvider 必须存在于 providers 列表中\r\n if (config.defaultProvider) {\r\n const exists = config.providers.some(\r\n (p) => p.name === config.defaultProvider,\r\n );\r\n if (!exists) {\r\n errors.push({\r\n field: \"defaultProvider\",\r\n message: `默认 Provider \"${config.defaultProvider}\" 未在 providers 中定义。`,\r\n });\r\n }\r\n }\r\n\r\n // 4. temperature 范围校验\r\n if (\r\n config.temperature !== undefined &&\r\n (config.temperature < 0 || config.temperature > 2)\r\n ) {\r\n errors.push({\r\n field: \"temperature\",\r\n message: \"temperature 必须在 0.0 ~ 2.0 之间。\",\r\n });\r\n }\r\n\r\n // 5. maxTokens 范围校验\r\n if (config.maxTokens !== undefined && config.maxTokens < 1) {\r\n errors.push({\r\n field: \"maxTokens\",\r\n message: \"maxTokens 必须大于等于 1。\",\r\n });\r\n }\r\n\r\n // 6. maxToolRounds 范围校验\r\n if (config.maxToolRounds !== undefined && config.maxToolRounds < 1) {\r\n errors.push({\r\n field: \"maxToolRounds\",\r\n message: \"maxToolRounds 必须大于等于 1。\",\r\n });\r\n }\r\n\r\n // 7. budgetLimit 范围校验\r\n if (config.budgetLimit !== undefined && config.budgetLimit < 0) {\r\n errors.push({\r\n field: \"budgetLimit\",\r\n message: \"budgetLimit 必须大于等于 0(0 表示不限制)。\",\r\n });\r\n }\r\n\r\n // 8. tokenBudgetLimit 范围校验\r\n if (config.tokenBudgetLimit !== undefined && config.tokenBudgetLimit < 0) {\r\n errors.push({\r\n field: \"tokenBudgetLimit\",\r\n message: \"tokenBudgetLimit 必须大于等于 0(0 表示不限制)。\",\r\n });\r\n }\r\n\r\n return errors;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 核心加载流程\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * 从多级配置源加载并合并配置。\r\n *\r\n * 解析顺序(后加载的优先级更高):\r\n * 1. 内置默认值 —— defaultConfig\r\n * 2. 用户全局 —— ~/.dskcode/settings.json\r\n * 3. 项目本地 —— .dskcode/settings.json(或通过 --config 指定的路径)\r\n * 4. 环境变量 —— DEEPSEEK_API_KEY、DSKCODE_* 等\r\n * 5. CLI flag —— 由调用方通过 applyCliOverrides() 单独注入\r\n */\r\nexport async function loadConfig(configPath?: string): Promise<Config> {\r\n const filePaths = resolveConfigFiles(configPath);\r\n\r\n let config: Config = structuredClone(defaultConfig);\r\n\r\n // 1-3. 依次加载 JSON 配置文件\r\n for (const filePath of filePaths) {\r\n try {\r\n const raw = await readFile(filePath, \"utf-8\");\r\n const parsed = JSON.parse(raw) as Partial<Config>;\r\n config = mergeConfig(config, parsed);\r\n } catch {\r\n // 文件不存在或权限不足 — 静默跳过\r\n }\r\n }\r\n\r\n // 4. 环境变量覆盖\r\n config = applyEnvVars(config);\r\n\r\n return config;\r\n}\r\n\r\n/**\r\n * 加载配置并同时执行校验。\r\n * 校验错误不会 throw,而是通过返回值中的 errors 字段返回,\r\n * 由调用方决定如何处理(例如在 middleware 中输出警告)。\r\n */\r\nexport async function loadAndValidate(\r\n configPath?: string,\r\n): Promise<{ config: Config; errors: ConfigError[] }> {\r\n const config = await loadConfig(configPath);\r\n const errors = validateConfig(config);\r\n return { config, errors };\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 配置热加载(Watch 模式)\r\n// ---------------------------------------------------------------------------\r\n\r\nexport type ConfigChangeCallback = (config: Config) => void;\r\n\r\n/**\r\n * 监听配置文件变更,在文件被修改时重新加载配置并调用回调。\r\n *\r\n * @param callback 配置变更后的回调函数\r\n * @param configPath 可选,指定配置文件路径(对应 --config flag)\r\n * @returns 一个 unwatch 函数,调用后可停止监听\r\n */\r\nexport function watchConfig(\r\n callback: ConfigChangeCallback,\r\n configPath?: string,\r\n): () => void {\r\n const filePaths = resolveConfigFiles(configPath).filter((fp) => existsSync(fp));\r\n\r\n // 如果一个文件都不存在,则监听项目本地的 .dskcode/settings.json(即使还没创建)\r\n if (filePaths.length === 0) {\r\n filePaths.push(join(process.cwd(), \".dskcode\", \"settings.json\"));\r\n }\r\n\r\n const watchers: ReturnType<typeof watch>[] = [];\r\n let debounceTimer: ReturnType<typeof setTimeout> | undefined;\r\n\r\n for (const filePath of filePaths) {\r\n try {\r\n const watcher = watch(filePath, (eventType) => {\r\n if (eventType !== \"change\") return;\r\n\r\n // 防抖:多次连续变更只触发一次\r\n if (debounceTimer) clearTimeout(debounceTimer);\r\n debounceTimer = setTimeout(() => {\r\n void (async () => {\r\n try {\r\n const config = await loadConfig(configPath);\r\n callback(config);\r\n } catch {\r\n // 重载失败时不回调,等待下一次变更\r\n }\r\n })();\r\n }, 300);\r\n });\r\n\r\n watchers.push(watcher);\r\n } catch {\r\n // 无法监听的文件(例如还不存在)— 跳过\r\n }\r\n }\r\n\r\n return () => {\r\n if (debounceTimer) clearTimeout(debounceTimer);\r\n for (const w of watchers) {\r\n w.close();\r\n }\r\n };\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// API Key 持久化\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * 将 API Key 保存到用户全局配置 ~/.dskcode/settings.json。\r\n * 如果文件已存在,合并写入;不存在则新建。\r\n * 返回保存的文件路径。\r\n */\r\nexport async function saveApiKey(apiKey: string): Promise<string> {\r\n const home = process.env.HOME ?? process.env.USERPROFILE ?? \"~\";\r\n const configDir = join(home, \".dskcode\");\r\n const configFile = join(configDir, \"settings.json\");\r\n\r\n // 确保目录存在\r\n await mkdir(configDir, { recursive: true });\r\n\r\n // 读取现有配置,或从默认配置开始\r\n let configData: Record<string, unknown>;\r\n try {\r\n const raw = await readFile(configFile, \"utf-8\");\r\n configData = JSON.parse(raw);\r\n } catch {\r\n // 文件不存在,用内置默认值填充(tools、plugins 等都会写入)\r\n configData = structuredClone(defaultConfig) as unknown as Record<string, unknown>;\r\n }\r\n\r\n // 更新或创建 deepseek provider\r\n const providers = (configData.providers as Record<string, unknown>[]) ?? [];\r\n const existing = providers.find((p) => p.name === \"deepseek\");\r\n\r\n if (existing) {\r\n existing.apiKey = apiKey;\r\n } else {\r\n providers.push({\r\n name: \"deepseek\",\r\n apiKey,\r\n baseUrl: \"https://api.deepseek.com\",\r\n model: \"deepseek-v4-flash\",\r\n });\r\n }\r\n\r\n configData.providers = providers;\r\n\r\n // 写回文件\r\n await writeFile(configFile, JSON.stringify(configData, null, 2), \"utf-8\");\r\n\r\n return configFile;\r\n}\r\n\r\n/**\r\n * 将模型偏好保存到用户全局配置 ~/.dskcode/settings.json。\r\n * 只更新 defaultProvider 的 model 字段,不覆盖其他配置。\r\n * 返回保存的文件路径。\r\n */\r\nexport async function saveModelConfig(model: string): Promise<string> {\r\n const home = process.env.HOME ?? process.env.USERPROFILE ?? \"~\";\r\n const configDir = join(home, \".dskcode\");\r\n const configFile = join(configDir, \"settings.json\");\r\n\r\n // 确保目录存在\r\n await mkdir(configDir, { recursive: true });\r\n\r\n // 读取现有配置\r\n let configData: Record<string, unknown>;\r\n try {\r\n const raw = await readFile(configFile, \"utf-8\");\r\n configData = JSON.parse(raw);\r\n } catch {\r\n configData = structuredClone(defaultConfig) as unknown as Record<string, unknown>;\r\n }\r\n\r\n // 更新 defaultProvider 的 model\r\n const providers = (configData.providers as Record<string, unknown>[]) ?? [];\r\n const defaultProviderName = (configData.defaultProvider as string) ?? \"deepseek\";\r\n const existing = providers.find((p) => p.name === defaultProviderName);\r\n\r\n if (existing) {\r\n existing.model = model;\r\n } else {\r\n // 没有 default provider,创建一个\r\n providers.push({\r\n name: defaultProviderName,\r\n baseUrl: \"https://api.deepseek.com\",\r\n model,\r\n });\r\n }\r\n\r\n configData.providers = providers;\r\n\r\n // 写回文件\r\n await writeFile(configFile, JSON.stringify(configData, null, 2), \"utf-8\");\r\n\r\n return configFile;\r\n}\r\n\r\n/**\r\n * 将自选股配置保存到用户全局配置 ~/.dskcode/settings.json。\r\n * 如果文件已存在,合并写入;不存在则新建。\r\n * 返回保存的文件路径。\r\n */\r\nexport async function saveStockConfig(symbols: StockConfig[\"symbols\"]): Promise<string> {\r\n const home = process.env.HOME ?? process.env.USERPROFILE ?? \"~\";\r\n const configDir = join(home, \".dskcode\");\r\n const configFile = join(configDir, \"settings.json\");\r\n\r\n // 确保目录存在\r\n await mkdir(configDir, { recursive: true });\r\n\r\n // 读取现有配置,或从默认配置开始\r\n let configData: Record<string, unknown>;\r\n try {\r\n const raw = await readFile(configFile, \"utf-8\");\r\n configData = JSON.parse(raw);\r\n } catch {\r\n configData = structuredClone(defaultConfig) as unknown as Record<string, unknown>;\r\n }\r\n\r\n // 写入自选股配置\r\n configData.stock = { symbols };\r\n\r\n // 写回文件\r\n await writeFile(configFile, JSON.stringify(configData, null, 2), \"utf-8\");\r\n\r\n return configFile;\r\n}\r\n","import type { Command } from \"commander\";\r\nimport {\r\n loadAndValidate,\r\n applyCliOverrides,\r\n defaultConfig,\r\n} from \"../config/index.js\";\r\nimport type { Config } from \"../config/index.js\";\r\n\r\n/**\r\n * dskcode 运行时上下文。\r\n * 通过 commander 的 preAction hook 注入到每个命令中。\r\n */\r\nexport interface DskcodeContext {\r\n config: Config;\r\n verbose: boolean;\r\n}\r\n\r\n/**\r\n * 在 preAction hook 中加载配置并构造上下文。\r\n *\r\n * 完整的配置解析流水线:\r\n * 1. 内置默认值 —— defaultConfig\r\n * 2. 用户全局 —— ~/.dskcode/settings.json\r\n * 3. 项目本地 —— .dskcode/settings.json(或 --config 指定的路径)\r\n * 4. 环境变量 —— DEEPSEEK_API_KEY / DSKCODE_*\r\n * 5. CLI flag —— --verbose / --model 等\r\n */\r\nexport async function loadConfigMiddleware(\r\n this: Command,\r\n): Promise<DskcodeContext> {\r\n const opts = this.optsWithGlobals() as {\r\n verbose?: boolean;\r\n config?: string;\r\n model?: string;\r\n };\r\n const verbose = opts.verbose ?? false;\r\n\r\n // 1-4. 加载 TOML 文件 + 环境变量\r\n let config: Config;\r\n const errors: string[] = [];\r\n try {\r\n const result = await loadAndValidate(opts.config);\r\n config = result.config;\r\n if (result.errors.length > 0) {\r\n for (const e of result.errors) {\r\n errors.push(e.message);\r\n }\r\n }\r\n } catch {\r\n config = structuredClone(defaultConfig);\r\n }\r\n\r\n // 5. CLI flag 覆盖(优先级最高)\r\n config = applyCliOverrides(config, {\r\n verbose,\r\n model: opts.model,\r\n });\r\n\r\n // 校验错误输出(不阻断执行)\r\n if (errors.length > 0 && verbose) {\r\n for (const msg of errors) {\r\n console.error(` ⚠ ${msg}`);\r\n }\r\n }\r\n\r\n return { config, verbose };\r\n}\r\n","import type { Command } from \"commander\";\r\nimport chalk from \"chalk\";\r\n\r\nexport function customHelp(program: Command): string {\r\n const lines: string[] = [];\r\n\r\n lines.push(\"\");\r\n lines.push(chalk.bold(\"用法:\"));\r\n lines.push(` ${chalk.cyan(\"dskcode\")} ${chalk.dim(\"[global-options]\")} ${chalk.green(\"<command>\")} ${chalk.dim(\"[options]\")}`);\r\n lines.push(\"\");\r\n\r\n const globalOpts = program.options.filter(\r\n (o) => o.long !== \"--help\" && o.long !== \"--version\" && o.long !== \"--config\",\r\n );\r\n if (globalOpts.length > 0) {\r\n lines.push(chalk.bold(\"全局选项:\"));\r\n for (const opt of globalOpts) {\r\n const flags = [opt.short, opt.long].filter(Boolean).join(\", \");\r\n lines.push(` ${chalk.cyan(flags.padEnd(24))} ${opt.description ?? \"\"}`);\r\n }\r\n lines.push(\"\");\r\n }\r\n\r\n lines.push(chalk.bold(\"内置选项:\"));\r\n for (const flag of [\"-h, --help\", \"-V, --version\"]) {\r\n const opt = program.options.find(\r\n (o) => o.long === (flag.includes(\"help\") ? \"--help\" : \"--version\"),\r\n );\r\n if (opt) {\r\n lines.push(` ${chalk.cyan(flag.padEnd(24))} ${opt.description ?? \"\"}`);\r\n }\r\n }\r\n lines.push(\"\");\r\n\r\n const cmds = program.commands.filter((c) => !c.name().startsWith(\"help\"));\r\n if (cmds.length > 0) {\r\n lines.push(chalk.bold(\"命令:\"));\r\n for (const cmd of cmds) {\r\n lines.push(` ${chalk.green(cmd.name().padEnd(24))} ${cmd.description()}`);\r\n }\r\n lines.push(\"\");\r\n }\r\n\r\n lines.push(chalk.bold(\"示例:\"));\r\n lines.push(` ${chalk.dim(\"# 启动交互式对话\")}`);\r\n lines.push(\" dskcode chat\");\r\n lines.push(` ${chalk.dim(\"# 让 AI 执行一个任务\")}`);\r\n lines.push(\" dskcode run 修改所有 TODO 注释\");\r\n lines.push(` ${chalk.dim(\"# 运行配置向导\")}`);\r\n lines.push(\" dskcode setup\");\r\n lines.push(` ${chalk.dim(\"# 生成 shell 自动补全\")}`);\r\n lines.push(\" dskcode completion\");\r\n lines.push(` ${chalk.dim(\"# 查看自选股行情\")}`);\r\n lines.push(\" dskcode stock\");\r\n lines.push(` ${chalk.dim(\"# 查看指定股票行情\")}`);\r\n lines.push(\" dskcode stock sh513090 sz000001\");\r\n lines.push(\"\");\r\n\r\n return lines.join(\"\\n\");\r\n}\r\n","import { createInterface } from \"node:readline\";\r\nimport chalk from \"chalk\";\r\n\r\n/**\r\n * 检测是否有可用的 API Key。\r\n * 遍历所有 provider 检查是否配置了 apiKey,同时检查 DEEPSEEK_API_KEY 环境变量。\r\n */\r\nexport function hasApiKey(providers: { apiKey?: string }[]): boolean {\r\n if (providers.some((p) => p.apiKey)) return true;\r\n if (process.env.DEEPSEEK_API_KEY) return true;\r\n return false;\r\n}\r\n\r\n/**\r\n * API Key 最小长度校验值。\r\n * DeepSeek 的 Key 通常以 sk- 开头,长度远超 10 位。\r\n */\r\nconst MIN_API_KEY_LENGTH = 10;\r\n\r\n/**\r\n * 交互式提示用户输入 DeepSeek API Key。\r\n * 使用 Node readline 的 password 模式(输入不可见)。\r\n * 返回用户输入的 Key,如果用户取消则返回 null。\r\n */\r\nexport async function promptForApiKey(): Promise<string | null> {\r\n console.log(chalk.yellow(\"\\n ⚠ 未检测到 API Key 配置\"));\r\n console.log(chalk.dim(\" 你可以通过以下任一方式配置:\"));\r\n console.log(chalk.dim(\" · 环境变量: export DEEPSEEK_API_KEY=sk-xxx\"));\r\n console.log(chalk.dim(\" · 配置文件: ~/.dskcode/settings.json\"));\r\n console.log(chalk.dim(\" · 下面直接输入,自动保存到全局配置\\n\"));\r\n\r\n const rl = createInterface({\r\n input: process.stdin,\r\n output: process.stdout,\r\n });\r\n\r\n return new Promise<string | null>((resolve) => {\r\n let resolved = false;\r\n\r\n const cleanup = () => {\r\n if (resolved) return;\r\n resolved = true;\r\n process.stdin.removeListener(\"keypress\", onKeypress);\r\n rl.close();\r\n };\r\n\r\n const onKeypress = (_: unknown, key: { ctrl?: boolean; name?: string }) => {\r\n if (key.ctrl && key.name === \"c\") {\r\n cleanup();\r\n resolve(null);\r\n }\r\n };\r\n\r\n process.stdin.on(\"keypress\", onKeypress);\r\n\r\n rl.question(\r\n ` ${chalk.cyan(\"🔑\")} ${chalk.bold(\"请输入你的 DeepSeek API Key:\")} `,\r\n (answer) => {\r\n cleanup();\r\n const trimmed = answer.trim();\r\n if (!trimmed) {\r\n console.log(chalk.red(\" ✖ API Key 不能为空\"));\r\n resolve(null);\r\n return;\r\n }\r\n if (trimmed.length < MIN_API_KEY_LENGTH) {\r\n console.log(chalk.red(\" ✖ API Key 格式不正确,长度至少 10 位\"));\r\n resolve(null);\r\n return;\r\n }\r\n resolve(trimmed);\r\n },\r\n );\r\n });\r\n}\r\n","import { createInterface } from \"node:readline\";\r\nimport { existsSync, statSync, lstatSync } from \"node:fs\";\r\nimport { mkdir, readdir, cp, access, realpath, readFile } from \"node:fs/promises\";\r\nimport { join } from \"node:path\";\r\nimport chalk from \"chalk\";\r\n\r\n/**\r\n * Claude Code 全局 skills 目录路径(~/.claude/skills)。\r\n * 跨平台兼容:优先用 HOME,Windows 下回退到 USERPROFILE。\r\n */\r\nfunction getClaudeSkillsDir(): string {\r\n const home = process.env.HOME ?? process.env.USERPROFILE ?? \"~\";\r\n return join(home, \".claude\", \"skills\");\r\n}\r\n\r\n/**\r\n * dskcode 全局 skills 目录路径(~/.dskcode/skills)。\r\n */\r\nfunction getDskcodeSkillsDir(): string {\r\n const home = process.env.HOME ?? process.env.USERPROFILE ?? \"~\";\r\n return join(home, \".dskcode\", \"skills\");\r\n}\r\n\r\n/**\r\n * 判断某个目录是否为一个有效的 skill(包含 SKILL.md)。\r\n */\r\nasync function isSkillDir(dir: string): Promise<boolean> {\r\n try {\r\n await access(join(dir, \"SKILL.md\"));\r\n return true;\r\n } catch {\r\n return false;\r\n }\r\n}\r\n\r\n/**\r\n * 列出 Claude Code skills 目录下所有有效的 skill 名称(子目录包含 SKILL.md)。\r\n * 找不到目录返回空数组。\r\n */\r\nexport async function listClaudeSkills(): Promise<string[]> {\r\n const claudeDir = getClaudeSkillsDir();\r\n if (!existsSync(claudeDir)) return [];\r\n\r\n let entries: string[];\r\n try {\r\n entries = await readdir(claudeDir);\r\n } catch {\r\n return [];\r\n }\r\n\r\n const skills: string[] = [];\r\n for (const name of entries) {\r\n const full = join(claudeDir, name);\r\n const stat = statSync(full);\r\n if (stat.isDirectory() && (await isSkillDir(full))) {\r\n skills.push(name);\r\n }\r\n }\r\n return skills;\r\n}\r\n\r\n/**\r\n * 是否存在可导入的 Claude skills。\r\n */\r\nexport async function hasClaudeSkills(): Promise<boolean> {\r\n const skills = await listClaudeSkills();\r\n return skills.length > 0;\r\n}\r\n\r\n/**\r\n * 交互式询问用户是否确认。默认接受回车视为\"否\"。\r\n * 返回 true 表示用户确认。\r\n */\r\nfunction askConfirm(question: string): Promise<boolean> {\r\n const rl = createInterface({\r\n input: process.stdin,\r\n output: process.stdout,\r\n });\r\n\r\n return new Promise<boolean>((resolve) => {\r\n let resolved = false;\r\n\r\n const cleanup = () => {\r\n if (resolved) return;\r\n resolved = true;\r\n process.stdin.removeListener(\"keypress\", onKeypress);\r\n rl.close();\r\n };\r\n\r\n const onKeypress = (_: unknown, key: { ctrl?: boolean; name?: string }) => {\r\n if (key.ctrl && key.name === \"c\") {\r\n cleanup();\r\n resolve(false);\r\n }\r\n };\r\n\r\n process.stdin.on(\"keypress\", onKeypress);\r\n\r\n rl.question(question, (answer) => {\r\n cleanup();\r\n const trimmed = answer.trim().toLowerCase();\r\n // 默认回车 = 否;明确输入 y / yes 才算确认\r\n resolve(trimmed === \"y\" || trimmed === \"yes\");\r\n });\r\n });\r\n}\r\n\r\n/**\r\n * 将 Claude skills 目录复制到 ~/.dskcode/skills。\r\n * 已存在的同名 skill 跳过,避免覆盖用户自定义修改。\r\n * 返回 { imported, skipped } 两个数组。\r\n */\r\nexport async function importClaudeSkills(\r\n skillNames: string[],\r\n): Promise<{ imported: string[]; skipped: string[] }> {\r\n const claudeDir = getClaudeSkillsDir();\r\n const dskcodeDir = getDskcodeSkillsDir();\r\n\r\n await mkdir(dskcodeDir, { recursive: true });\r\n\r\n const imported: string[] = [];\r\n const skipped: string[] = [];\r\n\r\n for (const name of skillNames) {\r\n const src = join(claudeDir, name);\r\n const dest = join(dskcodeDir, name);\r\n\r\n if (existsSync(dest)) {\r\n skipped.push(name);\r\n continue;\r\n }\r\n\r\n // 递归复制整个 skill 目录,保留原始结构(SKILL.md、reference 等)。\r\n // 两个解引用手段叠加:\r\n // 1) realpath() 解决 source 顶层是 symlink 的情况(cp 的 dereference 只处理源目录内部 symlink)\r\n // 2) cp 的 dereference: true 解决源目录内部遇到的 symlink\r\n // 这样可以避开 Windows 上非管理员无法创建 symlink 的 EPERM 错误\r\n // (Claude Code 的 skill 目录往往是 symlink 指向 ~/.agents/skills)。\r\n const realSrc = lstatSync(src).isSymbolicLink() ? await realpath(src) : src;\r\n await cp(realSrc, dest, { recursive: true, dereference: true });\r\n imported.push(name);\r\n }\r\n\r\n return { imported, skipped };\r\n}\r\n\r\n/**\r\n * 统计全局 ~/.dskcode/skills 下的有效 skill 数量。\r\n */\r\nexport async function countDskcodeSkills(): Promise<number> {\r\n const dskcodeDir = getDskcodeSkillsDir();\r\n if (!existsSync(dskcodeDir)) return 0;\r\n\r\n let entries: string[];\r\n try {\r\n entries = await readdir(dskcodeDir);\r\n } catch {\r\n return 0;\r\n }\r\n\r\n let count = 0;\r\n for (const name of entries) {\r\n const full = join(dskcodeDir, name);\r\n if (statSync(full).isDirectory() && (await isSkillDir(full))) {\r\n count++;\r\n }\r\n }\r\n return count;\r\n}\r\n\r\n/**\r\n * 项目本地 skill 目录路径({cwd}/.dskcode/skill)。\r\n */\r\nexport function getProjectSkillDir(cwd: string): string {\r\n return join(cwd, \".dskcode\", \"skill\");\r\n}\r\n\r\n/**\r\n * 检测项目本地 .dskcode/skill 目录下是否存在有效 skill。\r\n */\r\nexport async function hasProjectLocalSkills(cwd: string): Promise<boolean> {\r\n const count = await countProjectLocalSkills(cwd);\r\n return count > 0;\r\n}\r\n\r\n/**\r\n * 统计项目本地 .dskcode/skill 下的有效 skill 数量。\r\n */\r\nexport async function countProjectLocalSkills(cwd: string): Promise<number> {\r\n const skillDir = getProjectSkillDir(cwd);\r\n if (!existsSync(skillDir)) return 0;\r\n\r\n let entries: string[];\r\n try {\r\n entries = await readdir(skillDir);\r\n } catch {\r\n return 0;\r\n }\r\n\r\n let count = 0;\r\n for (const name of entries) {\r\n const full = join(skillDir, name);\r\n const stat = statSync(full);\r\n if (stat.isDirectory() && (await isSkillDir(full))) {\r\n count++;\r\n }\r\n }\r\n return count;\r\n}\r\n\r\n/**\r\n * 检测全局 ~/.dskcode/skills 目录下是否存在有效 skill。\r\n */\r\nexport async function hasDskcodeSkills(): Promise<boolean> {\r\n const dskcodeDir = getDskcodeSkillsDir();\r\n if (!existsSync(dskcodeDir)) return false;\r\n\r\n let entries: string[];\r\n try {\r\n entries = await readdir(dskcodeDir);\r\n } catch {\r\n return false;\r\n }\r\n\r\n for (const name of entries) {\r\n const full = join(dskcodeDir, name);\r\n const stat = statSync(full);\r\n if (stat.isDirectory() && (await isSkillDir(full))) {\r\n return true;\r\n }\r\n }\r\n return false;\r\n}\r\n\r\n/**\r\n * 主流程:检测已有 skill 并决定是否提示导入 Claude Code skill。\r\n * 通常在 chat 命令的 API Key 检查之后调用。\r\n *\r\n * 规则(按优先级):\r\n * 1. 如果项目本地 .dskcode/skill 下有 skill,跳过导入\r\n * 2. 如果全局 ~/.dskcode/skills 下有 skill(已导入过),跳过导入\r\n * 3. 否则检测 Claude Code 并询问是否导入\r\n */\r\nexport async function promptImportClaudeSkills(cwd?: string): Promise<void> {\r\n // 检查项目本地 .dskcode/skill\r\n if (cwd) {\r\n const hasLocal = await hasProjectLocalSkills(cwd);\r\n if (hasLocal) {\r\n console.log(chalk.dim(\" 检测到项目本地 .dskcode/skill,跳过导入 Claude Code skill\\n\"));\r\n return;\r\n }\r\n }\r\n\r\n // 检查全局 ~/.dskcode/skills(已经导入过的无需再提示)\r\n const hasGlobal = await hasDskcodeSkills();\r\n if (hasGlobal) {\r\n return;\r\n }\r\n\r\n const skills = await listClaudeSkills();\r\n if (skills.length === 0) return;\r\n\r\n console.log(chalk.cyan(`\\n ✦ 检测到你在 Claude Code 中安装了 ${String(skills.length)} 个 skill:`));\r\n // 每行最多展示 5 个,避免列表过长\r\n const preview = skills.slice(0, 5).map((s) => chalk.dim(` · ${s}`)).join(\"\\n\");\r\n console.log(preview);\r\n if (skills.length > 5) {\r\n console.log(chalk.dim(` · ...等 ${String(skills.length)} 个`));\r\n }\r\n console.log(chalk.dim(` 源目录: ${getClaudeSkillsDir()}`));\r\n console.log(chalk.dim(` 目标目录: ${getDskcodeSkillsDir()}\\n`));\r\n\r\n const confirmed = await askConfirm(\r\n ` ${chalk.cyan(\"📦\")} 是否将这些 skill 导入到 dskcode?${chalk.dim(\"[y/N] \")} `,\r\n );\r\n\r\n if (!confirmed) {\r\n console.log(chalk.dim(\" 已跳过 skill 导入\\n\"));\r\n return;\r\n }\r\n\r\n try {\r\n const { imported, skipped } = await importClaudeSkills(skills);\r\n if (imported.length > 0) {\r\n console.log(\r\n chalk.green(` ✔ 已导入 ${String(imported.length)} 个 skill 到 ~/.dskcode/skills`),\r\n );\r\n }\r\n if (skipped.length > 0) {\r\n console.log(\r\n chalk.yellow(` ⚠ ${String(skipped.length)} 个 skill 已存在,已跳过:${skipped.join(\", \")}`),\r\n );\r\n }\r\n console.log(\"\");\r\n } catch (err) {\r\n console.log(\r\n chalk.red(` ✖ 导入 skill 失败: ${err instanceof Error ? err.message : String(err)}`),\r\n );\r\n console.log(chalk.dim(\" 你可以稍后手动复制 ~/.claude/skills 到 ~/.dskcode/skills\\n\"));\r\n }\r\n}\r\n\r\n/** 单个 skill 的展示信息 */\r\nexport interface SkillInfo {\r\n name: string;\r\n description: string;\r\n}\r\n\r\n/**\r\n * 列出全局 ~/.dskcode/skills 下所有有效的 skill 名称。\r\n */\r\nexport async function listDskcodeSkills(): Promise<string[]> {\r\n const dskcodeDir = getDskcodeSkillsDir();\r\n if (!existsSync(dskcodeDir)) return [];\r\n\r\n let entries: string[];\r\n try {\r\n entries = await readdir(dskcodeDir);\r\n } catch {\r\n return [];\r\n }\r\n\r\n const skills: string[] = [];\r\n for (const name of entries) {\r\n const full = join(dskcodeDir, name);\r\n if (statSync(full).isDirectory() && (await isSkillDir(full))) {\r\n skills.push(name);\r\n }\r\n }\r\n return skills;\r\n}\r\n\r\n/**\r\n * 列出项目本地 .dskcode/skill 下所有有效的 skill 名称。\r\n */\r\nexport async function listProjectSkills(cwd: string): Promise<string[]> {\r\n const skillDir = getProjectSkillDir(cwd);\r\n if (!existsSync(skillDir)) return [];\r\n\r\n let entries: string[];\r\n try {\r\n entries = await readdir(skillDir);\r\n } catch {\r\n return [];\r\n }\r\n\r\n const skills: string[] = [];\r\n for (const name of entries) {\r\n const full = join(skillDir, name);\r\n if (statSync(full).isDirectory() && (await isSkillDir(full))) {\r\n skills.push(name);\r\n }\r\n }\r\n return skills;\r\n}\r\n\r\n/**\r\n * 解析 SKILL.md 的 YAML front matter,获取 skill 名称和描述。\r\n * 解析失败时返回 null。\r\n */\r\nasync function readSkillInfo(skillDir: string): Promise<SkillInfo | null> {\r\n try {\r\n const content = await readFile(join(skillDir, \"SKILL.md\"), \"utf-8\");\r\n const match = content.match(/^---\\n([\\s\\S]*?)\\n---/);\r\n if (!match) return null;\r\n\r\n const frontMatter = match[1]!;\r\n const nameMatch = frontMatter.match(/^name:\\s*(.+)$/m);\r\n const descMatch = frontMatter.match(/^description:\\s*(.+)$/m);\r\n\r\n return {\r\n name: nameMatch?.[1]?.trim() ?? \"\",\r\n description: descMatch?.[1]?.trim() ?? \"\",\r\n };\r\n } catch {\r\n return null;\r\n }\r\n}\r\n\r\n/**\r\n * 获取所有可用 skill 的详细信息(全局 + 项目本地)。\r\n * 返回 SkillInfo 列表,解析失败时仍使用目录名作为 name。\r\n */\r\nexport async function getAllSkills(cwd: string): Promise<SkillInfo[]> {\r\n const [globalNames, localNames] = await Promise.all([\r\n listDskcodeSkills(),\r\n listProjectSkills(cwd),\r\n ]);\r\n\r\n // 合并去重,项目本地优先\r\n const seen = new Set<string>();\r\n const allNames: string[] = [];\r\n for (const name of [...localNames, ...globalNames]) {\r\n if (!seen.has(name)) {\r\n seen.add(name);\r\n allNames.push(name);\r\n }\r\n }\r\n\r\n const dskcodeDir = getDskcodeSkillsDir();\r\n const projectDir = getProjectSkillDir(cwd);\r\n\r\n const results: SkillInfo[] = [];\r\n for (const name of allNames) {\r\n // 优先读项目本地的 SKILL.md,再读全局\r\n const localPath = join(projectDir, name);\r\n let info: SkillInfo | null = null;\r\n if (existsSync(localPath)) {\r\n info = await readSkillInfo(localPath);\r\n }\r\n if (!info) {\r\n const globalPath = join(dskcodeDir, name);\r\n if (existsSync(globalPath)) {\r\n info = await readSkillInfo(globalPath);\r\n }\r\n }\r\n results.push(info ?? { name, description: \"\" });\r\n }\r\n\r\n return results;\r\n}\r\n\r\n// 暴露路径获取函数,便于测试\r\nexport { getClaudeSkillsDir, getDskcodeSkillsDir };","import { render } from \"ink\";\r\nimport type { ReactNode } from \"react\";\r\n\r\nexport interface RenderScopeHandle {\r\n waitUntilExit: Promise<unknown>;\r\n unmount: () => void;\r\n clear: () => void;\r\n}\r\n\r\nexport function renderApp(node: ReactNode): RenderScopeHandle {\r\n // exitOnCtrlC: false — 让组件自行处理 Ctrl+C(双击退出)\r\n const { waitUntilExit, clear, unmount } = render(node, { exitOnCtrlC: false });\r\n return { waitUntilExit: waitUntilExit(), clear, unmount };\r\n}\r\n\r\nexport async function unmountApp(handle: RenderScopeHandle): Promise<void> {\r\n handle.unmount();\r\n await new Promise((resolve) => setTimeout(resolve, 50));\r\n}\r\n","import { Text } from \"ink\";\r\nimport InkSpinner from \"ink-spinner\";\r\n\r\ninterface SpinnerProps {\r\n type?: \"dots\" | \"line\" | \"bouncingBar\" | \"aesthetic\";\r\n label?: string;\r\n}\r\n\r\nexport function Spinner({ type = \"dots\", label }: SpinnerProps) {\r\n return (\r\n <Text>\r\n <Text color=\"cyan\">\r\n <InkSpinner type={type} />\r\n </Text>\r\n {label ? <Text> {label}</Text> : null}\r\n </Text>\r\n );\r\n}\r\n","import { Box, Text } from \"ink\";\r\n\r\ntype MessageType = \"info\" | \"success\" | \"warning\" | \"error\";\r\n\r\ninterface StatusMessageProps {\r\n type?: MessageType;\r\n label: string;\r\n detail?: string;\r\n}\r\n\r\nconst STYLES: Record<MessageType, { color: string; icon: string }> = {\r\n info: { color: \"cyan\", icon: \"ℹ\" },\r\n success: { color: \"green\", icon: \"✔\" },\r\n warning: { color: \"yellow\", icon: \"⚠\" },\r\n error: { color: \"red\", icon: \"✖\" },\r\n};\r\n\r\nexport function StatusMessage({ type = \"info\", label, detail }: StatusMessageProps) {\r\n const { color, icon } = STYLES[type];\r\n return (\r\n <Box>\r\n <Text color={color}>\r\n {icon} {label}\r\n </Text>\r\n {detail ? <Text dimColor>: {detail}</Text> : null}\r\n </Box>\r\n );\r\n}\r\n","import { Box, Text } from \"ink\";\r\nimport { useEffect, useState } from \"react\";\r\n\r\n/** 赛博朋克调色板 — 多处组件复用 */\r\nexport const CYBER_PALETTE = [\"#00ffff\", \"#ff00ff\", \"#00ff41\", \"#ff1493\", \"#8b00ff\"] as const;\r\n\r\n/** ASCII LOGO 行 — 多处组件复用 */\r\nexport const LOGO_LINES = [\r\n \" ██████╗ ███████╗██╗ ██╗\",\r\n \" ██╔══██╗██╔════╝██║ ██╔╝\",\r\n \" ██║ ██║███████╗█████╔╝ \",\r\n \" ██║ ██║╚════██║██╔═██╗ \",\r\n \" ██████╔╝███████║██║ ██╗\",\r\n \" ╚═════╝ ╚══════╝╚═╝ ╚═╝\",\r\n] as const;\r\n\r\nexport function DskcodeSplash() {\r\n const [offset, setOffset] = useState(0);\r\n\r\n useEffect(() => {\r\n const timer = setInterval(() => {\r\n setOffset((prev) => (prev + 1) % CYBER_PALETTE.length);\r\n }, 500);\r\n return () => clearInterval(timer);\r\n }, []);\r\n\r\n return (\r\n <Box flexDirection=\"column\" paddingLeft={1}>\r\n {LOGO_LINES.map((line, i) => {\r\n const colorIndex = (i + offset) % CYBER_PALETTE.length;\r\n return (\r\n <Box key={i}>\r\n <Text bold color={CYBER_PALETTE[colorIndex]}>\r\n {line}\r\n </Text>\r\n </Box>\r\n );\r\n })}\r\n </Box>\r\n );\r\n}\r\n","// ---------------------------------------------------------------------------\r\n// 交互式聊天会话组件 — 接入 Agent 主循环,实现流式对话\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { Box, Text, useInput, Static } from \"ink\";\r\nimport TextInput from \"ink-text-input\";\r\nimport { useEffect, useState, useCallback, useMemo, useRef } from \"react\";\r\nimport { useDoubleCtrlC } from \"./useDoubleCtrlC.js\";\r\nimport { CYBER_PALETTE, LOGO_LINES } from \"./DskcodeSplash.js\";\r\nimport { Spinner } from \"./Spinner.js\";\r\nimport InkSpinner from \"ink-spinner\";\r\nimport { AssistantMessage } from \"./AssistantMessage.js\";\r\nimport { DiffPreview } from \"./DiffPreview.js\";\r\nimport { SkillSelector } from \"./SkillSelector.js\";\r\nimport { FileSelector } from \"./FileSelector.js\";\r\nimport type { SkillInfo } from \"../cli/skill-import.js\";\r\nimport { CostTracker } from \"../provider/cost-tracker.js\";\r\nimport type { ModelId, ProviderToolCall, UsageInfo } from \"../provider/index.js\";\r\nimport type { FileDiff } from \"../tool/types.js\";\r\nimport { createProvider } from \"../provider/index.js\";\r\nimport { Session } from \"../agent/index.js\";\r\nimport type { AgentEvent } from \"../agent/types.js\";\r\nimport { builtinTools } from \"../tool/index.js\";\r\nimport { ToolRegistry } from \"../tool/registry.js\";\r\nimport {\r\n IDLE_GRADIENT_STOPS,\r\n STREAMING_GRADIENT_STOPS,\r\n CMD_TIP_GRADIENT_STOPS,\r\n GRADIENT_ANIMATION,\r\n getGradientColors,\r\n} from \"../utils/gradient.js\";\r\nimport { SUPPORTED_MODELS, calculateCost } from \"../provider/models.js\";\r\nimport { saveModelConfig } from \"../config/loader.js\";\r\n\r\n/** 命令处理结果的类型,支持文本响应和动作跳转 */\r\nexport type CommandAction =\r\n | { kind: \"text\"; content: string }\r\n | { kind: \"exit\" }\r\n | { kind: \"clear\" }\r\n | { kind: \"navigate\"; target: \"game\" | \"stock\" };\r\n\r\nexport interface ChatCommand {\r\n desc: string;\r\n handler: () => CommandAction;\r\n}\r\n\r\n/** 命令注册表,支持动态注册新命令 */\r\nconst commandRegistry = new Map<string, ChatCommand>();\r\n\r\n/** 注册一个命令 */\r\nexport function registerCommand(name: string, cmd: ChatCommand): void {\r\n commandRegistry.set(name, cmd);\r\n}\r\n\r\n/** 获取所有已注册命令(用于 /help 生成帮助文本) */\r\nfunction getRegisteredCommands(): Map<string, ChatCommand> {\r\n return commandRegistry;\r\n}\r\n\r\n// 注册内置命令\r\nregisterCommand(\"/exit\", { desc: \"退出对话\", handler: () => ({ kind: \"exit\" }) });\r\nregisterCommand(\"/quit\", { desc: \"退出对话\", handler: () => ({ kind: \"exit\" }) });\r\nregisterCommand(\"/help\", {\r\n desc: \"显示帮助信息\",\r\n handler: () => {\r\n const commands = getRegisteredCommands();\r\n const lines = [\"可用命令:\"];\r\n for (const [name, cmd] of commands) {\r\n lines.push(` ${name.padEnd(16)}${cmd.desc}`);\r\n }\r\n return { kind: \"text\", content: lines.join(\"\\n\") };\r\n },\r\n});\r\nregisterCommand(\"/clear\", { desc: \"清空对话历史\", handler: () => ({ kind: \"clear\" }) });\r\nregisterCommand(\"/version\", { desc: \"显示版本信息\", handler: () => ({ kind: \"text\", content: \"dskcode v0.1.10\" }) });\r\nregisterCommand(\"/model\", { desc: \"切换模型\", handler: () => ({ kind: \"text\", content: \"请直接输入 /model 进入选择界面\" }) });\r\nregisterCommand(\"/thinking\", { desc: \"切换深度思考模式\", handler: () => ({ kind: \"text\", content: \"请直接输入 /thinking 切换\" }) });\r\nregisterCommand(\"/effort\", { desc: \"切换推理等级 High/Max\", handler: () => ({ kind: \"text\", content: \"请直接输入 /effort 切换\" }) });\r\nregisterCommand(\"/game\", { desc: \"启动游戏\", handler: () => ({ kind: \"navigate\", target: \"game\" }) });\r\nregisterCommand(\"/stock\", { desc: \"查看股票行情\", handler: () => ({ kind: \"navigate\", target: \"stock\" }) });\r\n\r\n/** 流式输出时,输入框随机展示的占位符列表 */\r\nconst STREAMING_PLACEHOLDERS = [\r\n \"让子弹飞一会儿...\",\r\n \"马上就好...\",\r\n \"正在憋大招...\",\r\n \"稍等一下下~\",\r\n \"码字中...\",\r\n \"脑子转得飞快...\",\r\n];\r\n\r\n/** 空闲时输入框随机展示的占位符列表 */\r\nconst IDLE_PLACEHOLDERS = [\r\n \"想干啥?直接说~\",\r\n \"来吧,吩咐点啥\",\r\n \"随时待命...\",\r\n \"戳这里开聊...\",\r\n \"等你开口...\",\r\n \"尽管使唤~\",\r\n];\r\n\r\nfunction pickRandom<T>(arr: readonly T[]): T {\r\n return arr[Math.floor(Math.random() * arr.length)]!;\r\n}\r\n\r\n/** 单条已完成的助手消息 */\r\ninterface CompletedAssistant {\r\n content: string;\r\n toolCalls?: ProviderToolCall[];\r\n usage?: UsageInfo;\r\n elapsed?: number;\r\n cost?: number;\r\n model?: string;\r\n}\r\n\r\n/** 单条显示消息 */\r\ninterface DisplayMessage {\r\n role: \"user\" | \"assistant\" | \"tool\";\r\n content: string;\r\n /** 已完成的助手消息详情(仅在 role=assistant 时) */\r\n assistantDetail?: CompletedAssistant;\r\n /** 文件变更 diff(仅在 role=tool 且有 diff 时) */\r\n diff?: FileDiff;\r\n}\r\n\r\ninterface ChatSessionProps {\r\n skillCount: number;\r\n /** 可用 skill 详情列表(用于 / 输入时展示) */\r\n skills?: SkillInfo[];\r\n /** 项目源码文件路径列表(用于 @ 输入时展示) */\r\n files?: string[];\r\n toolCount: number;\r\n verbose: boolean;\r\n apiKey?: string;\r\n baseUrl?: string;\r\n costTracker?: CostTracker;\r\n model?: string;\r\n onLaunchGame?: () => void;\r\n onLaunchStock?: () => void;\r\n}\r\n\r\nexport function ChatSession({\r\n skillCount,\r\n skills = [],\r\n files = [],\r\n toolCount,\r\n verbose,\r\n apiKey,\r\n baseUrl,\r\n costTracker: externalCostTracker,\r\n model = \"deepseek-v4-flash\",\r\n onLaunchGame,\r\n onLaunchStock,\r\n}: ChatSessionProps) {\r\n const termWidth = typeof process.stdout.columns === \"number\" ? process.stdout.columns : 80;\r\n const dividerWidth = Math.max(termWidth - 2, 1);\r\n\r\n const [offset, setOffset] = useState(0);\r\n const [displayMessages, setDisplayMessages] = useState<DisplayMessage[]>([]);\r\n const [staticKey, setStaticKey] = useState(0);\r\n const [input, setInput] = useState(\"\");\r\n const [balance, setBalance] = useState<number | null>(null);\r\n const [balanceLoading, setBalanceLoading] = useState(false);\r\n const [todayCost, setTodayCost] = useState<number | null>(null);\r\n\r\n // 流式状态\r\n const [isStreaming, setIsStreaming] = useState(false);\r\n const [streamingPlaceholder, setStreamingPlaceholder] = useState(\"\");\r\n const [idlePlaceholder, setIdlePlaceholder] = useState(() => pickRandom(IDLE_PLACEHOLDERS));\r\n const [gradientColors, setGradientColors] = useState<string[]>([]);\r\n const gradientPhaseRef = useRef(0);\r\n const [streamingGradientColors, setStreamingGradientColors] = useState<string[]>([]);\r\n const streamingPhaseRef = useRef(0);\r\n const [currentContent, setCurrentContent] = useState(\"\");\r\n const [currentToolCalls, setCurrentToolCalls] = useState<ProviderToolCall[]>([]);\r\n const [currentUsage, setCurrentUsage] = useState<UsageInfo | undefined>(undefined);\r\n const [currentElapsed, setCurrentElapsed] = useState<number | undefined>(undefined);\r\n const [currentCost, setCurrentCost] = useState<number | undefined>(undefined);\r\n const [activeModel, setActiveModel] = useState<ModelId>(model as ModelId);\r\n const [streamingModel, setStreamingModel] = useState<string | undefined>(undefined);\r\n const [streamError, setStreamError] = useState<string | undefined>(undefined);\r\n\r\n // 高级设置\r\n const [thinkingEnabled, setThinkingEnabled] = useState(true);\r\n const [thinkingEffort, setThinkingEffort] = useState<\"high\" | \"max\">(\"high\");\r\n const [responseFormat, setResponseFormat] = useState<\"text\" | \"json_object\">(\"text\");\r\n const [toolChoice, setToolChoice] = useState<\"auto\" | \"required\" | \"none\" | undefined>(undefined);\r\n\r\n // 本次会话累计费用(从已完成的助手消息中汇总)\r\n const sessionCost = useMemo(() => {\r\n return displayMessages.reduce((sum, msg) => {\r\n if (msg.assistantDetail?.cost) {\r\n return sum + msg.assistantDetail.cost;\r\n }\r\n return sum;\r\n }, 0);\r\n }, [displayMessages]);\r\n\r\n const hasConversationStarted = displayMessages.length > 0;\r\n\r\n // 命令提示轮播索引\r\n const cmdTips = Array.from(getRegisteredCommands())\r\n .filter(([name]) => name !== \"/exit\" && name !== \"/quit\")\r\n .map(([name, cmd]) => ({ name, desc: cmd.desc }));\r\n const [cmdTipIndex, setCmdTipIndex] = useState(0);\r\n const [cmdTipGradientColors, setCmdTipGradientColors] = useState<string[]>([]);\r\n const cmdTipPhaseRef = useRef(0);\r\n\r\n // skill 选择索引(用于上下键导航)\r\n const [skillSelectIndex, setSkillSelectIndex] = useState(0);\r\n // 文件选择索引\r\n const [fileSelectIndex, setFileSelectIndex] = useState(0);\r\n // 输入框 key(补全时递增,强制 TextInput 重挂载以重置光标到末尾)\r\n const [inputKey, setInputKey] = useState(0);\r\n\r\n // 模型选择模式\r\n const [selectingModel, setSelectingModel] = useState(false);\r\n const [modelSelectIndex, setModelSelectIndex] = useState(0);\r\n const modelOptions: ModelId[] = [\"deepseek-v4-flash\", \"deepseek-v4-pro\"];\r\n\r\n // Session 引用(保持跨渲染稳定)\r\n const sessionRef = useRef<Session | null>(null);\r\n const abortRef = useRef<AbortController | null>(null);\r\n\r\n // 用 ref 跟踪流式内容的最新值,以便 finally 块能获取非闭包过期的值\r\n const currentContentRef = useRef(\"\");\r\n const currentToolCallsRef = useRef<ProviderToolCall[]>([]);\r\n const currentUsageRef = useRef<UsageInfo | undefined>(undefined);\r\n const currentElapsedRef = useRef<number | undefined>(undefined);\r\n const currentCostRef = useRef<number | undefined>(undefined);\r\n const currentModelRef = useRef<string | undefined>(undefined);\r\n const streamErrorRef = useRef<string | undefined>(undefined);\r\n\r\n // 输入变更时重置选择索引\r\n useEffect(() => {\r\n setSkillSelectIndex(0);\r\n setFileSelectIndex(0);\r\n }, [input]);\r\n\r\n // 获取当前输入匹配的 skill 列表\r\n const getFilteredSkills = useCallback(\r\n (value: string) => {\r\n const match = value.match(/(?:^|\\s)\\/([^/]*)$/);\r\n if (!match) return [];\r\n const q = match[1]!.toLowerCase().trim();\r\n if (!q) {\r\n if (value.startsWith(\"/\")) return skills.slice(0, 3);\r\n return [];\r\n }\r\n const matched = skills.filter((s) => s.name.toLowerCase().includes(q)).slice(0, 3);\r\n // 精确匹配时表示已补全完成,不显示列表\r\n if (matched.some((s) => s.name.toLowerCase() === q)) return [];\r\n return matched;\r\n },\r\n [skills],\r\n );\r\n\r\n // 获取当前输入匹配的文件列表\r\n const getFilteredFiles = useCallback(\r\n (value: string) => {\r\n const match = value.match(/(?:^|\\s)@([^@]*)$/);\r\n if (!match) return [];\r\n const q = match[1]!.toLowerCase().trim();\r\n // @ 在开头且无后续内容时,展示前 5 个文件作为提示\r\n if (!q) {\r\n if (value.startsWith(\"@\")) return files.slice(0, 5);\r\n return [];\r\n }\r\n const matched = files.filter((f) => f.toLowerCase().includes(q)).slice(0, 5);\r\n // 精确匹配时表示已补全完成,不显示列表\r\n if (matched.some((f) => f.toLowerCase() === q)) return [];\r\n return matched;\r\n },\r\n [files],\r\n );\r\n\r\n const { doubleCtrlC, handleCtrlC } = useDoubleCtrlC(() => {\r\n // 双击 Ctrl+C 退出进程\r\n process.exit(0);\r\n });\r\n\r\n // 捕获 Ctrl+C 和渐变占位符状态下的字符输入\r\n useInput(\r\n useCallback(\r\n (_input, key) => {\r\n // 模型选择模式\r\n if (selectingModel) {\r\n if (key.upArrow) {\r\n setModelSelectIndex((prev) => (prev - 1 + modelOptions.length) % modelOptions.length);\r\n } else if (key.downArrow) {\r\n setModelSelectIndex((prev) => (prev + 1) % modelOptions.length);\r\n } else if (key.return) {\r\n const selected = modelOptions[modelSelectIndex]!;\r\n if (selected === activeModel) {\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"assistant\", content: `已经在使用 ${SUPPORTED_MODELS[selected].displayName}` },\r\n ]);\r\n } else {\r\n // 切换模型:持久化到配置 + 重置 Session\r\n setActiveModel(selected);\r\n sessionRef.current?.reset();\r\n saveModelConfig(selected).catch(() => {\r\n // 持久化失败静默处理,不影响当前会话\r\n });\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"assistant\", content: `模型已切换为 ${SUPPORTED_MODELS[selected].displayName}(${selected})` },\r\n ]);\r\n }\r\n setSelectingModel(false);\r\n } else if (key.escape) {\r\n setSelectingModel(false);\r\n }\r\n return;\r\n }\r\n\r\n // 文件 @ 选择模式 / skill 选择模式\r\n // 优先 @(文件匹配),没有文件匹配再走 skill 匹配\r\n const fileList = getFilteredFiles(input);\r\n const skillList = fileList.length === 0 ? getFilteredSkills(input) : [];\r\n\r\n if (fileList.length > 0) {\r\n if (key.upArrow) {\r\n setFileSelectIndex((prev) => (prev - 1 + fileList.length) % fileList.length);\r\n return;\r\n }\r\n if (key.downArrow) {\r\n setFileSelectIndex((prev) => (prev + 1) % fileList.length);\r\n return;\r\n }\r\n // Tab 补全选中的文件\r\n if (key.tab) {\r\n const selected = fileList[fileSelectIndex];\r\n if (selected) {\r\n const atIdx = input.lastIndexOf(\"@\");\r\n if (atIdx >= 0) {\r\n setInput(input.slice(0, atIdx) + \"@\" + selected + \" \");\r\n setInputKey((k) => k + 1);\r\n }\r\n }\r\n return;\r\n }\r\n }\r\n\r\n if (skillList.length > 0) {\r\n if (key.upArrow) {\r\n setSkillSelectIndex((prev) => (prev - 1 + skillList.length) % skillList.length);\r\n return;\r\n }\r\n if (key.downArrow) {\r\n setSkillSelectIndex((prev) => (prev + 1) % skillList.length);\r\n return;\r\n }\r\n // Tab 补全选中的 skill(补全后光标自动移至末尾)\r\n if (key.tab) {\r\n const selected = skillList[skillSelectIndex];\r\n if (selected) {\r\n // 只替换最后一个 / 之后的部分,保留之前输入的内容\r\n // 用 lastIndexOf 定位 / 的精确位置,确保前置空格不被吃掉\r\n const slashIdx = input.lastIndexOf(\"/\");\r\n if (slashIdx >= 0) {\r\n // 补全后加空格分隔,方便继续输入下一个 skill 或问题\r\n setInput(input.slice(0, slashIdx) + \"/\" + selected.name + \" \");\r\n setInputKey((k) => k + 1);\r\n }\r\n }\r\n return;\r\n }\r\n }\r\n\r\n // 模型选择 / 设置选择模式下的通用键盘处理\r\n if (selectingModel) {\r\n if (key.upArrow || key.downArrow) {\r\n // 已在模型选择模式下处理上下键\r\n }\r\n return;\r\n }\r\n\r\n if (key.ctrl && _input === \"c\") {\r\n if (isStreaming) {\r\n abortRef.current?.abort();\r\n } else {\r\n handleCtrlC();\r\n }\r\n return;\r\n }\r\n\r\n // 渐变占位符显示时(TextInput 未渲染),将按键字符加入 input 触发切换\r\n if (!input && !isStreaming && _input) {\r\n setInput(_input);\r\n }\r\n },\r\n [selectingModel, modelSelectIndex, modelOptions, activeModel, isStreaming, handleCtrlC, input, skills, skillSelectIndex, fileSelectIndex, getFilteredSkills, getFilteredFiles]\r\n ),\r\n );\r\n\r\n // 命令提示轮播 — 每 2 秒切换下一条\r\n useEffect(() => {\r\n if (cmdTips.length <= 1) return;\r\n const timer = setInterval(() => {\r\n setCmdTipIndex((prev) => (prev + 1) % cmdTips.length);\r\n }, 2000);\r\n return () => clearInterval(timer);\r\n }, [cmdTips.length]);\r\n\r\n // 命令提示条渐变动画(反向流动:1 - phase)\r\n useEffect(() => {\r\n const tip = cmdTips[cmdTipIndex % cmdTips.length];\r\n if (!tip) {\r\n setCmdTipGradientColors([]);\r\n return;\r\n }\r\n const text = `${tip.name} ${tip.desc}`;\r\n cmdTipPhaseRef.current = 0;\r\n setCmdTipGradientColors(getGradientColors(text, 1, CMD_TIP_GRADIENT_STOPS));\r\n\r\n const interval = setInterval(() => {\r\n cmdTipPhaseRef.current = (cmdTipPhaseRef.current + GRADIENT_ANIMATION.cmdTipPhaseStep) % 1;\r\n // 反向 phase 使色彩从左到右流动\r\n setCmdTipGradientColors(getGradientColors(text, 1 - cmdTipPhaseRef.current, CMD_TIP_GRADIENT_STOPS));\r\n }, GRADIENT_ANIMATION.cmdTipInterval);\r\n\r\n return () => clearInterval(interval);\r\n }, [cmdTipIndex, cmdTips.length]);\r\n\r\n // Logo 色彩动画\r\n useEffect(() => {\r\n const timer = setInterval(() => {\r\n setOffset((prev) => (prev + 1) % CYBER_PALETTE.length);\r\n }, 500);\r\n return () => clearInterval(timer);\r\n }, []);\r\n\r\n // 初始化 Session(当模型切换时重建)\r\n useEffect(() => {\r\n if (!apiKey || !baseUrl) return;\r\n\r\n const provider = createProvider({\r\n name: \"deepseek\",\r\n apiKey,\r\n baseUrl,\r\n model: activeModel,\r\n });\r\n\r\n const tracker = externalCostTracker ?? new CostTracker();\r\n // 注册所有内置工具\r\n const toolRegistry = new ToolRegistry();\r\n toolRegistry.registerAll(builtinTools);\r\n const session = new Session(provider, toolRegistry, tracker, {\r\n cwd: process.cwd(),\r\n });\r\n sessionRef.current = session;\r\n\r\n return () => {\r\n sessionRef.current = null;\r\n };\r\n }, [apiKey, baseUrl, activeModel, externalCostTracker]);\r\n\r\n // 查询余额\r\n useEffect(() => {\r\n if (!apiKey || !baseUrl) return;\r\n let cancelled = false;\r\n setBalanceLoading(true);\r\n import(\"../provider/deepseek.js\").then(({ DeepSeekProvider }) => {\r\n const provider = new DeepSeekProvider({\r\n apiKey,\r\n baseUrl,\r\n model: \"deepseek-v4-flash\",\r\n });\r\n return provider.getBalance();\r\n }).then((result) => {\r\n if (cancelled) return;\r\n const cny = result.balances.find((b) => b.currency === \"CNY\");\r\n if (cny) {\r\n setBalance(cny.totalBalance);\r\n }\r\n }).catch(() => {\r\n // 查询失败静默处理,不影响主流程\r\n }).finally(() => {\r\n if (!cancelled) setBalanceLoading(false);\r\n });\r\n return () => { cancelled = true; };\r\n }, [apiKey, baseUrl]);\r\n\r\n // 加载今日消耗历史数据,并定时刷新\r\n useEffect(() => {\r\n if (!externalCostTracker) return;\r\n let cancelled = false;\r\n let timer: ReturnType<typeof setInterval> | undefined;\r\n\r\n const refresh = () => {\r\n setTodayCost(externalCostTracker.todayTotalCost);\r\n };\r\n\r\n externalCostTracker.load().then(() => {\r\n if (cancelled) return;\r\n refresh();\r\n timer = setInterval(refresh, 5000);\r\n }).catch(() => {\r\n // 加载失败静默处理\r\n });\r\n\r\n return () => {\r\n cancelled = true;\r\n if (timer) clearInterval(timer);\r\n };\r\n }, [externalCostTracker]);\r\n\r\n // 空闲占位符渐变动画\r\n useEffect(() => {\r\n if (isStreaming || !idlePlaceholder) {\r\n setGradientColors([]);\r\n return;\r\n }\r\n\r\n gradientPhaseRef.current = 0;\r\n setGradientColors(getGradientColors(idlePlaceholder, 1, IDLE_GRADIENT_STOPS));\r\n\r\n const interval = setInterval(() => {\r\n gradientPhaseRef.current = (gradientPhaseRef.current + GRADIENT_ANIMATION.idlePhaseStep) % 1;\r\n setGradientColors(getGradientColors(idlePlaceholder, 1 - gradientPhaseRef.current, IDLE_GRADIENT_STOPS));\r\n }, GRADIENT_ANIMATION.idleInterval);\r\n\r\n return () => clearInterval(interval);\r\n }, [isStreaming, idlePlaceholder]);\r\n\r\n // 流式占位符渐变动画\r\n useEffect(() => {\r\n if (!isStreaming || !streamingPlaceholder) {\r\n setStreamingGradientColors([]);\r\n return;\r\n }\r\n\r\n streamingPhaseRef.current = 0;\r\n setStreamingGradientColors(getGradientColors(streamingPlaceholder, 1, STREAMING_GRADIENT_STOPS));\r\n\r\n const interval = setInterval(() => {\r\n streamingPhaseRef.current = (streamingPhaseRef.current + GRADIENT_ANIMATION.streamingPhaseStep) % 1;\r\n setStreamingGradientColors(getGradientColors(streamingPlaceholder, 1 - streamingPhaseRef.current, STREAMING_GRADIENT_STOPS));\r\n }, GRADIENT_ANIMATION.streamingInterval);\r\n\r\n return () => clearInterval(interval);\r\n }, [isStreaming, streamingPlaceholder]);\r\n\r\n /** 处理用户输入 */\r\n const handleSubmit = useCallback(async (value: string) => {\r\n const trimmed = value.trim();\r\n if (!trimmed) return;\r\n\r\n // 处理斜杠命令\r\n if (trimmed.startsWith(\"/\") && trimmed.length > 1) {\r\n const cmdLower = trimmed.toLowerCase();\r\n\r\n // /model 命令:进入模型选择模式\r\n if (cmdLower === \"/model\") {\r\n const curIdx = modelOptions.indexOf(activeModel);\r\n setModelSelectIndex(curIdx >= 0 ? curIdx : 0);\r\n setSelectingModel(true);\r\n setInput(\"\");\r\n return;\r\n }\r\n\r\n // /thinking 命令:切换深度思考\r\n if (cmdLower === \"/thinking\") {\r\n setThinkingEnabled((prev) => !prev);\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"user\", content: trimmed },\r\n { role: \"assistant\", content: `深度思考已${thinkingEnabled ? \"关闭\" : \"开启\"}` },\r\n ]);\r\n setInput(\"\");\r\n return;\r\n }\r\n\r\n // /effort 命令:切换推理等级\r\n if (cmdLower === \"/effort\") {\r\n const next = thinkingEffort === \"high\" ? \"max\" : \"high\";\r\n setThinkingEffort(next);\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"user\", content: trimmed },\r\n { role: \"assistant\", content: `推理等级已切换为 ${next === \"high\" ? \"High\" : \"Max\"}` },\r\n ]);\r\n setInput(\"\");\r\n return;\r\n }\r\n\r\n\r\n\r\n // /tools 命令:切换工具调用策略\r\n if (cmdLower === \"/tools\") {\r\n const next = toolChoice === undefined ? \"none\"\r\n : toolChoice === \"none\" ? \"required\"\r\n : toolChoice === \"required\" ? \"auto\"\r\n : undefined;\r\n setToolChoice(next);\r\n const label = next === undefined ? \"自动(默认)\"\r\n : next === \"none\" ? \"禁止调用\"\r\n : next === \"required\" ? \"强制调用\"\r\n : \"自动(默认)\";\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"user\", content: trimmed },\r\n { role: \"assistant\", content: `工具调用策略已切换为 ${label}` },\r\n ]);\r\n setInput(\"\");\r\n return;\r\n }\r\n\r\n const cmd = commandRegistry.get(cmdLower);\r\n if (cmd) {\r\n const result = cmd.handler();\r\n\r\n switch (result.kind) {\r\n case \"exit\":\r\n process.exit(0);\r\n return;\r\n case \"clear\":\r\n // 清空所有会话状态,回到初始界面\r\n setDisplayMessages([]);\r\n setStaticKey((prev) => prev + 1);\r\n setInput(\"\");\r\n setStreamError(undefined);\r\n // 先清屏再清滚动区,确保终端完全重置\r\n process.stdout.write(\"\\x1b[2J\\x1b[H\\x1b[3J\");\r\n // 重置 Session 历史\r\n sessionRef.current?.reset();\r\n return;\r\n case \"navigate\":\r\n setInput(\"\");\r\n if (result.target === \"game\") {\r\n onLaunchGame?.();\r\n } else if (result.target === \"stock\") {\r\n onLaunchStock?.();\r\n }\r\n return;\r\n case \"text\":\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"user\", content: trimmed },\r\n { role: \"assistant\", content: result.content },\r\n ]);\r\n setInput(\"\");\r\n return;\r\n }\r\n }\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"user\", content: trimmed },\r\n { role: \"assistant\", content: `未知命令:${trimmed}。输入 /help 查看。` },\r\n ]);\r\n setInput(\"\");\r\n return;\r\n }\r\n\r\n // 检查 Session 是否就绪\r\n if (!sessionRef.current) {\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"user\", content: trimmed },\r\n { role: \"assistant\", content: \"⚠ 无法连接到 Provider。请检查 API Key 和网络配置。\" },\r\n ]);\r\n setInput(\"\");\r\n return;\r\n }\r\n\r\n // ---- 正常对话:接入 Agent 流式主循环 ----\r\n\r\n // 追加用户消息到显示列表\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"user\", content: trimmed },\r\n ]);\r\n\r\n // 进入流式状态\r\n setInput(\"\");\r\n // 重置流式状态\r\n setIsStreaming(true);\r\n setStreamingPlaceholder(pickRandom(STREAMING_PLACEHOLDERS));\r\n setCurrentContent(\"\");\r\n setCurrentToolCalls([]);\r\n setCurrentUsage(undefined);\r\n setCurrentElapsed(undefined);\r\n setCurrentCost(undefined);\r\n setStreamingModel(undefined);\r\n setStreamError(undefined);\r\n // 同步重置 ref\r\n currentContentRef.current = \"\";\r\n currentToolCallsRef.current = [];\r\n currentUsageRef.current = undefined;\r\n currentElapsedRef.current = undefined;\r\n currentCostRef.current = undefined;\r\n currentModelRef.current = undefined;\r\n streamErrorRef.current = undefined;\r\n\r\n const session = sessionRef.current;\r\n const abortController = new AbortController();\r\n abortRef.current = abortController;\r\n\r\n try {\r\n for await (const event of session!.chat(trimmed, {\r\n thinkingAllowed: thinkingEnabled || undefined,\r\n thinkingEffort: thinkingEnabled ? thinkingEffort : undefined,\r\n responseFormat: responseFormat !== \"text\" ? responseFormat : undefined,\r\n toolChoice,\r\n })) {\r\n // 如果请求被取消,跳过后续事件\r\n if (abortController.signal.aborted) break;\r\n\r\n switch (event.type) {\r\n case \"text_delta\":\r\n setCurrentContent((prev) => {\r\n const next = prev + event.content;\r\n currentContentRef.current = next;\r\n return next;\r\n });\r\n break;\r\n\r\n case \"tool_calls\":\r\n setCurrentToolCalls((prev) => {\r\n const next = [...prev, ...event.calls];\r\n currentToolCallsRef.current = next;\r\n return next;\r\n });\r\n break;\r\n\r\n case \"tool_result\":\r\n // 工具执行完成 — 重置流式状态,准备接收模型的新一轮回复\r\n // 这是因为 Agent 循环会在工具执行后再次调用模型\r\n setCurrentContent(\"\");\r\n currentContentRef.current = \"\";\r\n setCurrentToolCalls([]);\r\n currentToolCallsRef.current = [];\r\n // 将工具结果追加为一条用户可见的消息\r\n // 优先使用工具提供的 summary(一行简短摘要),避免在 UI 中撑出大段文件内容\r\n const r = event.result;\r\n const line = r.success\r\n ? r.summary ?? `✅ ${event.name}: ${r.data.slice(0, 500)}${r.data.length > 500 ? \"...\" : \"\"}`\r\n : `❌ ${event.name}: ${r.error ?? \"执行失败\"}`;\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n {\r\n role: \"tool\" as const,\r\n content: line,\r\n diff: r.diff,\r\n },\r\n ]);\r\n break;\r\n\r\n case \"usage\":\r\n setCurrentUsage(event.usage);\r\n setStreamingModel(event.model);\r\n currentUsageRef.current = event.usage;\r\n currentModelRef.current = event.model;\r\n // 同步计算费用\r\n {\r\n const cost = calculateCost(event.usage, event.model as ModelId);\r\n setCurrentCost(cost.totalCost);\r\n currentCostRef.current = cost.totalCost;\r\n }\r\n break;\r\n\r\n case \"done\":\r\n setCurrentElapsed(event.elapsed);\r\n currentElapsedRef.current = event.elapsed;\r\n break;\r\n\r\n case \"error\":\r\n setStreamError(event.error.message);\r\n streamErrorRef.current = event.error.message;\r\n break;\r\n }\r\n }\r\n } catch (err: unknown) {\r\n const msg = err instanceof Error ? err.message : String(err);\r\n setStreamError(msg);\r\n streamErrorRef.current = msg;\r\n } finally {\r\n setIsStreaming(false);\r\n setIdlePlaceholder(pickRandom(IDLE_PLACEHOLDERS));\r\n abortRef.current = null;\r\n\r\n // 流式结束后,用 ref 拿到最新值,直接追加完成的助手消息\r\n const finContent = currentContentRef.current;\r\n const finToolCalls = currentToolCallsRef.current.length > 0 ? currentToolCallsRef.current : undefined;\r\n const finStreamError = streamErrorRef.current;\r\n\r\n if (finContent || finToolCalls || finStreamError) {\r\n const completed: CompletedAssistant = {\r\n content: finStreamError ? `⚠ 请求出错:${finStreamError}` : (finContent || \"\"),\r\n toolCalls: finToolCalls,\r\n usage: currentUsageRef.current,\r\n elapsed: currentElapsedRef.current,\r\n cost: currentCostRef.current,\r\n model: currentModelRef.current,\r\n };\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n {\r\n role: \"assistant\" as const,\r\n content: completed.content,\r\n assistantDetail: completed,\r\n },\r\n ]);\r\n }\r\n }\r\n }, [onLaunchGame, onLaunchStock, currentContent, currentToolCalls, skills, skillSelectIndex, getFilteredSkills, thinkingEnabled, thinkingEffort, responseFormat, toolChoice, activeModel]);\r\n\r\n // 从 costTracker 更新今日消耗(每次流式结束后刷新)\r\n useEffect(() => {\r\n if (!isStreaming && externalCostTracker) {\r\n setTodayCost(externalCostTracker.todayTotalCost);\r\n }\r\n }, [isStreaming, externalCostTracker]);\r\n\r\n\r\n\r\n return (\r\n <Box flexDirection=\"column\" paddingLeft={1} paddingRight={1}>\r\n {/* Logo + 状态栏 + 余额 — 三栏布局(仅对话未开始时显示) */}\r\n {!hasConversationStarted && (\r\n <Box flexDirection=\"row\" marginBottom={1}>\r\n {/* Logo */}\r\n <Box flexDirection=\"column\" marginRight={4}>\r\n {LOGO_LINES.map((line, i) => {\r\n const colorIndex = (i + offset) % CYBER_PALETTE.length;\r\n return (\r\n <Box key={i}>\r\n <Text bold color={CYBER_PALETTE[colorIndex]}>\r\n {line}\r\n </Text>\r\n </Box>\r\n );\r\n })}\r\n </Box>\r\n\r\n {/* 状态信息 */}\r\n <Box flexDirection=\"column\" justifyContent=\"center\">\r\n <Text color=\"#00ff41\">{\" ✔ \"}已就绪 {skillCount} 个 Skill</Text>\r\n <Text color=\"#00ffff\">{\" ℹ \"}已就绪 {toolCount} 个工具</Text>\r\n <Text color=\"#00ffff\">{\" 🔧 模型 \"}{SUPPORTED_MODELS[activeModel]?.displayName ?? activeModel}</Text>\r\n {thinkingEnabled && (\r\n <Text color=\"#ff9800\">{\" 🧠 深度思考 \"}{thinkingEffort === \"max\" ? \"Max\" : \"High\"}</Text>\r\n )}\r\n {responseFormat === \"json_object\" && (\r\n <Text color=\"#4caf50\">{\" 📄 JSON\"}</Text>\r\n )}\r\n {toolChoice !== undefined && (\r\n <Text color=\"#e91e63\">{\" 🛠 \"}{\r\n toolChoice === \"none\" ? \"禁止工具\"\r\n : toolChoice === \"required\" ? \"强制工具\"\r\n : \"\"\r\n }</Text>\r\n )}\r\n {/* 命令提示轮播 */}\r\n {cmdTips.length > 0 && (() => {\r\n const tip = cmdTips[cmdTipIndex % cmdTips.length];\r\n if (!tip) return null;\r\n const text = `${tip.name} ${tip.desc}`;\r\n return (\r\n <Text>\r\n <Text color=\"#808080\">{\" 💡 \"}</Text>\r\n {cmdTipGradientColors.length > 0\r\n ? text.split(\"\").map((ch, i) => (\r\n <Text key={i} color={cmdTipGradientColors[i] || undefined}>{ch}</Text>\r\n ))\r\n : <Text color=\"#808080\">{text}</Text>}\r\n </Text>\r\n );\r\n })()}\r\n {verbose ? <Text color=\"#ff1493\">{\" ⚡ Verbose\"}</Text> : null}\r\n </Box>\r\n\r\n {/* 右侧余额 + 今日消耗 */}\r\n <Box flexGrow={1} flexDirection=\"column\" justifyContent=\"center\" alignItems=\"flex-end\">\r\n {balanceLoading && balance === null ? (\r\n <Text color=\"yellow\">{\" ⏳ 查询余额...\"}</Text>\r\n ) : balance !== null ? (\r\n <Box flexDirection=\"row\">\r\n <Text color=\"yellow\">{\"💰 \"}</Text>\r\n <Text color=\"yellow\">{\"余额 ¥\"}{balance.toFixed(2)}</Text>\r\n </Box>\r\n ) : null}\r\n {todayCost !== null ? (\r\n <Box flexDirection=\"row\">\r\n <Text color=\"cyan\">{\"📊 \"}</Text>\r\n <Text color=\"cyan\">{\"今日 ¥\"}{todayCost.toFixed(2)}</Text>\r\n </Box>\r\n ) : null}\r\n </Box>\r\n </Box>\r\n )}\r\n\r\n {/* 消息列表 - 已完成的消息用 Static 固定,避免重绘时丢失滚动位置 */}\r\n <Box flexDirection=\"column\" marginTop={1}>\r\n <Static key={staticKey} items={displayMessages}>\r\n {(msg, i) => {\r\n if (msg.role === \"user\") {\r\n return (\r\n <Box key={i} marginTop={1}>\r\n <Box width={4} flexShrink={0}>\r\n <Text bold color=\"#00ff41\">{\"👤\"}</Text>\r\n </Box>\r\n <Box flexGrow={1}>\r\n <Text wrap=\"wrap\">{msg.content}</Text>\r\n </Box>\r\n </Box>\r\n );\r\n }\r\n\r\n // 工具消息 — 显示文本内容 + Diff 预览\r\n if (msg.role === \"tool\") {\r\n return (\r\n <Box key={i} marginTop={1} flexDirection=\"column\">\r\n <Box flexDirection=\"row\">\r\n <Box width={4} flexShrink={0}>\r\n <Text bold color=\"#f59e0b\">{\"🔧\"}</Text>\r\n </Box>\r\n <Box flexGrow={1}>\r\n <Text wrap=\"wrap\">{msg.content}</Text>\r\n </Box>\r\n </Box>\r\n {msg.diff && <DiffPreview diff={msg.diff} />}\r\n </Box>\r\n );\r\n }\r\n\r\n // 已完成的助手消息\r\n const detail = msg.assistantDetail;\r\n return (\r\n <AssistantMessage\r\n key={i}\r\n content={msg.content}\r\n toolCalls={detail?.toolCalls}\r\n isStreaming={false}\r\n usage={detail?.usage}\r\n elapsed={detail?.elapsed}\r\n cost={detail?.cost}\r\n model={detail?.model}\r\n />\r\n );\r\n }}\r\n </Static>\r\n\r\n {/* 正在流式输出的助手消息 */}\r\n {isStreaming && (\r\n <AssistantMessage\r\n content={currentContent}\r\n toolCalls={currentToolCalls.length > 0 ? currentToolCalls : undefined}\r\n isStreaming={true}\r\n />\r\n )}\r\n\r\n {/* 思考中 Spinner */}\r\n {isStreaming && !currentContent && currentToolCalls.length === 0 && (\r\n <Box marginTop={1} marginLeft={4}>\r\n <Spinner type=\"dots\" label=\"思考中...\" />\r\n </Box>\r\n )}\r\n\r\n {/* 错误信息(流式结束后显示) */}\r\n {!isStreaming && streamError && (\r\n <Box marginTop={1} marginLeft={3}>\r\n <Text color=\"red\">⚠ {streamError}</Text>\r\n </Box>\r\n )}\r\n </Box>\r\n\r\n {/* 输入区 */}\r\n {selectingModel ? (\r\n <Box marginTop={1} flexDirection=\"column\">\r\n <Text color=\"#00ffff\" dimColor>{\"─\".repeat(dividerWidth)}</Text>\r\n <Box flexDirection=\"column\" marginTop={1}>\r\n <Text bold color=\"#00ffff\">选择模型:</Text>\r\n {modelOptions.map((id, i) => {\r\n const meta = SUPPORTED_MODELS[id];\r\n const isCurrent = id === activeModel;\r\n const isSelected = i === modelSelectIndex;\r\n const marker = isSelected ? \" > \" : \" \";\r\n const suffix = isCurrent ? \" (当前)\" : \"\";\r\n return (\r\n <Box key={id}>\r\n <Text\r\n color={isSelected ? \"#00ff41\" : undefined}\r\n bold={isSelected}\r\n >\r\n {marker}{meta.displayName}{suffix}\r\n </Text>\r\n {isSelected && <Text color=\"#808080\"> — {id}</Text>}\r\n </Box>\r\n );\r\n })}\r\n <Box marginTop={1}>\r\n <Text color=\"#808080\" dimColor>↑↓ 选择 · Enter 确认 · Esc 取消</Text>\r\n </Box>\r\n </Box>\r\n <Text color=\"#00ffff\" dimColor>{\"─\".repeat(dividerWidth)}</Text>\r\n </Box>\r\n ) : (\r\n <>\r\n <Box marginTop={1}>\r\n {hasConversationStarted && (balance !== null || sessionCost > 0 || isStreaming) ? (\r\n <Text color=\"#00ffff\" dimColor>\r\n {\"─\".repeat(Math.max(dividerWidth - 35, 10))}\r\n {balance !== null && (\r\n <Text color=\"yellow\">{\" 💰 余额 ¥\"}{balance.toFixed(2)}</Text>\r\n )}\r\n {isStreaming ? (\r\n <Text color=\"cyan\">{\" 📊 本次 ¥\"}{sessionCost > 0 ? sessionCost.toFixed(4) + \" \" : \"\"}<InkSpinner type=\"dots\" /></Text>\r\n ) : sessionCost > 0 ? (\r\n <Text color=\"cyan\">{\" 📊 本次 ¥\"}{sessionCost.toFixed(4)}</Text>\r\n ) : null}\r\n </Text>\r\n ) : (\r\n <Text color=\"#00ffff\" dimColor>{\"─\".repeat(dividerWidth)}</Text>\r\n )}\r\n </Box>\r\n <Box>\r\n <Box width={4} flexShrink={0}>\r\n <Text bold color=\"#00ff41\">{\"⚡\"}</Text>\r\n </Box>\r\n <Box flexGrow={1}>\r\n {!input && !isStreaming && idlePlaceholder && gradientColors.length > 0 ? (\r\n <Text>\r\n {idlePlaceholder.split(\"\").map((ch, i) => (\r\n <Text key={i} color={gradientColors[i] ?? undefined}>{ch}</Text>\r\n ))}\r\n </Text>\r\n ) : !input && isStreaming && streamingPlaceholder && streamingGradientColors.length > 0 ? (\r\n <Text>\r\n {streamingPlaceholder.split(\"\").map((ch, i) => (\r\n <Text key={i} color={streamingGradientColors[i] ?? undefined}>{ch}</Text>\r\n ))}\r\n </Text>\r\n ) : (\r\n <TextInput\r\n key={inputKey}\r\n value={input}\r\n onChange={setInput}\r\n onSubmit={handleSubmit}\r\n placeholder=\"\"\r\n />\r\n )}\r\n </Box>\r\n </Box>\r\n <Box>\r\n <Text color=\"#00ffff\" dimColor>{\"─\".repeat(dividerWidth)}</Text>\r\n </Box>\r\n\r\n {/* 用户输入 / 时显示 skill 列表 */}\r\n <SkillSelector skills={skills} input={input} selectedIndex={skillSelectIndex} />\r\n {/* 用户输入 @ 时显示文件列表 */}\r\n <FileSelector files={files} input={input} selectedIndex={fileSelectIndex} />\r\n </>\r\n )}\r\n\r\n {/* 双击 Ctrl+C 退出提示 */}\r\n {doubleCtrlC && !isStreaming && (\r\n <Box marginTop={1}>\r\n <Text color=\"#ff1493\" bold>\r\n {\" ⚠ 再按一次 Ctrl+C 退出 dskcode\"}\r\n </Text>\r\n </Box>\r\n )}\r\n\r\n {/* 流式中 Ctrl+C 取消提示 */}\r\n {isStreaming && (\r\n <Box marginTop={1}>\r\n <Text color=\"yellow\" dimColor>\r\n {\" 提示:按 Ctrl+C 取消当前请求\"}\r\n </Text>\r\n </Box>\r\n )}\r\n </Box>\r\n );\r\n}\r\n","import { useState, useCallback, useEffect, useRef } from \"react\";\r\n\r\nconst CTRL_C_TIMEOUT_MS = 1500;\r\n\r\n/**\r\n * 提供\"双击 Ctrl+C 才退出\"的交互逻辑。\r\n *\r\n * - 第一次 Ctrl+C:设置提示状态,1.5 秒后自动重置\r\n * - 第二次 Ctrl+C(1.5 秒内):执行 onExit 回调\r\n *\r\n * 返回 { doubleCtrlC, handleCtrlC },组件通过 useInput 捕获\r\n * Ctrl+C 后调用 handleCtrlC,并根据 doubleCtrlC 显示提示。\r\n */\r\nexport function useDoubleCtrlC(onExit: () => void) {\r\n const [doubleCtrlC, setDoubleCtrlC] = useState(false);\r\n const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\r\n const onExitRef = useRef(onExit);\r\n onExitRef.current = onExit;\r\n\r\n // 组件卸载时清理定时器\r\n useEffect(() => {\r\n return () => {\r\n if (timerRef.current) clearTimeout(timerRef.current);\r\n };\r\n }, []);\r\n\r\n const handleCtrlC = useCallback(() => {\r\n if (doubleCtrlC) {\r\n // 第二次 Ctrl+C → 退出\r\n onExitRef.current();\r\n return;\r\n }\r\n\r\n // 第一次 Ctrl+C → 显示提示,启动倒计时重置\r\n setDoubleCtrlC(true);\r\n if (timerRef.current) clearTimeout(timerRef.current);\r\n timerRef.current = setTimeout(() => {\r\n setDoubleCtrlC(false);\r\n timerRef.current = null;\r\n }, CTRL_C_TIMEOUT_MS);\r\n }, [doubleCtrlC]);\r\n\r\n return { doubleCtrlC, handleCtrlC };\r\n}","// ---------------------------------------------------------------------------\r\n// 助手消息组件 — 渲染单条 assistant 回复(流式文本 + 工具调用 + 成本行)\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { Box, Text } from \"ink\";\r\nimport type { ProviderToolCall, UsageInfo } from \"../provider/index.js\";\r\nimport { formatMoney, formatCallCostLine } from \"../provider/cost-tracker.js\";\r\nimport { ToolCallBlock } from \"./ToolCallBlock.js\";\r\nimport { formatUsageSummary } from \"../agent/message-builder.js\";\r\nimport { HighlightedText } from \"./HighlightedText.js\";\r\n\r\ninterface AssistantMessageProps {\r\n /** 助手回复的文本内容 */\r\n content: string;\r\n /** 工具调用列表 */\r\n toolCalls?: ProviderToolCall[];\r\n /** 是否正在流式输出中 */\r\n isStreaming?: boolean;\r\n /** Token 使用统计 */\r\n usage?: UsageInfo;\r\n /** 本次调用耗时(毫秒) */\r\n elapsed?: number;\r\n /** 本次调用费用 */\r\n cost?: number;\r\n /** 使用的模型标识 */\r\n model?: string;\r\n}\r\n\r\n/** 格式化毫秒为人类可读的时间 */\r\nfunction formatElapsed(ms: number): string {\r\n if (ms < 1000) return `${ms}ms`;\r\n const seconds = (ms / 1000).toFixed(1);\r\n return `${seconds}s`;\r\n}\r\n\r\n/**\r\n * 渲染单条助手回复。\r\n *\r\n * 显示结构:\r\n * 🤖 流式文本内容...\r\n * 📦 tool_name ────\r\n * 参数摘要\r\n * ⏳ 等待执行\r\n * ────────────────────────────\r\n * 💰 本次 ¥0.0012 │ 🕐 3.2s │ 📦 1.2k tokens\r\n */\r\nexport function AssistantMessage({\r\n content,\r\n toolCalls,\r\n isStreaming = false,\r\n usage,\r\n elapsed,\r\n cost,\r\n model,\r\n}: AssistantMessageProps) {\r\n // 内容为空且无工具调用时不渲染\r\n if (!content && (!toolCalls || toolCalls.length === 0) && !isStreaming) {\r\n return null;\r\n }\r\n\r\n return (\r\n <Box flexDirection=\"column\" marginTop={1}>\r\n {/* 助手标识 + 内容 */}\r\n <Box flexDirection=\"row\">\r\n <Box width={4} flexShrink={0}>\r\n <Text bold color=\"#ff00ff\">{\"🤖\"}</Text>\r\n </Box>\r\n <Box flexGrow={1} flexDirection=\"column\">\r\n {/* 文本内容(带语法高亮) */}\r\n {content && (\r\n <HighlightedText>{content}</HighlightedText>\r\n )}\r\n {/* 流式输出时的光标 */}\r\n {isStreaming && !content && (\r\n <Text color=\"#888888\">...</Text>\r\n )}\r\n </Box>\r\n </Box>\r\n\r\n {/* 工具调用 */}\r\n {toolCalls && toolCalls.length > 0 && (\r\n <Box flexDirection=\"column\">\r\n {toolCalls.map((tc, i) => (\r\n <ToolCallBlock key={tc.id ?? i} call={tc} />\r\n ))}\r\n </Box>\r\n )}\r\n\r\n {/* 成本/耗时摘要行(仅在流式结束后显示) */}\r\n {!isStreaming && (usage || elapsed !== undefined) && (\r\n <Box flexDirection=\"column\" marginTop={1} marginLeft={3}>\r\n <Text color=\"#555555\">{\"─\".repeat(36)}</Text>\r\n <Box flexDirection=\"row\" gap={2}>\r\n {cost !== undefined && cost > 0 && (\r\n <Text color=\"yellow\">\r\n 💰 本次 {formatMoney(cost)}\r\n </Text>\r\n )}\r\n {elapsed !== undefined && (\r\n <Text color=\"cyan\">\r\n 🕐 {formatElapsed(elapsed)}\r\n </Text>\r\n )}\r\n {usage && (\r\n <Text color=\"#888888\">\r\n {formatUsageSummary(usage)}\r\n </Text>\r\n )}\r\n </Box>\r\n </Box>\r\n )}\r\n </Box>\r\n );\r\n}","// ---------------------------------------------------------------------------\r\n// 成本追踪器 — 会话级 / 日级 / 历史级三层成本统计与持久化\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { mkdir, readFile, writeFile } from \"node:fs/promises\";\r\nimport { join } from \"node:path\";\r\nimport type { UsageInfo, CostInfo, ModelId } from \"./types.js\";\r\nimport { calculateCost } from \"./models.js\";\r\n\r\n// ---------------------------------------------------------------------------\r\n// 类型定义\r\n// ---------------------------------------------------------------------------\r\n\r\n/** 单次 API 调用的成本记录 */\r\nexport interface CostRecord {\r\n /** 记录时间戳(ISO 8601) */\r\n timestamp: string;\r\n /** 使用的模型 */\r\n model: ModelId;\r\n /** Token 使用量 */\r\n usage: UsageInfo;\r\n /** 费用明细(单位:元) */\r\n cost: CostInfo;\r\n}\r\n\r\n/** 单个会话的成本汇总 */\r\nexport interface SessionCostSummary {\r\n /** 会话 ID */\r\n sessionId: string;\r\n /** 会话开始时间(ISO 8601) */\r\n startedAt: string;\r\n /** 总输入 token 数 */\r\n totalPromptTokens: number;\r\n /** 总输出 token 数 */\r\n totalCompletionTokens: number;\r\n /** 总缓存命中 token 数 */\r\n totalCachedTokens: number;\r\n /** 总费用(元) */\r\n totalCost: number;\r\n /** 每次调用的详细记录 */\r\n records: CostRecord[];\r\n}\r\n\r\n/** 一天的成本汇总 */\r\nexport interface DailyCostSummary {\r\n /** 日期(YYYY-MM-DD 格式) */\r\n date: string;\r\n /** 当日总输入 token 数 */\r\n totalPromptTokens: number;\r\n /** 当日总输出 token 数 */\r\n totalCompletionTokens: number;\r\n /** 当日总缓存命中 token 数 */\r\n totalCachedTokens: number;\r\n /** 当日总费用(元) */\r\n totalCost: number;\r\n /** 当日 API 调用次数 */\r\n totalCalls: number;\r\n /** 按模型分类的明细 */\r\n byModel: Record<string, ModelCostSummary>;\r\n}\r\n\r\n/** 按模型分类的成本汇总 */\r\nexport interface ModelCostSummary {\r\n /** 模型标识 */\r\n model: ModelId;\r\n /** 该模型的总输入 token 数 */\r\n totalPromptTokens: number;\r\n /** 该模型的总输出 token 数 */\r\n totalCompletionTokens: number;\r\n /** 该模型的总缓存命中 token 数 */\r\n totalCachedTokens: number;\r\n /** 该模型的总费用(元) */\r\n totalCost: number;\r\n /** 该模型的 API 调用次数 */\r\n totalCalls: number;\r\n}\r\n\r\n/** 持久化文件格式 */\r\nexport interface CostStore {\r\n /** 版本号,便于未来格式迁移 */\r\n version: 1;\r\n /** 按日期索引的汇总数据 */\r\n daily: Record<string, DailyCostSummary>;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// CostTracker 配置\r\n// ---------------------------------------------------------------------------\r\n\r\nexport interface CostTrackerOptions {\r\n /** 成本持久化目录,默认 ~/.dskcode/costs */\r\n costDir?: string;\r\n /** 预算上限(元),超过后触发回调,0 表示不限制 */\r\n budgetLimit?: number;\r\n /** Token 预算上限,超过后触发回调,0 表示不限制 */\r\n tokenBudgetLimit?: number;\r\n /** 预算超限回调 */\r\n onBudgetExceeded?: (tracker: CostTracker) => void;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// CostTracker 类\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * 成本追踪器 — 管理三层成本统计:\r\n *\r\n * 1. **会话级**(内存):当前会话内的累计 token 与费用\r\n * 2. **日级**(内存 + 持久化):按日聚合,支持\"今日消耗\"查询\r\n * 3. **历史级**(持久化):跨日的历史汇总查询\r\n *\r\n * 使用方式:\r\n * ```ts\r\n * const tracker = new CostTracker({ budgetLimit: 10 });\r\n * tracker.record(usage, 'deepseek-v4-flash');\r\n * console.log(tracker.todaySummary());\r\n * ```\r\n */\r\nexport class CostTracker {\r\n readonly #costDir: string;\r\n readonly #budgetLimit: number;\r\n readonly #tokenBudgetLimit: number;\r\n readonly #onBudgetExceeded?: (tracker: CostTracker) => void;\r\n\r\n // 会话级统计(内存)\r\n #sessionId: string;\r\n #sessionStartedAt: string;\r\n readonly #sessionRecords: CostRecord[] = [];\r\n\r\n // 日级统计(内存缓存,启动时从磁盘恢复)\r\n #todayDate: string;\r\n #todaySummary: DailyCostSummary;\r\n\r\n // 持久化标记\r\n #dirty = false;\r\n #flushInProgress = false;\r\n\r\n constructor(options: CostTrackerOptions = {}) {\r\n const home = process.env.HOME ?? process.env.USERPROFILE ?? \"~\";\r\n this.#costDir = options.costDir ?? join(home, \".dskcode\", \"costs\");\r\n this.#budgetLimit = options.budgetLimit ?? 0;\r\n this.#tokenBudgetLimit = options.tokenBudgetLimit ?? 0;\r\n this.#onBudgetExceeded = options.onBudgetExceeded;\r\n\r\n // 初始化会话\r\n this.#sessionId = generateSessionId();\r\n this.#sessionStartedAt = new Date().toISOString();\r\n\r\n // 初始化日级统计\r\n const today = getTodayStr();\r\n this.#todayDate = today;\r\n this.#todaySummary = createEmptyDailySummary(today);\r\n }\r\n\r\n // -----------------------------------------------------------------------\r\n // 核心方法\r\n // -----------------------------------------------------------------------\r\n\r\n /**\r\n * 记录一次 API 调用的 Token 使用量和成本。\r\n * 自动计算费用,累加到会话级和日级统计。\r\n */\r\n record(usage: UsageInfo, model: ModelId): CostInfo {\r\n const cost = calculateCost(usage, model);\r\n const record: CostRecord = {\r\n timestamp: new Date().toISOString(),\r\n model,\r\n usage: { ...usage },\r\n cost: { ...cost },\r\n };\r\n\r\n // 1. 会话级累加\r\n this.#sessionRecords.push(record);\r\n\r\n // 2. 日级累加(自动处理日期变更)\r\n this.#ensureTodayBucket();\r\n this.#addToDaily(record);\r\n\r\n // 3. 标记需要持久化\r\n this.#dirty = true;\r\n\r\n // 4. 预算检查\r\n this.#checkBudget();\r\n\r\n return cost;\r\n }\r\n\r\n // -----------------------------------------------------------------------\r\n // 会话级查询\r\n // -----------------------------------------------------------------------\r\n\r\n /** 当前会话 ID */\r\n get sessionId(): string {\r\n return this.#sessionId;\r\n }\r\n\r\n /** 当前会话的所有成本记录 */\r\n get records(): readonly CostRecord[] {\r\n return this.#sessionRecords;\r\n }\r\n\r\n /** 当前会话累计成本汇总 */\r\n get sessionSummary(): SessionCostSummary {\r\n let totalPromptTokens = 0;\r\n let totalCompletionTokens = 0;\r\n let totalCachedTokens = 0;\r\n let totalCost = 0;\r\n\r\n for (const r of this.#sessionRecords) {\r\n totalPromptTokens += r.usage.promptTokens;\r\n totalCompletionTokens += r.usage.completionTokens;\r\n totalCachedTokens += r.usage.cachedPromptTokens ?? 0;\r\n totalCost += r.cost.totalCost;\r\n }\r\n\r\n return {\r\n sessionId: this.#sessionId,\r\n startedAt: this.#sessionStartedAt,\r\n totalPromptTokens,\r\n totalCompletionTokens,\r\n totalCachedTokens,\r\n totalCost,\r\n records: [...this.#sessionRecords],\r\n };\r\n }\r\n\r\n /** 当前会话总费用(元) */\r\n get sessionTotalCost(): number {\r\n return this.#sessionRecords.reduce((sum, r) => sum + r.cost.totalCost, 0);\r\n }\r\n\r\n /** 当前会话总 prompt token 数 */\r\n get sessionPromptTokens(): number {\r\n return this.#sessionRecords.reduce((sum, r) => sum + r.usage.promptTokens, 0);\r\n }\r\n\r\n /** 当前会话总 completion token 数 */\r\n get sessionCompletionTokens(): number {\r\n return this.#sessionRecords.reduce((sum, r) => sum + r.usage.completionTokens, 0);\r\n }\r\n\r\n /** 当前会话 API 调用次数 */\r\n get sessionCallCount(): number {\r\n return this.#sessionRecords.length;\r\n }\r\n\r\n // -----------------------------------------------------------------------\r\n // 日级查询\r\n // -----------------------------------------------------------------------\r\n\r\n /** 今日成本汇总 */\r\n get todaySummary(): DailyCostSummary {\r\n this.#ensureTodayBucket();\r\n return { ...this.#todaySummary };\r\n }\r\n\r\n /** 今日总费用(元) */\r\n get todayTotalCost(): number {\r\n this.#ensureTodayBucket();\r\n return this.#todaySummary.totalCost;\r\n }\r\n\r\n /** 今日 API 调用次数 */\r\n get todayCallCount(): number {\r\n this.#ensureTodayBucket();\r\n return this.#todaySummary.totalCalls;\r\n }\r\n\r\n // -----------------------------------------------------------------------\r\n // 预算检查\r\n // -----------------------------------------------------------------------\r\n\r\n /** 是否已超出预算 */\r\n get isBudgetExceeded(): boolean {\r\n if (this.#budgetLimit > 0 && this.todayTotalCost >= this.#budgetLimit) {\r\n return true;\r\n }\r\n if (this.#tokenBudgetLimit > 0) {\r\n const totalTokens =\r\n this.#todaySummary.totalPromptTokens +\r\n this.#todaySummary.totalCompletionTokens;\r\n if (totalTokens >= this.#tokenBudgetLimit) return true;\r\n }\r\n return false;\r\n }\r\n\r\n /** 剩余预算(元),无限制时返回 Infinity */\r\n get remainingBudget(): number {\r\n if (this.#budgetLimit <= 0) return Infinity;\r\n return Math.max(0, this.#budgetLimit - this.todayTotalCost);\r\n }\r\n\r\n // -----------------------------------------------------------------------\r\n // 持久化\r\n // -----------------------------------------------------------------------\r\n\r\n /**\r\n * 从磁盘加载历史成本数据。\r\n * 启动时调用,用于恢复今日和历史数据。\r\n */\r\n async load(): Promise<void> {\r\n const store = await this.#loadStore();\r\n const today = getTodayStr();\r\n\r\n if (store.daily[today]) {\r\n this.#todaySummary = store.daily[today];\r\n this.#todayDate = today;\r\n }\r\n }\r\n\r\n /**\r\n * 将脏数据持久化到磁盘。\r\n * 通常在每次 record() 后异步调用,或在会话结束时主动调用。\r\n */\r\n async flush(): Promise<void> {\r\n if (!this.#dirty || this.#flushInProgress) return;\r\n this.#flushInProgress = true;\r\n this.#dirty = false;\r\n\r\n try {\r\n await this.#saveStore();\r\n } finally {\r\n this.#flushInProgress = false;\r\n }\r\n }\r\n\r\n /**\r\n * 查询指定日期范围的成本汇总。\r\n * @param startDate 起始日期(YYYY-MM-DD),包含\r\n * @param endDate 截止日期(YYYY-MM-DD),包含,默认同 startDate\r\n */\r\n async queryRange(\r\n startDate: string,\r\n endDate?: string,\r\n ): Promise<DailyCostSummary[]> {\r\n const store = await this.#loadStore();\r\n const end = endDate ?? startDate;\r\n const result: DailyCostSummary[] = [];\r\n\r\n for (const [date, summary] of Object.entries(store.daily)) {\r\n if (date >= startDate && date <= end) {\r\n result.push(summary);\r\n }\r\n }\r\n\r\n result.sort((a, b) => a.date.localeCompare(b.date));\r\n return result;\r\n }\r\n\r\n /** 重置会话(开始新会话,不影响日级累计) */\r\n resetSession(): void {\r\n this.#sessionId = generateSessionId();\r\n this.#sessionStartedAt = new Date().toISOString();\r\n this.#sessionRecords.length = 0;\r\n }\r\n\r\n // -----------------------------------------------------------------------\r\n // 内部方法\r\n // -----------------------------------------------------------------------\r\n\r\n /** 确保今日的统计桶存在(处理跨日情况) */\r\n #ensureTodayBucket(): void {\r\n const today = getTodayStr();\r\n if (today !== this.#todayDate) {\r\n // 日期变更:将旧日数据持久化后切换到新日\r\n this.#todayDate = today;\r\n this.#todaySummary = createEmptyDailySummary(today);\r\n this.#dirty = true;\r\n }\r\n }\r\n\r\n /** 将一条记录累加到日级汇总 */\r\n #addToDaily(record: CostRecord): void {\r\n this.#todaySummary.totalPromptTokens += record.usage.promptTokens;\r\n this.#todaySummary.totalCompletionTokens += record.usage.completionTokens;\r\n this.#todaySummary.totalCachedTokens += record.usage.cachedPromptTokens ?? 0;\r\n this.#todaySummary.totalCost += record.cost.totalCost;\r\n this.#todaySummary.totalCalls += 1;\r\n\r\n // 按模型分类累加\r\n const modelKey = record.model;\r\n if (!this.#todaySummary.byModel[modelKey]) {\r\n this.#todaySummary.byModel[modelKey] = {\r\n model: record.model,\r\n totalPromptTokens: 0,\r\n totalCompletionTokens: 0,\r\n totalCachedTokens: 0,\r\n totalCost: 0,\r\n totalCalls: 0,\r\n };\r\n }\r\n\r\n const modelSummary = this.#todaySummary.byModel[modelKey];\r\n modelSummary.totalPromptTokens += record.usage.promptTokens;\r\n modelSummary.totalCompletionTokens += record.usage.completionTokens;\r\n modelSummary.totalCachedTokens += record.usage.cachedPromptTokens ?? 0;\r\n modelSummary.totalCost += record.cost.totalCost;\r\n modelSummary.totalCalls += 1;\r\n }\r\n\r\n /** 预算超限检查 */\r\n #checkBudget(): void {\r\n if (!this.isBudgetExceeded) return;\r\n this.#onBudgetExceeded?.(this);\r\n }\r\n\r\n /** 从磁盘加载成本存储 */\r\n async #loadStore(): Promise<CostStore> {\r\n const filePath = join(this.#costDir, \"history.json\");\r\n try {\r\n const raw = await readFile(filePath, \"utf-8\");\r\n return JSON.parse(raw) as CostStore;\r\n } catch {\r\n return { version: 1, daily: {} };\r\n }\r\n }\r\n\r\n /** 保存成本存储到磁盘 */\r\n async #saveStore(): Promise<void> {\r\n const store = await this.#loadStore();\r\n\r\n // 合并今日数据到存储中\r\n store.daily[this.#todayDate] = this.#todaySummary;\r\n\r\n // 保留最近 90 天的数据(避免文件过大)\r\n const cutoffDate = getDateNDaysAgo(90);\r\n const datesToRemove = Object.keys(store.daily).filter(\r\n (date) => date < cutoffDate,\r\n );\r\n for (const date of datesToRemove) {\r\n // eslint-disable-next-line @typescript-eslint/no-dynamic-delete\r\n delete store.daily[date];\r\n }\r\n\r\n // 确保目录存在\r\n await mkdir(this.#costDir, { recursive: true });\r\n\r\n const filePath = join(this.#costDir, \"history.json\");\r\n await writeFile(filePath, JSON.stringify(store, null, 2), \"utf-8\");\r\n }\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 工具函数\r\n// ---------------------------------------------------------------------------\r\n\r\n/** 生成会话 ID:时间戳 + 随机后缀 */\r\nfunction generateSessionId(): string {\r\n const ts = Date.now().toString(36);\r\n const rand = Math.random().toString(36).slice(2, 8);\r\n return `${ts}-${rand}`;\r\n}\r\n\r\n/** 获取今日日期字符串(YYYY-MM-DD) */\r\nfunction getTodayStr(): string {\r\n return new Date().toISOString().slice(0, 10);\r\n}\r\n\r\n/** 获取 N 天前的日期字符串 */\r\nfunction getDateNDaysAgo(n: number): string {\r\n const d = new Date();\r\n d.setDate(d.getDate() - n);\r\n return d.toISOString().slice(0, 10);\r\n}\r\n\r\n/** 创建空的日级成本汇总 */\r\nfunction createEmptyDailySummary(date: string): DailyCostSummary {\r\n return {\r\n date,\r\n totalPromptTokens: 0,\r\n totalCompletionTokens: 0,\r\n totalCachedTokens: 0,\r\n totalCost: 0,\r\n totalCalls: 0,\r\n byModel: {},\r\n };\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 格式化工具\r\n// ---------------------------------------------------------------------------\r\n\r\n/** 格式化费用金额(保留4位小数) */\r\nexport function formatMoney(yuan: number): string {\r\n return `¥${yuan.toFixed(4)}`;\r\n}\r\n\r\n/** 格式化 token 数量(带千分位) */\r\nexport function formatTokens(tokens: number): string {\r\n return tokens.toLocaleString(\"en-US\");\r\n}\r\n\r\n/** 格式化缓存命中率百分比 */\r\nexport function formatCacheHitRate(cached: number, total: number): string {\r\n if (total === 0) return \"0.0%\";\r\n return `${((cached / total) * 100).toFixed(1)}%`;\r\n}\r\n\r\n/** 生成今日消耗报告的终端友好文本 */\r\nexport function formatTodayReport(summary: DailyCostSummary): string {\r\n const lines: string[] = [];\r\n\r\n lines.push(`📊 今日消耗报告 (${summary.date})`);\r\n lines.push(\"─\".repeat(40));\r\n lines.push(` 💰 总费用: ${formatMoney(summary.totalCost)}`);\r\n lines.push(` 📞 调用次数: ${String(summary.totalCalls)} 次`);\r\n lines.push(` 📥 输入 Token: ${formatTokens(summary.totalPromptTokens)}`);\r\n lines.push(` 📤 输出 Token: ${formatTokens(summary.totalCompletionTokens)}`);\r\n lines.push(\r\n ` 🗄️ 缓存命中: ${formatTokens(summary.totalCachedTokens)} (${formatCacheHitRate(summary.totalCachedTokens, summary.totalPromptTokens)})`,\r\n );\r\n lines.push(\"\");\r\n\r\n // 按模型分类明细\r\n const models = Object.values(summary.byModel);\r\n if (models.length > 0) {\r\n lines.push(\"📈 按模型分类:\");\r\n for (const m of models) {\r\n lines.push(` ─ ${m.model} ─`);\r\n lines.push(` 费用: ${formatMoney(m.totalCost)} | 调用: ${String(m.totalCalls)} 次`);\r\n lines.push(\r\n ` 输入: ${formatTokens(m.totalPromptTokens)} | 输出: ${formatTokens(m.totalCompletionTokens)}`,\r\n );\r\n lines.push(\r\n ` 缓存命中: ${formatTokens(m.totalCachedTokens)} (${formatCacheHitRate(m.totalCachedTokens, m.totalPromptTokens)})`,\r\n );\r\n }\r\n }\r\n\r\n return lines.join(\"\\n\");\r\n}\r\n\r\n/** 生成会话成本摘要的单行文本 */\r\nexport function formatSessionCostLine(summary: SessionCostSummary): string {\r\n const cacheRate = formatCacheHitRate(\r\n summary.totalCachedTokens,\r\n summary.totalPromptTokens,\r\n );\r\n return (\r\n `💰 ¥${summary.totalCost.toFixed(4)} | ` +\r\n `📥${formatTokens(summary.totalPromptTokens)} ` +\r\n `📤${formatTokens(summary.totalCompletionTokens)} ` +\r\n `🗄️${cacheRate} | ` +\r\n `📞${String(summary.records.length)}次`\r\n );\r\n}\r\n\r\n/** 生成单次调用的成本单行文本 */\r\nexport function formatCallCostLine(record: CostRecord): string {\r\n const cacheRate = formatCacheHitRate(\r\n record.usage.cachedPromptTokens ?? 0,\r\n record.usage.promptTokens,\r\n );\r\n return (\r\n `≈¥${record.cost.totalCost.toFixed(4)} ` +\r\n `(📥${String(record.usage.promptTokens)} 📤${String(record.usage.completionTokens)} 🗄️${cacheRate})`\r\n );\r\n}","// ---------------------------------------------------------------------------\r\n// 工具调用块组件 — 在终端中渲染单个工具调用\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { Box, Text } from \"ink\";\r\nimport type { ProviderToolCall } from \"../provider/index.js\";\r\n\r\ninterface ToolCallBlockProps {\r\n call: ProviderToolCall;\r\n /** 是否显示\"等待执行\"提示(默认 true) */\r\n showPendingHint?: boolean;\r\n}\r\n\r\n/**\r\n * 格式化工具调用参数的摘要。\r\n * 短参数直接展示,长参数截断。\r\n */\r\nfunction formatArgsSummary(args: string): string {\r\n try {\r\n const parsed = JSON.parse(args) as Record<string, unknown>;\r\n const lines = Object.entries(parsed).map(([key, value]) => {\r\n const val = String(value);\r\n const truncated = val.length > 80 ? val.slice(0, 77) + \"...\" : val;\r\n return ` ${key}: ${truncated}`;\r\n });\r\n return lines.join(\"\\n\");\r\n } catch {\r\n // 无法解析的 JSON,直接截断展示\r\n const truncated = args.length > 120 ? args.slice(0, 117) + \"...\" : args;\r\n return ` ${truncated}`;\r\n }\r\n}\r\n\r\n/**\r\n * 渲染单个工具调用信息块。\r\n *\r\n * 显示效果:\r\n * 📦 read_file ─────────────────\r\n * 📂 path: src/provider/types.ts\r\n * ⏳ 等待执行\r\n */\r\nexport function ToolCallBlock({ call, showPendingHint = true }: ToolCallBlockProps) {\r\n const argsDisplay = formatArgsSummary(call.arguments);\r\n\r\n return (\r\n <Box flexDirection=\"column\" marginLeft={3} marginTop={1}>\r\n <Box>\r\n <Text color=\"#00ffff\" bold>📦 {call.name}</Text>\r\n <Text color=\"#555555\"> {\"─\".repeat(Math.max(1, 30 - call.name.length))}</Text>\r\n </Box>\r\n <Box flexDirection=\"column\">\r\n <Text color=\"#888888\">{argsDisplay}</Text>\r\n </Box>\r\n {showPendingHint && (\r\n <Box>\r\n <Text color=\"yellow\">⏳ 等待执行</Text>\r\n </Box>\r\n )}\r\n </Box>\r\n );\r\n}","// ---------------------------------------------------------------------------\r\n// 消息组装 — 将历史消息与系统提示词合并,支持上下文裁剪\r\n// ---------------------------------------------------------------------------\r\n\r\nimport type { ChatMessage, UsageInfo } from \"../provider/index.js\";\r\nimport type { ModelId } from \"../provider/types.js\";\r\nimport { getModelMeta, estimateTokens } from \"../provider/models.js\";\r\n\r\n/** 上下文裁剪策略 */\r\nexport interface TrimOptions {\r\n /** 模型标识,用于获取上下文窗口大小 */\r\n model: ModelId;\r\n /** 为模型输出预留的 token 数 */\r\n reservedForOutput: number;\r\n /** 系统提示词(始终保留) */\r\n systemPrompt: string;\r\n /** 最近 N 轮对话强制保留(不参与裁剪) */\r\n preserveRecentRounds: number;\r\n}\r\n\r\n/**\r\n * 估算消息的 token 数。\r\n * 简单估算:content 长度 × 0.3 + 角色开销 ~10 tokens\r\n */\r\nfunction estimateMessageTokens(msg: ChatMessage): number {\r\n let text = msg.content;\r\n // 工具调用的参数也算\r\n if (msg.toolCalls) {\r\n for (const tc of msg.toolCalls) {\r\n text += tc.name + tc.arguments;\r\n }\r\n }\r\n return estimateTokens(text) + 10;\r\n}\r\n\r\n/**\r\n * 将消息历史划分为不可分割的「回合」单元。\r\n *\r\n * 一个回合的定义:\r\n * - 以一条 user 消息开头\r\n * - 后续连续的 assistant / tool 附属属于该回合,直到下一条 user 出现\r\n * - 特别地,assistant 携带 tool_calls 时,其后紧跟的若干 tool 消息必须与该 assistant 绑定\r\n * (DeepSeek/OpenAI 兼容 API 要求 tool_calls 与对应的 tool 结果消息一一配对,\r\n * 否则返回 400 错误)。因此裁剪时不能把这对拆开。\r\n *\r\n * @returns 回合数组;每个回合是一个非空消息数组,原顺序保留\r\n */\r\nfunction groupIntoTurns(messages: ChatMessage[]): ChatMessage[][] {\r\n const turns: ChatMessage[][] = [];\r\n let current: ChatMessage[] | null = null;\r\n\r\n for (const msg of messages) {\r\n if (msg.role === \"user\") {\r\n // 新回合开始\r\n if (current) turns.push(current);\r\n current = [msg];\r\n } else {\r\n // assistant / tool:归属当前回合;若无当前回合(历史以非 user 开头)\r\n // 则单独为保证不丢失而新建一个虚拟回合\r\n if (!current) current = [];\r\n current.push(msg);\r\n }\r\n }\r\n if (current && current.length > 0) turns.push(current);\r\n return turns;\r\n}\r\n\r\n/**\r\n * 估算一个回合内所有消息的总 token 数。\r\n */\r\nfunction estimateTurnTokens(turn: ChatMessage[]): number {\r\n let sum = 0;\r\n for (const msg of turn) sum += estimateMessageTokens(msg);\r\n return sum;\r\n}\r\n\r\n/**\r\n * 裁剪消息历史以适应上下文窗口,保证工具调用回合不被拆散。\r\n *\r\n * 策略:\r\n * 1. 系统提示词始终保留,不计入裁剪范围\r\n * 2. 将历史按「回合」分组(user 起头 → 直到下一个 user 之前的所有 assistant/tool)\r\n * 3. 最近的 `preserveRecentRounds` 个回合整体强制保留\r\n * 4. 若保留区超限,从最前的回合整组丢弃,直到不超限\r\n * 5. 否则,从最近向 earliest 后填更早的整回合,装不下就停\r\n *\r\n * 关键不变量:任何含 toolCalls 的 assistant 与其后续的 tool 结果消息\r\n * 始终在同一回合里,裁剪不会拆开它们。\r\n *\r\n * @param messages 原始消息历史(不含 system prompt)\r\n * @param opts 裁剪选项\r\n * @returns [裁剪后的消息数组, 是否发生了裁剪]\r\n */\r\nexport function trimMessages(\r\n messages: ChatMessage[],\r\n opts: TrimOptions,\r\n): [ChatMessage[], boolean] {\r\n const meta = getModelMeta(opts.model);\r\n const maxInputTokens = meta.contextWindow - opts.reservedForOutput;\r\n\r\n // 系统提示词 token 估算\r\n const systemTokens = estimateTokens(opts.systemPrompt);\r\n let remaining = maxInputTokens - systemTokens;\r\n\r\n const turns = groupIntoTurns(messages);\r\n\r\n // 保留最近 N 个回合\r\n const preservedTurns: ChatMessage[][] = [];\r\n let roundsPreserved = 0;\r\n for (let i = turns.length - 1; i >= 0 && roundsPreserved < opts.preserveRecentRounds; i--) {\r\n preservedTurns.unshift(turns[i]!);\r\n roundsPreserved++;\r\n }\r\n\r\n const preserved: ChatMessage[] = preservedTurns.flat();\r\n for (const msg of preserved) {\r\n remaining -= estimateMessageTokens(msg);\r\n }\r\n\r\n // 保留区本身超限:从最前的回合整组丢\r\n if (remaining < 0) {\r\n while (preservedTurns.length > 1 && remaining < 0) {\r\n const dropped = preservedTurns.shift()!;\r\n remaining += estimateTurnTokens(dropped);\r\n }\r\n return [preservedTurns.flat(), true];\r\n }\r\n\r\n // 向 earliest 填更早的回合\r\n const olderTurns = turns.slice(0, turns.length - preservedTurns.length);\r\n const keptTurns: ChatMessage[][] = [];\r\n\r\n for (let i = olderTurns.length - 1; i >= 0; i--) {\r\n const cost = estimateTurnTokens(olderTurns[i]!);\r\n if (remaining - cost < 0) break;\r\n remaining -= cost;\r\n keptTurns.unshift(olderTurns[i]!);\r\n }\r\n\r\n const result = [...keptTurns, ...preservedTurns].flat();\r\n const trimmed = result.length < messages.length;\r\n return [result, trimmed];\r\n}\r\n\r\n/**\r\n * 构建完整的 API 请求消息数组。\r\n *\r\n * 在消息历史前面插入 system prompt,返回可直接传给 provider.chat() 的消息数组。\r\n */\r\nexport function buildApiMessages(\r\n systemPrompt: string,\r\n history: ChatMessage[],\r\n): ChatMessage[] {\r\n return [\r\n { role: \"system\", content: systemPrompt },\r\n ...history,\r\n ];\r\n}\r\n\r\n/**\r\n * 将 Token 使用量格式化为人类可读的摘要字符串。\r\n */\r\nexport function formatUsageSummary(usage: UsageInfo): string {\r\n const prompt = usage.promptTokens.toLocaleString();\r\n const completion = usage.completionTokens.toLocaleString();\r\n const total = (usage.promptTokens + usage.completionTokens).toLocaleString();\r\n\r\n let summary = `📥 ${prompt} + 📤 ${completion} = 📦 ${total} tokens`;\r\n\r\n if (usage.cachedPromptTokens && usage.cachedPromptTokens > 0) {\r\n const cacheRate = ((usage.cachedPromptTokens / usage.promptTokens) * 100).toFixed(1);\r\n summary += ` │ 🗄️ 缓存命中 ${cacheRate}%`;\r\n }\r\n\r\n return summary;\r\n}","// ---------------------------------------------------------------------------\r\n// HighlightedText — AI 回复文本高亮渲染\r\n//\r\n// 支持两种语法:\r\n// `` `code` `` → 天蓝色(代码标识符、文件路径)\r\n// `**bold**` → 紫色(强调内容,如\"第 12 行\")\r\n// 流式传输中未闭合的标记自动降级为纯文本,不会闪烁。\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { Text } from \"ink\";\r\nimport type { ReactNode } from \"react\";\r\n\r\n/** 解析后的文本段类型 */\r\ntype SegmentType = \"plain\" | \"code\" | \"bold\";\r\n\r\ninterface Segment {\r\n text: string;\r\n type: SegmentType;\r\n}\r\n\r\n/**\r\n * 第一遍:按 `**...**` 对拆分为段。\r\n * 未闭合的 `**`(流式传输中)降级为纯文本。\r\n */\r\nfunction parseBoldPairs(text: string): Segment[] {\r\n const segments: Segment[] = [];\r\n let current = 0;\r\n\r\n while (current < text.length) {\r\n const openIdx = text.indexOf(\"**\", current);\r\n\r\n if (openIdx === -1) {\r\n segments.push({ text: text.slice(current), type: \"plain\" });\r\n break;\r\n }\r\n\r\n // ** 前的纯文本\r\n if (openIdx > current) {\r\n segments.push({ text: text.slice(current, openIdx), type: \"plain\" });\r\n }\r\n\r\n const closeIdx = text.indexOf(\"**\", openIdx + 2);\r\n\r\n // 未闭合 → 降级为纯文本\r\n if (closeIdx === -1) {\r\n segments.push({ text: text.slice(openIdx), type: \"plain\" });\r\n break;\r\n }\r\n\r\n // 成对 **...** 内的内容 → bold\r\n segments.push({ text: text.slice(openIdx + 2, closeIdx), type: \"bold\" });\r\n current = closeIdx + 2;\r\n }\r\n\r\n return segments;\r\n}\r\n\r\n/**\r\n * 第二遍:在 plain 段内按 `` `...` `` 对拆分为 code 段。\r\n * 未闭合的反引号降级为纯文本。\r\n */\r\nfunction parseCodePairs(segments: Segment[]): Segment[] {\r\n const result: Segment[] = [];\r\n\r\n for (const seg of segments) {\r\n if (seg.type !== \"plain\") {\r\n result.push(seg);\r\n continue;\r\n }\r\n\r\n let current = 0;\r\n const text = seg.text;\r\n\r\n while (current < text.length) {\r\n const openIdx = text.indexOf(\"`\", current);\r\n\r\n if (openIdx === -1) {\r\n result.push({ text: text.slice(current), type: \"plain\" });\r\n break;\r\n }\r\n\r\n if (openIdx > current) {\r\n result.push({ text: text.slice(current, openIdx), type: \"plain\" });\r\n }\r\n\r\n const closeIdx = text.indexOf(\"`\", openIdx + 1);\r\n\r\n if (closeIdx === -1) {\r\n result.push({ text: text.slice(openIdx), type: \"plain\" });\r\n break;\r\n }\r\n\r\n result.push({ text: text.slice(openIdx + 1, closeIdx), type: \"code\" });\r\n current = closeIdx + 1;\r\n }\r\n }\r\n\r\n return result;\r\n}\r\n\r\n/** 代码高亮色 — 天蓝 */\r\nconst CODE_COLOR = \"#00BFFF\";\r\n/** 加粗强调色 — 紫色 */\r\nconst BOLD_COLOR = \"#A855F7\";\r\n\r\ninterface HighlightedTextProps {\r\n children: string;\r\n}\r\n\r\n/**\r\n * 渲染 AI 回复文本,支持两种内联标记高亮:\r\n * - `` `code` `` → 天蓝色\r\n * - `**bold**` → 紫色\r\n *\r\n * 用法:\r\n * ```tsx\r\n * <HighlightedText>已完成。`test.ts` 中 **第 12 行** 的修改。</HighlightedText>\r\n * ```\r\n * 渲染结果:\r\n * 已完成。<Text color=\"#00BFFF\">test.ts</Text> 中\r\n * <Text color=\"#A855F7\">第 12 行</Text> 的修改。\r\n */\r\nexport function HighlightedText({ children: text }: HighlightedTextProps): ReactNode {\r\n // 先解析 **bold**,再在 plain 段内解析 `code`\r\n const boldSegments = parseBoldPairs(text);\r\n const segments = parseCodePairs(boldSegments);\r\n\r\n // 纯文本捷径:不嵌套 Text 提高渲染性能\r\n const isSimple = segments.length === 1 && segments[0]!.type === \"plain\";\r\n if (isSimple) {\r\n return <Text wrap=\"wrap\">{text}</Text>;\r\n }\r\n\r\n return (\r\n <Text wrap=\"wrap\">\r\n {segments.map((seg, i) => {\r\n if (seg.type === \"code\") {\r\n return (\r\n <Text key={i} color={CODE_COLOR}>\r\n {seg.text}\r\n </Text>\r\n );\r\n }\r\n if (seg.type === \"bold\") {\r\n return (\r\n <Text key={i} color={BOLD_COLOR}>\r\n {seg.text}\r\n </Text>\r\n );\r\n }\r\n return seg.text;\r\n })}\r\n </Text>\r\n );\r\n}\r\n","// ---------------------------------------------------------------------------\r\n// Diff 预览组件 — 终端中渲染带色彩的 unified diff\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { Box, Text } from \"ink\";\r\nimport type { FileDiff } from \"../tool/types.js\";\r\n\r\ninterface DiffPreviewProps {\r\n /** FileDiff 对象 */\r\n diff: FileDiff;\r\n}\r\n\r\n/**\r\n * 解析 unified diff 文本,按行着色渲染。\r\n *\r\n * 颜色方案:\r\n * - `@@ ... @@` → 青色(hunk 头)\r\n * - `+` 添加行 → 绿色\r\n * - `-` 删除行 → 红色\r\n * - ` ` 上下文行 → 灰色\r\n *\r\n * 注意:文件头“--- a/...” / “+++ b/...”由摘要行展示,不重复渲染。\r\n */\r\nexport function DiffPreview({ diff }: DiffPreviewProps) {\r\n const { patch, additions, deletions, existedBefore, filePath } = diff;\r\n\r\n // 无变更\r\n if (!patch || patch.length === 0) {\r\n return null;\r\n }\r\n\r\n const lines = patch.split(\"\\n\");\r\n const fileName = filePath.replace(/\\\\/g, \"/\").split(\"/\").pop() ?? filePath;\r\n\r\n return (\r\n <Box flexDirection=\"column\" marginTop={1}>\r\n {/* 摘要行 */}\r\n <Box flexDirection=\"row\" gap={1}>\r\n <Text bold color=\"#00ffff\">\r\n 📝 {existedBefore ? \"修改\" : \"新建\"}:\r\n </Text>\r\n <Text bold>{fileName}</Text>\r\n <Text color=\"#555555\">\r\n (+{additions} -{deletions})\r\n </Text>\r\n </Box>\r\n\r\n {/* Diff 内容(跳过文件头两行“--- a/x” / \"+++ b/x\",避免与摘要行重复展示文件名) */}\r\n <Box flexDirection=\"column\" marginLeft={2}>\r\n {lines\r\n .filter((line) => !line.startsWith(\"---\") && !line.startsWith(\"+++\"))\r\n .map((line, i) => (\r\n <DiffLine key={i} line={line} />\r\n ))}\r\n </Box>\r\n </Box>\r\n );\r\n}\r\n\r\n/** 渲染单行 diff 内容 */\r\nfunction DiffLine({ line }: { line: string }) {\r\n // 文件头行(如 \"--- a/path\" / \"+++ b/path\" / \"--- /dev/null\")\r\n // 摘要行已展示文件名与新建/修改状态,无需重复渲染\r\n if (line.startsWith(\"---\") || line.startsWith(\"+++\")) {\r\n return null;\r\n }\r\n\r\n // Hunk 头行 @@ -x,y +a,b @@\r\n if (line.startsWith(\"@@\")) {\r\n // 将 @@ 标记和行号范围分别着色\r\n return <Text color=\"#00cccc\">{line}</Text>;\r\n }\r\n\r\n // 添加行\r\n if (line.startsWith(\"+\")) {\r\n return <Text color=\"#22c55e\">{line}</Text>;\r\n }\r\n\r\n // 删除行\r\n if (line.startsWith(\"-\")) {\r\n return <Text color=\"#ef4444\">{line}</Text>;\r\n }\r\n\r\n // 上下文行(空格开头或空行)\r\n return <Text color=\"#6b7280\">{line}</Text>;\r\n}","// ---------------------------------------------------------------------------\r\n// SkillSelector — 用户输入 / 时渲染的 skill 列表组件\r\n// 通过 selectedIndex 高亮当前选中项,按 Tab 可补全 skill 名称\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { Box, Text } from \"ink\";\r\nimport type { SkillInfo } from \"../cli/skill-import.js\";\r\n\r\nconst HIGHLIGHT_COLOR = \"#00bfff\";\r\n\r\ninterface SkillSelectorProps {\r\n /** 所有可用 skill 列表 */\r\n skills: SkillInfo[];\r\n /** 当前输入框文本 */\r\n input: string;\r\n /** 当前选中的 skill 索引 */\r\n selectedIndex: number;\r\n}\r\n\r\nexport function SkillSelector({ skills, input, selectedIndex }: SkillSelectorProps) {\r\n // 匹配规则:/ 必须在输入开头,或者前面有空格\r\n const match = input.match(/(?:^|\\s)\\/([^/]*)$/);\r\n if (!match) return null;\r\n\r\n const query = match[1]!.toLowerCase().trim();\r\n if (skills.length === 0) return null;\r\n\r\n // 斜杠在开头且无后续内容时,展示全部 skill 作为提示\r\n const matched = !query && input.startsWith(\"/\")\r\n ? skills.slice(0, 3)\r\n : skills\r\n .filter((s) => s.name.toLowerCase().includes(query))\r\n .slice(0, 3);\r\n\r\n // 精确匹配时表示已补全完成,不显示列表\r\n if (query && matched.length > 0 && matched.some((s) => s.name.toLowerCase() === query)) return null;\r\n\r\n if (matched.length === 0) return null;\r\n\r\n return (\r\n <Box flexDirection=\"column\" marginLeft={4} marginTop={1}>\r\n <Text color=\"#808080\" dimColor>\r\n 支持的 Skill:\r\n </Text>\r\n {matched.map((skill, i) => (\r\n <Box key={skill.name}>\r\n <Text color={i === selectedIndex ? HIGHLIGHT_COLOR : \"#808080\"}>\r\n {i === selectedIndex ? \" › \" : \" \"}{skill.name}\r\n </Text>\r\n </Box>\r\n ))}\r\n <Box marginTop={1}>\r\n <Text color=\"#808080\" dimColor>\r\n ↑↓ 选择 · Tab 补全\r\n </Text>\r\n </Box>\r\n </Box>\r\n );\r\n}\r\n","// ---------------------------------------------------------------------------\r\n// FileSelector — 用户输入 @ 时渲染的文件列表组件\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { Box, Text } from \"ink\";\r\n\r\ninterface FileSelectorProps {\r\n /** 所有项目文件路径列表(相对于 CWD) */\r\n files: string[];\r\n /** 当前输入框文本 */\r\n input: string;\r\n /** 当前选中的文件索引 */\r\n selectedIndex: number;\r\n}\r\n\r\nconst HIGHLIGHT_COLOR = \"#00ff41\";\r\n\r\nexport function FileSelector({ files, input, selectedIndex }: FileSelectorProps) {\r\n // 匹配规则:@ 必须在输入开头,或者前面有空格\r\n const match = input.match(/(?:^|\\s)@([^@]*)$/);\r\n if (!match) return null;\r\n\r\n const query = match[1]!.toLowerCase().trim();\r\n if (files.length === 0) return null;\r\n\r\n // @ 在开头且无后续内容时,展示前 5 个文件作为提示\r\n const matched = !query && input.startsWith(\"@\")\r\n ? files.slice(0, 5)\r\n : files\r\n .filter((f) => f.toLowerCase().includes(query))\r\n .slice(0, 5);\r\n\r\n // 精确匹配时表示已补全完成,不显示列表\r\n if (query && matched.some((f) => f.toLowerCase() === query)) return null;\r\n\r\n if (matched.length === 0) return null;\r\n\r\n return (\r\n <Box flexDirection=\"column\" marginLeft={4} marginTop={1}>\r\n <Text color=\"#808080\" dimColor>\r\n 项目文件:\r\n </Text>\r\n {matched.map((file, i) => (\r\n <Box key={file}>\r\n <Text color={i === selectedIndex ? HIGHLIGHT_COLOR : \"#808080\"}>\r\n {i === selectedIndex ? \" › \" : \" \"}{file}\r\n </Text>\r\n </Box>\r\n ))}\r\n <Box marginTop={1}>\r\n <Text color=\"#808080\" dimColor>\r\n ↑↓ 选择 · Tab 补全\r\n </Text>\r\n </Box>\r\n </Box>\r\n );\r\n}\r\n","// ---------------------------------------------------------------------------\r\n// Provider 注册表 — 工厂模式管理 Provider 实例\r\n// ---------------------------------------------------------------------------\r\n\r\nimport type { Provider, ModelId } from \"./types.js\";\r\nimport { isSupportedModel } from \"./models.js\";\r\nimport { ModelNotSupportedError } from \"./errors.js\";\r\nimport { DeepSeekProvider } from \"./deepseek.js\";\r\n\r\n/** Provider 工厂函数类型 */\r\nexport type ProviderFactory = (config: {\r\n apiKey: string;\r\n baseUrl: string;\r\n model: string;\r\n}) => Provider;\r\n\r\n/**\r\n * Provider 注册表 — 工厂模式管理 Provider 实例。\r\n *\r\n * 使用方式:\r\n * ```ts\r\n * const provider = registry.get(\"deepseek\", { apiKey, baseUrl, model });\r\n * for await (const chunk of provider.chat(messages)) { ... }\r\n * ```\r\n */\r\nexport class ProviderRegistry {\r\n readonly #factories = new Map<string, ProviderFactory>();\r\n readonly #instances = new Map<string, Provider>();\r\n\r\n /** 注册一个 Provider 工厂 */\r\n register(name: string, factory: ProviderFactory): void {\r\n this.#factories.set(name, factory);\r\n }\r\n\r\n /**\r\n * 获取或创建一个 Provider 实例(单例)。\r\n *\r\n * 相同的 name + baseUrl + model 组合会复用同一实例。\r\n * 如果 model 不在支持列表中,抛出 ModelNotSupportedError。\r\n */\r\n get(\r\n name: string,\r\n config: { apiKey: string; baseUrl: string; model: string },\r\n ): Provider {\r\n if (!isSupportedModel(config.model)) {\r\n throw new ModelNotSupportedError(config.model);\r\n }\r\n\r\n // 缓存键 = name:baseUrl:model(不同配置会创建不同实例)\r\n const cacheKey = `${name}:${config.baseUrl}:${config.model}`;\r\n\r\n const cached = this.#instances.get(cacheKey);\r\n if (cached) return cached;\r\n\r\n const factory = this.#factories.get(name);\r\n if (!factory) {\r\n const available = [...this.#factories.keys()].join(\", \");\r\n throw new Error(`未注册的 Provider: \"${name}\"。可用: ${available}`);\r\n }\r\n\r\n const instance = factory(config);\r\n this.#instances.set(cacheKey, instance);\r\n return instance;\r\n }\r\n\r\n /** 列出已注册的 Provider 名称 */\r\n list(): string[] {\r\n return [...this.#factories.keys()];\r\n }\r\n\r\n /** 清除缓存的实例(用于配置热重载) */\r\n clear(): void {\r\n this.#instances.clear();\r\n }\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 全局默认注册表 + 预注册内置 Provider\r\n// ---------------------------------------------------------------------------\r\n\r\n/** 全局默认 Provider 注册表 */\r\nexport const defaultRegistry = new ProviderRegistry();\r\n\r\ndefaultRegistry.register(\"deepseek\", (config) => {\r\n // 模型合法性已在 registry.get() 中通过 isSupportedModel 校验\r\n return new DeepSeekProvider({\r\n apiKey: config.apiKey,\r\n baseUrl: config.baseUrl,\r\n model: config.model as ModelId,\r\n });\r\n});\r\n\r\n/**\r\n * 从配置快速创建 Provider 实例的快捷方式。\r\n *\r\n * 内部委托给 defaultRegistry,会在注册表中校验模型并缓存实例。\r\n */\r\nexport function createProvider(config: {\r\n name: string;\r\n apiKey: string;\r\n baseUrl: string;\r\n model: string;\r\n}): Provider {\r\n return defaultRegistry.get(config.name, config);\r\n}","// ---------------------------------------------------------------------------\r\n// 工具系统核心类型定义\r\n// ---------------------------------------------------------------------------\r\n\r\n/** 工具的分类语义,替代原来的 boolean readOnly */\r\nexport enum ToolKind {\r\n /** 纯读操作,无副作用,可并行执行 */\r\n Read = \"read\",\r\n /** 文件/目录编辑(修改内容) */\r\n Edit = \"edit\",\r\n /** 文件/目录删除 */\r\n Delete = \"delete\",\r\n /** 移动/重命名 */\r\n Move = \"move\",\r\n /** 其他(bash、fetch 等) */\r\n Other = \"other\",\r\n}\r\n\r\n/** 工具参数的 JSON Schema 表示 */\r\nexport interface JSONSchema {\r\n type: \"object\";\r\n properties?: Record<string, unknown>;\r\n required?: string[];\r\n additionalProperties?: boolean | Record<string, unknown>;\r\n /** Schema 自带的 description,自动用作 tool description */\r\n description?: string;\r\n}\r\n\r\n/** 每次工具执行时传入的上下文 */\r\nexport interface ToolContext {\r\n /** 当前工作目录,用于路径约束和相对路径解析 */\r\n cwd: string;\r\n /** 中止信号,用于取消正在执行的工具 */\r\n signal?: AbortSignal;\r\n /** 命令执行超时(毫秒),默认 30000 */\r\n timeout?: number;\r\n /**\r\n * 写操作的允许根目录列表(绝对路径)。\r\n * 非空时,写工具必须确保目标路径在其中一个根下;\r\n * 为空数组表示不限制(保持兼容)。\r\n */\r\n writeRoots?: string[];\r\n}\r\n\r\n/** 文件变更的 diff 信息 */\r\nexport interface FileDiff {\r\n /** 文件路径(绝对路径) */\r\n filePath: string;\r\n /** unified diff 文本 */\r\n patch: string;\r\n /** 变更前是否存在(新建文件时为 false) */\r\n existedBefore: boolean;\r\n /** 变更统计:新增行数 */\r\n additions: number;\r\n /** 变更统计:删除行数 */\r\n deletions: number;\r\n}\r\n\r\n/**\r\n * 工具执行结果。\r\n *\r\n * 设计要点:\r\n * - 成功和失败共用相同结构,LLM 总能读到可理解的 data\r\n * - error 字段仅做分类标记(如 TEXT_NOT_FOUND),不暴露内部细节\r\n * - diff 仅在文件修改工具携带\r\n * - summary 给 UI 展示,不暴露完整 data 给 UI\r\n */\r\nexport interface ToolResult {\r\n /** 是否执行成功 */\r\n success: boolean;\r\n /** 结果内容(成功时为输出,失败时为错误信息),LLM 可读 */\r\n data: string;\r\n /** 错误分类标记(仅在 success=false 时有意义) */\r\n error?: string;\r\n /** 文件变更 diff(仅文件修改工具携带) */\r\n diff?: FileDiff;\r\n /**\r\n * 给 UI 展示的简短摘要(一行)。\r\n * 与 data 区别:data 喂给 LLM 保留完整内容,summary 仅做一行回显。\r\n * 未设置时 UI 自动降级到 data 的前 500 字符。\r\n */\r\n summary?: string;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// AgentTool — 静态类型安全的工具定义\r\n//\r\n// 每个工具定义为一个对象,提供类型安全的泛型参数。\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * AgentTool 定义 — 一个工具的完整声明。\r\n *\r\n * @template I 工具输入参数类型\r\n * @template O 工具输出类型(从 ToolResult 继承)\r\n */\r\nexport interface AgentTool<I, O extends ToolResult = ToolResult> {\r\n /** 工具名称,全局唯一标识符 */\r\n readonly name: string;\r\n\r\n /** 工具的分类语义 */\r\n readonly kind: ToolKind;\r\n\r\n /** 参数 JSON Schema 定义 */\r\n readonly parameters: JSONSchema;\r\n\r\n /**\r\n * 工具描述 — 默认从 parameters.description 自动提取,\r\n * 也可显式覆盖以提供更详细的说明。\r\n */\r\n readonly description: string;\r\n\r\n /** 工具执行逻辑 */\r\n execute(args: I, ctx: ToolContext): Promise<O>;\r\n\r\n /**\r\n * UI 初始标题(可选)。\r\n * 用于在 UI 中显示正在执行的工具名称。\r\n */\r\n initialTitle?(args: I): string;\r\n\r\n /**\r\n * 是否支持输入流式传输(可选)。\r\n * 用于支持 LLM 流式传输工具调用参数。\r\n */\r\n supportsInputStreaming?: boolean;\r\n\r\n /**\r\n * 工具支持的 LLM Provider(可选)。\r\n * 返回空/undefined 表示支持所有 provider。\r\n */\r\n supportedProviders?: string[];\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// AnyAgentTool — 动态分发接口(Type Erasure 模式)\r\n//\r\n// 将类型参数擦除,用于 Registry 等需要存储异构工具集合的场景。\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * AnyAgentTool — 类型擦除后的工具接口。\r\n * Registry 存储的是此类型,执行时内部做反序列化。\r\n */\r\nexport interface AnyAgentTool {\r\n readonly name: string;\r\n readonly description: string;\r\n readonly kind: ToolKind;\r\n readonly parameters: JSONSchema;\r\n readonly supportsInputStreaming: boolean;\r\n readonly supportedProviders: string[];\r\n\r\n execute(args: unknown, ctx: ToolContext): Promise<ToolResult>;\r\n initialTitle?(args: unknown): string;\r\n}\r\n\r\n/**\r\n * 将类型安全的 AgentTool 擦除为 AnyAgentTool。\r\n */\r\nexport function eraseTool<I, O extends ToolResult = ToolResult>(\r\n tool: AgentTool<I, O>,\r\n): AnyAgentTool {\r\n return {\r\n get name() { return tool.name; },\r\n get description() { return tool.description; },\r\n get kind() { return tool.kind; },\r\n get parameters() { return tool.parameters; },\r\n get supportsInputStreaming() { return tool.supportsInputStreaming ?? false; },\r\n get supportedProviders() { return tool.supportedProviders ?? []; },\r\n\r\n async execute(args: unknown, ctx: ToolContext): Promise<ToolResult> {\r\n return tool.execute(args as I, ctx);\r\n },\r\n\r\n initialTitle(args: unknown): string {\r\n return tool.initialTitle?.(args as I) ?? `${tool.name}`;\r\n },\r\n };\r\n}\r\n\r\n/**\r\n * 从 JSON Schema 中提取 description。\r\n * 若 schema.properties 的顶层有 description,使用它;\r\n * 否则使用 schema 本身的 description。\r\n */\r\nexport function extractDescription(schema: JSONSchema, fallback?: string): string {\r\n if (schema.description) return schema.description;\r\n if (fallback) return fallback;\r\n return \"\";\r\n}\r\n\r\n/**\r\n * 根据 ToolKind 判断是否只读(可并行执行)。\r\n * Read 一定只读;Edit/Delete/Move 一定写;Other 默认非只读。\r\n */\r\nexport function isReadOnly(kind: ToolKind): boolean {\r\n return kind === ToolKind.Read;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Permission/Gate 接口\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Gate 权限门接口 — 控制工具执行前是否需要审批。\r\n */\r\nexport interface Gate {\r\n check(toolName: string, args: unknown): boolean | Promise<boolean>;\r\n}\r\n\r\n/**\r\n * AlwaysAllowGate — 默认权限门,所有工具调用默认放行。\r\n */\r\nexport class AlwaysAllowGate implements Gate {\r\n check(_toolName: string, _args: unknown): boolean {\r\n return true;\r\n }\r\n}\r\n\r\n/** 工具调用记录(用于风暴检测等) */\r\nexport interface ToolCallRecord {\r\n name: string;\r\n success: boolean;\r\n error?: string;\r\n timestamp: number;\r\n}\r\n","// ---------------------------------------------------------------------------\r\n// 系统提示词构建 — 使用 Handlebars 模板引擎渲染\r\n// ---------------------------------------------------------------------------\r\n\r\nimport Handlebars from \"handlebars\";\r\nimport type { SystemPromptOptions, ToolDescription } from \"./types.js\";\r\nimport systemPromptTemplate from \"./prompts/system-prompt.hbs\";\r\n\r\nconst compiledTemplate = Handlebars.compile(systemPromptTemplate);\r\n\r\ninterface ToolView {\r\n name: string;\r\n description: string;\r\n params?: string;\r\n}\r\n\r\nfunction formatTools(tools: ToolDescription[]): ToolView[] {\r\n return tools.map((t) => {\r\n const params =\r\n t.parameters &&\r\n Object.keys((t.parameters as Record<string, unknown>).properties ?? {})\r\n .join(\", \");\r\n return {\r\n name: t.name,\r\n description: t.description,\r\n params: params || undefined,\r\n };\r\n });\r\n}\r\n\r\n/**\r\n * 构建 Agent 的系统提示词。\r\n *\r\n * 使用 Handlebars 模板 `prompts/system-prompt.hbs` 渲染,\r\n * 动态注入模型信息、工具描述、时间、项目上下文。\r\n */\r\nexport function buildSystemPrompt(opts: SystemPromptOptions): string {\r\n const now = new Date();\r\n const date = now.toLocaleDateString(\"zh-CN\", {\r\n year: \"numeric\",\r\n month: \"long\",\r\n day: \"numeric\",\r\n weekday: \"long\",\r\n });\r\n const time = now.toLocaleTimeString(\"zh-CN\", {\r\n hour: \"2-digit\",\r\n minute: \"2-digit\",\r\n });\r\n\r\n return compiledTemplate({\r\n model: opts.model,\r\n maxToolRounds: opts.maxToolRounds,\r\n cwd: opts.cwd,\r\n date,\r\n time,\r\n tools: opts.tools ? formatTools(opts.tools) : undefined,\r\n projectContext: opts.projectContext ?? undefined,\r\n });\r\n}\r\n","你是 dskcode,一个基于 DeepSeek 的 AI 编程助手,运行在用户终端中。你的职责是帮助开发者编写、理解、调试和重构代码。\r\n\r\n## 核心原则\r\n- 回答使用中文,代码标识符保持英文\r\n- 提供准确、实用、可操作的建议\r\n- 对不确定的内容明确标注,不编造事实\r\n- 代码示例保持简洁,附带必要的注释\r\n\r\n## 工作流程\r\n\r\n你处于一个工具调用循环中:\r\n1. 收到用户请求\r\n2. 调用工具获取信息或修改文件\r\n3. 工具结果返回给你\r\n4. 根据结果决定下一步(继续调用工具或给出最终回答)\r\n5. 一轮最多 {{maxToolRounds}} 次工具调用\r\n\r\n## 工具使用策略\r\n\r\n{{#if tools}}\r\n当前可用的工具:\r\n{{#each tools}}\r\n- **{{name}}**:{{description}}{{#if params}}(参数:{{params}}){{/if}}\r\n{{/each}}\r\n\r\n使用原则:\r\n- **先搜索再读取** — 不确定目标位置时先用 grep/glob 定位,再用 read_file 读取\r\n- **先读取再修改** — 编辑文件前先 read_file 确认当前内容\r\n- **批量操作一次完成** — 同一文件的多处改动在单次 edit_file 中完成\r\n- **工具出错时** — 分析错误原因(路径不对?格式不对?权限不足?),修正后重试,不要重复相同的错误调用\r\n- **审慎执行** — bash 执行危险命令(rm -rf、强制写入等)前先确认\r\n{{/if}}\r\n\r\n{{#if projectContext}}\r\n## 项目上下文\r\n\r\n{{projectContext}}\r\n{{/if}}\r\n\r\n## 回答格式\r\n\r\n终端不支持 Markdown 渲染。回复格式要求:\r\n\r\n- 用 emoji 做视觉标记(📌 要点 ⚠️ 注意 📝 待办 ✅ 完成)\r\n- 用数字序号或短横线组织列表\r\n- 代码标识符、文件路径、类型名用单个反引号 ` 包裹,会被自动高亮\r\n- 强调内容用 **加粗** 包裹,会被自动高亮\r\n- 多行代码用缩进 4 个空格表示,不要用三反引号代码块\r\n- 段落间用空行分隔,不要用标题符号\r\n\r\n正确示例:\r\n\r\n📌 需要修改两个地方:\r\n1. 第 12 行的 `interface FileDiff` 改为 `interface Diff`\r\n2. 第 432 行的 `): FileDiff` 改为 `): Diff`\r\n\r\n 代码示例:\r\n const pool = new Pool({ max: 20 });\r\n await pool.query(\"SELECT * FROM users\");\r\n\r\n## 行为约束\r\n- 如果用户请求不明确,主动询问澄清\r\n- 对不确定的信息明确标注,不编造事实\r\n- 不执行可能造成不可逆损害的操作(如 rm -rf),除非用户明确逐条确认\r\n- 工具调用返回错误时,先分析原因再重试,不要盲目重试\r\n\r\n## 当前模型\r\n- 模型:{{model}}\r\n\r\n## 时间上下文\r\n- 当前日期:{{date}}\r\n- 当前时间:{{time}}\r\n- 工作目录:{{cwd}}\r\n","// ---------------------------------------------------------------------------\r\n// 工具注册表 — 注册、查找、列出、过滤工具\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { ToolKind, type AnyAgentTool, type ToolContext, type ToolResult, type AgentTool, eraseTool, isReadOnly } from \"./types.js\";\r\n\r\n/** 工具注册表配置 */\r\nexport interface ToolRegistryOptions {\r\n /** 工具名称黑名单,这些工具不会被 list() 返回 */\r\n disabledTools?: string[];\r\n /**\r\n * Feature Flag 检查函数,返回 true 表示该工具可用。\r\n * 不在列表中的工具默认启用。\r\n */\r\n featureFlagChecker?: (toolName: string) => boolean;\r\n /**\r\n * 当前使用的 LLM Provider ID。\r\n * 提供后只会列出支持该 provider 的工具。\r\n */\r\n provider?: string;\r\n}\r\n\r\n/**\r\n * ToolRegistry — 管理所有已注册的工具。\r\n *\r\n * 设计要点:\r\n * - 注册时检查名称唯一性\r\n * - 支持 disable/enable 工具(基于配置项过滤)\r\n * - 支持 Feature Flag 门控\r\n * - 支持 Provider 过滤\r\n * - get() 返回 undefined 而非抛异常,调用方自行处理\r\n * - ALL_TOOL_NAMES 静态常量列表\r\n */\r\nexport class ToolRegistry {\r\n readonly #tools = new Map<string, AnyAgentTool>();\r\n readonly #disabledNames: Set<string>;\r\n readonly #featureFlagChecker: (toolName: string) => boolean;\r\n readonly #provider: string | undefined;\r\n\r\n /** 所有已注册工具的名称列表(编译期等效) */\r\n static readonly ALL_TOOL_NAMES: string[] = [];\r\n\r\n constructor(opts?: ToolRegistryOptions) {\r\n this.#disabledNames = new Set(opts?.disabledTools ?? []);\r\n this.#featureFlagChecker = opts?.featureFlagChecker ?? (() => true);\r\n this.#provider = opts?.provider;\r\n }\r\n\r\n /**\r\n * 注册一个类型安全的 AgentTool。\r\n * 自动擦除类型参数后存储。\r\n * 如果同名工具已存在则抛出错误。\r\n */\r\n register<I, O extends ToolResult = ToolResult>(tool: AgentTool<I, O>): this {\r\n return this.registerErased(eraseTool(tool));\r\n }\r\n\r\n /**\r\n * 注册一个已经擦除类型 AnyAgentTool。\r\n * 如果同名工具已存在则抛出错误。\r\n */\r\n registerErased(tool: AnyAgentTool): this {\r\n if (this.#tools.has(tool.name)) {\r\n throw new Error(`工具 \"${tool.name}\" 已注册,不能重复注册`);\r\n }\r\n this.#tools.set(tool.name, tool);\r\n return this;\r\n }\r\n\r\n /**\r\n * 批量注册工具。\r\n */\r\n registerAll(tools: AnyAgentTool[]): this {\r\n for (const tool of tools) {\r\n this.registerErased(tool);\r\n }\r\n return this;\r\n }\r\n\r\n /**\r\n * 注销一个工具。\r\n */\r\n unregister(name: string): boolean {\r\n return this.#tools.delete(name);\r\n }\r\n\r\n /**\r\n * 按名称获取工具(未擦除类型版本,调用方自行断言类型)。\r\n * 如果工具被禁用、被 feature flag 拦截、或不支持当前 provider,返回 undefined。\r\n */\r\n get(name: string): AnyAgentTool | undefined {\r\n if (!this.#isToolEnabled(name)) return undefined;\r\n return this.#tools.get(name);\r\n }\r\n\r\n /**\r\n * 获取所有启用的工具列表。\r\n * 依次应用:禁用列表 → Feature Flag → Provider 过滤。\r\n */\r\n list(): AnyAgentTool[] {\r\n const result: AnyAgentTool[] = [];\r\n for (const [name, tool] of this.#tools) {\r\n if (this.#isToolEnabled(name)) {\r\n result.push(tool);\r\n }\r\n }\r\n return result;\r\n }\r\n\r\n /**\r\n * 按 ToolKind 分类获取工具列表。\r\n */\r\n listByKind(kind: ToolKind): AnyAgentTool[] {\r\n return this.list().filter((t) => t.kind === kind);\r\n }\r\n\r\n /**\r\n * 获取读工具列表(可并行执行)。\r\n */\r\n listReadTools(): AnyAgentTool[] {\r\n return this.listByKind(ToolKind.Read);\r\n }\r\n\r\n /**\r\n * 获取写工具列表(需串行执行)。\r\n */\r\n listWriteTools(): AnyAgentTool[] {\r\n return this.list().filter((t) => !isReadOnly(t.kind));\r\n }\r\n\r\n /**\r\n * 获取所有已注册的工具名称(含禁用的)。\r\n */\r\n names(): string[] {\r\n return [...this.#tools.keys()];\r\n }\r\n\r\n /**\r\n * 检查工具是否已注册(含禁用的)。\r\n */\r\n has(name: string): boolean {\r\n return this.#tools.has(name);\r\n }\r\n\r\n /**\r\n * 检查工具是否已启用(注册 + 未禁用 + 通过 feature flag + 支持 provider)。\r\n */\r\n isEnabled(name: string): boolean {\r\n return this.#tools.has(name) && this.#isToolEnabled(name);\r\n }\r\n\r\n /**\r\n * 禁用一个工具。\r\n */\r\n disable(name: string): void {\r\n this.#disabledNames.add(name);\r\n }\r\n\r\n /**\r\n * 启用一个之前被禁用的工具。\r\n */\r\n enable(name: string): void {\r\n this.#disabledNames.delete(name);\r\n }\r\n\r\n /**\r\n * 获取工具的分类语义。\r\n */\r\n kindOf(name: string): ToolKind | undefined {\r\n return this.#tools.get(name)?.kind;\r\n }\r\n\r\n /**\r\n * 执行指定工具。\r\n *\r\n * @returns 工具执行结果;如果工具不存在或被禁用,返回失败结果\r\n */\r\n async execute(name: string, args: unknown, ctx: ToolContext): Promise<ToolResult> {\r\n const tool = this.get(name);\r\n if (!tool) {\r\n return {\r\n success: false,\r\n data: `工具 \"${name}\" 不存在或已被禁用`,\r\n error: \"TOOL_NOT_FOUND\",\r\n };\r\n }\r\n\r\n try {\r\n return await tool.execute(args, ctx);\r\n } catch (err: unknown) {\r\n const message = err instanceof Error ? err.message : String(err);\r\n return {\r\n success: false,\r\n data: `工具 \"${name}\" 执行异常:${message}`,\r\n error: \"EXECUTION_ERROR\",\r\n };\r\n }\r\n }\r\n\r\n // ---------------------------------------------------------------------------\r\n // 内部方法\r\n // ---------------------------------------------------------------------------\r\n\r\n /**\r\n * 判断工具是否应该启用。\r\n * 依次检查:是否在禁用列表 → 是否通过 Feature Flag → 是否支持当前 Provider。\r\n */\r\n #isToolEnabled(name: string): boolean {\r\n const tool = this.#tools.get(name);\r\n if (!tool) return false;\r\n\r\n // 1. 禁用列表检查\r\n if (this.#disabledNames.has(name)) return false;\r\n\r\n // 2. Feature Flag 检查\r\n if (!this.#featureFlagChecker(name)) return false;\r\n\r\n // 3. Provider 兼容性检查\r\n if (this.#provider && tool.supportedProviders.length > 0) {\r\n if (!tool.supportedProviders.includes(this.#provider)) return false;\r\n }\r\n\r\n return true;\r\n }\r\n}\r\n","// ---------------------------------------------------------------------------\r\n// Agent 会话 — 消息编排、流式 LLM 调用、工具执行循环、成本追踪\r\n// ---------------------------------------------------------------------------\r\n\r\nimport type {\r\n ChatMessage,\r\n Provider,\r\n ProviderToolCall,\r\n UsageInfo,\r\n ModelId,\r\n ToolDefinition,\r\n} from \"../provider/index.js\";\r\nimport { CostTracker } from \"../provider/index.js\";\r\nimport type { ToolContext, AnyAgentTool } from \"../tool/index.js\";\r\nimport { isReadOnly } from \"../tool/types.js\";\r\nimport type { AgentEvent, SystemPromptOptions } from \"./types.js\";\r\nimport { buildSystemPrompt } from \"./system-prompt.js\";\r\nimport { trimMessages, buildApiMessages } from \"./message-builder.js\";\r\nimport { ToolRegistry } from \"../tool/registry.js\";\r\nimport type { Gate, ToolCallRecord, ToolResult } from \"../tool/types.js\";\r\nimport { AlwaysAllowGate } from \"../tool/types.js\";\r\n\r\n/** Session 构造选项 */\r\nexport interface SessionOptions {\r\n /** 当前工作目录(注入到 system prompt) */\r\n cwd?: string;\r\n /** 最大工具调用轮次(防止无限循环),默认 20 */\r\n maxToolRounds?: number;\r\n /** 为模型输出预留的 token 数,默认 4096 */\r\n reservedForOutput?: number;\r\n /** 强制保留最近 N 轮对话(不参与上下文裁剪),默认 10 */\r\n preserveRecentRounds?: number;\r\n /** 项目上下文(AGENTS.md 内容),可选 */\r\n projectContext?: string;\r\n /** Gate 权限门 — 控制工具执行前的审批,默认 AlwaysAllowGate */\r\n gate?: Gate;\r\n /** 写目录的白名单(用于 confine 路径安全),默认 [cwd] */\r\n writeRoots?: string[];\r\n}\r\n\r\n/**\r\n * Session 表示一个 Agent 会话 — 与 LLM 的一次完整对话,\r\n * 包含消息历史、流式输出、工具执行、成本追踪。\r\n */\r\nexport class Session {\r\n readonly #messages: ChatMessage[] = [];\r\n readonly #provider: Provider;\r\n readonly #toolRegistry: ToolRegistry;\r\n readonly #costTracker: CostTracker;\r\n readonly #options: Required<\r\n Pick<SessionOptions, \"cwd\" | \"maxToolRounds\" | \"reservedForOutput\" | \"preserveRecentRounds\">\r\n > & { projectContext?: string; gate: Gate; writeRoots: string[] };\r\n readonly #abortController = new AbortController();\r\n\r\n // 风暴检测:记录每轮的工具调用错误\r\n #stormRecords: ToolCallRecord[] = [];\r\n\r\n constructor(\r\n provider: Provider,\r\n tools: AnyAgentTool[] | ToolRegistry = [],\r\n costTracker?: CostTracker,\r\n options?: SessionOptions,\r\n ) {\r\n this.#provider = provider;\r\n // 兼容 AnyAgentTool[] 和 ToolRegistry 两种入参\r\n if (tools instanceof ToolRegistry) {\r\n this.#toolRegistry = tools;\r\n } else {\r\n this.#toolRegistry = new ToolRegistry();\r\n this.#toolRegistry.registerAll(tools);\r\n }\r\n this.#costTracker = costTracker ?? new CostTracker();\r\n this.#options = {\r\n cwd: options?.cwd ?? process.cwd(),\r\n maxToolRounds: options?.maxToolRounds ?? 20,\r\n reservedForOutput: options?.reservedForOutput ?? 4096,\r\n preserveRecentRounds: options?.preserveRecentRounds ?? 10,\r\n projectContext: options?.projectContext,\r\n gate: options?.gate ?? new AlwaysAllowGate(),\r\n writeRoots: options?.writeRoots ?? [options?.cwd ?? process.cwd()],\r\n };\r\n }\r\n\r\n // -------------------------------------------------------------------------\r\n // 公共只读属性\r\n // -------------------------------------------------------------------------\r\n\r\n get messages(): readonly ChatMessage[] {\r\n return this.#messages;\r\n }\r\n\r\n get accumulatedCost(): number {\r\n return this.#costTracker.sessionTotalCost;\r\n }\r\n\r\n get costTracker(): CostTracker {\r\n return this.#costTracker;\r\n }\r\n\r\n get model(): string {\r\n return this.#provider.model();\r\n }\r\n\r\n /** 获取工具注册表(只读视图) */\r\n get toolRegistry(): ToolRegistry {\r\n return this.#toolRegistry;\r\n }\r\n\r\n // -------------------------------------------------------------------------\r\n // 流式对话 — Agent 主循环\r\n // -------------------------------------------------------------------------\r\n\r\n /**\r\n * 执行一轮用户对话,以 AsyncGenerator 形式逐步 yield 事件。\r\n *\r\n * 主循环流程:\r\n * 1. 追加用户消息\r\n * 2. 进入 Agent 循环(最多 maxToolRounds 轮)\r\n * a. 构建消息 → 裁剪 → 调用 Provider 流式接口\r\n * b. 解析响应:文本增量、工具调用、使用量\r\n * c. 如果有工具调用 → 执行工具 → 追加结果 → 继续循环\r\n * d. 如果没有工具调用 → 退出循环\r\n * 3. yield done 事件\r\n */\r\n async *chat(userInput: string, opts?: import(\"../provider/types.js\").ChatOptions): AsyncGenerator<AgentEvent> {\r\n // 1. 追加用户消息\r\n this.#messages.push({ role: \"user\", content: userInput });\r\n\r\n // 2. 进入 Agent 循环\r\n const startTime = Date.now();\r\n let toolRounds = 0;\r\n\r\n try {\r\n while (toolRounds < this.#options.maxToolRounds) {\r\n // a. 构建消息\r\n const systemPrompt = this.#buildSystemPrompt();\r\n const [trimmed] = trimMessages(\r\n [...this.#messages],\r\n {\r\n model: this.#provider.model() as ModelId,\r\n reservedForOutput: this.#options.reservedForOutput,\r\n systemPrompt,\r\n preserveRecentRounds: this.#options.preserveRecentRounds,\r\n },\r\n );\r\n const apiMessages = buildApiMessages(systemPrompt, trimmed);\r\n\r\n // b. 调用 Provider 流式接口\r\n const toolDefs = this.#buildToolDefinitions();\r\n // 合并内部选项与用户传入的设置\r\n const stream = this.#provider.chat(apiMessages, {\r\n signal: this.#abortController.signal,\r\n tools: toolDefs.length > 0 ? toolDefs : undefined,\r\n thinkingAllowed: opts?.thinkingAllowed,\r\n thinkingEffort: opts?.thinkingEffort,\r\n responseFormat: opts?.responseFormat,\r\n toolChoice: opts?.toolChoice,\r\n });\r\n\r\n // c. 逐步解析流式响应\r\n let accumulatedText = \"\";\r\n let lastUsage: UsageInfo | undefined;\r\n let lastToolCalls: ProviderToolCall[] | undefined;\r\n let lastFinishReason: string | null = null;\r\n\r\n for await (const chunk of stream) {\r\n if (chunk.content) {\r\n accumulatedText += chunk.content;\r\n yield { type: \"text_delta\", content: chunk.content };\r\n }\r\n\r\n if (chunk.toolCalls && chunk.toolCalls.length > 0) {\r\n lastToolCalls = chunk.toolCalls;\r\n }\r\n\r\n if (chunk.usage) {\r\n lastUsage = chunk.usage;\r\n }\r\n\r\n if (chunk.finishReason) {\r\n lastFinishReason = chunk.finishReason;\r\n }\r\n }\r\n\r\n // d. 记录使用量与成本\r\n if (lastUsage) {\r\n const modelId = this.#provider.model() as ModelId;\r\n this.#costTracker.record(lastUsage, modelId);\r\n yield { type: \"usage\", usage: lastUsage, model: modelId };\r\n }\r\n\r\n // e. 追加助手消息到历史\r\n const assistantMsg: ChatMessage = {\r\n role: \"assistant\",\r\n content: accumulatedText,\r\n };\r\n if (lastToolCalls && lastToolCalls.length > 0) {\r\n assistantMsg.toolCalls = lastToolCalls;\r\n }\r\n this.#messages.push(assistantMsg);\r\n\r\n // f. 如果有工具调用,执行工具并继续循环\r\n if (lastToolCalls && lastToolCalls.length > 0) {\r\n yield { type: \"tool_calls\", calls: lastToolCalls };\r\n\r\n // 风暴检测:检查上一个回合是否同一工具同一错误重复\r\n const stormBroken = this.#checkStormBreak(lastToolCalls);\r\n if (stormBroken) {\r\n const stormMsg = \"\\n⚠️ 同一工具重复出错,已强制切换策略\\n\";\r\n yield { type: \"text_delta\", content: stormMsg };\r\n // 清除 assistant 消息中的 toolCalls,风暴中断不再执行这些调用\r\n assistantMsg.toolCalls = undefined;\r\n // 将风暴信息追加到助手消息文本中\r\n assistantMsg.content += stormMsg;\r\n // 重置风暴计数\r\n this.#stormRecords = [];\r\n toolRounds++;\r\n continue;\r\n }\r\n\r\n // 执行工具调用批次\r\n const results = await this.#executeBatch(lastToolCalls);\r\n this.#stormRecords = results.records;\r\n\r\n for (const item of results.items) {\r\n yield { type: \"tool_result\", name: item.name, result: item.result };\r\n\r\n // 构建 tool 消息内容\r\n let toolContent = item.result.data;\r\n if (item.result.diff && item.result.diff.patch) {\r\n toolContent += `\\n\\n${item.result.diff.patch}`;\r\n }\r\n\r\n this.#messages.push({\r\n role: \"tool\",\r\n content: toolContent,\r\n toolCallId: item.callId,\r\n name: item.name,\r\n });\r\n }\r\n\r\n toolRounds++;\r\n continue;\r\n }\r\n\r\n // 没有工具调用,退出循环\r\n break;\r\n }\r\n } catch (err: unknown) {\r\n if (err instanceof DOMException && err.name === \"AbortError\") {\r\n return;\r\n }\r\n if (err instanceof Error && err.name === \"AbortError\") {\r\n return;\r\n }\r\n\r\n yield {\r\n type: \"error\",\r\n error: err instanceof Error ? err : new Error(String(err)),\r\n };\r\n return;\r\n }\r\n\r\n const elapsed = Date.now() - startTime;\r\n yield { type: \"done\", elapsed };\r\n }\r\n\r\n // -------------------------------------------------------------------------\r\n // 工具执行 — 批量、并行/串行、Gate、风暴检测\r\n // -------------------------------------------------------------------------\r\n\r\n /**\r\n * 执行一批工具调用。\r\n *\r\n * 并行策略:\r\n * - 如果这批工具全部是 ReadOnly 的,并行执行(最多 8 并发)\r\n * - 否则按顺序串行执行,保证写/读顺序\r\n */\r\n async #executeBatch(calls: ProviderToolCall[]): Promise<{ items: Array<{ name: string; callId: string; result: ToolResult }>; records: ToolCallRecord[] }> {\r\n const toolCtx: ToolContext = {\r\n cwd: this.#options.cwd,\r\n signal: this.#abortController.signal,\r\n writeRoots: this.#options.writeRoots,\r\n };\r\n\r\n // 判断是否能并行:全部 ReadOnly(使用 ToolKind 语义分类)\r\n const allReadOnly = calls.every((tc) => {\r\n const tool = this.#toolRegistry.get(tc.name);\r\n return tool ? isReadOnly(tool.kind) : true;\r\n });\r\n\r\n if (allReadOnly && calls.length > 1) {\r\n // 并行执行 ReadOnly 工具\r\n const MAX_PARALLEL = 8;\r\n const items: Array<{ name: string; callId: string; result: ToolResult }> = [];\r\n const records: ToolCallRecord[] = [];\r\n\r\n // 分批并行,每批最多 MAX_PARALLEL 个\r\n for (let i = 0; i < calls.length; i += MAX_PARALLEL) {\r\n const batch = calls.slice(i, i + MAX_PARALLEL);\r\n const promises = batch.map((tc) => this.#executeOne(tc, toolCtx));\r\n const batchResults = await Promise.all(promises);\r\n for (const r of batchResults) {\r\n items.push(r.item);\r\n records.push(r.record);\r\n }\r\n }\r\n\r\n return { items, records };\r\n }\r\n\r\n // 串行执行\r\n const items: Array<{ name: string; callId: string; result: ToolResult }> = [];\r\n const records: ToolCallRecord[] = [];\r\n for (const tc of calls) {\r\n const r = await this.#executeOne(tc, toolCtx);\r\n items.push(r.item);\r\n records.push(r.record);\r\n }\r\n\r\n return { items, records };\r\n }\r\n\r\n /**\r\n * 执行单个工具调用,包含 Gate 检查和预览。\r\n */\r\n async #executeOne(\r\n tc: ProviderToolCall,\r\n ctx: ToolContext,\r\n ): Promise<{ item: { name: string; callId: string; result: ToolResult }; record: ToolCallRecord }> {\r\n const toolName = tc.name;\r\n const timestamp = Date.now();\r\n\r\n // 1. 查找工具\r\n const tool = this.#toolRegistry.get(toolName);\r\n if (!tool) {\r\n const errMsg = `工具 \"${toolName}\" 不存在或已被禁用`;\r\n return {\r\n item: { name: toolName, callId: tc.id, result: { success: false, data: errMsg, error: \"TOOL_NOT_FOUND\" } },\r\n record: { name: toolName, success: false, error: \"TOOL_NOT_FOUND\", timestamp },\r\n };\r\n }\r\n\r\n // 2. 解析参数\r\n let toolArgs: unknown;\r\n try {\r\n toolArgs = tc.arguments ? JSON.parse(tc.arguments) : {};\r\n } catch {\r\n toolArgs = {};\r\n }\r\n\r\n // 3. Gate 检查(权限门)\r\n const gateResult = await this.#options.gate.check(toolName, toolArgs);\r\n if (!gateResult) {\r\n const errMsg = `工具 \"${toolName}\" 被权限门拒绝`;\r\n return {\r\n item: { name: toolName, callId: tc.id, result: { success: false, data: errMsg, error: \"GATE_DENIED\" } },\r\n record: { name: toolName, success: false, error: \"GATE_DENIED\", timestamp },\r\n };\r\n }\r\n\r\n // 4. 非只读工具有预览(可选)\r\n if (!isReadOnly(tool.kind)) {\r\n const maybePreview = (tool as { preview?: (args: unknown, ctx: ToolContext) => Promise<unknown> }).preview;\r\n if (typeof maybePreview === \"function\") {\r\n try {\r\n await maybePreview(toolArgs, ctx);\r\n } catch {\r\n // 预览失败不影响执行\r\n }\r\n }\r\n }\r\n\r\n // 5. 执行工具\r\n try {\r\n const result = await tool.execute(toolArgs, ctx);\r\n return {\r\n item: { name: toolName, callId: tc.id, result },\r\n record: {\r\n name: toolName,\r\n success: result.success,\r\n error: result.error,\r\n timestamp,\r\n },\r\n };\r\n } catch (err: unknown) {\r\n const message = err instanceof Error ? err.message : String(err);\r\n const errorResult = { success: false, data: `工具 \"${toolName}\" 执行异常:${message}`, error: \"EXECUTION_ERROR\" };\r\n return {\r\n item: { name: toolName, callId: tc.id, result: errorResult },\r\n record: { name: toolName, success: false, error: \"EXECUTION_ERROR\", timestamp },\r\n };\r\n }\r\n }\r\n\r\n // -------------------------------------------------------------------------\r\n // 风暴检测 — 同一工具同一错误连续 3 次 → 强制换策略\r\n // -------------------------------------------------------------------------\r\n\r\n /**\r\n * 连续 3 次同一工具同一错误 → 触发风暴中断。\r\n */\r\n #checkStormBreak(currentCalls: ProviderToolCall[]): boolean {\r\n if (this.#stormRecords.length < 3) return false;\r\n\r\n // 检查当前调用中是否有工具名与最近 3 次错误记录匹配\r\n const recentErrors = this.#stormRecords.slice(-3);\r\n if (recentErrors.length < 3) return false;\r\n\r\n // 检查最近 3 次是否同一工具同一错误\r\n const first = recentErrors[0] as ToolCallRecord;\r\n const allSame = recentErrors.every(\r\n (r) => r.name === first.name && r.error === first.error && !r.success,\r\n );\r\n\r\n if (!allSame) return false;\r\n\r\n // 检查当前调用是否还包含这个工具\r\n return currentCalls.some((tc) => tc.name === first.name);\r\n }\r\n\r\n // -------------------------------------------------------------------------\r\n // 会话管理\r\n // -------------------------------------------------------------------------\r\n\r\n /** 取消正在进行的流式请求 */\r\n abort(): void {\r\n this.#abortController.abort();\r\n }\r\n\r\n /** 重置会话历史(保留 provider/tools 配置,重置成本追踪) */\r\n reset(): void {\r\n this.#messages.length = 0;\r\n this.#costTracker.resetSession();\r\n this.#stormRecords = [];\r\n }\r\n\r\n // -------------------------------------------------------------------------\r\n // 内部方法\r\n // -------------------------------------------------------------------------\r\n\r\n /** 构建系统提示词 */\r\n #buildSystemPrompt(): string {\r\n const enabledTools = this.#toolRegistry.list();\r\n const toolDescs = enabledTools.map((t) => ({\r\n name: t.name,\r\n description: t.description,\r\n parameters: t.parameters as unknown as Record<string, unknown>,\r\n }));\r\n\r\n const opts: SystemPromptOptions = {\r\n model: this.#provider.model(),\r\n maxToolRounds: this.#options.maxToolRounds,\r\n tools: toolDescs.length > 0 ? toolDescs : undefined,\r\n projectContext: this.#options.projectContext ?? undefined,\r\n cwd: this.#options.cwd,\r\n };\r\n\r\n return buildSystemPrompt(opts);\r\n }\r\n\r\n /** 将注册的工具转为 ToolDefinition 格式(预留给 function calling) */\r\n #buildToolDefinitions(): ToolDefinition[] {\r\n return this.#toolRegistry.list().map((t) => ({\r\n type: \"function\" as const,\r\n function: {\r\n name: t.name,\r\n description: t.description,\r\n parameters: t.parameters as unknown as Record<string, unknown>,\r\n },\r\n }));\r\n }\r\n}\r\n","// ---------------------------------------------------------------------------\r\n// 工具执行沙箱 — 路径约束、路径安全、超时控制、输出截断\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { resolve, relative, isAbsolute } from \"node:path\";\r\nimport { realpath } from \"node:fs/promises\";\r\nimport { spawn } from \"node:child_process\";\r\nimport process from \"node:process\";\r\n\r\n/** 沙箱默认配置 */\r\nconst DEFAULT_TIMEOUT_MS = 30_000;\r\nconst DEFAULT_MAX_OUTPUT_LENGTH = 50_000;\r\nconst DEFAULT_MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB\r\n\r\n/** 是否为 Windows 平台 */\r\nconst isWindows = process.platform === \"win32\";\r\n\r\n// ---------------------------------------------------------------------------\r\n// 路径安全 — confine / resolveIn / realPath\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * 将路径解析为绝对路径。\r\n * 相对路径基于 cwd 解析,绝对路径原样返回。\r\n */\r\nexport function resolvePath(inputPath: string, cwd: string): string {\r\n const resolved = isAbsolute(inputPath) ? inputPath : resolve(cwd, inputPath);\r\n return resolve(resolved);\r\n}\r\n\r\n/**\r\n * 获取路径的真实路径(解析符号链接)。\r\n * 如果路径不存在,则对其父目录逐级解析,尽可能获取真实路径。\r\n */\r\nasync function realPath(target: string): Promise<string> {\r\n try {\r\n return await realpath(target);\r\n } catch {\r\n // 路径可能还不存在(新建文件),尝试解析父目录\r\n const parent = resolve(target, \"..\");\r\n try {\r\n const realParent = await realpath(parent);\r\n return resolve(realParent, relative(parent, target));\r\n } catch {\r\n // 父目录也不存在,返回原路径\r\n return resolve(target);\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * 检查目标路径是否在允许的根目录范围内。\r\n *\r\n * @param allowedRoots 允许的根目录列表(绝对路径)\r\n * @param target 要检查的目标路径(绝对路径)\r\n * @returns 如果目标在范围内返回 true,否则返回 false 及错误信息\r\n */\r\nexport async function confine(\r\n allowedRoots: string[],\r\n target: string,\r\n): Promise<{ ok: true } | { ok: false; error: string }> {\r\n if (allowedRoots.length === 0) {\r\n return { ok: true }; // 没有限制,默认允许\r\n }\r\n\r\n const realTarget = await realPath(target);\r\n\r\n for (const root of allowedRoots) {\r\n const realRoot = await realPath(root);\r\n // 检查 target 是否在 root 之下\r\n const rel = relative(realRoot, realTarget);\r\n if (!rel.startsWith(\"..\") && rel !== \"\" && !rel.startsWith(\"/\") && !rel.startsWith(\"\\\\\")) {\r\n return { ok: true };\r\n }\r\n // 允许 target 恰好等于 root\r\n if (realTarget === realRoot) {\r\n return { ok: true };\r\n }\r\n }\r\n\r\n return {\r\n ok: false,\r\n error: `路径 \"${target}\" 不在允许的写入范围内 ${allowedRoots.join(\", \")}`,\r\n };\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 输出截断\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * 截断过长的输出内容。\r\n */\r\nexport function truncateOutput(content: string, maxLength = DEFAULT_MAX_OUTPUT_LENGTH): string {\r\n if (content.length <= maxLength) return content;\r\n const truncated = content.slice(0, maxLength);\r\n return `${truncated}\\n\\n... [输出过长,已截断,共 ${String(content.length)} 字符]`;\r\n}\r\n\r\n/**\r\n * 获取默认超时时间。\r\n */\r\nexport function getDefaultTimeout(): number {\r\n return DEFAULT_TIMEOUT_MS;\r\n}\r\n\r\n/**\r\n * 获取默认最大文件大小。\r\n */\r\nexport function getDefaultMaxFileSize(): number {\r\n return DEFAULT_MAX_FILE_SIZE;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 超时控制\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * 创建一个带超时的 AbortController。\r\n */\r\nexport function createTimeoutSignal(signal?: AbortSignal, timeoutMs = DEFAULT_TIMEOUT_MS): AbortController {\r\n const controller = new AbortController();\r\n\r\n // 外部信号触发时,联动中止\r\n if (signal) {\r\n signal.addEventListener(\"abort\", () => {\r\n controller.abort();\r\n }, { once: true });\r\n }\r\n\r\n // 超时自动中止\r\n const timer = setTimeout(() => {\r\n controller.abort();\r\n }, timeoutMs);\r\n controller.signal.addEventListener(\"abort\", () => {\r\n clearTimeout(timer);\r\n }, { once: true });\r\n\r\n return controller;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Shell 命令执行\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * 执行 shell 命令的通用封装。\r\n */\r\nexport async function execCommand(\r\n command: string,\r\n args: string[],\r\n cwd: string,\r\n timeoutMs = DEFAULT_TIMEOUT_MS,\r\n signal?: AbortSignal,\r\n /** 如果为 true,表示 command 已经是 shell 程序(如 cmd/sh),不需要再包装 */\r\n isShellCommand?: boolean,\r\n): Promise<{ stdout: string; stderr: string; exitCode: number | null }> {\r\n return new Promise((resolve) => {\r\n let spawnCmd: string;\r\n let spawnArgs: string[];\r\n let useShell: boolean;\r\n\r\n if (isShellCommand) {\r\n spawnCmd = command;\r\n spawnArgs = args;\r\n useShell = false;\r\n } else {\r\n useShell = !isWindows;\r\n spawnCmd = isWindows ? \"cmd\" : command;\r\n spawnArgs = isWindows\r\n ? [\"/c\", command, ...args]\r\n : args;\r\n }\r\n\r\n const child = spawn(spawnCmd, spawnArgs, {\r\n cwd,\r\n shell: useShell,\r\n env: { ...process.env },\r\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\r\n });\r\n\r\n let stdout = \"\";\r\n let stderr = \"\";\r\n\r\n child.stdout.on(\"data\", (data: Buffer) => {\r\n stdout += data.toString();\r\n });\r\n\r\n child.stderr.on(\"data\", (data: Buffer) => {\r\n stderr += data.toString();\r\n });\r\n\r\n const timeout = setTimeout(() => {\r\n child.kill(\"SIGTERM\");\r\n // 给 5 秒优雅退出时间\r\n setTimeout(() => {\r\n child.kill(\"SIGKILL\");\r\n }, 5000);\r\n }, timeoutMs);\r\n\r\n // 外部中止信号\r\n if (signal) {\r\n if (signal.aborted) {\r\n child.kill(\"SIGTERM\");\r\n }\r\n signal.addEventListener(\"abort\", () => {\r\n child.kill(\"SIGTERM\");\r\n }, { once: true });\r\n }\r\n\r\n child.on(\"close\", (code) => {\r\n clearTimeout(timeout);\r\n resolve({ stdout, stderr, exitCode: code });\r\n });\r\n\r\n child.on(\"error\", (err) => {\r\n clearTimeout(timeout);\r\n stderr += `\\n进程启动失败:${err.message}`;\r\n resolve({ stdout, stderr, exitCode: null });\r\n });\r\n });\r\n}\r\n","// ---------------------------------------------------------------------------\r\n// 文件差异计算 — Myers O(ND) 算法 + unified patch 生成\r\n//\r\n// Myers 算法与 Git 的 diff 引擎同款,产生最短编辑脚本(SES)。\r\n// 时间复杂度 O((N+M)D),空间复杂度 O(N+M),其中 D 为编辑距离。\r\n//\r\n// 实现方式:非递归的 Myers 差分算法。\r\n// 1. 正向搜索:找到从 (0,0) 到 (N,M) 的最短编辑路径\r\n// 2. 回溯路径:从 (N,M) 回溯到 (0,0),生成 DiffLine 序列\r\n// ---------------------------------------------------------------------------\r\n\r\nimport type { FileDiff } from \"./types.js\";\r\n\r\n// ---------------------------------------------------------------------------\r\n// 内部类型\r\n// ---------------------------------------------------------------------------\r\n\r\n/** 单行 diff 操作类型 */\r\ntype Op = \"equal\" | \"add\" | \"remove\";\r\n\r\n/** 一行 diff 记录 */\r\ninterface DiffLine {\r\n op: Op;\r\n line: string;\r\n}\r\n\r\n/** unified diff 中的一个 hunk */\r\ninterface DiffHunk {\r\n oldStart: number;\r\n oldCount: number;\r\n newStart: number;\r\n newCount: number;\r\n lines: DiffLine[];\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// EOL(行尾风格)检测\r\n// ---------------------------------------------------------------------------\r\n\r\ntype EOFStyle = \"lf\" | \"crlf\" | \"no-eol\";\r\n\r\ninterface SplitResult {\r\n lines: string[];\r\n eol: string;\r\n eofStyle: EOFStyle;\r\n}\r\n\r\nfunction splitLines(text: string): SplitResult {\r\n if (text.length === 0) {\r\n return { lines: [], eol: \"\\n\", eofStyle: \"no-eol\" };\r\n }\r\n\r\n const crlfIdx = text.indexOf(\"\\r\\n\");\r\n const lfIdx = text.indexOf(\"\\n\");\r\n let eol: string;\r\n if (crlfIdx !== -1 && (lfIdx === -1 || crlfIdx <= lfIdx)) {\r\n eol = \"\\r\\n\";\r\n } else if (lfIdx !== -1) {\r\n eol = \"\\n\";\r\n } else {\r\n eol = \"\\n\";\r\n }\r\n\r\n const lines = text.split(eol);\r\n\r\n let eofStyle: EOFStyle;\r\n if (text.endsWith(eol)) {\r\n if (lines.length > 0 && lines[lines.length - 1] === \"\") {\r\n lines.pop();\r\n }\r\n eofStyle = \"lf\";\r\n } else {\r\n eofStyle = \"no-eol\";\r\n }\r\n\r\n return { lines, eol, eofStyle };\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Myers 非递归差分算法\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * 计算两个行数组之间的最短编辑脚本(SES)。\r\n *\r\n * 使用非递归 Myers 算法:\r\n * 1. 正向搜索 V 数组,记录每步的进度\r\n * 2. 到达终点后回溯路径生成 DiffLine 列表\r\n */\r\nfunction computeLineDiff(oldLines: string[], newLines: string[]): DiffLine[] {\r\n const N = oldLines.length;\r\n const M = newLines.length;\r\n\r\n if (N === 0 && M === 0) return [];\r\n if (N === 0) return newLines.map((line) => ({ op: \"add\" as Op, line }));\r\n if (M === 0) return oldLines.map((line) => ({ op: \"remove\" as Op, line }));\r\n\r\n // 存储每一步的 V 快照,用于回溯\r\n const trace: Map<number, number>[] = [];\r\n\r\n const maxD = N + M;\r\n // V[k] = x position;初始化为 -1 表示未访问\r\n const V: number[] = new Array(2 * maxD + 2).fill(-1);\r\n // k 的偏移量,使索引非负\r\n const offset = maxD;\r\n\r\n V[offset + 1] = 0;\r\n\r\n // 正向搜索:从 d=0 开始,逐层扩展\r\n outer:\r\n for (let d = 0; d <= maxD; d++) {\r\n // 保存当前层的 V 快照\r\n const snapshot = new Map<number, number>();\r\n\r\n const kStart = -d;\r\n const kEnd = d;\r\n\r\n for (let k = kStart; k <= kEnd; k += 2) {\r\n const ki = k + offset;\r\n let x: number;\r\n\r\n if (k === -d || (k !== d && V[ki - 1]! < V[ki + 1]!)) {\r\n x = V[ki + 1]!;\r\n } else {\r\n x = V[ki - 1]! + 1;\r\n }\r\n\r\n let y = x - k;\r\n\r\n // 沿着对角线延伸\r\n while (x < N && y < M && oldLines[x] === newLines[y]) {\r\n x++;\r\n y++;\r\n }\r\n\r\n V[ki] = x;\r\n snapshot.set(k, x);\r\n\r\n // 到达终点\r\n if (x >= N && y >= M) {\r\n trace.push(snapshot);\r\n // 回溯路径\r\n return backtrack(trace, N, M, oldLines, newLines, offset);\r\n }\r\n }\r\n\r\n trace.push(snapshot);\r\n }\r\n\r\n // 不应该到达这里(总有解)\r\n return oldLines.map((line) => ({ op: \"remove\" as Op, line }))\r\n .concat(newLines.map((line) => ({ op: \"add\" as Op, line })));\r\n}\r\n\r\n/**\r\n * 从 trace 中回溯最短编辑路径,生成 DiffLine 列表。\r\n *\r\n * trace 存储了每一层 (d) 的 V 快照。\r\n * 从终点 (N, M) 开始,逆着路径逐层回溯到 (0, 0)。\r\n */\r\nfunction backtrack(\r\n trace: Map<number, number>[],\r\n N: number,\r\n M: number,\r\n oldLines: string[],\r\n newLines: string[],\r\n offset: number,\r\n): DiffLine[] {\r\n const result: DiffLine[] = [];\r\n\r\n let x = N;\r\n let y = M;\r\n\r\n // 从最后一层回溯到第 1 层(第 0 层只有初始蛇行,没有实际移动)\r\n for (let d = trace.length - 1; d >= 1; d--) {\r\n const k = x - y;\r\n\r\n // 从 trace[d-1] 中查找上一步的 x 位置\r\n // 注意:trace[d] 存的是本层结果,前一层的数据在 trace[d-1] 中\r\n const prevSnapshot = d > 0 ? (trace[d - 1] as Map<number, number>) : new Map();\r\n const prevKDown = prevSnapshot.get(k - 1);\r\n const prevKUp = prevSnapshot.get(k + 1);\r\n\r\n let prevK: number;\r\n if (k === -d) {\r\n // 只能在 k+1 方向\r\n prevK = k + 1;\r\n } else if (k === d) {\r\n // 只能在 k-1 方向\r\n prevK = k - 1;\r\n } else if ((prevKDown ?? -1) < (prevKUp ?? -1)) {\r\n // k+1 方向的 V 值更大,从那里过来(删除)\r\n prevK = k + 1;\r\n } else {\r\n // k-1 方向的 V 值更大,从那里过来(添加)\r\n prevK = k - 1;\r\n }\r\n\r\n const prevX = prevSnapshot.get(prevK) ?? 0;\r\n const prevY = prevX - prevK;\r\n\r\n // ===== 从 (prevX, prevY) 到 (x, y) 的回溯路径 =====\r\n\r\n if (prevK === k - 1) {\r\n // prevK = k-1 → V[k-1] 被使用 → x = V[k-1] + 1 → 垂直移动(删除)\r\n // 路径: (prevX, prevY) → (prevX+1, prevY) [删除] → 蛇行 [(prevX+2, prevY+1), ...]\r\n // 回溯: 先走蛇行(相等行), 再走删除行\r\n\r\n // 第一步:蛇行(相等行),从 (x,y) 回到 (prevX+1, prevY)\r\n while (x > prevX + 1 && y > prevY) {\r\n x--;\r\n y--;\r\n result.unshift({ op: \"equal\", line: oldLines[x] as string });\r\n }\r\n\r\n // 第二步:删除行\r\n if (x > prevX) {\r\n x--;\r\n result.unshift({ op: \"remove\", line: oldLines[x] as string });\r\n }\r\n } else {\r\n // prevK = k+1 → V[k+1] 被使用 → x = V[k+1] → 水平移动(添加)\r\n // 路径: (prevX, prevY) → (prevX, prevY+1) [添加] → 蛇行 [(prevX+1, prevY+2), ...]\r\n // 回溯: 先走蛇行(相等行), 再走添加行\r\n\r\n // 第一步:蛇行(相等行),从 (x,y) 回到 (prevX, prevY+1)\r\n while (x > prevX && y > prevY + 1) {\r\n x--;\r\n y--;\r\n result.unshift({ op: \"equal\", line: oldLines[x] as string });\r\n }\r\n\r\n // 第二步:添加行\r\n if (y > prevY) {\r\n y--;\r\n result.unshift({ op: \"add\", line: newLines[y] as string });\r\n }\r\n }\r\n }\r\n\r\n // 处理初始蛇行:从 (0, 0) 到当前 (x, y) 的相等行\r\n // 逆向(从 (x, y) 到 (1, 1) 到 (0, 0) 回溯)\r\n while (x > 0 && y > 0) {\r\n x--;\r\n y--;\r\n result.unshift({ op: \"equal\", line: oldLines[x] as string });\r\n }\r\n\r\n return result;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Hunk 分组\r\n// ---------------------------------------------------------------------------\r\n\r\n/** 上下文行数(unified diff 标准 3 行) */\r\nconst CONTEXT_LINES = 3;\r\n\r\n/**\r\n * 将 diff 行列表分组为 hunks。\r\n */\r\nfunction groupIntoHunks(diffLines: DiffLine[]): DiffHunk[] {\r\n if (diffLines.length === 0) return [];\r\n\r\n // 找出所有变更行的索引\r\n const changeIndices: number[] = [];\r\n for (let i = 0; i < diffLines.length; i++) {\r\n const line = diffLines[i] as DiffLine;\r\n if (line.op !== \"equal\") {\r\n changeIndices.push(i);\r\n }\r\n }\r\n\r\n if (changeIndices.length === 0) return [];\r\n\r\n // 将相近的变更行分组\r\n const groups: number[][] = [[changeIndices[0] as number]];\r\n\r\n for (let idx = 1; idx < changeIndices.length; idx++) {\r\n const lastGroup = groups[groups.length - 1] as number[];\r\n const prevIdx = lastGroup[lastGroup.length - 1] as number;\r\n const currIdx = changeIndices[idx] as number;\r\n\r\n let gap = 0;\r\n for (let k = prevIdx + 1; k < currIdx; k++) {\r\n const line = diffLines[k] as DiffLine;\r\n if (line.op === \"equal\") gap++;\r\n }\r\n\r\n if (gap <= 2 * CONTEXT_LINES) {\r\n lastGroup.push(currIdx);\r\n } else {\r\n groups.push([currIdx]);\r\n }\r\n }\r\n\r\n // 为每个分组生成 hunk\r\n const hunks: DiffHunk[] = [];\r\n\r\n for (const group of groups) {\r\n const firstChangeIdx = group[0] as number;\r\n const lastChangeIdx = group[group.length - 1] as number;\r\n\r\n const startIdx = Math.max(0, firstChangeIdx - CONTEXT_LINES);\r\n const endIdx = Math.min(diffLines.length - 1, lastChangeIdx + CONTEXT_LINES);\r\n\r\n const lines = diffLines.slice(startIdx, endIdx + 1);\r\n\r\n let oldLine = 0;\r\n let newLine = 0;\r\n for (let i = 0; i < startIdx; i++) {\r\n const line = diffLines[i] as DiffLine;\r\n if (line.op === \"equal\" || line.op === \"remove\") oldLine++;\r\n if (line.op === \"equal\" || line.op === \"add\") newLine++;\r\n }\r\n\r\n let oldCount = 0;\r\n let newCount = 0;\r\n for (const diffLine of lines) {\r\n if (diffLine.op === \"equal\" || diffLine.op === \"remove\") oldCount++;\r\n if (diffLine.op === \"equal\" || diffLine.op === \"add\") newCount++;\r\n }\r\n\r\n hunks.push({\r\n oldStart: oldLine + 1,\r\n oldCount,\r\n newStart: newLine + 1,\r\n newCount,\r\n lines,\r\n });\r\n }\r\n\r\n return hunks;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Unified diff 格式化\r\n// ---------------------------------------------------------------------------\r\n\r\nfunction formatUnifiedDiff(hunks: DiffHunk[], filePath: string): string {\r\n if (hunks.length === 0) return \"\";\r\n\r\n const parts: string[] = [];\r\n const fileName = extractFileName(filePath);\r\n\r\n parts.push(`--- a/${fileName}`);\r\n parts.push(`+++ b/${fileName}`);\r\n\r\n for (const hunk of hunks) {\r\n parts.push(\r\n `@@ -${String(hunk.oldStart)},${String(hunk.oldCount)} +${String(hunk.newStart)},${String(hunk.newCount)} @@`,\r\n );\r\n\r\n for (const diffLine of hunk.lines) {\r\n switch (diffLine.op) {\r\n case \"equal\":\r\n parts.push(` ${diffLine.line}`);\r\n break;\r\n case \"remove\":\r\n parts.push(`-${diffLine.line}`);\r\n break;\r\n case \"add\":\r\n parts.push(`+${diffLine.line}`);\r\n break;\r\n }\r\n }\r\n }\r\n\r\n return parts.join(\"\\n\");\r\n}\r\n\r\nfunction formatNewFileDiff(lines: string[], filePath: string): string {\r\n const fileName = extractFileName(filePath);\r\n const parts: string[] = [];\r\n\r\n parts.push(`--- /dev/null`);\r\n parts.push(`+++ b/${fileName}`);\r\n parts.push(`@@ -0,0 +1,${String(lines.length)} @@`);\r\n\r\n for (const line of lines) {\r\n parts.push(`+${line}`);\r\n }\r\n\r\n return parts.join(\"\\n\");\r\n}\r\n\r\nfunction formatDeletedFileDiff(lines: string[], filePath: string): string {\r\n const fileName = extractFileName(filePath);\r\n const parts: string[] = [];\r\n\r\n parts.push(`--- a/${fileName}`);\r\n parts.push(`+++ /dev/null`);\r\n parts.push(`@@ -1,${String(lines.length)} +0,0 @@`);\r\n\r\n for (const line of lines) {\r\n parts.push(`-${line}`);\r\n }\r\n\r\n return parts.join(\"\\n\");\r\n}\r\n\r\nfunction extractFileName(filePath: string): string {\r\n const normalized = filePath.replace(/\\\\/g, \"/\");\r\n return normalized.split(\"/\").pop() ?? filePath;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 公共 API\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * 计算两个文本之间的 unified diff,返回 FileDiff 对象。\r\n *\r\n * 使用 Myers O(ND) 算法产生最短编辑脚本,与 Git 的 diff 引擎同款。\r\n * 自动检测并保留原始文件的行尾风格(LF / CRLF)。\r\n */\r\nexport function computeFileDiff(\r\n oldContent: string,\r\n newContent: string,\r\n filePath: string,\r\n): FileDiff {\r\n if (oldContent === newContent) {\r\n return {\r\n filePath,\r\n patch: \"\",\r\n existedBefore: oldContent.length > 0,\r\n additions: 0,\r\n deletions: 0,\r\n };\r\n }\r\n\r\n if (oldContent.length === 0) {\r\n const split = splitLines(newContent);\r\n const patch = formatNewFileDiff(split.lines, filePath);\r\n return {\r\n filePath,\r\n patch,\r\n existedBefore: false,\r\n additions: split.lines.length,\r\n deletions: 0,\r\n };\r\n }\r\n\r\n if (newContent.length === 0) {\r\n const split = splitLines(oldContent);\r\n const patch = formatDeletedFileDiff(split.lines, filePath);\r\n return {\r\n filePath,\r\n patch,\r\n existedBefore: true,\r\n additions: 0,\r\n deletions: split.lines.length,\r\n };\r\n }\r\n\r\n const oldSplit = splitLines(oldContent);\r\n const newSplit = splitLines(newContent);\r\n\r\n const diffLines = computeLineDiff(oldSplit.lines, newSplit.lines);\r\n\r\n let additions = 0;\r\n let deletions = 0;\r\n for (const line of diffLines) {\r\n if (line.op === \"add\") additions++;\r\n if (line.op === \"remove\") deletions++;\r\n }\r\n\r\n const hunks = groupIntoHunks(diffLines);\r\n const patch = formatUnifiedDiff(hunks, filePath);\r\n\r\n return {\r\n filePath,\r\n patch,\r\n existedBefore: true,\r\n additions,\r\n deletions,\r\n };\r\n}\r\n\r\n/**\r\n * 将文件变更应用于原内容,返回新的文件内容。\r\n */\r\nexport function applyChange(\r\n kind: \"edit\" | \"create\" | \"delete\",\r\n oldContent: string,\r\n newContent: string,\r\n): string {\r\n if (kind === \"delete\") return \"\";\r\n if (kind === \"create\") return newContent;\r\n return newContent;\r\n}\r\n","// ---------------------------------------------------------------------------\r\n// 行尾(EOL)风格检测与保留 — 写工具统一使用此模块\r\n// ---------------------------------------------------------------------------\r\n//\r\n// 解决问题:原文件为 CRLF(Windows 项目常态)时,写工具若直接 writeFile\r\n// 新内容(往往以 LF 编码),会导致整文件 EOL 翻转,git diff 全文件飘红,\r\n// AI 看到大量\"假改动\"后陷入修复循环。此模块提供公共的 EOL 检测与落盘封装。\r\n//\r\n// 设计要点:\r\n// - 检测策略与 diff.ts 的 splitLines 保持一致:首个 CRLF 早于首个 LF → CRLF\r\n// - 对原内容做 EOL 探测,按探测结果统一新内容的行尾\r\n// - 保留原文件是否以行尾符结尾(trailing newline)的特征\r\n\r\nimport { writeFile } from \"node:fs/promises\";\r\n\r\n/**\r\n * 检测文本的行尾风格。\r\n *\r\n * 与 diff.ts 的 splitLines 探测策略一致:\r\n * - 找到最早出现的换行符位置;若最早的是 \\r\\n 则判为 CRLF,否则 LF\r\n *\r\n * @returns 行尾字符串:\"\\r\\n\" 或 \"\\n\";空文本默认 \"\\n\"\r\n */\r\nexport function detectEol(text: string): string {\r\n if (text.length === 0) return \"\\n\";\r\n\r\n const crlfIdx = text.indexOf(\"\\r\\n\");\r\n const lfIdx = text.indexOf(\"\\n\");\r\n\r\n if (crlfIdx !== -1 && (lfIdx === -1 || crlfIdx <= lfIdx)) {\r\n return \"\\r\\n\";\r\n }\r\n return \"\\n\";\r\n}\r\n\r\n/**\r\n * 检测文本是否以行尾符结尾(trailing newline)。\r\n */\r\nexport function hasTrailingNewline(text: string): boolean {\r\n return text.endsWith(\"\\n\") || text.endsWith(\"\\r\\n\");\r\n}\r\n\r\n/**\r\n * 按给定 EOL 规整文本,并保留与原内容一致的\"是否以行尾结尾\"特征。\r\n *\r\n * 规整步骤:\r\n * 1. 先统一拆成行片段(兼容现有 \\r\\n 与 \\n 混合)\r\n * 2. 用目标 EOL 重新拼接\r\n * 3. 若原内容以行尾结尾但规整后没有,补一个;反之不补\r\n *\r\n * 用于写入前对 newContent 做归一化,保证不在 EOL 上产生噪声 diff。\r\n *\r\n * @param originalContent 原文件内容(用于探测 EOL 风格与尾行尾特征)\r\n * @param newContent 待写入的新内容\r\n * @returns 应实际落盘的内容\r\n */\r\nexport function normalizeEol(originalContent: string, newContent: string): string {\r\n const targetEol = detectEol(originalContent);\r\n\r\n // 拆行:兼容混合 \\r\\n / \\n;先把 \\r\\n 统一成 \\n 再按 \\n 拆\r\n const lines = newContent.replace(/\\r\\n/g, \"\\n\").split(\"\\n\");\r\n\r\n // 判断原内容是否以行尾符结尾\r\n const originalHasTrailing = hasTrailingNewline(originalContent);\r\n\r\n // newContent 经 replace+split 后,行尾结尾会在末尾多一个空行元素\r\n // 如 \"a\\nb\\n\" → [\"a\",\"b\",\"\"],\"a\\nb\" → [\"a\",\"b\"]\r\n const newHasTrailing = lines.length > 0 && lines[lines.length - 1] === \"\";\r\n\r\n // 若新内容原本就带尾行尾,split 出的末尾空字符串保留即可\r\n // 若新内容不带尾行尾,但原内容带,则补一个\r\n let result = lines.join(targetEol);\r\n if (originalHasTrailing && !newHasTrailing) {\r\n result += targetEol;\r\n }\r\n // 若新内容带尾行尾但原内容不带:去掉末尾 EOL\r\n if (!originalHasTrailing && newHasTrailing) {\r\n // result 此时末尾恰有一个 targetEol,截悼\r\n if (result.endsWith(targetEol)) {\r\n result = result.slice(0, -targetEol.length);\r\n }\r\n }\r\n\r\n return result;\r\n}\r\n\r\n/**\r\n * 写文件并保留原文件的 EOL 风格与尾行尾特征。\r\n *\r\n * - 原内容存在时:按原 EOL 归一化新内容后写入\r\n * - 原内容不存在(新建文件):LF + 保留新内容自带的尾行尾特征\r\n *\r\n * 所有写工具(write_file / edit_file / multi_edit / delete_range)应统一走此函数,\r\n * 避免 EOL 翻转造成的全文件 diff 风暴。\r\n *\r\n * @param filePath 目标文件绝对路径\r\n * @param originalContent 原文件内容;文件不存在时传空字符串\r\n * @param newContent 新内容\r\n */\r\nexport async function writeFileWithEol(\r\n filePath: string,\r\n originalContent: string,\r\n newContent: string,\r\n): Promise<void> {\r\n const content =\r\n originalContent.length > 0\r\n ? normalizeEol(originalContent, newContent)\r\n : newContent;\r\n await writeFile(filePath, content, \"utf-8\");\r\n}","// ---------------------------------------------------------------------------\r\n// read_file 工具 — 读取指定路径的文件内容\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { readFile, stat } from \"node:fs/promises\";\r\nimport { open } from \"node:fs/promises\";\r\nimport { relative } from \"node:path\";\r\nimport { ToolKind, type AgentTool, type ToolContext, type ToolResult } from \"../types.js\";\r\nimport { resolvePath, truncateOutput } from \"../sandbox.js\";\r\n\r\n/** read_file 工具的参数格式 */\r\nexport interface ReadFileArgs {\r\n /** 文件路径(相对于 cwd 或绝对路径) */\r\n path: string;\r\n /** 起始行号(1-based),默认 1 */\r\n startLine?: number;\r\n /** 结束行号(1-based,包含),默认到文件末尾 */\r\n endLine?: number;\r\n}\r\n\r\n/** 读取前 N 字节检测是否为二进制文件 */\r\nasync function checkBinary(filePath: string): Promise<boolean> {\r\n const fileHandle = await open(filePath, \"r\");\r\n try {\r\n const buffer = Buffer.alloc(8192); // 8KB\r\n const { bytesRead } = await fileHandle.read(buffer, 0, 8192, 0);\r\n return buffer.subarray(0, bytesRead).includes(0); // NUL 字节 → 二进制\r\n } finally {\r\n await fileHandle.close();\r\n }\r\n}\r\n\r\n/**\r\n * read_file 工具 — 读取文件内容,支持行号范围选择。\r\n *\r\n * 功能:\r\n * - 按行号范围读取部分内容\r\n * - 自动添加行号前缀(→ 分隔)\r\n * - 文件大小限制(默认 10MB)\r\n * - 输出长度截断(默认 50K 字符)\r\n * - 二进制检测(阻止读取二进制文件)\r\n */\r\nexport const readFileTool: AgentTool<ReadFileArgs> = {\r\n name: \"read_file\",\r\n kind: ToolKind.Read,\r\n description:\r\n \"读取指定路径的文件内容。支持行号范围选择,输出带行号。适用于查看源代码、配置文件等文本文件。自动拒绝二进制文件。\",\r\n parameters: {\r\n type: \"object\",\r\n properties: {\r\n path: {\r\n type: \"string\",\r\n description: \"文件路径(相对于当前工作目录或绝对路径)\",\r\n },\r\n startLine: {\r\n type: \"number\",\r\n description: \"起始行号(从 1 开始),默认为 1\",\r\n },\r\n endLine: {\r\n type: \"number\",\r\n description: \"结束行号(包含),默认到文件末尾\",\r\n },\r\n },\r\n required: [\"path\"],\r\n additionalProperties: false,\r\n },\r\n\r\n async execute(args: ReadFileArgs, ctx: ToolContext): Promise<ToolResult> {\r\n if (!args?.path || typeof args.path !== \"string\") {\r\n return { success: false, data: \"缺少必要参数 path\", error: \"INVALID_ARGS\" };\r\n }\r\n\r\n const filePath = resolvePath(args.path, ctx.cwd);\r\n\r\n try {\r\n // 检查文件大小\r\n const fileStat = await stat(filePath);\r\n const maxSize = 10 * 1024 * 1024; // 10MB\r\n if (fileStat.size > maxSize) {\r\n return {\r\n success: false,\r\n data: `文件过大(${(fileStat.size / 1024 / 1024).toFixed(1)}MB),超过 10MB 限制`,\r\n error: \"FILE_TOO_LARGE\",\r\n };\r\n }\r\n\r\n // 检查是否为目录\r\n if (fileStat.isDirectory()) {\r\n return {\r\n success: false,\r\n data: `\"${filePath}\" 是一个目录,请使用 ls 工具查看目录内容`,\r\n error: \"IS_DIRECTORY\",\r\n };\r\n }\r\n\r\n // 二进制检测:扫描前 8KB 看是否包含 NUL 字节\r\n if (fileStat.size > 0) {\r\n const isBin = await checkBinary(filePath);\r\n if (isBin) {\r\n return {\r\n success: false,\r\n data: `\"${filePath}\" 看起来是二进制文件,不支持读取`,\r\n error: \"BINARY_FILE\",\r\n };\r\n }\r\n }\r\n\r\n const content = await readFile(filePath, \"utf-8\");\r\n const lines = content.split(\"\\n\");\r\n\r\n // 如果是空文件,最后一项为空行,pop 掉\r\n if (lines.length > 0 && lines[lines.length - 1] === \"\" && content.endsWith(\"\\n\")) {\r\n lines.pop();\r\n }\r\n\r\n // 行号范围处理(1-based → 0-based)\r\n const startLine = Math.max(1, args.startLine ?? 1) - 1;\r\n const endLine = args.endLine\r\n ? Math.min(args.endLine, lines.length)\r\n : lines.length;\r\n const selectedLines = lines.slice(startLine, endLine);\r\n\r\n // 添加行号前缀(右对齐 + →)\r\n const lineNumWidth = String(endLine).length;\r\n const result = selectedLines\r\n .map((line, i) => {\r\n const lineNum = String(startLine + i + 1).padStart(lineNumWidth, \" \");\r\n return `${lineNum}→${line}`;\r\n })\r\n .join(\"\\n\");\r\n\r\n // 尾部提示:告知剩余行数\r\n const remaining = lines.length - endLine;\r\n const tailHint = remaining > 0\r\n ? `\\n\\n[还有 ${remaining} 行;使用 startLine=${endLine + 1} 继续查看]`\r\n : \"\";\r\n\r\n // UI 摘要:仅显示路径与行数范围,避免把文件内容塞进 UI\r\n const relPath = relative(ctx.cwd, filePath).replace(/\\\\/g, \"/\");\r\n const rangeLabel = (startLine > 0 || endLine < lines.length)\r\n ? `第 ${startLine + 1}-${endLine} 行`\r\n : `${lines.length} 行`;\r\n\r\n return {\r\n success: true,\r\n data: truncateOutput(result) + tailHint,\r\n summary: `📖 ${relPath}(${rangeLabel})`,\r\n };\r\n } catch (err: unknown) {\r\n const message = err instanceof Error ? err.message : String(err);\r\n return {\r\n success: false,\r\n data: `读取文件失败:${message}`,\r\n error: \"READ_ERROR\",\r\n };\r\n }\r\n },\r\n};\r\n","// ---------------------------------------------------------------------------\r\n// write_file 工具 — 创建或覆盖文件\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { mkdir, readFile } from \"node:fs/promises\";\r\nimport { dirname, relative, basename } from \"node:path\";\r\nimport { ToolKind, type AgentTool, type ToolContext, type ToolResult } from \"../types.js\";\r\nimport { resolvePath, confine } from \"../sandbox.js\";\r\nimport { computeFileDiff } from \"../diff.js\";\r\nimport { writeFileWithEol } from \"../eol.js\";\r\n\r\n/** write_file 工具的参数格式 */\r\nexport interface WriteFileArgs {\r\n /** 文件路径(相对于 cwd 或绝对路径) */\r\n path: string;\r\n /** 要写入的文件内容 */\r\n content: string;\r\n}\r\n\r\n/**\r\n * write_file 工具 — 创建或覆盖文件。\r\n */\r\nexport const writeFileTool: AgentTool<WriteFileArgs> = {\r\n name: \"write_file\",\r\n kind: ToolKind.Edit,\r\n description:\r\n \"创建或覆盖文件。如果父目录不存在会自动创建。适用于创建新文件或完全替换文件内容。请勿用此工具创建 _temp_、_debug_ 等用于诊断/调试的临时文件——如需诊断请改用 bash 工具内联脚本(node -e)。\",\r\n parameters: {\r\n type: \"object\",\r\n properties: {\r\n path: {\r\n type: \"string\",\r\n description: \"文件路径(相对于当前工作目录或绝对路径)\",\r\n },\r\n content: {\r\n type: \"string\",\r\n description: \"要写入的文件内容\",\r\n },\r\n },\r\n required: [\"path\", \"content\"],\r\n additionalProperties: false,\r\n },\r\n\r\n async execute(args: WriteFileArgs, ctx: ToolContext): Promise<ToolResult> {\r\n if (!args?.path || typeof args.path !== \"string\") {\r\n return { success: false, data: \"缺少必要参数 path\", error: \"INVALID_ARGS\" };\r\n }\r\n if (args.content === undefined || args.content === null) {\r\n return { success: false, data: \"缺少必要参数 content\", error: \"INVALID_ARGS\" };\r\n }\r\n\r\n const filePath = resolvePath(args.path, ctx.cwd);\r\n\r\n try {\r\n if (ctx.writeRoots && ctx.writeRoots.length > 0) {\r\n const conf = await confine(ctx.writeRoots, filePath);\r\n if (!conf.ok) {\r\n return { success: false, data: conf.error, error: \"OUTSIDE_WRITE_ROOTS\" };\r\n }\r\n }\r\n\r\n let oldContent = \"\";\r\n let existedBefore = false;\r\n try {\r\n oldContent = await readFile(filePath, \"utf-8\");\r\n existedBefore = true;\r\n } catch {\r\n // 文件不存在,这是新建文件\r\n }\r\n\r\n await mkdir(dirname(filePath), { recursive: true });\r\n\r\n const content = String(args.content);\r\n await writeFileWithEol(filePath, oldContent, content);\r\n\r\n const diff = computeFileDiff(oldContent, content, filePath);\r\n diff.existedBefore = existedBefore;\r\n\r\n const lineCount = content.split(\"\\n\").length;\r\n const byteSize = Buffer.byteLength(content, \"utf-8\");\r\n\r\n const action = existedBefore ? \"已修改\" : \"已创建\";\r\n const diffSummary = existedBefore\r\n ? `,+${diff.additions} -${diff.deletions}`\r\n : `,+${diff.additions} 行(新建)`;\r\n\r\n const fileName = basename(filePath);\r\n const summary = existedBefore\r\n ? `📝 修改: ${fileName} (+${diff.additions} -${diff.deletions})`\r\n : `📝 新建: ${fileName} (+${diff.additions} 行)`;\r\n\r\n return {\r\n success: true,\r\n data: `文件${action}:${relative(ctx.cwd, filePath).replace(/\\\\/g, \"/\")}(${lineCount} 行,${byteSize} 字节${diffSummary})`,\r\n summary,\r\n diff,\r\n };\r\n } catch (err: unknown) {\r\n const message = err instanceof Error ? err.message : String(err);\r\n return {\r\n success: false,\r\n data: `写入文件失败:${message}`,\r\n error: \"WRITE_ERROR\",\r\n };\r\n }\r\n },\r\n};\r\n","// ---------------------------------------------------------------------------\r\n// edit_file 工具 — 精确字符串替换编辑文件\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { readFile } from \"node:fs/promises\";\r\nimport { basename } from \"node:path\";\r\nimport { ToolKind, type AgentTool, type ToolContext, type ToolResult } from \"../types.js\";\r\nimport { resolvePath, confine } from \"../sandbox.js\";\r\nimport { computeFileDiff } from \"../diff.js\";\r\nimport { writeFileWithEol } from \"../eol.js\";\r\n\r\n/** edit_file 工具的参数格式 */\r\nexport interface EditFileArgs {\r\n /** 文件路径(相对于 cwd 或绝对路径) */\r\n path: string;\r\n /** 要查找的原始文本(精确匹配) */\r\n old_text: string;\r\n /** 替换后的新文本 */\r\n new_text: string;\r\n}\r\n\r\n/**\r\n * edit_file 工具 — 对文件进行精确字符串替换。\r\n */\r\nexport const editFileTool: AgentTool<EditFileArgs> = {\r\n name: \"edit_file\",\r\n kind: ToolKind.Edit,\r\n description:\r\n \"对文件进行精确字符串替换。查找文件中的 old_text 并替换为 new_text。如果 old_text 出现多次或未找到则报错。适用于小范围精确修改。\",\r\n parameters: {\r\n type: \"object\",\r\n properties: {\r\n path: {\r\n type: \"string\",\r\n description: \"文件路径(相对于当前工作目录或绝对路径)\",\r\n },\r\n old_text: {\r\n type: \"string\",\r\n description: \"要查找的原始文本(精确匹配)\",\r\n },\r\n new_text: {\r\n type: \"string\",\r\n description: \"替换后的新文本\",\r\n },\r\n },\r\n required: [\"path\", \"old_text\", \"new_text\"],\r\n additionalProperties: false,\r\n },\r\n\r\n async execute(args: EditFileArgs, ctx: ToolContext): Promise<ToolResult> {\r\n if (!args?.path || typeof args.path !== \"string\") {\r\n return { success: false, data: \"缺少必要参数 path\", error: \"INVALID_ARGS\" };\r\n }\r\n if (typeof args.old_text !== \"string\") {\r\n return { success: false, data: \"缺少必要参数 old_text\", error: \"INVALID_ARGS\" };\r\n }\r\n if (typeof args.new_text !== \"string\") {\r\n return { success: false, data: \"缺少必要参数 new_text\", error: \"INVALID_ARGS\" };\r\n }\r\n\r\n const filePath = resolvePath(args.path, ctx.cwd);\r\n\r\n if (ctx.writeRoots && ctx.writeRoots.length > 0) {\r\n const conf = await confine(ctx.writeRoots, filePath);\r\n if (!conf.ok) {\r\n return { success: false, data: conf.error, error: \"OUTSIDE_WRITE_ROOTS\" };\r\n }\r\n }\r\n\r\n try {\r\n const content = await readFile(filePath, \"utf-8\");\r\n\r\n const firstIndex = content.indexOf(args.old_text);\r\n if (firstIndex === -1) {\r\n return {\r\n success: false,\r\n data: `未找到要替换的文本。请确认 old_text 与文件内容完全一致(包括缩进和空格)。`,\r\n error: \"TEXT_NOT_FOUND\",\r\n };\r\n }\r\n\r\n const secondIndex = content.indexOf(args.old_text, firstIndex + 1);\r\n if (secondIndex !== -1) {\r\n return {\r\n success: false,\r\n data: `要替换的文本在文件中出现多次,请提供更多上下文以精确定位。`,\r\n error: \"TEXT_MULTIPLE_MATCHES\",\r\n };\r\n }\r\n\r\n const newContent = content.replace(args.old_text, args.new_text);\r\n await writeFileWithEol(filePath, content, newContent);\r\n\r\n const diff = computeFileDiff(content, newContent, filePath);\r\n diff.existedBefore = true;\r\n\r\n const beforeText = content.slice(0, firstIndex);\r\n const startLine = beforeText.split(\"\\n\").length;\r\n const oldLines = args.old_text.split(\"\\n\").length;\r\n const newLines = args.new_text.split(\"\\n\").length;\r\n\r\n const diffSummary = `+${diff.additions} -${diff.deletions}`;\r\n const summary = `📝 修改: ${basename(filePath)} (+${diff.additions} -${diff.deletions})`;\r\n\r\n return {\r\n success: true,\r\n data: `文件已编辑:${filePath}\\n替换位置:第 ${startLine} 行\\n${oldLines} 行 → ${newLines} 行\\n变更:${diffSummary}`,\r\n summary,\r\n diff,\r\n };\r\n } catch (err: unknown) {\r\n const message = err instanceof Error ? err.message : String(err);\r\n return {\r\n success: false,\r\n data: `编辑文件失败:${message}`,\r\n error: \"EDIT_ERROR\",\r\n };\r\n }\r\n },\r\n};\r\n","// ---------------------------------------------------------------------------\r\n// multi_edit 工具 — 精确字符串批量替换编辑文件(原子性)\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { readFile } from \"node:fs/promises\";\r\nimport { basename } from \"node:path\";\r\nimport { ToolKind, type AgentTool, type ToolContext, type ToolResult } from \"../types.js\";\r\nimport { resolvePath, confine } from \"../sandbox.js\";\r\nimport { computeFileDiff } from \"../diff.js\";\r\nimport { writeFileWithEol } from \"../eol.js\";\r\n\r\n/** 单个编辑步骤 */\r\nexport interface EditStep {\r\n /** 要查找的原始文本(精确匹配) */\r\n oldText: string;\r\n /** 替换后的新文本 */\r\n newText: string;\r\n /** 是否替换文件中所有匹配项,默认 false */\r\n replaceAll?: boolean;\r\n}\r\n\r\n/** multi_edit 工具的参数格式 */\r\nexport interface MultiEditArgs {\r\n /** 文件路径(相对于 cwd 或绝对路径) */\r\n path: string;\r\n /** 有序编辑步骤数组 */\r\n edits: EditStep[];\r\n}\r\n\r\n/**\r\n * multi_edit 工具 — 对文件进行原子批量替换编辑。\r\n */\r\nexport const multiEditTool: AgentTool<MultiEditArgs> = {\r\n name: \"multi_edit\",\r\n kind: ToolKind.Edit,\r\n description:\r\n \"对文件进行原子批量替换编辑。在一个请求中执行多个精确替换,任一失败则全部回滚。支持 replaceAll 参数做全局替换。适用于需要同时修改文件多处的场景。\",\r\n parameters: {\r\n type: \"object\",\r\n properties: {\r\n path: {\r\n type: \"string\",\r\n description: \"文件路径(相对于当前工作目录或绝对路径)\",\r\n },\r\n edits: {\r\n type: \"array\",\r\n description: \"有序的编辑步骤列表。每个步骤包含 oldText(精确匹配)、newText(替换内容)和可选的 replaceAll(是否替换所有匹配项)\",\r\n items: {\r\n type: \"object\",\r\n properties: {\r\n oldText: { type: \"string\", description: \"要查找的原始文本(精确匹配)\" },\r\n newText: { type: \"string\", description: \"替换后的新文本\" },\r\n replaceAll: { type: \"boolean\", description: \"是否替换所有匹配项,默认 false\" },\r\n },\r\n required: [\"oldText\", \"newText\"],\r\n additionalProperties: false,\r\n },\r\n },\r\n },\r\n required: [\"path\", \"edits\"],\r\n additionalProperties: false,\r\n },\r\n\r\n async execute(args: MultiEditArgs, ctx: ToolContext): Promise<ToolResult> {\r\n if (!args?.path || typeof args.path !== \"string\") {\r\n return { success: false, data: \"缺少必要参数 path\", error: \"INVALID_ARGS\" };\r\n }\r\n if (!Array.isArray(args.edits) || args.edits.length === 0) {\r\n return { success: false, data: \"缺少必要参数 edits(非空数组)\", error: \"INVALID_ARGS\" };\r\n }\r\n\r\n const filePath = resolvePath(args.path, ctx.cwd);\r\n\r\n if (ctx.writeRoots && ctx.writeRoots.length > 0) {\r\n const conf = await confine(ctx.writeRoots, filePath);\r\n if (!conf.ok) {\r\n return { success: false, data: conf.error, error: \"OUTSIDE_WRITE_ROOTS\" };\r\n }\r\n }\r\n\r\n try {\r\n const originalContent = await readFile(filePath, \"utf-8\");\r\n let currentContent = originalContent;\r\n\r\n for (let idx = 0; idx < args.edits.length; idx++) {\r\n const step = args.edits[idx]!;\r\n if (typeof step.oldText !== \"string\") {\r\n return {\r\n success: false,\r\n data: `第 ${idx + 1} 步:缺少有效的 oldText`,\r\n error: \"INVALID_STEP_ARGS\",\r\n };\r\n }\r\n\r\n if (step.replaceAll) {\r\n let count = 0;\r\n let pos = -1;\r\n while ((pos = currentContent.indexOf(step.oldText, pos + 1)) !== -1) {\r\n count++;\r\n }\r\n\r\n if (count === 0) {\r\n return {\r\n success: false,\r\n data: `第 ${idx + 1} 步:未找到要替换的文本,请确认 oldText 与文件内容完全一致`,\r\n error: \"TEXT_NOT_FOUND\",\r\n };\r\n }\r\n\r\n currentContent = currentContent.split(step.oldText).join(step.newText);\r\n } else {\r\n const firstIdx = currentContent.indexOf(step.oldText);\r\n if (firstIdx === -1) {\r\n return {\r\n success: false,\r\n data: `第 ${idx + 1} 步:未找到要替换的文本。请确认 oldText 与文件内容完全一致(包括缩进和空格)。`,\r\n error: \"TEXT_NOT_FOUND\",\r\n };\r\n }\r\n\r\n const secondIdx = currentContent.indexOf(step.oldText, firstIdx + 1);\r\n if (secondIdx !== -1) {\r\n return {\r\n success: false,\r\n data: `第 ${idx + 1} 步:要替换的文本在文件中出现多次,请提供更多上下文以精确定位,或设置 replaceAll=true`,\r\n error: \"TEXT_MULTIPLE_MATCHES\",\r\n };\r\n }\r\n\r\n currentContent = currentContent.replace(step.oldText, step.newText);\r\n }\r\n }\r\n\r\n await writeFileWithEol(filePath, originalContent, currentContent);\r\n\r\n const diff = computeFileDiff(originalContent, currentContent, filePath);\r\n diff.existedBefore = true;\r\n\r\n return {\r\n success: true,\r\n data: `文件已编辑:${filePath}\\n共执行 ${args.edits.length} 步替换\\n变更:+${diff.additions} -${diff.deletions}`,\r\n summary: `📝 修改: ${basename(filePath)} (${args.edits.length} 步, +${diff.additions} -${diff.deletions})`,\r\n diff,\r\n };\r\n } catch (err: unknown) {\r\n const message = err instanceof Error ? err.message : String(err);\r\n return {\r\n success: false,\r\n data: `批量编辑失败:${message}`,\r\n error: \"MULTI_EDIT_ERROR\",\r\n };\r\n }\r\n },\r\n};\r\n","// ---------------------------------------------------------------------------\r\n// delete_range 工具 — 按行锚点删除文件中的行范围\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { readFile } from \"node:fs/promises\";\r\nimport { basename } from \"node:path\";\r\nimport { ToolKind, type AgentTool, type ToolContext, type ToolResult } from \"../types.js\";\r\nimport { resolvePath, confine } from \"../sandbox.js\";\r\nimport { computeFileDiff } from \"../diff.js\";\r\nimport { writeFileWithEol } from \"../eol.js\";\r\n\r\n/** delete_range 工具的参数格式 */\r\nexport interface DeleteRangeArgs {\r\n /** 文件路径(相对于 cwd 或绝对路径) */\r\n path: string;\r\n /** 删除范围的起始标记行(必须唯一) */\r\n startAnchor: string;\r\n /** 删除范围的结束标记行(必须唯一,须在 start_anchor 之后) */\r\n endAnchor: string;\r\n /** 是否包含锚点行本身,默认 false(只删除中间内容) */\r\n inclusive?: boolean;\r\n}\r\n\r\n/**\r\n * 在行列表中查找唯一匹配的行号。\r\n */\r\nfunction findUniqueLine(lines: string[], anchor: string, label: string): { line: number } | { error: string } {\r\n const matches: number[] = [];\r\n for (let i = 0; i < lines.length; i++) {\r\n if ((lines[i] as string) === anchor) {\r\n matches.push(i);\r\n }\r\n }\r\n\r\n if (matches.length === 0) {\r\n return { error: `未找到 \"${label}\" 锚点行,请确认内容完全一致` };\r\n }\r\n if (matches.length > 1) {\r\n return { error: `\"${label}\" 锚点行在文件中出现 ${matches.length} 次,请使用更唯一的行内容` };\r\n }\r\n\r\n return { line: matches[0] as number };\r\n}\r\n\r\n/**\r\n * delete_range 工具 — 删除文件中两个锚点行之间的内容。\r\n */\r\nexport const deleteRangeTool: AgentTool<DeleteRangeArgs> = {\r\n name: \"delete_range\",\r\n kind: ToolKind.Edit,\r\n description:\r\n \"删除文件中两个锚点行之间的内容。通过两个唯一的行内容精确定位范围。适用于删除方法、代码块、配置段等。\",\r\n parameters: {\r\n type: \"object\",\r\n properties: {\r\n path: {\r\n type: \"string\",\r\n description: \"文件路径(相对于当前工作目录或绝对路径)\",\r\n },\r\n startAnchor: {\r\n type: \"string\",\r\n description: \"删除范围的起始标记行内容(必须在文件中唯一,精确匹配整行)\",\r\n },\r\n endAnchor: {\r\n type: \"string\",\r\n description: \"删除范围的结束标记行内容(必须在文件中唯一,精确匹配整行,须在 start_anchor 之后)\",\r\n },\r\n inclusive: {\r\n type: \"boolean\",\r\n description: \"是否包含锚点行本身,默认 false(只删除两行之间的内容)\",\r\n },\r\n },\r\n required: [\"path\", \"startAnchor\", \"endAnchor\"],\r\n additionalProperties: false,\r\n },\r\n\r\n async execute(args: DeleteRangeArgs, ctx: ToolContext): Promise<ToolResult> {\r\n if (!args?.path || typeof args.path !== \"string\") {\r\n return { success: false, data: \"缺少必要参数 path\", error: \"INVALID_ARGS\" };\r\n }\r\n if (typeof args.startAnchor !== \"string\") {\r\n return { success: false, data: \"缺少有效参数 startAnchor\", error: \"INVALID_ARGS\" };\r\n }\r\n if (typeof args.endAnchor !== \"string\") {\r\n return { success: false, data: \"缺少有效参数 endAnchor\", error: \"INVALID_ARGS\" };\r\n }\r\n\r\n const filePath = resolvePath(args.path, ctx.cwd);\r\n const inclusive = args.inclusive ?? false;\r\n\r\n if (ctx.writeRoots && ctx.writeRoots.length > 0) {\r\n const conf = await confine(ctx.writeRoots, filePath);\r\n if (!conf.ok) {\r\n return { success: false, data: conf.error, error: \"OUTSIDE_WRITE_ROOTS\" };\r\n }\r\n }\r\n\r\n try {\r\n const content = await readFile(filePath, \"utf-8\");\r\n const lines = content.split(\"\\n\");\r\n\r\n const startResult = findUniqueLine(lines, args.startAnchor, \"start_anchor\");\r\n if (\"error\" in startResult) {\r\n return { success: false, data: startResult.error, error: \"ANCHOR_NOT_FOUND\" };\r\n }\r\n\r\n const endResult = findUniqueLine(lines, args.endAnchor, \"end_anchor\");\r\n if (\"error\" in endResult) {\r\n return { success: false, data: endResult.error, error: \"ANCHOR_NOT_FOUND\" };\r\n }\r\n\r\n const startLine = startResult.line;\r\n const endLine = endResult.line;\r\n\r\n if (startLine >= endLine) {\r\n return {\r\n success: false,\r\n data: \"end_anchor 必须在 start_anchor 之后\",\r\n error: \"ANCHOR_ORDER_ERROR\",\r\n };\r\n }\r\n\r\n const rangeStart = inclusive ? startLine : startLine + 1;\r\n const rangeEnd = inclusive ? endLine : endLine - 1;\r\n\r\n if (rangeStart > rangeEnd) {\r\n return {\r\n success: false,\r\n data: \"删除范围为空(两行锚点相邻且 inclusive=false)\",\r\n error: \"EMPTY_RANGE\",\r\n };\r\n }\r\n\r\n const newLines = [...lines.slice(0, rangeStart), ...lines.slice(rangeEnd + 1)];\r\n const newContent = newLines.join(\"\\n\");\r\n\r\n await writeFileWithEol(filePath, content, newContent);\r\n\r\n const diff = computeFileDiff(content, newContent, filePath);\r\n diff.existedBefore = true;\r\n\r\n const deletedLines = rangeEnd - rangeStart + 1;\r\n\r\n const summary = `📝 修改: ${basename(filePath)} (删 ${deletedLines} 行, +${diff.additions} -${diff.deletions})`;\r\n\r\n return {\r\n success: true,\r\n data: `文件已编辑:${filePath}\\n删除行范围:第 ${rangeStart + 1} 行 ~ 第 ${rangeEnd + 1} 行(共 ${deletedLines} 行)\\n变更:+${diff.additions} -${diff.deletions}`,\r\n summary,\r\n diff,\r\n };\r\n } catch (err: unknown) {\r\n const message = err instanceof Error ? err.message : String(err);\r\n return {\r\n success: false,\r\n data: `删除范围失败:${message}`,\r\n error: \"DELETE_RANGE_ERROR\",\r\n };\r\n }\r\n },\r\n};\r\n","// ---------------------------------------------------------------------------\r\n// bash 工具 — 执行 shell 命令\r\n// ---------------------------------------------------------------------------\r\n\r\nimport process from \"node:process\";\r\nimport { ToolKind, type AgentTool, type ToolContext, type ToolResult } from \"../types.js\";\r\nimport { execCommand, truncateOutput, getDefaultTimeout } from \"../sandbox.js\";\r\n\r\n/** 是否为 Windows 平台 */\r\nconst isWindows = process.platform === \"win32\";\r\n\r\n/** bash 工具的参数格式 */\r\nexport interface BashArgs {\r\n /** 要执行的命令 */\r\n command: string;\r\n /** 执行超时时间(毫秒),默认 30000 */\r\n timeout?: number;\r\n}\r\n\r\n/**\r\n * bash 工具 — 在 shell 中执行命令。\r\n */\r\nexport const bashTool: AgentTool<BashArgs> = {\r\n name: \"bash\",\r\n kind: ToolKind.Other,\r\n description:\r\n \"在 shell 中执行命令。返回标准输出、标准错误和退出码。支持超时控制和信号中止。适用于运行构建、测试、Git 操作等命令。\",\r\n parameters: {\r\n type: \"object\",\r\n properties: {\r\n command: {\r\n type: \"string\",\r\n description: \"要执行的 shell 命令\",\r\n },\r\n timeout: {\r\n type: \"number\",\r\n description: \"执行超时时间(毫秒),默认 30000\",\r\n },\r\n },\r\n required: [\"command\"],\r\n additionalProperties: false,\r\n },\r\n\r\n async execute(args: BashArgs, ctx: ToolContext): Promise<ToolResult> {\r\n if (!args?.command || typeof args.command !== \"string\") {\r\n return { success: false, data: \"缺少必要参数 command\", error: \"INVALID_ARGS\" };\r\n }\r\n\r\n const timeout = args.timeout ?? ctx.timeout ?? getDefaultTimeout();\r\n\r\n try {\r\n const shellCommand = isWindows ? \"cmd\" : \"sh\";\r\n const shellArgs = isWindows ? [\"/c\", args.command] : [\"-c\", args.command];\r\n\r\n const result = await execCommand(\r\n shellCommand,\r\n shellArgs,\r\n ctx.cwd,\r\n timeout,\r\n ctx.signal,\r\n true,\r\n );\r\n\r\n const parts: string[] = [];\r\n if (result.stdout) {\r\n parts.push(truncateOutput(result.stdout));\r\n }\r\n if (result.stderr) {\r\n parts.push(`[stderr]\\n${truncateOutput(result.stderr)}`);\r\n }\r\n\r\n const success = result.exitCode === 0;\r\n const output = parts.length > 0 ? parts.join(\"\\n\") : \"(无输出)\";\r\n\r\n const cmdPreview = args.command.length > 60\r\n ? args.command.slice(0, 57) + \"...\"\r\n : args.command;\r\n const summary = `🔧 $ ${cmdPreview}(exit ${result.exitCode ?? \"未知\"})`;\r\n\r\n return {\r\n success,\r\n data: `${output}\\n[退出码: ${result.exitCode ?? \"未知\"}]`,\r\n summary,\r\n error: success ? undefined : `EXIT_CODE_${result.exitCode ?? \"UNKNOWN\"}`,\r\n };\r\n } catch (err: unknown) {\r\n const message = err instanceof Error ? err.message : String(err);\r\n return {\r\n success: false,\r\n data: `命令执行失败:${message}`,\r\n error: \"EXECUTION_ERROR\",\r\n };\r\n }\r\n },\r\n};\r\n","// ---------------------------------------------------------------------------\r\n// glob 工具 — 文件搜索(模式匹配)\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { readdir, stat } from \"node:fs/promises\";\r\nimport { join, relative, isAbsolute } from \"node:path\";\r\nimport { ToolKind, type AgentTool, type ToolContext, type ToolResult } from \"../types.js\";\r\nimport { truncateOutput } from \"../sandbox.js\";\r\n\r\n/** glob 工具的参数格式 */\r\nexport interface GlobArgs {\r\n /** 搜索模式(支持 * 和 ** 通配符) */\r\n pattern: string;\r\n /** 搜索的起始目录,默认为 cwd */\r\n directory?: string;\r\n}\r\n\r\n/**\r\n * 将 glob 模式转换为正则表达式。\r\n */\r\nfunction globToRegex(pattern: string): RegExp {\r\n let regexStr = pattern;\r\n regexStr = regexStr.replace(/\\*\\*\\//g, \"<<GLOBSTAR_SLASH>>\");\r\n regexStr = regexStr.replace(/\\*\\*/g, \"<<GLOBSTAR>>\");\r\n regexStr = regexStr.replace(/\\?/g, \"<<QUESTION>>\");\r\n regexStr = regexStr.replace(/[.+^${}()|[\\]\\\\]/g, \"\\\\$&\");\r\n regexStr = regexStr.replace(/\\*/g, \"[^/]*\");\r\n regexStr = regexStr.replace(/<<GLOBSTAR_SLASH>>/g, \"(.*/)?\");\r\n regexStr = regexStr.replace(/<<GLOBSTAR>>/g, \".*\");\r\n regexStr = regexStr.replace(/<<QUESTION>>/g, \"[^/]\");\r\n return new RegExp(`^${regexStr}$`, \"i\");\r\n}\r\n\r\n/**\r\n * 递归遍历目录收集文件路径。\r\n */\r\nasync function walkDir(dir: string, baseDir: string): Promise<string[]> {\r\n const results: string[] = [];\r\n let entries;\r\n\r\n try {\r\n entries = await readdir(dir, { withFileTypes: true });\r\n } catch {\r\n return results;\r\n }\r\n\r\n for (const entry of entries) {\r\n if (entry.isDirectory() && (entry.name === \"node_modules\" || entry.name === \".git\")) {\r\n continue;\r\n }\r\n\r\n const fullPath = join(dir, entry.name);\r\n const relPath = relative(baseDir, fullPath);\r\n\r\n if (entry.isDirectory()) {\r\n results.push(...await walkDir(fullPath, baseDir));\r\n } else {\r\n results.push(relPath);\r\n }\r\n }\r\n\r\n return results;\r\n}\r\n\r\n/**\r\n * glob 工具 — 按模式搜索文件路径。\r\n *\r\n * 功能:\r\n * - 支持 * 和 ** 通配符\r\n * - 自动跳过 node_modules 和 .git 目录\r\n * - 返回相对于搜索目录的路径列表\r\n */\r\nexport const globTool: AgentTool<GlobArgs> = {\r\n name: \"glob\",\r\n kind: ToolKind.Read,\r\n description:\r\n \"按模式搜索文件路径。支持 *(匹配文件名部分)和 **(匹配多层目录)通配符。自动跳过 node_modules 和 .git 目录。\",\r\n parameters: {\r\n type: \"object\",\r\n properties: {\r\n pattern: {\r\n type: \"string\",\r\n description: \"搜索模式(支持 * 和 ** 通配符,如 **/*.ts、src/**/*.test.ts)\",\r\n },\r\n directory: {\r\n type: \"string\",\r\n description: \"搜索的起始目录,默认为当前工作目录\",\r\n },\r\n },\r\n required: [\"pattern\"],\r\n additionalProperties: false,\r\n },\r\n\r\n async execute(args: GlobArgs, ctx: ToolContext): Promise<ToolResult> {\r\n if (!args?.pattern || typeof args.pattern !== \"string\") {\r\n return { success: false, data: \"缺少必要参数 pattern\", error: \"INVALID_ARGS\" };\r\n }\r\n\r\n const searchDir = args.directory\r\n ? (isAbsolute(args.directory) ? args.directory : join(ctx.cwd, args.directory))\r\n : ctx.cwd;\r\n const regex = globToRegex(args.pattern);\r\n\r\n try {\r\n const dirStat = await stat(searchDir);\r\n if (!dirStat.isDirectory()) {\r\n return { success: false, data: `路径不是目录:${searchDir}`, error: \"NOT_DIRECTORY\" };\r\n }\r\n\r\n const allFiles = await walkDir(searchDir, searchDir);\r\n const matched = allFiles.filter((f) => regex.test(f));\r\n\r\n if (matched.length === 0) {\r\n return {\r\n success: true,\r\n data: `未找到匹配 \"${args.pattern}\" 的文件`,\r\n summary: `${args.pattern} → 0 个文件`,\r\n };\r\n }\r\n\r\n const limit = 200;\r\n const limited = matched.slice(0, limit);\r\n const output = limited.join(\"\\n\");\r\n const suffix = matched.length > limit ? `\\n\\n... 共 ${matched.length} 个文件,只显示前 ${limit} 个` : \"\";\r\n\r\n return {\r\n success: true,\r\n data: truncateOutput(output + suffix),\r\n summary: `${args.pattern} → ${matched.length} 个文件`,\r\n };\r\n } catch (err: unknown) {\r\n const message = err instanceof Error ? err.message : String(err);\r\n return {\r\n success: false,\r\n data: `文件搜索失败:${message}`,\r\n error: \"GLOB_ERROR\",\r\n };\r\n }\r\n },\r\n};\r\n","// ---------------------------------------------------------------------------\r\n// grep 工具 — 文件内容正则搜索\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { readdir, readFile, stat } from \"node:fs/promises\";\r\nimport { join, relative, isAbsolute } from \"node:path\";\r\nimport { ToolKind, type AgentTool, type ToolContext, type ToolResult } from \"../types.js\";\r\nimport { truncateOutput } from \"../sandbox.js\";\r\n\r\n/** grep 工具的参数格式 */\r\nexport interface GrepArgs {\r\n /** 正则表达式模式 */\r\n pattern: string;\r\n /** 搜索的目录路径,默认为 cwd */\r\n directory?: string;\r\n /** 文件扩展名过滤(如 \"ts\"、\"json\"),不含点号 */\r\n include?: string;\r\n /** 是否大小写敏感,默认 false */\r\n case_sensitive?: boolean;\r\n /** 最大搜索文件数,默认 200 */\r\n max_files?: number;\r\n}\r\n\r\n/** 单个匹配结果 */\r\ninterface GrepMatch {\r\n file: string;\r\n line: number;\r\n content: string;\r\n}\r\n\r\n/**\r\n * 递归遍历目录收集文件路径。\r\n */\r\nasync function collectFiles(\r\n dir: string,\r\n baseDir: string,\r\n extension?: string,\r\n maxFiles = 200,\r\n): Promise<string[]> {\r\n const results: string[] = [];\r\n let entries;\r\n\r\n try {\r\n entries = await readdir(dir, { withFileTypes: true });\r\n } catch {\r\n return results;\r\n }\r\n\r\n for (const entry of entries) {\r\n if (results.length >= maxFiles) break;\r\n\r\n if (entry.isDirectory() && (entry.name === \"node_modules\" || entry.name === \".git\" || entry.name === \"dist\")) {\r\n continue;\r\n }\r\n\r\n const fullPath = join(dir, entry.name);\r\n\r\n if (entry.isDirectory()) {\r\n results.push(...await collectFiles(fullPath, baseDir, extension, maxFiles - results.length));\r\n } else {\r\n if (extension && !entry.name.endsWith(`.${extension}`)) {\r\n continue;\r\n }\r\n results.push(fullPath);\r\n }\r\n }\r\n\r\n return results;\r\n}\r\n\r\n/**\r\n * grep 工具 — 在文件内容中搜索正则表达式匹配。\r\n */\r\nexport const grepTool: AgentTool<GrepArgs> = {\r\n name: \"grep\",\r\n kind: ToolKind.Read,\r\n description:\r\n \"在文件内容中搜索正则表达式。返回匹配行的文件路径、行号和内容。支持大小写敏感、文件扩展名过滤。\",\r\n parameters: {\r\n type: \"object\",\r\n properties: {\r\n pattern: {\r\n type: \"string\",\r\n description: \"正则表达式搜索模式\",\r\n },\r\n directory: {\r\n type: \"string\",\r\n description: \"搜索的目录路径,默认为当前工作目录\",\r\n },\r\n include: {\r\n type: \"string\",\r\n description: \"文件扩展名过滤(如 ts、json),不含点号\",\r\n },\r\n case_sensitive: {\r\n type: \"boolean\",\r\n description: \"是否大小写敏感,默认 false\",\r\n },\r\n max_files: {\r\n type: \"number\",\r\n description: \"最大搜索文件数,默认 200\",\r\n },\r\n },\r\n required: [\"pattern\"],\r\n additionalProperties: false,\r\n },\r\n\r\n async execute(args: GrepArgs, ctx: ToolContext): Promise<ToolResult> {\r\n if (!args?.pattern || typeof args.pattern !== \"string\") {\r\n return { success: false, data: \"缺少必要参数 pattern\", error: \"INVALID_ARGS\" };\r\n }\r\n\r\n const searchDir = args.directory\r\n ? (isAbsolute(args.directory) ? args.directory : join(ctx.cwd, args.directory))\r\n : ctx.cwd;\r\n const maxFiles = args.max_files ?? 200;\r\n\r\n try {\r\n const flags = args.case_sensitive ? \"g\" : \"gi\";\r\n const regex = new RegExp(args.pattern, flags);\r\n\r\n const dirStat = await stat(searchDir);\r\n if (!dirStat.isDirectory()) {\r\n return { success: false, data: `路径不是目录:${searchDir}`, error: \"NOT_DIRECTORY\" };\r\n }\r\n\r\n const files = await collectFiles(searchDir, searchDir, args.include, maxFiles);\r\n const matches: GrepMatch[] = [];\r\n\r\n for (const filePath of files) {\r\n try {\r\n const content = await readFile(filePath, \"utf-8\");\r\n const lines = content.split(\"\\n\");\r\n const relPath = relative(searchDir, filePath);\r\n\r\n for (let i = 0; i < lines.length; i++) {\r\n if (regex.test(lines[i]!)) {\r\n regex.lastIndex = 0;\r\n matches.push({\r\n file: relPath,\r\n line: i + 1,\r\n content: lines[i]!,\r\n });\r\n if (matches.length >= 500) break;\r\n }\r\n }\r\n\r\n if (matches.length >= 500) break;\r\n } catch {\r\n continue;\r\n }\r\n }\r\n\r\n if (matches.length === 0) {\r\n return {\r\n success: true,\r\n data: `未找到匹配 \"${args.pattern}\" 的内容`,\r\n summary: `🔍 \"${args.pattern}\" → 0 条命中`,\r\n };\r\n }\r\n\r\n const output = matches\r\n .map((m) => `${m.file}:${m.line}: ${m.content}`)\r\n .join(\"\\n\");\r\n\r\n const fileSet = new Set(matches.map((m) => m.file));\r\n const summary = `🔍 \"${args.pattern}\" → ${matches.length} 条命中 / ${fileSet.size} 个文件`;\r\n\r\n return {\r\n success: true,\r\n data: truncateOutput(output),\r\n summary,\r\n };\r\n } catch (err: unknown) {\r\n const message = err instanceof Error ? err.message : String(err);\r\n return {\r\n success: false,\r\n data: `内容搜索失败:${message}`,\r\n error: \"GREP_ERROR\",\r\n };\r\n }\r\n },\r\n};\r\n","// ---------------------------------------------------------------------------\r\n// ls 工具 — 列出目录内容\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { readdir, stat } from \"node:fs/promises\";\r\nimport { join, relative } from \"node:path\";\r\nimport { ToolKind, type AgentTool, type ToolContext, type ToolResult } from \"../types.js\";\r\nimport { resolvePath, truncateOutput } from \"../sandbox.js\";\r\n\r\n/** ls 工具的参数格式 */\r\nexport interface LsArgs {\r\n /** 目录路径,默认为 cwd */\r\n path?: string;\r\n /** 是否显示隐藏文件,默认 false */\r\n all?: boolean;\r\n}\r\n\r\n/** 目录项类型标记 */\r\ntype EntryType = \"FILE\" | \"DIR\" | \"LINK\";\r\n\r\n/**\r\n * ls 工具 — 列出目录内容。\r\n *\r\n * 功能:\r\n * - 显示文件/目录类型标记\r\n * - 显示文件大小\r\n * - 可选显示隐藏文件\r\n * - 自动跳过无权访问的目录\r\n */\r\nexport const lsTool: AgentTool<LsArgs> = {\r\n name: \"ls\",\r\n kind: ToolKind.Read,\r\n description:\r\n \"列出目录内容。显示条目类型(文件/目录/链接)和大小。可选择是否显示隐藏文件。\",\r\n parameters: {\r\n type: \"object\",\r\n properties: {\r\n path: {\r\n type: \"string\",\r\n description: \"目录路径(相对于当前工作目录或绝对路径),默认为当前目录\",\r\n },\r\n all: {\r\n type: \"boolean\",\r\n description: \"是否显示隐藏文件(以 . 开头的文件),默认 false\",\r\n },\r\n },\r\n required: [],\r\n additionalProperties: false,\r\n },\r\n\r\n async execute(args: LsArgs, ctx: ToolContext): Promise<ToolResult> {\r\n const dirPath = args.path ? resolvePath(args.path, ctx.cwd) : ctx.cwd;\r\n const showAll = args.all ?? false;\r\n\r\n try {\r\n const entries = await readdir(dirPath, { withFileTypes: true });\r\n const lines: string[] = [];\r\n\r\n // 按名称排序:目录在前,文件在后\r\n const sorted = [...entries].sort((a, b) => {\r\n if (a.isDirectory() !== b.isDirectory()) {\r\n return a.isDirectory() ? -1 : 1;\r\n }\r\n return a.name.localeCompare(b.name);\r\n });\r\n\r\n for (const entry of sorted) {\r\n // 过滤隐藏文件\r\n if (!showAll && entry.name.startsWith(\".\")) continue;\r\n\r\n const typeMark: EntryType = entry.isDirectory()\r\n ? \"DIR\"\r\n : entry.isSymbolicLink()\r\n ? \"LINK\"\r\n : \"FILE\";\r\n\r\n let sizeStr = \"\";\r\n if (typeMark === \"FILE\") {\r\n try {\r\n const fileStat = await stat(join(dirPath, entry.name));\r\n if (fileStat.size < 1024) {\r\n sizeStr = `${fileStat.size}B`;\r\n } else if (fileStat.size < 1024 * 1024) {\r\n sizeStr = `${(fileStat.size / 1024).toFixed(1)}KB`;\r\n } else {\r\n sizeStr = `${(fileStat.size / 1024 / 1024).toFixed(1)}MB`;\r\n }\r\n } catch {\r\n sizeStr = \"?\";\r\n }\r\n }\r\n\r\n const typeLabel = typeMark === \"DIR\" ? \"📁\" : typeMark === \"LINK\" ? \"🔗\" : \"📄\";\r\n lines.push(`${typeLabel} ${entry.name}${sizeStr ? ` (${sizeStr})` : \"\"}`);\r\n }\r\n\r\n if (lines.length === 0) {\r\n return { success: true, data: \"目录为空\", summary: `📂 ${relative(ctx.cwd, dirPath).replace(/\\\\/g, \"/\")}(空)` };\r\n }\r\n\r\n const relPath = relative(ctx.cwd, dirPath).replace(/\\\\/g, \"/\");\r\n return {\r\n success: true,\r\n data: truncateOutput(`目录:${dirPath}\\n${lines.join(\"\\n\")}`),\r\n summary: `📂 ${relPath}(${lines.length} 项)`,\r\n };\r\n } catch (err: unknown) {\r\n const message = err instanceof Error ? err.message : String(err);\r\n return {\r\n success: false,\r\n data: `列目录失败:${message}`,\r\n error: \"LS_ERROR\",\r\n };\r\n }\r\n },\r\n};\r\n","// ---------------------------------------------------------------------------\r\n// fetch 工具 — HTTP 请求\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { ToolKind, type AgentTool, type ToolContext, type ToolResult } from \"../types.js\";\r\nimport { truncateOutput } from \"../sandbox.js\";\r\n\r\n/** fetch 工具的参数格式 */\r\nexport interface FetchArgs {\r\n /** 请求 URL */\r\n url: string;\r\n /** 请求方法,默认 GET */\r\n method?: string;\r\n /** 请求头 */\r\n headers?: Record<string, string>;\r\n /** 请求体(POST/PUT 时使用) */\r\n body?: string;\r\n /** 响应最大长度(字符),默认 50000 */\r\n max_length?: number;\r\n}\r\n\r\n/**\r\n * fetch 工具 — 发起 HTTP 请求并返回响应内容。\r\n */\r\nexport const fetchTool: AgentTool<FetchArgs> = {\r\n name: \"fetch\",\r\n kind: ToolKind.Read,\r\n description:\r\n \"发起 HTTP 请求并返回响应内容。支持自定义方法和请求头。适用于获取网页内容、API 调用等场景。\",\r\n parameters: {\r\n type: \"object\",\r\n properties: {\r\n url: {\r\n type: \"string\",\r\n description: \"请求的 URL\",\r\n },\r\n method: {\r\n type: \"string\",\r\n description: \"HTTP 方法(GET、POST、PUT、DELETE 等),默认 GET\",\r\n },\r\n headers: {\r\n type: \"object\",\r\n description: \"请求头键值对\",\r\n additionalProperties: { type: \"string\" },\r\n },\r\n body: {\r\n type: \"string\",\r\n description: \"请求体内容(POST/PUT 时使用)\",\r\n },\r\n max_length: {\r\n type: \"number\",\r\n description: \"响应内容最大长度(字符),默认 50000\",\r\n },\r\n },\r\n required: [\"url\"],\r\n additionalProperties: false,\r\n },\r\n\r\n async execute(args: FetchArgs, ctx: ToolContext): Promise<ToolResult> {\r\n if (!args?.url || typeof args.url !== \"string\") {\r\n return { success: false, data: \"缺少必要参数 url\", error: \"INVALID_ARGS\" };\r\n }\r\n\r\n const method = (args.method ?? \"GET\").toUpperCase();\r\n const timeout = 30_000;\r\n const maxLength = args.max_length ?? 50_000;\r\n\r\n try {\r\n const fetchOptions: RequestInit = {\r\n method,\r\n headers: args.headers ?? {},\r\n signal: ctx.signal ?? AbortSignal.timeout(timeout),\r\n };\r\n\r\n if (args.body && (method === \"POST\" || method === \"PUT\" || method === \"PATCH\")) {\r\n (fetchOptions.headers as Record<string, string>)[\"Content-Type\"] ??= \"application/json\";\r\n fetchOptions.body = args.body;\r\n }\r\n\r\n const response = await fetch(args.url, fetchOptions);\r\n\r\n const contentType = response.headers.get(\"content-type\") ?? \"unknown\";\r\n const statusText = `${response.status} ${response.statusText}`;\r\n\r\n let body: string;\r\n try {\r\n body = await response.text();\r\n } catch {\r\n body = \"(无法读取响应体)\";\r\n }\r\n\r\n const truncatedBody = truncateOutput(body, maxLength);\r\n\r\n const header = `状态: ${statusText}\\n内容类型: ${contentType}`;\r\n const separator = body.length > 0 ? \"\\n---\\n\" : \"\";\r\n\r\n const urlPreview = args.url.length > 60\r\n ? args.url.slice(0, 57) + \"...\"\r\n : args.url;\r\n const summary = `🌐 ${method} ${urlPreview} → ${response.status}`;\r\n\r\n return {\r\n success: response.ok,\r\n data: `${header}${separator}${truncatedBody}`,\r\n summary,\r\n error: response.ok ? undefined : `HTTP_${response.status}`,\r\n };\r\n } catch (err: unknown) {\r\n if (err instanceof DOMException && err.name === \"AbortError\") {\r\n return { success: false, data: \"请求被中止\", error: \"ABORTED\" };\r\n }\r\n if (err instanceof TypeError && err.message.includes(\"fetch\")) {\r\n return { success: false, data: `网络错误:${err.message}`, error: \"NETWORK_ERROR\" };\r\n }\r\n const message = err instanceof Error ? err.message : String(err);\r\n return {\r\n success: false,\r\n data: `HTTP 请求失败:${message}`,\r\n error: \"FETCH_ERROR\",\r\n };\r\n }\r\n },\r\n};\r\n","// ---------------------------------------------------------------------------\r\n// 内置工具 — 注册所有内置工具的工厂函数\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { type AnyAgentTool, eraseTool } from \"../types.js\";\r\nimport { readFileTool } from \"./read-file.js\";\r\nimport { writeFileTool } from \"./write-file.js\";\r\nimport { editFileTool } from \"./edit-file.js\";\r\nimport { multiEditTool } from \"./multi-edit.js\";\r\nimport { deleteRangeTool } from \"./delete-range.js\";\r\nimport { bashTool } from \"./bash.js\";\r\nimport { globTool } from \"./glob.js\";\r\nimport { grepTool } from \"./grep.js\";\r\nimport { lsTool } from \"./ls.js\";\r\nimport { fetchTool } from \"./fetch.js\";\r\n\r\n/** 所有内置工具列表(类型已擦除,可直接用于注册) */\r\nexport const builtinTools: AnyAgentTool[] = [\r\n eraseTool(readFileTool),\r\n eraseTool(writeFileTool),\r\n eraseTool(editFileTool),\r\n eraseTool(multiEditTool),\r\n eraseTool(deleteRangeTool),\r\n eraseTool(bashTool),\r\n eraseTool(globTool),\r\n eraseTool(grepTool),\r\n eraseTool(lsTool),\r\n eraseTool(fetchTool),\r\n];\r\n\r\n/**\r\n * 获取所有内置工具映射表(按名称索引)。\r\n */\r\nexport function getBuiltinToolMap(): Map<string, AnyAgentTool> {\r\n return new Map(builtinTools.map((t) => [t.name, t]));\r\n}\r\n","// ---------------------------------------------------------------------------\r\n// 渐变色工具 — 用于占位符从左到右的渐变动画\r\n// ---------------------------------------------------------------------------\r\n\r\n/** RGB 色标元组 [R, G, B],每个值 0~255 */\r\nexport type ColorStop = [number, number, number];\r\n\r\n// ---- 预设渐变方案 ----\r\n\r\n/** 空闲占位符渐变:赛博朋克绿 → 青 → 紫 */\r\nexport const IDLE_GRADIENT_STOPS: ColorStop[] = [\r\n [0, 255, 65], // #00ff41\r\n [0, 255, 255], // #00ffff\r\n [189, 147, 249], // #bd93f9\r\n];\r\n\r\n/** 命令提示条渐变:浅柠黄 → 暖黄 → 橙色(从左到右流动) */\r\nexport const CMD_TIP_GRADIENT_STOPS: ColorStop[] = [\r\n [255, 245, 180], // #fff5b4 浅柠黄\r\n [255, 210, 80], // #ffd250 暖黄\r\n [255, 150, 50], // #ff9632 橙色\r\n];\r\n\r\n/** 流式占位符渐变:琥珀 → 焦糖 → 棕红 */\r\nexport const STREAMING_GRADIENT_STOPS: ColorStop[] = [\r\n [255, 191, 0], // #ffbf00 琥珀金\r\n [210, 140, 60], // #d28c3c 焦糖\r\n [175, 70, 40], // #af4628 棕红\r\n];\r\n\r\n// ---- 渐变动画配置 ----\r\n\r\n/** 动画参数:每帧相位增量 & 帧间隔(ms) */\r\nexport const GRADIENT_ANIMATION = {\r\n /** 空闲占位符动画:每帧相位步进(值越大流速越快) */\r\n idlePhaseStep: 0.06,\r\n /** 空闲占位符动画:帧间隔(ms) */\r\n idleInterval: 40,\r\n /** 流式占位符动画:每帧相位步进 */\r\n streamingPhaseStep: 0.05,\r\n /** 流式占位符动画:帧间隔(ms) */\r\n streamingInterval: 40,\r\n /** 命令提示条动画:每帧相位步进 */\r\n cmdTipPhaseStep: 0.05,\r\n /** 命令提示条动画:帧间隔(ms) */\r\n cmdTipInterval: 40,\r\n} as const;\r\n\r\n// ---- 渲染时跳过着色的字符 ----\r\n\r\n/** 不参与渐变着色的字符(空格、标点、emoji 等) */\r\nexport const SKIP_CHARS = new Set([\" \", \"~\", \".\", \"。\", \"!\", \"👇\"]);\r\n\r\n// ---- 核心算法 ----\r\n\r\n/** 根据 t(0~1) 在色标间线性插值,返回 hex 色值 */\r\nexport function lerpStopsToHex(t: number, stops: readonly ColorStop[]): string {\r\n const len = stops.length;\r\n const segment = t * (len - 1);\r\n const idx = Math.min(Math.floor(segment), len - 2);\r\n const frac = segment - idx;\r\n const stop = stops[idx];\r\n const nextStop = stops[idx + 1];\r\n if (!stop || !nextStop) return \"#808080\";\r\n const r = Math.round(stop[0] + (nextStop[0] - stop[0]) * frac);\r\n const g = Math.round(stop[1] + (nextStop[1] - stop[1]) * frac);\r\n const b = Math.round(stop[2] + (nextStop[2] - stop[2]) * frac);\r\n return `#${r.toString(16).padStart(2, \"0\")}${g.toString(16).padStart(2, \"0\")}${b.toString(16).padStart(2, \"0\")}`;\r\n}\r\n\r\n/** 对文本计算逐字渐变色数组,phase 控制色彩偏移位置(0~1) */\r\nexport function getGradientColors(\r\n text: string,\r\n phase: number,\r\n stops: readonly ColorStop[],\r\n): string[] {\r\n const len = text.length;\r\n if (len === 0) return [];\r\n return text.split(\"\").map((ch, i) => {\r\n if (SKIP_CHARS.has(ch)) return \"\";\r\n const t = ((i / (len - 1)) + phase) % 1;\r\n return lerpStopsToHex(t, stops);\r\n });\r\n}","import { Box, Text, useInput } from \"ink\";\r\nimport { useState, useCallback } from \"react\";\r\nimport type { Game } from \"../game/index.js\";\r\nimport { useDoubleCtrlC } from \"../ui/useDoubleCtrlC.js\";\r\n\r\ninterface GamePickerProps {\r\n games: Game[];\r\n onSelect: (game: Game) => void;\r\n onExit?: () => void;\r\n onBackToChat?: () => void;\r\n}\r\n\r\nexport function GamePicker({ games, onSelect, onExit, onBackToChat }: GamePickerProps) {\r\n const [selectedIndex, setSelectedIndex] = useState(0);\r\n\r\n const exitAction = onBackToChat ?? onExit ?? (() => process.exit(0));\r\n const { doubleCtrlC, handleCtrlC } = useDoubleCtrlC(exitAction);\r\n\r\n useInput(\r\n useCallback(\r\n (input, key) => {\r\n // Ctrl+C:双击退出\r\n if (input === \"c\" && key.ctrl) {\r\n handleCtrlC();\r\n return;\r\n }\r\n\r\n if (games.length === 0) return;\r\n if (key.upArrow || input === \"k\") {\r\n setSelectedIndex((prev) => (prev > 0 ? prev - 1 : games.length - 1));\r\n } else if (key.downArrow || input === \"j\") {\r\n setSelectedIndex((prev) => (prev < games.length - 1 ? prev + 1 : 0));\r\n } else if (key.return) {\r\n const game = games[selectedIndex];\r\n if (game) onSelect(game);\r\n } else if (key.escape || input === \"q\") {\r\n if (onBackToChat) onBackToChat();\r\n else onExit?.();\r\n }\r\n },\r\n [games, selectedIndex, onSelect, onExit, onBackToChat, handleCtrlC],\r\n ),\r\n );\r\n\r\n return (\r\n <Box flexDirection=\"column\">\r\n <Box marginBottom={1}>\r\n <Text bold color=\"#00ffff\">\r\n 🎮 游戏列表\r\n </Text>\r\n </Box>\r\n\r\n <Box flexDirection=\"column\">\r\n {games.map((game, index) => {\r\n const isSelected = index === selectedIndex;\r\n return (\r\n <Box key={game.id} flexDirection=\"row\">\r\n <Box width={3} flexShrink={0}>\r\n {isSelected ? (\r\n <Text bold color=\"#00ff41\">\r\n {\"▸ \"}\r\n </Text>\r\n ) : (\r\n <Text>{\" \"}</Text>\r\n )}\r\n </Box>\r\n <Box width={20} flexShrink={0}>\r\n <Text bold color={isSelected ? \"#00ff41\" : \"#ffffff\"}>\r\n {game.name}\r\n </Text>\r\n </Box>\r\n <Box>\r\n <Text color=\"#888888\">{game.description}</Text>\r\n </Box>\r\n </Box>\r\n );\r\n })}\r\n </Box>\r\n\r\n <Box marginTop={1}>\r\n <Text dimColor>\r\n {\" ↑/↓ 选择 Enter 启动 q 返回\"}\r\n </Text>\r\n </Box>\r\n\r\n {/* 双击 Ctrl+C 退出提示 */}\r\n {doubleCtrlC && (\r\n <Box marginTop={1}>\r\n <Text color=\"#ff1493\" bold>\r\n {\" ⚠ 再按一次 Ctrl+C 退出 dskcode\"}\r\n </Text>\r\n </Box>\r\n )}\r\n </Box>\r\n );\r\n}\r\n","export interface Game {\r\n /** 游戏唯一标识 */\r\n id: string;\r\n /** 游戏名称 */\r\n name: string;\r\n /** 简短描述 */\r\n description: string;\r\n /** 启动游戏 */\r\n play: () => Promise<void>;\r\n}\r\n\r\nconst registry = new Map<string, Game>();\r\n\r\nexport function registerGame(game: Game): void {\r\n registry.set(game.id, game);\r\n}\r\n\r\nexport function getGame(id: string): Game | undefined {\r\n return registry.get(id);\r\n}\r\n\r\nexport function listGames(): Game[] {\r\n return Array.from(registry.values());\r\n}\r\n","import { Box, Text, useInput, render } from \"ink\";\r\nimport { useState, useEffect, useRef, useCallback } from \"react\";\r\n\r\nconst GAME_WIDTH = 40;\r\nconst GAME_HEIGHT = 18;\r\nconst PADDLE_WIDTH = 9;\r\nconst BRICK_COLORS = [166, 214, 76, 69];\r\n\r\ninterface Vec2 {\r\n x: number;\r\n y: number;\r\n}\r\n\r\ninterface Brick {\r\n x: number;\r\n y: number;\r\n w: number;\r\n alive: boolean;\r\n}\r\n\r\n// ─── 10 个关卡定义 ──────────────────────────────────────────────\r\n\r\ninterface LevelDef {\r\n rows: number;\r\n cols: number;\r\n bw: number; // 砖块宽度(格数)\r\n desc: string;\r\n // 返回 true 表示该位置有砖块\r\n pattern: (col: number, row: number, rows: number, cols: number) => boolean;\r\n}\r\n\r\nconst LEVELS: LevelDef[] = [\r\n { rows: 4, cols: 8, bw: 3, desc: \"经典 4×8\", pattern: () => true },\r\n { rows: 3, cols: 8, bw: 3, desc: \"轻松 3 层\", pattern: () => true },\r\n { rows: 6, cols: 8, bw: 3, desc: \"厚墙 6 层\", pattern: () => true },\r\n { rows: 4, cols: 8, bw: 3, desc: \"棋盘格\", pattern: (c, r) => (c + r) % 2 === 0 },\r\n { rows: 4, cols: 8, bw: 3, desc: \"金字塔\", pattern: (c, r, _, tc) => c >= r && c < tc - r },\r\n { rows: 4, cols: 8, bw: 3, desc: \"交错排列\", pattern: (c, r) => r % 2 === 0 || (c >= 1 && c <= 6) },\r\n { rows: 4, cols: 8, bw: 3, desc: \"中空边框\", pattern: (c, r, tr, tc) => r === 0 || r === tr - 1 || c === 0 || c === tc - 1 },\r\n { rows: 4, cols: 10, bw: 2, desc: \"密集 10 列\", pattern: () => true },\r\n { rows: 7, cols: 8, bw: 3, desc: \"高墙 7 层\", pattern: () => true },\r\n { rows: 8, cols: 8, bw: 3, desc: \"满屏 8 层\", pattern: () => true },\r\n];\r\n\r\nfunction getLevel(level: number): LevelDef {\r\n return LEVELS[(level - 1) % LEVELS.length] as LevelDef;\r\n}\r\n\r\nfunction createBricks(level: number): Brick[] {\r\n const def = getLevel(level);\r\n const bricks: Brick[] = [];\r\n const gap = 2;\r\n const totalW = def.cols * def.bw + (def.cols - 1) * gap;\r\n const startX = Math.floor((GAME_WIDTH - totalW) / 2);\r\n const step = def.bw + gap;\r\n\r\n for (let row = 0; row < def.rows; row++) {\r\n for (let col = 0; col < def.cols; col++) {\r\n if (def.pattern(col, row, def.rows, def.cols)) {\r\n bricks.push({\r\n x: startX + col * step,\r\n y: 2 + row * 2,\r\n w: def.bw,\r\n alive: true,\r\n });\r\n }\r\n }\r\n }\r\n return bricks;\r\n}\r\n\r\n// ─── 游戏状态 ──────────────────────────────────────────────────\r\n\r\ninterface GameState {\r\n level: number;\r\n bricks: Brick[];\r\n paddleX: number;\r\n ball: Vec2;\r\n ballDir: Vec2;\r\n score: number;\r\n lives: number;\r\n gameOver: boolean;\r\n win: boolean;\r\n paused: boolean;\r\n}\r\n\r\nfunction createInitialState(level: number): GameState {\r\n const def = getLevel(level);\r\n const totalW = def.cols * def.bw + (def.cols - 1) * 2;\r\n const startX = Math.floor((GAME_WIDTH - totalW) / 2);\r\n return {\r\n level,\r\n bricks: createBricks(level),\r\n paddleX: Math.floor(GAME_WIDTH / 2) - Math.floor(PADDLE_WIDTH / 2),\r\n ball: { x: GAME_WIDTH / 2, y: GAME_HEIGHT - 3 },\r\n ballDir: { x: 1, y: -1 },\r\n score: 0,\r\n lives: 3,\r\n gameOver: false,\r\n win: false,\r\n paused: false,\r\n };\r\n}\r\n\r\n// ─── 物理更新 ──────────────────────────────────────────────────\r\n\r\nfunction update(state: GameState): void {\r\n if (state.paused || state.gameOver || state.win) return;\r\n\r\n state.ball.x += state.ballDir.x;\r\n state.ball.y += state.ballDir.y;\r\n\r\n if (state.ball.x <= 0) { state.ball.x = 0; state.ballDir.x = 1; }\r\n if (state.ball.x >= GAME_WIDTH - 1) { state.ball.x = GAME_WIDTH - 1; state.ballDir.x = -1; }\r\n if (state.ball.y <= 0) { state.ball.y = 0; state.ballDir.y = 1; }\r\n\r\n // 挡板碰撞\r\n if (\r\n state.ball.y === GAME_HEIGHT - 1 &&\r\n state.ball.x >= state.paddleX &&\r\n state.ball.x <= state.paddleX + PADDLE_WIDTH\r\n ) {\r\n state.ballDir.y = -1;\r\n const hitPos = (state.ball.x - state.paddleX) / PADDLE_WIDTH;\r\n state.ballDir.x = hitPos < 0.5 ? -1 : 1;\r\n }\r\n\r\n // 出界\r\n if (state.ball.y > GAME_HEIGHT) {\r\n state.lives--;\r\n if (state.lives <= 0) {\r\n state.gameOver = true;\r\n } else {\r\n state.ball = { x: GAME_WIDTH / 2, y: GAME_HEIGHT - 3 };\r\n state.ballDir = { x: 1, y: -1 };\r\n state.paddleX = Math.floor(GAME_WIDTH / 2) - Math.floor(PADDLE_WIDTH / 2);\r\n }\r\n }\r\n\r\n // 砖块碰撞\r\n const hitBrick = state.bricks.find((b) => {\r\n if (!b.alive) return false;\r\n return state.ball.x >= b.x && state.ball.x < b.x + b.w && state.ball.y >= b.y && state.ball.y < b.y + 1;\r\n });\r\n\r\n if (hitBrick) {\r\n hitBrick.alive = false;\r\n state.score += 10;\r\n state.ballDir.y = -state.ballDir.y;\r\n }\r\n\r\n if (state.bricks.every((b) => !b.alive)) state.win = true;\r\n}\r\n\r\n// ─── 画面渲染(含 ANSI 颜色) ─────────────────────────────────\r\n\r\nfunction buildBoard(state: GameState): string {\r\n const lines: string[] = [];\r\n\r\n function brickColorIndex(x: number, y: number): number | undefined {\r\n const row = Math.floor((y - 2) / 2);\r\n if (row >= 0) {\r\n const b = state.bricks.find((br) => br.y === y && br.alive && x >= br.x && x < br.x + br.w);\r\n if (b) return row % BRICK_COLORS.length;\r\n }\r\n return undefined;\r\n }\r\n\r\n for (let y = 0; y < GAME_HEIGHT; y++) {\r\n let line = \"\";\r\n for (let x = 0; x < GAME_WIDTH; x++) {\r\n const isBall = state.ball.x === x && state.ball.y === y;\r\n const isPaddle = y === GAME_HEIGHT - 1 && x >= state.paddleX && x < state.paddleX + PADDLE_WIDTH;\r\n const brickRow = brickColorIndex(x, y);\r\n\r\n if (isBall) {\r\n line += \"\\x1b[97m●\\x1b[0m\";\r\n } else if (isPaddle) {\r\n line += \"\\x1b[94m▄\\x1b[0m\";\r\n } else if (brickRow !== undefined) {\r\n line += `\\x1b[38;5;${BRICK_COLORS[brickRow] as number}m▀\\x1b[0m`;\r\n } else {\r\n line += \" \";\r\n }\r\n }\r\n lines.push(line);\r\n }\r\n\r\n return lines.map((l) => `│${l}│`).join(\"\\n\");\r\n}\r\n\r\n// ─── Ink 组件 ──────────────────────────────────────────────────\r\n\r\ninterface BrickBreakerGameProps {\r\n onExit: () => void;\r\n}\r\n\r\nfunction BrickBreakerGame({ onExit: _onExit }: BrickBreakerGameProps) {\r\n const [initialLevel, setInitialLevel] = useState(1);\r\n const [selectingLevel, setSelectingLevel] = useState(true);\r\n const stateRef = useRef<GameState>(createInitialState(initialLevel));\r\n const [tick, setTick] = useState(0);\r\n const onExitRef = useRef(_onExit);\r\n onExitRef.current = _onExit;\r\n\r\n useEffect(() => {\r\n if (selectingLevel) return;\r\n const interval = setInterval(() => {\r\n update(stateRef.current);\r\n setTick((t) => t + 1);\r\n }, 80);\r\n return () => clearInterval(interval);\r\n }, [selectingLevel]);\r\n\r\n // 重新开始(保留当前关卡)\r\n const restart = useCallback((level?: number) => {\r\n const lv = level ?? stateRef.current.level;\r\n stateRef.current = createInitialState(lv);\r\n setInitialLevel(lv);\r\n setSelectingLevel(false);\r\n setTick(0);\r\n }, []);\r\n\r\n // 选关\r\n const startLevelSelect = useCallback(() => {\r\n setSelectingLevel(true);\r\n }, []);\r\n\r\n useInput(\r\n useCallback((input, key) => {\r\n const s = stateRef.current;\r\n\r\n // 选关模式\r\n if (selectingLevel) {\r\n if (input >= \"1\" && input <= \"9\") {\r\n restart(Number(input));\r\n } else if (input === \"0\") {\r\n restart(10);\r\n } else if (key.escape || input === \"q\") {\r\n onExitRef.current();\r\n }\r\n return;\r\n }\r\n\r\n if (key.leftArrow) {\r\n s.paddleX = Math.max(0, s.paddleX - 1);\r\n setTick((t) => t + 1);\r\n } else if (key.rightArrow) {\r\n s.paddleX = Math.min(GAME_WIDTH - PADDLE_WIDTH, s.paddleX + 1);\r\n setTick((t) => t + 1);\r\n } else if (input === \"p\" || input === \" \") {\r\n s.paused = !s.paused;\r\n } else if (input === \"r\") {\r\n if (s.gameOver || s.win) restart();\r\n } else if (input === \"l\") {\r\n if (s.gameOver || s.win) startLevelSelect();\r\n } else if (input === \"q\" || key.escape) {\r\n onExitRef.current();\r\n }\r\n }, [selectingLevel, restart, startLevelSelect]),\r\n );\r\n\r\n const s = stateRef.current;\r\n const aliveCount = s.bricks.filter((b) => b.alive).length;\r\n const board = buildBoard(s);\r\n const def = getLevel(s.level);\r\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\r\n void tick;\r\n\r\n return (\r\n <Box flexDirection=\"column\">\r\n {/* 状态栏 */}\r\n <Box flexDirection=\"row\">\r\n <Box width={20}>\r\n <Text>\r\n 关卡 {s.level}: <Text color=\"cyan\">{def.desc}</Text>\r\n </Text>\r\n </Box>\r\n <Box width={12}>\r\n <Text>\r\n 分数: <Text color=\"yellow\">{String(s.score).padStart(3, \"0\")}</Text>\r\n </Text>\r\n </Box>\r\n <Box width={12}>\r\n <Text>\r\n 生命: <Text color=\"red\">{\"♥\".repeat(Math.max(0, s.lives))}</Text>\r\n </Text>\r\n </Box>\r\n <Box width={10}>\r\n <Text>\r\n 砖块: <Text color=\"cyan\">{aliveCount}</Text>\r\n </Text>\r\n </Box>\r\n <Box>\r\n <Text color={s.paused ? \"gray\" : \"green\"}>\r\n [{s.paused ? \"暂停\" : \"运行中\"}]\r\n </Text>\r\n </Box>\r\n </Box>\r\n\r\n {/* 游戏画面 */}\r\n <Box flexDirection=\"column\">\r\n <Text>┌{\"─\".repeat(GAME_WIDTH)}┐</Text>\r\n <Text>{selectingLevel ? \" \\x1b[90m选择关卡后开始...\\x1b[0m\" : board}</Text>\r\n <Text>└{\"─\".repeat(GAME_WIDTH)}┘</Text>\r\n </Box>\r\n\r\n {/* 选关界面 */}\r\n {selectingLevel && (\r\n <Box marginTop={1} flexDirection=\"column\">\r\n <Text bold color=\"yellow\">选择关卡</Text>\r\n <Box flexDirection=\"row\" flexWrap=\"wrap\">\r\n {LEVELS.map((lv, i) => (\r\n <Box key={i} width={22}>\r\n <Text>\r\n <Text color={initialLevel === i + 1 ? \"green\" : \"white\"}>\r\n {i + 1 === 10 ? \"0\" : String(i + 1)}\r\n </Text>\r\n . {lv.desc} ({lv.rows}×{lv.cols})\r\n </Text>\r\n </Box>\r\n ))}\r\n </Box>\r\n <Box marginTop={1}>\r\n <Text dimColor>按数字选关 q 退出</Text>\r\n </Box>\r\n </Box>\r\n )}\r\n\r\n {/* 结束/通关信息 */}\r\n {!selectingLevel && (s.gameOver || s.win) && (\r\n <Box marginTop={1}>\r\n <Text bold color={s.gameOver ? \"red\" : \"green\"}>\r\n {s.gameOver ? \"游戏结束!\" : \"恭喜通关!\"}\r\n </Text>\r\n <Text>\r\n {\" 分数: \"}<Text color=\"yellow\">{s.score}</Text>\r\n </Text>\r\n </Box>\r\n )}\r\n\r\n {/* 操作提示 */}\r\n {!selectingLevel && (\r\n <Box marginTop={1}>\r\n {s.gameOver || s.win ? (\r\n <Text dimColor>\r\n {\"← → 移动 r 重开 l 选关 q 退出\"}\r\n </Text>\r\n ) : (\r\n <Text dimColor>\r\n {\"← → 移动 p 暂停 q 退出\"}\r\n </Text>\r\n )}\r\n </Box>\r\n )}\r\n </Box>\r\n );\r\n}\r\n\r\n// ─── 导出 ──────────────────────────────────────────────────────\r\n\r\nexport default {\r\n id: \"brick-breaker\",\r\n name: \"Brick Breaker\",\r\n description: \"经典打砖块游戏,10 个关卡可选!\",\r\n play: async () => {\r\n await new Promise<void>((resolve) => {\r\n const { unmount } = render(\r\n <BrickBreakerGame onExit={() => { unmount(); resolve(); }} />,\r\n );\r\n });\r\n },\r\n};\r\n","import { Box, Text, useInput, render } from \"ink\";\r\nimport { useState, useEffect, useRef, useCallback } from \"react\";\r\n\r\nconst GAME_W = 66;\r\nconst GAME_H = 20; // 6 行分数 + 14 行掉落单词\r\nconst SCORE_H = 6;\r\nconst WORD_H = 14;\r\nconst MAX_WORDS = 10;\r\nconst CYBER_PALETTE = [\"#00ffff\", \"#ff00ff\", \"#00ff41\", \"#ff1493\", \"#8b00ff\"];\r\n\r\n// 3×5 像素大号数字(每个像素 = ██,每数字 6 列)\r\nconst DIGIT_ART: Record<string, string[]> = {\r\n \"0\": [\" ██████ \", \"██ ██\", \"██ ██\", \"██ ██\", \" ██████ \"],\r\n \"1\": [\" ██ \", \" ████ \", \" ██ \", \" ██ \", \" ██████ \"],\r\n \"2\": [\" ██████ \", \" ██ \", \" ██████ \", \" ██ \", \" ██████ \"],\r\n \"3\": [\" ██████ \", \" ██ \", \" ██████ \", \" ██ \", \" ██████ \"],\r\n \"4\": [\"██ ██\", \"██ ██\", \" ██████ \", \" ██ \", \" ██ \"],\r\n \"5\": [\" ██████ \", \" ██ \", \" ██████ \", \" ██ \", \" ██████ \"],\r\n \"6\": [\" ██████ \", \" ██ \", \" ██████ \", \" ██ ██\", \" ██████ \"],\r\n \"7\": [\" ██████ \", \" ██ \", \" ██ \", \" ██ \", \" ██ \"],\r\n \"8\": [\" ██████ \", \"██ ██\", \" ██████ \", \"██ ██\", \" ██████ \"],\r\n \"9\": [\" ██████ \", \"██ ██\", \" ██████ \", \" ██ \", \" ██████ \"],\r\n};\r\n\r\nfunction buildScoreLines(scoreStr: string): string[] {\r\n const lines = [\"\", \"\", \"\", \"\", \"\"];\r\n for (const ch of scoreStr) {\r\n const art = DIGIT_ART[ch] ?? DIGIT_ART[\"0\"]!;\r\n for (let r = 0; r < 5; r++) {\r\n lines[r] += \" \" + (art[r] ?? \"\");\r\n }\r\n }\r\n // 左右各留 6 格间距\r\n const pad = 6;\r\n return lines.map((l) => \" \".repeat(pad) + l);\r\n}\r\n\r\nfunction hexToRgb(hex: string): string {\r\n const r = parseInt(hex.slice(1, 3), 16);\r\n const g = parseInt(hex.slice(3, 5), 16);\r\n const b = parseInt(hex.slice(5, 7), 16);\r\n return `${r};${g};${b}`;\r\n}\r\n\r\n// ─── 200 个常见单词 ────────────────────────────────────────────\r\n\r\nconst WORDS_BANK = [\r\n // ── 框架 & 库 ──\r\n \"react\", \"vue\", \"next\", \"node\", \"axios\", \"express\", \"lodash\", \"jquery\",\r\n \"webpack\", \"vite\", \"babel\", \"eslint\", \"prettier\", \"tailwind\", \"bootstrap\",\r\n \"sass\", \"less\", \"postcss\", \"redux\", \"pinia\", \"vuex\", \"router\",\r\n \"nestjs\", \"socket\", \"graphql\", \"rest\", \"grpc\", \"prisma\", \"typeorm\",\r\n\r\n // ── 前端 ──\r\n \"html\", \"css\", \"jsx\", \"tsx\", \"dom\", \"spa\", \"ssr\", \"csr\", \"pwa\",\r\n \"component\", \"prop\", \"hook\", \"composable\", \"directive\", \"filter\", \"mixin\",\r\n \"template\", \"render\", \"virtual\", \"diff\", \"patch\", \"hydration\",\r\n \"responsive\", \"flexbox\", \"grid\", \"animation\", \"transition\",\r\n\r\n // ── 后端 ──\r\n \"api\", \"route\", \"middleware\", \"controller\", \"service\", \"module\",\r\n \"dto\", \"entity\", \"schema\", \"migration\", \"seeder\", \"factory\",\r\n \"auth\", \"jwt\", \"oauth\", \"session\", \"cookie\", \"token\", \"cors\",\r\n \"cache\", \"redis\", \"mq\", \"rabbit\", \"kafka\", \"nats\",\r\n\r\n // ── 数据库 ──\r\n \"sql\", \"mysql\", \"pg\", \"sqlite\", \"mongo\", \"redis\", \"orm\",\r\n \"table\", \"index\", \"query\", \"join\", \"union\", \"group\", \"order\",\r\n \"where\", \"having\", \"limit\", \"offset\", \"insert\", \"update\", \"delete\",\r\n\r\n // ── DevOps ──\r\n \"docker\", \"nginx\", \"linux\", \"bash\", \"shell\", \"yaml\", \"toml\",\r\n \"ci\", \"cd\", \"deploy\", \"rollback\", \"release\", \"build\", \"test\",\r\n \"lint\", \"format\", \"stage\", \"commit\", \"branch\", \"merge\", \"rebase\",\r\n\r\n // ── 数据结构 & 算法 ──\r\n \"array\", \"stack\", \"queue\", \"tree\", \"graph\", \"list\", \"map\", \"set\",\r\n \"sort\", \"search\", \"filter\", \"reduce\", \"map\", \"async\", \"await\",\r\n \"promise\", \"callback\", \"closure\", \"proxy\", \"reflect\", \"decorator\",\r\n\r\n // ── 常用操作 ──\r\n \"create\", \"read\", \"update\", \"delete\", \"crud\", \"parse\", \"stringify\",\r\n \"encode\", \"decode\", \"transform\", \"validate\", \"format\", \"parse\",\r\n \"upload\", \"download\", \"export\", \"import\", \"backup\", \"restore\",\r\n\r\n // ── 类型 & 变量 ──\r\n \"string\", \"number\", \"boolean\", \"object\", \"array\", \"tuple\", \"enum\",\r\n \"interface\", \"type\", \"class\", \"function\", \"method\", \"property\",\r\n \"public\", \"private\", \"static\", \"readonly\", \"optional\", \"abstract\",\r\n \"const\", \"let\", \"var\", \"void\", \"null\", \"undef\", \"never\", \"any\",\r\n\r\n // ── 补充 ──\r\n \"config\", \"logger\", \"monitor\", \"metric\", \"alert\", \"webhook\",\r\n \"endpoint\", \"payload\", \"header\", \"status\", \"timeout\", \"retry\",\r\n \"fallback\", \"circuit\", \"breaker\", \"throttle\", \"debounce\",\r\n \"scroll\", \"resize\", \"click\", \"hover\", \"focus\", \"blur\",\r\n];\r\n\r\nfunction randomWord(used: Set<string>): string {\r\n let w: string;\r\n do { w = WORDS_BANK[Math.floor(Math.random() * WORDS_BANK.length)] as string; }\r\n while (used.has(w));\r\n return w;\r\n}\r\n\r\n// ─── 游戏状态 ──────────────────────────────────────────────────\r\n\r\ninterface DropWord {\r\n text: string;\r\n row: number;\r\n col: number;\r\n}\r\n\r\ninterface GameState {\r\n words: DropWord[];\r\n score: number;\r\n lives: number;\r\n speed: number;\r\n spawnTimer: number;\r\n gameOver: boolean;\r\n paused: boolean;\r\n typed: string;\r\n target: string | null;\r\n combo: number;\r\n message: string;\r\n messageTimer: number;\r\n usedWords: Set<string>;\r\n}\r\n\r\nfunction createInitialState(): GameState {\r\n return {\r\n words: [],\r\n score: 0,\r\n lives: 3,\r\n speed: 0.3,\r\n spawnTimer: 0,\r\n gameOver: false,\r\n paused: false,\r\n typed: \"\",\r\n target: null,\r\n combo: 0,\r\n message: \"\",\r\n messageTimer: 0,\r\n usedWords: new Set(),\r\n };\r\n}\r\n\r\nfunction pickTarget(s: GameState): string | null {\r\n // 选取最靠左的存活单词作为目标\r\n let best: DropWord | null = null;\r\n for (const w of s.words) {\r\n if (!best || w.col < best.col) best = w;\r\n }\r\n return best?.text ?? null;\r\n}\r\n\r\n// ─── 物理更新 ──────────────────────────────────────────────────\r\n\r\nfunction update(s: GameState): void {\r\n if (s.paused || s.gameOver) return;\r\n\r\n // 生成新单词\r\n s.spawnTimer++;\r\n if (s.spawnTimer >= Math.max(20, 50 - Math.floor(s.speed * 15))) {\r\n s.spawnTimer = 0;\r\n if (s.words.length < MAX_WORDS) {\r\n const used = new Set(s.usedWords);\r\n for (const w of s.words) used.add(w.text);\r\n const text = randomWord(used);\r\n // 找空白行\r\n const usedRows = new Set(s.words.map((w) => w.row));\r\n let row = -1;\r\n for (let r = SCORE_H; r < GAME_H; r++) {\r\n if (!usedRows.has(r)) { row = r; break; }\r\n }\r\n if (row >= 0) {\r\n s.words.push({ text, row, col: GAME_W - 1 });\r\n s.usedWords.add(text);\r\n }\r\n }\r\n }\r\n\r\n // 移动单词\r\n for (const w of s.words) {\r\n w.col -= s.speed;\r\n }\r\n\r\n // 移除超出左边的 + 更新目标\r\n const before = s.words.length;\r\n s.words = s.words.filter((w) => w.col > -w.text.length);\r\n const removed = before - s.words.length;\r\n\r\n // 单词漏掉了 → 扣命\r\n if (removed > 0) {\r\n s.lives -= removed;\r\n s.typed = \"\";\r\n s.target = pickTarget(s);\r\n if (s.lives <= 0) {\r\n s.gameOver = true;\r\n s.words = [];\r\n }\r\n }\r\n\r\n // 更新目标\r\n s.target = pickTarget(s);\r\n\r\n // 消息计时\r\n if (s.messageTimer > 0) {\r\n s.messageTimer--;\r\n if (s.messageTimer <= 0) s.message = \"\";\r\n }\r\n}\r\n\r\n// ─── 画面渲染 ──────────────────────────────────────────────────\r\n\r\nfunction buildGameView(s: GameState, scoreLines: string[], scoreColor: string, message: string): string[] {\r\n const rows: string[] = [];\r\n\r\n for (let y = 0; y < GAME_H; y++) {\r\n let line = \"\";\r\n\r\n // 分数区域(前 6 行)\r\n if (y < SCORE_H) {\r\n if (y < 5) {\r\n // 数字行 — 先补足宽度再套赛博朋克色\r\n const raw = (scoreLines[y] ?? \"\").padEnd(GAME_W);\r\n line = `\\x1b[38;2;${hexToRgb(scoreColor)}m${raw}\\x1b[0m`;\r\n } else if (y === 5) {\r\n // 第 6 行:连击 / 暂停\r\n if (s.combo >= 3) {\r\n const comboText = `${s.combo}连击!`;\r\n const pad = Math.floor((GAME_W - comboText.length) / 2);\r\n const comboColor = CYBER_PALETTE[s.combo % CYBER_PALETTE.length] as string;\r\n const raw = \" \".repeat(pad) + comboText + \" \".repeat(GAME_W - pad - comboText.length);\r\n line = `\\x1b[38;2;${hexToRgb(comboColor)}m${raw}\\x1b[0m`;\r\n } else if (s.paused) {\r\n const pauseText = \"暂停\";\r\n const pad = Math.floor((GAME_W - pauseText.length) / 2);\r\n line = \" \".repeat(pad) + pauseText;\r\n line = line.padEnd(GAME_W);\r\n }\r\n }\r\n // 补全到 GAME_W 宽度\r\n line = line.padEnd(GAME_W);\r\n } else {\r\n // 单词区域\r\n // 第 1 行显示消息(如果有)\r\n if (y === SCORE_H && message) {\r\n const pad = Math.floor((GAME_W - message.length) / 2);\r\n const raw = \" \".repeat(pad) + message + \" \".repeat(GAME_W - pad - message.length);\r\n const msgColor = CYBER_PALETTE[Math.floor(Math.random() * CYBER_PALETTE.length)] as string;\r\n line = `\\x1b[38;2;${hexToRgb(msgColor)}m${raw}\\x1b[0m`;\r\n } else {\r\n for (let x = 0; x < GAME_W; x++) {\r\n const word = s.words.find((w) => {\r\n const charIdx = x - Math.floor(w.col);\r\n return charIdx >= 0 && charIdx < w.text.length && w.row === y;\r\n });\r\n if (word) {\r\n const charIdx = x - Math.floor(word.col);\r\n const ch = word.text[charIdx] as string;\r\n const isTarget = word.text === s.target;\r\n const typedIdx = s.target === word.text ? s.typed.length : 0;\r\n const isTyped = isTarget && charIdx < typedIdx;\r\n if (isTarget) {\r\n line += isTyped ? `\\x1b[92m${ch}\\x1b[0m` : `\\x1b[97m${ch}\\x1b[0m`;\r\n } else {\r\n line += `\\x1b[90m${ch}\\x1b[0m`;\r\n }\r\n } else {\r\n line += \" \";\r\n }\r\n }\r\n }\r\n }\r\n\r\n rows.push(line);\r\n }\r\n return rows;\r\n}\r\n\r\n// ─── Ink 组件 ──────────────────────────────────────────────────\r\n\r\ninterface CoderCheckProps {\r\n onExit: () => void;\r\n}\r\n\r\nfunction CoderCheck({ onExit: _onExit }: CoderCheckProps) {\r\n const stateRef = useRef<GameState>(createInitialState());\r\n const [tick, setTick] = useState(0);\r\n const [colorOffset, setColorOffset] = useState(0);\r\n const onExitRef = useRef(_onExit);\r\n onExitRef.current = _onExit;\r\n\r\n // 赛博朋克颜色动画\r\n useEffect(() => {\r\n const timer = setInterval(() => {\r\n setColorOffset((prev) => (prev + 1) % CYBER_PALETTE.length);\r\n }, 400);\r\n return () => clearInterval(timer);\r\n }, []);\r\n\r\n // 游戏循环\r\n useEffect(() => {\r\n const interval = setInterval(() => {\r\n update(stateRef.current);\r\n setTick((t) => t + 1);\r\n }, 60);\r\n return () => clearInterval(interval);\r\n }, []);\r\n\r\n useInput(\r\n useCallback((input, key) => {\r\n const s = stateRef.current;\r\n if (s.gameOver) {\r\n if (input === \"r\") {\r\n stateRef.current = createInitialState();\r\n setTick(0);\r\n } else if (input === \"q\" || key.escape) {\r\n onExitRef.current();\r\n }\r\n return;\r\n }\r\n\r\n if ((input === \"p\" && key.ctrl) || input === \" \") {\r\n s.paused = !s.paused;\r\n return;\r\n }\r\n if ((input === \"q\" && key.ctrl) || key.escape) {\r\n onExitRef.current();\r\n return;\r\n }\r\n\r\n if (s.paused) return;\r\n\r\n // 打字输入\r\n if (input.length === 1 && input >= \"a\" && input <= \"z\") {\r\n // 选取目标\r\n if (!s.target) {\r\n s.target = pickTarget(s);\r\n }\r\n if (s.target) {\r\n const nextChar = s.target[s.typed.length];\r\n if (nextChar === input) {\r\n s.typed += input;\r\n // 检查是否完成\r\n if (s.typed === s.target) {\r\n // 移除该单词\r\n s.words = s.words.filter((w) => w.text !== s.target);\r\n s.combo++;\r\n // 连击 >= 3 时以当前分数翻倍奖励\r\n const prevScore = s.score;\r\n const basePts = s.target.length;\r\n const comboBonus = s.combo >= 3 ? s.score : 0;\r\n s.score += basePts + comboBonus;\r\n s.speed = s.speed + 0.02;\r\n\r\n // 每 500 分提速\r\n const prevMilestone = Math.floor(prevScore / 500);\r\n const newMilestone = Math.floor(s.score / 500);\r\n if (newMilestone > prevMilestone) {\r\n s.speed += 0.15;\r\n }\r\n\r\n // 超过 99999 分提示通关\r\n if (s.score >= 100000 && prevScore < 100000) {\r\n s.message = \"恭喜通关! 难度最大化!\";\r\n s.messageTimer = 100;\r\n }\r\n s.typed = \"\";\r\n s.target = pickTarget(s);\r\n }\r\n } else {\r\n // 打错字符,连击中断\r\n s.combo = 0;\r\n }\r\n }\r\n }\r\n }, []),\r\n );\r\n\r\n const s = stateRef.current;\r\n const scoreColor = CYBER_PALETTE[colorOffset] as string;\r\n const scoreStr = String(s.score).padStart(5, \"0\");\r\n const scoreLines = buildScoreLines(scoreStr);\r\n const view = buildGameView(s, scoreLines, scoreColor, s.message);\r\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\r\n void tick;\r\n\r\n return (\r\n <Box flexDirection=\"column\" paddingX={1}>\r\n {/* 顶栏:生命 + 速度 */}\r\n <Box flexDirection=\"row\">\r\n <Text>\r\n 生命 <Text color=\"red\">{\"♥\".repeat(Math.max(0, s.lives))}</Text>\r\n {\" \"}速度 <Text color=\"cyan\">Lv.{Math.floor(s.speed * 10)}</Text>\r\n </Text>\r\n </Box>\r\n\r\n {/* 目标单词提示 */}\r\n {!s.gameOver && s.target && (\r\n <Box marginTop={1}>\r\n <Text>\r\n 打字: <Text color=\"green\">{s.typed}</Text>\r\n <Text color=\"white\">{s.target.slice(s.typed.length)}</Text>\r\n </Text>\r\n </Box>\r\n )}\r\n\r\n {/* 游戏画面(含分数) */}\r\n <Box flexDirection=\"column\" marginTop={1}>\r\n <Text>┌{\"─\".repeat(GAME_W)}┐</Text>\r\n {view.map((row, i) => (\r\n <Text key={i}>{`│${row}│`}</Text>\r\n ))}\r\n <Text>└{\"─\".repeat(GAME_W)}┘</Text>\r\n </Box>\r\n\r\n {/* 游戏结束 */}\r\n {s.gameOver && (\r\n <Box marginTop={1}>\r\n <Text bold color=\"red\">\r\n 游戏结束!\r\n </Text>\r\n <Text>\r\n {\" 得分: \"}<Text color=\"yellow\">{s.score}</Text>\r\n {\" r 重开 q 退出\"}\r\n </Text>\r\n </Box>\r\n )}\r\n\r\n {/* 操作提示 */}\r\n <Box marginTop={1}>\r\n <Text dimColor>\r\n {\"打字消除单词 空格/Ctrl+P暂停 Ctrl+Q退出\"}\r\n </Text>\r\n </Box>\r\n </Box>\r\n );\r\n}\r\n\r\n// ─── 导出 ──────────────────────────────────────────────────────\r\n\r\nexport default {\r\n id: \"coder-check\",\r\n name: \"Coder Check\",\r\n description: \"极速打字游戏,输入单词消除它们!\",\r\n play: async () => {\r\n await new Promise<void>((resolve) => {\r\n const { unmount } = render(\r\n <CoderCheck onExit={() => { unmount(); resolve(); }} />,\r\n );\r\n });\r\n },\r\n};\r\n","import type { Direction, GameState, GameConfig, Food, Point } from './types.js';\r\n\r\n// ─── 辅助函数 ───────────────────────────────────────────\r\n\r\nfunction isOpposite(a: Direction, b: Direction): boolean {\r\n return (\r\n (a === 'UP' && b === 'DOWN') ||\r\n (a === 'DOWN' && b === 'UP') ||\r\n (a === 'LEFT' && b === 'RIGHT') ||\r\n (a === 'RIGHT' && b === 'LEFT')\r\n );\r\n}\r\n\r\nfunction samePoint(a: Point, b: Point): boolean {\r\n return a.x === b.x && a.y === b.y;\r\n}\r\n\r\nfunction movePoint(p: Point, dir: Direction): Point {\r\n switch (dir) {\r\n case 'UP': return { x: p.x, y: p.y - 1 };\r\n case 'DOWN': return { x: p.x, y: p.y + 1 };\r\n case 'LEFT': return { x: p.x - 1, y: p.y };\r\n case 'RIGHT': return { x: p.x + 1, y: p.y };\r\n }\r\n}\r\n\r\nfunction wrapPoint(p: Point, config: GameConfig): Point {\r\n return {\r\n x: ((p.x % config.width) + config.width) % config.width,\r\n y: ((p.y % config.height) + config.height) % config.height,\r\n };\r\n}\r\n\r\n// ─── 食物生成 ───────────────────────────────────────────\r\n\r\n/**\r\n * 在蛇身 + 指定排除位置之外的随机空位生成食物。\r\n * 排除位置用于防止普通食物和特殊食物重叠。\r\n */\r\nfunction generateFood(\r\n snake: Point[],\r\n config: GameConfig,\r\n type: 'normal' | 'special',\r\n excludePositions: Point[] = [],\r\n): Food | null {\r\n const occupied = new Set(snake.map(p => `${p.x},${p.y}`));\r\n // 排除位置也加入已占用集合(防止与另一种食物重叠)\r\n for (const p of excludePositions) {\r\n occupied.add(`${p.x},${p.y}`);\r\n }\r\n\r\n const totalCells = config.width * config.height;\r\n if (occupied.size >= totalCells) return null;\r\n\r\n let pos: Point;\r\n do {\r\n pos = {\r\n x: Math.floor(Math.random() * config.width),\r\n y: Math.floor(Math.random() * config.height),\r\n };\r\n } while (occupied.has(`${pos.x},${pos.y}`));\r\n\r\n return {\r\n position: pos,\r\n type,\r\n ...(type === 'special' ? { spawnedAt: Date.now() } : {}),\r\n };\r\n}\r\n\r\n// ─── 状态工厂 ───────────────────────────────────────────\r\n\r\n/** 创建初始游戏状态 */\r\nexport function createInitialState(config: GameConfig): GameState {\r\n const startX = Math.floor(config.width / 2);\r\n const startY = Math.floor(config.height / 2);\r\n\r\n const snake: Point[] = [\r\n { x: startX, y: startY },\r\n { x: startX - 1, y: startY },\r\n { x: startX - 2, y: startY },\r\n ];\r\n\r\n const food = generateFood(snake, config, 'normal')!;\r\n\r\n return {\r\n snake,\r\n direction: 'RIGHT',\r\n nextDirection: 'RIGHT',\r\n food,\r\n specialFood: null,\r\n score: 0,\r\n speedLevel: 1,\r\n isGameOver: false,\r\n isPaused: false,\r\n wallWrap: Math.random() < 0.5,\r\n tickInterval: config.initialSpeed,\r\n };\r\n}\r\n\r\n// ─── 核心逻辑:每帧推进 ─────────────────────────────────\r\n\r\nexport function tick(state: GameState, config: GameConfig): void {\r\n // 1. 应用缓冲方向(防止同帧内 180° 掉头)\r\n state.direction = state.nextDirection;\r\n\r\n // 2. 计算新蛇头位置\r\n const head = state.snake[0]!;\r\n let newHead = movePoint(head, state.direction);\r\n\r\n // 3. 边界处理\r\n const outOfBounds =\r\n newHead.x < 0 ||\r\n newHead.x >= config.width ||\r\n newHead.y < 0 ||\r\n newHead.y >= config.height;\r\n\r\n if (outOfBounds) {\r\n if (state.wallWrap) {\r\n newHead = wrapPoint(newHead, config);\r\n } else {\r\n state.isGameOver = true;\r\n return;\r\n }\r\n }\r\n\r\n // 4. 判断本帧是否会吃到食物(影响自碰撞检测的判定范围)\r\n const ateNormal = samePoint(newHead, state.food.position);\r\n const ateSpecial = state.specialFood !== null && samePoint(newHead, state.specialFood.position);\r\n const willEat = ateNormal || ateSpecial;\r\n\r\n // 自碰撞检测:不吃食物时排除蛇尾(因为蛇尾会移除)\r\n const bodyToCheck = willEat ? state.snake : state.snake.slice(0, -1);\r\n for (const segment of bodyToCheck) {\r\n if (samePoint(newHead, segment)) {\r\n state.isGameOver = true;\r\n return;\r\n }\r\n }\r\n\r\n // 5. 移动蛇\r\n state.snake.unshift(newHead);\r\n\r\n // 6. 处理进食\r\n if (ateNormal) {\r\n state.score += 1;\r\n updateSpeed(state, config);\r\n\r\n // 生成新普通食物时排除特殊食物的位置\r\n const exclude: Point[] = state.specialFood ? [state.specialFood.position] : [];\r\n const newFood = generateFood(state.snake, config, 'normal', exclude);\r\n if (newFood) {\r\n state.food = newFood;\r\n } else {\r\n // 没有空位了(蛇填满了棋盘),宣告胜利\r\n state.isGameOver = true;\r\n return;\r\n }\r\n\r\n // 25% 概率生成特殊食物,排除普通食物的位置\r\n if (state.specialFood === null && Math.random() < 0.25) {\r\n state.specialFood = generateFood(state.snake, config, 'special', [state.food.position]);\r\n }\r\n } else if (ateSpecial) {\r\n state.score += 3;\r\n updateSpeed(state, config);\r\n state.specialFood = null;\r\n } else {\r\n // 没吃到食物,移除蛇尾\r\n state.snake.pop();\r\n }\r\n\r\n // 7. 特殊食物倒计时(5 秒后消失)\r\n if (state.specialFood?.spawnedAt !== undefined) {\r\n if (Date.now() - state.specialFood.spawnedAt > 5000) {\r\n state.specialFood = null;\r\n }\r\n }\r\n}\r\n\r\n// ─── 速度更新 ───────────────────────────────────────────\r\n\r\nfunction updateSpeed(state: GameState, config: GameConfig): void {\r\n const newLevel = Math.floor(state.score / config.scorePerLevel) + 1;\r\n state.speedLevel = newLevel;\r\n state.tickInterval = Math.max(\r\n config.minSpeed,\r\n config.initialSpeed - (newLevel - 1) * config.speedDecrement,\r\n );\r\n}\r\n\r\n// ─── 方向变更(外部调用) ──────────────────────────────\r\n\r\n/**\r\n * 尝试改变方向。\r\n * 检查新方向是否与当前移动方向相反,防止 180° 掉头。\r\n */\r\nexport function changeDirection(state: GameState, newDir: Direction): void {\r\n if (!isOpposite(newDir, state.direction)) {\r\n state.nextDirection = newDir;\r\n }\r\n}\r\n","import chalk from 'chalk';\r\nimport type { GameState, GameConfig, Point } from './types.js';\r\n\r\n// 每个格子在终端中占 2 个字符宽度,让网格看起来更接近正方形\r\nconst CELL_W = 2;\r\nconst EMPTY = ' ';\r\nconst BODY_CHAR = '▓';\r\nconst HEAD_CHAR = '▓';\r\nconst NORMAL_FOOD_CHAR = '◆';\r\nconst SPECIAL_FOOD_CHAR = '★';\r\n\r\n// ─── ANSI 控制序列 ──────────────────────────────────────\r\n\r\nconst CSI = '\\x1b[';\r\nconst CURSOR_HIDE = `${CSI}?25l`;\r\nconst CURSOR_SHOW = `${CSI}?25h`;\r\nconst CURSOR_HOME = `${CSI}H`;\r\n\r\n/** 隐藏终端光标 */\r\nexport function hideCursor(): void {\r\n process.stdout.write(CURSOR_HIDE);\r\n}\r\n\r\n/** 显示终端光标 */\r\nexport function showCursor(): void {\r\n process.stdout.write(CURSOR_SHOW);\r\n}\r\n\r\n/** 清屏并复位光标 */\r\nexport function clearScreen(): void {\r\n process.stdout.write(`${CSI}2J${CURSOR_HOME}`);\r\n}\r\n\r\n// ─── 着色辅助 ───────────────────────────────────────────\r\n\r\nfunction cellColor(char: string, type: 'snake-head' | 'snake-body' | 'normal-food' | 'special-food' | 'empty'): string {\r\n switch (type) {\r\n case 'snake-head': return chalk.greenBright(char);\r\n case 'snake-body': return chalk.green(char);\r\n case 'normal-food': return chalk.red(char);\r\n case 'special-food': return chalk.yellow(char);\r\n case 'empty': return char;\r\n }\r\n}\r\n\r\n// ─── 主渲染函数 ─────────────────────────────────────────\r\n\r\n/**\r\n * 将当前游戏状态渲染到终端。\r\n * 先移动光标到左上角再逐行覆盖,避免闪烁。\r\n */\r\nexport function renderToTerminal(state: GameState, config: GameConfig): void {\r\n const lines = buildRenderLines(state, config);\r\n // 先清屏再回左上角绘制,避免状态切换/终端滚动时旧行残留导致重叠\r\n process.stdout.write(`${CSI}2J${CURSOR_HOME}` + lines.join('\\n'));\r\n}\r\n\r\nfunction buildRenderLines(state: GameState, config: GameConfig): string[] {\r\n const { width: W, height: H } = config;\r\n const lines: string[] = [];\r\n\r\n // ── 构建网格数据 ──\r\n // grid[y][x] 存储该位置要显示的内容类型\r\n const grid: string[][] = Array.from({ length: H }, () => Array(W).fill('empty'));\r\n\r\n // 蛇身(除蛇头外)\r\n for (let i = 1; i < state.snake.length; i++) {\r\n const seg = state.snake[i]!;\r\n if (seg.y >= 0 && seg.y < H && seg.x >= 0 && seg.x < W) {\r\n grid[seg.y]![seg.x] = 'snake-body';\r\n }\r\n }\r\n\r\n // 蛇头(覆盖蛇身标记,确保蛇头颜色优先)\r\n const head = state.snake[0]!;\r\n if (head.y >= 0 && head.y < H && head.x >= 0 && head.x < W) {\r\n grid[head.y]![head.x] = 'snake-head';\r\n }\r\n\r\n // 普通食物\r\n const fp = state.food.position;\r\n if (grid[fp.y]![fp.x] === 'empty') {\r\n grid[fp.y]![fp.x] = 'normal-food';\r\n }\r\n\r\n // 特殊食物\r\n if (state.specialFood) {\r\n const sp = state.specialFood.position;\r\n if (grid[sp.y]![sp.x] === 'empty') {\r\n grid[sp.y]![sp.x] = 'special-food';\r\n }\r\n }\r\n\r\n // ── 顶部边框 ──\r\n lines.push(' ' + chalk.gray('╔' + '══'.repeat(W) + '╗'));\r\n\r\n // ── 每行渲染 ──\r\n for (let y = 0; y < H; y++) {\r\n let row = ' ' + chalk.gray('║');\r\n for (let x = 0; x < W; x++) {\r\n const cellType = grid[y]![x]!;\r\n let char: string;\r\n switch (cellType) {\r\n case 'snake-head':\r\n char = HEAD_CHAR.repeat(CELL_W);\r\n break;\r\n case 'snake-body':\r\n char = BODY_CHAR.repeat(CELL_W);\r\n break;\r\n case 'normal-food':\r\n char = NORMAL_FOOD_CHAR + ' ';\r\n break;\r\n case 'special-food':\r\n char = SPECIAL_FOOD_CHAR + ' ';\r\n break;\r\n default:\r\n char = EMPTY;\r\n }\r\n row += cellColor(char, cellType as any);\r\n }\r\n row += chalk.gray('║');\r\n lines.push(row);\r\n }\r\n\r\n // ── 底部边框 ──\r\n lines.push(' ' + chalk.gray('╚' + '══'.repeat(W) + '╝'));\r\n\r\n // ── HUD 信息 ──\r\n lines.push('');\r\n lines.push(\r\n ` ${chalk.cyan('分数')}: ${state.score} ` +\r\n `${chalk.cyan('长度')}: ${state.snake.length} ` +\r\n `${chalk.cyan('速度')}: Lv.${state.speedLevel} ` +\r\n `${chalk.cyan('边界')}: ${state.wallWrap ? '穿墙' : '撞墙'}`,\r\n );\r\n\r\n // 特殊食物倒计时提示\r\n if (state.specialFood?.spawnedAt !== undefined) {\r\n const remaining = Math.ceil((5000 - (Date.now() - state.specialFood.spawnedAt)) / 1000);\r\n if (remaining > 0) {\r\n lines.push(` ${chalk.yellow(`⭐ 特殊食物 ${remaining} 秒后消失`)}`);\r\n }\r\n }\r\n\r\n // 操作提示\r\n lines.push(` ${chalk.gray('W/A/S/D 或 方向键移动 | P 暂停 | M 切换模式 | Q 退出')}`);\r\n\r\n // 暂停 / 游戏结束提示\r\n if (state.isPaused) {\r\n lines.push('');\r\n lines.push(` ${chalk.yellow('⏸ 已暂停 — 按 P 继续')}`);\r\n }\r\n\r\n if (state.isGameOver) {\r\n lines.push('');\r\n lines.push(` ${chalk.red.bold('💀 游戏结束!')} ${chalk.cyan('最终分数: ' + state.score)}`);\r\n lines.push(` ${chalk.gray('按 R 重新开始 | 按 Q 退出')}`);\r\n }\r\n\r\n return lines;\r\n}\r\n","import * as readline from 'node:readline';\r\nimport type { Direction, GameState } from './types.js';\r\nimport { changeDirection } from './engine.js';\r\n\r\n// ─── 按键 → 方向映射 ───────────────────────────────────\r\n\r\nconst KEY_TO_DIR: Record<string, Direction> = {\r\n up: 'UP',\r\n w: 'UP',\r\n W: 'UP',\r\n down: 'DOWN',\r\n s: 'DOWN',\r\n S: 'DOWN',\r\n left: 'LEFT',\r\n a: 'LEFT',\r\n A: 'LEFT',\r\n right: 'RIGHT',\r\n d: 'RIGHT',\r\n D: 'RIGHT',\r\n};\r\n\r\n// ─── 输入设置 ───────────────────────────────────────────\r\n\r\nexport interface InputContext {\r\n state: GameState;\r\n}\r\n\r\nexport type InputCleanup = () => void;\r\n\r\n/**\r\n * 设置终端键盘输入监听。\r\n * - WASD / 方向键 → 控制方向\r\n * - P → 暂停/继续\r\n * - R → 游戏结束后重新开始\r\n * - Q / Ctrl+C → 退出游戏\r\n *\r\n * @param ctx 可变状态上下文(重启时替换 state 引用)\r\n * @param onQuit 退出回调\r\n * @param onRestart 重新开始回调(应返回新的 GameState)\r\n */\r\nexport function setupInput(\r\n ctx: InputContext,\r\n onQuit: () => void,\r\n onRestart: () => GameState,\r\n): InputCleanup {\r\n readline.emitKeypressEvents(process.stdin);\r\n\r\n if (process.stdin.isTTY) {\r\n process.stdin.setRawMode(true);\r\n }\r\n\r\n const handler = (_: unknown, key: readline.Key) => {\r\n if (!key || !key.name) return;\r\n\r\n const { name, ctrl } = key;\r\n\r\n // Ctrl+C / Ctrl+D → 退出\r\n if (ctrl && (name === 'c' || name === 'd')) {\r\n onQuit();\r\n return;\r\n }\r\n\r\n // Q → 退出\r\n if (name === 'q') {\r\n onQuit();\r\n return;\r\n }\r\n\r\n const state = ctx.state;\r\n\r\n // 游戏结束后 R → 重新开始\r\n if (state.isGameOver) {\r\n if (name === 'r') {\r\n ctx.state = onRestart();\r\n }\r\n return;\r\n }\r\n\r\n // P → 暂停/继续\r\n if (name === 'p') {\r\n state.isPaused = !state.isPaused;\r\n return;\r\n }\r\n\r\n // M → 切换边界模式(穿墙 / 撞墙)\r\n if (name === 'm') {\r\n state.wallWrap = !state.wallWrap;\r\n return;\r\n }\r\n\r\n // 方向控制\r\n const dir = KEY_TO_DIR[name];\r\n if (dir) {\r\n changeDirection(state, dir);\r\n }\r\n };\r\n\r\n process.stdin.on('keypress', handler);\r\n\r\n return () => {\r\n process.stdin.removeListener('keypress', handler);\r\n if (process.stdin.isTTY) {\r\n process.stdin.setRawMode(false);\r\n }\r\n };\r\n}\r\n","import type { Game } from '../index.js';\r\nimport type { GameConfig } from './types.js';\r\nimport { createInitialState, tick } from './engine.js';\r\nimport { renderToTerminal, clearScreen, hideCursor, showCursor } from './renderer.js';\r\nimport { setupInput } from './input.js';\r\nimport type { InputContext } from './input.js';\r\n\r\nconst CONFIG: GameConfig = {\r\n width: 20,\r\n height: 16,\r\n initialSpeed: 180,\r\n minSpeed: 60,\r\n speedDecrement: 10,\r\n scorePerLevel: 5,\r\n};\r\n\r\n/** 游戏结束后或暂停时的循环间隔(不需要高帧率) */\r\nconst IDLE_INTERVAL = 200;\r\n\r\nconst snakeGame: Game = {\r\n id: 'snake',\r\n name: 'Big Snake',\r\n description: '经典贪吃蛇,双食物系统、难度递增、穿墙/撞墙模式',\r\n play: async () => {\r\n const ctx: InputContext = { state: createInitialState(CONFIG) };\r\n\r\n return new Promise<void>((resolve) => {\r\n const cleanupInput = setupInput(\r\n ctx,\r\n () => {\r\n // ── 退出 ──\r\n stop = true;\r\n clearTimeout(timerId);\r\n showCursor();\r\n cleanupInput();\r\n resolve();\r\n },\r\n () => {\r\n // ── 重新开始 ──\r\n return createInitialState(CONFIG);\r\n },\r\n );\r\n\r\n // 初始化终端\r\n clearScreen();\r\n hideCursor();\r\n\r\n let stop = false;\r\n let timerId: ReturnType<typeof setTimeout>;\r\n\r\n function loop(): void {\r\n if (stop) return;\r\n\r\n const s = ctx.state;\r\n\r\n if (!s.isGameOver && !s.isPaused) {\r\n tick(s, CONFIG);\r\n }\r\n\r\n renderToTerminal(s, CONFIG);\r\n\r\n // 游戏进行中 → 用 tick 速度;暂停/结束时用恒定低速,节省 CPU\r\n const interval = s.isGameOver || s.isPaused ? IDLE_INTERVAL : s.tickInterval;\r\n timerId = setTimeout(loop, interval);\r\n }\r\n\r\n timerId = setTimeout(loop, 0);\r\n });\r\n },\r\n};\r\n\r\nexport default snakeGame;\r\n","import { registerGame, listGames } from \"./index.js\";\r\nimport type { Game } from \"./index.js\";\r\nimport brickBreaker from \"./brick-breaker/index.js\";\r\nimport coderCheck from \"./coder-check/index.js\";\r\nimport snake from \"./snake/index.js\";\r\n\r\n/** 在此注册所有游戏 */\r\nexport function initGames(): Game[] {\r\n registerGame(brickBreaker);\r\n registerGame(coderCheck);\r\n registerGame(snake);\r\n return listGames();\r\n}\r\n","import { Box, Text, useInput } from \"ink\";\r\nimport { useState, useCallback, useEffect } from \"react\";\r\nimport asciichart from \"asciichart\";\r\nimport os from \"node:os\";\r\nimport { join } from \"node:path\";\r\nimport type { StockRow } from \"./types.js\";\r\nimport { useDoubleCtrlC } from \"../ui/useDoubleCtrlC.js\";\r\n\r\n// ---------------------------------------------------------------------------\r\n// 自选股配置文件路径(取用户全局 ~/.dskcode/settings.json 的真实绝对路径)\r\n// ---------------------------------------------------------------------------\r\n\r\nconst SETTINGS_PATH = join(os.homedir(), \".dskcode\", \"settings.json\");\r\n\r\n/**\r\n * 将本地绝对路径转换为 file:// URL,兼容 Windows 与 POSIX。\r\n * Windows: C:\\Users\\me\\.dskcode\\settings.json → file:///C:/Users/me/.dskcode/settings.json\r\n */\r\nfunction toFileUrl(p: string): string {\r\n const norm = p.replace(/\\\\/g, \"/\");\r\n if (process.platform === \"win32\") {\r\n return \"file://\" + norm.replace(/^([a-zA-Z]:)/, \"/$1\");\r\n }\r\n return \"file://\" + norm;\r\n}\r\n\r\n/**\r\n * 用 OSC 8 转义序列把 text 包装成可点击的终端超链接。\r\n * 现代终端(Windows Terminal / iTerm2 / kitty / WezTerm 等)点击后会以系统默认程序打开该 URI。\r\n * 不可见宽度的转义对 string-width 透明(strip-ansi 会剥除),不影响布局。\r\n */\r\nfunction osc8Link(uri: string, text: string): string {\r\n return `\\x1b]8;;${uri}\\x1b\\\\${text}\\x1b]8;;\\x1b\\\\`;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 分时数据接口\r\n// ---------------------------------------------------------------------------\r\n\r\n/** API 接口地址模板 */\r\nconst MINUTE_API = \"https://web.ifzq.gtimg.cn/appstock/app/minute/query?code={code}&r=0.1\";\r\n\r\ninterface MinuteResponse {\r\n code: number;\r\n data?: Record<string, {\r\n data?: { data?: string[]; date?: string };\r\n qt?: Record<string, (string | string[])[]>;\r\n }>;\r\n}\r\n\r\n/** 确保股票代码带市场前缀(如 513090 → sh513090) */\r\nfunction normalizeApiCode(code: string): string {\r\n if (code.startsWith(\"sh\") || code.startsWith(\"sz\")) return code;\r\n if (/^60|^68|^51/.test(code)) return \"sh\" + code;\r\n if (/^00|^30|^39/.test(code)) return \"sz\" + code;\r\n return \"sh\" + code;\r\n}\r\n\r\n/**\r\n * 调用分时接口获取某只股票的全天分钟数据。\r\n * 返回 { prices: 每分钟价格数组, quote: 实时行情快照, date: 日期 }\r\n */\r\nasync function fetchStockMinute(code: string): Promise<{\r\n prices: number[];\r\n quote: StockRow | null;\r\n date: string;\r\n} | null> {\r\n const url = MINUTE_API.replace(\"{code}\", normalizeApiCode(code));\r\n try {\r\n const resp = await fetch(url);\r\n const json = (await resp.json()) as MinuteResponse;\r\n if (json.code !== 0) return null;\r\n\r\n const stockKey = normalizeApiCode(code);\r\n const stockData = json.data?.[stockKey];\r\n if (!stockData) return null;\r\n\r\n // 1. 分钟行情 — 用于折线图\r\n const rawMinutes = stockData.data?.data ?? [];\r\n const prices: number[] = [];\r\n for (const line of rawMinutes) {\r\n // 格式: \"HHMM price volume amount\"\r\n const parts = (line as string).split(\" \");\r\n if (parts.length >= 2) {\r\n const p = parseFloat(parts[1]!);\r\n if (!isNaN(p)) prices.push(p);\r\n }\r\n }\r\n\r\n // 2. 实时行情快照 — 用于列表\r\n const qtKey = stockKey;\r\n const qt = stockData.qt?.[qtKey] as string[] | undefined;\r\n let quote: StockRow | null = null;\r\n if (qt && qt.length >= 35) {\r\n quote = {\r\n code,\r\n name: qt[1] ?? \"\",\r\n price: parseFloat(qt[3] ?? \"0\"),\r\n changePercent: parseFloat(qt[32] ?? \"0\"),\r\n changeAmount: parseFloat(qt[31] ?? \"0\"),\r\n high: parseFloat(qt[33] ?? \"0\"),\r\n low: parseFloat(qt[34] ?? \"0\"),\r\n volume: parseInt(qt[6] ?? \"0\", 10),\r\n // 成交额(单位:万元)→ 转为元\r\n amount: parseFloat(qt[37] ?? \"0\") * 10_000,\r\n };\r\n }\r\n\r\n return {\r\n prices,\r\n quote,\r\n date: stockData.data?.date ?? \"\",\r\n };\r\n } catch {\r\n return null;\r\n }\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 缓存\r\n// ---------------------------------------------------------------------------\r\n\r\n/** 缓存各股票的分钟价格数据,key = 股票代码 */\r\nconst minuteCache = new Map<string, number[]>();\r\n\r\nfunction cacheMinute(code: string, prices: number[]): void {\r\n minuteCache.set(code, prices);\r\n}\r\n\r\n/** 仅用于测试环境重置缓存 */\r\nexport function _clearMinuteCache(): void {\r\n minuteCache.clear();\r\n}\r\n\r\nfunction getCachedMinutes(code: string): number[] | undefined {\r\n return minuteCache.get(code);\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 演示兜底数据\r\n// ---------------------------------------------------------------------------\r\n\r\nconst FALLBACK_STOCKS: StockRow[] = [\r\n // 上证指数成交额约 1.56 万亿\r\n { code: \"000001\", name: \"上证指数\", price: 3150.00, changePercent: 0.35, changeAmount: 11.02, high: 3160.00, low: 3140.00, volume: 285430000, amount: 1_560_000_000_000 },\r\n // 紫金矿业成交额约 108.5 亿\r\n { code: \"601899\", name: \"紫金矿业\", price: 18.20, changePercent: 1.85, changeAmount: 0.33, high: 18.45, low: 17.90, volume: 59615384, amount: 10_850_000_000 },\r\n { code: \"399006\", name: \"创业板指\", price: 1820.00, changePercent: -0.52, changeAmount: -9.50, high: 1835.00, low: 1815.00, volume: 98650000, amount: 18_240_000_000 },\r\n];\r\n\r\n// ---------------------------------------------------------------------------\r\n// fetchStocks — 加载列表数据\r\n// ---------------------------------------------------------------------------\r\n\r\nasync function fetchStocks(codes: string[]): Promise<StockRow[]> {\r\n const results = await Promise.all(\r\n codes.map(async (code) => {\r\n const data = await fetchStockMinute(code);\r\n if (data?.quote) {\r\n // 同时缓存分钟数据,进详情就不用再请求了\r\n if (data.prices.length > 0) cacheMinute(code, data.prices);\r\n return data.quote;\r\n }\r\n return null;\r\n }),\r\n );\r\n\r\n // 用真实数据替换,缺失的用兜底\r\n const real: StockRow[] = results.filter((r): r is StockRow => r !== null);\r\n if (real.length > 0) return real;\r\n\r\n // 全都没取到?用兜底数据\r\n return FALLBACK_STOCKS;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 格式化工具\r\n// ---------------------------------------------------------------------------\r\n\r\nfunction formatPrice(p: number): string {\r\n return p >= 100 ? p.toFixed(2) : p.toFixed(3);\r\n}\r\n\r\nfunction formatVolume(v: number): string {\r\n if (v >= 10000) return (v / 10000).toFixed(1) + \"万\";\r\n return v.toLocaleString();\r\n}\r\n\r\n/**\r\n * 成交额格式化(元 → 万/亿/万亿)\r\n * 1 万亿 = 1_0000_0000_0000,1 亿 = 1_0000_0000,1 万 = 1_0000\r\n */\r\nfunction formatAmount(yuan: number): string {\r\n if (yuan >= 1_000_000_000_000) return (yuan / 1_000_000_000_000).toFixed(2) + \"万亿\";\r\n if (yuan >= 100_000_000) return (yuan / 100_000_000).toFixed(2) + \"亿\";\r\n if (yuan >= 10_000) return (yuan / 10_000).toFixed(1) + \"万\";\r\n return yuan.toLocaleString();\r\n}\r\n\r\n/**\r\n * 取最新 maxPoints 个点,用于绘制折线图。\r\n * 分时数据从 09:30 累积到当前时间,取尾部最新的 60 点\r\n * 能展示最近的行情走势,同时适配终端宽度。\r\n */\r\nfunction latestPoints(data: number[], maxPoints = 60): number[] {\r\n if (data.length <= maxPoints) return data;\r\n return data.slice(data.length - maxPoints);\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// StockList 组件\r\n// ---------------------------------------------------------------------------\r\n\r\ninterface StockListProps {\r\n /** 自选股代码列表 */\r\n codes?: string[];\r\n /** 按 q 时退出 */\r\n onExit: () => void;\r\n /** 按 q 时返回上级(从 chat 内跳转时使用) */\r\n onBackToChat?: () => void;\r\n}\r\n\r\nexport function StockList({ codes, onExit, onBackToChat }: StockListProps) {\r\n const [stocks, setStocks] = useState<StockRow[]>([]);\r\n const [selectedIndex, setSelectedIndex] = useState(0);\r\n const [loading, setLoading] = useState(true);\r\n const [lastUpdate, setLastUpdate] = useState<string>(\"\");\r\n const [detailView, setDetailView] = useState<StockRow | null>(null);\r\n const [detailPrices, setDetailPrices] = useState<number[] | null>(null);\r\n const [detailLoading, setDetailLoading] = useState(false);\r\n const [detailCountdown, setDetailCountdown] = useState(10);\r\n const [countdown, setCountdown] = useState(5);\r\n const [currentTime, setCurrentTime] = useState<string>(() =>\r\n new Date().toLocaleTimeString(\"zh-CN\", { hour12: false }),\r\n );\r\n // h 键一键置灰:所有高亮色替换为 dimColor\r\n const [dimMode, setDimMode] = useState(false);\r\n\r\n const { doubleCtrlC, handleCtrlC } = useDoubleCtrlC(onExit);\r\n\r\n // 实时时钟(每秒更新)\r\n useEffect(() => {\r\n const timer = setInterval(() => {\r\n setCurrentTime(new Date().toLocaleTimeString(\"zh-CN\", { hour12: false }));\r\n }, 1000);\r\n return () => clearInterval(timer);\r\n }, []);\r\n\r\n // ---------- 数据加载 ----------\r\n const loadData = useCallback(async () => {\r\n setLoading(true);\r\n try {\r\n const data = await fetchStocks(codes ?? []);\r\n setStocks(data);\r\n setLastUpdate(new Date().toLocaleTimeString());\r\n } catch {\r\n // 保持旧数据\r\n }\r\n setLoading(false);\r\n }, [codes]);\r\n\r\n // 首次加载\r\n useEffect(() => {\r\n loadData();\r\n }, [loadData]);\r\n\r\n // 5 秒自动刷新 + 倒计时\r\n useEffect(() => {\r\n const interval = setInterval(() => {\r\n setCountdown((prev) => {\r\n if (prev <= 1) {\r\n loadData();\r\n return 5;\r\n }\r\n return prev - 1;\r\n });\r\n }, 1000);\r\n return () => clearInterval(interval);\r\n }, [loadData]);\r\n\r\n // ---------- 进入详情时加载分钟数据,每 30s 自动刷新 ----------\r\n useEffect(() => {\r\n if (!detailView) {\r\n setDetailPrices(null);\r\n setDetailLoading(false);\r\n return;\r\n }\r\n\r\n const loadDetail = () => {\r\n // 清除缓存,强制走网络\r\n minuteCache.delete(detailView.code);\r\n fetchStockMinute(detailView.code).then((data) => {\r\n if (data && data.prices.length > 0) {\r\n cacheMinute(detailView.code, data.prices);\r\n setDetailPrices(data.prices);\r\n }\r\n });\r\n };\r\n\r\n // 立即加载\r\n loadDetail();\r\n\r\n // 重置倒计时\r\n setDetailCountdown(10);\r\n\r\n // 每 10 秒刷新\r\n const timer = setInterval(loadDetail, 10000);\r\n return () => clearInterval(timer);\r\n }, [detailView]);\r\n\r\n // 详情页倒计时\r\n useEffect(() => {\r\n if (!detailView) return;\r\n const timer = setInterval(() => {\r\n setDetailCountdown((prev) => (prev > 0 ? prev - 1 : 10));\r\n }, 1000);\r\n return () => clearInterval(timer);\r\n }, [detailView]);\r\n\r\n // ---------- 键盘控制 ----------\r\n useInput(\r\n useCallback(\r\n (input, key) => {\r\n // Ctrl+C:双击退出\r\n if (input === \"c\" && key.ctrl) {\r\n handleCtrlC();\r\n return;\r\n }\r\n\r\n if (detailView) {\r\n // 详情模式下按 Esc/q 返回列表\r\n if (key.escape || input === \"q\" || input === \" \") {\r\n setDetailView(null);\r\n }\r\n return;\r\n }\r\n\r\n if (stocks.length === 0) return;\r\n\r\n if (key.upArrow || input === \"k\") {\r\n setSelectedIndex((prev) => (prev > 0 ? prev - 1 : stocks.length - 1));\r\n } else if (key.downArrow || input === \"j\") {\r\n setSelectedIndex((prev) => (prev < stocks.length - 1 ? prev + 1 : 0));\r\n } else if (key.return) {\r\n const stock = stocks[selectedIndex];\r\n if (stock) setDetailView(stock);\r\n } else if (key.escape || input === \"q\") {\r\n if (onBackToChat) onBackToChat();\r\n else onExit();\r\n } else if (input === \"r\") {\r\n setCountdown(5);\r\n loadData();\r\n } else if (input === \"h\") {\r\n setDimMode((v) => !v);\r\n }\r\n },\r\n [stocks, selectedIndex, detailView, onExit, onBackToChat, loadData, handleCtrlC, setDimMode],\r\n ),\r\n );\r\n\r\n // ---------- 详情视图 ----------\r\n if (detailView) {\r\n if (detailLoading) {\r\n return (\r\n <Box paddingLeft={1}>\r\n <Text dimColor>{\" ⟳ 加载分时数据...\"}</Text>\r\n </Box>\r\n );\r\n }\r\n return renderDetail(detailView, () => setDetailView(null), detailPrices ?? undefined, detailCountdown, currentTime);\r\n }\r\n\r\n // ---------- 列表视图 ----------\r\n // 置灰模式下,所有高亮色统一替换为 dimColor\r\n const cp = (c: string) => dimMode ? { dimColor: true } : { color: c };\r\n return (\r\n <Box flexDirection=\"column\">\r\n {/* 顶部状态栏 */}\r\n <Box marginBottom={1} justifyContent=\"space-between\">\r\n <Text bold {...cp(\"#00ffff\")}>\r\n {\" 📈 自选股监控\"}\r\n </Text>\r\n <Text dimColor>\r\n {\" 🕐 \"}{currentTime}\r\n </Text>\r\n <Text dimColor>\r\n {loading ? \" ⟳ 刷新中...\" : ` ${countdown}s 后自动刷新`}\r\n </Text>\r\n </Box>\r\n\r\n {/* 表头 */}\r\n <Box>\r\n <Box width={3} />\r\n <Box width={9}>\r\n <Text dimColor>代码</Text>\r\n </Box>\r\n <Box width={16}>\r\n <Text dimColor>名称</Text>\r\n </Box>\r\n <Box width={12}>\r\n <Text dimColor>最新价</Text>\r\n </Box>\r\n <Box width={12}>\r\n <Text dimColor>涨跌幅</Text>\r\n </Box>\r\n <Box width={12}>\r\n <Text dimColor>涨跌额</Text>\r\n </Box>\r\n <Box width={12}>\r\n <Text dimColor>最高</Text>\r\n </Box>\r\n <Box width={12}>\r\n <Text dimColor>最低</Text>\r\n </Box>\r\n <Box>\r\n <Text dimColor>成交额</Text>\r\n </Box>\r\n </Box>\r\n\r\n {/* 分隔线 */}\r\n <Box>\r\n <Text dimColor>{\" \" + \"─\".repeat(100)}</Text>\r\n </Box>\r\n\r\n {/* 股票列表 */}\r\n <Box flexDirection=\"column\">\r\n {stocks.map((stock, index) => {\r\n const isSelected = index === selectedIndex;\r\n const isUp = stock.changePercent >= 0;\r\n const color = isUp ? \"#ff1493\" : \"#00ff41\";\r\n\r\n return (\r\n <Box key={stock.code}>\r\n <Box width={3} flexShrink={0}>\r\n {isSelected ? (\r\n <Text bold {...cp(\"#00ffff\")}>\r\n {\"▸ \"}\r\n </Text>\r\n ) : (\r\n <Text>{\" \"}</Text>\r\n )}\r\n </Box>\r\n <Box width={9}>\r\n <Text bold {...cp(isSelected ? \"#00ffff\" : \"#ffffff\")}>\r\n {stock.code}\r\n </Text>\r\n </Box>\r\n <Box width={16}>\r\n <Text {...cp(isSelected ? \"#ffffff\" : \"#cccccc\")}>\r\n {stock.name}\r\n </Text>\r\n </Box>\r\n <Box width={12}>\r\n <Text bold {...cp(color)}>\r\n {formatPrice(stock.price)}\r\n </Text>\r\n </Box>\r\n <Box width={12}>\r\n <Text {...cp(color)}>\r\n {isUp ? \"+\" : \"\"}{stock.changePercent.toFixed(2)}%\r\n </Text>\r\n </Box>\r\n <Box width={12}>\r\n <Text {...cp(color)}>\r\n {isUp ? \"+\" : \"\"}{stock.changeAmount.toFixed(3)}\r\n </Text>\r\n </Box>\r\n <Box width={12}>\r\n <Text {...cp(\"#cccccc\")}>\r\n {formatPrice(stock.high)}\r\n </Text>\r\n </Box>\r\n <Box width={12}>\r\n <Text {...cp(\"#888888\")}>\r\n {formatPrice(stock.low)}\r\n </Text>\r\n </Box>\r\n <Box>\r\n <Text {...cp(\"#888888\")}>\r\n {formatAmount(stock.amount)}\r\n </Text>\r\n </Box>\r\n </Box>\r\n );\r\n })}\r\n </Box>\r\n\r\n {/* 底栏 */}\r\n <Box marginTop={1}>\r\n <Text dimColor>\r\n {` ↑/↓ 选择 Enter 详情 r 手动刷新 h 置灰/恢复 q 返回`}\r\n </Text>\r\n </Box>\r\n <Box>\r\n <Text dimColor>{` 最后更新: ${lastUpdate} 编辑自选股: `}</Text>\r\n <Text {...cp(\"#c792ea\")}>\r\n {osc8Link(toFileUrl(SETTINGS_PATH), SETTINGS_PATH)}\r\n </Text>\r\n </Box>\r\n\r\n {/* 双击 Ctrl+C 退出提示 */}\r\n {doubleCtrlC && (\r\n <Box marginTop={1}>\r\n <Text bold {...cp(\"#ff1493\")}>\r\n {\" ⚠ 再按一次 Ctrl+C 退出 dskcode\"}\r\n </Text>\r\n </Box>\r\n )}\r\n </Box>\r\n );\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 详情视图(独立的渲染函数)\r\n// ---------------------------------------------------------------------------\r\n\r\nfunction renderDetail(\r\n stock: StockRow,\r\n _onBack: () => void,\r\n prices?: number[],\r\n countdown = 10,\r\n currentTime?: string,\r\n) {\r\n const isUp = stock.changePercent >= 0;\r\n const colorCode = isUp ? \"#ff1493\" : \"#00ff41\";\r\n const arrow = isUp ? \"▲\" : \"▼\";\r\n\r\n // 折线图\r\n let chartLines: string[] = [];\r\n if (prices && prices.length > 0) {\r\n const chartColor = isUp ? asciichart.red : asciichart.green;\r\n // 取最新 60 条分时数据展示近期走势\r\n const latest = latestPoints(prices, 60);\r\n let raw = asciichart.plot(latest, {\r\n height: 10,\r\n colors: [chartColor],\r\n });\r\n // 弯角 → 直角,折线更硬朗\r\n raw = raw\r\n .replaceAll(\"╭\", \"┌\")\r\n .replaceAll(\"╮\", \"┐\")\r\n .replaceAll(\"╰\", \"└\")\r\n .replaceAll(\"╯\", \"┘\");\r\n chartLines = raw.split(\"\\n\");\r\n }\r\n\r\n return (\r\n <Box flexDirection=\"column\" paddingLeft={1}>\r\n {/* 标题行 — 左:名称代码 + 实时时间,右:刷新倒计时 */}\r\n <Box marginBottom={1} justifyContent=\"space-between\">\r\n <Box>\r\n <Text bold color=\"#00ffff\">\r\n {\" 📊 \"}{stock.name}{\" \"}\r\n </Text>\r\n <Text dimColor>\r\n {stock.code}\r\n </Text>\r\n {currentTime && (\r\n <Text dimColor>\r\n {\" 🕐 \"}{currentTime}\r\n </Text>\r\n )}\r\n </Box>\r\n <Text dimColor>\r\n {`${countdown}s 后刷新`}\r\n </Text>\r\n </Box>\r\n\r\n {/* 价格摘要 */}\r\n <Box>\r\n <Box width={16}>\r\n <Text bold color=\"#888888\">当前价</Text>\r\n </Box>\r\n <Box>\r\n <Text bold color={colorCode}>\r\n {arrow} {formatPrice(stock.price)}\r\n </Text>\r\n </Box>\r\n </Box>\r\n <Box>\r\n <Box width={16}>\r\n <Text color=\"#888888\">涨跌幅</Text>\r\n </Box>\r\n <Box>\r\n <Text color={colorCode}>\r\n {isUp ? \"+\" : \"\"}{stock.changePercent.toFixed(2)}%\r\n {\" \"}\r\n {isUp ? \"+\" : \"\"}{stock.changeAmount.toFixed(3)}\r\n </Text>\r\n </Box>\r\n </Box>\r\n\r\n {/* 折线图 */}\r\n {chartLines.length > 0 && (\r\n <Box marginTop={1} flexDirection=\"column\">\r\n {chartLines.map((line, i) => (\r\n <Box key={i}>\r\n <Text color={colorCode}>\r\n {line || \" \"}\r\n </Text>\r\n </Box>\r\n ))}\r\n </Box>\r\n )}\r\n\r\n {/* 底部 */}\r\n <Box marginTop={1}>\r\n <Text dimColor>\r\n {\" Space/q 返回列表\"}\r\n </Text>\r\n </Box>\r\n </Box>\r\n );\r\n}\r\n","// ---------------------------------------------------------------------------\r\n// 递归扫描项目源码文件(排除 node_modules 等非源码目录)\r\n// 启动时调用一次,结果缓存为扁平路径数组传给 UI\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { readdir } from \"node:fs/promises\";\r\nimport { join, relative } from \"node:path\";\r\n\r\n/** 需要跳过的目录名(任何层级) */\r\nconst IGNORE_DIRS = new Set([\r\n \"node_modules\",\r\n \".git\",\r\n \".svn\",\r\n \".hg\",\r\n \".dskcode\",\r\n \".claude\",\r\n \"dist\",\r\n \"build\",\r\n \".next\",\r\n \".turbo\",\r\n \".nx\",\r\n \"coverage\",\r\n \".cache\",\r\n \".nyc_output\",\r\n \".vscode\",\r\n \".idea\",\r\n \"__pycache__\",\r\n \".venv\",\r\n \"venv\",\r\n \".tox\",\r\n \"target\",\r\n \"vendor\",\r\n \"bower_components\",\r\n \"jspm_packages\",\r\n]);\r\n\r\n/** 只索引这些源码扩展名 */\r\nconst SOURCE_EXTS = new Set([\r\n \".ts\", \".tsx\", \".js\", \".jsx\", \".mjs\", \".cjs\",\r\n \".vue\", \".svelte\", \".astro\",\r\n \".css\", \".scss\", \".less\",\r\n \".html\",\r\n \".json\",\r\n \".md\",\r\n \".yaml\", \".yml\",\r\n \".toml\",\r\n]);\r\n\r\n/**\r\n * 递归扫描 dir 下的所有文件,返回相对于 baseDir 的路径数组。\r\n *\r\n * 优化手段:\r\n * - readdir withFileTypes 省掉 stat 调用(减少一半 syscall)\r\n * - 同层目录的 readdir 用 Promise.all 并行\r\n * - 跳过以 . 开头的隐藏目录和已知非源码目录\r\n *\r\n * 性能预期(SSD):\r\n * 1,000 文件 → ~15ms\r\n * 10,000 文件 → ~150ms\r\n * 50,000 文件 → ~800ms\r\n */\r\nexport async function scanProjectFiles(\r\n baseDir: string,\r\n dir?: string,\r\n): Promise<string[]> {\r\n const currentDir = dir ?? baseDir;\r\n\r\n let entries;\r\n try {\r\n entries = await readdir(currentDir, { withFileTypes: true });\r\n } catch {\r\n return [];\r\n }\r\n\r\n const files: string[] = [];\r\n const dirs: string[] = [];\r\n\r\n for (const entry of entries) {\r\n const name = entry.name;\r\n if (IGNORE_DIRS.has(name) || name.startsWith(\".\")) continue;\r\n\r\n if (entry.isDirectory()) {\r\n dirs.push(name);\r\n } else if (entry.isFile()) {\r\n const ext = name.slice(name.lastIndexOf(\".\")).toLowerCase();\r\n if (SOURCE_EXTS.has(ext)) {\r\n files.push(relative(baseDir, join(currentDir, name)));\r\n }\r\n }\r\n }\r\n\r\n // 同层子目录并行扫描\r\n const nested = await Promise.all(\r\n dirs.map((d) => scanProjectFiles(baseDir, join(currentDir, d))),\r\n );\r\n\r\n for (const n of nested) {\r\n files.push(...n);\r\n }\r\n\r\n return files;\r\n}\r\n","/** dskcode 退出码规范 */\r\nexport const ExitCode = {\r\n /** 正常执行完成 */\r\n SUCCESS: 0,\r\n /** 通用错误 */\r\n GENERAL_ERROR: 1,\r\n /** 配置错误 */\r\n CONFIG_ERROR: 2,\r\n /** 用户通过 Ctrl+C 中断 */\r\n SIGINT: 130,\r\n} as const;\r\n","#!/usr/bin/env node\r\n\r\nimport { createCli } from \"./cli/index.js\";\r\nimport { ExitCode } from \"./cli/exit-codes.js\";\r\n\r\n/**\r\n * 双击 Ctrl+C 退出:\r\n * 第一次 SIGINT → 提示用户再按一次\r\n * 第二次 SIGINT(1.5 秒内)→ 立即退出\r\n *\r\n * 在 ink 交互模式下,Ctrl+C 由 useInput 捕获处理,\r\n * 此处只处理 ink 未运行时的 SIGINT(如启动阶段)。\r\n */\r\nlet sigintCount = 0;\r\nlet sigintTimer: ReturnType<typeof setTimeout> | null = null;\r\n\r\n/**\r\n * 全局兜底 SIGINT 处理:双击退出。\r\n * ink 交互模式下 Ctrl+C 由 useDoubleCtrlC hook 处理,\r\n * 此处仅处理 ink 未运行时的场景(如启动阶段、异常退出后)。\r\n */\r\nprocess.on(\"SIGINT\", () => {\r\n sigintCount++;\r\n if (sigintCount >= 2) {\r\n process.exit(ExitCode.SIGINT);\r\n }\r\n process.stdout.write(\"\\n ⚠ 再按一次 Ctrl+C 退出 dskcode\\n\");\r\n if (sigintTimer) clearTimeout(sigintTimer);\r\n sigintTimer = setTimeout(() => {\r\n sigintCount = 0;\r\n }, 1500);\r\n});\r\n\r\nconst program = createCli();\r\n\r\ntry {\r\n await program.parseAsync(process.argv);\r\n} catch (err: unknown) {\r\n const error = err as { exitCode?: number; code?: string };\r\n\r\n if (error.code === \"commander.helpDisplayed\" || error.code === \"commander.version\") {\r\n process.exit(error.exitCode ?? ExitCode.SUCCESS);\r\n }\r\n\r\n if (typeof error.exitCode === \"number\") {\r\n process.exit(error.exitCode);\r\n }\r\n\r\n console.error(String(err));\r\n process.exit(ExitCode.GENERAL_ERROR);\r\n}\r\n"],"mappings":";;;;;;;;;;;;AAAA,SAAS,eAAe;;;ACAxB,SAAS,YAAY,aAAa;AAClC,SAAS,OAAO,UAAU,iBAAiB;AAC3C,SAAS,YAAY;AAOd,IAAM,gBAAwB;AAAA,EACnC,iBAAiB;AAAA,EACjB,WAAW;AAAA,EACX,aAAa;AAAA,EACb,eAAe;AAAA,EACf,WAAW;AAAA,IACT;AAAA,MACE,MAAM;AAAA,MACN,SAAS;AAAA,MACT,OAAO;AAAA,IACT;AAAA,EACF;AAAA,EACA,OAAO;AAAA,IACL,EAAE,MAAM,aAAa,SAAS,KAAK;AAAA,IACnC,EAAE,MAAM,cAAc,SAAS,KAAK;AAAA,IACpC,EAAE,MAAM,aAAa,SAAS,KAAK;AAAA,IACnC,EAAE,MAAM,QAAQ,SAAS,KAAK;AAAA,IAC9B,EAAE,MAAM,QAAQ,SAAS,KAAK;AAAA,IAC9B,EAAE,MAAM,QAAQ,SAAS,KAAK;AAAA,IAC9B,EAAE,MAAM,MAAM,SAAS,KAAK;AAAA,IAC5B,EAAE,MAAM,SAAS,SAAS,KAAK;AAAA,EACjC;AAAA,EACA,SAAS,CAAC;AAAA,EACV,OAAO;AAAA,IACL,SAAS;AAAA,MACP,EAAE,MAAM,WAAW;AAAA,MACnB,EAAE,MAAM,WAAW;AAAA,MACnB,EAAE,MAAM,WAAW;AAAA,IACrB;AAAA,EACF;AACF;AAuBA,SAAS,mBAAmB,YAA+B;AACzD,MAAI,YAAY;AACd,WAAO,CAAC,UAAU;AAAA,EACpB;AAEA,QAAM,OAAO,QAAQ,IAAI,QAAQ,QAAQ,IAAI,eAAe;AAC5D,SAAO;AAAA,IACL,KAAK,MAAM,YAAY,eAAe;AAAA,IACtC,KAAK,QAAQ,IAAI,GAAG,YAAY,eAAe;AAAA,EACjD;AACF;AAaA,SAAS,YAAY,MAAc,SAAkC;AACnE,QAAM,SAAiB,EAAE,GAAG,KAAK;AAEjC,MAAI,QAAQ,oBAAoB,QAAW;AACzC,WAAO,kBAAkB,QAAQ;AAAA,EACnC;AACA,MAAI,QAAQ,YAAY,QAAW;AACjC,WAAO,UAAU,QAAQ;AAAA,EAC3B;AACA,MAAI,QAAQ,cAAc,QAAW;AACnC,WAAO,YAAY,QAAQ;AAAA,EAC7B;AACA,MAAI,QAAQ,gBAAgB,QAAW;AACrC,WAAO,cAAc,QAAQ;AAAA,EAC/B;AACA,MAAI,QAAQ,kBAAkB,QAAW;AACvC,WAAO,gBAAgB,QAAQ;AAAA,EACjC;AACA,MAAI,QAAQ,gBAAgB,QAAW;AACrC,WAAO,cAAc,QAAQ;AAAA,EAC/B;AACA,MAAI,QAAQ,qBAAqB,QAAW;AAC1C,WAAO,mBAAmB,QAAQ;AAAA,EACpC;AACA,MAAI,QAAQ,cAAc,QAAW;AACnC,WAAO,YAAY,QAAQ;AAAA,EAC7B;AACA,MAAI,QAAQ,UAAU,QAAW;AAC/B,WAAO,QAAQ,QAAQ;AAAA,EACzB;AACA,MAAI,QAAQ,YAAY,QAAW;AACjC,WAAO,UAAU,QAAQ;AAAA,EAC3B;AACA,MAAI,QAAQ,UAAU,QAAW;AAC/B,WAAO,QAAQ,QAAQ;AAAA,EACzB;AAEA,SAAO;AACT;AAOA,IAAM,aAAa;AAGnB,IAAM,UAAwC;AAAA,EAC5C,CAAC,GAAG,UAAU,kBAAkB,GAAG;AAAA,EACnC,CAAC,GAAG,UAAU,SAAS,GAAG;AAAA,EAC1B,CAAC,GAAG,UAAU,YAAY,GAAG;AAAA,EAC7B,CAAC,GAAG,UAAU,aAAa,GAAG;AAAA,EAC9B,CAAC,GAAG,UAAU,iBAAiB,GAAG;AAAA,EAClC,CAAC,GAAG,UAAU,cAAc,GAAG;AAAA,EAC/B,CAAC,GAAG,UAAU,oBAAoB,GAAG;AACvC;AAOA,SAAS,aAAa,QAAwB;AAE5C,QAAM,SAAiB,EAAE,GAAG,QAAQ,WAAW,CAAC,GAAG,OAAO,SAAS,EAAE;AAGrE,aAAW,CAAC,QAAQ,SAAS,KAAK,OAAO,QAAQ,OAAO,GAAG;AACzD,UAAM,MAAM,QAAQ,IAAI,MAAM;AAC9B,QAAI,QAAQ,OAAW;AAEvB,YAAQ,WAAW;AAAA,MACjB,KAAK;AAAA,MACL,KAAK,mBAAmB;AACtB,QAAC,OAA8C,SAAS,IAAI;AAC5D;AAAA,MACF;AAAA,MACA,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK,oBAAoB;AACvB,cAAM,IAAI,OAAO,GAAG;AACpB,YAAI,OAAO,SAAS,CAAC,KAAK,KAAK,GAAG;AAChC,UAAC,OAA8C,SAAS,IAAI;AAAA,QAC9D;AACA;AAAA,MACF;AAAA,MACA,KAAK,eAAe;AAClB,cAAM,IAAI,OAAO,GAAG;AACpB,YAAI,OAAO,SAAS,CAAC,KAAK,KAAK,KAAK,KAAK,GAAG;AAC1C,UAAC,OAA8C,SAAS,IAAI;AAAA,QAC9D;AACA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAGA,QAAM,SAAS,QAAQ,IAAI;AAC3B,MAAI,QAAQ;AACV,UAAM,cAAc,OAAO,UAAU,UAAU,CAAC,MAAM,EAAE,SAAS,UAAU;AAC3E,QAAI,gBAAgB,IAAI;AACtB,YAAM,WAAW,OAAO,UAAU,WAAW;AAC7C,UAAI,CAAC,SAAS,QAAQ;AACpB,eAAO,UAAU,WAAW,IAAI,EAAE,GAAG,UAAU,OAAO;AAAA,MACxD;AAAA,IACF,OAAO;AAEL,aAAO,UAAU,QAAQ;AAAA,QACvB,MAAM;AAAA,QACN,SAAS;AAAA,QACT,OAAO;AAAA,QACP;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO;AACT;AAoBO,SAAS,kBAAkB,QAAgB,OAAyB;AACzE,QAAM,SAAiB,EAAE,GAAG,QAAQ,WAAW,CAAC,GAAG,OAAO,SAAS,EAAE;AAErE,MAAI,MAAM,YAAY,QAAW;AAC/B,WAAO,UAAU,MAAM;AAAA,EACzB;AACA,MAAI,MAAM,UAAU,QAAW;AAG7B,UAAM,cAAc,OAAO,UAAU;AAAA,MACnC,CAAC,MAAM,EAAE,SAAS,OAAO;AAAA,IAC3B;AACA,QAAI,gBAAgB,IAAI;AACtB,aAAO,UAAU,WAAW,IAAI;AAAA,QAC9B,GAAG,OAAO,UAAU,WAAW;AAAA,QAC/B,OAAO,MAAM;AAAA,MACf;AAAA,IACF;AAAA,EACF;AACA,MAAI,MAAM,cAAc,UAAa,MAAM,YAAY,GAAG;AACxD,WAAO,YAAY,MAAM;AAAA,EAC3B;AACA,MACE,MAAM,gBAAgB,UACtB,MAAM,eAAe,KACrB,MAAM,eAAe,GACrB;AACA,WAAO,cAAc,MAAM;AAAA,EAC7B;AACA,MAAI,MAAM,gBAAgB,UAAa,MAAM,eAAe,GAAG;AAC7D,WAAO,cAAc,MAAM;AAAA,EAC7B;AACA,MAAI,MAAM,qBAAqB,UAAa,MAAM,oBAAoB,GAAG;AACvE,WAAO,mBAAmB,MAAM;AAAA,EAClC;AACA,SAAO;AACT;AAeO,SAAS,eAAe,QAA+B;AAC5D,QAAM,SAAwB,CAAC;AAG/B,MAAI,CAAC,OAAO,aAAa,OAAO,UAAU,WAAW,GAAG;AACtD,WAAO,KAAK;AAAA,MACV,OAAO;AAAA,MACP,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AAGA,QAAMA,oBAAmB,CAAC,qBAAqB,iBAAiB;AAChE,WAAS,IAAI,GAAG,IAAI,OAAO,UAAU,QAAQ,KAAK;AAChD,UAAM,IAAI,OAAO,UAAU,CAAC;AAC5B,QAAI,CAAC,EAAE,MAAM;AACX,aAAO,KAAK;AAAA,QACV,OAAO,aAAa,CAAC;AAAA,QACrB,SAAS,UAAK,IAAI,CAAC;AAAA,MACrB,CAAC;AAAA,IACH;AACA,QAAI,CAAC,EAAE,OAAO;AACZ,aAAO,KAAK;AAAA,QACV,OAAO,aAAa,CAAC;AAAA,QACrB,SAAS,aAAa,EAAE,QAAQ,CAAC;AAAA,MACnC,CAAC;AAAA,IACH,WAAW,CAACA,kBAAiB,SAAS,EAAE,KAAK,GAAG;AAC9C,aAAO,KAAK;AAAA,QACV,OAAO,aAAa,CAAC;AAAA,QACrB,SAAS,aAAa,EAAE,QAAQ,CAAC,mBAAc,EAAE,KAAK,+DAAuBA,kBAAiB,KAAK,IAAI,CAAC;AAAA,MAC1G,CAAC;AAAA,IACH;AAAA,EACF;AAGA,MAAI,OAAO,iBAAiB;AAC1B,UAAM,SAAS,OAAO,UAAU;AAAA,MAC9B,CAAC,MAAM,EAAE,SAAS,OAAO;AAAA,IAC3B;AACA,QAAI,CAAC,QAAQ;AACX,aAAO,KAAK;AAAA,QACV,OAAO;AAAA,QACP,SAAS,0BAAgB,OAAO,eAAe;AAAA,MACjD,CAAC;AAAA,IACH;AAAA,EACF;AAGA,MACE,OAAO,gBAAgB,WACtB,OAAO,cAAc,KAAK,OAAO,cAAc,IAChD;AACA,WAAO,KAAK;AAAA,MACV,OAAO;AAAA,MACP,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AAGA,MAAI,OAAO,cAAc,UAAa,OAAO,YAAY,GAAG;AAC1D,WAAO,KAAK;AAAA,MACV,OAAO;AAAA,MACP,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AAGA,MAAI,OAAO,kBAAkB,UAAa,OAAO,gBAAgB,GAAG;AAClE,WAAO,KAAK;AAAA,MACV,OAAO;AAAA,MACP,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AAGA,MAAI,OAAO,gBAAgB,UAAa,OAAO,cAAc,GAAG;AAC9D,WAAO,KAAK;AAAA,MACV,OAAO;AAAA,MACP,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AAGA,MAAI,OAAO,qBAAqB,UAAa,OAAO,mBAAmB,GAAG;AACxE,WAAO,KAAK;AAAA,MACV,OAAO;AAAA,MACP,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AAEA,SAAO;AACT;AAgBA,eAAsB,WAAW,YAAsC;AACrE,QAAM,YAAY,mBAAmB,UAAU;AAE/C,MAAI,SAAiB,gBAAgB,aAAa;AAGlD,aAAW,YAAY,WAAW;AAChC,QAAI;AACF,YAAM,MAAM,MAAM,SAAS,UAAU,OAAO;AAC5C,YAAM,SAAS,KAAK,MAAM,GAAG;AAC7B,eAAS,YAAY,QAAQ,MAAM;AAAA,IACrC,QAAQ;AAAA,IAER;AAAA,EACF;AAGA,WAAS,aAAa,MAAM;AAE5B,SAAO;AACT;AAOA,eAAsB,gBACpB,YACoD;AACpD,QAAM,SAAS,MAAM,WAAW,UAAU;AAC1C,QAAM,SAAS,eAAe,MAAM;AACpC,SAAO,EAAE,QAAQ,OAAO;AAC1B;AAuEA,eAAsB,WAAW,QAAiC;AAChE,QAAM,OAAO,QAAQ,IAAI,QAAQ,QAAQ,IAAI,eAAe;AAC5D,QAAM,YAAY,KAAK,MAAM,UAAU;AACvC,QAAM,aAAa,KAAK,WAAW,eAAe;AAGlD,QAAM,MAAM,WAAW,EAAE,WAAW,KAAK,CAAC;AAG1C,MAAI;AACJ,MAAI;AACF,UAAM,MAAM,MAAM,SAAS,YAAY,OAAO;AAC9C,iBAAa,KAAK,MAAM,GAAG;AAAA,EAC7B,QAAQ;AAEN,iBAAa,gBAAgB,aAAa;AAAA,EAC5C;AAGA,QAAM,YAAa,WAAW,aAA2C,CAAC;AAC1E,QAAM,WAAW,UAAU,KAAK,CAAC,MAAM,EAAE,SAAS,UAAU;AAE5D,MAAI,UAAU;AACZ,aAAS,SAAS;AAAA,EACpB,OAAO;AACL,cAAU,KAAK;AAAA,MACb,MAAM;AAAA,MACN;AAAA,MACA,SAAS;AAAA,MACT,OAAO;AAAA,IACT,CAAC;AAAA,EACH;AAEA,aAAW,YAAY;AAGvB,QAAM,UAAU,YAAY,KAAK,UAAU,YAAY,MAAM,CAAC,GAAG,OAAO;AAExE,SAAO;AACT;AAOA,eAAsB,gBAAgB,OAAgC;AACpE,QAAM,OAAO,QAAQ,IAAI,QAAQ,QAAQ,IAAI,eAAe;AAC5D,QAAM,YAAY,KAAK,MAAM,UAAU;AACvC,QAAM,aAAa,KAAK,WAAW,eAAe;AAGlD,QAAM,MAAM,WAAW,EAAE,WAAW,KAAK,CAAC;AAG1C,MAAI;AACJ,MAAI;AACF,UAAM,MAAM,MAAM,SAAS,YAAY,OAAO;AAC9C,iBAAa,KAAK,MAAM,GAAG;AAAA,EAC7B,QAAQ;AACN,iBAAa,gBAAgB,aAAa;AAAA,EAC5C;AAGA,QAAM,YAAa,WAAW,aAA2C,CAAC;AAC1E,QAAM,sBAAuB,WAAW,mBAA8B;AACtE,QAAM,WAAW,UAAU,KAAK,CAAC,MAAM,EAAE,SAAS,mBAAmB;AAErE,MAAI,UAAU;AACZ,aAAS,QAAQ;AAAA,EACnB,OAAO;AAEL,cAAU,KAAK;AAAA,MACb,MAAM;AAAA,MACN,SAAS;AAAA,MACT;AAAA,IACF,CAAC;AAAA,EACH;AAEA,aAAW,YAAY;AAGvB,QAAM,UAAU,YAAY,KAAK,UAAU,YAAY,MAAM,CAAC,GAAG,OAAO;AAExE,SAAO;AACT;AAOA,eAAsB,gBAAgB,SAAkD;AACtF,QAAM,OAAO,QAAQ,IAAI,QAAQ,QAAQ,IAAI,eAAe;AAC5D,QAAM,YAAY,KAAK,MAAM,UAAU;AACvC,QAAM,aAAa,KAAK,WAAW,eAAe;AAGlD,QAAM,MAAM,WAAW,EAAE,WAAW,KAAK,CAAC;AAG1C,MAAI;AACJ,MAAI;AACF,UAAM,MAAM,MAAM,SAAS,YAAY,OAAO;AAC9C,iBAAa,KAAK,MAAM,GAAG;AAAA,EAC7B,QAAQ;AACN,iBAAa,gBAAgB,aAAa;AAAA,EAC5C;AAGA,aAAW,QAAQ,EAAE,QAAQ;AAG7B,QAAM,UAAU,YAAY,KAAK,UAAU,YAAY,MAAM,CAAC,GAAG,OAAO;AAExE,SAAO;AACT;;;AC/jBA,eAAsB,uBAEK;AACzB,QAAM,OAAO,KAAK,gBAAgB;AAKlC,QAAM,UAAU,KAAK,WAAW;AAGhC,MAAI;AACJ,QAAM,SAAmB,CAAC;AAC1B,MAAI;AACF,UAAM,SAAS,MAAM,gBAAgB,KAAK,MAAM;AAChD,aAAS,OAAO;AAChB,QAAI,OAAO,OAAO,SAAS,GAAG;AAC5B,iBAAW,KAAK,OAAO,QAAQ;AAC7B,eAAO,KAAK,EAAE,OAAO;AAAA,MACvB;AAAA,IACF;AAAA,EACF,QAAQ;AACN,aAAS,gBAAgB,aAAa;AAAA,EACxC;AAGA,WAAS,kBAAkB,QAAQ;AAAA,IACjC;AAAA,IACA,OAAO,KAAK;AAAA,EACd,CAAC;AAGD,MAAI,OAAO,SAAS,KAAK,SAAS;AAChC,eAAW,OAAO,QAAQ;AACxB,cAAQ,MAAM,YAAO,GAAG,EAAE;AAAA,IAC5B;AAAA,EACF;AAEA,SAAO,EAAE,QAAQ,QAAQ;AAC3B;;;ACjEA,OAAO,WAAW;AAEX,SAAS,WAAWC,UAA0B;AACnD,QAAM,QAAkB,CAAC;AAEzB,QAAM,KAAK,EAAE;AACb,QAAM,KAAK,MAAM,KAAK,eAAK,CAAC;AAC5B,QAAM,KAAK,KAAK,MAAM,KAAK,SAAS,CAAC,IAAI,MAAM,IAAI,kBAAkB,CAAC,IAAI,MAAM,MAAM,WAAW,CAAC,IAAI,MAAM,IAAI,WAAW,CAAC,EAAE;AAC9H,QAAM,KAAK,EAAE;AAEb,QAAM,aAAaA,SAAQ,QAAQ;AAAA,IACjC,CAAC,MAAM,EAAE,SAAS,YAAY,EAAE,SAAS,eAAe,EAAE,SAAS;AAAA,EACrE;AACA,MAAI,WAAW,SAAS,GAAG;AACzB,UAAM,KAAK,MAAM,KAAK,2BAAO,CAAC;AAC9B,eAAW,OAAO,YAAY;AAC5B,YAAM,QAAQ,CAAC,IAAI,OAAO,IAAI,IAAI,EAAE,OAAO,OAAO,EAAE,KAAK,IAAI;AAC7D,YAAM,KAAK,KAAK,MAAM,KAAK,MAAM,OAAO,EAAE,CAAC,CAAC,IAAI,IAAI,eAAe,EAAE,EAAE;AAAA,IACzE;AACA,UAAM,KAAK,EAAE;AAAA,EACf;AAEA,QAAM,KAAK,MAAM,KAAK,2BAAO,CAAC;AAC9B,aAAW,QAAQ,CAAC,cAAc,eAAe,GAAG;AAClD,UAAM,MAAMA,SAAQ,QAAQ;AAAA,MAC1B,CAAC,MAAM,EAAE,UAAU,KAAK,SAAS,MAAM,IAAI,WAAW;AAAA,IACxD;AACA,QAAI,KAAK;AACP,YAAM,KAAK,KAAK,MAAM,KAAK,KAAK,OAAO,EAAE,CAAC,CAAC,IAAI,IAAI,eAAe,EAAE,EAAE;AAAA,IACxE;AAAA,EACF;AACA,QAAM,KAAK,EAAE;AAEb,QAAM,OAAOA,SAAQ,SAAS,OAAO,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,WAAW,MAAM,CAAC;AACxE,MAAI,KAAK,SAAS,GAAG;AACnB,UAAM,KAAK,MAAM,KAAK,eAAK,CAAC;AAC5B,eAAW,OAAO,MAAM;AACtB,YAAM,KAAK,KAAK,MAAM,MAAM,IAAI,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC,IAAI,IAAI,YAAY,CAAC,EAAE;AAAA,IAC3E;AACA,UAAM,KAAK,EAAE;AAAA,EACf;AAEA,QAAM,KAAK,MAAM,KAAK,eAAK,CAAC;AAC5B,QAAM,KAAK,KAAK,MAAM,IAAI,8CAAW,CAAC,EAAE;AACxC,QAAM,KAAK,gBAAgB;AAC3B,QAAM,KAAK,KAAK,MAAM,IAAI,kDAAe,CAAC,EAAE;AAC5C,QAAM,KAAK,0DAA4B;AACvC,QAAM,KAAK,KAAK,MAAM,IAAI,wCAAU,CAAC,EAAE;AACvC,QAAM,KAAK,iBAAiB;AAC5B,QAAM,KAAK,KAAK,MAAM,IAAI,+CAAiB,CAAC,EAAE;AAC9C,QAAM,KAAK,sBAAsB;AACjC,QAAM,KAAK,KAAK,MAAM,IAAI,8CAAW,CAAC,EAAE;AACxC,QAAM,KAAK,iBAAiB;AAC5B,QAAM,KAAK,KAAK,MAAM,IAAI,oDAAY,CAAC,EAAE;AACzC,QAAM,KAAK,mCAAmC;AAC9C,QAAM,KAAK,EAAE;AAEb,SAAO,MAAM,KAAK,IAAI;AACxB;;;AC3DA,SAAS,uBAAuB;AAChC,OAAOC,YAAW;AAMX,SAAS,UAAU,WAA2C;AACnE,MAAI,UAAU,KAAK,CAAC,MAAM,EAAE,MAAM,EAAG,QAAO;AAC5C,MAAI,QAAQ,IAAI,iBAAkB,QAAO;AACzC,SAAO;AACT;AAMA,IAAM,qBAAqB;AAO3B,eAAsB,kBAA0C;AAC9D,UAAQ,IAAIA,OAAM,OAAO,0DAAuB,CAAC;AACjD,UAAQ,IAAIA,OAAM,IAAI,wFAAkB,CAAC;AACzC,UAAQ,IAAIA,OAAM,IAAI,mEAA4C,CAAC;AACnE,UAAQ,IAAIA,OAAM,IAAI,6DAAsC,CAAC;AAC7D,UAAQ,IAAIA,OAAM,IAAI,6GAA0B,CAAC;AAEjD,QAAM,KAAK,gBAAgB;AAAA,IACzB,OAAO,QAAQ;AAAA,IACf,QAAQ,QAAQ;AAAA,EAClB,CAAC;AAED,SAAO,IAAI,QAAuB,CAACC,aAAY;AAC7C,QAAI,WAAW;AAEf,UAAM,UAAU,MAAM;AACpB,UAAI,SAAU;AACd,iBAAW;AACX,cAAQ,MAAM,eAAe,YAAY,UAAU;AACnD,SAAG,MAAM;AAAA,IACX;AAEA,UAAM,aAAa,CAAC,GAAY,QAA2C;AACzE,UAAI,IAAI,QAAQ,IAAI,SAAS,KAAK;AAChC,gBAAQ;AACR,QAAAA,SAAQ,IAAI;AAAA,MACd;AAAA,IACF;AAEA,YAAQ,MAAM,GAAG,YAAY,UAAU;AAEvC,OAAG;AAAA,MACD,KAAKD,OAAM,KAAK,WAAI,CAAC,IAAIA,OAAM,KAAK,kDAAyB,CAAC;AAAA,MAC9D,CAAC,WAAW;AACV,gBAAQ;AACR,cAAM,UAAU,OAAO,KAAK;AAC5B,YAAI,CAAC,SAAS;AACZ,kBAAQ,IAAIA,OAAM,IAAI,2CAAkB,CAAC;AACzC,UAAAC,SAAQ,IAAI;AACZ;AAAA,QACF;AACA,YAAI,QAAQ,SAAS,oBAAoB;AACvC,kBAAQ,IAAID,OAAM,IAAI,yFAA6B,CAAC;AACpD,UAAAC,SAAQ,IAAI;AACZ;AAAA,QACF;AACA,QAAAA,SAAQ,OAAO;AAAA,MACjB;AAAA,IACF;AAAA,EACF,CAAC;AACH;;;AC1EA,SAAS,mBAAAC,wBAAuB;AAChC,SAAS,cAAAC,aAAY,UAAU,iBAAiB;AAChD,SAAS,SAAAC,QAAO,SAAS,IAAI,QAAQ,UAAU,YAAAC,iBAAgB;AAC/D,SAAS,QAAAC,aAAY;AACrB,OAAOC,YAAW;AAMlB,SAAS,qBAA6B;AACpC,QAAM,OAAO,QAAQ,IAAI,QAAQ,QAAQ,IAAI,eAAe;AAC5D,SAAOD,MAAK,MAAM,WAAW,QAAQ;AACvC;AAKA,SAAS,sBAA8B;AACrC,QAAM,OAAO,QAAQ,IAAI,QAAQ,QAAQ,IAAI,eAAe;AAC5D,SAAOA,MAAK,MAAM,YAAY,QAAQ;AACxC;AAKA,eAAe,WAAW,KAA+B;AACvD,MAAI;AACF,UAAM,OAAOA,MAAK,KAAK,UAAU,CAAC;AAClC,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAMA,eAAsB,mBAAsC;AAC1D,QAAM,YAAY,mBAAmB;AACrC,MAAI,CAACH,YAAW,SAAS,EAAG,QAAO,CAAC;AAEpC,MAAI;AACJ,MAAI;AACF,cAAU,MAAM,QAAQ,SAAS;AAAA,EACnC,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AAEA,QAAM,SAAmB,CAAC;AAC1B,aAAW,QAAQ,SAAS;AAC1B,UAAM,OAAOG,MAAK,WAAW,IAAI;AACjC,UAAME,QAAO,SAAS,IAAI;AAC1B,QAAIA,MAAK,YAAY,KAAM,MAAM,WAAW,IAAI,GAAI;AAClD,aAAO,KAAK,IAAI;AAAA,IAClB;AAAA,EACF;AACA,SAAO;AACT;AAcA,SAAS,WAAW,UAAoC;AACtD,QAAM,KAAKC,iBAAgB;AAAA,IACzB,OAAO,QAAQ;AAAA,IACf,QAAQ,QAAQ;AAAA,EAClB,CAAC;AAED,SAAO,IAAI,QAAiB,CAACC,aAAY;AACvC,QAAI,WAAW;AAEf,UAAM,UAAU,MAAM;AACpB,UAAI,SAAU;AACd,iBAAW;AACX,cAAQ,MAAM,eAAe,YAAY,UAAU;AACnD,SAAG,MAAM;AAAA,IACX;AAEA,UAAM,aAAa,CAAC,GAAY,QAA2C;AACzE,UAAI,IAAI,QAAQ,IAAI,SAAS,KAAK;AAChC,gBAAQ;AACR,QAAAA,SAAQ,KAAK;AAAA,MACf;AAAA,IACF;AAEA,YAAQ,MAAM,GAAG,YAAY,UAAU;AAEvC,OAAG,SAAS,UAAU,CAAC,WAAW;AAChC,cAAQ;AACR,YAAM,UAAU,OAAO,KAAK,EAAE,YAAY;AAE1C,MAAAA,SAAQ,YAAY,OAAO,YAAY,KAAK;AAAA,IAC9C,CAAC;AAAA,EACH,CAAC;AACH;AAOA,eAAsB,mBACpB,YACoD;AACpD,QAAM,YAAY,mBAAmB;AACrC,QAAM,aAAa,oBAAoB;AAEvC,QAAMC,OAAM,YAAY,EAAE,WAAW,KAAK,CAAC;AAE3C,QAAM,WAAqB,CAAC;AAC5B,QAAM,UAAoB,CAAC;AAE3B,aAAW,QAAQ,YAAY;AAC7B,UAAM,MAAMC,MAAK,WAAW,IAAI;AAChC,UAAM,OAAOA,MAAK,YAAY,IAAI;AAElC,QAAIC,YAAW,IAAI,GAAG;AACpB,cAAQ,KAAK,IAAI;AACjB;AAAA,IACF;AAQA,UAAM,UAAU,UAAU,GAAG,EAAE,eAAe,IAAI,MAAM,SAAS,GAAG,IAAI;AACxE,UAAM,GAAG,SAAS,MAAM,EAAE,WAAW,MAAM,aAAa,KAAK,CAAC;AAC9D,aAAS,KAAK,IAAI;AAAA,EACpB;AAEA,SAAO,EAAE,UAAU,QAAQ;AAC7B;AAKA,eAAsB,qBAAsC;AAC1D,QAAM,aAAa,oBAAoB;AACvC,MAAI,CAACA,YAAW,UAAU,EAAG,QAAO;AAEpC,MAAI;AACJ,MAAI;AACF,cAAU,MAAM,QAAQ,UAAU;AAAA,EACpC,QAAQ;AACN,WAAO;AAAA,EACT;AAEA,MAAI,QAAQ;AACZ,aAAW,QAAQ,SAAS;AAC1B,UAAM,OAAOD,MAAK,YAAY,IAAI;AAClC,QAAI,SAAS,IAAI,EAAE,YAAY,KAAM,MAAM,WAAW,IAAI,GAAI;AAC5D;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAKO,SAAS,mBAAmB,KAAqB;AACtD,SAAOA,MAAK,KAAK,YAAY,OAAO;AACtC;AAKA,eAAsB,sBAAsB,KAA+B;AACzE,QAAM,QAAQ,MAAM,wBAAwB,GAAG;AAC/C,SAAO,QAAQ;AACjB;AAKA,eAAsB,wBAAwB,KAA8B;AAC1E,QAAM,WAAW,mBAAmB,GAAG;AACvC,MAAI,CAACC,YAAW,QAAQ,EAAG,QAAO;AAElC,MAAI;AACJ,MAAI;AACF,cAAU,MAAM,QAAQ,QAAQ;AAAA,EAClC,QAAQ;AACN,WAAO;AAAA,EACT;AAEA,MAAI,QAAQ;AACZ,aAAW,QAAQ,SAAS;AAC1B,UAAM,OAAOD,MAAK,UAAU,IAAI;AAChC,UAAME,QAAO,SAAS,IAAI;AAC1B,QAAIA,MAAK,YAAY,KAAM,MAAM,WAAW,IAAI,GAAI;AAClD;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAKA,eAAsB,mBAAqC;AACzD,QAAM,aAAa,oBAAoB;AACvC,MAAI,CAACD,YAAW,UAAU,EAAG,QAAO;AAEpC,MAAI;AACJ,MAAI;AACF,cAAU,MAAM,QAAQ,UAAU;AAAA,EACpC,QAAQ;AACN,WAAO;AAAA,EACT;AAEA,aAAW,QAAQ,SAAS;AAC1B,UAAM,OAAOD,MAAK,YAAY,IAAI;AAClC,UAAME,QAAO,SAAS,IAAI;AAC1B,QAAIA,MAAK,YAAY,KAAM,MAAM,WAAW,IAAI,GAAI;AAClD,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AAWA,eAAsB,yBAAyB,KAA6B;AAE1E,MAAI,KAAK;AACP,UAAM,WAAW,MAAM,sBAAsB,GAAG;AAChD,QAAI,UAAU;AACZ,cAAQ,IAAIC,OAAM,IAAI,+GAAmD,CAAC;AAC1E;AAAA,IACF;AAAA,EACF;AAGA,QAAM,YAAY,MAAM,iBAAiB;AACzC,MAAI,WAAW;AACb;AAAA,EACF;AAEA,QAAM,SAAS,MAAM,iBAAiB;AACtC,MAAI,OAAO,WAAW,EAAG;AAEzB,UAAQ,IAAIA,OAAM,KAAK;AAAA,+EAAgC,OAAO,OAAO,MAAM,CAAC,qBAAW,CAAC;AAExF,QAAM,UAAU,OAAO,MAAM,GAAG,CAAC,EAAE,IAAI,CAAC,MAAMA,OAAM,IAAI,YAAS,CAAC,EAAE,CAAC,EAAE,KAAK,IAAI;AAChF,UAAQ,IAAI,OAAO;AACnB,MAAI,OAAO,SAAS,GAAG;AACrB,YAAQ,IAAIA,OAAM,IAAI,sBAAc,OAAO,OAAO,MAAM,CAAC,SAAI,CAAC;AAAA,EAChE;AACA,UAAQ,IAAIA,OAAM,IAAI,yBAAU,mBAAmB,CAAC,EAAE,CAAC;AACvD,UAAQ,IAAIA,OAAM,IAAI,+BAAW,oBAAoB,CAAC;AAAA,CAAI,CAAC;AAE3D,QAAM,YAAY,MAAM;AAAA,IACtB,KAAKA,OAAM,KAAK,WAAI,CAAC,yEAA4BA,OAAM,IAAI,QAAQ,CAAC;AAAA,EACtE;AAEA,MAAI,CAAC,WAAW;AACd,YAAQ,IAAIA,OAAM,IAAI,2CAAkB,CAAC;AACzC;AAAA,EACF;AAEA,MAAI;AACF,UAAM,EAAE,UAAU,QAAQ,IAAI,MAAM,mBAAmB,MAAM;AAC7D,QAAI,SAAS,SAAS,GAAG;AACvB,cAAQ;AAAA,QACNA,OAAM,MAAM,+BAAW,OAAO,SAAS,MAAM,CAAC,wCAA8B;AAAA,MAC9E;AAAA,IACF;AACA,QAAI,QAAQ,SAAS,GAAG;AACtB,cAAQ;AAAA,QACNA,OAAM,OAAO,YAAO,OAAO,QAAQ,MAAM,CAAC,iEAAoB,QAAQ,KAAK,IAAI,CAAC,EAAE;AAAA,MACpF;AAAA,IACF;AACA,YAAQ,IAAI,EAAE;AAAA,EAChB,SAAS,KAAK;AACZ,YAAQ;AAAA,MACNA,OAAM,IAAI,6CAAoB,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC,EAAE;AAAA,IAClF;AACA,YAAQ,IAAIA,OAAM,IAAI,sGAAoD,CAAC;AAAA,EAC7E;AACF;AAWA,eAAsB,oBAAuC;AAC3D,QAAM,aAAa,oBAAoB;AACvC,MAAI,CAACF,YAAW,UAAU,EAAG,QAAO,CAAC;AAErC,MAAI;AACJ,MAAI;AACF,cAAU,MAAM,QAAQ,UAAU;AAAA,EACpC,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AAEA,QAAM,SAAmB,CAAC;AAC1B,aAAW,QAAQ,SAAS;AAC1B,UAAM,OAAOD,MAAK,YAAY,IAAI;AAClC,QAAI,SAAS,IAAI,EAAE,YAAY,KAAM,MAAM,WAAW,IAAI,GAAI;AAC5D,aAAO,KAAK,IAAI;AAAA,IAClB;AAAA,EACF;AACA,SAAO;AACT;AAKA,eAAsB,kBAAkB,KAAgC;AACtE,QAAM,WAAW,mBAAmB,GAAG;AACvC,MAAI,CAACC,YAAW,QAAQ,EAAG,QAAO,CAAC;AAEnC,MAAI;AACJ,MAAI;AACF,cAAU,MAAM,QAAQ,QAAQ;AAAA,EAClC,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AAEA,QAAM,SAAmB,CAAC;AAC1B,aAAW,QAAQ,SAAS;AAC1B,UAAM,OAAOD,MAAK,UAAU,IAAI;AAChC,QAAI,SAAS,IAAI,EAAE,YAAY,KAAM,MAAM,WAAW,IAAI,GAAI;AAC5D,aAAO,KAAK,IAAI;AAAA,IAClB;AAAA,EACF;AACA,SAAO;AACT;AAMA,eAAe,cAAc,UAA6C;AACxE,MAAI;AACF,UAAM,UAAU,MAAMI,UAASJ,MAAK,UAAU,UAAU,GAAG,OAAO;AAClE,UAAM,QAAQ,QAAQ,MAAM,uBAAuB;AACnD,QAAI,CAAC,MAAO,QAAO;AAEnB,UAAM,cAAc,MAAM,CAAC;AAC3B,UAAM,YAAY,YAAY,MAAM,iBAAiB;AACrD,UAAM,YAAY,YAAY,MAAM,wBAAwB;AAE5D,WAAO;AAAA,MACL,MAAM,YAAY,CAAC,GAAG,KAAK,KAAK;AAAA,MAChC,aAAa,YAAY,CAAC,GAAG,KAAK,KAAK;AAAA,IACzC;AAAA,EACF,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAMA,eAAsB,aAAa,KAAmC;AACpE,QAAM,CAAC,aAAa,UAAU,IAAI,MAAM,QAAQ,IAAI;AAAA,IAClD,kBAAkB;AAAA,IAClB,kBAAkB,GAAG;AAAA,EACvB,CAAC;AAGD,QAAM,OAAO,oBAAI,IAAY;AAC7B,QAAM,WAAqB,CAAC;AAC5B,aAAW,QAAQ,CAAC,GAAG,YAAY,GAAG,WAAW,GAAG;AAClD,QAAI,CAAC,KAAK,IAAI,IAAI,GAAG;AACnB,WAAK,IAAI,IAAI;AACb,eAAS,KAAK,IAAI;AAAA,IACpB;AAAA,EACF;AAEA,QAAM,aAAa,oBAAoB;AACvC,QAAM,aAAa,mBAAmB,GAAG;AAEzC,QAAM,UAAuB,CAAC;AAC9B,aAAW,QAAQ,UAAU;AAE3B,UAAM,YAAYA,MAAK,YAAY,IAAI;AACvC,QAAI,OAAyB;AAC7B,QAAIC,YAAW,SAAS,GAAG;AACzB,aAAO,MAAM,cAAc,SAAS;AAAA,IACtC;AACA,QAAI,CAAC,MAAM;AACT,YAAM,aAAaD,MAAK,YAAY,IAAI;AACxC,UAAIC,YAAW,UAAU,GAAG;AAC1B,eAAO,MAAM,cAAc,UAAU;AAAA,MACvC;AAAA,IACF;AACA,YAAQ,KAAK,QAAQ,EAAE,MAAM,aAAa,GAAG,CAAC;AAAA,EAChD;AAEA,SAAO;AACT;;;AL7ZA,SAAS,YAAAI,kBAAgB;AACzB,SAAS,QAAAC,aAAY;;;AMRrB,SAAS,cAAc;AAShB,SAAS,UAAU,MAAoC;AAE5D,QAAM,EAAE,eAAe,OAAO,QAAQ,IAAI,OAAO,MAAM,EAAE,aAAa,MAAM,CAAC;AAC7E,SAAO,EAAE,eAAe,cAAc,GAAG,OAAO,QAAQ;AAC1D;;;ACbA,SAAS,YAAY;AACrB,OAAO,gBAAgB;AAWf,cAEO,YAFP;AAJD,SAAS,QAAQ,EAAE,OAAO,QAAQ,MAAM,GAAiB;AAC9D,SACE,qBAAC,QACC;AAAA,wBAAC,QAAK,OAAM,QACV,8BAAC,cAAW,MAAY,GAC1B;AAAA,IACC,QAAQ,qBAAC,QAAK;AAAA;AAAA,MAAE;AAAA,OAAM,IAAU;AAAA,KACnC;AAEJ;;;ACjBA,SAAS,KAAK,QAAAC,aAAY;AAqBpB,iBAAAC,aAAA;;;ACrBN,SAAS,OAAAC,MAAK,QAAAC,aAAY;AAC1B,SAAS,WAAW,gBAAgB;AA+BxB,gBAAAC,YAAA;AA5BL,IAAM,gBAAgB,CAAC,WAAW,WAAW,WAAW,WAAW,SAAS;AAG5E,IAAM,aAAa;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;;;ACVA,SAAS,OAAAC,MAAK,QAAAC,QAAM,UAAU,cAAc;AAC5C,OAAO,eAAe;AACtB,SAAS,aAAAC,YAAW,YAAAC,WAAU,eAAAC,cAAa,SAAS,UAAAC,eAAc;;;ACNlE,SAAS,YAAAC,WAAU,aAAa,aAAAC,YAAW,cAAc;AAEzD,IAAM,oBAAoB;AAWnB,SAAS,eAAe,QAAoB;AACjD,QAAM,CAAC,aAAa,cAAc,IAAID,UAAS,KAAK;AACpD,QAAM,WAAW,OAA6C,IAAI;AAClE,QAAM,YAAY,OAAO,MAAM;AAC/B,YAAU,UAAU;AAGpB,EAAAC,WAAU,MAAM;AACd,WAAO,MAAM;AACX,UAAI,SAAS,QAAS,cAAa,SAAS,OAAO;AAAA,IACrD;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,QAAM,cAAc,YAAY,MAAM;AACpC,QAAI,aAAa;AAEf,gBAAU,QAAQ;AAClB;AAAA,IACF;AAGA,mBAAe,IAAI;AACnB,QAAI,SAAS,QAAS,cAAa,SAAS,OAAO;AACnD,aAAS,UAAU,WAAW,MAAM;AAClC,qBAAe,KAAK;AACpB,eAAS,UAAU;AAAA,IACrB,GAAG,iBAAiB;AAAA,EACtB,GAAG,CAAC,WAAW,CAAC;AAEhB,SAAO,EAAE,aAAa,YAAY;AACpC;;;ADjCA,OAAOC,iBAAgB;;;AENvB,SAAS,OAAAC,MAAK,QAAAC,aAAY;;;ACA1B,SAAS,SAAAC,QAAO,YAAAC,WAAU,aAAAC,kBAAiB;AAC3C,SAAS,QAAAC,aAAY;AAiHd,IAAM,cAAN,MAAkB;AAAA,EACd;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGT;AAAA,EACA;AAAA,EACS,kBAAgC,CAAC;AAAA;AAAA,EAG1C;AAAA,EACA;AAAA;AAAA,EAGA,SAAS;AAAA,EACT,mBAAmB;AAAA,EAEnB,YAAY,UAA8B,CAAC,GAAG;AAC5C,UAAM,OAAO,QAAQ,IAAI,QAAQ,QAAQ,IAAI,eAAe;AAC5D,SAAK,WAAW,QAAQ,WAAWC,MAAK,MAAM,YAAY,OAAO;AACjE,SAAK,eAAe,QAAQ,eAAe;AAC3C,SAAK,oBAAoB,QAAQ,oBAAoB;AACrD,SAAK,oBAAoB,QAAQ;AAGjC,SAAK,aAAa,kBAAkB;AACpC,SAAK,qBAAoB,oBAAI,KAAK,GAAE,YAAY;AAGhD,UAAM,QAAQ,YAAY;AAC1B,SAAK,aAAa;AAClB,SAAK,gBAAgB,wBAAwB,KAAK;AAAA,EACpD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,OAAO,OAAkB,OAA0B;AACjD,UAAM,OAAO,cAAc,OAAO,KAAK;AACvC,UAAM,SAAqB;AAAA,MACzB,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,MAClC;AAAA,MACA,OAAO,EAAE,GAAG,MAAM;AAAA,MAClB,MAAM,EAAE,GAAG,KAAK;AAAA,IAClB;AAGA,SAAK,gBAAgB,KAAK,MAAM;AAGhC,SAAK,mBAAmB;AACxB,SAAK,YAAY,MAAM;AAGvB,SAAK,SAAS;AAGd,SAAK,aAAa;AAElB,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,IAAI,YAAoB;AACtB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,IAAI,UAAiC;AACnC,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,IAAI,iBAAqC;AACvC,QAAI,oBAAoB;AACxB,QAAI,wBAAwB;AAC5B,QAAI,oBAAoB;AACxB,QAAI,YAAY;AAEhB,eAAW,KAAK,KAAK,iBAAiB;AACpC,2BAAqB,EAAE,MAAM;AAC7B,+BAAyB,EAAE,MAAM;AACjC,2BAAqB,EAAE,MAAM,sBAAsB;AACnD,mBAAa,EAAE,KAAK;AAAA,IACtB;AAEA,WAAO;AAAA,MACL,WAAW,KAAK;AAAA,MAChB,WAAW,KAAK;AAAA,MAChB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,SAAS,CAAC,GAAG,KAAK,eAAe;AAAA,IACnC;AAAA,EACF;AAAA;AAAA,EAGA,IAAI,mBAA2B;AAC7B,WAAO,KAAK,gBAAgB,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,KAAK,WAAW,CAAC;AAAA,EAC1E;AAAA;AAAA,EAGA,IAAI,sBAA8B;AAChC,WAAO,KAAK,gBAAgB,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,MAAM,cAAc,CAAC;AAAA,EAC9E;AAAA;AAAA,EAGA,IAAI,0BAAkC;AACpC,WAAO,KAAK,gBAAgB,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAAA,EAClF;AAAA;AAAA,EAGA,IAAI,mBAA2B;AAC7B,WAAO,KAAK,gBAAgB;AAAA,EAC9B;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,IAAI,eAAiC;AACnC,SAAK,mBAAmB;AACxB,WAAO,EAAE,GAAG,KAAK,cAAc;AAAA,EACjC;AAAA;AAAA,EAGA,IAAI,iBAAyB;AAC3B,SAAK,mBAAmB;AACxB,WAAO,KAAK,cAAc;AAAA,EAC5B;AAAA;AAAA,EAGA,IAAI,iBAAyB;AAC3B,SAAK,mBAAmB;AACxB,WAAO,KAAK,cAAc;AAAA,EAC5B;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,IAAI,mBAA4B;AAC9B,QAAI,KAAK,eAAe,KAAK,KAAK,kBAAkB,KAAK,cAAc;AACrE,aAAO;AAAA,IACT;AACA,QAAI,KAAK,oBAAoB,GAAG;AAC9B,YAAM,cACJ,KAAK,cAAc,oBACnB,KAAK,cAAc;AACrB,UAAI,eAAe,KAAK,kBAAmB,QAAO;AAAA,IACpD;AACA,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,IAAI,kBAA0B;AAC5B,QAAI,KAAK,gBAAgB,EAAG,QAAO;AACnC,WAAO,KAAK,IAAI,GAAG,KAAK,eAAe,KAAK,cAAc;AAAA,EAC5D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAM,OAAsB;AAC1B,UAAM,QAAQ,MAAM,KAAK,WAAW;AACpC,UAAM,QAAQ,YAAY;AAE1B,QAAI,MAAM,MAAM,KAAK,GAAG;AACtB,WAAK,gBAAgB,MAAM,MAAM,KAAK;AACtC,WAAK,aAAa;AAAA,IACpB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,QAAuB;AAC3B,QAAI,CAAC,KAAK,UAAU,KAAK,iBAAkB;AAC3C,SAAK,mBAAmB;AACxB,SAAK,SAAS;AAEd,QAAI;AACF,YAAM,KAAK,WAAW;AAAA,IACxB,UAAE;AACA,WAAK,mBAAmB;AAAA,IAC1B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,WACJ,WACA,SAC6B;AAC7B,UAAM,QAAQ,MAAM,KAAK,WAAW;AACpC,UAAM,MAAM,WAAW;AACvB,UAAM,SAA6B,CAAC;AAEpC,eAAW,CAAC,MAAM,OAAO,KAAK,OAAO,QAAQ,MAAM,KAAK,GAAG;AACzD,UAAI,QAAQ,aAAa,QAAQ,KAAK;AACpC,eAAO,KAAK,OAAO;AAAA,MACrB;AAAA,IACF;AAEA,WAAO,KAAK,CAAC,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,IAAI,CAAC;AAClD,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,eAAqB;AACnB,SAAK,aAAa,kBAAkB;AACpC,SAAK,qBAAoB,oBAAI,KAAK,GAAE,YAAY;AAChD,SAAK,gBAAgB,SAAS;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,qBAA2B;AACzB,UAAM,QAAQ,YAAY;AAC1B,QAAI,UAAU,KAAK,YAAY;AAE7B,WAAK,aAAa;AAClB,WAAK,gBAAgB,wBAAwB,KAAK;AAClD,WAAK,SAAS;AAAA,IAChB;AAAA,EACF;AAAA;AAAA,EAGA,YAAY,QAA0B;AACpC,SAAK,cAAc,qBAAqB,OAAO,MAAM;AACrD,SAAK,cAAc,yBAAyB,OAAO,MAAM;AACzD,SAAK,cAAc,qBAAqB,OAAO,MAAM,sBAAsB;AAC3E,SAAK,cAAc,aAAa,OAAO,KAAK;AAC5C,SAAK,cAAc,cAAc;AAGjC,UAAM,WAAW,OAAO;AACxB,QAAI,CAAC,KAAK,cAAc,QAAQ,QAAQ,GAAG;AACzC,WAAK,cAAc,QAAQ,QAAQ,IAAI;AAAA,QACrC,OAAO,OAAO;AAAA,QACd,mBAAmB;AAAA,QACnB,uBAAuB;AAAA,QACvB,mBAAmB;AAAA,QACnB,WAAW;AAAA,QACX,YAAY;AAAA,MACd;AAAA,IACF;AAEA,UAAM,eAAe,KAAK,cAAc,QAAQ,QAAQ;AACxD,iBAAa,qBAAqB,OAAO,MAAM;AAC/C,iBAAa,yBAAyB,OAAO,MAAM;AACnD,iBAAa,qBAAqB,OAAO,MAAM,sBAAsB;AACrE,iBAAa,aAAa,OAAO,KAAK;AACtC,iBAAa,cAAc;AAAA,EAC7B;AAAA;AAAA,EAGA,eAAqB;AACnB,QAAI,CAAC,KAAK,iBAAkB;AAC5B,SAAK,oBAAoB,IAAI;AAAA,EAC/B;AAAA;AAAA,EAGA,MAAM,aAAiC;AACrC,UAAM,WAAWA,MAAK,KAAK,UAAU,cAAc;AACnD,QAAI;AACF,YAAM,MAAM,MAAMC,UAAS,UAAU,OAAO;AAC5C,aAAO,KAAK,MAAM,GAAG;AAAA,IACvB,QAAQ;AACN,aAAO,EAAE,SAAS,GAAG,OAAO,CAAC,EAAE;AAAA,IACjC;AAAA,EACF;AAAA;AAAA,EAGA,MAAM,aAA4B;AAChC,UAAM,QAAQ,MAAM,KAAK,WAAW;AAGpC,UAAM,MAAM,KAAK,UAAU,IAAI,KAAK;AAGpC,UAAM,aAAa,gBAAgB,EAAE;AACrC,UAAM,gBAAgB,OAAO,KAAK,MAAM,KAAK,EAAE;AAAA,MAC7C,CAAC,SAAS,OAAO;AAAA,IACnB;AACA,eAAW,QAAQ,eAAe;AAEhC,aAAO,MAAM,MAAM,IAAI;AAAA,IACzB;AAGA,UAAMC,OAAM,KAAK,UAAU,EAAE,WAAW,KAAK,CAAC;AAE9C,UAAM,WAAWF,MAAK,KAAK,UAAU,cAAc;AACnD,UAAMG,WAAU,UAAU,KAAK,UAAU,OAAO,MAAM,CAAC,GAAG,OAAO;AAAA,EACnE;AACF;AAOA,SAAS,oBAA4B;AACnC,QAAM,KAAK,KAAK,IAAI,EAAE,SAAS,EAAE;AACjC,QAAM,OAAO,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,GAAG,CAAC;AAClD,SAAO,GAAG,EAAE,IAAI,IAAI;AACtB;AAGA,SAAS,cAAsB;AAC7B,UAAO,oBAAI,KAAK,GAAE,YAAY,EAAE,MAAM,GAAG,EAAE;AAC7C;AAGA,SAAS,gBAAgB,GAAmB;AAC1C,QAAM,IAAI,oBAAI,KAAK;AACnB,IAAE,QAAQ,EAAE,QAAQ,IAAI,CAAC;AACzB,SAAO,EAAE,YAAY,EAAE,MAAM,GAAG,EAAE;AACpC;AAGA,SAAS,wBAAwB,MAAgC;AAC/D,SAAO;AAAA,IACL;AAAA,IACA,mBAAmB;AAAA,IACnB,uBAAuB;AAAA,IACvB,mBAAmB;AAAA,IACnB,WAAW;AAAA,IACX,YAAY;AAAA,IACZ,SAAS,CAAC;AAAA,EACZ;AACF;AAOO,SAAS,YAAY,MAAsB;AAChD,SAAO,OAAI,KAAK,QAAQ,CAAC,CAAC;AAC5B;;;ACjeA,SAAS,OAAAC,MAAK,QAAAC,aAAY;AA2ClB,SAIA,OAAAC,MAJA,QAAAC,aAAA;AA9BR,SAAS,kBAAkB,MAAsB;AAC/C,MAAI;AACF,UAAM,SAAS,KAAK,MAAM,IAAI;AAC9B,UAAM,QAAQ,OAAO,QAAQ,MAAM,EAAE,IAAI,CAAC,CAAC,KAAK,KAAK,MAAM;AACzD,YAAM,MAAM,OAAO,KAAK;AACxB,YAAM,YAAY,IAAI,SAAS,KAAK,IAAI,MAAM,GAAG,EAAE,IAAI,QAAQ;AAC/D,aAAO,QAAQ,GAAG,KAAK,SAAS;AAAA,IAClC,CAAC;AACD,WAAO,MAAM,KAAK,IAAI;AAAA,EACxB,QAAQ;AAEN,UAAM,YAAY,KAAK,SAAS,MAAM,KAAK,MAAM,GAAG,GAAG,IAAI,QAAQ;AACnE,WAAO,QAAQ,SAAS;AAAA,EAC1B;AACF;AAUO,SAAS,cAAc,EAAE,MAAM,kBAAkB,KAAK,GAAuB;AAClF,QAAM,cAAc,kBAAkB,KAAK,SAAS;AAEpD,SACE,gBAAAA,MAACH,MAAA,EAAI,eAAc,UAAS,YAAY,GAAG,WAAW,GACpD;AAAA,oBAAAG,MAACH,MAAA,EACC;AAAA,sBAAAG,MAACF,OAAA,EAAK,OAAM,WAAU,MAAI,MAAC;AAAA;AAAA,QAAI,KAAK;AAAA,SAAK;AAAA,MACzC,gBAAAE,MAACF,OAAA,EAAK,OAAM,WAAU;AAAA;AAAA,QAAE,SAAI,OAAO,KAAK,IAAI,GAAG,KAAK,KAAK,KAAK,MAAM,CAAC;AAAA,SAAE;AAAA,OACzE;AAAA,IACA,gBAAAC,KAACF,MAAA,EAAI,eAAc,UACjB,0BAAAE,KAACD,OAAA,EAAK,OAAM,WAAW,uBAAY,GACrC;AAAA,IACC,mBACC,gBAAAC,KAACF,MAAA,EACC,0BAAAE,KAACD,OAAA,EAAK,OAAM,UAAS,6CAAM,GAC7B;AAAA,KAEJ;AAEJ;;;ACpCA,SAAS,sBAAsB,KAA0B;AACvD,MAAI,OAAO,IAAI;AAEf,MAAI,IAAI,WAAW;AACjB,eAAW,MAAM,IAAI,WAAW;AAC9B,cAAQ,GAAG,OAAO,GAAG;AAAA,IACvB;AAAA,EACF;AACA,SAAO,eAAe,IAAI,IAAI;AAChC;AAcA,SAAS,eAAe,UAA0C;AAChE,QAAM,QAAyB,CAAC;AAChC,MAAI,UAAgC;AAEpC,aAAW,OAAO,UAAU;AAC1B,QAAI,IAAI,SAAS,QAAQ;AAEvB,UAAI,QAAS,OAAM,KAAK,OAAO;AAC/B,gBAAU,CAAC,GAAG;AAAA,IAChB,OAAO;AAGL,UAAI,CAAC,QAAS,WAAU,CAAC;AACzB,cAAQ,KAAK,GAAG;AAAA,IAClB;AAAA,EACF;AACA,MAAI,WAAW,QAAQ,SAAS,EAAG,OAAM,KAAK,OAAO;AACrD,SAAO;AACT;AAKA,SAAS,mBAAmB,MAA6B;AACvD,MAAI,MAAM;AACV,aAAW,OAAO,KAAM,QAAO,sBAAsB,GAAG;AACxD,SAAO;AACT;AAmBO,SAAS,aACd,UACA,MAC0B;AAC1B,QAAM,OAAO,aAAa,KAAK,KAAK;AACpC,QAAM,iBAAiB,KAAK,gBAAgB,KAAK;AAGjD,QAAM,eAAe,eAAe,KAAK,YAAY;AACrD,MAAI,YAAY,iBAAiB;AAEjC,QAAM,QAAQ,eAAe,QAAQ;AAGrC,QAAM,iBAAkC,CAAC;AACzC,MAAI,kBAAkB;AACtB,WAAS,IAAI,MAAM,SAAS,GAAG,KAAK,KAAK,kBAAkB,KAAK,sBAAsB,KAAK;AACzF,mBAAe,QAAQ,MAAM,CAAC,CAAE;AAChC;AAAA,EACF;AAEA,QAAM,YAA2B,eAAe,KAAK;AACrD,aAAW,OAAO,WAAW;AAC3B,iBAAa,sBAAsB,GAAG;AAAA,EACxC;AAGA,MAAI,YAAY,GAAG;AACjB,WAAO,eAAe,SAAS,KAAK,YAAY,GAAG;AACjD,YAAM,UAAU,eAAe,MAAM;AACrC,mBAAa,mBAAmB,OAAO;AAAA,IACzC;AACA,WAAO,CAAC,eAAe,KAAK,GAAG,IAAI;AAAA,EACrC;AAGA,QAAM,aAAa,MAAM,MAAM,GAAG,MAAM,SAAS,eAAe,MAAM;AACtE,QAAM,YAA6B,CAAC;AAEpC,WAAS,IAAI,WAAW,SAAS,GAAG,KAAK,GAAG,KAAK;AAC/C,UAAM,OAAO,mBAAmB,WAAW,CAAC,CAAE;AAC9C,QAAI,YAAY,OAAO,EAAG;AAC1B,iBAAa;AACb,cAAU,QAAQ,WAAW,CAAC,CAAE;AAAA,EAClC;AAEA,QAAM,SAAS,CAAC,GAAG,WAAW,GAAG,cAAc,EAAE,KAAK;AACtD,QAAM,UAAU,OAAO,SAAS,SAAS;AACzC,SAAO,CAAC,QAAQ,OAAO;AACzB;AAOO,SAAS,iBACd,cACA,SACe;AACf,SAAO;AAAA,IACL,EAAE,MAAM,UAAU,SAAS,aAAa;AAAA,IACxC,GAAG;AAAA,EACL;AACF;AAKO,SAAS,mBAAmB,OAA0B;AAC3D,QAAM,SAAS,MAAM,aAAa,eAAe;AACjD,QAAM,aAAa,MAAM,iBAAiB,eAAe;AACzD,QAAM,SAAS,MAAM,eAAe,MAAM,kBAAkB,eAAe;AAE3E,MAAI,UAAU,aAAM,MAAM,gBAAS,UAAU,gBAAS,KAAK;AAE3D,MAAI,MAAM,sBAAsB,MAAM,qBAAqB,GAAG;AAC5D,UAAM,aAAc,MAAM,qBAAqB,MAAM,eAAgB,KAAK,QAAQ,CAAC;AACnF,eAAW,oDAAe,SAAS;AAAA,EACrC;AAEA,SAAO;AACT;;;ACtKA,SAAS,QAAAG,aAAY;AAyHV,gBAAAC,YAAA;AA1GX,SAAS,eAAe,MAAyB;AAC/C,QAAM,WAAsB,CAAC;AAC7B,MAAI,UAAU;AAEd,SAAO,UAAU,KAAK,QAAQ;AAC5B,UAAM,UAAU,KAAK,QAAQ,MAAM,OAAO;AAE1C,QAAI,YAAY,IAAI;AAClB,eAAS,KAAK,EAAE,MAAM,KAAK,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC;AAC1D;AAAA,IACF;AAGA,QAAI,UAAU,SAAS;AACrB,eAAS,KAAK,EAAE,MAAM,KAAK,MAAM,SAAS,OAAO,GAAG,MAAM,QAAQ,CAAC;AAAA,IACrE;AAEA,UAAM,WAAW,KAAK,QAAQ,MAAM,UAAU,CAAC;AAG/C,QAAI,aAAa,IAAI;AACnB,eAAS,KAAK,EAAE,MAAM,KAAK,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC;AAC1D;AAAA,IACF;AAGA,aAAS,KAAK,EAAE,MAAM,KAAK,MAAM,UAAU,GAAG,QAAQ,GAAG,MAAM,OAAO,CAAC;AACvE,cAAU,WAAW;AAAA,EACvB;AAEA,SAAO;AACT;AAMA,SAAS,eAAe,UAAgC;AACtD,QAAM,SAAoB,CAAC;AAE3B,aAAW,OAAO,UAAU;AAC1B,QAAI,IAAI,SAAS,SAAS;AACxB,aAAO,KAAK,GAAG;AACf;AAAA,IACF;AAEA,QAAI,UAAU;AACd,UAAM,OAAO,IAAI;AAEjB,WAAO,UAAU,KAAK,QAAQ;AAC5B,YAAM,UAAU,KAAK,QAAQ,KAAK,OAAO;AAEzC,UAAI,YAAY,IAAI;AAClB,eAAO,KAAK,EAAE,MAAM,KAAK,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC;AACxD;AAAA,MACF;AAEA,UAAI,UAAU,SAAS;AACrB,eAAO,KAAK,EAAE,MAAM,KAAK,MAAM,SAAS,OAAO,GAAG,MAAM,QAAQ,CAAC;AAAA,MACnE;AAEA,YAAM,WAAW,KAAK,QAAQ,KAAK,UAAU,CAAC;AAE9C,UAAI,aAAa,IAAI;AACnB,eAAO,KAAK,EAAE,MAAM,KAAK,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC;AACxD;AAAA,MACF;AAEA,aAAO,KAAK,EAAE,MAAM,KAAK,MAAM,UAAU,GAAG,QAAQ,GAAG,MAAM,OAAO,CAAC;AACrE,gBAAU,WAAW;AAAA,IACvB;AAAA,EACF;AAEA,SAAO;AACT;AAGA,IAAM,aAAa;AAEnB,IAAM,aAAa;AAmBZ,SAAS,gBAAgB,EAAE,UAAU,KAAK,GAAoC;AAEnF,QAAM,eAAe,eAAe,IAAI;AACxC,QAAM,WAAW,eAAe,YAAY;AAG5C,QAAM,WAAW,SAAS,WAAW,KAAK,SAAS,CAAC,EAAG,SAAS;AAChE,MAAI,UAAU;AACZ,WAAO,gBAAAA,KAACD,OAAA,EAAK,MAAK,QAAQ,gBAAK;AAAA,EACjC;AAEA,SACE,gBAAAC,KAACD,OAAA,EAAK,MAAK,QACR,mBAAS,IAAI,CAAC,KAAK,MAAM;AACxB,QAAI,IAAI,SAAS,QAAQ;AACvB,aACE,gBAAAC,KAACD,OAAA,EAAa,OAAO,YAClB,cAAI,QADI,CAEX;AAAA,IAEJ;AACA,QAAI,IAAI,SAAS,QAAQ;AACvB,aACE,gBAAAC,KAACD,OAAA,EAAa,OAAO,YAClB,cAAI,QADI,CAEX;AAAA,IAEJ;AACA,WAAO,IAAI;AAAA,EACb,CAAC,GACH;AAEJ;;;AJzFU,gBAAAE,MAEF,QAAAC,aAFE;AApCV,SAAS,cAAc,IAAoB;AACzC,MAAI,KAAK,IAAM,QAAO,GAAG,EAAE;AAC3B,QAAM,WAAW,KAAK,KAAM,QAAQ,CAAC;AACrC,SAAO,GAAG,OAAO;AACnB;AAaO,SAAS,iBAAiB;AAAA,EAC/B;AAAA,EACA;AAAA,EACA,cAAc;AAAA,EACd;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAA0B;AAExB,MAAI,CAAC,YAAY,CAAC,aAAa,UAAU,WAAW,MAAM,CAAC,aAAa;AACtE,WAAO;AAAA,EACT;AAEA,SACE,gBAAAA,MAACC,MAAA,EAAI,eAAc,UAAS,WAAW,GAErC;AAAA,oBAAAD,MAACC,MAAA,EAAI,eAAc,OACjB;AAAA,sBAAAF,KAACE,MAAA,EAAI,OAAO,GAAG,YAAY,GACzB,0BAAAF,KAACG,OAAA,EAAK,MAAI,MAAC,OAAM,WAAW,uBAAK,GACnC;AAAA,MACA,gBAAAF,MAACC,MAAA,EAAI,UAAU,GAAG,eAAc,UAE7B;AAAA,mBACC,gBAAAF,KAAC,mBAAiB,mBAAQ;AAAA,QAG3B,eAAe,CAAC,WACf,gBAAAA,KAACG,OAAA,EAAK,OAAM,WAAU,iBAAG;AAAA,SAE7B;AAAA,OACF;AAAA,IAGC,aAAa,UAAU,SAAS,KAC/B,gBAAAH,KAACE,MAAA,EAAI,eAAc,UAChB,oBAAU,IAAI,CAAC,IAAI,MAClB,gBAAAF,KAAC,iBAA+B,MAAM,MAAlB,GAAG,MAAM,CAAa,CAC3C,GACH;AAAA,IAID,CAAC,gBAAgB,SAAS,YAAY,WACrC,gBAAAC,MAACC,MAAA,EAAI,eAAc,UAAS,WAAW,GAAG,YAAY,GACpD;AAAA,sBAAAF,KAACG,OAAA,EAAK,OAAM,WAAW,mBAAI,OAAO,EAAE,GAAE;AAAA,MACtC,gBAAAF,MAACC,MAAA,EAAI,eAAc,OAAM,KAAK,GAC3B;AAAA,iBAAS,UAAa,OAAO,KAC5B,gBAAAD,MAACE,OAAA,EAAK,OAAM,UAAS;AAAA;AAAA,UACZ,YAAY,IAAI;AAAA,WACzB;AAAA,QAED,YAAY,UACX,gBAAAF,MAACE,OAAA,EAAK,OAAM,QAAO;AAAA;AAAA,UACb,cAAc,OAAO;AAAA,WAC3B;AAAA,QAED,SACC,gBAAAH,KAACG,OAAA,EAAK,OAAM,WACT,6BAAmB,KAAK,GAC3B;AAAA,SAEJ;AAAA,OACF;AAAA,KAEJ;AAEJ;;;AK7GA,SAAS,OAAAC,MAAK,QAAAC,aAAY;AAkClB,SAGA,OAAAC,MAHA,QAAAC,aAAA;AAfD,SAAS,YAAY,EAAE,KAAK,GAAqB;AACtD,QAAM,EAAE,OAAO,WAAW,WAAW,eAAe,SAAS,IAAI;AAGjE,MAAI,CAAC,SAAS,MAAM,WAAW,GAAG;AAChC,WAAO;AAAA,EACT;AAEA,QAAM,QAAQ,MAAM,MAAM,IAAI;AAC9B,QAAM,WAAW,SAAS,QAAQ,OAAO,GAAG,EAAE,MAAM,GAAG,EAAE,IAAI,KAAK;AAElE,SACE,gBAAAA,MAACH,MAAA,EAAI,eAAc,UAAS,WAAW,GAErC;AAAA,oBAAAG,MAACH,MAAA,EAAI,eAAc,OAAM,KAAK,GAC5B;AAAA,sBAAAG,MAACF,OAAA,EAAK,MAAI,MAAC,OAAM,WAAU;AAAA;AAAA,QACrB,gBAAgB,iBAAO;AAAA,QAAK;AAAA,SAClC;AAAA,MACA,gBAAAC,KAACD,OAAA,EAAK,MAAI,MAAE,oBAAS;AAAA,MACrB,gBAAAE,MAACF,OAAA,EAAK,OAAM,WAAU;AAAA;AAAA,QACjB;AAAA,QAAU;AAAA,QAAG;AAAA,QAAU;AAAA,SAC5B;AAAA,OACF;AAAA,IAGA,gBAAAC,KAACF,MAAA,EAAI,eAAc,UAAS,YAAY,GACrC,gBACE,OAAO,CAAC,SAAS,CAAC,KAAK,WAAW,KAAK,KAAK,CAAC,KAAK,WAAW,KAAK,CAAC,EACnE,IAAI,CAAC,MAAM,MACV,gBAAAE,KAAC,YAAiB,QAAH,CAAe,CAC/B,GACL;AAAA,KACF;AAEJ;AAGA,SAAS,SAAS,EAAE,KAAK,GAAqB;AAG5C,MAAI,KAAK,WAAW,KAAK,KAAK,KAAK,WAAW,KAAK,GAAG;AACpD,WAAO;AAAA,EACT;AAGA,MAAI,KAAK,WAAW,IAAI,GAAG;AAEzB,WAAO,gBAAAA,KAACD,OAAA,EAAK,OAAM,WAAW,gBAAK;AAAA,EACrC;AAGA,MAAI,KAAK,WAAW,GAAG,GAAG;AACxB,WAAO,gBAAAC,KAACD,OAAA,EAAK,OAAM,WAAW,gBAAK;AAAA,EACrC;AAGA,MAAI,KAAK,WAAW,GAAG,GAAG;AACxB,WAAO,gBAAAC,KAACD,OAAA,EAAK,OAAM,WAAW,gBAAK;AAAA,EACrC;AAGA,SAAO,gBAAAC,KAACD,OAAA,EAAK,OAAM,WAAW,gBAAK;AACrC;;;AChFA,SAAS,OAAAG,MAAK,QAAAC,aAAY;AAoCpB,gBAAAC,MAKI,QAAAC,aALJ;AAjCN,IAAM,kBAAkB;AAWjB,SAAS,cAAc,EAAE,QAAQ,OAAO,cAAc,GAAuB;AAElF,QAAM,QAAQ,MAAM,MAAM,oBAAoB;AAC9C,MAAI,CAAC,MAAO,QAAO;AAEnB,QAAM,QAAQ,MAAM,CAAC,EAAG,YAAY,EAAE,KAAK;AAC3C,MAAI,OAAO,WAAW,EAAG,QAAO;AAGhC,QAAM,UAAU,CAAC,SAAS,MAAM,WAAW,GAAG,IAC1C,OAAO,MAAM,GAAG,CAAC,IACjB,OACG,OAAO,CAAC,MAAM,EAAE,KAAK,YAAY,EAAE,SAAS,KAAK,CAAC,EAClD,MAAM,GAAG,CAAC;AAGjB,MAAI,SAAS,QAAQ,SAAS,KAAK,QAAQ,KAAK,CAAC,MAAM,EAAE,KAAK,YAAY,MAAM,KAAK,EAAG,QAAO;AAE/F,MAAI,QAAQ,WAAW,EAAG,QAAO;AAEjC,SACE,gBAAAA,MAACH,MAAA,EAAI,eAAc,UAAS,YAAY,GAAG,WAAW,GACpD;AAAA,oBAAAE,KAACD,OAAA,EAAK,OAAM,WAAU,UAAQ,MAAC,4CAE/B;AAAA,IACC,QAAQ,IAAI,CAAC,OAAO,MACnB,gBAAAC,KAACF,MAAA,EACC,0BAAAG,MAACF,OAAA,EAAK,OAAO,MAAM,gBAAgB,kBAAkB,WAClD;AAAA,YAAM,gBAAgB,cAAS;AAAA,MAAQ,MAAM;AAAA,OAChD,KAHQ,MAAM,IAIhB,CACD;AAAA,IACD,gBAAAC,KAACF,MAAA,EAAI,WAAW,GACd,0BAAAE,KAACD,OAAA,EAAK,OAAM,WAAU,UAAQ,MAAC,6DAE/B,GACF;AAAA,KACF;AAEJ;;;ACtDA,SAAS,OAAAG,MAAK,QAAAC,aAAY;AAmCpB,gBAAAC,MAKI,QAAAC,aALJ;AAxBN,IAAMC,mBAAkB;AAEjB,SAAS,aAAa,EAAE,OAAO,OAAO,cAAc,GAAsB;AAE/E,QAAM,QAAQ,MAAM,MAAM,mBAAmB;AAC7C,MAAI,CAAC,MAAO,QAAO;AAEnB,QAAM,QAAQ,MAAM,CAAC,EAAG,YAAY,EAAE,KAAK;AAC3C,MAAI,MAAM,WAAW,EAAG,QAAO;AAG/B,QAAM,UAAU,CAAC,SAAS,MAAM,WAAW,GAAG,IAC1C,MAAM,MAAM,GAAG,CAAC,IAChB,MACG,OAAO,CAAC,MAAM,EAAE,YAAY,EAAE,SAAS,KAAK,CAAC,EAC7C,MAAM,GAAG,CAAC;AAGjB,MAAI,SAAS,QAAQ,KAAK,CAAC,MAAM,EAAE,YAAY,MAAM,KAAK,EAAG,QAAO;AAEpE,MAAI,QAAQ,WAAW,EAAG,QAAO;AAEjC,SACE,gBAAAD,MAACH,MAAA,EAAI,eAAc,UAAS,YAAY,GAAG,WAAW,GACpD;AAAA,oBAAAE,KAACD,OAAA,EAAK,OAAM,WAAU,UAAQ,MAAC,4CAE/B;AAAA,IACC,QAAQ,IAAI,CAAC,MAAM,MAClB,gBAAAC,KAACF,MAAA,EACC,0BAAAG,MAACF,OAAA,EAAK,OAAO,MAAM,gBAAgBG,mBAAkB,WAClD;AAAA,YAAM,gBAAgB,cAAS;AAAA,MAAQ;AAAA,OAC1C,KAHQ,IAIV,CACD;AAAA,IACD,gBAAAF,KAACF,MAAA,EAAI,WAAW,GACd,0BAAAE,KAACD,OAAA,EAAK,OAAM,WAAU,UAAQ,MAAC,6DAE/B,GACF;AAAA,KACF;AAEJ;;;AC/BO,IAAM,mBAAN,MAAuB;AAAA,EACnB,aAAa,oBAAI,IAA6B;AAAA,EAC9C,aAAa,oBAAI,IAAsB;AAAA;AAAA,EAGhD,SAAS,MAAc,SAAgC;AACrD,SAAK,WAAW,IAAI,MAAM,OAAO;AAAA,EACnC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,IACE,MACA,QACU;AACV,QAAI,CAAC,iBAAiB,OAAO,KAAK,GAAG;AACnC,YAAM,IAAI,uBAAuB,OAAO,KAAK;AAAA,IAC/C;AAGA,UAAM,WAAW,GAAG,IAAI,IAAI,OAAO,OAAO,IAAI,OAAO,KAAK;AAE1D,UAAM,SAAS,KAAK,WAAW,IAAI,QAAQ;AAC3C,QAAI,OAAQ,QAAO;AAEnB,UAAM,UAAU,KAAK,WAAW,IAAI,IAAI;AACxC,QAAI,CAAC,SAAS;AACZ,YAAM,YAAY,CAAC,GAAG,KAAK,WAAW,KAAK,CAAC,EAAE,KAAK,IAAI;AACvD,YAAM,IAAI,MAAM,uCAAmB,IAAI,wBAAS,SAAS,EAAE;AAAA,IAC7D;AAEA,UAAM,WAAW,QAAQ,MAAM;AAC/B,SAAK,WAAW,IAAI,UAAU,QAAQ;AACtC,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,OAAiB;AACf,WAAO,CAAC,GAAG,KAAK,WAAW,KAAK,CAAC;AAAA,EACnC;AAAA;AAAA,EAGA,QAAc;AACZ,SAAK,WAAW,MAAM;AAAA,EACxB;AACF;AAOO,IAAM,kBAAkB,IAAI,iBAAiB;AAEpD,gBAAgB,SAAS,YAAY,CAAC,WAAW;AAE/C,SAAO,IAAI,iBAAiB;AAAA,IAC1B,QAAQ,OAAO;AAAA,IACf,SAAS,OAAO;AAAA,IAChB,OAAO,OAAO;AAAA,EAChB,CAAC;AACH,CAAC;AAOM,SAAS,eAAe,QAKlB;AACX,SAAO,gBAAgB,IAAI,OAAO,MAAM,MAAM;AAChD;;;ACuDO,SAAS,UACd,MACc;AACd,SAAO;AAAA,IACL,IAAI,OAAO;AAAE,aAAO,KAAK;AAAA,IAAM;AAAA,IAC/B,IAAI,cAAc;AAAE,aAAO,KAAK;AAAA,IAAa;AAAA,IAC7C,IAAI,OAAO;AAAE,aAAO,KAAK;AAAA,IAAM;AAAA,IAC/B,IAAI,aAAa;AAAE,aAAO,KAAK;AAAA,IAAY;AAAA,IAC3C,IAAI,yBAAyB;AAAE,aAAO,KAAK,0BAA0B;AAAA,IAAO;AAAA,IAC5E,IAAI,qBAAqB;AAAE,aAAO,KAAK,sBAAsB,CAAC;AAAA,IAAG;AAAA,IAEjE,MAAM,QAAQ,MAAe,KAAuC;AAClE,aAAO,KAAK,QAAQ,MAAW,GAAG;AAAA,IACpC;AAAA,IAEA,aAAa,MAAuB;AAClC,aAAO,KAAK,eAAe,IAAS,KAAK,GAAG,KAAK,IAAI;AAAA,IACvD;AAAA,EACF;AACF;AAiBO,SAAS,WAAW,MAAyB;AAClD,SAAO,SAAS;AAClB;AAgBO,IAAM,kBAAN,MAAsC;AAAA,EAC3C,MAAM,WAAmB,OAAyB;AAChD,WAAO;AAAA,EACT;AACF;;;ACrNA,OAAO,gBAAgB;;;ACJvB;;;ADQA,IAAM,mBAAmB,WAAW,QAAQ,qBAAoB;AAQhE,SAAS,YAAY,OAAsC;AACzD,SAAO,MAAM,IAAI,CAAC,MAAM;AACtB,UAAM,SACJ,EAAE,cACF,OAAO,KAAM,EAAE,WAAuC,cAAc,CAAC,CAAC,EACnE,KAAK,IAAI;AACd,WAAO;AAAA,MACL,MAAM,EAAE;AAAA,MACR,aAAa,EAAE;AAAA,MACf,QAAQ,UAAU;AAAA,IACpB;AAAA,EACF,CAAC;AACH;AAQO,SAAS,kBAAkB,MAAmC;AACnE,QAAM,MAAM,oBAAI,KAAK;AACrB,QAAM,OAAO,IAAI,mBAAmB,SAAS;AAAA,IAC3C,MAAM;AAAA,IACN,OAAO;AAAA,IACP,KAAK;AAAA,IACL,SAAS;AAAA,EACX,CAAC;AACD,QAAM,OAAO,IAAI,mBAAmB,SAAS;AAAA,IAC3C,MAAM;AAAA,IACN,QAAQ;AAAA,EACV,CAAC;AAED,SAAO,iBAAiB;AAAA,IACtB,OAAO,KAAK;AAAA,IACZ,eAAe,KAAK;AAAA,IACpB,KAAK,KAAK;AAAA,IACV;AAAA,IACA;AAAA,IACA,OAAO,KAAK,QAAQ,YAAY,KAAK,KAAK,IAAI;AAAA,IAC9C,gBAAgB,KAAK,kBAAkB;AAAA,EACzC,CAAC;AACH;;;AEzBO,IAAM,eAAN,MAAmB;AAAA,EACf,SAAS,oBAAI,IAA0B;AAAA,EACvC;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAGT,OAAgB,iBAA2B,CAAC;AAAA,EAE5C,YAAY,MAA4B;AACtC,SAAK,iBAAiB,IAAI,IAAI,MAAM,iBAAiB,CAAC,CAAC;AACvD,SAAK,sBAAsB,MAAM,uBAAuB,MAAM;AAC9D,SAAK,YAAY,MAAM;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,SAA+C,MAA6B;AAC1E,WAAO,KAAK,eAAe,UAAU,IAAI,CAAC;AAAA,EAC5C;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,eAAe,MAA0B;AACvC,QAAI,KAAK,OAAO,IAAI,KAAK,IAAI,GAAG;AAC9B,YAAM,IAAI,MAAM,iBAAO,KAAK,IAAI,gEAAc;AAAA,IAChD;AACA,SAAK,OAAO,IAAI,KAAK,MAAM,IAAI;AAC/B,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,YAAY,OAA6B;AACvC,eAAW,QAAQ,OAAO;AACxB,WAAK,eAAe,IAAI;AAAA,IAC1B;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,WAAW,MAAuB;AAChC,WAAO,KAAK,OAAO,OAAO,IAAI;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,IAAI,MAAwC;AAC1C,QAAI,CAAC,KAAK,eAAe,IAAI,EAAG,QAAO;AACvC,WAAO,KAAK,OAAO,IAAI,IAAI;AAAA,EAC7B;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,OAAuB;AACrB,UAAM,SAAyB,CAAC;AAChC,eAAW,CAAC,MAAM,IAAI,KAAK,KAAK,QAAQ;AACtC,UAAI,KAAK,eAAe,IAAI,GAAG;AAC7B,eAAO,KAAK,IAAI;AAAA,MAClB;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,WAAW,MAAgC;AACzC,WAAO,KAAK,KAAK,EAAE,OAAO,CAAC,MAAM,EAAE,SAAS,IAAI;AAAA,EAClD;AAAA;AAAA;AAAA;AAAA,EAKA,gBAAgC;AAC9B,WAAO,KAAK,4BAAwB;AAAA,EACtC;AAAA;AAAA;AAAA;AAAA,EAKA,iBAAiC;AAC/B,WAAO,KAAK,KAAK,EAAE,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE,IAAI,CAAC;AAAA,EACtD;AAAA;AAAA;AAAA;AAAA,EAKA,QAAkB;AAChB,WAAO,CAAC,GAAG,KAAK,OAAO,KAAK,CAAC;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,MAAuB;AACzB,WAAO,KAAK,OAAO,IAAI,IAAI;AAAA,EAC7B;AAAA;AAAA;AAAA;AAAA,EAKA,UAAU,MAAuB;AAC/B,WAAO,KAAK,OAAO,IAAI,IAAI,KAAK,KAAK,eAAe,IAAI;AAAA,EAC1D;AAAA;AAAA;AAAA;AAAA,EAKA,QAAQ,MAAoB;AAC1B,SAAK,eAAe,IAAI,IAAI;AAAA,EAC9B;AAAA;AAAA;AAAA;AAAA,EAKA,OAAO,MAAoB;AACzB,SAAK,eAAe,OAAO,IAAI;AAAA,EACjC;AAAA;AAAA;AAAA;AAAA,EAKA,OAAO,MAAoC;AACzC,WAAO,KAAK,OAAO,IAAI,IAAI,GAAG;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,QAAQ,MAAc,MAAe,KAAuC;AAChF,UAAM,OAAO,KAAK,IAAI,IAAI;AAC1B,QAAI,CAAC,MAAM;AACT,aAAO;AAAA,QACL,SAAS;AAAA,QACT,MAAM,iBAAO,IAAI;AAAA,QACjB,OAAO;AAAA,MACT;AAAA,IACF;AAEA,QAAI;AACF,aAAO,MAAM,KAAK,QAAQ,MAAM,GAAG;AAAA,IACrC,SAAS,KAAc;AACrB,YAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,aAAO;AAAA,QACL,SAAS;AAAA,QACT,MAAM,iBAAO,IAAI,mCAAU,OAAO;AAAA,QAClC,OAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,eAAe,MAAuB;AACpC,UAAM,OAAO,KAAK,OAAO,IAAI,IAAI;AACjC,QAAI,CAAC,KAAM,QAAO;AAGlB,QAAI,KAAK,eAAe,IAAI,IAAI,EAAG,QAAO;AAG1C,QAAI,CAAC,KAAK,oBAAoB,IAAI,EAAG,QAAO;AAG5C,QAAI,KAAK,aAAa,KAAK,mBAAmB,SAAS,GAAG;AACxD,UAAI,CAAC,KAAK,mBAAmB,SAAS,KAAK,SAAS,EAAG,QAAO;AAAA,IAChE;AAEA,WAAO;AAAA,EACT;AACF;;;ACpLO,IAAM,UAAN,MAAc;AAAA,EACV,YAA2B,CAAC;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAGA,mBAAmB,IAAI,gBAAgB;AAAA;AAAA,EAGhD,gBAAkC,CAAC;AAAA,EAEnC,YACE,UACA,QAAuC,CAAC,GACxC,aACA,SACA;AACA,SAAK,YAAY;AAEjB,QAAI,iBAAiB,cAAc;AACjC,WAAK,gBAAgB;AAAA,IACvB,OAAO;AACL,WAAK,gBAAgB,IAAI,aAAa;AACtC,WAAK,cAAc,YAAY,KAAK;AAAA,IACtC;AACA,SAAK,eAAe,eAAe,IAAI,YAAY;AACnD,SAAK,WAAW;AAAA,MACd,KAAK,SAAS,OAAO,QAAQ,IAAI;AAAA,MACjC,eAAe,SAAS,iBAAiB;AAAA,MACzC,mBAAmB,SAAS,qBAAqB;AAAA,MACjD,sBAAsB,SAAS,wBAAwB;AAAA,MACvD,gBAAgB,SAAS;AAAA,MACzB,MAAM,SAAS,QAAQ,IAAI,gBAAgB;AAAA,MAC3C,YAAY,SAAS,cAAc,CAAC,SAAS,OAAO,QAAQ,IAAI,CAAC;AAAA,IACnE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAMA,IAAI,WAAmC;AACrC,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,kBAA0B;AAC5B,WAAO,KAAK,aAAa;AAAA,EAC3B;AAAA,EAEA,IAAI,cAA2B;AAC7B,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,QAAgB;AAClB,WAAO,KAAK,UAAU,MAAM;AAAA,EAC9B;AAAA;AAAA,EAGA,IAAI,eAA6B;AAC/B,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkBA,OAAO,KAAK,WAAmB,MAA+E;AAE5G,SAAK,UAAU,KAAK,EAAE,MAAM,QAAQ,SAAS,UAAU,CAAC;AAGxD,UAAM,YAAY,KAAK,IAAI;AAC3B,QAAI,aAAa;AAEjB,QAAI;AACF,aAAO,aAAa,KAAK,SAAS,eAAe;AAE/C,cAAM,eAAe,KAAK,mBAAmB;AAC7C,cAAM,CAAC,OAAO,IAAI;AAAA,UAChB,CAAC,GAAG,KAAK,SAAS;AAAA,UAClB;AAAA,YACE,OAAO,KAAK,UAAU,MAAM;AAAA,YAC5B,mBAAmB,KAAK,SAAS;AAAA,YACjC;AAAA,YACA,sBAAsB,KAAK,SAAS;AAAA,UACtC;AAAA,QACF;AACA,cAAM,cAAc,iBAAiB,cAAc,OAAO;AAG1D,cAAM,WAAW,KAAK,sBAAsB;AAE5C,cAAM,SAAS,KAAK,UAAU,KAAK,aAAa;AAAA,UAC9C,QAAQ,KAAK,iBAAiB;AAAA,UAC9B,OAAO,SAAS,SAAS,IAAI,WAAW;AAAA,UACxC,iBAAiB,MAAM;AAAA,UACvB,gBAAgB,MAAM;AAAA,UACtB,gBAAgB,MAAM;AAAA,UACtB,YAAY,MAAM;AAAA,QACpB,CAAC;AAGD,YAAI,kBAAkB;AACtB,YAAI;AACJ,YAAI;AACJ,YAAI,mBAAkC;AAEtC,yBAAiB,SAAS,QAAQ;AAChC,cAAI,MAAM,SAAS;AACjB,+BAAmB,MAAM;AACzB,kBAAM,EAAE,MAAM,cAAc,SAAS,MAAM,QAAQ;AAAA,UACrD;AAEA,cAAI,MAAM,aAAa,MAAM,UAAU,SAAS,GAAG;AACjD,4BAAgB,MAAM;AAAA,UACxB;AAEA,cAAI,MAAM,OAAO;AACf,wBAAY,MAAM;AAAA,UACpB;AAEA,cAAI,MAAM,cAAc;AACtB,+BAAmB,MAAM;AAAA,UAC3B;AAAA,QACF;AAGA,YAAI,WAAW;AACb,gBAAM,UAAU,KAAK,UAAU,MAAM;AACrC,eAAK,aAAa,OAAO,WAAW,OAAO;AAC3C,gBAAM,EAAE,MAAM,SAAS,OAAO,WAAW,OAAO,QAAQ;AAAA,QAC1D;AAGA,cAAM,eAA4B;AAAA,UAChC,MAAM;AAAA,UACN,SAAS;AAAA,QACX;AACA,YAAI,iBAAiB,cAAc,SAAS,GAAG;AAC7C,uBAAa,YAAY;AAAA,QAC3B;AACA,aAAK,UAAU,KAAK,YAAY;AAGhC,YAAI,iBAAiB,cAAc,SAAS,GAAG;AAC7C,gBAAM,EAAE,MAAM,cAAc,OAAO,cAAc;AAGjD,gBAAM,cAAc,KAAK,iBAAiB,aAAa;AACvD,cAAI,aAAa;AACf,kBAAM,WAAW;AACjB,kBAAM,EAAE,MAAM,cAAc,SAAS,SAAS;AAE9C,yBAAa,YAAY;AAEzB,yBAAa,WAAW;AAExB,iBAAK,gBAAgB,CAAC;AACtB;AACA;AAAA,UACF;AAGA,gBAAM,UAAU,MAAM,KAAK,cAAc,aAAa;AACtD,eAAK,gBAAgB,QAAQ;AAE7B,qBAAW,QAAQ,QAAQ,OAAO;AAChC,kBAAM,EAAE,MAAM,eAAe,MAAM,KAAK,MAAM,QAAQ,KAAK,OAAO;AAGlE,gBAAI,cAAc,KAAK,OAAO;AAC9B,gBAAI,KAAK,OAAO,QAAQ,KAAK,OAAO,KAAK,OAAO;AAC9C,6BAAe;AAAA;AAAA,EAAO,KAAK,OAAO,KAAK,KAAK;AAAA,YAC9C;AAEA,iBAAK,UAAU,KAAK;AAAA,cAClB,MAAM;AAAA,cACN,SAAS;AAAA,cACT,YAAY,KAAK;AAAA,cACjB,MAAM,KAAK;AAAA,YACb,CAAC;AAAA,UACH;AAEA;AACA;AAAA,QACF;AAGA;AAAA,MACF;AAAA,IACF,SAAS,KAAc;AACrB,UAAI,eAAe,gBAAgB,IAAI,SAAS,cAAc;AAC5D;AAAA,MACF;AACA,UAAI,eAAe,SAAS,IAAI,SAAS,cAAc;AACrD;AAAA,MACF;AAEA,YAAM;AAAA,QACJ,MAAM;AAAA,QACN,OAAO,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC;AAAA,MAC3D;AACA;AAAA,IACF;AAEA,UAAM,UAAU,KAAK,IAAI,IAAI;AAC7B,UAAM,EAAE,MAAM,QAAQ,QAAQ;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,MAAM,cAAc,OAAuI;AACzJ,UAAM,UAAuB;AAAA,MAC3B,KAAK,KAAK,SAAS;AAAA,MACnB,QAAQ,KAAK,iBAAiB;AAAA,MAC9B,YAAY,KAAK,SAAS;AAAA,IAC5B;AAGA,UAAM,cAAc,MAAM,MAAM,CAAC,OAAO;AACtC,YAAM,OAAO,KAAK,cAAc,IAAI,GAAG,IAAI;AAC3C,aAAO,OAAO,WAAW,KAAK,IAAI,IAAI;AAAA,IACxC,CAAC;AAED,QAAI,eAAe,MAAM,SAAS,GAAG;AAEnC,YAAM,eAAe;AACrB,YAAMI,SAAqE,CAAC;AAC5E,YAAMC,WAA4B,CAAC;AAGnC,eAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK,cAAc;AACnD,cAAM,QAAQ,MAAM,MAAM,GAAG,IAAI,YAAY;AAC7C,cAAM,WAAW,MAAM,IAAI,CAAC,OAAO,KAAK,YAAY,IAAI,OAAO,CAAC;AAChE,cAAM,eAAe,MAAM,QAAQ,IAAI,QAAQ;AAC/C,mBAAW,KAAK,cAAc;AAC5B,UAAAD,OAAM,KAAK,EAAE,IAAI;AACjB,UAAAC,SAAQ,KAAK,EAAE,MAAM;AAAA,QACvB;AAAA,MACF;AAEA,aAAO,EAAE,OAAAD,QAAO,SAAAC,SAAQ;AAAA,IAC1B;AAGA,UAAM,QAAqE,CAAC;AAC5E,UAAM,UAA4B,CAAC;AACnC,eAAW,MAAM,OAAO;AACtB,YAAM,IAAI,MAAM,KAAK,YAAY,IAAI,OAAO;AAC5C,YAAM,KAAK,EAAE,IAAI;AACjB,cAAQ,KAAK,EAAE,MAAM;AAAA,IACvB;AAEA,WAAO,EAAE,OAAO,QAAQ;AAAA,EAC1B;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,YACJ,IACA,KACiG;AACjG,UAAM,WAAW,GAAG;AACpB,UAAM,YAAY,KAAK,IAAI;AAG3B,UAAM,OAAO,KAAK,cAAc,IAAI,QAAQ;AAC5C,QAAI,CAAC,MAAM;AACT,YAAM,SAAS,iBAAO,QAAQ;AAC9B,aAAO;AAAA,QACL,MAAM,EAAE,MAAM,UAAU,QAAQ,GAAG,IAAI,QAAQ,EAAE,SAAS,OAAO,MAAM,QAAQ,OAAO,iBAAiB,EAAE;AAAA,QACzG,QAAQ,EAAE,MAAM,UAAU,SAAS,OAAO,OAAO,kBAAkB,UAAU;AAAA,MAC/E;AAAA,IACF;AAGA,QAAI;AACJ,QAAI;AACF,iBAAW,GAAG,YAAY,KAAK,MAAM,GAAG,SAAS,IAAI,CAAC;AAAA,IACxD,QAAQ;AACN,iBAAW,CAAC;AAAA,IACd;AAGA,UAAM,aAAa,MAAM,KAAK,SAAS,KAAK,MAAM,UAAU,QAAQ;AACpE,QAAI,CAAC,YAAY;AACf,YAAM,SAAS,iBAAO,QAAQ;AAC9B,aAAO;AAAA,QACL,MAAM,EAAE,MAAM,UAAU,QAAQ,GAAG,IAAI,QAAQ,EAAE,SAAS,OAAO,MAAM,QAAQ,OAAO,cAAc,EAAE;AAAA,QACtG,QAAQ,EAAE,MAAM,UAAU,SAAS,OAAO,OAAO,eAAe,UAAU;AAAA,MAC5E;AAAA,IACF;AAGA,QAAI,CAAC,WAAW,KAAK,IAAI,GAAG;AAC1B,YAAM,eAAgB,KAA6E;AACnG,UAAI,OAAO,iBAAiB,YAAY;AACtC,YAAI;AACF,gBAAM,aAAa,UAAU,GAAG;AAAA,QAClC,QAAQ;AAAA,QAER;AAAA,MACF;AAAA,IACF;AAGA,QAAI;AACF,YAAM,SAAS,MAAM,KAAK,QAAQ,UAAU,GAAG;AAC/C,aAAO;AAAA,QACL,MAAM,EAAE,MAAM,UAAU,QAAQ,GAAG,IAAI,OAAO;AAAA,QAC9C,QAAQ;AAAA,UACN,MAAM;AAAA,UACN,SAAS,OAAO;AAAA,UAChB,OAAO,OAAO;AAAA,UACd;AAAA,QACF;AAAA,MACF;AAAA,IACF,SAAS,KAAc;AACrB,YAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,YAAM,cAAc,EAAE,SAAS,OAAO,MAAM,iBAAO,QAAQ,mCAAU,OAAO,IAAI,OAAO,kBAAkB;AACzG,aAAO;AAAA,QACL,MAAM,EAAE,MAAM,UAAU,QAAQ,GAAG,IAAI,QAAQ,YAAY;AAAA,QAC3D,QAAQ,EAAE,MAAM,UAAU,SAAS,OAAO,OAAO,mBAAmB,UAAU;AAAA,MAChF;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,iBAAiB,cAA2C;AAC1D,QAAI,KAAK,cAAc,SAAS,EAAG,QAAO;AAG1C,UAAM,eAAe,KAAK,cAAc,MAAM,EAAE;AAChD,QAAI,aAAa,SAAS,EAAG,QAAO;AAGpC,UAAM,QAAQ,aAAa,CAAC;AAC5B,UAAM,UAAU,aAAa;AAAA,MAC3B,CAAC,MAAM,EAAE,SAAS,MAAM,QAAQ,EAAE,UAAU,MAAM,SAAS,CAAC,EAAE;AAAA,IAChE;AAEA,QAAI,CAAC,QAAS,QAAO;AAGrB,WAAO,aAAa,KAAK,CAAC,OAAO,GAAG,SAAS,MAAM,IAAI;AAAA,EACzD;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,QAAc;AACZ,SAAK,iBAAiB,MAAM;AAAA,EAC9B;AAAA;AAAA,EAGA,QAAc;AACZ,SAAK,UAAU,SAAS;AACxB,SAAK,aAAa,aAAa;AAC/B,SAAK,gBAAgB,CAAC;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,qBAA6B;AAC3B,UAAM,eAAe,KAAK,cAAc,KAAK;AAC7C,UAAM,YAAY,aAAa,IAAI,CAAC,OAAO;AAAA,MACzC,MAAM,EAAE;AAAA,MACR,aAAa,EAAE;AAAA,MACf,YAAY,EAAE;AAAA,IAChB,EAAE;AAEF,UAAM,OAA4B;AAAA,MAChC,OAAO,KAAK,UAAU,MAAM;AAAA,MAC5B,eAAe,KAAK,SAAS;AAAA,MAC7B,OAAO,UAAU,SAAS,IAAI,YAAY;AAAA,MAC1C,gBAAgB,KAAK,SAAS,kBAAkB;AAAA,MAChD,KAAK,KAAK,SAAS;AAAA,IACrB;AAEA,WAAO,kBAAkB,IAAI;AAAA,EAC/B;AAAA;AAAA,EAGA,wBAA0C;AACxC,WAAO,KAAK,cAAc,KAAK,EAAE,IAAI,CAAC,OAAO;AAAA,MAC3C,MAAM;AAAA,MACN,UAAU;AAAA,QACR,MAAM,EAAE;AAAA,QACR,aAAa,EAAE;AAAA,QACf,YAAY,EAAE;AAAA,MAChB;AAAA,IACF,EAAE;AAAA,EACJ;AACF;;;ACpdA,SAAS,SAAS,UAAU,kBAAkB;AAC9C,SAAS,YAAAC,iBAAgB;AACzB,SAAS,aAAa;AACtB,OAAOC,cAAa;AAGpB,IAAM,qBAAqB;AAC3B,IAAM,4BAA4B;AAClC,IAAM,wBAAwB,KAAK,OAAO;AAG1C,IAAM,YAAYA,SAAQ,aAAa;AAUhC,SAAS,YAAY,WAAmB,KAAqB;AAClE,QAAM,WAAW,WAAW,SAAS,IAAI,YAAY,QAAQ,KAAK,SAAS;AAC3E,SAAO,QAAQ,QAAQ;AACzB;AAMA,eAAe,SAAS,QAAiC;AACvD,MAAI;AACF,WAAO,MAAMD,UAAS,MAAM;AAAA,EAC9B,QAAQ;AAEN,UAAM,SAAS,QAAQ,QAAQ,IAAI;AACnC,QAAI;AACF,YAAM,aAAa,MAAMA,UAAS,MAAM;AACxC,aAAO,QAAQ,YAAY,SAAS,QAAQ,MAAM,CAAC;AAAA,IACrD,QAAQ;AAEN,aAAO,QAAQ,MAAM;AAAA,IACvB;AAAA,EACF;AACF;AASA,eAAsB,QACpB,cACA,QACsD;AACtD,MAAI,aAAa,WAAW,GAAG;AAC7B,WAAO,EAAE,IAAI,KAAK;AAAA,EACpB;AAEA,QAAM,aAAa,MAAM,SAAS,MAAM;AAExC,aAAW,QAAQ,cAAc;AAC/B,UAAM,WAAW,MAAM,SAAS,IAAI;AAEpC,UAAM,MAAM,SAAS,UAAU,UAAU;AACzC,QAAI,CAAC,IAAI,WAAW,IAAI,KAAK,QAAQ,MAAM,CAAC,IAAI,WAAW,GAAG,KAAK,CAAC,IAAI,WAAW,IAAI,GAAG;AACxF,aAAO,EAAE,IAAI,KAAK;AAAA,IACpB;AAEA,QAAI,eAAe,UAAU;AAC3B,aAAO,EAAE,IAAI,KAAK;AAAA,IACpB;AAAA,EACF;AAEA,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,OAAO,iBAAO,MAAM,kEAAgB,aAAa,KAAK,IAAI,CAAC;AAAA,EAC7D;AACF;AASO,SAAS,eAAe,SAAiB,YAAY,2BAAmC;AAC7F,MAAI,QAAQ,UAAU,UAAW,QAAO;AACxC,QAAM,YAAY,QAAQ,MAAM,GAAG,SAAS;AAC5C,SAAO,GAAG,SAAS;AAAA;AAAA,oEAAuB,OAAO,QAAQ,MAAM,CAAC;AAClE;AAKO,SAAS,oBAA4B;AAC1C,SAAO;AACT;AA4CA,eAAsB,YACpB,SACA,MACA,KACA,YAAY,oBACZ,QAEA,gBACsE;AACtE,SAAO,IAAI,QAAQ,CAACE,aAAY;AAC9B,QAAI;AACJ,QAAI;AACJ,QAAI;AAEJ,QAAI,gBAAgB;AAClB,iBAAW;AACX,kBAAY;AACZ,iBAAW;AAAA,IACb,OAAO;AACL,iBAAW,CAAC;AACZ,iBAAW,YAAY,QAAQ;AAC/B,kBAAY,YACR,CAAC,MAAM,SAAS,GAAG,IAAI,IACvB;AAAA,IACN;AAEA,UAAM,QAAQ,MAAM,UAAU,WAAW;AAAA,MACvC;AAAA,MACA,OAAO;AAAA,MACP,KAAK,EAAE,GAAGC,SAAQ,IAAI;AAAA,MACtB,OAAO,CAAC,QAAQ,QAAQ,MAAM;AAAA,IAChC,CAAC;AAED,QAAI,SAAS;AACb,QAAI,SAAS;AAEb,UAAM,OAAO,GAAG,QAAQ,CAAC,SAAiB;AACxC,gBAAU,KAAK,SAAS;AAAA,IAC1B,CAAC;AAED,UAAM,OAAO,GAAG,QAAQ,CAAC,SAAiB;AACxC,gBAAU,KAAK,SAAS;AAAA,IAC1B,CAAC;AAED,UAAM,UAAU,WAAW,MAAM;AAC/B,YAAM,KAAK,SAAS;AAEpB,iBAAW,MAAM;AACf,cAAM,KAAK,SAAS;AAAA,MACtB,GAAG,GAAI;AAAA,IACT,GAAG,SAAS;AAGZ,QAAI,QAAQ;AACV,UAAI,OAAO,SAAS;AAClB,cAAM,KAAK,SAAS;AAAA,MACtB;AACA,aAAO,iBAAiB,SAAS,MAAM;AACrC,cAAM,KAAK,SAAS;AAAA,MACtB,GAAG,EAAE,MAAM,KAAK,CAAC;AAAA,IACnB;AAEA,UAAM,GAAG,SAAS,CAAC,SAAS;AAC1B,mBAAa,OAAO;AACpB,MAAAD,SAAQ,EAAE,QAAQ,QAAQ,UAAU,KAAK,CAAC;AAAA,IAC5C,CAAC;AAED,UAAM,GAAG,SAAS,CAAC,QAAQ;AACzB,mBAAa,OAAO;AACpB,gBAAU;AAAA,4CAAY,IAAI,OAAO;AACjC,MAAAA,SAAQ,EAAE,QAAQ,QAAQ,UAAU,KAAK,CAAC;AAAA,IAC5C,CAAC;AAAA,EACH,CAAC;AACH;;;AC9KA,SAAS,WAAW,MAA2B;AAC7C,MAAI,KAAK,WAAW,GAAG;AACrB,WAAO,EAAE,OAAO,CAAC,GAAG,KAAK,MAAM,UAAU,SAAS;AAAA,EACpD;AAEA,QAAM,UAAU,KAAK,QAAQ,MAAM;AACnC,QAAM,QAAQ,KAAK,QAAQ,IAAI;AAC/B,MAAI;AACJ,MAAI,YAAY,OAAO,UAAU,MAAM,WAAW,QAAQ;AACxD,UAAM;AAAA,EACR,WAAW,UAAU,IAAI;AACvB,UAAM;AAAA,EACR,OAAO;AACL,UAAM;AAAA,EACR;AAEA,QAAM,QAAQ,KAAK,MAAM,GAAG;AAE5B,MAAI;AACJ,MAAI,KAAK,SAAS,GAAG,GAAG;AACtB,QAAI,MAAM,SAAS,KAAK,MAAM,MAAM,SAAS,CAAC,MAAM,IAAI;AACtD,YAAM,IAAI;AAAA,IACZ;AACA,eAAW;AAAA,EACb,OAAO;AACL,eAAW;AAAA,EACb;AAEA,SAAO,EAAE,OAAO,KAAK,SAAS;AAChC;AAaA,SAAS,gBAAgB,UAAoB,UAAgC;AAC3E,QAAM,IAAI,SAAS;AACnB,QAAM,IAAI,SAAS;AAEnB,MAAI,MAAM,KAAK,MAAM,EAAG,QAAO,CAAC;AAChC,MAAI,MAAM,EAAG,QAAO,SAAS,IAAI,CAAC,UAAU,EAAE,IAAI,OAAa,KAAK,EAAE;AACtE,MAAI,MAAM,EAAG,QAAO,SAAS,IAAI,CAAC,UAAU,EAAE,IAAI,UAAgB,KAAK,EAAE;AAGzE,QAAM,QAA+B,CAAC;AAEtC,QAAM,OAAO,IAAI;AAEjB,QAAM,IAAc,IAAI,MAAM,IAAI,OAAO,CAAC,EAAE,KAAK,EAAE;AAEnD,QAAM,SAAS;AAEf,IAAE,SAAS,CAAC,IAAI;AAGhB;AACA,aAAS,IAAI,GAAG,KAAK,MAAM,KAAK;AAE9B,YAAM,WAAW,oBAAI,IAAoB;AAEzC,YAAM,SAAS,CAAC;AAChB,YAAM,OAAO;AAEb,eAAS,IAAI,QAAQ,KAAK,MAAM,KAAK,GAAG;AACtC,cAAM,KAAK,IAAI;AACf,YAAI;AAEJ,YAAI,MAAM,CAAC,KAAM,MAAM,KAAK,EAAE,KAAK,CAAC,IAAK,EAAE,KAAK,CAAC,GAAK;AACpD,cAAI,EAAE,KAAK,CAAC;AAAA,QACd,OAAO;AACL,cAAI,EAAE,KAAK,CAAC,IAAK;AAAA,QACnB;AAEA,YAAI,IAAI,IAAI;AAGZ,eAAO,IAAI,KAAK,IAAI,KAAK,SAAS,CAAC,MAAM,SAAS,CAAC,GAAG;AACpD;AACA;AAAA,QACF;AAEA,UAAE,EAAE,IAAI;AACR,iBAAS,IAAI,GAAG,CAAC;AAGjB,YAAI,KAAK,KAAK,KAAK,GAAG;AACpB,gBAAM,KAAK,QAAQ;AAEnB,iBAAO,UAAU,OAAO,GAAG,GAAG,UAAU,UAAU,MAAM;AAAA,QAC1D;AAAA,MACF;AAEA,YAAM,KAAK,QAAQ;AAAA,IACrB;AAGA,SAAO,SAAS,IAAI,CAAC,UAAU,EAAE,IAAI,UAAgB,KAAK,EAAE,EACzD,OAAO,SAAS,IAAI,CAAC,UAAU,EAAE,IAAI,OAAa,KAAK,EAAE,CAAC;AAC/D;AAQA,SAAS,UACP,OACA,GACA,GACA,UACA,UACA,QACY;AACZ,QAAM,SAAqB,CAAC;AAE5B,MAAI,IAAI;AACR,MAAI,IAAI;AAGR,WAAS,IAAI,MAAM,SAAS,GAAG,KAAK,GAAG,KAAK;AAC1C,UAAM,IAAI,IAAI;AAId,UAAM,eAAe,IAAI,IAAK,MAAM,IAAI,CAAC,IAA4B,oBAAI,IAAI;AAC7E,UAAM,YAAY,aAAa,IAAI,IAAI,CAAC;AACxC,UAAM,UAAU,aAAa,IAAI,IAAI,CAAC;AAEtC,QAAI;AACJ,QAAI,MAAM,CAAC,GAAG;AAEZ,cAAQ,IAAI;AAAA,IACd,WAAW,MAAM,GAAG;AAElB,cAAQ,IAAI;AAAA,IACd,YAAY,aAAa,OAAO,WAAW,KAAK;AAE9C,cAAQ,IAAI;AAAA,IACd,OAAO;AAEL,cAAQ,IAAI;AAAA,IACd;AAEA,UAAM,QAAQ,aAAa,IAAI,KAAK,KAAK;AACzC,UAAM,QAAQ,QAAQ;AAItB,QAAI,UAAU,IAAI,GAAG;AAMnB,aAAO,IAAI,QAAQ,KAAK,IAAI,OAAO;AACjC;AACA;AACA,eAAO,QAAQ,EAAE,IAAI,SAAS,MAAM,SAAS,CAAC,EAAY,CAAC;AAAA,MAC7D;AAGA,UAAI,IAAI,OAAO;AACb;AACA,eAAO,QAAQ,EAAE,IAAI,UAAU,MAAM,SAAS,CAAC,EAAY,CAAC;AAAA,MAC9D;AAAA,IACF,OAAO;AAML,aAAO,IAAI,SAAS,IAAI,QAAQ,GAAG;AACjC;AACA;AACA,eAAO,QAAQ,EAAE,IAAI,SAAS,MAAM,SAAS,CAAC,EAAY,CAAC;AAAA,MAC7D;AAGA,UAAI,IAAI,OAAO;AACb;AACA,eAAO,QAAQ,EAAE,IAAI,OAAO,MAAM,SAAS,CAAC,EAAY,CAAC;AAAA,MAC3D;AAAA,IACF;AAAA,EACF;AAIA,SAAO,IAAI,KAAK,IAAI,GAAG;AACrB;AACA;AACA,WAAO,QAAQ,EAAE,IAAI,SAAS,MAAM,SAAS,CAAC,EAAY,CAAC;AAAA,EAC7D;AAEA,SAAO;AACT;AAOA,IAAM,gBAAgB;AAKtB,SAAS,eAAe,WAAmC;AACzD,MAAI,UAAU,WAAW,EAAG,QAAO,CAAC;AAGpC,QAAM,gBAA0B,CAAC;AACjC,WAAS,IAAI,GAAG,IAAI,UAAU,QAAQ,KAAK;AACzC,UAAM,OAAO,UAAU,CAAC;AACxB,QAAI,KAAK,OAAO,SAAS;AACvB,oBAAc,KAAK,CAAC;AAAA,IACtB;AAAA,EACF;AAEA,MAAI,cAAc,WAAW,EAAG,QAAO,CAAC;AAGxC,QAAM,SAAqB,CAAC,CAAC,cAAc,CAAC,CAAW,CAAC;AAExD,WAAS,MAAM,GAAG,MAAM,cAAc,QAAQ,OAAO;AACnD,UAAM,YAAY,OAAO,OAAO,SAAS,CAAC;AAC1C,UAAM,UAAU,UAAU,UAAU,SAAS,CAAC;AAC9C,UAAM,UAAU,cAAc,GAAG;AAEjC,QAAI,MAAM;AACV,aAAS,IAAI,UAAU,GAAG,IAAI,SAAS,KAAK;AAC1C,YAAM,OAAO,UAAU,CAAC;AACxB,UAAI,KAAK,OAAO,QAAS;AAAA,IAC3B;AAEA,QAAI,OAAO,IAAI,eAAe;AAC5B,gBAAU,KAAK,OAAO;AAAA,IACxB,OAAO;AACL,aAAO,KAAK,CAAC,OAAO,CAAC;AAAA,IACvB;AAAA,EACF;AAGA,QAAM,QAAoB,CAAC;AAE3B,aAAW,SAAS,QAAQ;AAC1B,UAAM,iBAAiB,MAAM,CAAC;AAC9B,UAAM,gBAAgB,MAAM,MAAM,SAAS,CAAC;AAE5C,UAAM,WAAW,KAAK,IAAI,GAAG,iBAAiB,aAAa;AAC3D,UAAM,SAAS,KAAK,IAAI,UAAU,SAAS,GAAG,gBAAgB,aAAa;AAE3E,UAAM,QAAQ,UAAU,MAAM,UAAU,SAAS,CAAC;AAElD,QAAI,UAAU;AACd,QAAI,UAAU;AACd,aAAS,IAAI,GAAG,IAAI,UAAU,KAAK;AACjC,YAAM,OAAO,UAAU,CAAC;AACxB,UAAI,KAAK,OAAO,WAAW,KAAK,OAAO,SAAU;AACjD,UAAI,KAAK,OAAO,WAAW,KAAK,OAAO,MAAO;AAAA,IAChD;AAEA,QAAI,WAAW;AACf,QAAI,WAAW;AACf,eAAW,YAAY,OAAO;AAC5B,UAAI,SAAS,OAAO,WAAW,SAAS,OAAO,SAAU;AACzD,UAAI,SAAS,OAAO,WAAW,SAAS,OAAO,MAAO;AAAA,IACxD;AAEA,UAAM,KAAK;AAAA,MACT,UAAU,UAAU;AAAA,MACpB;AAAA,MACA,UAAU,UAAU;AAAA,MACpB;AAAA,MACA;AAAA,IACF,CAAC;AAAA,EACH;AAEA,SAAO;AACT;AAMA,SAAS,kBAAkB,OAAmB,UAA0B;AACtE,MAAI,MAAM,WAAW,EAAG,QAAO;AAE/B,QAAM,QAAkB,CAAC;AACzB,QAAM,WAAW,gBAAgB,QAAQ;AAEzC,QAAM,KAAK,SAAS,QAAQ,EAAE;AAC9B,QAAM,KAAK,SAAS,QAAQ,EAAE;AAE9B,aAAW,QAAQ,OAAO;AACxB,UAAM;AAAA,MACJ,OAAO,OAAO,KAAK,QAAQ,CAAC,IAAI,OAAO,KAAK,QAAQ,CAAC,KAAK,OAAO,KAAK,QAAQ,CAAC,IAAI,OAAO,KAAK,QAAQ,CAAC;AAAA,IAC1G;AAEA,eAAW,YAAY,KAAK,OAAO;AACjC,cAAQ,SAAS,IAAI;AAAA,QACnB,KAAK;AACH,gBAAM,KAAK,IAAI,SAAS,IAAI,EAAE;AAC9B;AAAA,QACF,KAAK;AACH,gBAAM,KAAK,IAAI,SAAS,IAAI,EAAE;AAC9B;AAAA,QACF,KAAK;AACH,gBAAM,KAAK,IAAI,SAAS,IAAI,EAAE;AAC9B;AAAA,MACJ;AAAA,IACF;AAAA,EACF;AAEA,SAAO,MAAM,KAAK,IAAI;AACxB;AAEA,SAAS,kBAAkB,OAAiB,UAA0B;AACpE,QAAM,WAAW,gBAAgB,QAAQ;AACzC,QAAM,QAAkB,CAAC;AAEzB,QAAM,KAAK,eAAe;AAC1B,QAAM,KAAK,SAAS,QAAQ,EAAE;AAC9B,QAAM,KAAK,cAAc,OAAO,MAAM,MAAM,CAAC,KAAK;AAElD,aAAW,QAAQ,OAAO;AACxB,UAAM,KAAK,IAAI,IAAI,EAAE;AAAA,EACvB;AAEA,SAAO,MAAM,KAAK,IAAI;AACxB;AAEA,SAAS,sBAAsB,OAAiB,UAA0B;AACxE,QAAM,WAAW,gBAAgB,QAAQ;AACzC,QAAM,QAAkB,CAAC;AAEzB,QAAM,KAAK,SAAS,QAAQ,EAAE;AAC9B,QAAM,KAAK,eAAe;AAC1B,QAAM,KAAK,SAAS,OAAO,MAAM,MAAM,CAAC,UAAU;AAElD,aAAW,QAAQ,OAAO;AACxB,UAAM,KAAK,IAAI,IAAI,EAAE;AAAA,EACvB;AAEA,SAAO,MAAM,KAAK,IAAI;AACxB;AAEA,SAAS,gBAAgB,UAA0B;AACjD,QAAM,aAAa,SAAS,QAAQ,OAAO,GAAG;AAC9C,SAAO,WAAW,MAAM,GAAG,EAAE,IAAI,KAAK;AACxC;AAYO,SAAS,gBACd,YACA,YACA,UACU;AACV,MAAI,eAAe,YAAY;AAC7B,WAAO;AAAA,MACL;AAAA,MACA,OAAO;AAAA,MACP,eAAe,WAAW,SAAS;AAAA,MACnC,WAAW;AAAA,MACX,WAAW;AAAA,IACb;AAAA,EACF;AAEA,MAAI,WAAW,WAAW,GAAG;AAC3B,UAAM,QAAQ,WAAW,UAAU;AACnC,UAAME,SAAQ,kBAAkB,MAAM,OAAO,QAAQ;AACrD,WAAO;AAAA,MACL;AAAA,MACA,OAAAA;AAAA,MACA,eAAe;AAAA,MACf,WAAW,MAAM,MAAM;AAAA,MACvB,WAAW;AAAA,IACb;AAAA,EACF;AAEA,MAAI,WAAW,WAAW,GAAG;AAC3B,UAAM,QAAQ,WAAW,UAAU;AACnC,UAAMA,SAAQ,sBAAsB,MAAM,OAAO,QAAQ;AACzD,WAAO;AAAA,MACL;AAAA,MACA,OAAAA;AAAA,MACA,eAAe;AAAA,MACf,WAAW;AAAA,MACX,WAAW,MAAM,MAAM;AAAA,IACzB;AAAA,EACF;AAEA,QAAM,WAAW,WAAW,UAAU;AACtC,QAAM,WAAW,WAAW,UAAU;AAEtC,QAAM,YAAY,gBAAgB,SAAS,OAAO,SAAS,KAAK;AAEhE,MAAI,YAAY;AAChB,MAAI,YAAY;AAChB,aAAW,QAAQ,WAAW;AAC5B,QAAI,KAAK,OAAO,MAAO;AACvB,QAAI,KAAK,OAAO,SAAU;AAAA,EAC5B;AAEA,QAAM,QAAQ,eAAe,SAAS;AACtC,QAAM,QAAQ,kBAAkB,OAAO,QAAQ;AAE/C,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,eAAe;AAAA,IACf;AAAA,IACA;AAAA,EACF;AACF;;;AChdA,SAAS,aAAAC,kBAAiB;AAUnB,SAAS,UAAU,MAAsB;AAC9C,MAAI,KAAK,WAAW,EAAG,QAAO;AAE9B,QAAM,UAAU,KAAK,QAAQ,MAAM;AACnC,QAAM,QAAQ,KAAK,QAAQ,IAAI;AAE/B,MAAI,YAAY,OAAO,UAAU,MAAM,WAAW,QAAQ;AACxD,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAKO,SAAS,mBAAmB,MAAuB;AACxD,SAAO,KAAK,SAAS,IAAI,KAAK,KAAK,SAAS,MAAM;AACpD;AAgBO,SAAS,aAAa,iBAAyB,YAA4B;AAChF,QAAM,YAAY,UAAU,eAAe;AAG3C,QAAM,QAAQ,WAAW,QAAQ,SAAS,IAAI,EAAE,MAAM,IAAI;AAG1D,QAAM,sBAAsB,mBAAmB,eAAe;AAI9D,QAAM,iBAAiB,MAAM,SAAS,KAAK,MAAM,MAAM,SAAS,CAAC,MAAM;AAIvE,MAAI,SAAS,MAAM,KAAK,SAAS;AACjC,MAAI,uBAAuB,CAAC,gBAAgB;AAC1C,cAAU;AAAA,EACZ;AAEA,MAAI,CAAC,uBAAuB,gBAAgB;AAE1C,QAAI,OAAO,SAAS,SAAS,GAAG;AAC9B,eAAS,OAAO,MAAM,GAAG,CAAC,UAAU,MAAM;AAAA,IAC5C;AAAA,EACF;AAEA,SAAO;AACT;AAeA,eAAsB,iBACpB,UACA,iBACA,YACe;AACf,QAAM,UACJ,gBAAgB,SAAS,IACrB,aAAa,iBAAiB,UAAU,IACxC;AACN,QAAMA,WAAU,UAAU,SAAS,OAAO;AAC5C;;;ACzGA,SAAS,YAAAC,WAAU,YAAY;AAC/B,SAAS,YAAY;AACrB,SAAS,YAAAC,iBAAgB;AAezB,eAAe,YAAY,UAAoC;AAC7D,QAAM,aAAa,MAAM,KAAK,UAAU,GAAG;AAC3C,MAAI;AACF,UAAM,SAAS,OAAO,MAAM,IAAI;AAChC,UAAM,EAAE,UAAU,IAAI,MAAM,WAAW,KAAK,QAAQ,GAAG,MAAM,CAAC;AAC9D,WAAO,OAAO,SAAS,GAAG,SAAS,EAAE,SAAS,CAAC;AAAA,EACjD,UAAE;AACA,UAAM,WAAW,MAAM;AAAA,EACzB;AACF;AAYO,IAAM,eAAwC;AAAA,EACnD,MAAM;AAAA,EACN;AAAA,EACA,aACE;AAAA,EACF,YAAY;AAAA,IACV,MAAM;AAAA,IACN,YAAY;AAAA,MACV,MAAM;AAAA,QACJ,MAAM;AAAA,QACN,aAAa;AAAA,MACf;AAAA,MACA,WAAW;AAAA,QACT,MAAM;AAAA,QACN,aAAa;AAAA,MACf;AAAA,MACA,SAAS;AAAA,QACP,MAAM;AAAA,QACN,aAAa;AAAA,MACf;AAAA,IACF;AAAA,IACA,UAAU,CAAC,MAAM;AAAA,IACjB,sBAAsB;AAAA,EACxB;AAAA,EAEA,MAAM,QAAQ,MAAoB,KAAuC;AACvE,QAAI,CAAC,MAAM,QAAQ,OAAO,KAAK,SAAS,UAAU;AAChD,aAAO,EAAE,SAAS,OAAO,MAAM,6CAAe,OAAO,eAAe;AAAA,IACtE;AAEA,UAAM,WAAW,YAAY,KAAK,MAAM,IAAI,GAAG;AAE/C,QAAI;AAEF,YAAM,WAAW,MAAM,KAAK,QAAQ;AACpC,YAAM,UAAU,KAAK,OAAO;AAC5B,UAAI,SAAS,OAAO,SAAS;AAC3B,eAAO;AAAA,UACL,SAAS;AAAA,UACT,MAAM,kCAAS,SAAS,OAAO,OAAO,MAAM,QAAQ,CAAC,CAAC;AAAA,UACtD,OAAO;AAAA,QACT;AAAA,MACF;AAGA,UAAI,SAAS,YAAY,GAAG;AAC1B,eAAO;AAAA,UACL,SAAS;AAAA,UACT,MAAM,IAAI,QAAQ;AAAA,UAClB,OAAO;AAAA,QACT;AAAA,MACF;AAGA,UAAI,SAAS,OAAO,GAAG;AACrB,cAAM,QAAQ,MAAM,YAAY,QAAQ;AACxC,YAAI,OAAO;AACT,iBAAO;AAAA,YACL,SAAS;AAAA,YACT,MAAM,IAAI,QAAQ;AAAA,YAClB,OAAO;AAAA,UACT;AAAA,QACF;AAAA,MACF;AAEA,YAAM,UAAU,MAAMC,UAAS,UAAU,OAAO;AAChD,YAAM,QAAQ,QAAQ,MAAM,IAAI;AAGhC,UAAI,MAAM,SAAS,KAAK,MAAM,MAAM,SAAS,CAAC,MAAM,MAAM,QAAQ,SAAS,IAAI,GAAG;AAChF,cAAM,IAAI;AAAA,MACZ;AAGA,YAAM,YAAY,KAAK,IAAI,GAAG,KAAK,aAAa,CAAC,IAAI;AACrD,YAAM,UAAU,KAAK,UACjB,KAAK,IAAI,KAAK,SAAS,MAAM,MAAM,IACnC,MAAM;AACV,YAAM,gBAAgB,MAAM,MAAM,WAAW,OAAO;AAGpD,YAAM,eAAe,OAAO,OAAO,EAAE;AACrC,YAAM,SAAS,cACZ,IAAI,CAAC,MAAM,MAAM;AAChB,cAAM,UAAU,OAAO,YAAY,IAAI,CAAC,EAAE,SAAS,cAAc,GAAG;AACpE,eAAO,GAAG,OAAO,SAAI,IAAI;AAAA,MAC3B,CAAC,EACA,KAAK,IAAI;AAGZ,YAAM,YAAY,MAAM,SAAS;AACjC,YAAM,WAAW,YAAY,IACzB;AAAA;AAAA,gBAAW,SAAS,uCAAmB,UAAU,CAAC,+BAClD;AAGJ,YAAM,UAAUC,UAAS,IAAI,KAAK,QAAQ,EAAE,QAAQ,OAAO,GAAG;AAC9D,YAAM,aAAc,YAAY,KAAK,UAAU,MAAM,SACjD,UAAK,YAAY,CAAC,IAAI,OAAO,YAC7B,GAAG,MAAM,MAAM;AAEnB,aAAO;AAAA,QACL,SAAS;AAAA,QACT,MAAM,eAAe,MAAM,IAAI;AAAA,QAC/B,SAAS,aAAM,OAAO,SAAI,UAAU;AAAA,MACtC;AAAA,IACF,SAAS,KAAc;AACrB,YAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,aAAO;AAAA,QACL,SAAS;AAAA,QACT,MAAM,6CAAU,OAAO;AAAA,QACvB,OAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AACF;;;ACzJA,SAAS,SAAAC,QAAO,YAAAC,iBAAgB;AAChC,SAAS,SAAS,YAAAC,WAAU,gBAAgB;AAiBrC,IAAM,gBAA0C;AAAA,EACrD,MAAM;AAAA,EACN;AAAA,EACA,aACE;AAAA,EACF,YAAY;AAAA,IACV,MAAM;AAAA,IACN,YAAY;AAAA,MACV,MAAM;AAAA,QACJ,MAAM;AAAA,QACN,aAAa;AAAA,MACf;AAAA,MACA,SAAS;AAAA,QACP,MAAM;AAAA,QACN,aAAa;AAAA,MACf;AAAA,IACF;AAAA,IACA,UAAU,CAAC,QAAQ,SAAS;AAAA,IAC5B,sBAAsB;AAAA,EACxB;AAAA,EAEA,MAAM,QAAQ,MAAqB,KAAuC;AACxE,QAAI,CAAC,MAAM,QAAQ,OAAO,KAAK,SAAS,UAAU;AAChD,aAAO,EAAE,SAAS,OAAO,MAAM,6CAAe,OAAO,eAAe;AAAA,IACtE;AACA,QAAI,KAAK,YAAY,UAAa,KAAK,YAAY,MAAM;AACvD,aAAO,EAAE,SAAS,OAAO,MAAM,gDAAkB,OAAO,eAAe;AAAA,IACzE;AAEA,UAAM,WAAW,YAAY,KAAK,MAAM,IAAI,GAAG;AAE/C,QAAI;AACF,UAAI,IAAI,cAAc,IAAI,WAAW,SAAS,GAAG;AAC/C,cAAM,OAAO,MAAM,QAAQ,IAAI,YAAY,QAAQ;AACnD,YAAI,CAAC,KAAK,IAAI;AACZ,iBAAO,EAAE,SAAS,OAAO,MAAM,KAAK,OAAO,OAAO,sBAAsB;AAAA,QAC1E;AAAA,MACF;AAEA,UAAI,aAAa;AACjB,UAAI,gBAAgB;AACpB,UAAI;AACF,qBAAa,MAAMC,UAAS,UAAU,OAAO;AAC7C,wBAAgB;AAAA,MAClB,QAAQ;AAAA,MAER;AAEA,YAAMC,OAAM,QAAQ,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;AAElD,YAAM,UAAU,OAAO,KAAK,OAAO;AACnC,YAAM,iBAAiB,UAAU,YAAY,OAAO;AAEpD,YAAM,OAAO,gBAAgB,YAAY,SAAS,QAAQ;AAC1D,WAAK,gBAAgB;AAErB,YAAM,YAAY,QAAQ,MAAM,IAAI,EAAE;AACtC,YAAM,WAAW,OAAO,WAAW,SAAS,OAAO;AAEnD,YAAM,SAAS,gBAAgB,uBAAQ;AACvC,YAAM,cAAc,gBAChB,UAAK,KAAK,SAAS,KAAK,KAAK,SAAS,KACtC,UAAK,KAAK,SAAS;AAEvB,YAAM,WAAW,SAAS,QAAQ;AAClC,YAAM,UAAU,gBACZ,2BAAU,QAAQ,MAAM,KAAK,SAAS,KAAK,KAAK,SAAS,MACzD,2BAAU,QAAQ,MAAM,KAAK,SAAS;AAE1C,aAAO;AAAA,QACL,SAAS;AAAA,QACT,MAAM,eAAK,MAAM,SAAIC,UAAS,IAAI,KAAK,QAAQ,EAAE,QAAQ,OAAO,GAAG,CAAC,SAAI,SAAS,gBAAM,QAAQ,gBAAM,WAAW;AAAA,QAChH;AAAA,QACA;AAAA,MACF;AAAA,IACF,SAAS,KAAc;AACrB,YAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,aAAO;AAAA,QACL,SAAS;AAAA,QACT,MAAM,6CAAU,OAAO;AAAA,QACvB,OAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AACF;;;ACtGA,SAAS,YAAAC,iBAAgB;AACzB,SAAS,YAAAC,iBAAgB;AAmBlB,IAAM,eAAwC;AAAA,EACnD,MAAM;AAAA,EACN;AAAA,EACA,aACE;AAAA,EACF,YAAY;AAAA,IACV,MAAM;AAAA,IACN,YAAY;AAAA,MACV,MAAM;AAAA,QACJ,MAAM;AAAA,QACN,aAAa;AAAA,MACf;AAAA,MACA,UAAU;AAAA,QACR,MAAM;AAAA,QACN,aAAa;AAAA,MACf;AAAA,MACA,UAAU;AAAA,QACR,MAAM;AAAA,QACN,aAAa;AAAA,MACf;AAAA,IACF;AAAA,IACA,UAAU,CAAC,QAAQ,YAAY,UAAU;AAAA,IACzC,sBAAsB;AAAA,EACxB;AAAA,EAEA,MAAM,QAAQ,MAAoB,KAAuC;AACvE,QAAI,CAAC,MAAM,QAAQ,OAAO,KAAK,SAAS,UAAU;AAChD,aAAO,EAAE,SAAS,OAAO,MAAM,6CAAe,OAAO,eAAe;AAAA,IACtE;AACA,QAAI,OAAO,KAAK,aAAa,UAAU;AACrC,aAAO,EAAE,SAAS,OAAO,MAAM,iDAAmB,OAAO,eAAe;AAAA,IAC1E;AACA,QAAI,OAAO,KAAK,aAAa,UAAU;AACrC,aAAO,EAAE,SAAS,OAAO,MAAM,iDAAmB,OAAO,eAAe;AAAA,IAC1E;AAEA,UAAM,WAAW,YAAY,KAAK,MAAM,IAAI,GAAG;AAE/C,QAAI,IAAI,cAAc,IAAI,WAAW,SAAS,GAAG;AAC/C,YAAM,OAAO,MAAM,QAAQ,IAAI,YAAY,QAAQ;AACnD,UAAI,CAAC,KAAK,IAAI;AACZ,eAAO,EAAE,SAAS,OAAO,MAAM,KAAK,OAAO,OAAO,sBAAsB;AAAA,MAC1E;AAAA,IACF;AAEA,QAAI;AACF,YAAM,UAAU,MAAMC,UAAS,UAAU,OAAO;AAEhD,YAAM,aAAa,QAAQ,QAAQ,KAAK,QAAQ;AAChD,UAAI,eAAe,IAAI;AACrB,eAAO;AAAA,UACL,SAAS;AAAA,UACT,MAAM;AAAA,UACN,OAAO;AAAA,QACT;AAAA,MACF;AAEA,YAAM,cAAc,QAAQ,QAAQ,KAAK,UAAU,aAAa,CAAC;AACjE,UAAI,gBAAgB,IAAI;AACtB,eAAO;AAAA,UACL,SAAS;AAAA,UACT,MAAM;AAAA,UACN,OAAO;AAAA,QACT;AAAA,MACF;AAEA,YAAM,aAAa,QAAQ,QAAQ,KAAK,UAAU,KAAK,QAAQ;AAC/D,YAAM,iBAAiB,UAAU,SAAS,UAAU;AAEpD,YAAM,OAAO,gBAAgB,SAAS,YAAY,QAAQ;AAC1D,WAAK,gBAAgB;AAErB,YAAM,aAAa,QAAQ,MAAM,GAAG,UAAU;AAC9C,YAAM,YAAY,WAAW,MAAM,IAAI,EAAE;AACzC,YAAM,WAAW,KAAK,SAAS,MAAM,IAAI,EAAE;AAC3C,YAAM,WAAW,KAAK,SAAS,MAAM,IAAI,EAAE;AAE3C,YAAM,cAAc,IAAI,KAAK,SAAS,KAAK,KAAK,SAAS;AACzD,YAAM,UAAU,2BAAUC,UAAS,QAAQ,CAAC,MAAM,KAAK,SAAS,KAAK,KAAK,SAAS;AAEnF,aAAO;AAAA,QACL,SAAS;AAAA,QACT,MAAM,uCAAS,QAAQ;AAAA,uCAAY,SAAS;AAAA,EAAO,QAAQ,kBAAQ,QAAQ;AAAA,oBAAU,WAAW;AAAA,QAChG;AAAA,QACA;AAAA,MACF;AAAA,IACF,SAAS,KAAc;AACrB,YAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,aAAO;AAAA,QACL,SAAS;AAAA,QACT,MAAM,6CAAU,OAAO;AAAA,QACvB,OAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AACF;;;ACnHA,SAAS,YAAAC,iBAAgB;AACzB,SAAS,YAAAC,iBAAgB;AA2BlB,IAAM,gBAA0C;AAAA,EACrD,MAAM;AAAA,EACN;AAAA,EACA,aACE;AAAA,EACF,YAAY;AAAA,IACV,MAAM;AAAA,IACN,YAAY;AAAA,MACV,MAAM;AAAA,QACJ,MAAM;AAAA,QACN,aAAa;AAAA,MACf;AAAA,MACA,OAAO;AAAA,QACL,MAAM;AAAA,QACN,aAAa;AAAA,QACb,OAAO;AAAA,UACL,MAAM;AAAA,UACN,YAAY;AAAA,YACV,SAAS,EAAE,MAAM,UAAU,aAAa,uFAAiB;AAAA,YACzD,SAAS,EAAE,MAAM,UAAU,aAAa,6CAAU;AAAA,YAClD,YAAY,EAAE,MAAM,WAAW,aAAa,iFAAqB;AAAA,UACnE;AAAA,UACA,UAAU,CAAC,WAAW,SAAS;AAAA,UAC/B,sBAAsB;AAAA,QACxB;AAAA,MACF;AAAA,IACF;AAAA,IACA,UAAU,CAAC,QAAQ,OAAO;AAAA,IAC1B,sBAAsB;AAAA,EACxB;AAAA,EAEA,MAAM,QAAQ,MAAqB,KAAuC;AACxE,QAAI,CAAC,MAAM,QAAQ,OAAO,KAAK,SAAS,UAAU;AAChD,aAAO,EAAE,SAAS,OAAO,MAAM,6CAAe,OAAO,eAAe;AAAA,IACtE;AACA,QAAI,CAAC,MAAM,QAAQ,KAAK,KAAK,KAAK,KAAK,MAAM,WAAW,GAAG;AACzD,aAAO,EAAE,SAAS,OAAO,MAAM,kFAAsB,OAAO,eAAe;AAAA,IAC7E;AAEA,UAAM,WAAW,YAAY,KAAK,MAAM,IAAI,GAAG;AAE/C,QAAI,IAAI,cAAc,IAAI,WAAW,SAAS,GAAG;AAC/C,YAAM,OAAO,MAAM,QAAQ,IAAI,YAAY,QAAQ;AACnD,UAAI,CAAC,KAAK,IAAI;AACZ,eAAO,EAAE,SAAS,OAAO,MAAM,KAAK,OAAO,OAAO,sBAAsB;AAAA,MAC1E;AAAA,IACF;AAEA,QAAI;AACF,YAAM,kBAAkB,MAAMC,UAAS,UAAU,OAAO;AACxD,UAAI,iBAAiB;AAErB,eAAS,MAAM,GAAG,MAAM,KAAK,MAAM,QAAQ,OAAO;AAChD,cAAM,OAAO,KAAK,MAAM,GAAG;AAC3B,YAAI,OAAO,KAAK,YAAY,UAAU;AACpC,iBAAO;AAAA,YACL,SAAS;AAAA,YACT,MAAM,UAAK,MAAM,CAAC;AAAA,YAClB,OAAO;AAAA,UACT;AAAA,QACF;AAEA,YAAI,KAAK,YAAY;AACnB,cAAI,QAAQ;AACZ,cAAI,MAAM;AACV,kBAAQ,MAAM,eAAe,QAAQ,KAAK,SAAS,MAAM,CAAC,OAAO,IAAI;AACnE;AAAA,UACF;AAEA,cAAI,UAAU,GAAG;AACf,mBAAO;AAAA,cACL,SAAS;AAAA,cACT,MAAM,UAAK,MAAM,CAAC;AAAA,cAClB,OAAO;AAAA,YACT;AAAA,UACF;AAEA,2BAAiB,eAAe,MAAM,KAAK,OAAO,EAAE,KAAK,KAAK,OAAO;AAAA,QACvE,OAAO;AACL,gBAAM,WAAW,eAAe,QAAQ,KAAK,OAAO;AACpD,cAAI,aAAa,IAAI;AACnB,mBAAO;AAAA,cACL,SAAS;AAAA,cACT,MAAM,UAAK,MAAM,CAAC;AAAA,cAClB,OAAO;AAAA,YACT;AAAA,UACF;AAEA,gBAAM,YAAY,eAAe,QAAQ,KAAK,SAAS,WAAW,CAAC;AACnE,cAAI,cAAc,IAAI;AACpB,mBAAO;AAAA,cACL,SAAS;AAAA,cACT,MAAM,UAAK,MAAM,CAAC;AAAA,cAClB,OAAO;AAAA,YACT;AAAA,UACF;AAEA,2BAAiB,eAAe,QAAQ,KAAK,SAAS,KAAK,OAAO;AAAA,QACpE;AAAA,MACF;AAEA,YAAM,iBAAiB,UAAU,iBAAiB,cAAc;AAEhE,YAAM,OAAO,gBAAgB,iBAAiB,gBAAgB,QAAQ;AACtE,WAAK,gBAAgB;AAErB,aAAO;AAAA,QACL,SAAS;AAAA,QACT,MAAM,uCAAS,QAAQ;AAAA,qBAAS,KAAK,MAAM,MAAM;AAAA,qBAAa,KAAK,SAAS,KAAK,KAAK,SAAS;AAAA,QAC/F,SAAS,2BAAUC,UAAS,QAAQ,CAAC,KAAK,KAAK,MAAM,MAAM,aAAQ,KAAK,SAAS,KAAK,KAAK,SAAS;AAAA,QACpG;AAAA,MACF;AAAA,IACF,SAAS,KAAc;AACrB,YAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,aAAO;AAAA,QACL,SAAS;AAAA,QACT,MAAM,6CAAU,OAAO;AAAA,QACvB,OAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AACF;;;ACrJA,SAAS,YAAAC,iBAAgB;AACzB,SAAS,YAAAC,iBAAgB;AAqBzB,SAAS,eAAe,OAAiB,QAAgB,OAAqD;AAC5G,QAAM,UAAoB,CAAC;AAC3B,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,QAAK,MAAM,CAAC,MAAiB,QAAQ;AACnC,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF;AAEA,MAAI,QAAQ,WAAW,GAAG;AACxB,WAAO,EAAE,OAAO,uBAAQ,KAAK,mFAAkB;AAAA,EACjD;AACA,MAAI,QAAQ,SAAS,GAAG;AACtB,WAAO,EAAE,OAAO,IAAI,KAAK,4DAAe,QAAQ,MAAM,4EAAgB;AAAA,EACxE;AAEA,SAAO,EAAE,MAAM,QAAQ,CAAC,EAAY;AACtC;AAKO,IAAM,kBAA8C;AAAA,EACzD,MAAM;AAAA,EACN;AAAA,EACA,aACE;AAAA,EACF,YAAY;AAAA,IACV,MAAM;AAAA,IACN,YAAY;AAAA,MACV,MAAM;AAAA,QACJ,MAAM;AAAA,QACN,aAAa;AAAA,MACf;AAAA,MACA,aAAa;AAAA,QACX,MAAM;AAAA,QACN,aAAa;AAAA,MACf;AAAA,MACA,WAAW;AAAA,QACT,MAAM;AAAA,QACN,aAAa;AAAA,MACf;AAAA,MACA,WAAW;AAAA,QACT,MAAM;AAAA,QACN,aAAa;AAAA,MACf;AAAA,IACF;AAAA,IACA,UAAU,CAAC,QAAQ,eAAe,WAAW;AAAA,IAC7C,sBAAsB;AAAA,EACxB;AAAA,EAEA,MAAM,QAAQ,MAAuB,KAAuC;AAC1E,QAAI,CAAC,MAAM,QAAQ,OAAO,KAAK,SAAS,UAAU;AAChD,aAAO,EAAE,SAAS,OAAO,MAAM,6CAAe,OAAO,eAAe;AAAA,IACtE;AACA,QAAI,OAAO,KAAK,gBAAgB,UAAU;AACxC,aAAO,EAAE,SAAS,OAAO,MAAM,oDAAsB,OAAO,eAAe;AAAA,IAC7E;AACA,QAAI,OAAO,KAAK,cAAc,UAAU;AACtC,aAAO,EAAE,SAAS,OAAO,MAAM,kDAAoB,OAAO,eAAe;AAAA,IAC3E;AAEA,UAAM,WAAW,YAAY,KAAK,MAAM,IAAI,GAAG;AAC/C,UAAM,YAAY,KAAK,aAAa;AAEpC,QAAI,IAAI,cAAc,IAAI,WAAW,SAAS,GAAG;AAC/C,YAAM,OAAO,MAAM,QAAQ,IAAI,YAAY,QAAQ;AACnD,UAAI,CAAC,KAAK,IAAI;AACZ,eAAO,EAAE,SAAS,OAAO,MAAM,KAAK,OAAO,OAAO,sBAAsB;AAAA,MAC1E;AAAA,IACF;AAEA,QAAI;AACF,YAAM,UAAU,MAAMC,UAAS,UAAU,OAAO;AAChD,YAAM,QAAQ,QAAQ,MAAM,IAAI;AAEhC,YAAM,cAAc,eAAe,OAAO,KAAK,aAAa,cAAc;AAC1E,UAAI,WAAW,aAAa;AAC1B,eAAO,EAAE,SAAS,OAAO,MAAM,YAAY,OAAO,OAAO,mBAAmB;AAAA,MAC9E;AAEA,YAAM,YAAY,eAAe,OAAO,KAAK,WAAW,YAAY;AACpE,UAAI,WAAW,WAAW;AACxB,eAAO,EAAE,SAAS,OAAO,MAAM,UAAU,OAAO,OAAO,mBAAmB;AAAA,MAC5E;AAEA,YAAM,YAAY,YAAY;AAC9B,YAAM,UAAU,UAAU;AAE1B,UAAI,aAAa,SAAS;AACxB,eAAO;AAAA,UACL,SAAS;AAAA,UACT,MAAM;AAAA,UACN,OAAO;AAAA,QACT;AAAA,MACF;AAEA,YAAM,aAAa,YAAY,YAAY,YAAY;AACvD,YAAM,WAAW,YAAY,UAAU,UAAU;AAEjD,UAAI,aAAa,UAAU;AACzB,eAAO;AAAA,UACL,SAAS;AAAA,UACT,MAAM;AAAA,UACN,OAAO;AAAA,QACT;AAAA,MACF;AAEA,YAAM,WAAW,CAAC,GAAG,MAAM,MAAM,GAAG,UAAU,GAAG,GAAG,MAAM,MAAM,WAAW,CAAC,CAAC;AAC7E,YAAM,aAAa,SAAS,KAAK,IAAI;AAErC,YAAM,iBAAiB,UAAU,SAAS,UAAU;AAEpD,YAAM,OAAO,gBAAgB,SAAS,YAAY,QAAQ;AAC1D,WAAK,gBAAgB;AAErB,YAAM,eAAe,WAAW,aAAa;AAE7C,YAAM,UAAU,2BAAUC,UAAS,QAAQ,CAAC,YAAO,YAAY,aAAQ,KAAK,SAAS,KAAK,KAAK,SAAS;AAExG,aAAO;AAAA,QACL,SAAS;AAAA,QACT,MAAM,uCAAS,QAAQ;AAAA,6CAAa,aAAa,CAAC,oBAAU,WAAW,CAAC,uBAAQ,YAAY;AAAA,qBAAY,KAAK,SAAS,KAAK,KAAK,SAAS;AAAA,QACzI;AAAA,QACA;AAAA,MACF;AAAA,IACF,SAAS,KAAc;AACrB,YAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,aAAO;AAAA,QACL,SAAS;AAAA,QACT,MAAM,6CAAU,OAAO;AAAA,QACvB,OAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AACF;;;AC5JA,OAAOC,cAAa;AAKpB,IAAMC,aAAYC,SAAQ,aAAa;AAahC,IAAM,WAAgC;AAAA,EAC3C,MAAM;AAAA,EACN;AAAA,EACA,aACE;AAAA,EACF,YAAY;AAAA,IACV,MAAM;AAAA,IACN,YAAY;AAAA,MACV,SAAS;AAAA,QACP,MAAM;AAAA,QACN,aAAa;AAAA,MACf;AAAA,MACA,SAAS;AAAA,QACP,MAAM;AAAA,QACN,aAAa;AAAA,MACf;AAAA,IACF;AAAA,IACA,UAAU,CAAC,SAAS;AAAA,IACpB,sBAAsB;AAAA,EACxB;AAAA,EAEA,MAAM,QAAQ,MAAgB,KAAuC;AACnE,QAAI,CAAC,MAAM,WAAW,OAAO,KAAK,YAAY,UAAU;AACtD,aAAO,EAAE,SAAS,OAAO,MAAM,gDAAkB,OAAO,eAAe;AAAA,IACzE;AAEA,UAAM,UAAU,KAAK,WAAW,IAAI,WAAW,kBAAkB;AAEjE,QAAI;AACF,YAAM,eAAeD,aAAY,QAAQ;AACzC,YAAM,YAAYA,aAAY,CAAC,MAAM,KAAK,OAAO,IAAI,CAAC,MAAM,KAAK,OAAO;AAExE,YAAM,SAAS,MAAM;AAAA,QACnB;AAAA,QACA;AAAA,QACA,IAAI;AAAA,QACJ;AAAA,QACA,IAAI;AAAA,QACJ;AAAA,MACF;AAEA,YAAM,QAAkB,CAAC;AACzB,UAAI,OAAO,QAAQ;AACjB,cAAM,KAAK,eAAe,OAAO,MAAM,CAAC;AAAA,MAC1C;AACA,UAAI,OAAO,QAAQ;AACjB,cAAM,KAAK;AAAA,EAAa,eAAe,OAAO,MAAM,CAAC,EAAE;AAAA,MACzD;AAEA,YAAM,UAAU,OAAO,aAAa;AACpC,YAAM,SAAS,MAAM,SAAS,IAAI,MAAM,KAAK,IAAI,IAAI;AAErD,YAAM,aAAa,KAAK,QAAQ,SAAS,KACrC,KAAK,QAAQ,MAAM,GAAG,EAAE,IAAI,QAC5B,KAAK;AACT,YAAM,UAAU,eAAQ,UAAU,cAAS,OAAO,YAAY,cAAI;AAElE,aAAO;AAAA,QACL;AAAA,QACA,MAAM,GAAG,MAAM;AAAA,uBAAW,OAAO,YAAY,cAAI;AAAA,QACjD;AAAA,QACA,OAAO,UAAU,SAAY,aAAa,OAAO,YAAY,SAAS;AAAA,MACxE;AAAA,IACF,SAAS,KAAc;AACrB,YAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,aAAO;AAAA,QACL,SAAS;AAAA,QACT,MAAM,6CAAU,OAAO;AAAA,QACvB,OAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AACF;;;AC1FA,SAAS,WAAAE,UAAS,QAAAC,aAAY;AAC9B,SAAS,QAAAC,OAAM,YAAAC,WAAU,cAAAC,mBAAkB;AAe3C,SAAS,YAAY,SAAyB;AAC5C,MAAI,WAAW;AACf,aAAW,SAAS,QAAQ,WAAW,oBAAoB;AAC3D,aAAW,SAAS,QAAQ,SAAS,cAAc;AACnD,aAAW,SAAS,QAAQ,OAAO,cAAc;AACjD,aAAW,SAAS,QAAQ,qBAAqB,MAAM;AACvD,aAAW,SAAS,QAAQ,OAAO,OAAO;AAC1C,aAAW,SAAS,QAAQ,uBAAuB,QAAQ;AAC3D,aAAW,SAAS,QAAQ,iBAAiB,IAAI;AACjD,aAAW,SAAS,QAAQ,iBAAiB,MAAM;AACnD,SAAO,IAAI,OAAO,IAAI,QAAQ,KAAK,GAAG;AACxC;AAKA,eAAe,QAAQ,KAAa,SAAoC;AACtE,QAAM,UAAoB,CAAC;AAC3B,MAAI;AAEJ,MAAI;AACF,cAAU,MAAMC,SAAQ,KAAK,EAAE,eAAe,KAAK,CAAC;AAAA,EACtD,QAAQ;AACN,WAAO;AAAA,EACT;AAEA,aAAW,SAAS,SAAS;AAC3B,QAAI,MAAM,YAAY,MAAM,MAAM,SAAS,kBAAkB,MAAM,SAAS,SAAS;AACnF;AAAA,IACF;AAEA,UAAM,WAAWC,MAAK,KAAK,MAAM,IAAI;AACrC,UAAM,UAAUC,UAAS,SAAS,QAAQ;AAE1C,QAAI,MAAM,YAAY,GAAG;AACvB,cAAQ,KAAK,GAAG,MAAM,QAAQ,UAAU,OAAO,CAAC;AAAA,IAClD,OAAO;AACL,cAAQ,KAAK,OAAO;AAAA,IACtB;AAAA,EACF;AAEA,SAAO;AACT;AAUO,IAAM,WAAgC;AAAA,EAC3C,MAAM;AAAA,EACN;AAAA,EACA,aACE;AAAA,EACF,YAAY;AAAA,IACV,MAAM;AAAA,IACN,YAAY;AAAA,MACV,SAAS;AAAA,QACP,MAAM;AAAA,QACN,aAAa;AAAA,MACf;AAAA,MACA,WAAW;AAAA,QACT,MAAM;AAAA,QACN,aAAa;AAAA,MACf;AAAA,IACF;AAAA,IACA,UAAU,CAAC,SAAS;AAAA,IACpB,sBAAsB;AAAA,EACxB;AAAA,EAEA,MAAM,QAAQ,MAAgB,KAAuC;AACnE,QAAI,CAAC,MAAM,WAAW,OAAO,KAAK,YAAY,UAAU;AACtD,aAAO,EAAE,SAAS,OAAO,MAAM,gDAAkB,OAAO,eAAe;AAAA,IACzE;AAEA,UAAM,YAAY,KAAK,YAClBC,YAAW,KAAK,SAAS,IAAI,KAAK,YAAYF,MAAK,IAAI,KAAK,KAAK,SAAS,IAC3E,IAAI;AACR,UAAM,QAAQ,YAAY,KAAK,OAAO;AAEtC,QAAI;AACF,YAAM,UAAU,MAAMG,MAAK,SAAS;AACpC,UAAI,CAAC,QAAQ,YAAY,GAAG;AAC1B,eAAO,EAAE,SAAS,OAAO,MAAM,6CAAU,SAAS,IAAI,OAAO,gBAAgB;AAAA,MAC/E;AAEA,YAAM,WAAW,MAAM,QAAQ,WAAW,SAAS;AACnD,YAAM,UAAU,SAAS,OAAO,CAAC,MAAM,MAAM,KAAK,CAAC,CAAC;AAEpD,UAAI,QAAQ,WAAW,GAAG;AACxB,eAAO;AAAA,UACL,SAAS;AAAA,UACT,MAAM,mCAAU,KAAK,OAAO;AAAA,UAC5B,SAAS,GAAG,KAAK,OAAO;AAAA,QAC1B;AAAA,MACF;AAEA,YAAM,QAAQ;AACd,YAAM,UAAU,QAAQ,MAAM,GAAG,KAAK;AACtC,YAAM,SAAS,QAAQ,KAAK,IAAI;AAChC,YAAM,SAAS,QAAQ,SAAS,QAAQ;AAAA;AAAA,aAAa,QAAQ,MAAM,qDAAa,KAAK,YAAO;AAE5F,aAAO;AAAA,QACL,SAAS;AAAA,QACT,MAAM,eAAe,SAAS,MAAM;AAAA,QACpC,SAAS,GAAG,KAAK,OAAO,WAAM,QAAQ,MAAM;AAAA,MAC9C;AAAA,IACF,SAAS,KAAc;AACrB,YAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,aAAO;AAAA,QACL,SAAS;AAAA,QACT,MAAM,6CAAU,OAAO;AAAA,QACvB,OAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AACF;;;ACvIA,SAAS,WAAAC,UAAS,YAAAC,WAAU,QAAAC,aAAY;AACxC,SAAS,QAAAC,OAAM,YAAAC,WAAU,cAAAC,mBAAkB;AA4B3C,eAAe,aACb,KACA,SACA,WACA,WAAW,KACQ;AACnB,QAAM,UAAoB,CAAC;AAC3B,MAAI;AAEJ,MAAI;AACF,cAAU,MAAMC,SAAQ,KAAK,EAAE,eAAe,KAAK,CAAC;AAAA,EACtD,QAAQ;AACN,WAAO;AAAA,EACT;AAEA,aAAW,SAAS,SAAS;AAC3B,QAAI,QAAQ,UAAU,SAAU;AAEhC,QAAI,MAAM,YAAY,MAAM,MAAM,SAAS,kBAAkB,MAAM,SAAS,UAAU,MAAM,SAAS,SAAS;AAC5G;AAAA,IACF;AAEA,UAAM,WAAWC,MAAK,KAAK,MAAM,IAAI;AAErC,QAAI,MAAM,YAAY,GAAG;AACvB,cAAQ,KAAK,GAAG,MAAM,aAAa,UAAU,SAAS,WAAW,WAAW,QAAQ,MAAM,CAAC;AAAA,IAC7F,OAAO;AACL,UAAI,aAAa,CAAC,MAAM,KAAK,SAAS,IAAI,SAAS,EAAE,GAAG;AACtD;AAAA,MACF;AACA,cAAQ,KAAK,QAAQ;AAAA,IACvB;AAAA,EACF;AAEA,SAAO;AACT;AAKO,IAAM,WAAgC;AAAA,EAC3C,MAAM;AAAA,EACN;AAAA,EACA,aACE;AAAA,EACF,YAAY;AAAA,IACV,MAAM;AAAA,IACN,YAAY;AAAA,MACV,SAAS;AAAA,QACP,MAAM;AAAA,QACN,aAAa;AAAA,MACf;AAAA,MACA,WAAW;AAAA,QACT,MAAM;AAAA,QACN,aAAa;AAAA,MACf;AAAA,MACA,SAAS;AAAA,QACP,MAAM;AAAA,QACN,aAAa;AAAA,MACf;AAAA,MACA,gBAAgB;AAAA,QACd,MAAM;AAAA,QACN,aAAa;AAAA,MACf;AAAA,MACA,WAAW;AAAA,QACT,MAAM;AAAA,QACN,aAAa;AAAA,MACf;AAAA,IACF;AAAA,IACA,UAAU,CAAC,SAAS;AAAA,IACpB,sBAAsB;AAAA,EACxB;AAAA,EAEA,MAAM,QAAQ,MAAgB,KAAuC;AACnE,QAAI,CAAC,MAAM,WAAW,OAAO,KAAK,YAAY,UAAU;AACtD,aAAO,EAAE,SAAS,OAAO,MAAM,gDAAkB,OAAO,eAAe;AAAA,IACzE;AAEA,UAAM,YAAY,KAAK,YAClBC,YAAW,KAAK,SAAS,IAAI,KAAK,YAAYD,MAAK,IAAI,KAAK,KAAK,SAAS,IAC3E,IAAI;AACR,UAAM,WAAW,KAAK,aAAa;AAEnC,QAAI;AACF,YAAM,QAAQ,KAAK,iBAAiB,MAAM;AAC1C,YAAM,QAAQ,IAAI,OAAO,KAAK,SAAS,KAAK;AAE5C,YAAM,UAAU,MAAME,MAAK,SAAS;AACpC,UAAI,CAAC,QAAQ,YAAY,GAAG;AAC1B,eAAO,EAAE,SAAS,OAAO,MAAM,6CAAU,SAAS,IAAI,OAAO,gBAAgB;AAAA,MAC/E;AAEA,YAAM,QAAQ,MAAM,aAAa,WAAW,WAAW,KAAK,SAAS,QAAQ;AAC7E,YAAM,UAAuB,CAAC;AAE9B,iBAAW,YAAY,OAAO;AAC5B,YAAI;AACF,gBAAM,UAAU,MAAMC,UAAS,UAAU,OAAO;AAChD,gBAAM,QAAQ,QAAQ,MAAM,IAAI;AAChC,gBAAM,UAAUC,UAAS,WAAW,QAAQ;AAE5C,mBAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,gBAAI,MAAM,KAAK,MAAM,CAAC,CAAE,GAAG;AACzB,oBAAM,YAAY;AAClB,sBAAQ,KAAK;AAAA,gBACX,MAAM;AAAA,gBACN,MAAM,IAAI;AAAA,gBACV,SAAS,MAAM,CAAC;AAAA,cAClB,CAAC;AACD,kBAAI,QAAQ,UAAU,IAAK;AAAA,YAC7B;AAAA,UACF;AAEA,cAAI,QAAQ,UAAU,IAAK;AAAA,QAC7B,QAAQ;AACN;AAAA,QACF;AAAA,MACF;AAEA,UAAI,QAAQ,WAAW,GAAG;AACxB,eAAO;AAAA,UACL,SAAS;AAAA,UACT,MAAM,mCAAU,KAAK,OAAO;AAAA,UAC5B,SAAS,cAAO,KAAK,OAAO;AAAA,QAC9B;AAAA,MACF;AAEA,YAAM,SAAS,QACZ,IAAI,CAAC,MAAM,GAAG,EAAE,IAAI,IAAI,EAAE,IAAI,KAAK,EAAE,OAAO,EAAE,EAC9C,KAAK,IAAI;AAEZ,YAAM,UAAU,IAAI,IAAI,QAAQ,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC;AAClD,YAAM,UAAU,cAAO,KAAK,OAAO,YAAO,QAAQ,MAAM,yBAAU,QAAQ,IAAI;AAE9E,aAAO;AAAA,QACL,SAAS;AAAA,QACT,MAAM,eAAe,MAAM;AAAA,QAC3B;AAAA,MACF;AAAA,IACF,SAAS,KAAc;AACrB,YAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,aAAO;AAAA,QACL,SAAS;AAAA,QACT,MAAM,6CAAU,OAAO;AAAA,QACvB,OAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AACF;;;ACjLA,SAAS,WAAAC,UAAS,QAAAC,aAAY;AAC9B,SAAS,QAAAC,OAAM,YAAAC,iBAAgB;AAwBxB,IAAM,SAA4B;AAAA,EACvC,MAAM;AAAA,EACN;AAAA,EACA,aACE;AAAA,EACF,YAAY;AAAA,IACV,MAAM;AAAA,IACN,YAAY;AAAA,MACV,MAAM;AAAA,QACJ,MAAM;AAAA,QACN,aAAa;AAAA,MACf;AAAA,MACA,KAAK;AAAA,QACH,MAAM;AAAA,QACN,aAAa;AAAA,MACf;AAAA,IACF;AAAA,IACA,UAAU,CAAC;AAAA,IACX,sBAAsB;AAAA,EACxB;AAAA,EAEA,MAAM,QAAQ,MAAc,KAAuC;AACjE,UAAM,UAAU,KAAK,OAAO,YAAY,KAAK,MAAM,IAAI,GAAG,IAAI,IAAI;AAClE,UAAM,UAAU,KAAK,OAAO;AAE5B,QAAI;AACF,YAAM,UAAU,MAAMC,SAAQ,SAAS,EAAE,eAAe,KAAK,CAAC;AAC9D,YAAM,QAAkB,CAAC;AAGzB,YAAM,SAAS,CAAC,GAAG,OAAO,EAAE,KAAK,CAAC,GAAG,MAAM;AACzC,YAAI,EAAE,YAAY,MAAM,EAAE,YAAY,GAAG;AACvC,iBAAO,EAAE,YAAY,IAAI,KAAK;AAAA,QAChC;AACA,eAAO,EAAE,KAAK,cAAc,EAAE,IAAI;AAAA,MACpC,CAAC;AAED,iBAAW,SAAS,QAAQ;AAE1B,YAAI,CAAC,WAAW,MAAM,KAAK,WAAW,GAAG,EAAG;AAE5C,cAAM,WAAsB,MAAM,YAAY,IAC1C,QACA,MAAM,eAAe,IACnB,SACA;AAEN,YAAI,UAAU;AACd,YAAI,aAAa,QAAQ;AACvB,cAAI;AACF,kBAAM,WAAW,MAAMC,MAAKC,MAAK,SAAS,MAAM,IAAI,CAAC;AACrD,gBAAI,SAAS,OAAO,MAAM;AACxB,wBAAU,GAAG,SAAS,IAAI;AAAA,YAC5B,WAAW,SAAS,OAAO,OAAO,MAAM;AACtC,wBAAU,IAAI,SAAS,OAAO,MAAM,QAAQ,CAAC,CAAC;AAAA,YAChD,OAAO;AACL,wBAAU,IAAI,SAAS,OAAO,OAAO,MAAM,QAAQ,CAAC,CAAC;AAAA,YACvD;AAAA,UACF,QAAQ;AACN,sBAAU;AAAA,UACZ;AAAA,QACF;AAEA,cAAM,YAAY,aAAa,QAAQ,cAAO,aAAa,SAAS,cAAO;AAC3E,cAAM,KAAK,GAAG,SAAS,IAAI,MAAM,IAAI,GAAG,UAAU,KAAK,OAAO,MAAM,EAAE,EAAE;AAAA,MAC1E;AAEA,UAAI,MAAM,WAAW,GAAG;AACtB,eAAO,EAAE,SAAS,MAAM,MAAM,4BAAQ,SAAS,aAAMC,UAAS,IAAI,KAAK,OAAO,EAAE,QAAQ,OAAO,GAAG,CAAC,qBAAM;AAAA,MAC3G;AAEA,YAAM,UAAUA,UAAS,IAAI,KAAK,OAAO,EAAE,QAAQ,OAAO,GAAG;AAC7D,aAAO;AAAA,QACL,SAAS;AAAA,QACT,MAAM,eAAe,qBAAM,OAAO;AAAA,EAAK,MAAM,KAAK,IAAI,CAAC,EAAE;AAAA,QACzD,SAAS,aAAM,OAAO,SAAI,MAAM,MAAM;AAAA,MACxC;AAAA,IACF,SAAS,KAAc;AACrB,YAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,aAAO;AAAA,QACL,SAAS;AAAA,QACT,MAAM,uCAAS,OAAO;AAAA,QACtB,OAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AACF;;;AC3FO,IAAM,YAAkC;AAAA,EAC7C,MAAM;AAAA,EACN;AAAA,EACA,aACE;AAAA,EACF,YAAY;AAAA,IACV,MAAM;AAAA,IACN,YAAY;AAAA,MACV,KAAK;AAAA,QACH,MAAM;AAAA,QACN,aAAa;AAAA,MACf;AAAA,MACA,QAAQ;AAAA,QACN,MAAM;AAAA,QACN,aAAa;AAAA,MACf;AAAA,MACA,SAAS;AAAA,QACP,MAAM;AAAA,QACN,aAAa;AAAA,QACb,sBAAsB,EAAE,MAAM,SAAS;AAAA,MACzC;AAAA,MACA,MAAM;AAAA,QACJ,MAAM;AAAA,QACN,aAAa;AAAA,MACf;AAAA,MACA,YAAY;AAAA,QACV,MAAM;AAAA,QACN,aAAa;AAAA,MACf;AAAA,IACF;AAAA,IACA,UAAU,CAAC,KAAK;AAAA,IAChB,sBAAsB;AAAA,EACxB;AAAA,EAEA,MAAM,QAAQ,MAAiB,KAAuC;AACpE,QAAI,CAAC,MAAM,OAAO,OAAO,KAAK,QAAQ,UAAU;AAC9C,aAAO,EAAE,SAAS,OAAO,MAAM,4CAAc,OAAO,eAAe;AAAA,IACrE;AAEA,UAAM,UAAU,KAAK,UAAU,OAAO,YAAY;AAClD,UAAM,UAAU;AAChB,UAAM,YAAY,KAAK,cAAc;AAErC,QAAI;AACF,YAAM,eAA4B;AAAA,QAChC;AAAA,QACA,SAAS,KAAK,WAAW,CAAC;AAAA,QAC1B,QAAQ,IAAI,UAAU,YAAY,QAAQ,OAAO;AAAA,MACnD;AAEA,UAAI,KAAK,SAAS,WAAW,UAAU,WAAW,SAAS,WAAW,UAAU;AAC9E,QAAC,aAAa,QAAmC,cAAc,MAAM;AACrE,qBAAa,OAAO,KAAK;AAAA,MAC3B;AAEA,YAAM,WAAW,MAAM,MAAM,KAAK,KAAK,YAAY;AAEnD,YAAM,cAAc,SAAS,QAAQ,IAAI,cAAc,KAAK;AAC5D,YAAM,aAAa,GAAG,SAAS,MAAM,IAAI,SAAS,UAAU;AAE5D,UAAI;AACJ,UAAI;AACF,eAAO,MAAM,SAAS,KAAK;AAAA,MAC7B,QAAQ;AACN,eAAO;AAAA,MACT;AAEA,YAAM,gBAAgB,eAAe,MAAM,SAAS;AAEpD,YAAM,SAAS,iBAAO,UAAU;AAAA,4BAAW,WAAW;AACtD,YAAM,YAAY,KAAK,SAAS,IAAI,YAAY;AAEhD,YAAM,aAAa,KAAK,IAAI,SAAS,KACjC,KAAK,IAAI,MAAM,GAAG,EAAE,IAAI,QACxB,KAAK;AACT,YAAM,UAAU,aAAM,MAAM,IAAI,UAAU,WAAM,SAAS,MAAM;AAE/D,aAAO;AAAA,QACL,SAAS,SAAS;AAAA,QAClB,MAAM,GAAG,MAAM,GAAG,SAAS,GAAG,aAAa;AAAA,QAC3C;AAAA,QACA,OAAO,SAAS,KAAK,SAAY,QAAQ,SAAS,MAAM;AAAA,MAC1D;AAAA,IACF,SAAS,KAAc;AACrB,UAAI,eAAe,gBAAgB,IAAI,SAAS,cAAc;AAC5D,eAAO,EAAE,SAAS,OAAO,MAAM,kCAAS,OAAO,UAAU;AAAA,MAC3D;AACA,UAAI,eAAe,aAAa,IAAI,QAAQ,SAAS,OAAO,GAAG;AAC7D,eAAO,EAAE,SAAS,OAAO,MAAM,iCAAQ,IAAI,OAAO,IAAI,OAAO,gBAAgB;AAAA,MAC/E;AACA,YAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,aAAO;AAAA,QACL,SAAS;AAAA,QACT,MAAM,sCAAa,OAAO;AAAA,QAC1B,OAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AACF;;;ACzGO,IAAM,eAA+B;AAAA,EAC1C,UAAU,YAAY;AAAA,EACtB,UAAU,aAAa;AAAA,EACvB,UAAU,YAAY;AAAA,EACtB,UAAU,aAAa;AAAA,EACvB,UAAU,eAAe;AAAA,EACzB,UAAU,QAAQ;AAAA,EAClB,UAAU,QAAQ;AAAA,EAClB,UAAU,QAAQ;AAAA,EAClB,UAAU,MAAM;AAAA,EAChB,UAAU,SAAS;AACrB;;;AClBO,IAAM,sBAAmC;AAAA,EAC9C,CAAC,GAAG,KAAK,EAAE;AAAA;AAAA,EACX,CAAC,GAAG,KAAK,GAAG;AAAA;AAAA,EACZ,CAAC,KAAK,KAAK,GAAG;AAAA;AAChB;AAGO,IAAM,yBAAsC;AAAA,EACjD,CAAC,KAAK,KAAK,GAAG;AAAA;AAAA,EACd,CAAC,KAAK,KAAK,EAAE;AAAA;AAAA,EACb,CAAC,KAAK,KAAK,EAAE;AAAA;AACf;AAGO,IAAM,2BAAwC;AAAA,EACnD,CAAC,KAAK,KAAK,CAAC;AAAA;AAAA,EACZ,CAAC,KAAK,KAAK,EAAE;AAAA;AAAA,EACb,CAAC,KAAK,IAAI,EAAE;AAAA;AACd;AAKO,IAAM,qBAAqB;AAAA;AAAA,EAEhC,eAAe;AAAA;AAAA,EAEf,cAAc;AAAA;AAAA,EAEd,oBAAoB;AAAA;AAAA,EAEpB,mBAAmB;AAAA;AAAA,EAEnB,iBAAiB;AAAA;AAAA,EAEjB,gBAAgB;AAClB;AAKO,IAAM,aAAa,oBAAI,IAAI,CAAC,KAAK,KAAK,KAAK,UAAK,UAAK,WAAI,CAAC;AAK1D,SAAS,eAAe,GAAW,OAAqC;AAC7E,QAAM,MAAM,MAAM;AAClB,QAAM,UAAU,KAAK,MAAM;AAC3B,QAAM,MAAM,KAAK,IAAI,KAAK,MAAM,OAAO,GAAG,MAAM,CAAC;AACjD,QAAM,OAAO,UAAU;AACvB,QAAM,OAAO,MAAM,GAAG;AACtB,QAAM,WAAW,MAAM,MAAM,CAAC;AAC9B,MAAI,CAAC,QAAQ,CAAC,SAAU,QAAO;AAC/B,QAAM,IAAI,KAAK,MAAM,KAAK,CAAC,KAAK,SAAS,CAAC,IAAI,KAAK,CAAC,KAAK,IAAI;AAC7D,QAAM,IAAI,KAAK,MAAM,KAAK,CAAC,KAAK,SAAS,CAAC,IAAI,KAAK,CAAC,KAAK,IAAI;AAC7D,QAAM,IAAI,KAAK,MAAM,KAAK,CAAC,KAAK,SAAS,CAAC,IAAI,KAAK,CAAC,KAAK,IAAI;AAC7D,SAAO,IAAI,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,GAAG,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,GAAG,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC;AAChH;AAGO,SAAS,kBACd,MACA,OACA,OACU;AACV,QAAM,MAAM,KAAK;AACjB,MAAI,QAAQ,EAAG,QAAO,CAAC;AACvB,SAAO,KAAK,MAAM,EAAE,EAAE,IAAI,CAAC,IAAI,MAAM;AACnC,QAAI,WAAW,IAAI,EAAE,EAAG,QAAO;AAC/B,UAAM,KAAM,KAAK,MAAM,KAAM,SAAS;AACtC,WAAO,eAAe,GAAG,KAAK;AAAA,EAChC,CAAC;AACH;;;A9B0uBkB,SA2KV,UA3KU,OAAAC,MAUN,QAAAC,aAVM;AA9wBlB,IAAM,kBAAkB,oBAAI,IAAyB;AAG9C,SAAS,gBAAgB,MAAc,KAAwB;AACpE,kBAAgB,IAAI,MAAM,GAAG;AAC/B;AAGA,SAAS,wBAAkD;AACzD,SAAO;AACT;AAGA,gBAAgB,SAAS,EAAE,MAAM,4BAAQ,SAAS,OAAO,EAAE,MAAM,OAAO,GAAG,CAAC;AAC5E,gBAAgB,SAAS,EAAE,MAAM,4BAAQ,SAAS,OAAO,EAAE,MAAM,OAAO,GAAG,CAAC;AAC5E,gBAAgB,SAAS;AAAA,EACvB,MAAM;AAAA,EACN,SAAS,MAAM;AACb,UAAM,WAAW,sBAAsB;AACvC,UAAM,QAAQ,CAAC,gCAAO;AACtB,eAAW,CAAC,MAAM,GAAG,KAAK,UAAU;AAClC,YAAM,KAAK,KAAK,KAAK,OAAO,EAAE,CAAC,GAAG,IAAI,IAAI,EAAE;AAAA,IAC9C;AACA,WAAO,EAAE,MAAM,QAAQ,SAAS,MAAM,KAAK,IAAI,EAAE;AAAA,EACnD;AACF,CAAC;AACD,gBAAgB,UAAU,EAAE,MAAM,wCAAU,SAAS,OAAO,EAAE,MAAM,QAAQ,GAAG,CAAC;AAChF,gBAAgB,YAAY,EAAE,MAAM,wCAAU,SAAS,OAAO,EAAE,MAAM,QAAQ,SAAS,kBAAkB,GAAG,CAAC;AAC7G,gBAAgB,UAAU,EAAE,MAAM,4BAAQ,SAAS,OAAO,EAAE,MAAM,QAAQ,SAAS,6EAAsB,GAAG,CAAC;AAC7G,gBAAgB,aAAa,EAAE,MAAM,oDAAY,SAAS,OAAO,EAAE,MAAM,QAAQ,SAAS,wDAAqB,GAAG,CAAC;AACnH,gBAAgB,WAAW,EAAE,MAAM,iDAAmB,SAAS,OAAO,EAAE,MAAM,QAAQ,SAAS,sDAAmB,GAAG,CAAC;AACtH,gBAAgB,SAAS,EAAE,MAAM,4BAAQ,SAAS,OAAO,EAAE,MAAM,YAAY,QAAQ,OAAO,GAAG,CAAC;AAChG,gBAAgB,UAAU,EAAE,MAAM,wCAAU,SAAS,OAAO,EAAE,MAAM,YAAY,QAAQ,QAAQ,GAAG,CAAC;AAGpG,IAAM,yBAAyB;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAGA,IAAM,oBAAoB;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,SAAS,WAAc,KAAsB;AAC3C,SAAO,IAAI,KAAK,MAAM,KAAK,OAAO,IAAI,IAAI,MAAM,CAAC;AACnD;AAsCO,SAAS,YAAY;AAAA,EAC1B;AAAA,EACA,SAAS,CAAC;AAAA,EACV,QAAQ,CAAC;AAAA,EACT;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,aAAa;AAAA,EACb,QAAQ;AAAA,EACR;AAAA,EACA;AACF,GAAqB;AACnB,QAAM,YAAY,OAAO,QAAQ,OAAO,YAAY,WAAW,QAAQ,OAAO,UAAU;AACxF,QAAM,eAAe,KAAK,IAAI,YAAY,GAAG,CAAC;AAE9C,QAAM,CAAC,QAAQ,SAAS,IAAIC,UAAS,CAAC;AACtC,QAAM,CAAC,iBAAiB,kBAAkB,IAAIA,UAA2B,CAAC,CAAC;AAC3E,QAAM,CAAC,WAAW,YAAY,IAAIA,UAAS,CAAC;AAC5C,QAAM,CAAC,OAAO,QAAQ,IAAIA,UAAS,EAAE;AACrC,QAAM,CAAC,SAAS,UAAU,IAAIA,UAAwB,IAAI;AAC1D,QAAM,CAAC,gBAAgB,iBAAiB,IAAIA,UAAS,KAAK;AAC1D,QAAM,CAAC,WAAW,YAAY,IAAIA,UAAwB,IAAI;AAG9D,QAAM,CAAC,aAAa,cAAc,IAAIA,UAAS,KAAK;AACpD,QAAM,CAAC,sBAAsB,uBAAuB,IAAIA,UAAS,EAAE;AACnE,QAAM,CAAC,iBAAiB,kBAAkB,IAAIA,UAAS,MAAM,WAAW,iBAAiB,CAAC;AAC1F,QAAM,CAAC,gBAAgB,iBAAiB,IAAIA,UAAmB,CAAC,CAAC;AACjE,QAAM,mBAAmBC,QAAO,CAAC;AACjC,QAAM,CAAC,yBAAyB,0BAA0B,IAAID,UAAmB,CAAC,CAAC;AACnF,QAAM,oBAAoBC,QAAO,CAAC;AAClC,QAAM,CAAC,gBAAgB,iBAAiB,IAAID,UAAS,EAAE;AACvD,QAAM,CAAC,kBAAkB,mBAAmB,IAAIA,UAA6B,CAAC,CAAC;AAC/E,QAAM,CAAC,cAAc,eAAe,IAAIA,UAAgC,MAAS;AACjF,QAAM,CAAC,gBAAgB,iBAAiB,IAAIA,UAA6B,MAAS;AAClF,QAAM,CAAC,aAAa,cAAc,IAAIA,UAA6B,MAAS;AAC5E,QAAM,CAAC,aAAa,cAAc,IAAIA,UAAkB,KAAgB;AACxE,QAAM,CAAC,gBAAgB,iBAAiB,IAAIA,UAA6B,MAAS;AAClF,QAAM,CAAC,aAAa,cAAc,IAAIA,UAA6B,MAAS;AAG5E,QAAM,CAAC,iBAAiB,kBAAkB,IAAIA,UAAS,IAAI;AAC3D,QAAM,CAAC,gBAAgB,iBAAiB,IAAIA,UAAyB,MAAM;AAC3E,QAAM,CAAC,gBAAgB,iBAAiB,IAAIA,UAAiC,MAAM;AACnF,QAAM,CAAC,YAAY,aAAa,IAAIA,UAAmD,MAAS;AAGhG,QAAM,cAAc,QAAQ,MAAM;AAChC,WAAO,gBAAgB,OAAO,CAAC,KAAK,QAAQ;AAC1C,UAAI,IAAI,iBAAiB,MAAM;AAC7B,eAAO,MAAM,IAAI,gBAAgB;AAAA,MACnC;AACA,aAAO;AAAA,IACT,GAAG,CAAC;AAAA,EACN,GAAG,CAAC,eAAe,CAAC;AAEpB,QAAM,yBAAyB,gBAAgB,SAAS;AAGxD,QAAM,UAAU,MAAM,KAAK,sBAAsB,CAAC,EAC/C,OAAO,CAAC,CAAC,IAAI,MAAM,SAAS,WAAW,SAAS,OAAO,EACvD,IAAI,CAAC,CAAC,MAAM,GAAG,OAAO,EAAE,MAAM,MAAM,IAAI,KAAK,EAAE;AAClD,QAAM,CAAC,aAAa,cAAc,IAAIA,UAAS,CAAC;AAChD,QAAM,CAAC,sBAAsB,uBAAuB,IAAIA,UAAmB,CAAC,CAAC;AAC7E,QAAM,iBAAiBC,QAAO,CAAC;AAG/B,QAAM,CAAC,kBAAkB,mBAAmB,IAAID,UAAS,CAAC;AAE1D,QAAM,CAAC,iBAAiB,kBAAkB,IAAIA,UAAS,CAAC;AAExD,QAAM,CAAC,UAAU,WAAW,IAAIA,UAAS,CAAC;AAG1C,QAAM,CAAC,gBAAgB,iBAAiB,IAAIA,UAAS,KAAK;AAC1D,QAAM,CAAC,kBAAkB,mBAAmB,IAAIA,UAAS,CAAC;AAC1D,QAAM,eAA0B,CAAC,qBAAqB,iBAAiB;AAGvE,QAAM,aAAaC,QAAuB,IAAI;AAC9C,QAAM,WAAWA,QAA+B,IAAI;AAGpD,QAAM,oBAAoBA,QAAO,EAAE;AACnC,QAAM,sBAAsBA,QAA2B,CAAC,CAAC;AACzD,QAAM,kBAAkBA,QAA8B,MAAS;AAC/D,QAAM,oBAAoBA,QAA2B,MAAS;AAC9D,QAAM,iBAAiBA,QAA2B,MAAS;AAC3D,QAAM,kBAAkBA,QAA2B,MAAS;AAC5D,QAAM,iBAAiBA,QAA2B,MAAS;AAG3D,EAAAC,WAAU,MAAM;AACd,wBAAoB,CAAC;AACrB,uBAAmB,CAAC;AAAA,EACtB,GAAG,CAAC,KAAK,CAAC;AAGV,QAAM,oBAAoBC;AAAA,IACxB,CAAC,UAAkB;AACjB,YAAM,QAAQ,MAAM,MAAM,oBAAoB;AAC9C,UAAI,CAAC,MAAO,QAAO,CAAC;AACpB,YAAM,IAAI,MAAM,CAAC,EAAG,YAAY,EAAE,KAAK;AACvC,UAAI,CAAC,GAAG;AACN,YAAI,MAAM,WAAW,GAAG,EAAG,QAAO,OAAO,MAAM,GAAG,CAAC;AACnD,eAAO,CAAC;AAAA,MACV;AACA,YAAM,UAAU,OAAO,OAAO,CAAC,MAAM,EAAE,KAAK,YAAY,EAAE,SAAS,CAAC,CAAC,EAAE,MAAM,GAAG,CAAC;AAEjF,UAAI,QAAQ,KAAK,CAAC,MAAM,EAAE,KAAK,YAAY,MAAM,CAAC,EAAG,QAAO,CAAC;AAC7D,aAAO;AAAA,IACT;AAAA,IACA,CAAC,MAAM;AAAA,EACT;AAGA,QAAM,mBAAmBA;AAAA,IACvB,CAAC,UAAkB;AACjB,YAAM,QAAQ,MAAM,MAAM,mBAAmB;AAC7C,UAAI,CAAC,MAAO,QAAO,CAAC;AACpB,YAAM,IAAI,MAAM,CAAC,EAAG,YAAY,EAAE,KAAK;AAEvC,UAAI,CAAC,GAAG;AACN,YAAI,MAAM,WAAW,GAAG,EAAG,QAAO,MAAM,MAAM,GAAG,CAAC;AAClD,eAAO,CAAC;AAAA,MACV;AACA,YAAM,UAAU,MAAM,OAAO,CAAC,MAAM,EAAE,YAAY,EAAE,SAAS,CAAC,CAAC,EAAE,MAAM,GAAG,CAAC;AAE3E,UAAI,QAAQ,KAAK,CAAC,MAAM,EAAE,YAAY,MAAM,CAAC,EAAG,QAAO,CAAC;AACxD,aAAO;AAAA,IACT;AAAA,IACA,CAAC,KAAK;AAAA,EACR;AAEA,QAAM,EAAE,aAAa,YAAY,IAAI,eAAe,MAAM;AAExD,YAAQ,KAAK,CAAC;AAAA,EAChB,CAAC;AAGD;AAAA,IACEA;AAAA,MACE,CAAC,QAAQ,QAAQ;AAEf,YAAI,gBAAgB;AAClB,cAAI,IAAI,SAAS;AACf,gCAAoB,CAAC,UAAU,OAAO,IAAI,aAAa,UAAU,aAAa,MAAM;AAAA,UACtF,WAAW,IAAI,WAAW;AACxB,gCAAoB,CAAC,UAAU,OAAO,KAAK,aAAa,MAAM;AAAA,UAChE,WAAW,IAAI,QAAQ;AACrB,kBAAM,WAAW,aAAa,gBAAgB;AAC9C,gBAAI,aAAa,aAAa;AAC5B,iCAAmB,CAAC,SAAS;AAAA,gBAC3B,GAAG;AAAA,gBACH,EAAE,MAAM,aAAa,SAAS,kCAAS,iBAAiB,QAAQ,EAAE,WAAW,GAAG;AAAA,cAClF,CAAC;AAAA,YACH,OAAO;AAEL,6BAAe,QAAQ;AACvB,yBAAW,SAAS,MAAM;AAC1B,8BAAgB,QAAQ,EAAE,MAAM,MAAM;AAAA,cAEtC,CAAC;AACD,iCAAmB,CAAC,SAAS;AAAA,gBAC3B,GAAG;AAAA,gBACH,EAAE,MAAM,aAAa,SAAS,wCAAU,iBAAiB,QAAQ,EAAE,WAAW,SAAI,QAAQ,SAAI;AAAA,cAChG,CAAC;AAAA,YACH;AACA,8BAAkB,KAAK;AAAA,UACzB,WAAW,IAAI,QAAQ;AACrB,8BAAkB,KAAK;AAAA,UACzB;AACA;AAAA,QACF;AAIA,cAAM,WAAW,iBAAiB,KAAK;AACvC,cAAM,YAAY,SAAS,WAAW,IAAI,kBAAkB,KAAK,IAAI,CAAC;AAEtE,YAAI,SAAS,SAAS,GAAG;AACvB,cAAI,IAAI,SAAS;AACf,+BAAmB,CAAC,UAAU,OAAO,IAAI,SAAS,UAAU,SAAS,MAAM;AAC3E;AAAA,UACF;AACA,cAAI,IAAI,WAAW;AACjB,+BAAmB,CAAC,UAAU,OAAO,KAAK,SAAS,MAAM;AACzD;AAAA,UACF;AAEA,cAAI,IAAI,KAAK;AACX,kBAAM,WAAW,SAAS,eAAe;AACzC,gBAAI,UAAU;AACZ,oBAAM,QAAQ,MAAM,YAAY,GAAG;AACnC,kBAAI,SAAS,GAAG;AACd,yBAAS,MAAM,MAAM,GAAG,KAAK,IAAI,MAAM,WAAW,GAAG;AACrD,4BAAY,CAAC,MAAM,IAAI,CAAC;AAAA,cAC1B;AAAA,YACF;AACA;AAAA,UACF;AAAA,QACF;AAEA,YAAI,UAAU,SAAS,GAAG;AACxB,cAAI,IAAI,SAAS;AACf,gCAAoB,CAAC,UAAU,OAAO,IAAI,UAAU,UAAU,UAAU,MAAM;AAC9E;AAAA,UACF;AACA,cAAI,IAAI,WAAW;AACjB,gCAAoB,CAAC,UAAU,OAAO,KAAK,UAAU,MAAM;AAC3D;AAAA,UACF;AAEA,cAAI,IAAI,KAAK;AACX,kBAAM,WAAW,UAAU,gBAAgB;AAC3C,gBAAI,UAAU;AAGZ,oBAAM,WAAW,MAAM,YAAY,GAAG;AACtC,kBAAI,YAAY,GAAG;AAEjB,yBAAS,MAAM,MAAM,GAAG,QAAQ,IAAI,MAAM,SAAS,OAAO,GAAG;AAC7D,4BAAY,CAAC,MAAM,IAAI,CAAC;AAAA,cAC1B;AAAA,YACF;AACA;AAAA,UACF;AAAA,QACF;AAGA,YAAI,gBAAgB;AAClB,cAAI,IAAI,WAAW,IAAI,WAAW;AAAA,UAElC;AACA;AAAA,QACF;AAEA,YAAI,IAAI,QAAQ,WAAW,KAAK;AAC9B,cAAI,aAAa;AACf,qBAAS,SAAS,MAAM;AAAA,UAC1B,OAAO;AACL,wBAAY;AAAA,UACd;AACA;AAAA,QACF;AAGA,YAAI,CAAC,SAAS,CAAC,eAAe,QAAQ;AACpC,mBAAS,MAAM;AAAA,QACjB;AAAA,MACF;AAAA,MACA,CAAC,gBAAgB,kBAAkB,cAAc,aAAa,aAAa,aAAa,OAAO,QAAQ,kBAAkB,iBAAiB,mBAAmB,gBAAgB;AAAA,IAC/K;AAAA,EACF;AAGA,EAAAD,WAAU,MAAM;AACd,QAAI,QAAQ,UAAU,EAAG;AACzB,UAAM,QAAQ,YAAY,MAAM;AAC9B,qBAAe,CAAC,UAAU,OAAO,KAAK,QAAQ,MAAM;AAAA,IACtD,GAAG,GAAI;AACP,WAAO,MAAM,cAAc,KAAK;AAAA,EAClC,GAAG,CAAC,QAAQ,MAAM,CAAC;AAGnB,EAAAA,WAAU,MAAM;AACd,UAAM,MAAM,QAAQ,cAAc,QAAQ,MAAM;AAChD,QAAI,CAAC,KAAK;AACR,8BAAwB,CAAC,CAAC;AAC1B;AAAA,IACF;AACA,UAAM,OAAO,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI;AACpC,mBAAe,UAAU;AACzB,4BAAwB,kBAAkB,MAAM,GAAG,sBAAsB,CAAC;AAE1E,UAAM,WAAW,YAAY,MAAM;AACjC,qBAAe,WAAW,eAAe,UAAU,mBAAmB,mBAAmB;AAEzF,8BAAwB,kBAAkB,MAAM,IAAI,eAAe,SAAS,sBAAsB,CAAC;AAAA,IACrG,GAAG,mBAAmB,cAAc;AAEpC,WAAO,MAAM,cAAc,QAAQ;AAAA,EACrC,GAAG,CAAC,aAAa,QAAQ,MAAM,CAAC;AAGhC,EAAAA,WAAU,MAAM;AACd,UAAM,QAAQ,YAAY,MAAM;AAC9B,gBAAU,CAAC,UAAU,OAAO,KAAK,cAAc,MAAM;AAAA,IACvD,GAAG,GAAG;AACN,WAAO,MAAM,cAAc,KAAK;AAAA,EAClC,GAAG,CAAC,CAAC;AAGL,EAAAA,WAAU,MAAM;AACd,QAAI,CAAC,UAAU,CAAC,QAAS;AAEzB,UAAM,WAAW,eAAe;AAAA,MAC9B,MAAM;AAAA,MACN;AAAA,MACA;AAAA,MACA,OAAO;AAAA,IACT,CAAC;AAED,UAAM,UAAU,uBAAuB,IAAI,YAAY;AAEvD,UAAM,eAAe,IAAI,aAAa;AACtC,iBAAa,YAAY,YAAY;AACrC,UAAM,UAAU,IAAI,QAAQ,UAAU,cAAc,SAAS;AAAA,MAC3D,KAAK,QAAQ,IAAI;AAAA,IACnB,CAAC;AACD,eAAW,UAAU;AAErB,WAAO,MAAM;AACX,iBAAW,UAAU;AAAA,IACvB;AAAA,EACF,GAAG,CAAC,QAAQ,SAAS,aAAa,mBAAmB,CAAC;AAGtD,EAAAA,WAAU,MAAM;AACd,QAAI,CAAC,UAAU,CAAC,QAAS;AACzB,QAAI,YAAY;AAChB,sBAAkB,IAAI;AACtB,WAAO,wBAAyB,EAAE,KAAK,CAAC,EAAE,kBAAAE,kBAAiB,MAAM;AAC/D,YAAM,WAAW,IAAIA,kBAAiB;AAAA,QACpC;AAAA,QACA;AAAA,QACA,OAAO;AAAA,MACT,CAAC;AACD,aAAO,SAAS,WAAW;AAAA,IAC7B,CAAC,EAAE,KAAK,CAAC,WAAW;AAClB,UAAI,UAAW;AACf,YAAM,MAAM,OAAO,SAAS,KAAK,CAAC,MAAM,EAAE,aAAa,KAAK;AAC5D,UAAI,KAAK;AACP,mBAAW,IAAI,YAAY;AAAA,MAC7B;AAAA,IACF,CAAC,EAAE,MAAM,MAAM;AAAA,IAEf,CAAC,EAAE,QAAQ,MAAM;AACf,UAAI,CAAC,UAAW,mBAAkB,KAAK;AAAA,IACzC,CAAC;AACD,WAAO,MAAM;AAAE,kBAAY;AAAA,IAAM;AAAA,EACnC,GAAG,CAAC,QAAQ,OAAO,CAAC;AAGpB,EAAAF,WAAU,MAAM;AACd,QAAI,CAAC,oBAAqB;AAC1B,QAAI,YAAY;AAChB,QAAI;AAEJ,UAAM,UAAU,MAAM;AACpB,mBAAa,oBAAoB,cAAc;AAAA,IACjD;AAEA,wBAAoB,KAAK,EAAE,KAAK,MAAM;AACpC,UAAI,UAAW;AACf,cAAQ;AACR,cAAQ,YAAY,SAAS,GAAI;AAAA,IACnC,CAAC,EAAE,MAAM,MAAM;AAAA,IAEf,CAAC;AAED,WAAO,MAAM;AACX,kBAAY;AACZ,UAAI,MAAO,eAAc,KAAK;AAAA,IAChC;AAAA,EACF,GAAG,CAAC,mBAAmB,CAAC;AAGxB,EAAAA,WAAU,MAAM;AACd,QAAI,eAAe,CAAC,iBAAiB;AACnC,wBAAkB,CAAC,CAAC;AACpB;AAAA,IACF;AAEA,qBAAiB,UAAU;AAC3B,sBAAkB,kBAAkB,iBAAiB,GAAG,mBAAmB,CAAC;AAE5E,UAAM,WAAW,YAAY,MAAM;AACjC,uBAAiB,WAAW,iBAAiB,UAAU,mBAAmB,iBAAiB;AAC3F,wBAAkB,kBAAkB,iBAAiB,IAAI,iBAAiB,SAAS,mBAAmB,CAAC;AAAA,IACzG,GAAG,mBAAmB,YAAY;AAElC,WAAO,MAAM,cAAc,QAAQ;AAAA,EACrC,GAAG,CAAC,aAAa,eAAe,CAAC;AAGjC,EAAAA,WAAU,MAAM;AACd,QAAI,CAAC,eAAe,CAAC,sBAAsB;AACzC,iCAA2B,CAAC,CAAC;AAC7B;AAAA,IACF;AAEA,sBAAkB,UAAU;AAC5B,+BAA2B,kBAAkB,sBAAsB,GAAG,wBAAwB,CAAC;AAE/F,UAAM,WAAW,YAAY,MAAM;AACjC,wBAAkB,WAAW,kBAAkB,UAAU,mBAAmB,sBAAsB;AAClG,iCAA2B,kBAAkB,sBAAsB,IAAI,kBAAkB,SAAS,wBAAwB,CAAC;AAAA,IAC7H,GAAG,mBAAmB,iBAAiB;AAEvC,WAAO,MAAM,cAAc,QAAQ;AAAA,EACrC,GAAG,CAAC,aAAa,oBAAoB,CAAC;AAGtC,QAAM,eAAeC,aAAY,OAAO,UAAkB;AACxD,UAAM,UAAU,MAAM,KAAK;AAC3B,QAAI,CAAC,QAAS;AAGd,QAAI,QAAQ,WAAW,GAAG,KAAK,QAAQ,SAAS,GAAG;AACjD,YAAM,WAAW,QAAQ,YAAY;AAGrC,UAAI,aAAa,UAAU;AACzB,cAAM,SAAS,aAAa,QAAQ,WAAW;AAC/C,4BAAoB,UAAU,IAAI,SAAS,CAAC;AAC5C,0BAAkB,IAAI;AACtB,iBAAS,EAAE;AACX;AAAA,MACF;AAGA,UAAI,aAAa,aAAa;AAC5B,2BAAmB,CAAC,SAAS,CAAC,IAAI;AAClC,2BAAmB,CAAC,SAAS;AAAA,UAC3B,GAAG;AAAA,UACH,EAAE,MAAM,QAAQ,SAAS,QAAQ;AAAA,UACjC,EAAE,MAAM,aAAa,SAAS,iCAAQ,kBAAkB,iBAAO,cAAI,GAAG;AAAA,QACxE,CAAC;AACD,iBAAS,EAAE;AACX;AAAA,MACF;AAGA,UAAI,aAAa,WAAW;AAC1B,cAAM,OAAO,mBAAmB,SAAS,QAAQ;AACjD,0BAAkB,IAAI;AACtB,2BAAmB,CAAC,SAAS;AAAA,UAC3B,GAAG;AAAA,UACH,EAAE,MAAM,QAAQ,SAAS,QAAQ;AAAA,UACjC,EAAE,MAAM,aAAa,SAAS,oDAAY,SAAS,SAAS,SAAS,KAAK,GAAG;AAAA,QAC/E,CAAC;AACD,iBAAS,EAAE;AACX;AAAA,MACF;AAKA,UAAI,aAAa,UAAU;AACzB,cAAM,OAAO,eAAe,SAAY,SACpC,eAAe,SAAS,aACxB,eAAe,aAAa,SAC5B;AACJ,sBAAc,IAAI;AAClB,cAAM,QAAQ,SAAS,SAAY,yCAC/B,SAAS,SAAS,6BAClB,SAAS,aAAa,6BACtB;AACJ,2BAAmB,CAAC,SAAS;AAAA,UAC3B,GAAG;AAAA,UACH,EAAE,MAAM,QAAQ,SAAS,QAAQ;AAAA,UACjC,EAAE,MAAM,aAAa,SAAS,gEAAc,KAAK,GAAG;AAAA,QACtD,CAAC;AACD,iBAAS,EAAE;AACX;AAAA,MACF;AAEA,YAAM,MAAM,gBAAgB,IAAI,QAAQ;AACxC,UAAI,KAAK;AACP,cAAM,SAAS,IAAI,QAAQ;AAE3B,gBAAQ,OAAO,MAAM;AAAA,UACnB,KAAK;AACH,oBAAQ,KAAK,CAAC;AACd;AAAA,UACF,KAAK;AAEH,+BAAmB,CAAC,CAAC;AACrB,yBAAa,CAAC,SAAS,OAAO,CAAC;AAC/B,qBAAS,EAAE;AACX,2BAAe,MAAS;AAExB,oBAAQ,OAAO,MAAM,sBAAsB;AAE3C,uBAAW,SAAS,MAAM;AAC1B;AAAA,UACF,KAAK;AACH,qBAAS,EAAE;AACX,gBAAI,OAAO,WAAW,QAAQ;AAC5B,6BAAe;AAAA,YACjB,WAAW,OAAO,WAAW,SAAS;AACpC,8BAAgB;AAAA,YAClB;AACA;AAAA,UACF,KAAK;AACH,+BAAmB,CAAC,SAAS;AAAA,cAC3B,GAAG;AAAA,cACH,EAAE,MAAM,QAAQ,SAAS,QAAQ;AAAA,cACjC,EAAE,MAAM,aAAa,SAAS,OAAO,QAAQ;AAAA,YAC/C,CAAC;AACD,qBAAS,EAAE;AACX;AAAA,QACJ;AAAA,MACF;AACA,yBAAmB,CAAC,SAAS;AAAA,QAC3B,GAAG;AAAA,QACH,EAAE,MAAM,QAAQ,SAAS,QAAQ;AAAA,QACjC,EAAE,MAAM,aAAa,SAAS,iCAAQ,OAAO,8CAAgB;AAAA,MAC/D,CAAC;AACD,eAAS,EAAE;AACX;AAAA,IACF;AAGA,QAAI,CAAC,WAAW,SAAS;AACvB,yBAAmB,CAAC,SAAS;AAAA,QAC3B,GAAG;AAAA,QACH,EAAE,MAAM,QAAQ,SAAS,QAAQ;AAAA,QACjC,EAAE,MAAM,aAAa,SAAS,sHAAsC;AAAA,MACtE,CAAC;AACD,eAAS,EAAE;AACX;AAAA,IACF;AAKA,uBAAmB,CAAC,SAAS;AAAA,MAC3B,GAAG;AAAA,MACH,EAAE,MAAM,QAAQ,SAAS,QAAQ;AAAA,IACnC,CAAC;AAGD,aAAS,EAAE;AAEX,mBAAe,IAAI;AACnB,4BAAwB,WAAW,sBAAsB,CAAC;AAC1D,sBAAkB,EAAE;AACpB,wBAAoB,CAAC,CAAC;AACtB,oBAAgB,MAAS;AACzB,sBAAkB,MAAS;AAC3B,mBAAe,MAAS;AACxB,sBAAkB,MAAS;AAC3B,mBAAe,MAAS;AAExB,sBAAkB,UAAU;AAC5B,wBAAoB,UAAU,CAAC;AAC/B,oBAAgB,UAAU;AAC1B,sBAAkB,UAAU;AAC5B,mBAAe,UAAU;AACzB,oBAAgB,UAAU;AAC1B,mBAAe,UAAU;AAEzB,UAAM,UAAU,WAAW;AAC3B,UAAM,kBAAkB,IAAI,gBAAgB;AAC5C,aAAS,UAAU;AAEnB,QAAI;AACF,uBAAiB,SAAS,QAAS,KAAK,SAAS;AAAA,QAC/C,iBAAiB,mBAAmB;AAAA,QACpC,gBAAgB,kBAAkB,iBAAiB;AAAA,QACnD,gBAAgB,mBAAmB,SAAS,iBAAiB;AAAA,QAC7D;AAAA,MACF,CAAC,GAAG;AAEF,YAAI,gBAAgB,OAAO,QAAS;AAEpC,gBAAQ,MAAM,MAAM;AAAA,UAClB,KAAK;AACH,8BAAkB,CAAC,SAAS;AAC1B,oBAAM,OAAO,OAAO,MAAM;AAC1B,gCAAkB,UAAU;AAC5B,qBAAO;AAAA,YACT,CAAC;AACD;AAAA,UAEF,KAAK;AACH,gCAAoB,CAAC,SAAS;AAC5B,oBAAM,OAAO,CAAC,GAAG,MAAM,GAAG,MAAM,KAAK;AACrC,kCAAoB,UAAU;AAC9B,qBAAO;AAAA,YACT,CAAC;AACD;AAAA,UAEF,KAAK;AAGH,8BAAkB,EAAE;AACpB,8BAAkB,UAAU;AAC5B,gCAAoB,CAAC,CAAC;AACtB,gCAAoB,UAAU,CAAC;AAG/B,kBAAM,IAAI,MAAM;AAChB,kBAAM,OAAO,EAAE,UACX,EAAE,WAAW,UAAK,MAAM,IAAI,KAAK,EAAE,KAAK,MAAM,GAAG,GAAG,CAAC,GAAG,EAAE,KAAK,SAAS,MAAM,QAAQ,EAAE,KACxF,UAAK,MAAM,IAAI,KAAK,EAAE,SAAS,0BAAM;AACzC,+BAAmB,CAAC,SAAS;AAAA,cAC3B,GAAG;AAAA,cACH;AAAA,gBACE,MAAM;AAAA,gBACN,SAAS;AAAA,gBACT,MAAM,EAAE;AAAA,cACV;AAAA,YACF,CAAC;AACD;AAAA,UAEF,KAAK;AACH,4BAAgB,MAAM,KAAK;AAC3B,8BAAkB,MAAM,KAAK;AAC7B,4BAAgB,UAAU,MAAM;AAChC,4BAAgB,UAAU,MAAM;AAEhC;AACE,oBAAM,OAAO,cAAc,MAAM,OAAO,MAAM,KAAgB;AAC9D,6BAAe,KAAK,SAAS;AAC7B,6BAAe,UAAU,KAAK;AAAA,YAChC;AACA;AAAA,UAEF,KAAK;AACH,8BAAkB,MAAM,OAAO;AAC/B,8BAAkB,UAAU,MAAM;AAClC;AAAA,UAEF,KAAK;AACH,2BAAe,MAAM,MAAM,OAAO;AAClC,2BAAe,UAAU,MAAM,MAAM;AACrC;AAAA,QACJ;AAAA,MACF;AAAA,IACF,SAAS,KAAc;AACrB,YAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D,qBAAe,GAAG;AAClB,qBAAe,UAAU;AAAA,IAC3B,UAAE;AACA,qBAAe,KAAK;AACpB,yBAAmB,WAAW,iBAAiB,CAAC;AAChD,eAAS,UAAU;AAGnB,YAAM,aAAa,kBAAkB;AACrC,YAAM,eAAe,oBAAoB,QAAQ,SAAS,IAAI,oBAAoB,UAAU;AAC5F,YAAM,iBAAiB,eAAe;AAEtC,UAAI,cAAc,gBAAgB,gBAAgB;AAChD,cAAM,YAAgC;AAAA,UACpC,SAAS,iBAAiB,wCAAU,cAAc,KAAM,cAAc;AAAA,UACtE,WAAW;AAAA,UACX,OAAO,gBAAgB;AAAA,UACvB,SAAS,kBAAkB;AAAA,UAC3B,MAAM,eAAe;AAAA,UACrB,OAAO,gBAAgB;AAAA,QACzB;AACA,2BAAmB,CAAC,SAAS;AAAA,UAC3B,GAAG;AAAA,UACH;AAAA,YACE,MAAM;AAAA,YACN,SAAS,UAAU;AAAA,YACnB,iBAAiB;AAAA,UACnB;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF,GAAG,CAAC,cAAc,eAAe,gBAAgB,kBAAkB,QAAQ,kBAAkB,mBAAmB,iBAAiB,gBAAgB,gBAAgB,YAAY,WAAW,CAAC;AAGzL,EAAAD,WAAU,MAAM;AACd,QAAI,CAAC,eAAe,qBAAqB;AACvC,mBAAa,oBAAoB,cAAc;AAAA,IACjD;AAAA,EACF,GAAG,CAAC,aAAa,mBAAmB,CAAC;AAIrC,SACE,gBAAAH,MAACM,MAAA,EAAI,eAAc,UAAS,aAAa,GAAG,cAAc,GAEvD;AAAA,KAAC,0BACA,gBAAAN,MAACM,MAAA,EAAI,eAAc,OAAM,cAAc,GAErC;AAAA,sBAAAP,KAACO,MAAA,EAAI,eAAc,UAAS,aAAa,GACtC,qBAAW,IAAI,CAAC,MAAM,MAAM;AAC3B,cAAM,cAAc,IAAI,UAAU,cAAc;AAChD,eACE,gBAAAP,KAACO,MAAA,EACC,0BAAAP,KAACQ,QAAA,EAAK,MAAI,MAAC,OAAO,cAAc,UAAU,GACvC,gBACH,KAHQ,CAIV;AAAA,MAEJ,CAAC,GACH;AAAA,MAGA,gBAAAP,MAACM,MAAA,EAAI,eAAc,UAAS,gBAAe,UACzC;AAAA,wBAAAN,MAACO,QAAA,EAAK,OAAM,WAAW;AAAA;AAAA,UAAO;AAAA,UAAK;AAAA,UAAW;AAAA,WAAQ;AAAA,QACtD,gBAAAP,MAACO,QAAA,EAAK,OAAM,WAAW;AAAA;AAAA,UAAO;AAAA,UAAK;AAAA,UAAU;AAAA,WAAI;AAAA,QACjD,gBAAAP,MAACO,QAAA,EAAK,OAAM,WAAW;AAAA;AAAA,UAAY,iBAAiB,WAAW,GAAG,eAAe;AAAA,WAAY;AAAA,QAC5F,mBACC,gBAAAP,MAACO,QAAA,EAAK,OAAM,WAAW;AAAA;AAAA,UAAc,mBAAmB,QAAQ,QAAQ;AAAA,WAAO;AAAA,QAEhF,mBAAmB,iBAClB,gBAAAR,KAACQ,QAAA,EAAK,OAAM,WAAW,8BAAY;AAAA,QAEpC,eAAe,UACd,gBAAAP,MAACO,QAAA,EAAK,OAAM,WAAW;AAAA;AAAA,UACrB,eAAe,SAAS,6BACtB,eAAe,aAAa,6BAC5B;AAAA,WACH;AAAA,QAGF,QAAQ,SAAS,MAAM,MAAM;AAC5B,gBAAM,MAAM,QAAQ,cAAc,QAAQ,MAAM;AAChD,cAAI,CAAC,IAAK,QAAO;AACjB,gBAAM,OAAO,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI;AACpC,iBACE,gBAAAP,MAACO,QAAA,EACC;AAAA,4BAAAR,KAACQ,QAAA,EAAK,OAAM,WAAW,0BAAQ;AAAA,YAC9B,qBAAqB,SAAS,IAC3B,KAAK,MAAM,EAAE,EAAE,IAAI,CAAC,IAAI,MACtB,gBAAAR,KAACQ,QAAA,EAAa,OAAO,qBAAqB,CAAC,KAAK,QAAY,gBAAjD,CAAoD,CAChE,IACD,gBAAAR,KAACQ,QAAA,EAAK,OAAM,WAAW,gBAAK;AAAA,aAClC;AAAA,QAEJ,GAAG;AAAA,QACF,UAAU,gBAAAR,KAACQ,QAAA,EAAK,OAAM,WAAW,8BAAc,IAAU;AAAA,SAC5D;AAAA,MAGA,gBAAAP,MAACM,MAAA,EAAI,UAAU,GAAG,eAAc,UAAS,gBAAe,UAAS,YAAW,YACzE;AAAA,0BAAkB,YAAY,OAC7B,gBAAAP,KAACQ,QAAA,EAAK,OAAM,UAAU,kDAAc,IAClC,YAAY,OACd,gBAAAP,MAACM,MAAA,EAAI,eAAc,OACjB;AAAA,0BAAAP,KAACQ,QAAA,EAAK,OAAM,UAAU,wBAAM;AAAA,UAC5B,gBAAAP,MAACO,QAAA,EAAK,OAAM,UAAU;AAAA;AAAA,YAAQ,QAAQ,QAAQ,CAAC;AAAA,aAAE;AAAA,WACnD,IACE;AAAA,QACH,cAAc,OACb,gBAAAP,MAACM,MAAA,EAAI,eAAc,OACjB;AAAA,0BAAAP,KAACQ,QAAA,EAAK,OAAM,QAAQ,wBAAM;AAAA,UAC1B,gBAAAP,MAACO,QAAA,EAAK,OAAM,QAAQ;AAAA;AAAA,YAAQ,UAAU,QAAQ,CAAC;AAAA,aAAE;AAAA,WACnD,IACE;AAAA,SACN;AAAA,OACF;AAAA,IAIF,gBAAAP,MAACM,MAAA,EAAI,eAAc,UAAS,WAAW,GACrC;AAAA,sBAAAP,KAAC,UAAuB,OAAO,iBAC5B,WAAC,KAAK,MAAM;AACX,YAAI,IAAI,SAAS,QAAQ;AACvB,iBACE,gBAAAC,MAACM,MAAA,EAAY,WAAW,GACtB;AAAA,4BAAAP,KAACO,MAAA,EAAI,OAAO,GAAG,YAAY,GACzB,0BAAAP,KAACQ,QAAA,EAAK,MAAI,MAAC,OAAM,WAAW,uBAAK,GACnC;AAAA,YACA,gBAAAR,KAACO,MAAA,EAAI,UAAU,GACb,0BAAAP,KAACQ,QAAA,EAAK,MAAK,QAAQ,cAAI,SAAQ,GACjC;AAAA,eANQ,CAOV;AAAA,QAEJ;AAGA,YAAI,IAAI,SAAS,QAAQ;AACvB,iBACE,gBAAAP,MAACM,MAAA,EAAY,WAAW,GAAG,eAAc,UACvC;AAAA,4BAAAN,MAACM,MAAA,EAAI,eAAc,OACjB;AAAA,8BAAAP,KAACO,MAAA,EAAI,OAAO,GAAG,YAAY,GACzB,0BAAAP,KAACQ,QAAA,EAAK,MAAI,MAAC,OAAM,WAAW,uBAAK,GACnC;AAAA,cACA,gBAAAR,KAACO,MAAA,EAAI,UAAU,GACb,0BAAAP,KAACQ,QAAA,EAAK,MAAK,QAAQ,cAAI,SAAQ,GACjC;AAAA,eACF;AAAA,YACC,IAAI,QAAQ,gBAAAR,KAAC,eAAY,MAAM,IAAI,MAAM;AAAA,eATlC,CAUV;AAAA,QAEJ;AAGA,cAAM,SAAS,IAAI;AACnB,eACE,gBAAAA;AAAA,UAAC;AAAA;AAAA,YAEC,SAAS,IAAI;AAAA,YACb,WAAW,QAAQ;AAAA,YACnB,aAAa;AAAA,YACb,OAAO,QAAQ;AAAA,YACf,SAAS,QAAQ;AAAA,YACjB,MAAM,QAAQ;AAAA,YACd,OAAO,QAAQ;AAAA;AAAA,UAPV;AAAA,QAQP;AAAA,MAEJ,KA9CW,SA+Cb;AAAA,MAGC,eACC,gBAAAA;AAAA,QAAC;AAAA;AAAA,UACC,SAAS;AAAA,UACT,WAAW,iBAAiB,SAAS,IAAI,mBAAmB;AAAA,UAC5D,aAAa;AAAA;AAAA,MACf;AAAA,MAID,eAAe,CAAC,kBAAkB,iBAAiB,WAAW,KAC7D,gBAAAA,KAACO,MAAA,EAAI,WAAW,GAAG,YAAY,GAC7B,0BAAAP,KAAC,WAAQ,MAAK,QAAO,OAAM,yBAAS,GACtC;AAAA,MAID,CAAC,eAAe,eACf,gBAAAA,KAACO,MAAA,EAAI,WAAW,GAAG,YAAY,GAC7B,0BAAAN,MAACO,QAAA,EAAK,OAAM,OAAM;AAAA;AAAA,QAAG;AAAA,SAAY,GACnC;AAAA,OAEJ;AAAA,IAGC,iBACC,gBAAAP,MAACM,MAAA,EAAI,WAAW,GAAG,eAAc,UAC/B;AAAA,sBAAAP,KAACQ,QAAA,EAAK,OAAM,WAAU,UAAQ,MAAE,mBAAI,OAAO,YAAY,GAAE;AAAA,MACzD,gBAAAP,MAACM,MAAA,EAAI,eAAc,UAAS,WAAW,GACrC;AAAA,wBAAAP,KAACQ,QAAA,EAAK,MAAI,MAAC,OAAM,WAAU,4CAAK;AAAA,QAC/B,aAAa,IAAI,CAAC,IAAI,MAAM;AAC3B,gBAAM,OAAO,iBAAiB,EAAE;AAChC,gBAAM,YAAY,OAAO;AACzB,gBAAM,aAAa,MAAM;AACzB,gBAAM,SAAS,aAAa,QAAQ;AACpC,gBAAM,SAAS,YAAY,oBAAU;AACrC,iBACE,gBAAAP,MAACM,MAAA,EACC;AAAA,4BAAAN;AAAA,cAACO;AAAA,cAAA;AAAA,gBACC,OAAO,aAAa,YAAY;AAAA,gBAChC,MAAM;AAAA,gBAEL;AAAA;AAAA,kBAAQ,KAAK;AAAA,kBAAa;AAAA;AAAA;AAAA,YAC7B;AAAA,YACC,cAAc,gBAAAP,MAACO,QAAA,EAAK,OAAM,WAAU;AAAA;AAAA,cAAI;AAAA,eAAG;AAAA,eAPpC,EAQV;AAAA,QAEJ,CAAC;AAAA,QACD,gBAAAR,KAACO,MAAA,EAAI,WAAW,GACd,0BAAAP,KAACQ,QAAA,EAAK,OAAM,WAAU,UAAQ,MAAC,qFAAyB,GAC1D;AAAA,SACF;AAAA,MACA,gBAAAR,KAACQ,QAAA,EAAK,OAAM,WAAU,UAAQ,MAAE,mBAAI,OAAO,YAAY,GAAE;AAAA,OAC3D,IAEA,gBAAAP,MAAA,YACE;AAAA,sBAAAD,KAACO,MAAA,EAAI,WAAW,GACb,qCAA2B,YAAY,QAAQ,cAAc,KAAK,eACjE,gBAAAN,MAACO,QAAA,EAAK,OAAM,WAAU,UAAQ,MAC3B;AAAA,iBAAI,OAAO,KAAK,IAAI,eAAe,IAAI,EAAE,CAAC;AAAA,QAC1C,YAAY,QACX,gBAAAP,MAACO,QAAA,EAAK,OAAM,UAAU;AAAA;AAAA,UAAY,QAAQ,QAAQ,CAAC;AAAA,WAAE;AAAA,QAEtD,cACC,gBAAAP,MAACO,QAAA,EAAK,OAAM,QAAQ;AAAA;AAAA,UAAa,cAAc,IAAI,YAAY,QAAQ,CAAC,IAAI,MAAM;AAAA,UAAG,gBAAAR,KAACS,aAAA,EAAW,MAAK,QAAO;AAAA,WAAE,IAC7G,cAAc,IAChB,gBAAAR,MAACO,QAAA,EAAK,OAAM,QAAQ;AAAA;AAAA,UAAa,YAAY,QAAQ,CAAC;AAAA,WAAE,IACtD;AAAA,SACN,IAEA,gBAAAR,KAACQ,QAAA,EAAK,OAAM,WAAU,UAAQ,MAAE,mBAAI,OAAO,YAAY,GAAE,GAE7D;AAAA,MACA,gBAAAP,MAACM,MAAA,EACC;AAAA,wBAAAP,KAACO,MAAA,EAAI,OAAO,GAAG,YAAY,GACzB,0BAAAP,KAACQ,QAAA,EAAK,MAAI,MAAC,OAAM,WAAW,oBAAI,GAClC;AAAA,QACA,gBAAAR,KAACO,MAAA,EAAI,UAAU,GACZ,WAAC,SAAS,CAAC,eAAe,mBAAmB,eAAe,SAAS,IACpE,gBAAAP,KAACQ,QAAA,EACE,0BAAgB,MAAM,EAAE,EAAE,IAAI,CAAC,IAAI,MAClC,gBAAAR,KAACQ,QAAA,EAAa,OAAO,eAAe,CAAC,KAAK,QAAY,gBAA3C,CAA8C,CAC1D,GACH,IACE,CAAC,SAAS,eAAe,wBAAwB,wBAAwB,SAAS,IACpF,gBAAAR,KAACQ,QAAA,EACE,+BAAqB,MAAM,EAAE,EAAE,IAAI,CAAC,IAAI,MACvC,gBAAAR,KAACQ,QAAA,EAAa,OAAO,wBAAwB,CAAC,KAAK,QAAY,gBAApD,CAAuD,CACnE,GACH,IAEA,gBAAAR;AAAA,UAAC;AAAA;AAAA,YAEC,OAAO;AAAA,YACP,UAAU;AAAA,YACV,UAAU;AAAA,YACV,aAAY;AAAA;AAAA,UAJP;AAAA,QAKP,GAEJ;AAAA,SACF;AAAA,MACA,gBAAAA,KAACO,MAAA,EACC,0BAAAP,KAACQ,QAAA,EAAK,OAAM,WAAU,UAAQ,MAAE,mBAAI,OAAO,YAAY,GAAE,GAC3D;AAAA,MAGA,gBAAAR,KAAC,iBAAc,QAAgB,OAAc,eAAe,kBAAkB;AAAA,MAE9E,gBAAAA,KAAC,gBAAa,OAAc,OAAc,eAAe,iBAAiB;AAAA,OAC5E;AAAA,IAID,eAAe,CAAC,eACf,gBAAAA,KAACO,MAAA,EAAI,WAAW,GACd,0BAAAP,KAACQ,QAAA,EAAK,OAAM,WAAU,MAAI,MACvB,2EACH,GACF;AAAA,IAID,eACC,gBAAAR,KAACO,MAAA,EAAI,WAAW,GACd,0BAAAP,KAACQ,QAAA,EAAK,OAAM,UAAS,UAAQ,MAC1B,oFACH,GACF;AAAA,KAEJ;AAEJ;;;A+BpjCA,SAAS,OAAAE,MAAK,QAAAC,QAAM,YAAAC,iBAAgB;AACpC,SAAS,YAAAC,WAAU,eAAAC,oBAAmB;AA8C9B,gBAAAC,OASI,QAAAC,aATJ;AAnCD,SAAS,WAAW,EAAE,OAAO,UAAU,QAAQ,aAAa,GAAoB;AACrF,QAAM,CAAC,eAAe,gBAAgB,IAAIC,UAAS,CAAC;AAEpD,QAAM,aAAa,gBAAgB,WAAW,MAAM,QAAQ,KAAK,CAAC;AAClE,QAAM,EAAE,aAAa,YAAY,IAAI,eAAe,UAAU;AAE9D,EAAAC;AAAA,IACEC;AAAA,MACE,CAAC,OAAO,QAAQ;AAEd,YAAI,UAAU,OAAO,IAAI,MAAM;AAC7B,sBAAY;AACZ;AAAA,QACF;AAEA,YAAI,MAAM,WAAW,EAAG;AACxB,YAAI,IAAI,WAAW,UAAU,KAAK;AAChC,2BAAiB,CAAC,SAAU,OAAO,IAAI,OAAO,IAAI,MAAM,SAAS,CAAE;AAAA,QACrE,WAAW,IAAI,aAAa,UAAU,KAAK;AACzC,2BAAiB,CAAC,SAAU,OAAO,MAAM,SAAS,IAAI,OAAO,IAAI,CAAE;AAAA,QACrE,WAAW,IAAI,QAAQ;AACrB,gBAAM,OAAO,MAAM,aAAa;AAChC,cAAI,KAAM,UAAS,IAAI;AAAA,QACzB,WAAW,IAAI,UAAU,UAAU,KAAK;AACtC,cAAI,aAAc,cAAa;AAAA,cAC1B,UAAS;AAAA,QAChB;AAAA,MACF;AAAA,MACA,CAAC,OAAO,eAAe,UAAU,QAAQ,cAAc,WAAW;AAAA,IACpE;AAAA,EACF;AAEA,SACE,gBAAAH,MAACI,MAAA,EAAI,eAAc,UACjB;AAAA,oBAAAL,MAACK,MAAA,EAAI,cAAc,GACjB,0BAAAL,MAACM,QAAA,EAAK,MAAI,MAAC,OAAM,WAAU,gDAE3B,GACF;AAAA,IAEA,gBAAAN,MAACK,MAAA,EAAI,eAAc,UAChB,gBAAM,IAAI,CAAC,MAAM,UAAU;AAC1B,YAAM,aAAa,UAAU;AAC7B,aACE,gBAAAJ,MAACI,MAAA,EAAkB,eAAc,OAC/B;AAAA,wBAAAL,MAACK,MAAA,EAAI,OAAO,GAAG,YAAY,GACxB,uBACC,gBAAAL,MAACM,QAAA,EAAK,MAAI,MAAC,OAAM,WACd,qBACH,IAEA,gBAAAN,MAACM,QAAA,EAAM,gBAAK,GAEhB;AAAA,QACA,gBAAAN,MAACK,MAAA,EAAI,OAAO,IAAI,YAAY,GAC1B,0BAAAL,MAACM,QAAA,EAAK,MAAI,MAAC,OAAO,aAAa,YAAY,WACxC,eAAK,MACR,GACF;AAAA,QACA,gBAAAN,MAACK,MAAA,EACC,0BAAAL,MAACM,QAAA,EAAK,OAAM,WAAW,eAAK,aAAY,GAC1C;AAAA,WAjBQ,KAAK,EAkBf;AAAA,IAEJ,CAAC,GACH;AAAA,IAEA,gBAAAN,MAACK,MAAA,EAAI,WAAW,GACd,0BAAAL,MAACM,QAAA,EAAK,UAAQ,MACX,8EACH,GACF;AAAA,IAGC,eACC,gBAAAN,MAACK,MAAA,EAAI,WAAW,GACd,0BAAAL,MAACM,QAAA,EAAK,OAAM,WAAU,MAAI,MACvB,2EACH,GACF;AAAA,KAEJ;AAEJ;;;ACpFA,IAAM,WAAW,oBAAI,IAAkB;AAEhC,SAAS,aAAa,MAAkB;AAC7C,WAAS,IAAI,KAAK,IAAI,IAAI;AAC5B;AAEO,SAAS,QAAQ,IAA8B;AACpD,SAAO,SAAS,IAAI,EAAE;AACxB;AAEO,SAAS,YAAoB;AAClC,SAAO,MAAM,KAAK,SAAS,OAAO,CAAC;AACrC;;;ACvBA,SAAS,OAAAC,OAAK,QAAAC,QAAM,YAAAC,WAAU,UAAAC,eAAc;AAC5C,SAAS,YAAAC,WAAU,aAAAC,YAAW,UAAAC,SAAQ,eAAAC,oBAAmB;AAiR/C,SACgB,OAAAC,OADhB,QAAAC,cAAA;AA/QV,IAAM,aAAa;AACnB,IAAM,cAAc;AACpB,IAAM,eAAe;AACrB,IAAM,eAAe,CAAC,KAAK,KAAK,IAAI,EAAE;AAyBtC,IAAM,SAAqB;AAAA,EACzB,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,MAAM,uBAAgB,SAAS,MAAM,KAAK;AAAA,EACrE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,MAAM,yBAAgB,SAAS,MAAM,KAAK;AAAA,EACrE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,MAAM,yBAAgB,SAAS,MAAM,KAAK;AAAA,EACrE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,MAAM,sBAAiB,SAAS,CAAC,GAAG,OAAO,IAAI,KAAK,MAAM,EAAE;AAAA,EACvF,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,MAAM,sBAAiB,SAAS,CAAC,GAAG,GAAG,GAAG,OAAO,KAAK,KAAK,IAAI,KAAK,EAAE;AAAA,EACjG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,MAAM,4BAAgB,SAAS,CAAC,GAAG,MAAM,IAAI,MAAM,KAAM,KAAK,KAAK,KAAK,EAAG;AAAA,EACtG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,MAAM,4BAAgB,SAAS,CAAC,GAAG,GAAG,IAAI,OAAO,MAAM,KAAK,MAAM,KAAK,KAAK,MAAM,KAAK,MAAM,KAAK,EAAE;AAAA,EAC/H,EAAE,MAAM,GAAG,MAAM,IAAI,IAAI,GAAG,MAAM,0BAAgB,SAAS,MAAM,KAAK;AAAA,EACtE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,MAAM,yBAAgB,SAAS,MAAM,KAAK;AAAA,EACrE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,MAAM,yBAAgB,SAAS,MAAM,KAAK;AACvE;AAEA,SAAS,SAAS,OAAyB;AACzC,SAAO,QAAQ,QAAQ,KAAK,OAAO,MAAM;AAC3C;AAEA,SAAS,aAAa,OAAwB;AAC5C,QAAM,MAAM,SAAS,KAAK;AAC1B,QAAM,SAAkB,CAAC;AACzB,QAAM,MAAM;AACZ,QAAM,SAAS,IAAI,OAAO,IAAI,MAAM,IAAI,OAAO,KAAK;AACpD,QAAM,SAAS,KAAK,OAAO,aAAa,UAAU,CAAC;AACnD,QAAM,OAAO,IAAI,KAAK;AAEtB,WAAS,MAAM,GAAG,MAAM,IAAI,MAAM,OAAO;AACvC,aAAS,MAAM,GAAG,MAAM,IAAI,MAAM,OAAO;AACvC,UAAI,IAAI,QAAQ,KAAK,KAAK,IAAI,MAAM,IAAI,IAAI,GAAG;AAC7C,eAAO,KAAK;AAAA,UACV,GAAG,SAAS,MAAM;AAAA,UAClB,GAAG,IAAI,MAAM;AAAA,UACb,GAAG,IAAI;AAAA,UACP,OAAO;AAAA,QACT,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAiBA,SAAS,mBAAmB,OAA0B;AACpD,QAAM,MAAM,SAAS,KAAK;AAC1B,QAAM,SAAS,IAAI,OAAO,IAAI,MAAM,IAAI,OAAO,KAAK;AACpD,QAAM,SAAS,KAAK,OAAO,aAAa,UAAU,CAAC;AACnD,SAAO;AAAA,IACL;AAAA,IACA,QAAQ,aAAa,KAAK;AAAA,IAC1B,SAAS,KAAK,MAAM,aAAa,CAAC,IAAI,KAAK,MAAM,eAAe,CAAC;AAAA,IACjE,MAAM,EAAE,GAAG,aAAa,GAAG,GAAG,cAAc,EAAE;AAAA,IAC9C,SAAS,EAAE,GAAG,GAAG,GAAG,GAAG;AAAA,IACvB,OAAO;AAAA,IACP,OAAO;AAAA,IACP,UAAU;AAAA,IACV,KAAK;AAAA,IACL,QAAQ;AAAA,EACV;AACF;AAIA,SAAS,OAAO,OAAwB;AACtC,MAAI,MAAM,UAAU,MAAM,YAAY,MAAM,IAAK;AAEjD,QAAM,KAAK,KAAK,MAAM,QAAQ;AAC9B,QAAM,KAAK,KAAK,MAAM,QAAQ;AAE9B,MAAI,MAAM,KAAK,KAAK,GAAG;AAAE,UAAM,KAAK,IAAI;AAAG,UAAM,QAAQ,IAAI;AAAA,EAAG;AAChE,MAAI,MAAM,KAAK,KAAK,aAAa,GAAG;AAAE,UAAM,KAAK,IAAI,aAAa;AAAG,UAAM,QAAQ,IAAI;AAAA,EAAI;AAC3F,MAAI,MAAM,KAAK,KAAK,GAAG;AAAE,UAAM,KAAK,IAAI;AAAG,UAAM,QAAQ,IAAI;AAAA,EAAG;AAGhE,MACE,MAAM,KAAK,MAAM,cAAc,KAC/B,MAAM,KAAK,KAAK,MAAM,WACtB,MAAM,KAAK,KAAK,MAAM,UAAU,cAChC;AACA,UAAM,QAAQ,IAAI;AAClB,UAAM,UAAU,MAAM,KAAK,IAAI,MAAM,WAAW;AAChD,UAAM,QAAQ,IAAI,SAAS,MAAM,KAAK;AAAA,EACxC;AAGA,MAAI,MAAM,KAAK,IAAI,aAAa;AAC9B,UAAM;AACN,QAAI,MAAM,SAAS,GAAG;AACpB,YAAM,WAAW;AAAA,IACnB,OAAO;AACL,YAAM,OAAO,EAAE,GAAG,aAAa,GAAG,GAAG,cAAc,EAAE;AACrD,YAAM,UAAU,EAAE,GAAG,GAAG,GAAG,GAAG;AAC9B,YAAM,UAAU,KAAK,MAAM,aAAa,CAAC,IAAI,KAAK,MAAM,eAAe,CAAC;AAAA,IAC1E;AAAA,EACF;AAGA,QAAM,WAAW,MAAM,OAAO,KAAK,CAAC,MAAM;AACxC,QAAI,CAAC,EAAE,MAAO,QAAO;AACrB,WAAO,MAAM,KAAK,KAAK,EAAE,KAAK,MAAM,KAAK,IAAI,EAAE,IAAI,EAAE,KAAK,MAAM,KAAK,KAAK,EAAE,KAAK,MAAM,KAAK,IAAI,EAAE,IAAI;AAAA,EACxG,CAAC;AAED,MAAI,UAAU;AACZ,aAAS,QAAQ;AACjB,UAAM,SAAS;AACf,UAAM,QAAQ,IAAI,CAAC,MAAM,QAAQ;AAAA,EACnC;AAEA,MAAI,MAAM,OAAO,MAAM,CAAC,MAAM,CAAC,EAAE,KAAK,EAAG,OAAM,MAAM;AACvD;AAIA,SAAS,WAAW,OAA0B;AAC5C,QAAM,QAAkB,CAAC;AAEzB,WAAS,gBAAgB,GAAW,GAA+B;AACjE,UAAM,MAAM,KAAK,OAAO,IAAI,KAAK,CAAC;AAClC,QAAI,OAAO,GAAG;AACZ,YAAM,IAAI,MAAM,OAAO,KAAK,CAAC,OAAO,GAAG,MAAM,KAAK,GAAG,SAAS,KAAK,GAAG,KAAK,IAAI,GAAG,IAAI,GAAG,CAAC;AAC1F,UAAI,EAAG,QAAO,MAAM,aAAa;AAAA,IACnC;AACA,WAAO;AAAA,EACT;AAEA,WAAS,IAAI,GAAG,IAAI,aAAa,KAAK;AACpC,QAAI,OAAO;AACX,aAAS,IAAI,GAAG,IAAI,YAAY,KAAK;AACnC,YAAM,SAAS,MAAM,KAAK,MAAM,KAAK,MAAM,KAAK,MAAM;AACtD,YAAM,WAAW,MAAM,cAAc,KAAK,KAAK,MAAM,WAAW,IAAI,MAAM,UAAU;AACpF,YAAM,WAAW,gBAAgB,GAAG,CAAC;AAErC,UAAI,QAAQ;AACV,gBAAQ;AAAA,MACV,WAAW,UAAU;AACnB,gBAAQ;AAAA,MACV,WAAW,aAAa,QAAW;AACjC,gBAAQ,aAAa,aAAa,QAAQ,CAAW;AAAA,MACvD,OAAO;AACL,gBAAQ;AAAA,MACV;AAAA,IACF;AACA,UAAM,KAAK,IAAI;AAAA,EACjB;AAEA,SAAO,MAAM,IAAI,CAAC,MAAM,SAAI,CAAC,QAAG,EAAE,KAAK,IAAI;AAC7C;AAQA,SAAS,iBAAiB,EAAE,QAAQ,QAAQ,GAA0B;AACpE,QAAM,CAAC,cAAc,eAAe,IAAIL,UAAS,CAAC;AAClD,QAAM,CAAC,gBAAgB,iBAAiB,IAAIA,UAAS,IAAI;AACzD,QAAM,WAAWE,QAAkB,mBAAmB,YAAY,CAAC;AACnE,QAAM,CAACI,OAAM,OAAO,IAAIN,UAAS,CAAC;AAClC,QAAM,YAAYE,QAAO,OAAO;AAChC,YAAU,UAAU;AAEpB,EAAAD,WAAU,MAAM;AACd,QAAI,eAAgB;AACpB,UAAM,WAAW,YAAY,MAAM;AACjC,aAAO,SAAS,OAAO;AACvB,cAAQ,CAAC,MAAM,IAAI,CAAC;AAAA,IACtB,GAAG,EAAE;AACL,WAAO,MAAM,cAAc,QAAQ;AAAA,EACrC,GAAG,CAAC,cAAc,CAAC;AAGnB,QAAM,UAAUE,aAAY,CAAC,UAAmB;AAC9C,UAAM,KAAK,SAAS,SAAS,QAAQ;AACrC,aAAS,UAAU,mBAAmB,EAAE;AACxC,oBAAgB,EAAE;AAClB,sBAAkB,KAAK;AACvB,YAAQ,CAAC;AAAA,EACX,GAAG,CAAC,CAAC;AAGL,QAAM,mBAAmBA,aAAY,MAAM;AACzC,sBAAkB,IAAI;AAAA,EACxB,GAAG,CAAC,CAAC;AAEL,EAAAL;AAAA,IACEK,aAAY,CAAC,OAAO,QAAQ;AAC1B,YAAMI,KAAI,SAAS;AAGnB,UAAI,gBAAgB;AAClB,YAAI,SAAS,OAAO,SAAS,KAAK;AAChC,kBAAQ,OAAO,KAAK,CAAC;AAAA,QACvB,WAAW,UAAU,KAAK;AACxB,kBAAQ,EAAE;AAAA,QACZ,WAAW,IAAI,UAAU,UAAU,KAAK;AACtC,oBAAU,QAAQ;AAAA,QACpB;AACA;AAAA,MACF;AAEA,UAAI,IAAI,WAAW;AACjB,QAAAA,GAAE,UAAU,KAAK,IAAI,GAAGA,GAAE,UAAU,CAAC;AACrC,gBAAQ,CAAC,MAAM,IAAI,CAAC;AAAA,MACtB,WAAW,IAAI,YAAY;AACzB,QAAAA,GAAE,UAAU,KAAK,IAAI,aAAa,cAAcA,GAAE,UAAU,CAAC;AAC7D,gBAAQ,CAAC,MAAM,IAAI,CAAC;AAAA,MACtB,WAAW,UAAU,OAAO,UAAU,KAAK;AACzC,QAAAA,GAAE,SAAS,CAACA,GAAE;AAAA,MAChB,WAAW,UAAU,KAAK;AACxB,YAAIA,GAAE,YAAYA,GAAE,IAAK,SAAQ;AAAA,MACnC,WAAW,UAAU,KAAK;AACxB,YAAIA,GAAE,YAAYA,GAAE,IAAK,kBAAiB;AAAA,MAC5C,WAAW,UAAU,OAAO,IAAI,QAAQ;AACtC,kBAAU,QAAQ;AAAA,MACpB;AAAA,IACF,GAAG,CAAC,gBAAgB,SAAS,gBAAgB,CAAC;AAAA,EAChD;AAEA,QAAM,IAAI,SAAS;AACnB,QAAM,aAAa,EAAE,OAAO,OAAO,CAAC,MAAM,EAAE,KAAK,EAAE;AACnD,QAAM,QAAQ,WAAW,CAAC;AAC1B,QAAM,MAAM,SAAS,EAAE,KAAK;AAE5B,OAAKD;AAEL,SACE,gBAAAD,OAACT,OAAA,EAAI,eAAc,UAEjB;AAAA,oBAAAS,OAACT,OAAA,EAAI,eAAc,OACjB;AAAA,sBAAAQ,MAACR,OAAA,EAAI,OAAO,IACV,0BAAAS,OAACR,QAAA,EAAK;AAAA;AAAA,QACA,EAAE;AAAA,QAAM;AAAA,QAAE,gBAAAO,MAACP,QAAA,EAAK,OAAM,QAAQ,cAAI,MAAK;AAAA,SAC7C,GACF;AAAA,MACA,gBAAAO,MAACR,OAAA,EAAI,OAAO,IACV,0BAAAS,OAACR,QAAA,EAAK;AAAA;AAAA,QACA,gBAAAO,MAACP,QAAA,EAAK,OAAM,UAAU,iBAAO,EAAE,KAAK,EAAE,SAAS,GAAG,GAAG,GAAE;AAAA,SAC7D,GACF;AAAA,MACA,gBAAAO,MAACR,OAAA,EAAI,OAAO,IACV,0BAAAS,OAACR,QAAA,EAAK;AAAA;AAAA,QACA,gBAAAO,MAACP,QAAA,EAAK,OAAM,OAAO,mBAAI,OAAO,KAAK,IAAI,GAAG,EAAE,KAAK,CAAC,GAAE;AAAA,SAC1D,GACF;AAAA,MACA,gBAAAO,MAACR,OAAA,EAAI,OAAO,IACV,0BAAAS,OAACR,QAAA,EAAK;AAAA;AAAA,QACA,gBAAAO,MAACP,QAAA,EAAK,OAAM,QAAQ,sBAAW;AAAA,SACrC,GACF;AAAA,MACA,gBAAAO,MAACR,OAAA,EACC,0BAAAS,OAACR,QAAA,EAAK,OAAO,EAAE,SAAS,SAAS,SAAS;AAAA;AAAA,QACtC,EAAE,SAAS,iBAAO;AAAA,QAAM;AAAA,SAC5B,GACF;AAAA,OACF;AAAA,IAGA,gBAAAQ,OAACT,OAAA,EAAI,eAAc,UACjB;AAAA,sBAAAS,OAACR,QAAA,EAAK;AAAA;AAAA,QAAE,SAAI,OAAO,UAAU;AAAA,QAAE;AAAA,SAAC;AAAA,MAChC,gBAAAO,MAACP,QAAA,EAAM,2BAAiB,mEAAgC,OAAM;AAAA,MAC9D,gBAAAQ,OAACR,QAAA,EAAK;AAAA;AAAA,QAAE,SAAI,OAAO,UAAU;AAAA,QAAE;AAAA,SAAC;AAAA,OAClC;AAAA,IAGC,kBACC,gBAAAQ,OAACT,OAAA,EAAI,WAAW,GAAG,eAAc,UAC/B;AAAA,sBAAAQ,MAACP,QAAA,EAAK,MAAI,MAAC,OAAM,UAAS,sCAAI;AAAA,MAC9B,gBAAAO,MAACR,OAAA,EAAI,eAAc,OAAM,UAAS,QAC/B,iBAAO,IAAI,CAAC,IAAI,MACf,gBAAAQ,MAACR,OAAA,EAAY,OAAO,IAClB,0BAAAS,OAACR,QAAA,EACC;AAAA,wBAAAO,MAACP,QAAA,EAAK,OAAO,iBAAiB,IAAI,IAAI,UAAU,SAC7C,cAAI,MAAM,KAAK,MAAM,OAAO,IAAI,CAAC,GACpC;AAAA,QAAO;AAAA,QACJ,GAAG;AAAA,QAAK;AAAA,QAAG,GAAG;AAAA,QAAK;AAAA,QAAE,GAAG;AAAA,QAAK;AAAA,SAClC,KANQ,CAOV,CACD,GACH;AAAA,MACA,gBAAAO,MAACR,OAAA,EAAI,WAAW,GACd,0BAAAQ,MAACP,QAAA,EAAK,UAAQ,MAAC,4DAAW,GAC5B;AAAA,OACF;AAAA,IAID,CAAC,mBAAmB,EAAE,YAAY,EAAE,QACnC,gBAAAQ,OAACT,OAAA,EAAI,WAAW,GACd;AAAA,sBAAAQ,MAACP,QAAA,EAAK,MAAI,MAAC,OAAO,EAAE,WAAW,QAAQ,SACpC,YAAE,WAAW,mCAAU,kCAC1B;AAAA,MACA,gBAAAQ,OAACR,QAAA,EACE;AAAA;AAAA,QAAS,gBAAAO,MAACP,QAAA,EAAK,OAAM,UAAU,YAAE,OAAM;AAAA,SAC1C;AAAA,OACF;AAAA,IAID,CAAC,kBACA,gBAAAO,MAACR,OAAA,EAAI,WAAW,GACb,YAAE,YAAY,EAAE,MACf,gBAAAQ,MAACP,QAAA,EAAK,UAAQ,MACX,wFACH,IAEA,gBAAAO,MAACP,QAAA,EAAK,UAAQ,MACX,wEACH,GAEJ;AAAA,KAEJ;AAEJ;AAIA,IAAO,wBAAQ;AAAA,EACb,IAAI;AAAA,EACJ,MAAM;AAAA,EACN,aAAa;AAAA,EACb,MAAM,YAAY;AAChB,UAAM,IAAI,QAAc,CAACW,aAAY;AACnC,YAAM,EAAE,QAAQ,IAAIT;AAAA,QAClB,gBAAAK,MAAC,oBAAiB,QAAQ,MAAM;AAAE,kBAAQ;AAAG,UAAAI,SAAQ;AAAA,QAAG,GAAG;AAAA,MAC7D;AAAA,IACF,CAAC;AAAA,EACH;AACF;;;ACpXA,SAAS,OAAAC,OAAK,QAAAC,QAAM,YAAAC,WAAU,UAAAC,eAAc;AAC5C,SAAS,YAAAC,WAAU,aAAAC,YAAW,UAAAC,SAAQ,eAAAC,oBAAmB;AAyY5C,gBAAAC,OACM,QAAAC,cADN;AAvYb,IAAM,SAAS;AACf,IAAM,SAAS;AACf,IAAM,UAAU;AAEhB,IAAM,YAAY;AAClB,IAAMC,iBAAgB,CAAC,WAAW,WAAW,WAAW,WAAW,SAAS;AAG5E,IAAM,YAAsC;AAAA,EAC1C,KAAK,CAAC,0CAAY,gCAAY,gCAAY,gCAAY,wCAAU;AAAA,EAChE,KAAK,CAAC,sBAAY,gCAAY,sBAAY,sBAAY,wCAAU;AAAA,EAChE,KAAK,CAAC,0CAAY,sBAAY,0CAAY,sBAAY,wCAAU;AAAA,EAChE,KAAK,CAAC,0CAAY,sBAAY,0CAAY,sBAAY,wCAAU;AAAA,EAChE,KAAK,CAAC,gCAAY,gCAAY,0CAAY,sBAAY,oBAAU;AAAA,EAChE,KAAK,CAAC,0CAAY,sBAAY,0CAAY,sBAAY,wCAAU;AAAA,EAChE,KAAK,CAAC,0CAAY,sBAAY,0CAAY,gCAAY,wCAAU;AAAA,EAChE,KAAK,CAAC,0CAAY,sBAAY,sBAAY,sBAAY,oBAAU;AAAA,EAChE,KAAK,CAAC,0CAAY,gCAAY,0CAAY,gCAAY,wCAAU;AAAA,EAChE,KAAK,CAAC,0CAAY,gCAAY,0CAAY,sBAAY,wCAAU;AAClE;AAEA,SAAS,gBAAgB,UAA4B;AACnD,QAAM,QAAQ,CAAC,IAAI,IAAI,IAAI,IAAI,EAAE;AACjC,aAAW,MAAM,UAAU;AACzB,UAAM,MAAM,UAAU,EAAE,KAAK,UAAU,GAAG;AAC1C,aAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,YAAM,CAAC,KAAK,OAAO,IAAI,CAAC,KAAK;AAAA,IAC/B;AAAA,EACF;AAEA,QAAM,MAAM;AACZ,SAAO,MAAM,IAAI,CAAC,MAAM,IAAI,OAAO,GAAG,IAAI,CAAC;AAC7C;AAEA,SAAS,SAAS,KAAqB;AACrC,QAAM,IAAI,SAAS,IAAI,MAAM,GAAG,CAAC,GAAG,EAAE;AACtC,QAAM,IAAI,SAAS,IAAI,MAAM,GAAG,CAAC,GAAG,EAAE;AACtC,QAAM,IAAI,SAAS,IAAI,MAAM,GAAG,CAAC,GAAG,EAAE;AACtC,SAAO,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC;AACvB;AAIA,IAAM,aAAa;AAAA;AAAA,EAEjB;AAAA,EAAS;AAAA,EAAO;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAS;AAAA,EAAW;AAAA,EAAU;AAAA,EAC9D;AAAA,EAAW;AAAA,EAAQ;AAAA,EAAS;AAAA,EAAU;AAAA,EAAY;AAAA,EAAY;AAAA,EAC9D;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAW;AAAA,EAAS;AAAA,EAAS;AAAA,EAAQ;AAAA,EACrD;AAAA,EAAU;AAAA,EAAU;AAAA,EAAW;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAU;AAAA;AAAA,EAGzD;AAAA,EAAQ;AAAA,EAAO;AAAA,EAAO;AAAA,EAAO;AAAA,EAAO;AAAA,EAAO;AAAA,EAAO;AAAA,EAAO;AAAA,EACzD;AAAA,EAAa;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAc;AAAA,EAAa;AAAA,EAAU;AAAA,EAClE;AAAA,EAAY;AAAA,EAAU;AAAA,EAAW;AAAA,EAAQ;AAAA,EAAS;AAAA,EAClD;AAAA,EAAc;AAAA,EAAW;AAAA,EAAQ;AAAA,EAAa;AAAA;AAAA,EAG9C;AAAA,EAAO;AAAA,EAAS;AAAA,EAAc;AAAA,EAAc;AAAA,EAAW;AAAA,EACvD;AAAA,EAAO;AAAA,EAAU;AAAA,EAAU;AAAA,EAAa;AAAA,EAAU;AAAA,EAClD;AAAA,EAAQ;AAAA,EAAO;AAAA,EAAS;AAAA,EAAW;AAAA,EAAU;AAAA,EAAS;AAAA,EACtD;AAAA,EAAS;AAAA,EAAS;AAAA,EAAM;AAAA,EAAU;AAAA,EAAS;AAAA;AAAA,EAG3C;AAAA,EAAO;AAAA,EAAS;AAAA,EAAM;AAAA,EAAU;AAAA,EAAS;AAAA,EAAS;AAAA,EAClD;AAAA,EAAS;AAAA,EAAS;AAAA,EAAS;AAAA,EAAQ;AAAA,EAAS;AAAA,EAAS;AAAA,EACrD;AAAA,EAAS;AAAA,EAAU;AAAA,EAAS;AAAA,EAAU;AAAA,EAAU;AAAA,EAAU;AAAA;AAAA,EAG1D;AAAA,EAAU;AAAA,EAAS;AAAA,EAAS;AAAA,EAAQ;AAAA,EAAS;AAAA,EAAQ;AAAA,EACrD;AAAA,EAAM;AAAA,EAAM;AAAA,EAAU;AAAA,EAAY;AAAA,EAAW;AAAA,EAAS;AAAA,EACtD;AAAA,EAAQ;AAAA,EAAU;AAAA,EAAS;AAAA,EAAU;AAAA,EAAU;AAAA,EAAS;AAAA;AAAA,EAGxD;AAAA,EAAS;AAAA,EAAS;AAAA,EAAS;AAAA,EAAQ;AAAA,EAAS;AAAA,EAAQ;AAAA,EAAO;AAAA,EAC3D;AAAA,EAAQ;AAAA,EAAU;AAAA,EAAU;AAAA,EAAU;AAAA,EAAO;AAAA,EAAS;AAAA,EACtD;AAAA,EAAW;AAAA,EAAY;AAAA,EAAW;AAAA,EAAS;AAAA,EAAW;AAAA;AAAA,EAGtD;AAAA,EAAU;AAAA,EAAQ;AAAA,EAAU;AAAA,EAAU;AAAA,EAAQ;AAAA,EAAS;AAAA,EACvD;AAAA,EAAU;AAAA,EAAU;AAAA,EAAa;AAAA,EAAY;AAAA,EAAU;AAAA,EACvD;AAAA,EAAU;AAAA,EAAY;AAAA,EAAU;AAAA,EAAU;AAAA,EAAU;AAAA;AAAA,EAGpD;AAAA,EAAU;AAAA,EAAU;AAAA,EAAW;AAAA,EAAU;AAAA,EAAS;AAAA,EAAS;AAAA,EAC3D;AAAA,EAAa;AAAA,EAAQ;AAAA,EAAS;AAAA,EAAY;AAAA,EAAU;AAAA,EACpD;AAAA,EAAU;AAAA,EAAW;AAAA,EAAU;AAAA,EAAY;AAAA,EAAY;AAAA,EACvD;AAAA,EAAS;AAAA,EAAO;AAAA,EAAO;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAS;AAAA,EAAS;AAAA;AAAA,EAGzD;AAAA,EAAU;AAAA,EAAU;AAAA,EAAW;AAAA,EAAU;AAAA,EAAS;AAAA,EAClD;AAAA,EAAY;AAAA,EAAW;AAAA,EAAU;AAAA,EAAU;AAAA,EAAW;AAAA,EACtD;AAAA,EAAY;AAAA,EAAW;AAAA,EAAW;AAAA,EAAY;AAAA,EAC9C;AAAA,EAAU;AAAA,EAAU;AAAA,EAAS;AAAA,EAAS;AAAA,EAAS;AACjD;AAEA,SAAS,WAAW,MAA2B;AAC7C,MAAI;AACJ,KAAG;AAAE,QAAI,WAAW,KAAK,MAAM,KAAK,OAAO,IAAI,WAAW,MAAM,CAAC;AAAA,EAAa,SACvE,KAAK,IAAI,CAAC;AACjB,SAAO;AACT;AA0BA,SAASC,sBAAgC;AACvC,SAAO;AAAA,IACL,OAAO,CAAC;AAAA,IACR,OAAO;AAAA,IACP,OAAO;AAAA,IACP,OAAO;AAAA,IACP,YAAY;AAAA,IACZ,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,SAAS;AAAA,IACT,cAAc;AAAA,IACd,WAAW,oBAAI,IAAI;AAAA,EACrB;AACF;AAEA,SAAS,WAAW,GAA6B;AAE/C,MAAI,OAAwB;AAC5B,aAAW,KAAK,EAAE,OAAO;AACvB,QAAI,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAK,QAAO;AAAA,EACxC;AACA,SAAO,MAAM,QAAQ;AACvB;AAIA,SAASC,QAAO,GAAoB;AAClC,MAAI,EAAE,UAAU,EAAE,SAAU;AAG5B,IAAE;AACF,MAAI,EAAE,cAAc,KAAK,IAAI,IAAI,KAAK,KAAK,MAAM,EAAE,QAAQ,EAAE,CAAC,GAAG;AAC/D,MAAE,aAAa;AACf,QAAI,EAAE,MAAM,SAAS,WAAW;AAC9B,YAAM,OAAO,IAAI,IAAI,EAAE,SAAS;AAChC,iBAAW,KAAK,EAAE,MAAO,MAAK,IAAI,EAAE,IAAI;AACxC,YAAM,OAAO,WAAW,IAAI;AAE5B,YAAM,WAAW,IAAI,IAAI,EAAE,MAAM,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC;AAClD,UAAI,MAAM;AACV,eAAS,IAAI,SAAS,IAAI,QAAQ,KAAK;AACrC,YAAI,CAAC,SAAS,IAAI,CAAC,GAAG;AAAE,gBAAM;AAAG;AAAA,QAAO;AAAA,MAC1C;AACA,UAAI,OAAO,GAAG;AACZ,UAAE,MAAM,KAAK,EAAE,MAAM,KAAK,KAAK,SAAS,EAAE,CAAC;AAC3C,UAAE,UAAU,IAAI,IAAI;AAAA,MACtB;AAAA,IACF;AAAA,EACF;AAGA,aAAW,KAAK,EAAE,OAAO;AACvB,MAAE,OAAO,EAAE;AAAA,EACb;AAGA,QAAM,SAAS,EAAE,MAAM;AACvB,IAAE,QAAQ,EAAE,MAAM,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,KAAK,MAAM;AACtD,QAAM,UAAU,SAAS,EAAE,MAAM;AAGjC,MAAI,UAAU,GAAG;AACf,MAAE,SAAS;AACX,MAAE,QAAQ;AACV,MAAE,SAAS,WAAW,CAAC;AACvB,QAAI,EAAE,SAAS,GAAG;AAChB,QAAE,WAAW;AACb,QAAE,QAAQ,CAAC;AAAA,IACb;AAAA,EACF;AAGA,IAAE,SAAS,WAAW,CAAC;AAGvB,MAAI,EAAE,eAAe,GAAG;AACtB,MAAE;AACF,QAAI,EAAE,gBAAgB,EAAG,GAAE,UAAU;AAAA,EACvC;AACF;AAIA,SAAS,cAAc,GAAc,YAAsB,YAAoB,SAA2B;AACxG,QAAM,OAAiB,CAAC;AAExB,WAAS,IAAI,GAAG,IAAI,QAAQ,KAAK;AAC/B,QAAI,OAAO;AAGX,QAAI,IAAI,SAAS;AACf,UAAI,IAAI,GAAG;AAET,cAAM,OAAO,WAAW,CAAC,KAAK,IAAI,OAAO,MAAM;AAC/C,eAAO,aAAa,SAAS,UAAU,CAAC,IAAI,GAAG;AAAA,MACjD,WAAW,MAAM,GAAG;AAElB,YAAI,EAAE,SAAS,GAAG;AAChB,gBAAM,YAAY,GAAG,EAAE,KAAK;AAC5B,gBAAM,MAAM,KAAK,OAAO,SAAS,UAAU,UAAU,CAAC;AACtD,gBAAM,aAAaF,eAAc,EAAE,QAAQA,eAAc,MAAM;AAC/D,gBAAM,MAAM,IAAI,OAAO,GAAG,IAAI,YAAY,IAAI,OAAO,SAAS,MAAM,UAAU,MAAM;AACpF,iBAAO,aAAa,SAAS,UAAU,CAAC,IAAI,GAAG;AAAA,QACjD,WAAW,EAAE,QAAQ;AACnB,gBAAM,YAAY;AAClB,gBAAM,MAAM,KAAK,OAAO,SAAS,UAAU,UAAU,CAAC;AACtD,iBAAO,IAAI,OAAO,GAAG,IAAI;AACzB,iBAAO,KAAK,OAAO,MAAM;AAAA,QAC3B;AAAA,MACF;AAEA,aAAO,KAAK,OAAO,MAAM;AAAA,IAC3B,OAAO;AAGL,UAAI,MAAM,WAAW,SAAS;AAC5B,cAAM,MAAM,KAAK,OAAO,SAAS,QAAQ,UAAU,CAAC;AACpD,cAAM,MAAM,IAAI,OAAO,GAAG,IAAI,UAAU,IAAI,OAAO,SAAS,MAAM,QAAQ,MAAM;AAChF,cAAM,WAAWA,eAAc,KAAK,MAAM,KAAK,OAAO,IAAIA,eAAc,MAAM,CAAC;AAC/E,eAAO,aAAa,SAAS,QAAQ,CAAC,IAAI,GAAG;AAAA,MAC/C,OAAO;AACL,iBAAS,IAAI,GAAG,IAAI,QAAQ,KAAK;AAC/B,gBAAM,OAAO,EAAE,MAAM,KAAK,CAAC,MAAM;AAC/B,kBAAM,UAAU,IAAI,KAAK,MAAM,EAAE,GAAG;AACpC,mBAAO,WAAW,KAAK,UAAU,EAAE,KAAK,UAAU,EAAE,QAAQ;AAAA,UAC9D,CAAC;AACD,cAAI,MAAM;AACR,kBAAM,UAAU,IAAI,KAAK,MAAM,KAAK,GAAG;AACvC,kBAAM,KAAK,KAAK,KAAK,OAAO;AAC5B,kBAAM,WAAW,KAAK,SAAS,EAAE;AACjC,kBAAM,WAAW,EAAE,WAAW,KAAK,OAAO,EAAE,MAAM,SAAS;AAC3D,kBAAM,UAAU,YAAY,UAAU;AACtC,gBAAI,UAAU;AACZ,sBAAQ,UAAU,WAAW,EAAE,YAAY,WAAW,EAAE;AAAA,YAC1D,OAAO;AACL,sBAAQ,WAAW,EAAE;AAAA,YACvB;AAAA,UACF,OAAO;AACL,oBAAQ;AAAA,UACV;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,SAAK,KAAK,IAAI;AAAA,EAChB;AACA,SAAO;AACT;AAQA,SAAS,WAAW,EAAE,QAAQ,QAAQ,GAAoB;AACxD,QAAM,WAAWG,QAAkBF,oBAAmB,CAAC;AACvD,QAAM,CAACG,OAAM,OAAO,IAAIC,UAAS,CAAC;AAClC,QAAM,CAAC,aAAa,cAAc,IAAIA,UAAS,CAAC;AAChD,QAAM,YAAYF,QAAO,OAAO;AAChC,YAAU,UAAU;AAGpB,EAAAG,WAAU,MAAM;AACd,UAAM,QAAQ,YAAY,MAAM;AAC9B,qBAAe,CAAC,UAAU,OAAO,KAAKN,eAAc,MAAM;AAAA,IAC5D,GAAG,GAAG;AACN,WAAO,MAAM,cAAc,KAAK;AAAA,EAClC,GAAG,CAAC,CAAC;AAGL,EAAAM,WAAU,MAAM;AACd,UAAM,WAAW,YAAY,MAAM;AACjC,MAAAJ,QAAO,SAAS,OAAO;AACvB,cAAQ,CAAC,MAAM,IAAI,CAAC;AAAA,IACtB,GAAG,EAAE;AACL,WAAO,MAAM,cAAc,QAAQ;AAAA,EACrC,GAAG,CAAC,CAAC;AAEL,EAAAK;AAAA,IACEC,aAAY,CAAC,OAAO,QAAQ;AAC1B,YAAMC,KAAI,SAAS;AACnB,UAAIA,GAAE,UAAU;AACd,YAAI,UAAU,KAAK;AACjB,mBAAS,UAAUR,oBAAmB;AACtC,kBAAQ,CAAC;AAAA,QACX,WAAW,UAAU,OAAO,IAAI,QAAQ;AACtC,oBAAU,QAAQ;AAAA,QACpB;AACA;AAAA,MACF;AAEA,UAAK,UAAU,OAAO,IAAI,QAAS,UAAU,KAAK;AAChD,QAAAQ,GAAE,SAAS,CAACA,GAAE;AACd;AAAA,MACF;AACA,UAAK,UAAU,OAAO,IAAI,QAAS,IAAI,QAAQ;AAC7C,kBAAU,QAAQ;AAClB;AAAA,MACF;AAEA,UAAIA,GAAE,OAAQ;AAGd,UAAI,MAAM,WAAW,KAAK,SAAS,OAAO,SAAS,KAAK;AAEtD,YAAI,CAACA,GAAE,QAAQ;AACb,UAAAA,GAAE,SAAS,WAAWA,EAAC;AAAA,QACzB;AACA,YAAIA,GAAE,QAAQ;AACZ,gBAAM,WAAWA,GAAE,OAAOA,GAAE,MAAM,MAAM;AACxC,cAAI,aAAa,OAAO;AACtB,YAAAA,GAAE,SAAS;AAEX,gBAAIA,GAAE,UAAUA,GAAE,QAAQ;AAExB,cAAAA,GAAE,QAAQA,GAAE,MAAM,OAAO,CAAC,MAAM,EAAE,SAASA,GAAE,MAAM;AACnD,cAAAA,GAAE;AAEF,oBAAM,YAAYA,GAAE;AACpB,oBAAM,UAAUA,GAAE,OAAO;AACzB,oBAAM,aAAaA,GAAE,SAAS,IAAIA,GAAE,QAAQ;AAC5C,cAAAA,GAAE,SAAS,UAAU;AACrB,cAAAA,GAAE,QAAQA,GAAE,QAAQ;AAGpB,oBAAM,gBAAgB,KAAK,MAAM,YAAY,GAAG;AAChD,oBAAM,eAAe,KAAK,MAAMA,GAAE,QAAQ,GAAG;AAC7C,kBAAI,eAAe,eAAe;AAChC,gBAAAA,GAAE,SAAS;AAAA,cACb;AAGA,kBAAIA,GAAE,SAAS,OAAU,YAAY,KAAQ;AAC3C,gBAAAA,GAAE,UAAU;AACZ,gBAAAA,GAAE,eAAe;AAAA,cACnB;AACA,cAAAA,GAAE,QAAQ;AACV,cAAAA,GAAE,SAAS,WAAWA,EAAC;AAAA,YACzB;AAAA,UACF,OAAO;AAEL,YAAAA,GAAE,QAAQ;AAAA,UACZ;AAAA,QACF;AAAA,MACF;AAAA,IACF,GAAG,CAAC,CAAC;AAAA,EACP;AAEA,QAAM,IAAI,SAAS;AACnB,QAAM,aAAaT,eAAc,WAAW;AAC5C,QAAM,WAAW,OAAO,EAAE,KAAK,EAAE,SAAS,GAAG,GAAG;AAChD,QAAM,aAAa,gBAAgB,QAAQ;AAC3C,QAAM,OAAO,cAAc,GAAG,YAAY,YAAY,EAAE,OAAO;AAE/D,OAAKI;AAEL,SACE,gBAAAM,OAACC,OAAA,EAAI,eAAc,UAAS,UAAU,GAEpC;AAAA,oBAAAC,MAACD,OAAA,EAAI,eAAc,OACjB,0BAAAD,OAACG,QAAA,EAAK;AAAA;AAAA,MACD,gBAAAD,MAACC,QAAA,EAAK,OAAM,OAAO,mBAAI,OAAO,KAAK,IAAI,GAAG,EAAE,KAAK,CAAC,GAAE;AAAA,MACtD;AAAA,MAAK;AAAA,MAAG,gBAAAH,OAACG,QAAA,EAAK,OAAM,QAAO;AAAA;AAAA,QAAI,KAAK,MAAM,EAAE,QAAQ,EAAE;AAAA,SAAE;AAAA,OAC3D,GACF;AAAA,IAGC,CAAC,EAAE,YAAY,EAAE,UAChB,gBAAAD,MAACD,OAAA,EAAI,WAAW,GACd,0BAAAD,OAACG,QAAA,EAAK;AAAA;AAAA,MACA,gBAAAD,MAACC,QAAA,EAAK,OAAM,SAAS,YAAE,OAAM;AAAA,MACjC,gBAAAD,MAACC,QAAA,EAAK,OAAM,SAAS,YAAE,OAAO,MAAM,EAAE,MAAM,MAAM,GAAE;AAAA,OACtD,GACF;AAAA,IAIF,gBAAAH,OAACC,OAAA,EAAI,eAAc,UAAS,WAAW,GACrC;AAAA,sBAAAD,OAACG,QAAA,EAAK;AAAA;AAAA,QAAE,SAAI,OAAO,MAAM;AAAA,QAAE;AAAA,SAAC;AAAA,MAC3B,KAAK,IAAI,CAAC,KAAK,MACd,gBAAAD,MAACC,QAAA,EAAc,mBAAI,GAAG,YAAX,CAAe,CAC3B;AAAA,MACD,gBAAAH,OAACG,QAAA,EAAK;AAAA;AAAA,QAAE,SAAI,OAAO,MAAM;AAAA,QAAE;AAAA,SAAC;AAAA,OAC9B;AAAA,IAGC,EAAE,YACD,gBAAAH,OAACC,OAAA,EAAI,WAAW,GACd;AAAA,sBAAAC,MAACC,QAAA,EAAK,MAAI,MAAC,OAAM,OAAM,4CAEvB;AAAA,MACA,gBAAAH,OAACG,QAAA,EACE;AAAA;AAAA,QAAS,gBAAAD,MAACC,QAAA,EAAK,OAAM,UAAU,YAAE,OAAM;AAAA,QACvC;AAAA,SACH;AAAA,OACF;AAAA,IAIF,gBAAAD,MAACD,OAAA,EAAI,WAAW,GACd,0BAAAC,MAACC,QAAA,EAAK,UAAQ,MACX,uGACH,GACF;AAAA,KACF;AAEJ;AAIA,IAAO,sBAAQ;AAAA,EACb,IAAI;AAAA,EACJ,MAAM;AAAA,EACN,aAAa;AAAA,EACb,MAAM,YAAY;AAChB,UAAM,IAAI,QAAc,CAACC,aAAY;AACnC,YAAM,EAAE,QAAQ,IAAIC;AAAA,QAClB,gBAAAH,MAAC,cAAW,QAAQ,MAAM;AAAE,kBAAQ;AAAG,UAAAE,SAAQ;AAAA,QAAG,GAAG;AAAA,MACvD;AAAA,IACF,CAAC;AAAA,EACH;AACF;;;AClcA,SAAS,WAAW,GAAc,GAAuB;AACvD,SACG,MAAM,QAAQ,MAAM,UACpB,MAAM,UAAU,MAAM,QACtB,MAAM,UAAU,MAAM,WACtB,MAAM,WAAW,MAAM;AAE5B;AAEA,SAAS,UAAU,GAAU,GAAmB;AAC9C,SAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE;AAClC;AAEA,SAAS,UAAU,GAAU,KAAuB;AAClD,UAAQ,KAAK;AAAA,IACX,KAAK;AAAS,aAAO,EAAE,GAAG,EAAE,GAAG,GAAG,EAAE,IAAI,EAAE;AAAA,IAC1C,KAAK;AAAS,aAAO,EAAE,GAAG,EAAE,GAAG,GAAG,EAAE,IAAI,EAAE;AAAA,IAC1C,KAAK;AAAS,aAAO,EAAE,GAAG,EAAE,IAAI,GAAG,GAAG,EAAE,EAAE;AAAA,IAC1C,KAAK;AAAS,aAAO,EAAE,GAAG,EAAE,IAAI,GAAG,GAAG,EAAE,EAAE;AAAA,EAC5C;AACF;AAEA,SAAS,UAAU,GAAU,QAA2B;AACtD,SAAO;AAAA,IACL,IAAK,EAAE,IAAI,OAAO,QAAS,OAAO,SAAS,OAAO;AAAA,IAClD,IAAK,EAAE,IAAI,OAAO,SAAU,OAAO,UAAU,OAAO;AAAA,EACtD;AACF;AAQA,SAAS,aACP,OACA,QACA,MACA,mBAA4B,CAAC,GAChB;AACb,QAAM,WAAW,IAAI,IAAI,MAAM,IAAI,OAAK,GAAG,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC;AAExD,aAAW,KAAK,kBAAkB;AAChC,aAAS,IAAI,GAAG,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE;AAAA,EAC9B;AAEA,QAAM,aAAa,OAAO,QAAQ,OAAO;AACzC,MAAI,SAAS,QAAQ,WAAY,QAAO;AAExC,MAAI;AACJ,KAAG;AACD,UAAM;AAAA,MACJ,GAAG,KAAK,MAAM,KAAK,OAAO,IAAI,OAAO,KAAK;AAAA,MAC1C,GAAG,KAAK,MAAM,KAAK,OAAO,IAAI,OAAO,MAAM;AAAA,IAC7C;AAAA,EACF,SAAS,SAAS,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,CAAC,EAAE;AAEzC,SAAO;AAAA,IACL,UAAU;AAAA,IACV;AAAA,IACA,GAAI,SAAS,YAAY,EAAE,WAAW,KAAK,IAAI,EAAE,IAAI,CAAC;AAAA,EACxD;AACF;AAKO,SAASE,oBAAmB,QAA+B;AAChE,QAAM,SAAS,KAAK,MAAM,OAAO,QAAQ,CAAC;AAC1C,QAAM,SAAS,KAAK,MAAM,OAAO,SAAS,CAAC;AAE3C,QAAM,QAAiB;AAAA,IACrB,EAAE,GAAG,QAAQ,GAAG,OAAO;AAAA,IACvB,EAAE,GAAG,SAAS,GAAG,GAAG,OAAO;AAAA,IAC3B,EAAE,GAAG,SAAS,GAAG,GAAG,OAAO;AAAA,EAC7B;AAEA,QAAM,OAAO,aAAa,OAAO,QAAQ,QAAQ;AAEjD,SAAO;AAAA,IACL;AAAA,IACA,WAAW;AAAA,IACX,eAAe;AAAA,IACf;AAAA,IACA,aAAa;AAAA,IACb,OAAO;AAAA,IACP,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,UAAU;AAAA,IACV,UAAU,KAAK,OAAO,IAAI;AAAA,IAC1B,cAAc,OAAO;AAAA,EACvB;AACF;AAIO,SAAS,KAAK,OAAkB,QAA0B;AAE/D,QAAM,YAAY,MAAM;AAGxB,QAAM,OAAO,MAAM,MAAM,CAAC;AAC1B,MAAI,UAAU,UAAU,MAAM,MAAM,SAAS;AAG7C,QAAM,cACJ,QAAQ,IAAI,KACZ,QAAQ,KAAK,OAAO,SACpB,QAAQ,IAAI,KACZ,QAAQ,KAAK,OAAO;AAEtB,MAAI,aAAa;AACf,QAAI,MAAM,UAAU;AAClB,gBAAU,UAAU,SAAS,MAAM;AAAA,IACrC,OAAO;AACL,YAAM,aAAa;AACnB;AAAA,IACF;AAAA,EACF;AAGA,QAAM,YAAY,UAAU,SAAS,MAAM,KAAK,QAAQ;AACxD,QAAM,aAAa,MAAM,gBAAgB,QAAQ,UAAU,SAAS,MAAM,YAAY,QAAQ;AAC9F,QAAM,UAAU,aAAa;AAG7B,QAAM,cAAc,UAAU,MAAM,QAAQ,MAAM,MAAM,MAAM,GAAG,EAAE;AACnE,aAAW,WAAW,aAAa;AACjC,QAAI,UAAU,SAAS,OAAO,GAAG;AAC/B,YAAM,aAAa;AACnB;AAAA,IACF;AAAA,EACF;AAGA,QAAM,MAAM,QAAQ,OAAO;AAG3B,MAAI,WAAW;AACb,UAAM,SAAS;AACf,gBAAY,OAAO,MAAM;AAGzB,UAAM,UAAmB,MAAM,cAAc,CAAC,MAAM,YAAY,QAAQ,IAAI,CAAC;AAC7E,UAAM,UAAU,aAAa,MAAM,OAAO,QAAQ,UAAU,OAAO;AACnE,QAAI,SAAS;AACX,YAAM,OAAO;AAAA,IACf,OAAO;AAEL,YAAM,aAAa;AACnB;AAAA,IACF;AAGA,QAAI,MAAM,gBAAgB,QAAQ,KAAK,OAAO,IAAI,MAAM;AACtD,YAAM,cAAc,aAAa,MAAM,OAAO,QAAQ,WAAW,CAAC,MAAM,KAAK,QAAQ,CAAC;AAAA,IACxF;AAAA,EACF,WAAW,YAAY;AACrB,UAAM,SAAS;AACf,gBAAY,OAAO,MAAM;AACzB,UAAM,cAAc;AAAA,EACtB,OAAO;AAEL,UAAM,MAAM,IAAI;AAAA,EAClB;AAGA,MAAI,MAAM,aAAa,cAAc,QAAW;AAC9C,QAAI,KAAK,IAAI,IAAI,MAAM,YAAY,YAAY,KAAM;AACnD,YAAM,cAAc;AAAA,IACtB;AAAA,EACF;AACF;AAIA,SAAS,YAAY,OAAkB,QAA0B;AAC/D,QAAM,WAAW,KAAK,MAAM,MAAM,QAAQ,OAAO,aAAa,IAAI;AAClE,QAAM,aAAa;AACnB,QAAM,eAAe,KAAK;AAAA,IACxB,OAAO;AAAA,IACP,OAAO,gBAAgB,WAAW,KAAK,OAAO;AAAA,EAChD;AACF;AAQO,SAAS,gBAAgB,OAAkB,QAAyB;AACzE,MAAI,CAAC,WAAW,QAAQ,MAAM,SAAS,GAAG;AACxC,UAAM,gBAAgB;AAAA,EACxB;AACF;;;ACxMA,OAAOC,YAAW;AAIlB,IAAM,SAAS;AACf,IAAM,QAAQ;AACd,IAAM,YAAY;AAClB,IAAM,YAAY;AAClB,IAAM,mBAAmB;AACzB,IAAM,oBAAoB;AAI1B,IAAM,MAAM;AACZ,IAAM,cAAc,GAAG,GAAG;AAC1B,IAAM,cAAc,GAAG,GAAG;AAC1B,IAAM,cAAc,GAAG,GAAG;AAGnB,SAAS,aAAmB;AACjC,UAAQ,OAAO,MAAM,WAAW;AAClC;AAGO,SAAS,aAAmB;AACjC,UAAQ,OAAO,MAAM,WAAW;AAClC;AAGO,SAAS,cAAoB;AAClC,UAAQ,OAAO,MAAM,GAAG,GAAG,KAAK,WAAW,EAAE;AAC/C;AAIA,SAAS,UAAU,MAAc,MAAsF;AACrH,UAAQ,MAAM;AAAA,IACZ,KAAK;AAAe,aAAOA,OAAM,YAAY,IAAI;AAAA,IACjD,KAAK;AAAe,aAAOA,OAAM,MAAM,IAAI;AAAA,IAC3C,KAAK;AAAe,aAAOA,OAAM,IAAI,IAAI;AAAA,IACzC,KAAK;AAAgB,aAAOA,OAAM,OAAO,IAAI;AAAA,IAC7C,KAAK;AAAe,aAAO;AAAA,EAC7B;AACF;AAQO,SAAS,iBAAiB,OAAkB,QAA0B;AAC3E,QAAM,QAAQ,iBAAiB,OAAO,MAAM;AAE5C,UAAQ,OAAO,MAAM,GAAG,GAAG,KAAK,WAAW,KAAK,MAAM,KAAK,IAAI,CAAC;AAClE;AAEA,SAAS,iBAAiB,OAAkB,QAA8B;AACxE,QAAM,EAAE,OAAO,GAAG,QAAQ,EAAE,IAAI;AAChC,QAAM,QAAkB,CAAC;AAIzB,QAAM,OAAmB,MAAM,KAAK,EAAE,QAAQ,EAAE,GAAG,MAAM,MAAM,CAAC,EAAE,KAAK,OAAO,CAAC;AAG/E,WAAS,IAAI,GAAG,IAAI,MAAM,MAAM,QAAQ,KAAK;AAC3C,UAAM,MAAM,MAAM,MAAM,CAAC;AACzB,QAAI,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,IAAI,KAAK,KAAK,IAAI,IAAI,GAAG;AACtD,WAAK,IAAI,CAAC,EAAG,IAAI,CAAC,IAAI;AAAA,IACxB;AAAA,EACF;AAGA,QAAM,OAAO,MAAM,MAAM,CAAC;AAC1B,MAAI,KAAK,KAAK,KAAK,KAAK,IAAI,KAAK,KAAK,KAAK,KAAK,KAAK,IAAI,GAAG;AAC1D,SAAK,KAAK,CAAC,EAAG,KAAK,CAAC,IAAI;AAAA,EAC1B;AAGA,QAAM,KAAK,MAAM,KAAK;AACtB,MAAI,KAAK,GAAG,CAAC,EAAG,GAAG,CAAC,MAAM,SAAS;AACjC,SAAK,GAAG,CAAC,EAAG,GAAG,CAAC,IAAI;AAAA,EACtB;AAGA,MAAI,MAAM,aAAa;AACrB,UAAM,KAAK,MAAM,YAAY;AAC7B,QAAI,KAAK,GAAG,CAAC,EAAG,GAAG,CAAC,MAAM,SAAS;AACjC,WAAK,GAAG,CAAC,EAAG,GAAG,CAAC,IAAI;AAAA,IACtB;AAAA,EACF;AAGA,QAAM,KAAK,OAAOA,OAAM,KAAK,WAAM,eAAK,OAAO,CAAC,IAAI,QAAG,CAAC;AAGxD,WAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,QAAI,MAAM,OAAOA,OAAM,KAAK,QAAG;AAC/B,aAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,YAAM,WAAW,KAAK,CAAC,EAAG,CAAC;AAC3B,UAAI;AACJ,cAAQ,UAAU;AAAA,QAChB,KAAK;AACH,iBAAO,UAAU,OAAO,MAAM;AAC9B;AAAA,QACF,KAAK;AACH,iBAAO,UAAU,OAAO,MAAM;AAC9B;AAAA,QACF,KAAK;AACH,iBAAO,mBAAmB;AAC1B;AAAA,QACF,KAAK;AACH,iBAAO,oBAAoB;AAC3B;AAAA,QACF;AACE,iBAAO;AAAA,MACX;AACA,aAAO,UAAU,MAAM,QAAe;AAAA,IACxC;AACA,WAAOA,OAAM,KAAK,QAAG;AACrB,UAAM,KAAK,GAAG;AAAA,EAChB;AAGA,QAAM,KAAK,OAAOA,OAAM,KAAK,WAAM,eAAK,OAAO,CAAC,IAAI,QAAG,CAAC;AAGxD,QAAM,KAAK,EAAE;AACb,QAAM;AAAA,IACJ,KAAKA,OAAM,KAAK,cAAI,CAAC,KAAK,MAAM,KAAK,KAClCA,OAAM,KAAK,cAAI,CAAC,KAAK,MAAM,MAAM,MAAM,KACvCA,OAAM,KAAK,cAAI,CAAC,QAAQ,MAAM,UAAU,KACxCA,OAAM,KAAK,cAAI,CAAC,KAAK,MAAM,WAAW,iBAAO,cAAI;AAAA,EACtD;AAGA,MAAI,MAAM,aAAa,cAAc,QAAW;AAC9C,UAAM,YAAY,KAAK,MAAM,OAAQ,KAAK,IAAI,IAAI,MAAM,YAAY,cAAc,GAAI;AACtF,QAAI,YAAY,GAAG;AACjB,YAAM,KAAK,KAAKA,OAAM,OAAO,mCAAU,SAAS,2BAAO,CAAC,EAAE;AAAA,IAC5D;AAAA,EACF;AAGA,QAAM,KAAK,KAAKA,OAAM,KAAK,8GAAwC,CAAC,EAAE;AAGtE,MAAI,MAAM,UAAU;AAClB,UAAM,KAAK,EAAE;AACb,UAAM,KAAK,KAAKA,OAAM,OAAO,yDAAiB,CAAC,EAAE;AAAA,EACnD;AAEA,MAAI,MAAM,YAAY;AACpB,UAAM,KAAK,EAAE;AACb,UAAM,KAAK,KAAKA,OAAM,IAAI,KAAK,sCAAW,CAAC,KAAKA,OAAM,KAAK,+BAAW,MAAM,KAAK,CAAC,EAAE;AACpF,UAAM,KAAK,KAAKA,OAAM,KAAK,2DAAmB,CAAC,EAAE;AAAA,EACnD;AAEA,SAAO;AACT;;;AChKA,YAAY,cAAc;AAM1B,IAAM,aAAwC;AAAA,EAC5C,IAAI;AAAA,EACJ,GAAG;AAAA,EACH,GAAG;AAAA,EACH,MAAM;AAAA,EACN,GAAG;AAAA,EACH,GAAG;AAAA,EACH,MAAM;AAAA,EACN,GAAG;AAAA,EACH,GAAG;AAAA,EACH,OAAO;AAAA,EACP,GAAG;AAAA,EACH,GAAG;AACL;AAqBO,SAAS,WACd,KACA,QACA,WACc;AACd,EAAS,4BAAmB,QAAQ,KAAK;AAEzC,MAAI,QAAQ,MAAM,OAAO;AACvB,YAAQ,MAAM,WAAW,IAAI;AAAA,EAC/B;AAEA,QAAM,UAAU,CAAC,GAAY,QAAsB;AACjD,QAAI,CAAC,OAAO,CAAC,IAAI,KAAM;AAEvB,UAAM,EAAE,MAAM,KAAK,IAAI;AAGvB,QAAI,SAAS,SAAS,OAAO,SAAS,MAAM;AAC1C,aAAO;AACP;AAAA,IACF;AAGA,QAAI,SAAS,KAAK;AAChB,aAAO;AACP;AAAA,IACF;AAEA,UAAM,QAAQ,IAAI;AAGlB,QAAI,MAAM,YAAY;AACpB,UAAI,SAAS,KAAK;AAChB,YAAI,QAAQ,UAAU;AAAA,MACxB;AACA;AAAA,IACF;AAGA,QAAI,SAAS,KAAK;AAChB,YAAM,WAAW,CAAC,MAAM;AACxB;AAAA,IACF;AAGA,QAAI,SAAS,KAAK;AAChB,YAAM,WAAW,CAAC,MAAM;AACxB;AAAA,IACF;AAGA,UAAM,MAAM,WAAW,IAAI;AAC3B,QAAI,KAAK;AACP,sBAAgB,OAAO,GAAG;AAAA,IAC5B;AAAA,EACF;AAEA,UAAQ,MAAM,GAAG,YAAY,OAAO;AAEpC,SAAO,MAAM;AACX,YAAQ,MAAM,eAAe,YAAY,OAAO;AAChD,QAAI,QAAQ,MAAM,OAAO;AACvB,cAAQ,MAAM,WAAW,KAAK;AAAA,IAChC;AAAA,EACF;AACF;;;AClGA,IAAM,SAAqB;AAAA,EACzB,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,cAAc;AAAA,EACd,UAAU;AAAA,EACV,gBAAgB;AAAA,EAChB,eAAe;AACjB;AAGA,IAAM,gBAAgB;AAEtB,IAAM,YAAkB;AAAA,EACtB,IAAI;AAAA,EACJ,MAAM;AAAA,EACN,aAAa;AAAA,EACb,MAAM,YAAY;AAChB,UAAM,MAAoB,EAAE,OAAOC,oBAAmB,MAAM,EAAE;AAE9D,WAAO,IAAI,QAAc,CAACC,aAAY;AACpC,YAAM,eAAe;AAAA,QACnB;AAAA,QACA,MAAM;AAEJ,iBAAO;AACP,uBAAa,OAAO;AACpB,qBAAW;AACX,uBAAa;AACb,UAAAA,SAAQ;AAAA,QACV;AAAA,QACA,MAAM;AAEJ,iBAAOD,oBAAmB,MAAM;AAAA,QAClC;AAAA,MACF;AAGA,kBAAY;AACZ,iBAAW;AAEX,UAAI,OAAO;AACX,UAAI;AAEJ,eAAS,OAAa;AACpB,YAAI,KAAM;AAEV,cAAM,IAAI,IAAI;AAEd,YAAI,CAAC,EAAE,cAAc,CAAC,EAAE,UAAU;AAChC,eAAK,GAAG,MAAM;AAAA,QAChB;AAEA,yBAAiB,GAAG,MAAM;AAG1B,cAAM,WAAW,EAAE,cAAc,EAAE,WAAW,gBAAgB,EAAE;AAChE,kBAAU,WAAW,MAAM,QAAQ;AAAA,MACrC;AAEA,gBAAU,WAAW,MAAM,CAAC;AAAA,IAC9B,CAAC;AAAA,EACH;AACF;AAEA,IAAO,gBAAQ;;;AChER,SAAS,YAAoB;AAClC,eAAa,qBAAY;AACzB,eAAa,mBAAU;AACvB,eAAa,aAAK;AAClB,SAAO,UAAU;AACnB;;;AjDEA,SAAS,UAAAE,eAAc;AACvB,OAAOC,YAAW;;;AkDflB,SAAS,OAAAC,OAAK,QAAAC,QAAM,YAAAC,iBAAgB;AACpC,SAAS,YAAAC,WAAU,eAAAC,cAAa,aAAAC,kBAAiB;AACjD,OAAO,gBAAgB;AACvB,OAAO,QAAQ;AACf,SAAS,QAAAC,aAAY;AAyWX,gBAAAC,OAiBF,QAAAC,cAjBE;AAjWV,IAAM,gBAAgBC,MAAK,GAAG,QAAQ,GAAG,YAAY,eAAe;AAMpE,SAAS,UAAU,GAAmB;AACpC,QAAM,OAAO,EAAE,QAAQ,OAAO,GAAG;AACjC,MAAI,QAAQ,aAAa,SAAS;AAChC,WAAO,YAAY,KAAK,QAAQ,gBAAgB,KAAK;AAAA,EACvD;AACA,SAAO,YAAY;AACrB;AAOA,SAAS,SAAS,KAAa,MAAsB;AACnD,SAAO,WAAW,GAAG,SAAS,IAAI;AACpC;AAOA,IAAM,aAAa;AAWnB,SAAS,iBAAiB,MAAsB;AAC9C,MAAI,KAAK,WAAW,IAAI,KAAK,KAAK,WAAW,IAAI,EAAG,QAAO;AAC3D,MAAI,cAAc,KAAK,IAAI,EAAG,QAAO,OAAO;AAC5C,MAAI,cAAc,KAAK,IAAI,EAAG,QAAO,OAAO;AAC5C,SAAO,OAAO;AAChB;AAMA,eAAe,iBAAiB,MAItB;AACR,QAAM,MAAM,WAAW,QAAQ,UAAU,iBAAiB,IAAI,CAAC;AAC/D,MAAI;AACF,UAAM,OAAO,MAAM,MAAM,GAAG;AAC5B,UAAM,OAAQ,MAAM,KAAK,KAAK;AAC9B,QAAI,KAAK,SAAS,EAAG,QAAO;AAE5B,UAAM,WAAW,iBAAiB,IAAI;AACtC,UAAM,YAAY,KAAK,OAAO,QAAQ;AACtC,QAAI,CAAC,UAAW,QAAO;AAGvB,UAAM,aAAa,UAAU,MAAM,QAAQ,CAAC;AAC5C,UAAM,SAAmB,CAAC;AAC1B,eAAW,QAAQ,YAAY;AAE7B,YAAM,QAAS,KAAgB,MAAM,GAAG;AACxC,UAAI,MAAM,UAAU,GAAG;AACrB,cAAM,IAAI,WAAW,MAAM,CAAC,CAAE;AAC9B,YAAI,CAAC,MAAM,CAAC,EAAG,QAAO,KAAK,CAAC;AAAA,MAC9B;AAAA,IACF;AAGA,UAAM,QAAQ;AACd,UAAM,KAAK,UAAU,KAAK,KAAK;AAC/B,QAAI,QAAyB;AAC7B,QAAI,MAAM,GAAG,UAAU,IAAI;AACzB,cAAQ;AAAA,QACN;AAAA,QACA,MAAM,GAAG,CAAC,KAAK;AAAA,QACf,OAAO,WAAW,GAAG,CAAC,KAAK,GAAG;AAAA,QAC9B,eAAe,WAAW,GAAG,EAAE,KAAK,GAAG;AAAA,QACvC,cAAc,WAAW,GAAG,EAAE,KAAK,GAAG;AAAA,QACtC,MAAM,WAAW,GAAG,EAAE,KAAK,GAAG;AAAA,QAC9B,KAAK,WAAW,GAAG,EAAE,KAAK,GAAG;AAAA,QAC7B,QAAQ,SAAS,GAAG,CAAC,KAAK,KAAK,EAAE;AAAA;AAAA,QAEjC,QAAQ,WAAW,GAAG,EAAE,KAAK,GAAG,IAAI;AAAA,MACtC;AAAA,IACF;AAEA,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA,MAAM,UAAU,MAAM,QAAQ;AAAA,IAChC;AAAA,EACF,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAOA,IAAM,cAAc,oBAAI,IAAsB;AAE9C,SAAS,YAAY,MAAc,QAAwB;AACzD,cAAY,IAAI,MAAM,MAAM;AAC9B;AAeA,IAAM,kBAA8B;AAAA;AAAA,EAElC,EAAE,MAAM,UAAU,MAAM,4BAAQ,OAAO,MAAS,eAAe,MAAM,cAAc,OAAO,MAAM,MAAS,KAAK,MAAS,QAAQ,SAAW,QAAQ,OAAkB;AAAA;AAAA,EAEpK,EAAE,MAAM,UAAU,MAAM,4BAAQ,OAAO,MAAO,eAAe,MAAM,cAAc,MAAM,MAAM,OAAO,KAAK,MAAO,QAAQ,UAAU,QAAQ,OAAe;AAAA,EACzJ,EAAE,MAAM,UAAU,MAAM,4BAAQ,OAAO,MAAS,eAAe,OAAO,cAAc,MAAO,MAAM,MAAS,KAAK,MAAS,QAAQ,QAAU,QAAQ,OAAe;AACnK;AAMA,eAAe,YAAY,OAAsC;AAC/D,QAAM,UAAU,MAAM,QAAQ;AAAA,IAC5B,MAAM,IAAI,OAAO,SAAS;AACxB,YAAM,OAAO,MAAM,iBAAiB,IAAI;AACxC,UAAI,MAAM,OAAO;AAEf,YAAI,KAAK,OAAO,SAAS,EAAG,aAAY,MAAM,KAAK,MAAM;AACzD,eAAO,KAAK;AAAA,MACd;AACA,aAAO;AAAA,IACT,CAAC;AAAA,EACH;AAGA,QAAM,OAAmB,QAAQ,OAAO,CAAC,MAAqB,MAAM,IAAI;AACxE,MAAI,KAAK,SAAS,EAAG,QAAO;AAG5B,SAAO;AACT;AAMA,SAAS,YAAY,GAAmB;AACtC,SAAO,KAAK,MAAM,EAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;AAC9C;AAWA,SAAS,aAAa,MAAsB;AAC1C,MAAI,QAAQ,KAAmB,SAAQ,OAAO,MAAmB,QAAQ,CAAC,IAAI;AAC9E,MAAI,QAAQ,IAAa,SAAQ,OAAO,KAAa,QAAQ,CAAC,IAAI;AAClE,MAAI,QAAQ,IAAQ,SAAQ,OAAO,KAAQ,QAAQ,CAAC,IAAI;AACxD,SAAO,KAAK,eAAe;AAC7B;AAOA,SAAS,aAAa,MAAgB,YAAY,IAAc;AAC9D,MAAI,KAAK,UAAU,UAAW,QAAO;AACrC,SAAO,KAAK,MAAM,KAAK,SAAS,SAAS;AAC3C;AAeO,SAAS,UAAU,EAAE,OAAO,QAAQ,aAAa,GAAmB;AACzE,QAAM,CAAC,QAAQ,SAAS,IAAIC,UAAqB,CAAC,CAAC;AACnD,QAAM,CAAC,eAAe,gBAAgB,IAAIA,UAAS,CAAC;AACpD,QAAM,CAAC,SAAS,UAAU,IAAIA,UAAS,IAAI;AAC3C,QAAM,CAAC,YAAY,aAAa,IAAIA,UAAiB,EAAE;AACvD,QAAM,CAAC,YAAY,aAAa,IAAIA,UAA0B,IAAI;AAClE,QAAM,CAAC,cAAc,eAAe,IAAIA,UAA0B,IAAI;AACtE,QAAM,CAAC,eAAe,gBAAgB,IAAIA,UAAS,KAAK;AACxD,QAAM,CAAC,iBAAiB,kBAAkB,IAAIA,UAAS,EAAE;AACzD,QAAM,CAAC,WAAW,YAAY,IAAIA,UAAS,CAAC;AAC5C,QAAM,CAAC,aAAa,cAAc,IAAIA;AAAA,IAAiB,OACrD,oBAAI,KAAK,GAAE,mBAAmB,SAAS,EAAE,QAAQ,MAAM,CAAC;AAAA,EAC1D;AAEA,QAAM,CAAC,SAAS,UAAU,IAAIA,UAAS,KAAK;AAE5C,QAAM,EAAE,aAAa,YAAY,IAAI,eAAe,MAAM;AAG1D,EAAAC,WAAU,MAAM;AACd,UAAM,QAAQ,YAAY,MAAM;AAC9B,sBAAe,oBAAI,KAAK,GAAE,mBAAmB,SAAS,EAAE,QAAQ,MAAM,CAAC,CAAC;AAAA,IAC1E,GAAG,GAAI;AACP,WAAO,MAAM,cAAc,KAAK;AAAA,EAClC,GAAG,CAAC,CAAC;AAGL,QAAM,WAAWC,aAAY,YAAY;AACvC,eAAW,IAAI;AACf,QAAI;AACF,YAAM,OAAO,MAAM,YAAY,SAAS,CAAC,CAAC;AAC1C,gBAAU,IAAI;AACd,qBAAc,oBAAI,KAAK,GAAE,mBAAmB,CAAC;AAAA,IAC/C,QAAQ;AAAA,IAER;AACA,eAAW,KAAK;AAAA,EAClB,GAAG,CAAC,KAAK,CAAC;AAGV,EAAAD,WAAU,MAAM;AACd,aAAS;AAAA,EACX,GAAG,CAAC,QAAQ,CAAC;AAGb,EAAAA,WAAU,MAAM;AACd,UAAM,WAAW,YAAY,MAAM;AACjC,mBAAa,CAAC,SAAS;AACrB,YAAI,QAAQ,GAAG;AACb,mBAAS;AACT,iBAAO;AAAA,QACT;AACA,eAAO,OAAO;AAAA,MAChB,CAAC;AAAA,IACH,GAAG,GAAI;AACP,WAAO,MAAM,cAAc,QAAQ;AAAA,EACrC,GAAG,CAAC,QAAQ,CAAC;AAGb,EAAAA,WAAU,MAAM;AACd,QAAI,CAAC,YAAY;AACf,sBAAgB,IAAI;AACpB,uBAAiB,KAAK;AACtB;AAAA,IACF;AAEA,UAAM,aAAa,MAAM;AAEvB,kBAAY,OAAO,WAAW,IAAI;AAClC,uBAAiB,WAAW,IAAI,EAAE,KAAK,CAAC,SAAS;AAC/C,YAAI,QAAQ,KAAK,OAAO,SAAS,GAAG;AAClC,sBAAY,WAAW,MAAM,KAAK,MAAM;AACxC,0BAAgB,KAAK,MAAM;AAAA,QAC7B;AAAA,MACF,CAAC;AAAA,IACH;AAGA,eAAW;AAGX,uBAAmB,EAAE;AAGrB,UAAM,QAAQ,YAAY,YAAY,GAAK;AAC3C,WAAO,MAAM,cAAc,KAAK;AAAA,EAClC,GAAG,CAAC,UAAU,CAAC;AAGf,EAAAA,WAAU,MAAM;AACd,QAAI,CAAC,WAAY;AACjB,UAAM,QAAQ,YAAY,MAAM;AAC9B,yBAAmB,CAAC,SAAU,OAAO,IAAI,OAAO,IAAI,EAAG;AAAA,IACzD,GAAG,GAAI;AACP,WAAO,MAAM,cAAc,KAAK;AAAA,EAClC,GAAG,CAAC,UAAU,CAAC;AAGf,EAAAE;AAAA,IACED;AAAA,MACE,CAAC,OAAO,QAAQ;AAEd,YAAI,UAAU,OAAO,IAAI,MAAM;AAC7B,sBAAY;AACZ;AAAA,QACF;AAEA,YAAI,YAAY;AAEd,cAAI,IAAI,UAAU,UAAU,OAAO,UAAU,KAAK;AAChD,0BAAc,IAAI;AAAA,UACpB;AACA;AAAA,QACF;AAEA,YAAI,OAAO,WAAW,EAAG;AAEzB,YAAI,IAAI,WAAW,UAAU,KAAK;AAChC,2BAAiB,CAAC,SAAU,OAAO,IAAI,OAAO,IAAI,OAAO,SAAS,CAAE;AAAA,QACtE,WAAW,IAAI,aAAa,UAAU,KAAK;AACzC,2BAAiB,CAAC,SAAU,OAAO,OAAO,SAAS,IAAI,OAAO,IAAI,CAAE;AAAA,QACtE,WAAW,IAAI,QAAQ;AACrB,gBAAM,QAAQ,OAAO,aAAa;AAClC,cAAI,MAAO,eAAc,KAAK;AAAA,QAChC,WAAW,IAAI,UAAU,UAAU,KAAK;AACtC,cAAI,aAAc,cAAa;AAAA,cAC1B,QAAO;AAAA,QACd,WAAW,UAAU,KAAK;AACxB,uBAAa,CAAC;AACd,mBAAS;AAAA,QACX,WAAW,UAAU,KAAK;AACxB,qBAAW,CAAC,MAAM,CAAC,CAAC;AAAA,QACtB;AAAA,MACF;AAAA,MACA,CAAC,QAAQ,eAAe,YAAY,QAAQ,cAAc,UAAU,aAAa,UAAU;AAAA,IAC7F;AAAA,EACF;AAGA,MAAI,YAAY;AACd,QAAI,eAAe;AACjB,aACE,gBAAAE,MAACC,OAAA,EAAI,aAAa,GAChB,0BAAAD,MAACE,QAAA,EAAK,UAAQ,MAAE,8DAAgB,GAClC;AAAA,IAEJ;AACA,WAAO,aAAa,YAAY,MAAM,cAAc,IAAI,GAAG,gBAAgB,QAAW,iBAAiB,WAAW;AAAA,EACpH;AAIA,QAAMC,MAAK,CAAC,MAAc,UAAU,EAAE,UAAU,KAAK,IAAI,EAAE,OAAO,EAAE;AACpE,SACE,gBAAAC,OAACH,OAAA,EAAI,eAAc,UAEjB;AAAA,oBAAAG,OAACH,OAAA,EAAI,cAAc,GAAG,gBAAe,iBACnC;AAAA,sBAAAD,MAACE,QAAA,EAAK,MAAI,MAAE,GAAGC,IAAG,SAAS,GACxB,wDACH;AAAA,MACA,gBAAAC,OAACF,QAAA,EAAK,UAAQ,MACX;AAAA;AAAA,QAAS;AAAA,SACZ;AAAA,MACA,gBAAAF,MAACE,QAAA,EAAK,UAAQ,MACX,oBAAU,mCAAe,KAAK,SAAS,oCAC1C;AAAA,OACF;AAAA,IAGA,gBAAAE,OAACH,OAAA,EACC;AAAA,sBAAAD,MAACC,OAAA,EAAI,OAAO,GAAG;AAAA,MACf,gBAAAD,MAACC,OAAA,EAAI,OAAO,GACV,0BAAAD,MAACE,QAAA,EAAK,UAAQ,MAAC,0BAAE,GACnB;AAAA,MACA,gBAAAF,MAACC,OAAA,EAAI,OAAO,IACV,0BAAAD,MAACE,QAAA,EAAK,UAAQ,MAAC,0BAAE,GACnB;AAAA,MACA,gBAAAF,MAACC,OAAA,EAAI,OAAO,IACV,0BAAAD,MAACE,QAAA,EAAK,UAAQ,MAAC,gCAAG,GACpB;AAAA,MACA,gBAAAF,MAACC,OAAA,EAAI,OAAO,IACV,0BAAAD,MAACE,QAAA,EAAK,UAAQ,MAAC,gCAAG,GACpB;AAAA,MACA,gBAAAF,MAACC,OAAA,EAAI,OAAO,IACV,0BAAAD,MAACE,QAAA,EAAK,UAAQ,MAAC,gCAAG,GACpB;AAAA,MACA,gBAAAF,MAACC,OAAA,EAAI,OAAO,IACV,0BAAAD,MAACE,QAAA,EAAK,UAAQ,MAAC,0BAAE,GACnB;AAAA,MACA,gBAAAF,MAACC,OAAA,EAAI,OAAO,IACV,0BAAAD,MAACE,QAAA,EAAK,UAAQ,MAAC,0BAAE,GACnB;AAAA,MACA,gBAAAF,MAACC,OAAA,EACC,0BAAAD,MAACE,QAAA,EAAK,UAAQ,MAAC,gCAAG,GACpB;AAAA,OACF;AAAA,IAGA,gBAAAF,MAACC,OAAA,EACC,0BAAAD,MAACE,QAAA,EAAK,UAAQ,MAAE,iBAAO,SAAI,OAAO,GAAG,GAAE,GACzC;AAAA,IAGA,gBAAAF,MAACC,OAAA,EAAI,eAAc,UAChB,iBAAO,IAAI,CAAC,OAAO,UAAU;AAC5B,YAAM,aAAa,UAAU;AAC7B,YAAM,OAAO,MAAM,iBAAiB;AACpC,YAAM,QAAQ,OAAO,YAAY;AAEjC,aACE,gBAAAG,OAACH,OAAA,EACC;AAAA,wBAAAD,MAACC,OAAA,EAAI,OAAO,GAAG,YAAY,GACxB,uBACC,gBAAAD,MAACE,QAAA,EAAK,MAAI,MAAE,GAAGC,IAAG,SAAS,GACxB,qBACH,IAEA,gBAAAH,MAACE,QAAA,EAAM,gBAAK,GAEhB;AAAA,QACA,gBAAAF,MAACC,OAAA,EAAI,OAAO,GACV,0BAAAD,MAACE,QAAA,EAAK,MAAI,MAAE,GAAGC,IAAG,aAAa,YAAY,SAAS,GACjD,gBAAM,MACT,GACF;AAAA,QACA,gBAAAH,MAACC,OAAA,EAAI,OAAO,IACV,0BAAAD,MAACE,QAAA,EAAM,GAAGC,IAAG,aAAa,YAAY,SAAS,GAC5C,gBAAM,MACT,GACF;AAAA,QACA,gBAAAH,MAACC,OAAA,EAAI,OAAO,IACV,0BAAAD,MAACE,QAAA,EAAK,MAAI,MAAE,GAAGC,IAAG,KAAK,GACpB,sBAAY,MAAM,KAAK,GAC1B,GACF;AAAA,QACA,gBAAAH,MAACC,OAAA,EAAI,OAAO,IACV,0BAAAG,OAACF,QAAA,EAAM,GAAGC,IAAG,KAAK,GACf;AAAA,iBAAO,MAAM;AAAA,UAAI,MAAM,cAAc,QAAQ,CAAC;AAAA,UAAE;AAAA,WACnD,GACF;AAAA,QACA,gBAAAH,MAACC,OAAA,EAAI,OAAO,IACV,0BAAAG,OAACF,QAAA,EAAM,GAAGC,IAAG,KAAK,GACf;AAAA,iBAAO,MAAM;AAAA,UAAI,MAAM,aAAa,QAAQ,CAAC;AAAA,WAChD,GACF;AAAA,QACA,gBAAAH,MAACC,OAAA,EAAI,OAAO,IACV,0BAAAD,MAACE,QAAA,EAAM,GAAGC,IAAG,SAAS,GACnB,sBAAY,MAAM,IAAI,GACzB,GACF;AAAA,QACA,gBAAAH,MAACC,OAAA,EAAI,OAAO,IACV,0BAAAD,MAACE,QAAA,EAAM,GAAGC,IAAG,SAAS,GACnB,sBAAY,MAAM,GAAG,GACxB,GACF;AAAA,QACA,gBAAAH,MAACC,OAAA,EACC,0BAAAD,MAACE,QAAA,EAAM,GAAGC,IAAG,SAAS,GACnB,uBAAa,MAAM,MAAM,GAC5B,GACF;AAAA,WAjDQ,MAAM,IAkDhB;AAAA,IAEJ,CAAC,GACH;AAAA,IAGA,gBAAAH,MAACC,OAAA,EAAI,WAAW,GACd,0BAAAD,MAACE,QAAA,EAAK,UAAQ,MACX,uIACH,GACF;AAAA,IACA,gBAAAE,OAACH,OAAA,EACC;AAAA,sBAAAD,MAACE,QAAA,EAAK,UAAQ,MAAE,yCAAW,UAAU,sCAAY;AAAA,MACjD,gBAAAF,MAACE,QAAA,EAAM,GAAGC,IAAG,SAAS,GACnB,mBAAS,UAAU,aAAa,GAAG,aAAa,GACnD;AAAA,OACF;AAAA,IAGC,eACC,gBAAAH,MAACC,OAAA,EAAI,WAAW,GACd,0BAAAD,MAACE,QAAA,EAAK,MAAI,MAAE,GAAGC,IAAG,SAAS,GACxB,2EACH,GACF;AAAA,KAEJ;AAEJ;AAMA,SAAS,aACP,OACA,SACA,QACA,YAAY,IACZ,aACA;AACA,QAAM,OAAO,MAAM,iBAAiB;AACpC,QAAM,YAAY,OAAO,YAAY;AACrC,QAAM,QAAQ,OAAO,WAAM;AAG3B,MAAI,aAAuB,CAAC;AAC5B,MAAI,UAAU,OAAO,SAAS,GAAG;AAC/B,UAAM,aAAa,OAAO,WAAW,MAAM,WAAW;AAEtD,UAAM,SAAS,aAAa,QAAQ,EAAE;AACtC,QAAI,MAAM,WAAW,KAAK,QAAQ;AAAA,MAChC,QAAQ;AAAA,MACR,QAAQ,CAAC,UAAU;AAAA,IACrB,CAAC;AAED,UAAM,IACH,WAAW,UAAK,QAAG,EACnB,WAAW,UAAK,QAAG,EACnB,WAAW,UAAK,QAAG,EACnB,WAAW,UAAK,QAAG;AACtB,iBAAa,IAAI,MAAM,IAAI;AAAA,EAC7B;AAEA,SACE,gBAAAC,OAACH,OAAA,EAAI,eAAc,UAAS,aAAa,GAEvC;AAAA,oBAAAG,OAACH,OAAA,EAAI,cAAc,GAAG,gBAAe,iBACnC;AAAA,sBAAAG,OAACH,OAAA,EACC;AAAA,wBAAAG,OAACF,QAAA,EAAK,MAAI,MAAC,OAAM,WACd;AAAA;AAAA,UAAS,MAAM;AAAA,UAAM;AAAA,WACxB;AAAA,QACA,gBAAAF,MAACE,QAAA,EAAK,UAAQ,MACX,gBAAM,MACT;AAAA,QACC,eACC,gBAAAE,OAACF,QAAA,EAAK,UAAQ,MACX;AAAA;AAAA,UAAS;AAAA,WACZ;AAAA,SAEJ;AAAA,MACA,gBAAAF,MAACE,QAAA,EAAK,UAAQ,MACX,aAAG,SAAS,wBACf;AAAA,OACF;AAAA,IAGA,gBAAAE,OAACH,OAAA,EACC;AAAA,sBAAAD,MAACC,OAAA,EAAI,OAAO,IACV,0BAAAD,MAACE,QAAA,EAAK,MAAI,MAAC,OAAM,WAAU,gCAAG,GAChC;AAAA,MACA,gBAAAF,MAACC,OAAA,EACC,0BAAAG,OAACF,QAAA,EAAK,MAAI,MAAC,OAAO,WACf;AAAA;AAAA,QAAM;AAAA,QAAE,YAAY,MAAM,KAAK;AAAA,SAClC,GACF;AAAA,OACF;AAAA,IACA,gBAAAE,OAACH,OAAA,EACC;AAAA,sBAAAD,MAACC,OAAA,EAAI,OAAO,IACV,0BAAAD,MAACE,QAAA,EAAK,OAAM,WAAU,gCAAG,GAC3B;AAAA,MACA,gBAAAF,MAACC,OAAA,EACC,0BAAAG,OAACF,QAAA,EAAK,OAAO,WACV;AAAA,eAAO,MAAM;AAAA,QAAI,MAAM,cAAc,QAAQ,CAAC;AAAA,QAAE;AAAA,QAChD;AAAA,QACA,OAAO,MAAM;AAAA,QAAI,MAAM,aAAa,QAAQ,CAAC;AAAA,SAChD,GACF;AAAA,OACF;AAAA,IAGC,WAAW,SAAS,KACnB,gBAAAF,MAACC,OAAA,EAAI,WAAW,GAAG,eAAc,UAC9B,qBAAW,IAAI,CAAC,MAAM,MACrB,gBAAAD,MAACC,OAAA,EACC,0BAAAD,MAACE,QAAA,EAAK,OAAO,WACV,kBAAQ,KACX,KAHQ,CAIV,CACD,GACH;AAAA,IAIF,gBAAAF,MAACC,OAAA,EAAI,WAAW,GACd,0BAAAD,MAACE,QAAA,EAAK,UAAQ,MACX,gDACH,GACF;AAAA,KACF;AAEJ;;;AChmBA,SAAS,WAAAG,gBAAe;AACxB,SAAS,QAAAC,OAAM,YAAAC,iBAAgB;AAG/B,IAAM,cAAc,oBAAI,IAAI;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAGD,IAAM,cAAc,oBAAI,IAAI;AAAA,EAC1B;AAAA,EAAO;AAAA,EAAQ;AAAA,EAAO;AAAA,EAAQ;AAAA,EAAQ;AAAA,EACtC;AAAA,EAAQ;AAAA,EAAW;AAAA,EACnB;AAAA,EAAQ;AAAA,EAAS;AAAA,EACjB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAAS;AAAA,EACT;AACF,CAAC;AAeD,eAAsB,iBACpB,SACA,KACmB;AACnB,QAAM,aAAa,OAAO;AAE1B,MAAI;AACJ,MAAI;AACF,cAAU,MAAMF,SAAQ,YAAY,EAAE,eAAe,KAAK,CAAC;AAAA,EAC7D,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AAEA,QAAM,QAAkB,CAAC;AACzB,QAAM,OAAiB,CAAC;AAExB,aAAW,SAAS,SAAS;AAC3B,UAAM,OAAO,MAAM;AACnB,QAAI,YAAY,IAAI,IAAI,KAAK,KAAK,WAAW,GAAG,EAAG;AAEnD,QAAI,MAAM,YAAY,GAAG;AACvB,WAAK,KAAK,IAAI;AAAA,IAChB,WAAW,MAAM,OAAO,GAAG;AACzB,YAAM,MAAM,KAAK,MAAM,KAAK,YAAY,GAAG,CAAC,EAAE,YAAY;AAC1D,UAAI,YAAY,IAAI,GAAG,GAAG;AACxB,cAAM,KAAKE,UAAS,SAASD,MAAK,YAAY,IAAI,CAAC,CAAC;AAAA,MACtD;AAAA,IACF;AAAA,EACF;AAGA,QAAM,SAAS,MAAM,QAAQ;AAAA,IAC3B,KAAK,IAAI,CAAC,MAAM,iBAAiB,SAASA,MAAK,YAAY,CAAC,CAAC,CAAC;AAAA,EAChE;AAEA,aAAW,KAAK,QAAQ;AACtB,UAAM,KAAK,GAAG,CAAC;AAAA,EACjB;AAEA,SAAO;AACT;;;AnDYc,gBAAAE,aAAA;AA7Fd,IAAM,cAAc,CAAC,QAAQ,OAAO,SAAS,QAAQ,cAAc,QAAQ,OAAO;AAE3E,SAAS,YAAqB;AACnC,QAAMC,WAAU,IAAI,QAAQ;AAC5B,EAAAA,SAAQ,aAAa;AAErB,EAAAA,SACG,KAAK,SAAS,EACd,YAAY,kFAA2B,EACvC,QAAQ,SAAS,iBAAiB,gCAAO,EACzC,OAAO,aAAa,kDAAU,EAC9B,OAAO,mBAAmB,kDAAU;AAEvC,EAAAA,SAAQ,kBAAkB,MAAM,WAAWA,QAAO;AAElD,EAAAA,SAAQ,KAAK,aAAa,OAAO,aAAa,kBAAkB;AAC9D,UAAM,MAAM,MAAM,qBAAqB,KAAK,WAAW;AACvD,IAAC,cAAqD,aAAa;AAAA,EACrE,CAAC;AAGD,EAAAA,SACG,QAAQ,MAAM,EACd,YAAY,wDAAW,EACvB,OAAO,iBAAkB;AACxB,QAAI,CAAC,QAAQ,MAAM,OAAO;AACxB,cAAQ,MAAM,+JAAiD;AAC/D,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,QAAI,MAAO,KAA4C;AAGvD,QAAI,OAAO,CAAC,UAAU,IAAI,OAAO,SAAS,GAAG;AAC3C,YAAM,MAAM,MAAM,gBAAgB;AAClC,UAAI,CAAC,IAAK,SAAQ,KAAK,CAAC;AAGxB,YAAM,YAAY,MAAM,WAAW,GAAG;AACtC,cAAQ,IAAI,KAAKC,OAAM,MAAM,QAAG,CAAC,qCAAiBA,OAAM,IAAI,SAAS,CAAC;AAAA,CAAI;AAG1E,YAAM,SAAS,MAAM,gBAAgB;AACrC,YAAM,EAAE,GAAG,KAAK,QAAQ,OAAO,OAAO;AAAA,IACxC;AAIA,UAAM,yBAAyB,QAAQ,IAAI,CAAC;AAG9C,UAAM,cAAc,IAAI,YAAY;AAAA,MAClC,aAAa,KAAK,OAAO,eAAe;AAAA,MACxC,kBAAkB,KAAK,OAAO,oBAAoB;AAAA,IACpD,CAAC;AAED,cAAU,KAAK,WAAW;AAAA,EAC5B,CAAC;AAED,iBAAe,UACb,KACA,aACA;AAEA,UAAM,CAAC,kBAAkB,iBAAiB,QAAQ,KAAK,IAAI,MAAM,QAAQ,IAAI;AAAA,MAC3E,mBAAmB;AAAA,MACnB,wBAAwB,QAAQ,IAAI,CAAC;AAAA,MACrC,aAAa,QAAQ,IAAI,CAAC;AAAA,MAC1B,iBAAiB,QAAQ,IAAI,CAAC;AAAA,IAChC,CAAC;AACD,UAAM,aAAa,mBAAmB;AAEtC,UAAM,kBAAkB,KAAK,OAAO,UAAU;AAAA,MAC5C,CAAC,MAAM,EAAE,UAAU,KAAK,OAAO,mBAAmB;AAAA,IACpD;AACA,UAAM,QAAQ,iBAAiB,SAAS;AACxC,UAAM,UAAU;AAAA,MACd,gBAAAF;AAAA,QAAC;AAAA;AAAA,UACC;AAAA,UACA;AAAA,UACA;AAAA,UACA,WAAW,KAAK,OAAO,MAAM,UAAU;AAAA,UACvC,SAAS,KAAK,WAAW;AAAA,UACzB,QAAQ,iBAAiB;AAAA,UACzB,SAAS,iBAAiB,WAAW;AAAA,UACrC;AAAA,UACA;AAAA,UACA,cAAc,MAAM;AAClB,oBAAQ,QAAQ;AAChB,yBAAa,MAAM;AACjB,wBAAU;AACV,oBAAM,QAAQ,UAAU;AACxB,oBAAM,EAAE,QAAQ,IAAIG;AAAA,gBAClB,gBAAAH;AAAA,kBAAC;AAAA;AAAA,oBACC;AAAA,oBACA,UAAU,OAAO,SAAe;AAC9B,8BAAQ;AACR,4BAAM,KAAK,KAAK;AAEhB,gCAAU,KAAK,WAAW;AAAA,oBAC5B;AAAA,oBACA,cAAc,MAAM;AAClB,8BAAQ;AACR,mCAAa,MAAM,UAAU,KAAK,WAAW,CAAC;AAAA,oBAChD;AAAA;AAAA,gBACF;AAAA,gBACA,EAAE,aAAa,MAAM;AAAA,cACvB;AAAA,YACF,CAAC;AAAA,UACH;AAAA,UACA,eAAe,MAAM;AACnB,oBAAQ,QAAQ;AAChB,yBAAa,MAAM;AAEjB,oBAAM,oBAAoB,KAAK,OAAO,OAAO,SAAS,IAAI,CAAC,MAAM,EAAE,IAAI,KAClE,CAAC,YAAY,YAAY,UAAU;AACxC,oBAAM,WAAW;AAAA,gBACf,gBAAAA;AAAA,kBAAC;AAAA;AAAA,oBACC,OAAO;AAAA,oBACP,cAAc,MAAM;AAClB,+BAAS,QAAQ;AACjB,mCAAa,MAAM,UAAU,KAAK,WAAW,CAAC;AAAA,oBAChD;AAAA,oBACA,QAAQ,MAAM,QAAQ,KAAK,CAAC;AAAA;AAAA,gBAC9B;AAAA,cACF;AAAA,YACF,CAAC;AAAA,UACH;AAAA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAGA,EAAAC,SACG,QAAQ,KAAK,EACb,YAAY,4CAAS,EACrB,SAAS,eAAe,0BAAM,EAC9B,OAAO,kBAAkB,4CAAS,EAClC,OAAO,eAAgB,SAAmB;AACzC,YAAQ,IAAI,iEAAyB;AAAA,EACvC,CAAC;AAGH,EAAAA,SACG,QAAQ,OAAO,EACf,YAAY,sCAAQ,EACpB,OAAO,YAAY,kDAAe,EAClC,OAAO,UAAU,yCAAgB,EACjC,OAAO,iBAAkB;AACxB,YAAQ,IAAI,mEAA2B;AAAA,EACzC,CAAC;AAGH,EAAAA,SACG,QAAQ,MAAM,EACd,YAAY,2GAA2B,EACvC,OAAO,iBAAkB;AACxB,YAAQ,IAAI,kEAA0B;AAAA,EACxC,CAAC;AAGH,EAAAA,SACG,QAAQ,YAAY,EACpB,YAAY,yFAA6B,EACzC,SAAS,WAAW,sBAAY,eAAe,EAC/C,OAAO,eAAgB,OAAgB;AACtC,QAAI,CAAC,OAAO;AACV,cAAQ,IAAI,kGAA+D;AAC3E;AAAA,IACF;AACA,QAAI,UAAU,QAAQ;AACpB,cAAQ,IAAI;AAAA;AAAA;AAAA;AAAA,gCAIY,YAAY,KAAK,GAAG,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,wCAKb;AAAA,IAClC,OAAO;AACL,cAAQ,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oCAcgB;AAAA,IAC9B;AAAA,EACF,CAAC;AAGH,EAAAA,SACG,QAAQ,OAAO,EACf,YAAY,wDAAW,EACvB,SAAS,cAAc,wFAA4B,EACnD,OAAO,eAAgB,OAAiB;AACvC,UAAM,MAAO,KAA4C;AAGzD,UAAM,OAAO,QAAQ,IAAI,QAAQ,QAAQ,IAAI,eAAe;AAC5D,UAAM,mBAAmBG,MAAK,MAAM,YAAY,eAAe;AAC/D,QAAI,uBAAuB;AAC3B,QAAI;AACF,YAAM,MAAM,MAAMC,WAAS,kBAAkB,OAAO;AACpD,YAAM,SAAS,KAAK,MAAM,GAAG;AAC7B,YAAM,QAAQ,OAAO;AACrB,6BAAuB,MAAM,QAAQ,OAAO,OAAO,KAAM,MAAO,QAAsB,SAAS;AAAA,IACjG,QAAQ;AAAA,IAER;AAEA,QAAI,CAAC,sBAAsB;AACzB,YAAM,iBAAiB;AAAA,QACrB,EAAE,MAAM,WAAW;AAAA,QACnB,EAAE,MAAM,WAAW;AAAA,QACnB,EAAE,MAAM,WAAW;AAAA,MACrB;AACA,YAAM,YAAY,MAAM,gBAAgB,cAAc;AACtD,cAAQ,IAAI,GAAGH,OAAM,MAAM,QAAG,CAAC,sDAAcA,OAAM,IAAI,SAAS,CAAC,EAAE;AACnE,cAAQ,IAAI,GAAGA,OAAM,IAAI,4GAAuB,CAAC;AAAA,CAAI;AAAA,IACvD;AAGA,UAAM,cAAc,MAAM,gBAAgB;AAC1C,UAAM,WAAW,SAAS,MAAM,SAAS,IACrC,QACA,YAAY,OAAO,OAAO,SAAS,IAAI,CAAC,MAAM,EAAE,IAAI,KACjD,CAAC,YAAY,YAAY,UAAU;AAE1C,UAAM,MAAM;AAAA,MACV,gBAAAF;AAAA,QAAC;AAAA;AAAA,UACC,OAAO;AAAA,UACP,QAAQ,MAAM,QAAQ,KAAK,CAAC;AAAA;AAAA,MAC9B;AAAA,IACF;AAEA,UAAM,IAAI;AAAA,EACZ,CAAC;AAGH,YAAU;AAEV,EAAAC,SACG,QAAQ,MAAM,EACd,YAAY,4CAAS,EACrB,SAAS,UAAU,8GAAoB,EACvC,OAAO,eAAgB,MAAe;AACrC,QAAI,MAAM;AACR,YAAM,OAAO,QAAQ,IAAI;AACzB,UAAI,CAAC,MAAM;AACT,gBAAQ,MAAM,mCAAU,IAAI,yFAA6B;AACzD,gBAAQ,KAAK,CAAC;AAAA,MAChB;AACA,cAAQ,IAAI,6BAAS,KAAK,IAAI,WAAM,KAAK,WAAW;AAAA,CAAI;AACxD,YAAM,KAAK,KAAK;AAAA,IAClB,OAAO;AACL,YAAM,QAAQ,UAAU;AACxB,UAAI,MAAM,WAAW,GAAG;AACtB,gBAAQ,IAAI,4CAAS;AACrB;AAAA,MACF;AAEA,YAAM,eAAe,MAAM,IAAI,QAAqB,CAACK,aAAY;AAC/D,cAAM,EAAE,QAAQ,IAAIH;AAAA,UAClB,gBAAAH;AAAA,YAAC;AAAA;AAAA,cACC;AAAA,cACA,UAAU,CAAC,SAAS;AAClB,wBAAQ;AACR,gBAAAM,SAAQ,IAAI;AAAA,cACd;AAAA,cACA,QAAQ,MAAM;AACZ,wBAAQ;AACR,gBAAAA,SAAQ,IAAI;AAAA,cACd;AAAA;AAAA,UACF;AAAA,UACA,EAAE,aAAa,MAAM;AAAA,QACvB;AAAA,MACF,CAAC;AAED,UAAI,cAAc;AAChB,gBAAQ,IAAI;AAAA,8BAAaJ,OAAM,MAAM,aAAa,IAAI,CAAC;AAAA,CAAI;AAC3D,cAAM,aAAa,KAAK;AAAA,MAC1B;AAAA,IACF;AAAA,EACF,CAAC;AAEH,SAAOD;AACT;;;AoD5TO,IAAM,WAAW;AAAA;AAAA,EAEtB,SAAS;AAAA;AAAA,EAET,eAAe;AAAA;AAAA,EAEf,cAAc;AAAA;AAAA,EAEd,QAAQ;AACV;;;ACGA,IAAI,cAAc;AAClB,IAAI,cAAoD;AAOxD,QAAQ,GAAG,UAAU,MAAM;AACzB;AACA,MAAI,eAAe,GAAG;AACpB,YAAQ,KAAK,SAAS,MAAM;AAAA,EAC9B;AACA,UAAQ,OAAO,MAAM,mEAAgC;AACrD,MAAI,YAAa,cAAa,WAAW;AACzC,gBAAc,WAAW,MAAM;AAC7B,kBAAc;AAAA,EAChB,GAAG,IAAI;AACT,CAAC;AAED,IAAM,UAAU,UAAU;AAE1B,IAAI;AACF,QAAM,QAAQ,WAAW,QAAQ,IAAI;AACvC,SAAS,KAAc;AACrB,QAAM,QAAQ;AAEd,MAAI,MAAM,SAAS,6BAA6B,MAAM,SAAS,qBAAqB;AAClF,YAAQ,KAAK,MAAM,YAAY,SAAS,OAAO;AAAA,EACjD;AAEA,MAAI,OAAO,MAAM,aAAa,UAAU;AACtC,YAAQ,KAAK,MAAM,QAAQ;AAAA,EAC7B;AAEA,UAAQ,MAAM,OAAO,GAAG,CAAC;AACzB,UAAQ,KAAK,SAAS,aAAa;AACrC;","names":["SUPPORTED_MODELS","program","chalk","resolve","createInterface","existsSync","mkdir","readFile","join","chalk","stat","createInterface","resolve","mkdir","join","existsSync","stat","chalk","readFile","readFile","join","Text","jsxs","Box","Text","jsx","Box","Text","useEffect","useState","useCallback","useRef","useState","useEffect","InkSpinner","Box","Text","mkdir","readFile","writeFile","join","join","readFile","mkdir","writeFile","Box","Text","jsx","jsxs","Text","jsx","jsx","jsxs","Box","Text","Box","Text","jsx","jsxs","Box","Text","jsx","jsxs","Box","Text","jsx","jsxs","HIGHLIGHT_COLOR","items","records","realpath","process","resolve","process","patch","writeFile","readFile","relative","readFile","relative","mkdir","readFile","relative","readFile","mkdir","relative","readFile","basename","readFile","basename","readFile","basename","readFile","basename","readFile","basename","readFile","basename","process","isWindows","process","readdir","stat","join","relative","isAbsolute","readdir","join","relative","isAbsolute","stat","readdir","readFile","stat","join","relative","isAbsolute","readdir","join","isAbsolute","stat","readFile","relative","readdir","stat","join","relative","readdir","stat","join","relative","jsx","jsxs","useState","useRef","useEffect","useCallback","DeepSeekProvider","Box","Text","InkSpinner","Box","Text","useInput","useState","useCallback","jsx","jsxs","useState","useInput","useCallback","Box","Text","Box","Text","useInput","render","useState","useEffect","useRef","useCallback","jsx","jsxs","tick","s","resolve","Box","Text","useInput","render","useState","useEffect","useRef","useCallback","jsx","jsxs","CYBER_PALETTE","createInitialState","update","useRef","tick","useState","useEffect","useInput","useCallback","s","jsxs","Box","jsx","Text","resolve","render","createInitialState","chalk","createInitialState","resolve","render","chalk","Box","Text","useInput","useState","useCallback","useEffect","join","jsx","jsxs","join","useState","useEffect","useCallback","useInput","jsx","Box","Text","cp","jsxs","readdir","join","relative","jsx","program","chalk","render","join","readFile","resolve"]}