chatlab-cli 0.30.2-beta.1 → 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-CYSOCeE0.js → BufferResource-BPGkshPa.js} +1 -1
- package/dist-web/assets/{CanvasRenderer-j7Z_ij_m.js → CanvasRenderer-DTKkZhq4.js} +1 -1
- package/dist-web/assets/{EChartWordcloud-Clg60FMo.js → EChartWordcloud-DU3ZYdgz.js} +1 -1
- package/dist-web/assets/{Filter-BgGyBeXz.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-D2owKyDp.js → RelationshipGalaxyCanvas-Dwdy5flh.js} +3 -3
- package/dist-web/assets/{RelationshipGalaxyThreeCanvas-DfJ2x8y9.js → RelationshipGalaxyThreeCanvas-CtY4TGen.js} +1 -1
- package/dist-web/assets/{RenderTargetSystem-BQ__RbzR.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-DuThUm1R.js → WebGLRenderer-Dm18dxOf.js} +1 -1
- package/dist-web/assets/{WebGPURenderer-d8BwTpxP.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-t_tpJM2L.js → browserAll-Cn_CfkXl.js} +1 -1
- package/dist-web/assets/dateFormat-BcqNKTQ1.js +1 -0
- package/dist-web/assets/{electron-6T3FvCfK.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-DFifsv8L.js → fetch-BRXmDI_2.js} +1 -1
- package/dist-web/assets/{fetch-BgeRLaoA.js → fetch-BUm1isWe.js} +1 -1
- package/dist-web/assets/{fetch-Y-LYZmiT.js → fetch-CBOJwwJp.js} +3 -3
- package/dist-web/assets/{fetch-C7rlHKE7.js → fetch-CEhUu1Yu.js} +1 -1
- package/dist-web/assets/fetch-C_EfVFJi.js +3 -0
- package/dist-web/assets/{fetch-BVM5kQ6-.js → fetch-DL_u25Ui.js} +1 -1
- package/dist-web/assets/{fetch-BLTlXcRA.js → fetch-DMLq3xPG.js} +1 -1
- package/dist-web/assets/{fetch-BgIWhYDV.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-BA87rT8F.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-BUZ-dZy0.js +1 -0
- package/dist-web/assets/{webworkerAll-CoXPTsvw.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-Bk9RvIsW.js +0 -110
- package/dist-web/assets/MessageExportModal-BkEhC8s3.js +0 -1
- package/dist-web/assets/PageHeader-Bbn87QZS.css +0 -1
- package/dist-web/assets/PageHeader-CUwv2AFR.js +0 -1
- package/dist-web/assets/electron-D9vE5t3H.js +0 -1
- package/dist-web/assets/fetch-D0ccqGk_.js +0 -1
- package/dist-web/assets/fetch-DdoIAbX4.js +0 -1
- package/dist-web/assets/fetch-HpiZrx8C.js +0 -3
- package/dist-web/assets/index---rXG1hZ.css +0 -1
- package/dist-web/assets/index-B4l3rHie.css +0 -1
- package/dist-web/assets/index-B5YKZflT.js +0 -2
- package/dist-web/assets/index-C1p9CbmN.css +0 -1
- package/dist-web/assets/index-COpza1A4.css +0 -1
- package/dist-web/assets/index-CQsrsY5d.js +0 -64
- package/dist-web/assets/index-CdsKtFEJ.js +0 -22
- package/dist-web/assets/index-DosG7LmJ.css +0 -1
- package/dist-web/assets/index-DraLHiEK.js +0 -173
- package/dist-web/assets/index-IL5tkxhM.js +0 -2
- package/dist-web/assets/index-Tgk7RCvc.js +0 -1
- package/dist-web/assets/index-cyh3gOBe.js +0 -1
- package/dist-web/assets/index-g8yqhgfj.js +0 -1
- package/dist-web/assets/web-DAgXT8gQ.js +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
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/http/index.ts","../src/http/server.ts","../../../packages/http-routes/src/errors.ts","../../../packages/http-routes/src/routes/system.ts","../../../packages/http-routes/src/routes/sessions.ts","../../../packages/http-routes/src/routes/imports.ts","../../../packages/http-routes/src/routes/web/sessions.ts","../../../packages/http-routes/src/routes/web/members.ts","../../../packages/http-routes/src/routes/web/contacts.ts","../../../packages/http-routes/src/routes/web/people-relationships.ts","../../../packages/http-routes/src/routes/web/global-insight.ts","../../../packages/http-routes/src/routes/web/preferences.ts","../../../packages/http-routes/src/routes/web/analytics.ts","../../../packages/http-routes/src/helpers.ts","../../../packages/http-routes/src/analytics-cache.ts","../../../packages/http-routes/src/routes/web/sql.ts","../../../packages/http-routes/src/routes/web/session-index.ts","../../../packages/http-routes/src/routes/web/export.ts","../../../packages/http-routes/src/routes/web/nlp.ts","../../../packages/http-routes/src/routes/web/ai-assistants.ts","../../../packages/http-routes/src/routes/web/ai-skills.ts","../../../packages/http-routes/src/routes/web/ai-llm.ts","../../../packages/http-routes/src/routes/web/ai-llm-stream.ts","../../../packages/http-routes/src/routes/web/ai-agent-stream.ts","../../../packages/http-routes/src/routes/web/ai-tools.ts","../../../packages/http-routes/src/routes/web/ai-chats.ts","../../../packages/http-routes/src/routes/web/ai-summaries.ts","../../../packages/http-routes/src/routes/web/ai-logs.ts","../../../packages/http-routes/src/routes/web/ai-semantic-index.ts","../../../packages/http-routes/src/routes/web/merge.ts","../../../packages/http-routes/src/routes/web/cache.ts","../../../packages/http-routes/src/routes/web/telemetry.ts","../../../packages/http-routes/src/routes/web/logs.ts","../../../packages/http-routes/src/register.ts","../../../packages/http-routes/src/auth.ts","../../../packages/http-routes/src/routes/web/automation.ts","../src/http/routes/web/index.ts","../src/http/routes/web/import.ts","../src/http/routes/web/helpers.ts","../src/http/routes/web/cache.ts","../src/http/routes/web/update-check.ts","../src/http/routes/proxy.ts","../src/ai/manager-factory.ts","../../../packages/sync/src/types.ts","../../../packages/sync/src/data-source-manager.ts","../../../packages/sync/src/pull-engine.ts","../../../packages/sync/src/scheduler.ts","../src/sync/adapters.ts","../src/sync/index.ts"],"sourcesContent":["/**\n * ChatLab HTTP API — Server lifecycle manager\n *\n * 独立于 Electron 的 HTTP API 服务入口。\n * 使用 DatabaseManager + @openchatlab/core 直接访问数据。\n */\n\nimport * as fs from 'fs'\nimport * as crypto from 'crypto'\nimport type { FastifyInstance } from 'fastify'\nimport { loadConfig, writeConfigField, MigrationRunner, ALL_MIGRATIONS } from '@openchatlab/config'\nimport type { ChatLabConfig } from '@openchatlab/config'\nimport {\n NodePathProvider,\n DatabaseManager,\n AIChatManager,\n LLMConfigStore,\n CustomProviderStore,\n CustomModelStore,\n applyPendingNodeDataDirMigrationIfNeeded,\n hasPendingElectronDataWarning,\n verifyCliDataPath,\n createSemanticIndexWorkerRuntimeClient,\n initAppLogger,\n appLogger,\n logNativeParserStatus,\n} from '@openchatlab/node-runtime'\nimport type { ConfigStorage, SemanticIndexRuntime } from '@openchatlab/node-runtime'\nimport { createServer } from './server'\nimport { setAuthToken, setRequireAuth } from '@openchatlab/http-routes'\nimport { registerWebRoutes } from './routes/web'\nimport { registerProxyRoutes } from './routes/proxy'\nimport { initServerAiLogger, closeServerAiLogger } from '../ai/logger'\nimport { getAssistantManager, getSkillManagerCore } from '../ai/manager-factory'\nimport { createCliRunAgentStream } from '../ai/agent-stream-runner'\nimport { initSync, cleanupSync } from '../sync'\nimport { resolveCliPath } from '../paths'\nimport { resolveApiKey, writeAuthProfile, deleteAuthProfile } from '@openchatlab/config'\nimport { assertCliDataDirCompatible } from '../runtime-compat'\n\nlet server: FastifyInstance | null = null\nlet dbManager: DatabaseManager | null = null\nlet aiChatManager: AIChatManager | null = null\n\nfunction createFileConfigStorage(aiDataDir: string): ConfigStorage {\n return {\n readJson<T>(key: string): T | null {\n try {\n return JSON.parse(fs.readFileSync(`${aiDataDir}/${key}.json`, 'utf-8')) as T\n } catch {\n return null\n }\n },\n writeJson<T>(key: string, data: T): void {\n if (!fs.existsSync(aiDataDir)) fs.mkdirSync(aiDataDir, { recursive: true })\n fs.writeFileSync(`${aiDataDir}/${key}.json`, JSON.stringify(data, null, 2), 'utf-8')\n },\n }\n}\n\nexport interface HttpServerOptions {\n port?: number\n host?: string\n token?: string\n /** dist-web/ 目录路径,启用后托管 Web SPA 静态资源 */\n webRoot?: string\n /** When true, /_web/* also requires Bearer token (for server/headless deployments) */\n requireAuth?: boolean\n}\n\nfunction 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\nfunction ensureToken(config: ChatLabConfig): string {\n if (config.api.token) return config.api.token\n\n const token = `clb_${crypto.randomBytes(32).toString('hex')}`\n try {\n writeConfigField('api', 'token', token)\n } catch {\n // best-effort: token still usable for this session\n }\n return token\n}\n\n/**\n * 启动独立 HTTP API 服务\n */\nexport async function startHttpServer(options?: HttpServerOptions): Promise<{\n port: number\n host: string\n token: string\n}> {\n if (server) {\n throw new Error('HTTP server is already running')\n }\n\n let config = loadConfig()\n const port = options?.port ?? config.api.port\n const host = options?.host ?? config.api.host\n const token = options?.token ?? ensureToken(config)\n\n const pendingMigration = applyPendingNodeDataDirMigrationIfNeeded()\n if (!pendingMigration.skipped) {\n if (pendingMigration.success) {\n console.log('[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\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 console.error(\n '\\n' +\n '='.repeat(68) +\n '\\n' +\n ' ChatLab: Electron desktop data not found\\n' +\n '='.repeat(68) +\n '\\n\\n' +\n ' Detected that ChatLab desktop app was installed on this machine,\\n' +\n ' but could not locate your chat databases.\\n\\n' +\n ' This usually means you changed the data directory in desktop settings.\\n\\n' +\n ' To fix this, choose one of:\\n\\n' +\n ' 1. Open ChatLab desktop app — it will auto-migrate your data\\n' +\n ' 2. Set the data directory manually:\\n' +\n ' export CHATLAB_DATA_DIR=\"/path/to/your/data\"\\n' +\n ' 3. Edit ~/.chatlab/config.toml:\\n' +\n ' [data]\\n' +\n ' user_data_dir = \"/path/to/your/data\"\\n\\n' +\n '='.repeat(68) +\n '\\n'\n )\n }\n\n const migrationRunner = new MigrationRunner(ALL_MIGRATIONS, {\n dataDir: pathProvider.getSystemDir(),\n aiDataDir: pathProvider.getAiDataDir(),\n logger: {\n info: (_cat: string, msg: string) => console.log(`[Migration] ${msg}`),\n warn: (_cat: string, msg: string) => console.warn(`[Migration] ${msg}`),\n error: (_cat: string, msg: string, ...args: unknown[]) => console.error(`[Migration] ${msg}`, ...args),\n },\n })\n await migrationRunner.run()\n const nativeBinding = resolveNativeBinding()\n dbManager = new DatabaseManager(pathProvider, { nativeBinding, runtime })\n\n const aiDataDir = pathProvider.getAiDataDir()\n aiChatManager = new AIChatManager(aiDataDir, { nativeBinding })\n\n const assistantManager = getAssistantManager(aiDataDir)\n const skillManagerCore = getSkillManagerCore(aiDataDir)\n const llmConfigStore = new LLMConfigStore(createFileConfigStorage(aiDataDir), {\n resolveApiKey: (provider, authProfile) => resolveApiKey(provider, authProfile) || undefined,\n onApiKeyCreated: (config, apiKey) => {\n const profileName = config.name?.toLowerCase().replace(/\\s+/g, '-') || config.provider\n writeAuthProfile(profileName, { type: 'api_key', provider: config.provider, key: apiKey })\n return profileName\n },\n onApiKeyDeleted: (config) => {\n const profileName = (config as unknown as Record<string, unknown>).authProfile as string | undefined\n if (profileName) deleteAuthProfile(profileName)\n },\n })\n\n initAppLogger(pathProvider.getLogsDir())\n initServerAiLogger(pathProvider.getLogsDir())\n appLogger.info('server', `HTTP server starting on ${host}:${port}`)\n // 记录 Rust native parser 可用性(导入是否走 Rust 内核),便于按日志排查回退原因\n logNativeParserStatus()\n\n setAuthToken(token)\n setRequireAuth(!!(options?.requireAuth ?? config.api.require_auth))\n\n server = createServer()\n\n const multipart = await import('@fastify/multipart')\n await server.register(multipart.default, {\n limits: { fileSize: 1024 * 1024 * 1024 }, // 1GB\n })\n\n // 语义索引 worker client:启动 HTTP 服务时不拉起 worker;状态/构建/检索按需 lazy start。\n let semanticIndexService: SemanticIndexRuntime | undefined\n try {\n semanticIndexService = createSemanticIndexWorkerRuntimeClient({\n pathProvider,\n runtime,\n nativeBinding,\n workerEntryUrl: import.meta.url.endsWith('.ts')\n ? undefined\n : new URL('./semantic-index-worker.mjs', import.meta.url),\n })\n server.addHook('onClose', async () => semanticIndexService?.close())\n } catch (err) {\n console.warn('[semantic-index] worker client unavailable:', err instanceof Error ? err.message : String(err))\n semanticIndexService = undefined\n }\n\n registerWebRoutes(server, dbManager, {\n pathProvider,\n nativeBinding,\n runtimeIdentity: runtime,\n semanticIndexService,\n aiContext: {\n aiDataDir,\n aiChatManager,\n assistantManager,\n skillManagerCore,\n llmConfigStore,\n customProviderStore: new CustomProviderStore(createFileConfigStorage(aiDataDir)),\n customModelStore: new CustomModelStore(createFileConfigStorage(aiDataDir)),\n runAgentStream: createCliRunAgentStream(dbManager, aiChatManager, semanticIndexService),\n },\n })\n\n initSync(server, dbManager, pathProvider, { port, host, token })\n\n if (options?.webRoot && fs.existsSync(options.webRoot)) {\n // 注册反向代理:将 /_proxy/chatlab.fun/* 转发至 https://chatlab.fun,\n // 行为与 vite dev proxy 一致,解决浏览器 CORS 问题(见 vite.web.config.mts:138-144)。\n // 必须在 @fastify/static 之前注册,确保显式路由优先于静态文件/SPA fallback。\n registerProxyRoutes(server)\n const fastifyStatic = await import('@fastify/static')\n await server.register(fastifyStatic.default, {\n root: options.webRoot,\n prefix: '/',\n wildcard: false,\n })\n // SPA fallback: 所有非 API/非静态文件路由返回 index.html\n server.setNotFoundHandler(async (_request, reply) => {\n return reply.sendFile('index.html')\n })\n }\n\n await server.listen({ port, host })\n\n return { port, host, token }\n}\n\n/**\n * 停止 HTTP API 服务\n */\nexport async function stopHttpServer(): Promise<void> {\n if (!server) return\n\n try {\n await server.close()\n } finally {\n cleanupSync()\n if (aiChatManager) {\n aiChatManager.close()\n aiChatManager = null\n }\n if (dbManager) {\n dbManager.closeAll()\n dbManager = null\n }\n closeServerAiLogger()\n setRequireAuth(false)\n server = null\n }\n}\n\nexport { createServer } from './server'\nexport { registerWebRoutes } from './routes/web'\n","/**\n * ChatLab HTTP API — Fastify server factory\n *\n * 从 electron/main/api/server.ts 迁移,完全平台无关。\n */\n\nimport Fastify, { type FastifyInstance, type FastifyError } from 'fastify'\nimport {\n authHook,\n ApiError,\n ApiErrorCode,\n apiErrorFromUnknown,\n errorResponse,\n serverError,\n} from '@openchatlab/http-routes'\nimport { appLogger } from '@openchatlab/node-runtime'\n\nconst JSON_BODY_LIMIT = 50 * 1024 * 1024 // 50MB\n\nexport function createServer(): FastifyInstance {\n const server = Fastify({\n logger: false,\n bodyLimit: JSON_BODY_LIMIT,\n })\n\n server.addHook('onRequest', authHook)\n\n server.setErrorHandler((error: FastifyError, request, reply) => {\n const apiError = apiErrorFromUnknown(error)\n if (apiError) {\n reply.code(apiError.statusCode).send(errorResponse(apiError))\n return\n }\n\n if (error.statusCode === 413) {\n const bodyErr = new ApiError(ApiErrorCode.BODY_TOO_LARGE, 'Request body exceeds 50MB limit')\n reply.code(413).send(errorResponse(bodyErr))\n return\n }\n\n const statusCode = (error as any).statusCode\n if (statusCode && statusCode >= 400 && statusCode < 600) {\n reply.code(statusCode).send({ success: false, error: { code: 'CLIENT_ERROR', message: error.message } })\n return\n }\n\n appLogger.error('http', `${request.method} ${request.url} -> 500`, error)\n const err = serverError(error.message)\n reply.code(err.statusCode).send(errorResponse(err))\n })\n\n return server\n}\n","/**\n * ChatLab HTTP API — Error codes and factory functions\n *\n * Platform-agnostic error handling shared by CLI Server and Electron Internal Server.\n */\n\nimport { DataDirCompatibilityError } from '@openchatlab/node-runtime/src/data-dir-compat'\nimport { ImportInProgressError } from '@openchatlab/node-runtime/src/import/import-lock'\n\nexport enum ApiErrorCode {\n UNAUTHORIZED = 'UNAUTHORIZED',\n SESSION_NOT_FOUND = 'SESSION_NOT_FOUND',\n INVALID_FORMAT = 'INVALID_FORMAT',\n INVALID_PAYLOAD = 'INVALID_PAYLOAD',\n SQL_READONLY_VIOLATION = 'SQL_READONLY_VIOLATION',\n SQL_EXECUTION_ERROR = 'SQL_EXECUTION_ERROR',\n EXPORT_TOO_LARGE = 'EXPORT_TOO_LARGE',\n BODY_TOO_LARGE = 'BODY_TOO_LARGE',\n IMPORT_IN_PROGRESS = 'IMPORT_IN_PROGRESS',\n IMPORT_FAILED = 'IMPORT_FAILED',\n DATA_DIR_INCOMPATIBLE = 'DATA_DIR_INCOMPATIBLE',\n SERVER_ERROR = 'SERVER_ERROR',\n}\n\nconst HTTP_STATUS: Record<ApiErrorCode, number> = {\n [ApiErrorCode.UNAUTHORIZED]: 401,\n [ApiErrorCode.SESSION_NOT_FOUND]: 404,\n [ApiErrorCode.INVALID_FORMAT]: 400,\n [ApiErrorCode.INVALID_PAYLOAD]: 400,\n [ApiErrorCode.SQL_READONLY_VIOLATION]: 400,\n [ApiErrorCode.SQL_EXECUTION_ERROR]: 400,\n [ApiErrorCode.EXPORT_TOO_LARGE]: 400,\n [ApiErrorCode.BODY_TOO_LARGE]: 413,\n [ApiErrorCode.IMPORT_IN_PROGRESS]: 409,\n [ApiErrorCode.IMPORT_FAILED]: 500,\n [ApiErrorCode.DATA_DIR_INCOMPATIBLE]: 409,\n [ApiErrorCode.SERVER_ERROR]: 500,\n}\n\nexport class ApiError extends Error {\n code: ApiErrorCode\n statusCode: number\n\n constructor(code: ApiErrorCode, message: string) {\n super(message)\n this.name = 'ApiError'\n this.code = code\n this.statusCode = HTTP_STATUS[code]\n }\n}\n\nexport function unauthorized(message = 'Invalid or missing token'): ApiError {\n return new ApiError(ApiErrorCode.UNAUTHORIZED, message)\n}\n\nexport function sessionNotFound(id: string): ApiError {\n return new ApiError(ApiErrorCode.SESSION_NOT_FOUND, `Session not found: ${id}`)\n}\n\nexport function invalidPayload(message: string): ApiError {\n return new ApiError(ApiErrorCode.INVALID_PAYLOAD, message)\n}\n\nexport function sqlReadonlyViolation(): ApiError {\n return new ApiError(ApiErrorCode.SQL_READONLY_VIOLATION, 'Only SELECT queries are allowed')\n}\n\nexport function sqlExecutionError(message: string): ApiError {\n return new ApiError(ApiErrorCode.SQL_EXECUTION_ERROR, message)\n}\n\nexport function exportTooLarge(count: number, limit: number): ApiError {\n return new ApiError(\n ApiErrorCode.EXPORT_TOO_LARGE,\n `Message count ${count} exceeds export limit ${limit}. Use paginated /messages API instead.`\n )\n}\n\nexport function serverError(message = 'Internal server error'): ApiError {\n return new ApiError(ApiErrorCode.SERVER_ERROR, message)\n}\n\nexport function importInProgress(): ApiError {\n return new ApiError(ApiErrorCode.IMPORT_IN_PROGRESS, 'Another import is already in progress')\n}\n\nexport function importFailed(message: string): ApiError {\n return new ApiError(ApiErrorCode.IMPORT_FAILED, message)\n}\n\nexport function dataDirIncompatible(message: string): ApiError {\n return new ApiError(ApiErrorCode.DATA_DIR_INCOMPATIBLE, message)\n}\n\nexport function apiErrorFromUnknown(error: unknown): ApiError | null {\n if (error instanceof ApiError) return error\n if (error instanceof ImportInProgressError) return importInProgress()\n if (error instanceof DataDirCompatibilityError) return dataDirIncompatible(error.message)\n return null\n}\n\nexport function successResponse<T>(data: T, meta?: Record<string, unknown>) {\n return {\n success: true as const,\n data,\n meta: {\n timestamp: Math.floor(Date.now() / 1000),\n version: '0.0.2',\n ...meta,\n },\n }\n}\n\nexport function errorResponse(error: ApiError) {\n return {\n success: false as const,\n error: {\n code: error.code,\n message: error.message,\n },\n }\n}\n","/**\n * ChatLab HTTP API — System routes\n *\n * GET /api/v1/status Service status\n * GET /api/v1/schema ChatLab Format JSON Schema\n */\n\nimport type { FastifyInstance } from 'fastify'\nimport type { HttpRouteContext } from '../context'\nimport { successResponse } from '../errors'\n\nexport function registerSystemRoutes(server: FastifyInstance, ctx: HttpRouteContext): void {\n server.get('/api/v1/status', async () => {\n let sessionCount = 0\n try {\n sessionCount = ctx.dbManager.listSessionIds().length\n } catch {\n // ignore\n }\n\n return successResponse({\n name: 'ChatLab API',\n version: ctx.getVersion(),\n uptime: Math.floor(process.uptime()),\n sessionCount,\n })\n })\n\n server.get('/api/v1/schema', async () => {\n return successResponse({\n format: 'ChatLab Format',\n version: '0.0.2',\n spec: {\n chatlab: {\n type: 'object',\n required: ['version'],\n properties: {\n version: { type: 'string' },\n exportedAt: { type: 'number' },\n generator: { type: 'string' },\n },\n },\n meta: {\n type: 'object',\n required: ['name', 'platform', 'type'],\n properties: {\n name: { type: 'string' },\n platform: {\n type: 'string',\n enum: ['qq', 'wechat', 'telegram', 'discord', 'line', 'whatsapp', 'instagram', 'unknown'],\n },\n type: { type: 'string', enum: ['group', 'private'] },\n groupId: { type: 'string' },\n },\n },\n members: {\n type: 'array',\n items: {\n type: 'object',\n required: ['platformId', 'accountName'],\n properties: {\n platformId: { type: 'string' },\n accountName: { type: 'string' },\n groupNickname: { type: 'string' },\n avatar: { type: 'string' },\n },\n },\n },\n messages: {\n type: 'array',\n items: {\n type: 'object',\n required: ['sender', 'timestamp', 'type'],\n properties: {\n platformMessageId: { type: 'string' },\n sender: { type: 'string' },\n accountName: { type: 'string' },\n groupNickname: { type: 'string' },\n timestamp: { type: 'number' },\n type: { type: 'number' },\n content: { type: ['string', 'null'] },\n replyToMessageId: { type: 'string' },\n },\n },\n },\n },\n })\n })\n}\n","/**\n * ChatLab HTTP API — REST Session routes (/api/v1/sessions/*)\n *\n * Public REST API for external tools, scripts, and integrations.\n * Uses DatabaseManager + @openchatlab/core for data access.\n */\n\nimport type { FastifyInstance } from 'fastify'\nimport type { HttpRouteContext } from '../context'\nimport {\n getSessionInfo,\n getSessionMeta,\n getSessionOverview,\n queryMessages,\n getMembers,\n getMembersDetailed,\n getMemberActivity,\n getMessageTypeStats,\n executeReadonlySql,\n getLastPlatformMessageId,\n} from '@openchatlab/core'\nimport { successResponse, errorResponse, sessionNotFound, exportTooLarge, sqlExecutionError, ApiError } from '../errors'\n\nconst EXPORT_MESSAGE_LIMIT = 100_000\n\nfunction ensureDb(ctx: HttpRouteContext, sessionId: string) {\n const db = ctx.dbManager.open(sessionId)\n if (!db) throw sessionNotFound(sessionId)\n return db\n}\n\nexport function registerRestSessionRoutes(server: FastifyInstance, ctx: HttpRouteContext): void {\n server.get('/api/v1/sessions', async () => {\n const sessionIds = ctx.dbManager.listSessionIds()\n const sessions = sessionIds\n .map((id) => {\n const db = ctx.dbManager.open(id)\n if (!db) return null\n const info = getSessionInfo(db)\n if (!info) return null\n return {\n id,\n name: info.name,\n platform: info.platform,\n type: info.type,\n groupId: info.groupId || undefined,\n messageCount: info.messageCount,\n memberCount: info.memberCount,\n firstTimestamp: info.firstMessageTs,\n lastTimestamp: info.lastMessageTs,\n }\n })\n .filter(Boolean)\n return successResponse(sessions)\n })\n\n server.get<{ Params: { id: string } }>('/api/v1/sessions/:id', async (request) => {\n const db = ensureDb(ctx, request.params.id)\n const info = getSessionInfo(db)\n if (!info) throw sessionNotFound(request.params.id)\n return successResponse({\n id: request.params.id,\n name: info.name,\n platform: info.platform,\n type: info.type,\n groupId: info.groupId || undefined,\n messageCount: info.messageCount,\n memberCount: info.memberCount,\n firstTimestamp: info.firstMessageTs,\n lastTimestamp: info.lastMessageTs,\n lastPlatformMessageId: getLastPlatformMessageId(db),\n importedAt: info.importedAt,\n })\n })\n\n server.get<{\n Params: { id: string }\n Querystring: {\n page?: string\n limit?: string\n startTime?: string\n endTime?: string\n keyword?: string\n senderId?: string\n }\n }>('/api/v1/sessions/:id/messages', async (request) => {\n const { id } = request.params\n const db = ensureDb(ctx, id)\n\n const page = Math.max(1, parseInt(request.query.page || '1', 10) || 1)\n const limit = Math.min(1000, Math.max(1, parseInt(request.query.limit || '100', 10) || 100))\n const offset = (page - 1) * limit\n\n const { startTime, endTime, keyword, senderId } = request.query\n\n const result = queryMessages(db, {\n keyword: keyword || undefined,\n startTs: startTime ? parseInt(startTime, 10) : undefined,\n endTs: endTime ? parseInt(endTime, 10) : undefined,\n senderId: senderId ? parseInt(senderId, 10) : undefined,\n limit,\n offset,\n })\n\n return successResponse({\n messages: result.messages,\n total: result.total,\n page,\n limit,\n totalPages: result.totalPages,\n })\n })\n\n server.get<{ Params: { id: string } }>('/api/v1/sessions/:id/members', async (request) => {\n const db = ensureDb(ctx, request.params.id)\n const members = getMembers(db)\n return successResponse(members)\n })\n\n server.get<{ Params: { id: string } }>('/api/v1/sessions/:id/stats/overview', async (request) => {\n const { id } = request.params\n const db = ensureDb(ctx, id)\n\n const overview = getSessionOverview(db)\n const memberActivity = getMemberActivity(db)\n const typeDistribution = getMessageTypeStats(db)\n\n const typeMap: Record<string, number> = {}\n for (const item of typeDistribution) {\n typeMap[String(item.type)] = item.count\n }\n\n const topMembers = memberActivity.slice(0, 10).map((m) => ({\n platformId: m.platformId,\n name: m.name,\n messageCount: m.messageCount,\n percentage: m.percentage,\n }))\n\n return successResponse({\n messageCount: overview.totalMessages,\n memberCount: overview.totalMembers,\n timeRange: {\n start: overview.firstMessageTs ?? 0,\n end: overview.lastMessageTs ?? 0,\n },\n messageTypeDistribution: typeMap,\n topMembers,\n })\n })\n\n server.post<{ Params: { id: string }; Body: { sql: string } }>('/api/v1/sessions/:id/sql', async (request, reply) => {\n const { id } = request.params\n const db = ensureDb(ctx, id)\n\n const { sql } = request.body || {}\n if (!sql || typeof sql !== 'string') {\n const err = sqlExecutionError('Missing sql parameter')\n return reply.code(err.statusCode).send(errorResponse(err))\n }\n\n try {\n const result = executeReadonlySql(db, sql)\n return successResponse(result)\n } catch (err: unknown) {\n const message = err instanceof Error ? err.message : 'SQL execution error'\n if (message.includes('SELECT') || message.includes('只读') || message.includes('readonly')) {\n const apiErr = new ApiError('SQL_READONLY_VIOLATION' as ApiError['code'], message)\n apiErr.statusCode = 400\n return reply.code(400).send(errorResponse(apiErr))\n }\n const apiErr = sqlExecutionError(message)\n return reply.code(apiErr.statusCode).send(errorResponse(apiErr))\n }\n })\n\n server.get<{ Params: { id: string } }>('/api/v1/sessions/:id/export', async (request, reply) => {\n const { id } = request.params\n const db = ensureDb(ctx, id)\n const meta = getSessionMeta(db)\n if (!meta) throw sessionNotFound(id)\n const overview = getSessionOverview(db)\n\n if (overview.totalMessages > EXPORT_MESSAGE_LIMIT) {\n const err = exportTooLarge(overview.totalMessages, EXPORT_MESSAGE_LIMIT)\n return reply.code(err.statusCode).send(errorResponse(err))\n }\n\n const members = getMembersDetailed(db)\n const allMessages = queryMessages(db, { limit: EXPORT_MESSAGE_LIMIT, offset: 0 })\n\n const chatLabFormat = {\n chatlab: {\n version: '0.0.2',\n exportedAt: Math.floor(Date.now() / 1000),\n generator: 'ChatLab API',\n },\n meta: {\n name: meta.name,\n platform: meta.platform,\n type: meta.type,\n groupId: meta.groupId || undefined,\n },\n members: members.map((m) => ({\n platformId: m.platformId,\n accountName: m.accountName || m.platformId,\n groupNickname: m.groupNickname || undefined,\n })),\n messages: allMessages.messages.map((msg) => ({\n sender: msg.senderPlatformId,\n accountName: msg.senderName || undefined,\n timestamp: msg.timestamp,\n type: msg.type,\n content: msg.content || null,\n })),\n }\n\n return successResponse(chatLabFormat)\n })\n}\n","/**\n * ChatLab HTTP API — Push Import route (/api/v1/imports/*)\n *\n * POST /api/v1/imports/:sessionId\n * Creates a new session or appends messages to an existing one.\n * See docs/cn/standard/chatlab-import.md for protocol spec.\n */\n\nimport type { FastifyInstance } from 'fastify'\nimport type { HttpRouteContext } from '../context'\nimport { pushImport } from '@openchatlab/node-runtime'\nimport type { PushImportPayload } from '@openchatlab/node-runtime'\nimport { successResponse, errorResponse, invalidPayload, importInProgress, importFailed } from '../errors'\n\nexport function registerImportRoutes(server: FastifyInstance, ctx: HttpRouteContext): void {\n server.post<{ Params: { sessionId: string }; Body: PushImportPayload }>(\n '/api/v1/imports/:sessionId',\n async (request, reply) => {\n const { sessionId } = request.params\n const contentType = request.headers['content-type'] || ''\n\n if (!contentType.includes('application/json')) {\n const err = invalidPayload('Content-Type must be application/json (JSONL is not yet supported)')\n return reply.code(err.statusCode).send(errorResponse(err))\n }\n\n if (request.headers['x-dry-run'] === 'true') {\n const err = invalidPayload('X-Dry-Run is not yet supported')\n return reply.code(err.statusCode).send(errorResponse(err))\n }\n\n const outcome = await pushImport(ctx.dbManager, sessionId, request.body ?? {})\n\n if (!outcome.ok) {\n if (outcome.reason === 'import_in_progress') {\n const err = importInProgress()\n return reply.code(err.statusCode).send(errorResponse(err))\n }\n if (outcome.reason === 'invalid_payload') {\n const err = invalidPayload(outcome.message)\n return reply.code(err.statusCode).send(errorResponse(err))\n }\n const err = importFailed(outcome.message)\n return reply.code(err.statusCode).send(errorResponse(err))\n }\n\n return successResponse(outcome.result)\n }\n )\n}\n","import type { FastifyInstance } from 'fastify'\nimport type { HttpRouteContext } from '../../context'\nimport { sessionService, ownerProfileService, PreferencesManager } from '@openchatlab/node-runtime'\n\nexport function registerSessionRoutes(server: FastifyInstance, ctx: HttpRouteContext): void {\n const { sessionAdapter: adapter } = ctx\n\n // Lazy: only owner-profile routes need preferences.json access\n let preferencesInstance: PreferencesManager | null = null\n const preferences = () => {\n preferencesInstance ??= ctx.preferencesManager ?? new PreferencesManager(ctx.pathProvider.getSystemDir())\n return preferencesInstance\n }\n\n server.get('/_web/sessions', async () => {\n const aiChatCounts = ctx.aiChatManager?.getAIChatCountsBySession()\n return sessionService.listAnalysisSessions(adapter, {\n enrichSession: aiChatCounts?.size\n ? (dto) => ({ ...dto, aiConversationCount: aiChatCounts.get(dto.id) ?? 0 })\n : undefined,\n })\n })\n\n server.get<{ Params: { id: string } }>('/_web/sessions/:id', async (request) => {\n const session = sessionService.getAnalysisSession(adapter, request.params.id)\n if (!session) {\n throw Object.assign(new Error(`Session not found: ${request.params.id}`), { statusCode: 404 })\n }\n if (ctx.aiChatManager) {\n session.aiConversationCount = ctx.aiChatManager.getAIChats(request.params.id).length\n }\n return session\n })\n\n server.delete<{ Params: { id: string } }>('/_web/sessions/:id', async (request, reply) => {\n const { id } = request.params\n try {\n const deleted = sessionService.deleteSession(adapter, id)\n if (!deleted) {\n return reply.code(404).send({ success: false, error: 'File not found' })\n }\n return { success: true }\n } catch (err) {\n return reply.code(500).send({ success: false, error: String(err) })\n }\n })\n\n server.patch<{ Params: { id: string }; Body: { name: string } }>('/_web/sessions/:id/name', async (request) => {\n sessionService.renameSession(adapter, request.params.id, request.body.name)\n return { success: true }\n })\n\n server.patch<{ Params: { id: string }; Body: { ownerId: string | null } }>(\n '/_web/sessions/:id/owner',\n async (request) => {\n sessionService.updateSessionOwnerId(adapter, request.params.id, request.body.ownerId ?? null)\n return { success: true }\n }\n )\n\n // Try to auto-apply the stored platform owner profile to this session.\n server.post<{ Params: { id: string } }>('/_web/sessions/:id/owner/apply-profile', async (request) => {\n return ownerProfileService.tryApplyOwnerProfile(adapter, preferences(), request.params.id)\n })\n\n // Manually select owner: writes meta.owner_id, updates the platform profile,\n // and batch-applies it to other unowned same-platform sessions.\n server.post<{ Params: { id: string }; Body: { ownerPlatformId: string } }>(\n '/_web/sessions/:id/owner/select',\n async (request) => {\n return ownerProfileService.setOwnerAndApplyProfile(\n adapter,\n preferences(),\n request.params.id,\n request.body.ownerPlatformId\n )\n }\n )\n\n // Suppress the owner prompt for this session (UI-only).\n server.post<{ Params: { id: string } }>('/_web/sessions/:id/owner/dismiss-prompt', async (request) => {\n ownerProfileService.dismissOwnerPrompt(preferences(), request.params.id)\n return { success: true }\n })\n}\n","import type { FastifyInstance } from 'fastify'\nimport type { HttpRouteContext } from '../../context'\nimport { memberService } from '@openchatlab/node-runtime'\n\nexport function registerMemberRoutes(server: FastifyInstance, ctx: HttpRouteContext): void {\n const { sessionAdapter: adapter } = ctx\n\n server.get<{ Params: { id: string } }>('/_web/sessions/:id/members', async (request) => {\n return memberService.getMembers(adapter, request.params.id)\n })\n\n server.get<{\n Params: { id: string }\n Querystring: { page?: string; pageSize?: string; search?: string; sortOrder?: string }\n }>('/_web/sessions/:id/members/paginated', async (request) => {\n return memberService.getMembersPaginated(adapter, request.params.id, {\n page: parseInt(request.query.page || '1', 10),\n pageSize: parseInt(request.query.pageSize || '20', 10),\n search: request.query.search,\n sortOrder: request.query.sortOrder === 'asc' ? 'asc' : 'desc',\n })\n })\n\n server.patch<{ Params: { id: string; memberId: string }; Body: { aliases: string[] } }>(\n '/_web/sessions/:id/members/:memberId/aliases',\n async (request) => {\n const memberId = parseInt(request.params.memberId, 10)\n memberService.updateMemberAliases(adapter, request.params.id, memberId, request.body.aliases)\n return { success: true }\n }\n )\n\n server.delete<{ Params: { id: string; memberId: string } }>(\n '/_web/sessions/:id/members/:memberId',\n async (request) => {\n const memberId = parseInt(request.params.memberId, 10)\n memberService.deleteMember(adapter, request.params.id, memberId)\n return { success: true }\n }\n )\n\n server.post<{ Params: { id: string }; Body: { memberId1: number; memberId2: number } }>(\n '/_web/sessions/:id/members/merge',\n async (request) => {\n const { memberId1, memberId2 } = request.body\n memberService.mergeMembers(adapter, request.params.id, memberId1, memberId2)\n return { success: true }\n }\n )\n\n server.get<{ Params: { id: string; memberId: string } }>(\n '/_web/sessions/:id/members/:memberId/history',\n async (request) => {\n const memberId = parseInt(request.params.memberId, 10)\n return memberService.getMemberNameHistory(adapter, request.params.id, memberId)\n }\n )\n}\n","import type { FastifyInstance } from 'fastify'\nimport { createContactsService } from '@openchatlab/node-runtime'\nimport { CONTACTS_TIME_RANGE_PRESETS, type ContactPool, type ContactsTimeRangePreset } from '@openchatlab/shared-types'\nimport type { HttpRouteContext } from '../../context'\n\ntype ContactsQuery = {\n acceptStale?: string\n timeRange?: string\n pool?: string\n page?: string\n pageSize?: string\n q?: string\n}\n\nexport function registerContactsRoutes(server: FastifyInstance, ctx: HttpRouteContext): void {\n const service =\n ctx.contactsService ??\n createContactsService({\n adapter: ctx.sessionAdapter,\n pathProvider: ctx.pathProvider,\n runtimeIdentity: ctx.runtimeIdentity,\n nativeBinding: ctx.nativeBinding,\n })\n server.addHook('onClose', async () => {\n await service.close()\n })\n\n server.get<{ Querystring: ContactsQuery }>('/_web/contacts', async (request) => {\n return service.getContactsPage({\n acceptStale: isTruthy(request.query.acceptStale),\n timeRangePreset: parseContactsTimeRangePreset(request.query.timeRange),\n pool: parseContactPool(request.query.pool),\n page: parsePositiveInteger(request.query.page),\n pageSize: parsePositiveInteger(request.query.pageSize),\n query: request.query.q,\n })\n })\n\n server.post<{ Querystring: ContactsQuery }>('/_web/contacts/recompute', async (request) => {\n return service.startRecompute({\n timeRangePreset: parseContactsTimeRangePreset(request.query.timeRange),\n pool: parseContactPool(request.query.pool),\n page: parsePositiveInteger(request.query.page),\n pageSize: parsePositiveInteger(request.query.pageSize),\n query: request.query.q,\n })\n })\n\n server.get<{ Params: { key: string }; Querystring: ContactsQuery }>('/_web/contacts/:key/detail', async (request) => {\n return service.getContactDetail(request.params.key, {\n acceptStale: isTruthy(request.query.acceptStale),\n timeRangePreset: parseContactsTimeRangePreset(request.query.timeRange),\n })\n })\n\n server.put<{ Params: { key: string }; Querystring: ContactsQuery }>(\n '/_web/contacts/:key/mark-friend',\n async (request) => {\n return service.markContactAsFriend(request.params.key, {\n timeRangePreset: parseContactsTimeRangePreset(request.query.timeRange),\n })\n }\n )\n\n server.delete<{ Params: { key: string }; Querystring: ContactsQuery }>(\n '/_web/contacts/:key/mark-friend',\n async (request) => {\n return service.unmarkContactAsFriend(request.params.key, {\n timeRangePreset: parseContactsTimeRangePreset(request.query.timeRange),\n })\n }\n )\n}\n\nfunction isTruthy(value: string | undefined): boolean {\n return value === '1' || value === 'true' || value === 'yes'\n}\n\nfunction parseContactsTimeRangePreset(value: string | undefined): ContactsTimeRangePreset {\n return CONTACTS_TIME_RANGE_PRESETS.includes(value as ContactsTimeRangePreset)\n ? (value as ContactsTimeRangePreset)\n : '1y'\n}\n\nfunction parseContactPool(value: string | undefined): ContactPool | undefined {\n return value === 'friend' || value === 'non_friend' ? value : undefined\n}\n\nfunction parsePositiveInteger(value: string | undefined): number | undefined {\n if (!value) return undefined\n const parsed = Number(value)\n if (!Number.isFinite(parsed)) return undefined\n return Math.max(1, Math.trunc(parsed))\n}\n","import type { FastifyInstance } from 'fastify'\nimport {\n CONTACTS_TIME_RANGE_PRESETS,\n type ContactsTimeRangePreset,\n type PeopleRelationshipsGraphScope,\n} from '@openchatlab/shared-types'\nimport { createPeopleRelationshipsService } from '@openchatlab/node-runtime'\nimport type { HttpRouteContext } from '../../context'\n\ntype PeopleRelationshipsQuery = {\n acceptStale?: string\n timeRange?: string\n scope?: string\n q?: string\n}\n\nexport function registerPeopleRelationshipsRoutes(server: FastifyInstance, ctx: HttpRouteContext): void {\n const service =\n ctx.peopleRelationshipsService ??\n createPeopleRelationshipsService({\n adapter: ctx.sessionAdapter,\n pathProvider: ctx.pathProvider,\n runtimeIdentity: ctx.runtimeIdentity,\n nativeBinding: ctx.nativeBinding,\n })\n server.addHook('onClose', async () => {\n await service.close()\n })\n\n server.get<{ Querystring: PeopleRelationshipsQuery }>('/_web/people/relationships', async (request) => {\n return service.getGraph({\n acceptStale: isTruthy(request.query.acceptStale),\n timeRangePreset: parseContactsTimeRangePreset(request.query.timeRange),\n graphScope: parsePeopleRelationshipsGraphScope(request.query.scope),\n query: request.query.q,\n })\n })\n\n server.post<{ Querystring: PeopleRelationshipsQuery }>('/_web/people/relationships/recompute', async (request) => {\n return service.startRecompute({\n timeRangePreset: parseContactsTimeRangePreset(request.query.timeRange),\n graphScope: parsePeopleRelationshipsGraphScope(request.query.scope),\n query: request.query.q,\n })\n })\n\n server.get<{ Params: { key: string }; Querystring: PeopleRelationshipsQuery }>(\n '/_web/people/relationships/:key/neighborhood',\n async (request) => {\n return service.getNeighborhood(request.params.key, {\n acceptStale: isTruthy(request.query.acceptStale),\n timeRangePreset: parseContactsTimeRangePreset(request.query.timeRange),\n })\n }\n )\n}\n\nfunction isTruthy(value: string | undefined): boolean {\n return value === '1' || value === 'true' || value === 'yes'\n}\n\nfunction parseContactsTimeRangePreset(value: string | undefined): ContactsTimeRangePreset {\n return CONTACTS_TIME_RANGE_PRESETS.includes(value as ContactsTimeRangePreset)\n ? (value as ContactsTimeRangePreset)\n : '1y'\n}\n\nfunction parsePeopleRelationshipsGraphScope(value: string | undefined): PeopleRelationshipsGraphScope {\n return value === 'close' || value === 'friends' ? value : 'panorama'\n}\n","import type { FastifyInstance } from 'fastify'\nimport { createGlobalInsightService } from '@openchatlab/node-runtime'\nimport type { HttpRouteContext } from '../../context'\n\ninterface GlobalInsightQuery {\n mode?: string\n year?: string\n days?: string\n acceptStale?: string\n}\n\nexport function registerGlobalInsightRoutes(server: FastifyInstance, ctx: HttpRouteContext): void {\n const service =\n ctx.globalInsightService ??\n createGlobalInsightService({\n adapter: ctx.sessionAdapter,\n pathProvider: ctx.pathProvider,\n runtimeIdentity: ctx.runtimeIdentity,\n nativeBinding: ctx.nativeBinding,\n })\n server.addHook('onClose', async () => service.close())\n\n server.get<{ Querystring: GlobalInsightQuery }>('/_web/global-insight/annual-summary', async (request) => {\n return service.getAnnualSummary({\n ...parseRange(request.query),\n acceptStale: isTruthy(request.query.acceptStale),\n })\n })\n\n server.post<{ Querystring: GlobalInsightQuery }>('/_web/global-insight/annual-summary/recompute', async (request) =>\n service.startRecompute(parseRange(request.query))\n )\n}\n\nfunction parseRange(query: GlobalInsightQuery) {\n const mode = query.mode === 'recent' ? ('recent' as const) : ('year' as const)\n return {\n mode,\n year: parseInteger(query.year),\n days: query.days === '365' ? (365 as const) : undefined,\n }\n}\n\nfunction parseInteger(value: string | undefined): number | undefined {\n if (!value) return undefined\n const parsed = Number(value)\n return Number.isInteger(parsed) ? parsed : undefined\n}\n\nfunction isTruthy(value: string | undefined): boolean {\n return value === '1' || value === 'true' || value === 'yes'\n}\n","/**\n * Preferences HTTP routes — /_web/preferences/*\n *\n * Read/write preferences.json and config.toml [ui] section for Web UI.\n */\n\nimport type { FastifyInstance } from 'fastify'\nimport type { HttpRouteContext } from '../../context'\nimport { PreferencesManager, type Preferences } from '@openchatlab/node-runtime'\nimport { loadConfig, writeConfigField, type UiConfig } from '@openchatlab/config'\n\nexport function registerPreferencesRoutes(server: FastifyInstance, ctx: HttpRouteContext): void {\n const prefManager = ctx.preferencesManager ?? new PreferencesManager(ctx.pathProvider.getSystemDir())\n\n server.get('/_web/preferences', async () => {\n return prefManager.load()\n })\n\n server.patch<{ Body: Partial<Preferences> }>('/_web/preferences', async (request) => {\n return prefManager.save(request.body)\n })\n\n server.get('/_web/preferences/ui-config', async () => {\n const config = loadConfig()\n return config.ui\n })\n\n server.patch<{ Body: Partial<UiConfig> }>('/_web/preferences/ui-config', async (request) => {\n try {\n for (const [key, value] of Object.entries(request.body)) {\n if (value !== undefined) {\n writeConfigField('ui', key, value as string | number)\n }\n }\n return { success: true }\n } catch (err) {\n return { success: false, error: err instanceof Error ? err.message : String(err) }\n }\n })\n\n server.get('/_web/preferences/locale', async () => {\n const config = loadConfig()\n return { lang: config.locale.lang }\n })\n\n server.patch<{ Body: { lang: string } }>('/_web/preferences/locale', async (request) => {\n try {\n writeConfigField('locale', 'lang', request.body.lang)\n return { success: true }\n } catch (err) {\n return { success: false, error: err instanceof Error ? err.message : String(err) }\n }\n })\n}\n","import * as fs from 'fs'\nimport * as path from 'path'\nimport type { FastifyInstance } from 'fastify'\nimport type { HttpRouteContext } from '../../context'\nimport { createJiebaNlpProvider } from '@openchatlab/node-runtime'\nimport {\n getTimeRange,\n getAvailableYears,\n getMemberActivity,\n getHourlyActivity,\n getDailyActivity,\n getWeekdayActivity,\n getMessageTypeStats,\n getMonthlyActivity,\n getYearlyActivity,\n getMessageLengthDistribution,\n getTextStats,\n getLongMessageCount,\n getMemberMonthlyTrend,\n getTextLengthPercentiles,\n getRelationshipStats,\n getCatchphraseAnalysis,\n getMentionAnalysis,\n getMentionGraph,\n getLaughAnalysis,\n getClusterGraph,\n getLanguagePreferenceAnalysis,\n getDragonKingAnalysis,\n getDivingAnalysis,\n getCheckInAnalysis,\n getMemeBattleAnalysis,\n getNightOwlAnalysis,\n getRepeatAnalysis,\n} from '@openchatlab/core'\nimport type { ClusterGraphOptions } from '@openchatlab/core'\nimport { parseTimeFilter } from '../../helpers'\nimport { withAnalyticsCache } from '../../analytics-cache'\n\ntype FilteredQuery = { startTs?: string; endTs?: string; memberId?: string }\n\nexport function registerAnalyticsRoutes(server: FastifyInstance, ctx: HttpRouteContext): void {\n const { sessionAdapter: adapter } = ctx\n\n /** Cache-first wrapper bound to this context; `params` must capture all inputs that affect the result. */\n const cached = <T>(\n name: string,\n sessionId: string,\n params: Record<string, unknown>,\n compute: () => T,\n options?: { dailyInvalidate?: boolean; extraVersion?: string }\n ): T => withAnalyticsCache(ctx, sessionId, `analytics.${name}`, params, compute, options)\n\n server.get<{ Params: { id: string } }>('/_web/sessions/:id/years', async (request) => {\n const db = adapter.ensureReadonly(request.params.id)\n return getAvailableYears(db)\n })\n\n server.get<{ Params: { id: string } }>('/_web/sessions/:id/time-range', async (request) => {\n const db = adapter.ensureReadonly(request.params.id)\n return getTimeRange(db)\n })\n\n server.get<{ Params: { id: string }; Querystring: FilteredQuery }>(\n '/_web/sessions/:id/stats/member-activity',\n async (request) => {\n const id = request.params.id\n const filter = parseTimeFilter(request.query)\n return cached('member-activity', id, { ...filter }, () => getMemberActivity(adapter.ensureReadonly(id), filter))\n }\n )\n\n server.get<{ Params: { id: string }; Querystring: FilteredQuery }>(\n '/_web/sessions/:id/stats/hourly',\n async (request) => {\n const id = request.params.id\n const filter = parseTimeFilter(request.query)\n return cached('hourly', id, { ...filter }, () => getHourlyActivity(adapter.ensureReadonly(id), filter))\n }\n )\n\n server.get<{ Params: { id: string }; Querystring: FilteredQuery }>(\n '/_web/sessions/:id/stats/daily',\n async (request) => {\n const id = request.params.id\n const filter = parseTimeFilter(request.query)\n return cached('daily', id, { ...filter }, () => getDailyActivity(adapter.ensureReadonly(id), filter))\n }\n )\n\n server.get<{ Params: { id: string }; Querystring: FilteredQuery }>(\n '/_web/sessions/:id/stats/weekday',\n async (request) => {\n const id = request.params.id\n const filter = parseTimeFilter(request.query)\n return cached('weekday', id, { ...filter }, () => getWeekdayActivity(adapter.ensureReadonly(id), filter))\n }\n )\n\n server.get<{ Params: { id: string }; Querystring: FilteredQuery }>(\n '/_web/sessions/:id/stats/message-types',\n async (request) => {\n const id = request.params.id\n const filter = parseTimeFilter(request.query)\n return cached('message-types', id, { ...filter }, () => getMessageTypeStats(adapter.ensureReadonly(id), filter))\n }\n )\n\n server.get<{ Params: { id: string }; Querystring: FilteredQuery }>(\n '/_web/sessions/:id/analytics/relationship',\n async (request) => {\n const id = request.params.id\n const filter = parseTimeFilter(request.query)\n return cached('relationship', id, { ...filter }, () => getRelationshipStats(adapter.ensureReadonly(id), filter))\n }\n )\n\n server.get<{ Params: { id: string }; Querystring: FilteredQuery }>(\n '/_web/sessions/:id/analytics/catchphrase',\n async (request) => {\n const id = request.params.id\n const filter = parseTimeFilter(request.query)\n return cached('catchphrase', id, { ...filter }, () => getCatchphraseAnalysis(adapter.ensureReadonly(id), filter))\n }\n )\n\n server.get<{ Params: { id: string }; Querystring: FilteredQuery }>(\n '/_web/sessions/:id/analytics/mention',\n async (request) => {\n const id = request.params.id\n const filter = parseTimeFilter(request.query)\n return cached('mention', id, { ...filter }, () => getMentionAnalysis(adapter.ensureReadonly(id), filter))\n }\n )\n\n server.get<{ Params: { id: string }; Querystring: FilteredQuery }>(\n '/_web/sessions/:id/analytics/mention-graph',\n async (request) => {\n const id = request.params.id\n const filter = parseTimeFilter(request.query)\n return cached('mention-graph', id, { ...filter }, () => getMentionGraph(adapter.ensureReadonly(id), filter))\n }\n )\n\n server.get<{ Params: { id: string }; Querystring: FilteredQuery }>(\n '/_web/sessions/:id/analytics/laugh',\n async (request) => {\n const id = request.params.id\n const filter = parseTimeFilter(request.query)\n return cached('laugh', id, { ...filter }, () => getLaughAnalysis(adapter.ensureReadonly(id), filter))\n }\n )\n\n server.get<{\n Params: { id: string }\n Querystring: FilteredQuery & { topEdges?: string; lookAhead?: string; decaySeconds?: string }\n }>('/_web/sessions/:id/analytics/cluster', async (request) => {\n const id = request.params.id\n const filter = parseTimeFilter(request.query)\n // 仅收集显式传入的参数,避免 undefined 覆盖核心算法默认值\n const options: ClusterGraphOptions = {}\n if (request.query.topEdges) options.topEdges = parseInt(request.query.topEdges, 10)\n if (request.query.lookAhead) options.lookAhead = parseInt(request.query.lookAhead, 10)\n if (request.query.decaySeconds) options.decaySeconds = parseInt(request.query.decaySeconds, 10)\n const hasOptions = Object.keys(options).length > 0\n return cached('cluster', id, { ...filter, ...options }, () =>\n getClusterGraph(adapter.ensureReadonly(id), filter, hasOptions ? options : undefined)\n )\n })\n\n server.get<{ Params: { id: string }; Querystring: FilteredQuery & { locale?: string } }>(\n '/_web/sessions/:id/analytics/language-preference',\n async (request) => {\n const id = request.params.id\n const filter = parseTimeFilter(request.query)\n const locale = request.query.locale || 'zh-CN'\n const zhDictPath = path.join(ctx.pathProvider.getSystemDir(), 'nlp', 'zh-CN.dict')\n let zhDictVersion: string\n try {\n const st = fs.statSync(zhDictPath)\n zhDictVersion = `${Math.floor(st.mtimeMs)}:${st.size}`\n } catch {\n zhDictVersion = '-'\n }\n return cached(\n 'language-preference',\n id,\n { ...filter, locale },\n () =>\n getLanguagePreferenceAnalysis(adapter.ensureReadonly(id), {\n locale,\n timeFilter: filter,\n nlpProvider: createJiebaNlpProvider(),\n }),\n { extraVersion: `dict:${zhDictVersion}` }\n )\n }\n )\n\n server.get<{ Params: { id: string }; Querystring: FilteredQuery }>(\n '/_web/sessions/:id/analytics/monthly-activity',\n async (request) => {\n const id = request.params.id\n const filter = parseTimeFilter(request.query)\n return cached('monthly-activity', id, { ...filter }, () => getMonthlyActivity(adapter.ensureReadonly(id), filter))\n }\n )\n\n server.get<{ Params: { id: string }; Querystring: FilteredQuery }>(\n '/_web/sessions/:id/analytics/yearly-activity',\n async (request) => {\n const id = request.params.id\n const filter = parseTimeFilter(request.query)\n return cached('yearly-activity', id, { ...filter }, () => getYearlyActivity(adapter.ensureReadonly(id), filter))\n }\n )\n\n server.get<{ Params: { id: string }; Querystring: FilteredQuery }>(\n '/_web/sessions/:id/analytics/message-length-distribution',\n async (request) => {\n const id = request.params.id\n const filter = parseTimeFilter(request.query)\n return cached('message-length-distribution', id, { ...filter }, () =>\n getMessageLengthDistribution(adapter.ensureReadonly(id), filter)\n )\n }\n )\n\n server.get<{ Params: { id: string }; Querystring: FilteredQuery }>(\n '/_web/sessions/:id/analytics/text-stats',\n async (request) => {\n const id = request.params.id\n const filter = parseTimeFilter(request.query)\n return cached('text-stats', id, { ...filter }, () => getTextStats(adapter.ensureReadonly(id), filter))\n }\n )\n\n server.get<{ Params: { id: string }; Querystring: FilteredQuery & { minLength?: string } }>(\n '/_web/sessions/:id/analytics/long-message-count',\n async (request) => {\n const id = request.params.id\n const filter = parseTimeFilter(request.query)\n const minLength = request.query.minLength ? parseInt(request.query.minLength, 10) : undefined\n return cached('long-message-count', id, { ...filter, minLength }, () =>\n getLongMessageCount(adapter.ensureReadonly(id), filter, minLength)\n )\n }\n )\n\n server.get<{ Params: { id: string }; Querystring: FilteredQuery }>(\n '/_web/sessions/:id/analytics/member-monthly-trend',\n async (request) => {\n const id = request.params.id\n const filter = parseTimeFilter(request.query)\n return cached('member-monthly-trend', id, { ...filter }, () =>\n getMemberMonthlyTrend(adapter.ensureReadonly(id), filter)\n )\n }\n )\n\n server.get<{ Params: { id: string }; Querystring: FilteredQuery }>(\n '/_web/sessions/:id/analytics/text-length-percentiles',\n async (request) => {\n const id = request.params.id\n const filter = parseTimeFilter(request.query)\n return cached('text-length-percentiles', id, { ...filter }, () =>\n getTextLengthPercentiles(adapter.ensureReadonly(id), filter)\n )\n }\n )\n\n server.get<{ Params: { id: string }; Querystring: FilteredQuery }>(\n '/_web/sessions/:id/analytics/dragon-king',\n async (request) => {\n const id = request.params.id\n const filter = parseTimeFilter(request.query)\n return cached('dragon-king', id, { ...filter }, () => getDragonKingAnalysis(adapter.ensureReadonly(id), filter))\n }\n )\n\n server.get<{ Params: { id: string }; Querystring: FilteredQuery }>(\n '/_web/sessions/:id/analytics/diving',\n async (request) => {\n const id = request.params.id\n const filter = parseTimeFilter(request.query)\n return cached('diving', id, { ...filter }, () => getDivingAnalysis(adapter.ensureReadonly(id), filter), {\n dailyInvalidate: true,\n })\n }\n )\n\n server.get<{ Params: { id: string }; Querystring: FilteredQuery }>(\n '/_web/sessions/:id/analytics/check-in',\n async (request) => {\n const id = request.params.id\n const filter = parseTimeFilter(request.query)\n return cached('check-in', id, { ...filter }, () => getCheckInAnalysis(adapter.ensureReadonly(id), filter))\n }\n )\n\n server.get<{ Params: { id: string }; Querystring: FilteredQuery }>(\n '/_web/sessions/:id/analytics/meme-battle',\n async (request) => {\n const id = request.params.id\n const filter = parseTimeFilter(request.query)\n return cached('meme-battle', id, { ...filter }, () => getMemeBattleAnalysis(adapter.ensureReadonly(id), filter))\n }\n )\n\n server.get<{ Params: { id: string }; Querystring: FilteredQuery }>(\n '/_web/sessions/:id/analytics/night-owl',\n async (request) => {\n const id = request.params.id\n const filter = parseTimeFilter(request.query)\n return cached('night-owl', id, { ...filter }, () => getNightOwlAnalysis(adapter.ensureReadonly(id), filter), {\n dailyInvalidate: true,\n })\n }\n )\n\n server.get<{ Params: { id: string }; Querystring: FilteredQuery }>(\n '/_web/sessions/:id/analytics/repeat',\n async (request) => {\n const id = request.params.id\n const filter = parseTimeFilter(request.query)\n return cached('repeat', id, { ...filter }, () => getRepeatAnalysis(adapter.ensureReadonly(id), filter))\n }\n )\n}\n","/**\n * Shared helpers for route modules.\n */\n\nimport type { TimeFilter } from '@openchatlab/shared-types'\n\nexport function parseTimeFilter(query: Record<string, string | undefined>): TimeFilter | undefined {\n const { startTs, endTs, memberId } = query\n if (!startTs && !endTs && !memberId) return undefined\n const filter: TimeFilter = {}\n if (startTs) filter.startTs = parseInt(startTs, 10)\n if (endTs) filter.endTs = parseInt(endTs, 10)\n if (memberId) filter.memberId = parseInt(memberId, 10)\n return filter\n}\n","/**\n * Shared analytics result caching for HTTP route handlers.\n *\n * Wraps expensive analytics / NLP computations with the platform-agnostic\n * analytics cache (cacheDir/query/{sessionId}.cache.json). Used by both CLI Web\n * (`chatlab start`) and the Electron internal server so the two share one cache\n * implementation. Validity is keyed to the product version plus the session DB\n * file fingerprint, so any release that changes query logic, and any import /\n * incremental import / member edit, transparently invalidates entries.\n */\n\nimport * as path from 'path'\nimport { getDbFileVersion, getOrComputeAnalysisCache } from '@openchatlab/node-runtime'\nimport type { HttpRouteContext } from './context'\n\n/** Deterministic stringify: sorts object keys recursively and drops `undefined`. */\nfunction canonical(value: unknown): string {\n if (value === undefined) return 'null'\n if (value === null || typeof value !== 'object') return JSON.stringify(value)\n if (Array.isArray(value)) return `[${value.map(canonical).join(',')}]`\n const obj = value as Record<string, unknown>\n const keys = Object.keys(obj)\n .filter((k) => obj[k] !== undefined)\n .sort()\n return `{${keys.map((k) => `${JSON.stringify(k)}:${canonical(obj[k])}`).join(',')}}`\n}\n\n/**\n * Build a stable cache key from a namespace (endpoint id) and its params.\n * Key order does not matter and `undefined` params are ignored, so equivalent\n * requests map to the same key.\n */\nexport function buildAnalyticsCacheKey(namespace: string, params: Record<string, unknown>): string {\n return `${namespace}:${canonical(params)}`\n}\n\n/**\n * Cache-first wrapper for an analytics computation bound to a session.\n * Returns the cached result when the DB file is unchanged; otherwise computes,\n * persists (tagged with the current DB fingerprint) and returns it.\n *\n * Pass `options.dailyInvalidate: true` for endpoints whose results depend on\n * the current date (e.g. daysSinceLastMessage, currentStreak). This appends\n * today's date to the version string so the cache is refreshed each day even\n * when the DB file has not changed.\n *\n * Pass `options.extraVersion` to append an arbitrary fingerprint to the version\n * (e.g. an external file's mtime/size) so the cache is invalidated whenever\n * that resource changes independently of the DB or app version.\n */\nexport function withAnalyticsCache<T>(\n ctx: HttpRouteContext,\n sessionId: string,\n namespace: string,\n params: Record<string, unknown>,\n compute: () => T,\n options?: { dailyInvalidate?: boolean; extraVersion?: string }\n): T {\n const queryCacheDir = path.join(ctx.pathProvider.getCacheDir(), 'query')\n const dateStr = options?.dailyInvalidate ? `|date:${new Date().toISOString().split('T')[0]}` : ''\n const extraStr = options?.extraVersion ? `|${options.extraVersion}` : ''\n const version = `${ctx.getVersion()}|${getDbFileVersion(ctx.sessionAdapter.getDbPath(sessionId))}${dateStr}${extraStr}`\n const key = buildAnalyticsCacheKey(namespace, params)\n return getOrComputeAnalysisCache(sessionId, key, queryCacheDir, version, compute)\n}\n","import type { FastifyInstance } from 'fastify'\nimport type { HttpRouteContext } from '../../context'\nimport { executeSql, getSchemaDetailed } from '@openchatlab/core'\n\nexport function registerSqlRoutes(server: FastifyInstance, ctx: HttpRouteContext): void {\n const { sessionAdapter: adapter } = ctx\n\n server.post<{ Params: { id: string }; Body: { sql: string } }>('/_web/sessions/:id/sql', async (request, reply) => {\n const db = adapter.ensureReadonly(request.params.id)\n const { sql } = request.body || {}\n if (!sql || typeof sql !== 'string') {\n return reply.code(400).send({ error: 'Missing sql parameter' })\n }\n try {\n const result = executeSql(db, sql, { columnar: true, timing: true, maxRows: 0 })\n return {\n columns: result.columns,\n rows: result.rows,\n rowCount: result.rowCount,\n duration: result.duration ?? 0,\n limited: result.truncated,\n }\n } catch (err: unknown) {\n const message = err instanceof Error ? err.message : 'SQL execution error'\n return reply.code(400).send({ error: message })\n }\n })\n\n server.get<{ Params: { id: string } }>('/_web/sessions/:id/schema', async (request) => {\n const db = adapter.ensureReadonly(request.params.id)\n return getSchemaDetailed(db)\n })\n\n server.post<{\n Params: { id: string }\n Body: { sql: string; params?: unknown[] | Record<string, unknown> }\n }>('/_web/sessions/:id/query', async (request) => {\n const db = adapter.ensureReadonly(request.params.id)\n const { sql, params = [] } = request.body as { sql: string; params?: unknown[] | Record<string, unknown> }\n\n if (!sql || typeof sql !== 'string') {\n throw Object.assign(new Error('Missing or invalid \"sql\" field'), { statusCode: 400 })\n }\n\n const stmt = db.prepare(sql.trim())\n\n if (!stmt.readonly) {\n throw Object.assign(new Error('Only READ-ONLY statements are allowed'), { statusCode: 403 })\n }\n\n if (Array.isArray(params)) {\n return stmt.all(...params)\n }\n return stmt.all(params)\n })\n}\n","import type { FastifyInstance } from 'fastify'\nimport type { HttpRouteContext } from '../../context'\nimport { sessionIndexService } from '@openchatlab/node-runtime'\n\nexport function registerSessionIndexRoutes(server: FastifyInstance, ctx: HttpRouteContext): void {\n const { sessionAdapter: adapter } = ctx\n\n server.get('/_web/sessions/index-stats', async () => {\n return sessionIndexService.getAllIndexStats(adapter)\n })\n\n server.post<{\n Params: { id: string }\n Body: { gapThreshold?: number }\n }>('/_web/sessions/:id/generate-index', async (request) => {\n const gapThreshold = (request.body as any)?.gapThreshold ?? 1800\n const sessionCount = sessionIndexService.generateIndex(adapter, request.params.id, gapThreshold)\n return { sessionCount }\n })\n\n server.post<{\n Params: { id: string }\n Body: { gapThreshold?: number }\n }>('/_web/sessions/:id/generate-incremental-index', async (request) => {\n const gapThreshold = (request.body as any)?.gapThreshold ?? 1800\n const sessionCount = sessionIndexService.generateIncrementalIndex(adapter, request.params.id, gapThreshold)\n return { sessionCount }\n })\n\n server.post<{ Params: { id: string } }>('/_web/sessions/:id/clear-index', async (request) => {\n sessionIndexService.clearIndex(adapter, request.params.id)\n return { success: true }\n })\n\n server.get<{\n Params: { id: string }\n Querystring: { keywords: string; limit?: string; offset?: string }\n }>('/_web/sessions/:id/search/fts', async (request, reply) => {\n if (!sessionIndexService.getFtsStatus(adapter, request.params.id)) {\n return reply.code(400).send({ error: 'FTS index not built for this session' })\n }\n const keywords = request.query.keywords.split(/\\s+/).filter(Boolean)\n if (keywords.length === 0) return { rowids: [], total: 0 }\n const limit = parseInt(request.query.limit || '100', 10)\n const offset = parseInt(request.query.offset || '0', 10)\n return sessionIndexService.searchFts(adapter, request.params.id, keywords, limit, offset)\n })\n\n server.get<{ Params: { id: string } }>('/_web/sessions/:id/fts/status', async (request) => {\n return { hasFtsIndex: sessionIndexService.getFtsStatus(adapter, request.params.id) }\n })\n\n server.post<{ Params: { id: string } }>('/_web/sessions/:id/fts/rebuild', async (request) => {\n const result = sessionIndexService.rebuildFts(adapter, request.params.id)\n return { success: true, indexed: result.indexed }\n })\n}\n","import type { FastifyInstance } from 'fastify'\nimport type { HttpRouteContext } from '../../context'\nimport { exportService } from '@openchatlab/node-runtime'\nimport type { ExportFormat } from '@openchatlab/node-runtime'\n\nexport function registerExportRoutes(server: FastifyInstance, ctx: HttpRouteContext): void {\n const { sessionAdapter: adapter } = ctx\n\n server.post<{\n Params: { id: string }\n Body: {\n sessionName: string\n format?: ExportFormat\n timeFilter?: { startTs: number; endTs: number }\n }\n }>('/_web/sessions/:id/export', async (request, reply) => {\n const { id } = request.params\n const body = request.body as any\n const sessionName = body?.sessionName || id\n const format: ExportFormat = body?.format || 'txt'\n\n const result = exportService.exportFormatted(adapter, {\n sessionId: id,\n sessionName,\n format,\n timeFilter: body.timeFilter,\n })\n\n if (!result.success) {\n return reply.code(result.totalMessages === 0 ? 404 : 500).send({ error: result.error })\n }\n\n reply.header('Content-Type', result.mimeType)\n reply.header('Content-Disposition', `attachment; filename=\"${encodeURIComponent(result.filename)}\"`)\n return reply.send(result.content)\n })\n}\n","/**\n * NLP Web API — /_web/nlp/ routes\n *\n * Word frequency, POS tags, dictionary management.\n * Business logic from @openchatlab/core and @openchatlab/node-runtime.\n */\n\nimport * as fs from 'fs'\nimport * as path from 'path'\nimport type { FastifyInstance } from 'fastify'\nimport type { HttpRouteContext } from '../../context'\nimport { withAnalyticsCache } from '../../analytics-cache'\nimport type { WordFrequencyParams, SupportedLocale } from '@openchatlab/core'\nimport { POS_TAG_DEFINITIONS } from '@openchatlab/core'\nimport {\n initNlpDir,\n computeWordFrequency,\n segmentText,\n getDictList,\n isDictDownloaded,\n downloadDict,\n deleteDict,\n ensureDefaultDict,\n} from '@openchatlab/node-runtime'\n\nexport function registerNlpRoutes(server: FastifyInstance, ctx: HttpRouteContext): void {\n const nlpDir = path.join(ctx.pathProvider.getSystemDir(), 'nlp')\n initNlpDir(nlpDir)\n ensureDefaultDict(nlpDir).catch((err) => console.warn('[NLP] Auto-download zh-CN dict failed:', err))\n\n server.get('/_web/nlp/pos-tags', async () => {\n return POS_TAG_DEFINITIONS\n })\n\n server.get('/_web/nlp/dicts', async () => {\n return getDictList(nlpDir)\n })\n\n server.get<{ Params: { id: string } }>('/_web/nlp/dicts/:id/status', async (request) => {\n return isDictDownloaded(nlpDir, request.params.id)\n })\n\n server.post<{ Params: { id: string } }>('/_web/nlp/dicts/:id/download', async (request) => {\n return downloadDict(nlpDir, request.params.id)\n })\n\n server.delete<{ Params: { id: string } }>('/_web/nlp/dicts/:id', async (request) => {\n return deleteDict(nlpDir, request.params.id)\n })\n\n server.post<{ Body: WordFrequencyParams }>('/_web/nlp/word-frequency', async (request) => {\n const params = request.body\n const db = ctx.dbManager.open(params.sessionId)\n if (!db) {\n throw Object.assign(new Error(`Session not found: ${params.sessionId}`), { statusCode: 404 })\n }\n const { sessionId, ...keyParams } = params\n const effectiveDictId = !params.dictType || params.dictType === 'default' ? 'zh-CN' : String(params.dictType)\n const dictFilePath = path.join(nlpDir, `${effectiveDictId}.dict`)\n let dictVersion: string\n try {\n const st = fs.statSync(dictFilePath)\n dictVersion = `${Math.floor(st.mtimeMs)}:${st.size}`\n } catch {\n dictVersion = '-'\n }\n return withAnalyticsCache(ctx, sessionId, 'nlp.word-frequency', keyParams, () => computeWordFrequency(db, params), {\n extraVersion: `dict:${dictVersion}`,\n })\n })\n\n server.post<{ Body: { text: string; locale: SupportedLocale; minLength?: number } }>(\n '/_web/nlp/segment',\n async (request) => {\n const { text, locale, minLength } = request.body\n return segmentText(text, locale, minLength)\n }\n )\n}\n","import type { FastifyInstance } from 'fastify'\nimport type { HttpRouteContext } from '../../context'\nimport type { AssistantConfig } from '@openchatlab/node-runtime'\nimport { BUILTIN_TOOL_CATALOG } from '@openchatlab/core'\n\nexport function registerAiAssistantRoutes(server: FastifyInstance, ctx: HttpRouteContext): void {\n // Tool catalog is static data — always available regardless of AI context\n server.get('/_web/ai/tools/catalog', async () => {\n return BUILTIN_TOOL_CATALOG\n })\n\n const mgr = ctx.assistantManager\n if (!mgr) return\n\n server.get('/_web/ai/assistants', async () => {\n return mgr.getAllAssistants()\n })\n\n server.get<{ Params: { id: string } }>('/_web/ai/assistants/:id', async (request, reply) => {\n const config = mgr.getAssistantConfig(request.params.id)\n if (!config) return reply.code(404).send({ error: 'Not found' })\n return config\n })\n\n server.post<{\n Body: Omit<AssistantConfig, 'id'>\n }>('/_web/ai/assistants', async (request) => {\n return mgr.createAssistant(request.body)\n })\n\n server.put<{\n Params: { id: string }\n Body: Partial<AssistantConfig>\n }>('/_web/ai/assistants/:id', async (request, reply) => {\n const result = mgr.updateAssistant(request.params.id, request.body)\n if (!result.success) return reply.code(404).send(result)\n return result\n })\n\n server.delete<{ Params: { id: string } }>('/_web/ai/assistants/:id', async (request, reply) => {\n const result = mgr.deleteAssistant(request.params.id)\n if (!result.success) return reply.code(400).send(result)\n return result\n })\n\n server.post<{ Params: { id: string } }>('/_web/ai/assistants/:id/reset', async (request, reply) => {\n const result = mgr.resetAssistant(request.params.id)\n if (!result.success) return reply.code(400).send(result)\n return result\n })\n\n server.post<{ Body: { rawMd: string } }>('/_web/ai/assistants/import', async (request) => {\n return mgr.importAssistantFromMd(request.body.rawMd)\n })\n\n server.post<{ Body: { builtinId: string } }>('/_web/ai/assistants/import-builtin', async (request, reply) => {\n const result = mgr.importAssistant(request.body.builtinId)\n if (!result.success) return reply.code(400).send(result)\n return result\n })\n\n server.post<{ Params: { id: string } }>('/_web/ai/assistants/:id/reimport', async (request, reply) => {\n const result = mgr.reimportAssistant(request.params.id)\n if (!result.success) return reply.code(400).send(result)\n return result\n })\n}\n","import type { FastifyInstance } from 'fastify'\nimport type { HttpRouteContext } from '../../context'\n\nexport function registerAiSkillRoutes(server: FastifyInstance, ctx: HttpRouteContext): void {\n const mgr = ctx.skillManagerCore\n if (!mgr) return\n\n server.get('/_web/ai/skills', async () => {\n return mgr.getAllSkills()\n })\n\n server.get<{ Params: { id: string } }>('/_web/ai/skills/:id', async (request, reply) => {\n const config = mgr.getSkillConfig(request.params.id)\n if (!config) return reply.code(404).send({ error: 'Not found' })\n return config\n })\n\n server.post<{ Body: { rawMd: string } }>('/_web/ai/skills', async (request) => {\n return mgr.createSkill(request.body.rawMd)\n })\n\n server.put<{\n Params: { id: string }\n Body: { rawMd: string }\n }>('/_web/ai/skills/:id', async (request, reply) => {\n const result = mgr.updateSkill(request.params.id, request.body.rawMd)\n if (!result.success) return reply.code(404).send(result)\n return result\n })\n\n server.delete<{ Params: { id: string } }>('/_web/ai/skills/:id', async (request, reply) => {\n const result = mgr.deleteSkill(request.params.id)\n if (!result.success) return reply.code(400).send(result)\n return result\n })\n\n server.post<{ Body: { rawMd: string } }>('/_web/ai/skills/import', async (request) => {\n return mgr.importSkillFromMd(request.body.rawMd)\n })\n\n server.get('/_web/ai/skills/builtin-catalog', async () => {\n return mgr.getBuiltinCatalog()\n })\n\n server.post<{ Body: { builtinId: string } }>('/_web/ai/skills/import-builtin', async (request, reply) => {\n const result = mgr.importSkill(request.body.builtinId)\n if (!result.success) return reply.code(400).send(result)\n return result\n })\n\n server.post<{ Params: { id: string } }>('/_web/ai/skills/:id/reimport', async (request, reply) => {\n const result = mgr.reimportSkill(request.params.id)\n if (!result.success) return reply.code(400).send(result)\n return result\n })\n}\n","import type { FastifyInstance } from 'fastify'\nimport type { HttpRouteContext } from '../../context'\nimport { BUILTIN_PROVIDERS, BUILTIN_MODELS, getBuiltinModelsByProvider } from '@openchatlab/core'\nimport {\n validateApiKey,\n fetchRemoteModels,\n getDefaultRulesForLocale,\n mergeRulesForLocale,\n} from '@openchatlab/node-runtime'\n\n// ==================== API key display masking ====================\n\nfunction hasRealApiKey(apiKey: string): boolean {\n return !!apiKey && apiKey !== 'sk-no-key-required'\n}\n\nfunction toConfigDisplay(config: Record<string, unknown>): Record<string, unknown> {\n const { apiKey: _raw, ...rest } = config\n return { ...rest, apiKey: '', apiKeySet: hasRealApiKey(String(_raw || '')) }\n}\n\n// ==================== Route registration ====================\n\nexport function registerAiLlmRoutes(server: FastifyInstance, ctx: HttpRouteContext): void {\n const store = ctx.llmConfigStore\n const aiDataDir = ctx.aiDataDir\n if (!store || !aiDataDir) return\n\n // ---------- Config CRUD ----------\n\n server.get('/_web/ai/llm/has-config', async () => {\n return store.hasActiveConfig()\n })\n\n server.get('/_web/ai/llm/configs', async () => {\n const data = store.loadStore()\n return {\n configs: data.configs.map((c) => toConfigDisplay(c as unknown as Record<string, unknown>)),\n defaultAssistant: data.defaultAssistant,\n fastModel: data.fastModel,\n }\n })\n\n server.post<{\n Body: {\n name: string\n provider: string\n apiKey: string\n model?: string\n baseUrl?: string\n maxTokens?: number\n apiFormat?: string\n customModels?: Array<{ id: string; name: string }>\n }\n }>('/_web/ai/llm/configs', async (request) => {\n const result = store.addConfig(request.body)\n if (result.success && result.config) {\n return { ...result, config: toConfigDisplay(result.config as unknown as Record<string, unknown>) }\n }\n return result\n })\n\n server.put<{\n Params: { id: string }\n Body: {\n name?: string\n provider?: string\n apiKey?: string\n model?: string\n baseUrl?: string\n maxTokens?: number\n apiFormat?: string\n customModels?: Array<{ id: string; name: string }>\n }\n }>('/_web/ai/llm/configs/:id', async (request) => {\n return store.updateConfig(request.params.id, request.body)\n })\n\n server.delete<{ Params: { id: string } }>('/_web/ai/llm/configs/:id', async (request) => {\n return store.deleteConfig(request.params.id)\n })\n\n server.get('/_web/ai/llm/default-assistant-slot', async () => {\n return store.getDefaultAssistantSlot()\n })\n\n server.put<{\n Body: { configId: string; modelId: string }\n }>('/_web/ai/llm/default-assistant-slot', async (request) => {\n return store.setDefaultAssistantModel(request.body.configId, request.body.modelId)\n })\n\n server.get('/_web/ai/llm/fast-model-slot', async () => {\n return store.getFastModelSlot()\n })\n\n server.put<{\n Body: { configId: string; modelId: string } | null\n }>('/_web/ai/llm/fast-model-slot', async (request) => {\n return store.setFastModel(request.body)\n })\n\n // ---------- Provider Registry & Model Catalog ----------\n\n server.get('/_web/ai/llm/providers', async () => {\n return BUILTIN_PROVIDERS.map((p) => {\n const models = getBuiltinModelsByProvider(p.id)\n return {\n id: p.id,\n name: p.name,\n defaultBaseUrl: p.defaultBaseUrl,\n models: models\n .filter((m) => !m.capabilities.includes('embedding') && !m.capabilities.includes('ranking'))\n .map((m) => ({ id: m.id, name: m.name, description: m.description })),\n }\n })\n })\n\n server.get('/_web/ai/llm/provider-registry', async () => {\n const custom = ctx.customProviderStore?.getAll() ?? []\n return [...BUILTIN_PROVIDERS, ...custom]\n })\n\n server.get('/_web/ai/llm/model-catalog', async () => {\n const custom = ctx.customModelStore?.getAll() ?? []\n return [...BUILTIN_MODELS, ...custom]\n })\n\n // ---------- Custom Provider CRUD ----------\n\n server.post<{\n Body: {\n name: string\n kind: string\n defaultBaseUrl: string\n supportsCustomModels?: boolean\n modelIds?: string[]\n website?: string\n consoleUrl?: string\n }\n }>('/_web/ai/llm/custom-providers', async (request, reply) => {\n if (!ctx.customProviderStore)\n return reply.code(501).send({ success: false, error: 'Custom providers not available' })\n return ctx.customProviderStore.add(request.body)\n })\n\n server.put<{\n Params: { id: string }\n Body: Record<string, unknown>\n }>('/_web/ai/llm/custom-providers/:id', async (request, reply) => {\n if (!ctx.customProviderStore)\n return reply.code(501).send({ success: false, error: 'Custom providers not available' })\n const result = ctx.customProviderStore.update(request.params.id, request.body as any)\n if (!result.success) return reply.code(404).send(result)\n return result\n })\n\n server.delete<{ Params: { id: string } }>('/_web/ai/llm/custom-providers/:id', async (request, reply) => {\n if (!ctx.customProviderStore)\n return reply.code(501).send({ success: false, error: 'Custom providers not available' })\n const result = ctx.customProviderStore.delete(request.params.id)\n if (!result.success) return reply.code(404).send(result)\n return result\n })\n\n // ---------- Custom Model CRUD ----------\n\n server.post<{\n Body: {\n id: string\n providerId: string\n name: string\n description?: string\n contextWindow?: number\n capabilities?: string[]\n recommendedFor?: string[]\n status?: string\n }\n }>('/_web/ai/llm/custom-models', async (request, reply) => {\n if (!ctx.customModelStore) return reply.code(501).send({ success: false, error: 'Custom models not available' })\n const result = ctx.customModelStore.add(request.body)\n if (!result.success) return reply.code(409).send(result)\n return result\n })\n\n server.put<{\n Params: { providerId: string; modelId: string }\n Body: Record<string, unknown>\n }>('/_web/ai/llm/custom-models/:providerId/:modelId', async (request, reply) => {\n if (!ctx.customModelStore) return reply.code(501).send({ success: false, error: 'Custom models not available' })\n const result = ctx.customModelStore.update(request.params.providerId, request.params.modelId, request.body as any)\n if (!result.success) return reply.code(404).send(result)\n return result\n })\n\n server.delete<{\n Params: { providerId: string; modelId: string }\n }>('/_web/ai/llm/custom-models/:providerId/:modelId', async (request, reply) => {\n if (!ctx.customModelStore) return reply.code(501).send({ success: false, error: 'Custom models not available' })\n const result = ctx.customModelStore.delete(request.params.providerId, request.params.modelId)\n if (!result.success) return reply.code(404).send(result)\n return result\n })\n\n // ---------- Remote API ----------\n\n server.post<{\n Body: { provider: string; apiKey: string; baseUrl?: string; model?: string; apiFormat?: string; configId?: string }\n }>('/_web/ai/llm/validate-key', async (request) => {\n const { provider, apiKey, baseUrl, model, apiFormat, configId } = request.body\n const resolvedKey = apiKey?.trim() ? apiKey : configId ? store.getConfigById(configId)?.apiKey || '' : ''\n return validateApiKey(provider, resolvedKey, baseUrl, model, apiFormat)\n })\n\n server.post<{\n Body: { provider: string; apiKey: string; baseUrl?: string; apiFormat?: string; configId?: string }\n }>('/_web/ai/llm/remote-models', async (request) => {\n const { provider, apiKey, baseUrl, apiFormat, configId } = request.body\n const resolvedKey = apiKey?.trim() ? apiKey : configId ? store.getConfigById(configId)?.apiKey || '' : ''\n return fetchRemoteModels(provider, resolvedKey, baseUrl, apiFormat)\n })\n\n // ---------- Desensitize Rules ----------\n\n server.get<{\n Querystring: { locale?: string }\n }>('/_web/ai/desensitize-rules/defaults', async (request) => {\n return getDefaultRulesForLocale(request.query.locale ?? 'zh-CN')\n })\n\n server.post<{\n Body: { existingRules: unknown[]; locale: string; overrides?: Record<string, boolean> }\n }>('/_web/ai/desensitize-rules/merge', async (request) => {\n return mergeRulesForLocale(request.body.existingRules as any[], request.body.locale, request.body.overrides ?? {})\n })\n}\n","import type { FastifyInstance } from 'fastify'\nimport type { HttpRouteContext } from '../../context'\nimport { buildPiModel, runSimpleLlmStream } from '@openchatlab/node-runtime'\n\nexport function registerAiLlmStreamRoutes(server: FastifyInstance, ctx: HttpRouteContext): void {\n const store = ctx.llmConfigStore\n if (!store) return\n\n server.post<{\n Body: {\n messages: Array<{ role: string; content: string }>\n options?: { temperature?: number; maxTokens?: number }\n }\n }>('/_web/ai/llm/chat-stream', async (request, reply) => {\n const { messages, options } = request.body\n\n const llmConfig = store.getDefaultAssistantConfig()\n if (!llmConfig) {\n return reply.code(400).send({ success: false, error: 'LLM service not configured' })\n }\n\n const piModel = buildPiModel(llmConfig)\n const abortController = new AbortController()\n\n reply.raw.writeHead(200, {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n Connection: 'keep-alive',\n })\n\n reply.raw.on('close', () => {\n if (!abortController.signal.aborted) abortController.abort()\n })\n\n const sendChunk = (data: unknown) => {\n if (reply.raw.writableEnded || reply.raw.destroyed) return\n reply.raw.write(`event: chunk\\ndata: ${JSON.stringify(data)}\\n\\n`)\n }\n\n try {\n await runSimpleLlmStream({\n messages,\n apiKey: llmConfig.apiKey,\n piModel,\n temperature: options?.temperature,\n maxTokens: options?.maxTokens,\n onChunk: sendChunk,\n abortSignal: abortController.signal,\n })\n } catch (error) {\n if (abortController.signal.aborted) return\n const msg = error instanceof Error ? error.message : String(error)\n sendChunk({ content: '', isFinished: true, finishReason: 'error', error: msg })\n } finally {\n if (!reply.raw.writableEnded && !reply.raw.destroyed) reply.raw.end()\n }\n })\n}\n","import type { FastifyInstance } from 'fastify'\nimport type { HttpRouteContext, AgentStreamRequest } from '../../context'\n\nconst activeAgentAborts = new Map<string, AbortController>()\n\nexport function registerAiAgentStreamRoutes(server: FastifyInstance, ctx: HttpRouteContext): void {\n if (!ctx.runAgentStream) return\n\n const runAgentStream = ctx.runAgentStream\n\n server.post<{ Body: AgentStreamRequest }>('/_web/ai/agent/stream', async (request, reply) => {\n const requestId = `req_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`\n const abortController = new AbortController()\n activeAgentAborts.set(requestId, abortController)\n\n reply.raw.writeHead(200, {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n Connection: 'keep-alive',\n 'X-Request-Id': requestId,\n })\n\n let emittedDone = false\n\n const safeSendSSE = (event: string, data: unknown) => {\n if (reply.raw.writableEnded || reply.raw.destroyed) return\n reply.raw.write(`event: ${event}\\ndata: ${JSON.stringify(data)}\\n\\n`)\n }\n\n const safeEnd = () => {\n if (reply.raw.writableEnded || reply.raw.destroyed) return\n reply.raw.end()\n }\n\n safeSendSSE('meta', { requestId })\n\n reply.raw.on('close', () => {\n if (!abortController.signal.aborted) {\n abortController.abort()\n }\n activeAgentAborts.delete(requestId)\n })\n\n try {\n await runAgentStream(\n request.body,\n (chunk) => {\n if (chunk.type === 'done') emittedDone = true\n safeSendSSE(chunk.type, chunk)\n },\n abortController.signal\n )\n } catch (error) {\n const msg = error instanceof Error ? error.message : String(error)\n safeSendSSE('error', { type: 'error', error: { name: 'ServerError', message: msg } })\n } finally {\n if (!emittedDone) safeSendSSE('done', { type: 'done', isFinished: true })\n activeAgentAborts.delete(requestId)\n safeEnd()\n }\n })\n\n server.post<{\n Body: { requestId: string }\n }>('/_web/ai/agent/abort', async (request) => {\n const { requestId } = request.body\n const controller = activeAgentAborts.get(requestId)\n if (controller) {\n controller.abort()\n activeAgentAborts.delete(requestId)\n return { success: true }\n }\n return { success: false }\n })\n}\n","import type { FastifyInstance } from 'fastify'\nimport type { HttpRouteContext } from '../../context'\nimport { AGENT_TOOL_REGISTRY, CoreDataProvider } from '@openchatlab/tools'\nimport type { ToolExecutionContext } from '@openchatlab/tools'\nimport { stripAvatarFields } from '@openchatlab/core'\n\nconst MAX_RESULT_CHARS = 500_000\nconst activeToolTests = new Map<string, AbortController>()\n\nexport function registerAiToolRoutes(server: FastifyInstance, ctx: HttpRouteContext): void {\n server.get('/_web/ai/tools/full-catalog', async () => {\n return AGENT_TOOL_REGISTRY.map((tool) => ({\n name: tool.name,\n category: tool.category ?? 'core',\n description: tool.description,\n parameters: tool.inputSchema ?? {},\n }))\n })\n\n server.post<{\n Body: {\n testId: string\n toolName: string\n params: Record<string, unknown>\n sessionId: string\n }\n }>('/_web/ai/tools/execute', async (request, reply) => {\n const { testId, toolName, params, sessionId } = request.body\n\n const entry = AGENT_TOOL_REGISTRY.find((t) => t.name === toolName)\n if (!entry) {\n return reply.code(404).send({ success: false, error: `Tool not found: ${toolName}` })\n }\n\n const abortController = new AbortController()\n activeToolTests.set(testId, abortController)\n\n try {\n if (ctx.executeAiTool) {\n return await ctx.executeAiTool({ testId, toolName, params, sessionId, abortSignal: abortController.signal })\n }\n\n const db = ctx.dbManager.open(sessionId)\n if (!db) {\n return reply.code(404).send({ success: false, error: `Session not found: ${sessionId}` })\n }\n\n const execCtx: ToolExecutionContext = {\n sessionId,\n db,\n dataProvider: new CoreDataProvider(db),\n abortSignal: abortController.signal,\n }\n\n const startTime = Date.now()\n const result = await entry.handler(params, execCtx)\n const elapsed = Date.now() - startTime\n\n if (abortController.signal.aborted) {\n return { success: false, error: 'cancelled' }\n }\n\n let details = (result.data as Record<string, unknown> | undefined) ?? undefined\n let truncated = false\n\n if (details) {\n stripAvatarFields(details)\n const raw = JSON.stringify(details)\n if (raw.length > MAX_RESULT_CHARS) {\n truncated = true\n details = { _truncated: true, _originalSize: raw.length, _preview: raw.slice(0, MAX_RESULT_CHARS) }\n }\n }\n\n return {\n success: true,\n elapsed,\n content: [{ type: 'text', text: result.content }],\n details,\n truncated,\n }\n } catch (error) {\n if (abortController.signal.aborted) {\n return { success: false, error: 'cancelled' }\n }\n console.error(`Failed to execute tool ${toolName}:`, error)\n return { success: false, error: String(error) }\n } finally {\n activeToolTests.delete(testId)\n }\n })\n\n server.post<{ Body: { testId: string } }>('/_web/ai/tools/cancel', async (request) => {\n const { testId } = request.body\n const controller = activeToolTests.get(testId)\n if (controller) {\n controller.abort()\n activeToolTests.delete(testId)\n return { success: true }\n }\n return { success: false }\n })\n}\n","import type { FastifyInstance } from 'fastify'\nimport type { HttpRouteContext } from '../../context'\nimport { countMessagesTokens } from '@openchatlab/node-runtime'\n\nexport function registerAiChatRoutes(server: FastifyInstance, ctx: HttpRouteContext): void {\n const cm = ctx.aiChatManager\n if (!cm) return\n\n // ==================== AI Chat CRUD ====================\n\n server.post<{\n Body: { sessionId: string; title?: string; assistantId: string }\n }>('/_web/ai/chats', async (request) => {\n const { sessionId, title, assistantId } = request.body\n return cm.createAIChat(sessionId, title, assistantId)\n })\n\n server.get<{\n Querystring: { sessionId: string }\n }>('/_web/ai/chats', async (request) => {\n const { sessionId } = request.query\n if (!sessionId) return []\n return cm.getAIChats(sessionId)\n })\n\n server.get<{ Params: { id: string } }>('/_web/ai/chats/:id', async (request, reply) => {\n const conv = cm.getAIChat(request.params.id)\n if (!conv) return reply.code(404).send({ error: 'AI chat not found' })\n return conv\n })\n\n server.put<{\n Params: { id: string }\n Body: { title: string }\n }>('/_web/ai/chats/:id/title', async (request) => {\n return cm.updateAIChatTitle(request.params.id, request.body.title)\n })\n\n server.delete<{ Params: { id: string } }>('/_web/ai/chats/:id', async (request) => {\n return cm.deleteAIChat(request.params.id)\n })\n\n // ==================== Message CRUD ====================\n\n server.post<{\n Params: { id: string }\n Body: {\n role: 'user' | 'assistant' | 'summary'\n content: string\n dataKeywords?: string[]\n dataMessageCount?: number\n contentBlocks?: unknown[]\n tokenUsage?: {\n promptTokens: number\n completionTokens: number\n totalTokens: number\n cacheReadTokens?: number\n cacheWriteTokens?: number\n }\n }\n }>('/_web/ai/chats/:id/messages', async (request) => {\n const { role, content, dataKeywords, dataMessageCount, contentBlocks, tokenUsage } = request.body\n return cm.addMessage(\n request.params.id,\n role,\n content,\n dataKeywords,\n dataMessageCount,\n contentBlocks as any,\n tokenUsage\n )\n })\n\n server.get<{ Params: { id: string } }>('/_web/ai/chats/:id/messages', async (request) => {\n return cm.getMessages(request.params.id)\n })\n\n server.post<{\n Params: { id: string; messageId: string }\n }>('/_web/ai/chats/:id/messages/:messageId/delete-from', async (request, reply) => {\n const { id, messageId } = request.params\n if (!id || typeof id !== 'string' || !messageId || typeof messageId !== 'string') {\n return reply.code(400).send({ error: 'aiChatId and messageId are required' })\n }\n cm.deleteMessagesFrom(id, messageId)\n return { success: true }\n })\n\n server.post<{\n Params: { id: string }\n Body: { upToMessageId: string; title?: string }\n }>('/_web/ai/chats/:id/fork', async (request, reply) => {\n const { upToMessageId, title } = request.body\n if (!upToMessageId || typeof upToMessageId !== 'string') {\n return reply.code(400).send({ error: 'upToMessageId is required' })\n }\n return cm.forkAIChat(request.params.id, upToMessageId, title)\n })\n\n server.put<{\n Params: { messageId: string }\n Body: { content: string }\n }>('/_web/ai/messages/:messageId/content', async (request, reply) => {\n const { content } = request.body\n if (!content || typeof content !== 'string') {\n return reply.code(400).send({ error: 'content is required' })\n }\n cm.updateMessageContent(request.params.messageId, content)\n return { success: true }\n })\n\n server.post<{\n Params: { id: string; messageId: string }\n }>('/_web/ai/chats/:id/messages/:messageId/delete-relink', async (request, reply) => {\n const { id, messageId } = request.params\n if (!id || !messageId) {\n return reply.code(400).send({ error: 'aiChatId and messageId are required' })\n }\n cm.deleteAndRelinkMessage(id, messageId)\n return { success: true }\n })\n\n server.post<{\n Params: { id: string }\n Body: {\n afterMessageId: string\n role: 'user' | 'assistant' | 'summary'\n content: string\n contentBlocks?: unknown[]\n tokenUsage?: {\n promptTokens: number\n completionTokens: number\n totalTokens: number\n cacheReadTokens?: number\n cacheWriteTokens?: number\n }\n }\n }>('/_web/ai/chats/:id/messages/insert-after', async (request, reply) => {\n const { afterMessageId, role, content, contentBlocks, tokenUsage } = request.body\n if (!afterMessageId || typeof afterMessageId !== 'string') {\n return reply.code(400).send({ error: 'afterMessageId is required' })\n }\n return cm.insertMessageAfter(request.params.id, afterMessageId, role, content, contentBlocks as any, tokenUsage)\n })\n\n server.get<{ Params: { id: string } }>('/_web/ai/chats/:id/token-usage', async (request) => {\n return cm.getAIChatTokenUsage(request.params.id)\n })\n\n // ==================== Debug ====================\n\n server.get('/_web/ai/debug/schema', async () => {\n return cm.getAiSchema()\n })\n\n server.post<{\n Body: { sql: string }\n }>('/_web/ai/debug/execute-sql', async (request, reply) => {\n const { sql } = request.body\n if (!sql || typeof sql !== 'string') {\n return reply.code(400).send({ error: 'sql is required' })\n }\n try {\n return cm.executeAiSQL(sql)\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e)\n return reply.code(400).send({ error: msg })\n }\n })\n\n server.post('/_web/ai/debug/clear-debug-context', async () => {\n const cleared = cm.clearAllDebugContext()\n return { success: true, cleared }\n })\n\n server.get<{\n Params: { id: string }\n }>('/_web/ai/chats/:id/estimate-tokens', async (request) => {\n const history = cm.getHistoryForAgent(request.params.id)\n const tokens = countMessagesTokens(history.map((m) => ({ role: m.role, content: m.content })))\n return { success: true, tokens, messageCount: history.length }\n })\n}\n","import type { FastifyInstance } from 'fastify'\nimport type { HttpRouteContext } from '../../context'\nimport { summaryService, buildPiModel } from '@openchatlab/node-runtime'\nimport type { SummaryServiceDeps, LlmConfig, PiModelConfig } from '@openchatlab/node-runtime'\n\nfunction createSummaryDeps(ctx: HttpRouteContext): SummaryServiceDeps | null {\n const store = ctx.llmConfigStore\n if (!store) return null\n return {\n getLlmConfig(): LlmConfig | null {\n const config = store.getDefaultAssistantConfig()\n if (!config) return null\n return config\n },\n buildPiModel(config: LlmConfig) {\n return buildPiModel(config as unknown as PiModelConfig)\n },\n }\n}\n\nexport function registerAiSummaryRoutes(server: FastifyInstance, ctx: HttpRouteContext): void {\n const deps = createSummaryDeps(ctx)\n if (!deps) return\n\n const { sessionAdapter: adapter } = ctx\n\n server.post<{\n Params: { id: string }\n Body: { segmentId: number; locale?: string; forceRegenerate?: boolean; strategy?: 'brief' | 'standard' }\n }>('/_web/sessions/:id/summaries/generate', async (request, reply) => {\n const { segmentId, locale, forceRegenerate, strategy } = request.body\n const result = await summaryService.generateSummary(adapter, request.params.id, segmentId, deps, {\n locale,\n forceRegenerate,\n strategy,\n })\n if ('error' in result && !result.success) {\n return reply.code(400).send({ error: result.error })\n }\n return result\n })\n\n server.post<{\n Params: { id: string }\n Body: { locale?: string; forceRegenerate?: boolean }\n }>('/_web/sessions/:id/summaries/generate-all', async (request, reply) => {\n const { locale, forceRegenerate } = request.body\n const result = await summaryService.generateAllSummaries(adapter, request.params.id, deps, {\n locale,\n forceRegenerate,\n })\n if (result.error) {\n return reply.code(400).send({ error: result.error })\n }\n return result\n })\n\n server.post<{\n Params: { id: string }\n Body: { segmentIds: number[] }\n }>('/_web/sessions/:id/summaries/check-can-generate', async (request) => {\n const { segmentIds } = request.body\n return summaryService.checkCanGenerate(adapter, request.params.id, segmentIds)\n })\n}\n","import fs from 'node:fs'\nimport path from 'node:path'\nimport type { FastifyInstance } from 'fastify'\nimport type { HttpRouteContext } from '../../context'\n\nfunction findLatestAiLogPath(logsDir: string): string | null {\n const aiLogDir = path.join(logsDir, 'ai')\n if (!fs.existsSync(aiLogDir)) return null\n\n const latest = fs\n .readdirSync(aiLogDir)\n .filter((name) => name.startsWith('ai_') && name.endsWith('.log'))\n .map((name) => {\n const filePath = path.join(aiLogDir, name)\n const stat = fs.statSync(filePath)\n return { path: filePath, mtimeMs: stat.mtimeMs }\n })\n .sort((a, b) => b.mtimeMs - a.mtimeMs)[0]\n\n return latest?.path ?? null\n}\n\nfunction resolveAiLogPath(ctx: HttpRouteContext): string | null {\n const currentPath = ctx.getCurrentAiLogPath?.()\n if (currentPath && fs.existsSync(currentPath)) return currentPath\n return findLatestAiLogPath(ctx.pathProvider.getLogsDir())\n}\n\nfunction isLoopbackAddress(address?: string): boolean {\n if (!address) return true\n const normalized = address.replace(/^::ffff:/, '')\n return (\n normalized === 'localhost' ||\n normalized === '::1' ||\n normalized === '0:0:0:0:0:0:0:1' ||\n normalized.startsWith('127.')\n )\n}\n\nexport function registerAiLogRoutes(server: FastifyInstance, ctx: HttpRouteContext): void {\n server.post('/_web/ai/logs/show', async (request, reply) => {\n if (!ctx.showInFolder) return reply.code(501).send({ success: false, error: 'Not supported' })\n if (!isLoopbackAddress(request.ip)) {\n return { success: false, error: 'Opening AI logs is only supported on this device' }\n }\n\n const logPath = resolveAiLogPath(ctx)\n if (!logPath) return { success: false, error: 'No AI log files found' }\n\n try {\n await ctx.showInFolder(logPath)\n return { success: true, path: logPath }\n } catch (error) {\n return { success: false, error: error instanceof Error ? error.message : String(error) }\n }\n })\n}\n","/**\n * 语义索引共享 Web 路由(Electron Internal API 与 CLI Web 复用)\n *\n * 入参以 sessionId 暴露,不暴露 db_path_hash。service 缺失时整组路由优雅跳过。\n * AI pipeline 直接调用 SemanticIndexService,不经过这些 HTTP 路由。\n */\n\nimport path from 'node:path'\nimport type { FastifyInstance } from 'fastify'\nimport type { HttpRouteContext } from '../../context'\nimport type { SemanticIndexConfig } from '@openchatlab/node-runtime'\nimport {\n SemanticIndexConfigStore,\n SEMANTIC_INDEX_CONFIG_FILE,\n isSemanticIndexConfigured,\n persistSemanticIndexConfig,\n resolveSemanticIndexApiKeySet,\n} from '@openchatlab/node-runtime'\n\nexport function registerSemanticIndexRoutes(server: FastifyInstance, ctx: HttpRouteContext): void {\n const service = ctx.semanticIndexService\n\n // When the vector service is unavailable (e.g. sqlite-vec failed to load), register config\n // routes backed by the config-only store so the settings UI can still read/write configuration.\n if (!service) {\n if (ctx.aiDataDir) {\n const configStore = new SemanticIndexConfigStore(path.join(ctx.aiDataDir, SEMANTIC_INDEX_CONFIG_FILE))\n const configResponse = (config: SemanticIndexConfig) => ({\n config,\n apiKeySet: resolveSemanticIndexApiKeySet(config, ctx.resolveApiKey),\n configured: isSemanticIndexConfigured(config),\n })\n server.get('/_web/ai/semantic-index/config', async () => ({\n ...configResponse(configStore.get()),\n modelStatus: 'idle' as const,\n }))\n // 向量库不可用时仍允许写配置与 API Key(key 落 auth-profiles,不依赖向量库),\n // 否则 API 模式在降级期无法保存 key,恢复后会出现\"已配置但无法检索\"。\n server.put<{ Body: { config: SemanticIndexConfig; apiKey?: string } }>(\n '/_web/ai/semantic-index/config',\n async (request) => {\n const config = persistSemanticIndexConfig(configStore, request.body.config, {\n apiKey: request.body.apiKey,\n writeAuthProfile: ctx.writeAuthProfile,\n })\n return { ...configResponse(config), modelStatus: 'idle' as const }\n }\n )\n }\n server.get('/_web/ai/semantic-index/enabled', async () => ({ sessions: [] }))\n server.get('/_web/ai/semantic-index/status', async () => ({ status: null }))\n server.post('/_web/ai/semantic-index/status', async () => ({ sessions: [] }))\n return\n }\n\n server.get('/_web/ai/semantic-index/config', async () => {\n return {\n config: await service.getConfig(),\n apiKeySet: await service.hasApiKey(),\n configured: await service.isConfigured(),\n modelStatus: await service.getModelStatus(),\n }\n })\n\n server.put<{ Body: { config: SemanticIndexConfig; apiKey?: string } }>(\n '/_web/ai/semantic-index/config',\n async (request) => {\n const config = await service.setConfig(request.body.config, { apiKey: request.body.apiKey })\n return {\n config,\n apiKeySet: await service.hasApiKey(),\n configured: await service.isConfigured(),\n modelStatus: await service.getModelStatus(),\n }\n }\n )\n\n server.get('/_web/ai/semantic-index/enabled', async () => {\n return { sessions: await service.listEnabledStatuses() }\n })\n\n server.get<{ Querystring: { sessionId: string } }>('/_web/ai/semantic-index/status', async (request) => {\n return { status: await service.status(request.query.sessionId) }\n })\n\n server.post<{ Body: { sessionIds: string[] } }>('/_web/ai/semantic-index/status', async (request) => {\n return { sessions: await service.statusForSessions(request.body.sessionIds ?? []) }\n })\n\n const sessionAction = (path: string, action: (sessionId: string) => void | Promise<void>): void => {\n server.post<{ Body: { sessionId: string } }>(path, async (request, reply) => {\n const { sessionId } = request.body\n if (!sessionId) return reply.code(400).send({ error: 'sessionId is required' })\n await action(sessionId)\n return { status: await service.status(sessionId) }\n })\n }\n\n sessionAction('/_web/ai/semantic-index/enable', (id) => service.enable(id))\n sessionAction('/_web/ai/semantic-index/remove', (id) => service.remove(id))\n sessionAction('/_web/ai/semantic-index/build', (id) => service.build(id))\n sessionAction('/_web/ai/semantic-index/pause', (id) => service.pause(id))\n sessionAction('/_web/ai/semantic-index/cancel', (id) => service.cancel(id))\n sessionAction('/_web/ai/semantic-index/rebuild', (id) => service.rebuild(id))\n\n server.post('/_web/ai/semantic-index/build-pending', async () => {\n await service.buildAllPending()\n return { sessions: await service.listEnabledStatuses() }\n })\n\n server.post('/_web/ai/semantic-index/cleanup', async () => {\n return await service.cleanupUnused()\n })\n\n server.post<{ Body: { sessionId: string; query: string; finalTopK?: number } }>(\n '/_web/ai/semantic-index/search',\n async (request, reply) => {\n const { sessionId, query, finalTopK } = request.body\n if (!sessionId || !query) return reply.code(400).send({ error: 'sessionId and query are required' })\n return service.search(sessionId, query, { finalTopK })\n }\n )\n}\n","/**\n * Merge routes — shared across CLI, Electron, and web-serve.\n *\n * Parse supports dual-mode:\n * - multipart/form-data → file upload (web/CLI)\n * - application/json { filePath } → local disk path (Electron)\n */\n\nimport * as fs from 'fs'\nimport * as os from 'os'\nimport * as path from 'path'\nimport type { FastifyInstance } from 'fastify'\nimport type { HttpRouteContext } from '../../context'\nimport {\n streamParseFileInfo,\n checkConflictsFromSources,\n buildMergedOutput,\n serializeChatLabToJsonl,\n exportSessionToJson,\n TempDbReader,\n TempDbWriter,\n} from '@openchatlab/node-runtime'\nimport type { MergerDataSource } from '@openchatlab/node-runtime'\nimport { sessionNotFound } from '../../errors'\n\nfunction ensureDb(ctx: HttpRouteContext, sessionId: string) {\n const db = ctx.dbManager.open(sessionId)\n if (!db) throw sessionNotFound(sessionId)\n return db\n}\n\nexport function registerMergeRoutes(server: FastifyInstance, ctx: HttpRouteContext): void {\n const { dbManager, mergeSessionCache: mergeCache, streamImport } = ctx\n if (!mergeCache) return\n\n // ── parse (dual-mode) ──────────────────────────────────────────────\n\n server.post('/_web/merge/parse', async (request, reply) => {\n const contentType = request.headers['content-type'] || ''\n let filePath: string\n let cleanupFile = false\n let cleanupDir: string | undefined\n\n if (contentType.includes('multipart/form-data')) {\n const data = await (request as any).file()\n if (!data) return reply.code(400).send({ error: 'No file uploaded' })\n\n const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'chatlab-merge-'))\n const tmpPath = path.join(tmpDir, data.filename || 'upload')\n const chunks: Buffer[] = []\n for await (const chunk of data.file) {\n chunks.push(chunk)\n }\n fs.writeFileSync(tmpPath, Buffer.concat(chunks))\n filePath = tmpPath\n cleanupFile = true\n cleanupDir = tmpDir\n } else {\n const body = request.body as { filePath?: string }\n if (!body?.filePath) return reply.code(400).send({ error: 'Missing filePath in body' })\n if (!fs.existsSync(body.filePath)) return reply.code(400).send({ error: 'File not found' })\n filePath = body.filePath\n }\n\n try {\n const result = await streamParseFileInfo(filePath, {\n createTempDatabase(sourceFilePath: string) {\n return mergeCache.createTempDatabase(path.basename(sourceFilePath))\n },\n onProgress() {\n /* HTTP parse has no progress stream */\n },\n })\n\n const filename = path.basename(filePath)\n const handle = mergeCache.store(filename, result.tempDbPath)\n\n return {\n handle,\n name: result.name,\n format: result.format,\n platform: result.platform,\n messageCount: result.messageCount,\n memberCount: result.memberCount,\n fileSize: result.fileSize,\n }\n } finally {\n if (cleanupFile) {\n try {\n fs.unlinkSync(filePath)\n } catch {\n /* ignore */\n }\n }\n if (cleanupDir) {\n try {\n fs.rmdirSync(cleanupDir)\n } catch {\n /* ignore */\n }\n }\n }\n })\n\n // ── conflicts ──────────────────────────────────────────────────────\n\n server.post<{ Body: { handles: string[] } }>('/_web/merge/conflicts', async (request, reply) => {\n const { handles } = request.body as { handles?: string[] }\n if (!handles || !Array.isArray(handles) || handles.length === 0) {\n return reply.code(400).send({ error: 'Missing or empty handles array' })\n }\n\n const readers: TempDbReader[] = []\n try {\n const dataSources: Array<{ source: MergerDataSource; filename: string }> = []\n for (const handle of handles) {\n const entry = mergeCache.openReader(handle)\n if (!entry) return reply.code(404).send({ error: `Handle not found: ${handle}` })\n readers.push(entry.reader)\n dataSources.push({ source: entry.reader.toDataSource(), filename: entry.filename })\n }\n return checkConflictsFromSources(dataSources)\n } finally {\n for (const r of readers) r.close()\n }\n })\n\n // ── execute ────────────────────────────────────────────────────────\n\n server.post<{\n Body: { handles: string[]; outputName: string; format?: 'json' | 'jsonl'; andImport?: boolean }\n }>('/_web/merge/execute', async (request, reply) => {\n const { handles, outputName, format = 'json', andImport } = request.body as any\n if (!handles || !Array.isArray(handles) || handles.length === 0) {\n return reply.code(400).send({ error: 'Missing handles' })\n }\n if (!outputName) {\n return reply.code(400).send({ error: 'Missing outputName' })\n }\n\n const readers: TempDbReader[] = []\n try {\n const dataSources: Array<{ source: MergerDataSource; filename: string }> = []\n for (const handle of handles) {\n const entry = mergeCache.openReader(handle)\n if (!entry) return reply.code(404).send({ error: `Handle not found: ${handle}` })\n readers.push(entry.reader)\n dataSources.push({ source: entry.reader.toDataSource(), filename: entry.filename })\n }\n\n const merged = buildMergedOutput(dataSources, outputName)\n\n let sessionId: string | undefined\n if (andImport) {\n if (!streamImport) {\n return reply.code(501).send({ error: 'Import not available on this server' })\n }\n const jsonData = JSON.stringify(merged.chatLabData)\n const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'chatlab-merged-'))\n const safeName = path.basename(outputName).replace(/[/\\\\?%*:|\"<>]/g, '_') || 'merged'\n const tmpPath = path.join(tmpDir, `${safeName}.json`)\n fs.writeFileSync(tmpPath, jsonData, 'utf-8')\n\n try {\n const importResult = await streamImport(dbManager, tmpPath)\n sessionId = importResult.sessionId\n } finally {\n try {\n fs.unlinkSync(tmpPath)\n } catch {\n /* ignore */\n }\n try {\n fs.rmdirSync(tmpDir)\n } catch {\n /* ignore */\n }\n }\n }\n\n for (const handle of handles) {\n mergeCache.delete(handle)\n }\n\n if (format === 'jsonl') {\n const lines: string[] = []\n for (const line of serializeChatLabToJsonl(merged.chatLabData)) {\n lines.push(line)\n }\n return { success: true, sessionId, data: lines.join('\\n') }\n }\n\n return { success: true, sessionId, data: merged.chatLabData }\n } finally {\n for (const r of readers) r.close()\n }\n })\n\n // ── clear ──────────────────────────────────────────────────────────\n\n server.post<{ Body: { handle?: string } }>('/_web/merge/clear', async (request) => {\n const { handle } = (request.body as any) || {}\n if (handle) {\n mergeCache.delete(handle)\n } else {\n mergeCache.clear()\n }\n return { success: true }\n })\n\n // ── export sessions for merge ──────────────────────────────────────\n\n server.post<{\n Body: { sessionIds: string[] }\n }>('/_web/sessions/export-for-merge', async (request, reply) => {\n const { sessionIds } = request.body as { sessionIds?: string[] }\n if (!sessionIds || !Array.isArray(sessionIds) || sessionIds.length === 0) {\n return reply.code(400).send({ error: 'Missing sessionIds' })\n }\n\n const handles: Array<{ sessionId: string; handle: string }> = []\n for (const sid of sessionIds) {\n const db = ensureDb(ctx, sid)\n const exported = exportSessionToJson(db)\n const { db: tempDb, tempDbPath } = mergeCache.createTempDatabase(exported.meta.name)\n\n const writer = new TempDbWriter(tempDb)\n writer.writeMeta({\n name: exported.meta.name,\n platform: exported.meta.platform,\n type: exported.meta.type,\n groupId: exported.meta.groupId,\n groupAvatar: exported.meta.groupAvatar,\n })\n writer.writeMembers(\n exported.members.map((m) => ({\n platformId: m.platformId,\n accountName: m.accountName,\n groupNickname: m.groupNickname,\n avatar: m.avatar,\n }))\n )\n writer.writeMessages(\n exported.messages.map((msg) => ({\n senderPlatformId: msg.sender,\n senderAccountName: msg.accountName,\n senderGroupNickname: msg.groupNickname,\n timestamp: msg.timestamp,\n type: msg.type,\n content: msg.content,\n }))\n )\n writer.finish()\n\n const handle = mergeCache.store(exported.meta.name, tempDbPath)\n handles.push({ sessionId: sid, handle })\n }\n\n return { success: true, handles }\n })\n}\n","import * as fs from 'fs'\nimport * as fsp from 'fs/promises'\nimport * as path from 'path'\nimport type { FastifyInstance } from 'fastify'\nimport type { HttpRouteContext } from '../../context'\n\nasync function getDirSize(dirPath: string): Promise<number> {\n let totalSize = 0\n try {\n if (!fs.existsSync(dirPath)) return 0\n const files = await fsp.readdir(dirPath, { withFileTypes: true })\n for (const file of files) {\n const filePath = path.join(dirPath, file.name)\n if (file.isDirectory()) {\n totalSize += await getDirSize(filePath)\n } else {\n const stat = await fsp.stat(filePath)\n totalSize += stat.size\n }\n }\n } catch {\n /* directory inaccessible */\n }\n return totalSize\n}\n\nasync function getFileCount(dirPath: string): Promise<number> {\n let count = 0\n try {\n if (!fs.existsSync(dirPath)) return 0\n const files = await fsp.readdir(dirPath, { withFileTypes: true })\n for (const file of files) {\n const filePath = path.join(dirPath, file.name)\n if (file.isDirectory()) {\n count += await getFileCount(filePath)\n } else {\n count++\n }\n }\n } catch {\n /* directory inaccessible */\n }\n return count\n}\n\nfunction hasChatLabDatabases(dirPath: string): boolean {\n try {\n const markerPath = path.join(dirPath, '.chatlab')\n const dbDir = path.join(dirPath, 'databases')\n if (!fs.existsSync(markerPath) || !fs.existsSync(dbDir)) return false\n return fs.readdirSync(dbDir).some((file) => file.endsWith('.db'))\n } catch {\n return false\n }\n}\n\nexport function registerCacheRoutes(server: FastifyInstance, ctx: HttpRouteContext): void {\n const pp = ctx.pathProvider\n const downloadsDir = ctx.downloadsDir ?? pp.getDownloadsDir()\n\n server.get('/_web/cache/info', async () => {\n const directories = [\n {\n id: 'databases',\n name: 'settings.storage.cache.databases.name',\n description: 'settings.storage.cache.databases.description',\n path: pp.getDatabaseDir(),\n scope: 'user-data',\n rootPath: pp.getUserDataDir(),\n icon: 'i-heroicons-circle-stack',\n canClear: false,\n },\n {\n id: 'ai',\n name: 'settings.storage.cache.ai.name',\n description: 'settings.storage.cache.ai.description',\n path: pp.getAiDataDir(),\n scope: 'system-data',\n rootPath: pp.getSystemDir(),\n icon: 'i-heroicons-sparkles',\n canClear: false,\n },\n {\n id: 'cache',\n name: 'settings.storage.cache.statsCache.name',\n description: 'settings.storage.cache.statsCache.description',\n path: pp.getCacheDir(),\n scope: 'system-data',\n rootPath: pp.getSystemDir(),\n icon: 'i-heroicons-bolt',\n canClear: true,\n },\n {\n id: 'logs',\n name: 'settings.storage.cache.logs.name',\n description: 'settings.storage.cache.logs.description',\n path: pp.getLogsDir(),\n scope: 'system-data',\n rootPath: pp.getSystemDir(),\n icon: 'i-heroicons-document-text',\n canClear: true,\n },\n ]\n\n const results = await Promise.all(\n directories.map(async (dir) => {\n const [size, fileCount] = await Promise.all([getDirSize(dir.path), getFileCount(dir.path)])\n return { ...dir, size, fileCount, exists: fs.existsSync(dir.path) }\n })\n )\n\n return {\n baseDir: pp.getSystemDir(),\n directories: results,\n totalSize: results.reduce((sum, d) => sum + d.size, 0),\n }\n })\n\n server.post<{ Body: { cacheId: string } }>('/_web/cache/clear', async (request) => {\n const { cacheId } = request.body\n const allowedDirs: Record<string, string> = {\n cache: pp.getCacheDir(),\n logs: pp.getLogsDir(),\n }\n const dirPath = allowedDirs[cacheId]\n if (!dirPath) return { success: false, error: 'Not allowed to clear this directory' }\n\n if (!fs.existsSync(dirPath)) return { success: true }\n\n const files = await fsp.readdir(dirPath)\n for (const file of files) {\n const filePath = path.join(dirPath, file)\n const stat = await fsp.stat(filePath)\n if (stat.isDirectory()) {\n await fsp.rm(filePath, { recursive: true })\n } else {\n await fsp.unlink(filePath)\n }\n }\n return { success: true }\n })\n\n server.get('/_web/cache/data-dir', async () => {\n const pending = ctx.getPendingDataDirMigration?.()\n return {\n path: pp.getUserDataDir(),\n defaultPath: ctx.defaultUserDataDir,\n isCustom: ctx.isCustomDataDir ?? false,\n canSetDataDir: ctx.canSetDataDir ?? Boolean(ctx.setDataDir),\n managedScope: 'chat-databases',\n managedDescription: 'settings.storage.dataLocation.managedDescription',\n hasLegacyDataAtDefaultDir:\n Boolean(ctx.defaultUserDataDir) &&\n path.resolve(pp.getUserDataDir()) !== path.resolve(ctx.defaultUserDataDir ?? '') &&\n hasChatLabDatabases(ctx.defaultUserDataDir ?? ''),\n pendingMigration: pending\n ? {\n from: pending.from,\n to: pending.to,\n createdAt: pending.createdAt,\n }\n : undefined,\n }\n })\n\n server.post<{ Body: { path?: string | null; migrate?: boolean } }>('/_web/cache/data-dir', async (request, reply) => {\n if (!ctx.setDataDir) {\n return reply.code(501).send({ success: false, error: 'Data directory changes are not supported' })\n }\n\n const targetPath = typeof request.body?.path === 'string' ? request.body.path : null\n const migrate = request.body?.migrate !== false\n return ctx.setDataDir(targetPath, migrate)\n })\n\n server.get('/_web/cache/latest-import-log', async () => {\n const importLogDir = path.join(pp.getLogsDir(), 'import')\n if (!fs.existsSync(importLogDir)) {\n return { success: false, error: 'Log directory not found' }\n }\n\n const files = await fsp.readdir(importLogDir)\n const logFiles = files.filter((f) => f.startsWith('import_') && f.endsWith('.log'))\n if (logFiles.length === 0) {\n return { success: false, error: 'No import logs found' }\n }\n\n const fileStats = await Promise.all(\n logFiles.map(async (f) => {\n const filePath = path.join(importLogDir, f)\n const stat = await fsp.stat(filePath)\n return { name: f, path: filePath, mtime: stat.mtime.getTime() }\n })\n )\n fileStats.sort((a, b) => b.mtime - a.mtime)\n\n return { success: true, path: fileStats[0].path, name: fileStats[0].name }\n })\n\n server.post<{ Body: { filename: string; dataUrl: string } }>('/_web/cache/save-to-downloads', async (request) => {\n const { filename, dataUrl } = request.body\n if (!filename || !dataUrl) {\n return { success: false, error: 'filename and dataUrl are required' }\n }\n\n let buffer: Buffer\n if (dataUrl.includes(';base64,')) {\n const base64Data = dataUrl.split(';base64,')[1]\n buffer = Buffer.from(base64Data, 'base64')\n } else if (dataUrl.includes('charset=utf-8,')) {\n const textData = dataUrl.split('charset=utf-8,')[1]\n buffer = Buffer.from(decodeURIComponent(textData), 'utf-8')\n } else {\n const base64Data = dataUrl.replace(/^data:[^,]+,/, '')\n buffer = Buffer.from(base64Data, 'base64')\n }\n\n if (!fs.existsSync(downloadsDir)) {\n fs.mkdirSync(downloadsDir, { recursive: true })\n }\n\n const filePath = path.join(downloadsDir, filename)\n fs.writeFileSync(filePath, buffer)\n\n return { success: true, filePath }\n })\n\n // Shell operations — require platform-specific callbacks\n\n server.post<{ Body: { cacheId: string } }>('/_web/cache/open-dir', async (request, reply) => {\n if (!ctx.openDirectory) return reply.code(501).send({ success: false, error: 'Not supported' })\n\n const { cacheId } = request.body\n const dirPaths: Record<string, string> = {\n base: pp.getSystemDir(),\n userData: pp.getUserDataDir(),\n databases: pp.getDatabaseDir(),\n cache: pp.getCacheDir(),\n ai: pp.getAiDataDir(),\n logs: pp.getLogsDir(),\n downloads: downloadsDir,\n }\n const dirPath = dirPaths[cacheId]\n if (!dirPath) return { success: false, error: 'Unknown directory' }\n\n if (!fs.existsSync(dirPath)) {\n await fsp.mkdir(dirPath, { recursive: true })\n }\n\n try {\n await ctx.openDirectory(dirPath)\n } catch (error) {\n return { success: false, error: error instanceof Error ? error.message : String(error) }\n }\n return { success: true }\n })\n\n server.post<{ Body: { filePath: string } }>('/_web/cache/show-in-folder', async (request, reply) => {\n if (!ctx.showInFolder) return reply.code(501).send({ success: false, error: 'Not supported' })\n\n const { filePath } = request.body\n if (!filePath) return { success: false, error: 'filePath is required' }\n\n try {\n await ctx.showInFolder(filePath)\n } catch (error) {\n return { success: false, error: error instanceof Error ? error.message : String(error) }\n }\n return { success: true }\n })\n}\n","import type { FastifyInstance } from 'fastify'\nimport type { HttpRouteContext } from '../../context'\n\nexport function registerTelemetryRoutes(server: FastifyInstance, ctx: HttpRouteContext): void {\n server.get('/_web/telemetry/enabled', async () => {\n return { enabled: ctx.analyticsService?.getEnabled() ?? false }\n })\n\n server.post<{ Body: { enabled?: boolean } }>('/_web/telemetry/enabled', async (req) => {\n if (!ctx.analyticsService || typeof req.body?.enabled !== 'boolean') return { success: false }\n ctx.analyticsService.setEnabled(req.body.enabled)\n return { success: true }\n })\n\n server.post<{ Body: { eventName: string; properties?: Record<string, string | number> } }>(\n '/_web/telemetry/track',\n async (req, reply) => {\n if (!ctx.analyticsService) return { ok: true }\n const { eventName, properties } = req.body ?? {}\n if (!eventName) return reply.code(400).send({ error: 'eventName required' })\n if (eventName === 'app_active') {\n await ctx.analyticsService.trackDailyActive(properties)\n } else {\n await ctx.analyticsService.track(eventName, properties)\n }\n return { ok: true }\n }\n )\n}\n","import type { FastifyInstance } from 'fastify'\nimport { appLogger } from '@openchatlab/node-runtime'\nimport type { HttpRouteContext } from '../../context'\n\ninterface LogReportBody {\n level?: 'error' | 'warn'\n message: string\n stack?: string\n url?: string\n}\n\n/**\n * Front-end error report sink. The browser can't write files, so uncaught\n * front-end errors are POSTed here and appended to logs/app.log (scope 'web').\n */\nexport function registerLogRoutes(server: FastifyInstance, _ctx: HttpRouteContext): void {\n server.post<{ Body: LogReportBody }>('/_web/logs/report', async (req, reply) => {\n const { level, message, stack, url } = req.body ?? ({} as LogReportBody)\n if (!message) return reply.code(400).send({ error: 'message required' })\n\n const detail = [url ? `url=${url}` : null, stack ? `\\n${stack}` : null].filter(Boolean).join(' ')\n if (level === 'warn') appLogger.warn('web', message, detail || undefined)\n else appLogger.error('web', message, detail || undefined)\n return { ok: true }\n })\n}\n","/**\n * Aggregate route registration — one call to register all shared routes.\n *\n * CLI Server and Electron Internal Server call this instead of\n * importing individual route modules.\n */\n\nimport type { FastifyInstance } from 'fastify'\nimport type { HttpRouteContext } from './context'\nimport { PreferencesManager } from '@openchatlab/node-runtime'\nimport { registerSystemRoutes } from './routes/system'\nimport { registerRestSessionRoutes } from './routes/sessions'\nimport { registerImportRoutes } from './routes/imports'\nimport { registerSessionRoutes } from './routes/web/sessions'\nimport { registerMemberRoutes } from './routes/web/members'\nimport { registerContactsRoutes } from './routes/web/contacts'\nimport { registerPeopleRelationshipsRoutes } from './routes/web/people-relationships'\nimport { registerGlobalInsightRoutes } from './routes/web/global-insight'\nimport { registerPreferencesRoutes } from './routes/web/preferences'\nimport { registerAnalyticsRoutes } from './routes/web/analytics'\nimport { registerSqlRoutes } from './routes/web/sql'\nimport { registerSessionIndexRoutes } from './routes/web/session-index'\nimport { registerExportRoutes } from './routes/web/export'\nimport { registerNlpRoutes } from './routes/web/nlp'\nimport { registerAiAssistantRoutes } from './routes/web/ai-assistants'\nimport { registerAiSkillRoutes } from './routes/web/ai-skills'\nimport { registerAiLlmRoutes } from './routes/web/ai-llm'\nimport { registerAiLlmStreamRoutes } from './routes/web/ai-llm-stream'\nimport { registerAiAgentStreamRoutes } from './routes/web/ai-agent-stream'\nimport { registerAiToolRoutes } from './routes/web/ai-tools'\nimport { registerAiChatRoutes } from './routes/web/ai-chats'\nimport { registerAiSummaryRoutes } from './routes/web/ai-summaries'\nimport { registerAiLogRoutes } from './routes/web/ai-logs'\nimport { registerSemanticIndexRoutes } from './routes/web/ai-semantic-index'\nimport { registerMergeRoutes } from './routes/web/merge'\nimport { registerCacheRoutes } from './routes/web/cache'\nimport { registerTelemetryRoutes } from './routes/web/telemetry'\nimport { registerLogRoutes } from './routes/web/logs'\n\nexport interface SharedRouteOptions {\n /** When true, AI routes will throw on missing dependencies instead of silently skipping */\n requireAi?: boolean\n}\n\nexport function registerSharedRoutes(\n server: FastifyInstance,\n ctx: HttpRouteContext,\n options?: SharedRouteOptions\n): void {\n // Ensure all routes share one PreferencesManager instance to avoid stale-cache\n // overwrites between the session owner routes and the preferences routes.\n const resolvedCtx: HttpRouteContext = ctx.preferencesManager\n ? ctx\n : { ...ctx, preferencesManager: new PreferencesManager(ctx.pathProvider.getSystemDir()) }\n\n // REST API (/api/v1/*)\n registerSystemRoutes(server, resolvedCtx)\n registerRestSessionRoutes(server, resolvedCtx)\n registerImportRoutes(server, resolvedCtx)\n\n // Web UI API (/_web/*)\n registerSessionRoutes(server, resolvedCtx)\n registerMemberRoutes(server, resolvedCtx)\n registerContactsRoutes(server, resolvedCtx)\n registerPeopleRelationshipsRoutes(server, resolvedCtx)\n registerGlobalInsightRoutes(server, resolvedCtx)\n registerPreferencesRoutes(server, resolvedCtx)\n registerAnalyticsRoutes(server, resolvedCtx)\n registerSqlRoutes(server, resolvedCtx)\n registerSessionIndexRoutes(server, resolvedCtx)\n registerExportRoutes(server, resolvedCtx)\n registerNlpRoutes(server, resolvedCtx)\n\n if (options?.requireAi) {\n const missing: string[] = []\n if (!resolvedCtx.aiDataDir) missing.push('aiDataDir')\n if (!resolvedCtx.aiChatManager) missing.push('aiChatManager')\n if (!resolvedCtx.assistantManager) missing.push('assistantManager')\n if (!resolvedCtx.skillManagerCore) missing.push('skillManagerCore')\n if (!resolvedCtx.llmConfigStore) missing.push('llmConfigStore')\n if (!resolvedCtx.customProviderStore) missing.push('customProviderStore')\n if (!resolvedCtx.customModelStore) missing.push('customModelStore')\n if (!resolvedCtx.runAgentStream) missing.push('runAgentStream')\n if (missing.length > 0) {\n throw new Error(`[http-routes] requireAi is set but missing AI dependencies: ${missing.join(', ')}`)\n }\n }\n\n registerAiAssistantRoutes(server, resolvedCtx)\n registerAiSkillRoutes(server, resolvedCtx)\n registerAiLlmRoutes(server, resolvedCtx)\n registerAiLlmStreamRoutes(server, resolvedCtx)\n registerAiAgentStreamRoutes(server, resolvedCtx)\n registerAiToolRoutes(server, resolvedCtx)\n registerAiChatRoutes(server, resolvedCtx)\n registerAiSummaryRoutes(server, resolvedCtx)\n registerAiLogRoutes(server, resolvedCtx)\n registerSemanticIndexRoutes(server, resolvedCtx)\n\n // Merge routes (graceful skip when mergeSessionCache is absent)\n registerMergeRoutes(server, resolvedCtx)\n\n // Cache/storage routes\n registerCacheRoutes(server, resolvedCtx)\n\n // Telemetry routes\n registerTelemetryRoutes(server, resolvedCtx)\n\n // Front-end log report + open logs dir\n registerLogRoutes(server, resolvedCtx)\n}\n","/**\n * ChatLab HTTP API — Bearer Token authentication hook\n *\n * Shared auth middleware for CLI Server and Electron Internal Server.\n * URL classification: /api/* always requires token, /_web/* conditionally,\n * static files and SPA fallback are public.\n */\n\nimport type { FastifyRequest, FastifyReply } from 'fastify'\nimport { timingSafeEqual, createHmac, randomBytes } from 'crypto'\nimport { unauthorized, errorResponse } from './errors'\n\nlet cachedToken: string | null = null\nlet requireAuthEnabled = false\n\nexport function setAuthToken(token: string): void {\n cachedToken = token\n}\n\n/**\n * When enabled, /_web/* routes also require Bearer token (same as /api/*).\n * Used for server/headless deployments where same-origin assumption doesn't hold.\n */\nexport function setRequireAuth(enabled: boolean): void {\n requireAuthEnabled = enabled\n}\n\n// Compare via HMAC digests (fixed 32-byte length) to avoid leaking token length\nconst hmacKey = randomBytes(32)\n\nfunction safeTokenCompare(a: string, b: string): boolean {\n const hashA = createHmac('sha256', hmacKey).update(a).digest()\n const hashB = createHmac('sha256', hmacKey).update(b).digest()\n return timingSafeEqual(hashA, hashB)\n}\n\nexport async function authHook(request: FastifyRequest, reply: FastifyReply): Promise<void> {\n if (!cachedToken) return\n\n const url = request.url\n\n if (url.startsWith('/api/')) {\n return requireBearerToken(request, reply)\n }\n\n if (url.startsWith('/_web/')) {\n if (requireAuthEnabled) return requireBearerToken(request, reply)\n return\n }\n\n // Static files and SPA fallback are public\n}\n\nfunction requireBearerToken(request: FastifyRequest, reply: FastifyReply): void {\n if (!cachedToken) return\n\n const authHeader = request.headers.authorization\n if (!authHeader || !authHeader.startsWith('Bearer ')) {\n const err = unauthorized()\n reply.code(err.statusCode).send(errorResponse(err))\n return\n }\n\n const token = authHeader.slice(7)\n\n if (!safeTokenCompare(token, cachedToken)) {\n const err = unauthorized()\n reply.code(err.statusCode).send(errorResponse(err))\n return\n }\n}\n","import type { FastifyInstance } from 'fastify'\nimport type { HttpRouteContext } from '../../context'\n\ntype AutomationRouteHost = Pick<HttpRouteContext, 'automation'>\n\nfunction requireAutomation(ctx: AutomationRouteHost) {\n return ctx.automation ?? null\n}\n\nfunction normalizeBaseUrl(input: string): string {\n let url = input.trim().replace(/\\/+$/, '')\n if (url && !/^https?:\\/\\//i.test(url)) url = `http://${url}`\n if (url && !url.endsWith('/api/v1')) {\n url = url.replace(/\\/api\\/v1$/, '') + '/api/v1'\n }\n return url\n}\n\nfunction buildRemoteSessionsUrl(\n baseUrl: string,\n query: { keyword?: string; limit?: string | number; cursor?: string } = {}\n): string {\n const searchParams = new URLSearchParams()\n searchParams.set('format', 'chatlab')\n if (query.keyword?.trim()) searchParams.set('keyword', query.keyword.trim())\n if (query.limit && Number(query.limit) > 0) searchParams.set('limit', String(query.limit))\n if (query.cursor) searchParams.set('cursor', query.cursor)\n return `${baseUrl}/sessions?${searchParams.toString()}`\n}\n\nfunction asRecord(value: unknown): Record<string, unknown> | undefined {\n return value && typeof value === 'object' && !Array.isArray(value) ? (value as Record<string, unknown>) : undefined\n}\n\nfunction parseRemoteSessionsResponse(body: string): {\n sessions: unknown[]\n page?: { hasMore: boolean; nextCursor?: string }\n} {\n const parsed = JSON.parse(body)\n\n let sessions: unknown[]\n let pageSource: Record<string, unknown> | undefined\n\n if (Array.isArray(parsed)) {\n sessions = parsed\n } else {\n const record = asRecord(parsed)\n const data = asRecord(record?.data)\n const sessionsSource = record?.sessions ?? data?.sessions ?? record?.data\n sessions = Array.isArray(sessionsSource) ? sessionsSource : []\n pageSource = asRecord(record?.page) ?? asRecord(data?.page)\n }\n\n return {\n sessions,\n page:\n pageSource && typeof pageSource === 'object'\n ? {\n hasMore: Boolean(pageSource.hasMore),\n nextCursor: typeof pageSource.nextCursor === 'string' ? pageSource.nextCursor : undefined,\n }\n : undefined,\n }\n}\n\nexport function registerAutomationRoutes(server: FastifyInstance, ctx: AutomationRouteHost): void {\n server.get('/_web/automation/config', async (_request, reply) => {\n const automation = requireAutomation(ctx)\n if (!automation?.serverInfo) return reply.code(404).send({ error: 'Automation is not enabled' })\n return {\n enabled: true,\n port: automation.serverInfo.port,\n token: automation.serverInfo.token,\n host: automation.serverInfo.host,\n }\n })\n\n server.get('/_web/automation/data-sources', async (_request, reply) => {\n const automation = requireAutomation(ctx)\n if (!automation) return reply.code(404).send({ error: 'Automation is not enabled' })\n return automation.dsManager.loadAll()\n })\n\n server.post<{\n Body: { name?: string; baseUrl: string; token: string; intervalMinutes: number; pullLimit?: number }\n }>('/_web/automation/data-sources', async (request, reply) => {\n const automation = requireAutomation(ctx)\n if (!automation) return reply.code(404).send({ error: 'Automation is not enabled' })\n const ds = automation.dsManager.add(request.body)\n automation.reloadTimer?.((ds as { id: string }).id)\n return ds\n })\n\n server.patch<{\n Params: { id: string }\n Body: {\n name?: string\n baseUrl?: string\n token?: string\n intervalMinutes?: number\n pullLimit?: number\n enabled?: boolean\n }\n }>('/_web/automation/data-sources/:id', async (request, reply) => {\n const automation = requireAutomation(ctx)\n if (!automation) return reply.code(404).send({ error: 'Automation is not enabled' })\n const ds = automation.dsManager.update(request.params.id, request.body)\n if (!ds) return reply.code(404).send({ error: 'Data source not found' })\n automation.reloadTimer?.((ds as { id: string }).id)\n return ds\n })\n\n server.delete<{ Params: { id: string } }>('/_web/automation/data-sources/:id', async (request, reply) => {\n const automation = requireAutomation(ctx)\n if (!automation) return reply.code(404).send({ error: 'Automation is not enabled' })\n automation.stopTimer?.(request.params.id)\n const ok = automation.dsManager.delete(request.params.id)\n if (!ok) return reply.code(404).send({ error: 'Data source not found' })\n return { success: true }\n })\n\n server.post<{\n Params: { id: string }\n Body: { sessions: Array<{ name: string; remoteSessionId: string }> }\n }>('/_web/automation/data-sources/:id/sessions', async (request, reply) => {\n const automation = requireAutomation(ctx)\n if (!automation) return reply.code(404).send({ error: 'Automation is not enabled' })\n const added = automation.dsManager.addSessions(request.params.id, request.body.sessions)\n if (added.length === 0 && !automation.dsManager.get(request.params.id)) {\n return reply.code(404).send({ error: 'Data source not found' })\n }\n automation.reloadTimer?.(request.params.id, true)\n for (const sess of added as Array<{ id: string }>) {\n void automation.pullEngine.triggerPull(request.params.id, sess.id).catch(() => undefined)\n }\n return added\n })\n\n server.delete<{\n Params: { id: string; sessId: string }\n Querystring: { deleteData?: string }\n }>('/_web/automation/data-sources/:id/sessions/:sessId', async (request, reply) => {\n const automation = requireAutomation(ctx)\n if (!automation) return reply.code(404).send({ error: 'Automation is not enabled' })\n const removed = automation.dsManager.removeSession(request.params.id, request.params.sessId)\n if (!removed) return reply.code(404).send({ error: 'Session not found' })\n automation.reloadTimer?.(request.params.id)\n if (request.query.deleteData === 'true' && removed.targetSessionId) {\n automation.deleteSessionData?.(removed.targetSessionId)\n }\n return { success: true }\n })\n\n server.post<{ Params: { id: string }; Body: { sessionId?: string } }>(\n '/_web/automation/data-sources/:id/pull',\n async (request, reply) => {\n const automation = requireAutomation(ctx)\n if (!automation) return reply.code(404).send({ error: 'Automation is not enabled' })\n return automation.pullEngine.triggerPull(request.params.id, request.body?.sessionId)\n }\n )\n\n server.post<{ Params: { id: string } }>('/_web/automation/data-sources/:id/pull-all', async (request, reply) => {\n const automation = requireAutomation(ctx)\n if (!automation) return reply.code(404).send({ error: 'Automation is not enabled' })\n return automation.pullEngine.triggerPullAll(request.params.id)\n })\n\n server.get('/_web/automation/sync-progress', async (_request, reply) => {\n const automation = requireAutomation(ctx)\n if (!automation) return reply.code(404).send({ error: 'Automation is not enabled' })\n return automation.pullEngine.getProgress()\n })\n\n server.get<{\n Querystring: { baseUrl: string; token?: string; keyword?: string; limit?: string; cursor?: string }\n }>('/_web/automation/remote-sessions', async (request, reply) => {\n const automation = requireAutomation(ctx)\n if (!automation) return reply.code(404).send({ error: 'Automation is not enabled' })\n\n const { baseUrl, token, keyword, limit, cursor } = request.query\n if (!baseUrl) return reply.code(400).send({ error: 'baseUrl is required' })\n\n const url = buildRemoteSessionsUrl(normalizeBaseUrl(baseUrl), { keyword, limit, cursor })\n const headers: Record<string, string> = { Accept: 'application/json' }\n if (token) headers.Authorization = `Bearer ${token}`\n\n try {\n const resp = await fetch(url, { headers, signal: AbortSignal.timeout(30_000) })\n if (!resp.ok) throw new Error(`Remote server returned HTTP ${resp.status}`)\n const body = await resp.text()\n return parseRemoteSessionsResponse(body)\n } catch (err) {\n return reply.code(502).send({ error: err instanceof Error ? err.message : 'Failed to fetch remote sessions' })\n }\n })\n}\n","/**\n * ChatLab Internal Web API — /_web/ routes\n *\n * 供 CLI Web 前端使用的内部 API(无认证、UI 友好的响应格式)。\n * 数据格式直接对齐 QueryAdapter 接口,避免前端二次转换。\n *\n * Route modules:\n * sessions – Session CRUD\n * members – Member management\n * analytics – Stats and advanced analytics\n * sql – SQL Lab and plugin query\n * sessionIndex – Session index generation + FTS\n * summaries – LLM summary generation\n * import – File / directory / incremental import + demo\n * merge – Merge parse / conflicts / execute\n * export – Markdown export\n * cache – Storage management + save to downloads + show in folder\n */\n\nimport * as os from 'os'\nimport * as path from 'path'\nimport type { FastifyInstance } from 'fastify'\nimport type { PathProvider } from '@openchatlab/core'\nimport type {\n DatabaseManager,\n AIChatManager,\n AssistantManager,\n SkillManagerCore,\n LLMConfigStore,\n CustomProviderStore,\n CustomModelStore,\n PendingDataDirMigration,\n RuntimeIdentity,\n} from '@openchatlab/node-runtime'\nimport {\n createDatabaseManagerAdapter,\n createNodeDataDirSwitch,\n getDefaultNodeUserDataDir,\n getPendingNodeDataDirMigration,\n AnalyticsService,\n type SemanticIndexRuntime,\n} from '@openchatlab/node-runtime'\nimport { registerSharedRoutes } from '@openchatlab/http-routes'\nimport type { HttpRouteContext } from '@openchatlab/http-routes'\nimport { MergeSessionCache } from '../../../merger/merge-cache'\nimport { registerImportRoutes } from './import'\nimport { openDirectoryPath, showPathInFolder } from './cache'\nimport { getVersion } from '../../../version'\nimport { buildWebUpdateCheckResult } from './update-check'\nimport { getServerAiLogger } from '../../../ai/logger'\n\nexport interface AiContextOptions {\n aiDataDir: string\n aiChatManager: AIChatManager\n assistantManager: AssistantManager\n skillManagerCore: SkillManagerCore\n llmConfigStore: LLMConfigStore\n customProviderStore: CustomProviderStore\n customModelStore: CustomModelStore\n runAgentStream?: HttpRouteContext['runAgentStream']\n}\n\nexport function registerWebRoutes(\n server: FastifyInstance,\n dbManager: DatabaseManager,\n options?: {\n pathProvider?: PathProvider\n nativeBinding?: string\n runtimeIdentity?: RuntimeIdentity\n aiContext?: AiContextOptions\n /** 由 server 入口注入的共享语义索引运行时;传入时由调用方管理生命周期 */\n semanticIndexService?: SemanticIndexRuntime\n }\n): void {\n const adapter = createDatabaseManagerAdapter(dbManager)\n\n const mergeCache = options?.pathProvider\n ? new MergeSessionCache(options.pathProvider, { nativeBinding: options.nativeBinding })\n : null\n mergeCache?.cleanupOrphans()\n\n const fallbackPathProvider: PathProvider = {\n getSystemDir: () => path.join(os.homedir(), '.chatlab'),\n getUserDataDir: () => path.join(os.homedir(), '.chatlab', 'data'),\n getDatabaseDir: () => path.join(os.homedir(), '.chatlab', 'data', 'databases'),\n getVectorDir: () => path.join(os.homedir(), '.chatlab', 'data', 'vector'),\n getAiDataDir: () => path.join(os.homedir(), '.chatlab', 'ai'),\n getSettingsDir: () => path.join(os.homedir(), '.chatlab', 'settings'),\n getCacheDir: () => path.join(os.homedir(), '.chatlab', 'cache'),\n getTempDir: () => path.join(os.homedir(), '.chatlab', 'temp'),\n getLogsDir: () => path.join(os.homedir(), '.chatlab', 'logs'),\n getDownloadsDir: () => path.join(os.homedir(), 'Downloads'),\n }\n const resolvedPathProvider = options?.pathProvider ?? fallbackPathProvider\n\n const ai = options?.aiContext\n\n const cliStreamImport = async (dm: typeof dbManager, filePath: string) => {\n const { streamImport } = await import('../../../import/stream-import')\n const result = await streamImport(dm, filePath)\n if (!result.success) {\n const { IMPORT_IN_PROGRESS_ERROR_KEY, ImportInProgressError } = await import('@openchatlab/node-runtime')\n if (result.error === IMPORT_IN_PROGRESS_ERROR_KEY) throw new ImportInProgressError()\n throw new Error(result.error || 'Import failed')\n }\n if (!result.sessionId) throw new Error('Import succeeded but no sessionId returned')\n return { sessionId: result.sessionId }\n }\n\n const defaultUserDataDir = getDefaultNodeUserDataDir()\n const isCustom = path.resolve(resolvedPathProvider.getUserDataDir()) !== path.resolve(defaultUserDataDir)\n\n const semanticIndexService = options?.semanticIndexService\n\n const analyticsService = process.env.APTABASE_APP_KEY\n ? new AnalyticsService(resolvedPathProvider.getSystemDir(), process.env.APTABASE_APP_KEY, getVersion())\n : undefined\n\n registerSharedRoutes(\n server,\n {\n dbManager,\n sessionAdapter: adapter,\n pathProvider: resolvedPathProvider,\n runtimeIdentity: options?.runtimeIdentity,\n getVersion,\n nativeBinding: options?.nativeBinding,\n semanticIndexService,\n analyticsService,\n getCurrentAiLogPath: () => getServerAiLogger()?.getExistingLogPath() ?? null,\n openDirectory: openDirectoryPath,\n showInFolder: showPathInFolder,\n downloadsDir: resolvedPathProvider.getDownloadsDir(),\n defaultUserDataDir,\n isCustomDataDir: isCustom,\n canSetDataDir: !process.env.CHATLAB_DATA_DIR,\n getPendingDataDirMigration: (): PendingDataDirMigration | null =>\n getPendingNodeDataDirMigration(resolvedPathProvider.getSystemDir()),\n setDataDir: (dirPath, migrate) =>\n createNodeDataDirSwitch({\n systemDir: resolvedPathProvider.getSystemDir(),\n currentDir: resolvedPathProvider.getUserDataDir(),\n targetDir: dirPath,\n defaultDir: defaultUserDataDir,\n migrate,\n envDataDir: process.env.CHATLAB_DATA_DIR,\n }),\n ...(mergeCache && {\n mergeSessionCache: mergeCache,\n streamImport: cliStreamImport,\n }),\n ...(ai && {\n aiDataDir: ai.aiDataDir,\n aiChatManager: ai.aiChatManager,\n assistantManager: ai.assistantManager,\n skillManagerCore: ai.skillManagerCore,\n llmConfigStore: ai.llmConfigStore,\n customProviderStore: ai.customProviderStore,\n customModelStore: ai.customModelStore,\n runAgentStream: ai.runAgentStream,\n }),\n },\n ai ? { requireAi: true } : undefined\n )\n\n // CLI-specific routes not yet migrated to @openchatlab/http-routes\n registerImportRoutes(server, dbManager)\n\n server.get('/_web/system/check-update', async () => {\n const currentVersion = getVersion()\n try {\n const resp = await fetch('https://chatlab.fun/latest-version', {\n headers: { Accept: 'application/json' },\n signal: AbortSignal.timeout(10_000),\n })\n if (!resp.ok) {\n return { hasUpdate: false, currentVersion, error: `latest-version HTTP ${resp.status}` }\n }\n const data = (await resp.json()) as { version?: string }\n return buildWebUpdateCheckResult({ currentVersion, latestVersion: data.version })\n } catch (err) {\n return {\n hasUpdate: false,\n currentVersion,\n error: err instanceof Error ? err.message : String(err),\n }\n }\n })\n}\n","import * as fs from 'fs'\nimport * as os from 'os'\nimport * as path from 'path'\nimport { randomUUID } from 'node:crypto'\nimport { pipeline } from 'node:stream/promises'\nimport type { FastifyInstance } from 'fastify'\nimport { ArchiveImportError, ArchiveImportSourceManager, type DatabaseManager } from '@openchatlab/node-runtime'\nimport {\n autoImport,\n streamImport,\n incrementalImport,\n analyzeIncrementalImport,\n analyzeNewImport,\n detectFormat,\n detectAllFormats,\n getSupportedFormats,\n scanMultiChatFile,\n findEntryFileInDirectory,\n} from '../../../import'\nimport type { AutoImportResult, StreamImportOptions } from '../../../import'\nimport { resolveNativeBinding } from './helpers'\n\nconst DEMO_BASE_URL = 'https://chatlab.fun/assets/demo'\nconst ARCHIVE_UPLOAD_LIMIT = 50 * 1024 * 1024 * 1024\n\ninterface ImportRouteOptions {\n sourceManager?: ArchiveImportSourceManager\n runAutoImport?: (filePath: string, options: StreamImportOptions) => Promise<AutoImportResult>\n runPreparedImport?: (\n manifestPath: string,\n onProgress: (progress: unknown) => void\n ) => Promise<{ success: boolean; sessionId?: string; error?: string; messageCount?: number; memberCount?: number }>\n}\n\nfunction cleanupTemp(...paths: string[]) {\n for (const p of paths) {\n try {\n const stat = fs.statSync(p)\n if (stat.isDirectory()) {\n fs.rmSync(p, { recursive: true, force: true })\n } else {\n fs.unlinkSync(p)\n }\n } catch {\n /* ignore */\n }\n }\n}\n\nexport function registerImportRoutes(\n server: FastifyInstance,\n dbManager: DatabaseManager,\n options: ImportRouteOptions = {}\n): void {\n const sourceManager =\n options.sourceManager ??\n new ArchiveImportSourceManager({\n tempRoot: fs.mkdtempSync(path.join(os.tmpdir(), 'chatlab-import-sources-')),\n })\n const runAutoImport = options.runAutoImport ?? autoImport.bind(null, dbManager)\n const runPreparedImport =\n options.runPreparedImport ??\n (async (manifestPath: string, onProgress: (progress: unknown) => void) => {\n const result = await runAutoImport(manifestPath, {\n formatId: 'google-chat-takeout',\n nativeBinding: resolveNativeBinding(),\n onProgress: onProgress as any,\n })\n return result\n })\n\n server.addHook('onClose', async () => {\n await sourceManager.close()\n })\n\n server.post('/_web/import-sources', async (request, reply) => {\n const data = await (request as any).file({\n limits: { fileSize: ARCHIVE_UPLOAD_LIMIT },\n })\n if (!data) return reply.code(400).send({ success: false, error: 'error.no_file_selected' })\n\n const uploadPath = path.join(os.tmpdir(), `chatlab-archive-${randomUUID()}.zip`)\n try {\n await pipeline(data.file, fs.createWriteStream(uploadPath, { flags: 'wx' }))\n if (data.file.truncated) {\n cleanupTemp(uploadPath)\n return reply.code(413).send({ success: false, error: 'error.archive_limit_exceeded' })\n }\n\n const source = await sourceManager.prepareOwnedArchive(uploadPath)\n return { success: true, source }\n } catch (error) {\n cleanupTemp(uploadPath)\n if (error instanceof ArchiveImportError) {\n return reply.code(400).send({ success: false, error: error.code })\n }\n return reply.code(400).send({\n success: false,\n error: error instanceof Error ? error.message : String(error),\n })\n }\n })\n\n server.post<{ Params: { sourceId: string }; Body: { chatId?: string } }>(\n '/_web/import-sources/:sourceId/import',\n async (request, reply) => {\n const chatId = request.body?.chatId\n if (!chatId) return reply.code(400).send({ success: false, error: 'error.no_chat_selected' })\n\n reply.raw.writeHead(200, {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n Connection: 'keep-alive',\n })\n\n function sendEvent(event: string, eventData: unknown) {\n reply.raw.write(`event: ${event}\\ndata: ${JSON.stringify(eventData)}\\n\\n`)\n }\n\n try {\n const result = await sourceManager.withMaterializedChat(request.params.sourceId, chatId, (manifestPath) =>\n runPreparedImport(manifestPath, (progress) => sendEvent('progress', progress))\n )\n if (result.success) {\n sendEvent('done', result)\n } else {\n sendEvent('error', { success: false, error: result.error || 'error.import_failed' })\n }\n } catch (error) {\n sendEvent('error', {\n success: false,\n error:\n error instanceof ArchiveImportError ? error.code : error instanceof Error ? error.message : String(error),\n })\n } finally {\n reply.raw.end()\n }\n }\n )\n\n server.delete<{ Params: { sourceId: string } }>('/_web/import-sources/:sourceId', async (request) => {\n await sourceManager.release(request.params.sourceId)\n return { success: true }\n })\n\n server.get('/_web/supported-formats', async () => {\n return getSupportedFormats()\n })\n\n server.post('/_web/detect-format', async (request, reply) => {\n const data = await (request as any).file()\n if (!data) return reply.code(400).send({ error: 'No file uploaded' })\n\n const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'chatlab-detect-'))\n const tmpPath = path.join(tmpDir, data.filename || 'upload')\n\n try {\n const chunks: Buffer[] = []\n for await (const chunk of data.file) {\n chunks.push(chunk)\n }\n fs.writeFileSync(tmpPath, Buffer.concat(chunks))\n\n const format = detectFormat(tmpPath)\n const allFormats = detectAllFormats(tmpPath)\n return { format, allFormats }\n } finally {\n cleanupTemp(tmpPath, tmpDir)\n }\n })\n\n server.post('/_web/scan-multi-chat', async (request, reply) => {\n const data = await (request as any).file()\n if (!data) return reply.code(400).send({ error: 'No file uploaded' })\n\n const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'chatlab-scan-'))\n const tmpPath = path.join(tmpDir, data.filename || 'upload')\n\n try {\n const chunks: Buffer[] = []\n for await (const chunk of data.file) {\n chunks.push(chunk)\n }\n fs.writeFileSync(tmpPath, Buffer.concat(chunks))\n\n const chats = await scanMultiChatFile(tmpPath)\n return { chats }\n } finally {\n cleanupTemp(tmpPath, tmpDir)\n }\n })\n\n server.post('/_web/import', async (request, reply) => {\n const data = await (request as any).file()\n if (!data) return reply.code(400).send({ error: 'No file uploaded' })\n\n const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'chatlab-import-'))\n const tmpPath = path.join(tmpDir, data.filename || 'upload')\n\n const chunks: Buffer[] = []\n for await (const chunk of data.file) {\n chunks.push(chunk)\n }\n fs.writeFileSync(tmpPath, Buffer.concat(chunks))\n\n const formatId = (data.fields?.formatId as any)?.value as string | undefined\n const chatIndexStr = (data.fields?.chatIndex as any)?.value as string | undefined\n const chatIndex = chatIndexStr !== undefined ? parseInt(chatIndexStr, 10) : undefined\n\n reply.raw.writeHead(200, {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n Connection: 'keep-alive',\n })\n\n function sendEvent(event: string, eventData: unknown) {\n reply.raw.write(`event: ${event}\\ndata: ${JSON.stringify(eventData)}\\n\\n`)\n }\n\n try {\n const nativeBinding = resolveNativeBinding()\n const result = await runAutoImport(tmpPath, {\n formatId,\n chatIndex,\n nativeBinding,\n onProgress: (p) => sendEvent('progress', p),\n })\n\n if (result.success) {\n sendEvent('done', result)\n } else {\n sendEvent('error', { success: false, error: result.error })\n }\n } catch (err) {\n sendEvent('error', { success: false, error: err instanceof Error ? err.message : String(err) })\n } finally {\n reply.raw.end()\n cleanupTemp(tmpPath, tmpDir)\n }\n })\n\n // ==================== Directory Import ====================\n\n server.post('/_web/import-directory', async (request, reply) => {\n const parts = (request as any).parts()\n if (!parts) return reply.code(400).send({ error: 'No files uploaded' })\n\n const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'chatlab-dir-import-'))\n const relativePaths: string[] = []\n const fileBuffers: { data: Buffer; filename: string }[] = []\n\n try {\n for await (const part of parts) {\n if (part.type === 'field' && part.fieldname === 'relativePaths') {\n relativePaths.push(String(part.value))\n } else if (part.type === 'file') {\n const chunks: Buffer[] = []\n for await (const chunk of part.file) {\n chunks.push(chunk)\n }\n fileBuffers.push({ data: Buffer.concat(chunks), filename: part.filename || '' })\n }\n }\n\n for (let i = 0; i < fileBuffers.length; i++) {\n let relPath = relativePaths[i] || fileBuffers[i].filename || `file_${i}`\n const segments = relPath.split('/')\n if (segments.length > 1) {\n relPath = segments.slice(1).join('/')\n }\n const targetPath = path.resolve(tmpDir, relPath)\n if (!targetPath.startsWith(tmpDir + path.sep)) continue\n fs.mkdirSync(path.dirname(targetPath), { recursive: true })\n fs.writeFileSync(targetPath, fileBuffers[i].data)\n }\n\n const entryPath = findEntryFileInDirectory(tmpDir)\n if (!entryPath) {\n return reply.code(400).send({ error: 'No recognizable import format found in directory' })\n }\n\n reply.raw.writeHead(200, {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n Connection: 'keep-alive',\n })\n\n function sendEvent(event: string, eventData: unknown) {\n reply.raw.write(`event: ${event}\\ndata: ${JSON.stringify(eventData)}\\n\\n`)\n }\n\n const nativeBinding = resolveNativeBinding()\n const result = await runAutoImport(entryPath, {\n nativeBinding,\n onProgress: (p) => sendEvent('progress', p),\n })\n\n if (result.success) {\n sendEvent('done', result)\n } else {\n sendEvent('error', { success: false, error: result.error })\n }\n } catch (err) {\n if (!reply.raw.headersSent) {\n return reply.code(500).send({ error: err instanceof Error ? err.message : String(err) })\n }\n reply.raw.write(\n `event: error\\ndata: ${JSON.stringify({ success: false, error: err instanceof Error ? err.message : String(err) })}\\n\\n`\n )\n } finally {\n reply.raw.end()\n cleanupTemp(tmpDir)\n }\n })\n\n // ==================== Incremental Import ====================\n\n server.post<{ Params: { id: string } }>('/_web/sessions/:id/import/incremental', async (request, reply) => {\n const sessionId = request.params.id\n const data = await (request as any).file()\n if (!data) return reply.code(400).send({ error: 'No file uploaded' })\n\n const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'chatlab-inc-'))\n const tmpPath = path.join(tmpDir, data.filename || 'upload')\n\n const chunks: Buffer[] = []\n for await (const chunk of data.file) {\n chunks.push(chunk)\n }\n fs.writeFileSync(tmpPath, Buffer.concat(chunks))\n\n reply.raw.writeHead(200, {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n Connection: 'keep-alive',\n })\n\n function sendEvent(event: string, eventData: unknown) {\n reply.raw.write(`event: ${event}\\ndata: ${JSON.stringify(eventData)}\\n\\n`)\n }\n\n try {\n const result = await incrementalImport(dbManager, sessionId, tmpPath, {\n onProgress: (p) => sendEvent('progress', p),\n })\n\n if (result.success) {\n sendEvent('done', result)\n } else {\n sendEvent('error', { success: false, error: result.error })\n }\n } catch (err) {\n sendEvent('error', { success: false, error: err instanceof Error ? err.message : String(err) })\n } finally {\n reply.raw.end()\n cleanupTemp(tmpPath, tmpDir)\n }\n })\n\n server.post<{ Params: { id: string } }>('/_web/sessions/:id/import/incremental/analyze', async (request, reply) => {\n const sessionId = request.params.id\n const data = await (request as any).file()\n if (!data) return reply.code(400).send({ error: 'No file uploaded' })\n\n const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'chatlab-analyze-'))\n const tmpPath = path.join(tmpDir, data.filename || 'upload')\n\n const chunks: Buffer[] = []\n for await (const chunk of data.file) {\n chunks.push(chunk)\n }\n fs.writeFileSync(tmpPath, Buffer.concat(chunks))\n\n try {\n return await analyzeIncrementalImport(dbManager, sessionId, tmpPath)\n } catch (err) {\n return reply.code(500).send({ error: err instanceof Error ? err.message : String(err) })\n } finally {\n cleanupTemp(tmpPath, tmpDir)\n }\n })\n\n // ==================== Analyze New Import (dry-run) ====================\n\n server.post('/_web/import/analyze', async (request, reply) => {\n const data = await (request as any).file()\n if (!data) return reply.code(400).send({ error: 'No file uploaded' })\n\n const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'chatlab-analyze-'))\n const tmpPath = path.join(tmpDir, data.filename || 'upload')\n\n const chunks: Buffer[] = []\n for await (const chunk of data.file) {\n chunks.push(chunk)\n }\n fs.writeFileSync(tmpPath, Buffer.concat(chunks))\n\n try {\n return await analyzeNewImport(tmpPath)\n } catch (err) {\n return reply.code(500).send({ error: err instanceof Error ? err.message : String(err) })\n } finally {\n cleanupTemp(tmpPath, tmpDir)\n }\n })\n\n // ==================== Demo Import ====================\n\n const DEMO_FILES = [\n 'demo-group.json',\n 'demo-private-A-cuilan.json',\n 'demo-private-B-wukong.json',\n 'demo-private-C-spider.json',\n ]\n\n server.post<{ Body: { locale?: string } }>('/_web/demo/import', async (request, reply) => {\n const locale = (request.body as any)?.locale || 'en'\n const nativeBinding = resolveNativeBinding()\n const total = DEMO_FILES.length\n\n reply.raw.writeHead(200, {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n Connection: 'keep-alive',\n })\n\n function sendEvent(event: string, eventData: unknown) {\n reply.raw.write(`event: ${event}\\ndata: ${JSON.stringify(eventData)}\\n\\n`)\n }\n\n const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'chatlab-demo-'))\n\n try {\n const localPaths: string[] = []\n for (let i = 0; i < total; i++) {\n sendEvent('progress', { stage: 'downloading', current: i + 1, total })\n const localPath = path.join(tmpDir, DEMO_FILES[i])\n const resp = await fetch(`${DEMO_BASE_URL}/${locale}/${DEMO_FILES[i]}`, {\n signal: AbortSignal.timeout(60_000),\n })\n if (!resp.ok) throw new Error(`Download demo failed (${DEMO_FILES[i]}): ${resp.status}`)\n fs.writeFileSync(localPath, Buffer.from(await resp.arrayBuffer()))\n localPaths.push(localPath)\n }\n\n sendEvent('progress', { stage: 'importing', current: 1, total })\n const groupResult = await streamImport(dbManager, localPaths[0], { nativeBinding })\n if (!groupResult.success) throw new Error(groupResult.error || 'Failed to import group demo')\n\n const privateSessionIds: string[] = []\n for (let i = 1; i < localPaths.length; i++) {\n sendEvent('progress', { stage: 'importing', current: i + 1, total })\n const result = await streamImport(dbManager, localPaths[i], { nativeBinding })\n if (!result.success) throw new Error(result.error || `Failed to import private demo: ${DEMO_FILES[i]}`)\n if (result.sessionId) privateSessionIds.push(result.sessionId)\n }\n\n sendEvent('progress', { stage: 'done', current: total, total })\n sendEvent('result', {\n success: true,\n groupSessionId: groupResult.sessionId,\n privateSessionIds,\n })\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n sendEvent('progress', { stage: 'error', current: 0, total, message })\n sendEvent('result', { success: false, error: message })\n } finally {\n reply.raw.end()\n cleanupTemp(tmpDir)\n }\n })\n}\n","/**\n * Shared helpers for web route modules.\n */\n\nimport * as fs from 'fs'\nimport type { TimeFilter } from '@openchatlab/shared-types'\nimport type { DatabaseManager } from '@openchatlab/node-runtime'\nimport { resolveCliPath } from '../../../paths'\n\nexport function parseTimeFilter(query: Record<string, string | undefined>): TimeFilter | undefined {\n const { startTs, endTs, memberId } = query\n if (!startTs && !endTs && !memberId) return undefined\n const filter: TimeFilter = {}\n if (startTs) filter.startTs = parseInt(startTs, 10)\n if (endTs) filter.endTs = parseInt(endTs, 10)\n if (memberId) filter.memberId = parseInt(memberId, 10)\n return filter\n}\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 getAiDataDir(dbManager: DatabaseManager): string {\n const pathProvider = (dbManager as any)['pathProvider']\n if (!pathProvider) {\n throw Object.assign(new Error('PathProvider not available'), { statusCode: 500 })\n }\n return pathProvider.getAiDataDir()\n}\n\nexport function ensureDb(dbManager: DatabaseManager, sessionId: string) {\n const db = dbManager.open(sessionId)\n if (!db) {\n throw Object.assign(new Error(`Session not found: ${sessionId}`), { statusCode: 404 })\n }\n return db\n}\n","/**\n * CLI-specific shell helpers for cache routes.\n *\n * Route registration has been migrated to packages/http-routes (shared).\n * Only the platform-specific openDirectoryPath / showPathInFolder functions\n * remain here, injected into HttpRouteContext by the CLI web-route initializer.\n */\nimport * as path from 'path'\nimport { execFile } from 'child_process'\n\ntype ExecFileRunner = (file: string, args: string[], callback: (error: Error | null) => void) => unknown\n\nfunction runExecFile(file: string, args: string[], runner: ExecFileRunner = execFile): Promise<void> {\n return new Promise((resolve, reject) => {\n runner(file, args, (error) => {\n if (error) {\n reject(error)\n return\n }\n resolve()\n })\n })\n}\n\nexport async function openDirectoryPath(\n dirPath: string,\n platform: NodeJS.Platform = process.platform,\n runner?: ExecFileRunner\n): Promise<void> {\n if (platform === 'darwin') {\n await runExecFile('open', [dirPath], runner)\n } else if (platform === 'win32') {\n await runExecFile('explorer.exe', [dirPath], runner)\n } else {\n await runExecFile('xdg-open', [dirPath], runner)\n }\n}\n\nexport async function showPathInFolder(\n filePath: string,\n platform: NodeJS.Platform = process.platform,\n runner?: ExecFileRunner\n): Promise<void> {\n if (platform === 'darwin') {\n await runExecFile('open', ['-R', filePath], runner)\n } else if (platform === 'win32') {\n await runExecFile('explorer.exe', [`/select,${filePath}`], runner)\n } else {\n await runExecFile('xdg-open', [path.dirname(filePath)], runner)\n }\n}\n","import { isNewerStableVersion } from '@openchatlab/core'\n\nexport interface WebUpdateCheckResult {\n hasUpdate: boolean\n currentVersion: string\n latestVersion?: string\n}\n\nfunction isDevelopmentPlaceholderVersion(version: string): boolean {\n return /^0\\.0\\.0(?:$|-)/.test(version.trim())\n}\n\nexport function buildWebUpdateCheckResult(options: {\n currentVersion: string\n latestVersion?: string | null\n}): WebUpdateCheckResult {\n const latestVersion = options.latestVersion || options.currentVersion\n return {\n hasUpdate:\n !isDevelopmentPlaceholderVersion(options.currentVersion) &&\n isNewerStableVersion(latestVersion, options.currentVersion),\n currentVersion: options.currentVersion,\n latestVersion,\n }\n}\n","/**\n * 反向代理路由 — /_proxy/chatlab.fun/*\n *\n * 在 Web 生产模式(chatlab start)下,将 /_proxy/chatlab.fun/* 代理到\n * https://chatlab.fun,行为与 vite.web.config.mts 中的 dev proxy 保持一致,\n * 以解决浏览器 CORS 限制。\n *\n * 目标 host 硬编码为 https://chatlab.fun,不允许外部指定,避免开放代理风险。\n */\n\nimport type { FastifyInstance } from 'fastify'\n\nconst PROXY_TARGET = 'https://chatlab.fun'\n\nexport function registerProxyRoutes(server: FastifyInstance): void {\n server.get<{ Params: { '*': string } }>('/_proxy/chatlab.fun/*', async (request, reply) => {\n const subPath = request.params['*'] ?? ''\n const queryString = new URLSearchParams(\n request.query as Record<string, string>,\n ).toString()\n const targetUrl = queryString\n ? `${PROXY_TARGET}/${subPath}?${queryString}`\n : `${PROXY_TARGET}/${subPath}`\n\n try {\n const upstream = await fetch(targetUrl, {\n headers: { Accept: request.headers.accept ?? '*/*' },\n signal: AbortSignal.timeout(10_000),\n })\n\n const contentType = upstream.headers.get('content-type') ?? 'application/octet-stream'\n const body = Buffer.from(await upstream.arrayBuffer())\n\n reply.code(upstream.status).header('content-type', contentType).send(body)\n } catch (err) {\n reply.code(502).send({\n error: 'Proxy error',\n message: err instanceof Error ? err.message : String(err),\n })\n }\n })\n}\n","/**\n * Factory functions for creating AssistantManager and SkillManagerCore instances.\n *\n * Server-side equivalent of the Electron adapter in electron/main/ai/assistant/manager.ts.\n * Uses Node.js fs directly (no Electron dependency).\n */\n\nimport * as fs from 'fs'\nimport * as path from 'path'\nimport { createHash, randomUUID } from 'crypto'\nimport {\n AssistantManager,\n SkillManagerCore,\n type AssistantManagerFs,\n type SkillManagerFs,\n} from '@openchatlab/node-runtime'\n\nconst nodeFs: AssistantManagerFs & SkillManagerFs = {\n ensureDir(dir: string) {\n if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true })\n },\n listFiles(dir: string, ext: string) {\n if (!fs.existsSync(dir)) return []\n return fs.readdirSync(dir).filter((f) => f.endsWith(ext))\n },\n readFile(filePath: string) {\n return fs.readFileSync(filePath, 'utf-8')\n },\n writeFile(filePath: string, content: string) {\n fs.writeFileSync(filePath, content, 'utf-8')\n },\n deleteFile(filePath: string) {\n if (fs.existsSync(filePath)) fs.unlinkSync(filePath)\n },\n fileExists(filePath: string) {\n return fs.existsSync(filePath)\n },\n joinPath(...parts: string[]) {\n return path.join(...parts)\n },\n}\n\nconst managerCache = new Map<string, { assistant: AssistantManager; skill: SkillManagerCore }>()\n\nexport function getAssistantManager(aiDataDir: string): AssistantManager {\n let cached = managerCache.get(aiDataDir)\n if (!cached) {\n cached = {\n assistant: new AssistantManager({\n fs: nodeFs,\n assistantsDir: path.join(aiDataDir, 'assistants'),\n builtinRawConfigs: [],\n generateId: () => `custom_${randomUUID().replace(/-/g, '').slice(0, 12)}`,\n }),\n skill: new SkillManagerCore({\n fs: nodeFs,\n skillsDir: path.join(aiDataDir, 'skills'),\n builtinRawSkills: [],\n contentHash: (content: string) => createHash('md5').update(content).digest('hex'),\n }),\n }\n managerCache.set(aiDataDir, cached)\n }\n return cached.assistant\n}\n\nexport function getSkillManagerCore(aiDataDir: string): SkillManagerCore {\n let cached = managerCache.get(aiDataDir)\n if (!cached) {\n getAssistantManager(aiDataDir)\n cached = managerCache.get(aiDataDir)!\n }\n return cached.skill\n}\n","/**\n * @openchatlab/sync — Shared type definitions\n *\n * Platform-agnostic types used by config manager, data source manager,\n * pull engine, and scheduler.\n */\n\n// ==================== API Server Config ====================\n\nexport interface ApiServerConfig {\n enabled: boolean\n port: number\n token: string\n createdAt: number\n}\n\n// ==================== Data Source / Import Session ====================\n\nexport interface ImportSession {\n id: string\n name: string\n remoteSessionId: string\n targetSessionId: string\n lastPullAt: number\n lastStatus: 'idle' | 'success' | 'error'\n lastError: string\n lastNewMessages: number\n}\n\nexport interface DataSource {\n id: string\n name: string\n baseUrl: string\n token: string\n intervalMinutes: number\n pullLimit: number\n enabled: boolean\n createdAt: number\n sessions: ImportSession[]\n}\n\nexport type DataSourceUpdatable = Partial<\n Pick<DataSource, 'name' | 'baseUrl' | 'token' | 'intervalMinutes' | 'pullLimit' | 'enabled'>\n>\n\n// ==================== Remote Discovery ====================\n\nexport interface RemoteSession {\n id: string\n name: string\n platform: string\n type: string\n messageCount?: number\n memberCount?: number\n lastMessageAt?: number\n}\n\nexport interface RemoteSessionDiscoveryPage {\n hasMore: boolean\n nextCursor?: string\n}\n\nexport interface RemoteSessionDiscoveryResult {\n sessions: RemoteSession[]\n page?: RemoteSessionDiscoveryPage\n}\n\nexport interface RemoteSessionDiscoveryQuery {\n keyword?: string\n limit?: number\n cursor?: string\n}\n\n// ==================== Pull Engine Abstractions ====================\n\nexport interface FetchParams {\n since?: number\n offset?: number\n end?: number\n limit?: number\n}\n\nexport interface SyncMeta {\n hasMore: boolean\n nextSince?: number\n nextOffset?: number\n watermark?: number\n}\n\nexport interface ImportResult {\n success: boolean\n newMessageCount: number\n sessionId?: string\n error?: string\n needFullResync?: boolean\n /** The source page was not imported and should be retried without advancing the pull cursor. */\n retryable?: boolean\n}\n\nexport interface PullSessionResult {\n success: boolean\n newMessageCount: number\n error?: string\n sourceUnavailable?: boolean\n}\n\nexport interface PullProgress {\n sessionId: string\n sessionName: string\n current: number\n pages: number\n done: boolean\n}\n\n/**\n * Downloads remote data to a temporary file.\n * Platform implementations: Electron uses `net.request`, Node.js uses `fetch`.\n */\nexport interface HttpFetcher {\n fetchToTempFile(baseUrl: string, remoteSessionId: string, token: string, params: FetchParams): Promise<string>\n}\n\n/**\n * Imports a downloaded temp file into a local session database.\n * Platform implementations: Electron uses worker IPC, Server uses DatabaseManager.\n */\nexport interface DataImporter {\n /**\n * Import temp file into an existing or new local session.\n * If `targetSessionId` is provided, attempt incremental import.\n * Otherwise, create a new session (using `externalId` for deterministic naming).\n */\n importFile(tempFile: string, targetSessionId: string | undefined, externalId: string): Promise<ImportResult>\n\n /** Check if a local session database exists */\n sessionExists(sessionId: string): boolean\n}\n\n/**\n * Notifies the UI about sync events.\n * Electron uses BrowserWindow.webContents.send, Server can use SSE or noop.\n */\nexport interface SyncNotifier {\n onSessionListChanged(): void\n onPullResult(sourceId: string, sessionId: string | undefined, status: 'success' | 'error', detail: string): void\n}\n\nexport interface SyncLogger {\n info(message: string): void\n warn(message: string): void\n error(message: string, err?: unknown): void\n}\n\nconst noop = () => {}\n\nexport const NOOP_LOGGER: SyncLogger = { info: noop, warn: noop, error: noop }\n","/**\n * @openchatlab/sync — Data source configuration manager\n *\n * Extracted from electron/main/api/dataSource.ts.\n * Parameterized by `configDir` so it works in both Electron and CLI.\n *\n * DataSource (remote server) → ImportSession[] (subscribed conversations)\n */\n\nimport * as fs from 'fs'\nimport * as path from 'path'\nimport * as crypto from 'crypto'\nimport { NOOP_LOGGER } from './types'\nimport type { DataSource, DataSourceUpdatable, ImportSession, SyncLogger } from './types'\n\nconst CONFIG_FILE = 'data-sources.json'\n\nfunction generateId(prefix: string = 'ds'): string {\n return `${prefix}_${crypto.randomBytes(6).toString('hex')}`\n}\n\nexport function normalizeBaseUrl(input: string): string {\n let url = input.trim().replace(/\\/+$/, '')\n if (url && !/^https?:\\/\\//i.test(url)) {\n url = `http://${url}`\n }\n if (url && !url.endsWith('/api/v1')) {\n url = url.replace(/\\/api\\/v1$/, '') + '/api/v1'\n }\n return url\n}\n\nfunction isValidDataSourceArray(data: unknown): data is DataSource[] {\n if (!Array.isArray(data)) return false\n return data.every((item) => item && typeof item === 'object' && Array.isArray((item as any).sessions))\n}\n\nexport class DataSourceManager {\n private configDir: string\n private logger: SyncLogger\n\n constructor(configDir: string, logger?: SyncLogger) {\n this.configDir = configDir\n this.logger = logger ?? NOOP_LOGGER\n }\n\n private getConfigPath(): string {\n return path.join(this.configDir, CONFIG_FILE)\n }\n\n private ensureDir(): void {\n fs.mkdirSync(this.configDir, { recursive: true })\n }\n\n // ==================== Load / Save ====================\n\n loadAll(): DataSource[] {\n try {\n const filePath = this.getConfigPath()\n if (fs.existsSync(filePath)) {\n const raw = fs.readFileSync(filePath, 'utf-8')\n const parsed = JSON.parse(raw)\n\n if (!isValidDataSourceArray(parsed)) {\n this.logger.warn('[DataSource] Incompatible config format detected, returning empty.')\n return []\n }\n\n for (const ds of parsed) {\n if (!ds.pullLimit) ds.pullLimit = 1000\n }\n return parsed\n }\n } catch (err) {\n this.logger.error('[DataSource] Failed to load config', err)\n }\n return []\n }\n\n private saveAll(sources: DataSource[]): void {\n try {\n this.ensureDir()\n fs.writeFileSync(this.getConfigPath(), JSON.stringify(sources, null, 2), 'utf-8')\n } catch (err) {\n this.logger.error('[DataSource] Failed to save config', err)\n }\n }\n\n // ==================== DataSource CRUD ====================\n\n get(id: string): DataSource | null {\n return this.loadAll().find((s) => s.id === id) || null\n }\n\n add(partial: {\n name?: string\n baseUrl: string\n token: string\n intervalMinutes: number\n pullLimit?: number\n }): DataSource {\n const sources = this.loadAll()\n const ds: DataSource = {\n id: generateId('src'),\n name: partial.name || '',\n baseUrl: normalizeBaseUrl(partial.baseUrl),\n token: partial.token,\n intervalMinutes: partial.intervalMinutes,\n pullLimit: partial.pullLimit || 1000,\n enabled: true,\n createdAt: Math.floor(Date.now() / 1000),\n sessions: [],\n }\n sources.push(ds)\n this.saveAll(sources)\n return ds\n }\n\n update(id: string, updates: DataSourceUpdatable): DataSource | null {\n const sources = this.loadAll()\n const idx = sources.findIndex((s) => s.id === id)\n if (idx === -1) return null\n const ds = sources[idx]\n if (updates.name !== undefined) ds.name = updates.name\n if (updates.baseUrl !== undefined) ds.baseUrl = normalizeBaseUrl(updates.baseUrl)\n if (updates.token !== undefined) ds.token = updates.token\n if (updates.intervalMinutes !== undefined) ds.intervalMinutes = updates.intervalMinutes\n if (updates.pullLimit !== undefined) ds.pullLimit = updates.pullLimit\n if (updates.enabled !== undefined) ds.enabled = updates.enabled\n this.saveAll(sources)\n return ds\n }\n\n delete(id: string): boolean {\n const sources = this.loadAll()\n const filtered = sources.filter((s) => s.id !== id)\n if (filtered.length === sources.length) return false\n this.saveAll(filtered)\n return true\n }\n\n // ==================== ImportSession CRUD ====================\n\n addSessions(sourceId: string, sessions: Array<{ name: string; remoteSessionId: string }>): ImportSession[] {\n const sources = this.loadAll()\n const ds = sources.find((s) => s.id === sourceId)\n if (!ds) return []\n\n const added: ImportSession[] = []\n for (const sess of sessions) {\n if (ds.sessions.some((s) => s.remoteSessionId === sess.remoteSessionId)) continue\n const imp: ImportSession = {\n id: generateId('sess'),\n name: sess.name,\n remoteSessionId: sess.remoteSessionId,\n targetSessionId: '',\n lastPullAt: 0,\n lastStatus: 'idle',\n lastError: '',\n lastNewMessages: 0,\n }\n ds.sessions.push(imp)\n added.push(imp)\n }\n this.saveAll(sources)\n return added\n }\n\n removeSession(sourceId: string, sessionId: string): ImportSession | null {\n const sources = this.loadAll()\n const ds = sources.find((s) => s.id === sourceId)\n if (!ds) return null\n const removed = ds.sessions.find((s) => s.id === sessionId)\n if (!removed) return null\n ds.sessions = ds.sessions.filter((s) => s.id !== sessionId)\n this.saveAll(sources)\n return removed\n }\n\n updateSession(sourceId: string, sessionId: string, updates: Partial<ImportSession>): ImportSession | null {\n const sources = this.loadAll()\n const ds = sources.find((s) => s.id === sourceId)\n if (!ds) return null\n const sess = ds.sessions.find((s) => s.id === sessionId)\n if (!sess) return null\n Object.assign(sess, updates, { id: sessionId })\n this.saveAll(sources)\n return sess\n }\n}\n","/**\n * @openchatlab/sync — Pull engine\n *\n * Core paginated pull loop extracted from electron/main/api/pullScheduler.ts.\n * All platform-specific dependencies are injected via HttpFetcher, DataImporter, and SyncNotifier.\n */\n\nimport * as fs from 'fs'\nimport * as crypto from 'crypto'\nimport { NOOP_LOGGER } from './types'\nimport type {\n DataSource,\n ImportSession,\n HttpFetcher,\n DataImporter,\n SyncNotifier,\n SyncLogger,\n FetchParams,\n SyncMeta,\n ImportResult,\n PullSessionResult,\n PullProgress,\n} from './types'\nimport type { DataSourceManager } from './data-source-manager'\n\nconst MAX_PAGES_PER_PULL = 5000\nconst PULL_OVERLAP_SECONDS = 60\nconst SOURCE_UNAVAILABLE_LOG_COOLDOWN_MS = 5 * 60 * 1000\nconst SOURCE_UNAVAILABLE_ERROR_CODES = new Set(['ECONNREFUSED', 'ENOTFOUND', 'ECONNRESET', 'ETIMEDOUT', 'ABORT_ERR'])\n\n// ==================== Helpers ====================\n\nexport function buildPullUrl(baseUrl: string, remoteSessionId: string, params: FetchParams): string {\n const base = `${baseUrl}/sessions/${remoteSessionId}/messages`\n const qs: string[] = ['format=chatlab']\n if (params.since !== undefined && params.since > 0) qs.push(`since=${params.since}`)\n if (params.offset !== undefined && params.offset > 0) qs.push(`offset=${params.offset}`)\n if (params.limit !== undefined && params.limit > 0) qs.push(`limit=${params.limit}`)\n return base + '?' + qs.join('&')\n}\n\nexport function deriveLocalSessionId(baseUrl: string, remoteSessionId: string): string {\n const hash = crypto.createHash('sha256').update(`${baseUrl}\\0${remoteSessionId}`).digest('hex').slice(0, 12)\n return `remote_${hash}`\n}\n\nexport function parseSyncFromFile(filePath: string): SyncMeta | null {\n try {\n const isJsonl = filePath.endsWith('.jsonl')\n if (isJsonl) {\n const content = fs.readFileSync(filePath, 'utf-8')\n const lines = content.trimEnd().split('\\n')\n for (let i = lines.length - 1; i >= Math.max(0, lines.length - 5); i--) {\n try {\n const obj = JSON.parse(lines[i])\n if (obj._type === 'sync') {\n return {\n hasMore: !!obj.hasMore,\n nextSince: obj.nextSince,\n nextOffset: obj.nextOffset,\n watermark: obj.watermark,\n }\n }\n } catch {\n continue\n }\n }\n return null\n }\n\n const raw = fs.readFileSync(filePath, 'utf-8')\n const parsed = JSON.parse(raw)\n if (parsed.sync && typeof parsed.sync === 'object') {\n const s = parsed.sync\n return { hasMore: !!s.hasMore, nextSince: s.nextSince, nextOffset: s.nextOffset, watermark: s.watermark }\n }\n return null\n } catch {\n return null\n }\n}\n\nfunction fileContainsMessages(filePath: string): boolean {\n try {\n if (filePath.endsWith('.jsonl')) {\n const content = fs.readFileSync(filePath, 'utf-8')\n for (const line of content.split('\\n')) {\n const trimmed = line.trim()\n if (!trimmed) continue\n try {\n const obj = JSON.parse(trimmed)\n if (obj._type === 'message') return true\n } catch {\n continue\n }\n }\n return false\n }\n\n const raw = fs.readFileSync(filePath, 'utf-8')\n const parsed = JSON.parse(raw)\n return Array.isArray(parsed.messages) && parsed.messages.length > 0\n } catch {\n return false\n }\n}\n\nfunction getMaxMessageTimestampFromFile(filePath: string): number | null {\n try {\n let maxTs: number | null = null\n const visitTimestamp = (value: unknown) => {\n const ts = typeof value === 'string' && value.trim() !== '' ? Number(value) : value\n if (typeof ts === 'number' && Number.isFinite(ts)) {\n maxTs = maxTs === null ? ts : Math.max(maxTs, ts)\n }\n }\n\n if (filePath.endsWith('.jsonl')) {\n const content = fs.readFileSync(filePath, 'utf-8')\n for (const line of content.split('\\n')) {\n const trimmed = line.trim()\n if (!trimmed) continue\n try {\n const obj = JSON.parse(trimmed)\n if (obj._type === 'message') visitTimestamp(obj.timestamp)\n } catch {\n continue\n }\n }\n return maxTs\n }\n\n const raw = fs.readFileSync(filePath, 'utf-8')\n const parsed = JSON.parse(raw)\n if (Array.isArray(parsed.messages)) {\n for (const message of parsed.messages) {\n visitTimestamp(message?.timestamp)\n }\n }\n return maxTs\n } catch {\n return null\n }\n}\n\nfunction cleanupTempFile(filePath: string): void {\n try {\n if (fs.existsSync(filePath)) fs.unlinkSync(filePath)\n } catch {\n /* ignore */\n }\n}\n\nfunction getErrorProp(error: unknown, prop: string): unknown {\n return error && typeof error === 'object' ? (error as Record<string, unknown>)[prop] : undefined\n}\n\nfunction getErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error || 'Pull failed')\n}\n\n// 只把连接级网络错误归为数据源不可达;导入/解析/HTTP 状态码仍按单会话失败处理。\nfunction describeSourceUnavailableError(error: unknown): string | null {\n let current: unknown = error\n while (current && typeof current === 'object') {\n const code = getErrorProp(current, 'code')\n const name = getErrorProp(current, 'name')\n if (typeof code === 'string' && SOURCE_UNAVAILABLE_ERROR_CODES.has(code)) {\n const address = getErrorProp(current, 'address')\n const port = getErrorProp(current, 'port')\n if (typeof address === 'string' && (typeof port === 'number' || typeof port === 'string')) {\n return `${code} ${address}:${port}`\n }\n const host = getErrorProp(current, 'hostname') ?? getErrorProp(current, 'host')\n return typeof host === 'string' ? `${code} ${host}` : code\n }\n if (name === 'AbortError') return 'Request timeout'\n current = getErrorProp(current, 'cause')\n }\n return null\n}\n\n// ==================== Pull Engine ====================\n\nexport interface PullEngineOptions {\n fetcher: HttpFetcher\n importer: DataImporter\n notifier: SyncNotifier\n dsManager: DataSourceManager\n logger?: SyncLogger\n /** Return true when an import is already in progress for the local session (skip pull) */\n isImporting?: (sessionId: string | undefined) => boolean\n /**\n * Called after a pull session completes successfully with the local session ID.\n * Used for post-import side effects (e.g. applying the platform owner profile).\n * Errors thrown by the hook never fail the pull.\n */\n onSessionImported?: (localSessionId: string) => void\n}\n\nexport class PullEngine {\n private fetcher: HttpFetcher\n private importer: DataImporter\n private notifier: SyncNotifier\n private dsManager: DataSourceManager\n private logger: SyncLogger\n private isImporting: (sessionId: string | undefined) => boolean\n private onSessionImported?: (localSessionId: string) => void\n private pullingSourceIds = new Set<string>()\n private progressMap = new Map<string, PullProgress>()\n private sourceUnavailableLogs = new Map<string, { detail: string; lastLoggedAt: number }>()\n\n constructor(options: PullEngineOptions) {\n this.fetcher = options.fetcher\n this.importer = options.importer\n this.notifier = options.notifier\n this.dsManager = options.dsManager\n this.logger = options.logger ?? NOOP_LOGGER\n this.isImporting = options.isImporting ?? (() => false)\n this.onSessionImported = options.onSessionImported\n }\n\n getProgress(): PullProgress[] {\n return Array.from(this.progressMap.values())\n }\n\n private async importTempFile(baseUrl: string, sess: ImportSession, tempFile: string): Promise<ImportResult> {\n let targetId = sess.targetSessionId\n if (!targetId) {\n const derived = deriveLocalSessionId(baseUrl, sess.remoteSessionId)\n if (this.importer.sessionExists(derived)) {\n targetId = derived\n this.logger.info(`[Pull] Reusing existing local session ${derived} for \"${sess.name}\"`)\n }\n }\n\n const externalId = deriveLocalSessionId(baseUrl, sess.remoteSessionId)\n return this.importer.importFile(tempFile, targetId || undefined, externalId)\n }\n\n private deferRetryableImport(sess: ImportSession, error?: string): PullSessionResult {\n const errMsg = error || 'Import deferred'\n this.logger.info(`[Pull] Deferring \"${sess.name}\": ${errMsg}`)\n this.markProgressDone(sess.id)\n return { success: false, newMessageCount: 0, error: errMsg }\n }\n\n async executePullSession(sourceId: string, ds: DataSource, sess: ImportSession): Promise<PullSessionResult> {\n const currentDs = this.dsManager.get(sourceId)\n if (!currentDs || !currentDs.sessions.some((s) => s.id === sess.id)) {\n this.logger.info(`[Pull] Skipping \"${sess.name}\": session no longer exists`)\n return { success: true, newMessageCount: 0 }\n }\n\n const importStatusSessionId = sess.targetSessionId || deriveLocalSessionId(ds.baseUrl, sess.remoteSessionId)\n if (this.isImporting(importStatusSessionId)) {\n this.logger.info(`[Pull] Skipping \"${sess.name}\": import in progress`)\n return { success: false, newMessageCount: 0, error: 'Import in progress' }\n }\n\n let totalNewMessages = 0\n let since = sess.lastPullAt\n let offset: number | undefined\n let nextPullSince = sess.lastPullAt\n let pageCount = 0\n let resyncAttempted = false\n\n this.progressMap.set(sess.id, { sessionId: sess.id, sessionName: sess.name, current: 0, pages: 0, done: false })\n\n try {\n while (pageCount < MAX_PAGES_PER_PULL) {\n pageCount++\n const tempFile = await this.fetcher.fetchToTempFile(ds.baseUrl, sess.remoteSessionId, ds.token, {\n since,\n offset,\n limit: ds.pullLimit,\n })\n\n try {\n const stat = fs.statSync(tempFile)\n this.logger.info(`[Pull] \"${sess.name}\" page ${pageCount}: fetched ${stat.size} bytes`)\n\n const sync0 = parseSyncFromFile(tempFile)\n if (stat.size < 1024) {\n if (!fileContainsMessages(tempFile) && sync0?.hasMore === false) {\n cleanupTempFile(tempFile)\n if (sync0?.nextSince !== undefined) nextPullSince = Math.max(nextPullSince, sync0.nextSince)\n const retryDelays = [2000, 3000, 5000]\n let retrySuccess = false\n let retryHasMore = false\n for (let ri = 0; ri < retryDelays.length; ri++) {\n this.logger.info(\n `[Pull] \"${sess.name}\" page ${pageCount} got empty response, retry ${ri + 1}/${retryDelays.length} after ${retryDelays[ri]}ms`\n )\n await new Promise((r) => setTimeout(r, retryDelays[ri]))\n const retryFile = await this.fetcher.fetchToTempFile(ds.baseUrl, sess.remoteSessionId, ds.token, {\n since,\n offset,\n limit: ds.pullLimit,\n })\n const retryStat = fs.statSync(retryFile)\n this.logger.info(`[Pull] \"${sess.name}\" retry ${ri + 1}: fetched ${retryStat.size} bytes`)\n const retrySync = parseSyncFromFile(retryFile)\n if (retryStat.size < 1024 && !fileContainsMessages(retryFile) && retrySync?.hasMore === false) {\n // 空 retry 页也可能只返回服务端 watermark,跳过导入前仍要推进保存游标。\n if (retrySync.nextSince !== undefined) nextPullSince = Math.max(nextPullSince, retrySync.nextSince)\n cleanupTempFile(retryFile)\n continue\n }\n const retryMaxTs = getMaxMessageTimestampFromFile(retryFile)\n const retryResult = await this.importTempFile(ds.baseUrl, sess, retryFile)\n cleanupTempFile(retryFile)\n\n if (retryResult.needFullResync && !resyncAttempted) {\n resyncAttempted = true\n this.logger.info(`[Pull] Resetting since=0 for \"${sess.name}\" full resync`)\n since = 0\n offset = undefined\n nextPullSince = 0\n pageCount = 0\n sess.targetSessionId = ''\n sess.lastPullAt = 0\n this.dsManager.updateSession(sourceId, sess.id, { targetSessionId: '', lastPullAt: 0 })\n retrySuccess = true\n retryHasMore = true\n break\n }\n\n if (retryResult.needFullResync) {\n const errMsg = 'Full resync failed'\n this.logger.error(`[Pull] Full resync already attempted for \"${sess.name}\", aborting`)\n this.dsManager.updateSession(sourceId, sess.id, {\n lastPullAt: Math.floor(Date.now() / 1000),\n lastStatus: 'error',\n lastError: errMsg,\n })\n this.notifier.onPullResult(sourceId, sess.id, 'error', errMsg)\n this.markProgressDone(sess.id)\n return { success: false, newMessageCount: 0, error: errMsg }\n }\n\n if (!retryResult.success) {\n if (retryResult.retryable) return this.deferRetryableImport(sess, retryResult.error)\n\n const errMsg = retryResult.error || 'Import failed'\n this.dsManager.updateSession(sourceId, sess.id, {\n lastPullAt: Math.floor(Date.now() / 1000),\n lastStatus: 'error',\n lastError: errMsg,\n })\n this.notifier.onPullResult(sourceId, sess.id, 'error', errMsg)\n this.markProgressDone(sess.id)\n return { success: false, newMessageCount: 0, error: errMsg }\n }\n\n if (retryResult.success && retryResult.sessionId && !sess.targetSessionId) {\n sess.targetSessionId = retryResult.sessionId\n this.dsManager.updateSession(sourceId, sess.id, { targetSessionId: retryResult.sessionId })\n }\n totalNewMessages += retryResult.newMessageCount\n if (retryMaxTs !== null) nextPullSince = Math.max(nextPullSince, retryMaxTs)\n // retry 终止页也可能携带服务端 watermark,必须保存,否则下次会重复拉取尾部窗口。\n if (retrySync?.nextSince !== undefined) nextPullSince = Math.max(nextPullSince, retrySync.nextSince)\n this.progressMap.set(sess.id, {\n sessionId: sess.id,\n sessionName: sess.name,\n current: totalNewMessages,\n pages: pageCount,\n done: false,\n })\n if (retrySync?.hasMore && retrySync.nextSince !== undefined) {\n since = retrySync.nextSince\n offset = undefined\n } else if (retrySync?.hasMore && retrySync.nextOffset !== undefined) {\n offset = retrySync.nextOffset\n }\n retryHasMore = !!retrySync?.hasMore\n retrySuccess = true\n break\n }\n if (!retrySuccess) break\n if (!retryHasMore) break\n continue\n }\n }\n\n if (stat.size === 0) {\n cleanupTempFile(tempFile)\n break\n }\n\n const sync = sync0\n const maxMessageTs = getMaxMessageTimestampFromFile(tempFile)\n const result = await this.importTempFile(ds.baseUrl, sess, tempFile)\n cleanupTempFile(tempFile)\n\n if (result.needFullResync && !resyncAttempted) {\n resyncAttempted = true\n this.logger.info(`[Pull] Resetting since=0 for \"${sess.name}\" full resync`)\n since = 0\n offset = undefined\n nextPullSince = 0\n pageCount = 0\n sess.targetSessionId = ''\n sess.lastPullAt = 0\n this.dsManager.updateSession(sourceId, sess.id, { targetSessionId: '', lastPullAt: 0 })\n continue\n }\n\n if (result.needFullResync) {\n const errMsg = 'Full resync failed'\n this.logger.error(`[Pull] Full resync already attempted for \"${sess.name}\", aborting`)\n this.dsManager.updateSession(sourceId, sess.id, {\n lastPullAt: Math.floor(Date.now() / 1000),\n lastStatus: 'error',\n lastError: errMsg,\n })\n this.notifier.onPullResult(sourceId, sess.id, 'error', errMsg)\n this.markProgressDone(sess.id)\n return { success: false, newMessageCount: 0, error: errMsg }\n }\n\n if (!result.success) {\n if (result.retryable) return this.deferRetryableImport(sess, result.error)\n\n const errMsg = result.error || 'Import failed'\n this.dsManager.updateSession(sourceId, sess.id, {\n lastPullAt: Math.floor(Date.now() / 1000),\n lastStatus: 'error',\n lastError: errMsg,\n })\n this.notifier.onPullResult(sourceId, sess.id, 'error', errMsg)\n this.markProgressDone(sess.id)\n return { success: false, newMessageCount: 0, error: errMsg }\n }\n\n if (!sess.targetSessionId && result.sessionId) {\n sess.targetSessionId = result.sessionId\n this.dsManager.updateSession(sourceId, sess.id, { targetSessionId: result.sessionId })\n }\n\n totalNewMessages += result.newMessageCount\n if (maxMessageTs !== null) nextPullSince = Math.max(nextPullSince, maxMessageTs)\n this.progressMap.set(sess.id, {\n sessionId: sess.id,\n sessionName: sess.name,\n current: totalNewMessages,\n pages: pageCount,\n done: false,\n })\n\n if (sync?.nextSince !== undefined) nextPullSince = Math.max(nextPullSince, sync.nextSince)\n\n if (!sync || !sync.hasMore) break\n\n // 游标推进优先使用时间戳链;旧数据源只返回 nextOffset 时,继续使用 offset 续拉同一个 since 窗口。\n if (sync.nextSince !== undefined) {\n since = sync.nextSince\n offset = undefined\n } else if (sync.nextOffset !== undefined) {\n offset = sync.nextOffset\n } else {\n this.logger.warn(`[Pull] \"${sess.name}\" returned hasMore=true without nextSince or nextOffset, stopping`)\n break\n }\n } catch (importErr) {\n cleanupTempFile(tempFile)\n throw importErr\n }\n }\n\n if (pageCount >= MAX_PAGES_PER_PULL) {\n this.logger.warn(`[Pull] \"${sess.name}\" reached page limit (${MAX_PAGES_PER_PULL}), data may be incomplete`)\n }\n\n // 保留 overlap 窗口,但成功拉取未观察到更新游标时不能把已保存游标向后移动。\n const savedLastPullAt = Math.max(sess.lastPullAt, Math.max(0, nextPullSince - PULL_OVERLAP_SECONDS))\n this.dsManager.updateSession(sourceId, sess.id, {\n lastPullAt: savedLastPullAt,\n lastStatus: 'success',\n lastNewMessages: totalNewMessages,\n lastError: '',\n })\n if (totalNewMessages > 0) this.notifier.onSessionListChanged()\n this.notifier.onPullResult(sourceId, sess.id, 'success', `+${totalNewMessages} messages`)\n this.markProgressDone(sess.id)\n if (this.onSessionImported && sess.targetSessionId) {\n try {\n this.onSessionImported(sess.targetSessionId)\n } catch (hookErr) {\n this.logger.warn(`[Pull] onSessionImported hook failed for \"${sess.name}\": ${hookErr}`)\n }\n }\n return { success: true, newMessageCount: totalNewMessages }\n } catch (error: any) {\n const sourceUnavailable = describeSourceUnavailableError(error)\n const errMsg = sourceUnavailable ?? getErrorMessage(error)\n if (!sourceUnavailable) {\n this.logger.error(`[Pull] Pull failed for \"${sess.name}\": ${errMsg}`)\n }\n this.dsManager.updateSession(sourceId, sess.id, {\n lastPullAt: Math.floor(Date.now() / 1000),\n lastStatus: 'error',\n lastError: errMsg,\n })\n this.notifier.onPullResult(sourceId, sess.id, 'error', errMsg)\n this.markProgressDone(sess.id)\n return { success: false, newMessageCount: 0, error: errMsg, sourceUnavailable: !!sourceUnavailable }\n }\n }\n\n private markProgressDone(sessionId: string): void {\n const p = this.progressMap.get(sessionId)\n if (p) {\n p.done = true\n setTimeout(() => this.progressMap.delete(sessionId), 5000)\n }\n }\n\n async pullAllSessions(ds: DataSource): Promise<void> {\n if (this.pullingSourceIds.has(ds.id)) {\n this.logger.info(`[Pull] Skipping pullAllSessions for \"${ds.baseUrl}\": pull already in progress`)\n return\n }\n this.pullingSourceIds.add(ds.id)\n try {\n let sourceUnavailable = false\n for (let index = 0; index < ds.sessions.length; index++) {\n const sess = ds.sessions[index]\n const result = await this.executePullSession(ds.id, ds, sess)\n if (result.sourceUnavailable && result.error) {\n sourceUnavailable = true\n this.markRemainingSessionsSourceUnavailable(ds, index + 1, result.error)\n this.logSourceUnavailable(ds, result.error, ds.sessions.length - index)\n break\n }\n }\n if (!sourceUnavailable) this.sourceUnavailableLogs.delete(ds.id)\n } finally {\n this.pullingSourceIds.delete(ds.id)\n }\n }\n\n private markRemainingSessionsSourceUnavailable(ds: DataSource, startIndex: number, detail: string): void {\n const now = Math.floor(Date.now() / 1000)\n for (const sess of ds.sessions.slice(startIndex)) {\n this.dsManager.updateSession(ds.id, sess.id, {\n lastPullAt: now,\n lastStatus: 'error',\n lastError: detail,\n })\n this.notifier.onPullResult(ds.id, sess.id, 'error', detail)\n this.markProgressDone(sess.id)\n }\n }\n\n private logSourceUnavailable(ds: DataSource, detail: string, affectedSessions: number): void {\n const now = Date.now()\n const previous = this.sourceUnavailableLogs.get(ds.id)\n if (previous && previous.detail === detail && now - previous.lastLoggedAt < SOURCE_UNAVAILABLE_LOG_COOLDOWN_MS) {\n return\n }\n this.sourceUnavailableLogs.set(ds.id, { detail, lastLoggedAt: now })\n this.logger.warn(`[Pull] Source ${ds.baseUrl} unavailable: ${detail}, skipped ${affectedSessions} sessions`)\n }\n\n async triggerPull(sourceId: string, sessionId?: string): Promise<{ success: boolean; error?: string }> {\n const ds = this.dsManager.get(sourceId)\n if (!ds) return { success: false, error: 'Data source not found' }\n\n if (sessionId) {\n const sess = ds.sessions.find((s) => s.id === sessionId)\n if (!sess) return { success: false, error: 'Session not found' }\n const result = await this.executePullSession(sourceId, ds, sess)\n if (result.sourceUnavailable && result.error) this.logSourceUnavailable(ds, result.error, 1)\n if (result.success) this.sourceUnavailableLogs.delete(ds.id)\n return { success: result.success, error: result.error }\n }\n\n if (this.pullingSourceIds.has(sourceId)) {\n this.logger.info(`[Pull] Skipping triggerPull for source ${sourceId}: pull already in progress`)\n return { success: true }\n }\n this.pullingSourceIds.add(sourceId)\n try {\n const errors: string[] = []\n let sourceUnavailable = false\n for (let index = 0; index < ds.sessions.length; index++) {\n const sess = ds.sessions[index]\n const result = await this.executePullSession(sourceId, ds, sess)\n if (!result.success && result.error) errors.push(`${sess.name}: ${result.error}`)\n if (result.sourceUnavailable && result.error) {\n sourceUnavailable = true\n this.markRemainingSessionsSourceUnavailable(ds, index + 1, result.error)\n this.logSourceUnavailable(ds, result.error, ds.sessions.length - index)\n break\n }\n }\n if (!sourceUnavailable) this.sourceUnavailableLogs.delete(ds.id)\n if (errors.length > 0) {\n return { success: false, error: errors.join('; ') }\n }\n return { success: true }\n } finally {\n this.pullingSourceIds.delete(sourceId)\n }\n }\n\n async triggerPullAll(sourceId: string): Promise<{ success: boolean; error?: string }> {\n return this.triggerPull(sourceId)\n }\n}\n","/**\n * @openchatlab/sync — Timer-based pull scheduler\n *\n * Manages one timer per DataSource; each tick pulls all ImportSessions.\n * Pure setInterval logic, no platform dependencies.\n */\n\nimport { NOOP_LOGGER } from './types'\nimport type { DataSource, SyncLogger } from './types'\nimport type { DataSourceManager } from './data-source-manager'\nimport type { PullEngine } from './pull-engine'\n\nconst timers = new Map<string, ReturnType<typeof setInterval>>()\nlet initialized = false\n\nexport interface SchedulerOptions {\n dsManager: DataSourceManager\n pullEngine: PullEngine\n logger?: SyncLogger\n}\n\nlet _dsManager: DataSourceManager\nlet _pullEngine: PullEngine\nlet _logger: SyncLogger\n\nfunction startTimer(ds: DataSource, skipInitialPull = false): void {\n stopTimer(ds.id)\n if (!ds.enabled || ds.intervalMinutes < 1 || ds.sessions.length === 0) return\n\n const intervalMs = ds.intervalMinutes * 60 * 1000\n\n if (!skipInitialPull) {\n _pullEngine.pullAllSessions(ds).catch((err) => {\n _logger.error('[Pull] Initial pull failed', err)\n })\n }\n\n const timer = setInterval(() => {\n const current = _dsManager.loadAll().find((s) => s.id === ds.id)\n if (!current || !current.enabled || current.sessions.length === 0) {\n stopTimer(ds.id)\n return\n }\n _pullEngine.pullAllSessions(current).catch((err) => {\n _logger.error('[Pull] Scheduled pull failed', err)\n })\n }, intervalMs)\n\n timers.set(ds.id, timer)\n _logger.info(\n `[Pull] Timer started for source ${ds.baseUrl} (${ds.sessions.length} sessions, every ${ds.intervalMinutes}min)`\n )\n}\n\nexport function stopTimer(id: string): void {\n const timer = timers.get(id)\n if (timer) {\n clearInterval(timer)\n timers.delete(id)\n }\n}\n\nexport function initScheduler(options: SchedulerOptions): void {\n if (initialized) return\n initialized = true\n\n _dsManager = options.dsManager\n _pullEngine = options.pullEngine\n _logger = options.logger ?? NOOP_LOGGER\n\n const sources = _dsManager.loadAll()\n for (const ds of sources) {\n if (ds.enabled && ds.sessions.length > 0) {\n startTimer(ds)\n }\n }\n\n _logger.info(`[Pull] Initialized with ${sources.filter((s) => s.enabled).length} active sources`)\n}\n\nexport function stopAllTimers(): void {\n for (const [id] of timers) {\n stopTimer(id)\n }\n initialized = false\n _logger?.info('[Pull] All timers stopped')\n}\n\nexport function reloadTimer(dsId: string, skipInitialPull = false): void {\n stopTimer(dsId)\n const ds = _dsManager?.loadAll().find((s) => s.id === dsId)\n if (ds && ds.enabled) {\n startTimer(ds, skipInitialPull)\n }\n}\n","/**\n * Server-side implementations of @openchatlab/sync abstractions.\n *\n * NodeFetcher: uses Node.js fetch API\n * DirectImporter: uses DatabaseManager + streamImport/incrementalImport\n * NoopNotifier: placeholder (future: SSE push)\n */\n\nimport * as fs from 'fs'\nimport * as os from 'os'\nimport * as path from 'path'\nimport * as crypto from 'crypto'\nimport type { HttpFetcher, DataImporter, SyncNotifier, ImportResult, FetchParams, SyncLogger } from '@openchatlab/sync'\nimport { NOOP_LOGGER } from '@openchatlab/sync'\nimport { buildPullUrl } from '@openchatlab/sync'\nimport type { DatabaseManager } from '@openchatlab/node-runtime'\nimport { DataDirCompatibilityError, IMPORT_IN_PROGRESS_ERROR_KEY } from '@openchatlab/node-runtime'\nimport { streamImport, incrementalImport } from '../import/stream-import'\n\nfunction getTempFilePath(ext: string): string {\n const id = crypto.randomBytes(8).toString('hex')\n return path.join(os.tmpdir(), `chatlab-pull-${id}${ext}`)\n}\n\n// ==================== NodeFetcher ====================\n\nexport class NodeFetcher implements HttpFetcher {\n async fetchToTempFile(baseUrl: string, remoteSessionId: string, token: string, params: FetchParams): Promise<string> {\n const url = buildPullUrl(baseUrl, remoteSessionId, params)\n const headers: Record<string, string> = {\n Accept: 'application/json',\n }\n if (token) headers['Authorization'] = `Bearer ${token}`\n\n const response = await fetch(url, { headers, signal: AbortSignal.timeout(120_000) })\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}`)\n }\n\n const contentType = response.headers.get('content-type') || 'application/json'\n const isJsonl = contentType.includes('ndjson') || contentType.includes('jsonl')\n const tempFile = getTempFilePath(isJsonl ? '.jsonl' : '.json')\n\n const buffer = Buffer.from(await response.arrayBuffer())\n fs.writeFileSync(tempFile, buffer)\n return tempFile\n }\n}\n\n// ==================== DirectImporter ====================\n\nexport class DirectImporter implements DataImporter {\n private dbManager: DatabaseManager\n private logger: SyncLogger\n\n constructor(dbManager: DatabaseManager, logger?: SyncLogger) {\n this.dbManager = dbManager\n this.logger = logger ?? NOOP_LOGGER\n }\n\n sessionExists(sessionId: string): boolean {\n const dbPath = this.dbManager.getDbPath(sessionId)\n if (!fs.existsSync(dbPath)) return false\n try {\n const db = this.dbManager.openRawSessionDatabase(sessionId, { readonly: true })\n const row = db\n .prepare(\"SELECT COUNT(*) as cnt FROM sqlite_master WHERE type='table' AND name='message'\")\n .get() as { cnt: number }\n db.close()\n if (row.cnt === 0) {\n this.logger.warn(`[DirectImporter] DB file exists but has no message table: ${sessionId}, removing`)\n try {\n fs.unlinkSync(dbPath)\n } catch {\n /* ignore */\n }\n return false\n }\n return true\n } catch (error) {\n if (error instanceof DataDirCompatibilityError) throw error\n\n this.logger.warn(`[DirectImporter] Cannot validate DB file: ${sessionId}, removing`)\n try {\n fs.unlinkSync(dbPath)\n } catch {\n /* ignore */\n }\n return false\n }\n }\n\n async importFile(tempFile: string, targetSessionId: string | undefined, externalId: string): Promise<ImportResult> {\n if (targetSessionId && this.sessionExists(targetSessionId)) {\n return this.incrementalImportFile(targetSessionId, tempFile)\n }\n\n if (targetSessionId) {\n this.logger.info(`[DirectImporter] Session ${targetSessionId} not found locally, need full resync`)\n return { success: false, newMessageCount: 0, needFullResync: true }\n }\n\n return this.fullImportFile(tempFile, externalId)\n }\n\n private async incrementalImportFile(sessionId: string, tempFile: string): Promise<ImportResult> {\n try {\n this.dbManager.close(sessionId)\n const result = await incrementalImport(this.dbManager, sessionId, tempFile)\n\n if (result.success) {\n const newMessageCount = result.newMessageCount\n const duplicateCount = result.batch?.duplicateCount ?? 0\n this.logger.info(\n `[DirectImporter] Incremental OK: +${newMessageCount} messages (${duplicateCount} duplicates skipped)`\n )\n return { success: true, newMessageCount, sessionId }\n }\n\n if (result.error === 'error.session_not_found' || result.error?.includes('no such table')) {\n return { success: false, newMessageCount: 0, sessionId, needFullResync: true }\n }\n if (result.error === IMPORT_IN_PROGRESS_ERROR_KEY) {\n this.logger.info(`[DirectImporter] Incremental import deferred: another import is in progress`)\n return { success: false, newMessageCount: 0, sessionId, error: result.error, retryable: true }\n }\n\n return { success: false, newMessageCount: 0, sessionId, error: result.error }\n } catch (err: any) {\n this.logger.error(`[DirectImporter] Incremental import failed`, err)\n return { success: false, newMessageCount: 0, sessionId, error: err.message }\n }\n }\n\n private async fullImportFile(tempFile: string, externalId: string): Promise<ImportResult> {\n try {\n const result = await streamImport(this.dbManager, tempFile, { sessionId: externalId })\n\n if (result.success) {\n const newMessageCount = result.diagnostics?.messagesWritten ?? 0\n this.logger.info(`[DirectImporter] Full import OK: +${newMessageCount} messages`)\n return { success: true, newMessageCount, sessionId: result.sessionId ?? externalId }\n }\n if (result.error === IMPORT_IN_PROGRESS_ERROR_KEY) {\n this.logger.info(`[DirectImporter] Full import deferred: another import is in progress`)\n return { success: false, newMessageCount: 0, error: result.error, retryable: true }\n }\n\n return { success: false, newMessageCount: 0, error: result.error }\n } catch (err: any) {\n this.logger.error(`[DirectImporter] Full import failed`, err)\n return { success: false, newMessageCount: 0, error: err.message }\n }\n }\n}\n\n// ==================== NoopNotifier ====================\n\nconst noop = () => {}\n\nexport class NoopNotifier implements SyncNotifier {\n onSessionListChanged = noop\n onPullResult = noop\n}\n","/**\n * ChatLab Server — Sync module entry point\n *\n * Wires @openchatlab/sync with server-side implementations.\n */\n\nimport type { FastifyInstance } from 'fastify'\nimport type { DatabaseManager } from '@openchatlab/node-runtime'\nimport { PreferencesManager, createDatabaseManagerAdapter, ownerProfileService } from '@openchatlab/node-runtime'\nimport type { PathProvider } from '@openchatlab/core'\nimport { DataSourceManager, PullEngine, initScheduler, stopAllTimers, reloadTimer, stopTimer } from '@openchatlab/sync'\nimport type { SyncLogger } from '@openchatlab/sync'\nimport { NodeFetcher, DirectImporter, NoopNotifier } from './adapters'\nimport { registerAutomationRoutes } from './routes'\nimport type { AutomationRouteContext } from '@openchatlab/http-routes'\n\nexport interface SyncRouteContext {\n automation: AutomationRouteContext\n}\n\nconst syncLogger: SyncLogger = {\n info: (msg) => console.log(`[Sync] ${msg}`),\n warn: (msg) => console.warn(`[Sync] ${msg}`),\n error: (msg, err?) => console.error(`[Sync] ${msg}`, err ?? ''),\n}\n\nlet dsManager: DataSourceManager | null = null\nlet pullEngine: PullEngine | null = null\n\nexport function initSync(\n server: FastifyInstance,\n dbManager: DatabaseManager,\n pathProvider: PathProvider,\n serverInfo: { port: number; host: string; token: string }\n): void {\n const settingsDir = pathProvider.getSettingsDir()\n\n dsManager = new DataSourceManager(settingsDir, syncLogger)\n\n const fetcher = new NodeFetcher()\n const importer = new DirectImporter(dbManager, syncLogger)\n const notifier = new NoopNotifier()\n\n const sessionAdapter = createDatabaseManagerAdapter(dbManager)\n const preferences = new PreferencesManager(pathProvider.getSystemDir())\n\n pullEngine = new PullEngine({\n fetcher,\n importer,\n notifier,\n dsManager,\n logger: syncLogger,\n onSessionImported: (localSessionId) => {\n preferences.invalidateCache()\n const result = ownerProfileService.tryApplyOwnerProfile(sessionAdapter, preferences, localSessionId)\n if (result.applied) {\n syncLogger.info(`[Pull] Applied owner profile to session ${localSessionId} (owner: ${result.ownerId})`)\n }\n },\n })\n\n registerAutomationRoutes(server, {\n automation: {\n dsManager,\n pullEngine,\n serverInfo,\n deleteSessionData: (sessionId) => dbManager.deleteSessionDatabaseFiles(sessionId),\n reloadTimer,\n stopTimer,\n },\n })\n\n initScheduler({\n dsManager,\n pullEngine,\n logger: syncLogger,\n })\n}\n\nexport function cleanupSync(): void {\n stopAllTimers()\n dsManager = null\n pullEngine = null\n}\n"],"mappings":"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","names":["fs","crypto","server","server","apiErr","server","err","server","server","server","server","isTruthy","parseContactsTimeRangePreset","server","isTruthy","server","path","server","server","server","server","fs","path","server","server","server","server","server","server","server","server","server","fs","path","stat","server","path","server","path","fs","path","ensureDb","server","dbManager","streamImport","fs","path","stat","server","server","server","server","server","server","os","path","fs","os","path","fs","stat","registerImportRoutes","server","dbManager","sendEvent","path","resolve","server","dbManager","streamImport","IMPORT_IN_PROGRESS_ERROR_KEY","ImportInProgressError","registerImportRoutes","server","fs","path","randomUUID","randomUUID","fs","path","normalizeBaseUrl","fs","crypto","stat","fs","os","path","crypto","dbManager","noop","server","dbManager","resolveNativeBinding","config"]}
|