grepmax 0.26.40 → 0.26.41

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.
@@ -48,6 +48,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
48
48
  };
49
49
  Object.defineProperty(exports, "__esModule", { value: true });
50
50
  exports.validateMlxEmbeddingResponse = validateMlxEmbeddingResponse;
51
+ exports.requestMlxJSON = requestMlxJSON;
51
52
  exports.isMlxUp = isMlxUp;
52
53
  exports.mlxEmbed = mlxEmbed;
53
54
  const http = __importStar(require("node:http"));
@@ -81,81 +82,108 @@ function validateMlxEmbeddingResponse(data, textCount, expectedModel, expectedDi
81
82
  Number.isFinite(element) &&
82
83
  Math.abs(element) <= FLOAT32_MAX)));
83
84
  }
84
- function postJSON(reqPath, body) {
85
+ /** A hard deadline also bounds stalled/truncated response bodies. */
86
+ function requestMlxJSON(reqPath, body) {
85
87
  const start = performance.now();
86
88
  return new Promise((resolve) => {
87
- const payload = JSON.stringify(body);
89
+ const payload = body === undefined ? undefined : JSON.stringify(body);
90
+ let settled = false;
91
+ let deadline;
92
+ const finish = (result) => {
93
+ if (settled)
94
+ return;
95
+ settled = true;
96
+ clearTimeout(deadline);
97
+ resolve(Object.assign(Object.assign({}, result), { ms: Math.round(performance.now() - start) }));
98
+ };
88
99
  const req = http.request({
89
100
  hostname: MLX_HOST,
90
101
  port: MLX_PORT,
91
102
  path: reqPath,
92
- method: "POST",
93
- headers: {
94
- "Content-Type": "application/json",
95
- "Content-Length": Buffer.byteLength(payload),
96
- },
97
- timeout: MLX_TIMEOUT_MS,
103
+ method: payload === undefined ? "GET" : "POST",
104
+ headers: payload === undefined
105
+ ? undefined
106
+ : {
107
+ "Content-Type": "application/json",
108
+ "Content-Length": Buffer.byteLength(payload),
109
+ },
98
110
  }, (res) => {
99
111
  const chunks = [];
100
- res.on("data", (chunk) => chunks.push(chunk));
112
+ let bytes = 0;
113
+ res.on("data", (chunk) => {
114
+ bytes += chunk.length;
115
+ if (bytes > 16 * 1024 * 1024) {
116
+ finish({
117
+ ok: false,
118
+ category: "protocol",
119
+ status: res.statusCode,
120
+ detail: "response exceeds 16MiB",
121
+ });
122
+ req.destroy();
123
+ }
124
+ else
125
+ chunks.push(chunk);
126
+ });
127
+ res.on("aborted", () => finish({
128
+ ok: false,
129
+ category: "transport",
130
+ status: res.statusCode,
131
+ detail: "response aborted",
132
+ }));
133
+ res.on("error", (err) => finish({
134
+ ok: false,
135
+ category: "transport",
136
+ status: res.statusCode,
137
+ detail: err.message.slice(0, 512),
138
+ }));
101
139
  res.on("end", () => {
140
+ const raw = Buffer.concat(chunks).toString("utf8");
141
+ if (res.statusCode !== 200) {
142
+ finish({
143
+ ok: false,
144
+ category: "http",
145
+ status: res.statusCode,
146
+ detail: raw.slice(0, 512),
147
+ });
148
+ return;
149
+ }
102
150
  try {
103
- const data = JSON.parse(Buffer.concat(chunks).toString("utf-8"));
104
- const ok = res.statusCode === 200;
105
- (0, logger_1.debug)("mlx", `POST ${reqPath} → ${res.statusCode} ${(performance.now() - start).toFixed(0)}ms payload=${payload.length}B`);
106
- resolve({ ok, data });
151
+ finish({ ok: true, status: res.statusCode, data: JSON.parse(raw) });
107
152
  }
108
153
  catch (_a) {
109
- (0, logger_1.debug)("mlx", `POST ${reqPath} → parse error ${(performance.now() - start).toFixed(0)}ms`);
110
- resolve({ ok: false });
154
+ finish({
155
+ ok: false,
156
+ category: "protocol",
157
+ status: res.statusCode,
158
+ detail: "invalid JSON",
159
+ });
111
160
  }
112
161
  });
113
162
  });
114
- req.on("error", (err) => {
115
- (0, logger_1.debug)("mlx", `POST ${reqPath} → error: ${err.message} ${(performance.now() - start).toFixed(0)}ms`);
116
- resolve({ ok: false });
117
- });
118
- req.on("timeout", () => {
119
- (0, logger_1.debug)("mlx", `POST ${reqPath} → timeout after ${MLX_TIMEOUT_MS}ms`);
163
+ req.on("error", (err) => finish({
164
+ ok: false,
165
+ category: "transport",
166
+ detail: err.message.slice(0, 512),
167
+ }));
168
+ deadline = setTimeout(() => {
169
+ finish({
170
+ ok: false,
171
+ category: "timeout",
172
+ detail: "request deadline exceeded",
173
+ });
120
174
  req.destroy();
121
- resolve({ ok: false });
122
- });
123
- req.write(payload);
124
- req.end();
175
+ }, reqPath === "/health" ? 2000 : MLX_TIMEOUT_MS);
176
+ req.end(payload);
125
177
  });
126
178
  }
127
- /**
128
- * Check if MLX server is reachable. Caches result for CHECK_INTERVAL_MS.
129
- */
130
179
  function checkHealth(expectedModel) {
131
180
  return __awaiter(this, void 0, void 0, function* () {
132
- const start = performance.now();
133
- return new Promise((resolve) => {
134
- const req = http.get({ hostname: MLX_HOST, port: MLX_PORT, path: "/health", timeout: 2000 }, (res) => {
135
- const chunks = [];
136
- res.on("data", (chunk) => chunks.push(chunk));
137
- res.on("end", () => {
138
- let model;
139
- try {
140
- model = JSON.parse(Buffer.concat(chunks).toString("utf8")).model;
141
- }
142
- catch (_a) { }
143
- const ok = res.statusCode === 200 &&
144
- (!expectedModel || model === expectedModel);
145
- (0, logger_1.debug)("mlx", `health → ${ok ? "ok" : `status=${res.statusCode} model=${String(model)}`} ${(performance.now() - start).toFixed(0)}ms`);
146
- resolve(ok);
147
- });
148
- });
149
- req.on("error", (err) => {
150
- (0, logger_1.debug)("mlx", `health → error: ${err.message} ${(performance.now() - start).toFixed(0)}ms`);
151
- resolve(false);
152
- });
153
- req.on("timeout", () => {
154
- (0, logger_1.debug)("mlx", `health → timeout ${(performance.now() - start).toFixed(0)}ms`);
155
- req.destroy();
156
- resolve(false);
157
- });
158
- });
181
+ var _a;
182
+ var _b, _c;
183
+ const result = yield requestMlxJSON("/health");
184
+ const ok = result.ok && (!expectedModel || ((_a = result.data) === null || _a === void 0 ? void 0 : _a.model) === expectedModel);
185
+ (0, logger_1.debug)("mlx", `health ok=${ok} category=${(_b = result.category) !== null && _b !== void 0 ? _b : "none"} status=${(_c = result.status) !== null && _c !== void 0 ? _c : "none"} ms=${result.ms}`);
186
+ return ok;
159
187
  });
160
188
  }
161
189
  function isMlxUp(expectedModel) {
@@ -192,7 +220,7 @@ function isMlxUp(expectedModel) {
192
220
  */
193
221
  function mlxEmbed(texts, options) {
194
222
  return __awaiter(this, void 0, void 0, function* () {
195
- var _a;
223
+ var _a, _b, _c, _d, _e, _f, _g;
196
224
  const mode = (_a = options === null || options === void 0 ? void 0 : options.mode) !== null && _a !== void 0 ? _a : EMBED_MODE;
197
225
  if (mode === "cpu")
198
226
  return null;
@@ -201,7 +229,7 @@ function mlxEmbed(texts, options) {
201
229
  (0, logger_1.debug)("mlx", `embed ${texts.length} texts`);
202
230
  let postResult;
203
231
  try {
204
- postResult = yield postJSON("/embed", {
232
+ postResult = yield requestMlxJSON("/embed", {
205
233
  texts,
206
234
  expected_model: options === null || options === void 0 ? void 0 : options.expectedModel,
207
235
  });
@@ -223,17 +251,17 @@ function mlxEmbed(texts, options) {
223
251
  const now = Date.now();
224
252
  if (wasPreviouslyAvailable ||
225
253
  now - lastMlxWarning >= MLX_WARNING_INTERVAL_MS) {
226
- console.error("[mlx] Embed server failed: bad response (ok=" +
227
- ok +
228
- ", validResponse=" +
229
- responseMatches +
230
- ", dim=" +
231
- String(data === null || data === void 0 ? void 0 : data.dim) +
232
- ", model=" +
233
- String(data === null || data === void 0 ? void 0 : data.model) +
234
- ")");
254
+ console.error(`[mlx] Embed failed: category=${(_b = postResult.category) !== null && _b !== void 0 ? _b : "protocol"} status=${(_c = postResult.status) !== null && _c !== void 0 ? _c : "none"} ms=${postResult.ms} batch=${texts.length} detail=${JSON.stringify((_d = postResult.detail) !== null && _d !== void 0 ? _d : `invalid vectors/model: dim=${String(data === null || data === void 0 ? void 0 : data.dim)} model=${String(data === null || data === void 0 ? void 0 : data.model)}`).slice(0, 600)}`);
235
255
  lastMlxWarning = now;
236
256
  }
257
+ if ((postResult.ok && !responseMatches) ||
258
+ postResult.category === "protocol" ||
259
+ (postResult.category === "http" &&
260
+ ((_e = postResult.status) !== null && _e !== void 0 ? _e : 500) < 500 &&
261
+ postResult.status !== 429 &&
262
+ postResult.status !== 408)) {
263
+ throw new Error(`MLX embedding protocol failure: status=${(_f = postResult.status) !== null && _f !== void 0 ? _f : "none"} ${(_g = postResult.detail) !== null && _g !== void 0 ? _g : "invalid vectors or model identity"}`);
264
+ }
237
265
  return null;
238
266
  }
239
267
  return data.vectors.map((v) => new Float32Array(v));
@@ -58,6 +58,7 @@ const file_utils_1 = require("../utils/file-utils");
58
58
  const logger_1 = require("../utils/logger");
59
59
  const path_containment_1 = require("../utils/path-containment");
60
60
  const colbert_math_1 = require("./colbert-math");
61
+ const embedding_error_1 = require("./embedding-error");
61
62
  const colbert_1 = require("./embeddings/colbert");
62
63
  const granite_1 = require("./embeddings/granite");
63
64
  const mlx_client_1 = require("./embeddings/mlx-client");
@@ -158,10 +159,10 @@ class WorkerOrchestrator {
158
159
  expectedDim: this.generation.vectorDim,
159
160
  });
160
161
  if (!mlxResult && !this.allowOnnxFallback) {
161
- throw new Error(`MLX embedding model ${this.generation.mlxModel} is unavailable; ONNX fallback is disabled for this custom embedding generation`);
162
+ throw new embedding_error_1.EmbeddingBackendUnavailableError(`MLX embedding model ${this.generation.mlxModel} is unavailable; ONNX fallback is disabled for this custom embedding generation`);
162
163
  }
163
164
  if (!mlxResult && denseBackend === "mlx") {
164
- throw new Error(`MLX embedding model ${this.generation.mlxModel} became unavailable during this embedding operation`);
165
+ throw new embedding_error_1.EmbeddingBackendUnavailableError(`MLX embedding model ${this.generation.mlxModel} became unavailable during this embedding operation`);
165
166
  }
166
167
  if (!mlxResult && !mlxFallbackWarned) {
167
168
  console.warn("[embed] MLX unavailable, falling back to CPU (ONNX)");
@@ -384,7 +384,7 @@ class WorkerPool {
384
384
  if ("error" in msg) {
385
385
  const filePath = (_f = (_e = (_a = task.payload) === null || _a === void 0 ? void 0 : _a.path) !== null && _e !== void 0 ? _e : (_b = task.payload) === null || _b === void 0 ? void 0 : _b.absolutePath) !== null && _f !== void 0 ? _f : "unknown";
386
386
  (0, logger_1.debug)("pool", `error task=${task.id} method=${task.method} file=${filePath}: ${msg.error}`);
387
- task.reject(new Error(msg.error));
387
+ task.reject(Object.assign(new Error(msg.error), typeof msg.code === "string" ? { code: msg.code } : {}));
388
388
  }
389
389
  else {
390
390
  const elapsed = task.startTime
@@ -93,7 +93,8 @@ const handleMessage = (msg) => __awaiter(void 0, void 0, void 0, function* () {
93
93
  catch (err) {
94
94
  const message = err instanceof Error ? err.message : String(err);
95
95
  (0, logger_1.debug)("worker", `fail task=${id} method=${method} ${(performance.now() - start).toFixed(0)}ms: ${message}`);
96
- send({ id, error: message });
96
+ const code = err === null || err === void 0 ? void 0 : err.code;
97
+ send(Object.assign({ id, error: message }, (typeof code === "string" ? { code } : {})));
97
98
  }
98
99
  });
99
100
  const handleMessageSerially = (0, serialized_handler_1.createSerializedHandler)(handleMessage);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "grepmax",
3
- "version": "0.26.40",
3
+ "version": "0.26.41",
4
4
  "author": "Robert Owens <78518764+reowens@users.noreply.github.com>",
5
5
  "homepage": "https://github.com/reowens/grepmax",
6
6
  "bugs": {
@@ -86,7 +86,6 @@
86
86
  "cli-highlight": "^2.1.11",
87
87
  "commander": "^15.0.0",
88
88
  "dotenv": "^17.4.2",
89
- "fast-glob": "^3.3.3",
90
89
  "ignore": "^7.0.9",
91
90
  "lmdb": "^3.5.6",
92
91
  "numkong": "^7.8.2",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "grepmax",
3
- "version": "0.26.40",
3
+ "version": "0.26.41",
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",