chatlab-cli 0.27.2 → 0.28.1

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 (76) hide show
  1. package/README.md +1 -1
  2. package/dist/{chat-command-QVCZVDSB.mjs → chat-command-ISJ2DC7R.mjs} +5 -5
  3. package/dist/{chunk-OD3YH23N.mjs → chunk-3QBVO4J7.mjs} +2 -2
  4. package/dist/chunk-5FGB42EQ.mjs +7 -0
  5. package/dist/{chunk-JOXF6ROV.mjs → chunk-AB6TAXTQ.mjs} +206 -50
  6. package/dist/chunk-AB6TAXTQ.mjs.map +1 -0
  7. package/dist/{chunk-PXWAYFII.mjs → chunk-ERAAWLEF.mjs} +3 -3
  8. package/dist/{chunk-K7UUN3HT.mjs → chunk-GHTGFM6O.mjs} +26 -11
  9. package/dist/chunk-GHTGFM6O.mjs.map +1 -0
  10. package/dist/{chunk-H22VDCPB.mjs → chunk-NJB2NNB2.mjs} +2 -2
  11. package/dist/{chunk-MDDWX2MG.mjs → chunk-T7IETWHN.mjs} +6 -22
  12. package/dist/chunk-T7IETWHN.mjs.map +1 -0
  13. package/dist/chunk-YBS3YQB6.mjs +1336 -0
  14. package/dist/chunk-YBS3YQB6.mjs.map +1 -0
  15. package/dist/{chunk-B2VLK3FB.mjs → chunk-ZVDOFWL7.mjs} +13377 -4714
  16. package/dist/chunk-ZVDOFWL7.mjs.map +1 -0
  17. package/dist/cli.mjs +4 -4
  18. package/dist/{http-LSCVGWCI.mjs → http-MBILV37U.mjs} +9 -9
  19. package/dist/{import-E6HBJMMT.mjs → import-EYV3GXHE.mjs} +5 -12
  20. package/dist/index.mjs +9 -9
  21. package/dist/{mcp-I7JUC5JV.mjs → mcp-H55AEXU5.mjs} +5 -5
  22. package/dist/semantic-index-worker.mjs +1 -1
  23. package/dist/{src-HZILDI7C.mjs → src-36ZIR65K.mjs} +39 -21
  24. package/dist/{stream-import-IMRULRS2.mjs → stream-import-YZMZ56B5.mjs} +4 -4
  25. package/dist/stream-import-YZMZ56B5.mjs.map +1 -0
  26. package/dist/undici-ZSXBT7EA.mjs +18389 -0
  27. package/dist/undici-ZSXBT7EA.mjs.map +1 -0
  28. package/dist/{worker-runtime-WWQ7W74Q.mjs → worker-runtime-YDYS2C6T.mjs} +9 -5
  29. package/dist/worker-runtime-YDYS2C6T.mjs.map +1 -0
  30. package/dist-web/assets/{DebugToolsPanel.vue_vue_type_script_setup_true_lang-Du1FDD8L.js → DebugToolsPanel.vue_vue_type_script_setup_true_lang-Do3x4aL3.js} +5 -5
  31. package/dist-web/assets/{MessageExportModal-4ehLHG4x.js → MessageExportModal-rijqUusq.js} +1 -1
  32. package/dist-web/assets/electron-D9vE5t3H.js +1 -0
  33. package/dist-web/assets/{electron-B3AteJC8.js → electron-DWDCqx_d.js} +1 -1
  34. package/dist-web/assets/{fetch-BUcl-Frr.js → fetch-29ZBtGV0.js} +1 -1
  35. package/dist-web/assets/{fetch-BQxv_UGQ.js → fetch-BAxKmbKF.js} +1 -1
  36. package/dist-web/assets/{fetch-CZDFhvxY.js → fetch-BYczbT4C.js} +1 -1
  37. package/dist-web/assets/{fetch-ChZEF_n3.js → fetch-BcxrU2mr.js} +1 -1
  38. package/dist-web/assets/{fetch-BwQh9WUD.js → fetch-Bh1fo3Df.js} +1 -1
  39. package/dist-web/assets/{fetch-DlJFgbum.js → fetch-C3yhWO2S.js} +1 -1
  40. package/dist-web/assets/{fetch-DZFx8FA4.js → fetch-Da_TBxrX.js} +1 -1
  41. package/dist-web/assets/{fetch-C2mSdG3h.js → fetch-DyD8wDhb.js} +1 -1
  42. package/dist-web/assets/fetch-TCeykOkO.js +3 -0
  43. package/dist-web/assets/{fetch-DCQN6wfR.js → fetch-tIIVdXSv.js} +1 -1
  44. package/dist-web/assets/index-BzjLqlW_.js +173 -0
  45. package/dist-web/assets/{index-qVTX1tLg.js → index-C74qEDFh.js} +3 -3
  46. package/dist-web/assets/index-CM6xNPFp.css +1 -0
  47. package/dist-web/assets/index-CgK2LLIF.js +12 -0
  48. package/dist-web/assets/{index-DTlfOd_u.js → index-CjkOvM3N.js} +2 -2
  49. package/dist-web/assets/{index-DlkH2E7N.js → index-Dno7CADA.js} +1 -1
  50. package/dist-web/assets/{index-KAFPL6dl.js → index-fbWrQpgH.js} +1 -1
  51. package/dist-web/assets/{web-CoKgy4Oo.js → web-H3XLx159.js} +1 -1
  52. package/dist-web/index.html +2 -2
  53. package/package.json +1 -1
  54. package/dist/chunk-B2VLK3FB.mjs.map +0 -1
  55. package/dist/chunk-DHRSLYUM.mjs +0 -7433
  56. package/dist/chunk-DHRSLYUM.mjs.map +0 -1
  57. package/dist/chunk-JOXF6ROV.mjs.map +0 -1
  58. package/dist/chunk-K7UUN3HT.mjs.map +0 -1
  59. package/dist/chunk-MDDWX2MG.mjs.map +0 -1
  60. package/dist/chunk-ST5APJB2.mjs +0 -389
  61. package/dist/chunk-ST5APJB2.mjs.map +0 -1
  62. package/dist/worker-runtime-WWQ7W74Q.mjs.map +0 -1
  63. package/dist-web/assets/electron-2m4Vdk2z.js +0 -1
  64. package/dist-web/assets/fetch-O8M5ZWsc.js +0 -3
  65. package/dist-web/assets/index-B_uZZGqi.js +0 -173
  66. package/dist-web/assets/index-CoX0L-A8.css +0 -1
  67. package/dist-web/assets/index-DR78oafF.js +0 -11
  68. /package/dist/{chat-command-QVCZVDSB.mjs.map → chat-command-ISJ2DC7R.mjs.map} +0 -0
  69. /package/dist/{chunk-OD3YH23N.mjs.map → chunk-3QBVO4J7.mjs.map} +0 -0
  70. /package/dist/{http-LSCVGWCI.mjs.map → chunk-5FGB42EQ.mjs.map} +0 -0
  71. /package/dist/{chunk-PXWAYFII.mjs.map → chunk-ERAAWLEF.mjs.map} +0 -0
  72. /package/dist/{chunk-H22VDCPB.mjs.map → chunk-NJB2NNB2.mjs.map} +0 -0
  73. /package/dist/{import-E6HBJMMT.mjs.map → http-MBILV37U.mjs.map} +0 -0
  74. /package/dist/{src-HZILDI7C.mjs.map → import-EYV3GXHE.mjs.map} +0 -0
  75. /package/dist/{mcp-I7JUC5JV.mjs.map → mcp-H55AEXU5.mjs.map} +0 -0
  76. /package/dist/{stream-import-IMRULRS2.mjs.map → src-36ZIR65K.mjs.map} +0 -0
