grepmax 0.9.1 → 0.9.2
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/dist/commands/serve.js
CHANGED
|
@@ -208,7 +208,7 @@ exports.serve = new commander_1.Command("serve")
|
|
|
208
208
|
const metaCache = new meta_cache_1.MetaCache(paths.lmdbPath);
|
|
209
209
|
const searcher = new searcher_1.Searcher(vectorDb);
|
|
210
210
|
// Start live file watcher
|
|
211
|
-
let fileWatcher = (0, watcher_1.startWatcher)({
|
|
211
|
+
let fileWatcher = yield (0, watcher_1.startWatcher)({
|
|
212
212
|
projectRoot,
|
|
213
213
|
vectorDb,
|
|
214
214
|
metaCache,
|
package/dist/commands/watch.js
CHANGED
|
@@ -190,7 +190,7 @@ exports.watch = new commander_1.Command("watch")
|
|
|
190
190
|
// Open resources for watcher
|
|
191
191
|
const metaCache = new meta_cache_1.MetaCache(paths.lmdbPath);
|
|
192
192
|
// Start watching
|
|
193
|
-
const watcher = (0, watcher_1.startWatcher)({
|
|
193
|
+
const watcher = yield (0, watcher_1.startWatcher)({
|
|
194
194
|
projectRoot,
|
|
195
195
|
vectorDb,
|
|
196
196
|
metaCache,
|
|
@@ -46,7 +46,7 @@ exports.Daemon = void 0;
|
|
|
46
46
|
const fs = __importStar(require("node:fs"));
|
|
47
47
|
const net = __importStar(require("node:net"));
|
|
48
48
|
const path = __importStar(require("node:path"));
|
|
49
|
-
const
|
|
49
|
+
const watcher = __importStar(require("@parcel/watcher"));
|
|
50
50
|
const config_1 = require("../../config");
|
|
51
51
|
const batch_processor_1 = require("../index/batch-processor");
|
|
52
52
|
const watcher_1 = require("../index/watcher");
|
|
@@ -60,8 +60,8 @@ const IDLE_TIMEOUT_MS = 30 * 60 * 1000; // 30 minutes
|
|
|
60
60
|
const HEARTBEAT_INTERVAL_MS = 60 * 1000;
|
|
61
61
|
class Daemon {
|
|
62
62
|
constructor() {
|
|
63
|
-
this.watcher = null;
|
|
64
63
|
this.processors = new Map();
|
|
64
|
+
this.subscriptions = new Map();
|
|
65
65
|
this.vectorDb = null;
|
|
66
66
|
this.metaCache = null;
|
|
67
67
|
this.server = null;
|
|
@@ -70,9 +70,6 @@ class Daemon {
|
|
|
70
70
|
this.heartbeatInterval = null;
|
|
71
71
|
this.idleInterval = null;
|
|
72
72
|
this.shuttingDown = false;
|
|
73
|
-
// Sorted longest-first for prefix matching
|
|
74
|
-
this.sortedRoots = [];
|
|
75
|
-
// Guard against concurrent watchProject/unwatchProject
|
|
76
73
|
this.pendingOps = new Set();
|
|
77
74
|
}
|
|
78
75
|
start() {
|
|
@@ -102,43 +99,23 @@ class Daemon {
|
|
|
102
99
|
}
|
|
103
100
|
// 4. Register daemon (only after resources are open)
|
|
104
101
|
(0, watcher_store_1.registerDaemon)(process.pid);
|
|
105
|
-
// 5.
|
|
102
|
+
// 5. Subscribe to all registered projects
|
|
106
103
|
const projects = (0, project_registry_1.listProjects)().filter((p) => p.status === "indexed");
|
|
107
|
-
const initialRoots = [];
|
|
108
104
|
for (const p of projects) {
|
|
109
|
-
this.
|
|
110
|
-
initialRoots.push(p.root);
|
|
105
|
+
yield this.watchProject(p.root);
|
|
111
106
|
}
|
|
112
|
-
// 6.
|
|
113
|
-
// Daemon always uses polling — watching multiple large project trees
|
|
114
|
-
// with native fs.watch can exhaust file descriptors even on macOS.
|
|
115
|
-
// Polling at 5s intervals is lightweight and reliable for all platforms.
|
|
116
|
-
this.watcher = (0, chokidar_1.watch)(initialRoots, {
|
|
117
|
-
ignored: watcher_1.WATCHER_IGNORE_PATTERNS,
|
|
118
|
-
ignoreInitial: true,
|
|
119
|
-
persistent: true,
|
|
120
|
-
usePolling: true,
|
|
121
|
-
interval: 5000,
|
|
122
|
-
binaryInterval: 10000,
|
|
123
|
-
});
|
|
124
|
-
this.watcher.on("add", (p) => this.routeEvent("change", p));
|
|
125
|
-
this.watcher.on("change", (p) => this.routeEvent("change", p));
|
|
126
|
-
this.watcher.on("unlink", (p) => this.routeEvent("unlink", p));
|
|
127
|
-
this.watcher.on("error", (err) => {
|
|
128
|
-
console.error("[daemon] Watcher error:", err);
|
|
129
|
-
});
|
|
130
|
-
// 7. Heartbeat
|
|
107
|
+
// 6. Heartbeat
|
|
131
108
|
this.heartbeatInterval = setInterval(() => {
|
|
132
109
|
(0, watcher_store_1.heartbeat)(process.pid);
|
|
133
110
|
}, HEARTBEAT_INTERVAL_MS);
|
|
134
|
-
//
|
|
111
|
+
// 7. Idle timeout
|
|
135
112
|
this.idleInterval = setInterval(() => {
|
|
136
113
|
if (Date.now() - this.lastActivity > IDLE_TIMEOUT_MS) {
|
|
137
114
|
console.log("[daemon] Idle for 30 minutes, shutting down");
|
|
138
115
|
this.shutdown();
|
|
139
116
|
}
|
|
140
117
|
}, HEARTBEAT_INTERVAL_MS);
|
|
141
|
-
//
|
|
118
|
+
// 8. Socket server
|
|
142
119
|
this.server = net.createServer((conn) => {
|
|
143
120
|
let buf = "";
|
|
144
121
|
conn.on("data", (chunk) => {
|
|
@@ -162,7 +139,7 @@ class Daemon {
|
|
|
162
139
|
conn.end();
|
|
163
140
|
});
|
|
164
141
|
});
|
|
165
|
-
conn.on("error", () => { });
|
|
142
|
+
conn.on("error", () => { });
|
|
166
143
|
});
|
|
167
144
|
yield new Promise((resolve, reject) => {
|
|
168
145
|
this.server.on("error", (err) => {
|
|
@@ -189,52 +166,57 @@ class Daemon {
|
|
|
189
166
|
return __awaiter(this, void 0, void 0, function* () {
|
|
190
167
|
if (this.processors.has(root) || this.pendingOps.has(root))
|
|
191
168
|
return;
|
|
192
|
-
if (!this.
|
|
169
|
+
if (!this.vectorDb || !this.metaCache)
|
|
193
170
|
return;
|
|
194
|
-
this.
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
171
|
+
this.pendingOps.add(root);
|
|
172
|
+
const processor = new batch_processor_1.ProjectBatchProcessor({
|
|
173
|
+
projectRoot: root,
|
|
174
|
+
vectorDb: this.vectorDb,
|
|
175
|
+
metaCache: this.metaCache,
|
|
176
|
+
dataDir: config_1.PATHS.globalRoot,
|
|
177
|
+
onReindex: (files, ms) => {
|
|
178
|
+
console.log(`[daemon:${path.basename(root)}] Reindexed ${files} file${files !== 1 ? "s" : ""} (${(ms / 1000).toFixed(1)}s)`);
|
|
179
|
+
},
|
|
180
|
+
onActivity: () => {
|
|
181
|
+
this.lastActivity = Date.now();
|
|
182
|
+
},
|
|
183
|
+
});
|
|
184
|
+
this.processors.set(root, processor);
|
|
185
|
+
// Subscribe with @parcel/watcher — native backend, no polling
|
|
186
|
+
const sub = yield watcher.subscribe(root, (err, events) => {
|
|
187
|
+
if (err) {
|
|
188
|
+
console.error(`[daemon:${path.basename(root)}] Watcher error:`, err);
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
for (const event of events) {
|
|
192
|
+
processor.handleFileEvent(event.type === "delete" ? "unlink" : "change", event.path);
|
|
193
|
+
}
|
|
214
194
|
this.lastActivity = Date.now();
|
|
215
|
-
},
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
195
|
+
}, { ignore: watcher_1.WATCHER_IGNORE_GLOBS });
|
|
196
|
+
this.subscriptions.set(root, sub);
|
|
197
|
+
(0, watcher_store_1.registerWatcher)({
|
|
198
|
+
pid: process.pid,
|
|
199
|
+
projectRoot: root,
|
|
200
|
+
startTime: Date.now(),
|
|
201
|
+
status: "watching",
|
|
202
|
+
lastHeartbeat: Date.now(),
|
|
203
|
+
});
|
|
204
|
+
this.pendingOps.delete(root);
|
|
205
|
+
console.log(`[daemon] Watching ${root}`);
|
|
225
206
|
});
|
|
226
|
-
this.pendingOps.delete(root);
|
|
227
207
|
}
|
|
228
208
|
unwatchProject(root) {
|
|
229
209
|
return __awaiter(this, void 0, void 0, function* () {
|
|
230
|
-
var _a;
|
|
231
210
|
const processor = this.processors.get(root);
|
|
232
211
|
if (!processor)
|
|
233
212
|
return;
|
|
234
213
|
yield processor.close();
|
|
235
|
-
|
|
214
|
+
const sub = this.subscriptions.get(root);
|
|
215
|
+
if (sub) {
|
|
216
|
+
yield sub.unsubscribe();
|
|
217
|
+
this.subscriptions.delete(root);
|
|
218
|
+
}
|
|
236
219
|
this.processors.delete(root);
|
|
237
|
-
this.rebuildSortedRoots();
|
|
238
220
|
(0, watcher_store_1.unregisterWatcherByRoot)(root);
|
|
239
221
|
console.log(`[daemon] Unwatched ${root}`);
|
|
240
222
|
});
|
|
@@ -250,7 +232,7 @@ class Daemon {
|
|
|
250
232
|
}
|
|
251
233
|
shutdown() {
|
|
252
234
|
return __awaiter(this, void 0, void 0, function* () {
|
|
253
|
-
var _a, _b, _c
|
|
235
|
+
var _a, _b, _c;
|
|
254
236
|
if (this.shuttingDown)
|
|
255
237
|
return;
|
|
256
238
|
this.shuttingDown = true;
|
|
@@ -263,17 +245,20 @@ class Daemon {
|
|
|
263
245
|
for (const processor of this.processors.values()) {
|
|
264
246
|
yield processor.close();
|
|
265
247
|
}
|
|
266
|
-
//
|
|
267
|
-
|
|
268
|
-
|
|
248
|
+
// Unsubscribe all watchers
|
|
249
|
+
for (const sub of this.subscriptions.values()) {
|
|
250
|
+
try {
|
|
251
|
+
yield sub.unsubscribe();
|
|
252
|
+
}
|
|
253
|
+
catch (_d) { }
|
|
269
254
|
}
|
|
270
|
-
|
|
255
|
+
this.subscriptions.clear();
|
|
271
256
|
// Close server + socket
|
|
272
|
-
(
|
|
257
|
+
(_a = this.server) === null || _a === void 0 ? void 0 : _a.close();
|
|
273
258
|
try {
|
|
274
259
|
fs.unlinkSync(config_1.PATHS.daemonSocket);
|
|
275
260
|
}
|
|
276
|
-
catch (
|
|
261
|
+
catch (_e) { }
|
|
277
262
|
// Unregister all
|
|
278
263
|
for (const root of this.processors.keys()) {
|
|
279
264
|
(0, watcher_store_1.unregisterWatcherByRoot)(root);
|
|
@@ -282,33 +267,15 @@ class Daemon {
|
|
|
282
267
|
this.processors.clear();
|
|
283
268
|
// Close shared resources
|
|
284
269
|
try {
|
|
285
|
-
yield ((
|
|
270
|
+
yield ((_b = this.metaCache) === null || _b === void 0 ? void 0 : _b.close());
|
|
286
271
|
}
|
|
287
|
-
catch (
|
|
272
|
+
catch (_f) { }
|
|
288
273
|
try {
|
|
289
|
-
yield ((
|
|
274
|
+
yield ((_c = this.vectorDb) === null || _c === void 0 ? void 0 : _c.close());
|
|
290
275
|
}
|
|
291
|
-
catch (
|
|
276
|
+
catch (_g) { }
|
|
292
277
|
console.log("[daemon] Shutdown complete");
|
|
293
278
|
});
|
|
294
279
|
}
|
|
295
|
-
routeEvent(event, absPath) {
|
|
296
|
-
const processor = this.findProcessor(absPath);
|
|
297
|
-
if (processor) {
|
|
298
|
-
processor.handleFileEvent(event, absPath);
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
findProcessor(absPath) {
|
|
302
|
-
// sortedRoots is longest-first, so first match is the most specific
|
|
303
|
-
for (const root of this.sortedRoots) {
|
|
304
|
-
if (absPath.startsWith(root) && (absPath.length === root.length || absPath[root.length] === "/")) {
|
|
305
|
-
return this.processors.get(root);
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
return undefined;
|
|
309
|
-
}
|
|
310
|
-
rebuildSortedRoots() {
|
|
311
|
-
this.sortedRoots = [...this.processors.keys()].sort((a, b) => b.length - a.length);
|
|
312
|
-
}
|
|
313
280
|
}
|
|
314
281
|
exports.Daemon = Daemon;
|
|
@@ -42,52 +42,46 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
42
42
|
});
|
|
43
43
|
};
|
|
44
44
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
|
-
exports.
|
|
45
|
+
exports.WATCHER_IGNORE_GLOBS = void 0;
|
|
46
46
|
exports.startWatcher = startWatcher;
|
|
47
|
-
const
|
|
48
|
-
const chokidar_1 = require("chokidar");
|
|
47
|
+
const watcher = __importStar(require("@parcel/watcher"));
|
|
49
48
|
const batch_processor_1 = require("./batch-processor");
|
|
50
|
-
//
|
|
51
|
-
//
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
"
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
"
|
|
61
|
-
"
|
|
62
|
-
"
|
|
63
|
-
"
|
|
64
|
-
"
|
|
65
|
-
"
|
|
66
|
-
/(^|[/\\])\../, // dotfiles
|
|
49
|
+
// Ignore patterns for @parcel/watcher (micromatch globs + directory names).
|
|
50
|
+
// Directory names are matched at any depth automatically.
|
|
51
|
+
exports.WATCHER_IGNORE_GLOBS = [
|
|
52
|
+
"node_modules",
|
|
53
|
+
".git",
|
|
54
|
+
".gmax",
|
|
55
|
+
"dist",
|
|
56
|
+
"build",
|
|
57
|
+
"out",
|
|
58
|
+
"target",
|
|
59
|
+
"__pycache__",
|
|
60
|
+
"coverage",
|
|
61
|
+
"venv",
|
|
62
|
+
".next",
|
|
63
|
+
"lancedb",
|
|
64
|
+
".*", // dotfiles
|
|
67
65
|
];
|
|
68
66
|
function startWatcher(opts) {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
67
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
68
|
+
const { projectRoot } = opts;
|
|
69
|
+
const wtag = `watch:${projectRoot.split("/").pop()}`;
|
|
70
|
+
const processor = new batch_processor_1.ProjectBatchProcessor(opts);
|
|
71
|
+
const subscription = yield watcher.subscribe(projectRoot, (err, events) => {
|
|
72
|
+
if (err) {
|
|
73
|
+
console.error(`[${wtag}] Watcher error:`, err);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
for (const event of events) {
|
|
77
|
+
processor.handleFileEvent(event.type === "delete" ? "unlink" : "change", event.path);
|
|
78
|
+
}
|
|
79
|
+
}, { ignore: exports.WATCHER_IGNORE_GLOBS });
|
|
80
|
+
return {
|
|
81
|
+
close: () => __awaiter(this, void 0, void 0, function* () {
|
|
82
|
+
yield processor.close();
|
|
83
|
+
yield subscription.unsubscribe();
|
|
84
|
+
}),
|
|
85
|
+
};
|
|
83
86
|
});
|
|
84
|
-
watcher.on("add", (p) => processor.handleFileEvent("change", p));
|
|
85
|
-
watcher.on("change", (p) => processor.handleFileEvent("change", p));
|
|
86
|
-
watcher.on("unlink", (p) => processor.handleFileEvent("unlink", p));
|
|
87
|
-
return {
|
|
88
|
-
close: () => __awaiter(this, void 0, void 0, function* () {
|
|
89
|
-
yield processor.close();
|
|
90
|
-
yield watcher.close();
|
|
91
|
-
}),
|
|
92
|
-
};
|
|
93
87
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "grepmax",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.2",
|
|
4
4
|
"author": "Robert Owens <robowens@me.com>",
|
|
5
5
|
"homepage": "https://github.com/reowens/grepmax",
|
|
6
6
|
"bugs": {
|
|
@@ -36,9 +36,9 @@
|
|
|
36
36
|
"@huggingface/transformers": "^4.0.0",
|
|
37
37
|
"@lancedb/lancedb": "^0.27.1",
|
|
38
38
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
39
|
+
"@parcel/watcher": "^2.5.6",
|
|
39
40
|
"apache-arrow": "^18.1.0",
|
|
40
41
|
"chalk": "^5.6.2",
|
|
41
|
-
"chokidar": "^5.0.0",
|
|
42
42
|
"cli-highlight": "^2.1.11",
|
|
43
43
|
"commander": "^14.0.2",
|
|
44
44
|
"dotenv": "^17.2.3",
|