chatlab-cli 0.30.2 → 0.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chat-command-VBDMWOKL.mjs → chat-command-CHV7RLBH.mjs} +9 -8
- package/dist/{chat-command-VBDMWOKL.mjs.map → chat-command-CHV7RLBH.mjs.map} +1 -1
- package/dist/{chunk-326OCJOT.mjs → chunk-DA5T64RY.mjs} +2 -2
- package/dist/{chunk-M2PXDX4G.mjs → chunk-DBBL74RK.mjs} +31 -22
- package/dist/chunk-DBBL74RK.mjs.map +1 -0
- package/dist/{chunk-KBSAVUFV.mjs → chunk-EXD2SXWL.mjs} +89 -15
- package/dist/chunk-EXD2SXWL.mjs.map +1 -0
- package/dist/{chunk-D3DPCDII.mjs → chunk-JH5RAJT2.mjs} +83 -34
- package/dist/chunk-JH5RAJT2.mjs.map +1 -0
- package/dist/{chunk-5EE2EFPE.mjs → chunk-RD335EMA.mjs} +4 -4
- package/dist/{chunk-WV5GSRQM.mjs → chunk-RDCUGC24.mjs} +5 -15
- package/dist/chunk-RDCUGC24.mjs.map +1 -0
- package/dist/{chunk-TEX44HNL.mjs → chunk-SNCXU2QL.mjs} +5 -15
- package/dist/chunk-SNCXU2QL.mjs.map +1 -0
- package/dist/{chunk-36JGKW6K.mjs → chunk-T64GA5HP.mjs} +1134 -364
- package/dist/chunk-T64GA5HP.mjs.map +1 -0
- package/dist/{chunk-LVEJHB3X.mjs → chunk-UWWFOEGF.mjs} +494 -97
- package/dist/chunk-UWWFOEGF.mjs.map +1 -0
- package/dist/{chunk-ULNRUS55.mjs → chunk-WQCY45U2.mjs} +3 -3
- package/dist/chunk-YAQLYL52.mjs +14 -0
- package/dist/chunk-YAQLYL52.mjs.map +1 -0
- package/dist/{chunk-TCZHG3G7.mjs → chunk-YPQ72HMX.mjs} +2 -2
- package/dist/cli.mjs +8 -7
- package/dist/contacts-worker.mjs +2 -2
- package/dist/global-insight-worker.mjs +179 -0
- package/dist/global-insight-worker.mjs.map +1 -0
- package/dist/{http-L4AMELPZ.mjs → http-3B5ARN7A.mjs} +12 -11
- package/dist/{import-WWCSXGVS.mjs → import-2DZGNSEX.mjs} +10 -7
- package/dist/index.mjs +12 -11
- package/dist/{mcp-A4M3LT5O.mjs → mcp-44I6FUUQ.mjs} +12 -11
- package/dist/{mcp-A4M3LT5O.mjs.map → mcp-44I6FUUQ.mjs.map} +1 -1
- package/dist/people-relationships-worker.mjs +2 -2
- package/dist/semantic-index-worker.mjs +1 -1
- package/dist/{src-MPNBTFOE.mjs → src-R3QX6L3L.mjs} +26 -6
- package/dist/{stream-import-HIE7PNL4.mjs → stream-import-VUEL2TTU.mjs} +10 -7
- package/dist/{worker-runtime-BJXO24XX.mjs → worker-runtime-GJAOIIZW.mjs} +6 -5
- package/dist/{worker-runtime-BJXO24XX.mjs.map → worker-runtime-GJAOIIZW.mjs.map} +1 -1
- package/dist-web/assets/ActionToolsPanel-fAio70bI.css +1 -0
- package/dist-web/assets/ActionToolsPanel.vue_vue_type_script_setup_true_lang-CdJT0t2f.js +110 -0
- package/dist-web/assets/{BufferResource-q3rzaWWd.js → BufferResource-BPGkshPa.js} +1 -1
- package/dist-web/assets/{CanvasRenderer-BRk1y8tp.js → CanvasRenderer-DTKkZhq4.js} +1 -1
- package/dist-web/assets/{EChartWordcloud-Clg60FMo.js → EChartWordcloud-DU3ZYdgz.js} +1 -1
- package/dist-web/assets/{Filter-BJwode73.js → Filter-CHXKzS2l.js} +1 -1
- package/dist-web/assets/{LoadingState.vue_vue_type_script_setup_true_lang-D_dJuLWJ.js → LoadingState.vue_vue_type_script_setup_true_lang-DACMroOe.js} +1 -1
- package/dist-web/assets/MessageExportModal-h-zl0dx1.js +1 -0
- package/dist-web/assets/PageHeader-CK0hTsdx.js +1 -0
- package/dist-web/assets/PageHeader-XIXMTomn.css +1 -0
- package/dist-web/assets/{RelationshipGalaxyCanvas-CORMD7vL.js → RelationshipGalaxyCanvas-Dwdy5flh.js} +3 -3
- package/dist-web/assets/{RelationshipGalaxyThreeCanvas-B_w_P4ob.js → RelationshipGalaxyThreeCanvas-CtY4TGen.js} +1 -1
- package/dist-web/assets/{RenderTargetSystem-CGhV9R9E.js → RenderTargetSystem-BE56mKRG.js} +1 -1
- package/dist-web/assets/ThemeCard.vue_vue_type_script_setup_true_lang-BoZyssJF.js +1 -0
- package/dist-web/assets/TimeSelect.vue_vue_type_script_setup_true_lang-27hMyAUE.js +1 -0
- package/dist-web/assets/{WebGLRenderer-CAokTE5X.js → WebGLRenderer-Dm18dxOf.js} +1 -1
- package/dist-web/assets/{WebGPURenderer-DzP4ggSy.js → WebGPURenderer-BF6TF-is.js} +1 -1
- package/dist-web/assets/annual-summary-time-range-DVLXK1r4.js +1 -0
- package/dist-web/assets/base-CFPyD9Gy.js +1 -0
- package/dist-web/assets/{browserAll-CAZCK6dd.js → browserAll-Cn_CfkXl.js} +1 -1
- package/dist-web/assets/dateFormat-BcqNKTQ1.js +1 -0
- package/dist-web/assets/{electron-CkPDbAaS.js → electron-B6xOabTq.js} +1 -1
- package/dist-web/assets/electron-hcWiB7Gg.js +1 -0
- package/dist-web/assets/fetch-BPkgnY0R.js +1 -0
- package/dist-web/assets/{fetch-LX9jrJCC.js → fetch-BRXmDI_2.js} +1 -1
- package/dist-web/assets/{fetch-Apd5OLJw.js → fetch-BUm1isWe.js} +1 -1
- package/dist-web/assets/{fetch-B8rik0TM.js → fetch-CBOJwwJp.js} +3 -3
- package/dist-web/assets/{fetch-wqMSiaK3.js → fetch-CEhUu1Yu.js} +1 -1
- package/dist-web/assets/fetch-C_EfVFJi.js +3 -0
- package/dist-web/assets/{fetch-DQ9UNNAy.js → fetch-DL_u25Ui.js} +1 -1
- package/dist-web/assets/{fetch-BIEXkvDq.js → fetch-DMLq3xPG.js} +1 -1
- package/dist-web/assets/{fetch--UbRoBT7.js → fetch-DylpNKCR.js} +1 -1
- package/dist-web/assets/fetch-etgwVKIw.js +1 -0
- package/dist-web/assets/index-0x1QPzKZ.js +1 -0
- package/dist-web/assets/index-B5S9FnfP.js +1 -0
- package/dist-web/assets/index-BS6loLHW.js +1 -0
- package/dist-web/assets/index-BfO_LpDv.css +1 -0
- package/dist-web/assets/index-BrTlAl8b.js +1 -0
- package/dist-web/assets/index-ByUaiVQK.css +1 -0
- package/dist-web/assets/{index-S-YQRH1E.js → index-C3TnydSt.js} +1 -1
- package/dist-web/assets/index-CAWO10k1.js +2 -0
- package/dist-web/assets/index-C_0GHEcb.css +1 -0
- package/dist-web/assets/index-Chm17wNe.js +2 -0
- package/dist-web/assets/index-DAr_2DlV.js +173 -0
- package/dist-web/assets/index-Em6upenM.js +64 -0
- package/dist-web/assets/index-FoTy2Cce.js +1 -0
- package/dist-web/assets/index-YADL4XIp.js +22 -0
- package/dist-web/assets/index-ZsZwPLWy.css +1 -0
- package/dist-web/assets/index-_6lck7Bv.css +1 -0
- package/dist-web/assets/index-aMIgqj43.js +1 -0
- package/dist-web/assets/index-y7cxH1Et.css +1 -0
- package/dist-web/assets/index-zZVsV5HB.js +1 -0
- package/dist-web/assets/{people-page-header-Mupn-U_b.js → people-page-header-BdKM1qhX.js} +1 -1
- package/dist-web/assets/types-CZnACqaB.js +1 -0
- package/dist-web/assets/{vendor-echarts-hJVnODyU.js → vendor-echarts-B-i_56Av.js} +1 -1
- package/dist-web/assets/{vendor-echarts-wordcloud-k5ih2Jl4.js → vendor-echarts-wordcloud-Ca8n6qS6.js} +1 -1
- package/dist-web/assets/{vendor-nuxt-ui-DaLxYaC4.js → vendor-nuxt-ui-CS1uxE9C.js} +2 -2
- package/dist-web/assets/{vendor-reka-ui-CzM4kDQ7.js → vendor-reka-ui-Cta6JPvN.js} +1 -1
- package/dist-web/assets/{web-DbKs6-1b.js → web-BUZ-dZy0.js} +1 -1
- package/dist-web/assets/{webworkerAll-Bhv-hBqR.js → webworkerAll-BKGP--A4.js} +1 -1
- package/dist-web/index.html +4 -4
- package/package.json +1 -1
- package/dist/chunk-36JGKW6K.mjs.map +0 -1
- package/dist/chunk-D3DPCDII.mjs.map +0 -1
- package/dist/chunk-KBSAVUFV.mjs.map +0 -1
- package/dist/chunk-LVEJHB3X.mjs.map +0 -1
- package/dist/chunk-M2PXDX4G.mjs.map +0 -1
- package/dist/chunk-TEX44HNL.mjs.map +0 -1
- package/dist/chunk-WV5GSRQM.mjs.map +0 -1
- package/dist-web/assets/DebugToolsPanel-D_lFIW6j.css +0 -1
- package/dist-web/assets/DebugToolsPanel.vue_vue_type_script_setup_true_lang-F4n0zNX2.js +0 -110
- package/dist-web/assets/MessageExportModal-CGcdMOyw.js +0 -1
- package/dist-web/assets/PageHeader-Bbn87QZS.css +0 -1
- package/dist-web/assets/PageHeader-CZaqOdBQ.js +0 -1
- package/dist-web/assets/electron-D9vE5t3H.js +0 -1
- package/dist-web/assets/fetch-1pB-HbYw.js +0 -3
- package/dist-web/assets/fetch-CF_3ZqRa.js +0 -1
- package/dist-web/assets/fetch-FSNxSnAp.js +0 -1
- package/dist-web/assets/index---rXG1hZ.css +0 -1
- package/dist-web/assets/index-B01uUoHE.js +0 -1
- package/dist-web/assets/index-B4l3rHie.css +0 -1
- package/dist-web/assets/index-BSIyyt0z.js +0 -1
- package/dist-web/assets/index-Bh8z441f.js +0 -2
- package/dist-web/assets/index-C0rya8v4.js +0 -1
- package/dist-web/assets/index-C1p9CbmN.css +0 -1
- package/dist-web/assets/index-COpza1A4.css +0 -1
- package/dist-web/assets/index-CZbZv2fh.js +0 -64
- package/dist-web/assets/index-CqUuHQBQ.js +0 -22
- package/dist-web/assets/index-CzeTP5MJ.js +0 -173
- package/dist-web/assets/index-DgNxTM_8.js +0 -2
- package/dist-web/assets/index-DosG7LmJ.css +0 -1
- /package/dist/{chunk-326OCJOT.mjs.map → chunk-DA5T64RY.mjs.map} +0 -0
- /package/dist/{chunk-5EE2EFPE.mjs.map → chunk-RD335EMA.mjs.map} +0 -0
- /package/dist/{chunk-ULNRUS55.mjs.map → chunk-WQCY45U2.mjs.map} +0 -0
- /package/dist/{chunk-TCZHG3G7.mjs.map → chunk-YPQ72HMX.mjs.map} +0 -0
- /package/dist/{http-L4AMELPZ.mjs.map → http-3B5ARN7A.mjs.map} +0 -0
- /package/dist/{import-WWCSXGVS.mjs.map → import-2DZGNSEX.mjs.map} +0 -0
- /package/dist/{src-MPNBTFOE.mjs.map → src-R3QX6L3L.mjs.map} +0 -0
- /package/dist/{stream-import-HIE7PNL4.mjs.map → stream-import-VUEL2TTU.mjs.map} +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli.ts","../src/http/port.ts","../src/runtime.ts","../src/query/envelope.ts","../src/query/runner.ts","../src/query/context.ts","../src/query/preprocess-config.ts","../src/query/resolve.ts","../src/query/parse.ts","../src/query/commands-sessions-members.ts","../src/query/messages-output.ts","../src/query/commands-messages.ts","../src/query/commands-stats.ts","../src/query/commands-topics-sql.ts","../src/query/register.ts","../src/query/manifest.ts"],"sourcesContent":["/**\n * ChatLab CLI entry point\n *\n * Dev: pnpm --filter chatlab-cli run cli -- sessions\n */\n\nimport * as fs from 'fs'\nimport { execSync } from 'child_process'\nimport { Command } from 'commander'\nimport { DEFAULT_API_PORT, loadConfig, getConfigPath, setConfigField, ConfigSetError } from '@openchatlab/config'\nimport {\n AIChatManager,\n initAppLogger,\n appLogger,\n getSystemLogsDir,\n logNativeParserStatus,\n} from '@openchatlab/node-runtime'\nimport { getVersion } from './version'\nimport { resolveCliPath } from './paths'\nimport { isPortAvailable, formatPortInUseError } from './http/port'\nimport { initRuntime, resolveNativeBinding } from './runtime'\nimport { registerQueryCommands } from './query/register'\nimport { registerManifestCommand } from './query/manifest'\n\nconst program = new Command()\n\nprogram.name('chatlab').description('ChatLab - Chat history analysis tool').version(getVersion(), '-v, --version')\n\nprogram.hook('preAction', async (_thisCommand, actionCommand) => {\n if (actionCommand.name() === 'update') return\n const { checkForUpdatesInteractive } = await import('./update-checker')\n await checkForUpdatesInteractive()\n})\n\nprogram\n .command('update')\n .description('Update ChatLab CLI to the latest version')\n .action(async () => {\n const { performCliSelfUpdate } = await import('./update-checker')\n const result = await performCliSelfUpdate({\n write: (text) => process.stderr.write(text),\n })\n\n if (result.success) {\n console.error(' Updated successfully. Please restart chatlab to use the new version.\\n')\n return\n }\n\n console.error(` Update failed: ${result.error || 'unknown error'}\\n`)\n process.exitCode = 1\n })\n\n// Agent-facing query commands (sessions/members/messages/stats/topics/sql)\n// plus deprecated top-level aliases; see apps/cli/src/query/.\nregisterQueryCommands(program)\nregisterManifestCommand(program, getVersion())\n\nprogram\n .command('import <file>')\n .description('Import a chat history file (14+ formats: QQ/WeChat/Telegram/WhatsApp/LINE/Discord/Instagram, etc.)')\n .option('--session-id <id>', 'Specify session ID (auto-generated if omitted)')\n .option('--format <id>', 'Specify format ID (skip auto-detection)')\n .action(async (file, options) => {\n if (!fs.existsSync(file)) {\n console.error(`File not found: ${file}`)\n process.exit(1)\n }\n\n const { streamImport, detectFormat } = await import('./import')\n const { dbManager, pathProvider } = initRuntime()\n const nativeBinding = resolveNativeBinding()\n // 记录 Rust native parser 可用性(本次导入是否走 Rust 内核)\n logNativeParserStatus()\n\n const format = detectFormat(file)\n if (!format && !options.format) {\n console.error(`Unrecognized file format: ${file}`)\n console.error('Use --format <id> to specify manually, or run \"chatlab formats\" to see supported formats')\n process.exit(1)\n }\n\n console.log(`Importing: ${file}`)\n if (format) console.log(` Format: ${format.name} (${format.platform})`)\n\n try {\n const result = await streamImport(dbManager, file, {\n formatId: options.format,\n nativeBinding,\n onProgress: (p) => {\n process.stdout.write(`\\r ${p.stage}: ${p.progress}%`)\n },\n })\n\n if (result.success) {\n console.log(`\\n\\nImport succeeded!`)\n console.log(` Session ID: ${result.sessionId}`)\n console.log(` Messages written: ${result.diagnostics?.messagesWritten ?? 0}`)\n console.log(` Messages skipped: ${result.diagnostics?.messagesSkipped ?? 0}`)\n\n if (result.sessionId) {\n try {\n const { PreferencesManager, createDatabaseManagerAdapter, ownerProfileService } =\n await import('@openchatlab/node-runtime')\n const applied = ownerProfileService.tryApplyOwnerProfile(\n createDatabaseManagerAdapter(dbManager),\n new PreferencesManager(pathProvider.getSystemDir()),\n result.sessionId\n )\n if (applied.applied) {\n console.log(` Owner auto-detected: ${applied.ownerId}`)\n }\n } catch (ownerErr) {\n console.warn(` Owner profile apply skipped: ${ownerErr instanceof Error ? ownerErr.message : ownerErr}`)\n }\n }\n } else {\n console.error(`\\n\\nImport failed: ${result.error}`)\n process.exit(1)\n }\n } catch (err) {\n console.error(`\\n\\nImport error: ${err instanceof Error ? err.message : err}`)\n process.exit(1)\n } finally {\n dbManager.closeAll()\n }\n })\n\nprogram\n .command('formats')\n .description('List all supported chat history formats')\n .action(async () => {\n const { getSupportedFormats } = await import('./import')\n const formats = getSupportedFormats()\n console.log(`${formats.length} supported format(s):\\n`)\n for (const f of formats) {\n console.log(` ${f.id.padEnd(30)} ${f.name} (${f.platform}) [${f.extensions.join(', ')}]`)\n }\n })\n\nprogram\n .command('mcp')\n .description('Start MCP Server (stdio transport, for ClaudeCode / Cursor / AI agents)')\n .action(async () => {\n const { startCliMcpServer } = await import('./mcp')\n await startCliMcpServer()\n })\n\nprogram\n .command('chat')\n .description('Ask ChatLab AI about an imported chat session')\n .option('--session-id <id>', 'Source chat session ID')\n .option('--ai-chat-id <id>', 'Existing AI chat ID to continue')\n .option('-q, --question <text>', 'Question to ask')\n .option('--json', 'Output structured JSON')\n .option('--include-events', 'Include all agent stream chunks in JSON output')\n .option('--no-stream', 'Disable streaming output')\n .option('--locale <locale>', 'AI response locale', 'zh-CN')\n .action(async (options) => {\n const { runChatCommand } = await import('./ai/chat-command')\n const { dbManager, pathProvider } = initRuntime()\n const aiChatManager = new AIChatManager(pathProvider.getAiDataDir(), { nativeBinding: resolveNativeBinding() })\n\n try {\n await runChatCommand(\n {\n sessionId: options.sessionId,\n aiChatId: options.aiChatId,\n question: options.question,\n json: !!options.json,\n stream: options.stream,\n locale: options.locale,\n includeEvents: !!options.includeEvents,\n },\n { dbManager, pathProvider, aiChatManager }\n )\n } catch (error) {\n console.error(error instanceof Error ? error.message : String(error))\n process.exitCode = 1\n } finally {\n aiChatManager.close()\n dbManager.closeAll()\n }\n })\n\nprogram\n .command('start')\n .description('Start ChatLab (HTTP API + Web UI)')\n .option('--port <port>', 'Server port', String(DEFAULT_API_PORT))\n .option('--host <host>', 'Listen address', '127.0.0.1')\n .option('--token <token>', 'Custom Bearer Token (reads from config or auto-generates if omitted)')\n .option('--headless', 'API-only mode, do not serve the Web UI')\n .option('--require-auth', 'Require Bearer token for all routes including /_web/*')\n .option('--no-open', 'Do not auto-open the browser')\n .option('--daemon', 'Run as a resident system service (auto-start on login, macOS/Linux)')\n .action(async (options) => {\n // --daemon: install as system service and exit\n if (options.daemon) {\n const { serviceInstall } = await import('./daemon/service')\n serviceInstall({\n port: parseInt(options.port, 10),\n host: options.host,\n token: options.token || undefined,\n headless: options.headless,\n requireAuth: options.requireAuth || undefined,\n })\n return\n }\n\n const { startHttpServer } = await import('./http')\n const port = parseInt(options.port, 10)\n\n let webRoot: string | undefined\n if (!options.headless) {\n const webDir = resolveCliPath('dist-web')\n if (fs.existsSync(webDir)) {\n webRoot = webDir\n } else {\n console.warn('Warning: dist-web/ not found, starting in API-only mode')\n }\n }\n\n try {\n // 启动前预检端口,快速失败,避免无谓的初始化后再报 EADDRINUSE;\n // 置于 try 内确保非 EADDRINUSE 错误(EACCES/EADDRNOTAVAIL 等)\n // 也能走到统一的 Startup failed 错误处理路径。\n if (!(await isPortAvailable(port, options.host))) {\n console.error(formatPortInUseError(port))\n process.exit(1)\n }\n\n const info = await startHttpServer({\n port,\n host: options.host,\n token: options.token || undefined,\n webRoot,\n requireAuth: options.requireAuth || undefined,\n })\n\n const { startPeriodicUpdateCheck } = await import('./update-checker')\n startPeriodicUpdateCheck()\n\n const displayHost = info.host === '0.0.0.0' ? '127.0.0.1' : info.host\n const url = `http://${displayHost}:${info.port}`\n\n console.log(`\\nChatLab v${getVersion()}`)\n if (webRoot) console.log(` Web UI: ${url}/`)\n console.log(` API: ${url}`)\n console.log(` Token: ${info.token}`)\n console.log(`\\nExample:`)\n console.log(` curl -H \"Authorization: Bearer ${info.token}\" ${url}/api/v1/status`)\n\n if (webRoot && options.open) {\n openBrowser(url)\n console.log(`\\nBrowser opened. Press Ctrl+C to stop.\\n`)\n } else {\n console.log(`\\nPress Ctrl+C to stop.\\n`)\n }\n\n const shutdown = async () => {\n console.log('\\nShutting down...')\n const { stopHttpServer } = await import('./http')\n await stopHttpServer()\n process.exit(0)\n }\n\n process.on('SIGINT', shutdown)\n process.on('SIGTERM', shutdown)\n } catch (err: unknown) {\n const message = err instanceof Error ? err.message : String(err)\n if (message.includes('EADDRINUSE')) {\n // 极低概率的 TOCTOU 竞态(预检通过后端口被占),复用统一文案\n console.error(formatPortInUseError(port))\n } else {\n console.error(`Startup failed: ${message}`)\n }\n process.exit(1)\n }\n })\n\nconst configCmd = program.command('config').description('Configuration management')\n\nconfigCmd\n .command('path')\n .description('Show config file path')\n .action(() => {\n console.log(getConfigPath())\n })\n\nconfigCmd\n .command('show')\n .description('Show current configuration')\n .action(() => {\n const config = loadConfig()\n console.log(JSON.stringify(config, null, 2))\n })\n\nconfigCmd\n .command('set <key> <value>')\n .description('Set a config field, e.g. `chatlab config set cli.allow_raw true`')\n .action((key: string, value: string) => {\n try {\n const result = setConfigField(key, value)\n console.log(`${result.section}.${result.key} = ${JSON.stringify(result.value)}`)\n } catch (err) {\n if (err instanceof ConfigSetError) {\n console.error(`Error: ${err.message}`)\n process.exitCode = 2\n return\n }\n throw err\n }\n })\n\nprogram\n .command('stop')\n .description('Stop the resident service and remove auto-start (reverse of start --daemon)')\n .action(async () => {\n const { serviceUninstall } = await import('./daemon/service')\n serviceUninstall()\n })\n\nprogram\n .command('status')\n .description('Show resident service status')\n .action(async () => {\n const { getServiceStatus } = await import('./daemon/service')\n const svc = getServiceStatus()\n\n console.log('\\nChatLab Status')\n console.log('─'.repeat(36))\n console.log(` Logs: ${getSystemLogsDir()}`)\n\n if (svc.installed) {\n const portStr = svc.port ? `http://${svc.host ?? '127.0.0.1'}:${svc.port}` : ''\n console.log(` Service: ${svc.running ? 'running' : 'installed (not running)'}`)\n if (portStr) console.log(` Address: ${portStr}`)\n console.log(` Auto-start: enabled`)\n console.log(`\\n Use \\`chatlab stop\\` to remove the service.\\n`)\n } else {\n console.log(` Service: not installed`)\n console.log(` Auto-start: disabled`)\n console.log(`\\n Use \\`chatlab start --daemon\\` to install as a system service.\\n`)\n }\n })\n\n// --- 工具函数 ---\n\nfunction openBrowser(url: string): void {\n try {\n const cmd =\n process.platform === 'darwin'\n ? `open \"${url}\"`\n : process.platform === 'win32'\n ? `start \"\" \"${url}\"`\n : `xdg-open \"${url}\"`\n execSync(cmd, { stdio: 'ignore' })\n } catch {\n console.log(` Open manually: ${url}`)\n }\n}\n\n/** CLI entry function */\nexport function run(argv?: string[]): void {\n // Logs go to ~/.chatlab/logs/app.log regardless of configured user data dir.\n initAppLogger(getSystemLogsDir())\n process.on('uncaughtException', (error) => {\n console.error('Uncaught Exception:', error)\n appLogger.error('crash', 'uncaughtException', error)\n process.exit(1)\n })\n process.on('unhandledRejection', (reason) => {\n console.error('Unhandled Rejection:', reason)\n appLogger.error('crash', 'unhandledRejection', reason)\n process.exit(1)\n })\n program.parse(argv)\n}\n\n// Auto-execute when run directly as a script\nconst isDirectRun = process.argv[1]?.endsWith('cli.ts') || process.argv[1]?.endsWith('cli.js')\nif (isDirectRun) {\n run()\n}\n","/**\n * 端口可用性检测与错误文案\n */\n\nimport * as net from 'net'\n\n/**\n * 检测指定端口在给定 host 上是否可用。\n * 使用 net.createServer() 试探性绑定,无副作用(成功后立即释放)。\n */\nexport function isPortAvailable(port: number, host: string): Promise<boolean> {\n return new Promise((resolve, reject) => {\n const server = net.createServer()\n server.once('error', (err: NodeJS.ErrnoException) => {\n if (err.code === 'EADDRINUSE') {\n resolve(false)\n } else {\n reject(err)\n }\n })\n server.once('listening', () => server.close(() => resolve(true)))\n server.listen(port, host)\n })\n}\n\n/**\n * 返回端口被占用时统一的友好报错文案。\n */\nexport function formatPortInUseError(port: number): string {\n return [\n ``,\n ` ✖ Error: port ${port} is already in use.`,\n ``,\n ` Another process (possibly a ChatLab instance) is using this port. You can:`,\n ` • Use another port: chatlab start --port <port>`,\n ` • Find the process: lsof -iTCP:${port} -sTCP:LISTEN`,\n ``,\n ].join('\\n')\n}\n","/**\n * CLI runtime bootstrap: config load, data dir migration/compat checks,\n * DatabaseManager construction. Shared by legacy commands and query commands.\n */\n\nimport * as fs from 'fs'\nimport { loadConfig } from '@openchatlab/config'\nimport {\n NodePathProvider,\n DatabaseManager,\n applyPendingNodeDataDirMigrationIfNeeded,\n hasPendingElectronDataWarning,\n verifyCliDataPath,\n} from '@openchatlab/node-runtime'\nimport { resolveCliPath } from './paths'\nimport { assertCliDataDirCompatible } from './runtime-compat'\n\n/**\n * Resolve standalone better-sqlite3 native module path.\n * Used in non-Electron environments to avoid electron-rebuild conflicts.\n */\nexport function resolveNativeBinding(): string | undefined {\n if (process.versions.electron) return undefined\n const nativePath = resolveCliPath('native/better_sqlite3.node')\n if (fs.existsSync(nativePath)) return nativePath\n return undefined\n}\n\nexport function initRuntime() {\n let config = loadConfig()\n const pendingMigration = applyPendingNodeDataDirMigrationIfNeeded()\n if (!pendingMigration.skipped) {\n if (pendingMigration.success) {\n // stderr: stdout must stay parseable for agent/json consumers (design §6.2)\n console.error('[Migration] Pending data directory migration completed')\n config = loadConfig()\n } else {\n console.error('[Migration] Pending data directory migration failed:', pendingMigration.error)\n }\n }\n const userDataDir = config.data.user_data_dir || undefined\n const pathProvider = new NodePathProvider(userDataDir)\n pathProvider.ensureAllDirs()\n const runtime = assertCliDataDirCompatible(pathProvider, 'cli')\n\n if (hasPendingElectronDataWarning() || !verifyCliDataPath(pathProvider.getDatabaseDir())) {\n printElectronDataError()\n process.exit(1)\n }\n\n const nativeBinding = resolveNativeBinding()\n const dbManager = new DatabaseManager(pathProvider, { nativeBinding, runtime })\n return { config, pathProvider, dbManager }\n}\n\nfunction printElectronDataError(): void {\n console.error('\\n' + '='.repeat(68))\n console.error(' ChatLab: Electron desktop data not found')\n console.error('='.repeat(68))\n console.error('')\n console.error(' Detected that ChatLab desktop app was installed on this machine,')\n console.error(' but could not locate your chat databases.')\n console.error('')\n console.error(' This usually means you changed the data directory in desktop settings.')\n console.error('')\n console.error(' To fix this, choose one of:')\n console.error('')\n console.error(' 1. Open ChatLab desktop app — it will auto-migrate your data')\n console.error(' 2. Set the data directory manually:')\n console.error(' export CHATLAB_DATA_DIR=\"/path/to/your/data\"')\n console.error(' 3. Edit ~/.chatlab/config.toml:')\n console.error(' [data]')\n console.error(' user_data_dir = \"/path/to/your/data\"')\n console.error('')\n console.error('='.repeat(68) + '\\n')\n}\n","/**\n * Unified response envelope for agent-facing query commands.\n *\n * Contract (design: .docs/tasks/2026-07-03-agent-friendly-cli-design.md §6.2):\n * - `ok` and `command` are always present.\n * - Success carries `data` + `meta` (with `apiVersion`); failure carries `error`.\n * - No null placeholders for the missing side.\n * - Errors are written to stdout (single parse path for agents); exit codes are semantic.\n */\n\n/** Bump on breaking envelope changes (additive changes do not bump). */\nexport const API_VERSION = 1\n\nexport interface QueryErrorShape {\n code: string\n message: string\n hint?: string\n candidates?: unknown[]\n}\n\nexport class QueryError extends Error {\n readonly code: string\n readonly hint?: string\n readonly candidates?: unknown[]\n\n constructor(shape: QueryErrorShape) {\n super(shape.message)\n this.name = 'QueryError'\n this.code = shape.code\n this.hint = shape.hint\n this.candidates = shape.candidates\n }\n}\n\nexport interface SuccessEnvelope {\n ok: true\n command: string\n data: unknown\n meta: Record<string, unknown> & { apiVersion: number }\n}\n\nexport interface ErrorEnvelope {\n ok: false\n command: string\n error: QueryErrorShape\n meta?: Record<string, unknown>\n}\n\nexport function successEnvelope(command: string, data: unknown, meta?: Record<string, unknown>): SuccessEnvelope {\n return {\n ok: true,\n command,\n data,\n meta: { ...(meta ?? {}), apiVersion: API_VERSION },\n }\n}\n\nexport function errorEnvelope(\n command: string,\n error: QueryErrorShape | QueryError,\n meta?: Record<string, unknown>\n): ErrorEnvelope {\n const shape: QueryErrorShape = {\n code: error.code,\n message: error.message,\n ...(error.hint !== undefined ? { hint: error.hint } : {}),\n ...(error.candidates !== undefined ? { candidates: error.candidates } : {}),\n }\n return {\n ok: false,\n command,\n error: shape,\n ...(meta !== undefined ? { meta } : {}),\n }\n}\n\n/**\n * Semantic exit codes: 0 success, 2 argument errors (incl. disabled capabilities),\n * 3 resource not found, 4 ambiguity needing disambiguation, 5 SQL errors, 1 anything else.\n */\nexport function exitCodeForError(code: string): number {\n if (code === 'SQL_ERROR') return 5\n if (code.endsWith('_AMBIGUOUS')) return 4\n if (code.endsWith('_NOT_FOUND')) return 3\n if (code === 'INVALID_ARGUMENT' || code === 'CURSOR_INVALID' || code.endsWith('_DISABLED')) return 2\n return 1\n}\n","/**\n * Query command runner: format resolution, envelope emission, error mapping.\n *\n * Hard rule (design §6.2): in agent/json mode stdout carries only the envelope;\n * errors are structured envelopes on stdout with semantic exit codes. Text mode\n * prints human-readable output with errors on stderr.\n */\n\nimport { successEnvelope, errorEnvelope, exitCodeForError, QueryError } from './envelope'\n\nexport type OutputFormat = 'agent' | 'json' | 'text'\n\n/** Explicit --format wins; TTY defaults to text, pipes default to agent (design §6.3). */\nexport function resolveFormat(explicit?: string): OutputFormat {\n if (explicit !== undefined) {\n if (explicit === 'agent' || explicit === 'json' || explicit === 'text') return explicit\n throw new QueryError({\n code: 'INVALID_ARGUMENT',\n message: `Unknown format: ${explicit}`,\n hint: 'Supported formats: agent, json, text',\n })\n }\n return process.stdout.isTTY ? 'text' : 'agent'\n}\n\nexport interface QueryResult {\n data: unknown\n meta?: Record<string, unknown>\n /** Human rendering for --format text; falls back to pretty-printed data. */\n renderText?: () => string\n}\n\n/**\n * Execute a query command handler and emit the response envelope.\n * The handler receives the resolved format so it can shape data accordingly.\n */\nexport async function runQuery(\n command: string,\n options: { format?: string },\n handler: (format: OutputFormat) => Promise<QueryResult>\n): Promise<void> {\n let format: OutputFormat\n try {\n format = resolveFormat(options.format)\n } catch (err) {\n emitError(command, 'agent', err)\n return\n }\n\n try {\n const result = await handler(format)\n if (format === 'text') {\n console.log(result.renderText ? result.renderText() : JSON.stringify(result.data, null, 2))\n } else {\n console.log(JSON.stringify(successEnvelope(command, result.data, result.meta)))\n }\n } catch (err) {\n emitError(command, format, err)\n }\n}\n\nfunction emitError(command: string, format: OutputFormat, err: unknown): void {\n const queryError =\n err instanceof QueryError\n ? err\n : new QueryError({\n code: 'INTERNAL_ERROR',\n message: err instanceof Error ? err.message : String(err),\n })\n\n if (format === 'text') {\n console.error(`Error: ${queryError.message}${queryError.hint ? `\\n ${queryError.hint}` : ''}`)\n if (queryError.candidates) {\n for (const candidate of queryError.candidates) {\n console.error(` - ${JSON.stringify(candidate)}`)\n }\n }\n } else {\n console.log(JSON.stringify(errorEnvelope(command, queryError)))\n }\n process.exitCode = exitCodeForError(queryError.code)\n}\n","/**\n * Query command context: runtime bootstrap + session resolution + privacy config.\n *\n * Privacy defaults (design §6.1): the user's aiPreprocessConfig (with builtin\n * locale-aware desensitize rules merged) applies to every query command. The\n * --raw escape hatch is gated behind the user-side cli.allow_raw switch.\n */\n\nimport * as path from 'node:path'\nimport type { DatabaseAdapter } from '@openchatlab/core'\nimport { getSessionMeta } from '@openchatlab/core'\nimport type { PreprocessConfig } from '@openchatlab/node-runtime'\nimport { initNlpDir } from '@openchatlab/node-runtime'\nimport { initRuntime } from '../runtime'\nimport { loadCliPreprocessConfig, resolveCliLocale } from './preprocess-config'\nimport { resolveSession } from './resolve'\nimport { QueryError } from './envelope'\n\nexport interface QueryContext {\n db: DatabaseAdapter\n session: { id: string; name: string }\n locale: string\n preprocessConfig: PreprocessConfig\n ownerPlatformId?: string\n allowRaw: boolean\n allowSql: boolean\n close: () => void\n}\n\nexport interface RawCapableOptions {\n raw?: boolean\n}\n\n/** Bootstrap the runtime and resolve the target session for a query command. */\nexport function createQueryContext(options: { session?: string }): QueryContext {\n const { config, pathProvider, dbManager } = initRuntime()\n const resolved = resolveSession(dbManager, options.session)\n\n const locale = resolveCliLocale(config.locale.lang)\n const preprocessConfig = loadCliPreprocessConfig(pathProvider.getSystemDir(), locale)\n const ownerPlatformId = getSessionMeta(resolved.db)?.ownerId ?? undefined\n // Same dict location as the web NLP routes, so stats keywords can use downloaded dicts\n initNlpDir(path.join(pathProvider.getSystemDir(), 'nlp'))\n\n return {\n db: resolved.db,\n session: { id: resolved.id, name: resolved.name },\n locale,\n preprocessConfig,\n ownerPlatformId,\n allowRaw: config.cli.allow_raw,\n allowSql: config.cli.allow_sql,\n close: () => dbManager.closeAll(),\n }\n}\n\n/** Enforce the user-side gate for --raw (design §6.1: RAW_DISABLED unless opted in). */\nexport function assertRawAllowed(ctx: Pick<QueryContext, 'allowRaw'>, options: RawCapableOptions): void {\n if (options.raw && !ctx.allowRaw) {\n throw new QueryError({\n code: 'RAW_DISABLED',\n message: '--raw output is disabled by default',\n hint: 'The user must opt in: set CHATLAB_CLI_ALLOW_RAW=1 or `chatlab config set cli.allow_raw true`',\n })\n }\n}\n","/**\n * Assemble the AI preprocess config for CLI query commands.\n *\n * Reads the user's aiPreprocessConfig from ~/.chatlab/preferences.json and merges\n * builtin locale-aware desensitize rules via mergeRulesForLocale — user preferences\n * store custom rules only, so without the merge the desensitize switch would be a no-op.\n */\n\nimport { PreferencesManager, mergeRulesForLocale, type PreprocessConfig } from '@openchatlab/node-runtime'\n\n/** Locales that have dedicated builtin desensitize rule groups. */\nconst BUILTIN_RULE_LOCALES = ['zh-CN', 'en-US', 'ja-JP', 'ko-KR']\n\nconst PRIMARY_LANG_MAP: Record<string, string> = {\n zh: 'zh-CN',\n en: 'en-US',\n ja: 'ja-JP',\n ko: 'ko-KR',\n}\n\nfunction normalizeLocale(locale: string): string {\n if (BUILTIN_RULE_LOCALES.includes(locale)) return locale\n const primary = locale.split(/[-_]/)[0].toLowerCase()\n return PRIMARY_LANG_MAP[primary] ?? locale\n}\n\nfunction detectSystemLocale(): string {\n try {\n const resolved = Intl.DateTimeFormat().resolvedOptions().locale\n if (resolved) return resolved\n } catch {\n // fall through to env\n }\n const lang = process.env.LANG || ''\n return lang.split('.')[0] || ''\n}\n\n/**\n * Resolve the effective locale for desensitize rules:\n * config `locale.lang` first, then system locale (Intl / LANG), then zh-CN.\n */\nexport function resolveCliLocale(configLang?: string, systemLocale?: string): string {\n if (configLang) return normalizeLocale(configLang)\n const system = systemLocale !== undefined ? systemLocale : detectSystemLocale()\n if (system) return normalizeLocale(system)\n return 'zh-CN'\n}\n\n/**\n * Load the user's preprocess config with builtin desensitize rules merged in.\n *\n * @param systemDir directory containing preferences.json (NodePathProvider.getSystemDir())\n * @param locale effective locale from resolveCliLocale()\n */\nexport function loadCliPreprocessConfig(systemDir: string, locale: string): PreprocessConfig {\n const preferences = new PreferencesManager(systemDir).load()\n const config = preferences.aiPreprocessConfig\n return {\n ...config,\n desensitizeRules: mergeRulesForLocale(\n config.desensitizeRules,\n locale,\n config.desensitizeBuiltinRuleOverrides ?? {}\n ),\n }\n}\n","/**\n * Session and member reference resolution (design §4, §5.2).\n *\n * ID-first, name-tolerant: exact id wins, then unique name match. Ambiguity is a\n * structured error (exit code 4) with candidates so agents can retry with the id.\n */\n\nimport type { DatabaseAdapter } from '@openchatlab/core'\nimport { getSessionMeta, getMembersWithAliases, resolveOwnerMember } from '@openchatlab/core'\nimport { QueryError } from './envelope'\n\nexport interface ResolvedSession {\n id: string\n name: string\n db: DatabaseAdapter\n}\n\nexport interface SessionSource {\n listSessionIds(): string[]\n open(id: string): DatabaseAdapter | null | undefined\n}\n\ninterface SessionCandidate {\n id: string\n name: string\n platform: string\n}\n\nfunction listSessionCandidates(source: SessionSource): Array<SessionCandidate & { db: DatabaseAdapter }> {\n const result: Array<SessionCandidate & { db: DatabaseAdapter }> = []\n for (const id of source.listSessionIds()) {\n const db = source.open(id)\n if (!db) continue\n const meta = getSessionMeta(db)\n if (!meta) continue\n result.push({ id, name: meta.name, platform: meta.platform, db })\n }\n return result\n}\n\n/**\n * Resolve --session <ref> (session id or unique name). Without a ref, a single\n * local session is auto-selected; multiple sessions require disambiguation.\n */\nexport function resolveSession(source: SessionSource, ref?: string): ResolvedSession {\n if (ref) {\n const direct = source.open(ref)\n if (direct && getSessionMeta(direct)) {\n const meta = getSessionMeta(direct)!\n return { id: ref, name: meta.name, db: direct }\n }\n }\n\n const sessions = listSessionCandidates(source)\n\n if (!ref) {\n if (sessions.length === 1) {\n return { id: sessions[0].id, name: sessions[0].name, db: sessions[0].db }\n }\n if (sessions.length === 0) {\n throw new QueryError({\n code: 'SESSION_NOT_FOUND',\n message: 'No chat sessions found',\n hint: 'Import a chat export first (chatlab import <file>)',\n })\n }\n throw new QueryError({\n code: 'SESSION_AMBIGUOUS',\n message: `${sessions.length} sessions available, --session is required`,\n hint: 'Retry with --session <id>',\n candidates: sessions.map(({ id, name, platform }) => ({ id, name, platform })),\n })\n }\n\n const nameMatches = sessions.filter((s) => s.name === ref)\n if (nameMatches.length === 1) {\n return { id: nameMatches[0].id, name: nameMatches[0].name, db: nameMatches[0].db }\n }\n if (nameMatches.length > 1) {\n throw new QueryError({\n code: 'SESSION_AMBIGUOUS',\n message: `Session name '${ref}' matches ${nameMatches.length} sessions`,\n hint: 'Retry with --session <id>',\n candidates: nameMatches.map(({ id, name, platform }) => ({ id, name, platform })),\n })\n }\n\n const partial = sessions.filter((s) => s.name.includes(ref))\n if (partial.length > 0) {\n throw new QueryError({\n code: 'SESSION_AMBIGUOUS',\n message: `Session '${ref}' not found, ${partial.length} similar session(s) exist`,\n hint: 'Retry with --session <id>',\n candidates: partial.map(({ id, name, platform }) => ({ id, name, platform })),\n })\n }\n\n throw new QueryError({\n code: 'SESSION_NOT_FOUND',\n message: `Session '${ref}' not found`,\n hint: 'Run `chatlab sessions list` to see available sessions',\n })\n}\n\nexport interface ResolvedMember {\n id: number\n name: string\n}\n\n/**\n * Resolve --member <ref>: numeric id, exact name (current name or alias), or `me`\n * (owner profile). Ambiguous names return candidates for a two-step retry.\n */\nexport function resolveMember(db: DatabaseAdapter, ref: string): ResolvedMember {\n if (ref === 'me') {\n const owner = resolveOwnerMember(db)\n if (!owner) {\n throw new QueryError({\n code: 'MEMBER_NOT_FOUND',\n message: 'Owner profile is not configured for this session',\n hint: 'Set the owner in the ChatLab app, or use an explicit --member <id>',\n })\n }\n return { id: owner.id, name: owner.name }\n }\n\n const members = getMembersWithAliases(db)\n const displayName = (m: (typeof members)[number]) => m.groupNickname || m.accountName || m.platformId\n\n if (/^\\d+$/.test(ref)) {\n const byId = members.find((m) => m.id === Number(ref))\n if (byId) return { id: byId.id, name: displayName(byId) }\n throw new QueryError({\n code: 'MEMBER_NOT_FOUND',\n message: `Member id ${ref} not found`,\n hint: 'Run `chatlab members list` to see available members',\n })\n }\n\n const exact = members.filter(\n (m) => m.groupNickname === ref || m.accountName === ref || m.aliases.includes(ref) || m.platformId === ref\n )\n if (exact.length === 1) {\n return { id: exact[0].id, name: displayName(exact[0]) }\n }\n\n const pool = exact.length > 1 ? exact : members.filter((m) => displayName(m).includes(ref))\n if (pool.length > 0) {\n throw new QueryError({\n code: 'MEMBER_AMBIGUOUS',\n message: `Member name '${ref}' matches ${pool.length} member(s)`,\n hint: 'Retry with --member <id>',\n candidates: pool.slice(0, 10).map((m) => ({ id: m.id, name: displayName(m), messages: m.messageCount })),\n })\n }\n\n throw new QueryError({\n code: 'MEMBER_NOT_FOUND',\n message: `Member '${ref}' not found`,\n hint: 'Run `chatlab members list` to see available members',\n })\n}\n","/**\n * Argument parsing infrastructure for agent-facing query commands.\n *\n * Time values (design §5.1): ISO dates plus the closed keyword set\n * `today` / `yesterday`, and `--last <N>h|d|w`. Date-only values span the\n * whole day (`--until 2026-06-30` includes the full day). Resolved absolute\n * times are echoed in meta.timeRange so agents can self-verify boundaries.\n */\n\nimport { createHash } from 'node:crypto'\nimport { QueryError } from './envelope'\n\nexport interface TimeCliOptions {\n since?: string\n until?: string\n last?: string\n}\n\nexport interface ResolvedTimeRange {\n startTs?: number\n endTs?: number\n /** Echo of resolved absolute boundaries for meta.timeRange. */\n meta: { since: string | null; until: string | null }\n}\n\nexport interface CursorSnapshot {\n time?: ResolvedTimeRange\n}\n\n/** Format a Date as ISO 8601 with the local UTC offset (sortable, self-describing). */\nexport function toIsoWithOffset(date: Date): string {\n const pad = (n: number, w = 2) => String(Math.abs(n)).padStart(w, '0')\n const offsetMin = -date.getTimezoneOffset()\n const sign = offsetMin >= 0 ? '+' : '-'\n const offH = pad(Math.floor(Math.abs(offsetMin) / 60))\n const offM = pad(Math.abs(offsetMin) % 60)\n return (\n `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}` +\n `T${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}${sign}${offH}:${offM}`\n )\n}\n\n/** Convert epoch seconds to ISO string with local offset. */\nexport function epochToIso(ts: number): string {\n return toIsoWithOffset(new Date(ts * 1000))\n}\n\nconst DATE_ONLY = /^(\\d{4})-(\\d{2})-(\\d{2})$/\nconst DATE_TIME = /^(\\d{4})-(\\d{2})-(\\d{2})[T ](\\d{2}):(\\d{2})(?::(\\d{2}))?$/\nconst ISO_DATE_PREFIX = /^(\\d{4})-(\\d{2})-(\\d{2})T/\nconst LAST_DURATION = /^(\\d+)([hdw])$/\n\nfunction startOfDay(base: Date, dayOffset = 0): Date {\n return new Date(base.getFullYear(), base.getMonth(), base.getDate() + dayOffset)\n}\n\nfunction invalidTime(flag: string, value: string): QueryError {\n return new QueryError({\n code: 'INVALID_ARGUMENT',\n message: `Invalid ${flag} value: ${value}`,\n hint: 'Use YYYY-MM-DD, \"YYYY-MM-DD HH:mm\", ISO 8601, or the keywords today/yesterday',\n })\n}\n\nfunction isSameLocalDate(date: Date, y: number, m: number, d: number): boolean {\n return date.getFullYear() === y && date.getMonth() === m - 1 && date.getDate() === d\n}\n\nfunction isSameLocalDateTime(date: Date, y: number, m: number, d: number, h: number, min: number, s: number): boolean {\n return isSameLocalDate(date, y, m, d) && date.getHours() === h && date.getMinutes() === min && date.getSeconds() === s\n}\n\n/**\n * Parse a single time value. Date-only values resolve to the start of the day;\n * when `boundary` is 'end' they resolve to the end of the day (inclusive whole-day).\n */\nfunction parseTimeValue(value: string, boundary: 'start' | 'end', flag: string, now: Date): Date {\n if (value === 'today' || value === 'yesterday') {\n const dayOffset = value === 'yesterday' ? -1 : 0\n if (boundary === 'start') return startOfDay(now, dayOffset)\n return new Date(startOfDay(now, dayOffset + 1).getTime() - 1000)\n }\n\n const dateOnly = DATE_ONLY.exec(value)\n if (dateOnly) {\n const [, y, m, d] = dateOnly\n const year = Number(y)\n const month = Number(m)\n const day = Number(d)\n const base = new Date(year, month - 1, day)\n if (Number.isNaN(base.getTime()) || !isSameLocalDate(base, year, month, day)) throw invalidTime(flag, value)\n if (boundary === 'start') return base\n return new Date(new Date(year, month - 1, day + 1).getTime() - 1000)\n }\n\n const dateTime = DATE_TIME.exec(value)\n if (dateTime) {\n const [, y, m, d, h, min, s] = dateTime\n const year = Number(y)\n const month = Number(m)\n const day = Number(d)\n const hour = Number(h)\n const minute = Number(min)\n const second = Number(s ?? 0)\n const parsed = new Date(year, month - 1, day, hour, minute, second)\n if (Number.isNaN(parsed.getTime()) || !isSameLocalDateTime(parsed, year, month, day, hour, minute, second)) {\n throw invalidTime(flag, value)\n }\n return parsed\n }\n\n // full ISO 8601 (with timezone offset or Z)\n const parsed = new Date(value)\n if (!Number.isNaN(parsed.getTime()) && /\\d{4}-\\d{2}-\\d{2}T/.test(value)) {\n const isoDate = ISO_DATE_PREFIX.exec(value)\n if (isoDate) {\n const [, y, m, d] = isoDate\n const year = Number(y)\n const month = Number(m)\n const day = Number(d)\n const localDate = new Date(year, month - 1, day)\n if (!isSameLocalDate(localDate, year, month, day)) throw invalidTime(flag, value)\n }\n return parsed\n }\n\n throw invalidTime(flag, value)\n}\n\n/** Resolve --since/--until/--last into epoch-second bounds plus a meta echo. */\nexport function parseTimeOptions(options: TimeCliOptions, now = new Date()): ResolvedTimeRange {\n const { since, until, last } = options\n\n if (last !== undefined && (since !== undefined || until !== undefined)) {\n throw new QueryError({\n code: 'INVALID_ARGUMENT',\n message: '--last cannot be combined with --since/--until',\n hint: 'Use either --last <N>h|d|w or explicit --since/--until bounds',\n })\n }\n\n if (last !== undefined) {\n const match = LAST_DURATION.exec(last)\n if (!match) {\n throw new QueryError({\n code: 'INVALID_ARGUMENT',\n message: `Invalid --last value: ${last}`,\n hint: 'Supported units: h (hours), d (days), w (weeks), e.g. --last 30d',\n })\n }\n const n = Number(match[1])\n const unitSeconds = match[2] === 'h' ? 3600 : match[2] === 'd' ? 86400 : 604800\n const start = new Date(now.getTime() - n * unitSeconds * 1000)\n return {\n startTs: Math.floor(start.getTime() / 1000),\n meta: { since: toIsoWithOffset(start), until: toIsoWithOffset(now) },\n }\n }\n\n let startTs: number | undefined\n let endTs: number | undefined\n let sinceIso: string | null = null\n\n if (since !== undefined) {\n const parsed = parseTimeValue(since, 'start', '--since', now)\n startTs = Math.floor(parsed.getTime() / 1000)\n sinceIso = toIsoWithOffset(parsed)\n }\n if (until !== undefined) {\n const parsed = parseTimeValue(until, 'end', '--until', now)\n endTs = Math.floor(parsed.getTime() / 1000)\n }\n\n if (startTs !== undefined && endTs !== undefined && startTs > endTs) {\n throw new QueryError({\n code: 'INVALID_ARGUMENT',\n message: '--since is after --until',\n hint: 'Check the time range boundaries',\n })\n }\n\n return {\n startTs,\n endTs,\n meta: {\n since: sinceIso,\n until: endTs !== undefined ? epochToIso(endTs) : toIsoWithOffset(now),\n },\n }\n}\n\n/** Parse a positive integer flag, clamped to a hard cap. */\nexport function parseLimit(\n value: string | number | undefined,\n defaultValue: number,\n cap: number,\n flag: string,\n min = 0\n): number {\n if (value === undefined) return Math.min(defaultValue, cap)\n const n = typeof value === 'number' ? value : Number(value)\n if (!Number.isInteger(n) || n < min) {\n throw new QueryError({\n code: 'INVALID_ARGUMENT',\n message: `Invalid ${flag} value: ${value}`,\n hint:\n min > 0\n ? `${flag} must be an integer from ${min} to ${cap}`\n : `${flag} must be a non-negative integer (max ${cap})`,\n })\n }\n return Math.min(n, cap)\n}\n\n// ==================== Cursor ====================\n\n/** Stable fingerprint of query conditions; cursors from a different query are rejected. */\nexport function queryFingerprint(conditions: Record<string, unknown>): string {\n const canonical = JSON.stringify(conditions, Object.keys(conditions).sort())\n return createHash('sha256').update(canonical).digest('hex').slice(0, 12)\n}\n\nexport function freezeTimeRangeForCursor(time: ResolvedTimeRange): ResolvedTimeRange {\n if (time.endTs !== undefined || time.meta.until === null) return time\n const endMs = new Date(time.meta.until).getTime()\n if (Number.isNaN(endMs)) return time\n return { ...time, endTs: Math.floor(endMs / 1000) }\n}\n\nexport function resolveTimeOptionsForCursor(\n options: TimeCliOptions & { cursor?: string },\n now = new Date()\n): ResolvedTimeRange {\n const snapshot = options.cursor ? getCursorSnapshot(options.cursor) : undefined\n return snapshot?.time ?? freezeTimeRangeForCursor(parseTimeOptions(options, now))\n}\n\nexport function encodeCursor(offset: number, fingerprint: string, snapshot?: CursorSnapshot): string {\n if (snapshot?.time) {\n return Buffer.from(JSON.stringify({ v: 2, fp: fingerprint, offset, time: snapshot.time }), 'utf8').toString(\n 'base64url'\n )\n }\n return Buffer.from(`${fingerprint}:${offset}`, 'utf8').toString('base64url')\n}\n\nfunction asObject(value: unknown): Record<string, unknown> | null {\n return value !== null && typeof value === 'object' && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : null\n}\n\nfunction decodeCursorEnvelope(\n token: string\n): { fingerprint: string; offset: number; snapshot?: CursorSnapshot } | null {\n let decoded = ''\n try {\n decoded = Buffer.from(token, 'base64url').toString('utf8')\n } catch {\n return null\n }\n\n let payload: unknown\n try {\n payload = JSON.parse(decoded)\n } catch {\n return null\n }\n\n const object = asObject(payload)\n if (\n !object ||\n object.v !== 2 ||\n typeof object.fp !== 'string' ||\n typeof object.offset !== 'number' ||\n !Number.isInteger(object.offset) ||\n object.offset < 0\n ) {\n return null\n }\n\n const snapshot = decodeCursorSnapshot(object)\n return { fingerprint: object.fp, offset: object.offset, ...(snapshot ? { snapshot } : {}) }\n}\n\nfunction decodeCursorSnapshot(payload: Record<string, unknown>): CursorSnapshot | undefined {\n const timeObject = asObject(payload.time)\n if (!timeObject) return undefined\n const metaObject = asObject(timeObject.meta)\n if (!metaObject) return undefined\n\n const startTs = timeObject.startTs\n const endTs = timeObject.endTs\n const since = metaObject.since\n const until = metaObject.until\n if (startTs !== undefined && typeof startTs !== 'number') return undefined\n if (endTs !== undefined && typeof endTs !== 'number') return undefined\n if (since !== null && typeof since !== 'string') return undefined\n if (until !== null && typeof until !== 'string') return undefined\n\n return {\n time: {\n ...(startTs !== undefined ? { startTs } : {}),\n ...(endTs !== undefined ? { endTs } : {}),\n meta: { since, until },\n },\n }\n}\n\nexport function getCursorSnapshot(token: string): CursorSnapshot | undefined {\n return decodeCursorEnvelope(token)?.snapshot\n}\n\n/** Decode a cursor and validate it against the current query fingerprint. */\nexport function decodeCursor(token: string, fingerprint: string): number {\n const envelope = decodeCursorEnvelope(token)\n if (envelope) {\n if (envelope.fingerprint !== fingerprint) {\n throw new QueryError({\n code: 'CURSOR_INVALID',\n message: 'Cursor does not match the current query conditions',\n hint: 'Cursors are only valid for the exact query that produced them; re-run the query without --cursor',\n })\n }\n return envelope.offset\n }\n\n let decoded = ''\n try {\n decoded = Buffer.from(token, 'base64url').toString('utf8')\n } catch {\n // fall through to the shared error below\n }\n const match = /^([0-9a-f]{12}):(\\d+)$/.exec(decoded)\n if (!match || match[1] !== fingerprint) {\n throw new QueryError({\n code: 'CURSOR_INVALID',\n message: 'Cursor does not match the current query conditions',\n hint: 'Cursors are only valid for the exact query that produced them; re-run the query without --cursor',\n })\n }\n return Number(match[2])\n}\n","/**\n * sessions / members query command groups.\n *\n * Parent actions keep the legacy top-level behavior (`chatlab sessions`,\n * `chatlab members <session-id>`) as deprecated aliases for one major version.\n */\n\nimport type { Command } from 'commander'\nimport {\n getSessionMeta,\n getSessionOverview,\n getMembersWithAliases,\n getMemberNameHistory,\n getMembers,\n} from '@openchatlab/core'\nimport { initRuntime } from '../runtime'\nimport { runQuery } from './runner'\nimport { createQueryContext } from './context'\nimport { resolveMember } from './resolve'\nimport { epochToIso, parseLimit } from './parse'\n\nfunction epochOrNull(ts: number | null): string | null {\n return ts == null ? null : epochToIso(ts)\n}\n\nexport function registerSessionCommands(program: Command): void {\n const sessionsCmd = program\n .command('sessions')\n .description('Chat sessions (list, show)')\n .option('--format <format>', 'Output format (table|json for legacy list; agent|json|text for subcommands)')\n .action((options: { format?: string }) => {\n // legacy alias: `chatlab sessions [--format table|json]`\n console.error('[Deprecated] `chatlab sessions` — use `chatlab sessions list`; alias kept for one major version')\n legacySessionsList(options)\n })\n\n sessionsCmd\n .command('list')\n .description('List imported chat sessions')\n .option('--query <kw>', 'Filter by session name substring')\n .option('--platform <p>', 'Filter by platform id')\n .option('--format <format>', 'Output format: agent|json|text')\n .action(async (options: { query?: string; platform?: string; format?: string }) => {\n await runQuery('sessions.list', options, async () => {\n const { dbManager } = initRuntime()\n try {\n const items = dbManager\n .listSessionIds()\n .map((id) => {\n const db = dbManager.open(id)\n if (!db) return null\n const meta = getSessionMeta(db)\n if (!meta) return null\n const overview = getSessionOverview(db)\n return {\n id,\n name: meta.name,\n platform: meta.platform,\n type: meta.type,\n totalMessages: overview.totalMessages,\n totalMembers: overview.totalMembers,\n firstMessage: epochOrNull(overview.firstMessageTs),\n lastMessage: epochOrNull(overview.lastMessageTs),\n }\n })\n .filter((s): s is NonNullable<typeof s> => s !== null)\n .filter((s) => (options.query ? s.name.includes(options.query) : true))\n .filter((s) => (options.platform ? s.platform === options.platform : true))\n\n return {\n data: { items },\n meta: { returned: items.length },\n renderText: () =>\n items\n .map(\n (s) =>\n `${s.id} ${s.name}\\n ${s.platform} | ${s.type} | ${s.totalMembers} members | ${s.totalMessages} messages`\n )\n .join('\\n') || 'No chat sessions found.',\n }\n } finally {\n dbManager.closeAll()\n }\n })\n })\n\n sessionsCmd\n .command('show')\n .description('Show one session in detail')\n .option('--session <ref>', 'Session id or unique name')\n .option('--format <format>', 'Output format: agent|json|text')\n .action(async (options: { session?: string; format?: string }) => {\n await runQuery('sessions.show', options, async () => {\n const ctx = createQueryContext(options)\n try {\n const meta = getSessionMeta(ctx.db)!\n const overview = getSessionOverview(ctx.db)\n const data = {\n id: ctx.session.id,\n name: meta.name,\n platform: meta.platform,\n type: meta.type,\n totalMessages: overview.totalMessages,\n totalMembers: overview.totalMembers,\n firstMessage: epochOrNull(overview.firstMessageTs),\n lastMessage: epochOrNull(overview.lastMessageTs),\n }\n return {\n data,\n meta: { session: ctx.session },\n renderText: () =>\n `${data.name} (${data.id})\\n` +\n `Platform: ${data.platform} | Type: ${data.type}\\n` +\n `Messages: ${data.totalMessages} | Members: ${data.totalMembers}\\n` +\n `Range: ${data.firstMessage ?? '-'} ~ ${data.lastMessage ?? '-'}`,\n }\n } finally {\n ctx.close()\n }\n })\n })\n}\n\nexport function registerMemberCommands(program: Command): void {\n const membersCmd = program\n .command('members')\n .description('Session members (list, history)')\n .argument('[session-id]', 'legacy: session id (deprecated alias)')\n .option('--format <format>', 'Output format')\n .action((sessionId: string | undefined, options: { format?: string }, command: Command) => {\n if (!sessionId) {\n command.help({ error: false })\n return\n }\n console.error(\n '[Deprecated] `chatlab members <session-id>` — use `chatlab members list --session <id>`; alias kept for one major version'\n )\n legacyMembersList(sessionId, options)\n })\n\n membersCmd\n .command('list')\n .description('List session members')\n .option('--session <ref>', 'Session id or unique name')\n .option('--query <kw>', 'Filter by member name substring')\n .option('--sort <by>', 'Sort by: messages|name', 'messages')\n .option('--limit <n>', 'Max members to return (default 50)')\n .option('--format <format>', 'Output format: agent|json|text')\n .action(async (options: { session?: string; query?: string; sort?: string; limit?: string; format?: string }) => {\n await runQuery('members.list', options, async () => {\n const ctx = createQueryContext(options)\n try {\n const limit = parseLimit(options.limit, 50, 500, '--limit')\n let members = getMembersWithAliases(ctx.db).map((m) => ({\n id: m.id,\n name: m.groupNickname || m.accountName || m.platformId,\n ...(m.aliases.length > 0 ? { aliases: m.aliases } : {}),\n messageCount: m.messageCount,\n }))\n if (options.query) {\n const kw = options.query\n members = members.filter((m) => m.name.includes(kw) || m.aliases?.some((a) => a.includes(kw)))\n }\n if (options.sort === 'name') {\n members.sort((a, b) => a.name.localeCompare(b.name))\n }\n const total = members.length\n const items = members.slice(0, limit)\n return {\n data: { items },\n meta: { session: ctx.session, total, returned: items.length },\n renderText: () =>\n items.map((m, i) => `${i + 1}. ${m.name} (#${m.id}) - ${m.messageCount} messages`).join('\\n') ||\n 'No members.',\n }\n } finally {\n ctx.close()\n }\n })\n })\n\n membersCmd\n .command('history')\n .description('Show name change history for a member')\n .requiredOption('--member <ref>', 'Member id, exact name, or `me`')\n .option('--session <ref>', 'Session id or unique name')\n .option('--format <format>', 'Output format: agent|json|text')\n .action(async (options: { member: string; session?: string; format?: string }) => {\n await runQuery('members.history', options, async () => {\n const ctx = createQueryContext(options)\n try {\n const member = resolveMember(ctx.db, options.member)\n const items = getMemberNameHistory(ctx.db, member.id).map((h) => ({\n nameType: h.nameType,\n name: h.name,\n since: epochToIso(h.startTs),\n until: h.endTs == null ? null : epochToIso(h.endTs),\n }))\n return {\n data: { items },\n meta: { session: ctx.session, member, returned: items.length },\n renderText: () =>\n items.map((h) => `${h.since} ~ ${h.until ?? 'present'} [${h.nameType}] ${h.name}`).join('\\n') ||\n 'No name changes recorded.',\n }\n } finally {\n ctx.close()\n }\n })\n })\n}\n\n// ==================== Legacy aliases (output kept byte-compatible) ====================\n\nfunction legacySessionsList(options: { format?: string }): void {\n const { dbManager } = initRuntime()\n const sessionIds = dbManager.listSessionIds()\n\n if (sessionIds.length === 0) {\n console.log('No chat sessions found.')\n console.log(`Data directory: ${dbManager['pathProvider'].getUserDataDir()}`)\n dbManager.closeAll()\n return\n }\n\n const sessions = sessionIds\n .map((id) => {\n const db = dbManager.open(id)\n if (!db) return null\n const meta = getSessionMeta(db)\n if (!meta) return null\n const overview = getSessionOverview(db)\n return { id, ...meta, ...overview }\n })\n .filter(Boolean)\n\n if (options.format === 'json') {\n console.log(JSON.stringify(sessions, null, 2))\n } else {\n console.log(`${sessions.length} session(s) found:\\n`)\n for (const s of sessions) {\n if (!s) continue\n const range =\n s.firstMessageTs && s.lastMessageTs\n ? `${new Date(s.firstMessageTs * 1000).toLocaleDateString()} ~ ${new Date(s.lastMessageTs * 1000).toLocaleDateString()}`\n : ''\n console.log(` ${s.name}`)\n console.log(` ID: ${s.id}`)\n console.log(\n ` Platform: ${s.platform} | Type: ${s.type} | Members: ${s.totalMembers} | Messages: ${s.totalMessages}`\n )\n if (range) console.log(` Range: ${range}`)\n console.log()\n }\n }\n\n dbManager.closeAll()\n}\n\nfunction legacyMembersList(sessionId: string, options: { format?: string }): void {\n const { dbManager } = initRuntime()\n const db = dbManager.open(sessionId)\n if (!db) {\n console.error(`Session ${sessionId} not found`)\n process.exit(1)\n }\n\n const members = getMembers(db)\n\n if (options.format === 'json') {\n console.log(JSON.stringify(members, null, 2))\n } else {\n console.log(`${members.length} member(s):\\n`)\n for (const [i, m] of members.entries()) {\n console.log(` ${i + 1}. ${m.name} (${m.platformId}) - ${m.messageCount} messages`)\n }\n }\n\n dbManager.closeAll()\n}\n","/**\n * Shared output shaping for message-carrying query commands.\n *\n * agent format: AI-facing text from the shared preprocessing pipeline with\n * [#id] / [#id*] message markers and display-only [#a-b] merged ranges.\n * json format: structured items with privacy\n * steps applied (cleaning/blacklist/desensitize) but no merging/denoising.\n */\n\nimport { messageTypeToString } from '@openchatlab/core'\nimport {\n applyPreprocessingPipeline,\n preprocessMessages,\n type PreprocessConfig,\n type PreprocessableMessage,\n type PipelineStats,\n type TruncationStrategy,\n} from '@openchatlab/node-runtime'\nimport { epochToIso, parseLimit } from './parse'\nimport { QueryError } from './envelope'\nimport type { QueryContext } from './context'\n\nexport interface MessageLike {\n id: number\n senderId?: number\n senderName: string\n senderPlatformId?: string\n content: string | null\n timestamp: number\n type?: number\n}\n\nexport const MAX_TOKENS_DEFAULT = 4000\nexport const MAX_TOKENS_CAP = 32000\n\n/** Privacy-only variant of the user's preprocess config: no merging, no denoising. */\nexport function privacyOnlyConfig(config: PreprocessConfig): PreprocessConfig {\n return { ...config, mergeConsecutive: false, denoise: false, anonymizeNames: false }\n}\n\nexport interface TokenCliOptions {\n maxTokens?: string\n maxChars?: string\n full?: boolean\n verbose?: boolean\n raw?: boolean\n}\n\nexport interface AgentTextResult {\n text: string\n stats: PipelineStats\n preprocess: Record<string, unknown>\n preprocessDetail?: Record<string, unknown>\n}\n\n/** Reject --raw for agent format: raw output only pairs with json/text (design §6.1). */\nexport function assertRawFormatCompatible(format: string, options: { raw?: boolean }): void {\n if (options.raw && format === 'agent') {\n throw new QueryError({\n code: 'INVALID_ARGUMENT',\n message: '--raw cannot be combined with --format agent',\n hint: 'Raw output is a debugging path; use --format json or --format text with --raw',\n })\n }\n}\n\n/**\n * Build the agent-format text via the shared preprocessing pipeline.\n * Messages must be in chronological order (merge assumes consecutive ordering).\n */\nexport function buildAgentText(\n messages: MessageLike[],\n ctx: Pick<QueryContext, 'preprocessConfig' | 'locale' | 'ownerPlatformId'>,\n options: TokenCliOptions & {\n hitIds?: Iterable<number>\n strategy: TruncationStrategy\n /** Per-message content char default for this command (search uses 120). */\n defaultMaxChars?: number\n }\n): AgentTextResult {\n const maxTokens = parseLimit(options.maxTokens, MAX_TOKENS_DEFAULT, MAX_TOKENS_CAP, '--max-tokens')\n const maxChars = options.full\n ? 0\n : options.maxChars !== undefined\n ? parseLimit(options.maxChars, 200, 10000, '--max-chars')\n : options.defaultMaxChars\n\n const result = applyPreprocessingPipeline({\n rawMessages: messages as PreprocessableMessage[],\n preprocessConfig: ctx.preprocessConfig,\n locale: ctx.locale,\n anonymizeNames: ctx.preprocessConfig.anonymizeNames ?? false,\n ownerPlatformId: ctx.ownerPlatformId,\n maxToolResultTokens: maxTokens,\n truncationStrategy: options.strategy,\n includeMessageIds: true,\n hitIds: options.hitIds,\n maxContentChars: maxChars,\n })\n\n const config = ctx.preprocessConfig\n const preprocess = {\n cleaned: config.dataCleaning !== false,\n denoised: !!config.denoise,\n merged: !!config.mergeConsecutive,\n desensitized: !!config.desensitize && config.desensitizeRules.some((r) => r.enabled),\n truncated: result.stats.truncated,\n }\n\n const preprocessDetail = options.verbose\n ? {\n inputMessages: result.stats.input,\n renderedBlocks: result.stats.renderedBlocks,\n cleaned: result.stats.cleaned,\n blacklistRemoved: result.stats.blacklistRemoved,\n denoiseRemoved: result.stats.denoiseRemoved,\n mergeCombined: result.stats.mergeCombined,\n desensitizeRulesApplied: result.stats.desensitizeRulesApplied,\n }\n : undefined\n\n return { text: result.text, stats: result.stats, preprocess, preprocessDetail }\n}\n\nexport interface JsonItemsOptions {\n /** Per-message content char limit (json default 300, search 120); --full disables. */\n maxChars?: string\n full?: boolean\n fields?: string\n /** commander --no-content negated flag: options.content === false means omit content. */\n content?: boolean\n raw?: boolean\n hitIds?: Set<number>\n defaultMaxChars?: number\n}\n\nconst JSON_ITEM_FIELDS = new Set(['id', 'time', 'senderId', 'senderName', 'type', 'content', 'hit'])\n\n/**\n * Shape messages into json-format items. Privacy steps (cleaning/blacklist/\n * desensitize) apply unless `raw` (which must already be gate-checked).\n */\nexport function toJsonItems(\n messages: MessageLike[],\n preprocessConfig: PreprocessConfig,\n options: JsonItemsOptions\n): Record<string, unknown>[] {\n const visible = options.raw\n ? messages\n : (preprocessMessages(messages as PreprocessableMessage[], privacyOnlyConfig(preprocessConfig)) as MessageLike[])\n\n const noContent = options.content === false\n const maxChars = options.full\n ? 0\n : options.maxChars !== undefined\n ? parseLimit(options.maxChars, 300, 10000, '--max-chars')\n : (options.defaultMaxChars ?? 300)\n\n let fields: Set<string> | undefined\n if (options.fields) {\n fields = new Set(options.fields.split(',').map((f) => f.trim()))\n for (const field of fields) {\n if (!JSON_ITEM_FIELDS.has(field)) {\n throw new QueryError({\n code: 'INVALID_ARGUMENT',\n message: `Unknown field: ${field}`,\n hint: `Supported fields: ${[...JSON_ITEM_FIELDS].join(', ')}`,\n })\n }\n }\n }\n\n return visible.map((msg) => {\n let content: string | null | undefined = msg.content\n if (noContent) {\n content = undefined\n } else if (content && maxChars > 0 && content.length > maxChars) {\n content = content.slice(0, maxChars) + '...'\n }\n\n const item: Record<string, unknown> = {\n id: msg.id,\n time: epochToIso(msg.timestamp),\n ...(msg.senderId !== undefined ? { senderId: msg.senderId } : {}),\n senderName: msg.senderName,\n ...(msg.type !== undefined ? { type: messageTypeToString(msg.type) } : {}),\n ...(content !== undefined ? { content } : {}),\n ...(options.hitIds?.has(msg.id) ? { hit: true } : {}),\n }\n\n if (fields) {\n for (const key of Object.keys(item)) {\n if (!fields.has(key)) delete item[key]\n }\n }\n return item\n })\n}\n\n/** Chronological ordering for pipeline input (merge assumes consecutive order). */\nexport function sortChronological<T extends { timestamp: number; id: number }>(messages: T[]): T[] {\n return [...messages].sort((a, b) => a.timestamp - b.timestamp || a.id - b.id)\n}\n\nexport interface MessageResultOptions extends TokenCliOptions {\n fields?: string\n content?: boolean\n}\n\nexport interface MessageResultShape {\n hitIds?: Set<number>\n strategy: TruncationStrategy\n /** Per-message content char default for this command (search uses 120). */\n defaultMaxChars?: number\n}\n\nexport interface ShapedMessagesResult {\n data: unknown\n meta?: Record<string, unknown>\n renderText?: () => string\n}\n\n/**\n * Shared response assembly for message-carrying commands: agent text via the\n * pipeline, json items with privacy steps, raw text lines when gated --raw.\n */\nexport function buildMessagesResult(\n format: 'agent' | 'json' | 'text',\n ctx: Pick<QueryContext, 'preprocessConfig' | 'locale' | 'ownerPlatformId'>,\n options: MessageResultOptions,\n messages: MessageLike[],\n meta: Record<string, unknown>,\n shape: MessageResultShape\n): ShapedMessagesResult {\n const chronological = sortChronological(messages)\n\n if (format === 'json') {\n const items = toJsonItems(chronological, ctx.preprocessConfig, {\n maxChars: options.maxChars,\n full: options.full,\n fields: options.fields,\n content: options.content,\n raw: options.raw,\n hitIds: shape.hitIds,\n defaultMaxChars: shape.defaultMaxChars,\n })\n return {\n data: { items },\n meta: {\n ...meta,\n returned: items.length,\n preprocess: options.raw\n ? { raw: true }\n : {\n cleaned: ctx.preprocessConfig.dataCleaning !== false,\n desensitized:\n !!ctx.preprocessConfig.desensitize && ctx.preprocessConfig.desensitizeRules.some((r) => r.enabled),\n },\n },\n }\n }\n\n if (options.raw) {\n // text format + raw: plain lines without preprocessing\n return {\n data: { items: chronological },\n renderText: () =>\n chronological\n .map((m) => `[#${m.id}] ${new Date(m.timestamp * 1000).toLocaleString()} ${m.senderName}: ${m.content ?? ''}`)\n .join('\\n') || 'No messages.',\n }\n }\n\n const agent = buildAgentText(chronological, ctx, {\n hitIds: shape.hitIds,\n strategy: shape.strategy,\n maxTokens: options.maxTokens,\n maxChars: options.maxChars,\n full: options.full,\n verbose: options.verbose,\n defaultMaxChars: shape.defaultMaxChars,\n })\n\n return {\n data: { text: agent.text },\n meta: {\n ...meta,\n preprocess: agent.preprocess,\n ...(agent.preprocessDetail ? { preprocessDetail: agent.preprocessDetail } : {}),\n },\n renderText: () => agent.text,\n }\n}\n","/**\n * messages query command group: list / search / context / between.\n *\n * Counting and pagination operate on the privacy-filtered visible hit sequence:\n * user blacklist keywords are pushed down to SQL (design §5.4), so totalHits,\n * cursors and pages never expose blacklisted rows.\n */\n\nimport type { Command } from 'commander'\nimport {\n searchMessagesByKeywords,\n getMessageContext,\n getSearchMessageContext,\n getConversationBetween,\n} from '@openchatlab/core'\nimport { runQuery } from './runner'\nimport { createQueryContext, assertRawAllowed } from './context'\nimport { resolveMember } from './resolve'\nimport { parseLimit, queryFingerprint, encodeCursor, decodeCursor, resolveTimeOptionsForCursor } from './parse'\nimport { QueryError } from './envelope'\nimport { buildMessagesResult, assertRawFormatCompatible, type MessageLike } from './messages-output'\n\ninterface CommonMessageOptions {\n session?: string\n since?: string\n until?: string\n last?: string\n limit?: string\n cursor?: string\n format?: string\n maxTokens?: string\n maxChars?: string\n full?: boolean\n fields?: string\n content?: boolean\n raw?: boolean\n verbose?: boolean\n}\n\nexport function capExpandedSearchMessages(\n messages: MessageLike[],\n hitIds: ReadonlySet<number>,\n maxMessages: number\n): MessageLike[] {\n if (maxMessages <= 0) return []\n if (messages.length <= maxMessages) return messages\n\n const capped = messages.slice(0, maxMessages)\n const includedIds = new Set(capped.map((message) => message.id))\n const missingHits = messages.filter((message) => hitIds.has(message.id) && !includedIds.has(message.id))\n\n for (const hit of missingHits) {\n let replaceIndex = -1\n for (let i = capped.length - 1; i >= 0; i--) {\n if (!hitIds.has(capped[i].id)) {\n replaceIndex = i\n break\n }\n }\n if (replaceIndex === -1) break\n capped[replaceIndex] = hit\n }\n\n return capped.sort((a, b) => messages.indexOf(a) - messages.indexOf(b))\n}\n\nexport function appendUniqueTailMessages(expanded: MessageLike[], tail: MessageLike[]): MessageLike[] {\n const expandedIds = new Set(expanded.map((message) => message.id))\n return [...expanded, ...tail.filter((message) => !expandedIds.has(message.id))]\n}\n\nexport function parseContextIds(value: string): number[] {\n const tokens = value.split(',').map((token) => token.trim())\n const ids = tokens.map((token) => (token.length > 0 ? Number(token) : Number.NaN))\n if (ids.length === 0 || ids.some((id) => !Number.isInteger(id) || id <= 0)) {\n throw new QueryError({\n code: 'INVALID_ARGUMENT',\n message: `Invalid --id value: ${value}`,\n hint: 'Pass positive numeric message ids, e.g. --id 1021 or --id 1021,1058',\n })\n }\n return ids\n}\n\nexport function parseSearchKeywords(values: string[]): string[] {\n const keywords = values.map((value) => value.trim()).filter(Boolean)\n if (keywords.length === 0) {\n throw new QueryError({\n code: 'INVALID_ARGUMENT',\n message: 'Invalid search keywords',\n hint: 'Pass at least one non-empty search keyword',\n })\n }\n return keywords\n}\n\nexport function searchTruncationStrategy(sort: string | undefined): 'keep_first' | 'keep_last' {\n return sort === 'desc' ? 'keep_last' : 'keep_first'\n}\n\nexport function assertContextAnchorsPresent(ids: number[], messages: MessageLike[], rawValue: string): void {\n const returnedIds = new Set(messages.map((message) => message.id))\n if (ids.some((id) => !returnedIds.has(id))) {\n throw new QueryError({\n code: 'MESSAGE_NOT_FOUND',\n message: `No messages found for id(s) ${rawValue}`,\n hint: 'Use a single numeric id from [#id]/[#id*] markers; merged [#a-b] ranges are display-only',\n })\n }\n}\n\nfunction addSharedOptions(cmd: Command, options: { includeTime?: boolean } = {}): Command {\n const shared = cmd.option('--session <ref>', 'Session id or unique name (auto-selected when only one exists)')\n if (options.includeTime !== false) {\n shared\n .option('--since <t>', 'Start time: YYYY-MM-DD, \"YYYY-MM-DD HH:mm\", ISO 8601, today, yesterday')\n .option('--until <t>', 'End time (date-only values include the whole day)')\n .option('--last <dur>', 'Relative window: <N>h|d|w (mutually exclusive with --since/--until)')\n }\n return shared\n .option('--format <format>', 'Output format: agent|json|text (agents should pass agent explicitly)')\n .option('--max-tokens <n>', 'Token budget for agent text (default 4000)')\n .option('--max-chars <n>', 'Per-message content char limit')\n .option('--full', 'Disable per-message content truncation')\n .option('--fields <a,b>', 'json format: only include these item fields')\n .option('--no-content', 'json format: omit message content (distribution scouting)')\n .option('--raw', 'Bypass privacy preprocessing (debugging; requires user opt-in, json/text only)')\n .option('--verbose', 'Include preprocessing diagnostics in meta')\n}\n\nexport function registerMessageCommands(program: Command): void {\n const messagesCmd = program.command('messages').description('Query messages (list, search, context, between)')\n\n // ---------- messages list ----------\n const listCmd = messagesCmd\n .command('list')\n .description('List messages in a time window (most recent page first, rendered chronologically)')\n .option('--member <ref>', 'Filter by sender (member id, exact name, or `me`)')\n .option('--cursor <token>', 'Opaque pagination cursor from meta.nextCursor')\n .option('--limit <n>', 'Messages per page (default 50, max 500)')\n addSharedOptions(listCmd)\n listCmd.action(async (options: CommonMessageOptions & { member?: string }) => {\n await runQuery('messages.list', options, async (format) => {\n assertRawFormatCompatible(format, options)\n const ctx = createQueryContext(options)\n try {\n assertRawAllowed(ctx, options)\n const time = resolveTimeOptionsForCursor(options)\n const member = options.member ? resolveMember(ctx.db, options.member) : undefined\n const limit = parseLimit(options.limit, 50, 500, '--limit', 1)\n const excludeKeywords = options.raw ? undefined : ctx.preprocessConfig.blacklistKeywords\n\n const fingerprint = queryFingerprint({\n command: 'messages.list',\n session: ctx.session.id,\n startTs: time.startTs ?? null,\n endTs: time.endTs ?? null,\n member: member?.id ?? null,\n exclude: excludeKeywords ?? [],\n })\n const offset = options.cursor ? decodeCursor(options.cursor, fingerprint) : 0\n\n const result = searchMessagesByKeywords(ctx.db, [], {\n startTs: time.startTs,\n endTs: time.endTs,\n senderId: member?.id,\n limit,\n offset,\n excludeKeywords,\n sort: 'desc',\n })\n\n const total = result.total ?? result.messages.length\n const hasMore = offset + result.messages.length < total\n const meta: Record<string, unknown> = {\n session: ctx.session,\n timeRange: time.meta,\n ...(member ? { member } : {}),\n totalHits: total,\n returnedHits: result.messages.length,\n hasMore,\n ...(hasMore ? { nextCursor: encodeCursor(offset + result.messages.length, fingerprint, { time }) } : {}),\n }\n\n return buildMessagesResult(format, ctx, options, result.messages, meta, {\n strategy: 'keep_last',\n })\n } finally {\n ctx.close()\n }\n })\n })\n\n // ---------- messages search ----------\n const searchCmd = messagesCmd\n .command('search')\n .description('Search messages by keywords (LIKE match; multiple keywords default to OR)')\n .argument('<keywords...>', 'Search keywords')\n .option('--match <mode>', 'Keyword join mode: any (OR) | all (AND)', 'any')\n .option('--member <ref>', 'Filter by sender (member id, exact name, or `me`)')\n .option('--context <n>', 'Context messages before/after each hit (default 0)')\n .option('--sort <dir>', 'Hit order by time: asc|desc (asc for \"who said it first\")', 'desc')\n .option('--cursor <token>', 'Opaque pagination cursor from meta.nextCursor')\n .option('--limit <n>', 'Hits per page (default 20, max 500)')\n .option('--max-messages <n>', 'Cap on total messages after context expansion (default 200)')\n addSharedOptions(searchCmd)\n searchCmd.action(\n async (\n keywords: string[],\n options: CommonMessageOptions & {\n match?: string\n member?: string\n context?: string\n sort?: string\n maxMessages?: string\n }\n ) => {\n await runQuery('messages.search', options, async (format) => {\n assertRawFormatCompatible(format, options)\n const searchKeywords = parseSearchKeywords(keywords)\n if (options.match !== 'any' && options.match !== 'all') {\n throw new QueryError({\n code: 'INVALID_ARGUMENT',\n message: `Invalid --match value: ${options.match}`,\n hint: 'Supported: any (OR), all (AND)',\n })\n }\n if (options.sort !== 'asc' && options.sort !== 'desc') {\n throw new QueryError({\n code: 'INVALID_ARGUMENT',\n message: `Invalid --sort value: ${options.sort}`,\n hint: 'Supported: asc, desc',\n })\n }\n const ctx = createQueryContext(options)\n try {\n assertRawAllowed(ctx, options)\n const time = resolveTimeOptionsForCursor(options)\n const member = options.member ? resolveMember(ctx.db, options.member) : undefined\n const limit = parseLimit(options.limit, 20, 500, '--limit', 1)\n const context = parseLimit(options.context, 0, 50, '--context')\n const maxMessages = parseLimit(options.maxMessages, 200, 2000, '--max-messages')\n const excludeKeywords = options.raw ? undefined : ctx.preprocessConfig.blacklistKeywords\n\n const fingerprint = queryFingerprint({\n command: 'messages.search',\n session: ctx.session.id,\n keywords: searchKeywords,\n match: options.match,\n sort: options.sort,\n startTs: time.startTs ?? null,\n endTs: time.endTs ?? null,\n member: member?.id ?? null,\n exclude: excludeKeywords ?? [],\n })\n const offset = options.cursor ? decodeCursor(options.cursor, fingerprint) : 0\n\n const result = searchMessagesByKeywords(ctx.db, searchKeywords, {\n startTs: time.startTs,\n endTs: time.endTs,\n senderId: member?.id,\n limit,\n offset,\n matchMode: options.match,\n excludeKeywords,\n sort: options.sort as 'asc' | 'desc',\n })\n\n const hits = result.messages\n const hitIds = new Set(hits.map((m) => m.id))\n const warnings: string[] = []\n\n let pipelineMessages: MessageLike[] = hits\n if (context > 0 && hits.length > 0) {\n const perHitCost = context * 2 + 1\n let expandableHits = hits\n if (hits.length * perHitCost > maxMessages) {\n const allowed = Math.max(1, Math.floor(maxMessages / perHitCost))\n expandableHits = hits.slice(0, allowed)\n warnings.push(\n `--max-messages ${maxMessages}: context expanded for the first ${allowed} of ${hits.length} hits`\n )\n }\n const expanded = getSearchMessageContext(\n ctx.db,\n expandableHits.map((m) => m.id),\n context,\n context\n )\n const keptHits = new Set(expandableHits.map((m) => m.id))\n const tail = hits.filter((m) => !keptHits.has(m.id))\n pipelineMessages = appendUniqueTailMessages(expanded, tail)\n if (pipelineMessages.length > maxMessages) {\n pipelineMessages = capExpandedSearchMessages(pipelineMessages, hitIds, maxMessages)\n warnings.push(`response capped at ${maxMessages} messages`)\n }\n }\n\n const total = result.total ?? hits.length\n const hasMore = offset + hits.length < total\n const meta: Record<string, unknown> = {\n session: ctx.session,\n timeRange: time.meta,\n query: { keywords: searchKeywords, match: options.match, engine: 'like', sort: options.sort, context },\n ...(member ? { member } : {}),\n totalHits: total,\n returnedHits: hits.length,\n hasMore,\n ...(hasMore ? { nextCursor: encodeCursor(offset + hits.length, fingerprint, { time }) } : {}),\n ...(warnings.length > 0 ? { warnings } : {}),\n }\n\n return buildMessagesResult(format, ctx, options, pipelineMessages, meta, {\n hitIds,\n strategy: searchTruncationStrategy(options.sort),\n defaultMaxChars: 120,\n })\n } finally {\n ctx.close()\n }\n })\n }\n )\n\n // ---------- messages context ----------\n const contextCmd = messagesCmd\n .command('context')\n .description('Show messages around specific numeric message ids')\n .requiredOption('--id <ids>', 'Message id(s), comma-separated')\n .option('--window <n>', 'Messages before/after each id (default 10, max 100)')\n addSharedOptions(contextCmd, { includeTime: false })\n contextCmd.action(async (options: CommonMessageOptions & { id: string; window?: string }) => {\n await runQuery('messages.context', options, async (format) => {\n assertRawFormatCompatible(format, options)\n const ids = parseContextIds(options.id)\n const ctx = createQueryContext(options)\n try {\n assertRawAllowed(ctx, options)\n const window = parseLimit(options.window, 10, 100, '--window')\n const messages = getMessageContext(ctx.db, ids, window)\n assertContextAnchorsPresent(ids, messages, options.id)\n const meta: Record<string, unknown> = {\n session: ctx.session,\n anchorIds: ids,\n window,\n }\n return buildMessagesResult(format, ctx, options, messages, meta, {\n hitIds: new Set(ids),\n strategy: 'keep_last',\n })\n } finally {\n ctx.close()\n }\n })\n })\n\n // ---------- messages between ----------\n const betweenCmd = messagesCmd\n .command('between')\n .description('Conversation between two members (repeat --member exactly twice)')\n .option('--member <ref>', 'Member id, exact name, or `me` (repeat twice)', collectMembers, [] as string[])\n .option('--cursor <token>', 'Opaque pagination cursor from meta.nextCursor')\n .option('--limit <n>', 'Messages per page (default 50, max 500)')\n addSharedOptions(betweenCmd)\n betweenCmd.action(async (options: CommonMessageOptions & { member: string[] }) => {\n await runQuery('messages.between', options, async (format) => {\n assertRawFormatCompatible(format, options)\n if (options.member.length !== 2) {\n throw new QueryError({\n code: 'INVALID_ARGUMENT',\n message: `--member must be given exactly twice (got ${options.member.length})`,\n hint: 'Example: messages between --member me --member 小红',\n })\n }\n const ctx = createQueryContext(options)\n try {\n assertRawAllowed(ctx, options)\n const time = resolveTimeOptionsForCursor(options)\n const memberA = resolveMember(ctx.db, options.member[0])\n const memberB = resolveMember(ctx.db, options.member[1])\n const limit = parseLimit(options.limit, 50, 500, '--limit', 1)\n const excludeKeywords = options.raw ? undefined : ctx.preprocessConfig.blacklistKeywords\n\n const fingerprint = queryFingerprint({\n command: 'messages.between',\n session: ctx.session.id,\n members: [memberA.id, memberB.id],\n startTs: time.startTs ?? null,\n endTs: time.endTs ?? null,\n exclude: excludeKeywords ?? [],\n })\n const offset = options.cursor ? decodeCursor(options.cursor, fingerprint) : 0\n\n const timeFilter =\n time.startTs !== undefined || time.endTs !== undefined\n ? { startTs: time.startTs ?? 0, endTs: time.endTs ?? Math.floor(Date.now() / 1000) }\n : undefined\n const result = getConversationBetween(ctx.db, memberA.id, memberB.id, timeFilter, limit, {\n offset,\n excludeKeywords,\n })\n\n const returned = result.messages.length\n const hasMore = offset + returned < result.total\n const meta: Record<string, unknown> = {\n session: ctx.session,\n timeRange: time.meta,\n members: [memberA, memberB],\n totalHits: result.total,\n returnedHits: returned,\n hasMore,\n ...(hasMore ? { nextCursor: encodeCursor(offset + returned, fingerprint, { time }) } : {}),\n }\n\n return buildMessagesResult(format, ctx, options, result.messages, meta, {\n strategy: 'keep_last',\n })\n } finally {\n ctx.close()\n }\n })\n })\n}\n\nfunction collectMembers(value: string, previous: string[]): string[] {\n return [...previous, value]\n}\n","/**\n * stats query command group: overview / activity / time / keywords / response.\n *\n * `stats keywords` privacy prerequisites (design §15.4): blacklist keywords are\n * pushed down to SQL at message level, and the resulting vocabulary is filtered\n * through the user's desensitize rules (over-fetch then trim, so filtered-out\n * words don't leave the top-N short).\n */\n\nimport type { Command } from 'commander'\nimport {\n getSessionMeta,\n getSessionOverview,\n getMemberActivity,\n getHourlyActivity,\n getWeekdayActivity,\n getDailyActivity,\n getMonthlyActivity,\n getChatOverview,\n computeResponseTimeStats,\n executeParameterizedSql,\n} from '@openchatlab/core'\nimport type { SupportedLocale } from '@openchatlab/core'\nimport { computeWordFrequency, desensitizeText } from '@openchatlab/node-runtime'\nimport { initRuntime } from '../runtime'\nimport { runQuery } from './runner'\nimport { createQueryContext } from './context'\nimport { resolveMember } from './resolve'\nimport { parseTimeOptions, parseLimit, epochToIso } from './parse'\nimport { QueryError } from './envelope'\n\ninterface StatsTimeOptions {\n session?: string\n since?: string\n until?: string\n last?: string\n format?: string\n}\n\nfunction addStatsOptions(cmd: Command): Command {\n return cmd\n .option('--session <ref>', 'Session id or unique name (auto-selected when only one exists)')\n .option('--since <t>', 'Start time: YYYY-MM-DD, ISO 8601, today, yesterday')\n .option('--until <t>', 'End time (date-only values include the whole day)')\n .option('--last <dur>', 'Relative window: <N>h|d|w')\n .option('--format <format>', 'Output format: agent|json|text')\n}\n\nfunction toTimeFilter(time: ReturnType<typeof parseTimeOptions>): { startTs: number; endTs: number } | undefined {\n if (time.startTs === undefined && time.endTs === undefined) return undefined\n return { startTs: time.startTs ?? 0, endTs: time.endTs ?? Math.floor(Date.now() / 1000) }\n}\n\nexport function responseStatsMessagesSql(): string {\n return `SELECT msg.id, msg.sender_id, COALESCE(m.group_nickname, m.account_name) AS name, msg.ts\n FROM message msg\n JOIN member m ON msg.sender_id = m.id\n WHERE msg.type = 0 AND msg.ts >= @startTs\n ORDER BY msg.ts ASC, msg.id ASC`\n}\n\nexport function registerStatsCommands(program: Command): void {\n const statsCmd = program\n .command('stats')\n .description('Session statistics (overview, activity, time, keywords, response)')\n .argument('[session-id]', 'legacy: session id (deprecated alias)')\n .option('--format <format>', 'Output format')\n .option('--top <n>', 'legacy: top N members', '10')\n .action((sessionId: string | undefined, options: { format?: string; top: string }, command: Command) => {\n if (!sessionId) {\n command.help({ error: false })\n return\n }\n console.error(\n '[Deprecated] `chatlab stats <session-id>` — use `chatlab stats overview --session <id>`; alias kept for one major version'\n )\n legacyStats(sessionId, options)\n })\n\n // ---------- stats overview ----------\n statsCmd\n .command('overview')\n .description('Session overview: totals, time range, top members')\n .option('--session <ref>', 'Session id or unique name')\n .option('--format <format>', 'Output format: agent|json|text')\n .action(async (options: { session?: string; format?: string }) => {\n await runQuery('stats.overview', options, async () => {\n const ctx = createQueryContext(options)\n try {\n const overview = getChatOverview(ctx.db)\n if (!overview) {\n throw new QueryError({ code: 'SESSION_NOT_FOUND', message: 'Session metadata missing' })\n }\n const data = {\n name: overview.name,\n platform: overview.platform,\n type: overview.type,\n totalMessages: overview.totalMessages,\n totalMembers: overview.totalMembers,\n firstMessage: overview.firstMessageTs == null ? null : epochToIso(overview.firstMessageTs),\n lastMessage: overview.lastMessageTs == null ? null : epochToIso(overview.lastMessageTs),\n topMembers: overview.topMembers,\n summaryCount: overview.summaryCount,\n }\n return {\n data,\n meta: { session: ctx.session },\n renderText: () =>\n `${data.name}\\nMessages: ${data.totalMessages} | Members: ${data.totalMembers}\\n` +\n `Range: ${data.firstMessage ?? '-'} ~ ${data.lastMessage ?? '-'}\\n` +\n data.topMembers.map((m, i) => ` ${i + 1}. ${m.name} - ${m.count}`).join('\\n'),\n }\n } finally {\n ctx.close()\n }\n })\n })\n\n // ---------- stats activity ----------\n const activityCmd = statsCmd.command('activity').description('Member activity ranking')\n addStatsOptions(activityCmd)\n activityCmd.option('--top <n>', 'Top N members (default 10, max 100)')\n activityCmd.action(async (options: StatsTimeOptions & { top?: string }) => {\n await runQuery('stats.activity', options, async () => {\n const ctx = createQueryContext(options)\n try {\n const time = parseTimeOptions(options)\n const top = parseLimit(options.top, 10, 100, '--top')\n const items = getMemberActivity(ctx.db, toTimeFilter(time))\n .slice(0, top)\n .map((m) => ({ id: m.memberId, name: m.name, messageCount: m.messageCount, percentage: m.percentage }))\n return {\n data: { items },\n meta: { session: ctx.session, timeRange: time.meta, returned: items.length },\n renderText: () =>\n items.map((m, i) => `${i + 1}. ${m.name} - ${m.messageCount} messages (${m.percentage}%)`).join('\\n') ||\n 'No activity.',\n }\n } finally {\n ctx.close()\n }\n })\n })\n\n // ---------- stats time ----------\n const timeCmd = statsCmd.command('time').description('Message distribution over time')\n addStatsOptions(timeCmd)\n timeCmd.requiredOption('--by <unit>', 'Bucket unit: hour|weekday|day|month')\n timeCmd.action(async (options: StatsTimeOptions & { by: string }) => {\n await runQuery('stats.time', options, async () => {\n const ctx = createQueryContext(options)\n try {\n const time = parseTimeOptions(options)\n const filter = toTimeFilter(time)\n let items: unknown[]\n switch (options.by) {\n case 'hour':\n items = getHourlyActivity(ctx.db, filter)\n break\n case 'weekday':\n items = getWeekdayActivity(ctx.db, filter)\n break\n case 'day':\n items = getDailyActivity(ctx.db, filter)\n break\n case 'month':\n items = getMonthlyActivity(ctx.db, filter)\n break\n default:\n throw new QueryError({\n code: 'INVALID_ARGUMENT',\n message: `Invalid --by value: ${options.by}`,\n hint: 'Supported: hour, weekday, day, month',\n })\n }\n return {\n data: { items },\n meta: { session: ctx.session, timeRange: time.meta, by: options.by, returned: items.length },\n }\n } finally {\n ctx.close()\n }\n })\n })\n\n // ---------- stats keywords ----------\n const keywordsCmd = statsCmd.command('keywords').description('High-frequency words (privacy-filtered)')\n addStatsOptions(keywordsCmd)\n keywordsCmd.option('--top <n>', 'Top N words (default 20, max 100)')\n keywordsCmd.option('--member <ref>', 'Filter by sender (member id, exact name, or `me`)')\n keywordsCmd.action(async (options: StatsTimeOptions & { top?: string; member?: string }) => {\n await runQuery('stats.keywords', options, async () => {\n const ctx = createQueryContext(options)\n try {\n const time = parseTimeOptions(options)\n const top = parseLimit(options.top, 20, 100, '--top')\n const member = options.member ? resolveMember(ctx.db, options.member) : undefined\n // over-fetch so vocabulary-level desensitize filtering doesn't leave top-N short\n const fetchN = Math.max(top * 3, top + 20)\n\n let result\n try {\n result = computeWordFrequency(ctx.db, {\n sessionId: ctx.session.id,\n locale: ctx.locale as SupportedLocale,\n timeFilter: toTimeFilter(time),\n memberId: member?.id,\n topN: fetchN,\n excludeKeywords: ctx.preprocessConfig.blacklistKeywords,\n })\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n if (/jieba|segment/i.test(message)) {\n return {\n data: { items: [], status: 'segmentation_unavailable' },\n meta: {\n session: ctx.session,\n hint: 'Word segmentation engine unavailable (optional dependency @node-rs/jieba failed to load)',\n },\n }\n }\n throw err\n }\n\n const enabledRules = ctx.preprocessConfig.desensitize\n ? ctx.preprocessConfig.desensitizeRules.filter((r) => r.enabled)\n : []\n const items = result.words\n .filter((w) => enabledRules.length === 0 || desensitizeText(w.word, enabledRules) === w.word)\n .slice(0, top)\n\n return {\n data: { items },\n meta: {\n session: ctx.session,\n timeRange: time.meta,\n ...(member ? { member } : {}),\n totalMessages: result.totalMessages,\n uniqueWords: result.uniqueWords,\n returned: items.length,\n ...(items.length === 0 && result.totalMessages > 0\n ? {\n hint: 'No words extracted; the zh-CN segmentation dictionary may be missing (downloadable in the ChatLab app)',\n }\n : {}),\n },\n renderText: () => items.map((w, i) => `${i + 1}. ${w.word} (${w.count})`).join('\\n') || 'No words.',\n }\n } finally {\n ctx.close()\n }\n })\n })\n\n // ---------- stats response ----------\n const responseCmd = statsCmd.command('response').description('Reply speed ranking (median response gap)')\n responseCmd\n .option('--session <ref>', 'Session id or unique name')\n .option('--last <dur>', 'Relative window: <N>h|d|w (default 30d)')\n .option('--top <n>', 'Top N responders (default 10, max 100)')\n .option('--format <format>', 'Output format: agent|json|text')\n responseCmd.action(async (options: { session?: string; last?: string; top?: string; format?: string }) => {\n await runQuery('stats.response', options, async () => {\n const ctx = createQueryContext(options)\n try {\n const time = parseTimeOptions({ last: options.last ?? '30d' })\n const top = parseLimit(options.top, 10, 100, '--top')\n const rows = executeParameterizedSql<{ sender_id: number; name: string; ts: number }>(\n ctx.db,\n responseStatsMessagesSql(),\n { startTs: time.startTs ?? 0 }\n )\n const items = computeResponseTimeStats(\n rows.map((r) => ({ senderId: r.sender_id, name: r.name, ts: r.ts })),\n top\n )\n return {\n data: { items },\n meta: { session: ctx.session, timeRange: time.meta, returned: items.length },\n renderText: () =>\n items\n .map(\n (s, i) => `${i + 1}. ${s.name} - median ${s.medianSeconds}s, avg ${s.avgSeconds}s (${s.responseCount})`\n )\n .join('\\n') || 'Not enough response data.',\n }\n } finally {\n ctx.close()\n }\n })\n })\n}\n\n// ==================== Legacy alias (output kept byte-compatible) ====================\n\nfunction legacyStats(sessionId: string, options: { format?: string; top: string }): void {\n const { dbManager } = initRuntime()\n const db = dbManager.open(sessionId)\n if (!db) {\n console.error(`Session ${sessionId} not found`)\n process.exit(1)\n }\n\n const meta = getSessionMeta(db)\n const overview = getSessionOverview(db)\n const topMembers = getMemberActivity(db).slice(0, parseInt(options.top))\n\n if (options.format === 'json') {\n console.log(JSON.stringify({ meta, overview, topMembers }, null, 2))\n } else {\n console.log(`\\nSession: ${meta?.name}`)\n console.log(`Platform: ${meta?.platform} | Type: ${meta?.type}`)\n console.log(`Total messages: ${overview.totalMessages}`)\n console.log(`Total members: ${overview.totalMembers}`)\n const range =\n overview.firstMessageTs && overview.lastMessageTs\n ? `${new Date(overview.firstMessageTs * 1000).toLocaleDateString()} ~ ${new Date(overview.lastMessageTs * 1000).toLocaleDateString()}`\n : 'Unknown'\n console.log(`Time range: ${range}`)\n\n if (topMembers.length > 0) {\n console.log(`\\nActivity ranking (Top ${options.top}):`)\n for (const [i, m] of topMembers.entries()) {\n console.log(` ${i + 1}. ${m.name} - ${m.messageCount} messages (${m.percentage}%)`)\n }\n }\n }\n\n dbManager.closeAll()\n}\n","/**\n * topics / sql / schema query commands.\n *\n * Derived-text privacy (design §6.2): topic summaries are LLM-generated from raw\n * content and are desensitized by default. SQL string cells are desensitized,\n * but reading the message content column requires --raw so SQL expressions\n * cannot encode raw bodies before the sanitizer sees them.\n */\n\nimport type { Command } from 'commander'\nimport { getSegmentSummaries, getSegmentMessages, getDatabaseSchema, executeReadonlySql } from '@openchatlab/core'\nimport { desensitizeText, matchesBlacklist, type DesensitizeRule } from '@openchatlab/node-runtime'\nimport { runQuery } from './runner'\nimport { createQueryContext, assertRawAllowed, type QueryContext } from './context'\nimport { parseTimeOptions, parseLimit, epochToIso } from './parse'\nimport { QueryError } from './envelope'\nimport { buildMessagesResult, assertRawFormatCompatible } from './messages-output'\n\nfunction enabledRules(ctx: QueryContext): DesensitizeRule[] {\n return ctx.preprocessConfig.desensitize ? ctx.preprocessConfig.desensitizeRules.filter((r) => r.enabled) : []\n}\n\nexport function parseSqlRowLimit(value: string | undefined): number {\n return parseLimit(value, 100, 1000, '--limit', 1)\n}\n\nexport function clampSqlRows(\n rows: Record<string, unknown>[],\n maxRows: number,\n alreadyTruncated: boolean\n): { rows: Record<string, unknown>[]; truncated: boolean } {\n if (maxRows <= 0 || rows.length <= maxRows) {\n return { rows, truncated: alreadyTruncated }\n }\n return { rows: rows.slice(0, maxRows), truncated: true }\n}\n\nfunction escapeRegExp(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n}\n\nconst SQL_IDENTIFIER = String.raw`(?:\"[^\"]+\"|'[^']+'|` + '`[^`]+`' + String.raw`|\\[[^\\]]+\\]|[A-Za-z_][A-Za-z0-9_]*)`\nconst SQL_TABLE_REF_END = String.raw`(?=\\s|$|[,;])`\nconst SQL_KEYWORDS = new Set([\n 'where',\n 'join',\n 'left',\n 'right',\n 'inner',\n 'outer',\n 'cross',\n 'order',\n 'group',\n 'limit',\n 'on',\n 'using',\n])\n\nfunction sqlNamePattern(name: string): string {\n const escaped = escapeRegExp(name)\n return String.raw`(?:\"${escaped}\"|'${escaped}'|` + '`' + escaped + '`' + String.raw`|\\[${escaped}\\]|${escaped})`\n}\n\nfunction unquoteSqlIdentifier(value: string): string {\n const trimmed = value.trim()\n if (trimmed.startsWith('[') && trimmed.endsWith(']')) return trimmed.slice(1, -1)\n const quote = trimmed[0]\n if ((quote === '\"' || quote === \"'\" || quote === '`') && trimmed.endsWith(quote)) {\n return trimmed.slice(1, -1)\n }\n return trimmed\n}\n\nfunction messageTablePattern(): string {\n const schemaPrefix = String.raw`(?:${SQL_IDENTIFIER}\\s*\\.\\s*)?`\n return `${schemaPrefix}${sqlNamePattern('message')}`\n}\n\nfunction selectsMessageStar(query: string): boolean {\n const selectMatch = query.match(/\\bselect\\b([\\s\\S]+?)\\bfrom\\b/i)\n if (!selectMatch) return false\n\n const projection = selectMatch[1]\n const messageTable = messageTablePattern()\n if (/(^|[,\\s])\\*(?=$|[,\\s])/i.test(projection)) {\n return new RegExp(String.raw`\\b(?:from|join)\\s+${messageTable}${SQL_TABLE_REF_END}`, 'i').test(query)\n }\n\n const aliasMatches = query.matchAll(\n new RegExp(String.raw`\\b(?:from|join)\\s+${messageTable}(?:\\s+(?:as\\s+)?(${SQL_IDENTIFIER}))?`, 'gi')\n )\n const aliases = new Set(['message'])\n for (const match of aliasMatches) {\n const alias = match[1] ? unquoteSqlIdentifier(match[1]).toLowerCase() : undefined\n if (alias && !SQL_KEYWORDS.has(alias)) aliases.add(alias)\n }\n\n return [...aliases].some((alias) =>\n new RegExp(`${sqlNamePattern(alias)}\\\\s*\\\\.\\\\s*\\\\*(?=$|[\\\\s,])`, 'i').test(projection)\n )\n}\n\nexport function assertSqlPrivacyAllowed(query: string, raw: boolean): void {\n if (raw) return\n if (!/(^|[^A-Za-z0-9_])[\"'`[]?content[\"'`\\]]?(?![A-Za-z0-9_])/i.test(query) && !selectsMessageStar(query)) {\n return\n }\n\n throw new QueryError({\n code: 'INVALID_ARGUMENT',\n message: 'SQL queries that read message content require --raw',\n hint: 'Use dedicated message commands for privacy-processed content, or enable raw mode and pass --raw for debugging',\n })\n}\n\nexport function assertSqlResultPrivacyAllowed(columns: readonly string[], raw: boolean): void {\n if (raw) return\n if (!columns.some((column) => unquoteSqlIdentifier(column).toLowerCase() === 'content')) return\n\n throw new QueryError({\n code: 'INVALID_ARGUMENT',\n message: 'SQL results that include message content require --raw',\n hint: 'Use dedicated message commands for privacy-processed content, or enable raw mode and pass --raw for debugging',\n })\n}\n\nexport interface TopicListItem {\n id: number\n since: string\n until: string\n messages: number\n participants: string[]\n summary: string | null\n}\n\n/**\n * Agent-format body for topics list: compact text with [#segmentId] markers\n * that chain into `topics show --id <segmentId>`.\n */\nexport function topicsAgentText(items: TopicListItem[]): string {\n if (items.length === 0) return 'No summaries.'\n const blocks = items.map((t) => {\n const header = `[#${t.id}] ${t.since} ~ ${t.until} (${t.messages} msgs) ${t.participants.join(', ')}`\n return t.summary ? `${header}\\n${t.summary}` : header\n })\n return `returned: ${items.length}\\n\\n${blocks.join('\\n\\n')}`\n}\n\nexport function registerTopicCommands(program: Command): void {\n const topicsCmd = program.command('topics').description('Conversation segments and AI summaries')\n\n // ---------- topics list ----------\n topicsCmd\n .command('list')\n .description('List AI-generated segment summaries (desensitized)')\n .option('--session <ref>', 'Session id or unique name')\n .option('--since <t>', 'Start time')\n .option('--until <t>', 'End time')\n .option('--last <dur>', 'Relative window: <N>h|d|w')\n .option('--query <kw>', 'Filter summaries by substring')\n .option('--limit <n>', 'Max summaries (default 20, max 100)')\n .option('--format <format>', 'Output format: agent|json|text')\n .action(\n async (options: {\n session?: string\n since?: string\n until?: string\n last?: string\n query?: string\n limit?: string\n format?: string\n }) => {\n await runQuery('topics.list', options, async (format) => {\n const ctx = createQueryContext(options)\n try {\n const time = parseTimeOptions(options)\n const limit = parseLimit(options.limit, 20, 100, '--limit')\n const timeFilter =\n time.startTs !== undefined || time.endTs !== undefined\n ? { startTs: time.startTs ?? 0, endTs: time.endTs ?? Math.floor(Date.now() / 1000) }\n : undefined\n\n // over-fetch so blacklist-dropped summaries don't shrink the page\n const rows = getSegmentSummaries(ctx.db, { limit: limit + 20, timeFilter })\n const rules = enabledRules(ctx)\n const blacklist = ctx.preprocessConfig.blacklistKeywords\n\n let removed = 0\n const items = rows\n .filter((row) => {\n if (row.summary && matchesBlacklist(row.summary, blacklist)) {\n removed++\n return false\n }\n return true\n })\n .map((row) => ({\n id: row.id,\n since: epochToIso(row.startTs),\n until: epochToIso(row.endTs),\n messages: row.messageCount,\n participants: row.participants,\n summary: row.summary ? desensitizeText(row.summary, rules) : row.summary,\n }))\n .filter((row) => (options.query ? (row.summary ?? '').includes(options.query) : true))\n .slice(0, limit)\n\n if (items.length === 0) {\n const any = getSegmentSummaries(ctx.db, { limit: 1 })\n if (any.length === 0) {\n const hint = 'Run summary generation in the ChatLab app, or use `chatlab stats keywords` as a fallback.'\n return {\n data:\n format === 'agent'\n ? { text: 'No AI summaries generated yet.', status: 'summaries_not_generated' }\n : { items: [], status: 'summaries_not_generated' },\n meta: { session: ctx.session, hint },\n }\n }\n }\n\n const meta = {\n session: ctx.session,\n timeRange: time.meta,\n returned: items.length,\n ...(removed > 0 ? { warnings: [`${removed} summaries removed by blacklist`] } : {}),\n }\n return {\n data: format === 'agent' ? { text: topicsAgentText(items) } : { items },\n meta,\n renderText: () => topicsAgentText(items),\n }\n } finally {\n ctx.close()\n }\n })\n }\n )\n\n // ---------- topics show ----------\n topicsCmd\n .command('show')\n .description('Show original messages of one segment')\n .requiredOption('--id <segment-id>', 'Segment id from topics list')\n .option('--session <ref>', 'Session id or unique name')\n .option('--limit <n>', 'Max messages (default 200, max 500)')\n .option('--format <format>', 'Output format: agent|json|text')\n .option('--max-tokens <n>', 'Token budget for agent text (default 4000)')\n .option('--max-chars <n>', 'Per-message content char limit')\n .option('--full', 'Disable per-message content truncation')\n .option('--fields <a,b>', 'json format: only include these item fields')\n .option('--no-content', 'json format: omit message content')\n .option('--raw', 'Bypass privacy preprocessing (debugging; requires user opt-in, json/text only)')\n .option('--verbose', 'Include preprocessing diagnostics in meta')\n .action(\n async (options: {\n id: string\n session?: string\n limit?: string\n format?: string\n maxTokens?: string\n maxChars?: string\n full?: boolean\n fields?: string\n content?: boolean\n raw?: boolean\n verbose?: boolean\n }) => {\n await runQuery('topics.show', options, async (format) => {\n assertRawFormatCompatible(format, options)\n const segmentId = Number(options.id)\n if (!Number.isInteger(segmentId) || segmentId < 0) {\n throw new QueryError({\n code: 'INVALID_ARGUMENT',\n message: `Invalid --id value: ${options.id}`,\n hint: 'Segment ids come from `chatlab topics list`',\n })\n }\n const ctx = createQueryContext(options)\n try {\n assertRawAllowed(ctx, options)\n const limit = parseLimit(options.limit, 200, 500, '--limit')\n const segment = getSegmentMessages(ctx.db, segmentId, limit)\n if (!segment) {\n throw new QueryError({\n code: 'SEGMENT_NOT_FOUND',\n message: `Segment ${segmentId} not found`,\n hint: 'Run `chatlab topics list` to see available segments',\n })\n }\n const meta: Record<string, unknown> = {\n session: ctx.session,\n segment: {\n id: segment.segmentId,\n since: epochToIso(segment.startTs),\n until: epochToIso(segment.endTs),\n messageCount: segment.messageCount,\n participants: segment.participants,\n },\n returnedHits: segment.returnedCount,\n }\n return buildMessagesResult(format, ctx, options, segment.messages, meta, {\n strategy: 'keep_last',\n })\n } finally {\n ctx.close()\n }\n })\n }\n )\n}\n\nexport function registerSqlCommands(program: Command): void {\n program\n .command('sql')\n .description('Read-only SQL fallback (string cells are desensitized; message content requires --raw)')\n .argument('<query>', 'SELECT / WITH statement')\n .option('--session <ref>', 'Session id or unique name')\n .option('--limit <n>', 'Max rows (default 100, max 1000)')\n .option('--format <format>', 'Output format: agent|json|text')\n .option('--raw', 'Return raw cell values (debugging; requires user opt-in)')\n .action(async (query: string, options: { session?: string; limit?: string; format?: string; raw?: boolean }) => {\n await runQuery('sql', options, async () => {\n const ctx = createQueryContext(options)\n try {\n if (!ctx.allowSql) {\n throw new QueryError({\n code: 'SQL_DISABLED',\n message: 'The sql command is disabled by configuration',\n hint: 'The user can re-enable it with `chatlab config set cli.allow_sql true`',\n })\n }\n assertRawAllowed(ctx, options)\n assertSqlPrivacyAllowed(query, !!options.raw)\n const limit = parseSqlRowLimit(options.limit)\n\n let result\n try {\n result = executeReadonlySql(ctx.db, query, limit)\n } catch (err) {\n throw new QueryError({\n code: 'SQL_ERROR',\n message: err instanceof Error ? err.message : String(err),\n hint: 'Run `chatlab schema` to inspect available tables and columns',\n })\n }\n assertSqlResultPrivacyAllowed(result.columns, !!options.raw)\n\n const clamped = clampSqlRows(result.rows, limit, result.truncated)\n let rows = clamped.rows\n let removed = 0\n if (!options.raw) {\n const rules = enabledRules(ctx)\n const blacklist = ctx.preprocessConfig.blacklistKeywords\n const sanitized: Record<string, unknown>[] = []\n for (const row of rows) {\n let drop = false\n const clean: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(row)) {\n if (typeof value === 'string') {\n if (matchesBlacklist(value, blacklist)) {\n drop = true\n break\n }\n clean[key] = desensitizeText(value, rules)\n } else {\n clean[key] = value\n }\n }\n if (drop) removed++\n else sanitized.push(clean)\n }\n rows = sanitized\n }\n\n return {\n data: { columns: result.columns, rows, rowCount: rows.length, truncated: clamped.truncated },\n meta: {\n session: ctx.session,\n ...(options.raw ? { preprocess: { raw: true } } : {}),\n ...(removed > 0 ? { warnings: [`${removed} row(s) removed by blacklist`] } : {}),\n },\n renderText: () =>\n rows.length === 0\n ? 'No results.'\n : rows.map((r) => JSON.stringify(r)).join('\\n') +\n `\\n${rows.length} row(s)${clamped.truncated ? ' (truncated)' : ''}`,\n }\n } finally {\n ctx.close()\n }\n })\n })\n\n program\n .command('schema')\n .description('Show session database schema (for the sql command)')\n .option('--session <ref>', 'Session id or unique name')\n .option('--format <format>', 'Output format: agent|json|text')\n .action(async (options: { session?: string; format?: string }) => {\n await runQuery('schema', options, async () => {\n const ctx = createQueryContext(options)\n try {\n const items = getDatabaseSchema(ctx.db)\n return {\n data: { items },\n meta: { session: ctx.session, returned: items.length },\n renderText: () => items.map((t) => t.sql).join('\\n\\n'),\n }\n } finally {\n ctx.close()\n }\n })\n })\n}\n","/**\n * Registration entry for agent-facing query commands (design §7) and the\n * deprecated top-level aliases (`search` / `query`, hidden, one major version).\n */\n\nimport type { Command } from 'commander'\nimport type { ChatLabConfig } from '@openchatlab/config'\nimport { searchMessagesLike, executeReadonlySql } from '@openchatlab/core'\nimport { initRuntime } from '../runtime'\nimport { registerSessionCommands, registerMemberCommands } from './commands-sessions-members'\nimport { registerMessageCommands } from './commands-messages'\nimport { registerStatsCommands } from './commands-stats'\nimport {\n registerTopicCommands,\n registerSqlCommands,\n assertSqlPrivacyAllowed,\n assertSqlResultPrivacyAllowed,\n} from './commands-topics-sql'\nimport { QueryError } from './envelope'\n\nexport function registerQueryCommands(program: Command): void {\n registerSessionCommands(program)\n registerMemberCommands(program)\n registerMessageCommands(program)\n registerStatsCommands(program)\n registerTopicCommands(program)\n registerSqlCommands(program)\n registerLegacyAliases(program)\n}\n\nexport function assertLegacySqlAllowed(config: Pick<ChatLabConfig, 'cli'>, sql?: string): void {\n if (!config.cli.allow_sql) {\n throw new QueryError({\n code: 'SQL_DISABLED',\n message: 'The sql command is disabled by configuration',\n hint: 'The user can re-enable it with `chatlab config set cli.allow_sql true`',\n })\n }\n if (sql) assertSqlPrivacyAllowed(sql, false)\n}\n\n// ==================== Hidden legacy aliases (output kept byte-compatible) ====================\n\nfunction registerLegacyAliases(program: Command): void {\n program\n .command('search <session-id> <keyword>', { hidden: true })\n .description('Deprecated: use `chatlab messages search`')\n .option('--limit <n>', 'Max results to return', '20')\n .option('--format <format>', 'Output format (table|json)', 'table')\n .action((sessionId: string, keyword: string, options: { limit: string; format: string }) => {\n console.error(\n '[Deprecated] `chatlab search` — use `chatlab messages search <keyword> --session <id>`; alias kept for one major version'\n )\n const { dbManager } = initRuntime()\n const db = dbManager.open(sessionId)\n if (!db) {\n console.error(`Session ${sessionId} not found`)\n process.exit(1)\n }\n\n const limit = parseInt(options.limit)\n const result = searchMessagesLike(db, keyword, { limit })\n\n if (options.format === 'json') {\n console.log(JSON.stringify(result, null, 2))\n } else {\n console.log(\n `Search \"${keyword}\" - ${result.total} result(s)${result.hasMore ? ' (showing first ' + limit + ')' : ''}:\\n`\n )\n for (const msg of result.messages) {\n const time = new Date(msg.timestamp * 1000).toLocaleString()\n console.log(` [${time}] ${msg.senderName}: ${msg.content}`)\n }\n }\n\n dbManager.closeAll()\n })\n\n program\n .command('query <session-id>', { hidden: true })\n .description('Deprecated: use `chatlab sql`')\n .requiredOption('--sql <sql>', 'SQL query statement')\n .option('--format <format>', 'Output format (table|json)', 'table')\n .action((sessionId: string, options: { sql: string; format: string }) => {\n console.error(\n '[Deprecated] `chatlab query` — use `chatlab sql \"<SELECT ...>\" --session <id>`; alias kept for one major version'\n )\n const { config, dbManager } = initRuntime()\n const db = dbManager.open(sessionId)\n if (!db) {\n console.error(`Session ${sessionId} not found`)\n process.exit(1)\n }\n\n try {\n assertLegacySqlAllowed(config, options.sql)\n const result = executeReadonlySql(db, options.sql)\n assertSqlResultPrivacyAllowed(result.columns, false)\n if (options.format === 'json') {\n console.log(JSON.stringify(result, null, 2))\n } else {\n if (result.rows.length === 0) {\n console.log('No results.')\n } else {\n printTable(result.columns, result.rows)\n console.log(`\\n${result.rowCount} row(s)${result.truncated ? ' (truncated)' : ''}`)\n }\n }\n } catch (err) {\n if (err instanceof QueryError) {\n console.error(`${err.code}: ${err.message}`)\n if (err.hint) console.error(`Hint: ${err.hint}`)\n process.exit(2)\n }\n console.error(`SQL error: ${err instanceof Error ? err.message : err}`)\n process.exit(1)\n }\n\n dbManager.closeAll()\n })\n}\n\nfunction printTable(columns: string[], rows: Record<string, unknown>[]): void {\n const widths = columns.map((col) => {\n const maxData = rows.reduce((max, row) => {\n const val = String(row[col] ?? '')\n return Math.max(max, val.length)\n }, 0)\n return Math.max(col.length, Math.min(maxData, 40))\n })\n\n const header = columns.map((col, i) => col.padEnd(widths[i])).join(' | ')\n const separator = widths.map((w) => '-'.repeat(w)).join('-+-')\n console.log(header)\n console.log(separator)\n\n for (const row of rows) {\n const line = columns\n .map((col, i) => {\n const val = String(row[col] ?? '')\n return val.length > 40 ? val.slice(0, 37) + '...' : val.padEnd(widths[i])\n })\n .join(' | ')\n console.log(line)\n }\n}\n","/**\n * `chatlab manifest`: machine-readable command tree generated from commander\n * registration (design §7 discovery). One call replaces N --help probes.\n * Query commands carry full arg/option detail; app-level commands (import,\n * start, ...) are listed by name only and stay out of the query section.\n */\n\nimport type { Command } from 'commander'\nimport { API_VERSION } from './envelope'\n\n/** Command groups that make up the agent-facing query surface. */\nconst QUERY_GROUPS = new Set(['sessions', 'members', 'messages', 'stats', 'topics', 'sql', 'schema', 'manifest'])\n\nexport interface ManifestArg {\n name: string\n required: boolean\n variadic: boolean\n description?: string\n}\n\nexport interface ManifestOption {\n flags: string\n description: string\n default?: unknown\n}\n\nexport interface ManifestCommand {\n name: string\n description: string\n args: ManifestArg[]\n options: ManifestOption[]\n}\n\nexport interface ManifestExample {\n task: string\n command: string\n}\n\nexport interface Manifest {\n name: string\n version: string\n apiVersion: number\n formats: string[]\n commands: ManifestCommand[]\n appCommands: Array<{ name: string; description: string }>\n exitCodes: Record<string, string>\n examples: ManifestExample[]\n notes: string[]\n}\n\nconst EXIT_CODES: Record<string, string> = {\n '0': 'success',\n '1': 'internal error',\n '2': 'invalid argument, invalid cursor, or disabled capability',\n '3': 'resource not found (session/member/message/segment)',\n '4': 'ambiguous reference (see error.candidates)',\n '5': 'SQL error',\n}\n\n// Curated task recipes (design §13): agent format for message bodies,\n// json for structural scouting; --raw never appears here.\nconst EXAMPLES: ManifestExample[] = [\n { task: 'What did this group talk about today?', command: 'chatlab messages list --since today --format agent' },\n {\n task: 'Search a topic within a time range',\n command: 'chatlab messages search 旅游 --since 2026-01-01 --limit 20 --format agent',\n },\n {\n task: 'Who mentioned it first (message ids)?',\n command: 'chatlab messages search 报销 --sort asc --limit 5 --format agent',\n },\n {\n task: 'Follow up a single [#id] marker with context',\n command: 'chatlab messages context --id 1021 --window 10 --format agent',\n },\n {\n task: 'Conversation between me and a member last month',\n command: 'chatlab messages between --member me --member 小红 --last 30d --format agent',\n },\n { task: 'Most active members', command: 'chatlab stats activity --top 10 --format json' },\n { task: 'Monthly hot topics', command: 'chatlab topics list --since 2026-06-01 --until 2026-06-30 --format agent' },\n { task: 'High-frequency words', command: 'chatlab stats keywords --last 30d --top 20 --format json' },\n {\n task: 'Structural scouting without content',\n command: 'chatlab messages list --last 7d --no-content --fields id,senderName,time --format json',\n },\n { task: 'Discover sessions first', command: 'chatlab sessions list --format json' },\n]\n\nconst NOTES: string[] = [\n 'Always pass --format explicitly: agent for message bodies, json for structural data.',\n 'stdout carries exactly one JSON envelope in agent/json mode; logs go to stderr.',\n 'Success envelope: { ok, command, data, meta }; failure: { ok: false, command, error }.',\n 'Pagination: follow meta.nextCursor with --cursor; cursors are bound to the exact query.',\n 'Single-message [#id] and [#id*] markers are usable with `messages context --id`; merged [#a-b] ranges are display-only.',\n 'Privacy preprocessing (desensitize/blacklist) is always on by default.',\n]\n\ninterface CommandLike {\n name(): string\n description(): string\n commands: readonly CommandLike[]\n options: ReadonlyArray<{ flags: string; description: string; defaultValue?: unknown; hidden?: boolean }>\n registeredArguments?: ReadonlyArray<{\n name(): string\n required: boolean\n variadic: boolean\n description?: string\n }>\n _hidden?: boolean\n}\n\nfunction isHidden(cmd: CommandLike): boolean {\n return cmd._hidden === true\n}\n\nfunction serializeCommand(cmd: CommandLike, prefix: string): ManifestCommand {\n const args: ManifestArg[] = (cmd.registeredArguments ?? []).map((arg) => ({\n name: arg.name(),\n required: arg.required,\n variadic: arg.variadic,\n ...(arg.description ? { description: arg.description } : {}),\n }))\n const options: ManifestOption[] = cmd.options\n .filter((opt) => !opt.hidden)\n .map((opt) => ({\n flags: opt.flags,\n description: opt.description,\n ...(opt.defaultValue !== undefined ? { default: opt.defaultValue } : {}),\n }))\n return {\n name: prefix ? `${prefix} ${cmd.name()}` : cmd.name(),\n description: cmd.description(),\n args,\n options,\n }\n}\n\nfunction collectQueryCommands(cmd: CommandLike, prefix: string, into: ManifestCommand[]): void {\n if (cmd.commands.length === 0) {\n into.push(serializeCommand(cmd, prefix))\n return\n }\n const path = prefix ? `${prefix} ${cmd.name()}` : cmd.name()\n for (const sub of cmd.commands) {\n if (isHidden(sub)) continue\n collectQueryCommands(sub, path, into)\n }\n}\n\nexport function buildManifest(program: Command, version: string): Manifest {\n const commands: ManifestCommand[] = []\n const appCommands: Array<{ name: string; description: string }> = []\n\n for (const cmd of program.commands as unknown as CommandLike[]) {\n if (isHidden(cmd)) continue\n if (QUERY_GROUPS.has(cmd.name())) {\n collectQueryCommands(cmd, '', commands)\n } else {\n appCommands.push({ name: cmd.name(), description: cmd.description() })\n }\n }\n\n return {\n name: 'chatlab',\n version,\n apiVersion: API_VERSION,\n formats: ['agent', 'json', 'text'],\n commands,\n appCommands,\n exitCodes: EXIT_CODES,\n examples: EXAMPLES,\n notes: NOTES,\n }\n}\n\nexport function registerManifestCommand(program: Command, version: string): void {\n program\n .command('manifest')\n .description('Machine-readable command manifest for AI agents (JSON)')\n .action(() => {\n console.log(JSON.stringify(buildManifest(program, version), null, 2))\n })\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAMA,YAAYA,SAAQ;AACpB,SAAS,gBAAgB;AACzB,SAAS,eAAe;;;ACJxB,YAAY,SAAS;AAMd,SAAS,gBAAgB,MAAc,MAAgC;AAC5E,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAM,SAAa,iBAAa;AAChC,WAAO,KAAK,SAAS,CAAC,QAA+B;AACnD,UAAI,IAAI,SAAS,cAAc;AAC7B,gBAAQ,KAAK;AAAA,MACf,OAAO;AACL,eAAO,GAAG;AAAA,MACZ;AAAA,IACF,CAAC;AACD,WAAO,KAAK,aAAa,MAAM,OAAO,MAAM,MAAM,QAAQ,IAAI,CAAC,CAAC;AAChE,WAAO,OAAO,MAAM,IAAI;AAAA,EAC1B,CAAC;AACH;AAKO,SAAS,qBAAqB,MAAsB;AACzD,SAAO;AAAA,IACL;AAAA,IACA,wBAAmB,IAAI;AAAA,IACvB;AAAA,IACA;AAAA,IACA;AAAA,IACA,6CAAwC,IAAI;AAAA,IAC5C;AAAA,EACF,EAAE,KAAK,IAAI;AACb;;;ACjCA,YAAY,QAAQ;AAgBb,SAAS,uBAA2C;AACzD,MAAI,QAAQ,SAAS,SAAU,QAAO;AACtC,QAAM,aAAa,eAAe,4BAA4B;AAC9D,MAAO,cAAW,UAAU,EAAG,QAAO;AACtC,SAAO;AACT;AAEO,SAAS,cAAc;AAC5B,MAAI,SAAS,WAAW;AACxB,QAAM,mBAAmB,yCAAyC;AAClE,MAAI,CAAC,iBAAiB,SAAS;AAC7B,QAAI,iBAAiB,SAAS;AAE5B,cAAQ,MAAM,wDAAwD;AACtE,eAAS,WAAW;AAAA,IACtB,OAAO;AACL,cAAQ,MAAM,wDAAwD,iBAAiB,KAAK;AAAA,IAC9F;AAAA,EACF;AACA,QAAM,cAAc,OAAO,KAAK,iBAAiB;AACjD,QAAM,eAAe,IAAI,iBAAiB,WAAW;AACrD,eAAa,cAAc;AAC3B,QAAM,UAAU,2BAA2B,cAAc,KAAK;AAE9D,MAAI,8BAA8B,KAAK,CAAC,kBAAkB,aAAa,eAAe,CAAC,GAAG;AACxF,2BAAuB;AACvB,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,gBAAgB,qBAAqB;AAC3C,QAAM,YAAY,IAAI,gBAAgB,cAAc,EAAE,eAAe,QAAQ,CAAC;AAC9E,SAAO,EAAE,QAAQ,cAAc,UAAU;AAC3C;AAEA,SAAS,yBAA+B;AACtC,UAAQ,MAAM,OAAO,IAAI,OAAO,EAAE,CAAC;AACnC,UAAQ,MAAM,4CAA4C;AAC1D,UAAQ,MAAM,IAAI,OAAO,EAAE,CAAC;AAC5B,UAAQ,MAAM,EAAE;AAChB,UAAQ,MAAM,oEAAoE;AAClF,UAAQ,MAAM,6CAA6C;AAC3D,UAAQ,MAAM,EAAE;AAChB,UAAQ,MAAM,0EAA0E;AACxF,UAAQ,MAAM,EAAE;AAChB,UAAQ,MAAM,+BAA+B;AAC7C,UAAQ,MAAM,EAAE;AAChB,UAAQ,MAAM,qEAAgE;AAC9E,UAAQ,MAAM,uCAAuC;AACrD,UAAQ,MAAM,mDAAmD;AACjE,UAAQ,MAAM,mCAAmC;AACjD,UAAQ,MAAM,aAAa;AAC3B,UAAQ,MAAM,2CAA2C;AACzD,UAAQ,MAAM,EAAE;AAChB,UAAQ,MAAM,IAAI,OAAO,EAAE,IAAI,IAAI;AACrC;;;AChEO,IAAM,cAAc;AASpB,IAAM,aAAN,cAAyB,MAAM;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EAET,YAAY,OAAwB;AAClC,UAAM,MAAM,OAAO;AACnB,SAAK,OAAO;AACZ,SAAK,OAAO,MAAM;AAClB,SAAK,OAAO,MAAM;AAClB,SAAK,aAAa,MAAM;AAAA,EAC1B;AACF;AAgBO,SAAS,gBAAgB,SAAiB,MAAe,MAAiD;AAC/G,SAAO;AAAA,IACL,IAAI;AAAA,IACJ;AAAA,IACA;AAAA,IACA,MAAM,EAAE,GAAI,QAAQ,CAAC,GAAI,YAAY,YAAY;AAAA,EACnD;AACF;AAEO,SAAS,cACd,SACA,OACA,MACe;AACf,QAAM,QAAyB;AAAA,IAC7B,MAAM,MAAM;AAAA,IACZ,SAAS,MAAM;AAAA,IACf,GAAI,MAAM,SAAS,SAAY,EAAE,MAAM,MAAM,KAAK,IAAI,CAAC;AAAA,IACvD,GAAI,MAAM,eAAe,SAAY,EAAE,YAAY,MAAM,WAAW,IAAI,CAAC;AAAA,EAC3E;AACA,SAAO;AAAA,IACL,IAAI;AAAA,IACJ;AAAA,IACA,OAAO;AAAA,IACP,GAAI,SAAS,SAAY,EAAE,KAAK,IAAI,CAAC;AAAA,EACvC;AACF;AAMO,SAAS,iBAAiB,MAAsB;AACrD,MAAI,SAAS,YAAa,QAAO;AACjC,MAAI,KAAK,SAAS,YAAY,EAAG,QAAO;AACxC,MAAI,KAAK,SAAS,YAAY,EAAG,QAAO;AACxC,MAAI,SAAS,sBAAsB,SAAS,oBAAoB,KAAK,SAAS,WAAW,EAAG,QAAO;AACnG,SAAO;AACT;;;ACzEO,SAAS,cAAc,UAAiC;AAC7D,MAAI,aAAa,QAAW;AAC1B,QAAI,aAAa,WAAW,aAAa,UAAU,aAAa,OAAQ,QAAO;AAC/E,UAAM,IAAI,WAAW;AAAA,MACnB,MAAM;AAAA,MACN,SAAS,mBAAmB,QAAQ;AAAA,MACpC,MAAM;AAAA,IACR,CAAC;AAAA,EACH;AACA,SAAO,QAAQ,OAAO,QAAQ,SAAS;AACzC;AAaA,eAAsB,SACpB,SACA,SACA,SACe;AACf,MAAI;AACJ,MAAI;AACF,aAAS,cAAc,QAAQ,MAAM;AAAA,EACvC,SAAS,KAAK;AACZ,cAAU,SAAS,SAAS,GAAG;AAC/B;AAAA,EACF;AAEA,MAAI;AACF,UAAM,SAAS,MAAM,QAAQ,MAAM;AACnC,QAAI,WAAW,QAAQ;AACrB,cAAQ,IAAI,OAAO,aAAa,OAAO,WAAW,IAAI,KAAK,UAAU,OAAO,MAAM,MAAM,CAAC,CAAC;AAAA,IAC5F,OAAO;AACL,cAAQ,IAAI,KAAK,UAAU,gBAAgB,SAAS,OAAO,MAAM,OAAO,IAAI,CAAC,CAAC;AAAA,IAChF;AAAA,EACF,SAAS,KAAK;AACZ,cAAU,SAAS,QAAQ,GAAG;AAAA,EAChC;AACF;AAEA,SAAS,UAAU,SAAiB,QAAsB,KAAoB;AAC5E,QAAM,aACJ,eAAe,aACX,MACA,IAAI,WAAW;AAAA,IACb,MAAM;AAAA,IACN,SAAS,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,EAC1D,CAAC;AAEP,MAAI,WAAW,QAAQ;AACrB,YAAQ,MAAM,UAAU,WAAW,OAAO,GAAG,WAAW,OAAO;AAAA,IAAO,WAAW,IAAI,KAAK,EAAE,EAAE;AAC9F,QAAI,WAAW,YAAY;AACzB,iBAAW,aAAa,WAAW,YAAY;AAC7C,gBAAQ,MAAM,OAAO,KAAK,UAAU,SAAS,CAAC,EAAE;AAAA,MAClD;AAAA,IACF;AAAA,EACF,OAAO;AACL,YAAQ,IAAI,KAAK,UAAU,cAAc,SAAS,UAAU,CAAC,CAAC;AAAA,EAChE;AACA,UAAQ,WAAW,iBAAiB,WAAW,IAAI;AACrD;;;ACzEA,YAAY,UAAU;;;ACGtB,IAAM,uBAAuB,CAAC,SAAS,SAAS,SAAS,OAAO;AAEhE,IAAM,mBAA2C;AAAA,EAC/C,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AACN;AAEA,SAAS,gBAAgB,QAAwB;AAC/C,MAAI,qBAAqB,SAAS,MAAM,EAAG,QAAO;AAClD,QAAM,UAAU,OAAO,MAAM,MAAM,EAAE,CAAC,EAAE,YAAY;AACpD,SAAO,iBAAiB,OAAO,KAAK;AACtC;AAEA,SAAS,qBAA6B;AACpC,MAAI;AACF,UAAM,WAAW,KAAK,eAAe,EAAE,gBAAgB,EAAE;AACzD,QAAI,SAAU,QAAO;AAAA,EACvB,QAAQ;AAAA,EAER;AACA,QAAM,OAAO,QAAQ,IAAI,QAAQ;AACjC,SAAO,KAAK,MAAM,GAAG,EAAE,CAAC,KAAK;AAC/B;AAMO,SAAS,iBAAiB,YAAqB,cAA+B;AACnF,MAAI,WAAY,QAAO,gBAAgB,UAAU;AACjD,QAAM,SAAS,iBAAiB,SAAY,eAAe,mBAAmB;AAC9E,MAAI,OAAQ,QAAO,gBAAgB,MAAM;AACzC,SAAO;AACT;AAQO,SAAS,wBAAwB,WAAmB,QAAkC;AAC3F,QAAM,cAAc,IAAI,mBAAmB,SAAS,EAAE,KAAK;AAC3D,QAAM,SAAS,YAAY;AAC3B,SAAO;AAAA,IACL,GAAG;AAAA,IACH,kBAAkB;AAAA,MAChB,OAAO;AAAA,MACP;AAAA,MACA,OAAO,mCAAmC,CAAC;AAAA,IAC7C;AAAA,EACF;AACF;;;ACrCA,SAAS,sBAAsB,QAA0E;AACvG,QAAM,SAA4D,CAAC;AACnE,aAAW,MAAM,OAAO,eAAe,GAAG;AACxC,UAAM,KAAK,OAAO,KAAK,EAAE;AACzB,QAAI,CAAC,GAAI;AACT,UAAM,OAAO,eAAe,EAAE;AAC9B,QAAI,CAAC,KAAM;AACX,WAAO,KAAK,EAAE,IAAI,MAAM,KAAK,MAAM,UAAU,KAAK,UAAU,GAAG,CAAC;AAAA,EAClE;AACA,SAAO;AACT;AAMO,SAAS,eAAe,QAAuB,KAA+B;AACnF,MAAI,KAAK;AACP,UAAM,SAAS,OAAO,KAAK,GAAG;AAC9B,QAAI,UAAU,eAAe,MAAM,GAAG;AACpC,YAAM,OAAO,eAAe,MAAM;AAClC,aAAO,EAAE,IAAI,KAAK,MAAM,KAAK,MAAM,IAAI,OAAO;AAAA,IAChD;AAAA,EACF;AAEA,QAAM,WAAW,sBAAsB,MAAM;AAE7C,MAAI,CAAC,KAAK;AACR,QAAI,SAAS,WAAW,GAAG;AACzB,aAAO,EAAE,IAAI,SAAS,CAAC,EAAE,IAAI,MAAM,SAAS,CAAC,EAAE,MAAM,IAAI,SAAS,CAAC,EAAE,GAAG;AAAA,IAC1E;AACA,QAAI,SAAS,WAAW,GAAG;AACzB,YAAM,IAAI,WAAW;AAAA,QACnB,MAAM;AAAA,QACN,SAAS;AAAA,QACT,MAAM;AAAA,MACR,CAAC;AAAA,IACH;AACA,UAAM,IAAI,WAAW;AAAA,MACnB,MAAM;AAAA,MACN,SAAS,GAAG,SAAS,MAAM;AAAA,MAC3B,MAAM;AAAA,MACN,YAAY,SAAS,IAAI,CAAC,EAAE,IAAI,MAAM,SAAS,OAAO,EAAE,IAAI,MAAM,SAAS,EAAE;AAAA,IAC/E,CAAC;AAAA,EACH;AAEA,QAAM,cAAc,SAAS,OAAO,CAAC,MAAM,EAAE,SAAS,GAAG;AACzD,MAAI,YAAY,WAAW,GAAG;AAC5B,WAAO,EAAE,IAAI,YAAY,CAAC,EAAE,IAAI,MAAM,YAAY,CAAC,EAAE,MAAM,IAAI,YAAY,CAAC,EAAE,GAAG;AAAA,EACnF;AACA,MAAI,YAAY,SAAS,GAAG;AAC1B,UAAM,IAAI,WAAW;AAAA,MACnB,MAAM;AAAA,MACN,SAAS,iBAAiB,GAAG,aAAa,YAAY,MAAM;AAAA,MAC5D,MAAM;AAAA,MACN,YAAY,YAAY,IAAI,CAAC,EAAE,IAAI,MAAM,SAAS,OAAO,EAAE,IAAI,MAAM,SAAS,EAAE;AAAA,IAClF,CAAC;AAAA,EACH;AAEA,QAAM,UAAU,SAAS,OAAO,CAAC,MAAM,EAAE,KAAK,SAAS,GAAG,CAAC;AAC3D,MAAI,QAAQ,SAAS,GAAG;AACtB,UAAM,IAAI,WAAW;AAAA,MACnB,MAAM;AAAA,MACN,SAAS,YAAY,GAAG,gBAAgB,QAAQ,MAAM;AAAA,MACtD,MAAM;AAAA,MACN,YAAY,QAAQ,IAAI,CAAC,EAAE,IAAI,MAAM,SAAS,OAAO,EAAE,IAAI,MAAM,SAAS,EAAE;AAAA,IAC9E,CAAC;AAAA,EACH;AAEA,QAAM,IAAI,WAAW;AAAA,IACnB,MAAM;AAAA,IACN,SAAS,YAAY,GAAG;AAAA,IACxB,MAAM;AAAA,EACR,CAAC;AACH;AAWO,SAAS,cAAc,IAAqB,KAA6B;AAC9E,MAAI,QAAQ,MAAM;AAChB,UAAM,QAAQ,mBAAmB,EAAE;AACnC,QAAI,CAAC,OAAO;AACV,YAAM,IAAI,WAAW;AAAA,QACnB,MAAM;AAAA,QACN,SAAS;AAAA,QACT,MAAM;AAAA,MACR,CAAC;AAAA,IACH;AACA,WAAO,EAAE,IAAI,MAAM,IAAI,MAAM,MAAM,KAAK;AAAA,EAC1C;AAEA,QAAM,UAAU,sBAAsB,EAAE;AACxC,QAAM,cAAc,CAAC,MAAgC,EAAE,iBAAiB,EAAE,eAAe,EAAE;AAE3F,MAAI,QAAQ,KAAK,GAAG,GAAG;AACrB,UAAM,OAAO,QAAQ,KAAK,CAAC,MAAM,EAAE,OAAO,OAAO,GAAG,CAAC;AACrD,QAAI,KAAM,QAAO,EAAE,IAAI,KAAK,IAAI,MAAM,YAAY,IAAI,EAAE;AACxD,UAAM,IAAI,WAAW;AAAA,MACnB,MAAM;AAAA,MACN,SAAS,aAAa,GAAG;AAAA,MACzB,MAAM;AAAA,IACR,CAAC;AAAA,EACH;AAEA,QAAM,QAAQ,QAAQ;AAAA,IACpB,CAAC,MAAM,EAAE,kBAAkB,OAAO,EAAE,gBAAgB,OAAO,EAAE,QAAQ,SAAS,GAAG,KAAK,EAAE,eAAe;AAAA,EACzG;AACA,MAAI,MAAM,WAAW,GAAG;AACtB,WAAO,EAAE,IAAI,MAAM,CAAC,EAAE,IAAI,MAAM,YAAY,MAAM,CAAC,CAAC,EAAE;AAAA,EACxD;AAEA,QAAM,OAAO,MAAM,SAAS,IAAI,QAAQ,QAAQ,OAAO,CAAC,MAAM,YAAY,CAAC,EAAE,SAAS,GAAG,CAAC;AAC1F,MAAI,KAAK,SAAS,GAAG;AACnB,UAAM,IAAI,WAAW;AAAA,MACnB,MAAM;AAAA,MACN,SAAS,gBAAgB,GAAG,aAAa,KAAK,MAAM;AAAA,MACpD,MAAM;AAAA,MACN,YAAY,KAAK,MAAM,GAAG,EAAE,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,MAAM,YAAY,CAAC,GAAG,UAAU,EAAE,aAAa,EAAE;AAAA,IACzG,CAAC;AAAA,EACH;AAEA,QAAM,IAAI,WAAW;AAAA,IACnB,MAAM;AAAA,IACN,SAAS,WAAW,GAAG;AAAA,IACvB,MAAM;AAAA,EACR,CAAC;AACH;;;AF/HO,SAAS,mBAAmB,SAA6C;AAC9E,QAAM,EAAE,QAAQ,cAAc,UAAU,IAAI,YAAY;AACxD,QAAM,WAAW,eAAe,WAAW,QAAQ,OAAO;AAE1D,QAAM,SAAS,iBAAiB,OAAO,OAAO,IAAI;AAClD,QAAM,mBAAmB,wBAAwB,aAAa,aAAa,GAAG,MAAM;AACpF,QAAM,kBAAkB,eAAe,SAAS,EAAE,GAAG,WAAW;AAEhE,aAAgB,UAAK,aAAa,aAAa,GAAG,KAAK,CAAC;AAExD,SAAO;AAAA,IACL,IAAI,SAAS;AAAA,IACb,SAAS,EAAE,IAAI,SAAS,IAAI,MAAM,SAAS,KAAK;AAAA,IAChD;AAAA,IACA;AAAA,IACA;AAAA,IACA,UAAU,OAAO,IAAI;AAAA,IACrB,UAAU,OAAO,IAAI;AAAA,IACrB,OAAO,MAAM,UAAU,SAAS;AAAA,EAClC;AACF;AAGO,SAAS,iBAAiB,KAAqC,SAAkC;AACtG,MAAI,QAAQ,OAAO,CAAC,IAAI,UAAU;AAChC,UAAM,IAAI,WAAW;AAAA,MACnB,MAAM;AAAA,MACN,SAAS;AAAA,MACT,MAAM;AAAA,IACR,CAAC;AAAA,EACH;AACF;;;AGxDA,SAAS,kBAAkB;AAqBpB,SAAS,gBAAgB,MAAoB;AAClD,QAAM,MAAM,CAAC,GAAW,IAAI,MAAM,OAAO,KAAK,IAAI,CAAC,CAAC,EAAE,SAAS,GAAG,GAAG;AACrE,QAAM,YAAY,CAAC,KAAK,kBAAkB;AAC1C,QAAM,OAAO,aAAa,IAAI,MAAM;AACpC,QAAM,OAAO,IAAI,KAAK,MAAM,KAAK,IAAI,SAAS,IAAI,EAAE,CAAC;AACrD,QAAM,OAAO,IAAI,KAAK,IAAI,SAAS,IAAI,EAAE;AACzC,SACE,GAAG,KAAK,YAAY,CAAC,IAAI,IAAI,KAAK,SAAS,IAAI,CAAC,CAAC,IAAI,IAAI,KAAK,QAAQ,CAAC,CAAC,IACpE,IAAI,KAAK,SAAS,CAAC,CAAC,IAAI,IAAI,KAAK,WAAW,CAAC,CAAC,IAAI,IAAI,KAAK,WAAW,CAAC,CAAC,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI;AAEtG;AAGO,SAAS,WAAW,IAAoB;AAC7C,SAAO,gBAAgB,IAAI,KAAK,KAAK,GAAI,CAAC;AAC5C;AAEA,IAAM,YAAY;AAClB,IAAM,YAAY;AAClB,IAAM,kBAAkB;AACxB,IAAM,gBAAgB;AAEtB,SAAS,WAAW,MAAY,YAAY,GAAS;AACnD,SAAO,IAAI,KAAK,KAAK,YAAY,GAAG,KAAK,SAAS,GAAG,KAAK,QAAQ,IAAI,SAAS;AACjF;AAEA,SAAS,YAAY,MAAc,OAA2B;AAC5D,SAAO,IAAI,WAAW;AAAA,IACpB,MAAM;AAAA,IACN,SAAS,WAAW,IAAI,WAAW,KAAK;AAAA,IACxC,MAAM;AAAA,EACR,CAAC;AACH;AAEA,SAAS,gBAAgB,MAAY,GAAW,GAAW,GAAoB;AAC7E,SAAO,KAAK,YAAY,MAAM,KAAK,KAAK,SAAS,MAAM,IAAI,KAAK,KAAK,QAAQ,MAAM;AACrF;AAEA,SAAS,oBAAoB,MAAY,GAAW,GAAW,GAAW,GAAW,KAAa,GAAoB;AACpH,SAAO,gBAAgB,MAAM,GAAG,GAAG,CAAC,KAAK,KAAK,SAAS,MAAM,KAAK,KAAK,WAAW,MAAM,OAAO,KAAK,WAAW,MAAM;AACvH;AAMA,SAAS,eAAe,OAAe,UAA2B,MAAc,KAAiB;AAC/F,MAAI,UAAU,WAAW,UAAU,aAAa;AAC9C,UAAM,YAAY,UAAU,cAAc,KAAK;AAC/C,QAAI,aAAa,QAAS,QAAO,WAAW,KAAK,SAAS;AAC1D,WAAO,IAAI,KAAK,WAAW,KAAK,YAAY,CAAC,EAAE,QAAQ,IAAI,GAAI;AAAA,EACjE;AAEA,QAAM,WAAW,UAAU,KAAK,KAAK;AACrC,MAAI,UAAU;AACZ,UAAM,CAAC,EAAE,GAAG,GAAG,CAAC,IAAI;AACpB,UAAM,OAAO,OAAO,CAAC;AACrB,UAAM,QAAQ,OAAO,CAAC;AACtB,UAAM,MAAM,OAAO,CAAC;AACpB,UAAM,OAAO,IAAI,KAAK,MAAM,QAAQ,GAAG,GAAG;AAC1C,QAAI,OAAO,MAAM,KAAK,QAAQ,CAAC,KAAK,CAAC,gBAAgB,MAAM,MAAM,OAAO,GAAG,EAAG,OAAM,YAAY,MAAM,KAAK;AAC3G,QAAI,aAAa,QAAS,QAAO;AACjC,WAAO,IAAI,KAAK,IAAI,KAAK,MAAM,QAAQ,GAAG,MAAM,CAAC,EAAE,QAAQ,IAAI,GAAI;AAAA,EACrE;AAEA,QAAM,WAAW,UAAU,KAAK,KAAK;AACrC,MAAI,UAAU;AACZ,UAAM,CAAC,EAAE,GAAG,GAAG,GAAG,GAAG,KAAK,CAAC,IAAI;AAC/B,UAAM,OAAO,OAAO,CAAC;AACrB,UAAM,QAAQ,OAAO,CAAC;AACtB,UAAM,MAAM,OAAO,CAAC;AACpB,UAAM,OAAO,OAAO,CAAC;AACrB,UAAM,SAAS,OAAO,GAAG;AACzB,UAAM,SAAS,OAAO,KAAK,CAAC;AAC5B,UAAMC,UAAS,IAAI,KAAK,MAAM,QAAQ,GAAG,KAAK,MAAM,QAAQ,MAAM;AAClE,QAAI,OAAO,MAAMA,QAAO,QAAQ,CAAC,KAAK,CAAC,oBAAoBA,SAAQ,MAAM,OAAO,KAAK,MAAM,QAAQ,MAAM,GAAG;AAC1G,YAAM,YAAY,MAAM,KAAK;AAAA,IAC/B;AACA,WAAOA;AAAA,EACT;AAGA,QAAM,SAAS,IAAI,KAAK,KAAK;AAC7B,MAAI,CAAC,OAAO,MAAM,OAAO,QAAQ,CAAC,KAAK,qBAAqB,KAAK,KAAK,GAAG;AACvE,UAAM,UAAU,gBAAgB,KAAK,KAAK;AAC1C,QAAI,SAAS;AACX,YAAM,CAAC,EAAE,GAAG,GAAG,CAAC,IAAI;AACpB,YAAM,OAAO,OAAO,CAAC;AACrB,YAAM,QAAQ,OAAO,CAAC;AACtB,YAAM,MAAM,OAAO,CAAC;AACpB,YAAM,YAAY,IAAI,KAAK,MAAM,QAAQ,GAAG,GAAG;AAC/C,UAAI,CAAC,gBAAgB,WAAW,MAAM,OAAO,GAAG,EAAG,OAAM,YAAY,MAAM,KAAK;AAAA,IAClF;AACA,WAAO;AAAA,EACT;AAEA,QAAM,YAAY,MAAM,KAAK;AAC/B;AAGO,SAAS,iBAAiB,SAAyB,MAAM,oBAAI,KAAK,GAAsB;AAC7F,QAAM,EAAE,OAAO,OAAO,KAAK,IAAI;AAE/B,MAAI,SAAS,WAAc,UAAU,UAAa,UAAU,SAAY;AACtE,UAAM,IAAI,WAAW;AAAA,MACnB,MAAM;AAAA,MACN,SAAS;AAAA,MACT,MAAM;AAAA,IACR,CAAC;AAAA,EACH;AAEA,MAAI,SAAS,QAAW;AACtB,UAAM,QAAQ,cAAc,KAAK,IAAI;AACrC,QAAI,CAAC,OAAO;AACV,YAAM,IAAI,WAAW;AAAA,QACnB,MAAM;AAAA,QACN,SAAS,yBAAyB,IAAI;AAAA,QACtC,MAAM;AAAA,MACR,CAAC;AAAA,IACH;AACA,UAAM,IAAI,OAAO,MAAM,CAAC,CAAC;AACzB,UAAM,cAAc,MAAM,CAAC,MAAM,MAAM,OAAO,MAAM,CAAC,MAAM,MAAM,QAAQ;AACzE,UAAM,QAAQ,IAAI,KAAK,IAAI,QAAQ,IAAI,IAAI,cAAc,GAAI;AAC7D,WAAO;AAAA,MACL,SAAS,KAAK,MAAM,MAAM,QAAQ,IAAI,GAAI;AAAA,MAC1C,MAAM,EAAE,OAAO,gBAAgB,KAAK,GAAG,OAAO,gBAAgB,GAAG,EAAE;AAAA,IACrE;AAAA,EACF;AAEA,MAAI;AACJ,MAAI;AACJ,MAAI,WAA0B;AAE9B,MAAI,UAAU,QAAW;AACvB,UAAM,SAAS,eAAe,OAAO,SAAS,WAAW,GAAG;AAC5D,cAAU,KAAK,MAAM,OAAO,QAAQ,IAAI,GAAI;AAC5C,eAAW,gBAAgB,MAAM;AAAA,EACnC;AACA,MAAI,UAAU,QAAW;AACvB,UAAM,SAAS,eAAe,OAAO,OAAO,WAAW,GAAG;AAC1D,YAAQ,KAAK,MAAM,OAAO,QAAQ,IAAI,GAAI;AAAA,EAC5C;AAEA,MAAI,YAAY,UAAa,UAAU,UAAa,UAAU,OAAO;AACnE,UAAM,IAAI,WAAW;AAAA,MACnB,MAAM;AAAA,MACN,SAAS;AAAA,MACT,MAAM;AAAA,IACR,CAAC;AAAA,EACH;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,MAAM;AAAA,MACJ,OAAO;AAAA,MACP,OAAO,UAAU,SAAY,WAAW,KAAK,IAAI,gBAAgB,GAAG;AAAA,IACtE;AAAA,EACF;AACF;AAGO,SAAS,WACd,OACA,cACA,KACA,MACA,MAAM,GACE;AACR,MAAI,UAAU,OAAW,QAAO,KAAK,IAAI,cAAc,GAAG;AAC1D,QAAM,IAAI,OAAO,UAAU,WAAW,QAAQ,OAAO,KAAK;AAC1D,MAAI,CAAC,OAAO,UAAU,CAAC,KAAK,IAAI,KAAK;AACnC,UAAM,IAAI,WAAW;AAAA,MACnB,MAAM;AAAA,MACN,SAAS,WAAW,IAAI,WAAW,KAAK;AAAA,MACxC,MACE,MAAM,IACF,GAAG,IAAI,4BAA4B,GAAG,OAAO,GAAG,KAChD,GAAG,IAAI,wCAAwC,GAAG;AAAA,IAC1D,CAAC;AAAA,EACH;AACA,SAAO,KAAK,IAAI,GAAG,GAAG;AACxB;AAKO,SAAS,iBAAiB,YAA6C;AAC5E,QAAM,YAAY,KAAK,UAAU,YAAY,OAAO,KAAK,UAAU,EAAE,KAAK,CAAC;AAC3E,SAAO,WAAW,QAAQ,EAAE,OAAO,SAAS,EAAE,OAAO,KAAK,EAAE,MAAM,GAAG,EAAE;AACzE;AAEO,SAAS,yBAAyB,MAA4C;AACnF,MAAI,KAAK,UAAU,UAAa,KAAK,KAAK,UAAU,KAAM,QAAO;AACjE,QAAM,QAAQ,IAAI,KAAK,KAAK,KAAK,KAAK,EAAE,QAAQ;AAChD,MAAI,OAAO,MAAM,KAAK,EAAG,QAAO;AAChC,SAAO,EAAE,GAAG,MAAM,OAAO,KAAK,MAAM,QAAQ,GAAI,EAAE;AACpD;AAEO,SAAS,4BACd,SACA,MAAM,oBAAI,KAAK,GACI;AACnB,QAAM,WAAW,QAAQ,SAAS,kBAAkB,QAAQ,MAAM,IAAI;AACtE,SAAO,UAAU,QAAQ,yBAAyB,iBAAiB,SAAS,GAAG,CAAC;AAClF;AAEO,SAAS,aAAa,QAAgB,aAAqB,UAAmC;AACnG,MAAI,UAAU,MAAM;AAClB,WAAO,OAAO,KAAK,KAAK,UAAU,EAAE,GAAG,GAAG,IAAI,aAAa,QAAQ,MAAM,SAAS,KAAK,CAAC,GAAG,MAAM,EAAE;AAAA,MACjG;AAAA,IACF;AAAA,EACF;AACA,SAAO,OAAO,KAAK,GAAG,WAAW,IAAI,MAAM,IAAI,MAAM,EAAE,SAAS,WAAW;AAC7E;AAEA,SAAS,SAAS,OAAgD;AAChE,SAAO,UAAU,QAAQ,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK,IACrE,QACD;AACN;AAEA,SAAS,qBACP,OAC2E;AAC3E,MAAI,UAAU;AACd,MAAI;AACF,cAAU,OAAO,KAAK,OAAO,WAAW,EAAE,SAAS,MAAM;AAAA,EAC3D,QAAQ;AACN,WAAO;AAAA,EACT;AAEA,MAAI;AACJ,MAAI;AACF,cAAU,KAAK,MAAM,OAAO;AAAA,EAC9B,QAAQ;AACN,WAAO;AAAA,EACT;AAEA,QAAM,SAAS,SAAS,OAAO;AAC/B,MACE,CAAC,UACD,OAAO,MAAM,KACb,OAAO,OAAO,OAAO,YACrB,OAAO,OAAO,WAAW,YACzB,CAAC,OAAO,UAAU,OAAO,MAAM,KAC/B,OAAO,SAAS,GAChB;AACA,WAAO;AAAA,EACT;AAEA,QAAM,WAAW,qBAAqB,MAAM;AAC5C,SAAO,EAAE,aAAa,OAAO,IAAI,QAAQ,OAAO,QAAQ,GAAI,WAAW,EAAE,SAAS,IAAI,CAAC,EAAG;AAC5F;AAEA,SAAS,qBAAqB,SAA8D;AAC1F,QAAM,aAAa,SAAS,QAAQ,IAAI;AACxC,MAAI,CAAC,WAAY,QAAO;AACxB,QAAM,aAAa,SAAS,WAAW,IAAI;AAC3C,MAAI,CAAC,WAAY,QAAO;AAExB,QAAM,UAAU,WAAW;AAC3B,QAAM,QAAQ,WAAW;AACzB,QAAM,QAAQ,WAAW;AACzB,QAAM,QAAQ,WAAW;AACzB,MAAI,YAAY,UAAa,OAAO,YAAY,SAAU,QAAO;AACjE,MAAI,UAAU,UAAa,OAAO,UAAU,SAAU,QAAO;AAC7D,MAAI,UAAU,QAAQ,OAAO,UAAU,SAAU,QAAO;AACxD,MAAI,UAAU,QAAQ,OAAO,UAAU,SAAU,QAAO;AAExD,SAAO;AAAA,IACL,MAAM;AAAA,MACJ,GAAI,YAAY,SAAY,EAAE,QAAQ,IAAI,CAAC;AAAA,MAC3C,GAAI,UAAU,SAAY,EAAE,MAAM,IAAI,CAAC;AAAA,MACvC,MAAM,EAAE,OAAO,MAAM;AAAA,IACvB;AAAA,EACF;AACF;AAEO,SAAS,kBAAkB,OAA2C;AAC3E,SAAO,qBAAqB,KAAK,GAAG;AACtC;AAGO,SAAS,aAAa,OAAe,aAA6B;AACvE,QAAM,WAAW,qBAAqB,KAAK;AAC3C,MAAI,UAAU;AACZ,QAAI,SAAS,gBAAgB,aAAa;AACxC,YAAM,IAAI,WAAW;AAAA,QACnB,MAAM;AAAA,QACN,SAAS;AAAA,QACT,MAAM;AAAA,MACR,CAAC;AAAA,IACH;AACA,WAAO,SAAS;AAAA,EAClB;AAEA,MAAI,UAAU;AACd,MAAI;AACF,cAAU,OAAO,KAAK,OAAO,WAAW,EAAE,SAAS,MAAM;AAAA,EAC3D,QAAQ;AAAA,EAER;AACA,QAAM,QAAQ,yBAAyB,KAAK,OAAO;AACnD,MAAI,CAAC,SAAS,MAAM,CAAC,MAAM,aAAa;AACtC,UAAM,IAAI,WAAW;AAAA,MACnB,MAAM;AAAA,MACN,SAAS;AAAA,MACT,MAAM;AAAA,IACR,CAAC;AAAA,EACH;AACA,SAAO,OAAO,MAAM,CAAC,CAAC;AACxB;;;ACjUA,SAAS,YAAY,IAAkC;AACrD,SAAO,MAAM,OAAO,OAAO,WAAW,EAAE;AAC1C;AAEO,SAAS,wBAAwBC,UAAwB;AAC9D,QAAM,cAAcA,SACjB,QAAQ,UAAU,EAClB,YAAY,4BAA4B,EACxC,OAAO,qBAAqB,6EAA6E,EACzG,OAAO,CAAC,YAAiC;AAExC,YAAQ,MAAM,sGAAiG;AAC/G,uBAAmB,OAAO;AAAA,EAC5B,CAAC;AAEH,cACG,QAAQ,MAAM,EACd,YAAY,6BAA6B,EACzC,OAAO,gBAAgB,kCAAkC,EACzD,OAAO,kBAAkB,uBAAuB,EAChD,OAAO,qBAAqB,gCAAgC,EAC5D,OAAO,OAAO,YAAoE;AACjF,UAAM,SAAS,iBAAiB,SAAS,YAAY;AACnD,YAAM,EAAE,UAAU,IAAI,YAAY;AAClC,UAAI;AACF,cAAM,QAAQ,UACX,eAAe,EACf,IAAI,CAAC,OAAO;AACX,gBAAM,KAAK,UAAU,KAAK,EAAE;AAC5B,cAAI,CAAC,GAAI,QAAO;AAChB,gBAAM,OAAO,eAAe,EAAE;AAC9B,cAAI,CAAC,KAAM,QAAO;AAClB,gBAAM,WAAW,mBAAmB,EAAE;AACtC,iBAAO;AAAA,YACL;AAAA,YACA,MAAM,KAAK;AAAA,YACX,UAAU,KAAK;AAAA,YACf,MAAM,KAAK;AAAA,YACX,eAAe,SAAS;AAAA,YACxB,cAAc,SAAS;AAAA,YACvB,cAAc,YAAY,SAAS,cAAc;AAAA,YACjD,aAAa,YAAY,SAAS,aAAa;AAAA,UACjD;AAAA,QACF,CAAC,EACA,OAAO,CAAC,MAAkC,MAAM,IAAI,EACpD,OAAO,CAAC,MAAO,QAAQ,QAAQ,EAAE,KAAK,SAAS,QAAQ,KAAK,IAAI,IAAK,EACrE,OAAO,CAAC,MAAO,QAAQ,WAAW,EAAE,aAAa,QAAQ,WAAW,IAAK;AAE5E,eAAO;AAAA,UACL,MAAM,EAAE,MAAM;AAAA,UACd,MAAM,EAAE,UAAU,MAAM,OAAO;AAAA,UAC/B,YAAY,MACV,MACG;AAAA,YACC,CAAC,MACC,GAAG,EAAE,EAAE,KAAK,EAAE,IAAI;AAAA,IAAO,EAAE,QAAQ,MAAM,EAAE,IAAI,MAAM,EAAE,YAAY,cAAc,EAAE,aAAa;AAAA,UACpG,EACC,KAAK,IAAI,KAAK;AAAA,QACrB;AAAA,MACF,UAAE;AACA,kBAAU,SAAS;AAAA,MACrB;AAAA,IACF,CAAC;AAAA,EACH,CAAC;AAEH,cACG,QAAQ,MAAM,EACd,YAAY,4BAA4B,EACxC,OAAO,mBAAmB,2BAA2B,EACrD,OAAO,qBAAqB,gCAAgC,EAC5D,OAAO,OAAO,YAAmD;AAChE,UAAM,SAAS,iBAAiB,SAAS,YAAY;AACnD,YAAM,MAAM,mBAAmB,OAAO;AACtC,UAAI;AACF,cAAM,OAAO,eAAe,IAAI,EAAE;AAClC,cAAM,WAAW,mBAAmB,IAAI,EAAE;AAC1C,cAAM,OAAO;AAAA,UACX,IAAI,IAAI,QAAQ;AAAA,UAChB,MAAM,KAAK;AAAA,UACX,UAAU,KAAK;AAAA,UACf,MAAM,KAAK;AAAA,UACX,eAAe,SAAS;AAAA,UACxB,cAAc,SAAS;AAAA,UACvB,cAAc,YAAY,SAAS,cAAc;AAAA,UACjD,aAAa,YAAY,SAAS,aAAa;AAAA,QACjD;AACA,eAAO;AAAA,UACL;AAAA,UACA,MAAM,EAAE,SAAS,IAAI,QAAQ;AAAA,UAC7B,YAAY,MACV,GAAG,KAAK,IAAI,KAAK,KAAK,EAAE;AAAA,YACX,KAAK,QAAQ,YAAY,KAAK,IAAI;AAAA,YAClC,KAAK,aAAa,eAAe,KAAK,YAAY;AAAA,SACrD,KAAK,gBAAgB,GAAG,MAAM,KAAK,eAAe,GAAG;AAAA,QACnE;AAAA,MACF,UAAE;AACA,YAAI,MAAM;AAAA,MACZ;AAAA,IACF,CAAC;AAAA,EACH,CAAC;AACL;AAEO,SAAS,uBAAuBA,UAAwB;AAC7D,QAAM,aAAaA,SAChB,QAAQ,SAAS,EACjB,YAAY,iCAAiC,EAC7C,SAAS,gBAAgB,uCAAuC,EAChE,OAAO,qBAAqB,eAAe,EAC3C,OAAO,CAAC,WAA+B,SAA8B,YAAqB;AACzF,QAAI,CAAC,WAAW;AACd,cAAQ,KAAK,EAAE,OAAO,MAAM,CAAC;AAC7B;AAAA,IACF;AACA,YAAQ;AAAA,MACN;AAAA,IACF;AACA,sBAAkB,WAAW,OAAO;AAAA,EACtC,CAAC;AAEH,aACG,QAAQ,MAAM,EACd,YAAY,sBAAsB,EAClC,OAAO,mBAAmB,2BAA2B,EACrD,OAAO,gBAAgB,iCAAiC,EACxD,OAAO,eAAe,0BAA0B,UAAU,EAC1D,OAAO,eAAe,oCAAoC,EAC1D,OAAO,qBAAqB,gCAAgC,EAC5D,OAAO,OAAO,YAAkG;AAC/G,UAAM,SAAS,gBAAgB,SAAS,YAAY;AAClD,YAAM,MAAM,mBAAmB,OAAO;AACtC,UAAI;AACF,cAAM,QAAQ,WAAW,QAAQ,OAAO,IAAI,KAAK,SAAS;AAC1D,YAAI,UAAU,sBAAsB,IAAI,EAAE,EAAE,IAAI,CAAC,OAAO;AAAA,UACtD,IAAI,EAAE;AAAA,UACN,MAAM,EAAE,iBAAiB,EAAE,eAAe,EAAE;AAAA,UAC5C,GAAI,EAAE,QAAQ,SAAS,IAAI,EAAE,SAAS,EAAE,QAAQ,IAAI,CAAC;AAAA,UACrD,cAAc,EAAE;AAAA,QAClB,EAAE;AACF,YAAI,QAAQ,OAAO;AACjB,gBAAM,KAAK,QAAQ;AACnB,oBAAU,QAAQ,OAAO,CAAC,MAAM,EAAE,KAAK,SAAS,EAAE,KAAK,EAAE,SAAS,KAAK,CAAC,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;AAAA,QAC/F;AACA,YAAI,QAAQ,SAAS,QAAQ;AAC3B,kBAAQ,KAAK,CAAC,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,IAAI,CAAC;AAAA,QACrD;AACA,cAAM,QAAQ,QAAQ;AACtB,cAAM,QAAQ,QAAQ,MAAM,GAAG,KAAK;AACpC,eAAO;AAAA,UACL,MAAM,EAAE,MAAM;AAAA,UACd,MAAM,EAAE,SAAS,IAAI,SAAS,OAAO,UAAU,MAAM,OAAO;AAAA,UAC5D,YAAY,MACV,MAAM,IAAI,CAAC,GAAG,MAAM,GAAG,IAAI,CAAC,KAAK,EAAE,IAAI,MAAM,EAAE,EAAE,OAAO,EAAE,YAAY,WAAW,EAAE,KAAK,IAAI,KAC5F;AAAA,QACJ;AAAA,MACF,UAAE;AACA,YAAI,MAAM;AAAA,MACZ;AAAA,IACF,CAAC;AAAA,EACH,CAAC;AAEH,aACG,QAAQ,SAAS,EACjB,YAAY,uCAAuC,EACnD,eAAe,kBAAkB,gCAAgC,EACjE,OAAO,mBAAmB,2BAA2B,EACrD,OAAO,qBAAqB,gCAAgC,EAC5D,OAAO,OAAO,YAAmE;AAChF,UAAM,SAAS,mBAAmB,SAAS,YAAY;AACrD,YAAM,MAAM,mBAAmB,OAAO;AACtC,UAAI;AACF,cAAM,SAAS,cAAc,IAAI,IAAI,QAAQ,MAAM;AACnD,cAAM,QAAQ,qBAAqB,IAAI,IAAI,OAAO,EAAE,EAAE,IAAI,CAAC,OAAO;AAAA,UAChE,UAAU,EAAE;AAAA,UACZ,MAAM,EAAE;AAAA,UACR,OAAO,WAAW,EAAE,OAAO;AAAA,UAC3B,OAAO,EAAE,SAAS,OAAO,OAAO,WAAW,EAAE,KAAK;AAAA,QACpD,EAAE;AACF,eAAO;AAAA,UACL,MAAM,EAAE,MAAM;AAAA,UACd,MAAM,EAAE,SAAS,IAAI,SAAS,QAAQ,UAAU,MAAM,OAAO;AAAA,UAC7D,YAAY,MACV,MAAM,IAAI,CAAC,MAAM,GAAG,EAAE,KAAK,MAAM,EAAE,SAAS,SAAS,MAAM,EAAE,QAAQ,KAAK,EAAE,IAAI,EAAE,EAAE,KAAK,IAAI,KAC7F;AAAA,QACJ;AAAA,MACF,UAAE;AACA,YAAI,MAAM;AAAA,MACZ;AAAA,IACF,CAAC;AAAA,EACH,CAAC;AACL;AAIA,SAAS,mBAAmB,SAAoC;AAC9D,QAAM,EAAE,UAAU,IAAI,YAAY;AAClC,QAAM,aAAa,UAAU,eAAe;AAE5C,MAAI,WAAW,WAAW,GAAG;AAC3B,YAAQ,IAAI,yBAAyB;AACrC,YAAQ,IAAI,mBAAmB,UAAU,cAAc,EAAE,eAAe,CAAC,EAAE;AAC3E,cAAU,SAAS;AACnB;AAAA,EACF;AAEA,QAAM,WAAW,WACd,IAAI,CAAC,OAAO;AACX,UAAM,KAAK,UAAU,KAAK,EAAE;AAC5B,QAAI,CAAC,GAAI,QAAO;AAChB,UAAM,OAAO,eAAe,EAAE;AAC9B,QAAI,CAAC,KAAM,QAAO;AAClB,UAAM,WAAW,mBAAmB,EAAE;AACtC,WAAO,EAAE,IAAI,GAAG,MAAM,GAAG,SAAS;AAAA,EACpC,CAAC,EACA,OAAO,OAAO;AAEjB,MAAI,QAAQ,WAAW,QAAQ;AAC7B,YAAQ,IAAI,KAAK,UAAU,UAAU,MAAM,CAAC,CAAC;AAAA,EAC/C,OAAO;AACL,YAAQ,IAAI,GAAG,SAAS,MAAM;AAAA,CAAsB;AACpD,eAAW,KAAK,UAAU;AACxB,UAAI,CAAC,EAAG;AACR,YAAM,QACJ,EAAE,kBAAkB,EAAE,gBAClB,GAAG,IAAI,KAAK,EAAE,iBAAiB,GAAI,EAAE,mBAAmB,CAAC,MAAM,IAAI,KAAK,EAAE,gBAAgB,GAAI,EAAE,mBAAmB,CAAC,KACpH;AACN,cAAQ,IAAI,KAAK,EAAE,IAAI,EAAE;AACzB,cAAQ,IAAI,WAAW,EAAE,EAAE,EAAE;AAC7B,cAAQ;AAAA,QACN,iBAAiB,EAAE,QAAQ,YAAY,EAAE,IAAI,eAAe,EAAE,YAAY,gBAAgB,EAAE,aAAa;AAAA,MAC3G;AACA,UAAI,MAAO,SAAQ,IAAI,cAAc,KAAK,EAAE;AAC5C,cAAQ,IAAI;AAAA,IACd;AAAA,EACF;AAEA,YAAU,SAAS;AACrB;AAEA,SAAS,kBAAkB,WAAmB,SAAoC;AAChF,QAAM,EAAE,UAAU,IAAI,YAAY;AAClC,QAAM,KAAK,UAAU,KAAK,SAAS;AACnC,MAAI,CAAC,IAAI;AACP,YAAQ,MAAM,WAAW,SAAS,YAAY;AAC9C,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,UAAU,WAAW,EAAE;AAE7B,MAAI,QAAQ,WAAW,QAAQ;AAC7B,YAAQ,IAAI,KAAK,UAAU,SAAS,MAAM,CAAC,CAAC;AAAA,EAC9C,OAAO;AACL,YAAQ,IAAI,GAAG,QAAQ,MAAM;AAAA,CAAe;AAC5C,eAAW,CAAC,GAAG,CAAC,KAAK,QAAQ,QAAQ,GAAG;AACtC,cAAQ,IAAI,KAAK,IAAI,CAAC,KAAK,EAAE,IAAI,KAAK,EAAE,UAAU,OAAO,EAAE,YAAY,WAAW;AAAA,IACpF;AAAA,EACF;AAEA,YAAU,SAAS;AACrB;;;ACvPO,IAAM,qBAAqB;AAC3B,IAAM,iBAAiB;AAGvB,SAAS,kBAAkB,QAA4C;AAC5E,SAAO,EAAE,GAAG,QAAQ,kBAAkB,OAAO,SAAS,OAAO,gBAAgB,MAAM;AACrF;AAkBO,SAAS,0BAA0B,QAAgB,SAAkC;AAC1F,MAAI,QAAQ,OAAO,WAAW,SAAS;AACrC,UAAM,IAAI,WAAW;AAAA,MACnB,MAAM;AAAA,MACN,SAAS;AAAA,MACT,MAAM;AAAA,IACR,CAAC;AAAA,EACH;AACF;AAMO,SAAS,eACd,UACA,KACA,SAMiB;AACjB,QAAM,YAAY,WAAW,QAAQ,WAAW,oBAAoB,gBAAgB,cAAc;AAClG,QAAM,WAAW,QAAQ,OACrB,IACA,QAAQ,aAAa,SACnB,WAAW,QAAQ,UAAU,KAAK,KAAO,aAAa,IACtD,QAAQ;AAEd,QAAM,SAAS,2BAA2B;AAAA,IACxC,aAAa;AAAA,IACb,kBAAkB,IAAI;AAAA,IACtB,QAAQ,IAAI;AAAA,IACZ,gBAAgB,IAAI,iBAAiB,kBAAkB;AAAA,IACvD,iBAAiB,IAAI;AAAA,IACrB,qBAAqB;AAAA,IACrB,oBAAoB,QAAQ;AAAA,IAC5B,mBAAmB;AAAA,IACnB,QAAQ,QAAQ;AAAA,IAChB,iBAAiB;AAAA,EACnB,CAAC;AAED,QAAM,SAAS,IAAI;AACnB,QAAM,aAAa;AAAA,IACjB,SAAS,OAAO,iBAAiB;AAAA,IACjC,UAAU,CAAC,CAAC,OAAO;AAAA,IACnB,QAAQ,CAAC,CAAC,OAAO;AAAA,IACjB,cAAc,CAAC,CAAC,OAAO,eAAe,OAAO,iBAAiB,KAAK,CAAC,MAAM,EAAE,OAAO;AAAA,IACnF,WAAW,OAAO,MAAM;AAAA,EAC1B;AAEA,QAAM,mBAAmB,QAAQ,UAC7B;AAAA,IACE,eAAe,OAAO,MAAM;AAAA,IAC5B,gBAAgB,OAAO,MAAM;AAAA,IAC7B,SAAS,OAAO,MAAM;AAAA,IACtB,kBAAkB,OAAO,MAAM;AAAA,IAC/B,gBAAgB,OAAO,MAAM;AAAA,IAC7B,eAAe,OAAO,MAAM;AAAA,IAC5B,yBAAyB,OAAO,MAAM;AAAA,EACxC,IACA;AAEJ,SAAO,EAAE,MAAM,OAAO,MAAM,OAAO,OAAO,OAAO,YAAY,iBAAiB;AAChF;AAcA,IAAM,mBAAmB,oBAAI,IAAI,CAAC,MAAM,QAAQ,YAAY,cAAc,QAAQ,WAAW,KAAK,CAAC;AAM5F,SAAS,YACd,UACA,kBACA,SAC2B;AAC3B,QAAM,UAAU,QAAQ,MACpB,WACC,mBAAmB,UAAqC,kBAAkB,gBAAgB,CAAC;AAEhG,QAAM,YAAY,QAAQ,YAAY;AACtC,QAAM,WAAW,QAAQ,OACrB,IACA,QAAQ,aAAa,SACnB,WAAW,QAAQ,UAAU,KAAK,KAAO,aAAa,IACrD,QAAQ,mBAAmB;AAElC,MAAI;AACJ,MAAI,QAAQ,QAAQ;AAClB,aAAS,IAAI,IAAI,QAAQ,OAAO,MAAM,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;AAC/D,eAAW,SAAS,QAAQ;AAC1B,UAAI,CAAC,iBAAiB,IAAI,KAAK,GAAG;AAChC,cAAM,IAAI,WAAW;AAAA,UACnB,MAAM;AAAA,UACN,SAAS,kBAAkB,KAAK;AAAA,UAChC,MAAM,qBAAqB,CAAC,GAAG,gBAAgB,EAAE,KAAK,IAAI,CAAC;AAAA,QAC7D,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAEA,SAAO,QAAQ,IAAI,CAAC,QAAQ;AAC1B,QAAI,UAAqC,IAAI;AAC7C,QAAI,WAAW;AACb,gBAAU;AAAA,IACZ,WAAW,WAAW,WAAW,KAAK,QAAQ,SAAS,UAAU;AAC/D,gBAAU,QAAQ,MAAM,GAAG,QAAQ,IAAI;AAAA,IACzC;AAEA,UAAM,OAAgC;AAAA,MACpC,IAAI,IAAI;AAAA,MACR,MAAM,WAAW,IAAI,SAAS;AAAA,MAC9B,GAAI,IAAI,aAAa,SAAY,EAAE,UAAU,IAAI,SAAS,IAAI,CAAC;AAAA,MAC/D,YAAY,IAAI;AAAA,MAChB,GAAI,IAAI,SAAS,SAAY,EAAE,MAAM,oBAAoB,IAAI,IAAI,EAAE,IAAI,CAAC;AAAA,MACxE,GAAI,YAAY,SAAY,EAAE,QAAQ,IAAI,CAAC;AAAA,MAC3C,GAAI,QAAQ,QAAQ,IAAI,IAAI,EAAE,IAAI,EAAE,KAAK,KAAK,IAAI,CAAC;AAAA,IACrD;AAEA,QAAI,QAAQ;AACV,iBAAW,OAAO,OAAO,KAAK,IAAI,GAAG;AACnC,YAAI,CAAC,OAAO,IAAI,GAAG,EAAG,QAAO,KAAK,GAAG;AAAA,MACvC;AAAA,IACF;AACA,WAAO;AAAA,EACT,CAAC;AACH;AAGO,SAAS,kBAA+D,UAAoB;AACjG,SAAO,CAAC,GAAG,QAAQ,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,YAAY,EAAE,aAAa,EAAE,KAAK,EAAE,EAAE;AAC9E;AAwBO,SAAS,oBACd,QACA,KACA,SACA,UACA,MACA,OACsB;AACtB,QAAM,gBAAgB,kBAAkB,QAAQ;AAEhD,MAAI,WAAW,QAAQ;AACrB,UAAM,QAAQ,YAAY,eAAe,IAAI,kBAAkB;AAAA,MAC7D,UAAU,QAAQ;AAAA,MAClB,MAAM,QAAQ;AAAA,MACd,QAAQ,QAAQ;AAAA,MAChB,SAAS,QAAQ;AAAA,MACjB,KAAK,QAAQ;AAAA,MACb,QAAQ,MAAM;AAAA,MACd,iBAAiB,MAAM;AAAA,IACzB,CAAC;AACD,WAAO;AAAA,MACL,MAAM,EAAE,MAAM;AAAA,MACd,MAAM;AAAA,QACJ,GAAG;AAAA,QACH,UAAU,MAAM;AAAA,QAChB,YAAY,QAAQ,MAChB,EAAE,KAAK,KAAK,IACZ;AAAA,UACE,SAAS,IAAI,iBAAiB,iBAAiB;AAAA,UAC/C,cACE,CAAC,CAAC,IAAI,iBAAiB,eAAe,IAAI,iBAAiB,iBAAiB,KAAK,CAAC,MAAM,EAAE,OAAO;AAAA,QACrG;AAAA,MACN;AAAA,IACF;AAAA,EACF;AAEA,MAAI,QAAQ,KAAK;AAEf,WAAO;AAAA,MACL,MAAM,EAAE,OAAO,cAAc;AAAA,MAC7B,YAAY,MACV,cACG,IAAI,CAAC,MAAM,KAAK,EAAE,EAAE,KAAK,IAAI,KAAK,EAAE,YAAY,GAAI,EAAE,eAAe,CAAC,IAAI,EAAE,UAAU,KAAK,EAAE,WAAW,EAAE,EAAE,EAC5G,KAAK,IAAI,KAAK;AAAA,IACrB;AAAA,EACF;AAEA,QAAM,QAAQ,eAAe,eAAe,KAAK;AAAA,IAC/C,QAAQ,MAAM;AAAA,IACd,UAAU,MAAM;AAAA,IAChB,WAAW,QAAQ;AAAA,IACnB,UAAU,QAAQ;AAAA,IAClB,MAAM,QAAQ;AAAA,IACd,SAAS,QAAQ;AAAA,IACjB,iBAAiB,MAAM;AAAA,EACzB,CAAC;AAED,SAAO;AAAA,IACL,MAAM,EAAE,MAAM,MAAM,KAAK;AAAA,IACzB,MAAM;AAAA,MACJ,GAAG;AAAA,MACH,YAAY,MAAM;AAAA,MAClB,GAAI,MAAM,mBAAmB,EAAE,kBAAkB,MAAM,iBAAiB,IAAI,CAAC;AAAA,IAC/E;AAAA,IACA,YAAY,MAAM,MAAM;AAAA,EAC1B;AACF;;;AC7PO,SAAS,0BACd,UACA,QACA,aACe;AACf,MAAI,eAAe,EAAG,QAAO,CAAC;AAC9B,MAAI,SAAS,UAAU,YAAa,QAAO;AAE3C,QAAM,SAAS,SAAS,MAAM,GAAG,WAAW;AAC5C,QAAM,cAAc,IAAI,IAAI,OAAO,IAAI,CAAC,YAAY,QAAQ,EAAE,CAAC;AAC/D,QAAM,cAAc,SAAS,OAAO,CAAC,YAAY,OAAO,IAAI,QAAQ,EAAE,KAAK,CAAC,YAAY,IAAI,QAAQ,EAAE,CAAC;AAEvG,aAAW,OAAO,aAAa;AAC7B,QAAI,eAAe;AACnB,aAAS,IAAI,OAAO,SAAS,GAAG,KAAK,GAAG,KAAK;AAC3C,UAAI,CAAC,OAAO,IAAI,OAAO,CAAC,EAAE,EAAE,GAAG;AAC7B,uBAAe;AACf;AAAA,MACF;AAAA,IACF;AACA,QAAI,iBAAiB,GAAI;AACzB,WAAO,YAAY,IAAI;AAAA,EACzB;AAEA,SAAO,OAAO,KAAK,CAAC,GAAG,MAAM,SAAS,QAAQ,CAAC,IAAI,SAAS,QAAQ,CAAC,CAAC;AACxE;AAEO,SAAS,yBAAyB,UAAyB,MAAoC;AACpG,QAAM,cAAc,IAAI,IAAI,SAAS,IAAI,CAAC,YAAY,QAAQ,EAAE,CAAC;AACjE,SAAO,CAAC,GAAG,UAAU,GAAG,KAAK,OAAO,CAAC,YAAY,CAAC,YAAY,IAAI,QAAQ,EAAE,CAAC,CAAC;AAChF;AAEO,SAAS,gBAAgB,OAAyB;AACvD,QAAM,SAAS,MAAM,MAAM,GAAG,EAAE,IAAI,CAAC,UAAU,MAAM,KAAK,CAAC;AAC3D,QAAM,MAAM,OAAO,IAAI,CAAC,UAAW,MAAM,SAAS,IAAI,OAAO,KAAK,IAAI,OAAO,GAAI;AACjF,MAAI,IAAI,WAAW,KAAK,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,UAAU,EAAE,KAAK,MAAM,CAAC,GAAG;AAC1E,UAAM,IAAI,WAAW;AAAA,MACnB,MAAM;AAAA,MACN,SAAS,uBAAuB,KAAK;AAAA,MACrC,MAAM;AAAA,IACR,CAAC;AAAA,EACH;AACA,SAAO;AACT;AAEO,SAAS,oBAAoB,QAA4B;AAC9D,QAAM,WAAW,OAAO,IAAI,CAAC,UAAU,MAAM,KAAK,CAAC,EAAE,OAAO,OAAO;AACnE,MAAI,SAAS,WAAW,GAAG;AACzB,UAAM,IAAI,WAAW;AAAA,MACnB,MAAM;AAAA,MACN,SAAS;AAAA,MACT,MAAM;AAAA,IACR,CAAC;AAAA,EACH;AACA,SAAO;AACT;AAEO,SAAS,yBAAyB,MAAsD;AAC7F,SAAO,SAAS,SAAS,cAAc;AACzC;AAEO,SAAS,4BAA4B,KAAe,UAAyB,UAAwB;AAC1G,QAAM,cAAc,IAAI,IAAI,SAAS,IAAI,CAAC,YAAY,QAAQ,EAAE,CAAC;AACjE,MAAI,IAAI,KAAK,CAAC,OAAO,CAAC,YAAY,IAAI,EAAE,CAAC,GAAG;AAC1C,UAAM,IAAI,WAAW;AAAA,MACnB,MAAM;AAAA,MACN,SAAS,+BAA+B,QAAQ;AAAA,MAChD,MAAM;AAAA,IACR,CAAC;AAAA,EACH;AACF;AAEA,SAAS,iBAAiB,KAAc,UAAqC,CAAC,GAAY;AACxF,QAAM,SAAS,IAAI,OAAO,mBAAmB,gEAAgE;AAC7G,MAAI,QAAQ,gBAAgB,OAAO;AACjC,WACG,OAAO,eAAe,wEAAwE,EAC9F,OAAO,eAAe,mDAAmD,EACzE,OAAO,gBAAgB,qEAAqE;AAAA,EACjG;AACA,SAAO,OACJ,OAAO,qBAAqB,sEAAsE,EAClG,OAAO,oBAAoB,4CAA4C,EACvE,OAAO,mBAAmB,gCAAgC,EAC1D,OAAO,UAAU,wCAAwC,EACzD,OAAO,kBAAkB,6CAA6C,EACtE,OAAO,gBAAgB,2DAA2D,EAClF,OAAO,SAAS,gFAAgF,EAChG,OAAO,aAAa,2CAA2C;AACpE;AAEO,SAAS,wBAAwBC,UAAwB;AAC9D,QAAM,cAAcA,SAAQ,QAAQ,UAAU,EAAE,YAAY,iDAAiD;AAG7G,QAAM,UAAU,YACb,QAAQ,MAAM,EACd,YAAY,mFAAmF,EAC/F,OAAO,kBAAkB,mDAAmD,EAC5E,OAAO,oBAAoB,+CAA+C,EAC1E,OAAO,eAAe,yCAAyC;AAClE,mBAAiB,OAAO;AACxB,UAAQ,OAAO,OAAO,YAAwD;AAC5E,UAAM,SAAS,iBAAiB,SAAS,OAAO,WAAW;AACzD,gCAA0B,QAAQ,OAAO;AACzC,YAAM,MAAM,mBAAmB,OAAO;AACtC,UAAI;AACF,yBAAiB,KAAK,OAAO;AAC7B,cAAM,OAAO,4BAA4B,OAAO;AAChD,cAAM,SAAS,QAAQ,SAAS,cAAc,IAAI,IAAI,QAAQ,MAAM,IAAI;AACxE,cAAM,QAAQ,WAAW,QAAQ,OAAO,IAAI,KAAK,WAAW,CAAC;AAC7D,cAAM,kBAAkB,QAAQ,MAAM,SAAY,IAAI,iBAAiB;AAEvE,cAAM,cAAc,iBAAiB;AAAA,UACnC,SAAS;AAAA,UACT,SAAS,IAAI,QAAQ;AAAA,UACrB,SAAS,KAAK,WAAW;AAAA,UACzB,OAAO,KAAK,SAAS;AAAA,UACrB,QAAQ,QAAQ,MAAM;AAAA,UACtB,SAAS,mBAAmB,CAAC;AAAA,QAC/B,CAAC;AACD,cAAM,SAAS,QAAQ,SAAS,aAAa,QAAQ,QAAQ,WAAW,IAAI;AAE5E,cAAM,SAAS,yBAAyB,IAAI,IAAI,CAAC,GAAG;AAAA,UAClD,SAAS,KAAK;AAAA,UACd,OAAO,KAAK;AAAA,UACZ,UAAU,QAAQ;AAAA,UAClB;AAAA,UACA;AAAA,UACA;AAAA,UACA,MAAM;AAAA,QACR,CAAC;AAED,cAAM,QAAQ,OAAO,SAAS,OAAO,SAAS;AAC9C,cAAM,UAAU,SAAS,OAAO,SAAS,SAAS;AAClD,cAAM,OAAgC;AAAA,UACpC,SAAS,IAAI;AAAA,UACb,WAAW,KAAK;AAAA,UAChB,GAAI,SAAS,EAAE,OAAO,IAAI,CAAC;AAAA,UAC3B,WAAW;AAAA,UACX,cAAc,OAAO,SAAS;AAAA,UAC9B;AAAA,UACA,GAAI,UAAU,EAAE,YAAY,aAAa,SAAS,OAAO,SAAS,QAAQ,aAAa,EAAE,KAAK,CAAC,EAAE,IAAI,CAAC;AAAA,QACxG;AAEA,eAAO,oBAAoB,QAAQ,KAAK,SAAS,OAAO,UAAU,MAAM;AAAA,UACtE,UAAU;AAAA,QACZ,CAAC;AAAA,MACH,UAAE;AACA,YAAI,MAAM;AAAA,MACZ;AAAA,IACF,CAAC;AAAA,EACH,CAAC;AAGD,QAAM,YAAY,YACf,QAAQ,QAAQ,EAChB,YAAY,2EAA2E,EACvF,SAAS,iBAAiB,iBAAiB,EAC3C,OAAO,kBAAkB,2CAA2C,KAAK,EACzE,OAAO,kBAAkB,mDAAmD,EAC5E,OAAO,iBAAiB,oDAAoD,EAC5E,OAAO,gBAAgB,6DAA6D,MAAM,EAC1F,OAAO,oBAAoB,+CAA+C,EAC1E,OAAO,eAAe,qCAAqC,EAC3D,OAAO,sBAAsB,6DAA6D;AAC7F,mBAAiB,SAAS;AAC1B,YAAU;AAAA,IACR,OACE,UACA,YAOG;AACH,YAAM,SAAS,mBAAmB,SAAS,OAAO,WAAW;AAC3D,kCAA0B,QAAQ,OAAO;AACzC,cAAM,iBAAiB,oBAAoB,QAAQ;AACnD,YAAI,QAAQ,UAAU,SAAS,QAAQ,UAAU,OAAO;AACtD,gBAAM,IAAI,WAAW;AAAA,YACnB,MAAM;AAAA,YACN,SAAS,0BAA0B,QAAQ,KAAK;AAAA,YAChD,MAAM;AAAA,UACR,CAAC;AAAA,QACH;AACA,YAAI,QAAQ,SAAS,SAAS,QAAQ,SAAS,QAAQ;AACrD,gBAAM,IAAI,WAAW;AAAA,YACnB,MAAM;AAAA,YACN,SAAS,yBAAyB,QAAQ,IAAI;AAAA,YAC9C,MAAM;AAAA,UACR,CAAC;AAAA,QACH;AACA,cAAM,MAAM,mBAAmB,OAAO;AACtC,YAAI;AACF,2BAAiB,KAAK,OAAO;AAC7B,gBAAM,OAAO,4BAA4B,OAAO;AAChD,gBAAM,SAAS,QAAQ,SAAS,cAAc,IAAI,IAAI,QAAQ,MAAM,IAAI;AACxE,gBAAM,QAAQ,WAAW,QAAQ,OAAO,IAAI,KAAK,WAAW,CAAC;AAC7D,gBAAM,UAAU,WAAW,QAAQ,SAAS,GAAG,IAAI,WAAW;AAC9D,gBAAM,cAAc,WAAW,QAAQ,aAAa,KAAK,KAAM,gBAAgB;AAC/E,gBAAM,kBAAkB,QAAQ,MAAM,SAAY,IAAI,iBAAiB;AAEvE,gBAAM,cAAc,iBAAiB;AAAA,YACnC,SAAS;AAAA,YACT,SAAS,IAAI,QAAQ;AAAA,YACrB,UAAU;AAAA,YACV,OAAO,QAAQ;AAAA,YACf,MAAM,QAAQ;AAAA,YACd,SAAS,KAAK,WAAW;AAAA,YACzB,OAAO,KAAK,SAAS;AAAA,YACrB,QAAQ,QAAQ,MAAM;AAAA,YACtB,SAAS,mBAAmB,CAAC;AAAA,UAC/B,CAAC;AACD,gBAAM,SAAS,QAAQ,SAAS,aAAa,QAAQ,QAAQ,WAAW,IAAI;AAE5E,gBAAM,SAAS,yBAAyB,IAAI,IAAI,gBAAgB;AAAA,YAC9D,SAAS,KAAK;AAAA,YACd,OAAO,KAAK;AAAA,YACZ,UAAU,QAAQ;AAAA,YAClB;AAAA,YACA;AAAA,YACA,WAAW,QAAQ;AAAA,YACnB;AAAA,YACA,MAAM,QAAQ;AAAA,UAChB,CAAC;AAED,gBAAM,OAAO,OAAO;AACpB,gBAAM,SAAS,IAAI,IAAI,KAAK,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;AAC5C,gBAAM,WAAqB,CAAC;AAE5B,cAAI,mBAAkC;AACtC,cAAI,UAAU,KAAK,KAAK,SAAS,GAAG;AAClC,kBAAM,aAAa,UAAU,IAAI;AACjC,gBAAI,iBAAiB;AACrB,gBAAI,KAAK,SAAS,aAAa,aAAa;AAC1C,oBAAM,UAAU,KAAK,IAAI,GAAG,KAAK,MAAM,cAAc,UAAU,CAAC;AAChE,+BAAiB,KAAK,MAAM,GAAG,OAAO;AACtC,uBAAS;AAAA,gBACP,kBAAkB,WAAW,oCAAoC,OAAO,OAAO,KAAK,MAAM;AAAA,cAC5F;AAAA,YACF;AACA,kBAAM,WAAW;AAAA,cACf,IAAI;AAAA,cACJ,eAAe,IAAI,CAAC,MAAM,EAAE,EAAE;AAAA,cAC9B;AAAA,cACA;AAAA,YACF;AACA,kBAAM,WAAW,IAAI,IAAI,eAAe,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;AACxD,kBAAM,OAAO,KAAK,OAAO,CAAC,MAAM,CAAC,SAAS,IAAI,EAAE,EAAE,CAAC;AACnD,+BAAmB,yBAAyB,UAAU,IAAI;AAC1D,gBAAI,iBAAiB,SAAS,aAAa;AACzC,iCAAmB,0BAA0B,kBAAkB,QAAQ,WAAW;AAClF,uBAAS,KAAK,sBAAsB,WAAW,WAAW;AAAA,YAC5D;AAAA,UACF;AAEA,gBAAM,QAAQ,OAAO,SAAS,KAAK;AACnC,gBAAM,UAAU,SAAS,KAAK,SAAS;AACvC,gBAAM,OAAgC;AAAA,YACpC,SAAS,IAAI;AAAA,YACb,WAAW,KAAK;AAAA,YAChB,OAAO,EAAE,UAAU,gBAAgB,OAAO,QAAQ,OAAO,QAAQ,QAAQ,MAAM,QAAQ,MAAM,QAAQ;AAAA,YACrG,GAAI,SAAS,EAAE,OAAO,IAAI,CAAC;AAAA,YAC3B,WAAW;AAAA,YACX,cAAc,KAAK;AAAA,YACnB;AAAA,YACA,GAAI,UAAU,EAAE,YAAY,aAAa,SAAS,KAAK,QAAQ,aAAa,EAAE,KAAK,CAAC,EAAE,IAAI,CAAC;AAAA,YAC3F,GAAI,SAAS,SAAS,IAAI,EAAE,SAAS,IAAI,CAAC;AAAA,UAC5C;AAEA,iBAAO,oBAAoB,QAAQ,KAAK,SAAS,kBAAkB,MAAM;AAAA,YACvE;AAAA,YACA,UAAU,yBAAyB,QAAQ,IAAI;AAAA,YAC/C,iBAAiB;AAAA,UACnB,CAAC;AAAA,QACH,UAAE;AACA,cAAI,MAAM;AAAA,QACZ;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AAGA,QAAM,aAAa,YAChB,QAAQ,SAAS,EACjB,YAAY,mDAAmD,EAC/D,eAAe,cAAc,gCAAgC,EAC7D,OAAO,gBAAgB,qDAAqD;AAC/E,mBAAiB,YAAY,EAAE,aAAa,MAAM,CAAC;AACnD,aAAW,OAAO,OAAO,YAAoE;AAC3F,UAAM,SAAS,oBAAoB,SAAS,OAAO,WAAW;AAC5D,gCAA0B,QAAQ,OAAO;AACzC,YAAM,MAAM,gBAAgB,QAAQ,EAAE;AACtC,YAAM,MAAM,mBAAmB,OAAO;AACtC,UAAI;AACF,yBAAiB,KAAK,OAAO;AAC7B,cAAM,SAAS,WAAW,QAAQ,QAAQ,IAAI,KAAK,UAAU;AAC7D,cAAM,WAAW,kBAAkB,IAAI,IAAI,KAAK,MAAM;AACtD,oCAA4B,KAAK,UAAU,QAAQ,EAAE;AACrD,cAAM,OAAgC;AAAA,UACpC,SAAS,IAAI;AAAA,UACb,WAAW;AAAA,UACX;AAAA,QACF;AACA,eAAO,oBAAoB,QAAQ,KAAK,SAAS,UAAU,MAAM;AAAA,UAC/D,QAAQ,IAAI,IAAI,GAAG;AAAA,UACnB,UAAU;AAAA,QACZ,CAAC;AAAA,MACH,UAAE;AACA,YAAI,MAAM;AAAA,MACZ;AAAA,IACF,CAAC;AAAA,EACH,CAAC;AAGD,QAAM,aAAa,YAChB,QAAQ,SAAS,EACjB,YAAY,kEAAkE,EAC9E,OAAO,kBAAkB,iDAAiD,gBAAgB,CAAC,CAAa,EACxG,OAAO,oBAAoB,+CAA+C,EAC1E,OAAO,eAAe,yCAAyC;AAClE,mBAAiB,UAAU;AAC3B,aAAW,OAAO,OAAO,YAAyD;AAChF,UAAM,SAAS,oBAAoB,SAAS,OAAO,WAAW;AAC5D,gCAA0B,QAAQ,OAAO;AACzC,UAAI,QAAQ,OAAO,WAAW,GAAG;AAC/B,cAAM,IAAI,WAAW;AAAA,UACnB,MAAM;AAAA,UACN,SAAS,6CAA6C,QAAQ,OAAO,MAAM;AAAA,UAC3E,MAAM;AAAA,QACR,CAAC;AAAA,MACH;AACA,YAAM,MAAM,mBAAmB,OAAO;AACtC,UAAI;AACF,yBAAiB,KAAK,OAAO;AAC7B,cAAM,OAAO,4BAA4B,OAAO;AAChD,cAAM,UAAU,cAAc,IAAI,IAAI,QAAQ,OAAO,CAAC,CAAC;AACvD,cAAM,UAAU,cAAc,IAAI,IAAI,QAAQ,OAAO,CAAC,CAAC;AACvD,cAAM,QAAQ,WAAW,QAAQ,OAAO,IAAI,KAAK,WAAW,CAAC;AAC7D,cAAM,kBAAkB,QAAQ,MAAM,SAAY,IAAI,iBAAiB;AAEvE,cAAM,cAAc,iBAAiB;AAAA,UACnC,SAAS;AAAA,UACT,SAAS,IAAI,QAAQ;AAAA,UACrB,SAAS,CAAC,QAAQ,IAAI,QAAQ,EAAE;AAAA,UAChC,SAAS,KAAK,WAAW;AAAA,UACzB,OAAO,KAAK,SAAS;AAAA,UACrB,SAAS,mBAAmB,CAAC;AAAA,QAC/B,CAAC;AACD,cAAM,SAAS,QAAQ,SAAS,aAAa,QAAQ,QAAQ,WAAW,IAAI;AAE5E,cAAM,aACJ,KAAK,YAAY,UAAa,KAAK,UAAU,SACzC,EAAE,SAAS,KAAK,WAAW,GAAG,OAAO,KAAK,SAAS,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI,EAAE,IACjF;AACN,cAAM,SAAS,uBAAuB,IAAI,IAAI,QAAQ,IAAI,QAAQ,IAAI,YAAY,OAAO;AAAA,UACvF;AAAA,UACA;AAAA,QACF,CAAC;AAED,cAAM,WAAW,OAAO,SAAS;AACjC,cAAM,UAAU,SAAS,WAAW,OAAO;AAC3C,cAAM,OAAgC;AAAA,UACpC,SAAS,IAAI;AAAA,UACb,WAAW,KAAK;AAAA,UAChB,SAAS,CAAC,SAAS,OAAO;AAAA,UAC1B,WAAW,OAAO;AAAA,UAClB,cAAc;AAAA,UACd;AAAA,UACA,GAAI,UAAU,EAAE,YAAY,aAAa,SAAS,UAAU,aAAa,EAAE,KAAK,CAAC,EAAE,IAAI,CAAC;AAAA,QAC1F;AAEA,eAAO,oBAAoB,QAAQ,KAAK,SAAS,OAAO,UAAU,MAAM;AAAA,UACtE,UAAU;AAAA,QACZ,CAAC;AAAA,MACH,UAAE;AACA,YAAI,MAAM;AAAA,MACZ;AAAA,IACF,CAAC;AAAA,EACH,CAAC;AACH;AAEA,SAAS,eAAe,OAAe,UAA8B;AACnE,SAAO,CAAC,GAAG,UAAU,KAAK;AAC5B;;;ACnYA,SAAS,gBAAgB,KAAuB;AAC9C,SAAO,IACJ,OAAO,mBAAmB,gEAAgE,EAC1F,OAAO,eAAe,oDAAoD,EAC1E,OAAO,eAAe,mDAAmD,EACzE,OAAO,gBAAgB,2BAA2B,EAClD,OAAO,qBAAqB,gCAAgC;AACjE;AAEA,SAAS,aAAa,MAA2F;AAC/G,MAAI,KAAK,YAAY,UAAa,KAAK,UAAU,OAAW,QAAO;AACnE,SAAO,EAAE,SAAS,KAAK,WAAW,GAAG,OAAO,KAAK,SAAS,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI,EAAE;AAC1F;AAEO,SAAS,2BAAmC;AACjD,SAAO;AAAA;AAAA;AAAA;AAAA;AAKT;AAEO,SAAS,sBAAsBC,UAAwB;AAC5D,QAAM,WAAWA,SACd,QAAQ,OAAO,EACf,YAAY,mEAAmE,EAC/E,SAAS,gBAAgB,uCAAuC,EAChE,OAAO,qBAAqB,eAAe,EAC3C,OAAO,aAAa,yBAAyB,IAAI,EACjD,OAAO,CAAC,WAA+B,SAA2C,YAAqB;AACtG,QAAI,CAAC,WAAW;AACd,cAAQ,KAAK,EAAE,OAAO,MAAM,CAAC;AAC7B;AAAA,IACF;AACA,YAAQ;AAAA,MACN;AAAA,IACF;AACA,gBAAY,WAAW,OAAO;AAAA,EAChC,CAAC;AAGH,WACG,QAAQ,UAAU,EAClB,YAAY,mDAAmD,EAC/D,OAAO,mBAAmB,2BAA2B,EACrD,OAAO,qBAAqB,gCAAgC,EAC5D,OAAO,OAAO,YAAmD;AAChE,UAAM,SAAS,kBAAkB,SAAS,YAAY;AACpD,YAAM,MAAM,mBAAmB,OAAO;AACtC,UAAI;AACF,cAAM,WAAW,gBAAgB,IAAI,EAAE;AACvC,YAAI,CAAC,UAAU;AACb,gBAAM,IAAI,WAAW,EAAE,MAAM,qBAAqB,SAAS,2BAA2B,CAAC;AAAA,QACzF;AACA,cAAM,OAAO;AAAA,UACX,MAAM,SAAS;AAAA,UACf,UAAU,SAAS;AAAA,UACnB,MAAM,SAAS;AAAA,UACf,eAAe,SAAS;AAAA,UACxB,cAAc,SAAS;AAAA,UACvB,cAAc,SAAS,kBAAkB,OAAO,OAAO,WAAW,SAAS,cAAc;AAAA,UACzF,aAAa,SAAS,iBAAiB,OAAO,OAAO,WAAW,SAAS,aAAa;AAAA,UACtF,YAAY,SAAS;AAAA,UACrB,cAAc,SAAS;AAAA,QACzB;AACA,eAAO;AAAA,UACL;AAAA,UACA,MAAM,EAAE,SAAS,IAAI,QAAQ;AAAA,UAC7B,YAAY,MACV,GAAG,KAAK,IAAI;AAAA,YAAe,KAAK,aAAa,eAAe,KAAK,YAAY;AAAA,SACnE,KAAK,gBAAgB,GAAG,MAAM,KAAK,eAAe,GAAG;AAAA,IAC/D,KAAK,WAAW,IAAI,CAAC,GAAG,MAAM,KAAK,IAAI,CAAC,KAAK,EAAE,IAAI,MAAM,EAAE,KAAK,EAAE,EAAE,KAAK,IAAI;AAAA,QACjF;AAAA,MACF,UAAE;AACA,YAAI,MAAM;AAAA,MACZ;AAAA,IACF,CAAC;AAAA,EACH,CAAC;AAGH,QAAM,cAAc,SAAS,QAAQ,UAAU,EAAE,YAAY,yBAAyB;AACtF,kBAAgB,WAAW;AAC3B,cAAY,OAAO,aAAa,qCAAqC;AACrE,cAAY,OAAO,OAAO,YAAiD;AACzE,UAAM,SAAS,kBAAkB,SAAS,YAAY;AACpD,YAAM,MAAM,mBAAmB,OAAO;AACtC,UAAI;AACF,cAAM,OAAO,iBAAiB,OAAO;AACrC,cAAM,MAAM,WAAW,QAAQ,KAAK,IAAI,KAAK,OAAO;AACpD,cAAM,QAAQ,kBAAkB,IAAI,IAAI,aAAa,IAAI,CAAC,EACvD,MAAM,GAAG,GAAG,EACZ,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,UAAU,MAAM,EAAE,MAAM,cAAc,EAAE,cAAc,YAAY,EAAE,WAAW,EAAE;AACxG,eAAO;AAAA,UACL,MAAM,EAAE,MAAM;AAAA,UACd,MAAM,EAAE,SAAS,IAAI,SAAS,WAAW,KAAK,MAAM,UAAU,MAAM,OAAO;AAAA,UAC3E,YAAY,MACV,MAAM,IAAI,CAAC,GAAG,MAAM,GAAG,IAAI,CAAC,KAAK,EAAE,IAAI,MAAM,EAAE,YAAY,cAAc,EAAE,UAAU,IAAI,EAAE,KAAK,IAAI,KACpG;AAAA,QACJ;AAAA,MACF,UAAE;AACA,YAAI,MAAM;AAAA,MACZ;AAAA,IACF,CAAC;AAAA,EACH,CAAC;AAGD,QAAM,UAAU,SAAS,QAAQ,MAAM,EAAE,YAAY,gCAAgC;AACrF,kBAAgB,OAAO;AACvB,UAAQ,eAAe,eAAe,qCAAqC;AAC3E,UAAQ,OAAO,OAAO,YAA+C;AACnE,UAAM,SAAS,cAAc,SAAS,YAAY;AAChD,YAAM,MAAM,mBAAmB,OAAO;AACtC,UAAI;AACF,cAAM,OAAO,iBAAiB,OAAO;AACrC,cAAM,SAAS,aAAa,IAAI;AAChC,YAAI;AACJ,gBAAQ,QAAQ,IAAI;AAAA,UAClB,KAAK;AACH,oBAAQ,kBAAkB,IAAI,IAAI,MAAM;AACxC;AAAA,UACF,KAAK;AACH,oBAAQ,mBAAmB,IAAI,IAAI,MAAM;AACzC;AAAA,UACF,KAAK;AACH,oBAAQ,iBAAiB,IAAI,IAAI,MAAM;AACvC;AAAA,UACF,KAAK;AACH,oBAAQ,mBAAmB,IAAI,IAAI,MAAM;AACzC;AAAA,UACF;AACE,kBAAM,IAAI,WAAW;AAAA,cACnB,MAAM;AAAA,cACN,SAAS,uBAAuB,QAAQ,EAAE;AAAA,cAC1C,MAAM;AAAA,YACR,CAAC;AAAA,QACL;AACA,eAAO;AAAA,UACL,MAAM,EAAE,MAAM;AAAA,UACd,MAAM,EAAE,SAAS,IAAI,SAAS,WAAW,KAAK,MAAM,IAAI,QAAQ,IAAI,UAAU,MAAM,OAAO;AAAA,QAC7F;AAAA,MACF,UAAE;AACA,YAAI,MAAM;AAAA,MACZ;AAAA,IACF,CAAC;AAAA,EACH,CAAC;AAGD,QAAM,cAAc,SAAS,QAAQ,UAAU,EAAE,YAAY,yCAAyC;AACtG,kBAAgB,WAAW;AAC3B,cAAY,OAAO,aAAa,mCAAmC;AACnE,cAAY,OAAO,kBAAkB,mDAAmD;AACxF,cAAY,OAAO,OAAO,YAAkE;AAC1F,UAAM,SAAS,kBAAkB,SAAS,YAAY;AACpD,YAAM,MAAM,mBAAmB,OAAO;AACtC,UAAI;AACF,cAAM,OAAO,iBAAiB,OAAO;AACrC,cAAM,MAAM,WAAW,QAAQ,KAAK,IAAI,KAAK,OAAO;AACpD,cAAM,SAAS,QAAQ,SAAS,cAAc,IAAI,IAAI,QAAQ,MAAM,IAAI;AAExE,cAAM,SAAS,KAAK,IAAI,MAAM,GAAG,MAAM,EAAE;AAEzC,YAAI;AACJ,YAAI;AACF,mBAAS,qBAAqB,IAAI,IAAI;AAAA,YACpC,WAAW,IAAI,QAAQ;AAAA,YACvB,QAAQ,IAAI;AAAA,YACZ,YAAY,aAAa,IAAI;AAAA,YAC7B,UAAU,QAAQ;AAAA,YAClB,MAAM;AAAA,YACN,iBAAiB,IAAI,iBAAiB;AAAA,UACxC,CAAC;AAAA,QACH,SAAS,KAAK;AACZ,gBAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,cAAI,iBAAiB,KAAK,OAAO,GAAG;AAClC,mBAAO;AAAA,cACL,MAAM,EAAE,OAAO,CAAC,GAAG,QAAQ,2BAA2B;AAAA,cACtD,MAAM;AAAA,gBACJ,SAAS,IAAI;AAAA,gBACb,MAAM;AAAA,cACR;AAAA,YACF;AAAA,UACF;AACA,gBAAM;AAAA,QACR;AAEA,cAAMC,gBAAe,IAAI,iBAAiB,cACtC,IAAI,iBAAiB,iBAAiB,OAAO,CAAC,MAAM,EAAE,OAAO,IAC7D,CAAC;AACL,cAAM,QAAQ,OAAO,MAClB,OAAO,CAAC,MAAMA,cAAa,WAAW,KAAK,gBAAgB,EAAE,MAAMA,aAAY,MAAM,EAAE,IAAI,EAC3F,MAAM,GAAG,GAAG;AAEf,eAAO;AAAA,UACL,MAAM,EAAE,MAAM;AAAA,UACd,MAAM;AAAA,YACJ,SAAS,IAAI;AAAA,YACb,WAAW,KAAK;AAAA,YAChB,GAAI,SAAS,EAAE,OAAO,IAAI,CAAC;AAAA,YAC3B,eAAe,OAAO;AAAA,YACtB,aAAa,OAAO;AAAA,YACpB,UAAU,MAAM;AAAA,YAChB,GAAI,MAAM,WAAW,KAAK,OAAO,gBAAgB,IAC7C;AAAA,cACE,MAAM;AAAA,YACR,IACA,CAAC;AAAA,UACP;AAAA,UACA,YAAY,MAAM,MAAM,IAAI,CAAC,GAAG,MAAM,GAAG,IAAI,CAAC,KAAK,EAAE,IAAI,KAAK,EAAE,KAAK,GAAG,EAAE,KAAK,IAAI,KAAK;AAAA,QAC1F;AAAA,MACF,UAAE;AACA,YAAI,MAAM;AAAA,MACZ;AAAA,IACF,CAAC;AAAA,EACH,CAAC;AAGD,QAAM,cAAc,SAAS,QAAQ,UAAU,EAAE,YAAY,2CAA2C;AACxG,cACG,OAAO,mBAAmB,2BAA2B,EACrD,OAAO,gBAAgB,yCAAyC,EAChE,OAAO,aAAa,wCAAwC,EAC5D,OAAO,qBAAqB,gCAAgC;AAC/D,cAAY,OAAO,OAAO,YAAgF;AACxG,UAAM,SAAS,kBAAkB,SAAS,YAAY;AACpD,YAAM,MAAM,mBAAmB,OAAO;AACtC,UAAI;AACF,cAAM,OAAO,iBAAiB,EAAE,MAAM,QAAQ,QAAQ,MAAM,CAAC;AAC7D,cAAM,MAAM,WAAW,QAAQ,KAAK,IAAI,KAAK,OAAO;AACpD,cAAM,OAAO;AAAA,UACX,IAAI;AAAA,UACJ,yBAAyB;AAAA,UACzB,EAAE,SAAS,KAAK,WAAW,EAAE;AAAA,QAC/B;AACA,cAAM,QAAQ;AAAA,UACZ,KAAK,IAAI,CAAC,OAAO,EAAE,UAAU,EAAE,WAAW,MAAM,EAAE,MAAM,IAAI,EAAE,GAAG,EAAE;AAAA,UACnE;AAAA,QACF;AACA,eAAO;AAAA,UACL,MAAM,EAAE,MAAM;AAAA,UACd,MAAM,EAAE,SAAS,IAAI,SAAS,WAAW,KAAK,MAAM,UAAU,MAAM,OAAO;AAAA,UAC3E,YAAY,MACV,MACG;AAAA,YACC,CAAC,GAAG,MAAM,GAAG,IAAI,CAAC,KAAK,EAAE,IAAI,aAAa,EAAE,aAAa,UAAU,EAAE,UAAU,MAAM,EAAE,aAAa;AAAA,UACtG,EACC,KAAK,IAAI,KAAK;AAAA,QACrB;AAAA,MACF,UAAE;AACA,YAAI,MAAM;AAAA,MACZ;AAAA,IACF,CAAC;AAAA,EACH,CAAC;AACH;AAIA,SAAS,YAAY,WAAmB,SAAiD;AACvF,QAAM,EAAE,UAAU,IAAI,YAAY;AAClC,QAAM,KAAK,UAAU,KAAK,SAAS;AACnC,MAAI,CAAC,IAAI;AACP,YAAQ,MAAM,WAAW,SAAS,YAAY;AAC9C,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,OAAO,eAAe,EAAE;AAC9B,QAAM,WAAW,mBAAmB,EAAE;AACtC,QAAM,aAAa,kBAAkB,EAAE,EAAE,MAAM,GAAG,SAAS,QAAQ,GAAG,CAAC;AAEvE,MAAI,QAAQ,WAAW,QAAQ;AAC7B,YAAQ,IAAI,KAAK,UAAU,EAAE,MAAM,UAAU,WAAW,GAAG,MAAM,CAAC,CAAC;AAAA,EACrE,OAAO;AACL,YAAQ,IAAI;AAAA,WAAc,MAAM,IAAI,EAAE;AACtC,YAAQ,IAAI,aAAa,MAAM,QAAQ,YAAY,MAAM,IAAI,EAAE;AAC/D,YAAQ,IAAI,mBAAmB,SAAS,aAAa,EAAE;AACvD,YAAQ,IAAI,kBAAkB,SAAS,YAAY,EAAE;AACrD,UAAM,QACJ,SAAS,kBAAkB,SAAS,gBAChC,GAAG,IAAI,KAAK,SAAS,iBAAiB,GAAI,EAAE,mBAAmB,CAAC,MAAM,IAAI,KAAK,SAAS,gBAAgB,GAAI,EAAE,mBAAmB,CAAC,KAClI;AACN,YAAQ,IAAI,eAAe,KAAK,EAAE;AAElC,QAAI,WAAW,SAAS,GAAG;AACzB,cAAQ,IAAI;AAAA,wBAA2B,QAAQ,GAAG,IAAI;AACtD,iBAAW,CAAC,GAAG,CAAC,KAAK,WAAW,QAAQ,GAAG;AACzC,gBAAQ,IAAI,KAAK,IAAI,CAAC,KAAK,EAAE,IAAI,MAAM,EAAE,YAAY,cAAc,EAAE,UAAU,IAAI;AAAA,MACrF;AAAA,IACF;AAAA,EACF;AAEA,YAAU,SAAS;AACrB;;;ACvTA,SAAS,aAAa,KAAsC;AAC1D,SAAO,IAAI,iBAAiB,cAAc,IAAI,iBAAiB,iBAAiB,OAAO,CAAC,MAAM,EAAE,OAAO,IAAI,CAAC;AAC9G;AAEO,SAAS,iBAAiB,OAAmC;AAClE,SAAO,WAAW,OAAO,KAAK,KAAM,WAAW,CAAC;AAClD;AAEO,SAAS,aACd,MACA,SACA,kBACyD;AACzD,MAAI,WAAW,KAAK,KAAK,UAAU,SAAS;AAC1C,WAAO,EAAE,MAAM,WAAW,iBAAiB;AAAA,EAC7C;AACA,SAAO,EAAE,MAAM,KAAK,MAAM,GAAG,OAAO,GAAG,WAAW,KAAK;AACzD;AAEA,SAAS,aAAa,OAAuB;AAC3C,SAAO,MAAM,QAAQ,uBAAuB,MAAM;AACpD;AAEA,IAAM,iBAAiB,OAAO,2BAA2B,YAAY,OAAO;AAC5E,IAAM,oBAAoB,OAAO;AACjC,IAAM,eAAe,oBAAI,IAAI;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAED,SAAS,eAAe,MAAsB;AAC5C,QAAM,UAAU,aAAa,IAAI;AACjC,SAAO,OAAO,UAAU,OAAO,MAAM,OAAO,OAAO,MAAM,UAAU,MAAM,OAAO,SAAS,OAAO,MAAM,OAAO;AAC/G;AAEA,SAAS,qBAAqB,OAAuB;AACnD,QAAM,UAAU,MAAM,KAAK;AAC3B,MAAI,QAAQ,WAAW,GAAG,KAAK,QAAQ,SAAS,GAAG,EAAG,QAAO,QAAQ,MAAM,GAAG,EAAE;AAChF,QAAM,QAAQ,QAAQ,CAAC;AACvB,OAAK,UAAU,OAAO,UAAU,OAAO,UAAU,QAAQ,QAAQ,SAAS,KAAK,GAAG;AAChF,WAAO,QAAQ,MAAM,GAAG,EAAE;AAAA,EAC5B;AACA,SAAO;AACT;AAEA,SAAS,sBAA8B;AACrC,QAAM,eAAe,OAAO,SAAS,cAAc;AACnD,SAAO,GAAG,YAAY,GAAG,eAAe,SAAS,CAAC;AACpD;AAEA,SAAS,mBAAmB,OAAwB;AAClD,QAAM,cAAc,MAAM,MAAM,+BAA+B;AAC/D,MAAI,CAAC,YAAa,QAAO;AAEzB,QAAM,aAAa,YAAY,CAAC;AAChC,QAAM,eAAe,oBAAoB;AACzC,MAAI,0BAA0B,KAAK,UAAU,GAAG;AAC9C,WAAO,IAAI,OAAO,OAAO,wBAAwB,YAAY,GAAG,iBAAiB,IAAI,GAAG,EAAE,KAAK,KAAK;AAAA,EACtG;AAEA,QAAM,eAAe,MAAM;AAAA,IACzB,IAAI,OAAO,OAAO,wBAAwB,YAAY,oBAAoB,cAAc,OAAO,IAAI;AAAA,EACrG;AACA,QAAM,UAAU,oBAAI,IAAI,CAAC,SAAS,CAAC;AACnC,aAAW,SAAS,cAAc;AAChC,UAAM,QAAQ,MAAM,CAAC,IAAI,qBAAqB,MAAM,CAAC,CAAC,EAAE,YAAY,IAAI;AACxE,QAAI,SAAS,CAAC,aAAa,IAAI,KAAK,EAAG,SAAQ,IAAI,KAAK;AAAA,EAC1D;AAEA,SAAO,CAAC,GAAG,OAAO,EAAE;AAAA,IAAK,CAAC,UACxB,IAAI,OAAO,GAAG,eAAe,KAAK,CAAC,8BAA8B,GAAG,EAAE,KAAK,UAAU;AAAA,EACvF;AACF;AAEO,SAAS,wBAAwB,OAAe,KAAoB;AACzE,MAAI,IAAK;AACT,MAAI,CAAC,2DAA2D,KAAK,KAAK,KAAK,CAAC,mBAAmB,KAAK,GAAG;AACzG;AAAA,EACF;AAEA,QAAM,IAAI,WAAW;AAAA,IACnB,MAAM;AAAA,IACN,SAAS;AAAA,IACT,MAAM;AAAA,EACR,CAAC;AACH;AAEO,SAAS,8BAA8B,SAA4B,KAAoB;AAC5F,MAAI,IAAK;AACT,MAAI,CAAC,QAAQ,KAAK,CAAC,WAAW,qBAAqB,MAAM,EAAE,YAAY,MAAM,SAAS,EAAG;AAEzF,QAAM,IAAI,WAAW;AAAA,IACnB,MAAM;AAAA,IACN,SAAS;AAAA,IACT,MAAM;AAAA,EACR,CAAC;AACH;AAeO,SAAS,gBAAgB,OAAgC;AAC9D,MAAI,MAAM,WAAW,EAAG,QAAO;AAC/B,QAAM,SAAS,MAAM,IAAI,CAAC,MAAM;AAC9B,UAAM,SAAS,KAAK,EAAE,EAAE,KAAK,EAAE,KAAK,MAAM,EAAE,KAAK,KAAK,EAAE,QAAQ,UAAU,EAAE,aAAa,KAAK,IAAI,CAAC;AACnG,WAAO,EAAE,UAAU,GAAG,MAAM;AAAA,EAAK,EAAE,OAAO,KAAK;AAAA,EACjD,CAAC;AACD,SAAO,aAAa,MAAM,MAAM;AAAA;AAAA,EAAO,OAAO,KAAK,MAAM,CAAC;AAC5D;AAEO,SAAS,sBAAsBC,UAAwB;AAC5D,QAAM,YAAYA,SAAQ,QAAQ,QAAQ,EAAE,YAAY,wCAAwC;AAGhG,YACG,QAAQ,MAAM,EACd,YAAY,oDAAoD,EAChE,OAAO,mBAAmB,2BAA2B,EACrD,OAAO,eAAe,YAAY,EAClC,OAAO,eAAe,UAAU,EAChC,OAAO,gBAAgB,2BAA2B,EAClD,OAAO,gBAAgB,+BAA+B,EACtD,OAAO,eAAe,qCAAqC,EAC3D,OAAO,qBAAqB,gCAAgC,EAC5D;AAAA,IACC,OAAO,YAQD;AACJ,YAAM,SAAS,eAAe,SAAS,OAAO,WAAW;AACvD,cAAM,MAAM,mBAAmB,OAAO;AACtC,YAAI;AACF,gBAAM,OAAO,iBAAiB,OAAO;AACrC,gBAAM,QAAQ,WAAW,QAAQ,OAAO,IAAI,KAAK,SAAS;AAC1D,gBAAM,aACJ,KAAK,YAAY,UAAa,KAAK,UAAU,SACzC,EAAE,SAAS,KAAK,WAAW,GAAG,OAAO,KAAK,SAAS,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI,EAAE,IACjF;AAGN,gBAAM,OAAO,oBAAoB,IAAI,IAAI,EAAE,OAAO,QAAQ,IAAI,WAAW,CAAC;AAC1E,gBAAM,QAAQ,aAAa,GAAG;AAC9B,gBAAM,YAAY,IAAI,iBAAiB;AAEvC,cAAI,UAAU;AACd,gBAAM,QAAQ,KACX,OAAO,CAAC,QAAQ;AACf,gBAAI,IAAI,WAAW,iBAAiB,IAAI,SAAS,SAAS,GAAG;AAC3D;AACA,qBAAO;AAAA,YACT;AACA,mBAAO;AAAA,UACT,CAAC,EACA,IAAI,CAAC,SAAS;AAAA,YACb,IAAI,IAAI;AAAA,YACR,OAAO,WAAW,IAAI,OAAO;AAAA,YAC7B,OAAO,WAAW,IAAI,KAAK;AAAA,YAC3B,UAAU,IAAI;AAAA,YACd,cAAc,IAAI;AAAA,YAClB,SAAS,IAAI,UAAU,gBAAgB,IAAI,SAAS,KAAK,IAAI,IAAI;AAAA,UACnE,EAAE,EACD,OAAO,CAAC,QAAS,QAAQ,SAAS,IAAI,WAAW,IAAI,SAAS,QAAQ,KAAK,IAAI,IAAK,EACpF,MAAM,GAAG,KAAK;AAEjB,cAAI,MAAM,WAAW,GAAG;AACtB,kBAAM,MAAM,oBAAoB,IAAI,IAAI,EAAE,OAAO,EAAE,CAAC;AACpD,gBAAI,IAAI,WAAW,GAAG;AACpB,oBAAM,OAAO;AACb,qBAAO;AAAA,gBACL,MACE,WAAW,UACP,EAAE,MAAM,kCAAkC,QAAQ,0BAA0B,IAC5E,EAAE,OAAO,CAAC,GAAG,QAAQ,0BAA0B;AAAA,gBACrD,MAAM,EAAE,SAAS,IAAI,SAAS,KAAK;AAAA,cACrC;AAAA,YACF;AAAA,UACF;AAEA,gBAAM,OAAO;AAAA,YACX,SAAS,IAAI;AAAA,YACb,WAAW,KAAK;AAAA,YAChB,UAAU,MAAM;AAAA,YAChB,GAAI,UAAU,IAAI,EAAE,UAAU,CAAC,GAAG,OAAO,iCAAiC,EAAE,IAAI,CAAC;AAAA,UACnF;AACA,iBAAO;AAAA,YACL,MAAM,WAAW,UAAU,EAAE,MAAM,gBAAgB,KAAK,EAAE,IAAI,EAAE,MAAM;AAAA,YACtE;AAAA,YACA,YAAY,MAAM,gBAAgB,KAAK;AAAA,UACzC;AAAA,QACF,UAAE;AACA,cAAI,MAAM;AAAA,QACZ;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AAGF,YACG,QAAQ,MAAM,EACd,YAAY,uCAAuC,EACnD,eAAe,qBAAqB,6BAA6B,EACjE,OAAO,mBAAmB,2BAA2B,EACrD,OAAO,eAAe,qCAAqC,EAC3D,OAAO,qBAAqB,gCAAgC,EAC5D,OAAO,oBAAoB,4CAA4C,EACvE,OAAO,mBAAmB,gCAAgC,EAC1D,OAAO,UAAU,wCAAwC,EACzD,OAAO,kBAAkB,6CAA6C,EACtE,OAAO,gBAAgB,mCAAmC,EAC1D,OAAO,SAAS,gFAAgF,EAChG,OAAO,aAAa,2CAA2C,EAC/D;AAAA,IACC,OAAO,YAYD;AACJ,YAAM,SAAS,eAAe,SAAS,OAAO,WAAW;AACvD,kCAA0B,QAAQ,OAAO;AACzC,cAAM,YAAY,OAAO,QAAQ,EAAE;AACnC,YAAI,CAAC,OAAO,UAAU,SAAS,KAAK,YAAY,GAAG;AACjD,gBAAM,IAAI,WAAW;AAAA,YACnB,MAAM;AAAA,YACN,SAAS,uBAAuB,QAAQ,EAAE;AAAA,YAC1C,MAAM;AAAA,UACR,CAAC;AAAA,QACH;AACA,cAAM,MAAM,mBAAmB,OAAO;AACtC,YAAI;AACF,2BAAiB,KAAK,OAAO;AAC7B,gBAAM,QAAQ,WAAW,QAAQ,OAAO,KAAK,KAAK,SAAS;AAC3D,gBAAM,UAAU,mBAAmB,IAAI,IAAI,WAAW,KAAK;AAC3D,cAAI,CAAC,SAAS;AACZ,kBAAM,IAAI,WAAW;AAAA,cACnB,MAAM;AAAA,cACN,SAAS,WAAW,SAAS;AAAA,cAC7B,MAAM;AAAA,YACR,CAAC;AAAA,UACH;AACA,gBAAM,OAAgC;AAAA,YACpC,SAAS,IAAI;AAAA,YACb,SAAS;AAAA,cACP,IAAI,QAAQ;AAAA,cACZ,OAAO,WAAW,QAAQ,OAAO;AAAA,cACjC,OAAO,WAAW,QAAQ,KAAK;AAAA,cAC/B,cAAc,QAAQ;AAAA,cACtB,cAAc,QAAQ;AAAA,YACxB;AAAA,YACA,cAAc,QAAQ;AAAA,UACxB;AACA,iBAAO,oBAAoB,QAAQ,KAAK,SAAS,QAAQ,UAAU,MAAM;AAAA,YACvE,UAAU;AAAA,UACZ,CAAC;AAAA,QACH,UAAE;AACA,cAAI,MAAM;AAAA,QACZ;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AACJ;AAEO,SAAS,oBAAoBA,UAAwB;AAC1D,EAAAA,SACG,QAAQ,KAAK,EACb,YAAY,wFAAwF,EACpG,SAAS,WAAW,yBAAyB,EAC7C,OAAO,mBAAmB,2BAA2B,EACrD,OAAO,eAAe,kCAAkC,EACxD,OAAO,qBAAqB,gCAAgC,EAC5D,OAAO,SAAS,0DAA0D,EAC1E,OAAO,OAAO,OAAe,YAAkF;AAC9G,UAAM,SAAS,OAAO,SAAS,YAAY;AACzC,YAAM,MAAM,mBAAmB,OAAO;AACtC,UAAI;AACF,YAAI,CAAC,IAAI,UAAU;AACjB,gBAAM,IAAI,WAAW;AAAA,YACnB,MAAM;AAAA,YACN,SAAS;AAAA,YACT,MAAM;AAAA,UACR,CAAC;AAAA,QACH;AACA,yBAAiB,KAAK,OAAO;AAC7B,gCAAwB,OAAO,CAAC,CAAC,QAAQ,GAAG;AAC5C,cAAM,QAAQ,iBAAiB,QAAQ,KAAK;AAE5C,YAAI;AACJ,YAAI;AACF,mBAAS,mBAAmB,IAAI,IAAI,OAAO,KAAK;AAAA,QAClD,SAAS,KAAK;AACZ,gBAAM,IAAI,WAAW;AAAA,YACnB,MAAM;AAAA,YACN,SAAS,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,YACxD,MAAM;AAAA,UACR,CAAC;AAAA,QACH;AACA,sCAA8B,OAAO,SAAS,CAAC,CAAC,QAAQ,GAAG;AAE3D,cAAM,UAAU,aAAa,OAAO,MAAM,OAAO,OAAO,SAAS;AACjE,YAAI,OAAO,QAAQ;AACnB,YAAI,UAAU;AACd,YAAI,CAAC,QAAQ,KAAK;AAChB,gBAAM,QAAQ,aAAa,GAAG;AAC9B,gBAAM,YAAY,IAAI,iBAAiB;AACvC,gBAAM,YAAuC,CAAC;AAC9C,qBAAW,OAAO,MAAM;AACtB,gBAAI,OAAO;AACX,kBAAM,QAAiC,CAAC;AACxC,uBAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,GAAG,GAAG;AAC9C,kBAAI,OAAO,UAAU,UAAU;AAC7B,oBAAI,iBAAiB,OAAO,SAAS,GAAG;AACtC,yBAAO;AACP;AAAA,gBACF;AACA,sBAAM,GAAG,IAAI,gBAAgB,OAAO,KAAK;AAAA,cAC3C,OAAO;AACL,sBAAM,GAAG,IAAI;AAAA,cACf;AAAA,YACF;AACA,gBAAI,KAAM;AAAA,gBACL,WAAU,KAAK,KAAK;AAAA,UAC3B;AACA,iBAAO;AAAA,QACT;AAEA,eAAO;AAAA,UACL,MAAM,EAAE,SAAS,OAAO,SAAS,MAAM,UAAU,KAAK,QAAQ,WAAW,QAAQ,UAAU;AAAA,UAC3F,MAAM;AAAA,YACJ,SAAS,IAAI;AAAA,YACb,GAAI,QAAQ,MAAM,EAAE,YAAY,EAAE,KAAK,KAAK,EAAE,IAAI,CAAC;AAAA,YACnD,GAAI,UAAU,IAAI,EAAE,UAAU,CAAC,GAAG,OAAO,8BAA8B,EAAE,IAAI,CAAC;AAAA,UAChF;AAAA,UACA,YAAY,MACV,KAAK,WAAW,IACZ,gBACA,KAAK,IAAI,CAAC,MAAM,KAAK,UAAU,CAAC,CAAC,EAAE,KAAK,IAAI,IAC5C;AAAA,EAAK,KAAK,MAAM,UAAU,QAAQ,YAAY,iBAAiB,EAAE;AAAA,QACzE;AAAA,MACF,UAAE;AACA,YAAI,MAAM;AAAA,MACZ;AAAA,IACF,CAAC;AAAA,EACH,CAAC;AAEH,EAAAA,SACG,QAAQ,QAAQ,EAChB,YAAY,oDAAoD,EAChE,OAAO,mBAAmB,2BAA2B,EACrD,OAAO,qBAAqB,gCAAgC,EAC5D,OAAO,OAAO,YAAmD;AAChE,UAAM,SAAS,UAAU,SAAS,YAAY;AAC5C,YAAM,MAAM,mBAAmB,OAAO;AACtC,UAAI;AACF,cAAM,QAAQ,kBAAkB,IAAI,EAAE;AACtC,eAAO;AAAA,UACL,MAAM,EAAE,MAAM;AAAA,UACd,MAAM,EAAE,SAAS,IAAI,SAAS,UAAU,MAAM,OAAO;AAAA,UACrD,YAAY,MAAM,MAAM,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,KAAK,MAAM;AAAA,QACvD;AAAA,MACF,UAAE;AACA,YAAI,MAAM;AAAA,MACZ;AAAA,IACF,CAAC;AAAA,EACH,CAAC;AACL;;;AC1YO,SAAS,sBAAsBC,UAAwB;AAC5D,0BAAwBA,QAAO;AAC/B,yBAAuBA,QAAO;AAC9B,0BAAwBA,QAAO;AAC/B,wBAAsBA,QAAO;AAC7B,wBAAsBA,QAAO;AAC7B,sBAAoBA,QAAO;AAC3B,wBAAsBA,QAAO;AAC/B;AAEO,SAAS,uBAAuB,QAAoC,KAAoB;AAC7F,MAAI,CAAC,OAAO,IAAI,WAAW;AACzB,UAAM,IAAI,WAAW;AAAA,MACnB,MAAM;AAAA,MACN,SAAS;AAAA,MACT,MAAM;AAAA,IACR,CAAC;AAAA,EACH;AACA,MAAI,IAAK,yBAAwB,KAAK,KAAK;AAC7C;AAIA,SAAS,sBAAsBA,UAAwB;AACrD,EAAAA,SACG,QAAQ,iCAAiC,EAAE,QAAQ,KAAK,CAAC,EACzD,YAAY,2CAA2C,EACvD,OAAO,eAAe,yBAAyB,IAAI,EACnD,OAAO,qBAAqB,8BAA8B,OAAO,EACjE,OAAO,CAAC,WAAmB,SAAiB,YAA+C;AAC1F,YAAQ;AAAA,MACN;AAAA,IACF;AACA,UAAM,EAAE,UAAU,IAAI,YAAY;AAClC,UAAM,KAAK,UAAU,KAAK,SAAS;AACnC,QAAI,CAAC,IAAI;AACP,cAAQ,MAAM,WAAW,SAAS,YAAY;AAC9C,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,UAAM,QAAQ,SAAS,QAAQ,KAAK;AACpC,UAAM,SAAS,mBAAmB,IAAI,SAAS,EAAE,MAAM,CAAC;AAExD,QAAI,QAAQ,WAAW,QAAQ;AAC7B,cAAQ,IAAI,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC;AAAA,IAC7C,OAAO;AACL,cAAQ;AAAA,QACN,WAAW,OAAO,OAAO,OAAO,KAAK,aAAa,OAAO,UAAU,qBAAqB,QAAQ,MAAM,EAAE;AAAA;AAAA,MAC1G;AACA,iBAAW,OAAO,OAAO,UAAU;AACjC,cAAM,OAAO,IAAI,KAAK,IAAI,YAAY,GAAI,EAAE,eAAe;AAC3D,gBAAQ,IAAI,MAAM,IAAI,KAAK,IAAI,UAAU,KAAK,IAAI,OAAO,EAAE;AAAA,MAC7D;AAAA,IACF;AAEA,cAAU,SAAS;AAAA,EACrB,CAAC;AAEH,EAAAA,SACG,QAAQ,sBAAsB,EAAE,QAAQ,KAAK,CAAC,EAC9C,YAAY,+BAA+B,EAC3C,eAAe,eAAe,qBAAqB,EACnD,OAAO,qBAAqB,8BAA8B,OAAO,EACjE,OAAO,CAAC,WAAmB,YAA6C;AACvE,YAAQ;AAAA,MACN;AAAA,IACF;AACA,UAAM,EAAE,QAAQ,UAAU,IAAI,YAAY;AAC1C,UAAM,KAAK,UAAU,KAAK,SAAS;AACnC,QAAI,CAAC,IAAI;AACP,cAAQ,MAAM,WAAW,SAAS,YAAY;AAC9C,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,QAAI;AACF,6BAAuB,QAAQ,QAAQ,GAAG;AAC1C,YAAM,SAAS,mBAAmB,IAAI,QAAQ,GAAG;AACjD,oCAA8B,OAAO,SAAS,KAAK;AACnD,UAAI,QAAQ,WAAW,QAAQ;AAC7B,gBAAQ,IAAI,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC;AAAA,MAC7C,OAAO;AACL,YAAI,OAAO,KAAK,WAAW,GAAG;AAC5B,kBAAQ,IAAI,aAAa;AAAA,QAC3B,OAAO;AACL,qBAAW,OAAO,SAAS,OAAO,IAAI;AACtC,kBAAQ,IAAI;AAAA,EAAK,OAAO,QAAQ,UAAU,OAAO,YAAY,iBAAiB,EAAE,EAAE;AAAA,QACpF;AAAA,MACF;AAAA,IACF,SAAS,KAAK;AACZ,UAAI,eAAe,YAAY;AAC7B,gBAAQ,MAAM,GAAG,IAAI,IAAI,KAAK,IAAI,OAAO,EAAE;AAC3C,YAAI,IAAI,KAAM,SAAQ,MAAM,SAAS,IAAI,IAAI,EAAE;AAC/C,gBAAQ,KAAK,CAAC;AAAA,MAChB;AACA,cAAQ,MAAM,cAAc,eAAe,QAAQ,IAAI,UAAU,GAAG,EAAE;AACtE,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,cAAU,SAAS;AAAA,EACrB,CAAC;AACL;AAEA,SAAS,WAAW,SAAmB,MAAuC;AAC5E,QAAM,SAAS,QAAQ,IAAI,CAAC,QAAQ;AAClC,UAAM,UAAU,KAAK,OAAO,CAAC,KAAK,QAAQ;AACxC,YAAM,MAAM,OAAO,IAAI,GAAG,KAAK,EAAE;AACjC,aAAO,KAAK,IAAI,KAAK,IAAI,MAAM;AAAA,IACjC,GAAG,CAAC;AACJ,WAAO,KAAK,IAAI,IAAI,QAAQ,KAAK,IAAI,SAAS,EAAE,CAAC;AAAA,EACnD,CAAC;AAED,QAAM,SAAS,QAAQ,IAAI,CAAC,KAAK,MAAM,IAAI,OAAO,OAAO,CAAC,CAAC,CAAC,EAAE,KAAK,KAAK;AACxE,QAAM,YAAY,OAAO,IAAI,CAAC,MAAM,IAAI,OAAO,CAAC,CAAC,EAAE,KAAK,KAAK;AAC7D,UAAQ,IAAI,MAAM;AAClB,UAAQ,IAAI,SAAS;AAErB,aAAW,OAAO,MAAM;AACtB,UAAM,OAAO,QACV,IAAI,CAAC,KAAK,MAAM;AACf,YAAM,MAAM,OAAO,IAAI,GAAG,KAAK,EAAE;AACjC,aAAO,IAAI,SAAS,KAAK,IAAI,MAAM,GAAG,EAAE,IAAI,QAAQ,IAAI,OAAO,OAAO,CAAC,CAAC;AAAA,IAC1E,CAAC,EACA,KAAK,KAAK;AACb,YAAQ,IAAI,IAAI;AAAA,EAClB;AACF;;;ACtIA,IAAM,eAAe,oBAAI,IAAI,CAAC,YAAY,WAAW,YAAY,SAAS,UAAU,OAAO,UAAU,UAAU,CAAC;AAuChH,IAAM,aAAqC;AAAA,EACzC,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AACP;AAIA,IAAM,WAA8B;AAAA,EAClC,EAAE,MAAM,yCAAyC,SAAS,qDAAqD;AAAA,EAC/G;AAAA,IACE,MAAM;AAAA,IACN,SAAS;AAAA,EACX;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,SAAS;AAAA,EACX;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,SAAS;AAAA,EACX;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,SAAS;AAAA,EACX;AAAA,EACA,EAAE,MAAM,uBAAuB,SAAS,gDAAgD;AAAA,EACxF,EAAE,MAAM,sBAAsB,SAAS,2EAA2E;AAAA,EAClH,EAAE,MAAM,wBAAwB,SAAS,2DAA2D;AAAA,EACpG;AAAA,IACE,MAAM;AAAA,IACN,SAAS;AAAA,EACX;AAAA,EACA,EAAE,MAAM,2BAA2B,SAAS,sCAAsC;AACpF;AAEA,IAAM,QAAkB;AAAA,EACtB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAgBA,SAAS,SAAS,KAA2B;AAC3C,SAAO,IAAI,YAAY;AACzB;AAEA,SAAS,iBAAiB,KAAkB,QAAiC;AAC3E,QAAM,QAAuB,IAAI,uBAAuB,CAAC,GAAG,IAAI,CAAC,SAAS;AAAA,IACxE,MAAM,IAAI,KAAK;AAAA,IACf,UAAU,IAAI;AAAA,IACd,UAAU,IAAI;AAAA,IACd,GAAI,IAAI,cAAc,EAAE,aAAa,IAAI,YAAY,IAAI,CAAC;AAAA,EAC5D,EAAE;AACF,QAAM,UAA4B,IAAI,QACnC,OAAO,CAAC,QAAQ,CAAC,IAAI,MAAM,EAC3B,IAAI,CAAC,SAAS;AAAA,IACb,OAAO,IAAI;AAAA,IACX,aAAa,IAAI;AAAA,IACjB,GAAI,IAAI,iBAAiB,SAAY,EAAE,SAAS,IAAI,aAAa,IAAI,CAAC;AAAA,EACxE,EAAE;AACJ,SAAO;AAAA,IACL,MAAM,SAAS,GAAG,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,IAAI,KAAK;AAAA,IACpD,aAAa,IAAI,YAAY;AAAA,IAC7B;AAAA,IACA;AAAA,EACF;AACF;AAEA,SAAS,qBAAqB,KAAkB,QAAgB,MAA+B;AAC7F,MAAI,IAAI,SAAS,WAAW,GAAG;AAC7B,SAAK,KAAK,iBAAiB,KAAK,MAAM,CAAC;AACvC;AAAA,EACF;AACA,QAAMC,QAAO,SAAS,GAAG,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,IAAI,KAAK;AAC3D,aAAW,OAAO,IAAI,UAAU;AAC9B,QAAI,SAAS,GAAG,EAAG;AACnB,yBAAqB,KAAKA,OAAM,IAAI;AAAA,EACtC;AACF;AAEO,SAAS,cAAcC,UAAkB,SAA2B;AACzE,QAAM,WAA8B,CAAC;AACrC,QAAM,cAA4D,CAAC;AAEnE,aAAW,OAAOA,SAAQ,UAAsC;AAC9D,QAAI,SAAS,GAAG,EAAG;AACnB,QAAI,aAAa,IAAI,IAAI,KAAK,CAAC,GAAG;AAChC,2BAAqB,KAAK,IAAI,QAAQ;AAAA,IACxC,OAAO;AACL,kBAAY,KAAK,EAAE,MAAM,IAAI,KAAK,GAAG,aAAa,IAAI,YAAY,EAAE,CAAC;AAAA,IACvE;AAAA,EACF;AAEA,SAAO;AAAA,IACL,MAAM;AAAA,IACN;AAAA,IACA,YAAY;AAAA,IACZ,SAAS,CAAC,SAAS,QAAQ,MAAM;AAAA,IACjC;AAAA,IACA;AAAA,IACA,WAAW;AAAA,IACX,UAAU;AAAA,IACV,OAAO;AAAA,EACT;AACF;AAEO,SAAS,wBAAwBA,UAAkB,SAAuB;AAC/E,EAAAA,SACG,QAAQ,UAAU,EAClB,YAAY,wDAAwD,EACpE,OAAO,MAAM;AACZ,YAAQ,IAAI,KAAK,UAAU,cAAcA,UAAS,OAAO,GAAG,MAAM,CAAC,CAAC;AAAA,EACtE,CAAC;AACL;;;Af/JA,IAAM,UAAU,IAAI,QAAQ;AAE5B,QAAQ,KAAK,SAAS,EAAE,YAAY,sCAAsC,EAAE,QAAQ,WAAW,GAAG,eAAe;AAEjH,QAAQ,KAAK,aAAa,OAAO,cAAc,kBAAkB;AAC/D,MAAI,cAAc,KAAK,MAAM,SAAU;AACvC,QAAM,EAAE,2BAA2B,IAAI,MAAM,OAAO,+BAAkB;AACtE,QAAM,2BAA2B;AACnC,CAAC;AAED,QACG,QAAQ,QAAQ,EAChB,YAAY,0CAA0C,EACtD,OAAO,YAAY;AAClB,QAAM,EAAE,qBAAqB,IAAI,MAAM,OAAO,+BAAkB;AAChE,QAAM,SAAS,MAAM,qBAAqB;AAAA,IACxC,OAAO,CAAC,SAAS,QAAQ,OAAO,MAAM,IAAI;AAAA,EAC5C,CAAC;AAED,MAAI,OAAO,SAAS;AAClB,YAAQ,MAAM,0EAA0E;AACxF;AAAA,EACF;AAEA,UAAQ,MAAM,oBAAoB,OAAO,SAAS,eAAe;AAAA,CAAI;AACrE,UAAQ,WAAW;AACrB,CAAC;AAIH,sBAAsB,OAAO;AAC7B,wBAAwB,SAAS,WAAW,CAAC;AAE7C,QACG,QAAQ,eAAe,EACvB,YAAY,oGAAoG,EAChH,OAAO,qBAAqB,gDAAgD,EAC5E,OAAO,iBAAiB,yCAAyC,EACjE,OAAO,OAAO,MAAM,YAAY;AAC/B,MAAI,CAAI,eAAW,IAAI,GAAG;AACxB,YAAQ,MAAM,mBAAmB,IAAI,EAAE;AACvC,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,EAAE,cAAc,aAAa,IAAI,MAAM,OAAO,uBAAU;AAC9D,QAAM,EAAE,WAAW,aAAa,IAAI,YAAY;AAChD,QAAM,gBAAgB,qBAAqB;AAE3C,wBAAsB;AAEtB,QAAM,SAAS,aAAa,IAAI;AAChC,MAAI,CAAC,UAAU,CAAC,QAAQ,QAAQ;AAC9B,YAAQ,MAAM,6BAA6B,IAAI,EAAE;AACjD,YAAQ,MAAM,0FAA0F;AACxG,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,UAAQ,IAAI,cAAc,IAAI,EAAE;AAChC,MAAI,OAAQ,SAAQ,IAAI,aAAa,OAAO,IAAI,KAAK,OAAO,QAAQ,GAAG;AAEvE,MAAI;AACF,UAAM,SAAS,MAAM,aAAa,WAAW,MAAM;AAAA,MACjD,UAAU,QAAQ;AAAA,MAClB;AAAA,MACA,YAAY,CAAC,MAAM;AACjB,gBAAQ,OAAO,MAAM,OAAO,EAAE,KAAK,KAAK,EAAE,QAAQ,GAAG;AAAA,MACvD;AAAA,IACF,CAAC;AAED,QAAI,OAAO,SAAS;AAClB,cAAQ,IAAI;AAAA;AAAA,kBAAuB;AACnC,cAAQ,IAAI,iBAAiB,OAAO,SAAS,EAAE;AAC/C,cAAQ,IAAI,uBAAuB,OAAO,aAAa,mBAAmB,CAAC,EAAE;AAC7E,cAAQ,IAAI,uBAAuB,OAAO,aAAa,mBAAmB,CAAC,EAAE;AAE7E,UAAI,OAAO,WAAW;AACpB,YAAI;AACF,gBAAM,EAAE,oBAAAC,qBAAoB,8BAA8B,oBAAoB,IAC5E,MAAM,OAAO,oBAA2B;AAC1C,gBAAM,UAAU,oBAAoB;AAAA,YAClC,6BAA6B,SAAS;AAAA,YACtC,IAAIA,oBAAmB,aAAa,aAAa,CAAC;AAAA,YAClD,OAAO;AAAA,UACT;AACA,cAAI,QAAQ,SAAS;AACnB,oBAAQ,IAAI,0BAA0B,QAAQ,OAAO,EAAE;AAAA,UACzD;AAAA,QACF,SAAS,UAAU;AACjB,kBAAQ,KAAK,kCAAkC,oBAAoB,QAAQ,SAAS,UAAU,QAAQ,EAAE;AAAA,QAC1G;AAAA,MACF;AAAA,IACF,OAAO;AACL,cAAQ,MAAM;AAAA;AAAA,iBAAsB,OAAO,KAAK,EAAE;AAClD,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF,SAAS,KAAK;AACZ,YAAQ,MAAM;AAAA;AAAA,gBAAqB,eAAe,QAAQ,IAAI,UAAU,GAAG,EAAE;AAC7E,YAAQ,KAAK,CAAC;AAAA,EAChB,UAAE;AACA,cAAU,SAAS;AAAA,EACrB;AACF,CAAC;AAEH,QACG,QAAQ,SAAS,EACjB,YAAY,yCAAyC,EACrD,OAAO,YAAY;AAClB,QAAM,EAAE,oBAAoB,IAAI,MAAM,OAAO,uBAAU;AACvD,QAAM,UAAU,oBAAoB;AACpC,UAAQ,IAAI,GAAG,QAAQ,MAAM;AAAA,CAAyB;AACtD,aAAW,KAAK,SAAS;AACvB,YAAQ,IAAI,KAAK,EAAE,GAAG,OAAO,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,EAAE,QAAQ,MAAM,EAAE,WAAW,KAAK,IAAI,CAAC,GAAG;AAAA,EAC3F;AACF,CAAC;AAEH,QACG,QAAQ,KAAK,EACb,YAAY,yEAAyE,EACrF,OAAO,YAAY;AAClB,QAAM,EAAE,kBAAkB,IAAI,MAAM,OAAO,oBAAO;AAClD,QAAM,kBAAkB;AAC1B,CAAC;AAEH,QACG,QAAQ,MAAM,EACd,YAAY,+CAA+C,EAC3D,OAAO,qBAAqB,wBAAwB,EACpD,OAAO,qBAAqB,iCAAiC,EAC7D,OAAO,yBAAyB,iBAAiB,EACjD,OAAO,UAAU,wBAAwB,EACzC,OAAO,oBAAoB,gDAAgD,EAC3E,OAAO,eAAe,0BAA0B,EAChD,OAAO,qBAAqB,sBAAsB,OAAO,EACzD,OAAO,OAAO,YAAY;AACzB,QAAM,EAAE,eAAe,IAAI,MAAM,OAAO,6BAAmB;AAC3D,QAAM,EAAE,WAAW,aAAa,IAAI,YAAY;AAChD,QAAM,gBAAgB,IAAI,cAAc,aAAa,aAAa,GAAG,EAAE,eAAe,qBAAqB,EAAE,CAAC;AAE9G,MAAI;AACF,UAAM;AAAA,MACJ;AAAA,QACE,WAAW,QAAQ;AAAA,QACnB,UAAU,QAAQ;AAAA,QAClB,UAAU,QAAQ;AAAA,QAClB,MAAM,CAAC,CAAC,QAAQ;AAAA,QAChB,QAAQ,QAAQ;AAAA,QAChB,QAAQ,QAAQ;AAAA,QAChB,eAAe,CAAC,CAAC,QAAQ;AAAA,MAC3B;AAAA,MACA,EAAE,WAAW,cAAc,cAAc;AAAA,IAC3C;AAAA,EACF,SAAS,OAAO;AACd,YAAQ,MAAM,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AACpE,YAAQ,WAAW;AAAA,EACrB,UAAE;AACA,kBAAc,MAAM;AACpB,cAAU,SAAS;AAAA,EACrB;AACF,CAAC;AAEH,QACG,QAAQ,OAAO,EACf,YAAY,mCAAmC,EAC/C,OAAO,iBAAiB,eAAe,OAAO,gBAAgB,CAAC,EAC/D,OAAO,iBAAiB,kBAAkB,WAAW,EACrD,OAAO,mBAAmB,sEAAsE,EAChG,OAAO,cAAc,wCAAwC,EAC7D,OAAO,kBAAkB,uDAAuD,EAChF,OAAO,aAAa,8BAA8B,EAClD,OAAO,YAAY,qEAAqE,EACxF,OAAO,OAAO,YAAY;AAEzB,MAAI,QAAQ,QAAQ;AAClB,UAAM,EAAE,eAAe,IAAI,MAAM,OAAO,wBAAkB;AAC1D,mBAAe;AAAA,MACb,MAAM,SAAS,QAAQ,MAAM,EAAE;AAAA,MAC/B,MAAM,QAAQ;AAAA,MACd,OAAO,QAAQ,SAAS;AAAA,MACxB,UAAU,QAAQ;AAAA,MAClB,aAAa,QAAQ,eAAe;AAAA,IACtC,CAAC;AACD;AAAA,EACF;AAEA,QAAM,EAAE,gBAAgB,IAAI,MAAM,OAAO,qBAAQ;AACjD,QAAM,OAAO,SAAS,QAAQ,MAAM,EAAE;AAEtC,MAAI;AACJ,MAAI,CAAC,QAAQ,UAAU;AACrB,UAAM,SAAS,eAAe,UAAU;AACxC,QAAO,eAAW,MAAM,GAAG;AACzB,gBAAU;AAAA,IACZ,OAAO;AACL,cAAQ,KAAK,yDAAyD;AAAA,IACxE;AAAA,EACF;AAEA,MAAI;AAIF,QAAI,CAAE,MAAM,gBAAgB,MAAM,QAAQ,IAAI,GAAI;AAChD,cAAQ,MAAM,qBAAqB,IAAI,CAAC;AACxC,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,UAAM,OAAO,MAAM,gBAAgB;AAAA,MACjC;AAAA,MACA,MAAM,QAAQ;AAAA,MACd,OAAO,QAAQ,SAAS;AAAA,MACxB;AAAA,MACA,aAAa,QAAQ,eAAe;AAAA,IACtC,CAAC;AAED,UAAM,EAAE,yBAAyB,IAAI,MAAM,OAAO,+BAAkB;AACpE,6BAAyB;AAEzB,UAAM,cAAc,KAAK,SAAS,YAAY,cAAc,KAAK;AACjE,UAAM,MAAM,UAAU,WAAW,IAAI,KAAK,IAAI;AAE9C,YAAQ,IAAI;AAAA,WAAc,WAAW,CAAC,EAAE;AACxC,QAAI,QAAS,SAAQ,IAAI,aAAa,GAAG,GAAG;AAC5C,YAAQ,IAAI,aAAa,GAAG,EAAE;AAC9B,YAAQ,IAAI,aAAa,KAAK,KAAK,EAAE;AACrC,YAAQ,IAAI;AAAA,SAAY;AACxB,YAAQ,IAAI,oCAAoC,KAAK,KAAK,KAAK,GAAG,gBAAgB;AAElF,QAAI,WAAW,QAAQ,MAAM;AAC3B,kBAAY,GAAG;AACf,cAAQ,IAAI;AAAA;AAAA,CAA2C;AAAA,IACzD,OAAO;AACL,cAAQ,IAAI;AAAA;AAAA,CAA2B;AAAA,IACzC;AAEA,UAAM,WAAW,YAAY;AAC3B,cAAQ,IAAI,oBAAoB;AAChC,YAAM,EAAE,eAAe,IAAI,MAAM,OAAO,qBAAQ;AAChD,YAAM,eAAe;AACrB,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,YAAQ,GAAG,UAAU,QAAQ;AAC7B,YAAQ,GAAG,WAAW,QAAQ;AAAA,EAChC,SAAS,KAAc;AACrB,UAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,QAAI,QAAQ,SAAS,YAAY,GAAG;AAElC,cAAQ,MAAM,qBAAqB,IAAI,CAAC;AAAA,IAC1C,OAAO;AACL,cAAQ,MAAM,mBAAmB,OAAO,EAAE;AAAA,IAC5C;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF,CAAC;AAEH,IAAM,YAAY,QAAQ,QAAQ,QAAQ,EAAE,YAAY,0BAA0B;AAElF,UACG,QAAQ,MAAM,EACd,YAAY,uBAAuB,EACnC,OAAO,MAAM;AACZ,UAAQ,IAAI,cAAc,CAAC;AAC7B,CAAC;AAEH,UACG,QAAQ,MAAM,EACd,YAAY,4BAA4B,EACxC,OAAO,MAAM;AACZ,QAAM,SAAS,WAAW;AAC1B,UAAQ,IAAI,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC;AAC7C,CAAC;AAEH,UACG,QAAQ,mBAAmB,EAC3B,YAAY,kEAAkE,EAC9E,OAAO,CAAC,KAAa,UAAkB;AACtC,MAAI;AACF,UAAM,SAAS,eAAe,KAAK,KAAK;AACxC,YAAQ,IAAI,GAAG,OAAO,OAAO,IAAI,OAAO,GAAG,MAAM,KAAK,UAAU,OAAO,KAAK,CAAC,EAAE;AAAA,EACjF,SAAS,KAAK;AACZ,QAAI,eAAe,gBAAgB;AACjC,cAAQ,MAAM,UAAU,IAAI,OAAO,EAAE;AACrC,cAAQ,WAAW;AACnB;AAAA,IACF;AACA,UAAM;AAAA,EACR;AACF,CAAC;AAEH,QACG,QAAQ,MAAM,EACd,YAAY,6EAA6E,EACzF,OAAO,YAAY;AAClB,QAAM,EAAE,iBAAiB,IAAI,MAAM,OAAO,wBAAkB;AAC5D,mBAAiB;AACnB,CAAC;AAEH,QACG,QAAQ,QAAQ,EAChB,YAAY,8BAA8B,EAC1C,OAAO,YAAY;AAClB,QAAM,EAAE,iBAAiB,IAAI,MAAM,OAAO,wBAAkB;AAC5D,QAAM,MAAM,iBAAiB;AAE7B,UAAQ,IAAI,kBAAkB;AAC9B,UAAQ,IAAI,SAAI,OAAO,EAAE,CAAC;AAC1B,UAAQ,IAAI,iBAAiB,iBAAiB,CAAC,EAAE;AAEjD,MAAI,IAAI,WAAW;AACjB,UAAM,UAAU,IAAI,OAAO,UAAU,IAAI,QAAQ,WAAW,IAAI,IAAI,IAAI,KAAK;AAC7E,YAAQ,IAAI,iBAAiB,IAAI,UAAU,YAAY,yBAAyB,EAAE;AAClF,QAAI,QAAS,SAAQ,IAAI,iBAAiB,OAAO,EAAE;AACnD,YAAQ,IAAI,uBAAuB;AACnC,YAAQ,IAAI;AAAA;AAAA,CAAmD;AAAA,EACjE,OAAO;AACL,YAAQ,IAAI,6BAA6B;AACzC,YAAQ,IAAI,wBAAwB;AACpC,YAAQ,IAAI;AAAA;AAAA,CAAsE;AAAA,EACpF;AACF,CAAC;AAIH,SAAS,YAAY,KAAmB;AACtC,MAAI;AACF,UAAM,MACJ,QAAQ,aAAa,WACjB,SAAS,GAAG,MACZ,QAAQ,aAAa,UACnB,aAAa,GAAG,MAChB,aAAa,GAAG;AACxB,aAAS,KAAK,EAAE,OAAO,SAAS,CAAC;AAAA,EACnC,QAAQ;AACN,YAAQ,IAAI,oBAAoB,GAAG,EAAE;AAAA,EACvC;AACF;AAGO,SAAS,IAAI,MAAuB;AAEzC,gBAAc,iBAAiB,CAAC;AAChC,UAAQ,GAAG,qBAAqB,CAAC,UAAU;AACzC,YAAQ,MAAM,uBAAuB,KAAK;AAC1C,cAAU,MAAM,SAAS,qBAAqB,KAAK;AACnD,YAAQ,KAAK,CAAC;AAAA,EAChB,CAAC;AACD,UAAQ,GAAG,sBAAsB,CAAC,WAAW;AAC3C,YAAQ,MAAM,wBAAwB,MAAM;AAC5C,cAAU,MAAM,SAAS,sBAAsB,MAAM;AACrD,YAAQ,KAAK,CAAC;AAAA,EAChB,CAAC;AACD,UAAQ,MAAM,IAAI;AACpB;AAGA,IAAM,cAAc,QAAQ,KAAK,CAAC,GAAG,SAAS,QAAQ,KAAK,QAAQ,KAAK,CAAC,GAAG,SAAS,QAAQ;AAC7F,IAAI,aAAa;AACf,MAAI;AACN;","names":["fs","parsed","program","program","program","enabledRules","program","program","path","program","PreferencesManager"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../packages/node-runtime/src/services/contacts/facts-cache.ts","../../../packages/node-runtime/src/services/contacts/time-range.ts","../../../packages/node-runtime/src/services/contacts/compute.ts"],"sourcesContent":["import { ChatType, type ChatPlatform, type ContactsTimeRangeState } from '@openchatlab/shared-types'\nimport type { ContactMemberRef } from '@openchatlab/core'\nimport { getCache, setCache, deleteSessionCache } from '../../cache/session-cache'\n\nexport const CONTACTS_FACTS_FORMAT_VERSION = 1\n\nexport interface ContactsFactsCacheStats {\n latestHits: number\n latestMisses: number\n factsHits: number\n factsMisses: number\n writes: number\n}\n\nexport interface ContactsSessionMetaFacts {\n name: string\n platform: ChatPlatform\n type: ChatType.PRIVATE | ChatType.GROUP\n ownerId: string | null\n}\n\nexport interface ContactsCachedPrivateFacts {\n contact: ContactMemberRef\n privateMessageCount: number\n activeMonths: string[]\n lastMessageTs: number | null\n}\n\nexport interface ContactsCachedGroupFacts {\n contact: ContactMemberRef\n messageCount: number\n coOccurrenceCount: number\n coOccurrenceRawScore: number\n replyInteractionCount: number\n repliesFromOwnerToContact: number\n repliesFromContactToOwner: number\n lastInteractionTs: number | null\n}\n\nexport type ContactsSessionFacts =\n | { kind: 'not_chat_db'; latestMessageTs: null }\n | { kind: 'missing_meta'; latestMessageTs: number | null }\n | { kind: 'unsupported_type'; latestMessageTs: number | null }\n | { kind: 'missing_owner'; meta: ContactsSessionMetaFacts; latestMessageTs: number | null }\n | { kind: 'unresolved_owner'; meta: ContactsSessionMetaFacts; latestMessageTs: number | null }\n | { kind: 'private_missing'; meta: ContactsSessionMetaFacts; latestMessageTs: number | null }\n | { kind: 'private_ambiguous'; meta: ContactsSessionMetaFacts; latestMessageTs: number | null }\n | {\n kind: 'private'\n meta: ContactsSessionMetaFacts\n latestMessageTs: number | null\n facts: ContactsCachedPrivateFacts\n }\n | {\n kind: 'group'\n meta: ContactsSessionMetaFacts\n latestMessageTs: number | null\n facts: ContactsCachedGroupFacts[]\n }\n\nexport interface ContactsSessionLatestFacts {\n latestMessageTs: number | null\n}\n\nexport type ContactsCacheReadResult<T> = { hit: true; data: T } | { hit: false }\n\ninterface VersionedContactsCacheEntry<T> {\n v: string\n data: T\n}\n\nexport function createEmptyContactsFactsCacheStats(): ContactsFactsCacheStats {\n return {\n latestHits: 0,\n latestMisses: 0,\n factsHits: 0,\n factsMisses: 0,\n writes: 0,\n }\n}\n\nexport function buildContactsSessionLatestCacheKey(algorithmVersion: string): string {\n return `contacts:latest:v${CONTACTS_FACTS_FORMAT_VERSION}:${algorithmVersion}`\n}\n\nexport function buildContactsSessionFactsCacheKey(algorithmVersion: string, timeRange: ContactsTimeRangeState): string {\n return [\n 'contacts:facts',\n `v${CONTACTS_FACTS_FORMAT_VERSION}`,\n algorithmVersion,\n `preset:${timeRange.preset}`,\n `start:${timeRange.startTs ?? 'all'}`,\n ].join(':')\n}\n\nexport function readCachedContactsSessionLatest(\n sessionId: string,\n cacheDir: string,\n key: string,\n dbVersion: string\n): ContactsCacheReadResult<ContactsSessionLatestFacts> {\n const cached = getCache<VersionedContactsCacheEntry<ContactsSessionLatestFacts>>(sessionId, key, cacheDir)\n if (!cached || cached.v !== dbVersion || !isContactsSessionLatestFacts(cached.data)) return { hit: false }\n return { hit: true, data: cached.data }\n}\n\nexport function writeCachedContactsSessionLatest(\n sessionId: string,\n cacheDir: string,\n key: string,\n dbVersion: string,\n data: ContactsSessionLatestFacts\n): void {\n setCache<VersionedContactsCacheEntry<ContactsSessionLatestFacts>>(sessionId, key, { v: dbVersion, data }, cacheDir)\n}\n\nexport function readCachedContactsSessionFacts(\n sessionId: string,\n cacheDir: string,\n key: string,\n dbVersion: string\n): ContactsCacheReadResult<ContactsSessionFacts> {\n const cached = getCache<VersionedContactsCacheEntry<ContactsSessionFacts>>(sessionId, key, cacheDir)\n if (!cached || cached.v !== dbVersion || !isContactsSessionFacts(cached.data)) return { hit: false }\n return { hit: true, data: cached.data }\n}\n\nexport function writeCachedContactsSessionFacts(\n sessionId: string,\n cacheDir: string,\n key: string,\n dbVersion: string,\n data: ContactsSessionFacts\n): void {\n setCache<VersionedContactsCacheEntry<ContactsSessionFacts>>(sessionId, key, { v: dbVersion, data }, cacheDir)\n}\n\nexport function deleteContactsSessionFactsCache(sessionId: string, cacheDir: string): void {\n deleteSessionCache(sessionId, cacheDir)\n}\n\nfunction isContactsSessionLatestFacts(value: unknown): value is ContactsSessionLatestFacts {\n return isObject(value) && isNullableNumber(value.latestMessageTs)\n}\n\nfunction isContactsSessionFacts(value: unknown): value is ContactsSessionFacts {\n if (!isObject(value) || typeof value.kind !== 'string' || !isNullableNumber(value.latestMessageTs)) return false\n switch (value.kind) {\n case 'not_chat_db':\n case 'missing_meta':\n case 'unsupported_type':\n return true\n case 'missing_owner':\n case 'unresolved_owner':\n case 'private_missing':\n case 'private_ambiguous':\n return isContactsSessionMetaFacts(value.meta)\n case 'private':\n return isContactsSessionMetaFacts(value.meta) && isContactsCachedPrivateFacts(value.facts)\n case 'group':\n return (\n isContactsSessionMetaFacts(value.meta) &&\n Array.isArray(value.facts) &&\n value.facts.every(isContactsCachedGroupFacts)\n )\n default:\n return false\n }\n}\n\nfunction isContactsSessionMetaFacts(value: unknown): value is ContactsSessionMetaFacts {\n return (\n isObject(value) &&\n typeof value.name === 'string' &&\n typeof value.platform === 'string' &&\n (value.type === ChatType.PRIVATE || value.type === ChatType.GROUP) &&\n (typeof value.ownerId === 'string' || value.ownerId === null)\n )\n}\n\nfunction isContactsCachedPrivateFacts(value: unknown): value is ContactsCachedPrivateFacts {\n return (\n isObject(value) &&\n isContactMemberRef(value.contact) &&\n isFiniteNumber(value.privateMessageCount) &&\n Array.isArray(value.activeMonths) &&\n value.activeMonths.every((month) => typeof month === 'string') &&\n isNullableNumber(value.lastMessageTs)\n )\n}\n\nfunction isContactsCachedGroupFacts(value: unknown): value is ContactsCachedGroupFacts {\n return (\n isObject(value) &&\n isContactMemberRef(value.contact) &&\n isFiniteNumber(value.messageCount) &&\n isFiniteNumber(value.coOccurrenceCount) &&\n isFiniteNumber(value.coOccurrenceRawScore) &&\n isFiniteNumber(value.replyInteractionCount) &&\n isFiniteNumber(value.repliesFromOwnerToContact) &&\n isFiniteNumber(value.repliesFromContactToOwner) &&\n isNullableNumber(value.lastInteractionTs)\n )\n}\n\nfunction isContactMemberRef(value: unknown): value is ContactMemberRef {\n return (\n isObject(value) &&\n isFiniteNumber(value.id) &&\n typeof value.platformId === 'string' &&\n typeof value.name === 'string' &&\n Array.isArray(value.aliases) &&\n value.aliases.every((alias) => typeof alias === 'string') &&\n (typeof value.avatar === 'string' || value.avatar === null)\n )\n}\n\nfunction isNullableNumber(value: unknown): value is number | null {\n return value === null || isFiniteNumber(value)\n}\n\nfunction isFiniteNumber(value: unknown): value is number {\n return typeof value === 'number' && Number.isFinite(value)\n}\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null\n}\n","import {\n CONTACTS_TIME_RANGE_PRESETS,\n type ContactsTimeRangePreset,\n type ContactsTimeRangeState,\n} from '@openchatlab/shared-types'\n\nconst SECONDS_PER_YEAR = 365 * 24 * 60 * 60\nconst DEFAULT_CONTACTS_TIME_RANGE_PRESET: ContactsTimeRangePreset = '1y'\n\nconst YEARS_BY_PRESET: Partial<Record<ContactsTimeRangePreset, number>> = {\n '1y': 1,\n '2y': 2,\n '3y': 3,\n '5y': 5,\n}\n\nexport function normalizeContactsTimeRangePreset(value: unknown): ContactsTimeRangePreset {\n return CONTACTS_TIME_RANGE_PRESETS.includes(value as ContactsTimeRangePreset)\n ? (value as ContactsTimeRangePreset)\n : DEFAULT_CONTACTS_TIME_RANGE_PRESET\n}\n\nexport function resolveContactsTimeRange(\n presetInput: unknown,\n anchorTs: number | null | undefined\n): ContactsTimeRangeState {\n const preset = normalizeContactsTimeRangePreset(presetInput)\n const normalizedAnchor = typeof anchorTs === 'number' ? anchorTs : null\n const years = YEARS_BY_PRESET[preset]\n\n return {\n preset,\n anchorTs: normalizedAnchor,\n startTs: normalizedAnchor !== null && years ? normalizedAnchor - years * SECONDS_PER_YEAR : null,\n }\n}\n","import { ChatType } from '@openchatlab/shared-types'\nimport type {\n ChatPlatform,\n ContactItem,\n ContactsDiagnostics,\n ContactsTimeRangePreset,\n ContactsTimeRangeState,\n ContactSourceSession,\n} from '@openchatlab/shared-types'\nimport {\n computeFriendScores,\n computeNonFriendScores,\n getGroupContactFacts,\n getLatestContactMessageTs,\n getPrivateContactFacts,\n getSessionMeta,\n isChatSessionDb,\n isNameMatchPlatform,\n resolveOwnerMember,\n shouldEnableContactsEntry,\n} from '@openchatlab/core'\nimport type { ContactMemberRef, SessionMeta } from '@openchatlab/core'\nimport { getDbFileVersion } from '../../cache/analytics-cache'\nimport { appLogger } from '../../logging/app-logger'\nimport type { SessionRuntimeAdapter } from '../adapters'\nimport {\n buildContactsSessionFactsCacheKey,\n buildContactsSessionLatestCacheKey,\n createEmptyContactsFactsCacheStats,\n readCachedContactsSessionFacts,\n readCachedContactsSessionLatest,\n writeCachedContactsSessionFacts,\n writeCachedContactsSessionLatest,\n type ContactsCachedGroupFacts,\n type ContactsCachedPrivateFacts,\n type ContactsFactsCacheStats,\n type ContactsSessionFacts,\n type ContactsSessionLatestFacts,\n type ContactsSessionMetaFacts,\n} from './facts-cache'\nimport { resolveContactsTimeRange } from './time-range'\n\nexport const CONTACTS_ALGORITHM_VERSION = 'contacts-v2'\n\nexport interface ContactsWorkerStats {\n durationMs: number\n totalSessions: number\n processedSessions: number\n skippedFailedSessions: number\n}\n\nexport interface ContactsSnapshot {\n contacts: ContactItem[]\n diagnostics: ContactsDiagnostics\n algorithmVersion: string\n signature: string\n timeRange: ContactsTimeRangeState\n computedAt: number\n workerStats: ContactsWorkerStats\n}\n\nexport interface ContactsComputeProgress {\n processedSessions: number\n totalSessions: number\n currentSessionId?: string\n}\n\nexport interface ComputeContactsSnapshotOptions {\n adapter: SessionRuntimeAdapter\n signature: string\n timeRangePreset?: ContactsTimeRangePreset\n factsCacheDir?: string\n now?: () => number\n onProgress?: (progress: ContactsComputeProgress) => void\n}\n\ninterface ContactAccumulator {\n key: string\n platform: ChatPlatform\n platformId: string\n sessionScoped: boolean\n sessionId?: string\n displayName: string\n aliases: Set<string>\n avatar: string | null\n isFriend: boolean\n privateMessageCount: number\n activePrivateMonths: Set<string>\n commonGroupSessionIds: Set<string>\n coOccurrenceCount: number\n coOccurrenceRawScore: number\n replyInteractionCount: number\n repliesFromOwnerToContact: number\n repliesFromContactToOwner: number\n sourceSessions: ContactSourceSession[]\n lastInteractionTs: number | null\n}\n\ninterface BuildContactsResult {\n contacts: ContactItem[]\n diagnostics: ContactsDiagnostics\n}\n\ninterface ContactsFactsCacheContext {\n dir: string\n latestKey: string\n stats: ContactsFactsCacheStats\n}\n\nexport function computeContactsSnapshot(options: ComputeContactsSnapshotOptions): ContactsSnapshot {\n const startedAt = options.now?.() ?? Date.now()\n const sessionIds = options.adapter.listSessionIds()\n const factsCache = options.factsCacheDir ? createFactsCacheContext(options.factsCacheDir) : null\n const timeRange = resolveContactsTimeRange(\n options.timeRangePreset,\n findGlobalLatestMessageTs(options.adapter, sessionIds, factsCache)\n )\n const result = computeContacts({\n adapter: options.adapter,\n sessionIds,\n timeRange,\n factsCache,\n onProgress: options.onProgress,\n })\n const finishedAt = options.now?.() ?? Date.now()\n if (factsCache) {\n appLogger.info('contacts', 'contacts session facts cache summary', factsCache.stats)\n }\n return {\n ...result,\n algorithmVersion: CONTACTS_ALGORITHM_VERSION,\n signature: options.signature,\n timeRange,\n computedAt: finishedAt,\n workerStats: {\n durationMs: Math.max(0, finishedAt - startedAt),\n totalSessions: sessionIds.length,\n processedSessions: sessionIds.length,\n skippedFailedSessions: result.diagnostics.skippedFailedSessions,\n },\n }\n}\n\nfunction computeContacts(options: {\n adapter: SessionRuntimeAdapter\n sessionIds: string[]\n timeRange: ContactsTimeRangeState\n factsCache: ContactsFactsCacheContext | null\n onProgress?: (progress: ContactsComputeProgress) => void\n}): BuildContactsResult {\n const diagnostics = createEmptyDiagnostics()\n const accumulators = new Map<string, ContactAccumulator>()\n let processedSessions = 0\n let activeGroupSessionCount = 0\n\n for (const sessionId of options.sessionIds) {\n options.onProgress?.({ processedSessions, totalSessions: options.sessionIds.length, currentSessionId: sessionId })\n try {\n const facts = getSessionFacts(options.adapter, sessionId, options.timeRange, options.factsCache)\n if (applySessionFacts(accumulators, diagnostics, sessionId, facts)) activeGroupSessionCount++\n } catch (error) {\n diagnostics.skippedFailedSessions++\n appLogger.error('contacts', `failed to process contact session: ${sessionId}`, error)\n } finally {\n processedSessions++\n options.onProgress?.({ processedSessions, totalSessions: options.sessionIds.length, currentSessionId: sessionId })\n }\n }\n\n diagnostics.contactsEnabled = shouldEnableContactsEntry({\n privateSessionCount: diagnostics.activePrivateSessionCount,\n groupSessionCount: activeGroupSessionCount,\n })\n const contacts = buildContactItems([...accumulators.values()])\n return { contacts, diagnostics }\n}\n\nfunction findGlobalLatestMessageTs(\n adapter: SessionRuntimeAdapter,\n sessionIds: string[],\n factsCache: ContactsFactsCacheContext | null\n): number | null {\n let latest: number | null = null\n for (const sessionId of sessionIds) {\n const dbVersion = getSessionDbVersion(adapter, sessionId)\n const cached = readLatestFacts(sessionId, factsCache, dbVersion)\n if (cached) {\n if (cached.latestMessageTs !== null) latest = Math.max(latest ?? 0, cached.latestMessageTs)\n continue\n }\n try {\n const db = adapter.openReadonly(sessionId)\n const ts = db && isChatSessionDb(db) ? getLatestContactMessageTs(db) : null\n writeLatestFacts(adapter, sessionId, factsCache, { latestMessageTs: ts }, dbVersion)\n if (ts !== null) latest = Math.max(latest ?? 0, ts)\n } catch (error) {\n appLogger.error('contacts', `failed to inspect contact session range: ${sessionId}`, error)\n }\n }\n return latest\n}\n\nfunction getSessionFacts(\n adapter: SessionRuntimeAdapter,\n sessionId: string,\n timeRange: ContactsTimeRangeState,\n factsCache: ContactsFactsCacheContext | null\n): ContactsSessionFacts {\n const dbVersion = getSessionDbVersion(adapter, sessionId)\n const cached = readSessionFacts(sessionId, timeRange, factsCache, dbVersion)\n if (cached) return cached\n\n const facts = computeSessionFacts(adapter, sessionId, timeRange)\n writeSessionFacts(adapter, sessionId, timeRange, factsCache, facts, dbVersion)\n return facts\n}\n\nfunction computeSessionFacts(\n adapter: SessionRuntimeAdapter,\n sessionId: string,\n timeRange: ContactsTimeRangeState\n): ContactsSessionFacts {\n const db = adapter.openReadonly(sessionId)\n if (!db || !isChatSessionDb(db)) return { kind: 'not_chat_db', latestMessageTs: null }\n\n const latestMessageTs = getLatestContactMessageTs(db)\n const meta = getSessionMeta(db)\n if (!meta) return { kind: 'missing_meta', latestMessageTs }\n if (meta.type !== ChatType.PRIVATE && meta.type !== ChatType.GROUP)\n return { kind: 'unsupported_type', latestMessageTs }\n\n const cachedMeta = toContactsSessionMetaFacts(meta)\n if (!meta.ownerId?.trim()) return { kind: 'missing_owner', meta: cachedMeta, latestMessageTs }\n\n const owner = resolveOwnerMember(db)\n if (!owner) return { kind: 'unresolved_owner', meta: cachedMeta, latestMessageTs }\n\n if (meta.type === ChatType.PRIVATE) {\n const facts = getPrivateContactFacts(db, owner.id, { startTs: timeRange.startTs })\n if (facts.type === 'missing') return { kind: 'private_missing', meta: cachedMeta, latestMessageTs }\n if (facts.type === 'ambiguous') return { kind: 'private_ambiguous', meta: cachedMeta, latestMessageTs }\n return { kind: 'private', meta: cachedMeta, latestMessageTs, facts }\n }\n\n return {\n kind: 'group',\n meta: cachedMeta,\n latestMessageTs,\n facts: getGroupContactFacts(db, owner.id, { startTs: timeRange.startTs }),\n }\n}\n\nfunction applySessionFacts(\n accumulators: Map<string, ContactAccumulator>,\n diagnostics: ContactsDiagnostics,\n sessionId: string,\n sessionFacts: ContactsSessionFacts\n): boolean {\n if ('meta' in sessionFacts && sessionFacts.meta.type === ChatType.PRIVATE) diagnostics.privateSessionCount++\n\n switch (sessionFacts.kind) {\n case 'missing_owner':\n diagnostics.skippedMissingOwnerSessions++\n return false\n case 'unresolved_owner':\n diagnostics.skippedUnresolvedOwnerSessions++\n return false\n case 'private_ambiguous':\n diagnostics.skippedAmbiguousPrivateSessions++\n return false\n case 'private':\n applyPrivateFacts(accumulators, diagnostics, sessionId, sessionFacts.meta, sessionFacts.facts)\n return false\n case 'group':\n applyGroupFacts(accumulators, sessionId, sessionFacts.meta, sessionFacts.facts)\n return true\n default:\n return false\n }\n}\n\nfunction applyPrivateFacts(\n accumulators: Map<string, ContactAccumulator>,\n diagnostics: ContactsDiagnostics,\n sessionId: string,\n meta: ContactsSessionMetaFacts,\n facts: ContactsCachedPrivateFacts\n): void {\n if (facts.privateMessageCount <= 0) return\n diagnostics.activePrivateSessionCount++\n\n const acc = getOrCreateAccumulator(accumulators, sessionId, meta, facts.contact)\n acc.isFriend = true\n acc.privateMessageCount += facts.privateMessageCount\n for (const month of facts.activeMonths) acc.activePrivateMonths.add(month)\n updateLastInteraction(acc, facts.lastMessageTs)\n acc.sourceSessions.push({\n id: sessionId,\n name: meta.name,\n platform: meta.platform,\n type: ChatType.PRIVATE,\n messageCount: facts.privateMessageCount,\n privateMessageCount: facts.privateMessageCount,\n lastMessageTs: facts.lastMessageTs,\n })\n}\n\nfunction applyGroupFacts(\n accumulators: Map<string, ContactAccumulator>,\n sessionId: string,\n meta: ContactsSessionMetaFacts,\n sessionFacts: ContactsCachedGroupFacts[]\n): void {\n for (const facts of sessionFacts) {\n if (!hasGroupContactSignal(facts)) continue\n const acc = getOrCreateAccumulator(accumulators, sessionId, meta, facts.contact)\n acc.commonGroupSessionIds.add(sessionId)\n acc.coOccurrenceCount += facts.coOccurrenceCount\n acc.coOccurrenceRawScore += facts.coOccurrenceRawScore\n acc.replyInteractionCount += facts.replyInteractionCount\n acc.repliesFromOwnerToContact += facts.repliesFromOwnerToContact\n acc.repliesFromContactToOwner += facts.repliesFromContactToOwner\n updateLastInteraction(acc, facts.lastInteractionTs)\n acc.sourceSessions.push({\n id: sessionId,\n name: meta.name,\n platform: meta.platform,\n type: ChatType.GROUP,\n messageCount: facts.messageCount,\n coOccurrenceCount: facts.coOccurrenceCount,\n coOccurrenceRawScore: facts.coOccurrenceRawScore,\n replyInteractionCount: facts.replyInteractionCount,\n repliesFromOwnerToContact: facts.repliesFromOwnerToContact,\n repliesFromContactToOwner: facts.repliesFromContactToOwner,\n lastInteractionTs: facts.lastInteractionTs,\n })\n }\n}\n\nfunction hasGroupContactSignal(facts: ContactsCachedGroupFacts): boolean {\n return facts.messageCount > 0 || facts.coOccurrenceCount > 0 || facts.replyInteractionCount > 0\n}\n\nfunction createFactsCacheContext(dir: string): ContactsFactsCacheContext {\n return {\n dir,\n latestKey: buildContactsSessionLatestCacheKey(CONTACTS_ALGORITHM_VERSION),\n stats: createEmptyContactsFactsCacheStats(),\n }\n}\n\nfunction readLatestFacts(\n sessionId: string,\n factsCache: ContactsFactsCacheContext | null,\n dbVersion: string\n): ContactsSessionLatestFacts | null {\n if (!factsCache) return null\n const cached = readCachedContactsSessionLatest(sessionId, factsCache.dir, factsCache.latestKey, dbVersion)\n if (!cached.hit) {\n factsCache.stats.latestMisses++\n return null\n }\n factsCache.stats.latestHits++\n return cached.data\n}\n\nfunction writeLatestFacts(\n adapter: SessionRuntimeAdapter,\n sessionId: string,\n factsCache: ContactsFactsCacheContext | null,\n data: ContactsSessionLatestFacts,\n expectedDbVersion: string\n): void {\n if (!factsCache) return\n const dbVersion = getSessionDbVersion(adapter, sessionId)\n if (dbVersion !== expectedDbVersion) {\n appLogger.debug('contacts', 'skipped contacts latest facts cache write because db version changed', {\n sessionId,\n })\n return\n }\n writeCachedContactsSessionLatest(sessionId, factsCache.dir, factsCache.latestKey, dbVersion, data)\n factsCache.stats.writes++\n}\n\nfunction readSessionFacts(\n sessionId: string,\n timeRange: ContactsTimeRangeState,\n factsCache: ContactsFactsCacheContext | null,\n dbVersion: string\n): ContactsSessionFacts | null {\n if (!factsCache) return null\n const cached = readCachedContactsSessionFacts(\n sessionId,\n factsCache.dir,\n buildContactsSessionFactsCacheKey(CONTACTS_ALGORITHM_VERSION, timeRange),\n dbVersion\n )\n if (!cached.hit) {\n factsCache.stats.factsMisses++\n return null\n }\n factsCache.stats.factsHits++\n return cached.data\n}\n\nfunction writeSessionFacts(\n adapter: SessionRuntimeAdapter,\n sessionId: string,\n timeRange: ContactsTimeRangeState,\n factsCache: ContactsFactsCacheContext | null,\n facts: ContactsSessionFacts,\n expectedDbVersion: string\n): void {\n if (!factsCache) return\n const dbVersion = getSessionDbVersion(adapter, sessionId)\n if (dbVersion !== expectedDbVersion) {\n appLogger.debug('contacts', 'skipped contacts session facts cache write because db version changed', {\n sessionId,\n })\n return\n }\n writeCachedContactsSessionFacts(\n sessionId,\n factsCache.dir,\n buildContactsSessionFactsCacheKey(CONTACTS_ALGORITHM_VERSION, timeRange),\n dbVersion,\n facts\n )\n writeCachedContactsSessionLatest(sessionId, factsCache.dir, factsCache.latestKey, dbVersion, {\n latestMessageTs: facts.latestMessageTs,\n })\n factsCache.stats.writes += 2\n}\n\nfunction getSessionDbVersion(adapter: SessionRuntimeAdapter, sessionId: string): string {\n return getDbFileVersion(adapter.getDbPath(sessionId))\n}\n\nfunction toContactsSessionMetaFacts(meta: SessionMeta): ContactsSessionMetaFacts {\n return {\n name: meta.name,\n platform: meta.platform,\n type: meta.type as ChatType.PRIVATE | ChatType.GROUP,\n ownerId: meta.ownerId,\n }\n}\n\nfunction buildContactItems(accumulators: ContactAccumulator[]): ContactItem[] {\n const friendInputs = accumulators\n .filter((acc) => acc.isFriend)\n .map((acc) => ({\n acc,\n privateMessageCount: acc.privateMessageCount,\n activeMonths: [...acc.activePrivateMonths],\n commonGroupCount: acc.commonGroupSessionIds.size,\n }))\n const nonFriendInputs = accumulators\n .filter((acc) => !acc.isFriend)\n .map((acc) => ({\n acc,\n coOccurrenceRawScore: acc.coOccurrenceRawScore,\n commonGroupCount: acc.commonGroupSessionIds.size,\n replyInteractionCount: acc.replyInteractionCount,\n coOccurrenceCount: acc.coOccurrenceCount,\n }))\n\n const friendScores = computeFriendScores(friendInputs)\n const nonFriendScores = computeNonFriendScores(nonFriendInputs)\n const contacts: ContactItem[] = []\n\n for (const input of friendInputs) {\n const score = friendScores.get(input) ?? { score: 0, scoreBreakdown: {} }\n contacts.push(toContactItem(input.acc, 'friend', score))\n }\n\n for (const input of nonFriendInputs) {\n const score = nonFriendScores.get(input) ?? { score: 0, scoreBreakdown: {} }\n contacts.push(toContactItem(input.acc, 'non_friend', score))\n }\n\n return contacts.sort((a, b) => b.score - a.score || a.displayName.localeCompare(b.displayName))\n}\n\nfunction toContactItem(\n acc: ContactAccumulator,\n pool: 'friend' | 'non_friend',\n scoring: { score: number; scoreBreakdown: ContactItem['scoreBreakdown'] }\n): ContactItem {\n const aliases = [...acc.aliases].filter((alias) => alias !== acc.displayName)\n const searchText = [acc.displayName, acc.platformId, ...aliases].join(' ').toLowerCase()\n\n const item: ContactItem = {\n key: acc.key,\n platform: acc.platform,\n platformId: acc.platformId,\n sessionScoped: acc.sessionScoped,\n sessionId: acc.sessionId,\n displayName: acc.displayName,\n aliases,\n avatar: acc.avatar,\n isFriend: acc.isFriend,\n pool,\n score: scoring.score,\n scoreBreakdown: {\n ...scoring.scoreBreakdown,\n privateMessageCount: acc.privateMessageCount || scoring.scoreBreakdown.privateMessageCount,\n activePrivateMonths: acc.activePrivateMonths.size || scoring.scoreBreakdown.activePrivateMonths,\n commonGroupCount: acc.commonGroupSessionIds.size,\n coOccurrenceCount: acc.coOccurrenceCount,\n coOccurrenceRawScore: acc.coOccurrenceRawScore,\n replyInteractionCount: acc.replyInteractionCount,\n repliesFromOwnerToContact: acc.repliesFromOwnerToContact,\n repliesFromContactToOwner: acc.repliesFromContactToOwner,\n },\n sourceSessions: acc.sourceSessions,\n searchText,\n lastInteractionTs: acc.lastInteractionTs,\n }\n if (pool === 'friend') item.friendSource = 'private'\n return item\n}\n\nexport function createEmptyContactsDiagnostics(): ContactsDiagnostics {\n return createEmptyDiagnostics()\n}\n\nfunction createEmptyDiagnostics(): ContactsDiagnostics {\n return {\n privateSessionCount: 0,\n activePrivateSessionCount: 0,\n contactsEnabled: false,\n skippedMissingOwnerSessions: 0,\n skippedUnresolvedOwnerSessions: 0,\n skippedAmbiguousPrivateSessions: 0,\n skippedInvalidPlatformIdMembers: 0,\n skippedFailedSessions: 0,\n warnings: [],\n }\n}\n\nfunction getOrCreateAccumulator(\n accumulators: Map<string, ContactAccumulator>,\n sessionId: string,\n meta: ContactsSessionMetaFacts,\n contact: ContactMemberRef\n): ContactAccumulator {\n const sessionScoped = shouldScopeContactToSession(meta.platform, contact)\n const key = buildContactKey(meta.platform, contact.platformId, sessionScoped ? sessionId : undefined)\n const existing = accumulators.get(key)\n if (existing) {\n mergeContactIdentity(existing, contact)\n return existing\n }\n\n const created: ContactAccumulator = {\n key,\n platform: meta.platform,\n platformId: contact.platformId,\n sessionScoped,\n sessionId: sessionScoped ? sessionId : undefined,\n displayName: contact.name || contact.platformId,\n aliases: new Set([contact.platformId, contact.name, ...contact.aliases].filter(Boolean)),\n avatar: contact.avatar,\n isFriend: false,\n privateMessageCount: 0,\n activePrivateMonths: new Set(),\n commonGroupSessionIds: new Set(),\n coOccurrenceCount: 0,\n coOccurrenceRawScore: 0,\n replyInteractionCount: 0,\n repliesFromOwnerToContact: 0,\n repliesFromContactToOwner: 0,\n sourceSessions: [],\n lastInteractionTs: null,\n }\n accumulators.set(key, created)\n return created\n}\n\nfunction shouldScopeContactToSession(platform: ChatPlatform, contact: ContactMemberRef): boolean {\n if (isNameMatchPlatform(platform)) return true\n return platform.trim().toLowerCase() === 'qq' && contact.platformId.trim() === contact.name.trim()\n}\n\nfunction buildContactKey(platform: ChatPlatform, platformId: string, sessionId?: string): string {\n const normalizedPlatform = platform.trim()\n const normalizedPlatformId = platformId.trim()\n if (!normalizedPlatform) throw new Error('platform is required')\n if (!normalizedPlatformId) throw new Error('platformId is required')\n return sessionId?.trim()\n ? `${normalizedPlatform}:${sessionId.trim()}:${normalizedPlatformId}`\n : `${normalizedPlatform}:${normalizedPlatformId}`\n}\n\nfunction mergeContactIdentity(acc: ContactAccumulator, contact: ContactMemberRef): void {\n if (contact.name) acc.aliases.add(contact.name)\n acc.aliases.add(contact.platformId)\n for (const alias of contact.aliases) acc.aliases.add(alias)\n if ((!acc.displayName || acc.displayName === acc.platformId) && contact.name) {\n acc.displayName = contact.name\n }\n if (!acc.avatar && contact.avatar) acc.avatar = contact.avatar\n}\n\nfunction updateLastInteraction(acc: ContactAccumulator, ts: number | null): void {\n if (ts === null) return\n acc.lastInteractionTs = Math.max(acc.lastInteractionTs ?? 0, ts)\n}\n"],"mappings":"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","names":[]}
|