chatlab-cli 0.21.0 → 0.22.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 +5 -5
- package/dist/{chunk-XUHPCRVT.mjs → chunk-3UBWLWXB.mjs} +2 -2
- package/dist/{chunk-EWCSGYVT.mjs → chunk-4U45E7TO.mjs} +2 -2
- package/dist/{chunk-NB4BIAYM.mjs → chunk-6GLMVWGA.mjs} +24 -15
- package/dist/chunk-6GLMVWGA.mjs.map +1 -0
- package/dist/{chunk-2AIOU3PW.mjs → chunk-6NB475AA.mjs} +12 -3
- package/dist/chunk-6NB475AA.mjs.map +1 -0
- package/dist/{chunk-FBTHZU55.mjs → chunk-AMMSVJG4.mjs} +507 -115
- package/dist/chunk-AMMSVJG4.mjs.map +1 -0
- package/dist/{chunk-3DIWXYMJ.mjs → chunk-D2XJ4Z7G.mjs} +2 -2
- package/dist/{chunk-EIRLEPXP.mjs → chunk-QJQJIUUS.mjs} +107 -96
- package/dist/chunk-QJQJIUUS.mjs.map +1 -0
- package/dist/chunk-VSOWNKPA.mjs +49 -0
- package/dist/cli.mjs +4 -3
- package/dist/{http-QVYJ5HER.mjs → http-R5UECMPJ.mjs} +8 -7
- package/dist/{import-AJKL6JPR.mjs → import-3LOWRXWA.mjs} +5 -4
- package/dist/index.mjs +8 -7
- package/dist/{mcp-FRF3YAN5.mjs → mcp-WBHNLED2.mjs} +8 -7
- package/dist/mcp-WBHNLED2.mjs.map +1 -0
- package/dist/{src-KOIGBZJX.mjs → src-UHM2UDRD.mjs} +3 -2
- package/dist/{stream-import-URGOCXUH.mjs → stream-import-V6Z7DXSW.mjs} +4 -3
- package/dist/stream-import-V6Z7DXSW.mjs.map +1 -0
- package/dist/update-checker-MQMQ6A4B.mjs +191 -0
- package/dist/update-checker-MQMQ6A4B.mjs.map +1 -0
- package/dist-web/assets/{DebugToolsPanel-dUV6pVqD.css → DebugToolsPanel-DkbsB6lr.css} +1 -1
- package/dist-web/assets/DebugToolsPanel.vue_vue_type_script_setup_true_lang-CxYyne-Q.js +147 -0
- package/dist-web/assets/{EChartWordcloud-BYLFxPAX.js → EChartWordcloud-Ubal_aYg.js} +1 -1
- package/dist-web/assets/{MessageExportModal-BAf68-wB.js → MessageExportModal-De5_tX6a.js} +1 -1
- package/dist-web/assets/{electron-CTK4NJag.js → electron-CRSL7qPM.js} +1 -1
- package/dist-web/assets/electron-H65bUnhW.js +1 -0
- package/dist-web/assets/{fetch-Ayp337Ng.js → fetch-Q-PqsiMb.js} +1 -1
- package/dist-web/assets/fetch-cFjLAPG2.js +1 -0
- package/dist-web/assets/{index-CmU6x-uJ.css → index-BE64tUWq.css} +1 -1
- package/dist-web/assets/index-BJUGnYn5.js +24 -0
- package/dist-web/assets/index-B_31fXeB.js +2 -0
- package/dist-web/assets/index-CbOwHYzO.css +1 -0
- package/dist-web/assets/index-CbQMfheg.css +1 -0
- package/dist-web/assets/index-DKMUn8F5.js +173 -0
- package/dist-web/assets/{index-Bd5r2oFG.js → index-D_ksezTw.js} +4 -4
- package/dist-web/assets/index-Dhg3SDb6.js +7 -0
- package/dist-web/assets/logo-DXIuG_WE.svg +17 -0
- package/dist-web/assets/{vendor-echarts-C13vDgZW.js → vendor-echarts-C5u62NnM.js} +1 -1
- package/dist-web/assets/{vendor-echarts-wordcloud-uVdwYsYa.js → vendor-echarts-wordcloud-CZ9gsX8l.js} +1 -1
- package/dist-web/assets/{vendor-nuxt-ui-CYMNNdeD.js → vendor-nuxt-ui-7OAuiz_i.js} +1 -1
- package/dist-web/assets/{vendor-zrender-5DcYjSZk.js → vendor-zrender-DFl4U2Oe.js} +1 -1
- package/dist-web/assets/{web-C5yfcwjn.js → web-CZe0Ismu.js} +1 -1
- package/dist-web/index.html +3 -3
- package/package.json +4 -9
- package/dist/chunk-2AIOU3PW.mjs.map +0 -1
- package/dist/chunk-EIRLEPXP.mjs.map +0 -1
- package/dist/chunk-FBTHZU55.mjs.map +0 -1
- package/dist/chunk-NB4BIAYM.mjs.map +0 -1
- package/dist/mcp-FRF3YAN5.mjs.map +0 -1
- package/dist-web/assets/DebugToolsPanel.vue_vue_type_script_setup_true_lang-DFN1Co1H.js +0 -155
- package/dist-web/assets/electron-BHjOx6B6.js +0 -1
- package/dist-web/assets/fetch-BWtpQwBZ.js +0 -1
- package/dist-web/assets/index-BfZM-q0T.js +0 -7
- package/dist-web/assets/index-CLGv8Den.css +0 -1
- package/dist-web/assets/index-CLYHD_f4.js +0 -2
- package/dist-web/assets/index-CaTRpNBK.js +0 -16
- package/dist-web/assets/index-CzR1sElx.css +0 -1
- package/dist-web/assets/index-Ksa--o23.js +0 -173
- /package/dist/{chunk-XUHPCRVT.mjs.map → chunk-3UBWLWXB.mjs.map} +0 -0
- /package/dist/{chunk-EWCSGYVT.mjs.map → chunk-4U45E7TO.mjs.map} +0 -0
- /package/dist/{chunk-3DIWXYMJ.mjs.map → chunk-D2XJ4Z7G.mjs.map} +0 -0
- /package/dist/{http-QVYJ5HER.mjs.map → chunk-VSOWNKPA.mjs.map} +0 -0
- /package/dist/{import-AJKL6JPR.mjs.map → http-R5UECMPJ.mjs.map} +0 -0
- /package/dist/{src-KOIGBZJX.mjs.map → import-3LOWRXWA.mjs.map} +0 -0
- /package/dist/{stream-import-URGOCXUH.mjs.map → src-UHM2UDRD.mjs.map} +0 -0
|
@@ -7,20 +7,21 @@ const __dirname = __pathDirname(__filename);
|
|
|
7
7
|
import {
|
|
8
8
|
importData,
|
|
9
9
|
parseFile
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-3UBWLWXB.mjs";
|
|
11
11
|
import {
|
|
12
12
|
analyzeIncrementalImport,
|
|
13
13
|
analyzeNewImport,
|
|
14
14
|
incrementalImport,
|
|
15
15
|
streamImport
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-4U45E7TO.mjs";
|
|
17
17
|
import {
|
|
18
18
|
AGENT_TOOL_REGISTRY,
|
|
19
19
|
CoreDataProvider
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-D2XJ4Z7G.mjs";
|
|
21
21
|
import {
|
|
22
|
-
getVersion
|
|
23
|
-
|
|
22
|
+
getVersion,
|
|
23
|
+
resolveCliPath
|
|
24
|
+
} from "./chunk-6NB475AA.mjs";
|
|
24
25
|
import {
|
|
25
26
|
AIConversationManager,
|
|
26
27
|
ALL_MIGRATIONS,
|
|
@@ -114,11 +115,10 @@ import {
|
|
|
114
115
|
verifyCliDataPath,
|
|
115
116
|
writeAuthProfile,
|
|
116
117
|
writeConfigField
|
|
117
|
-
} from "./chunk-
|
|
118
|
+
} from "./chunk-QJQJIUUS.mjs";
|
|
118
119
|
|
|
119
120
|
// src/http/index.ts
|
|
120
121
|
import * as fs14 from "fs";
|
|
121
|
-
import * as path15 from "path";
|
|
122
122
|
import * as crypto5 from "crypto";
|
|
123
123
|
|
|
124
124
|
// src/http/server.ts
|
|
@@ -207,6 +207,10 @@ async function authHook(request, reply) {
|
|
|
207
207
|
if (!cachedToken) return;
|
|
208
208
|
if (request.url.startsWith("/_web/")) return;
|
|
209
209
|
if (webModeEnabled && !request.url.startsWith("/api/")) return;
|
|
210
|
+
return requireBearerToken(request, reply);
|
|
211
|
+
}
|
|
212
|
+
function requireBearerToken(request, reply) {
|
|
213
|
+
if (!cachedToken) return;
|
|
210
214
|
const authHeader = request.headers.authorization;
|
|
211
215
|
if (!authHeader || !authHeader.startsWith("Bearer ")) {
|
|
212
216
|
const err = unauthorized();
|
|
@@ -492,7 +496,7 @@ function registerSessionRoutes(server2, dbManager2) {
|
|
|
492
496
|
|
|
493
497
|
// src/http/routes/web/index.ts
|
|
494
498
|
import * as os3 from "os";
|
|
495
|
-
import * as
|
|
499
|
+
import * as path6 from "path";
|
|
496
500
|
|
|
497
501
|
// src/merger/merge-cache.ts
|
|
498
502
|
import * as fs from "fs";
|
|
@@ -653,7 +657,6 @@ function registerMemberRoutes(server2, adapter) {
|
|
|
653
657
|
|
|
654
658
|
// src/http/routes/web/helpers.ts
|
|
655
659
|
import * as fs2 from "fs";
|
|
656
|
-
import * as path2 from "path";
|
|
657
660
|
function parseTimeFilter(query) {
|
|
658
661
|
const { startTs, endTs, memberId } = query;
|
|
659
662
|
if (!startTs && !endTs && !memberId) return void 0;
|
|
@@ -665,7 +668,7 @@ function parseTimeFilter(query) {
|
|
|
665
668
|
}
|
|
666
669
|
function resolveNativeBinding() {
|
|
667
670
|
if (process.versions.electron) return void 0;
|
|
668
|
-
const nativePath =
|
|
671
|
+
const nativePath = resolveCliPath("native/better_sqlite3.node");
|
|
669
672
|
if (fs2.existsSync(nativePath)) return nativePath;
|
|
670
673
|
return void 0;
|
|
671
674
|
}
|
|
@@ -849,6 +852,11 @@ function registerSessionIndexRoutes(server2, adapter) {
|
|
|
849
852
|
const sessionCount = session_index_service_exports.generateIndex(adapter, request.params.id, gapThreshold);
|
|
850
853
|
return { sessionCount };
|
|
851
854
|
});
|
|
855
|
+
server2.post("/_web/sessions/:id/generate-incremental-index", async (request) => {
|
|
856
|
+
const gapThreshold = request.body?.gapThreshold ?? 1800;
|
|
857
|
+
const sessionCount = session_index_service_exports.generateIncrementalIndex(adapter, request.params.id, gapThreshold);
|
|
858
|
+
return { sessionCount };
|
|
859
|
+
});
|
|
852
860
|
server2.post("/_web/sessions/:id/clear-index", async (request) => {
|
|
853
861
|
session_index_service_exports.clearIndex(adapter, request.params.id);
|
|
854
862
|
return { success: true };
|
|
@@ -874,10 +882,10 @@ function registerSessionIndexRoutes(server2, adapter) {
|
|
|
874
882
|
|
|
875
883
|
// src/ai/llm-config.ts
|
|
876
884
|
import * as fs3 from "fs";
|
|
877
|
-
import * as
|
|
885
|
+
import * as path2 from "path";
|
|
878
886
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
879
887
|
function loadLlmConfig(aiDataDir) {
|
|
880
|
-
const configPath =
|
|
888
|
+
const configPath = path2.join(aiDataDir, "llm-config.json");
|
|
881
889
|
if (!fs3.existsSync(configPath)) {
|
|
882
890
|
return { configs: [], defaultAssistant: null, fastModel: null };
|
|
883
891
|
}
|
|
@@ -919,7 +927,7 @@ function getDefaultAssistantConfig(aiDataDir) {
|
|
|
919
927
|
}
|
|
920
928
|
var MAX_CONFIG_COUNT = 99;
|
|
921
929
|
function saveLlmConfig(aiDataDir, store) {
|
|
922
|
-
const configPath =
|
|
930
|
+
const configPath = path2.join(aiDataDir, "llm-config.json");
|
|
923
931
|
const toSave = {
|
|
924
932
|
...store,
|
|
925
933
|
configs: store.configs.map((c) => {
|
|
@@ -1023,7 +1031,7 @@ function setFastModelSlot(aiDataDir, slot) {
|
|
|
1023
1031
|
return { success: true };
|
|
1024
1032
|
}
|
|
1025
1033
|
function loadRawConfigStore(aiDataDir) {
|
|
1026
|
-
const configPath =
|
|
1034
|
+
const configPath = path2.join(aiDataDir, "llm-config.json");
|
|
1027
1035
|
if (!fs3.existsSync(configPath)) {
|
|
1028
1036
|
return { configs: [], defaultAssistant: null, fastModel: null };
|
|
1029
1037
|
}
|
|
@@ -1083,13 +1091,13 @@ function registerSummaryRoutes(server2, dbManager2, adapter) {
|
|
|
1083
1091
|
// src/http/routes/web/import.ts
|
|
1084
1092
|
import * as fs4 from "fs";
|
|
1085
1093
|
import * as os from "os";
|
|
1086
|
-
import * as
|
|
1094
|
+
import * as path3 from "path";
|
|
1087
1095
|
var DEMO_BASE_URL = "https://chatlab.fun/assets/demo";
|
|
1088
1096
|
function cleanupTemp(...paths) {
|
|
1089
1097
|
for (const p of paths) {
|
|
1090
1098
|
try {
|
|
1091
|
-
const
|
|
1092
|
-
if (
|
|
1099
|
+
const stat2 = fs4.statSync(p);
|
|
1100
|
+
if (stat2.isDirectory()) {
|
|
1093
1101
|
fs4.rmSync(p, { recursive: true, force: true });
|
|
1094
1102
|
} else {
|
|
1095
1103
|
fs4.unlinkSync(p);
|
|
@@ -1105,8 +1113,8 @@ function registerImportRoutes(server2, dbManager2) {
|
|
|
1105
1113
|
server2.post("/_web/detect-format", async (request, reply) => {
|
|
1106
1114
|
const data = await request.file();
|
|
1107
1115
|
if (!data) return reply.code(400).send({ error: "No file uploaded" });
|
|
1108
|
-
const tmpDir = fs4.mkdtempSync(
|
|
1109
|
-
const tmpPath =
|
|
1116
|
+
const tmpDir = fs4.mkdtempSync(path3.join(os.tmpdir(), "chatlab-detect-"));
|
|
1117
|
+
const tmpPath = path3.join(tmpDir, data.filename || "upload");
|
|
1110
1118
|
try {
|
|
1111
1119
|
const chunks = [];
|
|
1112
1120
|
for await (const chunk of data.file) {
|
|
@@ -1123,8 +1131,8 @@ function registerImportRoutes(server2, dbManager2) {
|
|
|
1123
1131
|
server2.post("/_web/scan-multi-chat", async (request, reply) => {
|
|
1124
1132
|
const data = await request.file();
|
|
1125
1133
|
if (!data) return reply.code(400).send({ error: "No file uploaded" });
|
|
1126
|
-
const tmpDir = fs4.mkdtempSync(
|
|
1127
|
-
const tmpPath =
|
|
1134
|
+
const tmpDir = fs4.mkdtempSync(path3.join(os.tmpdir(), "chatlab-scan-"));
|
|
1135
|
+
const tmpPath = path3.join(tmpDir, data.filename || "upload");
|
|
1128
1136
|
try {
|
|
1129
1137
|
const chunks = [];
|
|
1130
1138
|
for await (const chunk of data.file) {
|
|
@@ -1140,8 +1148,8 @@ function registerImportRoutes(server2, dbManager2) {
|
|
|
1140
1148
|
server2.post("/_web/import", async (request, reply) => {
|
|
1141
1149
|
const data = await request.file();
|
|
1142
1150
|
if (!data) return reply.code(400).send({ error: "No file uploaded" });
|
|
1143
|
-
const tmpDir = fs4.mkdtempSync(
|
|
1144
|
-
const tmpPath =
|
|
1151
|
+
const tmpDir = fs4.mkdtempSync(path3.join(os.tmpdir(), "chatlab-import-"));
|
|
1152
|
+
const tmpPath = path3.join(tmpDir, data.filename || "upload");
|
|
1145
1153
|
const chunks = [];
|
|
1146
1154
|
for await (const chunk of data.file) {
|
|
1147
1155
|
chunks.push(chunk);
|
|
@@ -1189,7 +1197,7 @@ data: ${JSON.stringify(eventData)}
|
|
|
1189
1197
|
server2.post("/_web/import-directory", async (request, reply) => {
|
|
1190
1198
|
const parts = request.parts();
|
|
1191
1199
|
if (!parts) return reply.code(400).send({ error: "No files uploaded" });
|
|
1192
|
-
const tmpDir = fs4.mkdtempSync(
|
|
1200
|
+
const tmpDir = fs4.mkdtempSync(path3.join(os.tmpdir(), "chatlab-dir-import-"));
|
|
1193
1201
|
const relativePaths = [];
|
|
1194
1202
|
const fileBuffers = [];
|
|
1195
1203
|
try {
|
|
@@ -1217,9 +1225,9 @@ data: ${JSON.stringify(eventData)}
|
|
|
1217
1225
|
if (segments.length > 1) {
|
|
1218
1226
|
relPath = segments.slice(1).join("/");
|
|
1219
1227
|
}
|
|
1220
|
-
const targetPath =
|
|
1221
|
-
if (!targetPath.startsWith(tmpDir +
|
|
1222
|
-
fs4.mkdirSync(
|
|
1228
|
+
const targetPath = path3.resolve(tmpDir, relPath);
|
|
1229
|
+
if (!targetPath.startsWith(tmpDir + path3.sep)) continue;
|
|
1230
|
+
fs4.mkdirSync(path3.dirname(targetPath), { recursive: true });
|
|
1223
1231
|
fs4.writeFileSync(targetPath, fileBuffers[i].data);
|
|
1224
1232
|
}
|
|
1225
1233
|
const entryPath = findEntryFileInDirectory(tmpDir);
|
|
@@ -1265,8 +1273,8 @@ data: ${JSON.stringify({ success: false, error: err instanceof Error ? err.messa
|
|
|
1265
1273
|
const sessionId = request.params.id;
|
|
1266
1274
|
const data = await request.file();
|
|
1267
1275
|
if (!data) return reply.code(400).send({ error: "No file uploaded" });
|
|
1268
|
-
const tmpDir = fs4.mkdtempSync(
|
|
1269
|
-
const tmpPath =
|
|
1276
|
+
const tmpDir = fs4.mkdtempSync(path3.join(os.tmpdir(), "chatlab-inc-"));
|
|
1277
|
+
const tmpPath = path3.join(tmpDir, data.filename || "upload");
|
|
1270
1278
|
const chunks = [];
|
|
1271
1279
|
for await (const chunk of data.file) {
|
|
1272
1280
|
chunks.push(chunk);
|
|
@@ -1303,8 +1311,8 @@ data: ${JSON.stringify(eventData)}
|
|
|
1303
1311
|
const sessionId = request.params.id;
|
|
1304
1312
|
const data = await request.file();
|
|
1305
1313
|
if (!data) return reply.code(400).send({ error: "No file uploaded" });
|
|
1306
|
-
const tmpDir = fs4.mkdtempSync(
|
|
1307
|
-
const tmpPath =
|
|
1314
|
+
const tmpDir = fs4.mkdtempSync(path3.join(os.tmpdir(), "chatlab-analyze-"));
|
|
1315
|
+
const tmpPath = path3.join(tmpDir, data.filename || "upload");
|
|
1308
1316
|
const chunks = [];
|
|
1309
1317
|
for await (const chunk of data.file) {
|
|
1310
1318
|
chunks.push(chunk);
|
|
@@ -1321,8 +1329,8 @@ data: ${JSON.stringify(eventData)}
|
|
|
1321
1329
|
server2.post("/_web/import/analyze", async (request, reply) => {
|
|
1322
1330
|
const data = await request.file();
|
|
1323
1331
|
if (!data) return reply.code(400).send({ error: "No file uploaded" });
|
|
1324
|
-
const tmpDir = fs4.mkdtempSync(
|
|
1325
|
-
const tmpPath =
|
|
1332
|
+
const tmpDir = fs4.mkdtempSync(path3.join(os.tmpdir(), "chatlab-analyze-"));
|
|
1333
|
+
const tmpPath = path3.join(tmpDir, data.filename || "upload");
|
|
1326
1334
|
const chunks = [];
|
|
1327
1335
|
for await (const chunk of data.file) {
|
|
1328
1336
|
chunks.push(chunk);
|
|
@@ -1350,9 +1358,9 @@ data: ${JSON.stringify(eventData)}
|
|
|
1350
1358
|
|
|
1351
1359
|
`);
|
|
1352
1360
|
}
|
|
1353
|
-
const tmpDir = fs4.mkdtempSync(
|
|
1354
|
-
const groupPath =
|
|
1355
|
-
const privatePath =
|
|
1361
|
+
const tmpDir = fs4.mkdtempSync(path3.join(os.tmpdir(), "chatlab-demo-"));
|
|
1362
|
+
const groupPath = path3.join(tmpDir, "demo-group.json");
|
|
1363
|
+
const privatePath = path3.join(tmpDir, "demo-private.json");
|
|
1356
1364
|
try {
|
|
1357
1365
|
sendEvent("progress", { stage: "downloading", current: 1, total: 2 });
|
|
1358
1366
|
const groupResp = await fetch(`${DEMO_BASE_URL}/${locale}/demo-group.json`, {
|
|
@@ -1392,13 +1400,13 @@ data: ${JSON.stringify(eventData)}
|
|
|
1392
1400
|
// src/http/routes/web/merge.ts
|
|
1393
1401
|
import * as fs5 from "fs";
|
|
1394
1402
|
import * as os2 from "os";
|
|
1395
|
-
import * as
|
|
1403
|
+
import * as path4 from "path";
|
|
1396
1404
|
function registerMergeRoutes(server2, dbManager2, mergeCache) {
|
|
1397
1405
|
server2.post("/_web/merge/parse", async (request, reply) => {
|
|
1398
1406
|
const data = await request.file();
|
|
1399
1407
|
if (!data) return reply.code(400).send({ error: "No file uploaded" });
|
|
1400
|
-
const tmpDir = fs5.mkdtempSync(
|
|
1401
|
-
const tmpPath =
|
|
1408
|
+
const tmpDir = fs5.mkdtempSync(path4.join(os2.tmpdir(), "chatlab-merge-"));
|
|
1409
|
+
const tmpPath = path4.join(tmpDir, data.filename || "upload");
|
|
1402
1410
|
const chunks = [];
|
|
1403
1411
|
for await (const chunk of data.file) {
|
|
1404
1412
|
chunks.push(chunk);
|
|
@@ -1407,7 +1415,7 @@ function registerMergeRoutes(server2, dbManager2, mergeCache) {
|
|
|
1407
1415
|
try {
|
|
1408
1416
|
const result = await streamParseFileInfo(tmpPath, {
|
|
1409
1417
|
createTempDatabase(sourceFilePath) {
|
|
1410
|
-
return mergeCache.createTempDatabase(
|
|
1418
|
+
return mergeCache.createTempDatabase(path4.basename(sourceFilePath));
|
|
1411
1419
|
},
|
|
1412
1420
|
onProgress() {
|
|
1413
1421
|
}
|
|
@@ -1472,10 +1480,10 @@ function registerMergeRoutes(server2, dbManager2, mergeCache) {
|
|
|
1472
1480
|
const merged = buildMergedOutput(dataSources, outputName);
|
|
1473
1481
|
let sessionId;
|
|
1474
1482
|
if (andImport) {
|
|
1475
|
-
const { streamImport: serverStreamImport } = await import("./stream-import-
|
|
1483
|
+
const { streamImport: serverStreamImport } = await import("./stream-import-V6Z7DXSW.mjs");
|
|
1476
1484
|
const jsonData = JSON.stringify(merged.chatLabData);
|
|
1477
|
-
const tmpDir = fs5.mkdtempSync(
|
|
1478
|
-
const tmpPath =
|
|
1485
|
+
const tmpDir = fs5.mkdtempSync(path4.join(os2.tmpdir(), "chatlab-merged-"));
|
|
1486
|
+
const tmpPath = path4.join(tmpDir, `${outputName}.json`);
|
|
1479
1487
|
fs5.writeFileSync(tmpPath, jsonData, "utf-8");
|
|
1480
1488
|
try {
|
|
1481
1489
|
const importResult = await serverStreamImport(dbManager2, tmpPath);
|
|
@@ -1525,7 +1533,7 @@ function registerMergeRoutes(server2, dbManager2, mergeCache) {
|
|
|
1525
1533
|
const db = ensureDb2(dbManager2, sid);
|
|
1526
1534
|
const exported = exportSessionToJson(db);
|
|
1527
1535
|
const { db: tempDb, tempDbPath } = mergeCache.createTempDatabase(exported.meta.name);
|
|
1528
|
-
const { TempDbWriter } = await import("./src-
|
|
1536
|
+
const { TempDbWriter } = await import("./src-UHM2UDRD.mjs");
|
|
1529
1537
|
const writer = new TempDbWriter(tempDb);
|
|
1530
1538
|
writer.writeMeta({
|
|
1531
1539
|
name: exported.meta.name,
|
|
@@ -1587,8 +1595,192 @@ function registerExportRoutes(server2, adapter) {
|
|
|
1587
1595
|
|
|
1588
1596
|
// src/http/routes/web/cache.ts
|
|
1589
1597
|
import * as fs6 from "fs";
|
|
1590
|
-
import * as
|
|
1591
|
-
|
|
1598
|
+
import * as fsp from "fs/promises";
|
|
1599
|
+
import * as path5 from "path";
|
|
1600
|
+
import { execFile } from "child_process";
|
|
1601
|
+
function runExecFile(file, args, runner = execFile) {
|
|
1602
|
+
return new Promise((resolve2, reject) => {
|
|
1603
|
+
runner(file, args, (error) => {
|
|
1604
|
+
if (error) {
|
|
1605
|
+
reject(error);
|
|
1606
|
+
return;
|
|
1607
|
+
}
|
|
1608
|
+
resolve2();
|
|
1609
|
+
});
|
|
1610
|
+
});
|
|
1611
|
+
}
|
|
1612
|
+
async function openDirectoryPath(dirPath, platform = process.platform, runner) {
|
|
1613
|
+
if (platform === "darwin") {
|
|
1614
|
+
await runExecFile("open", [dirPath], runner);
|
|
1615
|
+
} else if (platform === "win32") {
|
|
1616
|
+
await runExecFile("explorer.exe", [dirPath], runner);
|
|
1617
|
+
} else {
|
|
1618
|
+
await runExecFile("xdg-open", [dirPath], runner);
|
|
1619
|
+
}
|
|
1620
|
+
}
|
|
1621
|
+
async function showPathInFolder(filePath, platform = process.platform, runner) {
|
|
1622
|
+
if (platform === "darwin") {
|
|
1623
|
+
await runExecFile("open", ["-R", filePath], runner);
|
|
1624
|
+
} else if (platform === "win32") {
|
|
1625
|
+
await runExecFile("explorer.exe", [`/select,${filePath}`], runner);
|
|
1626
|
+
} else {
|
|
1627
|
+
await runExecFile("xdg-open", [path5.dirname(filePath)], runner);
|
|
1628
|
+
}
|
|
1629
|
+
}
|
|
1630
|
+
async function getDirSize(dirPath) {
|
|
1631
|
+
let totalSize = 0;
|
|
1632
|
+
try {
|
|
1633
|
+
if (!fs6.existsSync(dirPath)) return 0;
|
|
1634
|
+
const files = await fsp.readdir(dirPath, { withFileTypes: true });
|
|
1635
|
+
for (const file of files) {
|
|
1636
|
+
const filePath = path5.join(dirPath, file.name);
|
|
1637
|
+
if (file.isDirectory()) {
|
|
1638
|
+
totalSize += await getDirSize(filePath);
|
|
1639
|
+
} else {
|
|
1640
|
+
const stat2 = await fsp.stat(filePath);
|
|
1641
|
+
totalSize += stat2.size;
|
|
1642
|
+
}
|
|
1643
|
+
}
|
|
1644
|
+
} catch {
|
|
1645
|
+
}
|
|
1646
|
+
return totalSize;
|
|
1647
|
+
}
|
|
1648
|
+
async function getFileCount(dirPath) {
|
|
1649
|
+
let count = 0;
|
|
1650
|
+
try {
|
|
1651
|
+
if (!fs6.existsSync(dirPath)) return 0;
|
|
1652
|
+
const files = await fsp.readdir(dirPath, { withFileTypes: true });
|
|
1653
|
+
for (const file of files) {
|
|
1654
|
+
const filePath = path5.join(dirPath, file.name);
|
|
1655
|
+
if (file.isDirectory()) {
|
|
1656
|
+
count += await getFileCount(filePath);
|
|
1657
|
+
} else {
|
|
1658
|
+
count++;
|
|
1659
|
+
}
|
|
1660
|
+
}
|
|
1661
|
+
} catch {
|
|
1662
|
+
}
|
|
1663
|
+
return count;
|
|
1664
|
+
}
|
|
1665
|
+
function registerCacheRoutes(server2, pathProvider) {
|
|
1666
|
+
const downloadsDir = pathProvider.getDownloadsDir();
|
|
1667
|
+
server2.get("/_web/cache/info", async () => {
|
|
1668
|
+
const directories = [
|
|
1669
|
+
{
|
|
1670
|
+
id: "databases",
|
|
1671
|
+
name: "settings.storage.cache.databases.name",
|
|
1672
|
+
description: "settings.storage.cache.databases.description",
|
|
1673
|
+
path: pathProvider.getDatabaseDir(),
|
|
1674
|
+
icon: "i-heroicons-circle-stack",
|
|
1675
|
+
canClear: false
|
|
1676
|
+
},
|
|
1677
|
+
{
|
|
1678
|
+
id: "ai",
|
|
1679
|
+
name: "settings.storage.cache.ai.name",
|
|
1680
|
+
description: "settings.storage.cache.ai.description",
|
|
1681
|
+
path: pathProvider.getAiDataDir(),
|
|
1682
|
+
icon: "i-heroicons-sparkles",
|
|
1683
|
+
canClear: false
|
|
1684
|
+
},
|
|
1685
|
+
{
|
|
1686
|
+
id: "cache",
|
|
1687
|
+
name: "settings.storage.cache.statsCache.name",
|
|
1688
|
+
description: "settings.storage.cache.statsCache.description",
|
|
1689
|
+
path: pathProvider.getCacheDir(),
|
|
1690
|
+
icon: "i-heroicons-bolt",
|
|
1691
|
+
canClear: true
|
|
1692
|
+
},
|
|
1693
|
+
{
|
|
1694
|
+
id: "logs",
|
|
1695
|
+
name: "settings.storage.cache.logs.name",
|
|
1696
|
+
description: "settings.storage.cache.logs.description",
|
|
1697
|
+
path: pathProvider.getLogsDir(),
|
|
1698
|
+
icon: "i-heroicons-document-text",
|
|
1699
|
+
canClear: true
|
|
1700
|
+
}
|
|
1701
|
+
];
|
|
1702
|
+
const results = await Promise.all(
|
|
1703
|
+
directories.map(async (dir) => {
|
|
1704
|
+
const [size, fileCount] = await Promise.all([getDirSize(dir.path), getFileCount(dir.path)]);
|
|
1705
|
+
return { ...dir, size, fileCount, exists: fs6.existsSync(dir.path) };
|
|
1706
|
+
})
|
|
1707
|
+
);
|
|
1708
|
+
return {
|
|
1709
|
+
baseDir: pathProvider.getUserDataDir(),
|
|
1710
|
+
systemDir: pathProvider.getSystemDir(),
|
|
1711
|
+
directories: results,
|
|
1712
|
+
totalSize: results.reduce((sum, d) => sum + d.size, 0)
|
|
1713
|
+
};
|
|
1714
|
+
});
|
|
1715
|
+
server2.post("/_web/cache/clear", async (request) => {
|
|
1716
|
+
const { cacheId } = request.body;
|
|
1717
|
+
const allowedDirs = {
|
|
1718
|
+
cache: pathProvider.getCacheDir(),
|
|
1719
|
+
logs: pathProvider.getLogsDir()
|
|
1720
|
+
};
|
|
1721
|
+
const dirPath = allowedDirs[cacheId];
|
|
1722
|
+
if (!dirPath) return { success: false, error: "Not allowed to clear this directory" };
|
|
1723
|
+
if (!fs6.existsSync(dirPath)) return { success: true };
|
|
1724
|
+
const files = await fsp.readdir(dirPath);
|
|
1725
|
+
for (const file of files) {
|
|
1726
|
+
const filePath = path5.join(dirPath, file);
|
|
1727
|
+
const stat2 = await fsp.stat(filePath);
|
|
1728
|
+
if (stat2.isDirectory()) {
|
|
1729
|
+
await fsp.rm(filePath, { recursive: true });
|
|
1730
|
+
} else {
|
|
1731
|
+
await fsp.unlink(filePath);
|
|
1732
|
+
}
|
|
1733
|
+
}
|
|
1734
|
+
return { success: true };
|
|
1735
|
+
});
|
|
1736
|
+
server2.post("/_web/cache/open-dir", async (request) => {
|
|
1737
|
+
const { cacheId } = request.body;
|
|
1738
|
+
const dirPaths = {
|
|
1739
|
+
base: pathProvider.getUserDataDir(),
|
|
1740
|
+
system: pathProvider.getSystemDir(),
|
|
1741
|
+
databases: pathProvider.getDatabaseDir(),
|
|
1742
|
+
cache: pathProvider.getCacheDir(),
|
|
1743
|
+
ai: pathProvider.getAiDataDir(),
|
|
1744
|
+
logs: pathProvider.getLogsDir(),
|
|
1745
|
+
downloads: downloadsDir
|
|
1746
|
+
};
|
|
1747
|
+
const dirPath = dirPaths[cacheId];
|
|
1748
|
+
if (!dirPath) return { success: false, error: "Unknown directory" };
|
|
1749
|
+
if (!fs6.existsSync(dirPath)) {
|
|
1750
|
+
await fsp.mkdir(dirPath, { recursive: true });
|
|
1751
|
+
}
|
|
1752
|
+
try {
|
|
1753
|
+
await openDirectoryPath(dirPath);
|
|
1754
|
+
} catch (error) {
|
|
1755
|
+
return { success: false, error: error instanceof Error ? error.message : String(error) };
|
|
1756
|
+
}
|
|
1757
|
+
return { success: true };
|
|
1758
|
+
});
|
|
1759
|
+
server2.get("/_web/cache/data-dir", async () => {
|
|
1760
|
+
const config = loadConfig();
|
|
1761
|
+
const isCustom = Boolean(config.data.user_data_dir || process.env.CHATLAB_DATA_DIR);
|
|
1762
|
+
return { path: pathProvider.getUserDataDir(), isCustom };
|
|
1763
|
+
});
|
|
1764
|
+
server2.get("/_web/cache/latest-import-log", async () => {
|
|
1765
|
+
const importLogDir = path5.join(pathProvider.getLogsDir(), "import");
|
|
1766
|
+
if (!fs6.existsSync(importLogDir)) {
|
|
1767
|
+
return { success: false, error: "Log directory not found" };
|
|
1768
|
+
}
|
|
1769
|
+
const files = await fsp.readdir(importLogDir);
|
|
1770
|
+
const logFiles = files.filter((f) => f.startsWith("import_") && f.endsWith(".log"));
|
|
1771
|
+
if (logFiles.length === 0) {
|
|
1772
|
+
return { success: false, error: "No import logs found" };
|
|
1773
|
+
}
|
|
1774
|
+
const fileStats = await Promise.all(
|
|
1775
|
+
logFiles.map(async (f) => {
|
|
1776
|
+
const filePath = path5.join(importLogDir, f);
|
|
1777
|
+
const stat2 = await fsp.stat(filePath);
|
|
1778
|
+
return { name: f, path: filePath, mtime: stat2.mtime.getTime() };
|
|
1779
|
+
})
|
|
1780
|
+
);
|
|
1781
|
+
fileStats.sort((a, b) => b.mtime - a.mtime);
|
|
1782
|
+
return { success: true, path: fileStats[0].path, name: fileStats[0].name };
|
|
1783
|
+
});
|
|
1592
1784
|
server2.post("/_web/cache/save-to-downloads", async (request) => {
|
|
1593
1785
|
const { filename, dataUrl } = request.body;
|
|
1594
1786
|
if (!filename || !dataUrl) {
|
|
@@ -1600,7 +1792,7 @@ function registerCacheRoutes(server2, downloadsDir) {
|
|
|
1600
1792
|
if (!fs6.existsSync(downloadsDir)) {
|
|
1601
1793
|
fs6.mkdirSync(downloadsDir, { recursive: true });
|
|
1602
1794
|
}
|
|
1603
|
-
const filePath =
|
|
1795
|
+
const filePath = path5.join(downloadsDir, filename);
|
|
1604
1796
|
fs6.writeFileSync(filePath, buffer);
|
|
1605
1797
|
return { success: true, filePath };
|
|
1606
1798
|
});
|
|
@@ -1609,26 +1801,46 @@ function registerCacheRoutes(server2, downloadsDir) {
|
|
|
1609
1801
|
if (!filePath) {
|
|
1610
1802
|
return { success: false, error: "filePath is required" };
|
|
1611
1803
|
}
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
exec(`open -R "${filePath}"`);
|
|
1617
|
-
} else if (platform === "win32") {
|
|
1618
|
-
exec(`explorer /select,"${filePath}"`);
|
|
1619
|
-
} else {
|
|
1620
|
-
exec(`xdg-open "${dir}"`);
|
|
1804
|
+
try {
|
|
1805
|
+
await showPathInFolder(filePath);
|
|
1806
|
+
} catch (error) {
|
|
1807
|
+
return { success: false, error: error instanceof Error ? error.message : String(error) };
|
|
1621
1808
|
}
|
|
1622
1809
|
return { success: true };
|
|
1623
1810
|
});
|
|
1624
1811
|
}
|
|
1625
1812
|
|
|
1626
1813
|
// src/http/routes/web/index.ts
|
|
1814
|
+
function isNewerVersion(latest, current) {
|
|
1815
|
+
const parse = (v) => {
|
|
1816
|
+
const [core, pre] = v.split("-", 2);
|
|
1817
|
+
const parts = core.split(".").map(Number);
|
|
1818
|
+
return { major: parts[0] || 0, minor: parts[1] || 0, patch: parts[2] || 0, pre };
|
|
1819
|
+
};
|
|
1820
|
+
const l = parse(latest);
|
|
1821
|
+
const c = parse(current);
|
|
1822
|
+
if (l.major !== c.major) return l.major > c.major;
|
|
1823
|
+
if (l.minor !== c.minor) return l.minor > c.minor;
|
|
1824
|
+
if (l.patch !== c.patch) return l.patch > c.patch;
|
|
1825
|
+
if (c.pre && !l.pre) return true;
|
|
1826
|
+
return false;
|
|
1827
|
+
}
|
|
1627
1828
|
function registerWebRoutes(server2, dbManager2, options) {
|
|
1628
1829
|
const adapter = createDatabaseManagerAdapter(dbManager2);
|
|
1629
1830
|
const mergeCache = options?.pathProvider ? new MergeSessionCache(options.pathProvider, { nativeBinding: options.nativeBinding }) : null;
|
|
1630
1831
|
mergeCache?.cleanupOrphans();
|
|
1631
|
-
const
|
|
1832
|
+
const fallbackPathProvider = {
|
|
1833
|
+
getSystemDir: () => path6.join(os3.homedir(), ".chatlab"),
|
|
1834
|
+
getUserDataDir: () => path6.join(os3.homedir(), ".chatlab", "data"),
|
|
1835
|
+
getDatabaseDir: () => path6.join(os3.homedir(), ".chatlab", "data", "databases"),
|
|
1836
|
+
getAiDataDir: () => path6.join(os3.homedir(), ".chatlab", "ai"),
|
|
1837
|
+
getSettingsDir: () => path6.join(os3.homedir(), ".chatlab", "settings"),
|
|
1838
|
+
getCacheDir: () => path6.join(os3.homedir(), ".chatlab", "cache"),
|
|
1839
|
+
getTempDir: () => path6.join(os3.homedir(), ".chatlab", "temp"),
|
|
1840
|
+
getLogsDir: () => path6.join(os3.homedir(), ".chatlab", "logs"),
|
|
1841
|
+
getDownloadsDir: () => path6.join(os3.homedir(), "Downloads")
|
|
1842
|
+
};
|
|
1843
|
+
const resolvedPathProvider = options?.pathProvider ?? fallbackPathProvider;
|
|
1632
1844
|
registerSessionRoutes2(server2, adapter);
|
|
1633
1845
|
registerMemberRoutes(server2, adapter);
|
|
1634
1846
|
registerAnalyticsRoutes(server2, dbManager2, adapter);
|
|
@@ -1640,11 +1852,34 @@ function registerWebRoutes(server2, dbManager2, options) {
|
|
|
1640
1852
|
registerMergeRoutes(server2, dbManager2, mergeCache);
|
|
1641
1853
|
}
|
|
1642
1854
|
registerExportRoutes(server2, adapter);
|
|
1643
|
-
registerCacheRoutes(server2,
|
|
1855
|
+
registerCacheRoutes(server2, resolvedPathProvider);
|
|
1856
|
+
server2.get("/_web/system/check-update", async () => {
|
|
1857
|
+
const currentVersion = getVersion();
|
|
1858
|
+
const packageName = "chatlab-cli";
|
|
1859
|
+
try {
|
|
1860
|
+
const resp = await fetch(`https://registry.npmjs.org/${packageName}/latest`, {
|
|
1861
|
+
headers: { Accept: "application/json" },
|
|
1862
|
+
signal: AbortSignal.timeout(1e4)
|
|
1863
|
+
});
|
|
1864
|
+
if (!resp.ok) {
|
|
1865
|
+
return { hasUpdate: false, currentVersion, error: `npm registry HTTP ${resp.status}` };
|
|
1866
|
+
}
|
|
1867
|
+
const data = await resp.json();
|
|
1868
|
+
const latestVersion = data.version || currentVersion;
|
|
1869
|
+
const hasUpdate = isNewerVersion(latestVersion, currentVersion);
|
|
1870
|
+
return { hasUpdate, currentVersion, latestVersion };
|
|
1871
|
+
} catch (err) {
|
|
1872
|
+
return {
|
|
1873
|
+
hasUpdate: false,
|
|
1874
|
+
currentVersion,
|
|
1875
|
+
error: err instanceof Error ? err.message : String(err)
|
|
1876
|
+
};
|
|
1877
|
+
}
|
|
1878
|
+
});
|
|
1644
1879
|
}
|
|
1645
1880
|
|
|
1646
1881
|
// src/http/routes/nlp.ts
|
|
1647
|
-
import * as
|
|
1882
|
+
import * as path7 from "path";
|
|
1648
1883
|
function ensureDb3(dbManager2, sessionId) {
|
|
1649
1884
|
const db = dbManager2.open(sessionId);
|
|
1650
1885
|
if (!db) {
|
|
@@ -1655,7 +1890,7 @@ function ensureDb3(dbManager2, sessionId) {
|
|
|
1655
1890
|
function registerNlpRoutes(server2, dbManager2) {
|
|
1656
1891
|
const pathProvider = dbManager2["pathProvider"];
|
|
1657
1892
|
if (pathProvider) {
|
|
1658
|
-
const nlpDir =
|
|
1893
|
+
const nlpDir = path7.join(pathProvider.getSystemDir(), "nlp");
|
|
1659
1894
|
initNlpDir(nlpDir);
|
|
1660
1895
|
ensureDefaultDict(nlpDir).catch((err) => console.warn("[NLP] Auto-download zh-CN dict failed:", err));
|
|
1661
1896
|
}
|
|
@@ -1693,12 +1928,12 @@ function registerNlpRoutes(server2, dbManager2) {
|
|
|
1693
1928
|
}
|
|
1694
1929
|
function resolveNlpDir(dbManager2) {
|
|
1695
1930
|
const pathProvider = dbManager2["pathProvider"];
|
|
1696
|
-
return
|
|
1931
|
+
return path7.join(pathProvider.getSystemDir(), "nlp");
|
|
1697
1932
|
}
|
|
1698
1933
|
|
|
1699
1934
|
// src/http/routes/ai.ts
|
|
1700
1935
|
import * as fs10 from "fs";
|
|
1701
|
-
import * as
|
|
1936
|
+
import * as path11 from "path";
|
|
1702
1937
|
|
|
1703
1938
|
// src/ai/logger.ts
|
|
1704
1939
|
var logger = null;
|
|
@@ -1747,6 +1982,7 @@ function adaptToolsForAgent(tools, getContext, options) {
|
|
|
1747
1982
|
description: tool.description,
|
|
1748
1983
|
parameters: convertJsonSchemaToParameters(tool.inputSchema),
|
|
1749
1984
|
async execute(_toolCallId, params) {
|
|
1985
|
+
const toolParams = params && typeof params === "object" ? params : {};
|
|
1750
1986
|
const ctx = getContext();
|
|
1751
1987
|
const execCtx = {
|
|
1752
1988
|
db: ctx.db,
|
|
@@ -1756,7 +1992,7 @@ function adaptToolsForAgent(tools, getContext, options) {
|
|
|
1756
1992
|
segmentText: (texts, locale, options2) => batchSegmentWithFrequency(texts, locale, options2)
|
|
1757
1993
|
};
|
|
1758
1994
|
try {
|
|
1759
|
-
const result = await tool.handler(
|
|
1995
|
+
const result = await tool.handler(toolParams, execCtx);
|
|
1760
1996
|
if (result.rawMessages && result.rawMessages.length > 0) {
|
|
1761
1997
|
const pipelineResult = applyPreprocessingPipeline({
|
|
1762
1998
|
rawMessages: result.rawMessages,
|
|
@@ -1779,9 +2015,9 @@ function adaptToolsForAgent(tools, getContext, options) {
|
|
|
1779
2015
|
|
|
1780
2016
|
// src/ai/assistant-loader.ts
|
|
1781
2017
|
import * as fs7 from "fs";
|
|
1782
|
-
import * as
|
|
2018
|
+
import * as path8 from "path";
|
|
1783
2019
|
function loadAssistantConfig(aiDataDir, assistantId) {
|
|
1784
|
-
const filePath =
|
|
2020
|
+
const filePath = path8.join(aiDataDir, "assistants", `${assistantId}.md`);
|
|
1785
2021
|
if (!fs7.existsSync(filePath)) return null;
|
|
1786
2022
|
try {
|
|
1787
2023
|
const content = fs7.readFileSync(filePath, "utf-8");
|
|
@@ -1793,7 +2029,7 @@ function loadAssistantConfig(aiDataDir, assistantId) {
|
|
|
1793
2029
|
|
|
1794
2030
|
// src/ai/manager-factory.ts
|
|
1795
2031
|
import * as fs8 from "fs";
|
|
1796
|
-
import * as
|
|
2032
|
+
import * as path9 from "path";
|
|
1797
2033
|
import { createHash, randomUUID as randomUUID3 } from "crypto";
|
|
1798
2034
|
var nodeFs = {
|
|
1799
2035
|
ensureDir(dir) {
|
|
@@ -1816,7 +2052,7 @@ var nodeFs = {
|
|
|
1816
2052
|
return fs8.existsSync(filePath);
|
|
1817
2053
|
},
|
|
1818
2054
|
joinPath(...parts) {
|
|
1819
|
-
return
|
|
2055
|
+
return path9.join(...parts);
|
|
1820
2056
|
}
|
|
1821
2057
|
};
|
|
1822
2058
|
var managerCache = /* @__PURE__ */ new Map();
|
|
@@ -1826,13 +2062,13 @@ function getAssistantManager(aiDataDir) {
|
|
|
1826
2062
|
cached = {
|
|
1827
2063
|
assistant: new AssistantManager({
|
|
1828
2064
|
fs: nodeFs,
|
|
1829
|
-
assistantsDir:
|
|
2065
|
+
assistantsDir: path9.join(aiDataDir, "assistants"),
|
|
1830
2066
|
builtinRawConfigs: [],
|
|
1831
2067
|
generateId: () => `custom_${randomUUID3().replace(/-/g, "").slice(0, 12)}`
|
|
1832
2068
|
}),
|
|
1833
2069
|
skill: new SkillManagerCore({
|
|
1834
2070
|
fs: nodeFs,
|
|
1835
|
-
skillsDir:
|
|
2071
|
+
skillsDir: path9.join(aiDataDir, "skills"),
|
|
1836
2072
|
builtinRawSkills: [],
|
|
1837
2073
|
contentHash: (content) => createHash("md5").update(content).digest("hex")
|
|
1838
2074
|
})
|
|
@@ -1975,10 +2211,10 @@ async function runServerAgent(options) {
|
|
|
1975
2211
|
|
|
1976
2212
|
// src/ai/custom-store.ts
|
|
1977
2213
|
import * as fs9 from "fs";
|
|
1978
|
-
import * as
|
|
2214
|
+
import * as path10 from "path";
|
|
1979
2215
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
1980
2216
|
function readProviders(aiDataDir) {
|
|
1981
|
-
const storePath =
|
|
2217
|
+
const storePath = path10.join(aiDataDir, "custom-providers.json");
|
|
1982
2218
|
if (!fs9.existsSync(storePath)) return [];
|
|
1983
2219
|
try {
|
|
1984
2220
|
const content = fs9.readFileSync(storePath, "utf-8");
|
|
@@ -1988,8 +2224,8 @@ function readProviders(aiDataDir) {
|
|
|
1988
2224
|
}
|
|
1989
2225
|
}
|
|
1990
2226
|
function writeProviders(aiDataDir, providers) {
|
|
1991
|
-
const storePath =
|
|
1992
|
-
const dir =
|
|
2227
|
+
const storePath = path10.join(aiDataDir, "custom-providers.json");
|
|
2228
|
+
const dir = path10.dirname(storePath);
|
|
1993
2229
|
if (!fs9.existsSync(dir)) fs9.mkdirSync(dir, { recursive: true });
|
|
1994
2230
|
fs9.writeFileSync(storePath, JSON.stringify(providers, null, 2), "utf-8");
|
|
1995
2231
|
}
|
|
@@ -2022,7 +2258,7 @@ function deleteCustomProvider(aiDataDir, id) {
|
|
|
2022
2258
|
return { success: true };
|
|
2023
2259
|
}
|
|
2024
2260
|
function readModels(aiDataDir) {
|
|
2025
|
-
const storePath =
|
|
2261
|
+
const storePath = path10.join(aiDataDir, "custom-models.json");
|
|
2026
2262
|
if (!fs9.existsSync(storePath)) return [];
|
|
2027
2263
|
try {
|
|
2028
2264
|
const content = fs9.readFileSync(storePath, "utf-8");
|
|
@@ -2032,8 +2268,8 @@ function readModels(aiDataDir) {
|
|
|
2032
2268
|
}
|
|
2033
2269
|
}
|
|
2034
2270
|
function writeModels(aiDataDir, models) {
|
|
2035
|
-
const storePath =
|
|
2036
|
-
const dir =
|
|
2271
|
+
const storePath = path10.join(aiDataDir, "custom-models.json");
|
|
2272
|
+
const dir = path10.dirname(storePath);
|
|
2037
2273
|
if (!fs9.existsSync(dir)) fs9.mkdirSync(dir, { recursive: true });
|
|
2038
2274
|
fs9.writeFileSync(storePath, JSON.stringify(models, null, 2), "utf-8");
|
|
2039
2275
|
}
|
|
@@ -2149,7 +2385,7 @@ function registerAiRoutes(server2, dbManager2, convManager2) {
|
|
|
2149
2385
|
return mgr.getBuiltinCatalog();
|
|
2150
2386
|
});
|
|
2151
2387
|
server2.get("/_web/ai/llm/has-config", async () => {
|
|
2152
|
-
const configPath =
|
|
2388
|
+
const configPath = path11.join(getAiDir(dbManager2), "llm-config.json");
|
|
2153
2389
|
if (!fs10.existsSync(configPath)) return false;
|
|
2154
2390
|
try {
|
|
2155
2391
|
const data = JSON.parse(fs10.readFileSync(configPath, "utf-8"));
|
|
@@ -2160,7 +2396,7 @@ function registerAiRoutes(server2, dbManager2, convManager2) {
|
|
|
2160
2396
|
}
|
|
2161
2397
|
});
|
|
2162
2398
|
server2.get("/_web/ai/llm/configs", async () => {
|
|
2163
|
-
const configPath =
|
|
2399
|
+
const configPath = path11.join(getAiDir(dbManager2), "llm-config.json");
|
|
2164
2400
|
if (!fs10.existsSync(configPath)) return { configs: [], defaultAssistant: null, fastModel: null };
|
|
2165
2401
|
try {
|
|
2166
2402
|
const data = JSON.parse(fs10.readFileSync(configPath, "utf-8"));
|
|
@@ -2189,7 +2425,7 @@ function registerAiRoutes(server2, dbManager2, convManager2) {
|
|
|
2189
2425
|
});
|
|
2190
2426
|
});
|
|
2191
2427
|
server2.get("/_web/ai/llm/provider-registry", async () => {
|
|
2192
|
-
const customPath =
|
|
2428
|
+
const customPath = path11.join(getAiDir(dbManager2), "custom-providers.json");
|
|
2193
2429
|
let custom = [];
|
|
2194
2430
|
if (fs10.existsSync(customPath)) {
|
|
2195
2431
|
try {
|
|
@@ -2202,7 +2438,7 @@ function registerAiRoutes(server2, dbManager2, convManager2) {
|
|
|
2202
2438
|
return [...BUILTIN_PROVIDERS, ...custom];
|
|
2203
2439
|
});
|
|
2204
2440
|
server2.get("/_web/ai/llm/model-catalog", async () => {
|
|
2205
|
-
const customPath =
|
|
2441
|
+
const customPath = path11.join(getAiDir(dbManager2), "custom-models.json");
|
|
2206
2442
|
let custom = [];
|
|
2207
2443
|
if (fs10.existsSync(customPath)) {
|
|
2208
2444
|
try {
|
|
@@ -2215,7 +2451,7 @@ function registerAiRoutes(server2, dbManager2, convManager2) {
|
|
|
2215
2451
|
return [...BUILTIN_MODELS, ...custom];
|
|
2216
2452
|
});
|
|
2217
2453
|
server2.get("/_web/ai/llm/default-assistant-slot", async () => {
|
|
2218
|
-
const configPath =
|
|
2454
|
+
const configPath = path11.join(getAiDir(dbManager2), "llm-config.json");
|
|
2219
2455
|
if (!fs10.existsSync(configPath)) return null;
|
|
2220
2456
|
try {
|
|
2221
2457
|
const data = JSON.parse(fs10.readFileSync(configPath, "utf-8"));
|
|
@@ -2225,7 +2461,7 @@ function registerAiRoutes(server2, dbManager2, convManager2) {
|
|
|
2225
2461
|
}
|
|
2226
2462
|
});
|
|
2227
2463
|
server2.get("/_web/ai/llm/fast-model-slot", async () => {
|
|
2228
|
-
const configPath =
|
|
2464
|
+
const configPath = path11.join(getAiDir(dbManager2), "llm-config.json");
|
|
2229
2465
|
if (!fs10.existsSync(configPath)) return null;
|
|
2230
2466
|
try {
|
|
2231
2467
|
const data = JSON.parse(fs10.readFileSync(configPath, "utf-8"));
|
|
@@ -2582,7 +2818,7 @@ var NOOP_LOGGER = { info: noop, warn: noop, error: noop };
|
|
|
2582
2818
|
|
|
2583
2819
|
// ../../packages/sync/src/data-source-manager.ts
|
|
2584
2820
|
import * as fs11 from "fs";
|
|
2585
|
-
import * as
|
|
2821
|
+
import * as path12 from "path";
|
|
2586
2822
|
import * as crypto2 from "crypto";
|
|
2587
2823
|
var CONFIG_FILE = "data-sources.json";
|
|
2588
2824
|
function generateId(prefix = "ds") {
|
|
@@ -2610,7 +2846,7 @@ var DataSourceManager = class {
|
|
|
2610
2846
|
this.logger = logger2 ?? NOOP_LOGGER;
|
|
2611
2847
|
}
|
|
2612
2848
|
getConfigPath() {
|
|
2613
|
-
return
|
|
2849
|
+
return path12.join(this.configDir, CONFIG_FILE);
|
|
2614
2850
|
}
|
|
2615
2851
|
ensureDir() {
|
|
2616
2852
|
fs11.mkdirSync(this.configDir, { recursive: true });
|
|
@@ -2713,12 +2949,12 @@ var DataSourceManager = class {
|
|
|
2713
2949
|
removeSession(sourceId, sessionId) {
|
|
2714
2950
|
const sources = this.loadAll();
|
|
2715
2951
|
const ds = sources.find((s) => s.id === sourceId);
|
|
2716
|
-
if (!ds) return
|
|
2717
|
-
const
|
|
2952
|
+
if (!ds) return null;
|
|
2953
|
+
const removed = ds.sessions.find((s) => s.id === sessionId);
|
|
2954
|
+
if (!removed) return null;
|
|
2718
2955
|
ds.sessions = ds.sessions.filter((s) => s.id !== sessionId);
|
|
2719
|
-
if (ds.sessions.length === before) return false;
|
|
2720
2956
|
this.saveAll(sources);
|
|
2721
|
-
return
|
|
2957
|
+
return removed;
|
|
2722
2958
|
}
|
|
2723
2959
|
updateSession(sourceId, sessionId, updates) {
|
|
2724
2960
|
const sources = this.loadAll();
|
|
@@ -2766,14 +3002,13 @@ function parseRemoteSessionsResponse(body) {
|
|
|
2766
3002
|
// ../../packages/sync/src/pull-engine.ts
|
|
2767
3003
|
import * as fs12 from "fs";
|
|
2768
3004
|
import * as crypto3 from "crypto";
|
|
2769
|
-
var MAX_PAGES_PER_PULL =
|
|
3005
|
+
var MAX_PAGES_PER_PULL = 5e3;
|
|
2770
3006
|
var PULL_OVERLAP_SECONDS = 60;
|
|
2771
3007
|
function buildPullUrl(baseUrl, remoteSessionId, params) {
|
|
2772
3008
|
const base = `${baseUrl}/sessions/${remoteSessionId}/messages`;
|
|
2773
3009
|
const qs = ["format=chatlab"];
|
|
2774
3010
|
if (params.since !== void 0 && params.since > 0) qs.push(`since=${params.since}`);
|
|
2775
3011
|
if (params.offset !== void 0 && params.offset > 0) qs.push(`offset=${params.offset}`);
|
|
2776
|
-
if (params.end !== void 0 && params.end > 0) qs.push(`end=${params.end}`);
|
|
2777
3012
|
if (params.limit !== void 0 && params.limit > 0) qs.push(`limit=${params.limit}`);
|
|
2778
3013
|
return base + "?" + qs.join("&");
|
|
2779
3014
|
}
|
|
@@ -2815,6 +3050,29 @@ function parseSyncFromFile(filePath) {
|
|
|
2815
3050
|
return null;
|
|
2816
3051
|
}
|
|
2817
3052
|
}
|
|
3053
|
+
function fileContainsMessages(filePath) {
|
|
3054
|
+
try {
|
|
3055
|
+
if (filePath.endsWith(".jsonl")) {
|
|
3056
|
+
const content = fs12.readFileSync(filePath, "utf-8");
|
|
3057
|
+
for (const line of content.split("\n")) {
|
|
3058
|
+
const trimmed = line.trim();
|
|
3059
|
+
if (!trimmed) continue;
|
|
3060
|
+
try {
|
|
3061
|
+
const obj = JSON.parse(trimmed);
|
|
3062
|
+
if (obj._type === "message") return true;
|
|
3063
|
+
} catch {
|
|
3064
|
+
continue;
|
|
3065
|
+
}
|
|
3066
|
+
}
|
|
3067
|
+
return false;
|
|
3068
|
+
}
|
|
3069
|
+
const raw = fs12.readFileSync(filePath, "utf-8");
|
|
3070
|
+
const parsed = JSON.parse(raw);
|
|
3071
|
+
return Array.isArray(parsed.messages) && parsed.messages.length > 0;
|
|
3072
|
+
} catch {
|
|
3073
|
+
return false;
|
|
3074
|
+
}
|
|
3075
|
+
}
|
|
2818
3076
|
function cleanupTempFile(filePath) {
|
|
2819
3077
|
try {
|
|
2820
3078
|
if (fs12.existsSync(filePath)) fs12.unlinkSync(filePath);
|
|
@@ -2829,6 +3087,7 @@ var PullEngine = class {
|
|
|
2829
3087
|
logger;
|
|
2830
3088
|
isImporting;
|
|
2831
3089
|
pullingSourceIds = /* @__PURE__ */ new Set();
|
|
3090
|
+
progressMap = /* @__PURE__ */ new Map();
|
|
2832
3091
|
constructor(options) {
|
|
2833
3092
|
this.fetcher = options.fetcher;
|
|
2834
3093
|
this.importer = options.importer;
|
|
@@ -2837,6 +3096,9 @@ var PullEngine = class {
|
|
|
2837
3096
|
this.logger = options.logger ?? NOOP_LOGGER;
|
|
2838
3097
|
this.isImporting = options.isImporting ?? (() => false);
|
|
2839
3098
|
}
|
|
3099
|
+
getProgress() {
|
|
3100
|
+
return Array.from(this.progressMap.values());
|
|
3101
|
+
}
|
|
2840
3102
|
async importTempFile(baseUrl, sess, tempFile) {
|
|
2841
3103
|
let targetId = sess.targetSessionId;
|
|
2842
3104
|
if (!targetId) {
|
|
@@ -2850,6 +3112,11 @@ var PullEngine = class {
|
|
|
2850
3112
|
return this.importer.importFile(tempFile, targetId || void 0, externalId);
|
|
2851
3113
|
}
|
|
2852
3114
|
async executePullSession(sourceId, ds, sess) {
|
|
3115
|
+
const currentDs = this.dsManager.get(sourceId);
|
|
3116
|
+
if (!currentDs || !currentDs.sessions.some((s) => s.id === sess.id)) {
|
|
3117
|
+
this.logger.info(`[Pull] Skipping "${sess.name}": session no longer exists`);
|
|
3118
|
+
return { success: true, newMessageCount: 0 };
|
|
3119
|
+
}
|
|
2853
3120
|
if (this.isImporting()) {
|
|
2854
3121
|
this.logger.info(`[Pull] Skipping "${sess.name}": import in progress`);
|
|
2855
3122
|
return { success: false, newMessageCount: 0, error: "Import in progress" };
|
|
@@ -2857,34 +3124,114 @@ var PullEngine = class {
|
|
|
2857
3124
|
this.logger.info(`[Pull] Pulling "${sess.name}" from ${ds.baseUrl}`);
|
|
2858
3125
|
let totalNewMessages = 0;
|
|
2859
3126
|
let since = sess.lastPullAt;
|
|
2860
|
-
let offset = 0;
|
|
2861
|
-
let end;
|
|
2862
3127
|
let pageCount = 0;
|
|
2863
3128
|
let resyncAttempted = false;
|
|
3129
|
+
this.progressMap.set(sess.id, { sessionId: sess.id, sessionName: sess.name, current: 0, pages: 0, done: false });
|
|
2864
3130
|
try {
|
|
2865
3131
|
while (pageCount < MAX_PAGES_PER_PULL) {
|
|
2866
3132
|
pageCount++;
|
|
2867
3133
|
const tempFile = await this.fetcher.fetchToTempFile(ds.baseUrl, sess.remoteSessionId, ds.token, {
|
|
2868
3134
|
since,
|
|
2869
|
-
offset,
|
|
2870
|
-
end,
|
|
2871
3135
|
limit: ds.pullLimit
|
|
2872
3136
|
});
|
|
2873
3137
|
try {
|
|
2874
|
-
const
|
|
2875
|
-
this.logger.info(`[Pull] "${sess.name}" page ${pageCount}: fetched ${
|
|
2876
|
-
|
|
3138
|
+
const stat2 = fs12.statSync(tempFile);
|
|
3139
|
+
this.logger.info(`[Pull] "${sess.name}" page ${pageCount}: fetched ${stat2.size} bytes`);
|
|
3140
|
+
const sync0 = parseSyncFromFile(tempFile);
|
|
3141
|
+
if (stat2.size < 1024) {
|
|
3142
|
+
if (!fileContainsMessages(tempFile) && sync0?.hasMore === false) {
|
|
3143
|
+
cleanupTempFile(tempFile);
|
|
3144
|
+
const retryDelays = [2e3, 3e3, 5e3];
|
|
3145
|
+
let retrySuccess = false;
|
|
3146
|
+
let retryHasMore = false;
|
|
3147
|
+
for (let ri = 0; ri < retryDelays.length; ri++) {
|
|
3148
|
+
this.logger.info(
|
|
3149
|
+
`[Pull] "${sess.name}" page ${pageCount} got empty response, retry ${ri + 1}/${retryDelays.length} after ${retryDelays[ri]}ms`
|
|
3150
|
+
);
|
|
3151
|
+
await new Promise((r) => setTimeout(r, retryDelays[ri]));
|
|
3152
|
+
const retryFile = await this.fetcher.fetchToTempFile(ds.baseUrl, sess.remoteSessionId, ds.token, {
|
|
3153
|
+
since,
|
|
3154
|
+
limit: ds.pullLimit
|
|
3155
|
+
});
|
|
3156
|
+
const retryStat = fs12.statSync(retryFile);
|
|
3157
|
+
this.logger.info(`[Pull] "${sess.name}" retry ${ri + 1}: fetched ${retryStat.size} bytes`);
|
|
3158
|
+
const retrySync = parseSyncFromFile(retryFile);
|
|
3159
|
+
if (retryStat.size < 1024 && !fileContainsMessages(retryFile) && retrySync?.hasMore === false) {
|
|
3160
|
+
cleanupTempFile(retryFile);
|
|
3161
|
+
continue;
|
|
3162
|
+
}
|
|
3163
|
+
const retryResult = await this.importTempFile(ds.baseUrl, sess, retryFile);
|
|
3164
|
+
cleanupTempFile(retryFile);
|
|
3165
|
+
if (retryResult.needFullResync && !resyncAttempted) {
|
|
3166
|
+
resyncAttempted = true;
|
|
3167
|
+
this.logger.info(`[Pull] Resetting since=0 for "${sess.name}" full resync`);
|
|
3168
|
+
since = 0;
|
|
3169
|
+
pageCount = 0;
|
|
3170
|
+
sess.targetSessionId = "";
|
|
3171
|
+
sess.lastPullAt = 0;
|
|
3172
|
+
this.dsManager.updateSession(sourceId, sess.id, { targetSessionId: "", lastPullAt: 0 });
|
|
3173
|
+
retrySuccess = true;
|
|
3174
|
+
retryHasMore = true;
|
|
3175
|
+
break;
|
|
3176
|
+
}
|
|
3177
|
+
if (retryResult.needFullResync) {
|
|
3178
|
+
const errMsg = "Full resync failed";
|
|
3179
|
+
this.logger.error(`[Pull] Full resync already attempted for "${sess.name}", aborting`);
|
|
3180
|
+
this.dsManager.updateSession(sourceId, sess.id, {
|
|
3181
|
+
lastPullAt: Math.floor(Date.now() / 1e3),
|
|
3182
|
+
lastStatus: "error",
|
|
3183
|
+
lastError: errMsg
|
|
3184
|
+
});
|
|
3185
|
+
this.notifier.onPullResult(sourceId, sess.id, "error", errMsg);
|
|
3186
|
+
this.markProgressDone(sess.id);
|
|
3187
|
+
return { success: false, newMessageCount: 0, error: errMsg };
|
|
3188
|
+
}
|
|
3189
|
+
if (!retryResult.success) {
|
|
3190
|
+
const errMsg = retryResult.error || "Import failed";
|
|
3191
|
+
this.dsManager.updateSession(sourceId, sess.id, {
|
|
3192
|
+
lastPullAt: Math.floor(Date.now() / 1e3),
|
|
3193
|
+
lastStatus: "error",
|
|
3194
|
+
lastError: errMsg
|
|
3195
|
+
});
|
|
3196
|
+
this.notifier.onPullResult(sourceId, sess.id, "error", errMsg);
|
|
3197
|
+
this.markProgressDone(sess.id);
|
|
3198
|
+
return { success: false, newMessageCount: 0, error: errMsg };
|
|
3199
|
+
}
|
|
3200
|
+
if (retryResult.success && retryResult.sessionId && !sess.targetSessionId) {
|
|
3201
|
+
sess.targetSessionId = retryResult.sessionId;
|
|
3202
|
+
this.dsManager.updateSession(sourceId, sess.id, { targetSessionId: retryResult.sessionId });
|
|
3203
|
+
}
|
|
3204
|
+
totalNewMessages += retryResult.newMessageCount;
|
|
3205
|
+
this.progressMap.set(sess.id, {
|
|
3206
|
+
sessionId: sess.id,
|
|
3207
|
+
sessionName: sess.name,
|
|
3208
|
+
current: totalNewMessages,
|
|
3209
|
+
pages: pageCount,
|
|
3210
|
+
done: false
|
|
3211
|
+
});
|
|
3212
|
+
if (retrySync?.hasMore && retrySync.nextSince !== void 0) {
|
|
3213
|
+
since = retrySync.nextSince;
|
|
3214
|
+
}
|
|
3215
|
+
retryHasMore = !!retrySync?.hasMore;
|
|
3216
|
+
retrySuccess = true;
|
|
3217
|
+
break;
|
|
3218
|
+
}
|
|
3219
|
+
if (!retrySuccess) break;
|
|
3220
|
+
if (!retryHasMore) break;
|
|
3221
|
+
continue;
|
|
3222
|
+
}
|
|
3223
|
+
}
|
|
3224
|
+
if (stat2.size === 0) {
|
|
2877
3225
|
cleanupTempFile(tempFile);
|
|
2878
3226
|
break;
|
|
2879
3227
|
}
|
|
2880
|
-
const sync =
|
|
3228
|
+
const sync = sync0;
|
|
2881
3229
|
const result = await this.importTempFile(ds.baseUrl, sess, tempFile);
|
|
2882
3230
|
cleanupTempFile(tempFile);
|
|
2883
3231
|
if (result.needFullResync && !resyncAttempted) {
|
|
2884
3232
|
resyncAttempted = true;
|
|
2885
3233
|
this.logger.info(`[Pull] Resetting since=0 for "${sess.name}" full resync`);
|
|
2886
3234
|
since = 0;
|
|
2887
|
-
offset = 0;
|
|
2888
3235
|
pageCount = 0;
|
|
2889
3236
|
sess.targetSessionId = "";
|
|
2890
3237
|
sess.lastPullAt = 0;
|
|
@@ -2900,6 +3247,7 @@ var PullEngine = class {
|
|
|
2900
3247
|
lastError: errMsg
|
|
2901
3248
|
});
|
|
2902
3249
|
this.notifier.onPullResult(sourceId, sess.id, "error", errMsg);
|
|
3250
|
+
this.markProgressDone(sess.id);
|
|
2903
3251
|
return { success: false, newMessageCount: 0, error: errMsg };
|
|
2904
3252
|
}
|
|
2905
3253
|
if (!result.success) {
|
|
@@ -2910,6 +3258,7 @@ var PullEngine = class {
|
|
|
2910
3258
|
lastError: errMsg
|
|
2911
3259
|
});
|
|
2912
3260
|
this.notifier.onPullResult(sourceId, sess.id, "error", errMsg);
|
|
3261
|
+
this.markProgressDone(sess.id);
|
|
2913
3262
|
return { success: false, newMessageCount: 0, error: errMsg };
|
|
2914
3263
|
}
|
|
2915
3264
|
if (!sess.targetSessionId && result.sessionId) {
|
|
@@ -2917,11 +3266,15 @@ var PullEngine = class {
|
|
|
2917
3266
|
this.dsManager.updateSession(sourceId, sess.id, { targetSessionId: result.sessionId });
|
|
2918
3267
|
}
|
|
2919
3268
|
totalNewMessages += result.newMessageCount;
|
|
3269
|
+
this.progressMap.set(sess.id, {
|
|
3270
|
+
sessionId: sess.id,
|
|
3271
|
+
sessionName: sess.name,
|
|
3272
|
+
current: totalNewMessages,
|
|
3273
|
+
pages: pageCount,
|
|
3274
|
+
done: false
|
|
3275
|
+
});
|
|
2920
3276
|
if (!sync || !sync.hasMore) break;
|
|
2921
3277
|
if (sync.nextSince !== void 0) since = sync.nextSince;
|
|
2922
|
-
if (sync.nextOffset !== void 0) offset = sync.nextOffset;
|
|
2923
|
-
else offset = 0;
|
|
2924
|
-
if (sync.watermark !== void 0 && !end) end = sync.watermark;
|
|
2925
3278
|
} catch (importErr) {
|
|
2926
3279
|
cleanupTempFile(tempFile);
|
|
2927
3280
|
throw importErr;
|
|
@@ -2938,6 +3291,7 @@ var PullEngine = class {
|
|
|
2938
3291
|
});
|
|
2939
3292
|
if (totalNewMessages > 0) this.notifier.onSessionListChanged();
|
|
2940
3293
|
this.notifier.onPullResult(sourceId, sess.id, "success", `+${totalNewMessages} messages`);
|
|
3294
|
+
this.markProgressDone(sess.id);
|
|
2941
3295
|
return { success: true, newMessageCount: totalNewMessages };
|
|
2942
3296
|
} catch (error) {
|
|
2943
3297
|
const errMsg = error.message || "Pull failed";
|
|
@@ -2948,9 +3302,17 @@ var PullEngine = class {
|
|
|
2948
3302
|
lastError: errMsg
|
|
2949
3303
|
});
|
|
2950
3304
|
this.notifier.onPullResult(sourceId, sess.id, "error", errMsg);
|
|
3305
|
+
this.markProgressDone(sess.id);
|
|
2951
3306
|
return { success: false, newMessageCount: 0, error: errMsg };
|
|
2952
3307
|
}
|
|
2953
3308
|
}
|
|
3309
|
+
markProgressDone(sessionId) {
|
|
3310
|
+
const p = this.progressMap.get(sessionId);
|
|
3311
|
+
if (p) {
|
|
3312
|
+
p.done = true;
|
|
3313
|
+
setTimeout(() => this.progressMap.delete(sessionId), 5e3);
|
|
3314
|
+
}
|
|
3315
|
+
}
|
|
2954
3316
|
async pullAllSessions(ds) {
|
|
2955
3317
|
if (this.pullingSourceIds.has(ds.id)) {
|
|
2956
3318
|
this.logger.info(`[Pull] Skipping pullAllSessions for "${ds.baseUrl}": pull already in progress`);
|
|
@@ -3067,17 +3429,17 @@ function reloadTimer(dsId, skipInitialPull = false) {
|
|
|
3067
3429
|
// src/sync/adapters.ts
|
|
3068
3430
|
import * as fs13 from "fs";
|
|
3069
3431
|
import * as os4 from "os";
|
|
3070
|
-
import * as
|
|
3432
|
+
import * as path13 from "path";
|
|
3071
3433
|
import * as crypto4 from "crypto";
|
|
3072
3434
|
function getTempFilePath(ext) {
|
|
3073
3435
|
const id = crypto4.randomBytes(8).toString("hex");
|
|
3074
|
-
return
|
|
3436
|
+
return path13.join(os4.tmpdir(), `chatlab-pull-${id}${ext}`);
|
|
3075
3437
|
}
|
|
3076
3438
|
var NodeFetcher = class {
|
|
3077
3439
|
async fetchToTempFile(baseUrl, remoteSessionId, token, params) {
|
|
3078
3440
|
const url = buildPullUrl(baseUrl, remoteSessionId, params);
|
|
3079
3441
|
const headers = {
|
|
3080
|
-
Accept: "application/json
|
|
3442
|
+
Accept: "application/json"
|
|
3081
3443
|
};
|
|
3082
3444
|
if (token) headers["Authorization"] = `Bearer ${token}`;
|
|
3083
3445
|
const response = await fetch(url, { headers, signal: AbortSignal.timeout(12e4) });
|
|
@@ -3094,7 +3456,7 @@ var NodeFetcher = class {
|
|
|
3094
3456
|
};
|
|
3095
3457
|
function resolveNativeBinding2() {
|
|
3096
3458
|
if (process.versions.electron) return void 0;
|
|
3097
|
-
const nativePath =
|
|
3459
|
+
const nativePath = resolveCliPath("native/better_sqlite3.node");
|
|
3098
3460
|
if (fs13.existsSync(nativePath)) return nativePath;
|
|
3099
3461
|
return void 0;
|
|
3100
3462
|
}
|
|
@@ -3109,7 +3471,28 @@ var DirectImporter = class {
|
|
|
3109
3471
|
}
|
|
3110
3472
|
sessionExists(sessionId) {
|
|
3111
3473
|
const dbPath = this.dbManager.getDbPath(sessionId);
|
|
3112
|
-
|
|
3474
|
+
if (!fs13.existsSync(dbPath)) return false;
|
|
3475
|
+
try {
|
|
3476
|
+
const db = openBetterSqliteDatabase(dbPath, { readonly: true, nativeBinding: this.nativeBinding });
|
|
3477
|
+
const row = db.prepare("SELECT COUNT(*) as cnt FROM sqlite_master WHERE type='table' AND name='message'").get();
|
|
3478
|
+
db.close();
|
|
3479
|
+
if (row.cnt === 0) {
|
|
3480
|
+
this.logger.warn(`[DirectImporter] DB file exists but has no message table: ${sessionId}, removing`);
|
|
3481
|
+
try {
|
|
3482
|
+
fs13.unlinkSync(dbPath);
|
|
3483
|
+
} catch {
|
|
3484
|
+
}
|
|
3485
|
+
return false;
|
|
3486
|
+
}
|
|
3487
|
+
return true;
|
|
3488
|
+
} catch {
|
|
3489
|
+
this.logger.warn(`[DirectImporter] Cannot validate DB file: ${sessionId}, removing`);
|
|
3490
|
+
try {
|
|
3491
|
+
fs13.unlinkSync(dbPath);
|
|
3492
|
+
} catch {
|
|
3493
|
+
}
|
|
3494
|
+
return false;
|
|
3495
|
+
}
|
|
3113
3496
|
}
|
|
3114
3497
|
async importFile(tempFile, targetSessionId, externalId) {
|
|
3115
3498
|
if (targetSessionId && this.sessionExists(targetSessionId)) {
|
|
@@ -3139,7 +3522,7 @@ var DirectImporter = class {
|
|
|
3139
3522
|
sessionId
|
|
3140
3523
|
};
|
|
3141
3524
|
}
|
|
3142
|
-
if (result.error?.includes("not found") || result.error?.includes("session_not_found")) {
|
|
3525
|
+
if (result.error?.includes("not found") || result.error?.includes("session_not_found") || result.error?.includes("no such table")) {
|
|
3143
3526
|
return { success: false, newMessageCount: 0, sessionId, needFullResync: true };
|
|
3144
3527
|
}
|
|
3145
3528
|
return { success: false, newMessageCount: 0, sessionId, error: result.error };
|
|
@@ -3177,7 +3560,7 @@ var NoopNotifier = class {
|
|
|
3177
3560
|
|
|
3178
3561
|
// src/sync/routes.ts
|
|
3179
3562
|
function registerAutomationRoutes(server2, ctx) {
|
|
3180
|
-
const { dsManager: dsManager2, pullEngine: pullEngine2, serverInfo } = ctx;
|
|
3563
|
+
const { dsManager: dsManager2, pullEngine: pullEngine2, dbManager: dbManager2, serverInfo } = ctx;
|
|
3181
3564
|
server2.get("/_web/automation/config", async () => {
|
|
3182
3565
|
return {
|
|
3183
3566
|
enabled: true,
|
|
@@ -3219,9 +3602,15 @@ function registerAutomationRoutes(server2, ctx) {
|
|
|
3219
3602
|
return added;
|
|
3220
3603
|
});
|
|
3221
3604
|
server2.delete("/_web/automation/data-sources/:id/sessions/:sessId", async (request, reply) => {
|
|
3222
|
-
const
|
|
3223
|
-
if (!
|
|
3605
|
+
const removed = dsManager2.removeSession(request.params.id, request.params.sessId);
|
|
3606
|
+
if (!removed) return reply.code(404).send({ error: "Session not found" });
|
|
3224
3607
|
reloadTimer(request.params.id);
|
|
3608
|
+
if (request.query.deleteData === "true" && removed.targetSessionId) {
|
|
3609
|
+
dbManager2.close(removed.targetSessionId);
|
|
3610
|
+
const dbPath = dbManager2.getDbPath(removed.targetSessionId);
|
|
3611
|
+
const fs15 = await import("fs");
|
|
3612
|
+
if (fs15.existsSync(dbPath)) fs15.unlinkSync(dbPath);
|
|
3613
|
+
}
|
|
3225
3614
|
return { success: true };
|
|
3226
3615
|
});
|
|
3227
3616
|
server2.post(
|
|
@@ -3233,6 +3622,9 @@ function registerAutomationRoutes(server2, ctx) {
|
|
|
3233
3622
|
server2.post("/_web/automation/data-sources/:id/pull-all", async (request) => {
|
|
3234
3623
|
return pullEngine2.triggerPullAll(request.params.id);
|
|
3235
3624
|
});
|
|
3625
|
+
server2.get("/_web/automation/sync-progress", async () => {
|
|
3626
|
+
return pullEngine2.getProgress();
|
|
3627
|
+
});
|
|
3236
3628
|
server2.get("/_web/automation/remote-sessions", async (request, reply) => {
|
|
3237
3629
|
const { baseUrl, token, keyword, limit, cursor } = request.query;
|
|
3238
3630
|
if (!baseUrl) return reply.code(400).send({ error: "baseUrl is required" });
|
|
@@ -3276,7 +3668,7 @@ function initSync(server2, dbManager2, pathProvider, serverInfo) {
|
|
|
3276
3668
|
dsManager,
|
|
3277
3669
|
logger: syncLogger
|
|
3278
3670
|
});
|
|
3279
|
-
registerAutomationRoutes(server2, { dsManager, pullEngine, serverInfo });
|
|
3671
|
+
registerAutomationRoutes(server2, { dsManager, pullEngine, dbManager: dbManager2, serverInfo });
|
|
3280
3672
|
initScheduler({
|
|
3281
3673
|
dsManager,
|
|
3282
3674
|
pullEngine,
|
|
@@ -3295,7 +3687,7 @@ var dbManager = null;
|
|
|
3295
3687
|
var convManager = null;
|
|
3296
3688
|
function resolveNativeBinding3() {
|
|
3297
3689
|
if (process.versions.electron) return void 0;
|
|
3298
|
-
const nativePath =
|
|
3690
|
+
const nativePath = resolveCliPath("native/better_sqlite3.node");
|
|
3299
3691
|
if (fs14.existsSync(nativePath)) return nativePath;
|
|
3300
3692
|
return void 0;
|
|
3301
3693
|
}
|
|
@@ -3320,7 +3712,7 @@ async function startHttpServer(options) {
|
|
|
3320
3712
|
const pathProvider = new NodePathProvider(userDataDir);
|
|
3321
3713
|
pathProvider.ensureAllDirs();
|
|
3322
3714
|
if (hasPendingElectronDataWarning() || !verifyCliDataPath(pathProvider.getDatabaseDir())) {
|
|
3323
|
-
|
|
3715
|
+
console.error(
|
|
3324
3716
|
"\n" + "=".repeat(68) + "\n ChatLab: Electron desktop data not found\n" + "=".repeat(68) + '\n\n Detected that ChatLab desktop app was installed on this machine,\n but could not locate your chat databases.\n\n This usually means you changed the data directory in desktop settings.\n\n To fix this, choose one of:\n\n 1. Open ChatLab desktop app \u2014 it will auto-migrate your data\n 2. Set the data directory manually:\n export CHATLAB_DATA_DIR="/path/to/your/data"\n 3. Edit ~/.chatlab/config.toml:\n [data]\n user_data_dir = "/path/to/your/data"\n\n' + "=".repeat(68) + "\n"
|
|
3325
3717
|
);
|
|
3326
3718
|
}
|
|
@@ -3397,4 +3789,4 @@ export {
|
|
|
3397
3789
|
startHttpServer,
|
|
3398
3790
|
stopHttpServer
|
|
3399
3791
|
};
|
|
3400
|
-
//# sourceMappingURL=chunk-
|
|
3792
|
+
//# sourceMappingURL=chunk-AMMSVJG4.mjs.map
|