chatlab-cli 0.0.0 → 0.21.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 (68) hide show
  1. package/README.md +115 -3
  2. package/bin/chatlab.mjs +2 -0
  3. package/dist/chunk-2AIOU3PW.mjs +26 -0
  4. package/dist/chunk-2AIOU3PW.mjs.map +1 -0
  5. package/dist/chunk-3DIWXYMJ.mjs +1344 -0
  6. package/dist/chunk-3DIWXYMJ.mjs.map +1 -0
  7. package/dist/chunk-EIRLEPXP.mjs +19145 -0
  8. package/dist/chunk-EIRLEPXP.mjs.map +1 -0
  9. package/dist/chunk-EWCSGYVT.mjs +144 -0
  10. package/dist/chunk-EWCSGYVT.mjs.map +1 -0
  11. package/dist/chunk-FBTHZU55.mjs +3400 -0
  12. package/dist/chunk-FBTHZU55.mjs.map +1 -0
  13. package/dist/chunk-NB4BIAYM.mjs +411 -0
  14. package/dist/chunk-NB4BIAYM.mjs.map +1 -0
  15. package/dist/chunk-XUHPCRVT.mjs +395 -0
  16. package/dist/chunk-XUHPCRVT.mjs.map +1 -0
  17. package/dist/cli.mjs +15 -0
  18. package/dist/cli.mjs.map +1 -0
  19. package/dist/http-QVYJ5HER.mjs +32 -0
  20. package/dist/http-QVYJ5HER.mjs.map +1 -0
  21. package/dist/import-AJKL6JPR.mjs +41 -0
  22. package/dist/import-AJKL6JPR.mjs.map +1 -0
  23. package/dist/index.mjs +24 -0
  24. package/dist/index.mjs.map +1 -0
  25. package/dist/mcp-FRF3YAN5.mjs +405 -0
  26. package/dist/mcp-FRF3YAN5.mjs.map +1 -0
  27. package/dist/src-KOIGBZJX.mjs +339 -0
  28. package/dist/src-KOIGBZJX.mjs.map +1 -0
  29. package/dist/stream-import-URGOCXUH.mjs +33 -0
  30. package/dist/stream-import-URGOCXUH.mjs.map +1 -0
  31. package/dist-web/assets/DebugToolsPanel-dUV6pVqD.css +1 -0
  32. package/dist-web/assets/DebugToolsPanel.vue_vue_type_script_setup_true_lang-DFN1Co1H.js +155 -0
  33. package/dist-web/assets/EChartWordcloud-BYLFxPAX.js +1 -0
  34. package/dist-web/assets/MessageExportModal-BAf68-wB.js +1 -0
  35. package/dist-web/assets/electron-2m4Vdk2z.js +1 -0
  36. package/dist-web/assets/electron-8qi0d92j.js +1 -0
  37. package/dist-web/assets/electron-BHjOx6B6.js +1 -0
  38. package/dist-web/assets/electron-CTK4NJag.js +1 -0
  39. package/dist-web/assets/electron-DPsrYwTt.js +1 -0
  40. package/dist-web/assets/electron-DfpXsXsS.js +1 -0
  41. package/dist-web/assets/electron-HoVzpIVc.js +1 -0
  42. package/dist-web/assets/fetch-Ayp337Ng.js +17 -0
  43. package/dist-web/assets/fetch-BS4SlQjA.js +1 -0
  44. package/dist-web/assets/fetch-BWtpQwBZ.js +1 -0
  45. package/dist-web/assets/fetch-CclRWOHf.js +1 -0
  46. package/dist-web/assets/fetch-W43zfemj.js +1 -0
  47. package/dist-web/assets/fetch-o5NJ2uWE.js +3 -0
  48. package/dist-web/assets/http-CeXMt-Fv.js +1 -0
  49. package/dist-web/assets/index-BKFGuwJu.css +1 -0
  50. package/dist-web/assets/index-Bd5r2oFG.js +163 -0
  51. package/dist-web/assets/index-BfZM-q0T.js +7 -0
  52. package/dist-web/assets/index-CLGv8Den.css +1 -0
  53. package/dist-web/assets/index-CLYHD_f4.js +2 -0
  54. package/dist-web/assets/index-CaTRpNBK.js +16 -0
  55. package/dist-web/assets/index-CmU6x-uJ.css +1 -0
  56. package/dist-web/assets/index-CzR1sElx.css +1 -0
  57. package/dist-web/assets/index-Ksa--o23.js +173 -0
  58. package/dist-web/assets/sse-CACcGGWv.js +2 -0
  59. package/dist-web/assets/vendor-echarts-C13vDgZW.js +11 -0
  60. package/dist-web/assets/vendor-echarts-wordcloud-uVdwYsYa.js +1 -0
  61. package/dist-web/assets/vendor-nuxt-ui-CYMNNdeD.js +15 -0
  62. package/dist-web/assets/vendor-reka-ui-CW2cIgMu.js +11 -0
  63. package/dist-web/assets/vendor-snapdom-XnBoh1gd.js +8 -0
  64. package/dist-web/assets/vendor-zrender-5DcYjSZk.js +8 -0
  65. package/dist-web/assets/web-C5yfcwjn.js +1 -0
  66. package/dist-web/index.html +18 -0
  67. package/package.json +70 -15
  68. package/cli.js +0 -3
