chatlab-cli 0.30.0 → 0.30.1-beta.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 (87) hide show
  1. package/dist/{chat-command-UZ23GKO7.mjs → chat-command-VBDMWOKL.mjs} +8 -8
  2. package/dist/{chunk-RDR6PS2R.mjs → chunk-326OCJOT.mjs} +2 -2
  3. package/dist/{chunk-A2SANKG4.mjs → chunk-36JGKW6K.mjs} +593 -254
  4. package/dist/chunk-36JGKW6K.mjs.map +1 -0
  5. package/dist/{chunk-ZFU2CHW3.mjs → chunk-5EE2EFPE.mjs} +4 -4
  6. package/dist/{chunk-4IZ3WKYU.mjs → chunk-D3DPCDII.mjs} +24 -11
  7. package/dist/chunk-D3DPCDII.mjs.map +1 -0
  8. package/dist/{chunk-FCCL5Z4W.mjs → chunk-KBSAVUFV.mjs} +7 -4
  9. package/dist/chunk-KBSAVUFV.mjs.map +1 -0
  10. package/dist/{chunk-7BZLS2KW.mjs → chunk-LVEJHB3X.mjs} +242 -236
  11. package/dist/chunk-LVEJHB3X.mjs.map +1 -0
  12. package/dist/{chunk-M55KW2HA.mjs → chunk-M2PXDX4G.mjs} +14 -12
  13. package/dist/chunk-M2PXDX4G.mjs.map +1 -0
  14. package/dist/{chunk-5UBHNQFZ.mjs → chunk-TCZHG3G7.mjs} +2 -2
  15. package/dist/{chunk-CNSYXRJP.mjs → chunk-TEX44HNL.mjs} +16 -12
  16. package/dist/chunk-TEX44HNL.mjs.map +1 -0
  17. package/dist/{chunk-P3ZOLTGS.mjs → chunk-ULNRUS55.mjs} +5 -3
  18. package/dist/chunk-ULNRUS55.mjs.map +1 -0
  19. package/dist/{chunk-MUPILWAZ.mjs → chunk-WV5GSRQM.mjs} +2 -2
  20. package/dist/cli.mjs +7 -7
  21. package/dist/contacts-worker.mjs +2 -2
  22. package/dist/{http-UWRA5FLB.mjs → http-L4AMELPZ.mjs} +11 -11
  23. package/dist/{import-745KQL7N.mjs → import-WWCSXGVS.mjs} +7 -7
  24. package/dist/index.mjs +11 -11
  25. package/dist/{mcp-UHN5JOHC.mjs → mcp-A4M3LT5O.mjs} +8 -8
  26. package/dist/people-relationships-worker.mjs +2 -2
  27. package/dist/semantic-index-worker.mjs +1 -1
  28. package/dist/{src-Y7MDRXLJ.mjs → src-MPNBTFOE.mjs} +8 -6
  29. package/dist/{stream-import-3JMXQD7P.mjs → stream-import-HIE7PNL4.mjs} +7 -7
  30. package/dist/{worker-runtime-63RYMGZI.mjs → worker-runtime-BJXO24XX.mjs} +5 -5
  31. package/dist-web/assets/{BufferResource-BVdJ_I5X.js → BufferResource-DPkMC1wo.js} +1 -1
  32. package/dist-web/assets/{CanvasRenderer-BaRuT54R.js → CanvasRenderer-DokMtSX9.js} +1 -1
  33. package/dist-web/assets/{DebugToolsPanel.vue_vue_type_script_setup_true_lang-N5CoiJzN.js → DebugToolsPanel.vue_vue_type_script_setup_true_lang-Ceg-0WOD.js} +4 -4
  34. package/dist-web/assets/{Filter-B7cNFtuE.js → Filter-Bo_p3BVX.js} +1 -1
  35. package/dist-web/assets/{MessageExportModal-B2Qm6VVj.js → MessageExportModal-Ch0tqQix.js} +1 -1
  36. package/dist-web/assets/{PageHeader-PjlWy9fC.js → PageHeader-CRlmZoKw.js} +1 -1
  37. package/dist-web/assets/{RelationshipGalaxyCanvas-11MafciW.js → RelationshipGalaxyCanvas-W_6XfPOG.js} +3 -3
  38. package/dist-web/assets/{RelationshipGalaxyThreeCanvas-C1I4B8NX.js → RelationshipGalaxyThreeCanvas-bB1nCwQ_.js} +1 -1
  39. package/dist-web/assets/{RenderTargetSystem-Chkz1WlI.js → RenderTargetSystem-obRi6fMT.js} +1 -1
  40. package/dist-web/assets/{WebGLRenderer-xK19ru58.js → WebGLRenderer-xcDlnrCZ.js} +1 -1
  41. package/dist-web/assets/{WebGPURenderer-CL5ZaKsn.js → WebGPURenderer-DbAOiiYr.js} +1 -1
  42. package/dist-web/assets/{browserAll-CyyNzRD_.js → browserAll-DVRJimO8.js} +1 -1
  43. package/dist-web/assets/{electron-MXaTKaMu.js → electron-BSGRuxX9.js} +1 -1
  44. package/dist-web/assets/{fetch-D1dDiFO4.js → fetch-7UN4YW3A.js} +1 -1
  45. package/dist-web/assets/{fetch-DaaJwT-5.js → fetch-B2alAySK.js} +1 -1
  46. package/dist-web/assets/{fetch-DpnIGZSN.js → fetch-B2rVTMYY.js} +1 -1
  47. package/dist-web/assets/{fetch-DTwdRTXM.js → fetch-BSShKcMt.js} +1 -1
  48. package/dist-web/assets/{fetch-DsTgYu_X.js → fetch-BmxMf3I6.js} +1 -1
  49. package/dist-web/assets/{fetch-u9GTMhMF.js → fetch-CR5UDFW7.js} +1 -1
  50. package/dist-web/assets/{fetch-DsE-xcP2.js → fetch-ChAsglT5.js} +1 -1
  51. package/dist-web/assets/{fetch-hKE53UO4.js → fetch-DWEjTdcP.js} +1 -1
  52. package/dist-web/assets/{fetch-DDnVXGte.js → fetch-DfoxedXR.js} +1 -1
  53. package/dist-web/assets/{fetch-DeA0ztwT.js → fetch-t0WsRKHW.js} +1 -1
  54. package/dist-web/assets/{index-BcXLcFnV.css → index-B4l3rHie.css} +1 -1
  55. package/dist-web/assets/{index-DqtXt3wl.js → index-BVUTV9_w.js} +1 -1
  56. package/dist-web/assets/{index-DFCYmzGE.js → index-BmXNdMV5.js} +2 -2
  57. package/dist-web/assets/index-BsXljrlg.js +22 -0
  58. package/dist-web/assets/index-CQ5lRCHt.js +173 -0
  59. package/dist-web/assets/{index-Btr5H7d-.js → index-CZv0ZOID.js} +2 -2
  60. package/dist-web/assets/{index-DqXHRIhI.js → index-CfgDqd_z.js} +1 -1
  61. package/dist-web/assets/{index-BnHKyESZ.js → index-CtVR_KuP.js} +1 -1
  62. package/dist-web/assets/{index-Dszv-QFp.js → index-DT0Bu8mH.js} +3 -3
  63. package/dist-web/assets/{index-DzLrWGXu.js → index-DjcWJv4b.js} +1 -1
  64. package/dist-web/assets/{web-DPVWfPS-.js → web-knHSvvNj.js} +1 -1
  65. package/dist-web/assets/{webworkerAll-DF5QV_vN.js → webworkerAll-CMKoykfD.js} +1 -1
  66. package/dist-web/index.html +2 -2
  67. package/package.json +1 -1
  68. package/dist/chunk-4IZ3WKYU.mjs.map +0 -1
  69. package/dist/chunk-7BZLS2KW.mjs.map +0 -1
  70. package/dist/chunk-A2SANKG4.mjs.map +0 -1
  71. package/dist/chunk-CNSYXRJP.mjs.map +0 -1
  72. package/dist/chunk-FCCL5Z4W.mjs.map +0 -1
  73. package/dist/chunk-M55KW2HA.mjs.map +0 -1
  74. package/dist/chunk-P3ZOLTGS.mjs.map +0 -1
  75. package/dist-web/assets/index-BVkE4qis.js +0 -22
  76. package/dist-web/assets/index-D04LWJop.js +0 -173
  77. /package/dist/{chat-command-UZ23GKO7.mjs.map → chat-command-VBDMWOKL.mjs.map} +0 -0
  78. /package/dist/{chunk-RDR6PS2R.mjs.map → chunk-326OCJOT.mjs.map} +0 -0
  79. /package/dist/{chunk-ZFU2CHW3.mjs.map → chunk-5EE2EFPE.mjs.map} +0 -0
  80. /package/dist/{chunk-5UBHNQFZ.mjs.map → chunk-TCZHG3G7.mjs.map} +0 -0
  81. /package/dist/{chunk-MUPILWAZ.mjs.map → chunk-WV5GSRQM.mjs.map} +0 -0
  82. /package/dist/{http-UWRA5FLB.mjs.map → http-L4AMELPZ.mjs.map} +0 -0
  83. /package/dist/{import-745KQL7N.mjs.map → import-WWCSXGVS.mjs.map} +0 -0
  84. /package/dist/{mcp-UHN5JOHC.mjs.map → mcp-A4M3LT5O.mjs.map} +0 -0
  85. /package/dist/{src-Y7MDRXLJ.mjs.map → src-MPNBTFOE.mjs.map} +0 -0
  86. /package/dist/{stream-import-3JMXQD7P.mjs.map → stream-import-HIE7PNL4.mjs.map} +0 -0
  87. /package/dist/{worker-runtime-63RYMGZI.mjs.map → worker-runtime-BJXO24XX.mjs.map} +0 -0
