grepmax 0.8.2 → 0.9.1

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.
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.spawnDaemon = spawnDaemon;
37
+ const node_child_process_1 = require("node:child_process");
38
+ const path = __importStar(require("node:path"));
39
+ const config_1 = require("../../config");
40
+ const log_rotate_1 = require("./log-rotate");
41
+ /**
42
+ * Spawn the daemon in background mode.
43
+ * Returns the child PID, or null on failure.
44
+ */
45
+ function spawnDaemon() {
46
+ var _a;
47
+ try {
48
+ const logFile = path.join(config_1.PATHS.logsDir, "daemon.log");
49
+ const out = (0, log_rotate_1.openRotatedLog)(logFile);
50
+ const child = (0, node_child_process_1.spawn)(process.argv[0], [process.argv[1], "watch", "--daemon", "-b"], { detached: true, stdio: ["ignore", out, out] });
51
+ child.unref();
52
+ return (_a = child.pid) !== null && _a !== void 0 ? _a : null;
53
+ }
54
+ catch (err) {
55
+ const msg = err instanceof Error ? err.message : String(err);
56
+ console.error(`[daemon-launcher] Failed to spawn daemon: ${msg}`);
57
+ return null;
58
+ }
59
+ }
@@ -2,47 +2,89 @@
2
2
  /**
3
3
  * Centralized watcher launch logic.
4
4
  * Single function that all code paths use to spawn a watcher.
5
+ * Tries daemon IPC first, falls back to per-project spawn.
5
6
  */
7
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
8
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
9
+ return new (P || (P = Promise))(function (resolve, reject) {
10
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
11
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
12
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
13
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
14
+ });
15
+ };
6
16
  Object.defineProperty(exports, "__esModule", { value: true });
7
17
  exports.launchWatcher = launchWatcher;
8
18
  const node_child_process_1 = require("node:child_process");
19
+ const daemon_client_1 = require("./daemon-client");
20
+ const daemon_launcher_1 = require("./daemon-launcher");
9
21
  const project_registry_1 = require("./project-registry");
10
22
  const watcher_store_1 = require("./watcher-store");
11
23
  function launchWatcher(projectRoot) {
12
- var _a;
13
- // 1. Project must be registered
14
- const project = (0, project_registry_1.getProject)(projectRoot);
15
- if (!project) {
16
- return {
17
- ok: false,
18
- reason: "not-registered",
19
- message: `Project not registered. Run: gmax add ${projectRoot}`,
20
- };
21
- }
22
- // 2. Check if watcher already running
23
- const existing = (_a = (0, watcher_store_1.getWatcherForProject)(projectRoot)) !== null && _a !== void 0 ? _a : (0, watcher_store_1.getWatcherCoveringPath)(projectRoot);
24
- if (existing && (0, watcher_store_1.isProcessRunning)(existing.pid)) {
25
- return { ok: true, pid: existing.pid, reused: true };
26
- }
27
- // 3. Spawn
28
- try {
29
- const child = (0, node_child_process_1.spawn)(process.argv[0], [process.argv[1], "watch", "--path", projectRoot, "-b"], { detached: true, stdio: "ignore" });
30
- child.unref();
31
- if (child.pid) {
32
- return { ok: true, pid: child.pid, reused: false };
33
- }
34
- return {
35
- ok: false,
36
- reason: "spawn-failed",
37
- message: `Spawn returned no PID for ${projectRoot}`,
38
- };
39
- }
40
- catch (err) {
41
- const msg = err instanceof Error ? err.message : String(err);
42
- return {
43
- ok: false,
44
- reason: "spawn-failed",
45
- message: `Failed to start watcher for ${projectRoot}: ${msg}`,
46
- };
47
- }
24
+ return __awaiter(this, void 0, void 0, function* () {
25
+ var _a;
26
+ // 1. Project must be registered
27
+ const project = (0, project_registry_1.getProject)(projectRoot);
28
+ if (!project) {
29
+ return {
30
+ ok: false,
31
+ reason: "not-registered",
32
+ message: `Project not registered. Run: gmax add ${projectRoot}`,
33
+ };
34
+ }
35
+ // 2. Check if watcher already running (daemon registers per-project entries)
36
+ const existing = (_a = (0, watcher_store_1.getWatcherForProject)(projectRoot)) !== null && _a !== void 0 ? _a : (0, watcher_store_1.getWatcherCoveringPath)(projectRoot);
37
+ if (existing && (0, watcher_store_1.isProcessRunning)(existing.pid)) {
38
+ return { ok: true, pid: existing.pid, reused: true };
39
+ }
40
+ // 3. Try daemon IPC
41
+ let resp;
42
+ try {
43
+ resp = yield (0, daemon_client_1.sendDaemonCommand)({ cmd: "watch", root: projectRoot });
44
+ }
45
+ catch (_b) {
46
+ resp = { ok: false, error: "request-failed" };
47
+ }
48
+ if (resp.ok && typeof resp.pid === "number") {
49
+ return { ok: true, pid: resp.pid, reused: true };
50
+ }
51
+ // 4. Daemon not running — try to start it, poll until ready
52
+ const error = resp.error;
53
+ if (error === "ENOENT" || error === "ECONNREFUSED") {
54
+ const daemonPid = (0, daemon_launcher_1.spawnDaemon)();
55
+ if (daemonPid) {
56
+ for (let i = 0; i < 25; i++) {
57
+ yield new Promise((r) => setTimeout(r, 200));
58
+ try {
59
+ const retry = yield (0, daemon_client_1.sendDaemonCommand)({ cmd: "watch", root: projectRoot });
60
+ if (retry.ok && typeof retry.pid === "number") {
61
+ return { ok: true, pid: retry.pid, reused: false };
62
+ }
63
+ }
64
+ catch (_c) { }
65
+ }
66
+ }
67
+ }
68
+ // 5. Fall back to per-project spawn
69
+ try {
70
+ const child = (0, node_child_process_1.spawn)(process.argv[0], [process.argv[1], "watch", "--path", projectRoot, "-b"], { detached: true, stdio: "ignore" });
71
+ child.unref();
72
+ if (child.pid) {
73
+ return { ok: true, pid: child.pid, reused: false };
74
+ }
75
+ return {
76
+ ok: false,
77
+ reason: "spawn-failed",
78
+ message: `Spawn returned no PID for ${projectRoot}`,
79
+ };
80
+ }
81
+ catch (err) {
82
+ const msg = err instanceof Error ? err.message : String(err);
83
+ return {
84
+ ok: false,
85
+ reason: "spawn-failed",
86
+ message: `Failed to start watcher for ${projectRoot}: ${msg}`,
87
+ };
88
+ }
89
+ });
48
90
  }
