@deeplake/hivemind 0.7.44 → 0.7.46

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.
Files changed (35) hide show
  1. package/.claude-plugin/marketplace.json +3 -3
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/bundle/cli.js +3116 -208
  4. package/codex/bundle/capture.js +43 -0
  5. package/codex/bundle/commands/auth-login.js +43 -0
  6. package/codex/bundle/graph-pull-worker.js +1185 -0
  7. package/codex/bundle/pre-tool-use.js +43 -0
  8. package/codex/bundle/session-start-setup.js +43 -0
  9. package/codex/bundle/session-start.js +70 -4
  10. package/codex/bundle/shell/deeplake-shell.js +577 -25
  11. package/codex/bundle/skillify-worker.js +30 -3
  12. package/codex/bundle/stop.js +97 -50
  13. package/cursor/bundle/capture.js +97 -50
  14. package/cursor/bundle/commands/auth-login.js +43 -0
  15. package/cursor/bundle/graph-pull-worker.js +1185 -0
  16. package/cursor/bundle/pre-tool-use.js +43 -0
  17. package/cursor/bundle/session-end.js +49 -44
  18. package/cursor/bundle/session-start.js +66 -0
  19. package/cursor/bundle/shell/deeplake-shell.js +577 -25
  20. package/cursor/bundle/skillify-worker.js +30 -3
  21. package/hermes/bundle/capture.js +97 -50
  22. package/hermes/bundle/commands/auth-login.js +43 -0
  23. package/hermes/bundle/graph-pull-worker.js +1185 -0
  24. package/hermes/bundle/pre-tool-use.js +43 -0
  25. package/hermes/bundle/session-end.js +49 -44
  26. package/hermes/bundle/session-start.js +66 -0
  27. package/hermes/bundle/shell/deeplake-shell.js +577 -25
  28. package/hermes/bundle/skillify-worker.js +30 -3
  29. package/mcp/bundle/server.js +43 -0
  30. package/openclaw/dist/chunks/{config-XEK4MJJS.js → config-O5PDJQ7Y.js} +1 -0
  31. package/openclaw/dist/index.js +47 -2
  32. package/openclaw/dist/skillify-worker.js +30 -3
  33. package/openclaw/openclaw.plugin.json +1 -1
  34. package/openclaw/package.json +1 -1
  35. package/package.json +3 -1
