@deeplake/hivemind 0.7.31 → 0.7.32
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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/bundle/cli.js +427 -233
- package/codex/bundle/capture.js +550 -122
- package/codex/bundle/embeddings/embed-daemon.js +55 -4
- package/codex/bundle/pre-tool-use.js +447 -90
- package/codex/bundle/shell/deeplake-shell.js +431 -74
- package/codex/bundle/stop.js +437 -80
- package/codex/bundle/wiki-worker.js +429 -72
- package/cursor/bundle/capture.js +625 -197
- package/cursor/bundle/embeddings/embed-daemon.js +55 -4
- package/cursor/bundle/pre-tool-use.js +432 -75
- package/cursor/bundle/session-start.js +8 -1
- package/cursor/bundle/shell/deeplake-shell.js +431 -74
- package/cursor/bundle/wiki-worker.js +429 -72
- package/hermes/bundle/capture.js +626 -198
- package/hermes/bundle/embeddings/embed-daemon.js +55 -4
- package/hermes/bundle/pre-tool-use.js +431 -74
- package/hermes/bundle/session-start.js +8 -1
- package/hermes/bundle/shell/deeplake-shell.js +431 -74
- package/hermes/bundle/wiki-worker.js +429 -72
- package/openclaw/dist/index.js +1 -1
- package/openclaw/openclaw.plugin.json +1 -1
- package/openclaw/package.json +1 -1
- package/package.json +1 -1
|
@@ -53,13 +53,13 @@ var init_index_marker_store = __esm({
|
|
|
53
53
|
|
|
54
54
|
// dist/src/utils/stdin.js
|
|
55
55
|
function readStdin() {
|
|
56
|
-
return new Promise((
|
|
56
|
+
return new Promise((resolve2, reject) => {
|
|
57
57
|
let data = "";
|
|
58
58
|
process.stdin.setEncoding("utf-8");
|
|
59
59
|
process.stdin.on("data", (chunk) => data += chunk);
|
|
60
60
|
process.stdin.on("end", () => {
|
|
61
61
|
try {
|
|
62
|
-
|
|
62
|
+
resolve2(JSON.parse(data));
|
|
63
63
|
} catch (err) {
|
|
64
64
|
reject(new Error(`Failed to parse hook input: ${err}`));
|
|
65
65
|
}
|
|
@@ -175,7 +175,7 @@ function getQueryTimeoutMs() {
|
|
|
175
175
|
return Number(process.env.HIVEMIND_QUERY_TIMEOUT_MS ?? 1e4);
|
|
176
176
|
}
|
|
177
177
|
function sleep(ms) {
|
|
178
|
-
return new Promise((
|
|
178
|
+
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
179
179
|
}
|
|
180
180
|
function isTimeoutError(error) {
|
|
181
181
|
const name = error instanceof Error ? error.name.toLowerCase() : "";
|
|
@@ -205,7 +205,7 @@ var Semaphore = class {
|
|
|
205
205
|
this.active++;
|
|
206
206
|
return;
|
|
207
207
|
}
|
|
208
|
-
await new Promise((
|
|
208
|
+
await new Promise((resolve2) => this.waiting.push(resolve2));
|
|
209
209
|
}
|
|
210
210
|
release() {
|
|
211
211
|
this.active--;
|
|
@@ -1042,9 +1042,9 @@ function capOutputForClaude(output, options = {}) {
|
|
|
1042
1042
|
// dist/src/embeddings/client.js
|
|
1043
1043
|
import { connect } from "node:net";
|
|
1044
1044
|
import { spawn } from "node:child_process";
|
|
1045
|
-
import { openSync, closeSync, writeSync, unlinkSync, existsSync as
|
|
1046
|
-
import { homedir as
|
|
1047
|
-
import { join as
|
|
1045
|
+
import { openSync as openSync2, closeSync as closeSync2, writeSync, unlinkSync as unlinkSync2, existsSync as existsSync4, readFileSync as readFileSync5 } from "node:fs";
|
|
1046
|
+
import { homedir as homedir6 } from "node:os";
|
|
1047
|
+
import { join as join7 } from "node:path";
|
|
1048
1048
|
|
|
1049
1049
|
// dist/src/embeddings/protocol.js
|
|
1050
1050
|
var DEFAULT_SOCKET_DIR = "/tmp";
|
|
@@ -1057,13 +1057,234 @@ function pidPathFor(uid, dir = DEFAULT_SOCKET_DIR) {
|
|
|
1057
1057
|
return `${dir}/hivemind-embed-${uid}.pid`;
|
|
1058
1058
|
}
|
|
1059
1059
|
|
|
1060
|
+
// dist/src/notifications/queue.js
|
|
1061
|
+
import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, renameSync, mkdirSync as mkdirSync2, openSync, closeSync, unlinkSync, statSync } from "node:fs";
|
|
1062
|
+
import { join as join4, resolve } from "node:path";
|
|
1063
|
+
import { homedir as homedir3 } from "node:os";
|
|
1064
|
+
import { setTimeout as sleep2 } from "node:timers/promises";
|
|
1065
|
+
var log3 = (msg) => log("notifications-queue", msg);
|
|
1066
|
+
var LOCK_RETRY_MAX = 50;
|
|
1067
|
+
var LOCK_RETRY_BASE_MS = 5;
|
|
1068
|
+
var LOCK_STALE_MS = 5e3;
|
|
1069
|
+
function queuePath() {
|
|
1070
|
+
return join4(homedir3(), ".deeplake", "notifications-queue.json");
|
|
1071
|
+
}
|
|
1072
|
+
function lockPath() {
|
|
1073
|
+
return `${queuePath()}.lock`;
|
|
1074
|
+
}
|
|
1075
|
+
function readQueue() {
|
|
1076
|
+
try {
|
|
1077
|
+
const raw = readFileSync3(queuePath(), "utf-8");
|
|
1078
|
+
const parsed = JSON.parse(raw);
|
|
1079
|
+
if (!parsed || !Array.isArray(parsed.queue)) {
|
|
1080
|
+
log3(`queue malformed \u2192 treating as empty`);
|
|
1081
|
+
return { queue: [] };
|
|
1082
|
+
}
|
|
1083
|
+
return { queue: parsed.queue };
|
|
1084
|
+
} catch {
|
|
1085
|
+
return { queue: [] };
|
|
1086
|
+
}
|
|
1087
|
+
}
|
|
1088
|
+
function _isQueuePathInsideHome(path, home) {
|
|
1089
|
+
const r = resolve(path);
|
|
1090
|
+
const h = resolve(home);
|
|
1091
|
+
return r.startsWith(h + "/") || r === h;
|
|
1092
|
+
}
|
|
1093
|
+
function writeQueue(q) {
|
|
1094
|
+
const path = queuePath();
|
|
1095
|
+
const home = resolve(homedir3());
|
|
1096
|
+
if (!_isQueuePathInsideHome(path, home)) {
|
|
1097
|
+
throw new Error(`notifications-queue write blocked: ${path} is outside ${home}`);
|
|
1098
|
+
}
|
|
1099
|
+
mkdirSync2(join4(home, ".deeplake"), { recursive: true, mode: 448 });
|
|
1100
|
+
const tmp = `${path}.${process.pid}.tmp`;
|
|
1101
|
+
writeFileSync2(tmp, JSON.stringify(q, null, 2), { mode: 384 });
|
|
1102
|
+
renameSync(tmp, path);
|
|
1103
|
+
}
|
|
1104
|
+
async function withQueueLock(fn) {
|
|
1105
|
+
const path = lockPath();
|
|
1106
|
+
mkdirSync2(join4(homedir3(), ".deeplake"), { recursive: true, mode: 448 });
|
|
1107
|
+
let fd = null;
|
|
1108
|
+
for (let attempt = 0; attempt < LOCK_RETRY_MAX; attempt++) {
|
|
1109
|
+
try {
|
|
1110
|
+
fd = openSync(path, "wx", 384);
|
|
1111
|
+
break;
|
|
1112
|
+
} catch (e) {
|
|
1113
|
+
const code = e.code;
|
|
1114
|
+
if (code !== "EEXIST")
|
|
1115
|
+
throw e;
|
|
1116
|
+
try {
|
|
1117
|
+
const age = Date.now() - statSync(path).mtimeMs;
|
|
1118
|
+
if (age > LOCK_STALE_MS) {
|
|
1119
|
+
unlinkSync(path);
|
|
1120
|
+
continue;
|
|
1121
|
+
}
|
|
1122
|
+
} catch {
|
|
1123
|
+
}
|
|
1124
|
+
const delay = LOCK_RETRY_BASE_MS * (attempt + 1);
|
|
1125
|
+
await sleep2(delay);
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
if (fd === null) {
|
|
1129
|
+
log3(`lock acquisition gave up after ${LOCK_RETRY_MAX} attempts \u2014 proceeding unlocked (last-writer-wins)`);
|
|
1130
|
+
return fn();
|
|
1131
|
+
}
|
|
1132
|
+
try {
|
|
1133
|
+
return fn();
|
|
1134
|
+
} finally {
|
|
1135
|
+
try {
|
|
1136
|
+
closeSync(fd);
|
|
1137
|
+
} catch {
|
|
1138
|
+
}
|
|
1139
|
+
try {
|
|
1140
|
+
unlinkSync(path);
|
|
1141
|
+
} catch {
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
function sameDedupKey(a, b) {
|
|
1146
|
+
if (a.id !== b.id)
|
|
1147
|
+
return false;
|
|
1148
|
+
return JSON.stringify(a.dedupKey) === JSON.stringify(b.dedupKey);
|
|
1149
|
+
}
|
|
1150
|
+
async function enqueueNotification(n) {
|
|
1151
|
+
await withQueueLock(() => {
|
|
1152
|
+
const q = readQueue();
|
|
1153
|
+
if (q.queue.some((existing) => sameDedupKey(existing, n))) {
|
|
1154
|
+
return;
|
|
1155
|
+
}
|
|
1156
|
+
q.queue.push(n);
|
|
1157
|
+
writeQueue(q);
|
|
1158
|
+
});
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
// dist/src/embeddings/disable.js
|
|
1162
|
+
import { createRequire } from "node:module";
|
|
1163
|
+
import { homedir as homedir5 } from "node:os";
|
|
1164
|
+
import { join as join6 } from "node:path";
|
|
1165
|
+
import { pathToFileURL } from "node:url";
|
|
1166
|
+
|
|
1167
|
+
// dist/src/user-config.js
|
|
1168
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync4, renameSync as renameSync2, writeFileSync as writeFileSync3 } from "node:fs";
|
|
1169
|
+
import { homedir as homedir4 } from "node:os";
|
|
1170
|
+
import { dirname, join as join5 } from "node:path";
|
|
1171
|
+
var _configPath = () => process.env.HIVEMIND_CONFIG_PATH ?? join5(homedir4(), ".deeplake", "config.json");
|
|
1172
|
+
var _cache = null;
|
|
1173
|
+
var _migrated = false;
|
|
1174
|
+
function readUserConfig() {
|
|
1175
|
+
if (_cache !== null)
|
|
1176
|
+
return _cache;
|
|
1177
|
+
const path = _configPath();
|
|
1178
|
+
if (!existsSync3(path)) {
|
|
1179
|
+
_cache = {};
|
|
1180
|
+
return _cache;
|
|
1181
|
+
}
|
|
1182
|
+
try {
|
|
1183
|
+
const raw = readFileSync4(path, "utf-8");
|
|
1184
|
+
const parsed = JSON.parse(raw);
|
|
1185
|
+
_cache = isPlainObject(parsed) ? parsed : {};
|
|
1186
|
+
} catch {
|
|
1187
|
+
_cache = {};
|
|
1188
|
+
}
|
|
1189
|
+
return _cache;
|
|
1190
|
+
}
|
|
1191
|
+
function writeUserConfig(patch) {
|
|
1192
|
+
const current = readUserConfig();
|
|
1193
|
+
const merged = deepMerge(current, patch);
|
|
1194
|
+
const path = _configPath();
|
|
1195
|
+
const dir = dirname(path);
|
|
1196
|
+
if (!existsSync3(dir))
|
|
1197
|
+
mkdirSync3(dir, { recursive: true });
|
|
1198
|
+
const tmp = `${path}.tmp.${process.pid}`;
|
|
1199
|
+
writeFileSync3(tmp, JSON.stringify(merged, null, 2) + "\n", "utf-8");
|
|
1200
|
+
renameSync2(tmp, path);
|
|
1201
|
+
_cache = merged;
|
|
1202
|
+
return merged;
|
|
1203
|
+
}
|
|
1204
|
+
function getEmbeddingsEnabled() {
|
|
1205
|
+
const cfg = readUserConfig();
|
|
1206
|
+
if (cfg.embeddings && typeof cfg.embeddings.enabled === "boolean") {
|
|
1207
|
+
return cfg.embeddings.enabled;
|
|
1208
|
+
}
|
|
1209
|
+
if (_migrated) {
|
|
1210
|
+
return migrationValueFromEnv();
|
|
1211
|
+
}
|
|
1212
|
+
_migrated = true;
|
|
1213
|
+
const enabled = migrationValueFromEnv();
|
|
1214
|
+
try {
|
|
1215
|
+
writeUserConfig({ embeddings: { enabled } });
|
|
1216
|
+
} catch {
|
|
1217
|
+
_cache = { ...cfg ?? {}, embeddings: { ...cfg?.embeddings ?? {}, enabled } };
|
|
1218
|
+
}
|
|
1219
|
+
return enabled;
|
|
1220
|
+
}
|
|
1221
|
+
function migrationValueFromEnv() {
|
|
1222
|
+
const raw = process.env.HIVEMIND_EMBEDDINGS;
|
|
1223
|
+
if (raw === void 0)
|
|
1224
|
+
return false;
|
|
1225
|
+
if (raw === "false")
|
|
1226
|
+
return false;
|
|
1227
|
+
return true;
|
|
1228
|
+
}
|
|
1229
|
+
function isPlainObject(value) {
|
|
1230
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1231
|
+
}
|
|
1232
|
+
function deepMerge(base, patch) {
|
|
1233
|
+
const out = { ...base };
|
|
1234
|
+
for (const key of Object.keys(patch)) {
|
|
1235
|
+
const patchVal = patch[key];
|
|
1236
|
+
const baseVal = base[key];
|
|
1237
|
+
if (isPlainObject(patchVal) && isPlainObject(baseVal)) {
|
|
1238
|
+
out[key] = { ...baseVal, ...patchVal };
|
|
1239
|
+
} else if (patchVal !== void 0) {
|
|
1240
|
+
out[key] = patchVal;
|
|
1241
|
+
}
|
|
1242
|
+
}
|
|
1243
|
+
return out;
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1246
|
+
// dist/src/embeddings/disable.js
|
|
1247
|
+
var cachedStatus = null;
|
|
1248
|
+
function defaultResolveTransformers() {
|
|
1249
|
+
const sharedDir = join6(homedir5(), ".hivemind", "embed-deps");
|
|
1250
|
+
try {
|
|
1251
|
+
createRequire(pathToFileURL(`${sharedDir}/`).href).resolve("@huggingface/transformers");
|
|
1252
|
+
return;
|
|
1253
|
+
} catch {
|
|
1254
|
+
}
|
|
1255
|
+
createRequire(import.meta.url).resolve("@huggingface/transformers");
|
|
1256
|
+
}
|
|
1257
|
+
var _resolve = defaultResolveTransformers;
|
|
1258
|
+
var _readEnabled = getEmbeddingsEnabled;
|
|
1259
|
+
function detectStatus() {
|
|
1260
|
+
if (!_readEnabled())
|
|
1261
|
+
return "user-disabled";
|
|
1262
|
+
try {
|
|
1263
|
+
_resolve();
|
|
1264
|
+
return "enabled";
|
|
1265
|
+
} catch {
|
|
1266
|
+
return "no-transformers";
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
function embeddingsStatus() {
|
|
1270
|
+
if (cachedStatus !== null)
|
|
1271
|
+
return cachedStatus;
|
|
1272
|
+
cachedStatus = detectStatus();
|
|
1273
|
+
return cachedStatus;
|
|
1274
|
+
}
|
|
1275
|
+
function embeddingsDisabled() {
|
|
1276
|
+
return embeddingsStatus() !== "enabled";
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1060
1279
|
// dist/src/embeddings/client.js
|
|
1061
|
-
var SHARED_DAEMON_PATH =
|
|
1062
|
-
var
|
|
1280
|
+
var SHARED_DAEMON_PATH = join7(homedir6(), ".hivemind", "embed-deps", "embed-daemon.js");
|
|
1281
|
+
var log4 = (m) => log("embed-client", m);
|
|
1063
1282
|
function getUid() {
|
|
1064
1283
|
const uid = typeof process.getuid === "function" ? process.getuid() : void 0;
|
|
1065
1284
|
return uid !== void 0 ? String(uid) : process.env.USER ?? "default";
|
|
1066
1285
|
}
|
|
1286
|
+
var _signalledMissingDeps = false;
|
|
1287
|
+
var _recycledStuckDaemon = false;
|
|
1067
1288
|
var EmbedClient = class {
|
|
1068
1289
|
socketPath;
|
|
1069
1290
|
pidPath;
|
|
@@ -1072,13 +1293,14 @@ var EmbedClient = class {
|
|
|
1072
1293
|
autoSpawn;
|
|
1073
1294
|
spawnWaitMs;
|
|
1074
1295
|
nextId = 0;
|
|
1296
|
+
helloVerified = false;
|
|
1075
1297
|
constructor(opts = {}) {
|
|
1076
1298
|
const uid = getUid();
|
|
1077
1299
|
const dir = opts.socketDir ?? "/tmp";
|
|
1078
1300
|
this.socketPath = socketPathFor(uid, dir);
|
|
1079
1301
|
this.pidPath = pidPathFor(uid, dir);
|
|
1080
1302
|
this.timeoutMs = opts.timeoutMs ?? DEFAULT_CLIENT_TIMEOUT_MS;
|
|
1081
|
-
this.daemonEntry = opts.daemonEntry ?? process.env.HIVEMIND_EMBED_DAEMON ?? (
|
|
1303
|
+
this.daemonEntry = opts.daemonEntry ?? process.env.HIVEMIND_EMBED_DAEMON ?? (existsSync4(SHARED_DAEMON_PATH) ? SHARED_DAEMON_PATH : void 0);
|
|
1082
1304
|
this.autoSpawn = opts.autoSpawn ?? true;
|
|
1083
1305
|
this.spawnWaitMs = opts.spawnWaitMs ?? 5e3;
|
|
1084
1306
|
}
|
|
@@ -1088,8 +1310,33 @@ var EmbedClient = class {
|
|
|
1088
1310
|
*
|
|
1089
1311
|
* Fire-and-forget spawn on miss: if the daemon isn't up, this call returns
|
|
1090
1312
|
* null AND kicks off a background spawn. The next call finds a ready daemon.
|
|
1313
|
+
*
|
|
1314
|
+
* Stuck-daemon recycle: if the daemon returns a transformers-missing
|
|
1315
|
+
* error (typical after a marketplace upgrade left an older daemon process
|
|
1316
|
+
* alive but with no node_modules accessible from its bundle path), we
|
|
1317
|
+
* SIGTERM it and clear its sock/pid so the very next call spawns a fresh
|
|
1318
|
+
* daemon from the current bundle. Without this, the stuck daemon would
|
|
1319
|
+
* keep poisoning every session until its 10-minute idle-out fires.
|
|
1091
1320
|
*/
|
|
1092
1321
|
async embed(text, kind = "document") {
|
|
1322
|
+
const v = await this.embedAttempt(text, kind);
|
|
1323
|
+
if (v !== "recycled")
|
|
1324
|
+
return v;
|
|
1325
|
+
if (!this.autoSpawn)
|
|
1326
|
+
return null;
|
|
1327
|
+
this.trySpawnDaemon();
|
|
1328
|
+
await this.waitForDaemonReady();
|
|
1329
|
+
const retry = await this.embedAttempt(text, kind);
|
|
1330
|
+
return retry === "recycled" ? null : retry;
|
|
1331
|
+
}
|
|
1332
|
+
/**
|
|
1333
|
+
* One round-trip: connect → verify → embed. Returns:
|
|
1334
|
+
* - number[] : embedding vector (happy path)
|
|
1335
|
+
* - null : timeout / daemon error / transformers-missing
|
|
1336
|
+
* - "recycled": verifyDaemonOnce killed the daemon mid-call;
|
|
1337
|
+
* caller should respawn and retry once.
|
|
1338
|
+
*/
|
|
1339
|
+
async embedAttempt(text, kind) {
|
|
1093
1340
|
let sock;
|
|
1094
1341
|
try {
|
|
1095
1342
|
sock = await this.connectOnce();
|
|
@@ -1099,17 +1346,25 @@ var EmbedClient = class {
|
|
|
1099
1346
|
return null;
|
|
1100
1347
|
}
|
|
1101
1348
|
try {
|
|
1349
|
+
const recycled = await this.verifyDaemonOnce(sock);
|
|
1350
|
+
if (recycled) {
|
|
1351
|
+
return "recycled";
|
|
1352
|
+
}
|
|
1102
1353
|
const id = String(++this.nextId);
|
|
1103
1354
|
const req = { op: "embed", id, kind, text };
|
|
1104
1355
|
const resp = await this.sendAndWait(sock, req);
|
|
1105
1356
|
if (resp.error || !("embedding" in resp) || !resp.embedding) {
|
|
1106
|
-
|
|
1357
|
+
const err = resp.error ?? "no embedding";
|
|
1358
|
+
log4(`embed err: ${err}`);
|
|
1359
|
+
if (isTransformersMissingError(err)) {
|
|
1360
|
+
this.handleTransformersMissing(err);
|
|
1361
|
+
}
|
|
1107
1362
|
return null;
|
|
1108
1363
|
}
|
|
1109
1364
|
return resp.embedding;
|
|
1110
1365
|
} catch (e) {
|
|
1111
1366
|
const err = e instanceof Error ? e.message : String(e);
|
|
1112
|
-
|
|
1367
|
+
log4(`embed failed: ${err}`);
|
|
1113
1368
|
return null;
|
|
1114
1369
|
} finally {
|
|
1115
1370
|
try {
|
|
@@ -1118,6 +1373,139 @@ var EmbedClient = class {
|
|
|
1118
1373
|
}
|
|
1119
1374
|
}
|
|
1120
1375
|
}
|
|
1376
|
+
/**
|
|
1377
|
+
* Poll for the sock file to come back after `trySpawnDaemon` — used by
|
|
1378
|
+
* the recycle retry path. Best-effort: caps at `spawnWaitMs` and
|
|
1379
|
+
* returns regardless so the retry attempt can run.
|
|
1380
|
+
*/
|
|
1381
|
+
async waitForDaemonReady() {
|
|
1382
|
+
const deadline = Date.now() + this.spawnWaitMs;
|
|
1383
|
+
while (Date.now() < deadline) {
|
|
1384
|
+
if (existsSync4(this.socketPath))
|
|
1385
|
+
return;
|
|
1386
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
/**
|
|
1390
|
+
* Send a `hello` on first successful connect per EmbedClient instance.
|
|
1391
|
+
* If the daemon answers with a path that doesn't match our configured
|
|
1392
|
+
* daemonEntry — typical after a marketplace upgrade replaced the bundle
|
|
1393
|
+
* — SIGTERM the daemon + clear sock/pid so the next call spawns from the
|
|
1394
|
+
* current bundle.
|
|
1395
|
+
*
|
|
1396
|
+
* `helloVerified` is set ONLY after we've seen a compatible response,
|
|
1397
|
+
* so a transient probe failure or a recycle-triggering mismatch leaves
|
|
1398
|
+
* the flag false; the next reconnect re-runs verification against
|
|
1399
|
+
* whatever daemon is then live (typically the fresh spawn).
|
|
1400
|
+
*/
|
|
1401
|
+
async verifyDaemonOnce(sock) {
|
|
1402
|
+
if (this.helloVerified)
|
|
1403
|
+
return false;
|
|
1404
|
+
if (!this.daemonEntry) {
|
|
1405
|
+
this.helloVerified = true;
|
|
1406
|
+
return false;
|
|
1407
|
+
}
|
|
1408
|
+
const id = String(++this.nextId);
|
|
1409
|
+
const req = { op: "hello", id };
|
|
1410
|
+
let resp;
|
|
1411
|
+
try {
|
|
1412
|
+
resp = await this.sendAndWait(sock, req);
|
|
1413
|
+
} catch (e) {
|
|
1414
|
+
log4(`hello probe failed (inconclusive, will retry next connect): ${e instanceof Error ? e.message : String(e)}`);
|
|
1415
|
+
return false;
|
|
1416
|
+
}
|
|
1417
|
+
const hello = resp;
|
|
1418
|
+
if (_recycledStuckDaemon) {
|
|
1419
|
+
return false;
|
|
1420
|
+
}
|
|
1421
|
+
if (!hello.daemonPath) {
|
|
1422
|
+
_recycledStuckDaemon = true;
|
|
1423
|
+
log4(`daemon does not implement hello (older protocol); recycling`);
|
|
1424
|
+
this.recycleDaemon(hello.pid);
|
|
1425
|
+
return true;
|
|
1426
|
+
}
|
|
1427
|
+
if (hello.daemonPath !== this.daemonEntry && !existsSync4(hello.daemonPath)) {
|
|
1428
|
+
_recycledStuckDaemon = true;
|
|
1429
|
+
log4(`daemon path no longer on disk \u2014 running=${hello.daemonPath} (gone) expected=${this.daemonEntry}; recycling`);
|
|
1430
|
+
this.recycleDaemon(hello.pid);
|
|
1431
|
+
return true;
|
|
1432
|
+
}
|
|
1433
|
+
this.helloVerified = true;
|
|
1434
|
+
return false;
|
|
1435
|
+
}
|
|
1436
|
+
/**
|
|
1437
|
+
* On a transformers-missing error from the daemon, SIGTERM the stuck
|
|
1438
|
+
* daemon (the bundle daemon that can't find its deps) and clear
|
|
1439
|
+
* sock/pid so the next call spawns fresh. Also enqueue a one-time
|
|
1440
|
+
* notification telling the user to run `hivemind embeddings install`
|
|
1441
|
+
* — but only when the user has opted in. Suppressed when
|
|
1442
|
+
* embeddingsStatus() === "user-disabled" so we don't nag users who
|
|
1443
|
+
* explicitly chose to turn embeddings off.
|
|
1444
|
+
*/
|
|
1445
|
+
handleTransformersMissing(detail) {
|
|
1446
|
+
if (!_recycledStuckDaemon) {
|
|
1447
|
+
_recycledStuckDaemon = true;
|
|
1448
|
+
this.recycleDaemon(null);
|
|
1449
|
+
}
|
|
1450
|
+
if (_signalledMissingDeps)
|
|
1451
|
+
return;
|
|
1452
|
+
_signalledMissingDeps = true;
|
|
1453
|
+
let status;
|
|
1454
|
+
try {
|
|
1455
|
+
status = embeddingsStatus();
|
|
1456
|
+
} catch {
|
|
1457
|
+
status = "enabled";
|
|
1458
|
+
}
|
|
1459
|
+
if (status === "user-disabled")
|
|
1460
|
+
return;
|
|
1461
|
+
enqueueNotification({
|
|
1462
|
+
id: "embed-deps-missing",
|
|
1463
|
+
severity: "warn",
|
|
1464
|
+
title: "Hivemind embeddings disabled \u2014 deps missing",
|
|
1465
|
+
body: `Semantic memory search is off because @huggingface/transformers is not installed where the daemon can find it. Run \`hivemind embeddings install\` to enable.`,
|
|
1466
|
+
dedupKey: { reason: "transformers-missing", detail: detail.slice(0, 200) }
|
|
1467
|
+
}).catch((e) => {
|
|
1468
|
+
log4(`enqueue embed-deps-missing failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
1469
|
+
});
|
|
1470
|
+
}
|
|
1471
|
+
/**
|
|
1472
|
+
* Best-effort SIGTERM + sock/pid cleanup. Tolerant of every missing-file
|
|
1473
|
+
* combination and dead-PID cases.
|
|
1474
|
+
*
|
|
1475
|
+
* Identity check: gate the SIGTERM on the daemon's socket file still
|
|
1476
|
+
* existing. We know the daemon was alive moments ago (we either just
|
|
1477
|
+
* got a hello response or the caller saw a transformers-missing error
|
|
1478
|
+
* the daemon emitted), but if the socket file is gone by the time we
|
|
1479
|
+
* try to kill, the daemon process is also gone and the PID we
|
|
1480
|
+
* captured may already have been recycled by the OS to an unrelated
|
|
1481
|
+
* user process. Mirrors the gate added to `killEmbedDaemon` in the
|
|
1482
|
+
* CLI — same failure mode, rarer trigger.
|
|
1483
|
+
*/
|
|
1484
|
+
recycleDaemon(reportedPid) {
|
|
1485
|
+
let pid = reportedPid;
|
|
1486
|
+
if (pid === null) {
|
|
1487
|
+
try {
|
|
1488
|
+
pid = Number.parseInt(readFileSync5(this.pidPath, "utf-8").trim(), 10);
|
|
1489
|
+
} catch {
|
|
1490
|
+
}
|
|
1491
|
+
}
|
|
1492
|
+
if (Number.isFinite(pid) && pid !== null && pid > 0 && existsSync4(this.socketPath)) {
|
|
1493
|
+
try {
|
|
1494
|
+
process.kill(pid, "SIGTERM");
|
|
1495
|
+
} catch {
|
|
1496
|
+
}
|
|
1497
|
+
} else if (pid !== null) {
|
|
1498
|
+
log4(`recycle: socket gone, skipping SIGTERM on possibly-stale pid ${pid}`);
|
|
1499
|
+
}
|
|
1500
|
+
try {
|
|
1501
|
+
unlinkSync2(this.socketPath);
|
|
1502
|
+
} catch {
|
|
1503
|
+
}
|
|
1504
|
+
try {
|
|
1505
|
+
unlinkSync2(this.pidPath);
|
|
1506
|
+
} catch {
|
|
1507
|
+
}
|
|
1508
|
+
}
|
|
1121
1509
|
/**
|
|
1122
1510
|
* Wait up to spawnWaitMs for the daemon to accept connections, spawning if
|
|
1123
1511
|
* necessary. Meant for SessionStart / long-running batches — not the hot path.
|
|
@@ -1141,7 +1529,7 @@ var EmbedClient = class {
|
|
|
1141
1529
|
}
|
|
1142
1530
|
}
|
|
1143
1531
|
connectOnce() {
|
|
1144
|
-
return new Promise((
|
|
1532
|
+
return new Promise((resolve2, reject) => {
|
|
1145
1533
|
const sock = connect(this.socketPath);
|
|
1146
1534
|
const to = setTimeout(() => {
|
|
1147
1535
|
sock.destroy();
|
|
@@ -1149,7 +1537,7 @@ var EmbedClient = class {
|
|
|
1149
1537
|
}, this.timeoutMs);
|
|
1150
1538
|
sock.once("connect", () => {
|
|
1151
1539
|
clearTimeout(to);
|
|
1152
|
-
|
|
1540
|
+
resolve2(sock);
|
|
1153
1541
|
});
|
|
1154
1542
|
sock.once("error", (e) => {
|
|
1155
1543
|
clearTimeout(to);
|
|
@@ -1160,16 +1548,16 @@ var EmbedClient = class {
|
|
|
1160
1548
|
trySpawnDaemon() {
|
|
1161
1549
|
let fd;
|
|
1162
1550
|
try {
|
|
1163
|
-
fd =
|
|
1551
|
+
fd = openSync2(this.pidPath, "wx", 384);
|
|
1164
1552
|
writeSync(fd, String(process.pid));
|
|
1165
1553
|
} catch (e) {
|
|
1166
1554
|
if (this.isPidFileStale()) {
|
|
1167
1555
|
try {
|
|
1168
|
-
|
|
1556
|
+
unlinkSync2(this.pidPath);
|
|
1169
1557
|
} catch {
|
|
1170
1558
|
}
|
|
1171
1559
|
try {
|
|
1172
|
-
fd =
|
|
1560
|
+
fd = openSync2(this.pidPath, "wx", 384);
|
|
1173
1561
|
writeSync(fd, String(process.pid));
|
|
1174
1562
|
} catch {
|
|
1175
1563
|
return;
|
|
@@ -1178,11 +1566,11 @@ var EmbedClient = class {
|
|
|
1178
1566
|
return;
|
|
1179
1567
|
}
|
|
1180
1568
|
}
|
|
1181
|
-
if (!this.daemonEntry || !
|
|
1182
|
-
|
|
1569
|
+
if (!this.daemonEntry || !existsSync4(this.daemonEntry)) {
|
|
1570
|
+
log4(`daemonEntry not configured or missing: ${this.daemonEntry}`);
|
|
1183
1571
|
try {
|
|
1184
|
-
|
|
1185
|
-
|
|
1572
|
+
closeSync2(fd);
|
|
1573
|
+
unlinkSync2(this.pidPath);
|
|
1186
1574
|
} catch {
|
|
1187
1575
|
}
|
|
1188
1576
|
return;
|
|
@@ -1194,14 +1582,14 @@ var EmbedClient = class {
|
|
|
1194
1582
|
env: process.env
|
|
1195
1583
|
});
|
|
1196
1584
|
child.unref();
|
|
1197
|
-
|
|
1585
|
+
log4(`spawned daemon pid=${child.pid}`);
|
|
1198
1586
|
} finally {
|
|
1199
|
-
|
|
1587
|
+
closeSync2(fd);
|
|
1200
1588
|
}
|
|
1201
1589
|
}
|
|
1202
1590
|
isPidFileStale() {
|
|
1203
1591
|
try {
|
|
1204
|
-
const raw =
|
|
1592
|
+
const raw = readFileSync5(this.pidPath, "utf-8").trim();
|
|
1205
1593
|
const pid = Number(raw);
|
|
1206
1594
|
if (!pid || Number.isNaN(pid))
|
|
1207
1595
|
return true;
|
|
@@ -1219,9 +1607,9 @@ var EmbedClient = class {
|
|
|
1219
1607
|
const deadline = Date.now() + this.spawnWaitMs;
|
|
1220
1608
|
let delay = 30;
|
|
1221
1609
|
while (Date.now() < deadline) {
|
|
1222
|
-
await
|
|
1610
|
+
await sleep3(delay);
|
|
1223
1611
|
delay = Math.min(delay * 1.5, 300);
|
|
1224
|
-
if (!
|
|
1612
|
+
if (!existsSync4(this.socketPath))
|
|
1225
1613
|
continue;
|
|
1226
1614
|
try {
|
|
1227
1615
|
return await this.connectOnce();
|
|
@@ -1231,7 +1619,7 @@ var EmbedClient = class {
|
|
|
1231
1619
|
throw new Error("daemon did not become ready within spawnWaitMs");
|
|
1232
1620
|
}
|
|
1233
1621
|
sendAndWait(sock, req) {
|
|
1234
|
-
return new Promise((
|
|
1622
|
+
return new Promise((resolve2, reject) => {
|
|
1235
1623
|
let buf = "";
|
|
1236
1624
|
const to = setTimeout(() => {
|
|
1237
1625
|
sock.destroy();
|
|
@@ -1246,7 +1634,7 @@ var EmbedClient = class {
|
|
|
1246
1634
|
const line = buf.slice(0, nl);
|
|
1247
1635
|
clearTimeout(to);
|
|
1248
1636
|
try {
|
|
1249
|
-
|
|
1637
|
+
resolve2(JSON.parse(line));
|
|
1250
1638
|
} catch (e) {
|
|
1251
1639
|
reject(e);
|
|
1252
1640
|
}
|
|
@@ -1263,53 +1651,22 @@ var EmbedClient = class {
|
|
|
1263
1651
|
});
|
|
1264
1652
|
}
|
|
1265
1653
|
};
|
|
1266
|
-
function
|
|
1654
|
+
function sleep3(ms) {
|
|
1267
1655
|
return new Promise((r) => setTimeout(r, ms));
|
|
1268
1656
|
}
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
import { join as join5 } from "node:path";
|
|
1274
|
-
import { pathToFileURL } from "node:url";
|
|
1275
|
-
var cachedStatus = null;
|
|
1276
|
-
function defaultResolveTransformers() {
|
|
1277
|
-
try {
|
|
1278
|
-
createRequire(import.meta.url).resolve("@huggingface/transformers");
|
|
1279
|
-
return;
|
|
1280
|
-
} catch {
|
|
1281
|
-
}
|
|
1282
|
-
const sharedDir = join5(homedir4(), ".hivemind", "embed-deps");
|
|
1283
|
-
createRequire(pathToFileURL(`${sharedDir}/`).href).resolve("@huggingface/transformers");
|
|
1284
|
-
}
|
|
1285
|
-
var _resolve = defaultResolveTransformers;
|
|
1286
|
-
function detectStatus() {
|
|
1287
|
-
if (process.env.HIVEMIND_EMBEDDINGS === "false")
|
|
1288
|
-
return "env-disabled";
|
|
1289
|
-
try {
|
|
1290
|
-
_resolve();
|
|
1291
|
-
return "enabled";
|
|
1292
|
-
} catch {
|
|
1293
|
-
return "no-transformers";
|
|
1294
|
-
}
|
|
1295
|
-
}
|
|
1296
|
-
function embeddingsStatus() {
|
|
1297
|
-
if (cachedStatus !== null)
|
|
1298
|
-
return cachedStatus;
|
|
1299
|
-
cachedStatus = detectStatus();
|
|
1300
|
-
return cachedStatus;
|
|
1301
|
-
}
|
|
1302
|
-
function embeddingsDisabled() {
|
|
1303
|
-
return embeddingsStatus() !== "enabled";
|
|
1657
|
+
function isTransformersMissingError(err) {
|
|
1658
|
+
if (/hivemind embeddings install/i.test(err))
|
|
1659
|
+
return true;
|
|
1660
|
+
return /@huggingface\/transformers/i.test(err);
|
|
1304
1661
|
}
|
|
1305
1662
|
|
|
1306
1663
|
// dist/src/hooks/grep-direct.js
|
|
1307
1664
|
import { fileURLToPath } from "node:url";
|
|
1308
|
-
import { dirname, join as
|
|
1665
|
+
import { dirname as dirname2, join as join8 } from "node:path";
|
|
1309
1666
|
var SEMANTIC_ENABLED = process.env.HIVEMIND_SEMANTIC_SEARCH !== "false" && !embeddingsDisabled();
|
|
1310
1667
|
var SEMANTIC_TIMEOUT_MS = Number(process.env.HIVEMIND_SEMANTIC_EMBED_TIMEOUT_MS ?? "500");
|
|
1311
1668
|
function resolveDaemonPath() {
|
|
1312
|
-
return
|
|
1669
|
+
return join8(dirname2(fileURLToPath(import.meta.url)), "..", "embeddings", "embed-daemon.js");
|
|
1313
1670
|
}
|
|
1314
1671
|
var sharedEmbedClient = null;
|
|
1315
1672
|
function getEmbedClient() {
|
|
@@ -1662,9 +2019,9 @@ async function handleGrepDirect(api, table, sessionsTable, params) {
|
|
|
1662
2019
|
}
|
|
1663
2020
|
|
|
1664
2021
|
// dist/src/hooks/memory-path-utils.js
|
|
1665
|
-
import { homedir as
|
|
1666
|
-
import { join as
|
|
1667
|
-
var MEMORY_PATH =
|
|
2022
|
+
import { homedir as homedir7 } from "node:os";
|
|
2023
|
+
import { join as join9 } from "node:path";
|
|
2024
|
+
var MEMORY_PATH = join9(homedir7(), ".deeplake", "memory");
|
|
1668
2025
|
var TILDE_PATH = "~/.deeplake/memory";
|
|
1669
2026
|
var HOME_VAR_PATH = "$HOME/.deeplake/memory";
|
|
1670
2027
|
function touchesMemory(p) {
|
|
@@ -1675,7 +2032,7 @@ function rewritePaths(cmd) {
|
|
|
1675
2032
|
}
|
|
1676
2033
|
|
|
1677
2034
|
// dist/src/hooks/hermes/pre-tool-use.js
|
|
1678
|
-
var
|
|
2035
|
+
var log5 = (msg) => log("hermes-pre-tool-use", msg);
|
|
1679
2036
|
async function main() {
|
|
1680
2037
|
const input = await readStdin();
|
|
1681
2038
|
if (input.tool_name !== "terminal")
|
|
@@ -1692,7 +2049,7 @@ async function main() {
|
|
|
1692
2049
|
return;
|
|
1693
2050
|
const config = loadConfig();
|
|
1694
2051
|
if (!config) {
|
|
1695
|
-
|
|
2052
|
+
log5("no config \u2014 falling through to Hermes");
|
|
1696
2053
|
return;
|
|
1697
2054
|
}
|
|
1698
2055
|
const api = new DeeplakeApi(config.token, config.apiUrl, config.orgId, config.workspaceId, config.tableName);
|
|
@@ -1700,7 +2057,7 @@ async function main() {
|
|
|
1700
2057
|
const result = await handleGrepDirect(api, config.tableName, config.sessionsTableName, grepParams);
|
|
1701
2058
|
if (result === null)
|
|
1702
2059
|
return;
|
|
1703
|
-
|
|
2060
|
+
log5(`intercepted ${command.slice(0, 80)} \u2192 ${result.length} chars from SQL fast-path`);
|
|
1704
2061
|
const message = [
|
|
1705
2062
|
result,
|
|
1706
2063
|
"",
|
|
@@ -1709,10 +2066,10 @@ async function main() {
|
|
|
1709
2066
|
process.stdout.write(JSON.stringify({ action: "block", message }));
|
|
1710
2067
|
} catch (err) {
|
|
1711
2068
|
const msg = err instanceof Error ? err.message : String(err);
|
|
1712
|
-
|
|
2069
|
+
log5(`fast-path failed, falling through: ${msg}`);
|
|
1713
2070
|
}
|
|
1714
2071
|
}
|
|
1715
2072
|
main().catch((e) => {
|
|
1716
|
-
|
|
2073
|
+
log5(`fatal: ${e.message}`);
|
|
1717
2074
|
process.exit(0);
|
|
1718
2075
|
});
|