grepmax 0.26.81 → 0.26.82
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 +1 -1
- package/dist/lib/daemon/daemon.js +26 -2
- package/dist/lib/utils/operation-coordinator.js +9 -1
- package/dist/lib/workers/embeddings/mlx-client.js +4 -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.82",
|
|
14
14
|
"author": {
|
|
15
15
|
"name": "Robert Owens",
|
|
16
16
|
"email": "robowens@me.com"
|
package/README.md
CHANGED
|
@@ -524,7 +524,7 @@ To serve bounded reads under an existing startup quarantine, run `gmax watch --d
|
|
|
524
524
|
|
|
525
525
|
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. Do not repeatedly force repair, use future cutoffs or manually remove index fragments.
|
|
526
526
|
|
|
527
|
-
The package includes a separate prune-only helper with pinned Lance 12 dependencies, plus an exclusively leased Node wrapper. Runtime preparation uses `uv >=0.12.18`, an existing Python interpreter and locked wheels; it does not download Python, build from source, load an embedding model or run during installation. The helper currently supports macOS and Linux. The daemon runs independent prune-only retention: at most 128 old versions per pass, keeping current, tagged and two-minute recent versions. It retries backlog every minute and checks settled history every five minutes; status polling and watched edits do not prevent cleanup. Runtime setup happens before writer suspension. Older readers and external store owners are drained under an exclusive lease; new read-only queries may use the protected current version while history is deleted. Writers resume and watchers reconcile edits after the pass. Full-table compaction admission never gates this reclamation. Normal configured host policy applies; explicit offline recovery retains its separate admission.
|
|
527
|
+
The package includes a separate prune-only helper with pinned Lance 12 dependencies, plus an exclusively leased Node wrapper. Runtime preparation uses `uv >=0.12.18`, an existing Python interpreter and locked wheels; it does not download Python, build from source, load an embedding model or run during installation. The helper currently supports macOS and Linux. The daemon runs independent prune-only retention: at most 128 old versions per pass, keeping current, tagged and two-minute recent versions. It retries backlog every minute and checks settled history every five minutes; status polling and watched edits do not prevent cleanup. Runtime setup happens before writer suspension. Bulk project indexing defers retention without stopping watchers or closing reads, with a one-minute retry. Reads also remain available while watcher work quiesces; admission closes briefly to drain old native readers before pruning starts. Older readers and external store owners are drained under an exclusive lease; new read-only queries may use the protected current version while history is deleted. Writers resume and watchers reconcile edits after the pass. Full-table compaction admission never gates this reclamation. Normal configured host policy applies; explicit offline recovery retains its separate admission.
|
|
528
528
|
|
|
529
529
|
`gmax recover --table /path/to/lancedb/chunks.lance --json` reads the durable recovery receipt without starting a daemon or preparing Python. `--check` samples admission without pruning. `--prune --version <current-version> --cutoff <absolute-ISO-timestamp>` explicitly requests one offline prune. A prior uncertain attempt requires `--acknowledge-uncertain <attempt-id>` after inspection. Recovery never creates/clears quarantine, stops other owners, retries automatically or enables rewriting.
|
|
530
530
|
|
|
@@ -163,6 +163,7 @@ class Daemon {
|
|
|
163
163
|
this.cleanupInterval = null;
|
|
164
164
|
this.cleanupPromise = null;
|
|
165
165
|
this.lastCleanupAttempt = 0;
|
|
166
|
+
this.cleanupRetrySoon = false;
|
|
166
167
|
this.heartbeatTick = 0;
|
|
167
168
|
this.shuttingDown = false;
|
|
168
169
|
this.recycling = false;
|
|
@@ -854,7 +855,8 @@ class Daemon {
|
|
|
854
855
|
return;
|
|
855
856
|
this.cleanupInterval = setInterval(() => {
|
|
856
857
|
var _a;
|
|
857
|
-
const interval =
|
|
858
|
+
const interval = !this.cleanupRetrySoon &&
|
|
859
|
+
((_a = this.versionCleanupStatus()) === null || _a === void 0 ? void 0 : _a.eligibleVersionsRemaining) === 0
|
|
858
860
|
? 5 * 60000
|
|
859
861
|
: 60000;
|
|
860
862
|
if (!this.ready ||
|
|
@@ -875,6 +877,7 @@ class Daemon {
|
|
|
875
877
|
if (this.cleanupPromise)
|
|
876
878
|
return this.cleanupPromise;
|
|
877
879
|
this.lastCleanupAttempt = Date.now();
|
|
880
|
+
this.cleanupRetrySoon = false;
|
|
878
881
|
const run = this.performVersionCleanup();
|
|
879
882
|
const tracked = run.finally(() => {
|
|
880
883
|
if (this.cleanupPromise === tracked)
|
|
@@ -891,9 +894,26 @@ class Daemon {
|
|
|
891
894
|
this.recycling ||
|
|
892
895
|
this.pausedReason !== null)
|
|
893
896
|
throw new Error("daemon resources not ready for version cleanup");
|
|
897
|
+
const bulkIndexingActive = () => this.operations
|
|
898
|
+
.activeOperationNames()
|
|
899
|
+
.some((name) => [
|
|
900
|
+
"add-project",
|
|
901
|
+
"ensure-project",
|
|
902
|
+
"index-project",
|
|
903
|
+
"index-pending",
|
|
904
|
+
].includes(name));
|
|
905
|
+
const deferForIndexing = () => {
|
|
906
|
+
this.cleanupRetrySoon = true;
|
|
907
|
+
return (0, compaction_result_1.skippedCompaction)("project indexing is active; version cleanup deferred without closing reads");
|
|
908
|
+
};
|
|
909
|
+
if (bulkIndexingActive())
|
|
910
|
+
return deferForIndexing();
|
|
894
911
|
this.assertHeavyOperationAdmission("version-cleanup");
|
|
895
912
|
const deadline = AbortSignal.timeout(60000);
|
|
896
913
|
const runtime = yield (0, version_cleanup_1.prepareVersionCleanup)(config_1.PATHS.lancedbDir, deadline);
|
|
914
|
+
// Runtime setup yields: a bulk index can start before exclusive intent.
|
|
915
|
+
if (bulkIndexingActive())
|
|
916
|
+
return deferForIndexing();
|
|
897
917
|
let roots = [];
|
|
898
918
|
try {
|
|
899
919
|
return yield this.operations.runExclusive("version-cleanup", () => __awaiter(this, void 0, void 0, function* () {
|
|
@@ -919,7 +939,11 @@ class Daemon {
|
|
|
919
939
|
return (0, compaction_result_1.skippedCompaction)("no indexed table");
|
|
920
940
|
(0, logger_1.log)("daemon", `Version cleanup completed: ${JSON.stringify(result)}`);
|
|
921
941
|
return result;
|
|
922
|
-
}), {
|
|
942
|
+
}), {
|
|
943
|
+
queueShared: true,
|
|
944
|
+
pendingReads: (name) => (0, read_verbs_1.getReadVerb)(name) !== undefined ||
|
|
945
|
+
["search", "keyword-search", "documents", "project-stats"].includes(name),
|
|
946
|
+
});
|
|
923
947
|
}
|
|
924
948
|
finally {
|
|
925
949
|
if (!this.shuttingDown &&
|
|
@@ -117,6 +117,9 @@ class OperationCoordinator {
|
|
|
117
117
|
}
|
|
118
118
|
runExclusive(name, quiesce, fn, options = {}) {
|
|
119
119
|
var _a;
|
|
120
|
+
if (options.pendingReads &&
|
|
121
|
+
(name !== "version-cleanup" || !options.queueShared))
|
|
122
|
+
return Promise.reject(new Error("pending reads require queued prune-only cleanup"));
|
|
120
123
|
if (this.state.kind === "closing" || this.state.kind === "closed") {
|
|
121
124
|
return Promise.reject(new OperationClosedError());
|
|
122
125
|
}
|
|
@@ -126,11 +129,16 @@ class OperationCoordinator {
|
|
|
126
129
|
}
|
|
127
130
|
this.state = { kind: "exclusive-pending", name };
|
|
128
131
|
this.queueShared = (_a = options.queueShared) !== null && _a !== void 0 ? _a : false;
|
|
132
|
+
this.readWindow = options.pendingReads;
|
|
129
133
|
const controller = new AbortController();
|
|
130
134
|
this.controllers.add(controller);
|
|
131
135
|
const task = (() => __awaiter(this, void 0, void 0, function* () {
|
|
132
136
|
try {
|
|
133
137
|
yield quiesce();
|
|
138
|
+
// Reads remain available while writers quiesce. Close admission before
|
|
139
|
+
// taking the drain snapshot so every old native reader still settles
|
|
140
|
+
// before retention closes handles and deletes history.
|
|
141
|
+
this.readWindow = undefined;
|
|
134
142
|
yield Promise.allSettled([...this.sharedTasks]);
|
|
135
143
|
if (controller.signal.aborted) {
|
|
136
144
|
throw abortError(controller.signal.reason);
|
|
@@ -201,7 +209,7 @@ class OperationCoordinator {
|
|
|
201
209
|
}
|
|
202
210
|
if (this.state.kind === "exclusive" ||
|
|
203
211
|
this.state.kind === "exclusive-pending") {
|
|
204
|
-
if (
|
|
212
|
+
if ((_a = this.readWindow) === null || _a === void 0 ? void 0 : _a.call(this, name))
|
|
205
213
|
return;
|
|
206
214
|
throw new OperationBusyError(this.state.name);
|
|
207
215
|
}
|
|
@@ -102,6 +102,10 @@ function requestMlxJSON(reqPath, body) {
|
|
|
102
102
|
hostname: MLX_HOST,
|
|
103
103
|
port: MLX_PORT,
|
|
104
104
|
path: reqPath,
|
|
105
|
+
// A local connection is cheap. Avoid reusing a socket that uvicorn
|
|
106
|
+
// closed at its keep-alive boundary: that reset can incorrectly mark
|
|
107
|
+
// the healthy embedding backend unavailable and force CPU fallback.
|
|
108
|
+
agent: false,
|
|
105
109
|
method: payload === undefined ? "GET" : "POST",
|
|
106
110
|
headers: payload === undefined
|
|
107
111
|
? undefined
|
package/package.json
CHANGED