ultracontext 1.4.13 → 1.5.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/cli/entry.mjs +3 -3
- package/dist/cli/sdk-sync.mjs +259 -33
- package/dist/cli/sdk-sync.mjs.map +1 -1
- package/dist/{ctl-CXfNEPN8.mjs → ctl-_C5oTsoT.mjs} +2 -2
- package/dist/{ctl-CXfNEPN8.mjs.map → ctl-_C5oTsoT.mjs.map} +1 -1
- package/dist/{launcher-BMMjzr5k.mjs → launcher-374b809z.mjs} +3 -3
- package/dist/{launcher-BMMjzr5k.mjs.map → launcher-374b809z.mjs.map} +1 -1
- package/dist/{lock-5aJnda81.mjs → lock-DtnsoNoW.mjs} +2 -2
- package/dist/{lock-5aJnda81.mjs.map → lock-DtnsoNoW.mjs.map} +1 -1
- package/dist/{tui-DZ1SDOH2.mjs → tui-zO1IZH_S.mjs} +206 -24
- package/dist/tui-zO1IZH_S.mjs.map +1 -0
- package/dist/{utils-CmuIYHtm.mjs → utils-BiZUI99i.mjs} +18 -2
- package/dist/utils-BiZUI99i.mjs.map +1 -0
- package/package.json +1 -1
- package/dist/tui-DZ1SDOH2.mjs.map +0 -1
- package/dist/utils-CmuIYHtm.mjs.map +0 -1
package/dist/cli/entry.mjs
CHANGED
|
@@ -308,18 +308,18 @@ async function checkForUpdate() {
|
|
|
308
308
|
if (latest && isNewer(latest, current)) printUpdateNotice(current, latest);
|
|
309
309
|
}
|
|
310
310
|
async function launchSyncDaemon() {
|
|
311
|
-
const { launchDaemon } = await import("../launcher-
|
|
311
|
+
const { launchDaemon } = await import("../launcher-374b809z.mjs");
|
|
312
312
|
await launchDaemon({
|
|
313
313
|
entryPath: fileURLToPath(new URL("./sdk-sync.mjs", import.meta.url)),
|
|
314
314
|
diagnosticsHint: "DAEMON_VERBOSE=1 ultracontext sync"
|
|
315
315
|
});
|
|
316
316
|
}
|
|
317
317
|
async function runCtlSDK() {
|
|
318
|
-
const { runCtl } = await import("../ctl-
|
|
318
|
+
const { runCtl } = await import("../ctl-_C5oTsoT.mjs");
|
|
319
319
|
await runCtl();
|
|
320
320
|
}
|
|
321
321
|
async function launchTui() {
|
|
322
|
-
const { tuiBoot } = await import("../tui-
|
|
322
|
+
const { tuiBoot } = await import("../tui-zO1IZH_S.mjs");
|
|
323
323
|
await tuiBoot({ assetsRoot: path.resolve(__dirname, "..", "..") });
|
|
324
324
|
}
|
|
325
325
|
async function run() {
|
package/dist/cli/sdk-sync.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { a as asIso, c as extractSessionIdFromPath, d as normalizeWhitespace, f as preserveText,
|
|
1
|
+
import { a as asIso, c as extractSessionIdFromPath, d as normalizeWhitespace, f as preserveText, g as truncateString, h as toMessage, i as toInt, l as firstMessageTimestamp, m as stripIDEContextTags, n as extractProjectPathFromFile, o as coerceMessageText, p as safeJsonParse, r as sha256, s as expandHome, t as boolFromEnv, u as normalizeRole } from "../utils-BiZUI99i.mjs";
|
|
2
2
|
import { n as normalizeBootstrapMode, t as createBootstrapStateKey } from "../protocol-BI9ficcl.mjs";
|
|
3
|
-
import { n as resolveLockPath, t as acquireFileLock } from "../lock-
|
|
3
|
+
import { n as resolveLockPath, t as acquireFileLock } from "../lock-DtnsoNoW.mjs";
|
|
4
4
|
import process$1 from "node:process";
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import fs from "node:fs";
|
|
@@ -785,6 +785,124 @@ function parseOpenClawLine({ line, filePath }) {
|
|
|
785
785
|
return null;
|
|
786
786
|
}
|
|
787
787
|
|
|
788
|
+
//#endregion
|
|
789
|
+
//#region ../../packages/parsers/src/agents/cursor.mjs
|
|
790
|
+
function parseCursorLine({ line, filePath }) {
|
|
791
|
+
const parsed = safeJsonParse(line);
|
|
792
|
+
if (!parsed || typeof parsed !== "object") return null;
|
|
793
|
+
if (!parsed.type && parsed.role) parsed.type = parsed.role;
|
|
794
|
+
const result = parseClaudeCodeLine({
|
|
795
|
+
line: JSON.stringify(parsed),
|
|
796
|
+
filePath
|
|
797
|
+
});
|
|
798
|
+
if (!result) return null;
|
|
799
|
+
result.eventType = result.eventType.replace(/^claude\./, "cursor.");
|
|
800
|
+
if (result.kind === "user" && result.message) result.message = stripIDEContextTags(result.message);
|
|
801
|
+
return result;
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
//#endregion
|
|
805
|
+
//#region ../../packages/parsers/src/agents/gemini.mjs
|
|
806
|
+
const FILE_MOD_TOOLS = [
|
|
807
|
+
"write_file",
|
|
808
|
+
"edit_file",
|
|
809
|
+
"save_file",
|
|
810
|
+
"replace"
|
|
811
|
+
];
|
|
812
|
+
const FILE_PATH_KEYS = [
|
|
813
|
+
"file_path",
|
|
814
|
+
"path",
|
|
815
|
+
"filePath",
|
|
816
|
+
"filename"
|
|
817
|
+
];
|
|
818
|
+
function extractGeminiTextContent(content) {
|
|
819
|
+
if (!content) return "";
|
|
820
|
+
if (typeof content === "string") return preserveText(content);
|
|
821
|
+
if (Array.isArray(content)) {
|
|
822
|
+
const parts = [];
|
|
823
|
+
for (const item of content) {
|
|
824
|
+
if (!item || typeof item !== "object") continue;
|
|
825
|
+
if (typeof item.text === "string") {
|
|
826
|
+
const chunk = preserveText(item.text);
|
|
827
|
+
if (chunk) parts.push(chunk);
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
return parts.join("\n");
|
|
831
|
+
}
|
|
832
|
+
if (typeof content === "object" && typeof content.text === "string") return preserveText(content.text);
|
|
833
|
+
return "";
|
|
834
|
+
}
|
|
835
|
+
function formatToolCall(tc) {
|
|
836
|
+
const name = tc.name ?? "unknown";
|
|
837
|
+
const args = tc.args ?? {};
|
|
838
|
+
let filePath = "";
|
|
839
|
+
for (const key of FILE_PATH_KEYS) if (typeof args[key] === "string" && args[key]) {
|
|
840
|
+
filePath = args[key];
|
|
841
|
+
break;
|
|
842
|
+
}
|
|
843
|
+
if (FILE_MOD_TOOLS.includes(name)) {
|
|
844
|
+
const content = preserveText(args.content ?? args.file_text ?? args.new_content ?? "");
|
|
845
|
+
if (content) return `[${name}] ${filePath}\n${truncateString(content, 500)}`;
|
|
846
|
+
return `[${name}] ${filePath}`;
|
|
847
|
+
}
|
|
848
|
+
const compact = JSON.stringify(args);
|
|
849
|
+
const detail = compact.length > 500 ? compact.slice(0, 500) + "..." : compact;
|
|
850
|
+
return `[${name}]${filePath ? ` ${filePath}` : ""} ${detail}`;
|
|
851
|
+
}
|
|
852
|
+
function extractGeminiSessionId(filePath) {
|
|
853
|
+
const base = path.basename(filePath, ".json");
|
|
854
|
+
const match = base.match(/session-[\d-]+-(.+)$/);
|
|
855
|
+
if (match) return match[1];
|
|
856
|
+
return base || "unknown-session";
|
|
857
|
+
}
|
|
858
|
+
function extractGeminiTimestamp(filePath) {
|
|
859
|
+
const match = path.basename(filePath, ".json").match(/session-(\d{4})(\d{2})(\d{2})/);
|
|
860
|
+
if (match) return `${match[1]}-${match[2]}-${match[3]}T00:00:00.000Z`;
|
|
861
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
862
|
+
}
|
|
863
|
+
function parseGeminiFile({ fileContents, filePath }) {
|
|
864
|
+
const parsed = safeJsonParse(fileContents);
|
|
865
|
+
if (!parsed || typeof parsed !== "object") return [];
|
|
866
|
+
const messages = parsed.messages;
|
|
867
|
+
if (!Array.isArray(messages) || messages.length === 0) return [];
|
|
868
|
+
const sessionId = extractGeminiSessionId(filePath);
|
|
869
|
+
const fileTimestamp = extractGeminiTimestamp(filePath);
|
|
870
|
+
const events = [];
|
|
871
|
+
for (let i = 0; i < messages.length; i++) {
|
|
872
|
+
const msg = messages[i];
|
|
873
|
+
if (!msg || typeof msg !== "object") continue;
|
|
874
|
+
const type = String(msg.type ?? "").toLowerCase();
|
|
875
|
+
const isUser = type === "user";
|
|
876
|
+
if (!isUser && !(type === "gemini")) continue;
|
|
877
|
+
const text = extractGeminiTextContent(msg.content);
|
|
878
|
+
const toolCallTexts = [];
|
|
879
|
+
if (Array.isArray(msg.toolCalls)) for (const tc of msg.toolCalls) {
|
|
880
|
+
if (!tc || typeof tc !== "object") continue;
|
|
881
|
+
toolCallTexts.push(formatToolCall(tc));
|
|
882
|
+
}
|
|
883
|
+
const parts = [];
|
|
884
|
+
if (text) parts.push(text);
|
|
885
|
+
if (toolCallTexts.length) parts.push(toolCallTexts.join("\n\n"));
|
|
886
|
+
const message = parts.join("\n\n");
|
|
887
|
+
if (!message) continue;
|
|
888
|
+
events.push({
|
|
889
|
+
sessionId,
|
|
890
|
+
eventType: isUser ? "gemini.user" : "gemini.assistant",
|
|
891
|
+
kind: isUser ? "user" : "assistant",
|
|
892
|
+
timestamp: msg.timestamp ?? fileTimestamp,
|
|
893
|
+
message: toMessage(message),
|
|
894
|
+
raw: {
|
|
895
|
+
type: msg.type,
|
|
896
|
+
id: msg.id,
|
|
897
|
+
index: i,
|
|
898
|
+
hasToolCalls: toolCallTexts.length > 0,
|
|
899
|
+
toolCallCount: toolCallTexts.length
|
|
900
|
+
}
|
|
901
|
+
});
|
|
902
|
+
}
|
|
903
|
+
return events;
|
|
904
|
+
}
|
|
905
|
+
|
|
788
906
|
//#endregion
|
|
789
907
|
//#region ../../packages/parsers/src/gstack.mjs
|
|
790
908
|
function extractGstackProjectSlug(filePath) {
|
|
@@ -896,6 +1014,9 @@ async function hasLocalClaudeSession(sessionId, cwd = "", baseDir) {
|
|
|
896
1014
|
followSymbolicLinks: false
|
|
897
1015
|
})).some((filePath) => path.basename(filePath, ".jsonl") === id);
|
|
898
1016
|
}
|
|
1017
|
+
function isNativeClaudeEntry(raw) {
|
|
1018
|
+
return raw && typeof raw === "object" && "type" in raw && ("sessionId" in raw || "uuid" in raw);
|
|
1019
|
+
}
|
|
899
1020
|
async function writeClaudeSession({ sessionId, cwd, messages, baseDir }) {
|
|
900
1021
|
const runCwd = String(cwd || process.cwd());
|
|
901
1022
|
const resolvedSessionId = normalizeSessionUuid(sessionId);
|
|
@@ -913,11 +1034,21 @@ async function writeClaudeSession({ sessionId, cwd, messages, baseDir }) {
|
|
|
913
1034
|
let parentUuid = null;
|
|
914
1035
|
for (let i = 0; i < (messages?.length ?? 0); i += 1) {
|
|
915
1036
|
const message = messages[i];
|
|
1037
|
+
const raw = message?.content?.raw;
|
|
1038
|
+
if (isNativeClaudeEntry(raw)) {
|
|
1039
|
+
const entryUuid = raw.uuid || randomUUID();
|
|
1040
|
+
lines.push(JSON.stringify({
|
|
1041
|
+
...raw,
|
|
1042
|
+
parentUuid,
|
|
1043
|
+
sessionId: resolvedSessionId
|
|
1044
|
+
}));
|
|
1045
|
+
parentUuid = entryUuid;
|
|
1046
|
+
continue;
|
|
1047
|
+
}
|
|
916
1048
|
const normalizedRole = normalizeRole(message?.role);
|
|
917
1049
|
const role = normalizedRole === "assistant" ? "assistant" : normalizedRole === "user" ? "user" : "assistant";
|
|
918
|
-
const
|
|
919
|
-
if (!
|
|
920
|
-
const text = normalizedRole === "system" ? `[system] ${rawText}` : rawText;
|
|
1050
|
+
const text = extractMessageText(message, normalizedRole);
|
|
1051
|
+
if (!text) continue;
|
|
921
1052
|
const ts = asIso(message?.content?.timestamp ?? message?.metadata?.timestamp ?? new Date(new Date(firstTs).getTime() + i * 1e3).toISOString());
|
|
922
1053
|
const entryUuid = randomUUID();
|
|
923
1054
|
lines.push(JSON.stringify({
|
|
@@ -931,32 +1062,35 @@ async function writeClaudeSession({ sessionId, cwd, messages, baseDir }) {
|
|
|
931
1062
|
type: role,
|
|
932
1063
|
message: {
|
|
933
1064
|
role,
|
|
934
|
-
content:
|
|
1065
|
+
content: [{
|
|
1066
|
+
type: "text",
|
|
1067
|
+
text
|
|
1068
|
+
}]
|
|
935
1069
|
},
|
|
936
1070
|
timestamp: ts,
|
|
937
1071
|
uuid: entryUuid
|
|
938
1072
|
}));
|
|
939
1073
|
parentUuid = entryUuid;
|
|
940
1074
|
}
|
|
941
|
-
if (lines.length === 0) {
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
}
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
}
|
|
1075
|
+
if (lines.length === 0) lines.push(JSON.stringify({
|
|
1076
|
+
parentUuid: null,
|
|
1077
|
+
isSidechain: false,
|
|
1078
|
+
userType: "external",
|
|
1079
|
+
cwd: runCwd,
|
|
1080
|
+
sessionId: resolvedSessionId,
|
|
1081
|
+
version: "adapter",
|
|
1082
|
+
gitBranch: "",
|
|
1083
|
+
type: "assistant",
|
|
1084
|
+
message: {
|
|
1085
|
+
role: "assistant",
|
|
1086
|
+
content: [{
|
|
1087
|
+
type: "text",
|
|
1088
|
+
text: "[system] Session restored from UltraContext with no user/assistant messages."
|
|
1089
|
+
}]
|
|
1090
|
+
},
|
|
1091
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1092
|
+
uuid: randomUUID()
|
|
1093
|
+
}));
|
|
960
1094
|
await fs$1.writeFile(filePath, `${lines.join("\n")}\n`, "utf8");
|
|
961
1095
|
return {
|
|
962
1096
|
written: true,
|
|
@@ -974,6 +1108,17 @@ async function writeClaudeSession({ sessionId, cwd, messages, baseDir }) {
|
|
|
974
1108
|
};
|
|
975
1109
|
}
|
|
976
1110
|
}
|
|
1111
|
+
function extractMessageText(message, normalizedRole) {
|
|
1112
|
+
const raw = message?.content?.raw;
|
|
1113
|
+
if (raw?.type === "response_item" && raw?.payload?.type === "message") {
|
|
1114
|
+
const text = (raw.payload.content ?? []).map((c) => c.text ?? "").filter(Boolean).join("\n");
|
|
1115
|
+
if (text) return normalizedRole === "system" ? `[system] ${text}` : text;
|
|
1116
|
+
}
|
|
1117
|
+
if (raw?.type === "event_msg" && raw?.payload?.type === "user_message") return raw.payload.message ?? "";
|
|
1118
|
+
const msg = message?.content?.message ?? "";
|
|
1119
|
+
if (!msg) return "";
|
|
1120
|
+
return normalizedRole === "system" ? `[system] ${msg}` : msg;
|
|
1121
|
+
}
|
|
977
1122
|
|
|
978
1123
|
//#endregion
|
|
979
1124
|
//#region ../../packages/parsers/src/writers/codex.mjs
|
|
@@ -1350,7 +1495,7 @@ async function daemonBoot({ createStore, resolveDbPath }) {
|
|
|
1350
1495
|
return "";
|
|
1351
1496
|
}
|
|
1352
1497
|
function pushRecentLog(level, message, data) {
|
|
1353
|
-
let line = String(message ?? "");
|
|
1498
|
+
let line = String(message ?? "").replace(/[\r\n\t\v\f\x00-\x1f]+/g, " ").replace(/\s{2,}/g, " ");
|
|
1354
1499
|
if (line.startsWith("Appended event to session context")) line = "context append";
|
|
1355
1500
|
if (line.startsWith("Context created")) line = "Context created";
|
|
1356
1501
|
if (line.startsWith("Context created without metadata fallback")) line = "Context created (fallback)";
|
|
@@ -1561,6 +1706,20 @@ async function daemonBoot({ createStore, resolveDbPath }) {
|
|
|
1561
1706
|
globs: [openclawGlob],
|
|
1562
1707
|
parseLine: parseOpenClawLine
|
|
1563
1708
|
});
|
|
1709
|
+
const cursorGlob = expandHome(process$1.env.CURSOR_GLOB ?? "~/.cursor/projects/**/*.jsonl");
|
|
1710
|
+
if (boolFromEnv(process$1.env.INGEST_CURSOR, true)) sources.push({
|
|
1711
|
+
name: "cursor",
|
|
1712
|
+
enabled: true,
|
|
1713
|
+
globs: [cursorGlob],
|
|
1714
|
+
parseLine: parseCursorLine
|
|
1715
|
+
});
|
|
1716
|
+
const geminiGlob = expandHome(process$1.env.GEMINI_GLOB ?? "~/.gemini/tmp/*/chats/session-*.json");
|
|
1717
|
+
if (boolFromEnv(process$1.env.INGEST_GEMINI, true)) sources.push({
|
|
1718
|
+
name: "gemini",
|
|
1719
|
+
enabled: true,
|
|
1720
|
+
globs: [geminiGlob],
|
|
1721
|
+
parseFile: parseGeminiFile
|
|
1722
|
+
});
|
|
1564
1723
|
const gstackGlob = expandHome(process$1.env.GSTACK_GLOB ?? "~/.gstack/projects/**/*.jsonl");
|
|
1565
1724
|
if (boolFromEnv(process$1.env.INGEST_GSTACK, true)) sources.push({
|
|
1566
1725
|
name: "gstack",
|
|
@@ -1612,7 +1771,11 @@ async function daemonBoot({ createStore, resolveDbPath }) {
|
|
|
1612
1771
|
if (shouldStop()) break;
|
|
1613
1772
|
try {
|
|
1614
1773
|
const stat = await fs$1.stat(filePath);
|
|
1615
|
-
|
|
1774
|
+
const fileId = `${stat.dev}:${stat.ino}`;
|
|
1775
|
+
if (source.parseFile) {
|
|
1776
|
+
const contents = await fs$1.readFile(filePath, "utf8");
|
|
1777
|
+
store.setOffset(offsetStoreKey(source.name, fileId), sha256(contents));
|
|
1778
|
+
} else store.setOffset(offsetStoreKey(source.name, fileId), stat.size);
|
|
1616
1779
|
} catch {}
|
|
1617
1780
|
}
|
|
1618
1781
|
}
|
|
@@ -1783,6 +1946,18 @@ async function daemonBoot({ createStore, resolveDbPath }) {
|
|
|
1783
1946
|
started_at: sessionEvents[0].normalized.timestamp
|
|
1784
1947
|
};
|
|
1785
1948
|
if (projectPath) contextMeta.project_path = projectPath;
|
|
1949
|
+
const isRealUserEvent = (ev) => {
|
|
1950
|
+
if (ev.normalized.kind !== "user") return false;
|
|
1951
|
+
const et = ev.normalized.eventType ?? "";
|
|
1952
|
+
const msg = ev.normalized.message ?? "";
|
|
1953
|
+
if (et === "response_item.message") return false;
|
|
1954
|
+
if (msg.startsWith("A new session was started")) return false;
|
|
1955
|
+
if (msg.startsWith("[result]")) return false;
|
|
1956
|
+
if (msg.startsWith("<")) return false;
|
|
1957
|
+
return true;
|
|
1958
|
+
};
|
|
1959
|
+
const firstUserEvent = sessionEvents.find(isRealUserEvent) ?? sessionEvents.find((ev) => ev.normalized.kind === "user");
|
|
1960
|
+
if (firstUserEvent?.normalized?.message) contextMeta.title = firstUserEvent.normalized.message.replace(/[\r\n\t\v\f\x00-\x1f]+/g, " ").replace(/\s{2,}/g, " ").trim().slice(0, 120);
|
|
1786
1961
|
const ctxId = await getOrCreateContext(store, uc, sessionContextStoreKey(sourceName, sessionId), contextMeta, sourceName);
|
|
1787
1962
|
contextIds.set(sessionId, ctxId);
|
|
1788
1963
|
}
|
|
@@ -1821,12 +1996,13 @@ async function daemonBoot({ createStore, resolveDbPath }) {
|
|
|
1821
1996
|
lastSessionId: sessionId,
|
|
1822
1997
|
lastAt: Date.now()
|
|
1823
1998
|
});
|
|
1824
|
-
if (cfg.logAppends)
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1999
|
+
if (cfg.logAppends) for (const { normalized } of sessionEvents) {
|
|
2000
|
+
const msg = (normalized.message ?? "").replace(/[\r\n\t\v\f\x00-\x1f]+/g, " ").replace(/\s{2,}/g, " ").trim().slice(0, 80);
|
|
2001
|
+
log("info", `[${normalized.eventType}] ${msg}`, {
|
|
2002
|
+
source: sourceName,
|
|
2003
|
+
session_id: sessionId
|
|
2004
|
+
});
|
|
2005
|
+
}
|
|
1830
2006
|
});
|
|
1831
2007
|
}
|
|
1832
2008
|
async function readNewLines(filePath, offset) {
|
|
@@ -1881,6 +2057,56 @@ async function daemonBoot({ createStore, resolveDbPath }) {
|
|
|
1881
2057
|
bumpSourceStat(source.name, "filesScanned");
|
|
1882
2058
|
const fileId = `${stat.dev}:${stat.ino}`;
|
|
1883
2059
|
const offsetKey = offsetStoreKey(source.name, fileId);
|
|
2060
|
+
if (source.parseFile) {
|
|
2061
|
+
const fileContents = await fs$1.readFile(filePath, "utf8");
|
|
2062
|
+
const contentHash = sha256(fileContents);
|
|
2063
|
+
if (store.getOffset(offsetKey) === contentHash) return;
|
|
2064
|
+
noteSourceActivity(source.name, {
|
|
2065
|
+
lastFile: filePath,
|
|
2066
|
+
lastAt: Date.now()
|
|
2067
|
+
});
|
|
2068
|
+
const allEvents = source.parseFile({
|
|
2069
|
+
fileContents,
|
|
2070
|
+
filePath
|
|
2071
|
+
});
|
|
2072
|
+
if (!Array.isArray(allEvents) || allEvents.length === 0) return;
|
|
2073
|
+
bumpStat("linesRead", allEvents.length);
|
|
2074
|
+
bumpSourceStat(source.name, "linesRead", allEvents.length);
|
|
2075
|
+
const pendingEvents = [];
|
|
2076
|
+
for (let i = 0; i < allEvents.length; i++) {
|
|
2077
|
+
if (shouldStop()) break;
|
|
2078
|
+
const normalized = allEvents[i];
|
|
2079
|
+
if (!normalized || !normalized.sessionId) continue;
|
|
2080
|
+
if (ingestMode === "last_24h" && !isWithinLast24h(normalized.timestamp)) continue;
|
|
2081
|
+
bumpStat("parsedEvents");
|
|
2082
|
+
bumpSourceStat(source.name, "parsedEvents");
|
|
2083
|
+
noteSourceActivity(source.name, {
|
|
2084
|
+
lastEventType: normalized.eventType,
|
|
2085
|
+
lastSessionId: normalized.sessionId,
|
|
2086
|
+
lastAt: Date.now()
|
|
2087
|
+
});
|
|
2088
|
+
const eventId = sha256(`${source.name}|${cfg.host}|${cfg.userId}|${normalized.sessionId}|${fileId}|${i}`);
|
|
2089
|
+
if (!markEventSeen(store, source.name, eventId)) {
|
|
2090
|
+
bumpStat("deduped");
|
|
2091
|
+
bumpSourceStat(source.name, "deduped");
|
|
2092
|
+
continue;
|
|
2093
|
+
}
|
|
2094
|
+
pendingEvents.push({
|
|
2095
|
+
normalized,
|
|
2096
|
+
eventId,
|
|
2097
|
+
lineOffset: i
|
|
2098
|
+
});
|
|
2099
|
+
}
|
|
2100
|
+
if (pendingEvents.length > 0) await appendBulkToUltraContext({
|
|
2101
|
+
store,
|
|
2102
|
+
uc,
|
|
2103
|
+
sourceName: source.name,
|
|
2104
|
+
events: pendingEvents,
|
|
2105
|
+
filePath
|
|
2106
|
+
});
|
|
2107
|
+
store.setOffset(offsetKey, contentHash);
|
|
2108
|
+
return;
|
|
2109
|
+
}
|
|
1884
2110
|
const { lines, nextOffset } = await readNewLines(filePath, toInt(store.getOffset(offsetKey), 0));
|
|
1885
2111
|
bumpStat("linesRead", lines.length);
|
|
1886
2112
|
bumpSourceStat(source.name, "linesRead", lines.length);
|