chatlab-cli 0.26.2 → 0.27.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/README.md +10 -4
- package/dist/{chat-command-BVIG2LCO.mjs → chat-command-SKGPS3PG.mjs} +5 -5
- package/dist/{chunk-GBQQJJKK.mjs → chunk-3HOTLLFC.mjs} +476 -186
- package/dist/chunk-3HOTLLFC.mjs.map +1 -0
- package/dist/{chunk-QKQRV7RV.mjs → chunk-6FXGC6HE.mjs} +2 -2
- package/dist/{chunk-JFSTXSX2.mjs → chunk-7LPVQR77.mjs} +42 -7
- package/dist/chunk-7LPVQR77.mjs.map +1 -0
- package/dist/{chunk-6AOEMUCM.mjs → chunk-FHLPXRDL.mjs} +2 -2
- package/dist/{chunk-VSOWNKPA.mjs → chunk-GFHSSCCR.mjs} +11 -1
- package/dist/{chunk-TMZC3SNX.mjs → chunk-KCFVHVO4.mjs} +6128 -4255
- package/dist/chunk-KCFVHVO4.mjs.map +1 -0
- package/dist/{chunk-SCE4YL3M.mjs → chunk-M6ABWESL.mjs} +466 -116
- package/dist/chunk-M6ABWESL.mjs.map +1 -0
- package/dist/{chunk-GTXX72AQ.mjs → chunk-MJITS5CE.mjs} +16 -16
- package/dist/{chunk-3XFHIQIW.mjs → chunk-UBJHT3BU.mjs} +2 -2
- package/dist/cli.mjs +4 -4
- package/dist/{http-NLFLQDI7.mjs → http-CPFUTIUB.mjs} +9 -9
- package/dist/{import-ILK6MGYR.mjs → import-UJXKIKYN.mjs} +5 -5
- package/dist/index.mjs +9 -9
- package/dist/{mcp-FOLR5XJL.mjs → mcp-SW4DTCWJ.mjs} +8 -8
- package/dist/onnxruntime_binding-6Q6HXASN.node +0 -0
- package/dist/onnxruntime_binding-EKZT2NRK.node +0 -0
- package/dist/onnxruntime_binding-P6S7V3CI.node +0 -0
- package/dist/onnxruntime_binding-PJNNIIUO.node +0 -0
- package/dist/onnxruntime_binding-UN6SPTQK.node +0 -0
- package/dist/{service-KZTHBDQ5.mjs → service-KGL3RAIA.mjs} +2 -2
- package/dist/{src-OQN3VE73.mjs → src-M3MO3LEL.mjs} +15 -71
- package/dist/{stream-import-F5QI2HTO.mjs → stream-import-4VDJZHMY.mjs} +4 -4
- package/dist/transformers.node-PUMHAODN.mjs +42889 -0
- package/dist/transformers.node-PUMHAODN.mjs.map +1 -0
- package/dist/{update-checker-JIBA7T4U.mjs → update-checker-LGMTEKMT.mjs} +37 -13
- package/dist/update-checker-LGMTEKMT.mjs.map +1 -0
- package/dist-web/assets/{DebugToolsPanel-CrqULdXp.css → DebugToolsPanel-DzZmSzIU.css} +1 -1
- package/dist-web/assets/DebugToolsPanel.vue_vue_type_script_setup_true_lang-C8mwzxDa.js +110 -0
- package/dist-web/assets/{MessageExportModal-ttP_JPqO.js → MessageExportModal-DCB-mzf2.js} +1 -1
- package/dist-web/assets/{electron-BeJHY587.js → electron-DDUv0ohD.js} +1 -1
- package/dist-web/assets/{fetch-H4B1CK_T.js → fetch-B3gsbY_U.js} +1 -1
- package/dist-web/assets/{fetch-DBJ7Xkjt.js → fetch-B9Oy0bcD.js} +1 -1
- package/dist-web/assets/{fetch-rtQEc8Bu.js → fetch-Bry8iJpq.js} +1 -1
- package/dist-web/assets/fetch-C1aC5Fd1.js +1 -0
- package/dist-web/assets/{fetch-BETFYYe1.js → fetch-C69hj3wS.js} +1 -1
- package/dist-web/assets/{fetch-B7USXx9R.js → fetch-Ck4rGxkO.js} +1 -1
- package/dist-web/assets/{fetch-DW8r_JmP.js → fetch-Ckang-F8.js} +1 -1
- package/dist-web/assets/fetch-CwHncc6x.js +5 -0
- package/dist-web/assets/{fetch-4s20GRbb.js → fetch-D8Ng59an.js} +1 -1
- package/dist-web/assets/{fetch-DtXUkazy.js → fetch-DOtBAxUS.js} +1 -1
- package/dist-web/assets/index-B40SKtW8.js +173 -0
- package/dist-web/assets/index-BX3tqfLz.css +1 -0
- package/dist-web/assets/{index-Dov-rTKE.js → index-Bd9j73nw.js} +1 -1
- package/dist-web/assets/index-BfFj8Evo.js +64 -0
- package/dist-web/assets/index-C16V6EoP.js +11 -0
- package/dist-web/assets/{index-CYMTvtEI.js → index-D-J8dY7w.js} +1 -1
- package/dist-web/assets/index-I3SWZjnW.css +1 -0
- package/dist-web/assets/index-YmzTgkb2.css +1 -0
- package/dist-web/assets/index-zAi37ZQ4.js +2 -0
- package/dist-web/assets/{web-1-WNRDnP.js → web-DKFeBb_F.js} +1 -1
- package/dist-web/favicon.ico +0 -0
- package/dist-web/index.html +3 -3
- package/package.json +1 -1
- package/dist/chunk-GBQQJJKK.mjs.map +0 -1
- package/dist/chunk-JFSTXSX2.mjs.map +0 -1
- package/dist/chunk-SCE4YL3M.mjs.map +0 -1
- package/dist/chunk-TMZC3SNX.mjs.map +0 -1
- package/dist/update-checker-JIBA7T4U.mjs.map +0 -1
- package/dist-web/assets/DebugToolsPanel.vue_vue_type_script_setup_true_lang-D9mCNaYM.js +0 -182
- package/dist-web/assets/electron-Dbml-SPN.js +0 -1
- package/dist-web/assets/fetch-DcEBAz_3.js +0 -1
- package/dist-web/assets/fetch-DsCNr_M0.js +0 -1
- package/dist-web/assets/index-C2ts-hTr.js +0 -173
- package/dist-web/assets/index-CjnMyejM.css +0 -1
- package/dist-web/assets/index-CsxrXMkt.js +0 -2
- package/dist-web/assets/index-DkownpUj.js +0 -163
- package/dist-web/assets/index-Dtm2oajn.css +0 -1
- package/dist-web/assets/index-LCedyGl2.css +0 -1
- package/dist-web/assets/index-m7v4PP7r.js +0 -11
- /package/dist/{chat-command-BVIG2LCO.mjs.map → chat-command-SKGPS3PG.mjs.map} +0 -0
- /package/dist/{chunk-QKQRV7RV.mjs.map → chunk-6FXGC6HE.mjs.map} +0 -0
- /package/dist/{chunk-6AOEMUCM.mjs.map → chunk-FHLPXRDL.mjs.map} +0 -0
- /package/dist/{chunk-VSOWNKPA.mjs.map → chunk-GFHSSCCR.mjs.map} +0 -0
- /package/dist/{chunk-GTXX72AQ.mjs.map → chunk-MJITS5CE.mjs.map} +0 -0
- /package/dist/{chunk-3XFHIQIW.mjs.map → chunk-UBJHT3BU.mjs.map} +0 -0
- /package/dist/{http-NLFLQDI7.mjs.map → http-CPFUTIUB.mjs.map} +0 -0
- /package/dist/{import-ILK6MGYR.mjs.map → import-UJXKIKYN.mjs.map} +0 -0
- /package/dist/{mcp-FOLR5XJL.mjs.map → mcp-SW4DTCWJ.mjs.map} +0 -0
- /package/dist/{service-KZTHBDQ5.mjs.map → service-KGL3RAIA.mjs.map} +0 -0
- /package/dist/{src-OQN3VE73.mjs.map → src-M3MO3LEL.mjs.map} +0 -0
- /package/dist/{stream-import-F5QI2HTO.mjs.map → stream-import-4VDJZHMY.mjs.map} +0 -0
|
@@ -4,34 +4,34 @@ import { fileURLToPath as __fileURLToPath } from 'url';
|
|
|
4
4
|
const require = __createRequire(import.meta.url);
|
|
5
5
|
const __filename = __fileURLToPath(import.meta.url);
|
|
6
6
|
const __dirname = __pathDirname(__filename);
|
|
7
|
+
import {
|
|
8
|
+
closeServerAiLogger,
|
|
9
|
+
createCliRunAgentStream,
|
|
10
|
+
initServerAiLogger
|
|
11
|
+
} from "./chunk-7LPVQR77.mjs";
|
|
7
12
|
import {
|
|
8
13
|
importData,
|
|
9
14
|
parseFile
|
|
10
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-UBJHT3BU.mjs";
|
|
11
16
|
import {
|
|
12
17
|
analyzeIncrementalImport,
|
|
13
18
|
analyzeNewImport,
|
|
14
19
|
incrementalImport,
|
|
15
20
|
streamImport
|
|
16
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-6FXGC6HE.mjs";
|
|
22
|
+
import {
|
|
23
|
+
AGENT_TOOL_REGISTRY,
|
|
24
|
+
CoreDataProvider
|
|
25
|
+
} from "./chunk-M6ABWESL.mjs";
|
|
17
26
|
import {
|
|
18
27
|
assertCliDataDirCompatible
|
|
19
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-FHLPXRDL.mjs";
|
|
20
29
|
import {
|
|
21
30
|
getVersion
|
|
22
31
|
} from "./chunk-5ADKYYNA.mjs";
|
|
23
32
|
import {
|
|
24
33
|
resolveCliPath
|
|
25
34
|
} from "./chunk-FJ35V3TE.mjs";
|
|
26
|
-
import {
|
|
27
|
-
closeServerAiLogger,
|
|
28
|
-
createCliRunAgentStream,
|
|
29
|
-
initServerAiLogger
|
|
30
|
-
} from "./chunk-JFSTXSX2.mjs";
|
|
31
|
-
import {
|
|
32
|
-
AGENT_TOOL_REGISTRY,
|
|
33
|
-
CoreDataProvider
|
|
34
|
-
} from "./chunk-SCE4YL3M.mjs";
|
|
35
35
|
import {
|
|
36
36
|
AIChatManager,
|
|
37
37
|
ALL_MIGRATIONS,
|
|
@@ -60,6 +60,7 @@ import {
|
|
|
60
60
|
createDatabaseManagerAdapter,
|
|
61
61
|
createJiebaNlpProvider,
|
|
62
62
|
createNodeDataDirSwitch,
|
|
63
|
+
createSemanticIndexService,
|
|
63
64
|
deleteDict,
|
|
64
65
|
detectAllFormats,
|
|
65
66
|
detectFormat,
|
|
@@ -74,29 +75,41 @@ import {
|
|
|
74
75
|
getAvailableYears,
|
|
75
76
|
getBuiltinModelsByProvider,
|
|
76
77
|
getCatchphraseAnalysis,
|
|
78
|
+
getCheckInAnalysis,
|
|
77
79
|
getClusterGraph,
|
|
78
80
|
getDailyActivity,
|
|
81
|
+
getDbFileVersion,
|
|
79
82
|
getDefaultNodeUserDataDir,
|
|
80
83
|
getDefaultRulesForLocale,
|
|
81
84
|
getDictList,
|
|
85
|
+
getDivingAnalysis,
|
|
86
|
+
getDragonKingAnalysis,
|
|
82
87
|
getHourlyActivity,
|
|
83
88
|
getLanguagePreferenceAnalysis,
|
|
84
89
|
getLaughAnalysis,
|
|
90
|
+
getLongMessageCount,
|
|
85
91
|
getMemberActivity,
|
|
92
|
+
getMemberMonthlyTrend,
|
|
86
93
|
getMembers,
|
|
87
94
|
getMembersDetailed,
|
|
95
|
+
getMemeBattleAnalysis,
|
|
88
96
|
getMentionAnalysis,
|
|
89
97
|
getMentionGraph,
|
|
90
98
|
getMessageLengthDistribution,
|
|
91
99
|
getMessageTypeStats,
|
|
92
100
|
getMonthlyActivity,
|
|
101
|
+
getNightOwlAnalysis,
|
|
102
|
+
getOrComputeAnalysisCache,
|
|
93
103
|
getPendingNodeDataDirMigration,
|
|
94
104
|
getRelationshipStats,
|
|
105
|
+
getRepeatAnalysis,
|
|
95
106
|
getSchemaDetailed,
|
|
96
107
|
getSessionInfo,
|
|
97
108
|
getSessionMeta,
|
|
98
109
|
getSessionOverview,
|
|
99
110
|
getSupportedFormats,
|
|
111
|
+
getTextLengthPercentiles,
|
|
112
|
+
getTextStats,
|
|
100
113
|
getTimeRange,
|
|
101
114
|
getWeekdayActivity,
|
|
102
115
|
getYearlyActivity,
|
|
@@ -123,10 +136,10 @@ import {
|
|
|
123
136
|
verifyCliDataPath,
|
|
124
137
|
writeAuthProfile,
|
|
125
138
|
writeConfigField
|
|
126
|
-
} from "./chunk-
|
|
139
|
+
} from "./chunk-KCFVHVO4.mjs";
|
|
127
140
|
|
|
128
141
|
// src/http/index.ts
|
|
129
|
-
import * as
|
|
142
|
+
import * as fs11 from "fs";
|
|
130
143
|
import * as crypto4 from "crypto";
|
|
131
144
|
|
|
132
145
|
// src/http/server.ts
|
|
@@ -451,13 +464,19 @@ function registerSessionRoutes(server2, ctx) {
|
|
|
451
464
|
return preferencesInstance;
|
|
452
465
|
};
|
|
453
466
|
server2.get("/_web/sessions", async () => {
|
|
454
|
-
|
|
467
|
+
const aiChatCounts = ctx.aiChatManager?.getAIChatCountsBySession();
|
|
468
|
+
return session_service_exports.listAnalysisSessions(adapter, {
|
|
469
|
+
enrichSession: aiChatCounts?.size ? (dto) => ({ ...dto, aiConversationCount: aiChatCounts.get(dto.id) ?? 0 }) : void 0
|
|
470
|
+
});
|
|
455
471
|
});
|
|
456
472
|
server2.get("/_web/sessions/:id", async (request) => {
|
|
457
473
|
const session = session_service_exports.getAnalysisSession(adapter, request.params.id);
|
|
458
474
|
if (!session) {
|
|
459
475
|
throw Object.assign(new Error(`Session not found: ${request.params.id}`), { statusCode: 404 });
|
|
460
476
|
}
|
|
477
|
+
if (ctx.aiChatManager) {
|
|
478
|
+
session.aiConversationCount = ctx.aiChatManager.getAIChats(request.params.id).length;
|
|
479
|
+
}
|
|
461
480
|
return session;
|
|
462
481
|
});
|
|
463
482
|
server2.delete("/_web/sessions/:id", async (request, reply) => {
|
|
@@ -589,6 +608,10 @@ function registerPreferencesRoutes(server2, ctx) {
|
|
|
589
608
|
});
|
|
590
609
|
}
|
|
591
610
|
|
|
611
|
+
// ../../packages/http-routes/src/routes/web/analytics.ts
|
|
612
|
+
import * as fs from "fs";
|
|
613
|
+
import * as path2 from "path";
|
|
614
|
+
|
|
592
615
|
// ../../packages/http-routes/src/helpers.ts
|
|
593
616
|
function parseTimeFilter(query) {
|
|
594
617
|
const { startTs, endTs, memberId } = query;
|
|
@@ -600,9 +623,32 @@ function parseTimeFilter(query) {
|
|
|
600
623
|
return filter;
|
|
601
624
|
}
|
|
602
625
|
|
|
626
|
+
// ../../packages/http-routes/src/analytics-cache.ts
|
|
627
|
+
import * as path from "path";
|
|
628
|
+
function canonical(value) {
|
|
629
|
+
if (value === void 0) return "null";
|
|
630
|
+
if (value === null || typeof value !== "object") return JSON.stringify(value);
|
|
631
|
+
if (Array.isArray(value)) return `[${value.map(canonical).join(",")}]`;
|
|
632
|
+
const obj = value;
|
|
633
|
+
const keys = Object.keys(obj).filter((k) => obj[k] !== void 0).sort();
|
|
634
|
+
return `{${keys.map((k) => `${JSON.stringify(k)}:${canonical(obj[k])}`).join(",")}}`;
|
|
635
|
+
}
|
|
636
|
+
function buildAnalyticsCacheKey(namespace, params) {
|
|
637
|
+
return `${namespace}:${canonical(params)}`;
|
|
638
|
+
}
|
|
639
|
+
function withAnalyticsCache(ctx, sessionId, namespace, params, compute, options) {
|
|
640
|
+
const queryCacheDir = path.join(ctx.pathProvider.getCacheDir(), "query");
|
|
641
|
+
const dateStr = options?.dailyInvalidate ? `|date:${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}` : "";
|
|
642
|
+
const extraStr = options?.extraVersion ? `|${options.extraVersion}` : "";
|
|
643
|
+
const version = `${ctx.getVersion()}|${getDbFileVersion(ctx.sessionAdapter.getDbPath(sessionId))}${dateStr}${extraStr}`;
|
|
644
|
+
const key = buildAnalyticsCacheKey(namespace, params);
|
|
645
|
+
return getOrComputeAnalysisCache(sessionId, key, queryCacheDir, version, compute);
|
|
646
|
+
}
|
|
647
|
+
|
|
603
648
|
// ../../packages/http-routes/src/routes/web/analytics.ts
|
|
604
649
|
function registerAnalyticsRoutes(server2, ctx) {
|
|
605
650
|
const { sessionAdapter: adapter } = ctx;
|
|
651
|
+
const cached = (name, sessionId, params, compute, options) => withAnalyticsCache(ctx, sessionId, `analytics.${name}`, params, compute, options);
|
|
606
652
|
server2.get("/_web/sessions/:id/years", async (request) => {
|
|
607
653
|
const db = adapter.ensureReadonly(request.params.id);
|
|
608
654
|
return getAvailableYears(db);
|
|
@@ -614,111 +660,252 @@ function registerAnalyticsRoutes(server2, ctx) {
|
|
|
614
660
|
server2.get(
|
|
615
661
|
"/_web/sessions/:id/stats/member-activity",
|
|
616
662
|
async (request) => {
|
|
617
|
-
const
|
|
618
|
-
|
|
663
|
+
const id = request.params.id;
|
|
664
|
+
const filter = parseTimeFilter(request.query);
|
|
665
|
+
return cached("member-activity", id, { ...filter }, () => getMemberActivity(adapter.ensureReadonly(id), filter));
|
|
619
666
|
}
|
|
620
667
|
);
|
|
621
668
|
server2.get(
|
|
622
669
|
"/_web/sessions/:id/stats/hourly",
|
|
623
670
|
async (request) => {
|
|
624
|
-
const
|
|
625
|
-
|
|
671
|
+
const id = request.params.id;
|
|
672
|
+
const filter = parseTimeFilter(request.query);
|
|
673
|
+
return cached("hourly", id, { ...filter }, () => getHourlyActivity(adapter.ensureReadonly(id), filter));
|
|
626
674
|
}
|
|
627
675
|
);
|
|
628
676
|
server2.get(
|
|
629
677
|
"/_web/sessions/:id/stats/daily",
|
|
630
678
|
async (request) => {
|
|
631
|
-
const
|
|
632
|
-
|
|
679
|
+
const id = request.params.id;
|
|
680
|
+
const filter = parseTimeFilter(request.query);
|
|
681
|
+
return cached("daily", id, { ...filter }, () => getDailyActivity(adapter.ensureReadonly(id), filter));
|
|
633
682
|
}
|
|
634
683
|
);
|
|
635
684
|
server2.get(
|
|
636
685
|
"/_web/sessions/:id/stats/weekday",
|
|
637
686
|
async (request) => {
|
|
638
|
-
const
|
|
639
|
-
|
|
687
|
+
const id = request.params.id;
|
|
688
|
+
const filter = parseTimeFilter(request.query);
|
|
689
|
+
return cached("weekday", id, { ...filter }, () => getWeekdayActivity(adapter.ensureReadonly(id), filter));
|
|
640
690
|
}
|
|
641
691
|
);
|
|
642
692
|
server2.get(
|
|
643
693
|
"/_web/sessions/:id/stats/message-types",
|
|
644
694
|
async (request) => {
|
|
645
|
-
const
|
|
646
|
-
|
|
695
|
+
const id = request.params.id;
|
|
696
|
+
const filter = parseTimeFilter(request.query);
|
|
697
|
+
return cached("message-types", id, { ...filter }, () => getMessageTypeStats(adapter.ensureReadonly(id), filter));
|
|
647
698
|
}
|
|
648
699
|
);
|
|
649
700
|
server2.get(
|
|
650
701
|
"/_web/sessions/:id/analytics/relationship",
|
|
651
702
|
async (request) => {
|
|
652
|
-
const
|
|
653
|
-
|
|
703
|
+
const id = request.params.id;
|
|
704
|
+
const filter = parseTimeFilter(request.query);
|
|
705
|
+
return cached("relationship", id, { ...filter }, () => getRelationshipStats(adapter.ensureReadonly(id), filter));
|
|
654
706
|
}
|
|
655
707
|
);
|
|
656
708
|
server2.get(
|
|
657
709
|
"/_web/sessions/:id/analytics/catchphrase",
|
|
658
710
|
async (request) => {
|
|
659
|
-
const
|
|
660
|
-
|
|
711
|
+
const id = request.params.id;
|
|
712
|
+
const filter = parseTimeFilter(request.query);
|
|
713
|
+
return cached("catchphrase", id, { ...filter }, () => getCatchphraseAnalysis(adapter.ensureReadonly(id), filter));
|
|
661
714
|
}
|
|
662
715
|
);
|
|
663
716
|
server2.get(
|
|
664
717
|
"/_web/sessions/:id/analytics/mention",
|
|
665
718
|
async (request) => {
|
|
666
|
-
const
|
|
667
|
-
|
|
719
|
+
const id = request.params.id;
|
|
720
|
+
const filter = parseTimeFilter(request.query);
|
|
721
|
+
return cached("mention", id, { ...filter }, () => getMentionAnalysis(adapter.ensureReadonly(id), filter));
|
|
668
722
|
}
|
|
669
723
|
);
|
|
670
724
|
server2.get(
|
|
671
725
|
"/_web/sessions/:id/analytics/mention-graph",
|
|
672
726
|
async (request) => {
|
|
673
|
-
const
|
|
674
|
-
|
|
727
|
+
const id = request.params.id;
|
|
728
|
+
const filter = parseTimeFilter(request.query);
|
|
729
|
+
return cached("mention-graph", id, { ...filter }, () => getMentionGraph(adapter.ensureReadonly(id), filter));
|
|
675
730
|
}
|
|
676
731
|
);
|
|
677
732
|
server2.get(
|
|
678
733
|
"/_web/sessions/:id/analytics/laugh",
|
|
679
734
|
async (request) => {
|
|
680
|
-
const
|
|
681
|
-
return getLaughAnalysis(db, parseTimeFilter(request.query));
|
|
682
|
-
}
|
|
683
|
-
);
|
|
684
|
-
server2.get(
|
|
685
|
-
"/_web/sessions/:id/analytics/cluster",
|
|
686
|
-
async (request) => {
|
|
687
|
-
const db = adapter.ensureReadonly(request.params.id);
|
|
735
|
+
const id = request.params.id;
|
|
688
736
|
const filter = parseTimeFilter(request.query);
|
|
689
|
-
|
|
690
|
-
return getClusterGraph(db, filter, topEdges ? { topEdges } : void 0);
|
|
737
|
+
return cached("laugh", id, { ...filter }, () => getLaughAnalysis(adapter.ensureReadonly(id), filter));
|
|
691
738
|
}
|
|
692
739
|
);
|
|
740
|
+
server2.get("/_web/sessions/:id/analytics/cluster", async (request) => {
|
|
741
|
+
const id = request.params.id;
|
|
742
|
+
const filter = parseTimeFilter(request.query);
|
|
743
|
+
const options = {};
|
|
744
|
+
if (request.query.topEdges) options.topEdges = parseInt(request.query.topEdges, 10);
|
|
745
|
+
if (request.query.lookAhead) options.lookAhead = parseInt(request.query.lookAhead, 10);
|
|
746
|
+
if (request.query.decaySeconds) options.decaySeconds = parseInt(request.query.decaySeconds, 10);
|
|
747
|
+
const hasOptions = Object.keys(options).length > 0;
|
|
748
|
+
return cached(
|
|
749
|
+
"cluster",
|
|
750
|
+
id,
|
|
751
|
+
{ ...filter, ...options },
|
|
752
|
+
() => getClusterGraph(adapter.ensureReadonly(id), filter, hasOptions ? options : void 0)
|
|
753
|
+
);
|
|
754
|
+
});
|
|
693
755
|
server2.get(
|
|
694
756
|
"/_web/sessions/:id/analytics/language-preference",
|
|
695
757
|
async (request) => {
|
|
696
|
-
const
|
|
758
|
+
const id = request.params.id;
|
|
697
759
|
const filter = parseTimeFilter(request.query);
|
|
698
760
|
const locale = request.query.locale || "zh-CN";
|
|
699
|
-
const
|
|
700
|
-
|
|
761
|
+
const zhDictPath = path2.join(ctx.pathProvider.getSystemDir(), "nlp", "zh-CN.dict");
|
|
762
|
+
let zhDictVersion;
|
|
763
|
+
try {
|
|
764
|
+
const st = fs.statSync(zhDictPath);
|
|
765
|
+
zhDictVersion = `${Math.floor(st.mtimeMs)}:${st.size}`;
|
|
766
|
+
} catch {
|
|
767
|
+
zhDictVersion = "-";
|
|
768
|
+
}
|
|
769
|
+
return cached(
|
|
770
|
+
"language-preference",
|
|
771
|
+
id,
|
|
772
|
+
{ ...filter, locale },
|
|
773
|
+
() => getLanguagePreferenceAnalysis(adapter.ensureReadonly(id), {
|
|
774
|
+
locale,
|
|
775
|
+
timeFilter: filter,
|
|
776
|
+
nlpProvider: createJiebaNlpProvider()
|
|
777
|
+
}),
|
|
778
|
+
{ extraVersion: `dict:${zhDictVersion}` }
|
|
779
|
+
);
|
|
701
780
|
}
|
|
702
781
|
);
|
|
703
782
|
server2.get(
|
|
704
783
|
"/_web/sessions/:id/analytics/monthly-activity",
|
|
705
784
|
async (request) => {
|
|
706
|
-
const
|
|
707
|
-
|
|
785
|
+
const id = request.params.id;
|
|
786
|
+
const filter = parseTimeFilter(request.query);
|
|
787
|
+
return cached("monthly-activity", id, { ...filter }, () => getMonthlyActivity(adapter.ensureReadonly(id), filter));
|
|
708
788
|
}
|
|
709
789
|
);
|
|
710
790
|
server2.get(
|
|
711
791
|
"/_web/sessions/:id/analytics/yearly-activity",
|
|
712
792
|
async (request) => {
|
|
713
|
-
const
|
|
714
|
-
|
|
793
|
+
const id = request.params.id;
|
|
794
|
+
const filter = parseTimeFilter(request.query);
|
|
795
|
+
return cached("yearly-activity", id, { ...filter }, () => getYearlyActivity(adapter.ensureReadonly(id), filter));
|
|
715
796
|
}
|
|
716
797
|
);
|
|
717
798
|
server2.get(
|
|
718
799
|
"/_web/sessions/:id/analytics/message-length-distribution",
|
|
719
800
|
async (request) => {
|
|
720
|
-
const
|
|
721
|
-
|
|
801
|
+
const id = request.params.id;
|
|
802
|
+
const filter = parseTimeFilter(request.query);
|
|
803
|
+
return cached(
|
|
804
|
+
"message-length-distribution",
|
|
805
|
+
id,
|
|
806
|
+
{ ...filter },
|
|
807
|
+
() => getMessageLengthDistribution(adapter.ensureReadonly(id), filter)
|
|
808
|
+
);
|
|
809
|
+
}
|
|
810
|
+
);
|
|
811
|
+
server2.get(
|
|
812
|
+
"/_web/sessions/:id/analytics/text-stats",
|
|
813
|
+
async (request) => {
|
|
814
|
+
const id = request.params.id;
|
|
815
|
+
const filter = parseTimeFilter(request.query);
|
|
816
|
+
return cached("text-stats", id, { ...filter }, () => getTextStats(adapter.ensureReadonly(id), filter));
|
|
817
|
+
}
|
|
818
|
+
);
|
|
819
|
+
server2.get(
|
|
820
|
+
"/_web/sessions/:id/analytics/long-message-count",
|
|
821
|
+
async (request) => {
|
|
822
|
+
const id = request.params.id;
|
|
823
|
+
const filter = parseTimeFilter(request.query);
|
|
824
|
+
const minLength = request.query.minLength ? parseInt(request.query.minLength, 10) : void 0;
|
|
825
|
+
return cached(
|
|
826
|
+
"long-message-count",
|
|
827
|
+
id,
|
|
828
|
+
{ ...filter, minLength },
|
|
829
|
+
() => getLongMessageCount(adapter.ensureReadonly(id), filter, minLength)
|
|
830
|
+
);
|
|
831
|
+
}
|
|
832
|
+
);
|
|
833
|
+
server2.get(
|
|
834
|
+
"/_web/sessions/:id/analytics/member-monthly-trend",
|
|
835
|
+
async (request) => {
|
|
836
|
+
const id = request.params.id;
|
|
837
|
+
const filter = parseTimeFilter(request.query);
|
|
838
|
+
return cached(
|
|
839
|
+
"member-monthly-trend",
|
|
840
|
+
id,
|
|
841
|
+
{ ...filter },
|
|
842
|
+
() => getMemberMonthlyTrend(adapter.ensureReadonly(id), filter)
|
|
843
|
+
);
|
|
844
|
+
}
|
|
845
|
+
);
|
|
846
|
+
server2.get(
|
|
847
|
+
"/_web/sessions/:id/analytics/text-length-percentiles",
|
|
848
|
+
async (request) => {
|
|
849
|
+
const id = request.params.id;
|
|
850
|
+
const filter = parseTimeFilter(request.query);
|
|
851
|
+
return cached(
|
|
852
|
+
"text-length-percentiles",
|
|
853
|
+
id,
|
|
854
|
+
{ ...filter },
|
|
855
|
+
() => getTextLengthPercentiles(adapter.ensureReadonly(id), filter)
|
|
856
|
+
);
|
|
857
|
+
}
|
|
858
|
+
);
|
|
859
|
+
server2.get(
|
|
860
|
+
"/_web/sessions/:id/analytics/dragon-king",
|
|
861
|
+
async (request) => {
|
|
862
|
+
const id = request.params.id;
|
|
863
|
+
const filter = parseTimeFilter(request.query);
|
|
864
|
+
return cached("dragon-king", id, { ...filter }, () => getDragonKingAnalysis(adapter.ensureReadonly(id), filter));
|
|
865
|
+
}
|
|
866
|
+
);
|
|
867
|
+
server2.get(
|
|
868
|
+
"/_web/sessions/:id/analytics/diving",
|
|
869
|
+
async (request) => {
|
|
870
|
+
const id = request.params.id;
|
|
871
|
+
const filter = parseTimeFilter(request.query);
|
|
872
|
+
return cached("diving", id, { ...filter }, () => getDivingAnalysis(adapter.ensureReadonly(id), filter), {
|
|
873
|
+
dailyInvalidate: true
|
|
874
|
+
});
|
|
875
|
+
}
|
|
876
|
+
);
|
|
877
|
+
server2.get(
|
|
878
|
+
"/_web/sessions/:id/analytics/check-in",
|
|
879
|
+
async (request) => {
|
|
880
|
+
const id = request.params.id;
|
|
881
|
+
const filter = parseTimeFilter(request.query);
|
|
882
|
+
return cached("check-in", id, { ...filter }, () => getCheckInAnalysis(adapter.ensureReadonly(id), filter));
|
|
883
|
+
}
|
|
884
|
+
);
|
|
885
|
+
server2.get(
|
|
886
|
+
"/_web/sessions/:id/analytics/meme-battle",
|
|
887
|
+
async (request) => {
|
|
888
|
+
const id = request.params.id;
|
|
889
|
+
const filter = parseTimeFilter(request.query);
|
|
890
|
+
return cached("meme-battle", id, { ...filter }, () => getMemeBattleAnalysis(adapter.ensureReadonly(id), filter));
|
|
891
|
+
}
|
|
892
|
+
);
|
|
893
|
+
server2.get(
|
|
894
|
+
"/_web/sessions/:id/analytics/night-owl",
|
|
895
|
+
async (request) => {
|
|
896
|
+
const id = request.params.id;
|
|
897
|
+
const filter = parseTimeFilter(request.query);
|
|
898
|
+
return cached("night-owl", id, { ...filter }, () => getNightOwlAnalysis(adapter.ensureReadonly(id), filter), {
|
|
899
|
+
dailyInvalidate: true
|
|
900
|
+
});
|
|
901
|
+
}
|
|
902
|
+
);
|
|
903
|
+
server2.get(
|
|
904
|
+
"/_web/sessions/:id/analytics/repeat",
|
|
905
|
+
async (request) => {
|
|
906
|
+
const id = request.params.id;
|
|
907
|
+
const filter = parseTimeFilter(request.query);
|
|
908
|
+
return cached("repeat", id, { ...filter }, () => getRepeatAnalysis(adapter.ensureReadonly(id), filter));
|
|
722
909
|
}
|
|
723
910
|
);
|
|
724
911
|
}
|
|
@@ -823,9 +1010,10 @@ function registerExportRoutes(server2, ctx) {
|
|
|
823
1010
|
}
|
|
824
1011
|
|
|
825
1012
|
// ../../packages/http-routes/src/routes/web/nlp.ts
|
|
826
|
-
import * as
|
|
1013
|
+
import * as fs2 from "fs";
|
|
1014
|
+
import * as path3 from "path";
|
|
827
1015
|
function registerNlpRoutes(server2, ctx) {
|
|
828
|
-
const nlpDir =
|
|
1016
|
+
const nlpDir = path3.join(ctx.pathProvider.getSystemDir(), "nlp");
|
|
829
1017
|
initNlpDir(nlpDir);
|
|
830
1018
|
ensureDefaultDict(nlpDir).catch((err) => console.warn("[NLP] Auto-download zh-CN dict failed:", err));
|
|
831
1019
|
server2.get("/_web/nlp/pos-tags", async () => {
|
|
@@ -849,7 +1037,19 @@ function registerNlpRoutes(server2, ctx) {
|
|
|
849
1037
|
if (!db) {
|
|
850
1038
|
throw Object.assign(new Error(`Session not found: ${params.sessionId}`), { statusCode: 404 });
|
|
851
1039
|
}
|
|
852
|
-
|
|
1040
|
+
const { sessionId, ...keyParams } = params;
|
|
1041
|
+
const effectiveDictId = !params.dictType || params.dictType === "default" ? "zh-CN" : String(params.dictType);
|
|
1042
|
+
const dictFilePath = path3.join(nlpDir, `${effectiveDictId}.dict`);
|
|
1043
|
+
let dictVersion;
|
|
1044
|
+
try {
|
|
1045
|
+
const st = fs2.statSync(dictFilePath);
|
|
1046
|
+
dictVersion = `${Math.floor(st.mtimeMs)}:${st.size}`;
|
|
1047
|
+
} catch {
|
|
1048
|
+
dictVersion = "-";
|
|
1049
|
+
}
|
|
1050
|
+
return withAnalyticsCache(ctx, sessionId, "nlp.word-frequency", keyParams, () => computeWordFrequency(db, params), {
|
|
1051
|
+
extraVersion: `dict:${dictVersion}`
|
|
1052
|
+
});
|
|
853
1053
|
});
|
|
854
1054
|
server2.post(
|
|
855
1055
|
"/_web/nlp/segment",
|
|
@@ -1202,17 +1402,21 @@ function registerAiToolRoutes(server2, ctx) {
|
|
|
1202
1402
|
if (!entry) {
|
|
1203
1403
|
return reply.code(404).send({ success: false, error: `Tool not found: ${toolName}` });
|
|
1204
1404
|
}
|
|
1205
|
-
const db = ctx.dbManager.open(sessionId);
|
|
1206
|
-
if (!db) {
|
|
1207
|
-
return reply.code(404).send({ success: false, error: `Session not found: ${sessionId}` });
|
|
1208
|
-
}
|
|
1209
1405
|
const abortController = new AbortController();
|
|
1210
1406
|
activeToolTests.set(testId, abortController);
|
|
1211
1407
|
try {
|
|
1408
|
+
if (ctx.executeAiTool) {
|
|
1409
|
+
return await ctx.executeAiTool({ testId, toolName, params, sessionId, abortSignal: abortController.signal });
|
|
1410
|
+
}
|
|
1411
|
+
const db = ctx.dbManager.open(sessionId);
|
|
1412
|
+
if (!db) {
|
|
1413
|
+
return reply.code(404).send({ success: false, error: `Session not found: ${sessionId}` });
|
|
1414
|
+
}
|
|
1212
1415
|
const execCtx = {
|
|
1213
1416
|
sessionId,
|
|
1214
1417
|
db,
|
|
1215
|
-
dataProvider: new CoreDataProvider(db)
|
|
1418
|
+
dataProvider: new CoreDataProvider(db),
|
|
1419
|
+
abortSignal: abortController.signal
|
|
1216
1420
|
};
|
|
1217
1421
|
const startTime = Date.now();
|
|
1218
1422
|
const result = await entry.handler(params, execCtx);
|
|
@@ -1413,10 +1617,64 @@ function registerAiSummaryRoutes(server2, ctx) {
|
|
|
1413
1617
|
});
|
|
1414
1618
|
}
|
|
1415
1619
|
|
|
1620
|
+
// ../../packages/http-routes/src/routes/web/ai-semantic-index.ts
|
|
1621
|
+
function registerSemanticIndexRoutes(server2, ctx) {
|
|
1622
|
+
const service = ctx.semanticIndexService;
|
|
1623
|
+
if (!service) return;
|
|
1624
|
+
server2.get("/_web/ai/semantic-index/config", async () => {
|
|
1625
|
+
return { config: service.getConfig(), apiKeySet: service.hasApiKey(), configured: service.isConfigured() };
|
|
1626
|
+
});
|
|
1627
|
+
server2.put(
|
|
1628
|
+
"/_web/ai/semantic-index/config",
|
|
1629
|
+
async (request) => {
|
|
1630
|
+
const config = service.setConfig(request.body.config, { apiKey: request.body.apiKey });
|
|
1631
|
+
return { config, apiKeySet: service.hasApiKey(), configured: service.isConfigured() };
|
|
1632
|
+
}
|
|
1633
|
+
);
|
|
1634
|
+
server2.get("/_web/ai/semantic-index/enabled", async () => {
|
|
1635
|
+
return { sessions: service.listEnabledStatuses() };
|
|
1636
|
+
});
|
|
1637
|
+
server2.get("/_web/ai/semantic-index/status", async (request) => {
|
|
1638
|
+
return { status: service.status(request.query.sessionId) };
|
|
1639
|
+
});
|
|
1640
|
+
server2.post("/_web/ai/semantic-index/status", async (request) => {
|
|
1641
|
+
return { sessions: service.statusForSessions(request.body.sessionIds ?? []) };
|
|
1642
|
+
});
|
|
1643
|
+
const sessionAction = (path12, action) => {
|
|
1644
|
+
server2.post(path12, async (request, reply) => {
|
|
1645
|
+
const { sessionId } = request.body;
|
|
1646
|
+
if (!sessionId) return reply.code(400).send({ error: "sessionId is required" });
|
|
1647
|
+
await action(sessionId);
|
|
1648
|
+
return { status: service.status(sessionId) };
|
|
1649
|
+
});
|
|
1650
|
+
};
|
|
1651
|
+
sessionAction("/_web/ai/semantic-index/enable", (id) => service.enable(id));
|
|
1652
|
+
sessionAction("/_web/ai/semantic-index/disable", (id) => service.disable(id));
|
|
1653
|
+
sessionAction("/_web/ai/semantic-index/build", (id) => service.build(id));
|
|
1654
|
+
sessionAction("/_web/ai/semantic-index/pause", (id) => service.pause(id));
|
|
1655
|
+
sessionAction("/_web/ai/semantic-index/cancel", (id) => service.cancel(id));
|
|
1656
|
+
sessionAction("/_web/ai/semantic-index/rebuild", (id) => service.rebuild(id));
|
|
1657
|
+
server2.post("/_web/ai/semantic-index/build-pending", async () => {
|
|
1658
|
+
service.buildAllPending();
|
|
1659
|
+
return { sessions: service.listEnabledStatuses() };
|
|
1660
|
+
});
|
|
1661
|
+
server2.post("/_web/ai/semantic-index/cleanup", async () => {
|
|
1662
|
+
return service.cleanupUnused();
|
|
1663
|
+
});
|
|
1664
|
+
server2.post(
|
|
1665
|
+
"/_web/ai/semantic-index/search",
|
|
1666
|
+
async (request, reply) => {
|
|
1667
|
+
const { sessionId, query, finalTopK } = request.body;
|
|
1668
|
+
if (!sessionId || !query) return reply.code(400).send({ error: "sessionId and query are required" });
|
|
1669
|
+
return service.search(sessionId, query, { finalTopK });
|
|
1670
|
+
}
|
|
1671
|
+
);
|
|
1672
|
+
}
|
|
1673
|
+
|
|
1416
1674
|
// ../../packages/http-routes/src/routes/web/merge.ts
|
|
1417
|
-
import * as
|
|
1675
|
+
import * as fs3 from "fs";
|
|
1418
1676
|
import * as os from "os";
|
|
1419
|
-
import * as
|
|
1677
|
+
import * as path4 from "path";
|
|
1420
1678
|
function ensureDb2(ctx, sessionId) {
|
|
1421
1679
|
const db = ctx.dbManager.open(sessionId);
|
|
1422
1680
|
if (!db) throw sessionNotFound(sessionId);
|
|
@@ -1433,31 +1691,31 @@ function registerMergeRoutes(server2, ctx) {
|
|
|
1433
1691
|
if (contentType.includes("multipart/form-data")) {
|
|
1434
1692
|
const data = await request.file();
|
|
1435
1693
|
if (!data) return reply.code(400).send({ error: "No file uploaded" });
|
|
1436
|
-
const tmpDir =
|
|
1437
|
-
const tmpPath =
|
|
1694
|
+
const tmpDir = fs3.mkdtempSync(path4.join(os.tmpdir(), "chatlab-merge-"));
|
|
1695
|
+
const tmpPath = path4.join(tmpDir, data.filename || "upload");
|
|
1438
1696
|
const chunks = [];
|
|
1439
1697
|
for await (const chunk of data.file) {
|
|
1440
1698
|
chunks.push(chunk);
|
|
1441
1699
|
}
|
|
1442
|
-
|
|
1700
|
+
fs3.writeFileSync(tmpPath, Buffer.concat(chunks));
|
|
1443
1701
|
filePath = tmpPath;
|
|
1444
1702
|
cleanupFile = true;
|
|
1445
1703
|
cleanupDir = tmpDir;
|
|
1446
1704
|
} else {
|
|
1447
1705
|
const body = request.body;
|
|
1448
1706
|
if (!body?.filePath) return reply.code(400).send({ error: "Missing filePath in body" });
|
|
1449
|
-
if (!
|
|
1707
|
+
if (!fs3.existsSync(body.filePath)) return reply.code(400).send({ error: "File not found" });
|
|
1450
1708
|
filePath = body.filePath;
|
|
1451
1709
|
}
|
|
1452
1710
|
try {
|
|
1453
1711
|
const result = await streamParseFileInfo(filePath, {
|
|
1454
1712
|
createTempDatabase(sourceFilePath) {
|
|
1455
|
-
return mergeCache.createTempDatabase(
|
|
1713
|
+
return mergeCache.createTempDatabase(path4.basename(sourceFilePath));
|
|
1456
1714
|
},
|
|
1457
1715
|
onProgress() {
|
|
1458
1716
|
}
|
|
1459
1717
|
});
|
|
1460
|
-
const filename =
|
|
1718
|
+
const filename = path4.basename(filePath);
|
|
1461
1719
|
const handle = mergeCache.store(filename, result.tempDbPath);
|
|
1462
1720
|
return {
|
|
1463
1721
|
handle,
|
|
@@ -1471,13 +1729,13 @@ function registerMergeRoutes(server2, ctx) {
|
|
|
1471
1729
|
} finally {
|
|
1472
1730
|
if (cleanupFile) {
|
|
1473
1731
|
try {
|
|
1474
|
-
|
|
1732
|
+
fs3.unlinkSync(filePath);
|
|
1475
1733
|
} catch {
|
|
1476
1734
|
}
|
|
1477
1735
|
}
|
|
1478
1736
|
if (cleanupDir) {
|
|
1479
1737
|
try {
|
|
1480
|
-
|
|
1738
|
+
fs3.rmdirSync(cleanupDir);
|
|
1481
1739
|
} catch {
|
|
1482
1740
|
}
|
|
1483
1741
|
}
|
|
@@ -1526,20 +1784,20 @@ function registerMergeRoutes(server2, ctx) {
|
|
|
1526
1784
|
return reply.code(501).send({ error: "Import not available on this server" });
|
|
1527
1785
|
}
|
|
1528
1786
|
const jsonData = JSON.stringify(merged.chatLabData);
|
|
1529
|
-
const tmpDir =
|
|
1530
|
-
const safeName =
|
|
1531
|
-
const tmpPath =
|
|
1532
|
-
|
|
1787
|
+
const tmpDir = fs3.mkdtempSync(path4.join(os.tmpdir(), "chatlab-merged-"));
|
|
1788
|
+
const safeName = path4.basename(outputName).replace(/[/\\?%*:|"<>]/g, "_") || "merged";
|
|
1789
|
+
const tmpPath = path4.join(tmpDir, `${safeName}.json`);
|
|
1790
|
+
fs3.writeFileSync(tmpPath, jsonData, "utf-8");
|
|
1533
1791
|
try {
|
|
1534
1792
|
const importResult = await streamImport2(dbManager2, tmpPath);
|
|
1535
1793
|
sessionId = importResult.sessionId;
|
|
1536
1794
|
} finally {
|
|
1537
1795
|
try {
|
|
1538
|
-
|
|
1796
|
+
fs3.unlinkSync(tmpPath);
|
|
1539
1797
|
} catch {
|
|
1540
1798
|
}
|
|
1541
1799
|
try {
|
|
1542
|
-
|
|
1800
|
+
fs3.rmdirSync(tmpDir);
|
|
1543
1801
|
} catch {
|
|
1544
1802
|
}
|
|
1545
1803
|
}
|
|
@@ -1613,16 +1871,16 @@ function registerMergeRoutes(server2, ctx) {
|
|
|
1613
1871
|
}
|
|
1614
1872
|
|
|
1615
1873
|
// ../../packages/http-routes/src/routes/web/cache.ts
|
|
1616
|
-
import * as
|
|
1874
|
+
import * as fs4 from "fs";
|
|
1617
1875
|
import * as fsp from "fs/promises";
|
|
1618
|
-
import * as
|
|
1876
|
+
import * as path5 from "path";
|
|
1619
1877
|
async function getDirSize(dirPath) {
|
|
1620
1878
|
let totalSize = 0;
|
|
1621
1879
|
try {
|
|
1622
|
-
if (!
|
|
1880
|
+
if (!fs4.existsSync(dirPath)) return 0;
|
|
1623
1881
|
const files = await fsp.readdir(dirPath, { withFileTypes: true });
|
|
1624
1882
|
for (const file of files) {
|
|
1625
|
-
const filePath =
|
|
1883
|
+
const filePath = path5.join(dirPath, file.name);
|
|
1626
1884
|
if (file.isDirectory()) {
|
|
1627
1885
|
totalSize += await getDirSize(filePath);
|
|
1628
1886
|
} else {
|
|
@@ -1637,10 +1895,10 @@ async function getDirSize(dirPath) {
|
|
|
1637
1895
|
async function getFileCount(dirPath) {
|
|
1638
1896
|
let count = 0;
|
|
1639
1897
|
try {
|
|
1640
|
-
if (!
|
|
1898
|
+
if (!fs4.existsSync(dirPath)) return 0;
|
|
1641
1899
|
const files = await fsp.readdir(dirPath, { withFileTypes: true });
|
|
1642
1900
|
for (const file of files) {
|
|
1643
|
-
const filePath =
|
|
1901
|
+
const filePath = path5.join(dirPath, file.name);
|
|
1644
1902
|
if (file.isDirectory()) {
|
|
1645
1903
|
count += await getFileCount(filePath);
|
|
1646
1904
|
} else {
|
|
@@ -1653,10 +1911,10 @@ async function getFileCount(dirPath) {
|
|
|
1653
1911
|
}
|
|
1654
1912
|
function hasChatLabDatabases(dirPath) {
|
|
1655
1913
|
try {
|
|
1656
|
-
const markerPath =
|
|
1657
|
-
const dbDir =
|
|
1658
|
-
if (!
|
|
1659
|
-
return
|
|
1914
|
+
const markerPath = path5.join(dirPath, ".chatlab");
|
|
1915
|
+
const dbDir = path5.join(dirPath, "databases");
|
|
1916
|
+
if (!fs4.existsSync(markerPath) || !fs4.existsSync(dbDir)) return false;
|
|
1917
|
+
return fs4.readdirSync(dbDir).some((file) => file.endsWith(".db"));
|
|
1660
1918
|
} catch {
|
|
1661
1919
|
return false;
|
|
1662
1920
|
}
|
|
@@ -1702,7 +1960,7 @@ function registerCacheRoutes(server2, ctx) {
|
|
|
1702
1960
|
const results = await Promise.all(
|
|
1703
1961
|
directories.map(async (dir) => {
|
|
1704
1962
|
const [size, fileCount] = await Promise.all([getDirSize(dir.path), getFileCount(dir.path)]);
|
|
1705
|
-
return { ...dir, size, fileCount, exists:
|
|
1963
|
+
return { ...dir, size, fileCount, exists: fs4.existsSync(dir.path) };
|
|
1706
1964
|
})
|
|
1707
1965
|
);
|
|
1708
1966
|
return {
|
|
@@ -1719,10 +1977,10 @@ function registerCacheRoutes(server2, ctx) {
|
|
|
1719
1977
|
};
|
|
1720
1978
|
const dirPath = allowedDirs[cacheId];
|
|
1721
1979
|
if (!dirPath) return { success: false, error: "Not allowed to clear this directory" };
|
|
1722
|
-
if (!
|
|
1980
|
+
if (!fs4.existsSync(dirPath)) return { success: true };
|
|
1723
1981
|
const files = await fsp.readdir(dirPath);
|
|
1724
1982
|
for (const file of files) {
|
|
1725
|
-
const filePath =
|
|
1983
|
+
const filePath = path5.join(dirPath, file);
|
|
1726
1984
|
const stat2 = await fsp.stat(filePath);
|
|
1727
1985
|
if (stat2.isDirectory()) {
|
|
1728
1986
|
await fsp.rm(filePath, { recursive: true });
|
|
@@ -1739,7 +1997,7 @@ function registerCacheRoutes(server2, ctx) {
|
|
|
1739
1997
|
defaultPath: ctx.defaultUserDataDir,
|
|
1740
1998
|
isCustom: ctx.isCustomDataDir ?? false,
|
|
1741
1999
|
canSetDataDir: ctx.canSetDataDir ?? Boolean(ctx.setDataDir),
|
|
1742
|
-
hasLegacyDataAtDefaultDir: Boolean(ctx.defaultUserDataDir) &&
|
|
2000
|
+
hasLegacyDataAtDefaultDir: Boolean(ctx.defaultUserDataDir) && path5.resolve(pp.getUserDataDir()) !== path5.resolve(ctx.defaultUserDataDir ?? "") && hasChatLabDatabases(ctx.defaultUserDataDir ?? ""),
|
|
1743
2001
|
pendingMigration: pending ? {
|
|
1744
2002
|
from: pending.from,
|
|
1745
2003
|
to: pending.to,
|
|
@@ -1756,8 +2014,8 @@ function registerCacheRoutes(server2, ctx) {
|
|
|
1756
2014
|
return ctx.setDataDir(targetPath, migrate);
|
|
1757
2015
|
});
|
|
1758
2016
|
server2.get("/_web/cache/latest-import-log", async () => {
|
|
1759
|
-
const importLogDir =
|
|
1760
|
-
if (!
|
|
2017
|
+
const importLogDir = path5.join(pp.getLogsDir(), "import");
|
|
2018
|
+
if (!fs4.existsSync(importLogDir)) {
|
|
1761
2019
|
return { success: false, error: "Log directory not found" };
|
|
1762
2020
|
}
|
|
1763
2021
|
const files = await fsp.readdir(importLogDir);
|
|
@@ -1767,7 +2025,7 @@ function registerCacheRoutes(server2, ctx) {
|
|
|
1767
2025
|
}
|
|
1768
2026
|
const fileStats = await Promise.all(
|
|
1769
2027
|
logFiles.map(async (f) => {
|
|
1770
|
-
const filePath =
|
|
2028
|
+
const filePath = path5.join(importLogDir, f);
|
|
1771
2029
|
const stat2 = await fsp.stat(filePath);
|
|
1772
2030
|
return { name: f, path: filePath, mtime: stat2.mtime.getTime() };
|
|
1773
2031
|
})
|
|
@@ -1791,11 +2049,11 @@ function registerCacheRoutes(server2, ctx) {
|
|
|
1791
2049
|
const base64Data = dataUrl.replace(/^data:[^,]+,/, "");
|
|
1792
2050
|
buffer = Buffer.from(base64Data, "base64");
|
|
1793
2051
|
}
|
|
1794
|
-
if (!
|
|
1795
|
-
|
|
2052
|
+
if (!fs4.existsSync(downloadsDir)) {
|
|
2053
|
+
fs4.mkdirSync(downloadsDir, { recursive: true });
|
|
1796
2054
|
}
|
|
1797
|
-
const filePath =
|
|
1798
|
-
|
|
2055
|
+
const filePath = path5.join(downloadsDir, filename);
|
|
2056
|
+
fs4.writeFileSync(filePath, buffer);
|
|
1799
2057
|
return { success: true, filePath };
|
|
1800
2058
|
});
|
|
1801
2059
|
server2.post("/_web/cache/open-dir", async (request, reply) => {
|
|
@@ -1811,7 +2069,7 @@ function registerCacheRoutes(server2, ctx) {
|
|
|
1811
2069
|
};
|
|
1812
2070
|
const dirPath = dirPaths[cacheId];
|
|
1813
2071
|
if (!dirPath) return { success: false, error: "Unknown directory" };
|
|
1814
|
-
if (!
|
|
2072
|
+
if (!fs4.existsSync(dirPath)) {
|
|
1815
2073
|
await fsp.mkdir(dirPath, { recursive: true });
|
|
1816
2074
|
}
|
|
1817
2075
|
try {
|
|
@@ -1869,6 +2127,7 @@ function registerSharedRoutes(server2, ctx, options) {
|
|
|
1869
2127
|
registerAiToolRoutes(server2, resolvedCtx);
|
|
1870
2128
|
registerAiChatRoutes(server2, resolvedCtx);
|
|
1871
2129
|
registerAiSummaryRoutes(server2, resolvedCtx);
|
|
2130
|
+
registerSemanticIndexRoutes(server2, resolvedCtx);
|
|
1872
2131
|
registerMergeRoutes(server2, resolvedCtx);
|
|
1873
2132
|
registerCacheRoutes(server2, resolvedCtx);
|
|
1874
2133
|
}
|
|
@@ -1948,19 +2207,19 @@ function createServer() {
|
|
|
1948
2207
|
|
|
1949
2208
|
// src/http/routes/web/index.ts
|
|
1950
2209
|
import * as os3 from "os";
|
|
1951
|
-
import * as
|
|
2210
|
+
import * as path8 from "path";
|
|
1952
2211
|
|
|
1953
2212
|
// src/http/routes/web/import.ts
|
|
1954
|
-
import * as
|
|
2213
|
+
import * as fs6 from "fs";
|
|
1955
2214
|
import * as os2 from "os";
|
|
1956
|
-
import * as
|
|
2215
|
+
import * as path6 from "path";
|
|
1957
2216
|
|
|
1958
2217
|
// src/http/routes/web/helpers.ts
|
|
1959
|
-
import * as
|
|
2218
|
+
import * as fs5 from "fs";
|
|
1960
2219
|
function resolveNativeBinding() {
|
|
1961
2220
|
if (process.versions.electron) return void 0;
|
|
1962
2221
|
const nativePath = resolveCliPath("native/better_sqlite3.node");
|
|
1963
|
-
if (
|
|
2222
|
+
if (fs5.existsSync(nativePath)) return nativePath;
|
|
1964
2223
|
return void 0;
|
|
1965
2224
|
}
|
|
1966
2225
|
|
|
@@ -1969,11 +2228,11 @@ var DEMO_BASE_URL = "https://chatlab.fun/assets/demo";
|
|
|
1969
2228
|
function cleanupTemp(...paths) {
|
|
1970
2229
|
for (const p of paths) {
|
|
1971
2230
|
try {
|
|
1972
|
-
const stat2 =
|
|
2231
|
+
const stat2 = fs6.statSync(p);
|
|
1973
2232
|
if (stat2.isDirectory()) {
|
|
1974
|
-
|
|
2233
|
+
fs6.rmSync(p, { recursive: true, force: true });
|
|
1975
2234
|
} else {
|
|
1976
|
-
|
|
2235
|
+
fs6.unlinkSync(p);
|
|
1977
2236
|
}
|
|
1978
2237
|
} catch {
|
|
1979
2238
|
}
|
|
@@ -1986,14 +2245,14 @@ function registerImportRoutes(server2, dbManager2) {
|
|
|
1986
2245
|
server2.post("/_web/detect-format", async (request, reply) => {
|
|
1987
2246
|
const data = await request.file();
|
|
1988
2247
|
if (!data) return reply.code(400).send({ error: "No file uploaded" });
|
|
1989
|
-
const tmpDir =
|
|
1990
|
-
const tmpPath =
|
|
2248
|
+
const tmpDir = fs6.mkdtempSync(path6.join(os2.tmpdir(), "chatlab-detect-"));
|
|
2249
|
+
const tmpPath = path6.join(tmpDir, data.filename || "upload");
|
|
1991
2250
|
try {
|
|
1992
2251
|
const chunks = [];
|
|
1993
2252
|
for await (const chunk of data.file) {
|
|
1994
2253
|
chunks.push(chunk);
|
|
1995
2254
|
}
|
|
1996
|
-
|
|
2255
|
+
fs6.writeFileSync(tmpPath, Buffer.concat(chunks));
|
|
1997
2256
|
const format = detectFormat(tmpPath);
|
|
1998
2257
|
const allFormats = detectAllFormats(tmpPath);
|
|
1999
2258
|
return { format, allFormats };
|
|
@@ -2004,14 +2263,14 @@ function registerImportRoutes(server2, dbManager2) {
|
|
|
2004
2263
|
server2.post("/_web/scan-multi-chat", async (request, reply) => {
|
|
2005
2264
|
const data = await request.file();
|
|
2006
2265
|
if (!data) return reply.code(400).send({ error: "No file uploaded" });
|
|
2007
|
-
const tmpDir =
|
|
2008
|
-
const tmpPath =
|
|
2266
|
+
const tmpDir = fs6.mkdtempSync(path6.join(os2.tmpdir(), "chatlab-scan-"));
|
|
2267
|
+
const tmpPath = path6.join(tmpDir, data.filename || "upload");
|
|
2009
2268
|
try {
|
|
2010
2269
|
const chunks = [];
|
|
2011
2270
|
for await (const chunk of data.file) {
|
|
2012
2271
|
chunks.push(chunk);
|
|
2013
2272
|
}
|
|
2014
|
-
|
|
2273
|
+
fs6.writeFileSync(tmpPath, Buffer.concat(chunks));
|
|
2015
2274
|
const chats = await scanMultiChatFile(tmpPath);
|
|
2016
2275
|
return { chats };
|
|
2017
2276
|
} finally {
|
|
@@ -2021,13 +2280,13 @@ function registerImportRoutes(server2, dbManager2) {
|
|
|
2021
2280
|
server2.post("/_web/import", async (request, reply) => {
|
|
2022
2281
|
const data = await request.file();
|
|
2023
2282
|
if (!data) return reply.code(400).send({ error: "No file uploaded" });
|
|
2024
|
-
const tmpDir =
|
|
2025
|
-
const tmpPath =
|
|
2283
|
+
const tmpDir = fs6.mkdtempSync(path6.join(os2.tmpdir(), "chatlab-import-"));
|
|
2284
|
+
const tmpPath = path6.join(tmpDir, data.filename || "upload");
|
|
2026
2285
|
const chunks = [];
|
|
2027
2286
|
for await (const chunk of data.file) {
|
|
2028
2287
|
chunks.push(chunk);
|
|
2029
2288
|
}
|
|
2030
|
-
|
|
2289
|
+
fs6.writeFileSync(tmpPath, Buffer.concat(chunks));
|
|
2031
2290
|
const formatId = data.fields?.formatId?.value;
|
|
2032
2291
|
const chatIndexStr = data.fields?.chatIndex?.value;
|
|
2033
2292
|
const chatIndex = chatIndexStr !== void 0 ? parseInt(chatIndexStr, 10) : void 0;
|
|
@@ -2070,7 +2329,7 @@ data: ${JSON.stringify(eventData)}
|
|
|
2070
2329
|
server2.post("/_web/import-directory", async (request, reply) => {
|
|
2071
2330
|
const parts = request.parts();
|
|
2072
2331
|
if (!parts) return reply.code(400).send({ error: "No files uploaded" });
|
|
2073
|
-
const tmpDir =
|
|
2332
|
+
const tmpDir = fs6.mkdtempSync(path6.join(os2.tmpdir(), "chatlab-dir-import-"));
|
|
2074
2333
|
const relativePaths = [];
|
|
2075
2334
|
const fileBuffers = [];
|
|
2076
2335
|
try {
|
|
@@ -2098,10 +2357,10 @@ data: ${JSON.stringify(eventData)}
|
|
|
2098
2357
|
if (segments.length > 1) {
|
|
2099
2358
|
relPath = segments.slice(1).join("/");
|
|
2100
2359
|
}
|
|
2101
|
-
const targetPath =
|
|
2102
|
-
if (!targetPath.startsWith(tmpDir +
|
|
2103
|
-
|
|
2104
|
-
|
|
2360
|
+
const targetPath = path6.resolve(tmpDir, relPath);
|
|
2361
|
+
if (!targetPath.startsWith(tmpDir + path6.sep)) continue;
|
|
2362
|
+
fs6.mkdirSync(path6.dirname(targetPath), { recursive: true });
|
|
2363
|
+
fs6.writeFileSync(targetPath, fileBuffers[i].data);
|
|
2105
2364
|
}
|
|
2106
2365
|
const entryPath = findEntryFileInDirectory(tmpDir);
|
|
2107
2366
|
if (!entryPath) {
|
|
@@ -2146,13 +2405,13 @@ data: ${JSON.stringify({ success: false, error: err instanceof Error ? err.messa
|
|
|
2146
2405
|
const sessionId = request.params.id;
|
|
2147
2406
|
const data = await request.file();
|
|
2148
2407
|
if (!data) return reply.code(400).send({ error: "No file uploaded" });
|
|
2149
|
-
const tmpDir =
|
|
2150
|
-
const tmpPath =
|
|
2408
|
+
const tmpDir = fs6.mkdtempSync(path6.join(os2.tmpdir(), "chatlab-inc-"));
|
|
2409
|
+
const tmpPath = path6.join(tmpDir, data.filename || "upload");
|
|
2151
2410
|
const chunks = [];
|
|
2152
2411
|
for await (const chunk of data.file) {
|
|
2153
2412
|
chunks.push(chunk);
|
|
2154
2413
|
}
|
|
2155
|
-
|
|
2414
|
+
fs6.writeFileSync(tmpPath, Buffer.concat(chunks));
|
|
2156
2415
|
reply.raw.writeHead(200, {
|
|
2157
2416
|
"Content-Type": "text/event-stream",
|
|
2158
2417
|
"Cache-Control": "no-cache",
|
|
@@ -2184,13 +2443,13 @@ data: ${JSON.stringify(eventData)}
|
|
|
2184
2443
|
const sessionId = request.params.id;
|
|
2185
2444
|
const data = await request.file();
|
|
2186
2445
|
if (!data) return reply.code(400).send({ error: "No file uploaded" });
|
|
2187
|
-
const tmpDir =
|
|
2188
|
-
const tmpPath =
|
|
2446
|
+
const tmpDir = fs6.mkdtempSync(path6.join(os2.tmpdir(), "chatlab-analyze-"));
|
|
2447
|
+
const tmpPath = path6.join(tmpDir, data.filename || "upload");
|
|
2189
2448
|
const chunks = [];
|
|
2190
2449
|
for await (const chunk of data.file) {
|
|
2191
2450
|
chunks.push(chunk);
|
|
2192
2451
|
}
|
|
2193
|
-
|
|
2452
|
+
fs6.writeFileSync(tmpPath, Buffer.concat(chunks));
|
|
2194
2453
|
try {
|
|
2195
2454
|
return await analyzeIncrementalImport(dbManager2, sessionId, tmpPath);
|
|
2196
2455
|
} catch (err) {
|
|
@@ -2202,13 +2461,13 @@ data: ${JSON.stringify(eventData)}
|
|
|
2202
2461
|
server2.post("/_web/import/analyze", async (request, reply) => {
|
|
2203
2462
|
const data = await request.file();
|
|
2204
2463
|
if (!data) return reply.code(400).send({ error: "No file uploaded" });
|
|
2205
|
-
const tmpDir =
|
|
2206
|
-
const tmpPath =
|
|
2464
|
+
const tmpDir = fs6.mkdtempSync(path6.join(os2.tmpdir(), "chatlab-analyze-"));
|
|
2465
|
+
const tmpPath = path6.join(tmpDir, data.filename || "upload");
|
|
2207
2466
|
const chunks = [];
|
|
2208
2467
|
for await (const chunk of data.file) {
|
|
2209
2468
|
chunks.push(chunk);
|
|
2210
2469
|
}
|
|
2211
|
-
|
|
2470
|
+
fs6.writeFileSync(tmpPath, Buffer.concat(chunks));
|
|
2212
2471
|
try {
|
|
2213
2472
|
return await analyzeNewImport(tmpPath);
|
|
2214
2473
|
} catch (err) {
|
|
@@ -2238,17 +2497,17 @@ data: ${JSON.stringify(eventData)}
|
|
|
2238
2497
|
|
|
2239
2498
|
`);
|
|
2240
2499
|
}
|
|
2241
|
-
const tmpDir =
|
|
2500
|
+
const tmpDir = fs6.mkdtempSync(path6.join(os2.tmpdir(), "chatlab-demo-"));
|
|
2242
2501
|
try {
|
|
2243
2502
|
const localPaths = [];
|
|
2244
2503
|
for (let i = 0; i < total; i++) {
|
|
2245
2504
|
sendEvent("progress", { stage: "downloading", current: i + 1, total });
|
|
2246
|
-
const localPath =
|
|
2505
|
+
const localPath = path6.join(tmpDir, DEMO_FILES[i]);
|
|
2247
2506
|
const resp = await fetch(`${DEMO_BASE_URL}/${locale}/${DEMO_FILES[i]}`, {
|
|
2248
2507
|
signal: AbortSignal.timeout(6e4)
|
|
2249
2508
|
});
|
|
2250
2509
|
if (!resp.ok) throw new Error(`Download demo failed (${DEMO_FILES[i]}): ${resp.status}`);
|
|
2251
|
-
|
|
2510
|
+
fs6.writeFileSync(localPath, Buffer.from(await resp.arrayBuffer()));
|
|
2252
2511
|
localPaths.push(localPath);
|
|
2253
2512
|
}
|
|
2254
2513
|
sendEvent("progress", { stage: "importing", current: 1, total });
|
|
@@ -2279,7 +2538,7 @@ data: ${JSON.stringify(eventData)}
|
|
|
2279
2538
|
}
|
|
2280
2539
|
|
|
2281
2540
|
// src/http/routes/web/cache.ts
|
|
2282
|
-
import * as
|
|
2541
|
+
import * as path7 from "path";
|
|
2283
2542
|
import { execFile } from "child_process";
|
|
2284
2543
|
function runExecFile(file, args, runner = execFile) {
|
|
2285
2544
|
return new Promise((resolve4, reject) => {
|
|
@@ -2307,7 +2566,7 @@ async function showPathInFolder(filePath, platform = process.platform, runner) {
|
|
|
2307
2566
|
} else if (platform === "win32") {
|
|
2308
2567
|
await runExecFile("explorer.exe", [`/select,${filePath}`], runner);
|
|
2309
2568
|
} else {
|
|
2310
|
-
await runExecFile("xdg-open", [
|
|
2569
|
+
await runExecFile("xdg-open", [path7.dirname(filePath)], runner);
|
|
2311
2570
|
}
|
|
2312
2571
|
}
|
|
2313
2572
|
|
|
@@ -2330,26 +2589,42 @@ function registerWebRoutes(server2, dbManager2, options) {
|
|
|
2330
2589
|
const mergeCache = options?.pathProvider ? new MergeSessionCache(options.pathProvider, { nativeBinding: options.nativeBinding }) : null;
|
|
2331
2590
|
mergeCache?.cleanupOrphans();
|
|
2332
2591
|
const fallbackPathProvider = {
|
|
2333
|
-
getSystemDir: () =>
|
|
2334
|
-
getUserDataDir: () =>
|
|
2335
|
-
getDatabaseDir: () =>
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2592
|
+
getSystemDir: () => path8.join(os3.homedir(), ".chatlab"),
|
|
2593
|
+
getUserDataDir: () => path8.join(os3.homedir(), ".chatlab", "data"),
|
|
2594
|
+
getDatabaseDir: () => path8.join(os3.homedir(), ".chatlab", "data", "databases"),
|
|
2595
|
+
getVectorDir: () => path8.join(os3.homedir(), ".chatlab", "data", "vector"),
|
|
2596
|
+
getAiDataDir: () => path8.join(os3.homedir(), ".chatlab", "ai"),
|
|
2597
|
+
getSettingsDir: () => path8.join(os3.homedir(), ".chatlab", "settings"),
|
|
2598
|
+
getCacheDir: () => path8.join(os3.homedir(), ".chatlab", "cache"),
|
|
2599
|
+
getTempDir: () => path8.join(os3.homedir(), ".chatlab", "temp"),
|
|
2600
|
+
getLogsDir: () => path8.join(os3.homedir(), ".chatlab", "logs"),
|
|
2601
|
+
getDownloadsDir: () => path8.join(os3.homedir(), "Downloads")
|
|
2342
2602
|
};
|
|
2343
2603
|
const resolvedPathProvider = options?.pathProvider ?? fallbackPathProvider;
|
|
2344
2604
|
const ai = options?.aiContext;
|
|
2345
2605
|
const cliStreamImport = async (dm, filePath) => {
|
|
2346
|
-
const { streamImport: streamImport2 } = await import("./stream-import-
|
|
2606
|
+
const { streamImport: streamImport2 } = await import("./stream-import-4VDJZHMY.mjs");
|
|
2347
2607
|
const result = await streamImport2(dm, filePath);
|
|
2348
2608
|
if (!result.sessionId) throw new Error("Import succeeded but no sessionId returned");
|
|
2349
2609
|
return { sessionId: result.sessionId };
|
|
2350
2610
|
};
|
|
2351
2611
|
const defaultUserDataDir = getDefaultNodeUserDataDir();
|
|
2352
|
-
const isCustom =
|
|
2612
|
+
const isCustom = path8.resolve(resolvedPathProvider.getUserDataDir()) !== path8.resolve(defaultUserDataDir);
|
|
2613
|
+
let semanticIndexService = options?.semanticIndexService;
|
|
2614
|
+
if (!semanticIndexService) {
|
|
2615
|
+
try {
|
|
2616
|
+
semanticIndexService = createSemanticIndexService({
|
|
2617
|
+
pathProvider: resolvedPathProvider,
|
|
2618
|
+
sessionAdapter: adapter,
|
|
2619
|
+
nativeBinding: options?.nativeBinding
|
|
2620
|
+
});
|
|
2621
|
+
semanticIndexService.recover();
|
|
2622
|
+
server2.addHook("onClose", async () => semanticIndexService?.close());
|
|
2623
|
+
} catch (err) {
|
|
2624
|
+
console.warn("[semantic-index] service unavailable:", err instanceof Error ? err.message : String(err));
|
|
2625
|
+
semanticIndexService = void 0;
|
|
2626
|
+
}
|
|
2627
|
+
}
|
|
2353
2628
|
registerSharedRoutes(
|
|
2354
2629
|
server2,
|
|
2355
2630
|
{
|
|
@@ -2358,6 +2633,7 @@ function registerWebRoutes(server2, dbManager2, options) {
|
|
|
2358
2633
|
pathProvider: resolvedPathProvider,
|
|
2359
2634
|
getVersion,
|
|
2360
2635
|
nativeBinding: options?.nativeBinding,
|
|
2636
|
+
semanticIndexService,
|
|
2361
2637
|
openDirectory: openDirectoryPath,
|
|
2362
2638
|
showInFolder: showPathInFolder,
|
|
2363
2639
|
downloadsDir: resolvedPathProvider.getDownloadsDir(),
|
|
@@ -2440,31 +2716,31 @@ function registerProxyRoutes(server2) {
|
|
|
2440
2716
|
}
|
|
2441
2717
|
|
|
2442
2718
|
// src/ai/manager-factory.ts
|
|
2443
|
-
import * as
|
|
2444
|
-
import * as
|
|
2719
|
+
import * as fs7 from "fs";
|
|
2720
|
+
import * as path9 from "path";
|
|
2445
2721
|
import { createHash, randomUUID } from "crypto";
|
|
2446
2722
|
var nodeFs = {
|
|
2447
2723
|
ensureDir(dir) {
|
|
2448
|
-
if (!
|
|
2724
|
+
if (!fs7.existsSync(dir)) fs7.mkdirSync(dir, { recursive: true });
|
|
2449
2725
|
},
|
|
2450
2726
|
listFiles(dir, ext) {
|
|
2451
|
-
if (!
|
|
2452
|
-
return
|
|
2727
|
+
if (!fs7.existsSync(dir)) return [];
|
|
2728
|
+
return fs7.readdirSync(dir).filter((f) => f.endsWith(ext));
|
|
2453
2729
|
},
|
|
2454
2730
|
readFile(filePath) {
|
|
2455
|
-
return
|
|
2731
|
+
return fs7.readFileSync(filePath, "utf-8");
|
|
2456
2732
|
},
|
|
2457
2733
|
writeFile(filePath, content) {
|
|
2458
|
-
|
|
2734
|
+
fs7.writeFileSync(filePath, content, "utf-8");
|
|
2459
2735
|
},
|
|
2460
2736
|
deleteFile(filePath) {
|
|
2461
|
-
if (
|
|
2737
|
+
if (fs7.existsSync(filePath)) fs7.unlinkSync(filePath);
|
|
2462
2738
|
},
|
|
2463
2739
|
fileExists(filePath) {
|
|
2464
|
-
return
|
|
2740
|
+
return fs7.existsSync(filePath);
|
|
2465
2741
|
},
|
|
2466
2742
|
joinPath(...parts) {
|
|
2467
|
-
return
|
|
2743
|
+
return path9.join(...parts);
|
|
2468
2744
|
}
|
|
2469
2745
|
};
|
|
2470
2746
|
var managerCache = /* @__PURE__ */ new Map();
|
|
@@ -2474,13 +2750,13 @@ function getAssistantManager(aiDataDir) {
|
|
|
2474
2750
|
cached = {
|
|
2475
2751
|
assistant: new AssistantManager({
|
|
2476
2752
|
fs: nodeFs,
|
|
2477
|
-
assistantsDir:
|
|
2753
|
+
assistantsDir: path9.join(aiDataDir, "assistants"),
|
|
2478
2754
|
builtinRawConfigs: [],
|
|
2479
2755
|
generateId: () => `custom_${randomUUID().replace(/-/g, "").slice(0, 12)}`
|
|
2480
2756
|
}),
|
|
2481
2757
|
skill: new SkillManagerCore({
|
|
2482
2758
|
fs: nodeFs,
|
|
2483
|
-
skillsDir:
|
|
2759
|
+
skillsDir: path9.join(aiDataDir, "skills"),
|
|
2484
2760
|
builtinRawSkills: [],
|
|
2485
2761
|
contentHash: (content) => createHash("md5").update(content).digest("hex")
|
|
2486
2762
|
})
|
|
@@ -2504,8 +2780,8 @@ var noop = () => {
|
|
|
2504
2780
|
var NOOP_LOGGER = { info: noop, warn: noop, error: noop };
|
|
2505
2781
|
|
|
2506
2782
|
// ../../packages/sync/src/data-source-manager.ts
|
|
2507
|
-
import * as
|
|
2508
|
-
import * as
|
|
2783
|
+
import * as fs8 from "fs";
|
|
2784
|
+
import * as path10 from "path";
|
|
2509
2785
|
import * as crypto from "crypto";
|
|
2510
2786
|
var CONFIG_FILE = "data-sources.json";
|
|
2511
2787
|
function generateId(prefix = "ds") {
|
|
@@ -2533,17 +2809,17 @@ var DataSourceManager = class {
|
|
|
2533
2809
|
this.logger = logger ?? NOOP_LOGGER;
|
|
2534
2810
|
}
|
|
2535
2811
|
getConfigPath() {
|
|
2536
|
-
return
|
|
2812
|
+
return path10.join(this.configDir, CONFIG_FILE);
|
|
2537
2813
|
}
|
|
2538
2814
|
ensureDir() {
|
|
2539
|
-
|
|
2815
|
+
fs8.mkdirSync(this.configDir, { recursive: true });
|
|
2540
2816
|
}
|
|
2541
2817
|
// ==================== Load / Save ====================
|
|
2542
2818
|
loadAll() {
|
|
2543
2819
|
try {
|
|
2544
2820
|
const filePath = this.getConfigPath();
|
|
2545
|
-
if (
|
|
2546
|
-
const raw =
|
|
2821
|
+
if (fs8.existsSync(filePath)) {
|
|
2822
|
+
const raw = fs8.readFileSync(filePath, "utf-8");
|
|
2547
2823
|
const parsed = JSON.parse(raw);
|
|
2548
2824
|
if (!isValidDataSourceArray(parsed)) {
|
|
2549
2825
|
this.logger.warn("[DataSource] Incompatible config format detected, returning empty.");
|
|
@@ -2562,7 +2838,7 @@ var DataSourceManager = class {
|
|
|
2562
2838
|
saveAll(sources) {
|
|
2563
2839
|
try {
|
|
2564
2840
|
this.ensureDir();
|
|
2565
|
-
|
|
2841
|
+
fs8.writeFileSync(this.getConfigPath(), JSON.stringify(sources, null, 2), "utf-8");
|
|
2566
2842
|
} catch (err) {
|
|
2567
2843
|
this.logger.error("[DataSource] Failed to save config", err);
|
|
2568
2844
|
}
|
|
@@ -2687,7 +2963,7 @@ function parseRemoteSessionsResponse(body) {
|
|
|
2687
2963
|
}
|
|
2688
2964
|
|
|
2689
2965
|
// ../../packages/sync/src/pull-engine.ts
|
|
2690
|
-
import * as
|
|
2966
|
+
import * as fs9 from "fs";
|
|
2691
2967
|
import * as crypto2 from "crypto";
|
|
2692
2968
|
var MAX_PAGES_PER_PULL = 5e3;
|
|
2693
2969
|
var PULL_OVERLAP_SECONDS = 60;
|
|
@@ -2707,7 +2983,7 @@ function parseSyncFromFile(filePath) {
|
|
|
2707
2983
|
try {
|
|
2708
2984
|
const isJsonl = filePath.endsWith(".jsonl");
|
|
2709
2985
|
if (isJsonl) {
|
|
2710
|
-
const content =
|
|
2986
|
+
const content = fs9.readFileSync(filePath, "utf-8");
|
|
2711
2987
|
const lines = content.trimEnd().split("\n");
|
|
2712
2988
|
for (let i = lines.length - 1; i >= Math.max(0, lines.length - 5); i--) {
|
|
2713
2989
|
try {
|
|
@@ -2726,7 +3002,7 @@ function parseSyncFromFile(filePath) {
|
|
|
2726
3002
|
}
|
|
2727
3003
|
return null;
|
|
2728
3004
|
}
|
|
2729
|
-
const raw =
|
|
3005
|
+
const raw = fs9.readFileSync(filePath, "utf-8");
|
|
2730
3006
|
const parsed = JSON.parse(raw);
|
|
2731
3007
|
if (parsed.sync && typeof parsed.sync === "object") {
|
|
2732
3008
|
const s = parsed.sync;
|
|
@@ -2740,7 +3016,7 @@ function parseSyncFromFile(filePath) {
|
|
|
2740
3016
|
function fileContainsMessages(filePath) {
|
|
2741
3017
|
try {
|
|
2742
3018
|
if (filePath.endsWith(".jsonl")) {
|
|
2743
|
-
const content =
|
|
3019
|
+
const content = fs9.readFileSync(filePath, "utf-8");
|
|
2744
3020
|
for (const line of content.split("\n")) {
|
|
2745
3021
|
const trimmed = line.trim();
|
|
2746
3022
|
if (!trimmed) continue;
|
|
@@ -2753,7 +3029,7 @@ function fileContainsMessages(filePath) {
|
|
|
2753
3029
|
}
|
|
2754
3030
|
return false;
|
|
2755
3031
|
}
|
|
2756
|
-
const raw =
|
|
3032
|
+
const raw = fs9.readFileSync(filePath, "utf-8");
|
|
2757
3033
|
const parsed = JSON.parse(raw);
|
|
2758
3034
|
return Array.isArray(parsed.messages) && parsed.messages.length > 0;
|
|
2759
3035
|
} catch {
|
|
@@ -2762,7 +3038,7 @@ function fileContainsMessages(filePath) {
|
|
|
2762
3038
|
}
|
|
2763
3039
|
function cleanupTempFile(filePath) {
|
|
2764
3040
|
try {
|
|
2765
|
-
if (
|
|
3041
|
+
if (fs9.existsSync(filePath)) fs9.unlinkSync(filePath);
|
|
2766
3042
|
} catch {
|
|
2767
3043
|
}
|
|
2768
3044
|
}
|
|
@@ -2824,7 +3100,7 @@ var PullEngine = class {
|
|
|
2824
3100
|
limit: ds.pullLimit
|
|
2825
3101
|
});
|
|
2826
3102
|
try {
|
|
2827
|
-
const stat2 =
|
|
3103
|
+
const stat2 = fs9.statSync(tempFile);
|
|
2828
3104
|
this.logger.info(`[Pull] "${sess.name}" page ${pageCount}: fetched ${stat2.size} bytes`);
|
|
2829
3105
|
const sync0 = parseSyncFromFile(tempFile);
|
|
2830
3106
|
if (stat2.size < 1024) {
|
|
@@ -2842,7 +3118,7 @@ var PullEngine = class {
|
|
|
2842
3118
|
since,
|
|
2843
3119
|
limit: ds.pullLimit
|
|
2844
3120
|
});
|
|
2845
|
-
const retryStat =
|
|
3121
|
+
const retryStat = fs9.statSync(retryFile);
|
|
2846
3122
|
this.logger.info(`[Pull] "${sess.name}" retry ${ri + 1}: fetched ${retryStat.size} bytes`);
|
|
2847
3123
|
const retrySync = parseSyncFromFile(retryFile);
|
|
2848
3124
|
if (retryStat.size < 1024 && !fileContainsMessages(retryFile) && retrySync?.hasMore === false) {
|
|
@@ -3123,13 +3399,13 @@ function reloadTimer(dsId, skipInitialPull = false) {
|
|
|
3123
3399
|
}
|
|
3124
3400
|
|
|
3125
3401
|
// src/sync/adapters.ts
|
|
3126
|
-
import * as
|
|
3402
|
+
import * as fs10 from "fs";
|
|
3127
3403
|
import * as os4 from "os";
|
|
3128
|
-
import * as
|
|
3404
|
+
import * as path11 from "path";
|
|
3129
3405
|
import * as crypto3 from "crypto";
|
|
3130
3406
|
function getTempFilePath(ext) {
|
|
3131
3407
|
const id = crypto3.randomBytes(8).toString("hex");
|
|
3132
|
-
return
|
|
3408
|
+
return path11.join(os4.tmpdir(), `chatlab-pull-${id}${ext}`);
|
|
3133
3409
|
}
|
|
3134
3410
|
var NodeFetcher = class {
|
|
3135
3411
|
async fetchToTempFile(baseUrl, remoteSessionId, token, params) {
|
|
@@ -3146,7 +3422,7 @@ var NodeFetcher = class {
|
|
|
3146
3422
|
const isJsonl = contentType.includes("ndjson") || contentType.includes("jsonl");
|
|
3147
3423
|
const tempFile = getTempFilePath(isJsonl ? ".jsonl" : ".json");
|
|
3148
3424
|
const buffer = Buffer.from(await response.arrayBuffer());
|
|
3149
|
-
|
|
3425
|
+
fs10.writeFileSync(tempFile, buffer);
|
|
3150
3426
|
return tempFile;
|
|
3151
3427
|
}
|
|
3152
3428
|
};
|
|
@@ -3159,7 +3435,7 @@ var DirectImporter = class {
|
|
|
3159
3435
|
}
|
|
3160
3436
|
sessionExists(sessionId) {
|
|
3161
3437
|
const dbPath = this.dbManager.getDbPath(sessionId);
|
|
3162
|
-
if (!
|
|
3438
|
+
if (!fs10.existsSync(dbPath)) return false;
|
|
3163
3439
|
try {
|
|
3164
3440
|
const db = this.dbManager.openRawSessionDatabase(sessionId, { readonly: true });
|
|
3165
3441
|
const row = db.prepare("SELECT COUNT(*) as cnt FROM sqlite_master WHERE type='table' AND name='message'").get();
|
|
@@ -3167,7 +3443,7 @@ var DirectImporter = class {
|
|
|
3167
3443
|
if (row.cnt === 0) {
|
|
3168
3444
|
this.logger.warn(`[DirectImporter] DB file exists but has no message table: ${sessionId}, removing`);
|
|
3169
3445
|
try {
|
|
3170
|
-
|
|
3446
|
+
fs10.unlinkSync(dbPath);
|
|
3171
3447
|
} catch {
|
|
3172
3448
|
}
|
|
3173
3449
|
return false;
|
|
@@ -3177,7 +3453,7 @@ var DirectImporter = class {
|
|
|
3177
3453
|
if (error instanceof DataDirCompatibilityError) throw error;
|
|
3178
3454
|
this.logger.warn(`[DirectImporter] Cannot validate DB file: ${sessionId}, removing`);
|
|
3179
3455
|
try {
|
|
3180
|
-
|
|
3456
|
+
fs10.unlinkSync(dbPath);
|
|
3181
3457
|
} catch {
|
|
3182
3458
|
}
|
|
3183
3459
|
return false;
|
|
@@ -3382,21 +3658,21 @@ function createFileConfigStorage(aiDataDir) {
|
|
|
3382
3658
|
return {
|
|
3383
3659
|
readJson(key) {
|
|
3384
3660
|
try {
|
|
3385
|
-
return JSON.parse(
|
|
3661
|
+
return JSON.parse(fs11.readFileSync(`${aiDataDir}/${key}.json`, "utf-8"));
|
|
3386
3662
|
} catch {
|
|
3387
3663
|
return null;
|
|
3388
3664
|
}
|
|
3389
3665
|
},
|
|
3390
3666
|
writeJson(key, data) {
|
|
3391
|
-
if (!
|
|
3392
|
-
|
|
3667
|
+
if (!fs11.existsSync(aiDataDir)) fs11.mkdirSync(aiDataDir, { recursive: true });
|
|
3668
|
+
fs11.writeFileSync(`${aiDataDir}/${key}.json`, JSON.stringify(data, null, 2), "utf-8");
|
|
3393
3669
|
}
|
|
3394
3670
|
};
|
|
3395
3671
|
}
|
|
3396
3672
|
function resolveNativeBinding2() {
|
|
3397
3673
|
if (process.versions.electron) return void 0;
|
|
3398
3674
|
const nativePath = resolveCliPath("native/better_sqlite3.node");
|
|
3399
|
-
if (
|
|
3675
|
+
if (fs11.existsSync(nativePath)) return nativePath;
|
|
3400
3676
|
return void 0;
|
|
3401
3677
|
}
|
|
3402
3678
|
function ensureToken(config) {
|
|
@@ -3467,9 +3743,23 @@ async function startHttpServer(options) {
|
|
|
3467
3743
|
limits: { fileSize: 1024 * 1024 * 1024 }
|
|
3468
3744
|
// 1GB
|
|
3469
3745
|
});
|
|
3746
|
+
let semanticIndexService;
|
|
3747
|
+
try {
|
|
3748
|
+
semanticIndexService = createSemanticIndexService({
|
|
3749
|
+
pathProvider,
|
|
3750
|
+
sessionAdapter: createDatabaseManagerAdapter(dbManager),
|
|
3751
|
+
nativeBinding
|
|
3752
|
+
});
|
|
3753
|
+
semanticIndexService.recover();
|
|
3754
|
+
server.addHook("onClose", async () => semanticIndexService?.close());
|
|
3755
|
+
} catch (err) {
|
|
3756
|
+
console.warn("[semantic-index] service unavailable:", err instanceof Error ? err.message : String(err));
|
|
3757
|
+
semanticIndexService = void 0;
|
|
3758
|
+
}
|
|
3470
3759
|
registerWebRoutes(server, dbManager, {
|
|
3471
3760
|
pathProvider,
|
|
3472
3761
|
nativeBinding,
|
|
3762
|
+
semanticIndexService,
|
|
3473
3763
|
aiContext: {
|
|
3474
3764
|
aiDataDir,
|
|
3475
3765
|
aiChatManager,
|
|
@@ -3478,11 +3768,11 @@ async function startHttpServer(options) {
|
|
|
3478
3768
|
llmConfigStore,
|
|
3479
3769
|
customProviderStore: new CustomProviderStore(createFileConfigStorage(aiDataDir)),
|
|
3480
3770
|
customModelStore: new CustomModelStore(createFileConfigStorage(aiDataDir)),
|
|
3481
|
-
runAgentStream: createCliRunAgentStream(dbManager, aiChatManager)
|
|
3771
|
+
runAgentStream: createCliRunAgentStream(dbManager, aiChatManager, semanticIndexService)
|
|
3482
3772
|
}
|
|
3483
3773
|
});
|
|
3484
3774
|
initSync(server, dbManager, pathProvider, { port, host, token });
|
|
3485
|
-
if (options?.webRoot &&
|
|
3775
|
+
if (options?.webRoot && fs11.existsSync(options.webRoot)) {
|
|
3486
3776
|
registerProxyRoutes(server);
|
|
3487
3777
|
const fastifyStatic = await import("@fastify/static");
|
|
3488
3778
|
await server.register(fastifyStatic.default, {
|
|
@@ -3523,4 +3813,4 @@ export {
|
|
|
3523
3813
|
startHttpServer,
|
|
3524
3814
|
stopHttpServer
|
|
3525
3815
|
};
|
|
3526
|
-
//# sourceMappingURL=chunk-
|
|
3816
|
+
//# sourceMappingURL=chunk-3HOTLLFC.mjs.map
|