chatlab-cli 0.0.0 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/CHANGELOG.md +51 -0
  2. package/LICENSE +661 -0
  3. package/README.md +115 -3
  4. package/bin/chatlab.mjs +2 -0
  5. package/dist/chunk-2AIOU3PW.mjs +26 -0
  6. package/dist/chunk-2AIOU3PW.mjs.map +1 -0
  7. package/dist/chunk-DL3BFEAH.mjs +144 -0
  8. package/dist/chunk-DL3BFEAH.mjs.map +1 -0
  9. package/dist/chunk-JMRDNHIR.mjs +18601 -0
  10. package/dist/chunk-JMRDNHIR.mjs.map +1 -0
  11. package/dist/chunk-OC73KSMV.mjs +395 -0
  12. package/dist/chunk-OC73KSMV.mjs.map +1 -0
  13. package/dist/chunk-P4N5GEFF.mjs +411 -0
  14. package/dist/chunk-P4N5GEFF.mjs.map +1 -0
  15. package/dist/chunk-PF4QFHC6.mjs +3375 -0
  16. package/dist/chunk-PF4QFHC6.mjs.map +1 -0
  17. package/dist/chunk-YAOFJ73U.mjs +1393 -0
  18. package/dist/chunk-YAOFJ73U.mjs.map +1 -0
  19. package/dist/cli.mjs +15 -0
  20. package/dist/cli.mjs.map +1 -0
  21. package/dist/http-PQEOFGJK.mjs +32 -0
  22. package/dist/http-PQEOFGJK.mjs.map +1 -0
  23. package/dist/import-UR2IE2O7.mjs +39 -0
  24. package/dist/import-UR2IE2O7.mjs.map +1 -0
  25. package/dist/index.mjs +24 -0
  26. package/dist/index.mjs.map +1 -0
  27. package/dist/mcp-4XCDZ5PU.mjs +406 -0
  28. package/dist/mcp-4XCDZ5PU.mjs.map +1 -0
  29. package/dist/src-ZELOFE7P.mjs +327 -0
  30. package/dist/src-ZELOFE7P.mjs.map +1 -0
  31. package/dist/stream-import-RP3XB6NM.mjs +31 -0
  32. package/dist/stream-import-RP3XB6NM.mjs.map +1 -0
  33. package/dist-web/assets/DebugToolsPanel-dUV6pVqD.css +1 -0
  34. package/dist-web/assets/DebugToolsPanel.vue_vue_type_script_setup_true_lang-BuOhHrGp.js +155 -0
  35. package/dist-web/assets/EChartWordcloud-DcC12j9S.js +1 -0
  36. package/dist-web/assets/MessageExportModal-CUS9nnwi.js +1 -0
  37. package/dist-web/assets/electron-8qi0d92j.js +1 -0
  38. package/dist-web/assets/electron-BHjOx6B6.js +1 -0
  39. package/dist-web/assets/electron-CTK4NJag.js +1 -0
  40. package/dist-web/assets/electron-DPsrYwTt.js +1 -0
  41. package/dist-web/assets/electron-DfpXsXsS.js +1 -0
  42. package/dist-web/assets/electron-DvcSSc-D.js +1 -0
  43. package/dist-web/assets/electron-HoVzpIVc.js +1 -0
  44. package/dist-web/assets/fetch-BS4SlQjA.js +1 -0
  45. package/dist-web/assets/fetch-CJ8Q4qM2.js +1 -0
  46. package/dist-web/assets/fetch-CclRWOHf.js +1 -0
  47. package/dist-web/assets/fetch-DgQkWW73.js +3 -0
  48. package/dist-web/assets/fetch-DvfIZxuz.js +17 -0
  49. package/dist-web/assets/fetch-W43zfemj.js +1 -0
  50. package/dist-web/assets/http-CeXMt-Fv.js +1 -0
  51. package/dist-web/assets/index-68UnLyFT.js +173 -0
  52. package/dist-web/assets/index-BKFGuwJu.css +1 -0
  53. package/dist-web/assets/index-CCE1zef2.js +2 -0
  54. package/dist-web/assets/index-CLGv8Den.css +1 -0
  55. package/dist-web/assets/index-CUTe0Sc3.js +16 -0
  56. package/dist-web/assets/index-CmU6x-uJ.css +1 -0
  57. package/dist-web/assets/index-CzR1sElx.css +1 -0
  58. package/dist-web/assets/index-D3siJhmm.js +163 -0
  59. package/dist-web/assets/index-n2XUUgti.js +7 -0
  60. package/dist-web/assets/sse-CACcGGWv.js +2 -0
  61. package/dist-web/assets/vendor-echarts-BXkS9r5W.js +11 -0
  62. package/dist-web/assets/vendor-echarts-wordcloud-CmDVLUv0.js +1 -0
  63. package/dist-web/assets/vendor-nuxt-ui-D1aReMtR.js +15 -0
  64. package/dist-web/assets/vendor-reka-ui-CW2cIgMu.js +11 -0
  65. package/dist-web/assets/vendor-snapdom-XnBoh1gd.js +8 -0
  66. package/dist-web/assets/vendor-zrender-B6SpLIkk.js +8 -0
  67. package/dist-web/assets/web-DOZKbBMQ.js +1 -0
  68. package/dist-web/index.html +18 -0
  69. package/package.json +71 -15
  70. package/cli.js +0 -3
