@dikolab/kbdb 0.4.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/README.md +13 -5
  2. package/dist/{chunk-BJPSPNGX.mjs → chunk-BJXEVALU.mjs} +115 -1
  3. package/dist/chunk-BJXEVALU.mjs.map +7 -0
  4. package/dist/cli.cjs +462 -79
  5. package/dist/cli.cjs.map +4 -4
  6. package/dist/cli.mjs +356 -79
  7. package/dist/cli.mjs.map +4 -4
  8. package/dist/kbdb-worker.cjs +413 -53
  9. package/dist/kbdb-worker.cjs.map +4 -4
  10. package/dist/mod.cjs +230 -54
  11. package/dist/mod.cjs.map +4 -4
  12. package/dist/mod.mjs +229 -53
  13. package/dist/mod.mjs.map +4 -4
  14. package/dist/src/shared/cli/typings/cli-options.interface.d.ts +5 -0
  15. package/dist/src/shared/embedding/classes/tfidf-embedding-provider.class.d.ts +28 -0
  16. package/dist/src/shared/embedding/constants/tfidf-defaults.constant.d.ts +4 -0
  17. package/dist/src/shared/embedding/functions/create-embedding-provider.function.d.ts +5 -2
  18. package/dist/src/shared/embedding/index.d.ts +2 -0
  19. package/dist/src/shared/log/constants/log-colors.constant.d.ts +5 -0
  20. package/dist/src/shared/log/constants/log-defaults.constant.d.ts +3 -0
  21. package/dist/src/shared/log/constants/log-icons.constant.d.ts +3 -0
  22. package/dist/src/shared/log/functions/configure-log.function.d.ts +12 -0
  23. package/dist/src/shared/log/functions/log.function.d.ts +28 -0
  24. package/dist/src/shared/log/functions/parse-log-level.function.d.ts +10 -0
  25. package/dist/src/shared/log/functions/resolve-log-level.function.d.ts +12 -0
  26. package/dist/src/shared/log/functions/write-stderr.function.d.ts +10 -0
  27. package/dist/src/shared/log/index.d.ts +10 -0
  28. package/dist/src/shared/log/typings/log-config.interface.d.ts +7 -0
  29. package/dist/src/shared/log/typings/log-level.type.d.ts +7 -0
  30. package/dist/src/shared/log/typings/log-output.interface.d.ts +4 -0
  31. package/dist/src/shared/version/constants/version.constant.d.ts +1 -1
  32. package/dist/src/shared/worker-client/classes/worker-client.class.d.ts +7 -0
  33. package/dist/src/shared/worker-client/functions/retry-on-timeout.function.d.ts +1 -0
  34. package/dist/src/shared/worker-daemon/classes/worker-daemon.class.d.ts +1 -0
  35. package/dist/src/shared/worker-daemon/functions/run-worker.function.d.ts +6 -1
  36. package/dist/src/shared/worker-daemon/typings/wasm-modules.interface.d.ts +5 -0
  37. package/dist/wasm/default-embedding/kbdb_default_embedding.d.ts +55 -0
  38. package/dist/wasm/default-embedding/kbdb_default_embedding.js +156 -0
  39. package/dist/wasm/default-embedding/kbdb_default_embedding_bg.wasm +0 -0
  40. package/dist/wasm/default-embedding/kbdb_default_embedding_bg.wasm.d.ts +12 -0
  41. package/dist/wasm/default-embedding/package.json +13 -0
  42. package/dist/wasm/fs-database/kbdb_fs_database_bg.wasm +0 -0
  43. package/dist/wasm/fs-migration/kbdb_fs_migration.d.ts +38 -0
  44. package/dist/wasm/fs-migration/kbdb_fs_migration.js +130 -0
  45. package/dist/wasm/fs-migration/kbdb_fs_migration_bg.wasm +0 -0
  46. package/dist/wasm/fs-migration/kbdb_fs_migration_bg.wasm.d.ts +9 -0
  47. package/dist/wasm/fs-migration/package.json +13 -0
  48. package/dist/wasm/kb-worker/kbdb_worker_bg.wasm +0 -0
  49. package/dist/wasm/query-parser/kbdb_query_parser_bg.wasm +0 -0
  50. package/dist/worker.mjs +306 -54
  51. package/dist/worker.mjs.map +4 -4
  52. package/docs/details/agent-tooling.md +10 -0
  53. package/docs/details/cli.md +29 -13
  54. package/docs/details/knowledge-base.md +15 -8
  55. package/docs/details/mcp-server.md +16 -1
  56. package/docs/details/search-and-ranking.md +6 -2
  57. package/docs/goals/cli.md +33 -4
  58. package/docs/goals/content-parser.md +1 -1
  59. package/docs/goals/hybrid-search.md +71 -42
  60. package/docs/goals/mcp.md +3 -1
  61. package/docs/goals/overview.md +1 -1
  62. package/docs/goals/query-parser.md +1 -1
  63. package/docs/goals/semantic-dedup.md +7 -3
  64. package/docs/goals/worker-daemon.md +31 -20
  65. package/docs/modules/overview.md +43 -22
  66. package/docs/modules/rust/embedding/module.md +91 -0
  67. package/docs/modules/rust/overview.md +56 -21
  68. package/docs/modules/rust/query-parser/functions/module.md +7 -5
  69. package/docs/modules/rust/query-parser/functions/pipeline.md +6 -138
  70. package/docs/modules/rust/query-parser/module.md +16 -22
  71. package/docs/modules/rust/query-parser/types/module.md +5 -4
  72. package/docs/modules/rust/query-parser/types/token.md +5 -1
  73. package/docs/modules/rust/shared/content-parser/functions.md +1 -1
  74. package/docs/modules/rust/shared/memory/module.md +1 -1
  75. package/docs/modules/rust/shared/module.md +33 -5
  76. package/docs/modules/rust/shared/pipeline/functions.md +141 -0
  77. package/docs/modules/rust/shared/pipeline/module.md +62 -0
  78. package/docs/modules/rust/shared/pipeline/types.md +43 -0
  79. package/docs/modules/rust/shared/section/module.md +1 -1
  80. package/docs/modules/rust/shared/section/types.md +1 -1
  81. package/docs/modules/typescript/cli.md +4 -2
  82. package/docs/modules/typescript/kbdb-worker.md +20 -5
  83. package/docs/modules/typescript/overview.md +28 -6
  84. package/docs/modules/typescript/shared/cli/functions.md +15 -4
  85. package/docs/modules/typescript/shared/cli/typings.md +21 -0
  86. package/docs/modules/typescript/shared/embedding/functions.md +30 -3
  87. package/docs/modules/typescript/shared/embedding/module.md +10 -4
  88. package/docs/modules/typescript/shared/embedding/typings.md +7 -5
  89. package/docs/modules/typescript/shared/log/constants.md +82 -0
  90. package/docs/modules/typescript/shared/log/functions.md +131 -0
  91. package/docs/modules/typescript/shared/log/module.md +153 -0
  92. package/docs/modules/typescript/shared/log/typings.md +82 -0
  93. package/docs/modules/typescript/shared/module.md +22 -6
  94. package/docs/modules/typescript/shared/worker-daemon/functions.md +19 -6
  95. package/docs/modules/typescript/shared/worker-daemon/module.md +5 -2
  96. package/docs/modules/typescript/shared/worker-daemon/typings.md +13 -7
  97. package/docs/release-notes/0.4.1.md +149 -0
  98. package/package.json +2 -2
  99. package/dist/chunk-BJPSPNGX.mjs.map +0 -7
