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