package/README.md CHANGED
@@ -14,7 +14,7 @@ English | [简体中文](./README.zh-CN.md)
14
14
 
15
15
  ChatLab is an open-source desktop app for understanding your social conversations. It combines a flexible SQL engine with AI agents so you can explore patterns, ask better questions, and extract insights from chat data, all on your own machine.
16
16
 
17
- Currently supported: **WhatsApp, LINE, QQ, Discord, Instagram, Telegram, and iMessage**. Coming next: **Messenger and KakaoTalk**.
17
+ Currently supported: **WhatsApp, LINE, QQ, Discord, Instagram, Telegram, iMessage, and Google Chat**. Coming next: **Messenger and KakaoTalk**.
18
18
 
19
19
  > New install? Start here: [Getting started](https://docs.chatlab.fun/usage/quick-start)
20
20
 
@@ -6,10 +6,10 @@ const __filename = __fileURLToPath(import.meta.url);
6
6
  const __dirname = __pathDirname(__filename);
7
7
  import {
8
8
  createCliRunAgentStream
9
- } from "./chunk-PXWAYFII.mjs";
10
- import "./chunk-OD3YH23N.mjs";
11
- import "./chunk-DHRSLYUM.mjs";
12
- import "./chunk-B2VLK3FB.mjs";
9
+ } from "./chunk-ERAAWLEF.mjs";
10
+ import "./chunk-3QBVO4J7.mjs";
11
+ import "./chunk-YBS3YQB6.mjs";
12
+ import "./chunk-ZVDOFWL7.mjs";
13
13
  import "./chunk-GFHSSCCR.mjs";
14
14
 
15
15
  // src/ai/chat-command.ts
@@ -302,4 +302,4 @@ export {
302
302
  runChatCommand,
303
303
  runChatTurn
304
304
  };
