chatlab-cli 0.25.0 → 0.26.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.
Files changed (70) hide show
  1. package/dist/chat-command-ZP4WJVWC.mjs +304 -0
  2. package/dist/chat-command-ZP4WJVWC.mjs.map +1 -0
  3. package/dist/{chunk-CIBTUWAE.mjs → chunk-6G7AIF7I.mjs} +38 -8
  4. package/dist/chunk-6G7AIF7I.mjs.map +1 -0
  5. package/dist/chunk-ILGSSXOG.mjs +549 -0
  6. package/dist/chunk-ILGSSXOG.mjs.map +1 -0
  7. package/dist/{chunk-E65ABM3C.mjs → chunk-IRZRIHIT.mjs} +100 -63
  8. package/dist/chunk-IRZRIHIT.mjs.map +1 -0
  9. package/dist/{chunk-4OMV4VNH.mjs → chunk-QY67NN65.mjs} +1900 -729
  10. package/dist/chunk-QY67NN65.mjs.map +1 -0
  11. package/dist/{chunk-FSHVDWI2.mjs → chunk-SJIOPSMM.mjs} +116 -569
  12. package/dist/chunk-SJIOPSMM.mjs.map +1 -0
  13. package/dist/{chunk-GNT5G74Z.mjs → chunk-TC74HESZ.mjs} +59 -33
  14. package/dist/chunk-TC74HESZ.mjs.map +1 -0
  15. package/dist/chunk-UB4XBM4R.mjs +40 -0
  16. package/dist/chunk-UB4XBM4R.mjs.map +1 -0
  17. package/dist/{chunk-TCPXAX3F.mjs → chunk-WXC6RZCX.mjs} +17 -25
  18. package/dist/chunk-WXC6RZCX.mjs.map +1 -0
  19. package/dist/cli.mjs +3 -2
  20. package/dist/{http-PMNZ42EN.mjs → http-J3IOFFIA.mjs} +8 -6
  21. package/dist/{import-OP7R4CYY.mjs → import-POYG5LOX.mjs} +4 -4
  22. package/dist/index.mjs +10 -8
  23. package/dist/{mcp-ABXNFUJO.mjs → mcp-7HH7UMXT.mjs} +11 -6
  24. package/dist/mcp-7HH7UMXT.mjs.map +1 -0
  25. package/dist/{stream-import-L6RJHH4L.mjs → stream-import-O3H3FGV2.mjs} +3 -3
  26. package/dist-web/assets/{DebugToolsPanel-DaSNc1o-.css → DebugToolsPanel-CrqULdXp.css} +1 -1
  27. package/dist-web/assets/DebugToolsPanel.vue_vue_type_script_setup_true_lang-BTJxEma5.js +182 -0
  28. package/dist-web/assets/{EChartWordcloud-CzhfrEF_.js → EChartWordcloud-B_YisOW5.js} +1 -1
  29. package/dist-web/assets/{MessageExportModal-BAJKWX4y.js → MessageExportModal-C7NgEL8T.js} +1 -1
  30. package/dist-web/assets/{electron-CZt36wW5.js → electron-B3DFm1cC.js} +1 -1
  31. package/dist-web/assets/electron-BMM4Nfty.js +1 -0
  32. package/dist-web/assets/{fetch-qoGEVjNg.js → fetch-ArMjdMcy.js} +1 -1
  33. package/dist-web/assets/fetch-BFtuJaa1.js +1 -0
  34. package/dist-web/assets/{fetch-yWwp067l.js → fetch-BGfaAqO9.js} +1 -1
  35. package/dist-web/assets/{fetch-DD1EGRKV.js → fetch-BaJDB31c.js} +1 -1
  36. package/dist-web/assets/{fetch-JzVEwamn.js → fetch-BjDJgdN0.js} +1 -1
  37. package/dist-web/assets/{fetch-GMkMBzBS.js → fetch-C5CLcpQW.js} +1 -1
  38. package/dist-web/assets/{fetch-DXfZysDf.js → fetch-DQZvdHnG.js} +1 -1
  39. package/dist-web/assets/{fetch-D4p9vj1A.js → fetch-Dhfbt1BQ.js} +3 -3
  40. package/dist-web/assets/fetch-DnEP0uyF.js +1 -0
  41. package/dist-web/assets/{fetch-CgBIWyAo.js → fetch-SYrb-sZe.js} +1 -1
  42. package/dist-web/assets/index-CWfkrx37.js +173 -0
  43. package/dist-web/assets/index-D3N4NDFx.css +1 -0
  44. package/dist-web/assets/{index-O0PG26_v.js → index-D7_ch4zf.js} +1 -1
  45. package/dist-web/assets/{index-BrS_VrIY.js → index-DK9LEEzT.js} +1 -1
  46. package/dist-web/assets/index-DqDa_on9.js +9 -0
  47. package/dist-web/assets/{index-Cvlw0RLu.js → index-DrN1G9bl.js} +2 -2
  48. package/dist-web/assets/{index-DvWI6Yqa.js → index-MdYeovYo.js} +4 -4
  49. package/dist-web/assets/{vendor-nuxt-ui-C_8CsH6e.js → vendor-nuxt-ui-Zy8txNdC.js} +1 -1
  50. package/dist-web/assets/{vendor-reka-ui-CIMXwvEp.js → vendor-reka-ui-mMvma1EP.js} +1 -1
  51. package/dist-web/assets/{web-4rtrAcLu.js → web-zEXsvoZW.js} +1 -1
  52. package/dist-web/index.html +4 -4
  53. package/package.json +1 -1
  54. package/dist/chunk-4OMV4VNH.mjs.map +0 -1
  55. package/dist/chunk-CIBTUWAE.mjs.map +0 -1
  56. package/dist/chunk-E65ABM3C.mjs.map +0 -1
  57. package/dist/chunk-FSHVDWI2.mjs.map +0 -1
  58. package/dist/chunk-GNT5G74Z.mjs.map +0 -1
  59. package/dist/chunk-TCPXAX3F.mjs.map +0 -1
  60. package/dist/mcp-ABXNFUJO.mjs.map +0 -1
  61. package/dist-web/assets/DebugToolsPanel.vue_vue_type_script_setup_true_lang-BUwpIerl.js +0 -161
  62. package/dist-web/assets/electron-CwERrTWp.js +0 -1
  63. package/dist-web/assets/fetch-BxFAkm7O.js +0 -1
  64. package/dist-web/assets/fetch-DO8lty7_.js +0 -1
  65. package/dist-web/assets/index-C1t-kzfo.css +0 -1
  66. package/dist-web/assets/index-DABpUm3J.js +0 -9
  67. package/dist-web/assets/index-OsjX5hXw.js +0 -173
  68. /package/dist/{http-PMNZ42EN.mjs.map → http-J3IOFFIA.mjs.map} +0 -0
  69. /package/dist/{import-OP7R4CYY.mjs.map → import-POYG5LOX.mjs.map} +0 -0
  70. /package/dist/{stream-import-L6RJHH4L.mjs.map → stream-import-O3H3FGV2.mjs.map} +0 -0
@@ -5,48 +5,29 @@ const require = __createRequire(import.meta.url);
5
5
  const __filename = __fileURLToPath(import.meta.url);
6
6
  const __dirname = __pathDirname(__filename);
7
7
  import {
8
- CHAT_DB_TABLES,
8
+ DataDirCompatibilityError,
9
9
  analyzeIncrementalImport,
10
10
  analyzeNewImport,
11
11
  generateIncrementalSessionIndex,
12
12
  generateSessionIndex,
13
13
  incrementalImport,
14
- openBetterSqliteDatabase,
15
14
  streamingImport
16
- } from "./chunk-4OMV4VNH.mjs";
15
+ } from "./chunk-QY67NN65.mjs";
17
16
 
18
17
  // src/import/stream-import.ts
19
- import * as fs from "fs";
20
- import * as path from "path";
21
18
  import * as crypto from "crypto";
22
19
  function generateSessionId() {
23
20
  const ts = Date.now();
24
21
  const rand = crypto.randomBytes(4).toString("hex");
25
22
  return `chat_${ts}_${rand}`;
26
23
  }
