grepmax 0.26.57 → 0.26.59
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 +5 -1
- package/dist/commands/doctor.js +29 -13
- package/dist/commands/search-output.js +4 -2
- package/dist/commands/search-run.js +3 -0
- package/dist/commands/search.js +0 -2
- package/dist/commands/status.js +27 -14
- 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/operation-coordinator.js +6 -0
- package/package.json +1 -1
- 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.59",
|
|
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
|
|
|
@@ -474,6 +474,10 @@ gmax watch restart # Restart daemon
|
|
|
474
474
|
|
|
475
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
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. CLI agent output preserves the keyword-only warning, and daemon reads skip client-side model setup. 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
|
+
|
|
477
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.
|
|
478
482
|
|
|
479
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
|
@@ -347,7 +347,7 @@ exports.doctor = new commander_1.Command("doctor")
|
|
|
347
347
|
.option("--agent", "Compact output for AI agents", false)
|
|
348
348
|
.action((opts) => __awaiter(void 0, void 0, void 0, function* () {
|
|
349
349
|
var _a, _b;
|
|
350
|
-
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;
|
|
351
351
|
if (opts.fix) {
|
|
352
352
|
const denied = (0, maintenance_policy_1.storeMutationDeniedReason)();
|
|
353
353
|
if (denied !== null) {
|
|
@@ -358,6 +358,22 @@ exports.doctor = new commander_1.Command("doctor")
|
|
|
358
358
|
}
|
|
359
359
|
if (!opts.agent)
|
|
360
360
|
console.log("gmax Doctor\n");
|
|
361
|
+
// Metadata-only diagnostics while paused: never open a second native
|
|
362
|
+
// reader or scan the retained corpus on a pressured host.
|
|
363
|
+
const { sendDaemonCommand } = yield Promise.resolve().then(() => __importStar(require("../lib/utils/daemon-client")));
|
|
364
|
+
const daemonStatus = yield sendDaemonCommand({ cmd: "status" });
|
|
365
|
+
const service = daemonStatus.service;
|
|
366
|
+
if (daemonStatus.ok && (service === null || service === void 0 ? void 0 : service.mode) === "paused") {
|
|
367
|
+
const reason = String((_c = service.reason) !== null && _c !== void 0 ? _c : "read-only service requested").replace(/[\r\n\t]/g, " ");
|
|
368
|
+
console.log(opts.agent
|
|
369
|
+
? `daemon_service\tmode=paused\treads=bounded\treason=${reason}`
|
|
370
|
+
: `WARN Daemon available for bounded reads; indexing and embeddings paused: ${reason}`);
|
|
371
|
+
console.log(opts.agent
|
|
372
|
+
? "index_diagnostics\tskipped=paused_service"
|
|
373
|
+
: "INFO Native index diagnostics skipped while heavy work is paused");
|
|
374
|
+
process.exitCode = 2;
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
361
377
|
const startupBlockedReason = (0, autostart_1.daemonStartDeniedReason)();
|
|
362
378
|
if (startupBlockedReason !== null) {
|
|
363
379
|
const reason = startupBlockedReason.replace(/[\r\n\t]/g, " ");
|
|
@@ -382,12 +398,12 @@ exports.doctor = new commander_1.Command("doctor")
|
|
|
382
398
|
checkDir("Grammars", grammars);
|
|
383
399
|
}
|
|
384
400
|
const globalConfig = (0, index_config_1.readGlobalConfig)();
|
|
385
|
-
const tier = (
|
|
401
|
+
const tier = (_d = config_1.MODEL_TIERS[globalConfig.modelTier]) !== null && _d !== void 0 ? _d : config_1.MODEL_TIERS.small;
|
|
386
402
|
if (!config_1.MODEL_TIERS[globalConfig.modelTier]) {
|
|
387
403
|
console.log(`WARN Unknown model tier '${globalConfig.modelTier}', falling back to 'small'`);
|
|
388
404
|
}
|
|
389
405
|
const embedModel = globalConfig.embedMode === "gpu"
|
|
390
|
-
? ((
|
|
406
|
+
? ((_e = globalConfig.mlxModel) !== null && _e !== void 0 ? _e : tier.mlxModel)
|
|
391
407
|
: tier.onnxModel;
|
|
392
408
|
if (!opts.agent) {
|
|
393
409
|
console.log(`\nEmbed mode: ${globalConfig.embedMode} | Model tier: ${globalConfig.modelTier} (${tier.vectorDim}d)`);
|
|
@@ -448,7 +464,7 @@ exports.doctor = new commander_1.Command("doctor")
|
|
|
448
464
|
body: JSON.stringify({ texts: ["gmax health check"] }),
|
|
449
465
|
});
|
|
450
466
|
const embedData = yield embedRes.json();
|
|
451
|
-
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;
|
|
452
468
|
const ms = Date.now() - start;
|
|
453
469
|
const expectedDim = tier.vectorDim || 384;
|
|
454
470
|
if (dim === expectedDim) {
|
|
@@ -475,7 +491,7 @@ exports.doctor = new commander_1.Command("doctor")
|
|
|
475
491
|
// caller most likely to be inside the sandbox this check is about.
|
|
476
492
|
const sandbox = checkClaudeSandbox({
|
|
477
493
|
home: os.homedir(),
|
|
478
|
-
projectRoot: (
|
|
494
|
+
projectRoot: (_g = (0, project_root_1.findProjectRoot)(process.cwd())) !== null && _g !== void 0 ? _g : process.cwd(),
|
|
479
495
|
});
|
|
480
496
|
if (sandbox) {
|
|
481
497
|
if (opts.agent) {
|
|
@@ -594,7 +610,7 @@ exports.doctor = new commander_1.Command("doctor")
|
|
|
594
610
|
? "LOW"
|
|
595
611
|
: "ok";
|
|
596
612
|
}
|
|
597
|
-
catch (
|
|
613
|
+
catch (_q) { }
|
|
598
614
|
const staleChunkerProjects = projects.filter((p) => { var _a; return p.status === "indexed" &&
|
|
599
615
|
((_a = p.chunkerVersion) !== null && _a !== void 0 ? _a : 1) < config_1.CONFIG.CHUNKER_VERSION; });
|
|
600
616
|
const staleEmbeddingProjects = projects.filter((p) => p.status === "indexed" &&
|
|
@@ -623,9 +639,9 @@ exports.doctor = new commander_1.Command("doctor")
|
|
|
623
639
|
`schema_dim=${physicalDim !== null && physicalDim !== void 0 ? physicalDim : "none"}`,
|
|
624
640
|
`schema_dim_ok=${schemaGap ? "false" : "true"}`,
|
|
625
641
|
`ann_index=${vectorIndex ? "present" : "absent"}`,
|
|
626
|
-
`ann_indexed=${(
|
|
627
|
-
`ann_unindexed=${(
|
|
628
|
-
`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"}`,
|
|
629
645
|
];
|
|
630
646
|
console.log(fields.join("\t"));
|
|
631
647
|
if (schemaGap) {
|
|
@@ -680,7 +696,7 @@ exports.doctor = new commander_1.Command("doctor")
|
|
|
680
696
|
console.log(`ok Schema: vector table is ${physicalDim}d`);
|
|
681
697
|
}
|
|
682
698
|
if (vectorIndexStats) {
|
|
683
|
-
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"}`);
|
|
684
700
|
}
|
|
685
701
|
else {
|
|
686
702
|
console.log("INFO ANN: vector index not built (set GMAX_ANN=1)");
|
|
@@ -809,7 +825,7 @@ exports.doctor = new commander_1.Command("doctor")
|
|
|
809
825
|
yield mc.close();
|
|
810
826
|
}
|
|
811
827
|
}
|
|
812
|
-
catch (
|
|
828
|
+
catch (_r) { }
|
|
813
829
|
}
|
|
814
830
|
}
|
|
815
831
|
// --fix auto-remediation
|
|
@@ -887,7 +903,7 @@ exports.doctor = new commander_1.Command("doctor")
|
|
|
887
903
|
const done = yield sendStreamingCommand({ cmd: "index", root: p.root, reset: true }, () => { }).catch((e) => ({ ok: false, error: String(e) }));
|
|
888
904
|
if (done.ok &&
|
|
889
905
|
(!("degraded" in done) || done.degraded !== true)) {
|
|
890
|
-
const chunks = (
|
|
906
|
+
const chunks = (_m = done.indexed) !== null && _m !== void 0 ? _m : 0;
|
|
891
907
|
if (opts.agent) {
|
|
892
908
|
console.log(`stale_chunker_reindexed\tname=${name}\tchunks=${chunks}`);
|
|
893
909
|
}
|
|
@@ -899,7 +915,7 @@ exports.doctor = new commander_1.Command("doctor")
|
|
|
899
915
|
else if (!opts.agent) {
|
|
900
916
|
const degraded = "degraded" in done && done.degraded === true;
|
|
901
917
|
const reason = degraded
|
|
902
|
-
? `${(
|
|
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`
|
|
903
919
|
: done.error;
|
|
904
920
|
console.log(`FAIL ${name}: reindex failed (${reason})`);
|
|
905
921
|
}
|
|
@@ -210,9 +210,11 @@ function renderSearchOutput(params) {
|
|
|
210
210
|
var _a, _b, _c;
|
|
211
211
|
var _d, _e, _f, _g, _h, _j;
|
|
212
212
|
const { searchResult, options, minScore, crossProject, pattern, effectiveRoot, projectRoot, vectorDb, precomputedSkeletons, precomputedGraph, indexState, } = params;
|
|
213
|
-
if (
|
|
213
|
+
if ((_a = searchResult.warnings) === null || _a === void 0 ? void 0 : _a.length) {
|
|
214
214
|
for (const w of searchResult.warnings) {
|
|
215
|
-
console.warn(
|
|
215
|
+
console.warn(options.agent
|
|
216
|
+
? `warn\t${w.replace(/[\r\n\t]/g, " ")}`
|
|
217
|
+
: `Warning: ${w}`);
|
|
216
218
|
}
|
|
217
219
|
}
|
|
218
220
|
// Partial-index signal (Phase 6): when the index is mid-catchup, results
|
|
@@ -53,6 +53,7 @@ const sync_helpers_1 = require("../lib/index/sync-helpers");
|
|
|
53
53
|
const syncer_1 = require("../lib/index/syncer");
|
|
54
54
|
const per_file_1 = require("../lib/search/per-file");
|
|
55
55
|
const searcher_1 = require("../lib/search/searcher");
|
|
56
|
+
const setup_helpers_1 = require("../lib/setup/setup-helpers");
|
|
56
57
|
const vector_db_1 = require("../lib/store/vector-db");
|
|
57
58
|
const lock_1 = require("../lib/utils/lock");
|
|
58
59
|
const project_registry_1 = require("../lib/utils/project-registry");
|
|
@@ -160,6 +161,8 @@ function runSearch(params) {
|
|
|
160
161
|
// In-process fallback: open VectorDB, ensure index, run Searcher.
|
|
161
162
|
// Only entered when the daemon path didn't produce results.
|
|
162
163
|
if (!searchResult) {
|
|
164
|
+
// A daemon read needs no client-side model download or loading.
|
|
165
|
+
yield (0, setup_helpers_1.ensureSetup)();
|
|
163
166
|
const vectorDb = new vector_db_1.VectorDB(paths.lancedbDir);
|
|
164
167
|
openedDb = vectorDb;
|
|
165
168
|
// Check for active indexing lock and warn if present
|
package/dist/commands/search.js
CHANGED
|
@@ -46,7 +46,6 @@ exports.search = void 0;
|
|
|
46
46
|
const path = __importStar(require("node:path"));
|
|
47
47
|
const commander_1 = require("commander");
|
|
48
48
|
const per_file_1 = require("../lib/search/per-file");
|
|
49
|
-
const setup_helpers_1 = require("../lib/setup/setup-helpers");
|
|
50
49
|
const cross_project_1 = require("../lib/utils/cross-project");
|
|
51
50
|
const exit_1 = require("../lib/utils/exit");
|
|
52
51
|
const path_containment_1 = require("../lib/utils/path-containment");
|
|
@@ -238,7 +237,6 @@ Examples:
|
|
|
238
237
|
return;
|
|
239
238
|
}
|
|
240
239
|
try {
|
|
241
|
-
yield (0, setup_helpers_1.ensureSetup)();
|
|
242
240
|
let checkRoot = cwdProjectRoot;
|
|
243
241
|
if (options.root) {
|
|
244
242
|
const resolved = resolvedOptionRoot !== undefined
|
package/dist/commands/status.js
CHANGED
|
@@ -79,12 +79,13 @@ function loadStatusView(projects) {
|
|
|
79
79
|
daemon: () => (0, daemon_client_1.sendDaemonCommand)({ cmd: "status" }),
|
|
80
80
|
render: (resp) => __awaiter(this, void 0, void 0, function* () {
|
|
81
81
|
var _a;
|
|
82
|
+
var _b;
|
|
82
83
|
const watchers = new Map();
|
|
83
84
|
const entries = Array.isArray(resp.projects) ? resp.projects : [];
|
|
84
85
|
const health = new Map();
|
|
85
86
|
for (const entry of entries) {
|
|
86
87
|
if (entry && typeof entry.root === "string") {
|
|
87
|
-
watchers.set(entry.root, { status: (
|
|
88
|
+
watchers.set(entry.root, { status: (_b = entry.status) !== null && _b !== void 0 ? _b : "watching" });
|
|
88
89
|
if (entry.indexState)
|
|
89
90
|
health.set(entry.root, entry.indexState);
|
|
90
91
|
}
|
|
@@ -94,15 +95,21 @@ function loadStatusView(projects) {
|
|
|
94
95
|
// the in-process path does when the LanceDB query fails.
|
|
95
96
|
const chunkCounts = new Map();
|
|
96
97
|
for (const project of projects) {
|
|
98
|
+
if (((_a = resp.service) === null || _a === void 0 ? void 0 : _a.mode) === "paused")
|
|
99
|
+
break;
|
|
97
100
|
const stats = yield (0, daemon_client_1.sendDaemonCommand)({ cmd: "project-stats", root: project.root }, { timeoutMs: 30000 });
|
|
98
101
|
if (stats.ok && typeof stats.chunks === "number") {
|
|
99
102
|
chunkCounts.set(project.root, stats.chunks);
|
|
100
103
|
}
|
|
101
104
|
}
|
|
102
|
-
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
|
|
103
106
|
? { compaction: resp.compaction }
|
|
104
107
|
: {})), (resp.resources !== undefined
|
|
105
108
|
? { resources: resp.resources }
|
|
109
|
+
: {})), (resp.service
|
|
110
|
+
? {
|
|
111
|
+
service: resp.service,
|
|
112
|
+
}
|
|
106
113
|
: {})), { pid: typeof resp.pid === "number" ? resp.pid : null, uptimeSec: typeof resp.uptime === "number" ? resp.uptime : null,
|
|
107
114
|
// Older daemons do not report these.
|
|
108
115
|
workers: typeof resp.workers === "number" ? resp.workers : null, workerThreads: resp.workerThreads &&
|
|
@@ -207,9 +214,9 @@ function buildStatusJson(input) {
|
|
|
207
214
|
? ((_c = input.countWorkers) !== null && _c !== void 0 ? _c : countWorkerProcesses)(d.pid)
|
|
208
215
|
: null);
|
|
209
216
|
}
|
|
210
|
-
return Object.assign(Object.assign({}, (input.stores ? { stores: input.stores } : {})), { daemon: Object.assign(Object.assign(Object.assign(Object.assign({ running: d !== undefined }, (input.startupBlockedReason != null
|
|
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
|
|
211
218
|
? { startupBlockedReason: input.startupBlockedReason }
|
|
212
|
-
: {})), { 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.resources) !== undefined ? { resources: d.resources } : {})), ((d === null || d === void 0 ? void 0 : d.compaction) !== undefined ? { compaction: d.compaction } : {})), settings: {
|
|
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: {
|
|
213
220
|
embedMode: globalConfig.embedMode,
|
|
214
221
|
modelTier: globalConfig.modelTier,
|
|
215
222
|
vectorDim: globalConfig.vectorDim,
|
|
@@ -266,8 +273,8 @@ Examples:
|
|
|
266
273
|
gmax status --json The same, plus daemon and settings, as JSON
|
|
267
274
|
`)
|
|
268
275
|
.action((opts) => __awaiter(void 0, void 0, void 0, function* () {
|
|
269
|
-
var _a, _b;
|
|
270
|
-
var
|
|
276
|
+
var _a, _b, _c, _d;
|
|
277
|
+
var _e, _f, _g, _h, _j, _k, _l, _m;
|
|
271
278
|
const globalConfig = (0, index_config_1.readGlobalConfig)();
|
|
272
279
|
const projects = (0, project_registry_1.listProjects)();
|
|
273
280
|
const indexing = (0, lock_1.isLocked)(config_1.PATHS.globalRoot);
|
|
@@ -308,7 +315,13 @@ Examples:
|
|
|
308
315
|
// Header
|
|
309
316
|
console.log(`\n${style.bold("gmax")} · ${globalConfig.modelTier} (${globalConfig.vectorDim}d, ${globalConfig.embedMode})${indexing ? style.yellow(" · indexing...") : ""}`);
|
|
310
317
|
}
|
|
311
|
-
if (
|
|
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) {
|
|
312
325
|
const reason = startupBlockedReason.replace(/[\r\n\t]/g, " ");
|
|
313
326
|
console.log(opts.agent
|
|
314
327
|
? `daemon_startup\tblocked=true\treason=${reason}`
|
|
@@ -318,14 +331,14 @@ Examples:
|
|
|
318
331
|
if (opts.agent) {
|
|
319
332
|
console.log(`store\t${store.home}\t${store.state}\tprefixes=${store.prefixes.join(",")}${store.error ? `\terror=${store.error}` : ""}`);
|
|
320
333
|
for (const project of store.projects)
|
|
321
|
-
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`);
|
|
322
335
|
}
|
|
323
336
|
else {
|
|
324
337
|
console.log(`\nStore: ${shortenPath(store.home)} — ${store.state}${store.error ? ` (${store.error})` : ""}`);
|
|
325
338
|
if (store.state === "offline")
|
|
326
339
|
console.log(" Connect the drive to list its projects.");
|
|
327
340
|
for (const project of store.projects)
|
|
328
|
-
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"}`);
|
|
329
342
|
}
|
|
330
343
|
}
|
|
331
344
|
if (projects.length === 0) {
|
|
@@ -342,9 +355,9 @@ Examples:
|
|
|
342
355
|
for (const project of projects) {
|
|
343
356
|
const st = projectState(project, watchers.get(project.root));
|
|
344
357
|
const isCurrent = project.root === currentRoot;
|
|
345
|
-
const count = (
|
|
358
|
+
const count = (_j = chunkCounts.get(project.root)) !== null && _j !== void 0 ? _j : project.chunkCount;
|
|
346
359
|
const identity = (0, embedding_status_1.projectEmbeddingStatus)(project, globalConfig);
|
|
347
|
-
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 : ""}` : ""}`);
|
|
348
361
|
}
|
|
349
362
|
const legacyNotice = (0, embedding_status_1.formatLegacyEmbeddingNotice)((0, embedding_status_1.countLegacyEmbeddingProjects)(projects, globalConfig), { agent: true });
|
|
350
363
|
if (legacyNotice)
|
|
@@ -360,7 +373,7 @@ Examples:
|
|
|
360
373
|
const watcher = watchers.get(project.root);
|
|
361
374
|
// Status column
|
|
362
375
|
let statusStr;
|
|
363
|
-
const projectStatus = (
|
|
376
|
+
const projectStatus = (_l = project.status) !== null && _l !== void 0 ? _l : "indexed";
|
|
364
377
|
if (projectStatus === "pending") {
|
|
365
378
|
statusStr = style.yellow("pending");
|
|
366
379
|
}
|
|
@@ -380,7 +393,7 @@ Examples:
|
|
|
380
393
|
statusStr = style.dim("idle");
|
|
381
394
|
}
|
|
382
395
|
// Chunks column
|
|
383
|
-
const count = (
|
|
396
|
+
const count = (_m = chunkCounts.get(project.root)) !== null && _m !== void 0 ? _m : project.chunkCount;
|
|
384
397
|
const chunks = `${formatChunks(count)} chunks`;
|
|
385
398
|
// Age column
|
|
386
399
|
const age = formatAge(project.lastIndexed);
|
|
@@ -389,7 +402,7 @@ Examples:
|
|
|
389
402
|
const marker = isCurrent ? style.cyan(" ←") : "";
|
|
390
403
|
const name = project.name.padEnd(nameWidth);
|
|
391
404
|
console.log(` ${name} ${chunks.padEnd(12)} ${age.padEnd(10)} ${statusStr} embedding:${embedding}${marker}`);
|
|
392
|
-
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), {
|
|
393
406
|
agent: false,
|
|
394
407
|
});
|
|
395
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;
|