305
- //# sourceMappingURL=chat-command-QVCZVDSB.mjs.map
305
+ //# sourceMappingURL=chat-command-ISJ2DC7R.mjs.map
@@ -26,7 +26,7 @@ import {
26
26
  getSessionOverview,
27
27
  getWeekdayActivity,
28
28
  searchMessagesByKeywords
29
- } from "./chunk-B2VLK3FB.mjs";
29
+ } from "./chunk-ZVDOFWL7.mjs";
30
30
 
31
31
  // ../../packages/tools/src/definitions/sql-query.ts
32
32
  var inputSchema = {
@@ -1828,4 +1828,4 @@ export {
1828
1828
  MCP_TOOL_REGISTRY,
1829
1829
  CoreDataProvider
1830
1830
  };
1831
- //# sourceMappingURL=chunk-OD3YH23N.mjs.map
1831
+ //# sourceMappingURL=chunk-3QBVO4J7.mjs.map
@@ -0,0 +1,7 @@
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
+ //# sourceMappingURL=chunk-5FGB42EQ.mjs.map
@@ -8,30 +8,26 @@ import {
8
8
  closeServerAiLogger,
9
9
  createCliRunAgentStream,
10
10
  initServerAiLogger
11
- } from "./chunk-PXWAYFII.mjs";
11
+ } from "./chunk-ERAAWLEF.mjs";
12
12
  import {
13
13
  AGENT_TOOL_REGISTRY,
14
14
  CoreDataProvider
15
- } from "./chunk-OD3YH23N.mjs";
15
+ } from "./chunk-3QBVO4J7.mjs";
16
16
  import {
17
17
  assertCliDataDirCompatible
18
- } from "./chunk-H22VDCPB.mjs";
18
+ } from "./chunk-NJB2NNB2.mjs";
19
19
  import {
20
20
  getVersion
21
21
  } from "./chunk-5ADKYYNA.mjs";
22
22
  import {
23
23
  resolveCliPath
24
24
  } from "./chunk-FJ35V3TE.mjs";
