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.cjs
CHANGED
|
@@ -495,7 +495,7 @@ var require_ignore = __commonJS({
|
|
|
495
495
|
// path matching.
|
|
496
496
|
// - check `string` either `MODE_IGNORE` or `MODE_CHECK_IGNORE`
|
|
497
497
|
// @returns {TestResult} true if a file is ignored
|
|
498
|
-
test(
|
|
498
|
+
test(path24, checkUnignored, mode) {
|
|
499
499
|
let ignored = false;
|
|
500
500
|
let unignored = false;
|
|
501
501
|
let matchedRule;
|
|
@@ -504,7 +504,7 @@ var require_ignore = __commonJS({
|
|
|
504
504
|
if (unignored === negative && ignored !== unignored || negative && !ignored && !unignored && !checkUnignored) {
|
|
505
505
|
return;
|
|
506
506
|
}
|
|
507
|
-
const matched = rule[mode].test(
|
|
507
|
+
const matched = rule[mode].test(path24);
|
|
508
508
|
if (!matched) {
|
|
509
509
|
return;
|
|
510
510
|
}
|
|
@@ -525,17 +525,17 @@ var require_ignore = __commonJS({
|
|
|
525
525
|
var throwError = (message, Ctor) => {
|
|
526
526
|
throw new Ctor(message);
|
|
527
527
|
};
|
|
528
|
-
var checkPath = (
|
|
529
|
-
if (!isString(
|
|
528
|
+
var checkPath = (path24, originalPath, doThrow) => {
|
|
529
|
+
if (!isString(path24)) {
|
|
530
530
|
return doThrow(
|
|
531
531
|
`path must be a string, but got \`${originalPath}\``,
|
|
532
532
|
TypeError
|
|
533
533
|
);
|
|
534
534
|
}
|
|
535
|
-
if (!
|
|
535
|
+
if (!path24) {
|
|
536
536
|
return doThrow(`path must not be empty`, TypeError);
|
|
537
537
|
}
|
|
538
|
-
if (checkPath.isNotRelative(
|
|
538
|
+
if (checkPath.isNotRelative(path24)) {
|
|
539
539
|
const r = "`path.relative()`d";
|
|
540
540
|
return doThrow(
|
|
541
541
|
`path should be a ${r} string, but got "${originalPath}"`,
|
|
@@ -544,7 +544,7 @@ var require_ignore = __commonJS({
|
|
|
544
544
|
}
|
|
545
545
|
return true;
|
|
546
546
|
};
|
|
547
|
-
var isNotRelative = (
|
|
547
|
+
var isNotRelative = (path24) => REGEX_TEST_INVALID_PATH.test(path24);
|
|
548
548
|
checkPath.isNotRelative = isNotRelative;
|
|
549
549
|
checkPath.convert = (p) => p;
|
|
550
550
|
var Ignore2 = class {
|
|
@@ -574,19 +574,19 @@ var require_ignore = __commonJS({
|
|
|
574
574
|
}
|
|
575
575
|
// @returns {TestResult}
|
|
576
576
|
_test(originalPath, cache, checkUnignored, slices) {
|
|
577
|
-
const
|
|
577
|
+
const path24 = originalPath && checkPath.convert(originalPath);
|
|
578
578
|
checkPath(
|
|
579
|
-
|
|
579
|
+
path24,
|
|
580
580
|
originalPath,
|
|
581
581
|
this._strictPathCheck ? throwError : RETURN_FALSE
|
|
582
582
|
);
|
|
583
|
-
return this._t(
|
|
583
|
+
return this._t(path24, cache, checkUnignored, slices);
|
|
584
584
|
}
|
|
585
|
-
checkIgnore(
|
|
586
|
-
if (!REGEX_TEST_TRAILING_SLASH.test(
|
|
587
|
-
return this.test(
|
|
585
|
+
checkIgnore(path24) {
|
|
586
|
+
if (!REGEX_TEST_TRAILING_SLASH.test(path24)) {
|
|
587
|
+
return this.test(path24);
|
|
588
588
|
}
|
|
589
|
-
const slices =
|
|
589
|
+
const slices = path24.split(SLASH2).filter(Boolean);
|
|
590
590
|
slices.pop();
|
|
591
591
|
if (slices.length) {
|
|
592
592
|
const parent = this._t(
|
|
@@ -599,18 +599,18 @@ var require_ignore = __commonJS({
|
|
|
599
599
|
return parent;
|
|
600
600
|
}
|
|
601
601
|
}
|
|
602
|
-
return this._rules.test(
|
|
602
|
+
return this._rules.test(path24, false, MODE_CHECK_IGNORE);
|
|
603
603
|
}
|
|
604
|
-
_t(
|
|
605
|
-
if (
|
|
606
|
-
return cache[
|
|
604
|
+
_t(path24, cache, checkUnignored, slices) {
|
|
605
|
+
if (path24 in cache) {
|
|
606
|
+
return cache[path24];
|
|
607
607
|
}
|
|
608
608
|
if (!slices) {
|
|
609
|
-
slices =
|
|
609
|
+
slices = path24.split(SLASH2).filter(Boolean);
|
|
610
610
|
}
|
|
611
611
|
slices.pop();
|
|
612
612
|
if (!slices.length) {
|
|
613
|
-
return cache[
|
|
613
|
+
return cache[path24] = this._rules.test(path24, checkUnignored, MODE_IGNORE);
|
|
614
614
|
}
|
|
615
615
|
const parent = this._t(
|
|
616
616
|
slices.join(SLASH2) + SLASH2,
|
|
@@ -618,29 +618,29 @@ var require_ignore = __commonJS({
|
|
|
618
618
|
checkUnignored,
|
|
619
619
|
slices
|
|
620
620
|
);
|
|
621
|
-
return cache[
|
|
621
|
+
return cache[path24] = parent.ignored ? parent : this._rules.test(path24, checkUnignored, MODE_IGNORE);
|
|
622
622
|
}
|
|
623
|
-
ignores(
|
|
624
|
-
return this._test(
|
|
623
|
+
ignores(path24) {
|
|
624
|
+
return this._test(path24, this._ignoreCache, false).ignored;
|
|
625
625
|
}
|
|
626
626
|
createFilter() {
|
|
627
|
-
return (
|
|
627
|
+
return (path24) => !this.ignores(path24);
|
|
628
628
|
}
|
|
629
629
|
filter(paths) {
|
|
630
630
|
return makeArray(paths).filter(this.createFilter());
|
|
631
631
|
}
|
|
632
632
|
// @returns {TestResult}
|
|
633
|
-
test(
|
|
634
|
-
return this._test(
|
|
633
|
+
test(path24) {
|
|
634
|
+
return this._test(path24, this._testCache, true);
|
|
635
635
|
}
|
|
636
636
|
};
|
|
637
637
|
var factory = (options) => new Ignore2(options);
|
|
638
|
-
var isPathValid = (
|
|
638
|
+
var isPathValid = (path24) => checkPath(path24 && checkPath.convert(path24), path24, RETURN_FALSE);
|
|
639
639
|
var setupWindows = () => {
|
|
640
640
|
const makePosix = (str) => /^\\\\\?\\/.test(str) || /["<>|\u0000-\u001F]+/u.test(str) ? str : str.replace(/\\/g, "/");
|
|
641
641
|
checkPath.convert = makePosix;
|
|
642
642
|
const REGEX_TEST_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i;
|
|
643
|
-
checkPath.isNotRelative = (
|
|
643
|
+
checkPath.isNotRelative = (path24) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path24) || isNotRelative(path24);
|
|
644
644
|
};
|
|
645
645
|
if (
|
|
646
646
|
// Detect `process` so that it can run in browsers.
|
|
@@ -661,16 +661,16 @@ __export(index_exports, {
|
|
|
661
661
|
default: () => index_default
|
|
662
662
|
});
|
|
663
663
|
module.exports = __toCommonJS(index_exports);
|
|
664
|
-
var
|
|
665
|
-
var
|
|
664
|
+
var os6 = __toESM(require("os"), 1);
|
|
665
|
+
var path23 = __toESM(require("path"), 1);
|
|
666
666
|
var import_url2 = require("url");
|
|
667
667
|
|
|
668
668
|
// src/config/constants.ts
|
|
669
669
|
var DEFAULT_INCLUDE = [
|
|
670
|
-
"**/*.{ts,tsx,js,jsx,mjs,cjs}",
|
|
670
|
+
"**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}",
|
|
671
671
|
"**/*.{py,pyi}",
|
|
672
|
-
"**/*.{go,rs,java,kt,scala}",
|
|
673
|
-
"**/*.{c,cpp,cc,h,hpp}",
|
|
672
|
+
"**/*.{go,rs,java,cs,kt,scala}",
|
|
673
|
+
"**/*.{c,cpp,cc,cxx,h,hpp,hxx}",
|
|
674
674
|
"**/*.{rb,php,inc,swift}",
|
|
675
675
|
"**/*.{cls,trigger}",
|
|
676
676
|
"**/*.{vue,svelte,astro}",
|
|
@@ -1254,6 +1254,9 @@ function getHostProjectIndexRelativePath(host) {
|
|
|
1254
1254
|
function hasHostProjectConfig(projectRoot, host) {
|
|
1255
1255
|
return (0, import_fs3.existsSync)(path3.join(projectRoot, getProjectConfigRelativePath(host)));
|
|
1256
1256
|
}
|
|
1257
|
+
function hasHostGlobalConfig(host) {
|
|
1258
|
+
return (0, import_fs3.existsSync)(getGlobalConfigPath(host));
|
|
1259
|
+
}
|
|
1257
1260
|
function getGlobalIndexPath(host = "opencode") {
|
|
1258
1261
|
switch (host) {
|
|
1259
1262
|
case "opencode":
|
|
@@ -1293,6 +1296,9 @@ function resolveGlobalIndexPath(host = "opencode") {
|
|
|
1293
1296
|
return hostIndexPath;
|
|
1294
1297
|
}
|
|
1295
1298
|
if (host !== "opencode") {
|
|
1299
|
+
if (hasHostGlobalConfig(host)) {
|
|
1300
|
+
return hostIndexPath;
|
|
1301
|
+
}
|
|
1296
1302
|
const legacyIndexPath = getGlobalIndexPath("opencode");
|
|
1297
1303
|
if ((0, import_fs3.existsSync)(legacyIndexPath)) {
|
|
1298
1304
|
return legacyIndexPath;
|
|
@@ -1591,13 +1597,429 @@ function loadMergedConfig(projectRoot, host = "opencode") {
|
|
|
1591
1597
|
return merged;
|
|
1592
1598
|
}
|
|
1593
1599
|
|
|
1600
|
+
// src/indexer/index-lock.ts
|
|
1601
|
+
var import_crypto = require("crypto");
|
|
1602
|
+
var import_fs5 = require("fs");
|
|
1603
|
+
var os2 = __toESM(require("os"), 1);
|
|
1604
|
+
var path7 = __toESM(require("path"), 1);
|
|
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((0, import_fs5.readFileSync)(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((0, import_fs5.readFileSync)(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}.${(0, import_crypto.randomUUID)()}`;
|
|
1711
|
+
(0, import_fs5.mkdirSync)(candidatePath, { mode: 448 });
|
|
1712
|
+
try {
|
|
1713
|
+
(0, import_fs5.writeFileSync)(path7.join(candidatePath, OWNER_FILE_NAME), JSON.stringify(value), {
|
|
1714
|
+
encoding: "utf-8",
|
|
1715
|
+
flag: "wx",
|
|
1716
|
+
mode: 384
|
|
1717
|
+
});
|
|
1718
|
+
if ((0, import_fs5.existsSync)(finalPath)) return false;
|
|
1719
|
+
try {
|
|
1720
|
+
(0, import_fs5.renameSync)(candidatePath, finalPath);
|
|
1721
|
+
return true;
|
|
1722
|
+
} catch (error) {
|
|
1723
|
+
if ((0, import_fs5.existsSync)(finalPath)) return false;
|
|
1724
|
+
throw error;
|
|
1725
|
+
}
|
|
1726
|
+
} finally {
|
|
1727
|
+
if ((0, import_fs5.existsSync)(candidatePath)) (0, import_fs5.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: (0, import_crypto.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 = (0, import_fs5.readdirSync)(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 = (0, import_fs5.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 (0, import_fs5.readdirSync)(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
|
+
(0, import_fs5.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}.${(0, import_crypto.randomUUID)()}`;
|
|
1805
|
+
try {
|
|
1806
|
+
(0, import_fs5.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 (!(0, import_fs5.existsSync)(markerPath) && (0, import_fs5.existsSync)(claimedMarkerPath)) {
|
|
1815
|
+
(0, import_fs5.renameSync)(claimedMarkerPath, markerPath);
|
|
1816
|
+
}
|
|
1817
|
+
return false;
|
|
1818
|
+
}
|
|
1819
|
+
(0, import_fs5.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: (0, import_crypto.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
|
+
(0, import_fs5.renameSync)(lockPath, quarantinePath);
|
|
1850
|
+
(0, import_fs5.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
|
+
(0, import_fs5.mkdirSync)(indexPath, { recursive: true });
|
|
1880
|
+
const canonicalIndexPath = import_fs5.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 = (0, import_fs5.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(() => (0, import_fs5.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 (!(0, import_fs5.existsSync)(lease.lockPath) && (0, import_fs5.existsSync)(releasePath)) {
|
|
1938
|
+
(0, import_fs5.renameSync)(releasePath, lease.lockPath);
|
|
1939
|
+
}
|
|
1940
|
+
return false;
|
|
1941
|
+
}
|
|
1942
|
+
try {
|
|
1943
|
+
retryTransientFilesystemOperation(() => (0, import_fs5.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 ((0, import_fs5.existsSync)(temporaryPath)) (0, import_fs5.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 (!(0, import_fs5.existsSync)(backupPath)) continue;
|
|
1995
|
+
if ((0, import_fs5.existsSync)(targetPath)) (0, import_fs5.rmSync)(targetPath, { recursive: true, force: true });
|
|
1996
|
+
(0, import_fs5.renameSync)(backupPath, targetPath);
|
|
1997
|
+
}
|
|
1998
|
+
const temporaryOwnerMarker = `.tmp.${owner.pid}.${owner.token}.`;
|
|
1999
|
+
for (const entry of (0, import_fs5.readdirSync)(indexPath)) {
|
|
2000
|
+
if (!entry.includes(temporaryOwnerMarker)) continue;
|
|
2001
|
+
(0, import_fs5.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
|
+
(0, import_fs5.rmSync)(recovery.markerPath, { recursive: true, force: true });
|
|
2013
|
+
}
|
|
2014
|
+
}
|
|
2015
|
+
|
|
1594
2016
|
// src/tools/operations.ts
|
|
1595
|
-
var
|
|
1596
|
-
var
|
|
2017
|
+
var import_fs10 = require("fs");
|
|
2018
|
+
var path16 = __toESM(require("path"), 1);
|
|
1597
2019
|
|
|
1598
2020
|
// src/indexer/index.ts
|
|
1599
|
-
var
|
|
1600
|
-
var
|
|
2021
|
+
var import_fs8 = require("fs");
|
|
2022
|
+
var path13 = __toESM(require("path"), 1);
|
|
1601
2023
|
var import_perf_hooks = require("perf_hooks");
|
|
1602
2024
|
var import_child_process3 = require("child_process");
|
|
1603
2025
|
var import_util3 = require("util");
|
|
@@ -2647,17 +3069,17 @@ async function pRetry(input, options = {}) {
|
|
|
2647
3069
|
}
|
|
2648
3070
|
|
|
2649
3071
|
// src/embeddings/detector.ts
|
|
2650
|
-
var
|
|
2651
|
-
var
|
|
2652
|
-
var
|
|
3072
|
+
var import_fs6 = require("fs");
|
|
3073
|
+
var path8 = __toESM(require("path"), 1);
|
|
3074
|
+
var os3 = __toESM(require("os"), 1);
|
|
2653
3075
|
function getOpenCodeAuthPath() {
|
|
2654
|
-
return
|
|
3076
|
+
return path8.join(os3.homedir(), ".local", "share", "opencode", "auth.json");
|
|
2655
3077
|
}
|
|
2656
3078
|
function loadOpenCodeAuth() {
|
|
2657
3079
|
const authPath = getOpenCodeAuthPath();
|
|
2658
3080
|
try {
|
|
2659
|
-
if ((0,
|
|
2660
|
-
return JSON.parse((0,
|
|
3081
|
+
if ((0, import_fs6.existsSync)(authPath)) {
|
|
3082
|
+
return JSON.parse((0, import_fs6.readFileSync)(authPath, "utf-8"));
|
|
2661
3083
|
}
|
|
2662
3084
|
} catch {
|
|
2663
3085
|
}
|
|
@@ -2879,17 +3301,17 @@ function validateExternalUrl(urlString) {
|
|
|
2879
3301
|
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
2880
3302
|
return { valid: false, reason: `Blocked protocol: ${parsed.protocol}` };
|
|
2881
3303
|
}
|
|
2882
|
-
const
|
|
2883
|
-
if (BLOCKED_HOSTNAMES.has(
|
|
2884
|
-
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})` };
|
|
2885
3307
|
}
|
|
2886
3308
|
for (const pattern of BLOCKED_METADATA_IPS) {
|
|
2887
|
-
if (pattern.test(
|
|
2888
|
-
return { valid: false, reason: `Blocked: cloud metadata IP (${
|
|
3309
|
+
if (pattern.test(hostname2)) {
|
|
3310
|
+
return { valid: false, reason: `Blocked: cloud metadata IP (${hostname2})` };
|
|
2889
3311
|
}
|
|
2890
3312
|
}
|
|
2891
|
-
if (/^169\.254\./.test(
|
|
2892
|
-
return { valid: false, reason: `Blocked: link-local address (${
|
|
3313
|
+
if (/^169\.254\./.test(hostname2)) {
|
|
3314
|
+
return { valid: false, reason: `Blocked: link-local address (${hostname2})` };
|
|
2893
3315
|
}
|
|
2894
3316
|
return { valid: true };
|
|
2895
3317
|
}
|
|
@@ -3360,8 +3782,8 @@ var SiliconFlowReranker = class {
|
|
|
3360
3782
|
|
|
3361
3783
|
// src/utils/files.ts
|
|
3362
3784
|
var import_ignore = __toESM(require_ignore(), 1);
|
|
3363
|
-
var
|
|
3364
|
-
var
|
|
3785
|
+
var import_fs7 = require("fs");
|
|
3786
|
+
var path9 = __toESM(require("path"), 1);
|
|
3365
3787
|
var PROJECT_MARKERS = [
|
|
3366
3788
|
".git",
|
|
3367
3789
|
"package.json",
|
|
@@ -3381,7 +3803,7 @@ var PROJECT_MARKERS = [
|
|
|
3381
3803
|
];
|
|
3382
3804
|
function hasProjectMarker(projectRoot) {
|
|
3383
3805
|
for (const marker of PROJECT_MARKERS) {
|
|
3384
|
-
if ((0,
|
|
3806
|
+
if ((0, import_fs7.existsSync)(path9.join(projectRoot, marker))) {
|
|
3385
3807
|
return true;
|
|
3386
3808
|
}
|
|
3387
3809
|
}
|
|
@@ -3408,16 +3830,16 @@ function createIgnoreFilter(projectRoot) {
|
|
|
3408
3830
|
"**/*build*/**"
|
|
3409
3831
|
];
|
|
3410
3832
|
ig.add(defaultIgnores);
|
|
3411
|
-
const gitignorePath =
|
|
3412
|
-
if ((0,
|
|
3413
|
-
const gitignoreContent = (0,
|
|
3833
|
+
const gitignorePath = path9.join(projectRoot, ".gitignore");
|
|
3834
|
+
if ((0, import_fs7.existsSync)(gitignorePath)) {
|
|
3835
|
+
const gitignoreContent = (0, import_fs7.readFileSync)(gitignorePath, "utf-8");
|
|
3414
3836
|
ig.add(gitignoreContent);
|
|
3415
3837
|
}
|
|
3416
3838
|
return ig;
|
|
3417
3839
|
}
|
|
3418
3840
|
function shouldIncludeFile(filePath, projectRoot, includePatterns, excludePatterns, ignoreFilter) {
|
|
3419
|
-
const relativePath =
|
|
3420
|
-
if (hasFilteredPathSegment(relativePath,
|
|
3841
|
+
const relativePath = path9.relative(projectRoot, filePath);
|
|
3842
|
+
if (hasFilteredPathSegment(relativePath, path9.sep)) {
|
|
3421
3843
|
return false;
|
|
3422
3844
|
}
|
|
3423
3845
|
if (ignoreFilter.ignores(relativePath)) {
|
|
@@ -3451,12 +3873,12 @@ function matchGlob(filePath, pattern) {
|
|
|
3451
3873
|
return regex.test(filePath);
|
|
3452
3874
|
}
|
|
3453
3875
|
async function* walkDirectory(dir, projectRoot, includePatterns, excludePatterns, ignoreFilter, maxFileSize, skipped, options, currentDepth = 0) {
|
|
3454
|
-
const entries = await
|
|
3876
|
+
const entries = await import_fs7.promises.readdir(dir, { withFileTypes: true });
|
|
3455
3877
|
const filesInDir = [];
|
|
3456
3878
|
const subdirs = [];
|
|
3457
3879
|
for (const entry of entries) {
|
|
3458
|
-
const fullPath =
|
|
3459
|
-
const relativePath =
|
|
3880
|
+
const fullPath = path9.join(dir, entry.name);
|
|
3881
|
+
const relativePath = path9.relative(projectRoot, fullPath);
|
|
3460
3882
|
if (isHiddenPathSegment(entry.name)) {
|
|
3461
3883
|
if (entry.isDirectory()) {
|
|
3462
3884
|
skipped.push({ path: relativePath, reason: "excluded" });
|
|
@@ -3476,7 +3898,7 @@ async function* walkDirectory(dir, projectRoot, includePatterns, excludePatterns
|
|
|
3476
3898
|
if (entry.isDirectory()) {
|
|
3477
3899
|
subdirs.push({ fullPath, relativePath });
|
|
3478
3900
|
} else if (entry.isFile()) {
|
|
3479
|
-
const stat4 = await
|
|
3901
|
+
const stat4 = await import_fs7.promises.stat(fullPath);
|
|
3480
3902
|
if (stat4.size > maxFileSize) {
|
|
3481
3903
|
skipped.push({ path: relativePath, reason: "too_large" });
|
|
3482
3904
|
continue;
|
|
@@ -3505,7 +3927,7 @@ async function* walkDirectory(dir, projectRoot, includePatterns, excludePatterns
|
|
|
3505
3927
|
yield f;
|
|
3506
3928
|
}
|
|
3507
3929
|
for (let i = options.maxFilesPerDirectory; i < filesInDir.length; i++) {
|
|
3508
|
-
skipped.push({ path:
|
|
3930
|
+
skipped.push({ path: path9.relative(projectRoot, filesInDir[i].path), reason: "excluded" });
|
|
3509
3931
|
}
|
|
3510
3932
|
const canRecurse = options.maxDepth === -1 || currentDepth < options.maxDepth;
|
|
3511
3933
|
if (canRecurse) {
|
|
@@ -3545,14 +3967,14 @@ async function collectFiles(projectRoot, includePatterns, excludePatterns, maxFi
|
|
|
3545
3967
|
if (additionalRoots && additionalRoots.length > 0) {
|
|
3546
3968
|
const normalizedRoots = /* @__PURE__ */ new Set();
|
|
3547
3969
|
for (const kbRoot of additionalRoots) {
|
|
3548
|
-
const resolved =
|
|
3549
|
-
|
|
3970
|
+
const resolved = path9.normalize(
|
|
3971
|
+
path9.isAbsolute(kbRoot) ? kbRoot : path9.resolve(projectRoot, kbRoot)
|
|
3550
3972
|
);
|
|
3551
3973
|
normalizedRoots.add(resolved);
|
|
3552
3974
|
}
|
|
3553
3975
|
for (const resolvedKbRoot of normalizedRoots) {
|
|
3554
3976
|
try {
|
|
3555
|
-
const stat4 = await
|
|
3977
|
+
const stat4 = await import_fs7.promises.stat(resolvedKbRoot);
|
|
3556
3978
|
if (!stat4.isDirectory()) {
|
|
3557
3979
|
skipped.push({ path: resolvedKbRoot, reason: "excluded" });
|
|
3558
3980
|
continue;
|
|
@@ -3953,14 +4375,14 @@ function initializeLogger(config) {
|
|
|
3953
4375
|
}
|
|
3954
4376
|
|
|
3955
4377
|
// src/native/index.ts
|
|
3956
|
-
var
|
|
3957
|
-
var
|
|
4378
|
+
var path10 = __toESM(require("path"), 1);
|
|
4379
|
+
var os4 = __toESM(require("os"), 1);
|
|
3958
4380
|
var module2 = __toESM(require("module"), 1);
|
|
3959
4381
|
var import_url = require("url");
|
|
3960
4382
|
var import_meta = {};
|
|
3961
4383
|
function getNativeBinding() {
|
|
3962
|
-
const platform2 =
|
|
3963
|
-
const arch2 =
|
|
4384
|
+
const platform2 = os4.platform();
|
|
4385
|
+
const arch2 = os4.arch();
|
|
3964
4386
|
let bindingName;
|
|
3965
4387
|
if (platform2 === "darwin" && arch2 === "arm64") {
|
|
3966
4388
|
bindingName = "codebase-index-native.darwin-arm64.node";
|
|
@@ -3978,19 +4400,19 @@ function getNativeBinding() {
|
|
|
3978
4400
|
let currentDir;
|
|
3979
4401
|
let requireTarget;
|
|
3980
4402
|
if (typeof import_meta !== "undefined" && import_meta.url) {
|
|
3981
|
-
currentDir =
|
|
4403
|
+
currentDir = path10.dirname((0, import_url.fileURLToPath)(import_meta.url));
|
|
3982
4404
|
requireTarget = import_meta.url;
|
|
3983
4405
|
} else if (typeof __dirname !== "undefined") {
|
|
3984
4406
|
currentDir = __dirname;
|
|
3985
4407
|
requireTarget = __filename;
|
|
3986
4408
|
} else {
|
|
3987
4409
|
currentDir = process.cwd();
|
|
3988
|
-
requireTarget =
|
|
4410
|
+
requireTarget = path10.join(currentDir, "index.js");
|
|
3989
4411
|
}
|
|
3990
4412
|
const normalizedDir = currentDir.replace(/\\/g, "/");
|
|
3991
|
-
const isDevMode = normalizedDir.includes("/src/native") || currentDir.includes(
|
|
3992
|
-
const packageRoot = isDevMode ?
|
|
3993
|
-
const nativePath =
|
|
4413
|
+
const isDevMode = normalizedDir.includes("/src/native") || currentDir.includes(path10.join("src", "native"));
|
|
4414
|
+
const packageRoot = isDevMode ? path10.resolve(currentDir, "../..") : path10.resolve(currentDir, "..");
|
|
4415
|
+
const nativePath = path10.join(packageRoot, "native", bindingName);
|
|
3994
4416
|
const require2 = module2.createRequire(requireTarget);
|
|
3995
4417
|
return require2(nativePath);
|
|
3996
4418
|
}
|
|
@@ -4032,6 +4454,12 @@ function createMockNativeBinding() {
|
|
|
4032
4454
|
constructor() {
|
|
4033
4455
|
throw error;
|
|
4034
4456
|
}
|
|
4457
|
+
static openReadOnly() {
|
|
4458
|
+
throw error;
|
|
4459
|
+
}
|
|
4460
|
+
static createEmptyReadOnly() {
|
|
4461
|
+
throw error;
|
|
4462
|
+
}
|
|
4035
4463
|
close() {
|
|
4036
4464
|
throw error;
|
|
4037
4465
|
}
|
|
@@ -4135,6 +4563,12 @@ var VectorStore = class {
|
|
|
4135
4563
|
load() {
|
|
4136
4564
|
this.inner.load();
|
|
4137
4565
|
}
|
|
4566
|
+
loadStrict() {
|
|
4567
|
+
this.inner.loadStrict();
|
|
4568
|
+
}
|
|
4569
|
+
hasFingerprint() {
|
|
4570
|
+
return this.inner.hasFingerprint();
|
|
4571
|
+
}
|
|
4138
4572
|
count() {
|
|
4139
4573
|
return this.inner.count();
|
|
4140
4574
|
}
|
|
@@ -4417,12 +4851,24 @@ var InvertedIndex = class {
|
|
|
4417
4851
|
return this.inner.documentCount();
|
|
4418
4852
|
}
|
|
4419
4853
|
};
|
|
4420
|
-
var Database = class {
|
|
4854
|
+
var Database = class _Database {
|
|
4421
4855
|
inner;
|
|
4422
4856
|
closed = false;
|
|
4423
4857
|
constructor(dbPath) {
|
|
4424
4858
|
this.inner = new native.Database(dbPath);
|
|
4425
4859
|
}
|
|
4860
|
+
static fromNative(inner) {
|
|
4861
|
+
const database = Object.create(_Database.prototype);
|
|
4862
|
+
database.inner = inner;
|
|
4863
|
+
database.closed = false;
|
|
4864
|
+
return database;
|
|
4865
|
+
}
|
|
4866
|
+
static openReadOnly(dbPath) {
|
|
4867
|
+
return _Database.fromNative(native.Database.openReadOnly(dbPath));
|
|
4868
|
+
}
|
|
4869
|
+
static createEmptyReadOnly() {
|
|
4870
|
+
return _Database.fromNative(native.Database.createEmptyReadOnly());
|
|
4871
|
+
}
|
|
4426
4872
|
throwIfClosed() {
|
|
4427
4873
|
if (this.closed) {
|
|
4428
4874
|
throw new Error("Database is closed");
|
|
@@ -4699,7 +5145,7 @@ var Database = class {
|
|
|
4699
5145
|
};
|
|
4700
5146
|
|
|
4701
5147
|
// src/tools/changed-files.ts
|
|
4702
|
-
var
|
|
5148
|
+
var path11 = __toESM(require("path"), 1);
|
|
4703
5149
|
var import_child_process = require("child_process");
|
|
4704
5150
|
var import_util = require("util");
|
|
4705
5151
|
var execFileAsync = (0, import_util.promisify)(import_child_process.execFile);
|
|
@@ -4787,8 +5233,8 @@ function normalizeFiles(rawFiles, projectRoot) {
|
|
|
4787
5233
|
for (const raw of rawFiles) {
|
|
4788
5234
|
const trimmed = raw.trim();
|
|
4789
5235
|
if (!trimmed) continue;
|
|
4790
|
-
const absolute =
|
|
4791
|
-
const relative11 =
|
|
5236
|
+
const absolute = path11.resolve(projectRoot, trimmed);
|
|
5237
|
+
const relative11 = path11.relative(projectRoot, absolute);
|
|
4792
5238
|
const cleaned = relative11.startsWith("./") ? relative11.slice(2) : relative11;
|
|
4793
5239
|
if (!seen.has(cleaned)) {
|
|
4794
5240
|
seen.add(cleaned);
|
|
@@ -4800,7 +5246,7 @@ function normalizeFiles(rawFiles, projectRoot) {
|
|
|
4800
5246
|
|
|
4801
5247
|
// src/indexer/git-blame.ts
|
|
4802
5248
|
var import_child_process2 = require("child_process");
|
|
4803
|
-
var
|
|
5249
|
+
var path12 = __toESM(require("path"), 1);
|
|
4804
5250
|
var import_util2 = require("util");
|
|
4805
5251
|
var execFileAsync2 = (0, import_util2.promisify)(import_child_process2.execFile);
|
|
4806
5252
|
function parseGitBlamePorcelain(output) {
|
|
@@ -4838,7 +5284,7 @@ function parseGitBlamePorcelain(output) {
|
|
|
4838
5284
|
return Array.from(commits.values()).filter((commit) => commit.lines > 0).sort((a, b) => b.lines - a.lines || b.committedAt - a.committedAt)[0];
|
|
4839
5285
|
}
|
|
4840
5286
|
async function getChunkGitBlame(projectRoot, filePath, startLine, endLine) {
|
|
4841
|
-
const relativePath =
|
|
5287
|
+
const relativePath = path12.relative(projectRoot, filePath);
|
|
4842
5288
|
try {
|
|
4843
5289
|
const { stdout } = await execFileAsync2(
|
|
4844
5290
|
"git",
|
|
@@ -4932,6 +5378,7 @@ function isSqliteCorruptionError(error) {
|
|
|
4932
5378
|
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");
|
|
4933
5379
|
}
|
|
4934
5380
|
var STARTUP_WARNING_METADATA_KEY = "index.startupWarning";
|
|
5381
|
+
var READER_ARTIFACT_RETRY_INTERVAL_MS = 1e3;
|
|
4935
5382
|
function metadataFromBlame(blame) {
|
|
4936
5383
|
if (!blame) {
|
|
4937
5384
|
return {};
|
|
@@ -5129,9 +5576,9 @@ function hasAllEmbeddingParts(parts, expectedPartCount) {
|
|
|
5129
5576
|
return true;
|
|
5130
5577
|
}
|
|
5131
5578
|
function isPathWithinRoot(filePath, rootPath) {
|
|
5132
|
-
const normalizedFilePath =
|
|
5133
|
-
const normalizedRoot =
|
|
5134
|
-
return normalizedFilePath === normalizedRoot || normalizedFilePath.startsWith(`${normalizedRoot}${
|
|
5579
|
+
const normalizedFilePath = path13.resolve(filePath);
|
|
5580
|
+
const normalizedRoot = path13.resolve(rootPath);
|
|
5581
|
+
return normalizedFilePath === normalizedRoot || normalizedFilePath.startsWith(`${normalizedRoot}${path13.sep}`);
|
|
5135
5582
|
}
|
|
5136
5583
|
var rankingQueryTokenCache = /* @__PURE__ */ new Map();
|
|
5137
5584
|
var rankingNameTokenCache = /* @__PURE__ */ new Map();
|
|
@@ -6197,26 +6644,133 @@ var Indexer = class {
|
|
|
6197
6644
|
queryCacheTtlMs = 5 * 60 * 1e3;
|
|
6198
6645
|
querySimilarityThreshold = 0.85;
|
|
6199
6646
|
indexCompatibility = null;
|
|
6200
|
-
|
|
6647
|
+
activeIndexLease = null;
|
|
6648
|
+
initializationPromise = null;
|
|
6649
|
+
initializationMode = "none";
|
|
6650
|
+
readIssues = [];
|
|
6651
|
+
retiredDatabases = [];
|
|
6652
|
+
readerArtifactFingerprint = null;
|
|
6653
|
+
writerArtifactFingerprint = null;
|
|
6654
|
+
readerArtifactRetryAfter = /* @__PURE__ */ new Map();
|
|
6201
6655
|
constructor(projectRoot, config, host = "opencode") {
|
|
6202
6656
|
this.projectRoot = projectRoot;
|
|
6203
6657
|
this.config = config;
|
|
6204
6658
|
this.host = host;
|
|
6205
6659
|
this.indexPath = this.getIndexPath();
|
|
6206
|
-
this.fileHashCachePath =
|
|
6207
|
-
this.failedBatchesPath =
|
|
6208
|
-
this.indexingLockPath = path12.join(this.indexPath, "indexing.lock");
|
|
6660
|
+
this.fileHashCachePath = path13.join(this.indexPath, "file-hashes.json");
|
|
6661
|
+
this.failedBatchesPath = path13.join(this.indexPath, "failed-batches.json");
|
|
6209
6662
|
this.logger = initializeLogger(config.debug);
|
|
6210
6663
|
}
|
|
6211
6664
|
getIndexPath() {
|
|
6212
6665
|
return resolveProjectIndexPath(this.projectRoot, this.config.scope, this.host);
|
|
6213
6666
|
}
|
|
6667
|
+
isLocalProjectIndexPath() {
|
|
6668
|
+
const localProjectIndexPaths = [path13.join(this.projectRoot, getHostProjectIndexRelativePath(this.host))];
|
|
6669
|
+
if (this.host !== "opencode") {
|
|
6670
|
+
localProjectIndexPaths.push(path13.join(this.projectRoot, getHostProjectIndexRelativePath("opencode")));
|
|
6671
|
+
}
|
|
6672
|
+
return localProjectIndexPaths.some((localPath) => {
|
|
6673
|
+
if (!(0, import_fs8.existsSync)(localPath) || !(0, import_fs8.existsSync)(this.indexPath)) {
|
|
6674
|
+
return path13.resolve(this.indexPath) === path13.resolve(localPath);
|
|
6675
|
+
}
|
|
6676
|
+
const indexStats = (0, import_fs8.statSync)(this.indexPath);
|
|
6677
|
+
const localStats = (0, import_fs8.statSync)(localPath);
|
|
6678
|
+
return indexStats.dev === localStats.dev && indexStats.ino === localStats.ino;
|
|
6679
|
+
});
|
|
6680
|
+
}
|
|
6681
|
+
resetLoadedIndexState(retireDatabase = false) {
|
|
6682
|
+
if (this.database) {
|
|
6683
|
+
if (retireDatabase) {
|
|
6684
|
+
this.retiredDatabases.push(this.database);
|
|
6685
|
+
} else {
|
|
6686
|
+
this.database.close();
|
|
6687
|
+
}
|
|
6688
|
+
}
|
|
6689
|
+
this.store = null;
|
|
6690
|
+
this.invertedIndex = null;
|
|
6691
|
+
this.database = null;
|
|
6692
|
+
this.provider = null;
|
|
6693
|
+
this.configuredProviderInfo = null;
|
|
6694
|
+
this.reranker = null;
|
|
6695
|
+
this.indexCompatibility = null;
|
|
6696
|
+
this.initializationMode = "none";
|
|
6697
|
+
this.readIssues = [];
|
|
6698
|
+
this.readerArtifactFingerprint = null;
|
|
6699
|
+
this.writerArtifactFingerprint = null;
|
|
6700
|
+
this.readerArtifactRetryAfter.clear();
|
|
6701
|
+
this.fileHashCache.clear();
|
|
6702
|
+
}
|
|
6703
|
+
refreshLoadedIndexState() {
|
|
6704
|
+
if (!this.store || !this.invertedIndex || !this.configuredProviderInfo) return;
|
|
6705
|
+
this.store.load();
|
|
6706
|
+
this.invertedIndex.load();
|
|
6707
|
+
this.fileHashCache.clear();
|
|
6708
|
+
this.loadFileHashCache();
|
|
6709
|
+
this.indexCompatibility = this.validateIndexCompatibility(this.configuredProviderInfo);
|
|
6710
|
+
this.readIssues = [];
|
|
6711
|
+
this.readerArtifactRetryAfter.clear();
|
|
6712
|
+
}
|
|
6713
|
+
async withIndexMutationLease(operation, callback) {
|
|
6714
|
+
const lease = acquireIndexLock(this.indexPath, operation);
|
|
6715
|
+
this.indexPath = lease.canonicalIndexPath;
|
|
6716
|
+
this.fileHashCachePath = path13.join(this.indexPath, "file-hashes.json");
|
|
6717
|
+
this.failedBatchesPath = path13.join(this.indexPath, "failed-batches.json");
|
|
6718
|
+
this.activeIndexLease = lease;
|
|
6719
|
+
let result;
|
|
6720
|
+
let callbackError;
|
|
6721
|
+
let callbackFailed = false;
|
|
6722
|
+
try {
|
|
6723
|
+
result = await callback(lease.recoveries.map(({ owner }) => owner));
|
|
6724
|
+
} catch (error) {
|
|
6725
|
+
callbackFailed = true;
|
|
6726
|
+
callbackError = error;
|
|
6727
|
+
}
|
|
6728
|
+
if (!callbackFailed) {
|
|
6729
|
+
try {
|
|
6730
|
+
completeLeaseRecovery(lease);
|
|
6731
|
+
this.writerArtifactFingerprint = this.captureReaderArtifactFingerprint();
|
|
6732
|
+
} catch (error) {
|
|
6733
|
+
callbackFailed = true;
|
|
6734
|
+
callbackError = error;
|
|
6735
|
+
}
|
|
6736
|
+
}
|
|
6737
|
+
let releaseError;
|
|
6738
|
+
try {
|
|
6739
|
+
if (!releaseIndexLock(lease)) {
|
|
6740
|
+
releaseError = new Error(`Lost ownership of index mutation lease ${lease.owner.token}`);
|
|
6741
|
+
this.writerArtifactFingerprint = null;
|
|
6742
|
+
if (this.activeIndexLease?.owner.token === lease.owner.token) {
|
|
6743
|
+
this.activeIndexLease = null;
|
|
6744
|
+
}
|
|
6745
|
+
} else if (this.activeIndexLease?.owner.token === lease.owner.token) {
|
|
6746
|
+
this.activeIndexLease = null;
|
|
6747
|
+
}
|
|
6748
|
+
} catch (error) {
|
|
6749
|
+
releaseError = error;
|
|
6750
|
+
this.writerArtifactFingerprint = null;
|
|
6751
|
+
if (!(0, import_fs8.existsSync)(lease.lockPath) && this.activeIndexLease?.owner.token === lease.owner.token) {
|
|
6752
|
+
this.activeIndexLease = null;
|
|
6753
|
+
}
|
|
6754
|
+
}
|
|
6755
|
+
if (releaseError !== void 0) {
|
|
6756
|
+
if (callbackFailed) throw new AggregateError([callbackError, releaseError], "Index mutation and lease release both failed");
|
|
6757
|
+
throw releaseError;
|
|
6758
|
+
}
|
|
6759
|
+
if (callbackFailed) throw callbackError;
|
|
6760
|
+
return result;
|
|
6761
|
+
}
|
|
6762
|
+
requireActiveLease() {
|
|
6763
|
+
if (!this.activeIndexLease) {
|
|
6764
|
+
throw new Error("Index mutation attempted without an active interprocess lease");
|
|
6765
|
+
}
|
|
6766
|
+
return this.activeIndexLease;
|
|
6767
|
+
}
|
|
6214
6768
|
loadFileHashCache() {
|
|
6215
|
-
if (!(0,
|
|
6769
|
+
if (!(0, import_fs8.existsSync)(this.fileHashCachePath)) {
|
|
6216
6770
|
return;
|
|
6217
6771
|
}
|
|
6218
6772
|
try {
|
|
6219
|
-
const data = (0,
|
|
6773
|
+
const data = (0, import_fs8.readFileSync)(this.fileHashCachePath, "utf-8");
|
|
6220
6774
|
const parsed = JSON.parse(data);
|
|
6221
6775
|
this.fileHashCache = new Map(Object.entries(parsed));
|
|
6222
6776
|
} catch (error) {
|
|
@@ -6236,15 +6790,26 @@ var Indexer = class {
|
|
|
6236
6790
|
this.atomicWriteSync(this.fileHashCachePath, JSON.stringify(obj));
|
|
6237
6791
|
}
|
|
6238
6792
|
atomicWriteSync(targetPath, data) {
|
|
6239
|
-
const
|
|
6240
|
-
(
|
|
6241
|
-
(0,
|
|
6242
|
-
|
|
6793
|
+
const lease = this.requireActiveLease();
|
|
6794
|
+
const tempPath = createLeaseTemporaryPath(targetPath, lease.owner, "tmp");
|
|
6795
|
+
(0, import_fs8.mkdirSync)(path13.dirname(targetPath), { recursive: true });
|
|
6796
|
+
try {
|
|
6797
|
+
(0, import_fs8.writeFileSync)(tempPath, data);
|
|
6798
|
+
(0, import_fs8.renameSync)(tempPath, targetPath);
|
|
6799
|
+
} finally {
|
|
6800
|
+
removeLeaseTemporaryPath(tempPath);
|
|
6801
|
+
}
|
|
6802
|
+
}
|
|
6803
|
+
saveInvertedIndex(invertedIndex) {
|
|
6804
|
+
this.atomicWriteSync(
|
|
6805
|
+
path13.join(this.indexPath, "inverted-index.json"),
|
|
6806
|
+
invertedIndex.serialize()
|
|
6807
|
+
);
|
|
6243
6808
|
}
|
|
6244
6809
|
getScopedRoots() {
|
|
6245
|
-
const roots = /* @__PURE__ */ new Set([
|
|
6810
|
+
const roots = /* @__PURE__ */ new Set([path13.resolve(this.projectRoot)]);
|
|
6246
6811
|
for (const kbRoot of this.config.knowledgeBases) {
|
|
6247
|
-
roots.add(
|
|
6812
|
+
roots.add(path13.resolve(this.projectRoot, kbRoot));
|
|
6248
6813
|
}
|
|
6249
6814
|
return Array.from(roots);
|
|
6250
6815
|
}
|
|
@@ -6253,29 +6818,29 @@ var Indexer = class {
|
|
|
6253
6818
|
if (this.config.scope !== "global") {
|
|
6254
6819
|
return branchName;
|
|
6255
6820
|
}
|
|
6256
|
-
const projectHash = hashContent(
|
|
6821
|
+
const projectHash = hashContent(path13.resolve(this.projectRoot)).slice(0, 16);
|
|
6257
6822
|
return `${projectHash}:${branchName}`;
|
|
6258
6823
|
}
|
|
6259
6824
|
getBranchCatalogKeyFor(branchName) {
|
|
6260
6825
|
if (this.config.scope !== "global") {
|
|
6261
6826
|
return branchName;
|
|
6262
6827
|
}
|
|
6263
|
-
const projectHash = hashContent(
|
|
6828
|
+
const projectHash = hashContent(path13.resolve(this.projectRoot)).slice(0, 16);
|
|
6264
6829
|
return `${projectHash}:${branchName}`;
|
|
6265
6830
|
}
|
|
6266
6831
|
getLegacyBranchCatalogKey() {
|
|
6267
6832
|
return this.currentBranch || "default";
|
|
6268
6833
|
}
|
|
6269
6834
|
getLegacyMigrationMetadataKey() {
|
|
6270
|
-
const projectHash = hashContent(
|
|
6835
|
+
const projectHash = hashContent(path13.resolve(this.projectRoot)).slice(0, 16);
|
|
6271
6836
|
return `index.globalBranchMigration.${projectHash}`;
|
|
6272
6837
|
}
|
|
6273
6838
|
getProjectEmbeddingStrategyMetadataKey() {
|
|
6274
|
-
const projectHash = hashContent(
|
|
6839
|
+
const projectHash = hashContent(path13.resolve(this.projectRoot)).slice(0, 16);
|
|
6275
6840
|
return `index.embeddingStrategyVersion.${projectHash}`;
|
|
6276
6841
|
}
|
|
6277
6842
|
getProjectForceReembedMetadataKey() {
|
|
6278
|
-
const projectHash = hashContent(
|
|
6843
|
+
const projectHash = hashContent(path13.resolve(this.projectRoot)).slice(0, 16);
|
|
6279
6844
|
return `index.forceReembed.${projectHash}`;
|
|
6280
6845
|
}
|
|
6281
6846
|
hasProjectForceReembedPending() {
|
|
@@ -6369,7 +6934,7 @@ var Indexer = class {
|
|
|
6369
6934
|
if (!this.database) {
|
|
6370
6935
|
return { chunkIds, symbolIds };
|
|
6371
6936
|
}
|
|
6372
|
-
const projectRootPath =
|
|
6937
|
+
const projectRootPath = path13.resolve(this.projectRoot);
|
|
6373
6938
|
const projectLocalFilePaths = /* @__PURE__ */ new Set([
|
|
6374
6939
|
...Array.from(this.fileHashCache.keys()).filter(
|
|
6375
6940
|
(filePath) => this.isFileInCurrentScope(filePath, roots) && isPathWithinRoot(filePath, projectRootPath)
|
|
@@ -6392,7 +6957,7 @@ var Indexer = class {
|
|
|
6392
6957
|
if (this.config.scope !== "global") {
|
|
6393
6958
|
return this.getBranchCatalogCleanupKeys();
|
|
6394
6959
|
}
|
|
6395
|
-
const projectHash = hashContent(
|
|
6960
|
+
const projectHash = hashContent(path13.resolve(this.projectRoot)).slice(0, 16);
|
|
6396
6961
|
const keys = /* @__PURE__ */ new Set();
|
|
6397
6962
|
const projectChunkIdSet = new Set(projectChunkIds);
|
|
6398
6963
|
const projectSymbolIdSet = new Set(projectSymbolIds);
|
|
@@ -6476,7 +7041,7 @@ var Indexer = class {
|
|
|
6476
7041
|
if (!this.database || this.config.scope !== "global") {
|
|
6477
7042
|
return false;
|
|
6478
7043
|
}
|
|
6479
|
-
const projectHash = hashContent(
|
|
7044
|
+
const projectHash = hashContent(path13.resolve(this.projectRoot)).slice(0, 16);
|
|
6480
7045
|
const roots = this.getScopedRoots();
|
|
6481
7046
|
const { chunkIds: projectLocalChunkIds, symbolIds: projectLocalSymbolIds } = this.getProjectLocalScopedOwnershipIds(roots);
|
|
6482
7047
|
return this.database.getAllBranches().some(
|
|
@@ -6510,7 +7075,7 @@ var Indexer = class {
|
|
|
6510
7075
|
...Array.from(this.fileHashCache.keys()).filter((filePath) => this.isFileInCurrentScope(filePath, roots)),
|
|
6511
7076
|
...scopedEntries.map(({ metadata }) => metadata.filePath)
|
|
6512
7077
|
]);
|
|
6513
|
-
const projectRootPath =
|
|
7078
|
+
const projectRootPath = path13.resolve(this.projectRoot);
|
|
6514
7079
|
const projectLocalFilePaths = new Set(
|
|
6515
7080
|
Array.from(filePaths).filter((filePath) => isPathWithinRoot(filePath, projectRootPath))
|
|
6516
7081
|
);
|
|
@@ -6572,34 +7137,27 @@ var Indexer = class {
|
|
|
6572
7137
|
database.gcOrphanEmbeddings();
|
|
6573
7138
|
database.gcOrphanChunks();
|
|
6574
7139
|
store.save();
|
|
6575
|
-
|
|
7140
|
+
this.saveInvertedIndex(invertedIndex);
|
|
6576
7141
|
return {
|
|
6577
7142
|
removedChunkIds: removedChunkIdList,
|
|
6578
7143
|
hasForeignData: allMetadata.some(({ metadata }) => !this.isFileInCurrentScope(metadata.filePath, roots))
|
|
6579
7144
|
};
|
|
6580
7145
|
}
|
|
6581
|
-
|
|
6582
|
-
|
|
6583
|
-
|
|
6584
|
-
|
|
6585
|
-
|
|
6586
|
-
|
|
6587
|
-
|
|
6588
|
-
|
|
6589
|
-
(0, import_fs7.writeFileSync)(this.indexingLockPath, JSON.stringify(lockData));
|
|
6590
|
-
}
|
|
6591
|
-
releaseIndexingLock() {
|
|
6592
|
-
if ((0, import_fs7.existsSync)(this.indexingLockPath)) {
|
|
6593
|
-
(0, import_fs7.unlinkSync)(this.indexingLockPath);
|
|
7146
|
+
async recoverFromInterruptedIndexingUnlocked(owners) {
|
|
7147
|
+
for (const owner of owners) {
|
|
7148
|
+
this.logger.warn("Detected interrupted indexing session, recovering...", {
|
|
7149
|
+
pid: owner.pid,
|
|
7150
|
+
hostname: owner.hostname,
|
|
7151
|
+
operation: owner.operation,
|
|
7152
|
+
startedAt: owner.startedAt
|
|
7153
|
+
});
|
|
6594
7154
|
}
|
|
6595
|
-
|
|
6596
|
-
|
|
6597
|
-
|
|
6598
|
-
|
|
6599
|
-
|
|
7155
|
+
if (this.config.scope === "global") {
|
|
7156
|
+
if ((0, import_fs8.existsSync)(this.fileHashCachePath)) {
|
|
7157
|
+
(0, import_fs8.unlinkSync)(this.fileHashCachePath);
|
|
7158
|
+
}
|
|
7159
|
+
await this.healthCheckUnlocked();
|
|
6600
7160
|
}
|
|
6601
|
-
await this.healthCheck();
|
|
6602
|
-
this.releaseIndexingLock();
|
|
6603
7161
|
this.logger.info("Recovery complete, next index will re-process all files");
|
|
6604
7162
|
}
|
|
6605
7163
|
loadFailedBatches(maxChunkTokens) {
|
|
@@ -6615,10 +7173,10 @@ var Indexer = class {
|
|
|
6615
7173
|
}
|
|
6616
7174
|
}
|
|
6617
7175
|
loadSerializedFailedBatches() {
|
|
6618
|
-
if (!(0,
|
|
7176
|
+
if (!(0, import_fs8.existsSync)(this.failedBatchesPath)) {
|
|
6619
7177
|
return [];
|
|
6620
7178
|
}
|
|
6621
|
-
const data = (0,
|
|
7179
|
+
const data = (0, import_fs8.readFileSync)(this.failedBatchesPath, "utf-8");
|
|
6622
7180
|
const parsed = JSON.parse(data);
|
|
6623
7181
|
return parsed.map((batch) => {
|
|
6624
7182
|
const chunks = Array.isArray(batch.chunks) ? batch.chunks : [];
|
|
@@ -6635,15 +7193,15 @@ var Indexer = class {
|
|
|
6635
7193
|
}
|
|
6636
7194
|
saveFailedBatches(batches) {
|
|
6637
7195
|
if (batches.length === 0) {
|
|
6638
|
-
if ((0,
|
|
7196
|
+
if ((0, import_fs8.existsSync)(this.failedBatchesPath)) {
|
|
6639
7197
|
try {
|
|
6640
|
-
(0,
|
|
7198
|
+
(0, import_fs8.unlinkSync)(this.failedBatchesPath);
|
|
6641
7199
|
} catch {
|
|
6642
7200
|
}
|
|
6643
7201
|
}
|
|
6644
7202
|
return;
|
|
6645
7203
|
}
|
|
6646
|
-
|
|
7204
|
+
this.atomicWriteSync(this.failedBatchesPath, JSON.stringify(batches, null, 2));
|
|
6647
7205
|
}
|
|
6648
7206
|
collectRetryableFailedChunks(currentFileHashes, unchangedFilePaths, maxChunkTokens) {
|
|
6649
7207
|
const retryableById = /* @__PURE__ */ new Map();
|
|
@@ -6823,7 +7381,7 @@ var Indexer = class {
|
|
|
6823
7381
|
const intent = isLikelyImplementationPath(candidate.metadata.filePath) ? "implementation" : "doc_or_test";
|
|
6824
7382
|
parts.push(`intent_hint: ${intent}`);
|
|
6825
7383
|
try {
|
|
6826
|
-
const fileContent = await
|
|
7384
|
+
const fileContent = await import_fs8.promises.readFile(candidate.metadata.filePath, "utf-8");
|
|
6827
7385
|
const lines = fileContent.split("\n");
|
|
6828
7386
|
const snippetStartLine = Math.max(1, candidate.metadata.startLine - 2);
|
|
6829
7387
|
const snippetEndLine = Math.min(lines.length, candidate.metadata.endLine + 2);
|
|
@@ -6837,6 +7395,222 @@ var Indexer = class {
|
|
|
6837
7395
|
return parts.join("\n");
|
|
6838
7396
|
}
|
|
6839
7397
|
async initialize() {
|
|
7398
|
+
if (this.initializationPromise) {
|
|
7399
|
+
await this.initializationPromise;
|
|
7400
|
+
}
|
|
7401
|
+
if (this.isInitializedFor("reader")) {
|
|
7402
|
+
return;
|
|
7403
|
+
}
|
|
7404
|
+
await this.initializeOnce("reader", [], { skipAutoGc: true });
|
|
7405
|
+
}
|
|
7406
|
+
async initializeOnce(mode, recoveredOwners, options) {
|
|
7407
|
+
if (this.initializationPromise) {
|
|
7408
|
+
await this.initializationPromise;
|
|
7409
|
+
if (this.isInitializedFor(mode)) {
|
|
7410
|
+
return;
|
|
7411
|
+
}
|
|
7412
|
+
return this.initializeOnce(mode, recoveredOwners, options);
|
|
7413
|
+
}
|
|
7414
|
+
if (this.isInitializedFor(mode)) {
|
|
7415
|
+
return;
|
|
7416
|
+
}
|
|
7417
|
+
const initialization = this.initializeUnlocked(mode, recoveredOwners, options).catch((error) => {
|
|
7418
|
+
this.resetLoadedIndexState();
|
|
7419
|
+
throw error;
|
|
7420
|
+
}).finally(() => {
|
|
7421
|
+
if (this.initializationPromise === initialization) {
|
|
7422
|
+
this.initializationPromise = null;
|
|
7423
|
+
}
|
|
7424
|
+
});
|
|
7425
|
+
this.initializationPromise = initialization;
|
|
7426
|
+
await initialization;
|
|
7427
|
+
}
|
|
7428
|
+
isInitializedFor(mode) {
|
|
7429
|
+
const hasState = Boolean(
|
|
7430
|
+
this.store && this.provider && this.invertedIndex && this.configuredProviderInfo && this.database
|
|
7431
|
+
);
|
|
7432
|
+
if (!hasState) {
|
|
7433
|
+
return false;
|
|
7434
|
+
}
|
|
7435
|
+
return mode === "reader" ? this.initializationMode !== "none" : this.initializationMode === "writer";
|
|
7436
|
+
}
|
|
7437
|
+
recordReadIssue(component, message, error) {
|
|
7438
|
+
this.readIssues.push(this.createReadIssue(component, message));
|
|
7439
|
+
this.readerArtifactRetryAfter.set(component, Date.now() + READER_ARTIFACT_RETRY_INTERVAL_MS);
|
|
7440
|
+
this.logger.warn(message, error === void 0 ? void 0 : { error: getErrorMessage2(error) });
|
|
7441
|
+
}
|
|
7442
|
+
createReadIssue(component, message) {
|
|
7443
|
+
return {
|
|
7444
|
+
component,
|
|
7445
|
+
message,
|
|
7446
|
+
blocking: component !== "keyword"
|
|
7447
|
+
};
|
|
7448
|
+
}
|
|
7449
|
+
getVectorReadIssueMessage() {
|
|
7450
|
+
if (this.config.scope === "global") {
|
|
7451
|
+
return "Shared vector index could not be read. Restore or repair the complete fingerprinted shared vector artifacts; automatic reset is disabled for global scope.";
|
|
7452
|
+
}
|
|
7453
|
+
if (!this.isLocalProjectIndexPath()) {
|
|
7454
|
+
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.";
|
|
7455
|
+
}
|
|
7456
|
+
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.";
|
|
7457
|
+
}
|
|
7458
|
+
getKeywordReadIssueMessage() {
|
|
7459
|
+
if (this.config.scope === "global") {
|
|
7460
|
+
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.";
|
|
7461
|
+
}
|
|
7462
|
+
if (!this.isLocalProjectIndexPath()) {
|
|
7463
|
+
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.";
|
|
7464
|
+
}
|
|
7465
|
+
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.";
|
|
7466
|
+
}
|
|
7467
|
+
getDatabaseReadIssueMessage() {
|
|
7468
|
+
if (this.config.scope === "global") {
|
|
7469
|
+
return "Shared index database could not be read. Restore or repair the shared SQLite database; automatic reset is disabled for global scope.";
|
|
7470
|
+
}
|
|
7471
|
+
if (!this.isLocalProjectIndexPath()) {
|
|
7472
|
+
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.";
|
|
7473
|
+
}
|
|
7474
|
+
return "Index database could not be read. Run index_codebase after the active writer finishes to repair or migrate it under the writer lease.";
|
|
7475
|
+
}
|
|
7476
|
+
getReaderFileFingerprint(filePath, identityOnly = false) {
|
|
7477
|
+
try {
|
|
7478
|
+
const stats = (0, import_fs8.statSync)(filePath);
|
|
7479
|
+
if (identityOnly) {
|
|
7480
|
+
return `${stats.dev}:${stats.ino}`;
|
|
7481
|
+
}
|
|
7482
|
+
return `${stats.dev}:${stats.ino}:${stats.size}:${stats.mtimeMs}:${stats.ctimeMs}`;
|
|
7483
|
+
} catch (error) {
|
|
7484
|
+
return `unavailable:${getErrorMessage2(error)}`;
|
|
7485
|
+
}
|
|
7486
|
+
}
|
|
7487
|
+
captureReaderArtifactFingerprint() {
|
|
7488
|
+
const storePath = path13.join(this.indexPath, "vectors");
|
|
7489
|
+
return {
|
|
7490
|
+
vectors: `${this.getReaderFileFingerprint(storePath)}|${this.getReaderFileFingerprint(`${storePath}.meta.json`)}`,
|
|
7491
|
+
keyword: this.getReaderFileFingerprint(path13.join(this.indexPath, "inverted-index.json")),
|
|
7492
|
+
database: this.getReaderFileFingerprint(path13.join(this.indexPath, "codebase.db")),
|
|
7493
|
+
databaseIdentity: this.getReaderFileFingerprint(path13.join(this.indexPath, "codebase.db"), true)
|
|
7494
|
+
};
|
|
7495
|
+
}
|
|
7496
|
+
refreshReaderArtifacts() {
|
|
7497
|
+
if (this.initializationMode !== "reader" || !this.configuredProviderInfo) {
|
|
7498
|
+
return;
|
|
7499
|
+
}
|
|
7500
|
+
const previousFingerprint = this.readerArtifactFingerprint;
|
|
7501
|
+
const currentFingerprint = this.captureReaderArtifactFingerprint();
|
|
7502
|
+
const issues = new Map(this.readIssues.map((issue) => [issue.component, issue]));
|
|
7503
|
+
const retryDue = (component) => issues.has(component) && Date.now() >= (this.readerArtifactRetryAfter.get(component) ?? 0);
|
|
7504
|
+
const vectorsChanged = !previousFingerprint || currentFingerprint.vectors !== previousFingerprint.vectors;
|
|
7505
|
+
const keywordChanged = !previousFingerprint || currentFingerprint.keyword !== previousFingerprint.keyword;
|
|
7506
|
+
const databaseChanged = !previousFingerprint || currentFingerprint.database !== previousFingerprint.database;
|
|
7507
|
+
const databaseReplaced = !previousFingerprint || currentFingerprint.databaseIdentity !== previousFingerprint.databaseIdentity;
|
|
7508
|
+
if (previousFingerprint && !vectorsChanged && !keywordChanged && !databaseChanged && !Array.from(issues.keys()).some(retryDue)) {
|
|
7509
|
+
return;
|
|
7510
|
+
}
|
|
7511
|
+
const setIssue = (component, message, error) => {
|
|
7512
|
+
if (!issues.has(component)) {
|
|
7513
|
+
this.logger.warn(message, error === void 0 ? void 0 : { error: getErrorMessage2(error) });
|
|
7514
|
+
}
|
|
7515
|
+
issues.set(component, this.createReadIssue(component, message));
|
|
7516
|
+
this.readerArtifactRetryAfter.set(component, Date.now() + READER_ARTIFACT_RETRY_INTERVAL_MS);
|
|
7517
|
+
};
|
|
7518
|
+
const storePath = path13.join(this.indexPath, "vectors");
|
|
7519
|
+
const vectorMetadataPath = `${storePath}.meta.json`;
|
|
7520
|
+
const invertedIndexPath = path13.join(this.indexPath, "inverted-index.json");
|
|
7521
|
+
const dbPath = path13.join(this.indexPath, "codebase.db");
|
|
7522
|
+
if (vectorsChanged || retryDue("vectors")) {
|
|
7523
|
+
const vectorStoreExists = (0, import_fs8.existsSync)(storePath);
|
|
7524
|
+
const vectorMetadataExists = (0, import_fs8.existsSync)(vectorMetadataPath);
|
|
7525
|
+
if (vectorStoreExists && vectorMetadataExists) {
|
|
7526
|
+
try {
|
|
7527
|
+
const store = new VectorStore(storePath, this.configuredProviderInfo.modelInfo.dimensions);
|
|
7528
|
+
store.loadStrict();
|
|
7529
|
+
this.store = store;
|
|
7530
|
+
issues.delete("vectors");
|
|
7531
|
+
this.readerArtifactRetryAfter.delete("vectors");
|
|
7532
|
+
} catch (error) {
|
|
7533
|
+
setIssue("vectors", this.getVectorReadIssueMessage(), error);
|
|
7534
|
+
}
|
|
7535
|
+
} else if (vectorStoreExists !== vectorMetadataExists || issues.has("vectors")) {
|
|
7536
|
+
setIssue("vectors", this.getVectorReadIssueMessage());
|
|
7537
|
+
}
|
|
7538
|
+
}
|
|
7539
|
+
if (keywordChanged || retryDue("keyword") || !(0, import_fs8.existsSync)(invertedIndexPath) && (this.store?.count() ?? 0) > 0) {
|
|
7540
|
+
if ((0, import_fs8.existsSync)(invertedIndexPath)) {
|
|
7541
|
+
try {
|
|
7542
|
+
const invertedIndex = new InvertedIndex(invertedIndexPath);
|
|
7543
|
+
invertedIndex.load();
|
|
7544
|
+
this.invertedIndex = invertedIndex;
|
|
7545
|
+
issues.delete("keyword");
|
|
7546
|
+
this.readerArtifactRetryAfter.delete("keyword");
|
|
7547
|
+
} catch (error) {
|
|
7548
|
+
setIssue("keyword", this.getKeywordReadIssueMessage(), error);
|
|
7549
|
+
}
|
|
7550
|
+
} else if ((this.store?.count() ?? 0) > 0 || issues.has("keyword")) {
|
|
7551
|
+
setIssue("keyword", this.getKeywordReadIssueMessage());
|
|
7552
|
+
}
|
|
7553
|
+
}
|
|
7554
|
+
if (databaseReplaced || databaseChanged && issues.has("database") || retryDue("database")) {
|
|
7555
|
+
if ((0, import_fs8.existsSync)(dbPath)) {
|
|
7556
|
+
try {
|
|
7557
|
+
const database = Database.openReadOnly(dbPath);
|
|
7558
|
+
if (this.database) {
|
|
7559
|
+
this.retiredDatabases.push(this.database);
|
|
7560
|
+
}
|
|
7561
|
+
this.database = database;
|
|
7562
|
+
issues.delete("database");
|
|
7563
|
+
this.readerArtifactRetryAfter.delete("database");
|
|
7564
|
+
} catch (error) {
|
|
7565
|
+
setIssue("database", this.getDatabaseReadIssueMessage(), error);
|
|
7566
|
+
}
|
|
7567
|
+
} else if ((this.store?.count() ?? 0) > 0 || issues.has("database")) {
|
|
7568
|
+
setIssue("database", this.getDatabaseReadIssueMessage());
|
|
7569
|
+
}
|
|
7570
|
+
}
|
|
7571
|
+
if (!issues.has("database")) {
|
|
7572
|
+
try {
|
|
7573
|
+
this.indexCompatibility = this.validateIndexCompatibility(this.configuredProviderInfo);
|
|
7574
|
+
} catch (error) {
|
|
7575
|
+
setIssue("database", this.getDatabaseReadIssueMessage(), error);
|
|
7576
|
+
}
|
|
7577
|
+
}
|
|
7578
|
+
this.readIssues = Array.from(issues.values());
|
|
7579
|
+
this.readerArtifactFingerprint = currentFingerprint;
|
|
7580
|
+
}
|
|
7581
|
+
refreshInactiveWriterArtifacts() {
|
|
7582
|
+
if (this.initializationMode !== "writer" || this.activeIndexLease) {
|
|
7583
|
+
return true;
|
|
7584
|
+
}
|
|
7585
|
+
const previousFingerprint = this.writerArtifactFingerprint;
|
|
7586
|
+
const currentFingerprint = this.captureReaderArtifactFingerprint();
|
|
7587
|
+
const retryDue = this.readIssues.some(
|
|
7588
|
+
(issue) => Date.now() >= (this.readerArtifactRetryAfter.get(issue.component) ?? 0)
|
|
7589
|
+
);
|
|
7590
|
+
const artifactsChanged = !previousFingerprint || currentFingerprint.vectors !== previousFingerprint.vectors || currentFingerprint.keyword !== previousFingerprint.keyword || currentFingerprint.database !== previousFingerprint.database || currentFingerprint.databaseIdentity !== previousFingerprint.databaseIdentity;
|
|
7591
|
+
if (!artifactsChanged && !retryDue) {
|
|
7592
|
+
return true;
|
|
7593
|
+
}
|
|
7594
|
+
if (!previousFingerprint || currentFingerprint.databaseIdentity !== previousFingerprint.databaseIdentity) {
|
|
7595
|
+
return false;
|
|
7596
|
+
}
|
|
7597
|
+
this.initializationMode = "reader";
|
|
7598
|
+
this.readerArtifactFingerprint = previousFingerprint;
|
|
7599
|
+
try {
|
|
7600
|
+
this.refreshReaderArtifacts();
|
|
7601
|
+
this.writerArtifactFingerprint = this.readerArtifactFingerprint ?? currentFingerprint;
|
|
7602
|
+
} finally {
|
|
7603
|
+
this.readerArtifactFingerprint = null;
|
|
7604
|
+
this.initializationMode = "writer";
|
|
7605
|
+
}
|
|
7606
|
+
return true;
|
|
7607
|
+
}
|
|
7608
|
+
async initializeUnlocked(mode, recoveredOwners = [], options = {}) {
|
|
7609
|
+
if (mode === "writer") {
|
|
7610
|
+
this.requireActiveLease();
|
|
7611
|
+
}
|
|
7612
|
+
this.readIssues = [];
|
|
7613
|
+
this.readerArtifactRetryAfter.clear();
|
|
6840
7614
|
if (this.config.embeddingProvider === "custom") {
|
|
6841
7615
|
if (!this.config.customProvider) {
|
|
6842
7616
|
throw new Error("embeddingProvider is 'custom' but customProvider config is missing.");
|
|
@@ -6868,36 +7642,103 @@ var Indexer = class {
|
|
|
6868
7642
|
});
|
|
6869
7643
|
}
|
|
6870
7644
|
}
|
|
6871
|
-
await import_fs7.promises.mkdir(this.indexPath, { recursive: true });
|
|
6872
7645
|
const dimensions = this.configuredProviderInfo.modelInfo.dimensions;
|
|
6873
|
-
const storePath =
|
|
6874
|
-
|
|
6875
|
-
const
|
|
6876
|
-
|
|
6877
|
-
|
|
6878
|
-
|
|
6879
|
-
|
|
6880
|
-
|
|
6881
|
-
|
|
6882
|
-
|
|
6883
|
-
|
|
6884
|
-
|
|
6885
|
-
|
|
7646
|
+
const storePath = path13.join(this.indexPath, "vectors");
|
|
7647
|
+
const vectorMetadataPath = `${storePath}.meta.json`;
|
|
7648
|
+
const invertedIndexPath = path13.join(this.indexPath, "inverted-index.json");
|
|
7649
|
+
const dbPath = path13.join(this.indexPath, "codebase.db");
|
|
7650
|
+
let dbIsNew = !(0, import_fs8.existsSync)(dbPath);
|
|
7651
|
+
const readerArtifactFingerprint = mode === "reader" ? this.captureReaderArtifactFingerprint() : null;
|
|
7652
|
+
if (mode === "writer") {
|
|
7653
|
+
await import_fs8.promises.mkdir(this.indexPath, { recursive: true });
|
|
7654
|
+
if (recoveredOwners.length > 0 && this.config.scope === "project" && !this.isLocalProjectIndexPath()) {
|
|
7655
|
+
throw new Error(
|
|
7656
|
+
"Interrupted indexing recovery is unsafe while using an inherited worktree index. Run index_codebase with force=true to create a local project index boundary."
|
|
7657
|
+
);
|
|
7658
|
+
}
|
|
7659
|
+
for (const recoveredOwner of recoveredOwners) {
|
|
7660
|
+
recoverLeaseArtifacts(this.indexPath, recoveredOwner, [
|
|
7661
|
+
storePath,
|
|
7662
|
+
`${storePath}.meta.json`
|
|
7663
|
+
]);
|
|
7664
|
+
}
|
|
7665
|
+
if (recoveredOwners.length > 0 && this.config.scope === "project") {
|
|
7666
|
+
await this.resetLocalIndexArtifacts();
|
|
7667
|
+
}
|
|
7668
|
+
this.store = new VectorStore(storePath, dimensions);
|
|
7669
|
+
if ((0, import_fs8.existsSync)(storePath) || (0, import_fs8.existsSync)(vectorMetadataPath)) {
|
|
7670
|
+
this.store.load();
|
|
6886
7671
|
}
|
|
6887
7672
|
this.invertedIndex = new InvertedIndex(invertedIndexPath);
|
|
6888
|
-
|
|
6889
|
-
|
|
6890
|
-
|
|
6891
|
-
|
|
6892
|
-
|
|
6893
|
-
|
|
6894
|
-
|
|
6895
|
-
throw error;
|
|
7673
|
+
try {
|
|
7674
|
+
this.invertedIndex.load();
|
|
7675
|
+
} catch {
|
|
7676
|
+
if ((0, import_fs8.existsSync)(invertedIndexPath)) {
|
|
7677
|
+
await import_fs8.promises.unlink(invertedIndexPath);
|
|
7678
|
+
}
|
|
7679
|
+
this.invertedIndex = new InvertedIndex(invertedIndexPath);
|
|
6896
7680
|
}
|
|
7681
|
+
try {
|
|
7682
|
+
this.database = new Database(dbPath);
|
|
7683
|
+
} catch (error) {
|
|
7684
|
+
if (!await this.tryResetCorruptedIndex("initializing index database", error)) {
|
|
7685
|
+
throw error;
|
|
7686
|
+
}
|
|
7687
|
+
this.store = new VectorStore(storePath, dimensions);
|
|
7688
|
+
this.invertedIndex = new InvertedIndex(invertedIndexPath);
|
|
7689
|
+
this.database = new Database(dbPath);
|
|
7690
|
+
dbIsNew = true;
|
|
7691
|
+
}
|
|
7692
|
+
} else {
|
|
6897
7693
|
this.store = new VectorStore(storePath, dimensions);
|
|
7694
|
+
const vectorStoreExists = (0, import_fs8.existsSync)(storePath);
|
|
7695
|
+
const vectorMetadataExists = (0, import_fs8.existsSync)(vectorMetadataPath);
|
|
7696
|
+
const vectorReadFailureMessage = this.getVectorReadIssueMessage();
|
|
7697
|
+
if (vectorStoreExists !== vectorMetadataExists) {
|
|
7698
|
+
this.recordReadIssue("vectors", vectorReadFailureMessage);
|
|
7699
|
+
} else if (vectorStoreExists) {
|
|
7700
|
+
try {
|
|
7701
|
+
this.store.loadStrict();
|
|
7702
|
+
} catch (error) {
|
|
7703
|
+
this.recordReadIssue("vectors", vectorReadFailureMessage, error);
|
|
7704
|
+
this.store = new VectorStore(storePath, dimensions);
|
|
7705
|
+
}
|
|
7706
|
+
}
|
|
6898
7707
|
this.invertedIndex = new InvertedIndex(invertedIndexPath);
|
|
6899
|
-
|
|
6900
|
-
|
|
7708
|
+
if ((0, import_fs8.existsSync)(invertedIndexPath)) {
|
|
7709
|
+
try {
|
|
7710
|
+
this.invertedIndex.load();
|
|
7711
|
+
} catch (error) {
|
|
7712
|
+
this.recordReadIssue(
|
|
7713
|
+
"keyword",
|
|
7714
|
+
this.getKeywordReadIssueMessage(),
|
|
7715
|
+
error
|
|
7716
|
+
);
|
|
7717
|
+
this.invertedIndex = new InvertedIndex(invertedIndexPath);
|
|
7718
|
+
}
|
|
7719
|
+
} else if (this.store.count() > 0) {
|
|
7720
|
+
this.recordReadIssue("keyword", this.getKeywordReadIssueMessage());
|
|
7721
|
+
}
|
|
7722
|
+
if ((0, import_fs8.existsSync)(dbPath)) {
|
|
7723
|
+
try {
|
|
7724
|
+
this.database = Database.openReadOnly(dbPath);
|
|
7725
|
+
} catch (error) {
|
|
7726
|
+
this.recordReadIssue(
|
|
7727
|
+
"database",
|
|
7728
|
+
this.getDatabaseReadIssueMessage(),
|
|
7729
|
+
error
|
|
7730
|
+
);
|
|
7731
|
+
this.database = Database.createEmptyReadOnly();
|
|
7732
|
+
}
|
|
7733
|
+
} else {
|
|
7734
|
+
this.database = Database.createEmptyReadOnly();
|
|
7735
|
+
if (this.store.count() > 0) {
|
|
7736
|
+
this.recordReadIssue(
|
|
7737
|
+
"database",
|
|
7738
|
+
`Index database is missing for the published vectors. ${this.getDatabaseReadIssueMessage()}`
|
|
7739
|
+
);
|
|
7740
|
+
}
|
|
7741
|
+
}
|
|
6901
7742
|
}
|
|
6902
7743
|
if (isGitRepo(this.projectRoot)) {
|
|
6903
7744
|
this.currentBranch = getBranchOrDefault(this.projectRoot);
|
|
@@ -6911,10 +7752,10 @@ var Indexer = class {
|
|
|
6911
7752
|
this.baseBranch = "default";
|
|
6912
7753
|
this.logger.branch("debug", "Not a git repository, using default branch");
|
|
6913
7754
|
}
|
|
6914
|
-
if (
|
|
6915
|
-
await this.
|
|
7755
|
+
if (mode === "writer" && recoveredOwners.length > 0) {
|
|
7756
|
+
await this.recoverFromInterruptedIndexingUnlocked(recoveredOwners);
|
|
6916
7757
|
}
|
|
6917
|
-
if (dbIsNew && this.store.count() > 0) {
|
|
7758
|
+
if (mode === "writer" && dbIsNew && this.store.count() > 0) {
|
|
6918
7759
|
this.migrateFromLegacyIndex();
|
|
6919
7760
|
}
|
|
6920
7761
|
this.loadFileHashCache();
|
|
@@ -6926,9 +7767,11 @@ var Indexer = class {
|
|
|
6926
7767
|
configuredProviderInfo: this.configuredProviderInfo
|
|
6927
7768
|
});
|
|
6928
7769
|
}
|
|
6929
|
-
if (this.config.indexing.autoGc) {
|
|
7770
|
+
if (mode === "writer" && this.config.indexing.autoGc && !options.skipAutoGc) {
|
|
6930
7771
|
await this.maybeRunAutoGc();
|
|
6931
7772
|
}
|
|
7773
|
+
this.initializationMode = mode;
|
|
7774
|
+
this.readerArtifactFingerprint = readerArtifactFingerprint;
|
|
6932
7775
|
}
|
|
6933
7776
|
async maybeRunAutoGc() {
|
|
6934
7777
|
if (!this.database) return;
|
|
@@ -6945,7 +7788,7 @@ var Indexer = class {
|
|
|
6945
7788
|
}
|
|
6946
7789
|
}
|
|
6947
7790
|
if (shouldRunGc) {
|
|
6948
|
-
const result = await this.
|
|
7791
|
+
const result = await this.healthCheckUnlocked();
|
|
6949
7792
|
if (result.warning) {
|
|
6950
7793
|
this.database.setMetadata(STARTUP_WARNING_METADATA_KEY, result.warning);
|
|
6951
7794
|
} else {
|
|
@@ -6967,7 +7810,7 @@ var Indexer = class {
|
|
|
6967
7810
|
if (await this.tryResetCorruptedIndex("running automatic orphan garbage collection", error)) {
|
|
6968
7811
|
return {
|
|
6969
7812
|
resetCorruptedIndex: true,
|
|
6970
|
-
warning: this.getCorruptedIndexWarning(
|
|
7813
|
+
warning: this.getCorruptedIndexWarning(path13.join(this.indexPath, "codebase.db"))
|
|
6971
7814
|
};
|
|
6972
7815
|
}
|
|
6973
7816
|
throw error;
|
|
@@ -6982,28 +7825,29 @@ var Indexer = class {
|
|
|
6982
7825
|
return;
|
|
6983
7826
|
}
|
|
6984
7827
|
const retainedEntries = store.getAllMetadata().filter(({ key }) => !excludedSet.has(key));
|
|
6985
|
-
const storeBasePath =
|
|
6986
|
-
const storeIndexPath =
|
|
7828
|
+
const storeBasePath = path13.join(this.indexPath, "vectors");
|
|
7829
|
+
const storeIndexPath = storeBasePath;
|
|
6987
7830
|
const storeMetadataPath = `${storeBasePath}.meta.json`;
|
|
6988
|
-
const
|
|
6989
|
-
const
|
|
7831
|
+
const lease = this.requireActiveLease();
|
|
7832
|
+
const backupIndexPath = createLeaseTemporaryPath(storeIndexPath, lease.owner, "bak");
|
|
7833
|
+
const backupMetadataPath = createLeaseTemporaryPath(storeMetadataPath, lease.owner, "bak");
|
|
6990
7834
|
let backedUpIndex = false;
|
|
6991
7835
|
let backedUpMetadata = false;
|
|
6992
7836
|
let rebuiltCount = 0;
|
|
6993
7837
|
let skippedCount = 0;
|
|
6994
|
-
if ((0,
|
|
6995
|
-
(0,
|
|
7838
|
+
if ((0, import_fs8.existsSync)(backupIndexPath)) {
|
|
7839
|
+
(0, import_fs8.unlinkSync)(backupIndexPath);
|
|
6996
7840
|
}
|
|
6997
|
-
if ((0,
|
|
6998
|
-
(0,
|
|
7841
|
+
if ((0, import_fs8.existsSync)(backupMetadataPath)) {
|
|
7842
|
+
(0, import_fs8.unlinkSync)(backupMetadataPath);
|
|
6999
7843
|
}
|
|
7000
7844
|
try {
|
|
7001
|
-
if ((0,
|
|
7002
|
-
(0,
|
|
7845
|
+
if ((0, import_fs8.existsSync)(storeIndexPath)) {
|
|
7846
|
+
(0, import_fs8.renameSync)(storeIndexPath, backupIndexPath);
|
|
7003
7847
|
backedUpIndex = true;
|
|
7004
7848
|
}
|
|
7005
|
-
if ((0,
|
|
7006
|
-
(0,
|
|
7849
|
+
if ((0, import_fs8.existsSync)(storeMetadataPath)) {
|
|
7850
|
+
(0, import_fs8.renameSync)(storeMetadataPath, backupMetadataPath);
|
|
7007
7851
|
backedUpMetadata = true;
|
|
7008
7852
|
}
|
|
7009
7853
|
store.clear();
|
|
@@ -7023,11 +7867,11 @@ var Indexer = class {
|
|
|
7023
7867
|
rebuiltCount += 1;
|
|
7024
7868
|
}
|
|
7025
7869
|
store.save();
|
|
7026
|
-
if (backedUpIndex && (0,
|
|
7027
|
-
(0,
|
|
7870
|
+
if (backedUpIndex && (0, import_fs8.existsSync)(backupIndexPath)) {
|
|
7871
|
+
(0, import_fs8.unlinkSync)(backupIndexPath);
|
|
7028
7872
|
}
|
|
7029
|
-
if (backedUpMetadata && (0,
|
|
7030
|
-
(0,
|
|
7873
|
+
if (backedUpMetadata && (0, import_fs8.existsSync)(backupMetadataPath)) {
|
|
7874
|
+
(0, import_fs8.unlinkSync)(backupMetadataPath);
|
|
7031
7875
|
}
|
|
7032
7876
|
this.logger.gc("info", "Rebuilt vector store to avoid native remove", {
|
|
7033
7877
|
excludedChunks: excludedSet.size,
|
|
@@ -7039,17 +7883,17 @@ var Indexer = class {
|
|
|
7039
7883
|
store.clear();
|
|
7040
7884
|
} catch {
|
|
7041
7885
|
}
|
|
7042
|
-
if ((0,
|
|
7043
|
-
(0,
|
|
7886
|
+
if ((0, import_fs8.existsSync)(storeIndexPath)) {
|
|
7887
|
+
(0, import_fs8.unlinkSync)(storeIndexPath);
|
|
7044
7888
|
}
|
|
7045
|
-
if ((0,
|
|
7046
|
-
(0,
|
|
7889
|
+
if ((0, import_fs8.existsSync)(storeMetadataPath)) {
|
|
7890
|
+
(0, import_fs8.unlinkSync)(storeMetadataPath);
|
|
7047
7891
|
}
|
|
7048
|
-
if (backedUpIndex && (0,
|
|
7049
|
-
(0,
|
|
7892
|
+
if (backedUpIndex && (0, import_fs8.existsSync)(backupIndexPath)) {
|
|
7893
|
+
(0, import_fs8.renameSync)(backupIndexPath, storeIndexPath);
|
|
7050
7894
|
}
|
|
7051
|
-
if (backedUpMetadata && (0,
|
|
7052
|
-
(0,
|
|
7895
|
+
if (backedUpMetadata && (0, import_fs8.existsSync)(backupMetadataPath)) {
|
|
7896
|
+
(0, import_fs8.renameSync)(backupMetadataPath, storeMetadataPath);
|
|
7053
7897
|
}
|
|
7054
7898
|
if (backedUpIndex || backedUpMetadata) {
|
|
7055
7899
|
store.load();
|
|
@@ -7063,11 +7907,37 @@ var Indexer = class {
|
|
|
7063
7907
|
}
|
|
7064
7908
|
return `Detected a corrupted local SQLite index at ${dbPath} and reset the local index. Run index_codebase to rebuild search data.`;
|
|
7065
7909
|
}
|
|
7910
|
+
async resetLocalIndexArtifacts() {
|
|
7911
|
+
this.store = null;
|
|
7912
|
+
this.invertedIndex = null;
|
|
7913
|
+
this.database?.close();
|
|
7914
|
+
this.database = null;
|
|
7915
|
+
this.indexCompatibility = null;
|
|
7916
|
+
this.initializationMode = "none";
|
|
7917
|
+
this.readIssues = [];
|
|
7918
|
+
this.readerArtifactFingerprint = null;
|
|
7919
|
+
this.writerArtifactFingerprint = null;
|
|
7920
|
+
this.readerArtifactRetryAfter.clear();
|
|
7921
|
+
this.fileHashCache.clear();
|
|
7922
|
+
const resetPaths = [
|
|
7923
|
+
path13.join(this.indexPath, "codebase.db"),
|
|
7924
|
+
path13.join(this.indexPath, "codebase.db-shm"),
|
|
7925
|
+
path13.join(this.indexPath, "codebase.db-wal"),
|
|
7926
|
+
path13.join(this.indexPath, "vectors"),
|
|
7927
|
+
path13.join(this.indexPath, "vectors.usearch"),
|
|
7928
|
+
path13.join(this.indexPath, "vectors.meta.json"),
|
|
7929
|
+
path13.join(this.indexPath, "inverted-index.json"),
|
|
7930
|
+
path13.join(this.indexPath, "file-hashes.json"),
|
|
7931
|
+
path13.join(this.indexPath, "failed-batches.json")
|
|
7932
|
+
];
|
|
7933
|
+
await Promise.all(resetPaths.map((targetPath) => import_fs8.promises.rm(targetPath, { recursive: true, force: true })));
|
|
7934
|
+
await import_fs8.promises.mkdir(this.indexPath, { recursive: true });
|
|
7935
|
+
}
|
|
7066
7936
|
async tryResetCorruptedIndex(stage, error) {
|
|
7067
7937
|
if (!isSqliteCorruptionError(error)) {
|
|
7068
7938
|
return false;
|
|
7069
7939
|
}
|
|
7070
|
-
const dbPath =
|
|
7940
|
+
const dbPath = path13.join(this.indexPath, "codebase.db");
|
|
7071
7941
|
const warning = this.getCorruptedIndexWarning(dbPath);
|
|
7072
7942
|
const errorMessage = getErrorMessage2(error);
|
|
7073
7943
|
if (this.config.scope === "global") {
|
|
@@ -7083,30 +7953,7 @@ var Indexer = class {
|
|
|
7083
7953
|
dbPath,
|
|
7084
7954
|
error: errorMessage
|
|
7085
7955
|
});
|
|
7086
|
-
this.
|
|
7087
|
-
this.invertedIndex = null;
|
|
7088
|
-
this.database?.close();
|
|
7089
|
-
this.database = null;
|
|
7090
|
-
this.indexCompatibility = null;
|
|
7091
|
-
this.fileHashCache.clear();
|
|
7092
|
-
const resetPaths = [
|
|
7093
|
-
path12.join(this.indexPath, "codebase.db"),
|
|
7094
|
-
path12.join(this.indexPath, "codebase.db-shm"),
|
|
7095
|
-
path12.join(this.indexPath, "codebase.db-wal"),
|
|
7096
|
-
path12.join(this.indexPath, "vectors.usearch"),
|
|
7097
|
-
path12.join(this.indexPath, "inverted-index.json"),
|
|
7098
|
-
path12.join(this.indexPath, "file-hashes.json"),
|
|
7099
|
-
path12.join(this.indexPath, "failed-batches.json"),
|
|
7100
|
-
path12.join(this.indexPath, "indexing.lock"),
|
|
7101
|
-
path12.join(this.indexPath, "vectors")
|
|
7102
|
-
];
|
|
7103
|
-
await Promise.all(resetPaths.map(async (targetPath) => {
|
|
7104
|
-
try {
|
|
7105
|
-
await import_fs7.promises.rm(targetPath, { recursive: true, force: true });
|
|
7106
|
-
} catch {
|
|
7107
|
-
}
|
|
7108
|
-
}));
|
|
7109
|
-
await import_fs7.promises.mkdir(this.indexPath, { recursive: true });
|
|
7956
|
+
await this.resetLocalIndexArtifacts();
|
|
7110
7957
|
return true;
|
|
7111
7958
|
}
|
|
7112
7959
|
migrateFromLegacyIndex() {
|
|
@@ -7225,8 +8072,62 @@ var Indexer = class {
|
|
|
7225
8072
|
return this.indexCompatibility;
|
|
7226
8073
|
}
|
|
7227
8074
|
async ensureInitialized() {
|
|
8075
|
+
let initializedReader = false;
|
|
8076
|
+
while (true) {
|
|
8077
|
+
if (this.initializationPromise) {
|
|
8078
|
+
await this.initializationPromise;
|
|
8079
|
+
}
|
|
8080
|
+
if (!this.isInitializedFor("reader")) {
|
|
8081
|
+
await this.initialize();
|
|
8082
|
+
initializedReader = true;
|
|
8083
|
+
continue;
|
|
8084
|
+
}
|
|
8085
|
+
if (this.initializationMode === "writer" && !this.activeIndexLease && !initializedReader) {
|
|
8086
|
+
if (!this.refreshInactiveWriterArtifacts()) {
|
|
8087
|
+
this.resetLoadedIndexState(true);
|
|
8088
|
+
await this.initialize();
|
|
8089
|
+
initializedReader = true;
|
|
8090
|
+
continue;
|
|
8091
|
+
}
|
|
8092
|
+
}
|
|
8093
|
+
if (this.initializationMode === "reader" && !initializedReader) {
|
|
8094
|
+
this.refreshReaderArtifacts();
|
|
8095
|
+
}
|
|
8096
|
+
const state = this.requireLoadedIndexState();
|
|
8097
|
+
return {
|
|
8098
|
+
...state,
|
|
8099
|
+
readIssues: [...this.readIssues],
|
|
8100
|
+
compatibility: this.indexCompatibility ?? this.validateIndexCompatibility(state.configuredProviderInfo)
|
|
8101
|
+
};
|
|
8102
|
+
}
|
|
8103
|
+
}
|
|
8104
|
+
async ensureInitializedUnlocked(recoveredOwners = []) {
|
|
8105
|
+
this.requireActiveLease();
|
|
8106
|
+
if (this.initializationPromise) {
|
|
8107
|
+
await this.initializationPromise;
|
|
8108
|
+
}
|
|
8109
|
+
if (recoveredOwners.length > 0 || !this.isInitializedFor("writer")) {
|
|
8110
|
+
const retireReaderDatabase = this.initializationMode === "reader";
|
|
8111
|
+
this.resetLoadedIndexState(retireReaderDatabase);
|
|
8112
|
+
await this.initializeOnce("writer", recoveredOwners, { skipAutoGc: true });
|
|
8113
|
+
} else {
|
|
8114
|
+
this.refreshLoadedIndexState();
|
|
8115
|
+
}
|
|
8116
|
+
if (this.config.indexing.autoGc) {
|
|
8117
|
+
await this.maybeRunAutoGc();
|
|
8118
|
+
}
|
|
8119
|
+
return this.requireLoadedIndexState();
|
|
8120
|
+
}
|
|
8121
|
+
requireReadableComponents(readIssues, ...components) {
|
|
8122
|
+
const componentSet = new Set(components);
|
|
8123
|
+
const issues = readIssues.filter((issue) => issue.blocking && componentSet.has(issue.component));
|
|
8124
|
+
if (issues.length > 0) {
|
|
8125
|
+
throw new Error(issues.map((issue) => issue.message).join(" "));
|
|
8126
|
+
}
|
|
8127
|
+
}
|
|
8128
|
+
requireLoadedIndexState() {
|
|
7228
8129
|
if (!this.store || !this.provider || !this.invertedIndex || !this.configuredProviderInfo || !this.database) {
|
|
7229
|
-
|
|
8130
|
+
throw new Error("Index state is not initialized");
|
|
7230
8131
|
}
|
|
7231
8132
|
return {
|
|
7232
8133
|
store: this.store,
|
|
@@ -7250,7 +8151,12 @@ var Indexer = class {
|
|
|
7250
8151
|
return createCostEstimate(files, configuredProviderInfo);
|
|
7251
8152
|
}
|
|
7252
8153
|
async index(onProgress) {
|
|
7253
|
-
|
|
8154
|
+
return this.withIndexMutationLease("index", async (recoveredOwners) => {
|
|
8155
|
+
return this.indexUnlocked(onProgress, recoveredOwners);
|
|
8156
|
+
});
|
|
8157
|
+
}
|
|
8158
|
+
async indexUnlocked(onProgress, recoveredOwners = [], stateReady = false) {
|
|
8159
|
+
const { store, provider, invertedIndex, database, configuredProviderInfo } = stateReady ? this.requireLoadedIndexState() : await this.ensureInitializedUnlocked(recoveredOwners);
|
|
7254
8160
|
const scopedRoots = this.config.scope === "global" ? this.getScopedRoots() : null;
|
|
7255
8161
|
const branchCatalogKey = this.getBranchCatalogKey();
|
|
7256
8162
|
const forceScopedReembed = scopedRoots !== null && database.getMetadata(this.getProjectForceReembedMetadataKey()) === "true";
|
|
@@ -7260,7 +8166,6 @@ var Indexer = class {
|
|
|
7260
8166
|
`${this.indexCompatibility?.reason} Run index_codebase with force=true to rebuild the index.`
|
|
7261
8167
|
);
|
|
7262
8168
|
}
|
|
7263
|
-
this.acquireIndexingLock();
|
|
7264
8169
|
this.logger.recordIndexingStart();
|
|
7265
8170
|
this.logger.info("Starting indexing", { projectRoot: this.projectRoot });
|
|
7266
8171
|
const startTime = Date.now();
|
|
@@ -7311,7 +8216,7 @@ var Indexer = class {
|
|
|
7311
8216
|
unchangedFilePaths.add(f.path);
|
|
7312
8217
|
this.logger.recordCacheHit();
|
|
7313
8218
|
} else {
|
|
7314
|
-
const content = await
|
|
8219
|
+
const content = await import_fs8.promises.readFile(f.path, "utf-8");
|
|
7315
8220
|
changedFiles.push({ path: f.path, content, hash: currentHash });
|
|
7316
8221
|
this.logger.recordCacheMiss();
|
|
7317
8222
|
}
|
|
@@ -7409,7 +8314,7 @@ var Indexer = class {
|
|
|
7409
8314
|
for (const parsed of parsedFiles) {
|
|
7410
8315
|
currentFilePaths.add(parsed.path);
|
|
7411
8316
|
if (parsed.chunks.length === 0) {
|
|
7412
|
-
const relativePath =
|
|
8317
|
+
const relativePath = path13.relative(this.projectRoot, parsed.path);
|
|
7413
8318
|
stats.parseFailures.push(relativePath);
|
|
7414
8319
|
}
|
|
7415
8320
|
let fileChunkCount = 0;
|
|
@@ -7609,7 +8514,9 @@ var Indexer = class {
|
|
|
7609
8514
|
database.addChunksToBranchBatch(branchCatalogKey, Array.from(currentChunkIds));
|
|
7610
8515
|
database.clearBranchSymbols(branchCatalogKey);
|
|
7611
8516
|
database.addSymbolsToBranchBatch(branchCatalogKey, Array.from(allSymbolIds));
|
|
7612
|
-
|
|
8517
|
+
const vectorPath = path13.join(this.indexPath, "vectors");
|
|
8518
|
+
const shouldFingerprintLegacyPair = !store.hasFingerprint() && (0, import_fs8.existsSync)(vectorPath) && (0, import_fs8.existsSync)(`${vectorPath}.meta.json`);
|
|
8519
|
+
if (backfilledBlameMetadata || shouldFingerprintLegacyPair) {
|
|
7613
8520
|
store.save();
|
|
7614
8521
|
}
|
|
7615
8522
|
if (scopedRoots) {
|
|
@@ -7630,7 +8537,6 @@ var Indexer = class {
|
|
|
7630
8537
|
chunksProcessed: 0,
|
|
7631
8538
|
totalChunks: 0
|
|
7632
8539
|
});
|
|
7633
|
-
this.releaseIndexingLock();
|
|
7634
8540
|
return stats;
|
|
7635
8541
|
}
|
|
7636
8542
|
if (pendingChunks.length === 0) {
|
|
@@ -7639,7 +8545,7 @@ var Indexer = class {
|
|
|
7639
8545
|
database.clearBranchSymbols(branchCatalogKey);
|
|
7640
8546
|
database.addSymbolsToBranchBatch(branchCatalogKey, Array.from(allSymbolIds));
|
|
7641
8547
|
store.save();
|
|
7642
|
-
|
|
8548
|
+
this.saveInvertedIndex(invertedIndex);
|
|
7643
8549
|
if (scopedRoots) {
|
|
7644
8550
|
this.replaceScopedFileHashCache(currentFileHashes, scopedRoots);
|
|
7645
8551
|
this.clearScopedFailedBatches(scopedRoots);
|
|
@@ -7658,7 +8564,6 @@ var Indexer = class {
|
|
|
7658
8564
|
chunksProcessed: 0,
|
|
7659
8565
|
totalChunks: 0
|
|
7660
8566
|
});
|
|
7661
|
-
this.releaseIndexingLock();
|
|
7662
8567
|
return stats;
|
|
7663
8568
|
}
|
|
7664
8569
|
onProgress?.({
|
|
@@ -7889,7 +8794,7 @@ var Indexer = class {
|
|
|
7889
8794
|
database.clearBranchSymbols(branchCatalogKey);
|
|
7890
8795
|
database.addSymbolsToBranchBatch(branchCatalogKey, Array.from(allSymbolIds));
|
|
7891
8796
|
store.save();
|
|
7892
|
-
|
|
8797
|
+
this.saveInvertedIndex(invertedIndex);
|
|
7893
8798
|
if (scopedRoots) {
|
|
7894
8799
|
this.replaceScopedFileHashCache(currentFileHashes, scopedRoots);
|
|
7895
8800
|
} else {
|
|
@@ -7941,7 +8846,6 @@ var Indexer = class {
|
|
|
7941
8846
|
chunksProcessed: stats.indexedChunks,
|
|
7942
8847
|
totalChunks: pendingChunks.length
|
|
7943
8848
|
});
|
|
7944
|
-
this.releaseIndexingLock();
|
|
7945
8849
|
return stats;
|
|
7946
8850
|
}
|
|
7947
8851
|
async getQueryEmbedding(query, provider) {
|
|
@@ -8007,8 +8911,8 @@ var Indexer = class {
|
|
|
8007
8911
|
return intersection / union;
|
|
8008
8912
|
}
|
|
8009
8913
|
async search(query, limit, options) {
|
|
8010
|
-
const { store, provider, database } = await this.ensureInitialized();
|
|
8011
|
-
|
|
8914
|
+
const { store, provider, invertedIndex, database, readIssues, compatibility } = await this.ensureInitialized();
|
|
8915
|
+
this.requireReadableComponents(readIssues, "vectors", "database");
|
|
8012
8916
|
if (!compatibility.compatible) {
|
|
8013
8917
|
throw new Error(
|
|
8014
8918
|
`${compatibility.reason ?? "Index is incompatible with current embedding provider."} A possible solution is to run index_codebase with force=true to rebuild the index.`
|
|
@@ -8022,6 +8926,7 @@ var Indexer = class {
|
|
|
8022
8926
|
const maxResults = limit ?? this.config.search.maxResults;
|
|
8023
8927
|
const hybridWeight = options?.hybridWeight ?? this.config.search.hybridWeight;
|
|
8024
8928
|
const fusionStrategy = this.config.search.fusionStrategy;
|
|
8929
|
+
const effectiveHybridWeight = fusionStrategy === "weighted" && readIssues.some((issue) => issue.component === "keyword") ? 0 : hybridWeight;
|
|
8025
8930
|
const rrfK = this.config.search.rrfK;
|
|
8026
8931
|
const rerankTopN = this.config.search.rerankTopN;
|
|
8027
8932
|
const filterByBranch = options?.filterByBranch ?? true;
|
|
@@ -8030,7 +8935,7 @@ var Indexer = class {
|
|
|
8030
8935
|
this.logger.search("debug", "Starting search", {
|
|
8031
8936
|
query,
|
|
8032
8937
|
maxResults,
|
|
8033
|
-
hybridWeight,
|
|
8938
|
+
hybridWeight: effectiveHybridWeight,
|
|
8034
8939
|
fusionStrategy,
|
|
8035
8940
|
rrfK,
|
|
8036
8941
|
rerankTopN,
|
|
@@ -8044,7 +8949,7 @@ var Indexer = class {
|
|
|
8044
8949
|
const semanticResults = store.search(embedding, maxResults * 4);
|
|
8045
8950
|
const vectorMs = import_perf_hooks.performance.now() - vectorStartTime;
|
|
8046
8951
|
const keywordStartTime = import_perf_hooks.performance.now();
|
|
8047
|
-
const keywordResults = await this.keywordSearch(query, maxResults * 4);
|
|
8952
|
+
const keywordResults = await this.keywordSearch(query, maxResults * 4, store, invertedIndex);
|
|
8048
8953
|
const keywordMs = import_perf_hooks.performance.now() - keywordStartTime;
|
|
8049
8954
|
let branchChunkIds = null;
|
|
8050
8955
|
if (filterByBranch && (this.config.scope === "global" || this.currentBranch !== "default")) {
|
|
@@ -8081,7 +8986,7 @@ var Indexer = class {
|
|
|
8081
8986
|
rrfK,
|
|
8082
8987
|
rerankTopN,
|
|
8083
8988
|
limit: maxResults,
|
|
8084
|
-
hybridWeight,
|
|
8989
|
+
hybridWeight: effectiveHybridWeight,
|
|
8085
8990
|
prioritizeSourcePaths: sourceIntent
|
|
8086
8991
|
});
|
|
8087
8992
|
const rerankedCombined = await this.rerankCandidatesWithApi(query, combined, {
|
|
@@ -8155,7 +9060,7 @@ var Indexer = class {
|
|
|
8155
9060
|
let contextEndLine = r.metadata.endLine;
|
|
8156
9061
|
if (!metadataOnly && this.config.search.includeContext) {
|
|
8157
9062
|
try {
|
|
8158
|
-
const fileContent = await
|
|
9063
|
+
const fileContent = await import_fs8.promises.readFile(
|
|
8159
9064
|
r.metadata.filePath,
|
|
8160
9065
|
"utf-8"
|
|
8161
9066
|
);
|
|
@@ -8181,8 +9086,7 @@ var Indexer = class {
|
|
|
8181
9086
|
})
|
|
8182
9087
|
);
|
|
8183
9088
|
}
|
|
8184
|
-
async keywordSearch(query, limit) {
|
|
8185
|
-
const { store, invertedIndex } = await this.ensureInitialized();
|
|
9089
|
+
async keywordSearch(query, limit, store, invertedIndex) {
|
|
8186
9090
|
const scores = invertedIndex.search(query);
|
|
8187
9091
|
if (scores.size === 0) {
|
|
8188
9092
|
return [];
|
|
@@ -8200,24 +9104,54 @@ var Indexer = class {
|
|
|
8200
9104
|
return results.slice(0, limit);
|
|
8201
9105
|
}
|
|
8202
9106
|
async getStatus() {
|
|
8203
|
-
const { store, configuredProviderInfo, database } = await this.ensureInitialized();
|
|
9107
|
+
const { store, configuredProviderInfo, database, readIssues, compatibility } = await this.ensureInitialized();
|
|
8204
9108
|
const failedBatchesCount = this.getFailedBatchesCount();
|
|
9109
|
+
const vectorCount = store.count();
|
|
9110
|
+
const statusReadIssues = [...readIssues];
|
|
9111
|
+
let startupWarning = "";
|
|
9112
|
+
if (!statusReadIssues.some((issue) => issue.component === "database")) {
|
|
9113
|
+
try {
|
|
9114
|
+
startupWarning = database.getMetadata(STARTUP_WARNING_METADATA_KEY) ?? "";
|
|
9115
|
+
} catch (error) {
|
|
9116
|
+
const message = this.getDatabaseReadIssueMessage();
|
|
9117
|
+
statusReadIssues.push(this.createReadIssue("database", message));
|
|
9118
|
+
if (!this.readIssues.some((issue) => issue.component === "database")) {
|
|
9119
|
+
this.recordReadIssue("database", message, error);
|
|
9120
|
+
}
|
|
9121
|
+
}
|
|
9122
|
+
}
|
|
9123
|
+
const readWarning = statusReadIssues.map((issue) => issue.message).join(" ");
|
|
9124
|
+
const warning = [readWarning, startupWarning].filter((message) => message.length > 0).join(" ");
|
|
9125
|
+
const hasBlockingReadIssue = statusReadIssues.some((issue) => issue.blocking);
|
|
8205
9126
|
return {
|
|
8206
|
-
indexed:
|
|
8207
|
-
vectorCount
|
|
9127
|
+
indexed: vectorCount > 0 && !hasBlockingReadIssue,
|
|
9128
|
+
vectorCount,
|
|
8208
9129
|
provider: configuredProviderInfo.provider,
|
|
8209
9130
|
model: configuredProviderInfo.modelInfo.model,
|
|
8210
9131
|
indexPath: this.indexPath,
|
|
8211
9132
|
currentBranch: this.currentBranch,
|
|
8212
9133
|
baseBranch: this.baseBranch,
|
|
8213
|
-
compatibility
|
|
9134
|
+
compatibility,
|
|
8214
9135
|
failedBatchesCount,
|
|
8215
9136
|
failedBatchesPath: failedBatchesCount > 0 ? this.failedBatchesPath : void 0,
|
|
8216
|
-
warning:
|
|
9137
|
+
warning: warning || void 0
|
|
8217
9138
|
};
|
|
8218
9139
|
}
|
|
9140
|
+
async forceIndex(onProgress) {
|
|
9141
|
+
return this.withIndexMutationLease("force-index", async (recoveredOwners) => {
|
|
9142
|
+
await this.ensureInitializedUnlocked(recoveredOwners);
|
|
9143
|
+
await this.clearIndexUnlocked();
|
|
9144
|
+
return this.indexUnlocked(onProgress, [], true);
|
|
9145
|
+
});
|
|
9146
|
+
}
|
|
8219
9147
|
async clearIndex() {
|
|
8220
|
-
|
|
9148
|
+
await this.withIndexMutationLease("clear", async (recoveredOwners) => {
|
|
9149
|
+
await this.ensureInitializedUnlocked(recoveredOwners);
|
|
9150
|
+
await this.clearIndexUnlocked();
|
|
9151
|
+
});
|
|
9152
|
+
}
|
|
9153
|
+
async clearIndexUnlocked() {
|
|
9154
|
+
const { store, invertedIndex, database } = this.requireLoadedIndexState();
|
|
8221
9155
|
if (this.config.scope === "global") {
|
|
8222
9156
|
store.load();
|
|
8223
9157
|
invertedIndex.load();
|
|
@@ -8244,7 +9178,7 @@ var Indexer = class {
|
|
|
8244
9178
|
store.clear();
|
|
8245
9179
|
store.save();
|
|
8246
9180
|
invertedIndex.clear();
|
|
8247
|
-
|
|
9181
|
+
this.saveInvertedIndex(invertedIndex);
|
|
8248
9182
|
this.fileHashCache.clear();
|
|
8249
9183
|
this.saveFileHashCache();
|
|
8250
9184
|
database.clearAllIndexedData();
|
|
@@ -8268,14 +9202,7 @@ var Indexer = class {
|
|
|
8268
9202
|
this.indexCompatibility = compatibility;
|
|
8269
9203
|
return;
|
|
8270
9204
|
}
|
|
8271
|
-
|
|
8272
|
-
if (this.host !== "opencode") {
|
|
8273
|
-
localProjectIndexPaths.push(path12.join(this.projectRoot, getHostProjectIndexRelativePath("opencode")));
|
|
8274
|
-
}
|
|
8275
|
-
const isLocalProjectIndex = localProjectIndexPaths.some(
|
|
8276
|
-
(localPath) => path12.resolve(this.indexPath) === path12.resolve(localPath)
|
|
8277
|
-
);
|
|
8278
|
-
if (!isLocalProjectIndex) {
|
|
9205
|
+
if (!this.isLocalProjectIndexPath()) {
|
|
8279
9206
|
throw new Error(
|
|
8280
9207
|
"Project-scoped force rebuild is unsafe while using an inherited worktree index. Create a local project config boundary before clearing the index."
|
|
8281
9208
|
);
|
|
@@ -8283,7 +9210,7 @@ var Indexer = class {
|
|
|
8283
9210
|
store.clear();
|
|
8284
9211
|
store.save();
|
|
8285
9212
|
invertedIndex.clear();
|
|
8286
|
-
|
|
9213
|
+
this.saveInvertedIndex(invertedIndex);
|
|
8287
9214
|
this.fileHashCache.clear();
|
|
8288
9215
|
this.saveFileHashCache();
|
|
8289
9216
|
database.clearAllIndexedData();
|
|
@@ -8301,7 +9228,13 @@ var Indexer = class {
|
|
|
8301
9228
|
this.indexCompatibility = this.validateIndexCompatibility(this.configuredProviderInfo);
|
|
8302
9229
|
}
|
|
8303
9230
|
async healthCheck() {
|
|
8304
|
-
|
|
9231
|
+
return this.withIndexMutationLease("health-check", async (recoveredOwners) => {
|
|
9232
|
+
await this.ensureInitializedUnlocked(recoveredOwners);
|
|
9233
|
+
return this.healthCheckUnlocked();
|
|
9234
|
+
});
|
|
9235
|
+
}
|
|
9236
|
+
async healthCheckUnlocked() {
|
|
9237
|
+
const { store, invertedIndex, database } = this.requireLoadedIndexState();
|
|
8305
9238
|
this.logger.gc("info", "Starting health check");
|
|
8306
9239
|
const allMetadata = store.getAllMetadata();
|
|
8307
9240
|
const filePathsToChunkKeys = /* @__PURE__ */ new Map();
|
|
@@ -8314,7 +9247,7 @@ var Indexer = class {
|
|
|
8314
9247
|
const removedChunkKeys = [];
|
|
8315
9248
|
const chunkKeysByRemovedFile = /* @__PURE__ */ new Map();
|
|
8316
9249
|
for (const [filePath, chunkKeys] of filePathsToChunkKeys) {
|
|
8317
|
-
if (!(0,
|
|
9250
|
+
if (!(0, import_fs8.existsSync)(filePath)) {
|
|
8318
9251
|
chunkKeysByRemovedFile.set(filePath, chunkKeys);
|
|
8319
9252
|
for (const key of chunkKeys) {
|
|
8320
9253
|
removedChunkKeys.push(key);
|
|
@@ -8339,7 +9272,7 @@ var Indexer = class {
|
|
|
8339
9272
|
const removedCount = removedChunkKeys.length;
|
|
8340
9273
|
if (removedCount > 0) {
|
|
8341
9274
|
store.save();
|
|
8342
|
-
|
|
9275
|
+
this.saveInvertedIndex(invertedIndex);
|
|
8343
9276
|
}
|
|
8344
9277
|
let gcOrphanEmbeddings;
|
|
8345
9278
|
let gcOrphanChunks;
|
|
@@ -8354,7 +9287,7 @@ var Indexer = class {
|
|
|
8354
9287
|
if (!await this.tryResetCorruptedIndex("running index health check", error)) {
|
|
8355
9288
|
throw error;
|
|
8356
9289
|
}
|
|
8357
|
-
await this.
|
|
9290
|
+
await this.initializeUnlocked("writer", [], { skipAutoGc: true });
|
|
8358
9291
|
return {
|
|
8359
9292
|
removed: 0,
|
|
8360
9293
|
filePaths: [],
|
|
@@ -8363,7 +9296,7 @@ var Indexer = class {
|
|
|
8363
9296
|
gcOrphanSymbols: 0,
|
|
8364
9297
|
gcOrphanCallEdges: 0,
|
|
8365
9298
|
resetCorruptedIndex: true,
|
|
8366
|
-
warning: this.getCorruptedIndexWarning(
|
|
9299
|
+
warning: this.getCorruptedIndexWarning(path13.join(this.indexPath, "codebase.db"))
|
|
8367
9300
|
};
|
|
8368
9301
|
}
|
|
8369
9302
|
this.logger.recordGc(removedCount, gcOrphanChunks, gcOrphanEmbeddings);
|
|
@@ -8376,7 +9309,13 @@ var Indexer = class {
|
|
|
8376
9309
|
return { removed: removedCount, filePaths: removedFilePaths, gcOrphanEmbeddings, gcOrphanChunks, gcOrphanSymbols, gcOrphanCallEdges };
|
|
8377
9310
|
}
|
|
8378
9311
|
async retryFailedBatches() {
|
|
8379
|
-
|
|
9312
|
+
return this.withIndexMutationLease("retry-failed-batches", async (recoveredOwners) => {
|
|
9313
|
+
await this.ensureInitializedUnlocked(recoveredOwners);
|
|
9314
|
+
return this.retryFailedBatchesUnlocked();
|
|
9315
|
+
});
|
|
9316
|
+
}
|
|
9317
|
+
async retryFailedBatchesUnlocked() {
|
|
9318
|
+
const { store, provider, invertedIndex, database, configuredProviderInfo } = this.requireLoadedIndexState();
|
|
8380
9319
|
const maxChunkTokens = getSafeEmbeddingChunkTokenLimit(configuredProviderInfo);
|
|
8381
9320
|
const providerRateLimits = this.getProviderRateLimits(configuredProviderInfo.provider);
|
|
8382
9321
|
const roots = this.config.scope === "global" ? this.getScopedRoots() : null;
|
|
@@ -8540,7 +9479,7 @@ var Indexer = class {
|
|
|
8540
9479
|
}
|
|
8541
9480
|
if (succeeded > 0) {
|
|
8542
9481
|
store.save();
|
|
8543
|
-
|
|
9482
|
+
this.saveInvertedIndex(invertedIndex);
|
|
8544
9483
|
}
|
|
8545
9484
|
if (roots && succeeded > 0 && persistedStillFailing.length === 0 && this.hasProjectForceReembedPending()) {
|
|
8546
9485
|
database.deleteMetadata(this.getProjectForceReembedMetadataKey());
|
|
@@ -8568,15 +9507,16 @@ var Indexer = class {
|
|
|
8568
9507
|
}
|
|
8569
9508
|
}
|
|
8570
9509
|
async getDatabaseStats() {
|
|
8571
|
-
const { database } = await this.ensureInitialized();
|
|
9510
|
+
const { database, readIssues } = await this.ensureInitialized();
|
|
9511
|
+
this.requireReadableComponents(readIssues, "database");
|
|
8572
9512
|
return database.getStats();
|
|
8573
9513
|
}
|
|
8574
9514
|
getLogger() {
|
|
8575
9515
|
return this.logger;
|
|
8576
9516
|
}
|
|
8577
9517
|
async findSimilar(code, limit = this.config.search.maxResults, options) {
|
|
8578
|
-
const { store, provider, database } = await this.ensureInitialized();
|
|
8579
|
-
|
|
9518
|
+
const { store, provider, database, readIssues, compatibility } = await this.ensureInitialized();
|
|
9519
|
+
this.requireReadableComponents(readIssues, "vectors", "database");
|
|
8580
9520
|
if (!compatibility.compatible) {
|
|
8581
9521
|
throw new Error(
|
|
8582
9522
|
`${compatibility.reason ?? "Index is incompatible with current embedding provider."} Run index_codebase with force=true to rebuild the index.`
|
|
@@ -8666,7 +9606,7 @@ var Indexer = class {
|
|
|
8666
9606
|
let content = "";
|
|
8667
9607
|
if (this.config.search.includeContext) {
|
|
8668
9608
|
try {
|
|
8669
|
-
const fileContent = await
|
|
9609
|
+
const fileContent = await import_fs8.promises.readFile(
|
|
8670
9610
|
r.metadata.filePath,
|
|
8671
9611
|
"utf-8"
|
|
8672
9612
|
);
|
|
@@ -8690,7 +9630,8 @@ var Indexer = class {
|
|
|
8690
9630
|
);
|
|
8691
9631
|
}
|
|
8692
9632
|
async getCallers(targetName, callTypeFilter) {
|
|
8693
|
-
const { database } = await this.ensureInitialized();
|
|
9633
|
+
const { database, readIssues } = await this.ensureInitialized();
|
|
9634
|
+
this.requireReadableComponents(readIssues, "database");
|
|
8694
9635
|
const seen = /* @__PURE__ */ new Set();
|
|
8695
9636
|
const results = [];
|
|
8696
9637
|
for (const branchKey of this.getBranchCatalogKeys()) {
|
|
@@ -8704,7 +9645,8 @@ var Indexer = class {
|
|
|
8704
9645
|
return results;
|
|
8705
9646
|
}
|
|
8706
9647
|
async getCallees(symbolId, callTypeFilter) {
|
|
8707
|
-
const { database } = await this.ensureInitialized();
|
|
9648
|
+
const { database, readIssues } = await this.ensureInitialized();
|
|
9649
|
+
this.requireReadableComponents(readIssues, "database");
|
|
8708
9650
|
const seen = /* @__PURE__ */ new Set();
|
|
8709
9651
|
const results = [];
|
|
8710
9652
|
for (const branchKey of this.getBranchCatalogKeys()) {
|
|
@@ -8718,43 +9660,50 @@ var Indexer = class {
|
|
|
8718
9660
|
return results;
|
|
8719
9661
|
}
|
|
8720
9662
|
async findCallPath(fromName, toName, maxDepth) {
|
|
8721
|
-
const { database } = await this.ensureInitialized();
|
|
9663
|
+
const { database, readIssues } = await this.ensureInitialized();
|
|
9664
|
+
this.requireReadableComponents(readIssues, "database");
|
|
8722
9665
|
let shortest = [];
|
|
8723
9666
|
for (const branchKey of this.getBranchCatalogKeys()) {
|
|
8724
|
-
const
|
|
8725
|
-
if (
|
|
8726
|
-
shortest =
|
|
9667
|
+
const path24 = database.findShortestPath(fromName, toName, branchKey, maxDepth);
|
|
9668
|
+
if (path24.length > 0 && (shortest.length === 0 || path24.length < shortest.length)) {
|
|
9669
|
+
shortest = path24;
|
|
8727
9670
|
}
|
|
8728
9671
|
}
|
|
8729
9672
|
return shortest;
|
|
8730
9673
|
}
|
|
8731
9674
|
async getSymbolsForBranch(branch) {
|
|
8732
|
-
const { database } = await this.ensureInitialized();
|
|
9675
|
+
const { database, readIssues } = await this.ensureInitialized();
|
|
9676
|
+
this.requireReadableComponents(readIssues, "database");
|
|
8733
9677
|
const resolvedBranch = branch ?? this.getBranchCatalogKey();
|
|
8734
9678
|
return database.getSymbolsForBranch(resolvedBranch);
|
|
8735
9679
|
}
|
|
8736
9680
|
async getSymbolsForFiles(filePaths, branch) {
|
|
8737
|
-
const { database } = await this.ensureInitialized();
|
|
9681
|
+
const { database, readIssues } = await this.ensureInitialized();
|
|
9682
|
+
this.requireReadableComponents(readIssues, "database");
|
|
8738
9683
|
const resolvedBranch = branch ?? this.getBranchCatalogKey();
|
|
8739
9684
|
return database.getSymbolsForFiles(filePaths, resolvedBranch);
|
|
8740
9685
|
}
|
|
8741
9686
|
async getTransitiveReachability(rootSymbolIds, direction, maxDepth) {
|
|
8742
|
-
const { database } = await this.ensureInitialized();
|
|
9687
|
+
const { database, readIssues } = await this.ensureInitialized();
|
|
9688
|
+
this.requireReadableComponents(readIssues, "database");
|
|
8743
9689
|
const branch = this.getBranchCatalogKey();
|
|
8744
9690
|
return database.getTransitiveReachability(rootSymbolIds, branch, direction, maxDepth);
|
|
8745
9691
|
}
|
|
8746
9692
|
async detectCommunities(branch, symbolIds) {
|
|
8747
|
-
const { database } = await this.ensureInitialized();
|
|
9693
|
+
const { database, readIssues } = await this.ensureInitialized();
|
|
9694
|
+
this.requireReadableComponents(readIssues, "database");
|
|
8748
9695
|
const resolvedBranch = branch ?? this.getBranchCatalogKey();
|
|
8749
9696
|
return database.detectCommunities(resolvedBranch, symbolIds);
|
|
8750
9697
|
}
|
|
8751
9698
|
async computeCentrality(branch) {
|
|
8752
|
-
const { database } = await this.ensureInitialized();
|
|
9699
|
+
const { database, readIssues } = await this.ensureInitialized();
|
|
9700
|
+
this.requireReadableComponents(readIssues, "database");
|
|
8753
9701
|
const resolvedBranch = branch ?? this.getBranchCatalogKey();
|
|
8754
9702
|
return database.computeCentrality(resolvedBranch);
|
|
8755
9703
|
}
|
|
8756
9704
|
async getPrImpact(opts) {
|
|
8757
|
-
const { database } = await this.ensureInitialized();
|
|
9705
|
+
const { database, readIssues } = await this.ensureInitialized();
|
|
9706
|
+
this.requireReadableComponents(readIssues, "database");
|
|
8758
9707
|
const execFileAsync3 = (0, import_util3.promisify)(import_child_process3.execFile);
|
|
8759
9708
|
const changedFilesResult = await getChangedFiles({
|
|
8760
9709
|
pr: opts.pr,
|
|
@@ -8777,7 +9726,7 @@ var Indexer = class {
|
|
|
8777
9726
|
"Run index_codebase first to build the call graph and symbol index for this branch."
|
|
8778
9727
|
);
|
|
8779
9728
|
}
|
|
8780
|
-
const absoluteChangedFiles = changedFiles.map((f) =>
|
|
9729
|
+
const absoluteChangedFiles = changedFiles.map((f) => path13.resolve(this.projectRoot, f));
|
|
8781
9730
|
const directSymbols = database.getSymbolsForFiles(absoluteChangedFiles, branchKey);
|
|
8782
9731
|
const directIds = directSymbols.map((s) => s.id);
|
|
8783
9732
|
const direction = opts.direction ?? "both";
|
|
@@ -8860,7 +9809,7 @@ var Indexer = class {
|
|
|
8860
9809
|
projectRoot: this.projectRoot,
|
|
8861
9810
|
baseBranch: this.baseBranch
|
|
8862
9811
|
});
|
|
8863
|
-
const otherAbsolute = otherChanged.files.map((f) =>
|
|
9812
|
+
const otherAbsolute = otherChanged.files.map((f) => path13.resolve(this.projectRoot, f));
|
|
8864
9813
|
const otherBranchKey = this.getBranchCatalogKeyFor(openPr.headRefName);
|
|
8865
9814
|
const otherSymbols = database.getSymbolsForFiles(otherAbsolute, otherBranchKey);
|
|
8866
9815
|
const otherLabels = /* @__PURE__ */ new Set();
|
|
@@ -8910,7 +9859,8 @@ var Indexer = class {
|
|
|
8910
9859
|
};
|
|
8911
9860
|
}
|
|
8912
9861
|
async getVisualizationData(options) {
|
|
8913
|
-
const { database, store } = await this.ensureInitialized();
|
|
9862
|
+
const { database, store, readIssues } = await this.ensureInitialized();
|
|
9863
|
+
this.requireReadableComponents(readIssues, "vectors", "database");
|
|
8914
9864
|
const seenSymbols = /* @__PURE__ */ new Map();
|
|
8915
9865
|
const seenEdges = /* @__PURE__ */ new Map();
|
|
8916
9866
|
for (const branchKey of this.getBranchCatalogKeys()) {
|
|
@@ -8923,12 +9873,12 @@ var Indexer = class {
|
|
|
8923
9873
|
if (meta.filePath) filePaths.add(meta.filePath);
|
|
8924
9874
|
}
|
|
8925
9875
|
const directory = options?.directory?.replace(/\/$/, "");
|
|
8926
|
-
const absoluteDirectoryFilter = directory ?
|
|
9876
|
+
const absoluteDirectoryFilter = directory ? path13.resolve(this.projectRoot, directory) : void 0;
|
|
8927
9877
|
for (const filePath of filePaths) {
|
|
8928
9878
|
if (directory) {
|
|
8929
|
-
const absoluteFilePath =
|
|
9879
|
+
const absoluteFilePath = path13.resolve(filePath);
|
|
8930
9880
|
const matchesRelative = filePath === directory || filePath.startsWith(directory + "/");
|
|
8931
|
-
const matchesProjectRelative = absoluteDirectoryFilter !== void 0 && (absoluteFilePath === absoluteDirectoryFilter || absoluteFilePath.startsWith(absoluteDirectoryFilter +
|
|
9881
|
+
const matchesProjectRelative = absoluteDirectoryFilter !== void 0 && (absoluteFilePath === absoluteDirectoryFilter || absoluteFilePath.startsWith(absoluteDirectoryFilter + path13.sep));
|
|
8932
9882
|
if (!matchesRelative && !matchesProjectRelative) {
|
|
8933
9883
|
continue;
|
|
8934
9884
|
}
|
|
@@ -8950,53 +9900,64 @@ var Indexer = class {
|
|
|
8950
9900
|
return { symbols: [...seenSymbols.values()], edges: [...seenEdges.values()] };
|
|
8951
9901
|
}
|
|
8952
9902
|
async close() {
|
|
8953
|
-
|
|
9903
|
+
this.database?.close();
|
|
9904
|
+
for (const database of this.retiredDatabases) {
|
|
9905
|
+
database.close();
|
|
9906
|
+
}
|
|
9907
|
+
this.retiredDatabases = [];
|
|
8954
9908
|
this.database = null;
|
|
8955
9909
|
this.store = null;
|
|
8956
9910
|
this.invertedIndex = null;
|
|
8957
9911
|
this.provider = null;
|
|
8958
9912
|
this.reranker = null;
|
|
9913
|
+
this.configuredProviderInfo = null;
|
|
9914
|
+
this.indexCompatibility = null;
|
|
9915
|
+
this.initializationMode = "none";
|
|
9916
|
+
this.readIssues = [];
|
|
9917
|
+
this.readerArtifactFingerprint = null;
|
|
9918
|
+
this.writerArtifactFingerprint = null;
|
|
9919
|
+
this.readerArtifactRetryAfter.clear();
|
|
8959
9920
|
}
|
|
8960
9921
|
};
|
|
8961
9922
|
|
|
8962
9923
|
// src/tools/knowledge-base-paths.ts
|
|
8963
|
-
var
|
|
9924
|
+
var path14 = __toESM(require("path"), 1);
|
|
8964
9925
|
function resolveConfigPathValue(value, baseDir) {
|
|
8965
9926
|
const trimmed = value.trim();
|
|
8966
9927
|
if (!trimmed) {
|
|
8967
9928
|
return trimmed;
|
|
8968
9929
|
}
|
|
8969
|
-
const absolutePath =
|
|
8970
|
-
return
|
|
9930
|
+
const absolutePath = path14.isAbsolute(trimmed) ? trimmed : path14.resolve(baseDir, trimmed);
|
|
9931
|
+
return path14.normalize(absolutePath);
|
|
8971
9932
|
}
|
|
8972
9933
|
function serializeConfigPathValue(value, baseDir) {
|
|
8973
9934
|
const trimmed = value.trim();
|
|
8974
9935
|
if (!trimmed) {
|
|
8975
9936
|
return trimmed;
|
|
8976
9937
|
}
|
|
8977
|
-
if (!
|
|
8978
|
-
return normalizePathSeparators(
|
|
9938
|
+
if (!path14.isAbsolute(trimmed)) {
|
|
9939
|
+
return normalizePathSeparators(path14.normalize(trimmed));
|
|
8979
9940
|
}
|
|
8980
|
-
const relativePath =
|
|
8981
|
-
if (!relativePath || !relativePath.startsWith("..") && !
|
|
8982
|
-
return normalizePathSeparators(
|
|
9941
|
+
const relativePath = path14.relative(baseDir, trimmed);
|
|
9942
|
+
if (!relativePath || !relativePath.startsWith("..") && !path14.isAbsolute(relativePath)) {
|
|
9943
|
+
return normalizePathSeparators(path14.normalize(relativePath || "."));
|
|
8983
9944
|
}
|
|
8984
|
-
return
|
|
9945
|
+
return path14.normalize(trimmed);
|
|
8985
9946
|
}
|
|
8986
9947
|
function resolveKnowledgeBasePath(value, projectRoot) {
|
|
8987
|
-
return
|
|
9948
|
+
return path14.isAbsolute(value) ? value : path14.resolve(projectRoot, value);
|
|
8988
9949
|
}
|
|
8989
9950
|
function normalizeKnowledgeBasePath2(value, projectRoot) {
|
|
8990
|
-
return
|
|
9951
|
+
return path14.normalize(resolveKnowledgeBasePath(value, projectRoot));
|
|
8991
9952
|
}
|
|
8992
9953
|
function hasMatchingKnowledgeBasePath(knowledgeBases, inputPath, projectRoot) {
|
|
8993
|
-
const normalizedInput =
|
|
9954
|
+
const normalizedInput = path14.normalize(inputPath);
|
|
8994
9955
|
return knowledgeBases.some((kb) => normalizeKnowledgeBasePath2(kb, projectRoot) === normalizedInput);
|
|
8995
9956
|
}
|
|
8996
9957
|
function findKnowledgeBasePathIndex(knowledgeBases, inputPath, projectRoot) {
|
|
8997
|
-
const normalizedInput =
|
|
9958
|
+
const normalizedInput = path14.normalize(inputPath);
|
|
8998
9959
|
return knowledgeBases.findIndex(
|
|
8999
|
-
(kb) =>
|
|
9960
|
+
(kb) => path14.normalize(kb) === normalizedInput || normalizeKnowledgeBasePath2(kb, projectRoot) === normalizedInput
|
|
9000
9961
|
);
|
|
9001
9962
|
}
|
|
9002
9963
|
|
|
@@ -9096,6 +10057,10 @@ function formatStatus(status) {
|
|
|
9096
10057
|
lines.push(`Failed batches: ${status.failedBatchesPath}`);
|
|
9097
10058
|
}
|
|
9098
10059
|
}
|
|
10060
|
+
if (status.warning) {
|
|
10061
|
+
lines.push("");
|
|
10062
|
+
lines.push(`INDEX WARNING: ${status.warning}`);
|
|
10063
|
+
}
|
|
9099
10064
|
if (status.compatibility && !status.compatibility.compatible) {
|
|
9100
10065
|
lines.push("");
|
|
9101
10066
|
lines.push(`COMPATIBILITY WARNING: ${status.compatibility.reason}`);
|
|
@@ -9201,16 +10166,16 @@ function formatCallGraphCallees(symbolId, callees, relationshipType) {
|
|
|
9201
10166
|
return `[${index + 1}] \u2192 ${edge.targetName} (${edge.callType})${confidence} at line ${edge.line}${edge.isResolved ? ` [resolved: ${edge.toSymbolId}]` : " [unresolved]"}`;
|
|
9202
10167
|
}).join("\n");
|
|
9203
10168
|
}
|
|
9204
|
-
function formatCallGraphPath(from, to,
|
|
9205
|
-
if (
|
|
10169
|
+
function formatCallGraphPath(from, to, path24) {
|
|
10170
|
+
if (path24.length === 0) {
|
|
9206
10171
|
return `No path found between "${from}" and "${to}". They may be in disconnected components, or the call graph index needs updating.`;
|
|
9207
10172
|
}
|
|
9208
|
-
const formatted =
|
|
10173
|
+
const formatted = path24.map((hop, index) => {
|
|
9209
10174
|
const prefix = index === 0 ? "[start]" : `--${hop.callType}-->`;
|
|
9210
10175
|
const location = hop.filePath ? ` (${hop.filePath}:${hop.line})` : "";
|
|
9211
10176
|
return `${prefix} ${hop.symbolName}${location}`;
|
|
9212
10177
|
});
|
|
9213
|
-
return `Path (${
|
|
10178
|
+
return `Path (${path24.length} hops):
|
|
9214
10179
|
${formatted.join("\n")}`;
|
|
9215
10180
|
}
|
|
9216
10181
|
function formatResultHeader(result, index) {
|
|
@@ -9250,8 +10215,8 @@ ${truncateContent(r.content)}
|
|
|
9250
10215
|
}
|
|
9251
10216
|
|
|
9252
10217
|
// src/tools/config-state.ts
|
|
9253
|
-
var
|
|
9254
|
-
var
|
|
10218
|
+
var import_fs9 = require("fs");
|
|
10219
|
+
var path15 = __toESM(require("path"), 1);
|
|
9255
10220
|
function normalizeKnowledgeBasePaths(config, projectRoot) {
|
|
9256
10221
|
const normalized = { ...config };
|
|
9257
10222
|
if (Array.isArray(normalized.knowledgeBases)) {
|
|
@@ -9278,10 +10243,10 @@ function loadEditableConfig(projectRoot, host = "opencode") {
|
|
|
9278
10243
|
}
|
|
9279
10244
|
function saveConfig(projectRoot, config, host = "opencode") {
|
|
9280
10245
|
const configPath = getConfigPath(projectRoot, host);
|
|
9281
|
-
const configDir =
|
|
9282
|
-
const configBaseDir =
|
|
9283
|
-
if (!(0,
|
|
9284
|
-
(0,
|
|
10246
|
+
const configDir = path15.dirname(configPath);
|
|
10247
|
+
const configBaseDir = path15.dirname(configDir);
|
|
10248
|
+
if (!(0, import_fs9.existsSync)(configDir)) {
|
|
10249
|
+
(0, import_fs9.mkdirSync)(configDir, { recursive: true });
|
|
9285
10250
|
}
|
|
9286
10251
|
const serializableConfig = { ...config };
|
|
9287
10252
|
if (Array.isArray(serializableConfig.knowledgeBases)) {
|
|
@@ -9289,13 +10254,31 @@ function saveConfig(projectRoot, config, host = "opencode") {
|
|
|
9289
10254
|
(kb) => serializeConfigPathValue(kb, configBaseDir)
|
|
9290
10255
|
);
|
|
9291
10256
|
}
|
|
9292
|
-
(0,
|
|
10257
|
+
(0, import_fs9.writeFileSync)(configPath, JSON.stringify(serializableConfig, null, 2) + "\n", "utf-8");
|
|
9293
10258
|
}
|
|
9294
10259
|
|
|
9295
10260
|
// src/tools/operations.ts
|
|
9296
10261
|
var indexerCache = /* @__PURE__ */ new Map();
|
|
9297
10262
|
var configCache = /* @__PURE__ */ new Map();
|
|
9298
10263
|
var defaultProjectRoots = /* @__PURE__ */ new Map();
|
|
10264
|
+
function getIndexBusyResult(error) {
|
|
10265
|
+
if (!isIndexLockContentionError(error)) return null;
|
|
10266
|
+
const owner = error.owner;
|
|
10267
|
+
const ownerText = owner ? `PID ${owner.pid}, operation ${owner.operation}, since ${owner.startedAt}` : "unreadable owner";
|
|
10268
|
+
if (error.reason === "legacy-lock") {
|
|
10269
|
+
return {
|
|
10270
|
+
kind: "busy",
|
|
10271
|
+
text: `INDEX_BUSY: legacy lock format detected (${ownerText}). Verify the PID and remove this lock manually only if it is stale.`
|
|
10272
|
+
};
|
|
10273
|
+
}
|
|
10274
|
+
if (error.reason === "unknown-owner") {
|
|
10275
|
+
return {
|
|
10276
|
+
kind: "busy",
|
|
10277
|
+
text: `INDEX_BUSY: unreadable or remote lock owner (${ownerText}). Automatic recovery was refused; manual verification is required.`
|
|
10278
|
+
};
|
|
10279
|
+
}
|
|
10280
|
+
return { kind: "busy", text: `INDEX_BUSY: another index operation is already in progress (${ownerText}).` };
|
|
10281
|
+
}
|
|
9299
10282
|
function getProjectRoot(projectRoot, host) {
|
|
9300
10283
|
if (projectRoot) {
|
|
9301
10284
|
return projectRoot;
|
|
@@ -9340,8 +10323,8 @@ function refreshIndexerForDirectory(projectRoot, host = "opencode", config = par
|
|
|
9340
10323
|
}
|
|
9341
10324
|
function shouldForceLocalizeProjectIndex(projectRoot, host = "opencode") {
|
|
9342
10325
|
const root = getProjectRoot(projectRoot, host);
|
|
9343
|
-
const localIndexPath =
|
|
9344
|
-
if ((0,
|
|
10326
|
+
const localIndexPath = path16.join(root, getHostProjectIndexRelativePath(host));
|
|
10327
|
+
if ((0, import_fs10.existsSync)(localIndexPath)) {
|
|
9345
10328
|
return false;
|
|
9346
10329
|
}
|
|
9347
10330
|
const inheritedIndexPath = resolveWorktreeFallbackProjectIndexPath(root, host);
|
|
@@ -9397,35 +10380,46 @@ async function getCallGraphPath(projectRoot, host, from, to, maxDepth) {
|
|
|
9397
10380
|
async function runIndexCodebase(projectRoot, host, args, onProgress) {
|
|
9398
10381
|
const root = getProjectRoot(projectRoot, host);
|
|
9399
10382
|
const key = getIndexerCacheKey(root, host);
|
|
9400
|
-
const cachedConfig = configCache.get(key);
|
|
9401
10383
|
let indexer = getIndexerForProject(root, host);
|
|
9402
|
-
|
|
9403
|
-
|
|
9404
|
-
|
|
9405
|
-
|
|
9406
|
-
|
|
9407
|
-
|
|
9408
|
-
|
|
9409
|
-
|
|
9410
|
-
|
|
9411
|
-
|
|
9412
|
-
|
|
9413
|
-
|
|
9414
|
-
|
|
9415
|
-
|
|
9416
|
-
|
|
9417
|
-
|
|
9418
|
-
|
|
9419
|
-
|
|
9420
|
-
|
|
9421
|
-
chunksProcessed: progress.chunksProcessed,
|
|
9422
|
-
totalChunks: progress.totalChunks,
|
|
9423
|
-
percentage: calculatePercentage(progress)
|
|
10384
|
+
const runtimeConfig = configCache.get(key);
|
|
10385
|
+
try {
|
|
10386
|
+
if (args.estimateOnly) {
|
|
10387
|
+
return { kind: "estimate", estimate: await indexer.estimateCost() };
|
|
10388
|
+
}
|
|
10389
|
+
const runIndex = async (target) => {
|
|
10390
|
+
const operation = args.force ? target.forceIndex.bind(target) : target.index.bind(target);
|
|
10391
|
+
return operation((progress) => {
|
|
10392
|
+
if (onProgress) {
|
|
10393
|
+
return onProgress(formatProgressTitle(progress), {
|
|
10394
|
+
phase: progress.phase,
|
|
10395
|
+
filesProcessed: progress.filesProcessed,
|
|
10396
|
+
totalFiles: progress.totalFiles,
|
|
10397
|
+
chunksProcessed: progress.chunksProcessed,
|
|
10398
|
+
totalChunks: progress.totalChunks,
|
|
10399
|
+
percentage: calculatePercentage(progress)
|
|
10400
|
+
});
|
|
10401
|
+
}
|
|
10402
|
+
return Promise.resolve();
|
|
9424
10403
|
});
|
|
10404
|
+
};
|
|
10405
|
+
let stats;
|
|
10406
|
+
if (args.force && shouldForceLocalizeProjectIndex(root, host)) {
|
|
10407
|
+
const inheritedIndexPath = resolveProjectIndexPath(root, runtimeConfig.scope, host);
|
|
10408
|
+
stats = await withIndexLock(inheritedIndexPath, "force-index", async () => {
|
|
10409
|
+
materializeLocalProjectConfig(root, loadProjectConfigLayer(root, host), host);
|
|
10410
|
+
refreshIndexerForDirectory(root, host, runtimeConfig);
|
|
10411
|
+
indexer = getIndexerForProject(root, host);
|
|
10412
|
+
return runIndex(indexer);
|
|
10413
|
+
}, { completeRecoveries: false });
|
|
10414
|
+
} else {
|
|
10415
|
+
stats = await runIndex(indexer);
|
|
9425
10416
|
}
|
|
9426
|
-
return
|
|
9427
|
-
})
|
|
9428
|
-
|
|
10417
|
+
return { kind: "stats", stats };
|
|
10418
|
+
} catch (error) {
|
|
10419
|
+
const busyResult = getIndexBusyResult(error);
|
|
10420
|
+
if (!busyResult) throw error;
|
|
10421
|
+
return busyResult;
|
|
10422
|
+
}
|
|
9429
10423
|
}
|
|
9430
10424
|
async function getIndexStatus(projectRoot, host) {
|
|
9431
10425
|
const indexer = getIndexerForProject(projectRoot, host);
|
|
@@ -9435,6 +10429,15 @@ async function getIndexHealthCheck(projectRoot, host) {
|
|
|
9435
10429
|
const indexer = getIndexerForProject(projectRoot, host);
|
|
9436
10430
|
return indexer.healthCheck();
|
|
9437
10431
|
}
|
|
10432
|
+
async function runIndexHealthCheck(projectRoot, host) {
|
|
10433
|
+
try {
|
|
10434
|
+
return { kind: "health", health: await getIndexHealthCheck(projectRoot, host) };
|
|
10435
|
+
} catch (error) {
|
|
10436
|
+
const busyResult = getIndexBusyResult(error);
|
|
10437
|
+
if (!busyResult) throw error;
|
|
10438
|
+
return busyResult;
|
|
10439
|
+
}
|
|
10440
|
+
}
|
|
9438
10441
|
async function getIndexMetrics(projectRoot, host) {
|
|
9439
10442
|
const indexer = getIndexerForProject(projectRoot, host);
|
|
9440
10443
|
const logger = indexer.getLogger();
|
|
@@ -9490,15 +10493,15 @@ async function getIndexLogs(projectRoot, host, args) {
|
|
|
9490
10493
|
function addKnowledgeBase(projectRoot, host, knowledgeBasePath) {
|
|
9491
10494
|
const root = getProjectRoot(projectRoot, host);
|
|
9492
10495
|
const inputPath = knowledgeBasePath.trim();
|
|
9493
|
-
const normalizedPath =
|
|
9494
|
-
|
|
10496
|
+
const normalizedPath = path16.resolve(
|
|
10497
|
+
path16.isAbsolute(inputPath) ? inputPath : resolveKnowledgeBasePath(inputPath, root)
|
|
9495
10498
|
);
|
|
9496
|
-
if (!(0,
|
|
10499
|
+
if (!(0, import_fs10.existsSync)(normalizedPath)) {
|
|
9497
10500
|
return `Error: Directory does not exist: ${normalizedPath}`;
|
|
9498
10501
|
}
|
|
9499
10502
|
let realPath;
|
|
9500
10503
|
try {
|
|
9501
|
-
realPath = (0,
|
|
10504
|
+
realPath = (0, import_fs10.realpathSync)(normalizedPath);
|
|
9502
10505
|
} catch {
|
|
9503
10506
|
return `Error: Cannot resolve path: ${normalizedPath}`;
|
|
9504
10507
|
}
|
|
@@ -9527,13 +10530,13 @@ function addKnowledgeBase(projectRoot, host, knowledgeBasePath) {
|
|
|
9527
10530
|
}
|
|
9528
10531
|
}
|
|
9529
10532
|
for (const dotDir of sensitiveDotDirs) {
|
|
9530
|
-
const sensitiveDir =
|
|
10533
|
+
const sensitiveDir = path16.join(homeDir, dotDir);
|
|
9531
10534
|
if (sensitiveDir && (realPath === sensitiveDir || realPath.startsWith(`${sensitiveDir}/`))) {
|
|
9532
10535
|
return `Error: Adding sensitive directory as knowledge base is not allowed: ${normalizedPath}`;
|
|
9533
10536
|
}
|
|
9534
10537
|
}
|
|
9535
10538
|
try {
|
|
9536
|
-
const stat4 = (0,
|
|
10539
|
+
const stat4 = (0, import_fs10.statSync)(normalizedPath);
|
|
9537
10540
|
if (!stat4.isDirectory()) {
|
|
9538
10541
|
return `Error: Path is not a directory: ${normalizedPath}`;
|
|
9539
10542
|
}
|
|
@@ -9573,7 +10576,7 @@ function listKnowledgeBases(projectRoot, host) {
|
|
|
9573
10576
|
for (let i = 0; i < knowledgeBases.length; i++) {
|
|
9574
10577
|
const kb = knowledgeBases[i];
|
|
9575
10578
|
const resolvedPath = resolveKnowledgeBasePath(kb, root);
|
|
9576
|
-
const exists = (0,
|
|
10579
|
+
const exists = (0, import_fs10.existsSync)(resolvedPath);
|
|
9577
10580
|
result += `[${i + 1}] ${kb}
|
|
9578
10581
|
`;
|
|
9579
10582
|
result += ` Resolved: ${resolvedPath}
|
|
@@ -9582,7 +10585,7 @@ function listKnowledgeBases(projectRoot, host) {
|
|
|
9582
10585
|
`;
|
|
9583
10586
|
if (exists) {
|
|
9584
10587
|
try {
|
|
9585
|
-
const stat4 = (0,
|
|
10588
|
+
const stat4 = (0, import_fs10.statSync)(resolvedPath);
|
|
9586
10589
|
result += ` Type: ${stat4.isDirectory() ? "Directory" : "File"}
|
|
9587
10590
|
`;
|
|
9588
10591
|
} catch {
|
|
@@ -9590,7 +10593,7 @@ function listKnowledgeBases(projectRoot, host) {
|
|
|
9590
10593
|
}
|
|
9591
10594
|
result += "\n";
|
|
9592
10595
|
}
|
|
9593
|
-
const hasHostConfig = (0,
|
|
10596
|
+
const hasHostConfig = (0, import_fs10.existsSync)(path16.join(root, getHostProjectConfigRelativePath(host)));
|
|
9594
10597
|
if (hasHostConfig) {
|
|
9595
10598
|
result += `
|
|
9596
10599
|
Config sources: 1 file(s).`;
|
|
@@ -9713,7 +10716,7 @@ var ReaddirpStream = class extends import_node_stream.Readable {
|
|
|
9713
10716
|
this._directoryFilter = normalizeFilter(opts.directoryFilter);
|
|
9714
10717
|
const statMethod = opts.lstat ? import_promises.lstat : import_promises.stat;
|
|
9715
10718
|
if (wantBigintFsStats) {
|
|
9716
|
-
this._stat = (
|
|
10719
|
+
this._stat = (path24) => statMethod(path24, { bigint: true });
|
|
9717
10720
|
} else {
|
|
9718
10721
|
this._stat = statMethod;
|
|
9719
10722
|
}
|
|
@@ -9738,8 +10741,8 @@ var ReaddirpStream = class extends import_node_stream.Readable {
|
|
|
9738
10741
|
const par = this.parent;
|
|
9739
10742
|
const fil = par && par.files;
|
|
9740
10743
|
if (fil && fil.length > 0) {
|
|
9741
|
-
const { path:
|
|
9742
|
-
const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent,
|
|
10744
|
+
const { path: path24, depth } = par;
|
|
10745
|
+
const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path24));
|
|
9743
10746
|
const awaited = await Promise.all(slice);
|
|
9744
10747
|
for (const entry of awaited) {
|
|
9745
10748
|
if (!entry)
|
|
@@ -9779,20 +10782,20 @@ var ReaddirpStream = class extends import_node_stream.Readable {
|
|
|
9779
10782
|
this.reading = false;
|
|
9780
10783
|
}
|
|
9781
10784
|
}
|
|
9782
|
-
async _exploreDir(
|
|
10785
|
+
async _exploreDir(path24, depth) {
|
|
9783
10786
|
let files;
|
|
9784
10787
|
try {
|
|
9785
|
-
files = await (0, import_promises.readdir)(
|
|
10788
|
+
files = await (0, import_promises.readdir)(path24, this._rdOptions);
|
|
9786
10789
|
} catch (error) {
|
|
9787
10790
|
this._onError(error);
|
|
9788
10791
|
}
|
|
9789
|
-
return { files, depth, path:
|
|
10792
|
+
return { files, depth, path: path24 };
|
|
9790
10793
|
}
|
|
9791
|
-
async _formatEntry(dirent,
|
|
10794
|
+
async _formatEntry(dirent, path24) {
|
|
9792
10795
|
let entry;
|
|
9793
10796
|
const basename5 = this._isDirent ? dirent.name : dirent;
|
|
9794
10797
|
try {
|
|
9795
|
-
const fullPath = (0, import_node_path.resolve)((0, import_node_path.join)(
|
|
10798
|
+
const fullPath = (0, import_node_path.resolve)((0, import_node_path.join)(path24, basename5));
|
|
9796
10799
|
entry = { path: (0, import_node_path.relative)(this._root, fullPath), fullPath, basename: basename5 };
|
|
9797
10800
|
entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
|
|
9798
10801
|
} catch (err) {
|
|
@@ -10192,16 +11195,16 @@ var delFromSet = (main, prop, item) => {
|
|
|
10192
11195
|
};
|
|
10193
11196
|
var isEmptySet = (val) => val instanceof Set ? val.size === 0 : !val;
|
|
10194
11197
|
var FsWatchInstances = /* @__PURE__ */ new Map();
|
|
10195
|
-
function createFsWatchInstance(
|
|
11198
|
+
function createFsWatchInstance(path24, options, listener, errHandler, emitRaw) {
|
|
10196
11199
|
const handleEvent = (rawEvent, evPath) => {
|
|
10197
|
-
listener(
|
|
10198
|
-
emitRaw(rawEvent, evPath, { watchedPath:
|
|
10199
|
-
if (evPath &&
|
|
10200
|
-
fsWatchBroadcast(sp.resolve(
|
|
11200
|
+
listener(path24);
|
|
11201
|
+
emitRaw(rawEvent, evPath, { watchedPath: path24 });
|
|
11202
|
+
if (evPath && path24 !== evPath) {
|
|
11203
|
+
fsWatchBroadcast(sp.resolve(path24, evPath), KEY_LISTENERS, sp.join(path24, evPath));
|
|
10201
11204
|
}
|
|
10202
11205
|
};
|
|
10203
11206
|
try {
|
|
10204
|
-
return (0, import_node_fs.watch)(
|
|
11207
|
+
return (0, import_node_fs.watch)(path24, {
|
|
10205
11208
|
persistent: options.persistent
|
|
10206
11209
|
}, handleEvent);
|
|
10207
11210
|
} catch (error) {
|
|
@@ -10217,12 +11220,12 @@ var fsWatchBroadcast = (fullPath, listenerType, val1, val2, val3) => {
|
|
|
10217
11220
|
listener(val1, val2, val3);
|
|
10218
11221
|
});
|
|
10219
11222
|
};
|
|
10220
|
-
var setFsWatchListener = (
|
|
11223
|
+
var setFsWatchListener = (path24, fullPath, options, handlers) => {
|
|
10221
11224
|
const { listener, errHandler, rawEmitter } = handlers;
|
|
10222
11225
|
let cont = FsWatchInstances.get(fullPath);
|
|
10223
11226
|
let watcher;
|
|
10224
11227
|
if (!options.persistent) {
|
|
10225
|
-
watcher = createFsWatchInstance(
|
|
11228
|
+
watcher = createFsWatchInstance(path24, options, listener, errHandler, rawEmitter);
|
|
10226
11229
|
if (!watcher)
|
|
10227
11230
|
return;
|
|
10228
11231
|
return watcher.close.bind(watcher);
|
|
@@ -10233,7 +11236,7 @@ var setFsWatchListener = (path23, fullPath, options, handlers) => {
|
|
|
10233
11236
|
addAndConvert(cont, KEY_RAW, rawEmitter);
|
|
10234
11237
|
} else {
|
|
10235
11238
|
watcher = createFsWatchInstance(
|
|
10236
|
-
|
|
11239
|
+
path24,
|
|
10237
11240
|
options,
|
|
10238
11241
|
fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS),
|
|
10239
11242
|
errHandler,
|
|
@@ -10248,7 +11251,7 @@ var setFsWatchListener = (path23, fullPath, options, handlers) => {
|
|
|
10248
11251
|
cont.watcherUnusable = true;
|
|
10249
11252
|
if (isWindows && error.code === "EPERM") {
|
|
10250
11253
|
try {
|
|
10251
|
-
const fd = await (0, import_promises2.open)(
|
|
11254
|
+
const fd = await (0, import_promises2.open)(path24, "r");
|
|
10252
11255
|
await fd.close();
|
|
10253
11256
|
broadcastErr(error);
|
|
10254
11257
|
} catch (err) {
|
|
@@ -10279,7 +11282,7 @@ var setFsWatchListener = (path23, fullPath, options, handlers) => {
|
|
|
10279
11282
|
};
|
|
10280
11283
|
};
|
|
10281
11284
|
var FsWatchFileInstances = /* @__PURE__ */ new Map();
|
|
10282
|
-
var setFsWatchFileListener = (
|
|
11285
|
+
var setFsWatchFileListener = (path24, fullPath, options, handlers) => {
|
|
10283
11286
|
const { listener, rawEmitter } = handlers;
|
|
10284
11287
|
let cont = FsWatchFileInstances.get(fullPath);
|
|
10285
11288
|
const copts = cont && cont.options;
|
|
@@ -10301,7 +11304,7 @@ var setFsWatchFileListener = (path23, fullPath, options, handlers) => {
|
|
|
10301
11304
|
});
|
|
10302
11305
|
const currmtime = curr.mtimeMs;
|
|
10303
11306
|
if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {
|
|
10304
|
-
foreach(cont.listeners, (listener2) => listener2(
|
|
11307
|
+
foreach(cont.listeners, (listener2) => listener2(path24, curr));
|
|
10305
11308
|
}
|
|
10306
11309
|
})
|
|
10307
11310
|
};
|
|
@@ -10331,13 +11334,13 @@ var NodeFsHandler = class {
|
|
|
10331
11334
|
* @param listener on fs change
|
|
10332
11335
|
* @returns closer for the watcher instance
|
|
10333
11336
|
*/
|
|
10334
|
-
_watchWithNodeFs(
|
|
11337
|
+
_watchWithNodeFs(path24, listener) {
|
|
10335
11338
|
const opts = this.fsw.options;
|
|
10336
|
-
const directory = sp.dirname(
|
|
10337
|
-
const basename5 = sp.basename(
|
|
11339
|
+
const directory = sp.dirname(path24);
|
|
11340
|
+
const basename5 = sp.basename(path24);
|
|
10338
11341
|
const parent = this.fsw._getWatchedDir(directory);
|
|
10339
11342
|
parent.add(basename5);
|
|
10340
|
-
const absolutePath = sp.resolve(
|
|
11343
|
+
const absolutePath = sp.resolve(path24);
|
|
10341
11344
|
const options = {
|
|
10342
11345
|
persistent: opts.persistent
|
|
10343
11346
|
};
|
|
@@ -10347,12 +11350,12 @@ var NodeFsHandler = class {
|
|
|
10347
11350
|
if (opts.usePolling) {
|
|
10348
11351
|
const enableBin = opts.interval !== opts.binaryInterval;
|
|
10349
11352
|
options.interval = enableBin && isBinaryPath(basename5) ? opts.binaryInterval : opts.interval;
|
|
10350
|
-
closer = setFsWatchFileListener(
|
|
11353
|
+
closer = setFsWatchFileListener(path24, absolutePath, options, {
|
|
10351
11354
|
listener,
|
|
10352
11355
|
rawEmitter: this.fsw._emitRaw
|
|
10353
11356
|
});
|
|
10354
11357
|
} else {
|
|
10355
|
-
closer = setFsWatchListener(
|
|
11358
|
+
closer = setFsWatchListener(path24, absolutePath, options, {
|
|
10356
11359
|
listener,
|
|
10357
11360
|
errHandler: this._boundHandleError,
|
|
10358
11361
|
rawEmitter: this.fsw._emitRaw
|
|
@@ -10374,7 +11377,7 @@ var NodeFsHandler = class {
|
|
|
10374
11377
|
let prevStats = stats;
|
|
10375
11378
|
if (parent.has(basename5))
|
|
10376
11379
|
return;
|
|
10377
|
-
const listener = async (
|
|
11380
|
+
const listener = async (path24, newStats) => {
|
|
10378
11381
|
if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5))
|
|
10379
11382
|
return;
|
|
10380
11383
|
if (!newStats || newStats.mtimeMs === 0) {
|
|
@@ -10388,11 +11391,11 @@ var NodeFsHandler = class {
|
|
|
10388
11391
|
this.fsw._emit(EV.CHANGE, file, newStats2);
|
|
10389
11392
|
}
|
|
10390
11393
|
if ((isMacos || isLinux || isFreeBSD) && prevStats.ino !== newStats2.ino) {
|
|
10391
|
-
this.fsw._closeFile(
|
|
11394
|
+
this.fsw._closeFile(path24);
|
|
10392
11395
|
prevStats = newStats2;
|
|
10393
11396
|
const closer2 = this._watchWithNodeFs(file, listener);
|
|
10394
11397
|
if (closer2)
|
|
10395
|
-
this.fsw._addPathCloser(
|
|
11398
|
+
this.fsw._addPathCloser(path24, closer2);
|
|
10396
11399
|
} else {
|
|
10397
11400
|
prevStats = newStats2;
|
|
10398
11401
|
}
|
|
@@ -10424,7 +11427,7 @@ var NodeFsHandler = class {
|
|
|
10424
11427
|
* @param item basename of this item
|
|
10425
11428
|
* @returns true if no more processing is needed for this entry.
|
|
10426
11429
|
*/
|
|
10427
|
-
async _handleSymlink(entry, directory,
|
|
11430
|
+
async _handleSymlink(entry, directory, path24, item) {
|
|
10428
11431
|
if (this.fsw.closed) {
|
|
10429
11432
|
return;
|
|
10430
11433
|
}
|
|
@@ -10434,7 +11437,7 @@ var NodeFsHandler = class {
|
|
|
10434
11437
|
this.fsw._incrReadyCount();
|
|
10435
11438
|
let linkPath;
|
|
10436
11439
|
try {
|
|
10437
|
-
linkPath = await (0, import_promises2.realpath)(
|
|
11440
|
+
linkPath = await (0, import_promises2.realpath)(path24);
|
|
10438
11441
|
} catch (e) {
|
|
10439
11442
|
this.fsw._emitReady();
|
|
10440
11443
|
return true;
|
|
@@ -10444,12 +11447,12 @@ var NodeFsHandler = class {
|
|
|
10444
11447
|
if (dir.has(item)) {
|
|
10445
11448
|
if (this.fsw._symlinkPaths.get(full) !== linkPath) {
|
|
10446
11449
|
this.fsw._symlinkPaths.set(full, linkPath);
|
|
10447
|
-
this.fsw._emit(EV.CHANGE,
|
|
11450
|
+
this.fsw._emit(EV.CHANGE, path24, entry.stats);
|
|
10448
11451
|
}
|
|
10449
11452
|
} else {
|
|
10450
11453
|
dir.add(item);
|
|
10451
11454
|
this.fsw._symlinkPaths.set(full, linkPath);
|
|
10452
|
-
this.fsw._emit(EV.ADD,
|
|
11455
|
+
this.fsw._emit(EV.ADD, path24, entry.stats);
|
|
10453
11456
|
}
|
|
10454
11457
|
this.fsw._emitReady();
|
|
10455
11458
|
return true;
|
|
@@ -10479,9 +11482,9 @@ var NodeFsHandler = class {
|
|
|
10479
11482
|
return;
|
|
10480
11483
|
}
|
|
10481
11484
|
const item = entry.path;
|
|
10482
|
-
let
|
|
11485
|
+
let path24 = sp.join(directory, item);
|
|
10483
11486
|
current.add(item);
|
|
10484
|
-
if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory,
|
|
11487
|
+
if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path24, item)) {
|
|
10485
11488
|
return;
|
|
10486
11489
|
}
|
|
10487
11490
|
if (this.fsw.closed) {
|
|
@@ -10490,8 +11493,8 @@ var NodeFsHandler = class {
|
|
|
10490
11493
|
}
|
|
10491
11494
|
if (item === target || !target && !previous.has(item)) {
|
|
10492
11495
|
this.fsw._incrReadyCount();
|
|
10493
|
-
|
|
10494
|
-
this._addToNodeFs(
|
|
11496
|
+
path24 = sp.join(dir, sp.relative(dir, path24));
|
|
11497
|
+
this._addToNodeFs(path24, initialAdd, wh, depth + 1);
|
|
10495
11498
|
}
|
|
10496
11499
|
}).on(EV.ERROR, this._boundHandleError);
|
|
10497
11500
|
return new Promise((resolve13, reject) => {
|
|
@@ -10560,13 +11563,13 @@ var NodeFsHandler = class {
|
|
|
10560
11563
|
* @param depth Child path actually targeted for watch
|
|
10561
11564
|
* @param target Child path actually targeted for watch
|
|
10562
11565
|
*/
|
|
10563
|
-
async _addToNodeFs(
|
|
11566
|
+
async _addToNodeFs(path24, initialAdd, priorWh, depth, target) {
|
|
10564
11567
|
const ready = this.fsw._emitReady;
|
|
10565
|
-
if (this.fsw._isIgnored(
|
|
11568
|
+
if (this.fsw._isIgnored(path24) || this.fsw.closed) {
|
|
10566
11569
|
ready();
|
|
10567
11570
|
return false;
|
|
10568
11571
|
}
|
|
10569
|
-
const wh = this.fsw._getWatchHelpers(
|
|
11572
|
+
const wh = this.fsw._getWatchHelpers(path24);
|
|
10570
11573
|
if (priorWh) {
|
|
10571
11574
|
wh.filterPath = (entry) => priorWh.filterPath(entry);
|
|
10572
11575
|
wh.filterDir = (entry) => priorWh.filterDir(entry);
|
|
@@ -10582,8 +11585,8 @@ var NodeFsHandler = class {
|
|
|
10582
11585
|
const follow = this.fsw.options.followSymlinks;
|
|
10583
11586
|
let closer;
|
|
10584
11587
|
if (stats.isDirectory()) {
|
|
10585
|
-
const absPath = sp.resolve(
|
|
10586
|
-
const targetPath = follow ? await (0, import_promises2.realpath)(
|
|
11588
|
+
const absPath = sp.resolve(path24);
|
|
11589
|
+
const targetPath = follow ? await (0, import_promises2.realpath)(path24) : path24;
|
|
10587
11590
|
if (this.fsw.closed)
|
|
10588
11591
|
return;
|
|
10589
11592
|
closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
|
|
@@ -10593,29 +11596,29 @@ var NodeFsHandler = class {
|
|
|
10593
11596
|
this.fsw._symlinkPaths.set(absPath, targetPath);
|
|
10594
11597
|
}
|
|
10595
11598
|
} else if (stats.isSymbolicLink()) {
|
|
10596
|
-
const targetPath = follow ? await (0, import_promises2.realpath)(
|
|
11599
|
+
const targetPath = follow ? await (0, import_promises2.realpath)(path24) : path24;
|
|
10597
11600
|
if (this.fsw.closed)
|
|
10598
11601
|
return;
|
|
10599
11602
|
const parent = sp.dirname(wh.watchPath);
|
|
10600
11603
|
this.fsw._getWatchedDir(parent).add(wh.watchPath);
|
|
10601
11604
|
this.fsw._emit(EV.ADD, wh.watchPath, stats);
|
|
10602
|
-
closer = await this._handleDir(parent, stats, initialAdd, depth,
|
|
11605
|
+
closer = await this._handleDir(parent, stats, initialAdd, depth, path24, wh, targetPath);
|
|
10603
11606
|
if (this.fsw.closed)
|
|
10604
11607
|
return;
|
|
10605
11608
|
if (targetPath !== void 0) {
|
|
10606
|
-
this.fsw._symlinkPaths.set(sp.resolve(
|
|
11609
|
+
this.fsw._symlinkPaths.set(sp.resolve(path24), targetPath);
|
|
10607
11610
|
}
|
|
10608
11611
|
} else {
|
|
10609
11612
|
closer = this._handleFile(wh.watchPath, stats, initialAdd);
|
|
10610
11613
|
}
|
|
10611
11614
|
ready();
|
|
10612
11615
|
if (closer)
|
|
10613
|
-
this.fsw._addPathCloser(
|
|
11616
|
+
this.fsw._addPathCloser(path24, closer);
|
|
10614
11617
|
return false;
|
|
10615
11618
|
} catch (error) {
|
|
10616
11619
|
if (this.fsw._handleError(error)) {
|
|
10617
11620
|
ready();
|
|
10618
|
-
return
|
|
11621
|
+
return path24;
|
|
10619
11622
|
}
|
|
10620
11623
|
}
|
|
10621
11624
|
}
|
|
@@ -10658,24 +11661,24 @@ function createPattern(matcher) {
|
|
|
10658
11661
|
}
|
|
10659
11662
|
return () => false;
|
|
10660
11663
|
}
|
|
10661
|
-
function normalizePath(
|
|
10662
|
-
if (typeof
|
|
11664
|
+
function normalizePath(path24) {
|
|
11665
|
+
if (typeof path24 !== "string")
|
|
10663
11666
|
throw new Error("string expected");
|
|
10664
|
-
|
|
10665
|
-
|
|
11667
|
+
path24 = sp2.normalize(path24);
|
|
11668
|
+
path24 = path24.replace(/\\/g, "/");
|
|
10666
11669
|
let prepend = false;
|
|
10667
|
-
if (
|
|
11670
|
+
if (path24.startsWith("//"))
|
|
10668
11671
|
prepend = true;
|
|
10669
|
-
|
|
11672
|
+
path24 = path24.replace(DOUBLE_SLASH_RE, "/");
|
|
10670
11673
|
if (prepend)
|
|
10671
|
-
|
|
10672
|
-
return
|
|
11674
|
+
path24 = "/" + path24;
|
|
11675
|
+
return path24;
|
|
10673
11676
|
}
|
|
10674
11677
|
function matchPatterns(patterns, testString, stats) {
|
|
10675
|
-
const
|
|
11678
|
+
const path24 = normalizePath(testString);
|
|
10676
11679
|
for (let index = 0; index < patterns.length; index++) {
|
|
10677
11680
|
const pattern = patterns[index];
|
|
10678
|
-
if (pattern(
|
|
11681
|
+
if (pattern(path24, stats)) {
|
|
10679
11682
|
return true;
|
|
10680
11683
|
}
|
|
10681
11684
|
}
|
|
@@ -10713,19 +11716,19 @@ var toUnix = (string) => {
|
|
|
10713
11716
|
}
|
|
10714
11717
|
return str;
|
|
10715
11718
|
};
|
|
10716
|
-
var normalizePathToUnix = (
|
|
10717
|
-
var normalizeIgnored = (cwd = "") => (
|
|
10718
|
-
if (typeof
|
|
10719
|
-
return normalizePathToUnix(sp2.isAbsolute(
|
|
11719
|
+
var normalizePathToUnix = (path24) => toUnix(sp2.normalize(toUnix(path24)));
|
|
11720
|
+
var normalizeIgnored = (cwd = "") => (path24) => {
|
|
11721
|
+
if (typeof path24 === "string") {
|
|
11722
|
+
return normalizePathToUnix(sp2.isAbsolute(path24) ? path24 : sp2.join(cwd, path24));
|
|
10720
11723
|
} else {
|
|
10721
|
-
return
|
|
11724
|
+
return path24;
|
|
10722
11725
|
}
|
|
10723
11726
|
};
|
|
10724
|
-
var getAbsolutePath = (
|
|
10725
|
-
if (sp2.isAbsolute(
|
|
10726
|
-
return
|
|
11727
|
+
var getAbsolutePath = (path24, cwd) => {
|
|
11728
|
+
if (sp2.isAbsolute(path24)) {
|
|
11729
|
+
return path24;
|
|
10727
11730
|
}
|
|
10728
|
-
return sp2.join(cwd,
|
|
11731
|
+
return sp2.join(cwd, path24);
|
|
10729
11732
|
};
|
|
10730
11733
|
var EMPTY_SET = Object.freeze(/* @__PURE__ */ new Set());
|
|
10731
11734
|
var DirEntry = class {
|
|
@@ -10790,10 +11793,10 @@ var WatchHelper = class {
|
|
|
10790
11793
|
dirParts;
|
|
10791
11794
|
followSymlinks;
|
|
10792
11795
|
statMethod;
|
|
10793
|
-
constructor(
|
|
11796
|
+
constructor(path24, follow, fsw) {
|
|
10794
11797
|
this.fsw = fsw;
|
|
10795
|
-
const watchPath =
|
|
10796
|
-
this.path =
|
|
11798
|
+
const watchPath = path24;
|
|
11799
|
+
this.path = path24 = path24.replace(REPLACER_RE, "");
|
|
10797
11800
|
this.watchPath = watchPath;
|
|
10798
11801
|
this.fullWatchPath = sp2.resolve(watchPath);
|
|
10799
11802
|
this.dirParts = [];
|
|
@@ -10933,20 +11936,20 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
10933
11936
|
this._closePromise = void 0;
|
|
10934
11937
|
let paths = unifyPaths(paths_);
|
|
10935
11938
|
if (cwd) {
|
|
10936
|
-
paths = paths.map((
|
|
10937
|
-
const absPath = getAbsolutePath(
|
|
11939
|
+
paths = paths.map((path24) => {
|
|
11940
|
+
const absPath = getAbsolutePath(path24, cwd);
|
|
10938
11941
|
return absPath;
|
|
10939
11942
|
});
|
|
10940
11943
|
}
|
|
10941
|
-
paths.forEach((
|
|
10942
|
-
this._removeIgnoredPath(
|
|
11944
|
+
paths.forEach((path24) => {
|
|
11945
|
+
this._removeIgnoredPath(path24);
|
|
10943
11946
|
});
|
|
10944
11947
|
this._userIgnored = void 0;
|
|
10945
11948
|
if (!this._readyCount)
|
|
10946
11949
|
this._readyCount = 0;
|
|
10947
11950
|
this._readyCount += paths.length;
|
|
10948
|
-
Promise.all(paths.map(async (
|
|
10949
|
-
const res = await this._nodeFsHandler._addToNodeFs(
|
|
11951
|
+
Promise.all(paths.map(async (path24) => {
|
|
11952
|
+
const res = await this._nodeFsHandler._addToNodeFs(path24, !_internal, void 0, 0, _origAdd);
|
|
10950
11953
|
if (res)
|
|
10951
11954
|
this._emitReady();
|
|
10952
11955
|
return res;
|
|
@@ -10968,17 +11971,17 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
10968
11971
|
return this;
|
|
10969
11972
|
const paths = unifyPaths(paths_);
|
|
10970
11973
|
const { cwd } = this.options;
|
|
10971
|
-
paths.forEach((
|
|
10972
|
-
if (!sp2.isAbsolute(
|
|
11974
|
+
paths.forEach((path24) => {
|
|
11975
|
+
if (!sp2.isAbsolute(path24) && !this._closers.has(path24)) {
|
|
10973
11976
|
if (cwd)
|
|
10974
|
-
|
|
10975
|
-
|
|
11977
|
+
path24 = sp2.join(cwd, path24);
|
|
11978
|
+
path24 = sp2.resolve(path24);
|
|
10976
11979
|
}
|
|
10977
|
-
this._closePath(
|
|
10978
|
-
this._addIgnoredPath(
|
|
10979
|
-
if (this._watched.has(
|
|
11980
|
+
this._closePath(path24);
|
|
11981
|
+
this._addIgnoredPath(path24);
|
|
11982
|
+
if (this._watched.has(path24)) {
|
|
10980
11983
|
this._addIgnoredPath({
|
|
10981
|
-
path:
|
|
11984
|
+
path: path24,
|
|
10982
11985
|
recursive: true
|
|
10983
11986
|
});
|
|
10984
11987
|
}
|
|
@@ -11042,38 +12045,38 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
11042
12045
|
* @param stats arguments to be passed with event
|
|
11043
12046
|
* @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag
|
|
11044
12047
|
*/
|
|
11045
|
-
async _emit(event,
|
|
12048
|
+
async _emit(event, path24, stats) {
|
|
11046
12049
|
if (this.closed)
|
|
11047
12050
|
return;
|
|
11048
12051
|
const opts = this.options;
|
|
11049
12052
|
if (isWindows)
|
|
11050
|
-
|
|
12053
|
+
path24 = sp2.normalize(path24);
|
|
11051
12054
|
if (opts.cwd)
|
|
11052
|
-
|
|
11053
|
-
const args = [
|
|
12055
|
+
path24 = sp2.relative(opts.cwd, path24);
|
|
12056
|
+
const args = [path24];
|
|
11054
12057
|
if (stats != null)
|
|
11055
12058
|
args.push(stats);
|
|
11056
12059
|
const awf = opts.awaitWriteFinish;
|
|
11057
12060
|
let pw;
|
|
11058
|
-
if (awf && (pw = this._pendingWrites.get(
|
|
12061
|
+
if (awf && (pw = this._pendingWrites.get(path24))) {
|
|
11059
12062
|
pw.lastChange = /* @__PURE__ */ new Date();
|
|
11060
12063
|
return this;
|
|
11061
12064
|
}
|
|
11062
12065
|
if (opts.atomic) {
|
|
11063
12066
|
if (event === EVENTS.UNLINK) {
|
|
11064
|
-
this._pendingUnlinks.set(
|
|
12067
|
+
this._pendingUnlinks.set(path24, [event, ...args]);
|
|
11065
12068
|
setTimeout(() => {
|
|
11066
|
-
this._pendingUnlinks.forEach((entry,
|
|
12069
|
+
this._pendingUnlinks.forEach((entry, path25) => {
|
|
11067
12070
|
this.emit(...entry);
|
|
11068
12071
|
this.emit(EVENTS.ALL, ...entry);
|
|
11069
|
-
this._pendingUnlinks.delete(
|
|
12072
|
+
this._pendingUnlinks.delete(path25);
|
|
11070
12073
|
});
|
|
11071
12074
|
}, typeof opts.atomic === "number" ? opts.atomic : 100);
|
|
11072
12075
|
return this;
|
|
11073
12076
|
}
|
|
11074
|
-
if (event === EVENTS.ADD && this._pendingUnlinks.has(
|
|
12077
|
+
if (event === EVENTS.ADD && this._pendingUnlinks.has(path24)) {
|
|
11075
12078
|
event = EVENTS.CHANGE;
|
|
11076
|
-
this._pendingUnlinks.delete(
|
|
12079
|
+
this._pendingUnlinks.delete(path24);
|
|
11077
12080
|
}
|
|
11078
12081
|
}
|
|
11079
12082
|
if (awf && (event === EVENTS.ADD || event === EVENTS.CHANGE) && this._readyEmitted) {
|
|
@@ -11091,16 +12094,16 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
11091
12094
|
this.emitWithAll(event, args);
|
|
11092
12095
|
}
|
|
11093
12096
|
};
|
|
11094
|
-
this._awaitWriteFinish(
|
|
12097
|
+
this._awaitWriteFinish(path24, awf.stabilityThreshold, event, awfEmit);
|
|
11095
12098
|
return this;
|
|
11096
12099
|
}
|
|
11097
12100
|
if (event === EVENTS.CHANGE) {
|
|
11098
|
-
const isThrottled = !this._throttle(EVENTS.CHANGE,
|
|
12101
|
+
const isThrottled = !this._throttle(EVENTS.CHANGE, path24, 50);
|
|
11099
12102
|
if (isThrottled)
|
|
11100
12103
|
return this;
|
|
11101
12104
|
}
|
|
11102
12105
|
if (opts.alwaysStat && stats === void 0 && (event === EVENTS.ADD || event === EVENTS.ADD_DIR || event === EVENTS.CHANGE)) {
|
|
11103
|
-
const fullPath = opts.cwd ? sp2.join(opts.cwd,
|
|
12106
|
+
const fullPath = opts.cwd ? sp2.join(opts.cwd, path24) : path24;
|
|
11104
12107
|
let stats2;
|
|
11105
12108
|
try {
|
|
11106
12109
|
stats2 = await (0, import_promises3.stat)(fullPath);
|
|
@@ -11131,23 +12134,23 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
11131
12134
|
* @param timeout duration of time to suppress duplicate actions
|
|
11132
12135
|
* @returns tracking object or false if action should be suppressed
|
|
11133
12136
|
*/
|
|
11134
|
-
_throttle(actionType,
|
|
12137
|
+
_throttle(actionType, path24, timeout) {
|
|
11135
12138
|
if (!this._throttled.has(actionType)) {
|
|
11136
12139
|
this._throttled.set(actionType, /* @__PURE__ */ new Map());
|
|
11137
12140
|
}
|
|
11138
12141
|
const action = this._throttled.get(actionType);
|
|
11139
12142
|
if (!action)
|
|
11140
12143
|
throw new Error("invalid throttle");
|
|
11141
|
-
const actionPath = action.get(
|
|
12144
|
+
const actionPath = action.get(path24);
|
|
11142
12145
|
if (actionPath) {
|
|
11143
12146
|
actionPath.count++;
|
|
11144
12147
|
return false;
|
|
11145
12148
|
}
|
|
11146
12149
|
let timeoutObject;
|
|
11147
12150
|
const clear = () => {
|
|
11148
|
-
const item = action.get(
|
|
12151
|
+
const item = action.get(path24);
|
|
11149
12152
|
const count = item ? item.count : 0;
|
|
11150
|
-
action.delete(
|
|
12153
|
+
action.delete(path24);
|
|
11151
12154
|
clearTimeout(timeoutObject);
|
|
11152
12155
|
if (item)
|
|
11153
12156
|
clearTimeout(item.timeoutObject);
|
|
@@ -11155,7 +12158,7 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
11155
12158
|
};
|
|
11156
12159
|
timeoutObject = setTimeout(clear, timeout);
|
|
11157
12160
|
const thr = { timeoutObject, clear, count: 0 };
|
|
11158
|
-
action.set(
|
|
12161
|
+
action.set(path24, thr);
|
|
11159
12162
|
return thr;
|
|
11160
12163
|
}
|
|
11161
12164
|
_incrReadyCount() {
|
|
@@ -11169,44 +12172,44 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
11169
12172
|
* @param event
|
|
11170
12173
|
* @param awfEmit Callback to be called when ready for event to be emitted.
|
|
11171
12174
|
*/
|
|
11172
|
-
_awaitWriteFinish(
|
|
12175
|
+
_awaitWriteFinish(path24, threshold, event, awfEmit) {
|
|
11173
12176
|
const awf = this.options.awaitWriteFinish;
|
|
11174
12177
|
if (typeof awf !== "object")
|
|
11175
12178
|
return;
|
|
11176
12179
|
const pollInterval = awf.pollInterval;
|
|
11177
12180
|
let timeoutHandler;
|
|
11178
|
-
let fullPath =
|
|
11179
|
-
if (this.options.cwd && !sp2.isAbsolute(
|
|
11180
|
-
fullPath = sp2.join(this.options.cwd,
|
|
12181
|
+
let fullPath = path24;
|
|
12182
|
+
if (this.options.cwd && !sp2.isAbsolute(path24)) {
|
|
12183
|
+
fullPath = sp2.join(this.options.cwd, path24);
|
|
11181
12184
|
}
|
|
11182
12185
|
const now = /* @__PURE__ */ new Date();
|
|
11183
12186
|
const writes = this._pendingWrites;
|
|
11184
12187
|
function awaitWriteFinishFn(prevStat) {
|
|
11185
12188
|
(0, import_node_fs2.stat)(fullPath, (err, curStat) => {
|
|
11186
|
-
if (err || !writes.has(
|
|
12189
|
+
if (err || !writes.has(path24)) {
|
|
11187
12190
|
if (err && err.code !== "ENOENT")
|
|
11188
12191
|
awfEmit(err);
|
|
11189
12192
|
return;
|
|
11190
12193
|
}
|
|
11191
12194
|
const now2 = Number(/* @__PURE__ */ new Date());
|
|
11192
12195
|
if (prevStat && curStat.size !== prevStat.size) {
|
|
11193
|
-
writes.get(
|
|
12196
|
+
writes.get(path24).lastChange = now2;
|
|
11194
12197
|
}
|
|
11195
|
-
const pw = writes.get(
|
|
12198
|
+
const pw = writes.get(path24);
|
|
11196
12199
|
const df = now2 - pw.lastChange;
|
|
11197
12200
|
if (df >= threshold) {
|
|
11198
|
-
writes.delete(
|
|
12201
|
+
writes.delete(path24);
|
|
11199
12202
|
awfEmit(void 0, curStat);
|
|
11200
12203
|
} else {
|
|
11201
12204
|
timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);
|
|
11202
12205
|
}
|
|
11203
12206
|
});
|
|
11204
12207
|
}
|
|
11205
|
-
if (!writes.has(
|
|
11206
|
-
writes.set(
|
|
12208
|
+
if (!writes.has(path24)) {
|
|
12209
|
+
writes.set(path24, {
|
|
11207
12210
|
lastChange: now,
|
|
11208
12211
|
cancelWait: () => {
|
|
11209
|
-
writes.delete(
|
|
12212
|
+
writes.delete(path24);
|
|
11210
12213
|
clearTimeout(timeoutHandler);
|
|
11211
12214
|
return event;
|
|
11212
12215
|
}
|
|
@@ -11217,8 +12220,8 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
11217
12220
|
/**
|
|
11218
12221
|
* Determines whether user has asked to ignore this path.
|
|
11219
12222
|
*/
|
|
11220
|
-
_isIgnored(
|
|
11221
|
-
if (this.options.atomic && DOT_RE.test(
|
|
12223
|
+
_isIgnored(path24, stats) {
|
|
12224
|
+
if (this.options.atomic && DOT_RE.test(path24))
|
|
11222
12225
|
return true;
|
|
11223
12226
|
if (!this._userIgnored) {
|
|
11224
12227
|
const { cwd } = this.options;
|
|
@@ -11228,17 +12231,17 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
11228
12231
|
const list = [...ignoredPaths.map(normalizeIgnored(cwd)), ...ignored];
|
|
11229
12232
|
this._userIgnored = anymatch(list, void 0);
|
|
11230
12233
|
}
|
|
11231
|
-
return this._userIgnored(
|
|
12234
|
+
return this._userIgnored(path24, stats);
|
|
11232
12235
|
}
|
|
11233
|
-
_isntIgnored(
|
|
11234
|
-
return !this._isIgnored(
|
|
12236
|
+
_isntIgnored(path24, stat4) {
|
|
12237
|
+
return !this._isIgnored(path24, stat4);
|
|
11235
12238
|
}
|
|
11236
12239
|
/**
|
|
11237
12240
|
* Provides a set of common helpers and properties relating to symlink handling.
|
|
11238
12241
|
* @param path file or directory pattern being watched
|
|
11239
12242
|
*/
|
|
11240
|
-
_getWatchHelpers(
|
|
11241
|
-
return new WatchHelper(
|
|
12243
|
+
_getWatchHelpers(path24) {
|
|
12244
|
+
return new WatchHelper(path24, this.options.followSymlinks, this);
|
|
11242
12245
|
}
|
|
11243
12246
|
// Directory helpers
|
|
11244
12247
|
// -----------------
|
|
@@ -11270,63 +12273,63 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
11270
12273
|
* @param item base path of item/directory
|
|
11271
12274
|
*/
|
|
11272
12275
|
_remove(directory, item, isDirectory) {
|
|
11273
|
-
const
|
|
11274
|
-
const fullPath = sp2.resolve(
|
|
11275
|
-
isDirectory = isDirectory != null ? isDirectory : this._watched.has(
|
|
11276
|
-
if (!this._throttle("remove",
|
|
12276
|
+
const path24 = sp2.join(directory, item);
|
|
12277
|
+
const fullPath = sp2.resolve(path24);
|
|
12278
|
+
isDirectory = isDirectory != null ? isDirectory : this._watched.has(path24) || this._watched.has(fullPath);
|
|
12279
|
+
if (!this._throttle("remove", path24, 100))
|
|
11277
12280
|
return;
|
|
11278
12281
|
if (!isDirectory && this._watched.size === 1) {
|
|
11279
12282
|
this.add(directory, item, true);
|
|
11280
12283
|
}
|
|
11281
|
-
const wp = this._getWatchedDir(
|
|
12284
|
+
const wp = this._getWatchedDir(path24);
|
|
11282
12285
|
const nestedDirectoryChildren = wp.getChildren();
|
|
11283
|
-
nestedDirectoryChildren.forEach((nested) => this._remove(
|
|
12286
|
+
nestedDirectoryChildren.forEach((nested) => this._remove(path24, nested));
|
|
11284
12287
|
const parent = this._getWatchedDir(directory);
|
|
11285
12288
|
const wasTracked = parent.has(item);
|
|
11286
12289
|
parent.remove(item);
|
|
11287
12290
|
if (this._symlinkPaths.has(fullPath)) {
|
|
11288
12291
|
this._symlinkPaths.delete(fullPath);
|
|
11289
12292
|
}
|
|
11290
|
-
let relPath =
|
|
12293
|
+
let relPath = path24;
|
|
11291
12294
|
if (this.options.cwd)
|
|
11292
|
-
relPath = sp2.relative(this.options.cwd,
|
|
12295
|
+
relPath = sp2.relative(this.options.cwd, path24);
|
|
11293
12296
|
if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
|
|
11294
12297
|
const event = this._pendingWrites.get(relPath).cancelWait();
|
|
11295
12298
|
if (event === EVENTS.ADD)
|
|
11296
12299
|
return;
|
|
11297
12300
|
}
|
|
11298
|
-
this._watched.delete(
|
|
12301
|
+
this._watched.delete(path24);
|
|
11299
12302
|
this._watched.delete(fullPath);
|
|
11300
12303
|
const eventName = isDirectory ? EVENTS.UNLINK_DIR : EVENTS.UNLINK;
|
|
11301
|
-
if (wasTracked && !this._isIgnored(
|
|
11302
|
-
this._emit(eventName,
|
|
11303
|
-
this._closePath(
|
|
12304
|
+
if (wasTracked && !this._isIgnored(path24))
|
|
12305
|
+
this._emit(eventName, path24);
|
|
12306
|
+
this._closePath(path24);
|
|
11304
12307
|
}
|
|
11305
12308
|
/**
|
|
11306
12309
|
* Closes all watchers for a path
|
|
11307
12310
|
*/
|
|
11308
|
-
_closePath(
|
|
11309
|
-
this._closeFile(
|
|
11310
|
-
const dir = sp2.dirname(
|
|
11311
|
-
this._getWatchedDir(dir).remove(sp2.basename(
|
|
12311
|
+
_closePath(path24) {
|
|
12312
|
+
this._closeFile(path24);
|
|
12313
|
+
const dir = sp2.dirname(path24);
|
|
12314
|
+
this._getWatchedDir(dir).remove(sp2.basename(path24));
|
|
11312
12315
|
}
|
|
11313
12316
|
/**
|
|
11314
12317
|
* Closes only file-specific watchers
|
|
11315
12318
|
*/
|
|
11316
|
-
_closeFile(
|
|
11317
|
-
const closers = this._closers.get(
|
|
12319
|
+
_closeFile(path24) {
|
|
12320
|
+
const closers = this._closers.get(path24);
|
|
11318
12321
|
if (!closers)
|
|
11319
12322
|
return;
|
|
11320
12323
|
closers.forEach((closer) => closer());
|
|
11321
|
-
this._closers.delete(
|
|
12324
|
+
this._closers.delete(path24);
|
|
11322
12325
|
}
|
|
11323
|
-
_addPathCloser(
|
|
12326
|
+
_addPathCloser(path24, closer) {
|
|
11324
12327
|
if (!closer)
|
|
11325
12328
|
return;
|
|
11326
|
-
let list = this._closers.get(
|
|
12329
|
+
let list = this._closers.get(path24);
|
|
11327
12330
|
if (!list) {
|
|
11328
12331
|
list = [];
|
|
11329
|
-
this._closers.set(
|
|
12332
|
+
this._closers.set(path24, list);
|
|
11330
12333
|
}
|
|
11331
12334
|
list.push(closer);
|
|
11332
12335
|
}
|
|
@@ -11356,7 +12359,7 @@ function watch(paths, options = {}) {
|
|
|
11356
12359
|
var chokidar_default = { watch, FSWatcher };
|
|
11357
12360
|
|
|
11358
12361
|
// src/watcher/file-watcher.ts
|
|
11359
|
-
var
|
|
12362
|
+
var path17 = __toESM(require("path"), 1);
|
|
11360
12363
|
var FileWatcher = class {
|
|
11361
12364
|
watcher = null;
|
|
11362
12365
|
projectRoot;
|
|
@@ -11367,6 +12370,8 @@ var FileWatcher = class {
|
|
|
11367
12370
|
debounceTimer = null;
|
|
11368
12371
|
debounceMs = 1e3;
|
|
11369
12372
|
onChanges = null;
|
|
12373
|
+
readyPromise = null;
|
|
12374
|
+
resolveReady = null;
|
|
11370
12375
|
constructor(projectRoot, config, host = "opencode", options = {}) {
|
|
11371
12376
|
this.projectRoot = projectRoot;
|
|
11372
12377
|
this.config = config;
|
|
@@ -11382,15 +12387,15 @@ var FileWatcher = class {
|
|
|
11382
12387
|
const watchTargets = this.configPath ? [this.projectRoot, this.configPath] : this.projectRoot;
|
|
11383
12388
|
this.watcher = chokidar_default.watch(watchTargets, {
|
|
11384
12389
|
ignored: (filePath) => {
|
|
11385
|
-
const relativePath =
|
|
12390
|
+
const relativePath = path17.relative(this.projectRoot, filePath);
|
|
11386
12391
|
if (!relativePath) return false;
|
|
11387
12392
|
if (this.isProjectConfigPathOrAncestor(relativePath)) {
|
|
11388
12393
|
return false;
|
|
11389
12394
|
}
|
|
11390
|
-
if (hasFilteredPathSegment(relativePath,
|
|
12395
|
+
if (hasFilteredPathSegment(relativePath, path17.sep)) {
|
|
11391
12396
|
return true;
|
|
11392
12397
|
}
|
|
11393
|
-
if (isRestrictedDirectory(relativePath,
|
|
12398
|
+
if (isRestrictedDirectory(relativePath, path17.sep)) {
|
|
11394
12399
|
return true;
|
|
11395
12400
|
}
|
|
11396
12401
|
if (ignoreFilter.ignores(relativePath)) {
|
|
@@ -11405,6 +12410,13 @@ var FileWatcher = class {
|
|
|
11405
12410
|
pollInterval: 100
|
|
11406
12411
|
}
|
|
11407
12412
|
});
|
|
12413
|
+
this.readyPromise = new Promise((resolve13) => {
|
|
12414
|
+
this.resolveReady = resolve13;
|
|
12415
|
+
});
|
|
12416
|
+
this.watcher.once("ready", () => {
|
|
12417
|
+
this.resolveReady?.();
|
|
12418
|
+
this.resolveReady = null;
|
|
12419
|
+
});
|
|
11408
12420
|
this.watcher.on("error", (error) => {
|
|
11409
12421
|
const err = error instanceof Error ? error : null;
|
|
11410
12422
|
if (err?.code === "EPERM" || err?.code === "EACCES") {
|
|
@@ -11436,24 +12448,24 @@ var FileWatcher = class {
|
|
|
11436
12448
|
this.scheduleFlush();
|
|
11437
12449
|
}
|
|
11438
12450
|
isProjectConfigPath(filePath) {
|
|
11439
|
-
const relativePath =
|
|
11440
|
-
const normalizedRelativePath =
|
|
12451
|
+
const relativePath = path17.relative(this.projectRoot, filePath);
|
|
12452
|
+
const normalizedRelativePath = path17.normalize(relativePath);
|
|
11441
12453
|
return this.getProjectConfigRelativePaths().some((configPath) => configPath === normalizedRelativePath);
|
|
11442
12454
|
}
|
|
11443
12455
|
isProjectConfigPathOrAncestor(relativePath) {
|
|
11444
|
-
const normalizedRelativePath =
|
|
12456
|
+
const normalizedRelativePath = path17.normalize(relativePath);
|
|
11445
12457
|
return this.getProjectConfigRelativePaths().some(
|
|
11446
|
-
(configPath) => configPath === normalizedRelativePath || configPath.startsWith(`${normalizedRelativePath}${
|
|
12458
|
+
(configPath) => configPath === normalizedRelativePath || configPath.startsWith(`${normalizedRelativePath}${path17.sep}`)
|
|
11447
12459
|
);
|
|
11448
12460
|
}
|
|
11449
12461
|
getProjectConfigRelativePaths() {
|
|
11450
12462
|
if (this.configPath) {
|
|
11451
|
-
return [
|
|
12463
|
+
return [path17.normalize(path17.relative(this.projectRoot, this.configPath))];
|
|
11452
12464
|
}
|
|
11453
12465
|
return [
|
|
11454
12466
|
resolveProjectConfigPath(this.projectRoot, this.host),
|
|
11455
12467
|
resolveWritableProjectConfigPath(this.projectRoot, this.host)
|
|
11456
|
-
].map((configPath) =>
|
|
12468
|
+
].map((configPath) => path17.normalize(path17.relative(this.projectRoot, configPath)));
|
|
11457
12469
|
}
|
|
11458
12470
|
scheduleFlush() {
|
|
11459
12471
|
if (this.debounceTimer) {
|
|
@@ -11468,7 +12480,7 @@ var FileWatcher = class {
|
|
|
11468
12480
|
return;
|
|
11469
12481
|
}
|
|
11470
12482
|
const changes = Array.from(this.pendingChanges.entries()).map(
|
|
11471
|
-
([
|
|
12483
|
+
([path24, type]) => ({ path: path24, type })
|
|
11472
12484
|
);
|
|
11473
12485
|
this.pendingChanges.clear();
|
|
11474
12486
|
try {
|
|
@@ -11477,25 +12489,32 @@ var FileWatcher = class {
|
|
|
11477
12489
|
console.error("Error handling file changes:", error);
|
|
11478
12490
|
}
|
|
11479
12491
|
}
|
|
11480
|
-
stop() {
|
|
12492
|
+
async stop() {
|
|
11481
12493
|
if (this.debounceTimer) {
|
|
11482
12494
|
clearTimeout(this.debounceTimer);
|
|
11483
12495
|
this.debounceTimer = null;
|
|
11484
12496
|
}
|
|
11485
12497
|
if (this.watcher) {
|
|
11486
|
-
this.watcher
|
|
12498
|
+
const watcher = this.watcher;
|
|
11487
12499
|
this.watcher = null;
|
|
12500
|
+
await watcher.close();
|
|
11488
12501
|
}
|
|
12502
|
+
this.resolveReady?.();
|
|
12503
|
+
this.resolveReady = null;
|
|
12504
|
+
this.readyPromise = null;
|
|
11489
12505
|
this.pendingChanges.clear();
|
|
11490
12506
|
this.onChanges = null;
|
|
11491
12507
|
}
|
|
11492
12508
|
isRunning() {
|
|
11493
12509
|
return this.watcher !== null;
|
|
11494
12510
|
}
|
|
12511
|
+
async waitUntilReady() {
|
|
12512
|
+
await (this.readyPromise ?? Promise.resolve());
|
|
12513
|
+
}
|
|
11495
12514
|
};
|
|
11496
12515
|
|
|
11497
12516
|
// src/watcher/git-head-watcher.ts
|
|
11498
|
-
var
|
|
12517
|
+
var path18 = __toESM(require("path"), 1);
|
|
11499
12518
|
var GitHeadWatcher = class {
|
|
11500
12519
|
watcher = null;
|
|
11501
12520
|
projectRoot;
|
|
@@ -11517,7 +12536,7 @@ var GitHeadWatcher = class {
|
|
|
11517
12536
|
this.onBranchChange = handler;
|
|
11518
12537
|
this.currentBranch = getCurrentBranch(this.projectRoot);
|
|
11519
12538
|
const headPath = getHeadPath(this.projectRoot);
|
|
11520
|
-
const refsPath =
|
|
12539
|
+
const refsPath = path18.join(this.projectRoot, ".git", "refs", "heads");
|
|
11521
12540
|
this.watcher = chokidar_default.watch([headPath, refsPath], {
|
|
11522
12541
|
persistent: true,
|
|
11523
12542
|
ignoreInitial: true,
|
|
@@ -11554,14 +12573,15 @@ var GitHeadWatcher = class {
|
|
|
11554
12573
|
getCurrentBranch() {
|
|
11555
12574
|
return this.currentBranch;
|
|
11556
12575
|
}
|
|
11557
|
-
stop() {
|
|
12576
|
+
async stop() {
|
|
11558
12577
|
if (this.debounceTimer) {
|
|
11559
12578
|
clearTimeout(this.debounceTimer);
|
|
11560
12579
|
this.debounceTimer = null;
|
|
11561
12580
|
}
|
|
11562
12581
|
if (this.watcher) {
|
|
11563
|
-
this.watcher
|
|
12582
|
+
const watcher = this.watcher;
|
|
11564
12583
|
this.watcher = null;
|
|
12584
|
+
await watcher.close();
|
|
11565
12585
|
}
|
|
11566
12586
|
this.onBranchChange = null;
|
|
11567
12587
|
}
|
|
@@ -11579,6 +12599,8 @@ var BackgroundReindexer = class {
|
|
|
11579
12599
|
running = false;
|
|
11580
12600
|
pending = false;
|
|
11581
12601
|
stopped = false;
|
|
12602
|
+
retryTimer = null;
|
|
12603
|
+
retryDelayMs = 50;
|
|
11582
12604
|
request() {
|
|
11583
12605
|
if (this.stopped) {
|
|
11584
12606
|
return;
|
|
@@ -11589,9 +12611,13 @@ var BackgroundReindexer = class {
|
|
|
11589
12611
|
stop() {
|
|
11590
12612
|
this.stopped = true;
|
|
11591
12613
|
this.pending = false;
|
|
12614
|
+
if (this.retryTimer) {
|
|
12615
|
+
clearTimeout(this.retryTimer);
|
|
12616
|
+
this.retryTimer = null;
|
|
12617
|
+
}
|
|
11592
12618
|
}
|
|
11593
12619
|
drain() {
|
|
11594
|
-
if (this.stopped || this.running || !this.pending) {
|
|
12620
|
+
if (this.stopped || this.running || this.retryTimer || !this.pending) {
|
|
11595
12621
|
return;
|
|
11596
12622
|
}
|
|
11597
12623
|
this.pending = false;
|
|
@@ -11599,13 +12625,29 @@ var BackgroundReindexer = class {
|
|
|
11599
12625
|
void this.run();
|
|
11600
12626
|
}
|
|
11601
12627
|
async run() {
|
|
12628
|
+
let retryAfterContention = false;
|
|
11602
12629
|
try {
|
|
11603
12630
|
await this.runIndex();
|
|
12631
|
+
this.retryDelayMs = 50;
|
|
11604
12632
|
} catch (error) {
|
|
11605
|
-
|
|
12633
|
+
if (isTransientIndexLockContention(error)) {
|
|
12634
|
+
this.pending = true;
|
|
12635
|
+
retryAfterContention = true;
|
|
12636
|
+
} else {
|
|
12637
|
+
console.error("[codebase-index] Background reindex failed:", error);
|
|
12638
|
+
}
|
|
11606
12639
|
} finally {
|
|
11607
12640
|
this.running = false;
|
|
11608
|
-
this.
|
|
12641
|
+
if (retryAfterContention && !this.stopped) {
|
|
12642
|
+
const delay = this.retryDelayMs;
|
|
12643
|
+
this.retryDelayMs = Math.min(this.retryDelayMs * 2, 500);
|
|
12644
|
+
this.retryTimer = setTimeout(() => {
|
|
12645
|
+
this.retryTimer = null;
|
|
12646
|
+
this.drain();
|
|
12647
|
+
}, delay);
|
|
12648
|
+
} else {
|
|
12649
|
+
this.drain();
|
|
12650
|
+
}
|
|
11609
12651
|
}
|
|
11610
12652
|
}
|
|
11611
12653
|
};
|
|
@@ -11639,10 +12681,12 @@ function createWatcherWithIndexer(getIndexer, projectRoot, config, host = "openc
|
|
|
11639
12681
|
return {
|
|
11640
12682
|
fileWatcher,
|
|
11641
12683
|
gitWatcher,
|
|
11642
|
-
|
|
12684
|
+
whenReady() {
|
|
12685
|
+
return fileWatcher.waitUntilReady();
|
|
12686
|
+
},
|
|
12687
|
+
async stop() {
|
|
11643
12688
|
backgroundReindexer.stop();
|
|
11644
|
-
fileWatcher.stop();
|
|
11645
|
-
gitWatcher?.stop();
|
|
12689
|
+
await Promise.all([fileWatcher.stop(), gitWatcher?.stop()]);
|
|
11646
12690
|
}
|
|
11647
12691
|
};
|
|
11648
12692
|
}
|
|
@@ -11748,13 +12792,13 @@ var pr_impact = (0, import_plugin.tool)({
|
|
|
11748
12792
|
});
|
|
11749
12793
|
|
|
11750
12794
|
// src/tools/index.ts
|
|
11751
|
-
var
|
|
11752
|
-
var
|
|
11753
|
-
var
|
|
12795
|
+
var import_fs11 = require("fs");
|
|
12796
|
+
var os5 = __toESM(require("os"), 1);
|
|
12797
|
+
var path21 = __toESM(require("path"), 1);
|
|
11754
12798
|
|
|
11755
12799
|
// src/tools/visualize/activity.ts
|
|
11756
12800
|
var import_child_process4 = require("child_process");
|
|
11757
|
-
var
|
|
12801
|
+
var path19 = __toESM(require("path"), 1);
|
|
11758
12802
|
function attachRecentActivity(data, projectRoot) {
|
|
11759
12803
|
const activity = readGitActivity(projectRoot);
|
|
11760
12804
|
const changes = activity.size > 0 ? buildGitChanges(data, activity, projectRoot) : buildGraphChanges(data);
|
|
@@ -11916,7 +12960,7 @@ function normalizePath2(filePath) {
|
|
|
11916
12960
|
return filePath.replace(/\\/g, "/");
|
|
11917
12961
|
}
|
|
11918
12962
|
function toGitRelativePath(projectRoot, filePath) {
|
|
11919
|
-
const relativePath =
|
|
12963
|
+
const relativePath = path19.isAbsolute(filePath) ? path19.relative(projectRoot, filePath) : filePath;
|
|
11920
12964
|
return normalizePath2(relativePath);
|
|
11921
12965
|
}
|
|
11922
12966
|
|
|
@@ -12174,7 +13218,7 @@ render();
|
|
|
12174
13218
|
}
|
|
12175
13219
|
|
|
12176
13220
|
// src/tools/visualize/transform.ts
|
|
12177
|
-
var
|
|
13221
|
+
var path20 = __toESM(require("path"), 1);
|
|
12178
13222
|
|
|
12179
13223
|
// src/tools/visualize/modules.ts
|
|
12180
13224
|
var MAX_MODULES = 18;
|
|
@@ -12434,7 +13478,7 @@ function transformForVisualization(symbols, edges, options = {}) {
|
|
|
12434
13478
|
filePath: s.filePath,
|
|
12435
13479
|
kind: s.kind,
|
|
12436
13480
|
line: s.startLine,
|
|
12437
|
-
directory:
|
|
13481
|
+
directory: path20.dirname(s.filePath),
|
|
12438
13482
|
moduleId: "",
|
|
12439
13483
|
moduleLabel: ""
|
|
12440
13484
|
}));
|
|
@@ -12515,7 +13559,9 @@ var index_codebase = (0, import_plugin2.tool)({
|
|
|
12515
13559
|
const result = await runIndexCodebase(context?.worktree, DEFAULT_HOST, args, (title, metadata) => {
|
|
12516
13560
|
context.metadata({ title, metadata });
|
|
12517
13561
|
});
|
|
12518
|
-
|
|
13562
|
+
if (result.kind === "estimate") return formatCostEstimate(result.estimate);
|
|
13563
|
+
if (result.kind === "busy") return result.text;
|
|
13564
|
+
return formatIndexStats(result.stats, args.verbose ?? false);
|
|
12519
13565
|
}
|
|
12520
13566
|
});
|
|
12521
13567
|
var index_status = (0, import_plugin2.tool)({
|
|
@@ -12529,7 +13575,9 @@ var index_health_check = (0, import_plugin2.tool)({
|
|
|
12529
13575
|
description: "Check index health and remove stale entries from deleted files. Run this to clean up the index after files have been deleted.",
|
|
12530
13576
|
args: {},
|
|
12531
13577
|
async execute(_args, context) {
|
|
12532
|
-
|
|
13578
|
+
const result = await runIndexHealthCheck(context?.worktree, DEFAULT_HOST);
|
|
13579
|
+
if (result.kind === "busy") return result.text;
|
|
13580
|
+
return formatHealthCheck(result.health);
|
|
12533
13581
|
}
|
|
12534
13582
|
});
|
|
12535
13583
|
var index_metrics = (0, import_plugin2.tool)({
|
|
@@ -12649,8 +13697,8 @@ var call_graph_path = (0, import_plugin2.tool)({
|
|
|
12649
13697
|
maxDepth: z2.number().optional().default(10).describe("Maximum traversal depth (default: 10)")
|
|
12650
13698
|
},
|
|
12651
13699
|
async execute(args, context) {
|
|
12652
|
-
const
|
|
12653
|
-
return formatCallGraphPath(args.from, args.to,
|
|
13700
|
+
const path24 = await getCallGraphPath(context?.worktree, DEFAULT_HOST, args.from, args.to, args.maxDepth);
|
|
13701
|
+
return formatCallGraphPath(args.from, args.to, path24);
|
|
12654
13702
|
}
|
|
12655
13703
|
});
|
|
12656
13704
|
var add_knowledge_base = (0, import_plugin2.tool)({
|
|
@@ -12703,8 +13751,8 @@ var index_visualize = (0, import_plugin2.tool)({
|
|
|
12703
13751
|
return "No connected symbols found for visualization. Try including orphans with includeOrphans=true, or check that the call graph has resolved edges.";
|
|
12704
13752
|
}
|
|
12705
13753
|
const html = generateVisualizationHtml(vizData);
|
|
12706
|
-
const outputPath =
|
|
12707
|
-
(0,
|
|
13754
|
+
const outputPath = path21.join(os5.tmpdir(), `call-graph-${Date.now()}.html`);
|
|
13755
|
+
(0, import_fs11.writeFileSync)(outputPath, html, "utf-8");
|
|
12708
13756
|
let result = `Temporal call graph visualization generated: ${outputPath}
|
|
12709
13757
|
|
|
12710
13758
|
`;
|
|
@@ -12725,8 +13773,8 @@ var index_visualize = (0, import_plugin2.tool)({
|
|
|
12725
13773
|
});
|
|
12726
13774
|
|
|
12727
13775
|
// src/commands/loader.ts
|
|
12728
|
-
var
|
|
12729
|
-
var
|
|
13776
|
+
var import_fs12 = require("fs");
|
|
13777
|
+
var path22 = __toESM(require("path"), 1);
|
|
12730
13778
|
function parseFrontmatter(content) {
|
|
12731
13779
|
const frontmatterRegex = /^---\s*\n([\s\S]*?)\n---\s*\n([\s\S]*)$/;
|
|
12732
13780
|
const match = content.match(frontmatterRegex);
|
|
@@ -12747,21 +13795,21 @@ function parseFrontmatter(content) {
|
|
|
12747
13795
|
}
|
|
12748
13796
|
function loadCommandsFromDirectory(commandsDir) {
|
|
12749
13797
|
const commands = /* @__PURE__ */ new Map();
|
|
12750
|
-
if (!(0,
|
|
13798
|
+
if (!(0, import_fs12.existsSync)(commandsDir)) {
|
|
12751
13799
|
return commands;
|
|
12752
13800
|
}
|
|
12753
|
-
const files = (0,
|
|
13801
|
+
const files = (0, import_fs12.readdirSync)(commandsDir).filter((f) => f.endsWith(".md"));
|
|
12754
13802
|
for (const file of files) {
|
|
12755
|
-
const filePath =
|
|
13803
|
+
const filePath = path22.join(commandsDir, file);
|
|
12756
13804
|
let content;
|
|
12757
13805
|
try {
|
|
12758
|
-
content = (0,
|
|
13806
|
+
content = (0, import_fs12.readFileSync)(filePath, "utf-8");
|
|
12759
13807
|
} catch (error) {
|
|
12760
13808
|
const message = error instanceof Error ? error.message : String(error);
|
|
12761
13809
|
throw new Error(`Failed to load command file ${filePath}: ${message}`);
|
|
12762
13810
|
}
|
|
12763
13811
|
const { frontmatter, body } = parseFrontmatter(content);
|
|
12764
|
-
const name =
|
|
13812
|
+
const name = path22.basename(file, ".md");
|
|
12765
13813
|
const description = frontmatter.description || `Run the ${name} command`;
|
|
12766
13814
|
commands.set(name, {
|
|
12767
13815
|
description,
|
|
@@ -13065,18 +14113,37 @@ var RoutingHintController = class {
|
|
|
13065
14113
|
};
|
|
13066
14114
|
|
|
13067
14115
|
// src/utils/auto-index.ts
|
|
14116
|
+
var INITIAL_RETRY_DELAY_MS = 50;
|
|
14117
|
+
var MAX_RETRY_DELAY_MS = 500;
|
|
14118
|
+
var autoIndexStates = /* @__PURE__ */ new WeakMap();
|
|
13068
14119
|
function getErrorMessage3(error) {
|
|
13069
14120
|
return error instanceof Error ? error.message : String(error);
|
|
13070
14121
|
}
|
|
13071
|
-
function
|
|
13072
|
-
indexer.
|
|
13073
|
-
|
|
13074
|
-
console.error(`[codebase-index] Auto-index failed for "${projectRoot}": ${getErrorMessage3(error)}`);
|
|
13075
|
-
});
|
|
14122
|
+
function runAutoIndex(indexer, projectRoot, state) {
|
|
14123
|
+
void indexer.index().then(() => {
|
|
14124
|
+
autoIndexStates.delete(indexer);
|
|
13076
14125
|
}).catch((error) => {
|
|
13077
|
-
|
|
14126
|
+
if (!isTransientIndexLockContention(error)) {
|
|
14127
|
+
autoIndexStates.delete(indexer);
|
|
14128
|
+
console.error(`[codebase-index] Auto-index failed for "${projectRoot}": ${getErrorMessage3(error)}`);
|
|
14129
|
+
return;
|
|
14130
|
+
}
|
|
14131
|
+
const retryDelayMs = state.retryDelayMs;
|
|
14132
|
+
state.retryDelayMs = Math.min(retryDelayMs * 2, MAX_RETRY_DELAY_MS);
|
|
14133
|
+
const retryTimer = setTimeout(() => {
|
|
14134
|
+
runAutoIndex(indexer, projectRoot, state);
|
|
14135
|
+
}, retryDelayMs);
|
|
14136
|
+
retryTimer.unref?.();
|
|
13078
14137
|
});
|
|
13079
14138
|
}
|
|
14139
|
+
function startAutoIndex(indexer, projectRoot) {
|
|
14140
|
+
if (autoIndexStates.has(indexer)) return;
|
|
14141
|
+
const state = {
|
|
14142
|
+
retryDelayMs: INITIAL_RETRY_DELAY_MS
|
|
14143
|
+
};
|
|
14144
|
+
autoIndexStates.set(indexer, state);
|
|
14145
|
+
runAutoIndex(indexer, projectRoot, state);
|
|
14146
|
+
}
|
|
13080
14147
|
|
|
13081
14148
|
// src/index.ts
|
|
13082
14149
|
var import_meta2 = {};
|
|
@@ -13094,9 +14161,9 @@ function replaceActiveWatcher(projectRoot, nextWatcher) {
|
|
|
13094
14161
|
function getCommandsDir() {
|
|
13095
14162
|
let currentDir = process.cwd();
|
|
13096
14163
|
if (typeof import_meta2 !== "undefined" && import_meta2.url) {
|
|
13097
|
-
currentDir =
|
|
14164
|
+
currentDir = path23.dirname((0, import_url2.fileURLToPath)(import_meta2.url));
|
|
13098
14165
|
}
|
|
13099
|
-
return
|
|
14166
|
+
return path23.join(currentDir, "..", "commands");
|
|
13100
14167
|
}
|
|
13101
14168
|
function appendRoutingHints(output, hints, preferredRole) {
|
|
13102
14169
|
const preferredBucket = preferredRole === "developer" ? output.developer : output.system;
|
|
@@ -13116,7 +14183,7 @@ var plugin = async ({ directory, worktree }) => {
|
|
|
13116
14183
|
initializeTools2(projectRoot, config);
|
|
13117
14184
|
const getProjectIndexer = () => getIndexerForProject2(projectRoot);
|
|
13118
14185
|
const routingHints = config.search.routingHints ? new RoutingHintController(() => getProjectIndexer().getStatus(), 200, config.search.routingGraphHandoffHints) : null;
|
|
13119
|
-
const isHomeDir =
|
|
14186
|
+
const isHomeDir = path23.resolve(projectRoot) === path23.resolve(os6.homedir());
|
|
13120
14187
|
const isValidProject = !isHomeDir && (!config.indexing.requireProjectMarker || hasProjectMarker(projectRoot));
|
|
13121
14188
|
if (isHomeDir) {
|
|
13122
14189
|
console.warn(
|