dskcode 0.1.27 → 0.1.29

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/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/agent/prompts/plan-prompt.hbs","../src/tool/registry.ts","../src/checkpoint/git-checkpoint.ts","../src/session-store/session-store.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 // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\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 // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\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 void startChat(ctx, costTracker);\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 // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\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 // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\r\n const parsed = JSON.parse(raw) as Record<string, unknown>;\r\n // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\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\r\nasync 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 void 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","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, 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 void 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;\r\n }\r\n if (overlay.tools !== undefined) {\r\n result.tools = overlay.tools;\r\n }\r\n if (overlay.plugins !== undefined) {\r\n result.plugins = overlay.plugins;\r\n }\r\n if (overlay.stock !== undefined) {\r\n result.stock = overlay.stock;\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 // eslint-disable-next-line typescript/no-unsafe-type-assertion\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 // eslint-disable-next-line typescript/no-unsafe-type-assertion\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 // eslint-disable-next-line typescript/no-unsafe-type-assertion\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 // eslint-disable-next-line typescript/no-unsafe-type-assertion\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>;\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 // eslint-disable-next-line typescript/no-unsafe-type-assertion\r\n configData = structuredClone(defaultConfig) as unknown as Record<string, unknown>;\r\n }\r\n\r\n // 更新或创建 deepseek provider\r\n // eslint-disable-next-line typescript/no-unsafe-type-assertion\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 // eslint-disable-next-line typescript/no-unsafe-type-assertion\r\n configData = structuredClone(defaultConfig) as unknown as Record<string, unknown>;\r\n }\r\n\r\n // 更新 defaultProvider 的 model\r\n // eslint-disable-next-line typescript/no-unsafe-type-assertion\r\n const providers = (configData.providers as Record<string, unknown>[]) ?? [];\r\n // eslint-disable-next-line typescript/no-unsafe-type-assertion\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 // eslint-disable-next-line typescript/no-unsafe-type-assertion\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 // eslint-disable-next-line oxc/no-this-in-exported-function, @typescript-eslint/no-unsafe-type-assertion\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 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, type MessageCheckpointInfo, type RewindResult } from \"../agent/index.js\";\r\nimport type { SessionMode } 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\nconst PHASE_CONFIG = {\r\n thinking: { icon: \"🧠\", label: \"思考中\", color: \"#ff9800\" },\r\n generating: { icon: \"✨\", label: \"生成中\", color: \"#00ff41\" },\r\n calling_tools: { icon: \"🛠\", label: \"调用工具\", color: \"#f59e0b\" },\r\n executing_tools: { icon: \"⚡\", label: \"执行工具\", color: \"#00ffff\" },\r\n} as const;\r\n\r\n/** 判断工具名是否属于会修改文件系统的类(用于决定 rewind 提示是否出现) */\r\nexport function isFileMutatingTool(name: string): boolean {\r\n return name === \"edit_file\" || name === \"write_file\" || name === \"multi_edit\" || name === \"delete_range\";\r\n}\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\nregisterCommand(\"/plan\", { desc: \"切换为计划模式(Shift+Tab)\", handler: () => ({ kind: \"text\", content: \"输入 /plan 或按 Shift+Tab 切换为计划模式\" }) });\r\nregisterCommand(\"/code\", { desc: \"切换回代码模式(Shift+Tab)\", handler: () => ({ kind: \"text\", content: \"输入 /code 或按 Shift+Tab 切换回代码模式\" }) });\r\nregisterCommand(\"/rewind\", { desc: \"回退到历史检查点(1 = 最新)\", handler: () => ({ kind: \"text\", content: \"请直接输入 /rewind 查看可回退的检查点列表,或 /rewind <序号> 直接回退(1 = 最新,2 = 上一次,依此类推)\" }) });\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 [streamingPhase, setStreamingPhase] = useState<\"thinking\" | \"generating\" | \"calling_tools\" | \"executing_tools\" | null>(null);\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 // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\r\n const [activeModel, setActiveModel] = useState<ModelId>(model as unknown as ModelId);\r\n const [_streamingModel, setStreamingModel] = useState<string | undefined>(undefined);\r\n const [streamError, setStreamError] = useState<string | undefined>(undefined);\r\n\r\n // 会话模式(code / plan)\r\n const [sessionMode, setSessionMode] = useState<SessionMode>(\"code\");\r\n\r\n // 高级设置\r\n const [thinkingEnabled, setThinkingEnabled] = useState(true);\r\n const [thinkingEffort, setThinkingEffort] = useState<\"high\" | \"max\">(\"high\");\r\n const [responseFormat] = 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 // /rewind 回退选择模式\r\n const [rewindSelecting, setRewindSelecting] = useState(false);\r\n const [rewindSelectIndex, setRewindSelectIndex] = useState(0);\r\n const [rewindList, setRewindList] = useState<MessageCheckpointInfo[]>([]);\r\n const [rewinding, setRewinding] = useState(false);\r\n\r\n // /rewind 提示:某轮对话修改了文件后,在原状态 loading 处显示「/rewind 1 可撤回本次修改」。\r\n // 三阶段:idle(不显示)→ pending(流式刚结束,等 2s)→ visible(展示提示)\r\n // 提示一旦出现会一直保留到下次对话开始,不会被用户输入打断。\r\n const [rewindHintPhase, setRewindHintPhase] = useState<\"idle\" | \"pending\" | \"visible\">(\"idle\");\r\n // 本轮是否触发了文件修改类工具调用(edit_file/write_file/multi_edit/delete_range)\r\n const currentRoundModifiedRef = useRef(false);\r\n // 提示计时器句柄\r\n const rewindHintTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\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);\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 // 输入变更时仅重置选择索引;rewind 提示由下一轮对话开始时清掉\r\n useEffect(() => {\r\n setSkillSelectIndex(0);\r\n setFileSelectIndex(0);\r\n }, [input]);\r\n\r\n // 组件卸载时清掉 rewind 提示计时器,避免内存泄露\r\n useEffect(() => {\r\n return () => {\r\n if (rewindHintTimerRef.current) {\r\n clearTimeout(rewindHintTimerRef.current);\r\n rewindHintTimerRef.current = null;\r\n }\r\n };\r\n }, []);\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 // 根据 session.messages 重建 displayMessages,绕开下标对齐问题\r\n function rebuildDisplayFromSession(sess: Session): void {\r\n const next: DisplayMessage[] = [];\r\n for (const m of sess.messages) {\r\n if (m.role === \"user\") {\r\n next.push({ role: \"user\", content: m.content });\r\n } else if (m.role === \"assistant\") {\r\n next.push({\r\n role: \"assistant\",\r\n content: m.content,\r\n assistantDetail: {\r\n content: m.content,\r\n toolCalls: m.toolCalls,\r\n },\r\n });\r\n } else if (m.role === \"tool\") {\r\n next.push({ role: \"tool\", content: m.content });\r\n }\r\n // system 消息不展示\r\n }\r\n setDisplayMessages(next);\r\n setStaticKey((prev) => prev + 1);\r\n }\r\n\r\n const doRewind = useCallback(async (target: MessageCheckpointInfo, displayNumber: number) => {\r\n const session = sessionRef.current;\r\n if (!session) return;\r\n setRewinding(true);\r\n setIsStreaming(true);\r\n setStreamingPhase(\"thinking\");\r\n setStreamingPlaceholder(\"⏪ 正在回退到检查点…\");\r\n setInput(\"\");\r\n try {\r\n const r: RewindResult = await session.rewind(target.index);\r\n if (r.ok) {\r\n rebuildDisplayFromSession(session);\r\n const tail = r.fileRestored ? \",工作区文件已恢复\" : \"(仅对话回退,未恢复文件)\";\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"assistant\", content: `⏪ 已回退到检查点 #${displayNumber}${tail}。` },\r\n ]);\r\n } else {\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"assistant\", content: `⚠ 回退失败:${r.error}` },\r\n ]);\r\n }\r\n } catch (err: unknown) {\r\n const msg = err instanceof Error ? err.message : String(err);\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"assistant\", content: `⚠ 回退异常:${msg}` },\r\n ]);\r\n } finally {\r\n setRewinding(false);\r\n setIsStreaming(false);\r\n setStreamingPhase(null);\r\n setStreamingPlaceholder(\"\");\r\n }\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 // /rewind 选择模式\r\n if (rewindSelecting) {\r\n if (key.upArrow) {\r\n setRewindSelectIndex((prev) => (prev - 1 + rewindList.length) % rewindList.length);\r\n } else if (key.downArrow) {\r\n setRewindSelectIndex((prev) => (prev + 1) % rewindList.length);\r\n } else if (key.return) {\r\n const target = rewindList[rewindSelectIndex];\r\n setRewindSelecting(false);\r\n if (target) {\r\n void doRewind(target, rewindSelectIndex + 1);\r\n }\r\n } else if (key.escape) {\r\n setRewindSelecting(false);\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"assistant\", content: \"已取消回退。\" },\r\n ]);\r\n }\r\n return;\r\n }\r\n\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 // Shift+Tab 切换计划模式 / 代码模式\r\n if (key.shift && key.tab) {\r\n if (!isStreaming) {\r\n const newMode: SessionMode = sessionMode === \"plan\" ? \"code\" : \"plan\";\r\n setSessionMode(newMode);\r\n sessionRef.current?.setMode(newMode);\r\n setToolChoice(undefined);\r\n sessionRef.current?.reset();\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"assistant\", content: newMode === \"plan\"\r\n ? \"📋 已切换为 **计划模式**(Shift+Tab)\\n\\n在此模式下,我只能读取和分析代码,不会执行任何修改。\"\r\n : \"🛠 已切换为 **代码模式**(Shift+Tab)\\n\\n现在可以正常读取和修改代码了。\",\r\n },\r\n ]);\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, sessionMode, setSessionMode, rewindSelecting, rewindSelectIndex, rewindList, doRewind]\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>;\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 // /rewind 命令:回退到历史检查点\r\n if (cmdLower === \"/rewind\" || cmdLower.startsWith(\"/rewind \")) {\r\n if (isStreaming || rewinding) {\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"user\", content: trimmed },\r\n { role: \"assistant\", content: \"⚠ 正在生成或回退中,请稍后再试 /rewind。\" },\r\n ]);\r\n setInput(\"\");\r\n return;\r\n }\r\n if (!sessionRef.current) {\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"user\", content: trimmed },\r\n { role: \"assistant\", content: \"⚠ Session 未就绪,无法回退。\" },\r\n ]);\r\n setInput(\"\");\r\n return;\r\n }\r\n const cps = sessionRef.current.listCheckpoints();\r\n if (cps.length === 0) {\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"user\", content: trimmed },\r\n { role: \"assistant\", content: \"⚠ 没有可回退的检查点。\\n只有在 git 仓库内且发生过对话后才会生成检查点。\" },\r\n ]);\r\n setInput(\"\");\r\n return;\r\n }\r\n // /rewind <N>:1 表示最新(与 git reset HEAD~ 习惯一致),2 表示上一个,依次类推\r\n const parts = trimmed.split(/\\s+/);\r\n if (parts.length >= 2) {\r\n const n = Number(parts[1]);\r\n if (!Number.isInteger(n) || n < 1 || n > cps.length) {\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"user\", content: trimmed },\r\n { role: \"assistant\", content: `⚠ 无效的序号「${parts[1]}」。可用范围 1~${cps.length}(1 表示最新)。` },\r\n ]);\r\n setInput(\"\");\r\n return;\r\n }\r\n // 将用户输入的 1-based \"最新优先\"序号转换为 cps 列表里的索引\r\n const target = cps[cps.length - n]!;\r\n setInput(\"\");\r\n await doRewind(target, n);\r\n return;\r\n }\r\n // 无参数:进入选择模式。逆序展示(最新在第 1 位),默认选中最新\r\n setRewindList([...cps].reverse());\r\n setRewindSelectIndex(0);\r\n setRewindSelecting(true);\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"user\", content: trimmed },\r\n { role: \"assistant\", content: `⏷↑↓ 选择检查点,Enter 确认,Esc 取消(共 ${cps.length} 个可回退位置)` },\r\n ]);\r\n setInput(\"\");\r\n return;\r\n }\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 // /plan 命令:切换为计划模式(只读分析,不能修改代码)\r\n if (cmdLower === \"/plan\") {\r\n if (sessionMode === \"plan\") {\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"user\", content: trimmed },\r\n { role: \"assistant\", content: \"已经在计划模式中。输入 /code 切回代码模式。\" },\r\n ]);\r\n } else {\r\n setSessionMode(\"plan\");\r\n sessionRef.current?.setMode(\"plan\");\r\n // 计划模式下自动启用工具调用,确保模型可以使用读工具\r\n setToolChoice(undefined);\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"user\", content: trimmed },\r\n { role: \"assistant\", content: \"📋 已切换为 **计划模式**\\n\\n在此模式下,我只能读取和分析代码,不会执行任何修改。\\n输入 /code 切回代码模式。\" },\r\n ]);\r\n // 清空会话历史,防止旧模式下产生的工具调用消息干扰新模式\r\n sessionRef.current?.reset();\r\n }\r\n setInput(\"\");\r\n return;\r\n }\r\n\r\n // /code 命令:切换回代码模式\r\n if (cmdLower === \"/code\") {\r\n if (sessionMode === \"code\") {\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"user\", content: trimmed },\r\n { role: \"assistant\", content: \"已经在代码模式中。输入 /plan 切回计划模式。\" },\r\n ]);\r\n } else {\r\n setSessionMode(\"code\");\r\n sessionRef.current?.setMode(\"code\");\r\n // 回到代码模式时重置工具调用策略,确保可以正常使用所有工具\r\n setToolChoice(undefined);\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"user\", content: trimmed },\r\n { role: \"assistant\", content: \"🛠 已切换为 **代码模式**\\n\\n现在可以正常读取和修改代码了。\\n输入 /plan 切换为计划模式(只读分析)。\" },\r\n ]);\r\n // 清空会话历史,防止计划模式下产生的消息干扰代码模式\r\n sessionRef.current?.reset();\r\n }\r\n setInput(\"\");\r\n return;\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 setStreamingPhase(\"thinking\");\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 // 新一轮对话开始:清除上轮可能残留的 rewind 提示\r\n currentRoundModifiedRef.current = false;\r\n setRewindHintPhase(\"idle\");\r\n if (rewindHintTimerRef.current) {\r\n clearTimeout(rewindHintTimerRef.current);\r\n rewindHintTimerRef.current = null;\r\n }\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 setStreamingPhase(\"generating\");\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 setStreamingPhase(\"calling_tools\");\r\n setCurrentToolCalls((prev) => {\r\n const next = [...prev, ...event.calls];\r\n currentToolCallsRef.current = next;\r\n return next;\r\n });\r\n // 标记本轮是否触发了文件修改类工具调用\r\n for (const call of event.calls) {\r\n if (isFileMutatingTool(call.name)) {\r\n currentRoundModifiedRef.current = true;\r\n break;\r\n }\r\n }\r\n break;\r\n\r\n case \"tool_result\":\r\n // 工具执行完成 — 重置流式状态,准备接收模型的新一轮回复\r\n // 这是因为 Agent 循环会在工具执行后再次调用模型\r\n setStreamingPhase(\"executing_tools\");\r\n // 延迟重置为思考中,表示模型正在处理工具结果进行下一轮推理\r\n setTimeout(() => setStreamingPhase(\"thinking\"), 300);\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 // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\r\n const cost = calculateCost(event.usage, event.model as unknown 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 setStreamingPhase(null);\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 // 如果本轮触发了文件修改且无流错误 -> 2s 后展示 rewind 提示\r\n // 提示会一直保留到下次对话开始(流式再次启动)为止\r\n if (currentRoundModifiedRef.current && !finStreamError) {\r\n setRewindHintPhase(\"pending\");\r\n if (rewindHintTimerRef.current) clearTimeout(rewindHintTimerRef.current);\r\n rewindHintTimerRef.current = setTimeout(() => {\r\n setRewindHintPhase((prev) => (prev === \"pending\" ? \"visible\" : prev));\r\n rewindHintTimerRef.current = null;\r\n }, 2000);\r\n }\r\n }\r\n }, [onLaunchGame, onLaunchStock, currentContent, currentToolCalls, skills, skillSelectIndex, getFilteredSkills, thinkingEnabled, thinkingEffort, responseFormat, toolChoice, activeModel, sessionMode]);\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 {/* 模式指示器 */}\r\n {sessionMode === \"plan\" && (\r\n <Text color=\"#ff69b4\" bold>{\" 📋 计划模式\"}</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 {/* 错误信息(流式结束后显示) */}\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 ) : rewindSelecting ? (\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=\"#ff9800\">选择要回退的检查点:</Text>\r\n {rewindList.map((cp, i) => {\r\n const isSelected = i === rewindSelectIndex;\r\n const marker = isSelected ? \" > \" : \" \";\r\n const time = new Date(cp.timestamp).toLocaleTimeString();\r\n const preview = cp.preview || \"(空)\";\r\n const tag = cp.isGitRepo ? \"\" : \" [非 git,仅对话]\";\r\n return (\r\n <Box key={cp.index}>\r\n <Text\r\n color={isSelected ? \"#ff9800\" : undefined}\r\n bold={isSelected}\r\n >\r\n {marker}#{i + 1} {time} `{preview}{tag}`\r\n </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 {/* 流式状态指示器 — 上框上方居中显示 */}\r\n {(hasConversationStarted || sessionMode === \"plan\") && isStreaming && streamingPhase ? (\r\n <Box marginTop={1} justifyContent=\"center\">\r\n <Text bold color={PHASE_CONFIG[streamingPhase].color}>\r\n {PHASE_CONFIG[streamingPhase].icon} {PHASE_CONFIG[streamingPhase].label}{\" \"}\r\n <InkSpinner type=\"dots\" />\r\n </Text>\r\n </Box>\r\n ) : rewindHintPhase === \"visible\" ? (\r\n // 本轮修改了文件时,流式结束后 2s 在原位置展示 /rewind 提示\r\n // 一直保留到下次对话开始,与 /rewind 1(最新检查点)配套\r\n <Box marginTop={1} justifyContent=\"center\">\r\n <Text color=\"#808080\">{\"↩ /rewind 1 可撤回本次修改\"}</Text>\r\n </Box>\r\n ) : null}\r\n <Box marginTop={1}>\r\n {hasConversationStarted || sessionMode === \"plan\" ? (\r\n <Text color={sessionMode === \"plan\" ? \"#ff69b4\" : \"#00ffff\"} dimColor={sessionMode !== \"plan\"}>\r\n <Text>{sessionMode === \"plan\"\r\n ? \"─\".repeat(Math.max(dividerWidth - 48, 1))\r\n : \"─\".repeat(Math.max(dividerWidth - 35, 10))}</Text>\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 {sessionMode === \"plan\" && (\r\n <Text color=\"#ff69b4\" bold>{\" 📋 计划模式\"}</Text>\r\n )}\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);\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 } 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: _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 // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\r\n return JSON.parse(raw) as unknown 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 // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\r\n const parsed = JSON.parse(args) as unknown 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// ```...``` 代码块 → 独立块渲染,diff 内容自动绿/红着色\r\n// | col | col | 表格 → 框线表格,自动对齐\r\n// 流式传输中未闭合的标记自动降级为纯文本,不会闪烁。\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { Box, Text } from \"ink\";\r\nimport type { ReactNode } from \"react\";\r\n\r\n/** 解析后的文本段类型 */\r\ntype SegmentType = \"plain\" | \"code\" | \"bold\" | \"code_block\" | \"table\";\r\n\r\ninterface Segment {\r\n text: string;\r\n type: SegmentType;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 第 0 遍:提取三反引号代码块\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * 在全文中查找 ```...``` 代码块,将其标记为 \"code_block\" 类型。\r\n * 代码块之外的内容保持 \"plain\" 类型,后续交给 bold/code 解析。\r\n *\r\n * 未闭合的三反引号降级为纯文本(流式传输安全)。\r\n */\r\nfunction parseCodeBlocks(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 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 + 3);\r\n\r\n if (closeIdx === -1) {\r\n // 未闭合 → 降级为纯文本\r\n segments.push({ text: text.slice(openIdx), type: \"plain\" });\r\n break;\r\n }\r\n\r\n // 提取代码块内容(含开头的语言标识行)\r\n const codeContent = text.slice(openIdx + 3, closeIdx);\r\n segments.push({ text: codeContent, type: \"code_block\" });\r\n current = closeIdx + 3;\r\n }\r\n\r\n return segments;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 第 0.5 遍:Markdown 表格检测(在 plain 段内)\r\n// ---------------------------------------------------------------------------\r\n\r\n/** 判断一行是不是表格行(以 | 开头并以 | 结尾) */\r\nfunction isTableRow(line: string): boolean {\r\n const t = line.trim();\r\n return /^\\|.+\\|$/.test(t);\r\n}\r\n\r\n/** 判断一行是不是表格分隔行(|:---:|:---:| 等) */\r\nfunction isTableSepRow(line: string): boolean {\r\n const t = line.trim();\r\n // 字符类需包含 | 以匹配多列分隔行,如 |---|---|\r\n return /^\\|[-: |]+\\|$/.test(t) && t.includes(\"-\");\r\n}\r\n\r\n/**\r\n * 在 plain 文本中检测 Markdown 表格块(连续的 `|...|` 行),标记为 \"table\" 类型。\r\n *\r\n * 表格判定条件:\r\n * 1. 开头行是表格行\r\n * 2. 第二行是分隔行(含 `-`)\r\n * 3. 第三行也是表格行\r\n */\r\nfunction parseTables(text: string): Segment[] {\r\n const lines = text.split(\"\\n\");\r\n const result: Segment[] = [];\r\n let plainBuf = \"\";\r\n\r\n function flushPlain() {\r\n if (plainBuf) {\r\n result.push({ text: plainBuf, type: \"plain\" });\r\n plainBuf = \"\";\r\n }\r\n }\r\n\r\n for (let i = 0; i < lines.length; i++) {\r\n const line = lines[i] ?? \"\";\r\n\r\n if (\r\n isTableRow(line) &&\r\n i + 1 < lines.length &&\r\n isTableSepRow(lines[i + 1] ?? \"\") &&\r\n i + 2 < lines.length &&\r\n isTableRow(lines[i + 2] ?? \"\")\r\n ) {\r\n flushPlain();\r\n\r\n // 收集所有连续的表格行(含分隔行)\r\n const tableLineList: string[] = [];\r\n let j = i;\r\n while (j < lines.length && isTableRow(lines[j] ?? \"\")) {\r\n tableLineList.push(lines[j]!);\r\n j++;\r\n }\r\n\r\n result.push({ text: tableLineList.join(\"\\n\"), type: \"table\" });\r\n i = j - 1;\r\n } else {\r\n if (plainBuf) plainBuf += \"\\n\" + line;\r\n else plainBuf = line;\r\n }\r\n }\r\n\r\n flushPlain();\r\n return result;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 第 1 遍:**bold** 解析(与之前相同)\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// 第 2 遍:`code` 解析(处理 plain 和 bold 段内的单反引号对)\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * 在纯文本中查找成对的单反引号,返回 [plain, code, plain, ...] 段序列。\r\n *\r\n * - 只处理 `...` 单反引号对;多反引号序列降级为纯文本。\r\n * - 未闭合的反引号降级为纯文本。\r\n */\r\nfunction parseInlineCode(text: string): Segment[] {\r\n const result: 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 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 // 计算连续反引号的数量\r\n let runLength = 1;\r\n while (\r\n openIdx + runLength < text.length &&\r\n text[openIdx + runLength] === \"`\"\r\n ) {\r\n runLength++;\r\n }\r\n\r\n if (runLength === 1) {\r\n // 单反引号对 → code 段\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 } else {\r\n // 多反引号序列(```、`` 等)保持原样\r\n result.push({\r\n text: text.slice(openIdx, openIdx + runLength),\r\n type: \"plain\",\r\n });\r\n current = openIdx + runLength;\r\n }\r\n }\r\n\r\n return result;\r\n}\r\n\r\n/**\r\n * 在 plain 和 bold 段内按 `` `...` `` 对拆分为 code 段。\r\n *\r\n * - **plain** 段内的 code → 标蓝,剩余部分保持默认色\r\n * - **bold** 段内的 code → 标蓝,剩余部分保持紫色\r\n * - 多反引号序列(```` ```code``` ````、`` ``code`` `` 等)统一降级为纯文本\r\n * - code_block / table 段直接透传\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 === \"code_block\" || seg.type === \"table\") {\r\n result.push(seg);\r\n continue;\r\n }\r\n\r\n // 对 plain 和 bold 的内容都做反引号解析\r\n const parts = parseInlineCode(seg.text);\r\n\r\n for (const part of parts) {\r\n if (part.type === \"code\") {\r\n // 反引号内容始终标蓝\r\n result.push(part);\r\n } else if (seg.type === \"bold\") {\r\n // bold 段内非反引号部分 → 保持紫色\r\n result.push({ text: part.text, type: \"bold\" });\r\n } else {\r\n // plain 段内非反引号部分 → 保持默认色\r\n result.push(part);\r\n }\r\n }\r\n }\r\n\r\n return result;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 代码块渲染\r\n// ---------------------------------------------------------------------------\r\n\r\n/** 添加行绿色 */\r\nconst DIFF_ADD_COLOR = \"#22c55e\";\r\n/** 删除行红色 */\r\nconst DIFF_DEL_COLOR = \"#ef4444\";\r\n/** Hunk 头青色 */\r\nconst DIFF_HUNK_COLOR = \"#00cccc\";\r\n\r\n/**\r\n * 渲染三反引号代码块的内容,对 diff 风格的内容行着色。\r\n *\r\n * 代码块内的第一行若为纯文本(非 `+`/`-`/`@@`/空格开头),\r\n * 视为语言标识(如 \"diff\"、\"python\"),用灰色标出。\r\n */\r\nfunction CodeBlockRenderer({ code }: { code: string }): ReactNode {\r\n const lines = code.split(\"\\n\");\r\n\r\n // 判断第一行是否为语言标识行\r\n const firstLine = lines[0] ?? \"\";\r\n const hasLangLine =\r\n firstLine.trim().length > 0 &&\r\n !firstLine.startsWith(\"+\") &&\r\n !firstLine.startsWith(\"-\") &&\r\n !firstLine.startsWith(\"@@\") &&\r\n !firstLine.startsWith(\" \");\r\n\r\n const lang = hasLangLine ? firstLine : undefined;\r\n const codeLines = hasLangLine ? lines.slice(1) : lines;\r\n\r\n // 空内容\r\n if (codeLines.length === 0) {\r\n return hasLangLine ? (\r\n <Box marginLeft={2}>\r\n <Text color=\"#888888\">┌ {lang}</Text>\r\n </Box>\r\n ) : null;\r\n }\r\n\r\n return (\r\n <Box flexDirection=\"column\" marginLeft={2} marginTop={1}>\r\n {lang && <Text color=\"#888888\">┌ {lang}</Text>}\r\n {codeLines.map((line, i) => {\r\n if (line.startsWith(\"+\")) {\r\n return (\r\n <Text key={i} color={DIFF_ADD_COLOR}>\r\n {line}\r\n </Text>\r\n );\r\n }\r\n if (line.startsWith(\"-\")) {\r\n return (\r\n <Text key={i} color={DIFF_DEL_COLOR}>\r\n {line}\r\n </Text>\r\n );\r\n }\r\n if (line.startsWith(\"@@\")) {\r\n return (\r\n <Text key={i} color={DIFF_HUNK_COLOR}>\r\n {line}\r\n </Text>\r\n );\r\n }\r\n // 上下文行或非 diff 代码行\r\n return <Text key={i}>{line}</Text>;\r\n })}\r\n </Box>\r\n );\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 表格渲染\r\n// ---------------------------------------------------------------------------\r\n\r\n/** 表格最大总宽度 */\r\nconst TABLE_MAX_WIDTH = 90;\r\n/** 表格列最小宽度 */\r\nconst TABLE_MIN_COL_WIDTH = 3;\r\n\r\n/**\r\n * 计算单个字符在终端中的显示宽度。\r\n * CJK 文字、全角符号、emoji 占 2 格;其余占 1 格。\r\n */\r\nfunction charWidth(ch: string): number {\r\n const cp = ch.codePointAt(0);\r\n if (cp === undefined) return 0;\r\n\r\n // CJK 相关范围\r\n if (\r\n (cp >= 0x1100 && cp <= 0x115F) || // Hangul Jamo\r\n cp === 0x2329 || cp === 0x232A ||\r\n (cp >= 0x2E80 && cp <= 0xA4CF) || // CJK Radicals ~ Yi\r\n (cp >= 0xA960 && cp <= 0xA97F) || // Hangul Extended\r\n (cp >= 0xAC00 && cp <= 0xD7AF) || // Hangul Syllables\r\n (cp >= 0xF900 && cp <= 0xFAFF) || // CJK Compat\r\n (cp >= 0xFE10 && cp <= 0xFE1F) || // Vertical forms\r\n (cp >= 0xFF01 && cp <= 0xFF60) || // Fullwidth\r\n (cp >= 0xFFE0 && cp <= 0xFFE6) ||\r\n (cp >= 0x1F000 && cp <= 0x1FFFF) // Emoji / Symbols\r\n ) {\r\n return 2;\r\n }\r\n return 1;\r\n}\r\n\r\n/** 计算字符串在终端中的视觉宽度 */\r\nfunction visualWidth(text: string): number {\r\n let w = 0;\r\n for (const ch of text) {\r\n w += charWidth(ch);\r\n }\r\n return w;\r\n}\r\n\r\n/** 用空格将字符串填充到目标视觉宽度 */\r\nfunction padToWidth(text: string, targetWidth: number, align: \"left\" | \"center\" | \"right\"): string {\r\n const vw = visualWidth(text);\r\n const delta = targetWidth - vw;\r\n if (delta <= 0) return text;\r\n\r\n const leftPad = align === \"right\" ? delta : align === \"center\" ? Math.floor(delta / 2) : 0;\r\n const rightPad = delta - leftPad;\r\n\r\n return \" \".repeat(leftPad) + text + \" \".repeat(rightPad);\r\n}\r\n\r\n/** 解析表格一行为单元格数组 */\r\nfunction parseTableCells(line: string): string[] {\r\n const t = line.trim();\r\n // 去掉首尾的 |,再按 | 切割\r\n const inner = t.slice(1, t.length - 1);\r\n return inner.split(\"|\").map((c) => c.trim());\r\n}\r\n\r\n/** 从分隔行解析每列对齐方式 */\r\nfunction parseAlignments(sepLine: string): Array<\"left\" | \"center\" | \"right\"> {\r\n const cells = parseTableCells(sepLine);\r\n return cells.map((c) => {\r\n const l = c.startsWith(\":\");\r\n const r = c.endsWith(\":\");\r\n if (l && r) return \"center\";\r\n if (r) return \"right\";\r\n return \"left\";\r\n });\r\n}\r\n\r\n/**\r\n * 渲染 Markdown 表格为终端框线表格。\r\n *\r\n * 输出示例:\r\n * ┌──────────┬──────────────────────────┬──────────────────────────┐\r\n * │ 位置 │ 原内容 │ 新内容 │\r\n * ├──────────┼──────────────────────────┼──────────────────────────┤\r\n * │ 第 12 行 │ interface FileDiffs { │ interface Diffs { │\r\n * └──────────┴──────────────────────────┴──────────────────────────┘\r\n */\r\nfunction TableRenderer({ text }: { text: string }): ReactNode {\r\n const rawLines = text.split(\"\\n\").filter((l) => l.trim().length > 0);\r\n if (rawLines.length < 2) return <Text>{text}</Text>;\r\n\r\n // 找到分隔行\r\n let sepIdx = -1;\r\n for (let k = 0; k < rawLines.length; k++) {\r\n if (isTableSepRow(rawLines[k]!)) {\r\n sepIdx = k;\r\n break;\r\n }\r\n }\r\n if (sepIdx === -1) return <Text>{text}</Text>;\r\n\r\n // 表头(分隔行之前的所有行合并为一行,防止多行表头)\r\n const headerText = rawLines.slice(0, sepIdx).join(\"\");\r\n const headerCells = parseTableCells(headerText);\r\n const alignments = parseAlignments(rawLines[sepIdx]!);\r\n const dataRows = rawLines.slice(sepIdx + 1).map(parseTableCells);\r\n\r\n const colCount = headerCells.length;\r\n\r\n // 计算每列最大视觉宽度\r\n const colMaxWidths = headerCells.map((_, ci) => {\r\n let max = visualWidth(headerCells[ci] ?? \"\");\r\n for (const row of dataRows) {\r\n const w = visualWidth(row[ci] ?? \"\");\r\n if (w > max) max = w;\r\n }\r\n return Math.max(max, TABLE_MIN_COL_WIDTH);\r\n });\r\n\r\n // 计算每列最终宽度(留 2 格边距 = 左右各 1 空格)\r\n const margin = 2;\r\n const totalMinWidth = colMaxWidths.reduce((s, w) => s + w + margin, 1); // +1 for leading │\r\n let colWidths: number[];\r\n if (totalMinWidth <= TABLE_MAX_WIDTH) {\r\n colWidths = colMaxWidths;\r\n } else {\r\n // 超出总宽 → 等比例缩减\r\n const available = TABLE_MAX_WIDTH - 1 - margin * colCount;\r\n const sum = colMaxWidths.reduce((s, w) => s + w, 0);\r\n colWidths = colMaxWidths.map((w) => Math.max(TABLE_MIN_COL_WIDTH, Math.floor((w / sum) * available)));\r\n }\r\n\r\n // 框线字符\r\n const H = \"─\";\r\n const makeSep = (l: string, m: string, r: string) =>\r\n l + colWidths.map((w) => H.repeat(w + margin)).join(m) + r;\r\n\r\n const topBorder = makeSep(\"┌\", \"┬\", \"┐\");\r\n const midBorder = makeSep(\"├\", \"┼\", \"┤\");\r\n const botBorder = makeSep(\"└\", \"┴\", \"┘\");\r\n\r\n // 渲染一行单元格\r\n function rowLine(cells: string[], keyBase: string): ReactNode {\r\n return (\r\n <Text key={keyBase}>\r\n {\"│\"}\r\n {cells.map((cell, ci) => (\r\n <Text key={ci}>\r\n {\" \"}\r\n {padToWidth(cell.slice(0, colWidths[ci] ?? TABLE_MIN_COL_WIDTH), colWidths[ci] ?? TABLE_MIN_COL_WIDTH, alignments[ci] ?? \"left\")}\r\n {\" \"}│\r\n </Text>\r\n ))}\r\n </Text>\r\n );\r\n }\r\n\r\n const rows: ReactNode[] = [\r\n <Text key=\"top\" color=\"#888888\">{topBorder}</Text>,\r\n rowLine(headerCells, \"hdr\"),\r\n <Text key=\"mid\" color=\"#888888\">{midBorder}</Text>,\r\n ...dataRows.map((cells, ri) => rowLine(cells, `d${ri}`)),\r\n <Text key=\"bot\" color=\"#888888\">{botBorder}</Text>,\r\n ];\r\n\r\n return (\r\n <Box flexDirection=\"column\" marginTop={1}>\r\n {rows}\r\n </Box>\r\n );\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Emoji 检测 — 跳过图标字符的颜色包裹\r\n// ---------------------------------------------------------------------------\r\n\r\n/** 匹配 emoji 相关 code point 的正则 */\r\nconst EMOJI_CP_RE = /[\\u{1F000}-\\u{1FFFF}\\u{FE00}-\\u{FE0F}\\u{200D}\\u{20E3}\\u{2600}-\\u{27BF}\\u{231A}-\\u{23FF}\\u{2934}\\u{2935}\\u{25AA}-\\u{25FE}\\u{2B50}\\u{2B55}\\u{00A9}\\u{00AE}\\u{2122}\\u{3030}\\u{303D}\\u{3297}\\u{3299}]/u;\r\n\r\n/**\r\n * 检测一个 grapheme cluster 是否属于 emoji 或图标字符。\r\n * 此类字符在 bold 渲染中应跳过颜色包裹,避免终端渲染异常。\r\n */\r\nfunction isEmojiCluster(text: string): boolean {\r\n return EMOJI_CP_RE.test(text);\r\n}\r\n\r\n/**\r\n * 将文本按 grapheme cluster 分割,标记每个 cluster 是否为 emoji。\r\n * 降级策略:若 Intl.Segmenter 不可用,将整个文本视为纯文本。\r\n */\r\nfunction splitEmojiClusters(\r\n text: string,\r\n): Array<{ text: string; isEmoji: boolean }> {\r\n try {\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n const segmenter = new (Intl as any).Segmenter(\"en\", {\r\n granularity: \"grapheme\",\r\n });\r\n const segments = [...segmenter.segment(text)];\r\n return segments.map((s) => ({\r\n text: s.segment,\r\n isEmoji: isEmojiCluster(s.segment),\r\n }));\r\n } catch {\r\n // 降级:整个文本作为一个纯文本块\r\n return [{ text, isEmoji: false }];\r\n }\r\n}\r\n\r\n/**\r\n * 渲染 bold(紫色)文本,对 emoji 字符跳过颜色包裹。\r\n */\r\nfunction renderBoldText(text: string, key: number | string): ReactNode {\r\n const clusters = splitEmojiClusters(text);\r\n\r\n // 不含 emoji — 简单路径(和之前一致)\r\n if (clusters.length === 1 && !clusters[0]!.isEmoji) {\r\n return (\r\n <Text key={key} color={BOLD_COLOR}>\r\n {text}\r\n </Text>\r\n );\r\n }\r\n\r\n // 合并相邻的同类 cluster 减少嵌套\r\n const groups: Array<{ text: string; isEmoji: boolean }> = [];\r\n for (const c of clusters) {\r\n const last = groups[groups.length - 1];\r\n if (last && last.isEmoji === c.isEmoji) {\r\n last.text += c.text;\r\n } else {\r\n groups.push({ text: c.text, isEmoji: c.isEmoji });\r\n }\r\n }\r\n\r\n return (\r\n <Text key={key}>\r\n {groups.map((g, i) =>\r\n g.isEmoji ? (\r\n g.text\r\n ) : (\r\n <Text key={i} color={BOLD_COLOR}>\r\n {g.text}\r\n </Text>\r\n ),\r\n )}\r\n </Text>\r\n );\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 高亮颜色常量\r\n// ---------------------------------------------------------------------------\r\n\r\n/** 行内代码高亮色 — 天蓝 */\r\nconst CODE_COLOR = \"#00BFFF\";\r\n/** 加粗强调色 — 紫色 */\r\nconst BOLD_COLOR = \"#A855F7\";\r\n\r\n// ---------------------------------------------------------------------------\r\n// 主组件\r\n// ---------------------------------------------------------------------------\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 * - ```` ```diff ```` → 独立块,`+` 绿色 / `-` 红色 / `@@` 青色\r\n * - `| ... |` Markdown → 框线表格\r\n */\r\nexport function HighlightedText({ children: text }: HighlightedTextProps): ReactNode {\r\n // 第 0 遍:提取 ```...``` 代码块\r\n const codeBlockSegments = parseCodeBlocks(text);\r\n\r\n // 第 0.5 遍:在非代码块的 plain 段中检测 Markdown 表格\r\n const tableSegments: Segment[] = [];\r\n for (const seg of codeBlockSegments) {\r\n if (seg.type === \"code_block\") {\r\n tableSegments.push(seg);\r\n } else {\r\n tableSegments.push(...parseTables(seg.text));\r\n }\r\n }\r\n\r\n // 第 1 遍:在 inline plain 段内解析 **bold**\r\n const boldSegments: Segment[] = [];\r\n for (const seg of tableSegments) {\r\n if (seg.type === \"code_block\" || seg.type === \"table\") {\r\n boldSegments.push(seg);\r\n } else {\r\n boldSegments.push(...parseBoldPairs(seg.text));\r\n }\r\n }\r\n\r\n // 第 2 遍:在 inline plain 段内解析 `code`\r\n const segments = parseCodePairs(boldSegments);\r\n\r\n // 检查是否包含块级元素\r\n const hasBlock = segments.some(\r\n (s) => s.type === \"code_block\" || s.type === \"table\",\r\n );\r\n\r\n // ---- 无块级元素:纯内联渲染 ----\r\n if (!hasBlock) {\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 renderBoldText(seg.text, i);\r\n }\r\n return seg.text;\r\n })}\r\n </Text>\r\n );\r\n }\r\n\r\n // ---- 有块级元素:分组渲染(inline 组 + 块级渲染) ----\r\n const rendered: ReactNode[] = [];\r\n let inlineGroup: Segment[] = [];\r\n\r\n function flushInline(groupIdx: number) {\r\n if (inlineGroup.length === 0) return;\r\n const isSimpleInline =\r\n inlineGroup.length === 1 && inlineGroup[0]!.type === \"plain\";\r\n if (isSimpleInline) {\r\n rendered.push(\r\n <Text key={groupIdx} wrap=\"wrap\">\r\n {inlineGroup[0]!.text}\r\n </Text>,\r\n );\r\n } else {\r\n rendered.push(\r\n <Text key={groupIdx} wrap=\"wrap\">\r\n {inlineGroup.map((seg, j) => {\r\n if (seg.type === \"code\") {\r\n return (\r\n <Text key={j} color={CODE_COLOR}>\r\n {seg.text}\r\n </Text>\r\n );\r\n }\r\n if (seg.type === \"bold\") {\r\n return renderBoldText(seg.text, j);\r\n }\r\n return seg.text;\r\n })}\r\n </Text>,\r\n );\r\n }\r\n inlineGroup = [];\r\n }\r\n\r\n for (const seg of segments) {\r\n if (seg.type === \"code_block\") {\r\n flushInline(rendered.length);\r\n rendered.push(\r\n <CodeBlockRenderer key={`b${rendered.length}`} code={seg.text} />,\r\n );\r\n } else if (seg.type === \"table\") {\r\n flushInline(rendered.length);\r\n rendered.push(\r\n <TableRenderer key={`t${rendered.length}`} text={seg.text} />,\r\n );\r\n } else {\r\n inlineGroup.push(seg);\r\n }\r\n }\r\n flushInline(rendered.length);\r\n\r\n return <Box flexDirection=\"column\">{rendered}</Box>;\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.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 // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\r\n model: config.model as unknown 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 // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\r\n return tool.execute(args as I, ctx);\r\n },\r\n\r\n initialTitle(args: unknown): string {\r\n // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\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\nimport planPromptTemplate from \"./prompts/plan-prompt.hbs\";\r\n\r\nconst compiledTemplate = Handlebars.compile(systemPromptTemplate);\r\nconst compiledPlanTemplate = Handlebars.compile(planPromptTemplate);\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).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\nfunction buildTemplateVars(opts: SystemPromptOptions) {\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 {\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\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 return compiledTemplate(buildTemplateVars(opts));\r\n}\r\n\r\n/**\r\n * 构建计划模式(Plan Mode)的系统提示词。\r\n *\r\n * 使用 Handlebars 模板 `prompts/plan-prompt.hbs` 渲染,\r\n * 聚焦于代码分析、架构设计和任务规划,不包含写工具信息。\r\n */\r\nexport function buildPlanSystemPrompt(opts: SystemPromptOptions): string {\r\n return compiledPlanTemplate(buildTemplateVars(opts));\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- **不要使用 `#` 符号** — 终端不渲染 Markdown 标题,请用 emoji + 加粗替代(如 `📌 **标题**`)\r\n\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","你是 dskcode,当前工作在**计划模式(Plan Mode)**。\r\n\r\n在此模式下,**你只能读取和分析代码,不能执行任何修改操作**。你的职责是帮助用户理解代码、设计架构、规划任务、评估方案。\r\n\r\n## 核心原则\r\n- 回答使用中文,代码标识符保持英文\r\n- 提供准确、实用、可操作的建议\r\n- 对不确定的内容明确标注,不编造事实\r\n- **绝不执行任何写操作** — 你只能读取和分析\r\n\r\n## 工作流程\r\n1. 收到用户请求\r\n2. 使用读工具(read-file, grep, glob, ls, fetch)探索代码库\r\n3. 根据发现的信息提供结构化的分析或计划\r\n4. 工具返回出错时分析原因并修正后重试\r\n5. 完成后输出最终分析结果\r\n\r\n## 工具限制\r\n**你只能使用读工具**,不能使用任何写工具(write-file, edit-file, multi-edit, delete-range, bash)。\r\n如果用户请求修改代码,请:\r\n- 提供修改建议和方案\r\n- 指出需要修改的文件和位置\r\n- 解释修改的影响\r\n- 但不执行任何修改\r\n\r\n## 计划输出格式\r\n\r\n当用户请求计划/方案时,建议按照以下结构组织输出:\r\n\r\n📋 **概述**\r\n 1-2 句话说明要做什么以及为什么要做\r\n\r\n🔍 **现状分析**\r\n 当前代码的组织结构和关键发现\r\n\r\n⚡ **影响范围**\r\n 哪些文件/模块会受到影响\r\n\r\n📝 **实施步骤**\r\n 1. 第一步(文件路径/变更说明)\r\n 2. 第二步(文件路径/变更说明)\r\n 3. ...\r\n\r\n⚠️ **风险与注意事项**\r\n 可能的风险点、依赖关系、回退策略\r\n\r\n## 回答格式\r\n\r\n终端不支持 Markdown 渲染。回复格式要求:\r\n- 用 emoji 做视觉标记\r\n- 用数字序号或短横线组织列表\r\n- 代码标识符、文件路径用单个反引号 ` 包裹\r\n- 多行代码用缩进 4 个空格表示\r\n- 段落间用空行分隔\r\n- **不要使用 `#` 符号** — 终端不渲染 Markdown 标题,请用 emoji + 加粗替代\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","import { execFile } from \"node:child_process\";\r\nimport { promisify } from \"node:util\";\r\nimport type { Checkpoint } from \"./types.js\";\r\n\r\nconst execFileAsync = promisify(execFile);\r\nconst EXEC_OPTIONS = { windowsHide: true } as const;\r\n\r\nexport async function isGitRepo(cwd: string): Promise<boolean> {\r\n try {\r\n await execFileAsync(\"git\", [\"rev-parse\", \"--is-inside-work-tree\"], { cwd, ...EXEC_OPTIONS });\r\n return true;\r\n } catch { return false; }\r\n}\r\n\r\nasync function hasCommits(cwd: string): Promise<boolean> {\r\n try {\r\n await execFileAsync(\"git\", [\"rev-parse\", \"--verify\", \"HEAD\"], { cwd, ...EXEC_OPTIONS });\r\n return true;\r\n } catch { return false; }\r\n}\r\n\r\nasync function hasWorkingChanges(cwd: string): Promise<boolean> {\r\n const out = await execFileAsync(\"git\", [\"status\", \"--porcelain\"], { cwd, ...EXEC_OPTIONS });\r\n return out.stdout.trim().length > 0;\r\n}\r\n\r\nasync function git(args: string[], cwd: string): Promise<string> {\r\n try {\r\n const { stdout } = await execFileAsync(\"git\", args, { cwd, ...EXEC_OPTIONS });\r\n return stdout;\r\n } catch (err: unknown) {\r\n const msg = err instanceof Error ? err.message : String(err);\r\n throw new Error(`git ${args.join(\" \")} 失败: ${msg}`);\r\n }\r\n}\r\n\r\nasync function listStashShas(cwd: string): Promise<string[]> {\r\n const out = await git([\"stash\", \"list\", \"--format=%H\"], cwd);\r\n return out.split(\"\\n\").map((l) => l.trim()).filter(Boolean);\r\n}\r\n\r\nexport async function createCheckpoint(cwd: string): Promise<Checkpoint> {\r\n const timestamp = Date.now();\r\n const inRepo = await isGitRepo(cwd);\r\n if (!inRepo) return { stashSha: \"\", timestamp, cwd, isGitRepo: false };\r\n if (!(await hasCommits(cwd))) return { stashSha: \"\", timestamp, cwd, isGitRepo: true };\r\n if (!(await hasWorkingChanges(cwd))) return { stashSha: \"\", timestamp, cwd, isGitRepo: true };\r\n\r\n // 为了既登记检查点供 rewind 恢复、又**不**让工作区被 stash \"挪走\"\r\n // (`stash push` 默认会把工作区清空,导致后续对话看不到前面对话产生的修改 —— 这就是 bug),\r\n // 这里使用 `stash push -u` 暂存 + 立即 `stash apply` 还原到工作区。\r\n // stash entry 仍保留在 list 中,rewind 时按 SHA 仍可被精准 drop / apply。\r\n const beforeShas = await listStashShas(cwd);\r\n await git([\"stash\", \"push\", \"-m\", `dskcode-cp-${timestamp}`, \"-u\"], cwd);\r\n\r\n const newShas = await listStashShas(cwd);\r\n if (newShas.length === 0) throw new Error(\"git stash push 未能创建 stash entry\");\r\n const newSha = newShas.find((s) => !beforeShas.includes(s)) ?? newShas[0]!;\r\n\r\n // 立即把 stash 还原到工作区:这样前面对话产生的修改不会因为本次 chat 开始\r\n // 而被\"隐藏\"到 stash 里。stash entry 不会被 `apply` 消耗(仅 `pop`/`drop` 才会),\r\n // 所以后续 rewind 仍能基于 newSha 找回这个快照。\r\n await git([\"stash\", \"apply\", newSha], cwd);\r\n\r\n return { stashSha: newSha, timestamp, cwd, isGitRepo: true };\r\n}\r\n\r\nexport async function restoreCheckpointForce(checkpoint: Checkpoint): Promise<void> {\r\n if (!checkpoint.isGitRepo) throw new Error(\"非 git 仓库,无法恢复文件状态\");\r\n if (!checkpoint.stashSha) throw new Error(\"检查点为空(工作区原本就干净),无需恢复\");\r\n\r\n const { cwd, stashSha } = checkpoint;\r\n const currentShas = await listStashShas(cwd);\r\n if (!currentShas.includes(stashSha)) {\r\n throw new Error(\"检查点已失效(stash entry 已被消费或 GC),无法恢复\");\r\n }\r\n await git([\"checkout\", \"--\", \".\"], cwd);\r\n await git([\"clean\", \"-fd\"], cwd);\r\n await git([\"stash\", \"apply\", stashSha], cwd);\r\n const refIndex = currentShas.indexOf(stashSha);\r\n if (refIndex >= 0) await git([\"stash\", \"drop\", `stash@{${refIndex}}`], cwd);\r\n}\r\n\r\n/**\r\n * 将工作区重置为 HEAD 干净状态(丢弃所有未提交修改与未跟踪文件)。\r\n * 用于「目标检查点 stashSha 为空」的 rewind 场景——那时工作区本就干净,\r\n * 但累积的后续对话修改需要被丢弃。\r\n */\r\nexport async function restoreToClean(cwd: string): Promise<void> {\r\n const inRepo = await isGitRepo(cwd);\r\n if (!inRepo) throw new Error(\"非 git 仓库,无法恢复文件状态\");\r\n if (!(await hasCommits(cwd))) throw new Error(\"仓库无 commit,无法恢复\");\r\n await git([\"checkout\", \"--\", \".\"], cwd);\r\n await git([\"clean\", \"-fd\"], cwd);\r\n}\r\n\r\nexport async function discardCheckpoint(checkpoint: Checkpoint): Promise<void> {\r\n if (!checkpoint.isGitRepo || !checkpoint.stashSha) return;\r\n const { cwd, stashSha } = checkpoint;\r\n const shas = await listStashShas(cwd);\r\n const idx = shas.indexOf(stashSha);\r\n if (idx < 0) return;\r\n try { await git([\"stash\", \"drop\", `stash@{${idx}}`], cwd); } catch { /* ignore */ }\r\n}\r\n","import { mkdir, readFile, writeFile, readdir, unlink, rename } from \"node:fs/promises\";\r\nimport { join } from \"node:path\";\r\nimport { randomUUID } from \"node:crypto\";\r\nimport type { StoredSession, SessionSummary } from \"./types.js\";\r\n\r\nexport function defaultSessionsDir(): string {\r\n const home = process.env.HOME ?? process.env.USERPROFILE ?? \"~\";\r\n return join(home, \".dskcode\", \"sessions\");\r\n}\r\n\r\nexport class SessionStore {\r\n readonly #dir: string;\r\n constructor(dir?: string) { this.#dir = dir ?? defaultSessionsDir(); }\r\n get dir(): string { return this.#dir; }\r\n\r\n async save(session: StoredSession): Promise<void> {\r\n await mkdir(this.#dir, { recursive: true });\r\n const finalPath = join(this.#dir, `${session.id}.json`);\r\n const tmpPath = join(this.#dir, `.${session.id}.json.tmp`);\r\n await writeFile(tmpPath, JSON.stringify(session, null, 2), \"utf-8\");\r\n await rename(tmpPath, finalPath);\r\n }\r\n\r\n async load(id: string): Promise<StoredSession | null> {\r\n const path = join(this.#dir, `${id}.json`);\r\n try {\r\n const content = await readFile(path, \"utf-8\");\r\n return JSON.parse(content) as StoredSession;\r\n } catch (err: unknown) {\r\n if (isENOENT(err)) return null;\r\n throw err;\r\n }\r\n }\r\n\r\n async list(): Promise<SessionSummary[]> {\r\n let files: string[];\r\n try { files = await readdir(this.#dir); }\r\n catch (err: unknown) { if (isENOENT(err)) return []; throw err; }\r\n\r\n const results: SessionSummary[] = [];\r\n for (const file of files) {\r\n if (!file.endsWith(\".json\") || file.startsWith(\".\")) continue;\r\n try {\r\n const content = await readFile(join(this.#dir, file), \"utf-8\");\r\n const s = JSON.parse(content) as StoredSession;\r\n results.push({\r\n id: s.id, title: s.title || \"(无标题)\", updatedAt: s.updatedAt,\r\n cwd: s.cwd, messageCount: s.messages?.length ?? 0,\r\n });\r\n } catch { /* skip corrupted */ }\r\n }\r\n return results.sort((a, b) => b.updatedAt - a.updatedAt);\r\n }\r\n\r\n async delete(id: string): Promise<void> {\r\n const path = join(this.#dir, `${id}.json`);\r\n try { await unlink(path); }\r\n catch (err: unknown) { if (!isENOENT(err)) throw err; }\r\n }\r\n\r\n async exists(id: string): Promise<boolean> { return (await this.load(id)) !== null; }\r\n static newId(): string { return randomUUID(); }\r\n}\r\n\r\nfunction isENOENT(err: unknown): boolean {\r\n return typeof err === \"object\" && err !== null && \"code\" in err && (err as { code: unknown }).code === \"ENOENT\";\r\n}\r\n","// ---------------------------------------------------------------------------\r\n// Agent 会话 — 消息编排、流式 LLM 调用、工具执行循环、成本追踪、检查点、持久化\r\n// ---------------------------------------------------------------------------\r\n\r\nimport type {\r\n ChatMessage,\r\n ChatOptions,\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, SessionMode, SystemPromptOptions } from \"./types.js\";\r\nimport { buildSystemPrompt, buildPlanSystemPrompt } 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\nimport {\r\n createCheckpoint,\r\n restoreCheckpointForce,\r\n restoreToClean,\r\n discardCheckpoint,\r\n type Checkpoint,\r\n} from \"../checkpoint/index.js\";\r\nimport { SessionStore, type StoredSession } from \"../session-store/index.js\";\r\n\r\n/** Session 构造选项 */\r\nexport interface SessionOptions {\r\n cwd?: string;\r\n maxToolRounds?: number;\r\n reservedForOutput?: number;\r\n preserveRecentRounds?: number;\r\n projectContext?: string;\r\n gate?: Gate;\r\n writeRoots?: string[];\r\n /**\r\n * 会话 ID。传入则复用,不传则生成新 UUID。\r\n * 使用 Session.resume() 恢复会话时必须传。\r\n */\r\n sessionId?: string;\r\n /**\r\n * 会话存储实例。不传则使用默认 ~/.dskcode/sessions/。\r\n * 传 false 禁用持久化(用于测试和不需要保存的场景)。\r\n */\r\n store?: SessionStore | false;\r\n /** 是否启用 checkpoint(/rewind 需要),默认 true */\r\n enableCheckpoint?: boolean;\r\n}\r\n\r\n/** 消息检查点信息(对外暴露) */\r\nexport interface MessageCheckpointInfo {\r\n index: number;\r\n preview: string;\r\n timestamp: number;\r\n isGitRepo: boolean;\r\n}\r\n\r\n/** rewind 操作结果 */\r\nexport type RewindResult =\r\n | { ok: true; fileRestored: boolean }\r\n | { ok: false; error: string };\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[]; enableCheckpoint: boolean };\r\n readonly #abortController = new AbortController();\r\n\r\n readonly #sessionId: string;\r\n readonly #store: SessionStore | null;\r\n #createdAt: number;\r\n #persistTimer: NodeJS.Timeout | null = null;\r\n\r\n #checkpoints = new Map<number, Checkpoint>();\r\n #stormRecords: ToolCallRecord[] = [];\r\n #mode: SessionMode = \"code\";\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 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 enableCheckpoint: options?.enableCheckpoint ?? true,\r\n };\r\n this.#sessionId = options?.sessionId ?? SessionStore.newId();\r\n this.#store = options?.store === false ? null : (options?.store ?? new SessionStore());\r\n this.#createdAt = Date.now();\r\n }\r\n\r\n get messages(): readonly ChatMessage[] { return this.#messages; }\r\n get accumulatedCost(): number { return this.#costTracker.sessionTotalCost; }\r\n get costTracker(): CostTracker { return this.#costTracker; }\r\n get model(): string { return this.#provider.model(); }\r\n get toolRegistry(): ToolRegistry { return this.#toolRegistry; }\r\n get mode(): SessionMode { return this.#mode; }\r\n get id(): string { return this.#sessionId; }\r\n get store(): SessionStore | null { return this.#store; }\r\n get createdAt(): number { return this.#createdAt; }\r\n\r\n setMode(mode: SessionMode): SessionMode { this.#mode = mode; return this.#mode; }\r\n\r\n async *chat(userInput: string, opts?: ChatOptions): AsyncGenerator<AgentEvent> {\r\n this.#messages.push({ role: \"user\", content: userInput });\r\n const userMsgIndex = this.#messages.length - 1;\r\n if (this.#options.enableCheckpoint) {\r\n try {\r\n const checkpoint = await createCheckpoint(this.#options.cwd);\r\n this.#checkpoints.set(userMsgIndex, checkpoint);\r\n } catch { /* swallow */ }\r\n }\r\n\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 const systemPrompt = this.#buildSystemPrompt();\r\n const [trimmed] = trimMessages(\r\n [...this.#messages],\r\n {\r\n model: this.#provider.model() as unknown 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 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 thinkingAllowed: opts?.thinkingAllowed,\r\n thinkingEffort: opts?.thinkingEffort,\r\n responseFormat: opts?.responseFormat,\r\n toolChoice: opts?.toolChoice,\r\n });\r\n\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 if (chunk.toolCalls && chunk.toolCalls.length > 0) lastToolCalls = chunk.toolCalls;\r\n if (chunk.usage) lastUsage = chunk.usage;\r\n if (chunk.finishReason) _lastFinishReason = chunk.finishReason;\r\n }\r\n\r\n if (lastUsage) {\r\n const modelId = this.#provider.model() as unknown as ModelId;\r\n this.#costTracker.record(lastUsage, modelId);\r\n yield { type: \"usage\", usage: lastUsage, model: modelId };\r\n }\r\n\r\n const assistantMsg: ChatMessage = { role: \"assistant\", content: accumulatedText };\r\n if (lastToolCalls && lastToolCalls.length > 0) assistantMsg.toolCalls = lastToolCalls;\r\n this.#messages.push(assistantMsg);\r\n\r\n if (lastToolCalls && lastToolCalls.length > 0) {\r\n yield { type: \"tool_calls\", calls: lastToolCalls };\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 assistantMsg.toolCalls = undefined;\r\n assistantMsg.content += stormMsg;\r\n this.#stormRecords = [];\r\n toolRounds++;\r\n continue;\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 let toolContent = item.result.data;\r\n if (item.result.diff && item.result.diff.patch) toolContent += `\\n\\n${item.result.diff.patch}`;\r\n this.#messages.push({\r\n role: \"tool\", content: toolContent,\r\n toolCallId: item.callId, name: item.name,\r\n });\r\n }\r\n\r\n toolRounds++;\r\n continue;\r\n }\r\n\r\n break;\r\n }\r\n } catch (err: unknown) {\r\n if (err instanceof DOMException && err.name === \"AbortError\") return;\r\n if (err instanceof Error && err.name === \"AbortError\") return;\r\n yield { type: \"error\", error: err instanceof Error ? err : new Error(String(err)) };\r\n return;\r\n }\r\n\r\n const elapsed = Date.now() - startTime;\r\n void this.#persist();\r\n yield { type: \"done\", elapsed };\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 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 const MAX_PARALLEL = 8;\r\n const items: Array<{ name: string; callId: string; result: ToolResult }> = [];\r\n const records: ToolCallRecord[] = [];\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 return { items, records };\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 return { items, records };\r\n }\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 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 let toolArgs: unknown;\r\n try { toolArgs = tc.arguments ? JSON.parse(tc.arguments) : {}; } catch { toolArgs = {}; }\r\n\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 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 { await maybePreview(toolArgs, ctx); } catch { /* ignore */ }\r\n }\r\n }\r\n\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: { name: toolName, success: result.success, error: result.error, timestamp },\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 #checkStormBreak(currentCalls: ProviderToolCall[]): boolean {\r\n if (this.#stormRecords.length < 3) return false;\r\n const recentErrors = this.#stormRecords.slice(-3);\r\n if (recentErrors.length < 3) return false;\r\n const first = recentErrors[0]!;\r\n const allSame = recentErrors.every((r) => r.name === first.name && r.error === first.error && !r.success);\r\n if (!allSame) return false;\r\n return currentCalls.some((tc) => tc.name === first.name);\r\n }\r\n\r\n abort(): void {\r\n this.#abortController.abort();\r\n if (this.#persistTimer) { clearTimeout(this.#persistTimer); this.#persistTimer = null; }\r\n }\r\n\r\n reset(): void {\r\n this.#messages.length = 0;\r\n this.#costTracker.resetSession();\r\n this.#stormRecords = [];\r\n this.#checkpoints.clear();\r\n }\r\n\r\n // -------------------------------------------------------------------------\r\n // 持久化与恢复\r\n // -------------------------------------------------------------------------\r\n\r\n async persistNow(): Promise<void> {\r\n if (this.#persistTimer) { clearTimeout(this.#persistTimer); this.#persistTimer = null; }\r\n await this.#doPersist();\r\n }\r\n\r\n #persist(): void {\r\n if (!this.#store) return;\r\n if (this.#persistTimer) { this.#persistTimer.refresh(); return; }\r\n this.#persistTimer = setTimeout(() => {\r\n this.#persistTimer = null;\r\n void this.#doPersist();\r\n }, 500);\r\n this.#persistTimer.unref();\r\n }\r\n\r\n async #doPersist(): Promise<void> {\r\n if (!this.#store) return;\r\n const stored: StoredSession = {\r\n id: this.#sessionId,\r\n title: this.#deriveTitle(),\r\n createdAt: this.#createdAt,\r\n updatedAt: Date.now(),\r\n cwd: this.#options.cwd,\r\n model: this.#provider.model(),\r\n messages: this.#serializeMessages(),\r\n totalCost: this.#costTracker.sessionTotalCost,\r\n };\r\n try { await this.#store.save(stored); }\r\n catch (err) { console.error(\"[Session] 持久化失败:\", err); }\r\n }\r\n\r\n #deriveTitle(): string {\r\n for (const m of this.#messages) {\r\n if (m.role === \"user\" && m.content.trim()) return m.content.trim().slice(0, 40);\r\n }\r\n return \"新会话\";\r\n }\r\n\r\n #serializeMessages(): StoredSession[\"messages\"] {\r\n return this.#messages.map((msg, idx) => {\r\n const checkpoint = this.#checkpoints.get(idx);\r\n if (msg.role === \"user\" && checkpoint) return { ...msg, checkpoint };\r\n return { ...msg };\r\n });\r\n }\r\n\r\n static async resume(\r\n id: string,\r\n provider: Provider,\r\n tools: AnyAgentTool[] | ToolRegistry = [],\r\n costTracker?: CostTracker,\r\n options?: SessionOptions,\r\n ): Promise<Session> {\r\n const store = options?.store === false ? null : (options?.store ?? new SessionStore());\r\n if (!store) throw new Error(\"resume 需要启用持久化(options.store 不能为 false)\");\r\n const stored = await store.load(id);\r\n if (!stored) throw new Error(`会话 ${id} 不存在`);\r\n\r\n const session = new Session(provider, tools, costTracker, { ...options, sessionId: id, store });\r\n for (const m of stored.messages) {\r\n session.#messages.push({\r\n role: m.role, content: m.content,\r\n toolCallId: m.toolCallId, name: m.name, toolCalls: m.toolCalls,\r\n });\r\n }\r\n for (let i = 0; i < stored.messages.length; i++) {\r\n const cp = stored.messages[i]?.checkpoint;\r\n if (cp) session.#checkpoints.set(i, cp);\r\n }\r\n session.#createdAt = stored.createdAt;\r\n return session;\r\n }\r\n\r\n // -------------------------------------------------------------------------\r\n // 检查点与 Rewind\r\n // -------------------------------------------------------------------------\r\n\r\n listCheckpoints(): MessageCheckpointInfo[] {\r\n const result: MessageCheckpointInfo[] = [];\r\n for (const [index, checkpoint] of this.#checkpoints) {\r\n const msg = this.#messages[index];\r\n if (!msg || msg.role !== \"user\") continue;\r\n result.push({ index, preview: msg.content.slice(0, 80), timestamp: checkpoint.timestamp, isGitRepo: checkpoint.isGitRepo });\r\n }\r\n return result.sort((a, b) => a.index - b.index);\r\n }\r\n\r\n async rewind(targetIndex: number): Promise<RewindResult> {\r\n if (targetIndex < 0 || targetIndex >= this.#messages.length) {\r\n return { ok: false, error: `无效的消息索引 ${targetIndex}` };\r\n }\r\n const target = this.#messages[targetIndex];\r\n if (!target || target.role !== \"user\") {\r\n return { ok: false, error: `索引 ${targetIndex} 不是 user 消息` };\r\n }\r\n const checkpoint = this.#checkpoints.get(targetIndex);\r\n if (!checkpoint) return { ok: false, error: \"该消息没有检查点\" };\r\n\r\n this.#messages.length = targetIndex + 1;\r\n const toDiscard: Checkpoint[] = [];\r\n for (const [idx, cp] of this.#checkpoints) {\r\n if (idx > targetIndex) { toDiscard.push(cp); this.#checkpoints.delete(idx); }\r\n }\r\n\r\n let fileRestored = false;\r\n if (checkpoint.isGitRepo) {\r\n try {\r\n if (checkpoint.stashSha) {\r\n // 目标检查点有 stash 快照——恢复该快照\r\n await restoreCheckpointForce(checkpoint);\r\n } else {\r\n // 目标检查点 stashSha 为空,代表「那一刻工作区就是 HEAD 干净状态」。\r\n // 但后续对话产生的修改可能还积累在工作区,需要丢弃才能真正回退到那一刻。\r\n await restoreToClean(this.#options.cwd);\r\n }\r\n fileRestored = true;\r\n } catch (err) {\r\n const msg = err instanceof Error ? err.message : String(err);\r\n return { ok: false, error: `对话已截断但文件恢复失败:${msg}` };\r\n }\r\n }\r\n\r\n // restoreCheckpointForce 内部已 drop 了该 checkpoints 的 stash entry,\r\n // 所以只需从 Map 中移除即可,无需再 discardCheckpoint。\r\n this.#checkpoints.delete(targetIndex);\r\n for (const cp of toDiscard) { void discardCheckpoint(cp); }\r\n this.#persist();\r\n return { ok: true, fileRestored };\r\n }\r\n\r\n hasCheckpoints(): boolean { return this.listCheckpoints().length > 0; }\r\n\r\n async delete(): Promise<void> {\r\n if (this.#store) await this.#store.delete(this.#sessionId);\r\n for (const cp of this.#checkpoints.values()) { void discardCheckpoint(cp); }\r\n this.#checkpoints.clear();\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, description: t.description,\r\n parameters: t.parameters as unknown as Record<string, unknown>,\r\n }));\r\n const opts: SystemPromptOptions = {\r\n model: this.#provider.model(), 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 if (this.#mode === \"plan\") return buildPlanSystemPrompt(opts);\r\n return buildSystemPrompt(opts);\r\n }\r\n\r\n #buildToolDefinitions(): ToolDefinition[] {\r\n const tools = this.#mode === \"plan\" ? this.#toolRegistry.listReadTools() : this.#toolRegistry.list();\r\n return tools.map((t) => ({\r\n type: \"function\" as const,\r\n function: { name: t.name, description: t.description, parameters: t.parameters as unknown as Record<string, unknown> },\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 // eslint-disable-next-line unicorn/no-new-array, @typescript-eslint/no-unsafe-type-assertion\r\n const V: number[] = new Array(2 * maxD + 2).fill(-1) as number[];\r\n // k 的偏移量,使索引非负\r\n const offset = maxD;\r\n\r\n V[offset + 1] = 0;\r\n\r\n // 正向搜索:从 d=0 开始,逐层扩展\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 = trace[d - 1]!;\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]! });\r\n }\r\n\r\n // 第二步:删除行\r\n if (x > prevX) {\r\n x--;\r\n result.unshift({ op: \"remove\", line: oldLines[x]! });\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]! });\r\n }\r\n\r\n // 第二步:添加行\r\n if (y > prevY) {\r\n y--;\r\n result.unshift({ op: \"add\", line: newLines[y]! });\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]! });\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]!;\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]!]];\r\n\r\n for (let idx = 1; idx < changeIndices.length; idx++) {\r\n const lastGroup = groups[groups.length - 1]!;\r\n const prevIdx = lastGroup[lastGroup.length - 1]!;\r\n const currIdx = changeIndices[idx]!;\r\n\r\n let gap = 0;\r\n for (let k = prevIdx + 1; k < currIdx; k++) {\r\n const line = diffLines[k]!;\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]!;\r\n const lastChangeIdx = group[group.length - 1]!;\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]!;\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 = 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] === 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]! };\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 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, 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, 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].toSorted((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 // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\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]!;\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]}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\n// const 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)]!; }\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]!;\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)]!;\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]!;\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]!;\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 } 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 // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\r\n row += cellColor(char, cellType as unknown as 'snake-head' | 'snake-body' | 'normal-food' | 'special-food' | 'empty');\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, useMemo } 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 // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\r\n const json = (await resp.json()) as unknown 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).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 // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\r\n const qt = stockData.qt?.[qtKey] as unknown 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 // o 键排序:默认 → 降序 → 升序 → 默认\r\n type SortOrder = \"default\" | \"asc\" | \"desc\";\r\n const [sortOrder, setSortOrder] = useState<SortOrder>(\"default\");\r\n\r\n const sortedStocks = useMemo(() => {\r\n if (sortOrder === \"default\") return stocks;\r\n return [...stocks].toSorted((a, b) =>\r\n sortOrder === \"desc\"\r\n ? b.changePercent - a.changePercent\r\n : a.changePercent - b.changePercent,\r\n );\r\n }, [stocks, sortOrder]);\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 void 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 void 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 const loadDetail = () => {\r\n // 清除缓存,强制走网络\r\n minuteCache.delete(detailView.code);\r\n void 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 }\r\n\r\n setDetailPrices(null);\r\n setDetailLoading(false);\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 void loadData();\r\n } else if (input === \"o\") {\r\n setSortOrder((prev) => (prev === \"default\" ? \"desc\" : prev === \"desc\" ? \"asc\" : \"default\"));\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>\r\n 涨跌幅{sortOrder === \"desc\" ? \" ▼\" : sortOrder === \"asc\" ? \" ▲\" : \"\"}\r\n </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 {sortedStocks.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 手动刷新 o 排序 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\n// eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents\r\nlet sigintTimer: NodeJS.Timeout | 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 // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\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":"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","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","Box","Text","jsx","jsxs","cp","jsx","jsxs","Box","Text","Box","Text","jsx","jsxs","Box","Text","jsx","jsxs","Box","Text","jsx","jsxs","HIGHLIGHT_COLOR","mkdir","readFile","writeFile","readdir","join","items","records","cp","realpath","process","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","cp","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","tick","s","resolve","createInitialState","chalk","createInitialState","resolve","render","chalk","Box","Text","useInput","useState","useCallback","useEffect","useMemo","join","jsx","jsxs","join","useState","useMemo","useEffect","useCallback","useInput","jsx","Box","Text","cp","jsxs","readdir","join","relative","jsx","program","chalk","join","readFile","resolve","render"]}
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/agent/prompts/plan-prompt.hbs","../src/tool/registry.ts","../src/checkpoint/git-checkpoint.ts","../src/session-store/session-store.ts","../src/logger/logger.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 // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\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 // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\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 void startChat(ctx, costTracker);\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 // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\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 // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\r\n const parsed = JSON.parse(raw) as Record<string, unknown>;\r\n // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\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\r\nasync 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 void 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","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, 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 void 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;\r\n }\r\n if (overlay.tools !== undefined) {\r\n result.tools = overlay.tools;\r\n }\r\n if (overlay.plugins !== undefined) {\r\n result.plugins = overlay.plugins;\r\n }\r\n if (overlay.stock !== undefined) {\r\n result.stock = overlay.stock;\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 // eslint-disable-next-line typescript/no-unsafe-type-assertion\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 // eslint-disable-next-line typescript/no-unsafe-type-assertion\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 // eslint-disable-next-line typescript/no-unsafe-type-assertion\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 // eslint-disable-next-line typescript/no-unsafe-type-assertion\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>;\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 // eslint-disable-next-line typescript/no-unsafe-type-assertion\r\n configData = structuredClone(defaultConfig) as unknown as Record<string, unknown>;\r\n }\r\n\r\n // 更新或创建 deepseek provider\r\n // eslint-disable-next-line typescript/no-unsafe-type-assertion\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 // eslint-disable-next-line typescript/no-unsafe-type-assertion\r\n configData = structuredClone(defaultConfig) as unknown as Record<string, unknown>;\r\n }\r\n\r\n // 更新 defaultProvider 的 model\r\n // eslint-disable-next-line typescript/no-unsafe-type-assertion\r\n const providers = (configData.providers as Record<string, unknown>[]) ?? [];\r\n // eslint-disable-next-line typescript/no-unsafe-type-assertion\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 // eslint-disable-next-line typescript/no-unsafe-type-assertion\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 // eslint-disable-next-line oxc/no-this-in-exported-function, @typescript-eslint/no-unsafe-type-assertion\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 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, type MessageCheckpointInfo, type RewindResult } from \"../agent/index.js\";\r\nimport type { SessionMode } 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\nconst PHASE_CONFIG = {\r\n thinking: { icon: \"🧠\", label: \"思考中\", color: \"#ff9800\" },\r\n generating: { icon: \"✨\", label: \"生成中\", color: \"#00ff41\" },\r\n calling_tools: { icon: \"🛠\", label: \"调用工具\", color: \"#f59e0b\" },\r\n executing_tools: { icon: \"⚡\", label: \"执行工具\", color: \"#00ffff\" },\r\n} as const;\r\n\r\n/** 判断工具名是否属于会修改文件系统的类(用于决定 rewind 提示是否出现) */\r\nexport function isFileMutatingTool(name: string): boolean {\r\n return name === \"edit_file\" || name === \"write_file\" || name === \"multi_edit\" || name === \"delete_range\";\r\n}\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\nregisterCommand(\"/plan\", { desc: \"切换为计划模式(Shift+Tab)\", handler: () => ({ kind: \"text\", content: \"输入 /plan 或按 Shift+Tab 切换为计划模式\" }) });\r\nregisterCommand(\"/code\", { desc: \"切换回代码模式(Shift+Tab)\", handler: () => ({ kind: \"text\", content: \"输入 /code 或按 Shift+Tab 切换回代码模式\" }) });\r\nregisterCommand(\"/rewind\", { desc: \"回退到历史检查点(1 = 最新)\", handler: () => ({ kind: \"text\", content: \"请直接输入 /rewind 查看可回退的检查点列表,或 /rewind <序号> 直接回退(1 = 最新,2 = 上一次,依此类推)\" }) });\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 [streamingPhase, setStreamingPhase] = useState<\"thinking\" | \"generating\" | \"calling_tools\" | \"executing_tools\" | null>(null);\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 // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\r\n const [activeModel, setActiveModel] = useState<ModelId>(model as unknown as ModelId);\r\n const [_streamingModel, setStreamingModel] = useState<string | undefined>(undefined);\r\n const [streamError, setStreamError] = useState<string | undefined>(undefined);\r\n\r\n // 会话模式(code / plan)\r\n const [sessionMode, setSessionMode] = useState<SessionMode>(\"code\");\r\n\r\n // 高级设置\r\n const [thinkingEnabled, setThinkingEnabled] = useState(true);\r\n const [thinkingEffort, setThinkingEffort] = useState<\"high\" | \"max\">(\"high\");\r\n const [responseFormat] = 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 // /rewind 回退选择模式\r\n const [rewindSelecting, setRewindSelecting] = useState(false);\r\n const [rewindSelectIndex, setRewindSelectIndex] = useState(0);\r\n const [rewindList, setRewindList] = useState<MessageCheckpointInfo[]>([]);\r\n const [rewinding, setRewinding] = useState(false);\r\n\r\n // /rewind 提示:某轮对话修改了文件后,在原状态 loading 处显示「/rewind 1 可撤回本次修改」。\r\n // 三阶段:idle(不显示)→ pending(流式刚结束,等 2s)→ visible(展示提示)\r\n // 提示一旦出现会一直保留到下次对话开始,不会被用户输入打断。\r\n const [rewindHintPhase, setRewindHintPhase] = useState<\"idle\" | \"pending\" | \"visible\">(\"idle\");\r\n // 本轮是否触发了文件修改类工具调用(edit_file/write_file/multi_edit/delete_range)\r\n const currentRoundModifiedRef = useRef(false);\r\n // 提示计时器句柄\r\n const rewindHintTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\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);\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 // 输入变更时仅重置选择索引;rewind 提示由下一轮对话开始时清掉\r\n useEffect(() => {\r\n setSkillSelectIndex(0);\r\n setFileSelectIndex(0);\r\n }, [input]);\r\n\r\n // 组件卸载时清掉 rewind 提示计时器,避免内存泄露\r\n useEffect(() => {\r\n return () => {\r\n if (rewindHintTimerRef.current) {\r\n clearTimeout(rewindHintTimerRef.current);\r\n rewindHintTimerRef.current = null;\r\n }\r\n };\r\n }, []);\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 // 根据 session.messages 重建 displayMessages,绕开下标对齐问题\r\n function rebuildDisplayFromSession(sess: Session): void {\r\n const next: DisplayMessage[] = [];\r\n for (const m of sess.messages) {\r\n if (m.role === \"user\") {\r\n next.push({ role: \"user\", content: m.content });\r\n } else if (m.role === \"assistant\") {\r\n next.push({\r\n role: \"assistant\",\r\n content: m.content,\r\n assistantDetail: {\r\n content: m.content,\r\n toolCalls: m.toolCalls,\r\n },\r\n });\r\n } else if (m.role === \"tool\") {\r\n next.push({ role: \"tool\", content: m.content });\r\n }\r\n // system 消息不展示\r\n }\r\n setDisplayMessages(next);\r\n setStaticKey((prev) => prev + 1);\r\n }\r\n\r\n const doRewind = useCallback(async (target: MessageCheckpointInfo, displayNumber: number) => {\r\n const session = sessionRef.current;\r\n if (!session) return;\r\n setRewinding(true);\r\n setIsStreaming(true);\r\n setStreamingPhase(\"thinking\");\r\n setStreamingPlaceholder(\"⏪ 正在回退到检查点…\");\r\n setInput(\"\");\r\n try {\r\n const r: RewindResult = await session.rewind(target.index);\r\n if (r.ok) {\r\n rebuildDisplayFromSession(session);\r\n const tail = r.fileRestored ? \",工作区文件已恢复\" : \"(仅对话回退,未恢复文件)\";\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"assistant\", content: `⏪ 已回退到检查点 #${displayNumber}${tail}。` },\r\n ]);\r\n } else {\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"assistant\", content: `⚠ 回退失败:${r.error}` },\r\n ]);\r\n }\r\n } catch (err: unknown) {\r\n const msg = err instanceof Error ? err.message : String(err);\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"assistant\", content: `⚠ 回退异常:${msg}` },\r\n ]);\r\n } finally {\r\n setRewinding(false);\r\n setIsStreaming(false);\r\n setStreamingPhase(null);\r\n setStreamingPlaceholder(\"\");\r\n }\r\n }, []);\r\n\r\n const { doubleCtrlC, handleCtrlC } = useDoubleCtrlC(() => {\r\n // 双击 Ctrl+C 退出进程 — 先刷新日志再退出\r\n const s = sessionRef.current;\r\n if (s) {\r\n void s.flushLog().finally(() => process.exit(0));\r\n } else {\r\n process.exit(0);\r\n }\r\n });\r\n\r\n // 捕获 Ctrl+C 和渐变占位符状态下的字符输入\r\n useInput(\r\n useCallback(\r\n (_input, key) => {\r\n // /rewind 选择模式\r\n if (rewindSelecting) {\r\n if (key.upArrow) {\r\n setRewindSelectIndex((prev) => (prev - 1 + rewindList.length) % rewindList.length);\r\n } else if (key.downArrow) {\r\n setRewindSelectIndex((prev) => (prev + 1) % rewindList.length);\r\n } else if (key.return) {\r\n const target = rewindList[rewindSelectIndex];\r\n setRewindSelecting(false);\r\n if (target) {\r\n void doRewind(target, rewindSelectIndex + 1);\r\n }\r\n } else if (key.escape) {\r\n setRewindSelecting(false);\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"assistant\", content: \"已取消回退。\" },\r\n ]);\r\n }\r\n return;\r\n }\r\n\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 // Shift+Tab 切换计划模式 / 代码模式\r\n if (key.shift && key.tab) {\r\n if (!isStreaming) {\r\n const newMode: SessionMode = sessionMode === \"plan\" ? \"code\" : \"plan\";\r\n setSessionMode(newMode);\r\n sessionRef.current?.setMode(newMode);\r\n setToolChoice(undefined);\r\n sessionRef.current?.reset();\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"assistant\", content: newMode === \"plan\"\r\n ? \"📋 已切换为 **计划模式**(Shift+Tab)\\n\\n在此模式下,我只能读取和分析代码,不会执行任何修改。\"\r\n : \"🛠 已切换为 **代码模式**(Shift+Tab)\\n\\n现在可以正常读取和修改代码了。\",\r\n },\r\n ]);\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, sessionMode, setSessionMode, rewindSelecting, rewindSelectIndex, rewindList, doRewind]\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>;\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 // /rewind 命令:回退到历史检查点\r\n if (cmdLower === \"/rewind\" || cmdLower.startsWith(\"/rewind \")) {\r\n if (isStreaming || rewinding) {\r\n const reason = rewinding ? \"回退中\" : \"生成中\";\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"user\", content: trimmed },\r\n { role: \"assistant\", content: `⚠ 正在${reason},请稍后再试 /rewind。` },\r\n ]);\r\n setInput(\"\");\r\n return;\r\n }\r\n if (!sessionRef.current) {\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"user\", content: trimmed },\r\n { role: \"assistant\", content: \"⚠ Session 未就绪,无法回退。\" },\r\n ]);\r\n setInput(\"\");\r\n return;\r\n }\r\n const cps = sessionRef.current.listCheckpoints();\r\n if (cps.length === 0) {\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"user\", content: trimmed },\r\n { role: \"assistant\", content: \"⚠ 没有可回退的检查点。\\n只有在 git 仓库内且发生过对话后才会生成检查点。\" },\r\n ]);\r\n setInput(\"\");\r\n return;\r\n }\r\n // /rewind <N>:1 表示最新(与 git reset HEAD~ 习惯一致),2 表示上一个,依次类推\r\n const parts = trimmed.split(/\\s+/);\r\n if (parts.length >= 2) {\r\n const n = Number(parts[1]);\r\n if (!Number.isInteger(n) || n < 1 || n > cps.length) {\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"user\", content: trimmed },\r\n { role: \"assistant\", content: `⚠ 无效的序号「${parts[1]}」。可用范围 1~${cps.length}(1 表示最新)。` },\r\n ]);\r\n setInput(\"\");\r\n return;\r\n }\r\n // 将用户输入的 1-based \"最新优先\"序号转换为 cps 列表里的索引\r\n const target = cps[cps.length - n]!;\r\n setInput(\"\");\r\n await doRewind(target, n);\r\n return;\r\n }\r\n // 无参数:进入选择模式。逆序展示(最新在第 1 位),默认选中最新\r\n setRewindList([...cps].reverse());\r\n setRewindSelectIndex(0);\r\n setRewindSelecting(true);\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"user\", content: trimmed },\r\n { role: \"assistant\", content: `⏷↑↓ 选择检查点,Enter 确认,Esc 取消(共 ${cps.length} 个可回退位置)` },\r\n ]);\r\n setInput(\"\");\r\n return;\r\n }\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 // /plan 命令:切换为计划模式(只读分析,不能修改代码)\r\n if (cmdLower === \"/plan\") {\r\n if (sessionMode === \"plan\") {\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"user\", content: trimmed },\r\n { role: \"assistant\", content: \"已经在计划模式中。输入 /code 切回代码模式。\" },\r\n ]);\r\n } else {\r\n setSessionMode(\"plan\");\r\n sessionRef.current?.setMode(\"plan\");\r\n // 计划模式下自动启用工具调用,确保模型可以使用读工具\r\n setToolChoice(undefined);\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"user\", content: trimmed },\r\n { role: \"assistant\", content: \"📋 已切换为 **计划模式**\\n\\n在此模式下,我只能读取和分析代码,不会执行任何修改。\\n输入 /code 切回代码模式。\" },\r\n ]);\r\n // 清空会话历史,防止旧模式下产生的工具调用消息干扰新模式\r\n sessionRef.current?.reset();\r\n }\r\n setInput(\"\");\r\n return;\r\n }\r\n\r\n // /code 命令:切换回代码模式\r\n if (cmdLower === \"/code\") {\r\n if (sessionMode === \"code\") {\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"user\", content: trimmed },\r\n { role: \"assistant\", content: \"已经在代码模式中。输入 /plan 切回计划模式。\" },\r\n ]);\r\n } else {\r\n setSessionMode(\"code\");\r\n sessionRef.current?.setMode(\"code\");\r\n // 回到代码模式时重置工具调用策略,确保可以正常使用所有工具\r\n setToolChoice(undefined);\r\n setDisplayMessages((prev) => [\r\n ...prev,\r\n { role: \"user\", content: trimmed },\r\n { role: \"assistant\", content: \"🛠 已切换为 **代码模式**\\n\\n现在可以正常读取和修改代码了。\\n输入 /plan 切换为计划模式(只读分析)。\" },\r\n ]);\r\n // 清空会话历史,防止计划模式下产生的消息干扰代码模式\r\n sessionRef.current?.reset();\r\n }\r\n setInput(\"\");\r\n return;\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 await sessionRef.current?.flushLog();\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 setStreamingPhase(\"thinking\");\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 // 新一轮对话开始:清除上轮可能残留的 rewind 提示\r\n currentRoundModifiedRef.current = false;\r\n setRewindHintPhase(\"idle\");\r\n if (rewindHintTimerRef.current) {\r\n clearTimeout(rewindHintTimerRef.current);\r\n rewindHintTimerRef.current = null;\r\n }\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 setStreamingPhase(\"generating\");\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 setStreamingPhase(\"calling_tools\");\r\n setCurrentToolCalls((prev) => {\r\n const next = [...prev, ...event.calls];\r\n currentToolCallsRef.current = next;\r\n return next;\r\n });\r\n // 标记本轮是否触发了文件修改类工具调用\r\n for (const call of event.calls) {\r\n if (isFileMutatingTool(call.name)) {\r\n currentRoundModifiedRef.current = true;\r\n break;\r\n }\r\n }\r\n break;\r\n\r\n case \"tool_result\":\r\n // 工具执行完成 — 重置流式状态,准备接收模型的新一轮回复\r\n // 这是因为 Agent 循环会在工具执行后再次调用模型\r\n setStreamingPhase(\"executing_tools\");\r\n // 延迟重置为思考中,表示模型正在处理工具结果进行下一轮推理\r\n setTimeout(() => setStreamingPhase(\"thinking\"), 300);\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 // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\r\n const cost = calculateCost(event.usage, event.model as unknown 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 setStreamingPhase(null);\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 // 如果本轮触发了文件修改且无流错误 -> 2s 后展示 rewind 提示\r\n // 提示会一直保留到下次对话开始(流式再次启动)为止\r\n if (currentRoundModifiedRef.current && !finStreamError) {\r\n setRewindHintPhase(\"pending\");\r\n if (rewindHintTimerRef.current) clearTimeout(rewindHintTimerRef.current);\r\n rewindHintTimerRef.current = setTimeout(() => {\r\n setRewindHintPhase((prev) => (prev === \"pending\" ? \"visible\" : prev));\r\n rewindHintTimerRef.current = null;\r\n }, 2000);\r\n }\r\n }\r\n }, [onLaunchGame, onLaunchStock, currentContent, currentToolCalls, skills, skillSelectIndex, getFilteredSkills, thinkingEnabled, thinkingEffort, responseFormat, toolChoice, activeModel, sessionMode, isStreaming, rewinding]);\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 {/* 模式指示器 */}\r\n {sessionMode === \"plan\" && (\r\n <Text color=\"#ff69b4\" bold>{\" 📋 计划模式\"}</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 {/* 错误信息(流式结束后显示) */}\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 ) : rewindSelecting ? (\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=\"#ff9800\">选择要回退的检查点:</Text>\r\n {rewindList.map((cp, i) => {\r\n const isSelected = i === rewindSelectIndex;\r\n const marker = isSelected ? \" > \" : \" \";\r\n const time = new Date(cp.timestamp).toLocaleTimeString();\r\n const preview = cp.preview || \"(空)\";\r\n const tag = cp.isGitRepo ? \"\" : \" [非 git,仅对话]\";\r\n return (\r\n <Box key={cp.index}>\r\n <Text\r\n color={isSelected ? \"#ff9800\" : undefined}\r\n bold={isSelected}\r\n >\r\n {marker}#{i + 1} {time} `{preview}{tag}`\r\n </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 {/* 流式状态指示器 — 上框上方居中显示 */}\r\n {(hasConversationStarted || sessionMode === \"plan\") && isStreaming && streamingPhase ? (\r\n <Box marginTop={1} justifyContent=\"center\">\r\n <Text bold color={PHASE_CONFIG[streamingPhase].color}>\r\n {PHASE_CONFIG[streamingPhase].icon} {PHASE_CONFIG[streamingPhase].label}{\" \"}\r\n <InkSpinner type=\"dots\" />\r\n </Text>\r\n </Box>\r\n ) : rewindHintPhase === \"visible\" ? (\r\n // 本轮修改了文件时,流式结束后 2s 在原位置展示 /rewind 提示\r\n // 一直保留到下次对话开始,与 /rewind 1(最新检查点)配套\r\n <Box marginTop={1} justifyContent=\"center\">\r\n <Text color=\"#808080\">{\"↩ /rewind 1 可撤回本次修改\"}</Text>\r\n </Box>\r\n ) : null}\r\n <Box marginTop={1}>\r\n {hasConversationStarted || sessionMode === \"plan\" ? (\r\n <Text color={sessionMode === \"plan\" ? \"#ff69b4\" : \"#00ffff\"} dimColor={sessionMode !== \"plan\"}>\r\n <Text>{sessionMode === \"plan\"\r\n ? \"─\".repeat(Math.max(dividerWidth - 48, 1))\r\n : \"─\".repeat(Math.max(dividerWidth - 35, 10))}</Text>\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 {sessionMode === \"plan\" && (\r\n <Text color=\"#ff69b4\" bold>{\" 📋 计划模式\"}</Text>\r\n )}\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);\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 } 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: _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 /** 全局活跃实例表,用于进程退出时统一 flush。 */\r\n static readonly #instances = new Set<CostTracker>();\r\n\r\n /** 进程退出兜底:统一 flush 所有活跃 CostTracker。 */\r\n static async flushAll(): Promise<void> {\r\n await Promise.allSettled(\r\n [...CostTracker.#instances].map((t) => t.flush()),\r\n );\r\n }\r\n\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 // 注册到全局实例表(用于进程退出时统一 flush)\r\n CostTracker.#instances.add(this);\r\n }\r\n\r\n // -----------------------------------------------------------------------\r\n // 核心方法\r\n // -----------------------------------------------------------------------\r\n\r\n /**\r\n * 记录一次 API 调用的 Token 使用量和成本。\r\n * 自动计算费用,累加到会话级和日级统计。\r\n * 标记脏数据(dirty),由上层在合适的时机调用 flush() 持久化到磁盘。\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. 标记需要持久化(上层在对话周期结束时调用 flush() 落盘)\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 * 副作用:确保成本目录存在(首次运行时创建 ~/.dskcode/costs)。\r\n */\r\n async load(): Promise<void> {\r\n await this.#ensureCostDir();\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 * 会话结束/进程退出时主动调用一次即可。需要每日数据精确时也可在每次\r\n * record() 后调用。并发安全:靠 #flushInProgress 互斥。\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.#ensureCostDir();\r\n await this.#saveStore();\r\n } catch (err) {\r\n // 落盘失败:把 dirty 标记还回去,等下次重试\r\n this.#dirty = true;\r\n console.error(\"[CostTracker] flush 失败:\", err);\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 * 销毁实例:从全局实例表移除并 flush。\r\n * 一般用不到,仅在测试或显式生命周期管理时使用。\r\n */\r\n async dispose(): Promise<void> {\r\n CostTracker.#instances.delete(this);\r\n await this.flush();\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 // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\r\n return JSON.parse(raw) as unknown as CostStore;\r\n } catch {\r\n return { version: 1, daily: {} };\r\n }\r\n }\r\n\r\n /** 确保成本目录存在(首次运行时创建 ~/.dskcode/costs) */\r\n async #ensureCostDir(): Promise<void> {\r\n await mkdir(this.#costDir, { recursive: true });\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 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 // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\r\n const parsed = JSON.parse(args) as unknown 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// ```...``` 代码块 → 独立块渲染,diff 内容自动绿/红着色\r\n// | col | col | 表格 → 框线表格,自动对齐\r\n// 流式传输中未闭合的标记自动降级为纯文本,不会闪烁。\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { Box, Text } from \"ink\";\r\nimport type { ReactNode } from \"react\";\r\n\r\n/** 解析后的文本段类型 */\r\ntype SegmentType = \"plain\" | \"code\" | \"bold\" | \"code_block\" | \"table\";\r\n\r\ninterface Segment {\r\n text: string;\r\n type: SegmentType;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 第 0 遍:提取三反引号代码块\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * 在全文中查找 ```...``` 代码块,将其标记为 \"code_block\" 类型。\r\n * 代码块之外的内容保持 \"plain\" 类型,后续交给 bold/code 解析。\r\n *\r\n * 未闭合的三反引号降级为纯文本(流式传输安全)。\r\n */\r\nfunction parseCodeBlocks(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 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 + 3);\r\n\r\n if (closeIdx === -1) {\r\n // 未闭合 → 降级为纯文本\r\n segments.push({ text: text.slice(openIdx), type: \"plain\" });\r\n break;\r\n }\r\n\r\n // 提取代码块内容(含开头的语言标识行)\r\n const codeContent = text.slice(openIdx + 3, closeIdx);\r\n segments.push({ text: codeContent, type: \"code_block\" });\r\n current = closeIdx + 3;\r\n }\r\n\r\n return segments;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 第 0.5 遍:Markdown 表格检测(在 plain 段内)\r\n// ---------------------------------------------------------------------------\r\n\r\n/** 判断一行是不是表格行(以 | 开头并以 | 结尾) */\r\nfunction isTableRow(line: string): boolean {\r\n const t = line.trim();\r\n return /^\\|.+\\|$/.test(t);\r\n}\r\n\r\n/** 判断一行是不是表格分隔行(|:---:|:---:| 等) */\r\nfunction isTableSepRow(line: string): boolean {\r\n const t = line.trim();\r\n // 字符类需包含 | 以匹配多列分隔行,如 |---|---|\r\n return /^\\|[-: |]+\\|$/.test(t) && t.includes(\"-\");\r\n}\r\n\r\n/**\r\n * 在 plain 文本中检测 Markdown 表格块(连续的 `|...|` 行),标记为 \"table\" 类型。\r\n *\r\n * 表格判定条件:\r\n * 1. 开头行是表格行\r\n * 2. 第二行是分隔行(含 `-`)\r\n * 3. 第三行也是表格行\r\n */\r\nfunction parseTables(text: string): Segment[] {\r\n const lines = text.split(\"\\n\");\r\n const result: Segment[] = [];\r\n let plainBuf = \"\";\r\n\r\n function flushPlain() {\r\n if (plainBuf) {\r\n result.push({ text: plainBuf, type: \"plain\" });\r\n plainBuf = \"\";\r\n }\r\n }\r\n\r\n for (let i = 0; i < lines.length; i++) {\r\n const line = lines[i] ?? \"\";\r\n\r\n if (\r\n isTableRow(line) &&\r\n i + 1 < lines.length &&\r\n isTableSepRow(lines[i + 1] ?? \"\") &&\r\n i + 2 < lines.length &&\r\n isTableRow(lines[i + 2] ?? \"\")\r\n ) {\r\n flushPlain();\r\n\r\n // 收集所有连续的表格行(含分隔行)\r\n const tableLineList: string[] = [];\r\n let j = i;\r\n while (j < lines.length && isTableRow(lines[j] ?? \"\")) {\r\n tableLineList.push(lines[j]!);\r\n j++;\r\n }\r\n\r\n result.push({ text: tableLineList.join(\"\\n\"), type: \"table\" });\r\n i = j - 1;\r\n } else {\r\n if (plainBuf) plainBuf += \"\\n\" + line;\r\n else plainBuf = line;\r\n }\r\n }\r\n\r\n flushPlain();\r\n return result;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 第 1 遍:**bold** 解析(与之前相同)\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// 第 2 遍:`code` 解析(处理 plain 和 bold 段内的单反引号对)\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * 在纯文本中查找成对的单反引号,返回 [plain, code, plain, ...] 段序列。\r\n *\r\n * - 只处理 `...` 单反引号对;多反引号序列降级为纯文本。\r\n * - 未闭合的反引号降级为纯文本。\r\n */\r\nfunction parseInlineCode(text: string): Segment[] {\r\n const result: 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 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 // 计算连续反引号的数量\r\n let runLength = 1;\r\n while (\r\n openIdx + runLength < text.length &&\r\n text[openIdx + runLength] === \"`\"\r\n ) {\r\n runLength++;\r\n }\r\n\r\n if (runLength === 1) {\r\n // 单反引号对 → code 段\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 } else {\r\n // 多反引号序列(```、`` 等)保持原样\r\n result.push({\r\n text: text.slice(openIdx, openIdx + runLength),\r\n type: \"plain\",\r\n });\r\n current = openIdx + runLength;\r\n }\r\n }\r\n\r\n return result;\r\n}\r\n\r\n/**\r\n * 在 plain 和 bold 段内按 `` `...` `` 对拆分为 code 段。\r\n *\r\n * - **plain** 段内的 code → 标蓝,剩余部分保持默认色\r\n * - **bold** 段内的 code → 标蓝,剩余部分保持紫色\r\n * - 多反引号序列(```` ```code``` ````、`` ``code`` `` 等)统一降级为纯文本\r\n * - code_block / table 段直接透传\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 === \"code_block\" || seg.type === \"table\") {\r\n result.push(seg);\r\n continue;\r\n }\r\n\r\n // 对 plain 和 bold 的内容都做反引号解析\r\n const parts = parseInlineCode(seg.text);\r\n\r\n for (const part of parts) {\r\n if (part.type === \"code\") {\r\n // 反引号内容始终标蓝\r\n result.push(part);\r\n } else if (seg.type === \"bold\") {\r\n // bold 段内非反引号部分 → 保持紫色\r\n result.push({ text: part.text, type: \"bold\" });\r\n } else {\r\n // plain 段内非反引号部分 → 保持默认色\r\n result.push(part);\r\n }\r\n }\r\n }\r\n\r\n return result;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 代码块渲染\r\n// ---------------------------------------------------------------------------\r\n\r\n/** 添加行绿色 */\r\nconst DIFF_ADD_COLOR = \"#22c55e\";\r\n/** 删除行红色 */\r\nconst DIFF_DEL_COLOR = \"#ef4444\";\r\n/** Hunk 头青色 */\r\nconst DIFF_HUNK_COLOR = \"#00cccc\";\r\n\r\n/**\r\n * 渲染三反引号代码块的内容,对 diff 风格的内容行着色。\r\n *\r\n * 代码块内的第一行若为纯文本(非 `+`/`-`/`@@`/空格开头),\r\n * 视为语言标识(如 \"diff\"、\"python\"),用灰色标出。\r\n */\r\nfunction CodeBlockRenderer({ code }: { code: string }): ReactNode {\r\n const lines = code.split(\"\\n\");\r\n\r\n // 判断第一行是否为语言标识行\r\n const firstLine = lines[0] ?? \"\";\r\n const hasLangLine =\r\n firstLine.trim().length > 0 &&\r\n !firstLine.startsWith(\"+\") &&\r\n !firstLine.startsWith(\"-\") &&\r\n !firstLine.startsWith(\"@@\") &&\r\n !firstLine.startsWith(\" \");\r\n\r\n const lang = hasLangLine ? firstLine : undefined;\r\n const codeLines = hasLangLine ? lines.slice(1) : lines;\r\n\r\n // 空内容\r\n if (codeLines.length === 0) {\r\n return hasLangLine ? (\r\n <Box marginLeft={2}>\r\n <Text color=\"#888888\">┌ {lang}</Text>\r\n </Box>\r\n ) : null;\r\n }\r\n\r\n return (\r\n <Box flexDirection=\"column\" marginLeft={2} marginTop={1}>\r\n {lang && <Text color=\"#888888\">┌ {lang}</Text>}\r\n {codeLines.map((line, i) => {\r\n if (line.startsWith(\"+\")) {\r\n return (\r\n <Text key={i} color={DIFF_ADD_COLOR}>\r\n {line}\r\n </Text>\r\n );\r\n }\r\n if (line.startsWith(\"-\")) {\r\n return (\r\n <Text key={i} color={DIFF_DEL_COLOR}>\r\n {line}\r\n </Text>\r\n );\r\n }\r\n if (line.startsWith(\"@@\")) {\r\n return (\r\n <Text key={i} color={DIFF_HUNK_COLOR}>\r\n {line}\r\n </Text>\r\n );\r\n }\r\n // 上下文行或非 diff 代码行\r\n return <Text key={i}>{line}</Text>;\r\n })}\r\n </Box>\r\n );\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 表格渲染\r\n// ---------------------------------------------------------------------------\r\n\r\n/** 表格最大总宽度 */\r\nconst TABLE_MAX_WIDTH = 90;\r\n/** 表格列最小宽度 */\r\nconst TABLE_MIN_COL_WIDTH = 3;\r\n\r\n/**\r\n * 计算单个字符在终端中的显示宽度。\r\n * CJK 文字、全角符号、emoji 占 2 格;其余占 1 格。\r\n */\r\nfunction charWidth(ch: string): number {\r\n const cp = ch.codePointAt(0);\r\n if (cp === undefined) return 0;\r\n\r\n // CJK 相关范围\r\n if (\r\n (cp >= 0x1100 && cp <= 0x115F) || // Hangul Jamo\r\n cp === 0x2329 || cp === 0x232A ||\r\n (cp >= 0x2E80 && cp <= 0xA4CF) || // CJK Radicals ~ Yi\r\n (cp >= 0xA960 && cp <= 0xA97F) || // Hangul Extended\r\n (cp >= 0xAC00 && cp <= 0xD7AF) || // Hangul Syllables\r\n (cp >= 0xF900 && cp <= 0xFAFF) || // CJK Compat\r\n (cp >= 0xFE10 && cp <= 0xFE1F) || // Vertical forms\r\n (cp >= 0xFF01 && cp <= 0xFF60) || // Fullwidth\r\n (cp >= 0xFFE0 && cp <= 0xFFE6) ||\r\n (cp >= 0x1F000 && cp <= 0x1FFFF) // Emoji / Symbols\r\n ) {\r\n return 2;\r\n }\r\n return 1;\r\n}\r\n\r\n/** 计算字符串在终端中的视觉宽度 */\r\nfunction visualWidth(text: string): number {\r\n let w = 0;\r\n for (const ch of text) {\r\n w += charWidth(ch);\r\n }\r\n return w;\r\n}\r\n\r\n/** 用空格将字符串填充到目标视觉宽度 */\r\nfunction padToWidth(text: string, targetWidth: number, align: \"left\" | \"center\" | \"right\"): string {\r\n const vw = visualWidth(text);\r\n const delta = targetWidth - vw;\r\n if (delta <= 0) return text;\r\n\r\n const leftPad = align === \"right\" ? delta : align === \"center\" ? Math.floor(delta / 2) : 0;\r\n const rightPad = delta - leftPad;\r\n\r\n return \" \".repeat(leftPad) + text + \" \".repeat(rightPad);\r\n}\r\n\r\n/** 解析表格一行为单元格数组 */\r\nfunction parseTableCells(line: string): string[] {\r\n const t = line.trim();\r\n // 去掉首尾的 |,再按 | 切割\r\n const inner = t.slice(1, t.length - 1);\r\n return inner.split(\"|\").map((c) => c.trim());\r\n}\r\n\r\n/** 从分隔行解析每列对齐方式 */\r\nfunction parseAlignments(sepLine: string): Array<\"left\" | \"center\" | \"right\"> {\r\n const cells = parseTableCells(sepLine);\r\n return cells.map((c) => {\r\n const l = c.startsWith(\":\");\r\n const r = c.endsWith(\":\");\r\n if (l && r) return \"center\";\r\n if (r) return \"right\";\r\n return \"left\";\r\n });\r\n}\r\n\r\n/**\r\n * 渲染 Markdown 表格为终端框线表格。\r\n *\r\n * 输出示例:\r\n * ┌──────────┬──────────────────────────┬──────────────────────────┐\r\n * │ 位置 │ 原内容 │ 新内容 │\r\n * ├──────────┼──────────────────────────┼──────────────────────────┤\r\n * │ 第 12 行 │ interface FileDiffs { │ interface Diffs { │\r\n * └──────────┴──────────────────────────┴──────────────────────────┘\r\n */\r\nfunction TableRenderer({ text }: { text: string }): ReactNode {\r\n const rawLines = text.split(\"\\n\").filter((l) => l.trim().length > 0);\r\n if (rawLines.length < 2) return <Text>{text}</Text>;\r\n\r\n // 找到分隔行\r\n let sepIdx = -1;\r\n for (let k = 0; k < rawLines.length; k++) {\r\n if (isTableSepRow(rawLines[k]!)) {\r\n sepIdx = k;\r\n break;\r\n }\r\n }\r\n if (sepIdx === -1) return <Text>{text}</Text>;\r\n\r\n // 表头(分隔行之前的所有行合并为一行,防止多行表头)\r\n const headerText = rawLines.slice(0, sepIdx).join(\"\");\r\n const headerCells = parseTableCells(headerText);\r\n const alignments = parseAlignments(rawLines[sepIdx]!);\r\n const dataRows = rawLines.slice(sepIdx + 1).map(parseTableCells);\r\n\r\n const colCount = headerCells.length;\r\n\r\n // 计算每列最大视觉宽度\r\n const colMaxWidths = headerCells.map((_, ci) => {\r\n let max = visualWidth(headerCells[ci] ?? \"\");\r\n for (const row of dataRows) {\r\n const w = visualWidth(row[ci] ?? \"\");\r\n if (w > max) max = w;\r\n }\r\n return Math.max(max, TABLE_MIN_COL_WIDTH);\r\n });\r\n\r\n // 计算每列最终宽度(留 2 格边距 = 左右各 1 空格)\r\n const margin = 2;\r\n const totalMinWidth = colMaxWidths.reduce((s, w) => s + w + margin, 1); // +1 for leading │\r\n let colWidths: number[];\r\n if (totalMinWidth <= TABLE_MAX_WIDTH) {\r\n colWidths = colMaxWidths;\r\n } else {\r\n // 超出总宽 → 等比例缩减\r\n const available = TABLE_MAX_WIDTH - 1 - margin * colCount;\r\n const sum = colMaxWidths.reduce((s, w) => s + w, 0);\r\n colWidths = colMaxWidths.map((w) => Math.max(TABLE_MIN_COL_WIDTH, Math.floor((w / sum) * available)));\r\n }\r\n\r\n // 框线字符\r\n const H = \"─\";\r\n const makeSep = (l: string, m: string, r: string) =>\r\n l + colWidths.map((w) => H.repeat(w + margin)).join(m) + r;\r\n\r\n const topBorder = makeSep(\"┌\", \"┬\", \"┐\");\r\n const midBorder = makeSep(\"├\", \"┼\", \"┤\");\r\n const botBorder = makeSep(\"└\", \"┴\", \"┘\");\r\n\r\n // 渲染一行单元格\r\n function rowLine(cells: string[], keyBase: string): ReactNode {\r\n return (\r\n <Text key={keyBase}>\r\n {\"│\"}\r\n {cells.map((cell, ci) => (\r\n <Text key={ci}>\r\n {\" \"}\r\n {padToWidth(cell.slice(0, colWidths[ci] ?? TABLE_MIN_COL_WIDTH), colWidths[ci] ?? TABLE_MIN_COL_WIDTH, alignments[ci] ?? \"left\")}\r\n {\" \"}│\r\n </Text>\r\n ))}\r\n </Text>\r\n );\r\n }\r\n\r\n const rows: ReactNode[] = [\r\n <Text key=\"top\" color=\"#888888\">{topBorder}</Text>,\r\n rowLine(headerCells, \"hdr\"),\r\n <Text key=\"mid\" color=\"#888888\">{midBorder}</Text>,\r\n ...dataRows.map((cells, ri) => rowLine(cells, `d${ri}`)),\r\n <Text key=\"bot\" color=\"#888888\">{botBorder}</Text>,\r\n ];\r\n\r\n return (\r\n <Box flexDirection=\"column\" marginTop={1}>\r\n {rows}\r\n </Box>\r\n );\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Emoji 检测 — 跳过图标字符的颜色包裹\r\n// ---------------------------------------------------------------------------\r\n\r\n/** 匹配 emoji 相关 code point 的正则 */\r\nconst EMOJI_CP_RE = /[\\u{1F000}-\\u{1FFFF}\\u{FE00}-\\u{FE0F}\\u{200D}\\u{20E3}\\u{2600}-\\u{27BF}\\u{231A}-\\u{23FF}\\u{2934}\\u{2935}\\u{25AA}-\\u{25FE}\\u{2B50}\\u{2B55}\\u{00A9}\\u{00AE}\\u{2122}\\u{3030}\\u{303D}\\u{3297}\\u{3299}]/u;\r\n\r\n/**\r\n * 检测一个 grapheme cluster 是否属于 emoji 或图标字符。\r\n * 此类字符在 bold 渲染中应跳过颜色包裹,避免终端渲染异常。\r\n */\r\nfunction isEmojiCluster(text: string): boolean {\r\n return EMOJI_CP_RE.test(text);\r\n}\r\n\r\n/**\r\n * 将文本按 grapheme cluster 分割,标记每个 cluster 是否为 emoji。\r\n * 降级策略:若 Intl.Segmenter 不可用,将整个文本视为纯文本。\r\n */\r\nfunction splitEmojiClusters(\r\n text: string,\r\n): Array<{ text: string; isEmoji: boolean }> {\r\n try {\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n const segmenter = new (Intl as any).Segmenter(\"en\", {\r\n granularity: \"grapheme\",\r\n });\r\n const segments = [...segmenter.segment(text)];\r\n return segments.map((s) => ({\r\n text: s.segment,\r\n isEmoji: isEmojiCluster(s.segment),\r\n }));\r\n } catch {\r\n // 降级:整个文本作为一个纯文本块\r\n return [{ text, isEmoji: false }];\r\n }\r\n}\r\n\r\n/**\r\n * 渲染 bold(紫色)文本,对 emoji 字符跳过颜色包裹。\r\n */\r\nfunction renderBoldText(text: string, key: number | string): ReactNode {\r\n const clusters = splitEmojiClusters(text);\r\n\r\n // 不含 emoji — 简单路径(和之前一致)\r\n if (clusters.length === 1 && !clusters[0]!.isEmoji) {\r\n return (\r\n <Text key={key} color={BOLD_COLOR}>\r\n {text}\r\n </Text>\r\n );\r\n }\r\n\r\n // 合并相邻的同类 cluster 减少嵌套\r\n const groups: Array<{ text: string; isEmoji: boolean }> = [];\r\n for (const c of clusters) {\r\n const last = groups[groups.length - 1];\r\n if (last && last.isEmoji === c.isEmoji) {\r\n last.text += c.text;\r\n } else {\r\n groups.push({ text: c.text, isEmoji: c.isEmoji });\r\n }\r\n }\r\n\r\n return (\r\n <Text key={key}>\r\n {groups.map((g, i) =>\r\n g.isEmoji ? (\r\n g.text\r\n ) : (\r\n <Text key={i} color={BOLD_COLOR}>\r\n {g.text}\r\n </Text>\r\n ),\r\n )}\r\n </Text>\r\n );\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// 高亮颜色常量\r\n// ---------------------------------------------------------------------------\r\n\r\n/** 行内代码高亮色 — 天蓝 */\r\nconst CODE_COLOR = \"#00BFFF\";\r\n/** 加粗强调色 — 紫色 */\r\nconst BOLD_COLOR = \"#A855F7\";\r\n\r\n// ---------------------------------------------------------------------------\r\n// 主组件\r\n// ---------------------------------------------------------------------------\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 * - ```` ```diff ```` → 独立块,`+` 绿色 / `-` 红色 / `@@` 青色\r\n * - `| ... |` Markdown → 框线表格\r\n */\r\nexport function HighlightedText({ children: text }: HighlightedTextProps): ReactNode {\r\n // 第 0 遍:提取 ```...``` 代码块\r\n const codeBlockSegments = parseCodeBlocks(text);\r\n\r\n // 第 0.5 遍:在非代码块的 plain 段中检测 Markdown 表格\r\n const tableSegments: Segment[] = [];\r\n for (const seg of codeBlockSegments) {\r\n if (seg.type === \"code_block\") {\r\n tableSegments.push(seg);\r\n } else {\r\n tableSegments.push(...parseTables(seg.text));\r\n }\r\n }\r\n\r\n // 第 1 遍:在 inline plain 段内解析 **bold**\r\n const boldSegments: Segment[] = [];\r\n for (const seg of tableSegments) {\r\n if (seg.type === \"code_block\" || seg.type === \"table\") {\r\n boldSegments.push(seg);\r\n } else {\r\n boldSegments.push(...parseBoldPairs(seg.text));\r\n }\r\n }\r\n\r\n // 第 2 遍:在 inline plain 段内解析 `code`\r\n const segments = parseCodePairs(boldSegments);\r\n\r\n // 检查是否包含块级元素\r\n const hasBlock = segments.some(\r\n (s) => s.type === \"code_block\" || s.type === \"table\",\r\n );\r\n\r\n // ---- 无块级元素:纯内联渲染 ----\r\n if (!hasBlock) {\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 renderBoldText(seg.text, i);\r\n }\r\n return seg.text;\r\n })}\r\n </Text>\r\n );\r\n }\r\n\r\n // ---- 有块级元素:分组渲染(inline 组 + 块级渲染) ----\r\n const rendered: ReactNode[] = [];\r\n let inlineGroup: Segment[] = [];\r\n\r\n function flushInline(groupIdx: number) {\r\n if (inlineGroup.length === 0) return;\r\n const isSimpleInline =\r\n inlineGroup.length === 1 && inlineGroup[0]!.type === \"plain\";\r\n if (isSimpleInline) {\r\n rendered.push(\r\n <Text key={groupIdx} wrap=\"wrap\">\r\n {inlineGroup[0]!.text}\r\n </Text>,\r\n );\r\n } else {\r\n rendered.push(\r\n <Text key={groupIdx} wrap=\"wrap\">\r\n {inlineGroup.map((seg, j) => {\r\n if (seg.type === \"code\") {\r\n return (\r\n <Text key={j} color={CODE_COLOR}>\r\n {seg.text}\r\n </Text>\r\n );\r\n }\r\n if (seg.type === \"bold\") {\r\n return renderBoldText(seg.text, j);\r\n }\r\n return seg.text;\r\n })}\r\n </Text>,\r\n );\r\n }\r\n inlineGroup = [];\r\n }\r\n\r\n for (const seg of segments) {\r\n if (seg.type === \"code_block\") {\r\n flushInline(rendered.length);\r\n rendered.push(\r\n <CodeBlockRenderer key={`b${rendered.length}`} code={seg.text} />,\r\n );\r\n } else if (seg.type === \"table\") {\r\n flushInline(rendered.length);\r\n rendered.push(\r\n <TableRenderer key={`t${rendered.length}`} text={seg.text} />,\r\n );\r\n } else {\r\n inlineGroup.push(seg);\r\n }\r\n }\r\n flushInline(rendered.length);\r\n\r\n return <Box flexDirection=\"column\">{rendered}</Box>;\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.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 // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\r\n model: config.model as unknown 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 // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\r\n return tool.execute(args as I, ctx);\r\n },\r\n\r\n initialTitle(args: unknown): string {\r\n // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\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\nimport planPromptTemplate from \"./prompts/plan-prompt.hbs\";\r\n\r\nconst compiledTemplate = Handlebars.compile(systemPromptTemplate);\r\nconst compiledPlanTemplate = Handlebars.compile(planPromptTemplate);\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).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\nfunction buildTemplateVars(opts: SystemPromptOptions) {\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 {\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\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 return compiledTemplate(buildTemplateVars(opts));\r\n}\r\n\r\n/**\r\n * 构建计划模式(Plan Mode)的系统提示词。\r\n *\r\n * 使用 Handlebars 模板 `prompts/plan-prompt.hbs` 渲染,\r\n * 聚焦于代码分析、架构设计和任务规划,不包含写工具信息。\r\n */\r\nexport function buildPlanSystemPrompt(opts: SystemPromptOptions): string {\r\n return compiledPlanTemplate(buildTemplateVars(opts));\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- **不要使用 `#` 符号** — 终端不渲染 Markdown 标题,请用 emoji + 加粗替代(如 `📌 **标题**`)\r\n\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","你是 dskcode,当前工作在**计划模式(Plan Mode)**。\r\n\r\n在此模式下,**你只能读取和分析代码,不能执行任何修改操作**。你的职责是帮助用户理解代码、设计架构、规划任务、评估方案。\r\n\r\n## 核心原则\r\n- 回答使用中文,代码标识符保持英文\r\n- 提供准确、实用、可操作的建议\r\n- 对不确定的内容明确标注,不编造事实\r\n- **绝不执行任何写操作** — 你只能读取和分析\r\n\r\n## 工作流程\r\n1. 收到用户请求\r\n2. 使用读工具(read-file, grep, glob, ls, fetch)探索代码库\r\n3. 根据发现的信息提供结构化的分析或计划\r\n4. 工具返回出错时分析原因并修正后重试\r\n5. 完成后输出最终分析结果\r\n\r\n## 工具限制\r\n**你只能使用读工具**,不能使用任何写工具(write-file, edit-file, multi-edit, delete-range, bash)。\r\n如果用户请求修改代码,请:\r\n- 提供修改建议和方案\r\n- 指出需要修改的文件和位置\r\n- 解释修改的影响\r\n- 但不执行任何修改\r\n\r\n## 计划输出格式\r\n\r\n当用户请求计划/方案时,建议按照以下结构组织输出:\r\n\r\n📋 **概述**\r\n 1-2 句话说明要做什么以及为什么要做\r\n\r\n🔍 **现状分析**\r\n 当前代码的组织结构和关键发现\r\n\r\n⚡ **影响范围**\r\n 哪些文件/模块会受到影响\r\n\r\n📝 **实施步骤**\r\n 1. 第一步(文件路径/变更说明)\r\n 2. 第二步(文件路径/变更说明)\r\n 3. ...\r\n\r\n⚠️ **风险与注意事项**\r\n 可能的风险点、依赖关系、回退策略\r\n\r\n## 回答格式\r\n\r\n终端不支持 Markdown 渲染。回复格式要求:\r\n- 用 emoji 做视觉标记\r\n- 用数字序号或短横线组织列表\r\n- 代码标识符、文件路径用单个反引号 ` 包裹\r\n- 多行代码用缩进 4 个空格表示\r\n- 段落间用空行分隔\r\n- **不要使用 `#` 符号** — 终端不渲染 Markdown 标题,请用 emoji + 加粗替代\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","import { execFile } from \"node:child_process\";\r\nimport { promisify } from \"node:util\";\r\nimport type { Checkpoint } from \"./types.js\";\r\n\r\nconst execFileAsync = promisify(execFile);\r\nconst EXEC_OPTIONS = { windowsHide: true } as const;\r\n\r\nexport async function isGitRepo(cwd: string): Promise<boolean> {\r\n try {\r\n await execFileAsync(\"git\", [\"rev-parse\", \"--is-inside-work-tree\"], { cwd, ...EXEC_OPTIONS });\r\n return true;\r\n } catch { return false; }\r\n}\r\n\r\nasync function hasCommits(cwd: string): Promise<boolean> {\r\n try {\r\n await execFileAsync(\"git\", [\"rev-parse\", \"--verify\", \"HEAD\"], { cwd, ...EXEC_OPTIONS });\r\n return true;\r\n } catch { return false; }\r\n}\r\n\r\nasync function hasWorkingChanges(cwd: string): Promise<boolean> {\r\n const out = await execFileAsync(\"git\", [\"status\", \"--porcelain\"], { cwd, ...EXEC_OPTIONS });\r\n return out.stdout.trim().length > 0;\r\n}\r\n\r\nasync function git(args: string[], cwd: string): Promise<string> {\r\n try {\r\n const { stdout } = await execFileAsync(\"git\", args, { cwd, ...EXEC_OPTIONS });\r\n return stdout;\r\n } catch (err: unknown) {\r\n const msg = err instanceof Error ? err.message : String(err);\r\n throw new Error(`git ${args.join(\" \")} 失败: ${msg}`);\r\n }\r\n}\r\n\r\nasync function listStashShas(cwd: string): Promise<string[]> {\r\n const out = await git([\"stash\", \"list\", \"--format=%H\"], cwd);\r\n return out.split(\"\\n\").map((l) => l.trim()).filter(Boolean);\r\n}\r\n\r\nexport async function createCheckpoint(cwd: string): Promise<Checkpoint> {\r\n const timestamp = Date.now();\r\n const inRepo = await isGitRepo(cwd);\r\n if (!inRepo) return { stashSha: \"\", timestamp, cwd, isGitRepo: false };\r\n if (!(await hasCommits(cwd))) return { stashSha: \"\", timestamp, cwd, isGitRepo: true };\r\n if (!(await hasWorkingChanges(cwd))) return { stashSha: \"\", timestamp, cwd, isGitRepo: true };\r\n\r\n // 为了既登记检查点供 rewind 恢复、又**不**让工作区被 stash \"挪走\"\r\n // (`stash push` 默认会把工作区清空,导致后续对话看不到前面对话产生的修改 —— 这就是 bug),\r\n // 这里使用 `stash push -u` 暂存 + 立即 `stash apply` 还原到工作区。\r\n // stash entry 仍保留在 list 中,rewind 时按 SHA 仍可被精准 drop / apply。\r\n const beforeShas = await listStashShas(cwd);\r\n await git([\"stash\", \"push\", \"-m\", `dskcode-cp-${timestamp}`, \"-u\"], cwd);\r\n\r\n const newShas = await listStashShas(cwd);\r\n if (newShas.length === 0) throw new Error(\"git stash push 未能创建 stash entry\");\r\n const newSha = newShas.find((s) => !beforeShas.includes(s)) ?? newShas[0]!;\r\n\r\n // 立即把 stash 还原到工作区:这样前面对话产生的修改不会因为本次 chat 开始\r\n // 而被\"隐藏\"到 stash 里。stash entry 不会被 `apply` 消耗(仅 `pop`/`drop` 才会),\r\n // 所以后续 rewind 仍能基于 newSha 找回这个快照。\r\n await git([\"stash\", \"apply\", newSha], cwd);\r\n\r\n return { stashSha: newSha, timestamp, cwd, isGitRepo: true };\r\n}\r\n\r\nexport async function restoreCheckpointForce(checkpoint: Checkpoint): Promise<void> {\r\n if (!checkpoint.isGitRepo) throw new Error(\"非 git 仓库,无法恢复文件状态\");\r\n if (!checkpoint.stashSha) throw new Error(\"检查点为空(工作区原本就干净),无需恢复\");\r\n\r\n const { cwd, stashSha } = checkpoint;\r\n const currentShas = await listStashShas(cwd);\r\n if (!currentShas.includes(stashSha)) {\r\n throw new Error(\"检查点已失效(stash entry 已被消费或 GC),无法恢复\");\r\n }\r\n await git([\"checkout\", \"--\", \".\"], cwd);\r\n await git([\"clean\", \"-fd\"], cwd);\r\n await git([\"stash\", \"apply\", stashSha], cwd);\r\n const refIndex = currentShas.indexOf(stashSha);\r\n if (refIndex >= 0) await git([\"stash\", \"drop\", `stash@{${refIndex}}`], cwd);\r\n}\r\n\r\n/**\r\n * 将工作区重置为 HEAD 干净状态(丢弃所有未提交修改与未跟踪文件)。\r\n * 用于「目标检查点 stashSha 为空」的 rewind 场景——那时工作区本就干净,\r\n * 但累积的后续对话修改需要被丢弃。\r\n */\r\nexport async function restoreToClean(cwd: string): Promise<void> {\r\n const inRepo = await isGitRepo(cwd);\r\n if (!inRepo) throw new Error(\"非 git 仓库,无法恢复文件状态\");\r\n if (!(await hasCommits(cwd))) throw new Error(\"仓库无 commit,无法恢复\");\r\n await git([\"checkout\", \"--\", \".\"], cwd);\r\n await git([\"clean\", \"-fd\"], cwd);\r\n}\r\n\r\nexport async function discardCheckpoint(checkpoint: Checkpoint): Promise<void> {\r\n if (!checkpoint.isGitRepo || !checkpoint.stashSha) return;\r\n const { cwd, stashSha } = checkpoint;\r\n const shas = await listStashShas(cwd);\r\n const idx = shas.indexOf(stashSha);\r\n if (idx < 0) return;\r\n try { await git([\"stash\", \"drop\", `stash@{${idx}}`], cwd); } catch { /* ignore */ }\r\n}\r\n","import { mkdir, readFile, writeFile, readdir, unlink, rename } from \"node:fs/promises\";\r\nimport { join } from \"node:path\";\r\nimport { randomUUID } from \"node:crypto\";\r\nimport type { StoredSession, SessionSummary } from \"./types.js\";\r\n\r\nexport function defaultSessionsDir(): string {\r\n const home = process.env.HOME ?? process.env.USERPROFILE ?? \"~\";\r\n return join(home, \".dskcode\", \"sessions\");\r\n}\r\n\r\nexport class SessionStore {\r\n readonly #dir: string;\r\n constructor(dir?: string) { this.#dir = dir ?? defaultSessionsDir(); }\r\n get dir(): string { return this.#dir; }\r\n\r\n async save(session: StoredSession): Promise<void> {\r\n await mkdir(this.#dir, { recursive: true });\r\n const finalPath = join(this.#dir, `${session.id}.json`);\r\n const tmpPath = join(this.#dir, `.${session.id}.json.tmp`);\r\n await writeFile(tmpPath, JSON.stringify(session, null, 2), \"utf-8\");\r\n await rename(tmpPath, finalPath);\r\n }\r\n\r\n async load(id: string): Promise<StoredSession | null> {\r\n const path = join(this.#dir, `${id}.json`);\r\n try {\r\n const content = await readFile(path, \"utf-8\");\r\n return JSON.parse(content) as StoredSession;\r\n } catch (err: unknown) {\r\n if (isENOENT(err)) return null;\r\n throw err;\r\n }\r\n }\r\n\r\n async list(): Promise<SessionSummary[]> {\r\n let files: string[];\r\n try { files = await readdir(this.#dir); }\r\n catch (err: unknown) { if (isENOENT(err)) return []; throw err; }\r\n\r\n const results: SessionSummary[] = [];\r\n for (const file of files) {\r\n if (!file.endsWith(\".json\") || file.startsWith(\".\")) continue;\r\n try {\r\n const content = await readFile(join(this.#dir, file), \"utf-8\");\r\n const s = JSON.parse(content) as StoredSession;\r\n results.push({\r\n id: s.id, title: s.title || \"(无标题)\", updatedAt: s.updatedAt,\r\n cwd: s.cwd, messageCount: s.messages?.length ?? 0,\r\n });\r\n } catch { /* skip corrupted */ }\r\n }\r\n return results.sort((a, b) => b.updatedAt - a.updatedAt);\r\n }\r\n\r\n async delete(id: string): Promise<void> {\r\n const path = join(this.#dir, `${id}.json`);\r\n try { await unlink(path); }\r\n catch (err: unknown) { if (!isENOENT(err)) throw err; }\r\n }\r\n\r\n async exists(id: string): Promise<boolean> { return (await this.load(id)) !== null; }\r\n static newId(): string { return randomUUID(); }\r\n}\r\n\r\nfunction isENOENT(err: unknown): boolean {\r\n return typeof err === \"object\" && err !== null && \"code\" in err && (err as { code: unknown }).code === \"ENOENT\";\r\n}\r\n","// ---------------------------------------------------------------------------\r\n// 对话日志记录器 — 将用户与 AI 的完整交互过程写入 JSONL 日志文件\r\n//\r\n// 设计要点:\r\n// 1. JSONL 格式(每行一个 JSON 对象),便于机器解析和流式追加\r\n// 2. 异步写入,错误静默处理,绝不影响主对话流程\r\n// 3. 按会话 ID 分文件,一个会话一个日志文件\r\n// 4. 工具结果数据截断到 MAX_DATA_LEN,避免日志文件膨胀\r\n// 5. 写入操作串行化(队列),保证事件顺序与实际发生顺序一致\r\n// ---------------------------------------------------------------------------\r\n\r\nimport { mkdir, appendFile } from \"node:fs/promises\";\r\nimport { join, basename } from \"node:path\";\r\nimport type { LogEvent } from \"./types.js\";\r\n\r\n/** 工具结果数据的最大记录长度(超出截断并标记) */\r\nconst MAX_DATA_LEN = 2000;\r\n\r\n/** 写入队列最大长度,超过则丢弃最旧的事件(防止极端情况下无限堆积) */\r\nconst MAX_QUEUE = 500;\r\n\r\n/** 默认日志根目录:~/.dskcode/logs */\r\nexport function defaultLogsDir(): string {\r\n const home = process.env.HOME ?? process.env.USERPROFILE ?? \"~\";\r\n return join(home, \".dskcode\", \"logs\");\r\n}\r\n\r\n/** ConversationLogger 构造选项 */\r\nexport interface ConversationLoggerOptions {\r\n /** 是否启用日志记录,默认 true */\r\n enabled?: boolean;\r\n /**\r\n * 日志根目录,默认 ~/.dskcode/logs。\r\n * 实际日志路径为 `<logsDir>/<项目名>/<sessionId>.jsonl`。\r\n * 测试中可指定临时目录。\r\n */\r\n logsDir?: string;\r\n}\r\n\r\n/**\r\n * 从工作目录提取项目名(用于日志分组)。\r\n * 取 cwd 的 basename,根目录等边界情况兜底为 \"_root\"。\r\n */\r\nfunction projectNameFromCwd(cwd: string): string {\r\n const name = basename(cwd);\r\n return name || \"_root\";\r\n}\r\n\r\n/**\r\n * 对话日志记录器。\r\n *\r\n * 每个会话创建一个实例,日志写入 `~/.dskcode/logs/<项目名>/<sessionId>.jsonl`。\r\n * 所有写入操作异步串行执行,调用方无需 await(fire-and-forget)。\r\n */\r\nexport class ConversationLogger {\r\n /** 所有活跃的 logger 实例(用于退出时统一 flush) */\r\n static readonly #instances = new Set<ConversationLogger>();\r\n\r\n /**\r\n * 刷新所有活跃 logger 实例的缓冲,确保事件落盘。\r\n * 进程退出前调用。\r\n */\r\n static async flushAll(): Promise<void> {\r\n const promises: Promise<void>[] = [];\r\n for (const logger of ConversationLogger.#instances) {\r\n promises.push(logger.flush());\r\n }\r\n await Promise.allSettled(promises);\r\n }\r\n\r\n readonly #logPath: string;\r\n readonly #enabled: boolean;\r\n #queue: Promise<void> = Promise.resolve();\r\n #queueLen = 0;\r\n #closed = false;\r\n\r\n constructor(sessionId: string, cwd: string, options?: ConversationLoggerOptions) {\r\n this.#enabled = options?.enabled ?? true;\r\n if (!this.#enabled) {\r\n this.#logPath = \"\";\r\n return;\r\n }\r\n const baseDir = options?.logsDir ?? defaultLogsDir();\r\n const projectName = projectNameFromCwd(cwd);\r\n this.#logPath = join(baseDir, projectName, `${sessionId}.jsonl`);\r\n ConversationLogger.#instances.add(this);\r\n }\r\n\r\n /** 日志文件完整路径(禁用时返回空字符串) */\r\n get logPath(): string {\r\n return this.#logPath;\r\n }\r\n\r\n /** 是否启用日志记录 */\r\n get enabled(): boolean {\r\n return this.#enabled;\r\n }\r\n\r\n /**\r\n * 记录一个事件。\r\n *\r\n * 内部将事件序列化为 JSON 并追加到日志文件。\r\n * 写入操作串行排队,保证事件顺序。调用方无需 await。\r\n */\r\n log(event: LogEvent): void {\r\n if (!this.#enabled || this.#closed) return;\r\n\r\n // 队列过长时丢弃最旧事件,防止内存无限增长\r\n if (this.#queueLen >= MAX_QUEUE) return;\r\n\r\n this.#queueLen++;\r\n this.#queue = this.#queue\r\n .then(() => this.#writeLine(event))\r\n .catch(() => { /* 日志写入失败静默忽略 */ })\r\n .finally(() => this.#queueLen--);\r\n }\r\n\r\n /**\r\n * 关闭日志记录器。\r\n *\r\n * 标记关闭,不再接受新事件,但会等待队列中已有事件写入完成。\r\n * 返回的 Promise resolve 后保证所有已 log 的事件都已落盘。\r\n */\r\n async flush(): Promise<void> {\r\n this.#closed = true;\r\n await this.#queue.catch(() => {});\r\n ConversationLogger.#instances.delete(this);\r\n }\r\n\r\n /** 将一行 JSON 写入日志文件 */\r\n async #writeLine(event: LogEvent): Promise<void> {\r\n const dir = join(this.#logPath, \"..\");\r\n await mkdir(dir, { recursive: true });\r\n const line = JSON.stringify(event) + \"\\n\";\r\n await appendFile(this.#logPath, line, \"utf-8\");\r\n }\r\n\r\n // -----------------------------------------------------------------------\r\n // 便捷方法 — 封装常见事件类型,减少调用方样板代码\r\n // -----------------------------------------------------------------------\r\n\r\n /** 记录会话开始 */\r\n logSessionStart(sessionId: string, cwd: string, model: string, mode: string): void {\r\n this.log({ ts: Date.now(), type: \"session_start\", sessionId, cwd, model, mode });\r\n }\r\n\r\n /** 记录用户消息 */\r\n logUserMessage(content: string): void {\r\n this.log({ ts: Date.now(), type: \"user_message\", content });\r\n }\r\n\r\n /** 记录助手文本(一轮中模型输出的完整文本) */\r\n logAssistantText(content: string, round: number): void {\r\n this.log({ ts: Date.now(), type: \"assistant_text\", content, round });\r\n }\r\n\r\n /** 记录工具调用 */\r\n logToolCall(name: string, callId: string, args: string, round: number): void {\r\n this.log({ ts: Date.now(), type: \"tool_call\", name, callId, arguments: args, round });\r\n }\r\n\r\n /** 记录工具结果 */\r\n logToolResult(\r\n name: string,\r\n callId: string,\r\n success: boolean,\r\n data: string,\r\n error: string | undefined,\r\n elapsed: number | undefined,\r\n round: number,\r\n ): void {\r\n this.log({\r\n ts: Date.now(),\r\n type: \"tool_result\",\r\n name,\r\n callId,\r\n success,\r\n data: truncate(data),\r\n ...(error ? { error } : {}),\r\n ...(elapsed !== undefined ? { elapsed } : {}),\r\n round,\r\n });\r\n }\r\n\r\n /** 记录 Token 用量与费用 */\r\n logUsage(\r\n model: string,\r\n promptTokens: number,\r\n completionTokens: number,\r\n cachedPromptTokens: number | undefined,\r\n cost: number,\r\n round: number,\r\n ): void {\r\n this.log({\r\n ts: Date.now(),\r\n type: \"usage\",\r\n model,\r\n promptTokens,\r\n completionTokens,\r\n ...(cachedPromptTokens !== undefined ? { cachedPromptTokens } : {}),\r\n cost,\r\n round,\r\n });\r\n }\r\n\r\n /** 记录错误 */\r\n logError(message: string, stack?: string): void {\r\n this.log({ ts: Date.now(), type: \"error\", message, ...(stack ? { stack } : {}) });\r\n }\r\n\r\n /** 记录一轮对话完成 */\r\n logTurnDone(elapsed: number, toolRounds: number): void {\r\n this.log({ ts: Date.now(), type: \"turn_done\", elapsed, toolRounds });\r\n }\r\n\r\n /** 记录会话结束 */\r\n logSessionEnd(elapsed: number): void {\r\n this.log({ ts: Date.now(), type: \"session_end\", elapsed });\r\n }\r\n}\r\n\r\n/**\r\n * 截断过长的字符串,超出部分用占位符标记。\r\n */\r\nfunction truncate(text: string): string {\r\n if (text.length <= MAX_DATA_LEN) return text;\r\n return text.slice(0, MAX_DATA_LEN) + `...[已截断,原始长度 ${text.length}]`;\r\n}\r\n","// ---------------------------------------------------------------------------\r\n// Agent 会话 — 消息编排、流式 LLM 调用、工具执行循环、成本追踪、检查点、持久化\r\n// ---------------------------------------------------------------------------\r\n\r\nimport type {\r\n ChatMessage,\r\n ChatOptions,\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, SessionMode, SystemPromptOptions } from \"./types.js\";\r\nimport { buildSystemPrompt, buildPlanSystemPrompt } 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\nimport {\r\n createCheckpoint,\r\n restoreCheckpointForce,\r\n restoreToClean,\r\n discardCheckpoint,\r\n type Checkpoint,\r\n} from \"../checkpoint/index.js\";\r\nimport { SessionStore, type StoredSession } from \"../session-store/index.js\";\r\nimport { ConversationLogger } from \"../logger/index.js\";\r\nimport { calculateCost } from \"../provider/index.js\";\r\n\r\n/** Session 构造选项 */\r\nexport interface SessionOptions {\r\n cwd?: string;\r\n maxToolRounds?: number;\r\n reservedForOutput?: number;\r\n preserveRecentRounds?: number;\r\n projectContext?: string;\r\n gate?: Gate;\r\n writeRoots?: string[];\r\n /**\r\n * 会话 ID。传入则复用,不传则生成新 UUID。\r\n * 使用 Session.resume() 恢复会话时必须传。\r\n */\r\n sessionId?: string;\r\n /**\r\n * 会话存储实例。不传则使用默认 ~/.dskcode/sessions/。\r\n * 传 false 禁用持久化(用于测试和不需要保存的场景)。\r\n */\r\n store?: SessionStore | false;\r\n /** 是否启用 checkpoint(/rewind 需要),默认 true */\r\n enableCheckpoint?: boolean;\r\n /** 是否启用对话日志记录(写入 .dskcode/logs/),默认 true */\r\n enableLog?: boolean;\r\n}\r\n\r\n/** 消息检查点信息(对外暴露) */\r\nexport interface MessageCheckpointInfo {\r\n index: number;\r\n preview: string;\r\n timestamp: number;\r\n isGitRepo: boolean;\r\n}\r\n\r\n/** rewind 操作结果 */\r\nexport type RewindResult =\r\n | { ok: true; fileRestored: boolean }\r\n | { ok: false; error: string };\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[]; enableCheckpoint: boolean; enableLog: boolean };\r\n readonly #abortController = new AbortController();\r\n\r\n readonly #sessionId: string;\r\n readonly #store: SessionStore | null;\r\n #createdAt: number;\r\n #persistTimer: NodeJS.Timeout | null = null;\r\n\r\n #checkpoints = new Map<number, Checkpoint>();\r\n #stormRecords: ToolCallRecord[] = [];\r\n #mode: SessionMode = \"code\";\r\n readonly #logger: ConversationLogger;\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 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 enableCheckpoint: options?.enableCheckpoint ?? true,\r\n enableLog: options?.enableLog ?? true,\r\n };\r\n this.#sessionId = options?.sessionId ?? SessionStore.newId();\r\n this.#store = options?.store === false ? null : (options?.store ?? new SessionStore());\r\n this.#createdAt = Date.now();\r\n this.#logger = new ConversationLogger(this.#sessionId, this.#options.cwd, {\r\n enabled: this.#options.enableLog,\r\n });\r\n this.#logger.logSessionStart(this.#sessionId, this.#options.cwd, this.#provider.model(), this.#mode);\r\n }\r\n\r\n get messages(): readonly ChatMessage[] { return this.#messages; }\r\n get accumulatedCost(): number { return this.#costTracker.sessionTotalCost; }\r\n get costTracker(): CostTracker { return this.#costTracker; }\r\n get model(): string { return this.#provider.model(); }\r\n get toolRegistry(): ToolRegistry { return this.#toolRegistry; }\r\n get mode(): SessionMode { return this.#mode; }\r\n get id(): string { return this.#sessionId; }\r\n get store(): SessionStore | null { return this.#store; }\r\n get createdAt(): number { return this.#createdAt; }\r\n\r\n setMode(mode: SessionMode): SessionMode { this.#mode = mode; return this.#mode; }\r\n\r\n async *chat(userInput: string, opts?: ChatOptions): AsyncGenerator<AgentEvent> {\r\n this.#messages.push({ role: \"user\", content: userInput });\r\n this.#logger.logUserMessage(userInput);\r\n const userMsgIndex = this.#messages.length - 1;\r\n if (this.#options.enableCheckpoint) {\r\n try {\r\n const checkpoint = await createCheckpoint(this.#options.cwd);\r\n this.#checkpoints.set(userMsgIndex, checkpoint);\r\n } catch { /* swallow */ }\r\n }\r\n\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 const systemPrompt = this.#buildSystemPrompt();\r\n const [trimmed] = trimMessages(\r\n [...this.#messages],\r\n {\r\n model: this.#provider.model() as unknown 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 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 thinkingAllowed: opts?.thinkingAllowed,\r\n thinkingEffort: opts?.thinkingEffort,\r\n responseFormat: opts?.responseFormat,\r\n toolChoice: opts?.toolChoice,\r\n });\r\n\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 if (chunk.toolCalls && chunk.toolCalls.length > 0) lastToolCalls = chunk.toolCalls;\r\n if (chunk.usage) lastUsage = chunk.usage;\r\n if (chunk.finishReason) _lastFinishReason = chunk.finishReason;\r\n }\r\n\r\n if (lastUsage) {\r\n const modelId = this.#provider.model() as unknown as ModelId;\r\n this.#costTracker.record(lastUsage, modelId);\r\n const cost = calculateCost(lastUsage, modelId);\r\n this.#logger.logUsage(\r\n modelId, lastUsage.promptTokens, lastUsage.completionTokens,\r\n lastUsage.cachedPromptTokens, cost.totalCost, toolRounds,\r\n );\r\n yield { type: \"usage\", usage: lastUsage, model: modelId };\r\n }\r\n\r\n const assistantMsg: ChatMessage = { role: \"assistant\", content: accumulatedText };\r\n if (lastToolCalls && lastToolCalls.length > 0) assistantMsg.toolCalls = lastToolCalls;\r\n this.#messages.push(assistantMsg);\r\n this.#logger.logAssistantText(accumulatedText, toolRounds);\r\n\r\n if (lastToolCalls && lastToolCalls.length > 0) {\r\n yield { type: \"tool_calls\", calls: lastToolCalls };\r\n for (const tc of lastToolCalls) {\r\n this.#logger.logToolCall(tc.name, tc.id, tc.arguments, toolRounds);\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 assistantMsg.toolCalls = undefined;\r\n assistantMsg.content += stormMsg;\r\n this.#stormRecords = [];\r\n toolRounds++;\r\n continue;\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 this.#logger.logToolResult(\r\n item.name, item.callId, item.result.success,\r\n item.result.data, item.result.error, undefined, toolRounds,\r\n );\r\n let toolContent = item.result.data;\r\n if (item.result.diff && item.result.diff.patch) toolContent += `\\n\\n${item.result.diff.patch}`;\r\n this.#messages.push({\r\n role: \"tool\", content: toolContent,\r\n toolCallId: item.callId, name: item.name,\r\n });\r\n }\r\n\r\n toolRounds++;\r\n continue;\r\n }\r\n\r\n break;\r\n }\r\n } catch (err: unknown) {\r\n if (err instanceof DOMException && err.name === \"AbortError\") return;\r\n if (err instanceof Error && err.name === \"AbortError\") return;\r\n this.#logger.logError(\r\n err instanceof Error ? err.message : String(err),\r\n err instanceof Error ? err.stack : undefined,\r\n );\r\n yield { type: \"error\", error: err instanceof Error ? err : new Error(String(err)) };\r\n return;\r\n }\r\n\r\n const elapsed = Date.now() - startTime;\r\n this.#logger.logTurnDone(elapsed, toolRounds);\r\n void this.#persist();\r\n // 持久化今日成本数据,确保进程退出后重开不会丢失\r\n await this.#costTracker.flush().catch(() => {});\r\n yield { type: \"done\", elapsed };\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 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 const MAX_PARALLEL = 8;\r\n const items: Array<{ name: string; callId: string; result: ToolResult }> = [];\r\n const records: ToolCallRecord[] = [];\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 return { items, records };\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 return { items, records };\r\n }\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 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 let toolArgs: unknown;\r\n try { toolArgs = tc.arguments ? JSON.parse(tc.arguments) : {}; } catch { toolArgs = {}; }\r\n\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 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 { await maybePreview(toolArgs, ctx); } catch { /* ignore */ }\r\n }\r\n }\r\n\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: { name: toolName, success: result.success, error: result.error, timestamp },\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 #checkStormBreak(currentCalls: ProviderToolCall[]): boolean {\r\n if (this.#stormRecords.length < 3) return false;\r\n const recentErrors = this.#stormRecords.slice(-3);\r\n if (recentErrors.length < 3) return false;\r\n const first = recentErrors[0]!;\r\n const allSame = recentErrors.every((r) => r.name === first.name && r.error === first.error && !r.success);\r\n if (!allSame) return false;\r\n return currentCalls.some((tc) => tc.name === first.name);\r\n }\r\n\r\n abort(): void {\r\n this.#abortController.abort();\r\n if (this.#persistTimer) { clearTimeout(this.#persistTimer); this.#persistTimer = null; }\r\n }\r\n\r\n /** 刷新日志缓冲,确保所有已记录的事件落盘 */\r\n async flushLog(): Promise<void> {\r\n await this.#logger.flush();\r\n }\r\n\r\n reset(): void {\r\n this.#messages.length = 0;\r\n this.#costTracker.resetSession();\r\n this.#stormRecords = [];\r\n this.#checkpoints.clear();\r\n }\r\n\r\n // -------------------------------------------------------------------------\r\n // 持久化与恢复\r\n // -------------------------------------------------------------------------\r\n\r\n async persistNow(): Promise<void> {\r\n if (this.#persistTimer) { clearTimeout(this.#persistTimer); this.#persistTimer = null; }\r\n await this.#doPersist();\r\n }\r\n\r\n #persist(): void {\r\n if (!this.#store) return;\r\n if (this.#persistTimer) { this.#persistTimer.refresh(); return; }\r\n this.#persistTimer = setTimeout(() => {\r\n this.#persistTimer = null;\r\n void this.#doPersist();\r\n }, 500);\r\n this.#persistTimer.unref();\r\n }\r\n\r\n async #doPersist(): Promise<void> {\r\n if (!this.#store) return;\r\n const stored: StoredSession = {\r\n id: this.#sessionId,\r\n title: this.#deriveTitle(),\r\n createdAt: this.#createdAt,\r\n updatedAt: Date.now(),\r\n cwd: this.#options.cwd,\r\n model: this.#provider.model(),\r\n messages: this.#serializeMessages(),\r\n totalCost: this.#costTracker.sessionTotalCost,\r\n };\r\n try { await this.#store.save(stored); }\r\n catch (err) { console.error(\"[Session] 持久化失败:\", err); }\r\n }\r\n\r\n #deriveTitle(): string {\r\n for (const m of this.#messages) {\r\n if (m.role === \"user\" && m.content.trim()) return m.content.trim().slice(0, 40);\r\n }\r\n return \"新会话\";\r\n }\r\n\r\n #serializeMessages(): StoredSession[\"messages\"] {\r\n return this.#messages.map((msg, idx) => {\r\n const checkpoint = this.#checkpoints.get(idx);\r\n if (msg.role === \"user\" && checkpoint) return { ...msg, checkpoint };\r\n return { ...msg };\r\n });\r\n }\r\n\r\n static async resume(\r\n id: string,\r\n provider: Provider,\r\n tools: AnyAgentTool[] | ToolRegistry = [],\r\n costTracker?: CostTracker,\r\n options?: SessionOptions,\r\n ): Promise<Session> {\r\n const store = options?.store === false ? null : (options?.store ?? new SessionStore());\r\n if (!store) throw new Error(\"resume 需要启用持久化(options.store 不能为 false)\");\r\n const stored = await store.load(id);\r\n if (!stored) throw new Error(`会话 ${id} 不存在`);\r\n\r\n const session = new Session(provider, tools, costTracker, { ...options, sessionId: id, store });\r\n for (const m of stored.messages) {\r\n session.#messages.push({\r\n role: m.role, content: m.content,\r\n toolCallId: m.toolCallId, name: m.name, toolCalls: m.toolCalls,\r\n });\r\n }\r\n for (let i = 0; i < stored.messages.length; i++) {\r\n const cp = stored.messages[i]?.checkpoint;\r\n if (cp) session.#checkpoints.set(i, cp);\r\n }\r\n session.#createdAt = stored.createdAt;\r\n return session;\r\n }\r\n\r\n // -------------------------------------------------------------------------\r\n // 检查点与 Rewind\r\n // -------------------------------------------------------------------------\r\n\r\n listCheckpoints(): MessageCheckpointInfo[] {\r\n const result: MessageCheckpointInfo[] = [];\r\n for (const [index, checkpoint] of this.#checkpoints) {\r\n const msg = this.#messages[index];\r\n if (!msg || msg.role !== \"user\") continue;\r\n result.push({ index, preview: msg.content.slice(0, 80), timestamp: checkpoint.timestamp, isGitRepo: checkpoint.isGitRepo });\r\n }\r\n return result.sort((a, b) => a.index - b.index);\r\n }\r\n\r\n async rewind(targetIndex: number): Promise<RewindResult> {\r\n if (targetIndex < 0 || targetIndex >= this.#messages.length) {\r\n return { ok: false, error: `无效的消息索引 ${targetIndex}` };\r\n }\r\n const target = this.#messages[targetIndex];\r\n if (!target || target.role !== \"user\") {\r\n return { ok: false, error: `索引 ${targetIndex} 不是 user 消息` };\r\n }\r\n const checkpoint = this.#checkpoints.get(targetIndex);\r\n if (!checkpoint) return { ok: false, error: \"该消息没有检查点\" };\r\n\r\n this.#messages.length = targetIndex + 1;\r\n const toDiscard: Checkpoint[] = [];\r\n for (const [idx, cp] of this.#checkpoints) {\r\n if (idx > targetIndex) { toDiscard.push(cp); this.#checkpoints.delete(idx); }\r\n }\r\n\r\n let fileRestored = false;\r\n if (checkpoint.isGitRepo) {\r\n try {\r\n if (checkpoint.stashSha) {\r\n // 目标检查点有 stash 快照——恢复该快照\r\n await restoreCheckpointForce(checkpoint);\r\n } else {\r\n // 目标检查点 stashSha 为空,代表「那一刻工作区就是 HEAD 干净状态」。\r\n // 但后续对话产生的修改可能还积累在工作区,需要丢弃才能真正回退到那一刻。\r\n await restoreToClean(this.#options.cwd);\r\n }\r\n fileRestored = true;\r\n } catch (err) {\r\n const msg = err instanceof Error ? err.message : String(err);\r\n return { ok: false, error: `对话已截断但文件恢复失败:${msg}` };\r\n }\r\n }\r\n\r\n // restoreCheckpointForce 内部已 drop 了该 checkpoints 的 stash entry,\r\n // 所以只需从 Map 中移除即可,无需再 discardCheckpoint。\r\n this.#checkpoints.delete(targetIndex);\r\n for (const cp of toDiscard) { void discardCheckpoint(cp); }\r\n this.#persist();\r\n return { ok: true, fileRestored };\r\n }\r\n\r\n hasCheckpoints(): boolean { return this.listCheckpoints().length > 0; }\r\n\r\n async delete(): Promise<void> {\r\n if (this.#store) await this.#store.delete(this.#sessionId);\r\n for (const cp of this.#checkpoints.values()) { void discardCheckpoint(cp); }\r\n this.#checkpoints.clear();\r\n this.#logger.logSessionEnd(Date.now() - this.#createdAt);\r\n await this.#logger.flush();\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, description: t.description,\r\n parameters: t.parameters as unknown as Record<string, unknown>,\r\n }));\r\n const opts: SystemPromptOptions = {\r\n model: this.#provider.model(), 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 if (this.#mode === \"plan\") return buildPlanSystemPrompt(opts);\r\n return buildSystemPrompt(opts);\r\n }\r\n\r\n #buildToolDefinitions(): ToolDefinition[] {\r\n const tools = this.#mode === \"plan\" ? this.#toolRegistry.listReadTools() : this.#toolRegistry.list();\r\n return tools.map((t) => ({\r\n type: \"function\" as const,\r\n function: { name: t.name, description: t.description, parameters: t.parameters as unknown as Record<string, unknown> },\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\r\n // cmd.exe 默认用 GBK 输出中文错误,会在 UTF-8 终端里变成乱码。\r\n // 所以在 cmd 这条路径下,把命令带上 `chcp 65001 >nul &&` 前缀\r\n // 切到 UTF-8 代码页,stderr 才能被 LLM 读懂。\r\n // 启动期探测出的 SHELL_INFO 已不允许这种耗时切换,能在 git bash 赏i\r\n // 就不进 cmd 这条分支;这里作为“找不到 Git Bash 时的兒底”出现。\r\n //\r\n // 注意:chcp 切换只对 cmd.exe /c 调用里可见,不会污染外层进程。\r\n if (isWindows && spawnCmd === \"cmd\" && spawnArgs[0] === \"/c\") {\r\n const prefixed = `chcp 65001 >nul && ${spawnArgs[1] ?? \"\"}`;\r\n spawnArgs = [\"/c\", prefixed];\r\n }\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(\"utf8\");\r\n });\r\n\r\n child.stderr.on(\"data\", (data: Buffer) => {\r\n stderr += data.toString(\"utf8\");\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 // eslint-disable-next-line unicorn/no-new-array, @typescript-eslint/no-unsafe-type-assertion\r\n const V: number[] = new Array(2 * maxD + 2).fill(-1) as number[];\r\n // k 的偏移量,使索引非负\r\n const offset = maxD;\r\n\r\n V[offset + 1] = 0;\r\n\r\n // 正向搜索:从 d=0 开始,逐层扩展\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 = trace[d - 1]!;\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]! });\r\n }\r\n\r\n // 第二步:删除行\r\n if (x > prevX) {\r\n x--;\r\n result.unshift({ op: \"remove\", line: oldLines[x]! });\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]! });\r\n }\r\n\r\n // 第二步:添加行\r\n if (y > prevY) {\r\n y--;\r\n result.unshift({ op: \"add\", line: newLines[y]! });\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]! });\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]!;\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]!]];\r\n\r\n for (let idx = 1; idx < changeIndices.length; idx++) {\r\n const lastGroup = groups[groups.length - 1]!;\r\n const prevIdx = lastGroup[lastGroup.length - 1]!;\r\n const currIdx = changeIndices[idx]!;\r\n\r\n let gap = 0;\r\n for (let k = prevIdx + 1; k < currIdx; k++) {\r\n const line = diffLines[k]!;\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]!;\r\n const lastChangeIdx = group[group.length - 1]!;\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]!;\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 = 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] === 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]! };\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 { existsSync } from \"node:fs\";\r\nimport { join } from \"node:path\";\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\ninterface DetectResult {\r\n /** shell 可执行文件路径,Windows 未找到 Git Bash 时为 null */\r\n bin: string | null;\r\n /** shell 调用参数前缀,bash 用 [\"-c\"],cmd 用 [\"/c\"] */\r\n args: string[];\r\n /** 给 LLM 看的 shell 名称 */\r\n label: string;\r\n /** 给 LLM 看的语法指引 */\r\n hint: string;\r\n}\r\n\r\n/**\r\n * 探测本机可用的 bash。\r\n *\r\n * Windows 上优先 Git Bash(命令语法与 Unix 一致,跨平台命令可复用),\r\n * 找不到才回退 cmd.exe。这样 LLM 生成的 `pwd && ls` 在装有 Git Bash 的\r\n * Windows 机器上能直接跑通,不再像旧的“硬走 cmd”实现那样在中文\r\n * GBK 报错里循环重试(见曾经的日志\r\n * e3cb5aff-34c0-499a-a3be-dee13c18d14f 中 round 2-6 的 EXIT_CODE_1 风暴)。\r\n */\r\nfunction detectShell(): DetectResult {\r\n if (!isWindows) {\r\n return {\r\n bin: \"sh\",\r\n args: [\"-c\"],\r\n label: \"sh\",\r\n hint: \"当前 shell 是 bash 兼容语法,支持 pwd/ls/&&/管道等标准 Unix 工具。\",\r\n };\r\n }\r\n\r\n // 1. 用户显式指定的 bash 路径(优先级最高)\r\n const envBash = process.env.DSKCODE_BASH;\r\n if (envBash && existsSync(envBash)) {\r\n return { bin: envBash, args: [\"-c\"], label: \"Git Bash\", hint: SHELL_HINT_BASH };\r\n }\r\n\r\n // 2. 常见 Git for Windows 安装位置(C/D 盘 × Program Files × LocalAppData)\r\n // 同时覆盖 64 位默认目录与 32 位兼容目录,以及单用户安装。\r\n const roots = [\r\n process.env.ProgramFiles,\r\n process.env[\"ProgramFiles(x86)\"],\r\n process.env.LOCALAPPDATA ? join(process.env.LOCALAPPDATA, \"Programs\") : null,\r\n // 非 C 盘安装(你机器就是 D 盘),枚举几个常见盘符根\r\n \"D:\\\\Program Files\",\r\n \"E:\\\\Program Files\",\r\n ].filter((v): v is string => Boolean(v));\r\n\r\n for (const root of roots) {\r\n for (const sub of [\"Git\\\\bin\\\\bash.exe\", \"Git\\\\usr\\\\bin\\\\bash.exe\"]) {\r\n const candidate = join(root, sub);\r\n if (existsSync(candidate)) {\r\n return { bin: candidate, args: [\"-c\"], label: \"Git Bash\", hint: SHELL_HINT_BASH };\r\n }\r\n }\r\n }\r\n\r\n // 3. 回退 cmd.exe —— 命令语法不同,要在 hint 里明确告知 LLM\r\n return {\r\n bin: \"cmd\",\r\n args: [\"/c\"],\r\n label: \"cmd.exe\",\r\n hint:\r\n \"当前 shell 是 Windows cmd.exe。请使用 cmd 语法:路径用反斜杠或正斜杠均可、命令链用 & 或换行,避免使用 pwd/ls/&& 等 Unix 命令。\",\r\n };\r\n}\r\n\r\n/** Git Bash 检出后给 LLM 的语法提示 */\r\nconst SHELL_HINT_BASH =\r\n \"当前 shell 是 bash 兼容语法,支持 pwd/ls/&&/管道等标准 Unix 工具。\";\r\n\r\nconst SHELL_INFO = detectShell();\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/** 仅供测试导出当前探测到的 shell 信息(不参与运行期逻辑) */\r\nexport const __shellInfo = SHELL_INFO;\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 中执行命令,返回 stdout / stderr / 退出码。支持超时控制和信号中止。\",\r\n `当前 shell:${SHELL_INFO.label}。${SHELL_INFO.hint}`,\r\n \"读取文件请优先使用 read_file 工具,不要用 cat/type/Get-Content;\",\r\n \"搜索代码用 grep / glob,列目录用 ls——这些专用工具在所有平台都可用且无需关心 shell 语法。\",\r\n ].join(\"\"),\r\n parameters: {\r\n type: \"object\",\r\n properties: {\r\n command: {\r\n type: \"string\",\r\n description: `要执行的 shell 命令(当前 shell:${SHELL_INFO.label})`,\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 // shell 已在启动期按平台探测好:Windows 走 Git Bash,没有才回退 cmd\r\n const shellCommand = SHELL_INFO.bin ?? \"cmd\";\r\n const shellArgs = [...SHELL_INFO.args, 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 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, 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, 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].toSorted((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 // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\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]!;\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]}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\n// const 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)]!; }\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]!;\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)]!;\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]!;\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]!;\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 } 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 // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\r\n row += cellColor(char, cellType as unknown as 'snake-head' | 'snake-body' | 'normal-food' | 'special-food' | 'empty');\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, useMemo } 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 // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\r\n const json = (await resp.json()) as unknown 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).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 // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\r\n const qt = stockData.qt?.[qtKey] as unknown 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 // o 键排序:默认 → 降序 → 升序 → 默认\r\n type SortOrder = \"default\" | \"asc\" | \"desc\";\r\n const [sortOrder, setSortOrder] = useState<SortOrder>(\"default\");\r\n\r\n const sortedStocks = useMemo(() => {\r\n if (sortOrder === \"default\") return stocks;\r\n return [...stocks].toSorted((a, b) =>\r\n sortOrder === \"desc\"\r\n ? b.changePercent - a.changePercent\r\n : a.changePercent - b.changePercent,\r\n );\r\n }, [stocks, sortOrder]);\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 void 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 void 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 const loadDetail = () => {\r\n // 清除缓存,强制走网络\r\n minuteCache.delete(detailView.code);\r\n void 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 }\r\n\r\n setDetailPrices(null);\r\n setDetailLoading(false);\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 void loadData();\r\n } else if (input === \"o\") {\r\n setSortOrder((prev) => (prev === \"default\" ? \"desc\" : prev === \"desc\" ? \"asc\" : \"default\"));\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>\r\n 涨跌幅{sortOrder === \"desc\" ? \" ▼\" : sortOrder === \"asc\" ? \" ▲\" : \"\"}\r\n </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 {sortedStocks.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 手动刷新 o 排序 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\nimport { CostTracker } from \"./provider/cost-tracker.js\";\r\nimport { ConversationLogger } from \"./logger/index.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\n// eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents\r\nlet sigintTimer: NodeJS.Timeout | null = null;\r\n\r\n/**\r\n * 全局兜底 SIGINT 处理:双击退出。\r\n * ink 交互模式下 Ctrl+C 由 useDoubleCtrlC hook 处理,\r\n * 此处仅处理 ink 未运行时的场景(如启动阶段、异常退出后)。\r\n *\r\n * 第二次 SIGINT / 任意 SIGTERM / 正常退出前都会统一 flush 所有\r\n * CostTracker 实例,确保今日用量数据落盘。\r\n */\r\nasync function gracefulExit(code: number): Promise<never> {\r\n try {\r\n await Promise.all([\r\n CostTracker.flushAll(),\r\n ConversationLogger.flushAll(),\r\n ]);\r\n } catch {\r\n // 兜底路径,flush 失败不影响退出码\r\n }\r\n process.exit(code);\r\n}\r\n\r\nprocess.on(\"SIGINT\", () => {\r\n sigintCount++;\r\n if (sigintCount >= 2) {\r\n void gracefulExit(ExitCode.SIGINT);\r\n return;\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\nprocess.on(\"SIGTERM\", () => {\r\n void gracefulExit(ExitCode.SIGINT);\r\n});\r\n\r\nconst program = createCli();\r\n\r\ntry {\r\n await program.parseAsync(process.argv);\r\n // 正常执行完毕后也兜底 flush(一般 record() 内部已 fire-and-forget,\r\n // 这里保证若本次没有任何 record 调用也不漏盘)\r\n await Promise.all([CostTracker.flushAll(), ConversationLogger.flushAll()]);\r\n} catch (err: unknown) {\r\n // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\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 await Promise.all([CostTracker.flushAll(), ConversationLogger.flushAll()]);\r\n process.exit(error.exitCode ?? ExitCode.SUCCESS);\r\n }\r\n\r\n if (typeof error.exitCode === \"number\") {\r\n await Promise.all([CostTracker.flushAll(), ConversationLogger.flushAll()]);\r\n process.exit(error.exitCode);\r\n }\r\n\r\n console.error(String(err));\r\n await Promise.all([CostTracker.flushAll(), ConversationLogger.flushAll()]);\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;AAEtB,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;AAEhC,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;AAE1C,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;AAE5C,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;AAGN,iBAAa,gBAAgB,aAAa;AAAA,EAC5C;AAIA,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;AAEN,iBAAa,gBAAgB,aAAa;AAAA,EAC5C;AAIA,QAAM,YAAa,WAAW,aAA2C,CAAC;AAE1E,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;AAEN,iBAAa,gBAAgB,aAAa;AAAA,EAC5C;AAGA,aAAW,QAAQ,EAAE,QAAQ;AAG7B,QAAM,UAAU,YAAY,KAAK,UAAU,YAAY,MAAM,CAAC,GAAG,OAAO;AAExE,SAAO;AACT;;;ACzkBA,eAAsB,uBAEK;AAEzB,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;;;AClEA,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,cAAY;;;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;;;ACZR,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,OAAsC,IAAI;AAC3D,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;;;ADlCA,OAAOC,iBAAgB;;;AELvB,SAAS,OAAAC,MAAK,QAAAC,aAAY;;;ACA1B,SAAS,SAAAC,QAAO,YAAAC,WAAU,aAAAC,kBAAiB;AAC3C,SAAS,QAAAC,aAAY;AAiHd,IAAM,cAAN,MAAM,aAAY;AAAA;AAAA,EAEvB,OAAgB,aAAa,oBAAI,IAAiB;AAAA;AAAA,EAGlD,aAAa,WAA0B;AACrC,UAAM,QAAQ;AAAA,MACZ,CAAC,GAAG,aAAY,UAAU,EAAE,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC;AAAA,IAClD;AAAA,EACF;AAAA,EAES;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;AAGlD,iBAAY,WAAW,IAAI,IAAI;AAAA,EACjC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,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;AAAA,EAWA,MAAM,OAAsB;AAC1B,UAAM,KAAK,eAAe;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;AAAA,EAOA,MAAM,QAAuB;AAC3B,QAAI,CAAC,KAAK,UAAU,KAAK,iBAAkB;AAC3C,SAAK,mBAAmB;AACxB,SAAK,SAAS;AAEd,QAAI;AACF,YAAM,KAAK,eAAe;AAC1B,YAAM,KAAK,WAAW;AAAA,IACxB,SAAS,KAAK;AAEZ,WAAK,SAAS;AACd,cAAQ,MAAM,qCAA2B,GAAG;AAAA,IAC9C,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,EAMA,MAAM,UAAyB;AAC7B,iBAAY,WAAW,OAAO,IAAI;AAClC,UAAM,KAAK,MAAM;AAAA,EACnB;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;AAE5C,aAAO,KAAK,MAAM,GAAG;AAAA,IACvB,QAAQ;AACN,aAAO,EAAE,SAAS,GAAG,OAAO,CAAC,EAAE;AAAA,IACjC;AAAA,EACF;AAAA;AAAA,EAGA,MAAM,iBAAgC;AACpC,UAAMC,OAAM,KAAK,UAAU,EAAE,WAAW,KAAK,CAAC;AAAA,EAChD;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;AAEA,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;;;ACngBA,SAAS,OAAAC,MAAK,QAAAC,aAAY;AA4ClB,SAIA,OAAAC,MAJA,QAAAC,aAAA;AA/BR,SAAS,kBAAkB,MAAsB;AAC/C,MAAI;AAEF,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;;;ACrCA,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;;;ACpKA,SAAS,OAAAG,MAAK,QAAAC,aAAY;AAuSpB,gBAAAC,MACE,QAAAC,aADF;AAlRN,SAAS,gBAAgB,MAAyB;AAChD,QAAM,WAAsB,CAAC;AAC7B,MAAI,UAAU;AAEd,SAAO,UAAU,KAAK,QAAQ;AAC5B,UAAM,UAAU,KAAK,QAAQ,OAAO,OAAO;AAE3C,QAAI,YAAY,IAAI;AAClB,eAAS,KAAK,EAAE,MAAM,KAAK,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC;AAC1D;AAAA,IACF;AAEA,QAAI,UAAU,SAAS;AACrB,eAAS,KAAK,EAAE,MAAM,KAAK,MAAM,SAAS,OAAO,GAAG,MAAM,QAAQ,CAAC;AAAA,IACrE;AAEA,UAAM,WAAW,KAAK,QAAQ,OAAO,UAAU,CAAC;AAEhD,QAAI,aAAa,IAAI;AAEnB,eAAS,KAAK,EAAE,MAAM,KAAK,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC;AAC1D;AAAA,IACF;AAGA,UAAM,cAAc,KAAK,MAAM,UAAU,GAAG,QAAQ;AACpD,aAAS,KAAK,EAAE,MAAM,aAAa,MAAM,aAAa,CAAC;AACvD,cAAU,WAAW;AAAA,EACvB;AAEA,SAAO;AACT;AAOA,SAAS,WAAW,MAAuB;AACzC,QAAM,IAAI,KAAK,KAAK;AACpB,SAAO,WAAW,KAAK,CAAC;AAC1B;AAGA,SAAS,cAAc,MAAuB;AAC5C,QAAM,IAAI,KAAK,KAAK;AAEpB,SAAO,gBAAgB,KAAK,CAAC,KAAK,EAAE,SAAS,GAAG;AAClD;AAUA,SAAS,YAAY,MAAyB;AAC5C,QAAM,QAAQ,KAAK,MAAM,IAAI;AAC7B,QAAM,SAAoB,CAAC;AAC3B,MAAI,WAAW;AAEf,WAAS,aAAa;AACpB,QAAI,UAAU;AACZ,aAAO,KAAK,EAAE,MAAM,UAAU,MAAM,QAAQ,CAAC;AAC7C,iBAAW;AAAA,IACb;AAAA,EACF;AAEA,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,UAAM,OAAO,MAAM,CAAC,KAAK;AAEzB,QACE,WAAW,IAAI,KACf,IAAI,IAAI,MAAM,UACd,cAAc,MAAM,IAAI,CAAC,KAAK,EAAE,KAChC,IAAI,IAAI,MAAM,UACd,WAAW,MAAM,IAAI,CAAC,KAAK,EAAE,GAC7B;AACA,iBAAW;AAGX,YAAM,gBAA0B,CAAC;AACjC,UAAI,IAAI;AACR,aAAO,IAAI,MAAM,UAAU,WAAW,MAAM,CAAC,KAAK,EAAE,GAAG;AACrD,sBAAc,KAAK,MAAM,CAAC,CAAE;AAC5B;AAAA,MACF;AAEA,aAAO,KAAK,EAAE,MAAM,cAAc,KAAK,IAAI,GAAG,MAAM,QAAQ,CAAC;AAC7D,UAAI,IAAI;AAAA,IACV,OAAO;AACL,UAAI,SAAU,aAAY,OAAO;AAAA,UAC5B,YAAW;AAAA,IAClB;AAAA,EACF;AAEA,aAAW;AACX,SAAO;AACT;AAUA,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;AAYA,SAAS,gBAAgB,MAAyB;AAChD,QAAM,SAAoB,CAAC;AAC3B,MAAI,UAAU;AAEd,SAAO,UAAU,KAAK,QAAQ;AAC5B,UAAM,UAAU,KAAK,QAAQ,KAAK,OAAO;AAEzC,QAAI,YAAY,IAAI;AAClB,aAAO,KAAK,EAAE,MAAM,KAAK,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC;AACxD;AAAA,IACF;AAEA,QAAI,UAAU,SAAS;AACrB,aAAO,KAAK,EAAE,MAAM,KAAK,MAAM,SAAS,OAAO,GAAG,MAAM,QAAQ,CAAC;AAAA,IACnE;AAGA,QAAI,YAAY;AAChB,WACE,UAAU,YAAY,KAAK,UAC3B,KAAK,UAAU,SAAS,MAAM,KAC9B;AACA;AAAA,IACF;AAEA,QAAI,cAAc,GAAG;AAEnB,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,OAAO;AAEL,aAAO,KAAK;AAAA,QACV,MAAM,KAAK,MAAM,SAAS,UAAU,SAAS;AAAA,QAC7C,MAAM;AAAA,MACR,CAAC;AACD,gBAAU,UAAU;AAAA,IACtB;AAAA,EACF;AAEA,SAAO;AACT;AAUA,SAAS,eAAe,UAAgC;AACtD,QAAM,SAAoB,CAAC;AAE3B,aAAW,OAAO,UAAU;AAC1B,QAAI,IAAI,SAAS,gBAAgB,IAAI,SAAS,SAAS;AACrD,aAAO,KAAK,GAAG;AACf;AAAA,IACF;AAGA,UAAM,QAAQ,gBAAgB,IAAI,IAAI;AAEtC,eAAW,QAAQ,OAAO;AACxB,UAAI,KAAK,SAAS,QAAQ;AAExB,eAAO,KAAK,IAAI;AAAA,MAClB,WAAW,IAAI,SAAS,QAAQ;AAE9B,eAAO,KAAK,EAAE,MAAM,KAAK,MAAM,MAAM,OAAO,CAAC;AAAA,MAC/C,OAAO;AAEL,eAAO,KAAK,IAAI;AAAA,MAClB;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAOA,IAAM,iBAAiB;AAEvB,IAAM,iBAAiB;AAEvB,IAAM,kBAAkB;AAQxB,SAAS,kBAAkB,EAAE,KAAK,GAAgC;AAChE,QAAM,QAAQ,KAAK,MAAM,IAAI;AAG7B,QAAM,YAAY,MAAM,CAAC,KAAK;AAC9B,QAAM,cACJ,UAAU,KAAK,EAAE,SAAS,KAC1B,CAAC,UAAU,WAAW,GAAG,KACzB,CAAC,UAAU,WAAW,GAAG,KACzB,CAAC,UAAU,WAAW,IAAI,KAC1B,CAAC,UAAU,WAAW,GAAG;AAE3B,QAAM,OAAO,cAAc,YAAY;AACvC,QAAM,YAAY,cAAc,MAAM,MAAM,CAAC,IAAI;AAGjD,MAAI,UAAU,WAAW,GAAG;AAC1B,WAAO,cACL,gBAAAD,KAACF,MAAA,EAAI,YAAY,GACf,0BAAAG,MAACF,OAAA,EAAK,OAAM,WAAU;AAAA;AAAA,MAAG;AAAA,OAAK,GAChC,IACE;AAAA,EACN;AAEA,SACE,gBAAAE,MAACH,MAAA,EAAI,eAAc,UAAS,YAAY,GAAG,WAAW,GACnD;AAAA,YAAQ,gBAAAG,MAACF,OAAA,EAAK,OAAM,WAAU;AAAA;AAAA,MAAG;AAAA,OAAK;AAAA,IACtC,UAAU,IAAI,CAAC,MAAM,MAAM;AAC1B,UAAI,KAAK,WAAW,GAAG,GAAG;AACxB,eACE,gBAAAC,KAACD,OAAA,EAAa,OAAO,gBAClB,kBADQ,CAEX;AAAA,MAEJ;AACA,UAAI,KAAK,WAAW,GAAG,GAAG;AACxB,eACE,gBAAAC,KAACD,OAAA,EAAa,OAAO,gBAClB,kBADQ,CAEX;AAAA,MAEJ;AACA,UAAI,KAAK,WAAW,IAAI,GAAG;AACzB,eACE,gBAAAC,KAACD,OAAA,EAAa,OAAO,iBAClB,kBADQ,CAEX;AAAA,MAEJ;AAEA,aAAO,gBAAAC,KAACD,OAAA,EAAc,kBAAJ,CAAS;AAAA,IAC7B,CAAC;AAAA,KACH;AAEJ;AAOA,IAAM,kBAAkB;AAExB,IAAM,sBAAsB;AAM5B,SAAS,UAAU,IAAoB;AACrC,QAAMG,MAAK,GAAG,YAAY,CAAC;AAC3B,MAAIA,QAAO,OAAW,QAAO;AAG7B,MACGA,OAAM,QAAUA,OAAM;AAAA,EACvBA,QAAO,QAAUA,QAAO,QACvBA,OAAM,SAAUA,OAAM;AAAA,EACtBA,OAAM,SAAUA,OAAM;AAAA,EACtBA,OAAM,SAAUA,OAAM;AAAA,EACtBA,OAAM,SAAUA,OAAM;AAAA,EACtBA,OAAM,SAAUA,OAAM;AAAA,EACtBA,OAAM,SAAUA,OAAM;AAAA,EACtBA,OAAM,SAAUA,OAAM,SACtBA,OAAM,UAAWA,OAAM,QACxB;AACA,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAGA,SAAS,YAAY,MAAsB;AACzC,MAAI,IAAI;AACR,aAAW,MAAM,MAAM;AACrB,SAAK,UAAU,EAAE;AAAA,EACnB;AACA,SAAO;AACT;AAGA,SAAS,WAAW,MAAc,aAAqB,OAA4C;AACjG,QAAM,KAAK,YAAY,IAAI;AAC3B,QAAM,QAAQ,cAAc;AAC5B,MAAI,SAAS,EAAG,QAAO;AAEvB,QAAM,UAAU,UAAU,UAAU,QAAQ,UAAU,WAAW,KAAK,MAAM,QAAQ,CAAC,IAAI;AACzF,QAAM,WAAW,QAAQ;AAEzB,SAAO,IAAI,OAAO,OAAO,IAAI,OAAO,IAAI,OAAO,QAAQ;AACzD;AAGA,SAAS,gBAAgB,MAAwB;AAC/C,QAAM,IAAI,KAAK,KAAK;AAEpB,QAAM,QAAQ,EAAE,MAAM,GAAG,EAAE,SAAS,CAAC;AACrC,SAAO,MAAM,MAAM,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC;AAC7C;AAGA,SAAS,gBAAgB,SAAqD;AAC5E,QAAM,QAAQ,gBAAgB,OAAO;AACrC,SAAO,MAAM,IAAI,CAAC,MAAM;AACtB,UAAM,IAAI,EAAE,WAAW,GAAG;AAC1B,UAAM,IAAI,EAAE,SAAS,GAAG;AACxB,QAAI,KAAK,EAAG,QAAO;AACnB,QAAI,EAAG,QAAO;AACd,WAAO;AAAA,EACT,CAAC;AACH;AAYA,SAAS,cAAc,EAAE,KAAK,GAAgC;AAC5D,QAAM,WAAW,KAAK,MAAM,IAAI,EAAE,OAAO,CAAC,MAAM,EAAE,KAAK,EAAE,SAAS,CAAC;AACnE,MAAI,SAAS,SAAS,EAAG,QAAO,gBAAAF,KAACD,OAAA,EAAM,gBAAK;AAG5C,MAAI,SAAS;AACb,WAAS,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;AACxC,QAAI,cAAc,SAAS,CAAC,CAAE,GAAG;AAC/B,eAAS;AACT;AAAA,IACF;AAAA,EACF;AACA,MAAI,WAAW,GAAI,QAAO,gBAAAC,KAACD,OAAA,EAAM,gBAAK;AAGtC,QAAM,aAAa,SAAS,MAAM,GAAG,MAAM,EAAE,KAAK,EAAE;AACpD,QAAM,cAAc,gBAAgB,UAAU;AAC9C,QAAM,aAAa,gBAAgB,SAAS,MAAM,CAAE;AACpD,QAAM,WAAW,SAAS,MAAM,SAAS,CAAC,EAAE,IAAI,eAAe;AAE/D,QAAM,WAAW,YAAY;AAG7B,QAAM,eAAe,YAAY,IAAI,CAAC,GAAG,OAAO;AAC9C,QAAI,MAAM,YAAY,YAAY,EAAE,KAAK,EAAE;AAC3C,eAAW,OAAO,UAAU;AAC1B,YAAM,IAAI,YAAY,IAAI,EAAE,KAAK,EAAE;AACnC,UAAI,IAAI,IAAK,OAAM;AAAA,IACrB;AACA,WAAO,KAAK,IAAI,KAAK,mBAAmB;AAAA,EAC1C,CAAC;AAGD,QAAM,SAAS;AACf,QAAM,gBAAgB,aAAa,OAAO,CAAC,GAAG,MAAM,IAAI,IAAI,QAAQ,CAAC;AACrE,MAAI;AACJ,MAAI,iBAAiB,iBAAiB;AACpC,gBAAY;AAAA,EACd,OAAO;AAEL,UAAM,YAAY,kBAAkB,IAAI,SAAS;AACjD,UAAM,MAAM,aAAa,OAAO,CAAC,GAAG,MAAM,IAAI,GAAG,CAAC;AAClD,gBAAY,aAAa,IAAI,CAAC,MAAM,KAAK,IAAI,qBAAqB,KAAK,MAAO,IAAI,MAAO,SAAS,CAAC,CAAC;AAAA,EACtG;AAGA,QAAM,IAAI;AACV,QAAM,UAAU,CAAC,GAAW,GAAW,MACrC,IAAI,UAAU,IAAI,CAAC,MAAM,EAAE,OAAO,IAAI,MAAM,CAAC,EAAE,KAAK,CAAC,IAAI;AAE3D,QAAM,YAAY,QAAQ,UAAK,UAAK,QAAG;AACvC,QAAM,YAAY,QAAQ,UAAK,UAAK,QAAG;AACvC,QAAM,YAAY,QAAQ,UAAK,UAAK,QAAG;AAGvC,WAAS,QAAQ,OAAiB,SAA4B;AAC5D,WACE,gBAAAE,MAACF,OAAA,EACE;AAAA;AAAA,MACA,MAAM,IAAI,CAAC,MAAM,OAChB,gBAAAE,MAACF,OAAA,EACE;AAAA;AAAA,QACA,WAAW,KAAK,MAAM,GAAG,UAAU,EAAE,KAAK,mBAAmB,GAAG,UAAU,EAAE,KAAK,qBAAqB,WAAW,EAAE,KAAK,MAAM;AAAA,QAC9H;AAAA,QAAI;AAAA,WAHI,EAIX,CACD;AAAA,SARQ,OASX;AAAA,EAEJ;AAEA,QAAM,OAAoB;AAAA,IACxB,gBAAAC,KAACD,OAAA,EAAe,OAAM,WAAW,uBAAvB,KAAiC;AAAA,IAC3C,QAAQ,aAAa,KAAK;AAAA,IAC1B,gBAAAC,KAACD,OAAA,EAAe,OAAM,WAAW,uBAAvB,KAAiC;AAAA,IAC3C,GAAG,SAAS,IAAI,CAAC,OAAO,OAAO,QAAQ,OAAO,IAAI,EAAE,EAAE,CAAC;AAAA,IACvD,gBAAAC,KAACD,OAAA,EAAe,OAAM,WAAW,uBAAvB,KAAiC;AAAA,EAC7C;AAEA,SACE,gBAAAC,KAACF,MAAA,EAAI,eAAc,UAAS,WAAW,GACpC,gBACH;AAEJ;AAOA,IAAM,cAAc;AAMpB,SAAS,eAAe,MAAuB;AAC7C,SAAO,YAAY,KAAK,IAAI;AAC9B;AAMA,SAAS,mBACP,MAC2C;AAC3C,MAAI;AAEF,UAAM,YAAY,IAAK,KAAa,UAAU,MAAM;AAAA,MAClD,aAAa;AAAA,IACf,CAAC;AACD,UAAM,WAAW,CAAC,GAAG,UAAU,QAAQ,IAAI,CAAC;AAC5C,WAAO,SAAS,IAAI,CAAC,OAAO;AAAA,MAC1B,MAAM,EAAE;AAAA,MACR,SAAS,eAAe,EAAE,OAAO;AAAA,IACnC,EAAE;AAAA,EACJ,QAAQ;AAEN,WAAO,CAAC,EAAE,MAAM,SAAS,MAAM,CAAC;AAAA,EAClC;AACF;AAKA,SAAS,eAAe,MAAc,KAAiC;AACrE,QAAM,WAAW,mBAAmB,IAAI;AAGxC,MAAI,SAAS,WAAW,KAAK,CAAC,SAAS,CAAC,EAAG,SAAS;AAClD,WACE,gBAAAE,KAACD,OAAA,EAAe,OAAO,YACpB,kBADQ,GAEX;AAAA,EAEJ;AAGA,QAAM,SAAoD,CAAC;AAC3D,aAAW,KAAK,UAAU;AACxB,UAAM,OAAO,OAAO,OAAO,SAAS,CAAC;AACrC,QAAI,QAAQ,KAAK,YAAY,EAAE,SAAS;AACtC,WAAK,QAAQ,EAAE;AAAA,IACjB,OAAO;AACL,aAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,EAAE,QAAQ,CAAC;AAAA,IAClD;AAAA,EACF;AAEA,SACE,gBAAAC,KAACD,OAAA,EACE,iBAAO;AAAA,IAAI,CAAC,GAAG,MACd,EAAE,UACA,EAAE,OAEF,gBAAAC,KAACD,OAAA,EAAa,OAAO,YAClB,YAAE,QADM,CAEX;AAAA,EAEJ,KATS,GAUX;AAEJ;AAOA,IAAM,aAAa;AAEnB,IAAM,aAAa;AAiBZ,SAAS,gBAAgB,EAAE,UAAU,KAAK,GAAoC;AAEnF,QAAM,oBAAoB,gBAAgB,IAAI;AAG9C,QAAM,gBAA2B,CAAC;AAClC,aAAW,OAAO,mBAAmB;AACnC,QAAI,IAAI,SAAS,cAAc;AAC7B,oBAAc,KAAK,GAAG;AAAA,IACxB,OAAO;AACL,oBAAc,KAAK,GAAG,YAAY,IAAI,IAAI,CAAC;AAAA,IAC7C;AAAA,EACF;AAGA,QAAM,eAA0B,CAAC;AACjC,aAAW,OAAO,eAAe;AAC/B,QAAI,IAAI,SAAS,gBAAgB,IAAI,SAAS,SAAS;AACrD,mBAAa,KAAK,GAAG;AAAA,IACvB,OAAO;AACL,mBAAa,KAAK,GAAG,eAAe,IAAI,IAAI,CAAC;AAAA,IAC/C;AAAA,EACF;AAGA,QAAM,WAAW,eAAe,YAAY;AAG5C,QAAM,WAAW,SAAS;AAAA,IACxB,CAAC,MAAM,EAAE,SAAS,gBAAgB,EAAE,SAAS;AAAA,EAC/C;AAGA,MAAI,CAAC,UAAU;AACb,UAAM,WAAW,SAAS,WAAW,KAAK,SAAS,CAAC,EAAG,SAAS;AAChE,QAAI,UAAU;AACZ,aAAO,gBAAAC,KAACD,OAAA,EAAK,MAAK,QAAQ,gBAAK;AAAA,IACjC;AAEA,WACE,gBAAAC,KAACD,OAAA,EAAK,MAAK,QACR,mBAAS,IAAI,CAAC,KAAK,MAAM;AACxB,UAAI,IAAI,SAAS,QAAQ;AACvB,eACE,gBAAAC,KAACD,OAAA,EAAa,OAAO,YAClB,cAAI,QADI,CAEX;AAAA,MAEJ;AACA,UAAI,IAAI,SAAS,QAAQ;AACvB,eAAO,eAAe,IAAI,MAAM,CAAC;AAAA,MACnC;AACA,aAAO,IAAI;AAAA,IACb,CAAC,GACH;AAAA,EAEJ;AAGA,QAAM,WAAwB,CAAC;AAC/B,MAAI,cAAyB,CAAC;AAE9B,WAAS,YAAY,UAAkB;AACrC,QAAI,YAAY,WAAW,EAAG;AAC9B,UAAM,iBACJ,YAAY,WAAW,KAAK,YAAY,CAAC,EAAG,SAAS;AACvD,QAAI,gBAAgB;AAClB,eAAS;AAAA,QACP,gBAAAC,KAACD,OAAA,EAAoB,MAAK,QACvB,sBAAY,CAAC,EAAG,QADR,QAEX;AAAA,MACF;AAAA,IACF,OAAO;AACL,eAAS;AAAA,QACP,gBAAAC,KAACD,OAAA,EAAoB,MAAK,QACvB,sBAAY,IAAI,CAAC,KAAK,MAAM;AAC3B,cAAI,IAAI,SAAS,QAAQ;AACvB,mBACE,gBAAAC,KAACD,OAAA,EAAa,OAAO,YAClB,cAAI,QADI,CAEX;AAAA,UAEJ;AACA,cAAI,IAAI,SAAS,QAAQ;AACvB,mBAAO,eAAe,IAAI,MAAM,CAAC;AAAA,UACnC;AACA,iBAAO,IAAI;AAAA,QACb,CAAC,KAbQ,QAcX;AAAA,MACF;AAAA,IACF;AACA,kBAAc,CAAC;AAAA,EACjB;AAEA,aAAW,OAAO,UAAU;AAC1B,QAAI,IAAI,SAAS,cAAc;AAC7B,kBAAY,SAAS,MAAM;AAC3B,eAAS;AAAA,QACP,gBAAAC,KAAC,qBAA8C,MAAM,IAAI,QAAjC,IAAI,SAAS,MAAM,EAAoB;AAAA,MACjE;AAAA,IACF,WAAW,IAAI,SAAS,SAAS;AAC/B,kBAAY,SAAS,MAAM;AAC3B,eAAS;AAAA,QACP,gBAAAA,KAAC,iBAA0C,MAAM,IAAI,QAAjC,IAAI,SAAS,MAAM,EAAoB;AAAA,MAC7D;AAAA,IACF,OAAO;AACL,kBAAY,KAAK,GAAG;AAAA,IACtB;AAAA,EACF;AACA,cAAY,SAAS,MAAM;AAE3B,SAAO,gBAAAA,KAACF,MAAA,EAAI,eAAc,UAAU,oBAAS;AAC/C;;;AJzpBU,gBAAAK,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,OAAO;AACT,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,KAAK,CAAC,MAAM,EAAE,KAAK,YAAY,MAAM,KAAK,EAAG,QAAO;AAEzE,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;AAAA,IAEhB,OAAO,OAAO;AAAA,EAChB,CAAC;AACH,CAAC;AAOM,SAAS,eAAe,QAKlB;AACX,SAAO,gBAAgB,IAAI,OAAO,MAAM,MAAM;AAChD;;;ACsDO,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;AAElE,aAAO,KAAK,QAAQ,MAAW,GAAG;AAAA,IACpC;AAAA,IAEA,aAAa,MAAuB;AAElC,aAAO,KAAK,eAAe,IAAS,KAAK,KAAK;AAAA,IAChD;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;;;ACvNA,OAAO,gBAAgB;;;ACJvB;;;ACAA;;;AFSA,IAAM,mBAAmB,WAAW,QAAQ,qBAAoB;AAChE,IAAM,uBAAuB,WAAW,QAAQ,mBAAkB;AAQlE,SAAS,YAAY,OAAsC;AACzD,SAAO,MAAM,IAAI,CAAC,MAAM;AACtB,UAAM,SACJ,EAAE,cACF,OAAO,KAAM,EAAE,WAAY,cAAc,CAAC,CAAC,EACxC,KAAK,IAAI;AACd,WAAO;AAAA,MACL,MAAM,EAAE;AAAA,MACR,aAAa,EAAE;AAAA,MACf,QAAQ,UAAU;AAAA,IACpB;AAAA,EACF,CAAC;AACH;AAGA,SAAS,kBAAkB,MAA2B;AACpD,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;AAAA,IACL,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;AACF;AAQO,SAAS,kBAAkB,MAAmC;AACnE,SAAO,iBAAiB,kBAAkB,IAAI,CAAC;AACjD;AAQO,SAAS,sBAAsB,MAAmC;AACvE,SAAO,qBAAqB,kBAAkB,IAAI,CAAC;AACrD;;;AG1CO,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;;;AChOA,SAAS,gBAAgB;AACzB,SAAS,iBAAiB;AAG1B,IAAM,gBAAgB,UAAU,QAAQ;AACxC,IAAM,eAAe,EAAE,aAAa,KAAK;AAEzC,eAAsB,UAAU,KAA+B;AAC7D,MAAI;AACF,UAAM,cAAc,OAAO,CAAC,aAAa,uBAAuB,GAAG,EAAE,KAAK,GAAG,aAAa,CAAC;AAC3F,WAAO;AAAA,EACT,QAAQ;AAAE,WAAO;AAAA,EAAO;AAC1B;AAEA,eAAe,WAAW,KAA+B;AACvD,MAAI;AACF,UAAM,cAAc,OAAO,CAAC,aAAa,YAAY,MAAM,GAAG,EAAE,KAAK,GAAG,aAAa,CAAC;AACtF,WAAO;AAAA,EACT,QAAQ;AAAE,WAAO;AAAA,EAAO;AAC1B;AAEA,eAAe,kBAAkB,KAA+B;AAC9D,QAAM,MAAM,MAAM,cAAc,OAAO,CAAC,UAAU,aAAa,GAAG,EAAE,KAAK,GAAG,aAAa,CAAC;AAC1F,SAAO,IAAI,OAAO,KAAK,EAAE,SAAS;AACpC;AAEA,eAAe,IAAI,MAAgB,KAA8B;AAC/D,MAAI;AACF,UAAM,EAAE,OAAO,IAAI,MAAM,cAAc,OAAO,MAAM,EAAE,KAAK,GAAG,aAAa,CAAC;AAC5E,WAAO;AAAA,EACT,SAAS,KAAc;AACrB,UAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D,UAAM,IAAI,MAAM,OAAO,KAAK,KAAK,GAAG,CAAC,kBAAQ,GAAG,EAAE;AAAA,EACpD;AACF;AAEA,eAAe,cAAc,KAAgC;AAC3D,QAAM,MAAM,MAAM,IAAI,CAAC,SAAS,QAAQ,aAAa,GAAG,GAAG;AAC3D,SAAO,IAAI,MAAM,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,OAAO;AAC5D;AAEA,eAAsB,iBAAiB,KAAkC;AACvE,QAAM,YAAY,KAAK,IAAI;AAC3B,QAAM,SAAS,MAAM,UAAU,GAAG;AAClC,MAAI,CAAC,OAAQ,QAAO,EAAE,UAAU,IAAI,WAAW,KAAK,WAAW,MAAM;AACrE,MAAI,CAAE,MAAM,WAAW,GAAG,EAAI,QAAO,EAAE,UAAU,IAAI,WAAW,KAAK,WAAW,KAAK;AACrF,MAAI,CAAE,MAAM,kBAAkB,GAAG,EAAI,QAAO,EAAE,UAAU,IAAI,WAAW,KAAK,WAAW,KAAK;AAM5F,QAAM,aAAa,MAAM,cAAc,GAAG;AAC1C,QAAM,IAAI,CAAC,SAAS,QAAQ,MAAM,cAAc,SAAS,IAAI,IAAI,GAAG,GAAG;AAEvE,QAAM,UAAU,MAAM,cAAc,GAAG;AACvC,MAAI,QAAQ,WAAW,EAAG,OAAM,IAAI,MAAM,qDAAiC;AAC3E,QAAM,SAAS,QAAQ,KAAK,CAAC,MAAM,CAAC,WAAW,SAAS,CAAC,CAAC,KAAK,QAAQ,CAAC;AAKxE,QAAM,IAAI,CAAC,SAAS,SAAS,MAAM,GAAG,GAAG;AAEzC,SAAO,EAAE,UAAU,QAAQ,WAAW,KAAK,WAAW,KAAK;AAC7D;AAEA,eAAsB,uBAAuB,YAAuC;AAClF,MAAI,CAAC,WAAW,UAAW,OAAM,IAAI,MAAM,+EAAmB;AAC9D,MAAI,CAAC,WAAW,SAAU,OAAM,IAAI,MAAM,0HAAsB;AAEhE,QAAM,EAAE,KAAK,SAAS,IAAI;AAC1B,QAAM,cAAc,MAAM,cAAc,GAAG;AAC3C,MAAI,CAAC,YAAY,SAAS,QAAQ,GAAG;AACnC,UAAM,IAAI,MAAM,6HAAmC;AAAA,EACrD;AACA,QAAM,IAAI,CAAC,YAAY,MAAM,GAAG,GAAG,GAAG;AACtC,QAAM,IAAI,CAAC,SAAS,KAAK,GAAG,GAAG;AAC/B,QAAM,IAAI,CAAC,SAAS,SAAS,QAAQ,GAAG,GAAG;AAC3C,QAAM,WAAW,YAAY,QAAQ,QAAQ;AAC7C,MAAI,YAAY,EAAG,OAAM,IAAI,CAAC,SAAS,QAAQ,UAAU,QAAQ,GAAG,GAAG,GAAG;AAC5E;AAOA,eAAsB,eAAe,KAA4B;AAC/D,QAAM,SAAS,MAAM,UAAU,GAAG;AAClC,MAAI,CAAC,OAAQ,OAAM,IAAI,MAAM,+EAAmB;AAChD,MAAI,CAAE,MAAM,WAAW,GAAG,EAAI,OAAM,IAAI,MAAM,yDAAiB;AAC/D,QAAM,IAAI,CAAC,YAAY,MAAM,GAAG,GAAG,GAAG;AACtC,QAAM,IAAI,CAAC,SAAS,KAAK,GAAG,GAAG;AACjC;AAEA,eAAsB,kBAAkB,YAAuC;AAC7E,MAAI,CAAC,WAAW,aAAa,CAAC,WAAW,SAAU;AACnD,QAAM,EAAE,KAAK,SAAS,IAAI;AAC1B,QAAM,OAAO,MAAM,cAAc,GAAG;AACpC,QAAM,MAAM,KAAK,QAAQ,QAAQ;AACjC,MAAI,MAAM,EAAG;AACb,MAAI;AAAE,UAAM,IAAI,CAAC,SAAS,QAAQ,UAAU,GAAG,GAAG,GAAG,GAAG;AAAA,EAAG,QAAQ;AAAA,EAAe;AACpF;;;ACvGA,SAAS,SAAAI,QAAO,YAAAC,WAAU,aAAAC,YAAW,WAAAC,UAAS,QAAQ,cAAc;AACpE,SAAS,QAAAC,aAAY;AACrB,SAAS,kBAAkB;AAGpB,SAAS,qBAA6B;AAC3C,QAAM,OAAO,QAAQ,IAAI,QAAQ,QAAQ,IAAI,eAAe;AAC5D,SAAOA,MAAK,MAAM,YAAY,UAAU;AAC1C;AAEO,IAAM,eAAN,MAAmB;AAAA,EACf;AAAA,EACT,YAAY,KAAc;AAAE,SAAK,OAAO,OAAO,mBAAmB;AAAA,EAAG;AAAA,EACrE,IAAI,MAAc;AAAE,WAAO,KAAK;AAAA,EAAM;AAAA,EAEtC,MAAM,KAAK,SAAuC;AAChD,UAAMJ,OAAM,KAAK,MAAM,EAAE,WAAW,KAAK,CAAC;AAC1C,UAAM,YAAYI,MAAK,KAAK,MAAM,GAAG,QAAQ,EAAE,OAAO;AACtD,UAAM,UAAUA,MAAK,KAAK,MAAM,IAAI,QAAQ,EAAE,WAAW;AACzD,UAAMF,WAAU,SAAS,KAAK,UAAU,SAAS,MAAM,CAAC,GAAG,OAAO;AAClE,UAAM,OAAO,SAAS,SAAS;AAAA,EACjC;AAAA,EAEA,MAAM,KAAK,IAA2C;AACpD,UAAM,OAAOE,MAAK,KAAK,MAAM,GAAG,EAAE,OAAO;AACzC,QAAI;AACF,YAAM,UAAU,MAAMH,UAAS,MAAM,OAAO;AAC5C,aAAO,KAAK,MAAM,OAAO;AAAA,IAC3B,SAAS,KAAc;AACrB,UAAI,SAAS,GAAG,EAAG,QAAO;AAC1B,YAAM;AAAA,IACR;AAAA,EACF;AAAA,EAEA,MAAM,OAAkC;AACtC,QAAI;AACJ,QAAI;AAAE,cAAQ,MAAME,SAAQ,KAAK,IAAI;AAAA,IAAG,SACjC,KAAc;AAAE,UAAI,SAAS,GAAG,EAAG,QAAO,CAAC;AAAG,YAAM;AAAA,IAAK;AAEhE,UAAM,UAA4B,CAAC;AACnC,eAAW,QAAQ,OAAO;AACxB,UAAI,CAAC,KAAK,SAAS,OAAO,KAAK,KAAK,WAAW,GAAG,EAAG;AACrD,UAAI;AACF,cAAM,UAAU,MAAMF,UAASG,MAAK,KAAK,MAAM,IAAI,GAAG,OAAO;AAC7D,cAAM,IAAI,KAAK,MAAM,OAAO;AAC5B,gBAAQ,KAAK;AAAA,UACX,IAAI,EAAE;AAAA,UAAI,OAAO,EAAE,SAAS;AAAA,UAAS,WAAW,EAAE;AAAA,UAClD,KAAK,EAAE;AAAA,UAAK,cAAc,EAAE,UAAU,UAAU;AAAA,QAClD,CAAC;AAAA,MACH,QAAQ;AAAA,MAAuB;AAAA,IACjC;AACA,WAAO,QAAQ,KAAK,CAAC,GAAG,MAAM,EAAE,YAAY,EAAE,SAAS;AAAA,EACzD;AAAA,EAEA,MAAM,OAAO,IAA2B;AACtC,UAAM,OAAOA,MAAK,KAAK,MAAM,GAAG,EAAE,OAAO;AACzC,QAAI;AAAE,YAAM,OAAO,IAAI;AAAA,IAAG,SACnB,KAAc;AAAE,UAAI,CAAC,SAAS,GAAG,EAAG,OAAM;AAAA,IAAK;AAAA,EACxD;AAAA,EAEA,MAAM,OAAO,IAA8B;AAAE,WAAQ,MAAM,KAAK,KAAK,EAAE,MAAO;AAAA,EAAM;AAAA,EACpF,OAAO,QAAgB;AAAE,WAAO,WAAW;AAAA,EAAG;AAChD;AAEA,SAAS,SAAS,KAAuB;AACvC,SAAO,OAAO,QAAQ,YAAY,QAAQ,QAAQ,UAAU,OAAQ,IAA0B,SAAS;AACzG;;;ACvDA,SAAS,SAAAC,QAAO,kBAAkB;AAClC,SAAS,QAAAC,OAAM,gBAAgB;AAI/B,IAAM,eAAe;AAGrB,IAAM,YAAY;AAGX,SAAS,iBAAyB;AACvC,QAAM,OAAO,QAAQ,IAAI,QAAQ,QAAQ,IAAI,eAAe;AAC5D,SAAOA,MAAK,MAAM,YAAY,MAAM;AACtC;AAkBA,SAAS,mBAAmB,KAAqB;AAC/C,QAAM,OAAO,SAAS,GAAG;AACzB,SAAO,QAAQ;AACjB;AAQO,IAAM,qBAAN,MAAM,oBAAmB;AAAA;AAAA,EAE9B,OAAgB,aAAa,oBAAI,IAAwB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMzD,aAAa,WAA0B;AACrC,UAAM,WAA4B,CAAC;AACnC,eAAW,UAAU,oBAAmB,YAAY;AAClD,eAAS,KAAK,OAAO,MAAM,CAAC;AAAA,IAC9B;AACA,UAAM,QAAQ,WAAW,QAAQ;AAAA,EACnC;AAAA,EAES;AAAA,EACA;AAAA,EACT,SAAwB,QAAQ,QAAQ;AAAA,EACxC,YAAY;AAAA,EACZ,UAAU;AAAA,EAEV,YAAY,WAAmB,KAAa,SAAqC;AAC/E,SAAK,WAAW,SAAS,WAAW;AACpC,QAAI,CAAC,KAAK,UAAU;AAClB,WAAK,WAAW;AAChB;AAAA,IACF;AACA,UAAM,UAAU,SAAS,WAAW,eAAe;AACnD,UAAM,cAAc,mBAAmB,GAAG;AAC1C,SAAK,WAAWA,MAAK,SAAS,aAAa,GAAG,SAAS,QAAQ;AAC/D,wBAAmB,WAAW,IAAI,IAAI;AAAA,EACxC;AAAA;AAAA,EAGA,IAAI,UAAkB;AACpB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,IAAI,UAAmB;AACrB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,IAAI,OAAuB;AACzB,QAAI,CAAC,KAAK,YAAY,KAAK,QAAS;AAGpC,QAAI,KAAK,aAAa,UAAW;AAEjC,SAAK;AACL,SAAK,SAAS,KAAK,OAChB,KAAK,MAAM,KAAK,WAAW,KAAK,CAAC,EACjC,MAAM,MAAM;AAAA,IAAmB,CAAC,EAChC,QAAQ,MAAM,KAAK,WAAW;AAAA,EACnC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,QAAuB;AAC3B,SAAK,UAAU;AACf,UAAM,KAAK,OAAO,MAAM,MAAM;AAAA,IAAC,CAAC;AAChC,wBAAmB,WAAW,OAAO,IAAI;AAAA,EAC3C;AAAA;AAAA,EAGA,MAAM,WAAW,OAAgC;AAC/C,UAAM,MAAMA,MAAK,KAAK,UAAU,IAAI;AACpC,UAAMD,OAAM,KAAK,EAAE,WAAW,KAAK,CAAC;AACpC,UAAM,OAAO,KAAK,UAAU,KAAK,IAAI;AACrC,UAAM,WAAW,KAAK,UAAU,MAAM,OAAO;AAAA,EAC/C;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,gBAAgB,WAAmB,KAAa,OAAe,MAAoB;AACjF,SAAK,IAAI,EAAE,IAAI,KAAK,IAAI,GAAG,MAAM,iBAAiB,WAAW,KAAK,OAAO,KAAK,CAAC;AAAA,EACjF;AAAA;AAAA,EAGA,eAAe,SAAuB;AACpC,SAAK,IAAI,EAAE,IAAI,KAAK,IAAI,GAAG,MAAM,gBAAgB,QAAQ,CAAC;AAAA,EAC5D;AAAA;AAAA,EAGA,iBAAiB,SAAiB,OAAqB;AACrD,SAAK,IAAI,EAAE,IAAI,KAAK,IAAI,GAAG,MAAM,kBAAkB,SAAS,MAAM,CAAC;AAAA,EACrE;AAAA;AAAA,EAGA,YAAY,MAAc,QAAgB,MAAc,OAAqB;AAC3E,SAAK,IAAI,EAAE,IAAI,KAAK,IAAI,GAAG,MAAM,aAAa,MAAM,QAAQ,WAAW,MAAM,MAAM,CAAC;AAAA,EACtF;AAAA;AAAA,EAGA,cACE,MACA,QACA,SACA,MACA,OACA,SACA,OACM;AACN,SAAK,IAAI;AAAA,MACP,IAAI,KAAK,IAAI;AAAA,MACb,MAAM;AAAA,MACN;AAAA,MACA;AAAA,MACA;AAAA,MACA,MAAM,SAAS,IAAI;AAAA,MACnB,GAAI,QAAQ,EAAE,MAAM,IAAI,CAAC;AAAA,MACzB,GAAI,YAAY,SAAY,EAAE,QAAQ,IAAI,CAAC;AAAA,MAC3C;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA,EAGA,SACE,OACA,cACA,kBACA,oBACA,MACA,OACM;AACN,SAAK,IAAI;AAAA,MACP,IAAI,KAAK,IAAI;AAAA,MACb,MAAM;AAAA,MACN;AAAA,MACA;AAAA,MACA;AAAA,MACA,GAAI,uBAAuB,SAAY,EAAE,mBAAmB,IAAI,CAAC;AAAA,MACjE;AAAA,MACA;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA,EAGA,SAAS,SAAiB,OAAsB;AAC9C,SAAK,IAAI,EAAE,IAAI,KAAK,IAAI,GAAG,MAAM,SAAS,SAAS,GAAI,QAAQ,EAAE,MAAM,IAAI,CAAC,EAAG,CAAC;AAAA,EAClF;AAAA;AAAA,EAGA,YAAY,SAAiB,YAA0B;AACrD,SAAK,IAAI,EAAE,IAAI,KAAK,IAAI,GAAG,MAAM,aAAa,SAAS,WAAW,CAAC;AAAA,EACrE;AAAA;AAAA,EAGA,cAAc,SAAuB;AACnC,SAAK,IAAI,EAAE,IAAI,KAAK,IAAI,GAAG,MAAM,eAAe,QAAQ,CAAC;AAAA,EAC3D;AACF;AAKA,SAAS,SAAS,MAAsB;AACtC,MAAI,KAAK,UAAU,aAAc,QAAO;AACxC,SAAO,KAAK,MAAM,GAAG,YAAY,IAAI,wDAAgB,KAAK,MAAM;AAClE;;;AC5JO,IAAM,UAAN,MAAM,SAAQ;AAAA,EACV,YAA2B,CAAC;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAGA,mBAAmB,IAAI,gBAAgB;AAAA,EAEvC;AAAA,EACA;AAAA,EACT;AAAA,EACA,gBAAuC;AAAA,EAEvC,eAAe,oBAAI,IAAwB;AAAA,EAC3C,gBAAkC,CAAC;AAAA,EACnC,QAAqB;AAAA,EACZ;AAAA,EAET,YACE,UACA,QAAuC,CAAC,GACxC,aACA,SACA;AACA,SAAK,YAAY;AACjB,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,MACjE,kBAAkB,SAAS,oBAAoB;AAAA,MAC/C,WAAW,SAAS,aAAa;AAAA,IACnC;AACA,SAAK,aAAa,SAAS,aAAa,aAAa,MAAM;AAC3D,SAAK,SAAS,SAAS,UAAU,QAAQ,OAAQ,SAAS,SAAS,IAAI,aAAa;AACpF,SAAK,aAAa,KAAK,IAAI;AAC3B,SAAK,UAAU,IAAI,mBAAmB,KAAK,YAAY,KAAK,SAAS,KAAK;AAAA,MACxE,SAAS,KAAK,SAAS;AAAA,IACzB,CAAC;AACD,SAAK,QAAQ,gBAAgB,KAAK,YAAY,KAAK,SAAS,KAAK,KAAK,UAAU,MAAM,GAAG,KAAK,KAAK;AAAA,EACrG;AAAA,EAEA,IAAI,WAAmC;AAAE,WAAO,KAAK;AAAA,EAAW;AAAA,EAChE,IAAI,kBAA0B;AAAE,WAAO,KAAK,aAAa;AAAA,EAAkB;AAAA,EAC3E,IAAI,cAA2B;AAAE,WAAO,KAAK;AAAA,EAAc;AAAA,EAC3D,IAAI,QAAgB;AAAE,WAAO,KAAK,UAAU,MAAM;AAAA,EAAG;AAAA,EACrD,IAAI,eAA6B;AAAE,WAAO,KAAK;AAAA,EAAe;AAAA,EAC9D,IAAI,OAAoB;AAAE,WAAO,KAAK;AAAA,EAAO;AAAA,EAC7C,IAAI,KAAa;AAAE,WAAO,KAAK;AAAA,EAAY;AAAA,EAC3C,IAAI,QAA6B;AAAE,WAAO,KAAK;AAAA,EAAQ;AAAA,EACvD,IAAI,YAAoB;AAAE,WAAO,KAAK;AAAA,EAAY;AAAA,EAElD,QAAQ,MAAgC;AAAE,SAAK,QAAQ;AAAM,WAAO,KAAK;AAAA,EAAO;AAAA,EAEhF,OAAO,KAAK,WAAmB,MAAgD;AAC7E,SAAK,UAAU,KAAK,EAAE,MAAM,QAAQ,SAAS,UAAU,CAAC;AACxD,SAAK,QAAQ,eAAe,SAAS;AACrC,UAAM,eAAe,KAAK,UAAU,SAAS;AAC7C,QAAI,KAAK,SAAS,kBAAkB;AAClC,UAAI;AACF,cAAM,aAAa,MAAM,iBAAiB,KAAK,SAAS,GAAG;AAC3D,aAAK,aAAa,IAAI,cAAc,UAAU;AAAA,MAChD,QAAQ;AAAA,MAAgB;AAAA,IAC1B;AAEA,UAAM,YAAY,KAAK,IAAI;AAC3B,QAAI,aAAa;AAEjB,QAAI;AACF,aAAO,aAAa,KAAK,SAAS,eAAe;AAC/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;AAE1D,cAAM,WAAW,KAAK,sBAAsB;AAC5C,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;AAED,YAAI,kBAAkB;AACtB,YAAI;AACJ,YAAI;AACJ,YAAI,oBAAmC;AAEvC,yBAAiB,SAAS,QAAQ;AAChC,cAAI,MAAM,SAAS;AACjB,+BAAmB,MAAM;AACzB,kBAAM,EAAE,MAAM,cAAc,SAAS,MAAM,QAAQ;AAAA,UACrD;AACA,cAAI,MAAM,aAAa,MAAM,UAAU,SAAS,EAAG,iBAAgB,MAAM;AACzE,cAAI,MAAM,MAAO,aAAY,MAAM;AACnC,cAAI,MAAM,aAAc,qBAAoB,MAAM;AAAA,QACpD;AAEA,YAAI,WAAW;AACb,gBAAM,UAAU,KAAK,UAAU,MAAM;AACrC,eAAK,aAAa,OAAO,WAAW,OAAO;AAC3C,gBAAM,OAAO,cAAc,WAAW,OAAO;AAC7C,eAAK,QAAQ;AAAA,YACX;AAAA,YAAS,UAAU;AAAA,YAAc,UAAU;AAAA,YAC3C,UAAU;AAAA,YAAoB,KAAK;AAAA,YAAW;AAAA,UAChD;AACA,gBAAM,EAAE,MAAM,SAAS,OAAO,WAAW,OAAO,QAAQ;AAAA,QAC1D;AAEA,cAAM,eAA4B,EAAE,MAAM,aAAa,SAAS,gBAAgB;AAChF,YAAI,iBAAiB,cAAc,SAAS,EAAG,cAAa,YAAY;AACxE,aAAK,UAAU,KAAK,YAAY;AAChC,aAAK,QAAQ,iBAAiB,iBAAiB,UAAU;AAEzD,YAAI,iBAAiB,cAAc,SAAS,GAAG;AAC7C,gBAAM,EAAE,MAAM,cAAc,OAAO,cAAc;AACjD,qBAAW,MAAM,eAAe;AAC9B,iBAAK,QAAQ,YAAY,GAAG,MAAM,GAAG,IAAI,GAAG,WAAW,UAAU;AAAA,UACnE;AAEA,gBAAM,cAAc,KAAK,iBAAiB,aAAa;AACvD,cAAI,aAAa;AACf,kBAAM,WAAW;AACjB,kBAAM,EAAE,MAAM,cAAc,SAAS,SAAS;AAC9C,yBAAa,YAAY;AACzB,yBAAa,WAAW;AACxB,iBAAK,gBAAgB,CAAC;AACtB;AACA;AAAA,UACF;AAEA,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;AAClE,iBAAK,QAAQ;AAAA,cACX,KAAK;AAAA,cAAM,KAAK;AAAA,cAAQ,KAAK,OAAO;AAAA,cACpC,KAAK,OAAO;AAAA,cAAM,KAAK,OAAO;AAAA,cAAO;AAAA,cAAW;AAAA,YAClD;AACA,gBAAI,cAAc,KAAK,OAAO;AAC9B,gBAAI,KAAK,OAAO,QAAQ,KAAK,OAAO,KAAK,MAAO,gBAAe;AAAA;AAAA,EAAO,KAAK,OAAO,KAAK,KAAK;AAC5F,iBAAK,UAAU,KAAK;AAAA,cAClB,MAAM;AAAA,cAAQ,SAAS;AAAA,cACvB,YAAY,KAAK;AAAA,cAAQ,MAAM,KAAK;AAAA,YACtC,CAAC;AAAA,UACH;AAEA;AACA;AAAA,QACF;AAEA;AAAA,MACF;AAAA,IACF,SAAS,KAAc;AACrB,UAAI,eAAe,gBAAgB,IAAI,SAAS,aAAc;AAC9D,UAAI,eAAe,SAAS,IAAI,SAAS,aAAc;AACvD,WAAK,QAAQ;AAAA,QACX,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,QAC/C,eAAe,QAAQ,IAAI,QAAQ;AAAA,MACrC;AACA,YAAM,EAAE,MAAM,SAAS,OAAO,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC,EAAE;AAClF;AAAA,IACF;AAEA,UAAM,UAAU,KAAK,IAAI,IAAI;AAC7B,SAAK,QAAQ,YAAY,SAAS,UAAU;AAC5C,SAAK,KAAK,SAAS;AAEnB,UAAM,KAAK,aAAa,MAAM,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AAC9C,UAAM,EAAE,MAAM,QAAQ,QAAQ;AAAA,EAChC;AAAA,EAEA,MAAM,cAAc,OAAuI;AACzJ,UAAM,UAAuB;AAAA,MAC3B,KAAK,KAAK,SAAS;AAAA,MACnB,QAAQ,KAAK,iBAAiB;AAAA,MAC9B,YAAY,KAAK,SAAS;AAAA,IAC5B;AAEA,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;AACnC,YAAM,eAAe;AACrB,YAAME,SAAqE,CAAC;AAC5E,YAAMC,WAA4B,CAAC;AACnC,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;AACA,aAAO,EAAE,OAAAD,QAAO,SAAAC,SAAQ;AAAA,IAC1B;AAEA,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;AACA,WAAO,EAAE,OAAO,QAAQ;AAAA,EAC1B;AAAA,EAEA,MAAM,YACJ,IACA,KACiG;AACjG,UAAM,WAAW,GAAG;AACpB,UAAM,YAAY,KAAK,IAAI;AAC3B,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;AAEA,QAAI;AACJ,QAAI;AAAE,iBAAW,GAAG,YAAY,KAAK,MAAM,GAAG,SAAS,IAAI,CAAC;AAAA,IAAG,QAAQ;AAAE,iBAAW,CAAC;AAAA,IAAG;AAExF,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;AAEA,QAAI,CAAC,WAAW,KAAK,IAAI,GAAG;AAC1B,YAAM,eAAgB,KAA6E;AACnG,UAAI,OAAO,iBAAiB,YAAY;AACtC,YAAI;AAAE,gBAAM,aAAa,UAAU,GAAG;AAAA,QAAG,QAAQ;AAAA,QAAe;AAAA,MAClE;AAAA,IACF;AAEA,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,EAAE,MAAM,UAAU,SAAS,OAAO,SAAS,OAAO,OAAO,OAAO,UAAU;AAAA,MACpF;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,EAEA,iBAAiB,cAA2C;AAC1D,QAAI,KAAK,cAAc,SAAS,EAAG,QAAO;AAC1C,UAAM,eAAe,KAAK,cAAc,MAAM,EAAE;AAChD,QAAI,aAAa,SAAS,EAAG,QAAO;AACpC,UAAM,QAAQ,aAAa,CAAC;AAC5B,UAAM,UAAU,aAAa,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,QAAQ,EAAE,UAAU,MAAM,SAAS,CAAC,EAAE,OAAO;AACxG,QAAI,CAAC,QAAS,QAAO;AACrB,WAAO,aAAa,KAAK,CAAC,OAAO,GAAG,SAAS,MAAM,IAAI;AAAA,EACzD;AAAA,EAEA,QAAc;AACZ,SAAK,iBAAiB,MAAM;AAC5B,QAAI,KAAK,eAAe;AAAE,mBAAa,KAAK,aAAa;AAAG,WAAK,gBAAgB;AAAA,IAAM;AAAA,EACzF;AAAA;AAAA,EAGA,MAAM,WAA0B;AAC9B,UAAM,KAAK,QAAQ,MAAM;AAAA,EAC3B;AAAA,EAEA,QAAc;AACZ,SAAK,UAAU,SAAS;AACxB,SAAK,aAAa,aAAa;AAC/B,SAAK,gBAAgB,CAAC;AACtB,SAAK,aAAa,MAAM;AAAA,EAC1B;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,aAA4B;AAChC,QAAI,KAAK,eAAe;AAAE,mBAAa,KAAK,aAAa;AAAG,WAAK,gBAAgB;AAAA,IAAM;AACvF,UAAM,KAAK,WAAW;AAAA,EACxB;AAAA,EAEA,WAAiB;AACf,QAAI,CAAC,KAAK,OAAQ;AAClB,QAAI,KAAK,eAAe;AAAE,WAAK,cAAc,QAAQ;AAAG;AAAA,IAAQ;AAChE,SAAK,gBAAgB,WAAW,MAAM;AACpC,WAAK,gBAAgB;AACrB,WAAK,KAAK,WAAW;AAAA,IACvB,GAAG,GAAG;AACN,SAAK,cAAc,MAAM;AAAA,EAC3B;AAAA,EAEA,MAAM,aAA4B;AAChC,QAAI,CAAC,KAAK,OAAQ;AAClB,UAAM,SAAwB;AAAA,MAC5B,IAAI,KAAK;AAAA,MACT,OAAO,KAAK,aAAa;AAAA,MACzB,WAAW,KAAK;AAAA,MAChB,WAAW,KAAK,IAAI;AAAA,MACpB,KAAK,KAAK,SAAS;AAAA,MACnB,OAAO,KAAK,UAAU,MAAM;AAAA,MAC5B,UAAU,KAAK,mBAAmB;AAAA,MAClC,WAAW,KAAK,aAAa;AAAA,IAC/B;AACA,QAAI;AAAE,YAAM,KAAK,OAAO,KAAK,MAAM;AAAA,IAAG,SAC/B,KAAK;AAAE,cAAQ,MAAM,6CAAoB,GAAG;AAAA,IAAG;AAAA,EACxD;AAAA,EAEA,eAAuB;AACrB,eAAW,KAAK,KAAK,WAAW;AAC9B,UAAI,EAAE,SAAS,UAAU,EAAE,QAAQ,KAAK,EAAG,QAAO,EAAE,QAAQ,KAAK,EAAE,MAAM,GAAG,EAAE;AAAA,IAChF;AACA,WAAO;AAAA,EACT;AAAA,EAEA,qBAAgD;AAC9C,WAAO,KAAK,UAAU,IAAI,CAAC,KAAK,QAAQ;AACtC,YAAM,aAAa,KAAK,aAAa,IAAI,GAAG;AAC5C,UAAI,IAAI,SAAS,UAAU,WAAY,QAAO,EAAE,GAAG,KAAK,WAAW;AACnE,aAAO,EAAE,GAAG,IAAI;AAAA,IAClB,CAAC;AAAA,EACH;AAAA,EAEA,aAAa,OACX,IACA,UACA,QAAuC,CAAC,GACxC,aACA,SACkB;AAClB,UAAM,QAAQ,SAAS,UAAU,QAAQ,OAAQ,SAAS,SAAS,IAAI,aAAa;AACpF,QAAI,CAAC,MAAO,OAAM,IAAI,MAAM,qGAAyC;AACrE,UAAM,SAAS,MAAM,MAAM,KAAK,EAAE;AAClC,QAAI,CAAC,OAAQ,OAAM,IAAI,MAAM,gBAAM,EAAE,qBAAM;AAE3C,UAAM,UAAU,IAAI,SAAQ,UAAU,OAAO,aAAa,EAAE,GAAG,SAAS,WAAW,IAAI,MAAM,CAAC;AAC9F,eAAW,KAAK,OAAO,UAAU;AAC/B,cAAQ,UAAU,KAAK;AAAA,QACrB,MAAM,EAAE;AAAA,QAAM,SAAS,EAAE;AAAA,QACzB,YAAY,EAAE;AAAA,QAAY,MAAM,EAAE;AAAA,QAAM,WAAW,EAAE;AAAA,MACvD,CAAC;AAAA,IACH;AACA,aAAS,IAAI,GAAG,IAAI,OAAO,SAAS,QAAQ,KAAK;AAC/C,YAAMC,MAAK,OAAO,SAAS,CAAC,GAAG;AAC/B,UAAIA,IAAI,SAAQ,aAAa,IAAI,GAAGA,GAAE;AAAA,IACxC;AACA,YAAQ,aAAa,OAAO;AAC5B,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAMA,kBAA2C;AACzC,UAAM,SAAkC,CAAC;AACzC,eAAW,CAAC,OAAO,UAAU,KAAK,KAAK,cAAc;AACnD,YAAM,MAAM,KAAK,UAAU,KAAK;AAChC,UAAI,CAAC,OAAO,IAAI,SAAS,OAAQ;AACjC,aAAO,KAAK,EAAE,OAAO,SAAS,IAAI,QAAQ,MAAM,GAAG,EAAE,GAAG,WAAW,WAAW,WAAW,WAAW,WAAW,UAAU,CAAC;AAAA,IAC5H;AACA,WAAO,OAAO,KAAK,CAAC,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK;AAAA,EAChD;AAAA,EAEA,MAAM,OAAO,aAA4C;AACvD,QAAI,cAAc,KAAK,eAAe,KAAK,UAAU,QAAQ;AAC3D,aAAO,EAAE,IAAI,OAAO,OAAO,8CAAW,WAAW,GAAG;AAAA,IACtD;AACA,UAAM,SAAS,KAAK,UAAU,WAAW;AACzC,QAAI,CAAC,UAAU,OAAO,SAAS,QAAQ;AACrC,aAAO,EAAE,IAAI,OAAO,OAAO,gBAAM,WAAW,kCAAc;AAAA,IAC5D;AACA,UAAM,aAAa,KAAK,aAAa,IAAI,WAAW;AACpD,QAAI,CAAC,WAAY,QAAO,EAAE,IAAI,OAAO,OAAO,mDAAW;AAEvD,SAAK,UAAU,SAAS,cAAc;AACtC,UAAM,YAA0B,CAAC;AACjC,eAAW,CAAC,KAAKA,GAAE,KAAK,KAAK,cAAc;AACzC,UAAI,MAAM,aAAa;AAAE,kBAAU,KAAKA,GAAE;AAAG,aAAK,aAAa,OAAO,GAAG;AAAA,MAAG;AAAA,IAC9E;AAEA,QAAI,eAAe;AACnB,QAAI,WAAW,WAAW;AACxB,UAAI;AACF,YAAI,WAAW,UAAU;AAEvB,gBAAM,uBAAuB,UAAU;AAAA,QACzC,OAAO;AAGL,gBAAM,eAAe,KAAK,SAAS,GAAG;AAAA,QACxC;AACA,uBAAe;AAAA,MACjB,SAAS,KAAK;AACZ,cAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D,eAAO,EAAE,IAAI,OAAO,OAAO,iFAAgB,GAAG,GAAG;AAAA,MACnD;AAAA,IACF;AAIA,SAAK,aAAa,OAAO,WAAW;AACpC,eAAWA,OAAM,WAAW;AAAE,WAAK,kBAAkBA,GAAE;AAAA,IAAG;AAC1D,SAAK,SAAS;AACd,WAAO,EAAE,IAAI,MAAM,aAAa;AAAA,EAClC;AAAA,EAEA,iBAA0B;AAAE,WAAO,KAAK,gBAAgB,EAAE,SAAS;AAAA,EAAG;AAAA,EAEtE,MAAM,SAAwB;AAC5B,QAAI,KAAK,OAAQ,OAAM,KAAK,OAAO,OAAO,KAAK,UAAU;AACzD,eAAWA,OAAM,KAAK,aAAa,OAAO,GAAG;AAAE,WAAK,kBAAkBA,GAAE;AAAA,IAAG;AAC3E,SAAK,aAAa,MAAM;AACxB,SAAK,QAAQ,cAAc,KAAK,IAAI,IAAI,KAAK,UAAU;AACvD,UAAM,KAAK,QAAQ,MAAM;AAAA,EAC3B;AAAA;AAAA;AAAA;AAAA,EAMA,qBAA6B;AAC3B,UAAM,eAAe,KAAK,cAAc,KAAK;AAC7C,UAAM,YAAY,aAAa,IAAI,CAAC,OAAO;AAAA,MACzC,MAAM,EAAE;AAAA,MAAM,aAAa,EAAE;AAAA,MAC7B,YAAY,EAAE;AAAA,IAChB,EAAE;AACF,UAAM,OAA4B;AAAA,MAChC,OAAO,KAAK,UAAU,MAAM;AAAA,MAAG,eAAe,KAAK,SAAS;AAAA,MAC5D,OAAO,UAAU,SAAS,IAAI,YAAY;AAAA,MAC1C,gBAAgB,KAAK,SAAS,kBAAkB;AAAA,MAChD,KAAK,KAAK,SAAS;AAAA,IACrB;AACA,QAAI,KAAK,UAAU,OAAQ,QAAO,sBAAsB,IAAI;AAC5D,WAAO,kBAAkB,IAAI;AAAA,EAC/B;AAAA,EAEA,wBAA0C;AACxC,UAAM,QAAQ,KAAK,UAAU,SAAS,KAAK,cAAc,cAAc,IAAI,KAAK,cAAc,KAAK;AACnG,WAAO,MAAM,IAAI,CAAC,OAAO;AAAA,MACvB,MAAM;AAAA,MACN,UAAU,EAAE,MAAM,EAAE,MAAM,aAAa,EAAE,aAAa,YAAY,EAAE,WAAiD;AAAA,IACvH,EAAE;AAAA,EACJ;AACF;;;ACniBA,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,CAAC,aAAa;AAC7B,QAAI;AACN,QAAI;AACJ,QAAI;AAEJ,QAAI,gBAAgB;AAClB,iBAAW;AACX,kBAAY;AACZ,iBAAW;AASX,UAAI,aAAa,aAAa,SAAS,UAAU,CAAC,MAAM,MAAM;AAC5D,cAAM,WAAW,sBAAsB,UAAU,CAAC,KAAK,EAAE;AACzD,oBAAY,CAAC,MAAM,QAAQ;AAAA,MAC7B;AAAA,IACF,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,GAAGE,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,MAAM;AAAA,IAChC,CAAC;AAED,UAAM,OAAO,GAAG,QAAQ,CAAC,SAAiB;AACxC,gBAAU,KAAK,SAAS,MAAM;AAAA,IAChC,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,eAAS,EAAE,QAAQ,QAAQ,UAAU,KAAK,CAAC;AAAA,IAC7C,CAAC;AAED,UAAM,GAAG,SAAS,CAAC,QAAQ;AACzB,mBAAa,OAAO;AACpB,gBAAU;AAAA,4CAAY,IAAI,OAAO;AACjC,eAAS,EAAE,QAAQ,QAAQ,UAAU,KAAK,CAAC;AAAA,IAC7C,CAAC;AAAA,EACH,CAAC;AACH;;;AC1LA,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;AAGjB,QAAM,IAAc,IAAI,MAAM,IAAI,OAAO,CAAC,EAAE,KAAK,EAAE;AAEnD,QAAM,SAAS;AAEf,IAAE,SAAS,CAAC,IAAI;AAGhB,WAAS,IAAI,GAAG,KAAK,MAAM,KAAK;AAE9B,UAAM,WAAW,oBAAI,IAAoB;AAEzC,UAAM,SAAS,CAAC;AAChB,UAAM,OAAO;AAEb,aAAS,IAAI,QAAQ,KAAK,MAAM,KAAK,GAAG;AACtC,YAAM,KAAK,IAAI;AACf,UAAI;AAEJ,UAAI,MAAM,CAAC,KAAM,MAAM,KAAK,EAAE,KAAK,CAAC,IAAK,EAAE,KAAK,CAAC,GAAK;AACpD,YAAI,EAAE,KAAK,CAAC;AAAA,MACd,OAAO;AACL,YAAI,EAAE,KAAK,CAAC,IAAK;AAAA,MACnB;AAEA,UAAI,IAAI,IAAI;AAGZ,aAAO,IAAI,KAAK,IAAI,KAAK,SAAS,CAAC,MAAM,SAAS,CAAC,GAAG;AACpD;AACA;AAAA,MACF;AAEA,QAAE,EAAE,IAAI;AACR,eAAS,IAAI,GAAG,CAAC;AAGjB,UAAI,KAAK,KAAK,KAAK,GAAG;AACpB,cAAM,KAAK,QAAQ;AAEnB,eAAO,UAAU,OAAO,GAAG,GAAG,UAAU,UAAU,MAAM;AAAA,MAC1D;AAAA,IACF;AAEA,UAAM,KAAK,QAAQ;AAAA,EACrB;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,SACY;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,MAAM,IAAI,CAAC;AAChC,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,EAAG,CAAC;AAAA,MACpD;AAGA,UAAI,IAAI,OAAO;AACb;AACA,eAAO,QAAQ,EAAE,IAAI,UAAU,MAAM,SAAS,CAAC,EAAG,CAAC;AAAA,MACrD;AAAA,IACF,OAAO;AAML,aAAO,IAAI,SAAS,IAAI,QAAQ,GAAG;AACjC;AACA;AACA,eAAO,QAAQ,EAAE,IAAI,SAAS,MAAM,SAAS,CAAC,EAAG,CAAC;AAAA,MACpD;AAGA,UAAI,IAAI,OAAO;AACb;AACA,eAAO,QAAQ,EAAE,IAAI,OAAO,MAAM,SAAS,CAAC,EAAG,CAAC;AAAA,MAClD;AAAA,IACF;AAAA,EACF;AAIA,SAAO,IAAI,KAAK,IAAI,GAAG;AACrB;AACA;AACA,WAAO,QAAQ,EAAE,IAAI,SAAS,MAAM,SAAS,CAAC,EAAG,CAAC;AAAA,EACpD;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,CAAE,CAAC;AAE/C,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,UAAMC,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,YAAAC,iBAAgB;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,KAAK;AACrB,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,WAAWC,UAAS,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,QAAI,MAAM,CAAC,MAAM,QAAQ;AACvB,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,EAAG;AAC7B;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;AACpB,SAAS,cAAAC,mBAAkB;AAC3B,SAAS,QAAAC,aAAY;AAKrB,IAAMC,aAAYC,SAAQ,aAAa;AAsBvC,SAAS,cAA4B;AACnC,MAAI,CAACD,YAAW;AACd,WAAO;AAAA,MACL,KAAK;AAAA,MACL,MAAM,CAAC,IAAI;AAAA,MACX,OAAO;AAAA,MACP,MAAM;AAAA,IACR;AAAA,EACF;AAGA,QAAM,UAAUC,SAAQ,IAAI;AAC5B,MAAI,WAAWC,YAAW,OAAO,GAAG;AAClC,WAAO,EAAE,KAAK,SAAS,MAAM,CAAC,IAAI,GAAG,OAAO,YAAY,MAAM,gBAAgB;AAAA,EAChF;AAIA,QAAM,QAAQ;AAAA,IACZD,SAAQ,IAAI;AAAA,IACZA,SAAQ,IAAI,mBAAmB;AAAA,IAC/BA,SAAQ,IAAI,eAAeE,MAAKF,SAAQ,IAAI,cAAc,UAAU,IAAI;AAAA;AAAA,IAExE;AAAA,IACA;AAAA,EACF,EAAE,OAAO,CAAC,MAAmB,QAAQ,CAAC,CAAC;AAEvC,aAAW,QAAQ,OAAO;AACxB,eAAW,OAAO,CAAC,sBAAsB,yBAAyB,GAAG;AACnE,YAAM,YAAYE,MAAK,MAAM,GAAG;AAChC,UAAID,YAAW,SAAS,GAAG;AACzB,eAAO,EAAE,KAAK,WAAW,MAAM,CAAC,IAAI,GAAG,OAAO,YAAY,MAAM,gBAAgB;AAAA,MAClF;AAAA,IACF;AAAA,EACF;AAGA,SAAO;AAAA,IACL,KAAK;AAAA,IACL,MAAM,CAAC,IAAI;AAAA,IACX,OAAO;AAAA,IACP,MACE;AAAA,EACJ;AACF;AAGA,IAAM,kBACJ;AAEF,IAAM,aAAa,YAAY;AAgBxB,IAAM,WAAgC;AAAA,EAC3C,MAAM;AAAA,EACN;AAAA,EACA,aAAa;AAAA,IACX;AAAA,IACA,2BAAY,WAAW,KAAK,SAAI,WAAW,IAAI;AAAA,IAC/C;AAAA,IACA;AAAA,EACF,EAAE,KAAK,EAAE;AAAA,EACT,YAAY;AAAA,IACV,MAAM;AAAA,IACN,YAAY;AAAA,MACV,SAAS;AAAA,QACP,MAAM;AAAA,QACN,aAAa,4EAA0B,WAAW,KAAK;AAAA,MACzD;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;AAEF,YAAM,eAAe,WAAW,OAAO;AACvC,YAAM,YAAY,CAAC,GAAG,WAAW,MAAM,KAAK,OAAO;AAEnD,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;;;AC5KA,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,YAAU,QAAAC,aAAY;AACxC,SAAS,QAAAC,OAAM,YAAAC,WAAU,cAAAC,mBAAkB;AA4B3C,eAAe,aACb,KACA,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,WAAW,WAAW,QAAQ,MAAM,CAAC;AAAA,IACpF,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,KAAK,SAAS,QAAQ;AAClE,YAAM,UAAuB,CAAC;AAE9B,iBAAW,YAAY,OAAO;AAC5B,YAAI;AACF,gBAAM,UAAU,MAAMC,WAAS,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,KAAK,EAAE,GAAG;AAC9B,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;;;AChLA,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,SAAS,CAAC,GAAG,MAAM;AAC7C,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;AAE9E,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;;;AC1GO,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;;;AlCqhCkB,SAoMV,UApMU,OAAAC,MAUN,QAAAC,aAVM;AAtkClB,IAAM,eAAe;AAAA,EACnB,UAAU,EAAE,MAAM,aAAM,OAAO,sBAAO,OAAO,UAAU;AAAA,EACvD,YAAY,EAAE,MAAM,UAAK,OAAO,sBAAO,OAAO,UAAU;AAAA,EACxD,eAAe,EAAE,MAAM,aAAM,OAAO,4BAAQ,OAAO,UAAU;AAAA,EAC7D,iBAAiB,EAAE,MAAM,UAAK,OAAO,4BAAQ,OAAO,UAAU;AAChE;AAGO,SAAS,mBAAmB,MAAuB;AACxD,SAAO,SAAS,eAAe,SAAS,gBAAgB,SAAS,gBAAgB,SAAS;AAC5F;AAeA,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;AACpG,gBAAgB,SAAS,EAAE,MAAM,mEAAsB,SAAS,OAAO,EAAE,MAAM,QAAQ,SAAS,uFAAgC,GAAG,CAAC;AACpI,gBAAgB,SAAS,EAAE,MAAM,mEAAsB,SAAS,OAAO,EAAE,MAAM,QAAQ,SAAS,uFAAgC,GAAG,CAAC;AACpI,gBAAgB,WAAW,EAAE,MAAM,gFAAoB,SAAS,OAAO,EAAE,MAAM,QAAQ,SAAS,8PAAqE,GAAG,CAAC;AAGzK,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,gBAAgB,iBAAiB,IAAIA,UAAiF,IAAI;AACjI,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,eAAe,eAAe,IAAIA,UAAgC,MAAS;AAClF,QAAM,CAAC,iBAAiB,iBAAiB,IAAIA,UAA6B,MAAS;AACnF,QAAM,CAAC,cAAc,cAAc,IAAIA,UAA6B,MAAS;AAE7E,QAAM,CAAC,aAAa,cAAc,IAAIA,UAAkB,KAA2B;AACnF,QAAM,CAAC,iBAAiB,iBAAiB,IAAIA,UAA6B,MAAS;AACnF,QAAM,CAAC,aAAa,cAAc,IAAIA,UAA6B,MAAS;AAG5E,QAAM,CAAC,aAAa,cAAc,IAAIA,UAAsB,MAAM;AAGlE,QAAM,CAAC,iBAAiB,kBAAkB,IAAIA,UAAS,IAAI;AAC3D,QAAM,CAAC,gBAAgB,iBAAiB,IAAIA,UAAyB,MAAM;AAC3E,QAAM,CAAC,cAAc,IAAIA,UAAiC,MAAM;AAChE,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,iBAAiB,kBAAkB,IAAIA,UAAS,KAAK;AAC5D,QAAM,CAAC,mBAAmB,oBAAoB,IAAIA,UAAS,CAAC;AAC5D,QAAM,CAAC,YAAY,aAAa,IAAIA,UAAkC,CAAC,CAAC;AACxE,QAAM,CAAC,WAAW,YAAY,IAAIA,UAAS,KAAK;AAKhD,QAAM,CAAC,iBAAiB,kBAAkB,IAAIA,UAAyC,MAAM;AAE7F,QAAM,0BAA0BC,QAAO,KAAK;AAE5C,QAAM,qBAAqBA,QAA6C,IAAI;AAG5E,QAAM,CAAC,gBAAgB,iBAAiB,IAAID,UAAS,KAAK;AAC1D,QAAM,CAAC,kBAAkB,mBAAmB,IAAIA,UAAS,CAAC;AAC1D,QAAM,eAA0B,CAAC,qBAAqB,iBAAiB;AAGvE,QAAM,aAAaC,QAAuB,IAAI;AAC9C,QAAM,WAAWA,QAAwB,IAAI;AAG7C,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,EAAAA,WAAU,MAAM;AACd,WAAO,MAAM;AACX,UAAI,mBAAmB,SAAS;AAC9B,qBAAa,mBAAmB,OAAO;AACvC,2BAAmB,UAAU;AAAA,MAC/B;AAAA,IACF;AAAA,EACF,GAAG,CAAC,CAAC;AAGL,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;AAGA,WAAS,0BAA0B,MAAqB;AACtD,UAAM,OAAyB,CAAC;AAChC,eAAW,KAAK,KAAK,UAAU;AAC7B,UAAI,EAAE,SAAS,QAAQ;AACrB,aAAK,KAAK,EAAE,MAAM,QAAQ,SAAS,EAAE,QAAQ,CAAC;AAAA,MAChD,WAAW,EAAE,SAAS,aAAa;AACjC,aAAK,KAAK;AAAA,UACR,MAAM;AAAA,UACN,SAAS,EAAE;AAAA,UACX,iBAAiB;AAAA,YACf,SAAS,EAAE;AAAA,YACX,WAAW,EAAE;AAAA,UACf;AAAA,QACF,CAAC;AAAA,MACH,WAAW,EAAE,SAAS,QAAQ;AAC5B,aAAK,KAAK,EAAE,MAAM,QAAQ,SAAS,EAAE,QAAQ,CAAC;AAAA,MAChD;AAAA,IAEF;AACA,uBAAmB,IAAI;AACvB,iBAAa,CAAC,SAAS,OAAO,CAAC;AAAA,EACjC;AAEA,QAAM,WAAWA,aAAY,OAAO,QAA+B,kBAA0B;AAC3F,UAAM,UAAU,WAAW;AAC3B,QAAI,CAAC,QAAS;AACd,iBAAa,IAAI;AACjB,mBAAe,IAAI;AACnB,sBAAkB,UAAU;AAC5B,4BAAwB,+DAAa;AACrC,aAAS,EAAE;AACX,QAAI;AACF,YAAM,IAAkB,MAAM,QAAQ,OAAO,OAAO,KAAK;AACzD,UAAI,EAAE,IAAI;AACR,kCAA0B,OAAO;AACjC,cAAM,OAAO,EAAE,eAAe,2DAAc;AAC5C,2BAAmB,CAAC,SAAS;AAAA,UAC3B,GAAG;AAAA,UACH,EAAE,MAAM,aAAa,SAAS,sDAAc,aAAa,GAAG,IAAI,SAAI;AAAA,QACtE,CAAC;AAAA,MACH,OAAO;AACL,2BAAmB,CAAC,SAAS;AAAA,UAC3B,GAAG;AAAA,UACH,EAAE,MAAM,aAAa,SAAS,wCAAU,EAAE,KAAK,GAAG;AAAA,QACpD,CAAC;AAAA,MACH;AAAA,IACF,SAAS,KAAc;AACrB,YAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D,yBAAmB,CAAC,SAAS;AAAA,QAC3B,GAAG;AAAA,QACH,EAAE,MAAM,aAAa,SAAS,wCAAU,GAAG,GAAG;AAAA,MAChD,CAAC;AAAA,IACH,UAAE;AACA,mBAAa,KAAK;AAClB,qBAAe,KAAK;AACpB,wBAAkB,IAAI;AACtB,8BAAwB,EAAE;AAAA,IAC5B;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,QAAM,EAAE,aAAa,YAAY,IAAI,eAAe,MAAM;AAExD,UAAM,IAAI,WAAW;AACrB,QAAI,GAAG;AACL,WAAK,EAAE,SAAS,EAAE,QAAQ,MAAM,QAAQ,KAAK,CAAC,CAAC;AAAA,IACjD,OAAO;AACL,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF,CAAC;AAGD;AAAA,IACEA;AAAA,MACE,CAAC,QAAQ,QAAQ;AAEf,YAAI,iBAAiB;AACnB,cAAI,IAAI,SAAS;AACf,iCAAqB,CAAC,UAAU,OAAO,IAAI,WAAW,UAAU,WAAW,MAAM;AAAA,UACnF,WAAW,IAAI,WAAW;AACxB,iCAAqB,CAAC,UAAU,OAAO,KAAK,WAAW,MAAM;AAAA,UAC/D,WAAW,IAAI,QAAQ;AACrB,kBAAM,SAAS,WAAW,iBAAiB;AAC3C,+BAAmB,KAAK;AACxB,gBAAI,QAAQ;AACV,mBAAK,SAAS,QAAQ,oBAAoB,CAAC;AAAA,YAC7C;AAAA,UACF,WAAW,IAAI,QAAQ;AACrB,+BAAmB,KAAK;AACxB,+BAAmB,CAAC,SAAS;AAAA,cAC3B,GAAG;AAAA,cACH,EAAE,MAAM,aAAa,SAAS,uCAAS;AAAA,YACzC,CAAC;AAAA,UACH;AACA;AAAA,QACF;AAGA,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,IAAI,SAAS,IAAI,KAAK;AACxB,cAAI,CAAC,aAAa;AAChB,kBAAM,UAAuB,gBAAgB,SAAS,SAAS;AAC/D,2BAAe,OAAO;AACtB,uBAAW,SAAS,QAAQ,OAAO;AACnC,0BAAc,MAAS;AACvB,uBAAW,SAAS,MAAM;AAC1B,+BAAmB,CAAC,SAAS;AAAA,cAC3B,GAAG;AAAA,cACH;AAAA,gBAAE,MAAM;AAAA,gBAAa,SAAS,YAAY,SACtC,yPACA;AAAA,cACJ;AAAA,YACF,CAAC;AAAA,UACH;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,kBAAkB,aAAa,gBAAgB,iBAAiB,mBAAmB,YAAY,QAAQ;AAAA,IACtQ;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,aAAa,SAAS,WAAW,UAAU,GAAG;AAC7D,YAAI,eAAe,WAAW;AAC5B,gBAAM,SAAS,YAAY,uBAAQ;AACnC,6BAAmB,CAAC,SAAS;AAAA,YAC3B,GAAG;AAAA,YACH,EAAE,MAAM,QAAQ,SAAS,QAAQ;AAAA,YACjC,EAAE,MAAM,aAAa,SAAS,sBAAO,MAAM,qDAAkB;AAAA,UAC/D,CAAC;AACD,mBAAS,EAAE;AACX;AAAA,QACF;AACA,YAAI,CAAC,WAAW,SAAS;AACvB,6BAAmB,CAAC,SAAS;AAAA,YAC3B,GAAG;AAAA,YACH,EAAE,MAAM,QAAQ,SAAS,QAAQ;AAAA,YACjC,EAAE,MAAM,aAAa,SAAS,wEAAsB;AAAA,UACtD,CAAC;AACD,mBAAS,EAAE;AACX;AAAA,QACF;AACA,cAAM,MAAM,WAAW,QAAQ,gBAAgB;AAC/C,YAAI,IAAI,WAAW,GAAG;AACpB,6BAAmB,CAAC,SAAS;AAAA,YAC3B,GAAG;AAAA,YACH,EAAE,MAAM,QAAQ,SAAS,QAAQ;AAAA,YACjC,EAAE,MAAM,aAAa,SAAS,2MAA2C;AAAA,UAC3E,CAAC;AACD,mBAAS,EAAE;AACX;AAAA,QACF;AAEA,cAAM,QAAQ,QAAQ,MAAM,KAAK;AACjC,YAAI,MAAM,UAAU,GAAG;AACrB,gBAAM,IAAI,OAAO,MAAM,CAAC,CAAC;AACzB,cAAI,CAAC,OAAO,UAAU,CAAC,KAAK,IAAI,KAAK,IAAI,IAAI,QAAQ;AACnD,+BAAmB,CAAC,SAAS;AAAA,cAC3B,GAAG;AAAA,cACH,EAAE,MAAM,QAAQ,SAAS,QAAQ;AAAA,cACjC,EAAE,MAAM,aAAa,SAAS,8CAAW,MAAM,CAAC,CAAC,0CAAY,IAAI,MAAM,+CAAY;AAAA,YACrF,CAAC;AACD,qBAAS,EAAE;AACX;AAAA,UACF;AAEA,gBAAM,SAAS,IAAI,IAAI,SAAS,CAAC;AACjC,mBAAS,EAAE;AACX,gBAAM,SAAS,QAAQ,CAAC;AACxB;AAAA,QACF;AAEA,sBAAc,CAAC,GAAG,GAAG,EAAE,QAAQ,CAAC;AAChC,6BAAqB,CAAC;AACtB,2BAAmB,IAAI;AACvB,2BAAmB,CAAC,SAAS;AAAA,UAC3B,GAAG;AAAA,UACH,EAAE,MAAM,QAAQ,SAAS,QAAQ;AAAA,UACjC,EAAE,MAAM,aAAa,SAAS,+GAA+B,IAAI,MAAM,8CAAW;AAAA,QACpF,CAAC;AACD,iBAAS,EAAE;AACX;AAAA,MACF;AAGA,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,SAAS;AACxB,YAAI,gBAAgB,QAAQ;AAC1B,6BAAmB,CAAC,SAAS;AAAA,YAC3B,GAAG;AAAA,YACH,EAAE,MAAM,QAAQ,SAAS,QAAQ;AAAA,YACjC,EAAE,MAAM,aAAa,SAAS,sHAA4B;AAAA,UAC5D,CAAC;AAAA,QACH,OAAO;AACL,yBAAe,MAAM;AACrB,qBAAW,SAAS,QAAQ,MAAM;AAElC,wBAAc,MAAS;AACvB,6BAAmB,CAAC,SAAS;AAAA,YAC3B,GAAG;AAAA,YACH,EAAE,MAAM,QAAQ,SAAS,QAAQ;AAAA,YACjC,EAAE,MAAM,aAAa,SAAS,iSAAmE;AAAA,UACnG,CAAC;AAED,qBAAW,SAAS,MAAM;AAAA,QAC5B;AACA,iBAAS,EAAE;AACX;AAAA,MACF;AAGA,UAAI,aAAa,SAAS;AACxB,YAAI,gBAAgB,QAAQ;AAC1B,6BAAmB,CAAC,SAAS;AAAA,YAC3B,GAAG;AAAA,YACH,EAAE,MAAM,QAAQ,SAAS,QAAQ;AAAA,YACjC,EAAE,MAAM,aAAa,SAAS,sHAA4B;AAAA,UAC5D,CAAC;AAAA,QACH,OAAO;AACL,yBAAe,MAAM;AACrB,qBAAW,SAAS,QAAQ,MAAM;AAElC,wBAAc,MAAS;AACvB,6BAAmB,CAAC,SAAS;AAAA,YAC3B,GAAG;AAAA,YACH,EAAE,MAAM,QAAQ,SAAS,QAAQ;AAAA,YACjC,EAAE,MAAM,aAAa,SAAS,yQAA+D;AAAA,UAC/F,CAAC;AAED,qBAAW,SAAS,MAAM;AAAA,QAC5B;AACA,iBAAS,EAAE;AACX;AAAA,MACF;AAGA,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,kBAAM,WAAW,SAAS,SAAS;AACnC,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,sBAAkB,UAAU;AAC5B,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,4BAAwB,UAAU;AAClC,uBAAmB,MAAM;AACzB,QAAI,mBAAmB,SAAS;AAC9B,mBAAa,mBAAmB,OAAO;AACvC,yBAAmB,UAAU;AAAA,IAC/B;AAEA,UAAM,UAAU,WAAW;AAC3B,UAAM,kBAAkB,IAAI,gBAAgB;AAC5C,aAAS,UAAU;AAEnB,QAAI;AACF,uBAAiB,SAAS,QAAQ,KAAK,SAAS;AAAA,QAC9C,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,YAAY;AAC9B,8BAAkB,CAAC,SAAS;AAC1B,oBAAM,OAAO,OAAO,MAAM;AAC1B,gCAAkB,UAAU;AAC5B,qBAAO;AAAA,YACT,CAAC;AACD;AAAA,UAEF,KAAK;AACH,8BAAkB,eAAe;AACjC,gCAAoB,CAAC,SAAS;AAC5B,oBAAM,OAAO,CAAC,GAAG,MAAM,GAAG,MAAM,KAAK;AACrC,kCAAoB,UAAU;AAC9B,qBAAO;AAAA,YACT,CAAC;AAED,uBAAW,QAAQ,MAAM,OAAO;AAC9B,kBAAI,mBAAmB,KAAK,IAAI,GAAG;AACjC,wCAAwB,UAAU;AAClC;AAAA,cACF;AAAA,YACF;AACA;AAAA,UAEF,KAAK;AAGH,8BAAkB,iBAAiB;AAEnC,uBAAW,MAAM,kBAAkB,UAAU,GAAG,GAAG;AACnD,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;AAEE,oBAAM,OAAO,cAAc,MAAM,OAAO,MAAM,KAA2B;AACzE,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,wBAAkB,IAAI;AACtB,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;AAIA,UAAI,wBAAwB,WAAW,CAAC,gBAAgB;AACtD,2BAAmB,SAAS;AAC5B,YAAI,mBAAmB,QAAS,cAAa,mBAAmB,OAAO;AACvE,2BAAmB,UAAU,WAAW,MAAM;AAC5C,6BAAmB,CAAC,SAAU,SAAS,YAAY,YAAY,IAAK;AACpE,6BAAmB,UAAU;AAAA,QAC/B,GAAG,GAAI;AAAA,MACT;AAAA,IACF;AAAA,EACF,GAAG,CAAC,cAAc,eAAe,gBAAgB,kBAAkB,QAAQ,kBAAkB,mBAAmB,iBAAiB,gBAAgB,gBAAgB,YAAY,aAAa,aAAa,aAAa,SAAS,CAAC;AAG9N,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,QAGhF,gBAAgB,UACf,gBAAAR,KAACQ,QAAA,EAAK,OAAM,WAAU,MAAI,MAAE,kDAAY;AAAA,QAEzC,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,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,IACE,kBACF,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,0EAAU;AAAA,QACpC,WAAW,IAAI,CAACC,KAAI,MAAM;AACzB,gBAAM,aAAa,MAAM;AACzB,gBAAM,SAAS,aAAa,QAAQ;AACpC,gBAAM,OAAO,IAAI,KAAKA,IAAG,SAAS,EAAE,mBAAmB;AACvD,gBAAM,UAAUA,IAAG,WAAW;AAC9B,gBAAM,MAAMA,IAAG,YAAY,KAAK;AAChC,iBACE,gBAAAT,KAACO,MAAA,EACC,0BAAAN;AAAA,YAACO;AAAA,YAAA;AAAA,cACC,OAAO,aAAa,YAAY;AAAA,cAChC,MAAM;AAAA,cAEL;AAAA;AAAA,gBAAO;AAAA,gBAAE,IAAI;AAAA,gBAAE;AAAA,gBAAE;AAAA,gBAAK;AAAA,gBAAG;AAAA,gBAAS;AAAA,gBAAI;AAAA;AAAA;AAAA,UACzC,KANQC,IAAG,KAOb;AAAA,QAEJ,CAAC;AAAA,QACD,gBAAAT,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,YAEI;AAAA,iCAA0B,gBAAgB,WAAW,eAAe,iBACpE,gBAAAD,KAACO,MAAA,EAAI,WAAW,GAAG,gBAAe,UAChC,0BAAAN,MAACO,QAAA,EAAK,MAAI,MAAC,OAAO,aAAa,cAAc,EAAE,OAC5C;AAAA,qBAAa,cAAc,EAAE;AAAA,QAAK;AAAA,QAAE,aAAa,cAAc,EAAE;AAAA,QAAO;AAAA,QACzE,gBAAAR,KAACU,aAAA,EAAW,MAAK,QAAO;AAAA,SAC1B,GACF,IACE,oBAAoB;AAAA;AAAA;AAAA,QAGtB,gBAAAV,KAACO,MAAA,EAAI,WAAW,GAAG,gBAAe,UAChC,0BAAAP,KAACQ,QAAA,EAAK,OAAM,WAAW,yEAAsB,GAC/C;AAAA,UACE;AAAA,MACJ,gBAAAR,KAACO,MAAA,EAAI,WAAW,GACb,oCAA0B,gBAAgB,SACzC,gBAAAN,MAACO,QAAA,EAAK,OAAO,gBAAgB,SAAS,YAAY,WAAW,UAAU,gBAAgB,QACrF;AAAA,wBAAAR,KAACQ,QAAA,EAAM,0BAAgB,SACnB,SAAI,OAAO,KAAK,IAAI,eAAe,IAAI,CAAC,CAAC,IACzC,SAAI,OAAO,KAAK,IAAI,eAAe,IAAI,EAAE,CAAC,GAAE;AAAA,QAC/C,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,KAACU,aAAA,EAAW,MAAK,QAAO;AAAA,WAAE,IAC7G,cAAc,IAChB,gBAAAT,MAACO,QAAA,EAAK,OAAM,QAAQ;AAAA;AAAA,UAAa,YAAY,QAAQ,CAAC;AAAA,WAAE,IACtD;AAAA,QACH,gBAAgB,UACf,gBAAAR,KAACQ,QAAA,EAAK,OAAM,WAAU,MAAI,MAAE,kDAAY;AAAA,SAE5C,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;;;AmC54CA,SAAS,OAAAG,OAAK,QAAAC,QAAM,YAAAC,iBAAgB;AACpC,SAAS,YAAAC,WAAU,eAAAC,oBAAmB;AA8C9B,gBAAAC,OASI,QAAAC,cATJ;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,OAACI,OAAA,EAAI,eAAc,UACjB;AAAA,oBAAAL,MAACK,OAAA,EAAI,cAAc,GACjB,0BAAAL,MAACM,QAAA,EAAK,MAAI,MAAC,OAAM,WAAU,gDAE3B,GACF;AAAA,IAEA,gBAAAN,MAACK,OAAA,EAAI,eAAc,UAChB,gBAAM,IAAI,CAAC,MAAM,UAAU;AAC1B,YAAM,aAAa,UAAU;AAC7B,aACE,gBAAAJ,OAACI,OAAA,EAAkB,eAAc,OAC/B;AAAA,wBAAAL,MAACK,OAAA,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,OAAA,EAAI,OAAO,IAAI,YAAY,GAC1B,0BAAAL,MAACM,QAAA,EAAK,MAAI,MAAC,OAAO,aAAa,YAAY,WACxC,eAAK,MACR,GACF;AAAA,QACA,gBAAAN,MAACK,OAAA,EACC,0BAAAL,MAACM,QAAA,EAAK,OAAM,WAAW,eAAK,aAAY,GAC1C;AAAA,WAjBQ,KAAK,EAkBf;AAAA,IAEJ,CAAC,GACH;AAAA,IAEA,gBAAAN,MAACK,OAAA,EAAI,WAAW,GACd,0BAAAL,MAACM,QAAA,EAAK,UAAQ,MACX,8EACH,GACF;AAAA,IAGC,eACC,gBAAAN,MAACK,OAAA,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,UAAU,KAAK,OAAO,aAAa,UAAU,CAAC;AACpD,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,CAAC;AAAA,MAC7C,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,EAAI,SAC9D,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,WAAWJ,QAAkBK,oBAAmB,CAAC;AACvD,QAAM,CAACE,OAAM,OAAO,IAAIT,UAAS,CAAC;AAClC,QAAM,CAAC,aAAa,cAAc,IAAIA,UAAS,CAAC;AAChD,QAAM,YAAYE,QAAO,OAAO;AAChC,YAAU,UAAU;AAGpB,EAAAD,WAAU,MAAM;AACd,UAAM,QAAQ,YAAY,MAAM;AAC9B,qBAAe,CAAC,UAAU,OAAO,KAAKK,eAAc,MAAM;AAAA,IAC5D,GAAG,GAAG;AACN,WAAO,MAAM,cAAc,KAAK;AAAA,EAClC,GAAG,CAAC,CAAC;AAGL,EAAAL,WAAU,MAAM;AACd,UAAM,WAAW,YAAY,MAAM;AACjC,MAAAO,QAAO,SAAS,OAAO;AACvB,cAAQ,CAAC,MAAM,IAAI,CAAC;AAAA,IACtB,GAAG,EAAE;AACL,WAAO,MAAM,cAAc,QAAQ;AAAA,EACrC,GAAG,CAAC,CAAC;AAEL,EAAAV;AAAA,IACEK,aAAY,CAAC,OAAO,QAAQ;AAC1B,YAAMO,KAAI,SAAS;AACnB,UAAIA,GAAE,UAAU;AACd,YAAI,UAAU,KAAK;AACjB,mBAAS,UAAUH,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,QAAAG,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,aAAaJ,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,OAAKG;AAEL,SACE,gBAAAJ,OAACT,OAAA,EAAI,eAAc,UAAS,UAAU,GAEpC;AAAA,oBAAAQ,MAACR,OAAA,EAAI,eAAc,OACjB,0BAAAS,OAACR,QAAA,EAAK;AAAA;AAAA,MACD,gBAAAO,MAACP,QAAA,EAAK,OAAM,OAAO,mBAAI,OAAO,KAAK,IAAI,GAAG,EAAE,KAAK,CAAC,GAAE;AAAA,MACtD;AAAA,MAAK;AAAA,MAAG,gBAAAQ,OAACR,QAAA,EAAK,OAAM,QAAO;AAAA;AAAA,QAAI,KAAK,MAAM,EAAE,QAAQ,EAAE;AAAA,SAAE;AAAA,OAC3D,GACF;AAAA,IAGC,CAAC,EAAE,YAAY,EAAE,UAChB,gBAAAO,MAACR,OAAA,EAAI,WAAW,GACd,0BAAAS,OAACR,QAAA,EAAK;AAAA;AAAA,MACA,gBAAAO,MAACP,QAAA,EAAK,OAAM,SAAS,YAAE,OAAM;AAAA,MACjC,gBAAAO,MAACP,QAAA,EAAK,OAAM,SAAS,YAAE,OAAO,MAAM,EAAE,MAAM,MAAM,GAAE;AAAA,OACtD,GACF;AAAA,IAIF,gBAAAQ,OAACT,OAAA,EAAI,eAAc,UAAS,WAAW,GACrC;AAAA,sBAAAS,OAACR,QAAA,EAAK;AAAA;AAAA,QAAE,SAAI,OAAO,MAAM;AAAA,QAAE;AAAA,SAAC;AAAA,MAC3B,KAAK,IAAI,CAAC,KAAK,MACd,gBAAAO,MAACP,QAAA,EAAc,mBAAI,GAAG,YAAX,CAAe,CAC3B;AAAA,MACD,gBAAAQ,OAACR,QAAA,EAAK;AAAA;AAAA,QAAE,SAAI,OAAO,MAAM;AAAA,QAAE;AAAA,SAAC;AAAA,OAC9B;AAAA,IAGC,EAAE,YACD,gBAAAQ,OAACT,OAAA,EAAI,WAAW,GACd;AAAA,sBAAAQ,MAACP,QAAA,EAAK,MAAI,MAAC,OAAM,OAAM,4CAEvB;AAAA,MACA,gBAAAQ,OAACR,QAAA,EACE;AAAA;AAAA,QAAS,gBAAAO,MAACP,QAAA,EAAK,OAAM,UAAU,YAAE,OAAM;AAAA,QACvC;AAAA,SACH;AAAA,OACF;AAAA,IAIF,gBAAAO,MAACR,OAAA,EAAI,WAAW,GACd,0BAAAQ,MAACP,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,IAAIZ;AAAA,QAClB,gBAAAK,MAAC,cAAW,QAAQ,MAAM;AAAE,kBAAQ;AAAG,UAAAO,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,SAASC,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;AAEA,aAAO,UAAU,MAAM,QAA6F;AAAA,IACtH;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;;;ACjKA,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;;;ArDEA,SAAS,UAAAE,eAAc;AACvB,OAAOC,YAAW;;;AsDflB,SAAS,OAAAC,OAAK,QAAAC,QAAM,YAAAC,iBAAgB;AACpC,SAAS,YAAAC,WAAU,eAAAC,cAAa,aAAAC,YAAW,WAAAC,gBAAe;AAC1D,OAAO,gBAAgB;AACvB,OAAO,QAAQ;AACf,SAAS,QAAAC,cAAY;AAyXX,gBAAAC,OAiBF,QAAAC,cAjBE;AAjXV,IAAM,gBAAgBC,OAAK,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;AAE5B,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,KAAM,MAAM,GAAG;AAC9B,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;AAEd,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;AAI5C,QAAM,CAAC,WAAW,YAAY,IAAIA,UAAoB,SAAS;AAE/D,QAAM,eAAeC,SAAQ,MAAM;AACjC,QAAI,cAAc,UAAW,QAAO;AACpC,WAAO,CAAC,GAAG,MAAM,EAAE;AAAA,MAAS,CAAC,GAAG,MAC9B,cAAc,SACV,EAAE,gBAAgB,EAAE,gBACpB,EAAE,gBAAgB,EAAE;AAAA,IAC1B;AAAA,EACF,GAAG,CAAC,QAAQ,SAAS,CAAC;AAEtB,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,SAAK,SAAS;AAAA,EAChB,GAAG,CAAC,QAAQ,CAAC;AAGb,EAAAA,WAAU,MAAM;AACd,UAAM,WAAW,YAAY,MAAM;AACjC,mBAAa,CAAC,SAAS;AACrB,YAAI,QAAQ,GAAG;AACb,eAAK,SAAS;AACd,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,YAAY;AACd,YAAM,aAAa,MAAM;AAEvB,oBAAY,OAAO,WAAW,IAAI;AAClC,aAAK,iBAAiB,WAAW,IAAI,EAAE,KAAK,CAAC,SAAS;AACpD,cAAI,QAAQ,KAAK,OAAO,SAAS,GAAG;AAClC,wBAAY,WAAW,MAAM,KAAK,MAAM;AACxC,4BAAgB,KAAK,MAAM;AAAA,UAC7B;AAAA,QACF,CAAC;AAAA,MACH;AAGA,iBAAW;AAGX,yBAAmB,EAAE;AAGrB,YAAM,QAAQ,YAAY,YAAY,GAAK;AAC3C,aAAO,MAAM,cAAc,KAAK;AAAA,IAClC;AAEA,oBAAgB,IAAI;AACpB,qBAAiB,KAAK;AAAA,EACxB,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,eAAK,SAAS;AAAA,QAChB,WAAW,UAAU,KAAK;AACxB,uBAAa,CAAC,SAAU,SAAS,YAAY,SAAS,SAAS,SAAS,QAAQ,SAAU;AAAA,QAC5F,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,0BAAAG,OAACF,QAAA,EAAK,UAAQ,MAAC;AAAA;AAAA,QACT,cAAc,SAAS,YAAO,cAAc,QAAQ,YAAO;AAAA,SACjE,GACF;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,uBAAa,IAAI,CAAC,OAAO,UAAU;AAClC,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,uJACH,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;;;AClnBA,SAAS,WAAAG,gBAAe;AACxB,SAAS,QAAAC,QAAM,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,OAAK,YAAY,IAAI,CAAC,CAAC;AAAA,MACtD;AAAA,IACF;AAAA,EACF;AAGA,QAAM,SAAS,MAAM,QAAQ;AAAA,IAC3B,KAAK,IAAI,CAAC,MAAM,iBAAiB,SAASA,OAAK,YAAY,CAAC,CAAC,CAAC;AAAA,EAChE;AAEA,aAAW,KAAK,QAAQ;AACtB,UAAM,KAAK,GAAG,CAAC;AAAA,EACjB;AAEA,SAAO;AACT;;;AvD2FQ,gBAAAE,aAAA;AA5KR,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;AAEvD,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;AAGA,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,SAAK,UAAU,KAAK,WAAW;AAAA,EACjC,CAAC;AAGD,EAAAD,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;AAEvC,UAAM,OAAQ,KAA4C;AAG1D,UAAM,OAAO,QAAQ,IAAI,QAAQ,QAAQ,IAAI,eAAe;AAC5D,UAAM,mBAAmBE,OAAK,MAAM,YAAY,eAAe;AAC/D,QAAI,uBAAuB;AAC3B,QAAI;AACF,YAAM,MAAM,MAAMC,WAAS,kBAAkB,OAAO;AAEpD,YAAM,SAAS,KAAK,MAAM,GAAG;AAE7B,YAAM,QAAQ,OAAO;AACrB,6BAAuB,MAAM,QAAQ,OAAO,OAAO,KAAM,MAAM,QAAsB,SAAS;AAAA,IAChG,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,GAAGF,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,CAACI,aAAY;AAC/D,cAAM,EAAE,QAAQ,IAAIC;AAAA,UAClB,gBAAAN;AAAA,YAAC;AAAA;AAAA,cACC;AAAA,cACA,UAAU,CAAC,SAAS;AAClB,wBAAQ;AACR,gBAAAK,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,8BAAaH,OAAM,MAAM,aAAa,IAAI,CAAC;AAAA,CAAI;AAC3D,cAAM,aAAa,KAAK;AAAA,MAC1B;AAAA,IACF;AAAA,EACF,CAAC;AAEH,SAAOD;AACT;AAEA,eAAe,UACb,KACA,aACA;AAEA,QAAM,CAAC,kBAAkB,iBAAiB,QAAQ,KAAK,IAAI,MAAM,QAAQ,IAAI;AAAA,IAC3E,mBAAmB;AAAA,IACnB,wBAAwB,QAAQ,IAAI,CAAC;AAAA,IACrC,aAAa,QAAQ,IAAI,CAAC;AAAA,IAC1B,iBAAiB,QAAQ,IAAI,CAAC;AAAA,EAChC,CAAC;AACD,QAAM,aAAa,mBAAmB;AAEtC,QAAM,kBAAkB,KAAK,OAAO,UAAU;AAAA,IAC5C,CAAC,MAAM,EAAE,UAAU,KAAK,OAAO,mBAAmB;AAAA,EACpD;AACA,QAAM,QAAQ,iBAAiB,SAAS;AACxC,QAAM,UAAU;AAAA,IACd,gBAAAD;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA;AAAA,QACA,WAAW,KAAK,OAAO,MAAM,UAAU;AAAA,QACvC,SAAS,KAAK,WAAW;AAAA,QACzB,QAAQ,iBAAiB;AAAA,QACzB,SAAS,iBAAiB,WAAW;AAAA,QACrC;AAAA,QACA;AAAA,QACA,cAAc,MAAM;AAClB,kBAAQ,QAAQ;AAChB,uBAAa,MAAM;AACjB,sBAAU;AACV,kBAAM,QAAQ,UAAU;AACxB,kBAAM,EAAE,QAAQ,IAAIM;AAAA,cAClB,gBAAAN;AAAA,gBAAC;AAAA;AAAA,kBACC;AAAA,kBACA,UAAU,OAAO,SAAe;AAC9B,4BAAQ;AACR,0BAAM,KAAK,KAAK;AAEhB,yBAAK,UAAU,KAAK,WAAW;AAAA,kBACjC;AAAA,kBACA,cAAc,MAAM;AAClB,4BAAQ;AACR,iCAAa,MAAM,UAAU,KAAK,WAAW,CAAC;AAAA,kBAChD;AAAA;AAAA,cACF;AAAA,cACA,EAAE,aAAa,MAAM;AAAA,YACvB;AAAA,UACF,CAAC;AAAA,QACH;AAAA,QACA,eAAe,MAAM;AACnB,kBAAQ,QAAQ;AAChB,uBAAa,MAAM;AAEjB,kBAAM,oBAAoB,KAAK,OAAO,OAAO,SAAS,IAAI,CAAC,MAAM,EAAE,IAAI,KAClE,CAAC,YAAY,YAAY,UAAU;AACxC,kBAAM,WAAW;AAAA,cACf,gBAAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,OAAO;AAAA,kBACP,cAAc,MAAM;AAClB,6BAAS,QAAQ;AACjB,iCAAa,MAAM,UAAU,KAAK,WAAW,CAAC;AAAA,kBAChD;AAAA,kBACA,QAAQ,MAAM,QAAQ,KAAK,CAAC;AAAA;AAAA,cAC9B;AAAA,YACF;AAAA,UACF,CAAC;AAAA,QACH;AAAA;AAAA,IACF;AAAA,EACF;AACF;;;AwDjUO,IAAM,WAAW;AAAA;AAAA,EAEtB,SAAS;AAAA;AAAA,EAET,eAAe;AAAA;AAAA,EAEf,cAAc;AAAA;AAAA,EAEd,QAAQ;AACV;;;ACKA,IAAI,cAAc;AAElB,IAAI,cAAqC;AAUzC,eAAe,aAAa,MAA8B;AACxD,MAAI;AACF,UAAM,QAAQ,IAAI;AAAA,MAChB,YAAY,SAAS;AAAA,MACrB,mBAAmB,SAAS;AAAA,IAC9B,CAAC;AAAA,EACH,QAAQ;AAAA,EAER;AACA,UAAQ,KAAK,IAAI;AACnB;AAEA,QAAQ,GAAG,UAAU,MAAM;AACzB;AACA,MAAI,eAAe,GAAG;AACpB,SAAK,aAAa,SAAS,MAAM;AACjC;AAAA,EACF;AACA,UAAQ,OAAO,MAAM,mEAAgC;AACrD,MAAI,YAAa,cAAa,WAAW;AACzC,gBAAc,WAAW,MAAM;AAC7B,kBAAc;AAAA,EAChB,GAAG,IAAI;AACT,CAAC;AAED,QAAQ,GAAG,WAAW,MAAM;AAC1B,OAAK,aAAa,SAAS,MAAM;AACnC,CAAC;AAED,IAAM,UAAU,UAAU;AAE1B,IAAI;AACF,QAAM,QAAQ,WAAW,QAAQ,IAAI;AAGrC,QAAM,QAAQ,IAAI,CAAC,YAAY,SAAS,GAAG,mBAAmB,SAAS,CAAC,CAAC;AAC3E,SAAS,KAAc;AAErB,QAAM,QAAQ;AAEd,MAAI,MAAM,SAAS,6BAA6B,MAAM,SAAS,qBAAqB;AAClF,UAAM,QAAQ,IAAI,CAAC,YAAY,SAAS,GAAG,mBAAmB,SAAS,CAAC,CAAC;AACzE,YAAQ,KAAK,MAAM,YAAY,SAAS,OAAO;AAAA,EACjD;AAEA,MAAI,OAAO,MAAM,aAAa,UAAU;AACtC,UAAM,QAAQ,IAAI,CAAC,YAAY,SAAS,GAAG,mBAAmB,SAAS,CAAC,CAAC;AACzE,YAAQ,KAAK,MAAM,QAAQ;AAAA,EAC7B;AAEA,UAAQ,MAAM,OAAO,GAAG,CAAC;AACzB,QAAM,QAAQ,IAAI,CAAC,YAAY,SAAS,GAAG,mBAAmB,SAAS,CAAC,CAAC;AACzE,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","Box","Text","jsx","jsxs","cp","jsx","jsxs","Box","Text","Box","Text","jsx","jsxs","Box","Text","jsx","jsxs","Box","Text","jsx","jsxs","HIGHLIGHT_COLOR","mkdir","readFile","writeFile","readdir","join","mkdir","join","items","records","cp","realpath","process","process","patch","writeFile","readFile","relative","readFile","relative","mkdir","readFile","relative","basename","readFile","mkdir","basename","relative","readFile","basename","readFile","basename","readFile","basename","readFile","basename","readFile","basename","readFile","basename","process","existsSync","join","isWindows","process","existsSync","join","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","cp","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","tick","s","resolve","createInitialState","chalk","createInitialState","resolve","render","chalk","Box","Text","useInput","useState","useCallback","useEffect","useMemo","join","jsx","jsxs","join","useState","useMemo","useEffect","useCallback","useInput","jsx","Box","Text","cp","jsxs","readdir","join","relative","jsx","program","chalk","join","readFile","resolve","render"]}