25
- import {
26
- importData,
27
- parseFile
28
- } from "./chunk-ST5APJB2.mjs";
29
25
  import {
30
26
  analyzeIncrementalImport,
31
27
  analyzeNewImport,
32
28
  incrementalImport,
33
29
  streamImport
34
- } from "./chunk-MDDWX2MG.mjs";
30
+ } from "./chunk-T7IETWHN.mjs";
35
31
  import {
36
32
  AnalyticsService,
37
33
  NodePathProvider,
@@ -42,22 +38,18 @@ import {
42
38
  createJiebaNlpProvider,
43
39
  createNodeDataDirSwitch,
44
40
  createSemanticIndexWorkerRuntimeClient,
45
- detectAllFormats,
46
- detectFormat,
47
- findEntryFileInDirectory,
48
41
  getDbFileVersion,
49
42
  getDefaultNodeUserDataDir,
50
43
  getOrComputeAnalysisCache,
51
44
  getPendingNodeDataDirMigration,
52
- getSupportedFormats,
53
45
  hasPendingElectronDataWarning,
54
- scanMultiChatFile,
55
- serializeChatLabToJsonl,
56
- streamParseFileInfo
57
- } from "./chunk-DHRSLYUM.mjs";
46
+ serializeChatLabToJsonl
47
+ } from "./chunk-YBS3YQB6.mjs";
58
48
  import {
59
49
  AIChatManager,
60
50
  ALL_MIGRATIONS,
51
+ ArchiveImportError,
52
+ ArchiveImportSourceManager,
61
53
  AssistantManager,
62
54
  BUILTIN_MODELS,
63
55
  BUILTIN_PROVIDERS,
@@ -74,12 +66,15 @@ import {
74
66
  SemanticIndexConfigStore,
75
67
  SkillManagerCore,
76
68
  TempDbWriter,
69
+ appLogger,
77
70
  buildPiModel,
78
71
  computeWordFrequency,
79
72
  countMessagesTokens,
80
73
  createDatabaseManagerAdapter,
81
74
  deleteAuthProfile,
82
75
  deleteDict,
76
+ detectAllFormats,
77
+ detectFormat,
83
78
  downloadDict,
84
79
  ensureDefaultDict,
85
80
  executeReadonlySql,
@@ -87,6 +82,7 @@ import {
87
82
  exportSessionToJson,
88
83
  export_service_exports,
89
84
  fetchRemoteModels,
85
+ findEntryFileInDirectory,
90
86
  getAvailableYears,
91
87
  getBuiltinModelsByProvider,
92
88
  getCatchphraseAnalysis,
@@ -99,6 +95,7 @@ import {
99
95
  getDragonKingAnalysis,
100
96
  getHourlyActivity,
101
97
  getLanguagePreferenceAnalysis,
98
+ getLastPlatformMessageId,
102
99
  getLaughAnalysis,
103
100
  getLongMessageCount,
104
101
  getMemberActivity,
@@ -118,11 +115,13 @@ import {
118
115
  getSessionInfo,
119
116
  getSessionMeta,
120
117
  getSessionOverview,
118
+ getSupportedFormats,
121
119
  getTextLengthPercentiles,
122
120
  getTextStats,
123
121
  getTimeRange,
124
122
  getWeekdayActivity,
125
123
  getYearlyActivity,
124
+ initAppLogger,
126
125
  initNlpDir,
127
126
  isDictDownloaded,
128
127
  isNewerStableVersion,
@@ -132,20 +131,23 @@ import {
132
131
  mergeRulesForLocale,
133
132
  owner_profile_service_exports,
134
133
  persistSemanticIndexConfig,
134
+ pushImport,
135
135
  queryMessages,
136
136
  resolveApiKey,
137
137
  resolveSemanticIndexApiKeySet,
138
138
  runSimpleLlmStream,
139
+ scanMultiChatFile,
139
140
  segmentText,
140
141
  session_index_service_exports,
141
142
  session_service_exports,
143
+ streamParseFileInfo,
142
144
  stripAvatarFields,
143
145
  summary_service_exports,
144
146
  validateApiKey,
145
147
  verifyCliDataPath,
146
148
  writeAuthProfile,
147
149
  writeConfigField
148
- } from "./chunk-B2VLK3FB.mjs";
150
+ } from "./chunk-ZVDOFWL7.mjs";
149
151
 
150
152
  // src/http/index.ts
151
153
  import * as fs11 from "fs";
@@ -164,6 +166,8 @@ var HTTP_STATUS = {
164
166
  ["SQL_EXECUTION_ERROR" /* SQL_EXECUTION_ERROR */]: 400,
165
167
  ["EXPORT_TOO_LARGE" /* EXPORT_TOO_LARGE */]: 400,
166
168
  ["BODY_TOO_LARGE" /* BODY_TOO_LARGE */]: 413,
169
+ ["IMPORT_IN_PROGRESS" /* IMPORT_IN_PROGRESS */]: 409,
170
+ ["IMPORT_FAILED" /* IMPORT_FAILED */]: 500,
167
171
  ["DATA_DIR_INCOMPATIBLE" /* DATA_DIR_INCOMPATIBLE */]: 409,
168
172
  ["SERVER_ERROR" /* SERVER_ERROR */]: 500
169
173
  };
@@ -183,6 +187,9 @@ function unauthorized(message = "Invalid or missing token") {
183
187
  function sessionNotFound(id) {
184
188
  return new ApiError("SESSION_NOT_FOUND" /* SESSION_NOT_FOUND */, `Session not found: ${id}`);
185
189
  }
190
+ function invalidPayload(message) {
191
+ return new ApiError("INVALID_PAYLOAD" /* INVALID_PAYLOAD */, message);
192
+ }
186
193
  function sqlExecutionError(message) {
187
194
  return new ApiError("SQL_EXECUTION_ERROR" /* SQL_EXECUTION_ERROR */, message);
188
195
  }
@@ -195,6 +202,12 @@ function exportTooLarge(count, limit) {
195
202
  function serverError(message = "Internal server error") {
196
203
  return new ApiError("SERVER_ERROR" /* SERVER_ERROR */, message);
197
204
  }
205
+ function importInProgress() {
206
+ return new ApiError("IMPORT_IN_PROGRESS" /* IMPORT_IN_PROGRESS */, "Another import is already in progress");
207
+ }
208
+ function importFailed(message) {
209
+ return new ApiError("IMPORT_FAILED" /* IMPORT_FAILED */, message);
210
+ }
198
211
  function dataDirIncompatible(message) {
199
212
  return new ApiError("DATA_DIR_INCOMPATIBLE" /* DATA_DIR_INCOMPATIBLE */, message);
200
213
  }
@@ -343,7 +356,9 @@ function registerRestSessionRoutes(server2, ctx) {
343
356
  messageCount: info.messageCount,
344
357
  memberCount: info.memberCount,
345
358
  firstTimestamp: info.firstMessageTs,
346
- lastTimestamp: info.lastMessageTs
359
+ lastTimestamp: info.lastMessageTs,
360
+ lastPlatformMessageId: getLastPlatformMessageId(db),
361
+ importedAt: info.importedAt
347
362
  });
348
363
  });
349
364
  server2.get("/api/v1/sessions/:id/messages", async (request) => {
@@ -464,6 +479,39 @@ function registerRestSessionRoutes(server2, ctx) {
464
479
  });
465
480
  }
466
481
 
482
+ // ../../packages/http-routes/src/routes/imports.ts
483
+ function registerImportRoutes(server2, ctx) {
484
+ server2.post(
485
+ "/api/v1/imports/:sessionId",
486
+ async (request, reply) => {
487
+ const { sessionId } = request.params;
488
+ const contentType = request.headers["content-type"] || "";
489
+ if (!contentType.includes("application/json")) {
490
+ const err = invalidPayload("Content-Type must be application/json (JSONL is not yet supported)");
491
+ return reply.code(err.statusCode).send(errorResponse(err));
492
+ }
493
+ if (request.headers["x-dry-run"] === "true") {
494
+ const err = invalidPayload("X-Dry-Run is not yet supported");
495
+ return reply.code(err.statusCode).send(errorResponse(err));
496
+ }
497
+ const outcome = await pushImport(ctx.dbManager, sessionId, request.body ?? {});
498
+ if (!outcome.ok) {
499
+ if (outcome.reason === "import_in_progress") {
500
+ const err2 = importInProgress();
501
+ return reply.code(err2.statusCode).send(errorResponse(err2));
502
+ }
503
+ if (outcome.reason === "invalid_payload") {
504
+ const err2 = invalidPayload(outcome.message);
505
+ return reply.code(err2.statusCode).send(errorResponse(err2));
506
+ }
507
+ const err = importFailed(outcome.message);
508
+ return reply.code(err.statusCode).send(errorResponse(err));
509
+ }
510
+ return successResponse(outcome.result);
511
+ }
512
+ );
513
+ }
514
+
467
515
  // ../../packages/http-routes/src/routes/web/sessions.ts
468
516
  function registerSessionRoutes(server2, ctx) {
469
517
  const { sessionAdapter: adapter } = ctx;
@@ -1638,7 +1686,10 @@ function registerSemanticIndexRoutes(server2, ctx) {
1638
1686
  apiKeySet: resolveSemanticIndexApiKeySet(config, ctx.resolveApiKey),
1639
1687
  configured: isSemanticIndexConfigured(config)
1640
1688
  });
1641
- server2.get("/_web/ai/semantic-index/config", async () => configResponse(configStore.get()));
1689
+ server2.get("/_web/ai/semantic-index/config", async () => ({
1690
+ ...configResponse(configStore.get()),
1691
+ modelStatus: "idle"
1692
+ }));
1642
1693
  server2.put(
1643
1694
  "/_web/ai/semantic-index/config",
1644
1695
  async (request) => {
@@ -1646,7 +1697,7 @@ function registerSemanticIndexRoutes(server2, ctx) {
1646
1697
  apiKey: request.body.apiKey,
1647
1698
  writeAuthProfile: ctx.writeAuthProfile
1648
1699
  });
1649
- return configResponse(config);
1700
+ return { ...configResponse(config), modelStatus: "idle" };
1650
1701
  }
1651
1702
  );
1652
1703
  }
@@ -1659,14 +1710,20 @@ function registerSemanticIndexRoutes(server2, ctx) {
1659
1710
  return {
1660
1711
  config: await service.getConfig(),
1661
1712
  apiKeySet: await service.hasApiKey(),
1662
- configured: await service.isConfigured()
1713
+ configured: await service.isConfigured(),
1714
+ modelStatus: await service.getModelStatus()
1663
1715
  };
1664
1716
  });
