chatlab-cli 0.28.0 → 0.29.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 (120) hide show
  1. package/dist/{chat-command-7WL5NMZ6.mjs → chat-command-JKG3FY5B.mjs} +8 -5
  2. package/dist/{chat-command-7WL5NMZ6.mjs.map → chat-command-JKG3FY5B.mjs.map} +1 -1
  3. package/dist/chunk-2MZ6G4AC.mjs +1339 -0
  4. package/dist/chunk-2MZ6G4AC.mjs.map +1 -0
  5. package/dist/chunk-5FGB42EQ.mjs +7 -0
  6. package/dist/{chunk-WGS33EWH.mjs → chunk-6FPEP2ZX.mjs} +2 -2
  7. package/dist/chunk-7RW4CQAO.mjs +1310 -0
  8. package/dist/chunk-7RW4CQAO.mjs.map +1 -0
  9. package/dist/{chunk-GFRG7TZQ.mjs → chunk-ARZM6HBX.mjs} +17 -15
  10. package/dist/{chunk-GFRG7TZQ.mjs.map → chunk-ARZM6HBX.mjs.map} +1 -1
  11. package/dist/{chunk-VASK54KT.mjs → chunk-CIB23U32.mjs} +2 -2
  12. package/dist/chunk-D4WTE7LV.mjs +526 -0
  13. package/dist/chunk-D4WTE7LV.mjs.map +1 -0
  14. package/dist/{chunk-ZSFVX6FM.mjs → chunk-I35WHWA6.mjs} +7 -21
  15. package/dist/chunk-I35WHWA6.mjs.map +1 -0
  16. package/dist/{chunk-YQ2FJGBT.mjs → chunk-KL2NGIMQ.mjs} +241 -80
  17. package/dist/chunk-KL2NGIMQ.mjs.map +1 -0
  18. package/dist/{chunk-55WR6WKW.mjs → chunk-QQK7MLCR.mjs} +10044 -8354
  19. package/dist/chunk-QQK7MLCR.mjs.map +1 -0
  20. package/dist/{chunk-XNU5X7EV.mjs → chunk-SKUG5XVD.mjs} +10 -8
  21. package/dist/{chunk-XNU5X7EV.mjs.map → chunk-SKUG5XVD.mjs.map} +1 -1
  22. package/dist/chunk-WPI6YSAR.mjs +8813 -0
  23. package/dist/chunk-WPI6YSAR.mjs.map +1 -0
  24. package/dist/cli.mjs +7 -4
  25. package/dist/contacts-worker.mjs +48 -0
  26. package/dist/contacts-worker.mjs.map +1 -0
  27. package/dist/{http-C2FBHNZG.mjs → http-5E7ARYFW.mjs} +12 -9
  28. package/dist/{import-ED6JCPSJ.mjs → import-3KG6VK6O.mjs} +8 -12
  29. package/dist/index.mjs +12 -9
  30. package/dist/{mcp-5JIBER4M.mjs → mcp-JBI4CUQT.mjs} +8 -5
  31. package/dist/{mcp-5JIBER4M.mjs.map → mcp-JBI4CUQT.mjs.map} +1 -1
  32. package/dist/people-relationships-worker.mjs +48 -0
  33. package/dist/people-relationships-worker.mjs.map +1 -0
  34. package/dist/semantic-index-worker.mjs +1 -1
  35. package/dist/{src-WBNM42SL.mjs → src-UTBQAOJ3.mjs} +86 -66
  36. package/dist/{stream-import-37U3SROO.mjs → stream-import-7HZ4IRYJ.mjs} +7 -4
  37. package/dist/stream-import-7HZ4IRYJ.mjs.map +1 -0
  38. package/dist/undici-ZSXBT7EA.mjs +18389 -0
  39. package/dist/undici-ZSXBT7EA.mjs.map +1 -0
  40. package/dist/{worker-runtime-6DGYL4TF.mjs → worker-runtime-ALS4MR2A.mjs} +13 -5
  41. package/dist/worker-runtime-ALS4MR2A.mjs.map +1 -0
  42. package/dist-web/assets/BufferResource-MsqP5sq3.js +185 -0
  43. package/dist-web/assets/CanvasRenderer-BKCvcc2p.js +1 -0
  44. package/dist-web/assets/DebugToolsPanel-D_lFIW6j.css +1 -0
  45. package/dist-web/assets/DebugToolsPanel.vue_vue_type_script_setup_true_lang-HGJb0Et1.js +110 -0
  46. package/dist-web/assets/{EChartWordcloud-B_YisOW5.js → EChartWordcloud-Clg60FMo.js} +1 -1
  47. package/dist-web/assets/Filter-wRJtdXx9.js +1 -0
  48. package/dist-web/assets/MessageExportModal-BCrMf5Kw.js +1 -0
  49. package/dist-web/assets/PageHeader-Bbn87QZS.css +1 -0
  50. package/dist-web/assets/PageHeader-C_yw54le.js +1 -0
  51. package/dist-web/assets/RelationshipGalaxyCanvas-DTzJq7q7.js +244 -0
  52. package/dist-web/assets/RelationshipGalaxyThreeCanvas-BluPHjCR.css +1 -0
  53. package/dist-web/assets/RelationshipGalaxyThreeCanvas-DIE-0_iM.js +4108 -0
  54. package/dist-web/assets/RenderTargetSystem-BSAQFamI.js +184 -0
  55. package/dist-web/assets/WebGLRenderer-lfzmCZNT.js +156 -0
  56. package/dist-web/assets/WebGPURenderer-BnEdfdr_.js +41 -0
  57. package/dist-web/assets/browserAll-gRNfDyPu.js +14 -0
  58. package/dist-web/assets/{electron-DjminAKK.js → electron-BHaEaaZP.js} +1 -1
  59. package/dist-web/assets/{fetch-B_kLRm6-.js → fetch-BW3aO7PS.js} +1 -1
  60. package/dist-web/assets/fetch-BwF5hNuF.js +1 -0
  61. package/dist-web/assets/{fetch-DV8A1dGB.js → fetch-C1fI_KOX.js} +1 -1
  62. package/dist-web/assets/{fetch-uM3erGZm.js → fetch-CBZbgZqk.js} +1 -1
  63. package/dist-web/assets/{fetch-Wad5mWVH.js → fetch-CFRoSIAn.js} +1 -1
  64. package/dist-web/assets/fetch-CN91TIwV.js +1 -0
  65. package/dist-web/assets/{fetch-DOhCQ5J8.js → fetch-CQt-gb-f.js} +1 -1
  66. package/dist-web/assets/{fetch-Cle6QKmL.js → fetch-DPCJ-1l-.js} +1 -1
  67. package/dist-web/assets/{fetch-r83wdzHy.js → fetch-gMB4wuBd.js} +1 -1
  68. package/dist-web/assets/{fetch-srCJzcWc.js → fetch-mFlEBZyB.js} +1 -1
  69. package/dist-web/assets/index---rXG1hZ.css +1 -0
  70. package/dist-web/assets/index--6C0sK6a.css +1 -0
  71. package/dist-web/assets/index-B6wEGata.js +173 -0
  72. package/dist-web/assets/index-Bg4GGPXv.js +1 -0
  73. package/dist-web/assets/index-BgVbj5qX.js +12 -0
  74. package/dist-web/assets/index-BkoXaBV_.js +64 -0
  75. package/dist-web/assets/index-C56IuV5U.css +1 -0
  76. package/dist-web/assets/index-CLRaKwpT.js +2 -0
  77. package/dist-web/assets/index-Dj1BSD7O.js +1 -0
  78. package/dist-web/assets/index-DosG7LmJ.css +1 -0
  79. package/dist-web/assets/index-DveHdPfK.js +1 -0
  80. package/dist-web/assets/{index-BX3tqfLz.css → index-MBRD3wPd.css} +1 -1
  81. package/dist-web/assets/index-RoDgJcHU.js +2 -0
  82. package/dist-web/assets/{index-tkbEq8av.js → index-ZdCB7xcf.js} +4 -4
  83. package/dist-web/assets/index-aljYO5ms.js +1 -0
  84. package/dist-web/assets/people-page-header-Mupn-U_b.js +1 -0
  85. package/dist-web/assets/relationship-galaxy-viewport-Cwfa51ER.js +1 -0
  86. package/dist-web/assets/{vendor-echarts-CITQeskI.js → vendor-echarts-hJVnODyU.js} +2 -2
  87. package/dist-web/assets/{vendor-echarts-wordcloud-B1kQMklB.js → vendor-echarts-wordcloud-k5ih2Jl4.js} +1 -1
  88. package/dist-web/assets/{vendor-nuxt-ui-Zy8txNdC.js → vendor-nuxt-ui-Dr2T8_OZ.js} +2 -2
  89. package/dist-web/assets/{vendor-reka-ui-mMvma1EP.js → vendor-reka-ui-CzM4kDQ7.js} +1 -1
  90. package/dist-web/assets/{vendor-zrender-DFl4U2Oe.js → vendor-zrender-5DcYjSZk.js} +1 -1
  91. package/dist-web/assets/{web-D27K6zSX.js → web-BakeV58u.js} +1 -1
  92. package/dist-web/assets/webworkerAll-D_6PgURS.js +83 -0
  93. package/dist-web/index.html +4 -4
  94. package/package.json +1 -1
  95. package/dist/chunk-55WR6WKW.mjs.map +0 -1
  96. package/dist/chunk-GPF34WXV.mjs +0 -9573
  97. package/dist/chunk-GPF34WXV.mjs.map +0 -1
  98. package/dist/chunk-YQ2FJGBT.mjs.map +0 -1
  99. package/dist/chunk-YZDEZ6UI.mjs +0 -389
  100. package/dist/chunk-YZDEZ6UI.mjs.map +0 -1
  101. package/dist/chunk-ZSFVX6FM.mjs.map +0 -1
  102. package/dist/worker-runtime-6DGYL4TF.mjs.map +0 -1
  103. package/dist-web/assets/DebugToolsPanel-DFw6_G5V.css +0 -1
  104. package/dist-web/assets/DebugToolsPanel.vue_vue_type_script_setup_true_lang-CCz0Syxo.js +0 -110
  105. package/dist-web/assets/MessageExportModal-Dd6R-cX_.js +0 -1
  106. package/dist-web/assets/fetch-CURbMNJV.js +0 -1
  107. package/dist-web/assets/fetch-DFCYOQe5.js +0 -1
  108. package/dist-web/assets/index-7E5GSvR2.css +0 -1
  109. package/dist-web/assets/index-BCRHSc1k.js +0 -173
  110. package/dist-web/assets/index-BIvLzdwg.js +0 -1
  111. package/dist-web/assets/index-BpszRZ8V.js +0 -64
  112. package/dist-web/assets/index-D5UgZUpS.js +0 -12
  113. package/dist-web/assets/index-I3SWZjnW.css +0 -1
  114. package/dist-web/assets/index-hOsKRsp6.js +0 -2
  115. /package/dist/{http-C2FBHNZG.mjs.map → chunk-5FGB42EQ.mjs.map} +0 -0
  116. /package/dist/{chunk-WGS33EWH.mjs.map → chunk-6FPEP2ZX.mjs.map} +0 -0
  117. /package/dist/{chunk-VASK54KT.mjs.map → chunk-CIB23U32.mjs.map} +0 -0
  118. /package/dist/{import-ED6JCPSJ.mjs.map → http-5E7ARYFW.mjs.map} +0 -0
  119. /package/dist/{src-WBNM42SL.mjs.map → import-3KG6VK6O.mjs.map} +0 -0
  120. /package/dist/{stream-import-37U3SROO.mjs.map → src-UTBQAOJ3.mjs.map} +0 -0
