chatlab-cli 0.34.2 → 0.35.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chat-command-6IOH2QRF.mjs → chat-command-24RYGJAS.mjs} +8 -8
- package/dist/{chunk-INBJ7SND.mjs → chunk-22YJOBA3.mjs} +589 -513
- package/dist/chunk-22YJOBA3.mjs.map +1 -0
- package/dist/{chunk-4LW6443F.mjs → chunk-4GWWKHWA.mjs} +833 -886
- package/dist/chunk-4GWWKHWA.mjs.map +1 -0
- package/dist/{chunk-MENKFEPC.mjs → chunk-7IGBPZMI.mjs} +56 -48
- package/dist/chunk-7IGBPZMI.mjs.map +1 -0
- package/dist/{chunk-72RWABYJ.mjs → chunk-BRZ6YIBR.mjs} +3 -3
- package/dist/chunk-BRZ6YIBR.mjs.map +1 -0
- package/dist/{chunk-2XCNTG4O.mjs → chunk-DC7IUDNV.mjs} +677 -69
- package/dist/chunk-DC7IUDNV.mjs.map +1 -0
- package/dist/{chunk-H6D4DZOC.mjs → chunk-DEFEU567.mjs} +12 -18
- package/dist/chunk-DEFEU567.mjs.map +1 -0
- package/dist/{chunk-4RLZFGO5.mjs → chunk-GQMCDWZQ.mjs} +2 -2
- package/dist/{chunk-52P5ZRBD.mjs → chunk-KIMJXKZO.mjs} +2 -2
- package/dist/{chunk-652KEAX3.mjs → chunk-KXDJ7N6N.mjs} +2 -2
- package/dist/{chunk-U6MVJSXX.mjs → chunk-QKEARRFH.mjs} +15 -9
- package/dist/chunk-QKEARRFH.mjs.map +1 -0
- package/dist/{chunk-UMBEHBIZ.mjs → chunk-U36TQMAF.mjs} +2 -2
- package/dist/{chunk-JX6MFS6F.mjs → chunk-ZBPPLXM4.mjs} +15 -15
- package/dist/cli.mjs +9 -9
- package/dist/contacts-worker.mjs +2 -2
- package/dist/global-insight-worker.mjs +1 -1
- package/dist/{http-HSK46XEU.mjs → http-3K4YMWDE.mjs} +12 -12
- package/dist/{import-DF2WD2HE.mjs → import-D4LV565A.mjs} +7 -7
- package/dist/index.mjs +12 -12
- package/dist/{mcp-A2RND7KN.mjs → mcp-KL22FKQ2.mjs} +8 -8
- package/dist/people-relationships-worker.mjs +2 -2
- package/dist/semantic-index-worker.mjs +1 -1
- package/dist/{src-SHFHW2QB.mjs → src-COIB6Z4P.mjs} +24 -38
- package/dist/{stream-import-GZ2QDE3Z.mjs → stream-import-FSXCFG4M.mjs} +7 -7
- package/dist/{worker-runtime-OWN56GG5.mjs → worker-runtime-DPS2LURI.mjs} +5 -5
- package/dist-cli-web/assets/AISettingsTab-rQC2ocFH.js +1 -0
- package/dist-cli-web/assets/AboutTab-DMPziKjj.js +1 -0
- package/dist-cli-web/assets/ActionToolsPanel.vue_vue_type_script_setup_true_lang-CnRLt_1j.js +110 -0
- package/dist-cli-web/assets/Alert--bTQXRcZ.js +1 -0
- package/dist-cli-web/assets/ApiSettingsTab-CtXl4I28.js +2 -0
- package/dist-cli-web/assets/BatchManageTab-1KaCPweN.js +1 -0
- package/dist-cli-web/assets/{BufferResource-BdKXeRZS.js → BufferResource-BXSJADxx.js} +1 -1
- package/dist-cli-web/assets/CanvasRenderer-Cq--8UQ_.js +1 -0
- package/dist-cli-web/assets/ChatRecordDrawer-BC0F7ok1.js +11 -0
- package/dist-cli-web/assets/ChatRecordDrawer-Tt6UduRC.css +1 -0
- package/dist-cli-web/assets/Checkbox-CwajNPGF.js +1 -0
- package/dist-cli-web/assets/DataDirCleanupNotice-D0Oruka2.js +1 -0
- package/dist-cli-web/assets/DatePicker.vue_vue_type_script_setup_true_lang-C4CsoIlq.js +1 -0
- package/dist-cli-web/assets/DebugToolsPanel-DVDwS-wz.js +1 -0
- package/dist-cli-web/assets/EChartWordcloud-KV0MdsDE.js +1 -0
- package/dist-cli-web/assets/{Filter-B2eEAPxI.js → Filter-DVaqcFmI.js} +1 -1
- package/dist-cli-web/assets/GlobalTaskBar-COW_uBX6.css +1 -0
- package/dist-cli-web/assets/GlobalTaskBar-DpPMX0jx.js +1 -0
- package/dist-cli-web/assets/GroupRelationshipGalaxyView-CLnO3phW.js +1 -0
- package/dist-cli-web/assets/Label-Bt2U4lGy.js +1 -0
- package/dist-cli-web/assets/LoadingState.vue_vue_type_script_setup_true_lang-Cnshyn0f.js +1 -0
- package/dist-cli-web/assets/MessageExportModal-BozaR9UZ.js +1 -0
- package/dist-cli-web/assets/PageHeader-CEe0xTl3.js +1 -0
- package/dist-cli-web/assets/PageTabs.vue_vue_type_script_setup_true_lang-D2DZrrnv.js +1 -0
- package/dist-cli-web/assets/{RelationshipGalaxyCanvas-CwQ63TPi.js → RelationshipGalaxyCanvas-6EamagxG.js} +3 -3
- package/dist-cli-web/assets/{RelationshipGalaxyThreeCanvas-BLqISQe5.js → RelationshipGalaxyThreeCanvas-B6GB4PWS.js} +166 -166
- package/dist-cli-web/assets/{RelationshipGalaxyThreeCanvas-CMuibfiA.css → RelationshipGalaxyThreeCanvas-TYGOUDiv.css} +1 -1
- package/dist-cli-web/assets/{RenderTargetSystem-eC_VrDAr.js → RenderTargetSystem-mYuj4uBS.js} +1 -1
- package/dist-cli-web/assets/RovingFocusItem-B2y6BG1a.js +1 -0
- package/dist-cli-web/assets/ScreenCaptureModal-BvAtSNNR.js +1 -0
- package/dist-cli-web/assets/SectionTabs.vue_vue_type_script_setup_true_lang-ueEF4zHE.js +1 -0
- package/dist-cli-web/assets/Select-Cm4I8u9n.js +1 -0
- package/dist-cli-web/assets/SettingsModal-CjVax3I6.css +1 -0
- package/dist-cli-web/assets/SettingsModal-Cwshzj7i.js +2 -0
- package/dist-cli-web/assets/StorageTab-D7HzH16Y.js +1 -0
- package/dist-cli-web/assets/Switch-BVw4e_19.js +1 -0
- package/dist-cli-web/assets/Tabs-Dqwkh3Ea.js +1 -0
- package/dist-cli-web/assets/Tabs-OrJkANIA.js +1 -0
- package/dist-cli-web/assets/ThemeCard.vue_vue_type_script_setup_true_lang-DPIvbq75.js +1 -0
- package/dist-cli-web/assets/TimeSelect.vue_vue_type_script_setup_true_lang-dEdo6CqM.js +1 -0
- package/dist-cli-web/assets/VisuallyHiddenInput-BOdkR_GC.js +1 -0
- package/dist-cli-web/assets/{WebGLRenderer-CRp_HC84.js → WebGLRenderer-BB8ukBI_.js} +2 -2
- package/dist-cli-web/assets/{WebGPURenderer-WIUnEhpi.js → WebGPURenderer-DDOzF1Kc.js} +2 -2
- package/dist-cli-web/assets/aiChat-CzFSFuOJ.js +8 -0
- package/dist-cli-web/assets/annual-summary-time-range-CmrAqcuj.js +1 -0
- package/dist-cli-web/assets/base-CawUl6ET.js +1 -0
- package/dist-cli-web/assets/{browserAll-B29Ox2in.js → browserAll-BRqlnNxq.js} +2 -2
- package/dist-cli-web/assets/chatlabSiteLocale-CrCNb8QP.js +1 -0
- package/dist-cli-web/assets/clamp-jFYhBC76.js +1 -0
- package/dist-cli-web/assets/cli-web-ClAJ6Tao.js +1 -0
- package/dist-cli-web/assets/{dateFormat-T7kMS5IP.js → dateFormat-DXuDxIye.js} +1 -1
- package/dist-cli-web/assets/fetch-3_xX8Yi8.js +1 -0
- package/dist-cli-web/assets/{fetch-DsHf-61a.js → fetch-Bcy2T8Fe.js} +1 -1
- package/dist-cli-web/assets/{fetch-DvrVGAzR.js → fetch-CGu53cxT.js} +1 -1
- package/dist-cli-web/assets/{fetch-BBgZQob_.js → fetch-CP1afnT0.js} +1 -1
- package/dist-cli-web/assets/{fetch-D9sn99zX.js → fetch-DWAIk_6_.js} +1 -1
- package/dist-cli-web/assets/{fetch-BKBlpBl4.js → fetch-DijSOvop.js} +1 -1
- package/dist-cli-web/assets/{fetch-CYaGyohV.js → fetch-Dy38lkAa.js} +1 -1
- package/dist-cli-web/assets/{fetch-L6PKD8-N.js → fetch-DzKlTaLz.js} +1 -1
- package/dist-cli-web/assets/{fetch-D8hJZa8o.js → fetch-LY-yerw1.js} +1 -1
- package/dist-cli-web/assets/{fetch-BLi6Jsjg.js → fetch-q480XYZQ.js} +1 -1
- package/dist-cli-web/assets/index-0dp58uL4.css +1 -0
- package/dist-cli-web/assets/index-1X7zdLgJ.css +1 -0
- package/dist-cli-web/assets/index-4enwdBcq.css +1 -0
- package/dist-cli-web/assets/index-AudHHbMs.js +1 -0
- package/dist-cli-web/assets/{index-0qcENyvH.js → index-BRV_D8dv.js} +3 -3
- package/dist-cli-web/assets/index-CF4DTbvD.js +1 -0
- package/dist-cli-web/assets/index-CMLoht5R.js +1 -0
- package/dist-cli-web/assets/index-CMPHZH6B.css +1 -0
- package/dist-cli-web/assets/index-CNzv7aBW.js +1 -0
- package/dist-cli-web/assets/index-CXUTiy-V.js +1 -0
- package/dist-cli-web/assets/index-D5m0P4ss.js +36 -0
- package/dist-cli-web/assets/index-Di6FmCdk.js +64 -0
- package/dist-cli-web/assets/index-DkOrt7_X.js +2 -0
- package/dist-cli-web/assets/index-Dlb6fC68.js +1 -0
- package/dist-cli-web/assets/index-IA7YUjQX.js +2 -0
- package/dist-cli-web/assets/index-kzU381y1.js +1 -0
- package/dist-cli-web/assets/index-z8Vogc-b.js +137 -0
- package/dist-cli-web/assets/lazy-overlay-visibility-Bd9ZFOgR.js +1 -0
- package/dist-cli-web/assets/member-select-utils-BXZ3oHct.js +1 -0
- package/dist-cli-web/assets/people-page-header-Bh3KXD4g.js +1 -0
- package/dist-cli-web/assets/prompt-DcAcgQiB.js +1 -0
- package/dist-cli-web/assets/relationship-galaxy-connections-Cg4G5yYE.js +1 -0
- package/dist-cli-web/assets/semantic-index-config-builder-CK6v64Y1.js +1 -0
- package/dist-cli-web/assets/service-YZnJyEMq.js +1 -0
- package/dist-cli-web/assets/useFormControl-B-aXrJZN.js +1 -0
- package/dist-cli-web/assets/useTable-DPEMaGby.js +1 -0
- package/dist-cli-web/assets/useToast-B36MzFFy.js +1 -0
- package/dist-cli-web/assets/vendor-echarts-DfAMQxdr.js +11 -0
- package/dist-cli-web/assets/{vendor-echarts-wordcloud-Ca8n6qS6.js → vendor-echarts-wordcloud-BsaEJTPY.js} +1 -1
- package/dist-cli-web/assets/vendor-zrender-PFmZe1EY.js +8 -0
- package/dist-cli-web/assets/{webworkerAll-B2qvP2qR.js → webworkerAll-Dwqmy_YO.js} +1 -1
- package/dist-cli-web/index.html +2 -2
- package/package.json +1 -1
- package/dist/chunk-2XCNTG4O.mjs.map +0 -1
- package/dist/chunk-4LW6443F.mjs.map +0 -1
- package/dist/chunk-72RWABYJ.mjs.map +0 -1
- package/dist/chunk-H6D4DZOC.mjs.map +0 -1
- package/dist/chunk-INBJ7SND.mjs.map +0 -1
- package/dist/chunk-MENKFEPC.mjs.map +0 -1
- package/dist/chunk-U6MVJSXX.mjs.map +0 -1
- package/dist-cli-web/assets/ActionToolsPanel.vue_vue_type_script_setup_true_lang-B06LYYS9.js +0 -110
- package/dist-cli-web/assets/CanvasRenderer-DDRdIyZ8.js +0 -1
- package/dist-cli-web/assets/DataDirCleanupNotice-CP8P1lsL.js +0 -1
- package/dist-cli-web/assets/EChartWordcloud-CqknPGui.js +0 -1
- package/dist-cli-web/assets/LoadingState.vue_vue_type_script_setup_true_lang-BFaXTz1c.js +0 -1
- package/dist-cli-web/assets/MessageExportModal-CZge6qub.js +0 -1
- package/dist-cli-web/assets/PageHeader-CxKbcLDc.js +0 -1
- package/dist-cli-web/assets/ThemeCard.vue_vue_type_script_setup_true_lang-DVmpDz05.js +0 -1
- package/dist-cli-web/assets/TimeSelect.vue_vue_type_script_setup_true_lang-CCH1UQGc.js +0 -1
- package/dist-cli-web/assets/annual-summary-time-range-R3pLPEuN.js +0 -1
- package/dist-cli-web/assets/base-CLwWn3Rs.js +0 -1
- package/dist-cli-web/assets/cli-web-CVLb5Pkq.js +0 -1
- package/dist-cli-web/assets/fetch-Cm2WR2Cv.js +0 -1
- package/dist-cli-web/assets/index-AkF_KqVI.js +0 -1
- package/dist-cli-web/assets/index-BVOFyB9E.js +0 -137
- package/dist-cli-web/assets/index-BkdOH41D.js +0 -1
- package/dist-cli-web/assets/index-C4TuoOLM.js +0 -2
- package/dist-cli-web/assets/index-C6Mk-Xqt.js +0 -1
- package/dist-cli-web/assets/index-CkgUsaeX.js +0 -1
- package/dist-cli-web/assets/index-CuAy1Xmt.js +0 -64
- package/dist-cli-web/assets/index-D_0qeKKc.js +0 -53
- package/dist-cli-web/assets/index-DeR0Vukh.js +0 -1
- package/dist-cli-web/assets/index-DrjoMv9s.css +0 -1
- package/dist-cli-web/assets/index-Dx-XvfIK.js +0 -2
- package/dist-cli-web/assets/index-Jhwca9kg.css +0 -1
- package/dist-cli-web/assets/index-_DrviTnP.js +0 -1
- package/dist-cli-web/assets/index-vtF05fh7.css +0 -1
- package/dist-cli-web/assets/index-xN0sjOLK.js +0 -1
- package/dist-cli-web/assets/people-page-header-CCNJOrmK.js +0 -1
- package/dist-cli-web/assets/vendor-echarts-B-i_56Av.js +0 -11
- package/dist-cli-web/assets/vendor-zrender-5DcYjSZk.js +0 -8
- /package/dist/{chat-command-6IOH2QRF.mjs.map → chat-command-24RYGJAS.mjs.map} +0 -0
- /package/dist/{chunk-4RLZFGO5.mjs.map → chunk-GQMCDWZQ.mjs.map} +0 -0
- /package/dist/{chunk-52P5ZRBD.mjs.map → chunk-KIMJXKZO.mjs.map} +0 -0
- /package/dist/{chunk-652KEAX3.mjs.map → chunk-KXDJ7N6N.mjs.map} +0 -0
- /package/dist/{chunk-UMBEHBIZ.mjs.map → chunk-U36TQMAF.mjs.map} +0 -0
- /package/dist/{chunk-JX6MFS6F.mjs.map → chunk-ZBPPLXM4.mjs.map} +0 -0
- /package/dist/{http-HSK46XEU.mjs.map → http-3K4YMWDE.mjs.map} +0 -0
- /package/dist/{import-DF2WD2HE.mjs.map → import-D4LV565A.mjs.map} +0 -0
- /package/dist/{mcp-A2RND7KN.mjs.map → mcp-KL22FKQ2.mjs.map} +0 -0
- /package/dist/{src-SHFHW2QB.mjs.map → src-COIB6Z4P.mjs.map} +0 -0
- /package/dist/{stream-import-GZ2QDE3Z.mjs.map → stream-import-FSXCFG4M.mjs.map} +0 -0
- /package/dist/{worker-runtime-OWN56GG5.mjs.map → worker-runtime-DPS2LURI.mjs.map} +0 -0
|
@@ -40,13 +40,17 @@ var cliConfigSchema = z.object({
|
|
|
40
40
|
/** Allow the read-only `sql` query command. */
|
|
41
41
|
allow_sql: z.boolean().default(true)
|
|
42
42
|
});
|
|
43
|
+
var desktopConfigSchema = z.object({
|
|
44
|
+
close_behavior: z.enum(["background", "quit"]).default("background")
|
|
45
|
+
});
|
|
43
46
|
var configSchema = z.object({
|
|
44
47
|
llm: llmConfigSchema.default({}),
|
|
45
48
|
data: dataConfigSchema.default({}),
|
|
46
49
|
api: apiConfigSchema.default({}),
|
|
47
50
|
locale: localeConfigSchema.default({}),
|
|
48
51
|
ui: uiConfigSchema.default({}),
|
|
49
|
-
cli: cliConfigSchema.default({})
|
|
52
|
+
cli: cliConfigSchema.default({}),
|
|
53
|
+
desktop: desktopConfigSchema.default({})
|
|
50
54
|
});
|
|
51
55
|
|
|
52
56
|
// ../../packages/config/src/loader.ts
|
|
@@ -4108,7 +4112,7 @@ function buildMsgConditions(options) {
|
|
|
4108
4112
|
|
|
4109
4113
|
// ../../packages/core/src/query/advanced/social.ts
|
|
4110
4114
|
function getMentionAnalysis(db, filter) {
|
|
4111
|
-
const
|
|
4115
|
+
const emptyResult2 = {
|
|
4112
4116
|
topMentioners: [],
|
|
4113
4117
|
topMentioned: [],
|
|
4114
4118
|
totalMentions: 0
|
|
@@ -4117,7 +4121,7 @@ function getMentionAnalysis(db, filter) {
|
|
|
4117
4121
|
`SELECT id, platform_id as platformId, COALESCE(group_nickname, account_name, platform_id) as name
|
|
4118
4122
|
FROM member WHERE COALESCE(account_name, '') != '\u7CFB\u7EDF\u6D88\u606F'`
|
|
4119
4123
|
).all();
|
|
4120
|
-
if (members.length === 0) return
|
|
4124
|
+
if (members.length === 0) return emptyResult2;
|
|
4121
4125
|
const nameToMemberId = /* @__PURE__ */ new Map();
|
|
4122
4126
|
const memberIdToInfo = /* @__PURE__ */ new Map();
|
|
4123
4127
|
for (const member of members) {
|
|
@@ -4158,7 +4162,7 @@ function getMentionAnalysis(db, filter) {
|
|
|
4158
4162
|
}
|
|
4159
4163
|
}
|
|
4160
4164
|
}
|
|
4161
|
-
if (totalMentions === 0) return
|
|
4165
|
+
if (totalMentions === 0) return emptyResult2;
|
|
4162
4166
|
const topMentioners = [];
|
|
4163
4167
|
for (const [memberId, count] of mentionerCount.entries()) {
|
|
4164
4168
|
const info = memberIdToInfo.get(memberId);
|
|
@@ -4185,87 +4189,13 @@ function getMentionAnalysis(db, filter) {
|
|
|
4185
4189
|
topMentioned.sort((a, b) => b.count - a.count);
|
|
4186
4190
|
return { topMentioners, topMentioned, totalMentions };
|
|
4187
4191
|
}
|
|
4188
|
-
function getMentionGraph(db, filter) {
|
|
4189
|
-
const emptyResult = { nodes: [], links: [], maxLinkValue: 0 };
|
|
4190
|
-
const { clause, params } = buildTimeFilter(filter);
|
|
4191
|
-
const msgFilterBase = clause ? clause.replace("WHERE", "AND") : "";
|
|
4192
|
-
const msgFilterWithSystem = msgFilterBase + " AND COALESCE(m.account_name, '') != '\u7CFB\u7EDF\u6D88\u606F'";
|
|
4193
|
-
const members = db.prepare(
|
|
4194
|
-
`SELECT m.id, m.platform_id as platformId, COALESCE(m.group_nickname, m.account_name, m.platform_id) as name,
|
|
4195
|
-
COUNT(msg.id) as messageCount
|
|
4196
|
-
FROM member m LEFT JOIN message msg ON m.id = msg.sender_id ${msgFilterWithSystem}
|
|
4197
|
-
WHERE COALESCE(m.account_name, '') != '\u7CFB\u7EDF\u6D88\u606F' GROUP BY m.id`
|
|
4198
|
-
).all(...params);
|
|
4199
|
-
if (members.length === 0) return emptyResult;
|
|
4200
|
-
const nameToMemberId = /* @__PURE__ */ new Map();
|
|
4201
|
-
const memberIdToInfo = /* @__PURE__ */ new Map();
|
|
4202
|
-
for (const member of members) {
|
|
4203
|
-
memberIdToInfo.set(member.id, { name: member.name, messageCount: member.messageCount });
|
|
4204
|
-
nameToMemberId.set(member.name, member.id);
|
|
4205
|
-
const history = db.prepare("SELECT name FROM member_name_history WHERE member_id = ?").all(member.id);
|
|
4206
|
-
for (const h of history) {
|
|
4207
|
-
if (!nameToMemberId.has(h.name)) nameToMemberId.set(h.name, member.id);
|
|
4208
|
-
}
|
|
4209
|
-
}
|
|
4210
|
-
let whereClause = clause;
|
|
4211
|
-
if (whereClause.includes("WHERE")) {
|
|
4212
|
-
whereClause += " AND COALESCE(m.account_name, '') != '\u7CFB\u7EDF\u6D88\u606F' AND msg.type = 0 AND msg.content IS NOT NULL AND msg.content LIKE '%@%'";
|
|
4213
|
-
} else {
|
|
4214
|
-
whereClause = " WHERE COALESCE(m.account_name, '') != '\u7CFB\u7EDF\u6D88\u606F' AND msg.type = 0 AND msg.content IS NOT NULL AND msg.content LIKE '%@%'";
|
|
4215
|
-
}
|
|
4216
|
-
const messages = db.prepare(
|
|
4217
|
-
`SELECT msg.sender_id as senderId, msg.content FROM message msg JOIN member m ON msg.sender_id = m.id ${whereClause}`
|
|
4218
|
-
).all(...params);
|
|
4219
|
-
const mentionMatrix = /* @__PURE__ */ new Map();
|
|
4220
|
-
const mentionRegex = /@([^\s@]+)/g;
|
|
4221
|
-
for (const msg of messages) {
|
|
4222
|
-
const matches = msg.content.matchAll(mentionRegex);
|
|
4223
|
-
const mentionedInThisMsg = /* @__PURE__ */ new Set();
|
|
4224
|
-
for (const match of matches) {
|
|
4225
|
-
const mentionedId = nameToMemberId.get(match[1]);
|
|
4226
|
-
if (mentionedId && mentionedId !== msg.senderId && !mentionedInThisMsg.has(mentionedId)) {
|
|
4227
|
-
mentionedInThisMsg.add(mentionedId);
|
|
4228
|
-
if (!mentionMatrix.has(msg.senderId)) mentionMatrix.set(msg.senderId, /* @__PURE__ */ new Map());
|
|
4229
|
-
const fromMap = mentionMatrix.get(msg.senderId);
|
|
4230
|
-
fromMap.set(mentionedId, (fromMap.get(mentionedId) || 0) + 1);
|
|
4231
|
-
}
|
|
4232
|
-
}
|
|
4233
|
-
}
|
|
4234
|
-
const involvedMemberIds = /* @__PURE__ */ new Set();
|
|
4235
|
-
for (const [fromId, toMap] of mentionMatrix.entries()) {
|
|
4236
|
-
involvedMemberIds.add(fromId);
|
|
4237
|
-
for (const toId of toMap.keys()) involvedMemberIds.add(toId);
|
|
4238
|
-
}
|
|
4239
|
-
const maxMessageCount = Math.max(...members.filter((m) => involvedMemberIds.has(m.id)).map((m) => m.messageCount), 1);
|
|
4240
|
-
const nodes = [];
|
|
4241
|
-
for (const memberId of involvedMemberIds) {
|
|
4242
|
-
const info = memberIdToInfo.get(memberId);
|
|
4243
|
-
if (info) {
|
|
4244
|
-
const symbolSize = 20 + info.messageCount / maxMessageCount * 40;
|
|
4245
|
-
nodes.push({ id: memberId, name: info.name, value: info.messageCount, symbolSize: Math.round(symbolSize) });
|
|
4246
|
-
}
|
|
4247
|
-
}
|
|
4248
|
-
const links = [];
|
|
4249
|
-
let maxLinkValue = 0;
|
|
4250
|
-
for (const [fromId, toMap] of mentionMatrix.entries()) {
|
|
4251
|
-
const fromInfo = memberIdToInfo.get(fromId);
|
|
4252
|
-
if (!fromInfo) continue;
|
|
4253
|
-
for (const [toId, count] of toMap.entries()) {
|
|
4254
|
-
const toInfo = memberIdToInfo.get(toId);
|
|
4255
|
-
if (!toInfo) continue;
|
|
4256
|
-
links.push({ source: fromInfo.name, target: toInfo.name, value: count });
|
|
4257
|
-
maxLinkValue = Math.max(maxLinkValue, count);
|
|
4258
|
-
}
|
|
4259
|
-
}
|
|
4260
|
-
return { nodes, links, maxLinkValue };
|
|
4261
|
-
}
|
|
4262
4192
|
function keywordToPattern(keyword) {
|
|
4263
4193
|
const escaped = keyword.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
4264
4194
|
if (keyword === "\u54C8\u54C8") return "\u54C8\u54C8+";
|
|
4265
4195
|
return escaped;
|
|
4266
4196
|
}
|
|
4267
4197
|
function getLaughAnalysis(db, filter, keywords) {
|
|
4268
|
-
const
|
|
4198
|
+
const emptyResult2 = {
|
|
4269
4199
|
rankByRate: [],
|
|
4270
4200
|
rankByCount: [],
|
|
4271
4201
|
typeDistribution: [],
|
|
@@ -4273,7 +4203,8 @@ function getLaughAnalysis(db, filter, keywords) {
|
|
|
4273
4203
|
totalMessages: 0,
|
|
4274
4204
|
groupLaughRate: 0
|
|
4275
4205
|
};
|
|
4276
|
-
const laughKeywords = keywords
|
|
4206
|
+
const laughKeywords = keywords?.map((keyword) => keyword.trim()).filter(Boolean) ?? [];
|
|
4207
|
+
if (laughKeywords.length === 0) return emptyResult2;
|
|
4277
4208
|
const patterns = laughKeywords.map(keywordToPattern);
|
|
4278
4209
|
const laughRegex = new RegExp(`(${patterns.join("|")})`, "gi");
|
|
4279
4210
|
const { clause, params } = buildTimeFilter(filter);
|
|
@@ -4288,7 +4219,7 @@ function getLaughAnalysis(db, filter, keywords) {
|
|
|
4288
4219
|
COALESCE(m.group_nickname, m.account_name, m.platform_id) as name
|
|
4289
4220
|
FROM message msg JOIN member m ON msg.sender_id = m.id ${whereClause}`
|
|
4290
4221
|
).all(...params);
|
|
4291
|
-
if (messages.length === 0) return
|
|
4222
|
+
if (messages.length === 0) return emptyResult2;
|
|
4292
4223
|
const memberStats = /* @__PURE__ */ new Map();
|
|
4293
4224
|
const typeCount = /* @__PURE__ */ new Map();
|
|
4294
4225
|
let totalLaughs = 0;
|
|
@@ -4321,7 +4252,7 @@ function getLaughAnalysis(db, filter, keywords) {
|
|
|
4321
4252
|
}
|
|
4322
4253
|
}
|
|
4323
4254
|
}
|
|
4324
|
-
if (totalLaughs === 0) return
|
|
4255
|
+
if (totalLaughs === 0) return emptyResult2;
|
|
4325
4256
|
const rankItems = [];
|
|
4326
4257
|
for (const [memberId, stats] of memberStats.entries()) {
|
|
4327
4258
|
if (stats.laughCount > 0) {
|
|
@@ -4411,7 +4342,7 @@ function accumulateCoOccurrencePairs(messages, options) {
|
|
|
4411
4342
|
}
|
|
4412
4343
|
function getClusterGraph(db, filter, options) {
|
|
4413
4344
|
const opts = { ...DEFAULT_CLUSTER_OPTIONS, ...options };
|
|
4414
|
-
const
|
|
4345
|
+
const emptyResult2 = {
|
|
4415
4346
|
nodes: [],
|
|
4416
4347
|
links: [],
|
|
4417
4348
|
maxLinkValue: 0,
|
|
@@ -4423,7 +4354,7 @@ function getClusterGraph(db, filter, options) {
|
|
|
4423
4354
|
(SELECT COUNT(*) FROM message WHERE sender_id = member.id) as messageCount
|
|
4424
4355
|
FROM member WHERE COALESCE(account_name, '') != '\u7CFB\u7EDF\u6D88\u606F'`
|
|
4425
4356
|
).all();
|
|
4426
|
-
if (members.length < 2) return { ...
|
|
4357
|
+
if (members.length < 2) return { ...emptyResult2, stats: { ...emptyResult2.stats, totalMembers: members.length } };
|
|
4427
4358
|
const memberInfo = /* @__PURE__ */ new Map();
|
|
4428
4359
|
for (const m of members)
|
|
4429
4360
|
memberInfo.set(m.id, { name: m.name, platformId: m.platformId, messageCount: m.messageCount });
|
|
@@ -4440,8 +4371,8 @@ function getClusterGraph(db, filter, options) {
|
|
|
4440
4371
|
).all(...params);
|
|
4441
4372
|
if (messages.length < 2)
|
|
4442
4373
|
return {
|
|
4443
|
-
...
|
|
4444
|
-
stats: { ...
|
|
4374
|
+
...emptyResult2,
|
|
4375
|
+
stats: { ...emptyResult2.stats, totalMembers: members.length, totalMessages: messages.length }
|
|
4445
4376
|
};
|
|
4446
4377
|
const memberMsgCount = /* @__PURE__ */ new Map();
|
|
4447
4378
|
for (const msg of messages) memberMsgCount.set(msg.senderId, (memberMsgCount.get(msg.senderId) || 0) + 1);
|
|
@@ -4480,8 +4411,8 @@ function getClusterGraph(db, filter, options) {
|
|
|
4480
4411
|
const keptEdges = edges.slice(0, opts.topEdges);
|
|
4481
4412
|
if (keptEdges.length === 0)
|
|
4482
4413
|
return {
|
|
4483
|
-
...
|
|
4484
|
-
stats: { ...
|
|
4414
|
+
...emptyResult2,
|
|
4415
|
+
stats: { ...emptyResult2.stats, totalMembers: members.length, totalMessages: messages.length }
|
|
4485
4416
|
};
|
|
4486
4417
|
const involvedIds = /* @__PURE__ */ new Set();
|
|
4487
4418
|
for (const edge of keptEdges) {
|
|
@@ -4754,7 +4685,7 @@ function getGroupRelationshipGraphFacts(db, ownerMemberId, options = {}) {
|
|
|
4754
4685
|
ORDER BY msg.ts ASC, msg.id ASC`
|
|
4755
4686
|
).all(...messageTimeFilter.params);
|
|
4756
4687
|
const edgeStats = /* @__PURE__ */ new Map();
|
|
4757
|
-
const
|
|
4688
|
+
const ensureEdge2 = (aId, bId) => {
|
|
4758
4689
|
const sourceId = Math.min(aId, bId);
|
|
4759
4690
|
const targetId = Math.max(aId, bId);
|
|
4760
4691
|
const key = `${sourceId}:${targetId}`;
|
|
@@ -4774,7 +4705,7 @@ function getGroupRelationshipGraphFacts(db, ownerMemberId, options = {}) {
|
|
|
4774
4705
|
};
|
|
4775
4706
|
for (const pair of accumulateCoOccurrencePairs(coOccurrenceRows)) {
|
|
4776
4707
|
if (!contactById.has(pair.sourceId) || !contactById.has(pair.targetId)) continue;
|
|
4777
|
-
const edge =
|
|
4708
|
+
const edge = ensureEdge2(pair.sourceId, pair.targetId);
|
|
4778
4709
|
edge.coOccurrenceCount += pair.coOccurrenceCount;
|
|
4779
4710
|
edge.coOccurrenceRawScore += pair.rawScore;
|
|
4780
4711
|
edge.lastInteractionTs = Math.max(edge.lastInteractionTs ?? 0, pair.lastOccurrenceTs);
|
|
@@ -4796,7 +4727,7 @@ function getGroupRelationshipGraphFacts(db, ownerMemberId, options = {}) {
|
|
|
4796
4727
|
for (const row of replyRows) {
|
|
4797
4728
|
if (!contactById.has(row.replySenderId) || !contactById.has(row.targetSenderId)) continue;
|
|
4798
4729
|
if (row.replySenderId === row.targetSenderId) continue;
|
|
4799
|
-
const edge =
|
|
4730
|
+
const edge = ensureEdge2(row.replySenderId, row.targetSenderId);
|
|
4800
4731
|
if (row.replySenderId === edge.sourceId) edge.repliesFromSourceToTarget++;
|
|
4801
4732
|
else edge.repliesFromTargetToSource++;
|
|
4802
4733
|
edge.lastInteractionTs = Math.max(edge.lastInteractionTs ?? 0, row.replyTs);
|
|
@@ -6004,12 +5935,456 @@ function getCatchphraseAnalysis(db, filter) {
|
|
|
6004
5935
|
return { members };
|
|
6005
5936
|
}
|
|
6006
5937
|
|
|
5938
|
+
// ../../packages/core/src/query/advanced/group-relationship-galaxy.ts
|
|
5939
|
+
var GROUP_RELATIONSHIP_GALAXY_ALGORITHM_VERSION = "group-relationship-galaxy-v1";
|
|
5940
|
+
var REPLY_WEIGHT = 3;
|
|
5941
|
+
var MENTION_WEIGHT = 2;
|
|
5942
|
+
var CO_OCCURRENCE_COUNT_WEIGHT = 0.05;
|
|
5943
|
+
var MIN_EDGE_WEIGHT = 1e-6;
|
|
5944
|
+
var DEFAULT_LIMITS = {
|
|
5945
|
+
nodeLimit: 400,
|
|
5946
|
+
edgeLimit: 1200,
|
|
5947
|
+
perNodeEdgeLimit: 10
|
|
5948
|
+
};
|
|
5949
|
+
var COMMUNITY_COLORS = [
|
|
5950
|
+
"#7dd3fc",
|
|
5951
|
+
"#c4b5fd",
|
|
5952
|
+
"#f0abfc",
|
|
5953
|
+
"#5eead4",
|
|
5954
|
+
"#fda4af",
|
|
5955
|
+
"#fde68a",
|
|
5956
|
+
"#93c5fd",
|
|
5957
|
+
"#6ee7b7",
|
|
5958
|
+
"#d8b4fe",
|
|
5959
|
+
"#fdba74"
|
|
5960
|
+
];
|
|
5961
|
+
function getGroupRelationshipGalaxy(db, filter, options = {}) {
|
|
5962
|
+
const limits = normalizeLimits(options);
|
|
5963
|
+
const allMembers = getNonSystemMembersForContacts(db);
|
|
5964
|
+
const memberById = new Map(allMembers.map((member) => [member.id, member]));
|
|
5965
|
+
const messageRows = queryRelationshipMessages(db, filter).filter((message) => memberById.has(message.senderId));
|
|
5966
|
+
const activeMembers = buildActiveMembers(allMembers, messageRows);
|
|
5967
|
+
const activeMemberById = new Map(activeMembers.map((member) => [member.id, member]));
|
|
5968
|
+
if (activeMembers.length < 2) {
|
|
5969
|
+
return emptyResult(allMembers.length, activeMembers.length);
|
|
5970
|
+
}
|
|
5971
|
+
const edgeByPair = /* @__PURE__ */ new Map();
|
|
5972
|
+
for (const pair of accumulateCoOccurrencePairs(messageRows)) {
|
|
5973
|
+
if (!activeMemberById.has(pair.sourceId) || !activeMemberById.has(pair.targetId)) continue;
|
|
5974
|
+
const edge = ensureEdge(edgeByPair, activeMemberById, pair.sourceId, pair.targetId);
|
|
5975
|
+
edge.coOccurrenceCount += pair.coOccurrenceCount;
|
|
5976
|
+
edge.coOccurrenceRawScore += pair.rawScore;
|
|
5977
|
+
edge.lastInteractionTs = maxTimestamp(edge.lastInteractionTs, pair.lastOccurrenceTs);
|
|
5978
|
+
}
|
|
5979
|
+
accumulateReplies(db, filter, activeMemberById, edgeByPair);
|
|
5980
|
+
accumulateMentions(db, filter, activeMembers, activeMemberById, edgeByPair);
|
|
5981
|
+
const weightedEdges = [...edgeByPair.values()].map((edge) => ({ ...edge, weight: roundNum2(computeEdgeWeight(edge)) })).filter((edge) => edge.weight >= MIN_EDGE_WEIGHT).sort(compareEdges);
|
|
5982
|
+
if (weightedEdges.length === 0) {
|
|
5983
|
+
return emptyResult(allMembers.length, activeMembers.length);
|
|
5984
|
+
}
|
|
5985
|
+
const candidateNodeKeys = selectCandidateNodeKeys(weightedEdges, limits.nodeLimit);
|
|
5986
|
+
const candidateEdges = weightedEdges.filter(
|
|
5987
|
+
(edge) => candidateNodeKeys.has(edge.sourceKey) && candidateNodeKeys.has(edge.targetKey)
|
|
5988
|
+
);
|
|
5989
|
+
const keptEdges = selectEdges(candidateEdges, limits.perNodeEdgeLimit, limits.edgeLimit);
|
|
5990
|
+
const displayedNodeKeys = /* @__PURE__ */ new Set();
|
|
5991
|
+
for (const edge of keptEdges) {
|
|
5992
|
+
displayedNodeKeys.add(edge.sourceKey);
|
|
5993
|
+
displayedNodeKeys.add(edge.targetKey);
|
|
5994
|
+
}
|
|
5995
|
+
const displayedMembers = activeMembers.filter((member) => displayedNodeKeys.has(member.key));
|
|
5996
|
+
const degreeByKey = computeDegreeByKey(keptEdges);
|
|
5997
|
+
const rankedMembers = [...displayedMembers].sort((a, b) => {
|
|
5998
|
+
const degreeDiff = (degreeByKey.get(b.key) ?? 0) - (degreeByKey.get(a.key) ?? 0);
|
|
5999
|
+
return degreeDiff || b.messageCount - a.messageCount || a.key.localeCompare(b.key);
|
|
6000
|
+
});
|
|
6001
|
+
const communityAssignments = detectWeightedCommunities(rankedMembers, keptEdges, degreeByKey);
|
|
6002
|
+
const communityByNodeKey = /* @__PURE__ */ new Map();
|
|
6003
|
+
for (const community of communityAssignments) {
|
|
6004
|
+
for (const nodeKey of community.nodeKeys) communityByNodeKey.set(nodeKey, community);
|
|
6005
|
+
}
|
|
6006
|
+
const maxDegree = Math.max(...degreeByKey.values(), 1);
|
|
6007
|
+
const renderNodes = [];
|
|
6008
|
+
const memberDetails = [];
|
|
6009
|
+
const incidentTotals = buildIncidentTotals(weightedEdges);
|
|
6010
|
+
for (const [index, member] of rankedMembers.entries()) {
|
|
6011
|
+
const degree = degreeByKey.get(member.key) ?? 0;
|
|
6012
|
+
const score = degree / maxDegree;
|
|
6013
|
+
const community = communityByNodeKey.get(member.key);
|
|
6014
|
+
const position = layoutNode(member.key, index, community, rankedMembers, communityByNodeKey);
|
|
6015
|
+
const totals = incidentTotals.get(member.key) ?? createIncidentTotals();
|
|
6016
|
+
const rank = index + 1;
|
|
6017
|
+
renderNodes.push({
|
|
6018
|
+
key: member.key,
|
|
6019
|
+
displayName: member.displayName,
|
|
6020
|
+
avatar: member.avatar,
|
|
6021
|
+
score: roundNum2(score),
|
|
6022
|
+
rank,
|
|
6023
|
+
communityId: community.id,
|
|
6024
|
+
x: position.x,
|
|
6025
|
+
y: position.y,
|
|
6026
|
+
size: roundNum2(18 + Math.sqrt(score) * 30, 2),
|
|
6027
|
+
color: community.color,
|
|
6028
|
+
labelVisibility: rank <= 8 ? 2 : rank <= 30 ? 1 : 0
|
|
6029
|
+
});
|
|
6030
|
+
memberDetails.push({
|
|
6031
|
+
key: member.key,
|
|
6032
|
+
memberId: member.id,
|
|
6033
|
+
platformId: member.platformId,
|
|
6034
|
+
displayName: member.displayName,
|
|
6035
|
+
avatar: member.avatar,
|
|
6036
|
+
messageCount: member.messageCount,
|
|
6037
|
+
lastMessageTs: member.lastMessageTs,
|
|
6038
|
+
relationshipScore: roundNum2(score),
|
|
6039
|
+
rank,
|
|
6040
|
+
communityId: community.id,
|
|
6041
|
+
replyInteractionCount: totals.replyInteractionCount,
|
|
6042
|
+
mentionInteractionCount: totals.mentionInteractionCount,
|
|
6043
|
+
coOccurrenceCount: totals.coOccurrenceCount,
|
|
6044
|
+
coOccurrenceRawScore: roundNum2(totals.coOccurrenceRawScore),
|
|
6045
|
+
lastInteractionTs: totals.lastInteractionTs
|
|
6046
|
+
});
|
|
6047
|
+
}
|
|
6048
|
+
const maxEdgeWeight = Math.max(...keptEdges.map((edge) => edge.weight), 1);
|
|
6049
|
+
const renderEdges = keptEdges.map((edge) => ({
|
|
6050
|
+
id: edge.id,
|
|
6051
|
+
sourceKey: edge.sourceKey,
|
|
6052
|
+
targetKey: edge.targetKey,
|
|
6053
|
+
weight: edge.weight,
|
|
6054
|
+
visibility: edge.weight / maxEdgeWeight >= 0.35 ? 2 : 1,
|
|
6055
|
+
lastInteractionTs: edge.lastInteractionTs
|
|
6056
|
+
}));
|
|
6057
|
+
const edgeDetails = keptEdges.map((edge) => ({
|
|
6058
|
+
id: edge.id,
|
|
6059
|
+
sourceKey: edge.sourceKey,
|
|
6060
|
+
targetKey: edge.targetKey,
|
|
6061
|
+
weight: edge.weight,
|
|
6062
|
+
coOccurrenceCount: edge.coOccurrenceCount,
|
|
6063
|
+
coOccurrenceRawScore: roundNum2(edge.coOccurrenceRawScore),
|
|
6064
|
+
replyInteractionCount: edge.replyInteractionCount,
|
|
6065
|
+
mentionInteractionCount: edge.mentionInteractionCount,
|
|
6066
|
+
lastInteractionTs: edge.lastInteractionTs
|
|
6067
|
+
}));
|
|
6068
|
+
const renderCommunities = communityAssignments.map((community, index) => ({
|
|
6069
|
+
id: community.id,
|
|
6070
|
+
label: `community-${index + 1}`,
|
|
6071
|
+
size: community.nodeKeys.length,
|
|
6072
|
+
x: community.x,
|
|
6073
|
+
y: community.y,
|
|
6074
|
+
color: community.color
|
|
6075
|
+
}));
|
|
6076
|
+
return {
|
|
6077
|
+
graph: { nodes: renderNodes, edges: renderEdges, communities: renderCommunities },
|
|
6078
|
+
members: memberDetails,
|
|
6079
|
+
edges: edgeDetails,
|
|
6080
|
+
stats: {
|
|
6081
|
+
totalMembers: allMembers.length,
|
|
6082
|
+
activeMembers: activeMembers.length,
|
|
6083
|
+
displayedMembers: renderNodes.length,
|
|
6084
|
+
displayedEdges: renderEdges.length,
|
|
6085
|
+
communityCount: renderCommunities.length
|
|
6086
|
+
},
|
|
6087
|
+
algorithmVersion: GROUP_RELATIONSHIP_GALAXY_ALGORITHM_VERSION
|
|
6088
|
+
};
|
|
6089
|
+
}
|
|
6090
|
+
function queryRelationshipMessages(db, filter) {
|
|
6091
|
+
const { clause, params } = buildTimeFilter(filter, "msg");
|
|
6092
|
+
const where = appendConditions(clause, ["msg.type NOT IN (80, 81)"]);
|
|
6093
|
+
return db.prepare(`SELECT msg.sender_id as senderId, msg.ts FROM message msg ${where} ORDER BY msg.ts ASC, msg.id ASC`).all(...params);
|
|
6094
|
+
}
|
|
6095
|
+
function buildActiveMembers(allMembers, messages) {
|
|
6096
|
+
const messageStats = /* @__PURE__ */ new Map();
|
|
6097
|
+
for (const message of messages) {
|
|
6098
|
+
const stats = messageStats.get(message.senderId) ?? { count: 0, lastTs: 0 };
|
|
6099
|
+
stats.count++;
|
|
6100
|
+
stats.lastTs = Math.max(stats.lastTs, message.ts);
|
|
6101
|
+
messageStats.set(message.senderId, stats);
|
|
6102
|
+
}
|
|
6103
|
+
const active = allMembers.filter((member) => messageStats.has(member.id)).map((member) => ({
|
|
6104
|
+
...member,
|
|
6105
|
+
key: memberKey(member.id),
|
|
6106
|
+
displayName: member.name,
|
|
6107
|
+
messageCount: messageStats.get(member.id).count,
|
|
6108
|
+
lastMessageTs: messageStats.get(member.id).lastTs || null
|
|
6109
|
+
}));
|
|
6110
|
+
const nameCounts = /* @__PURE__ */ new Map();
|
|
6111
|
+
for (const member of active) nameCounts.set(member.displayName, (nameCounts.get(member.displayName) ?? 0) + 1);
|
|
6112
|
+
for (const member of active) {
|
|
6113
|
+
if ((nameCounts.get(member.displayName) ?? 0) > 1) {
|
|
6114
|
+
member.displayName = `${member.displayName}#${member.platformId.slice(-4)}`;
|
|
6115
|
+
}
|
|
6116
|
+
}
|
|
6117
|
+
return active;
|
|
6118
|
+
}
|
|
6119
|
+
function accumulateReplies(db, filter, activeMemberById, edgeByPair) {
|
|
6120
|
+
if (!hasColumn(db, "message", "reply_to_message_id") || !hasColumn(db, "message", "platform_message_id")) return;
|
|
6121
|
+
const { clause, params } = buildTimeFilter(filter, "msg");
|
|
6122
|
+
const conditions = ["msg.type NOT IN (80, 81)", "target.type NOT IN (80, 81)", "msg.reply_to_message_id IS NOT NULL"];
|
|
6123
|
+
const targetParams = [];
|
|
6124
|
+
if (filter?.startTs !== void 0) {
|
|
6125
|
+
conditions.push("target.ts >= ?");
|
|
6126
|
+
targetParams.push(filter.startTs);
|
|
6127
|
+
}
|
|
6128
|
+
if (filter?.endTs !== void 0) {
|
|
6129
|
+
conditions.push("target.ts <= ?");
|
|
6130
|
+
targetParams.push(filter.endTs);
|
|
6131
|
+
}
|
|
6132
|
+
const rows = db.prepare(
|
|
6133
|
+
`SELECT msg.sender_id as replySenderId, msg.ts as replyTs, target.sender_id as targetSenderId
|
|
6134
|
+
FROM message msg
|
|
6135
|
+
JOIN message target ON msg.reply_to_message_id = target.platform_message_id
|
|
6136
|
+
${appendConditions(clause, conditions)}`
|
|
6137
|
+
).all(...params, ...targetParams);
|
|
6138
|
+
for (const row of rows) {
|
|
6139
|
+
if (row.replySenderId === row.targetSenderId) continue;
|
|
6140
|
+
if (!activeMemberById.has(row.replySenderId) || !activeMemberById.has(row.targetSenderId)) continue;
|
|
6141
|
+
const edge = ensureEdge(edgeByPair, activeMemberById, row.replySenderId, row.targetSenderId);
|
|
6142
|
+
edge.replyInteractionCount++;
|
|
6143
|
+
edge.lastInteractionTs = maxTimestamp(edge.lastInteractionTs, row.replyTs);
|
|
6144
|
+
}
|
|
6145
|
+
}
|
|
6146
|
+
function accumulateMentions(db, filter, activeMembers, activeMemberById, edgeByPair) {
|
|
6147
|
+
const nameToMemberId = buildMentionNameIndex(db, activeMembers);
|
|
6148
|
+
const { clause, params } = buildTimeFilter(filter, "msg");
|
|
6149
|
+
const where = appendConditions(clause, ["msg.type = 0", "msg.content IS NOT NULL", "msg.content LIKE '%@%'"]);
|
|
6150
|
+
const rows = db.prepare(`SELECT msg.sender_id as senderId, msg.ts, msg.content FROM message msg ${where}`).all(...params);
|
|
6151
|
+
const mentionRegex = /@([^\s@]+)/g;
|
|
6152
|
+
for (const row of rows) {
|
|
6153
|
+
if (!activeMemberById.has(row.senderId)) continue;
|
|
6154
|
+
const mentionedInMessage = /* @__PURE__ */ new Set();
|
|
6155
|
+
for (const match of row.content.matchAll(mentionRegex)) {
|
|
6156
|
+
const mentionedId = nameToMemberId.get(match[1]);
|
|
6157
|
+
if (mentionedId === null || mentionedId === void 0 || mentionedId === row.senderId) continue;
|
|
6158
|
+
if (!activeMemberById.has(mentionedId) || mentionedInMessage.has(mentionedId)) continue;
|
|
6159
|
+
mentionedInMessage.add(mentionedId);
|
|
6160
|
+
const edge = ensureEdge(edgeByPair, activeMemberById, row.senderId, mentionedId);
|
|
6161
|
+
edge.mentionInteractionCount++;
|
|
6162
|
+
edge.lastInteractionTs = maxTimestamp(edge.lastInteractionTs, row.ts);
|
|
6163
|
+
}
|
|
6164
|
+
}
|
|
6165
|
+
}
|
|
6166
|
+
function buildMentionNameIndex(db, members) {
|
|
6167
|
+
const index = /* @__PURE__ */ new Map();
|
|
6168
|
+
const addName = (name, memberId) => {
|
|
6169
|
+
const trimmed = name.trim();
|
|
6170
|
+
if (!trimmed) return;
|
|
6171
|
+
const existing = index.get(trimmed);
|
|
6172
|
+
if (existing === void 0 || existing === memberId) index.set(trimmed, memberId);
|
|
6173
|
+
else index.set(trimmed, null);
|
|
6174
|
+
};
|
|
6175
|
+
for (const member of members) {
|
|
6176
|
+
addName(member.name, member.id);
|
|
6177
|
+
for (const alias of member.aliases) addName(alias, member.id);
|
|
6178
|
+
}
|
|
6179
|
+
if (hasTable(db, "member_name_history")) {
|
|
6180
|
+
const memberIds = new Set(members.map((member) => member.id));
|
|
6181
|
+
const rows = db.prepare("SELECT member_id as memberId, name FROM member_name_history").all();
|
|
6182
|
+
for (const row of rows) {
|
|
6183
|
+
if (memberIds.has(row.memberId)) addName(row.name, row.memberId);
|
|
6184
|
+
}
|
|
6185
|
+
}
|
|
6186
|
+
return index;
|
|
6187
|
+
}
|
|
6188
|
+
function ensureEdge(edgeByPair, memberById, aId, bId) {
|
|
6189
|
+
const sourceId = Math.min(aId, bId);
|
|
6190
|
+
const targetId = Math.max(aId, bId);
|
|
6191
|
+
const id = `${memberKey(sourceId)}--${memberKey(targetId)}`;
|
|
6192
|
+
const existing = edgeByPair.get(id);
|
|
6193
|
+
if (existing) return existing;
|
|
6194
|
+
const created = {
|
|
6195
|
+
id,
|
|
6196
|
+
sourceId,
|
|
6197
|
+
targetId,
|
|
6198
|
+
sourceKey: memberById.get(sourceId).key,
|
|
6199
|
+
targetKey: memberById.get(targetId).key,
|
|
6200
|
+
coOccurrenceCount: 0,
|
|
6201
|
+
coOccurrenceRawScore: 0,
|
|
6202
|
+
replyInteractionCount: 0,
|
|
6203
|
+
mentionInteractionCount: 0,
|
|
6204
|
+
lastInteractionTs: null,
|
|
6205
|
+
weight: 0
|
|
6206
|
+
};
|
|
6207
|
+
edgeByPair.set(id, created);
|
|
6208
|
+
return created;
|
|
6209
|
+
}
|
|
6210
|
+
function computeEdgeWeight(edge) {
|
|
6211
|
+
return edge.coOccurrenceRawScore + edge.replyInteractionCount * REPLY_WEIGHT + edge.mentionInteractionCount * MENTION_WEIGHT + edge.coOccurrenceCount * CO_OCCURRENCE_COUNT_WEIGHT;
|
|
6212
|
+
}
|
|
6213
|
+
function selectCandidateNodeKeys(edges, nodeLimit) {
|
|
6214
|
+
const degree = computeDegreeByKey(edges);
|
|
6215
|
+
return new Set(
|
|
6216
|
+
[...degree.entries()].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])).slice(0, nodeLimit).map(([key]) => key)
|
|
6217
|
+
);
|
|
6218
|
+
}
|
|
6219
|
+
function selectEdges(edges, perNodeLimit, edgeLimit) {
|
|
6220
|
+
const incident = /* @__PURE__ */ new Map();
|
|
6221
|
+
for (const edge of edges) {
|
|
6222
|
+
for (const key of [edge.sourceKey, edge.targetKey]) {
|
|
6223
|
+
const list = incident.get(key) ?? [];
|
|
6224
|
+
list.push(edge);
|
|
6225
|
+
incident.set(key, list);
|
|
6226
|
+
}
|
|
6227
|
+
}
|
|
6228
|
+
const selectedIds = /* @__PURE__ */ new Set();
|
|
6229
|
+
for (const list of incident.values()) {
|
|
6230
|
+
list.sort(compareEdges);
|
|
6231
|
+
for (const edge of list.slice(0, perNodeLimit)) selectedIds.add(edge.id);
|
|
6232
|
+
}
|
|
6233
|
+
return edges.filter((edge) => selectedIds.has(edge.id)).sort(compareEdges).slice(0, edgeLimit);
|
|
6234
|
+
}
|
|
6235
|
+
function computeDegreeByKey(edges) {
|
|
6236
|
+
const degree = /* @__PURE__ */ new Map();
|
|
6237
|
+
for (const edge of edges) {
|
|
6238
|
+
degree.set(edge.sourceKey, (degree.get(edge.sourceKey) ?? 0) + edge.weight);
|
|
6239
|
+
degree.set(edge.targetKey, (degree.get(edge.targetKey) ?? 0) + edge.weight);
|
|
6240
|
+
}
|
|
6241
|
+
return degree;
|
|
6242
|
+
}
|
|
6243
|
+
function detectWeightedCommunities(members, edges, degreeByKey) {
|
|
6244
|
+
const parent = new Map(members.map((member) => [member.key, member.key]));
|
|
6245
|
+
const strongestByKey = /* @__PURE__ */ new Map();
|
|
6246
|
+
for (const edge of edges) {
|
|
6247
|
+
strongestByKey.set(edge.sourceKey, Math.max(strongestByKey.get(edge.sourceKey) ?? 0, edge.weight));
|
|
6248
|
+
strongestByKey.set(edge.targetKey, Math.max(strongestByKey.get(edge.targetKey) ?? 0, edge.weight));
|
|
6249
|
+
}
|
|
6250
|
+
const find = (key) => {
|
|
6251
|
+
const current = parent.get(key) ?? key;
|
|
6252
|
+
if (current === key) return current;
|
|
6253
|
+
const root = find(current);
|
|
6254
|
+
parent.set(key, root);
|
|
6255
|
+
return root;
|
|
6256
|
+
};
|
|
6257
|
+
const union = (a, b) => {
|
|
6258
|
+
const aRoot = find(a);
|
|
6259
|
+
const bRoot = find(b);
|
|
6260
|
+
if (aRoot === bRoot) return;
|
|
6261
|
+
const [source, target] = aRoot.localeCompare(bRoot) <= 0 ? [aRoot, bRoot] : [bRoot, aRoot];
|
|
6262
|
+
parent.set(target, source);
|
|
6263
|
+
};
|
|
6264
|
+
for (const edge of edges) {
|
|
6265
|
+
const sourceThreshold = (strongestByKey.get(edge.sourceKey) ?? edge.weight) * 0.6;
|
|
6266
|
+
const targetThreshold = (strongestByKey.get(edge.targetKey) ?? edge.weight) * 0.6;
|
|
6267
|
+
if (edge.weight >= sourceThreshold && edge.weight >= targetThreshold) union(edge.sourceKey, edge.targetKey);
|
|
6268
|
+
}
|
|
6269
|
+
const groups = /* @__PURE__ */ new Map();
|
|
6270
|
+
for (const member of members) {
|
|
6271
|
+
const root = find(member.key);
|
|
6272
|
+
const list = groups.get(root) ?? [];
|
|
6273
|
+
list.push(member.key);
|
|
6274
|
+
groups.set(root, list);
|
|
6275
|
+
}
|
|
6276
|
+
const sortedGroups = [...groups.values()].map((keys) => keys.sort()).sort((a, b) => {
|
|
6277
|
+
const aWeight = a.reduce((sum, key) => sum + (degreeByKey.get(key) ?? 0), 0);
|
|
6278
|
+
const bWeight = b.reduce((sum, key) => sum + (degreeByKey.get(key) ?? 0), 0);
|
|
6279
|
+
return bWeight - aWeight || a[0].localeCompare(b[0]);
|
|
6280
|
+
});
|
|
6281
|
+
return sortedGroups.map((nodeKeys, index) => {
|
|
6282
|
+
const center = layoutCommunity(index, sortedGroups.length);
|
|
6283
|
+
return {
|
|
6284
|
+
id: `community:${nodeKeys[0]}`,
|
|
6285
|
+
nodeKeys,
|
|
6286
|
+
color: COMMUNITY_COLORS[stableHash(nodeKeys[0]) % COMMUNITY_COLORS.length],
|
|
6287
|
+
x: center.x,
|
|
6288
|
+
y: center.y
|
|
6289
|
+
};
|
|
6290
|
+
});
|
|
6291
|
+
}
|
|
6292
|
+
function layoutCommunity(index, count) {
|
|
6293
|
+
if (index === 0 || count === 1) return { x: 0, y: 0 };
|
|
6294
|
+
const ringIndex = index - 1;
|
|
6295
|
+
const ring = Math.floor(ringIndex / 6);
|
|
6296
|
+
const position = ringIndex % 6;
|
|
6297
|
+
const radius = 380 + ring * 280;
|
|
6298
|
+
const angle = -Math.PI / 2 + position / 6 * Math.PI * 2 + ring * 0.31;
|
|
6299
|
+
return { x: roundNum2(Math.cos(angle) * radius, 2), y: roundNum2(Math.sin(angle) * radius, 2) };
|
|
6300
|
+
}
|
|
6301
|
+
function layoutNode(nodeKey, globalIndex, community, members, communityByNodeKey) {
|
|
6302
|
+
const communityMembers = members.filter((member) => communityByNodeKey.get(member.key)?.id === community.id);
|
|
6303
|
+
const localIndex = communityMembers.findIndex((member) => member.key === nodeKey);
|
|
6304
|
+
const angle = localIndex * 2.399963229728653 + stableHash(nodeKey) % 1e3 / 1e3;
|
|
6305
|
+
const radius = localIndex === 0 ? 0 : 54 + Math.sqrt(localIndex) * 42;
|
|
6306
|
+
const globalJitter = globalIndex % 3 * 2;
|
|
6307
|
+
return {
|
|
6308
|
+
x: roundNum2(community.x + Math.cos(angle) * (radius + globalJitter), 2),
|
|
6309
|
+
y: roundNum2(community.y + Math.sin(angle) * (radius + globalJitter), 2)
|
|
6310
|
+
};
|
|
6311
|
+
}
|
|
6312
|
+
function buildIncidentTotals(edges) {
|
|
6313
|
+
const totals = /* @__PURE__ */ new Map();
|
|
6314
|
+
for (const edge of edges) {
|
|
6315
|
+
for (const key of [edge.sourceKey, edge.targetKey]) {
|
|
6316
|
+
const item = totals.get(key) ?? createIncidentTotals();
|
|
6317
|
+
item.replyInteractionCount += edge.replyInteractionCount;
|
|
6318
|
+
item.mentionInteractionCount += edge.mentionInteractionCount;
|
|
6319
|
+
item.coOccurrenceCount += edge.coOccurrenceCount;
|
|
6320
|
+
item.coOccurrenceRawScore += edge.coOccurrenceRawScore;
|
|
6321
|
+
item.lastInteractionTs = maxTimestamp(item.lastInteractionTs, edge.lastInteractionTs);
|
|
6322
|
+
totals.set(key, item);
|
|
6323
|
+
}
|
|
6324
|
+
}
|
|
6325
|
+
return totals;
|
|
6326
|
+
}
|
|
6327
|
+
function createIncidentTotals() {
|
|
6328
|
+
return {
|
|
6329
|
+
replyInteractionCount: 0,
|
|
6330
|
+
mentionInteractionCount: 0,
|
|
6331
|
+
coOccurrenceCount: 0,
|
|
6332
|
+
coOccurrenceRawScore: 0,
|
|
6333
|
+
lastInteractionTs: null
|
|
6334
|
+
};
|
|
6335
|
+
}
|
|
6336
|
+
function normalizeLimits(options) {
|
|
6337
|
+
return {
|
|
6338
|
+
nodeLimit: normalizeLimit(options.nodeLimit, DEFAULT_LIMITS.nodeLimit),
|
|
6339
|
+
edgeLimit: normalizeLimit(options.edgeLimit, DEFAULT_LIMITS.edgeLimit),
|
|
6340
|
+
perNodeEdgeLimit: normalizeLimit(options.perNodeEdgeLimit, DEFAULT_LIMITS.perNodeEdgeLimit)
|
|
6341
|
+
};
|
|
6342
|
+
}
|
|
6343
|
+
function normalizeLimit(value, fallback) {
|
|
6344
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.floor(value) : fallback;
|
|
6345
|
+
}
|
|
6346
|
+
function appendConditions(clause, conditions) {
|
|
6347
|
+
const existing = clause.trim().replace(/^WHERE\s+/i, "");
|
|
6348
|
+
return `WHERE ${[existing, ...conditions].filter(Boolean).join(" AND ")}`;
|
|
6349
|
+
}
|
|
6350
|
+
function emptyResult(totalMembers, activeMembers) {
|
|
6351
|
+
return {
|
|
6352
|
+
graph: { nodes: [], edges: [], communities: [] },
|
|
6353
|
+
members: [],
|
|
6354
|
+
edges: [],
|
|
6355
|
+
stats: { totalMembers, activeMembers, displayedMembers: 0, displayedEdges: 0, communityCount: 0 },
|
|
6356
|
+
algorithmVersion: GROUP_RELATIONSHIP_GALAXY_ALGORITHM_VERSION
|
|
6357
|
+
};
|
|
6358
|
+
}
|
|
6359
|
+
function compareEdges(a, b) {
|
|
6360
|
+
return b.weight - a.weight || a.id.localeCompare(b.id);
|
|
6361
|
+
}
|
|
6362
|
+
function memberKey(memberId) {
|
|
6363
|
+
return `member:${memberId}`;
|
|
6364
|
+
}
|
|
6365
|
+
function maxTimestamp(current, next) {
|
|
6366
|
+
if (next === null) return current;
|
|
6367
|
+
return Math.max(current ?? 0, next) || null;
|
|
6368
|
+
}
|
|
6369
|
+
function roundNum2(value, digits = 4) {
|
|
6370
|
+
const factor = 10 ** digits;
|
|
6371
|
+
return Math.round(value * factor) / factor;
|
|
6372
|
+
}
|
|
6373
|
+
function stableHash(value) {
|
|
6374
|
+
let hash = 2166136261;
|
|
6375
|
+
for (let index = 0; index < value.length; index++) {
|
|
6376
|
+
hash ^= value.charCodeAt(index);
|
|
6377
|
+
hash = Math.imul(hash, 16777619);
|
|
6378
|
+
}
|
|
6379
|
+
return hash >>> 0;
|
|
6380
|
+
}
|
|
6381
|
+
|
|
6007
6382
|
// ../../packages/core/src/query/advanced/relationship.ts
|
|
6008
6383
|
var ICE_BREAK_THRESHOLD = 24 * 60 * 60;
|
|
6009
6384
|
var DEFAULT_PERSEVERANCE_THRESHOLD = 300;
|
|
6010
6385
|
function getRelationshipStats(db, filter, options) {
|
|
6011
6386
|
const perseveranceThreshold = options?.perseveranceThreshold ?? DEFAULT_PERSEVERANCE_THRESHOLD;
|
|
6012
|
-
const
|
|
6387
|
+
const emptyResult2 = {
|
|
6013
6388
|
months: [],
|
|
6014
6389
|
members: [],
|
|
6015
6390
|
totalSessions: 0,
|
|
@@ -6021,7 +6396,7 @@ function getRelationshipStats(db, filter, options) {
|
|
|
6021
6396
|
monthlyPerseverance: []
|
|
6022
6397
|
};
|
|
6023
6398
|
const sessionCount = db.prepare("SELECT COUNT(*) as count FROM segment").get();
|
|
6024
|
-
if (!sessionCount || sessionCount.count === 0) return
|
|
6399
|
+
if (!sessionCount || sessionCount.count === 0) return emptyResult2;
|
|
6025
6400
|
const timeConditions = [];
|
|
6026
6401
|
const params = [];
|
|
6027
6402
|
if (filter?.startTs !== void 0) {
|
|
@@ -6223,8 +6598,8 @@ function getJourneyStats(db, filter) {
|
|
|
6223
6598
|
const indexAvailable = hasSessionIndex(db) && hasTable(db, "message_context");
|
|
6224
6599
|
const segmentRows = indexAvailable ? querySegmentRows(db, effectiveFilter) : [];
|
|
6225
6600
|
const segmentCountByMonth = /* @__PURE__ */ new Map();
|
|
6226
|
-
for (const
|
|
6227
|
-
segmentCountByMonth.set(
|
|
6601
|
+
for (const segment of segmentRows) {
|
|
6602
|
+
segmentCountByMonth.set(segment.startMonth, (segmentCountByMonth.get(segment.startMonth) ?? 0) + 1);
|
|
6228
6603
|
}
|
|
6229
6604
|
const firstRow = monthlyRows[0];
|
|
6230
6605
|
const lastRow = monthlyRows[monthlyRows.length - 1];
|
|
@@ -6754,7 +7129,7 @@ function getCheckInAnalysis(db, filter) {
|
|
|
6754
7129
|
return computeCheckIn(dailyActivity);
|
|
6755
7130
|
}
|
|
6756
7131
|
function computeMemeBattle(messages) {
|
|
6757
|
-
const
|
|
7132
|
+
const emptyResult2 = {
|
|
6758
7133
|
topBattles: [],
|
|
6759
7134
|
rankByCount: [],
|
|
6760
7135
|
rankByImageCount: [],
|
|
@@ -6788,7 +7163,7 @@ function computeMemeBattle(messages) {
|
|
|
6788
7163
|
}
|
|
6789
7164
|
}
|
|
6790
7165
|
processChain();
|
|
6791
|
-
if (battles.length === 0) return
|
|
7166
|
+
if (battles.length === 0) return emptyResult2;
|
|
6792
7167
|
const topBattles = battles.map((battle) => ({
|
|
6793
7168
|
startTime: battle.startTime,
|
|
6794
7169
|
endTime: battle.endTime,
|
|
@@ -6867,7 +7242,7 @@ function getMemeBattleAnalysis(db, filter) {
|
|
|
6867
7242
|
return computeMemeBattle(messages);
|
|
6868
7243
|
}
|
|
6869
7244
|
function computeNightOwl(messages) {
|
|
6870
|
-
const
|
|
7245
|
+
const emptyResult2 = {
|
|
6871
7246
|
nightOwlRank: [],
|
|
6872
7247
|
lastSpeakerRank: [],
|
|
6873
7248
|
firstSpeakerRank: [],
|
|
@@ -6875,7 +7250,7 @@ function computeNightOwl(messages) {
|
|
|
6875
7250
|
champions: [],
|
|
6876
7251
|
totalDays: 0
|
|
6877
7252
|
};
|
|
6878
|
-
if (messages.length === 0) return
|
|
7253
|
+
if (messages.length === 0) return emptyResult2;
|
|
6879
7254
|
function getNightOwlTitleByCount(count) {
|
|
6880
7255
|
if (count === 0) return "\u517B\u751F\u8FBE\u4EBA";
|
|
6881
7256
|
if (count <= 20) return "\u5076\u5C14\u5931\u7720";
|
|
@@ -7702,7 +8077,7 @@ function mergeMembers2(sources, collidingIds) {
|
|
|
7702
8077
|
}
|
|
7703
8078
|
|
|
7704
8079
|
// ../../packages/core/src/schema/tables.ts
|
|
7705
|
-
var CURRENT_SCHEMA_VERSION =
|
|
8080
|
+
var CURRENT_SCHEMA_VERSION = 9;
|
|
7706
8081
|
var CHAT_DB_TABLES = `
|
|
7707
8082
|
CREATE TABLE IF NOT EXISTS meta (
|
|
7708
8083
|
name TEXT NOT NULL,
|
|
@@ -7776,13 +8151,6 @@ var CHAT_DB_INDEXES = `
|
|
|
7776
8151
|
CREATE INDEX IF NOT EXISTS idx_context_segment ON message_context(segment_id);
|
|
7777
8152
|
`;
|
|
7778
8153
|
var CHAT_DB_SCHEMA = CHAT_DB_TABLES + CHAT_DB_INDEXES;
|
|
7779
|
-
var FTS_TABLE_SCHEMA = `
|
|
7780
|
-
CREATE VIRTUAL TABLE IF NOT EXISTS message_fts USING fts5(
|
|
7781
|
-
content,
|
|
7782
|
-
content='',
|
|
7783
|
-
content_rowid=id
|
|
7784
|
-
);
|
|
7785
|
-
`;
|
|
7786
8154
|
|
|
7787
8155
|
// ../../packages/core/src/schema/migrations.ts
|
|
7788
8156
|
function getSchemaVersion(db) {
|
|
@@ -8040,282 +8408,30 @@ var appLogger = {
|
|
|
8040
8408
|
getLogPath: () => instance?.getLogPath() ?? null
|
|
8041
8409
|
};
|
|
8042
8410
|
|
|
8043
|
-
// ../../packages/node-runtime/src/nlp/segmenter.ts
|
|
8044
|
-
import * as fs8 from "fs";
|
|
8045
|
-
import * as path13 from "path";
|
|
8046
|
-
import { createRequire } from "module";
|
|
8047
|
-
var _nlpDir = null;
|
|
8048
|
-
var jiebaInstances = /* @__PURE__ */ new Map();
|
|
8049
|
-
var require2 = createRequire(import.meta.url);
|
|
8050
|
-
function initNlpDir(nlpDir) {
|
|
8051
|
-
_nlpDir = nlpDir;
|
|
8052
|
-
}
|
|
8053
|
-
function getNlpDir() {
|
|
8054
|
-
return _nlpDir;
|
|
8055
|
-
}
|
|
8056
|
-
function existsDictOnDisk(dictId) {
|
|
8057
|
-
if (!_nlpDir) return false;
|
|
8058
|
-
return fs8.existsSync(path13.join(_nlpDir, `${dictId}.dict`));
|
|
8059
|
-
}
|
|
8060
|
-
function tryLoadDictFromDisk(dictId) {
|
|
8061
|
-
if (!_nlpDir) return null;
|
|
8062
|
-
const dictPath = path13.join(_nlpDir, `${dictId}.dict`);
|
|
8063
|
-
if (!fs8.existsSync(dictPath)) return null;
|
|
8064
|
-
try {
|
|
8065
|
-
return fs8.readFileSync(dictPath);
|
|
8066
|
-
} catch {
|
|
8067
|
-
return null;
|
|
8068
|
-
}
|
|
8069
|
-
}
|
|
8070
|
-
function getJieba(dictType = "default") {
|
|
8071
|
-
const effectiveType = dictType === "default" ? "zh-CN" : dictType;
|
|
8072
|
-
const cached = jiebaInstances.get(effectiveType);
|
|
8073
|
-
if (cached) {
|
|
8074
|
-
if (cached.fromDiskDict === existsDictOnDisk(effectiveType)) return cached.instance;
|
|
8075
|
-
jiebaInstances.delete(effectiveType);
|
|
8076
|
-
console.error(`[NLP] jieba cache invalidated (dict ${cached.fromDiskDict ? "removed" : "added"}): ${effectiveType}`);
|
|
8077
|
-
}
|
|
8078
|
-
try {
|
|
8079
|
-
const { Jieba } = require2("@node-rs/jieba");
|
|
8080
|
-
let instance2;
|
|
8081
|
-
const diskDict = tryLoadDictFromDisk(effectiveType);
|
|
8082
|
-
if (diskDict) {
|
|
8083
|
-
instance2 = Jieba.withDict(diskDict);
|
|
8084
|
-
console.error(`[NLP] jieba dict loaded: ${effectiveType} (${diskDict.length} bytes)`);
|
|
8085
|
-
} else {
|
|
8086
|
-
instance2 = new Jieba();
|
|
8087
|
-
console.warn(`[NLP] jieba dict missing: ${effectiveType}, fallback to built-in tokenizer`);
|
|
8088
|
-
}
|
|
8089
|
-
jiebaInstances.set(effectiveType, { instance: instance2, fromDiskDict: !!diskDict });
|
|
8090
|
-
return instance2;
|
|
8091
|
-
} catch (error) {
|
|
8092
|
-
console.error(`[NLP] Failed to load jieba module (dict=${effectiveType}):`, error);
|
|
8093
|
-
throw new Error(`jieba \u6A21\u5757\u52A0\u8F7D\u5931\u8D25 (${effectiveType})`);
|
|
8094
|
-
}
|
|
8095
|
-
}
|
|
8096
|
-
function clearJiebaInstance(dictType) {
|
|
8097
|
-
jiebaInstances.delete(dictType);
|
|
8098
|
-
console.error(`[NLP] jieba instance cleared: ${dictType}`);
|
|
8099
|
-
}
|
|
8100
|
-
function segmentChinese(text, options = {}) {
|
|
8101
|
-
const { posFilterMode = "meaningful", customPosTags, dictType = "default" } = options;
|
|
8102
|
-
const cleaned = cleanText(text);
|
|
8103
|
-
if (!cleaned) return [];
|
|
8104
|
-
try {
|
|
8105
|
-
const jieba = getJieba(dictType);
|
|
8106
|
-
if (posFilterMode === "all") return jieba.cut(cleaned, false);
|
|
8107
|
-
const tagged = jieba.tag(cleaned);
|
|
8108
|
-
const allowedTags = posFilterMode === "custom" && customPosTags ? new Set(customPosTags) : MEANINGFUL_POS_TAGS;
|
|
8109
|
-
return tagged.filter((item) => allowedTags.has(item.tag)).map((item) => item.word);
|
|
8110
|
-
} catch (error) {
|
|
8111
|
-
console.error("[NLP] Chinese segmentation failed:", error);
|
|
8112
|
-
try {
|
|
8113
|
-
return getJieba("default").cut(cleanText(text), false);
|
|
8114
|
-
} catch {
|
|
8115
|
-
return cleaned.split("");
|
|
8116
|
-
}
|
|
8117
|
-
}
|
|
8118
|
-
}
|
|
8119
|
-
function segmentEnglish(text) {
|
|
8120
|
-
const cleaned = cleanText(text);
|
|
8121
|
-
if (!cleaned) return [];
|
|
8122
|
-
try {
|
|
8123
|
-
const segmenter = new Intl.Segmenter("en", { granularity: "word" });
|
|
8124
|
-
return [...segmenter.segment(cleaned)].filter((s) => s.isWordLike).map((s) => s.segment.toLowerCase());
|
|
8125
|
-
} catch {
|
|
8126
|
-
return cleaned.toLowerCase().split(/\s+/).filter((w) => w.length > 0);
|
|
8127
|
-
}
|
|
8128
|
-
}
|
|
8129
|
-
function segmentJapanese(text) {
|
|
8130
|
-
const cleaned = cleanText(text);
|
|
8131
|
-
if (!cleaned) return [];
|
|
8132
|
-
try {
|
|
8133
|
-
const segmenter = new Intl.Segmenter("ja", { granularity: "word" });
|
|
8134
|
-
return [...segmenter.segment(cleaned)].filter((s) => s.isWordLike).map((s) => s.segment);
|
|
8135
|
-
} catch {
|
|
8136
|
-
return cleaned.split("").filter((ch) => ch.trim().length > 0);
|
|
8137
|
-
}
|
|
8138
|
-
}
|
|
8139
|
-
function segment(text, locale, options = {}) {
|
|
8140
|
-
const {
|
|
8141
|
-
minLength,
|
|
8142
|
-
posFilterMode = "meaningful",
|
|
8143
|
-
customPosTags,
|
|
8144
|
-
enableStopwords = true,
|
|
8145
|
-
dictType = "default"
|
|
8146
|
-
} = options;
|
|
8147
|
-
const isChinese = locale.startsWith("zh");
|
|
8148
|
-
const isJapanese = locale === "ja-JP";
|
|
8149
|
-
const defaultMinLength = isChinese || isJapanese ? 2 : 3;
|
|
8150
|
-
const effectiveMinLength = minLength ?? defaultMinLength;
|
|
8151
|
-
let words;
|
|
8152
|
-
if (isChinese) {
|
|
8153
|
-
words = segmentChinese(text, { posFilterMode, customPosTags, dictType });
|
|
8154
|
-
} else if (isJapanese) {
|
|
8155
|
-
words = segmentJapanese(text);
|
|
8156
|
-
} else {
|
|
8157
|
-
words = segmentEnglish(text);
|
|
8158
|
-
}
|
|
8159
|
-
return words.filter((word) => isValidWord(word, locale, effectiveMinLength, enableStopwords, isStopword));
|
|
8160
|
-
}
|
|
8161
|
-
function batchSegmentWithFrequency(texts, locale, options = {}) {
|
|
8162
|
-
const {
|
|
8163
|
-
minLength,
|
|
8164
|
-
minCount = 2,
|
|
8165
|
-
topN = 100,
|
|
8166
|
-
posFilterMode,
|
|
8167
|
-
customPosTags,
|
|
8168
|
-
enableStopwords,
|
|
8169
|
-
dictType,
|
|
8170
|
-
excludeWords
|
|
8171
|
-
} = options;
|
|
8172
|
-
const wordFrequency = /* @__PURE__ */ new Map();
|
|
8173
|
-
const excludeSet = excludeWords?.length ? new Set(excludeWords.map((w) => w.toLowerCase())) : null;
|
|
8174
|
-
for (const text of texts) {
|
|
8175
|
-
const words = segment(text, locale, { minLength, posFilterMode, customPosTags, enableStopwords, dictType });
|
|
8176
|
-
for (const word of words) {
|
|
8177
|
-
if (excludeSet && excludeSet.has(word.toLowerCase())) continue;
|
|
8178
|
-
wordFrequency.set(word, (wordFrequency.get(word) || 0) + 1);
|
|
8179
|
-
}
|
|
8180
|
-
}
|
|
8181
|
-
const filtered = /* @__PURE__ */ new Map();
|
|
8182
|
-
let totalWords = 0;
|
|
8183
|
-
for (const [word, count] of wordFrequency) {
|
|
8184
|
-
if (count >= minCount) {
|
|
8185
|
-
filtered.set(word, count);
|
|
8186
|
-
totalWords += count;
|
|
8187
|
-
}
|
|
8188
|
-
}
|
|
8189
|
-
const sorted = [...filtered.entries()].sort((a, b) => b[1] - a[1]).slice(0, topN);
|
|
8190
|
-
return { words: new Map(sorted), uniqueWords: filtered.size, totalWords };
|
|
8191
|
-
}
|
|
8192
|
-
function batchSegmentChineseWithStats(texts, locale, options = {}) {
|
|
8193
|
-
const {
|
|
8194
|
-
minLength,
|
|
8195
|
-
minCount = 2,
|
|
8196
|
-
topN = 100,
|
|
8197
|
-
posFilterMode = "meaningful",
|
|
8198
|
-
customPosTags,
|
|
8199
|
-
enableStopwords = true,
|
|
8200
|
-
dictType = "default",
|
|
8201
|
-
excludeWords
|
|
8202
|
-
} = options;
|
|
8203
|
-
const effectiveMinLength = minLength ?? 2;
|
|
8204
|
-
const allowedTags = posFilterMode === "custom" && customPosTags ? new Set(customPosTags) : MEANINGFUL_POS_TAGS;
|
|
8205
|
-
const excludeSet = excludeWords?.length ? new Set(excludeWords.map((w) => w.toLowerCase())) : null;
|
|
8206
|
-
const wordFrequency = /* @__PURE__ */ new Map();
|
|
8207
|
-
const posTagStats = /* @__PURE__ */ new Map();
|
|
8208
|
-
try {
|
|
8209
|
-
const jieba = getJieba(dictType);
|
|
8210
|
-
for (const text of texts) {
|
|
8211
|
-
const cleaned = cleanText(text);
|
|
8212
|
-
if (!cleaned) continue;
|
|
8213
|
-
for (const { tag, word } of jieba.tag(cleaned)) {
|
|
8214
|
-
if (!isValidWord(word, locale, effectiveMinLength, enableStopwords, isStopword)) continue;
|
|
8215
|
-
posTagStats.set(tag, (posTagStats.get(tag) || 0) + 1);
|
|
8216
|
-
if (!allowedTags.has(tag)) continue;
|
|
8217
|
-
if (excludeSet && excludeSet.has(word.toLowerCase())) continue;
|
|
8218
|
-
wordFrequency.set(word, (wordFrequency.get(word) || 0) + 1);
|
|
8219
|
-
}
|
|
8220
|
-
}
|
|
8221
|
-
} catch (error) {
|
|
8222
|
-
console.error("[NLP] Chinese single-pass segmentation failed:", error);
|
|
8223
|
-
}
|
|
8224
|
-
const filtered = /* @__PURE__ */ new Map();
|
|
8225
|
-
let totalWords = 0;
|
|
8226
|
-
for (const [word, count] of wordFrequency) {
|
|
8227
|
-
if (count >= minCount) {
|
|
8228
|
-
filtered.set(word, count);
|
|
8229
|
-
totalWords += count;
|
|
8230
|
-
}
|
|
8231
|
-
}
|
|
8232
|
-
const sorted = [...filtered.entries()].sort((a, b) => b[1] - a[1]).slice(0, topN);
|
|
8233
|
-
return { words: new Map(sorted), uniqueWords: filtered.size, totalWords, posTagStats };
|
|
8234
|
-
}
|
|
8235
|
-
function collectPosTagStats(texts, minWordLength = 2, enableStopwords = true, dictType = "default") {
|
|
8236
|
-
const posStats = /* @__PURE__ */ new Map();
|
|
8237
|
-
try {
|
|
8238
|
-
const jieba = getJieba(dictType);
|
|
8239
|
-
for (const text of texts) {
|
|
8240
|
-
const cleaned = cleanText(text);
|
|
8241
|
-
if (!cleaned) continue;
|
|
8242
|
-
const tagged = jieba.tag(cleaned);
|
|
8243
|
-
for (const item of tagged) {
|
|
8244
|
-
if (!isValidWord(item.word, "zh-CN", minWordLength, enableStopwords, isStopword)) continue;
|
|
8245
|
-
posStats.set(item.tag, (posStats.get(item.tag) || 0) + 1);
|
|
8246
|
-
}
|
|
8247
|
-
}
|
|
8248
|
-
} catch (error) {
|
|
8249
|
-
console.error("[NLP] Failed to collect POS stats:", error);
|
|
8250
|
-
}
|
|
8251
|
-
return posStats;
|
|
8252
|
-
}
|
|
8253
|
-
function getPosTagDefinitions() {
|
|
8254
|
-
return POS_TAG_DEFINITIONS;
|
|
8255
|
-
}
|
|
8256
|
-
|
|
8257
|
-
// ../../packages/node-runtime/src/nlp/fts-tokenizer.ts
|
|
8258
|
-
function tokenizeForFts(text) {
|
|
8259
|
-
if (!text || text.trim().length === 0) return "";
|
|
8260
|
-
try {
|
|
8261
|
-
const jieba = getJieba();
|
|
8262
|
-
const tokens = jieba.cut(text, false);
|
|
8263
|
-
return tokens.map((t) => t.trim().toLowerCase()).filter((t) => t.length > 0).join(" ");
|
|
8264
|
-
} catch {
|
|
8265
|
-
return fallbackTokenize(text);
|
|
8266
|
-
}
|
|
8267
|
-
}
|
|
8268
|
-
function fallbackTokenize(text) {
|
|
8269
|
-
return text.toLowerCase().split(/\s+/).filter((t) => t.length > 0).join(" ");
|
|
8270
|
-
}
|
|
8271
|
-
function escapeToken(token) {
|
|
8272
|
-
return `"${token.replace(/"/g, '""')}"`;
|
|
8273
|
-
}
|
|
8274
|
-
function tokenizeQueryForFts(keywords) {
|
|
8275
|
-
if (keywords.length === 0) return "";
|
|
8276
|
-
const groups = keywords.map((kw) => {
|
|
8277
|
-
const trimmed = kw.trim();
|
|
8278
|
-
if (!trimmed) return "";
|
|
8279
|
-
try {
|
|
8280
|
-
const jieba = getJieba();
|
|
8281
|
-
const tokens = jieba.cut(trimmed, false).map((t) => t.trim().toLowerCase()).filter((t) => t.length > 0);
|
|
8282
|
-
if (tokens.length === 0) return "";
|
|
8283
|
-
if (tokens.length === 1) return escapeToken(tokens[0]);
|
|
8284
|
-
return tokens.map(escapeToken).join(" ");
|
|
8285
|
-
} catch {
|
|
8286
|
-
const simple = trimmed.toLowerCase().trim();
|
|
8287
|
-
return simple ? escapeToken(simple) : "";
|
|
8288
|
-
}
|
|
8289
|
-
}).filter((g) => g.length > 0);
|
|
8290
|
-
if (groups.length === 0) return "";
|
|
8291
|
-
if (groups.length === 1) return groups[0];
|
|
8292
|
-
return groups.map((g) => g.includes(" ") ? `(${g})` : g).join(" OR ");
|
|
8293
|
-
}
|
|
8294
|
-
|
|
8295
8411
|
// ../../packages/node-runtime/src/migrations/electron-data-migration.ts
|
|
8296
|
-
import * as
|
|
8412
|
+
import * as fs8 from "fs";
|
|
8297
8413
|
import * as os3 from "os";
|
|
8298
|
-
import * as
|
|
8414
|
+
import * as path13 from "path";
|
|
8299
8415
|
var SYSTEM_SUBDIRS = ["ai", "settings", "cache", "logs", "nlp"];
|
|
8300
8416
|
var STORAGE_CONFIG_FILE = "storage.json";
|
|
8301
8417
|
function getElectronUserDataDir(options = {}) {
|
|
8302
8418
|
const home = options.homeDir ?? os3.homedir();
|
|
8303
8419
|
switch (options.platform ?? process.platform) {
|
|
8304
8420
|
case "darwin":
|
|
8305
|
-
return
|
|
8421
|
+
return path13.join(home, "Library", "Application Support", "ChatLab");
|
|
8306
8422
|
case "win32": {
|
|
8307
|
-
const appData = options.appDataDir ?? process.env.APPDATA ??
|
|
8308
|
-
return
|
|
8423
|
+
const appData = options.appDataDir ?? process.env.APPDATA ?? path13.join(home, "AppData", "Roaming");
|
|
8424
|
+
return path13.join(appData, "ChatLab");
|
|
8309
8425
|
}
|
|
8310
8426
|
default:
|
|
8311
|
-
return
|
|
8427
|
+
return path13.join(home, ".config", "ChatLab");
|
|
8312
8428
|
}
|
|
8313
8429
|
}
|
|
8314
8430
|
function readElectronStorageConfig(electronUserData) {
|
|
8315
|
-
const configPath =
|
|
8316
|
-
if (!
|
|
8431
|
+
const configPath = path13.join(electronUserData, STORAGE_CONFIG_FILE);
|
|
8432
|
+
if (!fs8.existsSync(configPath)) return {};
|
|
8317
8433
|
try {
|
|
8318
|
-
return JSON.parse(
|
|
8434
|
+
return JSON.parse(fs8.readFileSync(configPath, "utf-8"));
|
|
8319
8435
|
} catch {
|
|
8320
8436
|
return {};
|
|
8321
8437
|
}
|
|
@@ -8323,45 +8439,45 @@ function readElectronStorageConfig(electronUserData) {
|
|
|
8323
8439
|
function resolveElectronDataDir() {
|
|
8324
8440
|
const electronUserData = getElectronUserDataDir();
|
|
8325
8441
|
const storageConfig = readElectronStorageConfig(electronUserData);
|
|
8326
|
-
if (storageConfig.dataDir &&
|
|
8442
|
+
if (storageConfig.dataDir && path13.isAbsolute(storageConfig.dataDir)) {
|
|
8327
8443
|
return storageConfig.dataDir;
|
|
8328
8444
|
}
|
|
8329
|
-
return
|
|
8445
|
+
return path13.join(electronUserData, "data");
|
|
8330
8446
|
}
|
|
8331
8447
|
function hasDatabases(dir) {
|
|
8332
|
-
const dbDir =
|
|
8333
|
-
if (!
|
|
8448
|
+
const dbDir = path13.join(dir, "databases");
|
|
8449
|
+
if (!fs8.existsSync(dbDir)) return false;
|
|
8334
8450
|
try {
|
|
8335
|
-
return
|
|
8451
|
+
return fs8.readdirSync(dbDir).some((f) => f.endsWith(".db"));
|
|
8336
8452
|
} catch {
|
|
8337
8453
|
return false;
|
|
8338
8454
|
}
|
|
8339
8455
|
}
|
|
8340
8456
|
function ensureDir2(dirPath) {
|
|
8341
|
-
if (!
|
|
8342
|
-
|
|
8457
|
+
if (!fs8.existsSync(dirPath)) {
|
|
8458
|
+
fs8.mkdirSync(dirPath, { recursive: true });
|
|
8343
8459
|
}
|
|
8344
8460
|
}
|
|
8345
8461
|
function copyDirMerge(src, dest) {
|
|
8346
8462
|
const stats = { copied: 0, skipped: 0 };
|
|
8347
|
-
if (!
|
|
8463
|
+
if (!fs8.existsSync(src)) return stats;
|
|
8348
8464
|
ensureDir2(dest);
|
|
8349
8465
|
let entries;
|
|
8350
8466
|
try {
|
|
8351
|
-
entries =
|
|
8467
|
+
entries = fs8.readdirSync(src, { withFileTypes: true });
|
|
8352
8468
|
} catch {
|
|
8353
8469
|
return stats;
|
|
8354
8470
|
}
|
|
8355
8471
|
for (const entry of entries) {
|
|
8356
|
-
const srcPath =
|
|
8357
|
-
const destPath =
|
|
8472
|
+
const srcPath = path13.join(src, entry.name);
|
|
8473
|
+
const destPath = path13.join(dest, entry.name);
|
|
8358
8474
|
try {
|
|
8359
8475
|
if (entry.isDirectory()) {
|
|
8360
8476
|
const sub = copyDirMerge(srcPath, destPath);
|
|
8361
8477
|
stats.copied += sub.copied;
|
|
8362
8478
|
stats.skipped += sub.skipped;
|
|
8363
|
-
} else if (!
|
|
8364
|
-
|
|
8479
|
+
} else if (!fs8.existsSync(destPath)) {
|
|
8480
|
+
fs8.copyFileSync(srcPath, destPath);
|
|
8365
8481
|
stats.copied++;
|
|
8366
8482
|
} else {
|
|
8367
8483
|
stats.skipped++;
|
|
@@ -8374,22 +8490,22 @@ function copyDirMerge(src, dest) {
|
|
|
8374
8490
|
function writeMigrationLog(logDir, message) {
|
|
8375
8491
|
try {
|
|
8376
8492
|
ensureDir2(logDir);
|
|
8377
|
-
const logPath =
|
|
8493
|
+
const logPath = path13.join(logDir, "app.log");
|
|
8378
8494
|
const ts = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").slice(0, 19);
|
|
8379
|
-
|
|
8495
|
+
fs8.appendFileSync(logPath, `[${ts}] [MIGRATION] ${message}
|
|
8380
8496
|
`, "utf-8");
|
|
8381
8497
|
} catch {
|
|
8382
8498
|
}
|
|
8383
8499
|
}
|
|
8384
8500
|
function wasElectronUsed() {
|
|
8385
8501
|
const electronUserData = getElectronUserDataDir();
|
|
8386
|
-
if (!
|
|
8387
|
-
return
|
|
8502
|
+
if (!fs8.existsSync(electronUserData)) return false;
|
|
8503
|
+
return fs8.existsSync(path13.join(electronUserData, "Preferences")) || fs8.existsSync(path13.join(electronUserData, "Local Storage")) || fs8.existsSync(path13.join(electronUserData, "Session Storage")) || fs8.existsSync(path13.join(electronUserData, "data"));
|
|
8388
8504
|
}
|
|
8389
8505
|
function verifyCliDataPath(databaseDir) {
|
|
8390
|
-
if (
|
|
8506
|
+
if (fs8.existsSync(databaseDir)) {
|
|
8391
8507
|
try {
|
|
8392
|
-
if (
|
|
8508
|
+
if (fs8.readdirSync(databaseDir).some((f) => f.endsWith(".db"))) return true;
|
|
8393
8509
|
} catch {
|
|
8394
8510
|
}
|
|
8395
8511
|
}
|
|
@@ -8407,9 +8523,9 @@ function migrateFromElectronIfNeeded(systemDir) {
|
|
|
8407
8523
|
let totalSkipped = 0;
|
|
8408
8524
|
const movedDirs = [];
|
|
8409
8525
|
for (const subDir of SYSTEM_SUBDIRS) {
|
|
8410
|
-
const srcDir =
|
|
8411
|
-
const destDir =
|
|
8412
|
-
if (!
|
|
8526
|
+
const srcDir = path13.join(electronDataDir, subDir);
|
|
8527
|
+
const destDir = path13.join(systemDir, subDir);
|
|
8528
|
+
if (!fs8.existsSync(srcDir)) continue;
|
|
8413
8529
|
const { copied, skipped } = copyDirMerge(srcDir, destDir);
|
|
8414
8530
|
if (copied > 0 || skipped > 0) {
|
|
8415
8531
|
movedDirs.push(subDir);
|
|
@@ -8419,13 +8535,13 @@ function migrateFromElectronIfNeeded(systemDir) {
|
|
|
8419
8535
|
}
|
|
8420
8536
|
}
|
|
8421
8537
|
for (const subDir of movedDirs) {
|
|
8422
|
-
const srcDir =
|
|
8538
|
+
const srcDir = path13.join(electronDataDir, subDir);
|
|
8423
8539
|
try {
|
|
8424
|
-
|
|
8540
|
+
fs8.rmSync(srcDir, { recursive: true, force: true });
|
|
8425
8541
|
} catch {
|
|
8426
8542
|
}
|
|
8427
8543
|
}
|
|
8428
|
-
const logsDir =
|
|
8544
|
+
const logsDir = path13.join(systemDir, "logs");
|
|
8429
8545
|
const summary = `CLI first-run: detected Electron data at ${electronDataDir}, pointed user_data_dir there, copied ${totalCopied} system files (${movedDirs.join(", ") || "none"})`;
|
|
8430
8546
|
writeMigrationLog(logsDir, summary);
|
|
8431
8547
|
console.log(`[Migration] ${summary}`);
|
|
@@ -8439,7 +8555,7 @@ function migrateFromElectronIfNeeded(systemDir) {
|
|
|
8439
8555
|
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
8440
8556
|
console.error("[Migration] Electron data migration failed:", errorMsg);
|
|
8441
8557
|
try {
|
|
8442
|
-
writeMigrationLog(
|
|
8558
|
+
writeMigrationLog(path13.join(systemDir, "logs"), `Electron migration failed: ${errorMsg}`);
|
|
8443
8559
|
} catch {
|
|
8444
8560
|
}
|
|
8445
8561
|
return { migrated: false, electronDetected: true, error: errorMsg };
|
|
@@ -8447,26 +8563,26 @@ function migrateFromElectronIfNeeded(systemDir) {
|
|
|
8447
8563
|
}
|
|
8448
8564
|
|
|
8449
8565
|
// ../../packages/node-runtime/src/cache/session-cache.ts
|
|
8450
|
-
import * as
|
|
8451
|
-
import * as
|
|
8566
|
+
import * as fs9 from "fs";
|
|
8567
|
+
import * as path14 from "path";
|
|
8452
8568
|
function getCachePath(sessionId, cacheDir) {
|
|
8453
|
-
return
|
|
8569
|
+
return path14.join(cacheDir, `${sessionId}.cache.json`);
|
|
8454
8570
|
}
|
|
8455
8571
|
function readCacheFile(cachePath) {
|
|
8456
8572
|
try {
|
|
8457
|
-
if (!
|
|
8458
|
-
return JSON.parse(
|
|
8573
|
+
if (!fs9.existsSync(cachePath)) return null;
|
|
8574
|
+
return JSON.parse(fs9.readFileSync(cachePath, "utf-8"));
|
|
8459
8575
|
} catch {
|
|
8460
8576
|
return null;
|
|
8461
8577
|
}
|
|
8462
8578
|
}
|
|
8463
8579
|
function writeCacheFile(cachePath, content) {
|
|
8464
8580
|
try {
|
|
8465
|
-
const dir =
|
|
8466
|
-
if (!
|
|
8467
|
-
|
|
8581
|
+
const dir = path14.dirname(cachePath);
|
|
8582
|
+
if (!fs9.existsSync(dir)) {
|
|
8583
|
+
fs9.mkdirSync(dir, { recursive: true });
|
|
8468
8584
|
}
|
|
8469
|
-
|
|
8585
|
+
fs9.writeFileSync(cachePath, JSON.stringify(content), "utf-8");
|
|
8470
8586
|
} catch {
|
|
8471
8587
|
}
|
|
8472
8588
|
}
|
|
@@ -8486,7 +8602,7 @@ function invalidateCache(sessionId, cacheDir, key) {
|
|
|
8486
8602
|
const cachePath = getCachePath(sessionId, cacheDir);
|
|
8487
8603
|
try {
|
|
8488
8604
|
if (!key) {
|
|
8489
|
-
if (
|
|
8605
|
+
if (fs9.existsSync(cachePath)) fs9.unlinkSync(cachePath);
|
|
8490
8606
|
} else {
|
|
8491
8607
|
const file = readCacheFile(cachePath);
|
|
8492
8608
|
if (file && file[key]) {
|
|
@@ -8500,7 +8616,7 @@ function invalidateCache(sessionId, cacheDir, key) {
|
|
|
8500
8616
|
function deleteSessionCache(sessionId, cacheDir) {
|
|
8501
8617
|
const cachePath = getCachePath(sessionId, cacheDir);
|
|
8502
8618
|
try {
|
|
8503
|
-
if (
|
|
8619
|
+
if (fs9.existsSync(cachePath)) fs9.unlinkSync(cachePath);
|
|
8504
8620
|
} catch {
|
|
8505
8621
|
}
|
|
8506
8622
|
}
|
|
@@ -8554,8 +8670,8 @@ function computeAndSetMembersCache(db, sessionId, cacheDir) {
|
|
|
8554
8670
|
}
|
|
8555
8671
|
|
|
8556
8672
|
// ../../packages/node-runtime/src/data-dir-compat.ts
|
|
8557
|
-
import
|
|
8558
|
-
import
|
|
8673
|
+
import fs10 from "fs";
|
|
8674
|
+
import path15 from "path";
|
|
8559
8675
|
var DataDirCompatibilityError = class extends Error {
|
|
8560
8676
|
code;
|
|
8561
8677
|
userDataDir;
|
|
@@ -8575,10 +8691,10 @@ var DataDirCompatibilityError = class extends Error {
|
|
|
8575
8691
|
};
|
|
8576
8692
|
function readDataDirCompatibilityMeta(userDataDir) {
|
|
8577
8693
|
const metaPath = getMetaPath(userDataDir);
|
|
8578
|
-
if (!
|
|
8694
|
+
if (!fs10.existsSync(metaPath)) return null;
|
|
8579
8695
|
let parsed;
|
|
8580
8696
|
try {
|
|
8581
|
-
parsed = JSON.parse(
|
|
8697
|
+
parsed = JSON.parse(fs10.readFileSync(metaPath, "utf-8"));
|
|
8582
8698
|
} catch (error) {
|
|
8583
8699
|
throw createInvalidMetaError(userDataDir, metaPath, "Invalid data directory compatibility meta JSON.", error);
|
|
8584
8700
|
}
|
|
@@ -8661,7 +8777,7 @@ function raiseDataDirMinRuntimeVersion(pathProvider, input) {
|
|
|
8661
8777
|
return nextMeta;
|
|
8662
8778
|
}
|
|
8663
8779
|
function getMetaPath(userDataDir) {
|
|
8664
|
-
return
|
|
8780
|
+
return path15.join(userDataDir, ".chatlab-meta.json");
|
|
8665
8781
|
}
|
|
8666
8782
|
function validateDataDirCompatibilityMeta(value, userDataDir, metaPath) {
|
|
8667
8783
|
if (!isRecord2(value)) {
|
|
@@ -8732,15 +8848,15 @@ function mergeReasons(existing, next) {
|
|
|
8732
8848
|
return [.../* @__PURE__ */ new Set([...existing, next])];
|
|
8733
8849
|
}
|
|
8734
8850
|
function writeMetaAtomic(userDataDir, metaPath, meta) {
|
|
8735
|
-
|
|
8736
|
-
const tempPath =
|
|
8851
|
+
fs10.mkdirSync(userDataDir, { recursive: true });
|
|
8852
|
+
const tempPath = path15.join(userDataDir, `.chatlab-meta.json.${process.pid}.${Date.now()}.tmp`);
|
|
8737
8853
|
try {
|
|
8738
|
-
|
|
8854
|
+
fs10.writeFileSync(tempPath, `${JSON.stringify(meta, null, 2)}
|
|
8739
8855
|
`, "utf-8");
|
|
8740
|
-
|
|
8856
|
+
fs10.renameSync(tempPath, metaPath);
|
|
8741
8857
|
} catch (error) {
|
|
8742
8858
|
try {
|
|
8743
|
-
if (
|
|
8859
|
+
if (fs10.existsSync(tempPath)) fs10.unlinkSync(tempPath);
|
|
8744
8860
|
} catch {
|
|
8745
8861
|
}
|
|
8746
8862
|
throw error;
|
|
@@ -8777,7 +8893,7 @@ function raiseChatDbCompatibilityGate(pathProvider, runtime) {
|
|
|
8777
8893
|
});
|
|
8778
8894
|
}
|
|
8779
8895
|
}
|
|
8780
|
-
function getChatDbMigrations(
|
|
8896
|
+
function getChatDbMigrations() {
|
|
8781
8897
|
const hasColumn2 = (db, tableName, columnName) => {
|
|
8782
8898
|
const tableInfo = db.pragma(`table_info(${tableName})`);
|
|
8783
8899
|
return tableInfo.some((col) => col.name === columnName);
|
|
@@ -8856,39 +8972,8 @@ function getChatDbMigrations(deps) {
|
|
|
8856
8972
|
},
|
|
8857
8973
|
{
|
|
8858
8974
|
version: 4,
|
|
8859
|
-
description: "
|
|
8860
|
-
up: (
|
|
8861
|
-
const hasTable2 = db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='message_fts'").get();
|
|
8862
|
-
if (hasTable2) return;
|
|
8863
|
-
db.exec(`
|
|
8864
|
-
CREATE VIRTUAL TABLE IF NOT EXISTS message_fts USING fts5(
|
|
8865
|
-
content,
|
|
8866
|
-
content='',
|
|
8867
|
-
content_rowid=id
|
|
8868
|
-
)
|
|
8869
|
-
`);
|
|
8870
|
-
const tokenize = deps?.tokenizeForFts;
|
|
8871
|
-
if (!tokenize) return;
|
|
8872
|
-
const BATCH_SIZE = 5e3;
|
|
8873
|
-
const insertFts = db.prepare("INSERT INTO message_fts(rowid, content) VALUES (?, ?)");
|
|
8874
|
-
const countRow = db.prepare("SELECT COUNT(*) as total FROM message WHERE type = 0 AND content IS NOT NULL AND content != ''").get();
|
|
8875
|
-
const total = countRow?.total ?? 0;
|
|
8876
|
-
let offset = 0;
|
|
8877
|
-
while (offset < total) {
|
|
8878
|
-
const rows = db.prepare(
|
|
8879
|
-
`SELECT id, content FROM message
|
|
8880
|
-
WHERE type = 0 AND content IS NOT NULL AND content != ''
|
|
8881
|
-
ORDER BY id ASC LIMIT ? OFFSET ?`
|
|
8882
|
-
).all(BATCH_SIZE, offset);
|
|
8883
|
-
if (rows.length === 0) break;
|
|
8884
|
-
for (const row of rows) {
|
|
8885
|
-
const tokens = tokenize(row.content);
|
|
8886
|
-
if (tokens) {
|
|
8887
|
-
insertFts.run(row.id, tokens);
|
|
8888
|
-
}
|
|
8889
|
-
}
|
|
8890
|
-
offset += BATCH_SIZE;
|
|
8891
|
-
}
|
|
8975
|
+
description: "Preserve the legacy schema migration sequence",
|
|
8976
|
+
up: () => {
|
|
8892
8977
|
}
|
|
8893
8978
|
},
|
|
8894
8979
|
{
|
|
@@ -9076,49 +9161,56 @@ function getChatDbMigrations(deps) {
|
|
|
9076
9161
|
CREATE INDEX IF NOT EXISTS idx_message_reply_to ON message(reply_to_message_id);
|
|
9077
9162
|
`);
|
|
9078
9163
|
}
|
|
9164
|
+
},
|
|
9165
|
+
{
|
|
9166
|
+
version: 9,
|
|
9167
|
+
description: "Remove the obsolete per-session full-text index",
|
|
9168
|
+
up: (db) => {
|
|
9169
|
+
db.exec("DROP TABLE IF EXISTS message_fts");
|
|
9170
|
+
}
|
|
9079
9171
|
}
|
|
9080
9172
|
];
|
|
9081
9173
|
}
|
|
9082
9174
|
|
|
9083
9175
|
// ../../packages/node-runtime/src/services/contacts/paths.ts
|
|
9084
|
-
import
|
|
9176
|
+
import path16 from "path";
|
|
9085
9177
|
var CONTACTS_DIR_NAME = "contacts";
|
|
9086
9178
|
var CONTACTS_FACTS_DIR_NAME = "facts";
|
|
9087
9179
|
function getContactsDir(userDataDir) {
|
|
9088
|
-
return
|
|
9180
|
+
return path16.join(userDataDir, CONTACTS_DIR_NAME);
|
|
9089
9181
|
}
|
|
9090
9182
|
function getContactsFactsCacheDir(userDataDir) {
|
|
9091
|
-
return
|
|
9183
|
+
return path16.join(getContactsDir(userDataDir), CONTACTS_FACTS_DIR_NAME);
|
|
9092
9184
|
}
|
|
9093
9185
|
|
|
9094
9186
|
// ../../packages/node-runtime/src/services/global-insight/paths.ts
|
|
9095
|
-
import
|
|
9187
|
+
import path17 from "path";
|
|
9096
9188
|
function getGlobalInsightDir(userDataDir) {
|
|
9097
|
-
return
|
|
9189
|
+
return path17.join(userDataDir, "insight", "annual-summary");
|
|
9098
9190
|
}
|
|
9099
9191
|
function getGlobalInsightFactsCacheDir(userDataDir) {
|
|
9100
|
-
return
|
|
9192
|
+
return path17.join(getGlobalInsightDir(userDataDir), "facts");
|
|
9101
9193
|
}
|
|
9102
9194
|
|
|
9103
9195
|
// ../../packages/node-runtime/src/services/people/relationships/paths.ts
|
|
9104
|
-
import
|
|
9196
|
+
import path18 from "path";
|
|
9105
9197
|
var PEOPLE_DIR_NAME = "people";
|
|
9106
9198
|
var RELATIONSHIPS_DIR_NAME = "relationships";
|
|
9107
9199
|
var RELATIONSHIPS_FACTS_DIR_NAME = "facts";
|
|
9108
9200
|
function getPeopleRelationshipsDir(userDataDir) {
|
|
9109
|
-
return
|
|
9201
|
+
return path18.join(userDataDir, PEOPLE_DIR_NAME, RELATIONSHIPS_DIR_NAME);
|
|
9110
9202
|
}
|
|
9111
9203
|
function getPeopleRelationshipsFactsCacheDir(userDataDir) {
|
|
9112
|
-
return
|
|
9204
|
+
return path18.join(getPeopleRelationshipsDir(userDataDir), RELATIONSHIPS_FACTS_DIR_NAME);
|
|
9113
9205
|
}
|
|
9114
9206
|
|
|
9115
9207
|
// ../../packages/node-runtime/src/database-manager.ts
|
|
9116
|
-
import * as
|
|
9117
|
-
import * as
|
|
9208
|
+
import * as fs12 from "fs";
|
|
9209
|
+
import * as path20 from "path";
|
|
9118
9210
|
|
|
9119
9211
|
// ../../packages/node-runtime/src/services/global-insight/snapshot.ts
|
|
9120
|
-
import
|
|
9121
|
-
import
|
|
9212
|
+
import fs11 from "fs";
|
|
9213
|
+
import path19 from "path";
|
|
9122
9214
|
|
|
9123
9215
|
// ../../packages/node-runtime/src/services/global-insight/time-range.ts
|
|
9124
9216
|
function normalizeAnnualSummaryRange(input, nowInput = /* @__PURE__ */ new Date()) {
|
|
@@ -9169,17 +9261,17 @@ function toAnnualSummaryLocalDate(range) {
|
|
|
9169
9261
|
// ../../packages/node-runtime/src/services/global-insight/snapshot.ts
|
|
9170
9262
|
var SNAPSHOT_TMP_PREFIX = "annual-summary.tmp-";
|
|
9171
9263
|
function getAnnualSummarySnapshotPath(snapshotDir, range) {
|
|
9172
|
-
return
|
|
9264
|
+
return path19.join(snapshotDir, `annual-summary-${toAnnualSummaryRangeKey(range)}.json`);
|
|
9173
9265
|
}
|
|
9174
9266
|
function readAnnualSummarySnapshot(snapshotDir, range, options = {}) {
|
|
9175
9267
|
const snapshotPath = getAnnualSummarySnapshotPath(snapshotDir, range);
|
|
9176
|
-
if (!
|
|
9268
|
+
if (!fs11.existsSync(snapshotPath)) return null;
|
|
9177
9269
|
try {
|
|
9178
|
-
return JSON.parse(
|
|
9270
|
+
return JSON.parse(fs11.readFileSync(snapshotPath, "utf-8"));
|
|
9179
9271
|
} catch (error) {
|
|
9180
|
-
const backupPath =
|
|
9272
|
+
const backupPath = path19.join(snapshotDir, `annual-summary.corrupt-${options.now?.() ?? Date.now()}.json`);
|
|
9181
9273
|
try {
|
|
9182
|
-
|
|
9274
|
+
fs11.renameSync(snapshotPath, backupPath);
|
|
9183
9275
|
} catch (renameError) {
|
|
9184
9276
|
appLogger.warn("global-insight", "failed to backup corrupt annual summary snapshot", renameError);
|
|
9185
9277
|
}
|
|
@@ -9188,28 +9280,28 @@ function readAnnualSummarySnapshot(snapshotDir, range, options = {}) {
|
|
|
9188
9280
|
}
|
|
9189
9281
|
}
|
|
9190
9282
|
function writeAnnualSummarySnapshot(snapshotDir, snapshot) {
|
|
9191
|
-
|
|
9192
|
-
const tmpPath =
|
|
9193
|
-
|
|
9194
|
-
|
|
9283
|
+
fs11.mkdirSync(snapshotDir, { recursive: true });
|
|
9284
|
+
const tmpPath = path19.join(snapshotDir, `${SNAPSHOT_TMP_PREFIX}${process.pid}-${Date.now()}`);
|
|
9285
|
+
fs11.writeFileSync(tmpPath, JSON.stringify(snapshot, null, 2), "utf-8");
|
|
9286
|
+
fs11.renameSync(tmpPath, getAnnualSummarySnapshotPath(snapshotDir, snapshot.range));
|
|
9195
9287
|
}
|
|
9196
9288
|
function cleanupAnnualSummarySnapshotTempFiles(snapshotDir) {
|
|
9197
|
-
if (!
|
|
9198
|
-
for (const name of
|
|
9289
|
+
if (!fs11.existsSync(snapshotDir)) return;
|
|
9290
|
+
for (const name of fs11.readdirSync(snapshotDir)) {
|
|
9199
9291
|
if (!name.startsWith(SNAPSHOT_TMP_PREFIX)) continue;
|
|
9200
9292
|
try {
|
|
9201
|
-
|
|
9293
|
+
fs11.rmSync(path19.join(snapshotDir, name), { force: true });
|
|
9202
9294
|
} catch (error) {
|
|
9203
9295
|
appLogger.warn("global-insight", "failed to remove annual summary snapshot temp file", error);
|
|
9204
9296
|
}
|
|
9205
9297
|
}
|
|
9206
9298
|
}
|
|
9207
9299
|
function deleteAnnualSummarySnapshots(snapshotDir) {
|
|
9208
|
-
if (!
|
|
9209
|
-
for (const name of
|
|
9300
|
+
if (!fs11.existsSync(snapshotDir)) return;
|
|
9301
|
+
for (const name of fs11.readdirSync(snapshotDir)) {
|
|
9210
9302
|
if (!name.startsWith("annual-summary-") && !name.startsWith("annual-summary.")) continue;
|
|
9211
9303
|
try {
|
|
9212
|
-
|
|
9304
|
+
fs11.rmSync(path19.join(snapshotDir, name), { force: true });
|
|
9213
9305
|
} catch (error) {
|
|
9214
9306
|
appLogger.warn("global-insight", "failed to remove annual summary snapshot", error);
|
|
9215
9307
|
}
|
|
@@ -9217,15 +9309,9 @@ function deleteAnnualSummarySnapshots(snapshotDir) {
|
|
|
9217
9309
|
}
|
|
9218
9310
|
|
|
9219
9311
|
// ../../packages/node-runtime/src/database-manager.ts
|
|
9220
|
-
function createMigrationDeps(overrides) {
|
|
9221
|
-
return {
|
|
9222
|
-
tokenizeForFts,
|
|
9223
|
-
...overrides
|
|
9224
|
-
};
|
|
9225
|
-
}
|
|
9226
9312
|
function listDatabaseCandidateIds(databaseDir) {
|
|
9227
|
-
if (!
|
|
9228
|
-
return
|
|
9313
|
+
if (!fs12.existsSync(databaseDir)) return [];
|
|
9314
|
+
return fs12.readdirSync(databaseDir).filter((fileName) => fileName.endsWith(".db") && !fileName.startsWith("._")).map((fileName) => fileName.slice(0, -3));
|
|
9229
9315
|
}
|
|
9230
9316
|
var DatabaseManager = class {
|
|
9231
9317
|
constructor(pathProvider, options = {}) {
|
|
@@ -9234,13 +9320,11 @@ var DatabaseManager = class {
|
|
|
9234
9320
|
throw new Error("DatabaseManager runtime identity is required for data directory compatibility checks.");
|
|
9235
9321
|
}
|
|
9236
9322
|
this.nativeBinding = options?.nativeBinding;
|
|
9237
|
-
this.migrationDeps = createMigrationDeps(options?.migrationDeps);
|
|
9238
9323
|
this.runtime = options?.runtime ?? null;
|
|
9239
9324
|
}
|
|
9240
9325
|
pathProvider;
|
|
9241
9326
|
cache = /* @__PURE__ */ new Map();
|
|
9242
9327
|
nativeBinding;
|
|
9243
|
-
migrationDeps;
|
|
9244
9328
|
runtime;
|
|
9245
9329
|
/**
|
|
9246
9330
|
* Open a session DB (read-only by default, cached).
|
|
@@ -9251,7 +9335,7 @@ var DatabaseManager = class {
|
|
|
9251
9335
|
return this.cache.get(sessionId);
|
|
9252
9336
|
}
|
|
9253
9337
|
const dbPath = this.getDbPath(sessionId);
|
|
9254
|
-
if (!
|
|
9338
|
+
if (!fs12.existsSync(dbPath)) return null;
|
|
9255
9339
|
this.migrateIfNeeded(dbPath);
|
|
9256
9340
|
this.assertCompatible();
|
|
9257
9341
|
const adapter = openBetterSqliteDatabase(dbPath, {
|
|
@@ -9278,7 +9362,7 @@ var DatabaseManager = class {
|
|
|
9278
9362
|
nativeBinding: this.nativeBinding
|
|
9279
9363
|
});
|
|
9280
9364
|
try {
|
|
9281
|
-
const migrations = getChatDbMigrations(
|
|
9365
|
+
const migrations = getChatDbMigrations();
|
|
9282
9366
|
this.runMigrations(adapter, migrations);
|
|
9283
9367
|
} finally {
|
|
9284
9368
|
adapter.close();
|
|
@@ -9297,13 +9381,13 @@ var DatabaseManager = class {
|
|
|
9297
9381
|
this.cache.delete(sessionId);
|
|
9298
9382
|
}
|
|
9299
9383
|
const dbPath = this.getDbPath(sessionId);
|
|
9300
|
-
if (!
|
|
9384
|
+
if (!fs12.existsSync(dbPath)) return null;
|
|
9301
9385
|
const adapter = openBetterSqliteDatabase(dbPath, {
|
|
9302
9386
|
readonly: false,
|
|
9303
9387
|
nativeBinding: this.nativeBinding
|
|
9304
9388
|
});
|
|
9305
9389
|
if (needsMigration(adapter, CURRENT_SCHEMA_VERSION)) {
|
|
9306
|
-
const migrations = getChatDbMigrations(
|
|
9390
|
+
const migrations = getChatDbMigrations();
|
|
9307
9391
|
this.runMigrations(adapter, migrations);
|
|
9308
9392
|
this.assertCompatible();
|
|
9309
9393
|
} else {
|
|
@@ -9365,17 +9449,17 @@ var DatabaseManager = class {
|
|
|
9365
9449
|
* 获取数据库文件路径
|
|
9366
9450
|
*/
|
|
9367
9451
|
getDbPath(sessionId) {
|
|
9368
|
-
return
|
|
9452
|
+
return path20.join(this.pathProvider.getDatabaseDir(), `${sessionId}.db`);
|
|
9369
9453
|
}
|
|
9370
9454
|
openRawSessionDatabase(sessionId, options = {}) {
|
|
9371
9455
|
this.assertCompatible();
|
|
9372
9456
|
const dbPath = this.getDbPath(sessionId);
|
|
9373
|
-
if (!options.create && !
|
|
9457
|
+
if (!options.create && !fs12.existsSync(dbPath)) {
|
|
9374
9458
|
throw new Error(`Session database not found: ${sessionId}`);
|
|
9375
9459
|
}
|
|
9376
9460
|
if (!options.readonly) {
|
|
9377
9461
|
this.close(sessionId);
|
|
9378
|
-
|
|
9462
|
+
fs12.mkdirSync(path20.dirname(dbPath), { recursive: true });
|
|
9379
9463
|
}
|
|
9380
9464
|
const adapter = openBetterSqliteDatabase(dbPath, {
|
|
9381
9465
|
readonly: options.readonly ?? false,
|
|
@@ -9393,17 +9477,17 @@ var DatabaseManager = class {
|
|
|
9393
9477
|
deleteSessionDatabaseFiles(sessionId) {
|
|
9394
9478
|
this.close(sessionId);
|
|
9395
9479
|
const dbPath = this.getDbPath(sessionId);
|
|
9396
|
-
const existed = ["", "-wal", "-shm"].some((suffix) =>
|
|
9480
|
+
const existed = ["", "-wal", "-shm"].some((suffix) => fs12.existsSync(dbPath + suffix));
|
|
9397
9481
|
for (const suffix of ["", "-wal", "-shm"]) {
|
|
9398
9482
|
try {
|
|
9399
9483
|
const filePath = dbPath + suffix;
|
|
9400
|
-
if (
|
|
9484
|
+
if (fs12.existsSync(filePath)) fs12.unlinkSync(filePath);
|
|
9401
9485
|
} catch {
|
|
9402
9486
|
}
|
|
9403
9487
|
}
|
|
9404
9488
|
const cacheDir = this.pathProvider.getCacheDir();
|
|
9405
9489
|
deleteSessionCache(sessionId, cacheDir);
|
|
9406
|
-
deleteSessionCache(sessionId,
|
|
9490
|
+
deleteSessionCache(sessionId, path20.join(cacheDir, "query"));
|
|
9407
9491
|
deleteSessionCache(sessionId, getContactsFactsCacheDir(this.pathProvider.getUserDataDir()));
|
|
9408
9492
|
deleteSessionCache(sessionId, getGlobalInsightFactsCacheDir(this.pathProvider.getUserDataDir()));
|
|
9409
9493
|
deleteAnnualSummarySnapshots(getGlobalInsightDir(this.pathProvider.getUserDataDir()));
|
|
@@ -9444,12 +9528,12 @@ function shouldRepairCompatibilityGate(schemaVersion) {
|
|
|
9444
9528
|
}
|
|
9445
9529
|
|
|
9446
9530
|
// ../../packages/node-runtime/src/cache/analytics-cache.ts
|
|
9447
|
-
import * as
|
|
9531
|
+
import * as fs13 from "fs";
|
|
9448
9532
|
function getDbFileVersion(dbPath) {
|
|
9449
9533
|
const parts = [];
|
|
9450
9534
|
for (const p of [dbPath, `${dbPath}-wal`]) {
|
|
9451
9535
|
try {
|
|
9452
|
-
const st =
|
|
9536
|
+
const st = fs13.statSync(p);
|
|
9453
9537
|
parts.push(`${Math.floor(st.mtimeMs)}:${st.size}`);
|
|
9454
9538
|
} catch {
|
|
9455
9539
|
parts.push("-");
|
|
@@ -9605,9 +9689,10 @@ export {
|
|
|
9605
9689
|
POS_TAG_DEFINITIONS,
|
|
9606
9690
|
MEANINGFUL_POS_TAGS,
|
|
9607
9691
|
isStopword,
|
|
9692
|
+
cleanText,
|
|
9693
|
+
isValidWord,
|
|
9608
9694
|
buildExcludeKeywordsConditions,
|
|
9609
9695
|
getMentionAnalysis,
|
|
9610
|
-
getMentionGraph,
|
|
9611
9696
|
getLaughAnalysis,
|
|
9612
9697
|
getClusterGraph,
|
|
9613
9698
|
resolveOwnerMember,
|
|
@@ -9654,6 +9739,8 @@ export {
|
|
|
9654
9739
|
deleteMembers,
|
|
9655
9740
|
deleteMember,
|
|
9656
9741
|
getCatchphraseAnalysis,
|
|
9742
|
+
GROUP_RELATIONSHIP_GALAXY_ALGORITHM_VERSION,
|
|
9743
|
+
getGroupRelationshipGalaxy,
|
|
9657
9744
|
getRelationshipStats,
|
|
9658
9745
|
getJourneyStats,
|
|
9659
9746
|
getLanguagePreferenceAnalysis,
|
|
@@ -9683,28 +9770,17 @@ export {
|
|
|
9683
9770
|
generateMessageKey,
|
|
9684
9771
|
writeParseResultToDb,
|
|
9685
9772
|
getCollidingPlatformIds,
|
|
9773
|
+
getCollidingPlatformIdsFromMessages,
|
|
9686
9774
|
normalizePlatformId,
|
|
9687
9775
|
detectConflictsInMessages,
|
|
9688
9776
|
mergeMembers2,
|
|
9689
9777
|
CHAT_DB_INDEXES,
|
|
9690
9778
|
CHAT_DB_SCHEMA,
|
|
9691
|
-
FTS_TABLE_SCHEMA,
|
|
9692
9779
|
AiLogger,
|
|
9693
9780
|
extractErrorInfo,
|
|
9694
9781
|
extractErrorStack,
|
|
9695
9782
|
initAppLogger,
|
|
9696
9783
|
appLogger,
|
|
9697
|
-
initNlpDir,
|
|
9698
|
-
getNlpDir,
|
|
9699
|
-
getJieba,
|
|
9700
|
-
clearJiebaInstance,
|
|
9701
|
-
segment,
|
|
9702
|
-
batchSegmentWithFrequency,
|
|
9703
|
-
batchSegmentChineseWithStats,
|
|
9704
|
-
collectPosTagStats,
|
|
9705
|
-
getPosTagDefinitions,
|
|
9706
|
-
tokenizeForFts,
|
|
9707
|
-
tokenizeQueryForFts,
|
|
9708
9784
|
wasElectronUsed,
|
|
9709
9785
|
verifyCliDataPath,
|
|
9710
9786
|
migrateFromElectronIfNeeded,
|
|
@@ -9747,4 +9823,4 @@ export {
|
|
|
9747
9823
|
StaticPathProvider,
|
|
9748
9824
|
createDatabaseManagerAdapter
|
|
9749
9825
|
};
|
|
9750
|
-
//# sourceMappingURL=chunk-
|
|
9826
|
+
//# sourceMappingURL=chunk-22YJOBA3.mjs.map
|