claudefix 2.6.2 → 2.7.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.
Files changed (2) hide show
  1. package/index.cjs +83 -0
  2. package/package.json +1 -1
package/index.cjs CHANGED
@@ -105,6 +105,85 @@ const config = {
105
105
  stripColors: process.env.CLAUDE_STRIP_COLORS !== '0', // strip by default, disable with =0
106
106
  };
107
107
 
108
+ // ============================================================================
109
+ // RESOURCE LIMITER — V8 heap cap, forced GC, CPU limiting
110
+ // ============================================================================
111
+ const os = require('os');
112
+ const fs = require('fs');
113
+ const path = require('path');
114
+
115
+ function _loadUserConfig() {
116
+ try {
117
+ const cfgPath = path.join(os.homedir(), '.claudefix.json');
118
+ if (fs.existsSync(cfgPath)) return JSON.parse(fs.readFileSync(cfgPath, 'utf8'));
119
+ } catch (_) {}
120
+ return {};
121
+ }
122
+
123
+ const _userCfg = _loadUserConfig();
124
+ const _totalMemMB = Math.floor(os.totalmem() / 1048576);
125
+ const _memPct = parseInt(process.env.CLAUDEFIX_MEM_PERCENT || '', 10) || _userCfg.memPercent || 35;
126
+ const _cpuPct = parseInt(process.env.CLAUDEFIX_CPU_PERCENT || process.env.CLAUDE_MAX_CPU || '', 10) || _userCfg.cpuPercent || 0;
127
+ const MAX_HEAP_MB = parseInt(process.env.CLAUDE_MAX_RAM || '', 10) || Math.floor(_totalMemMB * Math.min(100, Math.max(1, _memPct)) / 100);
128
+
129
+ const _resLimiter = { intervalId: null, gcId: null, cpulimitPid: null };
130
+
131
+ function installResourceLimiter() {
132
+ if (process.env.CLAUDE_RESOURCE_LIMIT === '0') return;
133
+ if (process.platform !== 'linux' && process.platform !== 'darwin') return;
134
+
135
+ // V8 heap cap + expose GC for child processes
136
+ const opts = process.env.NODE_OPTIONS || '';
137
+ if (!opts.includes('--max-old-space-size')) {
138
+ process.env.NODE_OPTIONS = (opts + ' --max-old-space-size=' + MAX_HEAP_MB).trim();
139
+ }
140
+ if (!opts.includes('--expose-gc')) {
141
+ process.env.NODE_OPTIONS = (process.env.NODE_OPTIONS + ' --expose-gc').trim();
142
+ }
143
+
144
+ // CPU limiting (only if configured > 0)
145
+ if (_cpuPct > 0 && process.platform === 'linux') {
146
+ const { execSync, spawn } = require('child_process');
147
+ const pid = process.pid;
148
+ const cores = os.cpus().length;
149
+ const cpulimitVal = Math.floor(_cpuPct * cores / 100) * 100 || _cpuPct;
150
+ try {
151
+ execSync('which cpulimit 2>/dev/null', { stdio: 'pipe' });
152
+ const proc = spawn('cpulimit', ['-p', String(pid), '-l', String(cpulimitVal), '-z'], { stdio: 'ignore', detached: true });
153
+ proc.unref();
154
+ _resLimiter.cpulimitPid = proc.pid;
155
+ } catch (_) {
156
+ try {
157
+ const niceVal = Math.max(0, Math.min(19, Math.floor(19 * (1 - _cpuPct / 100))));
158
+ execSync('renice ' + niceVal + ' -p ' + process.pid + ' 2>/dev/null', { stdio: 'pipe' });
159
+ } catch (_) {}
160
+ }
161
+ }
162
+
163
+ // Periodic forced GC (every 60s)
164
+ _resLimiter.gcId = setInterval(() => {
165
+ try { if (global.gc) global.gc(); } catch (_) {}
166
+ }, 60000);
167
+ if (_resLimiter.gcId && _resLimiter.gcId.unref) _resLimiter.gcId.unref();
168
+
169
+ // RAM monitoring (every 30s)
170
+ const warnMB = Math.floor(MAX_HEAP_MB * 0.7);
171
+ const critMB = Math.floor(MAX_HEAP_MB * 0.9);
172
+ _resLimiter.intervalId = setInterval(() => {
173
+ try {
174
+ const rssMB = Math.round(process.memoryUsage().rss / 1048576);
175
+ if (rssMB > critMB && global.gc) global.gc();
176
+ } catch (_) {}
177
+ }, 30000);
178
+ if (_resLimiter.intervalId && _resLimiter.intervalId.unref) _resLimiter.intervalId.unref();
179
+ }
180
+
181
+ function cleanupResourceLimiter() {
182
+ if (_resLimiter.intervalId) { clearInterval(_resLimiter.intervalId); _resLimiter.intervalId = null; }
183
+ if (_resLimiter.gcId) { clearInterval(_resLimiter.gcId); _resLimiter.gcId = null; }
184
+ if (_resLimiter.cpulimitPid) { try { process.kill(_resLimiter.cpulimitPid); } catch (_) {} _resLimiter.cpulimitPid = null; }
185
+ }
186
+
108
187
  // state tracking
109
188
  let renderCount = 0;
110
189
  let lastResizeTime = 0;
@@ -221,6 +300,9 @@ function install() {
221
300
  return;
222
301
  }
223
302
 
303
+ // Resource limiting — V8 heap cap, forced GC, CPU limit
304
+ installResourceLimiter();
305
+
224
306
  originalWrite = process.stdout.write.bind(process.stdout);
225
307
 
226
308
  // track stdin to know when user is typing
@@ -365,6 +447,7 @@ function setConfig(key, value) {
365
447
  function disable() {
366
448
  if (originalWrite) {
367
449
  process.stdout.write = originalWrite;
450
+ cleanupResourceLimiter();
368
451
  log('disabled');
369
452
  }
370
453
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claudefix",
3
- "version": "2.6.2",
3
+ "version": "2.7.1",
4
4
  "description": "Fixes screen glitching, blocky colors, AND MEMORY LEAKS in Claude Code CLI on Linux and macOS. All features optional via env vars. Shows config options on install. Developed by Hardwick Software Services @ https://justcalljon.pro",
5
5
  "main": "index.cjs",
6
6
  "bin": {