chatlab-cli 0.26.1 → 0.26.3
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-T3Z4O4M5.mjs → chat-command-D37MMEJ7.mjs} +4 -4
- package/dist/{chunk-MZG2GLJR.mjs → chunk-742TAKEP.mjs} +2 -2
- package/dist/{chunk-3APSMVOM.mjs → chunk-D5MWER7N.mjs} +2 -2
- package/dist/{chunk-6O2SW5GL.mjs → chunk-GNGZHBKM.mjs} +3 -3
- package/dist/{chunk-SC77N66X.mjs → chunk-K5SGXTVJ.mjs} +31 -16
- package/dist/chunk-K5SGXTVJ.mjs.map +1 -0
- package/dist/{chunk-AZZYKUBR.mjs → chunk-QMXNJRTT.mjs} +448 -209
- package/dist/chunk-QMXNJRTT.mjs.map +1 -0
- package/dist/{chunk-FACKH2BR.mjs → chunk-RMITK4IC.mjs} +8 -4
- package/dist/chunk-RMITK4IC.mjs.map +1 -0
- package/dist/{chunk-FAOZNMPK.mjs → chunk-VCN64OD6.mjs} +2 -2
- package/dist/{chunk-GUGKY2GV.mjs → chunk-YPTJVTVJ.mjs} +9213 -6374
- package/dist/chunk-YPTJVTVJ.mjs.map +1 -0
- package/dist/cli.mjs +3 -3
- package/dist/{http-6LQUOYQ6.mjs → http-6F6TYWCI.mjs} +8 -8
- package/dist/{import-B2MAIBAK.mjs → import-VYPOMSSA.mjs} +4 -4
- package/dist/index.mjs +8 -8
- package/dist/{mcp-ZMMYW46I.mjs → mcp-AR7L5FA3.mjs} +7 -7
- package/dist/src-6S7JPKHX.mjs +443 -0
- package/dist/{stream-import-FHFBCEDV.mjs → stream-import-KZFC3XT2.mjs} +3 -3
- package/dist/stream-import-KZFC3XT2.mjs.map +1 -0
- package/dist/{update-checker-JIBA7T4U.mjs → update-checker-3N7HQEP2.mjs} +36 -12
- package/dist/update-checker-3N7HQEP2.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-BTdqmm0y.js +107 -0
- package/dist-web/assets/{MessageExportModal-B8DKeSAH.js → MessageExportModal-CF6yaor7.js} +1 -1
- package/dist-web/assets/{electron-C-aMus7e.js → electron-DKVw61Ww.js} +1 -1
- package/dist-web/assets/{fetch-xB6cl_1L.js → fetch-BC8jyJ_U.js} +1 -1
- package/dist-web/assets/{fetch-B26JuD67.js → fetch-BMdgfBVS.js} +1 -1
- package/dist-web/assets/fetch-BQlEQsqi.js +5 -0
- package/dist-web/assets/{fetch-CvnJ4Jgf.js → fetch-Bh6HC-IR.js} +1 -1
- package/dist-web/assets/{fetch-phbrO2bY.js → fetch-BvVfWR-P.js} +1 -1
- package/dist-web/assets/{fetch-BLKibnXa.js → fetch-CPeSfqsU.js} +1 -1
- package/dist-web/assets/fetch-CUw-PUYP.js +1 -0
- package/dist-web/assets/{fetch-CV8jezsM.js → fetch-CpYSD089.js} +1 -1
- package/dist-web/assets/{fetch-DP3yNfJM.js → fetch-D2jNBqs8.js} +1 -1
- package/dist-web/assets/{fetch-BSJY7tRo.js → fetch-DqEMAea3.js} +1 -1
- package/dist-web/assets/{index-DJQFNVUJ.js → index-BHQnAj6l.js} +1 -1
- package/dist-web/assets/index-BwzuCu5R.js +2 -0
- package/dist-web/assets/index-CG746E15.js +64 -0
- package/dist-web/assets/index-CXoQ_U22.js +173 -0
- package/dist-web/assets/index-DqybGdJH.css +1 -0
- package/dist-web/assets/index-HgIuP1Td.css +1 -0
- package/dist-web/assets/index-Qh0XLt3R.js +24 -0
- package/dist-web/assets/index-R82N2_0b.css +1 -0
- package/dist-web/assets/index-wPAmuQZY.js +11 -0
- package/dist-web/assets/{web-DUQU-v8U.js → web-Bx2TiHDk.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-AZZYKUBR.mjs.map +0 -1
- package/dist/chunk-FACKH2BR.mjs.map +0 -1
- package/dist/chunk-GUGKY2GV.mjs.map +0 -1
- package/dist/chunk-SC77N66X.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-CgtTQTfN.js +0 -182
- package/dist-web/assets/electron-dBbXcMDB.js +0 -1
- package/dist-web/assets/fetch-B-LdQP9s.js +0 -1
- package/dist-web/assets/fetch-BNNqdS2B.js +0 -1
- package/dist-web/assets/index-8Zcs8peI.css +0 -1
- package/dist-web/assets/index-B_lOzUD8.js +0 -2
- package/dist-web/assets/index-BvWcJc3c.js +0 -173
- package/dist-web/assets/index-CSDI5pAA.js +0 -24
- package/dist-web/assets/index-CX2FucK5.css +0 -1
- package/dist-web/assets/index-DZ6Szt_y.js +0 -163
- package/dist-web/assets/index-DhpclWeF.js +0 -11
- package/dist-web/assets/index-Dhql4DJr.css +0 -1
- /package/dist/{chat-command-T3Z4O4M5.mjs.map → chat-command-D37MMEJ7.mjs.map} +0 -0
- /package/dist/{chunk-MZG2GLJR.mjs.map → chunk-742TAKEP.mjs.map} +0 -0
- /package/dist/{chunk-3APSMVOM.mjs.map → chunk-D5MWER7N.mjs.map} +0 -0
- /package/dist/{chunk-6O2SW5GL.mjs.map → chunk-GNGZHBKM.mjs.map} +0 -0
- /package/dist/{chunk-FAOZNMPK.mjs.map → chunk-VCN64OD6.mjs.map} +0 -0
- /package/dist/{http-6LQUOYQ6.mjs.map → http-6F6TYWCI.mjs.map} +0 -0
- /package/dist/{import-B2MAIBAK.mjs.map → import-VYPOMSSA.mjs.map} +0 -0
- /package/dist/{mcp-ZMMYW46I.mjs.map → mcp-AR7L5FA3.mjs.map} +0 -0
- /package/dist/{stream-import-FHFBCEDV.mjs.map → src-6S7JPKHX.mjs.map} +0 -0
|
@@ -7,28 +7,31 @@ const __dirname = __pathDirname(__filename);
|
|
|
7
7
|
import {
|
|
8
8
|
importData,
|
|
9
9
|
parseFile
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-D5MWER7N.mjs";
|
|
11
11
|
import {
|
|
12
12
|
analyzeIncrementalImport,
|
|
13
13
|
analyzeNewImport,
|
|
14
14
|
incrementalImport,
|
|
15
15
|
streamImport
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-VCN64OD6.mjs";
|
|
17
17
|
import {
|
|
18
18
|
assertCliDataDirCompatible
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-742TAKEP.mjs";
|
|
20
20
|
import {
|
|
21
21
|
getVersion
|
|
22
22
|
} from "./chunk-5ADKYYNA.mjs";
|
|
23
|
+
import {
|
|
24
|
+
resolveCliPath
|
|
25
|
+
} from "./chunk-FJ35V3TE.mjs";
|
|
23
26
|
import {
|
|
24
27
|
closeServerAiLogger,
|
|
25
28
|
createCliRunAgentStream,
|
|
26
29
|
initServerAiLogger
|
|
27
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-GNGZHBKM.mjs";
|
|
28
31
|
import {
|
|
29
32
|
AGENT_TOOL_REGISTRY,
|
|
30
33
|
CoreDataProvider
|
|
31
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-RMITK4IC.mjs";
|
|
32
35
|
import {
|
|
33
36
|
AIChatManager,
|
|
34
37
|
ALL_MIGRATIONS,
|
|
@@ -71,29 +74,41 @@ import {
|
|
|
71
74
|
getAvailableYears,
|
|
72
75
|
getBuiltinModelsByProvider,
|
|
73
76
|
getCatchphraseAnalysis,
|
|
77
|
+
getCheckInAnalysis,
|
|
74
78
|
getClusterGraph,
|
|
75
79
|
getDailyActivity,
|
|
80
|
+
getDbFileVersion,
|
|
76
81
|
getDefaultNodeUserDataDir,
|
|
77
82
|
getDefaultRulesForLocale,
|
|
78
83
|
getDictList,
|
|
84
|
+
getDivingAnalysis,
|
|
85
|
+
getDragonKingAnalysis,
|
|
79
86
|
getHourlyActivity,
|
|
80
87
|
getLanguagePreferenceAnalysis,
|
|
81
88
|
getLaughAnalysis,
|
|
89
|
+
getLongMessageCount,
|
|
82
90
|
getMemberActivity,
|
|
91
|
+
getMemberMonthlyTrend,
|
|
83
92
|
getMembers,
|
|
84
93
|
getMembersDetailed,
|
|
94
|
+
getMemeBattleAnalysis,
|
|
85
95
|
getMentionAnalysis,
|
|
86
96
|
getMentionGraph,
|
|
87
97
|
getMessageLengthDistribution,
|
|
88
98
|
getMessageTypeStats,
|
|
89
99
|
getMonthlyActivity,
|
|
100
|
+
getNightOwlAnalysis,
|
|
101
|
+
getOrComputeAnalysisCache,
|
|
90
102
|
getPendingNodeDataDirMigration,
|
|
91
103
|
getRelationshipStats,
|
|
104
|
+
getRepeatAnalysis,
|
|
92
105
|
getSchemaDetailed,
|
|
93
106
|
getSessionInfo,
|
|
94
107
|
getSessionMeta,
|
|
95
108
|
getSessionOverview,
|
|
96
109
|
getSupportedFormats,
|
|
110
|
+
getTextLengthPercentiles,
|
|
111
|
+
getTextStats,
|
|
97
112
|
getTimeRange,
|
|
98
113
|
getWeekdayActivity,
|
|
99
114
|
getYearlyActivity,
|
|
@@ -104,6 +119,7 @@ import {
|
|
|
104
119
|
loadConfig,
|
|
105
120
|
member_service_exports,
|
|
106
121
|
mergeRulesForLocale,
|
|
122
|
+
owner_profile_service_exports,
|
|
107
123
|
queryMessages,
|
|
108
124
|
resolveApiKey,
|
|
109
125
|
runSimpleLlmStream,
|
|
@@ -119,13 +135,10 @@ import {
|
|
|
119
135
|
verifyCliDataPath,
|
|
120
136
|
writeAuthProfile,
|
|
121
137
|
writeConfigField
|
|
122
|
-
} from "./chunk-
|
|
123
|
-
import {
|
|
124
|
-
resolveCliPath
|
|
125
|
-
} from "./chunk-FJ35V3TE.mjs";
|
|
138
|
+
} from "./chunk-YPTJVTVJ.mjs";
|
|
126
139
|
|
|
127
140
|
// src/http/index.ts
|
|
128
|
-
import * as
|
|
141
|
+
import * as fs11 from "fs";
|
|
129
142
|
import * as crypto4 from "crypto";
|
|
130
143
|
|
|
131
144
|
// src/http/server.ts
|
|
@@ -444,14 +457,25 @@ function registerRestSessionRoutes(server2, ctx) {
|
|
|
444
457
|
// ../../packages/http-routes/src/routes/web/sessions.ts
|
|
445
458
|
function registerSessionRoutes(server2, ctx) {
|
|
446
459
|
const { sessionAdapter: adapter } = ctx;
|
|
460
|
+
let preferencesInstance = null;
|
|
461
|
+
const preferences = () => {
|
|
462
|
+
preferencesInstance ??= ctx.preferencesManager ?? new PreferencesManager(ctx.pathProvider.getSystemDir());
|
|
463
|
+
return preferencesInstance;
|
|
464
|
+
};
|
|
447
465
|
server2.get("/_web/sessions", async () => {
|
|
448
|
-
|
|
466
|
+
const aiChatCounts = ctx.aiChatManager?.getAIChatCountsBySession();
|
|
467
|
+
return session_service_exports.listAnalysisSessions(adapter, {
|
|
468
|
+
enrichSession: aiChatCounts?.size ? (dto) => ({ ...dto, aiConversationCount: aiChatCounts.get(dto.id) ?? 0 }) : void 0
|
|
469
|
+
});
|
|
449
470
|
});
|
|
450
471
|
server2.get("/_web/sessions/:id", async (request) => {
|
|
451
472
|
const session = session_service_exports.getAnalysisSession(adapter, request.params.id);
|
|
452
473
|
if (!session) {
|
|
453
474
|
throw Object.assign(new Error(`Session not found: ${request.params.id}`), { statusCode: 404 });
|
|
454
475
|
}
|
|
476
|
+
if (ctx.aiChatManager) {
|
|
477
|
+
session.aiConversationCount = ctx.aiChatManager.getAIChats(request.params.id).length;
|
|
478
|
+
}
|
|
455
479
|
return session;
|
|
456
480
|
});
|
|
457
481
|
server2.delete("/_web/sessions/:id", async (request, reply) => {
|
|
@@ -477,6 +501,24 @@ function registerSessionRoutes(server2, ctx) {
|
|
|
477
501
|
return { success: true };
|
|
478
502
|
}
|
|
479
503
|
);
|
|
504
|
+
server2.post("/_web/sessions/:id/owner/apply-profile", async (request) => {
|
|
505
|
+
return owner_profile_service_exports.tryApplyOwnerProfile(adapter, preferences(), request.params.id);
|
|
506
|
+
});
|
|
507
|
+
server2.post(
|
|
508
|
+
"/_web/sessions/:id/owner/select",
|
|
509
|
+
async (request) => {
|
|
510
|
+
return owner_profile_service_exports.setOwnerAndApplyProfile(
|
|
511
|
+
adapter,
|
|
512
|
+
preferences(),
|
|
513
|
+
request.params.id,
|
|
514
|
+
request.body.ownerPlatformId
|
|
515
|
+
);
|
|
516
|
+
}
|
|
517
|
+
);
|
|
518
|
+
server2.post("/_web/sessions/:id/owner/dismiss-prompt", async (request) => {
|
|
519
|
+
owner_profile_service_exports.dismissOwnerPrompt(preferences(), request.params.id);
|
|
520
|
+
return { success: true };
|
|
521
|
+
});
|
|
480
522
|
}
|
|
481
523
|
|
|
482
524
|
// ../../packages/http-routes/src/routes/web/members.ts
|
|
@@ -565,6 +607,10 @@ function registerPreferencesRoutes(server2, ctx) {
|
|
|
565
607
|
});
|
|
566
608
|
}
|
|
567
609
|
|
|
610
|
+
// ../../packages/http-routes/src/routes/web/analytics.ts
|
|
611
|
+
import * as fs from "fs";
|
|
612
|
+
import * as path2 from "path";
|
|
613
|
+
|
|
568
614
|
// ../../packages/http-routes/src/helpers.ts
|
|
569
615
|
function parseTimeFilter(query) {
|
|
570
616
|
const { startTs, endTs, memberId } = query;
|
|
@@ -576,9 +622,32 @@ function parseTimeFilter(query) {
|
|
|
576
622
|
return filter;
|
|
577
623
|
}
|
|
578
624
|
|
|
625
|
+
// ../../packages/http-routes/src/analytics-cache.ts
|
|
626
|
+
import * as path from "path";
|
|
627
|
+
function canonical(value) {
|
|
628
|
+
if (value === void 0) return "null";
|
|
629
|
+
if (value === null || typeof value !== "object") return JSON.stringify(value);
|
|
630
|
+
if (Array.isArray(value)) return `[${value.map(canonical).join(",")}]`;
|
|
631
|
+
const obj = value;
|
|
632
|
+
const keys = Object.keys(obj).filter((k) => obj[k] !== void 0).sort();
|
|
633
|
+
return `{${keys.map((k) => `${JSON.stringify(k)}:${canonical(obj[k])}`).join(",")}}`;
|
|
634
|
+
}
|
|
635
|
+
function buildAnalyticsCacheKey(namespace, params) {
|
|
636
|
+
return `${namespace}:${canonical(params)}`;
|
|
637
|
+
}
|
|
638
|
+
function withAnalyticsCache(ctx, sessionId, namespace, params, compute, options) {
|
|
639
|
+
const queryCacheDir = path.join(ctx.pathProvider.getCacheDir(), "query");
|
|
640
|
+
const dateStr = options?.dailyInvalidate ? `|date:${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}` : "";
|
|
641
|
+
const extraStr = options?.extraVersion ? `|${options.extraVersion}` : "";
|
|
642
|
+
const version = `${ctx.getVersion()}|${getDbFileVersion(ctx.sessionAdapter.getDbPath(sessionId))}${dateStr}${extraStr}`;
|
|
643
|
+
const key = buildAnalyticsCacheKey(namespace, params);
|
|
644
|
+
return getOrComputeAnalysisCache(sessionId, key, queryCacheDir, version, compute);
|
|
645
|
+
}
|
|
646
|
+
|
|
579
647
|
// ../../packages/http-routes/src/routes/web/analytics.ts
|
|
580
648
|
function registerAnalyticsRoutes(server2, ctx) {
|
|
581
649
|
const { sessionAdapter: adapter } = ctx;
|
|
650
|
+
const cached = (name, sessionId, params, compute, options) => withAnalyticsCache(ctx, sessionId, `analytics.${name}`, params, compute, options);
|
|
582
651
|
server2.get("/_web/sessions/:id/years", async (request) => {
|
|
583
652
|
const db = adapter.ensureReadonly(request.params.id);
|
|
584
653
|
return getAvailableYears(db);
|
|
@@ -590,111 +659,252 @@ function registerAnalyticsRoutes(server2, ctx) {
|
|
|
590
659
|
server2.get(
|
|
591
660
|
"/_web/sessions/:id/stats/member-activity",
|
|
592
661
|
async (request) => {
|
|
593
|
-
const
|
|
594
|
-
|
|
662
|
+
const id = request.params.id;
|
|
663
|
+
const filter = parseTimeFilter(request.query);
|
|
664
|
+
return cached("member-activity", id, { ...filter }, () => getMemberActivity(adapter.ensureReadonly(id), filter));
|
|
595
665
|
}
|
|
596
666
|
);
|
|
597
667
|
server2.get(
|
|
598
668
|
"/_web/sessions/:id/stats/hourly",
|
|
599
669
|
async (request) => {
|
|
600
|
-
const
|
|
601
|
-
|
|
670
|
+
const id = request.params.id;
|
|
671
|
+
const filter = parseTimeFilter(request.query);
|
|
672
|
+
return cached("hourly", id, { ...filter }, () => getHourlyActivity(adapter.ensureReadonly(id), filter));
|
|
602
673
|
}
|
|
603
674
|
);
|
|
604
675
|
server2.get(
|
|
605
676
|
"/_web/sessions/:id/stats/daily",
|
|
606
677
|
async (request) => {
|
|
607
|
-
const
|
|
608
|
-
|
|
678
|
+
const id = request.params.id;
|
|
679
|
+
const filter = parseTimeFilter(request.query);
|
|
680
|
+
return cached("daily", id, { ...filter }, () => getDailyActivity(adapter.ensureReadonly(id), filter));
|
|
609
681
|
}
|
|
610
682
|
);
|
|
611
683
|
server2.get(
|
|
612
684
|
"/_web/sessions/:id/stats/weekday",
|
|
613
685
|
async (request) => {
|
|
614
|
-
const
|
|
615
|
-
|
|
686
|
+
const id = request.params.id;
|
|
687
|
+
const filter = parseTimeFilter(request.query);
|
|
688
|
+
return cached("weekday", id, { ...filter }, () => getWeekdayActivity(adapter.ensureReadonly(id), filter));
|
|
616
689
|
}
|
|
617
690
|
);
|
|
618
691
|
server2.get(
|
|
619
692
|
"/_web/sessions/:id/stats/message-types",
|
|
620
693
|
async (request) => {
|
|
621
|
-
const
|
|
622
|
-
|
|
694
|
+
const id = request.params.id;
|
|
695
|
+
const filter = parseTimeFilter(request.query);
|
|
696
|
+
return cached("message-types", id, { ...filter }, () => getMessageTypeStats(adapter.ensureReadonly(id), filter));
|
|
623
697
|
}
|
|
624
698
|
);
|
|
625
699
|
server2.get(
|
|
626
700
|
"/_web/sessions/:id/analytics/relationship",
|
|
627
701
|
async (request) => {
|
|
628
|
-
const
|
|
629
|
-
|
|
702
|
+
const id = request.params.id;
|
|
703
|
+
const filter = parseTimeFilter(request.query);
|
|
704
|
+
return cached("relationship", id, { ...filter }, () => getRelationshipStats(adapter.ensureReadonly(id), filter));
|
|
630
705
|
}
|
|
631
706
|
);
|
|
632
707
|
server2.get(
|
|
633
708
|
"/_web/sessions/:id/analytics/catchphrase",
|
|
634
709
|
async (request) => {
|
|
635
|
-
const
|
|
636
|
-
|
|
710
|
+
const id = request.params.id;
|
|
711
|
+
const filter = parseTimeFilter(request.query);
|
|
712
|
+
return cached("catchphrase", id, { ...filter }, () => getCatchphraseAnalysis(adapter.ensureReadonly(id), filter));
|
|
637
713
|
}
|
|
638
714
|
);
|
|
639
715
|
server2.get(
|
|
640
716
|
"/_web/sessions/:id/analytics/mention",
|
|
641
717
|
async (request) => {
|
|
642
|
-
const
|
|
643
|
-
|
|
718
|
+
const id = request.params.id;
|
|
719
|
+
const filter = parseTimeFilter(request.query);
|
|
720
|
+
return cached("mention", id, { ...filter }, () => getMentionAnalysis(adapter.ensureReadonly(id), filter));
|
|
644
721
|
}
|
|
645
722
|
);
|
|
646
723
|
server2.get(
|
|
647
724
|
"/_web/sessions/:id/analytics/mention-graph",
|
|
648
725
|
async (request) => {
|
|
649
|
-
const
|
|
650
|
-
|
|
726
|
+
const id = request.params.id;
|
|
727
|
+
const filter = parseTimeFilter(request.query);
|
|
728
|
+
return cached("mention-graph", id, { ...filter }, () => getMentionGraph(adapter.ensureReadonly(id), filter));
|
|
651
729
|
}
|
|
652
730
|
);
|
|
653
731
|
server2.get(
|
|
654
732
|
"/_web/sessions/:id/analytics/laugh",
|
|
655
733
|
async (request) => {
|
|
656
|
-
const
|
|
657
|
-
return getLaughAnalysis(db, parseTimeFilter(request.query));
|
|
658
|
-
}
|
|
659
|
-
);
|
|
660
|
-
server2.get(
|
|
661
|
-
"/_web/sessions/:id/analytics/cluster",
|
|
662
|
-
async (request) => {
|
|
663
|
-
const db = adapter.ensureReadonly(request.params.id);
|
|
734
|
+
const id = request.params.id;
|
|
664
735
|
const filter = parseTimeFilter(request.query);
|
|
665
|
-
|
|
666
|
-
return getClusterGraph(db, filter, topEdges ? { topEdges } : void 0);
|
|
736
|
+
return cached("laugh", id, { ...filter }, () => getLaughAnalysis(adapter.ensureReadonly(id), filter));
|
|
667
737
|
}
|
|
668
738
|
);
|
|
739
|
+
server2.get("/_web/sessions/:id/analytics/cluster", async (request) => {
|
|
740
|
+
const id = request.params.id;
|
|
741
|
+
const filter = parseTimeFilter(request.query);
|
|
742
|
+
const options = {};
|
|
743
|
+
if (request.query.topEdges) options.topEdges = parseInt(request.query.topEdges, 10);
|
|
744
|
+
if (request.query.lookAhead) options.lookAhead = parseInt(request.query.lookAhead, 10);
|
|
745
|
+
if (request.query.decaySeconds) options.decaySeconds = parseInt(request.query.decaySeconds, 10);
|
|
746
|
+
const hasOptions = Object.keys(options).length > 0;
|
|
747
|
+
return cached(
|
|
748
|
+
"cluster",
|
|
749
|
+
id,
|
|
750
|
+
{ ...filter, ...options },
|
|
751
|
+
() => getClusterGraph(adapter.ensureReadonly(id), filter, hasOptions ? options : void 0)
|
|
752
|
+
);
|
|
753
|
+
});
|
|
669
754
|
server2.get(
|
|
670
755
|
"/_web/sessions/:id/analytics/language-preference",
|
|
671
756
|
async (request) => {
|
|
672
|
-
const
|
|
757
|
+
const id = request.params.id;
|
|
673
758
|
const filter = parseTimeFilter(request.query);
|
|
674
759
|
const locale = request.query.locale || "zh-CN";
|
|
675
|
-
const
|
|
676
|
-
|
|
760
|
+
const zhDictPath = path2.join(ctx.pathProvider.getSystemDir(), "nlp", "zh-CN.dict");
|
|
761
|
+
let zhDictVersion;
|
|
762
|
+
try {
|
|
763
|
+
const st = fs.statSync(zhDictPath);
|
|
764
|
+
zhDictVersion = `${Math.floor(st.mtimeMs)}:${st.size}`;
|
|
765
|
+
} catch {
|
|
766
|
+
zhDictVersion = "-";
|
|
767
|
+
}
|
|
768
|
+
return cached(
|
|
769
|
+
"language-preference",
|
|
770
|
+
id,
|
|
771
|
+
{ ...filter, locale },
|
|
772
|
+
() => getLanguagePreferenceAnalysis(adapter.ensureReadonly(id), {
|
|
773
|
+
locale,
|
|
774
|
+
timeFilter: filter,
|
|
775
|
+
nlpProvider: createJiebaNlpProvider()
|
|
776
|
+
}),
|
|
777
|
+
{ extraVersion: `dict:${zhDictVersion}` }
|
|
778
|
+
);
|
|
677
779
|
}
|
|
678
780
|
);
|
|
679
781
|
server2.get(
|
|
680
782
|
"/_web/sessions/:id/analytics/monthly-activity",
|
|
681
783
|
async (request) => {
|
|
682
|
-
const
|
|
683
|
-
|
|
784
|
+
const id = request.params.id;
|
|
785
|
+
const filter = parseTimeFilter(request.query);
|
|
786
|
+
return cached("monthly-activity", id, { ...filter }, () => getMonthlyActivity(adapter.ensureReadonly(id), filter));
|
|
684
787
|
}
|
|
685
788
|
);
|
|
686
789
|
server2.get(
|
|
687
790
|
"/_web/sessions/:id/analytics/yearly-activity",
|
|
688
791
|
async (request) => {
|
|
689
|
-
const
|
|
690
|
-
|
|
792
|
+
const id = request.params.id;
|
|
793
|
+
const filter = parseTimeFilter(request.query);
|
|
794
|
+
return cached("yearly-activity", id, { ...filter }, () => getYearlyActivity(adapter.ensureReadonly(id), filter));
|
|
691
795
|
}
|
|
692
796
|
);
|
|
693
797
|
server2.get(
|
|
694
798
|
"/_web/sessions/:id/analytics/message-length-distribution",
|
|
695
799
|
async (request) => {
|
|
696
|
-
const
|
|
697
|
-
|
|
800
|
+
const id = request.params.id;
|
|
801
|
+
const filter = parseTimeFilter(request.query);
|
|
802
|
+
return cached(
|
|
803
|
+
"message-length-distribution",
|
|
804
|
+
id,
|
|
805
|
+
{ ...filter },
|
|
806
|
+
() => getMessageLengthDistribution(adapter.ensureReadonly(id), filter)
|
|
807
|
+
);
|
|
808
|
+
}
|
|
809
|
+
);
|
|
810
|
+
server2.get(
|
|
811
|
+
"/_web/sessions/:id/analytics/text-stats",
|
|
812
|
+
async (request) => {
|
|
813
|
+
const id = request.params.id;
|
|
814
|
+
const filter = parseTimeFilter(request.query);
|
|
815
|
+
return cached("text-stats", id, { ...filter }, () => getTextStats(adapter.ensureReadonly(id), filter));
|
|
816
|
+
}
|
|
817
|
+
);
|
|
818
|
+
server2.get(
|
|
819
|
+
"/_web/sessions/:id/analytics/long-message-count",
|
|
820
|
+
async (request) => {
|
|
821
|
+
const id = request.params.id;
|
|
822
|
+
const filter = parseTimeFilter(request.query);
|
|
823
|
+
const minLength = request.query.minLength ? parseInt(request.query.minLength, 10) : void 0;
|
|
824
|
+
return cached(
|
|
825
|
+
"long-message-count",
|
|
826
|
+
id,
|
|
827
|
+
{ ...filter, minLength },
|
|
828
|
+
() => getLongMessageCount(adapter.ensureReadonly(id), filter, minLength)
|
|
829
|
+
);
|
|
830
|
+
}
|
|
831
|
+
);
|
|
832
|
+
server2.get(
|
|
833
|
+
"/_web/sessions/:id/analytics/member-monthly-trend",
|
|
834
|
+
async (request) => {
|
|
835
|
+
const id = request.params.id;
|
|
836
|
+
const filter = parseTimeFilter(request.query);
|
|
837
|
+
return cached(
|
|
838
|
+
"member-monthly-trend",
|
|
839
|
+
id,
|
|
840
|
+
{ ...filter },
|
|
841
|
+
() => getMemberMonthlyTrend(adapter.ensureReadonly(id), filter)
|
|
842
|
+
);
|
|
843
|
+
}
|
|
844
|
+
);
|
|
845
|
+
server2.get(
|
|
846
|
+
"/_web/sessions/:id/analytics/text-length-percentiles",
|
|
847
|
+
async (request) => {
|
|
848
|
+
const id = request.params.id;
|
|
849
|
+
const filter = parseTimeFilter(request.query);
|
|
850
|
+
return cached(
|
|
851
|
+
"text-length-percentiles",
|
|
852
|
+
id,
|
|
853
|
+
{ ...filter },
|
|
854
|
+
() => getTextLengthPercentiles(adapter.ensureReadonly(id), filter)
|
|
855
|
+
);
|
|
856
|
+
}
|
|
857
|
+
);
|
|
858
|
+
server2.get(
|
|
859
|
+
"/_web/sessions/:id/analytics/dragon-king",
|
|
860
|
+
async (request) => {
|
|
861
|
+
const id = request.params.id;
|
|
862
|
+
const filter = parseTimeFilter(request.query);
|
|
863
|
+
return cached("dragon-king", id, { ...filter }, () => getDragonKingAnalysis(adapter.ensureReadonly(id), filter));
|
|
864
|
+
}
|
|
865
|
+
);
|
|
866
|
+
server2.get(
|
|
867
|
+
"/_web/sessions/:id/analytics/diving",
|
|
868
|
+
async (request) => {
|
|
869
|
+
const id = request.params.id;
|
|
870
|
+
const filter = parseTimeFilter(request.query);
|
|
871
|
+
return cached("diving", id, { ...filter }, () => getDivingAnalysis(adapter.ensureReadonly(id), filter), {
|
|
872
|
+
dailyInvalidate: true
|
|
873
|
+
});
|
|
874
|
+
}
|
|
875
|
+
);
|
|
876
|
+
server2.get(
|
|
877
|
+
"/_web/sessions/:id/analytics/check-in",
|
|
878
|
+
async (request) => {
|
|
879
|
+
const id = request.params.id;
|
|
880
|
+
const filter = parseTimeFilter(request.query);
|
|
881
|
+
return cached("check-in", id, { ...filter }, () => getCheckInAnalysis(adapter.ensureReadonly(id), filter));
|
|
882
|
+
}
|
|
883
|
+
);
|
|
884
|
+
server2.get(
|
|
885
|
+
"/_web/sessions/:id/analytics/meme-battle",
|
|
886
|
+
async (request) => {
|
|
887
|
+
const id = request.params.id;
|
|
888
|
+
const filter = parseTimeFilter(request.query);
|
|
889
|
+
return cached("meme-battle", id, { ...filter }, () => getMemeBattleAnalysis(adapter.ensureReadonly(id), filter));
|
|
890
|
+
}
|
|
891
|
+
);
|
|
892
|
+
server2.get(
|
|
893
|
+
"/_web/sessions/:id/analytics/night-owl",
|
|
894
|
+
async (request) => {
|
|
895
|
+
const id = request.params.id;
|
|
896
|
+
const filter = parseTimeFilter(request.query);
|
|
897
|
+
return cached("night-owl", id, { ...filter }, () => getNightOwlAnalysis(adapter.ensureReadonly(id), filter), {
|
|
898
|
+
dailyInvalidate: true
|
|
899
|
+
});
|
|
900
|
+
}
|
|
901
|
+
);
|
|
902
|
+
server2.get(
|
|
903
|
+
"/_web/sessions/:id/analytics/repeat",
|
|
904
|
+
async (request) => {
|
|
905
|
+
const id = request.params.id;
|
|
906
|
+
const filter = parseTimeFilter(request.query);
|
|
907
|
+
return cached("repeat", id, { ...filter }, () => getRepeatAnalysis(adapter.ensureReadonly(id), filter));
|
|
698
908
|
}
|
|
699
909
|
);
|
|
700
910
|
}
|
|
@@ -799,9 +1009,10 @@ function registerExportRoutes(server2, ctx) {
|
|
|
799
1009
|
}
|
|
800
1010
|
|
|
801
1011
|
// ../../packages/http-routes/src/routes/web/nlp.ts
|
|
802
|
-
import * as
|
|
1012
|
+
import * as fs2 from "fs";
|
|
1013
|
+
import * as path3 from "path";
|
|
803
1014
|
function registerNlpRoutes(server2, ctx) {
|
|
804
|
-
const nlpDir =
|
|
1015
|
+
const nlpDir = path3.join(ctx.pathProvider.getSystemDir(), "nlp");
|
|
805
1016
|
initNlpDir(nlpDir);
|
|
806
1017
|
ensureDefaultDict(nlpDir).catch((err) => console.warn("[NLP] Auto-download zh-CN dict failed:", err));
|
|
807
1018
|
server2.get("/_web/nlp/pos-tags", async () => {
|
|
@@ -825,7 +1036,19 @@ function registerNlpRoutes(server2, ctx) {
|
|
|
825
1036
|
if (!db) {
|
|
826
1037
|
throw Object.assign(new Error(`Session not found: ${params.sessionId}`), { statusCode: 404 });
|
|
827
1038
|
}
|
|
828
|
-
|
|
1039
|
+
const { sessionId, ...keyParams } = params;
|
|
1040
|
+
const effectiveDictId = !params.dictType || params.dictType === "default" ? "zh-CN" : String(params.dictType);
|
|
1041
|
+
const dictFilePath = path3.join(nlpDir, `${effectiveDictId}.dict`);
|
|
1042
|
+
let dictVersion;
|
|
1043
|
+
try {
|
|
1044
|
+
const st = fs2.statSync(dictFilePath);
|
|
1045
|
+
dictVersion = `${Math.floor(st.mtimeMs)}:${st.size}`;
|
|
1046
|
+
} catch {
|
|
1047
|
+
dictVersion = "-";
|
|
1048
|
+
}
|
|
1049
|
+
return withAnalyticsCache(ctx, sessionId, "nlp.word-frequency", keyParams, () => computeWordFrequency(db, params), {
|
|
1050
|
+
extraVersion: `dict:${dictVersion}`
|
|
1051
|
+
});
|
|
829
1052
|
});
|
|
830
1053
|
server2.post(
|
|
831
1054
|
"/_web/nlp/segment",
|
|
@@ -1390,9 +1613,9 @@ function registerAiSummaryRoutes(server2, ctx) {
|
|
|
1390
1613
|
}
|
|
1391
1614
|
|
|
1392
1615
|
// ../../packages/http-routes/src/routes/web/merge.ts
|
|
1393
|
-
import * as
|
|
1616
|
+
import * as fs3 from "fs";
|
|
1394
1617
|
import * as os from "os";
|
|
1395
|
-
import * as
|
|
1618
|
+
import * as path4 from "path";
|
|
1396
1619
|
function ensureDb2(ctx, sessionId) {
|
|
1397
1620
|
const db = ctx.dbManager.open(sessionId);
|
|
1398
1621
|
if (!db) throw sessionNotFound(sessionId);
|
|
@@ -1409,31 +1632,31 @@ function registerMergeRoutes(server2, ctx) {
|
|
|
1409
1632
|
if (contentType.includes("multipart/form-data")) {
|
|
1410
1633
|
const data = await request.file();
|
|
1411
1634
|
if (!data) return reply.code(400).send({ error: "No file uploaded" });
|
|
1412
|
-
const tmpDir =
|
|
1413
|
-
const tmpPath =
|
|
1635
|
+
const tmpDir = fs3.mkdtempSync(path4.join(os.tmpdir(), "chatlab-merge-"));
|
|
1636
|
+
const tmpPath = path4.join(tmpDir, data.filename || "upload");
|
|
1414
1637
|
const chunks = [];
|
|
1415
1638
|
for await (const chunk of data.file) {
|
|
1416
1639
|
chunks.push(chunk);
|
|
1417
1640
|
}
|
|
1418
|
-
|
|
1641
|
+
fs3.writeFileSync(tmpPath, Buffer.concat(chunks));
|
|
1419
1642
|
filePath = tmpPath;
|
|
1420
1643
|
cleanupFile = true;
|
|
1421
1644
|
cleanupDir = tmpDir;
|
|
1422
1645
|
} else {
|
|
1423
1646
|
const body = request.body;
|
|
1424
1647
|
if (!body?.filePath) return reply.code(400).send({ error: "Missing filePath in body" });
|
|
1425
|
-
if (!
|
|
1648
|
+
if (!fs3.existsSync(body.filePath)) return reply.code(400).send({ error: "File not found" });
|
|
1426
1649
|
filePath = body.filePath;
|
|
1427
1650
|
}
|
|
1428
1651
|
try {
|
|
1429
1652
|
const result = await streamParseFileInfo(filePath, {
|
|
1430
1653
|
createTempDatabase(sourceFilePath) {
|
|
1431
|
-
return mergeCache.createTempDatabase(
|
|
1654
|
+
return mergeCache.createTempDatabase(path4.basename(sourceFilePath));
|
|
1432
1655
|
},
|
|
1433
1656
|
onProgress() {
|
|
1434
1657
|
}
|
|
1435
1658
|
});
|
|
1436
|
-
const filename =
|
|
1659
|
+
const filename = path4.basename(filePath);
|
|
1437
1660
|
const handle = mergeCache.store(filename, result.tempDbPath);
|
|
1438
1661
|
return {
|
|
1439
1662
|
handle,
|
|
@@ -1447,13 +1670,13 @@ function registerMergeRoutes(server2, ctx) {
|
|
|
1447
1670
|
} finally {
|
|
1448
1671
|
if (cleanupFile) {
|
|
1449
1672
|
try {
|
|
1450
|
-
|
|
1673
|
+
fs3.unlinkSync(filePath);
|
|
1451
1674
|
} catch {
|
|
1452
1675
|
}
|
|
1453
1676
|
}
|
|
1454
1677
|
if (cleanupDir) {
|
|
1455
1678
|
try {
|
|
1456
|
-
|
|
1679
|
+
fs3.rmdirSync(cleanupDir);
|
|
1457
1680
|
} catch {
|
|
1458
1681
|
}
|
|
1459
1682
|
}
|
|
@@ -1502,20 +1725,20 @@ function registerMergeRoutes(server2, ctx) {
|
|
|
1502
1725
|
return reply.code(501).send({ error: "Import not available on this server" });
|
|
1503
1726
|
}
|
|
1504
1727
|
const jsonData = JSON.stringify(merged.chatLabData);
|
|
1505
|
-
const tmpDir =
|
|
1506
|
-
const safeName =
|
|
1507
|
-
const tmpPath =
|
|
1508
|
-
|
|
1728
|
+
const tmpDir = fs3.mkdtempSync(path4.join(os.tmpdir(), "chatlab-merged-"));
|
|
1729
|
+
const safeName = path4.basename(outputName).replace(/[/\\?%*:|"<>]/g, "_") || "merged";
|
|
1730
|
+
const tmpPath = path4.join(tmpDir, `${safeName}.json`);
|
|
1731
|
+
fs3.writeFileSync(tmpPath, jsonData, "utf-8");
|
|
1509
1732
|
try {
|
|
1510
1733
|
const importResult = await streamImport2(dbManager2, tmpPath);
|
|
1511
1734
|
sessionId = importResult.sessionId;
|
|
1512
1735
|
} finally {
|
|
1513
1736
|
try {
|
|
1514
|
-
|
|
1737
|
+
fs3.unlinkSync(tmpPath);
|
|
1515
1738
|
} catch {
|
|
1516
1739
|
}
|
|
1517
1740
|
try {
|
|
1518
|
-
|
|
1741
|
+
fs3.rmdirSync(tmpDir);
|
|
1519
1742
|
} catch {
|
|
1520
1743
|
}
|
|
1521
1744
|
}
|
|
@@ -1589,16 +1812,16 @@ function registerMergeRoutes(server2, ctx) {
|
|
|
1589
1812
|
}
|
|
1590
1813
|
|
|
1591
1814
|
// ../../packages/http-routes/src/routes/web/cache.ts
|
|
1592
|
-
import * as
|
|
1815
|
+
import * as fs4 from "fs";
|
|
1593
1816
|
import * as fsp from "fs/promises";
|
|
1594
|
-
import * as
|
|
1817
|
+
import * as path5 from "path";
|
|
1595
1818
|
async function getDirSize(dirPath) {
|
|
1596
1819
|
let totalSize = 0;
|
|
1597
1820
|
try {
|
|
1598
|
-
if (!
|
|
1821
|
+
if (!fs4.existsSync(dirPath)) return 0;
|
|
1599
1822
|
const files = await fsp.readdir(dirPath, { withFileTypes: true });
|
|
1600
1823
|
for (const file of files) {
|
|
1601
|
-
const filePath =
|
|
1824
|
+
const filePath = path5.join(dirPath, file.name);
|
|
1602
1825
|
if (file.isDirectory()) {
|
|
1603
1826
|
totalSize += await getDirSize(filePath);
|
|
1604
1827
|
} else {
|
|
@@ -1613,10 +1836,10 @@ async function getDirSize(dirPath) {
|
|
|
1613
1836
|
async function getFileCount(dirPath) {
|
|
1614
1837
|
let count = 0;
|
|
1615
1838
|
try {
|
|
1616
|
-
if (!
|
|
1839
|
+
if (!fs4.existsSync(dirPath)) return 0;
|
|
1617
1840
|
const files = await fsp.readdir(dirPath, { withFileTypes: true });
|
|
1618
1841
|
for (const file of files) {
|
|
1619
|
-
const filePath =
|
|
1842
|
+
const filePath = path5.join(dirPath, file.name);
|
|
1620
1843
|
if (file.isDirectory()) {
|
|
1621
1844
|
count += await getFileCount(filePath);
|
|
1622
1845
|
} else {
|
|
@@ -1629,10 +1852,10 @@ async function getFileCount(dirPath) {
|
|
|
1629
1852
|
}
|
|
1630
1853
|
function hasChatLabDatabases(dirPath) {
|
|
1631
1854
|
try {
|
|
1632
|
-
const markerPath =
|
|
1633
|
-
const dbDir =
|
|
1634
|
-
if (!
|
|
1635
|
-
return
|
|
1855
|
+
const markerPath = path5.join(dirPath, ".chatlab");
|
|
1856
|
+
const dbDir = path5.join(dirPath, "databases");
|
|
1857
|
+
if (!fs4.existsSync(markerPath) || !fs4.existsSync(dbDir)) return false;
|
|
1858
|
+
return fs4.readdirSync(dbDir).some((file) => file.endsWith(".db"));
|
|
1636
1859
|
} catch {
|
|
1637
1860
|
return false;
|
|
1638
1861
|
}
|
|
@@ -1678,7 +1901,7 @@ function registerCacheRoutes(server2, ctx) {
|
|
|
1678
1901
|
const results = await Promise.all(
|
|
1679
1902
|
directories.map(async (dir) => {
|
|
1680
1903
|
const [size, fileCount] = await Promise.all([getDirSize(dir.path), getFileCount(dir.path)]);
|
|
1681
|
-
return { ...dir, size, fileCount, exists:
|
|
1904
|
+
return { ...dir, size, fileCount, exists: fs4.existsSync(dir.path) };
|
|
1682
1905
|
})
|
|
1683
1906
|
);
|
|
1684
1907
|
return {
|
|
@@ -1695,10 +1918,10 @@ function registerCacheRoutes(server2, ctx) {
|
|
|
1695
1918
|
};
|
|
1696
1919
|
const dirPath = allowedDirs[cacheId];
|
|
1697
1920
|
if (!dirPath) return { success: false, error: "Not allowed to clear this directory" };
|
|
1698
|
-
if (!
|
|
1921
|
+
if (!fs4.existsSync(dirPath)) return { success: true };
|
|
1699
1922
|
const files = await fsp.readdir(dirPath);
|
|
1700
1923
|
for (const file of files) {
|
|
1701
|
-
const filePath =
|
|
1924
|
+
const filePath = path5.join(dirPath, file);
|
|
1702
1925
|
const stat2 = await fsp.stat(filePath);
|
|
1703
1926
|
if (stat2.isDirectory()) {
|
|
1704
1927
|
await fsp.rm(filePath, { recursive: true });
|
|
@@ -1715,7 +1938,7 @@ function registerCacheRoutes(server2, ctx) {
|
|
|
1715
1938
|
defaultPath: ctx.defaultUserDataDir,
|
|
1716
1939
|
isCustom: ctx.isCustomDataDir ?? false,
|
|
1717
1940
|
canSetDataDir: ctx.canSetDataDir ?? Boolean(ctx.setDataDir),
|
|
1718
|
-
hasLegacyDataAtDefaultDir: Boolean(ctx.defaultUserDataDir) &&
|
|
1941
|
+
hasLegacyDataAtDefaultDir: Boolean(ctx.defaultUserDataDir) && path5.resolve(pp.getUserDataDir()) !== path5.resolve(ctx.defaultUserDataDir ?? "") && hasChatLabDatabases(ctx.defaultUserDataDir ?? ""),
|
|
1719
1942
|
pendingMigration: pending ? {
|
|
1720
1943
|
from: pending.from,
|
|
1721
1944
|
to: pending.to,
|
|
@@ -1732,8 +1955,8 @@ function registerCacheRoutes(server2, ctx) {
|
|
|
1732
1955
|
return ctx.setDataDir(targetPath, migrate);
|
|
1733
1956
|
});
|
|
1734
1957
|
server2.get("/_web/cache/latest-import-log", async () => {
|
|
1735
|
-
const importLogDir =
|
|
1736
|
-
if (!
|
|
1958
|
+
const importLogDir = path5.join(pp.getLogsDir(), "import");
|
|
1959
|
+
if (!fs4.existsSync(importLogDir)) {
|
|
1737
1960
|
return { success: false, error: "Log directory not found" };
|
|
1738
1961
|
}
|
|
1739
1962
|
const files = await fsp.readdir(importLogDir);
|
|
@@ -1743,7 +1966,7 @@ function registerCacheRoutes(server2, ctx) {
|
|
|
1743
1966
|
}
|
|
1744
1967
|
const fileStats = await Promise.all(
|
|
1745
1968
|
logFiles.map(async (f) => {
|
|
1746
|
-
const filePath =
|
|
1969
|
+
const filePath = path5.join(importLogDir, f);
|
|
1747
1970
|
const stat2 = await fsp.stat(filePath);
|
|
1748
1971
|
return { name: f, path: filePath, mtime: stat2.mtime.getTime() };
|
|
1749
1972
|
})
|
|
@@ -1767,11 +1990,11 @@ function registerCacheRoutes(server2, ctx) {
|
|
|
1767
1990
|
const base64Data = dataUrl.replace(/^data:[^,]+,/, "");
|
|
1768
1991
|
buffer = Buffer.from(base64Data, "base64");
|
|
1769
1992
|
}
|
|
1770
|
-
if (!
|
|
1771
|
-
|
|
1993
|
+
if (!fs4.existsSync(downloadsDir)) {
|
|
1994
|
+
fs4.mkdirSync(downloadsDir, { recursive: true });
|
|
1772
1995
|
}
|
|
1773
|
-
const filePath =
|
|
1774
|
-
|
|
1996
|
+
const filePath = path5.join(downloadsDir, filename);
|
|
1997
|
+
fs4.writeFileSync(filePath, buffer);
|
|
1775
1998
|
return { success: true, filePath };
|
|
1776
1999
|
});
|
|
1777
2000
|
server2.post("/_web/cache/open-dir", async (request, reply) => {
|
|
@@ -1787,7 +2010,7 @@ function registerCacheRoutes(server2, ctx) {
|
|
|
1787
2010
|
};
|
|
1788
2011
|
const dirPath = dirPaths[cacheId];
|
|
1789
2012
|
if (!dirPath) return { success: false, error: "Unknown directory" };
|
|
1790
|
-
if (!
|
|
2013
|
+
if (!fs4.existsSync(dirPath)) {
|
|
1791
2014
|
await fsp.mkdir(dirPath, { recursive: true });
|
|
1792
2015
|
}
|
|
1793
2016
|
try {
|
|
@@ -1812,40 +2035,41 @@ function registerCacheRoutes(server2, ctx) {
|
|
|
1812
2035
|
|
|
1813
2036
|
// ../../packages/http-routes/src/register.ts
|
|
1814
2037
|
function registerSharedRoutes(server2, ctx, options) {
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
2038
|
+
const resolvedCtx = ctx.preferencesManager ? ctx : { ...ctx, preferencesManager: new PreferencesManager(ctx.pathProvider.getSystemDir()) };
|
|
2039
|
+
registerSystemRoutes(server2, resolvedCtx);
|
|
2040
|
+
registerRestSessionRoutes(server2, resolvedCtx);
|
|
2041
|
+
registerSessionRoutes(server2, resolvedCtx);
|
|
2042
|
+
registerMemberRoutes(server2, resolvedCtx);
|
|
2043
|
+
registerPreferencesRoutes(server2, resolvedCtx);
|
|
2044
|
+
registerAnalyticsRoutes(server2, resolvedCtx);
|
|
2045
|
+
registerSqlRoutes(server2, resolvedCtx);
|
|
2046
|
+
registerSessionIndexRoutes(server2, resolvedCtx);
|
|
2047
|
+
registerExportRoutes(server2, resolvedCtx);
|
|
2048
|
+
registerNlpRoutes(server2, resolvedCtx);
|
|
1825
2049
|
if (options?.requireAi) {
|
|
1826
2050
|
const missing = [];
|
|
1827
|
-
if (!
|
|
1828
|
-
if (!
|
|
1829
|
-
if (!
|
|
1830
|
-
if (!
|
|
1831
|
-
if (!
|
|
1832
|
-
if (!
|
|
1833
|
-
if (!
|
|
1834
|
-
if (!
|
|
2051
|
+
if (!resolvedCtx.aiDataDir) missing.push("aiDataDir");
|
|
2052
|
+
if (!resolvedCtx.aiChatManager) missing.push("aiChatManager");
|
|
2053
|
+
if (!resolvedCtx.assistantManager) missing.push("assistantManager");
|
|
2054
|
+
if (!resolvedCtx.skillManagerCore) missing.push("skillManagerCore");
|
|
2055
|
+
if (!resolvedCtx.llmConfigStore) missing.push("llmConfigStore");
|
|
2056
|
+
if (!resolvedCtx.customProviderStore) missing.push("customProviderStore");
|
|
2057
|
+
if (!resolvedCtx.customModelStore) missing.push("customModelStore");
|
|
2058
|
+
if (!resolvedCtx.runAgentStream) missing.push("runAgentStream");
|
|
1835
2059
|
if (missing.length > 0) {
|
|
1836
2060
|
throw new Error(`[http-routes] requireAi is set but missing AI dependencies: ${missing.join(", ")}`);
|
|
1837
2061
|
}
|
|
1838
2062
|
}
|
|
1839
|
-
registerAiAssistantRoutes(server2,
|
|
1840
|
-
registerAiSkillRoutes(server2,
|
|
1841
|
-
registerAiLlmRoutes(server2,
|
|
1842
|
-
registerAiLlmStreamRoutes(server2,
|
|
1843
|
-
registerAiAgentStreamRoutes(server2,
|
|
1844
|
-
registerAiToolRoutes(server2,
|
|
1845
|
-
registerAiChatRoutes(server2,
|
|
1846
|
-
registerAiSummaryRoutes(server2,
|
|
1847
|
-
registerMergeRoutes(server2,
|
|
1848
|
-
registerCacheRoutes(server2,
|
|
2063
|
+
registerAiAssistantRoutes(server2, resolvedCtx);
|
|
2064
|
+
registerAiSkillRoutes(server2, resolvedCtx);
|
|
2065
|
+
registerAiLlmRoutes(server2, resolvedCtx);
|
|
2066
|
+
registerAiLlmStreamRoutes(server2, resolvedCtx);
|
|
2067
|
+
registerAiAgentStreamRoutes(server2, resolvedCtx);
|
|
2068
|
+
registerAiToolRoutes(server2, resolvedCtx);
|
|
2069
|
+
registerAiChatRoutes(server2, resolvedCtx);
|
|
2070
|
+
registerAiSummaryRoutes(server2, resolvedCtx);
|
|
2071
|
+
registerMergeRoutes(server2, resolvedCtx);
|
|
2072
|
+
registerCacheRoutes(server2, resolvedCtx);
|
|
1849
2073
|
}
|
|
1850
2074
|
|
|
1851
2075
|
// ../../packages/http-routes/src/auth.ts
|
|
@@ -1923,19 +2147,19 @@ function createServer() {
|
|
|
1923
2147
|
|
|
1924
2148
|
// src/http/routes/web/index.ts
|
|
1925
2149
|
import * as os3 from "os";
|
|
1926
|
-
import * as
|
|
2150
|
+
import * as path8 from "path";
|
|
1927
2151
|
|
|
1928
2152
|
// src/http/routes/web/import.ts
|
|
1929
|
-
import * as
|
|
2153
|
+
import * as fs6 from "fs";
|
|
1930
2154
|
import * as os2 from "os";
|
|
1931
|
-
import * as
|
|
2155
|
+
import * as path6 from "path";
|
|
1932
2156
|
|
|
1933
2157
|
// src/http/routes/web/helpers.ts
|
|
1934
|
-
import * as
|
|
2158
|
+
import * as fs5 from "fs";
|
|
1935
2159
|
function resolveNativeBinding() {
|
|
1936
2160
|
if (process.versions.electron) return void 0;
|
|
1937
2161
|
const nativePath = resolveCliPath("native/better_sqlite3.node");
|
|
1938
|
-
if (
|
|
2162
|
+
if (fs5.existsSync(nativePath)) return nativePath;
|
|
1939
2163
|
return void 0;
|
|
1940
2164
|
}
|
|
1941
2165
|
|
|
@@ -1944,11 +2168,11 @@ var DEMO_BASE_URL = "https://chatlab.fun/assets/demo";
|
|
|
1944
2168
|
function cleanupTemp(...paths) {
|
|
1945
2169
|
for (const p of paths) {
|
|
1946
2170
|
try {
|
|
1947
|
-
const stat2 =
|
|
2171
|
+
const stat2 = fs6.statSync(p);
|
|
1948
2172
|
if (stat2.isDirectory()) {
|
|
1949
|
-
|
|
2173
|
+
fs6.rmSync(p, { recursive: true, force: true });
|
|
1950
2174
|
} else {
|
|
1951
|
-
|
|
2175
|
+
fs6.unlinkSync(p);
|
|
1952
2176
|
}
|
|
1953
2177
|
} catch {
|
|
1954
2178
|
}
|
|
@@ -1961,14 +2185,14 @@ function registerImportRoutes(server2, dbManager2) {
|
|
|
1961
2185
|
server2.post("/_web/detect-format", async (request, reply) => {
|
|
1962
2186
|
const data = await request.file();
|
|
1963
2187
|
if (!data) return reply.code(400).send({ error: "No file uploaded" });
|
|
1964
|
-
const tmpDir =
|
|
1965
|
-
const tmpPath =
|
|
2188
|
+
const tmpDir = fs6.mkdtempSync(path6.join(os2.tmpdir(), "chatlab-detect-"));
|
|
2189
|
+
const tmpPath = path6.join(tmpDir, data.filename || "upload");
|
|
1966
2190
|
try {
|
|
1967
2191
|
const chunks = [];
|
|
1968
2192
|
for await (const chunk of data.file) {
|
|
1969
2193
|
chunks.push(chunk);
|
|
1970
2194
|
}
|
|
1971
|
-
|
|
2195
|
+
fs6.writeFileSync(tmpPath, Buffer.concat(chunks));
|
|
1972
2196
|
const format = detectFormat(tmpPath);
|
|
1973
2197
|
const allFormats = detectAllFormats(tmpPath);
|
|
1974
2198
|
return { format, allFormats };
|
|
@@ -1979,14 +2203,14 @@ function registerImportRoutes(server2, dbManager2) {
|
|
|
1979
2203
|
server2.post("/_web/scan-multi-chat", async (request, reply) => {
|
|
1980
2204
|
const data = await request.file();
|
|
1981
2205
|
if (!data) return reply.code(400).send({ error: "No file uploaded" });
|
|
1982
|
-
const tmpDir =
|
|
1983
|
-
const tmpPath =
|
|
2206
|
+
const tmpDir = fs6.mkdtempSync(path6.join(os2.tmpdir(), "chatlab-scan-"));
|
|
2207
|
+
const tmpPath = path6.join(tmpDir, data.filename || "upload");
|
|
1984
2208
|
try {
|
|
1985
2209
|
const chunks = [];
|
|
1986
2210
|
for await (const chunk of data.file) {
|
|
1987
2211
|
chunks.push(chunk);
|
|
1988
2212
|
}
|
|
1989
|
-
|
|
2213
|
+
fs6.writeFileSync(tmpPath, Buffer.concat(chunks));
|
|
1990
2214
|
const chats = await scanMultiChatFile(tmpPath);
|
|
1991
2215
|
return { chats };
|
|
1992
2216
|
} finally {
|
|
@@ -1996,13 +2220,13 @@ function registerImportRoutes(server2, dbManager2) {
|
|
|
1996
2220
|
server2.post("/_web/import", async (request, reply) => {
|
|
1997
2221
|
const data = await request.file();
|
|
1998
2222
|
if (!data) return reply.code(400).send({ error: "No file uploaded" });
|
|
1999
|
-
const tmpDir =
|
|
2000
|
-
const tmpPath =
|
|
2223
|
+
const tmpDir = fs6.mkdtempSync(path6.join(os2.tmpdir(), "chatlab-import-"));
|
|
2224
|
+
const tmpPath = path6.join(tmpDir, data.filename || "upload");
|
|
2001
2225
|
const chunks = [];
|
|
2002
2226
|
for await (const chunk of data.file) {
|
|
2003
2227
|
chunks.push(chunk);
|
|
2004
2228
|
}
|
|
2005
|
-
|
|
2229
|
+
fs6.writeFileSync(tmpPath, Buffer.concat(chunks));
|
|
2006
2230
|
const formatId = data.fields?.formatId?.value;
|
|
2007
2231
|
const chatIndexStr = data.fields?.chatIndex?.value;
|
|
2008
2232
|
const chatIndex = chatIndexStr !== void 0 ? parseInt(chatIndexStr, 10) : void 0;
|
|
@@ -2045,7 +2269,7 @@ data: ${JSON.stringify(eventData)}
|
|
|
2045
2269
|
server2.post("/_web/import-directory", async (request, reply) => {
|
|
2046
2270
|
const parts = request.parts();
|
|
2047
2271
|
if (!parts) return reply.code(400).send({ error: "No files uploaded" });
|
|
2048
|
-
const tmpDir =
|
|
2272
|
+
const tmpDir = fs6.mkdtempSync(path6.join(os2.tmpdir(), "chatlab-dir-import-"));
|
|
2049
2273
|
const relativePaths = [];
|
|
2050
2274
|
const fileBuffers = [];
|
|
2051
2275
|
try {
|
|
@@ -2073,10 +2297,10 @@ data: ${JSON.stringify(eventData)}
|
|
|
2073
2297
|
if (segments.length > 1) {
|
|
2074
2298
|
relPath = segments.slice(1).join("/");
|
|
2075
2299
|
}
|
|
2076
|
-
const targetPath =
|
|
2077
|
-
if (!targetPath.startsWith(tmpDir +
|
|
2078
|
-
|
|
2079
|
-
|
|
2300
|
+
const targetPath = path6.resolve(tmpDir, relPath);
|
|
2301
|
+
if (!targetPath.startsWith(tmpDir + path6.sep)) continue;
|
|
2302
|
+
fs6.mkdirSync(path6.dirname(targetPath), { recursive: true });
|
|
2303
|
+
fs6.writeFileSync(targetPath, fileBuffers[i].data);
|
|
2080
2304
|
}
|
|
2081
2305
|
const entryPath = findEntryFileInDirectory(tmpDir);
|
|
2082
2306
|
if (!entryPath) {
|
|
@@ -2121,13 +2345,13 @@ data: ${JSON.stringify({ success: false, error: err instanceof Error ? err.messa
|
|
|
2121
2345
|
const sessionId = request.params.id;
|
|
2122
2346
|
const data = await request.file();
|
|
2123
2347
|
if (!data) return reply.code(400).send({ error: "No file uploaded" });
|
|
2124
|
-
const tmpDir =
|
|
2125
|
-
const tmpPath =
|
|
2348
|
+
const tmpDir = fs6.mkdtempSync(path6.join(os2.tmpdir(), "chatlab-inc-"));
|
|
2349
|
+
const tmpPath = path6.join(tmpDir, data.filename || "upload");
|
|
2126
2350
|
const chunks = [];
|
|
2127
2351
|
for await (const chunk of data.file) {
|
|
2128
2352
|
chunks.push(chunk);
|
|
2129
2353
|
}
|
|
2130
|
-
|
|
2354
|
+
fs6.writeFileSync(tmpPath, Buffer.concat(chunks));
|
|
2131
2355
|
reply.raw.writeHead(200, {
|
|
2132
2356
|
"Content-Type": "text/event-stream",
|
|
2133
2357
|
"Cache-Control": "no-cache",
|
|
@@ -2159,13 +2383,13 @@ data: ${JSON.stringify(eventData)}
|
|
|
2159
2383
|
const sessionId = request.params.id;
|
|
2160
2384
|
const data = await request.file();
|
|
2161
2385
|
if (!data) return reply.code(400).send({ error: "No file uploaded" });
|
|
2162
|
-
const tmpDir =
|
|
2163
|
-
const tmpPath =
|
|
2386
|
+
const tmpDir = fs6.mkdtempSync(path6.join(os2.tmpdir(), "chatlab-analyze-"));
|
|
2387
|
+
const tmpPath = path6.join(tmpDir, data.filename || "upload");
|
|
2164
2388
|
const chunks = [];
|
|
2165
2389
|
for await (const chunk of data.file) {
|
|
2166
2390
|
chunks.push(chunk);
|
|
2167
2391
|
}
|
|
2168
|
-
|
|
2392
|
+
fs6.writeFileSync(tmpPath, Buffer.concat(chunks));
|
|
2169
2393
|
try {
|
|
2170
2394
|
return await analyzeIncrementalImport(dbManager2, sessionId, tmpPath);
|
|
2171
2395
|
} catch (err) {
|
|
@@ -2177,13 +2401,13 @@ data: ${JSON.stringify(eventData)}
|
|
|
2177
2401
|
server2.post("/_web/import/analyze", async (request, reply) => {
|
|
2178
2402
|
const data = await request.file();
|
|
2179
2403
|
if (!data) return reply.code(400).send({ error: "No file uploaded" });
|
|
2180
|
-
const tmpDir =
|
|
2181
|
-
const tmpPath =
|
|
2404
|
+
const tmpDir = fs6.mkdtempSync(path6.join(os2.tmpdir(), "chatlab-analyze-"));
|
|
2405
|
+
const tmpPath = path6.join(tmpDir, data.filename || "upload");
|
|
2182
2406
|
const chunks = [];
|
|
2183
2407
|
for await (const chunk of data.file) {
|
|
2184
2408
|
chunks.push(chunk);
|
|
2185
2409
|
}
|
|
2186
|
-
|
|
2410
|
+
fs6.writeFileSync(tmpPath, Buffer.concat(chunks));
|
|
2187
2411
|
try {
|
|
2188
2412
|
return await analyzeNewImport(tmpPath);
|
|
2189
2413
|
} catch (err) {
|
|
@@ -2213,17 +2437,17 @@ data: ${JSON.stringify(eventData)}
|
|
|
2213
2437
|
|
|
2214
2438
|
`);
|
|
2215
2439
|
}
|
|
2216
|
-
const tmpDir =
|
|
2440
|
+
const tmpDir = fs6.mkdtempSync(path6.join(os2.tmpdir(), "chatlab-demo-"));
|
|
2217
2441
|
try {
|
|
2218
2442
|
const localPaths = [];
|
|
2219
2443
|
for (let i = 0; i < total; i++) {
|
|
2220
2444
|
sendEvent("progress", { stage: "downloading", current: i + 1, total });
|
|
2221
|
-
const localPath =
|
|
2445
|
+
const localPath = path6.join(tmpDir, DEMO_FILES[i]);
|
|
2222
2446
|
const resp = await fetch(`${DEMO_BASE_URL}/${locale}/${DEMO_FILES[i]}`, {
|
|
2223
2447
|
signal: AbortSignal.timeout(6e4)
|
|
2224
2448
|
});
|
|
2225
2449
|
if (!resp.ok) throw new Error(`Download demo failed (${DEMO_FILES[i]}): ${resp.status}`);
|
|
2226
|
-
|
|
2450
|
+
fs6.writeFileSync(localPath, Buffer.from(await resp.arrayBuffer()));
|
|
2227
2451
|
localPaths.push(localPath);
|
|
2228
2452
|
}
|
|
2229
2453
|
sendEvent("progress", { stage: "importing", current: 1, total });
|
|
@@ -2254,7 +2478,7 @@ data: ${JSON.stringify(eventData)}
|
|
|
2254
2478
|
}
|
|
2255
2479
|
|
|
2256
2480
|
// src/http/routes/web/cache.ts
|
|
2257
|
-
import * as
|
|
2481
|
+
import * as path7 from "path";
|
|
2258
2482
|
import { execFile } from "child_process";
|
|
2259
2483
|
function runExecFile(file, args, runner = execFile) {
|
|
2260
2484
|
return new Promise((resolve4, reject) => {
|
|
@@ -2282,7 +2506,7 @@ async function showPathInFolder(filePath, platform = process.platform, runner) {
|
|
|
2282
2506
|
} else if (platform === "win32") {
|
|
2283
2507
|
await runExecFile("explorer.exe", [`/select,${filePath}`], runner);
|
|
2284
2508
|
} else {
|
|
2285
|
-
await runExecFile("xdg-open", [
|
|
2509
|
+
await runExecFile("xdg-open", [path7.dirname(filePath)], runner);
|
|
2286
2510
|
}
|
|
2287
2511
|
}
|
|
2288
2512
|
|
|
@@ -2305,26 +2529,26 @@ function registerWebRoutes(server2, dbManager2, options) {
|
|
|
2305
2529
|
const mergeCache = options?.pathProvider ? new MergeSessionCache(options.pathProvider, { nativeBinding: options.nativeBinding }) : null;
|
|
2306
2530
|
mergeCache?.cleanupOrphans();
|
|
2307
2531
|
const fallbackPathProvider = {
|
|
2308
|
-
getSystemDir: () =>
|
|
2309
|
-
getUserDataDir: () =>
|
|
2310
|
-
getDatabaseDir: () =>
|
|
2311
|
-
getAiDataDir: () =>
|
|
2312
|
-
getSettingsDir: () =>
|
|
2313
|
-
getCacheDir: () =>
|
|
2314
|
-
getTempDir: () =>
|
|
2315
|
-
getLogsDir: () =>
|
|
2316
|
-
getDownloadsDir: () =>
|
|
2532
|
+
getSystemDir: () => path8.join(os3.homedir(), ".chatlab"),
|
|
2533
|
+
getUserDataDir: () => path8.join(os3.homedir(), ".chatlab", "data"),
|
|
2534
|
+
getDatabaseDir: () => path8.join(os3.homedir(), ".chatlab", "data", "databases"),
|
|
2535
|
+
getAiDataDir: () => path8.join(os3.homedir(), ".chatlab", "ai"),
|
|
2536
|
+
getSettingsDir: () => path8.join(os3.homedir(), ".chatlab", "settings"),
|
|
2537
|
+
getCacheDir: () => path8.join(os3.homedir(), ".chatlab", "cache"),
|
|
2538
|
+
getTempDir: () => path8.join(os3.homedir(), ".chatlab", "temp"),
|
|
2539
|
+
getLogsDir: () => path8.join(os3.homedir(), ".chatlab", "logs"),
|
|
2540
|
+
getDownloadsDir: () => path8.join(os3.homedir(), "Downloads")
|
|
2317
2541
|
};
|
|
2318
2542
|
const resolvedPathProvider = options?.pathProvider ?? fallbackPathProvider;
|
|
2319
2543
|
const ai = options?.aiContext;
|
|
2320
2544
|
const cliStreamImport = async (dm, filePath) => {
|
|
2321
|
-
const { streamImport: streamImport2 } = await import("./stream-import-
|
|
2545
|
+
const { streamImport: streamImport2 } = await import("./stream-import-KZFC3XT2.mjs");
|
|
2322
2546
|
const result = await streamImport2(dm, filePath);
|
|
2323
2547
|
if (!result.sessionId) throw new Error("Import succeeded but no sessionId returned");
|
|
2324
2548
|
return { sessionId: result.sessionId };
|
|
2325
2549
|
};
|
|
2326
2550
|
const defaultUserDataDir = getDefaultNodeUserDataDir();
|
|
2327
|
-
const isCustom =
|
|
2551
|
+
const isCustom = path8.resolve(resolvedPathProvider.getUserDataDir()) !== path8.resolve(defaultUserDataDir);
|
|
2328
2552
|
registerSharedRoutes(
|
|
2329
2553
|
server2,
|
|
2330
2554
|
{
|
|
@@ -2415,31 +2639,31 @@ function registerProxyRoutes(server2) {
|
|
|
2415
2639
|
}
|
|
2416
2640
|
|
|
2417
2641
|
// src/ai/manager-factory.ts
|
|
2418
|
-
import * as
|
|
2419
|
-
import * as
|
|
2642
|
+
import * as fs7 from "fs";
|
|
2643
|
+
import * as path9 from "path";
|
|
2420
2644
|
import { createHash, randomUUID } from "crypto";
|
|
2421
2645
|
var nodeFs = {
|
|
2422
2646
|
ensureDir(dir) {
|
|
2423
|
-
if (!
|
|
2647
|
+
if (!fs7.existsSync(dir)) fs7.mkdirSync(dir, { recursive: true });
|
|
2424
2648
|
},
|
|
2425
2649
|
listFiles(dir, ext) {
|
|
2426
|
-
if (!
|
|
2427
|
-
return
|
|
2650
|
+
if (!fs7.existsSync(dir)) return [];
|
|
2651
|
+
return fs7.readdirSync(dir).filter((f) => f.endsWith(ext));
|
|
2428
2652
|
},
|
|
2429
2653
|
readFile(filePath) {
|
|
2430
|
-
return
|
|
2654
|
+
return fs7.readFileSync(filePath, "utf-8");
|
|
2431
2655
|
},
|
|
2432
2656
|
writeFile(filePath, content) {
|
|
2433
|
-
|
|
2657
|
+
fs7.writeFileSync(filePath, content, "utf-8");
|
|
2434
2658
|
},
|
|
2435
2659
|
deleteFile(filePath) {
|
|
2436
|
-
if (
|
|
2660
|
+
if (fs7.existsSync(filePath)) fs7.unlinkSync(filePath);
|
|
2437
2661
|
},
|
|
2438
2662
|
fileExists(filePath) {
|
|
2439
|
-
return
|
|
2663
|
+
return fs7.existsSync(filePath);
|
|
2440
2664
|
},
|
|
2441
2665
|
joinPath(...parts) {
|
|
2442
|
-
return
|
|
2666
|
+
return path9.join(...parts);
|
|
2443
2667
|
}
|
|
2444
2668
|
};
|
|
2445
2669
|
var managerCache = /* @__PURE__ */ new Map();
|
|
@@ -2449,13 +2673,13 @@ function getAssistantManager(aiDataDir) {
|
|
|
2449
2673
|
cached = {
|
|
2450
2674
|
assistant: new AssistantManager({
|
|
2451
2675
|
fs: nodeFs,
|
|
2452
|
-
assistantsDir:
|
|
2676
|
+
assistantsDir: path9.join(aiDataDir, "assistants"),
|
|
2453
2677
|
builtinRawConfigs: [],
|
|
2454
2678
|
generateId: () => `custom_${randomUUID().replace(/-/g, "").slice(0, 12)}`
|
|
2455
2679
|
}),
|
|
2456
2680
|
skill: new SkillManagerCore({
|
|
2457
2681
|
fs: nodeFs,
|
|
2458
|
-
skillsDir:
|
|
2682
|
+
skillsDir: path9.join(aiDataDir, "skills"),
|
|
2459
2683
|
builtinRawSkills: [],
|
|
2460
2684
|
contentHash: (content) => createHash("md5").update(content).digest("hex")
|
|
2461
2685
|
})
|
|
@@ -2479,8 +2703,8 @@ var noop = () => {
|
|
|
2479
2703
|
var NOOP_LOGGER = { info: noop, warn: noop, error: noop };
|
|
2480
2704
|
|
|
2481
2705
|
// ../../packages/sync/src/data-source-manager.ts
|
|
2482
|
-
import * as
|
|
2483
|
-
import * as
|
|
2706
|
+
import * as fs8 from "fs";
|
|
2707
|
+
import * as path10 from "path";
|
|
2484
2708
|
import * as crypto from "crypto";
|
|
2485
2709
|
var CONFIG_FILE = "data-sources.json";
|
|
2486
2710
|
function generateId(prefix = "ds") {
|
|
@@ -2508,17 +2732,17 @@ var DataSourceManager = class {
|
|
|
2508
2732
|
this.logger = logger ?? NOOP_LOGGER;
|
|
2509
2733
|
}
|
|
2510
2734
|
getConfigPath() {
|
|
2511
|
-
return
|
|
2735
|
+
return path10.join(this.configDir, CONFIG_FILE);
|
|
2512
2736
|
}
|
|
2513
2737
|
ensureDir() {
|
|
2514
|
-
|
|
2738
|
+
fs8.mkdirSync(this.configDir, { recursive: true });
|
|
2515
2739
|
}
|
|
2516
2740
|
// ==================== Load / Save ====================
|
|
2517
2741
|
loadAll() {
|
|
2518
2742
|
try {
|
|
2519
2743
|
const filePath = this.getConfigPath();
|
|
2520
|
-
if (
|
|
2521
|
-
const raw =
|
|
2744
|
+
if (fs8.existsSync(filePath)) {
|
|
2745
|
+
const raw = fs8.readFileSync(filePath, "utf-8");
|
|
2522
2746
|
const parsed = JSON.parse(raw);
|
|
2523
2747
|
if (!isValidDataSourceArray(parsed)) {
|
|
2524
2748
|
this.logger.warn("[DataSource] Incompatible config format detected, returning empty.");
|
|
@@ -2537,7 +2761,7 @@ var DataSourceManager = class {
|
|
|
2537
2761
|
saveAll(sources) {
|
|
2538
2762
|
try {
|
|
2539
2763
|
this.ensureDir();
|
|
2540
|
-
|
|
2764
|
+
fs8.writeFileSync(this.getConfigPath(), JSON.stringify(sources, null, 2), "utf-8");
|
|
2541
2765
|
} catch (err) {
|
|
2542
2766
|
this.logger.error("[DataSource] Failed to save config", err);
|
|
2543
2767
|
}
|
|
@@ -2662,7 +2886,7 @@ function parseRemoteSessionsResponse(body) {
|
|
|
2662
2886
|
}
|
|
2663
2887
|
|
|
2664
2888
|
// ../../packages/sync/src/pull-engine.ts
|
|
2665
|
-
import * as
|
|
2889
|
+
import * as fs9 from "fs";
|
|
2666
2890
|
import * as crypto2 from "crypto";
|
|
2667
2891
|
var MAX_PAGES_PER_PULL = 5e3;
|
|
2668
2892
|
var PULL_OVERLAP_SECONDS = 60;
|
|
@@ -2682,7 +2906,7 @@ function parseSyncFromFile(filePath) {
|
|
|
2682
2906
|
try {
|
|
2683
2907
|
const isJsonl = filePath.endsWith(".jsonl");
|
|
2684
2908
|
if (isJsonl) {
|
|
2685
|
-
const content =
|
|
2909
|
+
const content = fs9.readFileSync(filePath, "utf-8");
|
|
2686
2910
|
const lines = content.trimEnd().split("\n");
|
|
2687
2911
|
for (let i = lines.length - 1; i >= Math.max(0, lines.length - 5); i--) {
|
|
2688
2912
|
try {
|
|
@@ -2701,7 +2925,7 @@ function parseSyncFromFile(filePath) {
|
|
|
2701
2925
|
}
|
|
2702
2926
|
return null;
|
|
2703
2927
|
}
|
|
2704
|
-
const raw =
|
|
2928
|
+
const raw = fs9.readFileSync(filePath, "utf-8");
|
|
2705
2929
|
const parsed = JSON.parse(raw);
|
|
2706
2930
|
if (parsed.sync && typeof parsed.sync === "object") {
|
|
2707
2931
|
const s = parsed.sync;
|
|
@@ -2715,7 +2939,7 @@ function parseSyncFromFile(filePath) {
|
|
|
2715
2939
|
function fileContainsMessages(filePath) {
|
|
2716
2940
|
try {
|
|
2717
2941
|
if (filePath.endsWith(".jsonl")) {
|
|
2718
|
-
const content =
|
|
2942
|
+
const content = fs9.readFileSync(filePath, "utf-8");
|
|
2719
2943
|
for (const line of content.split("\n")) {
|
|
2720
2944
|
const trimmed = line.trim();
|
|
2721
2945
|
if (!trimmed) continue;
|
|
@@ -2728,7 +2952,7 @@ function fileContainsMessages(filePath) {
|
|
|
2728
2952
|
}
|
|
2729
2953
|
return false;
|
|
2730
2954
|
}
|
|
2731
|
-
const raw =
|
|
2955
|
+
const raw = fs9.readFileSync(filePath, "utf-8");
|
|
2732
2956
|
const parsed = JSON.parse(raw);
|
|
2733
2957
|
return Array.isArray(parsed.messages) && parsed.messages.length > 0;
|
|
2734
2958
|
} catch {
|
|
@@ -2737,7 +2961,7 @@ function fileContainsMessages(filePath) {
|
|
|
2737
2961
|
}
|
|
2738
2962
|
function cleanupTempFile(filePath) {
|
|
2739
2963
|
try {
|
|
2740
|
-
if (
|
|
2964
|
+
if (fs9.existsSync(filePath)) fs9.unlinkSync(filePath);
|
|
2741
2965
|
} catch {
|
|
2742
2966
|
}
|
|
2743
2967
|
}
|
|
@@ -2748,6 +2972,7 @@ var PullEngine = class {
|
|
|
2748
2972
|
dsManager;
|
|
2749
2973
|
logger;
|
|
2750
2974
|
isImporting;
|
|
2975
|
+
onSessionImported;
|
|
2751
2976
|
pullingSourceIds = /* @__PURE__ */ new Set();
|
|
2752
2977
|
progressMap = /* @__PURE__ */ new Map();
|
|
2753
2978
|
constructor(options) {
|
|
@@ -2757,6 +2982,7 @@ var PullEngine = class {
|
|
|
2757
2982
|
this.dsManager = options.dsManager;
|
|
2758
2983
|
this.logger = options.logger ?? NOOP_LOGGER;
|
|
2759
2984
|
this.isImporting = options.isImporting ?? (() => false);
|
|
2985
|
+
this.onSessionImported = options.onSessionImported;
|
|
2760
2986
|
}
|
|
2761
2987
|
getProgress() {
|
|
2762
2988
|
return Array.from(this.progressMap.values());
|
|
@@ -2797,7 +3023,7 @@ var PullEngine = class {
|
|
|
2797
3023
|
limit: ds.pullLimit
|
|
2798
3024
|
});
|
|
2799
3025
|
try {
|
|
2800
|
-
const stat2 =
|
|
3026
|
+
const stat2 = fs9.statSync(tempFile);
|
|
2801
3027
|
this.logger.info(`[Pull] "${sess.name}" page ${pageCount}: fetched ${stat2.size} bytes`);
|
|
2802
3028
|
const sync0 = parseSyncFromFile(tempFile);
|
|
2803
3029
|
if (stat2.size < 1024) {
|
|
@@ -2815,7 +3041,7 @@ var PullEngine = class {
|
|
|
2815
3041
|
since,
|
|
2816
3042
|
limit: ds.pullLimit
|
|
2817
3043
|
});
|
|
2818
|
-
const retryStat =
|
|
3044
|
+
const retryStat = fs9.statSync(retryFile);
|
|
2819
3045
|
this.logger.info(`[Pull] "${sess.name}" retry ${ri + 1}: fetched ${retryStat.size} bytes`);
|
|
2820
3046
|
const retrySync = parseSyncFromFile(retryFile);
|
|
2821
3047
|
if (retryStat.size < 1024 && !fileContainsMessages(retryFile) && retrySync?.hasMore === false) {
|
|
@@ -2954,6 +3180,13 @@ var PullEngine = class {
|
|
|
2954
3180
|
if (totalNewMessages > 0) this.notifier.onSessionListChanged();
|
|
2955
3181
|
this.notifier.onPullResult(sourceId, sess.id, "success", `+${totalNewMessages} messages`);
|
|
2956
3182
|
this.markProgressDone(sess.id);
|
|
3183
|
+
if (this.onSessionImported && sess.targetSessionId) {
|
|
3184
|
+
try {
|
|
3185
|
+
this.onSessionImported(sess.targetSessionId);
|
|
3186
|
+
} catch (hookErr) {
|
|
3187
|
+
this.logger.warn(`[Pull] onSessionImported hook failed for "${sess.name}": ${hookErr}`);
|
|
3188
|
+
}
|
|
3189
|
+
}
|
|
2957
3190
|
return { success: true, newMessageCount: totalNewMessages };
|
|
2958
3191
|
} catch (error) {
|
|
2959
3192
|
const errMsg = error.message || "Pull failed";
|
|
@@ -3089,13 +3322,13 @@ function reloadTimer(dsId, skipInitialPull = false) {
|
|
|
3089
3322
|
}
|
|
3090
3323
|
|
|
3091
3324
|
// src/sync/adapters.ts
|
|
3092
|
-
import * as
|
|
3325
|
+
import * as fs10 from "fs";
|
|
3093
3326
|
import * as os4 from "os";
|
|
3094
|
-
import * as
|
|
3327
|
+
import * as path11 from "path";
|
|
3095
3328
|
import * as crypto3 from "crypto";
|
|
3096
3329
|
function getTempFilePath(ext) {
|
|
3097
3330
|
const id = crypto3.randomBytes(8).toString("hex");
|
|
3098
|
-
return
|
|
3331
|
+
return path11.join(os4.tmpdir(), `chatlab-pull-${id}${ext}`);
|
|
3099
3332
|
}
|
|
3100
3333
|
var NodeFetcher = class {
|
|
3101
3334
|
async fetchToTempFile(baseUrl, remoteSessionId, token, params) {
|
|
@@ -3112,7 +3345,7 @@ var NodeFetcher = class {
|
|
|
3112
3345
|
const isJsonl = contentType.includes("ndjson") || contentType.includes("jsonl");
|
|
3113
3346
|
const tempFile = getTempFilePath(isJsonl ? ".jsonl" : ".json");
|
|
3114
3347
|
const buffer = Buffer.from(await response.arrayBuffer());
|
|
3115
|
-
|
|
3348
|
+
fs10.writeFileSync(tempFile, buffer);
|
|
3116
3349
|
return tempFile;
|
|
3117
3350
|
}
|
|
3118
3351
|
};
|
|
@@ -3125,7 +3358,7 @@ var DirectImporter = class {
|
|
|
3125
3358
|
}
|
|
3126
3359
|
sessionExists(sessionId) {
|
|
3127
3360
|
const dbPath = this.dbManager.getDbPath(sessionId);
|
|
3128
|
-
if (!
|
|
3361
|
+
if (!fs10.existsSync(dbPath)) return false;
|
|
3129
3362
|
try {
|
|
3130
3363
|
const db = this.dbManager.openRawSessionDatabase(sessionId, { readonly: true });
|
|
3131
3364
|
const row = db.prepare("SELECT COUNT(*) as cnt FROM sqlite_master WHERE type='table' AND name='message'").get();
|
|
@@ -3133,7 +3366,7 @@ var DirectImporter = class {
|
|
|
3133
3366
|
if (row.cnt === 0) {
|
|
3134
3367
|
this.logger.warn(`[DirectImporter] DB file exists but has no message table: ${sessionId}, removing`);
|
|
3135
3368
|
try {
|
|
3136
|
-
|
|
3369
|
+
fs10.unlinkSync(dbPath);
|
|
3137
3370
|
} catch {
|
|
3138
3371
|
}
|
|
3139
3372
|
return false;
|
|
@@ -3143,7 +3376,7 @@ var DirectImporter = class {
|
|
|
3143
3376
|
if (error instanceof DataDirCompatibilityError) throw error;
|
|
3144
3377
|
this.logger.warn(`[DirectImporter] Cannot validate DB file: ${sessionId}, removing`);
|
|
3145
3378
|
try {
|
|
3146
|
-
|
|
3379
|
+
fs10.unlinkSync(dbPath);
|
|
3147
3380
|
} catch {
|
|
3148
3381
|
}
|
|
3149
3382
|
return false;
|
|
@@ -3259,10 +3492,7 @@ function registerAutomationRoutes(server2, ctx) {
|
|
|
3259
3492
|
if (!removed) return reply.code(404).send({ error: "Session not found" });
|
|
3260
3493
|
reloadTimer(request.params.id);
|
|
3261
3494
|
if (request.query.deleteData === "true" && removed.targetSessionId) {
|
|
3262
|
-
dbManager2.
|
|
3263
|
-
const dbPath = dbManager2.getDbPath(removed.targetSessionId);
|
|
3264
|
-
const fs10 = await import("fs");
|
|
3265
|
-
if (fs10.existsSync(dbPath)) fs10.unlinkSync(dbPath);
|
|
3495
|
+
dbManager2.deleteSessionDatabaseFiles(removed.targetSessionId);
|
|
3266
3496
|
}
|
|
3267
3497
|
return { success: true };
|
|
3268
3498
|
});
|
|
@@ -3314,12 +3544,21 @@ function initSync(server2, dbManager2, pathProvider, serverInfo) {
|
|
|
3314
3544
|
const fetcher = new NodeFetcher();
|
|
3315
3545
|
const importer = new DirectImporter(dbManager2, syncLogger);
|
|
3316
3546
|
const notifier = new NoopNotifier();
|
|
3547
|
+
const sessionAdapter = createDatabaseManagerAdapter(dbManager2);
|
|
3548
|
+
const preferences = new PreferencesManager(pathProvider.getSystemDir());
|
|
3317
3549
|
pullEngine = new PullEngine({
|
|
3318
3550
|
fetcher,
|
|
3319
3551
|
importer,
|
|
3320
3552
|
notifier,
|
|
3321
3553
|
dsManager,
|
|
3322
|
-
logger: syncLogger
|
|
3554
|
+
logger: syncLogger,
|
|
3555
|
+
onSessionImported: (localSessionId) => {
|
|
3556
|
+
preferences.invalidateCache();
|
|
3557
|
+
const result = owner_profile_service_exports.tryApplyOwnerProfile(sessionAdapter, preferences, localSessionId);
|
|
3558
|
+
if (result.applied) {
|
|
3559
|
+
syncLogger.info(`[Pull] Applied owner profile to session ${localSessionId} (owner: ${result.ownerId})`);
|
|
3560
|
+
}
|
|
3561
|
+
}
|
|
3323
3562
|
});
|
|
3324
3563
|
registerAutomationRoutes(server2, { dsManager, pullEngine, dbManager: dbManager2, serverInfo });
|
|
3325
3564
|
initScheduler({
|
|
@@ -3342,21 +3581,21 @@ function createFileConfigStorage(aiDataDir) {
|
|
|
3342
3581
|
return {
|
|
3343
3582
|
readJson(key) {
|
|
3344
3583
|
try {
|
|
3345
|
-
return JSON.parse(
|
|
3584
|
+
return JSON.parse(fs11.readFileSync(`${aiDataDir}/${key}.json`, "utf-8"));
|
|
3346
3585
|
} catch {
|
|
3347
3586
|
return null;
|
|
3348
3587
|
}
|
|
3349
3588
|
},
|
|
3350
3589
|
writeJson(key, data) {
|
|
3351
|
-
if (!
|
|
3352
|
-
|
|
3590
|
+
if (!fs11.existsSync(aiDataDir)) fs11.mkdirSync(aiDataDir, { recursive: true });
|
|
3591
|
+
fs11.writeFileSync(`${aiDataDir}/${key}.json`, JSON.stringify(data, null, 2), "utf-8");
|
|
3353
3592
|
}
|
|
3354
3593
|
};
|
|
3355
3594
|
}
|
|
3356
3595
|
function resolveNativeBinding2() {
|
|
3357
3596
|
if (process.versions.electron) return void 0;
|
|
3358
3597
|
const nativePath = resolveCliPath("native/better_sqlite3.node");
|
|
3359
|
-
if (
|
|
3598
|
+
if (fs11.existsSync(nativePath)) return nativePath;
|
|
3360
3599
|
return void 0;
|
|
3361
3600
|
}
|
|
3362
3601
|
function ensureToken(config) {
|
|
@@ -3442,7 +3681,7 @@ async function startHttpServer(options) {
|
|
|
3442
3681
|
}
|
|
3443
3682
|
});
|
|
3444
3683
|
initSync(server, dbManager, pathProvider, { port, host, token });
|
|
3445
|
-
if (options?.webRoot &&
|
|
3684
|
+
if (options?.webRoot && fs11.existsSync(options.webRoot)) {
|
|
3446
3685
|
registerProxyRoutes(server);
|
|
3447
3686
|
const fastifyStatic = await import("@fastify/static");
|
|
3448
3687
|
await server.register(fastifyStatic.default, {
|
|
@@ -3483,4 +3722,4 @@ export {
|
|
|
3483
3722
|
startHttpServer,
|
|
3484
3723
|
stopHttpServer
|
|
3485
3724
|
};
|
|
3486
|
-
//# sourceMappingURL=chunk-
|
|
3725
|
+
//# sourceMappingURL=chunk-QMXNJRTT.mjs.map
|