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.
- package/index.cjs +83 -0
- 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.
|
|
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": {
|