opencode-codebase-index 0.14.0 → 0.15.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.
- package/README.md +40 -4
- package/dist/cli.cjs +1639 -557
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +1638 -546
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +1598 -531
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1594 -517
- package/dist/index.js.map +1 -1
- package/dist/pi-extension.cjs +1305 -303
- package/dist/pi-extension.cjs.map +1 -1
- package/dist/pi-extension.js +1304 -292
- package/dist/pi-extension.js.map +1 -1
- package/native/codebase-index-native.darwin-arm64.node +0 -0
- package/native/codebase-index-native.darwin-x64.node +0 -0
- package/native/codebase-index-native.linux-arm64-gnu.node +0 -0
- package/native/codebase-index-native.linux-x64-gnu.node +0 -0
- package/native/codebase-index-native.win32-x64-msvc.node +0 -0
- package/package.json +11 -8
package/dist/index.js
CHANGED
|
@@ -490,7 +490,7 @@ var require_ignore = __commonJS({
|
|
|
490
490
|
// path matching.
|
|
491
491
|
// - check `string` either `MODE_IGNORE` or `MODE_CHECK_IGNORE`
|
|
492
492
|
// @returns {TestResult} true if a file is ignored
|
|
493
|
-
test(
|
|
493
|
+
test(path24, checkUnignored, mode) {
|
|
494
494
|
let ignored = false;
|
|
495
495
|
let unignored = false;
|
|
496
496
|
let matchedRule;
|
|
@@ -499,7 +499,7 @@ var require_ignore = __commonJS({
|
|
|
499
499
|
if (unignored === negative && ignored !== unignored || negative && !ignored && !unignored && !checkUnignored) {
|
|
500
500
|
return;
|
|
501
501
|
}
|
|
502
|
-
const matched = rule[mode].test(
|
|
502
|
+
const matched = rule[mode].test(path24);
|
|
503
503
|
if (!matched) {
|
|
504
504
|
return;
|
|
505
505
|
}
|
|
@@ -520,17 +520,17 @@ var require_ignore = __commonJS({
|
|
|
520
520
|
var throwError = (message, Ctor) => {
|
|
521
521
|
throw new Ctor(message);
|
|
522
522
|
};
|
|
523
|
-
var checkPath = (
|
|
524
|
-
if (!isString(
|
|
523
|
+
var checkPath = (path24, originalPath, doThrow) => {
|
|
524
|
+
if (!isString(path24)) {
|
|
525
525
|
return doThrow(
|
|
526
526
|
`path must be a string, but got \`${originalPath}\``,
|
|
527
527
|
TypeError
|
|
528
528
|
);
|
|
529
529
|
}
|
|
530
|
-
if (!
|
|
530
|
+
if (!path24) {
|
|
531
531
|
return doThrow(`path must not be empty`, TypeError);
|
|
532
532
|
}
|
|
533
|
-
if (checkPath.isNotRelative(
|
|
533
|
+
if (checkPath.isNotRelative(path24)) {
|
|
534
534
|
const r = "`path.relative()`d";
|
|
535
535
|
return doThrow(
|
|
536
536
|
`path should be a ${r} string, but got "${originalPath}"`,
|
|
@@ -539,7 +539,7 @@ var require_ignore = __commonJS({
|
|
|
539
539
|
}
|
|
540
540
|
return true;
|
|
541
541
|
};
|
|
542
|
-
var isNotRelative = (
|
|
542
|
+
var isNotRelative = (path24) => REGEX_TEST_INVALID_PATH.test(path24);
|
|
543
543
|
checkPath.isNotRelative = isNotRelative;
|
|
544
544
|
checkPath.convert = (p) => p;
|
|
545
545
|
var Ignore2 = class {
|
|
@@ -569,19 +569,19 @@ var require_ignore = __commonJS({
|
|
|
569
569
|
}
|
|
570
570
|
// @returns {TestResult}
|
|
571
571
|
_test(originalPath, cache, checkUnignored, slices) {
|
|
572
|
-
const
|
|
572
|
+
const path24 = originalPath && checkPath.convert(originalPath);
|
|
573
573
|
checkPath(
|
|
574
|
-
|
|
574
|
+
path24,
|
|
575
575
|
originalPath,
|
|
576
576
|
this._strictPathCheck ? throwError : RETURN_FALSE
|
|
577
577
|
);
|
|
578
|
-
return this._t(
|
|
578
|
+
return this._t(path24, cache, checkUnignored, slices);
|
|
579
579
|
}
|
|
580
|
-
checkIgnore(
|
|
581
|
-
if (!REGEX_TEST_TRAILING_SLASH.test(
|
|
582
|
-
return this.test(
|
|
580
|
+
checkIgnore(path24) {
|
|
581
|
+
if (!REGEX_TEST_TRAILING_SLASH.test(path24)) {
|
|
582
|
+
return this.test(path24);
|
|
583
583
|
}
|
|
584
|
-
const slices =
|
|
584
|
+
const slices = path24.split(SLASH2).filter(Boolean);
|
|
585
585
|
slices.pop();
|
|
586
586
|
if (slices.length) {
|
|
587
587
|
const parent = this._t(
|
|
@@ -594,18 +594,18 @@ var require_ignore = __commonJS({
|
|
|
594
594
|
return parent;
|
|
595
595
|
}
|
|
596
596
|
}
|
|
597
|
-
return this._rules.test(
|
|
597
|
+
return this._rules.test(path24, false, MODE_CHECK_IGNORE);
|
|
598
598
|
}
|
|
599
|
-
_t(
|
|
600
|
-
if (
|
|
601
|
-
return cache[
|
|
599
|
+
_t(path24, cache, checkUnignored, slices) {
|
|
600
|
+
if (path24 in cache) {
|
|
601
|
+
return cache[path24];
|
|
602
602
|
}
|
|
603
603
|
if (!slices) {
|
|
604
|
-
slices =
|
|
604
|
+
slices = path24.split(SLASH2).filter(Boolean);
|
|
605
605
|
}
|
|
606
606
|
slices.pop();
|
|
607
607
|
if (!slices.length) {
|
|
608
|
-
return cache[
|
|
608
|
+
return cache[path24] = this._rules.test(path24, checkUnignored, MODE_IGNORE);
|
|
609
609
|
}
|
|
610
610
|
const parent = this._t(
|
|
611
611
|
slices.join(SLASH2) + SLASH2,
|
|
@@ -613,29 +613,29 @@ var require_ignore = __commonJS({
|
|
|
613
613
|
checkUnignored,
|
|
614
614
|
slices
|
|
615
615
|
);
|
|
616
|
-
return cache[
|
|
616
|
+
return cache[path24] = parent.ignored ? parent : this._rules.test(path24, checkUnignored, MODE_IGNORE);
|
|
617
617
|
}
|
|
618
|
-
ignores(
|
|
619
|
-
return this._test(
|
|
618
|
+
ignores(path24) {
|
|
619
|
+
return this._test(path24, this._ignoreCache, false).ignored;
|
|
620
620
|
}
|
|
621
621
|
createFilter() {
|
|
622
|
-
return (
|
|
622
|
+
return (path24) => !this.ignores(path24);
|
|
623
623
|
}
|
|
624
624
|
filter(paths) {
|
|
625
625
|
return makeArray(paths).filter(this.createFilter());
|
|
626
626
|
}
|
|
627
627
|
// @returns {TestResult}
|
|
628
|
-
test(
|
|
629
|
-
return this._test(
|
|
628
|
+
test(path24) {
|
|
629
|
+
return this._test(path24, this._testCache, true);
|
|
630
630
|
}
|
|
631
631
|
};
|
|
632
632
|
var factory = (options) => new Ignore2(options);
|
|
633
|
-
var isPathValid = (
|
|
633
|
+
var isPathValid = (path24) => checkPath(path24 && checkPath.convert(path24), path24, RETURN_FALSE);
|
|
634
634
|
var setupWindows = () => {
|
|
635
635
|
const makePosix = (str) => /^\\\\\?\\/.test(str) || /["<>|\u0000-\u001F]+/u.test(str) ? str : str.replace(/\\/g, "/");
|
|
636
636
|
checkPath.convert = makePosix;
|
|
637
637
|
const REGEX_TEST_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i;
|
|
638
|
-
checkPath.isNotRelative = (
|
|
638
|
+
checkPath.isNotRelative = (path24) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path24) || isNotRelative(path24);
|
|
639
639
|
};
|
|
640
640
|
if (
|
|
641
641
|
// Detect `process` so that it can run in browsers.
|
|
@@ -651,16 +651,16 @@ var require_ignore = __commonJS({
|
|
|
651
651
|
});
|
|
652
652
|
|
|
653
653
|
// src/index.ts
|
|
654
|
-
import * as
|
|
655
|
-
import * as
|
|
654
|
+
import * as os6 from "os";
|
|
655
|
+
import * as path23 from "path";
|
|
656
656
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
657
657
|
|
|
658
658
|
// src/config/constants.ts
|
|
659
659
|
var DEFAULT_INCLUDE = [
|
|
660
|
-
"**/*.{ts,tsx,js,jsx,mjs,cjs}",
|
|
660
|
+
"**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}",
|
|
661
661
|
"**/*.{py,pyi}",
|
|
662
|
-
"**/*.{go,rs,java,kt,scala}",
|
|
663
|
-
"**/*.{c,cpp,cc,h,hpp}",
|
|
662
|
+
"**/*.{go,rs,java,cs,kt,scala}",
|
|
663
|
+
"**/*.{c,cpp,cc,cxx,h,hpp,hxx}",
|
|
664
664
|
"**/*.{rb,php,inc,swift}",
|
|
665
665
|
"**/*.{cls,trigger}",
|
|
666
666
|
"**/*.{vue,svelte,astro}",
|
|
@@ -1244,6 +1244,9 @@ function getHostProjectIndexRelativePath(host) {
|
|
|
1244
1244
|
function hasHostProjectConfig(projectRoot, host) {
|
|
1245
1245
|
return existsSync3(path3.join(projectRoot, getProjectConfigRelativePath(host)));
|
|
1246
1246
|
}
|
|
1247
|
+
function hasHostGlobalConfig(host) {
|
|
1248
|
+
return existsSync3(getGlobalConfigPath(host));
|
|
1249
|
+
}
|
|
1247
1250
|
function getGlobalIndexPath(host = "opencode") {
|
|
1248
1251
|
switch (host) {
|
|
1249
1252
|
case "opencode":
|
|
@@ -1283,6 +1286,9 @@ function resolveGlobalIndexPath(host = "opencode") {
|
|
|
1283
1286
|
return hostIndexPath;
|
|
1284
1287
|
}
|
|
1285
1288
|
if (host !== "opencode") {
|
|
1289
|
+
if (hasHostGlobalConfig(host)) {
|
|
1290
|
+
return hostIndexPath;
|
|
1291
|
+
}
|
|
1286
1292
|
const legacyIndexPath = getGlobalIndexPath("opencode");
|
|
1287
1293
|
if (existsSync3(legacyIndexPath)) {
|
|
1288
1294
|
return legacyIndexPath;
|
|
@@ -1581,13 +1587,439 @@ function loadMergedConfig(projectRoot, host = "opencode") {
|
|
|
1581
1587
|
return merged;
|
|
1582
1588
|
}
|
|
1583
1589
|
|
|
1590
|
+
// src/indexer/index-lock.ts
|
|
1591
|
+
import { randomUUID } from "crypto";
|
|
1592
|
+
import {
|
|
1593
|
+
existsSync as existsSync5,
|
|
1594
|
+
lstatSync,
|
|
1595
|
+
mkdirSync as mkdirSync2,
|
|
1596
|
+
readFileSync as readFileSync4,
|
|
1597
|
+
readdirSync as readdirSync2,
|
|
1598
|
+
realpathSync,
|
|
1599
|
+
renameSync,
|
|
1600
|
+
rmSync,
|
|
1601
|
+
writeFileSync as writeFileSync2
|
|
1602
|
+
} from "fs";
|
|
1603
|
+
import * as os2 from "os";
|
|
1604
|
+
import * as path7 from "path";
|
|
1605
|
+
var OWNER_FILE_NAME = "owner.json";
|
|
1606
|
+
var RECLAIM_DIRECTORY_NAME = "reclaiming";
|
|
1607
|
+
var RECOVERY_MARKER_PREFIX = "indexing.lock.recovery.";
|
|
1608
|
+
var UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
1609
|
+
var VALID_OPERATIONS = /* @__PURE__ */ new Set([
|
|
1610
|
+
"initialize",
|
|
1611
|
+
"index",
|
|
1612
|
+
"force-index",
|
|
1613
|
+
"clear",
|
|
1614
|
+
"health-check",
|
|
1615
|
+
"retry-failed-batches",
|
|
1616
|
+
"recovery"
|
|
1617
|
+
]);
|
|
1618
|
+
var temporaryCounter = 0;
|
|
1619
|
+
function getErrorCode(error) {
|
|
1620
|
+
return typeof error === "object" && error !== null && "code" in error ? String(error.code) : void 0;
|
|
1621
|
+
}
|
|
1622
|
+
function retryTransientFilesystemOperation(operation) {
|
|
1623
|
+
let lastError;
|
|
1624
|
+
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
1625
|
+
try {
|
|
1626
|
+
operation();
|
|
1627
|
+
return;
|
|
1628
|
+
} catch (error) {
|
|
1629
|
+
lastError = error;
|
|
1630
|
+
const code = getErrorCode(error);
|
|
1631
|
+
if (code !== "EBUSY" && code !== "EPERM") throw error;
|
|
1632
|
+
if (attempt < 2) {
|
|
1633
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, (attempt + 1) * 10);
|
|
1634
|
+
}
|
|
1635
|
+
}
|
|
1636
|
+
}
|
|
1637
|
+
throw lastError;
|
|
1638
|
+
}
|
|
1639
|
+
function parseOwner(value) {
|
|
1640
|
+
if (typeof value !== "object" || value === null) return null;
|
|
1641
|
+
const candidate = value;
|
|
1642
|
+
if (!Number.isInteger(candidate.pid) || (candidate.pid ?? 0) <= 0) return null;
|
|
1643
|
+
if (typeof candidate.hostname !== "string" || candidate.hostname.length === 0) return null;
|
|
1644
|
+
if (typeof candidate.startedAt !== "string" || Number.isNaN(Date.parse(candidate.startedAt))) return null;
|
|
1645
|
+
if (typeof candidate.operation !== "string" || !VALID_OPERATIONS.has(candidate.operation)) return null;
|
|
1646
|
+
if (typeof candidate.token !== "string" || !UUID_PATTERN.test(candidate.token)) return null;
|
|
1647
|
+
return candidate;
|
|
1648
|
+
}
|
|
1649
|
+
function parseReclaimOwner(value) {
|
|
1650
|
+
if (typeof value !== "object" || value === null) return null;
|
|
1651
|
+
const candidate = value;
|
|
1652
|
+
if (!Number.isInteger(candidate.pid) || (candidate.pid ?? 0) <= 0) return null;
|
|
1653
|
+
if (typeof candidate.hostname !== "string" || candidate.hostname.length === 0) return null;
|
|
1654
|
+
if (typeof candidate.startedAt !== "string" || Number.isNaN(Date.parse(candidate.startedAt))) return null;
|
|
1655
|
+
if (typeof candidate.token !== "string" || !UUID_PATTERN.test(candidate.token)) return null;
|
|
1656
|
+
if (typeof candidate.expectedOwnerToken !== "string" || !UUID_PATTERN.test(candidate.expectedOwnerToken)) return null;
|
|
1657
|
+
return candidate;
|
|
1658
|
+
}
|
|
1659
|
+
function readJsonDirectory(directoryPath, parser) {
|
|
1660
|
+
try {
|
|
1661
|
+
return parser(JSON.parse(readFileSync4(path7.join(directoryPath, OWNER_FILE_NAME), "utf-8")));
|
|
1662
|
+
} catch {
|
|
1663
|
+
return null;
|
|
1664
|
+
}
|
|
1665
|
+
}
|
|
1666
|
+
function readDirectoryOwner(lockPath) {
|
|
1667
|
+
return readJsonDirectory(lockPath, parseOwner);
|
|
1668
|
+
}
|
|
1669
|
+
function readReclaimOwner(markerPath) {
|
|
1670
|
+
return readJsonDirectory(markerPath, parseReclaimOwner);
|
|
1671
|
+
}
|
|
1672
|
+
function readRecoveryOwner(markerPath) {
|
|
1673
|
+
return readDirectoryOwner(markerPath);
|
|
1674
|
+
}
|
|
1675
|
+
function readLegacyOwner(lockPath) {
|
|
1676
|
+
try {
|
|
1677
|
+
const parsed = JSON.parse(readFileSync4(lockPath, "utf-8"));
|
|
1678
|
+
if (!Number.isInteger(parsed.pid) || Number(parsed.pid) <= 0) return null;
|
|
1679
|
+
if (typeof parsed.startedAt !== "string" || Number.isNaN(Date.parse(parsed.startedAt))) return null;
|
|
1680
|
+
return {
|
|
1681
|
+
pid: Number(parsed.pid),
|
|
1682
|
+
hostname: typeof parsed.hostname === "string" ? parsed.hostname : os2.hostname(),
|
|
1683
|
+
startedAt: parsed.startedAt,
|
|
1684
|
+
operation: typeof parsed.operation === "string" && VALID_OPERATIONS.has(parsed.operation) ? parsed.operation : "index",
|
|
1685
|
+
token: typeof parsed.token === "string" ? parsed.token : "legacy-v0.14.0"
|
|
1686
|
+
};
|
|
1687
|
+
} catch {
|
|
1688
|
+
return null;
|
|
1689
|
+
}
|
|
1690
|
+
}
|
|
1691
|
+
function getOwnerLiveness(owner) {
|
|
1692
|
+
if (owner.hostname !== os2.hostname()) return "unknown";
|
|
1693
|
+
try {
|
|
1694
|
+
process.kill(owner.pid, 0);
|
|
1695
|
+
return "alive";
|
|
1696
|
+
} catch (error) {
|
|
1697
|
+
const code = getErrorCode(error);
|
|
1698
|
+
if (code === "ESRCH") return "dead";
|
|
1699
|
+
if (code === "EPERM") return "alive";
|
|
1700
|
+
return "unknown";
|
|
1701
|
+
}
|
|
1702
|
+
}
|
|
1703
|
+
function sameOwner(left, right) {
|
|
1704
|
+
return left.pid === right.pid && left.hostname === right.hostname && left.token === right.token;
|
|
1705
|
+
}
|
|
1706
|
+
function sameReclaimOwner(left, right) {
|
|
1707
|
+
return left.pid === right.pid && left.hostname === right.hostname && left.token === right.token && left.expectedOwnerToken === right.expectedOwnerToken;
|
|
1708
|
+
}
|
|
1709
|
+
function publishJsonDirectory(finalPath, value) {
|
|
1710
|
+
const candidatePath = `${finalPath}.candidate.${process.pid}.${randomUUID()}`;
|
|
1711
|
+
mkdirSync2(candidatePath, { mode: 448 });
|
|
1712
|
+
try {
|
|
1713
|
+
writeFileSync2(path7.join(candidatePath, OWNER_FILE_NAME), JSON.stringify(value), {
|
|
1714
|
+
encoding: "utf-8",
|
|
1715
|
+
flag: "wx",
|
|
1716
|
+
mode: 384
|
|
1717
|
+
});
|
|
1718
|
+
if (existsSync5(finalPath)) return false;
|
|
1719
|
+
try {
|
|
1720
|
+
renameSync(candidatePath, finalPath);
|
|
1721
|
+
return true;
|
|
1722
|
+
} catch (error) {
|
|
1723
|
+
if (existsSync5(finalPath)) return false;
|
|
1724
|
+
throw error;
|
|
1725
|
+
}
|
|
1726
|
+
} finally {
|
|
1727
|
+
if (existsSync5(candidatePath)) rmSync(candidatePath, { recursive: true, force: true });
|
|
1728
|
+
}
|
|
1729
|
+
}
|
|
1730
|
+
function createOwner(operation) {
|
|
1731
|
+
return {
|
|
1732
|
+
pid: process.pid,
|
|
1733
|
+
hostname: os2.hostname(),
|
|
1734
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1735
|
+
operation,
|
|
1736
|
+
token: randomUUID()
|
|
1737
|
+
};
|
|
1738
|
+
}
|
|
1739
|
+
function recoveryMarkerPath(indexPath, owner) {
|
|
1740
|
+
return path7.join(indexPath, `${RECOVERY_MARKER_PREFIX}${owner.token}`);
|
|
1741
|
+
}
|
|
1742
|
+
function publishRecoveryMarker(indexPath, owner) {
|
|
1743
|
+
const markerPath = recoveryMarkerPath(indexPath, owner);
|
|
1744
|
+
if (!publishJsonDirectory(markerPath, owner)) {
|
|
1745
|
+
const markerOwner = readRecoveryOwner(markerPath);
|
|
1746
|
+
if (!markerOwner || !sameOwner(markerOwner, owner)) {
|
|
1747
|
+
throw new IndexLockContentionError(markerPath, markerOwner, "unknown-owner");
|
|
1748
|
+
}
|
|
1749
|
+
}
|
|
1750
|
+
return markerPath;
|
|
1751
|
+
}
|
|
1752
|
+
function getPendingRecoveries(indexPath) {
|
|
1753
|
+
const recoveries = [];
|
|
1754
|
+
const markerNames = readdirSync2(indexPath).filter((name) => {
|
|
1755
|
+
if (!name.startsWith(RECOVERY_MARKER_PREFIX)) return false;
|
|
1756
|
+
return UUID_PATTERN.test(name.slice(RECOVERY_MARKER_PREFIX.length));
|
|
1757
|
+
}).sort();
|
|
1758
|
+
for (const markerName of markerNames) {
|
|
1759
|
+
const markerPath = path7.join(indexPath, markerName);
|
|
1760
|
+
const markerToken = markerName.slice(RECOVERY_MARKER_PREFIX.length);
|
|
1761
|
+
let markerStats;
|
|
1762
|
+
try {
|
|
1763
|
+
markerStats = lstatSync(markerPath);
|
|
1764
|
+
} catch (error) {
|
|
1765
|
+
if (getErrorCode(error) === "ENOENT") continue;
|
|
1766
|
+
throw error;
|
|
1767
|
+
}
|
|
1768
|
+
if (!markerStats.isDirectory()) {
|
|
1769
|
+
throw new IndexLockContentionError(markerPath, null, "unknown-owner");
|
|
1770
|
+
}
|
|
1771
|
+
const owner = readRecoveryOwner(markerPath);
|
|
1772
|
+
if (!owner || owner.token !== markerToken || getOwnerLiveness(owner) !== "dead") {
|
|
1773
|
+
throw new IndexLockContentionError(markerPath, owner, "unknown-owner");
|
|
1774
|
+
}
|
|
1775
|
+
recoveries.push({ owner, markerPath });
|
|
1776
|
+
}
|
|
1777
|
+
recoveries.sort((left, right) => {
|
|
1778
|
+
const byTimestamp = left.owner.startedAt.localeCompare(right.owner.startedAt);
|
|
1779
|
+
return byTimestamp !== 0 ? byTimestamp : left.markerPath.localeCompare(right.markerPath);
|
|
1780
|
+
});
|
|
1781
|
+
return recoveries;
|
|
1782
|
+
}
|
|
1783
|
+
function cleanupDeadPublicationCandidates(indexPath) {
|
|
1784
|
+
const candidatePattern = /^indexing\.lock(?:\.recovery\.[0-9a-f-]{36})?\.candidate\.(\d+)\.[0-9a-f-]{36}$/i;
|
|
1785
|
+
for (const entry of readdirSync2(indexPath)) {
|
|
1786
|
+
const match = candidatePattern.exec(entry);
|
|
1787
|
+
if (!match) continue;
|
|
1788
|
+
const pid = Number(match[1]);
|
|
1789
|
+
if (!Number.isInteger(pid) || pid <= 0) continue;
|
|
1790
|
+
if (getOwnerLiveness({ pid, hostname: os2.hostname() }) !== "dead") continue;
|
|
1791
|
+
rmSync(path7.join(indexPath, entry), { recursive: true, force: true });
|
|
1792
|
+
}
|
|
1793
|
+
}
|
|
1794
|
+
function removeDeadReclaimMarker(lockPath, expectedOwner) {
|
|
1795
|
+
const markerPath = path7.join(lockPath, RECLAIM_DIRECTORY_NAME);
|
|
1796
|
+
const marker = readReclaimOwner(markerPath);
|
|
1797
|
+
if (!marker || marker.expectedOwnerToken !== expectedOwner.token || getOwnerLiveness(marker) !== "dead") {
|
|
1798
|
+
return false;
|
|
1799
|
+
}
|
|
1800
|
+
const currentOwner = readDirectoryOwner(lockPath);
|
|
1801
|
+
if (!currentOwner || !sameOwner(currentOwner, expectedOwner) || getOwnerLiveness(currentOwner) !== "dead") {
|
|
1802
|
+
return false;
|
|
1803
|
+
}
|
|
1804
|
+
const claimedMarkerPath = `${markerPath}.stale.${marker.pid}.${marker.token}.${randomUUID()}`;
|
|
1805
|
+
try {
|
|
1806
|
+
renameSync(markerPath, claimedMarkerPath);
|
|
1807
|
+
} catch (error) {
|
|
1808
|
+
if (getErrorCode(error) === "ENOENT") return false;
|
|
1809
|
+
throw error;
|
|
1810
|
+
}
|
|
1811
|
+
const claimedMarker = readReclaimOwner(claimedMarkerPath);
|
|
1812
|
+
const ownerAfterClaim = readDirectoryOwner(lockPath);
|
|
1813
|
+
if (!claimedMarker || !sameReclaimOwner(claimedMarker, marker) || !ownerAfterClaim || !sameOwner(ownerAfterClaim, expectedOwner) || getOwnerLiveness(ownerAfterClaim) !== "dead") {
|
|
1814
|
+
if (!existsSync5(markerPath) && existsSync5(claimedMarkerPath)) {
|
|
1815
|
+
renameSync(claimedMarkerPath, markerPath);
|
|
1816
|
+
}
|
|
1817
|
+
return false;
|
|
1818
|
+
}
|
|
1819
|
+
rmSync(claimedMarkerPath, { recursive: true, force: true });
|
|
1820
|
+
return true;
|
|
1821
|
+
}
|
|
1822
|
+
function reclaimDeadOwner(indexPath, lockPath, expectedOwner) {
|
|
1823
|
+
const reclaimPath = path7.join(lockPath, RECLAIM_DIRECTORY_NAME);
|
|
1824
|
+
const reclaimOwner = {
|
|
1825
|
+
pid: process.pid,
|
|
1826
|
+
hostname: os2.hostname(),
|
|
1827
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1828
|
+
token: randomUUID(),
|
|
1829
|
+
expectedOwnerToken: expectedOwner.token
|
|
1830
|
+
};
|
|
1831
|
+
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
1832
|
+
if (publishJsonDirectory(reclaimPath, reclaimOwner)) break;
|
|
1833
|
+
if (attempt === 0 && removeDeadReclaimMarker(lockPath, expectedOwner)) continue;
|
|
1834
|
+
return false;
|
|
1835
|
+
}
|
|
1836
|
+
try {
|
|
1837
|
+
const currentReclaimer = readReclaimOwner(reclaimPath);
|
|
1838
|
+
const currentOwner = readDirectoryOwner(lockPath);
|
|
1839
|
+
if (!currentReclaimer || !sameReclaimOwner(currentReclaimer, reclaimOwner) || !currentOwner || !sameOwner(currentOwner, expectedOwner) || getOwnerLiveness(currentOwner) !== "dead") {
|
|
1840
|
+
return false;
|
|
1841
|
+
}
|
|
1842
|
+
publishRecoveryMarker(indexPath, expectedOwner);
|
|
1843
|
+
const ownerBeforeQuarantine = readDirectoryOwner(lockPath);
|
|
1844
|
+
const reclaimerBeforeQuarantine = readReclaimOwner(reclaimPath);
|
|
1845
|
+
if (!ownerBeforeQuarantine || !sameOwner(ownerBeforeQuarantine, expectedOwner) || getOwnerLiveness(ownerBeforeQuarantine) !== "dead" || !reclaimerBeforeQuarantine || !sameReclaimOwner(reclaimerBeforeQuarantine, reclaimOwner)) {
|
|
1846
|
+
return false;
|
|
1847
|
+
}
|
|
1848
|
+
const quarantinePath = `${lockPath}.stale.${expectedOwner.token}.${reclaimOwner.token}`;
|
|
1849
|
+
renameSync(lockPath, quarantinePath);
|
|
1850
|
+
rmSync(quarantinePath, { recursive: true, force: true });
|
|
1851
|
+
return true;
|
|
1852
|
+
} catch (error) {
|
|
1853
|
+
if (getErrorCode(error) === "ENOENT") return false;
|
|
1854
|
+
throw error;
|
|
1855
|
+
}
|
|
1856
|
+
}
|
|
1857
|
+
var IndexLockContentionError = class extends Error {
|
|
1858
|
+
constructor(lockPath, owner, reason) {
|
|
1859
|
+
const ownerDescription = owner ? `PID ${owner.pid} on ${owner.hostname}, operation ${owner.operation}, since ${owner.startedAt}` : "an unreadable owner";
|
|
1860
|
+
super(`Index mutation already in progress: ${ownerDescription}`);
|
|
1861
|
+
this.lockPath = lockPath;
|
|
1862
|
+
this.owner = owner;
|
|
1863
|
+
this.reason = reason;
|
|
1864
|
+
this.name = "IndexLockContentionError";
|
|
1865
|
+
}
|
|
1866
|
+
lockPath;
|
|
1867
|
+
owner;
|
|
1868
|
+
reason;
|
|
1869
|
+
code = "INDEX_BUSY";
|
|
1870
|
+
};
|
|
1871
|
+
function isIndexLockContentionError(error) {
|
|
1872
|
+
return error instanceof IndexLockContentionError || typeof error === "object" && error !== null && "code" in error && error.code === "INDEX_BUSY";
|
|
1873
|
+
}
|
|
1874
|
+
function isTransientIndexLockContention(error) {
|
|
1875
|
+
if (!isIndexLockContentionError(error) || !("reason" in error)) return false;
|
|
1876
|
+
return error.reason === "active" || error.reason === "reclaiming";
|
|
1877
|
+
}
|
|
1878
|
+
function acquireIndexLock(indexPath, operation) {
|
|
1879
|
+
mkdirSync2(indexPath, { recursive: true });
|
|
1880
|
+
const canonicalIndexPath = realpathSync.native(indexPath);
|
|
1881
|
+
const lockPath = path7.join(canonicalIndexPath, "indexing.lock");
|
|
1882
|
+
cleanupDeadPublicationCandidates(canonicalIndexPath);
|
|
1883
|
+
for (let attempt = 0; attempt < 6; attempt += 1) {
|
|
1884
|
+
const owner = createOwner(operation);
|
|
1885
|
+
if (publishJsonDirectory(lockPath, owner)) {
|
|
1886
|
+
const lease = {
|
|
1887
|
+
canonicalIndexPath,
|
|
1888
|
+
lockPath,
|
|
1889
|
+
owner,
|
|
1890
|
+
recoveries: []
|
|
1891
|
+
};
|
|
1892
|
+
try {
|
|
1893
|
+
lease.recoveries = getPendingRecoveries(canonicalIndexPath);
|
|
1894
|
+
return lease;
|
|
1895
|
+
} catch (error) {
|
|
1896
|
+
releaseIndexLock(lease);
|
|
1897
|
+
throw error;
|
|
1898
|
+
}
|
|
1899
|
+
}
|
|
1900
|
+
let stats;
|
|
1901
|
+
try {
|
|
1902
|
+
stats = lstatSync(lockPath);
|
|
1903
|
+
} catch (error) {
|
|
1904
|
+
if (getErrorCode(error) === "ENOENT") continue;
|
|
1905
|
+
throw error;
|
|
1906
|
+
}
|
|
1907
|
+
if (!stats.isDirectory()) {
|
|
1908
|
+
const legacyOwner = readLegacyOwner(lockPath);
|
|
1909
|
+
throw new IndexLockContentionError(lockPath, legacyOwner, "legacy-lock");
|
|
1910
|
+
}
|
|
1911
|
+
const existingOwner = readDirectoryOwner(lockPath);
|
|
1912
|
+
if (!existingOwner) {
|
|
1913
|
+
throw new IndexLockContentionError(lockPath, null, "unknown-owner");
|
|
1914
|
+
}
|
|
1915
|
+
const liveness = getOwnerLiveness(existingOwner);
|
|
1916
|
+
if (liveness !== "dead") {
|
|
1917
|
+
throw new IndexLockContentionError(lockPath, existingOwner, liveness === "alive" ? "active" : "unknown-owner");
|
|
1918
|
+
}
|
|
1919
|
+
if (!reclaimDeadOwner(canonicalIndexPath, lockPath, existingOwner)) {
|
|
1920
|
+
throw new IndexLockContentionError(lockPath, existingOwner, "reclaiming");
|
|
1921
|
+
}
|
|
1922
|
+
}
|
|
1923
|
+
throw new IndexLockContentionError(lockPath, null, "reclaiming");
|
|
1924
|
+
}
|
|
1925
|
+
function releaseIndexLock(lease) {
|
|
1926
|
+
const currentOwner = readDirectoryOwner(lease.lockPath);
|
|
1927
|
+
if (!currentOwner || !sameOwner(currentOwner, lease.owner)) return false;
|
|
1928
|
+
const releasePath = `${lease.lockPath}.release.${lease.owner.pid}.${lease.owner.token}`;
|
|
1929
|
+
try {
|
|
1930
|
+
retryTransientFilesystemOperation(() => renameSync(lease.lockPath, releasePath));
|
|
1931
|
+
} catch (error) {
|
|
1932
|
+
if (getErrorCode(error) === "ENOENT") return false;
|
|
1933
|
+
throw error;
|
|
1934
|
+
}
|
|
1935
|
+
const claimedOwner = readDirectoryOwner(releasePath);
|
|
1936
|
+
if (!claimedOwner || !sameOwner(claimedOwner, lease.owner)) {
|
|
1937
|
+
if (!existsSync5(lease.lockPath) && existsSync5(releasePath)) {
|
|
1938
|
+
renameSync(releasePath, lease.lockPath);
|
|
1939
|
+
}
|
|
1940
|
+
return false;
|
|
1941
|
+
}
|
|
1942
|
+
try {
|
|
1943
|
+
retryTransientFilesystemOperation(() => rmSync(releasePath, { recursive: true, force: true }));
|
|
1944
|
+
} catch (error) {
|
|
1945
|
+
console.error(`[codebase-index] Lease released but tombstone cleanup failed: ${releasePath}`, error);
|
|
1946
|
+
}
|
|
1947
|
+
return true;
|
|
1948
|
+
}
|
|
1949
|
+
async function withIndexLock(indexPath, operation, callback, options = {}) {
|
|
1950
|
+
const lease = acquireIndexLock(indexPath, operation);
|
|
1951
|
+
let result;
|
|
1952
|
+
let callbackError;
|
|
1953
|
+
let callbackFailed = false;
|
|
1954
|
+
try {
|
|
1955
|
+
result = await callback(lease);
|
|
1956
|
+
} catch (error) {
|
|
1957
|
+
callbackFailed = true;
|
|
1958
|
+
callbackError = error;
|
|
1959
|
+
}
|
|
1960
|
+
if (!callbackFailed && options.completeRecoveries !== false) {
|
|
1961
|
+
try {
|
|
1962
|
+
completeLeaseRecovery(lease);
|
|
1963
|
+
} catch (error) {
|
|
1964
|
+
callbackFailed = true;
|
|
1965
|
+
callbackError = error;
|
|
1966
|
+
}
|
|
1967
|
+
}
|
|
1968
|
+
let releaseError;
|
|
1969
|
+
try {
|
|
1970
|
+
if (!releaseIndexLock(lease)) {
|
|
1971
|
+
releaseError = new Error(`Lost ownership of index mutation lease ${lease.owner.token}`);
|
|
1972
|
+
}
|
|
1973
|
+
} catch (error) {
|
|
1974
|
+
releaseError = error;
|
|
1975
|
+
}
|
|
1976
|
+
if (releaseError !== void 0) {
|
|
1977
|
+
if (callbackFailed) throw new AggregateError([callbackError, releaseError], "Index mutation and lease release both failed");
|
|
1978
|
+
throw releaseError;
|
|
1979
|
+
}
|
|
1980
|
+
if (callbackFailed) throw callbackError;
|
|
1981
|
+
return result;
|
|
1982
|
+
}
|
|
1983
|
+
function createLeaseTemporaryPath(targetPath, owner, kind = "tmp") {
|
|
1984
|
+
if (kind === "bak") return `${targetPath}.bak.${owner.pid}.${owner.token}`;
|
|
1985
|
+
temporaryCounter += 1;
|
|
1986
|
+
return `${targetPath}.tmp.${owner.pid}.${owner.token}.${temporaryCounter}`;
|
|
1987
|
+
}
|
|
1988
|
+
function removeLeaseTemporaryPath(temporaryPath) {
|
|
1989
|
+
if (existsSync5(temporaryPath)) rmSync(temporaryPath, { recursive: true, force: true });
|
|
1990
|
+
}
|
|
1991
|
+
function recoverLeaseArtifacts(indexPath, owner, backupTargets) {
|
|
1992
|
+
for (const targetPath of backupTargets) {
|
|
1993
|
+
const backupPath = createLeaseTemporaryPath(targetPath, owner, "bak");
|
|
1994
|
+
if (!existsSync5(backupPath)) continue;
|
|
1995
|
+
if (existsSync5(targetPath)) rmSync(targetPath, { recursive: true, force: true });
|
|
1996
|
+
renameSync(backupPath, targetPath);
|
|
1997
|
+
}
|
|
1998
|
+
const temporaryOwnerMarker = `.tmp.${owner.pid}.${owner.token}.`;
|
|
1999
|
+
for (const entry of readdirSync2(indexPath)) {
|
|
2000
|
+
if (!entry.includes(temporaryOwnerMarker)) continue;
|
|
2001
|
+
rmSync(path7.join(indexPath, entry), { recursive: true, force: true });
|
|
2002
|
+
}
|
|
2003
|
+
}
|
|
2004
|
+
function completeLeaseRecovery(lease) {
|
|
2005
|
+
for (const recovery of lease.recoveries) {
|
|
2006
|
+
const markerOwner = readRecoveryOwner(recovery.markerPath);
|
|
2007
|
+
if (!markerOwner || !sameOwner(markerOwner, recovery.owner)) {
|
|
2008
|
+
throw new Error(`Recovery marker ownership changed: ${recovery.markerPath}`);
|
|
2009
|
+
}
|
|
2010
|
+
}
|
|
2011
|
+
for (const recovery of lease.recoveries) {
|
|
2012
|
+
rmSync(recovery.markerPath, { recursive: true, force: true });
|
|
2013
|
+
}
|
|
2014
|
+
}
|
|
2015
|
+
|
|
1584
2016
|
// src/tools/operations.ts
|
|
1585
|
-
import { existsSync as
|
|
1586
|
-
import * as
|
|
2017
|
+
import { existsSync as existsSync10, realpathSync as realpathSync2, statSync as statSync4 } from "fs";
|
|
2018
|
+
import * as path16 from "path";
|
|
1587
2019
|
|
|
1588
2020
|
// src/indexer/index.ts
|
|
1589
|
-
import { existsSync as
|
|
1590
|
-
import * as
|
|
2021
|
+
import { existsSync as existsSync8, readFileSync as readFileSync7, statSync as statSync3, writeFileSync as writeFileSync3, renameSync as renameSync2, unlinkSync, mkdirSync as mkdirSync3, promises as fsPromises2 } from "fs";
|
|
2022
|
+
import * as path13 from "path";
|
|
1591
2023
|
import { performance as performance2 } from "perf_hooks";
|
|
1592
2024
|
import { execFile as execFile3 } from "child_process";
|
|
1593
2025
|
import { promisify as promisify3 } from "util";
|
|
@@ -2637,17 +3069,17 @@ async function pRetry(input, options = {}) {
|
|
|
2637
3069
|
}
|
|
2638
3070
|
|
|
2639
3071
|
// src/embeddings/detector.ts
|
|
2640
|
-
import { existsSync as
|
|
2641
|
-
import * as
|
|
2642
|
-
import * as
|
|
3072
|
+
import { existsSync as existsSync6, readFileSync as readFileSync5 } from "fs";
|
|
3073
|
+
import * as path8 from "path";
|
|
3074
|
+
import * as os3 from "os";
|
|
2643
3075
|
function getOpenCodeAuthPath() {
|
|
2644
|
-
return
|
|
3076
|
+
return path8.join(os3.homedir(), ".local", "share", "opencode", "auth.json");
|
|
2645
3077
|
}
|
|
2646
3078
|
function loadOpenCodeAuth() {
|
|
2647
3079
|
const authPath = getOpenCodeAuthPath();
|
|
2648
3080
|
try {
|
|
2649
|
-
if (
|
|
2650
|
-
return JSON.parse(
|
|
3081
|
+
if (existsSync6(authPath)) {
|
|
3082
|
+
return JSON.parse(readFileSync5(authPath, "utf-8"));
|
|
2651
3083
|
}
|
|
2652
3084
|
} catch {
|
|
2653
3085
|
}
|
|
@@ -2869,17 +3301,17 @@ function validateExternalUrl(urlString) {
|
|
|
2869
3301
|
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
2870
3302
|
return { valid: false, reason: `Blocked protocol: ${parsed.protocol}` };
|
|
2871
3303
|
}
|
|
2872
|
-
const
|
|
2873
|
-
if (BLOCKED_HOSTNAMES.has(
|
|
2874
|
-
return { valid: false, reason: `Blocked: cloud metadata service (${
|
|
3304
|
+
const hostname2 = parsed.hostname.toLowerCase();
|
|
3305
|
+
if (BLOCKED_HOSTNAMES.has(hostname2)) {
|
|
3306
|
+
return { valid: false, reason: `Blocked: cloud metadata service (${hostname2})` };
|
|
2875
3307
|
}
|
|
2876
3308
|
for (const pattern of BLOCKED_METADATA_IPS) {
|
|
2877
|
-
if (pattern.test(
|
|
2878
|
-
return { valid: false, reason: `Blocked: cloud metadata IP (${
|
|
3309
|
+
if (pattern.test(hostname2)) {
|
|
3310
|
+
return { valid: false, reason: `Blocked: cloud metadata IP (${hostname2})` };
|
|
2879
3311
|
}
|
|
2880
3312
|
}
|
|
2881
|
-
if (/^169\.254\./.test(
|
|
2882
|
-
return { valid: false, reason: `Blocked: link-local address (${
|
|
3313
|
+
if (/^169\.254\./.test(hostname2)) {
|
|
3314
|
+
return { valid: false, reason: `Blocked: link-local address (${hostname2})` };
|
|
2883
3315
|
}
|
|
2884
3316
|
return { valid: true };
|
|
2885
3317
|
}
|
|
@@ -3350,8 +3782,8 @@ var SiliconFlowReranker = class {
|
|
|
3350
3782
|
|
|
3351
3783
|
// src/utils/files.ts
|
|
3352
3784
|
var import_ignore = __toESM(require_ignore(), 1);
|
|
3353
|
-
import { existsSync as
|
|
3354
|
-
import * as
|
|
3785
|
+
import { existsSync as existsSync7, readFileSync as readFileSync6, promises as fsPromises } from "fs";
|
|
3786
|
+
import * as path9 from "path";
|
|
3355
3787
|
var PROJECT_MARKERS = [
|
|
3356
3788
|
".git",
|
|
3357
3789
|
"package.json",
|
|
@@ -3371,7 +3803,7 @@ var PROJECT_MARKERS = [
|
|
|
3371
3803
|
];
|
|
3372
3804
|
function hasProjectMarker(projectRoot) {
|
|
3373
3805
|
for (const marker of PROJECT_MARKERS) {
|
|
3374
|
-
if (
|
|
3806
|
+
if (existsSync7(path9.join(projectRoot, marker))) {
|
|
3375
3807
|
return true;
|
|
3376
3808
|
}
|
|
3377
3809
|
}
|
|
@@ -3398,16 +3830,16 @@ function createIgnoreFilter(projectRoot) {
|
|
|
3398
3830
|
"**/*build*/**"
|
|
3399
3831
|
];
|
|
3400
3832
|
ig.add(defaultIgnores);
|
|
3401
|
-
const gitignorePath =
|
|
3402
|
-
if (
|
|
3403
|
-
const gitignoreContent =
|
|
3833
|
+
const gitignorePath = path9.join(projectRoot, ".gitignore");
|
|
3834
|
+
if (existsSync7(gitignorePath)) {
|
|
3835
|
+
const gitignoreContent = readFileSync6(gitignorePath, "utf-8");
|
|
3404
3836
|
ig.add(gitignoreContent);
|
|
3405
3837
|
}
|
|
3406
3838
|
return ig;
|
|
3407
3839
|
}
|
|
3408
3840
|
function shouldIncludeFile(filePath, projectRoot, includePatterns, excludePatterns, ignoreFilter) {
|
|
3409
|
-
const relativePath =
|
|
3410
|
-
if (hasFilteredPathSegment(relativePath,
|
|
3841
|
+
const relativePath = path9.relative(projectRoot, filePath);
|
|
3842
|
+
if (hasFilteredPathSegment(relativePath, path9.sep)) {
|
|
3411
3843
|
return false;
|
|
3412
3844
|
}
|
|
3413
3845
|
if (ignoreFilter.ignores(relativePath)) {
|
|
@@ -3445,8 +3877,8 @@ async function* walkDirectory(dir, projectRoot, includePatterns, excludePatterns
|
|
|
3445
3877
|
const filesInDir = [];
|
|
3446
3878
|
const subdirs = [];
|
|
3447
3879
|
for (const entry of entries) {
|
|
3448
|
-
const fullPath =
|
|
3449
|
-
const relativePath =
|
|
3880
|
+
const fullPath = path9.join(dir, entry.name);
|
|
3881
|
+
const relativePath = path9.relative(projectRoot, fullPath);
|
|
3450
3882
|
if (isHiddenPathSegment(entry.name)) {
|
|
3451
3883
|
if (entry.isDirectory()) {
|
|
3452
3884
|
skipped.push({ path: relativePath, reason: "excluded" });
|
|
@@ -3495,7 +3927,7 @@ async function* walkDirectory(dir, projectRoot, includePatterns, excludePatterns
|
|
|
3495
3927
|
yield f;
|
|
3496
3928
|
}
|
|
3497
3929
|
for (let i = options.maxFilesPerDirectory; i < filesInDir.length; i++) {
|
|
3498
|
-
skipped.push({ path:
|
|
3930
|
+
skipped.push({ path: path9.relative(projectRoot, filesInDir[i].path), reason: "excluded" });
|
|
3499
3931
|
}
|
|
3500
3932
|
const canRecurse = options.maxDepth === -1 || currentDepth < options.maxDepth;
|
|
3501
3933
|
if (canRecurse) {
|
|
@@ -3535,8 +3967,8 @@ async function collectFiles(projectRoot, includePatterns, excludePatterns, maxFi
|
|
|
3535
3967
|
if (additionalRoots && additionalRoots.length > 0) {
|
|
3536
3968
|
const normalizedRoots = /* @__PURE__ */ new Set();
|
|
3537
3969
|
for (const kbRoot of additionalRoots) {
|
|
3538
|
-
const resolved =
|
|
3539
|
-
|
|
3970
|
+
const resolved = path9.normalize(
|
|
3971
|
+
path9.isAbsolute(kbRoot) ? kbRoot : path9.resolve(projectRoot, kbRoot)
|
|
3540
3972
|
);
|
|
3541
3973
|
normalizedRoots.add(resolved);
|
|
3542
3974
|
}
|
|
@@ -3943,13 +4375,13 @@ function initializeLogger(config) {
|
|
|
3943
4375
|
}
|
|
3944
4376
|
|
|
3945
4377
|
// src/native/index.ts
|
|
3946
|
-
import * as
|
|
3947
|
-
import * as
|
|
4378
|
+
import * as path10 from "path";
|
|
4379
|
+
import * as os4 from "os";
|
|
3948
4380
|
import * as module from "module";
|
|
3949
4381
|
import { fileURLToPath } from "url";
|
|
3950
4382
|
function getNativeBinding() {
|
|
3951
|
-
const platform2 =
|
|
3952
|
-
const arch2 =
|
|
4383
|
+
const platform2 = os4.platform();
|
|
4384
|
+
const arch2 = os4.arch();
|
|
3953
4385
|
let bindingName;
|
|
3954
4386
|
if (platform2 === "darwin" && arch2 === "arm64") {
|
|
3955
4387
|
bindingName = "codebase-index-native.darwin-arm64.node";
|
|
@@ -3967,19 +4399,19 @@ function getNativeBinding() {
|
|
|
3967
4399
|
let currentDir;
|
|
3968
4400
|
let requireTarget;
|
|
3969
4401
|
if (typeof import.meta !== "undefined" && import.meta.url) {
|
|
3970
|
-
currentDir =
|
|
4402
|
+
currentDir = path10.dirname(fileURLToPath(import.meta.url));
|
|
3971
4403
|
requireTarget = import.meta.url;
|
|
3972
4404
|
} else if (typeof __dirname !== "undefined") {
|
|
3973
4405
|
currentDir = __dirname;
|
|
3974
4406
|
requireTarget = __filename;
|
|
3975
4407
|
} else {
|
|
3976
4408
|
currentDir = process.cwd();
|
|
3977
|
-
requireTarget =
|
|
4409
|
+
requireTarget = path10.join(currentDir, "index.js");
|
|
3978
4410
|
}
|
|
3979
4411
|
const normalizedDir = currentDir.replace(/\\/g, "/");
|
|
3980
|
-
const isDevMode = normalizedDir.includes("/src/native") || currentDir.includes(
|
|
3981
|
-
const packageRoot = isDevMode ?
|
|
3982
|
-
const nativePath =
|
|
4412
|
+
const isDevMode = normalizedDir.includes("/src/native") || currentDir.includes(path10.join("src", "native"));
|
|
4413
|
+
const packageRoot = isDevMode ? path10.resolve(currentDir, "../..") : path10.resolve(currentDir, "..");
|
|
4414
|
+
const nativePath = path10.join(packageRoot, "native", bindingName);
|
|
3983
4415
|
const require2 = module.createRequire(requireTarget);
|
|
3984
4416
|
return require2(nativePath);
|
|
3985
4417
|
}
|
|
@@ -4021,6 +4453,12 @@ function createMockNativeBinding() {
|
|
|
4021
4453
|
constructor() {
|
|
4022
4454
|
throw error;
|
|
4023
4455
|
}
|
|
4456
|
+
static openReadOnly() {
|
|
4457
|
+
throw error;
|
|
4458
|
+
}
|
|
4459
|
+
static createEmptyReadOnly() {
|
|
4460
|
+
throw error;
|
|
4461
|
+
}
|
|
4024
4462
|
close() {
|
|
4025
4463
|
throw error;
|
|
4026
4464
|
}
|
|
@@ -4124,6 +4562,12 @@ var VectorStore = class {
|
|
|
4124
4562
|
load() {
|
|
4125
4563
|
this.inner.load();
|
|
4126
4564
|
}
|
|
4565
|
+
loadStrict() {
|
|
4566
|
+
this.inner.loadStrict();
|
|
4567
|
+
}
|
|
4568
|
+
hasFingerprint() {
|
|
4569
|
+
return this.inner.hasFingerprint();
|
|
4570
|
+
}
|
|
4127
4571
|
count() {
|
|
4128
4572
|
return this.inner.count();
|
|
4129
4573
|
}
|
|
@@ -4406,12 +4850,24 @@ var InvertedIndex = class {
|
|
|
4406
4850
|
return this.inner.documentCount();
|
|
4407
4851
|
}
|
|
4408
4852
|
};
|
|
4409
|
-
var Database = class {
|
|
4853
|
+
var Database = class _Database {
|
|
4410
4854
|
inner;
|
|
4411
4855
|
closed = false;
|
|
4412
4856
|
constructor(dbPath) {
|
|
4413
4857
|
this.inner = new native.Database(dbPath);
|
|
4414
4858
|
}
|
|
4859
|
+
static fromNative(inner) {
|
|
4860
|
+
const database = Object.create(_Database.prototype);
|
|
4861
|
+
database.inner = inner;
|
|
4862
|
+
database.closed = false;
|
|
4863
|
+
return database;
|
|
4864
|
+
}
|
|
4865
|
+
static openReadOnly(dbPath) {
|
|
4866
|
+
return _Database.fromNative(native.Database.openReadOnly(dbPath));
|
|
4867
|
+
}
|
|
4868
|
+
static createEmptyReadOnly() {
|
|
4869
|
+
return _Database.fromNative(native.Database.createEmptyReadOnly());
|
|
4870
|
+
}
|
|
4415
4871
|
throwIfClosed() {
|
|
4416
4872
|
if (this.closed) {
|
|
4417
4873
|
throw new Error("Database is closed");
|
|
@@ -4688,7 +5144,7 @@ var Database = class {
|
|
|
4688
5144
|
};
|
|
4689
5145
|
|
|
4690
5146
|
// src/tools/changed-files.ts
|
|
4691
|
-
import * as
|
|
5147
|
+
import * as path11 from "path";
|
|
4692
5148
|
import { execFile } from "child_process";
|
|
4693
5149
|
import { promisify } from "util";
|
|
4694
5150
|
var execFileAsync = promisify(execFile);
|
|
@@ -4776,8 +5232,8 @@ function normalizeFiles(rawFiles, projectRoot) {
|
|
|
4776
5232
|
for (const raw of rawFiles) {
|
|
4777
5233
|
const trimmed = raw.trim();
|
|
4778
5234
|
if (!trimmed) continue;
|
|
4779
|
-
const absolute =
|
|
4780
|
-
const relative11 =
|
|
5235
|
+
const absolute = path11.resolve(projectRoot, trimmed);
|
|
5236
|
+
const relative11 = path11.relative(projectRoot, absolute);
|
|
4781
5237
|
const cleaned = relative11.startsWith("./") ? relative11.slice(2) : relative11;
|
|
4782
5238
|
if (!seen.has(cleaned)) {
|
|
4783
5239
|
seen.add(cleaned);
|
|
@@ -4789,7 +5245,7 @@ function normalizeFiles(rawFiles, projectRoot) {
|
|
|
4789
5245
|
|
|
4790
5246
|
// src/indexer/git-blame.ts
|
|
4791
5247
|
import { execFile as execFile2 } from "child_process";
|
|
4792
|
-
import * as
|
|
5248
|
+
import * as path12 from "path";
|
|
4793
5249
|
import { promisify as promisify2 } from "util";
|
|
4794
5250
|
var execFileAsync2 = promisify2(execFile2);
|
|
4795
5251
|
function parseGitBlamePorcelain(output) {
|
|
@@ -4827,7 +5283,7 @@ function parseGitBlamePorcelain(output) {
|
|
|
4827
5283
|
return Array.from(commits.values()).filter((commit) => commit.lines > 0).sort((a, b) => b.lines - a.lines || b.committedAt - a.committedAt)[0];
|
|
4828
5284
|
}
|
|
4829
5285
|
async function getChunkGitBlame(projectRoot, filePath, startLine, endLine) {
|
|
4830
|
-
const relativePath =
|
|
5286
|
+
const relativePath = path12.relative(projectRoot, filePath);
|
|
4831
5287
|
try {
|
|
4832
5288
|
const { stdout } = await execFileAsync2(
|
|
4833
5289
|
"git",
|
|
@@ -4921,6 +5377,7 @@ function isSqliteCorruptionError(error) {
|
|
|
4921
5377
|
return message.includes("database disk image is malformed") || message.includes("file is not a database") || message.includes("database schema is corrupt") || message.includes("sqlite_corrupt");
|
|
4922
5378
|
}
|
|
4923
5379
|
var STARTUP_WARNING_METADATA_KEY = "index.startupWarning";
|
|
5380
|
+
var READER_ARTIFACT_RETRY_INTERVAL_MS = 1e3;
|
|
4924
5381
|
function metadataFromBlame(blame) {
|
|
4925
5382
|
if (!blame) {
|
|
4926
5383
|
return {};
|
|
@@ -5118,9 +5575,9 @@ function hasAllEmbeddingParts(parts, expectedPartCount) {
|
|
|
5118
5575
|
return true;
|
|
5119
5576
|
}
|
|
5120
5577
|
function isPathWithinRoot(filePath, rootPath) {
|
|
5121
|
-
const normalizedFilePath =
|
|
5122
|
-
const normalizedRoot =
|
|
5123
|
-
return normalizedFilePath === normalizedRoot || normalizedFilePath.startsWith(`${normalizedRoot}${
|
|
5578
|
+
const normalizedFilePath = path13.resolve(filePath);
|
|
5579
|
+
const normalizedRoot = path13.resolve(rootPath);
|
|
5580
|
+
return normalizedFilePath === normalizedRoot || normalizedFilePath.startsWith(`${normalizedRoot}${path13.sep}`);
|
|
5124
5581
|
}
|
|
5125
5582
|
var rankingQueryTokenCache = /* @__PURE__ */ new Map();
|
|
5126
5583
|
var rankingNameTokenCache = /* @__PURE__ */ new Map();
|
|
@@ -6186,26 +6643,133 @@ var Indexer = class {
|
|
|
6186
6643
|
queryCacheTtlMs = 5 * 60 * 1e3;
|
|
6187
6644
|
querySimilarityThreshold = 0.85;
|
|
6188
6645
|
indexCompatibility = null;
|
|
6189
|
-
|
|
6646
|
+
activeIndexLease = null;
|
|
6647
|
+
initializationPromise = null;
|
|
6648
|
+
initializationMode = "none";
|
|
6649
|
+
readIssues = [];
|
|
6650
|
+
retiredDatabases = [];
|
|
6651
|
+
readerArtifactFingerprint = null;
|
|
6652
|
+
writerArtifactFingerprint = null;
|
|
6653
|
+
readerArtifactRetryAfter = /* @__PURE__ */ new Map();
|
|
6190
6654
|
constructor(projectRoot, config, host = "opencode") {
|
|
6191
6655
|
this.projectRoot = projectRoot;
|
|
6192
6656
|
this.config = config;
|
|
6193
6657
|
this.host = host;
|
|
6194
6658
|
this.indexPath = this.getIndexPath();
|
|
6195
|
-
this.fileHashCachePath =
|
|
6196
|
-
this.failedBatchesPath =
|
|
6197
|
-
this.indexingLockPath = path12.join(this.indexPath, "indexing.lock");
|
|
6659
|
+
this.fileHashCachePath = path13.join(this.indexPath, "file-hashes.json");
|
|
6660
|
+
this.failedBatchesPath = path13.join(this.indexPath, "failed-batches.json");
|
|
6198
6661
|
this.logger = initializeLogger(config.debug);
|
|
6199
6662
|
}
|
|
6200
6663
|
getIndexPath() {
|
|
6201
6664
|
return resolveProjectIndexPath(this.projectRoot, this.config.scope, this.host);
|
|
6202
6665
|
}
|
|
6666
|
+
isLocalProjectIndexPath() {
|
|
6667
|
+
const localProjectIndexPaths = [path13.join(this.projectRoot, getHostProjectIndexRelativePath(this.host))];
|
|
6668
|
+
if (this.host !== "opencode") {
|
|
6669
|
+
localProjectIndexPaths.push(path13.join(this.projectRoot, getHostProjectIndexRelativePath("opencode")));
|
|
6670
|
+
}
|
|
6671
|
+
return localProjectIndexPaths.some((localPath) => {
|
|
6672
|
+
if (!existsSync8(localPath) || !existsSync8(this.indexPath)) {
|
|
6673
|
+
return path13.resolve(this.indexPath) === path13.resolve(localPath);
|
|
6674
|
+
}
|
|
6675
|
+
const indexStats = statSync3(this.indexPath);
|
|
6676
|
+
const localStats = statSync3(localPath);
|
|
6677
|
+
return indexStats.dev === localStats.dev && indexStats.ino === localStats.ino;
|
|
6678
|
+
});
|
|
6679
|
+
}
|
|
6680
|
+
resetLoadedIndexState(retireDatabase = false) {
|
|
6681
|
+
if (this.database) {
|
|
6682
|
+
if (retireDatabase) {
|
|
6683
|
+
this.retiredDatabases.push(this.database);
|
|
6684
|
+
} else {
|
|
6685
|
+
this.database.close();
|
|
6686
|
+
}
|
|
6687
|
+
}
|
|
6688
|
+
this.store = null;
|
|
6689
|
+
this.invertedIndex = null;
|
|
6690
|
+
this.database = null;
|
|
6691
|
+
this.provider = null;
|
|
6692
|
+
this.configuredProviderInfo = null;
|
|
6693
|
+
this.reranker = null;
|
|
6694
|
+
this.indexCompatibility = null;
|
|
6695
|
+
this.initializationMode = "none";
|
|
6696
|
+
this.readIssues = [];
|
|
6697
|
+
this.readerArtifactFingerprint = null;
|
|
6698
|
+
this.writerArtifactFingerprint = null;
|
|
6699
|
+
this.readerArtifactRetryAfter.clear();
|
|
6700
|
+
this.fileHashCache.clear();
|
|
6701
|
+
}
|
|
6702
|
+
refreshLoadedIndexState() {
|
|
6703
|
+
if (!this.store || !this.invertedIndex || !this.configuredProviderInfo) return;
|
|
6704
|
+
this.store.load();
|
|
6705
|
+
this.invertedIndex.load();
|
|
6706
|
+
this.fileHashCache.clear();
|
|
6707
|
+
this.loadFileHashCache();
|
|
6708
|
+
this.indexCompatibility = this.validateIndexCompatibility(this.configuredProviderInfo);
|
|
6709
|
+
this.readIssues = [];
|
|
6710
|
+
this.readerArtifactRetryAfter.clear();
|
|
6711
|
+
}
|
|
6712
|
+
async withIndexMutationLease(operation, callback) {
|
|
6713
|
+
const lease = acquireIndexLock(this.indexPath, operation);
|
|
6714
|
+
this.indexPath = lease.canonicalIndexPath;
|
|
6715
|
+
this.fileHashCachePath = path13.join(this.indexPath, "file-hashes.json");
|
|
6716
|
+
this.failedBatchesPath = path13.join(this.indexPath, "failed-batches.json");
|
|
6717
|
+
this.activeIndexLease = lease;
|
|
6718
|
+
let result;
|
|
6719
|
+
let callbackError;
|
|
6720
|
+
let callbackFailed = false;
|
|
6721
|
+
try {
|
|
6722
|
+
result = await callback(lease.recoveries.map(({ owner }) => owner));
|
|
6723
|
+
} catch (error) {
|
|
6724
|
+
callbackFailed = true;
|
|
6725
|
+
callbackError = error;
|
|
6726
|
+
}
|
|
6727
|
+
if (!callbackFailed) {
|
|
6728
|
+
try {
|
|
6729
|
+
completeLeaseRecovery(lease);
|
|
6730
|
+
this.writerArtifactFingerprint = this.captureReaderArtifactFingerprint();
|
|
6731
|
+
} catch (error) {
|
|
6732
|
+
callbackFailed = true;
|
|
6733
|
+
callbackError = error;
|
|
6734
|
+
}
|
|
6735
|
+
}
|
|
6736
|
+
let releaseError;
|
|
6737
|
+
try {
|
|
6738
|
+
if (!releaseIndexLock(lease)) {
|
|
6739
|
+
releaseError = new Error(`Lost ownership of index mutation lease ${lease.owner.token}`);
|
|
6740
|
+
this.writerArtifactFingerprint = null;
|
|
6741
|
+
if (this.activeIndexLease?.owner.token === lease.owner.token) {
|
|
6742
|
+
this.activeIndexLease = null;
|
|
6743
|
+
}
|
|
6744
|
+
} else if (this.activeIndexLease?.owner.token === lease.owner.token) {
|
|
6745
|
+
this.activeIndexLease = null;
|
|
6746
|
+
}
|
|
6747
|
+
} catch (error) {
|
|
6748
|
+
releaseError = error;
|
|
6749
|
+
this.writerArtifactFingerprint = null;
|
|
6750
|
+
if (!existsSync8(lease.lockPath) && this.activeIndexLease?.owner.token === lease.owner.token) {
|
|
6751
|
+
this.activeIndexLease = null;
|
|
6752
|
+
}
|
|
6753
|
+
}
|
|
6754
|
+
if (releaseError !== void 0) {
|
|
6755
|
+
if (callbackFailed) throw new AggregateError([callbackError, releaseError], "Index mutation and lease release both failed");
|
|
6756
|
+
throw releaseError;
|
|
6757
|
+
}
|
|
6758
|
+
if (callbackFailed) throw callbackError;
|
|
6759
|
+
return result;
|
|
6760
|
+
}
|
|
6761
|
+
requireActiveLease() {
|
|
6762
|
+
if (!this.activeIndexLease) {
|
|
6763
|
+
throw new Error("Index mutation attempted without an active interprocess lease");
|
|
6764
|
+
}
|
|
6765
|
+
return this.activeIndexLease;
|
|
6766
|
+
}
|
|
6203
6767
|
loadFileHashCache() {
|
|
6204
|
-
if (!
|
|
6768
|
+
if (!existsSync8(this.fileHashCachePath)) {
|
|
6205
6769
|
return;
|
|
6206
6770
|
}
|
|
6207
6771
|
try {
|
|
6208
|
-
const data =
|
|
6772
|
+
const data = readFileSync7(this.fileHashCachePath, "utf-8");
|
|
6209
6773
|
const parsed = JSON.parse(data);
|
|
6210
6774
|
this.fileHashCache = new Map(Object.entries(parsed));
|
|
6211
6775
|
} catch (error) {
|
|
@@ -6225,15 +6789,26 @@ var Indexer = class {
|
|
|
6225
6789
|
this.atomicWriteSync(this.fileHashCachePath, JSON.stringify(obj));
|
|
6226
6790
|
}
|
|
6227
6791
|
atomicWriteSync(targetPath, data) {
|
|
6228
|
-
const
|
|
6229
|
-
|
|
6230
|
-
|
|
6231
|
-
|
|
6792
|
+
const lease = this.requireActiveLease();
|
|
6793
|
+
const tempPath = createLeaseTemporaryPath(targetPath, lease.owner, "tmp");
|
|
6794
|
+
mkdirSync3(path13.dirname(targetPath), { recursive: true });
|
|
6795
|
+
try {
|
|
6796
|
+
writeFileSync3(tempPath, data);
|
|
6797
|
+
renameSync2(tempPath, targetPath);
|
|
6798
|
+
} finally {
|
|
6799
|
+
removeLeaseTemporaryPath(tempPath);
|
|
6800
|
+
}
|
|
6801
|
+
}
|
|
6802
|
+
saveInvertedIndex(invertedIndex) {
|
|
6803
|
+
this.atomicWriteSync(
|
|
6804
|
+
path13.join(this.indexPath, "inverted-index.json"),
|
|
6805
|
+
invertedIndex.serialize()
|
|
6806
|
+
);
|
|
6232
6807
|
}
|
|
6233
6808
|
getScopedRoots() {
|
|
6234
|
-
const roots = /* @__PURE__ */ new Set([
|
|
6809
|
+
const roots = /* @__PURE__ */ new Set([path13.resolve(this.projectRoot)]);
|
|
6235
6810
|
for (const kbRoot of this.config.knowledgeBases) {
|
|
6236
|
-
roots.add(
|
|
6811
|
+
roots.add(path13.resolve(this.projectRoot, kbRoot));
|
|
6237
6812
|
}
|
|
6238
6813
|
return Array.from(roots);
|
|
6239
6814
|
}
|
|
@@ -6242,29 +6817,29 @@ var Indexer = class {
|
|
|
6242
6817
|
if (this.config.scope !== "global") {
|
|
6243
6818
|
return branchName;
|
|
6244
6819
|
}
|
|
6245
|
-
const projectHash = hashContent(
|
|
6820
|
+
const projectHash = hashContent(path13.resolve(this.projectRoot)).slice(0, 16);
|
|
6246
6821
|
return `${projectHash}:${branchName}`;
|
|
6247
6822
|
}
|
|
6248
6823
|
getBranchCatalogKeyFor(branchName) {
|
|
6249
6824
|
if (this.config.scope !== "global") {
|
|
6250
6825
|
return branchName;
|
|
6251
6826
|
}
|
|
6252
|
-
const projectHash = hashContent(
|
|
6827
|
+
const projectHash = hashContent(path13.resolve(this.projectRoot)).slice(0, 16);
|
|
6253
6828
|
return `${projectHash}:${branchName}`;
|
|
6254
6829
|
}
|
|
6255
6830
|
getLegacyBranchCatalogKey() {
|
|
6256
6831
|
return this.currentBranch || "default";
|
|
6257
6832
|
}
|
|
6258
6833
|
getLegacyMigrationMetadataKey() {
|
|
6259
|
-
const projectHash = hashContent(
|
|
6834
|
+
const projectHash = hashContent(path13.resolve(this.projectRoot)).slice(0, 16);
|
|
6260
6835
|
return `index.globalBranchMigration.${projectHash}`;
|
|
6261
6836
|
}
|
|
6262
6837
|
getProjectEmbeddingStrategyMetadataKey() {
|
|
6263
|
-
const projectHash = hashContent(
|
|
6838
|
+
const projectHash = hashContent(path13.resolve(this.projectRoot)).slice(0, 16);
|
|
6264
6839
|
return `index.embeddingStrategyVersion.${projectHash}`;
|
|
6265
6840
|
}
|
|
6266
6841
|
getProjectForceReembedMetadataKey() {
|
|
6267
|
-
const projectHash = hashContent(
|
|
6842
|
+
const projectHash = hashContent(path13.resolve(this.projectRoot)).slice(0, 16);
|
|
6268
6843
|
return `index.forceReembed.${projectHash}`;
|
|
6269
6844
|
}
|
|
6270
6845
|
hasProjectForceReembedPending() {
|
|
@@ -6358,7 +6933,7 @@ var Indexer = class {
|
|
|
6358
6933
|
if (!this.database) {
|
|
6359
6934
|
return { chunkIds, symbolIds };
|
|
6360
6935
|
}
|
|
6361
|
-
const projectRootPath =
|
|
6936
|
+
const projectRootPath = path13.resolve(this.projectRoot);
|
|
6362
6937
|
const projectLocalFilePaths = /* @__PURE__ */ new Set([
|
|
6363
6938
|
...Array.from(this.fileHashCache.keys()).filter(
|
|
6364
6939
|
(filePath) => this.isFileInCurrentScope(filePath, roots) && isPathWithinRoot(filePath, projectRootPath)
|
|
@@ -6381,7 +6956,7 @@ var Indexer = class {
|
|
|
6381
6956
|
if (this.config.scope !== "global") {
|
|
6382
6957
|
return this.getBranchCatalogCleanupKeys();
|
|
6383
6958
|
}
|
|
6384
|
-
const projectHash = hashContent(
|
|
6959
|
+
const projectHash = hashContent(path13.resolve(this.projectRoot)).slice(0, 16);
|
|
6385
6960
|
const keys = /* @__PURE__ */ new Set();
|
|
6386
6961
|
const projectChunkIdSet = new Set(projectChunkIds);
|
|
6387
6962
|
const projectSymbolIdSet = new Set(projectSymbolIds);
|
|
@@ -6465,7 +7040,7 @@ var Indexer = class {
|
|
|
6465
7040
|
if (!this.database || this.config.scope !== "global") {
|
|
6466
7041
|
return false;
|
|
6467
7042
|
}
|
|
6468
|
-
const projectHash = hashContent(
|
|
7043
|
+
const projectHash = hashContent(path13.resolve(this.projectRoot)).slice(0, 16);
|
|
6469
7044
|
const roots = this.getScopedRoots();
|
|
6470
7045
|
const { chunkIds: projectLocalChunkIds, symbolIds: projectLocalSymbolIds } = this.getProjectLocalScopedOwnershipIds(roots);
|
|
6471
7046
|
return this.database.getAllBranches().some(
|
|
@@ -6499,7 +7074,7 @@ var Indexer = class {
|
|
|
6499
7074
|
...Array.from(this.fileHashCache.keys()).filter((filePath) => this.isFileInCurrentScope(filePath, roots)),
|
|
6500
7075
|
...scopedEntries.map(({ metadata }) => metadata.filePath)
|
|
6501
7076
|
]);
|
|
6502
|
-
const projectRootPath =
|
|
7077
|
+
const projectRootPath = path13.resolve(this.projectRoot);
|
|
6503
7078
|
const projectLocalFilePaths = new Set(
|
|
6504
7079
|
Array.from(filePaths).filter((filePath) => isPathWithinRoot(filePath, projectRootPath))
|
|
6505
7080
|
);
|
|
@@ -6561,34 +7136,27 @@ var Indexer = class {
|
|
|
6561
7136
|
database.gcOrphanEmbeddings();
|
|
6562
7137
|
database.gcOrphanChunks();
|
|
6563
7138
|
store.save();
|
|
6564
|
-
|
|
7139
|
+
this.saveInvertedIndex(invertedIndex);
|
|
6565
7140
|
return {
|
|
6566
7141
|
removedChunkIds: removedChunkIdList,
|
|
6567
7142
|
hasForeignData: allMetadata.some(({ metadata }) => !this.isFileInCurrentScope(metadata.filePath, roots))
|
|
6568
7143
|
};
|
|
6569
7144
|
}
|
|
6570
|
-
|
|
6571
|
-
|
|
6572
|
-
|
|
6573
|
-
|
|
6574
|
-
|
|
6575
|
-
|
|
6576
|
-
|
|
6577
|
-
|
|
6578
|
-
writeFileSync2(this.indexingLockPath, JSON.stringify(lockData));
|
|
6579
|
-
}
|
|
6580
|
-
releaseIndexingLock() {
|
|
6581
|
-
if (existsSync7(this.indexingLockPath)) {
|
|
6582
|
-
unlinkSync(this.indexingLockPath);
|
|
7145
|
+
async recoverFromInterruptedIndexingUnlocked(owners) {
|
|
7146
|
+
for (const owner of owners) {
|
|
7147
|
+
this.logger.warn("Detected interrupted indexing session, recovering...", {
|
|
7148
|
+
pid: owner.pid,
|
|
7149
|
+
hostname: owner.hostname,
|
|
7150
|
+
operation: owner.operation,
|
|
7151
|
+
startedAt: owner.startedAt
|
|
7152
|
+
});
|
|
6583
7153
|
}
|
|
6584
|
-
|
|
6585
|
-
|
|
6586
|
-
|
|
6587
|
-
|
|
6588
|
-
|
|
7154
|
+
if (this.config.scope === "global") {
|
|
7155
|
+
if (existsSync8(this.fileHashCachePath)) {
|
|
7156
|
+
unlinkSync(this.fileHashCachePath);
|
|
7157
|
+
}
|
|
7158
|
+
await this.healthCheckUnlocked();
|
|
6589
7159
|
}
|
|
6590
|
-
await this.healthCheck();
|
|
6591
|
-
this.releaseIndexingLock();
|
|
6592
7160
|
this.logger.info("Recovery complete, next index will re-process all files");
|
|
6593
7161
|
}
|
|
6594
7162
|
loadFailedBatches(maxChunkTokens) {
|
|
@@ -6604,10 +7172,10 @@ var Indexer = class {
|
|
|
6604
7172
|
}
|
|
6605
7173
|
}
|
|
6606
7174
|
loadSerializedFailedBatches() {
|
|
6607
|
-
if (!
|
|
7175
|
+
if (!existsSync8(this.failedBatchesPath)) {
|
|
6608
7176
|
return [];
|
|
6609
7177
|
}
|
|
6610
|
-
const data =
|
|
7178
|
+
const data = readFileSync7(this.failedBatchesPath, "utf-8");
|
|
6611
7179
|
const parsed = JSON.parse(data);
|
|
6612
7180
|
return parsed.map((batch) => {
|
|
6613
7181
|
const chunks = Array.isArray(batch.chunks) ? batch.chunks : [];
|
|
@@ -6624,7 +7192,7 @@ var Indexer = class {
|
|
|
6624
7192
|
}
|
|
6625
7193
|
saveFailedBatches(batches) {
|
|
6626
7194
|
if (batches.length === 0) {
|
|
6627
|
-
if (
|
|
7195
|
+
if (existsSync8(this.failedBatchesPath)) {
|
|
6628
7196
|
try {
|
|
6629
7197
|
unlinkSync(this.failedBatchesPath);
|
|
6630
7198
|
} catch {
|
|
@@ -6632,7 +7200,7 @@ var Indexer = class {
|
|
|
6632
7200
|
}
|
|
6633
7201
|
return;
|
|
6634
7202
|
}
|
|
6635
|
-
|
|
7203
|
+
this.atomicWriteSync(this.failedBatchesPath, JSON.stringify(batches, null, 2));
|
|
6636
7204
|
}
|
|
6637
7205
|
collectRetryableFailedChunks(currentFileHashes, unchangedFilePaths, maxChunkTokens) {
|
|
6638
7206
|
const retryableById = /* @__PURE__ */ new Map();
|
|
@@ -6826,6 +7394,222 @@ var Indexer = class {
|
|
|
6826
7394
|
return parts.join("\n");
|
|
6827
7395
|
}
|
|
6828
7396
|
async initialize() {
|
|
7397
|
+
if (this.initializationPromise) {
|
|
7398
|
+
await this.initializationPromise;
|
|
7399
|
+
}
|
|
7400
|
+
if (this.isInitializedFor("reader")) {
|
|
7401
|
+
return;
|
|
7402
|
+
}
|
|
7403
|
+
await this.initializeOnce("reader", [], { skipAutoGc: true });
|
|
7404
|
+
}
|
|
7405
|
+
async initializeOnce(mode, recoveredOwners, options) {
|
|
7406
|
+
if (this.initializationPromise) {
|
|
7407
|
+
await this.initializationPromise;
|
|
7408
|
+
if (this.isInitializedFor(mode)) {
|
|
7409
|
+
return;
|
|
7410
|
+
}
|
|
7411
|
+
return this.initializeOnce(mode, recoveredOwners, options);
|
|
7412
|
+
}
|
|
7413
|
+
if (this.isInitializedFor(mode)) {
|
|
7414
|
+
return;
|
|
7415
|
+
}
|
|
7416
|
+
const initialization = this.initializeUnlocked(mode, recoveredOwners, options).catch((error) => {
|
|
7417
|
+
this.resetLoadedIndexState();
|
|
7418
|
+
throw error;
|
|
7419
|
+
}).finally(() => {
|
|
7420
|
+
if (this.initializationPromise === initialization) {
|
|
7421
|
+
this.initializationPromise = null;
|
|
7422
|
+
}
|
|
7423
|
+
});
|
|
7424
|
+
this.initializationPromise = initialization;
|
|
7425
|
+
await initialization;
|
|
7426
|
+
}
|
|
7427
|
+
isInitializedFor(mode) {
|
|
7428
|
+
const hasState = Boolean(
|
|
7429
|
+
this.store && this.provider && this.invertedIndex && this.configuredProviderInfo && this.database
|
|
7430
|
+
);
|
|
7431
|
+
if (!hasState) {
|
|
7432
|
+
return false;
|
|
7433
|
+
}
|
|
7434
|
+
return mode === "reader" ? this.initializationMode !== "none" : this.initializationMode === "writer";
|
|
7435
|
+
}
|
|
7436
|
+
recordReadIssue(component, message, error) {
|
|
7437
|
+
this.readIssues.push(this.createReadIssue(component, message));
|
|
7438
|
+
this.readerArtifactRetryAfter.set(component, Date.now() + READER_ARTIFACT_RETRY_INTERVAL_MS);
|
|
7439
|
+
this.logger.warn(message, error === void 0 ? void 0 : { error: getErrorMessage2(error) });
|
|
7440
|
+
}
|
|
7441
|
+
createReadIssue(component, message) {
|
|
7442
|
+
return {
|
|
7443
|
+
component,
|
|
7444
|
+
message,
|
|
7445
|
+
blocking: component !== "keyword"
|
|
7446
|
+
};
|
|
7447
|
+
}
|
|
7448
|
+
getVectorReadIssueMessage() {
|
|
7449
|
+
if (this.config.scope === "global") {
|
|
7450
|
+
return "Shared vector index could not be read. Restore or repair the complete fingerprinted shared vector artifacts; automatic reset is disabled for global scope.";
|
|
7451
|
+
}
|
|
7452
|
+
if (!this.isLocalProjectIndexPath()) {
|
|
7453
|
+
return "Vector index could not be read from an inherited project index. Restore or fingerprint it from the checkout that owns the index; do not remove or rebuild it from this worktree.";
|
|
7454
|
+
}
|
|
7455
|
+
return "Vector index could not be read. Run index_codebase after the active writer finishes to fingerprint a structurally valid legacy pair, or remove this checkout's local index directory and run index_codebase to rebuild it.";
|
|
7456
|
+
}
|
|
7457
|
+
getKeywordReadIssueMessage() {
|
|
7458
|
+
if (this.config.scope === "global") {
|
|
7459
|
+
return "Shared keyword index could not be read; semantic search remains available. Restore or repair the shared keyword artifact; automatic reset is disabled for global scope.";
|
|
7460
|
+
}
|
|
7461
|
+
if (!this.isLocalProjectIndexPath()) {
|
|
7462
|
+
return "Keyword index could not be read from an inherited project index; semantic search remains available. Restore or repair it from the checkout that owns the index; do not rebuild it from this worktree.";
|
|
7463
|
+
}
|
|
7464
|
+
return "Keyword index could not be read; semantic search remains available. Restore a readable published keyword index, or run index_codebase with force=true after the active writer finishes.";
|
|
7465
|
+
}
|
|
7466
|
+
getDatabaseReadIssueMessage() {
|
|
7467
|
+
if (this.config.scope === "global") {
|
|
7468
|
+
return "Shared index database could not be read. Restore or repair the shared SQLite database; automatic reset is disabled for global scope.";
|
|
7469
|
+
}
|
|
7470
|
+
if (!this.isLocalProjectIndexPath()) {
|
|
7471
|
+
return "Index database could not be read from an inherited project index. Restore or repair it from the checkout that owns the index; do not migrate or rebuild it from this worktree.";
|
|
7472
|
+
}
|
|
7473
|
+
return "Index database could not be read. Run index_codebase after the active writer finishes to repair or migrate it under the writer lease.";
|
|
7474
|
+
}
|
|
7475
|
+
getReaderFileFingerprint(filePath, identityOnly = false) {
|
|
7476
|
+
try {
|
|
7477
|
+
const stats = statSync3(filePath);
|
|
7478
|
+
if (identityOnly) {
|
|
7479
|
+
return `${stats.dev}:${stats.ino}`;
|
|
7480
|
+
}
|
|
7481
|
+
return `${stats.dev}:${stats.ino}:${stats.size}:${stats.mtimeMs}:${stats.ctimeMs}`;
|
|
7482
|
+
} catch (error) {
|
|
7483
|
+
return `unavailable:${getErrorMessage2(error)}`;
|
|
7484
|
+
}
|
|
7485
|
+
}
|
|
7486
|
+
captureReaderArtifactFingerprint() {
|
|
7487
|
+
const storePath = path13.join(this.indexPath, "vectors");
|
|
7488
|
+
return {
|
|
7489
|
+
vectors: `${this.getReaderFileFingerprint(storePath)}|${this.getReaderFileFingerprint(`${storePath}.meta.json`)}`,
|
|
7490
|
+
keyword: this.getReaderFileFingerprint(path13.join(this.indexPath, "inverted-index.json")),
|
|
7491
|
+
database: this.getReaderFileFingerprint(path13.join(this.indexPath, "codebase.db")),
|
|
7492
|
+
databaseIdentity: this.getReaderFileFingerprint(path13.join(this.indexPath, "codebase.db"), true)
|
|
7493
|
+
};
|
|
7494
|
+
}
|
|
7495
|
+
refreshReaderArtifacts() {
|
|
7496
|
+
if (this.initializationMode !== "reader" || !this.configuredProviderInfo) {
|
|
7497
|
+
return;
|
|
7498
|
+
}
|
|
7499
|
+
const previousFingerprint = this.readerArtifactFingerprint;
|
|
7500
|
+
const currentFingerprint = this.captureReaderArtifactFingerprint();
|
|
7501
|
+
const issues = new Map(this.readIssues.map((issue) => [issue.component, issue]));
|
|
7502
|
+
const retryDue = (component) => issues.has(component) && Date.now() >= (this.readerArtifactRetryAfter.get(component) ?? 0);
|
|
7503
|
+
const vectorsChanged = !previousFingerprint || currentFingerprint.vectors !== previousFingerprint.vectors;
|
|
7504
|
+
const keywordChanged = !previousFingerprint || currentFingerprint.keyword !== previousFingerprint.keyword;
|
|
7505
|
+
const databaseChanged = !previousFingerprint || currentFingerprint.database !== previousFingerprint.database;
|
|
7506
|
+
const databaseReplaced = !previousFingerprint || currentFingerprint.databaseIdentity !== previousFingerprint.databaseIdentity;
|
|
7507
|
+
if (previousFingerprint && !vectorsChanged && !keywordChanged && !databaseChanged && !Array.from(issues.keys()).some(retryDue)) {
|
|
7508
|
+
return;
|
|
7509
|
+
}
|
|
7510
|
+
const setIssue = (component, message, error) => {
|
|
7511
|
+
if (!issues.has(component)) {
|
|
7512
|
+
this.logger.warn(message, error === void 0 ? void 0 : { error: getErrorMessage2(error) });
|
|
7513
|
+
}
|
|
7514
|
+
issues.set(component, this.createReadIssue(component, message));
|
|
7515
|
+
this.readerArtifactRetryAfter.set(component, Date.now() + READER_ARTIFACT_RETRY_INTERVAL_MS);
|
|
7516
|
+
};
|
|
7517
|
+
const storePath = path13.join(this.indexPath, "vectors");
|
|
7518
|
+
const vectorMetadataPath = `${storePath}.meta.json`;
|
|
7519
|
+
const invertedIndexPath = path13.join(this.indexPath, "inverted-index.json");
|
|
7520
|
+
const dbPath = path13.join(this.indexPath, "codebase.db");
|
|
7521
|
+
if (vectorsChanged || retryDue("vectors")) {
|
|
7522
|
+
const vectorStoreExists = existsSync8(storePath);
|
|
7523
|
+
const vectorMetadataExists = existsSync8(vectorMetadataPath);
|
|
7524
|
+
if (vectorStoreExists && vectorMetadataExists) {
|
|
7525
|
+
try {
|
|
7526
|
+
const store = new VectorStore(storePath, this.configuredProviderInfo.modelInfo.dimensions);
|
|
7527
|
+
store.loadStrict();
|
|
7528
|
+
this.store = store;
|
|
7529
|
+
issues.delete("vectors");
|
|
7530
|
+
this.readerArtifactRetryAfter.delete("vectors");
|
|
7531
|
+
} catch (error) {
|
|
7532
|
+
setIssue("vectors", this.getVectorReadIssueMessage(), error);
|
|
7533
|
+
}
|
|
7534
|
+
} else if (vectorStoreExists !== vectorMetadataExists || issues.has("vectors")) {
|
|
7535
|
+
setIssue("vectors", this.getVectorReadIssueMessage());
|
|
7536
|
+
}
|
|
7537
|
+
}
|
|
7538
|
+
if (keywordChanged || retryDue("keyword") || !existsSync8(invertedIndexPath) && (this.store?.count() ?? 0) > 0) {
|
|
7539
|
+
if (existsSync8(invertedIndexPath)) {
|
|
7540
|
+
try {
|
|
7541
|
+
const invertedIndex = new InvertedIndex(invertedIndexPath);
|
|
7542
|
+
invertedIndex.load();
|
|
7543
|
+
this.invertedIndex = invertedIndex;
|
|
7544
|
+
issues.delete("keyword");
|
|
7545
|
+
this.readerArtifactRetryAfter.delete("keyword");
|
|
7546
|
+
} catch (error) {
|
|
7547
|
+
setIssue("keyword", this.getKeywordReadIssueMessage(), error);
|
|
7548
|
+
}
|
|
7549
|
+
} else if ((this.store?.count() ?? 0) > 0 || issues.has("keyword")) {
|
|
7550
|
+
setIssue("keyword", this.getKeywordReadIssueMessage());
|
|
7551
|
+
}
|
|
7552
|
+
}
|
|
7553
|
+
if (databaseReplaced || databaseChanged && issues.has("database") || retryDue("database")) {
|
|
7554
|
+
if (existsSync8(dbPath)) {
|
|
7555
|
+
try {
|
|
7556
|
+
const database = Database.openReadOnly(dbPath);
|
|
7557
|
+
if (this.database) {
|
|
7558
|
+
this.retiredDatabases.push(this.database);
|
|
7559
|
+
}
|
|
7560
|
+
this.database = database;
|
|
7561
|
+
issues.delete("database");
|
|
7562
|
+
this.readerArtifactRetryAfter.delete("database");
|
|
7563
|
+
} catch (error) {
|
|
7564
|
+
setIssue("database", this.getDatabaseReadIssueMessage(), error);
|
|
7565
|
+
}
|
|
7566
|
+
} else if ((this.store?.count() ?? 0) > 0 || issues.has("database")) {
|
|
7567
|
+
setIssue("database", this.getDatabaseReadIssueMessage());
|
|
7568
|
+
}
|
|
7569
|
+
}
|
|
7570
|
+
if (!issues.has("database")) {
|
|
7571
|
+
try {
|
|
7572
|
+
this.indexCompatibility = this.validateIndexCompatibility(this.configuredProviderInfo);
|
|
7573
|
+
} catch (error) {
|
|
7574
|
+
setIssue("database", this.getDatabaseReadIssueMessage(), error);
|
|
7575
|
+
}
|
|
7576
|
+
}
|
|
7577
|
+
this.readIssues = Array.from(issues.values());
|
|
7578
|
+
this.readerArtifactFingerprint = currentFingerprint;
|
|
7579
|
+
}
|
|
7580
|
+
refreshInactiveWriterArtifacts() {
|
|
7581
|
+
if (this.initializationMode !== "writer" || this.activeIndexLease) {
|
|
7582
|
+
return true;
|
|
7583
|
+
}
|
|
7584
|
+
const previousFingerprint = this.writerArtifactFingerprint;
|
|
7585
|
+
const currentFingerprint = this.captureReaderArtifactFingerprint();
|
|
7586
|
+
const retryDue = this.readIssues.some(
|
|
7587
|
+
(issue) => Date.now() >= (this.readerArtifactRetryAfter.get(issue.component) ?? 0)
|
|
7588
|
+
);
|
|
7589
|
+
const artifactsChanged = !previousFingerprint || currentFingerprint.vectors !== previousFingerprint.vectors || currentFingerprint.keyword !== previousFingerprint.keyword || currentFingerprint.database !== previousFingerprint.database || currentFingerprint.databaseIdentity !== previousFingerprint.databaseIdentity;
|
|
7590
|
+
if (!artifactsChanged && !retryDue) {
|
|
7591
|
+
return true;
|
|
7592
|
+
}
|
|
7593
|
+
if (!previousFingerprint || currentFingerprint.databaseIdentity !== previousFingerprint.databaseIdentity) {
|
|
7594
|
+
return false;
|
|
7595
|
+
}
|
|
7596
|
+
this.initializationMode = "reader";
|
|
7597
|
+
this.readerArtifactFingerprint = previousFingerprint;
|
|
7598
|
+
try {
|
|
7599
|
+
this.refreshReaderArtifacts();
|
|
7600
|
+
this.writerArtifactFingerprint = this.readerArtifactFingerprint ?? currentFingerprint;
|
|
7601
|
+
} finally {
|
|
7602
|
+
this.readerArtifactFingerprint = null;
|
|
7603
|
+
this.initializationMode = "writer";
|
|
7604
|
+
}
|
|
7605
|
+
return true;
|
|
7606
|
+
}
|
|
7607
|
+
async initializeUnlocked(mode, recoveredOwners = [], options = {}) {
|
|
7608
|
+
if (mode === "writer") {
|
|
7609
|
+
this.requireActiveLease();
|
|
7610
|
+
}
|
|
7611
|
+
this.readIssues = [];
|
|
7612
|
+
this.readerArtifactRetryAfter.clear();
|
|
6829
7613
|
if (this.config.embeddingProvider === "custom") {
|
|
6830
7614
|
if (!this.config.customProvider) {
|
|
6831
7615
|
throw new Error("embeddingProvider is 'custom' but customProvider config is missing.");
|
|
@@ -6857,36 +7641,103 @@ var Indexer = class {
|
|
|
6857
7641
|
});
|
|
6858
7642
|
}
|
|
6859
7643
|
}
|
|
6860
|
-
await fsPromises2.mkdir(this.indexPath, { recursive: true });
|
|
6861
7644
|
const dimensions = this.configuredProviderInfo.modelInfo.dimensions;
|
|
6862
|
-
const storePath =
|
|
6863
|
-
|
|
6864
|
-
const
|
|
6865
|
-
|
|
6866
|
-
|
|
6867
|
-
|
|
6868
|
-
|
|
6869
|
-
|
|
6870
|
-
|
|
6871
|
-
|
|
6872
|
-
|
|
6873
|
-
|
|
6874
|
-
|
|
7645
|
+
const storePath = path13.join(this.indexPath, "vectors");
|
|
7646
|
+
const vectorMetadataPath = `${storePath}.meta.json`;
|
|
7647
|
+
const invertedIndexPath = path13.join(this.indexPath, "inverted-index.json");
|
|
7648
|
+
const dbPath = path13.join(this.indexPath, "codebase.db");
|
|
7649
|
+
let dbIsNew = !existsSync8(dbPath);
|
|
7650
|
+
const readerArtifactFingerprint = mode === "reader" ? this.captureReaderArtifactFingerprint() : null;
|
|
7651
|
+
if (mode === "writer") {
|
|
7652
|
+
await fsPromises2.mkdir(this.indexPath, { recursive: true });
|
|
7653
|
+
if (recoveredOwners.length > 0 && this.config.scope === "project" && !this.isLocalProjectIndexPath()) {
|
|
7654
|
+
throw new Error(
|
|
7655
|
+
"Interrupted indexing recovery is unsafe while using an inherited worktree index. Run index_codebase with force=true to create a local project index boundary."
|
|
7656
|
+
);
|
|
7657
|
+
}
|
|
7658
|
+
for (const recoveredOwner of recoveredOwners) {
|
|
7659
|
+
recoverLeaseArtifacts(this.indexPath, recoveredOwner, [
|
|
7660
|
+
storePath,
|
|
7661
|
+
`${storePath}.meta.json`
|
|
7662
|
+
]);
|
|
7663
|
+
}
|
|
7664
|
+
if (recoveredOwners.length > 0 && this.config.scope === "project") {
|
|
7665
|
+
await this.resetLocalIndexArtifacts();
|
|
7666
|
+
}
|
|
7667
|
+
this.store = new VectorStore(storePath, dimensions);
|
|
7668
|
+
if (existsSync8(storePath) || existsSync8(vectorMetadataPath)) {
|
|
7669
|
+
this.store.load();
|
|
6875
7670
|
}
|
|
6876
7671
|
this.invertedIndex = new InvertedIndex(invertedIndexPath);
|
|
6877
|
-
|
|
6878
|
-
|
|
6879
|
-
|
|
6880
|
-
|
|
6881
|
-
|
|
6882
|
-
|
|
6883
|
-
|
|
6884
|
-
throw error;
|
|
7672
|
+
try {
|
|
7673
|
+
this.invertedIndex.load();
|
|
7674
|
+
} catch {
|
|
7675
|
+
if (existsSync8(invertedIndexPath)) {
|
|
7676
|
+
await fsPromises2.unlink(invertedIndexPath);
|
|
7677
|
+
}
|
|
7678
|
+
this.invertedIndex = new InvertedIndex(invertedIndexPath);
|
|
6885
7679
|
}
|
|
7680
|
+
try {
|
|
7681
|
+
this.database = new Database(dbPath);
|
|
7682
|
+
} catch (error) {
|
|
7683
|
+
if (!await this.tryResetCorruptedIndex("initializing index database", error)) {
|
|
7684
|
+
throw error;
|
|
7685
|
+
}
|
|
7686
|
+
this.store = new VectorStore(storePath, dimensions);
|
|
7687
|
+
this.invertedIndex = new InvertedIndex(invertedIndexPath);
|
|
7688
|
+
this.database = new Database(dbPath);
|
|
7689
|
+
dbIsNew = true;
|
|
7690
|
+
}
|
|
7691
|
+
} else {
|
|
6886
7692
|
this.store = new VectorStore(storePath, dimensions);
|
|
7693
|
+
const vectorStoreExists = existsSync8(storePath);
|
|
7694
|
+
const vectorMetadataExists = existsSync8(vectorMetadataPath);
|
|
7695
|
+
const vectorReadFailureMessage = this.getVectorReadIssueMessage();
|
|
7696
|
+
if (vectorStoreExists !== vectorMetadataExists) {
|
|
7697
|
+
this.recordReadIssue("vectors", vectorReadFailureMessage);
|
|
7698
|
+
} else if (vectorStoreExists) {
|
|
7699
|
+
try {
|
|
7700
|
+
this.store.loadStrict();
|
|
7701
|
+
} catch (error) {
|
|
7702
|
+
this.recordReadIssue("vectors", vectorReadFailureMessage, error);
|
|
7703
|
+
this.store = new VectorStore(storePath, dimensions);
|
|
7704
|
+
}
|
|
7705
|
+
}
|
|
6887
7706
|
this.invertedIndex = new InvertedIndex(invertedIndexPath);
|
|
6888
|
-
|
|
6889
|
-
|
|
7707
|
+
if (existsSync8(invertedIndexPath)) {
|
|
7708
|
+
try {
|
|
7709
|
+
this.invertedIndex.load();
|
|
7710
|
+
} catch (error) {
|
|
7711
|
+
this.recordReadIssue(
|
|
7712
|
+
"keyword",
|
|
7713
|
+
this.getKeywordReadIssueMessage(),
|
|
7714
|
+
error
|
|
7715
|
+
);
|
|
7716
|
+
this.invertedIndex = new InvertedIndex(invertedIndexPath);
|
|
7717
|
+
}
|
|
7718
|
+
} else if (this.store.count() > 0) {
|
|
7719
|
+
this.recordReadIssue("keyword", this.getKeywordReadIssueMessage());
|
|
7720
|
+
}
|
|
7721
|
+
if (existsSync8(dbPath)) {
|
|
7722
|
+
try {
|
|
7723
|
+
this.database = Database.openReadOnly(dbPath);
|
|
7724
|
+
} catch (error) {
|
|
7725
|
+
this.recordReadIssue(
|
|
7726
|
+
"database",
|
|
7727
|
+
this.getDatabaseReadIssueMessage(),
|
|
7728
|
+
error
|
|
7729
|
+
);
|
|
7730
|
+
this.database = Database.createEmptyReadOnly();
|
|
7731
|
+
}
|
|
7732
|
+
} else {
|
|
7733
|
+
this.database = Database.createEmptyReadOnly();
|
|
7734
|
+
if (this.store.count() > 0) {
|
|
7735
|
+
this.recordReadIssue(
|
|
7736
|
+
"database",
|
|
7737
|
+
`Index database is missing for the published vectors. ${this.getDatabaseReadIssueMessage()}`
|
|
7738
|
+
);
|
|
7739
|
+
}
|
|
7740
|
+
}
|
|
6890
7741
|
}
|
|
6891
7742
|
if (isGitRepo(this.projectRoot)) {
|
|
6892
7743
|
this.currentBranch = getBranchOrDefault(this.projectRoot);
|
|
@@ -6900,10 +7751,10 @@ var Indexer = class {
|
|
|
6900
7751
|
this.baseBranch = "default";
|
|
6901
7752
|
this.logger.branch("debug", "Not a git repository, using default branch");
|
|
6902
7753
|
}
|
|
6903
|
-
if (
|
|
6904
|
-
await this.
|
|
7754
|
+
if (mode === "writer" && recoveredOwners.length > 0) {
|
|
7755
|
+
await this.recoverFromInterruptedIndexingUnlocked(recoveredOwners);
|
|
6905
7756
|
}
|
|
6906
|
-
if (dbIsNew && this.store.count() > 0) {
|
|
7757
|
+
if (mode === "writer" && dbIsNew && this.store.count() > 0) {
|
|
6907
7758
|
this.migrateFromLegacyIndex();
|
|
6908
7759
|
}
|
|
6909
7760
|
this.loadFileHashCache();
|
|
@@ -6915,9 +7766,11 @@ var Indexer = class {
|
|
|
6915
7766
|
configuredProviderInfo: this.configuredProviderInfo
|
|
6916
7767
|
});
|
|
6917
7768
|
}
|
|
6918
|
-
if (this.config.indexing.autoGc) {
|
|
7769
|
+
if (mode === "writer" && this.config.indexing.autoGc && !options.skipAutoGc) {
|
|
6919
7770
|
await this.maybeRunAutoGc();
|
|
6920
7771
|
}
|
|
7772
|
+
this.initializationMode = mode;
|
|
7773
|
+
this.readerArtifactFingerprint = readerArtifactFingerprint;
|
|
6921
7774
|
}
|
|
6922
7775
|
async maybeRunAutoGc() {
|
|
6923
7776
|
if (!this.database) return;
|
|
@@ -6934,7 +7787,7 @@ var Indexer = class {
|
|
|
6934
7787
|
}
|
|
6935
7788
|
}
|
|
6936
7789
|
if (shouldRunGc) {
|
|
6937
|
-
const result = await this.
|
|
7790
|
+
const result = await this.healthCheckUnlocked();
|
|
6938
7791
|
if (result.warning) {
|
|
6939
7792
|
this.database.setMetadata(STARTUP_WARNING_METADATA_KEY, result.warning);
|
|
6940
7793
|
} else {
|
|
@@ -6956,7 +7809,7 @@ var Indexer = class {
|
|
|
6956
7809
|
if (await this.tryResetCorruptedIndex("running automatic orphan garbage collection", error)) {
|
|
6957
7810
|
return {
|
|
6958
7811
|
resetCorruptedIndex: true,
|
|
6959
|
-
warning: this.getCorruptedIndexWarning(
|
|
7812
|
+
warning: this.getCorruptedIndexWarning(path13.join(this.indexPath, "codebase.db"))
|
|
6960
7813
|
};
|
|
6961
7814
|
}
|
|
6962
7815
|
throw error;
|
|
@@ -6971,28 +7824,29 @@ var Indexer = class {
|
|
|
6971
7824
|
return;
|
|
6972
7825
|
}
|
|
6973
7826
|
const retainedEntries = store.getAllMetadata().filter(({ key }) => !excludedSet.has(key));
|
|
6974
|
-
const storeBasePath =
|
|
6975
|
-
const storeIndexPath =
|
|
7827
|
+
const storeBasePath = path13.join(this.indexPath, "vectors");
|
|
7828
|
+
const storeIndexPath = storeBasePath;
|
|
6976
7829
|
const storeMetadataPath = `${storeBasePath}.meta.json`;
|
|
6977
|
-
const
|
|
6978
|
-
const
|
|
7830
|
+
const lease = this.requireActiveLease();
|
|
7831
|
+
const backupIndexPath = createLeaseTemporaryPath(storeIndexPath, lease.owner, "bak");
|
|
7832
|
+
const backupMetadataPath = createLeaseTemporaryPath(storeMetadataPath, lease.owner, "bak");
|
|
6979
7833
|
let backedUpIndex = false;
|
|
6980
7834
|
let backedUpMetadata = false;
|
|
6981
7835
|
let rebuiltCount = 0;
|
|
6982
7836
|
let skippedCount = 0;
|
|
6983
|
-
if (
|
|
7837
|
+
if (existsSync8(backupIndexPath)) {
|
|
6984
7838
|
unlinkSync(backupIndexPath);
|
|
6985
7839
|
}
|
|
6986
|
-
if (
|
|
7840
|
+
if (existsSync8(backupMetadataPath)) {
|
|
6987
7841
|
unlinkSync(backupMetadataPath);
|
|
6988
7842
|
}
|
|
6989
7843
|
try {
|
|
6990
|
-
if (
|
|
6991
|
-
|
|
7844
|
+
if (existsSync8(storeIndexPath)) {
|
|
7845
|
+
renameSync2(storeIndexPath, backupIndexPath);
|
|
6992
7846
|
backedUpIndex = true;
|
|
6993
7847
|
}
|
|
6994
|
-
if (
|
|
6995
|
-
|
|
7848
|
+
if (existsSync8(storeMetadataPath)) {
|
|
7849
|
+
renameSync2(storeMetadataPath, backupMetadataPath);
|
|
6996
7850
|
backedUpMetadata = true;
|
|
6997
7851
|
}
|
|
6998
7852
|
store.clear();
|
|
@@ -7012,10 +7866,10 @@ var Indexer = class {
|
|
|
7012
7866
|
rebuiltCount += 1;
|
|
7013
7867
|
}
|
|
7014
7868
|
store.save();
|
|
7015
|
-
if (backedUpIndex &&
|
|
7869
|
+
if (backedUpIndex && existsSync8(backupIndexPath)) {
|
|
7016
7870
|
unlinkSync(backupIndexPath);
|
|
7017
7871
|
}
|
|
7018
|
-
if (backedUpMetadata &&
|
|
7872
|
+
if (backedUpMetadata && existsSync8(backupMetadataPath)) {
|
|
7019
7873
|
unlinkSync(backupMetadataPath);
|
|
7020
7874
|
}
|
|
7021
7875
|
this.logger.gc("info", "Rebuilt vector store to avoid native remove", {
|
|
@@ -7028,17 +7882,17 @@ var Indexer = class {
|
|
|
7028
7882
|
store.clear();
|
|
7029
7883
|
} catch {
|
|
7030
7884
|
}
|
|
7031
|
-
if (
|
|
7885
|
+
if (existsSync8(storeIndexPath)) {
|
|
7032
7886
|
unlinkSync(storeIndexPath);
|
|
7033
7887
|
}
|
|
7034
|
-
if (
|
|
7888
|
+
if (existsSync8(storeMetadataPath)) {
|
|
7035
7889
|
unlinkSync(storeMetadataPath);
|
|
7036
7890
|
}
|
|
7037
|
-
if (backedUpIndex &&
|
|
7038
|
-
|
|
7891
|
+
if (backedUpIndex && existsSync8(backupIndexPath)) {
|
|
7892
|
+
renameSync2(backupIndexPath, storeIndexPath);
|
|
7039
7893
|
}
|
|
7040
|
-
if (backedUpMetadata &&
|
|
7041
|
-
|
|
7894
|
+
if (backedUpMetadata && existsSync8(backupMetadataPath)) {
|
|
7895
|
+
renameSync2(backupMetadataPath, storeMetadataPath);
|
|
7042
7896
|
}
|
|
7043
7897
|
if (backedUpIndex || backedUpMetadata) {
|
|
7044
7898
|
store.load();
|
|
@@ -7052,11 +7906,37 @@ var Indexer = class {
|
|
|
7052
7906
|
}
|
|
7053
7907
|
return `Detected a corrupted local SQLite index at ${dbPath} and reset the local index. Run index_codebase to rebuild search data.`;
|
|
7054
7908
|
}
|
|
7909
|
+
async resetLocalIndexArtifacts() {
|
|
7910
|
+
this.store = null;
|
|
7911
|
+
this.invertedIndex = null;
|
|
7912
|
+
this.database?.close();
|
|
7913
|
+
this.database = null;
|
|
7914
|
+
this.indexCompatibility = null;
|
|
7915
|
+
this.initializationMode = "none";
|
|
7916
|
+
this.readIssues = [];
|
|
7917
|
+
this.readerArtifactFingerprint = null;
|
|
7918
|
+
this.writerArtifactFingerprint = null;
|
|
7919
|
+
this.readerArtifactRetryAfter.clear();
|
|
7920
|
+
this.fileHashCache.clear();
|
|
7921
|
+
const resetPaths = [
|
|
7922
|
+
path13.join(this.indexPath, "codebase.db"),
|
|
7923
|
+
path13.join(this.indexPath, "codebase.db-shm"),
|
|
7924
|
+
path13.join(this.indexPath, "codebase.db-wal"),
|
|
7925
|
+
path13.join(this.indexPath, "vectors"),
|
|
7926
|
+
path13.join(this.indexPath, "vectors.usearch"),
|
|
7927
|
+
path13.join(this.indexPath, "vectors.meta.json"),
|
|
7928
|
+
path13.join(this.indexPath, "inverted-index.json"),
|
|
7929
|
+
path13.join(this.indexPath, "file-hashes.json"),
|
|
7930
|
+
path13.join(this.indexPath, "failed-batches.json")
|
|
7931
|
+
];
|
|
7932
|
+
await Promise.all(resetPaths.map((targetPath) => fsPromises2.rm(targetPath, { recursive: true, force: true })));
|
|
7933
|
+
await fsPromises2.mkdir(this.indexPath, { recursive: true });
|
|
7934
|
+
}
|
|
7055
7935
|
async tryResetCorruptedIndex(stage, error) {
|
|
7056
7936
|
if (!isSqliteCorruptionError(error)) {
|
|
7057
7937
|
return false;
|
|
7058
7938
|
}
|
|
7059
|
-
const dbPath =
|
|
7939
|
+
const dbPath = path13.join(this.indexPath, "codebase.db");
|
|
7060
7940
|
const warning = this.getCorruptedIndexWarning(dbPath);
|
|
7061
7941
|
const errorMessage = getErrorMessage2(error);
|
|
7062
7942
|
if (this.config.scope === "global") {
|
|
@@ -7072,30 +7952,7 @@ var Indexer = class {
|
|
|
7072
7952
|
dbPath,
|
|
7073
7953
|
error: errorMessage
|
|
7074
7954
|
});
|
|
7075
|
-
this.
|
|
7076
|
-
this.invertedIndex = null;
|
|
7077
|
-
this.database?.close();
|
|
7078
|
-
this.database = null;
|
|
7079
|
-
this.indexCompatibility = null;
|
|
7080
|
-
this.fileHashCache.clear();
|
|
7081
|
-
const resetPaths = [
|
|
7082
|
-
path12.join(this.indexPath, "codebase.db"),
|
|
7083
|
-
path12.join(this.indexPath, "codebase.db-shm"),
|
|
7084
|
-
path12.join(this.indexPath, "codebase.db-wal"),
|
|
7085
|
-
path12.join(this.indexPath, "vectors.usearch"),
|
|
7086
|
-
path12.join(this.indexPath, "inverted-index.json"),
|
|
7087
|
-
path12.join(this.indexPath, "file-hashes.json"),
|
|
7088
|
-
path12.join(this.indexPath, "failed-batches.json"),
|
|
7089
|
-
path12.join(this.indexPath, "indexing.lock"),
|
|
7090
|
-
path12.join(this.indexPath, "vectors")
|
|
7091
|
-
];
|
|
7092
|
-
await Promise.all(resetPaths.map(async (targetPath) => {
|
|
7093
|
-
try {
|
|
7094
|
-
await fsPromises2.rm(targetPath, { recursive: true, force: true });
|
|
7095
|
-
} catch {
|
|
7096
|
-
}
|
|
7097
|
-
}));
|
|
7098
|
-
await fsPromises2.mkdir(this.indexPath, { recursive: true });
|
|
7955
|
+
await this.resetLocalIndexArtifacts();
|
|
7099
7956
|
return true;
|
|
7100
7957
|
}
|
|
7101
7958
|
migrateFromLegacyIndex() {
|
|
@@ -7214,8 +8071,62 @@ var Indexer = class {
|
|
|
7214
8071
|
return this.indexCompatibility;
|
|
7215
8072
|
}
|
|
7216
8073
|
async ensureInitialized() {
|
|
8074
|
+
let initializedReader = false;
|
|
8075
|
+
while (true) {
|
|
8076
|
+
if (this.initializationPromise) {
|
|
8077
|
+
await this.initializationPromise;
|
|
8078
|
+
}
|
|
8079
|
+
if (!this.isInitializedFor("reader")) {
|
|
8080
|
+
await this.initialize();
|
|
8081
|
+
initializedReader = true;
|
|
8082
|
+
continue;
|
|
8083
|
+
}
|
|
8084
|
+
if (this.initializationMode === "writer" && !this.activeIndexLease && !initializedReader) {
|
|
8085
|
+
if (!this.refreshInactiveWriterArtifacts()) {
|
|
8086
|
+
this.resetLoadedIndexState(true);
|
|
8087
|
+
await this.initialize();
|
|
8088
|
+
initializedReader = true;
|
|
8089
|
+
continue;
|
|
8090
|
+
}
|
|
8091
|
+
}
|
|
8092
|
+
if (this.initializationMode === "reader" && !initializedReader) {
|
|
8093
|
+
this.refreshReaderArtifacts();
|
|
8094
|
+
}
|
|
8095
|
+
const state = this.requireLoadedIndexState();
|
|
8096
|
+
return {
|
|
8097
|
+
...state,
|
|
8098
|
+
readIssues: [...this.readIssues],
|
|
8099
|
+
compatibility: this.indexCompatibility ?? this.validateIndexCompatibility(state.configuredProviderInfo)
|
|
8100
|
+
};
|
|
8101
|
+
}
|
|
8102
|
+
}
|
|
8103
|
+
async ensureInitializedUnlocked(recoveredOwners = []) {
|
|
8104
|
+
this.requireActiveLease();
|
|
8105
|
+
if (this.initializationPromise) {
|
|
8106
|
+
await this.initializationPromise;
|
|
8107
|
+
}
|
|
8108
|
+
if (recoveredOwners.length > 0 || !this.isInitializedFor("writer")) {
|
|
8109
|
+
const retireReaderDatabase = this.initializationMode === "reader";
|
|
8110
|
+
this.resetLoadedIndexState(retireReaderDatabase);
|
|
8111
|
+
await this.initializeOnce("writer", recoveredOwners, { skipAutoGc: true });
|
|
8112
|
+
} else {
|
|
8113
|
+
this.refreshLoadedIndexState();
|
|
8114
|
+
}
|
|
8115
|
+
if (this.config.indexing.autoGc) {
|
|
8116
|
+
await this.maybeRunAutoGc();
|
|
8117
|
+
}
|
|
8118
|
+
return this.requireLoadedIndexState();
|
|
8119
|
+
}
|
|
8120
|
+
requireReadableComponents(readIssues, ...components) {
|
|
8121
|
+
const componentSet = new Set(components);
|
|
8122
|
+
const issues = readIssues.filter((issue) => issue.blocking && componentSet.has(issue.component));
|
|
8123
|
+
if (issues.length > 0) {
|
|
8124
|
+
throw new Error(issues.map((issue) => issue.message).join(" "));
|
|
8125
|
+
}
|
|
8126
|
+
}
|
|
8127
|
+
requireLoadedIndexState() {
|
|
7217
8128
|
if (!this.store || !this.provider || !this.invertedIndex || !this.configuredProviderInfo || !this.database) {
|
|
7218
|
-
|
|
8129
|
+
throw new Error("Index state is not initialized");
|
|
7219
8130
|
}
|
|
7220
8131
|
return {
|
|
7221
8132
|
store: this.store,
|
|
@@ -7239,7 +8150,12 @@ var Indexer = class {
|
|
|
7239
8150
|
return createCostEstimate(files, configuredProviderInfo);
|
|
7240
8151
|
}
|
|
7241
8152
|
async index(onProgress) {
|
|
7242
|
-
|
|
8153
|
+
return this.withIndexMutationLease("index", async (recoveredOwners) => {
|
|
8154
|
+
return this.indexUnlocked(onProgress, recoveredOwners);
|
|
8155
|
+
});
|
|
8156
|
+
}
|
|
8157
|
+
async indexUnlocked(onProgress, recoveredOwners = [], stateReady = false) {
|
|
8158
|
+
const { store, provider, invertedIndex, database, configuredProviderInfo } = stateReady ? this.requireLoadedIndexState() : await this.ensureInitializedUnlocked(recoveredOwners);
|
|
7243
8159
|
const scopedRoots = this.config.scope === "global" ? this.getScopedRoots() : null;
|
|
7244
8160
|
const branchCatalogKey = this.getBranchCatalogKey();
|
|
7245
8161
|
const forceScopedReembed = scopedRoots !== null && database.getMetadata(this.getProjectForceReembedMetadataKey()) === "true";
|
|
@@ -7249,7 +8165,6 @@ var Indexer = class {
|
|
|
7249
8165
|
`${this.indexCompatibility?.reason} Run index_codebase with force=true to rebuild the index.`
|
|
7250
8166
|
);
|
|
7251
8167
|
}
|
|
7252
|
-
this.acquireIndexingLock();
|
|
7253
8168
|
this.logger.recordIndexingStart();
|
|
7254
8169
|
this.logger.info("Starting indexing", { projectRoot: this.projectRoot });
|
|
7255
8170
|
const startTime = Date.now();
|
|
@@ -7398,7 +8313,7 @@ var Indexer = class {
|
|
|
7398
8313
|
for (const parsed of parsedFiles) {
|
|
7399
8314
|
currentFilePaths.add(parsed.path);
|
|
7400
8315
|
if (parsed.chunks.length === 0) {
|
|
7401
|
-
const relativePath =
|
|
8316
|
+
const relativePath = path13.relative(this.projectRoot, parsed.path);
|
|
7402
8317
|
stats.parseFailures.push(relativePath);
|
|
7403
8318
|
}
|
|
7404
8319
|
let fileChunkCount = 0;
|
|
@@ -7598,7 +8513,9 @@ var Indexer = class {
|
|
|
7598
8513
|
database.addChunksToBranchBatch(branchCatalogKey, Array.from(currentChunkIds));
|
|
7599
8514
|
database.clearBranchSymbols(branchCatalogKey);
|
|
7600
8515
|
database.addSymbolsToBranchBatch(branchCatalogKey, Array.from(allSymbolIds));
|
|
7601
|
-
|
|
8516
|
+
const vectorPath = path13.join(this.indexPath, "vectors");
|
|
8517
|
+
const shouldFingerprintLegacyPair = !store.hasFingerprint() && existsSync8(vectorPath) && existsSync8(`${vectorPath}.meta.json`);
|
|
8518
|
+
if (backfilledBlameMetadata || shouldFingerprintLegacyPair) {
|
|
7602
8519
|
store.save();
|
|
7603
8520
|
}
|
|
7604
8521
|
if (scopedRoots) {
|
|
@@ -7619,7 +8536,6 @@ var Indexer = class {
|
|
|
7619
8536
|
chunksProcessed: 0,
|
|
7620
8537
|
totalChunks: 0
|
|
7621
8538
|
});
|
|
7622
|
-
this.releaseIndexingLock();
|
|
7623
8539
|
return stats;
|
|
7624
8540
|
}
|
|
7625
8541
|
if (pendingChunks.length === 0) {
|
|
@@ -7628,7 +8544,7 @@ var Indexer = class {
|
|
|
7628
8544
|
database.clearBranchSymbols(branchCatalogKey);
|
|
7629
8545
|
database.addSymbolsToBranchBatch(branchCatalogKey, Array.from(allSymbolIds));
|
|
7630
8546
|
store.save();
|
|
7631
|
-
|
|
8547
|
+
this.saveInvertedIndex(invertedIndex);
|
|
7632
8548
|
if (scopedRoots) {
|
|
7633
8549
|
this.replaceScopedFileHashCache(currentFileHashes, scopedRoots);
|
|
7634
8550
|
this.clearScopedFailedBatches(scopedRoots);
|
|
@@ -7647,7 +8563,6 @@ var Indexer = class {
|
|
|
7647
8563
|
chunksProcessed: 0,
|
|
7648
8564
|
totalChunks: 0
|
|
7649
8565
|
});
|
|
7650
|
-
this.releaseIndexingLock();
|
|
7651
8566
|
return stats;
|
|
7652
8567
|
}
|
|
7653
8568
|
onProgress?.({
|
|
@@ -7878,7 +8793,7 @@ var Indexer = class {
|
|
|
7878
8793
|
database.clearBranchSymbols(branchCatalogKey);
|
|
7879
8794
|
database.addSymbolsToBranchBatch(branchCatalogKey, Array.from(allSymbolIds));
|
|
7880
8795
|
store.save();
|
|
7881
|
-
|
|
8796
|
+
this.saveInvertedIndex(invertedIndex);
|
|
7882
8797
|
if (scopedRoots) {
|
|
7883
8798
|
this.replaceScopedFileHashCache(currentFileHashes, scopedRoots);
|
|
7884
8799
|
} else {
|
|
@@ -7930,7 +8845,6 @@ var Indexer = class {
|
|
|
7930
8845
|
chunksProcessed: stats.indexedChunks,
|
|
7931
8846
|
totalChunks: pendingChunks.length
|
|
7932
8847
|
});
|
|
7933
|
-
this.releaseIndexingLock();
|
|
7934
8848
|
return stats;
|
|
7935
8849
|
}
|
|
7936
8850
|
async getQueryEmbedding(query, provider) {
|
|
@@ -7996,8 +8910,8 @@ var Indexer = class {
|
|
|
7996
8910
|
return intersection / union;
|
|
7997
8911
|
}
|
|
7998
8912
|
async search(query, limit, options) {
|
|
7999
|
-
const { store, provider, database } = await this.ensureInitialized();
|
|
8000
|
-
|
|
8913
|
+
const { store, provider, invertedIndex, database, readIssues, compatibility } = await this.ensureInitialized();
|
|
8914
|
+
this.requireReadableComponents(readIssues, "vectors", "database");
|
|
8001
8915
|
if (!compatibility.compatible) {
|
|
8002
8916
|
throw new Error(
|
|
8003
8917
|
`${compatibility.reason ?? "Index is incompatible with current embedding provider."} A possible solution is to run index_codebase with force=true to rebuild the index.`
|
|
@@ -8011,6 +8925,7 @@ var Indexer = class {
|
|
|
8011
8925
|
const maxResults = limit ?? this.config.search.maxResults;
|
|
8012
8926
|
const hybridWeight = options?.hybridWeight ?? this.config.search.hybridWeight;
|
|
8013
8927
|
const fusionStrategy = this.config.search.fusionStrategy;
|
|
8928
|
+
const effectiveHybridWeight = fusionStrategy === "weighted" && readIssues.some((issue) => issue.component === "keyword") ? 0 : hybridWeight;
|
|
8014
8929
|
const rrfK = this.config.search.rrfK;
|
|
8015
8930
|
const rerankTopN = this.config.search.rerankTopN;
|
|
8016
8931
|
const filterByBranch = options?.filterByBranch ?? true;
|
|
@@ -8019,7 +8934,7 @@ var Indexer = class {
|
|
|
8019
8934
|
this.logger.search("debug", "Starting search", {
|
|
8020
8935
|
query,
|
|
8021
8936
|
maxResults,
|
|
8022
|
-
hybridWeight,
|
|
8937
|
+
hybridWeight: effectiveHybridWeight,
|
|
8023
8938
|
fusionStrategy,
|
|
8024
8939
|
rrfK,
|
|
8025
8940
|
rerankTopN,
|
|
@@ -8033,7 +8948,7 @@ var Indexer = class {
|
|
|
8033
8948
|
const semanticResults = store.search(embedding, maxResults * 4);
|
|
8034
8949
|
const vectorMs = performance2.now() - vectorStartTime;
|
|
8035
8950
|
const keywordStartTime = performance2.now();
|
|
8036
|
-
const keywordResults = await this.keywordSearch(query, maxResults * 4);
|
|
8951
|
+
const keywordResults = await this.keywordSearch(query, maxResults * 4, store, invertedIndex);
|
|
8037
8952
|
const keywordMs = performance2.now() - keywordStartTime;
|
|
8038
8953
|
let branchChunkIds = null;
|
|
8039
8954
|
if (filterByBranch && (this.config.scope === "global" || this.currentBranch !== "default")) {
|
|
@@ -8070,7 +8985,7 @@ var Indexer = class {
|
|
|
8070
8985
|
rrfK,
|
|
8071
8986
|
rerankTopN,
|
|
8072
8987
|
limit: maxResults,
|
|
8073
|
-
hybridWeight,
|
|
8988
|
+
hybridWeight: effectiveHybridWeight,
|
|
8074
8989
|
prioritizeSourcePaths: sourceIntent
|
|
8075
8990
|
});
|
|
8076
8991
|
const rerankedCombined = await this.rerankCandidatesWithApi(query, combined, {
|
|
@@ -8170,8 +9085,7 @@ var Indexer = class {
|
|
|
8170
9085
|
})
|
|
8171
9086
|
);
|
|
8172
9087
|
}
|
|
8173
|
-
async keywordSearch(query, limit) {
|
|
8174
|
-
const { store, invertedIndex } = await this.ensureInitialized();
|
|
9088
|
+
async keywordSearch(query, limit, store, invertedIndex) {
|
|
8175
9089
|
const scores = invertedIndex.search(query);
|
|
8176
9090
|
if (scores.size === 0) {
|
|
8177
9091
|
return [];
|
|
@@ -8189,24 +9103,54 @@ var Indexer = class {
|
|
|
8189
9103
|
return results.slice(0, limit);
|
|
8190
9104
|
}
|
|
8191
9105
|
async getStatus() {
|
|
8192
|
-
const { store, configuredProviderInfo, database } = await this.ensureInitialized();
|
|
9106
|
+
const { store, configuredProviderInfo, database, readIssues, compatibility } = await this.ensureInitialized();
|
|
8193
9107
|
const failedBatchesCount = this.getFailedBatchesCount();
|
|
9108
|
+
const vectorCount = store.count();
|
|
9109
|
+
const statusReadIssues = [...readIssues];
|
|
9110
|
+
let startupWarning = "";
|
|
9111
|
+
if (!statusReadIssues.some((issue) => issue.component === "database")) {
|
|
9112
|
+
try {
|
|
9113
|
+
startupWarning = database.getMetadata(STARTUP_WARNING_METADATA_KEY) ?? "";
|
|
9114
|
+
} catch (error) {
|
|
9115
|
+
const message = this.getDatabaseReadIssueMessage();
|
|
9116
|
+
statusReadIssues.push(this.createReadIssue("database", message));
|
|
9117
|
+
if (!this.readIssues.some((issue) => issue.component === "database")) {
|
|
9118
|
+
this.recordReadIssue("database", message, error);
|
|
9119
|
+
}
|
|
9120
|
+
}
|
|
9121
|
+
}
|
|
9122
|
+
const readWarning = statusReadIssues.map((issue) => issue.message).join(" ");
|
|
9123
|
+
const warning = [readWarning, startupWarning].filter((message) => message.length > 0).join(" ");
|
|
9124
|
+
const hasBlockingReadIssue = statusReadIssues.some((issue) => issue.blocking);
|
|
8194
9125
|
return {
|
|
8195
|
-
indexed:
|
|
8196
|
-
vectorCount
|
|
9126
|
+
indexed: vectorCount > 0 && !hasBlockingReadIssue,
|
|
9127
|
+
vectorCount,
|
|
8197
9128
|
provider: configuredProviderInfo.provider,
|
|
8198
9129
|
model: configuredProviderInfo.modelInfo.model,
|
|
8199
9130
|
indexPath: this.indexPath,
|
|
8200
9131
|
currentBranch: this.currentBranch,
|
|
8201
9132
|
baseBranch: this.baseBranch,
|
|
8202
|
-
compatibility
|
|
9133
|
+
compatibility,
|
|
8203
9134
|
failedBatchesCount,
|
|
8204
9135
|
failedBatchesPath: failedBatchesCount > 0 ? this.failedBatchesPath : void 0,
|
|
8205
|
-
warning:
|
|
9136
|
+
warning: warning || void 0
|
|
8206
9137
|
};
|
|
8207
9138
|
}
|
|
9139
|
+
async forceIndex(onProgress) {
|
|
9140
|
+
return this.withIndexMutationLease("force-index", async (recoveredOwners) => {
|
|
9141
|
+
await this.ensureInitializedUnlocked(recoveredOwners);
|
|
9142
|
+
await this.clearIndexUnlocked();
|
|
9143
|
+
return this.indexUnlocked(onProgress, [], true);
|
|
9144
|
+
});
|
|
9145
|
+
}
|
|
8208
9146
|
async clearIndex() {
|
|
8209
|
-
|
|
9147
|
+
await this.withIndexMutationLease("clear", async (recoveredOwners) => {
|
|
9148
|
+
await this.ensureInitializedUnlocked(recoveredOwners);
|
|
9149
|
+
await this.clearIndexUnlocked();
|
|
9150
|
+
});
|
|
9151
|
+
}
|
|
9152
|
+
async clearIndexUnlocked() {
|
|
9153
|
+
const { store, invertedIndex, database } = this.requireLoadedIndexState();
|
|
8210
9154
|
if (this.config.scope === "global") {
|
|
8211
9155
|
store.load();
|
|
8212
9156
|
invertedIndex.load();
|
|
@@ -8233,7 +9177,7 @@ var Indexer = class {
|
|
|
8233
9177
|
store.clear();
|
|
8234
9178
|
store.save();
|
|
8235
9179
|
invertedIndex.clear();
|
|
8236
|
-
|
|
9180
|
+
this.saveInvertedIndex(invertedIndex);
|
|
8237
9181
|
this.fileHashCache.clear();
|
|
8238
9182
|
this.saveFileHashCache();
|
|
8239
9183
|
database.clearAllIndexedData();
|
|
@@ -8257,14 +9201,7 @@ var Indexer = class {
|
|
|
8257
9201
|
this.indexCompatibility = compatibility;
|
|
8258
9202
|
return;
|
|
8259
9203
|
}
|
|
8260
|
-
|
|
8261
|
-
if (this.host !== "opencode") {
|
|
8262
|
-
localProjectIndexPaths.push(path12.join(this.projectRoot, getHostProjectIndexRelativePath("opencode")));
|
|
8263
|
-
}
|
|
8264
|
-
const isLocalProjectIndex = localProjectIndexPaths.some(
|
|
8265
|
-
(localPath) => path12.resolve(this.indexPath) === path12.resolve(localPath)
|
|
8266
|
-
);
|
|
8267
|
-
if (!isLocalProjectIndex) {
|
|
9204
|
+
if (!this.isLocalProjectIndexPath()) {
|
|
8268
9205
|
throw new Error(
|
|
8269
9206
|
"Project-scoped force rebuild is unsafe while using an inherited worktree index. Create a local project config boundary before clearing the index."
|
|
8270
9207
|
);
|
|
@@ -8272,7 +9209,7 @@ var Indexer = class {
|
|
|
8272
9209
|
store.clear();
|
|
8273
9210
|
store.save();
|
|
8274
9211
|
invertedIndex.clear();
|
|
8275
|
-
|
|
9212
|
+
this.saveInvertedIndex(invertedIndex);
|
|
8276
9213
|
this.fileHashCache.clear();
|
|
8277
9214
|
this.saveFileHashCache();
|
|
8278
9215
|
database.clearAllIndexedData();
|
|
@@ -8290,7 +9227,13 @@ var Indexer = class {
|
|
|
8290
9227
|
this.indexCompatibility = this.validateIndexCompatibility(this.configuredProviderInfo);
|
|
8291
9228
|
}
|
|
8292
9229
|
async healthCheck() {
|
|
8293
|
-
|
|
9230
|
+
return this.withIndexMutationLease("health-check", async (recoveredOwners) => {
|
|
9231
|
+
await this.ensureInitializedUnlocked(recoveredOwners);
|
|
9232
|
+
return this.healthCheckUnlocked();
|
|
9233
|
+
});
|
|
9234
|
+
}
|
|
9235
|
+
async healthCheckUnlocked() {
|
|
9236
|
+
const { store, invertedIndex, database } = this.requireLoadedIndexState();
|
|
8294
9237
|
this.logger.gc("info", "Starting health check");
|
|
8295
9238
|
const allMetadata = store.getAllMetadata();
|
|
8296
9239
|
const filePathsToChunkKeys = /* @__PURE__ */ new Map();
|
|
@@ -8303,7 +9246,7 @@ var Indexer = class {
|
|
|
8303
9246
|
const removedChunkKeys = [];
|
|
8304
9247
|
const chunkKeysByRemovedFile = /* @__PURE__ */ new Map();
|
|
8305
9248
|
for (const [filePath, chunkKeys] of filePathsToChunkKeys) {
|
|
8306
|
-
if (!
|
|
9249
|
+
if (!existsSync8(filePath)) {
|
|
8307
9250
|
chunkKeysByRemovedFile.set(filePath, chunkKeys);
|
|
8308
9251
|
for (const key of chunkKeys) {
|
|
8309
9252
|
removedChunkKeys.push(key);
|
|
@@ -8328,7 +9271,7 @@ var Indexer = class {
|
|
|
8328
9271
|
const removedCount = removedChunkKeys.length;
|
|
8329
9272
|
if (removedCount > 0) {
|
|
8330
9273
|
store.save();
|
|
8331
|
-
|
|
9274
|
+
this.saveInvertedIndex(invertedIndex);
|
|
8332
9275
|
}
|
|
8333
9276
|
let gcOrphanEmbeddings;
|
|
8334
9277
|
let gcOrphanChunks;
|
|
@@ -8343,7 +9286,7 @@ var Indexer = class {
|
|
|
8343
9286
|
if (!await this.tryResetCorruptedIndex("running index health check", error)) {
|
|
8344
9287
|
throw error;
|
|
8345
9288
|
}
|
|
8346
|
-
await this.
|
|
9289
|
+
await this.initializeUnlocked("writer", [], { skipAutoGc: true });
|
|
8347
9290
|
return {
|
|
8348
9291
|
removed: 0,
|
|
8349
9292
|
filePaths: [],
|
|
@@ -8352,7 +9295,7 @@ var Indexer = class {
|
|
|
8352
9295
|
gcOrphanSymbols: 0,
|
|
8353
9296
|
gcOrphanCallEdges: 0,
|
|
8354
9297
|
resetCorruptedIndex: true,
|
|
8355
|
-
warning: this.getCorruptedIndexWarning(
|
|
9298
|
+
warning: this.getCorruptedIndexWarning(path13.join(this.indexPath, "codebase.db"))
|
|
8356
9299
|
};
|
|
8357
9300
|
}
|
|
8358
9301
|
this.logger.recordGc(removedCount, gcOrphanChunks, gcOrphanEmbeddings);
|
|
@@ -8365,7 +9308,13 @@ var Indexer = class {
|
|
|
8365
9308
|
return { removed: removedCount, filePaths: removedFilePaths, gcOrphanEmbeddings, gcOrphanChunks, gcOrphanSymbols, gcOrphanCallEdges };
|
|
8366
9309
|
}
|
|
8367
9310
|
async retryFailedBatches() {
|
|
8368
|
-
|
|
9311
|
+
return this.withIndexMutationLease("retry-failed-batches", async (recoveredOwners) => {
|
|
9312
|
+
await this.ensureInitializedUnlocked(recoveredOwners);
|
|
9313
|
+
return this.retryFailedBatchesUnlocked();
|
|
9314
|
+
});
|
|
9315
|
+
}
|
|
9316
|
+
async retryFailedBatchesUnlocked() {
|
|
9317
|
+
const { store, provider, invertedIndex, database, configuredProviderInfo } = this.requireLoadedIndexState();
|
|
8369
9318
|
const maxChunkTokens = getSafeEmbeddingChunkTokenLimit(configuredProviderInfo);
|
|
8370
9319
|
const providerRateLimits = this.getProviderRateLimits(configuredProviderInfo.provider);
|
|
8371
9320
|
const roots = this.config.scope === "global" ? this.getScopedRoots() : null;
|
|
@@ -8529,7 +9478,7 @@ var Indexer = class {
|
|
|
8529
9478
|
}
|
|
8530
9479
|
if (succeeded > 0) {
|
|
8531
9480
|
store.save();
|
|
8532
|
-
|
|
9481
|
+
this.saveInvertedIndex(invertedIndex);
|
|
8533
9482
|
}
|
|
8534
9483
|
if (roots && succeeded > 0 && persistedStillFailing.length === 0 && this.hasProjectForceReembedPending()) {
|
|
8535
9484
|
database.deleteMetadata(this.getProjectForceReembedMetadataKey());
|
|
@@ -8557,15 +9506,16 @@ var Indexer = class {
|
|
|
8557
9506
|
}
|
|
8558
9507
|
}
|
|
8559
9508
|
async getDatabaseStats() {
|
|
8560
|
-
const { database } = await this.ensureInitialized();
|
|
9509
|
+
const { database, readIssues } = await this.ensureInitialized();
|
|
9510
|
+
this.requireReadableComponents(readIssues, "database");
|
|
8561
9511
|
return database.getStats();
|
|
8562
9512
|
}
|
|
8563
9513
|
getLogger() {
|
|
8564
9514
|
return this.logger;
|
|
8565
9515
|
}
|
|
8566
9516
|
async findSimilar(code, limit = this.config.search.maxResults, options) {
|
|
8567
|
-
const { store, provider, database } = await this.ensureInitialized();
|
|
8568
|
-
|
|
9517
|
+
const { store, provider, database, readIssues, compatibility } = await this.ensureInitialized();
|
|
9518
|
+
this.requireReadableComponents(readIssues, "vectors", "database");
|
|
8569
9519
|
if (!compatibility.compatible) {
|
|
8570
9520
|
throw new Error(
|
|
8571
9521
|
`${compatibility.reason ?? "Index is incompatible with current embedding provider."} Run index_codebase with force=true to rebuild the index.`
|
|
@@ -8679,7 +9629,8 @@ var Indexer = class {
|
|
|
8679
9629
|
);
|
|
8680
9630
|
}
|
|
8681
9631
|
async getCallers(targetName, callTypeFilter) {
|
|
8682
|
-
const { database } = await this.ensureInitialized();
|
|
9632
|
+
const { database, readIssues } = await this.ensureInitialized();
|
|
9633
|
+
this.requireReadableComponents(readIssues, "database");
|
|
8683
9634
|
const seen = /* @__PURE__ */ new Set();
|
|
8684
9635
|
const results = [];
|
|
8685
9636
|
for (const branchKey of this.getBranchCatalogKeys()) {
|
|
@@ -8693,7 +9644,8 @@ var Indexer = class {
|
|
|
8693
9644
|
return results;
|
|
8694
9645
|
}
|
|
8695
9646
|
async getCallees(symbolId, callTypeFilter) {
|
|
8696
|
-
const { database } = await this.ensureInitialized();
|
|
9647
|
+
const { database, readIssues } = await this.ensureInitialized();
|
|
9648
|
+
this.requireReadableComponents(readIssues, "database");
|
|
8697
9649
|
const seen = /* @__PURE__ */ new Set();
|
|
8698
9650
|
const results = [];
|
|
8699
9651
|
for (const branchKey of this.getBranchCatalogKeys()) {
|
|
@@ -8707,43 +9659,50 @@ var Indexer = class {
|
|
|
8707
9659
|
return results;
|
|
8708
9660
|
}
|
|
8709
9661
|
async findCallPath(fromName, toName, maxDepth) {
|
|
8710
|
-
const { database } = await this.ensureInitialized();
|
|
9662
|
+
const { database, readIssues } = await this.ensureInitialized();
|
|
9663
|
+
this.requireReadableComponents(readIssues, "database");
|
|
8711
9664
|
let shortest = [];
|
|
8712
9665
|
for (const branchKey of this.getBranchCatalogKeys()) {
|
|
8713
|
-
const
|
|
8714
|
-
if (
|
|
8715
|
-
shortest =
|
|
9666
|
+
const path24 = database.findShortestPath(fromName, toName, branchKey, maxDepth);
|
|
9667
|
+
if (path24.length > 0 && (shortest.length === 0 || path24.length < shortest.length)) {
|
|
9668
|
+
shortest = path24;
|
|
8716
9669
|
}
|
|
8717
9670
|
}
|
|
8718
9671
|
return shortest;
|
|
8719
9672
|
}
|
|
8720
9673
|
async getSymbolsForBranch(branch) {
|
|
8721
|
-
const { database } = await this.ensureInitialized();
|
|
9674
|
+
const { database, readIssues } = await this.ensureInitialized();
|
|
9675
|
+
this.requireReadableComponents(readIssues, "database");
|
|
8722
9676
|
const resolvedBranch = branch ?? this.getBranchCatalogKey();
|
|
8723
9677
|
return database.getSymbolsForBranch(resolvedBranch);
|
|
8724
9678
|
}
|
|
8725
9679
|
async getSymbolsForFiles(filePaths, branch) {
|
|
8726
|
-
const { database } = await this.ensureInitialized();
|
|
9680
|
+
const { database, readIssues } = await this.ensureInitialized();
|
|
9681
|
+
this.requireReadableComponents(readIssues, "database");
|
|
8727
9682
|
const resolvedBranch = branch ?? this.getBranchCatalogKey();
|
|
8728
9683
|
return database.getSymbolsForFiles(filePaths, resolvedBranch);
|
|
8729
9684
|
}
|
|
8730
9685
|
async getTransitiveReachability(rootSymbolIds, direction, maxDepth) {
|
|
8731
|
-
const { database } = await this.ensureInitialized();
|
|
9686
|
+
const { database, readIssues } = await this.ensureInitialized();
|
|
9687
|
+
this.requireReadableComponents(readIssues, "database");
|
|
8732
9688
|
const branch = this.getBranchCatalogKey();
|
|
8733
9689
|
return database.getTransitiveReachability(rootSymbolIds, branch, direction, maxDepth);
|
|
8734
9690
|
}
|
|
8735
9691
|
async detectCommunities(branch, symbolIds) {
|
|
8736
|
-
const { database } = await this.ensureInitialized();
|
|
9692
|
+
const { database, readIssues } = await this.ensureInitialized();
|
|
9693
|
+
this.requireReadableComponents(readIssues, "database");
|
|
8737
9694
|
const resolvedBranch = branch ?? this.getBranchCatalogKey();
|
|
8738
9695
|
return database.detectCommunities(resolvedBranch, symbolIds);
|
|
8739
9696
|
}
|
|
8740
9697
|
async computeCentrality(branch) {
|
|
8741
|
-
const { database } = await this.ensureInitialized();
|
|
9698
|
+
const { database, readIssues } = await this.ensureInitialized();
|
|
9699
|
+
this.requireReadableComponents(readIssues, "database");
|
|
8742
9700
|
const resolvedBranch = branch ?? this.getBranchCatalogKey();
|
|
8743
9701
|
return database.computeCentrality(resolvedBranch);
|
|
8744
9702
|
}
|
|
8745
9703
|
async getPrImpact(opts) {
|
|
8746
|
-
const { database } = await this.ensureInitialized();
|
|
9704
|
+
const { database, readIssues } = await this.ensureInitialized();
|
|
9705
|
+
this.requireReadableComponents(readIssues, "database");
|
|
8747
9706
|
const execFileAsync3 = promisify3(execFile3);
|
|
8748
9707
|
const changedFilesResult = await getChangedFiles({
|
|
8749
9708
|
pr: opts.pr,
|
|
@@ -8766,7 +9725,7 @@ var Indexer = class {
|
|
|
8766
9725
|
"Run index_codebase first to build the call graph and symbol index for this branch."
|
|
8767
9726
|
);
|
|
8768
9727
|
}
|
|
8769
|
-
const absoluteChangedFiles = changedFiles.map((f) =>
|
|
9728
|
+
const absoluteChangedFiles = changedFiles.map((f) => path13.resolve(this.projectRoot, f));
|
|
8770
9729
|
const directSymbols = database.getSymbolsForFiles(absoluteChangedFiles, branchKey);
|
|
8771
9730
|
const directIds = directSymbols.map((s) => s.id);
|
|
8772
9731
|
const direction = opts.direction ?? "both";
|
|
@@ -8849,7 +9808,7 @@ var Indexer = class {
|
|
|
8849
9808
|
projectRoot: this.projectRoot,
|
|
8850
9809
|
baseBranch: this.baseBranch
|
|
8851
9810
|
});
|
|
8852
|
-
const otherAbsolute = otherChanged.files.map((f) =>
|
|
9811
|
+
const otherAbsolute = otherChanged.files.map((f) => path13.resolve(this.projectRoot, f));
|
|
8853
9812
|
const otherBranchKey = this.getBranchCatalogKeyFor(openPr.headRefName);
|
|
8854
9813
|
const otherSymbols = database.getSymbolsForFiles(otherAbsolute, otherBranchKey);
|
|
8855
9814
|
const otherLabels = /* @__PURE__ */ new Set();
|
|
@@ -8899,7 +9858,8 @@ var Indexer = class {
|
|
|
8899
9858
|
};
|
|
8900
9859
|
}
|
|
8901
9860
|
async getVisualizationData(options) {
|
|
8902
|
-
const { database, store } = await this.ensureInitialized();
|
|
9861
|
+
const { database, store, readIssues } = await this.ensureInitialized();
|
|
9862
|
+
this.requireReadableComponents(readIssues, "vectors", "database");
|
|
8903
9863
|
const seenSymbols = /* @__PURE__ */ new Map();
|
|
8904
9864
|
const seenEdges = /* @__PURE__ */ new Map();
|
|
8905
9865
|
for (const branchKey of this.getBranchCatalogKeys()) {
|
|
@@ -8912,12 +9872,12 @@ var Indexer = class {
|
|
|
8912
9872
|
if (meta.filePath) filePaths.add(meta.filePath);
|
|
8913
9873
|
}
|
|
8914
9874
|
const directory = options?.directory?.replace(/\/$/, "");
|
|
8915
|
-
const absoluteDirectoryFilter = directory ?
|
|
9875
|
+
const absoluteDirectoryFilter = directory ? path13.resolve(this.projectRoot, directory) : void 0;
|
|
8916
9876
|
for (const filePath of filePaths) {
|
|
8917
9877
|
if (directory) {
|
|
8918
|
-
const absoluteFilePath =
|
|
9878
|
+
const absoluteFilePath = path13.resolve(filePath);
|
|
8919
9879
|
const matchesRelative = filePath === directory || filePath.startsWith(directory + "/");
|
|
8920
|
-
const matchesProjectRelative = absoluteDirectoryFilter !== void 0 && (absoluteFilePath === absoluteDirectoryFilter || absoluteFilePath.startsWith(absoluteDirectoryFilter +
|
|
9880
|
+
const matchesProjectRelative = absoluteDirectoryFilter !== void 0 && (absoluteFilePath === absoluteDirectoryFilter || absoluteFilePath.startsWith(absoluteDirectoryFilter + path13.sep));
|
|
8921
9881
|
if (!matchesRelative && !matchesProjectRelative) {
|
|
8922
9882
|
continue;
|
|
8923
9883
|
}
|
|
@@ -8939,53 +9899,64 @@ var Indexer = class {
|
|
|
8939
9899
|
return { symbols: [...seenSymbols.values()], edges: [...seenEdges.values()] };
|
|
8940
9900
|
}
|
|
8941
9901
|
async close() {
|
|
8942
|
-
|
|
9902
|
+
this.database?.close();
|
|
9903
|
+
for (const database of this.retiredDatabases) {
|
|
9904
|
+
database.close();
|
|
9905
|
+
}
|
|
9906
|
+
this.retiredDatabases = [];
|
|
8943
9907
|
this.database = null;
|
|
8944
9908
|
this.store = null;
|
|
8945
9909
|
this.invertedIndex = null;
|
|
8946
9910
|
this.provider = null;
|
|
8947
9911
|
this.reranker = null;
|
|
9912
|
+
this.configuredProviderInfo = null;
|
|
9913
|
+
this.indexCompatibility = null;
|
|
9914
|
+
this.initializationMode = "none";
|
|
9915
|
+
this.readIssues = [];
|
|
9916
|
+
this.readerArtifactFingerprint = null;
|
|
9917
|
+
this.writerArtifactFingerprint = null;
|
|
9918
|
+
this.readerArtifactRetryAfter.clear();
|
|
8948
9919
|
}
|
|
8949
9920
|
};
|
|
8950
9921
|
|
|
8951
9922
|
// src/tools/knowledge-base-paths.ts
|
|
8952
|
-
import * as
|
|
9923
|
+
import * as path14 from "path";
|
|
8953
9924
|
function resolveConfigPathValue(value, baseDir) {
|
|
8954
9925
|
const trimmed = value.trim();
|
|
8955
9926
|
if (!trimmed) {
|
|
8956
9927
|
return trimmed;
|
|
8957
9928
|
}
|
|
8958
|
-
const absolutePath =
|
|
8959
|
-
return
|
|
9929
|
+
const absolutePath = path14.isAbsolute(trimmed) ? trimmed : path14.resolve(baseDir, trimmed);
|
|
9930
|
+
return path14.normalize(absolutePath);
|
|
8960
9931
|
}
|
|
8961
9932
|
function serializeConfigPathValue(value, baseDir) {
|
|
8962
9933
|
const trimmed = value.trim();
|
|
8963
9934
|
if (!trimmed) {
|
|
8964
9935
|
return trimmed;
|
|
8965
9936
|
}
|
|
8966
|
-
if (!
|
|
8967
|
-
return normalizePathSeparators(
|
|
9937
|
+
if (!path14.isAbsolute(trimmed)) {
|
|
9938
|
+
return normalizePathSeparators(path14.normalize(trimmed));
|
|
8968
9939
|
}
|
|
8969
|
-
const relativePath =
|
|
8970
|
-
if (!relativePath || !relativePath.startsWith("..") && !
|
|
8971
|
-
return normalizePathSeparators(
|
|
9940
|
+
const relativePath = path14.relative(baseDir, trimmed);
|
|
9941
|
+
if (!relativePath || !relativePath.startsWith("..") && !path14.isAbsolute(relativePath)) {
|
|
9942
|
+
return normalizePathSeparators(path14.normalize(relativePath || "."));
|
|
8972
9943
|
}
|
|
8973
|
-
return
|
|
9944
|
+
return path14.normalize(trimmed);
|
|
8974
9945
|
}
|
|
8975
9946
|
function resolveKnowledgeBasePath(value, projectRoot) {
|
|
8976
|
-
return
|
|
9947
|
+
return path14.isAbsolute(value) ? value : path14.resolve(projectRoot, value);
|
|
8977
9948
|
}
|
|
8978
9949
|
function normalizeKnowledgeBasePath2(value, projectRoot) {
|
|
8979
|
-
return
|
|
9950
|
+
return path14.normalize(resolveKnowledgeBasePath(value, projectRoot));
|
|
8980
9951
|
}
|
|
8981
9952
|
function hasMatchingKnowledgeBasePath(knowledgeBases, inputPath, projectRoot) {
|
|
8982
|
-
const normalizedInput =
|
|
9953
|
+
const normalizedInput = path14.normalize(inputPath);
|
|
8983
9954
|
return knowledgeBases.some((kb) => normalizeKnowledgeBasePath2(kb, projectRoot) === normalizedInput);
|
|
8984
9955
|
}
|
|
8985
9956
|
function findKnowledgeBasePathIndex(knowledgeBases, inputPath, projectRoot) {
|
|
8986
|
-
const normalizedInput =
|
|
9957
|
+
const normalizedInput = path14.normalize(inputPath);
|
|
8987
9958
|
return knowledgeBases.findIndex(
|
|
8988
|
-
(kb) =>
|
|
9959
|
+
(kb) => path14.normalize(kb) === normalizedInput || normalizeKnowledgeBasePath2(kb, projectRoot) === normalizedInput
|
|
8989
9960
|
);
|
|
8990
9961
|
}
|
|
8991
9962
|
|
|
@@ -9085,6 +10056,10 @@ function formatStatus(status) {
|
|
|
9085
10056
|
lines.push(`Failed batches: ${status.failedBatchesPath}`);
|
|
9086
10057
|
}
|
|
9087
10058
|
}
|
|
10059
|
+
if (status.warning) {
|
|
10060
|
+
lines.push("");
|
|
10061
|
+
lines.push(`INDEX WARNING: ${status.warning}`);
|
|
10062
|
+
}
|
|
9088
10063
|
if (status.compatibility && !status.compatibility.compatible) {
|
|
9089
10064
|
lines.push("");
|
|
9090
10065
|
lines.push(`COMPATIBILITY WARNING: ${status.compatibility.reason}`);
|
|
@@ -9190,16 +10165,16 @@ function formatCallGraphCallees(symbolId, callees, relationshipType) {
|
|
|
9190
10165
|
return `[${index + 1}] \u2192 ${edge.targetName} (${edge.callType})${confidence} at line ${edge.line}${edge.isResolved ? ` [resolved: ${edge.toSymbolId}]` : " [unresolved]"}`;
|
|
9191
10166
|
}).join("\n");
|
|
9192
10167
|
}
|
|
9193
|
-
function formatCallGraphPath(from, to,
|
|
9194
|
-
if (
|
|
10168
|
+
function formatCallGraphPath(from, to, path24) {
|
|
10169
|
+
if (path24.length === 0) {
|
|
9195
10170
|
return `No path found between "${from}" and "${to}". They may be in disconnected components, or the call graph index needs updating.`;
|
|
9196
10171
|
}
|
|
9197
|
-
const formatted =
|
|
10172
|
+
const formatted = path24.map((hop, index) => {
|
|
9198
10173
|
const prefix = index === 0 ? "[start]" : `--${hop.callType}-->`;
|
|
9199
10174
|
const location = hop.filePath ? ` (${hop.filePath}:${hop.line})` : "";
|
|
9200
10175
|
return `${prefix} ${hop.symbolName}${location}`;
|
|
9201
10176
|
});
|
|
9202
|
-
return `Path (${
|
|
10177
|
+
return `Path (${path24.length} hops):
|
|
9203
10178
|
${formatted.join("\n")}`;
|
|
9204
10179
|
}
|
|
9205
10180
|
function formatResultHeader(result, index) {
|
|
@@ -9239,8 +10214,8 @@ ${truncateContent(r.content)}
|
|
|
9239
10214
|
}
|
|
9240
10215
|
|
|
9241
10216
|
// src/tools/config-state.ts
|
|
9242
|
-
import { existsSync as
|
|
9243
|
-
import * as
|
|
10217
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync4, writeFileSync as writeFileSync4 } from "fs";
|
|
10218
|
+
import * as path15 from "path";
|
|
9244
10219
|
function normalizeKnowledgeBasePaths(config, projectRoot) {
|
|
9245
10220
|
const normalized = { ...config };
|
|
9246
10221
|
if (Array.isArray(normalized.knowledgeBases)) {
|
|
@@ -9267,10 +10242,10 @@ function loadEditableConfig(projectRoot, host = "opencode") {
|
|
|
9267
10242
|
}
|
|
9268
10243
|
function saveConfig(projectRoot, config, host = "opencode") {
|
|
9269
10244
|
const configPath = getConfigPath(projectRoot, host);
|
|
9270
|
-
const configDir =
|
|
9271
|
-
const configBaseDir =
|
|
9272
|
-
if (!
|
|
9273
|
-
|
|
10245
|
+
const configDir = path15.dirname(configPath);
|
|
10246
|
+
const configBaseDir = path15.dirname(configDir);
|
|
10247
|
+
if (!existsSync9(configDir)) {
|
|
10248
|
+
mkdirSync4(configDir, { recursive: true });
|
|
9274
10249
|
}
|
|
9275
10250
|
const serializableConfig = { ...config };
|
|
9276
10251
|
if (Array.isArray(serializableConfig.knowledgeBases)) {
|
|
@@ -9278,13 +10253,31 @@ function saveConfig(projectRoot, config, host = "opencode") {
|
|
|
9278
10253
|
(kb) => serializeConfigPathValue(kb, configBaseDir)
|
|
9279
10254
|
);
|
|
9280
10255
|
}
|
|
9281
|
-
|
|
10256
|
+
writeFileSync4(configPath, JSON.stringify(serializableConfig, null, 2) + "\n", "utf-8");
|
|
9282
10257
|
}
|
|
9283
10258
|
|
|
9284
10259
|
// src/tools/operations.ts
|
|
9285
10260
|
var indexerCache = /* @__PURE__ */ new Map();
|
|
9286
10261
|
var configCache = /* @__PURE__ */ new Map();
|
|
9287
10262
|
var defaultProjectRoots = /* @__PURE__ */ new Map();
|
|
10263
|
+
function getIndexBusyResult(error) {
|
|
10264
|
+
if (!isIndexLockContentionError(error)) return null;
|
|
10265
|
+
const owner = error.owner;
|
|
10266
|
+
const ownerText = owner ? `PID ${owner.pid}, operation ${owner.operation}, since ${owner.startedAt}` : "unreadable owner";
|
|
10267
|
+
if (error.reason === "legacy-lock") {
|
|
10268
|
+
return {
|
|
10269
|
+
kind: "busy",
|
|
10270
|
+
text: `INDEX_BUSY: legacy lock format detected (${ownerText}). Verify the PID and remove this lock manually only if it is stale.`
|
|
10271
|
+
};
|
|
10272
|
+
}
|
|
10273
|
+
if (error.reason === "unknown-owner") {
|
|
10274
|
+
return {
|
|
10275
|
+
kind: "busy",
|
|
10276
|
+
text: `INDEX_BUSY: unreadable or remote lock owner (${ownerText}). Automatic recovery was refused; manual verification is required.`
|
|
10277
|
+
};
|
|
10278
|
+
}
|
|
10279
|
+
return { kind: "busy", text: `INDEX_BUSY: another index operation is already in progress (${ownerText}).` };
|
|
10280
|
+
}
|
|
9288
10281
|
function getProjectRoot(projectRoot, host) {
|
|
9289
10282
|
if (projectRoot) {
|
|
9290
10283
|
return projectRoot;
|
|
@@ -9329,8 +10322,8 @@ function refreshIndexerForDirectory(projectRoot, host = "opencode", config = par
|
|
|
9329
10322
|
}
|
|
9330
10323
|
function shouldForceLocalizeProjectIndex(projectRoot, host = "opencode") {
|
|
9331
10324
|
const root = getProjectRoot(projectRoot, host);
|
|
9332
|
-
const localIndexPath =
|
|
9333
|
-
if (
|
|
10325
|
+
const localIndexPath = path16.join(root, getHostProjectIndexRelativePath(host));
|
|
10326
|
+
if (existsSync10(localIndexPath)) {
|
|
9334
10327
|
return false;
|
|
9335
10328
|
}
|
|
9336
10329
|
const inheritedIndexPath = resolveWorktreeFallbackProjectIndexPath(root, host);
|
|
@@ -9386,35 +10379,46 @@ async function getCallGraphPath(projectRoot, host, from, to, maxDepth) {
|
|
|
9386
10379
|
async function runIndexCodebase(projectRoot, host, args, onProgress) {
|
|
9387
10380
|
const root = getProjectRoot(projectRoot, host);
|
|
9388
10381
|
const key = getIndexerCacheKey(root, host);
|
|
9389
|
-
const cachedConfig = configCache.get(key);
|
|
9390
10382
|
let indexer = getIndexerForProject(root, host);
|
|
9391
|
-
|
|
9392
|
-
|
|
9393
|
-
|
|
9394
|
-
|
|
9395
|
-
|
|
9396
|
-
|
|
9397
|
-
|
|
9398
|
-
|
|
9399
|
-
|
|
9400
|
-
|
|
9401
|
-
|
|
9402
|
-
|
|
9403
|
-
|
|
9404
|
-
|
|
9405
|
-
|
|
9406
|
-
|
|
9407
|
-
|
|
9408
|
-
|
|
9409
|
-
|
|
9410
|
-
chunksProcessed: progress.chunksProcessed,
|
|
9411
|
-
totalChunks: progress.totalChunks,
|
|
9412
|
-
percentage: calculatePercentage(progress)
|
|
10383
|
+
const runtimeConfig = configCache.get(key);
|
|
10384
|
+
try {
|
|
10385
|
+
if (args.estimateOnly) {
|
|
10386
|
+
return { kind: "estimate", estimate: await indexer.estimateCost() };
|
|
10387
|
+
}
|
|
10388
|
+
const runIndex = async (target) => {
|
|
10389
|
+
const operation = args.force ? target.forceIndex.bind(target) : target.index.bind(target);
|
|
10390
|
+
return operation((progress) => {
|
|
10391
|
+
if (onProgress) {
|
|
10392
|
+
return onProgress(formatProgressTitle(progress), {
|
|
10393
|
+
phase: progress.phase,
|
|
10394
|
+
filesProcessed: progress.filesProcessed,
|
|
10395
|
+
totalFiles: progress.totalFiles,
|
|
10396
|
+
chunksProcessed: progress.chunksProcessed,
|
|
10397
|
+
totalChunks: progress.totalChunks,
|
|
10398
|
+
percentage: calculatePercentage(progress)
|
|
10399
|
+
});
|
|
10400
|
+
}
|
|
10401
|
+
return Promise.resolve();
|
|
9413
10402
|
});
|
|
10403
|
+
};
|
|
10404
|
+
let stats;
|
|
10405
|
+
if (args.force && shouldForceLocalizeProjectIndex(root, host)) {
|
|
10406
|
+
const inheritedIndexPath = resolveProjectIndexPath(root, runtimeConfig.scope, host);
|
|
10407
|
+
stats = await withIndexLock(inheritedIndexPath, "force-index", async () => {
|
|
10408
|
+
materializeLocalProjectConfig(root, loadProjectConfigLayer(root, host), host);
|
|
10409
|
+
refreshIndexerForDirectory(root, host, runtimeConfig);
|
|
10410
|
+
indexer = getIndexerForProject(root, host);
|
|
10411
|
+
return runIndex(indexer);
|
|
10412
|
+
}, { completeRecoveries: false });
|
|
10413
|
+
} else {
|
|
10414
|
+
stats = await runIndex(indexer);
|
|
9414
10415
|
}
|
|
9415
|
-
return
|
|
9416
|
-
})
|
|
9417
|
-
|
|
10416
|
+
return { kind: "stats", stats };
|
|
10417
|
+
} catch (error) {
|
|
10418
|
+
const busyResult = getIndexBusyResult(error);
|
|
10419
|
+
if (!busyResult) throw error;
|
|
10420
|
+
return busyResult;
|
|
10421
|
+
}
|
|
9418
10422
|
}
|
|
9419
10423
|
async function getIndexStatus(projectRoot, host) {
|
|
9420
10424
|
const indexer = getIndexerForProject(projectRoot, host);
|
|
@@ -9424,6 +10428,15 @@ async function getIndexHealthCheck(projectRoot, host) {
|
|
|
9424
10428
|
const indexer = getIndexerForProject(projectRoot, host);
|
|
9425
10429
|
return indexer.healthCheck();
|
|
9426
10430
|
}
|
|
10431
|
+
async function runIndexHealthCheck(projectRoot, host) {
|
|
10432
|
+
try {
|
|
10433
|
+
return { kind: "health", health: await getIndexHealthCheck(projectRoot, host) };
|
|
10434
|
+
} catch (error) {
|
|
10435
|
+
const busyResult = getIndexBusyResult(error);
|
|
10436
|
+
if (!busyResult) throw error;
|
|
10437
|
+
return busyResult;
|
|
10438
|
+
}
|
|
10439
|
+
}
|
|
9427
10440
|
async function getIndexMetrics(projectRoot, host) {
|
|
9428
10441
|
const indexer = getIndexerForProject(projectRoot, host);
|
|
9429
10442
|
const logger = indexer.getLogger();
|
|
@@ -9479,15 +10492,15 @@ async function getIndexLogs(projectRoot, host, args) {
|
|
|
9479
10492
|
function addKnowledgeBase(projectRoot, host, knowledgeBasePath) {
|
|
9480
10493
|
const root = getProjectRoot(projectRoot, host);
|
|
9481
10494
|
const inputPath = knowledgeBasePath.trim();
|
|
9482
|
-
const normalizedPath =
|
|
9483
|
-
|
|
10495
|
+
const normalizedPath = path16.resolve(
|
|
10496
|
+
path16.isAbsolute(inputPath) ? inputPath : resolveKnowledgeBasePath(inputPath, root)
|
|
9484
10497
|
);
|
|
9485
|
-
if (!
|
|
10498
|
+
if (!existsSync10(normalizedPath)) {
|
|
9486
10499
|
return `Error: Directory does not exist: ${normalizedPath}`;
|
|
9487
10500
|
}
|
|
9488
10501
|
let realPath;
|
|
9489
10502
|
try {
|
|
9490
|
-
realPath =
|
|
10503
|
+
realPath = realpathSync2(normalizedPath);
|
|
9491
10504
|
} catch {
|
|
9492
10505
|
return `Error: Cannot resolve path: ${normalizedPath}`;
|
|
9493
10506
|
}
|
|
@@ -9516,13 +10529,13 @@ function addKnowledgeBase(projectRoot, host, knowledgeBasePath) {
|
|
|
9516
10529
|
}
|
|
9517
10530
|
}
|
|
9518
10531
|
for (const dotDir of sensitiveDotDirs) {
|
|
9519
|
-
const sensitiveDir =
|
|
10532
|
+
const sensitiveDir = path16.join(homeDir, dotDir);
|
|
9520
10533
|
if (sensitiveDir && (realPath === sensitiveDir || realPath.startsWith(`${sensitiveDir}/`))) {
|
|
9521
10534
|
return `Error: Adding sensitive directory as knowledge base is not allowed: ${normalizedPath}`;
|
|
9522
10535
|
}
|
|
9523
10536
|
}
|
|
9524
10537
|
try {
|
|
9525
|
-
const stat4 =
|
|
10538
|
+
const stat4 = statSync4(normalizedPath);
|
|
9526
10539
|
if (!stat4.isDirectory()) {
|
|
9527
10540
|
return `Error: Path is not a directory: ${normalizedPath}`;
|
|
9528
10541
|
}
|
|
@@ -9562,7 +10575,7 @@ function listKnowledgeBases(projectRoot, host) {
|
|
|
9562
10575
|
for (let i = 0; i < knowledgeBases.length; i++) {
|
|
9563
10576
|
const kb = knowledgeBases[i];
|
|
9564
10577
|
const resolvedPath = resolveKnowledgeBasePath(kb, root);
|
|
9565
|
-
const exists =
|
|
10578
|
+
const exists = existsSync10(resolvedPath);
|
|
9566
10579
|
result += `[${i + 1}] ${kb}
|
|
9567
10580
|
`;
|
|
9568
10581
|
result += ` Resolved: ${resolvedPath}
|
|
@@ -9571,7 +10584,7 @@ function listKnowledgeBases(projectRoot, host) {
|
|
|
9571
10584
|
`;
|
|
9572
10585
|
if (exists) {
|
|
9573
10586
|
try {
|
|
9574
|
-
const stat4 =
|
|
10587
|
+
const stat4 = statSync4(resolvedPath);
|
|
9575
10588
|
result += ` Type: ${stat4.isDirectory() ? "Directory" : "File"}
|
|
9576
10589
|
`;
|
|
9577
10590
|
} catch {
|
|
@@ -9579,7 +10592,7 @@ function listKnowledgeBases(projectRoot, host) {
|
|
|
9579
10592
|
}
|
|
9580
10593
|
result += "\n";
|
|
9581
10594
|
}
|
|
9582
|
-
const hasHostConfig =
|
|
10595
|
+
const hasHostConfig = existsSync10(path16.join(root, getHostProjectConfigRelativePath(host)));
|
|
9583
10596
|
if (hasHostConfig) {
|
|
9584
10597
|
result += `
|
|
9585
10598
|
Config sources: 1 file(s).`;
|
|
@@ -9702,7 +10715,7 @@ var ReaddirpStream = class extends Readable {
|
|
|
9702
10715
|
this._directoryFilter = normalizeFilter(opts.directoryFilter);
|
|
9703
10716
|
const statMethod = opts.lstat ? lstat : stat;
|
|
9704
10717
|
if (wantBigintFsStats) {
|
|
9705
|
-
this._stat = (
|
|
10718
|
+
this._stat = (path24) => statMethod(path24, { bigint: true });
|
|
9706
10719
|
} else {
|
|
9707
10720
|
this._stat = statMethod;
|
|
9708
10721
|
}
|
|
@@ -9727,8 +10740,8 @@ var ReaddirpStream = class extends Readable {
|
|
|
9727
10740
|
const par = this.parent;
|
|
9728
10741
|
const fil = par && par.files;
|
|
9729
10742
|
if (fil && fil.length > 0) {
|
|
9730
|
-
const { path:
|
|
9731
|
-
const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent,
|
|
10743
|
+
const { path: path24, depth } = par;
|
|
10744
|
+
const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path24));
|
|
9732
10745
|
const awaited = await Promise.all(slice);
|
|
9733
10746
|
for (const entry of awaited) {
|
|
9734
10747
|
if (!entry)
|
|
@@ -9768,20 +10781,20 @@ var ReaddirpStream = class extends Readable {
|
|
|
9768
10781
|
this.reading = false;
|
|
9769
10782
|
}
|
|
9770
10783
|
}
|
|
9771
|
-
async _exploreDir(
|
|
10784
|
+
async _exploreDir(path24, depth) {
|
|
9772
10785
|
let files;
|
|
9773
10786
|
try {
|
|
9774
|
-
files = await readdir(
|
|
10787
|
+
files = await readdir(path24, this._rdOptions);
|
|
9775
10788
|
} catch (error) {
|
|
9776
10789
|
this._onError(error);
|
|
9777
10790
|
}
|
|
9778
|
-
return { files, depth, path:
|
|
10791
|
+
return { files, depth, path: path24 };
|
|
9779
10792
|
}
|
|
9780
|
-
async _formatEntry(dirent,
|
|
10793
|
+
async _formatEntry(dirent, path24) {
|
|
9781
10794
|
let entry;
|
|
9782
10795
|
const basename5 = this._isDirent ? dirent.name : dirent;
|
|
9783
10796
|
try {
|
|
9784
|
-
const fullPath = presolve(pjoin(
|
|
10797
|
+
const fullPath = presolve(pjoin(path24, basename5));
|
|
9785
10798
|
entry = { path: prelative(this._root, fullPath), fullPath, basename: basename5 };
|
|
9786
10799
|
entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
|
|
9787
10800
|
} catch (err) {
|
|
@@ -10181,16 +11194,16 @@ var delFromSet = (main, prop, item) => {
|
|
|
10181
11194
|
};
|
|
10182
11195
|
var isEmptySet = (val) => val instanceof Set ? val.size === 0 : !val;
|
|
10183
11196
|
var FsWatchInstances = /* @__PURE__ */ new Map();
|
|
10184
|
-
function createFsWatchInstance(
|
|
11197
|
+
function createFsWatchInstance(path24, options, listener, errHandler, emitRaw) {
|
|
10185
11198
|
const handleEvent = (rawEvent, evPath) => {
|
|
10186
|
-
listener(
|
|
10187
|
-
emitRaw(rawEvent, evPath, { watchedPath:
|
|
10188
|
-
if (evPath &&
|
|
10189
|
-
fsWatchBroadcast(sp.resolve(
|
|
11199
|
+
listener(path24);
|
|
11200
|
+
emitRaw(rawEvent, evPath, { watchedPath: path24 });
|
|
11201
|
+
if (evPath && path24 !== evPath) {
|
|
11202
|
+
fsWatchBroadcast(sp.resolve(path24, evPath), KEY_LISTENERS, sp.join(path24, evPath));
|
|
10190
11203
|
}
|
|
10191
11204
|
};
|
|
10192
11205
|
try {
|
|
10193
|
-
return fs_watch(
|
|
11206
|
+
return fs_watch(path24, {
|
|
10194
11207
|
persistent: options.persistent
|
|
10195
11208
|
}, handleEvent);
|
|
10196
11209
|
} catch (error) {
|
|
@@ -10206,12 +11219,12 @@ var fsWatchBroadcast = (fullPath, listenerType, val1, val2, val3) => {
|
|
|
10206
11219
|
listener(val1, val2, val3);
|
|
10207
11220
|
});
|
|
10208
11221
|
};
|
|
10209
|
-
var setFsWatchListener = (
|
|
11222
|
+
var setFsWatchListener = (path24, fullPath, options, handlers) => {
|
|
10210
11223
|
const { listener, errHandler, rawEmitter } = handlers;
|
|
10211
11224
|
let cont = FsWatchInstances.get(fullPath);
|
|
10212
11225
|
let watcher;
|
|
10213
11226
|
if (!options.persistent) {
|
|
10214
|
-
watcher = createFsWatchInstance(
|
|
11227
|
+
watcher = createFsWatchInstance(path24, options, listener, errHandler, rawEmitter);
|
|
10215
11228
|
if (!watcher)
|
|
10216
11229
|
return;
|
|
10217
11230
|
return watcher.close.bind(watcher);
|
|
@@ -10222,7 +11235,7 @@ var setFsWatchListener = (path23, fullPath, options, handlers) => {
|
|
|
10222
11235
|
addAndConvert(cont, KEY_RAW, rawEmitter);
|
|
10223
11236
|
} else {
|
|
10224
11237
|
watcher = createFsWatchInstance(
|
|
10225
|
-
|
|
11238
|
+
path24,
|
|
10226
11239
|
options,
|
|
10227
11240
|
fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS),
|
|
10228
11241
|
errHandler,
|
|
@@ -10237,7 +11250,7 @@ var setFsWatchListener = (path23, fullPath, options, handlers) => {
|
|
|
10237
11250
|
cont.watcherUnusable = true;
|
|
10238
11251
|
if (isWindows && error.code === "EPERM") {
|
|
10239
11252
|
try {
|
|
10240
|
-
const fd = await open(
|
|
11253
|
+
const fd = await open(path24, "r");
|
|
10241
11254
|
await fd.close();
|
|
10242
11255
|
broadcastErr(error);
|
|
10243
11256
|
} catch (err) {
|
|
@@ -10268,7 +11281,7 @@ var setFsWatchListener = (path23, fullPath, options, handlers) => {
|
|
|
10268
11281
|
};
|
|
10269
11282
|
};
|
|
10270
11283
|
var FsWatchFileInstances = /* @__PURE__ */ new Map();
|
|
10271
|
-
var setFsWatchFileListener = (
|
|
11284
|
+
var setFsWatchFileListener = (path24, fullPath, options, handlers) => {
|
|
10272
11285
|
const { listener, rawEmitter } = handlers;
|
|
10273
11286
|
let cont = FsWatchFileInstances.get(fullPath);
|
|
10274
11287
|
const copts = cont && cont.options;
|
|
@@ -10290,7 +11303,7 @@ var setFsWatchFileListener = (path23, fullPath, options, handlers) => {
|
|
|
10290
11303
|
});
|
|
10291
11304
|
const currmtime = curr.mtimeMs;
|
|
10292
11305
|
if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {
|
|
10293
|
-
foreach(cont.listeners, (listener2) => listener2(
|
|
11306
|
+
foreach(cont.listeners, (listener2) => listener2(path24, curr));
|
|
10294
11307
|
}
|
|
10295
11308
|
})
|
|
10296
11309
|
};
|
|
@@ -10320,13 +11333,13 @@ var NodeFsHandler = class {
|
|
|
10320
11333
|
* @param listener on fs change
|
|
10321
11334
|
* @returns closer for the watcher instance
|
|
10322
11335
|
*/
|
|
10323
|
-
_watchWithNodeFs(
|
|
11336
|
+
_watchWithNodeFs(path24, listener) {
|
|
10324
11337
|
const opts = this.fsw.options;
|
|
10325
|
-
const directory = sp.dirname(
|
|
10326
|
-
const basename5 = sp.basename(
|
|
11338
|
+
const directory = sp.dirname(path24);
|
|
11339
|
+
const basename5 = sp.basename(path24);
|
|
10327
11340
|
const parent = this.fsw._getWatchedDir(directory);
|
|
10328
11341
|
parent.add(basename5);
|
|
10329
|
-
const absolutePath = sp.resolve(
|
|
11342
|
+
const absolutePath = sp.resolve(path24);
|
|
10330
11343
|
const options = {
|
|
10331
11344
|
persistent: opts.persistent
|
|
10332
11345
|
};
|
|
@@ -10336,12 +11349,12 @@ var NodeFsHandler = class {
|
|
|
10336
11349
|
if (opts.usePolling) {
|
|
10337
11350
|
const enableBin = opts.interval !== opts.binaryInterval;
|
|
10338
11351
|
options.interval = enableBin && isBinaryPath(basename5) ? opts.binaryInterval : opts.interval;
|
|
10339
|
-
closer = setFsWatchFileListener(
|
|
11352
|
+
closer = setFsWatchFileListener(path24, absolutePath, options, {
|
|
10340
11353
|
listener,
|
|
10341
11354
|
rawEmitter: this.fsw._emitRaw
|
|
10342
11355
|
});
|
|
10343
11356
|
} else {
|
|
10344
|
-
closer = setFsWatchListener(
|
|
11357
|
+
closer = setFsWatchListener(path24, absolutePath, options, {
|
|
10345
11358
|
listener,
|
|
10346
11359
|
errHandler: this._boundHandleError,
|
|
10347
11360
|
rawEmitter: this.fsw._emitRaw
|
|
@@ -10363,7 +11376,7 @@ var NodeFsHandler = class {
|
|
|
10363
11376
|
let prevStats = stats;
|
|
10364
11377
|
if (parent.has(basename5))
|
|
10365
11378
|
return;
|
|
10366
|
-
const listener = async (
|
|
11379
|
+
const listener = async (path24, newStats) => {
|
|
10367
11380
|
if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5))
|
|
10368
11381
|
return;
|
|
10369
11382
|
if (!newStats || newStats.mtimeMs === 0) {
|
|
@@ -10377,11 +11390,11 @@ var NodeFsHandler = class {
|
|
|
10377
11390
|
this.fsw._emit(EV.CHANGE, file, newStats2);
|
|
10378
11391
|
}
|
|
10379
11392
|
if ((isMacos || isLinux || isFreeBSD) && prevStats.ino !== newStats2.ino) {
|
|
10380
|
-
this.fsw._closeFile(
|
|
11393
|
+
this.fsw._closeFile(path24);
|
|
10381
11394
|
prevStats = newStats2;
|
|
10382
11395
|
const closer2 = this._watchWithNodeFs(file, listener);
|
|
10383
11396
|
if (closer2)
|
|
10384
|
-
this.fsw._addPathCloser(
|
|
11397
|
+
this.fsw._addPathCloser(path24, closer2);
|
|
10385
11398
|
} else {
|
|
10386
11399
|
prevStats = newStats2;
|
|
10387
11400
|
}
|
|
@@ -10413,7 +11426,7 @@ var NodeFsHandler = class {
|
|
|
10413
11426
|
* @param item basename of this item
|
|
10414
11427
|
* @returns true if no more processing is needed for this entry.
|
|
10415
11428
|
*/
|
|
10416
|
-
async _handleSymlink(entry, directory,
|
|
11429
|
+
async _handleSymlink(entry, directory, path24, item) {
|
|
10417
11430
|
if (this.fsw.closed) {
|
|
10418
11431
|
return;
|
|
10419
11432
|
}
|
|
@@ -10423,7 +11436,7 @@ var NodeFsHandler = class {
|
|
|
10423
11436
|
this.fsw._incrReadyCount();
|
|
10424
11437
|
let linkPath;
|
|
10425
11438
|
try {
|
|
10426
|
-
linkPath = await fsrealpath(
|
|
11439
|
+
linkPath = await fsrealpath(path24);
|
|
10427
11440
|
} catch (e) {
|
|
10428
11441
|
this.fsw._emitReady();
|
|
10429
11442
|
return true;
|
|
@@ -10433,12 +11446,12 @@ var NodeFsHandler = class {
|
|
|
10433
11446
|
if (dir.has(item)) {
|
|
10434
11447
|
if (this.fsw._symlinkPaths.get(full) !== linkPath) {
|
|
10435
11448
|
this.fsw._symlinkPaths.set(full, linkPath);
|
|
10436
|
-
this.fsw._emit(EV.CHANGE,
|
|
11449
|
+
this.fsw._emit(EV.CHANGE, path24, entry.stats);
|
|
10437
11450
|
}
|
|
10438
11451
|
} else {
|
|
10439
11452
|
dir.add(item);
|
|
10440
11453
|
this.fsw._symlinkPaths.set(full, linkPath);
|
|
10441
|
-
this.fsw._emit(EV.ADD,
|
|
11454
|
+
this.fsw._emit(EV.ADD, path24, entry.stats);
|
|
10442
11455
|
}
|
|
10443
11456
|
this.fsw._emitReady();
|
|
10444
11457
|
return true;
|
|
@@ -10468,9 +11481,9 @@ var NodeFsHandler = class {
|
|
|
10468
11481
|
return;
|
|
10469
11482
|
}
|
|
10470
11483
|
const item = entry.path;
|
|
10471
|
-
let
|
|
11484
|
+
let path24 = sp.join(directory, item);
|
|
10472
11485
|
current.add(item);
|
|
10473
|
-
if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory,
|
|
11486
|
+
if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path24, item)) {
|
|
10474
11487
|
return;
|
|
10475
11488
|
}
|
|
10476
11489
|
if (this.fsw.closed) {
|
|
@@ -10479,8 +11492,8 @@ var NodeFsHandler = class {
|
|
|
10479
11492
|
}
|
|
10480
11493
|
if (item === target || !target && !previous.has(item)) {
|
|
10481
11494
|
this.fsw._incrReadyCount();
|
|
10482
|
-
|
|
10483
|
-
this._addToNodeFs(
|
|
11495
|
+
path24 = sp.join(dir, sp.relative(dir, path24));
|
|
11496
|
+
this._addToNodeFs(path24, initialAdd, wh, depth + 1);
|
|
10484
11497
|
}
|
|
10485
11498
|
}).on(EV.ERROR, this._boundHandleError);
|
|
10486
11499
|
return new Promise((resolve13, reject) => {
|
|
@@ -10549,13 +11562,13 @@ var NodeFsHandler = class {
|
|
|
10549
11562
|
* @param depth Child path actually targeted for watch
|
|
10550
11563
|
* @param target Child path actually targeted for watch
|
|
10551
11564
|
*/
|
|
10552
|
-
async _addToNodeFs(
|
|
11565
|
+
async _addToNodeFs(path24, initialAdd, priorWh, depth, target) {
|
|
10553
11566
|
const ready = this.fsw._emitReady;
|
|
10554
|
-
if (this.fsw._isIgnored(
|
|
11567
|
+
if (this.fsw._isIgnored(path24) || this.fsw.closed) {
|
|
10555
11568
|
ready();
|
|
10556
11569
|
return false;
|
|
10557
11570
|
}
|
|
10558
|
-
const wh = this.fsw._getWatchHelpers(
|
|
11571
|
+
const wh = this.fsw._getWatchHelpers(path24);
|
|
10559
11572
|
if (priorWh) {
|
|
10560
11573
|
wh.filterPath = (entry) => priorWh.filterPath(entry);
|
|
10561
11574
|
wh.filterDir = (entry) => priorWh.filterDir(entry);
|
|
@@ -10571,8 +11584,8 @@ var NodeFsHandler = class {
|
|
|
10571
11584
|
const follow = this.fsw.options.followSymlinks;
|
|
10572
11585
|
let closer;
|
|
10573
11586
|
if (stats.isDirectory()) {
|
|
10574
|
-
const absPath = sp.resolve(
|
|
10575
|
-
const targetPath = follow ? await fsrealpath(
|
|
11587
|
+
const absPath = sp.resolve(path24);
|
|
11588
|
+
const targetPath = follow ? await fsrealpath(path24) : path24;
|
|
10576
11589
|
if (this.fsw.closed)
|
|
10577
11590
|
return;
|
|
10578
11591
|
closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
|
|
@@ -10582,29 +11595,29 @@ var NodeFsHandler = class {
|
|
|
10582
11595
|
this.fsw._symlinkPaths.set(absPath, targetPath);
|
|
10583
11596
|
}
|
|
10584
11597
|
} else if (stats.isSymbolicLink()) {
|
|
10585
|
-
const targetPath = follow ? await fsrealpath(
|
|
11598
|
+
const targetPath = follow ? await fsrealpath(path24) : path24;
|
|
10586
11599
|
if (this.fsw.closed)
|
|
10587
11600
|
return;
|
|
10588
11601
|
const parent = sp.dirname(wh.watchPath);
|
|
10589
11602
|
this.fsw._getWatchedDir(parent).add(wh.watchPath);
|
|
10590
11603
|
this.fsw._emit(EV.ADD, wh.watchPath, stats);
|
|
10591
|
-
closer = await this._handleDir(parent, stats, initialAdd, depth,
|
|
11604
|
+
closer = await this._handleDir(parent, stats, initialAdd, depth, path24, wh, targetPath);
|
|
10592
11605
|
if (this.fsw.closed)
|
|
10593
11606
|
return;
|
|
10594
11607
|
if (targetPath !== void 0) {
|
|
10595
|
-
this.fsw._symlinkPaths.set(sp.resolve(
|
|
11608
|
+
this.fsw._symlinkPaths.set(sp.resolve(path24), targetPath);
|
|
10596
11609
|
}
|
|
10597
11610
|
} else {
|
|
10598
11611
|
closer = this._handleFile(wh.watchPath, stats, initialAdd);
|
|
10599
11612
|
}
|
|
10600
11613
|
ready();
|
|
10601
11614
|
if (closer)
|
|
10602
|
-
this.fsw._addPathCloser(
|
|
11615
|
+
this.fsw._addPathCloser(path24, closer);
|
|
10603
11616
|
return false;
|
|
10604
11617
|
} catch (error) {
|
|
10605
11618
|
if (this.fsw._handleError(error)) {
|
|
10606
11619
|
ready();
|
|
10607
|
-
return
|
|
11620
|
+
return path24;
|
|
10608
11621
|
}
|
|
10609
11622
|
}
|
|
10610
11623
|
}
|
|
@@ -10647,24 +11660,24 @@ function createPattern(matcher) {
|
|
|
10647
11660
|
}
|
|
10648
11661
|
return () => false;
|
|
10649
11662
|
}
|
|
10650
|
-
function normalizePath(
|
|
10651
|
-
if (typeof
|
|
11663
|
+
function normalizePath(path24) {
|
|
11664
|
+
if (typeof path24 !== "string")
|
|
10652
11665
|
throw new Error("string expected");
|
|
10653
|
-
|
|
10654
|
-
|
|
11666
|
+
path24 = sp2.normalize(path24);
|
|
11667
|
+
path24 = path24.replace(/\\/g, "/");
|
|
10655
11668
|
let prepend = false;
|
|
10656
|
-
if (
|
|
11669
|
+
if (path24.startsWith("//"))
|
|
10657
11670
|
prepend = true;
|
|
10658
|
-
|
|
11671
|
+
path24 = path24.replace(DOUBLE_SLASH_RE, "/");
|
|
10659
11672
|
if (prepend)
|
|
10660
|
-
|
|
10661
|
-
return
|
|
11673
|
+
path24 = "/" + path24;
|
|
11674
|
+
return path24;
|
|
10662
11675
|
}
|
|
10663
11676
|
function matchPatterns(patterns, testString, stats) {
|
|
10664
|
-
const
|
|
11677
|
+
const path24 = normalizePath(testString);
|
|
10665
11678
|
for (let index = 0; index < patterns.length; index++) {
|
|
10666
11679
|
const pattern = patterns[index];
|
|
10667
|
-
if (pattern(
|
|
11680
|
+
if (pattern(path24, stats)) {
|
|
10668
11681
|
return true;
|
|
10669
11682
|
}
|
|
10670
11683
|
}
|
|
@@ -10702,19 +11715,19 @@ var toUnix = (string) => {
|
|
|
10702
11715
|
}
|
|
10703
11716
|
return str;
|
|
10704
11717
|
};
|
|
10705
|
-
var normalizePathToUnix = (
|
|
10706
|
-
var normalizeIgnored = (cwd = "") => (
|
|
10707
|
-
if (typeof
|
|
10708
|
-
return normalizePathToUnix(sp2.isAbsolute(
|
|
11718
|
+
var normalizePathToUnix = (path24) => toUnix(sp2.normalize(toUnix(path24)));
|
|
11719
|
+
var normalizeIgnored = (cwd = "") => (path24) => {
|
|
11720
|
+
if (typeof path24 === "string") {
|
|
11721
|
+
return normalizePathToUnix(sp2.isAbsolute(path24) ? path24 : sp2.join(cwd, path24));
|
|
10709
11722
|
} else {
|
|
10710
|
-
return
|
|
11723
|
+
return path24;
|
|
10711
11724
|
}
|
|
10712
11725
|
};
|
|
10713
|
-
var getAbsolutePath = (
|
|
10714
|
-
if (sp2.isAbsolute(
|
|
10715
|
-
return
|
|
11726
|
+
var getAbsolutePath = (path24, cwd) => {
|
|
11727
|
+
if (sp2.isAbsolute(path24)) {
|
|
11728
|
+
return path24;
|
|
10716
11729
|
}
|
|
10717
|
-
return sp2.join(cwd,
|
|
11730
|
+
return sp2.join(cwd, path24);
|
|
10718
11731
|
};
|
|
10719
11732
|
var EMPTY_SET = Object.freeze(/* @__PURE__ */ new Set());
|
|
10720
11733
|
var DirEntry = class {
|
|
@@ -10779,10 +11792,10 @@ var WatchHelper = class {
|
|
|
10779
11792
|
dirParts;
|
|
10780
11793
|
followSymlinks;
|
|
10781
11794
|
statMethod;
|
|
10782
|
-
constructor(
|
|
11795
|
+
constructor(path24, follow, fsw) {
|
|
10783
11796
|
this.fsw = fsw;
|
|
10784
|
-
const watchPath =
|
|
10785
|
-
this.path =
|
|
11797
|
+
const watchPath = path24;
|
|
11798
|
+
this.path = path24 = path24.replace(REPLACER_RE, "");
|
|
10786
11799
|
this.watchPath = watchPath;
|
|
10787
11800
|
this.fullWatchPath = sp2.resolve(watchPath);
|
|
10788
11801
|
this.dirParts = [];
|
|
@@ -10922,20 +11935,20 @@ var FSWatcher = class extends EventEmitter2 {
|
|
|
10922
11935
|
this._closePromise = void 0;
|
|
10923
11936
|
let paths = unifyPaths(paths_);
|
|
10924
11937
|
if (cwd) {
|
|
10925
|
-
paths = paths.map((
|
|
10926
|
-
const absPath = getAbsolutePath(
|
|
11938
|
+
paths = paths.map((path24) => {
|
|
11939
|
+
const absPath = getAbsolutePath(path24, cwd);
|
|
10927
11940
|
return absPath;
|
|
10928
11941
|
});
|
|
10929
11942
|
}
|
|
10930
|
-
paths.forEach((
|
|
10931
|
-
this._removeIgnoredPath(
|
|
11943
|
+
paths.forEach((path24) => {
|
|
11944
|
+
this._removeIgnoredPath(path24);
|
|
10932
11945
|
});
|
|
10933
11946
|
this._userIgnored = void 0;
|
|
10934
11947
|
if (!this._readyCount)
|
|
10935
11948
|
this._readyCount = 0;
|
|
10936
11949
|
this._readyCount += paths.length;
|
|
10937
|
-
Promise.all(paths.map(async (
|
|
10938
|
-
const res = await this._nodeFsHandler._addToNodeFs(
|
|
11950
|
+
Promise.all(paths.map(async (path24) => {
|
|
11951
|
+
const res = await this._nodeFsHandler._addToNodeFs(path24, !_internal, void 0, 0, _origAdd);
|
|
10939
11952
|
if (res)
|
|
10940
11953
|
this._emitReady();
|
|
10941
11954
|
return res;
|
|
@@ -10957,17 +11970,17 @@ var FSWatcher = class extends EventEmitter2 {
|
|
|
10957
11970
|
return this;
|
|
10958
11971
|
const paths = unifyPaths(paths_);
|
|
10959
11972
|
const { cwd } = this.options;
|
|
10960
|
-
paths.forEach((
|
|
10961
|
-
if (!sp2.isAbsolute(
|
|
11973
|
+
paths.forEach((path24) => {
|
|
11974
|
+
if (!sp2.isAbsolute(path24) && !this._closers.has(path24)) {
|
|
10962
11975
|
if (cwd)
|
|
10963
|
-
|
|
10964
|
-
|
|
11976
|
+
path24 = sp2.join(cwd, path24);
|
|
11977
|
+
path24 = sp2.resolve(path24);
|
|
10965
11978
|
}
|
|
10966
|
-
this._closePath(
|
|
10967
|
-
this._addIgnoredPath(
|
|
10968
|
-
if (this._watched.has(
|
|
11979
|
+
this._closePath(path24);
|
|
11980
|
+
this._addIgnoredPath(path24);
|
|
11981
|
+
if (this._watched.has(path24)) {
|
|
10969
11982
|
this._addIgnoredPath({
|
|
10970
|
-
path:
|
|
11983
|
+
path: path24,
|
|
10971
11984
|
recursive: true
|
|
10972
11985
|
});
|
|
10973
11986
|
}
|
|
@@ -11031,38 +12044,38 @@ var FSWatcher = class extends EventEmitter2 {
|
|
|
11031
12044
|
* @param stats arguments to be passed with event
|
|
11032
12045
|
* @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag
|
|
11033
12046
|
*/
|
|
11034
|
-
async _emit(event,
|
|
12047
|
+
async _emit(event, path24, stats) {
|
|
11035
12048
|
if (this.closed)
|
|
11036
12049
|
return;
|
|
11037
12050
|
const opts = this.options;
|
|
11038
12051
|
if (isWindows)
|
|
11039
|
-
|
|
12052
|
+
path24 = sp2.normalize(path24);
|
|
11040
12053
|
if (opts.cwd)
|
|
11041
|
-
|
|
11042
|
-
const args = [
|
|
12054
|
+
path24 = sp2.relative(opts.cwd, path24);
|
|
12055
|
+
const args = [path24];
|
|
11043
12056
|
if (stats != null)
|
|
11044
12057
|
args.push(stats);
|
|
11045
12058
|
const awf = opts.awaitWriteFinish;
|
|
11046
12059
|
let pw;
|
|
11047
|
-
if (awf && (pw = this._pendingWrites.get(
|
|
12060
|
+
if (awf && (pw = this._pendingWrites.get(path24))) {
|
|
11048
12061
|
pw.lastChange = /* @__PURE__ */ new Date();
|
|
11049
12062
|
return this;
|
|
11050
12063
|
}
|
|
11051
12064
|
if (opts.atomic) {
|
|
11052
12065
|
if (event === EVENTS.UNLINK) {
|
|
11053
|
-
this._pendingUnlinks.set(
|
|
12066
|
+
this._pendingUnlinks.set(path24, [event, ...args]);
|
|
11054
12067
|
setTimeout(() => {
|
|
11055
|
-
this._pendingUnlinks.forEach((entry,
|
|
12068
|
+
this._pendingUnlinks.forEach((entry, path25) => {
|
|
11056
12069
|
this.emit(...entry);
|
|
11057
12070
|
this.emit(EVENTS.ALL, ...entry);
|
|
11058
|
-
this._pendingUnlinks.delete(
|
|
12071
|
+
this._pendingUnlinks.delete(path25);
|
|
11059
12072
|
});
|
|
11060
12073
|
}, typeof opts.atomic === "number" ? opts.atomic : 100);
|
|
11061
12074
|
return this;
|
|
11062
12075
|
}
|
|
11063
|
-
if (event === EVENTS.ADD && this._pendingUnlinks.has(
|
|
12076
|
+
if (event === EVENTS.ADD && this._pendingUnlinks.has(path24)) {
|
|
11064
12077
|
event = EVENTS.CHANGE;
|
|
11065
|
-
this._pendingUnlinks.delete(
|
|
12078
|
+
this._pendingUnlinks.delete(path24);
|
|
11066
12079
|
}
|
|
11067
12080
|
}
|
|
11068
12081
|
if (awf && (event === EVENTS.ADD || event === EVENTS.CHANGE) && this._readyEmitted) {
|
|
@@ -11080,16 +12093,16 @@ var FSWatcher = class extends EventEmitter2 {
|
|
|
11080
12093
|
this.emitWithAll(event, args);
|
|
11081
12094
|
}
|
|
11082
12095
|
};
|
|
11083
|
-
this._awaitWriteFinish(
|
|
12096
|
+
this._awaitWriteFinish(path24, awf.stabilityThreshold, event, awfEmit);
|
|
11084
12097
|
return this;
|
|
11085
12098
|
}
|
|
11086
12099
|
if (event === EVENTS.CHANGE) {
|
|
11087
|
-
const isThrottled = !this._throttle(EVENTS.CHANGE,
|
|
12100
|
+
const isThrottled = !this._throttle(EVENTS.CHANGE, path24, 50);
|
|
11088
12101
|
if (isThrottled)
|
|
11089
12102
|
return this;
|
|
11090
12103
|
}
|
|
11091
12104
|
if (opts.alwaysStat && stats === void 0 && (event === EVENTS.ADD || event === EVENTS.ADD_DIR || event === EVENTS.CHANGE)) {
|
|
11092
|
-
const fullPath = opts.cwd ? sp2.join(opts.cwd,
|
|
12105
|
+
const fullPath = opts.cwd ? sp2.join(opts.cwd, path24) : path24;
|
|
11093
12106
|
let stats2;
|
|
11094
12107
|
try {
|
|
11095
12108
|
stats2 = await stat3(fullPath);
|
|
@@ -11120,23 +12133,23 @@ var FSWatcher = class extends EventEmitter2 {
|
|
|
11120
12133
|
* @param timeout duration of time to suppress duplicate actions
|
|
11121
12134
|
* @returns tracking object or false if action should be suppressed
|
|
11122
12135
|
*/
|
|
11123
|
-
_throttle(actionType,
|
|
12136
|
+
_throttle(actionType, path24, timeout) {
|
|
11124
12137
|
if (!this._throttled.has(actionType)) {
|
|
11125
12138
|
this._throttled.set(actionType, /* @__PURE__ */ new Map());
|
|
11126
12139
|
}
|
|
11127
12140
|
const action = this._throttled.get(actionType);
|
|
11128
12141
|
if (!action)
|
|
11129
12142
|
throw new Error("invalid throttle");
|
|
11130
|
-
const actionPath = action.get(
|
|
12143
|
+
const actionPath = action.get(path24);
|
|
11131
12144
|
if (actionPath) {
|
|
11132
12145
|
actionPath.count++;
|
|
11133
12146
|
return false;
|
|
11134
12147
|
}
|
|
11135
12148
|
let timeoutObject;
|
|
11136
12149
|
const clear = () => {
|
|
11137
|
-
const item = action.get(
|
|
12150
|
+
const item = action.get(path24);
|
|
11138
12151
|
const count = item ? item.count : 0;
|
|
11139
|
-
action.delete(
|
|
12152
|
+
action.delete(path24);
|
|
11140
12153
|
clearTimeout(timeoutObject);
|
|
11141
12154
|
if (item)
|
|
11142
12155
|
clearTimeout(item.timeoutObject);
|
|
@@ -11144,7 +12157,7 @@ var FSWatcher = class extends EventEmitter2 {
|
|
|
11144
12157
|
};
|
|
11145
12158
|
timeoutObject = setTimeout(clear, timeout);
|
|
11146
12159
|
const thr = { timeoutObject, clear, count: 0 };
|
|
11147
|
-
action.set(
|
|
12160
|
+
action.set(path24, thr);
|
|
11148
12161
|
return thr;
|
|
11149
12162
|
}
|
|
11150
12163
|
_incrReadyCount() {
|
|
@@ -11158,44 +12171,44 @@ var FSWatcher = class extends EventEmitter2 {
|
|
|
11158
12171
|
* @param event
|
|
11159
12172
|
* @param awfEmit Callback to be called when ready for event to be emitted.
|
|
11160
12173
|
*/
|
|
11161
|
-
_awaitWriteFinish(
|
|
12174
|
+
_awaitWriteFinish(path24, threshold, event, awfEmit) {
|
|
11162
12175
|
const awf = this.options.awaitWriteFinish;
|
|
11163
12176
|
if (typeof awf !== "object")
|
|
11164
12177
|
return;
|
|
11165
12178
|
const pollInterval = awf.pollInterval;
|
|
11166
12179
|
let timeoutHandler;
|
|
11167
|
-
let fullPath =
|
|
11168
|
-
if (this.options.cwd && !sp2.isAbsolute(
|
|
11169
|
-
fullPath = sp2.join(this.options.cwd,
|
|
12180
|
+
let fullPath = path24;
|
|
12181
|
+
if (this.options.cwd && !sp2.isAbsolute(path24)) {
|
|
12182
|
+
fullPath = sp2.join(this.options.cwd, path24);
|
|
11170
12183
|
}
|
|
11171
12184
|
const now = /* @__PURE__ */ new Date();
|
|
11172
12185
|
const writes = this._pendingWrites;
|
|
11173
12186
|
function awaitWriteFinishFn(prevStat) {
|
|
11174
12187
|
statcb(fullPath, (err, curStat) => {
|
|
11175
|
-
if (err || !writes.has(
|
|
12188
|
+
if (err || !writes.has(path24)) {
|
|
11176
12189
|
if (err && err.code !== "ENOENT")
|
|
11177
12190
|
awfEmit(err);
|
|
11178
12191
|
return;
|
|
11179
12192
|
}
|
|
11180
12193
|
const now2 = Number(/* @__PURE__ */ new Date());
|
|
11181
12194
|
if (prevStat && curStat.size !== prevStat.size) {
|
|
11182
|
-
writes.get(
|
|
12195
|
+
writes.get(path24).lastChange = now2;
|
|
11183
12196
|
}
|
|
11184
|
-
const pw = writes.get(
|
|
12197
|
+
const pw = writes.get(path24);
|
|
11185
12198
|
const df = now2 - pw.lastChange;
|
|
11186
12199
|
if (df >= threshold) {
|
|
11187
|
-
writes.delete(
|
|
12200
|
+
writes.delete(path24);
|
|
11188
12201
|
awfEmit(void 0, curStat);
|
|
11189
12202
|
} else {
|
|
11190
12203
|
timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);
|
|
11191
12204
|
}
|
|
11192
12205
|
});
|
|
11193
12206
|
}
|
|
11194
|
-
if (!writes.has(
|
|
11195
|
-
writes.set(
|
|
12207
|
+
if (!writes.has(path24)) {
|
|
12208
|
+
writes.set(path24, {
|
|
11196
12209
|
lastChange: now,
|
|
11197
12210
|
cancelWait: () => {
|
|
11198
|
-
writes.delete(
|
|
12211
|
+
writes.delete(path24);
|
|
11199
12212
|
clearTimeout(timeoutHandler);
|
|
11200
12213
|
return event;
|
|
11201
12214
|
}
|
|
@@ -11206,8 +12219,8 @@ var FSWatcher = class extends EventEmitter2 {
|
|
|
11206
12219
|
/**
|
|
11207
12220
|
* Determines whether user has asked to ignore this path.
|
|
11208
12221
|
*/
|
|
11209
|
-
_isIgnored(
|
|
11210
|
-
if (this.options.atomic && DOT_RE.test(
|
|
12222
|
+
_isIgnored(path24, stats) {
|
|
12223
|
+
if (this.options.atomic && DOT_RE.test(path24))
|
|
11211
12224
|
return true;
|
|
11212
12225
|
if (!this._userIgnored) {
|
|
11213
12226
|
const { cwd } = this.options;
|
|
@@ -11217,17 +12230,17 @@ var FSWatcher = class extends EventEmitter2 {
|
|
|
11217
12230
|
const list = [...ignoredPaths.map(normalizeIgnored(cwd)), ...ignored];
|
|
11218
12231
|
this._userIgnored = anymatch(list, void 0);
|
|
11219
12232
|
}
|
|
11220
|
-
return this._userIgnored(
|
|
12233
|
+
return this._userIgnored(path24, stats);
|
|
11221
12234
|
}
|
|
11222
|
-
_isntIgnored(
|
|
11223
|
-
return !this._isIgnored(
|
|
12235
|
+
_isntIgnored(path24, stat4) {
|
|
12236
|
+
return !this._isIgnored(path24, stat4);
|
|
11224
12237
|
}
|
|
11225
12238
|
/**
|
|
11226
12239
|
* Provides a set of common helpers and properties relating to symlink handling.
|
|
11227
12240
|
* @param path file or directory pattern being watched
|
|
11228
12241
|
*/
|
|
11229
|
-
_getWatchHelpers(
|
|
11230
|
-
return new WatchHelper(
|
|
12242
|
+
_getWatchHelpers(path24) {
|
|
12243
|
+
return new WatchHelper(path24, this.options.followSymlinks, this);
|
|
11231
12244
|
}
|
|
11232
12245
|
// Directory helpers
|
|
11233
12246
|
// -----------------
|
|
@@ -11259,63 +12272,63 @@ var FSWatcher = class extends EventEmitter2 {
|
|
|
11259
12272
|
* @param item base path of item/directory
|
|
11260
12273
|
*/
|
|
11261
12274
|
_remove(directory, item, isDirectory) {
|
|
11262
|
-
const
|
|
11263
|
-
const fullPath = sp2.resolve(
|
|
11264
|
-
isDirectory = isDirectory != null ? isDirectory : this._watched.has(
|
|
11265
|
-
if (!this._throttle("remove",
|
|
12275
|
+
const path24 = sp2.join(directory, item);
|
|
12276
|
+
const fullPath = sp2.resolve(path24);
|
|
12277
|
+
isDirectory = isDirectory != null ? isDirectory : this._watched.has(path24) || this._watched.has(fullPath);
|
|
12278
|
+
if (!this._throttle("remove", path24, 100))
|
|
11266
12279
|
return;
|
|
11267
12280
|
if (!isDirectory && this._watched.size === 1) {
|
|
11268
12281
|
this.add(directory, item, true);
|
|
11269
12282
|
}
|
|
11270
|
-
const wp = this._getWatchedDir(
|
|
12283
|
+
const wp = this._getWatchedDir(path24);
|
|
11271
12284
|
const nestedDirectoryChildren = wp.getChildren();
|
|
11272
|
-
nestedDirectoryChildren.forEach((nested) => this._remove(
|
|
12285
|
+
nestedDirectoryChildren.forEach((nested) => this._remove(path24, nested));
|
|
11273
12286
|
const parent = this._getWatchedDir(directory);
|
|
11274
12287
|
const wasTracked = parent.has(item);
|
|
11275
12288
|
parent.remove(item);
|
|
11276
12289
|
if (this._symlinkPaths.has(fullPath)) {
|
|
11277
12290
|
this._symlinkPaths.delete(fullPath);
|
|
11278
12291
|
}
|
|
11279
|
-
let relPath =
|
|
12292
|
+
let relPath = path24;
|
|
11280
12293
|
if (this.options.cwd)
|
|
11281
|
-
relPath = sp2.relative(this.options.cwd,
|
|
12294
|
+
relPath = sp2.relative(this.options.cwd, path24);
|
|
11282
12295
|
if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
|
|
11283
12296
|
const event = this._pendingWrites.get(relPath).cancelWait();
|
|
11284
12297
|
if (event === EVENTS.ADD)
|
|
11285
12298
|
return;
|
|
11286
12299
|
}
|
|
11287
|
-
this._watched.delete(
|
|
12300
|
+
this._watched.delete(path24);
|
|
11288
12301
|
this._watched.delete(fullPath);
|
|
11289
12302
|
const eventName = isDirectory ? EVENTS.UNLINK_DIR : EVENTS.UNLINK;
|
|
11290
|
-
if (wasTracked && !this._isIgnored(
|
|
11291
|
-
this._emit(eventName,
|
|
11292
|
-
this._closePath(
|
|
12303
|
+
if (wasTracked && !this._isIgnored(path24))
|
|
12304
|
+
this._emit(eventName, path24);
|
|
12305
|
+
this._closePath(path24);
|
|
11293
12306
|
}
|
|
11294
12307
|
/**
|
|
11295
12308
|
* Closes all watchers for a path
|
|
11296
12309
|
*/
|
|
11297
|
-
_closePath(
|
|
11298
|
-
this._closeFile(
|
|
11299
|
-
const dir = sp2.dirname(
|
|
11300
|
-
this._getWatchedDir(dir).remove(sp2.basename(
|
|
12310
|
+
_closePath(path24) {
|
|
12311
|
+
this._closeFile(path24);
|
|
12312
|
+
const dir = sp2.dirname(path24);
|
|
12313
|
+
this._getWatchedDir(dir).remove(sp2.basename(path24));
|
|
11301
12314
|
}
|
|
11302
12315
|
/**
|
|
11303
12316
|
* Closes only file-specific watchers
|
|
11304
12317
|
*/
|
|
11305
|
-
_closeFile(
|
|
11306
|
-
const closers = this._closers.get(
|
|
12318
|
+
_closeFile(path24) {
|
|
12319
|
+
const closers = this._closers.get(path24);
|
|
11307
12320
|
if (!closers)
|
|
11308
12321
|
return;
|
|
11309
12322
|
closers.forEach((closer) => closer());
|
|
11310
|
-
this._closers.delete(
|
|
12323
|
+
this._closers.delete(path24);
|
|
11311
12324
|
}
|
|
11312
|
-
_addPathCloser(
|
|
12325
|
+
_addPathCloser(path24, closer) {
|
|
11313
12326
|
if (!closer)
|
|
11314
12327
|
return;
|
|
11315
|
-
let list = this._closers.get(
|
|
12328
|
+
let list = this._closers.get(path24);
|
|
11316
12329
|
if (!list) {
|
|
11317
12330
|
list = [];
|
|
11318
|
-
this._closers.set(
|
|
12331
|
+
this._closers.set(path24, list);
|
|
11319
12332
|
}
|
|
11320
12333
|
list.push(closer);
|
|
11321
12334
|
}
|
|
@@ -11345,7 +12358,7 @@ function watch(paths, options = {}) {
|
|
|
11345
12358
|
var chokidar_default = { watch, FSWatcher };
|
|
11346
12359
|
|
|
11347
12360
|
// src/watcher/file-watcher.ts
|
|
11348
|
-
import * as
|
|
12361
|
+
import * as path17 from "path";
|
|
11349
12362
|
var FileWatcher = class {
|
|
11350
12363
|
watcher = null;
|
|
11351
12364
|
projectRoot;
|
|
@@ -11356,6 +12369,8 @@ var FileWatcher = class {
|
|
|
11356
12369
|
debounceTimer = null;
|
|
11357
12370
|
debounceMs = 1e3;
|
|
11358
12371
|
onChanges = null;
|
|
12372
|
+
readyPromise = null;
|
|
12373
|
+
resolveReady = null;
|
|
11359
12374
|
constructor(projectRoot, config, host = "opencode", options = {}) {
|
|
11360
12375
|
this.projectRoot = projectRoot;
|
|
11361
12376
|
this.config = config;
|
|
@@ -11371,15 +12386,15 @@ var FileWatcher = class {
|
|
|
11371
12386
|
const watchTargets = this.configPath ? [this.projectRoot, this.configPath] : this.projectRoot;
|
|
11372
12387
|
this.watcher = chokidar_default.watch(watchTargets, {
|
|
11373
12388
|
ignored: (filePath) => {
|
|
11374
|
-
const relativePath =
|
|
12389
|
+
const relativePath = path17.relative(this.projectRoot, filePath);
|
|
11375
12390
|
if (!relativePath) return false;
|
|
11376
12391
|
if (this.isProjectConfigPathOrAncestor(relativePath)) {
|
|
11377
12392
|
return false;
|
|
11378
12393
|
}
|
|
11379
|
-
if (hasFilteredPathSegment(relativePath,
|
|
12394
|
+
if (hasFilteredPathSegment(relativePath, path17.sep)) {
|
|
11380
12395
|
return true;
|
|
11381
12396
|
}
|
|
11382
|
-
if (isRestrictedDirectory(relativePath,
|
|
12397
|
+
if (isRestrictedDirectory(relativePath, path17.sep)) {
|
|
11383
12398
|
return true;
|
|
11384
12399
|
}
|
|
11385
12400
|
if (ignoreFilter.ignores(relativePath)) {
|
|
@@ -11394,6 +12409,13 @@ var FileWatcher = class {
|
|
|
11394
12409
|
pollInterval: 100
|
|
11395
12410
|
}
|
|
11396
12411
|
});
|
|
12412
|
+
this.readyPromise = new Promise((resolve13) => {
|
|
12413
|
+
this.resolveReady = resolve13;
|
|
12414
|
+
});
|
|
12415
|
+
this.watcher.once("ready", () => {
|
|
12416
|
+
this.resolveReady?.();
|
|
12417
|
+
this.resolveReady = null;
|
|
12418
|
+
});
|
|
11397
12419
|
this.watcher.on("error", (error) => {
|
|
11398
12420
|
const err = error instanceof Error ? error : null;
|
|
11399
12421
|
if (err?.code === "EPERM" || err?.code === "EACCES") {
|
|
@@ -11425,24 +12447,24 @@ var FileWatcher = class {
|
|
|
11425
12447
|
this.scheduleFlush();
|
|
11426
12448
|
}
|
|
11427
12449
|
isProjectConfigPath(filePath) {
|
|
11428
|
-
const relativePath =
|
|
11429
|
-
const normalizedRelativePath =
|
|
12450
|
+
const relativePath = path17.relative(this.projectRoot, filePath);
|
|
12451
|
+
const normalizedRelativePath = path17.normalize(relativePath);
|
|
11430
12452
|
return this.getProjectConfigRelativePaths().some((configPath) => configPath === normalizedRelativePath);
|
|
11431
12453
|
}
|
|
11432
12454
|
isProjectConfigPathOrAncestor(relativePath) {
|
|
11433
|
-
const normalizedRelativePath =
|
|
12455
|
+
const normalizedRelativePath = path17.normalize(relativePath);
|
|
11434
12456
|
return this.getProjectConfigRelativePaths().some(
|
|
11435
|
-
(configPath) => configPath === normalizedRelativePath || configPath.startsWith(`${normalizedRelativePath}${
|
|
12457
|
+
(configPath) => configPath === normalizedRelativePath || configPath.startsWith(`${normalizedRelativePath}${path17.sep}`)
|
|
11436
12458
|
);
|
|
11437
12459
|
}
|
|
11438
12460
|
getProjectConfigRelativePaths() {
|
|
11439
12461
|
if (this.configPath) {
|
|
11440
|
-
return [
|
|
12462
|
+
return [path17.normalize(path17.relative(this.projectRoot, this.configPath))];
|
|
11441
12463
|
}
|
|
11442
12464
|
return [
|
|
11443
12465
|
resolveProjectConfigPath(this.projectRoot, this.host),
|
|
11444
12466
|
resolveWritableProjectConfigPath(this.projectRoot, this.host)
|
|
11445
|
-
].map((configPath) =>
|
|
12467
|
+
].map((configPath) => path17.normalize(path17.relative(this.projectRoot, configPath)));
|
|
11446
12468
|
}
|
|
11447
12469
|
scheduleFlush() {
|
|
11448
12470
|
if (this.debounceTimer) {
|
|
@@ -11457,7 +12479,7 @@ var FileWatcher = class {
|
|
|
11457
12479
|
return;
|
|
11458
12480
|
}
|
|
11459
12481
|
const changes = Array.from(this.pendingChanges.entries()).map(
|
|
11460
|
-
([
|
|
12482
|
+
([path24, type]) => ({ path: path24, type })
|
|
11461
12483
|
);
|
|
11462
12484
|
this.pendingChanges.clear();
|
|
11463
12485
|
try {
|
|
@@ -11466,25 +12488,32 @@ var FileWatcher = class {
|
|
|
11466
12488
|
console.error("Error handling file changes:", error);
|
|
11467
12489
|
}
|
|
11468
12490
|
}
|
|
11469
|
-
stop() {
|
|
12491
|
+
async stop() {
|
|
11470
12492
|
if (this.debounceTimer) {
|
|
11471
12493
|
clearTimeout(this.debounceTimer);
|
|
11472
12494
|
this.debounceTimer = null;
|
|
11473
12495
|
}
|
|
11474
12496
|
if (this.watcher) {
|
|
11475
|
-
this.watcher
|
|
12497
|
+
const watcher = this.watcher;
|
|
11476
12498
|
this.watcher = null;
|
|
12499
|
+
await watcher.close();
|
|
11477
12500
|
}
|
|
12501
|
+
this.resolveReady?.();
|
|
12502
|
+
this.resolveReady = null;
|
|
12503
|
+
this.readyPromise = null;
|
|
11478
12504
|
this.pendingChanges.clear();
|
|
11479
12505
|
this.onChanges = null;
|
|
11480
12506
|
}
|
|
11481
12507
|
isRunning() {
|
|
11482
12508
|
return this.watcher !== null;
|
|
11483
12509
|
}
|
|
12510
|
+
async waitUntilReady() {
|
|
12511
|
+
await (this.readyPromise ?? Promise.resolve());
|
|
12512
|
+
}
|
|
11484
12513
|
};
|
|
11485
12514
|
|
|
11486
12515
|
// src/watcher/git-head-watcher.ts
|
|
11487
|
-
import * as
|
|
12516
|
+
import * as path18 from "path";
|
|
11488
12517
|
var GitHeadWatcher = class {
|
|
11489
12518
|
watcher = null;
|
|
11490
12519
|
projectRoot;
|
|
@@ -11506,7 +12535,7 @@ var GitHeadWatcher = class {
|
|
|
11506
12535
|
this.onBranchChange = handler;
|
|
11507
12536
|
this.currentBranch = getCurrentBranch(this.projectRoot);
|
|
11508
12537
|
const headPath = getHeadPath(this.projectRoot);
|
|
11509
|
-
const refsPath =
|
|
12538
|
+
const refsPath = path18.join(this.projectRoot, ".git", "refs", "heads");
|
|
11510
12539
|
this.watcher = chokidar_default.watch([headPath, refsPath], {
|
|
11511
12540
|
persistent: true,
|
|
11512
12541
|
ignoreInitial: true,
|
|
@@ -11543,14 +12572,15 @@ var GitHeadWatcher = class {
|
|
|
11543
12572
|
getCurrentBranch() {
|
|
11544
12573
|
return this.currentBranch;
|
|
11545
12574
|
}
|
|
11546
|
-
stop() {
|
|
12575
|
+
async stop() {
|
|
11547
12576
|
if (this.debounceTimer) {
|
|
11548
12577
|
clearTimeout(this.debounceTimer);
|
|
11549
12578
|
this.debounceTimer = null;
|
|
11550
12579
|
}
|
|
11551
12580
|
if (this.watcher) {
|
|
11552
|
-
this.watcher
|
|
12581
|
+
const watcher = this.watcher;
|
|
11553
12582
|
this.watcher = null;
|
|
12583
|
+
await watcher.close();
|
|
11554
12584
|
}
|
|
11555
12585
|
this.onBranchChange = null;
|
|
11556
12586
|
}
|
|
@@ -11568,6 +12598,8 @@ var BackgroundReindexer = class {
|
|
|
11568
12598
|
running = false;
|
|
11569
12599
|
pending = false;
|
|
11570
12600
|
stopped = false;
|
|
12601
|
+
retryTimer = null;
|
|
12602
|
+
retryDelayMs = 50;
|
|
11571
12603
|
request() {
|
|
11572
12604
|
if (this.stopped) {
|
|
11573
12605
|
return;
|
|
@@ -11578,9 +12610,13 @@ var BackgroundReindexer = class {
|
|
|
11578
12610
|
stop() {
|
|
11579
12611
|
this.stopped = true;
|
|
11580
12612
|
this.pending = false;
|
|
12613
|
+
if (this.retryTimer) {
|
|
12614
|
+
clearTimeout(this.retryTimer);
|
|
12615
|
+
this.retryTimer = null;
|
|
12616
|
+
}
|
|
11581
12617
|
}
|
|
11582
12618
|
drain() {
|
|
11583
|
-
if (this.stopped || this.running || !this.pending) {
|
|
12619
|
+
if (this.stopped || this.running || this.retryTimer || !this.pending) {
|
|
11584
12620
|
return;
|
|
11585
12621
|
}
|
|
11586
12622
|
this.pending = false;
|
|
@@ -11588,13 +12624,29 @@ var BackgroundReindexer = class {
|
|
|
11588
12624
|
void this.run();
|
|
11589
12625
|
}
|
|
11590
12626
|
async run() {
|
|
12627
|
+
let retryAfterContention = false;
|
|
11591
12628
|
try {
|
|
11592
12629
|
await this.runIndex();
|
|
12630
|
+
this.retryDelayMs = 50;
|
|
11593
12631
|
} catch (error) {
|
|
11594
|
-
|
|
12632
|
+
if (isTransientIndexLockContention(error)) {
|
|
12633
|
+
this.pending = true;
|
|
12634
|
+
retryAfterContention = true;
|
|
12635
|
+
} else {
|
|
12636
|
+
console.error("[codebase-index] Background reindex failed:", error);
|
|
12637
|
+
}
|
|
11595
12638
|
} finally {
|
|
11596
12639
|
this.running = false;
|
|
11597
|
-
this.
|
|
12640
|
+
if (retryAfterContention && !this.stopped) {
|
|
12641
|
+
const delay = this.retryDelayMs;
|
|
12642
|
+
this.retryDelayMs = Math.min(this.retryDelayMs * 2, 500);
|
|
12643
|
+
this.retryTimer = setTimeout(() => {
|
|
12644
|
+
this.retryTimer = null;
|
|
12645
|
+
this.drain();
|
|
12646
|
+
}, delay);
|
|
12647
|
+
} else {
|
|
12648
|
+
this.drain();
|
|
12649
|
+
}
|
|
11598
12650
|
}
|
|
11599
12651
|
}
|
|
11600
12652
|
};
|
|
@@ -11628,10 +12680,12 @@ function createWatcherWithIndexer(getIndexer, projectRoot, config, host = "openc
|
|
|
11628
12680
|
return {
|
|
11629
12681
|
fileWatcher,
|
|
11630
12682
|
gitWatcher,
|
|
11631
|
-
|
|
12683
|
+
whenReady() {
|
|
12684
|
+
return fileWatcher.waitUntilReady();
|
|
12685
|
+
},
|
|
12686
|
+
async stop() {
|
|
11632
12687
|
backgroundReindexer.stop();
|
|
11633
|
-
fileWatcher.stop();
|
|
11634
|
-
gitWatcher?.stop();
|
|
12688
|
+
await Promise.all([fileWatcher.stop(), gitWatcher?.stop()]);
|
|
11635
12689
|
}
|
|
11636
12690
|
};
|
|
11637
12691
|
}
|
|
@@ -11737,13 +12791,13 @@ var pr_impact = tool({
|
|
|
11737
12791
|
});
|
|
11738
12792
|
|
|
11739
12793
|
// src/tools/index.ts
|
|
11740
|
-
import { writeFileSync as
|
|
11741
|
-
import * as
|
|
11742
|
-
import * as
|
|
12794
|
+
import { writeFileSync as writeFileSync5 } from "fs";
|
|
12795
|
+
import * as os5 from "os";
|
|
12796
|
+
import * as path21 from "path";
|
|
11743
12797
|
|
|
11744
12798
|
// src/tools/visualize/activity.ts
|
|
11745
12799
|
import { execFileSync } from "child_process";
|
|
11746
|
-
import * as
|
|
12800
|
+
import * as path19 from "path";
|
|
11747
12801
|
function attachRecentActivity(data, projectRoot) {
|
|
11748
12802
|
const activity = readGitActivity(projectRoot);
|
|
11749
12803
|
const changes = activity.size > 0 ? buildGitChanges(data, activity, projectRoot) : buildGraphChanges(data);
|
|
@@ -11905,7 +12959,7 @@ function normalizePath2(filePath) {
|
|
|
11905
12959
|
return filePath.replace(/\\/g, "/");
|
|
11906
12960
|
}
|
|
11907
12961
|
function toGitRelativePath(projectRoot, filePath) {
|
|
11908
|
-
const relativePath =
|
|
12962
|
+
const relativePath = path19.isAbsolute(filePath) ? path19.relative(projectRoot, filePath) : filePath;
|
|
11909
12963
|
return normalizePath2(relativePath);
|
|
11910
12964
|
}
|
|
11911
12965
|
|
|
@@ -12163,7 +13217,7 @@ render();
|
|
|
12163
13217
|
}
|
|
12164
13218
|
|
|
12165
13219
|
// src/tools/visualize/transform.ts
|
|
12166
|
-
import * as
|
|
13220
|
+
import * as path20 from "path";
|
|
12167
13221
|
|
|
12168
13222
|
// src/tools/visualize/modules.ts
|
|
12169
13223
|
var MAX_MODULES = 18;
|
|
@@ -12423,7 +13477,7 @@ function transformForVisualization(symbols, edges, options = {}) {
|
|
|
12423
13477
|
filePath: s.filePath,
|
|
12424
13478
|
kind: s.kind,
|
|
12425
13479
|
line: s.startLine,
|
|
12426
|
-
directory:
|
|
13480
|
+
directory: path20.dirname(s.filePath),
|
|
12427
13481
|
moduleId: "",
|
|
12428
13482
|
moduleLabel: ""
|
|
12429
13483
|
}));
|
|
@@ -12504,7 +13558,9 @@ var index_codebase = tool2({
|
|
|
12504
13558
|
const result = await runIndexCodebase(context?.worktree, DEFAULT_HOST, args, (title, metadata) => {
|
|
12505
13559
|
context.metadata({ title, metadata });
|
|
12506
13560
|
});
|
|
12507
|
-
|
|
13561
|
+
if (result.kind === "estimate") return formatCostEstimate(result.estimate);
|
|
13562
|
+
if (result.kind === "busy") return result.text;
|
|
13563
|
+
return formatIndexStats(result.stats, args.verbose ?? false);
|
|
12508
13564
|
}
|
|
12509
13565
|
});
|
|
12510
13566
|
var index_status = tool2({
|
|
@@ -12518,7 +13574,9 @@ var index_health_check = tool2({
|
|
|
12518
13574
|
description: "Check index health and remove stale entries from deleted files. Run this to clean up the index after files have been deleted.",
|
|
12519
13575
|
args: {},
|
|
12520
13576
|
async execute(_args, context) {
|
|
12521
|
-
|
|
13577
|
+
const result = await runIndexHealthCheck(context?.worktree, DEFAULT_HOST);
|
|
13578
|
+
if (result.kind === "busy") return result.text;
|
|
13579
|
+
return formatHealthCheck(result.health);
|
|
12522
13580
|
}
|
|
12523
13581
|
});
|
|
12524
13582
|
var index_metrics = tool2({
|
|
@@ -12638,8 +13696,8 @@ var call_graph_path = tool2({
|
|
|
12638
13696
|
maxDepth: z2.number().optional().default(10).describe("Maximum traversal depth (default: 10)")
|
|
12639
13697
|
},
|
|
12640
13698
|
async execute(args, context) {
|
|
12641
|
-
const
|
|
12642
|
-
return formatCallGraphPath(args.from, args.to,
|
|
13699
|
+
const path24 = await getCallGraphPath(context?.worktree, DEFAULT_HOST, args.from, args.to, args.maxDepth);
|
|
13700
|
+
return formatCallGraphPath(args.from, args.to, path24);
|
|
12643
13701
|
}
|
|
12644
13702
|
});
|
|
12645
13703
|
var add_knowledge_base = tool2({
|
|
@@ -12692,8 +13750,8 @@ var index_visualize = tool2({
|
|
|
12692
13750
|
return "No connected symbols found for visualization. Try including orphans with includeOrphans=true, or check that the call graph has resolved edges.";
|
|
12693
13751
|
}
|
|
12694
13752
|
const html = generateVisualizationHtml(vizData);
|
|
12695
|
-
const outputPath =
|
|
12696
|
-
|
|
13753
|
+
const outputPath = path21.join(os5.tmpdir(), `call-graph-${Date.now()}.html`);
|
|
13754
|
+
writeFileSync5(outputPath, html, "utf-8");
|
|
12697
13755
|
let result = `Temporal call graph visualization generated: ${outputPath}
|
|
12698
13756
|
|
|
12699
13757
|
`;
|
|
@@ -12714,8 +13772,8 @@ var index_visualize = tool2({
|
|
|
12714
13772
|
});
|
|
12715
13773
|
|
|
12716
13774
|
// src/commands/loader.ts
|
|
12717
|
-
import { existsSync as
|
|
12718
|
-
import * as
|
|
13775
|
+
import { existsSync as existsSync11, readdirSync as readdirSync3, readFileSync as readFileSync8 } from "fs";
|
|
13776
|
+
import * as path22 from "path";
|
|
12719
13777
|
function parseFrontmatter(content) {
|
|
12720
13778
|
const frontmatterRegex = /^---\s*\n([\s\S]*?)\n---\s*\n([\s\S]*)$/;
|
|
12721
13779
|
const match = content.match(frontmatterRegex);
|
|
@@ -12736,21 +13794,21 @@ function parseFrontmatter(content) {
|
|
|
12736
13794
|
}
|
|
12737
13795
|
function loadCommandsFromDirectory(commandsDir) {
|
|
12738
13796
|
const commands = /* @__PURE__ */ new Map();
|
|
12739
|
-
if (!
|
|
13797
|
+
if (!existsSync11(commandsDir)) {
|
|
12740
13798
|
return commands;
|
|
12741
13799
|
}
|
|
12742
|
-
const files =
|
|
13800
|
+
const files = readdirSync3(commandsDir).filter((f) => f.endsWith(".md"));
|
|
12743
13801
|
for (const file of files) {
|
|
12744
|
-
const filePath =
|
|
13802
|
+
const filePath = path22.join(commandsDir, file);
|
|
12745
13803
|
let content;
|
|
12746
13804
|
try {
|
|
12747
|
-
content =
|
|
13805
|
+
content = readFileSync8(filePath, "utf-8");
|
|
12748
13806
|
} catch (error) {
|
|
12749
13807
|
const message = error instanceof Error ? error.message : String(error);
|
|
12750
13808
|
throw new Error(`Failed to load command file ${filePath}: ${message}`);
|
|
12751
13809
|
}
|
|
12752
13810
|
const { frontmatter, body } = parseFrontmatter(content);
|
|
12753
|
-
const name =
|
|
13811
|
+
const name = path22.basename(file, ".md");
|
|
12754
13812
|
const description = frontmatter.description || `Run the ${name} command`;
|
|
12755
13813
|
commands.set(name, {
|
|
12756
13814
|
description,
|
|
@@ -13054,18 +14112,37 @@ var RoutingHintController = class {
|
|
|
13054
14112
|
};
|
|
13055
14113
|
|
|
13056
14114
|
// src/utils/auto-index.ts
|
|
14115
|
+
var INITIAL_RETRY_DELAY_MS = 50;
|
|
14116
|
+
var MAX_RETRY_DELAY_MS = 500;
|
|
14117
|
+
var autoIndexStates = /* @__PURE__ */ new WeakMap();
|
|
13057
14118
|
function getErrorMessage3(error) {
|
|
13058
14119
|
return error instanceof Error ? error.message : String(error);
|
|
13059
14120
|
}
|
|
13060
|
-
function
|
|
13061
|
-
indexer.
|
|
13062
|
-
|
|
13063
|
-
console.error(`[codebase-index] Auto-index failed for "${projectRoot}": ${getErrorMessage3(error)}`);
|
|
13064
|
-
});
|
|
14121
|
+
function runAutoIndex(indexer, projectRoot, state) {
|
|
14122
|
+
void indexer.index().then(() => {
|
|
14123
|
+
autoIndexStates.delete(indexer);
|
|
13065
14124
|
}).catch((error) => {
|
|
13066
|
-
|
|
14125
|
+
if (!isTransientIndexLockContention(error)) {
|
|
14126
|
+
autoIndexStates.delete(indexer);
|
|
14127
|
+
console.error(`[codebase-index] Auto-index failed for "${projectRoot}": ${getErrorMessage3(error)}`);
|
|
14128
|
+
return;
|
|
14129
|
+
}
|
|
14130
|
+
const retryDelayMs = state.retryDelayMs;
|
|
14131
|
+
state.retryDelayMs = Math.min(retryDelayMs * 2, MAX_RETRY_DELAY_MS);
|
|
14132
|
+
const retryTimer = setTimeout(() => {
|
|
14133
|
+
runAutoIndex(indexer, projectRoot, state);
|
|
14134
|
+
}, retryDelayMs);
|
|
14135
|
+
retryTimer.unref?.();
|
|
13067
14136
|
});
|
|
13068
14137
|
}
|
|
14138
|
+
function startAutoIndex(indexer, projectRoot) {
|
|
14139
|
+
if (autoIndexStates.has(indexer)) return;
|
|
14140
|
+
const state = {
|
|
14141
|
+
retryDelayMs: INITIAL_RETRY_DELAY_MS
|
|
14142
|
+
};
|
|
14143
|
+
autoIndexStates.set(indexer, state);
|
|
14144
|
+
runAutoIndex(indexer, projectRoot, state);
|
|
14145
|
+
}
|
|
13069
14146
|
|
|
13070
14147
|
// src/index.ts
|
|
13071
14148
|
var activeWatchers = /* @__PURE__ */ new Map();
|
|
@@ -13082,9 +14159,9 @@ function replaceActiveWatcher(projectRoot, nextWatcher) {
|
|
|
13082
14159
|
function getCommandsDir() {
|
|
13083
14160
|
let currentDir = process.cwd();
|
|
13084
14161
|
if (typeof import.meta !== "undefined" && import.meta.url) {
|
|
13085
|
-
currentDir =
|
|
14162
|
+
currentDir = path23.dirname(fileURLToPath2(import.meta.url));
|
|
13086
14163
|
}
|
|
13087
|
-
return
|
|
14164
|
+
return path23.join(currentDir, "..", "commands");
|
|
13088
14165
|
}
|
|
13089
14166
|
function appendRoutingHints(output, hints, preferredRole) {
|
|
13090
14167
|
const preferredBucket = preferredRole === "developer" ? output.developer : output.system;
|
|
@@ -13104,7 +14181,7 @@ var plugin = async ({ directory, worktree }) => {
|
|
|
13104
14181
|
initializeTools2(projectRoot, config);
|
|
13105
14182
|
const getProjectIndexer = () => getIndexerForProject2(projectRoot);
|
|
13106
14183
|
const routingHints = config.search.routingHints ? new RoutingHintController(() => getProjectIndexer().getStatus(), 200, config.search.routingGraphHandoffHints) : null;
|
|
13107
|
-
const isHomeDir =
|
|
14184
|
+
const isHomeDir = path23.resolve(projectRoot) === path23.resolve(os6.homedir());
|
|
13108
14185
|
const isValidProject = !isHomeDir && (!config.indexing.requireProjectMarker || hasProjectMarker(projectRoot));
|
|
13109
14186
|
if (isHomeDir) {
|
|
13110
14187
|
console.warn(
|