@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/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-BU3Z3zrh.js"></script>
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, 10, 0, true);
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
- return items.sort((a, b) => {
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);