chatlab-cli 0.31.0 → 0.31.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.
- package/dist/{chat-command-CHV7RLBH.mjs → chat-command-MOETAGU5.mjs} +8 -8
- package/dist/{chunk-DBBL74RK.mjs → chunk-2IXPWPLR.mjs} +164 -82
- package/dist/chunk-2IXPWPLR.mjs.map +1 -0
- package/dist/{chunk-YPQ72HMX.mjs → chunk-3VMKS7PU.mjs} +44 -5
- package/dist/chunk-3VMKS7PU.mjs.map +1 -0
- package/dist/{chunk-SNCXU2QL.mjs → chunk-7HBVGVOB.mjs} +2 -2
- package/dist/{chunk-UWWFOEGF.mjs → chunk-DQ45LDKZ.mjs} +19 -2
- package/dist/chunk-DQ45LDKZ.mjs.map +1 -0
- package/dist/{chunk-WQCY45U2.mjs → chunk-E6W2RURS.mjs} +3 -3
- package/dist/{chunk-T64GA5HP.mjs → chunk-KCPGEILH.mjs} +388 -185
- package/dist/chunk-KCPGEILH.mjs.map +1 -0
- package/dist/{chunk-RDCUGC24.mjs → chunk-LJXSUN25.mjs} +2 -2
- package/dist/{chunk-EXD2SXWL.mjs → chunk-N5AGDT7I.mjs} +40 -7
- package/dist/chunk-N5AGDT7I.mjs.map +1 -0
- package/dist/{chunk-JH5RAJT2.mjs → chunk-SZEBCNBB.mjs} +121 -56
- package/dist/chunk-SZEBCNBB.mjs.map +1 -0
- package/dist/{chunk-RD335EMA.mjs → chunk-VOVLPSG6.mjs} +29 -52
- package/dist/chunk-VOVLPSG6.mjs.map +1 -0
- package/dist/{chunk-DA5T64RY.mjs → chunk-ZVWUR7ET.mjs} +2 -2
- package/dist/cli.mjs +9 -7
- package/dist/contacts-worker.mjs +2 -2
- package/dist/global-insight-worker.mjs +1 -1
- package/dist/{http-3B5ARN7A.mjs → http-M6NHWJZD.mjs} +11 -11
- package/dist/{import-2DZGNSEX.mjs → import-SEYHFXS5.mjs} +9 -7
- package/dist/index.mjs +12 -12
- package/dist/{mcp-44I6FUUQ.mjs → mcp-MIPFYRLV.mjs} +8 -8
- package/dist/people-relationships-worker.mjs +2 -2
- package/dist/semantic-index-worker.mjs +1 -1
- package/dist/{src-R3QX6L3L.mjs → src-7OD6RD5V.mjs} +18 -6
- package/dist/{stream-import-VUEL2TTU.mjs → stream-import-AWEHGQJY.mjs} +9 -7
- package/dist/{worker-runtime-GJAOIIZW.mjs → worker-runtime-TGYMASIE.mjs} +5 -5
- package/dist-web/assets/{ActionToolsPanel.vue_vue_type_script_setup_true_lang-CdJT0t2f.js → ActionToolsPanel.vue_vue_type_script_setup_true_lang-CkDGVRXe.js} +4 -4
- package/dist-web/assets/{BufferResource-BPGkshPa.js → BufferResource-CHI62pIA.js} +1 -1
- package/dist-web/assets/{CanvasRenderer-DTKkZhq4.js → CanvasRenderer-OZRoiF0j.js} +1 -1
- package/dist-web/assets/{Filter-CHXKzS2l.js → Filter-DNb5VErW.js} +1 -1
- package/dist-web/assets/{MessageExportModal-h-zl0dx1.js → MessageExportModal-CqP-w7HP.js} +1 -1
- package/dist-web/assets/{PageHeader-CK0hTsdx.js → PageHeader-Ck8ULyZH.js} +1 -1
- package/dist-web/assets/{RelationshipGalaxyCanvas-Dwdy5flh.js → RelationshipGalaxyCanvas-ioRNObb5.js} +3 -3
- package/dist-web/assets/{RelationshipGalaxyThreeCanvas-CtY4TGen.js → RelationshipGalaxyThreeCanvas-L1Y43p1A.js} +1 -1
- package/dist-web/assets/{RenderTargetSystem-BE56mKRG.js → RenderTargetSystem-fJ7D7Chv.js} +1 -1
- package/dist-web/assets/{TimeSelect.vue_vue_type_script_setup_true_lang-27hMyAUE.js → TimeSelect.vue_vue_type_script_setup_true_lang-7SEaCxDL.js} +1 -1
- package/dist-web/assets/{WebGLRenderer-Dm18dxOf.js → WebGLRenderer-BD3ZUtxH.js} +1 -1
- package/dist-web/assets/{WebGPURenderer-BF6TF-is.js → WebGPURenderer-DZXYsT6D.js} +1 -1
- package/dist-web/assets/{base-CFPyD9Gy.js → base-DTElkJcC.js} +1 -1
- package/dist-web/assets/{browserAll-Cn_CfkXl.js → browserAll-ChT6N6a9.js} +1 -1
- package/dist-web/assets/{dateFormat-BcqNKTQ1.js → dateFormat-D9WibwOu.js} +1 -1
- package/dist-web/assets/{electron-B6xOabTq.js → electron-CIzyGxH_.js} +1 -1
- package/dist-web/assets/{fetch-C_EfVFJi.js → fetch-Avtnh8cJ.js} +1 -1
- package/dist-web/assets/{fetch-DL_u25Ui.js → fetch-BFnrBahy.js} +1 -1
- package/dist-web/assets/fetch-C9rx2isC.js +31 -0
- package/dist-web/assets/{fetch-BPkgnY0R.js → fetch-Ccq_0ecb.js} +1 -1
- package/dist-web/assets/{fetch-DMLq3xPG.js → fetch-CuiQUcO_.js} +1 -1
- package/dist-web/assets/{fetch-BRXmDI_2.js → fetch-CyKILLnB.js} +1 -1
- package/dist-web/assets/fetch-Dd6t3p1r.js +6 -0
- package/dist-web/assets/{fetch-DylpNKCR.js → fetch-DnGx-1_t.js} +1 -1
- package/dist-web/assets/{fetch-CEhUu1Yu.js → fetch-qC7om4QG.js} +1 -1
- package/dist-web/assets/{fetch-etgwVKIw.js → fetch-wSSfDBdV.js} +1 -1
- package/dist-web/assets/index-BH9jI5kV.js +173 -0
- package/dist-web/assets/index-BWtNqcYK.js +22 -0
- package/dist-web/assets/{index-FoTy2Cce.js → index-Bkq7-GBp.js} +1 -1
- package/dist-web/assets/index-Bqr-LkS1.css +1 -0
- package/dist-web/assets/{index-aMIgqj43.js → index-C6qtsaP7.js} +1 -1
- package/dist-web/assets/{index-zZVsV5HB.js → index-ChFGuu70.js} +1 -1
- package/dist-web/assets/{index-C3TnydSt.js → index-CiS20iAR.js} +1 -1
- package/dist-web/assets/{index-BS6loLHW.js → index-CqD6u57u.js} +1 -1
- package/dist-web/assets/{index-CAWO10k1.js → index-CzDG8LVr.js} +2 -2
- package/dist-web/assets/{index-BrTlAl8b.js → index-D0Euk-vQ.js} +1 -1
- package/dist-web/assets/{index-B5S9FnfP.js → index-D26BWhHz.js} +1 -1
- package/dist-web/assets/{index-0x1QPzKZ.js → index-DXHYzIIj.js} +1 -1
- package/dist-web/assets/{index-Chm17wNe.js → index-Dq8u7gMb.js} +2 -2
- package/dist-web/assets/{index-Em6upenM.js → index-u_UH6rDY.js} +3 -3
- package/dist-web/assets/{web-BUZ-dZy0.js → web-BDBdpdK7.js} +1 -1
- package/dist-web/assets/{webworkerAll-BKGP--A4.js → webworkerAll-C3dAOQj4.js} +1 -1
- package/dist-web/index.html +2 -2
- package/package.json +1 -1
- package/dist/chunk-DBBL74RK.mjs.map +0 -1
- package/dist/chunk-EXD2SXWL.mjs.map +0 -1
- package/dist/chunk-JH5RAJT2.mjs.map +0 -1
- package/dist/chunk-RD335EMA.mjs.map +0 -1
- package/dist/chunk-T64GA5HP.mjs.map +0 -1
- package/dist/chunk-UWWFOEGF.mjs.map +0 -1
- package/dist/chunk-YPQ72HMX.mjs.map +0 -1
- package/dist-web/assets/fetch-BUm1isWe.js +0 -5
- package/dist-web/assets/fetch-CBOJwwJp.js +0 -17
- package/dist-web/assets/index-DAr_2DlV.js +0 -173
- package/dist-web/assets/index-YADL4XIp.js +0 -22
- package/dist-web/assets/index-_6lck7Bv.css +0 -1
- /package/dist/{chat-command-CHV7RLBH.mjs.map → chat-command-MOETAGU5.mjs.map} +0 -0
- /package/dist/{chunk-SNCXU2QL.mjs.map → chunk-7HBVGVOB.mjs.map} +0 -0
- /package/dist/{chunk-WQCY45U2.mjs.map → chunk-E6W2RURS.mjs.map} +0 -0
- /package/dist/{chunk-RDCUGC24.mjs.map → chunk-LJXSUN25.mjs.map} +0 -0
- /package/dist/{chunk-DA5T64RY.mjs.map → chunk-ZVWUR7ET.mjs.map} +0 -0
- /package/dist/{http-3B5ARN7A.mjs.map → http-M6NHWJZD.mjs.map} +0 -0
- /package/dist/{import-2DZGNSEX.mjs.map → import-SEYHFXS5.mjs.map} +0 -0
- /package/dist/{mcp-44I6FUUQ.mjs.map → mcp-MIPFYRLV.mjs.map} +0 -0
- /package/dist/{src-R3QX6L3L.mjs.map → src-7OD6RD5V.mjs.map} +0 -0
- /package/dist/{stream-import-VUEL2TTU.mjs.map → stream-import-AWEHGQJY.mjs.map} +0 -0
- /package/dist/{worker-runtime-GJAOIIZW.mjs.map → worker-runtime-TGYMASIE.mjs.map} +0 -0
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
createEmptyContactsDiagnostics,
|
|
10
10
|
normalizeContactsTimeRangePreset,
|
|
11
11
|
resolveContactsTimeRange
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-7HBVGVOB.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-
|
|
21
|
+
} from "./chunk-LJXSUN25.mjs";
|
|
22
22
|
import {
|
|
23
23
|
ANNUAL_SUMMARY_ALGORITHM_VERSION
|
|
24
24
|
} from "./chunk-YAQLYL52.mjs";
|
|
@@ -93,7 +93,7 @@ import {
|
|
|
93
93
|
updateSessionOwnerId,
|
|
94
94
|
writeAnnualSummarySnapshot,
|
|
95
95
|
writeAuthProfile
|
|
96
|
-
} from "./chunk-
|
|
96
|
+
} from "./chunk-DQ45LDKZ.mjs";
|
|
97
97
|
import {
|
|
98
98
|
__commonJS,
|
|
99
99
|
__export,
|
|
@@ -3881,6 +3881,9 @@ function extractNameFromFilePath3(filePath) {
|
|
|
3881
3881
|
const basename13 = path5.basename(filePath);
|
|
3882
3882
|
return basename13.replace(/\.json$/i, "") || "\u672A\u77E5\u7FA4\u804A";
|
|
3883
3883
|
}
|
|
3884
|
+
function isPlaceholderSenderId(id) {
|
|
3885
|
+
return id === "0" || id === "\u672A\u77E5";
|
|
3886
|
+
}
|
|
3884
3887
|
function countSenders(content) {
|
|
3885
3888
|
const sendersStart = content.indexOf('"senders"');
|
|
3886
3889
|
if (sendersStart === -1) return 0;
|
|
@@ -3895,8 +3898,19 @@ function countSenders(content) {
|
|
|
3895
3898
|
}
|
|
3896
3899
|
if (depth !== 0) return 0;
|
|
3897
3900
|
const sendersContent = content.slice(arrayStart + 1, i - 1);
|
|
3898
|
-
const uidMatches = sendersContent.
|
|
3899
|
-
|
|
3901
|
+
const uidMatches = sendersContent.matchAll(/"uid"\s*:\s*"([^"]*)"/g);
|
|
3902
|
+
let count = 0;
|
|
3903
|
+
for (const match of uidMatches) {
|
|
3904
|
+
const uid = match[1];
|
|
3905
|
+
if (uid && !isPlaceholderSenderId(uid)) count++;
|
|
3906
|
+
}
|
|
3907
|
+
return count;
|
|
3908
|
+
}
|
|
3909
|
+
function resolveChatType(chatInfoType, headContent) {
|
|
3910
|
+
if (chatInfoType === "group") return "group" /* GROUP */;
|
|
3911
|
+
const sendersCount = countSenders(headContent);
|
|
3912
|
+
if (chatInfoType === "private") return sendersCount > 2 ? "group" /* GROUP */ : "private" /* PRIVATE */;
|
|
3913
|
+
return sendersCount > 2 ? "group" /* GROUP */ : sendersCount > 0 ? "private" /* PRIVATE */ : "group" /* GROUP */;
|
|
3900
3914
|
}
|
|
3901
3915
|
function convertMessageType(messageType, content, isRecalled) {
|
|
3902
3916
|
if (isRecalled) return 81 /* RECALL */;
|
|
@@ -3950,8 +3964,7 @@ async function* parseV4(options2) {
|
|
|
3950
3964
|
}
|
|
3951
3965
|
} catch {
|
|
3952
3966
|
}
|
|
3953
|
-
const
|
|
3954
|
-
const chatType = sendersCount > 2 ? "group" /* GROUP */ : sendersCount > 0 ? "private" /* PRIVATE */ : "group" /* GROUP */;
|
|
3967
|
+
const chatType = resolveChatType(chatInfo.type, headContent);
|
|
3955
3968
|
const meta = {
|
|
3956
3969
|
name: chatInfo.name || extractNameFromFilePath3(filePath),
|
|
3957
3970
|
platform: KNOWN_PLATFORMS.QQ,
|
|
@@ -3970,7 +3983,7 @@ async function* parseV4(options2) {
|
|
|
3970
3983
|
const pipeline2 = chain3([readStream, parser4(), pick3({ filter: /^messages\.\d+$/ }), streamValues3()]);
|
|
3971
3984
|
pipeline2.on("data", ({ value }) => {
|
|
3972
3985
|
const platformId = value.sender.uin || value.sender.uid || value.rawMessage?.senderUin || value.rawMessage?.senderUid;
|
|
3973
|
-
if (!platformId) {
|
|
3986
|
+
if (!platformId || isPlaceholderSenderId(platformId)) {
|
|
3974
3987
|
skippedMessages++;
|
|
3975
3988
|
return;
|
|
3976
3989
|
}
|
|
@@ -3989,9 +4002,11 @@ async function* parseV4(options2) {
|
|
|
3989
4002
|
skippedMessages++;
|
|
3990
4003
|
return;
|
|
3991
4004
|
}
|
|
3992
|
-
const
|
|
4005
|
+
const isSystem = value.system ?? value.isSystemMessage;
|
|
4006
|
+
const isRecalled = value.recalled ?? value.isRecalled;
|
|
4007
|
+
const type = isSystem ? 80 /* SYSTEM */ : convertMessageType(value.messageType, value.content, isRecalled);
|
|
3993
4008
|
let textContent = value.content?.text || "";
|
|
3994
|
-
if (
|
|
4009
|
+
if (isRecalled) textContent = "[\u5DF2\u64A4\u56DE] " + textContent;
|
|
3995
4010
|
messageBatch.push({
|
|
3996
4011
|
platformMessageId: value.messageId,
|
|
3997
4012
|
// 消息的平台原始 ID
|
|
@@ -7890,8 +7905,8 @@ async function streamImportSingle(filePath, deps, formatFeature, formatOptions,
|
|
|
7890
7905
|
}
|
|
7891
7906
|
return { success: true, sessionId, diagnostics };
|
|
7892
7907
|
}
|
|
7893
|
-
async function analyzeNewImport(filePath, onProgress) {
|
|
7894
|
-
const formatFeature = detectFormat(filePath);
|
|
7908
|
+
async function analyzeNewImport(filePath, onProgress, options2) {
|
|
7909
|
+
const formatFeature = options2?.formatId ? getFormatFeatureById(options2.formatId) : detectFormat(filePath);
|
|
7895
7910
|
if (!formatFeature) {
|
|
7896
7911
|
return {
|
|
7897
7912
|
totalMessages: 0,
|
|
@@ -7908,30 +7923,35 @@ async function analyzeNewImport(filePath, onProgress) {
|
|
|
7908
7923
|
let totalMessages = 0;
|
|
7909
7924
|
let newMessageCount = 0;
|
|
7910
7925
|
let duplicateCount = 0;
|
|
7911
|
-
await streamParseFile(
|
|
7912
|
-
|
|
7913
|
-
|
|
7914
|
-
|
|
7915
|
-
|
|
7916
|
-
|
|
7917
|
-
|
|
7918
|
-
|
|
7919
|
-
|
|
7920
|
-
|
|
7921
|
-
|
|
7922
|
-
|
|
7923
|
-
|
|
7924
|
-
|
|
7925
|
-
const
|
|
7926
|
-
|
|
7927
|
-
|
|
7928
|
-
|
|
7929
|
-
|
|
7930
|
-
|
|
7926
|
+
await streamParseFile(
|
|
7927
|
+
filePath,
|
|
7928
|
+
{
|
|
7929
|
+
formatOptions: options2?.chatIndex === void 0 ? void 0 : { chatIndex: options2.chatIndex },
|
|
7930
|
+
onMeta: (parsedMeta) => {
|
|
7931
|
+
meta = { name: parsedMeta.name, platform: parsedMeta.platform, type: parsedMeta.type };
|
|
7932
|
+
},
|
|
7933
|
+
onMembers: (members) => {
|
|
7934
|
+
for (const m of members) memberSet.add(m.platformId);
|
|
7935
|
+
},
|
|
7936
|
+
onProgress: (progress) => {
|
|
7937
|
+
onProgress(progress);
|
|
7938
|
+
},
|
|
7939
|
+
onMessageBatch: (batch) => {
|
|
7940
|
+
for (const msg of batch) {
|
|
7941
|
+
totalMessages++;
|
|
7942
|
+
if (!memberSet.has(msg.senderPlatformId)) memberSet.add(msg.senderPlatformId);
|
|
7943
|
+
const prepared = prepareMessageForCreate(msg);
|
|
7944
|
+
if ("skipCounter" in prepared) continue;
|
|
7945
|
+
if (registerMessageAndCheckDuplicate(prepared.message, dedupState)) {
|
|
7946
|
+
duplicateCount++;
|
|
7947
|
+
} else {
|
|
7948
|
+
newMessageCount++;
|
|
7949
|
+
}
|
|
7931
7950
|
}
|
|
7932
7951
|
}
|
|
7933
|
-
}
|
|
7934
|
-
|
|
7952
|
+
},
|
|
7953
|
+
options2?.formatId
|
|
7954
|
+
);
|
|
7935
7955
|
return { totalMessages, newMessageCount, duplicateCount, totalMembers: memberSet.size, meta };
|
|
7936
7956
|
}
|
|
7937
7957
|
async function streamParseFileInfo(filePath, deps) {
|
|
@@ -8128,8 +8148,8 @@ function normalizeImportTimestamp(timestamp) {
|
|
|
8128
8148
|
const value = typeof timestamp === "string" && timestamp.trim() !== "" ? Number(timestamp) : timestamp;
|
|
8129
8149
|
return typeof value === "number" && value > 0 && Number.isFinite(value) ? value : null;
|
|
8130
8150
|
}
|
|
8131
|
-
async function analyzeIncrementalImport(sessionId, filePath, deps) {
|
|
8132
|
-
const formatFeature = detectFormat(filePath);
|
|
8151
|
+
async function analyzeIncrementalImport(sessionId, filePath, deps, options2) {
|
|
8152
|
+
const formatFeature = options2?.formatId ? getFormatFeatureById(options2.formatId) : detectFormat(filePath);
|
|
8133
8153
|
if (!formatFeature) {
|
|
8134
8154
|
return { error: "error.unrecognized_format", newMessageCount: 0, duplicateCount: 0, totalInFile: 0 };
|
|
8135
8155
|
}
|
|
@@ -8144,27 +8164,32 @@ async function analyzeIncrementalImport(sessionId, filePath, deps) {
|
|
|
8144
8164
|
let totalInFile = 0;
|
|
8145
8165
|
let newMessageCount = 0;
|
|
8146
8166
|
let duplicateCount = 0;
|
|
8147
|
-
await streamParseFile(
|
|
8148
|
-
|
|
8149
|
-
|
|
8150
|
-
|
|
8151
|
-
|
|
8152
|
-
|
|
8153
|
-
|
|
8154
|
-
|
|
8155
|
-
|
|
8156
|
-
|
|
8157
|
-
|
|
8158
|
-
|
|
8159
|
-
|
|
8160
|
-
|
|
8161
|
-
|
|
8162
|
-
|
|
8163
|
-
|
|
8167
|
+
await streamParseFile(
|
|
8168
|
+
filePath,
|
|
8169
|
+
{
|
|
8170
|
+
formatOptions: options2?.chatIndex === void 0 ? void 0 : { chatIndex: options2.chatIndex },
|
|
8171
|
+
onMeta: () => {
|
|
8172
|
+
},
|
|
8173
|
+
onMembers: () => {
|
|
8174
|
+
},
|
|
8175
|
+
onProgress: (progress) => {
|
|
8176
|
+
deps.onProgress(progress);
|
|
8177
|
+
},
|
|
8178
|
+
onMessageBatch: (batch) => {
|
|
8179
|
+
for (const msg of batch) {
|
|
8180
|
+
totalInFile++;
|
|
8181
|
+
const timestamp = normalizeImportTimestamp(msg.timestamp);
|
|
8182
|
+
if (timestamp === null) continue;
|
|
8183
|
+
if (isDuplicate({ ...msg, timestamp }, existingPlatformMsgIds, existingKeys, existingFallbackOnlyKeys)) {
|
|
8184
|
+
duplicateCount++;
|
|
8185
|
+
} else {
|
|
8186
|
+
newMessageCount++;
|
|
8187
|
+
}
|
|
8164
8188
|
}
|
|
8165
8189
|
}
|
|
8166
|
-
}
|
|
8167
|
-
|
|
8190
|
+
},
|
|
8191
|
+
options2?.formatId
|
|
8192
|
+
);
|
|
8168
8193
|
return { newMessageCount, duplicateCount, totalInFile };
|
|
8169
8194
|
}
|
|
8170
8195
|
async function incrementalImport(sessionId, filePath, deps, options2) {
|
|
@@ -8418,6 +8443,12 @@ async function incrementalImport(sessionId, filePath, deps, options2) {
|
|
|
8418
8443
|
}
|
|
8419
8444
|
}
|
|
8420
8445
|
|
|
8446
|
+
// ../../packages/node-runtime/src/import/session-id.ts
|
|
8447
|
+
var SAFE_IMPORT_SESSION_ID_RE = /^[A-Za-z0-9_@-][A-Za-z0-9._@-]{0,127}$/;
|
|
8448
|
+
function isValidImportSessionId(sessionId) {
|
|
8449
|
+
return SAFE_IMPORT_SESSION_ID_RE.test(sessionId) && !sessionId.includes("..");
|
|
8450
|
+
}
|
|
8451
|
+
|
|
8421
8452
|
// ../../packages/node-runtime/src/import/auto-import-matcher.ts
|
|
8422
8453
|
var MATCH_WINDOW_SIZE = 5;
|
|
8423
8454
|
function buildPrivateIdentity(ownerId, memberIds) {
|
|
@@ -8528,6 +8559,20 @@ async function resolveAutoImportTarget(filePath, deps, formatOptions) {
|
|
|
8528
8559
|
}
|
|
8529
8560
|
|
|
8530
8561
|
// ../../packages/node-runtime/src/import/auto-importer.ts
|
|
8562
|
+
async function planAutoImport(filePath, deps, options2) {
|
|
8563
|
+
if (options2.explicitSessionId) {
|
|
8564
|
+
if (!isValidImportSessionId(options2.explicitSessionId)) {
|
|
8565
|
+
throw new Error("sessionId contains invalid characters");
|
|
8566
|
+
}
|
|
8567
|
+
return deps.sessionExists(options2.explicitSessionId) ? { action: "incremental", sessionId: options2.explicitSessionId } : { action: "create", sessionId: options2.explicitSessionId };
|
|
8568
|
+
}
|
|
8569
|
+
const decision = await (deps.resolveTarget ?? resolveAutoImportTarget)(
|
|
8570
|
+
filePath,
|
|
8571
|
+
deps,
|
|
8572
|
+
options2.formatOptions
|
|
8573
|
+
);
|
|
8574
|
+
return decision.action === "incremental" ? { action: "incremental", sessionId: decision.sessionId, matchedBy: decision.matchedBy } : { action: "create", reason: decision.reason };
|
|
8575
|
+
}
|
|
8531
8576
|
function mapCreateResult(result, createReason) {
|
|
8532
8577
|
if (!result.success || !result.sessionId) {
|
|
8533
8578
|
return { success: false, error: result.error, diagnostics: result.diagnostics };
|
|
@@ -8539,6 +8584,11 @@ function mapCreateResult(result, createReason) {
|
|
|
8539
8584
|
...createReason ? { createReason } : {},
|
|
8540
8585
|
newMessageCount: result.diagnostics?.messagesWritten ?? 0,
|
|
8541
8586
|
duplicateCount: result.diagnostics?.duplicateCount ?? 0,
|
|
8587
|
+
batch: result.diagnostics ? {
|
|
8588
|
+
receivedCount: result.diagnostics.messagesReceived,
|
|
8589
|
+
writtenCount: result.diagnostics.messagesWritten,
|
|
8590
|
+
duplicateCount: result.diagnostics.duplicateCount
|
|
8591
|
+
} : void 0,
|
|
8542
8592
|
diagnostics: result.diagnostics
|
|
8543
8593
|
};
|
|
8544
8594
|
}
|
|
@@ -8550,61 +8600,41 @@ function mapIncrementalResult(sessionId, result, matchedBy) {
|
|
|
8550
8600
|
importMode: "incremental",
|
|
8551
8601
|
...matchedBy ? { matchedBy } : {},
|
|
8552
8602
|
newMessageCount: result.newMessageCount,
|
|
8553
|
-
duplicateCount: result.batch?.duplicateCount ?? 0
|
|
8603
|
+
duplicateCount: result.batch?.duplicateCount ?? 0,
|
|
8604
|
+
batch: result.batch,
|
|
8605
|
+
session: result.session,
|
|
8606
|
+
updates: result.updates
|
|
8554
8607
|
};
|
|
8555
8608
|
}
|
|
8556
8609
|
async function autoImportFile(filePath, deps, options2 = {}) {
|
|
8557
8610
|
try {
|
|
8558
|
-
if (options2.explicitSessionId) {
|
|
8559
|
-
|
|
8560
|
-
|
|
8561
|
-
options2.explicitSessionId,
|
|
8562
|
-
await deps.appendSession(options2.explicitSessionId, filePath, options2.formatOptions)
|
|
8563
|
-
);
|
|
8564
|
-
appLogger.info("import", "Explicit incremental import completed", {
|
|
8565
|
-
sessionId: options2.explicitSessionId,
|
|
8566
|
-
success: result3.success,
|
|
8567
|
-
newMessageCount: result3.newMessageCount,
|
|
8568
|
-
duplicateCount: result3.duplicateCount
|
|
8569
|
-
});
|
|
8570
|
-
return result3;
|
|
8571
|
-
}
|
|
8572
|
-
const result2 = mapCreateResult(
|
|
8573
|
-
await deps.createSession(filePath, options2.formatOptions, options2.explicitSessionId)
|
|
8574
|
-
);
|
|
8575
|
-
appLogger.info("import", "Explicit session import completed", {
|
|
8576
|
-
sessionId: result2.sessionId,
|
|
8577
|
-
success: result2.success,
|
|
8578
|
-
importMode: result2.importMode
|
|
8611
|
+
if (!options2.explicitSessionId) {
|
|
8612
|
+
appLogger.info("import", "Automatic session matching started", {
|
|
8613
|
+
candidateCount: deps.listSessionIds().length
|
|
8579
8614
|
});
|
|
8580
|
-
return result2;
|
|
8581
8615
|
}
|
|
8582
|
-
|
|
8583
|
-
|
|
8584
|
-
});
|
|
8585
|
-
const decision = await (deps.resolveTarget ?? resolveAutoImportTarget)(
|
|
8586
|
-
filePath,
|
|
8587
|
-
deps,
|
|
8588
|
-
options2.formatOptions
|
|
8589
|
-
);
|
|
8590
|
-
if (decision.action === "incremental") {
|
|
8616
|
+
const plan = await planAutoImport(filePath, deps, options2);
|
|
8617
|
+
if (plan.action === "incremental") {
|
|
8591
8618
|
const result2 = mapIncrementalResult(
|
|
8592
|
-
|
|
8593
|
-
await deps.appendSession(
|
|
8594
|
-
|
|
8619
|
+
plan.sessionId,
|
|
8620
|
+
await deps.appendSession(plan.sessionId, filePath, options2.formatOptions),
|
|
8621
|
+
plan.matchedBy
|
|
8595
8622
|
);
|
|
8596
|
-
appLogger.info("import", "
|
|
8597
|
-
sessionId:
|
|
8598
|
-
matchedBy:
|
|
8623
|
+
appLogger.info("import", "Incremental import completed", {
|
|
8624
|
+
sessionId: plan.sessionId,
|
|
8625
|
+
matchedBy: plan.matchedBy,
|
|
8599
8626
|
success: result2.success,
|
|
8600
8627
|
newMessageCount: result2.newMessageCount,
|
|
8601
8628
|
duplicateCount: result2.duplicateCount
|
|
8602
8629
|
});
|
|
8603
8630
|
return result2;
|
|
8604
8631
|
}
|
|
8605
|
-
const result = mapCreateResult(
|
|
8606
|
-
|
|
8607
|
-
|
|
8632
|
+
const result = mapCreateResult(
|
|
8633
|
+
await deps.createSession(filePath, options2.formatOptions, plan.sessionId),
|
|
8634
|
+
plan.reason
|
|
8635
|
+
);
|
|
8636
|
+
appLogger.info("import", "Import created a new session", {
|
|
8637
|
+
reason: plan.reason,
|
|
8608
8638
|
sessionId: result.sessionId,
|
|
8609
8639
|
success: result.success
|
|
8610
8640
|
});
|
|
@@ -8614,6 +8644,53 @@ async function autoImportFile(filePath, deps, options2 = {}) {
|
|
|
8614
8644
|
return { success: false, error: error instanceof Error ? error.message : String(error) };
|
|
8615
8645
|
}
|
|
8616
8646
|
}
|
|
8647
|
+
async function analyzeAutoImportFile(filePath, deps, options2 = {}) {
|
|
8648
|
+
try {
|
|
8649
|
+
const plan = await planAutoImport(filePath, deps, options2);
|
|
8650
|
+
if (plan.action === "incremental") {
|
|
8651
|
+
const analysis2 = await deps.analyzeAppendSession(plan.sessionId, filePath, options2.formatOptions);
|
|
8652
|
+
if (analysis2.error) return { success: false, error: analysis2.error };
|
|
8653
|
+
const result2 = {
|
|
8654
|
+
success: true,
|
|
8655
|
+
importMode: "incremental",
|
|
8656
|
+
sessionId: plan.sessionId,
|
|
8657
|
+
...plan.matchedBy ? { matchedBy: plan.matchedBy } : {},
|
|
8658
|
+
totalMessageCount: analysis2.totalInFile,
|
|
8659
|
+
newMessageCount: analysis2.newMessageCount,
|
|
8660
|
+
duplicateCount: analysis2.duplicateCount
|
|
8661
|
+
};
|
|
8662
|
+
appLogger.info("import", "Incremental import analysis completed", {
|
|
8663
|
+
sessionId: plan.sessionId,
|
|
8664
|
+
matchedBy: plan.matchedBy,
|
|
8665
|
+
newMessageCount: analysis2.newMessageCount,
|
|
8666
|
+
duplicateCount: analysis2.duplicateCount
|
|
8667
|
+
});
|
|
8668
|
+
return result2;
|
|
8669
|
+
}
|
|
8670
|
+
const analysis = await deps.analyzeCreateSession(filePath, options2.formatOptions);
|
|
8671
|
+
if (analysis.error) return { success: false, error: analysis.error };
|
|
8672
|
+
const result = {
|
|
8673
|
+
success: true,
|
|
8674
|
+
importMode: "created",
|
|
8675
|
+
...plan.sessionId ? { sessionId: plan.sessionId } : {},
|
|
8676
|
+
...plan.reason ? { createReason: plan.reason } : {},
|
|
8677
|
+
totalMessageCount: analysis.totalMessages,
|
|
8678
|
+
newMessageCount: analysis.newMessageCount,
|
|
8679
|
+
duplicateCount: analysis.duplicateCount,
|
|
8680
|
+
totalMemberCount: analysis.totalMembers,
|
|
8681
|
+
meta: analysis.meta
|
|
8682
|
+
};
|
|
8683
|
+
appLogger.info("import", "New session import analysis completed", {
|
|
8684
|
+
reason: plan.reason,
|
|
8685
|
+
messageCount: analysis.totalMessages,
|
|
8686
|
+
memberCount: analysis.totalMembers
|
|
8687
|
+
});
|
|
8688
|
+
return result;
|
|
8689
|
+
} catch (error) {
|
|
8690
|
+
appLogger.error("import", "Automatic import analysis failed", error);
|
|
8691
|
+
return { success: false, error: error instanceof Error ? error.message : String(error) };
|
|
8692
|
+
}
|
|
8693
|
+
}
|
|
8617
8694
|
|
|
8618
8695
|
// ../../packages/node-runtime/src/import/import-lock.ts
|
|
8619
8696
|
import * as fs21 from "fs";
|
|
@@ -20085,17 +20162,41 @@ var MergeSessionCache = class {
|
|
|
20085
20162
|
import * as fs34 from "fs";
|
|
20086
20163
|
var SYSTEM_SENDER_ID = "SYSTEM";
|
|
20087
20164
|
var SYSTEM_MEMBER_NAME = "\u7CFB\u7EDF\u6D88\u606F";
|
|
20165
|
+
function isRecord3(value) {
|
|
20166
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
20167
|
+
}
|
|
20168
|
+
function validateOptionalString(value, field) {
|
|
20169
|
+
return value !== void 0 && typeof value !== "string" ? `${field} must be a string` : null;
|
|
20170
|
+
}
|
|
20088
20171
|
function validatePayload(payload, isNew) {
|
|
20172
|
+
if (!isRecord3(payload)) return "payload must be an object";
|
|
20089
20173
|
const messages = payload.messages;
|
|
20090
20174
|
if (!Array.isArray(messages) || messages.length === 0) return "messages is required and must be a non-empty array";
|
|
20175
|
+
if (payload.chatlab !== void 0) {
|
|
20176
|
+
if (!isRecord3(payload.chatlab)) return "chatlab must be an object";
|
|
20177
|
+
if (typeof payload.chatlab.version !== "string" || payload.chatlab.version.length === 0)
|
|
20178
|
+
return "chatlab.version must be a string";
|
|
20179
|
+
if (typeof payload.chatlab.exportedAt !== "number" || !Number.isFinite(payload.chatlab.exportedAt))
|
|
20180
|
+
return "chatlab.exportedAt must be a number";
|
|
20181
|
+
const generatorError = validateOptionalString(payload.chatlab.generator, "chatlab.generator");
|
|
20182
|
+
if (generatorError) return generatorError;
|
|
20183
|
+
}
|
|
20091
20184
|
if (isNew) {
|
|
20092
20185
|
if (!payload.chatlab) return "chatlab is required for new sessions";
|
|
20093
20186
|
if (!payload.meta) return "meta is required for new sessions";
|
|
20094
|
-
const m = payload.meta;
|
|
20095
|
-
if (!m.name) return "meta.name is required";
|
|
20096
|
-
if (!m.platform) return "meta.platform is required";
|
|
20097
|
-
if (!m.type) return "meta.type is required";
|
|
20098
20187
|
}
|
|
20188
|
+
if (payload.meta !== void 0) {
|
|
20189
|
+
if (!isRecord3(payload.meta)) return "meta must be an object";
|
|
20190
|
+
const metaFields = ["name", "platform", "type", "groupId", "groupAvatar", "ownerId"];
|
|
20191
|
+
for (const field of metaFields) {
|
|
20192
|
+
const error = validateOptionalString(payload.meta[field], `meta.${field}`);
|
|
20193
|
+
if (error) return error;
|
|
20194
|
+
}
|
|
20195
|
+
if (isNew && !payload.meta.name) return "meta.name is required";
|
|
20196
|
+
if (isNew && !payload.meta.platform) return "meta.platform is required";
|
|
20197
|
+
if (isNew && !payload.meta.type) return "meta.type is required";
|
|
20198
|
+
}
|
|
20199
|
+
if (payload.options !== void 0 && !isRecord3(payload.options)) return "options must be an object";
|
|
20099
20200
|
const { metaUpdateMode, memberUpdateMode } = payload.options ?? {};
|
|
20100
20201
|
if (metaUpdateMode !== void 0 && metaUpdateMode !== "patch" && metaUpdateMode !== "none") {
|
|
20101
20202
|
return `options.metaUpdateMode must be 'patch' or 'none'`;
|
|
@@ -20106,16 +20207,35 @@ function validatePayload(payload, isNew) {
|
|
|
20106
20207
|
if (payload.members !== void 0 && !Array.isArray(payload.members)) return "members must be an array";
|
|
20107
20208
|
if (payload.members) {
|
|
20108
20209
|
for (let i = 0; i < payload.members.length; i++) {
|
|
20109
|
-
|
|
20210
|
+
const member = payload.members[i];
|
|
20211
|
+
if (!isRecord3(member)) return `members[${i}] must be an object`;
|
|
20212
|
+
if (typeof member.platformId !== "string" || member.platformId.length === 0)
|
|
20110
20213
|
return `members[${i}].platformId must be a string`;
|
|
20214
|
+
for (const field of ["accountName", "groupNickname", "avatar"]) {
|
|
20215
|
+
const error = validateOptionalString(member[field], `members[${i}].${field}`);
|
|
20216
|
+
if (error) return error;
|
|
20217
|
+
}
|
|
20218
|
+
if (member.roles !== void 0) {
|
|
20219
|
+
if (!Array.isArray(member.roles)) return `members[${i}].roles must be an array`;
|
|
20220
|
+
for (let j = 0; j < member.roles.length; j++) {
|
|
20221
|
+
const role = member.roles[j];
|
|
20222
|
+
if (!isRecord3(role) || typeof role.id !== "string" || role.id.length === 0)
|
|
20223
|
+
return `members[${i}].roles[${j}].id must be a string`;
|
|
20224
|
+
}
|
|
20225
|
+
}
|
|
20111
20226
|
}
|
|
20112
20227
|
}
|
|
20113
20228
|
for (let i = 0; i < messages.length; i++) {
|
|
20114
20229
|
const msg = messages[i];
|
|
20230
|
+
if (!isRecord3(msg)) return `messages[${i}] must be an object`;
|
|
20115
20231
|
if (typeof msg.sender !== "string" || msg.sender.length === 0) return `messages[${i}].sender must be a string`;
|
|
20116
|
-
if (typeof msg.timestamp !== "number" || msg.timestamp <= 0)
|
|
20232
|
+
if (typeof msg.timestamp !== "number" || !Number.isFinite(msg.timestamp) || msg.timestamp <= 0)
|
|
20117
20233
|
return `messages[${i}].timestamp must be a positive number`;
|
|
20118
|
-
if (typeof msg.type !== "number") return `messages[${i}].type must be a number`;
|
|
20234
|
+
if (typeof msg.type !== "number" || !Number.isFinite(msg.type)) return `messages[${i}].type must be a number`;
|
|
20235
|
+
for (const field of ["accountName", "groupNickname"]) {
|
|
20236
|
+
const error = validateOptionalString(msg[field], `messages[${i}].${field}`);
|
|
20237
|
+
if (error) return error;
|
|
20238
|
+
}
|
|
20119
20239
|
if (msg.content !== void 0 && msg.content !== null && typeof msg.content !== "string")
|
|
20120
20240
|
return `messages[${i}].content must be a string or null`;
|
|
20121
20241
|
if (msg.platformMessageId !== void 0 && typeof msg.platformMessageId !== "string")
|
|
@@ -20136,47 +20256,79 @@ function normalizeSenderAccountName(sender, accountName) {
|
|
|
20136
20256
|
function normalizeSenderGroupNickname(sender, groupNickname) {
|
|
20137
20257
|
return sender === SYSTEM_SENDER_ID ? SYSTEM_MEMBER_NAME : groupNickname;
|
|
20138
20258
|
}
|
|
20139
|
-
function
|
|
20259
|
+
function isCountedMember(platformId) {
|
|
20260
|
+
return platformId !== SYSTEM_SENDER_ID;
|
|
20261
|
+
}
|
|
20262
|
+
function registerPushMessageAndCheckDuplicate(message, dedupState) {
|
|
20263
|
+
return registerMessageAndCheckDuplicate(
|
|
20264
|
+
{
|
|
20265
|
+
platformMessageId: message.platformMessageId,
|
|
20266
|
+
timestamp: message.timestamp,
|
|
20267
|
+
senderPlatformId: message.sender,
|
|
20268
|
+
type: message.type,
|
|
20269
|
+
content: message.content ?? null,
|
|
20270
|
+
replyToMessageId: message.replyToMessageId
|
|
20271
|
+
},
|
|
20272
|
+
dedupState
|
|
20273
|
+
);
|
|
20274
|
+
}
|
|
20275
|
+
function createExistingMessageDedupState(db) {
|
|
20276
|
+
const existingPmids = /* @__PURE__ */ new Set();
|
|
20277
|
+
const existingKeys = /* @__PURE__ */ new Set();
|
|
20278
|
+
const existingFallbackOnlyKeys = /* @__PURE__ */ new Set();
|
|
20279
|
+
const existingMessages = db.prepare(
|
|
20280
|
+
`SELECT msg.ts, m.platform_id, msg.type, msg.content, msg.reply_to_message_id, msg.platform_message_id
|
|
20281
|
+
FROM message msg
|
|
20282
|
+
JOIN member m ON msg.sender_id = m.id`
|
|
20283
|
+
).all();
|
|
20284
|
+
for (const message of existingMessages) {
|
|
20285
|
+
if (message.platform_message_id) existingPmids.add(message.platform_message_id);
|
|
20286
|
+
const key = generateFallbackMessageKey({
|
|
20287
|
+
timestamp: message.ts,
|
|
20288
|
+
senderPlatformId: message.platform_id,
|
|
20289
|
+
type: message.type,
|
|
20290
|
+
content: message.content,
|
|
20291
|
+
replyToMessageId: message.reply_to_message_id ?? void 0
|
|
20292
|
+
});
|
|
20293
|
+
existingKeys.add(key);
|
|
20294
|
+
if (!message.platform_message_id) existingFallbackOnlyKeys.add(key);
|
|
20295
|
+
}
|
|
20296
|
+
return createMessageDedupState(existingPmids, existingKeys, existingFallbackOnlyKeys);
|
|
20297
|
+
}
|
|
20298
|
+
function writeMessages(db, messages, dedupState) {
|
|
20140
20299
|
const insertMsg = db.prepare(
|
|
20141
20300
|
`INSERT INTO message (sender_id, sender_account_name, sender_group_nickname, ts, type, content, reply_to_message_id, platform_message_id)
|
|
20142
20301
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`
|
|
20143
20302
|
);
|
|
20144
20303
|
const getMemberId = db.prepare("SELECT id FROM member WHERE platform_id = ?");
|
|
20145
|
-
const insertMinimalMember = db.prepare(
|
|
20304
|
+
const insertMinimalMember = db.prepare(
|
|
20305
|
+
"INSERT OR IGNORE INTO member (platform_id, account_name, group_nickname) VALUES (?, ?, ?)"
|
|
20306
|
+
);
|
|
20146
20307
|
const memberIdCache = /* @__PURE__ */ new Map();
|
|
20147
20308
|
let writtenCount = 0;
|
|
20148
20309
|
let duplicateCount = 0;
|
|
20310
|
+
let membersAdded = 0;
|
|
20149
20311
|
let minWrittenTs = Infinity;
|
|
20150
20312
|
const ftsEntries = [];
|
|
20151
20313
|
const sorted = [...messages].sort((a, b) => a.timestamp - b.timestamp);
|
|
20152
20314
|
db.transaction(() => {
|
|
20153
20315
|
for (const msg of sorted) {
|
|
20154
|
-
|
|
20155
|
-
|
|
20156
|
-
|
|
20157
|
-
duplicateCount++;
|
|
20158
|
-
continue;
|
|
20159
|
-
}
|
|
20160
|
-
if (existingKeys.has(key)) {
|
|
20161
|
-
duplicateCount++;
|
|
20162
|
-
continue;
|
|
20163
|
-
}
|
|
20164
|
-
existingPmids.add(msg.platformMessageId);
|
|
20165
|
-
existingKeys.add(key);
|
|
20166
|
-
} else {
|
|
20167
|
-
if (existingKeys.has(key)) {
|
|
20168
|
-
duplicateCount++;
|
|
20169
|
-
continue;
|
|
20170
|
-
}
|
|
20171
|
-
existingKeys.add(key);
|
|
20316
|
+
if (registerPushMessageAndCheckDuplicate(msg, dedupState)) {
|
|
20317
|
+
duplicateCount++;
|
|
20318
|
+
continue;
|
|
20172
20319
|
}
|
|
20173
20320
|
let memberId = memberIdCache.get(msg.sender);
|
|
20174
20321
|
if (!memberId) {
|
|
20175
|
-
insertMinimalMember.run(
|
|
20322
|
+
const insertResult = insertMinimalMember.run(
|
|
20323
|
+
msg.sender,
|
|
20324
|
+
normalizeSenderAccountName(msg.sender, msg.accountName) || null,
|
|
20325
|
+
normalizeSenderGroupNickname(msg.sender, msg.groupNickname) || null
|
|
20326
|
+
);
|
|
20176
20327
|
const row = getMemberId.get(msg.sender);
|
|
20177
20328
|
if (row) {
|
|
20178
20329
|
memberId = row.id;
|
|
20179
20330
|
memberIdCache.set(msg.sender, memberId);
|
|
20331
|
+
if (isCountedMember(msg.sender)) membersAdded += insertResult.changes;
|
|
20180
20332
|
}
|
|
20181
20333
|
}
|
|
20182
20334
|
if (!memberId) continue;
|
|
@@ -20195,7 +20347,7 @@ function writeMessages(db, messages, existingPmids, existingKeys) {
|
|
|
20195
20347
|
writtenCount++;
|
|
20196
20348
|
}
|
|
20197
20349
|
});
|
|
20198
|
-
return { writtenCount, duplicateCount, minWrittenTs, ftsEntries };
|
|
20350
|
+
return { writtenCount, duplicateCount, membersAdded, minWrittenTs, ftsEntries };
|
|
20199
20351
|
}
|
|
20200
20352
|
function fullImport(db, meta, members, messages) {
|
|
20201
20353
|
db.exec(CHAT_DB_SCHEMA);
|
|
@@ -20212,29 +20364,13 @@ function fullImport(db, meta, members, messages) {
|
|
|
20212
20364
|
}
|
|
20213
20365
|
}
|
|
20214
20366
|
const allMembers = extraMembers.length > 0 ? [...members, ...extraMembers] : members;
|
|
20215
|
-
const
|
|
20216
|
-
const hashKeys = /* @__PURE__ */ new Set();
|
|
20367
|
+
const dedupState = createMessageDedupState();
|
|
20217
20368
|
let duplicateCount = 0;
|
|
20218
20369
|
const dedupedMessages = [];
|
|
20219
20370
|
for (const msg of messages) {
|
|
20220
|
-
|
|
20221
|
-
|
|
20222
|
-
|
|
20223
|
-
duplicateCount++;
|
|
20224
|
-
continue;
|
|
20225
|
-
}
|
|
20226
|
-
if (hashKeys.has(key)) {
|
|
20227
|
-
duplicateCount++;
|
|
20228
|
-
continue;
|
|
20229
|
-
}
|
|
20230
|
-
pmids.add(msg.platformMessageId);
|
|
20231
|
-
hashKeys.add(key);
|
|
20232
|
-
} else {
|
|
20233
|
-
if (hashKeys.has(key)) {
|
|
20234
|
-
duplicateCount++;
|
|
20235
|
-
continue;
|
|
20236
|
-
}
|
|
20237
|
-
hashKeys.add(key);
|
|
20371
|
+
if (registerPushMessageAndCheckDuplicate(msg, dedupState)) {
|
|
20372
|
+
duplicateCount++;
|
|
20373
|
+
continue;
|
|
20238
20374
|
}
|
|
20239
20375
|
dedupedMessages.push(msg);
|
|
20240
20376
|
}
|
|
@@ -20262,15 +20398,12 @@ function fullImport(db, meta, members, messages) {
|
|
|
20262
20398
|
generateSessionIndex(db);
|
|
20263
20399
|
} catch {
|
|
20264
20400
|
}
|
|
20265
|
-
return { writtenCount: stats.messageCount, duplicateCount
|
|
20401
|
+
return { writtenCount: stats.messageCount, duplicateCount };
|
|
20266
20402
|
}
|
|
20267
|
-
function
|
|
20403
|
+
function writeIncrementalImport(db, payload) {
|
|
20268
20404
|
const metaUpdateMode = payload.options?.metaUpdateMode ?? "patch";
|
|
20269
20405
|
const memberUpdateMode = payload.options?.memberUpdateMode ?? "upsert";
|
|
20270
|
-
const
|
|
20271
|
-
const existingKeys = /* @__PURE__ */ new Set();
|
|
20272
|
-
db.prepare("SELECT platform_message_id FROM message WHERE platform_message_id IS NOT NULL").all().forEach((r) => existingPmids.add(r.platform_message_id));
|
|
20273
|
-
db.prepare(`SELECT msg.ts, m.platform_id, msg.content FROM message msg JOIN member m ON msg.sender_id = m.id`).all().forEach((r) => existingKeys.add(generateMessageKey(r.ts, r.platform_id, r.content)));
|
|
20406
|
+
const dedupState = createExistingMessageDedupState(db);
|
|
20274
20407
|
let metaUpdated = false;
|
|
20275
20408
|
let membersAdded = 0;
|
|
20276
20409
|
let membersUpdated = 0;
|
|
@@ -20307,22 +20440,23 @@ function incrementalImport2(db, payload) {
|
|
|
20307
20440
|
m.roles ? JSON.stringify(m.roles) : "[]"
|
|
20308
20441
|
);
|
|
20309
20442
|
if (!existed) {
|
|
20310
|
-
membersAdded++;
|
|
20443
|
+
if (isCountedMember(m.platformId)) membersAdded++;
|
|
20311
20444
|
const row = getMemberId.get(m.platformId);
|
|
20312
20445
|
if (row) existingMemberIds.add(m.platformId);
|
|
20313
|
-
} else {
|
|
20446
|
+
} else if (isCountedMember(m.platformId)) {
|
|
20314
20447
|
membersUpdated++;
|
|
20315
20448
|
}
|
|
20316
20449
|
}
|
|
20317
20450
|
});
|
|
20318
20451
|
}
|
|
20319
20452
|
const preWriteMaxTs = db.prepare("SELECT MAX(ts) as max_ts FROM message").get()?.max_ts ?? 0;
|
|
20320
|
-
const {
|
|
20321
|
-
|
|
20322
|
-
|
|
20323
|
-
|
|
20324
|
-
|
|
20325
|
-
|
|
20453
|
+
const {
|
|
20454
|
+
writtenCount,
|
|
20455
|
+
duplicateCount,
|
|
20456
|
+
membersAdded: autoCreatedMembersAdded,
|
|
20457
|
+
minWrittenTs,
|
|
20458
|
+
ftsEntries
|
|
20459
|
+
} = writeMessages(db, payload.messages, dedupState);
|
|
20326
20460
|
if (ftsEntries.length > 0) {
|
|
20327
20461
|
try {
|
|
20328
20462
|
insertFtsEntries(db, ftsEntries);
|
|
@@ -20342,18 +20476,40 @@ function incrementalImport2(db, payload) {
|
|
|
20342
20476
|
if (!metaUpdated) {
|
|
20343
20477
|
db.prepare("UPDATE meta SET imported_at = ?").run(Math.floor(Date.now() / 1e3));
|
|
20344
20478
|
}
|
|
20345
|
-
return {
|
|
20479
|
+
return {
|
|
20480
|
+
writtenCount,
|
|
20481
|
+
duplicateCount,
|
|
20482
|
+
metaUpdated,
|
|
20483
|
+
membersAdded: membersAdded + autoCreatedMembersAdded,
|
|
20484
|
+
membersUpdated
|
|
20485
|
+
};
|
|
20486
|
+
}
|
|
20487
|
+
function incrementalImport2(db, payload) {
|
|
20488
|
+
return db.transaction(() => writeIncrementalImport(db, payload));
|
|
20346
20489
|
}
|
|
20347
|
-
var SAFE_SESSION_ID_RE = /^[a-zA-Z0-9_@-][a-zA-Z0-9_@.-]*$/;
|
|
20348
20490
|
async function pushImport(dbManager, sessionId, payload) {
|
|
20349
|
-
if (!SAFE_SESSION_ID_RE.test(sessionId) || sessionId.includes("..")) {
|
|
20350
|
-
return { ok: false, reason: "invalid_payload", message: "sessionId contains invalid characters" };
|
|
20351
|
-
}
|
|
20352
20491
|
try {
|
|
20353
|
-
return await withDataDirImportLock(
|
|
20354
|
-
|
|
20355
|
-
|
|
20356
|
-
|
|
20492
|
+
return await withDataDirImportLock(dbManager.getUserDataDir(), async () => {
|
|
20493
|
+
const outcome = await executePushImportUnlocked(
|
|
20494
|
+
{
|
|
20495
|
+
getDbPath: (id) => dbManager.getDbPath(id),
|
|
20496
|
+
openDatabase: (id, options2) => dbManager.openRawSessionDatabase(id, options2),
|
|
20497
|
+
deleteDatabase: (id) => {
|
|
20498
|
+
dbManager.deleteSessionDatabaseFiles(id);
|
|
20499
|
+
}
|
|
20500
|
+
},
|
|
20501
|
+
sessionId,
|
|
20502
|
+
payload
|
|
20503
|
+
);
|
|
20504
|
+
if (!outcome.ok) return outcome;
|
|
20505
|
+
try {
|
|
20506
|
+
dbManager.raiseCurrentChatDbCompatibilityGate();
|
|
20507
|
+
} catch (error) {
|
|
20508
|
+
if (outcome.result.created) dbManager.deleteSessionDatabaseFiles(sessionId);
|
|
20509
|
+
throw error;
|
|
20510
|
+
}
|
|
20511
|
+
return outcome;
|
|
20512
|
+
});
|
|
20357
20513
|
} catch (error) {
|
|
20358
20514
|
if (error instanceof ImportInProgressError) {
|
|
20359
20515
|
return {
|
|
@@ -20365,8 +20521,11 @@ async function pushImport(dbManager, sessionId, payload) {
|
|
|
20365
20521
|
throw error;
|
|
20366
20522
|
}
|
|
20367
20523
|
}
|
|
20368
|
-
async function
|
|
20369
|
-
|
|
20524
|
+
async function executePushImportUnlocked(deps, sessionId, payload) {
|
|
20525
|
+
if (!isValidImportSessionId(sessionId)) {
|
|
20526
|
+
return { ok: false, reason: "invalid_payload", message: "sessionId contains invalid characters" };
|
|
20527
|
+
}
|
|
20528
|
+
const dbPath = deps.getDbPath(sessionId);
|
|
20370
20529
|
const isNew = !fs34.existsSync(dbPath);
|
|
20371
20530
|
const validationError = validatePayload(payload, isNew);
|
|
20372
20531
|
if (validationError) {
|
|
@@ -20374,17 +20533,12 @@ async function pushImportUnlocked(dbManager, sessionId, payload) {
|
|
|
20374
20533
|
}
|
|
20375
20534
|
try {
|
|
20376
20535
|
if (isNew) {
|
|
20377
|
-
const db2 =
|
|
20536
|
+
const db2 = deps.openDatabase(sessionId, { create: true });
|
|
20378
20537
|
try {
|
|
20379
|
-
const { writtenCount, duplicateCount
|
|
20380
|
-
db2,
|
|
20381
|
-
payload.meta,
|
|
20382
|
-
payload.members ?? [],
|
|
20383
|
-
payload.messages
|
|
20384
|
-
);
|
|
20538
|
+
const { writtenCount, duplicateCount } = fullImport(db2, payload.meta, payload.members ?? [], payload.messages);
|
|
20385
20539
|
const session = queryStats(db2);
|
|
20386
|
-
|
|
20387
|
-
|
|
20540
|
+
const membersAdded = session.memberCount;
|
|
20541
|
+
const outcome = {
|
|
20388
20542
|
ok: true,
|
|
20389
20543
|
result: {
|
|
20390
20544
|
sessionId,
|
|
@@ -20394,16 +20548,17 @@ async function pushImportUnlocked(dbManager, sessionId, payload) {
|
|
|
20394
20548
|
updates: { metaUpdated: true, membersAdded, membersUpdated: 0 }
|
|
20395
20549
|
}
|
|
20396
20550
|
};
|
|
20551
|
+
appLogger.info("push-import", "Push import completed", outcome.result);
|
|
20552
|
+
return outcome;
|
|
20397
20553
|
} finally {
|
|
20398
20554
|
db2.close();
|
|
20399
20555
|
}
|
|
20400
20556
|
}
|
|
20401
|
-
const db =
|
|
20557
|
+
const db = deps.openDatabase(sessionId, { readonly: false });
|
|
20402
20558
|
try {
|
|
20403
20559
|
const { writtenCount, duplicateCount, metaUpdated, membersAdded, membersUpdated } = incrementalImport2(db, payload);
|
|
20404
20560
|
const session = queryStats(db);
|
|
20405
|
-
|
|
20406
|
-
return {
|
|
20561
|
+
const outcome = {
|
|
20407
20562
|
ok: true,
|
|
20408
20563
|
result: {
|
|
20409
20564
|
sessionId,
|
|
@@ -20413,6 +20568,8 @@ async function pushImportUnlocked(dbManager, sessionId, payload) {
|
|
|
20413
20568
|
updates: { metaUpdated, membersAdded, membersUpdated }
|
|
20414
20569
|
}
|
|
20415
20570
|
};
|
|
20571
|
+
appLogger.info("push-import", "Push import completed", outcome.result);
|
|
20572
|
+
return outcome;
|
|
20416
20573
|
} finally {
|
|
20417
20574
|
db.close();
|
|
20418
20575
|
}
|
|
@@ -20420,14 +20577,54 @@ async function pushImportUnlocked(dbManager, sessionId, payload) {
|
|
|
20420
20577
|
if (err instanceof DataDirCompatibilityError) throw err;
|
|
20421
20578
|
if (isNew) {
|
|
20422
20579
|
try {
|
|
20423
|
-
|
|
20580
|
+
deps.deleteDatabase(sessionId);
|
|
20424
20581
|
} catch {
|
|
20425
20582
|
}
|
|
20426
20583
|
}
|
|
20584
|
+
appLogger.error("push-import", "Push import failed", err);
|
|
20427
20585
|
return { ok: false, reason: "import_failed", message: err instanceof Error ? err.message : String(err) };
|
|
20428
20586
|
}
|
|
20429
20587
|
}
|
|
20430
20588
|
|
|
20589
|
+
// ../../packages/node-runtime/src/services/import-idempotency.ts
|
|
20590
|
+
import { createHash as createHash4 } from "crypto";
|
|
20591
|
+
var DEFAULT_IMPORT_IDEMPOTENCY_TTL_MS = 60 * 60 * 1e3;
|
|
20592
|
+
function hashImportBody(body) {
|
|
20593
|
+
return createHash4("sha256").update(JSON.stringify(body ?? "")).digest("hex");
|
|
20594
|
+
}
|
|
20595
|
+
var ImportIdempotencyCache = class {
|
|
20596
|
+
constructor(ttlMs = DEFAULT_IMPORT_IDEMPOTENCY_TTL_MS) {
|
|
20597
|
+
this.ttlMs = ttlMs;
|
|
20598
|
+
}
|
|
20599
|
+
ttlMs;
|
|
20600
|
+
entries = /* @__PURE__ */ new Map();
|
|
20601
|
+
start(key, bodyHash, now = Date.now()) {
|
|
20602
|
+
this.prune(now);
|
|
20603
|
+
const entry = this.entries.get(key);
|
|
20604
|
+
if (!entry) {
|
|
20605
|
+
this.entries.set(key, { bodyHash, status: "pending", timestamp: now });
|
|
20606
|
+
return { status: "started" };
|
|
20607
|
+
}
|
|
20608
|
+
if (entry.bodyHash !== bodyHash) return { status: "conflict" };
|
|
20609
|
+
if (entry.status === "pending") return { status: "pending" };
|
|
20610
|
+
return { status: "success", response: entry.response };
|
|
20611
|
+
}
|
|
20612
|
+
success(key, response) {
|
|
20613
|
+
const entry = this.entries.get(key);
|
|
20614
|
+
if (!entry) return;
|
|
20615
|
+
entry.status = "success";
|
|
20616
|
+
entry.response = response;
|
|
20617
|
+
}
|
|
20618
|
+
fail(key) {
|
|
20619
|
+
this.entries.delete(key);
|
|
20620
|
+
}
|
|
20621
|
+
prune(now) {
|
|
20622
|
+
for (const [key, entry] of this.entries) {
|
|
20623
|
+
if (now - entry.timestamp > this.ttlMs) this.entries.delete(key);
|
|
20624
|
+
}
|
|
20625
|
+
}
|
|
20626
|
+
};
|
|
20627
|
+
|
|
20431
20628
|
export {
|
|
20432
20629
|
writeParseResultToDb,
|
|
20433
20630
|
detectFormat,
|
|
@@ -20466,8 +20663,10 @@ export {
|
|
|
20466
20663
|
streamParseFileInfo,
|
|
20467
20664
|
analyzeIncrementalImport,
|
|
20468
20665
|
incrementalImport,
|
|
20666
|
+
isValidImportSessionId,
|
|
20469
20667
|
resolveAutoImportTarget,
|
|
20470
20668
|
autoImportFile,
|
|
20669
|
+
analyzeAutoImportFile,
|
|
20471
20670
|
IMPORT_LOCK_FILENAME,
|
|
20472
20671
|
IMPORT_IN_PROGRESS_ERROR_KEY,
|
|
20473
20672
|
ImportInProgressError,
|
|
@@ -20636,6 +20835,10 @@ export {
|
|
|
20636
20835
|
createSemanticIndexService,
|
|
20637
20836
|
SemanticIndexService,
|
|
20638
20837
|
MergeSessionCache,
|
|
20639
|
-
pushImport
|
|
20838
|
+
pushImport,
|
|
20839
|
+
executePushImportUnlocked,
|
|
20840
|
+
DEFAULT_IMPORT_IDEMPOTENCY_TTL_MS,
|
|
20841
|
+
hashImportBody,
|
|
20842
|
+
ImportIdempotencyCache
|
|
20640
20843
|
};
|
|
20641
|
-
//# sourceMappingURL=chunk-
|
|
20844
|
+
//# sourceMappingURL=chunk-KCPGEILH.mjs.map
|