chatlab-cli 0.29.0 → 0.30.1-beta.1
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-VBDMWOKL.mjs} +8 -8
- package/dist/{chunk-6FPEP2ZX.mjs → chunk-326OCJOT.mjs} +2 -2
- package/dist/{chunk-QQK7MLCR.mjs → chunk-36JGKW6K.mjs} +710 -282
- package/dist/chunk-36JGKW6K.mjs.map +1 -0
- package/dist/{chunk-SKUG5XVD.mjs → chunk-5EE2EFPE.mjs} +5 -4
- package/dist/{chunk-KL2NGIMQ.mjs → chunk-D3DPCDII.mjs} +444 -274
- package/dist/chunk-D3DPCDII.mjs.map +1 -0
- package/dist/{chunk-I35WHWA6.mjs → chunk-KBSAVUFV.mjs} +7 -4
- package/dist/chunk-KBSAVUFV.mjs.map +1 -0
- package/dist/{chunk-WPI6YSAR.mjs → chunk-LVEJHB3X.mjs} +4570 -4412
- package/dist/chunk-LVEJHB3X.mjs.map +1 -0
- package/dist/chunk-M2PXDX4G.mjs +2325 -0
- package/dist/chunk-M2PXDX4G.mjs.map +1 -0
- package/dist/{chunk-CIB23U32.mjs → chunk-TCZHG3G7.mjs} +101 -41
- package/dist/chunk-TCZHG3G7.mjs.map +1 -0
- package/dist/{chunk-D4WTE7LV.mjs → chunk-TEX44HNL.mjs} +16 -12
- package/dist/chunk-TEX44HNL.mjs.map +1 -0
- package/dist/{chunk-7RW4CQAO.mjs → chunk-ULNRUS55.mjs} +5 -3
- package/dist/chunk-ULNRUS55.mjs.map +1 -0
- package/dist/{chunk-2MZ6G4AC.mjs → chunk-WV5GSRQM.mjs} +2 -2
- package/dist/cli.mjs +7 -7
- package/dist/contacts-worker.mjs +2 -2
- package/dist/{http-5E7ARYFW.mjs → http-L4AMELPZ.mjs} +11 -11
- package/dist/{import-3KG6VK6O.mjs → import-WWCSXGVS.mjs} +7 -7
- package/dist/index.mjs +11 -11
- package/dist/{mcp-JBI4CUQT.mjs → mcp-A4M3LT5O.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-MPNBTFOE.mjs} +14 -6
- package/dist/{stream-import-7HZ4IRYJ.mjs → stream-import-HIE7PNL4.mjs} +7 -7
- package/dist/{worker-runtime-ALS4MR2A.mjs → worker-runtime-BJXO24XX.mjs} +5 -5
- package/dist-web/assets/{BufferResource-MsqP5sq3.js → BufferResource-DPkMC1wo.js} +1 -1
- package/dist-web/assets/CanvasRenderer-DokMtSX9.js +1 -0
- package/dist-web/assets/DebugToolsPanel.vue_vue_type_script_setup_true_lang-Ceg-0WOD.js +110 -0
- package/dist-web/assets/{Filter-wRJtdXx9.js → Filter-Bo_p3BVX.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-Ch0tqQix.js} +1 -1
- package/dist-web/assets/{PageHeader-C_yw54le.js → PageHeader-CRlmZoKw.js} +1 -1
- package/dist-web/assets/{RelationshipGalaxyCanvas-DTzJq7q7.js → RelationshipGalaxyCanvas-W_6XfPOG.js} +3 -3
- package/dist-web/assets/{RelationshipGalaxyThreeCanvas-DIE-0_iM.js → RelationshipGalaxyThreeCanvas-bB1nCwQ_.js} +1 -1
- package/dist-web/assets/{RenderTargetSystem-BSAQFamI.js → RenderTargetSystem-obRi6fMT.js} +1 -1
- package/dist-web/assets/{WebGLRenderer-lfzmCZNT.js → WebGLRenderer-xcDlnrCZ.js} +1 -1
- package/dist-web/assets/{WebGPURenderer-BnEdfdr_.js → WebGPURenderer-DbAOiiYr.js} +1 -1
- package/dist-web/assets/{browserAll-gRNfDyPu.js → browserAll-DVRJimO8.js} +1 -1
- package/dist-web/assets/electron-BSGRuxX9.js +1 -0
- package/dist-web/assets/{fetch-BW3aO7PS.js → fetch-7UN4YW3A.js} +1 -1
- package/dist-web/assets/{fetch-CQt-gb-f.js → fetch-B2alAySK.js} +1 -1
- package/dist-web/assets/fetch-B2rVTMYY.js +17 -0
- package/dist-web/assets/{fetch-C1fI_KOX.js → fetch-BSShKcMt.js} +1 -1
- package/dist-web/assets/{fetch-mFlEBZyB.js → fetch-BmxMf3I6.js} +1 -1
- package/dist-web/assets/{fetch-BwF5hNuF.js → fetch-CR5UDFW7.js} +1 -1
- package/dist-web/assets/{fetch-CFRoSIAn.js → fetch-ChAsglT5.js} +1 -1
- package/dist-web/assets/{fetch-CN91TIwV.js → fetch-DWEjTdcP.js} +1 -1
- package/dist-web/assets/{fetch-DPCJ-1l-.js → fetch-DfoxedXR.js} +1 -1
- package/dist-web/assets/{fetch-gMB4wuBd.js → fetch-t0WsRKHW.js} +1 -1
- package/dist-web/assets/index-B4l3rHie.css +1 -0
- package/dist-web/assets/{index-ZdCB7xcf.js → index-BVUTV9_w.js} +4 -4
- package/dist-web/assets/index-BmXNdMV5.js +2 -0
- package/dist-web/assets/index-BsXljrlg.js +22 -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-CQ5lRCHt.js +173 -0
- package/dist-web/assets/index-CZv0ZOID.js +2 -0
- package/dist-web/assets/{index-DveHdPfK.js → index-CfgDqd_z.js} +1 -1
- package/dist-web/assets/{index-Bg4GGPXv.js → index-CtVR_KuP.js} +1 -1
- package/dist-web/assets/index-DT0Bu8mH.js +64 -0
- package/dist-web/assets/index-DjcWJv4b.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-knHSvvNj.js} +1 -1
- package/dist-web/assets/{webworkerAll-D_6PgURS.js → webworkerAll-CMKoykfD.js} +1 -1
- package/dist-web/index.html +3 -3
- package/package.json +1 -1
- package/dist/chunk-7RW4CQAO.mjs.map +0 -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-D4WTE7LV.mjs.map +0 -1
- package/dist/chunk-I35WHWA6.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-VBDMWOKL.mjs.map} +0 -0
- /package/dist/{chunk-6FPEP2ZX.mjs.map → chunk-326OCJOT.mjs.map} +0 -0
- /package/dist/{chunk-SKUG5XVD.mjs.map → chunk-5EE2EFPE.mjs.map} +0 -0
- /package/dist/{chunk-2MZ6G4AC.mjs.map → chunk-WV5GSRQM.mjs.map} +0 -0
- /package/dist/{http-5E7ARYFW.mjs.map → http-L4AMELPZ.mjs.map} +0 -0
- /package/dist/{import-3KG6VK6O.mjs.map → import-WWCSXGVS.mjs.map} +0 -0
- /package/dist/{mcp-JBI4CUQT.mjs.map → mcp-A4M3LT5O.mjs.map} +0 -0
- /package/dist/{src-UTBQAOJ3.mjs.map → src-MPNBTFOE.mjs.map} +0 -0
- /package/dist/{stream-import-7HZ4IRYJ.mjs.map → stream-import-HIE7PNL4.mjs.map} +0 -0
- /package/dist/{worker-runtime-ALS4MR2A.mjs.map → worker-runtime-BJXO24XX.mjs.map} +0 -0
|
@@ -9,19 +9,20 @@ import {
|
|
|
9
9
|
analyzeNewImport,
|
|
10
10
|
incrementalImport,
|
|
11
11
|
streamImport
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-KBSAVUFV.mjs";
|
|
13
13
|
import {
|
|
14
14
|
closeServerAiLogger,
|
|
15
15
|
createCliRunAgentStream,
|
|
16
|
+
getServerAiLogger,
|
|
16
17
|
initServerAiLogger
|
|
17
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-5EE2EFPE.mjs";
|
|
18
19
|
import {
|
|
19
20
|
AGENT_TOOL_REGISTRY,
|
|
20
21
|
CoreDataProvider
|
|
21
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-TCZHG3G7.mjs";
|
|
22
23
|
import {
|
|
23
24
|
assertCliDataDirCompatible
|
|
24
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-326OCJOT.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-ULNRUS55.mjs";
|
|
46
47
|
import {
|
|
47
48
|
AIChatManager,
|
|
48
49
|
ArchiveImportError,
|
|
@@ -75,6 +76,7 @@ import {
|
|
|
75
76
|
getSupportedFormats,
|
|
76
77
|
isDictDownloaded,
|
|
77
78
|
isSemanticIndexConfigured,
|
|
79
|
+
logNativeParserStatus,
|
|
78
80
|
member_service_exports,
|
|
79
81
|
mergeRulesForLocale,
|
|
80
82
|
owner_profile_service_exports,
|
|
@@ -89,7 +91,7 @@ import {
|
|
|
89
91
|
streamParseFileInfo,
|
|
90
92
|
summary_service_exports,
|
|
91
93
|
validateApiKey
|
|
92
|
-
} from "./chunk-
|
|
94
|
+
} from "./chunk-36JGKW6K.mjs";
|
|
93
95
|
import {
|
|
94
96
|
ALL_MIGRATIONS,
|
|
95
97
|
BUILTIN_MODELS,
|
|
@@ -152,10 +154,10 @@ import {
|
|
|
152
154
|
verifyCliDataPath,
|
|
153
155
|
writeAuthProfile,
|
|
154
156
|
writeConfigField
|
|
155
|
-
} from "./chunk-
|
|
157
|
+
} from "./chunk-LVEJHB3X.mjs";
|
|
156
158
|
|
|
157
159
|
// src/http/index.ts
|
|
158
|
-
import * as
|
|
160
|
+
import * as fs12 from "fs";
|
|
159
161
|
import * as crypto4 from "crypto";
|
|
160
162
|
|
|
161
163
|
// src/http/server.ts
|
|
@@ -1097,7 +1099,14 @@ function registerSqlRoutes(server2, ctx) {
|
|
|
1097
1099
|
return reply.code(400).send({ error: "Missing sql parameter" });
|
|
1098
1100
|
}
|
|
1099
1101
|
try {
|
|
1100
|
-
|
|
1102
|
+
const result = executeSql(db, sql, { columnar: true, timing: true, maxRows: 0 });
|
|
1103
|
+
return {
|
|
1104
|
+
columns: result.columns,
|
|
1105
|
+
rows: result.rows,
|
|
1106
|
+
rowCount: result.rowCount,
|
|
1107
|
+
duration: result.duration ?? 0,
|
|
1108
|
+
limited: result.truncated
|
|
1109
|
+
};
|
|
1101
1110
|
} catch (err) {
|
|
1102
1111
|
const message = err instanceof Error ? err.message : "SQL execution error";
|
|
1103
1112
|
return reply.code(400).send({ error: message });
|
|
@@ -1794,13 +1803,53 @@ function registerAiSummaryRoutes(server2, ctx) {
|
|
|
1794
1803
|
});
|
|
1795
1804
|
}
|
|
1796
1805
|
|
|
1797
|
-
// ../../packages/http-routes/src/routes/web/ai-
|
|
1806
|
+
// ../../packages/http-routes/src/routes/web/ai-logs.ts
|
|
1807
|
+
import fs3 from "fs";
|
|
1798
1808
|
import path4 from "path";
|
|
1809
|
+
function findLatestAiLogPath(logsDir) {
|
|
1810
|
+
const aiLogDir = path4.join(logsDir, "ai");
|
|
1811
|
+
if (!fs3.existsSync(aiLogDir)) return null;
|
|
1812
|
+
const latest = fs3.readdirSync(aiLogDir).filter((name) => name.startsWith("ai_") && name.endsWith(".log")).map((name) => {
|
|
1813
|
+
const filePath = path4.join(aiLogDir, name);
|
|
1814
|
+
const stat2 = fs3.statSync(filePath);
|
|
1815
|
+
return { path: filePath, mtimeMs: stat2.mtimeMs };
|
|
1816
|
+
}).sort((a, b) => b.mtimeMs - a.mtimeMs)[0];
|
|
1817
|
+
return latest?.path ?? null;
|
|
1818
|
+
}
|
|
1819
|
+
function resolveAiLogPath(ctx) {
|
|
1820
|
+
const currentPath = ctx.getCurrentAiLogPath?.();
|
|
1821
|
+
if (currentPath && fs3.existsSync(currentPath)) return currentPath;
|
|
1822
|
+
return findLatestAiLogPath(ctx.pathProvider.getLogsDir());
|
|
1823
|
+
}
|
|
1824
|
+
function isLoopbackAddress(address) {
|
|
1825
|
+
if (!address) return true;
|
|
1826
|
+
const normalized = address.replace(/^::ffff:/, "");
|
|
1827
|
+
return normalized === "localhost" || normalized === "::1" || normalized === "0:0:0:0:0:0:0:1" || normalized.startsWith("127.");
|
|
1828
|
+
}
|
|
1829
|
+
function registerAiLogRoutes(server2, ctx) {
|
|
1830
|
+
server2.post("/_web/ai/logs/show", async (request, reply) => {
|
|
1831
|
+
if (!ctx.showInFolder) return reply.code(501).send({ success: false, error: "Not supported" });
|
|
1832
|
+
if (!isLoopbackAddress(request.ip)) {
|
|
1833
|
+
return { success: false, error: "Opening AI logs is only supported on this device" };
|
|
1834
|
+
}
|
|
1835
|
+
const logPath = resolveAiLogPath(ctx);
|
|
1836
|
+
if (!logPath) return { success: false, error: "No AI log files found" };
|
|
1837
|
+
try {
|
|
1838
|
+
await ctx.showInFolder(logPath);
|
|
1839
|
+
return { success: true, path: logPath };
|
|
1840
|
+
} catch (error) {
|
|
1841
|
+
return { success: false, error: error instanceof Error ? error.message : String(error) };
|
|
1842
|
+
}
|
|
1843
|
+
});
|
|
1844
|
+
}
|
|
1845
|
+
|
|
1846
|
+
// ../../packages/http-routes/src/routes/web/ai-semantic-index.ts
|
|
1847
|
+
import path5 from "path";
|
|
1799
1848
|
function registerSemanticIndexRoutes(server2, ctx) {
|
|
1800
1849
|
const service = ctx.semanticIndexService;
|
|
1801
1850
|
if (!service) {
|
|
1802
1851
|
if (ctx.aiDataDir) {
|
|
1803
|
-
const configStore = new SemanticIndexConfigStore(
|
|
1852
|
+
const configStore = new SemanticIndexConfigStore(path5.join(ctx.aiDataDir, SEMANTIC_INDEX_CONFIG_FILE));
|
|
1804
1853
|
const configResponse = (config) => ({
|
|
1805
1854
|
config,
|
|
1806
1855
|
apiKeySet: resolveSemanticIndexApiKeySet(config, ctx.resolveApiKey),
|
|
@@ -1855,8 +1904,8 @@ function registerSemanticIndexRoutes(server2, ctx) {
|
|
|
1855
1904
|
server2.post("/_web/ai/semantic-index/status", async (request) => {
|
|
1856
1905
|
return { sessions: await service.statusForSessions(request.body.sessionIds ?? []) };
|
|
1857
1906
|
});
|
|
1858
|
-
const sessionAction = (
|
|
1859
|
-
server2.post(
|
|
1907
|
+
const sessionAction = (path14, action) => {
|
|
1908
|
+
server2.post(path14, async (request, reply) => {
|
|
1860
1909
|
const { sessionId } = request.body;
|
|
1861
1910
|
if (!sessionId) return reply.code(400).send({ error: "sessionId is required" });
|
|
1862
1911
|
await action(sessionId);
|
|
@@ -1887,9 +1936,9 @@ function registerSemanticIndexRoutes(server2, ctx) {
|
|
|
1887
1936
|
}
|
|
1888
1937
|
|
|
1889
1938
|
// ../../packages/http-routes/src/routes/web/merge.ts
|
|
1890
|
-
import * as
|
|
1939
|
+
import * as fs4 from "fs";
|
|
1891
1940
|
import * as os from "os";
|
|
1892
|
-
import * as
|
|
1941
|
+
import * as path6 from "path";
|
|
1893
1942
|
function ensureDb2(ctx, sessionId) {
|
|
1894
1943
|
const db = ctx.dbManager.open(sessionId);
|
|
1895
1944
|
if (!db) throw sessionNotFound(sessionId);
|
|
@@ -1906,31 +1955,31 @@ function registerMergeRoutes(server2, ctx) {
|
|
|
1906
1955
|
if (contentType.includes("multipart/form-data")) {
|
|
1907
1956
|
const data = await request.file();
|
|
1908
1957
|
if (!data) return reply.code(400).send({ error: "No file uploaded" });
|
|
1909
|
-
const tmpDir =
|
|
1910
|
-
const tmpPath =
|
|
1958
|
+
const tmpDir = fs4.mkdtempSync(path6.join(os.tmpdir(), "chatlab-merge-"));
|
|
1959
|
+
const tmpPath = path6.join(tmpDir, data.filename || "upload");
|
|
1911
1960
|
const chunks = [];
|
|
1912
1961
|
for await (const chunk of data.file) {
|
|
1913
1962
|
chunks.push(chunk);
|
|
1914
1963
|
}
|
|
1915
|
-
|
|
1964
|
+
fs4.writeFileSync(tmpPath, Buffer.concat(chunks));
|
|
1916
1965
|
filePath = tmpPath;
|
|
1917
1966
|
cleanupFile = true;
|
|
1918
1967
|
cleanupDir = tmpDir;
|
|
1919
1968
|
} else {
|
|
1920
1969
|
const body = request.body;
|
|
1921
1970
|
if (!body?.filePath) return reply.code(400).send({ error: "Missing filePath in body" });
|
|
1922
|
-
if (!
|
|
1971
|
+
if (!fs4.existsSync(body.filePath)) return reply.code(400).send({ error: "File not found" });
|
|
1923
1972
|
filePath = body.filePath;
|
|
1924
1973
|
}
|
|
1925
1974
|
try {
|
|
1926
1975
|
const result = await streamParseFileInfo(filePath, {
|
|
1927
1976
|
createTempDatabase(sourceFilePath) {
|
|
1928
|
-
return mergeCache.createTempDatabase(
|
|
1977
|
+
return mergeCache.createTempDatabase(path6.basename(sourceFilePath));
|
|
1929
1978
|
},
|
|
1930
1979
|
onProgress() {
|
|
1931
1980
|
}
|
|
1932
1981
|
});
|
|
1933
|
-
const filename =
|
|
1982
|
+
const filename = path6.basename(filePath);
|
|
1934
1983
|
const handle = mergeCache.store(filename, result.tempDbPath);
|
|
1935
1984
|
return {
|
|
1936
1985
|
handle,
|
|
@@ -1944,13 +1993,13 @@ function registerMergeRoutes(server2, ctx) {
|
|
|
1944
1993
|
} finally {
|
|
1945
1994
|
if (cleanupFile) {
|
|
1946
1995
|
try {
|
|
1947
|
-
|
|
1996
|
+
fs4.unlinkSync(filePath);
|
|
1948
1997
|
} catch {
|
|
1949
1998
|
}
|
|
1950
1999
|
}
|
|
1951
2000
|
if (cleanupDir) {
|
|
1952
2001
|
try {
|
|
1953
|
-
|
|
2002
|
+
fs4.rmdirSync(cleanupDir);
|
|
1954
2003
|
} catch {
|
|
1955
2004
|
}
|
|
1956
2005
|
}
|
|
@@ -1999,20 +2048,20 @@ function registerMergeRoutes(server2, ctx) {
|
|
|
1999
2048
|
return reply.code(501).send({ error: "Import not available on this server" });
|
|
2000
2049
|
}
|
|
2001
2050
|
const jsonData = JSON.stringify(merged.chatLabData);
|
|
2002
|
-
const tmpDir =
|
|
2003
|
-
const safeName =
|
|
2004
|
-
const tmpPath =
|
|
2005
|
-
|
|
2051
|
+
const tmpDir = fs4.mkdtempSync(path6.join(os.tmpdir(), "chatlab-merged-"));
|
|
2052
|
+
const safeName = path6.basename(outputName).replace(/[/\\?%*:|"<>]/g, "_") || "merged";
|
|
2053
|
+
const tmpPath = path6.join(tmpDir, `${safeName}.json`);
|
|
2054
|
+
fs4.writeFileSync(tmpPath, jsonData, "utf-8");
|
|
2006
2055
|
try {
|
|
2007
2056
|
const importResult = await streamImport2(dbManager2, tmpPath);
|
|
2008
2057
|
sessionId = importResult.sessionId;
|
|
2009
2058
|
} finally {
|
|
2010
2059
|
try {
|
|
2011
|
-
|
|
2060
|
+
fs4.unlinkSync(tmpPath);
|
|
2012
2061
|
} catch {
|
|
2013
2062
|
}
|
|
2014
2063
|
try {
|
|
2015
|
-
|
|
2064
|
+
fs4.rmdirSync(tmpDir);
|
|
2016
2065
|
} catch {
|
|
2017
2066
|
}
|
|
2018
2067
|
}
|
|
@@ -2086,16 +2135,16 @@ function registerMergeRoutes(server2, ctx) {
|
|
|
2086
2135
|
}
|
|
2087
2136
|
|
|
2088
2137
|
// ../../packages/http-routes/src/routes/web/cache.ts
|
|
2089
|
-
import * as
|
|
2138
|
+
import * as fs5 from "fs";
|
|
2090
2139
|
import * as fsp from "fs/promises";
|
|
2091
|
-
import * as
|
|
2140
|
+
import * as path7 from "path";
|
|
2092
2141
|
async function getDirSize(dirPath) {
|
|
2093
2142
|
let totalSize = 0;
|
|
2094
2143
|
try {
|
|
2095
|
-
if (!
|
|
2144
|
+
if (!fs5.existsSync(dirPath)) return 0;
|
|
2096
2145
|
const files = await fsp.readdir(dirPath, { withFileTypes: true });
|
|
2097
2146
|
for (const file of files) {
|
|
2098
|
-
const filePath =
|
|
2147
|
+
const filePath = path7.join(dirPath, file.name);
|
|
2099
2148
|
if (file.isDirectory()) {
|
|
2100
2149
|
totalSize += await getDirSize(filePath);
|
|
2101
2150
|
} else {
|
|
@@ -2110,10 +2159,10 @@ async function getDirSize(dirPath) {
|
|
|
2110
2159
|
async function getFileCount(dirPath) {
|
|
2111
2160
|
let count = 0;
|
|
2112
2161
|
try {
|
|
2113
|
-
if (!
|
|
2162
|
+
if (!fs5.existsSync(dirPath)) return 0;
|
|
2114
2163
|
const files = await fsp.readdir(dirPath, { withFileTypes: true });
|
|
2115
2164
|
for (const file of files) {
|
|
2116
|
-
const filePath =
|
|
2165
|
+
const filePath = path7.join(dirPath, file.name);
|
|
2117
2166
|
if (file.isDirectory()) {
|
|
2118
2167
|
count += await getFileCount(filePath);
|
|
2119
2168
|
} else {
|
|
@@ -2126,10 +2175,10 @@ async function getFileCount(dirPath) {
|
|
|
2126
2175
|
}
|
|
2127
2176
|
function hasChatLabDatabases(dirPath) {
|
|
2128
2177
|
try {
|
|
2129
|
-
const markerPath =
|
|
2130
|
-
const dbDir =
|
|
2131
|
-
if (!
|
|
2132
|
-
return
|
|
2178
|
+
const markerPath = path7.join(dirPath, ".chatlab");
|
|
2179
|
+
const dbDir = path7.join(dirPath, "databases");
|
|
2180
|
+
if (!fs5.existsSync(markerPath) || !fs5.existsSync(dbDir)) return false;
|
|
2181
|
+
return fs5.readdirSync(dbDir).some((file) => file.endsWith(".db"));
|
|
2133
2182
|
} catch {
|
|
2134
2183
|
return false;
|
|
2135
2184
|
}
|
|
@@ -2144,6 +2193,8 @@ function registerCacheRoutes(server2, ctx) {
|
|
|
2144
2193
|
name: "settings.storage.cache.databases.name",
|
|
2145
2194
|
description: "settings.storage.cache.databases.description",
|
|
2146
2195
|
path: pp.getDatabaseDir(),
|
|
2196
|
+
scope: "user-data",
|
|
2197
|
+
rootPath: pp.getUserDataDir(),
|
|
2147
2198
|
icon: "i-heroicons-circle-stack",
|
|
2148
2199
|
canClear: false
|
|
2149
2200
|
},
|
|
@@ -2152,6 +2203,8 @@ function registerCacheRoutes(server2, ctx) {
|
|
|
2152
2203
|
name: "settings.storage.cache.ai.name",
|
|
2153
2204
|
description: "settings.storage.cache.ai.description",
|
|
2154
2205
|
path: pp.getAiDataDir(),
|
|
2206
|
+
scope: "system-data",
|
|
2207
|
+
rootPath: pp.getSystemDir(),
|
|
2155
2208
|
icon: "i-heroicons-sparkles",
|
|
2156
2209
|
canClear: false
|
|
2157
2210
|
},
|
|
@@ -2160,6 +2213,8 @@ function registerCacheRoutes(server2, ctx) {
|
|
|
2160
2213
|
name: "settings.storage.cache.statsCache.name",
|
|
2161
2214
|
description: "settings.storage.cache.statsCache.description",
|
|
2162
2215
|
path: pp.getCacheDir(),
|
|
2216
|
+
scope: "system-data",
|
|
2217
|
+
rootPath: pp.getSystemDir(),
|
|
2163
2218
|
icon: "i-heroicons-bolt",
|
|
2164
2219
|
canClear: true
|
|
2165
2220
|
},
|
|
@@ -2168,6 +2223,8 @@ function registerCacheRoutes(server2, ctx) {
|
|
|
2168
2223
|
name: "settings.storage.cache.logs.name",
|
|
2169
2224
|
description: "settings.storage.cache.logs.description",
|
|
2170
2225
|
path: pp.getLogsDir(),
|
|
2226
|
+
scope: "system-data",
|
|
2227
|
+
rootPath: pp.getSystemDir(),
|
|
2171
2228
|
icon: "i-heroicons-document-text",
|
|
2172
2229
|
canClear: true
|
|
2173
2230
|
}
|
|
@@ -2175,11 +2232,11 @@ function registerCacheRoutes(server2, ctx) {
|
|
|
2175
2232
|
const results = await Promise.all(
|
|
2176
2233
|
directories.map(async (dir) => {
|
|
2177
2234
|
const [size, fileCount] = await Promise.all([getDirSize(dir.path), getFileCount(dir.path)]);
|
|
2178
|
-
return { ...dir, size, fileCount, exists:
|
|
2235
|
+
return { ...dir, size, fileCount, exists: fs5.existsSync(dir.path) };
|
|
2179
2236
|
})
|
|
2180
2237
|
);
|
|
2181
2238
|
return {
|
|
2182
|
-
baseDir: pp.
|
|
2239
|
+
baseDir: pp.getSystemDir(),
|
|
2183
2240
|
directories: results,
|
|
2184
2241
|
totalSize: results.reduce((sum, d) => sum + d.size, 0)
|
|
2185
2242
|
};
|
|
@@ -2192,10 +2249,10 @@ function registerCacheRoutes(server2, ctx) {
|
|
|
2192
2249
|
};
|
|
2193
2250
|
const dirPath = allowedDirs[cacheId];
|
|
2194
2251
|
if (!dirPath) return { success: false, error: "Not allowed to clear this directory" };
|
|
2195
|
-
if (!
|
|
2252
|
+
if (!fs5.existsSync(dirPath)) return { success: true };
|
|
2196
2253
|
const files = await fsp.readdir(dirPath);
|
|
2197
2254
|
for (const file of files) {
|
|
2198
|
-
const filePath =
|
|
2255
|
+
const filePath = path7.join(dirPath, file);
|
|
2199
2256
|
const stat2 = await fsp.stat(filePath);
|
|
2200
2257
|
if (stat2.isDirectory()) {
|
|
2201
2258
|
await fsp.rm(filePath, { recursive: true });
|
|
@@ -2212,7 +2269,9 @@ function registerCacheRoutes(server2, ctx) {
|
|
|
2212
2269
|
defaultPath: ctx.defaultUserDataDir,
|
|
2213
2270
|
isCustom: ctx.isCustomDataDir ?? false,
|
|
2214
2271
|
canSetDataDir: ctx.canSetDataDir ?? Boolean(ctx.setDataDir),
|
|
2215
|
-
|
|
2272
|
+
managedScope: "chat-databases",
|
|
2273
|
+
managedDescription: "settings.storage.dataLocation.managedDescription",
|
|
2274
|
+
hasLegacyDataAtDefaultDir: Boolean(ctx.defaultUserDataDir) && path7.resolve(pp.getUserDataDir()) !== path7.resolve(ctx.defaultUserDataDir ?? "") && hasChatLabDatabases(ctx.defaultUserDataDir ?? ""),
|
|
2216
2275
|
pendingMigration: pending ? {
|
|
2217
2276
|
from: pending.from,
|
|
2218
2277
|
to: pending.to,
|
|
@@ -2229,8 +2288,8 @@ function registerCacheRoutes(server2, ctx) {
|
|
|
2229
2288
|
return ctx.setDataDir(targetPath, migrate);
|
|
2230
2289
|
});
|
|
2231
2290
|
server2.get("/_web/cache/latest-import-log", async () => {
|
|
2232
|
-
const importLogDir =
|
|
2233
|
-
if (!
|
|
2291
|
+
const importLogDir = path7.join(pp.getLogsDir(), "import");
|
|
2292
|
+
if (!fs5.existsSync(importLogDir)) {
|
|
2234
2293
|
return { success: false, error: "Log directory not found" };
|
|
2235
2294
|
}
|
|
2236
2295
|
const files = await fsp.readdir(importLogDir);
|
|
@@ -2240,7 +2299,7 @@ function registerCacheRoutes(server2, ctx) {
|
|
|
2240
2299
|
}
|
|
2241
2300
|
const fileStats = await Promise.all(
|
|
2242
2301
|
logFiles.map(async (f) => {
|
|
2243
|
-
const filePath =
|
|
2302
|
+
const filePath = path7.join(importLogDir, f);
|
|
2244
2303
|
const stat2 = await fsp.stat(filePath);
|
|
2245
2304
|
return { name: f, path: filePath, mtime: stat2.mtime.getTime() };
|
|
2246
2305
|
})
|
|
@@ -2264,18 +2323,19 @@ function registerCacheRoutes(server2, ctx) {
|
|
|
2264
2323
|
const base64Data = dataUrl.replace(/^data:[^,]+,/, "");
|
|
2265
2324
|
buffer = Buffer.from(base64Data, "base64");
|
|
2266
2325
|
}
|
|
2267
|
-
if (!
|
|
2268
|
-
|
|
2326
|
+
if (!fs5.existsSync(downloadsDir)) {
|
|
2327
|
+
fs5.mkdirSync(downloadsDir, { recursive: true });
|
|
2269
2328
|
}
|
|
2270
|
-
const filePath =
|
|
2271
|
-
|
|
2329
|
+
const filePath = path7.join(downloadsDir, filename);
|
|
2330
|
+
fs5.writeFileSync(filePath, buffer);
|
|
2272
2331
|
return { success: true, filePath };
|
|
2273
2332
|
});
|
|
2274
2333
|
server2.post("/_web/cache/open-dir", async (request, reply) => {
|
|
2275
2334
|
if (!ctx.openDirectory) return reply.code(501).send({ success: false, error: "Not supported" });
|
|
2276
2335
|
const { cacheId } = request.body;
|
|
2277
2336
|
const dirPaths = {
|
|
2278
|
-
base: pp.
|
|
2337
|
+
base: pp.getSystemDir(),
|
|
2338
|
+
userData: pp.getUserDataDir(),
|
|
2279
2339
|
databases: pp.getDatabaseDir(),
|
|
2280
2340
|
cache: pp.getCacheDir(),
|
|
2281
2341
|
ai: pp.getAiDataDir(),
|
|
@@ -2284,7 +2344,7 @@ function registerCacheRoutes(server2, ctx) {
|
|
|
2284
2344
|
};
|
|
2285
2345
|
const dirPath = dirPaths[cacheId];
|
|
2286
2346
|
if (!dirPath) return { success: false, error: "Unknown directory" };
|
|
2287
|
-
if (!
|
|
2347
|
+
if (!fs5.existsSync(dirPath)) {
|
|
2288
2348
|
await fsp.mkdir(dirPath, { recursive: true });
|
|
2289
2349
|
}
|
|
2290
2350
|
try {
|
|
@@ -2384,6 +2444,7 @@ function registerSharedRoutes(server2, ctx, options) {
|
|
|
2384
2444
|
registerAiToolRoutes(server2, resolvedCtx);
|
|
2385
2445
|
registerAiChatRoutes(server2, resolvedCtx);
|
|
2386
2446
|
registerAiSummaryRoutes(server2, resolvedCtx);
|
|
2447
|
+
registerAiLogRoutes(server2, resolvedCtx);
|
|
2387
2448
|
registerSemanticIndexRoutes(server2, resolvedCtx);
|
|
2388
2449
|
registerMergeRoutes(server2, resolvedCtx);
|
|
2389
2450
|
registerCacheRoutes(server2, resolvedCtx);
|
|
@@ -2434,6 +2495,150 @@ function requireBearerToken(request, reply) {
|
|
|
2434
2495
|
}
|
|
2435
2496
|
}
|
|
2436
2497
|
|
|
2498
|
+
// ../../packages/http-routes/src/routes/web/automation.ts
|
|
2499
|
+
function requireAutomation(ctx) {
|
|
2500
|
+
return ctx.automation ?? null;
|
|
2501
|
+
}
|
|
2502
|
+
function normalizeBaseUrl(input) {
|
|
2503
|
+
let url = input.trim().replace(/\/+$/, "");
|
|
2504
|
+
if (url && !/^https?:\/\//i.test(url)) url = `http://${url}`;
|
|
2505
|
+
if (url && !url.endsWith("/api/v1")) {
|
|
2506
|
+
url = url.replace(/\/api\/v1$/, "") + "/api/v1";
|
|
2507
|
+
}
|
|
2508
|
+
return url;
|
|
2509
|
+
}
|
|
2510
|
+
function buildRemoteSessionsUrl(baseUrl, query = {}) {
|
|
2511
|
+
const searchParams = new URLSearchParams();
|
|
2512
|
+
searchParams.set("format", "chatlab");
|
|
2513
|
+
if (query.keyword?.trim()) searchParams.set("keyword", query.keyword.trim());
|
|
2514
|
+
if (query.limit && Number(query.limit) > 0) searchParams.set("limit", String(query.limit));
|
|
2515
|
+
if (query.cursor) searchParams.set("cursor", query.cursor);
|
|
2516
|
+
return `${baseUrl}/sessions?${searchParams.toString()}`;
|
|
2517
|
+
}
|
|
2518
|
+
function asRecord(value) {
|
|
2519
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
2520
|
+
}
|
|
2521
|
+
function parseRemoteSessionsResponse(body) {
|
|
2522
|
+
const parsed = JSON.parse(body);
|
|
2523
|
+
let sessions;
|
|
2524
|
+
let pageSource;
|
|
2525
|
+
if (Array.isArray(parsed)) {
|
|
2526
|
+
sessions = parsed;
|
|
2527
|
+
} else {
|
|
2528
|
+
const record = asRecord(parsed);
|
|
2529
|
+
const data = asRecord(record?.data);
|
|
2530
|
+
const sessionsSource = record?.sessions ?? data?.sessions ?? record?.data;
|
|
2531
|
+
sessions = Array.isArray(sessionsSource) ? sessionsSource : [];
|
|
2532
|
+
pageSource = asRecord(record?.page) ?? asRecord(data?.page);
|
|
2533
|
+
}
|
|
2534
|
+
return {
|
|
2535
|
+
sessions,
|
|
2536
|
+
page: pageSource && typeof pageSource === "object" ? {
|
|
2537
|
+
hasMore: Boolean(pageSource.hasMore),
|
|
2538
|
+
nextCursor: typeof pageSource.nextCursor === "string" ? pageSource.nextCursor : void 0
|
|
2539
|
+
} : void 0
|
|
2540
|
+
};
|
|
2541
|
+
}
|
|
2542
|
+
function registerAutomationRoutes(server2, ctx) {
|
|
2543
|
+
server2.get("/_web/automation/config", async (_request, reply) => {
|
|
2544
|
+
const automation = requireAutomation(ctx);
|
|
2545
|
+
if (!automation?.serverInfo) return reply.code(404).send({ error: "Automation is not enabled" });
|
|
2546
|
+
return {
|
|
2547
|
+
enabled: true,
|
|
2548
|
+
port: automation.serverInfo.port,
|
|
2549
|
+
token: automation.serverInfo.token,
|
|
2550
|
+
host: automation.serverInfo.host
|
|
2551
|
+
};
|
|
2552
|
+
});
|
|
2553
|
+
server2.get("/_web/automation/data-sources", async (_request, reply) => {
|
|
2554
|
+
const automation = requireAutomation(ctx);
|
|
2555
|
+
if (!automation) return reply.code(404).send({ error: "Automation is not enabled" });
|
|
2556
|
+
return automation.dsManager.loadAll();
|
|
2557
|
+
});
|
|
2558
|
+
server2.post("/_web/automation/data-sources", async (request, reply) => {
|
|
2559
|
+
const automation = requireAutomation(ctx);
|
|
2560
|
+
if (!automation) return reply.code(404).send({ error: "Automation is not enabled" });
|
|
2561
|
+
const ds = automation.dsManager.add(request.body);
|
|
2562
|
+
automation.reloadTimer?.(ds.id);
|
|
2563
|
+
return ds;
|
|
2564
|
+
});
|
|
2565
|
+
server2.patch("/_web/automation/data-sources/:id", async (request, reply) => {
|
|
2566
|
+
const automation = requireAutomation(ctx);
|
|
2567
|
+
if (!automation) return reply.code(404).send({ error: "Automation is not enabled" });
|
|
2568
|
+
const ds = automation.dsManager.update(request.params.id, request.body);
|
|
2569
|
+
if (!ds) return reply.code(404).send({ error: "Data source not found" });
|
|
2570
|
+
automation.reloadTimer?.(ds.id);
|
|
2571
|
+
return ds;
|
|
2572
|
+
});
|
|
2573
|
+
server2.delete("/_web/automation/data-sources/:id", async (request, reply) => {
|
|
2574
|
+
const automation = requireAutomation(ctx);
|
|
2575
|
+
if (!automation) return reply.code(404).send({ error: "Automation is not enabled" });
|
|
2576
|
+
automation.stopTimer?.(request.params.id);
|
|
2577
|
+
const ok = automation.dsManager.delete(request.params.id);
|
|
2578
|
+
if (!ok) return reply.code(404).send({ error: "Data source not found" });
|
|
2579
|
+
return { success: true };
|
|
2580
|
+
});
|
|
2581
|
+
server2.post("/_web/automation/data-sources/:id/sessions", async (request, reply) => {
|
|
2582
|
+
const automation = requireAutomation(ctx);
|
|
2583
|
+
if (!automation) return reply.code(404).send({ error: "Automation is not enabled" });
|
|
2584
|
+
const added = automation.dsManager.addSessions(request.params.id, request.body.sessions);
|
|
2585
|
+
if (added.length === 0 && !automation.dsManager.get(request.params.id)) {
|
|
2586
|
+
return reply.code(404).send({ error: "Data source not found" });
|
|
2587
|
+
}
|
|
2588
|
+
automation.reloadTimer?.(request.params.id, true);
|
|
2589
|
+
for (const sess of added) {
|
|
2590
|
+
void automation.pullEngine.triggerPull(request.params.id, sess.id).catch(() => void 0);
|
|
2591
|
+
}
|
|
2592
|
+
return added;
|
|
2593
|
+
});
|
|
2594
|
+
server2.delete("/_web/automation/data-sources/:id/sessions/:sessId", async (request, reply) => {
|
|
2595
|
+
const automation = requireAutomation(ctx);
|
|
2596
|
+
if (!automation) return reply.code(404).send({ error: "Automation is not enabled" });
|
|
2597
|
+
const removed = automation.dsManager.removeSession(request.params.id, request.params.sessId);
|
|
2598
|
+
if (!removed) return reply.code(404).send({ error: "Session not found" });
|
|
2599
|
+
automation.reloadTimer?.(request.params.id);
|
|
2600
|
+
if (request.query.deleteData === "true" && removed.targetSessionId) {
|
|
2601
|
+
automation.deleteSessionData?.(removed.targetSessionId);
|
|
2602
|
+
}
|
|
2603
|
+
return { success: true };
|
|
2604
|
+
});
|
|
2605
|
+
server2.post(
|
|
2606
|
+
"/_web/automation/data-sources/:id/pull",
|
|
2607
|
+
async (request, reply) => {
|
|
2608
|
+
const automation = requireAutomation(ctx);
|
|
2609
|
+
if (!automation) return reply.code(404).send({ error: "Automation is not enabled" });
|
|
2610
|
+
return automation.pullEngine.triggerPull(request.params.id, request.body?.sessionId);
|
|
2611
|
+
}
|
|
2612
|
+
);
|
|
2613
|
+
server2.post("/_web/automation/data-sources/:id/pull-all", async (request, reply) => {
|
|
2614
|
+
const automation = requireAutomation(ctx);
|
|
2615
|
+
if (!automation) return reply.code(404).send({ error: "Automation is not enabled" });
|
|
2616
|
+
return automation.pullEngine.triggerPullAll(request.params.id);
|
|
2617
|
+
});
|
|
2618
|
+
server2.get("/_web/automation/sync-progress", async (_request, reply) => {
|
|
2619
|
+
const automation = requireAutomation(ctx);
|
|
2620
|
+
if (!automation) return reply.code(404).send({ error: "Automation is not enabled" });
|
|
2621
|
+
return automation.pullEngine.getProgress();
|
|
2622
|
+
});
|
|
2623
|
+
server2.get("/_web/automation/remote-sessions", async (request, reply) => {
|
|
2624
|
+
const automation = requireAutomation(ctx);
|
|
2625
|
+
if (!automation) return reply.code(404).send({ error: "Automation is not enabled" });
|
|
2626
|
+
const { baseUrl, token, keyword, limit, cursor } = request.query;
|
|
2627
|
+
if (!baseUrl) return reply.code(400).send({ error: "baseUrl is required" });
|
|
2628
|
+
const url = buildRemoteSessionsUrl(normalizeBaseUrl(baseUrl), { keyword, limit, cursor });
|
|
2629
|
+
const headers = { Accept: "application/json" };
|
|
2630
|
+
if (token) headers.Authorization = `Bearer ${token}`;
|
|
2631
|
+
try {
|
|
2632
|
+
const resp = await fetch(url, { headers, signal: AbortSignal.timeout(3e4) });
|
|
2633
|
+
if (!resp.ok) throw new Error(`Remote server returned HTTP ${resp.status}`);
|
|
2634
|
+
const body = await resp.text();
|
|
2635
|
+
return parseRemoteSessionsResponse(body);
|
|
2636
|
+
} catch (err) {
|
|
2637
|
+
return reply.code(502).send({ error: err instanceof Error ? err.message : "Failed to fetch remote sessions" });
|
|
2638
|
+
}
|
|
2639
|
+
});
|
|
2640
|
+
}
|
|
2641
|
+
|
|
2437
2642
|
// src/http/server.ts
|
|
2438
2643
|
var JSON_BODY_LIMIT = 50 * 1024 * 1024;
|
|
2439
2644
|
function createServer() {
|
|
@@ -2467,21 +2672,21 @@ function createServer() {
|
|
|
2467
2672
|
|
|
2468
2673
|
// src/http/routes/web/index.ts
|
|
2469
2674
|
import * as os3 from "os";
|
|
2470
|
-
import * as
|
|
2675
|
+
import * as path10 from "path";
|
|
2471
2676
|
|
|
2472
2677
|
// src/http/routes/web/import.ts
|
|
2473
|
-
import * as
|
|
2678
|
+
import * as fs7 from "fs";
|
|
2474
2679
|
import * as os2 from "os";
|
|
2475
|
-
import * as
|
|
2680
|
+
import * as path8 from "path";
|
|
2476
2681
|
import { randomUUID } from "crypto";
|
|
2477
2682
|
import { pipeline } from "stream/promises";
|
|
2478
2683
|
|
|
2479
2684
|
// src/http/routes/web/helpers.ts
|
|
2480
|
-
import * as
|
|
2685
|
+
import * as fs6 from "fs";
|
|
2481
2686
|
function resolveNativeBinding() {
|
|
2482
2687
|
if (process.versions.electron) return void 0;
|
|
2483
2688
|
const nativePath = resolveCliPath("native/better_sqlite3.node");
|
|
2484
|
-
if (
|
|
2689
|
+
if (fs6.existsSync(nativePath)) return nativePath;
|
|
2485
2690
|
return void 0;
|
|
2486
2691
|
}
|
|
2487
2692
|
|
|
@@ -2491,11 +2696,11 @@ var ARCHIVE_UPLOAD_LIMIT = 50 * 1024 * 1024 * 1024;
|
|
|
2491
2696
|
function cleanupTemp(...paths) {
|
|
2492
2697
|
for (const p of paths) {
|
|
2493
2698
|
try {
|
|
2494
|
-
const stat2 =
|
|
2699
|
+
const stat2 = fs7.statSync(p);
|
|
2495
2700
|
if (stat2.isDirectory()) {
|
|
2496
|
-
|
|
2701
|
+
fs7.rmSync(p, { recursive: true, force: true });
|
|
2497
2702
|
} else {
|
|
2498
|
-
|
|
2703
|
+
fs7.unlinkSync(p);
|
|
2499
2704
|
}
|
|
2500
2705
|
} catch {
|
|
2501
2706
|
}
|
|
@@ -2503,7 +2708,7 @@ function cleanupTemp(...paths) {
|
|
|
2503
2708
|
}
|
|
2504
2709
|
function registerImportRoutes2(server2, dbManager2, options = {}) {
|
|
2505
2710
|
const sourceManager = options.sourceManager ?? new ArchiveImportSourceManager({
|
|
2506
|
-
tempRoot:
|
|
2711
|
+
tempRoot: fs7.mkdtempSync(path8.join(os2.tmpdir(), "chatlab-import-sources-"))
|
|
2507
2712
|
});
|
|
2508
2713
|
const runPreparedImport = options.runPreparedImport ?? (async (manifestPath, onProgress) => {
|
|
2509
2714
|
const result = await streamImport(dbManager2, manifestPath, {
|
|
@@ -2527,9 +2732,9 @@ function registerImportRoutes2(server2, dbManager2, options = {}) {
|
|
|
2527
2732
|
limits: { fileSize: ARCHIVE_UPLOAD_LIMIT }
|
|
2528
2733
|
});
|
|
2529
2734
|
if (!data) return reply.code(400).send({ success: false, error: "error.no_file_selected" });
|
|
2530
|
-
const uploadPath =
|
|
2735
|
+
const uploadPath = path8.join(os2.tmpdir(), `chatlab-archive-${randomUUID()}.zip`);
|
|
2531
2736
|
try {
|
|
2532
|
-
await pipeline(data.file,
|
|
2737
|
+
await pipeline(data.file, fs7.createWriteStream(uploadPath, { flags: "wx" }));
|
|
2533
2738
|
if (data.file.truncated) {
|
|
2534
2739
|
cleanupTemp(uploadPath);
|
|
2535
2740
|
return reply.code(413).send({ success: false, error: "error.archive_limit_exceeded" });
|
|
@@ -2594,14 +2799,14 @@ data: ${JSON.stringify(eventData)}
|
|
|
2594
2799
|
server2.post("/_web/detect-format", async (request, reply) => {
|
|
2595
2800
|
const data = await request.file();
|
|
2596
2801
|
if (!data) return reply.code(400).send({ error: "No file uploaded" });
|
|
2597
|
-
const tmpDir =
|
|
2598
|
-
const tmpPath =
|
|
2802
|
+
const tmpDir = fs7.mkdtempSync(path8.join(os2.tmpdir(), "chatlab-detect-"));
|
|
2803
|
+
const tmpPath = path8.join(tmpDir, data.filename || "upload");
|
|
2599
2804
|
try {
|
|
2600
2805
|
const chunks = [];
|
|
2601
2806
|
for await (const chunk of data.file) {
|
|
2602
2807
|
chunks.push(chunk);
|
|
2603
2808
|
}
|
|
2604
|
-
|
|
2809
|
+
fs7.writeFileSync(tmpPath, Buffer.concat(chunks));
|
|
2605
2810
|
const format = detectFormat(tmpPath);
|
|
2606
2811
|
const allFormats = detectAllFormats(tmpPath);
|
|
2607
2812
|
return { format, allFormats };
|
|
@@ -2612,14 +2817,14 @@ data: ${JSON.stringify(eventData)}
|
|
|
2612
2817
|
server2.post("/_web/scan-multi-chat", async (request, reply) => {
|
|
2613
2818
|
const data = await request.file();
|
|
2614
2819
|
if (!data) return reply.code(400).send({ error: "No file uploaded" });
|
|
2615
|
-
const tmpDir =
|
|
2616
|
-
const tmpPath =
|
|
2820
|
+
const tmpDir = fs7.mkdtempSync(path8.join(os2.tmpdir(), "chatlab-scan-"));
|
|
2821
|
+
const tmpPath = path8.join(tmpDir, data.filename || "upload");
|
|
2617
2822
|
try {
|
|
2618
2823
|
const chunks = [];
|
|
2619
2824
|
for await (const chunk of data.file) {
|
|
2620
2825
|
chunks.push(chunk);
|
|
2621
2826
|
}
|
|
2622
|
-
|
|
2827
|
+
fs7.writeFileSync(tmpPath, Buffer.concat(chunks));
|
|
2623
2828
|
const chats = await scanMultiChatFile(tmpPath);
|
|
2624
2829
|
return { chats };
|
|
2625
2830
|
} finally {
|
|
@@ -2629,13 +2834,13 @@ data: ${JSON.stringify(eventData)}
|
|
|
2629
2834
|
server2.post("/_web/import", async (request, reply) => {
|
|
2630
2835
|
const data = await request.file();
|
|
2631
2836
|
if (!data) return reply.code(400).send({ error: "No file uploaded" });
|
|
2632
|
-
const tmpDir =
|
|
2633
|
-
const tmpPath =
|
|
2837
|
+
const tmpDir = fs7.mkdtempSync(path8.join(os2.tmpdir(), "chatlab-import-"));
|
|
2838
|
+
const tmpPath = path8.join(tmpDir, data.filename || "upload");
|
|
2634
2839
|
const chunks = [];
|
|
2635
2840
|
for await (const chunk of data.file) {
|
|
2636
2841
|
chunks.push(chunk);
|
|
2637
2842
|
}
|
|
2638
|
-
|
|
2843
|
+
fs7.writeFileSync(tmpPath, Buffer.concat(chunks));
|
|
2639
2844
|
const formatId = data.fields?.formatId?.value;
|
|
2640
2845
|
const chatIndexStr = data.fields?.chatIndex?.value;
|
|
2641
2846
|
const chatIndex = chatIndexStr !== void 0 ? parseInt(chatIndexStr, 10) : void 0;
|
|
@@ -2678,7 +2883,7 @@ data: ${JSON.stringify(eventData)}
|
|
|
2678
2883
|
server2.post("/_web/import-directory", async (request, reply) => {
|
|
2679
2884
|
const parts = request.parts();
|
|
2680
2885
|
if (!parts) return reply.code(400).send({ error: "No files uploaded" });
|
|
2681
|
-
const tmpDir =
|
|
2886
|
+
const tmpDir = fs7.mkdtempSync(path8.join(os2.tmpdir(), "chatlab-dir-import-"));
|
|
2682
2887
|
const relativePaths = [];
|
|
2683
2888
|
const fileBuffers = [];
|
|
2684
2889
|
try {
|
|
@@ -2706,10 +2911,10 @@ data: ${JSON.stringify(eventData)}
|
|
|
2706
2911
|
if (segments.length > 1) {
|
|
2707
2912
|
relPath = segments.slice(1).join("/");
|
|
2708
2913
|
}
|
|
2709
|
-
const targetPath =
|
|
2710
|
-
if (!targetPath.startsWith(tmpDir +
|
|
2711
|
-
|
|
2712
|
-
|
|
2914
|
+
const targetPath = path8.resolve(tmpDir, relPath);
|
|
2915
|
+
if (!targetPath.startsWith(tmpDir + path8.sep)) continue;
|
|
2916
|
+
fs7.mkdirSync(path8.dirname(targetPath), { recursive: true });
|
|
2917
|
+
fs7.writeFileSync(targetPath, fileBuffers[i].data);
|
|
2713
2918
|
}
|
|
2714
2919
|
const entryPath = findEntryFileInDirectory(tmpDir);
|
|
2715
2920
|
if (!entryPath) {
|
|
@@ -2754,13 +2959,13 @@ data: ${JSON.stringify({ success: false, error: err instanceof Error ? err.messa
|
|
|
2754
2959
|
const sessionId = request.params.id;
|
|
2755
2960
|
const data = await request.file();
|
|
2756
2961
|
if (!data) return reply.code(400).send({ error: "No file uploaded" });
|
|
2757
|
-
const tmpDir =
|
|
2758
|
-
const tmpPath =
|
|
2962
|
+
const tmpDir = fs7.mkdtempSync(path8.join(os2.tmpdir(), "chatlab-inc-"));
|
|
2963
|
+
const tmpPath = path8.join(tmpDir, data.filename || "upload");
|
|
2759
2964
|
const chunks = [];
|
|
2760
2965
|
for await (const chunk of data.file) {
|
|
2761
2966
|
chunks.push(chunk);
|
|
2762
2967
|
}
|
|
2763
|
-
|
|
2968
|
+
fs7.writeFileSync(tmpPath, Buffer.concat(chunks));
|
|
2764
2969
|
reply.raw.writeHead(200, {
|
|
2765
2970
|
"Content-Type": "text/event-stream",
|
|
2766
2971
|
"Cache-Control": "no-cache",
|
|
@@ -2792,13 +2997,13 @@ data: ${JSON.stringify(eventData)}
|
|
|
2792
2997
|
const sessionId = request.params.id;
|
|
2793
2998
|
const data = await request.file();
|
|
2794
2999
|
if (!data) return reply.code(400).send({ error: "No file uploaded" });
|
|
2795
|
-
const tmpDir =
|
|
2796
|
-
const tmpPath =
|
|
3000
|
+
const tmpDir = fs7.mkdtempSync(path8.join(os2.tmpdir(), "chatlab-analyze-"));
|
|
3001
|
+
const tmpPath = path8.join(tmpDir, data.filename || "upload");
|
|
2797
3002
|
const chunks = [];
|
|
2798
3003
|
for await (const chunk of data.file) {
|
|
2799
3004
|
chunks.push(chunk);
|
|
2800
3005
|
}
|
|
2801
|
-
|
|
3006
|
+
fs7.writeFileSync(tmpPath, Buffer.concat(chunks));
|
|
2802
3007
|
try {
|
|
2803
3008
|
return await analyzeIncrementalImport(dbManager2, sessionId, tmpPath);
|
|
2804
3009
|
} catch (err) {
|
|
@@ -2810,13 +3015,13 @@ data: ${JSON.stringify(eventData)}
|
|
|
2810
3015
|
server2.post("/_web/import/analyze", async (request, reply) => {
|
|
2811
3016
|
const data = await request.file();
|
|
2812
3017
|
if (!data) return reply.code(400).send({ error: "No file uploaded" });
|
|
2813
|
-
const tmpDir =
|
|
2814
|
-
const tmpPath =
|
|
3018
|
+
const tmpDir = fs7.mkdtempSync(path8.join(os2.tmpdir(), "chatlab-analyze-"));
|
|
3019
|
+
const tmpPath = path8.join(tmpDir, data.filename || "upload");
|
|
2815
3020
|
const chunks = [];
|
|
2816
3021
|
for await (const chunk of data.file) {
|
|
2817
3022
|
chunks.push(chunk);
|
|
2818
3023
|
}
|
|
2819
|
-
|
|
3024
|
+
fs7.writeFileSync(tmpPath, Buffer.concat(chunks));
|
|
2820
3025
|
try {
|
|
2821
3026
|
return await analyzeNewImport(tmpPath);
|
|
2822
3027
|
} catch (err) {
|
|
@@ -2846,17 +3051,17 @@ data: ${JSON.stringify(eventData)}
|
|
|
2846
3051
|
|
|
2847
3052
|
`);
|
|
2848
3053
|
}
|
|
2849
|
-
const tmpDir =
|
|
3054
|
+
const tmpDir = fs7.mkdtempSync(path8.join(os2.tmpdir(), "chatlab-demo-"));
|
|
2850
3055
|
try {
|
|
2851
3056
|
const localPaths = [];
|
|
2852
3057
|
for (let i = 0; i < total; i++) {
|
|
2853
3058
|
sendEvent("progress", { stage: "downloading", current: i + 1, total });
|
|
2854
|
-
const localPath =
|
|
3059
|
+
const localPath = path8.join(tmpDir, DEMO_FILES[i]);
|
|
2855
3060
|
const resp = await fetch(`${DEMO_BASE_URL}/${locale}/${DEMO_FILES[i]}`, {
|
|
2856
3061
|
signal: AbortSignal.timeout(6e4)
|
|
2857
3062
|
});
|
|
2858
3063
|
if (!resp.ok) throw new Error(`Download demo failed (${DEMO_FILES[i]}): ${resp.status}`);
|
|
2859
|
-
|
|
3064
|
+
fs7.writeFileSync(localPath, Buffer.from(await resp.arrayBuffer()));
|
|
2860
3065
|
localPaths.push(localPath);
|
|
2861
3066
|
}
|
|
2862
3067
|
sendEvent("progress", { stage: "importing", current: 1, total });
|
|
@@ -2887,7 +3092,7 @@ data: ${JSON.stringify(eventData)}
|
|
|
2887
3092
|
}
|
|
2888
3093
|
|
|
2889
3094
|
// src/http/routes/web/cache.ts
|
|
2890
|
-
import * as
|
|
3095
|
+
import * as path9 from "path";
|
|
2891
3096
|
import { execFile } from "child_process";
|
|
2892
3097
|
function runExecFile(file, args, runner = execFile) {
|
|
2893
3098
|
return new Promise((resolve4, reject) => {
|
|
@@ -2915,7 +3120,7 @@ async function showPathInFolder(filePath, platform = process.platform, runner) {
|
|
|
2915
3120
|
} else if (platform === "win32") {
|
|
2916
3121
|
await runExecFile("explorer.exe", [`/select,${filePath}`], runner);
|
|
2917
3122
|
} else {
|
|
2918
|
-
await runExecFile("xdg-open", [
|
|
3123
|
+
await runExecFile("xdg-open", [path9.dirname(filePath)], runner);
|
|
2919
3124
|
}
|
|
2920
3125
|
}
|
|
2921
3126
|
|
|
@@ -2938,27 +3143,27 @@ function registerWebRoutes(server2, dbManager2, options) {
|
|
|
2938
3143
|
const mergeCache = options?.pathProvider ? new MergeSessionCache(options.pathProvider, { nativeBinding: options.nativeBinding }) : null;
|
|
2939
3144
|
mergeCache?.cleanupOrphans();
|
|
2940
3145
|
const fallbackPathProvider = {
|
|
2941
|
-
getSystemDir: () =>
|
|
2942
|
-
getUserDataDir: () =>
|
|
2943
|
-
getDatabaseDir: () =>
|
|
2944
|
-
getVectorDir: () =>
|
|
2945
|
-
getAiDataDir: () =>
|
|
2946
|
-
getSettingsDir: () =>
|
|
2947
|
-
getCacheDir: () =>
|
|
2948
|
-
getTempDir: () =>
|
|
2949
|
-
getLogsDir: () =>
|
|
2950
|
-
getDownloadsDir: () =>
|
|
3146
|
+
getSystemDir: () => path10.join(os3.homedir(), ".chatlab"),
|
|
3147
|
+
getUserDataDir: () => path10.join(os3.homedir(), ".chatlab", "data"),
|
|
3148
|
+
getDatabaseDir: () => path10.join(os3.homedir(), ".chatlab", "data", "databases"),
|
|
3149
|
+
getVectorDir: () => path10.join(os3.homedir(), ".chatlab", "data", "vector"),
|
|
3150
|
+
getAiDataDir: () => path10.join(os3.homedir(), ".chatlab", "ai"),
|
|
3151
|
+
getSettingsDir: () => path10.join(os3.homedir(), ".chatlab", "settings"),
|
|
3152
|
+
getCacheDir: () => path10.join(os3.homedir(), ".chatlab", "cache"),
|
|
3153
|
+
getTempDir: () => path10.join(os3.homedir(), ".chatlab", "temp"),
|
|
3154
|
+
getLogsDir: () => path10.join(os3.homedir(), ".chatlab", "logs"),
|
|
3155
|
+
getDownloadsDir: () => path10.join(os3.homedir(), "Downloads")
|
|
2951
3156
|
};
|
|
2952
3157
|
const resolvedPathProvider = options?.pathProvider ?? fallbackPathProvider;
|
|
2953
3158
|
const ai = options?.aiContext;
|
|
2954
3159
|
const cliStreamImport = async (dm, filePath) => {
|
|
2955
|
-
const { streamImport: streamImport2 } = await import("./stream-import-
|
|
3160
|
+
const { streamImport: streamImport2 } = await import("./stream-import-HIE7PNL4.mjs");
|
|
2956
3161
|
const result = await streamImport2(dm, filePath);
|
|
2957
3162
|
if (!result.sessionId) throw new Error("Import succeeded but no sessionId returned");
|
|
2958
3163
|
return { sessionId: result.sessionId };
|
|
2959
3164
|
};
|
|
2960
3165
|
const defaultUserDataDir = getDefaultNodeUserDataDir();
|
|
2961
|
-
const isCustom =
|
|
3166
|
+
const isCustom = path10.resolve(resolvedPathProvider.getUserDataDir()) !== path10.resolve(defaultUserDataDir);
|
|
2962
3167
|
const semanticIndexService = options?.semanticIndexService;
|
|
2963
3168
|
const analyticsService = "A-US-0629338532" ? new AnalyticsService(resolvedPathProvider.getSystemDir(), "A-US-0629338532", getVersion()) : void 0;
|
|
2964
3169
|
registerSharedRoutes(
|
|
@@ -2972,6 +3177,7 @@ function registerWebRoutes(server2, dbManager2, options) {
|
|
|
2972
3177
|
nativeBinding: options?.nativeBinding,
|
|
2973
3178
|
semanticIndexService,
|
|
2974
3179
|
analyticsService,
|
|
3180
|
+
getCurrentAiLogPath: () => getServerAiLogger()?.getExistingLogPath() ?? null,
|
|
2975
3181
|
openDirectory: openDirectoryPath,
|
|
2976
3182
|
showInFolder: showPathInFolder,
|
|
2977
3183
|
downloadsDir: resolvedPathProvider.getDownloadsDir(),
|
|
@@ -3054,31 +3260,31 @@ function registerProxyRoutes(server2) {
|
|
|
3054
3260
|
}
|
|
3055
3261
|
|
|
3056
3262
|
// src/ai/manager-factory.ts
|
|
3057
|
-
import * as
|
|
3058
|
-
import * as
|
|
3263
|
+
import * as fs8 from "fs";
|
|
3264
|
+
import * as path11 from "path";
|
|
3059
3265
|
import { createHash, randomUUID as randomUUID2 } from "crypto";
|
|
3060
3266
|
var nodeFs = {
|
|
3061
3267
|
ensureDir(dir) {
|
|
3062
|
-
if (!
|
|
3268
|
+
if (!fs8.existsSync(dir)) fs8.mkdirSync(dir, { recursive: true });
|
|
3063
3269
|
},
|
|
3064
3270
|
listFiles(dir, ext) {
|
|
3065
|
-
if (!
|
|
3066
|
-
return
|
|
3271
|
+
if (!fs8.existsSync(dir)) return [];
|
|
3272
|
+
return fs8.readdirSync(dir).filter((f) => f.endsWith(ext));
|
|
3067
3273
|
},
|
|
3068
3274
|
readFile(filePath) {
|
|
3069
|
-
return
|
|
3275
|
+
return fs8.readFileSync(filePath, "utf-8");
|
|
3070
3276
|
},
|
|
3071
3277
|
writeFile(filePath, content) {
|
|
3072
|
-
|
|
3278
|
+
fs8.writeFileSync(filePath, content, "utf-8");
|
|
3073
3279
|
},
|
|
3074
3280
|
deleteFile(filePath) {
|
|
3075
|
-
if (
|
|
3281
|
+
if (fs8.existsSync(filePath)) fs8.unlinkSync(filePath);
|
|
3076
3282
|
},
|
|
3077
3283
|
fileExists(filePath) {
|
|
3078
|
-
return
|
|
3284
|
+
return fs8.existsSync(filePath);
|
|
3079
3285
|
},
|
|
3080
3286
|
joinPath(...parts) {
|
|
3081
|
-
return
|
|
3287
|
+
return path11.join(...parts);
|
|
3082
3288
|
}
|
|
3083
3289
|
};
|
|
3084
3290
|
var managerCache = /* @__PURE__ */ new Map();
|
|
@@ -3088,13 +3294,13 @@ function getAssistantManager(aiDataDir) {
|
|
|
3088
3294
|
cached = {
|
|
3089
3295
|
assistant: new AssistantManager({
|
|
3090
3296
|
fs: nodeFs,
|
|
3091
|
-
assistantsDir:
|
|
3297
|
+
assistantsDir: path11.join(aiDataDir, "assistants"),
|
|
3092
3298
|
builtinRawConfigs: [],
|
|
3093
3299
|
generateId: () => `custom_${randomUUID2().replace(/-/g, "").slice(0, 12)}`
|
|
3094
3300
|
}),
|
|
3095
3301
|
skill: new SkillManagerCore({
|
|
3096
3302
|
fs: nodeFs,
|
|
3097
|
-
skillsDir:
|
|
3303
|
+
skillsDir: path11.join(aiDataDir, "skills"),
|
|
3098
3304
|
builtinRawSkills: [],
|
|
3099
3305
|
contentHash: (content) => createHash("md5").update(content).digest("hex")
|
|
3100
3306
|
})
|
|
@@ -3118,14 +3324,14 @@ var noop = () => {
|
|
|
3118
3324
|
var NOOP_LOGGER = { info: noop, warn: noop, error: noop };
|
|
3119
3325
|
|
|
3120
3326
|
// ../../packages/sync/src/data-source-manager.ts
|
|
3121
|
-
import * as
|
|
3122
|
-
import * as
|
|
3327
|
+
import * as fs9 from "fs";
|
|
3328
|
+
import * as path12 from "path";
|
|
3123
3329
|
import * as crypto from "crypto";
|
|
3124
3330
|
var CONFIG_FILE = "data-sources.json";
|
|
3125
3331
|
function generateId(prefix = "ds") {
|
|
3126
3332
|
return `${prefix}_${crypto.randomBytes(6).toString("hex")}`;
|
|
3127
3333
|
}
|
|
3128
|
-
function
|
|
3334
|
+
function normalizeBaseUrl2(input) {
|
|
3129
3335
|
let url = input.trim().replace(/\/+$/, "");
|
|
3130
3336
|
if (url && !/^https?:\/\//i.test(url)) {
|
|
3131
3337
|
url = `http://${url}`;
|
|
@@ -3147,17 +3353,17 @@ var DataSourceManager = class {
|
|
|
3147
3353
|
this.logger = logger ?? NOOP_LOGGER;
|
|
3148
3354
|
}
|
|
3149
3355
|
getConfigPath() {
|
|
3150
|
-
return
|
|
3356
|
+
return path12.join(this.configDir, CONFIG_FILE);
|
|
3151
3357
|
}
|
|
3152
3358
|
ensureDir() {
|
|
3153
|
-
|
|
3359
|
+
fs9.mkdirSync(this.configDir, { recursive: true });
|
|
3154
3360
|
}
|
|
3155
3361
|
// ==================== Load / Save ====================
|
|
3156
3362
|
loadAll() {
|
|
3157
3363
|
try {
|
|
3158
3364
|
const filePath = this.getConfigPath();
|
|
3159
|
-
if (
|
|
3160
|
-
const raw =
|
|
3365
|
+
if (fs9.existsSync(filePath)) {
|
|
3366
|
+
const raw = fs9.readFileSync(filePath, "utf-8");
|
|
3161
3367
|
const parsed = JSON.parse(raw);
|
|
3162
3368
|
if (!isValidDataSourceArray(parsed)) {
|
|
3163
3369
|
this.logger.warn("[DataSource] Incompatible config format detected, returning empty.");
|
|
@@ -3176,7 +3382,7 @@ var DataSourceManager = class {
|
|
|
3176
3382
|
saveAll(sources) {
|
|
3177
3383
|
try {
|
|
3178
3384
|
this.ensureDir();
|
|
3179
|
-
|
|
3385
|
+
fs9.writeFileSync(this.getConfigPath(), JSON.stringify(sources, null, 2), "utf-8");
|
|
3180
3386
|
} catch (err) {
|
|
3181
3387
|
this.logger.error("[DataSource] Failed to save config", err);
|
|
3182
3388
|
}
|
|
@@ -3190,7 +3396,7 @@ var DataSourceManager = class {
|
|
|
3190
3396
|
const ds = {
|
|
3191
3397
|
id: generateId("src"),
|
|
3192
3398
|
name: partial.name || "",
|
|
3193
|
-
baseUrl:
|
|
3399
|
+
baseUrl: normalizeBaseUrl2(partial.baseUrl),
|
|
3194
3400
|
token: partial.token,
|
|
3195
3401
|
intervalMinutes: partial.intervalMinutes,
|
|
3196
3402
|
pullLimit: partial.pullLimit || 1e3,
|
|
@@ -3208,7 +3414,7 @@ var DataSourceManager = class {
|
|
|
3208
3414
|
if (idx === -1) return null;
|
|
3209
3415
|
const ds = sources[idx];
|
|
3210
3416
|
if (updates.name !== void 0) ds.name = updates.name;
|
|
3211
|
-
if (updates.baseUrl !== void 0) ds.baseUrl =
|
|
3417
|
+
if (updates.baseUrl !== void 0) ds.baseUrl = normalizeBaseUrl2(updates.baseUrl);
|
|
3212
3418
|
if (updates.token !== void 0) ds.token = updates.token;
|
|
3213
3419
|
if (updates.intervalMinutes !== void 0) ds.intervalMinutes = updates.intervalMinutes;
|
|
3214
3420
|
if (updates.pullLimit !== void 0) ds.pullLimit = updates.pullLimit;
|
|
@@ -3269,42 +3475,13 @@ var DataSourceManager = class {
|
|
|
3269
3475
|
}
|
|
3270
3476
|
};
|
|
3271
3477
|
|
|
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
3478
|
// ../../packages/sync/src/pull-engine.ts
|
|
3304
|
-
import * as
|
|
3479
|
+
import * as fs10 from "fs";
|
|
3305
3480
|
import * as crypto2 from "crypto";
|
|
3306
3481
|
var MAX_PAGES_PER_PULL = 5e3;
|
|
3307
3482
|
var PULL_OVERLAP_SECONDS = 60;
|
|
3483
|
+
var SOURCE_UNAVAILABLE_LOG_COOLDOWN_MS = 5 * 60 * 1e3;
|
|
3484
|
+
var SOURCE_UNAVAILABLE_ERROR_CODES = /* @__PURE__ */ new Set(["ECONNREFUSED", "ENOTFOUND", "ECONNRESET", "ETIMEDOUT", "ABORT_ERR"]);
|
|
3308
3485
|
function buildPullUrl(baseUrl, remoteSessionId, params) {
|
|
3309
3486
|
const base = `${baseUrl}/sessions/${remoteSessionId}/messages`;
|
|
3310
3487
|
const qs = ["format=chatlab"];
|
|
@@ -3321,7 +3498,7 @@ function parseSyncFromFile(filePath) {
|
|
|
3321
3498
|
try {
|
|
3322
3499
|
const isJsonl = filePath.endsWith(".jsonl");
|
|
3323
3500
|
if (isJsonl) {
|
|
3324
|
-
const content =
|
|
3501
|
+
const content = fs10.readFileSync(filePath, "utf-8");
|
|
3325
3502
|
const lines = content.trimEnd().split("\n");
|
|
3326
3503
|
for (let i = lines.length - 1; i >= Math.max(0, lines.length - 5); i--) {
|
|
3327
3504
|
try {
|
|
@@ -3340,7 +3517,7 @@ function parseSyncFromFile(filePath) {
|
|
|
3340
3517
|
}
|
|
3341
3518
|
return null;
|
|
3342
3519
|
}
|
|
3343
|
-
const raw =
|
|
3520
|
+
const raw = fs10.readFileSync(filePath, "utf-8");
|
|
3344
3521
|
const parsed = JSON.parse(raw);
|
|
3345
3522
|
if (parsed.sync && typeof parsed.sync === "object") {
|
|
3346
3523
|
const s = parsed.sync;
|
|
@@ -3354,7 +3531,7 @@ function parseSyncFromFile(filePath) {
|
|
|
3354
3531
|
function fileContainsMessages(filePath) {
|
|
3355
3532
|
try {
|
|
3356
3533
|
if (filePath.endsWith(".jsonl")) {
|
|
3357
|
-
const content =
|
|
3534
|
+
const content = fs10.readFileSync(filePath, "utf-8");
|
|
3358
3535
|
for (const line of content.split("\n")) {
|
|
3359
3536
|
const trimmed = line.trim();
|
|
3360
3537
|
if (!trimmed) continue;
|
|
@@ -3367,7 +3544,7 @@ function fileContainsMessages(filePath) {
|
|
|
3367
3544
|
}
|
|
3368
3545
|
return false;
|
|
3369
3546
|
}
|
|
3370
|
-
const raw =
|
|
3547
|
+
const raw = fs10.readFileSync(filePath, "utf-8");
|
|
3371
3548
|
const parsed = JSON.parse(raw);
|
|
3372
3549
|
return Array.isArray(parsed.messages) && parsed.messages.length > 0;
|
|
3373
3550
|
} catch {
|
|
@@ -3384,7 +3561,7 @@ function getMaxMessageTimestampFromFile(filePath) {
|
|
|
3384
3561
|
}
|
|
3385
3562
|
};
|
|
3386
3563
|
if (filePath.endsWith(".jsonl")) {
|
|
3387
|
-
const content =
|
|
3564
|
+
const content = fs10.readFileSync(filePath, "utf-8");
|
|
3388
3565
|
for (const line of content.split("\n")) {
|
|
3389
3566
|
const trimmed = line.trim();
|
|
3390
3567
|
if (!trimmed) continue;
|
|
@@ -3397,7 +3574,7 @@ function getMaxMessageTimestampFromFile(filePath) {
|
|
|
3397
3574
|
}
|
|
3398
3575
|
return maxTs;
|
|
3399
3576
|
}
|
|
3400
|
-
const raw =
|
|
3577
|
+
const raw = fs10.readFileSync(filePath, "utf-8");
|
|
3401
3578
|
const parsed = JSON.parse(raw);
|
|
3402
3579
|
if (Array.isArray(parsed.messages)) {
|
|
3403
3580
|
for (const message of parsed.messages) {
|
|
@@ -3411,10 +3588,35 @@ function getMaxMessageTimestampFromFile(filePath) {
|
|
|
3411
3588
|
}
|
|
3412
3589
|
function cleanupTempFile(filePath) {
|
|
3413
3590
|
try {
|
|
3414
|
-
if (
|
|
3591
|
+
if (fs10.existsSync(filePath)) fs10.unlinkSync(filePath);
|
|
3415
3592
|
} catch {
|
|
3416
3593
|
}
|
|
3417
3594
|
}
|
|
3595
|
+
function getErrorProp(error, prop) {
|
|
3596
|
+
return error && typeof error === "object" ? error[prop] : void 0;
|
|
3597
|
+
}
|
|
3598
|
+
function getErrorMessage(error) {
|
|
3599
|
+
return error instanceof Error ? error.message : String(error || "Pull failed");
|
|
3600
|
+
}
|
|
3601
|
+
function describeSourceUnavailableError(error) {
|
|
3602
|
+
let current = error;
|
|
3603
|
+
while (current && typeof current === "object") {
|
|
3604
|
+
const code = getErrorProp(current, "code");
|
|
3605
|
+
const name = getErrorProp(current, "name");
|
|
3606
|
+
if (typeof code === "string" && SOURCE_UNAVAILABLE_ERROR_CODES.has(code)) {
|
|
3607
|
+
const address = getErrorProp(current, "address");
|
|
3608
|
+
const port = getErrorProp(current, "port");
|
|
3609
|
+
if (typeof address === "string" && (typeof port === "number" || typeof port === "string")) {
|
|
3610
|
+
return `${code} ${address}:${port}`;
|
|
3611
|
+
}
|
|
3612
|
+
const host = getErrorProp(current, "hostname") ?? getErrorProp(current, "host");
|
|
3613
|
+
return typeof host === "string" ? `${code} ${host}` : code;
|
|
3614
|
+
}
|
|
3615
|
+
if (name === "AbortError") return "Request timeout";
|
|
3616
|
+
current = getErrorProp(current, "cause");
|
|
3617
|
+
}
|
|
3618
|
+
return null;
|
|
3619
|
+
}
|
|
3418
3620
|
var PullEngine = class {
|
|
3419
3621
|
fetcher;
|
|
3420
3622
|
importer;
|
|
@@ -3425,6 +3627,7 @@ var PullEngine = class {
|
|
|
3425
3627
|
onSessionImported;
|
|
3426
3628
|
pullingSourceIds = /* @__PURE__ */ new Set();
|
|
3427
3629
|
progressMap = /* @__PURE__ */ new Map();
|
|
3630
|
+
sourceUnavailableLogs = /* @__PURE__ */ new Map();
|
|
3428
3631
|
constructor(options) {
|
|
3429
3632
|
this.fetcher = options.fetcher;
|
|
3430
3633
|
this.importer = options.importer;
|
|
@@ -3460,7 +3663,6 @@ var PullEngine = class {
|
|
|
3460
3663
|
this.logger.info(`[Pull] Skipping "${sess.name}": import in progress`);
|
|
3461
3664
|
return { success: false, newMessageCount: 0, error: "Import in progress" };
|
|
3462
3665
|
}
|
|
3463
|
-
this.logger.info(`[Pull] Pulling "${sess.name}" from ${ds.baseUrl}`);
|
|
3464
3666
|
let totalNewMessages = 0;
|
|
3465
3667
|
let since = sess.lastPullAt;
|
|
3466
3668
|
let offset;
|
|
@@ -3477,7 +3679,7 @@ var PullEngine = class {
|
|
|
3477
3679
|
limit: ds.pullLimit
|
|
3478
3680
|
});
|
|
3479
3681
|
try {
|
|
3480
|
-
const stat2 =
|
|
3682
|
+
const stat2 = fs10.statSync(tempFile);
|
|
3481
3683
|
this.logger.info(`[Pull] "${sess.name}" page ${pageCount}: fetched ${stat2.size} bytes`);
|
|
3482
3684
|
const sync0 = parseSyncFromFile(tempFile);
|
|
3483
3685
|
if (stat2.size < 1024) {
|
|
@@ -3497,7 +3699,7 @@ var PullEngine = class {
|
|
|
3497
3699
|
offset,
|
|
3498
3700
|
limit: ds.pullLimit
|
|
3499
3701
|
});
|
|
3500
|
-
const retryStat =
|
|
3702
|
+
const retryStat = fs10.statSync(retryFile);
|
|
3501
3703
|
this.logger.info(`[Pull] "${sess.name}" retry ${ri + 1}: fetched ${retryStat.size} bytes`);
|
|
3502
3704
|
const retrySync = parseSyncFromFile(retryFile);
|
|
3503
3705
|
if (retryStat.size < 1024 && !fileContainsMessages(retryFile) && retrySync?.hasMore === false) {
|
|
@@ -3668,8 +3870,11 @@ var PullEngine = class {
|
|
|
3668
3870
|
}
|
|
3669
3871
|
return { success: true, newMessageCount: totalNewMessages };
|
|
3670
3872
|
} catch (error) {
|
|
3671
|
-
const
|
|
3672
|
-
|
|
3873
|
+
const sourceUnavailable = describeSourceUnavailableError(error);
|
|
3874
|
+
const errMsg = sourceUnavailable ?? getErrorMessage(error);
|
|
3875
|
+
if (!sourceUnavailable) {
|
|
3876
|
+
this.logger.error(`[Pull] Pull failed for "${sess.name}": ${errMsg}`);
|
|
3877
|
+
}
|
|
3673
3878
|
this.dsManager.updateSession(sourceId, sess.id, {
|
|
3674
3879
|
lastPullAt: Math.floor(Date.now() / 1e3),
|
|
3675
3880
|
lastStatus: "error",
|
|
@@ -3677,7 +3882,7 @@ var PullEngine = class {
|
|
|
3677
3882
|
});
|
|
3678
3883
|
this.notifier.onPullResult(sourceId, sess.id, "error", errMsg);
|
|
3679
3884
|
this.markProgressDone(sess.id);
|
|
3680
|
-
return { success: false, newMessageCount: 0, error: errMsg };
|
|
3885
|
+
return { success: false, newMessageCount: 0, error: errMsg, sourceUnavailable: !!sourceUnavailable };
|
|
3681
3886
|
}
|
|
3682
3887
|
}
|
|
3683
3888
|
markProgressDone(sessionId) {
|
|
@@ -3694,13 +3899,43 @@ var PullEngine = class {
|
|
|
3694
3899
|
}
|
|
3695
3900
|
this.pullingSourceIds.add(ds.id);
|
|
3696
3901
|
try {
|
|
3697
|
-
|
|
3698
|
-
|
|
3902
|
+
let sourceUnavailable = false;
|
|
3903
|
+
for (let index = 0; index < ds.sessions.length; index++) {
|
|
3904
|
+
const sess = ds.sessions[index];
|
|
3905
|
+
const result = await this.executePullSession(ds.id, ds, sess);
|
|
3906
|
+
if (result.sourceUnavailable && result.error) {
|
|
3907
|
+
sourceUnavailable = true;
|
|
3908
|
+
this.markRemainingSessionsSourceUnavailable(ds, index + 1, result.error);
|
|
3909
|
+
this.logSourceUnavailable(ds, result.error, ds.sessions.length - index);
|
|
3910
|
+
break;
|
|
3911
|
+
}
|
|
3699
3912
|
}
|
|
3913
|
+
if (!sourceUnavailable) this.sourceUnavailableLogs.delete(ds.id);
|
|
3700
3914
|
} finally {
|
|
3701
3915
|
this.pullingSourceIds.delete(ds.id);
|
|
3702
3916
|
}
|
|
3703
3917
|
}
|
|
3918
|
+
markRemainingSessionsSourceUnavailable(ds, startIndex, detail) {
|
|
3919
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
3920
|
+
for (const sess of ds.sessions.slice(startIndex)) {
|
|
3921
|
+
this.dsManager.updateSession(ds.id, sess.id, {
|
|
3922
|
+
lastPullAt: now,
|
|
3923
|
+
lastStatus: "error",
|
|
3924
|
+
lastError: detail
|
|
3925
|
+
});
|
|
3926
|
+
this.notifier.onPullResult(ds.id, sess.id, "error", detail);
|
|
3927
|
+
this.markProgressDone(sess.id);
|
|
3928
|
+
}
|
|
3929
|
+
}
|
|
3930
|
+
logSourceUnavailable(ds, detail, affectedSessions) {
|
|
3931
|
+
const now = Date.now();
|
|
3932
|
+
const previous = this.sourceUnavailableLogs.get(ds.id);
|
|
3933
|
+
if (previous && previous.detail === detail && now - previous.lastLoggedAt < SOURCE_UNAVAILABLE_LOG_COOLDOWN_MS) {
|
|
3934
|
+
return;
|
|
3935
|
+
}
|
|
3936
|
+
this.sourceUnavailableLogs.set(ds.id, { detail, lastLoggedAt: now });
|
|
3937
|
+
this.logger.warn(`[Pull] Source ${ds.baseUrl} unavailable: ${detail}, skipped ${affectedSessions} sessions`);
|
|
3938
|
+
}
|
|
3704
3939
|
async triggerPull(sourceId, sessionId) {
|
|
3705
3940
|
const ds = this.dsManager.get(sourceId);
|
|
3706
3941
|
if (!ds) return { success: false, error: "Data source not found" };
|
|
@@ -3708,6 +3943,8 @@ var PullEngine = class {
|
|
|
3708
3943
|
const sess = ds.sessions.find((s) => s.id === sessionId);
|
|
3709
3944
|
if (!sess) return { success: false, error: "Session not found" };
|
|
3710
3945
|
const result = await this.executePullSession(sourceId, ds, sess);
|
|
3946
|
+
if (result.sourceUnavailable && result.error) this.logSourceUnavailable(ds, result.error, 1);
|
|
3947
|
+
if (result.success) this.sourceUnavailableLogs.delete(ds.id);
|
|
3711
3948
|
return { success: result.success, error: result.error };
|
|
3712
3949
|
}
|
|
3713
3950
|
if (this.pullingSourceIds.has(sourceId)) {
|
|
@@ -3717,10 +3954,19 @@ var PullEngine = class {
|
|
|
3717
3954
|
this.pullingSourceIds.add(sourceId);
|
|
3718
3955
|
try {
|
|
3719
3956
|
const errors = [];
|
|
3720
|
-
|
|
3957
|
+
let sourceUnavailable = false;
|
|
3958
|
+
for (let index = 0; index < ds.sessions.length; index++) {
|
|
3959
|
+
const sess = ds.sessions[index];
|
|
3721
3960
|
const result = await this.executePullSession(sourceId, ds, sess);
|
|
3722
3961
|
if (!result.success && result.error) errors.push(`${sess.name}: ${result.error}`);
|
|
3962
|
+
if (result.sourceUnavailable && result.error) {
|
|
3963
|
+
sourceUnavailable = true;
|
|
3964
|
+
this.markRemainingSessionsSourceUnavailable(ds, index + 1, result.error);
|
|
3965
|
+
this.logSourceUnavailable(ds, result.error, ds.sessions.length - index);
|
|
3966
|
+
break;
|
|
3967
|
+
}
|
|
3723
3968
|
}
|
|
3969
|
+
if (!sourceUnavailable) this.sourceUnavailableLogs.delete(ds.id);
|
|
3724
3970
|
if (errors.length > 0) {
|
|
3725
3971
|
return { success: false, error: errors.join("; ") };
|
|
3726
3972
|
}
|
|
@@ -3801,13 +4047,13 @@ function reloadTimer(dsId, skipInitialPull = false) {
|
|
|
3801
4047
|
}
|
|
3802
4048
|
|
|
3803
4049
|
// src/sync/adapters.ts
|
|
3804
|
-
import * as
|
|
4050
|
+
import * as fs11 from "fs";
|
|
3805
4051
|
import * as os4 from "os";
|
|
3806
|
-
import * as
|
|
4052
|
+
import * as path13 from "path";
|
|
3807
4053
|
import * as crypto3 from "crypto";
|
|
3808
4054
|
function getTempFilePath(ext) {
|
|
3809
4055
|
const id = crypto3.randomBytes(8).toString("hex");
|
|
3810
|
-
return
|
|
4056
|
+
return path13.join(os4.tmpdir(), `chatlab-pull-${id}${ext}`);
|
|
3811
4057
|
}
|
|
3812
4058
|
var NodeFetcher = class {
|
|
3813
4059
|
async fetchToTempFile(baseUrl, remoteSessionId, token, params) {
|
|
@@ -3824,7 +4070,7 @@ var NodeFetcher = class {
|
|
|
3824
4070
|
const isJsonl = contentType.includes("ndjson") || contentType.includes("jsonl");
|
|
3825
4071
|
const tempFile = getTempFilePath(isJsonl ? ".jsonl" : ".json");
|
|
3826
4072
|
const buffer = Buffer.from(await response.arrayBuffer());
|
|
3827
|
-
|
|
4073
|
+
fs11.writeFileSync(tempFile, buffer);
|
|
3828
4074
|
return tempFile;
|
|
3829
4075
|
}
|
|
3830
4076
|
};
|
|
@@ -3837,7 +4083,7 @@ var DirectImporter = class {
|
|
|
3837
4083
|
}
|
|
3838
4084
|
sessionExists(sessionId) {
|
|
3839
4085
|
const dbPath = this.dbManager.getDbPath(sessionId);
|
|
3840
|
-
if (!
|
|
4086
|
+
if (!fs11.existsSync(dbPath)) return false;
|
|
3841
4087
|
try {
|
|
3842
4088
|
const db = this.dbManager.openRawSessionDatabase(sessionId, { readonly: true });
|
|
3843
4089
|
const row = db.prepare("SELECT COUNT(*) as cnt FROM sqlite_master WHERE type='table' AND name='message'").get();
|
|
@@ -3845,7 +4091,7 @@ var DirectImporter = class {
|
|
|
3845
4091
|
if (row.cnt === 0) {
|
|
3846
4092
|
this.logger.warn(`[DirectImporter] DB file exists but has no message table: ${sessionId}, removing`);
|
|
3847
4093
|
try {
|
|
3848
|
-
|
|
4094
|
+
fs11.unlinkSync(dbPath);
|
|
3849
4095
|
} catch {
|
|
3850
4096
|
}
|
|
3851
4097
|
return false;
|
|
@@ -3855,7 +4101,7 @@ var DirectImporter = class {
|
|
|
3855
4101
|
if (error instanceof DataDirCompatibilityError) throw error;
|
|
3856
4102
|
this.logger.warn(`[DirectImporter] Cannot validate DB file: ${sessionId}, removing`);
|
|
3857
4103
|
try {
|
|
3858
|
-
|
|
4104
|
+
fs11.unlinkSync(dbPath);
|
|
3859
4105
|
} catch {
|
|
3860
4106
|
}
|
|
3861
4107
|
return false;
|
|
@@ -3914,92 +4160,6 @@ var NoopNotifier = class {
|
|
|
3914
4160
|
onPullResult = noop2;
|
|
3915
4161
|
};
|
|
3916
4162
|
|
|
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
4163
|
// src/sync/index.ts
|
|
4004
4164
|
var syncLogger = {
|
|
4005
4165
|
info: (msg) => console.log(`[Sync] ${msg}`),
|
|
@@ -4030,7 +4190,16 @@ function initSync(server2, dbManager2, pathProvider, serverInfo) {
|
|
|
4030
4190
|
}
|
|
4031
4191
|
}
|
|
4032
4192
|
});
|
|
4033
|
-
registerAutomationRoutes(server2, {
|
|
4193
|
+
registerAutomationRoutes(server2, {
|
|
4194
|
+
automation: {
|
|
4195
|
+
dsManager,
|
|
4196
|
+
pullEngine,
|
|
4197
|
+
serverInfo,
|
|
4198
|
+
deleteSessionData: (sessionId) => dbManager2.deleteSessionDatabaseFiles(sessionId),
|
|
4199
|
+
reloadTimer,
|
|
4200
|
+
stopTimer
|
|
4201
|
+
}
|
|
4202
|
+
});
|
|
4034
4203
|
initScheduler({
|
|
4035
4204
|
dsManager,
|
|
4036
4205
|
pullEngine,
|
|
@@ -4051,21 +4220,21 @@ function createFileConfigStorage(aiDataDir) {
|
|
|
4051
4220
|
return {
|
|
4052
4221
|
readJson(key) {
|
|
4053
4222
|
try {
|
|
4054
|
-
return JSON.parse(
|
|
4223
|
+
return JSON.parse(fs12.readFileSync(`${aiDataDir}/${key}.json`, "utf-8"));
|
|
4055
4224
|
} catch {
|
|
4056
4225
|
return null;
|
|
4057
4226
|
}
|
|
4058
4227
|
},
|
|
4059
4228
|
writeJson(key, data) {
|
|
4060
|
-
if (!
|
|
4061
|
-
|
|
4229
|
+
if (!fs12.existsSync(aiDataDir)) fs12.mkdirSync(aiDataDir, { recursive: true });
|
|
4230
|
+
fs12.writeFileSync(`${aiDataDir}/${key}.json`, JSON.stringify(data, null, 2), "utf-8");
|
|
4062
4231
|
}
|
|
4063
4232
|
};
|
|
4064
4233
|
}
|
|
4065
4234
|
function resolveNativeBinding2() {
|
|
4066
4235
|
if (process.versions.electron) return void 0;
|
|
4067
4236
|
const nativePath = resolveCliPath("native/better_sqlite3.node");
|
|
4068
|
-
if (
|
|
4237
|
+
if (fs12.existsSync(nativePath)) return nativePath;
|
|
4069
4238
|
return void 0;
|
|
4070
4239
|
}
|
|
4071
4240
|
function ensureToken(config) {
|
|
@@ -4134,6 +4303,7 @@ async function startHttpServer(options) {
|
|
|
4134
4303
|
initAppLogger(pathProvider.getLogsDir());
|
|
4135
4304
|
initServerAiLogger(pathProvider.getLogsDir());
|
|
4136
4305
|
appLogger.info("server", `HTTP server starting on ${host}:${port}`);
|
|
4306
|
+
logNativeParserStatus();
|
|
4137
4307
|
setAuthToken(token);
|
|
4138
4308
|
setRequireAuth(!!(options?.requireAuth ?? config.api.require_auth));
|
|
4139
4309
|
server = createServer();
|
|
@@ -4172,7 +4342,7 @@ async function startHttpServer(options) {
|
|
|
4172
4342
|
}
|
|
4173
4343
|
});
|
|
4174
4344
|
initSync(server, dbManager, pathProvider, { port, host, token });
|
|
4175
|
-
if (options?.webRoot &&
|
|
4345
|
+
if (options?.webRoot && fs12.existsSync(options.webRoot)) {
|
|
4176
4346
|
registerProxyRoutes(server);
|
|
4177
4347
|
const fastifyStatic = await import("@fastify/static");
|
|
4178
4348
|
await server.register(fastifyStatic.default, {
|
|
@@ -4213,4 +4383,4 @@ export {
|
|
|
4213
4383
|
startHttpServer,
|
|
4214
4384
|
stopHttpServer
|
|
4215
4385
|
};
|
|
4216
|
-
//# sourceMappingURL=chunk-
|
|
4386
|
+
//# sourceMappingURL=chunk-D3DPCDII.mjs.map
|