grepmax 0.7.31 → 0.7.33

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.
@@ -380,6 +380,7 @@ exports.serve = new commander_1.Command("serve")
380
380
  // Ensure we exit if server fails to start
381
381
  process.exit(1);
382
382
  });
383
+ server.setTimeout(60000); // 60s request timeout
383
384
  server.listen(port, () => {
384
385
  const address = server.address();
385
386
  const actualPort = typeof address === "object" && address ? address.port : port;
@@ -77,6 +77,8 @@ const DEBOUNCE_MS = 2000;
77
77
  const FTS_REBUILD_INTERVAL_MS = 5 * 60 * 1000;
78
78
  function startWatcher(opts) {
79
79
  const { projectRoot, vectorDb, metaCache, dataDir, onReindex } = opts;
80
+ const projectName = path.basename(projectRoot);
81
+ const wtag = `watch:${projectName}`;
80
82
  const pending = new Map();
81
83
  const retryCount = new Map();
82
84
  let debounceTimer = null;
@@ -96,14 +98,19 @@ function startWatcher(opts) {
96
98
  binaryInterval: 10000,
97
99
  });
98
100
  watcher.on("error", (err) => {
99
- console.error("[watch] Watcher error:", err);
101
+ console.error(`[${wtag}] Watcher error:`, err);
100
102
  });
101
103
  const scheduleBatch = () => {
102
104
  if (debounceTimer)
103
105
  clearTimeout(debounceTimer);
104
106
  debounceTimer = setTimeout(() => processBatch(), DEBOUNCE_MS);
105
107
  };
106
- const BATCH_TIMEOUT_MS = 120000;
108
+ const taskTimeoutMs = (() => {
109
+ var _a;
110
+ const fromEnv = Number.parseInt((_a = process.env.GMAX_WORKER_TASK_TIMEOUT_MS) !== null && _a !== void 0 ? _a : "", 10);
111
+ return Number.isFinite(fromEnv) && fromEnv > 0 ? fromEnv : 120000;
112
+ })();
113
+ const BATCH_TIMEOUT_MS = Math.max(Math.ceil(taskTimeoutMs * 1.5), 120000);
107
114
  const processBatch = () => __awaiter(this, void 0, void 0, function* () {
108
115
  var _a;
109
116
  if (closed || processing || pending.size === 0)
@@ -111,13 +118,13 @@ function startWatcher(opts) {
111
118
  processing = true;
112
119
  const batchTimeout = setTimeout(() => {
113
120
  if (processing) {
114
- console.error("[watch] Batch processing timed out after 120s, resetting");
121
+ console.error(`[${wtag}] Batch processing timed out after 120s, resetting`);
115
122
  processing = false;
116
123
  }
117
124
  }, BATCH_TIMEOUT_MS);
118
125
  const batch = new Map(pending);
119
126
  pending.clear();
120
- (0, logger_1.log)("watch", `Processing ${batch.size} changed files`);
127
+ (0, logger_1.log)(wtag, `Processing ${batch.size} changed files`);
121
128
  const start = Date.now();
122
129
  let reindexed = 0;
123
130
  const changedIds = [];
@@ -188,7 +195,11 @@ function startWatcher(opts) {
188
195
  reindexed++;
189
196
  }
190
197
  else {
191
- console.error(`[watch] Failed to process ${absPath}:`, err);
198
+ console.error(`[${wtag}] Failed to process ${absPath}:`, err);
199
+ if (!pool.isHealthy()) {
200
+ console.error(`[${wtag}] Worker pool unhealthy, aborting batch`);
201
+ break;
202
+ }
192
203
  }
193
204
  }
194
205
  }
@@ -216,10 +227,11 @@ function startWatcher(opts) {
216
227
  finally {
217
228
  yield lock.release();
218
229
  }
230
+ const duration = Date.now() - start;
219
231
  if (reindexed > 0) {
220
- const duration = Date.now() - start;
221
232
  onReindex === null || onReindex === void 0 ? void 0 : onReindex(reindexed, duration);
222
233
  }
234
+ (0, logger_1.log)(wtag, `Batch complete: ${batch.size} files, ${reindexed} reindexed (${(duration / 1000).toFixed(1)}s)`);
223
235
  consecutiveLockFailures = 0;
224
236
  for (const absPath of batch.keys()) {
225
237
  retryCount.delete(absPath);
@@ -230,7 +242,7 @@ function startWatcher(opts) {
230
242
  if (isLockError) {
231
243
  consecutiveLockFailures++;
232
244
  }
233
- console.error("[watch] Batch processing failed:", err);
245
+ console.error(`[${wtag}] Batch processing failed:`, err);
234
246
  let dropped = 0;
235
247
  for (const [absPath, event] of batch) {
236
248
  const count = ((_a = retryCount.get(absPath)) !== null && _a !== void 0 ? _a : 0) + 1;
@@ -244,7 +256,7 @@ function startWatcher(opts) {
244
256
  }
245
257
  }
246
258
  if (dropped > 0) {
247
- console.warn(`[watch] Dropped ${dropped} file(s) after ${MAX_RETRIES} failed retries`);
259
+ console.warn(`[${wtag}] Dropped ${dropped} file(s) after ${MAX_RETRIES} failed retries`);
248
260
  }
249
261
  if (pending.size > 0) {
250
262
  const backoffMs = Math.min(DEBOUNCE_MS * Math.pow(2, consecutiveLockFailures), 30000);
@@ -320,7 +332,7 @@ function startWatcher(opts) {
320
332
  yield vectorDb.createFTSIndex();
321
333
  }
322
334
  catch (err) {
323
- console.error("[watch] FTS rebuild failed:", err);
335
+ console.error(`[${wtag}] FTS rebuild failed:`, err);
324
336
  }
325
337
  }), FTS_REBUILD_INTERVAL_MS);
326
338
  ftsInterval.unref();
@@ -95,13 +95,9 @@ function postJSON(path, body) {
95
95
  /**
96
96
  * Check if MLX server is reachable. Caches result for CHECK_INTERVAL_MS.
97
97
  */
98
- function isMlxUp() {
98
+ function checkHealth() {
99
99
  return __awaiter(this, void 0, void 0, function* () {
100
- const now = Date.now();
101
- if (mlxAvailable !== null && now - lastCheck < CHECK_INTERVAL_MS) {
102
- return mlxAvailable;
103
- }
104
- const result = yield new Promise((resolve) => {
100
+ return new Promise((resolve) => {
105
101
  const req = http.get({ hostname: MLX_HOST, port: MLX_PORT, path: "/health", timeout: 2000 }, (res) => {
106
102
  res.resume();
107
103
  resolve(res.statusCode === 200);
@@ -112,6 +108,20 @@ function isMlxUp() {
112
108
  resolve(false);
113
109
  });
114
110
  });
111
+ });
112
+ }
113
+ function isMlxUp() {
114
+ return __awaiter(this, void 0, void 0, function* () {
115
+ const now = Date.now();
116
+ if (mlxAvailable !== null && now - lastCheck < CHECK_INTERVAL_MS) {
117
+ return mlxAvailable;
118
+ }
119
+ let result = yield checkHealth();
120
+ // On first check (cold start), retry once after 3s — server may still be loading
121
+ if (!result && mlxAvailable === null) {
122
+ yield new Promise((r) => setTimeout(r, 3000));
123
+ result = yield checkHealth();
124
+ }
115
125
  mlxAvailable = result;
116
126
  lastCheck = now;
117
127
  return result;
@@ -123,6 +123,9 @@ class WorkerPool {
123
123
  this.spawnWorker();
124
124
  }
125
125
  }
126
+ isHealthy() {
127
+ return !this.destroyed && this.workers.length > 0;
128
+ }
126
129
  clearTaskTimeout(task) {
127
130
  if (task.timeout) {
128
131
  clearTimeout(task.timeout);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "grepmax",
3
- "version": "0.7.31",
3
+ "version": "0.7.33",
4
4
  "author": "Robert Owens <robowens@me.com>",
5
5
  "homepage": "https://github.com/reowens/grepmax",
6
6
  "bugs": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "grepmax",
3
- "version": "0.7.31",
3
+ "version": "0.7.33",
4
4
  "description": "Semantic code search for Claude Code. Automatically indexes your project and provides intelligent search capabilities.",
5
5
  "author": {
6
6
  "name": "Robert Owens",