kibi-runtime 2.13.3 → 2.14.0

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.
@@ -102,7 +102,7 @@ function manifestVersion(manifestPath, expectedName) {
102
102
  }
103
103
  function cliVersion() {
104
104
  if (true)
105
- return "2.18.0";
105
+ return "2.19.0";
106
106
  return manifestVersion(path.join(moduleDirectory, "..", "package.json"), "kibi-cli") ?? "unknown";
107
107
  }
108
108
  function coreVersion() {
@@ -1798,6 +1798,33 @@ function classifyStoreLockHolder(owner) {
1798
1798
  }
1799
1799
  return "alive";
1800
1800
  }
1801
+ function readStoreLockOwner(storePath) {
1802
+ const candidates = [
1803
+ join(storePath, ".kibi-lock-owner.json"),
1804
+ join(storePath, "rdf", ".kibi-lock-owner.json")
1805
+ ];
1806
+ for (const journalPath of candidates) {
1807
+ if (!existsSync4(journalPath))
1808
+ continue;
1809
+ let parsed;
1810
+ try {
1811
+ parsed = JSON.parse(readFileSync3(journalPath, "utf8"));
1812
+ } catch {
1813
+ continue;
1814
+ }
1815
+ if (parsed === null || typeof parsed !== "object")
1816
+ continue;
1817
+ const record = parsed;
1818
+ const owner = {
1819
+ ...typeof record.pid === "number" ? { pid: record.pid } : {},
1820
+ ...typeof record.workspaceRoot === "string" ? { workspaceRoot: record.workspaceRoot } : {},
1821
+ ...typeof record.bootId === "string" ? { bootId: record.bootId } : {},
1822
+ ...typeof record.startedAt === "string" ? { startedAt: record.startedAt } : {}
1823
+ };
1824
+ return { owner, journalPath };
1825
+ }
1826
+ return null;
1827
+ }
1801
1828
  function breakStoreLock(rdfDirectory) {
1802
1829
  const candidates = [
1803
1830
  join(rdfDirectory, "lock"),
@@ -1818,6 +1845,32 @@ function recordStoreLockTakeover(storePath, brokenPid, reason, at = new Date) {
1818
1845
  `, { mode: 384 });
1819
1846
  } catch {}
1820
1847
  }
1848
+ function readRdfLockPid(lockPath) {
1849
+ try {
1850
+ const match = /pid\((\d+)\)/.exec(readFileSync3(lockPath, "utf8"));
1851
+ const pid = Number.parseInt(match?.[1] ?? "", 10);
1852
+ return Number.isInteger(pid) && pid > 0 ? pid : null;
1853
+ } catch {
1854
+ return null;
1855
+ }
1856
+ }
1857
+ function noteDeadHolderBeforeAttach(kbPath) {
1858
+ const lockPath = join(kbPath, "rdf", "lock");
1859
+ if (!existsSync4(lockPath))
1860
+ return () => {
1861
+ return;
1862
+ };
1863
+ const lockPid = readRdfLockPid(lockPath);
1864
+ const journal = readStoreLockOwner(kbPath)?.owner ?? null;
1865
+ const owner = journal !== null && (lockPid === null || journal.pid === lockPid) ? journal : lockPid !== null ? { pid: lockPid } : null;
1866
+ const decision = decideStoreLockTakeover(owner);
1867
+ if (decision.action !== "break")
1868
+ return () => {
1869
+ return;
1870
+ };
1871
+ const brokenPid = owner?.pid ?? null;
1872
+ return () => recordStoreLockTakeover(kbPath, brokenPid, decision.reason);
1873
+ }
1821
1874
  function decideStoreLockTakeover(owner) {
1822
1875
  const state = classifyStoreLockHolder(owner);
1823
1876
  switch (state) {
@@ -2302,26 +2355,13 @@ function recoverStaleJournalLock(branchPath) {
2302
2355
  const lockPath = path8.join(branchPath, "rdf", "lock");
2303
2356
  if (!existsSync6(lockPath))
2304
2357
  return;
2305
- let contents = "";
2306
- try {
2307
- contents = readFileSync5(lockPath, "utf8");
2308
- } catch {
2309
- return;
2310
- }
2311
- const match = /pid\((\d+)\)/.exec(contents);
2312
- if (!match)
2313
- return;
2314
- const pid = Number.parseInt(match[1] ?? "", 10);
2315
- if (!Number.isFinite(pid) || pid <= 0 || pid === process.pid)
2358
+ const pid = readRdfLockPid(lockPath);
2359
+ if (pid === null || pid === process.pid || isProcessAlive(pid))
2316
2360
  return;
2361
+ recordStoreLockTakeover(branchPath, pid, decideStoreLockTakeover({ pid }).reason);
2317
2362
  try {
2318
- process.kill(pid, 0);
2319
- return;
2320
- } catch {
2321
- try {
2322
- rmSync2(lockPath, { force: true });
2323
- } catch {}
2324
- }
2363
+ rmSync2(lockPath, { force: true });
2364
+ } catch {}
2325
2365
  }
2326
2366
  function connectSocket(socketPath, timeoutMs) {
2327
2367
  return new Promise((resolve, reject) => {
@@ -2541,8 +2581,11 @@ class EngineSession {
2541
2581
  }
2542
2582
  async boot() {
2543
2583
  await this.prolog.start();
2584
+ const recordTakeover = noteDeadHolderBeforeAttach(this.branchPath);
2544
2585
  const attached = await this.prolog.query(`kb_attach('${quoteProlog(this.branchPath)}')`);
2545
- if (!attached.success) {
2586
+ if (attached.success)
2587
+ recordTakeover();
2588
+ else {
2546
2589
  const recovered = await retryAttachAfterBreakingStaleLock(this.prolog, this.branchPath, attached);
2547
2590
  if (!recovered.success)
2548
2591
  throw new Error(recovered.error ?? "Failed to attach branch KB");