@@ -41,6 +41,7 @@ var __importStar = (this && this.__importStar) || (function () {
41
41
  };
42
42
  })();
43
43
  Object.defineProperty(exports, "__esModule", { value: true });
44
+ exports.DAEMON_KEY = void 0;
44
45
  exports.registerWatcher = registerWatcher;
45
46
  exports.updateWatcherStatus = updateWatcherStatus;
46
47
  exports.heartbeat = heartbeat;
@@ -49,6 +50,10 @@ exports.getWatcherForProject = getWatcherForProject;
49
50
  exports.getWatcherCoveringPath = getWatcherCoveringPath;
50
51
  exports.listWatchers = listWatchers;
51
52
  exports.migrateFromJson = migrateFromJson;
53
+ exports.registerDaemon = registerDaemon;
54
+ exports.unregisterDaemon = unregisterDaemon;
55
+ exports.getDaemonInfo = getDaemonInfo;
56
+ exports.unregisterWatcherByRoot = unregisterWatcherByRoot;
52
57
  exports.isProcessRunning = isProcessRunning;
53
58
  const fs = __importStar(require("node:fs"));
54
59
  const path = __importStar(require("node:path"));
@@ -154,6 +159,8 @@ function listWatchers() {
154
159
  for (const { key, value } of db.getRange()) {
155
160
  if (!value)
156
161
  continue;
162
+ if (String(key) === exports.DAEMON_KEY)
163
+ continue;
157
164
  if (isAlive(value)) {
158
165
  alive.push(value);
159
166
  }
@@ -191,3 +198,38 @@ function migrateFromJson() {
191
198
  // Best effort — ignore
192
199
  }
193
200
  }
201
+ // --- Daemon registry ---
202
+ exports.DAEMON_KEY = "__daemon__";
203
+ function registerDaemon(pid) {
204
+ getDb().put(exports.DAEMON_KEY, {
205
+ pid,
206
+ projectRoot: exports.DAEMON_KEY,
207
+ startTime: Date.now(),
208
+ status: "watching",
209
+ lastHeartbeat: Date.now(),
210
+ });
211
+ }
212
+ function unregisterDaemon() {
213
+ try {
214
+ getDb().remove(exports.DAEMON_KEY);
215
+ }
216
+ catch (_a) { }
217
+ }
218
+ function getDaemonInfo() {
219
+ const info = getDb().get(exports.DAEMON_KEY);
220
+ if (info && isAlive(info))
221
+ return info;
222
+ if (info) {
223
+ try {
224
+ getDb().remove(exports.DAEMON_KEY);
225
+ }
226
+ catch (_a) { }
227
+ }
228
+ return undefined;
229
+ }
230
+ function unregisterWatcherByRoot(projectRoot) {
231
+ try {
232
+ getDb().remove(projectRoot);
233
+ }
234
+ catch (_a) { }
235
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "grepmax",
3
- "version": "0.8.2",
3
+ "version": "0.9.1",
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.8.2",
3
+ "version": "0.9.1",
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",
@@ -86,9 +86,14 @@ function isProjectRegistered() {
86
86
  function startWatcher() {
87
87
  if (!isProjectRegistered()) return;
88
88
  try {
89
- execFileSync("gmax", ["watch", "-b"], { timeout: 5000, stdio: "ignore" });
89
+ execFileSync("gmax", ["watch", "--daemon", "-b"], { timeout: 5000, stdio: "ignore" });
90
90
  } catch {
91
- // Watcher may already be running or gmax not in PATH — ignore
91
+ // Fallback to per-project mode (older gmax without --daemon)
92
+ try {
93
+ execFileSync("gmax", ["watch", "-b"], { timeout: 5000, stdio: "ignore" });
94
+ } catch {
95
+ // Watcher may already be running or gmax not in PATH — ignore
96
+ }
92
97
  }
93
98
  }
94
99