27
- function resolveNativeBinding(dbManager) {
28
- return dbManager.nativeBinding;
29
- }
30
24
  function buildStreamImportDeps(dbManager, onProgress) {
31
- const nativeBinding = resolveNativeBinding(dbManager);
32
25
  return {
33
26
  openDatabase(sessionId) {
34
- const dbPath = dbManager.getDbPath(sessionId);
35
- const dir = path.dirname(dbPath);
36
- if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
37
- const db = openBetterSqliteDatabase(dbPath, { nativeBinding });
38
- db.exec(CHAT_DB_TABLES);
39
- return db;
27
+ return dbManager.openRawSessionDatabase(sessionId, { create: true, initializeChatTables: true });
40
28
  },
41
29
  deleteDatabase(sessionId) {
42
- const dbPath = dbManager.getDbPath(sessionId);
43
- for (const suffix of ["", "-wal", "-shm"]) {
44
- try {
45
- const p = dbPath + suffix;
46
- if (fs.existsSync(p)) fs.unlinkSync(p);
47
- } catch {
48
- }
49
- }
30
+ dbManager.deleteSessionDatabaseFiles(sessionId);
50
31
  },
51
32
  onProgress: onProgress ?? (() => {
52
33
  }),
@@ -59,6 +40,9 @@ function buildStreamImportDeps(dbManager, onProgress) {
59
40
  generateSessionId
60
41
  };
61
42
  }
43
+ function deleteSessionDatabase(dbManager, sessionId) {
44
+ dbManager.deleteSessionDatabaseFiles(sessionId);
45
+ }
62
46
  async function streamImport(dbManager, filePath, options) {
63
47
  const { formatId, chatIndex, onProgress } = options || {};
64
48
  const formatOptions = {};
@@ -101,17 +85,29 @@ async function streamImport(dbManager, filePath, options) {
101
85
  } : () => {
102
86
  };
103
87
  const deps = buildStreamImportDeps(dbManager, progressAdapter);
104
- return streamingImport(filePath, deps, formatOptions);
88
+ const result = await streamingImport(filePath, deps, formatOptions);
89
+ if (!result.success || !result.sessionId) return result;
90
+ try {
91
+ dbManager.raiseCurrentChatDbCompatibilityGate();
92
+ } catch (error) {
93
+ deleteSessionDatabase(dbManager, result.sessionId);
94
+ return {
95
+ success: false,
96
+ error: error instanceof Error ? error.message : String(error),
97
+ diagnostics: result.diagnostics
98
+ };
99
+ }
100
+ return result;
105
101
  }
106
- function buildIncrementalDeps(dbManager, onProgress) {
107
- const nativeBinding = resolveNativeBinding(dbManager);
102
+ function buildIncrementalDeps(dbManager, onProgress, onCompatibilityError) {
108
103
  return {
109
104
  openDatabase(sessionId, readonly) {
110
- const dbPath = dbManager.getDbPath(sessionId);
111
- if (!fs.existsSync(dbPath)) {
112
- throw new Error(`Session database not found: ${sessionId}`);
105
+ try {
106
+ return dbManager.openRawSessionDatabase(sessionId, { readonly: readonly ?? false });
107
+ } catch (error) {
108
+ if (error instanceof DataDirCompatibilityError) onCompatibilityError?.(error);
109
+ throw error;
113
110
  }
114
- return openBetterSqliteDatabase(dbPath, { readonly: readonly ?? false, nativeBinding });
115
111
  },
116
112
  onProgress: onProgress ?? (() => {
117
113
  }),
@@ -125,10 +121,40 @@ function buildIncrementalDeps(dbManager, onProgress) {
125
121
  }
126
122
  async function incrementalImport2(dbManager, sessionId, filePath, options) {
127
123
  const { onProgress, ...importOpts } = options || {};
128
- return incrementalImport(sessionId, filePath, buildIncrementalDeps(dbManager, onProgress), importOpts);
124
+ let compatibilityError = null;
125
+ const result = await incrementalImport(
126
+ sessionId,
127
+ filePath,
128
+ buildIncrementalDeps(dbManager, onProgress, (error) => {
129
+ compatibilityError = error;
130
+ }),
131
+ importOpts
132
+ );
133
+ if (compatibilityError) throw compatibilityError;
134
+ if (!result.success) return result;
135
+ try {
136
+ dbManager.raiseCurrentChatDbCompatibilityGate();
137
+ } catch (error) {
138
+ return {
139
+ ...result,
140
+ success: false,
141
+ newMessageCount: 0,
142
+ error: error instanceof Error ? error.message : String(error)
143
+ };
144
+ }
145
+ return result;
129
146
  }
130
147
  async function analyzeIncrementalImport2(dbManager, sessionId, filePath, onProgress) {
131
- return analyzeIncrementalImport(sessionId, filePath, buildIncrementalDeps(dbManager, onProgress));
148
+ let compatibilityError = null;
149
+ const result = await analyzeIncrementalImport(
150
+ sessionId,
151
+ filePath,
152
+ buildIncrementalDeps(dbManager, onProgress, (error) => {
153
+ compatibilityError = error;
154
+ })
155
+ );
156
+ if (compatibilityError) throw compatibilityError;
157
+ return result;
132
158
  }
133
159
  async function analyzeNewImport2(filePath, onProgress) {
134
160
  return analyzeNewImport(filePath, onProgress ?? (() => {
@@ -141,4 +167,4 @@ export {
141
167
  analyzeIncrementalImport2 as analyzeIncrementalImport,
142
168
  analyzeNewImport2 as analyzeNewImport
143
169
  };
144
- //# sourceMappingURL=chunk-GNT5G74Z.mjs.map
170
+ //# sourceMappingURL=chunk-TC74HESZ.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/import/stream-import.ts"],"sourcesContent":["/**\n * Server/CLI streaming import — adapter for @openchatlab/node-runtime StreamingImporter.\n *\n * Replaces the old buffered-in-memory approach with the same high-performance\n * streaming pipeline used by Electron (batched transactions, deferred indexes,\n * nickname history, FTS, format fallback).\n */\n\nimport type { DatabaseManager } from '@openchatlab/node-runtime'\nimport {\n DataDirCompatibilityError,\n streamingImport,\n analyzeNewImport as sharedAnalyzeNewImport,\n analyzeIncrementalImport as sharedAnalyzeIncremental,\n incrementalImport as sharedIncrementalImport,\n} from '@openchatlab/node-runtime'\nimport { generateSessionIndex, generateIncrementalSessionIndex } from '@openchatlab/core'\nimport type {\n StreamImportResult,\n StreamImportDeps,\n ImportProgressCallback,\n IncrementalImportResult,\n IncrementalAnalyzeResult,\n IncrementalImportDeps,\n ImportOptions,\n AnalyzeNewImportResult,\n} from '@openchatlab/node-runtime'\nimport {\n detectFormat as parserDetectFormat,\n detectAllFormats,\n getFormatFeatureById,\n getSupportedFormats as parserGetSupportedFormats,\n scanMultiChatFile as parserScanMultiChatFile,\n findEntryFileInDirectory,\n type FormatFeature,\n type MultiChatInfo,\n type ParseProgress,\n} from '@openchatlab/parser'\nimport * as crypto from 'crypto'\n\n// ==================== Legacy progress interface (for SSE routes) ====================\n\nexport interface StreamImportProgress {\n stage: 'detecting' | 'parsing' | 'saving' | 'indexing' | 'done' | 'error'\n progress: number\n message: string\n bytesRead?: number\n totalBytes?: number\n messagesProcessed?: number\n}\n\nexport interface StreamImportOptions {\n formatId?: string\n chatIndex?: number\n nativeBinding?: string\n onProgress?: (progress: StreamImportProgress) => void\n}\n\nfunction generateSessionId(): string {\n const ts = Date.now()\n const rand = crypto.randomBytes(4).toString('hex')\n return `chat_${ts}_${rand}`\n}\n\nfunction buildStreamImportDeps(dbManager: DatabaseManager, onProgress?: ImportProgressCallback): StreamImportDeps {\n return {\n openDatabase(sessionId: string) {\n return dbManager.openRawSessionDatabase(sessionId, { create: true, initializeChatTables: true })\n },\n deleteDatabase(sessionId: string) {\n dbManager.deleteSessionDatabaseFiles(sessionId)\n },\n onProgress: onProgress ?? (() => {}),\n postImportHook(db) {\n try {\n generateSessionIndex(db)\n } catch {\n /* non-fatal: frontend can regenerate via generate-index route */\n }\n },\n generateSessionId,\n }\n}\n\nfunction deleteSessionDatabase(dbManager: DatabaseManager, sessionId: string): void {\n dbManager.deleteSessionDatabaseFiles(sessionId)\n}\n\n/**\n * High-performance streaming import: parse a file and write to DB\n * with batched transactions, deferred indexes, and FTS.\n */\nexport async function streamImport(\n dbManager: DatabaseManager,\n filePath: string,\n options?: StreamImportOptions\n): Promise<StreamImportResult> {\n const { formatId, chatIndex, onProgress } = options || {}\n\n const formatOptions: Record<string, unknown> = {}\n if (formatId) formatOptions.formatId = formatId\n if (chatIndex !== undefined) formatOptions.chatIndex = chatIndex\n\n const progressAdapter: ImportProgressCallback = onProgress\n ? (progress) => {\n let stage: StreamImportProgress['stage'] = 'parsing'\n let pct = 0\n switch (progress.stage) {\n case 'detecting':\n stage = 'detecting'\n pct = 5\n break\n case 'parsing':\n stage = 'parsing'\n pct = Math.min(Math.round(progress.percentage * 0.7), 70)\n break\n case 'importing':\n case 'saving':\n stage = 'saving'\n pct = 80\n break\n case 'done':\n stage = 'done'\n pct = 100\n break\n case 'error':\n stage = 'error'\n pct = 0\n break\n }\n onProgress({\n stage,\n progress: pct,\n message: progress.message || '',\n bytesRead: progress.bytesRead,\n totalBytes: progress.totalBytes,\n messagesProcessed: progress.messagesProcessed,\n })\n }\n : () => {}\n\n const deps = buildStreamImportDeps(dbManager, progressAdapter)\n const result = await streamingImport(filePath, deps, formatOptions)\n if (!result.success || !result.sessionId) return result\n\n try {\n dbManager.raiseCurrentChatDbCompatibilityGate()\n } catch (error) {\n deleteSessionDatabase(dbManager, result.sessionId)\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n diagnostics: result.diagnostics,\n }\n }\n\n return result\n}\n\n// ==================== Incremental import ====================\n\nfunction buildIncrementalDeps(\n dbManager: DatabaseManager,\n onProgress?: ImportProgressCallback,\n onCompatibilityError?: (error: DataDirCompatibilityError) => void\n): IncrementalImportDeps {\n return {\n openDatabase(sessionId: string, readonly?: boolean) {\n try {\n return dbManager.openRawSessionDatabase(sessionId, { readonly: readonly ?? false })\n } catch (error) {\n if (error instanceof DataDirCompatibilityError) onCompatibilityError?.(error)\n throw error\n }\n },\n onProgress: onProgress ?? (() => {}),\n postImportHook(db) {\n try {\n generateIncrementalSessionIndex(db)\n } catch {\n /* non-fatal */\n }\n },\n }\n}\n\nexport async function incrementalImport(\n dbManager: DatabaseManager,\n sessionId: string,\n filePath: string,\n options?: ImportOptions & { onProgress?: ImportProgressCallback }\n): Promise<IncrementalImportResult> {\n const { onProgress, ...importOpts } = options || {}\n let compatibilityError: DataDirCompatibilityError | null = null\n const result = await sharedIncrementalImport(\n sessionId,\n filePath,\n buildIncrementalDeps(dbManager, onProgress, (error) => {\n compatibilityError = error\n }),\n importOpts\n )\n if (compatibilityError) throw compatibilityError\n if (!result.success) return result\n\n try {\n dbManager.raiseCurrentChatDbCompatibilityGate()\n } catch (error) {\n return {\n ...result,\n success: false,\n newMessageCount: 0,\n error: error instanceof Error ? error.message : String(error),\n }\n }\n\n return result\n}\n\nexport async function analyzeIncrementalImport(\n dbManager: DatabaseManager,\n sessionId: string,\n filePath: string,\n onProgress?: ImportProgressCallback\n): Promise<IncrementalAnalyzeResult> {\n let compatibilityError: DataDirCompatibilityError | null = null\n const result = await sharedAnalyzeIncremental(\n sessionId,\n filePath,\n buildIncrementalDeps(dbManager, onProgress, (error) => {\n compatibilityError = error\n })\n )\n if (compatibilityError) throw compatibilityError\n return result\n}\n\nexport async function analyzeNewImport(\n filePath: string,\n onProgress?: ImportProgressCallback\n): Promise<AnalyzeNewImportResult> {\n return sharedAnalyzeNewImport(filePath, onProgress ?? (() => {}))\n}\n\n// ==================== Re-exports from parser ====================\n\nexport {\n parserDetectFormat as detectFormat,\n detectAllFormats,\n getFormatFeatureById,\n parserGetSupportedFormats as getSupportedFormats,\n parserScanMultiChatFile as scanMultiChatFile,\n findEntryFileInDirectory,\n}\nexport type { FormatFeature, MultiChatInfo, ParseProgress }\nexport type {\n StreamImportResult,\n IncrementalImportResult,\n IncrementalAnalyzeResult,\n AnalyzeNewImportResult,\n ImportOptions,\n}\n"],"mappings":"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","names":["incrementalImport","analyzeIncrementalImport","analyzeNewImport"]}
@@ -0,0 +1,40 @@
1
+ import { createRequire as __createRequire } from 'module';
2
+ import { dirname as __pathDirname } from 'path';
3
+ import { fileURLToPath as __fileURLToPath } from 'url';
4
+ const require = __createRequire(import.meta.url);
5
+ const __filename = __fileURLToPath(import.meta.url);
6
+ const __dirname = __pathDirname(__filename);
7
+ import {
8
+ getVersion
9
+ } from "./chunk-5ADKYYNA.mjs";
10
+ import {
11
+ DataDirCompatibilityError,
12
+ assertDataDirCompatible
13
+ } from "./chunk-QY67NN65.mjs";
14
+
15
+ // src/runtime-compat.ts
16
+ function assertCliDataDirCompatible(pathProvider, kind, options) {
17
+ const runtime = { version: options?.version ?? getVersion(), kind };
18
+ try {
19
+ assertDataDirCompatible(pathProvider, runtime);
20
+ } catch (error) {
21
+ if (error instanceof DataDirCompatibilityError && error.code === "DATA_DIR_REQUIRES_NEWER_RUNTIME" && error.minRuntimeVersion) {
22
+ throw new Error(formatCliDataDirCompatibilityError(error, runtime), { cause: error });
23
+ }
24
+ throw error;
25
+ }
26
+ return runtime;
27
+ }
28
+ function formatCliDataDirCompatibilityError(error, runtime) {
29
+ return [
30
+ `ChatLab data directory requires ChatLab ${error.minRuntimeVersion} or newer.`,
31
+ `Current ${runtime.kind} version: ${runtime.version}.`,
32
+ `Data directory: ${error.userDataDir}.`,
33
+ "Upgrade: npm install -g chatlab-cli@latest"
34
+ ].join("\n");
35
+ }
36
+
37
+ export {
38
+ assertCliDataDirCompatible
39
+ };
40
+ //# sourceMappingURL=chunk-UB4XBM4R.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/runtime-compat.ts"],"sourcesContent":["import type { PathProvider } from '@openchatlab/core'\nimport {\n assertDataDirCompatible,\n DataDirCompatibilityError,\n type RuntimeIdentity,\n type RuntimeKind,\n} from '@openchatlab/node-runtime'\nimport { getVersion } from './version'\n\nexport function createCliRuntimeIdentity(kind: Extract<RuntimeKind, 'cli' | 'mcp'>): RuntimeIdentity {\n return { version: getVersion(), kind }\n}\n\nexport function assertCliDataDirCompatible(\n pathProvider: PathProvider,\n kind: Extract<RuntimeKind, 'cli' | 'mcp'>,\n options?: { version?: string }\n): RuntimeIdentity {\n const runtime: RuntimeIdentity = { version: options?.version ?? getVersion(), kind }\n\n try {\n assertDataDirCompatible(pathProvider, runtime)\n } catch (error) {\n if (\n error instanceof DataDirCompatibilityError &&\n error.code === 'DATA_DIR_REQUIRES_NEWER_RUNTIME' &&\n error.minRuntimeVersion\n ) {\n throw new Error(formatCliDataDirCompatibilityError(error, runtime), { cause: error })\n }\n\n throw error\n }\n\n return runtime\n}\n\nfunction formatCliDataDirCompatibilityError(error: DataDirCompatibilityError, runtime: RuntimeIdentity): string {\n return [\n `ChatLab data directory requires ChatLab ${error.minRuntimeVersion} or newer.`,\n `Current ${runtime.kind} version: ${runtime.version}.`,\n `Data directory: ${error.userDataDir}.`,\n 'Upgrade: npm install -g chatlab-cli@latest',\n ].join('\\n')\n}\n"],"mappings":";;;;;;;;;;;;;;;AAaO,SAAS,2BACd,cACA,MACA,SACiB;AACjB,QAAM,UAA2B,EAAE,SAAS,SAAS,WAAW,WAAW,GAAG,KAAK;AAEnF,MAAI;AACF,4BAAwB,cAAc,OAAO;AAAA,EAC/C,SAAS,OAAO;AACd,QACE,iBAAiB,6BACjB,MAAM,SAAS,qCACf,MAAM,mBACN;AACA,YAAM,IAAI,MAAM,mCAAmC,OAAO,OAAO,GAAG,EAAE,OAAO,MAAM,CAAC;AAAA,IACtF;AAEA,UAAM;AAAA,EACR;AAEA,SAAO;AACT;AAEA,SAAS,mCAAmC,OAAkC,SAAkC;AAC9G,SAAO;AAAA,IACL,2CAA2C,MAAM,iBAAiB;AAAA,IAClE,WAAW,QAAQ,IAAI,aAAa,QAAQ,OAAO;AAAA,IACnD,mBAAmB,MAAM,WAAW;AAAA,IACpC;AAAA,EACF,EAAE,KAAK,IAAI;AACb;","names":[]}
@@ -9,9 +9,8 @@ import {
9
9
  FTS_TABLE_SCHEMA,
10
10
  buildMemberIdMap,
11
11
  generateMessageKey,
12
- openBetterSqliteDatabase,
13
12
  writeParseResultToDb
14
- } from "./chunk-4OMV4VNH.mjs";
13
+ } from "./chunk-QY67NN65.mjs";
15
14
 
16
15
  // src/import/chatlab-reader.ts
17
16
  import * as fs from "fs";
@@ -344,36 +343,29 @@ async function importData(dbManager, data, options) {
344
343
  }
345
344
  try {
346
345
  if (exists) {
347
- dbManager.close(sessionId);
348
- const db2 = openBetterSqliteDatabase(dbPath, { readonly: false, nativeBinding: options?.nativeBinding });
346
+ const db2 = dbManager.openRawSessionDatabase(sessionId, { readonly: false });
347
+ let result2;
349
348
  try {
350
- const result2 = incrementalImport(db2, data, options?.onProgress);
349
+ result2 = incrementalImport(db2, data, options?.onProgress);
350
+ } finally {
351
351
  db2.close();
352
- return { success: true, sessionId, created: false, ...result2 };
353
- } catch (err) {
354
- db2.close();
355
- throw err;
356
352
  }
353
+ dbManager.raiseCurrentChatDbCompatibilityGate();
354
+ return { success: true, sessionId, created: false, ...result2 };
355
+ }
356
+ const db = dbManager.openRawSessionDatabase(sessionId, { create: true });
357
+ let result;
358
+ try {
359
+ result = fullImport(db, data, options?.onProgress);
360
+ } finally {
361
+ db.close();
357
362
  }
358
- const db = openBetterSqliteDatabase(dbPath, { nativeBinding: options?.nativeBinding });
359
- const result = fullImport(db, data, options?.onProgress);
360
- db.close();
363
+ dbManager.raiseCurrentChatDbCompatibilityGate();
361
364
  return { success: true, sessionId, created: true, ...result };
362
365
  } catch (err) {
363
366
  const message = err instanceof Error ? err.message : String(err);
364
367
  if (!exists) {
365
- try {
366
- fs2.unlinkSync(dbPath);
367
- } catch {
368
- }
369
- try {
370
- fs2.unlinkSync(dbPath + "-wal");
371
- } catch {
372
- }
373
- try {
374
- fs2.unlinkSync(dbPath + "-shm");
375
- } catch {
376
- }
368
+ dbManager.deleteSessionDatabaseFiles(sessionId);
377
369
  }
378
370
  return {
379
371
  success: false,
@@ -392,4 +384,4 @@ export {
392
384
  parseFile,
393
385
  importData
394
386
  };
395
- //# sourceMappingURL=chunk-TCPXAX3F.mjs.map
387
+ //# sourceMappingURL=chunk-WXC6RZCX.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/import/chatlab-reader.ts","../src/import/importer.ts"],"sourcesContent":["/**\n * ChatLab 格式读取器\n *\n * 支持 ChatLab JSON (.json) 和 ChatLab JSONL (.jsonl) 两种格式。\n * 不依赖 stream-json,使用原生 readline 和 JSON.parse 实现。\n */\n\nimport * as fs from 'fs'\nimport * as readline from 'readline'\n\nexport interface ImportMeta {\n name: string\n platform: string\n type: string\n groupId?: string\n groupAvatar?: string\n ownerId?: string\n}\n\nexport interface ImportMember {\n platformId: string\n accountName: string\n groupNickname?: string\n avatar?: string\n roles?: Array<{ id: string; name?: string }>\n}\n\nexport interface ImportMessage {\n platformMessageId?: string\n senderPlatformId: string\n senderAccountName: string\n senderGroupNickname?: string\n timestamp: number\n type: number\n content: string | null\n replyToMessageId?: string\n}\n\nexport interface ParsedData {\n meta: ImportMeta\n members: ImportMember[]\n messages: ImportMessage[]\n}\n\nexport type ProgressCallback = (processed: number, total: number) => void\n\n/**\n * 检测文件是否为 ChatLab 格式\n */\nexport function detectFormat(filePath: string): 'chatlab-json' | 'chatlab-jsonl' | null {\n const ext = filePath.toLowerCase()\n if (ext.endsWith('.jsonl')) {\n const head = fs.readFileSync(filePath, { encoding: 'utf-8', flag: 'r' }).slice(0, 1024)\n if (head.includes('\"_type\"') && head.includes('\"chatlab\"')) {\n return 'chatlab-jsonl'\n }\n return null\n }\n if (ext.endsWith('.json')) {\n const head = fs.readFileSync(filePath, { encoding: 'utf-8', flag: 'r' }).slice(0, 4096)\n if (/\"chatlab\"\\s*:\\s*\\{/.test(head)) {\n return 'chatlab-json'\n }\n return null\n }\n return null\n}\n\n/**\n * 解析 ChatLab JSON 格式文件\n */\nexport async function parseChatLabJson(filePath: string, onProgress?: ProgressCallback): Promise<ParsedData> {\n const raw = fs.readFileSync(filePath, 'utf-8')\n const data = JSON.parse(raw)\n\n if (!data.chatlab || typeof data.chatlab !== 'object') {\n throw new Error('Invalid ChatLab JSON: missing \"chatlab\" field')\n }\n\n const meta: ImportMeta = {\n name: data.meta?.name || '未知',\n platform: data.meta?.platform || 'unknown',\n type: data.meta?.type || 'group',\n groupId: data.meta?.groupId,\n groupAvatar: data.meta?.groupAvatar,\n ownerId: data.meta?.ownerId,\n }\n\n const members: ImportMember[] = (data.members || []).map((m: Record<string, unknown>) => ({\n platformId: String(m.platformId || ''),\n accountName: String(m.accountName || m.platformId || ''),\n groupNickname: m.groupNickname ? String(m.groupNickname) : undefined,\n avatar: m.avatar ? String(m.avatar) : undefined,\n roles: m.roles as ImportMember['roles'],\n }))\n\n const rawMessages = data.messages || []\n const totalMessages = rawMessages.length\n\n const messages: ImportMessage[] = rawMessages.map((msg: Record<string, unknown>, i: number) => {\n if (onProgress && i % 10000 === 0) {\n onProgress(i, totalMessages)\n }\n return {\n platformMessageId: msg.platformMessageId ? String(msg.platformMessageId) : undefined,\n senderPlatformId: String(msg.sender || ''),\n senderAccountName: String(msg.accountName || msg.sender || ''),\n senderGroupNickname: msg.groupNickname ? String(msg.groupNickname) : undefined,\n timestamp: Number(msg.timestamp),\n type: Number(msg.type ?? 0),\n content: msg.content != null ? String(msg.content) : null,\n replyToMessageId: msg.replyToMessageId ? String(msg.replyToMessageId) : undefined,\n }\n })\n\n if (onProgress) onProgress(totalMessages, totalMessages)\n\n if (members.length === 0) {\n collectMembersFromMessages(messages, members)\n }\n\n return { meta, members, messages }\n}\n\n/**\n * 解析 ChatLab JSONL 格式文件\n */\nexport async function parseChatLabJsonl(filePath: string, onProgress?: ProgressCallback): Promise<ParsedData> {\n const meta: ImportMeta = { name: '未知', platform: 'unknown', type: 'group' }\n const members: ImportMember[] = []\n const messages: ImportMessage[] = []\n\n const stat = fs.statSync(filePath)\n let bytesRead = 0\n\n const rl = readline.createInterface({\n input: fs.createReadStream(filePath, 'utf-8'),\n crlfDelay: Infinity,\n })\n\n for await (const line of rl) {\n bytesRead += Buffer.byteLength(line, 'utf-8') + 1\n\n const trimmed = line.trim()\n if (!trimmed) continue\n\n let obj: Record<string, unknown>\n try {\n obj = JSON.parse(trimmed)\n } catch {\n continue\n }\n\n const type = obj._type as string\n\n if (type === 'header' || (!type && obj.chatlab)) {\n const m = obj.meta as Record<string, unknown> | undefined\n if (m) {\n meta.name = String(m.name || '未知')\n meta.platform = String(m.platform || 'unknown')\n meta.type = String(m.type || 'group')\n if (m.groupId) meta.groupId = String(m.groupId)\n if (m.groupAvatar) meta.groupAvatar = String(m.groupAvatar)\n if (m.ownerId) meta.ownerId = String(m.ownerId)\n }\n } else if (type === 'member') {\n members.push({\n platformId: String(obj.platformId || ''),\n accountName: String(obj.accountName || obj.platformId || ''),\n groupNickname: obj.groupNickname ? String(obj.groupNickname) : undefined,\n avatar: obj.avatar ? String(obj.avatar) : undefined,\n roles: obj.roles as ImportMember['roles'],\n })\n } else if (type === 'message') {\n messages.push({\n platformMessageId: obj.platformMessageId ? String(obj.platformMessageId) : undefined,\n senderPlatformId: String(obj.sender || obj.senderPlatformId || ''),\n senderAccountName: String(obj.accountName || obj.senderAccountName || obj.sender || ''),\n senderGroupNickname: obj.groupNickname\n ? String(obj.groupNickname)\n : obj.senderGroupNickname\n ? String(obj.senderGroupNickname)\n : undefined,\n timestamp: Number(obj.timestamp),\n type: Number(obj.type ?? 0),\n content: obj.content != null ? String(obj.content) : null,\n replyToMessageId: obj.replyToMessageId ? String(obj.replyToMessageId) : undefined,\n })\n\n if (onProgress && messages.length % 10000 === 0) {\n onProgress(bytesRead, stat.size)\n }\n }\n }\n\n if (onProgress) onProgress(stat.size, stat.size)\n\n if (members.length === 0) {\n collectMembersFromMessages(messages, members)\n }\n\n return { meta, members, messages }\n}\n\nfunction collectMembersFromMessages(messages: ImportMessage[], members: ImportMember[]): void {\n const seen = new Set<string>()\n for (const msg of messages) {\n if (!seen.has(msg.senderPlatformId)) {\n seen.add(msg.senderPlatformId)\n members.push({\n platformId: msg.senderPlatformId,\n accountName: msg.senderAccountName || msg.senderPlatformId,\n groupNickname: msg.senderGroupNickname,\n })\n }\n }\n}\n\n/**\n * 统一入口:自动检测格式并解析\n */\nexport async function parseFile(filePath: string, onProgress?: ProgressCallback): Promise<ParsedData> {\n const format = detectFormat(filePath)\n if (!format) {\n throw new Error(`Unsupported file format: ${filePath}. Only ChatLab JSON (.json) and JSONL (.jsonl) are supported.`)\n }\n\n if (format === 'chatlab-json') {\n return parseChatLabJson(filePath, onProgress)\n } else {\n return parseChatLabJsonl(filePath, onProgress)\n }\n}\n","/**\n * ChatLab 数据导入器\n *\n * 将解析后的数据写入 SQLite 数据库。\n * 支持新建导入和增量导入(基于 platform_message_id 或内容哈希去重)。\n */\n\nimport * as fs from 'fs'\nimport * as crypto from 'crypto'\nimport type { DatabaseAdapter } from '@openchatlab/core'\nimport { CHAT_DB_SCHEMA, FTS_TABLE_SCHEMA, generateMessageKey, buildMemberIdMap } from '@openchatlab/core'\nimport type { DatabaseManager } from '@openchatlab/node-runtime'\nimport { writeParseResultToDb } from '@openchatlab/node-runtime'\nimport type { ParsedData, ImportMessage } from './chatlab-reader'\n\nexport interface ImportResult {\n success: boolean\n sessionId: string\n created: boolean\n messageCount: number\n memberCount: number\n duplicateCount: number\n error?: string\n}\n\nexport interface ImportOptions {\n sessionId?: string\n dryRun?: boolean\n nativeBinding?: string\n}\n\nfunction generateSessionId(): string {\n const ts = Date.now()\n const rand = crypto.randomBytes(4).toString('hex')\n return `chat_${ts}_${rand}`\n}\n\n/**\n * 新建导入:创建新的会话数据库并导入全部数据\n * Delegates to shared writeParseResultToDb for data writing + name history.\n */\nfunction fullImport(\n db: DatabaseAdapter,\n data: ParsedData,\n onProgress?: (msg: string) => void\n): { messageCount: number; memberCount: number; duplicateCount: number } {\n db.exec(CHAT_DB_SCHEMA)\n\n onProgress?.(`写入 ${data.members.length} 个成员, ${data.messages.length} 条消息...`)\n const stats = writeParseResultToDb(db, data.meta, data.members, data.messages)\n\n buildFts(db, onProgress)\n\n return { messageCount: stats.messageCount, memberCount: stats.memberCount, duplicateCount: stats.skippedCount }\n}\n\n/**\n * 增量导入:向已有数据库追加新消息,自动去重\n */\nfunction incrementalImport(\n db: DatabaseAdapter,\n data: ParsedData,\n onProgress?: (msg: string) => void\n): { messageCount: number; memberCount: number; duplicateCount: number } {\n onProgress?.('加载去重索引...')\n const existingKeys = new Set<string>()\n\n const existingPmids = db\n .prepare('SELECT platform_message_id FROM message WHERE platform_message_id IS NOT NULL')\n .all() as Array<{ platform_message_id: string }>\n for (const row of existingPmids) {\n existingKeys.add(`pmid:${row.platform_message_id}`)\n }\n\n const existingHashes = db\n .prepare(\n `SELECT msg.ts, m.platform_id, msg.content\n FROM message msg\n JOIN member m ON msg.sender_id = m.id\n WHERE msg.platform_message_id IS NULL`\n )\n .all() as Array<{ ts: number; platform_id: string; content: string | null }>\n for (const row of existingHashes) {\n existingKeys.add(`hash:${generateMessageKey(row.ts, row.platform_id, row.content)}`)\n }\n\n onProgress?.(`去重索引: ${existingKeys.size} 条已有消息`)\n\n const upsertMember = db.prepare(\n `INSERT INTO member (platform_id, account_name, group_nickname, avatar, roles)\n VALUES (?, ?, ?, ?, ?)\n ON CONFLICT(platform_id) DO UPDATE SET\n account_name = COALESCE(NULLIF(excluded.account_name, ''), account_name),\n group_nickname = COALESCE(excluded.group_nickname, group_nickname)`\n )\n\n let newMembers = 0\n for (const m of data.members) {\n const result = upsertMember.run(\n m.platformId,\n m.accountName || m.platformId,\n m.groupNickname || null,\n m.avatar || null,\n m.roles ? JSON.stringify(m.roles) : '[]'\n )\n if (result.changes > 0) newMembers++\n }\n\n const memberIdMap = buildMemberIdMap(db)\n\n const insertMsg = db.prepare(\n `INSERT INTO message (sender_id, sender_account_name, sender_group_nickname, ts, type, content, reply_to_message_id, platform_message_id)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?)`\n )\n const insertMemberMinimal = db.prepare(`INSERT OR IGNORE INTO member (platform_id, account_name) VALUES (?, ?)`)\n\n let written = 0\n let duplicates = 0\n const BATCH = 5000\n\n for (let i = 0; i < data.messages.length; i += BATCH) {\n const batch = data.messages.slice(i, i + BATCH)\n db.transaction(() => {\n for (const msg of batch) {\n if (isDuplicate(msg, existingKeys)) {\n duplicates++\n continue\n }\n\n let senderId = memberIdMap.get(msg.senderPlatformId)\n if (!senderId) {\n insertMemberMinimal.run(msg.senderPlatformId, msg.senderAccountName || msg.senderPlatformId)\n senderId = (\n db.prepare('SELECT id FROM member WHERE platform_id = ?').get(msg.senderPlatformId) as { id: number }\n )?.id\n if (senderId) memberIdMap.set(msg.senderPlatformId, senderId)\n }\n if (!senderId) continue\n\n insertMsg.run(\n senderId,\n msg.senderAccountName || null,\n msg.senderGroupNickname || null,\n msg.timestamp,\n msg.type,\n msg.content,\n msg.replyToMessageId || null,\n msg.platformMessageId || null\n )\n written++\n }\n })\n\n if (onProgress && (i + BATCH) % 50000 < BATCH) {\n onProgress(\n `已处理 ${Math.min(i + BATCH, data.messages.length)} / ${data.messages.length} 条消息 (新增 ${written}, 重复 ${duplicates})`\n )\n }\n }\n\n if (written > 0) {\n insertFtsEntries(db, onProgress)\n db.prepare('UPDATE meta SET imported_at = ?').run(Math.floor(Date.now() / 1000))\n }\n\n return { messageCount: written, memberCount: newMembers, duplicateCount: duplicates }\n}\n\nfunction isDuplicate(msg: ImportMessage, existingKeys: Set<string>): boolean {\n if (msg.platformMessageId) {\n return existingKeys.has(`pmid:${msg.platformMessageId}`)\n }\n const key = generateMessageKey(msg.timestamp, msg.senderPlatformId, msg.content)\n return existingKeys.has(`hash:${key}`)\n}\n\nfunction buildFts(db: DatabaseAdapter, onProgress?: (msg: string) => void): void {\n onProgress?.('创建全文搜索索引...')\n db.exec(FTS_TABLE_SCHEMA)\n\n const textMessages = db\n .prepare(\"SELECT id, content FROM message WHERE type = 0 AND content IS NOT NULL AND content != ''\")\n .all() as Array<{ id: number; content: string }>\n\n const insertFts = db.prepare('INSERT INTO message_fts(rowid, content) VALUES (?, ?)')\n const BATCH = 5000\n for (let i = 0; i < textMessages.length; i += BATCH) {\n const batch = textMessages.slice(i, i + BATCH)\n db.transaction(() => {\n for (const row of batch) {\n insertFts.run(row.id, row.content)\n }\n })\n }\n}\n\nfunction insertFtsEntries(db: DatabaseAdapter, onProgress?: (msg: string) => void): void {\n try {\n db.prepare(\"SELECT 1 FROM sqlite_master WHERE type='table' AND name='message_fts'\").get()\n } catch {\n return\n }\n\n const hasFts = db\n .prepare(\"SELECT COUNT(*) as cnt FROM sqlite_master WHERE type='table' AND name='message_fts'\")\n .get() as { cnt: number }\n if (!hasFts || hasFts.cnt === 0) return\n\n onProgress?.('更新全文搜索索引...')\n const maxRowid = (db.prepare('SELECT MAX(rowid) as m FROM message_fts').get() as { m: number | null })?.m ?? 0\n const newRows = db\n .prepare(\"SELECT id, content FROM message WHERE type = 0 AND content IS NOT NULL AND content != '' AND id > ?\")\n .all(maxRowid) as Array<{ id: number; content: string }>\n\n if (newRows.length === 0) return\n\n const insertFts = db.prepare('INSERT INTO message_fts(rowid, content) VALUES (?, ?)')\n db.transaction(() => {\n for (const row of newRows) {\n insertFts.run(row.id, row.content)\n }\n })\n}\n\n/**\n * 执行导入(新建或增量)\n */\nexport async function importData(\n dbManager: DatabaseManager,\n data: ParsedData,\n options?: ImportOptions & { onProgress?: (msg: string) => void }\n): Promise<ImportResult> {\n const sessionId = options?.sessionId || generateSessionId()\n const dbPath = dbManager.getDbPath(sessionId)\n const exists = fs.existsSync(dbPath)\n\n if (options?.dryRun) {\n if (exists) {\n const db = dbManager.open(sessionId)\n if (!db)\n return {\n success: false,\n sessionId,\n created: false,\n messageCount: 0,\n memberCount: 0,\n duplicateCount: 0,\n error: 'Failed to open database',\n }\n\n const existingCount = (db.prepare('SELECT COUNT(*) as cnt FROM message').get() as { cnt: number }).cnt\n return {\n success: true,\n sessionId,\n created: false,\n messageCount: data.messages.length,\n memberCount: data.members.length,\n duplicateCount: 0,\n error: `Dry run: ${data.messages.length} messages to import into existing session (${existingCount} existing messages)`,\n }\n }\n return {\n success: true,\n sessionId,\n created: true,\n messageCount: data.messages.length,\n memberCount: data.members.length,\n duplicateCount: 0,\n error: `Dry run: would create new session with ${data.messages.length} messages`,\n }\n }\n\n try {\n if (exists) {\n const db = dbManager.openRawSessionDatabase(sessionId, { readonly: false })\n let result: { messageCount: number; memberCount: number; duplicateCount: number }\n try {\n result = incrementalImport(db, data, options?.onProgress)\n } finally {\n db.close()\n }\n dbManager.raiseCurrentChatDbCompatibilityGate()\n return { success: true, sessionId, created: false, ...result }\n }\n\n const db = dbManager.openRawSessionDatabase(sessionId, { create: true })\n let result: { messageCount: number; memberCount: number; duplicateCount: number }\n try {\n result = fullImport(db, data, options?.onProgress)\n } finally {\n db.close()\n }\n dbManager.raiseCurrentChatDbCompatibilityGate()\n return { success: true, sessionId, created: true, ...result }\n } catch (err: unknown) {\n const message = err instanceof Error ? err.message : String(err)\n if (!exists) {\n dbManager.deleteSessionDatabaseFiles(sessionId)\n }\n return {\n success: false,\n sessionId,\n created: false,\n messageCount: 0,\n memberCount: 0,\n duplicateCount: 0,\n error: message,\n }\n }\n}\n"],"mappings":"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","names":["fs","db","result"]}
package/dist/cli.mjs CHANGED
@@ -6,9 +6,10 @@ const __filename = __fileURLToPath(import.meta.url);
6
6
  const __dirname = __pathDirname(__filename);
7
7
  import {
8
8
  run
9
- } from "./chunk-CIBTUWAE.mjs";
9
+ } from "./chunk-6G7AIF7I.mjs";
10
+ import "./chunk-UB4XBM4R.mjs";
10
11
  import "./chunk-5ADKYYNA.mjs";
11
- import "./chunk-4OMV4VNH.mjs";
12
+ import "./chunk-QY67NN65.mjs";
12
13
  import "./chunk-FJ35V3TE.mjs";
13
14
  import "./chunk-VSOWNKPA.mjs";
14
15
  export {
@@ -9,12 +9,14 @@ import {
9
9
  registerWebRoutes,
10
10
  startHttpServer,
11
11
  stopHttpServer
12
- } from "./chunk-FSHVDWI2.mjs";
13
- import "./chunk-TCPXAX3F.mjs";
14
- import "./chunk-GNT5G74Z.mjs";
15
- import "./chunk-E65ABM3C.mjs";
12
+ } from "./chunk-SJIOPSMM.mjs";
13
+ import "./chunk-WXC6RZCX.mjs";
14
+ import "./chunk-TC74HESZ.mjs";
15
+ import "./chunk-UB4XBM4R.mjs";
16
16
  import "./chunk-5ADKYYNA.mjs";
17
- import "./chunk-4OMV4VNH.mjs";
17
+ import "./chunk-ILGSSXOG.mjs";
18
+ import "./chunk-IRZRIHIT.mjs";
19
+ import "./chunk-QY67NN65.mjs";
18
20
  import "./chunk-FJ35V3TE.mjs";
19
21
  import "./chunk-VSOWNKPA.mjs";
20
22
  export {
@@ -23,4 +25,4 @@ export {
23
25
  startHttpServer,
24
26
  stopHttpServer
25
27
  };
26
- //# sourceMappingURL=http-PMNZ42EN.mjs.map
28
+ //# sourceMappingURL=http-J3IOFFIA.mjs.map
@@ -8,13 +8,13 @@ import {
8
8
  detectFormat as detectFormat2,
9
9
  importData,
10
10
  parseFile
11
- } from "./chunk-TCPXAX3F.mjs";
11
+ } from "./chunk-WXC6RZCX.mjs";
12
12
  import {
13
13
  analyzeIncrementalImport,
14
14
  analyzeNewImport,
15
15
  incrementalImport,
16
16
  streamImport
17
- } from "./chunk-GNT5G74Z.mjs";
17
+ } from "./chunk-TC74HESZ.mjs";
18
18
  import {
19
19
  detectAllFormats,
20
20
  detectFormat,
@@ -22,7 +22,7 @@ import {
22
22
  getFormatFeatureById,
23
23
  getSupportedFormats,
24
24
  scanMultiChatFile
25
- } from "./chunk-4OMV4VNH.mjs";
25
+ } from "./chunk-QY67NN65.mjs";
26
26
  import "./chunk-VSOWNKPA.mjs";
27
27
  export {
28
28
  analyzeIncrementalImport,
@@ -39,4 +39,4 @@ export {
39
39
  scanMultiChatFile,
40
40
  streamImport
41
41
  };
42
- //# sourceMappingURL=import-OP7R4CYY.mjs.map
42
+ //# sourceMappingURL=import-POYG5LOX.mjs.map
package/dist/index.mjs CHANGED
@@ -4,18 +4,20 @@ import { fileURLToPath as __fileURLToPath } from 'url';
4
4
  const require = __createRequire(import.meta.url);
5
5
  const __filename = __fileURLToPath(import.meta.url);
6
6
  const __dirname = __pathDirname(__filename);
7
- import {
8
- run
9
- } from "./chunk-CIBTUWAE.mjs";
10
7
  import {
11
8
  startHttpServer,
12
9
  stopHttpServer
13
- } from "./chunk-FSHVDWI2.mjs";
14
- import "./chunk-TCPXAX3F.mjs";
15
- import "./chunk-GNT5G74Z.mjs";
16
- import "./chunk-E65ABM3C.mjs";
10
+ } from "./chunk-SJIOPSMM.mjs";
11
+ import {
12
+ run
13
+ } from "./chunk-6G7AIF7I.mjs";
14
+ import "./chunk-WXC6RZCX.mjs";
15
+ import "./chunk-TC74HESZ.mjs";
16
+ import "./chunk-UB4XBM4R.mjs";
17
17
  import "./chunk-5ADKYYNA.mjs";
18
- import "./chunk-4OMV4VNH.mjs";
18
+ import "./chunk-ILGSSXOG.mjs";
19
+ import "./chunk-IRZRIHIT.mjs";
20
+ import "./chunk-QY67NN65.mjs";
19
21
  import "./chunk-FJ35V3TE.mjs";
20
22
  import "./chunk-VSOWNKPA.mjs";
21
23
  export {
@@ -5,12 +5,15 @@ const require = __createRequire(import.meta.url);
5
5
  const __filename = __fileURLToPath(import.meta.url);
6
6
  const __dirname = __pathDirname(__filename);
7
7
  import {
8
- CoreDataProvider,
9
- MCP_TOOL_REGISTRY
10
- } from "./chunk-E65ABM3C.mjs";
8
+ assertCliDataDirCompatible
9
+ } from "./chunk-UB4XBM4R.mjs";
11
10
  import {
12
11
  getVersion
13
12
  } from "./chunk-5ADKYYNA.mjs";
13
+ import {
14
+ CoreDataProvider,
15
+ MCP_TOOL_REGISTRY
16
+ } from "./chunk-IRZRIHIT.mjs";
14
17
  import {
15
18
  DatabaseManager,
16
19
  NodePathProvider,
@@ -20,7 +23,7 @@ import {
20
23
  hasPendingElectronDataWarning,
21
24
  loadConfig,
22
25
  verifyCliDataPath
23
- } from "./chunk-4OMV4VNH.mjs";
26
+ } from "./chunk-QY67NN65.mjs";
24
27
  import {
25
28
  resolveCliPath
26
29
  } from "./chunk-FJ35V3TE.mjs";
@@ -390,19 +393,21 @@ function initMcpRuntime() {
390
393
  const userDataDir = config.data.user_data_dir || void 0;
391
394
  const pathProvider = new NodePathProvider(userDataDir);
392
395
  pathProvider.ensureAllDirs();
396
+ const runtime = assertCliDataDirCompatible(pathProvider, "mcp");
393
397
  if (hasPendingElectronDataWarning() || !verifyCliDataPath(pathProvider.getDatabaseDir())) {
394
398
  console.error("[MCP] Electron desktop data detected but databases not found.");
395
399
  console.error("[MCP] Set CHATLAB_DATA_DIR or edit ~/.chatlab/config.toml to point to your data directory.");
396
400
  process.exit(1);
397
401
  }
398
402
  const nativeBinding = resolveNativeBinding();
399
- return new DatabaseManager(pathProvider, { nativeBinding });
403
+ return new DatabaseManager(pathProvider, { nativeBinding, runtime });
400
404
  }
401
405
  async function startCliMcpServer() {
402
406
  const dbManager = initMcpRuntime();
403
407
  await startMcpServer({ version: getVersion(), dbManager });
404
408
  }
405
409
  export {
410
+ initMcpRuntime,
406
411
  startCliMcpServer
407
412
  };
408
- //# sourceMappingURL=mcp-ABXNFUJO.mjs.map
413
+ //# sourceMappingURL=mcp-7HH7UMXT.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/mcp.ts","../../../packages/mcp-server/src/server.ts","../../../packages/mcp-server/src/schema.ts","../../../packages/mcp-server/src/format.ts"],"sourcesContent":["/**\n * CLI MCP Server entry point\n *\n * Thin wrapper: initializes Node.js runtime, then delegates to chatlab-mcp.\n */\n\nimport fs from 'fs'\nimport { loadConfig } from '@openchatlab/config'\nimport {\n NodePathProvider,\n DatabaseManager,\n hasPendingElectronDataWarning,\n verifyCliDataPath,\n} from '@openchatlab/node-runtime'\nimport { startMcpServer } from 'chatlab-mcp'\nimport { getVersion } from './version'\nimport { resolveCliPath } from './paths'\nimport { assertCliDataDirCompatible } from './runtime-compat'\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\nexport function initMcpRuntime(): DatabaseManager {\n const config = loadConfig()\n const userDataDir = config.data.user_data_dir || undefined\n const pathProvider = new NodePathProvider(userDataDir)\n pathProvider.ensureAllDirs()\n const runtime = assertCliDataDirCompatible(pathProvider, 'mcp')\n\n if (hasPendingElectronDataWarning() || !verifyCliDataPath(pathProvider.getDatabaseDir())) {\n console.error('[MCP] Electron desktop data detected but databases not found.')\n console.error('[MCP] Set CHATLAB_DATA_DIR or edit ~/.chatlab/config.toml to point to your data directory.')\n process.exit(1)\n }\n\n const nativeBinding = resolveNativeBinding()\n return new DatabaseManager(pathProvider, { nativeBinding, runtime })\n}\n\nexport async function startCliMcpServer(): Promise<void> {\n const dbManager = initMcpRuntime()\n await startMcpServer({ version: getVersion(), dbManager })\n}\n","/**\n * ChatLab MCP Server core\n *\n * Registers @openchatlab/tools as MCP tools and exposes session data as MCP resources.\n * Communicates with AI agents (ClaudeCode, Cursor, etc.) via stdio transport.\n */\n\nimport { McpServer, ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'\nimport { z } from 'zod'\nimport { getSessionMeta, getSessionOverview, getDatabaseSchema } from '@openchatlab/core'\nimport { MCP_TOOL_REGISTRY, CoreDataProvider } from '@openchatlab/tools'\nimport type { SessionListContext } from '@openchatlab/tools/src/definitions/sessions'\nimport type { McpDatabaseManager, McpServerOptions } from './types'\nimport { jsonSchemaToZod } from './schema'\nimport { formatToolResultAsText } from './format'\n\nconst MCP_TOOL_PREFIX = 'chatlab_'\n\nconst FORMAT_PARAM = z.enum(['text', 'json']).optional().describe('text (default) or json')\n\n/** Tools where JSON is a better default (raw SQL results, schema) */\nconst JSON_DEFAULT_TOOLS = new Set(['execute_sql', 'get_schema'])\n\nfunction applyFormat(content: string, format: string | undefined, toolName: string): string {\n const effectiveFormat = format ?? (JSON_DEFAULT_TOOLS.has(toolName) ? 'json' : 'text')\n if (effectiveFormat === 'json') return content\n\n const textResult = formatToolResultAsText(content)\n return textResult ?? content\n}\n\nfunction registerTools(server: McpServer, dbManager: McpDatabaseManager): void {\n for (const tool of MCP_TOOL_REGISTRY) {\n const mcpName = `${MCP_TOOL_PREFIX}${tool.name}`\n\n if (tool.name === 'list_sessions') {\n const zodShape = {\n ...jsonSchemaToZod(tool.inputSchema.properties, tool.inputSchema.required),\n format: FORMAT_PARAM,\n }\n\n server.tool(mcpName, tool.description, zodShape, async (params) => {\n const format = params.format as string | undefined\n const context: SessionListContext = {\n db: null as any,\n sessionId: '',\n listSessionIds: () => dbManager.listSessionIds(),\n openDb: (id) => dbManager.open(id),\n }\n const toolParams = { ...params } as Record<string, unknown>\n delete toolParams.format\n const result = await tool.handler(toolParams, context)\n const text = applyFormat(result.content, format, tool.name)\n return { content: [{ type: 'text' as const, text }] }\n })\n continue\n }\n\n const zodShape = {\n session_id: z.string().describe('Session ID'),\n ...jsonSchemaToZod(tool.inputSchema.properties, tool.inputSchema.required),\n format: FORMAT_PARAM,\n }\n\n server.tool(mcpName, tool.description, zodShape, async (params) => {\n const sessionId = params.session_id as string\n const format = params.format as string | undefined\n const db = dbManager.open(sessionId)\n if (!db) {\n return {\n content: [{ type: 'text' as const, text: `Session not found: ${sessionId}` }],\n isError: true,\n }\n }\n\n const toolParams = { ...params } as Record<string, unknown>\n delete toolParams.session_id\n delete toolParams.format\n\n const result = await tool.handler(toolParams, { db, sessionId, dataProvider: new CoreDataProvider(db) })\n const text = applyFormat(result.content, format, tool.name)\n return { content: [{ type: 'text' as const, text }] }\n })\n }\n}\n\nfunction registerResources(server: McpServer, dbManager: McpDatabaseManager): void {\n server.resource('sessions-list', 'chatlab://sessions', { description: '所有已导入的聊天会话列表' }, async () => {\n const sessionIds = dbManager.listSessionIds()\n const sessions = sessionIds\n .map((id) => {\n const db = dbManager.open(id)\n if (!db) return null\n const meta = getSessionMeta(db)\n if (!meta) return null\n return { id, name: meta.name, platform: meta.platform, type: meta.type }\n })\n .filter(Boolean)\n\n return {\n contents: [\n {\n uri: 'chatlab://sessions',\n text: JSON.stringify(sessions, null, 2),\n mimeType: 'application/json',\n },\n ],\n }\n })\n\n server.resource(\n 'session-meta',\n new ResourceTemplate('chatlab://sessions/{sessionId}/meta', { list: undefined }),\n { description: '会话元信息(名称、平台、消息数等)' },\n async (uri, params) => {\n const sessionId = params.sessionId as string\n const db = dbManager.open(sessionId)\n if (!db) {\n return { contents: [{ uri: uri.href, text: '{\"error\": \"Session not found\"}', mimeType: 'application/json' }] }\n }\n\n const meta = getSessionMeta(db)\n const overview = getSessionOverview(db)\n\n return {\n contents: [\n {\n uri: uri.href,\n text: JSON.stringify({ ...meta, ...overview }, null, 2),\n mimeType: 'application/json',\n },\n ],\n }\n }\n )\n\n server.resource(\n 'session-schema',\n new ResourceTemplate('chatlab://sessions/{sessionId}/schema', { list: undefined }),\n { description: '会话数据库的表结构' },\n async (uri, params) => {\n const sessionId = params.sessionId as string\n const db = dbManager.open(sessionId)\n if (!db) {\n return { contents: [{ uri: uri.href, text: '{\"error\": \"Session not found\"}', mimeType: 'application/json' }] }\n }\n\n const schema = getDatabaseSchema(db)\n\n return {\n contents: [\n {\n uri: uri.href,\n text: JSON.stringify(schema, null, 2),\n mimeType: 'application/json',\n },\n ],\n }\n }\n )\n}\n\nexport async function startMcpServer(options: McpServerOptions): Promise<void> {\n const { version, name = 'chatlab', dbManager } = options\n\n const server = new McpServer({ name, version })\n\n registerTools(server, dbManager)\n registerResources(server, dbManager)\n\n const transport = new StdioServerTransport()\n await server.connect(transport)\n}\n","/**\n * JSON Schema → Zod conversion for MCP tool registration\n */\n\nimport { z } from 'zod'\n\n/**\n * Convert simple JSON Schema properties to a Zod shape object.\n * Supports string / number / boolean / enum types.\n */\nexport function jsonSchemaToZod(\n properties: Record<string, { type: string; description?: string; default?: unknown; enum?: unknown[] }>,\n required?: string[]\n): Record<string, z.ZodTypeAny> {\n const shape: Record<string, z.ZodTypeAny> = {}\n const requiredSet = new Set(required ?? [])\n\n for (const [key, prop] of Object.entries(properties)) {\n let zodType: z.ZodTypeAny\n\n switch (prop.type) {\n case 'number':\n zodType = z.number().describe(prop.description ?? '')\n break\n case 'boolean':\n zodType = z.boolean().describe(prop.description ?? '')\n break\n case 'string':\n default:\n if (prop.enum) {\n zodType = z.enum(prop.enum as [string, ...string[]]).describe(prop.description ?? '')\n } else {\n zodType = z.string().describe(prop.description ?? '')\n }\n break\n }\n\n if (!requiredSet.has(key)) {\n zodType = zodType.optional()\n }\n\n shape[key] = zodType\n }\n\n return shape\n}\n","/**\n * MCP output formatting for token-efficient LLM consumption\n *\n * Provides message filtering, merging, truncation, and compact text output.\n * Inspired by community chatlab-mcp-server's format.ts.\n */\n\nconst MAX_CONTENT_LENGTH = 200\nconst MAX_MERGED_CONTENT_LENGTH = 400\n\nconst PLACEHOLDER_CONTENTS = new Set([\n '[图片]',\n '[语音]',\n '[视频]',\n '[文件]',\n '[表情]',\n '[动画表情]',\n '[位置]',\n '[名片]',\n '[红包]',\n '[转账]',\n '[撤回消息]',\n '[分享]',\n '[image]',\n '[voice]',\n '[video]',\n '[file]',\n '[sticker]',\n '[animated sticker]',\n '[location]',\n '[contact]',\n '[red packet]',\n '[transfer]',\n '[recalled message]',\n '[photo]',\n '[audio]',\n '[gif]',\n '[share]',\n])\n\nconst MEANINGLESS_SHORT_EN = new Set([\n 'ok',\n 'k',\n 'yes',\n 'no',\n 'ya',\n 'yep',\n 'nope',\n 'lol',\n 'haha',\n 'hehe',\n 'hmm',\n 'ah',\n 'oh',\n 'wow',\n 'thx',\n 'ty',\n 'np',\n 'gg',\n 'brb',\n 'idk',\n])\n\nconst MEANINGFUL_SHORT_ZH = new Set(['好的', '不是', '是的', '可以', '不行', '好吧', '明白', '知道', '同意'])\n\nconst SYSTEM_PATTERNS_ZH = [/^.*邀请.*加入了群聊$/, /^.*退出了群聊$/, /^.*撤回了一条消息$/, /^你撤回了一条消息$/]\n\nconst SYSTEM_PATTERNS_EN = [\n /^.*invited.*to the group$/i,\n /^.*left the group$/i,\n /^.*recalled a message$/i,\n /^.*joined the group$/i,\n /^.*has been removed$/i,\n]\n\nconst EMOJI_ONLY = /^[\\p{Emoji}\\s[\\]()()]+$/u\n\ninterface FormattableMessage {\n senderName: string\n content: string | null\n timestamp: number\n}\n\n/**\n * Check if message content is meaningful (not noise).\n */\nexport function isValidMessage(content: string): boolean {\n const trimmed = content.trim()\n if (!trimmed) return false\n\n if (trimmed.length <= 2 && !MEANINGFUL_SHORT_ZH.has(trimmed)) return false\n if (MEANINGLESS_SHORT_EN.has(trimmed.toLowerCase())) return false\n if (EMOJI_ONLY.test(trimmed)) return false\n if (PLACEHOLDER_CONTENTS.has(trimmed.toLowerCase())) return false\n if (SYSTEM_PATTERNS_ZH.some((p) => p.test(trimmed))) return false\n if (SYSTEM_PATTERNS_EN.some((p) => p.test(trimmed))) return false\n\n return true\n}\n\nfunction truncate(text: string, maxLen: number): string {\n return text.length <= maxLen ? text : text.slice(0, maxLen) + '...'\n}\n\nfunction formatTimestamp(ts: number): string {\n const d = new Date(ts * 1000)\n const M = d.getMonth() + 1\n const D = d.getDate()\n const h = String(d.getHours()).padStart(2, '0')\n const m = String(d.getMinutes()).padStart(2, '0')\n return `${M}/${D} ${h}:${m}`\n}\n\n/**\n * Format messages as compact plain text with consecutive-sender merging.\n * Filters out noise messages, merges same-sender runs, truncates long content.\n */\nexport function formatMessagesCompact(messages: FormattableMessage[]): string {\n if (messages.length === 0) return ''\n\n const valid = messages.filter((m) => m.content && isValidMessage(m.content))\n if (valid.length === 0) return ''\n\n const lines: string[] = []\n let prevSender = ''\n let pendingContents: string[] = []\n let pendingTs = 0\n\n const flush = () => {\n if (pendingContents.length === 0) return\n const combined = pendingContents.join('; ')\n const maxLen = pendingContents.length > 1 ? MAX_MERGED_CONTENT_LENGTH : MAX_CONTENT_LENGTH\n lines.push(`${formatTimestamp(pendingTs)} ${prevSender}: ${truncate(combined, maxLen)}`)\n }\n\n for (const msg of valid) {\n const content = msg.content!.trim()\n if (msg.senderName === prevSender) {\n pendingContents.push(content)\n } else {\n flush()\n prevSender = msg.senderName\n pendingContents = [content]\n pendingTs = msg.timestamp\n }\n }\n flush()\n\n return lines.join('\\n')\n}\n\n/**\n * Try to parse a tool result's JSON content and convert to compact text.\n * Returns null if the content is not suitable for text formatting.\n */\nexport function formatToolResultAsText(content: string): string | null {\n let parsed: unknown\n try {\n parsed = JSON.parse(content)\n } catch {\n return null\n }\n\n if (!parsed || typeof parsed !== 'object') return null\n const obj = parsed as Record<string, unknown>\n\n // If there's a messages array with senderName/content/timestamp, format it\n if (Array.isArray(obj.messages) && obj.messages.length > 0) {\n const msgs = obj.messages as FormattableMessage[]\n if (msgs[0] && 'senderName' in msgs[0] && 'timestamp' in msgs[0]) {\n return formatObjectWithMessages(obj, msgs)\n }\n }\n\n // If there are ranking/keywords arrays, format as numbered list\n if (Array.isArray(obj.ranking) || Array.isArray(obj.keywords)) {\n return formatArrayResult(obj)\n }\n\n // If there are sessions, format them compactly\n if (Array.isArray(obj.sessions)) {\n return formatSessionsList(obj)\n }\n\n return null\n}\n\nfunction formatObjectWithMessages(obj: Record<string, unknown>, msgs: FormattableMessage[]): string {\n const lines: string[] = []\n\n for (const [key, value] of Object.entries(obj)) {\n if (key === 'messages') continue\n if (value === undefined || value === null) continue\n if (typeof value === 'object' && !Array.isArray(value)) continue\n if (Array.isArray(value)) continue\n lines.push(`${key}: ${value}`)\n }\n\n const formatted = formatMessagesCompact(msgs)\n if (formatted) {\n if (lines.length > 0) lines.push('')\n lines.push(formatted)\n }\n\n return lines.join('\\n')\n}\n\nfunction formatArrayResult(obj: Record<string, unknown>): string {\n const lines: string[] = []\n\n for (const [key, value] of Object.entries(obj)) {\n if (Array.isArray(value) && value.length > 0 && typeof value[0] === 'string') {\n if (lines.length > 0) lines.push('')\n lines.push(...value)\n } else if (!Array.isArray(value) && value !== undefined && value !== null) {\n lines.push(`${key}: ${value}`)\n }\n }\n\n return lines.join('\\n')\n}\n\nfunction formatSessionsList(obj: Record<string, unknown>): string {\n const sessions = obj.sessions as Array<Record<string, unknown>>\n const total = obj.total ?? sessions.length\n const lines: string[] = [`${total} sessions:`]\n\n for (const s of sessions) {\n const name = s.name ?? s.id\n const platform = s.platform ? ` (${s.platform})` : ''\n const msgCount = s.totalMessages ?? s.messageCount ?? ''\n const members = s.totalMembers ?? s.memberCount ?? ''\n const stats = msgCount ? ` — ${msgCount} msgs, ${members} members` : ''\n lines.push(`- ${name}${platform}${stats} [${s.id}]`)\n }\n\n return lines.join('\\n')\n}\n"],"mappings":"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","names":["z","z","zodShape"]}
@@ -9,7 +9,7 @@ import {
9
9
  analyzeNewImport,
10
10
  incrementalImport,
11
11
  streamImport
12
- } from "./chunk-GNT5G74Z.mjs";
12
+ } from "./chunk-TC74HESZ.mjs";
13
13
  import {
14
14
  detectAllFormats,
15
15
  detectFormat,
@@ -17,7 +17,7 @@ import {
17
17
  getFormatFeatureById,
18
18
  getSupportedFormats,
19
19
  scanMultiChatFile
20
- } from "./chunk-4OMV4VNH.mjs";
20
+ } from "./chunk-QY67NN65.mjs";
21
21
  import "./chunk-VSOWNKPA.mjs";
22
22
  export {
23
23
  analyzeIncrementalImport,
@@ -31,4 +31,4 @@ export {
31
31
  scanMultiChatFile,
32
32
  streamImport
33
33
  };
34
- //# sourceMappingURL=stream-import-L6RJHH4L.mjs.map
34
+ //# sourceMappingURL=stream-import-O3H3FGV2.mjs.map
@@ -1 +1 @@
1
- .writing-vertical[data-v-19a25590]{writing-mode:vertical-rl;text-orientation:mixed}.data-flow-bg[data-v-19a25590]{background:repeating-linear-gradient(0deg,transparent,transparent 20px,currentColor 20px,currentColor 21px);animation:dataFlow-19a25590 20s linear infinite}@keyframes dataFlow-19a25590{0%{transform:translateY(0)}to{transform:translateY(-100px)}}.mention-menu-enter-active[data-v-0262c6da],.mention-menu-leave-active[data-v-0262c6da]{transition:opacity .16s ease,transform .16s ease}.mention-menu-enter-from[data-v-0262c6da],.mention-menu-leave-to[data-v-0262c6da]{transform:translateY(8px);opacity:0}.slash-menu-enter-active[data-v-0853b251],.slash-menu-leave-active[data-v-0853b251]{transition:opacity .16s ease,transform .16s ease}.slash-menu-enter-from[data-v-0853b251],.slash-menu-leave-to[data-v-0853b251]{transform:translateY(8px);opacity:0}.custom-scrollbar[data-v-94ffe515]::-webkit-scrollbar{width:4px}.custom-scrollbar[data-v-94ffe515]::-webkit-scrollbar-track{background:transparent}.custom-scrollbar[data-v-94ffe515]::-webkit-scrollbar-thumb{border-radius:4px;background-color:#9ca3af4d}.dark .custom-scrollbar[data-v-94ffe515]::-webkit-scrollbar-thumb{background-color:#9ca3af26}.slide-fade-enter-active[data-v-d3aed757],.slide-fade-leave-active[data-v-d3aed757]{transition:all .3s ease-out}.slide-fade-enter-from[data-v-d3aed757],.slide-fade-leave-to[data-v-d3aed757]{transform:translate(20px);opacity:0}.slide-up-enter-active[data-v-d3aed757],.slide-up-leave-active[data-v-d3aed757]{transition:all .3s ease-out}.slide-up-enter-from[data-v-d3aed757],.slide-up-leave-to[data-v-d3aed757]{transform:translateY(10px);opacity:0}.fade-up-enter-active[data-v-d3aed757],.fade-up-leave-active[data-v-d3aed757]{transition:opacity .2s ease-out}.fade-up-enter-from[data-v-d3aed757],.fade-up-leave-to[data-v-d3aed757]{opacity:0}.fade-enter-active[data-v-0df46b7b],.fade-leave-active[data-v-0df46b7b]{transition:opacity .15s ease}.fade-enter-from[data-v-0df46b7b],.fade-leave-to[data-v-0df46b7b]{opacity:0}.fade-enter-active[data-v-d712cb76],.fade-leave-active[data-v-d712cb76]{transition:opacity .15s ease}.fade-enter-from[data-v-d712cb76],.fade-leave-to[data-v-d712cb76]{opacity:0}.titlebar-drag-cover[data-v-b3e18446]{position:absolute;left:0;right:0;z-index:30;top:calc(-1 * var(--titlebar-area-height));height:calc(var(--titlebar-area-height) + 1rem);-webkit-app-region:drag}.header-actions[data-v-b3e18446]{-webkit-app-region:drag}.header-actions[data-v-b3e18446] button,.header-actions[data-v-b3e18446] button *,.header-actions[data-v-b3e18446] a,.header-actions[data-v-b3e18446] a *,.header-actions[data-v-b3e18446] [role=button],.header-actions[data-v-b3e18446] [role=button] *{-webkit-app-region:no-drag}
1
+ .writing-vertical[data-v-19a25590]{writing-mode:vertical-rl;text-orientation:mixed}.data-flow-bg[data-v-19a25590]{background:repeating-linear-gradient(0deg,transparent,transparent 20px,currentColor 20px,currentColor 21px);animation:dataFlow-19a25590 20s linear infinite}@keyframes dataFlow-19a25590{0%{transform:translateY(0)}to{transform:translateY(-100px)}}.mention-menu-enter-active[data-v-0262c6da],.mention-menu-leave-active[data-v-0262c6da]{transition:opacity .16s ease,transform .16s ease}.mention-menu-enter-from[data-v-0262c6da],.mention-menu-leave-to[data-v-0262c6da]{transform:translateY(8px);opacity:0}.slash-menu-enter-active[data-v-0853b251],.slash-menu-leave-active[data-v-0853b251]{transition:opacity .16s ease,transform .16s ease}.slash-menu-enter-from[data-v-0853b251],.slash-menu-leave-to[data-v-0853b251]{transform:translateY(8px);opacity:0}.custom-scrollbar[data-v-94ffe515]::-webkit-scrollbar{width:4px}.custom-scrollbar[data-v-94ffe515]::-webkit-scrollbar-track{background:transparent}.custom-scrollbar[data-v-94ffe515]::-webkit-scrollbar-thumb{border-radius:4px;background-color:#9ca3af4d}.dark .custom-scrollbar[data-v-94ffe515]::-webkit-scrollbar-thumb{background-color:#9ca3af26}.slide-fade-enter-active[data-v-33c0c9d6],.slide-fade-leave-active[data-v-33c0c9d6]{transition:all .3s ease-out}.slide-fade-enter-from[data-v-33c0c9d6],.slide-fade-leave-to[data-v-33c0c9d6]{transform:translate(20px);opacity:0}.slide-up-enter-active[data-v-33c0c9d6],.slide-up-leave-active[data-v-33c0c9d6]{transition:all .3s ease-out}.slide-up-enter-from[data-v-33c0c9d6],.slide-up-leave-to[data-v-33c0c9d6]{transform:translateY(10px);opacity:0}.fade-up-enter-active[data-v-33c0c9d6],.fade-up-leave-active[data-v-33c0c9d6]{transition:opacity .2s ease-out}.fade-up-enter-from[data-v-33c0c9d6],.fade-up-leave-to[data-v-33c0c9d6]{opacity:0}.fade-enter-active[data-v-0df46b7b],.fade-leave-active[data-v-0df46b7b]{transition:opacity .15s ease}.fade-enter-from[data-v-0df46b7b],.fade-leave-to[data-v-0df46b7b]{opacity:0}.fade-enter-active[data-v-d712cb76],.fade-leave-active[data-v-d712cb76]{transition:opacity .15s ease}.fade-enter-from[data-v-d712cb76],.fade-leave-to[data-v-d712cb76]{opacity:0}.titlebar-drag-cover[data-v-b3e18446]{position:absolute;left:0;right:0;z-index:30;top:calc(-1 * var(--titlebar-area-height));height:calc(var(--titlebar-area-height) + 1rem);-webkit-app-region:drag}.header-actions[data-v-b3e18446]{-webkit-app-region:drag}.header-actions[data-v-b3e18446] button,.header-actions[data-v-b3e18446] button *,.header-actions[data-v-b3e18446] a,.header-actions[data-v-b3e18446] a *,.header-actions[data-v-b3e18446] [role=button],.header-actions[data-v-b3e18446] [role=button] *{-webkit-app-region:no-drag}