grepmax 0.14.7 → 0.14.9

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.
@@ -131,7 +131,10 @@ class Daemon {
131
131
  stale: 120000,
132
132
  onCompromised: () => {
133
133
  console.error("[daemon] Lock compromised — another daemon took over. Shutting down.");
134
- this.shutdown();
134
+ // Force exit after timeout — shutdown() is async and may not fully
135
+ // clear event loop references, leaving zombie daemon processes.
136
+ setTimeout(() => process.exit(1), 10000).unref();
137
+ this.shutdown().finally(() => process.exit(0));
135
138
  },
136
139
  });
137
140
  (0, logger_1.debug)("daemon", "lock acquired");
@@ -143,20 +146,77 @@ class Daemon {
143
146
  }
144
147
  throw err;
145
148
  }
146
- // 2. Kill existing per-project watchers
149
+ // 2. Stale socket cleanup + start socket server EARLY.
150
+ // The socket must be listening before the PID file is written so that
151
+ // other daemons checking isDaemonRunning() never see a PID for a process
152
+ // that can't respond to pings. Without this, the slow initialization
153
+ // steps below (LanceDB, MLX, project watchers) create a window where
154
+ // new daemons kill this one as "unresponsive".
155
+ try {
156
+ fs.unlinkSync(config_1.PATHS.daemonSocket);
157
+ }
158
+ catch (_b) { }
159
+ this.server = net.createServer((conn) => {
160
+ (0, logger_1.debug)("daemon", "client connected");
161
+ let buf = "";
162
+ conn.on("data", (chunk) => {
163
+ buf += chunk.toString();
164
+ if (buf.length > 1000000) {
165
+ conn.destroy();
166
+ return;
167
+ }
168
+ const nl = buf.indexOf("\n");
169
+ if (nl === -1)
170
+ return;
171
+ const line = buf.slice(0, nl);
172
+ buf = buf.slice(nl + 1);
173
+ let cmd;
174
+ try {
175
+ cmd = JSON.parse(line);
176
+ }
177
+ catch (_a) {
178
+ conn.write(`${JSON.stringify({ ok: false, error: "invalid JSON" })}\n`);
179
+ conn.end();
180
+ return;
181
+ }
182
+ (0, ipc_handler_1.handleCommand)(this, cmd, conn).then((resp) => {
183
+ // null means the handler is managing the connection (streaming)
184
+ if (resp !== null) {
185
+ conn.write(`${JSON.stringify(resp)}\n`);
186
+ conn.end();
187
+ }
188
+ });
189
+ });
190
+ conn.on("error", () => { });
191
+ });
192
+ yield new Promise((resolve, reject) => {
193
+ this.server.on("error", (err) => {
194
+ const code = err.code;
195
+ if (code === "EADDRINUSE") {
196
+ console.error("[daemon] Socket already in use");
197
+ reject(err);
198
+ }
199
+ else if (code === "EOPNOTSUPP") {
200
+ console.error("[daemon] Filesystem does not support Unix sockets");
201
+ process.exitCode = 2;
202
+ reject(err);
203
+ }
204
+ else {
205
+ reject(err);
206
+ }
207
+ });
208
+ this.server.listen(config_1.PATHS.daemonSocket, () => resolve());
209
+ });
210
+ // 3. Write PID file AFTER socket is listening — ensures any process that
211
+ // reads the PID can immediately ping this daemon and get a response.
212
+ fs.writeFileSync(config_1.PATHS.daemonPidFile, String(process.pid));
213
+ // 4. Kill existing per-project watchers
147
214
  const existing = (0, watcher_store_1.listWatchers)();
148
215
  for (const w of existing) {
149
216
  console.log(`[daemon] Taking over from per-project watcher (PID: ${w.pid}, ${path.basename(w.projectRoot)})`);
150
217
  yield (0, process_1.killProcess)(w.pid);
151
218
  (0, watcher_store_1.unregisterWatcher)(w.pid);
152
219
  }
153
- // 3. Write PID file (informational only — lock is the real guard)
154
- fs.writeFileSync(config_1.PATHS.daemonPidFile, String(process.pid));
155
- // 4. Stale socket cleanup
156
- try {
157
- fs.unlinkSync(config_1.PATHS.daemonSocket);
158
- }
159
- catch (_b) { }
160
220
  // 5. Open shared resources
161
221
  try {
162
222
  fs.mkdirSync(config_1.PATHS.cacheDir, { recursive: true });
@@ -219,58 +279,6 @@ class Daemon {
219
279
  this.shutdown();
220
280
  }
221
281
  }, HEARTBEAT_INTERVAL_MS);
222
- // 11. Socket server
223
- this.server = net.createServer((conn) => {
224
- (0, logger_1.debug)("daemon", "client connected");
225
- let buf = "";
226
- conn.on("data", (chunk) => {
227
- buf += chunk.toString();
228
- if (buf.length > 1000000) {
229
- conn.destroy();
230
- return;
231
- }
232
- const nl = buf.indexOf("\n");
233
- if (nl === -1)
234
- return;
235
- const line = buf.slice(0, nl);
236
- buf = buf.slice(nl + 1);
237
- let cmd;
238
- try {
239
- cmd = JSON.parse(line);
240
- }
241
- catch (_a) {
242
- conn.write(`${JSON.stringify({ ok: false, error: "invalid JSON" })}\n`);
243
- conn.end();
244
- return;
245
- }
246
- (0, ipc_handler_1.handleCommand)(this, cmd, conn).then((resp) => {
247
- // null means the handler is managing the connection (streaming)
248
- if (resp !== null) {
249
- conn.write(`${JSON.stringify(resp)}\n`);
250
- conn.end();
251
- }
252
- });
253
- });
254
- conn.on("error", () => { });
255
- });
256
- yield new Promise((resolve, reject) => {
257
- this.server.on("error", (err) => {
258
- const code = err.code;
259
- if (code === "EADDRINUSE") {
260
- console.error("[daemon] Socket already in use");
261
- reject(err);
262
- }
263
- else if (code === "EOPNOTSUPP") {
264
- console.error("[daemon] Filesystem does not support Unix sockets");
265
- process.exitCode = 2;
266
- reject(err);
267
- }
268
- else {
269
- reject(err);
270
- }
271
- });
272
- this.server.listen(config_1.PATHS.daemonSocket, () => resolve());
273
- });
274
282
  console.log(`[daemon] Started (PID: ${process.pid}, ${this.processors.size} projects)`);
275
283
  });
276
284
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "grepmax",
3
- "version": "0.14.7",
3
+ "version": "0.14.9",
4
4
  "author": "Robert Owens <78518764+reowens@users.noreply.github.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.14.7",
3
+ "version": "0.14.9",
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",