grepmax 0.26.56 → 0.26.58
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/README.md +7 -1
- package/dist/commands/doctor.js +40 -13
- package/dist/commands/status.js +36 -12
- package/dist/commands/watch.js +22 -3
- package/dist/lib/daemon/daemon.js +214 -76
- package/dist/lib/daemon/ipc-handler.js +35 -14
- package/dist/lib/daemon/paused-reads.js +155 -0
- package/dist/lib/daemon/rows-handler.js +14 -8
- package/dist/lib/skeleton/retriever.js +3 -3
- package/dist/lib/store/vector-db.js +23 -14
- package/dist/lib/utils/daemon-launcher.js +8 -3
- package/dist/lib/utils/kernel-zone.js +6 -1
- package/dist/lib/utils/operation-coordinator.js +6 -0
- package/package.json +2 -2
- package/plugins/grepmax/.claude-plugin/plugin.json +1 -1
|
@@ -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.58",
|
|
14
14
|
"author": {
|
|
15
15
|
"name": "Robert Owens",
|
|
16
16
|
"email": "robowens@me.com"
|
package/README.md
CHANGED
|
@@ -57,7 +57,7 @@ gmax doctor # Health check
|
|
|
57
57
|
gmax doctor --fix # Repair checks; full-table maintenance is disabled
|
|
58
58
|
```
|
|
59
59
|
|
|
60
|
-
**Storage containment:** full-table compaction is disabled, including forced maintenance and doctor repair. Existing retained index copies are not reclaimed by this release. The verified prune-only recovery path is separate work; normal incremental indexing remains available when the host is healthy and not quarantined. A persistent host safety stop blocks mutation and daemon restart. Installing an update preserves existing quarantine. Speculative embedding warmup and maintenance timers are disabled. On macOS, startup, heavy queries, worker forks and MLX launches require known normal OS memory pressure and a healthy kernel-zone sample; unavailable probes
|
|
60
|
+
**Storage containment:** full-table compaction is disabled, including forced maintenance and doctor repair. Existing retained index copies are not reclaimed by this release. The verified prune-only recovery path is separate work; normal incremental indexing remains available when the host is healthy and not quarantined. A persistent host safety stop blocks mutation and normal daemon restart; an explicit read-only start can serve the retained index. Installing an update preserves existing quarantine. Speculative embedding warmup and maintenance timers are disabled. On macOS, startup, heavy queries, worker forks and MLX launches require known normal OS memory pressure and a healthy kernel-zone sample; warning or unavailable probes pause heavy work while the daemon serves bounded reads. Known critical pressure still stops the daemon.
|
|
61
61
|
|
|
62
62
|
### Core Commands
|
|
63
63
|
|
|
@@ -472,6 +472,12 @@ gmax watch restart # Restart daemon
|
|
|
472
472
|
|
|
473
473
|
`gmax doctor` reports ANN index state. `ANN: vector index not built` is normal with the default exact-search configuration.
|
|
474
474
|
|
|
475
|
+
`gmax doctor` and `gmax status` report persistent safety stops and daemon startup quarantine. `doctor` exits with code 2 when startup is blocked; `status --json` includes `daemon.startupBlockedReason`. Installing an update preserves these stops. macOS kernel-zone probes use `zprint -L` to omit the wired-memory report and kernel symbolication while retaining the same zone counters and pressure thresholds.
|
|
476
|
+
|
|
477
|
+
When heavy work is paused, the daemon keeps metadata, bounded symbol/file lookups, stored skeletons by exact file path, and keyword search available. Search uses the existing FTS index and excludes unindexed rows, loads no embedding model, returns at most 20 results, and reports keyword-only scores and retained-index freshness. Native queries have a two-second deadline and run one at a time with 48 MiB of combined Lance cache capacity. Indexing, watchers, vector/graph scans, and model launches remain paused. Missing FTS indexes return an error; paused search never rebuilds an index or falls back to a corpus scan.
|
|
478
|
+
|
|
479
|
+
To serve bounded reads under an existing startup quarantine, run `gmax watch --daemon --read-only -b`. This preserves the safety markers. `status --json` reports `daemon.service.mode` and its reason; `doctor` reports paused availability and skips native corpus diagnostics. Pausing is sticky for that daemon process. Full service requires a deliberate restart after host recovery and quarantine review. Known critical OS or kernel pressure still stops even the read-only service.
|
|
480
|
+
|
|
475
481
|
Full-table compaction and new FTS/ANN index builds are disabled in the containment release, including forced maintenance and doctor repair. Existing indexes remain readable; missing lexical indexes fall back to available retrieval. Retained copies are not recovered by this release. A separate exclusive prune-only path requires validation before deployment. Do not repeatedly force repair, use future cutoffs or manually remove index fragments.
|
|
476
482
|
|
|
477
483
|
`~/.gmax/logs/daemon.log` records containment skips and retained historical compaction outcomes, including duration, logical size, disk size before/after, free space, and bytes reclaimed. `gmax status --json` exposes the last outcome under `daemon.compaction`; its `at` timestamp identifies when it was recorded. Five-minute resource snapshots track footprint, RSS, heap, external/ArrayBuffer memory, Lance cache, workers and pending work for memory investigations. Reading status uses retained snapshots and does not scan the index directory. Logs rotate to `daemon.log.prev`.
|
package/dist/commands/doctor.js
CHANGED
|
@@ -52,6 +52,7 @@ const config_1 = require("../config");
|
|
|
52
52
|
const embedding_status_1 = require("../lib/index/embedding-status");
|
|
53
53
|
const index_config_1 = require("../lib/index/index-config");
|
|
54
54
|
const maintenance_policy_1 = require("../lib/store/maintenance-policy");
|
|
55
|
+
const autostart_1 = require("../lib/utils/autostart");
|
|
55
56
|
const doctor_optimize_1 = require("../lib/utils/doctor-optimize");
|
|
56
57
|
const doctor_status_1 = require("../lib/utils/doctor-status");
|
|
57
58
|
const exit_1 = require("../lib/utils/exit");
|
|
@@ -346,7 +347,7 @@ exports.doctor = new commander_1.Command("doctor")
|
|
|
346
347
|
.option("--agent", "Compact output for AI agents", false)
|
|
347
348
|
.action((opts) => __awaiter(void 0, void 0, void 0, function* () {
|
|
348
349
|
var _a, _b;
|
|
349
|
-
var _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
|
|
350
|
+
var _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
|
|
350
351
|
if (opts.fix) {
|
|
351
352
|
const denied = (0, maintenance_policy_1.storeMutationDeniedReason)();
|
|
352
353
|
if (denied !== null) {
|
|
@@ -357,6 +358,32 @@ exports.doctor = new commander_1.Command("doctor")
|
|
|
357
358
|
}
|
|
358
359
|
if (!opts.agent)
|
|
359
360
|
console.log("gmax Doctor\n");
|
|
361
|
+
const startupBlockedReason = (0, autostart_1.daemonStartDeniedReason)();
|
|
362
|
+
if (startupBlockedReason !== null) {
|
|
363
|
+
const reason = startupBlockedReason.replace(/[\r\n\t]/g, " ");
|
|
364
|
+
console.log(opts.agent
|
|
365
|
+
? `daemon_startup\tblocked=true\treason=${reason}`
|
|
366
|
+
: `FAIL Daemon startup blocked: ${reason}`);
|
|
367
|
+
// Inspect the retained index, but do not report a healthy installation
|
|
368
|
+
// while the persistent host stop or autostart quarantine prevents use.
|
|
369
|
+
process.exitCode = 2;
|
|
370
|
+
}
|
|
371
|
+
// Metadata-only diagnostics while paused: never open a second native
|
|
372
|
+
// reader or scan the retained corpus on a pressured host.
|
|
373
|
+
const { sendDaemonCommand } = yield Promise.resolve().then(() => __importStar(require("../lib/utils/daemon-client")));
|
|
374
|
+
const daemonStatus = yield sendDaemonCommand({ cmd: "status" });
|
|
375
|
+
const service = daemonStatus.service;
|
|
376
|
+
if (daemonStatus.ok && (service === null || service === void 0 ? void 0 : service.mode) === "paused") {
|
|
377
|
+
const reason = String((_c = service.reason) !== null && _c !== void 0 ? _c : "read-only service requested").replace(/[\r\n\t]/g, " ");
|
|
378
|
+
console.log(opts.agent
|
|
379
|
+
? `daemon_service\tmode=paused\treads=bounded\treason=${reason}`
|
|
380
|
+
: `WARN Daemon available for bounded reads; indexing and embeddings paused: ${reason}`);
|
|
381
|
+
console.log(opts.agent
|
|
382
|
+
? "index_diagnostics\tskipped=paused_service"
|
|
383
|
+
: "INFO Native index diagnostics skipped while heavy work is paused");
|
|
384
|
+
process.exitCode = 2;
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
360
387
|
const root = config_1.PATHS.globalRoot;
|
|
361
388
|
const models = config_1.PATHS.models;
|
|
362
389
|
const grammars = config_1.PATHS.grammars;
|
|
@@ -371,12 +398,12 @@ exports.doctor = new commander_1.Command("doctor")
|
|
|
371
398
|
checkDir("Grammars", grammars);
|
|
372
399
|
}
|
|
373
400
|
const globalConfig = (0, index_config_1.readGlobalConfig)();
|
|
374
|
-
const tier = (
|
|
401
|
+
const tier = (_d = config_1.MODEL_TIERS[globalConfig.modelTier]) !== null && _d !== void 0 ? _d : config_1.MODEL_TIERS.small;
|
|
375
402
|
if (!config_1.MODEL_TIERS[globalConfig.modelTier]) {
|
|
376
403
|
console.log(`WARN Unknown model tier '${globalConfig.modelTier}', falling back to 'small'`);
|
|
377
404
|
}
|
|
378
405
|
const embedModel = globalConfig.embedMode === "gpu"
|
|
379
|
-
? ((
|
|
406
|
+
? ((_e = globalConfig.mlxModel) !== null && _e !== void 0 ? _e : tier.mlxModel)
|
|
380
407
|
: tier.onnxModel;
|
|
381
408
|
if (!opts.agent) {
|
|
382
409
|
console.log(`\nEmbed mode: ${globalConfig.embedMode} | Model tier: ${globalConfig.modelTier} (${tier.vectorDim}d)`);
|
|
@@ -437,7 +464,7 @@ exports.doctor = new commander_1.Command("doctor")
|
|
|
437
464
|
body: JSON.stringify({ texts: ["gmax health check"] }),
|
|
438
465
|
});
|
|
439
466
|
const embedData = yield embedRes.json();
|
|
440
|
-
const dim = (
|
|
467
|
+
const dim = (_f = (_b = (_a = embedData === null || embedData === void 0 ? void 0 : embedData.vectors) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.length) !== null && _f !== void 0 ? _f : 0;
|
|
441
468
|
const ms = Date.now() - start;
|
|
442
469
|
const expectedDim = tier.vectorDim || 384;
|
|
443
470
|
if (dim === expectedDim) {
|
|
@@ -464,7 +491,7 @@ exports.doctor = new commander_1.Command("doctor")
|
|
|
464
491
|
// caller most likely to be inside the sandbox this check is about.
|
|
465
492
|
const sandbox = checkClaudeSandbox({
|
|
466
493
|
home: os.homedir(),
|
|
467
|
-
projectRoot: (
|
|
494
|
+
projectRoot: (_g = (0, project_root_1.findProjectRoot)(process.cwd())) !== null && _g !== void 0 ? _g : process.cwd(),
|
|
468
495
|
});
|
|
469
496
|
if (sandbox) {
|
|
470
497
|
if (opts.agent) {
|
|
@@ -583,7 +610,7 @@ exports.doctor = new commander_1.Command("doctor")
|
|
|
583
610
|
? "LOW"
|
|
584
611
|
: "ok";
|
|
585
612
|
}
|
|
586
|
-
catch (
|
|
613
|
+
catch (_q) { }
|
|
587
614
|
const staleChunkerProjects = projects.filter((p) => { var _a; return p.status === "indexed" &&
|
|
588
615
|
((_a = p.chunkerVersion) !== null && _a !== void 0 ? _a : 1) < config_1.CONFIG.CHUNKER_VERSION; });
|
|
589
616
|
const staleEmbeddingProjects = projects.filter((p) => p.status === "indexed" &&
|
|
@@ -612,9 +639,9 @@ exports.doctor = new commander_1.Command("doctor")
|
|
|
612
639
|
`schema_dim=${physicalDim !== null && physicalDim !== void 0 ? physicalDim : "none"}`,
|
|
613
640
|
`schema_dim_ok=${schemaGap ? "false" : "true"}`,
|
|
614
641
|
`ann_index=${vectorIndex ? "present" : "absent"}`,
|
|
615
|
-
`ann_indexed=${(
|
|
616
|
-
`ann_unindexed=${(
|
|
617
|
-
`ann_metric=${(
|
|
642
|
+
`ann_indexed=${(_h = vectorIndexStats === null || vectorIndexStats === void 0 ? void 0 : vectorIndexStats.numIndexedRows) !== null && _h !== void 0 ? _h : 0}`,
|
|
643
|
+
`ann_unindexed=${(_j = vectorIndexStats === null || vectorIndexStats === void 0 ? void 0 : vectorIndexStats.numUnindexedRows) !== null && _j !== void 0 ? _j : 0}`,
|
|
644
|
+
`ann_metric=${(_k = vectorIndexStats === null || vectorIndexStats === void 0 ? void 0 : vectorIndexStats.distanceType) !== null && _k !== void 0 ? _k : "none"}`,
|
|
618
645
|
];
|
|
619
646
|
console.log(fields.join("\t"));
|
|
620
647
|
if (schemaGap) {
|
|
@@ -669,7 +696,7 @@ exports.doctor = new commander_1.Command("doctor")
|
|
|
669
696
|
console.log(`ok Schema: vector table is ${physicalDim}d`);
|
|
670
697
|
}
|
|
671
698
|
if (vectorIndexStats) {
|
|
672
|
-
console.log(`ok ANN: ${vectorIndexStats.numIndexedRows.toLocaleString()} indexed, ${vectorIndexStats.numUnindexedRows.toLocaleString()} unindexed, metric=${(
|
|
699
|
+
console.log(`ok ANN: ${vectorIndexStats.numIndexedRows.toLocaleString()} indexed, ${vectorIndexStats.numUnindexedRows.toLocaleString()} unindexed, metric=${(_l = vectorIndexStats.distanceType) !== null && _l !== void 0 ? _l : "unknown"}`);
|
|
673
700
|
}
|
|
674
701
|
else {
|
|
675
702
|
console.log("INFO ANN: vector index not built (set GMAX_ANN=1)");
|
|
@@ -798,7 +825,7 @@ exports.doctor = new commander_1.Command("doctor")
|
|
|
798
825
|
yield mc.close();
|
|
799
826
|
}
|
|
800
827
|
}
|
|
801
|
-
catch (
|
|
828
|
+
catch (_r) { }
|
|
802
829
|
}
|
|
803
830
|
}
|
|
804
831
|
// --fix auto-remediation
|
|
@@ -876,7 +903,7 @@ exports.doctor = new commander_1.Command("doctor")
|
|
|
876
903
|
const done = yield sendStreamingCommand({ cmd: "index", root: p.root, reset: true }, () => { }).catch((e) => ({ ok: false, error: String(e) }));
|
|
877
904
|
if (done.ok &&
|
|
878
905
|
(!("degraded" in done) || done.degraded !== true)) {
|
|
879
|
-
const chunks = (
|
|
906
|
+
const chunks = (_m = done.indexed) !== null && _m !== void 0 ? _m : 0;
|
|
880
907
|
if (opts.agent) {
|
|
881
908
|
console.log(`stale_chunker_reindexed\tname=${name}\tchunks=${chunks}`);
|
|
882
909
|
}
|
|
@@ -888,7 +915,7 @@ exports.doctor = new commander_1.Command("doctor")
|
|
|
888
915
|
else if (!opts.agent) {
|
|
889
916
|
const degraded = "degraded" in done && done.degraded === true;
|
|
890
917
|
const reason = degraded
|
|
891
|
-
? `${(
|
|
918
|
+
? `${(_o = ("scanErrors" in done ? done.scanErrors : 0)) !== null && _o !== void 0 ? _o : 0} scan errors, ${(_p = ("failedFiles" in done ? done.failedFiles : 0)) !== null && _p !== void 0 ? _p : 0} file failures`
|
|
892
919
|
: done.error;
|
|
893
920
|
console.log(`FAIL ${name}: reindex failed (${reason})`);
|
|
894
921
|
}
|
package/dist/commands/status.js
CHANGED
|
@@ -51,6 +51,7 @@ const config_1 = require("../config");
|
|
|
51
51
|
const embedding_status_1 = require("../lib/index/embedding-status");
|
|
52
52
|
const index_config_1 = require("../lib/index/index-config");
|
|
53
53
|
const index_state_footer_1 = require("../lib/output/index-state-footer");
|
|
54
|
+
const autostart_1 = require("../lib/utils/autostart");
|
|
54
55
|
const daemon_client_1 = require("../lib/utils/daemon-client");
|
|
55
56
|
const exit_1 = require("../lib/utils/exit");
|
|
56
57
|
const filter_builder_1 = require("../lib/utils/filter-builder");
|
|
@@ -78,12 +79,13 @@ function loadStatusView(projects) {
|
|
|
78
79
|
daemon: () => (0, daemon_client_1.sendDaemonCommand)({ cmd: "status" }),
|
|
79
80
|
render: (resp) => __awaiter(this, void 0, void 0, function* () {
|
|
80
81
|
var _a;
|
|
82
|
+
var _b;
|
|
81
83
|
const watchers = new Map();
|
|
82
84
|
const entries = Array.isArray(resp.projects) ? resp.projects : [];
|
|
83
85
|
const health = new Map();
|
|
84
86
|
for (const entry of entries) {
|
|
85
87
|
if (entry && typeof entry.root === "string") {
|
|
86
|
-
watchers.set(entry.root, { status: (
|
|
88
|
+
watchers.set(entry.root, { status: (_b = entry.status) !== null && _b !== void 0 ? _b : "watching" });
|
|
87
89
|
if (entry.indexState)
|
|
88
90
|
health.set(entry.root, entry.indexState);
|
|
89
91
|
}
|
|
@@ -93,15 +95,21 @@ function loadStatusView(projects) {
|
|
|
93
95
|
// the in-process path does when the LanceDB query fails.
|
|
94
96
|
const chunkCounts = new Map();
|
|
95
97
|
for (const project of projects) {
|
|
98
|
+
if (((_a = resp.service) === null || _a === void 0 ? void 0 : _a.mode) === "paused")
|
|
99
|
+
break;
|
|
96
100
|
const stats = yield (0, daemon_client_1.sendDaemonCommand)({ cmd: "project-stats", root: project.root }, { timeoutMs: 30000 });
|
|
97
101
|
if (stats.ok && typeof stats.chunks === "number") {
|
|
98
102
|
chunkCounts.set(project.root, stats.chunks);
|
|
99
103
|
}
|
|
100
104
|
}
|
|
101
|
-
const daemon = Object.assign(Object.assign(Object.assign({}, (resp.compaction !== undefined
|
|
105
|
+
const daemon = Object.assign(Object.assign(Object.assign(Object.assign({}, (resp.compaction !== undefined
|
|
102
106
|
? { compaction: resp.compaction }
|
|
103
107
|
: {})), (resp.resources !== undefined
|
|
104
108
|
? { resources: resp.resources }
|
|
109
|
+
: {})), (resp.service
|
|
110
|
+
? {
|
|
111
|
+
service: resp.service,
|
|
112
|
+
}
|
|
105
113
|
: {})), { pid: typeof resp.pid === "number" ? resp.pid : null, uptimeSec: typeof resp.uptime === "number" ? resp.uptime : null,
|
|
106
114
|
// Older daemons do not report these.
|
|
107
115
|
workers: typeof resp.workers === "number" ? resp.workers : null, workerThreads: resp.workerThreads &&
|
|
@@ -206,7 +214,9 @@ function buildStatusJson(input) {
|
|
|
206
214
|
? ((_c = input.countWorkers) !== null && _c !== void 0 ? _c : countWorkerProcesses)(d.pid)
|
|
207
215
|
: null);
|
|
208
216
|
}
|
|
209
|
-
return Object.assign(Object.assign({}, (input.stores ? { stores: input.stores } : {})), { daemon: Object.assign(Object.assign(
|
|
217
|
+
return Object.assign(Object.assign({}, (input.stores ? { stores: input.stores } : {})), { daemon: Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({ running: d !== undefined }, (input.startupBlockedReason != null
|
|
218
|
+
? { startupBlockedReason: input.startupBlockedReason }
|
|
219
|
+
: {})), { pid: (_d = d === null || d === void 0 ? void 0 : d.pid) !== null && _d !== void 0 ? _d : null, since: (d === null || d === void 0 ? void 0 : d.uptimeSec) != null ? now - d.uptimeSec * 1000 : null, workerThreads: (_e = d === null || d === void 0 ? void 0 : d.workerThreads) !== null && _e !== void 0 ? _e : null }), ((d === null || d === void 0 ? void 0 : d.service) ? { service: d.service } : {})), ((d === null || d === void 0 ? void 0 : d.resources) !== undefined ? { resources: d.resources } : {})), ((d === null || d === void 0 ? void 0 : d.compaction) !== undefined ? { compaction: d.compaction } : {})), settings: {
|
|
210
220
|
embedMode: globalConfig.embedMode,
|
|
211
221
|
modelTier: globalConfig.modelTier,
|
|
212
222
|
vectorDim: globalConfig.vectorDim,
|
|
@@ -263,8 +273,8 @@ Examples:
|
|
|
263
273
|
gmax status --json The same, plus daemon and settings, as JSON
|
|
264
274
|
`)
|
|
265
275
|
.action((opts) => __awaiter(void 0, void 0, void 0, function* () {
|
|
266
|
-
var _a, _b;
|
|
267
|
-
var
|
|
276
|
+
var _a, _b, _c, _d;
|
|
277
|
+
var _e, _f, _g, _h, _j, _k, _l, _m;
|
|
268
278
|
const globalConfig = (0, index_config_1.readGlobalConfig)();
|
|
269
279
|
const projects = (0, project_registry_1.listProjects)();
|
|
270
280
|
const indexing = (0, lock_1.isLocked)(config_1.PATHS.globalRoot);
|
|
@@ -283,6 +293,7 @@ Examples:
|
|
|
283
293
|
throw err;
|
|
284
294
|
}
|
|
285
295
|
const { watchers, chunkCounts } = view;
|
|
296
|
+
const startupBlockedReason = (0, autostart_1.daemonStartDeniedReason)();
|
|
286
297
|
if (opts.json) {
|
|
287
298
|
const json = buildStatusJson({
|
|
288
299
|
stores,
|
|
@@ -290,6 +301,7 @@ Examples:
|
|
|
290
301
|
projects,
|
|
291
302
|
globalConfig,
|
|
292
303
|
indexing,
|
|
304
|
+
startupBlockedReason,
|
|
293
305
|
workerThreads: (0, config_1.resolveWorkerThreads)({
|
|
294
306
|
env: process.env.GMAX_WORKER_THREADS,
|
|
295
307
|
configValue: globalConfig.workerThreads,
|
|
@@ -303,18 +315,30 @@ Examples:
|
|
|
303
315
|
// Header
|
|
304
316
|
console.log(`\n${style.bold("gmax")} · ${globalConfig.modelTier} (${globalConfig.vectorDim}d, ${globalConfig.embedMode})${indexing ? style.yellow(" · indexing...") : ""}`);
|
|
305
317
|
}
|
|
318
|
+
if (((_b = (_a = view.daemon) === null || _a === void 0 ? void 0 : _a.service) === null || _b === void 0 ? void 0 : _b.mode) === "paused") {
|
|
319
|
+
const reason = ((_e = view.daemon.service.reason) !== null && _e !== void 0 ? _e : "host pressure").replace(/[\r\n\t]/g, " ");
|
|
320
|
+
console.log(opts.agent
|
|
321
|
+
? `daemon_service\tmode=paused\treason=${reason}`
|
|
322
|
+
: style.yellow(`Bounded reads available; indexing and embeddings paused: ${reason}`));
|
|
323
|
+
}
|
|
324
|
+
else if (startupBlockedReason !== null) {
|
|
325
|
+
const reason = startupBlockedReason.replace(/[\r\n\t]/g, " ");
|
|
326
|
+
console.log(opts.agent
|
|
327
|
+
? `daemon_startup\tblocked=true\treason=${reason}`
|
|
328
|
+
: style.red(`Daemon startup blocked: ${reason}`));
|
|
329
|
+
}
|
|
306
330
|
for (const store of stores) {
|
|
307
331
|
if (opts.agent) {
|
|
308
332
|
console.log(`store\t${store.home}\t${store.state}\tprefixes=${store.prefixes.join(",")}${store.error ? `\terror=${store.error}` : ""}`);
|
|
309
333
|
for (const project of store.projects)
|
|
310
|
-
console.log(`store_project\t${store.home}\t${project.name}\t${(
|
|
334
|
+
console.log(`store_project\t${store.home}\t${project.name}\t${(_f = project.chunkCount) !== null && _f !== void 0 ? _f : 0}\t${(_g = project.status) !== null && _g !== void 0 ? _g : "indexed"}\tcounts=cached`);
|
|
311
335
|
}
|
|
312
336
|
else {
|
|
313
337
|
console.log(`\nStore: ${shortenPath(store.home)} — ${store.state}${store.error ? ` (${store.error})` : ""}`);
|
|
314
338
|
if (store.state === "offline")
|
|
315
339
|
console.log(" Connect the drive to list its projects.");
|
|
316
340
|
for (const project of store.projects)
|
|
317
|
-
console.log(` ${project.name} ${formatChunks(project.chunkCount)} chunks (cached) ${(
|
|
341
|
+
console.log(` ${project.name} ${formatChunks(project.chunkCount)} chunks (cached) ${(_h = project.status) !== null && _h !== void 0 ? _h : "indexed"}`);
|
|
318
342
|
}
|
|
319
343
|
}
|
|
320
344
|
if (projects.length === 0) {
|
|
@@ -331,9 +355,9 @@ Examples:
|
|
|
331
355
|
for (const project of projects) {
|
|
332
356
|
const st = projectState(project, watchers.get(project.root));
|
|
333
357
|
const isCurrent = project.root === currentRoot;
|
|
334
|
-
const count = (
|
|
358
|
+
const count = (_j = chunkCounts.get(project.root)) !== null && _j !== void 0 ? _j : project.chunkCount;
|
|
335
359
|
const identity = (0, embedding_status_1.projectEmbeddingStatus)(project, globalConfig);
|
|
336
|
-
console.log(`${project.name}\t${formatChunks(count)}\t${formatAge(project.lastIndexed)}\t${st}\tembedding=${identity.state}${isCurrent ? "\tcurrent" : ""}${((
|
|
360
|
+
console.log(`${project.name}\t${formatChunks(count)}\t${formatAge(project.lastIndexed)}\t${st}\tembedding=${identity.state}${isCurrent ? "\tcurrent" : ""}${((_c = view.health) === null || _c === void 0 ? void 0 : _c.get(project.root)) ? `\t${(_k = (0, index_state_footer_1.formatIndexStateFooter)(view.health.get(project.root), { agent: true })) !== null && _k !== void 0 ? _k : ""}` : ""}`);
|
|
337
361
|
}
|
|
338
362
|
const legacyNotice = (0, embedding_status_1.formatLegacyEmbeddingNotice)((0, embedding_status_1.countLegacyEmbeddingProjects)(projects, globalConfig), { agent: true });
|
|
339
363
|
if (legacyNotice)
|
|
@@ -349,7 +373,7 @@ Examples:
|
|
|
349
373
|
const watcher = watchers.get(project.root);
|
|
350
374
|
// Status column
|
|
351
375
|
let statusStr;
|
|
352
|
-
const projectStatus = (
|
|
376
|
+
const projectStatus = (_l = project.status) !== null && _l !== void 0 ? _l : "indexed";
|
|
353
377
|
if (projectStatus === "pending") {
|
|
354
378
|
statusStr = style.yellow("pending");
|
|
355
379
|
}
|
|
@@ -369,7 +393,7 @@ Examples:
|
|
|
369
393
|
statusStr = style.dim("idle");
|
|
370
394
|
}
|
|
371
395
|
// Chunks column
|
|
372
|
-
const count = (
|
|
396
|
+
const count = (_m = chunkCounts.get(project.root)) !== null && _m !== void 0 ? _m : project.chunkCount;
|
|
373
397
|
const chunks = `${formatChunks(count)} chunks`;
|
|
374
398
|
// Age column
|
|
375
399
|
const age = formatAge(project.lastIndexed);
|
|
@@ -378,7 +402,7 @@ Examples:
|
|
|
378
402
|
const marker = isCurrent ? style.cyan(" ←") : "";
|
|
379
403
|
const name = project.name.padEnd(nameWidth);
|
|
380
404
|
console.log(` ${name} ${chunks.padEnd(12)} ${age.padEnd(10)} ${statusStr} embedding:${embedding}${marker}`);
|
|
381
|
-
const health = (0, index_state_footer_1.formatIndexStateFooter)((
|
|
405
|
+
const health = (0, index_state_footer_1.formatIndexStateFooter)((_d = view.health) === null || _d === void 0 ? void 0 : _d.get(project.root), {
|
|
382
406
|
agent: false,
|
|
383
407
|
});
|
|
384
408
|
if (health)
|
package/dist/commands/watch.js
CHANGED
|
@@ -72,12 +72,18 @@ exports.watch = new commander_1.Command("watch")
|
|
|
72
72
|
.description("Start background file watcher for live reindexing")
|
|
73
73
|
.option("-b, --background", "Run watcher in background and exit")
|
|
74
74
|
.option("-d, --daemon", "Run as centralized daemon watching projects that active sessions hold")
|
|
75
|
+
.option("--read-only", "Serve bounded reads without indexing, models, or clearing quarantine")
|
|
75
76
|
.option("-p, --path <dir>", "Directory to watch (defaults to project root)")
|
|
76
77
|
.option("--no-idle-timeout", "Disable the 30-minute idle shutdown")
|
|
77
78
|
.action((options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
78
79
|
var _a, _b;
|
|
80
|
+
if (options.readOnly && !options.daemon) {
|
|
81
|
+
console.error("gmax: --read-only requires --daemon");
|
|
82
|
+
process.exitCode = 2;
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
79
85
|
const safetyDenied = (0, autostart_1.daemonStartDeniedReason)();
|
|
80
|
-
if (safetyDenied !== null) {
|
|
86
|
+
if (safetyDenied !== null && !options.readOnly) {
|
|
81
87
|
console.error(`gmax: ${safetyDenied}; preserving containment`);
|
|
82
88
|
process.exitCode = 2;
|
|
83
89
|
return;
|
|
@@ -114,6 +120,14 @@ exports.watch = new commander_1.Command("watch")
|
|
|
114
120
|
const cliVersion = JSON.parse(fs.readFileSync(path.join(__dirname, "../../package.json"), "utf-8")).version;
|
|
115
121
|
const resp = yield sendDaemonCommand({ cmd: "ping" });
|
|
116
122
|
if (resp.version && resp.version === cliVersion) {
|
|
123
|
+
if (options.readOnly) {
|
|
124
|
+
const paused = yield sendDaemonCommand({
|
|
125
|
+
cmd: "pause",
|
|
126
|
+
reason: "read-only service requested",
|
|
127
|
+
});
|
|
128
|
+
if (!paused.ok)
|
|
129
|
+
throw new Error(String(paused.error));
|
|
130
|
+
}
|
|
117
131
|
process.exit(0);
|
|
118
132
|
}
|
|
119
133
|
console.log(`Daemon version mismatch (${resp.version} → ${cliVersion}), restarting...`);
|
|
@@ -151,7 +165,9 @@ exports.watch = new commander_1.Command("watch")
|
|
|
151
165
|
const { spawnDaemonProcess } = yield Promise.resolve().then(() => __importStar(require("../lib/utils/daemon-launcher")));
|
|
152
166
|
let started;
|
|
153
167
|
try {
|
|
154
|
-
started =
|
|
168
|
+
started = options.readOnly
|
|
169
|
+
? yield spawnDaemonProcess({ readOnly: true })
|
|
170
|
+
: yield spawnDaemonProcess();
|
|
155
171
|
}
|
|
156
172
|
catch (error) {
|
|
157
173
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -195,7 +211,10 @@ exports.watch = new commander_1.Command("watch")
|
|
|
195
211
|
console.error("[daemon] unhandledRejection:", reason);
|
|
196
212
|
});
|
|
197
213
|
try {
|
|
198
|
-
|
|
214
|
+
if (options.readOnly)
|
|
215
|
+
yield daemon.start({ readOnly: true });
|
|
216
|
+
else
|
|
217
|
+
yield daemon.start();
|
|
199
218
|
}
|
|
200
219
|
catch (err) {
|
|
201
220
|
const code = err === null || err === void 0 ? void 0 : err.code;
|