@@ -0,0 +1,395 @@
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
+ CHAT_DB_SCHEMA,
9
+ FTS_TABLE_SCHEMA,
10
+ buildMemberIdMap,
11
+ generateMessageKey,
12
+ openBetterSqliteDatabase,
13
+ writeParseResultToDb
14
+ } from "./chunk-EIRLEPXP.mjs";
15
+
16
+ // src/import/chatlab-reader.ts
17
+ import * as fs from "fs";
18
+ import * as readline from "readline";
19
+ function detectFormat(filePath) {
20
+ const ext = filePath.toLowerCase();
21
+ if (ext.endsWith(".jsonl")) {
22
+ const head = fs.readFileSync(filePath, { encoding: "utf-8", flag: "r" }).slice(0, 1024);
23
+ if (head.includes('"_type"') && head.includes('"chatlab"')) {
24
+ return "chatlab-jsonl";
25
+ }
26
+ return null;
27
+ }
28
+ if (ext.endsWith(".json")) {
29
+ const head = fs.readFileSync(filePath, { encoding: "utf-8", flag: "r" }).slice(0, 4096);
30
+ if (/"chatlab"\s*:\s*\{/.test(head)) {
31
+ return "chatlab-json";
32
+ }
33
+ return null;
34
+ }
35
+ return null;
36
+ }
37
+ async function parseChatLabJson(filePath, onProgress) {
38
+ const raw = fs.readFileSync(filePath, "utf-8");
39
+ const data = JSON.parse(raw);
40
+ if (!data.chatlab || typeof data.chatlab !== "object") {
41
+ throw new Error('Invalid ChatLab JSON: missing "chatlab" field');
42
+ }
43
+ const meta = {
44
+ name: data.meta?.name || "\u672A\u77E5",
45
+ platform: data.meta?.platform || "unknown",
46
+ type: data.meta?.type || "group",
47
+ groupId: data.meta?.groupId,
48
+ groupAvatar: data.meta?.groupAvatar,
49
+ ownerId: data.meta?.ownerId
50
+ };
51
+ const members = (data.members || []).map((m) => ({
52
+ platformId: String(m.platformId || ""),
53
+ accountName: String(m.accountName || m.platformId || ""),
54
+ groupNickname: m.groupNickname ? String(m.groupNickname) : void 0,
55
+ avatar: m.avatar ? String(m.avatar) : void 0,
56
+ roles: m.roles
57
+ }));
58
+ const rawMessages = data.messages || [];
59
+ const totalMessages = rawMessages.length;
60
+ const messages = rawMessages.map((msg, i) => {
61
+ if (onProgress && i % 1e4 === 0) {
62
+ onProgress(i, totalMessages);
63
+ }
64
+ return {
65
+ platformMessageId: msg.platformMessageId ? String(msg.platformMessageId) : void 0,
66
+ senderPlatformId: String(msg.sender || ""),
67
+ senderAccountName: String(msg.accountName || msg.sender || ""),
68
+ senderGroupNickname: msg.groupNickname ? String(msg.groupNickname) : void 0,
69
+ timestamp: Number(msg.timestamp),
70
+ type: Number(msg.type ?? 0),
71
+ content: msg.content != null ? String(msg.content) : null,
72
+ replyToMessageId: msg.replyToMessageId ? String(msg.replyToMessageId) : void 0
73
+ };
74
+ });
75
+ if (onProgress) onProgress(totalMessages, totalMessages);
76
+ if (members.length === 0) {
77
+ collectMembersFromMessages(messages, members);
78
+ }
79
+ return { meta, members, messages };
80
+ }
81
+ async function parseChatLabJsonl(filePath, onProgress) {
82
+ const meta = { name: "\u672A\u77E5", platform: "unknown", type: "group" };
83
+ const members = [];
84
+ const messages = [];
85
+ const stat = fs.statSync(filePath);
86
+ let bytesRead = 0;
87
+ const rl = readline.createInterface({
88
+ input: fs.createReadStream(filePath, "utf-8"),
89
+ crlfDelay: Infinity
90
+ });
91
+ for await (const line of rl) {
92
+ bytesRead += Buffer.byteLength(line, "utf-8") + 1;
93
+ const trimmed = line.trim();
94
+ if (!trimmed) continue;
95
+ let obj;
96
+ try {
97
+ obj = JSON.parse(trimmed);
98
+ } catch {
99
+ continue;
100
+ }
101
+ const type = obj._type;
102
+ if (type === "header" || !type && obj.chatlab) {
103
+ const m = obj.meta;
104
+ if (m) {
105
+ meta.name = String(m.name || "\u672A\u77E5");
106
+ meta.platform = String(m.platform || "unknown");
107
+ meta.type = String(m.type || "group");
108
+ if (m.groupId) meta.groupId = String(m.groupId);
109
+ if (m.groupAvatar) meta.groupAvatar = String(m.groupAvatar);
110
+ if (m.ownerId) meta.ownerId = String(m.ownerId);
111
+ }
112
+ } else if (type === "member") {
113
+ members.push({
114
+ platformId: String(obj.platformId || ""),
115
+ accountName: String(obj.accountName || obj.platformId || ""),
116
+ groupNickname: obj.groupNickname ? String(obj.groupNickname) : void 0,
117
+ avatar: obj.avatar ? String(obj.avatar) : void 0,
118
+ roles: obj.roles
119
+ });
120
+ } else if (type === "message") {
121
+ messages.push({
122
+ platformMessageId: obj.platformMessageId ? String(obj.platformMessageId) : void 0,
123
+ senderPlatformId: String(obj.sender || obj.senderPlatformId || ""),
124
+ senderAccountName: String(obj.accountName || obj.senderAccountName || obj.sender || ""),
125
+ senderGroupNickname: obj.groupNickname ? String(obj.groupNickname) : obj.senderGroupNickname ? String(obj.senderGroupNickname) : void 0,
126
+ timestamp: Number(obj.timestamp),
127
+ type: Number(obj.type ?? 0),
128
+ content: obj.content != null ? String(obj.content) : null,
129
+ replyToMessageId: obj.replyToMessageId ? String(obj.replyToMessageId) : void 0
130
+ });
131
+ if (onProgress && messages.length % 1e4 === 0) {
132
+ onProgress(bytesRead, stat.size);
133
+ }
134
+ }
135
+ }
136
+ if (onProgress) onProgress(stat.size, stat.size);
137
+ if (members.length === 0) {
138
+ collectMembersFromMessages(messages, members);
139
+ }
140
+ return { meta, members, messages };
141
+ }
142
+ function collectMembersFromMessages(messages, members) {
143
+ const seen = /* @__PURE__ */ new Set();
144
+ for (const msg of messages) {
145
+ if (!seen.has(msg.senderPlatformId)) {
146
+ seen.add(msg.senderPlatformId);
147
+ members.push({
148
+ platformId: msg.senderPlatformId,
149
+ accountName: msg.senderAccountName || msg.senderPlatformId,
150
+ groupNickname: msg.senderGroupNickname
151
+ });
152
+ }
153
+ }
154
+ }
155
+ async function parseFile(filePath, onProgress) {
156
+ const format = detectFormat(filePath);
157
+ if (!format) {
158
+ throw new Error(`Unsupported file format: ${filePath}. Only ChatLab JSON (.json) and JSONL (.jsonl) are supported.`);
159
+ }
160
+ if (format === "chatlab-json") {
161
+ return parseChatLabJson(filePath, onProgress);
162
+ } else {
163
+ return parseChatLabJsonl(filePath, onProgress);
164
+ }
165
+ }
166
+
167
+ // src/import/importer.ts
168
+ import * as fs2 from "fs";
169
+ import * as crypto from "crypto";
170
+ function generateSessionId() {
171
+ const ts = Date.now();
172
+ const rand = crypto.randomBytes(4).toString("hex");
173
+ return `chat_${ts}_${rand}`;
174
+ }
175
+ function fullImport(db, data, onProgress) {
176
+ db.exec(CHAT_DB_SCHEMA);
177
+ onProgress?.(`\u5199\u5165 ${data.members.length} \u4E2A\u6210\u5458, ${data.messages.length} \u6761\u6D88\u606F...`);
178
+ const stats = writeParseResultToDb(db, data.meta, data.members, data.messages);
179
+ buildFts(db, onProgress);
180
+ return { messageCount: stats.messageCount, memberCount: stats.memberCount, duplicateCount: stats.skippedCount };
181
+ }
182
+ function incrementalImport(db, data, onProgress) {
183
+ onProgress?.("\u52A0\u8F7D\u53BB\u91CD\u7D22\u5F15...");
184
+ const existingKeys = /* @__PURE__ */ new Set();
185
+ const existingPmids = db.prepare("SELECT platform_message_id FROM message WHERE platform_message_id IS NOT NULL").all();
186
+ for (const row of existingPmids) {
187
+ existingKeys.add(`pmid:${row.platform_message_id}`);
188
+ }
189
+ const existingHashes = db.prepare(
190
+ `SELECT msg.ts, m.platform_id, msg.content
191
+ FROM message msg
192
+ JOIN member m ON msg.sender_id = m.id
193
+ WHERE msg.platform_message_id IS NULL`
194
+ ).all();
195
+ for (const row of existingHashes) {
196
+ existingKeys.add(`hash:${generateMessageKey(row.ts, row.platform_id, row.content)}`);
197
+ }
198
+ onProgress?.(`\u53BB\u91CD\u7D22\u5F15: ${existingKeys.size} \u6761\u5DF2\u6709\u6D88\u606F`);
199
+ const upsertMember = db.prepare(
200
+ `INSERT INTO member (platform_id, account_name, group_nickname, avatar, roles)
201
+ VALUES (?, ?, ?, ?, ?)
202
+ ON CONFLICT(platform_id) DO UPDATE SET
203
+ account_name = COALESCE(NULLIF(excluded.account_name, ''), account_name),
204
+ group_nickname = COALESCE(excluded.group_nickname, group_nickname)`
205
+ );
206
+ let newMembers = 0;
207
+ for (const m of data.members) {
208
+ const result = upsertMember.run(
209
+ m.platformId,
210
+ m.accountName || m.platformId,
211
+ m.groupNickname || null,
212
+ m.avatar || null,
213
+ m.roles ? JSON.stringify(m.roles) : "[]"
214
+ );
215
+ if (result.changes > 0) newMembers++;
216
+ }
217
+ const memberIdMap = buildMemberIdMap(db);
218
+ const insertMsg = db.prepare(
219
+ `INSERT INTO message (sender_id, sender_account_name, sender_group_nickname, ts, type, content, reply_to_message_id, platform_message_id)
220
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)`
221
+ );
222
+ const insertMemberMinimal = db.prepare(`INSERT OR IGNORE INTO member (platform_id, account_name) VALUES (?, ?)`);
223
+ let written = 0;
224
+ let duplicates = 0;
225
+ const BATCH = 5e3;
226
+ for (let i = 0; i < data.messages.length; i += BATCH) {
227
+ const batch = data.messages.slice(i, i + BATCH);
228
+ db.transaction(() => {
229
+ for (const msg of batch) {
230
+ if (isDuplicate(msg, existingKeys)) {
231
+ duplicates++;
232
+ continue;
233
+ }
234
+ let senderId = memberIdMap.get(msg.senderPlatformId);
235
+ if (!senderId) {
236
+ insertMemberMinimal.run(msg.senderPlatformId, msg.senderAccountName || msg.senderPlatformId);
237
+ senderId = db.prepare("SELECT id FROM member WHERE platform_id = ?").get(msg.senderPlatformId)?.id;
238
+ if (senderId) memberIdMap.set(msg.senderPlatformId, senderId);
239
+ }
240
+ if (!senderId) continue;
241
+ insertMsg.run(
242
+ senderId,
243
+ msg.senderAccountName || null,
244
+ msg.senderGroupNickname || null,
245
+ msg.timestamp,
246
+ msg.type,
247
+ msg.content,
248
+ msg.replyToMessageId || null,
249
+ msg.platformMessageId || null
250
+ );
251
+ written++;
252
+ }
253
+ });
254
+ if (onProgress && (i + BATCH) % 5e4 < BATCH) {
255
+ onProgress(
256
+ `\u5DF2\u5904\u7406 ${Math.min(i + BATCH, data.messages.length)} / ${data.messages.length} \u6761\u6D88\u606F (\u65B0\u589E ${written}, \u91CD\u590D ${duplicates})`
257
+ );
258
+ }
259
+ }
260
+ if (written > 0) {
261
+ insertFtsEntries(db, onProgress);
262
+ db.prepare("UPDATE meta SET imported_at = ?").run(Math.floor(Date.now() / 1e3));
263
+ }
264
+ return { messageCount: written, memberCount: newMembers, duplicateCount: duplicates };
265
+ }
266
+ function isDuplicate(msg, existingKeys) {
267
+ if (msg.platformMessageId) {
268
+ return existingKeys.has(`pmid:${msg.platformMessageId}`);
269
+ }
270
+ const key = generateMessageKey(msg.timestamp, msg.senderPlatformId, msg.content);
271
+ return existingKeys.has(`hash:${key}`);
272
+ }
273
+ function buildFts(db, onProgress) {
274
+ onProgress?.("\u521B\u5EFA\u5168\u6587\u641C\u7D22\u7D22\u5F15...");
275
+ db.exec(FTS_TABLE_SCHEMA);
276
+ const textMessages = db.prepare("SELECT id, content FROM message WHERE type = 0 AND content IS NOT NULL AND content != ''").all();
277
+ const insertFts = db.prepare("INSERT INTO message_fts(rowid, content) VALUES (?, ?)");
278
+ const BATCH = 5e3;
279
+ for (let i = 0; i < textMessages.length; i += BATCH) {
280
+ const batch = textMessages.slice(i, i + BATCH);
281
+ db.transaction(() => {
282
+ for (const row of batch) {
283
+ insertFts.run(row.id, row.content);
284
+ }
285
+ });
286
+ }
287
+ }
288
+ function insertFtsEntries(db, onProgress) {
289
+ try {
290
+ db.prepare("SELECT 1 FROM sqlite_master WHERE type='table' AND name='message_fts'").get();
291
+ } catch {
292
+ return;
293
+ }
294
+ const hasFts = db.prepare("SELECT COUNT(*) as cnt FROM sqlite_master WHERE type='table' AND name='message_fts'").get();
295
+ if (!hasFts || hasFts.cnt === 0) return;
296
+ onProgress?.("\u66F4\u65B0\u5168\u6587\u641C\u7D22\u7D22\u5F15...");
297
+ const maxRowid = db.prepare("SELECT MAX(rowid) as m FROM message_fts").get()?.m ?? 0;
298
+ const newRows = db.prepare("SELECT id, content FROM message WHERE type = 0 AND content IS NOT NULL AND content != '' AND id > ?").all(maxRowid);
299
+ if (newRows.length === 0) return;
300
+ const insertFts = db.prepare("INSERT INTO message_fts(rowid, content) VALUES (?, ?)");
301
+ db.transaction(() => {
302
+ for (const row of newRows) {
303
+ insertFts.run(row.id, row.content);
304
+ }
305
+ });
306
+ }
307
+ async function importData(dbManager, data, options) {
308
+ const sessionId = options?.sessionId || generateSessionId();
309
+ const dbPath = dbManager.getDbPath(sessionId);
310
+ const exists = fs2.existsSync(dbPath);
311
+ if (options?.dryRun) {
312
+ if (exists) {
313
+ const db = dbManager.open(sessionId);
314
+ if (!db)
315
+ return {
316
+ success: false,
317
+ sessionId,
318
+ created: false,
319
+ messageCount: 0,
320
+ memberCount: 0,
321
+ duplicateCount: 0,
322
+ error: "Failed to open database"
323
+ };
324
+ const existingCount = db.prepare("SELECT COUNT(*) as cnt FROM message").get().cnt;
325
+ return {
326
+ success: true,
327
+ sessionId,
328
+ created: false,
329
+ messageCount: data.messages.length,
330
+ memberCount: data.members.length,
331
+ duplicateCount: 0,
332
+ error: `Dry run: ${data.messages.length} messages to import into existing session (${existingCount} existing messages)`
333
+ };
334
+ }
335
+ return {
336
+ success: true,
337
+ sessionId,
338
+ created: true,
339
+ messageCount: data.messages.length,
340
+ memberCount: data.members.length,
341
+ duplicateCount: 0,
342
+ error: `Dry run: would create new session with ${data.messages.length} messages`
343
+ };
344
+ }
345
+ try {
346
+ if (exists) {
347
+ dbManager.close(sessionId);
348
+ const db2 = openBetterSqliteDatabase(dbPath, { readonly: false, nativeBinding: options?.nativeBinding });
349
+ try {
350
+ const result2 = incrementalImport(db2, data, options?.onProgress);
351
+ db2.close();
352
+ return { success: true, sessionId, created: false, ...result2 };
353
+ } catch (err) {
354
+ db2.close();
355
+ throw err;
356
+ }
357
+ }
358
+ const db = openBetterSqliteDatabase(dbPath, { nativeBinding: options?.nativeBinding });
359
+ const result = fullImport(db, data, options?.onProgress);
360
+ db.close();
361
+ return { success: true, sessionId, created: true, ...result };
362
+ } catch (err) {
363
+ const message = err instanceof Error ? err.message : String(err);
364
+ 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
+ }
377
+ }
378
+ return {
379
+ success: false,
380
+ sessionId,
381
+ created: false,
382
+ messageCount: 0,
383
+ memberCount: 0,
384
+ duplicateCount: 0,
385
+ error: message
386
+ };
387
+ }
388
+ }
389
+
390
+ export {
391
+ detectFormat,
392
+ parseFile,
393
+ importData
394
+ };
395
+ //# sourceMappingURL=chunk-XUHPCRVT.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 { openBetterSqliteDatabase, 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 dbManager.close(sessionId)\n const db = openBetterSqliteDatabase(dbPath, { readonly: false, nativeBinding: options?.nativeBinding })\n try {\n const result = incrementalImport(db, data, options?.onProgress)\n db.close()\n return { success: true, sessionId, created: false, ...result }\n } catch (err) {\n db.close()\n throw err\n }\n }\n\n const db = openBetterSqliteDatabase(dbPath, { nativeBinding: options?.nativeBinding })\n const result = fullImport(db, data, options?.onProgress)\n db.close()\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 try {\n fs.unlinkSync(dbPath)\n } catch {\n /* ignore */\n }\n try {\n fs.unlinkSync(dbPath + '-wal')\n } catch {\n /* ignore */\n }\n try {\n fs.unlinkSync(dbPath + '-shm')\n } catch {\n /* ignore */\n }\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 ADDED
@@ -0,0 +1,15 @@
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
+ run
9
+ } from "./chunk-NB4BIAYM.mjs";
10
+ import "./chunk-2AIOU3PW.mjs";
11
+ import "./chunk-EIRLEPXP.mjs";
12
+ export {
13
+ run
14
+ };
15
+ //# sourceMappingURL=cli.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,32 @@
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
+ createServer,
9
+ registerAiRoutes,
10
+ registerNlpRoutes,
11
+ registerSessionRoutes,
12
+ registerSystemRoutes,
13
+ registerWebRoutes,
14
+ startHttpServer,
15
+ stopHttpServer
16
+ } from "./chunk-FBTHZU55.mjs";
17
+ import "./chunk-XUHPCRVT.mjs";
18
+ import "./chunk-EWCSGYVT.mjs";
19
+ import "./chunk-3DIWXYMJ.mjs";
20
+ import "./chunk-2AIOU3PW.mjs";
21
+ import "./chunk-EIRLEPXP.mjs";
22
+ export {
23
+ createServer,
24
+ registerAiRoutes,
25
+ registerNlpRoutes,
26
+ registerSessionRoutes,
27
+ registerSystemRoutes,
28
+ registerWebRoutes,
29
+ startHttpServer,
30
+ stopHttpServer
31
+ };
32
+ //# sourceMappingURL=http-QVYJ5HER.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,41 @@
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
+ detectFormat as detectFormat2,
9
+ importData,
10
+ parseFile
11
+ } from "./chunk-XUHPCRVT.mjs";
12
+ import {
13
+ analyzeIncrementalImport,
14
+ analyzeNewImport,
15
+ incrementalImport,
16
+ streamImport
17
+ } from "./chunk-EWCSGYVT.mjs";
18
+ import {
19
+ detectAllFormats,
20
+ detectFormat,
21
+ findEntryFileInDirectory,
22
+ getFormatFeatureById,
23
+ getSupportedFormats,
24
+ scanMultiChatFile
25
+ } from "./chunk-EIRLEPXP.mjs";
26
+ export {
27
+ analyzeIncrementalImport,
28
+ analyzeNewImport,
29
+ detectAllFormats,
30
+ detectFormat2 as detectChatLabFormat,
31
+ detectFormat,
32
+ findEntryFileInDirectory,
33
+ getFormatFeatureById,
34
+ getSupportedFormats,
35
+ importData,
36
+ incrementalImport,
37
+ parseFile,
38
+ scanMultiChatFile,
39
+ streamImport
40
+ };
41
+ //# sourceMappingURL=import-AJKL6JPR.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
package/dist/index.mjs ADDED
@@ -0,0 +1,24 @@
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
+ run
9
+ } from "./chunk-NB4BIAYM.mjs";
10
+ import {
11
+ startHttpServer,
12
+ stopHttpServer
13
+ } from "./chunk-FBTHZU55.mjs";
14
+ import "./chunk-XUHPCRVT.mjs";
15
+ import "./chunk-EWCSGYVT.mjs";
16
+ import "./chunk-3DIWXYMJ.mjs";
17
+ import "./chunk-2AIOU3PW.mjs";
18
+ import "./chunk-EIRLEPXP.mjs";
19
+ export {
20
+ run,
21
+ startHttpServer,
22
+ stopHttpServer
23
+ };
24
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}