@@ -1,389 +0,0 @@
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
- writeParseResultToDb
9
- } from "./chunk-GPF34WXV.mjs";
10
- import {
11
- CHAT_DB_SCHEMA,
12
- FTS_TABLE_SCHEMA,
13
- buildMemberIdMap,
14
- generateMessageKey
15
- } from "./chunk-55WR6WKW.mjs";
16
-
17
- // src/import/chatlab-reader.ts
18
- import * as fs from "fs";
19
- import * as readline from "readline";
20
- function detectFormat(filePath) {
21
- const ext = filePath.toLowerCase();
22
- if (ext.endsWith(".jsonl")) {
23
- const head = fs.readFileSync(filePath, { encoding: "utf-8", flag: "r" }).slice(0, 1024);
24
- if (head.includes('"_type"') && head.includes('"chatlab"')) {
25
- return "chatlab-jsonl";
26
- }
27
- return null;
28
- }
29
- if (ext.endsWith(".json")) {
30
- const head = fs.readFileSync(filePath, { encoding: "utf-8", flag: "r" }).slice(0, 4096);
31
- if (/"chatlab"\s*:\s*\{/.test(head)) {
32
- return "chatlab-json";
33
- }
34
- return null;
35
- }
36
- return null;
37
- }
38
- async function parseChatLabJson(filePath, onProgress) {
39
- const raw = fs.readFileSync(filePath, "utf-8");
40
- const data = JSON.parse(raw);
41
- if (!data.chatlab || typeof data.chatlab !== "object") {
42
- throw new Error('Invalid ChatLab JSON: missing "chatlab" field');
43
- }
44
- const meta = {
45
- name: data.meta?.name || "\u672A\u77E5",
46
- platform: data.meta?.platform || "unknown",
47
- type: data.meta?.type || "group",
48
- groupId: data.meta?.groupId,
49
- groupAvatar: data.meta?.groupAvatar,
50
- ownerId: data.meta?.ownerId
51
- };
52
- const members = (data.members || []).map((m) => ({
53
- platformId: String(m.platformId || ""),
54
- accountName: String(m.accountName || m.platformId || ""),
55
- groupNickname: m.groupNickname ? String(m.groupNickname) : void 0,
56
- avatar: m.avatar ? String(m.avatar) : void 0,
57
- roles: m.roles
58
- }));
59
- const rawMessages = data.messages || [];
60
- const totalMessages = rawMessages.length;
61
- const messages = rawMessages.map((msg, i) => {
62
- if (onProgress && i % 1e4 === 0) {
63
- onProgress(i, totalMessages);
64
- }
65
- return {
66
- platformMessageId: msg.platformMessageId ? String(msg.platformMessageId) : void 0,
67
- senderPlatformId: String(msg.sender || ""),
68
- senderAccountName: String(msg.accountName || msg.sender || ""),
69
- senderGroupNickname: msg.groupNickname ? String(msg.groupNickname) : void 0,
70
- timestamp: Number(msg.timestamp),
71
- type: Number(msg.type ?? 0),
72
- content: msg.content != null ? String(msg.content) : null,
73
- replyToMessageId: msg.replyToMessageId ? String(msg.replyToMessageId) : void 0
74
- };
75
- });
76
- if (onProgress) onProgress(totalMessages, totalMessages);
77
- if (members.length === 0) {
78
- collectMembersFromMessages(messages, members);
79
- }
80
- return { meta, members, messages };
81
- }
82
- async function parseChatLabJsonl(filePath, onProgress) {
83
- const meta = { name: "\u672A\u77E5", platform: "unknown", type: "group" };
84
- const members = [];
85
- const messages = [];
86
- const stat = fs.statSync(filePath);
87
- let bytesRead = 0;
88
- const rl = readline.createInterface({
89
- input: fs.createReadStream(filePath, "utf-8"),
90
- crlfDelay: Infinity
91
- });
92
- for await (const line of rl) {
93
- bytesRead += Buffer.byteLength(line, "utf-8") + 1;
94
- const trimmed = line.trim();
95
- if (!trimmed) continue;
96
- let obj;
97
- try {
98
- obj = JSON.parse(trimmed);
99
- } catch {
100
- continue;
101
- }
102
- const type = obj._type;
103
- if (type === "header" || !type && obj.chatlab) {
104
- const m = obj.meta;
105
- if (m) {
106
- meta.name = String(m.name || "\u672A\u77E5");
107
- meta.platform = String(m.platform || "unknown");
108
- meta.type = String(m.type || "group");
109
- if (m.groupId) meta.groupId = String(m.groupId);
110
- if (m.groupAvatar) meta.groupAvatar = String(m.groupAvatar);
111
- if (m.ownerId) meta.ownerId = String(m.ownerId);
112
- }
113
- } else if (type === "member") {
114
- members.push({
115
- platformId: String(obj.platformId || ""),
116
- accountName: String(obj.accountName || obj.platformId || ""),
117
- groupNickname: obj.groupNickname ? String(obj.groupNickname) : void 0,
118
- avatar: obj.avatar ? String(obj.avatar) : void 0,
119
- roles: obj.roles
120
- });
121
- } else if (type === "message") {
122
- messages.push({
123
- platformMessageId: obj.platformMessageId ? String(obj.platformMessageId) : void 0,
124
- senderPlatformId: String(obj.sender || obj.senderPlatformId || ""),
125
- senderAccountName: String(obj.accountName || obj.senderAccountName || obj.sender || ""),
126
- senderGroupNickname: obj.groupNickname ? String(obj.groupNickname) : obj.senderGroupNickname ? String(obj.senderGroupNickname) : void 0,
127
- timestamp: Number(obj.timestamp),
128
- type: Number(obj.type ?? 0),
129
- content: obj.content != null ? String(obj.content) : null,
130
- replyToMessageId: obj.replyToMessageId ? String(obj.replyToMessageId) : void 0
131
- });
132
- if (onProgress && messages.length % 1e4 === 0) {
133
- onProgress(bytesRead, stat.size);
134
- }
135
- }
136
- }
137
- if (onProgress) onProgress(stat.size, stat.size);
138
- if (members.length === 0) {
139
- collectMembersFromMessages(messages, members);
140
- }
141
- return { meta, members, messages };
142
- }
143
- function collectMembersFromMessages(messages, members) {
144
- const seen = /* @__PURE__ */ new Set();
145
- for (const msg of messages) {
146
- if (!seen.has(msg.senderPlatformId)) {
147
- seen.add(msg.senderPlatformId);
148
- members.push({
149
- platformId: msg.senderPlatformId,
150
- accountName: msg.senderAccountName || msg.senderPlatformId,
151
- groupNickname: msg.senderGroupNickname
152
- });
153
- }
154
- }
155
- }
156
- async function parseFile(filePath, onProgress) {
157
- const format = detectFormat(filePath);
158
- if (!format) {
159
- throw new Error(`Unsupported file format: ${filePath}. Only ChatLab JSON (.json) and JSONL (.jsonl) are supported.`);
160
- }
161
- if (format === "chatlab-json") {
162
- return parseChatLabJson(filePath, onProgress);
163
- } else {
164
- return parseChatLabJsonl(filePath, onProgress);
165
- }
166
- }
167
-
168
- // src/import/importer.ts
169
- import * as fs2 from "fs";
170
- import * as crypto from "crypto";
171
- function generateSessionId() {
172
- const ts = Date.now();
173
- const rand = crypto.randomBytes(4).toString("hex");
174
- return `chat_${ts}_${rand}`;
175
- }
176
- function fullImport(db, data, onProgress) {
177
- db.exec(CHAT_DB_SCHEMA);
178
- onProgress?.(`\u5199\u5165 ${data.members.length} \u4E2A\u6210\u5458, ${data.messages.length} \u6761\u6D88\u606F...`);
179
- const stats = writeParseResultToDb(db, data.meta, data.members, data.messages);
180
- buildFts(db, onProgress);
181
- return { messageCount: stats.messageCount, memberCount: stats.memberCount, duplicateCount: stats.skippedCount };
182
- }
183
- function incrementalImport(db, data, onProgress) {
184
- onProgress?.("\u52A0\u8F7D\u53BB\u91CD\u7D22\u5F15...");
185
- const existingKeys = /* @__PURE__ */ new Set();
186
- const existingPmids = db.prepare("SELECT platform_message_id FROM message WHERE platform_message_id IS NOT NULL").all();
187
- for (const row of existingPmids) {
188
- existingKeys.add(`pmid:${row.platform_message_id}`);
189
- }
190
- const existingHashes = db.prepare(
191
- `SELECT msg.ts, m.platform_id, msg.content
192
- FROM message msg
193
- JOIN member m ON msg.sender_id = m.id
194
- WHERE msg.platform_message_id IS NULL`
195
- ).all();
196
- for (const row of existingHashes) {
197
- existingKeys.add(`hash:${generateMessageKey(row.ts, row.platform_id, row.content)}`);
198
- }
199
- onProgress?.(`\u53BB\u91CD\u7D22\u5F15: ${existingKeys.size} \u6761\u5DF2\u6709\u6D88\u606F`);
200
- const upsertMember = db.prepare(
201
- `INSERT INTO member (platform_id, account_name, group_nickname, avatar, roles)
202
- VALUES (?, ?, ?, ?, ?)
203
- ON CONFLICT(platform_id) DO UPDATE SET
204
- account_name = COALESCE(NULLIF(excluded.account_name, ''), account_name),
205
- group_nickname = COALESCE(excluded.group_nickname, group_nickname)`
206
- );
207
- let newMembers = 0;
208
- for (const m of data.members) {
209
- const result = upsertMember.run(
210
- m.platformId,
211
- m.accountName || m.platformId,
212
- m.groupNickname || null,
213
- m.avatar || null,
214
- m.roles ? JSON.stringify(m.roles) : "[]"
215
- );
216
- if (result.changes > 0) newMembers++;
217
- }
218
- const memberIdMap = buildMemberIdMap(db);
219
- const insertMsg = db.prepare(
220
- `INSERT INTO message (sender_id, sender_account_name, sender_group_nickname, ts, type, content, reply_to_message_id, platform_message_id)
221
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)`
222
- );
223
- const insertMemberMinimal = db.prepare(`INSERT OR IGNORE INTO member (platform_id, account_name) VALUES (?, ?)`);
224
- let written = 0;
225
- let duplicates = 0;
226
- const BATCH = 5e3;
227
- for (let i = 0; i < data.messages.length; i += BATCH) {
228
- const batch = data.messages.slice(i, i + BATCH);
229
- db.transaction(() => {
230
- for (const msg of batch) {
231
- if (isDuplicate(msg, existingKeys)) {
232
- duplicates++;
233
- continue;
234
- }
235
- let senderId = memberIdMap.get(msg.senderPlatformId);
236
- if (!senderId) {
237
- insertMemberMinimal.run(msg.senderPlatformId, msg.senderAccountName || msg.senderPlatformId);
238
- senderId = db.prepare("SELECT id FROM member WHERE platform_id = ?").get(msg.senderPlatformId)?.id;
239
- if (senderId) memberIdMap.set(msg.senderPlatformId, senderId);
240
- }
241
- if (!senderId) continue;
242
- insertMsg.run(
243
- senderId,
244
- msg.senderAccountName || null,
245
- msg.senderGroupNickname || null,
246
- msg.timestamp,
247
- msg.type,
248
- msg.content,
249
- msg.replyToMessageId || null,
250
- msg.platformMessageId || null
251
- );
252
- written++;
253
- }
254
- });
255
- if (onProgress && (i + BATCH) % 5e4 < BATCH) {
256
- onProgress(
257
- `\u5DF2\u5904\u7406 ${Math.min(i + BATCH, data.messages.length)} / ${data.messages.length} \u6761\u6D88\u606F (\u65B0\u589E ${written}, \u91CD\u590D ${duplicates})`
258
- );
259
- }
260
- }
261
- if (written > 0) {
262
- insertFtsEntries(db, onProgress);
263
- db.prepare("UPDATE meta SET imported_at = ?").run(Math.floor(Date.now() / 1e3));
264
- }
265
- return { messageCount: written, memberCount: newMembers, duplicateCount: duplicates };
266
- }
267
- function isDuplicate(msg, existingKeys) {
268
- if (msg.platformMessageId) {
269
- return existingKeys.has(`pmid:${msg.platformMessageId}`);
270
- }
271
- const key = generateMessageKey(msg.timestamp, msg.senderPlatformId, msg.content);
272
- return existingKeys.has(`hash:${key}`);
273
- }
274
- function buildFts(db, onProgress) {
275
- onProgress?.("\u521B\u5EFA\u5168\u6587\u641C\u7D22\u7D22\u5F15...");
276
- db.exec(FTS_TABLE_SCHEMA);
277
- const textMessages = db.prepare("SELECT id, content FROM message WHERE type = 0 AND content IS NOT NULL AND content != ''").all();
278
- const insertFts = db.prepare("INSERT INTO message_fts(rowid, content) VALUES (?, ?)");
279
- const BATCH = 5e3;
280
- for (let i = 0; i < textMessages.length; i += BATCH) {
281
- const batch = textMessages.slice(i, i + BATCH);
282
- db.transaction(() => {
283
- for (const row of batch) {
284
- insertFts.run(row.id, row.content);
285
- }
286
- });
287
- }
288
- }
289
- function insertFtsEntries(db, onProgress) {
290
- try {
291
- db.prepare("SELECT 1 FROM sqlite_master WHERE type='table' AND name='message_fts'").get();
292
- } catch {
293
- return;
294
- }
295
- const hasFts = db.prepare("SELECT COUNT(*) as cnt FROM sqlite_master WHERE type='table' AND name='message_fts'").get();
296
- if (!hasFts || hasFts.cnt === 0) return;
297
- onProgress?.("\u66F4\u65B0\u5168\u6587\u641C\u7D22\u7D22\u5F15...");
298
- const maxRowid = db.prepare("SELECT MAX(rowid) as m FROM message_fts").get()?.m ?? 0;
299
- const newRows = db.prepare("SELECT id, content FROM message WHERE type = 0 AND content IS NOT NULL AND content != '' AND id > ?").all(maxRowid);
300
- if (newRows.length === 0) return;
301
- const insertFts = db.prepare("INSERT INTO message_fts(rowid, content) VALUES (?, ?)");
302
- db.transaction(() => {
303
- for (const row of newRows) {
304
- insertFts.run(row.id, row.content);
305
- }
306
- });
307
- }
308
- async function importData(dbManager, data, options) {
309
- const sessionId = options?.sessionId || generateSessionId();
310
- const dbPath = dbManager.getDbPath(sessionId);
311
- const exists = fs2.existsSync(dbPath);
312
- if (options?.dryRun) {
313
- if (exists) {
314
- const db = dbManager.open(sessionId);
315
- if (!db)
316
- return {
317
- success: false,
318
- sessionId,
319
- created: false,
320
- messageCount: 0,
321
- memberCount: 0,
322
- duplicateCount: 0,
323
- error: "Failed to open database"
324
- };
325
- const existingCount = db.prepare("SELECT COUNT(*) as cnt FROM message").get().cnt;
326
- return {
327
- success: true,
328
- sessionId,
329
- created: false,
330
- messageCount: data.messages.length,
331
- memberCount: data.members.length,
332
- duplicateCount: 0,
333
- error: `Dry run: ${data.messages.length} messages to import into existing session (${existingCount} existing messages)`
334
- };
335
- }
336
- return {
337
- success: true,
338
- sessionId,
339
- created: true,
340
- messageCount: data.messages.length,
341
- memberCount: data.members.length,
342
- duplicateCount: 0,
343
- error: `Dry run: would create new session with ${data.messages.length} messages`
344
- };
345
- }
346
- try {
347
- if (exists) {
348
- const db2 = dbManager.openRawSessionDatabase(sessionId, { readonly: false });
349
- let result2;
350
- try {
351
- result2 = incrementalImport(db2, data, options?.onProgress);
352
- } finally {
353
- db2.close();
354
- }
355
- dbManager.raiseCurrentChatDbCompatibilityGate();
356
- return { success: true, sessionId, created: false, ...result2 };
357
- }
358
- const db = dbManager.openRawSessionDatabase(sessionId, { create: true });
359
- let result;
360
- try {
361
- result = fullImport(db, data, options?.onProgress);
362
- } finally {
363
- db.close();
364
- }
365
- dbManager.raiseCurrentChatDbCompatibilityGate();
366
- return { success: true, sessionId, created: true, ...result };
367
- } catch (err) {
368
- const message = err instanceof Error ? err.message : String(err);
369
- if (!exists) {
370
- dbManager.deleteSessionDatabaseFiles(sessionId);
371
- }
372
- return {
373
- success: false,
374
- sessionId,
375
- created: false,
376
- messageCount: 0,
377
- memberCount: 0,
378
- duplicateCount: 0,
379
- error: message
380
- };
381
- }
382
- }
383
-
384
- export {
385
- detectFormat,
386
- parseFile,
387
- importData
388
- };
389
- //# sourceMappingURL=chunk-YZDEZ6UI.mjs.map
@@ -1 +0,0 @@
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"]}
@@ -1 +0,0 @@
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"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../packages/node-runtime/src/semantic-index/worker-runtime.ts"],"sourcesContent":["import type { RuntimeIdentity } from '../data-dir-compat'\nimport { DatabaseManager } from '../database-manager'\nimport { createDatabaseManagerAdapter } from '../services'\nimport { createSemanticIndexService } from './service'\nimport type { LoadSqliteVec } from './store'\nimport { StaticPathProvider, type StaticPathProviderSnapshot } from './static-path-provider'\nimport type { SemanticIndexRuntime } from './runtime'\n\nexport interface SemanticIndexWorkerStartupOptions {\n paths: StaticPathProviderSnapshot\n runtime: RuntimeIdentity\n nativeBinding?: string\n sqliteVecLoadablePath?: string\n}\n\nexport type SemanticIndexWorkerServiceFactory = () => SemanticIndexRuntime\n\nexport interface SemanticIndexWorkerRuntimeOptions {\n serviceFactory: SemanticIndexWorkerServiceFactory\n}\n\nexport class SemanticIndexWorkerRuntime {\n private service: SemanticIndexRuntime | null = null\n private readonly options: SemanticIndexWorkerRuntimeOptions\n\n constructor(options: SemanticIndexWorkerRuntimeOptions) {\n this.options = options\n }\n\n async handleRequest(method: string, args: unknown[]): Promise<unknown> {\n if (method === '__close') {\n await this.close()\n return null\n }\n const service = this.ensureService()\n const target = service as unknown as Record<string, (...args: unknown[]) => unknown>\n const fn = target[method]\n if (typeof fn !== 'function') throw new Error(`Unsupported semantic index worker method: ${method}`)\n return await fn.apply(service, args)\n }\n\n async close(): Promise<void> {\n const service = this.service\n this.service = null\n await service?.close()\n }\n\n private ensureService(): SemanticIndexRuntime {\n if (!this.service) {\n this.service = this.options.serviceFactory()\n void this.service.recover()\n }\n return this.service\n }\n}\n\nexport function createSemanticIndexWorkerRuntime(\n options: SemanticIndexWorkerRuntimeOptions\n): SemanticIndexWorkerRuntime {\n return new SemanticIndexWorkerRuntime(options)\n}\n\nexport function createSemanticIndexWorkerServiceFactory(\n options: SemanticIndexWorkerStartupOptions\n): SemanticIndexWorkerServiceFactory {\n return () => {\n const pathProvider = new StaticPathProvider(options.paths)\n const dbManager = new DatabaseManager(pathProvider, {\n nativeBinding: options.nativeBinding,\n runtime: options.runtime,\n })\n const sessionAdapter = createDatabaseManagerAdapter(dbManager)\n const loadSqliteVec: LoadSqliteVec | undefined = options.sqliteVecLoadablePath\n ? (db) => db.loadExtension(options.sqliteVecLoadablePath!)\n : undefined\n\n return createSemanticIndexService({\n pathProvider,\n sessionAdapter,\n nativeBinding: options.nativeBinding,\n loadSqliteVec,\n })\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;AAqBO,IAAM,6BAAN,MAAiC;AAAA,EAC9B,UAAuC;AAAA,EAC9B;AAAA,EAEjB,YAAY,SAA4C;AACtD,SAAK,UAAU;AAAA,EACjB;AAAA,EAEA,MAAM,cAAc,QAAgB,MAAmC;AACrE,QAAI,WAAW,WAAW;AACxB,YAAM,KAAK,MAAM;AACjB,aAAO;AAAA,IACT;AACA,UAAM,UAAU,KAAK,cAAc;AACnC,UAAM,SAAS;AACf,UAAM,KAAK,OAAO,MAAM;AACxB,QAAI,OAAO,OAAO,WAAY,OAAM,IAAI,MAAM,6CAA6C,MAAM,EAAE;AACnG,WAAO,MAAM,GAAG,MAAM,SAAS,IAAI;AAAA,EACrC;AAAA,EAEA,MAAM,QAAuB;AAC3B,UAAM,UAAU,KAAK;AACrB,SAAK,UAAU;AACf,UAAM,SAAS,MAAM;AAAA,EACvB;AAAA,EAEQ,gBAAsC;AAC5C,QAAI,CAAC,KAAK,SAAS;AACjB,WAAK,UAAU,KAAK,QAAQ,eAAe;AAC3C,WAAK,KAAK,QAAQ,QAAQ;AAAA,IAC5B;AACA,WAAO,KAAK;AAAA,EACd;AACF;AAEO,SAAS,iCACd,SAC4B;AAC5B,SAAO,IAAI,2BAA2B,OAAO;AAC/C;AAEO,SAAS,wCACd,SACmC;AACnC,SAAO,MAAM;AACX,UAAM,eAAe,IAAI,mBAAmB,QAAQ,KAAK;AACzD,UAAM,YAAY,IAAI,gBAAgB,cAAc;AAAA,MAClD,eAAe,QAAQ;AAAA,MACvB,SAAS,QAAQ;AAAA,IACnB,CAAC;AACD,UAAM,iBAAiB,6BAA6B,SAAS;AAC7D,UAAM,gBAA2C,QAAQ,wBACrD,CAAC,OAAO,GAAG,cAAc,QAAQ,qBAAsB,IACvD;AAEJ,WAAO,2BAA2B;AAAA,MAChC;AAAA,MACA;AAAA,MACA,eAAe,QAAQ;AAAA,MACvB;AAAA,IACF,CAAC;AAAA,EACH;AACF;","names":[]}
@@ -1 +0,0 @@
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-9fa5fc54],.slide-fade-leave-active[data-v-9fa5fc54]{transition:all .3s ease-out}.slide-fade-enter-from[data-v-9fa5fc54],.slide-fade-leave-to[data-v-9fa5fc54]{transform:translate(20px);opacity:0}.slide-up-enter-active[data-v-9fa5fc54],.slide-up-leave-active[data-v-9fa5fc54]{transition:all .3s ease-out}.slide-up-enter-from[data-v-9fa5fc54],.slide-up-leave-to[data-v-9fa5fc54]{transform:translateY(10px);opacity:0}.fade-up-enter-active[data-v-9fa5fc54],.fade-up-leave-active[data-v-9fa5fc54]{transition:opacity .2s ease-out}.fade-up-enter-from[data-v-9fa5fc54],.fade-up-leave-to[data-v-9fa5fc54]{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}