grepmax 0.26.15 → 0.26.16
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/.claude-plugin/marketplace.json +1 -1
- package/dist/lib/daemon/daemon.js +48 -0
- package/dist/lib/store/vector-db.js +100 -1
- package/dist/lib/utils/kernel-zone.js +117 -0
- package/package.json +1 -1
- package/plugins/grepmax/.claude-plugin/plugin.json +1 -1
- package/plugins/grepmax/hooks/start.js +16 -0
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"name": "grepmax",
|
|
11
11
|
"source": "./plugins/grepmax",
|
|
12
12
|
"description": "Semantic code search for Claude Code. Automatically indexes your project and provides intelligent search capabilities.",
|
|
13
|
-
"version": "0.26.
|
|
13
|
+
"version": "0.26.16",
|
|
14
14
|
"author": {
|
|
15
15
|
"name": "Robert Owens",
|
|
16
16
|
"email": "robowens@me.com"
|
|
@@ -61,6 +61,7 @@ const store_lease_1 = require("../store/store-lease");
|
|
|
61
61
|
const vector_db_1 = require("../store/vector-db");
|
|
62
62
|
const daemon_client_1 = require("../utils/daemon-client");
|
|
63
63
|
const daemon_launcher_1 = require("../utils/daemon-launcher");
|
|
64
|
+
const kernel_zone_1 = require("../utils/kernel-zone");
|
|
64
65
|
const keyed_mutex_1 = require("../utils/keyed-mutex");
|
|
65
66
|
const log_rotate_1 = require("../utils/log-rotate");
|
|
66
67
|
const logger_1 = require("../utils/logger");
|
|
@@ -127,6 +128,8 @@ class Daemon {
|
|
|
127
128
|
this.heartbeatInterval = null;
|
|
128
129
|
this.idleInterval = null;
|
|
129
130
|
this.heartbeatTick = 0;
|
|
131
|
+
/** Rate-limits the kernel-zone warn log — see checkKernelZonePressure. */
|
|
132
|
+
this.lastZoneWarnMs = 0;
|
|
130
133
|
this.shuttingDown = false;
|
|
131
134
|
this.recycling = false;
|
|
132
135
|
// False until LanceDB + MetaCache are open. The socket starts listening early
|
|
@@ -406,6 +409,7 @@ class Daemon {
|
|
|
406
409
|
void this.mlxServerManager.checkMlxHealth();
|
|
407
410
|
this.processManager.sweepOrphanWorkers();
|
|
408
411
|
this.maybeRecycle();
|
|
412
|
+
this.checkKernelZonePressure();
|
|
409
413
|
}
|
|
410
414
|
}, HEARTBEAT_INTERVAL_MS);
|
|
411
415
|
// 10. Idle timeout (skip when disabled via env)
|
|
@@ -1698,6 +1702,50 @@ class Daemon {
|
|
|
1698
1702
|
this.recycling = true;
|
|
1699
1703
|
void this.shutdown({ relaunch: true }).finally(() => process.exit(0));
|
|
1700
1704
|
}
|
|
1705
|
+
/**
|
|
1706
|
+
* Stand down before our own write volume exhausts a leaking kernel zone.
|
|
1707
|
+
*
|
|
1708
|
+
* On macOS 26.5.2 sustained filesystem writes leak `data.kalloc.1024`
|
|
1709
|
+
* irreversibly, and exhausting it panics the host with no warning of any kind —
|
|
1710
|
+
* the daemon does not get to observe the failure, it just dies with the machine.
|
|
1711
|
+
* This host lost three sessions that way before the compactor was identified as
|
|
1712
|
+
* the largest write source on it.
|
|
1713
|
+
*
|
|
1714
|
+
* So the daemon watches the zone it is pressurizing. A warn-level reading is
|
|
1715
|
+
* logged and left alone; a critical reading exits without relaunch, because
|
|
1716
|
+
* relaunching would resume the writes that caused it. Nothing here can reclaim
|
|
1717
|
+
* the leaked memory — only a reboot does that — so the only useful move is to
|
|
1718
|
+
* stop adding to it while the user still has a working machine.
|
|
1719
|
+
*
|
|
1720
|
+
* Deliberately does nothing when the sample is unavailable or unparseable
|
|
1721
|
+
* (non-macOS, no `zprint`, changed output format): an unknown reading must never
|
|
1722
|
+
* shut the daemon down.
|
|
1723
|
+
*/
|
|
1724
|
+
checkKernelZonePressure() {
|
|
1725
|
+
if (this.shuttingDown || this.recycling)
|
|
1726
|
+
return;
|
|
1727
|
+
const usage = (0, kernel_zone_1.readKernelZoneUsage)();
|
|
1728
|
+
if (!usage || usage.pressure === "ok")
|
|
1729
|
+
return;
|
|
1730
|
+
if (usage.pressure === "warn") {
|
|
1731
|
+
// Rate-limited to once an hour: at the 5-minute heartbeat this would
|
|
1732
|
+
// otherwise print 12 identical lines an hour for as long as the condition
|
|
1733
|
+
// holds, which on a leaking host is until reboot.
|
|
1734
|
+
const now = Date.now();
|
|
1735
|
+
if (now - this.lastZoneWarnMs < 60 * 60 * 1000)
|
|
1736
|
+
return;
|
|
1737
|
+
this.lastZoneWarnMs = now;
|
|
1738
|
+
console.log(`[daemon] WARNING: ${(0, kernel_zone_1.formatZoneUsage)(usage)} — macOS kernel zone is leaking ` +
|
|
1739
|
+
`under write pressure. The daemon will stop itself at ` +
|
|
1740
|
+
`${(kernel_zone_1.ZONE_THRESHOLDS.criticalBytes / Math.pow(1024, 3)).toFixed(0)}GiB. ` +
|
|
1741
|
+
`Only a reboot reclaims this memory.`);
|
|
1742
|
+
return;
|
|
1743
|
+
}
|
|
1744
|
+
console.log(`[daemon] CRITICAL: ${(0, kernel_zone_1.formatZoneUsage)(usage)} — stopping to avoid a kernel panic. ` +
|
|
1745
|
+
`Reboot to reclaim the zone; the index will catch up on next start.`);
|
|
1746
|
+
this.recycling = true;
|
|
1747
|
+
void this.shutdown().finally(() => process.exit(0));
|
|
1748
|
+
}
|
|
1701
1749
|
shutdown(opts = {}) {
|
|
1702
1750
|
if (!this.shutdownPromise) {
|
|
1703
1751
|
this.shutdownPromise = this.performShutdown(opts);
|
|
@@ -157,6 +157,36 @@ function isMissingTableError(err) {
|
|
|
157
157
|
const TABLE_NAME = "chunks";
|
|
158
158
|
const MAINTENANCE_INTERVAL_MS = 5 * 60 * 1000;
|
|
159
159
|
const CLEAN_MAINTENANCE_INTERVAL_MS = 60 * 60 * 1000;
|
|
160
|
+
/**
|
|
161
|
+
* Floor between opportunistic full-table compactions.
|
|
162
|
+
*
|
|
163
|
+
* A compaction rewrites the whole table, so its cost scales with store size, not
|
|
164
|
+
* with how much changed. Neither opportunistic caller was rate-limited: the
|
|
165
|
+
* 5-minute maintenance tick re-optimizes whenever the write epoch moved, and
|
|
166
|
+
* `compactIfNeeded` fires whenever small fragments cross a threshold. On a store
|
|
167
|
+
* under continuous reindex pressure both are true essentially always, so the
|
|
168
|
+
* table gets rewritten every few minutes forever.
|
|
169
|
+
*
|
|
170
|
+
* That is not hypothetical. On 2026-08-16..17 this store (16 GB) took 43 full
|
|
171
|
+
* compactions in two days — an accelerating 6 → 30 → 63 → 54 per day — which a
|
|
172
|
+
* microstackshot measured as 549.76 GB of file-backed writes in 12.3 hours. On
|
|
173
|
+
* macOS 26.5.2 that write volume exhausted the `data.kalloc.1024` kernel zone and
|
|
174
|
+
* panicked the host. See docs/2026-08-04-macos-kernel-zone-panic-incident.md.
|
|
175
|
+
*
|
|
176
|
+
* The floor caps compaction at twice an hour regardless of how much churn arrives.
|
|
177
|
+
* Fragments accumulating between passes cost read performance, which is recoverable;
|
|
178
|
+
* unbounded rewrite volume was not.
|
|
179
|
+
*/
|
|
180
|
+
const COMPACTION_MIN_INTERVAL_MS = 30 * 60 * 1000;
|
|
181
|
+
/**
|
|
182
|
+
* Ceiling for the unproductive-compaction backoff.
|
|
183
|
+
*
|
|
184
|
+
* A compaction that removes no fragments and frees no bytes rewrote the table for
|
|
185
|
+
* nothing, and the next one a few minutes later will almost certainly do the same.
|
|
186
|
+
* The interval doubles on each such pass and resets the moment one does real work,
|
|
187
|
+
* so a store that cannot be improved stops paying for the attempt.
|
|
188
|
+
*/
|
|
189
|
+
const COMPACTION_MAX_INTERVAL_MS = 6 * 60 * 60 * 1000;
|
|
160
190
|
class VectorDB {
|
|
161
191
|
constructor(lancedbDir, vectorDim, suppliedLease) {
|
|
162
192
|
this.lancedbDir = lancedbDir;
|
|
@@ -174,6 +204,9 @@ class VectorDB {
|
|
|
174
204
|
/** Only an index owner may create/rebuild shared indexes — see markIndexOwner. */
|
|
175
205
|
this.indexOwner = false;
|
|
176
206
|
this.lastOptimizeDidWork = false;
|
|
207
|
+
/** Compaction rate limiter — see COMPACTION_MIN_INTERVAL_MS. */
|
|
208
|
+
this.lastCompactionMs = 0;
|
|
209
|
+
this.compactionIntervalMs = COMPACTION_MIN_INTERVAL_MS;
|
|
177
210
|
this.maintenanceRunner = (fn) => fn();
|
|
178
211
|
/** Latched when a from-scratch FTS rebuild failed to stop an optimize
|
|
179
212
|
* panic. Stops the (expensive) rebuild from re-running every maintenance
|
|
@@ -1152,13 +1185,30 @@ class VectorDB {
|
|
|
1152
1185
|
}
|
|
1153
1186
|
yield this.createFTSIndex();
|
|
1154
1187
|
yield this.createVectorIndex();
|
|
1155
|
-
|
|
1188
|
+
// Index creation above is idempotent and self-guarding, so it stays on the
|
|
1189
|
+
// 5-minute cadence. Compaction is the expensive half — it rewrites the whole
|
|
1190
|
+
// table — and gets rate-limited. Without this the tick compacts every 5
|
|
1191
|
+
// minutes for as long as writes keep arriving, which is how this store
|
|
1192
|
+
// reached 43 full rewrites in two days and panicked the host.
|
|
1193
|
+
const throttleMs = options.force
|
|
1194
|
+
? 0
|
|
1195
|
+
: this.compactionThrottleRemainingMs();
|
|
1196
|
+
if (throttleMs > 0) {
|
|
1197
|
+
(0, logger_1.debug)("vectordb", `Compaction throttled — ${Math.ceil(throttleMs / 60000)}min until next full pass ` +
|
|
1198
|
+
`(interval ${Math.round(this.compactionIntervalMs / 60000)}min)`);
|
|
1199
|
+
}
|
|
1200
|
+
else if (pressure === "low") {
|
|
1156
1201
|
(0, logger_1.log)("vectordb", `Low disk — single-pass optimize (no bloat retry)`);
|
|
1157
1202
|
yield this.optimize(1, 0, true);
|
|
1203
|
+
this.noteCompaction(this.lastOptimizeDidWork);
|
|
1158
1204
|
}
|
|
1159
1205
|
else {
|
|
1160
1206
|
// Normal maintenance: full optimize + bloat check
|
|
1161
1207
|
yield this.optimize(5, 0, true);
|
|
1208
|
+
// Track across both passes: `lastOptimizeDidWork` is reset per optimize()
|
|
1209
|
+
// call, so a productive first pass followed by a barren bloat retry would
|
|
1210
|
+
// otherwise read as unproductive and trigger a spurious backoff.
|
|
1211
|
+
let didWork = this.lastOptimizeDidWork;
|
|
1162
1212
|
if (this.lastOptimizeDidWork) {
|
|
1163
1213
|
const table = yield this.openExistingTableUnsafe();
|
|
1164
1214
|
if (table) {
|
|
@@ -1180,9 +1230,11 @@ class VectorDB {
|
|
|
1180
1230
|
(0, logger_1.log)("vectordb", `Bloat detected after optimize: ${(diskSize / 1024 / 1024).toFixed(0)}MB disk vs ${(logicalSize / 1024 / 1024).toFixed(0)}MB logical (${bloatRatio.toFixed(1)}x) — retrying`);
|
|
1181
1231
|
yield new Promise((r) => setTimeout(r, 2000));
|
|
1182
1232
|
yield this.optimize(5, 0, true);
|
|
1233
|
+
didWork = didWork || this.lastOptimizeDidWork;
|
|
1183
1234
|
}
|
|
1184
1235
|
}
|
|
1185
1236
|
}
|
|
1237
|
+
this.noteCompaction(didWork);
|
|
1186
1238
|
}
|
|
1187
1239
|
const maintainedTable = yield this.openExistingTableUnsafe();
|
|
1188
1240
|
this.maintainedTableVersion =
|
|
@@ -1195,6 +1247,47 @@ class VectorDB {
|
|
|
1195
1247
|
}
|
|
1196
1248
|
});
|
|
1197
1249
|
}
|
|
1250
|
+
/**
|
|
1251
|
+
* Milliseconds until an opportunistic full-table compaction is allowed again,
|
|
1252
|
+
* or 0 if one may run now. Forced callers (`gmax index`, `doctor --fix`) are
|
|
1253
|
+
* expected to bypass this rather than consult it.
|
|
1254
|
+
*/
|
|
1255
|
+
compactionThrottleRemainingMs(now = Date.now()) {
|
|
1256
|
+
if (this.lastCompactionMs === 0)
|
|
1257
|
+
return 0;
|
|
1258
|
+
const elapsed = now - this.lastCompactionMs;
|
|
1259
|
+
// A clock that jumped backwards would otherwise wedge compaction shut.
|
|
1260
|
+
if (elapsed < 0)
|
|
1261
|
+
return 0;
|
|
1262
|
+
return Math.max(0, this.compactionIntervalMs - elapsed);
|
|
1263
|
+
}
|
|
1264
|
+
/**
|
|
1265
|
+
* Record a completed compaction and adjust the interval. Productive passes reset
|
|
1266
|
+
* to the floor; unproductive ones double up to the ceiling so a store that cannot
|
|
1267
|
+
* be improved stops being rewritten every few minutes.
|
|
1268
|
+
*/
|
|
1269
|
+
noteCompaction(didWork) {
|
|
1270
|
+
this.lastCompactionMs = Date.now();
|
|
1271
|
+
if (didWork) {
|
|
1272
|
+
if (this.compactionIntervalMs !== COMPACTION_MIN_INTERVAL_MS) {
|
|
1273
|
+
(0, logger_1.debug)("vectordb", "Productive compaction — interval reset to floor");
|
|
1274
|
+
}
|
|
1275
|
+
this.compactionIntervalMs = COMPACTION_MIN_INTERVAL_MS;
|
|
1276
|
+
return;
|
|
1277
|
+
}
|
|
1278
|
+
const next = Math.min(this.compactionIntervalMs * 2, COMPACTION_MAX_INTERVAL_MS);
|
|
1279
|
+
if (next !== this.compactionIntervalMs) {
|
|
1280
|
+
(0, logger_1.log)("vectordb", `Compaction reclaimed nothing — backing off to ${Math.round(next / 60000)}min`);
|
|
1281
|
+
}
|
|
1282
|
+
this.compactionIntervalMs = next;
|
|
1283
|
+
}
|
|
1284
|
+
/** Test/diagnostic view of the rate limiter. */
|
|
1285
|
+
getCompactionThrottleState() {
|
|
1286
|
+
return {
|
|
1287
|
+
intervalMs: this.compactionIntervalMs,
|
|
1288
|
+
remainingMs: this.compactionThrottleRemainingMs(),
|
|
1289
|
+
};
|
|
1290
|
+
}
|
|
1198
1291
|
compactIfNeeded() {
|
|
1199
1292
|
return __awaiter(this, arguments, void 0, function* (threshold = config_1.FRAGMENT_COMPACT_THRESHOLD) {
|
|
1200
1293
|
if (this.maintenanceRunning)
|
|
@@ -1206,8 +1299,14 @@ class VectorDB {
|
|
|
1206
1299
|
const table = yield this.ensureTable();
|
|
1207
1300
|
const stats = yield table.stats();
|
|
1208
1301
|
if (stats.fragmentStats.numSmallFragments > threshold) {
|
|
1302
|
+
const throttleMs = this.compactionThrottleRemainingMs();
|
|
1303
|
+
if (throttleMs > 0) {
|
|
1304
|
+
(0, logger_1.debug)("vectordb", `Fragment threshold exceeded (${stats.fragmentStats.numSmallFragments} > ${threshold}) — throttled, ${Math.ceil(throttleMs / 60000)}min remaining`);
|
|
1305
|
+
return false;
|
|
1306
|
+
}
|
|
1209
1307
|
(0, logger_1.log)("vectordb", `Fragment threshold exceeded (${stats.fragmentStats.numSmallFragments} > ${threshold}) — compacting`);
|
|
1210
1308
|
yield this.optimize(2, 0, true);
|
|
1309
|
+
this.noteCompaction(this.lastOptimizeDidWork);
|
|
1211
1310
|
return true;
|
|
1212
1311
|
}
|
|
1213
1312
|
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ZONE_THRESHOLDS = void 0;
|
|
4
|
+
exports.classifyZonePressure = classifyZonePressure;
|
|
5
|
+
exports.parseZprintOutput = parseZprintOutput;
|
|
6
|
+
exports.readKernelZoneUsage = readKernelZoneUsage;
|
|
7
|
+
exports.formatZoneUsage = formatZoneUsage;
|
|
8
|
+
const node_child_process_1 = require("node:child_process");
|
|
9
|
+
/**
|
|
10
|
+
* macOS kernel-zone pressure guard.
|
|
11
|
+
*
|
|
12
|
+
* On macOS 26.5.2 (build 25F84) the `data.kalloc.1024` kernel zone leaks under
|
|
13
|
+
* sustained filesystem write pressure and is never reclaimed. Exhausting it panics
|
|
14
|
+
* the host outright — no jetsam, no swap warning, no chance to intervene, because
|
|
15
|
+
* the failing resource is wired kernel memory rather than anything the VM system
|
|
16
|
+
* manages.
|
|
17
|
+
*
|
|
18
|
+
* This host panicked three times that way. The third time the zone sat near 1.4 GB
|
|
19
|
+
* for twelve days, then climbed to 17.9 GB in eight hours while gmax's compactor
|
|
20
|
+
* dirtied 549.76 GB of file-backed memory. See
|
|
21
|
+
* docs/2026-08-04-macos-kernel-zone-panic-incident.md.
|
|
22
|
+
*
|
|
23
|
+
* gmax cannot fix the kernel defect, but it can decline to be the thing that
|
|
24
|
+
* detonates it. Sampling the zone is cheap (~0.2 s), so the daemon watches its own
|
|
25
|
+
* blast radius and stands down while there are still hours of headroom.
|
|
26
|
+
*/
|
|
27
|
+
/** The zone that leaks. Named explicitly so `zprint` returns a single line. */
|
|
28
|
+
const ZONE_NAME = "data.kalloc.1024";
|
|
29
|
+
/**
|
|
30
|
+
* Warn threshold.
|
|
31
|
+
*
|
|
32
|
+
* Twelve days of ordinary use held this zone between 0.67 and 1.37 GiB, so 4 GiB is
|
|
33
|
+
* roughly 3x anything a healthy host has been observed to reach. During the
|
|
34
|
+
* August 17 burst the zone crossed this about 90 minutes in — early enough that
|
|
35
|
+
* stopping then would have avoided the panic with hours to spare.
|
|
36
|
+
*/
|
|
37
|
+
const WARN_BYTES = 4 * Math.pow(1024, 3);
|
|
38
|
+
/**
|
|
39
|
+
* Critical threshold — the daemon stands down here.
|
|
40
|
+
*
|
|
41
|
+
* The observed zone map cap on this hardware is ~17.6 GiB. Eight GiB is under half
|
|
42
|
+
* of it, and at the burst rate actually measured (~2.0 GiB/hour) it still leaves
|
|
43
|
+
* roughly five hours before exhaustion. The margin is deliberately generous: the
|
|
44
|
+
* cost of standing down early is a stale index, and the cost of standing down late
|
|
45
|
+
* is an unplanned reboot.
|
|
46
|
+
*/
|
|
47
|
+
const CRITICAL_BYTES = 8 * Math.pow(1024, 3);
|
|
48
|
+
function classifyZonePressure(bytes) {
|
|
49
|
+
if (bytes >= CRITICAL_BYTES)
|
|
50
|
+
return "critical";
|
|
51
|
+
if (bytes >= WARN_BYTES)
|
|
52
|
+
return "warn";
|
|
53
|
+
return "ok";
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Parse one `zprint <zone>` report.
|
|
57
|
+
*
|
|
58
|
+
* Exported for tests; the column layout is a stable but undocumented text format,
|
|
59
|
+
* so this validates rather than trusting it:
|
|
60
|
+
*
|
|
61
|
+
* ```
|
|
62
|
+
* elem cur max cur max cur alloc alloc
|
|
63
|
+
* zone name size size size #elts #elts inuse size count
|
|
64
|
+
* data.kalloc.1024 1024 0K 0K 0 0 3416 0K 0
|
|
65
|
+
* ```
|
|
66
|
+
*
|
|
67
|
+
* The `cur size` column reads `0K` on this build, which is why the byte figure is
|
|
68
|
+
* derived from `cur inuse` x `elem size` rather than read directly.
|
|
69
|
+
*/
|
|
70
|
+
function parseZprintOutput(output, zoneName = ZONE_NAME) {
|
|
71
|
+
for (const line of output.split("\n")) {
|
|
72
|
+
const fields = line.trim().split(/\s+/);
|
|
73
|
+
if (fields[0] !== zoneName)
|
|
74
|
+
continue;
|
|
75
|
+
const elementSize = Number(fields[1]);
|
|
76
|
+
const elements = Number(fields[6]);
|
|
77
|
+
if (!Number.isInteger(elementSize) || elementSize <= 0)
|
|
78
|
+
return null;
|
|
79
|
+
if (!Number.isInteger(elements) || elements < 0)
|
|
80
|
+
return null;
|
|
81
|
+
return { elements, elementSize };
|
|
82
|
+
}
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Sample the zone. Returns null off macOS, or whenever the sample cannot be
|
|
87
|
+
* trusted — an unparseable report must read as "unknown", never as "healthy" and
|
|
88
|
+
* never as a reason to stop working.
|
|
89
|
+
*/
|
|
90
|
+
function readKernelZoneUsage(zoneName = ZONE_NAME) {
|
|
91
|
+
if (process.platform !== "darwin")
|
|
92
|
+
return null;
|
|
93
|
+
try {
|
|
94
|
+
const output = (0, node_child_process_1.execFileSync)("zprint", [zoneName], {
|
|
95
|
+
encoding: "utf-8",
|
|
96
|
+
timeout: 5000,
|
|
97
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
98
|
+
});
|
|
99
|
+
const parsed = parseZprintOutput(output, zoneName);
|
|
100
|
+
if (!parsed)
|
|
101
|
+
return null;
|
|
102
|
+
const bytes = parsed.elements * parsed.elementSize;
|
|
103
|
+
return Object.assign(Object.assign({}, parsed), { bytes, pressure: classifyZonePressure(bytes) });
|
|
104
|
+
}
|
|
105
|
+
catch (_a) {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
function formatZoneUsage(usage) {
|
|
110
|
+
const gib = (usage.bytes / Math.pow(1024, 3)).toFixed(2);
|
|
111
|
+
return `${ZONE_NAME} at ${gib}GiB (${usage.elements.toLocaleString()} elements)`;
|
|
112
|
+
}
|
|
113
|
+
exports.ZONE_THRESHOLDS = {
|
|
114
|
+
warnBytes: WARN_BYTES,
|
|
115
|
+
criticalBytes: CRITICAL_BYTES,
|
|
116
|
+
zoneName: ZONE_NAME,
|
|
117
|
+
};
|
package/package.json
CHANGED
|
@@ -78,7 +78,23 @@ function isProjectRegistered() {
|
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
+
// A kill switch for the auto-start. Create ~/.gmax/autostart-disabled (or export
|
|
82
|
+
// GMAX_NO_AUTOSTART=1) to keep sessions from reviving the daemon — needed when the
|
|
83
|
+
// host is quarantined after a kernel-zone incident and the daemon's write volume is
|
|
84
|
+
// the thing under investigation. Delete the file to restore normal behavior.
|
|
85
|
+
function isAutostartDisabled() {
|
|
86
|
+
if (process.env.GMAX_NO_AUTOSTART === "1") return true;
|
|
87
|
+
try {
|
|
88
|
+
return require("node:fs").existsSync(
|
|
89
|
+
_path.join(require("node:os").homedir(), ".gmax", "autostart-disabled"),
|
|
90
|
+
);
|
|
91
|
+
} catch {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
81
96
|
function startWatcher() {
|
|
97
|
+
if (isAutostartDisabled()) return;
|
|
82
98
|
if (!isProjectRegistered()) return;
|
|
83
99
|
try {
|
|
84
100
|
execFileSync("gmax", ["watch", "--daemon", "-b"], {
|