@pixelbyte-software/pixcode 1.53.17 → 1.53.19
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/assets/{index-ByaiDQ05.js → index-CvIHrHIG.js} +195 -184
- package/dist/assets/index-DvudRU5X.css +32 -0
- package/dist/index.html +2 -2
- package/dist-server/server/index.js +246 -99
- package/dist-server/server/index.js.map +1 -1
- package/dist-server/server/projects.js +96 -78
- package/dist-server/server/projects.js.map +1 -1
- package/dist-server/server/routes/git.js +43 -29
- package/dist-server/server/routes/git.js.map +1 -1
- package/package.json +2 -1
- package/scripts/smoke/backend-resource-bounds.mjs +91 -0
- package/scripts/smoke/mobile-ux.mjs +28 -2
- package/server/index.js +262 -99
- package/server/projects.js +100 -76
- package/server/routes/git.js +42 -30
- package/dist/assets/index-nJQ4Q6hZ.css +0 -32
package/server/projects.js
CHANGED
|
@@ -75,6 +75,7 @@ const FILE_COUNT_SCAN_MAX_MS = 2500;
|
|
|
75
75
|
const CODEX_SESSION_INDEX_TTL_MS = 60 * 1000;
|
|
76
76
|
const CODEX_SESSION_INDEX_MAX_FILES = 1200;
|
|
77
77
|
const CODEX_SESSION_INDEX_MAX_DIRS = 2500;
|
|
78
|
+
const CLAUDE_JSONL_LINE_MAX_CHARS = 1024 * 1024;
|
|
78
79
|
const GEMINI_QWEN_PROJECT_ENTRY_CACHE_TTL_MS = 60 * 1000;
|
|
79
80
|
const CLI_CHAT_FILE_READ_MAX_BYTES = 2 * 1024 * 1024;
|
|
80
81
|
const CLI_CHAT_FILES_PER_PROJECT_LIMIT = 150;
|
|
@@ -932,11 +933,12 @@ async function getSessions(projectName, limit = 5, offset = 0) {
|
|
|
932
933
|
filesWithStats.sort((a, b) => b.mtime - a.mtime);
|
|
933
934
|
|
|
934
935
|
const allSessions = new Map();
|
|
935
|
-
const
|
|
936
|
-
const uuidToSessionMap = new Map();
|
|
936
|
+
const sessionFirstUserMessageIds = new Map();
|
|
937
937
|
|
|
938
938
|
// Collect all sessions and entries from all files
|
|
939
|
+
let filesScanned = 0;
|
|
939
940
|
for (const { file } of filesWithStats) {
|
|
941
|
+
filesScanned++;
|
|
940
942
|
const jsonlFile = path.join(projectDir, file);
|
|
941
943
|
const result = await parseJsonlSessions(jsonlFile);
|
|
942
944
|
|
|
@@ -946,35 +948,30 @@ async function getSessions(projectName, limit = 5, offset = 0) {
|
|
|
946
948
|
}
|
|
947
949
|
});
|
|
948
950
|
|
|
949
|
-
|
|
951
|
+
result.firstUserMessageIds.forEach((firstUserMsgId, sessionId) => {
|
|
952
|
+
if (!sessionFirstUserMessageIds.has(sessionId)) {
|
|
953
|
+
sessionFirstUserMessageIds.set(sessionId, firstUserMsgId);
|
|
954
|
+
}
|
|
955
|
+
});
|
|
950
956
|
|
|
951
957
|
// Early exit optimization for large projects
|
|
952
|
-
if (allSessions.size >= (limit + offset) * 2 &&
|
|
958
|
+
if (allSessions.size >= (limit + offset) * 2 && filesScanned >= Math.min(3, filesWithStats.length)) {
|
|
953
959
|
break;
|
|
954
960
|
}
|
|
955
961
|
}
|
|
956
962
|
|
|
957
|
-
// Build UUID-to-session mapping for timeline detection
|
|
958
|
-
allEntries.forEach(entry => {
|
|
959
|
-
if (entry.uuid && entry.sessionId) {
|
|
960
|
-
uuidToSessionMap.set(entry.uuid, entry.sessionId);
|
|
961
|
-
}
|
|
962
|
-
});
|
|
963
|
-
|
|
964
963
|
// Group sessions by first user message ID
|
|
965
964
|
const sessionGroups = new Map(); // firstUserMsgId -> { latestSession, allSessions[] }
|
|
966
965
|
const sessionToFirstUserMsgId = new Map(); // sessionId -> firstUserMsgId
|
|
967
966
|
|
|
968
967
|
// Find the first user message for each session
|
|
969
|
-
|
|
970
|
-
if (
|
|
971
|
-
// This is a first user message in a session (parentUuid is null)
|
|
972
|
-
const firstUserMsgId = entry.uuid;
|
|
968
|
+
sessionFirstUserMessageIds.forEach((firstUserMsgId, sessionId) => {
|
|
969
|
+
if (sessionId && firstUserMsgId) {
|
|
973
970
|
|
|
974
|
-
if (!sessionToFirstUserMsgId.has(
|
|
975
|
-
sessionToFirstUserMsgId.set(
|
|
971
|
+
if (!sessionToFirstUserMsgId.has(sessionId)) {
|
|
972
|
+
sessionToFirstUserMsgId.set(sessionId, firstUserMsgId);
|
|
976
973
|
|
|
977
|
-
const session = allSessions.get(
|
|
974
|
+
const session = allSessions.get(sessionId);
|
|
978
975
|
if (session) {
|
|
979
976
|
if (!sessionGroups.has(firstUserMsgId)) {
|
|
980
977
|
sessionGroups.set(firstUserMsgId, {
|
|
@@ -1038,7 +1035,7 @@ async function getSessions(projectName, limit = 5, offset = 0) {
|
|
|
1038
1035
|
|
|
1039
1036
|
async function parseJsonlSessions(filePath) {
|
|
1040
1037
|
const sessions = new Map();
|
|
1041
|
-
const
|
|
1038
|
+
const firstUserMessageIds = new Map();
|
|
1042
1039
|
const pendingSummaries = new Map(); // leafUuid -> summary for entries without sessionId
|
|
1043
1040
|
|
|
1044
1041
|
try {
|
|
@@ -1050,9 +1047,12 @@ async function parseJsonlSessions(filePath) {
|
|
|
1050
1047
|
|
|
1051
1048
|
for await (const line of rl) {
|
|
1052
1049
|
if (line.trim()) {
|
|
1050
|
+
if (line.length > CLAUDE_JSONL_LINE_MAX_CHARS) {
|
|
1051
|
+
continue;
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1053
1054
|
try {
|
|
1054
1055
|
const entry = JSON.parse(line);
|
|
1055
|
-
entries.push(entry);
|
|
1056
1056
|
|
|
1057
1057
|
// Handle summary entries that don't have sessionId yet
|
|
1058
1058
|
if (entry.type === 'summary' && entry.summary && !entry.sessionId && entry.leafUuid) {
|
|
@@ -1074,6 +1074,15 @@ async function parseJsonlSessions(filePath) {
|
|
|
1074
1074
|
|
|
1075
1075
|
const session = sessions.get(entry.sessionId);
|
|
1076
1076
|
|
|
1077
|
+
if (
|
|
1078
|
+
entry.type === 'user'
|
|
1079
|
+
&& entry.parentUuid === null
|
|
1080
|
+
&& entry.uuid
|
|
1081
|
+
&& !firstUserMessageIds.has(entry.sessionId)
|
|
1082
|
+
) {
|
|
1083
|
+
firstUserMessageIds.set(entry.sessionId, entry.uuid);
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1077
1086
|
// Apply pending summary if this entry has a parentUuid that matches a pending summary
|
|
1078
1087
|
if (session.summary === 'New Session' && entry.parentUuid && pendingSummaries.has(entry.parentUuid)) {
|
|
1079
1088
|
session.summary = pendingSummaries.get(entry.parentUuid);
|
|
@@ -1180,12 +1189,12 @@ async function parseJsonlSessions(filePath) {
|
|
|
1180
1189
|
|
|
1181
1190
|
return {
|
|
1182
1191
|
sessions: filteredSessions,
|
|
1183
|
-
|
|
1192
|
+
firstUserMessageIds,
|
|
1184
1193
|
};
|
|
1185
1194
|
|
|
1186
1195
|
} catch (error) {
|
|
1187
1196
|
console.error('Error reading JSONL file:', error);
|
|
1188
|
-
return { sessions: [],
|
|
1197
|
+
return { sessions: [], firstUserMessageIds: new Map() };
|
|
1189
1198
|
}
|
|
1190
1199
|
}
|
|
1191
1200
|
|
|
@@ -1249,6 +1258,9 @@ async function parseAgentTools(filePath) {
|
|
|
1249
1258
|
// Get messages for a specific session with pagination support
|
|
1250
1259
|
async function getSessionMessages(projectName, sessionId, limit = null, offset = 0) {
|
|
1251
1260
|
const projectDir = path.join(os.homedir(), '.claude', 'projects', projectName);
|
|
1261
|
+
const boundedLimit = limit === null ? null : Math.max(0, Number(limit) || 0);
|
|
1262
|
+
const boundedOffset = Math.max(0, Number(offset) || 0);
|
|
1263
|
+
const maxBufferedMessages = boundedLimit === null ? Infinity : boundedLimit + boundedOffset;
|
|
1252
1264
|
|
|
1253
1265
|
try {
|
|
1254
1266
|
const files = await fs.readdir(projectDir);
|
|
@@ -1261,6 +1273,7 @@ async function getSessionMessages(projectName, sessionId, limit = null, offset =
|
|
|
1261
1273
|
}
|
|
1262
1274
|
|
|
1263
1275
|
const messages = [];
|
|
1276
|
+
let totalMessages = 0;
|
|
1264
1277
|
// Map of agentId -> tools for subagent tool grouping
|
|
1265
1278
|
const agentToolsCache = new Map();
|
|
1266
1279
|
|
|
@@ -1276,9 +1289,17 @@ async function getSessionMessages(projectName, sessionId, limit = null, offset =
|
|
|
1276
1289
|
for await (const line of rl) {
|
|
1277
1290
|
if (line.trim()) {
|
|
1278
1291
|
try {
|
|
1292
|
+
if (line.length > CLAUDE_JSONL_LINE_MAX_CHARS) {
|
|
1293
|
+
continue;
|
|
1294
|
+
}
|
|
1295
|
+
|
|
1279
1296
|
const entry = JSON.parse(line);
|
|
1280
1297
|
if (entry.sessionId === sessionId) {
|
|
1298
|
+
totalMessages += 1;
|
|
1281
1299
|
messages.push(entry);
|
|
1300
|
+
if (messages.length > maxBufferedMessages) {
|
|
1301
|
+
messages.shift();
|
|
1302
|
+
}
|
|
1282
1303
|
}
|
|
1283
1304
|
} catch (parseError) {
|
|
1284
1305
|
// Silently skip malformed JSONL lines (common with concurrent writes)
|
|
@@ -1320,30 +1341,30 @@ async function getSessionMessages(projectName, sessionId, limit = null, offset =
|
|
|
1320
1341
|
new Date(a.timestamp || 0) - new Date(b.timestamp || 0)
|
|
1321
1342
|
);
|
|
1322
1343
|
|
|
1323
|
-
const total = sortedMessages.length;
|
|
1344
|
+
const total = boundedLimit === null ? sortedMessages.length : totalMessages;
|
|
1324
1345
|
|
|
1325
1346
|
// If no limit is specified, return all messages (backward compatibility)
|
|
1326
|
-
if (
|
|
1347
|
+
if (boundedLimit === null) {
|
|
1327
1348
|
return sortedMessages;
|
|
1328
1349
|
}
|
|
1329
1350
|
|
|
1330
1351
|
// Apply pagination - for recent messages, we need to slice from the end
|
|
1331
1352
|
// offset 0 should give us the most recent messages
|
|
1332
|
-
const startIndex = Math.max(0,
|
|
1333
|
-
const endIndex =
|
|
1353
|
+
const startIndex = Math.max(0, sortedMessages.length - boundedOffset - boundedLimit);
|
|
1354
|
+
const endIndex = sortedMessages.length - boundedOffset;
|
|
1334
1355
|
const paginatedMessages = sortedMessages.slice(startIndex, endIndex);
|
|
1335
|
-
const hasMore =
|
|
1356
|
+
const hasMore = total > boundedOffset + boundedLimit;
|
|
1336
1357
|
|
|
1337
1358
|
return {
|
|
1338
1359
|
messages: paginatedMessages,
|
|
1339
1360
|
total,
|
|
1340
1361
|
hasMore,
|
|
1341
|
-
offset,
|
|
1342
|
-
limit
|
|
1362
|
+
offset: boundedOffset,
|
|
1363
|
+
limit: boundedLimit
|
|
1343
1364
|
};
|
|
1344
1365
|
} catch (error) {
|
|
1345
1366
|
console.error(`Error reading messages for session ${sessionId}:`, error);
|
|
1346
|
-
return
|
|
1367
|
+
return boundedLimit === null ? [] : { messages: [], total: 0, hasMore: false };
|
|
1347
1368
|
}
|
|
1348
1369
|
}
|
|
1349
1370
|
|
|
@@ -1702,19 +1723,21 @@ async function findCodexJsonlFiles(dir, {
|
|
|
1702
1723
|
visitedDirs += 1;
|
|
1703
1724
|
|
|
1704
1725
|
try {
|
|
1705
|
-
const
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1726
|
+
const dirHandle = await fs.opendir(currentDir);
|
|
1727
|
+
try {
|
|
1728
|
+
for await (const entry of dirHandle) {
|
|
1729
|
+
if (files.length >= maxFiles) break;
|
|
1730
|
+
const fullPath = path.join(currentDir, entry.name);
|
|
1731
|
+
if (entry.isDirectory()) {
|
|
1732
|
+
if (visitedDirs + pendingDirs.length < maxDirs) {
|
|
1733
|
+
pendingDirs.push(fullPath);
|
|
1734
|
+
}
|
|
1735
|
+
} else if (entry.name.endsWith('.jsonl')) {
|
|
1736
|
+
files.push(fullPath);
|
|
1714
1737
|
}
|
|
1715
|
-
} else if (entry.name.endsWith('.jsonl')) {
|
|
1716
|
-
files.push(fullPath);
|
|
1717
1738
|
}
|
|
1739
|
+
} finally {
|
|
1740
|
+
await dirHandle.close().catch(() => undefined);
|
|
1718
1741
|
}
|
|
1719
1742
|
} catch {
|
|
1720
1743
|
// Skip directories we can't read
|
|
@@ -1900,29 +1923,18 @@ async function parseCodexSessionFile(filePath) {
|
|
|
1900
1923
|
|
|
1901
1924
|
// Get messages for a specific Codex session
|
|
1902
1925
|
async function getCodexSessionMessages(sessionId, limit = null, offset = 0) {
|
|
1926
|
+
const boundedLimit = limit === null ? null : Math.max(0, Number(limit) || 0);
|
|
1927
|
+
const boundedOffset = Math.max(0, Number(offset) || 0);
|
|
1928
|
+
const maxBufferedMessages = boundedLimit === null ? Infinity : boundedLimit + boundedOffset;
|
|
1929
|
+
|
|
1903
1930
|
try {
|
|
1904
1931
|
const codexSessionsDir = path.join(os.homedir(), '.codex', 'sessions');
|
|
1905
1932
|
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
const fullPath = path.join(dir, entry.name);
|
|
1912
|
-
if (entry.isDirectory()) {
|
|
1913
|
-
const found = await findSessionFile(fullPath);
|
|
1914
|
-
if (found) return found;
|
|
1915
|
-
} else if (entry.name.includes(sessionId) && entry.name.endsWith('.jsonl')) {
|
|
1916
|
-
return fullPath;
|
|
1917
|
-
}
|
|
1918
|
-
}
|
|
1919
|
-
} catch (error) {
|
|
1920
|
-
// Skip directories we can't read
|
|
1921
|
-
}
|
|
1922
|
-
return null;
|
|
1923
|
-
};
|
|
1924
|
-
|
|
1925
|
-
const sessionFilePath = await findSessionFile(codexSessionsDir);
|
|
1933
|
+
const sessionFiles = await findCodexJsonlFiles(codexSessionsDir, {
|
|
1934
|
+
maxFiles: Math.max(CODEX_SESSION_INDEX_MAX_FILES, 10000),
|
|
1935
|
+
maxDirs: CODEX_SESSION_INDEX_MAX_DIRS,
|
|
1936
|
+
});
|
|
1937
|
+
const sessionFilePath = sessionFiles.find(filePath => path.basename(filePath).includes(sessionId)) || null;
|
|
1926
1938
|
|
|
1927
1939
|
if (!sessionFilePath) {
|
|
1928
1940
|
console.warn(`Codex session file not found for session ${sessionId}`);
|
|
@@ -1930,6 +1942,14 @@ async function getCodexSessionMessages(sessionId, limit = null, offset = 0) {
|
|
|
1930
1942
|
}
|
|
1931
1943
|
|
|
1932
1944
|
const messages = [];
|
|
1945
|
+
let totalMessages = 0;
|
|
1946
|
+
const pushMessage = (message) => {
|
|
1947
|
+
totalMessages += 1;
|
|
1948
|
+
messages.push(message);
|
|
1949
|
+
if (messages.length > maxBufferedMessages) {
|
|
1950
|
+
messages.shift();
|
|
1951
|
+
}
|
|
1952
|
+
};
|
|
1933
1953
|
let tokenUsage = null;
|
|
1934
1954
|
const fileStream = fsSync.createReadStream(sessionFilePath);
|
|
1935
1955
|
const rl = readline.createInterface({
|
|
@@ -1957,6 +1977,10 @@ async function getCodexSessionMessages(sessionId, limit = null, offset = 0) {
|
|
|
1957
1977
|
for await (const line of rl) {
|
|
1958
1978
|
if (line.trim()) {
|
|
1959
1979
|
try {
|
|
1980
|
+
if (line.length > CLAUDE_JSONL_LINE_MAX_CHARS) {
|
|
1981
|
+
continue;
|
|
1982
|
+
}
|
|
1983
|
+
|
|
1960
1984
|
const entry = JSON.parse(line);
|
|
1961
1985
|
|
|
1962
1986
|
// Extract token usage from token_count events (keep latest)
|
|
@@ -1972,7 +1996,7 @@ async function getCodexSessionMessages(sessionId, limit = null, offset = 0) {
|
|
|
1972
1996
|
|
|
1973
1997
|
// Use event_msg.user_message for user-visible inputs.
|
|
1974
1998
|
if (entry.type === 'event_msg' && isVisibleCodexUserMessage(entry.payload)) {
|
|
1975
|
-
|
|
1999
|
+
pushMessage({
|
|
1976
2000
|
type: 'user',
|
|
1977
2001
|
timestamp: entry.timestamp,
|
|
1978
2002
|
message: {
|
|
@@ -1994,7 +2018,7 @@ async function getCodexSessionMessages(sessionId, limit = null, offset = 0) {
|
|
|
1994
2018
|
|
|
1995
2019
|
// Only add if there's actual content
|
|
1996
2020
|
if (textContent?.trim()) {
|
|
1997
|
-
|
|
2021
|
+
pushMessage({
|
|
1998
2022
|
type: 'assistant',
|
|
1999
2023
|
timestamp: entry.timestamp,
|
|
2000
2024
|
message: {
|
|
@@ -2011,7 +2035,7 @@ async function getCodexSessionMessages(sessionId, limit = null, offset = 0) {
|
|
|
2011
2035
|
.filter(Boolean)
|
|
2012
2036
|
.join('\n');
|
|
2013
2037
|
if (summaryText?.trim()) {
|
|
2014
|
-
|
|
2038
|
+
pushMessage({
|
|
2015
2039
|
type: 'thinking',
|
|
2016
2040
|
timestamp: entry.timestamp,
|
|
2017
2041
|
message: {
|
|
@@ -2037,7 +2061,7 @@ async function getCodexSessionMessages(sessionId, limit = null, offset = 0) {
|
|
|
2037
2061
|
}
|
|
2038
2062
|
}
|
|
2039
2063
|
|
|
2040
|
-
|
|
2064
|
+
pushMessage({
|
|
2041
2065
|
type: 'tool_use',
|
|
2042
2066
|
timestamp: entry.timestamp,
|
|
2043
2067
|
toolName: toolName,
|
|
@@ -2047,7 +2071,7 @@ async function getCodexSessionMessages(sessionId, limit = null, offset = 0) {
|
|
|
2047
2071
|
}
|
|
2048
2072
|
|
|
2049
2073
|
if (entry.type === 'response_item' && entry.payload?.type === 'function_call_output') {
|
|
2050
|
-
|
|
2074
|
+
pushMessage({
|
|
2051
2075
|
type: 'tool_result',
|
|
2052
2076
|
timestamp: entry.timestamp,
|
|
2053
2077
|
toolCallId: entry.payload.call_id,
|
|
@@ -2077,7 +2101,7 @@ async function getCodexSessionMessages(sessionId, limit = null, offset = 0) {
|
|
|
2077
2101
|
}
|
|
2078
2102
|
}
|
|
2079
2103
|
|
|
2080
|
-
|
|
2104
|
+
pushMessage({
|
|
2081
2105
|
type: 'tool_use',
|
|
2082
2106
|
timestamp: entry.timestamp,
|
|
2083
2107
|
toolName: 'Edit',
|
|
@@ -2089,7 +2113,7 @@ async function getCodexSessionMessages(sessionId, limit = null, offset = 0) {
|
|
|
2089
2113
|
toolCallId: entry.payload.call_id
|
|
2090
2114
|
});
|
|
2091
2115
|
} else {
|
|
2092
|
-
|
|
2116
|
+
pushMessage({
|
|
2093
2117
|
type: 'tool_use',
|
|
2094
2118
|
timestamp: entry.timestamp,
|
|
2095
2119
|
toolName: toolName,
|
|
@@ -2100,7 +2124,7 @@ async function getCodexSessionMessages(sessionId, limit = null, offset = 0) {
|
|
|
2100
2124
|
}
|
|
2101
2125
|
|
|
2102
2126
|
if (entry.type === 'response_item' && entry.payload?.type === 'custom_tool_call_output') {
|
|
2103
|
-
|
|
2127
|
+
pushMessage({
|
|
2104
2128
|
type: 'tool_result',
|
|
2105
2129
|
timestamp: entry.timestamp,
|
|
2106
2130
|
toolCallId: entry.payload.call_id,
|
|
@@ -2117,21 +2141,21 @@ async function getCodexSessionMessages(sessionId, limit = null, offset = 0) {
|
|
|
2117
2141
|
// Sort by timestamp
|
|
2118
2142
|
messages.sort((a, b) => new Date(a.timestamp || 0) - new Date(b.timestamp || 0));
|
|
2119
2143
|
|
|
2120
|
-
const total = messages.length;
|
|
2144
|
+
const total = boundedLimit === null ? messages.length : totalMessages;
|
|
2121
2145
|
|
|
2122
2146
|
// Apply pagination if limit is specified
|
|
2123
|
-
if (
|
|
2124
|
-
const startIndex = Math.max(0,
|
|
2125
|
-
const endIndex =
|
|
2147
|
+
if (boundedLimit !== null) {
|
|
2148
|
+
const startIndex = Math.max(0, messages.length - boundedOffset - boundedLimit);
|
|
2149
|
+
const endIndex = messages.length - boundedOffset;
|
|
2126
2150
|
const paginatedMessages = messages.slice(startIndex, endIndex);
|
|
2127
|
-
const hasMore =
|
|
2151
|
+
const hasMore = total > boundedOffset + boundedLimit;
|
|
2128
2152
|
|
|
2129
2153
|
return {
|
|
2130
2154
|
messages: paginatedMessages,
|
|
2131
2155
|
total,
|
|
2132
2156
|
hasMore,
|
|
2133
|
-
offset,
|
|
2134
|
-
limit,
|
|
2157
|
+
offset: boundedOffset,
|
|
2158
|
+
limit: boundedLimit,
|
|
2135
2159
|
tokenUsage
|
|
2136
2160
|
};
|
|
2137
2161
|
}
|
package/server/routes/git.js
CHANGED
|
@@ -29,6 +29,16 @@ const FILESYSTEM_SCAN_EXCLUDED_DIRS = new Set([
|
|
|
29
29
|
'.turbo',
|
|
30
30
|
'.cache',
|
|
31
31
|
'.pixcode-dev',
|
|
32
|
+
'.gradle',
|
|
33
|
+
'.repo',
|
|
34
|
+
'.venv',
|
|
35
|
+
'out',
|
|
36
|
+
'target',
|
|
37
|
+
'vendor',
|
|
38
|
+
'prebuilts',
|
|
39
|
+
'Pods',
|
|
40
|
+
'DerivedData',
|
|
41
|
+
'venv',
|
|
32
42
|
]);
|
|
33
43
|
|
|
34
44
|
router.use((req, res, next) => {
|
|
@@ -80,43 +90,45 @@ async function collectFilesystemSnapshot(projectPath) {
|
|
|
80
90
|
return;
|
|
81
91
|
}
|
|
82
92
|
|
|
83
|
-
let entries = [];
|
|
84
93
|
try {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
if (limitReached || shouldSkipFilesystemEntry(entry.name)) {
|
|
92
|
-
continue;
|
|
93
|
-
}
|
|
94
|
+
const dir = await fs.opendir(directoryPath);
|
|
95
|
+
try {
|
|
96
|
+
for await (const entry of dir) {
|
|
97
|
+
if (limitReached || shouldSkipFilesystemEntry(entry.name)) {
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
94
100
|
|
|
95
|
-
|
|
101
|
+
const absolutePath = path.join(directoryPath, entry.name);
|
|
96
102
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
103
|
+
if (entry.isDirectory()) {
|
|
104
|
+
await walk(absolutePath, depth + 1);
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
101
107
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
108
|
+
if (!entry.isFile()) {
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
105
111
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
112
|
+
try {
|
|
113
|
+
const stat = await fs.stat(absolutePath);
|
|
114
|
+
snapshot.set(toProjectRelativePath(projectPath, absolutePath), {
|
|
115
|
+
mtimeMs: Math.round(stat.mtimeMs),
|
|
116
|
+
size: stat.size,
|
|
117
|
+
});
|
|
118
|
+
} catch {
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
115
121
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
122
|
+
if (snapshot.size >= FILESYSTEM_SCAN_MAX_FILES) {
|
|
123
|
+
limitReached = true;
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
} finally {
|
|
128
|
+
await dir.close().catch(() => undefined);
|
|
119
129
|
}
|
|
130
|
+
} catch {
|
|
131
|
+
return;
|
|
120
132
|
}
|
|
121
133
|
}
|
|
122
134
|
|