@@ -9,7 +9,7 @@ import {
9
9
  createEmptyContactsDiagnostics,
10
10
  normalizeContactsTimeRangePreset,
11
11
  resolveContactsTimeRange
12
- } from "./chunk-CNSYXRJP.mjs";
12
+ } from "./chunk-TEX44HNL.mjs";
13
13
  import {
14
14
  PEOPLE_RELATIONSHIPS_ALGORITHM_VERSION,
15
15
  buildPeopleRelationshipsNeighborhoodGraph,
@@ -18,7 +18,7 @@ import {
18
18
  computePeopleRelationshipsSnapshot,
19
19
  normalizePeopleRelationshipsTimeRangePreset,
20
20
  resolvePeopleRelationshipsTimeRange
21
- } from "./chunk-MUPILWAZ.mjs";
21
+ } from "./chunk-WV5GSRQM.mjs";
22
22
  import {
23
23
  BUILTIN_MODELS,
24
24
  BUILTIN_PROVIDERS,
@@ -80,7 +80,7 @@ import {
80
80
  updateMemberAliases,
81
81
  updateSessionOwnerId,
82
82
  writeAuthProfile
83
- } from "./chunk-7BZLS2KW.mjs";
83
+ } from "./chunk-LVEJHB3X.mjs";
84
84
  import {
85
85
  __commonJS,
86
86
  __export,
@@ -2837,128 +2837,29 @@ function writeParseResultToDb(db, meta, members, messages) {
2837
2837
  return { messageCount, memberCount: members.length, skippedCount };
2838
2838
  }
2839
2839
 
2840
- // ../../packages/node-runtime/src/import/perf-logger.ts
2841
- import * as fs from "fs";
2842
- import * as path from "path";
2843
- var LogLevel = /* @__PURE__ */ ((LogLevel2) => {
2844
- LogLevel2["ERROR"] = "ERROR";
2845
- LogLevel2["INFO"] = "INFO";
2846
- return LogLevel2;
2847
- })(LogLevel || {});
2848
- var lastLogTime = Date.now();
2849
- var lastMessageCount = 0;
2850
- var currentLogFile = null;
2851
- var errorCount = 0;
2852
- function initPerfLog(sessionId, logDir) {
2853
- try {
2854
- if (!fs.existsSync(logDir)) {
2855
- fs.mkdirSync(logDir, { recursive: true });
2856
- }
2857
- currentLogFile = path.join(logDir, `import_${sessionId}_${Date.now()}.log`);
2858
- fs.writeFileSync(currentLogFile, `=== Import Log ===
2859
- Start time: ${(/* @__PURE__ */ new Date()).toISOString()}
2860
-
2861
- `, "utf-8");
2862
- } catch {
2863
- }
2864
- }
2865
- function logPerf(event, messagesProcessed, batchSize) {
2866
- const now = Date.now();
2867
- const duration = now - lastLogTime;
2868
- const messagesDelta = messagesProcessed - lastMessageCount;
2869
- const speed = duration > 0 ? Math.round(messagesDelta / duration * 1e3) : 0;
2870
- let memory = 0;
2871
- try {
2872
- const used = process.memoryUsage();
2873
- memory = Math.round(used.heapUsed / 1024 / 1024);
2874
- } catch {
2875
- }
2876
- const logLine = `[${(/* @__PURE__ */ new Date()).toISOString()}] ${event} | messages: ${messagesProcessed.toLocaleString()} | elapsed: ${duration}ms | speed: ${speed.toLocaleString()}/s | memory: ${memory}MB` + (batchSize ? ` | batch: ${batchSize}` : "") + "\n";
2877
- if (currentLogFile) {
2878
- try {
2879
- fs.appendFileSync(currentLogFile, logLine, "utf-8");
2880
- } catch {
2881
- }
2882
- }
2883
- lastLogTime = now;
2884
- lastMessageCount = messagesProcessed;
2885
- }
2886
- function logPerfDetail(detail) {
2887
- if (currentLogFile) {
2888
- try {
2889
- fs.appendFileSync(currentLogFile, ` ${detail}
2890
- `, "utf-8");
2891
- } catch {
2892
- }
2893
- }
2894
- }
2895
- function resetPerfLog() {
2896
- lastLogTime = Date.now();
2897
- lastMessageCount = 0;
2898
- currentLogFile = null;
2899
- errorCount = 0;
2900
- }
2901
- function getCurrentLogFile() {
2902
- return currentLogFile;
2903
- }
2904
- function writeLogLine(level, message) {
2905
- if (!currentLogFile) return;
2906
- const logLine = `[${(/* @__PURE__ */ new Date()).toISOString()}] [${level}] ${message}
2907
- `;
2908
- try {
2909
- fs.appendFileSync(currentLogFile, logLine, "utf-8");
2910
- } catch {
2911
- }
2912
- }
2913
- function logError(message, error) {
2914
- errorCount++;
2915
- const errorDetail = error ? `: ${error.message}` : "";
2916
- writeLogLine("ERROR" /* ERROR */, `${message}${errorDetail}`);
2917
- }
2918
- function logInfo(message) {
2919
- writeLogLine("INFO" /* INFO */, message);
2920
- }
2921
- function getErrorCount() {
2922
- return errorCount;
2923
- }
2924
- function logSummary(totalMessages, totalMembers) {
2925
- if (!currentLogFile) return;
2926
- const summary = `
2927
- === Import Summary ===
2928
- End time: ${(/* @__PURE__ */ new Date()).toISOString()}
2929
- Total messages: ${totalMessages.toLocaleString()}
2930
- Total members: ${totalMembers.toLocaleString()}
2931
- Errors: ${errorCount}
2932
- `;
2933
- try {
2934
- fs.appendFileSync(currentLogFile, summary, "utf-8");
2935
- } catch {
2936
- }
2937
- }
2938
-
2939
2840
  // ../../packages/parser/src/index.ts
2940
- import * as fs18 from "fs";
2941
- import * as path14 from "path";
2841
+ import * as fs17 from "fs";
2842
+ import * as path13 from "path";
2942
2843
 
2943
2844
  // ../../packages/parser/src/sniffer.ts
2944
- import * as fs2 from "fs";
2945
- import * as path2 from "path";
2845
+ import * as fs from "fs";
2846
+ import * as path from "path";
2946
2847
  var HEAD_SIZE = 64 * 1024;
2947
2848
  function readFileHead(filePath, size = HEAD_SIZE) {
2948
- const fd = fs2.openSync(filePath, "r");
2849
+ const fd = fs.openSync(filePath, "r");
2949
2850
  const buffer = Buffer.alloc(size);
2950
- const bytesRead = fs2.readSync(fd, buffer, 0, size, 0);
2951
- fs2.closeSync(fd);
2851
+ const bytesRead = fs.readSync(fd, buffer, 0, size, 0);
2852
+ fs.closeSync(fd);
2952
2853
  return buffer.slice(0, bytesRead).toString("utf-8");
2953
2854
  }
2954
2855
  function getExtension(filePath) {
2955
- return path2.extname(filePath).toLowerCase();
2856
+ return path.extname(filePath).toLowerCase();
2956
2857
  }
2957
2858
  function matchHeadSignatures(headContent, patterns) {
2958
2859
  return patterns.some((pattern) => pattern.test(headContent));
2959
2860
  }
2960
2861
  function matchFilenameSignatures(filePath, patterns) {
2961
- const filename = path2.basename(filePath);
2862
+ const filename = path.basename(filePath);
2962
2863
  return patterns.some((pattern) => pattern.test(filename));
2963
2864
  }
2964
2865
  function matchRequiredFields(headContent, fields) {
@@ -3106,18 +3007,18 @@ var import_stream_json = __toESM(require_stream_json(), 1);
3106
3007
  var import_Pick = __toESM(require_Pick(), 1);
3107
3008
  var import_StreamValues = __toESM(require_StreamValues(), 1);
3108
3009
  var import_stream_chain = __toESM(require_stream_chain(), 1);
3109
- import * as fs4 from "fs";
3010
+ import * as fs3 from "fs";
3110
3011
 
3111
3012
  // ../../packages/parser/src/utils.ts
3112
- import * as fs3 from "fs";
3013
+ import * as fs2 from "fs";
3113
3014
  function getFileSize(filePath) {
3114
- return fs3.statSync(filePath).size;
3015
+ return fs2.statSync(filePath).size;
3115
3016
  }
3116
3017
  function readFileHeadBytes(filePath, size) {
3117
- const fd = fs3.openSync(filePath, "r");
3018
+ const fd = fs2.openSync(filePath, "r");
3118
3019
  const buffer = Buffer.alloc(size);
3119
- const bytesRead = fs3.readSync(fd, buffer, 0, size, 0);
3120
- fs3.closeSync(fd);
3020
+ const bytesRead = fs2.readSync(fd, buffer, 0, size, 0);
3021
+ fs2.closeSync(fd);
3121
3022
  return buffer.slice(0, bytesRead).toString("utf-8");
3122
3023
  }
3123
3024
  var createProgress = (stage, bytesRead, totalBytes, messagesProcessed, message) => {
@@ -3147,13 +3048,185 @@ function isValidYear(timestampSeconds) {
3147
3048
  }
3148
3049
 
3149
3050
  // ../../packages/parser/src/formats/chatlab.ts
3150
- import * as path3 from "path";
3051
+ import * as path2 from "path";
3052
+
3053
+ // ../../packages/parser/src/native/loader.ts
3054
+ import { createRequire } from "module";
3055
+ var cachedModule;
3056
+ var cachedLoadError;
3057
+ function isNativeDisabled() {
3058
+ return process.env.CHATLAB_DISABLE_NATIVE_PERF === "1";
3059
+ }
3060
+ function isNativeParserModule(value) {
3061
+ return typeof value?.NativeParser === "function";
3062
+ }
3063
+ function requireNativeModule() {
3064
+ const requireFn = createRequire(import.meta.url);
3065
+ const module = requireFn("@openchatlab/parser-native");
3066
+ if (!isNativeParserModule(module)) {
3067
+ throw new Error("Native parser module missing NativeParser export");
3068
+ }
3069
+ return module;
3070
+ }
3071
+ function loadNativeParser() {
3072
+ if (isNativeDisabled()) return null;
3073
+ if (cachedModule !== void 0) return cachedModule;
3074
+ try {
3075
+ cachedModule = requireNativeModule();
3076
+ } catch (error) {
3077
+ cachedModule = null;
3078
+ cachedLoadError = error instanceof Error ? error.message : String(error);
3079
+ }
3080
+ return cachedModule;
3081
+ }
3082
+ function getNativeParserStatus() {
3083
+ if (isNativeDisabled()) return { available: false, disabled: true };
3084
+ const module = loadNativeParser();
3085
+ return { available: module !== null, disabled: false, error: module ? void 0 : cachedLoadError };
3086
+ }
3087
+
3088
+ // ../../packages/parser/src/native/create-native-parser.ts
3089
+ var PROGRESS_POLL_INTERVAL_MS = 200;
3090
+ async function* pumpNativeParser(adapter, options2, parser11, fallback) {
3091
+ const { filePath, batchSize = 5e3, onProgress, onLog } = options2;
3092
+ const totalBytes = getFileSize(filePath);
3093
+ const initialProgress = createProgress("parsing", 0, totalBytes, 0, "");
3094
+ yield { type: "progress", data: initialProgress };
3095
+ onProgress?.(initialProgress);
3096
+ onLog?.(
3097
+ "info",
3098
+ `[NativeParser] Parsing ${adapter.label} with Rust kernel, size: ${(totalBytes / 1024 / 1024).toFixed(2)} MB`
3099
+ );
3100
+ const progressTimer = setInterval(() => {
3101
+ const progress = parser11.progress();
3102
+ onProgress?.(
3103
+ createProgress(
3104
+ "parsing",
3105
+ progress.bytesRead,
3106
+ totalBytes,
3107
+ progress.messagesProcessed,
3108
+ `\u5DF2\u5904\u7406 ${progress.messagesProcessed} \u6761\u6D88\u606F...`
3109
+ )
3110
+ );
3111
+ }, PROGRESS_POLL_INTERVAL_MS);
3112
+ try {
3113
+ await parser11.parse();
3114
+ } catch (error) {
3115
+ onLog?.(
3116
+ "warn",
3117
+ `[NativeParser] Rust parse failed, falling back to TS parser: ${error instanceof Error ? error.message : String(error)}`
3118
+ );
3119
+ yield* fallback(options2);
3120
+ return;
3121
+ } finally {
3122
+ clearInterval(progressTimer);
3123
+ }
3124
+ const metaJson = JSON.parse(parser11.metaJson());
3125
+ yield { type: "meta", data: adapter.mapMeta(metaJson) };
3126
+ const members = adapter.mapMembers(parser11.takeMembers(), metaJson);
3127
+ yield { type: "members", data: members };
3128
+ let messagesProcessed = 0;
3129
+ while (true) {
3130
+ const batch = parser11.takeBatch(batchSize);
3131
+ if (!batch) break;
3132
+ messagesProcessed += batch.length;
3133
+ yield { type: "messages", data: batch.map((message) => adapter.mapMessage(message, metaJson)) };
3134
+ await new Promise((resolve2) => setImmediate(resolve2));
3135
+ }
3136
+ const doneProgress = createProgress("done", totalBytes, totalBytes, messagesProcessed, "");
3137
+ yield { type: "progress", data: doneProgress };
3138
+ onProgress?.(doneProgress);
3139
+ onLog?.("info", `\u89E3\u6790\u5B8C\u6210: ${messagesProcessed} \u6761\u6D88\u606F, ${members.length} \u4E2A\u6210\u5458`);
3140
+ yield { type: "done", data: { messageCount: messagesProcessed, memberCount: members.length } };
3141
+ }
3142
+ function createNativeFirstParser(adapter, fallback) {
3143
+ return async function* (options2) {
3144
+ const native = loadNativeParser();
3145
+ if (!native) {
3146
+ yield* fallback(options2);
3147
+ return;
3148
+ }
3149
+ let parser11;
3150
+ try {
3151
+ parser11 = new native.NativeParser(
3152
+ adapter.formatId,
3153
+ options2.filePath,
3154
+ options2.formatOptions ? JSON.stringify(options2.formatOptions) : void 0
3155
+ );
3156
+ } catch (error) {
3157
+ options2.onLog?.(
3158
+ "warn",
3159
+ `[NativeParser] Failed to create Rust parser, falling back to TS parser: ${error instanceof Error ? error.message : String(error)}`
3160
+ );
3161
+ yield* fallback(options2);
3162
+ return;
3163
+ }
3164
+ yield* pumpNativeParser(adapter, options2, parser11, fallback);
3165
+ };
3166
+ }
3167
+
3168
+ // ../../packages/parser/src/native/chatlab-native.ts
3169
+ function toParsedMember(member, fromHead) {
3170
+ if (!fromHead) {
3171
+ return {
3172
+ platformId: member.platformId,
3173
+ accountName: member.accountName,
3174
+ groupNickname: member.groupNickname
3175
+ };
3176
+ }
3177
+ return {
3178
+ platformId: member.platformId,
3179
+ accountName: member.accountName,
3180
+ groupNickname: member.groupNickname,
3181
+ avatar: member.avatar,
3182
+ // Role objects pass through with their original key set ({id} or {id, name}).
3183
+ roles: member.roles?.map((role) => role.name !== void 0 ? { id: role.id, name: role.name } : { id: role.id })
3184
+ };
3185
+ }
3186
+ var chatlabAdapter = {
3187
+ formatId: "chatlab",
3188
+ label: "ChatLab JSON",
3189
+ mapMeta(metaJson) {
3190
+ const meta = metaJson;
3191
+ return {
3192
+ name: meta.name,
3193
+ platform: meta.platform,
3194
+ type: meta.chatType,
3195
+ groupId: meta.groupId ?? void 0,
3196
+ groupAvatar: meta.groupAvatar ?? void 0
3197
+ };
3198
+ },
3199
+ mapMembers(members, metaJson) {
3200
+ const fromHead = metaJson.membersFromHead;
3201
+ return members.map((member) => toParsedMember(member, fromHead));
3202
+ },
3203
+ mapMessage(message) {
3204
+ return {
3205
+ senderPlatformId: message.senderPlatformId,
3206
+ senderAccountName: message.senderAccountName,
3207
+ senderGroupNickname: message.senderGroupNickname,
3208
+ // The kernel rejects non-number timestamps, so this is always set.
3209
+ timestamp: message.timestamp,
3210
+ type: message.messageType,
3211
+ // The kernel rejects a missing content key, so undefined here always
3212
+ // means the source value was JSON null.
3213
+ content: message.content ?? null,
3214
+ platformMessageId: message.platformMessageId,
3215
+ replyToMessageId: message.replyToMessageId
3216
+ };
3217
+ }
3218
+ };
3219
+ function withNativeChatlab(fallback) {
3220
+ return createNativeFirstParser(chatlabAdapter, fallback);
3221
+ }
3222
+
3223
+ // ../../packages/parser/src/formats/chatlab.ts
3151
3224
  var { parser } = import_stream_json.default;
3152
3225
  var { pick } = import_Pick.default;
3153
3226
  var { streamValues } = import_StreamValues.default;
3154
3227
  var { chain } = import_stream_chain.default;
3155
3228
  function extractNameFromFilePath(filePath) {
3156
- const basename13 = path3.basename(filePath);
3229
+ const basename13 = path2.basename(filePath);
3157
3230
  const name = basename13.replace(/\.json$/i, "");
3158
3231
  return name || "\u672A\u77E5\u7FA4\u804A";
3159
3232
  }
@@ -3242,7 +3315,7 @@ async function* parseChatLab(options2) {
3242
3315
  const memberMapFromMessages = /* @__PURE__ */ new Map();
3243
3316
  const messageBatch = [];
3244
3317
  await new Promise((resolve2, reject) => {
3245
- const readStream = fs4.createReadStream(filePath, { encoding: "utf-8" });
3318
+ const readStream = fs3.createReadStream(filePath, { encoding: "utf-8" });
3246
3319
  readStream.on("data", (chunk) => {
3247
3320
  bytesRead += typeof chunk === "string" ? Buffer.byteLength(chunk) : chunk.length;
3248
3321
  });
@@ -3311,9 +3384,10 @@ async function* parseChatLab(options2) {
3311
3384
  }
3312
3385
  };
3313
3386
  }
3387
+ var parseChatLabAccelerated = withNativeChatlab(parseChatLab);
3314
3388
  var parser_ = {
3315
3389
  feature,
3316
- parse: parseChatLab
3390
+ parse: parseChatLabAccelerated
3317
3391
  };
3318
3392
  var module_ = {
3319
3393
  feature,
@@ -3322,11 +3396,11 @@ var module_ = {
3322
3396
  var chatlab_default = module_;
3323
3397
 
3324
3398
  // ../../packages/parser/src/formats/chatlab-jsonl.ts
3325
- import * as fs5 from "fs";
3399
+ import * as fs4 from "fs";
3326
3400
  import * as readline from "readline";
3327
- import * as path4 from "path";
3401
+ import * as path3 from "path";
3328
3402
  function extractNameFromFilePath2(filePath) {
3329
- const basename13 = path4.basename(filePath);
3403
+ const basename13 = path3.basename(filePath);
3330
3404
  const name = basename13.replace(/\.jsonl$/i, "");
3331
3405
  return name || "\u672A\u77E5\u7FA4\u804A";
3332
3406
  }
@@ -3367,7 +3441,7 @@ async function* parseChatLabJsonl(options2) {
3367
3441
  const messageBatch = [];
3368
3442
  let meta = null;
3369
3443
  let headerParsed = false;
3370
- const fileStream = fs5.createReadStream(filePath, { encoding: "utf-8" });
3444
+ const fileStream = fs4.createReadStream(filePath, { encoding: "utf-8" });
3371
3445
  const rl = readline.createInterface({
3372
3446
  input: fileStream,
3373
3447
  crlfDelay: Infinity
@@ -3490,16 +3564,16 @@ var import_stream_json3 = __toESM(require_stream_json(), 1);
3490
3564
  var import_Pick3 = __toESM(require_Pick(), 1);
3491
3565
  var import_StreamValues3 = __toESM(require_StreamValues(), 1);
3492
3566
  var import_stream_chain3 = __toESM(require_stream_chain(), 1);
3493
- import * as fs7 from "fs";
3494
- import * as path6 from "path";
3567
+ import * as fs6 from "fs";
3568
+ import * as path5 from "path";
3495
3569
 
3496
3570
  // ../../packages/parser/src/formats/shuakami-qq-preprocessor.ts
3497
3571
  var import_stream_json2 = __toESM(require_stream_json(), 1);
3498
3572
  var import_Pick2 = __toESM(require_Pick(), 1);
3499
3573
  var import_StreamValues2 = __toESM(require_StreamValues(), 1);
3500
3574
  var import_stream_chain2 = __toESM(require_stream_chain(), 1);
3501
- import * as fs6 from "fs";
3502
- import * as path5 from "path";
3575
+ import * as fs5 from "fs";
3576
+ import * as path4 from "path";
3503
3577
  import * as os from "os";
3504
3578
  var { parser: parser3 } = import_stream_json2.default;
3505
3579
  var { pick: pick2 } = import_Pick2.default;
@@ -3507,11 +3581,11 @@ var { streamValues: streamValues2 } = import_StreamValues2.default;
3507
3581
  var { chain: chain2 } = import_stream_chain2.default;
3508
3582
  var PREPROCESS_THRESHOLD = 50 * 1024 * 1024;
3509
3583
  function getTempDir() {
3510
- return path5.join(os.tmpdir(), "chatlab");
3584
+ return path4.join(os.tmpdir(), "chatlab");
3511
3585
  }
3512
3586
  function ensureDir(dir) {
3513
- if (!fs6.existsSync(dir)) {
3514
- fs6.mkdirSync(dir, { recursive: true });
3587
+ if (!fs5.existsSync(dir)) {
3588
+ fs5.mkdirSync(dir, { recursive: true });
3515
3589
  }
3516
3590
  }
3517
3591
  function extractJsonObject(content, key) {
@@ -3553,12 +3627,12 @@ function extractJsonObject(content, key) {
3553
3627
  }
3554
3628
  function readAvatarsFromFile(filePath) {
3555
3629
  try {
3556
- const stats = fs6.statSync(filePath);
3630
+ const stats = fs5.statSync(filePath);
3557
3631
  const tailSize = Math.min(stats.size, 5e6);
3558
- const fd = fs6.openSync(filePath, "r");
3632
+ const fd = fs5.openSync(filePath, "r");
3559
3633
  const buffer = Buffer.alloc(tailSize);
3560
- fs6.readSync(fd, buffer, 0, tailSize, stats.size - tailSize);
3561
- fs6.closeSync(fd);
3634
+ fs5.readSync(fd, buffer, 0, tailSize, stats.size - tailSize);
3635
+ fs5.closeSync(fd);
3562
3636
  const tailContent = buffer.toString("utf-8");
3563
3637
  return extractJsonObject(tailContent, "avatars");
3564
3638
  } catch {
@@ -3617,15 +3691,15 @@ async function preprocessQQJson(inputPath, onProgress) {
3617
3691
  let messagesProcessed = 0;
3618
3692
  const tempDir = getTempDir();
3619
3693
  ensureDir(tempDir);
3620
- const outputFilename = `slim_${Date.now()}_${path5.basename(inputPath)}`;
3621
- const outputPath = path5.join(tempDir, outputFilename);
3694
+ const outputFilename = `slim_${Date.now()}_${path4.basename(inputPath)}`;
3695
+ const outputPath = path4.join(tempDir, outputFilename);
3622
3696
  onProgress?.(createProgress("parsing", 0, totalBytes, 0, ""));
3623
3697
  const avatarsStr = readAvatarsFromFile(inputPath);
3624
3698
  return new Promise((resolve2, reject) => {
3625
3699
  const headChunks = [];
3626
3700
  let headSize = 0;
3627
3701
  const maxHeadSize = 1e5;
3628
- const headStream = fs6.createReadStream(inputPath, { encoding: "utf-8" });
3702
+ const headStream = fs5.createReadStream(inputPath, { encoding: "utf-8" });
3629
3703
  let chatInfo = { name: "\u672A\u77E5\u7FA4\u804A", type: "group" };
3630
3704
  let metadata;
3631
3705
  let statistics;
@@ -3662,8 +3736,8 @@ async function preprocessQQJson(inputPath, onProgress) {
3662
3736
  } catch {
3663
3737
  }
3664
3738
  onProgress?.(createProgress("parsing", 0, totalBytes, 0, ""));
3665
- const readStream = fs6.createReadStream(inputPath, { encoding: "utf-8" });
3666
- const writeStream = fs6.createWriteStream(outputPath, { encoding: "utf-8" });
3739
+ const readStream = fs5.createReadStream(inputPath, { encoding: "utf-8" });
3740
+ const writeStream = fs5.createWriteStream(outputPath, { encoding: "utf-8" });
3667
3741
  readStream.on("data", (chunk) => {
3668
3742
  bytesRead += typeof chunk === "string" ? Buffer.byteLength(chunk) : chunk.length;
3669
3743
  });
@@ -3708,8 +3782,8 @@ async function preprocessQQJson(inputPath, onProgress) {
3708
3782
  });
3709
3783
  pipeline2.on("error", (err) => {
3710
3784
  writeStream.destroy();
3711
- if (fs6.existsSync(outputPath)) {
3712
- fs6.unlinkSync(outputPath);
3785
+ if (fs5.existsSync(outputPath)) {
3786
+ fs5.unlinkSync(outputPath);
3713
3787
  }
3714
3788
  onProgress?.(createProgress("error", bytesRead, totalBytes, messagesProcessed, err.message));
3715
3789
  reject(err);
@@ -3720,8 +3794,8 @@ async function preprocessQQJson(inputPath, onProgress) {
3720
3794
  }
3721
3795
  function cleanupTempFile(filePath) {
3722
3796
  try {
3723
- if (fs6.existsSync(filePath) && filePath.includes(getTempDir())) {
3724
- fs6.unlinkSync(filePath);
3797
+ if (fs5.existsSync(filePath) && filePath.includes(getTempDir())) {
3798
+ fs5.unlinkSync(filePath);
3725
3799
  }
3726
3800
  } catch {
3727
3801
  }
@@ -3788,7 +3862,7 @@ function extractJsonObject2(content, key) {
3788
3862
  return null;
3789
3863
  }
3790
3864
  function extractNameFromFilePath3(filePath) {
3791
- const basename13 = path6.basename(filePath);
3865
+ const basename13 = path5.basename(filePath);
3792
3866
  return basename13.replace(/\.json$/i, "") || "\u672A\u77E5\u7FA4\u804A";
3793
3867
  }
3794
3868
  function countSenders(content) {
@@ -3873,7 +3947,7 @@ async function* parseV4(options2) {
3873
3947
  const memberMap = /* @__PURE__ */ new Map();
3874
3948
  const messageBatch = [];
3875
3949
  await new Promise((resolve2, reject) => {
3876
- const readStream = fs7.createReadStream(filePath, { encoding: "utf-8" });
3950
+ const readStream = fs6.createReadStream(filePath, { encoding: "utf-8" });
3877
3951
  readStream.on("data", (chunk) => {
3878
3952
  bytesRead += typeof chunk === "string" ? Buffer.byteLength(chunk) : chunk.length;
3879
3953
  });
@@ -3931,12 +4005,12 @@ async function* parseV4(options2) {
3931
4005
  });
3932
4006
  const avatarsMap = /* @__PURE__ */ new Map();
3933
4007
  try {
3934
- const stats = fs7.statSync(filePath);
4008
+ const stats = fs6.statSync(filePath);
3935
4009
  const tailSize = Math.min(stats.size, 5e6);
3936
- const fd = fs7.openSync(filePath, "r");
4010
+ const fd = fs6.openSync(filePath, "r");
3937
4011
  const buffer = Buffer.alloc(tailSize);
3938
- fs7.readSync(fd, buffer, 0, tailSize, stats.size - tailSize);
3939
- fs7.closeSync(fd);
4012
+ fs6.readSync(fd, buffer, 0, tailSize, stats.size - tailSize);
4013
+ fs6.closeSync(fd);
3940
4014
  const tailContent = buffer.toString("utf-8");
3941
4015
  const avatarsStr = extractJsonObject2(tailContent, "avatars");
3942
4016
  if (avatarsStr) {
@@ -3988,8 +4062,8 @@ var module_3 = {
3988
4062
  var shuakami_qq_exporter_default = module_3;
3989
4063
 
3990
4064
  // ../../packages/parser/src/formats/shuakami-qq-exporter-chunked.ts
3991
- import * as fs8 from "fs";
3992
- import * as path7 from "path";
4065
+ import * as fs7 from "fs";
4066
+ import * as path6 from "path";
3993
4067
  import * as readline2 from "readline";
3994
4068
  var feature4 = {
3995
4069
  id: "shuakami-qq-exporter-chunked",
@@ -4038,7 +4112,7 @@ function convertMessageType2(msgType, content, isRecalled) {
4038
4112
  return 0 /* TEXT */;
4039
4113
  }
4040
4114
  function readManifest(manifestPath) {
4041
- const content = fs8.readFileSync(manifestPath, "utf-8");
4115
+ const content = fs7.readFileSync(manifestPath, "utf-8");
4042
4116
  return JSON.parse(content);
4043
4117
  }
4044
4118
  function getChunkRelativePath(chunk) {
@@ -4056,9 +4130,9 @@ function calculateTotalBytes(manifest, baseDir) {
4056
4130
  let total = 0;
4057
4131
  for (const chunk of manifest.chunked.chunks) {
4058
4132
  const relativePath = getChunkRelativePath(chunk);
4059
- const chunkPath = path7.join(baseDir, relativePath);
4060
- if (fs8.existsSync(chunkPath)) {
4061
- total += fs8.statSync(chunkPath).size;
4133
+ const chunkPath = path6.join(baseDir, relativePath);
4134
+ if (fs7.existsSync(chunkPath)) {
4135
+ total += fs7.statSync(chunkPath).size;
4062
4136
  }
4063
4137
  }
4064
4138
  return total;
@@ -4066,10 +4140,10 @@ function calculateTotalBytes(manifest, baseDir) {
4066
4140
  function readAvatars(baseDir, avatarsInfo) {
4067
4141
  const avatarsMap = /* @__PURE__ */ new Map();
4068
4142
  if (!avatarsInfo?.file) return avatarsMap;
4069
- const avatarsPath = path7.join(baseDir, avatarsInfo.file);
4070
- if (!fs8.existsSync(avatarsPath)) return avatarsMap;
4143
+ const avatarsPath = path6.join(baseDir, avatarsInfo.file);
4144
+ if (!fs7.existsSync(avatarsPath)) return avatarsMap;
4071
4145
  try {
4072
- const content = fs8.readFileSync(avatarsPath, "utf-8");
4146
+ const content = fs7.readFileSync(avatarsPath, "utf-8");
4073
4147
  const avatars = JSON.parse(content);
4074
4148
  for (const [uin, avatar] of Object.entries(avatars)) {
4075
4149
  if (avatar && typeof avatar === "string" && avatar.startsWith("data:image/")) {
@@ -4087,7 +4161,7 @@ async function* readJsonlFile(filePath, onParseError) {
4087
4161
  let parseErrorCount = 0;
4088
4162
  const MAX_PARSE_ERRORS_TO_LOG = 10;
4089
4163
  try {
4090
- fileStream = fs8.createReadStream(filePath, { encoding: "utf-8" });
4164
+ fileStream = fs7.createReadStream(filePath, { encoding: "utf-8" });
4091
4165
  rl = readline2.createInterface({
4092
4166
  input: fileStream,
4093
4167
  crlfDelay: Infinity
@@ -4118,7 +4192,7 @@ async function* readJsonlFile(filePath, onParseError) {
4118
4192
  async function* parseChunkedJsonl(options2) {
4119
4193
  const { filePath, batchSize = 5e3, onProgress, onLog } = options2;
4120
4194
  const manifestPath = filePath;
4121
- const baseDir = path7.dirname(manifestPath);
4195
+ const baseDir = path6.dirname(manifestPath);
4122
4196
  onLog?.("info", `\u5F00\u59CB\u89E3\u6790 manifest: ${manifestPath}`);
4123
4197
  onLog?.("info", `\u57FA\u7840\u76EE\u5F55: ${baseDir}`);
4124
4198
  let manifest;
@@ -4172,13 +4246,13 @@ async function* parseChunkedJsonl(options2) {
4172
4246
  for (let chunkIndex = 0; chunkIndex < manifest.chunked.chunks.length; chunkIndex++) {
4173
4247
  const chunkInfo = manifest.chunked.chunks[chunkIndex];
4174
4248
  const relativePath = getChunkRelativePath(chunkInfo);
4175
- const chunkPath = path7.join(baseDir, relativePath);
4249
+ const chunkPath = path6.join(baseDir, relativePath);
4176
4250
  const chunkMessageCount = getChunkMessageCount(chunkInfo);
4177
- if (!fs8.existsSync(chunkPath)) {
4251
+ if (!fs7.existsSync(chunkPath)) {
4178
4252
  onLog?.("error", `\u5206\u5757\u6587\u4EF6\u4E0D\u5B58\u5728: ${chunkPath}`);
4179
4253
  continue;
4180
4254
  }
4181
- const chunkSize = fs8.statSync(chunkPath).size;
4255
+ const chunkSize = fs7.statSync(chunkPath).size;
4182
4256
  let chunkMessagesRead = 0;
4183
4257
  let chunkParseErrors = 0;
4184
4258
  onLog?.(
@@ -4300,8 +4374,49 @@ var import_stream_json4 = __toESM(require_stream_json(), 1);
4300
4374
  var import_Pick4 = __toESM(require_Pick(), 1);
4301
4375
  var import_StreamValues4 = __toESM(require_StreamValues(), 1);
4302
4376
  var import_stream_chain4 = __toESM(require_stream_chain(), 1);
4303
- import * as fs9 from "fs";
4304
- import * as path8 from "path";
4377
+ import * as fs8 from "fs";
4378
+ import * as path7 from "path";
4379
+
4380
+ // ../../packages/parser/src/native/weflow-native.ts
4381
+ var weflowAdapter = {
4382
+ formatId: "weflow",
4383
+ label: "WeFlow export",
4384
+ mapMeta(metaJson) {
4385
+ const meta = metaJson;
4386
+ return {
4387
+ name: meta.name,
4388
+ platform: KNOWN_PLATFORMS.WECHAT,
4389
+ type: meta.chatType === "private" ? "private" /* PRIVATE */ : "group" /* GROUP */,
4390
+ groupId: meta.groupId ?? void 0,
4391
+ groupAvatar: meta.groupAvatar ?? void 0,
4392
+ ownerId: meta.ownerId ?? void 0
4393
+ };
4394
+ },
4395
+ mapMembers(members) {
4396
+ return members.map((member) => ({
4397
+ platformId: member.platformId,
4398
+ accountName: member.accountName,
4399
+ avatar: member.avatar
4400
+ }));
4401
+ },
4402
+ mapMessage(message) {
4403
+ return {
4404
+ // The kernel always emits an id string (String(localId), JS semantics).
4405
+ platformMessageId: message.platformMessageId,
4406
+ senderPlatformId: message.senderPlatformId,
4407
+ senderAccountName: message.senderAccountName,
4408
+ // WeFlow has no separate group nickname field (same as the TS parser).
4409
+ senderGroupNickname: void 0,
4410
+ // The TS parser passes createTime through as-is, including null.
4411
+ timestamp: message.timestamp ?? null,
4412
+ type: message.messageType,
4413
+ content: message.content ?? null
4414
+ };
4415
+ }
4416
+ };
4417
+ function withNativeWeflow(fallback) {
4418
+ return createNativeFirstParser(weflowAdapter, fallback);
4419
+ }
4305
4420
 
4306
4421
  // ../../packages/parser/src/formats/weflow-preprocessor.ts
4307
4422
  var weflowPreprocessor = {
@@ -4333,7 +4448,7 @@ var { pick: pick4 } = import_Pick4.default;
4333
4448
  var { streamValues: streamValues4 } = import_StreamValues4.default;
4334
4449
  var { chain: chain4 } = import_stream_chain4.default;
4335
4450
  function extractNameFromFilePath4(filePath) {
4336
- const basename13 = path8.basename(filePath);
4451
+ const basename13 = path7.basename(filePath);
4337
4452
  const name = basename13.replace(/\.json$/i, "");
4338
4453
  return name || "\u672A\u77E5\u804A\u5929";
4339
4454
  }
@@ -4402,7 +4517,7 @@ async function* parseWeFlow(options2) {
4402
4517
  let session = null;
4403
4518
  try {
4404
4519
  await new Promise((resolve2) => {
4405
- const sessionStream = fs9.createReadStream(filePath, { encoding: "utf-8" });
4520
+ const sessionStream = fs8.createReadStream(filePath, { encoding: "utf-8" });
4406
4521
  let sessionContent = "";
4407
4522
  let inSession = false;
4408
4523
  let braceDepth = 0;
@@ -4525,7 +4640,7 @@ async function* parseWeFlow(options2) {
4525
4640
  if (avatarsMap.size === 0) {
4526
4641
  try {
4527
4642
  await new Promise((resolve2) => {
4528
- const avatarStream = fs9.createReadStream(filePath, { encoding: "utf-8" });
4643
+ const avatarStream = fs8.createReadStream(filePath, { encoding: "utf-8" });
4529
4644
  let avatarsContent = "";
4530
4645
  let inAvatars = false;
4531
4646
  let braceDepth = 0;
@@ -4592,7 +4707,7 @@ async function* parseWeFlow(options2) {
4592
4707
  let ownerId;
4593
4708
  try {
4594
4709
  await new Promise((resolve2) => {
4595
- const scanStream = fs9.createReadStream(filePath, { encoding: "utf-8" });
4710
+ const scanStream = fs8.createReadStream(filePath, { encoding: "utf-8" });
4596
4711
  const scanPipeline = chain4([scanStream, parser5(), pick4({ filter: /^messages\.\d+$/ }), streamValues4()]);
4597
4712
  scanPipeline.on("data", ({ value }) => {
4598
4713
  if (value.isSend === 1 && value.senderUsername && !value.senderUsername.endsWith("@chatroom")) {
@@ -4618,7 +4733,7 @@ async function* parseWeFlow(options2) {
4618
4733
  const memberMap = /* @__PURE__ */ new Map();
4619
4734
  const messageBatch = [];
4620
4735
  await new Promise((resolve2, reject) => {
4621
- const readStream = fs9.createReadStream(filePath, { encoding: "utf-8" });
4736
+ const readStream = fs8.createReadStream(filePath, { encoding: "utf-8" });
4622
4737
  readStream.on("data", (chunk) => {
4623
4738
  bytesRead += typeof chunk === "string" ? Buffer.byteLength(chunk) : chunk.length;
4624
4739
  });
@@ -4713,9 +4828,10 @@ async function* parseWeFlow(options2) {
4713
4828
  data: { messageCount: messagesProcessed, memberCount: memberMap.size }
4714
4829
  };
4715
4830
  }
4831
+ var parseWeFlowAccelerated = withNativeWeflow(parseWeFlow);
4716
4832
  var parser_4 = {
4717
4833
  feature: feature5,
4718
- parse: parseWeFlow
4834
+ parse: parseWeFlowAccelerated
4719
4835
  };
4720
4836
  var module_5 = {
4721
4837
  feature: feature5,
@@ -4740,7 +4856,7 @@ var feature6 = {
4740
4856
  };
4741
4857
  var parser_5 = {
4742
4858
  feature: feature6,
4743
- parse: parseWeFlow
4859
+ parse: parseWeFlowAccelerated
4744
4860
  };
4745
4861
  var module_6 = {
4746
4862
  feature: feature6,
@@ -4754,7 +4870,7 @@ var import_stream_json5 = __toESM(require_stream_json(), 1);
4754
4870
  var import_Pick5 = __toESM(require_Pick(), 1);
4755
4871
  var import_StreamValues5 = __toESM(require_StreamValues(), 1);
4756
4872
  var import_stream_chain5 = __toESM(require_stream_chain(), 1);
4757
- import * as fs10 from "fs";
4873
+ import * as fs9 from "fs";
4758
4874
  var { parser: parser6 } = import_stream_json5.default;
4759
4875
  var { pick: pick5 } = import_Pick5.default;
4760
4876
  var { streamValues: streamValues5 } = import_StreamValues5.default;
@@ -4904,7 +5020,7 @@ async function* parseDiscordExporter(options2) {
4904
5020
  const memberMap = /* @__PURE__ */ new Map();
4905
5021
  const messageBatch = [];
4906
5022
  await new Promise((resolve2, reject) => {
4907
- const readStream = fs10.createReadStream(filePath, { encoding: "utf-8" });
5023
+ const readStream = fs9.createReadStream(filePath, { encoding: "utf-8" });
4908
5024
  readStream.on("data", (chunk) => {
4909
5025
  bytesRead += typeof chunk === "string" ? Buffer.byteLength(chunk) : chunk.length;
4910
5026
  });
@@ -5020,7 +5136,7 @@ var import_stream_chain6 = __toESM(require_stream_chain(), 1);
5020
5136
  var import_stream_json6 = __toESM(require_stream_json(), 1);
5021
5137
  var import_Pick6 = __toESM(require_Pick(), 1);
5022
5138
  var import_StreamValues6 = __toESM(require_StreamValues(), 1);
5023
- import * as fs11 from "fs";
5139
+ import * as fs10 from "fs";
5024
5140
 
5025
5141
  // ../../packages/parser/src/formats/utils/telegram-utils.ts
5026
5142
  function mapChatType(telegramType) {
@@ -5106,7 +5222,7 @@ var feature8 = {
5106
5222
  async function scanChats(filePath) {
5107
5223
  const chats = [];
5108
5224
  return new Promise((resolve2, reject) => {
5109
- const readStream = fs11.createReadStream(filePath, { encoding: "utf-8" });
5225
+ const readStream = fs10.createReadStream(filePath, { encoding: "utf-8" });
5110
5226
  const pipeline2 = chain6([readStream, parser7(), pick6({ filter: /^chats\.list\.\d+$/ }), streamValues6()]);
5111
5227
  pipeline2.on("data", ({ value }) => {
5112
5228
  const chat = value;
@@ -5139,7 +5255,7 @@ async function* parseTelegram(options2) {
5139
5255
  onLog?.("info", `\u76EE\u6807\u804A\u5929\u7D22\u5F15: ${chatIndex}`);
5140
5256
  const chatPathFilter = `chats.list.${chatIndex}`;
5141
5257
  const chatData = await new Promise((resolve2, reject) => {
5142
- const readStream = fs11.createReadStream(filePath, { encoding: "utf-8" });
5258
+ const readStream = fs10.createReadStream(filePath, { encoding: "utf-8" });
5143
5259
  readStream.on("data", (chunk) => {
5144
5260
  bytesRead += typeof chunk === "string" ? Buffer.byteLength(chunk) : chunk.length;
5145
5261
  });
@@ -5247,7 +5363,7 @@ var telegram_native_default = module_8;
5247
5363
  var import_stream_chain7 = __toESM(require_stream_chain(), 1);
5248
5364
  var import_stream_json7 = __toESM(require_stream_json(), 1);
5249
5365
  var import_StreamValues7 = __toESM(require_StreamValues(), 1);
5250
- import * as fs12 from "fs";
5366
+ import * as fs11 from "fs";
5251
5367
  var { chain: chain7 } = import_stream_chain7.default;
5252
5368
  var { parser: parser8 } = import_stream_json7.default;
5253
5369
  var { streamValues: streamValues7 } = import_StreamValues7.default;
@@ -5277,7 +5393,7 @@ async function* parseTelegramSingle(options2) {
5277
5393
  onProgress?.(initialProgress);
5278
5394
  onLog?.("info", `\u5F00\u59CB\u89E3\u6790 Telegram \u5355\u804A\u5929 JSON \u6587\u4EF6\uFF0C\u5927\u5C0F: ${(totalBytes / 1024 / 1024).toFixed(2)} MB`);
5279
5395
  const chatData = await new Promise((resolve2, reject) => {
5280
- const readStream = fs12.createReadStream(filePath, { encoding: "utf-8" });
5396
+ const readStream = fs11.createReadStream(filePath, { encoding: "utf-8" });
5281
5397
  const pipeline2 = chain7([readStream, parser8(), streamValues7()]);
5282
5398
  let found = false;
5283
5399
  pipeline2.on("data", ({ value }) => {
@@ -5375,11 +5491,11 @@ var module_9 = {
5375
5491
  var telegram_native_single_default = module_9;
5376
5492
 
5377
5493
  // ../../packages/parser/src/formats/whatsapp-native-txt.ts
5378
- import * as fs13 from "fs";
5379
- import * as path9 from "path";
5494
+ import * as fs12 from "fs";
5495
+ import * as path8 from "path";
5380
5496
  import * as readline3 from "readline";
5381
5497
  function extractNameFromFilePath5(filePath) {
5382
- const basename13 = path9.basename(filePath);
5498
+ const basename13 = path8.basename(filePath);
5383
5499
  const matchZhCn = basename13.match(/^与(.+?)的\s*WhatsApp\s*聊天\.txt$/i);
5384
5500
  if (matchZhCn) return matchZhCn[1].trim();
5385
5501
  const matchZhTw = basename13.match(/^與(.+?)的\s*WhatsApp\s*對話\.txt$/i);
@@ -5583,7 +5699,7 @@ async function* parseWhatsApp(options2) {
5583
5699
  messagesProcessed++;
5584
5700
  }
5585
5701
  };
5586
- const fileStream = fs13.createReadStream(filePath, { encoding: "utf-8" });
5702
+ const fileStream = fs12.createReadStream(filePath, { encoding: "utf-8" });
5587
5703
  const rl = readline3.createInterface({
5588
5704
  input: fileStream,
5589
5705
  crlfDelay: Infinity
@@ -5703,11 +5819,11 @@ var module_10 = {
5703
5819
  var whatsapp_native_txt_default = module_10;
5704
5820
 
5705
5821
  // ../../packages/parser/src/formats/qq-native-txt.ts
5706
- import * as fs14 from "fs";
5707
- import * as path10 from "path";
5822
+ import * as fs13 from "fs";
5823
+ import * as path9 from "path";
5708
5824
  import * as readline4 from "readline";
5709
5825
  function extractNameFromFilePath6(filePath) {
5710
- const basename13 = path10.basename(filePath);
5826
+ const basename13 = path9.basename(filePath);
5711
5827
  const name = basename13.replace(/\.txt$/i, "");
5712
5828
  return name || "\u672A\u77E5\u7FA4\u804A";
5713
5829
  }
@@ -5789,7 +5905,7 @@ async function* parseTxt(options2) {
5789
5905
  messagesProcessed++;
5790
5906
  }
5791
5907
  };
5792
- const fileStream = fs14.createReadStream(filePath, { encoding: "utf-8" });
5908
+ const fileStream = fs13.createReadStream(filePath, { encoding: "utf-8" });
5793
5909
  const rl = readline4.createInterface({
5794
5910
  input: fileStream,
5795
5911
  crlfDelay: Infinity
@@ -5889,8 +6005,8 @@ var module_11 = {
5889
6005
  var qq_native_txt_default = module_11;
5890
6006
 
5891
6007
  // ../../packages/parser/src/formats/instagram-native.ts
5892
- import * as fs15 from "fs";
5893
- import * as path11 from "path";
6008
+ import * as fs14 from "fs";
6009
+ import * as path10 from "path";
5894
6010
  var feature12 = {
5895
6011
  id: "instagram-native",
5896
6012
  name: "Instagram \u5B98\u65B9\u5BFC\u51FA",
@@ -5916,7 +6032,7 @@ function decodeInstagramText(text) {
5916
6032
  }
5917
6033
  }
5918
6034
  function extractNameFromFilePath7(filePath) {
5919
- const basename13 = path11.basename(filePath);
6035
+ const basename13 = path10.basename(filePath);
5920
6036
  return basename13.replace(/\.json$/i, "") || "\u672A\u77E5\u5BF9\u8BDD";
5921
6037
  }
5922
6038
  function isSystemMessage2(content) {
@@ -5985,7 +6101,7 @@ async function* parseInstagram(options2) {
5985
6101
  onLog?.("info", `\u5F00\u59CB\u89E3\u6790 Instagram \u804A\u5929\u8BB0\u5F55\uFF0C\u5927\u5C0F: ${(totalBytes / 1024 / 1024).toFixed(2)} MB`);
5986
6102
  let data;
5987
6103
  try {
5988
- const content = fs15.readFileSync(filePath, "utf-8");
6104
+ const content = fs14.readFileSync(filePath, "utf-8");
5989
6105
  data = JSON.parse(content);
5990
6106
  } catch (error) {
5991
6107
  const err = new Error(`\u65E0\u6CD5\u89E3\u6790 Instagram JSON \u6587\u4EF6: ${error}`);
@@ -6080,8 +6196,8 @@ var import_stream_chain8 = __toESM(require_stream_chain(), 1);
6080
6196
  var import_stream_json8 = __toESM(require_stream_json(), 1);
6081
6197
  var import_Pick7 = __toESM(require_Pick(), 1);
6082
6198
  var import_StreamValues8 = __toESM(require_StreamValues(), 1);
6083
- import * as fs16 from "fs";
6084
- import * as path12 from "path";
6199
+ import * as fs15 from "fs";
6200
+ import * as path11 from "path";
6085
6201
  var { chain: chain8 } = import_stream_chain8.default;
6086
6202
  var { parser: parser9 } = import_stream_json8.default;
6087
6203
  var { pick: pick7 } = import_Pick7.default;
@@ -6164,12 +6280,12 @@ function normalizeIdentity(user) {
6164
6280
  };
6165
6281
  }
6166
6282
  function readJson(filePath) {
6167
- return JSON.parse(fs16.readFileSync(filePath, "utf8"));
6283
+ return JSON.parse(fs15.readFileSync(filePath, "utf8"));
6168
6284
  }
6169
6285
  function resolveManifestFile(manifestDir, relativePath) {
6170
- const resolved = path12.resolve(manifestDir, relativePath);
6171
- const relative2 = path12.relative(manifestDir, resolved);
6172
- if (relative2.startsWith("..") || path12.isAbsolute(relative2)) {
6286
+ const resolved = path11.resolve(manifestDir, relativePath);
6287
+ const relative2 = path11.relative(manifestDir, resolved);
6288
+ if (relative2.startsWith("..") || path11.isAbsolute(relative2)) {
6173
6289
  throw new Error(`Google Chat manifest path escapes its directory: ${relativePath}`);
6174
6290
  }
6175
6291
  return resolved;
@@ -6182,7 +6298,7 @@ function readManifest2(filePath) {
6182
6298
  if (manifest.chatType !== "private" && manifest.chatType !== "group") {
6183
6299
  throw new Error("Invalid Google Chat chat type");
6184
6300
  }
6185
- const manifestDir = path12.dirname(filePath);
6301
+ const manifestDir = path11.dirname(filePath);
6186
6302
  return {
6187
6303
  manifest,
6188
6304
  userInfoPath: resolveManifestFile(manifestDir, manifest.userInfoFile),
@@ -6260,7 +6376,7 @@ async function* parseGoogleChat(options2) {
6260
6376
  });
6261
6377
  }
6262
6378
  yield { type: "members", data: Array.from(memberMap.values()) };
6263
- const readStream = fs16.createReadStream(messagesPath, { encoding: "utf8" });
6379
+ const readStream = fs15.createReadStream(messagesPath, { encoding: "utf8" });
6264
6380
  let bytesRead = 0;
6265
6381
  let messagesProcessed = 0;
6266
6382
  readStream.on("data", (chunk) => {
@@ -6313,8 +6429,8 @@ var module_13 = {
6313
6429
  var google_chat_takeout_default = module_13;
6314
6430
 
6315
6431
  // ../../packages/parser/src/formats/line-native-txt.ts
6316
- import * as fs17 from "fs";
6317
- import * as path13 from "path";
6432
+ import * as fs16 from "fs";
6433
+ import * as path12 from "path";
6318
6434
  var feature14 = {
6319
6435
  id: "line-native-txt",
6320
6436
  name: "LINE \u5B98\u65B9\u5BFC\u51FA TXT",
@@ -6342,7 +6458,7 @@ var feature14 = {
6342
6458
  }
6343
6459
  };
6344
6460
  function extractNameFromFilePath8(filePath) {
6345
- const basename13 = path13.basename(filePath, ".txt");
6461
+ const basename13 = path12.basename(filePath, ".txt");
6346
6462
  const name = basename13.replace(/^\[LINE\]\s*/i, "").trim();
6347
6463
  return name || "\u672A\u77E5\u804A\u5929";
6348
6464
  }
@@ -6522,7 +6638,7 @@ async function* parseLINE(options2) {
6522
6638
  yield { type: "progress", data: initialProgress };
6523
6639
  onProgress?.(initialProgress);
6524
6640
  onLog?.("info", `\u5F00\u59CB\u89E3\u6790 LINE \u5BFC\u51FA\u6587\u4EF6\uFF0C\u5927\u5C0F: ${(totalBytes / 1024 / 1024).toFixed(2)} MB`);
6525
- const content = fs17.readFileSync(filePath, "utf-8");
6641
+ const content = fs16.readFileSync(filePath, "utf-8");
6526
6642
  const lines = content.split("\n").map((line) => line.replace(/\r$/, ""));
6527
6643
  let currentDate = null;
6528
6644
  let chatName = extractNameFromFilePath8(filePath);
@@ -6830,12 +6946,12 @@ function findEntryFileInDirectory(dirPath, maxDepth = 1) {
6830
6946
  if (depth > maxDepth) return null;
6831
6947
  let entries;
6832
6948
  try {
6833
- entries = fs18.readdirSync(current, { withFileTypes: true });
6949
+ entries = fs17.readdirSync(current, { withFileTypes: true });
6834
6950
  } catch {
6835
6951
  return null;
6836
6952
  }
6837
6953
  for (const entry of entries) {
6838
- const full = path14.join(current, entry.name);
6954
+ const full = path13.join(current, entry.name);
6839
6955
  if (entry.isFile() && entry.name.endsWith(".json")) {
6840
6956
  const format = detectFormat(full);
6841
6957
  if (format) return full;
@@ -6843,7 +6959,7 @@ function findEntryFileInDirectory(dirPath, maxDepth = 1) {
6843
6959
  }
6844
6960
  for (const entry of entries) {
6845
6961
  if (entry.isDirectory()) {
6846
- const found = walk(path14.join(current, entry.name), depth + 1);
6962
+ const found = walk(path13.join(current, entry.name), depth + 1);
6847
6963
  if (found) return found;
6848
6964
  }
6849
6965
  }
@@ -6874,6 +6990,122 @@ async function streamParseFile(filePath, callbacks, formatId) {
6874
6990
  }
6875
6991
  }
6876
6992
 
6993
+ // ../../packages/node-runtime/src/import/native-parser-status.ts
6994
+ var logged = false;
6995
+ function logNativeParserStatus() {
6996
+ if (logged) return;
6997
+ logged = true;
6998
+ const status = getNativeParserStatus();
6999
+ if (status.available) {
7000
+ appLogger.info("import", "Native parser kernels available (Rust module loaded)");
7001
+ } else if (status.disabled) {
7002
+ appLogger.info("import", "Native parser kernels disabled via CHATLAB_DISABLE_NATIVE_PERF=1; using TS parsers");
7003
+ } else {
7004
+ appLogger.warn("import", "Native parser kernels unavailable; imports fall back to TS parsers", {
7005
+ reason: status.error ?? "unknown"
7006
+ });
7007
+ }
7008
+ }
7009
+
7010
+ // ../../packages/node-runtime/src/import/perf-logger.ts
7011
+ import * as fs18 from "fs";
7012
+ import * as path14 from "path";
7013
+ var LogLevel = /* @__PURE__ */ ((LogLevel2) => {
7014
+ LogLevel2["ERROR"] = "ERROR";
7015
+ LogLevel2["INFO"] = "INFO";
7016
+ return LogLevel2;
7017
+ })(LogLevel || {});
7018
+ var lastLogTime = Date.now();
7019
+ var lastMessageCount = 0;
7020
+ var currentLogFile = null;
7021
+ var errorCount = 0;
7022
+ function initPerfLog(sessionId, logDir) {
7023
+ try {
7024
+ if (!fs18.existsSync(logDir)) {
7025
+ fs18.mkdirSync(logDir, { recursive: true });
7026
+ }
7027
+ currentLogFile = path14.join(logDir, `import_${sessionId}_${Date.now()}.log`);
7028
+ fs18.writeFileSync(currentLogFile, `=== Import Log ===
7029
+ Start time: ${(/* @__PURE__ */ new Date()).toISOString()}
7030
+
7031
+ `, "utf-8");
7032
+ } catch {
7033
+ }
7034
+ }
7035
+ function logPerf(event, messagesProcessed, batchSize) {
7036
+ const now = Date.now();
7037
+ const duration = now - lastLogTime;
7038
+ const messagesDelta = messagesProcessed - lastMessageCount;
7039
+ const speed = duration > 0 ? Math.round(messagesDelta / duration * 1e3) : 0;
7040
+ let memory = 0;
7041
+ try {
7042
+ const used = process.memoryUsage();
7043
+ memory = Math.round(used.heapUsed / 1024 / 1024);
7044
+ } catch {
7045
+ }
7046
+ const logLine = `[${(/* @__PURE__ */ new Date()).toISOString()}] ${event} | messages: ${messagesProcessed.toLocaleString()} | elapsed: ${duration}ms | speed: ${speed.toLocaleString()}/s | memory: ${memory}MB` + (batchSize ? ` | batch: ${batchSize}` : "") + "\n";
7047
+ if (currentLogFile) {
7048
+ try {
7049
+ fs18.appendFileSync(currentLogFile, logLine, "utf-8");
7050
+ } catch {
7051
+ }
7052
+ }
7053
+ lastLogTime = now;
7054
+ lastMessageCount = messagesProcessed;
7055
+ }
7056
+ function logPerfDetail(detail) {
7057
+ if (currentLogFile) {
7058
+ try {
7059
+ fs18.appendFileSync(currentLogFile, ` ${detail}
7060
+ `, "utf-8");
7061
+ } catch {
7062
+ }
7063
+ }
7064
+ }
7065
+ function resetPerfLog() {
7066
+ lastLogTime = Date.now();
7067
+ lastMessageCount = 0;
7068
+ currentLogFile = null;
7069
+ errorCount = 0;
7070
+ }
7071
+ function getCurrentLogFile() {
7072
+ return currentLogFile;
7073
+ }
7074
+ function writeLogLine(level, message) {
7075
+ if (!currentLogFile) return;
7076
+ const logLine = `[${(/* @__PURE__ */ new Date()).toISOString()}] [${level}] ${message}
7077
+ `;
7078
+ try {
7079
+ fs18.appendFileSync(currentLogFile, logLine, "utf-8");
7080
+ } catch {
7081
+ }
7082
+ }
7083
+ function logError(message, error) {
7084
+ errorCount++;
7085
+ const errorDetail = error ? `: ${error.message}` : "";
7086
+ writeLogLine("ERROR" /* ERROR */, `${message}${errorDetail}`);
7087
+ }
7088
+ function logInfo(message) {
7089
+ writeLogLine("INFO" /* INFO */, message);
7090
+ }
7091
+ function getErrorCount() {
7092
+ return errorCount;
7093
+ }
7094
+ function logSummary(totalMessages, totalMembers) {
7095
+ if (!currentLogFile) return;
7096
+ const summary = `
7097
+ === Import Summary ===
7098
+ End time: ${(/* @__PURE__ */ new Date()).toISOString()}
7099
+ Total messages: ${totalMessages.toLocaleString()}
7100
+ Total members: ${totalMembers.toLocaleString()}
7101
+ Errors: ${errorCount}
7102
+ `;
7103
+ try {
7104
+ fs18.appendFileSync(currentLogFile, summary, "utf-8");
7105
+ } catch {
7106
+ }
7107
+ }
7108
+
6877
7109
  // ../../packages/node-runtime/src/nlp/word-frequency.ts
6878
7110
  function buildMessageQuery(timeFilter, memberId, excludeKeywords) {
6879
7111
  const conditions = [];
@@ -7180,6 +7412,7 @@ function searchByFts(db, keywords, limit = 1e3, offset = 0) {
7180
7412
  import * as fs20 from "fs";
7181
7413
  var BATCH_COMMIT_SIZE = 5e4;
7182
7414
  var CHECKPOINT_INTERVAL = 2e5;
7415
+ var yieldToEventLoop = () => new Promise((resolve2) => setImmediate(resolve2));
7183
7416
  function defaultGenerateSessionId() {
7184
7417
  const ts = Date.now();
7185
7418
  const rand = Math.random().toString(36).substring(2, 8);
@@ -7310,7 +7543,7 @@ async function streamImportSingle(filePath, deps, formatFeature, formatOptions,
7310
7543
  lastCheckpointCount = totalMessageCount;
7311
7544
  }
7312
7545
  onProgress({
7313
- stage: "importing",
7546
+ stage: "saving",
7314
7547
  bytesRead: 0,
7315
7548
  totalBytes: 0,
7316
7549
  messagesProcessed: totalMessageCount,
@@ -7482,13 +7715,14 @@ async function streamImportSingle(filePath, deps, formatFeature, formatOptions,
7482
7715
  inTransaction = false;
7483
7716
  }
7484
7717
  onProgress({
7485
- stage: "importing",
7718
+ stage: "saving",
7486
7719
  bytesRead: 0,
7487
7720
  totalBytes: 0,
7488
7721
  messagesProcessed: totalMessageCount,
7489
7722
  percentage: 100,
7490
7723
  message: ""
7491
7724
  });
7725
+ await yieldToEventLoop();
7492
7726
  logger?.perf("Writing nickname history", totalMessageCount);
7493
7727
  db.exec("BEGIN TRANSACTION");
7494
7728
  let historyCount = 0;
@@ -7504,13 +7738,14 @@ async function streamImportSingle(filePath, deps, formatFeature, formatOptions,
7504
7738
  db.exec("COMMIT");
7505
7739
  logger?.perf(`Nickname history written (${historyCount} entries)`, totalMessageCount);
7506
7740
  onProgress({
7507
- stage: "importing",
7741
+ stage: "indexing",
7508
7742
  bytesRead: 0,
7509
7743
  totalBytes: 0,
7510
7744
  messagesProcessed: totalMessageCount,
7511
7745
  percentage: 100,
7512
7746
  message: ""
7513
7747
  });
7748
+ await yieldToEventLoop();
7514
7749
  logger?.perf("Creating indexes", totalMessageCount);
7515
7750
  db.exec(CHAT_DB_INDEXES);
7516
7751
  logger?.perf("Indexes created", totalMessageCount);
@@ -7521,13 +7756,14 @@ async function streamImportSingle(filePath, deps, formatFeature, formatOptions,
7521
7756
  logger?.error("FTS index build failed (non-fatal)", ftsError instanceof Error ? ftsError : void 0);
7522
7757
  }
7523
7758
  onProgress({
7524
- stage: "importing",
7759
+ stage: "indexing",
7525
7760
  bytesRead: 0,
7526
7761
  totalBytes: 0,
7527
7762
  messagesProcessed: totalMessageCount,
7528
7763
  percentage: 100,
7529
7764
  message: ""
7530
7765
  });
7766
+ await yieldToEventLoop();
7531
7767
  doCheckpoint();
7532
7768
  logger?.perf("WAL checkpoint done", totalMessageCount);
7533
7769
  try {
@@ -16817,6 +17053,7 @@ __export(relationships_exports, {
16817
17053
  // ../../packages/node-runtime/src/semantic-index/config.ts
16818
17054
  import fs30 from "fs";
16819
17055
  import path27 from "path";
17056
+ var DEFAULT_SEMANTIC_INDEX_MODEL_DOWNLOAD_SOURCE = "huggingface";
16820
17057
  var SEMANTIC_INDEX_CONFIG_VERSION = 1;
16821
17058
  var SEARCH_MAX_RESULTS_DEFAULT = 10;
16822
17059
  var SEARCH_MAX_RESULTS_MIN = 5;
@@ -16831,7 +17068,7 @@ function defaultSemanticIndexConfig() {
16831
17068
  version: SEMANTIC_INDEX_CONFIG_VERSION,
16832
17069
  enabled: true,
16833
17070
  mode: "local",
16834
- local: { modelId: "" },
17071
+ local: { modelId: "", downloadSource: DEFAULT_SEMANTIC_INDEX_MODEL_DOWNLOAD_SOURCE },
16835
17072
  api: null,
16836
17073
  searchMaxResults: SEARCH_MAX_RESULTS_DEFAULT
16837
17074
  };
@@ -16876,7 +17113,10 @@ function normalizeConfig(raw) {
16876
17113
  // 旧配置无 enabled 字段时默认开启,保证既有用户功能不被意外关闭
16877
17114
  enabled: raw.enabled ?? true,
16878
17115
  mode,
16879
- local: { modelId: raw.local?.modelId ?? base.local.modelId },
17116
+ local: {
17117
+ modelId: raw.local?.modelId ?? base.local.modelId,
17118
+ downloadSource: raw.local?.downloadSource === "hf-mirror" ? "hf-mirror" : DEFAULT_SEMANTIC_INDEX_MODEL_DOWNLOAD_SOURCE
17119
+ },
16880
17120
  api,
16881
17121
  searchMaxResults: SEARCH_MAX_RESULTS_DEFAULT
16882
17122
  };
@@ -16926,7 +17166,7 @@ import path28 from "path";
16926
17166
 
16927
17167
  // ../../packages/node-runtime/src/semantic-index/chunker-config.ts
16928
17168
  import { createHash as createHash2 } from "crypto";
16929
- var CHUNKER_VERSION = "v1.2";
17169
+ var CHUNKER_VERSION = "v1.3";
16930
17170
  var STRATEGY_ID = "balanced";
16931
17171
  var DEFAULT_CHUNKER_CONFIG = {
16932
17172
  parentGapSeconds: 1800,
@@ -16974,6 +17214,44 @@ function parseParentBounds(parentId) {
16974
17214
  return { startMessageId, endMessageId };
16975
17215
  }
16976
17216
 
17217
+ // ../../packages/node-runtime/src/semantic-index/tokens.ts
17218
+ import { Buffer as Buffer2 } from "buffer";
17219
+ var UTF8_BYTES_PER_ESTIMATED_TOKEN = 4;
17220
+ function isCjkCharacter(char) {
17221
+ return /[\u3400-\u9fff\uf900-\ufaff]/u.test(char);
17222
+ }
17223
+ function estimateTokens2(text) {
17224
+ let cjk = 0;
17225
+ let other = 0;
17226
+ for (const ch of text) {
17227
+ if (isCjkCharacter(ch)) cjk++;
17228
+ else other++;
17229
+ }
17230
+ return cjk + Math.ceil(other / 4);
17231
+ }
17232
+ function clampEstimatedTokens(text, maxTokens) {
17233
+ const limit = Math.max(0, Math.floor(maxTokens));
17234
+ if (limit === 0) return "";
17235
+ const byteLimit = limit * UTF8_BYTES_PER_ESTIMATED_TOKEN;
17236
+ if (estimateTokens2(text) <= limit && Buffer2.byteLength(text, "utf8") <= byteLimit) return text;
17237
+ let cjk = 0;
17238
+ let other = 0;
17239
+ let bytes = 0;
17240
+ let end = 0;
17241
+ for (const char of text) {
17242
+ const isCjk = isCjkCharacter(char);
17243
+ const nextCjk = cjk + (isCjk ? 1 : 0);
17244
+ const nextOther = other + (isCjk ? 0 : 1);
17245
+ const nextBytes = bytes + Buffer2.byteLength(char, "utf8");
17246
+ if (nextCjk + Math.ceil(nextOther / 4) > limit || nextBytes > byteLimit) break;
17247
+ cjk = nextCjk;
17248
+ other = nextOther;
17249
+ bytes = nextBytes;
17250
+ end += char.length;
17251
+ }
17252
+ return text.slice(0, end);
17253
+ }
17254
+
16977
17255
  // ../../packages/node-runtime/src/semantic-index/embedding/api.ts
16978
17256
  var API_DOCUMENT_BATCH_SIZE = 8;
16979
17257
  var defaultFetchFn = (url, init) => fetch(url, init);
@@ -17013,7 +17291,19 @@ var OpenAICompatibleEmbeddingProvider = class {
17013
17291
  });
17014
17292
  if (!response.ok) {
17015
17293
  await response.text().catch(() => "");
17016
- throw new Error(`Embedding API request failed: ${response.status}`);
17294
+ const estimatedTokens = texts.map(estimateTokens2);
17295
+ const traceId = response.headers?.get("x-siliconcloud-trace-id")?.trim();
17296
+ const diagnostics = [
17297
+ `model=${this.config.model}`,
17298
+ `batchSize=${texts.length}`,
17299
+ `maxInputTokens=${Math.max(...estimatedTokens)}`,
17300
+ `totalInputTokens=${estimatedTokens.reduce((sum, count) => sum + count, 0)}`,
17301
+ `maxInputChars=${Math.max(...texts.map((text) => text.length))}`
17302
+ ];
17303
+ if (traceId) diagnostics.push(`traceId=${traceId}`);
17304
+ const error = new Error(`Embedding API request failed: ${response.status} (${diagnostics.join(", ")})`);
17305
+ appLogger.error("semantic-index", "embedding API request failed", error);
17306
+ throw error;
17017
17307
  }
17018
17308
  const payload = await response.json();
17019
17309
  const sorted = [...payload.data].sort((a, b) => a.index - b.index);
@@ -17045,6 +17335,11 @@ function clampTextChars(text, maxChars) {
17045
17335
  // ../../packages/node-runtime/src/semantic-index/embedding/local.ts
17046
17336
  var SOCKS_PROXY_PROTOCOLS = /* @__PURE__ */ new Set(["socks:", "socks4:", "socks5:"]);
17047
17337
  var LOCAL_ONNX_SESSION_OPTIONS = { intraOpNumThreads: 1, interOpNumThreads: 1 };
17338
+ var HUGGING_FACE_MODEL_HOST = "https://huggingface.co/";
17339
+ var HF_MIRROR_MODEL_HOST = "https://hf-mirror.com/";
17340
+ function resolveModelDownloadRemoteHost(source) {
17341
+ return source === "hf-mirror" ? HF_MIRROR_MODEL_HOST : HUGGING_FACE_MODEL_HOST;
17342
+ }
17048
17343
  async function createProxyFetch(proxyUrl) {
17049
17344
  const parsed = new URL(proxyUrl);
17050
17345
  if (SOCKS_PROXY_PROTOCOLS.has(parsed.protocol)) {
@@ -17059,8 +17354,15 @@ async function createProxyFetch(proxyUrl) {
17059
17354
  return undiciFetch(input, nextInit);
17060
17355
  });
17061
17356
  }
17062
- var defaultPipelineFactory = async ({ modelId, dtype, cacheDir, modelDownloadProxyUrl }) => {
17357
+ var defaultPipelineFactory = async ({
17358
+ modelId,
17359
+ dtype,
17360
+ cacheDir,
17361
+ modelDownloadProxyUrl,
17362
+ modelDownloadSource
17363
+ }) => {
17063
17364
  const transformers = await import("./transformers.node-PUMHAODN.mjs");
17365
+ transformers.env.remoteHost = resolveModelDownloadRemoteHost(modelDownloadSource);
17064
17366
  if (cacheDir) {
17065
17367
  transformers.env.cacheDir = cacheDir;
17066
17368
  transformers.env.allowRemoteModels = true;
@@ -17101,6 +17403,7 @@ var LocalEmbeddingProvider = class {
17101
17403
  dtype: this.profile.dtype,
17102
17404
  cacheDir: this.options.cacheDir,
17103
17405
  modelDownloadProxyUrl: this.options.modelDownloadProxyUrl,
17406
+ modelDownloadSource: this.options.modelDownloadSource,
17104
17407
  sessionOptions: LOCAL_ONNX_SESSION_OPTIONS
17105
17408
  }).catch((error) => {
17106
17409
  this.extractorPromise = null;
@@ -17194,6 +17497,7 @@ function createEmbedder(config, deps = {}) {
17194
17497
  return new LocalEmbeddingProvider(profile, {
17195
17498
  cacheDir: deps.modelsCacheDir,
17196
17499
  modelDownloadProxyUrl: deps.modelDownloadProxyUrl,
17500
+ modelDownloadSource: config.local.downloadSource,
17197
17501
  pipelineFactory: deps.localPipelineFactory
17198
17502
  });
17199
17503
  }
@@ -17745,19 +18049,6 @@ var SemanticIndexJobQueue = class {
17745
18049
 
17746
18050
  // ../../packages/node-runtime/src/semantic-index/chunker.ts
17747
18051
  import { createHash as createHash3 } from "crypto";
17748
-
17749
- // ../../packages/node-runtime/src/semantic-index/tokens.ts
17750
- function estimateTokens2(text) {
17751
- let cjk = 0;
17752
- let other = 0;
17753
- for (const ch of text) {
17754
- if (/[\u3400-\u9fff\uf900-\ufaff]/u.test(ch)) cjk++;
17755
- else other++;
17756
- }
17757
- return cjk + Math.ceil(other / 4);
17758
- }
17759
-
17760
- // ../../packages/node-runtime/src/semantic-index/chunker.ts
17761
18052
  var VOID_MESSAGE_TYPES = /* @__PURE__ */ new Set([
17762
18053
  1 /* IMAGE */,
17763
18054
  2 /* VOICE */,
@@ -17842,6 +18133,9 @@ function sumEffectiveChars(messages) {
17842
18133
  function sha256Hex2(input) {
17843
18134
  return createHash3("sha256").update(input, "utf-8").digest("hex");
17844
18135
  }
18136
+ var EMBEDDING_HEADER_BUDGET_RATIO = 0.25;
18137
+ var SOURCE_TITLE_HEADER_SHARE = 1 / 3;
18138
+ var SENDER_NAMES_BODY_BUDGET_RATIO = 0.1;
17845
18139
  function pad2(n) {
17846
18140
  return String(n).padStart(2, "0");
17847
18141
  }
@@ -17859,23 +18153,51 @@ function formatTimeRange2(startTs, endTs) {
17859
18153
  const sameDay = start.getFullYear() === end.getFullYear() && start.getMonth() === end.getMonth() && start.getDate() === end.getDate();
17860
18154
  return `${formatDateTime(startTs)} - ${sameDay ? formatTime2(endTs) : formatDateTime(endTs)}`;
17861
18155
  }
17862
- function buildEmbeddingInput(source, messages) {
18156
+ function resolveSenderNameTokenCap(nameTokenCounts, totalLimit) {
18157
+ const total = nameTokenCounts.reduce((sum, count) => sum + count, 0);
18158
+ if (total <= totalLimit) return null;
18159
+ const sorted = [...nameTokenCounts].sort((a, b) => a - b);
18160
+ let consumed = 0;
18161
+ for (let i = 0; i < sorted.length; i++) {
18162
+ const cap = Math.floor((totalLimit - consumed) / (sorted.length - i));
18163
+ if (sorted[i] > cap) return cap;
18164
+ consumed += sorted[i];
18165
+ }
18166
+ return null;
18167
+ }
18168
+ function buildEmbeddingInput(source, messages, maxTokens) {
18169
+ const limit = Math.max(0, Math.floor(maxTokens));
18170
+ if (limit === 0) return "";
17863
18171
  const effective = messages.filter((m) => !isSemanticVoid(m));
17864
18172
  const startTs = messages[0].ts;
17865
18173
  const endTs = messages[messages.length - 1].ts;
17866
- const lines = [];
18174
+ const headerLimit = Math.max(1, Math.floor(limit * EMBEDDING_HEADER_BUDGET_RATIO));
18175
+ const bodyFloor = Math.max(1, limit - headerLimit);
18176
+ const sourceTitleLimit = Math.max(1, Math.floor(headerLimit * SOURCE_TITLE_HEADER_SHARE));
18177
+ const senderNamesTotalLimit = Math.floor(bodyFloor * SENDER_NAMES_BODY_BUDGET_RATIO);
18178
+ const bodySenderNameCap = resolveSenderNameTokenCap(
18179
+ effective.map((m) => estimateTokens2(m.senderName)),
18180
+ senderNamesTotalLimit
18181
+ );
18182
+ const clampHeaderSenderName = (name) => clampEstimatedTokens(name, sourceTitleLimit);
18183
+ const clampBodySenderName = (name) => bodySenderNameCap === null ? name : clampEstimatedTokens(name, bodySenderNameCap);
18184
+ const headerLines = [];
17867
18185
  const kindLabel = source.kind === "group" ? "\u7FA4\u804A" : "\u79C1\u804A";
17868
- lines.push(`[\u6765\u6E90] ${source.title}\uFF08${kindLabel}\uFF09`);
17869
- lines.push(`[\u65F6\u95F4\u8303\u56F4] ${formatTimeRange2(startTs, endTs)}`);
18186
+ headerLines.push(`[\u6765\u6E90] ${clampEstimatedTokens(source.title, sourceTitleLimit)}\uFF08${kindLabel}\uFF09`);
18187
+ headerLines.push(`[\u65F6\u95F4\u8303\u56F4] ${formatTimeRange2(startTs, endTs)}`);
17870
18188
  if (source.kind === "group") {
17871
- const participants = [...new Set(effective.map((m) => m.senderName))];
17872
- lines.push(`[\u53C2\u4E0E\u8005] ${participants.join("\u3001")}`);
17873
- }
17874
- lines.push("");
17875
- for (const m of effective) {
17876
- lines.push(`${m.senderName}: ${(m.content ?? "").trim()}`);
17877
- }
17878
- return lines.join("\n");
18189
+ const participants = [...new Set(effective.map((m) => clampHeaderSenderName(m.senderName)))];
18190
+ headerLines.push(`[\u53C2\u4E0E\u8005] ${participants.join("\u3001")}`);
18191
+ }
18192
+ const header = clampEstimatedTokens(headerLines.join("\n"), headerLimit);
18193
+ const separator = "\n\n";
18194
+ const remaining = Math.max(0, limit - estimateTokens2(header) - estimateTokens2(separator));
18195
+ const body = clampEstimatedTokens(
18196
+ effective.map((m) => `${clampBodySenderName(m.senderName)}: ${(m.content ?? "").trim()}`).join("\n"),
18197
+ remaining
18198
+ );
18199
+ const combined = body ? `${header}${separator}${body}` : header;
18200
+ return clampEstimatedTokens(combined, limit);
17879
18201
  }
17880
18202
  function rawContentHashOf(messages) {
17881
18203
  return sha256Hex2(messages.map((m) => `${m.id}:${m.content ?? ""}`).join("\n"));
@@ -17950,7 +18272,7 @@ function chunkMessages(input) {
17950
18272
  }
17951
18273
  const startMessageId = draft[0].id;
17952
18274
  const endMessageId = draft[draft.length - 1].id;
17953
- const embeddingInput = buildEmbeddingInput(input.source, draft);
18275
+ const embeddingInput = buildEmbeddingInput(input.source, draft, config.childHardMaxTokens);
17954
18276
  chunks.push({
17955
18277
  localChunkId: `${parentId}#${startMessageId}-${endMessageId}`,
17956
18278
  parentId,
@@ -18398,6 +18720,7 @@ var SemanticIndexService = class {
18398
18720
  embedder = null;
18399
18721
  embedderModelId = null;
18400
18722
  modelPreloadStatus = "idle";
18723
+ modelPreloadGeneration = 0;
18401
18724
  /** 当前激活的 chunker 身份(version + 参数 hash),随索引记录用于重建判定 */
18402
18725
  chunkerVersion = CHUNKER_VERSION;
18403
18726
  chunkerConfigHash = computeChunkerConfigHash(DEFAULT_CHUNKER_CONFIG);
@@ -18446,6 +18769,7 @@ var SemanticIndexService = class {
18446
18769
  apiKey: options2?.apiKey,
18447
18770
  writeAuthProfile: this.options.writeAuthProfile
18448
18771
  });
18772
+ const preloadGeneration = ++this.modelPreloadGeneration;
18449
18773
  this.embedder = null;
18450
18774
  this.embedderModelId = null;
18451
18775
  this.modelPreloadStatus = "idle";
@@ -18454,15 +18778,26 @@ var SemanticIndexService = class {
18454
18778
  }
18455
18779
  if (saved.enabled && isSemanticIndexConfigured(saved) && saved.mode === "local") {
18456
18780
  const modelId = saved.local.modelId;
18781
+ const downloadSource = saved.local.downloadSource ?? DEFAULT_SEMANTIC_INDEX_MODEL_DOWNLOAD_SOURCE;
18457
18782
  const cacheDir = this.options.modelsCacheDir;
18458
18783
  this.modelPreloadStatus = "downloading";
18459
- appLogger.info("semantic-index", "local embedding model preload started", { modelId, cacheDir });
18784
+ appLogger.info("semantic-index", "local embedding model preload started", { modelId, downloadSource, cacheDir });
18460
18785
  void this.getEmbedder().preload?.().then(() => {
18786
+ if (preloadGeneration !== this.modelPreloadGeneration) return;
18461
18787
  this.modelPreloadStatus = "ready";
18462
- appLogger.info("semantic-index", "local embedding model preload completed", { modelId, cacheDir });
18788
+ appLogger.info("semantic-index", "local embedding model preload completed", {
18789
+ modelId,
18790
+ downloadSource,
18791
+ cacheDir
18792
+ });
18463
18793
  }).catch((error) => {
18794
+ if (preloadGeneration !== this.modelPreloadGeneration) return;
18464
18795
  this.modelPreloadStatus = "error";
18465
- appLogger.error("semantic-index", `local embedding model preload failed: ${modelId}`, error);
18796
+ appLogger.error(
18797
+ "semantic-index",
18798
+ `local embedding model preload failed: ${modelId} via ${downloadSource}`,
18799
+ error
18800
+ );
18466
18801
  });
18467
18802
  }
18468
18803
  return saved;
@@ -18891,7 +19226,9 @@ ${text}`;
18891
19226
  checkStop
18892
19227
  });
18893
19228
  const currentConfig = this.configStore.get();
18894
- if (result.status === "completed" && result.chunksWritten > 0 && jobConfig.mode === "local" && currentConfig.mode === "local" && currentConfig.local.modelId === jobConfig.local.modelId) {
19229
+ const jobDownloadSource = jobConfig.mode === "local" ? jobConfig.local.downloadSource ?? DEFAULT_SEMANTIC_INDEX_MODEL_DOWNLOAD_SOURCE : null;
19230
+ const currentDownloadSource = currentConfig.mode === "local" ? currentConfig.local.downloadSource ?? DEFAULT_SEMANTIC_INDEX_MODEL_DOWNLOAD_SOURCE : null;
19231
+ if (result.status === "completed" && result.chunksWritten > 0 && jobConfig.mode === "local" && currentConfig.mode === "local" && currentConfig.local.modelId === jobConfig.local.modelId && currentDownloadSource === jobDownloadSource) {
18895
19232
  this.modelPreloadStatus = "ready";
18896
19233
  }
18897
19234
  appLogger.info("semantic-index", "warmup job finished", {
@@ -19331,6 +19668,13 @@ async function pushImport(dbManager, sessionId, payload) {
19331
19668
 
19332
19669
  export {
19333
19670
  writeParseResultToDb,
19671
+ detectFormat,
19672
+ detectAllFormats,
19673
+ getSupportedFormats,
19674
+ getFormatFeatureById,
19675
+ scanMultiChatFile,
19676
+ findEntryFileInDirectory,
19677
+ logNativeParserStatus,
19334
19678
  LogLevel,
19335
19679
  initPerfLog,
19336
19680
  logPerf,
@@ -19341,12 +19685,6 @@ export {
19341
19685
  logInfo,
19342
19686
  getErrorCount,
19343
19687
  logSummary,
19344
- detectFormat,
19345
- detectAllFormats,
19346
- getSupportedFormats,
19347
- getFormatFeatureById,
19348
- scanMultiChatFile,
19349
- findEntryFileInDirectory,
19350
19688
  computeWordFrequency,
19351
19689
  segmentText,
19352
19690
  isDictDownloaded,
@@ -19488,6 +19826,7 @@ export {
19488
19826
  deriveParentId,
19489
19827
  parseParentBounds,
19490
19828
  estimateTokens2 as estimateTokens,
19829
+ DEFAULT_SEMANTIC_INDEX_MODEL_DOWNLOAD_SOURCE,
19491
19830
  SEMANTIC_INDEX_CONFIG_VERSION,
19492
19831
  SEARCH_MAX_RESULTS_DEFAULT,
19493
19832
  SEARCH_MAX_RESULTS_MIN,
@@ -19529,4 +19868,4 @@ export {
19529
19868
  MergeSessionCache,
19530
19869
  pushImport
19531
19870
  };
19532
- //# sourceMappingURL=chunk-A2SANKG4.mjs.map
19871
+ //# sourceMappingURL=chunk-36JGKW6K.mjs.map