1665
1717
  server2.put(
1666
1718
  "/_web/ai/semantic-index/config",
1667
1719
  async (request) => {
1668
1720
  const config = await service.setConfig(request.body.config, { apiKey: request.body.apiKey });
1669
- return { config, apiKeySet: await service.hasApiKey(), configured: await service.isConfigured() };
1721
+ return {
1722
+ config,
1723
+ apiKeySet: await service.hasApiKey(),
1724
+ configured: await service.isConfigured(),
1725
+ modelStatus: await service.getModelStatus()
1726
+ };
1670
1727
  }
1671
1728
  );
1672
1729
  server2.get("/_web/ai/semantic-index/enabled", async () => {
@@ -1687,7 +1744,7 @@ function registerSemanticIndexRoutes(server2, ctx) {
1687
1744
  });
1688
1745
  };
1689
1746
  sessionAction("/_web/ai/semantic-index/enable", (id) => service.enable(id));
1690
- sessionAction("/_web/ai/semantic-index/disable", (id) => service.disable(id));
1747
+ sessionAction("/_web/ai/semantic-index/remove", (id) => service.remove(id));
1691
1748
  sessionAction("/_web/ai/semantic-index/build", (id) => service.build(id));
1692
1749
  sessionAction("/_web/ai/semantic-index/pause", (id) => service.pause(id));
1693
1750
  sessionAction("/_web/ai/semantic-index/cancel", (id) => service.cancel(id));
@@ -2156,11 +2213,25 @@ function registerTelemetryRoutes(server2, ctx) {
2156
2213
  );
2157
2214
  }
2158
2215
 