package/dist/worker.mjs CHANGED
@@ -6,14 +6,18 @@ import {
6
6
  DEFAULT_TIMEOUT_MS,
7
7
  WORKER_TOML_FILENAME,
8
8
  computeSha256,
9
+ configureLog,
9
10
  getArgs,
10
11
  getIpcPath,
11
12
  isRemoteUrl,
12
13
  joinPath,
14
+ log,
15
+ parseLogLevel,
13
16
  parseWorkerToml,
14
17
  resolveBaseDir,
18
+ resolveLogLevel,
15
19
  resolveWasmDir
16
- } from "./chunk-BJPSPNGX.mjs";
20
+ } from "./chunk-BJXEVALU.mjs";
17
21
 
18
22
  // src/worker.ts
19
23
  import { existsSync as existsSync5 } from "node:fs";
@@ -91,6 +95,7 @@ var nullAllocator = { alloc: null };
91
95
  function createHostImports(contextPath, memoryRef = nullMemory, fs = nodeFsAdapter, allocator = nullAllocator) {
92
96
  function getBuffer() {
93
97
  if (!memoryRef.memory) {
98
+ log("WASM memory not initialized", 3 /* ERROR */);
94
99
  throw new Error("WASM memory not initialized");
95
100
  }
96
101
  return memoryRef.memory.buffer;
@@ -259,6 +264,7 @@ async function loadWasmModules(config, wasmDir2) {
259
264
  void 0,
260
265
  allocator
261
266
  );
267
+ log("loading WASM module: kb-worker", 0 /* DEBUG */);
262
268
  const wkPath = joinPath(wasmDir2, "kb-worker", "kbdb_worker_bg.wasm");
263
269
  const wkBytes = await readWasmFile(wkPath, "kb-worker");
264
270
  let wkModule;
@@ -282,7 +288,20 @@ async function loadWasmModules(config, wasmDir2) {
282
288
  if (typeof fn_ !== "function") return 0;
283
289
  return fn_(size);
284
290
  };
285
- return { memory, kbWorker };
291
+ const embPath = joinPath(
292
+ wasmDir2,
293
+ "default-embedding",
294
+ "kbdb_default_embedding_bg.wasm"
295
+ );
296
+ let embedding;
297
+ try {
298
+ const embBytes = await readWasmFile(embPath, "default-embedding");
299
+ const embModule = await WebAssembly.compile(embBytes);
300
+ embedding = await WebAssembly.instantiate(embModule, {});
301
+ } catch {
302
+ }
303
+ log("all WASM modules loaded", 1 /* INFO */);
304
+ return { memory, kbWorker, embedding };
286
305
  }
287
306
  async function readWasmFile(path, label) {
288
307
  try {
@@ -308,7 +327,7 @@ function rethrow(stage, label, err, path) {
308
327
  const detail = err instanceof Error ? err.message : String(err);
309
328
  const suffix = path ? ` (path: ${path})` : "";
310
329
  const message = `failed to ${stage} ${label} WASM: ${detail}${suffix}`;
311
- console.error(`kbdb-worker: ${message}`);
330
+ log(`kbdb-worker: ${message}`, 3 /* ERROR */);
312
331
  throw new Error(message, { cause: err });
313
332
  }
314
333
 
@@ -319,9 +338,14 @@ var KEEPALIVE_MS = 3e4;
319
338
  function createIpcServer(socketPath, handler) {
320
339
  const server = createServer((socket) => {
321
340
  socket.setKeepAlive(true, KEEPALIVE_MS);
341
+ log("ipc: connection accepted", 0 /* DEBUG */);
322
342
  let buffer = "";
323
343
  let processingQueue = Promise.resolve();
324
344
  socket.on("data", (chunk) => {
345
+ log(
346
+ "ipc: data received bytes=" + String(chunk.length),
347
+ 0 /* DEBUG */
348
+ );
325
349
  buffer += chunk.toString();
326
350
  const lines = buffer.split("\n");
327
351
  buffer = lines.pop() ?? "";
@@ -333,8 +357,9 @@ function createIpcServer(socketPath, handler) {
333
357
  }
334
358
  });
335
359
  socket.on("error", (err) => {
336
- console.error(
337
- `kbdb-worker: ipc connection error: ${err.message}`
360
+ log(
361
+ `kbdb-worker: ipc connection error: ${err.message}`,
362
+ 2 /* WARNING */
338
363
  );
339
364
  });
340
365
  });
@@ -345,8 +370,9 @@ function createIpcServer(socketPath, handler) {
345
370
  () => {
346
371
  server.removeListener("error", reject);
347
372
  server.on("error", (err) => {
348
- console.error(
349
- `kbdb-worker: ipc server error: ${err.message}`
373
+ log(
374
+ `kbdb-worker: ipc server error: ${err.message}`,
375
+ 3 /* ERROR */
350
376
  );
351
377
  });
352
378
  resolve2(server);
@@ -361,8 +387,13 @@ async function handleLine(line, handler, socket) {
361
387
  let response;
362
388
  try {
363
389
  const request = JSON.parse(line);
390
+ log(
391
+ "ipc: request parsed method=" + request.method + " id=" + String(request.id),
392
+ 0 /* DEBUG */
393
+ );
364
394
  response = await handler(request);
365
395
  } catch {
396
+ log("ipc: parse error on malformed request", 0 /* DEBUG */);
366
397
  response = {
367
398
  jsonrpc: "2.0",
368
399
  id: 0,
@@ -372,7 +403,12 @@ async function handleLine(line, handler, socket) {
372
403
  }
373
404
  };
374
405
  }
375
- socket.write(JSON.stringify(response, bigintReplacer) + "\n");
406
+ const encoded = JSON.stringify(response, bigintReplacer) + "\n";
407
+ log(
408
+ "ipc: response sent bytes=" + String(encoded.length),
409
+ 0 /* DEBUG */
410
+ );
411
+ socket.write(encoded);
376
412
  }
377
413
 
378
414
  // src/shared/embedding/classes/null-embedding-provider.class.ts
@@ -416,6 +452,7 @@ var OnnxEmbeddingProvider = class {
416
452
  * dependency not yet wired
417
453
  */
418
454
  embed(_texts) {
455
+ log("embed called on OnnxEmbeddingProvider", 0 /* DEBUG */);
419
456
  return Promise.reject(
420
457
  new Error(
421
458
  "ONNX Runtime not available; install onnxruntime-node"
@@ -435,6 +472,7 @@ var OnnxEmbeddingProvider = class {
435
472
  // src/shared/embedding/functions/parse-api-embedding-response.function.ts
436
473
  function parseApiEmbeddingResponse(raw) {
437
474
  if (!isEmbeddingApiResponse(raw)) {
475
+ log("Unexpected embedding API response shape", 3 /* ERROR */);
438
476
  throw new Error("Unexpected embedding API response shape");
439
477
  }
440
478
  return raw.data.map((entry) => new Float32Array(entry.embedding));
@@ -488,9 +526,9 @@ var ApiEmbeddingProvider = class {
488
526
  body: JSON.stringify({ input: texts, model: this.model })
489
527
  });
490
528
  if (!response.ok) {
491
- throw new Error(
492
- `Embedding API error: ${String(response.status)}`
493
- );
529
+ const msg = `Embedding API error: ${String(response.status)}`;
530
+ log(msg, 3 /* ERROR */);
531
+ throw new Error(msg);
494
532
  }
495
533
  const json = await response.json();
496
534
  return parseApiEmbeddingResponse(json);
@@ -505,17 +543,89 @@ var ApiEmbeddingProvider = class {
505
543
  }
506
544
  };
507
545
 
546
+ // src/shared/embedding/constants/tfidf-defaults.constant.ts
547
+ var TFIDF_DIMENSIONS = 256;
548
+ var TFIDF_MODEL_NAME = "tfidf-v1";
549
+
550
+ // src/shared/embedding/classes/tfidf-embedding-provider.class.ts
551
+ var TfidfEmbeddingProvider = class {
552
+ _alloc;
553
+ _batch;
554
+ _retPtr;
555
+ _memory;
556
+ /** @param wasm - instantiated `kbdb-default-embedding` WASM module */
557
+ constructor(wasm) {
558
+ const ex = wasm.exports;
559
+ this._memory = ex["memory"];
560
+ this._alloc = ex["kbdb_alloc"];
561
+ this._batch = ex["embedding_tfidf_batch"];
562
+ this._retPtr = ex["get_return_ptr"];
563
+ }
564
+ /**
565
+ * Embeds a batch of texts using TF-IDF computation in WASM.
566
+ *
567
+ * @param texts - input strings to embed
568
+ * @returns one 256-dimension `Float32Array` per input text
569
+ */
570
+ embed(texts) {
571
+ log(
572
+ "embed called on TfidfEmbeddingProvider: " + texts.length.toString() + " texts",
573
+ 0 /* DEBUG */
574
+ );
575
+ const input = texts.join("\n");
576
+ const encoded = new TextEncoder().encode(input);
577
+ const ptr = this._alloc(encoded.byteLength);
578
+ const view = new Uint8Array(this._memory.buffer);
579
+ view.set(encoded, ptr);
580
+ this._batch(ptr, encoded.byteLength);
581
+ const outPtr = this._retPtr();
582
+ const dv = new DataView(this._memory.buffer);
583
+ const count = dv.getUint32(outPtr, true);
584
+ const results = [];
585
+ let offset = outPtr + 4;
586
+ for (let i = 0; i < count; i++) {
587
+ const vec = new Float32Array(TFIDF_DIMENSIONS);
588
+ for (let d = 0; d < TFIDF_DIMENSIONS; d++) {
589
+ vec[d] = dv.getFloat32(offset, true);
590
+ offset += 4;
591
+ }
592
+ results.push(vec);
593
+ }
594
+ return Promise.resolve(results);
595
+ }
596
+ /** Returns 256 -- the fixed dimension count for TF-IDF vectors. */
597
+ dimensions() {
598
+ return TFIDF_DIMENSIONS;
599
+ }
600
+ /** Returns `'tfidf-v1'`. */
601
+ modelName() {
602
+ return TFIDF_MODEL_NAME;
603
+ }
604
+ };
605
+
508
606
  // src/shared/embedding/functions/create-embedding-provider.function.ts
509
- function createEmbeddingProvider(config) {
510
- if (!config || config.provider === "none") {
607
+ function createEmbeddingProvider(config, wasmInstance) {
608
+ log("creating embedding provider", 0 /* DEBUG */);
609
+ if (!config) {
610
+ if (wasmInstance) {
611
+ log("using TfidfEmbeddingProvider", 0 /* DEBUG */);
612
+ return new TfidfEmbeddingProvider(wasmInstance);
613
+ }
614
+ log("using NullEmbeddingProvider", 0 /* DEBUG */);
615
+ return new NullEmbeddingProvider();
616
+ }
617
+ if (config.provider === "none") {
618
+ log("using NullEmbeddingProvider", 0 /* DEBUG */);
511
619
  return new NullEmbeddingProvider();
512
620
  }
513
621
  if (config.provider === "onnx") {
622
+ log("using OnnxEmbeddingProvider", 0 /* DEBUG */);
514
623
  return new OnnxEmbeddingProvider(
515
624
  config.model ?? "",
516
625
  config.dimensions ?? 0
517
626
  );
518
627
  }
628
+ log("using ApiEmbeddingProvider", 0 /* DEBUG */);
519
629
  return new ApiEmbeddingProvider(
520
630
  config.endpoint ?? "",
521
631
  config.dimensions ?? 0,
@@ -552,6 +662,7 @@ function cosineSimilarity(a, b) {
552
662
  var HEADER_SIZE = 32;
553
663
  var KBID_WIDTH = 64;
554
664
  function searchVectors(filePath, queryVector, topN) {
665
+ log("searching vectors", 0 /* DEBUG */);
555
666
  const buf = readFileSync4(filePath);
556
667
  if (buf.length < HEADER_SIZE) {
557
668
  return [];
@@ -607,6 +718,7 @@ var IpcError = class extends Error {
607
718
 
608
719
  // src/shared/worker-daemon/functions/handle-hybrid-search.function.ts
609
720
  async function fuseSearchResults(lexical, algo, query, provider, targetDir) {
721
+ log("handling hybrid search", 0 /* DEBUG */);
610
722
  if (algo === "lexical") {
611
723
  return {
612
724
  ...lexical,
@@ -633,10 +745,9 @@ async function fuseSearchResults(lexical, algo, query, provider, targetDir) {
633
745
  }
634
746
  function assertProvider(provider) {
635
747
  if (!provider || provider.dimensions() === 0) {
636
- throw new IpcError(
637
- -32e3,
638
- "embedding provider not configured; set [embedding] in worker.toml"
639
- );
748
+ const embeddingErrMsg = "embedding provider not configured; set [embedding] in worker.toml";
749
+ log(embeddingErrMsg, 3 /* ERROR */);
750
+ throw new IpcError(-32e3, embeddingErrMsg);
640
751
  }
641
752
  }
642
753
  function buildVectorOnly(vectorResults, lexical) {
@@ -739,6 +850,7 @@ function writeRecord(buf, offset, entry, dimensions, encoder3) {
739
850
  }
740
851
  }
741
852
  function buildVectorIndex(dirPath, modelName, dimensions, entries) {
853
+ log("building vector index", 0 /* DEBUG */);
742
854
  const encoder3 = new TextEncoder();
743
855
  const modelBytes = encoder3.encode(modelName);
744
856
  const modelLen = modelBytes.length;
@@ -797,6 +909,7 @@ function collectSections(sectionsDir) {
797
909
  return result;
798
910
  }
799
911
  async function rebuildVectors(targetDir, provider) {
912
+ log("rebuilding vectors", 0 /* DEBUG */);
800
913
  if (provider.dimensions() === 0) {
801
914
  return 0;
802
915
  }
@@ -820,6 +933,10 @@ async function rebuildVectors(targetDir, provider) {
820
933
  provider.dimensions(),
821
934
  entries
822
935
  );
936
+ log(
937
+ "rebuilt " + sections.length.toString() + " vectors",
938
+ 1 /* INFO */
939
+ );
823
940
  return sections.length;
824
941
  }
825
942
 
@@ -1677,6 +1794,7 @@ var WorkerDaemon = class {
1677
1794
  _idleAt = 0;
1678
1795
  _idleTimer = null;
1679
1796
  _server = null;
1797
+ _sockets = /* @__PURE__ */ new Set();
1680
1798
  _wasmDir;
1681
1799
  /** Queue of waiters for the single-writer lock. */
1682
1800
  _writeQueue = [];
@@ -1713,6 +1831,10 @@ var WorkerDaemon = class {
1713
1831
  */
1714
1832
  checkPreconditions() {
1715
1833
  if (!existsSync4(this.targetDir)) {
1834
+ log(
1835
+ `database directory not found: ${this.targetDir}`,
1836
+ 3 /* ERROR */
1837
+ );
1716
1838
  throw new Error(
1717
1839
  `database directory not found: ${this.targetDir}`
1718
1840
  );
@@ -1723,9 +1845,9 @@ var WorkerDaemon = class {
1723
1845
  constants.R_OK | constants.W_OK
1724
1846
  );
1725
1847
  } catch {
1726
- throw new Error(
1727
- `cannot access database directory: ${this.targetDir} -- permission denied`
1728
- );
1848
+ const msg = `cannot access database directory: ${this.targetDir} -- permission denied`;
1849
+ log(msg, 3 /* ERROR */);
1850
+ throw new Error(msg);
1729
1851
  }
1730
1852
  this.checkCatalogVersion();
1731
1853
  }
@@ -1741,18 +1863,34 @@ var WorkerDaemon = class {
1741
1863
  * be created
1742
1864
  */
1743
1865
  async start() {
1866
+ log(
1867
+ "start: loading wasm modules ctx=" + this.ctx,
1868
+ 0 /* DEBUG */
1869
+ );
1744
1870
  const wasmModules = await loadWasmModules(
1745
1871
  this.config,
1746
1872
  this._wasmDir
1747
1873
  );
1748
1874
  this._wasmModules = wasmModules;
1875
+ log("start: wasm modules loaded", 0 /* DEBUG */);
1749
1876
  this._embeddingProvider = createEmbeddingProvider(
1750
- this.config.embedding
1877
+ this.config.embedding,
1878
+ wasmModules.embedding
1879
+ );
1880
+ log(
1881
+ "start: embedding provider created embedding=" + (this.config.embedding?.provider ?? "none"),
1882
+ 0 /* DEBUG */
1751
1883
  );
1752
1884
  this.initWorkerContext();
1885
+ log("start: worker context initialized", 0 /* DEBUG */);
1753
1886
  this.runStartupIntegrityCheck();
1887
+ log(
1888
+ "start: integrity check done ok=" + String(this._integrityOk),
1889
+ 0 /* DEBUG */
1890
+ );
1754
1891
  const ipcPath = getIpcPath(this.ctx);
1755
1892
  if (existsSync4(ipcPath)) {
1893
+ log("start: removing stale socket " + ipcPath, 0 /* DEBUG */);
1756
1894
  try {
1757
1895
  unlinkSync2(ipcPath);
1758
1896
  } catch {
@@ -1760,18 +1898,34 @@ var WorkerDaemon = class {
1760
1898
  }
1761
1899
  const handler = (req) => this.handleRequest(req);
1762
1900
  this._server = await createIpcServer(ipcPath, handler);
1763
- this._server.on("connection", () => {
1901
+ log("IPC server listening on " + ipcPath, 1 /* INFO */);
1902
+ this._server.on("connection", (socket) => {
1903
+ this._sockets.add(socket);
1764
1904
  this.incrementConnections();
1765
1905
  this.resetIdleTimer();
1766
- });
1767
- this._server.on("close", () => {
1768
- this.decrementConnections();
1769
- if (this._connectionCount === 0) {
1770
- this.resetIdleTimer();
1771
- }
1906
+ log(
1907
+ "client connected (count: " + String(this._connectionCount) + ")",
1908
+ 0 /* DEBUG */
1909
+ );
1910
+ socket.on("close", () => {
1911
+ this._sockets.delete(socket);
1912
+ this.decrementConnections();
1913
+ log(
1914
+ "client disconnected (count: " + String(this._connectionCount) + ")",
1915
+ 0 /* DEBUG */
1916
+ );
1917
+ if (this._connectionCount === 0) {
1918
+ this.resetIdleTimer();
1919
+ }
1920
+ });
1772
1921
  });
1773
1922
  const pidPath = join6(tmpdir(), `kbdb-${this.ctx}.pid`);
1774
1923
  writeFileSync3(pidPath, process.pid.toString());
1924
+ log(
1925
+ "start: pid file written pid=" + String(process.pid),
1926
+ 0 /* DEBUG */
1927
+ );
1928
+ log("start: daemon ready ctx=" + this.ctx, 0 /* DEBUG */);
1775
1929
  this.resetIdleTimer();
1776
1930
  }
1777
1931
  /**
@@ -1782,23 +1936,36 @@ var WorkerDaemon = class {
1782
1936
  * the process.
1783
1937
  */
1784
1938
  stop() {
1939
+ log(
1940
+ "stop: entry sockets=" + String(this._sockets.size),
1941
+ 0 /* DEBUG */
1942
+ );
1943
+ log("shutting down", 1 /* INFO */);
1785
1944
  if (this._idleTimer !== null) {
1786
1945
  clearTimeout(this._idleTimer);
1787
1946
  this._idleTimer = null;
1788
1947
  }
1948
+ for (const socket of this._sockets) {
1949
+ socket.destroy();
1950
+ }
1951
+ this._sockets.clear();
1789
1952
  if (this._server !== null) {
1790
1953
  this._server.close();
1791
1954
  this._server = null;
1955
+ log("stop: server closed", 0 /* DEBUG */);
1792
1956
  }
1793
1957
  this._wasmModules = null;
1958
+ log("stop: wasm resources released", 0 /* DEBUG */);
1794
1959
  const pidPath = join6(tmpdir(), `kbdb-${this.ctx}.pid`);
1795
1960
  try {
1796
1961
  unlinkSync2(pidPath);
1962
+ log("stop: pid file removed", 0 /* DEBUG */);
1797
1963
  } catch {
1798
1964
  }
1799
1965
  const ipcPath = getIpcPath(this.ctx);
1800
1966
  try {
1801
1967
  unlinkSync2(ipcPath);
1968
+ log("stop: socket file removed", 0 /* DEBUG */);
1802
1969
  } catch {
1803
1970
  }
1804
1971
  const crashPath = join6(tmpdir(), `kbdb-${this.ctx}.crash`);
@@ -1806,6 +1973,7 @@ var WorkerDaemon = class {
1806
1973
  unlinkSync2(crashPath);
1807
1974
  } catch {
1808
1975
  }
1976
+ log("stop: daemon exiting", 0 /* DEBUG */);
1809
1977
  process.exit(0);
1810
1978
  }
1811
1979
  /**
@@ -1819,8 +1987,16 @@ var WorkerDaemon = class {
1819
1987
  * @returns the JSON-RPC response
1820
1988
  */
1821
1989
  async handleRequest(request) {
1990
+ log(
1991
+ "handle-request: method=" + request.method + " id=" + String(request.id),
1992
+ 0 /* DEBUG */
1993
+ );
1822
1994
  const params = request.params;
1823
1995
  if (params === void 0 || params.ctx !== this.ctx) {
1996
+ log(
1997
+ "handle-request: context mismatch id=" + String(request.id),
1998
+ 0 /* DEBUG */
1999
+ );
1824
2000
  return this.errorResponse(
1825
2001
  request.id,
1826
2002
  -32001,
@@ -1831,6 +2007,10 @@ var WorkerDaemon = class {
1831
2007
  try {
1832
2008
  const result = await this.dispatch(request.method, params);
1833
2009
  this._idleAt = Math.floor(Date.now() / 1e3);
2010
+ log(
2011
+ "handle-request: success method=" + request.method + " id=" + String(request.id),
2012
+ 0 /* DEBUG */
2013
+ );
1834
2014
  return {
1835
2015
  jsonrpc: "2.0",
1836
2016
  id: request.id,
@@ -1839,6 +2019,10 @@ var WorkerDaemon = class {
1839
2019
  } catch (err) {
1840
2020
  this._idleAt = Math.floor(Date.now() / 1e3);
1841
2021
  if (err instanceof IpcError2) {
2022
+ log(
2023
+ "handle-request: ipc error method=" + request.method + " id=" + String(request.id) + " code=" + String(err.code),
2024
+ 0 /* DEBUG */
2025
+ );
1842
2026
  return this.errorResponse(
1843
2027
  request.id,
1844
2028
  err.code,
@@ -1846,6 +2030,10 @@ var WorkerDaemon = class {
1846
2030
  );
1847
2031
  }
1848
2032
  const message = err instanceof Error ? err.message : String(err);
2033
+ log(
2034
+ "handle-request: error method=" + request.method + " id=" + String(request.id),
2035
+ 0 /* DEBUG */
2036
+ );
1849
2037
  return this.errorResponse(request.id, -32e3, message);
1850
2038
  }
1851
2039
  }
@@ -1889,7 +2077,15 @@ var WorkerDaemon = class {
1889
2077
  clearTimeout(this._idleTimer);
1890
2078
  }
1891
2079
  if (this._connectionCount === 0) {
2080
+ log(
2081
+ "idle timer started (" + String(this.config.timeoutMs) + "ms)",
2082
+ 0 /* DEBUG */
2083
+ );
1892
2084
  this._idleTimer = setTimeout(() => {
2085
+ log(
2086
+ "idle timer fired: shutting down ctx=" + this.ctx,
2087
+ 0 /* DEBUG */
2088
+ );
1893
2089
  this.stop();
1894
2090
  }, this.config.timeoutMs);
1895
2091
  }
@@ -1901,25 +2097,25 @@ var WorkerDaemon = class {
1901
2097
  try {
1902
2098
  text = readFileSync6(catalogPath, "utf-8");
1903
2099
  } catch {
1904
- throw new Error(
1905
- `cannot read database directory: ${this.targetDir} -- permission denied`
1906
- );
2100
+ const msg = `cannot read database directory: ${this.targetDir} -- permission denied`;
2101
+ log(msg, 3 /* ERROR */);
2102
+ throw new Error(msg);
1907
2103
  }
1908
2104
  const version = parseCatalogVersion(text);
1909
2105
  if (version === null) {
1910
- throw new Error(
1911
- "database version 0 requires migration to version " + String(DB_FORMAT_VERSION) + " -- run `kbdb --migrate` before starting the daemon"
1912
- );
2106
+ const msg = "database version 0 requires migration to version " + String(DB_FORMAT_VERSION) + " -- run `kbdb --migrate` before starting the daemon";
2107
+ log(msg, 3 /* ERROR */);
2108
+ throw new Error(msg);
1913
2109
  }
1914
2110
  if (version > DB_FORMAT_VERSION) {
1915
- throw new Error(
1916
- "database version " + String(version) + " is newer than kbdb version " + String(DB_FORMAT_VERSION) + " -- upgrade kbdb to open this database"
1917
- );
2111
+ const msg = "database version " + String(version) + " is newer than kbdb version " + String(DB_FORMAT_VERSION) + " -- upgrade kbdb to open this database";
2112
+ log(msg, 3 /* ERROR */);
2113
+ throw new Error(msg);
1918
2114
  }
1919
2115
  if (version < DB_FORMAT_VERSION) {
1920
- throw new Error(
1921
- "database version " + String(version) + " requires migration to version " + String(DB_FORMAT_VERSION) + " -- run `kbdb --migrate` before starting the daemon"
1922
- );
2116
+ const msg = "database version " + String(version) + " requires migration to version " + String(DB_FORMAT_VERSION) + " -- run `kbdb --migrate` before starting the daemon";
2117
+ log(msg, 3 /* ERROR */);
2118
+ throw new Error(msg);
1923
2119
  }
1924
2120
  }
1925
2121
  /** Builds a JSON-RPC error response. */
@@ -2435,8 +2631,13 @@ var WorkerDaemon = class {
2435
2631
  acquireWriteLock() {
2436
2632
  if (!this._writeLocked) {
2437
2633
  this._writeLocked = true;
2634
+ log("write-lock: acquired", 0 /* DEBUG */);
2438
2635
  return Promise.resolve();
2439
2636
  }
2637
+ log(
2638
+ "write-lock: queued depth=" + String(this._writeQueue.length + 1),
2639
+ 0 /* DEBUG */
2640
+ );
2440
2641
  return new Promise((resolve2) => {
2441
2642
  this._writeQueue.push({ resolve: resolve2 });
2442
2643
  });
@@ -2450,9 +2651,14 @@ var WorkerDaemon = class {
2450
2651
  releaseWriteLock() {
2451
2652
  const next = this._writeQueue.shift();
2452
2653
  if (next !== void 0) {
2654
+ log(
2655
+ "write-lock: released to next waiter depth=" + String(this._writeQueue.length),
2656
+ 0 /* DEBUG */
2657
+ );
2453
2658
  next.resolve();
2454
2659
  } else {
2455
2660
  this._writeLocked = false;
2661
+ log("write-lock: released", 0 /* DEBUG */);
2456
2662
  }
2457
2663
  }
2458
2664
  // -- WASM lifecycle --
@@ -2477,26 +2683,29 @@ var WorkerDaemon = class {
2477
2683
  this._integrityOk = kv["ok"] === "true";
2478
2684
  if (!this._integrityOk) {
2479
2685
  this._integrityErrors = Object.entries(kv).filter(([k]) => k !== "ok").map(([k, v]) => `${k}=${v}`);
2480
- console.error(
2481
- "[kbdb-worker] startup integrity check failed:",
2482
- this._integrityErrors.join(", ")
2686
+ log(
2687
+ "[kbdb-worker] startup integrity check failed: " + this._integrityErrors.join(", "),
2688
+ 2 /* WARNING */
2483
2689
  );
2484
2690
  }
2485
2691
  } catch {
2486
2692
  this._integrityOk = false;
2487
2693
  this._integrityErrors = ["integrity_check_failed"];
2488
- console.error(
2489
- "[kbdb-worker] startup integrity check threw an error"
2694
+ log(
2695
+ "[kbdb-worker] startup integrity check threw an error",
2696
+ 2 /* WARNING */
2490
2697
  );
2491
2698
  }
2492
2699
  }
2493
2700
  // -- WASM bridge primitives --
2494
2701
  getExport(name) {
2495
2702
  if (this._wasmModules === null) {
2703
+ log("WASM modules not loaded", 3 /* ERROR */);
2496
2704
  throw new Error("WASM modules not loaded");
2497
2705
  }
2498
2706
  const fn = this._wasmModules.kbWorker.exports[name];
2499
2707
  if (typeof fn !== "function") {
2708
+ log(`WASM export not found: ${name}`, 3 /* ERROR */);
2500
2709
  throw new Error(`WASM export not found: ${name}`);
2501
2710
  }
2502
2711
  return fn;
@@ -2520,6 +2729,7 @@ var WorkerDaemon = class {
2520
2729
  if (!memory) return;
2521
2730
  const memView = new Uint8Array(memory.buffer);
2522
2731
  if (ptr + data.length > memView.length) {
2732
+ log("WASM memory overflow", 3 /* ERROR */);
2523
2733
  throw new Error("WASM memory overflow");
2524
2734
  }
2525
2735
  memView.set(data, ptr);
@@ -2631,9 +2841,18 @@ function parseCatalogVersion(text) {
2631
2841
 
2632
2842
  // src/shared/worker-daemon/functions/start-daemon.function.ts
2633
2843
  async function startDaemon(contextPath, wasmDir2) {
2844
+ log(
2845
+ "start-daemon: entry contextPath=" + contextPath,
2846
+ 0 /* DEBUG */
2847
+ );
2634
2848
  const config = readConfig(contextPath);
2849
+ log(
2850
+ "start-daemon: config loaded timeoutMs=" + String(config.timeoutMs) + " embedding=" + (config.embedding?.provider ?? "none"),
2851
+ 0 /* DEBUG */
2852
+ );
2635
2853
  const hash = await computeSha256(contextPath);
2636
2854
  const contextId = hash.slice(0, 16);
2855
+ log("start-daemon: contextId=" + contextId, 0 /* DEBUG */);
2637
2856
  const daemon = new WorkerDaemon(
2638
2857
  contextId,
2639
2858
  contextPath,
@@ -2644,22 +2863,30 @@ async function startDaemon(contextPath, wasmDir2) {
2644
2863
  daemon.checkPreconditions();
2645
2864
  } catch (err) {
2646
2865
  const message = err instanceof Error ? err.message : String(err);
2647
- console.error(`kbdb-worker: ${message}`);
2866
+ log(`kbdb-worker: ${message}`, 3 /* ERROR */);
2648
2867
  writeCrashLog(contextId, message);
2649
2868
  process.exit(1);
2650
2869
  }
2870
+ log("preconditions passed", 0 /* DEBUG */);
2651
2871
  try {
2652
2872
  await daemon.start();
2653
2873
  } catch (err) {
2654
2874
  const message = err instanceof Error ? err.message : String(err);
2655
- console.error(`kbdb-worker: ${message}`);
2875
+ log(`kbdb-worker: ${message}`, 3 /* ERROR */);
2656
2876
  writeCrashLog(contextId, message);
2657
2877
  process.exit(1);
2658
2878
  }
2879
+ log("daemon started", 1 /* INFO */);
2880
+ log(
2881
+ "start-daemon: registering SIGTERM/SIGINT handlers",
2882
+ 0 /* DEBUG */
2883
+ );
2659
2884
  process.on("SIGTERM", () => {
2885
+ log("start-daemon: SIGTERM received", 0 /* DEBUG */);
2660
2886
  daemon.stop();
2661
2887
  });
2662
2888
  process.on("SIGINT", () => {
2889
+ log("start-daemon: SIGINT received", 0 /* DEBUG */);
2663
2890
  daemon.stop();
2664
2891
  });
2665
2892
  }
@@ -2677,11 +2904,11 @@ function writeCrashLog(contextId, message) {
2677
2904
 
2678
2905
  // src/shared/worker-daemon/functions/run-worker.function.ts
2679
2906
  import { resolve } from "node:path";
2680
- async function runWorker(argv, deps) {
2681
- const contextPath = argv[0];
2907
+ async function runWorker(argv2, deps) {
2908
+ const { contextPath, logLevel } = parseWorkerArgs(argv2);
2682
2909
  if (!contextPath) {
2683
2910
  return {
2684
- error: "usage: kbdb-worker <context-path>",
2911
+ error: "usage: kbdb-worker <context-path> [--log-level <level>]",
2685
2912
  exitCode: 1
2686
2913
  };
2687
2914
  }
@@ -2693,27 +2920,52 @@ async function runWorker(argv, deps) {
2693
2920
  };
2694
2921
  }
2695
2922
  await deps.startDaemon(resolved, deps.wasmDir);
2696
- return { exitCode: 0 };
2923
+ return { exitCode: 0, logLevel };
2924
+ }
2925
+ function parseWorkerArgs(argv2) {
2926
+ let contextPath;
2927
+ let logLevel;
2928
+ for (let i = 0; i < argv2.length; i++) {
2929
+ if (argv2[i] === "--log-level" && argv2[i + 1]) {
2930
+ logLevel = parseLogLevel(argv2[++i]);
2931
+ } else if (!contextPath) {
2932
+ contextPath = argv2[i];
2933
+ }
2934
+ }
2935
+ return { contextPath, logLevel };
2697
2936
  }
2698
2937
 
2699
2938
  // src/worker.ts
2700
2939
  var baseDir = resolveBaseDir(import.meta.url, import.meta.dirname);
2701
2940
  var wasmDir = resolveWasmDir(baseDir.scriptDir);
2941
+ var argv = getArgs();
2942
+ var cliLogLevel = extractLogLevel(argv);
2943
+ configureLog({ level: resolveLogLevel(cliLogLevel) });
2944
+ log("daemon process starting", 0 /* DEBUG */);
2702
2945
  async function main() {
2703
- const result = await runWorker(getArgs(), {
2946
+ const result = await runWorker(argv, {
2704
2947
  existsSync: existsSync5,
2705
2948
  startDaemon,
2706
2949
  wasmDir
2707
2950
  });
2708
2951
  if (result.error) {
2709
- console.error(result.error);
2952
+ log(result.error, 3 /* ERROR */);
2710
2953
  }
2711
2954
  if (result.exitCode !== 0) {
2712
2955
  process.exitCode = result.exitCode;
2713
2956
  }
2714
2957
  }
2715
2958
  main().catch((err) => {
2716
- console.error(err);
2959
+ const msg = err instanceof Error ? err.message : String(err);
2960
+ log(msg, 3 /* ERROR */);
2717
2961
  process.exitCode = 1;
2718
2962
  });
2963
+ function extractLogLevel(args) {
2964
+ for (let i = 0; i < args.length; i++) {
2965
+ if (args[i] === "--log-level" && args[i + 1]) {
2966
+ return parseLogLevel(args[i + 1]);
2967
+ }
2968
+ }
2969
+ return void 0;
2970
+ }
2719
2971
  //# sourceMappingURL=worker.mjs.map