@@ -0,0 +1,1185 @@
1
+ #!/usr/bin/env node
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __esm = (fn, res) => function __init() {
5
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
6
+ };
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+
12
+ // dist/src/index-marker-store.js
13
+ var index_marker_store_exports = {};
14
+ __export(index_marker_store_exports, {
15
+ buildIndexMarkerPath: () => buildIndexMarkerPath,
16
+ getIndexMarkerDir: () => getIndexMarkerDir,
17
+ hasFreshIndexMarker: () => hasFreshIndexMarker,
18
+ writeIndexMarker: () => writeIndexMarker
19
+ });
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
+ import { tmpdir } from "node:os";
23
+ function getIndexMarkerDir() {
24
+ return process.env.HIVEMIND_INDEX_MARKER_DIR ?? join5(tmpdir(), "hivemind-deeplake-indexes");
25
+ }
26
+ function buildIndexMarkerPath(workspaceId, orgId, table, suffix) {
27
+ const markerKey = [workspaceId, orgId, table, suffix].join("__").replace(/[^a-zA-Z0-9_.-]/g, "_");
28
+ return join5(getIndexMarkerDir(), `${markerKey}.json`);
29
+ }
30
+ function hasFreshIndexMarker(markerPath) {
31
+ if (!existsSync2(markerPath))
32
+ return false;
33
+ try {
34
+ const raw = JSON.parse(readFileSync4(markerPath, "utf-8"));
35
+ const updatedAt = raw.updatedAt ? new Date(raw.updatedAt).getTime() : NaN;
36
+ if (!Number.isFinite(updatedAt) || Date.now() - updatedAt > INDEX_MARKER_TTL_MS)
37
+ return false;
38
+ return true;
39
+ } catch {
40
+ return false;
41
+ }
42
+ }
43
+ function writeIndexMarker(markerPath) {
44
+ mkdirSync3(getIndexMarkerDir(), { recursive: true });
45
+ writeFileSync3(markerPath, JSON.stringify({ updatedAt: (/* @__PURE__ */ new Date()).toISOString() }), "utf-8");
46
+ }
47
+ var INDEX_MARKER_TTL_MS;
48
+ var init_index_marker_store = __esm({
49
+ "dist/src/index-marker-store.js"() {
50
+ "use strict";
51
+ INDEX_MARKER_TTL_MS = Number(process.env.HIVEMIND_INDEX_MARKER_TTL_MS ?? 6 * 60 * 6e4);
52
+ }
53
+ });
54
+
55
+ // dist/src/hooks/graph-pull-worker.js
56
+ import { appendFileSync as appendFileSync3, mkdirSync as mkdirSync8 } from "node:fs";
57
+ import { join as join10 } from "node:path";
58
+
59
+ // dist/src/graph/deeplake-pull.js
60
+ import { execFileSync } from "node:child_process";
61
+ import { createHash as createHash3 } from "node:crypto";
62
+ import { existsSync as existsSync5, mkdirSync as mkdirSync7, renameSync as renameSync4, writeFileSync as writeFileSync6 } from "node:fs";
63
+ import { dirname as dirname4, join as join9 } from "node:path";
64
+
65
+ // dist/src/config.js
66
+ import { readFileSync, existsSync } from "node:fs";
67
+ import { join } from "node:path";
68
+ import { homedir, userInfo } from "node:os";
69
+ function loadConfig() {
70
+ const home = homedir();
71
+ const credPath = join(home, ".deeplake", "credentials.json");
72
+ let creds = null;
73
+ if (existsSync(credPath)) {
74
+ try {
75
+ creds = JSON.parse(readFileSync(credPath, "utf-8"));
76
+ } catch {
77
+ return null;
78
+ }
79
+ }
80
+ const token = process.env.HIVEMIND_TOKEN ?? creds?.token;
81
+ const orgId = process.env.HIVEMIND_ORG_ID ?? creds?.orgId;
82
+ if (!token || !orgId)
83
+ return null;
84
+ return {
85
+ token,
86
+ orgId,
87
+ orgName: creds?.orgName ?? orgId,
88
+ userName: creds?.userName || userInfo().username || "unknown",
89
+ workspaceId: process.env.HIVEMIND_WORKSPACE_ID ?? creds?.workspaceId ?? "default",
90
+ apiUrl: process.env.HIVEMIND_API_URL ?? creds?.apiUrl ?? "https://api.deeplake.ai",
91
+ tableName: process.env.HIVEMIND_TABLE ?? "memory",
92
+ sessionsTableName: process.env.HIVEMIND_SESSIONS_TABLE ?? "sessions",
93
+ skillsTableName: process.env.HIVEMIND_SKILLS_TABLE ?? "skills",
94
+ codebaseTableName: process.env.HIVEMIND_CODEBASE_TABLE ?? "codebase",
95
+ memoryPath: process.env.HIVEMIND_MEMORY_PATH ?? join(home, ".deeplake", "memory")
96
+ };
97
+ }
98
+
99
+ // dist/src/deeplake-api.js
100
+ import { randomUUID } from "node:crypto";
101
+
102
+ // dist/src/utils/debug.js
103
+ import { appendFileSync } from "node:fs";
104
+ import { join as join2 } from "node:path";
105
+ import { homedir as homedir2 } from "node:os";
106
+ var LOG = join2(homedir2(), ".deeplake", "hook-debug.log");
107
+ function isDebug() {
108
+ return process.env.HIVEMIND_DEBUG === "1";
109
+ }
110
+ function log(tag, msg) {
111
+ if (!isDebug())
112
+ return;
113
+ appendFileSync(LOG, `${(/* @__PURE__ */ new Date()).toISOString()} [${tag}] ${msg}
114
+ `);
115
+ }
116
+
117
+ // dist/src/utils/sql.js
118
+ function sqlStr(value) {
119
+ return value.replace(/\\/g, "\\\\").replace(/'/g, "''").replace(/\0/g, "").replace(/[\x01-\x08\x0b\x0c\x0e-\x1f\x7f]/g, "");
120
+ }
121
+ function sqlIdent(name) {
122
+ if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name)) {
123
+ throw new Error(`Invalid SQL identifier: ${JSON.stringify(name)}`);
124
+ }
125
+ return name;
126
+ }
127
+
128
+ // dist/src/embeddings/columns.js
129
+ var SUMMARY_EMBEDDING_COL = "summary_embedding";
130
+
131
+ // dist/src/utils/client-header.js
132
+ var DEEPLAKE_CLIENT_HEADER = "X-Deeplake-Client";
133
+ function deeplakeClientValue() {
134
+ return "hivemind";
135
+ }
136
+ function deeplakeClientHeader() {
137
+ return { [DEEPLAKE_CLIENT_HEADER]: deeplakeClientValue() };
138
+ }
139
+
140
+ // dist/src/deeplake-schema.js
141
+ var MEMORY_COLUMNS = Object.freeze([
142
+ { name: "id", sql: "TEXT NOT NULL DEFAULT ''" },
143
+ { name: "path", sql: "TEXT NOT NULL DEFAULT ''" },
144
+ { name: "filename", sql: "TEXT NOT NULL DEFAULT ''" },
145
+ { name: "summary", sql: "TEXT NOT NULL DEFAULT ''" },
146
+ { name: "summary_embedding", sql: "FLOAT4[]" },
147
+ { name: "author", sql: "TEXT NOT NULL DEFAULT ''" },
148
+ { name: "mime_type", sql: "TEXT NOT NULL DEFAULT 'text/plain'" },
149
+ { name: "size_bytes", sql: "BIGINT NOT NULL DEFAULT 0" },
150
+ { name: "project", sql: "TEXT NOT NULL DEFAULT ''" },
151
+ { name: "description", sql: "TEXT NOT NULL DEFAULT ''" },
152
+ { name: "agent", sql: "TEXT NOT NULL DEFAULT ''" },
153
+ { name: "plugin_version", sql: "TEXT NOT NULL DEFAULT ''" },
154
+ { name: "creation_date", sql: "TEXT NOT NULL DEFAULT ''" },
155
+ { name: "last_update_date", sql: "TEXT NOT NULL DEFAULT ''" }
156
+ ]);
157
+ var SESSIONS_COLUMNS = Object.freeze([
158
+ { name: "id", sql: "TEXT NOT NULL DEFAULT ''" },
159
+ { name: "path", sql: "TEXT NOT NULL DEFAULT ''" },
160
+ { name: "filename", sql: "TEXT NOT NULL DEFAULT ''" },
161
+ { name: "message", sql: "JSONB" },
162
+ { name: "message_embedding", sql: "FLOAT4[]" },
163
+ { name: "author", sql: "TEXT NOT NULL DEFAULT ''" },
164
+ { name: "mime_type", sql: "TEXT NOT NULL DEFAULT 'application/json'" },
165
+ { name: "size_bytes", sql: "BIGINT NOT NULL DEFAULT 0" },
166
+ { name: "project", sql: "TEXT NOT NULL DEFAULT ''" },
167
+ { name: "description", sql: "TEXT NOT NULL DEFAULT ''" },
168
+ { name: "agent", sql: "TEXT NOT NULL DEFAULT ''" },
169
+ { name: "plugin_version", sql: "TEXT NOT NULL DEFAULT ''" },
170
+ { name: "creation_date", sql: "TEXT NOT NULL DEFAULT ''" },
171
+ { name: "last_update_date", sql: "TEXT NOT NULL DEFAULT ''" }
172
+ ]);
173
+ var SKILLS_COLUMNS = Object.freeze([
174
+ { name: "id", sql: "TEXT NOT NULL DEFAULT ''" },
175
+ { name: "name", sql: "TEXT NOT NULL DEFAULT ''" },
176
+ { name: "project", sql: "TEXT NOT NULL DEFAULT ''" },
177
+ { name: "project_key", sql: "TEXT NOT NULL DEFAULT ''" },
178
+ { name: "local_path", sql: "TEXT NOT NULL DEFAULT ''" },
179
+ { name: "install", sql: "TEXT NOT NULL DEFAULT 'project'" },
180
+ { name: "source_sessions", sql: "TEXT NOT NULL DEFAULT '[]'" },
181
+ { name: "source_agent", sql: "TEXT NOT NULL DEFAULT ''" },
182
+ { name: "scope", sql: "TEXT NOT NULL DEFAULT 'me'" },
183
+ { name: "author", sql: "TEXT NOT NULL DEFAULT ''" },
184
+ { name: "contributors", sql: "TEXT NOT NULL DEFAULT '[]'" },
185
+ { name: "description", sql: "TEXT NOT NULL DEFAULT ''" },
186
+ { name: "trigger_text", sql: "TEXT NOT NULL DEFAULT ''" },
187
+ { name: "body", sql: "TEXT NOT NULL DEFAULT ''" },
188
+ { name: "version", sql: "BIGINT NOT NULL DEFAULT 1" },
189
+ { name: "created_at", sql: "TEXT NOT NULL DEFAULT ''" },
190
+ { name: "updated_at", sql: "TEXT NOT NULL DEFAULT ''" }
191
+ ]);
192
+ function validateSchema(label, cols) {
193
+ const seen = /* @__PURE__ */ new Set();
194
+ for (const col of cols) {
195
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(col.name)) {
196
+ throw new Error(`${label}: column name "${col.name}" is not a valid SQL identifier`);
197
+ }
198
+ if (seen.has(col.name)) {
199
+ throw new Error(`${label}: duplicate column "${col.name}"`);
200
+ }
201
+ seen.add(col.name);
202
+ const notNull = /\bNOT\s+NULL\b/i.test(col.sql);
203
+ const hasDefault = /\bDEFAULT\b/i.test(col.sql);
204
+ if (notNull && !hasDefault) {
205
+ throw new Error(`${label}: column "${col.name}" is NOT NULL but has no DEFAULT \u2014 ALTER TABLE ADD COLUMN on a populated table would fail.`);
206
+ }
207
+ }
208
+ }
209
+ var CODEBASE_COLUMNS = Object.freeze([
210
+ // Identity key (matches the PK below)
211
+ { name: "org_id", sql: "TEXT NOT NULL DEFAULT ''" },
212
+ { name: "workspace_id", sql: "TEXT NOT NULL DEFAULT ''" },
213
+ { name: "repo_slug", sql: "TEXT NOT NULL DEFAULT ''" },
214
+ { name: "user_id", sql: "TEXT NOT NULL DEFAULT ''" },
215
+ { name: "worktree_id", sql: "TEXT NOT NULL DEFAULT ''" },
216
+ { name: "commit_sha", sql: "TEXT NOT NULL DEFAULT ''" },
217
+ // Observation metadata
218
+ { name: "parent_sha", sql: "TEXT NOT NULL DEFAULT ''" },
219
+ { name: "branch", sql: "TEXT NOT NULL DEFAULT ''" },
220
+ { name: "ts", sql: "TIMESTAMP" },
221
+ { name: "pushed_by", sql: "TEXT NOT NULL DEFAULT ''" },
222
+ // Snapshot payload
223
+ { name: "snapshot_sha256", sql: "TEXT NOT NULL DEFAULT ''" },
224
+ { name: "snapshot_jsonb", sql: "TEXT NOT NULL DEFAULT ''" },
225
+ { name: "node_count", sql: "BIGINT NOT NULL DEFAULT 0" },
226
+ { name: "edge_count", sql: "BIGINT NOT NULL DEFAULT 0" },
227
+ // Generator metadata (for drift diagnostics — what hivemind version produced this?)
228
+ { name: "generator", sql: "TEXT NOT NULL DEFAULT 'hivemind-graph'" },
229
+ { name: "generator_version", sql: "TEXT NOT NULL DEFAULT ''" },
230
+ { name: "schema_version", sql: "BIGINT NOT NULL DEFAULT 1" }
231
+ ]);
232
+ validateSchema("MEMORY_COLUMNS", MEMORY_COLUMNS);
233
+ validateSchema("SESSIONS_COLUMNS", SESSIONS_COLUMNS);
234
+ validateSchema("SKILLS_COLUMNS", SKILLS_COLUMNS);
235
+ validateSchema("CODEBASE_COLUMNS", CODEBASE_COLUMNS);
236
+ function buildCreateTableSql(tableName, cols) {
237
+ const safe = sqlIdent(tableName);
238
+ const colSql = cols.map((c) => `${c.name} ${c.sql}`).join(", ");
239
+ return `CREATE TABLE IF NOT EXISTS "${safe}" (${colSql}) USING deeplake`;
240
+ }
241
+ function buildIntrospectionSql(tableName, workspaceId) {
242
+ return `SELECT column_name FROM information_schema.columns WHERE table_name = '${sqlStr(tableName)}' AND table_schema = '${sqlStr(workspaceId)}'`;
243
+ }
244
+ async function healMissingColumns(args) {
245
+ const safeTable = sqlIdent(args.tableName);
246
+ const introspectSql = buildIntrospectionSql(args.tableName, args.workspaceId);
247
+ const rows = await args.query(introspectSql);
248
+ const existing = /* @__PURE__ */ new Set();
249
+ for (const row of rows) {
250
+ const v = row?.column_name;
251
+ if (typeof v === "string")
252
+ existing.add(v.toLowerCase());
253
+ }
254
+ const missingCols = args.columns.filter((c) => !existing.has(c.name.toLowerCase()));
255
+ const missing = missingCols.map((c) => c.name);
256
+ if (missingCols.length === 0)
257
+ return { missing, altered: [] };
258
+ const altered = [];
259
+ for (const col of missingCols) {
260
+ try {
261
+ await args.query(`ALTER TABLE "${safeTable}" ADD COLUMN ${col.name} ${col.sql}`);
262
+ altered.push(col.name);
263
+ args.log?.(`schema-heal: added "${args.tableName}"."${col.name}"`);
264
+ } catch (e) {
265
+ const msg = e instanceof Error ? e.message : String(e);
266
+ if (!/already exists/i.test(msg))
267
+ throw e;
268
+ const recheck = await args.query(introspectSql);
269
+ const present = recheck.some((r) => {
270
+ const v = r?.column_name;
271
+ return typeof v === "string" && v.toLowerCase() === col.name.toLowerCase();
272
+ });
273
+ if (!present)
274
+ throw e;
275
+ args.log?.(`schema-heal: "${args.tableName}"."${col.name}" appeared via race, treating as success`);
276
+ }
277
+ }
278
+ return { missing, altered };
279
+ }
280
+
281
+ // dist/src/notifications/queue.js
282
+ import { readFileSync as readFileSync2, writeFileSync, renameSync, mkdirSync, openSync, closeSync, unlinkSync, statSync } from "node:fs";
283
+ import { join as join3, resolve } from "node:path";
284
+ import { homedir as homedir3 } from "node:os";
285
+ import { setTimeout as sleep } from "node:timers/promises";
286
+ var log2 = (msg) => log("notifications-queue", msg);
287
+ var LOCK_RETRY_MAX = 50;
288
+ var LOCK_RETRY_BASE_MS = 5;
289
+ var LOCK_STALE_MS = 5e3;
290
+ function queuePath() {
291
+ return join3(homedir3(), ".deeplake", "notifications-queue.json");
292
+ }
293
+ function lockPath() {
294
+ return `${queuePath()}.lock`;
295
+ }
296
+ function readQueue() {
297
+ try {
298
+ const raw = readFileSync2(queuePath(), "utf-8");
299
+ const parsed = JSON.parse(raw);
300
+ if (!parsed || !Array.isArray(parsed.queue)) {
301
+ log2(`queue malformed \u2192 treating as empty`);
302
+ return { queue: [] };
303
+ }
304
+ return { queue: parsed.queue };
305
+ } catch {
306
+ return { queue: [] };
307
+ }
308
+ }
309
+ function _isQueuePathInsideHome(path, home) {
310
+ const r = resolve(path);
311
+ const h = resolve(home);
312
+ return r.startsWith(h + "/") || r === h;
313
+ }
314
+ function writeQueue(q) {
315
+ const path = queuePath();
316
+ const home = resolve(homedir3());
317
+ if (!_isQueuePathInsideHome(path, home)) {
318
+ throw new Error(`notifications-queue write blocked: ${path} is outside ${home}`);
319
+ }
320
+ mkdirSync(join3(home, ".deeplake"), { recursive: true, mode: 448 });
321
+ const tmp = `${path}.${process.pid}.tmp`;
322
+ writeFileSync(tmp, JSON.stringify(q, null, 2), { mode: 384 });
323
+ renameSync(tmp, path);
324
+ }
325
+ async function withQueueLock(fn) {
326
+ const path = lockPath();
327
+ mkdirSync(join3(homedir3(), ".deeplake"), { recursive: true, mode: 448 });
328
+ let fd = null;
329
+ for (let attempt = 0; attempt < LOCK_RETRY_MAX; attempt++) {
330
+ try {
331
+ fd = openSync(path, "wx", 384);
332
+ break;
333
+ } catch (e) {
334
+ const code = e.code;
335
+ if (code !== "EEXIST")
336
+ throw e;
337
+ try {
338
+ const age = Date.now() - statSync(path).mtimeMs;
339
+ if (age > LOCK_STALE_MS) {
340
+ unlinkSync(path);
341
+ continue;
342
+ }
343
+ } catch {
344
+ }
345
+ const delay = LOCK_RETRY_BASE_MS * (attempt + 1);
346
+ await sleep(delay);
347
+ }
348
+ }
349
+ if (fd === null) {
350
+ log2(`lock acquisition gave up after ${LOCK_RETRY_MAX} attempts \u2014 proceeding unlocked (last-writer-wins)`);
351
+ return fn();
352
+ }
353
+ try {
354
+ return fn();
355
+ } finally {
356
+ try {
357
+ closeSync(fd);
358
+ } catch {
359
+ }
360
+ try {
361
+ unlinkSync(path);
362
+ } catch {
363
+ }
364
+ }
365
+ }
366
+ function sameDedupKey(a, b) {
367
+ if (a.id !== b.id)
368
+ return false;
369
+ return JSON.stringify(a.dedupKey) === JSON.stringify(b.dedupKey);
370
+ }
371
+ async function enqueueNotification(n) {
372
+ await withQueueLock(() => {
373
+ const q = readQueue();
374
+ if (q.queue.some((existing) => sameDedupKey(existing, n))) {
375
+ return;
376
+ }
377
+ q.queue.push(n);
378
+ writeQueue(q);
379
+ });
380
+ }
381
+
382
+ // dist/src/commands/auth-creds.js
383
+ import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, unlinkSync as unlinkSync2 } from "node:fs";
384
+ import { join as join4 } from "node:path";
385
+ import { homedir as homedir4 } from "node:os";
386
+ function configDir() {
387
+ return join4(homedir4(), ".deeplake");
388
+ }
389
+ function credsPath() {
390
+ return join4(configDir(), "credentials.json");
391
+ }
392
+ function loadCredentials() {
393
+ try {
394
+ return JSON.parse(readFileSync3(credsPath(), "utf-8"));
395
+ } catch {
396
+ return null;
397
+ }
398
+ }
399
+
400
+ // dist/src/deeplake-api.js
401
+ var indexMarkerStorePromise = null;
402
+ function getIndexMarkerStore() {
403
+ if (!indexMarkerStorePromise)
404
+ indexMarkerStorePromise = Promise.resolve().then(() => (init_index_marker_store(), index_marker_store_exports));
405
+ return indexMarkerStorePromise;
406
+ }
407
+ var log3 = (msg) => log("sdk", msg);
408
+ function summarizeSql(sql, maxLen = 220) {
409
+ const compact = sql.replace(/\s+/g, " ").trim();
410
+ return compact.length > maxLen ? `${compact.slice(0, maxLen)}...` : compact;
411
+ }
412
+ function traceSql(msg) {
413
+ const traceEnabled = process.env.HIVEMIND_TRACE_SQL === "1" || process.env.HIVEMIND_DEBUG === "1";
414
+ if (!traceEnabled)
415
+ return;
416
+ process.stderr.write(`[deeplake-sql] ${msg}
417
+ `);
418
+ if (process.env.HIVEMIND_DEBUG === "1")
419
+ log3(msg);
420
+ }
421
+ var _signalledBalanceExhausted = false;
422
+ function maybeSignalBalanceExhausted(status, bodyText) {
423
+ if (status !== 402)
424
+ return;
425
+ if (!bodyText.includes("balance_cents"))
426
+ return;
427
+ if (_signalledBalanceExhausted)
428
+ return;
429
+ _signalledBalanceExhausted = true;
430
+ log3(`balance exhausted \u2014 enqueuing session-start banner (body=${bodyText.slice(0, 120)})`);
431
+ enqueueNotification({
432
+ id: "balance-exhausted",
433
+ severity: "warn",
434
+ transient: true,
435
+ title: "Hivemind credits exhausted \u2014 top up to keep capturing",
436
+ body: `Sessions are not being saved and memory recall is returning empty. Top up at ${billingUrl()} to restore capture and recall.`,
437
+ dedupKey: { reason: "balance-zero" }
438
+ }).catch((e) => {
439
+ log3(`enqueue balance-exhausted failed: ${e instanceof Error ? e.message : String(e)}`);
440
+ });
441
+ }
442
+ function billingUrl() {
443
+ try {
444
+ const c = loadCredentials();
445
+ if (c?.orgName && c?.workspaceId) {
446
+ return `https://deeplake.ai/${encodeURIComponent(c.orgName)}/workspace/${encodeURIComponent(c.workspaceId)}/billing`;
447
+ }
448
+ } catch {
449
+ }
450
+ return "https://deeplake.ai";
451
+ }
452
+ var RETRYABLE_CODES = /* @__PURE__ */ new Set([429, 500, 502, 503, 504]);
453
+ var MAX_RETRIES = 3;
454
+ var BASE_DELAY_MS = 500;
455
+ var MAX_CONCURRENCY = 5;
456
+ function getQueryTimeoutMs() {
457
+ return Number(process.env.HIVEMIND_QUERY_TIMEOUT_MS ?? 1e4);
458
+ }
459
+ function sleep2(ms) {
460
+ return new Promise((resolve3) => setTimeout(resolve3, ms));
461
+ }
462
+ function isTimeoutError(error) {
463
+ const name = error instanceof Error ? error.name.toLowerCase() : "";
464
+ const message = error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase();
465
+ return name.includes("timeout") || name === "aborterror" || message.includes("timeout") || message.includes("timed out");
466
+ }
467
+ function isDuplicateIndexError(error) {
468
+ const message = error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase();
469
+ return message.includes("duplicate key value violates unique constraint") || message.includes("pg_class_relname_nsp_index") || message.includes("already exists");
470
+ }
471
+ function isSessionInsertQuery(sql) {
472
+ return /^\s*insert\s+into\s+"[^"]+"\s*\(\s*id\s*,\s*path\s*,\s*filename\s*,\s*message\s*,/i.test(sql);
473
+ }
474
+ function isTransientHtml403(text) {
475
+ const body = text.toLowerCase();
476
+ return body.includes("<html") || body.includes("403 forbidden") || body.includes("cloudflare") || body.includes("nginx");
477
+ }
478
+ var Semaphore = class {
479
+ max;
480
+ waiting = [];
481
+ active = 0;
482
+ constructor(max) {
483
+ this.max = max;
484
+ }
485
+ async acquire() {
486
+ if (this.active < this.max) {
487
+ this.active++;
488
+ return;
489
+ }
490
+ await new Promise((resolve3) => this.waiting.push(resolve3));
491
+ }
492
+ release() {
493
+ this.active--;
494
+ const next = this.waiting.shift();
495
+ if (next) {
496
+ this.active++;
497
+ next();
498
+ }
499
+ }
500
+ };
501
+ var DeeplakeApi = class {
502
+ token;
503
+ apiUrl;
504
+ orgId;
505
+ workspaceId;
506
+ tableName;
507
+ _pendingRows = [];
508
+ _sem = new Semaphore(MAX_CONCURRENCY);
509
+ _tablesCache = null;
510
+ constructor(token, apiUrl, orgId, workspaceId, tableName) {
511
+ this.token = token;
512
+ this.apiUrl = apiUrl;
513
+ this.orgId = orgId;
514
+ this.workspaceId = workspaceId;
515
+ this.tableName = tableName;
516
+ }
517
+ /** Execute SQL with retry on transient errors and bounded concurrency. */
518
+ async query(sql) {
519
+ const startedAt = Date.now();
520
+ const summary = summarizeSql(sql);
521
+ traceSql(`query start: ${summary}`);
522
+ await this._sem.acquire();
523
+ try {
524
+ const rows = await this._queryWithRetry(sql);
525
+ traceSql(`query ok (${Date.now() - startedAt}ms, rows=${rows.length}): ${summary}`);
526
+ return rows;
527
+ } catch (e) {
528
+ const message = e instanceof Error ? e.message : String(e);
529
+ traceSql(`query fail (${Date.now() - startedAt}ms): ${summary} :: ${message}`);
530
+ throw e;
531
+ } finally {
532
+ this._sem.release();
533
+ }
534
+ }
535
+ async _queryWithRetry(sql) {
536
+ let lastError;
537
+ for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
538
+ let resp;
539
+ const timeoutMs = getQueryTimeoutMs();
540
+ try {
541
+ const signal = AbortSignal.timeout(timeoutMs);
542
+ resp = await fetch(`${this.apiUrl}/workspaces/${this.workspaceId}/tables/query`, {
543
+ method: "POST",
544
+ headers: {
545
+ Authorization: `Bearer ${this.token}`,
546
+ "Content-Type": "application/json",
547
+ "X-Activeloop-Org-Id": this.orgId,
548
+ ...deeplakeClientHeader()
549
+ },
550
+ signal,
551
+ body: JSON.stringify({ query: sql })
552
+ });
553
+ } catch (e) {
554
+ if (isTimeoutError(e)) {
555
+ lastError = new Error(`Query timeout after ${timeoutMs}ms`);
556
+ throw lastError;
557
+ }
558
+ lastError = e instanceof Error ? e : new Error(String(e));
559
+ if (attempt < MAX_RETRIES) {
560
+ const delay = BASE_DELAY_MS * Math.pow(2, attempt) + Math.random() * 200;
561
+ log3(`query retry ${attempt + 1}/${MAX_RETRIES} (fetch error: ${lastError.message}) in ${delay.toFixed(0)}ms`);
562
+ await sleep2(delay);
563
+ continue;
564
+ }
565
+ throw lastError;
566
+ }
567
+ if (resp.ok) {
568
+ const raw = await resp.json();
569
+ if (!raw?.rows || !raw?.columns)
570
+ return [];
571
+ return raw.rows.map((row) => Object.fromEntries(raw.columns.map((col, i) => [col, row[i]])));
572
+ }
573
+ const text = await resp.text().catch(() => "");
574
+ const retryable403 = isSessionInsertQuery(sql) && (resp.status === 401 || resp.status === 403 && (text.length === 0 || isTransientHtml403(text)));
575
+ const alreadyExists = resp.status === 500 && isDuplicateIndexError(text);
576
+ if (!alreadyExists && attempt < MAX_RETRIES && (RETRYABLE_CODES.has(resp.status) || retryable403)) {
577
+ const delay = BASE_DELAY_MS * Math.pow(2, attempt) + Math.random() * 200;
578
+ log3(`query retry ${attempt + 1}/${MAX_RETRIES} (${resp.status}) in ${delay.toFixed(0)}ms`);
579
+ await sleep2(delay);
580
+ continue;
581
+ }
582
+ maybeSignalBalanceExhausted(resp.status, text);
583
+ throw new Error(`Query failed: ${resp.status}: ${text.slice(0, 200)}`);
584
+ }
585
+ throw lastError ?? new Error("Query failed: max retries exceeded");
586
+ }
587
+ // ── Writes ──────────────────────────────────────────────────────────────────
588
+ /** Queue rows for writing. Call commit() to flush. */
589
+ appendRows(rows) {
590
+ this._pendingRows.push(...rows);
591
+ }
592
+ /** Flush pending rows via SQL. */
593
+ async commit() {
594
+ if (this._pendingRows.length === 0)
595
+ return;
596
+ const rows = this._pendingRows;
597
+ this._pendingRows = [];
598
+ const CONCURRENCY = 10;
599
+ for (let i = 0; i < rows.length; i += CONCURRENCY) {
600
+ const chunk = rows.slice(i, i + CONCURRENCY);
601
+ await Promise.allSettled(chunk.map((r) => this.upsertRowSql(r)));
602
+ }
603
+ log3(`commit: ${rows.length} rows`);
604
+ }
605
+ async upsertRowSql(row) {
606
+ const ts = (/* @__PURE__ */ new Date()).toISOString();
607
+ const cd = row.creationDate ?? ts;
608
+ const lud = row.lastUpdateDate ?? ts;
609
+ const exists = await this.query(`SELECT path FROM "${this.tableName}" WHERE path = '${sqlStr(row.path)}' LIMIT 1`);
610
+ if (exists.length > 0) {
611
+ let setClauses = `summary = E'${sqlStr(row.contentText)}', ${SUMMARY_EMBEDDING_COL} = NULL, mime_type = '${sqlStr(row.mimeType)}', size_bytes = ${row.sizeBytes}, last_update_date = '${lud}'`;
612
+ if (row.project !== void 0)
613
+ setClauses += `, project = '${sqlStr(row.project)}'`;
614
+ if (row.description !== void 0)
615
+ setClauses += `, description = '${sqlStr(row.description)}'`;
616
+ await this.query(`UPDATE "${this.tableName}" SET ${setClauses} WHERE path = '${sqlStr(row.path)}'`);
617
+ } else {
618
+ const id = randomUUID();
619
+ let cols = `id, path, filename, summary, ${SUMMARY_EMBEDDING_COL}, mime_type, size_bytes, creation_date, last_update_date`;
620
+ let vals = `'${id}', '${sqlStr(row.path)}', '${sqlStr(row.filename)}', E'${sqlStr(row.contentText)}', NULL, '${sqlStr(row.mimeType)}', ${row.sizeBytes}, '${cd}', '${lud}'`;
621
+ if (row.project !== void 0) {
622
+ cols += ", project";
623
+ vals += `, '${sqlStr(row.project)}'`;
624
+ }
625
+ if (row.description !== void 0) {
626
+ cols += ", description";
627
+ vals += `, '${sqlStr(row.description)}'`;
628
+ }
629
+ await this.query(`INSERT INTO "${this.tableName}" (${cols}) VALUES (${vals})`);
630
+ }
631
+ }
632
+ /** Update specific columns on a row by path. */
633
+ async updateColumns(path, columns) {
634
+ const setClauses = Object.entries(columns).map(([col, val]) => typeof val === "number" ? `${col} = ${val}` : `${col} = '${sqlStr(String(val))}'`).join(", ");
635
+ await this.query(`UPDATE "${this.tableName}" SET ${setClauses} WHERE path = '${sqlStr(path)}'`);
636
+ }
637
+ // ── Convenience ─────────────────────────────────────────────────────────────
638
+ /** Create a BM25 search index on a column. */
639
+ async createIndex(column) {
640
+ await this.query(`CREATE INDEX IF NOT EXISTS idx_${sqlStr(column)}_bm25 ON "${this.tableName}" USING deeplake_index ("${column}")`);
641
+ }
642
+ buildLookupIndexName(table, suffix) {
643
+ return `idx_${table}_${suffix}`.replace(/[^a-zA-Z0-9_]/g, "_");
644
+ }
645
+ async ensureLookupIndex(table, suffix, columnsSql) {
646
+ const markers = await getIndexMarkerStore();
647
+ const markerPath = markers.buildIndexMarkerPath(this.workspaceId, this.orgId, table, suffix);
648
+ if (markers.hasFreshIndexMarker(markerPath))
649
+ return;
650
+ const indexName = this.buildLookupIndexName(table, suffix);
651
+ try {
652
+ await this.query(`CREATE INDEX IF NOT EXISTS "${indexName}" ON "${table}" ${columnsSql}`);
653
+ markers.writeIndexMarker(markerPath);
654
+ } catch (e) {
655
+ if (isDuplicateIndexError(e)) {
656
+ markers.writeIndexMarker(markerPath);
657
+ return;
658
+ }
659
+ log3(`index "${indexName}" skipped: ${e.message}`);
660
+ }
661
+ }
662
+ /**
663
+ * Heal any missing columns on a table so it matches one of the schema
664
+ * definitions in `deeplake-schema.ts`. One SELECT against
665
+ * `information_schema.columns` per call, then `ALTER TABLE ADD COLUMN`
666
+ * only the genuinely missing ones — never blanket, never `IF NOT
667
+ * EXISTS`.
668
+ *
669
+ * History: an earlier path used a local marker file (`col_<name>` under
670
+ * the index-marker dir) to skip even the SELECT after the first
671
+ * confirmation, plus per-column ALTERs for `summary_embedding`,
672
+ * `message_embedding`, `agent`, `plugin_version`. The marker existed
673
+ * because Deeplake used to expose a ~30s post-ALTER bug where
674
+ * subsequent INSERTs failed, so we wanted to keep ALTER traffic to a
675
+ * minimum. The bug was re-verified on 2026-05-18 against
676
+ * `api.deeplake.ai` (`test_plugin` org) and no longer reproduces
677
+ * (71/71 INSERTs OK, first success 2ms after ALTER). The single SELECT
678
+ * + targeted ALTER pattern survives the marker removal because: each
679
+ * ALTER still costs ~800ms (so blanket sweeps are wasteful) and the
680
+ * diff produces clearer logs than "ALTER all with IF NOT EXISTS".
681
+ */
682
+ async healSchema(table, columns) {
683
+ await healMissingColumns({
684
+ query: (sql) => this.query(sql),
685
+ tableName: table,
686
+ workspaceId: this.workspaceId,
687
+ columns,
688
+ log: log3
689
+ });
690
+ }
691
+ /** List all tables in the workspace (with retry). */
692
+ async listTables(forceRefresh = false) {
693
+ if (!forceRefresh && this._tablesCache)
694
+ return [...this._tablesCache];
695
+ const { tables, cacheable } = await this._fetchTables();
696
+ if (cacheable)
697
+ this._tablesCache = [...tables];
698
+ return tables;
699
+ }
700
+ async _fetchTables() {
701
+ for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
702
+ try {
703
+ const resp = await fetch(`${this.apiUrl}/workspaces/${this.workspaceId}/tables`, {
704
+ headers: {
705
+ Authorization: `Bearer ${this.token}`,
706
+ "X-Activeloop-Org-Id": this.orgId,
707
+ ...deeplakeClientHeader()
708
+ }
709
+ });
710
+ if (resp.ok) {
711
+ const data = await resp.json();
712
+ return {
713
+ tables: (data.tables ?? []).map((t) => t.table_name),
714
+ cacheable: true
715
+ };
716
+ }
717
+ if (attempt < MAX_RETRIES && RETRYABLE_CODES.has(resp.status)) {
718
+ await sleep2(BASE_DELAY_MS * Math.pow(2, attempt) + Math.random() * 200);
719
+ continue;
720
+ }
721
+ return { tables: [], cacheable: false };
722
+ } catch {
723
+ if (attempt < MAX_RETRIES) {
724
+ await sleep2(BASE_DELAY_MS * Math.pow(2, attempt));
725
+ continue;
726
+ }
727
+ return { tables: [], cacheable: false };
728
+ }
729
+ }
730
+ return { tables: [], cacheable: false };
731
+ }
732
+ /**
733
+ * Run a `CREATE TABLE` with an extra outer retry budget. The base
734
+ * `query()` already retries 3 times on fetch errors (~3.5s total), but a
735
+ * failed CREATE is permanent corruption — every subsequent SELECT against
736
+ * the missing table fails. Wrapping in an outer loop with longer backoff
737
+ * (2s, 5s, then 10s) gives us ~17s of reach across transient network
738
+ * blips before giving up. Failures still propagate; getApi() resets its
739
+ * cache on init failure (openclaw plugin) so the next call retries the
740
+ * whole init flow.
741
+ */
742
+ async createTableWithRetry(sql, label) {
743
+ const OUTER_BACKOFFS_MS = [2e3, 5e3, 1e4];
744
+ let lastErr = null;
745
+ for (let attempt = 0; attempt <= OUTER_BACKOFFS_MS.length; attempt++) {
746
+ try {
747
+ await this.query(sql);
748
+ return;
749
+ } catch (err) {
750
+ lastErr = err;
751
+ const msg = err instanceof Error ? err.message : String(err);
752
+ log3(`CREATE TABLE "${label}" attempt ${attempt + 1}/${OUTER_BACKOFFS_MS.length + 1} failed: ${msg}`);
753
+ if (attempt < OUTER_BACKOFFS_MS.length) {
754
+ await sleep2(OUTER_BACKOFFS_MS[attempt]);
755
+ }
756
+ }
757
+ }
758
+ throw lastErr;
759
+ }
760
+ /** Create the memory table if it doesn't already exist. Heal missing columns on existing tables. */
761
+ async ensureTable(name) {
762
+ if (!MEMORY_COLUMNS.some((c) => c.name === SUMMARY_EMBEDDING_COL)) {
763
+ throw new Error(`MEMORY_COLUMNS missing "${SUMMARY_EMBEDDING_COL}" (embeddings/columns.ts drift)`);
764
+ }
765
+ const tbl = sqlIdent(name ?? this.tableName);
766
+ const tables = await this.listTables();
767
+ if (!tables.includes(tbl)) {
768
+ log3(`table "${tbl}" not found, creating`);
769
+ await this.createTableWithRetry(buildCreateTableSql(tbl, MEMORY_COLUMNS), tbl);
770
+ log3(`table "${tbl}" created`);
771
+ if (!tables.includes(tbl))
772
+ this._tablesCache = [...tables, tbl];
773
+ }
774
+ await this.healSchema(tbl, MEMORY_COLUMNS);
775
+ }
776
+ /** Create the sessions table (uses JSONB for message since every row is a JSON event). */
777
+ async ensureSessionsTable(name) {
778
+ const safe = sqlIdent(name);
779
+ const tables = await this.listTables();
780
+ if (!tables.includes(safe)) {
781
+ log3(`table "${safe}" not found, creating`);
782
+ await this.createTableWithRetry(buildCreateTableSql(safe, SESSIONS_COLUMNS), safe);
783
+ log3(`table "${safe}" created`);
784
+ if (!tables.includes(safe))
785
+ this._tablesCache = [...tables, safe];
786
+ }
787
+ await this.healSchema(safe, SESSIONS_COLUMNS);
788
+ await this.ensureLookupIndex(safe, "path_creation_date", `("path", "creation_date")`);
789
+ }
790
+ /**
791
+ * Create the skills table.
792
+ *
793
+ * One row per skill version. Workers INSERT a fresh row on every KEEP /
794
+ * MERGE rather than UPDATE-ing in place, so the full version history is
795
+ * recoverable. Uniqueness in the *current* state is by (project_key, name)
796
+ * — newer rows shadow older ones at read time (ORDER BY version DESC).
797
+ * This sidesteps the Deeplake UPDATE-coalescing quirk that bit the wiki
798
+ * worker.
799
+ */
800
+ /**
801
+ * Create the codebase table. One row per (org, workspace, repo, user,
802
+ * worktree, commit) — see CODEBASE_COLUMNS for the schema. Healing
803
+ * + index follow the same pattern as ensureSessionsTable.
804
+ */
805
+ async ensureCodebaseTable(name) {
806
+ const safe = sqlIdent(name);
807
+ const tables = await this.listTables();
808
+ if (!tables.includes(safe)) {
809
+ log3(`table "${safe}" not found, creating`);
810
+ await this.createTableWithRetry(buildCreateTableSql(safe, CODEBASE_COLUMNS), safe);
811
+ log3(`table "${safe}" created`);
812
+ if (!tables.includes(safe))
813
+ this._tablesCache = [...tables, safe];
814
+ }
815
+ await this.healSchema(safe, CODEBASE_COLUMNS);
816
+ await this.ensureLookupIndex(safe, "codebase_identity", `("org_id", "workspace_id", "repo_slug", "user_id", "worktree_id", "commit_sha")`);
817
+ }
818
+ async ensureSkillsTable(name) {
819
+ const safe = sqlIdent(name);
820
+ const tables = await this.listTables();
821
+ if (!tables.includes(safe)) {
822
+ log3(`table "${safe}" not found, creating`);
823
+ await this.createTableWithRetry(buildCreateTableSql(safe, SKILLS_COLUMNS), safe);
824
+ log3(`table "${safe}" created`);
825
+ if (!tables.includes(safe))
826
+ this._tablesCache = [...tables, safe];
827
+ }
828
+ await this.healSchema(safe, SKILLS_COLUMNS);
829
+ await this.ensureLookupIndex(safe, "project_key_name", `("project_key", "name")`);
830
+ }
831
+ };
832
+
833
+ // dist/src/utils/repo-identity.js
834
+ import { execSync } from "node:child_process";
835
+ import { createHash } from "node:crypto";
836
+ import { basename, resolve as resolve2 } from "node:path";
837
+ var DEFAULT_PORTS = {
838
+ http: "80",
839
+ https: "443",
840
+ ssh: "22",
841
+ git: "9418"
842
+ };
843
+ function normalizeGitRemoteUrl(url) {
844
+ let s = url.trim();
845
+ const schemeMatch = s.match(/^([a-z][a-z0-9+.-]*):\/\//i);
846
+ const scheme = schemeMatch ? schemeMatch[1].toLowerCase() : null;
847
+ if (schemeMatch)
848
+ s = s.slice(schemeMatch[0].length);
849
+ if (!scheme) {
850
+ const scp = s.match(/^(?:[^@/\s]+@)?([^:/\s]+):(.+)$/);
851
+ if (scp)
852
+ s = `${scp[1]}/${scp[2]}`;
853
+ }
854
+ s = s.replace(/^[^@/]+@/, "");
855
+ if (scheme && DEFAULT_PORTS[scheme]) {
856
+ s = s.replace(new RegExp(`^([^/]+):${DEFAULT_PORTS[scheme]}(/|$)`), "$1$2");
857
+ }
858
+ s = s.replace(/\.git\/?$/i, "");
859
+ s = s.replace(/\/+$/, "");
860
+ return s.toLowerCase();
861
+ }
862
+ function deriveProjectKey(cwd) {
863
+ const absCwd = resolve2(cwd);
864
+ const project = basename(absCwd) || "unknown";
865
+ let signature = null;
866
+ try {
867
+ const raw = execSync("git config --get remote.origin.url", {
868
+ cwd: absCwd,
869
+ encoding: "utf-8",
870
+ stdio: ["ignore", "pipe", "ignore"]
871
+ }).trim();
872
+ signature = raw ? normalizeGitRemoteUrl(raw) : null;
873
+ } catch {
874
+ }
875
+ const input = signature ?? absCwd;
876
+ const key = createHash("sha1").update(input).digest("hex").slice(0, 16);
877
+ return { key, project };
878
+ }
879
+
880
+ // dist/src/graph/last-build.js
881
+ import { existsSync as existsSync3, mkdirSync as mkdirSync4, readFileSync as readFileSync5, renameSync as renameSync2, writeFileSync as writeFileSync4 } from "node:fs";
882
+ import { dirname, join as join6 } from "node:path";
883
+ function lastBuildPath(baseDir, worktreeId) {
884
+ if (worktreeId !== void 0) {
885
+ return join6(baseDir, "worktrees", worktreeId, ".last-build.json");
886
+ }
887
+ return join6(baseDir, ".last-build.json");
888
+ }
889
+ function writeLastBuild(baseDir, state, worktreeId) {
890
+ const path = lastBuildPath(baseDir, worktreeId);
891
+ try {
892
+ mkdirSync4(dirname(path), { recursive: true });
893
+ const tmp = `${path}.tmp.${process.pid}.${Date.now()}`;
894
+ writeFileSync4(tmp, JSON.stringify(state));
895
+ renameSync2(tmp, path);
896
+ } catch {
897
+ }
898
+ }
899
+ function readLastBuild(baseDir, worktreeId) {
900
+ let path = lastBuildPath(baseDir, worktreeId);
901
+ if (!existsSync3(path)) {
902
+ if (worktreeId === void 0)
903
+ return null;
904
+ const legacy = lastBuildPath(baseDir, void 0);
905
+ if (!existsSync3(legacy))
906
+ return null;
907
+ path = legacy;
908
+ }
909
+ let raw;
910
+ try {
911
+ raw = readFileSync5(path, "utf8");
912
+ } catch {
913
+ return null;
914
+ }
915
+ let parsed;
916
+ try {
917
+ parsed = JSON.parse(raw);
918
+ } catch {
919
+ return null;
920
+ }
921
+ if (parsed === null || typeof parsed !== "object")
922
+ return null;
923
+ const o = parsed;
924
+ if (typeof o.ts !== "number" || !Number.isFinite(o.ts))
925
+ return null;
926
+ if (o.commit_sha !== null && typeof o.commit_sha !== "string")
927
+ return null;
928
+ if (typeof o.snapshot_sha256 !== "string")
929
+ return null;
930
+ const out = { ts: o.ts, commit_sha: o.commit_sha, snapshot_sha256: o.snapshot_sha256 };
931
+ if (typeof o.node_count === "number" && Number.isFinite(o.node_count) && o.node_count >= 0) {
932
+ out.node_count = o.node_count;
933
+ }
934
+ if (typeof o.edge_count === "number" && Number.isFinite(o.edge_count) && o.edge_count >= 0) {
935
+ out.edge_count = o.edge_count;
936
+ }
937
+ return out;
938
+ }
939
+
940
+ // dist/src/graph/history.js
941
+ import { appendFileSync as appendFileSync2, existsSync as existsSync4, mkdirSync as mkdirSync5, readFileSync as readFileSync6 } from "node:fs";
942
+ import { dirname as dirname2, join as join7 } from "node:path";
943
+ function historyPath(baseDir) {
944
+ return join7(baseDir, "history.jsonl");
945
+ }
946
+ function appendHistoryEntry(baseDir, entry) {
947
+ const path = historyPath(baseDir);
948
+ try {
949
+ mkdirSync5(dirname2(path), { recursive: true });
950
+ appendFileSync2(path, JSON.stringify(entry) + "\n");
951
+ } catch {
952
+ }
953
+ }
954
+
955
+ // dist/src/graph/snapshot.js
956
+ import { createHash as createHash2 } from "node:crypto";
957
+ import { mkdirSync as mkdirSync6, renameSync as renameSync3, writeFileSync as writeFileSync5 } from "node:fs";
958
+ import { homedir as homedir5 } from "node:os";
959
+ import { dirname as dirname3, join as join8 } from "node:path";
960
+ function graphsRoot() {
961
+ return process.env.HIVEMIND_GRAPHS_HOME ?? join8(homedir5(), ".hivemind", "graphs");
962
+ }
963
+ function repoDir(repoKey) {
964
+ return join8(graphsRoot(), repoKey);
965
+ }
966
+
967
+ // dist/src/graph/deeplake-pull.js
968
+ function workTreeIdFor(cwd) {
969
+ return createHash3("sha256").update(cwd).digest("hex").slice(0, 16);
970
+ }
971
+ async function pullSnapshot(cwd, deps = {}) {
972
+ if (process.env.HIVEMIND_GRAPH_PULL === "0") {
973
+ return { kind: "skipped-disabled" };
974
+ }
975
+ const config = (deps.loadConfig ?? loadConfig)();
976
+ if (config === null) {
977
+ return { kind: "skipped-no-auth" };
978
+ }
979
+ const head = (deps.readHead ?? defaultReadHead)(cwd);
980
+ if (head === null) {
981
+ return { kind: "skipped-no-head" };
982
+ }
983
+ const api = (deps.makeApi ?? defaultMakeApi)(config);
984
+ try {
985
+ await api.ensureCodebaseTable(config.codebaseTableName);
986
+ } catch (err) {
987
+ return errorOutcome("ensureCodebaseTable", err);
988
+ }
989
+ const tableId = sqlIdent(config.codebaseTableName);
990
+ const { key: repoKey } = deriveProjectKey(cwd);
991
+ const selectSql = `SELECT snapshot_jsonb, snapshot_sha256, ts, node_count, edge_count, branch, generator_version, worktree_id FROM "${tableId}" WHERE org_id = '${sqlStr(config.orgId)}' AND workspace_id = '${sqlStr(config.workspaceId)}' AND repo_slug = '${sqlStr(repoKey)}' AND user_id = '${sqlStr(config.userName)}' AND commit_sha = '${sqlStr(head)}' ORDER BY ts DESC LIMIT 1`;
992
+ let rows;
993
+ try {
994
+ rows = await api.query(selectSql);
995
+ } catch (err) {
996
+ return errorOutcome("SELECT cloud row", err);
997
+ }
998
+ if (rows.length === 0) {
999
+ return { kind: "no-cloud-row", commitSha: head };
1000
+ }
1001
+ const row = rows[0];
1002
+ const cloudSha256 = String(row.snapshot_sha256 ?? "").trim();
1003
+ const cloudPayload = coerceSnapshotPayload(row.snapshot_jsonb);
1004
+ if (cloudPayload === null) {
1005
+ return errorOutcome("SELECT cloud row", new Error("invalid snapshot_jsonb payload"));
1006
+ }
1007
+ if (cloudSha256 !== "") {
1008
+ const computedSha = createHash3("sha256").update(cloudPayload).digest("hex");
1009
+ if (cloudSha256 !== computedSha) {
1010
+ return errorOutcome("SELECT cloud row", new Error(`snapshot_sha256 mismatch (expected ${cloudSha256}, got ${computedSha})`));
1011
+ }
1012
+ }
1013
+ const cloudTs = parseTs(row.ts);
1014
+ const baseDir = repoDir(repoKey);
1015
+ const worktreeId = workTreeIdFor(cwd);
1016
+ const local = readLastBuild(baseDir, worktreeId);
1017
+ if (local !== null && local.commit_sha === head) {
1018
+ if (cloudSha256 !== "" && local.snapshot_sha256 === cloudSha256) {
1019
+ return { kind: "up-to-date", commitSha: head, snapshotSha256: cloudSha256 };
1020
+ }
1021
+ if (local.ts > cloudTs) {
1022
+ return {
1023
+ kind: "local-newer",
1024
+ commitSha: head,
1025
+ localTs: local.ts,
1026
+ cloudTs
1027
+ };
1028
+ }
1029
+ }
1030
+ const snapshotsDir = join9(baseDir, "snapshots");
1031
+ const snapshotPath = join9(snapshotsDir, `${head}.json`);
1032
+ const worktreeRoot = join9(baseDir, "worktrees", worktreeId);
1033
+ try {
1034
+ writeFileAtomic(snapshotPath, cloudPayload);
1035
+ writeFileAtomic(join9(worktreeRoot, "latest-commit.txt"), `${head}
1036
+ `);
1037
+ writeLastBuild(baseDir, {
1038
+ ts: cloudTs,
1039
+ commit_sha: head,
1040
+ snapshot_sha256: cloudSha256,
1041
+ node_count: numOrUndefined(row.node_count),
1042
+ edge_count: numOrUndefined(row.edge_count)
1043
+ }, worktreeId);
1044
+ appendHistoryEntry(baseDir, {
1045
+ ts: new Date(cloudTs).toISOString(),
1046
+ commit_sha: head,
1047
+ snapshot_sha256: cloudSha256,
1048
+ node_count: Number(row.node_count ?? 0),
1049
+ edge_count: Number(row.edge_count ?? 0),
1050
+ trigger: "pull"
1051
+ });
1052
+ } catch (err) {
1053
+ return errorOutcome("write local files", err);
1054
+ }
1055
+ return {
1056
+ kind: "pulled",
1057
+ commitSha: head,
1058
+ snapshotSha256: cloudSha256,
1059
+ bytes: Buffer.byteLength(cloudPayload, "utf8"),
1060
+ cloudTs,
1061
+ sourceWorktreePath: String(row.worktree_id ?? "")
1062
+ };
1063
+ }
1064
+ function defaultReadHead(cwd) {
1065
+ try {
1066
+ return execFileSync("git", ["rev-parse", "HEAD"], {
1067
+ cwd,
1068
+ encoding: "utf8",
1069
+ stdio: ["ignore", "pipe", "ignore"]
1070
+ }).trim();
1071
+ } catch {
1072
+ return null;
1073
+ }
1074
+ }
1075
+ function defaultMakeApi(config) {
1076
+ return new DeeplakeApi(config.token, config.apiUrl, config.orgId, config.workspaceId, config.tableName);
1077
+ }
1078
+ function parseTs(raw) {
1079
+ if (typeof raw === "number" && Number.isFinite(raw)) {
1080
+ return raw < 1e12 ? raw * 1e3 : raw;
1081
+ }
1082
+ if (typeof raw === "string") {
1083
+ const parsed = Date.parse(raw);
1084
+ return Number.isFinite(parsed) ? parsed : 0;
1085
+ }
1086
+ return 0;
1087
+ }
1088
+ function numOrUndefined(raw) {
1089
+ if (typeof raw === "number" && Number.isFinite(raw) && raw >= 0)
1090
+ return raw;
1091
+ if (typeof raw === "string") {
1092
+ const n = Number(raw);
1093
+ if (Number.isFinite(n) && n >= 0)
1094
+ return n;
1095
+ }
1096
+ return void 0;
1097
+ }
1098
+ function coerceSnapshotPayload(raw) {
1099
+ if (typeof raw === "string")
1100
+ return raw;
1101
+ if (raw !== null && typeof raw === "object")
1102
+ return JSON.stringify(raw);
1103
+ return null;
1104
+ }
1105
+ function writeFileAtomic(filePath, contents) {
1106
+ mkdirSync7(dirname4(filePath), { recursive: true });
1107
+ const tmp = `${filePath}.tmp.${process.pid}.${Date.now()}`;
1108
+ writeFileSync6(tmp, contents);
1109
+ renameSync4(tmp, filePath);
1110
+ }
1111
+ function errorOutcome(stage, err) {
1112
+ const message = err instanceof Error ? err.message : String(err);
1113
+ return { kind: "error", message: `${stage}: ${message}` };
1114
+ }
1115
+
1116
+ // dist/src/hooks/graph-pull-worker.js
1117
+ function getArg(name) {
1118
+ const idx = process.argv.indexOf(name);
1119
+ if (idx === -1 || idx + 1 >= process.argv.length)
1120
+ return null;
1121
+ return process.argv[idx + 1];
1122
+ }
1123
+ function workerTimeoutMs() {
1124
+ const raw = process.env.HIVEMIND_GRAPH_PULL_TIMEOUT_MS;
1125
+ if (raw === void 0)
1126
+ return 3e4;
1127
+ const n = parseInt(raw, 10);
1128
+ return Number.isFinite(n) && n > 0 ? n : 3e4;
1129
+ }
1130
+ async function main() {
1131
+ const cwd = getArg("--cwd") ?? process.cwd();
1132
+ const t0 = Date.now();
1133
+ const timeoutPromise = new Promise((resolve3) => {
1134
+ setTimeout(() => resolve3("timeout"), workerTimeoutMs()).unref();
1135
+ });
1136
+ let logLine;
1137
+ try {
1138
+ const outcome = await Promise.race([pullSnapshot(cwd), timeoutPromise]);
1139
+ if (outcome === "timeout") {
1140
+ const dur2 = Date.now() - t0;
1141
+ logLine = `[${(/* @__PURE__ */ new Date()).toISOString()}] timeout (${dur2}ms) \u2014 pullSnapshot did not return within ${workerTimeoutMs()}ms; forcing exit
1142
+ `;
1143
+ try {
1144
+ const { key } = deriveProjectKey(cwd);
1145
+ const dir = repoDir(key);
1146
+ mkdirSync8(dir, { recursive: true });
1147
+ appendFileSync3(join10(dir, ".graph-pull.log"), logLine);
1148
+ } catch {
1149
+ }
1150
+ process.exit(0);
1151
+ }
1152
+ const dur = Date.now() - t0;
1153
+ const extras = [];
1154
+ if (outcome.kind === "pulled") {
1155
+ extras.push(`commit=${outcome.commitSha.slice(0, 7)}`);
1156
+ extras.push(`bytes=${outcome.bytes}`);
1157
+ extras.push(`sha256=${outcome.snapshotSha256.slice(0, 12)}`);
1158
+ } else if (outcome.kind === "up-to-date") {
1159
+ extras.push(`commit=${outcome.commitSha.slice(0, 7)}`);
1160
+ } else if (outcome.kind === "local-newer") {
1161
+ extras.push(`commit=${outcome.commitSha.slice(0, 7)}`);
1162
+ extras.push(`localTs=${outcome.localTs}`);
1163
+ extras.push(`cloudTs=${outcome.cloudTs}`);
1164
+ } else if (outcome.kind === "no-cloud-row") {
1165
+ extras.push(`commit=${outcome.commitSha.slice(0, 7)}`);
1166
+ } else if (outcome.kind === "error") {
1167
+ extras.push(outcome.message);
1168
+ }
1169
+ logLine = `[${(/* @__PURE__ */ new Date()).toISOString()}] ${outcome.kind} (${dur}ms)` + (extras.length ? ` \u2014 ${extras.join(" ")}` : "") + "\n";
1170
+ } catch (err) {
1171
+ const dur = Date.now() - t0;
1172
+ logLine = `[${(/* @__PURE__ */ new Date()).toISOString()}] threw ${err instanceof Error ? err.message : String(err)} (${dur}ms)
1173
+ `;
1174
+ }
1175
+ try {
1176
+ const { key } = deriveProjectKey(cwd);
1177
+ const dir = repoDir(key);
1178
+ mkdirSync8(dir, { recursive: true });
1179
+ appendFileSync3(join10(dir, ".graph-pull.log"), logLine);
1180
+ } catch {
1181
+ }
1182
+ }
1183
+ main().catch(() => {
1184
+ process.exit(0);
1185
+ });