2216
+ // ../../packages/http-routes/src/routes/web/logs.ts
2217
+ function registerLogRoutes(server2, _ctx) {
2218
+ server2.post("/_web/logs/report", async (req, reply) => {
2219
+ const { level, message, stack, url } = req.body ?? {};
2220
+ if (!message) return reply.code(400).send({ error: "message required" });
2221
+ const detail = [url ? `url=${url}` : null, stack ? `
2222
+ ${stack}` : null].filter(Boolean).join(" ");
2223
+ if (level === "warn") appLogger.warn("web", message, detail || void 0);
2224
+ else appLogger.error("web", message, detail || void 0);
2225
+ return { ok: true };
2226
+ });
2227
+ }
2228
+
2159
2229
  // ../../packages/http-routes/src/register.ts
2160
2230
  function registerSharedRoutes(server2, ctx, options) {
2161
2231
  const resolvedCtx = ctx.preferencesManager ? ctx : { ...ctx, preferencesManager: new PreferencesManager(ctx.pathProvider.getSystemDir()) };
2162
2232
  registerSystemRoutes(server2, resolvedCtx);
2163
2233
  registerRestSessionRoutes(server2, resolvedCtx);
2234
+ registerImportRoutes(server2, resolvedCtx);
2164
2235
  registerSessionRoutes(server2, resolvedCtx);
2165
2236
  registerMemberRoutes(server2, resolvedCtx);
2166
2237
  registerPreferencesRoutes(server2, resolvedCtx);
@@ -2195,6 +2266,7 @@ function registerSharedRoutes(server2, ctx, options) {
2195
2266
  registerMergeRoutes(server2, resolvedCtx);
2196
2267
  registerCacheRoutes(server2, resolvedCtx);
2197
2268
  registerTelemetryRoutes(server2, resolvedCtx);
2269
+ registerLogRoutes(server2, resolvedCtx);
2198
2270
  }
2199
2271
 
2200
2272
  // ../../packages/http-routes/src/auth.ts
@@ -2248,7 +2320,7 @@ function createServer() {
2248
2320
  bodyLimit: JSON_BODY_LIMIT
2249
2321
  });
2250
2322
  server2.addHook("onRequest", authHook);
