chatlab-cli 0.29.0 → 0.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chat-command-JKG3FY5B.mjs → chat-command-UZ23GKO7.mjs} +8 -8
- package/dist/{chunk-KL2NGIMQ.mjs → chunk-4IZ3WKYU.mjs} +429 -272
- package/dist/chunk-4IZ3WKYU.mjs.map +1 -0
- package/dist/{chunk-CIB23U32.mjs → chunk-5UBHNQFZ.mjs} +101 -41
- package/dist/chunk-5UBHNQFZ.mjs.map +1 -0
- package/dist/{chunk-WPI6YSAR.mjs → chunk-7BZLS2KW.mjs} +4114 -3962
- package/dist/chunk-7BZLS2KW.mjs.map +1 -0
- package/dist/{chunk-QQK7MLCR.mjs → chunk-A2SANKG4.mjs} +121 -32
- package/dist/chunk-A2SANKG4.mjs.map +1 -0
- package/dist/{chunk-D4WTE7LV.mjs → chunk-CNSYXRJP.mjs} +2 -2
- package/dist/{chunk-I35WHWA6.mjs → chunk-FCCL5Z4W.mjs} +3 -3
- package/dist/chunk-M55KW2HA.mjs +2323 -0
- package/dist/chunk-M55KW2HA.mjs.map +1 -0
- package/dist/{chunk-2MZ6G4AC.mjs → chunk-MUPILWAZ.mjs} +2 -2
- package/dist/{chunk-7RW4CQAO.mjs → chunk-P3ZOLTGS.mjs} +3 -3
- package/dist/{chunk-6FPEP2ZX.mjs → chunk-RDR6PS2R.mjs} +2 -2
- package/dist/{chunk-SKUG5XVD.mjs → chunk-ZFU2CHW3.mjs} +5 -4
- package/dist/cli.mjs +7 -7
- package/dist/contacts-worker.mjs +2 -2
- package/dist/{http-5E7ARYFW.mjs → http-UWRA5FLB.mjs} +11 -11
- package/dist/{import-3KG6VK6O.mjs → import-745KQL7N.mjs} +7 -7
- package/dist/index.mjs +11 -11
- package/dist/{mcp-JBI4CUQT.mjs → mcp-UHN5JOHC.mjs} +8 -8
- package/dist/people-relationships-worker.mjs +2 -2
- package/dist/semantic-index-worker.mjs +1 -1
- package/dist/{src-UTBQAOJ3.mjs → src-Y7MDRXLJ.mjs} +12 -6
- package/dist/{stream-import-7HZ4IRYJ.mjs → stream-import-3JMXQD7P.mjs} +7 -7
- package/dist/{worker-runtime-ALS4MR2A.mjs → worker-runtime-63RYMGZI.mjs} +5 -5
- package/dist-web/assets/{BufferResource-MsqP5sq3.js → BufferResource-BVdJ_I5X.js} +1 -1
- package/dist-web/assets/CanvasRenderer-BaRuT54R.js +1 -0
- package/dist-web/assets/DebugToolsPanel.vue_vue_type_script_setup_true_lang-N5CoiJzN.js +110 -0
- package/dist-web/assets/{Filter-wRJtdXx9.js → Filter-B7cNFtuE.js} +1 -1
- package/dist-web/assets/LoadingState.vue_vue_type_script_setup_true_lang-D_dJuLWJ.js +1 -0
- package/dist-web/assets/{MessageExportModal-BCrMf5Kw.js → MessageExportModal-B2Qm6VVj.js} +1 -1
- package/dist-web/assets/{PageHeader-C_yw54le.js → PageHeader-PjlWy9fC.js} +1 -1
- package/dist-web/assets/{RelationshipGalaxyCanvas-DTzJq7q7.js → RelationshipGalaxyCanvas-11MafciW.js} +3 -3
- package/dist-web/assets/{RelationshipGalaxyThreeCanvas-DIE-0_iM.js → RelationshipGalaxyThreeCanvas-C1I4B8NX.js} +1 -1
- package/dist-web/assets/{RenderTargetSystem-BSAQFamI.js → RenderTargetSystem-Chkz1WlI.js} +1 -1
- package/dist-web/assets/{WebGLRenderer-lfzmCZNT.js → WebGLRenderer-xK19ru58.js} +1 -1
- package/dist-web/assets/{WebGPURenderer-BnEdfdr_.js → WebGPURenderer-CL5ZaKsn.js} +1 -1
- package/dist-web/assets/{browserAll-gRNfDyPu.js → browserAll-CyyNzRD_.js} +1 -1
- package/dist-web/assets/electron-MXaTKaMu.js +1 -0
- package/dist-web/assets/{fetch-BW3aO7PS.js → fetch-D1dDiFO4.js} +1 -1
- package/dist-web/assets/{fetch-DPCJ-1l-.js → fetch-DDnVXGte.js} +1 -1
- package/dist-web/assets/{fetch-C1fI_KOX.js → fetch-DTwdRTXM.js} +1 -1
- package/dist-web/assets/{fetch-CQt-gb-f.js → fetch-DaaJwT-5.js} +1 -1
- package/dist-web/assets/{fetch-gMB4wuBd.js → fetch-DeA0ztwT.js} +1 -1
- package/dist-web/assets/fetch-DpnIGZSN.js +17 -0
- package/dist-web/assets/{fetch-CFRoSIAn.js → fetch-DsE-xcP2.js} +1 -1
- package/dist-web/assets/{fetch-mFlEBZyB.js → fetch-DsTgYu_X.js} +1 -1
- package/dist-web/assets/{fetch-CN91TIwV.js → fetch-hKE53UO4.js} +1 -1
- package/dist-web/assets/{fetch-BwF5hNuF.js → fetch-u9GTMhMF.js} +1 -1
- package/dist-web/assets/index-BVkE4qis.js +22 -0
- package/dist-web/assets/index-BcXLcFnV.css +1 -0
- package/dist-web/assets/{index-Bg4GGPXv.js → index-BnHKyESZ.js} +1 -1
- package/dist-web/assets/index-Btr5H7d-.js +2 -0
- package/dist-web/assets/{index-MBRD3wPd.css → index-C1p9CbmN.css} +1 -1
- package/dist-web/assets/index-COpza1A4.css +1 -0
- package/dist-web/assets/index-D04LWJop.js +173 -0
- package/dist-web/assets/index-DFCYmzGE.js +2 -0
- package/dist-web/assets/{index-DveHdPfK.js → index-DqXHRIhI.js} +1 -1
- package/dist-web/assets/{index-ZdCB7xcf.js → index-DqtXt3wl.js} +4 -4
- package/dist-web/assets/index-Dszv-QFp.js +64 -0
- package/dist-web/assets/index-DzLrWGXu.js +1 -0
- package/dist-web/assets/{vendor-nuxt-ui-Dr2T8_OZ.js → vendor-nuxt-ui-DaLxYaC4.js} +1 -1
- package/dist-web/assets/{web-BakeV58u.js → web-DPVWfPS-.js} +1 -1
- package/dist-web/assets/{webworkerAll-D_6PgURS.js → webworkerAll-DF5QV_vN.js} +1 -1
- package/dist-web/index.html +3 -3
- package/package.json +1 -1
- package/dist/chunk-ARZM6HBX.mjs +0 -542
- package/dist/chunk-ARZM6HBX.mjs.map +0 -1
- package/dist/chunk-CIB23U32.mjs.map +0 -1
- package/dist/chunk-KL2NGIMQ.mjs.map +0 -1
- package/dist/chunk-QQK7MLCR.mjs.map +0 -1
- package/dist/chunk-WPI6YSAR.mjs.map +0 -1
- package/dist-web/assets/CanvasRenderer-BKCvcc2p.js +0 -1
- package/dist-web/assets/DebugToolsPanel.vue_vue_type_script_setup_true_lang-HGJb0Et1.js +0 -110
- package/dist-web/assets/electron-BHaEaaZP.js +0 -1
- package/dist-web/assets/fetch-CBZbgZqk.js +0 -17
- package/dist-web/assets/index--6C0sK6a.css +0 -1
- package/dist-web/assets/index-B6wEGata.js +0 -173
- package/dist-web/assets/index-BgVbj5qX.js +0 -12
- package/dist-web/assets/index-BkoXaBV_.js +0 -64
- package/dist-web/assets/index-C56IuV5U.css +0 -1
- package/dist-web/assets/index-CLRaKwpT.js +0 -2
- package/dist-web/assets/index-Dj1BSD7O.js +0 -1
- package/dist-web/assets/index-RoDgJcHU.js +0 -2
- package/dist-web/assets/index-aljYO5ms.js +0 -1
- /package/dist/{chat-command-JKG3FY5B.mjs.map → chat-command-UZ23GKO7.mjs.map} +0 -0
- /package/dist/{chunk-D4WTE7LV.mjs.map → chunk-CNSYXRJP.mjs.map} +0 -0
- /package/dist/{chunk-I35WHWA6.mjs.map → chunk-FCCL5Z4W.mjs.map} +0 -0
- /package/dist/{chunk-2MZ6G4AC.mjs.map → chunk-MUPILWAZ.mjs.map} +0 -0
- /package/dist/{chunk-7RW4CQAO.mjs.map → chunk-P3ZOLTGS.mjs.map} +0 -0
- /package/dist/{chunk-6FPEP2ZX.mjs.map → chunk-RDR6PS2R.mjs.map} +0 -0
- /package/dist/{chunk-SKUG5XVD.mjs.map → chunk-ZFU2CHW3.mjs.map} +0 -0
- /package/dist/{http-5E7ARYFW.mjs.map → http-UWRA5FLB.mjs.map} +0 -0
- /package/dist/{import-3KG6VK6O.mjs.map → import-745KQL7N.mjs.map} +0 -0
- /package/dist/{mcp-JBI4CUQT.mjs.map → mcp-UHN5JOHC.mjs.map} +0 -0
- /package/dist/{src-UTBQAOJ3.mjs.map → src-Y7MDRXLJ.mjs.map} +0 -0
- /package/dist/{stream-import-7HZ4IRYJ.mjs.map → stream-import-3JMXQD7P.mjs.map} +0 -0
- /package/dist/{worker-runtime-ALS4MR2A.mjs.map → worker-runtime-63RYMGZI.mjs.map} +0 -0
|
@@ -9,19 +9,20 @@ import {
|
|
|
9
9
|
analyzeNewImport,
|
|
10
10
|
incrementalImport,
|
|
11
11
|
streamImport
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-FCCL5Z4W.mjs";
|
|
13
13
|
import {
|
|
14
14
|
closeServerAiLogger,
|
|
15
15
|
createCliRunAgentStream,
|
|
16
|
+
getServerAiLogger,
|
|
16
17
|
initServerAiLogger
|
|
17
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-ZFU2CHW3.mjs";
|
|
18
19
|
import {
|
|
19
20
|
AGENT_TOOL_REGISTRY,
|
|
20
21
|
CoreDataProvider
|
|
21
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-5UBHNQFZ.mjs";
|
|
22
23
|
import {
|
|
23
24
|
assertCliDataDirCompatible
|
|
24
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-RDR6PS2R.mjs";
|
|
25
26
|
import {
|
|
26
27
|
getVersion
|
|
27
28
|
} from "./chunk-5ADKYYNA.mjs";
|
|
@@ -42,7 +43,7 @@ import {
|
|
|
42
43
|
getPendingNodeDataDirMigration,
|
|
43
44
|
hasPendingElectronDataWarning,
|
|
44
45
|
serializeChatLabToJsonl
|
|
45
|
-
} from "./chunk-
|
|
46
|
+
} from "./chunk-P3ZOLTGS.mjs";
|
|
46
47
|
import {
|
|
47
48
|
AIChatManager,
|
|
48
49
|
ArchiveImportError,
|
|
@@ -89,7 +90,7 @@ import {
|
|
|
89
90
|
streamParseFileInfo,
|
|
90
91
|
summary_service_exports,
|
|
91
92
|
validateApiKey
|
|
92
|
-
} from "./chunk-
|
|
93
|
+
} from "./chunk-A2SANKG4.mjs";
|
|
93
94
|
import {
|
|
94
95
|
ALL_MIGRATIONS,
|
|
95
96
|
BUILTIN_MODELS,
|
|
@@ -152,10 +153,10 @@ import {
|
|
|
152
153
|
verifyCliDataPath,
|
|
153
154
|
writeAuthProfile,
|
|
154
155
|
writeConfigField
|
|
155
|
-
} from "./chunk-
|
|
156
|
+
} from "./chunk-7BZLS2KW.mjs";
|
|
156
157
|
|
|
157
158
|
// src/http/index.ts
|
|
158
|
-
import * as
|
|
159
|
+
import * as fs12 from "fs";
|
|
159
160
|
import * as crypto4 from "crypto";
|
|
160
161
|
|
|
161
162
|
// src/http/server.ts
|
|
@@ -1097,7 +1098,14 @@ function registerSqlRoutes(server2, ctx) {
|
|
|
1097
1098
|
return reply.code(400).send({ error: "Missing sql parameter" });
|
|
1098
1099
|
}
|
|
1099
1100
|
try {
|
|
1100
|
-
|
|
1101
|
+
const result = executeSql(db, sql, { columnar: true, timing: true, maxRows: 0 });
|
|
1102
|
+
return {
|
|
1103
|
+
columns: result.columns,
|
|
1104
|
+
rows: result.rows,
|
|
1105
|
+
rowCount: result.rowCount,
|
|
1106
|
+
duration: result.duration ?? 0,
|
|
1107
|
+
limited: result.truncated
|
|
1108
|
+
};
|
|
1101
1109
|
} catch (err) {
|
|
1102
1110
|
const message = err instanceof Error ? err.message : "SQL execution error";
|
|
1103
1111
|
return reply.code(400).send({ error: message });
|
|
@@ -1794,13 +1802,53 @@ function registerAiSummaryRoutes(server2, ctx) {
|
|
|
1794
1802
|
});
|
|
1795
1803
|
}
|
|
1796
1804
|
|
|
1797
|
-
// ../../packages/http-routes/src/routes/web/ai-
|
|
1805
|
+
// ../../packages/http-routes/src/routes/web/ai-logs.ts
|
|
1806
|
+
import fs3 from "fs";
|
|
1798
1807
|
import path4 from "path";
|
|
1808
|
+
function findLatestAiLogPath(logsDir) {
|
|
1809
|
+
const aiLogDir = path4.join(logsDir, "ai");
|
|
1810
|
+
if (!fs3.existsSync(aiLogDir)) return null;
|
|
1811
|
+
const latest = fs3.readdirSync(aiLogDir).filter((name) => name.startsWith("ai_") && name.endsWith(".log")).map((name) => {
|
|
1812
|
+
const filePath = path4.join(aiLogDir, name);
|
|
1813
|
+
const stat2 = fs3.statSync(filePath);
|
|
1814
|
+
return { path: filePath, mtimeMs: stat2.mtimeMs };
|
|
1815
|
+
}).sort((a, b) => b.mtimeMs - a.mtimeMs)[0];
|
|
1816
|
+
return latest?.path ?? null;
|
|
1817
|
+
}
|
|
1818
|
+
function resolveAiLogPath(ctx) {
|
|
1819
|
+
const currentPath = ctx.getCurrentAiLogPath?.();
|
|
1820
|
+
if (currentPath && fs3.existsSync(currentPath)) return currentPath;
|
|
1821
|
+
return findLatestAiLogPath(ctx.pathProvider.getLogsDir());
|
|
1822
|
+
}
|
|
1823
|
+
function isLoopbackAddress(address) {
|
|
1824
|
+
if (!address) return true;
|
|
1825
|
+
const normalized = address.replace(/^::ffff:/, "");
|
|
1826
|
+
return normalized === "localhost" || normalized === "::1" || normalized === "0:0:0:0:0:0:0:1" || normalized.startsWith("127.");
|
|
1827
|
+
}
|
|
1828
|
+
function registerAiLogRoutes(server2, ctx) {
|
|
1829
|
+
server2.post("/_web/ai/logs/show", async (request, reply) => {
|
|
1830
|
+
if (!ctx.showInFolder) return reply.code(501).send({ success: false, error: "Not supported" });
|
|
1831
|
+
if (!isLoopbackAddress(request.ip)) {
|
|
1832
|
+
return { success: false, error: "Opening AI logs is only supported on this device" };
|
|
1833
|
+
}
|
|
1834
|
+
const logPath = resolveAiLogPath(ctx);
|
|
1835
|
+
if (!logPath) return { success: false, error: "No AI log files found" };
|
|
1836
|
+
try {
|
|
1837
|
+
await ctx.showInFolder(logPath);
|
|
1838
|
+
return { success: true, path: logPath };
|
|
1839
|
+
} catch (error) {
|
|
1840
|
+
return { success: false, error: error instanceof Error ? error.message : String(error) };
|
|
1841
|
+
}
|
|
1842
|
+
});
|
|
1843
|
+
}
|
|
1844
|
+
|
|
1845
|
+
// ../../packages/http-routes/src/routes/web/ai-semantic-index.ts
|
|
1846
|
+
import path5 from "path";
|
|
1799
1847
|
function registerSemanticIndexRoutes(server2, ctx) {
|
|
1800
1848
|
const service = ctx.semanticIndexService;
|
|
1801
1849
|
if (!service) {
|
|
1802
1850
|
if (ctx.aiDataDir) {
|
|
1803
|
-
const configStore = new SemanticIndexConfigStore(
|
|
1851
|
+
const configStore = new SemanticIndexConfigStore(path5.join(ctx.aiDataDir, SEMANTIC_INDEX_CONFIG_FILE));
|
|
1804
1852
|
const configResponse = (config) => ({
|
|
1805
1853
|
config,
|
|
1806
1854
|
apiKeySet: resolveSemanticIndexApiKeySet(config, ctx.resolveApiKey),
|
|
@@ -1855,8 +1903,8 @@ function registerSemanticIndexRoutes(server2, ctx) {
|
|
|
1855
1903
|
server2.post("/_web/ai/semantic-index/status", async (request) => {
|
|
1856
1904
|
return { sessions: await service.statusForSessions(request.body.sessionIds ?? []) };
|
|
1857
1905
|
});
|
|
1858
|
-
const sessionAction = (
|
|
1859
|
-
server2.post(
|
|
1906
|
+
const sessionAction = (path14, action) => {
|
|
1907
|
+
server2.post(path14, async (request, reply) => {
|
|
1860
1908
|
const { sessionId } = request.body;
|
|
1861
1909
|
if (!sessionId) return reply.code(400).send({ error: "sessionId is required" });
|
|
1862
1910
|
await action(sessionId);
|
|
@@ -1887,9 +1935,9 @@ function registerSemanticIndexRoutes(server2, ctx) {
|
|
|
1887
1935
|
}
|
|
1888
1936
|
|
|
1889
1937
|
// ../../packages/http-routes/src/routes/web/merge.ts
|
|
1890
|
-
import * as
|
|
1938
|
+
import * as fs4 from "fs";
|
|
1891
1939
|
import * as os from "os";
|
|
1892
|
-
import * as
|
|
1940
|
+
import * as path6 from "path";
|
|
1893
1941
|
function ensureDb2(ctx, sessionId) {
|
|
1894
1942
|
const db = ctx.dbManager.open(sessionId);
|
|
1895
1943
|
if (!db) throw sessionNotFound(sessionId);
|
|
@@ -1906,31 +1954,31 @@ function registerMergeRoutes(server2, ctx) {
|
|
|
1906
1954
|
if (contentType.includes("multipart/form-data")) {
|
|
1907
1955
|
const data = await request.file();
|
|
1908
1956
|
if (!data) return reply.code(400).send({ error: "No file uploaded" });
|
|
1909
|
-
const tmpDir =
|
|
1910
|
-
const tmpPath =
|
|
1957
|
+
const tmpDir = fs4.mkdtempSync(path6.join(os.tmpdir(), "chatlab-merge-"));
|
|
1958
|
+
const tmpPath = path6.join(tmpDir, data.filename || "upload");
|
|
1911
1959
|
const chunks = [];
|
|
1912
1960
|
for await (const chunk of data.file) {
|
|
1913
1961
|
chunks.push(chunk);
|
|
1914
1962
|
}
|
|
1915
|
-
|
|
1963
|
+
fs4.writeFileSync(tmpPath, Buffer.concat(chunks));
|
|
1916
1964
|
filePath = tmpPath;
|
|
1917
1965
|
cleanupFile = true;
|
|
1918
1966
|
cleanupDir = tmpDir;
|
|
1919
1967
|
} else {
|
|
1920
1968
|
const body = request.body;
|
|
1921
1969
|
if (!body?.filePath) return reply.code(400).send({ error: "Missing filePath in body" });
|
|
1922
|
-
if (!
|
|
1970
|
+
if (!fs4.existsSync(body.filePath)) return reply.code(400).send({ error: "File not found" });
|
|
1923
1971
|
filePath = body.filePath;
|
|
1924
1972
|
}
|
|
1925
1973
|
try {
|
|
1926
1974
|
const result = await streamParseFileInfo(filePath, {
|
|
1927
1975
|
createTempDatabase(sourceFilePath) {
|
|
1928
|
-
return mergeCache.createTempDatabase(
|
|
1976
|
+
return mergeCache.createTempDatabase(path6.basename(sourceFilePath));
|
|
1929
1977
|
},
|
|
1930
1978
|
onProgress() {
|
|
1931
1979
|
}
|
|
1932
1980
|
});
|
|
1933
|
-
const filename =
|
|
1981
|
+
const filename = path6.basename(filePath);
|
|
1934
1982
|
const handle = mergeCache.store(filename, result.tempDbPath);
|
|
1935
1983
|
return {
|
|
1936
1984
|
handle,
|
|
@@ -1944,13 +1992,13 @@ function registerMergeRoutes(server2, ctx) {
|
|
|
1944
1992
|
} finally {
|
|
1945
1993
|
if (cleanupFile) {
|
|
1946
1994
|
try {
|
|
1947
|
-
|
|
1995
|
+
fs4.unlinkSync(filePath);
|
|
1948
1996
|
} catch {
|
|
1949
1997
|
}
|
|
1950
1998
|
}
|
|
1951
1999
|
if (cleanupDir) {
|
|
1952
2000
|
try {
|
|
1953
|
-
|
|
2001
|
+
fs4.rmdirSync(cleanupDir);
|
|
1954
2002
|
} catch {
|
|
1955
2003
|
}
|
|
1956
2004
|
}
|
|
@@ -1999,20 +2047,20 @@ function registerMergeRoutes(server2, ctx) {
|
|
|
1999
2047
|
return reply.code(501).send({ error: "Import not available on this server" });
|
|
2000
2048
|
}
|
|
2001
2049
|
const jsonData = JSON.stringify(merged.chatLabData);
|
|
2002
|
-
const tmpDir =
|
|
2003
|
-
const safeName =
|
|
2004
|
-
const tmpPath =
|
|
2005
|
-
|
|
2050
|
+
const tmpDir = fs4.mkdtempSync(path6.join(os.tmpdir(), "chatlab-merged-"));
|
|
2051
|
+
const safeName = path6.basename(outputName).replace(/[/\\?%*:|"<>]/g, "_") || "merged";
|
|
2052
|
+
const tmpPath = path6.join(tmpDir, `${safeName}.json`);
|
|
2053
|
+
fs4.writeFileSync(tmpPath, jsonData, "utf-8");
|
|
2006
2054
|
try {
|
|
2007
2055
|
const importResult = await streamImport2(dbManager2, tmpPath);
|
|
2008
2056
|
sessionId = importResult.sessionId;
|
|
2009
2057
|
} finally {
|
|
2010
2058
|
try {
|
|
2011
|
-
|
|
2059
|
+
fs4.unlinkSync(tmpPath);
|
|
2012
2060
|
} catch {
|
|
2013
2061
|
}
|
|
2014
2062
|
try {
|
|
2015
|
-
|
|
2063
|
+
fs4.rmdirSync(tmpDir);
|
|
2016
2064
|
} catch {
|
|
2017
2065
|
}
|
|
2018
2066
|
}
|
|
@@ -2086,16 +2134,16 @@ function registerMergeRoutes(server2, ctx) {
|
|
|
2086
2134
|
}
|
|
2087
2135
|
|
|
2088
2136
|
// ../../packages/http-routes/src/routes/web/cache.ts
|
|
2089
|
-
import * as
|
|
2137
|
+
import * as fs5 from "fs";
|
|
2090
2138
|
import * as fsp from "fs/promises";
|
|
2091
|
-
import * as
|
|
2139
|
+
import * as path7 from "path";
|
|
2092
2140
|
async function getDirSize(dirPath) {
|
|
2093
2141
|
let totalSize = 0;
|
|
2094
2142
|
try {
|
|
2095
|
-
if (!
|
|
2143
|
+
if (!fs5.existsSync(dirPath)) return 0;
|
|
2096
2144
|
const files = await fsp.readdir(dirPath, { withFileTypes: true });
|
|
2097
2145
|
for (const file of files) {
|
|
2098
|
-
const filePath =
|
|
2146
|
+
const filePath = path7.join(dirPath, file.name);
|
|
2099
2147
|
if (file.isDirectory()) {
|
|
2100
2148
|
totalSize += await getDirSize(filePath);
|
|
2101
2149
|
} else {
|
|
@@ -2110,10 +2158,10 @@ async function getDirSize(dirPath) {
|
|
|
2110
2158
|
async function getFileCount(dirPath) {
|
|
2111
2159
|
let count = 0;
|
|
2112
2160
|
try {
|
|
2113
|
-
if (!
|
|
2161
|
+
if (!fs5.existsSync(dirPath)) return 0;
|
|
2114
2162
|
const files = await fsp.readdir(dirPath, { withFileTypes: true });
|
|
2115
2163
|
for (const file of files) {
|
|
2116
|
-
const filePath =
|
|
2164
|
+
const filePath = path7.join(dirPath, file.name);
|
|
2117
2165
|
if (file.isDirectory()) {
|
|
2118
2166
|
count += await getFileCount(filePath);
|
|
2119
2167
|
} else {
|
|
@@ -2126,10 +2174,10 @@ async function getFileCount(dirPath) {
|
|
|
2126
2174
|
}
|
|
2127
2175
|
function hasChatLabDatabases(dirPath) {
|
|
2128
2176
|
try {
|
|
2129
|
-
const markerPath =
|
|
2130
|
-
const dbDir =
|
|
2131
|
-
if (!
|
|
2132
|
-
return
|
|
2177
|
+
const markerPath = path7.join(dirPath, ".chatlab");
|
|
2178
|
+
const dbDir = path7.join(dirPath, "databases");
|
|
2179
|
+
if (!fs5.existsSync(markerPath) || !fs5.existsSync(dbDir)) return false;
|
|
2180
|
+
return fs5.readdirSync(dbDir).some((file) => file.endsWith(".db"));
|
|
2133
2181
|
} catch {
|
|
2134
2182
|
return false;
|
|
2135
2183
|
}
|
|
@@ -2175,7 +2223,7 @@ function registerCacheRoutes(server2, ctx) {
|
|
|
2175
2223
|
const results = await Promise.all(
|
|
2176
2224
|
directories.map(async (dir) => {
|
|
2177
2225
|
const [size, fileCount] = await Promise.all([getDirSize(dir.path), getFileCount(dir.path)]);
|
|
2178
|
-
return { ...dir, size, fileCount, exists:
|
|
2226
|
+
return { ...dir, size, fileCount, exists: fs5.existsSync(dir.path) };
|
|
2179
2227
|
})
|
|
2180
2228
|
);
|
|
2181
2229
|
return {
|
|
@@ -2192,10 +2240,10 @@ function registerCacheRoutes(server2, ctx) {
|
|
|
2192
2240
|
};
|
|
2193
2241
|
const dirPath = allowedDirs[cacheId];
|
|
2194
2242
|
if (!dirPath) return { success: false, error: "Not allowed to clear this directory" };
|
|
2195
|
-
if (!
|
|
2243
|
+
if (!fs5.existsSync(dirPath)) return { success: true };
|
|
2196
2244
|
const files = await fsp.readdir(dirPath);
|
|
2197
2245
|
for (const file of files) {
|
|
2198
|
-
const filePath =
|
|
2246
|
+
const filePath = path7.join(dirPath, file);
|
|
2199
2247
|
const stat2 = await fsp.stat(filePath);
|
|
2200
2248
|
if (stat2.isDirectory()) {
|
|
2201
2249
|
await fsp.rm(filePath, { recursive: true });
|
|
@@ -2212,7 +2260,7 @@ function registerCacheRoutes(server2, ctx) {
|
|
|
2212
2260
|
defaultPath: ctx.defaultUserDataDir,
|
|
2213
2261
|
isCustom: ctx.isCustomDataDir ?? false,
|
|
2214
2262
|
canSetDataDir: ctx.canSetDataDir ?? Boolean(ctx.setDataDir),
|
|
2215
|
-
hasLegacyDataAtDefaultDir: Boolean(ctx.defaultUserDataDir) &&
|
|
2263
|
+
hasLegacyDataAtDefaultDir: Boolean(ctx.defaultUserDataDir) && path7.resolve(pp.getUserDataDir()) !== path7.resolve(ctx.defaultUserDataDir ?? "") && hasChatLabDatabases(ctx.defaultUserDataDir ?? ""),
|
|
2216
2264
|
pendingMigration: pending ? {
|
|
2217
2265
|
from: pending.from,
|
|
2218
2266
|
to: pending.to,
|
|
@@ -2229,8 +2277,8 @@ function registerCacheRoutes(server2, ctx) {
|
|
|
2229
2277
|
return ctx.setDataDir(targetPath, migrate);
|
|
2230
2278
|
});
|
|
2231
2279
|
server2.get("/_web/cache/latest-import-log", async () => {
|
|
2232
|
-
const importLogDir =
|
|
2233
|
-
if (!
|
|
2280
|
+
const importLogDir = path7.join(pp.getLogsDir(), "import");
|
|
2281
|
+
if (!fs5.existsSync(importLogDir)) {
|
|
2234
2282
|
return { success: false, error: "Log directory not found" };
|
|
2235
2283
|
}
|
|
2236
2284
|
const files = await fsp.readdir(importLogDir);
|
|
@@ -2240,7 +2288,7 @@ function registerCacheRoutes(server2, ctx) {
|
|
|
2240
2288
|
}
|
|
2241
2289
|
const fileStats = await Promise.all(
|
|
2242
2290
|
logFiles.map(async (f) => {
|
|
2243
|
-
const filePath =
|
|
2291
|
+
const filePath = path7.join(importLogDir, f);
|
|
2244
2292
|
const stat2 = await fsp.stat(filePath);
|
|
2245
2293
|
return { name: f, path: filePath, mtime: stat2.mtime.getTime() };
|
|
2246
2294
|
})
|
|
@@ -2264,11 +2312,11 @@ function registerCacheRoutes(server2, ctx) {
|
|
|
2264
2312
|
const base64Data = dataUrl.replace(/^data:[^,]+,/, "");
|
|
2265
2313
|
buffer = Buffer.from(base64Data, "base64");
|
|
2266
2314
|
}
|
|
2267
|
-
if (!
|
|
2268
|
-
|
|
2315
|
+
if (!fs5.existsSync(downloadsDir)) {
|
|
2316
|
+
fs5.mkdirSync(downloadsDir, { recursive: true });
|
|
2269
2317
|
}
|
|
2270
|
-
const filePath =
|
|
2271
|
-
|
|
2318
|
+
const filePath = path7.join(downloadsDir, filename);
|
|
2319
|
+
fs5.writeFileSync(filePath, buffer);
|
|
2272
2320
|
return { success: true, filePath };
|
|
2273
2321
|
});
|
|
2274
2322
|
server2.post("/_web/cache/open-dir", async (request, reply) => {
|
|
@@ -2284,7 +2332,7 @@ function registerCacheRoutes(server2, ctx) {
|
|
|
2284
2332
|
};
|
|
2285
2333
|
const dirPath = dirPaths[cacheId];
|
|
2286
2334
|
if (!dirPath) return { success: false, error: "Unknown directory" };
|
|
2287
|
-
if (!
|
|
2335
|
+
if (!fs5.existsSync(dirPath)) {
|
|
2288
2336
|
await fsp.mkdir(dirPath, { recursive: true });
|
|
2289
2337
|
}
|
|
2290
2338
|
try {
|
|
@@ -2384,6 +2432,7 @@ function registerSharedRoutes(server2, ctx, options) {
|
|
|
2384
2432
|
registerAiToolRoutes(server2, resolvedCtx);
|
|
2385
2433
|
registerAiChatRoutes(server2, resolvedCtx);
|
|
2386
2434
|
registerAiSummaryRoutes(server2, resolvedCtx);
|
|
2435
|
+
registerAiLogRoutes(server2, resolvedCtx);
|
|
2387
2436
|
registerSemanticIndexRoutes(server2, resolvedCtx);
|
|
2388
2437
|
registerMergeRoutes(server2, resolvedCtx);
|
|
2389
2438
|
registerCacheRoutes(server2, resolvedCtx);
|
|
@@ -2434,6 +2483,150 @@ function requireBearerToken(request, reply) {
|
|
|
2434
2483
|
}
|
|
2435
2484
|
}
|
|
2436
2485
|
|
|
2486
|
+
// ../../packages/http-routes/src/routes/web/automation.ts
|
|
2487
|
+
function requireAutomation(ctx) {
|
|
2488
|
+
return ctx.automation ?? null;
|
|
2489
|
+
}
|
|
2490
|
+
function normalizeBaseUrl(input) {
|
|
2491
|
+
let url = input.trim().replace(/\/+$/, "");
|
|
2492
|
+
if (url && !/^https?:\/\//i.test(url)) url = `http://${url}`;
|
|
2493
|
+
if (url && !url.endsWith("/api/v1")) {
|
|
2494
|
+
url = url.replace(/\/api\/v1$/, "") + "/api/v1";
|
|
2495
|
+
}
|
|
2496
|
+
return url;
|
|
2497
|
+
}
|
|
2498
|
+
function buildRemoteSessionsUrl(baseUrl, query = {}) {
|
|
2499
|
+
const searchParams = new URLSearchParams();
|
|
2500
|
+
searchParams.set("format", "chatlab");
|
|
2501
|
+
if (query.keyword?.trim()) searchParams.set("keyword", query.keyword.trim());
|
|
2502
|
+
if (query.limit && Number(query.limit) > 0) searchParams.set("limit", String(query.limit));
|
|
2503
|
+
if (query.cursor) searchParams.set("cursor", query.cursor);
|
|
2504
|
+
return `${baseUrl}/sessions?${searchParams.toString()}`;
|
|
2505
|
+
}
|
|
2506
|
+
function asRecord(value) {
|
|
2507
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
2508
|
+
}
|
|
2509
|
+
function parseRemoteSessionsResponse(body) {
|
|
2510
|
+
const parsed = JSON.parse(body);
|
|
2511
|
+
let sessions;
|
|
2512
|
+
let pageSource;
|
|
2513
|
+
if (Array.isArray(parsed)) {
|
|
2514
|
+
sessions = parsed;
|
|
2515
|
+
} else {
|
|
2516
|
+
const record = asRecord(parsed);
|
|
2517
|
+
const data = asRecord(record?.data);
|
|
2518
|
+
const sessionsSource = record?.sessions ?? data?.sessions ?? record?.data;
|
|
2519
|
+
sessions = Array.isArray(sessionsSource) ? sessionsSource : [];
|
|
2520
|
+
pageSource = asRecord(record?.page) ?? asRecord(data?.page);
|
|
2521
|
+
}
|
|
2522
|
+
return {
|
|
2523
|
+
sessions,
|
|
2524
|
+
page: pageSource && typeof pageSource === "object" ? {
|
|
2525
|
+
hasMore: Boolean(pageSource.hasMore),
|
|
2526
|
+
nextCursor: typeof pageSource.nextCursor === "string" ? pageSource.nextCursor : void 0
|
|
2527
|
+
} : void 0
|
|
2528
|
+
};
|
|
2529
|
+
}
|
|
2530
|
+
function registerAutomationRoutes(server2, ctx) {
|
|
2531
|
+
server2.get("/_web/automation/config", async (_request, reply) => {
|
|
2532
|
+
const automation = requireAutomation(ctx);
|
|
2533
|
+
if (!automation?.serverInfo) return reply.code(404).send({ error: "Automation is not enabled" });
|
|
2534
|
+
return {
|
|
2535
|
+
enabled: true,
|
|
2536
|
+
port: automation.serverInfo.port,
|
|
2537
|
+
token: automation.serverInfo.token,
|
|
2538
|
+
host: automation.serverInfo.host
|
|
2539
|
+
};
|
|
2540
|
+
});
|
|
2541
|
+
server2.get("/_web/automation/data-sources", async (_request, reply) => {
|
|
2542
|
+
const automation = requireAutomation(ctx);
|
|
2543
|
+
if (!automation) return reply.code(404).send({ error: "Automation is not enabled" });
|
|
2544
|
+
return automation.dsManager.loadAll();
|
|
2545
|
+
});
|
|
2546
|
+
server2.post("/_web/automation/data-sources", async (request, reply) => {
|
|
2547
|
+
const automation = requireAutomation(ctx);
|
|
2548
|
+
if (!automation) return reply.code(404).send({ error: "Automation is not enabled" });
|
|
2549
|
+
const ds = automation.dsManager.add(request.body);
|
|
2550
|
+
automation.reloadTimer?.(ds.id);
|
|
2551
|
+
return ds;
|
|
2552
|
+
});
|
|
2553
|
+
server2.patch("/_web/automation/data-sources/:id", async (request, reply) => {
|
|
2554
|
+
const automation = requireAutomation(ctx);
|
|
2555
|
+
if (!automation) return reply.code(404).send({ error: "Automation is not enabled" });
|
|
2556
|
+
const ds = automation.dsManager.update(request.params.id, request.body);
|
|
2557
|
+
if (!ds) return reply.code(404).send({ error: "Data source not found" });
|
|
2558
|
+
automation.reloadTimer?.(ds.id);
|
|
2559
|
+
return ds;
|
|
2560
|
+
});
|
|
2561
|
+
server2.delete("/_web/automation/data-sources/:id", async (request, reply) => {
|
|
2562
|
+
const automation = requireAutomation(ctx);
|
|
2563
|
+
if (!automation) return reply.code(404).send({ error: "Automation is not enabled" });
|
|
2564
|
+
automation.stopTimer?.(request.params.id);
|
|
2565
|
+
const ok = automation.dsManager.delete(request.params.id);
|
|
2566
|
+
if (!ok) return reply.code(404).send({ error: "Data source not found" });
|
|
2567
|
+
return { success: true };
|
|
2568
|
+
});
|
|
2569
|
+
server2.post("/_web/automation/data-sources/:id/sessions", async (request, reply) => {
|
|
2570
|
+
const automation = requireAutomation(ctx);
|
|
2571
|
+
if (!automation) return reply.code(404).send({ error: "Automation is not enabled" });
|
|
2572
|
+
const added = automation.dsManager.addSessions(request.params.id, request.body.sessions);
|
|
2573
|
+
if (added.length === 0 && !automation.dsManager.get(request.params.id)) {
|
|
2574
|
+
return reply.code(404).send({ error: "Data source not found" });
|
|
2575
|
+
}
|
|
2576
|
+
automation.reloadTimer?.(request.params.id, true);
|
|
2577
|
+
for (const sess of added) {
|
|
2578
|
+
void automation.pullEngine.triggerPull(request.params.id, sess.id).catch(() => void 0);
|
|
2579
|
+
}
|
|
2580
|
+
return added;
|
|
2581
|
+
});
|
|
2582
|
+
server2.delete("/_web/automation/data-sources/:id/sessions/:sessId", async (request, reply) => {
|
|
2583
|
+
const automation = requireAutomation(ctx);
|
|
2584
|
+
if (!automation) return reply.code(404).send({ error: "Automation is not enabled" });
|
|
2585
|
+
const removed = automation.dsManager.removeSession(request.params.id, request.params.sessId);
|
|
2586
|
+
if (!removed) return reply.code(404).send({ error: "Session not found" });
|
|
2587
|
+
automation.reloadTimer?.(request.params.id);
|
|
2588
|
+
if (request.query.deleteData === "true" && removed.targetSessionId) {
|
|
2589
|
+
automation.deleteSessionData?.(removed.targetSessionId);
|
|
2590
|
+
}
|
|
2591
|
+
return { success: true };
|
|
2592
|
+
});
|
|
2593
|
+
server2.post(
|
|
2594
|
+
"/_web/automation/data-sources/:id/pull",
|
|
2595
|
+
async (request, reply) => {
|
|
2596
|
+
const automation = requireAutomation(ctx);
|
|
2597
|
+
if (!automation) return reply.code(404).send({ error: "Automation is not enabled" });
|
|
2598
|
+
return automation.pullEngine.triggerPull(request.params.id, request.body?.sessionId);
|
|
2599
|
+
}
|
|
2600
|
+
);
|
|
2601
|
+
server2.post("/_web/automation/data-sources/:id/pull-all", async (request, reply) => {
|
|
2602
|
+
const automation = requireAutomation(ctx);
|
|
2603
|
+
if (!automation) return reply.code(404).send({ error: "Automation is not enabled" });
|
|
2604
|
+
return automation.pullEngine.triggerPullAll(request.params.id);
|
|
2605
|
+
});
|
|
2606
|
+
server2.get("/_web/automation/sync-progress", async (_request, reply) => {
|
|
2607
|
+
const automation = requireAutomation(ctx);
|
|
2608
|
+
if (!automation) return reply.code(404).send({ error: "Automation is not enabled" });
|
|
2609
|
+
return automation.pullEngine.getProgress();
|
|
2610
|
+
});
|
|
2611
|
+
server2.get("/_web/automation/remote-sessions", async (request, reply) => {
|
|
2612
|
+
const automation = requireAutomation(ctx);
|
|
2613
|
+
if (!automation) return reply.code(404).send({ error: "Automation is not enabled" });
|
|
2614
|
+
const { baseUrl, token, keyword, limit, cursor } = request.query;
|
|
2615
|
+
if (!baseUrl) return reply.code(400).send({ error: "baseUrl is required" });
|
|
2616
|
+
const url = buildRemoteSessionsUrl(normalizeBaseUrl(baseUrl), { keyword, limit, cursor });
|
|
2617
|
+
const headers = { Accept: "application/json" };
|
|
2618
|
+
if (token) headers.Authorization = `Bearer ${token}`;
|
|
2619
|
+
try {
|
|
2620
|
+
const resp = await fetch(url, { headers, signal: AbortSignal.timeout(3e4) });
|
|
2621
|
+
if (!resp.ok) throw new Error(`Remote server returned HTTP ${resp.status}`);
|
|
2622
|
+
const body = await resp.text();
|
|
2623
|
+
return parseRemoteSessionsResponse(body);
|
|
2624
|
+
} catch (err) {
|
|
2625
|
+
return reply.code(502).send({ error: err instanceof Error ? err.message : "Failed to fetch remote sessions" });
|
|
2626
|
+
}
|
|
2627
|
+
});
|
|
2628
|
+
}
|
|
2629
|
+
|
|
2437
2630
|
// src/http/server.ts
|
|
2438
2631
|
var JSON_BODY_LIMIT = 50 * 1024 * 1024;
|
|
2439
2632
|
function createServer() {
|
|
@@ -2467,21 +2660,21 @@ function createServer() {
|
|
|
2467
2660
|
|
|
2468
2661
|
// src/http/routes/web/index.ts
|
|
2469
2662
|
import * as os3 from "os";
|
|
2470
|
-
import * as
|
|
2663
|
+
import * as path10 from "path";
|
|
2471
2664
|
|
|
2472
2665
|
// src/http/routes/web/import.ts
|
|
2473
|
-
import * as
|
|
2666
|
+
import * as fs7 from "fs";
|
|
2474
2667
|
import * as os2 from "os";
|
|
2475
|
-
import * as
|
|
2668
|
+
import * as path8 from "path";
|
|
2476
2669
|
import { randomUUID } from "crypto";
|
|
2477
2670
|
import { pipeline } from "stream/promises";
|
|
2478
2671
|
|
|
2479
2672
|
// src/http/routes/web/helpers.ts
|
|
2480
|
-
import * as
|
|
2673
|
+
import * as fs6 from "fs";
|
|
2481
2674
|
function resolveNativeBinding() {
|
|
2482
2675
|
if (process.versions.electron) return void 0;
|
|
2483
2676
|
const nativePath = resolveCliPath("native/better_sqlite3.node");
|
|
2484
|
-
if (
|
|
2677
|
+
if (fs6.existsSync(nativePath)) return nativePath;
|
|
2485
2678
|
return void 0;
|
|
2486
2679
|
}
|
|
2487
2680
|
|
|
@@ -2491,11 +2684,11 @@ var ARCHIVE_UPLOAD_LIMIT = 50 * 1024 * 1024 * 1024;
|
|
|
2491
2684
|
function cleanupTemp(...paths) {
|
|
2492
2685
|
for (const p of paths) {
|
|
2493
2686
|
try {
|
|
2494
|
-
const stat2 =
|
|
2687
|
+
const stat2 = fs7.statSync(p);
|
|
2495
2688
|
if (stat2.isDirectory()) {
|
|
2496
|
-
|
|
2689
|
+
fs7.rmSync(p, { recursive: true, force: true });
|
|
2497
2690
|
} else {
|
|
2498
|
-
|
|
2691
|
+
fs7.unlinkSync(p);
|
|
2499
2692
|
}
|
|
2500
2693
|
} catch {
|
|
2501
2694
|
}
|
|
@@ -2503,7 +2696,7 @@ function cleanupTemp(...paths) {
|
|
|
2503
2696
|
}
|
|
2504
2697
|
function registerImportRoutes2(server2, dbManager2, options = {}) {
|
|
2505
2698
|
const sourceManager = options.sourceManager ?? new ArchiveImportSourceManager({
|
|
2506
|
-
tempRoot:
|
|
2699
|
+
tempRoot: fs7.mkdtempSync(path8.join(os2.tmpdir(), "chatlab-import-sources-"))
|
|
2507
2700
|
});
|
|
2508
2701
|
const runPreparedImport = options.runPreparedImport ?? (async (manifestPath, onProgress) => {
|
|
2509
2702
|
const result = await streamImport(dbManager2, manifestPath, {
|
|
@@ -2527,9 +2720,9 @@ function registerImportRoutes2(server2, dbManager2, options = {}) {
|
|
|
2527
2720
|
limits: { fileSize: ARCHIVE_UPLOAD_LIMIT }
|
|
2528
2721
|
});
|
|
2529
2722
|
if (!data) return reply.code(400).send({ success: false, error: "error.no_file_selected" });
|
|
2530
|
-
const uploadPath =
|
|
2723
|
+
const uploadPath = path8.join(os2.tmpdir(), `chatlab-archive-${randomUUID()}.zip`);
|
|
2531
2724
|
try {
|
|
2532
|
-
await pipeline(data.file,
|
|
2725
|
+
await pipeline(data.file, fs7.createWriteStream(uploadPath, { flags: "wx" }));
|
|
2533
2726
|
if (data.file.truncated) {
|
|
2534
2727
|
cleanupTemp(uploadPath);
|
|
2535
2728
|
return reply.code(413).send({ success: false, error: "error.archive_limit_exceeded" });
|
|
@@ -2594,14 +2787,14 @@ data: ${JSON.stringify(eventData)}
|
|
|
2594
2787
|
server2.post("/_web/detect-format", async (request, reply) => {
|
|
2595
2788
|
const data = await request.file();
|
|
2596
2789
|
if (!data) return reply.code(400).send({ error: "No file uploaded" });
|
|
2597
|
-
const tmpDir =
|
|
2598
|
-
const tmpPath =
|
|
2790
|
+
const tmpDir = fs7.mkdtempSync(path8.join(os2.tmpdir(), "chatlab-detect-"));
|
|
2791
|
+
const tmpPath = path8.join(tmpDir, data.filename || "upload");
|
|
2599
2792
|
try {
|
|
2600
2793
|
const chunks = [];
|
|
2601
2794
|
for await (const chunk of data.file) {
|
|
2602
2795
|
chunks.push(chunk);
|
|
2603
2796
|
}
|
|
2604
|
-
|
|
2797
|
+
fs7.writeFileSync(tmpPath, Buffer.concat(chunks));
|
|
2605
2798
|
const format = detectFormat(tmpPath);
|
|
2606
2799
|
const allFormats = detectAllFormats(tmpPath);
|
|
2607
2800
|
return { format, allFormats };
|
|
@@ -2612,14 +2805,14 @@ data: ${JSON.stringify(eventData)}
|
|
|
2612
2805
|
server2.post("/_web/scan-multi-chat", async (request, reply) => {
|
|
2613
2806
|
const data = await request.file();
|
|
2614
2807
|
if (!data) return reply.code(400).send({ error: "No file uploaded" });
|
|
2615
|
-
const tmpDir =
|
|
2616
|
-
const tmpPath =
|
|
2808
|
+
const tmpDir = fs7.mkdtempSync(path8.join(os2.tmpdir(), "chatlab-scan-"));
|
|
2809
|
+
const tmpPath = path8.join(tmpDir, data.filename || "upload");
|
|
2617
2810
|
try {
|
|
2618
2811
|
const chunks = [];
|
|
2619
2812
|
for await (const chunk of data.file) {
|
|
2620
2813
|
chunks.push(chunk);
|
|
2621
2814
|
}
|
|
2622
|
-
|
|
2815
|
+
fs7.writeFileSync(tmpPath, Buffer.concat(chunks));
|
|
2623
2816
|
const chats = await scanMultiChatFile(tmpPath);
|
|
2624
2817
|
return { chats };
|
|
2625
2818
|
} finally {
|
|
@@ -2629,13 +2822,13 @@ data: ${JSON.stringify(eventData)}
|
|
|
2629
2822
|
server2.post("/_web/import", async (request, reply) => {
|
|
2630
2823
|
const data = await request.file();
|
|
2631
2824
|
if (!data) return reply.code(400).send({ error: "No file uploaded" });
|
|
2632
|
-
const tmpDir =
|
|
2633
|
-
const tmpPath =
|
|
2825
|
+
const tmpDir = fs7.mkdtempSync(path8.join(os2.tmpdir(), "chatlab-import-"));
|
|
2826
|
+
const tmpPath = path8.join(tmpDir, data.filename || "upload");
|
|
2634
2827
|
const chunks = [];
|
|
2635
2828
|
for await (const chunk of data.file) {
|
|
2636
2829
|
chunks.push(chunk);
|
|
2637
2830
|
}
|
|
2638
|
-
|
|
2831
|
+
fs7.writeFileSync(tmpPath, Buffer.concat(chunks));
|
|
2639
2832
|
const formatId = data.fields?.formatId?.value;
|
|
2640
2833
|
const chatIndexStr = data.fields?.chatIndex?.value;
|
|
2641
2834
|
const chatIndex = chatIndexStr !== void 0 ? parseInt(chatIndexStr, 10) : void 0;
|
|
@@ -2678,7 +2871,7 @@ data: ${JSON.stringify(eventData)}
|
|
|
2678
2871
|
server2.post("/_web/import-directory", async (request, reply) => {
|
|
2679
2872
|
const parts = request.parts();
|
|
2680
2873
|
if (!parts) return reply.code(400).send({ error: "No files uploaded" });
|
|
2681
|
-
const tmpDir =
|
|
2874
|
+
const tmpDir = fs7.mkdtempSync(path8.join(os2.tmpdir(), "chatlab-dir-import-"));
|
|
2682
2875
|
const relativePaths = [];
|
|
2683
2876
|
const fileBuffers = [];
|
|
2684
2877
|
try {
|
|
@@ -2706,10 +2899,10 @@ data: ${JSON.stringify(eventData)}
|
|
|
2706
2899
|
if (segments.length > 1) {
|
|
2707
2900
|
relPath = segments.slice(1).join("/");
|
|
2708
2901
|
}
|
|
2709
|
-
const targetPath =
|
|
2710
|
-
if (!targetPath.startsWith(tmpDir +
|
|
2711
|
-
|
|
2712
|
-
|
|
2902
|
+
const targetPath = path8.resolve(tmpDir, relPath);
|
|
2903
|
+
if (!targetPath.startsWith(tmpDir + path8.sep)) continue;
|
|
2904
|
+
fs7.mkdirSync(path8.dirname(targetPath), { recursive: true });
|
|
2905
|
+
fs7.writeFileSync(targetPath, fileBuffers[i].data);
|
|
2713
2906
|
}
|
|
2714
2907
|
const entryPath = findEntryFileInDirectory(tmpDir);
|
|
2715
2908
|
if (!entryPath) {
|
|
@@ -2754,13 +2947,13 @@ data: ${JSON.stringify({ success: false, error: err instanceof Error ? err.messa
|
|
|
2754
2947
|
const sessionId = request.params.id;
|
|
2755
2948
|
const data = await request.file();
|
|
2756
2949
|
if (!data) return reply.code(400).send({ error: "No file uploaded" });
|
|
2757
|
-
const tmpDir =
|
|
2758
|
-
const tmpPath =
|
|
2950
|
+
const tmpDir = fs7.mkdtempSync(path8.join(os2.tmpdir(), "chatlab-inc-"));
|
|
2951
|
+
const tmpPath = path8.join(tmpDir, data.filename || "upload");
|
|
2759
2952
|
const chunks = [];
|
|
2760
2953
|
for await (const chunk of data.file) {
|
|
2761
2954
|
chunks.push(chunk);
|
|
2762
2955
|
}
|
|
2763
|
-
|
|
2956
|
+
fs7.writeFileSync(tmpPath, Buffer.concat(chunks));
|
|
2764
2957
|
reply.raw.writeHead(200, {
|
|
2765
2958
|
"Content-Type": "text/event-stream",
|
|
2766
2959
|
"Cache-Control": "no-cache",
|
|
@@ -2792,13 +2985,13 @@ data: ${JSON.stringify(eventData)}
|
|
|
2792
2985
|
const sessionId = request.params.id;
|
|
2793
2986
|
const data = await request.file();
|
|
2794
2987
|
if (!data) return reply.code(400).send({ error: "No file uploaded" });
|
|
2795
|
-
const tmpDir =
|
|
2796
|
-
const tmpPath =
|
|
2988
|
+
const tmpDir = fs7.mkdtempSync(path8.join(os2.tmpdir(), "chatlab-analyze-"));
|
|
2989
|
+
const tmpPath = path8.join(tmpDir, data.filename || "upload");
|
|
2797
2990
|
const chunks = [];
|
|
2798
2991
|
for await (const chunk of data.file) {
|
|
2799
2992
|
chunks.push(chunk);
|
|
2800
2993
|
}
|
|
2801
|
-
|
|
2994
|
+
fs7.writeFileSync(tmpPath, Buffer.concat(chunks));
|
|
2802
2995
|
try {
|
|
2803
2996
|
return await analyzeIncrementalImport(dbManager2, sessionId, tmpPath);
|
|
2804
2997
|
} catch (err) {
|
|
@@ -2810,13 +3003,13 @@ data: ${JSON.stringify(eventData)}
|
|
|
2810
3003
|
server2.post("/_web/import/analyze", async (request, reply) => {
|
|
2811
3004
|
const data = await request.file();
|
|
2812
3005
|
if (!data) return reply.code(400).send({ error: "No file uploaded" });
|
|
2813
|
-
const tmpDir =
|
|
2814
|
-
const tmpPath =
|
|
3006
|
+
const tmpDir = fs7.mkdtempSync(path8.join(os2.tmpdir(), "chatlab-analyze-"));
|
|
3007
|
+
const tmpPath = path8.join(tmpDir, data.filename || "upload");
|
|
2815
3008
|
const chunks = [];
|
|
2816
3009
|
for await (const chunk of data.file) {
|
|
2817
3010
|
chunks.push(chunk);
|
|
2818
3011
|
}
|
|
2819
|
-
|
|
3012
|
+
fs7.writeFileSync(tmpPath, Buffer.concat(chunks));
|
|
2820
3013
|
try {
|
|
2821
3014
|
return await analyzeNewImport(tmpPath);
|
|
2822
3015
|
} catch (err) {
|
|
@@ -2846,17 +3039,17 @@ data: ${JSON.stringify(eventData)}
|
|
|
2846
3039
|
|
|
2847
3040
|
`);
|
|
2848
3041
|
}
|
|
2849
|
-
const tmpDir =
|
|
3042
|
+
const tmpDir = fs7.mkdtempSync(path8.join(os2.tmpdir(), "chatlab-demo-"));
|
|
2850
3043
|
try {
|
|
2851
3044
|
const localPaths = [];
|
|
2852
3045
|
for (let i = 0; i < total; i++) {
|
|
2853
3046
|
sendEvent("progress", { stage: "downloading", current: i + 1, total });
|
|
2854
|
-
const localPath =
|
|
3047
|
+
const localPath = path8.join(tmpDir, DEMO_FILES[i]);
|
|
2855
3048
|
const resp = await fetch(`${DEMO_BASE_URL}/${locale}/${DEMO_FILES[i]}`, {
|
|
2856
3049
|
signal: AbortSignal.timeout(6e4)
|
|
2857
3050
|
});
|
|
2858
3051
|
if (!resp.ok) throw new Error(`Download demo failed (${DEMO_FILES[i]}): ${resp.status}`);
|
|
2859
|
-
|
|
3052
|
+
fs7.writeFileSync(localPath, Buffer.from(await resp.arrayBuffer()));
|
|
2860
3053
|
localPaths.push(localPath);
|
|
2861
3054
|
}
|
|
2862
3055
|
sendEvent("progress", { stage: "importing", current: 1, total });
|
|
@@ -2887,7 +3080,7 @@ data: ${JSON.stringify(eventData)}
|
|
|
2887
3080
|
}
|
|
2888
3081
|
|
|
2889
3082
|
// src/http/routes/web/cache.ts
|
|
2890
|
-
import * as
|
|
3083
|
+
import * as path9 from "path";
|
|
2891
3084
|
import { execFile } from "child_process";
|
|
2892
3085
|
function runExecFile(file, args, runner = execFile) {
|
|
2893
3086
|
return new Promise((resolve4, reject) => {
|
|
@@ -2915,7 +3108,7 @@ async function showPathInFolder(filePath, platform = process.platform, runner) {
|
|
|
2915
3108
|
} else if (platform === "win32") {
|
|
2916
3109
|
await runExecFile("explorer.exe", [`/select,${filePath}`], runner);
|
|
2917
3110
|
} else {
|
|
2918
|
-
await runExecFile("xdg-open", [
|
|
3111
|
+
await runExecFile("xdg-open", [path9.dirname(filePath)], runner);
|
|
2919
3112
|
}
|
|
2920
3113
|
}
|
|
2921
3114
|
|
|
@@ -2938,27 +3131,27 @@ function registerWebRoutes(server2, dbManager2, options) {
|
|
|
2938
3131
|
const mergeCache = options?.pathProvider ? new MergeSessionCache(options.pathProvider, { nativeBinding: options.nativeBinding }) : null;
|
|
2939
3132
|
mergeCache?.cleanupOrphans();
|
|
2940
3133
|
const fallbackPathProvider = {
|
|
2941
|
-
getSystemDir: () =>
|
|
2942
|
-
getUserDataDir: () =>
|
|
2943
|
-
getDatabaseDir: () =>
|
|
2944
|
-
getVectorDir: () =>
|
|
2945
|
-
getAiDataDir: () =>
|
|
2946
|
-
getSettingsDir: () =>
|
|
2947
|
-
getCacheDir: () =>
|
|
2948
|
-
getTempDir: () =>
|
|
2949
|
-
getLogsDir: () =>
|
|
2950
|
-
getDownloadsDir: () =>
|
|
3134
|
+
getSystemDir: () => path10.join(os3.homedir(), ".chatlab"),
|
|
3135
|
+
getUserDataDir: () => path10.join(os3.homedir(), ".chatlab", "data"),
|
|
3136
|
+
getDatabaseDir: () => path10.join(os3.homedir(), ".chatlab", "data", "databases"),
|
|
3137
|
+
getVectorDir: () => path10.join(os3.homedir(), ".chatlab", "data", "vector"),
|
|
3138
|
+
getAiDataDir: () => path10.join(os3.homedir(), ".chatlab", "ai"),
|
|
3139
|
+
getSettingsDir: () => path10.join(os3.homedir(), ".chatlab", "settings"),
|
|
3140
|
+
getCacheDir: () => path10.join(os3.homedir(), ".chatlab", "cache"),
|
|
3141
|
+
getTempDir: () => path10.join(os3.homedir(), ".chatlab", "temp"),
|
|
3142
|
+
getLogsDir: () => path10.join(os3.homedir(), ".chatlab", "logs"),
|
|
3143
|
+
getDownloadsDir: () => path10.join(os3.homedir(), "Downloads")
|
|
2951
3144
|
};
|
|
2952
3145
|
const resolvedPathProvider = options?.pathProvider ?? fallbackPathProvider;
|
|
2953
3146
|
const ai = options?.aiContext;
|
|
2954
3147
|
const cliStreamImport = async (dm, filePath) => {
|
|
2955
|
-
const { streamImport: streamImport2 } = await import("./stream-import-
|
|
3148
|
+
const { streamImport: streamImport2 } = await import("./stream-import-3JMXQD7P.mjs");
|
|
2956
3149
|
const result = await streamImport2(dm, filePath);
|
|
2957
3150
|
if (!result.sessionId) throw new Error("Import succeeded but no sessionId returned");
|
|
2958
3151
|
return { sessionId: result.sessionId };
|
|
2959
3152
|
};
|
|
2960
3153
|
const defaultUserDataDir = getDefaultNodeUserDataDir();
|
|
2961
|
-
const isCustom =
|
|
3154
|
+
const isCustom = path10.resolve(resolvedPathProvider.getUserDataDir()) !== path10.resolve(defaultUserDataDir);
|
|
2962
3155
|
const semanticIndexService = options?.semanticIndexService;
|
|
2963
3156
|
const analyticsService = "A-US-0629338532" ? new AnalyticsService(resolvedPathProvider.getSystemDir(), "A-US-0629338532", getVersion()) : void 0;
|
|
2964
3157
|
registerSharedRoutes(
|
|
@@ -2972,6 +3165,7 @@ function registerWebRoutes(server2, dbManager2, options) {
|
|
|
2972
3165
|
nativeBinding: options?.nativeBinding,
|
|
2973
3166
|
semanticIndexService,
|
|
2974
3167
|
analyticsService,
|
|
3168
|
+
getCurrentAiLogPath: () => getServerAiLogger()?.getExistingLogPath() ?? null,
|
|
2975
3169
|
openDirectory: openDirectoryPath,
|
|
2976
3170
|
showInFolder: showPathInFolder,
|
|
2977
3171
|
downloadsDir: resolvedPathProvider.getDownloadsDir(),
|
|
@@ -3054,31 +3248,31 @@ function registerProxyRoutes(server2) {
|
|
|
3054
3248
|
}
|
|
3055
3249
|
|
|
3056
3250
|
// src/ai/manager-factory.ts
|
|
3057
|
-
import * as
|
|
3058
|
-
import * as
|
|
3251
|
+
import * as fs8 from "fs";
|
|
3252
|
+
import * as path11 from "path";
|
|
3059
3253
|
import { createHash, randomUUID as randomUUID2 } from "crypto";
|
|
3060
3254
|
var nodeFs = {
|
|
3061
3255
|
ensureDir(dir) {
|
|
3062
|
-
if (!
|
|
3256
|
+
if (!fs8.existsSync(dir)) fs8.mkdirSync(dir, { recursive: true });
|
|
3063
3257
|
},
|
|
3064
3258
|
listFiles(dir, ext) {
|
|
3065
|
-
if (!
|
|
3066
|
-
return
|
|
3259
|
+
if (!fs8.existsSync(dir)) return [];
|
|
3260
|
+
return fs8.readdirSync(dir).filter((f) => f.endsWith(ext));
|
|
3067
3261
|
},
|
|
3068
3262
|
readFile(filePath) {
|
|
3069
|
-
return
|
|
3263
|
+
return fs8.readFileSync(filePath, "utf-8");
|
|
3070
3264
|
},
|
|
3071
3265
|
writeFile(filePath, content) {
|
|
3072
|
-
|
|
3266
|
+
fs8.writeFileSync(filePath, content, "utf-8");
|
|
3073
3267
|
},
|
|
3074
3268
|
deleteFile(filePath) {
|
|
3075
|
-
if (
|
|
3269
|
+
if (fs8.existsSync(filePath)) fs8.unlinkSync(filePath);
|
|
3076
3270
|
},
|
|
3077
3271
|
fileExists(filePath) {
|
|
3078
|
-
return
|
|
3272
|
+
return fs8.existsSync(filePath);
|
|
3079
3273
|
},
|
|
3080
3274
|
joinPath(...parts) {
|
|
3081
|
-
return
|
|
3275
|
+
return path11.join(...parts);
|
|
3082
3276
|
}
|
|
3083
3277
|
};
|
|
3084
3278
|
var managerCache = /* @__PURE__ */ new Map();
|
|
@@ -3088,13 +3282,13 @@ function getAssistantManager(aiDataDir) {
|
|
|
3088
3282
|
cached = {
|
|
3089
3283
|
assistant: new AssistantManager({
|
|
3090
3284
|
fs: nodeFs,
|
|
3091
|
-
assistantsDir:
|
|
3285
|
+
assistantsDir: path11.join(aiDataDir, "assistants"),
|
|
3092
3286
|
builtinRawConfigs: [],
|
|
3093
3287
|
generateId: () => `custom_${randomUUID2().replace(/-/g, "").slice(0, 12)}`
|
|
3094
3288
|
}),
|
|
3095
3289
|
skill: new SkillManagerCore({
|
|
3096
3290
|
fs: nodeFs,
|
|
3097
|
-
skillsDir:
|
|
3291
|
+
skillsDir: path11.join(aiDataDir, "skills"),
|
|
3098
3292
|
builtinRawSkills: [],
|
|
3099
3293
|
contentHash: (content) => createHash("md5").update(content).digest("hex")
|
|
3100
3294
|
})
|
|
@@ -3118,14 +3312,14 @@ var noop = () => {
|
|
|
3118
3312
|
var NOOP_LOGGER = { info: noop, warn: noop, error: noop };
|
|
3119
3313
|
|
|
3120
3314
|
// ../../packages/sync/src/data-source-manager.ts
|
|
3121
|
-
import * as
|
|
3122
|
-
import * as
|
|
3315
|
+
import * as fs9 from "fs";
|
|
3316
|
+
import * as path12 from "path";
|
|
3123
3317
|
import * as crypto from "crypto";
|
|
3124
3318
|
var CONFIG_FILE = "data-sources.json";
|
|
3125
3319
|
function generateId(prefix = "ds") {
|
|
3126
3320
|
return `${prefix}_${crypto.randomBytes(6).toString("hex")}`;
|
|
3127
3321
|
}
|
|
3128
|
-
function
|
|
3322
|
+
function normalizeBaseUrl2(input) {
|
|
3129
3323
|
let url = input.trim().replace(/\/+$/, "");
|
|
3130
3324
|
if (url && !/^https?:\/\//i.test(url)) {
|
|
3131
3325
|
url = `http://${url}`;
|
|
@@ -3147,17 +3341,17 @@ var DataSourceManager = class {
|
|
|
3147
3341
|
this.logger = logger ?? NOOP_LOGGER;
|
|
3148
3342
|
}
|
|
3149
3343
|
getConfigPath() {
|
|
3150
|
-
return
|
|
3344
|
+
return path12.join(this.configDir, CONFIG_FILE);
|
|
3151
3345
|
}
|
|
3152
3346
|
ensureDir() {
|
|
3153
|
-
|
|
3347
|
+
fs9.mkdirSync(this.configDir, { recursive: true });
|
|
3154
3348
|
}
|
|
3155
3349
|
// ==================== Load / Save ====================
|
|
3156
3350
|
loadAll() {
|
|
3157
3351
|
try {
|
|
3158
3352
|
const filePath = this.getConfigPath();
|
|
3159
|
-
if (
|
|
3160
|
-
const raw =
|
|
3353
|
+
if (fs9.existsSync(filePath)) {
|
|
3354
|
+
const raw = fs9.readFileSync(filePath, "utf-8");
|
|
3161
3355
|
const parsed = JSON.parse(raw);
|
|
3162
3356
|
if (!isValidDataSourceArray(parsed)) {
|
|
3163
3357
|
this.logger.warn("[DataSource] Incompatible config format detected, returning empty.");
|
|
@@ -3176,7 +3370,7 @@ var DataSourceManager = class {
|
|
|
3176
3370
|
saveAll(sources) {
|
|
3177
3371
|
try {
|
|
3178
3372
|
this.ensureDir();
|
|
3179
|
-
|
|
3373
|
+
fs9.writeFileSync(this.getConfigPath(), JSON.stringify(sources, null, 2), "utf-8");
|
|
3180
3374
|
} catch (err) {
|
|
3181
3375
|
this.logger.error("[DataSource] Failed to save config", err);
|
|
3182
3376
|
}
|
|
@@ -3190,7 +3384,7 @@ var DataSourceManager = class {
|
|
|
3190
3384
|
const ds = {
|
|
3191
3385
|
id: generateId("src"),
|
|
3192
3386
|
name: partial.name || "",
|
|
3193
|
-
baseUrl:
|
|
3387
|
+
baseUrl: normalizeBaseUrl2(partial.baseUrl),
|
|
3194
3388
|
token: partial.token,
|
|
3195
3389
|
intervalMinutes: partial.intervalMinutes,
|
|
3196
3390
|
pullLimit: partial.pullLimit || 1e3,
|
|
@@ -3208,7 +3402,7 @@ var DataSourceManager = class {
|
|
|
3208
3402
|
if (idx === -1) return null;
|
|
3209
3403
|
const ds = sources[idx];
|
|
3210
3404
|
if (updates.name !== void 0) ds.name = updates.name;
|
|
3211
|
-
if (updates.baseUrl !== void 0) ds.baseUrl =
|
|
3405
|
+
if (updates.baseUrl !== void 0) ds.baseUrl = normalizeBaseUrl2(updates.baseUrl);
|
|
3212
3406
|
if (updates.token !== void 0) ds.token = updates.token;
|
|
3213
3407
|
if (updates.intervalMinutes !== void 0) ds.intervalMinutes = updates.intervalMinutes;
|
|
3214
3408
|
if (updates.pullLimit !== void 0) ds.pullLimit = updates.pullLimit;
|
|
@@ -3269,42 +3463,13 @@ var DataSourceManager = class {
|
|
|
3269
3463
|
}
|
|
3270
3464
|
};
|
|
3271
3465
|
|
|
3272
|
-
// ../../packages/sync/src/discovery.ts
|
|
3273
|
-
function buildRemoteSessionsUrl(baseUrl, query = {}) {
|
|
3274
|
-
const searchParams = new URLSearchParams();
|
|
3275
|
-
searchParams.set("format", "chatlab");
|
|
3276
|
-
if (query.keyword?.trim()) searchParams.set("keyword", query.keyword.trim());
|
|
3277
|
-
if (query.limit && query.limit > 0) searchParams.set("limit", String(query.limit));
|
|
3278
|
-
if (query.cursor) searchParams.set("cursor", query.cursor);
|
|
3279
|
-
return `${baseUrl}/sessions?${searchParams.toString()}`;
|
|
3280
|
-
}
|
|
3281
|
-
function parseRemoteSessionsResponse(body) {
|
|
3282
|
-
const parsed = JSON.parse(body);
|
|
3283
|
-
let sessions;
|
|
3284
|
-
let pageSource;
|
|
3285
|
-
if (Array.isArray(parsed)) {
|
|
3286
|
-
sessions = parsed;
|
|
3287
|
-
} else if (parsed && typeof parsed === "object") {
|
|
3288
|
-
sessions = parsed.sessions ?? parsed.data?.sessions ?? parsed.data ?? [];
|
|
3289
|
-
if (!Array.isArray(sessions)) sessions = [];
|
|
3290
|
-
pageSource = parsed.page ?? parsed.data?.page;
|
|
3291
|
-
} else {
|
|
3292
|
-
sessions = [];
|
|
3293
|
-
}
|
|
3294
|
-
return {
|
|
3295
|
-
sessions,
|
|
3296
|
-
page: pageSource && typeof pageSource === "object" ? {
|
|
3297
|
-
hasMore: Boolean(pageSource.hasMore),
|
|
3298
|
-
nextCursor: typeof pageSource.nextCursor === "string" ? pageSource.nextCursor : void 0
|
|
3299
|
-
} : void 0
|
|
3300
|
-
};
|
|
3301
|
-
}
|
|
3302
|
-
|
|
3303
3466
|
// ../../packages/sync/src/pull-engine.ts
|
|
3304
|
-
import * as
|
|
3467
|
+
import * as fs10 from "fs";
|
|
3305
3468
|
import * as crypto2 from "crypto";
|
|
3306
3469
|
var MAX_PAGES_PER_PULL = 5e3;
|
|
3307
3470
|
var PULL_OVERLAP_SECONDS = 60;
|
|
3471
|
+
var SOURCE_UNAVAILABLE_LOG_COOLDOWN_MS = 5 * 60 * 1e3;
|
|
3472
|
+
var SOURCE_UNAVAILABLE_ERROR_CODES = /* @__PURE__ */ new Set(["ECONNREFUSED", "ENOTFOUND", "ECONNRESET", "ETIMEDOUT", "ABORT_ERR"]);
|
|
3308
3473
|
function buildPullUrl(baseUrl, remoteSessionId, params) {
|
|
3309
3474
|
const base = `${baseUrl}/sessions/${remoteSessionId}/messages`;
|
|
3310
3475
|
const qs = ["format=chatlab"];
|
|
@@ -3321,7 +3486,7 @@ function parseSyncFromFile(filePath) {
|
|
|
3321
3486
|
try {
|
|
3322
3487
|
const isJsonl = filePath.endsWith(".jsonl");
|
|
3323
3488
|
if (isJsonl) {
|
|
3324
|
-
const content =
|
|
3489
|
+
const content = fs10.readFileSync(filePath, "utf-8");
|
|
3325
3490
|
const lines = content.trimEnd().split("\n");
|
|
3326
3491
|
for (let i = lines.length - 1; i >= Math.max(0, lines.length - 5); i--) {
|
|
3327
3492
|
try {
|
|
@@ -3340,7 +3505,7 @@ function parseSyncFromFile(filePath) {
|
|
|
3340
3505
|
}
|
|
3341
3506
|
return null;
|
|
3342
3507
|
}
|
|
3343
|
-
const raw =
|
|
3508
|
+
const raw = fs10.readFileSync(filePath, "utf-8");
|
|
3344
3509
|
const parsed = JSON.parse(raw);
|
|
3345
3510
|
if (parsed.sync && typeof parsed.sync === "object") {
|
|
3346
3511
|
const s = parsed.sync;
|
|
@@ -3354,7 +3519,7 @@ function parseSyncFromFile(filePath) {
|
|
|
3354
3519
|
function fileContainsMessages(filePath) {
|
|
3355
3520
|
try {
|
|
3356
3521
|
if (filePath.endsWith(".jsonl")) {
|
|
3357
|
-
const content =
|
|
3522
|
+
const content = fs10.readFileSync(filePath, "utf-8");
|
|
3358
3523
|
for (const line of content.split("\n")) {
|
|
3359
3524
|
const trimmed = line.trim();
|
|
3360
3525
|
if (!trimmed) continue;
|
|
@@ -3367,7 +3532,7 @@ function fileContainsMessages(filePath) {
|
|
|
3367
3532
|
}
|
|
3368
3533
|
return false;
|
|
3369
3534
|
}
|
|
3370
|
-
const raw =
|
|
3535
|
+
const raw = fs10.readFileSync(filePath, "utf-8");
|
|
3371
3536
|
const parsed = JSON.parse(raw);
|
|
3372
3537
|
return Array.isArray(parsed.messages) && parsed.messages.length > 0;
|
|
3373
3538
|
} catch {
|
|
@@ -3384,7 +3549,7 @@ function getMaxMessageTimestampFromFile(filePath) {
|
|
|
3384
3549
|
}
|
|
3385
3550
|
};
|
|
3386
3551
|
if (filePath.endsWith(".jsonl")) {
|
|
3387
|
-
const content =
|
|
3552
|
+
const content = fs10.readFileSync(filePath, "utf-8");
|
|
3388
3553
|
for (const line of content.split("\n")) {
|
|
3389
3554
|
const trimmed = line.trim();
|
|
3390
3555
|
if (!trimmed) continue;
|
|
@@ -3397,7 +3562,7 @@ function getMaxMessageTimestampFromFile(filePath) {
|
|
|
3397
3562
|
}
|
|
3398
3563
|
return maxTs;
|
|
3399
3564
|
}
|
|
3400
|
-
const raw =
|
|
3565
|
+
const raw = fs10.readFileSync(filePath, "utf-8");
|
|
3401
3566
|
const parsed = JSON.parse(raw);
|
|
3402
3567
|
if (Array.isArray(parsed.messages)) {
|
|
3403
3568
|
for (const message of parsed.messages) {
|
|
@@ -3411,10 +3576,35 @@ function getMaxMessageTimestampFromFile(filePath) {
|
|
|
3411
3576
|
}
|
|
3412
3577
|
function cleanupTempFile(filePath) {
|
|
3413
3578
|
try {
|
|
3414
|
-
if (
|
|
3579
|
+
if (fs10.existsSync(filePath)) fs10.unlinkSync(filePath);
|
|
3415
3580
|
} catch {
|
|
3416
3581
|
}
|
|
3417
3582
|
}
|
|
3583
|
+
function getErrorProp(error, prop) {
|
|
3584
|
+
return error && typeof error === "object" ? error[prop] : void 0;
|
|
3585
|
+
}
|
|
3586
|
+
function getErrorMessage(error) {
|
|
3587
|
+
return error instanceof Error ? error.message : String(error || "Pull failed");
|
|
3588
|
+
}
|
|
3589
|
+
function describeSourceUnavailableError(error) {
|
|
3590
|
+
let current = error;
|
|
3591
|
+
while (current && typeof current === "object") {
|
|
3592
|
+
const code = getErrorProp(current, "code");
|
|
3593
|
+
const name = getErrorProp(current, "name");
|
|
3594
|
+
if (typeof code === "string" && SOURCE_UNAVAILABLE_ERROR_CODES.has(code)) {
|
|
3595
|
+
const address = getErrorProp(current, "address");
|
|
3596
|
+
const port = getErrorProp(current, "port");
|
|
3597
|
+
if (typeof address === "string" && (typeof port === "number" || typeof port === "string")) {
|
|
3598
|
+
return `${code} ${address}:${port}`;
|
|
3599
|
+
}
|
|
3600
|
+
const host = getErrorProp(current, "hostname") ?? getErrorProp(current, "host");
|
|
3601
|
+
return typeof host === "string" ? `${code} ${host}` : code;
|
|
3602
|
+
}
|
|
3603
|
+
if (name === "AbortError") return "Request timeout";
|
|
3604
|
+
current = getErrorProp(current, "cause");
|
|
3605
|
+
}
|
|
3606
|
+
return null;
|
|
3607
|
+
}
|
|
3418
3608
|
var PullEngine = class {
|
|
3419
3609
|
fetcher;
|
|
3420
3610
|
importer;
|
|
@@ -3425,6 +3615,7 @@ var PullEngine = class {
|
|
|
3425
3615
|
onSessionImported;
|
|
3426
3616
|
pullingSourceIds = /* @__PURE__ */ new Set();
|
|
3427
3617
|
progressMap = /* @__PURE__ */ new Map();
|
|
3618
|
+
sourceUnavailableLogs = /* @__PURE__ */ new Map();
|
|
3428
3619
|
constructor(options) {
|
|
3429
3620
|
this.fetcher = options.fetcher;
|
|
3430
3621
|
this.importer = options.importer;
|
|
@@ -3460,7 +3651,6 @@ var PullEngine = class {
|
|
|
3460
3651
|
this.logger.info(`[Pull] Skipping "${sess.name}": import in progress`);
|
|
3461
3652
|
return { success: false, newMessageCount: 0, error: "Import in progress" };
|
|
3462
3653
|
}
|
|
3463
|
-
this.logger.info(`[Pull] Pulling "${sess.name}" from ${ds.baseUrl}`);
|
|
3464
3654
|
let totalNewMessages = 0;
|
|
3465
3655
|
let since = sess.lastPullAt;
|
|
3466
3656
|
let offset;
|
|
@@ -3477,7 +3667,7 @@ var PullEngine = class {
|
|
|
3477
3667
|
limit: ds.pullLimit
|
|
3478
3668
|
});
|
|
3479
3669
|
try {
|
|
3480
|
-
const stat2 =
|
|
3670
|
+
const stat2 = fs10.statSync(tempFile);
|
|
3481
3671
|
this.logger.info(`[Pull] "${sess.name}" page ${pageCount}: fetched ${stat2.size} bytes`);
|
|
3482
3672
|
const sync0 = parseSyncFromFile(tempFile);
|
|
3483
3673
|
if (stat2.size < 1024) {
|
|
@@ -3497,7 +3687,7 @@ var PullEngine = class {
|
|
|
3497
3687
|
offset,
|
|
3498
3688
|
limit: ds.pullLimit
|
|
3499
3689
|
});
|
|
3500
|
-
const retryStat =
|
|
3690
|
+
const retryStat = fs10.statSync(retryFile);
|
|
3501
3691
|
this.logger.info(`[Pull] "${sess.name}" retry ${ri + 1}: fetched ${retryStat.size} bytes`);
|
|
3502
3692
|
const retrySync = parseSyncFromFile(retryFile);
|
|
3503
3693
|
if (retryStat.size < 1024 && !fileContainsMessages(retryFile) && retrySync?.hasMore === false) {
|
|
@@ -3668,8 +3858,11 @@ var PullEngine = class {
|
|
|
3668
3858
|
}
|
|
3669
3859
|
return { success: true, newMessageCount: totalNewMessages };
|
|
3670
3860
|
} catch (error) {
|
|
3671
|
-
const
|
|
3672
|
-
|
|
3861
|
+
const sourceUnavailable = describeSourceUnavailableError(error);
|
|
3862
|
+
const errMsg = sourceUnavailable ?? getErrorMessage(error);
|
|
3863
|
+
if (!sourceUnavailable) {
|
|
3864
|
+
this.logger.error(`[Pull] Pull failed for "${sess.name}": ${errMsg}`);
|
|
3865
|
+
}
|
|
3673
3866
|
this.dsManager.updateSession(sourceId, sess.id, {
|
|
3674
3867
|
lastPullAt: Math.floor(Date.now() / 1e3),
|
|
3675
3868
|
lastStatus: "error",
|
|
@@ -3677,7 +3870,7 @@ var PullEngine = class {
|
|
|
3677
3870
|
});
|
|
3678
3871
|
this.notifier.onPullResult(sourceId, sess.id, "error", errMsg);
|
|
3679
3872
|
this.markProgressDone(sess.id);
|
|
3680
|
-
return { success: false, newMessageCount: 0, error: errMsg };
|
|
3873
|
+
return { success: false, newMessageCount: 0, error: errMsg, sourceUnavailable: !!sourceUnavailable };
|
|
3681
3874
|
}
|
|
3682
3875
|
}
|
|
3683
3876
|
markProgressDone(sessionId) {
|
|
@@ -3694,13 +3887,43 @@ var PullEngine = class {
|
|
|
3694
3887
|
}
|
|
3695
3888
|
this.pullingSourceIds.add(ds.id);
|
|
3696
3889
|
try {
|
|
3697
|
-
|
|
3698
|
-
|
|
3890
|
+
let sourceUnavailable = false;
|
|
3891
|
+
for (let index = 0; index < ds.sessions.length; index++) {
|
|
3892
|
+
const sess = ds.sessions[index];
|
|
3893
|
+
const result = await this.executePullSession(ds.id, ds, sess);
|
|
3894
|
+
if (result.sourceUnavailable && result.error) {
|
|
3895
|
+
sourceUnavailable = true;
|
|
3896
|
+
this.markRemainingSessionsSourceUnavailable(ds, index + 1, result.error);
|
|
3897
|
+
this.logSourceUnavailable(ds, result.error, ds.sessions.length - index);
|
|
3898
|
+
break;
|
|
3899
|
+
}
|
|
3699
3900
|
}
|
|
3901
|
+
if (!sourceUnavailable) this.sourceUnavailableLogs.delete(ds.id);
|
|
3700
3902
|
} finally {
|
|
3701
3903
|
this.pullingSourceIds.delete(ds.id);
|
|
3702
3904
|
}
|
|
3703
3905
|
}
|
|
3906
|
+
markRemainingSessionsSourceUnavailable(ds, startIndex, detail) {
|
|
3907
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
3908
|
+
for (const sess of ds.sessions.slice(startIndex)) {
|
|
3909
|
+
this.dsManager.updateSession(ds.id, sess.id, {
|
|
3910
|
+
lastPullAt: now,
|
|
3911
|
+
lastStatus: "error",
|
|
3912
|
+
lastError: detail
|
|
3913
|
+
});
|
|
3914
|
+
this.notifier.onPullResult(ds.id, sess.id, "error", detail);
|
|
3915
|
+
this.markProgressDone(sess.id);
|
|
3916
|
+
}
|
|
3917
|
+
}
|
|
3918
|
+
logSourceUnavailable(ds, detail, affectedSessions) {
|
|
3919
|
+
const now = Date.now();
|
|
3920
|
+
const previous = this.sourceUnavailableLogs.get(ds.id);
|
|
3921
|
+
if (previous && previous.detail === detail && now - previous.lastLoggedAt < SOURCE_UNAVAILABLE_LOG_COOLDOWN_MS) {
|
|
3922
|
+
return;
|
|
3923
|
+
}
|
|
3924
|
+
this.sourceUnavailableLogs.set(ds.id, { detail, lastLoggedAt: now });
|
|
3925
|
+
this.logger.warn(`[Pull] Source ${ds.baseUrl} unavailable: ${detail}, skipped ${affectedSessions} sessions`);
|
|
3926
|
+
}
|
|
3704
3927
|
async triggerPull(sourceId, sessionId) {
|
|
3705
3928
|
const ds = this.dsManager.get(sourceId);
|
|
3706
3929
|
if (!ds) return { success: false, error: "Data source not found" };
|
|
@@ -3708,6 +3931,8 @@ var PullEngine = class {
|
|
|
3708
3931
|
const sess = ds.sessions.find((s) => s.id === sessionId);
|
|
3709
3932
|
if (!sess) return { success: false, error: "Session not found" };
|
|
3710
3933
|
const result = await this.executePullSession(sourceId, ds, sess);
|
|
3934
|
+
if (result.sourceUnavailable && result.error) this.logSourceUnavailable(ds, result.error, 1);
|
|
3935
|
+
if (result.success) this.sourceUnavailableLogs.delete(ds.id);
|
|
3711
3936
|
return { success: result.success, error: result.error };
|
|
3712
3937
|
}
|
|
3713
3938
|
if (this.pullingSourceIds.has(sourceId)) {
|
|
@@ -3717,10 +3942,19 @@ var PullEngine = class {
|
|
|
3717
3942
|
this.pullingSourceIds.add(sourceId);
|
|
3718
3943
|
try {
|
|
3719
3944
|
const errors = [];
|
|
3720
|
-
|
|
3945
|
+
let sourceUnavailable = false;
|
|
3946
|
+
for (let index = 0; index < ds.sessions.length; index++) {
|
|
3947
|
+
const sess = ds.sessions[index];
|
|
3721
3948
|
const result = await this.executePullSession(sourceId, ds, sess);
|
|
3722
3949
|
if (!result.success && result.error) errors.push(`${sess.name}: ${result.error}`);
|
|
3950
|
+
if (result.sourceUnavailable && result.error) {
|
|
3951
|
+
sourceUnavailable = true;
|
|
3952
|
+
this.markRemainingSessionsSourceUnavailable(ds, index + 1, result.error);
|
|
3953
|
+
this.logSourceUnavailable(ds, result.error, ds.sessions.length - index);
|
|
3954
|
+
break;
|
|
3955
|
+
}
|
|
3723
3956
|
}
|
|
3957
|
+
if (!sourceUnavailable) this.sourceUnavailableLogs.delete(ds.id);
|
|
3724
3958
|
if (errors.length > 0) {
|
|
3725
3959
|
return { success: false, error: errors.join("; ") };
|
|
3726
3960
|
}
|
|
@@ -3801,13 +4035,13 @@ function reloadTimer(dsId, skipInitialPull = false) {
|
|
|
3801
4035
|
}
|
|
3802
4036
|
|
|
3803
4037
|
// src/sync/adapters.ts
|
|
3804
|
-
import * as
|
|
4038
|
+
import * as fs11 from "fs";
|
|
3805
4039
|
import * as os4 from "os";
|
|
3806
|
-
import * as
|
|
4040
|
+
import * as path13 from "path";
|
|
3807
4041
|
import * as crypto3 from "crypto";
|
|
3808
4042
|
function getTempFilePath(ext) {
|
|
3809
4043
|
const id = crypto3.randomBytes(8).toString("hex");
|
|
3810
|
-
return
|
|
4044
|
+
return path13.join(os4.tmpdir(), `chatlab-pull-${id}${ext}`);
|
|
3811
4045
|
}
|
|
3812
4046
|
var NodeFetcher = class {
|
|
3813
4047
|
async fetchToTempFile(baseUrl, remoteSessionId, token, params) {
|
|
@@ -3824,7 +4058,7 @@ var NodeFetcher = class {
|
|
|
3824
4058
|
const isJsonl = contentType.includes("ndjson") || contentType.includes("jsonl");
|
|
3825
4059
|
const tempFile = getTempFilePath(isJsonl ? ".jsonl" : ".json");
|
|
3826
4060
|
const buffer = Buffer.from(await response.arrayBuffer());
|
|
3827
|
-
|
|
4061
|
+
fs11.writeFileSync(tempFile, buffer);
|
|
3828
4062
|
return tempFile;
|
|
3829
4063
|
}
|
|
3830
4064
|
};
|
|
@@ -3837,7 +4071,7 @@ var DirectImporter = class {
|
|
|
3837
4071
|
}
|
|
3838
4072
|
sessionExists(sessionId) {
|
|
3839
4073
|
const dbPath = this.dbManager.getDbPath(sessionId);
|
|
3840
|
-
if (!
|
|
4074
|
+
if (!fs11.existsSync(dbPath)) return false;
|
|
3841
4075
|
try {
|
|
3842
4076
|
const db = this.dbManager.openRawSessionDatabase(sessionId, { readonly: true });
|
|
3843
4077
|
const row = db.prepare("SELECT COUNT(*) as cnt FROM sqlite_master WHERE type='table' AND name='message'").get();
|
|
@@ -3845,7 +4079,7 @@ var DirectImporter = class {
|
|
|
3845
4079
|
if (row.cnt === 0) {
|
|
3846
4080
|
this.logger.warn(`[DirectImporter] DB file exists but has no message table: ${sessionId}, removing`);
|
|
3847
4081
|
try {
|
|
3848
|
-
|
|
4082
|
+
fs11.unlinkSync(dbPath);
|
|
3849
4083
|
} catch {
|
|
3850
4084
|
}
|
|
3851
4085
|
return false;
|
|
@@ -3855,7 +4089,7 @@ var DirectImporter = class {
|
|
|
3855
4089
|
if (error instanceof DataDirCompatibilityError) throw error;
|
|
3856
4090
|
this.logger.warn(`[DirectImporter] Cannot validate DB file: ${sessionId}, removing`);
|
|
3857
4091
|
try {
|
|
3858
|
-
|
|
4092
|
+
fs11.unlinkSync(dbPath);
|
|
3859
4093
|
} catch {
|
|
3860
4094
|
}
|
|
3861
4095
|
return false;
|
|
@@ -3914,92 +4148,6 @@ var NoopNotifier = class {
|
|
|
3914
4148
|
onPullResult = noop2;
|
|
3915
4149
|
};
|
|
3916
4150
|
|
|
3917
|
-
// src/sync/routes.ts
|
|
3918
|
-
function registerAutomationRoutes(server2, ctx) {
|
|
3919
|
-
const { dsManager: dsManager2, pullEngine: pullEngine2, dbManager: dbManager2, serverInfo } = ctx;
|
|
3920
|
-
server2.get("/_web/automation/config", async () => {
|
|
3921
|
-
return {
|
|
3922
|
-
enabled: true,
|
|
3923
|
-
port: serverInfo.port,
|
|
3924
|
-
token: serverInfo.token,
|
|
3925
|
-
host: serverInfo.host
|
|
3926
|
-
};
|
|
3927
|
-
});
|
|
3928
|
-
server2.get("/_web/automation/data-sources", async () => {
|
|
3929
|
-
return dsManager2.loadAll();
|
|
3930
|
-
});
|
|
3931
|
-
server2.post("/_web/automation/data-sources", async (request) => {
|
|
3932
|
-
const ds = dsManager2.add(request.body);
|
|
3933
|
-
reloadTimer(ds.id);
|
|
3934
|
-
return ds;
|
|
3935
|
-
});
|
|
3936
|
-
server2.patch("/_web/automation/data-sources/:id", async (request, reply) => {
|
|
3937
|
-
const ds = dsManager2.update(request.params.id, request.body);
|
|
3938
|
-
if (!ds) return reply.code(404).send({ error: "Data source not found" });
|
|
3939
|
-
reloadTimer(ds.id);
|
|
3940
|
-
return ds;
|
|
3941
|
-
});
|
|
3942
|
-
server2.delete("/_web/automation/data-sources/:id", async (request, reply) => {
|
|
3943
|
-
stopTimer(request.params.id);
|
|
3944
|
-
const ok = dsManager2.delete(request.params.id);
|
|
3945
|
-
if (!ok) return reply.code(404).send({ error: "Data source not found" });
|
|
3946
|
-
return { success: true };
|
|
3947
|
-
});
|
|
3948
|
-
server2.post("/_web/automation/data-sources/:id/sessions", async (request, reply) => {
|
|
3949
|
-
const added = dsManager2.addSessions(request.params.id, request.body.sessions);
|
|
3950
|
-
if (added.length === 0 && !dsManager2.get(request.params.id)) {
|
|
3951
|
-
return reply.code(404).send({ error: "Data source not found" });
|
|
3952
|
-
}
|
|
3953
|
-
reloadTimer(request.params.id, true);
|
|
3954
|
-
for (const sess of added) {
|
|
3955
|
-
pullEngine2.triggerPull(request.params.id, sess.id).catch(() => {
|
|
3956
|
-
});
|
|
3957
|
-
}
|
|
3958
|
-
return added;
|
|
3959
|
-
});
|
|
3960
|
-
server2.delete("/_web/automation/data-sources/:id/sessions/:sessId", async (request, reply) => {
|
|
3961
|
-
const removed = dsManager2.removeSession(request.params.id, request.params.sessId);
|
|
3962
|
-
if (!removed) return reply.code(404).send({ error: "Session not found" });
|
|
3963
|
-
reloadTimer(request.params.id);
|
|
3964
|
-
if (request.query.deleteData === "true" && removed.targetSessionId) {
|
|
3965
|
-
dbManager2.deleteSessionDatabaseFiles(removed.targetSessionId);
|
|
3966
|
-
}
|
|
3967
|
-
return { success: true };
|
|
3968
|
-
});
|
|
3969
|
-
server2.post(
|
|
3970
|
-
"/_web/automation/data-sources/:id/pull",
|
|
3971
|
-
async (request) => {
|
|
3972
|
-
return pullEngine2.triggerPull(request.params.id, request.body?.sessionId);
|
|
3973
|
-
}
|
|
3974
|
-
);
|
|
3975
|
-
server2.post("/_web/automation/data-sources/:id/pull-all", async (request) => {
|
|
3976
|
-
return pullEngine2.triggerPullAll(request.params.id);
|
|
3977
|
-
});
|
|
3978
|
-
server2.get("/_web/automation/sync-progress", async () => {
|
|
3979
|
-
return pullEngine2.getProgress();
|
|
3980
|
-
});
|
|
3981
|
-
server2.get("/_web/automation/remote-sessions", async (request, reply) => {
|
|
3982
|
-
const { baseUrl, token, keyword, limit, cursor } = request.query;
|
|
3983
|
-
if (!baseUrl) return reply.code(400).send({ error: "baseUrl is required" });
|
|
3984
|
-
const normalizedUrl = normalizeBaseUrl(baseUrl);
|
|
3985
|
-
const url = buildRemoteSessionsUrl(normalizedUrl, {
|
|
3986
|
-
keyword,
|
|
3987
|
-
limit: limit ? parseInt(limit, 10) : void 0,
|
|
3988
|
-
cursor
|
|
3989
|
-
});
|
|
3990
|
-
const headers = { Accept: "application/json" };
|
|
3991
|
-
if (token) headers["Authorization"] = `Bearer ${token}`;
|
|
3992
|
-
try {
|
|
3993
|
-
const resp = await fetch(url, { headers, signal: AbortSignal.timeout(3e4) });
|
|
3994
|
-
if (!resp.ok) throw new Error(`Remote server returned HTTP ${resp.status}`);
|
|
3995
|
-
const body = await resp.text();
|
|
3996
|
-
return parseRemoteSessionsResponse(body);
|
|
3997
|
-
} catch (err) {
|
|
3998
|
-
return reply.code(502).send({ error: err.message || "Failed to fetch remote sessions" });
|
|
3999
|
-
}
|
|
4000
|
-
});
|
|
4001
|
-
}
|
|
4002
|
-
|
|
4003
4151
|
// src/sync/index.ts
|
|
4004
4152
|
var syncLogger = {
|
|
4005
4153
|
info: (msg) => console.log(`[Sync] ${msg}`),
|
|
@@ -4030,7 +4178,16 @@ function initSync(server2, dbManager2, pathProvider, serverInfo) {
|
|
|
4030
4178
|
}
|
|
4031
4179
|
}
|
|
4032
4180
|
});
|
|
4033
|
-
registerAutomationRoutes(server2, {
|
|
4181
|
+
registerAutomationRoutes(server2, {
|
|
4182
|
+
automation: {
|
|
4183
|
+
dsManager,
|
|
4184
|
+
pullEngine,
|
|
4185
|
+
serverInfo,
|
|
4186
|
+
deleteSessionData: (sessionId) => dbManager2.deleteSessionDatabaseFiles(sessionId),
|
|
4187
|
+
reloadTimer,
|
|
4188
|
+
stopTimer
|
|
4189
|
+
}
|
|
4190
|
+
});
|
|
4034
4191
|
initScheduler({
|
|
4035
4192
|
dsManager,
|
|
4036
4193
|
pullEngine,
|
|
@@ -4051,21 +4208,21 @@ function createFileConfigStorage(aiDataDir) {
|
|
|
4051
4208
|
return {
|
|
4052
4209
|
readJson(key) {
|
|
4053
4210
|
try {
|
|
4054
|
-
return JSON.parse(
|
|
4211
|
+
return JSON.parse(fs12.readFileSync(`${aiDataDir}/${key}.json`, "utf-8"));
|
|
4055
4212
|
} catch {
|
|
4056
4213
|
return null;
|
|
4057
4214
|
}
|
|
4058
4215
|
},
|
|
4059
4216
|
writeJson(key, data) {
|
|
4060
|
-
if (!
|
|
4061
|
-
|
|
4217
|
+
if (!fs12.existsSync(aiDataDir)) fs12.mkdirSync(aiDataDir, { recursive: true });
|
|
4218
|
+
fs12.writeFileSync(`${aiDataDir}/${key}.json`, JSON.stringify(data, null, 2), "utf-8");
|
|
4062
4219
|
}
|
|
4063
4220
|
};
|
|
4064
4221
|
}
|
|
4065
4222
|
function resolveNativeBinding2() {
|
|
4066
4223
|
if (process.versions.electron) return void 0;
|
|
4067
4224
|
const nativePath = resolveCliPath("native/better_sqlite3.node");
|
|
4068
|
-
if (
|
|
4225
|
+
if (fs12.existsSync(nativePath)) return nativePath;
|
|
4069
4226
|
return void 0;
|
|
4070
4227
|
}
|
|
4071
4228
|
function ensureToken(config) {
|
|
@@ -4172,7 +4329,7 @@ async function startHttpServer(options) {
|
|
|
4172
4329
|
}
|
|
4173
4330
|
});
|
|
4174
4331
|
initSync(server, dbManager, pathProvider, { port, host, token });
|
|
4175
|
-
if (options?.webRoot &&
|
|
4332
|
+
if (options?.webRoot && fs12.existsSync(options.webRoot)) {
|
|
4176
4333
|
registerProxyRoutes(server);
|
|
4177
4334
|
const fastifyStatic = await import("@fastify/static");
|
|
4178
4335
|
await server.register(fastifyStatic.default, {
|
|
@@ -4213,4 +4370,4 @@ export {
|
|
|
4213
4370
|
startHttpServer,
|
|
4214
4371
|
stopHttpServer
|
|
4215
4372
|
};
|
|
4216
|
-
//# sourceMappingURL=chunk-
|
|
4373
|
+
//# sourceMappingURL=chunk-4IZ3WKYU.mjs.map
|