@pixelbyte-software/pixcode 1.54.8 → 1.54.10
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-BU3Z3zrh.js → index-DWOW_Jn0.js} +1 -1
- package/dist/index.html +1 -1
- package/dist-server/server/index.js +52 -2
- package/dist-server/server/index.js.map +1 -1
- package/dist-server/server/projects.js +16 -6
- package/dist-server/server/projects.js.map +1 -1
- package/dist-server/server/routes/agent.js +10 -1
- package/dist-server/server/routes/agent.js.map +1 -1
- package/dist-server/server/sessionManager.js +5 -5
- package/dist-server/server/sessionManager.js.map +1 -1
- package/package.json +1 -1
- package/server/index.js +53 -2
- package/server/projects.js +17 -7
- package/server/routes/agent.js +12 -1
- package/server/sessionManager.js +5 -5
package/dist/index.html
CHANGED
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
|
|
36
36
|
<!-- Prevent zoom on iOS -->
|
|
37
37
|
<meta name="format-detection" content="telephone=no" />
|
|
38
|
-
<script type="module" crossorigin src="/assets/index-
|
|
38
|
+
<script type="module" crossorigin src="/assets/index-DWOW_Jn0.js"></script>
|
|
39
39
|
<link rel="modulepreload" crossorigin href="/assets/vendor-react-DB6V5Fl1.js">
|
|
40
40
|
<link rel="modulepreload" crossorigin href="/assets/vendor-codemirror-CIYNS698.js">
|
|
41
41
|
<link rel="modulepreload" crossorigin href="/assets/vendor-xterm-C7tpxJl7.js">
|
|
@@ -1108,6 +1108,10 @@ const FILE_TREE_MAX_ITEMS = Number.parseInt(process.env.PIXCODE_FILE_TREE_MAX_IT
|
|
|
1108
1108
|
const FILE_TREE_MAX_DIRECTORIES = Number.parseInt(process.env.PIXCODE_FILE_TREE_MAX_DIRECTORIES || '', 10) || 1200;
|
|
1109
1109
|
const FILE_TREE_SCAN_MAX_MS = Number.parseInt(process.env.PIXCODE_FILE_TREE_SCAN_MAX_MS || '', 10) || 4000;
|
|
1110
1110
|
const FILE_TREE_MAX_ENTRIES_PER_DIRECTORY = Number.parseInt(process.env.PIXCODE_FILE_TREE_MAX_ENTRIES_PER_DIRECTORY || '', 10) || 1500;
|
|
1111
|
+
const FILE_TREE_MAX_DEPTH_DEFAULT = 5; // was 10, reduced to avoid deep traversal on huge projects
|
|
1112
|
+
const FILE_TREE_CACHE_TTL_MS = 3000; // short-lived cache for rapid successive requests
|
|
1113
|
+
const FILE_TREE_CACHE_MAX_ENTRIES = 50;
|
|
1114
|
+
const fileTreeCache = new Map();
|
|
1111
1115
|
const FILE_TREE_EXCLUDED_ENTRY_NAMES = new Set([
|
|
1112
1116
|
'.cache',
|
|
1113
1117
|
'.git',
|
|
@@ -2964,7 +2968,7 @@ app.get('/api/projects/:projectName/files', authenticateToken, requireProjectAcc
|
|
|
2964
2968
|
catch (e) {
|
|
2965
2969
|
return res.status(404).json({ error: `Project path not found: ${actualPath}` });
|
|
2966
2970
|
}
|
|
2967
|
-
const files = await getFileTree(actualPath,
|
|
2971
|
+
const files = await getFileTree(actualPath, FILE_TREE_MAX_DEPTH_DEFAULT, 0, true);
|
|
2968
2972
|
res.json(filterFileTreeForUser(files, req.user, {
|
|
2969
2973
|
name: req.params.projectName,
|
|
2970
2974
|
projectName: req.params.projectName,
|
|
@@ -4629,10 +4633,19 @@ app.get('/api/projects/:projectName/sessions/:sessionId/token-usage', authentica
|
|
|
4629
4633
|
throw error; // Re-throw other errors to be caught by outer try-catch
|
|
4630
4634
|
}
|
|
4631
4635
|
const fileStream = fs.createReadStream(jsonlPath, { encoding: 'utf8' });
|
|
4636
|
+
// Clean up the file stream on error to avoid fd leaks
|
|
4637
|
+
const destroyStream = () => {
|
|
4638
|
+
try {
|
|
4639
|
+
fileStream.destroy();
|
|
4640
|
+
}
|
|
4641
|
+
catch { /* noop */ }
|
|
4642
|
+
};
|
|
4643
|
+
fileStream.on('error', destroyStream);
|
|
4632
4644
|
const rl = readline.createInterface({
|
|
4633
4645
|
input: fileStream,
|
|
4634
4646
|
crlfDelay: Infinity,
|
|
4635
4647
|
});
|
|
4648
|
+
rl.on('error', destroyStream);
|
|
4636
4649
|
const parsedContextWindow = parseInt(process.env.CONTEXT_WINDOW, 10);
|
|
4637
4650
|
const contextWindow = Number.isFinite(parsedContextWindow) ? parsedContextWindow : 160000;
|
|
4638
4651
|
let inputTokens = 0;
|
|
@@ -4794,6 +4807,26 @@ function permToRwx(perm) {
|
|
|
4794
4807
|
const x = perm & 1 ? 'x' : '-';
|
|
4795
4808
|
return r + w + x;
|
|
4796
4809
|
}
|
|
4810
|
+
// File tree result cache (keyed by path:depth:showHidden, TTL 3s)
|
|
4811
|
+
function readFileTreeCache(cacheKey) {
|
|
4812
|
+
const entry = fileTreeCache.get(cacheKey);
|
|
4813
|
+
if (!entry || entry.expiresAt <= Date.now()) {
|
|
4814
|
+
if (entry)
|
|
4815
|
+
fileTreeCache.delete(cacheKey);
|
|
4816
|
+
// Prune stale entries
|
|
4817
|
+
if (fileTreeCache.size > FILE_TREE_CACHE_MAX_ENTRIES) {
|
|
4818
|
+
const keys = [...fileTreeCache.keys()];
|
|
4819
|
+
for (const k of keys.slice(0, keys.length - FILE_TREE_CACHE_MAX_ENTRIES)) {
|
|
4820
|
+
fileTreeCache.delete(k);
|
|
4821
|
+
}
|
|
4822
|
+
}
|
|
4823
|
+
return undefined;
|
|
4824
|
+
}
|
|
4825
|
+
return entry.tree;
|
|
4826
|
+
}
|
|
4827
|
+
function writeFileTreeCache(cacheKey, tree) {
|
|
4828
|
+
fileTreeCache.set(cacheKey, { tree, expiresAt: Date.now() + FILE_TREE_CACHE_TTL_MS });
|
|
4829
|
+
}
|
|
4797
4830
|
function createFileTreeScanContext() {
|
|
4798
4831
|
return {
|
|
4799
4832
|
startedAt: Date.now(),
|
|
@@ -4849,6 +4882,13 @@ async function readDirectoryEntriesBounded(dirPath, context) {
|
|
|
4849
4882
|
return entries;
|
|
4850
4883
|
}
|
|
4851
4884
|
async function getFileTree(dirPath, maxDepth = 3, currentDepth = 0, showHidden = true, scanContext = null) {
|
|
4885
|
+
// Check cache on top-level call
|
|
4886
|
+
if (currentDepth === 0 && !scanContext) {
|
|
4887
|
+
const cacheKey = `${path.resolve(dirPath)}:${maxDepth}:${showHidden}`;
|
|
4888
|
+
const cached = readFileTreeCache(cacheKey);
|
|
4889
|
+
if (cached)
|
|
4890
|
+
return cached;
|
|
4891
|
+
}
|
|
4852
4892
|
const context = scanContext || createFileTreeScanContext();
|
|
4853
4893
|
const items = [];
|
|
4854
4894
|
if (!hasFileTreeBudget(context)) {
|
|
@@ -4895,12 +4935,18 @@ async function getFileTree(dirPath, maxDepth = 3, currentDepth = 0, showHidden =
|
|
|
4895
4935
|
}
|
|
4896
4936
|
items.push(item);
|
|
4897
4937
|
}
|
|
4898
|
-
|
|
4938
|
+
const sorted = items.sort((a, b) => {
|
|
4899
4939
|
if (a.type !== b.type) {
|
|
4900
4940
|
return a.type === 'directory' ? -1 : 1;
|
|
4901
4941
|
}
|
|
4902
4942
|
return a.name.localeCompare(b.name);
|
|
4903
4943
|
});
|
|
4944
|
+
// Store in cache on top-level call
|
|
4945
|
+
if (currentDepth === 0 && !scanContext) {
|
|
4946
|
+
const cacheKey = `${path.resolve(dirPath)}:${maxDepth}:${showHidden}`;
|
|
4947
|
+
writeFileTreeCache(cacheKey, sorted);
|
|
4948
|
+
}
|
|
4949
|
+
return sorted;
|
|
4904
4950
|
}
|
|
4905
4951
|
const SERVER_PORT = process.env.SERVER_PORT || 3001;
|
|
4906
4952
|
const HOST = process.env.HOST || '0.0.0.0';
|
|
@@ -5059,6 +5105,10 @@ process.on('unhandledRejection', (reason, promise) => {
|
|
|
5059
5105
|
securityLog('unhandled_rejection', {
|
|
5060
5106
|
reason: reason instanceof Error ? reason.name : String(reason).slice(0, 200),
|
|
5061
5107
|
});
|
|
5108
|
+
// Give the security log time to flush, then exit.
|
|
5109
|
+
// In Node 15+, unhandled rejections crash the process anyway, but this
|
|
5110
|
+
// ensures a clean exit that the daemon manager (systemd/pm2) can detect.
|
|
5111
|
+
setTimeout(() => process.exit(1), 100);
|
|
5062
5112
|
});
|
|
5063
5113
|
process.on('uncaughtException', (error) => {
|
|
5064
5114
|
console.error('[FATAL] Uncaught exception:', error?.message || error);
|