2251
- server2.setErrorHandler((error, _request, reply) => {
2323
+ server2.setErrorHandler((error, request, reply) => {
2252
2324
  const apiError = apiErrorFromUnknown(error);
2253
2325
  if (apiError) {
2254
2326
  reply.code(apiError.statusCode).send(errorResponse(apiError));
@@ -2264,6 +2336,7 @@ function createServer() {
2264
2336
  reply.code(statusCode).send({ success: false, error: { code: "CLIENT_ERROR", message: error.message } });
2265
2337
  return;
2266
2338
  }
2339
+ appLogger.error("http", `${request.method} ${request.url} -> 500`, error);
2267
2340
  const err = serverError(error.message);
2268
2341
  reply.code(err.statusCode).send(errorResponse(err));
2269
2342
  });
@@ -2278,6 +2351,8 @@ import * as path9 from "path";
2278
2351
  import * as fs6 from "fs";
2279
2352
  import * as os2 from "os";
2280
2353
  import * as path7 from "path";
2354
+ import { randomUUID } from "crypto";
2355
+ import { pipeline } from "stream/promises";
2281
2356
 
2282
2357
  // src/http/routes/web/helpers.ts
2283
2358
  import * as fs5 from "fs";
@@ -2290,6 +2365,7 @@ function resolveNativeBinding() {
2290
2365
 
2291
2366
  // src/http/routes/web/import.ts
2292
2367
  var DEMO_BASE_URL = "https://chatlab.fun/assets/demo";
2368
+ var ARCHIVE_UPLOAD_LIMIT = 50 * 1024 * 1024 * 1024;
2293
2369
  function cleanupTemp(...paths) {
2294
2370
  for (const p of paths) {
2295
2371
  try {
@@ -2303,7 +2379,93 @@ function cleanupTemp(...paths) {
2303
2379
  }
2304
2380
  }
2305
2381
  }
2306
- function registerImportRoutes(server2, dbManager2) {
2382
+ function registerImportRoutes2(server2, dbManager2, options = {}) {
2383
+ const sourceManager = options.sourceManager ?? new ArchiveImportSourceManager({
2384
+ tempRoot: fs6.mkdtempSync(path7.join(os2.tmpdir(), "chatlab-import-sources-"))
2385
+ });
2386
+ const runPreparedImport = options.runPreparedImport ?? (async (manifestPath, onProgress) => {
2387
+ const result = await streamImport(dbManager2, manifestPath, {
2388
+ formatId: "google-chat-takeout",
2389
+ nativeBinding: resolveNativeBinding(),
2390
+ onProgress
2391
+ });
2392
+ return {
2393
+ success: result.success,
2394
+ sessionId: result.sessionId,
2395
+ error: result.error,
2396
+ messageCount: result.diagnostics?.messagesWritten ?? 0,
2397
+ memberCount: 0
2398
+ };
2399
+ });
2400
+ server2.addHook("onClose", async () => {
2401
+ await sourceManager.close();
2402
+ });
2403
+ server2.post("/_web/import-sources", async (request, reply) => {
2404
+ const data = await request.file({
2405
+ limits: { fileSize: ARCHIVE_UPLOAD_LIMIT }
2406
+ });
2407
+ if (!data) return reply.code(400).send({ success: false, error: "error.no_file_selected" });
2408
+ const uploadPath = path7.join(os2.tmpdir(), `chatlab-archive-${randomUUID()}.zip`);
2409
+ try {
2410
+ await pipeline(data.file, fs6.createWriteStream(uploadPath, { flags: "wx" }));
2411
+ if (data.file.truncated) {
2412
+ cleanupTemp(uploadPath);
2413
+ return reply.code(413).send({ success: false, error: "error.archive_limit_exceeded" });
2414
+ }
2415
+ const source = await sourceManager.prepareOwnedArchive(uploadPath);
2416
+ return { success: true, source };
2417
+ } catch (error) {
2418
+ cleanupTemp(uploadPath);
2419
+ if (error instanceof ArchiveImportError) {
2420
+ return reply.code(400).send({ success: false, error: error.code });
2421
+ }
2422
+ return reply.code(400).send({
2423
+ success: false,
2424
+ error: error instanceof Error ? error.message : String(error)
2425
+ });
2426
+ }
2427
+ });
2428
+ server2.post(
2429
+ "/_web/import-sources/:sourceId/import",
2430
+ async (request, reply) => {
2431
+ const chatId = request.body?.chatId;
2432
+ if (!chatId) return reply.code(400).send({ success: false, error: "error.no_chat_selected" });
2433
+ reply.raw.writeHead(200, {
2434
+ "Content-Type": "text/event-stream",
2435
+ "Cache-Control": "no-cache",
2436
+ Connection: "keep-alive"
2437
+ });
2438
+ function sendEvent(event, eventData) {
2439
+ reply.raw.write(`event: ${event}
2440
+ data: ${JSON.stringify(eventData)}
2441
+
2442
+ `);
2443
+ }
2444
+ try {
2445
+ const result = await sourceManager.withMaterializedChat(
2446
+ request.params.sourceId,
2447
+ chatId,
2448
+ (manifestPath) => runPreparedImport(manifestPath, (progress) => sendEvent("progress", progress))
2449
+ );
2450
+ if (result.success) {
2451
+ sendEvent("done", result);
2452
+ } else {
2453
+ sendEvent("error", { success: false, error: result.error || "error.import_failed" });
2454
+ }
2455
+ } catch (error) {
2456
+ sendEvent("error", {
2457
+ success: false,
2458
+ error: error instanceof ArchiveImportError ? error.code : error instanceof Error ? error.message : String(error)
2459
+ });
2460
+ } finally {
2461
+ reply.raw.end();
2462
+ }
2463
+ }
2464
+ );
2465
+ server2.delete("/_web/import-sources/:sourceId", async (request) => {
2466
+ await sourceManager.release(request.params.sourceId);
2467
+ return { success: true };
2468
+ });
2307
2469
  server2.get("/_web/supported-formats", async () => {
2308
2470
  return getSupportedFormats();
2309
2471
  });
@@ -2668,7 +2830,7 @@ function registerWebRoutes(server2, dbManager2, options) {
2668
2830
  const resolvedPathProvider = options?.pathProvider ?? fallbackPathProvider;
2669
2831
  const ai = options?.aiContext;
2670
2832
  const cliStreamImport = async (dm, filePath) => {
2671
- const { streamImport: streamImport2 } = await import("./stream-import-IMRULRS2.mjs");
2833
+ const { streamImport: streamImport2 } = await import("./stream-import-YZMZ56B5.mjs");
2672
2834
  const result = await streamImport2(dm, filePath);
2673
2835
  if (!result.sessionId) throw new Error("Import succeeded but no sessionId returned");
2674
2836
  return { sessionId: result.sessionId };
@@ -2719,7 +2881,7 @@ function registerWebRoutes(server2, dbManager2, options) {
2719
2881
  },
2720
2882
  ai ? { requireAi: true } : void 0
2721
2883
  );
2722
- registerImportRoutes(server2, dbManager2);
2884
+ registerImportRoutes2(server2, dbManager2);
2723
2885
  server2.get("/_web/system/check-update", async () => {
2724
2886
  const currentVersion = getVersion();
2725
2887
  try {
@@ -2771,7 +2933,7 @@ function registerProxyRoutes(server2) {
2771
2933
  // src/ai/manager-factory.ts
2772
2934
  import * as fs7 from "fs";
2773
2935
  import * as path10 from "path";
2774
- import { createHash, randomUUID } from "crypto";
2936
+ import { createHash, randomUUID as randomUUID2 } from "crypto";
2775
2937
  var nodeFs = {
2776
2938
  ensureDir(dir) {
2777
2939
  if (!fs7.existsSync(dir)) fs7.mkdirSync(dir, { recursive: true });
@@ -2805,7 +2967,7 @@ function getAssistantManager(aiDataDir) {
2805
2967
  fs: nodeFs,
2806
2968
  assistantsDir: path10.join(aiDataDir, "assistants"),
2807
2969
  builtinRawConfigs: [],
2808
- generateId: () => `custom_${randomUUID().replace(/-/g, "").slice(0, 12)}`
2970
+ generateId: () => `custom_${randomUUID2().replace(/-/g, "").slice(0, 12)}`
2809
2971
  }),
2810
2972
  skill: new SkillManagerCore({
2811
2973
  fs: nodeFs,
@@ -3170,7 +3332,8 @@ var PullEngine = class {
3170
3332
  this.logger.info(`[Pull] Skipping "${sess.name}": session no longer exists`);
3171
3333
  return { success: true, newMessageCount: 0 };
3172
3334
  }
3173
- if (this.isImporting()) {
3335
+ const importStatusSessionId = sess.targetSessionId || deriveLocalSessionId(ds.baseUrl, sess.remoteSessionId);
3336
+ if (this.isImporting(importStatusSessionId)) {
3174
3337
  this.logger.info(`[Pull] Skipping "${sess.name}": import in progress`);
3175
3338
  return { success: false, newMessageCount: 0, error: "Import in progress" };
3176
3339
  }
@@ -3587,22 +3750,17 @@ var DirectImporter = class {
3587
3750
  }
3588
3751
  async incrementalImportFile(sessionId, tempFile) {
3589
3752
  try {
3590
- const data = await parseFile(tempFile);
3591
3753
  this.dbManager.close(sessionId);
3592
- const result = await importData(this.dbManager, data, {
3593
- sessionId
3594
- });
3754
+ const result = await incrementalImport(this.dbManager, sessionId, tempFile);
3595
3755
  if (result.success) {
3756
+ const newMessageCount = result.newMessageCount;
3757
+ const duplicateCount = result.batch?.duplicateCount ?? 0;
3596
3758
  this.logger.info(
3597
- `[DirectImporter] Incremental OK: +${result.messageCount} messages (${result.duplicateCount} duplicates skipped)`
3759
+ `[DirectImporter] Incremental OK: +${newMessageCount} messages (${duplicateCount} duplicates skipped)`
3598
3760
  );
3599
- return {
3600
- success: true,
3601
- newMessageCount: result.messageCount,
3602
- sessionId
3603
- };
3761
+ return { success: true, newMessageCount, sessionId };
3604
3762
  }
3605
- if (result.error?.includes("not found") || result.error?.includes("session_not_found") || result.error?.includes("no such table")) {
3763
+ if (result.error === "error.session_not_found" || result.error?.includes("no such table")) {
3606
3764
  return { success: false, newMessageCount: 0, sessionId, needFullResync: true };
3607
3765
  }
3608
3766
  return { success: false, newMessageCount: 0, sessionId, error: result.error };
@@ -3613,15 +3771,11 @@ var DirectImporter = class {
3613
3771
  }
3614
3772
  async fullImportFile(tempFile, externalId) {
3615
3773
  try {
3616
- const data = await parseFile(tempFile);
3617
- const result = await importData(this.dbManager, data, {
3618
- sessionId: externalId
3619
- });
3774
+ const result = await streamImport(this.dbManager, tempFile, { sessionId: externalId });
3620
3775
  if (result.success) {
3621
- this.logger.info(
3622
- `[DirectImporter] Full import OK: ${result.messageCount} messages, ${result.memberCount} members`
3623
- );
3624
- return { success: true, newMessageCount: result.messageCount, sessionId: externalId };
3776
+ const newMessageCount = result.diagnostics?.messagesWritten ?? 0;
3777
+ this.logger.info(`[DirectImporter] Full import OK: +${newMessageCount} messages`);
3778
+ return { success: true, newMessageCount, sessionId: result.sessionId ?? externalId };
3625
3779
  }
3626
3780
  return { success: false, newMessageCount: 0, error: result.error };
3627
3781
  } catch (err) {
@@ -3854,7 +4008,9 @@ async function startHttpServer(options) {
3854
4008
  if (profileName) deleteAuthProfile(profileName);
3855
4009
  }
3856
4010
  });
4011
+ initAppLogger(pathProvider.getLogsDir());
3857
4012
  initServerAiLogger(pathProvider.getLogsDir());
4013
+ appLogger.info("server", `HTTP server starting on ${host}:${port}`);
3858
4014
  setAuthToken(token);
3859
4015
  setRequireAuth(!!(options?.requireAuth ?? config.api.require_auth));
3860
4016
  server = createServer();
@@ -3933,4 +4089,4 @@ export {
3933
4089
  startHttpServer,
3934
4090
  stopHttpServer
3935
4091
  };
3936
- //# sourceMappingURL=chunk-JOXF6ROV.mjs.map
4092
+ //# sourceMappingURL=chunk-AB6TAXTQ.mjs.map