@deeplake/hivemind 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.
- package/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/bundle/cli.js +594 -267
- package/codex/bundle/capture.js +585 -124
- package/codex/bundle/commands/auth-login.js +163 -30
- package/codex/bundle/embeddings/embed-daemon.js +55 -4
- package/codex/bundle/pre-tool-use.js +475 -85
- package/codex/bundle/session-start-setup.js +193 -60
- package/codex/bundle/session-start.js +221 -88
- package/codex/bundle/shell/deeplake-shell.js +458 -68
- package/codex/bundle/stop.js +565 -175
- package/codex/bundle/wiki-worker.js +258 -19
- package/cursor/bundle/capture.js +660 -199
- package/cursor/bundle/commands/auth-login.js +163 -30
- package/cursor/bundle/embeddings/embed-daemon.js +55 -4
- package/cursor/bundle/pre-tool-use.js +460 -70
- package/cursor/bundle/session-start.js +271 -131
- package/cursor/bundle/shell/deeplake-shell.js +458 -68
- package/cursor/bundle/wiki-worker.js +258 -19
- package/hermes/bundle/capture.js +661 -200
- package/hermes/bundle/commands/auth-login.js +163 -30
- package/hermes/bundle/embeddings/embed-daemon.js +55 -4
- package/hermes/bundle/pre-tool-use.js +459 -69
- package/hermes/bundle/session-start.js +268 -128
- package/hermes/bundle/shell/deeplake-shell.js +458 -68
- package/hermes/bundle/wiki-worker.js +258 -19
- package/mcp/bundle/server.js +190 -57
- package/openclaw/dist/index.js +160 -32
- package/openclaw/openclaw.plugin.json +1 -1
- package/openclaw/package.json +1 -1
- package/package.json +1 -1
package/codex/bundle/stop.js
CHANGED
|
@@ -17,21 +17,21 @@ __export(index_marker_store_exports, {
|
|
|
17
17
|
hasFreshIndexMarker: () => hasFreshIndexMarker,
|
|
18
18
|
writeIndexMarker: () => writeIndexMarker
|
|
19
19
|
});
|
|
20
|
-
import { existsSync as existsSync2, mkdirSync, readFileSync as
|
|
21
|
-
import { join as
|
|
20
|
+
import { existsSync as existsSync2, mkdirSync as mkdirSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "node:fs";
|
|
21
|
+
import { join as join5 } from "node:path";
|
|
22
22
|
import { tmpdir } from "node:os";
|
|
23
23
|
function getIndexMarkerDir() {
|
|
24
|
-
return process.env.HIVEMIND_INDEX_MARKER_DIR ??
|
|
24
|
+
return process.env.HIVEMIND_INDEX_MARKER_DIR ?? join5(tmpdir(), "hivemind-deeplake-indexes");
|
|
25
25
|
}
|
|
26
26
|
function buildIndexMarkerPath(workspaceId, orgId, table, suffix) {
|
|
27
27
|
const markerKey = [workspaceId, orgId, table, suffix].join("__").replace(/[^a-zA-Z0-9_.-]/g, "_");
|
|
28
|
-
return
|
|
28
|
+
return join5(getIndexMarkerDir(), `${markerKey}.json`);
|
|
29
29
|
}
|
|
30
30
|
function hasFreshIndexMarker(markerPath) {
|
|
31
31
|
if (!existsSync2(markerPath))
|
|
32
32
|
return false;
|
|
33
33
|
try {
|
|
34
|
-
const raw = JSON.parse(
|
|
34
|
+
const raw = JSON.parse(readFileSync4(markerPath, "utf-8"));
|
|
35
35
|
const updatedAt = raw.updatedAt ? new Date(raw.updatedAt).getTime() : NaN;
|
|
36
36
|
if (!Number.isFinite(updatedAt) || Date.now() - updatedAt > INDEX_MARKER_TTL_MS)
|
|
37
37
|
return false;
|
|
@@ -41,8 +41,8 @@ function hasFreshIndexMarker(markerPath) {
|
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
43
|
function writeIndexMarker(markerPath) {
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
mkdirSync3(getIndexMarkerDir(), { recursive: true });
|
|
45
|
+
writeFileSync3(markerPath, JSON.stringify({ updatedAt: (/* @__PURE__ */ new Date()).toISOString() }), "utf-8");
|
|
46
46
|
}
|
|
47
47
|
var INDEX_MARKER_TTL_MS;
|
|
48
48
|
var init_index_marker_store = __esm({
|
|
@@ -53,19 +53,19 @@ var init_index_marker_store = __esm({
|
|
|
53
53
|
});
|
|
54
54
|
|
|
55
55
|
// dist/src/hooks/codex/stop.js
|
|
56
|
-
import { readFileSync as
|
|
56
|
+
import { readFileSync as readFileSync11, existsSync as existsSync10 } from "node:fs";
|
|
57
57
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
58
|
-
import { dirname as
|
|
58
|
+
import { dirname as dirname5, join as join18 } from "node:path";
|
|
59
59
|
|
|
60
60
|
// dist/src/utils/stdin.js
|
|
61
61
|
function readStdin() {
|
|
62
|
-
return new Promise((
|
|
62
|
+
return new Promise((resolve2, reject) => {
|
|
63
63
|
let data = "";
|
|
64
64
|
process.stdin.setEncoding("utf-8");
|
|
65
65
|
process.stdin.on("data", (chunk) => data += chunk);
|
|
66
66
|
process.stdin.on("end", () => {
|
|
67
67
|
try {
|
|
68
|
-
|
|
68
|
+
resolve2(JSON.parse(data));
|
|
69
69
|
} catch (err) {
|
|
70
70
|
reject(new Error(`Failed to parse hook input: ${err}`));
|
|
71
71
|
}
|
|
@@ -152,6 +152,125 @@ function deeplakeClientHeader() {
|
|
|
152
152
|
return { [DEEPLAKE_CLIENT_HEADER]: deeplakeClientValue() };
|
|
153
153
|
}
|
|
154
154
|
|
|
155
|
+
// dist/src/notifications/queue.js
|
|
156
|
+
import { readFileSync as readFileSync2, writeFileSync, renameSync, mkdirSync, openSync, closeSync, unlinkSync, statSync } from "node:fs";
|
|
157
|
+
import { join as join3, resolve } from "node:path";
|
|
158
|
+
import { homedir as homedir3 } from "node:os";
|
|
159
|
+
import { setTimeout as sleep } from "node:timers/promises";
|
|
160
|
+
var log2 = (msg) => log("notifications-queue", msg);
|
|
161
|
+
var LOCK_RETRY_MAX = 50;
|
|
162
|
+
var LOCK_RETRY_BASE_MS = 5;
|
|
163
|
+
var LOCK_STALE_MS = 5e3;
|
|
164
|
+
function queuePath() {
|
|
165
|
+
return join3(homedir3(), ".deeplake", "notifications-queue.json");
|
|
166
|
+
}
|
|
167
|
+
function lockPath() {
|
|
168
|
+
return `${queuePath()}.lock`;
|
|
169
|
+
}
|
|
170
|
+
function readQueue() {
|
|
171
|
+
try {
|
|
172
|
+
const raw = readFileSync2(queuePath(), "utf-8");
|
|
173
|
+
const parsed = JSON.parse(raw);
|
|
174
|
+
if (!parsed || !Array.isArray(parsed.queue)) {
|
|
175
|
+
log2(`queue malformed \u2192 treating as empty`);
|
|
176
|
+
return { queue: [] };
|
|
177
|
+
}
|
|
178
|
+
return { queue: parsed.queue };
|
|
179
|
+
} catch {
|
|
180
|
+
return { queue: [] };
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
function _isQueuePathInsideHome(path, home) {
|
|
184
|
+
const r = resolve(path);
|
|
185
|
+
const h = resolve(home);
|
|
186
|
+
return r.startsWith(h + "/") || r === h;
|
|
187
|
+
}
|
|
188
|
+
function writeQueue(q) {
|
|
189
|
+
const path = queuePath();
|
|
190
|
+
const home = resolve(homedir3());
|
|
191
|
+
if (!_isQueuePathInsideHome(path, home)) {
|
|
192
|
+
throw new Error(`notifications-queue write blocked: ${path} is outside ${home}`);
|
|
193
|
+
}
|
|
194
|
+
mkdirSync(join3(home, ".deeplake"), { recursive: true, mode: 448 });
|
|
195
|
+
const tmp = `${path}.${process.pid}.tmp`;
|
|
196
|
+
writeFileSync(tmp, JSON.stringify(q, null, 2), { mode: 384 });
|
|
197
|
+
renameSync(tmp, path);
|
|
198
|
+
}
|
|
199
|
+
async function withQueueLock(fn) {
|
|
200
|
+
const path = lockPath();
|
|
201
|
+
mkdirSync(join3(homedir3(), ".deeplake"), { recursive: true, mode: 448 });
|
|
202
|
+
let fd = null;
|
|
203
|
+
for (let attempt = 0; attempt < LOCK_RETRY_MAX; attempt++) {
|
|
204
|
+
try {
|
|
205
|
+
fd = openSync(path, "wx", 384);
|
|
206
|
+
break;
|
|
207
|
+
} catch (e) {
|
|
208
|
+
const code = e.code;
|
|
209
|
+
if (code !== "EEXIST")
|
|
210
|
+
throw e;
|
|
211
|
+
try {
|
|
212
|
+
const age = Date.now() - statSync(path).mtimeMs;
|
|
213
|
+
if (age > LOCK_STALE_MS) {
|
|
214
|
+
unlinkSync(path);
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
} catch {
|
|
218
|
+
}
|
|
219
|
+
const delay = LOCK_RETRY_BASE_MS * (attempt + 1);
|
|
220
|
+
await sleep(delay);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
if (fd === null) {
|
|
224
|
+
log2(`lock acquisition gave up after ${LOCK_RETRY_MAX} attempts \u2014 proceeding unlocked (last-writer-wins)`);
|
|
225
|
+
return fn();
|
|
226
|
+
}
|
|
227
|
+
try {
|
|
228
|
+
return fn();
|
|
229
|
+
} finally {
|
|
230
|
+
try {
|
|
231
|
+
closeSync(fd);
|
|
232
|
+
} catch {
|
|
233
|
+
}
|
|
234
|
+
try {
|
|
235
|
+
unlinkSync(path);
|
|
236
|
+
} catch {
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
function sameDedupKey(a, b) {
|
|
241
|
+
if (a.id !== b.id)
|
|
242
|
+
return false;
|
|
243
|
+
return JSON.stringify(a.dedupKey) === JSON.stringify(b.dedupKey);
|
|
244
|
+
}
|
|
245
|
+
async function enqueueNotification(n) {
|
|
246
|
+
await withQueueLock(() => {
|
|
247
|
+
const q = readQueue();
|
|
248
|
+
if (q.queue.some((existing) => sameDedupKey(existing, n))) {
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
q.queue.push(n);
|
|
252
|
+
writeQueue(q);
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// dist/src/commands/auth-creds.js
|
|
257
|
+
import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, unlinkSync as unlinkSync2 } from "node:fs";
|
|
258
|
+
import { join as join4 } from "node:path";
|
|
259
|
+
import { homedir as homedir4 } from "node:os";
|
|
260
|
+
function configDir() {
|
|
261
|
+
return join4(homedir4(), ".deeplake");
|
|
262
|
+
}
|
|
263
|
+
function credsPath() {
|
|
264
|
+
return join4(configDir(), "credentials.json");
|
|
265
|
+
}
|
|
266
|
+
function loadCredentials() {
|
|
267
|
+
try {
|
|
268
|
+
return JSON.parse(readFileSync3(credsPath(), "utf-8"));
|
|
269
|
+
} catch {
|
|
270
|
+
return null;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
155
274
|
// dist/src/deeplake-api.js
|
|
156
275
|
var indexMarkerStorePromise = null;
|
|
157
276
|
function getIndexMarkerStore() {
|
|
@@ -159,7 +278,7 @@ function getIndexMarkerStore() {
|
|
|
159
278
|
indexMarkerStorePromise = Promise.resolve().then(() => (init_index_marker_store(), index_marker_store_exports));
|
|
160
279
|
return indexMarkerStorePromise;
|
|
161
280
|
}
|
|
162
|
-
var
|
|
281
|
+
var log3 = (msg) => log("sdk", msg);
|
|
163
282
|
function summarizeSql(sql, maxLen = 220) {
|
|
164
283
|
const compact = sql.replace(/\s+/g, " ").trim();
|
|
165
284
|
return compact.length > maxLen ? `${compact.slice(0, maxLen)}...` : compact;
|
|
@@ -171,7 +290,38 @@ function traceSql(msg) {
|
|
|
171
290
|
process.stderr.write(`[deeplake-sql] ${msg}
|
|
172
291
|
`);
|
|
173
292
|
if (process.env.HIVEMIND_DEBUG === "1")
|
|
174
|
-
|
|
293
|
+
log3(msg);
|
|
294
|
+
}
|
|
295
|
+
var _signalledBalanceExhausted = false;
|
|
296
|
+
function maybeSignalBalanceExhausted(status, bodyText) {
|
|
297
|
+
if (status !== 402)
|
|
298
|
+
return;
|
|
299
|
+
if (!bodyText.includes("balance_cents"))
|
|
300
|
+
return;
|
|
301
|
+
if (_signalledBalanceExhausted)
|
|
302
|
+
return;
|
|
303
|
+
_signalledBalanceExhausted = true;
|
|
304
|
+
log3(`balance exhausted \u2014 enqueuing session-start banner (body=${bodyText.slice(0, 120)})`);
|
|
305
|
+
enqueueNotification({
|
|
306
|
+
id: "balance-exhausted",
|
|
307
|
+
severity: "warn",
|
|
308
|
+
transient: true,
|
|
309
|
+
title: "Hivemind credits exhausted \u2014 top up to keep capturing",
|
|
310
|
+
body: `Sessions are not being saved and memory recall is returning empty. Top up at ${billingUrl()} to restore capture and recall.`,
|
|
311
|
+
dedupKey: { reason: "balance-zero" }
|
|
312
|
+
}).catch((e) => {
|
|
313
|
+
log3(`enqueue balance-exhausted failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
function billingUrl() {
|
|
317
|
+
try {
|
|
318
|
+
const c = loadCredentials();
|
|
319
|
+
if (c?.orgName && c?.workspaceId) {
|
|
320
|
+
return `https://deeplake.ai/${encodeURIComponent(c.orgName)}/workspace/${encodeURIComponent(c.workspaceId)}/billing`;
|
|
321
|
+
}
|
|
322
|
+
} catch {
|
|
323
|
+
}
|
|
324
|
+
return "https://deeplake.ai";
|
|
175
325
|
}
|
|
176
326
|
var RETRYABLE_CODES = /* @__PURE__ */ new Set([429, 500, 502, 503, 504]);
|
|
177
327
|
var MAX_RETRIES = 3;
|
|
@@ -180,8 +330,8 @@ var MAX_CONCURRENCY = 5;
|
|
|
180
330
|
function getQueryTimeoutMs() {
|
|
181
331
|
return Number(process.env.HIVEMIND_QUERY_TIMEOUT_MS ?? 1e4);
|
|
182
332
|
}
|
|
183
|
-
function
|
|
184
|
-
return new Promise((
|
|
333
|
+
function sleep2(ms) {
|
|
334
|
+
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
185
335
|
}
|
|
186
336
|
function isTimeoutError(error) {
|
|
187
337
|
const name = error instanceof Error ? error.name.toLowerCase() : "";
|
|
@@ -211,7 +361,7 @@ var Semaphore = class {
|
|
|
211
361
|
this.active++;
|
|
212
362
|
return;
|
|
213
363
|
}
|
|
214
|
-
await new Promise((
|
|
364
|
+
await new Promise((resolve2) => this.waiting.push(resolve2));
|
|
215
365
|
}
|
|
216
366
|
release() {
|
|
217
367
|
this.active--;
|
|
@@ -282,8 +432,8 @@ var DeeplakeApi = class {
|
|
|
282
432
|
lastError = e instanceof Error ? e : new Error(String(e));
|
|
283
433
|
if (attempt < MAX_RETRIES) {
|
|
284
434
|
const delay = BASE_DELAY_MS * Math.pow(2, attempt) + Math.random() * 200;
|
|
285
|
-
|
|
286
|
-
await
|
|
435
|
+
log3(`query retry ${attempt + 1}/${MAX_RETRIES} (fetch error: ${lastError.message}) in ${delay.toFixed(0)}ms`);
|
|
436
|
+
await sleep2(delay);
|
|
287
437
|
continue;
|
|
288
438
|
}
|
|
289
439
|
throw lastError;
|
|
@@ -299,10 +449,11 @@ var DeeplakeApi = class {
|
|
|
299
449
|
const alreadyExists = resp.status === 500 && isDuplicateIndexError(text);
|
|
300
450
|
if (!alreadyExists && attempt < MAX_RETRIES && (RETRYABLE_CODES.has(resp.status) || retryable403)) {
|
|
301
451
|
const delay = BASE_DELAY_MS * Math.pow(2, attempt) + Math.random() * 200;
|
|
302
|
-
|
|
303
|
-
await
|
|
452
|
+
log3(`query retry ${attempt + 1}/${MAX_RETRIES} (${resp.status}) in ${delay.toFixed(0)}ms`);
|
|
453
|
+
await sleep2(delay);
|
|
304
454
|
continue;
|
|
305
455
|
}
|
|
456
|
+
maybeSignalBalanceExhausted(resp.status, text);
|
|
306
457
|
throw new Error(`Query failed: ${resp.status}: ${text.slice(0, 200)}`);
|
|
307
458
|
}
|
|
308
459
|
throw lastError ?? new Error("Query failed: max retries exceeded");
|
|
@@ -323,7 +474,7 @@ var DeeplakeApi = class {
|
|
|
323
474
|
const chunk = rows.slice(i, i + CONCURRENCY);
|
|
324
475
|
await Promise.allSettled(chunk.map((r) => this.upsertRowSql(r)));
|
|
325
476
|
}
|
|
326
|
-
|
|
477
|
+
log3(`commit: ${rows.length} rows`);
|
|
327
478
|
}
|
|
328
479
|
async upsertRowSql(row) {
|
|
329
480
|
const ts = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -379,7 +530,7 @@ var DeeplakeApi = class {
|
|
|
379
530
|
markers.writeIndexMarker(markerPath);
|
|
380
531
|
return;
|
|
381
532
|
}
|
|
382
|
-
|
|
533
|
+
log3(`index "${indexName}" skipped: ${e.message}`);
|
|
383
534
|
}
|
|
384
535
|
}
|
|
385
536
|
/**
|
|
@@ -469,13 +620,13 @@ var DeeplakeApi = class {
|
|
|
469
620
|
};
|
|
470
621
|
}
|
|
471
622
|
if (attempt < MAX_RETRIES && RETRYABLE_CODES.has(resp.status)) {
|
|
472
|
-
await
|
|
623
|
+
await sleep2(BASE_DELAY_MS * Math.pow(2, attempt) + Math.random() * 200);
|
|
473
624
|
continue;
|
|
474
625
|
}
|
|
475
626
|
return { tables: [], cacheable: false };
|
|
476
627
|
} catch {
|
|
477
628
|
if (attempt < MAX_RETRIES) {
|
|
478
|
-
await
|
|
629
|
+
await sleep2(BASE_DELAY_MS * Math.pow(2, attempt));
|
|
479
630
|
continue;
|
|
480
631
|
}
|
|
481
632
|
return { tables: [], cacheable: false };
|
|
@@ -503,9 +654,9 @@ var DeeplakeApi = class {
|
|
|
503
654
|
} catch (err) {
|
|
504
655
|
lastErr = err;
|
|
505
656
|
const msg = err instanceof Error ? err.message : String(err);
|
|
506
|
-
|
|
657
|
+
log3(`CREATE TABLE "${label}" attempt ${attempt + 1}/${OUTER_BACKOFFS_MS.length + 1} failed: ${msg}`);
|
|
507
658
|
if (attempt < OUTER_BACKOFFS_MS.length) {
|
|
508
|
-
await
|
|
659
|
+
await sleep2(OUTER_BACKOFFS_MS[attempt]);
|
|
509
660
|
}
|
|
510
661
|
}
|
|
511
662
|
}
|
|
@@ -516,9 +667,9 @@ var DeeplakeApi = class {
|
|
|
516
667
|
const tbl = sqlIdent(name ?? this.tableName);
|
|
517
668
|
const tables = await this.listTables();
|
|
518
669
|
if (!tables.includes(tbl)) {
|
|
519
|
-
|
|
670
|
+
log3(`table "${tbl}" not found, creating`);
|
|
520
671
|
await this.createTableWithRetry(`CREATE TABLE IF NOT EXISTS "${tbl}" (id TEXT NOT NULL DEFAULT '', path TEXT NOT NULL DEFAULT '', filename TEXT NOT NULL DEFAULT '', summary TEXT NOT NULL DEFAULT '', summary_embedding FLOAT4[], author TEXT NOT NULL DEFAULT '', mime_type TEXT NOT NULL DEFAULT 'text/plain', size_bytes BIGINT NOT NULL DEFAULT 0, project TEXT NOT NULL DEFAULT '', description TEXT NOT NULL DEFAULT '', agent TEXT NOT NULL DEFAULT '', plugin_version TEXT NOT NULL DEFAULT '', creation_date TEXT NOT NULL DEFAULT '', last_update_date TEXT NOT NULL DEFAULT '') USING deeplake`, tbl);
|
|
521
|
-
|
|
672
|
+
log3(`table "${tbl}" created`);
|
|
522
673
|
if (!tables.includes(tbl))
|
|
523
674
|
this._tablesCache = [...tables, tbl];
|
|
524
675
|
}
|
|
@@ -531,9 +682,9 @@ var DeeplakeApi = class {
|
|
|
531
682
|
const safe = sqlIdent(name);
|
|
532
683
|
const tables = await this.listTables();
|
|
533
684
|
if (!tables.includes(safe)) {
|
|
534
|
-
|
|
685
|
+
log3(`table "${safe}" not found, creating`);
|
|
535
686
|
await this.createTableWithRetry(`CREATE TABLE IF NOT EXISTS "${safe}" (id TEXT NOT NULL DEFAULT '', path TEXT NOT NULL DEFAULT '', filename TEXT NOT NULL DEFAULT '', message JSONB, message_embedding FLOAT4[], author TEXT NOT NULL DEFAULT '', mime_type TEXT NOT NULL DEFAULT 'application/json', size_bytes BIGINT NOT NULL DEFAULT 0, project TEXT NOT NULL DEFAULT '', description TEXT NOT NULL DEFAULT '', agent TEXT NOT NULL DEFAULT '', plugin_version TEXT NOT NULL DEFAULT '', creation_date TEXT NOT NULL DEFAULT '', last_update_date TEXT NOT NULL DEFAULT '') USING deeplake`, safe);
|
|
536
|
-
|
|
687
|
+
log3(`table "${safe}" created`);
|
|
537
688
|
if (!tables.includes(safe))
|
|
538
689
|
this._tablesCache = [...tables, safe];
|
|
539
690
|
}
|
|
@@ -556,9 +707,9 @@ var DeeplakeApi = class {
|
|
|
556
707
|
const safe = sqlIdent(name);
|
|
557
708
|
const tables = await this.listTables();
|
|
558
709
|
if (!tables.includes(safe)) {
|
|
559
|
-
|
|
710
|
+
log3(`table "${safe}" not found, creating`);
|
|
560
711
|
await this.createTableWithRetry(`CREATE TABLE IF NOT EXISTS "${safe}" (id TEXT NOT NULL DEFAULT '', name TEXT NOT NULL DEFAULT '', project TEXT NOT NULL DEFAULT '', project_key TEXT NOT NULL DEFAULT '', local_path TEXT NOT NULL DEFAULT '', install TEXT NOT NULL DEFAULT 'project', source_sessions TEXT NOT NULL DEFAULT '[]', source_agent TEXT NOT NULL DEFAULT '', scope TEXT NOT NULL DEFAULT 'me', author TEXT NOT NULL DEFAULT '', description TEXT NOT NULL DEFAULT '', trigger_text TEXT NOT NULL DEFAULT '', body TEXT NOT NULL DEFAULT '', version BIGINT NOT NULL DEFAULT 1, created_at TEXT NOT NULL DEFAULT '', updated_at TEXT NOT NULL DEFAULT '') USING deeplake`, safe);
|
|
561
|
-
|
|
712
|
+
log3(`table "${safe}" created`);
|
|
562
713
|
if (!tables.includes(safe))
|
|
563
714
|
this._tablesCache = [...tables, safe];
|
|
564
715
|
}
|
|
@@ -569,20 +720,20 @@ var DeeplakeApi = class {
|
|
|
569
720
|
// dist/src/hooks/codex/spawn-wiki-worker.js
|
|
570
721
|
import { spawn, execSync } from "node:child_process";
|
|
571
722
|
import { fileURLToPath } from "node:url";
|
|
572
|
-
import { dirname as dirname2, join as
|
|
573
|
-
import { writeFileSync as
|
|
574
|
-
import { homedir as
|
|
723
|
+
import { dirname as dirname2, join as join8 } from "node:path";
|
|
724
|
+
import { writeFileSync as writeFileSync4, mkdirSync as mkdirSync5 } from "node:fs";
|
|
725
|
+
import { homedir as homedir5, tmpdir as tmpdir2 } from "node:os";
|
|
575
726
|
|
|
576
727
|
// dist/src/utils/wiki-log.js
|
|
577
|
-
import { mkdirSync as
|
|
578
|
-
import { join as
|
|
728
|
+
import { mkdirSync as mkdirSync4, appendFileSync as appendFileSync2 } from "node:fs";
|
|
729
|
+
import { join as join6 } from "node:path";
|
|
579
730
|
function makeWikiLogger(hooksDir, filename = "deeplake-wiki.log") {
|
|
580
|
-
const path =
|
|
731
|
+
const path = join6(hooksDir, filename);
|
|
581
732
|
return {
|
|
582
733
|
path,
|
|
583
734
|
log(msg) {
|
|
584
735
|
try {
|
|
585
|
-
|
|
736
|
+
mkdirSync4(hooksDir, { recursive: true });
|
|
586
737
|
appendFileSync2(path, `[${utcTimestamp()}] ${msg}
|
|
587
738
|
`);
|
|
588
739
|
} catch {
|
|
@@ -592,18 +743,18 @@ function makeWikiLogger(hooksDir, filename = "deeplake-wiki.log") {
|
|
|
592
743
|
}
|
|
593
744
|
|
|
594
745
|
// dist/src/utils/version-check.js
|
|
595
|
-
import { readFileSync as
|
|
596
|
-
import { dirname, join as
|
|
746
|
+
import { readFileSync as readFileSync5 } from "node:fs";
|
|
747
|
+
import { dirname, join as join7 } from "node:path";
|
|
597
748
|
function getInstalledVersion(bundleDir, pluginManifestDir) {
|
|
598
749
|
try {
|
|
599
|
-
const pluginJson =
|
|
600
|
-
const plugin = JSON.parse(
|
|
750
|
+
const pluginJson = join7(bundleDir, "..", pluginManifestDir, "plugin.json");
|
|
751
|
+
const plugin = JSON.parse(readFileSync5(pluginJson, "utf-8"));
|
|
601
752
|
if (plugin.version)
|
|
602
753
|
return plugin.version;
|
|
603
754
|
} catch {
|
|
604
755
|
}
|
|
605
756
|
try {
|
|
606
|
-
const stamp =
|
|
757
|
+
const stamp = readFileSync5(join7(bundleDir, "..", ".hivemind_version"), "utf-8").trim();
|
|
607
758
|
if (stamp)
|
|
608
759
|
return stamp;
|
|
609
760
|
} catch {
|
|
@@ -618,9 +769,9 @@ function getInstalledVersion(bundleDir, pluginManifestDir) {
|
|
|
618
769
|
]);
|
|
619
770
|
let dir = bundleDir;
|
|
620
771
|
for (let i = 0; i < 5; i++) {
|
|
621
|
-
const candidate =
|
|
772
|
+
const candidate = join7(dir, "package.json");
|
|
622
773
|
try {
|
|
623
|
-
const pkg = JSON.parse(
|
|
774
|
+
const pkg = JSON.parse(readFileSync5(candidate, "utf-8"));
|
|
624
775
|
if (HIVEMIND_PKG_NAMES.has(pkg.name) && pkg.version)
|
|
625
776
|
return pkg.version;
|
|
626
777
|
} catch {
|
|
@@ -634,8 +785,8 @@ function getInstalledVersion(bundleDir, pluginManifestDir) {
|
|
|
634
785
|
}
|
|
635
786
|
|
|
636
787
|
// dist/src/hooks/codex/spawn-wiki-worker.js
|
|
637
|
-
var HOME =
|
|
638
|
-
var wikiLogger = makeWikiLogger(
|
|
788
|
+
var HOME = homedir5();
|
|
789
|
+
var wikiLogger = makeWikiLogger(join8(HOME, ".codex", "hooks"));
|
|
639
790
|
var WIKI_LOG = wikiLogger.path;
|
|
640
791
|
var WIKI_PROMPT_TEMPLATE = `You are building a personal wiki from a coding session. Your goal is to extract every piece of knowledge \u2014 entities, decisions, relationships, and facts \u2014 into a structured, searchable wiki entry.
|
|
641
792
|
|
|
@@ -697,11 +848,11 @@ function findCodexBin() {
|
|
|
697
848
|
function spawnCodexWikiWorker(opts) {
|
|
698
849
|
const { config, sessionId, cwd, bundleDir, reason } = opts;
|
|
699
850
|
const projectName = cwd.split("/").pop() || "unknown";
|
|
700
|
-
const tmpDir =
|
|
701
|
-
|
|
851
|
+
const tmpDir = join8(tmpdir2(), `deeplake-wiki-${sessionId}-${Date.now()}`);
|
|
852
|
+
mkdirSync5(tmpDir, { recursive: true });
|
|
702
853
|
const pluginVersion = getInstalledVersion(bundleDir, ".codex-plugin") ?? "";
|
|
703
|
-
const configFile =
|
|
704
|
-
|
|
854
|
+
const configFile = join8(tmpDir, "config.json");
|
|
855
|
+
writeFileSync4(configFile, JSON.stringify({
|
|
705
856
|
apiUrl: config.apiUrl,
|
|
706
857
|
token: config.token,
|
|
707
858
|
orgId: config.orgId,
|
|
@@ -715,11 +866,11 @@ function spawnCodexWikiWorker(opts) {
|
|
|
715
866
|
tmpDir,
|
|
716
867
|
codexBin: findCodexBin(),
|
|
717
868
|
wikiLog: WIKI_LOG,
|
|
718
|
-
hooksDir:
|
|
869
|
+
hooksDir: join8(HOME, ".codex", "hooks"),
|
|
719
870
|
promptTemplate: WIKI_PROMPT_TEMPLATE
|
|
720
871
|
}));
|
|
721
872
|
wikiLog(`${reason}: spawning summary worker for ${sessionId}`);
|
|
722
|
-
const workerPath =
|
|
873
|
+
const workerPath = join8(bundleDir, "wiki-worker.js");
|
|
723
874
|
spawn("nohup", ["node", workerPath, configFile], {
|
|
724
875
|
detached: true,
|
|
725
876
|
stdio: ["ignore", "ignore", "ignore"]
|
|
@@ -733,15 +884,15 @@ function bundleDirFromImportMeta(importMetaUrl) {
|
|
|
733
884
|
// dist/src/skillify/spawn-skillify-worker.js
|
|
734
885
|
import { spawn as spawn2 } from "node:child_process";
|
|
735
886
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
736
|
-
import { dirname as dirname3, join as
|
|
737
|
-
import { writeFileSync as
|
|
738
|
-
import { homedir as
|
|
887
|
+
import { dirname as dirname3, join as join10 } from "node:path";
|
|
888
|
+
import { writeFileSync as writeFileSync5, mkdirSync as mkdirSync6, appendFileSync as appendFileSync3, chmodSync } from "node:fs";
|
|
889
|
+
import { homedir as homedir7, tmpdir as tmpdir3 } from "node:os";
|
|
739
890
|
|
|
740
891
|
// dist/src/skillify/gate-runner.js
|
|
741
892
|
import { existsSync as existsSync3 } from "node:fs";
|
|
742
893
|
import { createRequire } from "node:module";
|
|
743
|
-
import { homedir as
|
|
744
|
-
import { join as
|
|
894
|
+
import { homedir as homedir6 } from "node:os";
|
|
895
|
+
import { join as join9 } from "node:path";
|
|
745
896
|
var requireForCp = createRequire(import.meta.url);
|
|
746
897
|
var { execFileSync: runChildProcess } = requireForCp("node:child_process");
|
|
747
898
|
var inheritedEnv = process;
|
|
@@ -753,7 +904,7 @@ function firstExistingPath(candidates) {
|
|
|
753
904
|
return null;
|
|
754
905
|
}
|
|
755
906
|
function findAgentBin(agent) {
|
|
756
|
-
const home =
|
|
907
|
+
const home = homedir6();
|
|
757
908
|
switch (agent) {
|
|
758
909
|
// /usr/bin/<name> is included in every candidate list — that's the
|
|
759
910
|
// common Linux package-manager install path (apt, dnf, pacman). Old
|
|
@@ -762,54 +913,54 @@ function findAgentBin(agent) {
|
|
|
762
913
|
// #170 caught the gap.
|
|
763
914
|
case "claude_code":
|
|
764
915
|
return firstExistingPath([
|
|
765
|
-
|
|
916
|
+
join9(home, ".claude", "local", "claude"),
|
|
766
917
|
"/usr/local/bin/claude",
|
|
767
918
|
"/usr/bin/claude",
|
|
768
|
-
|
|
769
|
-
|
|
919
|
+
join9(home, ".npm-global", "bin", "claude"),
|
|
920
|
+
join9(home, ".local", "bin", "claude"),
|
|
770
921
|
"/opt/homebrew/bin/claude"
|
|
771
|
-
]) ??
|
|
922
|
+
]) ?? join9(home, ".claude", "local", "claude");
|
|
772
923
|
case "codex":
|
|
773
924
|
return firstExistingPath([
|
|
774
925
|
"/usr/local/bin/codex",
|
|
775
926
|
"/usr/bin/codex",
|
|
776
|
-
|
|
777
|
-
|
|
927
|
+
join9(home, ".npm-global", "bin", "codex"),
|
|
928
|
+
join9(home, ".local", "bin", "codex"),
|
|
778
929
|
"/opt/homebrew/bin/codex"
|
|
779
930
|
]) ?? "/usr/local/bin/codex";
|
|
780
931
|
case "cursor":
|
|
781
932
|
return firstExistingPath([
|
|
782
933
|
"/usr/local/bin/cursor-agent",
|
|
783
934
|
"/usr/bin/cursor-agent",
|
|
784
|
-
|
|
785
|
-
|
|
935
|
+
join9(home, ".npm-global", "bin", "cursor-agent"),
|
|
936
|
+
join9(home, ".local", "bin", "cursor-agent"),
|
|
786
937
|
"/opt/homebrew/bin/cursor-agent"
|
|
787
938
|
]) ?? "/usr/local/bin/cursor-agent";
|
|
788
939
|
case "hermes":
|
|
789
940
|
return firstExistingPath([
|
|
790
|
-
|
|
941
|
+
join9(home, ".local", "bin", "hermes"),
|
|
791
942
|
"/usr/local/bin/hermes",
|
|
792
943
|
"/usr/bin/hermes",
|
|
793
|
-
|
|
944
|
+
join9(home, ".npm-global", "bin", "hermes"),
|
|
794
945
|
"/opt/homebrew/bin/hermes"
|
|
795
|
-
]) ??
|
|
946
|
+
]) ?? join9(home, ".local", "bin", "hermes");
|
|
796
947
|
case "pi":
|
|
797
948
|
return firstExistingPath([
|
|
798
|
-
|
|
949
|
+
join9(home, ".local", "bin", "pi"),
|
|
799
950
|
"/usr/local/bin/pi",
|
|
800
951
|
"/usr/bin/pi",
|
|
801
|
-
|
|
952
|
+
join9(home, ".npm-global", "bin", "pi"),
|
|
802
953
|
"/opt/homebrew/bin/pi"
|
|
803
|
-
]) ??
|
|
954
|
+
]) ?? join9(home, ".local", "bin", "pi");
|
|
804
955
|
}
|
|
805
956
|
}
|
|
806
957
|
|
|
807
958
|
// dist/src/skillify/spawn-skillify-worker.js
|
|
808
|
-
var HOME2 =
|
|
809
|
-
var SKILLIFY_LOG =
|
|
959
|
+
var HOME2 = homedir7();
|
|
960
|
+
var SKILLIFY_LOG = join10(HOME2, ".claude", "hooks", "skillify.log");
|
|
810
961
|
function skillifyLog(msg) {
|
|
811
962
|
try {
|
|
812
|
-
|
|
963
|
+
mkdirSync6(dirname3(SKILLIFY_LOG), { recursive: true });
|
|
813
964
|
appendFileSync3(SKILLIFY_LOG, `[${utcTimestamp()}] ${msg}
|
|
814
965
|
`);
|
|
815
966
|
} catch {
|
|
@@ -817,11 +968,11 @@ function skillifyLog(msg) {
|
|
|
817
968
|
}
|
|
818
969
|
function spawnSkillifyWorker(opts) {
|
|
819
970
|
const { config, cwd, projectKey, project, bundleDir, agent, scopeConfig, currentSessionId, reason } = opts;
|
|
820
|
-
const tmpDir =
|
|
821
|
-
|
|
971
|
+
const tmpDir = join10(tmpdir3(), `deeplake-skillify-${projectKey}-${Date.now()}`);
|
|
972
|
+
mkdirSync6(tmpDir, { recursive: true, mode: 448 });
|
|
822
973
|
const gateBin = findAgentBin(agent);
|
|
823
|
-
const configFile =
|
|
824
|
-
|
|
974
|
+
const configFile = join10(tmpDir, "config.json");
|
|
975
|
+
writeFileSync5(configFile, JSON.stringify({
|
|
825
976
|
apiUrl: config.apiUrl,
|
|
826
977
|
token: config.token,
|
|
827
978
|
orgId: config.orgId,
|
|
@@ -851,7 +1002,7 @@ function spawnSkillifyWorker(opts) {
|
|
|
851
1002
|
} catch {
|
|
852
1003
|
}
|
|
853
1004
|
skillifyLog(`${reason}: spawning skillify worker for project=${project} key=${projectKey}`);
|
|
854
|
-
const workerPath =
|
|
1005
|
+
const workerPath = join10(bundleDir, "skillify-worker.js");
|
|
855
1006
|
spawn2("nohup", ["node", workerPath, configFile], {
|
|
856
1007
|
detached: true,
|
|
857
1008
|
stdio: ["ignore", "ignore", "ignore"]
|
|
@@ -860,31 +1011,31 @@ function spawnSkillifyWorker(opts) {
|
|
|
860
1011
|
}
|
|
861
1012
|
|
|
862
1013
|
// dist/src/skillify/state.js
|
|
863
|
-
import { readFileSync as
|
|
1014
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync6, writeSync, mkdirSync as mkdirSync7, renameSync as renameSync3, existsSync as existsSync5, unlinkSync as unlinkSync3, openSync as openSync2, closeSync as closeSync2 } from "node:fs";
|
|
864
1015
|
import { execSync as execSync2 } from "node:child_process";
|
|
865
|
-
import { homedir as
|
|
1016
|
+
import { homedir as homedir9 } from "node:os";
|
|
866
1017
|
import { createHash } from "node:crypto";
|
|
867
|
-
import { join as
|
|
1018
|
+
import { join as join12, basename } from "node:path";
|
|
868
1019
|
|
|
869
1020
|
// dist/src/skillify/legacy-migration.js
|
|
870
|
-
import { existsSync as existsSync4, renameSync } from "node:fs";
|
|
871
|
-
import { homedir as
|
|
872
|
-
import { join as
|
|
1021
|
+
import { existsSync as existsSync4, renameSync as renameSync2 } from "node:fs";
|
|
1022
|
+
import { homedir as homedir8 } from "node:os";
|
|
1023
|
+
import { join as join11 } from "node:path";
|
|
873
1024
|
var dlog = (msg) => log("skillify-migrate", msg);
|
|
874
1025
|
var attempted = false;
|
|
875
1026
|
function migrateLegacyStateDir() {
|
|
876
1027
|
if (attempted)
|
|
877
1028
|
return;
|
|
878
1029
|
attempted = true;
|
|
879
|
-
const root =
|
|
880
|
-
const legacy =
|
|
881
|
-
const current =
|
|
1030
|
+
const root = join11(homedir8(), ".deeplake", "state");
|
|
1031
|
+
const legacy = join11(root, "skilify");
|
|
1032
|
+
const current = join11(root, "skillify");
|
|
882
1033
|
if (!existsSync4(legacy))
|
|
883
1034
|
return;
|
|
884
1035
|
if (existsSync4(current))
|
|
885
1036
|
return;
|
|
886
1037
|
try {
|
|
887
|
-
|
|
1038
|
+
renameSync2(legacy, current);
|
|
888
1039
|
dlog(`migrated ${legacy} -> ${current}`);
|
|
889
1040
|
} catch (err) {
|
|
890
1041
|
const code = err.code;
|
|
@@ -898,17 +1049,17 @@ function migrateLegacyStateDir() {
|
|
|
898
1049
|
|
|
899
1050
|
// dist/src/skillify/state.js
|
|
900
1051
|
var dlog2 = (msg) => log("skillify-state", msg);
|
|
901
|
-
var STATE_DIR =
|
|
1052
|
+
var STATE_DIR = join12(homedir9(), ".deeplake", "state", "skillify");
|
|
902
1053
|
var YIELD_BUF = new Int32Array(new SharedArrayBuffer(4));
|
|
903
1054
|
var TRIGGER_THRESHOLD = (() => {
|
|
904
1055
|
const n = Number(process.env.HIVEMIND_SKILLIFY_EVERY_N_TURNS ?? "");
|
|
905
1056
|
return Number.isInteger(n) && n > 0 ? n : 20;
|
|
906
1057
|
})();
|
|
907
1058
|
function statePath(projectKey) {
|
|
908
|
-
return
|
|
1059
|
+
return join12(STATE_DIR, `${projectKey}.json`);
|
|
909
1060
|
}
|
|
910
|
-
function
|
|
911
|
-
return
|
|
1061
|
+
function lockPath2(projectKey) {
|
|
1062
|
+
return join12(STATE_DIR, `${projectKey}.lock`);
|
|
912
1063
|
}
|
|
913
1064
|
var DEFAULT_PORTS = {
|
|
914
1065
|
http: "80",
|
|
@@ -957,35 +1108,35 @@ function readState(projectKey) {
|
|
|
957
1108
|
if (!existsSync5(p))
|
|
958
1109
|
return null;
|
|
959
1110
|
try {
|
|
960
|
-
return JSON.parse(
|
|
1111
|
+
return JSON.parse(readFileSync6(p, "utf-8"));
|
|
961
1112
|
} catch {
|
|
962
1113
|
return null;
|
|
963
1114
|
}
|
|
964
1115
|
}
|
|
965
1116
|
function writeState(projectKey, state) {
|
|
966
1117
|
migrateLegacyStateDir();
|
|
967
|
-
|
|
1118
|
+
mkdirSync7(STATE_DIR, { recursive: true });
|
|
968
1119
|
const p = statePath(projectKey);
|
|
969
1120
|
const tmp = `${p}.${process.pid}.${Date.now()}.tmp`;
|
|
970
|
-
|
|
971
|
-
|
|
1121
|
+
writeFileSync6(tmp, JSON.stringify(state, null, 2));
|
|
1122
|
+
renameSync3(tmp, p);
|
|
972
1123
|
}
|
|
973
1124
|
function withRmwLock(projectKey, fn) {
|
|
974
1125
|
migrateLegacyStateDir();
|
|
975
|
-
|
|
976
|
-
const rmw =
|
|
1126
|
+
mkdirSync7(STATE_DIR, { recursive: true });
|
|
1127
|
+
const rmw = lockPath2(projectKey) + ".rmw";
|
|
977
1128
|
const deadline = Date.now() + 2e3;
|
|
978
1129
|
let fd = null;
|
|
979
1130
|
while (fd === null) {
|
|
980
1131
|
try {
|
|
981
|
-
fd =
|
|
1132
|
+
fd = openSync2(rmw, "wx");
|
|
982
1133
|
} catch (e) {
|
|
983
1134
|
if (e.code !== "EEXIST")
|
|
984
1135
|
throw e;
|
|
985
1136
|
if (Date.now() > deadline) {
|
|
986
1137
|
dlog2(`rmw lock deadline exceeded for ${projectKey}, reclaiming stale lock`);
|
|
987
1138
|
try {
|
|
988
|
-
|
|
1139
|
+
unlinkSync3(rmw);
|
|
989
1140
|
} catch (unlinkErr) {
|
|
990
1141
|
dlog2(`stale rmw lock unlink failed for ${projectKey}: ${unlinkErr.message}`);
|
|
991
1142
|
}
|
|
@@ -997,9 +1148,9 @@ function withRmwLock(projectKey, fn) {
|
|
|
997
1148
|
try {
|
|
998
1149
|
return fn();
|
|
999
1150
|
} finally {
|
|
1000
|
-
|
|
1151
|
+
closeSync2(fd);
|
|
1001
1152
|
try {
|
|
1002
|
-
|
|
1153
|
+
unlinkSync3(rmw);
|
|
1003
1154
|
} catch (unlinkErr) {
|
|
1004
1155
|
dlog2(`rmw lock cleanup failed for ${projectKey}: ${unlinkErr.message}`);
|
|
1005
1156
|
}
|
|
@@ -1015,29 +1166,29 @@ function resetCounter(projectKey) {
|
|
|
1015
1166
|
}
|
|
1016
1167
|
function tryAcquireWorkerLock(projectKey, maxAgeMs = 10 * 60 * 1e3) {
|
|
1017
1168
|
migrateLegacyStateDir();
|
|
1018
|
-
|
|
1019
|
-
const p =
|
|
1169
|
+
mkdirSync7(STATE_DIR, { recursive: true });
|
|
1170
|
+
const p = lockPath2(projectKey);
|
|
1020
1171
|
if (existsSync5(p)) {
|
|
1021
1172
|
try {
|
|
1022
|
-
const ageMs = Date.now() - parseInt(
|
|
1173
|
+
const ageMs = Date.now() - parseInt(readFileSync6(p, "utf-8"), 10);
|
|
1023
1174
|
if (Number.isFinite(ageMs) && ageMs < maxAgeMs)
|
|
1024
1175
|
return false;
|
|
1025
1176
|
} catch (readErr) {
|
|
1026
1177
|
dlog2(`worker lock unreadable for ${projectKey}, treating as stale: ${readErr.message}`);
|
|
1027
1178
|
}
|
|
1028
1179
|
try {
|
|
1029
|
-
|
|
1180
|
+
unlinkSync3(p);
|
|
1030
1181
|
} catch (unlinkErr) {
|
|
1031
1182
|
dlog2(`could not unlink stale worker lock for ${projectKey}: ${unlinkErr.message}`);
|
|
1032
1183
|
return false;
|
|
1033
1184
|
}
|
|
1034
1185
|
}
|
|
1035
1186
|
try {
|
|
1036
|
-
const fd =
|
|
1187
|
+
const fd = openSync2(p, "wx");
|
|
1037
1188
|
try {
|
|
1038
1189
|
writeSync(fd, String(Date.now()));
|
|
1039
1190
|
} finally {
|
|
1040
|
-
|
|
1191
|
+
closeSync2(fd);
|
|
1041
1192
|
}
|
|
1042
1193
|
return true;
|
|
1043
1194
|
} catch {
|
|
@@ -1045,26 +1196,26 @@ function tryAcquireWorkerLock(projectKey, maxAgeMs = 10 * 60 * 1e3) {
|
|
|
1045
1196
|
}
|
|
1046
1197
|
}
|
|
1047
1198
|
function releaseWorkerLock(projectKey) {
|
|
1048
|
-
const p =
|
|
1199
|
+
const p = lockPath2(projectKey);
|
|
1049
1200
|
try {
|
|
1050
|
-
|
|
1201
|
+
unlinkSync3(p);
|
|
1051
1202
|
} catch {
|
|
1052
1203
|
}
|
|
1053
1204
|
}
|
|
1054
1205
|
|
|
1055
1206
|
// dist/src/skillify/scope-config.js
|
|
1056
|
-
import { existsSync as existsSync6, mkdirSync as
|
|
1057
|
-
import { homedir as
|
|
1058
|
-
import { join as
|
|
1059
|
-
var STATE_DIR2 =
|
|
1060
|
-
var CONFIG_PATH =
|
|
1207
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync8, readFileSync as readFileSync7, writeFileSync as writeFileSync7 } from "node:fs";
|
|
1208
|
+
import { homedir as homedir10 } from "node:os";
|
|
1209
|
+
import { join as join13 } from "node:path";
|
|
1210
|
+
var STATE_DIR2 = join13(homedir10(), ".deeplake", "state", "skillify");
|
|
1211
|
+
var CONFIG_PATH = join13(STATE_DIR2, "config.json");
|
|
1061
1212
|
var DEFAULT = { scope: "me", team: [], install: "project" };
|
|
1062
1213
|
function loadScopeConfig() {
|
|
1063
1214
|
migrateLegacyStateDir();
|
|
1064
1215
|
if (!existsSync6(CONFIG_PATH))
|
|
1065
1216
|
return DEFAULT;
|
|
1066
1217
|
try {
|
|
1067
|
-
const raw = JSON.parse(
|
|
1218
|
+
const raw = JSON.parse(readFileSync7(CONFIG_PATH, "utf-8"));
|
|
1068
1219
|
const scope = raw.scope === "team" ? "team" : raw.scope === "org" ? "team" : "me";
|
|
1069
1220
|
const team = Array.isArray(raw.team) ? raw.team.filter((s) => typeof s === "string") : [];
|
|
1070
1221
|
const install = raw.install === "global" ? "global" : "project";
|
|
@@ -1115,39 +1266,39 @@ function forceSessionEndTrigger(opts) {
|
|
|
1115
1266
|
}
|
|
1116
1267
|
|
|
1117
1268
|
// dist/src/hooks/summary-state.js
|
|
1118
|
-
import { readFileSync as
|
|
1119
|
-
import { homedir as
|
|
1120
|
-
import { join as
|
|
1269
|
+
import { readFileSync as readFileSync8, writeFileSync as writeFileSync8, writeSync as writeSync2, mkdirSync as mkdirSync9, renameSync as renameSync4, existsSync as existsSync7, unlinkSync as unlinkSync4, openSync as openSync3, closeSync as closeSync3 } from "node:fs";
|
|
1270
|
+
import { homedir as homedir11 } from "node:os";
|
|
1271
|
+
import { join as join14 } from "node:path";
|
|
1121
1272
|
var dlog3 = (msg) => log("summary-state", msg);
|
|
1122
|
-
var STATE_DIR3 =
|
|
1273
|
+
var STATE_DIR3 = join14(homedir11(), ".claude", "hooks", "summary-state");
|
|
1123
1274
|
var YIELD_BUF2 = new Int32Array(new SharedArrayBuffer(4));
|
|
1124
|
-
function
|
|
1125
|
-
return
|
|
1275
|
+
function lockPath3(sessionId) {
|
|
1276
|
+
return join14(STATE_DIR3, `${sessionId}.lock`);
|
|
1126
1277
|
}
|
|
1127
1278
|
function tryAcquireLock(sessionId, maxAgeMs = 10 * 60 * 1e3) {
|
|
1128
|
-
|
|
1129
|
-
const p =
|
|
1279
|
+
mkdirSync9(STATE_DIR3, { recursive: true });
|
|
1280
|
+
const p = lockPath3(sessionId);
|
|
1130
1281
|
if (existsSync7(p)) {
|
|
1131
1282
|
try {
|
|
1132
|
-
const ageMs = Date.now() - parseInt(
|
|
1283
|
+
const ageMs = Date.now() - parseInt(readFileSync8(p, "utf-8"), 10);
|
|
1133
1284
|
if (Number.isFinite(ageMs) && ageMs < maxAgeMs)
|
|
1134
1285
|
return false;
|
|
1135
1286
|
} catch (readErr) {
|
|
1136
1287
|
dlog3(`lock file unreadable for ${sessionId}, treating as stale: ${readErr.message}`);
|
|
1137
1288
|
}
|
|
1138
1289
|
try {
|
|
1139
|
-
|
|
1290
|
+
unlinkSync4(p);
|
|
1140
1291
|
} catch (unlinkErr) {
|
|
1141
1292
|
dlog3(`could not unlink stale lock for ${sessionId}: ${unlinkErr.message}`);
|
|
1142
1293
|
return false;
|
|
1143
1294
|
}
|
|
1144
1295
|
}
|
|
1145
1296
|
try {
|
|
1146
|
-
const fd =
|
|
1297
|
+
const fd = openSync3(p, "wx");
|
|
1147
1298
|
try {
|
|
1148
1299
|
writeSync2(fd, String(Date.now()));
|
|
1149
1300
|
} finally {
|
|
1150
|
-
|
|
1301
|
+
closeSync3(fd);
|
|
1151
1302
|
}
|
|
1152
1303
|
return true;
|
|
1153
1304
|
} catch (e) {
|
|
@@ -1158,7 +1309,7 @@ function tryAcquireLock(sessionId, maxAgeMs = 10 * 60 * 1e3) {
|
|
|
1158
1309
|
}
|
|
1159
1310
|
function releaseLock(sessionId) {
|
|
1160
1311
|
try {
|
|
1161
|
-
|
|
1312
|
+
unlinkSync4(lockPath3(sessionId));
|
|
1162
1313
|
} catch (e) {
|
|
1163
1314
|
if (e?.code !== "ENOENT") {
|
|
1164
1315
|
dlog3(`releaseLock unlink failed for ${sessionId}: ${e.message}`);
|
|
@@ -1175,9 +1326,9 @@ function buildSessionPath(config, sessionId) {
|
|
|
1175
1326
|
// dist/src/embeddings/client.js
|
|
1176
1327
|
import { connect } from "node:net";
|
|
1177
1328
|
import { spawn as spawn3 } from "node:child_process";
|
|
1178
|
-
import { openSync as
|
|
1179
|
-
import { homedir as
|
|
1180
|
-
import { join as
|
|
1329
|
+
import { openSync as openSync4, closeSync as closeSync4, writeSync as writeSync3, unlinkSync as unlinkSync5, existsSync as existsSync8, readFileSync as readFileSync9 } from "node:fs";
|
|
1330
|
+
import { homedir as homedir12 } from "node:os";
|
|
1331
|
+
import { join as join15 } from "node:path";
|
|
1181
1332
|
|
|
1182
1333
|
// dist/src/embeddings/protocol.js
|
|
1183
1334
|
var DEFAULT_SOCKET_DIR = "/tmp";
|
|
@@ -1191,12 +1342,13 @@ function pidPathFor(uid, dir = DEFAULT_SOCKET_DIR) {
|
|
|
1191
1342
|
}
|
|
1192
1343
|
|
|
1193
1344
|
// dist/src/embeddings/client.js
|
|
1194
|
-
var SHARED_DAEMON_PATH =
|
|
1195
|
-
var
|
|
1345
|
+
var SHARED_DAEMON_PATH = join15(homedir12(), ".hivemind", "embed-deps", "embed-daemon.js");
|
|
1346
|
+
var log4 = (m) => log("embed-client", m);
|
|
1196
1347
|
function getUid() {
|
|
1197
1348
|
const uid = typeof process.getuid === "function" ? process.getuid() : void 0;
|
|
1198
1349
|
return uid !== void 0 ? String(uid) : process.env.USER ?? "default";
|
|
1199
1350
|
}
|
|
1351
|
+
var _recycledStuckDaemon = false;
|
|
1200
1352
|
var EmbedClient = class {
|
|
1201
1353
|
socketPath;
|
|
1202
1354
|
pidPath;
|
|
@@ -1205,6 +1357,7 @@ var EmbedClient = class {
|
|
|
1205
1357
|
autoSpawn;
|
|
1206
1358
|
spawnWaitMs;
|
|
1207
1359
|
nextId = 0;
|
|
1360
|
+
helloVerified = false;
|
|
1208
1361
|
constructor(opts = {}) {
|
|
1209
1362
|
const uid = getUid();
|
|
1210
1363
|
const dir = opts.socketDir ?? "/tmp";
|
|
@@ -1221,8 +1374,33 @@ var EmbedClient = class {
|
|
|
1221
1374
|
*
|
|
1222
1375
|
* Fire-and-forget spawn on miss: if the daemon isn't up, this call returns
|
|
1223
1376
|
* null AND kicks off a background spawn. The next call finds a ready daemon.
|
|
1377
|
+
*
|
|
1378
|
+
* Stuck-daemon recycle: if the daemon returns a transformers-missing
|
|
1379
|
+
* error (typical after a marketplace upgrade left an older daemon process
|
|
1380
|
+
* alive but with no node_modules accessible from its bundle path), we
|
|
1381
|
+
* SIGTERM it and clear its sock/pid so the very next call spawns a fresh
|
|
1382
|
+
* daemon from the current bundle. Without this, the stuck daemon would
|
|
1383
|
+
* keep poisoning every session until its 10-minute idle-out fires.
|
|
1224
1384
|
*/
|
|
1225
1385
|
async embed(text, kind = "document") {
|
|
1386
|
+
const v = await this.embedAttempt(text, kind);
|
|
1387
|
+
if (v !== "recycled")
|
|
1388
|
+
return v;
|
|
1389
|
+
if (!this.autoSpawn)
|
|
1390
|
+
return null;
|
|
1391
|
+
this.trySpawnDaemon();
|
|
1392
|
+
await this.waitForDaemonReady();
|
|
1393
|
+
const retry = await this.embedAttempt(text, kind);
|
|
1394
|
+
return retry === "recycled" ? null : retry;
|
|
1395
|
+
}
|
|
1396
|
+
/**
|
|
1397
|
+
* One round-trip: connect → verify → embed. Returns:
|
|
1398
|
+
* - number[] : embedding vector (happy path)
|
|
1399
|
+
* - null : timeout / daemon error / transformers-missing
|
|
1400
|
+
* - "recycled": verifyDaemonOnce killed the daemon mid-call;
|
|
1401
|
+
* caller should respawn and retry once.
|
|
1402
|
+
*/
|
|
1403
|
+
async embedAttempt(text, kind) {
|
|
1226
1404
|
let sock;
|
|
1227
1405
|
try {
|
|
1228
1406
|
sock = await this.connectOnce();
|
|
@@ -1232,17 +1410,25 @@ var EmbedClient = class {
|
|
|
1232
1410
|
return null;
|
|
1233
1411
|
}
|
|
1234
1412
|
try {
|
|
1413
|
+
const recycled = await this.verifyDaemonOnce(sock);
|
|
1414
|
+
if (recycled) {
|
|
1415
|
+
return "recycled";
|
|
1416
|
+
}
|
|
1235
1417
|
const id = String(++this.nextId);
|
|
1236
1418
|
const req = { op: "embed", id, kind, text };
|
|
1237
1419
|
const resp = await this.sendAndWait(sock, req);
|
|
1238
1420
|
if (resp.error || !("embedding" in resp) || !resp.embedding) {
|
|
1239
|
-
|
|
1421
|
+
const err = resp.error ?? "no embedding";
|
|
1422
|
+
log4(`embed err: ${err}`);
|
|
1423
|
+
if (isTransformersMissingError(err)) {
|
|
1424
|
+
this.handleTransformersMissing(err);
|
|
1425
|
+
}
|
|
1240
1426
|
return null;
|
|
1241
1427
|
}
|
|
1242
1428
|
return resp.embedding;
|
|
1243
1429
|
} catch (e) {
|
|
1244
1430
|
const err = e instanceof Error ? e.message : String(e);
|
|
1245
|
-
|
|
1431
|
+
log4(`embed failed: ${err}`);
|
|
1246
1432
|
return null;
|
|
1247
1433
|
} finally {
|
|
1248
1434
|
try {
|
|
@@ -1251,6 +1437,123 @@ var EmbedClient = class {
|
|
|
1251
1437
|
}
|
|
1252
1438
|
}
|
|
1253
1439
|
}
|
|
1440
|
+
/**
|
|
1441
|
+
* Poll for the sock file to come back after `trySpawnDaemon` — used by
|
|
1442
|
+
* the recycle retry path. Best-effort: caps at `spawnWaitMs` and
|
|
1443
|
+
* returns regardless so the retry attempt can run.
|
|
1444
|
+
*/
|
|
1445
|
+
async waitForDaemonReady() {
|
|
1446
|
+
const deadline = Date.now() + this.spawnWaitMs;
|
|
1447
|
+
while (Date.now() < deadline) {
|
|
1448
|
+
if (existsSync8(this.socketPath))
|
|
1449
|
+
return;
|
|
1450
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
1451
|
+
}
|
|
1452
|
+
}
|
|
1453
|
+
/**
|
|
1454
|
+
* Send a `hello` on first successful connect per EmbedClient instance.
|
|
1455
|
+
* If the daemon answers with a path that doesn't match our configured
|
|
1456
|
+
* daemonEntry — typical after a marketplace upgrade replaced the bundle
|
|
1457
|
+
* — SIGTERM the daemon + clear sock/pid so the next call spawns from the
|
|
1458
|
+
* current bundle.
|
|
1459
|
+
*
|
|
1460
|
+
* `helloVerified` is set ONLY after we've seen a compatible response,
|
|
1461
|
+
* so a transient probe failure or a recycle-triggering mismatch leaves
|
|
1462
|
+
* the flag false; the next reconnect re-runs verification against
|
|
1463
|
+
* whatever daemon is then live (typically the fresh spawn).
|
|
1464
|
+
*/
|
|
1465
|
+
async verifyDaemonOnce(sock) {
|
|
1466
|
+
if (this.helloVerified)
|
|
1467
|
+
return false;
|
|
1468
|
+
if (!this.daemonEntry) {
|
|
1469
|
+
this.helloVerified = true;
|
|
1470
|
+
return false;
|
|
1471
|
+
}
|
|
1472
|
+
const id = String(++this.nextId);
|
|
1473
|
+
const req = { op: "hello", id };
|
|
1474
|
+
let resp;
|
|
1475
|
+
try {
|
|
1476
|
+
resp = await this.sendAndWait(sock, req);
|
|
1477
|
+
} catch (e) {
|
|
1478
|
+
log4(`hello probe failed (inconclusive, will retry next connect): ${e instanceof Error ? e.message : String(e)}`);
|
|
1479
|
+
return false;
|
|
1480
|
+
}
|
|
1481
|
+
const hello = resp;
|
|
1482
|
+
if (_recycledStuckDaemon) {
|
|
1483
|
+
return false;
|
|
1484
|
+
}
|
|
1485
|
+
if (!hello.daemonPath) {
|
|
1486
|
+
_recycledStuckDaemon = true;
|
|
1487
|
+
log4(`daemon does not implement hello (older protocol); recycling`);
|
|
1488
|
+
this.recycleDaemon(hello.pid);
|
|
1489
|
+
return true;
|
|
1490
|
+
}
|
|
1491
|
+
if (hello.daemonPath !== this.daemonEntry && !existsSync8(hello.daemonPath)) {
|
|
1492
|
+
_recycledStuckDaemon = true;
|
|
1493
|
+
log4(`daemon path no longer on disk \u2014 running=${hello.daemonPath} (gone) expected=${this.daemonEntry}; recycling`);
|
|
1494
|
+
this.recycleDaemon(hello.pid);
|
|
1495
|
+
return true;
|
|
1496
|
+
}
|
|
1497
|
+
this.helloVerified = true;
|
|
1498
|
+
return false;
|
|
1499
|
+
}
|
|
1500
|
+
/**
|
|
1501
|
+
* On a transformers-missing error from the daemon, SIGTERM the stuck
|
|
1502
|
+
* daemon (the bundle daemon that can't find its deps) and clear
|
|
1503
|
+
* sock/pid so the next call spawns fresh.
|
|
1504
|
+
*
|
|
1505
|
+
* Previously this also enqueued a user-visible "Hivemind embeddings
|
|
1506
|
+
* disabled — deps missing" notification telling the user to run
|
|
1507
|
+
* `hivemind embeddings install`. The notification was removed because
|
|
1508
|
+
* (a) the recycle alone often fixes the issue silently, and (b) the
|
|
1509
|
+
* warning kept stacking on top of the primary session-start banner
|
|
1510
|
+
* which clashed with the single-slot priority model. The `detail`
|
|
1511
|
+
* argument is retained for future telemetry / debug logging.
|
|
1512
|
+
*/
|
|
1513
|
+
handleTransformersMissing(_detail) {
|
|
1514
|
+
if (!_recycledStuckDaemon) {
|
|
1515
|
+
_recycledStuckDaemon = true;
|
|
1516
|
+
this.recycleDaemon(null);
|
|
1517
|
+
}
|
|
1518
|
+
}
|
|
1519
|
+
/**
|
|
1520
|
+
* Best-effort SIGTERM + sock/pid cleanup. Tolerant of every missing-file
|
|
1521
|
+
* combination and dead-PID cases.
|
|
1522
|
+
*
|
|
1523
|
+
* Identity check: gate the SIGTERM on the daemon's socket file still
|
|
1524
|
+
* existing. We know the daemon was alive moments ago (we either just
|
|
1525
|
+
* got a hello response or the caller saw a transformers-missing error
|
|
1526
|
+
* the daemon emitted), but if the socket file is gone by the time we
|
|
1527
|
+
* try to kill, the daemon process is also gone and the PID we
|
|
1528
|
+
* captured may already have been recycled by the OS to an unrelated
|
|
1529
|
+
* user process. Mirrors the gate added to `killEmbedDaemon` in the
|
|
1530
|
+
* CLI — same failure mode, rarer trigger.
|
|
1531
|
+
*/
|
|
1532
|
+
recycleDaemon(reportedPid) {
|
|
1533
|
+
let pid = reportedPid;
|
|
1534
|
+
if (pid === null) {
|
|
1535
|
+
try {
|
|
1536
|
+
pid = Number.parseInt(readFileSync9(this.pidPath, "utf-8").trim(), 10);
|
|
1537
|
+
} catch {
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1540
|
+
if (Number.isFinite(pid) && pid !== null && pid > 0 && existsSync8(this.socketPath)) {
|
|
1541
|
+
try {
|
|
1542
|
+
process.kill(pid, "SIGTERM");
|
|
1543
|
+
} catch {
|
|
1544
|
+
}
|
|
1545
|
+
} else if (pid !== null) {
|
|
1546
|
+
log4(`recycle: socket gone, skipping SIGTERM on possibly-stale pid ${pid}`);
|
|
1547
|
+
}
|
|
1548
|
+
try {
|
|
1549
|
+
unlinkSync5(this.socketPath);
|
|
1550
|
+
} catch {
|
|
1551
|
+
}
|
|
1552
|
+
try {
|
|
1553
|
+
unlinkSync5(this.pidPath);
|
|
1554
|
+
} catch {
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1254
1557
|
/**
|
|
1255
1558
|
* Wait up to spawnWaitMs for the daemon to accept connections, spawning if
|
|
1256
1559
|
* necessary. Meant for SessionStart / long-running batches — not the hot path.
|
|
@@ -1274,7 +1577,7 @@ var EmbedClient = class {
|
|
|
1274
1577
|
}
|
|
1275
1578
|
}
|
|
1276
1579
|
connectOnce() {
|
|
1277
|
-
return new Promise((
|
|
1580
|
+
return new Promise((resolve2, reject) => {
|
|
1278
1581
|
const sock = connect(this.socketPath);
|
|
1279
1582
|
const to = setTimeout(() => {
|
|
1280
1583
|
sock.destroy();
|
|
@@ -1282,7 +1585,7 @@ var EmbedClient = class {
|
|
|
1282
1585
|
}, this.timeoutMs);
|
|
1283
1586
|
sock.once("connect", () => {
|
|
1284
1587
|
clearTimeout(to);
|
|
1285
|
-
|
|
1588
|
+
resolve2(sock);
|
|
1286
1589
|
});
|
|
1287
1590
|
sock.once("error", (e) => {
|
|
1288
1591
|
clearTimeout(to);
|
|
@@ -1293,16 +1596,16 @@ var EmbedClient = class {
|
|
|
1293
1596
|
trySpawnDaemon() {
|
|
1294
1597
|
let fd;
|
|
1295
1598
|
try {
|
|
1296
|
-
fd =
|
|
1599
|
+
fd = openSync4(this.pidPath, "wx", 384);
|
|
1297
1600
|
writeSync3(fd, String(process.pid));
|
|
1298
1601
|
} catch (e) {
|
|
1299
1602
|
if (this.isPidFileStale()) {
|
|
1300
1603
|
try {
|
|
1301
|
-
|
|
1604
|
+
unlinkSync5(this.pidPath);
|
|
1302
1605
|
} catch {
|
|
1303
1606
|
}
|
|
1304
1607
|
try {
|
|
1305
|
-
fd =
|
|
1608
|
+
fd = openSync4(this.pidPath, "wx", 384);
|
|
1306
1609
|
writeSync3(fd, String(process.pid));
|
|
1307
1610
|
} catch {
|
|
1308
1611
|
return;
|
|
@@ -1312,10 +1615,10 @@ var EmbedClient = class {
|
|
|
1312
1615
|
}
|
|
1313
1616
|
}
|
|
1314
1617
|
if (!this.daemonEntry || !existsSync8(this.daemonEntry)) {
|
|
1315
|
-
|
|
1618
|
+
log4(`daemonEntry not configured or missing: ${this.daemonEntry}`);
|
|
1316
1619
|
try {
|
|
1317
|
-
|
|
1318
|
-
|
|
1620
|
+
closeSync4(fd);
|
|
1621
|
+
unlinkSync5(this.pidPath);
|
|
1319
1622
|
} catch {
|
|
1320
1623
|
}
|
|
1321
1624
|
return;
|
|
@@ -1327,14 +1630,14 @@ var EmbedClient = class {
|
|
|
1327
1630
|
env: process.env
|
|
1328
1631
|
});
|
|
1329
1632
|
child.unref();
|
|
1330
|
-
|
|
1633
|
+
log4(`spawned daemon pid=${child.pid}`);
|
|
1331
1634
|
} finally {
|
|
1332
|
-
|
|
1635
|
+
closeSync4(fd);
|
|
1333
1636
|
}
|
|
1334
1637
|
}
|
|
1335
1638
|
isPidFileStale() {
|
|
1336
1639
|
try {
|
|
1337
|
-
const raw =
|
|
1640
|
+
const raw = readFileSync9(this.pidPath, "utf-8").trim();
|
|
1338
1641
|
const pid = Number(raw);
|
|
1339
1642
|
if (!pid || Number.isNaN(pid))
|
|
1340
1643
|
return true;
|
|
@@ -1352,7 +1655,7 @@ var EmbedClient = class {
|
|
|
1352
1655
|
const deadline = Date.now() + this.spawnWaitMs;
|
|
1353
1656
|
let delay = 30;
|
|
1354
1657
|
while (Date.now() < deadline) {
|
|
1355
|
-
await
|
|
1658
|
+
await sleep3(delay);
|
|
1356
1659
|
delay = Math.min(delay * 1.5, 300);
|
|
1357
1660
|
if (!existsSync8(this.socketPath))
|
|
1358
1661
|
continue;
|
|
@@ -1364,7 +1667,7 @@ var EmbedClient = class {
|
|
|
1364
1667
|
throw new Error("daemon did not become ready within spawnWaitMs");
|
|
1365
1668
|
}
|
|
1366
1669
|
sendAndWait(sock, req) {
|
|
1367
|
-
return new Promise((
|
|
1670
|
+
return new Promise((resolve2, reject) => {
|
|
1368
1671
|
let buf = "";
|
|
1369
1672
|
const to = setTimeout(() => {
|
|
1370
1673
|
sock.destroy();
|
|
@@ -1379,7 +1682,7 @@ var EmbedClient = class {
|
|
|
1379
1682
|
const line = buf.slice(0, nl);
|
|
1380
1683
|
clearTimeout(to);
|
|
1381
1684
|
try {
|
|
1382
|
-
|
|
1685
|
+
resolve2(JSON.parse(line));
|
|
1383
1686
|
} catch (e) {
|
|
1384
1687
|
reject(e);
|
|
1385
1688
|
}
|
|
@@ -1396,9 +1699,14 @@ var EmbedClient = class {
|
|
|
1396
1699
|
});
|
|
1397
1700
|
}
|
|
1398
1701
|
};
|
|
1399
|
-
function
|
|
1702
|
+
function sleep3(ms) {
|
|
1400
1703
|
return new Promise((r) => setTimeout(r, ms));
|
|
1401
1704
|
}
|
|
1705
|
+
function isTransformersMissingError(err) {
|
|
1706
|
+
if (/hivemind embeddings install/i.test(err))
|
|
1707
|
+
return true;
|
|
1708
|
+
return /@huggingface\/transformers/i.test(err);
|
|
1709
|
+
}
|
|
1402
1710
|
|
|
1403
1711
|
// dist/src/embeddings/sql.js
|
|
1404
1712
|
function embeddingSqlLiteral(vec) {
|
|
@@ -1415,23 +1723,105 @@ function embeddingSqlLiteral(vec) {
|
|
|
1415
1723
|
|
|
1416
1724
|
// dist/src/embeddings/disable.js
|
|
1417
1725
|
import { createRequire as createRequire2 } from "node:module";
|
|
1418
|
-
import { homedir as
|
|
1419
|
-
import { join as
|
|
1726
|
+
import { homedir as homedir14 } from "node:os";
|
|
1727
|
+
import { join as join17 } from "node:path";
|
|
1420
1728
|
import { pathToFileURL } from "node:url";
|
|
1729
|
+
|
|
1730
|
+
// dist/src/user-config.js
|
|
1731
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync10, readFileSync as readFileSync10, renameSync as renameSync5, writeFileSync as writeFileSync9 } from "node:fs";
|
|
1732
|
+
import { homedir as homedir13 } from "node:os";
|
|
1733
|
+
import { dirname as dirname4, join as join16 } from "node:path";
|
|
1734
|
+
var _configPath = () => process.env.HIVEMIND_CONFIG_PATH ?? join16(homedir13(), ".deeplake", "config.json");
|
|
1735
|
+
var _cache = null;
|
|
1736
|
+
var _migrated = false;
|
|
1737
|
+
function readUserConfig() {
|
|
1738
|
+
if (_cache !== null)
|
|
1739
|
+
return _cache;
|
|
1740
|
+
const path = _configPath();
|
|
1741
|
+
if (!existsSync9(path)) {
|
|
1742
|
+
_cache = {};
|
|
1743
|
+
return _cache;
|
|
1744
|
+
}
|
|
1745
|
+
try {
|
|
1746
|
+
const raw = readFileSync10(path, "utf-8");
|
|
1747
|
+
const parsed = JSON.parse(raw);
|
|
1748
|
+
_cache = isPlainObject(parsed) ? parsed : {};
|
|
1749
|
+
} catch {
|
|
1750
|
+
_cache = {};
|
|
1751
|
+
}
|
|
1752
|
+
return _cache;
|
|
1753
|
+
}
|
|
1754
|
+
function writeUserConfig(patch) {
|
|
1755
|
+
const current = readUserConfig();
|
|
1756
|
+
const merged = deepMerge(current, patch);
|
|
1757
|
+
const path = _configPath();
|
|
1758
|
+
const dir = dirname4(path);
|
|
1759
|
+
if (!existsSync9(dir))
|
|
1760
|
+
mkdirSync10(dir, { recursive: true });
|
|
1761
|
+
const tmp = `${path}.tmp.${process.pid}`;
|
|
1762
|
+
writeFileSync9(tmp, JSON.stringify(merged, null, 2) + "\n", "utf-8");
|
|
1763
|
+
renameSync5(tmp, path);
|
|
1764
|
+
_cache = merged;
|
|
1765
|
+
return merged;
|
|
1766
|
+
}
|
|
1767
|
+
function getEmbeddingsEnabled() {
|
|
1768
|
+
const cfg = readUserConfig();
|
|
1769
|
+
if (cfg.embeddings && typeof cfg.embeddings.enabled === "boolean") {
|
|
1770
|
+
return cfg.embeddings.enabled;
|
|
1771
|
+
}
|
|
1772
|
+
if (_migrated) {
|
|
1773
|
+
return migrationValueFromEnv();
|
|
1774
|
+
}
|
|
1775
|
+
_migrated = true;
|
|
1776
|
+
const enabled = migrationValueFromEnv();
|
|
1777
|
+
try {
|
|
1778
|
+
writeUserConfig({ embeddings: { enabled } });
|
|
1779
|
+
} catch {
|
|
1780
|
+
_cache = { ...cfg ?? {}, embeddings: { ...cfg?.embeddings ?? {}, enabled } };
|
|
1781
|
+
}
|
|
1782
|
+
return enabled;
|
|
1783
|
+
}
|
|
1784
|
+
function migrationValueFromEnv() {
|
|
1785
|
+
const raw = process.env.HIVEMIND_EMBEDDINGS;
|
|
1786
|
+
if (raw === void 0)
|
|
1787
|
+
return false;
|
|
1788
|
+
if (raw === "false")
|
|
1789
|
+
return false;
|
|
1790
|
+
return true;
|
|
1791
|
+
}
|
|
1792
|
+
function isPlainObject(value) {
|
|
1793
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1794
|
+
}
|
|
1795
|
+
function deepMerge(base, patch) {
|
|
1796
|
+
const out = { ...base };
|
|
1797
|
+
for (const key of Object.keys(patch)) {
|
|
1798
|
+
const patchVal = patch[key];
|
|
1799
|
+
const baseVal = base[key];
|
|
1800
|
+
if (isPlainObject(patchVal) && isPlainObject(baseVal)) {
|
|
1801
|
+
out[key] = { ...baseVal, ...patchVal };
|
|
1802
|
+
} else if (patchVal !== void 0) {
|
|
1803
|
+
out[key] = patchVal;
|
|
1804
|
+
}
|
|
1805
|
+
}
|
|
1806
|
+
return out;
|
|
1807
|
+
}
|
|
1808
|
+
|
|
1809
|
+
// dist/src/embeddings/disable.js
|
|
1421
1810
|
var cachedStatus = null;
|
|
1422
1811
|
function defaultResolveTransformers() {
|
|
1812
|
+
const sharedDir = join17(homedir14(), ".hivemind", "embed-deps");
|
|
1423
1813
|
try {
|
|
1424
|
-
createRequire2(
|
|
1814
|
+
createRequire2(pathToFileURL(`${sharedDir}/`).href).resolve("@huggingface/transformers");
|
|
1425
1815
|
return;
|
|
1426
1816
|
} catch {
|
|
1427
1817
|
}
|
|
1428
|
-
|
|
1429
|
-
createRequire2(pathToFileURL(`${sharedDir}/`).href).resolve("@huggingface/transformers");
|
|
1818
|
+
createRequire2(import.meta.url).resolve("@huggingface/transformers");
|
|
1430
1819
|
}
|
|
1431
1820
|
var _resolve = defaultResolveTransformers;
|
|
1821
|
+
var _readEnabled = getEmbeddingsEnabled;
|
|
1432
1822
|
function detectStatus() {
|
|
1433
|
-
if (
|
|
1434
|
-
return "
|
|
1823
|
+
if (!_readEnabled())
|
|
1824
|
+
return "user-disabled";
|
|
1435
1825
|
try {
|
|
1436
1826
|
_resolve();
|
|
1437
1827
|
return "enabled";
|
|
@@ -1450,11 +1840,11 @@ function embeddingsDisabled() {
|
|
|
1450
1840
|
}
|
|
1451
1841
|
|
|
1452
1842
|
// dist/src/hooks/codex/stop.js
|
|
1453
|
-
var
|
|
1843
|
+
var log5 = (msg) => log("codex-stop", msg);
|
|
1454
1844
|
function resolveEmbedDaemonPath() {
|
|
1455
|
-
return
|
|
1845
|
+
return join18(dirname5(fileURLToPath3(import.meta.url)), "embeddings", "embed-daemon.js");
|
|
1456
1846
|
}
|
|
1457
|
-
var __bundleDir =
|
|
1847
|
+
var __bundleDir = dirname5(fileURLToPath3(import.meta.url));
|
|
1458
1848
|
var PLUGIN_VERSION = getInstalledVersion(__bundleDir, ".codex-plugin") ?? "";
|
|
1459
1849
|
var CAPTURE = process.env.HIVEMIND_CAPTURE !== "false";
|
|
1460
1850
|
async function main() {
|
|
@@ -1466,7 +1856,7 @@ async function main() {
|
|
|
1466
1856
|
return;
|
|
1467
1857
|
const config = loadConfig();
|
|
1468
1858
|
if (!config) {
|
|
1469
|
-
|
|
1859
|
+
log5("no config");
|
|
1470
1860
|
return;
|
|
1471
1861
|
}
|
|
1472
1862
|
if (CAPTURE) {
|
|
@@ -1478,8 +1868,8 @@ async function main() {
|
|
|
1478
1868
|
if (input.transcript_path) {
|
|
1479
1869
|
try {
|
|
1480
1870
|
const transcriptPath = input.transcript_path;
|
|
1481
|
-
if (
|
|
1482
|
-
const transcript =
|
|
1871
|
+
if (existsSync10(transcriptPath)) {
|
|
1872
|
+
const transcript = readFileSync11(transcriptPath, "utf-8");
|
|
1483
1873
|
const lines = transcript.trim().split("\n").reverse();
|
|
1484
1874
|
for (const line2 of lines) {
|
|
1485
1875
|
try {
|
|
@@ -1496,10 +1886,10 @@ async function main() {
|
|
|
1496
1886
|
}
|
|
1497
1887
|
}
|
|
1498
1888
|
if (lastAssistantMessage)
|
|
1499
|
-
|
|
1889
|
+
log5(`extracted assistant message from transcript (${lastAssistantMessage.length} chars)`);
|
|
1500
1890
|
}
|
|
1501
1891
|
} catch (e) {
|
|
1502
|
-
|
|
1892
|
+
log5(`transcript read failed: ${e.message}`);
|
|
1503
1893
|
}
|
|
1504
1894
|
}
|
|
1505
1895
|
const entry = {
|
|
@@ -1522,9 +1912,9 @@ async function main() {
|
|
|
1522
1912
|
const embeddingSql = embeddingSqlLiteral(embedding);
|
|
1523
1913
|
const insertSql = `INSERT INTO "${sessionsTable}" (id, path, filename, message, message_embedding, author, size_bytes, project, description, agent, plugin_version, creation_date, last_update_date) VALUES ('${crypto.randomUUID()}', '${sqlStr(sessionPath)}', '${sqlStr(filename)}', '${jsonForSql}'::jsonb, ${embeddingSql}, '${sqlStr(config.userName)}', ${Buffer.byteLength(line, "utf-8")}, '${sqlStr(projectName)}', 'Stop', 'codex', '${sqlStr(PLUGIN_VERSION)}', '${ts}', '${ts}')`;
|
|
1524
1914
|
await api.query(insertSql);
|
|
1525
|
-
|
|
1915
|
+
log5("stop event captured");
|
|
1526
1916
|
} catch (e) {
|
|
1527
|
-
|
|
1917
|
+
log5(`capture failed: ${e.message}`);
|
|
1528
1918
|
}
|
|
1529
1919
|
}
|
|
1530
1920
|
if (!CAPTURE)
|
|
@@ -1543,11 +1933,11 @@ async function main() {
|
|
|
1543
1933
|
reason: "Stop"
|
|
1544
1934
|
});
|
|
1545
1935
|
} catch (e) {
|
|
1546
|
-
|
|
1936
|
+
log5(`spawn failed: ${e.message}`);
|
|
1547
1937
|
try {
|
|
1548
1938
|
releaseLock(sessionId);
|
|
1549
1939
|
} catch (releaseErr) {
|
|
1550
|
-
|
|
1940
|
+
log5(`releaseLock after spawn failure also failed: ${releaseErr.message}`);
|
|
1551
1941
|
}
|
|
1552
1942
|
throw e;
|
|
1553
1943
|
}
|
|
@@ -1560,6 +1950,6 @@ async function main() {
|
|
|
1560
1950
|
});
|
|
1561
1951
|
}
|
|
1562
1952
|
main().catch((e) => {
|
|
1563
|
-
|
|
1953
|
+
log5(`fatal: ${e.message}`);
|
|
1564
1954
|
process.exit(0);
|
|
1565
1955
|
});
|