@@ -0,0 +1,411 @@
1
+ import { createRequire as __createRequire } from 'module';
2
+ import { dirname as __pathDirname } from 'path';
3
+ import { fileURLToPath as __fileURLToPath } from 'url';
4
+ const require = __createRequire(import.meta.url);
5
+ const __filename = __fileURLToPath(import.meta.url);
6
+ const __dirname = __pathDirname(__filename);
7
+ import {
8
+ getVersion
9
+ } from "./chunk-2AIOU3PW.mjs";
10
+ import {
11
+ DEFAULT_API_PORT,
12
+ DatabaseManager,
13
+ NodePathProvider,
14
+ executeReadonlySql,
15
+ getConfigPath,
16
+ getMemberActivity,
17
+ getMembers,
18
+ getSessionMeta,
19
+ getSessionOverview,
20
+ hasPendingElectronDataWarning,
21
+ loadConfig,
22
+ searchMessagesLike,
23
+ verifyCliDataPath
24
+ } from "./chunk-JMRDNHIR.mjs";
25
+
26
+ // src/cli.ts
27
+ import * as fs from "fs";
28
+ import * as path from "path";
29
+ import { execSync } from "child_process";
30
+ import { Command } from "commander";
31
+ var program = new Command();
32
+ program.name("chatlab").description("ChatLab - Chat history analysis tool").version(getVersion());
33
+ program.command("sessions").description("List all imported chat sessions").option("--format <format>", "Output format (table|json)", "table").action((options) => {
34
+ const { dbManager } = initRuntime();
35
+ const sessionIds = dbManager.listSessionIds();
36
+ if (sessionIds.length === 0) {
37
+ console.log("No chat sessions found.");
38
+ console.log(`Data directory: ${dbManager["pathProvider"].getUserDataDir()}`);
39
+ dbManager.closeAll();
40
+ return;
41
+ }
42
+ const sessions = sessionIds.map((id) => {
43
+ const db = dbManager.open(id);
44
+ if (!db) return null;
45
+ const meta = getSessionMeta(db);
46
+ if (!meta) return null;
47
+ const overview = getSessionOverview(db);
48
+ return { id, ...meta, ...overview };
49
+ }).filter(Boolean);
50
+ if (options.format === "json") {
51
+ console.log(JSON.stringify(sessions, null, 2));
52
+ } else {
53
+ console.log(`${sessions.length} session(s) found:
54
+ `);
55
+ for (const s of sessions) {
56
+ if (!s) continue;
57
+ const timeRange = formatTimeRange(s.firstMessageTs, s.lastMessageTs);
58
+ console.log(` ${s.name}`);
59
+ console.log(` ID: ${s.id}`);
60
+ console.log(
61
+ ` Platform: ${s.platform} | Type: ${s.type} | Members: ${s.totalMembers} | Messages: ${s.totalMessages}`
62
+ );
63
+ if (timeRange) console.log(` Range: ${timeRange}`);
64
+ console.log();
65
+ }
66
+ }
67
+ dbManager.closeAll();
68
+ });
69
+ program.command("stats <session-id>").description("Show session statistics overview").option("--format <format>", "Output format (table|json)", "table").option("--top <n>", "Show top N active members", "10").action((sessionId, options) => {
70
+ const { dbManager } = initRuntime();
71
+ const db = dbManager.open(sessionId);
72
+ if (!db) {
73
+ console.error(`Session ${sessionId} not found`);
74
+ process.exit(1);
75
+ }
76
+ const meta = getSessionMeta(db);
77
+ const overview = getSessionOverview(db);
78
+ const topMembers = getMemberActivity(db).slice(0, parseInt(options.top));
79
+ if (options.format === "json") {
80
+ console.log(JSON.stringify({ meta, overview, topMembers }, null, 2));
81
+ } else {
82
+ console.log(`
83
+ Session: ${meta?.name}`);
84
+ console.log(`Platform: ${meta?.platform} | Type: ${meta?.type}`);
85
+ console.log(`Total messages: ${overview.totalMessages}`);
86
+ console.log(`Total members: ${overview.totalMembers}`);
87
+ console.log(`Time range: ${formatTimeRange(overview.firstMessageTs, overview.lastMessageTs)}`);
88
+ if (topMembers.length > 0) {
89
+ console.log(`
90
+ Activity ranking (Top ${options.top}):`);
91
+ for (const [i, m] of topMembers.entries()) {
92
+ console.log(` ${i + 1}. ${m.name} - ${m.messageCount} messages (${m.percentage}%)`);
93
+ }
94
+ }
95
+ }
96
+ dbManager.closeAll();
97
+ });
98
+ program.command("search <session-id> <keyword>").description("Search messages by keyword").option("--limit <n>", "Max results to return", "20").option("--format <format>", "Output format (table|json)", "table").action((sessionId, keyword, options) => {
99
+ const { dbManager } = initRuntime();
100
+ const db = dbManager.open(sessionId);
101
+ if (!db) {
102
+ console.error(`Session ${sessionId} not found`);
103
+ process.exit(1);
104
+ }
105
+ const limit = parseInt(options.limit);
106
+ const result = searchMessagesLike(db, keyword, { limit });
107
+ if (options.format === "json") {
108
+ console.log(JSON.stringify(result, null, 2));
109
+ } else {
110
+ console.log(
111
+ `Search "${keyword}" - ${result.total} result(s)${result.hasMore ? " (showing first " + limit + ")" : ""}:
112
+ `
113
+ );
114
+ for (const msg of result.messages) {
115
+ const time = new Date(msg.timestamp * 1e3).toLocaleString();
116
+ console.log(` [${time}] ${msg.senderName}: ${msg.content}`);
117
+ }
118
+ }
119
+ dbManager.closeAll();
120
+ });
121
+ program.command("members <session-id>").description("List session members").option("--format <format>", "Output format (table|json)", "table").action((sessionId, options) => {
122
+ const { dbManager } = initRuntime();
123
+ const db = dbManager.open(sessionId);
124
+ if (!db) {
125
+ console.error(`Session ${sessionId} not found`);
126
+ process.exit(1);
127
+ }
128
+ const members = getMembers(db);
129
+ if (options.format === "json") {
130
+ console.log(JSON.stringify(members, null, 2));
131
+ } else {
132
+ console.log(`${members.length} member(s):
133
+ `);
134
+ for (const [i, m] of members.entries()) {
135
+ console.log(` ${i + 1}. ${m.name} (${m.platformId}) - ${m.messageCount} messages`);
136
+ }
137
+ }
138
+ dbManager.closeAll();
139
+ });
140
+ program.command("query <session-id>").description("Execute a read-only SQL query on a session database").requiredOption("--sql <sql>", "SQL query statement").option("--format <format>", "Output format (table|json)", "table").action((sessionId, options) => {
141
+ const { dbManager } = initRuntime();
142
+ const db = dbManager.open(sessionId);
143
+ if (!db) {
144
+ console.error(`Session ${sessionId} not found`);
145
+ process.exit(1);
146
+ }
147
+ try {
148
+ const result = executeReadonlySql(db, options.sql);
149
+ if (options.format === "json") {
150
+ console.log(JSON.stringify(result, null, 2));
151
+ } else {
152
+ if (result.rows.length === 0) {
153
+ console.log("No results.");
154
+ } else {
155
+ printTable(result.columns, result.rows);
156
+ console.log(`
157
+ ${result.rowCount} row(s)${result.truncated ? " (truncated)" : ""}`);
158
+ }
159
+ }
160
+ } catch (err) {
161
+ console.error(`SQL error: ${err instanceof Error ? err.message : err}`);
162
+ process.exit(1);
163
+ }
164
+ dbManager.closeAll();
165
+ });
166
+ program.command("import <file>").description("Import a chat history file (14+ formats: QQ/WeChat/Telegram/WhatsApp/LINE/Discord/Instagram, etc.)").option("--session-id <id>", "Specify session ID (auto-generated if omitted)").option("--format <id>", "Specify format ID (skip auto-detection)").action(async (file, options) => {
167
+ if (!fs.existsSync(file)) {
168
+ console.error(`File not found: ${file}`);
169
+ process.exit(1);
170
+ }
171
+ const { streamImport, detectFormat } = await import("./import-UR2IE2O7.mjs");
172
+ const { dbManager } = initRuntime();
173
+ const nativeBinding = resolveNativeBinding();
174
+ const format = detectFormat(file);
175
+ if (!format && !options.format) {
176
+ console.error(`Unrecognized file format: ${file}`);
177
+ console.error('Use --format <id> to specify manually, or run "chatlab formats" to see supported formats');
178
+ process.exit(1);
179
+ }
180
+ console.log(`Importing: ${file}`);
181
+ if (format) console.log(` Format: ${format.name} (${format.platform})`);
182
+ try {
183
+ const result = await streamImport(dbManager, file, {
184
+ formatId: options.format,
185
+ nativeBinding,
186
+ onProgress: (p) => {
187
+ process.stdout.write(`\r ${p.stage}: ${p.progress}%`);
188
+ }
189
+ });
190
+ if (result.success) {
191
+ console.log(`
192
+
193
+ Import succeeded!`);
194
+ console.log(` Session ID: ${result.sessionId}`);
195
+ console.log(` Messages written: ${result.diagnostics?.messagesWritten ?? 0}`);
196
+ console.log(` Messages skipped: ${result.diagnostics?.messagesSkipped ?? 0}`);
197
+ } else {
198
+ console.error(`
199
+
200
+ Import failed: ${result.error}`);
201
+ process.exit(1);
202
+ }
203
+ } catch (err) {
204
+ console.error(`
205
+
206
+ Import error: ${err instanceof Error ? err.message : err}`);
207
+ process.exit(1);
208
+ } finally {
209
+ dbManager.closeAll();
210
+ }
211
+ });
212
+ program.command("formats").description("List all supported chat history formats").action(async () => {
213
+ const { getSupportedFormats } = await import("./import-UR2IE2O7.mjs");
214
+ const formats = getSupportedFormats();
215
+ console.log(`${formats.length} supported format(s):
216
+ `);
217
+ for (const f of formats) {
218
+ console.log(` ${f.id.padEnd(30)} ${f.name} (${f.platform}) [${f.extensions.join(", ")}]`);
219
+ }
220
+ });
221
+ program.command("mcp").description("Start MCP Server (stdio transport, for ClaudeCode / Cursor / AI agents)").action(async () => {
222
+ const { startCliMcpServer } = await import("./mcp-4XCDZ5PU.mjs");
223
+ await startCliMcpServer();
224
+ });
225
+ program.command("serve").description("Start the HTTP API server").option("--port <port>", "Server port", String(DEFAULT_API_PORT)).option("--host <host>", "Listen address", "127.0.0.1").option("--token <token>", "Custom Bearer Token (reads from config or auto-generates if omitted)").option("--web [dir]", "Serve Web frontend static assets (defaults to dist-web/)").action(async (options) => {
226
+ const { startHttpServer } = await import("./http-PQEOFGJK.mjs");
227
+ const port = parseInt(options.port, 10);
228
+ let webRoot;
229
+ if (options.web !== void 0) {
230
+ const webDir = typeof options.web === "string" ? options.web : path.resolve(__dirname, "../dist-web");
231
+ if (fs.existsSync(webDir)) {
232
+ webRoot = path.resolve(webDir);
233
+ } else {
234
+ console.warn(`Warning: Web directory not found: ${webDir}, starting in API-only mode`);
235
+ }
236
+ }
237
+ try {
238
+ const info = await startHttpServer({
239
+ port,
240
+ host: options.host,
241
+ token: options.token || void 0,
242
+ webRoot
243
+ });
244
+ console.log(`
245
+ ChatLab HTTP API started`);
246
+ console.log(` Address: http://${info.host}:${info.port}`);
247
+ console.log(` Token: ${info.token}`);
248
+ if (webRoot) {
249
+ console.log(` Web UI: http://${info.host}:${info.port}/`);
250
+ }
251
+ console.log(`
252
+ Example:`);
253
+ console.log(` curl -H "Authorization: Bearer ${info.token}" http://${info.host}:${info.port}/api/v1/status`);
254
+ console.log(`
255
+ Press Ctrl+C to stop.
256
+ `);
257
+ const shutdown = async () => {
258
+ console.log("\nShutting down...");
259
+ const { stopHttpServer } = await import("./http-PQEOFGJK.mjs");
260
+ await stopHttpServer();
261
+ process.exit(0);
262
+ };
263
+ process.on("SIGINT", shutdown);
264
+ process.on("SIGTERM", shutdown);
265
+ } catch (err) {
266
+ const message = err instanceof Error ? err.message : String(err);
267
+ if (message.includes("EADDRINUSE")) {
268
+ console.error(`Error: port ${port} is already in use`);
269
+ } else {
270
+ console.error(`Startup failed: ${message}`);
271
+ }
272
+ process.exit(1);
273
+ }
274
+ });
275
+ program.command("web").description("Launch Web UI with one click (HTTP API + browser auto-open)").option("--port <port>", "Server port", String(DEFAULT_API_PORT)).option("--host <host>", "Listen address", "127.0.0.1").option("--no-open", "Do not auto-open browser").action(async (options) => {
276
+ const { startHttpServer } = await import("./http-PQEOFGJK.mjs");
277
+ const port = parseInt(options.port, 10);
278
+ const webDir = path.resolve(__dirname, "../dist-web");
279
+ let webRoot;
280
+ if (fs.existsSync(webDir)) {
281
+ webRoot = webDir;
282
+ } else {
283
+ console.warn("Warning: dist-web/ not found, Web UI will not be available (API-only mode)");
284
+ }
285
+ try {
286
+ const info = await startHttpServer({ port, host: options.host, webRoot });
287
+ const url = `http://${info.host === "0.0.0.0" ? "127.0.0.1" : info.host}:${info.port}`;
288
+ console.log(`
289
+ ChatLab v${getVersion()}`);
290
+ console.log(` Web UI: ${url}`);
291
+ console.log(` Token: ${info.token}`);
292
+ if (options.open && webRoot) {
293
+ openBrowser(url);
294
+ console.log(`
295
+ Browser opened. Press Ctrl+C to stop.`);
296
+ } else {
297
+ console.log(`
298
+ Press Ctrl+C to stop.`);
299
+ }
300
+ const shutdown = async () => {
301
+ console.log("\nShutting down...");
302
+ const { stopHttpServer } = await import("./http-PQEOFGJK.mjs");
303
+ await stopHttpServer();
304
+ process.exit(0);
305
+ };
306
+ process.on("SIGINT", shutdown);
307
+ process.on("SIGTERM", shutdown);
308
+ } catch (err) {
309
+ const message = err instanceof Error ? err.message : String(err);
310
+ if (message.includes("EADDRINUSE")) {
311
+ console.error(`Error: port ${port} is already in use`);
312
+ } else {
313
+ console.error(`Startup failed: ${message}`);
314
+ }
315
+ process.exit(1);
316
+ }
317
+ });
318
+ var configCmd = program.command("config").description("Configuration management");
319
+ configCmd.command("path").description("Show config file path").action(() => {
320
+ console.log(getConfigPath());
321
+ });
322
+ configCmd.command("show").description("Show current configuration").action(() => {
323
+ const config = loadConfig();
324
+ console.log(JSON.stringify(config, null, 2));
325
+ });
326
+ function openBrowser(url) {
327
+ try {
328
+ const cmd = process.platform === "darwin" ? `open "${url}"` : process.platform === "win32" ? `start "" "${url}"` : `xdg-open "${url}"`;
329
+ execSync(cmd, { stdio: "ignore" });
330
+ } catch {
331
+ console.log(` Open manually: ${url}`);
332
+ }
333
+ }
334
+ function resolveNativeBinding() {
335
+ if (process.versions.electron) return void 0;
336
+ const nativePath = path.resolve(__dirname, "../native/better_sqlite3.node");
337
+ if (fs.existsSync(nativePath)) return nativePath;
338
+ return void 0;
339
+ }
340
+ function initRuntime() {
341
+ const config = loadConfig();
342
+ const userDataDir = config.data.user_data_dir || void 0;
343
+ const pathProvider = new NodePathProvider(userDataDir);
344
+ pathProvider.ensureAllDirs();
345
+ if (hasPendingElectronDataWarning() || !verifyCliDataPath(pathProvider.getDatabaseDir())) {
346
+ printElectronDataError();
347
+ process.exit(1);
348
+ }
349
+ const nativeBinding = resolveNativeBinding();
350
+ const dbManager = new DatabaseManager(pathProvider, { nativeBinding });
351
+ return { config, pathProvider, dbManager };
352
+ }
353
+ function printElectronDataError() {
354
+ console.error("\n" + "=".repeat(68));
355
+ console.error(" ChatLab: Electron desktop data not found");
356
+ console.error("=".repeat(68));
357
+ console.error("");
358
+ console.error(" Detected that ChatLab desktop app was installed on this machine,");
359
+ console.error(" but could not locate your chat databases.");
360
+ console.error("");
361
+ console.error(" This usually means you changed the data directory in desktop settings.");
362
+ console.error("");
363
+ console.error(" To fix this, choose one of:");
364
+ console.error("");
365
+ console.error(" 1. Open ChatLab desktop app \u2014 it will auto-migrate your data");
366
+ console.error(" 2. Set the data directory manually:");
367
+ console.error(' export CHATLAB_DATA_DIR="/path/to/your/data"');
368
+ console.error(" 3. Edit ~/.chatlab/config.toml:");
369
+ console.error(" [data]");
370
+ console.error(' user_data_dir = "/path/to/your/data"');
371
+ console.error("");
372
+ console.error("=".repeat(68) + "\n");
373
+ }
374
+ function formatTimeRange(first, last) {
375
+ if (!first || !last) return "Unknown";
376
+ const from = new Date(first * 1e3).toLocaleDateString();
377
+ const to = new Date(last * 1e3).toLocaleDateString();
378
+ return `${from} ~ ${to}`;
379
+ }
380
+ function printTable(columns, rows) {
381
+ const widths = columns.map((col) => {
382
+ const maxData = rows.reduce((max, row) => {
383
+ const val = String(row[col] ?? "");
384
+ return Math.max(max, val.length);
385
+ }, 0);
386
+ return Math.max(col.length, Math.min(maxData, 40));
387
+ });
388
+ const header = columns.map((col, i) => col.padEnd(widths[i])).join(" | ");
389
+ const separator = widths.map((w) => "-".repeat(w)).join("-+-");
390
+ console.log(header);
391
+ console.log(separator);
392
+ for (const row of rows) {
393
+ const line = columns.map((col, i) => {
394
+ const val = String(row[col] ?? "");
395
+ return val.length > 40 ? val.slice(0, 37) + "..." : val.padEnd(widths[i]);
396
+ }).join(" | ");
397
+ console.log(line);
398
+ }
399
+ }
400
+ function run(argv) {
401
+ program.parse(argv);
402
+ }
403
+ var isDirectRun = process.argv[1]?.endsWith("cli.ts") || process.argv[1]?.endsWith("cli.js");
404
+ if (isDirectRun) {
405
+ run();
406
+ }
407
+
408
+ export {
409
+ run
410
+ };
411
+ //# sourceMappingURL=chunk-P4N5GEFF.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli.ts"],"sourcesContent":["/**\n * ChatLab CLI entry point\n *\n * Dev: pnpm --filter chatlab-cli run cli -- sessions\n */\n\nimport * as fs from 'fs'\nimport * as path from 'path'\nimport { execSync } from 'child_process'\nimport { Command } from 'commander'\nimport { DEFAULT_API_PORT, loadConfig, getConfigPath } from '@openchatlab/config'\nimport {\n NodePathProvider,\n DatabaseManager,\n hasPendingElectronDataWarning,\n verifyCliDataPath,\n} from '@openchatlab/node-runtime'\nimport {\n getSessionMeta,\n getSessionOverview,\n getMemberActivity,\n searchMessagesLike,\n getMembers,\n executeReadonlySql,\n} from '@openchatlab/core'\nimport { getVersion } from './version'\n\nconst program = new Command()\n\nprogram.name('chatlab').description('ChatLab - Chat history analysis tool').version(getVersion())\n\nprogram\n .command('sessions')\n .description('List all imported chat sessions')\n .option('--format <format>', 'Output format (table|json)', 'table')\n .action((options) => {\n const { dbManager } = initRuntime()\n const sessionIds = dbManager.listSessionIds()\n\n if (sessionIds.length === 0) {\n console.log('No chat sessions found.')\n console.log(`Data directory: ${dbManager['pathProvider'].getUserDataDir()}`)\n dbManager.closeAll()\n return\n }\n\n const sessions = sessionIds\n .map((id) => {\n const db = dbManager.open(id)\n if (!db) return null\n const meta = getSessionMeta(db)\n if (!meta) return null\n const overview = getSessionOverview(db)\n return { id, ...meta, ...overview }\n })\n .filter(Boolean)\n\n if (options.format === 'json') {\n console.log(JSON.stringify(sessions, null, 2))\n } else {\n console.log(`${sessions.length} session(s) found:\\n`)\n for (const s of sessions) {\n if (!s) continue\n const timeRange = formatTimeRange(s.firstMessageTs, s.lastMessageTs)\n console.log(` ${s.name}`)\n console.log(` ID: ${s.id}`)\n console.log(\n ` Platform: ${s.platform} | Type: ${s.type} | Members: ${s.totalMembers} | Messages: ${s.totalMessages}`\n )\n if (timeRange) console.log(` Range: ${timeRange}`)\n console.log()\n }\n }\n\n dbManager.closeAll()\n })\n\nprogram\n .command('stats <session-id>')\n .description('Show session statistics overview')\n .option('--format <format>', 'Output format (table|json)', 'table')\n .option('--top <n>', 'Show top N active members', '10')\n .action((sessionId, options) => {\n const { dbManager } = initRuntime()\n const db = dbManager.open(sessionId)\n if (!db) {\n console.error(`Session ${sessionId} not found`)\n process.exit(1)\n }\n\n const meta = getSessionMeta(db)\n const overview = getSessionOverview(db)\n const topMembers = getMemberActivity(db).slice(0, parseInt(options.top))\n\n if (options.format === 'json') {\n console.log(JSON.stringify({ meta, overview, topMembers }, null, 2))\n } else {\n console.log(`\\nSession: ${meta?.name}`)\n console.log(`Platform: ${meta?.platform} | Type: ${meta?.type}`)\n console.log(`Total messages: ${overview.totalMessages}`)\n console.log(`Total members: ${overview.totalMembers}`)\n console.log(`Time range: ${formatTimeRange(overview.firstMessageTs, overview.lastMessageTs)}`)\n\n if (topMembers.length > 0) {\n console.log(`\\nActivity ranking (Top ${options.top}):`)\n for (const [i, m] of topMembers.entries()) {\n console.log(` ${i + 1}. ${m.name} - ${m.messageCount} messages (${m.percentage}%)`)\n }\n }\n }\n\n dbManager.closeAll()\n })\n\nprogram\n .command('search <session-id> <keyword>')\n .description('Search messages by keyword')\n .option('--limit <n>', 'Max results to return', '20')\n .option('--format <format>', 'Output format (table|json)', 'table')\n .action((sessionId, keyword, options) => {\n const { dbManager } = initRuntime()\n const db = dbManager.open(sessionId)\n if (!db) {\n console.error(`Session ${sessionId} not found`)\n process.exit(1)\n }\n\n const limit = parseInt(options.limit)\n const result = searchMessagesLike(db, keyword, { limit })\n\n if (options.format === 'json') {\n console.log(JSON.stringify(result, null, 2))\n } else {\n console.log(\n `Search \"${keyword}\" - ${result.total} result(s)${result.hasMore ? ' (showing first ' + limit + ')' : ''}:\\n`\n )\n for (const msg of result.messages) {\n const time = new Date(msg.timestamp * 1000).toLocaleString()\n console.log(` [${time}] ${msg.senderName}: ${msg.content}`)\n }\n }\n\n dbManager.closeAll()\n })\n\nprogram\n .command('members <session-id>')\n .description('List session members')\n .option('--format <format>', 'Output format (table|json)', 'table')\n .action((sessionId, options) => {\n const { dbManager } = initRuntime()\n const db = dbManager.open(sessionId)\n if (!db) {\n console.error(`Session ${sessionId} not found`)\n process.exit(1)\n }\n\n const members = getMembers(db)\n\n if (options.format === 'json') {\n console.log(JSON.stringify(members, null, 2))\n } else {\n console.log(`${members.length} member(s):\\n`)\n for (const [i, m] of members.entries()) {\n console.log(` ${i + 1}. ${m.name} (${m.platformId}) - ${m.messageCount} messages`)\n }\n }\n\n dbManager.closeAll()\n })\n\nprogram\n .command('query <session-id>')\n .description('Execute a read-only SQL query on a session database')\n .requiredOption('--sql <sql>', 'SQL query statement')\n .option('--format <format>', 'Output format (table|json)', 'table')\n .action((sessionId, options) => {\n const { dbManager } = initRuntime()\n const db = dbManager.open(sessionId)\n if (!db) {\n console.error(`Session ${sessionId} not found`)\n process.exit(1)\n }\n\n try {\n const result = executeReadonlySql(db, options.sql)\n if (options.format === 'json') {\n console.log(JSON.stringify(result, null, 2))\n } else {\n if (result.rows.length === 0) {\n console.log('No results.')\n } else {\n printTable(result.columns, result.rows)\n console.log(`\\n${result.rowCount} row(s)${result.truncated ? ' (truncated)' : ''}`)\n }\n }\n } catch (err) {\n console.error(`SQL error: ${err instanceof Error ? err.message : err}`)\n process.exit(1)\n }\n\n dbManager.closeAll()\n })\n\nprogram\n .command('import <file>')\n .description('Import a chat history file (14+ formats: QQ/WeChat/Telegram/WhatsApp/LINE/Discord/Instagram, etc.)')\n .option('--session-id <id>', 'Specify session ID (auto-generated if omitted)')\n .option('--format <id>', 'Specify format ID (skip auto-detection)')\n .action(async (file, options) => {\n if (!fs.existsSync(file)) {\n console.error(`File not found: ${file}`)\n process.exit(1)\n }\n\n const { streamImport, detectFormat } = await import('./import')\n const { dbManager } = initRuntime()\n const nativeBinding = resolveNativeBinding()\n\n const format = detectFormat(file)\n if (!format && !options.format) {\n console.error(`Unrecognized file format: ${file}`)\n console.error('Use --format <id> to specify manually, or run \"chatlab formats\" to see supported formats')\n process.exit(1)\n }\n\n console.log(`Importing: ${file}`)\n if (format) console.log(` Format: ${format.name} (${format.platform})`)\n\n try {\n const result = await streamImport(dbManager, file, {\n formatId: options.format,\n nativeBinding,\n onProgress: (p) => {\n process.stdout.write(`\\r ${p.stage}: ${p.progress}%`)\n },\n })\n\n if (result.success) {\n console.log(`\\n\\nImport succeeded!`)\n console.log(` Session ID: ${result.sessionId}`)\n console.log(` Messages written: ${result.diagnostics?.messagesWritten ?? 0}`)\n console.log(` Messages skipped: ${result.diagnostics?.messagesSkipped ?? 0}`)\n } else {\n console.error(`\\n\\nImport failed: ${result.error}`)\n process.exit(1)\n }\n } catch (err) {\n console.error(`\\n\\nImport error: ${err instanceof Error ? err.message : err}`)\n process.exit(1)\n } finally {\n dbManager.closeAll()\n }\n })\n\nprogram\n .command('formats')\n .description('List all supported chat history formats')\n .action(async () => {\n const { getSupportedFormats } = await import('./import')\n const formats = getSupportedFormats()\n console.log(`${formats.length} supported format(s):\\n`)\n for (const f of formats) {\n console.log(` ${f.id.padEnd(30)} ${f.name} (${f.platform}) [${f.extensions.join(', ')}]`)\n }\n })\n\nprogram\n .command('mcp')\n .description('Start MCP Server (stdio transport, for ClaudeCode / Cursor / AI agents)')\n .action(async () => {\n const { startCliMcpServer } = await import('./mcp')\n await startCliMcpServer()\n })\n\nprogram\n .command('serve')\n .description('Start the HTTP API server')\n .option('--port <port>', 'Server port', String(DEFAULT_API_PORT))\n .option('--host <host>', 'Listen address', '127.0.0.1')\n .option('--token <token>', 'Custom Bearer Token (reads from config or auto-generates if omitted)')\n .option('--web [dir]', 'Serve Web frontend static assets (defaults to dist-web/)')\n .action(async (options) => {\n const { startHttpServer } = await import('./http')\n const port = parseInt(options.port, 10)\n\n let webRoot: string | undefined\n if (options.web !== undefined) {\n const webDir = typeof options.web === 'string' ? options.web : path.resolve(__dirname, '../dist-web')\n if (fs.existsSync(webDir)) {\n webRoot = path.resolve(webDir)\n } else {\n console.warn(`Warning: Web directory not found: ${webDir}, starting in API-only mode`)\n }\n }\n\n try {\n const info = await startHttpServer({\n port,\n host: options.host,\n token: options.token || undefined,\n webRoot,\n })\n\n console.log(`\\nChatLab HTTP API started`)\n console.log(` Address: http://${info.host}:${info.port}`)\n console.log(` Token: ${info.token}`)\n if (webRoot) {\n console.log(` Web UI: http://${info.host}:${info.port}/`)\n }\n console.log(`\\nExample:`)\n console.log(` curl -H \"Authorization: Bearer ${info.token}\" http://${info.host}:${info.port}/api/v1/status`)\n console.log(`\\nPress Ctrl+C to stop.\\n`)\n\n const shutdown = async () => {\n console.log('\\nShutting down...')\n const { stopHttpServer } = await import('./http')\n await stopHttpServer()\n process.exit(0)\n }\n\n process.on('SIGINT', shutdown)\n process.on('SIGTERM', shutdown)\n } catch (err: unknown) {\n const message = err instanceof Error ? err.message : String(err)\n if (message.includes('EADDRINUSE')) {\n console.error(`Error: port ${port} is already in use`)\n } else {\n console.error(`Startup failed: ${message}`)\n }\n process.exit(1)\n }\n })\n\nprogram\n .command('web')\n .description('Launch Web UI with one click (HTTP API + browser auto-open)')\n .option('--port <port>', 'Server port', String(DEFAULT_API_PORT))\n .option('--host <host>', 'Listen address', '127.0.0.1')\n .option('--no-open', 'Do not auto-open browser')\n .action(async (options) => {\n const { startHttpServer } = await import('./http')\n const port = parseInt(options.port, 10)\n const webDir = path.resolve(__dirname, '../dist-web')\n let webRoot: string | undefined\n\n if (fs.existsSync(webDir)) {\n webRoot = webDir\n } else {\n console.warn('Warning: dist-web/ not found, Web UI will not be available (API-only mode)')\n }\n\n try {\n const info = await startHttpServer({ port, host: options.host, webRoot })\n const url = `http://${info.host === '0.0.0.0' ? '127.0.0.1' : info.host}:${info.port}`\n\n console.log(`\\nChatLab v${getVersion()}`)\n console.log(` Web UI: ${url}`)\n console.log(` Token: ${info.token}`)\n\n if (options.open && webRoot) {\n openBrowser(url)\n console.log(`\\nBrowser opened. Press Ctrl+C to stop.`)\n } else {\n console.log(`\\nPress Ctrl+C to stop.`)\n }\n\n const shutdown = async () => {\n console.log('\\nShutting down...')\n const { stopHttpServer } = await import('./http')\n await stopHttpServer()\n process.exit(0)\n }\n process.on('SIGINT', shutdown)\n process.on('SIGTERM', shutdown)\n } catch (err: unknown) {\n const message = err instanceof Error ? err.message : String(err)\n if (message.includes('EADDRINUSE')) {\n console.error(`Error: port ${port} is already in use`)\n } else {\n console.error(`Startup failed: ${message}`)\n }\n process.exit(1)\n }\n })\n\nconst configCmd = program.command('config').description('Configuration management')\n\nconfigCmd\n .command('path')\n .description('Show config file path')\n .action(() => {\n console.log(getConfigPath())\n })\n\nconfigCmd\n .command('show')\n .description('Show current configuration')\n .action(() => {\n const config = loadConfig()\n console.log(JSON.stringify(config, null, 2))\n })\n\n// --- 工具函数 ---\n\nfunction openBrowser(url: string): void {\n try {\n const cmd =\n process.platform === 'darwin'\n ? `open \"${url}\"`\n : process.platform === 'win32'\n ? `start \"\" \"${url}\"`\n : `xdg-open \"${url}\"`\n execSync(cmd, { stdio: 'ignore' })\n } catch {\n console.log(` Open manually: ${url}`)\n }\n}\n\n/**\n * Resolve standalone better-sqlite3 native module path.\n * Used in non-Electron environments to avoid electron-rebuild conflicts.\n */\nfunction resolveNativeBinding(): string | undefined {\n if (process.versions.electron) return undefined\n const nativePath = path.resolve(__dirname, '../native/better_sqlite3.node')\n if (fs.existsSync(nativePath)) return nativePath\n return undefined\n}\n\nfunction initRuntime() {\n const config = loadConfig()\n const userDataDir = config.data.user_data_dir || undefined\n const pathProvider = new NodePathProvider(userDataDir)\n pathProvider.ensureAllDirs()\n\n if (hasPendingElectronDataWarning() || !verifyCliDataPath(pathProvider.getDatabaseDir())) {\n printElectronDataError()\n process.exit(1)\n }\n\n const nativeBinding = resolveNativeBinding()\n const dbManager = new DatabaseManager(pathProvider, { nativeBinding })\n return { config, pathProvider, dbManager }\n}\n\nfunction printElectronDataError(): void {\n console.error('\\n' + '='.repeat(68))\n console.error(' ChatLab: Electron desktop data not found')\n console.error('='.repeat(68))\n console.error('')\n console.error(' Detected that ChatLab desktop app was installed on this machine,')\n console.error(' but could not locate your chat databases.')\n console.error('')\n console.error(' This usually means you changed the data directory in desktop settings.')\n console.error('')\n console.error(' To fix this, choose one of:')\n console.error('')\n console.error(' 1. Open ChatLab desktop app — it will auto-migrate your data')\n console.error(' 2. Set the data directory manually:')\n console.error(' export CHATLAB_DATA_DIR=\"/path/to/your/data\"')\n console.error(' 3. Edit ~/.chatlab/config.toml:')\n console.error(' [data]')\n console.error(' user_data_dir = \"/path/to/your/data\"')\n console.error('')\n console.error('='.repeat(68) + '\\n')\n}\n\nfunction formatTimeRange(first: number | null, last: number | null): string {\n if (!first || !last) return 'Unknown'\n const from = new Date(first * 1000).toLocaleDateString()\n const to = new Date(last * 1000).toLocaleDateString()\n return `${from} ~ ${to}`\n}\n\nfunction printTable(columns: string[], rows: Record<string, unknown>[]): void {\n const widths = columns.map((col) => {\n const maxData = rows.reduce((max, row) => {\n const val = String(row[col] ?? '')\n return Math.max(max, val.length)\n }, 0)\n return Math.max(col.length, Math.min(maxData, 40))\n })\n\n const header = columns.map((col, i) => col.padEnd(widths[i])).join(' | ')\n const separator = widths.map((w) => '-'.repeat(w)).join('-+-')\n console.log(header)\n console.log(separator)\n\n for (const row of rows) {\n const line = columns\n .map((col, i) => {\n const val = String(row[col] ?? '')\n return val.length > 40 ? val.slice(0, 37) + '...' : val.padEnd(widths[i])\n })\n .join(' | ')\n console.log(line)\n }\n}\n\n/** CLI entry function */\nexport function run(argv?: string[]): void {\n program.parse(argv)\n}\n\n// Auto-execute when run directly as a script\nconst isDirectRun = process.argv[1]?.endsWith('cli.ts') || process.argv[1]?.endsWith('cli.js')\nif (isDirectRun) {\n run()\n}\n"],"mappings":"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","names":[]}