opencode-codebase-index 0.14.0 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +40 -4
- package/dist/cli.cjs +1639 -557
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +1638 -546
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +1598 -531
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1594 -517
- package/dist/index.js.map +1 -1
- package/dist/pi-extension.cjs +1305 -303
- package/dist/pi-extension.cjs.map +1 -1
- package/dist/pi-extension.js +1304 -292
- package/dist/pi-extension.js.map +1 -1
- package/native/codebase-index-native.darwin-arm64.node +0 -0
- package/native/codebase-index-native.darwin-x64.node +0 -0
- package/native/codebase-index-native.linux-arm64-gnu.node +0 -0
- package/native/codebase-index-native.linux-x64-gnu.node +0 -0
- package/native/codebase-index-native.win32-x64-msvc.node +0 -0
- package/package.json +11 -8
package/dist/cli.cjs
CHANGED
|
@@ -496,7 +496,7 @@ var require_ignore = __commonJS({
|
|
|
496
496
|
// path matching.
|
|
497
497
|
// - check `string` either `MODE_IGNORE` or `MODE_CHECK_IGNORE`
|
|
498
498
|
// @returns {TestResult} true if a file is ignored
|
|
499
|
-
test(
|
|
499
|
+
test(path27, checkUnignored, mode) {
|
|
500
500
|
let ignored = false;
|
|
501
501
|
let unignored = false;
|
|
502
502
|
let matchedRule;
|
|
@@ -505,7 +505,7 @@ var require_ignore = __commonJS({
|
|
|
505
505
|
if (unignored === negative && ignored !== unignored || negative && !ignored && !unignored && !checkUnignored) {
|
|
506
506
|
return;
|
|
507
507
|
}
|
|
508
|
-
const matched = rule[mode].test(
|
|
508
|
+
const matched = rule[mode].test(path27);
|
|
509
509
|
if (!matched) {
|
|
510
510
|
return;
|
|
511
511
|
}
|
|
@@ -526,17 +526,17 @@ var require_ignore = __commonJS({
|
|
|
526
526
|
var throwError = (message, Ctor) => {
|
|
527
527
|
throw new Ctor(message);
|
|
528
528
|
};
|
|
529
|
-
var checkPath = (
|
|
530
|
-
if (!isString(
|
|
529
|
+
var checkPath = (path27, originalPath, doThrow) => {
|
|
530
|
+
if (!isString(path27)) {
|
|
531
531
|
return doThrow(
|
|
532
532
|
`path must be a string, but got \`${originalPath}\``,
|
|
533
533
|
TypeError
|
|
534
534
|
);
|
|
535
535
|
}
|
|
536
|
-
if (!
|
|
536
|
+
if (!path27) {
|
|
537
537
|
return doThrow(`path must not be empty`, TypeError);
|
|
538
538
|
}
|
|
539
|
-
if (checkPath.isNotRelative(
|
|
539
|
+
if (checkPath.isNotRelative(path27)) {
|
|
540
540
|
const r = "`path.relative()`d";
|
|
541
541
|
return doThrow(
|
|
542
542
|
`path should be a ${r} string, but got "${originalPath}"`,
|
|
@@ -545,7 +545,7 @@ var require_ignore = __commonJS({
|
|
|
545
545
|
}
|
|
546
546
|
return true;
|
|
547
547
|
};
|
|
548
|
-
var isNotRelative = (
|
|
548
|
+
var isNotRelative = (path27) => REGEX_TEST_INVALID_PATH.test(path27);
|
|
549
549
|
checkPath.isNotRelative = isNotRelative;
|
|
550
550
|
checkPath.convert = (p) => p;
|
|
551
551
|
var Ignore2 = class {
|
|
@@ -575,19 +575,19 @@ var require_ignore = __commonJS({
|
|
|
575
575
|
}
|
|
576
576
|
// @returns {TestResult}
|
|
577
577
|
_test(originalPath, cache, checkUnignored, slices) {
|
|
578
|
-
const
|
|
578
|
+
const path27 = originalPath && checkPath.convert(originalPath);
|
|
579
579
|
checkPath(
|
|
580
|
-
|
|
580
|
+
path27,
|
|
581
581
|
originalPath,
|
|
582
582
|
this._strictPathCheck ? throwError : RETURN_FALSE
|
|
583
583
|
);
|
|
584
|
-
return this._t(
|
|
584
|
+
return this._t(path27, cache, checkUnignored, slices);
|
|
585
585
|
}
|
|
586
|
-
checkIgnore(
|
|
587
|
-
if (!REGEX_TEST_TRAILING_SLASH.test(
|
|
588
|
-
return this.test(
|
|
586
|
+
checkIgnore(path27) {
|
|
587
|
+
if (!REGEX_TEST_TRAILING_SLASH.test(path27)) {
|
|
588
|
+
return this.test(path27);
|
|
589
589
|
}
|
|
590
|
-
const slices =
|
|
590
|
+
const slices = path27.split(SLASH2).filter(Boolean);
|
|
591
591
|
slices.pop();
|
|
592
592
|
if (slices.length) {
|
|
593
593
|
const parent = this._t(
|
|
@@ -600,18 +600,18 @@ var require_ignore = __commonJS({
|
|
|
600
600
|
return parent;
|
|
601
601
|
}
|
|
602
602
|
}
|
|
603
|
-
return this._rules.test(
|
|
603
|
+
return this._rules.test(path27, false, MODE_CHECK_IGNORE);
|
|
604
604
|
}
|
|
605
|
-
_t(
|
|
606
|
-
if (
|
|
607
|
-
return cache[
|
|
605
|
+
_t(path27, cache, checkUnignored, slices) {
|
|
606
|
+
if (path27 in cache) {
|
|
607
|
+
return cache[path27];
|
|
608
608
|
}
|
|
609
609
|
if (!slices) {
|
|
610
|
-
slices =
|
|
610
|
+
slices = path27.split(SLASH2).filter(Boolean);
|
|
611
611
|
}
|
|
612
612
|
slices.pop();
|
|
613
613
|
if (!slices.length) {
|
|
614
|
-
return cache[
|
|
614
|
+
return cache[path27] = this._rules.test(path27, checkUnignored, MODE_IGNORE);
|
|
615
615
|
}
|
|
616
616
|
const parent = this._t(
|
|
617
617
|
slices.join(SLASH2) + SLASH2,
|
|
@@ -619,29 +619,29 @@ var require_ignore = __commonJS({
|
|
|
619
619
|
checkUnignored,
|
|
620
620
|
slices
|
|
621
621
|
);
|
|
622
|
-
return cache[
|
|
622
|
+
return cache[path27] = parent.ignored ? parent : this._rules.test(path27, checkUnignored, MODE_IGNORE);
|
|
623
623
|
}
|
|
624
|
-
ignores(
|
|
625
|
-
return this._test(
|
|
624
|
+
ignores(path27) {
|
|
625
|
+
return this._test(path27, this._ignoreCache, false).ignored;
|
|
626
626
|
}
|
|
627
627
|
createFilter() {
|
|
628
|
-
return (
|
|
628
|
+
return (path27) => !this.ignores(path27);
|
|
629
629
|
}
|
|
630
630
|
filter(paths) {
|
|
631
631
|
return makeArray(paths).filter(this.createFilter());
|
|
632
632
|
}
|
|
633
633
|
// @returns {TestResult}
|
|
634
|
-
test(
|
|
635
|
-
return this._test(
|
|
634
|
+
test(path27) {
|
|
635
|
+
return this._test(path27, this._testCache, true);
|
|
636
636
|
}
|
|
637
637
|
};
|
|
638
638
|
var factory = (options) => new Ignore2(options);
|
|
639
|
-
var isPathValid = (
|
|
639
|
+
var isPathValid = (path27) => checkPath(path27 && checkPath.convert(path27), path27, RETURN_FALSE);
|
|
640
640
|
var setupWindows = () => {
|
|
641
641
|
const makePosix = (str) => /^\\\\\?\\/.test(str) || /["<>|\u0000-\u001F]+/u.test(str) ? str : str.replace(/\\/g, "/");
|
|
642
642
|
checkPath.convert = makePosix;
|
|
643
643
|
const REGEX_TEST_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i;
|
|
644
|
-
checkPath.isNotRelative = (
|
|
644
|
+
checkPath.isNotRelative = (path27) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path27) || isNotRelative(path27);
|
|
645
645
|
};
|
|
646
646
|
if (
|
|
647
647
|
// Detect `process` so that it can run in browsers.
|
|
@@ -665,17 +665,17 @@ __export(cli_exports, {
|
|
|
665
665
|
});
|
|
666
666
|
module.exports = __toCommonJS(cli_exports);
|
|
667
667
|
var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
668
|
-
var
|
|
669
|
-
var
|
|
670
|
-
var
|
|
668
|
+
var import_fs16 = require("fs");
|
|
669
|
+
var os6 = __toESM(require("os"), 1);
|
|
670
|
+
var path26 = __toESM(require("path"), 1);
|
|
671
671
|
var import_url2 = require("url");
|
|
672
672
|
|
|
673
673
|
// src/config/constants.ts
|
|
674
674
|
var DEFAULT_INCLUDE = [
|
|
675
|
-
"**/*.{ts,tsx,js,jsx,mjs,cjs}",
|
|
675
|
+
"**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}",
|
|
676
676
|
"**/*.{py,pyi}",
|
|
677
|
-
"**/*.{go,rs,java,kt,scala}",
|
|
678
|
-
"**/*.{c,cpp,cc,h,hpp}",
|
|
677
|
+
"**/*.{go,rs,java,cs,kt,scala}",
|
|
678
|
+
"**/*.{c,cpp,cc,cxx,h,hpp,hxx}",
|
|
679
679
|
"**/*.{rb,php,inc,swift}",
|
|
680
680
|
"**/*.{cls,trigger}",
|
|
681
681
|
"**/*.{vue,svelte,astro}",
|
|
@@ -1058,7 +1058,7 @@ var autoDetectProviders = AUTO_DETECT_PROVIDER_ORDER.filter(
|
|
|
1058
1058
|
);
|
|
1059
1059
|
|
|
1060
1060
|
// src/config/host.ts
|
|
1061
|
-
var HOST_MODES = ["opencode", "codex", "claude", "pi"];
|
|
1061
|
+
var HOST_MODES = ["opencode", "codex", "claude", "pi", "jcode"];
|
|
1062
1062
|
function isSupportedHostMode(value) {
|
|
1063
1063
|
return HOST_MODES.includes(value);
|
|
1064
1064
|
}
|
|
@@ -1113,9 +1113,9 @@ var import_fs = require("fs");
|
|
|
1113
1113
|
var path = __toESM(require("path"), 1);
|
|
1114
1114
|
|
|
1115
1115
|
// src/eval/report-formatters.ts
|
|
1116
|
-
function assertFiniteNumber(value,
|
|
1116
|
+
function assertFiniteNumber(value, path27) {
|
|
1117
1117
|
if (typeof value !== "number" || Number.isNaN(value) || !Number.isFinite(value)) {
|
|
1118
|
-
throw new Error(`${
|
|
1118
|
+
throw new Error(`${path27} must be a finite number`);
|
|
1119
1119
|
}
|
|
1120
1120
|
return value;
|
|
1121
1121
|
}
|
|
@@ -1303,13 +1303,13 @@ function buildPerQueryArtifact(perQuery) {
|
|
|
1303
1303
|
}
|
|
1304
1304
|
|
|
1305
1305
|
// src/eval/runner.ts
|
|
1306
|
-
var
|
|
1307
|
-
var
|
|
1306
|
+
var import_fs11 = require("fs");
|
|
1307
|
+
var path15 = __toESM(require("path"), 1);
|
|
1308
1308
|
var import_perf_hooks2 = require("perf_hooks");
|
|
1309
1309
|
|
|
1310
1310
|
// src/indexer/index.ts
|
|
1311
|
-
var
|
|
1312
|
-
var
|
|
1311
|
+
var import_fs8 = require("fs");
|
|
1312
|
+
var path12 = __toESM(require("path"), 1);
|
|
1313
1313
|
var import_perf_hooks = require("perf_hooks");
|
|
1314
1314
|
var import_child_process3 = require("child_process");
|
|
1315
1315
|
var import_util3 = require("util");
|
|
@@ -2591,17 +2591,17 @@ function validateExternalUrl(urlString) {
|
|
|
2591
2591
|
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
2592
2592
|
return { valid: false, reason: `Blocked protocol: ${parsed.protocol}` };
|
|
2593
2593
|
}
|
|
2594
|
-
const
|
|
2595
|
-
if (BLOCKED_HOSTNAMES.has(
|
|
2596
|
-
return { valid: false, reason: `Blocked: cloud metadata service (${
|
|
2594
|
+
const hostname2 = parsed.hostname.toLowerCase();
|
|
2595
|
+
if (BLOCKED_HOSTNAMES.has(hostname2)) {
|
|
2596
|
+
return { valid: false, reason: `Blocked: cloud metadata service (${hostname2})` };
|
|
2597
2597
|
}
|
|
2598
2598
|
for (const pattern of BLOCKED_METADATA_IPS) {
|
|
2599
|
-
if (pattern.test(
|
|
2600
|
-
return { valid: false, reason: `Blocked: cloud metadata IP (${
|
|
2599
|
+
if (pattern.test(hostname2)) {
|
|
2600
|
+
return { valid: false, reason: `Blocked: cloud metadata IP (${hostname2})` };
|
|
2601
2601
|
}
|
|
2602
2602
|
}
|
|
2603
|
-
if (/^169\.254\./.test(
|
|
2604
|
-
return { valid: false, reason: `Blocked: link-local address (${
|
|
2603
|
+
if (/^169\.254\./.test(hostname2)) {
|
|
2604
|
+
return { valid: false, reason: `Blocked: link-local address (${hostname2})` };
|
|
2605
2605
|
}
|
|
2606
2606
|
return { valid: true };
|
|
2607
2607
|
}
|
|
@@ -3792,6 +3792,12 @@ function createMockNativeBinding() {
|
|
|
3792
3792
|
constructor() {
|
|
3793
3793
|
throw error;
|
|
3794
3794
|
}
|
|
3795
|
+
static openReadOnly() {
|
|
3796
|
+
throw error;
|
|
3797
|
+
}
|
|
3798
|
+
static createEmptyReadOnly() {
|
|
3799
|
+
throw error;
|
|
3800
|
+
}
|
|
3795
3801
|
close() {
|
|
3796
3802
|
throw error;
|
|
3797
3803
|
}
|
|
@@ -3895,6 +3901,12 @@ var VectorStore = class {
|
|
|
3895
3901
|
load() {
|
|
3896
3902
|
this.inner.load();
|
|
3897
3903
|
}
|
|
3904
|
+
loadStrict() {
|
|
3905
|
+
this.inner.loadStrict();
|
|
3906
|
+
}
|
|
3907
|
+
hasFingerprint() {
|
|
3908
|
+
return this.inner.hasFingerprint();
|
|
3909
|
+
}
|
|
3898
3910
|
count() {
|
|
3899
3911
|
return this.inner.count();
|
|
3900
3912
|
}
|
|
@@ -4177,12 +4189,24 @@ var InvertedIndex = class {
|
|
|
4177
4189
|
return this.inner.documentCount();
|
|
4178
4190
|
}
|
|
4179
4191
|
};
|
|
4180
|
-
var Database = class {
|
|
4192
|
+
var Database = class _Database {
|
|
4181
4193
|
inner;
|
|
4182
4194
|
closed = false;
|
|
4183
4195
|
constructor(dbPath) {
|
|
4184
4196
|
this.inner = new native.Database(dbPath);
|
|
4185
4197
|
}
|
|
4198
|
+
static fromNative(inner) {
|
|
4199
|
+
const database = Object.create(_Database.prototype);
|
|
4200
|
+
database.inner = inner;
|
|
4201
|
+
database.closed = false;
|
|
4202
|
+
return database;
|
|
4203
|
+
}
|
|
4204
|
+
static openReadOnly(dbPath) {
|
|
4205
|
+
return _Database.fromNative(native.Database.openReadOnly(dbPath));
|
|
4206
|
+
}
|
|
4207
|
+
static createEmptyReadOnly() {
|
|
4208
|
+
return _Database.fromNative(native.Database.createEmptyReadOnly());
|
|
4209
|
+
}
|
|
4186
4210
|
throwIfClosed() {
|
|
4187
4211
|
if (this.closed) {
|
|
4188
4212
|
throw new Error("Database is closed");
|
|
@@ -4651,6 +4675,9 @@ function getHostProjectIndexRelativePath(host) {
|
|
|
4651
4675
|
function hasHostProjectConfig(projectRoot, host) {
|
|
4652
4676
|
return (0, import_fs6.existsSync)(path8.join(projectRoot, getProjectConfigRelativePath(host)));
|
|
4653
4677
|
}
|
|
4678
|
+
function hasHostGlobalConfig(host) {
|
|
4679
|
+
return (0, import_fs6.existsSync)(getGlobalConfigPath(host));
|
|
4680
|
+
}
|
|
4654
4681
|
function getGlobalIndexPath(host = "opencode") {
|
|
4655
4682
|
switch (host) {
|
|
4656
4683
|
case "opencode":
|
|
@@ -4690,6 +4717,9 @@ function resolveGlobalIndexPath(host = "opencode") {
|
|
|
4690
4717
|
return hostIndexPath;
|
|
4691
4718
|
}
|
|
4692
4719
|
if (host !== "opencode") {
|
|
4720
|
+
if (hasHostGlobalConfig(host)) {
|
|
4721
|
+
return hostIndexPath;
|
|
4722
|
+
}
|
|
4693
4723
|
const legacyIndexPath = getGlobalIndexPath("opencode");
|
|
4694
4724
|
if ((0, import_fs6.existsSync)(legacyIndexPath)) {
|
|
4695
4725
|
return legacyIndexPath;
|
|
@@ -4906,6 +4936,422 @@ async function getChunkGitBlame(projectRoot, filePath, startLine, endLine) {
|
|
|
4906
4936
|
}
|
|
4907
4937
|
}
|
|
4908
4938
|
|
|
4939
|
+
// src/indexer/index-lock.ts
|
|
4940
|
+
var import_crypto = require("crypto");
|
|
4941
|
+
var import_fs7 = require("fs");
|
|
4942
|
+
var os4 = __toESM(require("os"), 1);
|
|
4943
|
+
var path11 = __toESM(require("path"), 1);
|
|
4944
|
+
var OWNER_FILE_NAME = "owner.json";
|
|
4945
|
+
var RECLAIM_DIRECTORY_NAME = "reclaiming";
|
|
4946
|
+
var RECOVERY_MARKER_PREFIX = "indexing.lock.recovery.";
|
|
4947
|
+
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;
|
|
4948
|
+
var VALID_OPERATIONS = /* @__PURE__ */ new Set([
|
|
4949
|
+
"initialize",
|
|
4950
|
+
"index",
|
|
4951
|
+
"force-index",
|
|
4952
|
+
"clear",
|
|
4953
|
+
"health-check",
|
|
4954
|
+
"retry-failed-batches",
|
|
4955
|
+
"recovery"
|
|
4956
|
+
]);
|
|
4957
|
+
var temporaryCounter = 0;
|
|
4958
|
+
function getErrorCode(error) {
|
|
4959
|
+
return typeof error === "object" && error !== null && "code" in error ? String(error.code) : void 0;
|
|
4960
|
+
}
|
|
4961
|
+
function retryTransientFilesystemOperation(operation) {
|
|
4962
|
+
let lastError;
|
|
4963
|
+
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
4964
|
+
try {
|
|
4965
|
+
operation();
|
|
4966
|
+
return;
|
|
4967
|
+
} catch (error) {
|
|
4968
|
+
lastError = error;
|
|
4969
|
+
const code = getErrorCode(error);
|
|
4970
|
+
if (code !== "EBUSY" && code !== "EPERM") throw error;
|
|
4971
|
+
if (attempt < 2) {
|
|
4972
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, (attempt + 1) * 10);
|
|
4973
|
+
}
|
|
4974
|
+
}
|
|
4975
|
+
}
|
|
4976
|
+
throw lastError;
|
|
4977
|
+
}
|
|
4978
|
+
function parseOwner(value) {
|
|
4979
|
+
if (typeof value !== "object" || value === null) return null;
|
|
4980
|
+
const candidate = value;
|
|
4981
|
+
if (!Number.isInteger(candidate.pid) || (candidate.pid ?? 0) <= 0) return null;
|
|
4982
|
+
if (typeof candidate.hostname !== "string" || candidate.hostname.length === 0) return null;
|
|
4983
|
+
if (typeof candidate.startedAt !== "string" || Number.isNaN(Date.parse(candidate.startedAt))) return null;
|
|
4984
|
+
if (typeof candidate.operation !== "string" || !VALID_OPERATIONS.has(candidate.operation)) return null;
|
|
4985
|
+
if (typeof candidate.token !== "string" || !UUID_PATTERN.test(candidate.token)) return null;
|
|
4986
|
+
return candidate;
|
|
4987
|
+
}
|
|
4988
|
+
function parseReclaimOwner(value) {
|
|
4989
|
+
if (typeof value !== "object" || value === null) return null;
|
|
4990
|
+
const candidate = value;
|
|
4991
|
+
if (!Number.isInteger(candidate.pid) || (candidate.pid ?? 0) <= 0) return null;
|
|
4992
|
+
if (typeof candidate.hostname !== "string" || candidate.hostname.length === 0) return null;
|
|
4993
|
+
if (typeof candidate.startedAt !== "string" || Number.isNaN(Date.parse(candidate.startedAt))) return null;
|
|
4994
|
+
if (typeof candidate.token !== "string" || !UUID_PATTERN.test(candidate.token)) return null;
|
|
4995
|
+
if (typeof candidate.expectedOwnerToken !== "string" || !UUID_PATTERN.test(candidate.expectedOwnerToken)) return null;
|
|
4996
|
+
return candidate;
|
|
4997
|
+
}
|
|
4998
|
+
function readJsonDirectory(directoryPath, parser) {
|
|
4999
|
+
try {
|
|
5000
|
+
return parser(JSON.parse((0, import_fs7.readFileSync)(path11.join(directoryPath, OWNER_FILE_NAME), "utf-8")));
|
|
5001
|
+
} catch {
|
|
5002
|
+
return null;
|
|
5003
|
+
}
|
|
5004
|
+
}
|
|
5005
|
+
function readDirectoryOwner(lockPath) {
|
|
5006
|
+
return readJsonDirectory(lockPath, parseOwner);
|
|
5007
|
+
}
|
|
5008
|
+
function readReclaimOwner(markerPath) {
|
|
5009
|
+
return readJsonDirectory(markerPath, parseReclaimOwner);
|
|
5010
|
+
}
|
|
5011
|
+
function readRecoveryOwner(markerPath) {
|
|
5012
|
+
return readDirectoryOwner(markerPath);
|
|
5013
|
+
}
|
|
5014
|
+
function readLegacyOwner(lockPath) {
|
|
5015
|
+
try {
|
|
5016
|
+
const parsed = JSON.parse((0, import_fs7.readFileSync)(lockPath, "utf-8"));
|
|
5017
|
+
if (!Number.isInteger(parsed.pid) || Number(parsed.pid) <= 0) return null;
|
|
5018
|
+
if (typeof parsed.startedAt !== "string" || Number.isNaN(Date.parse(parsed.startedAt))) return null;
|
|
5019
|
+
return {
|
|
5020
|
+
pid: Number(parsed.pid),
|
|
5021
|
+
hostname: typeof parsed.hostname === "string" ? parsed.hostname : os4.hostname(),
|
|
5022
|
+
startedAt: parsed.startedAt,
|
|
5023
|
+
operation: typeof parsed.operation === "string" && VALID_OPERATIONS.has(parsed.operation) ? parsed.operation : "index",
|
|
5024
|
+
token: typeof parsed.token === "string" ? parsed.token : "legacy-v0.14.0"
|
|
5025
|
+
};
|
|
5026
|
+
} catch {
|
|
5027
|
+
return null;
|
|
5028
|
+
}
|
|
5029
|
+
}
|
|
5030
|
+
function getOwnerLiveness(owner) {
|
|
5031
|
+
if (owner.hostname !== os4.hostname()) return "unknown";
|
|
5032
|
+
try {
|
|
5033
|
+
process.kill(owner.pid, 0);
|
|
5034
|
+
return "alive";
|
|
5035
|
+
} catch (error) {
|
|
5036
|
+
const code = getErrorCode(error);
|
|
5037
|
+
if (code === "ESRCH") return "dead";
|
|
5038
|
+
if (code === "EPERM") return "alive";
|
|
5039
|
+
return "unknown";
|
|
5040
|
+
}
|
|
5041
|
+
}
|
|
5042
|
+
function sameOwner(left, right) {
|
|
5043
|
+
return left.pid === right.pid && left.hostname === right.hostname && left.token === right.token;
|
|
5044
|
+
}
|
|
5045
|
+
function sameReclaimOwner(left, right) {
|
|
5046
|
+
return left.pid === right.pid && left.hostname === right.hostname && left.token === right.token && left.expectedOwnerToken === right.expectedOwnerToken;
|
|
5047
|
+
}
|
|
5048
|
+
function publishJsonDirectory(finalPath, value) {
|
|
5049
|
+
const candidatePath = `${finalPath}.candidate.${process.pid}.${(0, import_crypto.randomUUID)()}`;
|
|
5050
|
+
(0, import_fs7.mkdirSync)(candidatePath, { mode: 448 });
|
|
5051
|
+
try {
|
|
5052
|
+
(0, import_fs7.writeFileSync)(path11.join(candidatePath, OWNER_FILE_NAME), JSON.stringify(value), {
|
|
5053
|
+
encoding: "utf-8",
|
|
5054
|
+
flag: "wx",
|
|
5055
|
+
mode: 384
|
|
5056
|
+
});
|
|
5057
|
+
if ((0, import_fs7.existsSync)(finalPath)) return false;
|
|
5058
|
+
try {
|
|
5059
|
+
(0, import_fs7.renameSync)(candidatePath, finalPath);
|
|
5060
|
+
return true;
|
|
5061
|
+
} catch (error) {
|
|
5062
|
+
if ((0, import_fs7.existsSync)(finalPath)) return false;
|
|
5063
|
+
throw error;
|
|
5064
|
+
}
|
|
5065
|
+
} finally {
|
|
5066
|
+
if ((0, import_fs7.existsSync)(candidatePath)) (0, import_fs7.rmSync)(candidatePath, { recursive: true, force: true });
|
|
5067
|
+
}
|
|
5068
|
+
}
|
|
5069
|
+
function createOwner(operation) {
|
|
5070
|
+
return {
|
|
5071
|
+
pid: process.pid,
|
|
5072
|
+
hostname: os4.hostname(),
|
|
5073
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5074
|
+
operation,
|
|
5075
|
+
token: (0, import_crypto.randomUUID)()
|
|
5076
|
+
};
|
|
5077
|
+
}
|
|
5078
|
+
function recoveryMarkerPath(indexPath, owner) {
|
|
5079
|
+
return path11.join(indexPath, `${RECOVERY_MARKER_PREFIX}${owner.token}`);
|
|
5080
|
+
}
|
|
5081
|
+
function publishRecoveryMarker(indexPath, owner) {
|
|
5082
|
+
const markerPath = recoveryMarkerPath(indexPath, owner);
|
|
5083
|
+
if (!publishJsonDirectory(markerPath, owner)) {
|
|
5084
|
+
const markerOwner = readRecoveryOwner(markerPath);
|
|
5085
|
+
if (!markerOwner || !sameOwner(markerOwner, owner)) {
|
|
5086
|
+
throw new IndexLockContentionError(markerPath, markerOwner, "unknown-owner");
|
|
5087
|
+
}
|
|
5088
|
+
}
|
|
5089
|
+
return markerPath;
|
|
5090
|
+
}
|
|
5091
|
+
function getPendingRecoveries(indexPath) {
|
|
5092
|
+
const recoveries = [];
|
|
5093
|
+
const markerNames = (0, import_fs7.readdirSync)(indexPath).filter((name) => {
|
|
5094
|
+
if (!name.startsWith(RECOVERY_MARKER_PREFIX)) return false;
|
|
5095
|
+
return UUID_PATTERN.test(name.slice(RECOVERY_MARKER_PREFIX.length));
|
|
5096
|
+
}).sort();
|
|
5097
|
+
for (const markerName of markerNames) {
|
|
5098
|
+
const markerPath = path11.join(indexPath, markerName);
|
|
5099
|
+
const markerToken = markerName.slice(RECOVERY_MARKER_PREFIX.length);
|
|
5100
|
+
let markerStats;
|
|
5101
|
+
try {
|
|
5102
|
+
markerStats = (0, import_fs7.lstatSync)(markerPath);
|
|
5103
|
+
} catch (error) {
|
|
5104
|
+
if (getErrorCode(error) === "ENOENT") continue;
|
|
5105
|
+
throw error;
|
|
5106
|
+
}
|
|
5107
|
+
if (!markerStats.isDirectory()) {
|
|
5108
|
+
throw new IndexLockContentionError(markerPath, null, "unknown-owner");
|
|
5109
|
+
}
|
|
5110
|
+
const owner = readRecoveryOwner(markerPath);
|
|
5111
|
+
if (!owner || owner.token !== markerToken || getOwnerLiveness(owner) !== "dead") {
|
|
5112
|
+
throw new IndexLockContentionError(markerPath, owner, "unknown-owner");
|
|
5113
|
+
}
|
|
5114
|
+
recoveries.push({ owner, markerPath });
|
|
5115
|
+
}
|
|
5116
|
+
recoveries.sort((left, right) => {
|
|
5117
|
+
const byTimestamp = left.owner.startedAt.localeCompare(right.owner.startedAt);
|
|
5118
|
+
return byTimestamp !== 0 ? byTimestamp : left.markerPath.localeCompare(right.markerPath);
|
|
5119
|
+
});
|
|
5120
|
+
return recoveries;
|
|
5121
|
+
}
|
|
5122
|
+
function cleanupDeadPublicationCandidates(indexPath) {
|
|
5123
|
+
const candidatePattern = /^indexing\.lock(?:\.recovery\.[0-9a-f-]{36})?\.candidate\.(\d+)\.[0-9a-f-]{36}$/i;
|
|
5124
|
+
for (const entry of (0, import_fs7.readdirSync)(indexPath)) {
|
|
5125
|
+
const match = candidatePattern.exec(entry);
|
|
5126
|
+
if (!match) continue;
|
|
5127
|
+
const pid = Number(match[1]);
|
|
5128
|
+
if (!Number.isInteger(pid) || pid <= 0) continue;
|
|
5129
|
+
if (getOwnerLiveness({ pid, hostname: os4.hostname() }) !== "dead") continue;
|
|
5130
|
+
(0, import_fs7.rmSync)(path11.join(indexPath, entry), { recursive: true, force: true });
|
|
5131
|
+
}
|
|
5132
|
+
}
|
|
5133
|
+
function removeDeadReclaimMarker(lockPath, expectedOwner) {
|
|
5134
|
+
const markerPath = path11.join(lockPath, RECLAIM_DIRECTORY_NAME);
|
|
5135
|
+
const marker = readReclaimOwner(markerPath);
|
|
5136
|
+
if (!marker || marker.expectedOwnerToken !== expectedOwner.token || getOwnerLiveness(marker) !== "dead") {
|
|
5137
|
+
return false;
|
|
5138
|
+
}
|
|
5139
|
+
const currentOwner = readDirectoryOwner(lockPath);
|
|
5140
|
+
if (!currentOwner || !sameOwner(currentOwner, expectedOwner) || getOwnerLiveness(currentOwner) !== "dead") {
|
|
5141
|
+
return false;
|
|
5142
|
+
}
|
|
5143
|
+
const claimedMarkerPath = `${markerPath}.stale.${marker.pid}.${marker.token}.${(0, import_crypto.randomUUID)()}`;
|
|
5144
|
+
try {
|
|
5145
|
+
(0, import_fs7.renameSync)(markerPath, claimedMarkerPath);
|
|
5146
|
+
} catch (error) {
|
|
5147
|
+
if (getErrorCode(error) === "ENOENT") return false;
|
|
5148
|
+
throw error;
|
|
5149
|
+
}
|
|
5150
|
+
const claimedMarker = readReclaimOwner(claimedMarkerPath);
|
|
5151
|
+
const ownerAfterClaim = readDirectoryOwner(lockPath);
|
|
5152
|
+
if (!claimedMarker || !sameReclaimOwner(claimedMarker, marker) || !ownerAfterClaim || !sameOwner(ownerAfterClaim, expectedOwner) || getOwnerLiveness(ownerAfterClaim) !== "dead") {
|
|
5153
|
+
if (!(0, import_fs7.existsSync)(markerPath) && (0, import_fs7.existsSync)(claimedMarkerPath)) {
|
|
5154
|
+
(0, import_fs7.renameSync)(claimedMarkerPath, markerPath);
|
|
5155
|
+
}
|
|
5156
|
+
return false;
|
|
5157
|
+
}
|
|
5158
|
+
(0, import_fs7.rmSync)(claimedMarkerPath, { recursive: true, force: true });
|
|
5159
|
+
return true;
|
|
5160
|
+
}
|
|
5161
|
+
function reclaimDeadOwner(indexPath, lockPath, expectedOwner) {
|
|
5162
|
+
const reclaimPath = path11.join(lockPath, RECLAIM_DIRECTORY_NAME);
|
|
5163
|
+
const reclaimOwner = {
|
|
5164
|
+
pid: process.pid,
|
|
5165
|
+
hostname: os4.hostname(),
|
|
5166
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5167
|
+
token: (0, import_crypto.randomUUID)(),
|
|
5168
|
+
expectedOwnerToken: expectedOwner.token
|
|
5169
|
+
};
|
|
5170
|
+
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
5171
|
+
if (publishJsonDirectory(reclaimPath, reclaimOwner)) break;
|
|
5172
|
+
if (attempt === 0 && removeDeadReclaimMarker(lockPath, expectedOwner)) continue;
|
|
5173
|
+
return false;
|
|
5174
|
+
}
|
|
5175
|
+
try {
|
|
5176
|
+
const currentReclaimer = readReclaimOwner(reclaimPath);
|
|
5177
|
+
const currentOwner = readDirectoryOwner(lockPath);
|
|
5178
|
+
if (!currentReclaimer || !sameReclaimOwner(currentReclaimer, reclaimOwner) || !currentOwner || !sameOwner(currentOwner, expectedOwner) || getOwnerLiveness(currentOwner) !== "dead") {
|
|
5179
|
+
return false;
|
|
5180
|
+
}
|
|
5181
|
+
publishRecoveryMarker(indexPath, expectedOwner);
|
|
5182
|
+
const ownerBeforeQuarantine = readDirectoryOwner(lockPath);
|
|
5183
|
+
const reclaimerBeforeQuarantine = readReclaimOwner(reclaimPath);
|
|
5184
|
+
if (!ownerBeforeQuarantine || !sameOwner(ownerBeforeQuarantine, expectedOwner) || getOwnerLiveness(ownerBeforeQuarantine) !== "dead" || !reclaimerBeforeQuarantine || !sameReclaimOwner(reclaimerBeforeQuarantine, reclaimOwner)) {
|
|
5185
|
+
return false;
|
|
5186
|
+
}
|
|
5187
|
+
const quarantinePath = `${lockPath}.stale.${expectedOwner.token}.${reclaimOwner.token}`;
|
|
5188
|
+
(0, import_fs7.renameSync)(lockPath, quarantinePath);
|
|
5189
|
+
(0, import_fs7.rmSync)(quarantinePath, { recursive: true, force: true });
|
|
5190
|
+
return true;
|
|
5191
|
+
} catch (error) {
|
|
5192
|
+
if (getErrorCode(error) === "ENOENT") return false;
|
|
5193
|
+
throw error;
|
|
5194
|
+
}
|
|
5195
|
+
}
|
|
5196
|
+
var IndexLockContentionError = class extends Error {
|
|
5197
|
+
constructor(lockPath, owner, reason) {
|
|
5198
|
+
const ownerDescription = owner ? `PID ${owner.pid} on ${owner.hostname}, operation ${owner.operation}, since ${owner.startedAt}` : "an unreadable owner";
|
|
5199
|
+
super(`Index mutation already in progress: ${ownerDescription}`);
|
|
5200
|
+
this.lockPath = lockPath;
|
|
5201
|
+
this.owner = owner;
|
|
5202
|
+
this.reason = reason;
|
|
5203
|
+
this.name = "IndexLockContentionError";
|
|
5204
|
+
}
|
|
5205
|
+
lockPath;
|
|
5206
|
+
owner;
|
|
5207
|
+
reason;
|
|
5208
|
+
code = "INDEX_BUSY";
|
|
5209
|
+
};
|
|
5210
|
+
function isIndexLockContentionError(error) {
|
|
5211
|
+
return error instanceof IndexLockContentionError || typeof error === "object" && error !== null && "code" in error && error.code === "INDEX_BUSY";
|
|
5212
|
+
}
|
|
5213
|
+
function isTransientIndexLockContention(error) {
|
|
5214
|
+
if (!isIndexLockContentionError(error) || !("reason" in error)) return false;
|
|
5215
|
+
return error.reason === "active" || error.reason === "reclaiming";
|
|
5216
|
+
}
|
|
5217
|
+
function acquireIndexLock(indexPath, operation) {
|
|
5218
|
+
(0, import_fs7.mkdirSync)(indexPath, { recursive: true });
|
|
5219
|
+
const canonicalIndexPath = import_fs7.realpathSync.native(indexPath);
|
|
5220
|
+
const lockPath = path11.join(canonicalIndexPath, "indexing.lock");
|
|
5221
|
+
cleanupDeadPublicationCandidates(canonicalIndexPath);
|
|
5222
|
+
for (let attempt = 0; attempt < 6; attempt += 1) {
|
|
5223
|
+
const owner = createOwner(operation);
|
|
5224
|
+
if (publishJsonDirectory(lockPath, owner)) {
|
|
5225
|
+
const lease = {
|
|
5226
|
+
canonicalIndexPath,
|
|
5227
|
+
lockPath,
|
|
5228
|
+
owner,
|
|
5229
|
+
recoveries: []
|
|
5230
|
+
};
|
|
5231
|
+
try {
|
|
5232
|
+
lease.recoveries = getPendingRecoveries(canonicalIndexPath);
|
|
5233
|
+
return lease;
|
|
5234
|
+
} catch (error) {
|
|
5235
|
+
releaseIndexLock(lease);
|
|
5236
|
+
throw error;
|
|
5237
|
+
}
|
|
5238
|
+
}
|
|
5239
|
+
let stats;
|
|
5240
|
+
try {
|
|
5241
|
+
stats = (0, import_fs7.lstatSync)(lockPath);
|
|
5242
|
+
} catch (error) {
|
|
5243
|
+
if (getErrorCode(error) === "ENOENT") continue;
|
|
5244
|
+
throw error;
|
|
5245
|
+
}
|
|
5246
|
+
if (!stats.isDirectory()) {
|
|
5247
|
+
const legacyOwner = readLegacyOwner(lockPath);
|
|
5248
|
+
throw new IndexLockContentionError(lockPath, legacyOwner, "legacy-lock");
|
|
5249
|
+
}
|
|
5250
|
+
const existingOwner = readDirectoryOwner(lockPath);
|
|
5251
|
+
if (!existingOwner) {
|
|
5252
|
+
throw new IndexLockContentionError(lockPath, null, "unknown-owner");
|
|
5253
|
+
}
|
|
5254
|
+
const liveness = getOwnerLiveness(existingOwner);
|
|
5255
|
+
if (liveness !== "dead") {
|
|
5256
|
+
throw new IndexLockContentionError(lockPath, existingOwner, liveness === "alive" ? "active" : "unknown-owner");
|
|
5257
|
+
}
|
|
5258
|
+
if (!reclaimDeadOwner(canonicalIndexPath, lockPath, existingOwner)) {
|
|
5259
|
+
throw new IndexLockContentionError(lockPath, existingOwner, "reclaiming");
|
|
5260
|
+
}
|
|
5261
|
+
}
|
|
5262
|
+
throw new IndexLockContentionError(lockPath, null, "reclaiming");
|
|
5263
|
+
}
|
|
5264
|
+
function releaseIndexLock(lease) {
|
|
5265
|
+
const currentOwner = readDirectoryOwner(lease.lockPath);
|
|
5266
|
+
if (!currentOwner || !sameOwner(currentOwner, lease.owner)) return false;
|
|
5267
|
+
const releasePath = `${lease.lockPath}.release.${lease.owner.pid}.${lease.owner.token}`;
|
|
5268
|
+
try {
|
|
5269
|
+
retryTransientFilesystemOperation(() => (0, import_fs7.renameSync)(lease.lockPath, releasePath));
|
|
5270
|
+
} catch (error) {
|
|
5271
|
+
if (getErrorCode(error) === "ENOENT") return false;
|
|
5272
|
+
throw error;
|
|
5273
|
+
}
|
|
5274
|
+
const claimedOwner = readDirectoryOwner(releasePath);
|
|
5275
|
+
if (!claimedOwner || !sameOwner(claimedOwner, lease.owner)) {
|
|
5276
|
+
if (!(0, import_fs7.existsSync)(lease.lockPath) && (0, import_fs7.existsSync)(releasePath)) {
|
|
5277
|
+
(0, import_fs7.renameSync)(releasePath, lease.lockPath);
|
|
5278
|
+
}
|
|
5279
|
+
return false;
|
|
5280
|
+
}
|
|
5281
|
+
try {
|
|
5282
|
+
retryTransientFilesystemOperation(() => (0, import_fs7.rmSync)(releasePath, { recursive: true, force: true }));
|
|
5283
|
+
} catch (error) {
|
|
5284
|
+
console.error(`[codebase-index] Lease released but tombstone cleanup failed: ${releasePath}`, error);
|
|
5285
|
+
}
|
|
5286
|
+
return true;
|
|
5287
|
+
}
|
|
5288
|
+
async function withIndexLock(indexPath, operation, callback, options = {}) {
|
|
5289
|
+
const lease = acquireIndexLock(indexPath, operation);
|
|
5290
|
+
let result;
|
|
5291
|
+
let callbackError;
|
|
5292
|
+
let callbackFailed = false;
|
|
5293
|
+
try {
|
|
5294
|
+
result = await callback(lease);
|
|
5295
|
+
} catch (error) {
|
|
5296
|
+
callbackFailed = true;
|
|
5297
|
+
callbackError = error;
|
|
5298
|
+
}
|
|
5299
|
+
if (!callbackFailed && options.completeRecoveries !== false) {
|
|
5300
|
+
try {
|
|
5301
|
+
completeLeaseRecovery(lease);
|
|
5302
|
+
} catch (error) {
|
|
5303
|
+
callbackFailed = true;
|
|
5304
|
+
callbackError = error;
|
|
5305
|
+
}
|
|
5306
|
+
}
|
|
5307
|
+
let releaseError;
|
|
5308
|
+
try {
|
|
5309
|
+
if (!releaseIndexLock(lease)) {
|
|
5310
|
+
releaseError = new Error(`Lost ownership of index mutation lease ${lease.owner.token}`);
|
|
5311
|
+
}
|
|
5312
|
+
} catch (error) {
|
|
5313
|
+
releaseError = error;
|
|
5314
|
+
}
|
|
5315
|
+
if (releaseError !== void 0) {
|
|
5316
|
+
if (callbackFailed) throw new AggregateError([callbackError, releaseError], "Index mutation and lease release both failed");
|
|
5317
|
+
throw releaseError;
|
|
5318
|
+
}
|
|
5319
|
+
if (callbackFailed) throw callbackError;
|
|
5320
|
+
return result;
|
|
5321
|
+
}
|
|
5322
|
+
function createLeaseTemporaryPath(targetPath, owner, kind = "tmp") {
|
|
5323
|
+
if (kind === "bak") return `${targetPath}.bak.${owner.pid}.${owner.token}`;
|
|
5324
|
+
temporaryCounter += 1;
|
|
5325
|
+
return `${targetPath}.tmp.${owner.pid}.${owner.token}.${temporaryCounter}`;
|
|
5326
|
+
}
|
|
5327
|
+
function removeLeaseTemporaryPath(temporaryPath) {
|
|
5328
|
+
if ((0, import_fs7.existsSync)(temporaryPath)) (0, import_fs7.rmSync)(temporaryPath, { recursive: true, force: true });
|
|
5329
|
+
}
|
|
5330
|
+
function recoverLeaseArtifacts(indexPath, owner, backupTargets) {
|
|
5331
|
+
for (const targetPath of backupTargets) {
|
|
5332
|
+
const backupPath = createLeaseTemporaryPath(targetPath, owner, "bak");
|
|
5333
|
+
if (!(0, import_fs7.existsSync)(backupPath)) continue;
|
|
5334
|
+
if ((0, import_fs7.existsSync)(targetPath)) (0, import_fs7.rmSync)(targetPath, { recursive: true, force: true });
|
|
5335
|
+
(0, import_fs7.renameSync)(backupPath, targetPath);
|
|
5336
|
+
}
|
|
5337
|
+
const temporaryOwnerMarker = `.tmp.${owner.pid}.${owner.token}.`;
|
|
5338
|
+
for (const entry of (0, import_fs7.readdirSync)(indexPath)) {
|
|
5339
|
+
if (!entry.includes(temporaryOwnerMarker)) continue;
|
|
5340
|
+
(0, import_fs7.rmSync)(path11.join(indexPath, entry), { recursive: true, force: true });
|
|
5341
|
+
}
|
|
5342
|
+
}
|
|
5343
|
+
function completeLeaseRecovery(lease) {
|
|
5344
|
+
for (const recovery of lease.recoveries) {
|
|
5345
|
+
const markerOwner = readRecoveryOwner(recovery.markerPath);
|
|
5346
|
+
if (!markerOwner || !sameOwner(markerOwner, recovery.owner)) {
|
|
5347
|
+
throw new Error(`Recovery marker ownership changed: ${recovery.markerPath}`);
|
|
5348
|
+
}
|
|
5349
|
+
}
|
|
5350
|
+
for (const recovery of lease.recoveries) {
|
|
5351
|
+
(0, import_fs7.rmSync)(recovery.markerPath, { recursive: true, force: true });
|
|
5352
|
+
}
|
|
5353
|
+
}
|
|
5354
|
+
|
|
4909
5355
|
// src/indexer/index.ts
|
|
4910
5356
|
var CALL_GRAPH_LANGUAGES = /* @__PURE__ */ new Set(["typescript", "tsx", "javascript", "jsx", "python", "go", "rust", "php", "apex", "zig", "gdscript", "matlab", "bash"]);
|
|
4911
5357
|
var CASE_INSENSITIVE_LANGUAGES = /* @__PURE__ */ new Set(["apex"]);
|
|
@@ -4987,6 +5433,7 @@ function isSqliteCorruptionError(error) {
|
|
|
4987
5433
|
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");
|
|
4988
5434
|
}
|
|
4989
5435
|
var STARTUP_WARNING_METADATA_KEY = "index.startupWarning";
|
|
5436
|
+
var READER_ARTIFACT_RETRY_INTERVAL_MS = 1e3;
|
|
4990
5437
|
function metadataFromBlame(blame) {
|
|
4991
5438
|
if (!blame) {
|
|
4992
5439
|
return {};
|
|
@@ -5184,9 +5631,9 @@ function hasAllEmbeddingParts(parts, expectedPartCount) {
|
|
|
5184
5631
|
return true;
|
|
5185
5632
|
}
|
|
5186
5633
|
function isPathWithinRoot(filePath, rootPath) {
|
|
5187
|
-
const normalizedFilePath =
|
|
5188
|
-
const normalizedRoot =
|
|
5189
|
-
return normalizedFilePath === normalizedRoot || normalizedFilePath.startsWith(`${normalizedRoot}${
|
|
5634
|
+
const normalizedFilePath = path12.resolve(filePath);
|
|
5635
|
+
const normalizedRoot = path12.resolve(rootPath);
|
|
5636
|
+
return normalizedFilePath === normalizedRoot || normalizedFilePath.startsWith(`${normalizedRoot}${path12.sep}`);
|
|
5190
5637
|
}
|
|
5191
5638
|
var rankingQueryTokenCache = /* @__PURE__ */ new Map();
|
|
5192
5639
|
var rankingNameTokenCache = /* @__PURE__ */ new Map();
|
|
@@ -6252,26 +6699,133 @@ var Indexer = class {
|
|
|
6252
6699
|
queryCacheTtlMs = 5 * 60 * 1e3;
|
|
6253
6700
|
querySimilarityThreshold = 0.85;
|
|
6254
6701
|
indexCompatibility = null;
|
|
6255
|
-
|
|
6702
|
+
activeIndexLease = null;
|
|
6703
|
+
initializationPromise = null;
|
|
6704
|
+
initializationMode = "none";
|
|
6705
|
+
readIssues = [];
|
|
6706
|
+
retiredDatabases = [];
|
|
6707
|
+
readerArtifactFingerprint = null;
|
|
6708
|
+
writerArtifactFingerprint = null;
|
|
6709
|
+
readerArtifactRetryAfter = /* @__PURE__ */ new Map();
|
|
6256
6710
|
constructor(projectRoot, config, host = "opencode") {
|
|
6257
6711
|
this.projectRoot = projectRoot;
|
|
6258
6712
|
this.config = config;
|
|
6259
6713
|
this.host = host;
|
|
6260
6714
|
this.indexPath = this.getIndexPath();
|
|
6261
|
-
this.fileHashCachePath =
|
|
6262
|
-
this.failedBatchesPath =
|
|
6263
|
-
this.indexingLockPath = path11.join(this.indexPath, "indexing.lock");
|
|
6715
|
+
this.fileHashCachePath = path12.join(this.indexPath, "file-hashes.json");
|
|
6716
|
+
this.failedBatchesPath = path12.join(this.indexPath, "failed-batches.json");
|
|
6264
6717
|
this.logger = initializeLogger(config.debug);
|
|
6265
6718
|
}
|
|
6266
6719
|
getIndexPath() {
|
|
6267
6720
|
return resolveProjectIndexPath(this.projectRoot, this.config.scope, this.host);
|
|
6268
6721
|
}
|
|
6722
|
+
isLocalProjectIndexPath() {
|
|
6723
|
+
const localProjectIndexPaths = [path12.join(this.projectRoot, getHostProjectIndexRelativePath(this.host))];
|
|
6724
|
+
if (this.host !== "opencode") {
|
|
6725
|
+
localProjectIndexPaths.push(path12.join(this.projectRoot, getHostProjectIndexRelativePath("opencode")));
|
|
6726
|
+
}
|
|
6727
|
+
return localProjectIndexPaths.some((localPath) => {
|
|
6728
|
+
if (!(0, import_fs8.existsSync)(localPath) || !(0, import_fs8.existsSync)(this.indexPath)) {
|
|
6729
|
+
return path12.resolve(this.indexPath) === path12.resolve(localPath);
|
|
6730
|
+
}
|
|
6731
|
+
const indexStats = (0, import_fs8.statSync)(this.indexPath);
|
|
6732
|
+
const localStats = (0, import_fs8.statSync)(localPath);
|
|
6733
|
+
return indexStats.dev === localStats.dev && indexStats.ino === localStats.ino;
|
|
6734
|
+
});
|
|
6735
|
+
}
|
|
6736
|
+
resetLoadedIndexState(retireDatabase = false) {
|
|
6737
|
+
if (this.database) {
|
|
6738
|
+
if (retireDatabase) {
|
|
6739
|
+
this.retiredDatabases.push(this.database);
|
|
6740
|
+
} else {
|
|
6741
|
+
this.database.close();
|
|
6742
|
+
}
|
|
6743
|
+
}
|
|
6744
|
+
this.store = null;
|
|
6745
|
+
this.invertedIndex = null;
|
|
6746
|
+
this.database = null;
|
|
6747
|
+
this.provider = null;
|
|
6748
|
+
this.configuredProviderInfo = null;
|
|
6749
|
+
this.reranker = null;
|
|
6750
|
+
this.indexCompatibility = null;
|
|
6751
|
+
this.initializationMode = "none";
|
|
6752
|
+
this.readIssues = [];
|
|
6753
|
+
this.readerArtifactFingerprint = null;
|
|
6754
|
+
this.writerArtifactFingerprint = null;
|
|
6755
|
+
this.readerArtifactRetryAfter.clear();
|
|
6756
|
+
this.fileHashCache.clear();
|
|
6757
|
+
}
|
|
6758
|
+
refreshLoadedIndexState() {
|
|
6759
|
+
if (!this.store || !this.invertedIndex || !this.configuredProviderInfo) return;
|
|
6760
|
+
this.store.load();
|
|
6761
|
+
this.invertedIndex.load();
|
|
6762
|
+
this.fileHashCache.clear();
|
|
6763
|
+
this.loadFileHashCache();
|
|
6764
|
+
this.indexCompatibility = this.validateIndexCompatibility(this.configuredProviderInfo);
|
|
6765
|
+
this.readIssues = [];
|
|
6766
|
+
this.readerArtifactRetryAfter.clear();
|
|
6767
|
+
}
|
|
6768
|
+
async withIndexMutationLease(operation, callback) {
|
|
6769
|
+
const lease = acquireIndexLock(this.indexPath, operation);
|
|
6770
|
+
this.indexPath = lease.canonicalIndexPath;
|
|
6771
|
+
this.fileHashCachePath = path12.join(this.indexPath, "file-hashes.json");
|
|
6772
|
+
this.failedBatchesPath = path12.join(this.indexPath, "failed-batches.json");
|
|
6773
|
+
this.activeIndexLease = lease;
|
|
6774
|
+
let result;
|
|
6775
|
+
let callbackError;
|
|
6776
|
+
let callbackFailed = false;
|
|
6777
|
+
try {
|
|
6778
|
+
result = await callback(lease.recoveries.map(({ owner }) => owner));
|
|
6779
|
+
} catch (error) {
|
|
6780
|
+
callbackFailed = true;
|
|
6781
|
+
callbackError = error;
|
|
6782
|
+
}
|
|
6783
|
+
if (!callbackFailed) {
|
|
6784
|
+
try {
|
|
6785
|
+
completeLeaseRecovery(lease);
|
|
6786
|
+
this.writerArtifactFingerprint = this.captureReaderArtifactFingerprint();
|
|
6787
|
+
} catch (error) {
|
|
6788
|
+
callbackFailed = true;
|
|
6789
|
+
callbackError = error;
|
|
6790
|
+
}
|
|
6791
|
+
}
|
|
6792
|
+
let releaseError;
|
|
6793
|
+
try {
|
|
6794
|
+
if (!releaseIndexLock(lease)) {
|
|
6795
|
+
releaseError = new Error(`Lost ownership of index mutation lease ${lease.owner.token}`);
|
|
6796
|
+
this.writerArtifactFingerprint = null;
|
|
6797
|
+
if (this.activeIndexLease?.owner.token === lease.owner.token) {
|
|
6798
|
+
this.activeIndexLease = null;
|
|
6799
|
+
}
|
|
6800
|
+
} else if (this.activeIndexLease?.owner.token === lease.owner.token) {
|
|
6801
|
+
this.activeIndexLease = null;
|
|
6802
|
+
}
|
|
6803
|
+
} catch (error) {
|
|
6804
|
+
releaseError = error;
|
|
6805
|
+
this.writerArtifactFingerprint = null;
|
|
6806
|
+
if (!(0, import_fs8.existsSync)(lease.lockPath) && this.activeIndexLease?.owner.token === lease.owner.token) {
|
|
6807
|
+
this.activeIndexLease = null;
|
|
6808
|
+
}
|
|
6809
|
+
}
|
|
6810
|
+
if (releaseError !== void 0) {
|
|
6811
|
+
if (callbackFailed) throw new AggregateError([callbackError, releaseError], "Index mutation and lease release both failed");
|
|
6812
|
+
throw releaseError;
|
|
6813
|
+
}
|
|
6814
|
+
if (callbackFailed) throw callbackError;
|
|
6815
|
+
return result;
|
|
6816
|
+
}
|
|
6817
|
+
requireActiveLease() {
|
|
6818
|
+
if (!this.activeIndexLease) {
|
|
6819
|
+
throw new Error("Index mutation attempted without an active interprocess lease");
|
|
6820
|
+
}
|
|
6821
|
+
return this.activeIndexLease;
|
|
6822
|
+
}
|
|
6269
6823
|
loadFileHashCache() {
|
|
6270
|
-
if (!(0,
|
|
6824
|
+
if (!(0, import_fs8.existsSync)(this.fileHashCachePath)) {
|
|
6271
6825
|
return;
|
|
6272
6826
|
}
|
|
6273
6827
|
try {
|
|
6274
|
-
const data = (0,
|
|
6828
|
+
const data = (0, import_fs8.readFileSync)(this.fileHashCachePath, "utf-8");
|
|
6275
6829
|
const parsed = JSON.parse(data);
|
|
6276
6830
|
this.fileHashCache = new Map(Object.entries(parsed));
|
|
6277
6831
|
} catch (error) {
|
|
@@ -6291,15 +6845,26 @@ var Indexer = class {
|
|
|
6291
6845
|
this.atomicWriteSync(this.fileHashCachePath, JSON.stringify(obj));
|
|
6292
6846
|
}
|
|
6293
6847
|
atomicWriteSync(targetPath, data) {
|
|
6294
|
-
const
|
|
6295
|
-
(
|
|
6296
|
-
(0,
|
|
6297
|
-
|
|
6848
|
+
const lease = this.requireActiveLease();
|
|
6849
|
+
const tempPath = createLeaseTemporaryPath(targetPath, lease.owner, "tmp");
|
|
6850
|
+
(0, import_fs8.mkdirSync)(path12.dirname(targetPath), { recursive: true });
|
|
6851
|
+
try {
|
|
6852
|
+
(0, import_fs8.writeFileSync)(tempPath, data);
|
|
6853
|
+
(0, import_fs8.renameSync)(tempPath, targetPath);
|
|
6854
|
+
} finally {
|
|
6855
|
+
removeLeaseTemporaryPath(tempPath);
|
|
6856
|
+
}
|
|
6857
|
+
}
|
|
6858
|
+
saveInvertedIndex(invertedIndex) {
|
|
6859
|
+
this.atomicWriteSync(
|
|
6860
|
+
path12.join(this.indexPath, "inverted-index.json"),
|
|
6861
|
+
invertedIndex.serialize()
|
|
6862
|
+
);
|
|
6298
6863
|
}
|
|
6299
6864
|
getScopedRoots() {
|
|
6300
|
-
const roots = /* @__PURE__ */ new Set([
|
|
6865
|
+
const roots = /* @__PURE__ */ new Set([path12.resolve(this.projectRoot)]);
|
|
6301
6866
|
for (const kbRoot of this.config.knowledgeBases) {
|
|
6302
|
-
roots.add(
|
|
6867
|
+
roots.add(path12.resolve(this.projectRoot, kbRoot));
|
|
6303
6868
|
}
|
|
6304
6869
|
return Array.from(roots);
|
|
6305
6870
|
}
|
|
@@ -6308,29 +6873,29 @@ var Indexer = class {
|
|
|
6308
6873
|
if (this.config.scope !== "global") {
|
|
6309
6874
|
return branchName;
|
|
6310
6875
|
}
|
|
6311
|
-
const projectHash = hashContent(
|
|
6876
|
+
const projectHash = hashContent(path12.resolve(this.projectRoot)).slice(0, 16);
|
|
6312
6877
|
return `${projectHash}:${branchName}`;
|
|
6313
6878
|
}
|
|
6314
6879
|
getBranchCatalogKeyFor(branchName) {
|
|
6315
6880
|
if (this.config.scope !== "global") {
|
|
6316
6881
|
return branchName;
|
|
6317
6882
|
}
|
|
6318
|
-
const projectHash = hashContent(
|
|
6883
|
+
const projectHash = hashContent(path12.resolve(this.projectRoot)).slice(0, 16);
|
|
6319
6884
|
return `${projectHash}:${branchName}`;
|
|
6320
6885
|
}
|
|
6321
6886
|
getLegacyBranchCatalogKey() {
|
|
6322
6887
|
return this.currentBranch || "default";
|
|
6323
6888
|
}
|
|
6324
6889
|
getLegacyMigrationMetadataKey() {
|
|
6325
|
-
const projectHash = hashContent(
|
|
6890
|
+
const projectHash = hashContent(path12.resolve(this.projectRoot)).slice(0, 16);
|
|
6326
6891
|
return `index.globalBranchMigration.${projectHash}`;
|
|
6327
6892
|
}
|
|
6328
6893
|
getProjectEmbeddingStrategyMetadataKey() {
|
|
6329
|
-
const projectHash = hashContent(
|
|
6894
|
+
const projectHash = hashContent(path12.resolve(this.projectRoot)).slice(0, 16);
|
|
6330
6895
|
return `index.embeddingStrategyVersion.${projectHash}`;
|
|
6331
6896
|
}
|
|
6332
6897
|
getProjectForceReembedMetadataKey() {
|
|
6333
|
-
const projectHash = hashContent(
|
|
6898
|
+
const projectHash = hashContent(path12.resolve(this.projectRoot)).slice(0, 16);
|
|
6334
6899
|
return `index.forceReembed.${projectHash}`;
|
|
6335
6900
|
}
|
|
6336
6901
|
hasProjectForceReembedPending() {
|
|
@@ -6424,7 +6989,7 @@ var Indexer = class {
|
|
|
6424
6989
|
if (!this.database) {
|
|
6425
6990
|
return { chunkIds, symbolIds };
|
|
6426
6991
|
}
|
|
6427
|
-
const projectRootPath =
|
|
6992
|
+
const projectRootPath = path12.resolve(this.projectRoot);
|
|
6428
6993
|
const projectLocalFilePaths = /* @__PURE__ */ new Set([
|
|
6429
6994
|
...Array.from(this.fileHashCache.keys()).filter(
|
|
6430
6995
|
(filePath) => this.isFileInCurrentScope(filePath, roots) && isPathWithinRoot(filePath, projectRootPath)
|
|
@@ -6447,7 +7012,7 @@ var Indexer = class {
|
|
|
6447
7012
|
if (this.config.scope !== "global") {
|
|
6448
7013
|
return this.getBranchCatalogCleanupKeys();
|
|
6449
7014
|
}
|
|
6450
|
-
const projectHash = hashContent(
|
|
7015
|
+
const projectHash = hashContent(path12.resolve(this.projectRoot)).slice(0, 16);
|
|
6451
7016
|
const keys = /* @__PURE__ */ new Set();
|
|
6452
7017
|
const projectChunkIdSet = new Set(projectChunkIds);
|
|
6453
7018
|
const projectSymbolIdSet = new Set(projectSymbolIds);
|
|
@@ -6531,7 +7096,7 @@ var Indexer = class {
|
|
|
6531
7096
|
if (!this.database || this.config.scope !== "global") {
|
|
6532
7097
|
return false;
|
|
6533
7098
|
}
|
|
6534
|
-
const projectHash = hashContent(
|
|
7099
|
+
const projectHash = hashContent(path12.resolve(this.projectRoot)).slice(0, 16);
|
|
6535
7100
|
const roots = this.getScopedRoots();
|
|
6536
7101
|
const { chunkIds: projectLocalChunkIds, symbolIds: projectLocalSymbolIds } = this.getProjectLocalScopedOwnershipIds(roots);
|
|
6537
7102
|
return this.database.getAllBranches().some(
|
|
@@ -6565,7 +7130,7 @@ var Indexer = class {
|
|
|
6565
7130
|
...Array.from(this.fileHashCache.keys()).filter((filePath) => this.isFileInCurrentScope(filePath, roots)),
|
|
6566
7131
|
...scopedEntries.map(({ metadata }) => metadata.filePath)
|
|
6567
7132
|
]);
|
|
6568
|
-
const projectRootPath =
|
|
7133
|
+
const projectRootPath = path12.resolve(this.projectRoot);
|
|
6569
7134
|
const projectLocalFilePaths = new Set(
|
|
6570
7135
|
Array.from(filePaths).filter((filePath) => isPathWithinRoot(filePath, projectRootPath))
|
|
6571
7136
|
);
|
|
@@ -6627,34 +7192,27 @@ var Indexer = class {
|
|
|
6627
7192
|
database.gcOrphanEmbeddings();
|
|
6628
7193
|
database.gcOrphanChunks();
|
|
6629
7194
|
store.save();
|
|
6630
|
-
|
|
7195
|
+
this.saveInvertedIndex(invertedIndex);
|
|
6631
7196
|
return {
|
|
6632
7197
|
removedChunkIds: removedChunkIdList,
|
|
6633
7198
|
hasForeignData: allMetadata.some(({ metadata }) => !this.isFileInCurrentScope(metadata.filePath, roots))
|
|
6634
7199
|
};
|
|
6635
7200
|
}
|
|
6636
|
-
|
|
6637
|
-
|
|
6638
|
-
|
|
6639
|
-
|
|
6640
|
-
|
|
6641
|
-
|
|
6642
|
-
|
|
6643
|
-
|
|
6644
|
-
(0, import_fs7.writeFileSync)(this.indexingLockPath, JSON.stringify(lockData));
|
|
6645
|
-
}
|
|
6646
|
-
releaseIndexingLock() {
|
|
6647
|
-
if ((0, import_fs7.existsSync)(this.indexingLockPath)) {
|
|
6648
|
-
(0, import_fs7.unlinkSync)(this.indexingLockPath);
|
|
7201
|
+
async recoverFromInterruptedIndexingUnlocked(owners) {
|
|
7202
|
+
for (const owner of owners) {
|
|
7203
|
+
this.logger.warn("Detected interrupted indexing session, recovering...", {
|
|
7204
|
+
pid: owner.pid,
|
|
7205
|
+
hostname: owner.hostname,
|
|
7206
|
+
operation: owner.operation,
|
|
7207
|
+
startedAt: owner.startedAt
|
|
7208
|
+
});
|
|
6649
7209
|
}
|
|
6650
|
-
|
|
6651
|
-
|
|
6652
|
-
|
|
6653
|
-
|
|
6654
|
-
|
|
7210
|
+
if (this.config.scope === "global") {
|
|
7211
|
+
if ((0, import_fs8.existsSync)(this.fileHashCachePath)) {
|
|
7212
|
+
(0, import_fs8.unlinkSync)(this.fileHashCachePath);
|
|
7213
|
+
}
|
|
7214
|
+
await this.healthCheckUnlocked();
|
|
6655
7215
|
}
|
|
6656
|
-
await this.healthCheck();
|
|
6657
|
-
this.releaseIndexingLock();
|
|
6658
7216
|
this.logger.info("Recovery complete, next index will re-process all files");
|
|
6659
7217
|
}
|
|
6660
7218
|
loadFailedBatches(maxChunkTokens) {
|
|
@@ -6670,10 +7228,10 @@ var Indexer = class {
|
|
|
6670
7228
|
}
|
|
6671
7229
|
}
|
|
6672
7230
|
loadSerializedFailedBatches() {
|
|
6673
|
-
if (!(0,
|
|
7231
|
+
if (!(0, import_fs8.existsSync)(this.failedBatchesPath)) {
|
|
6674
7232
|
return [];
|
|
6675
7233
|
}
|
|
6676
|
-
const data = (0,
|
|
7234
|
+
const data = (0, import_fs8.readFileSync)(this.failedBatchesPath, "utf-8");
|
|
6677
7235
|
const parsed = JSON.parse(data);
|
|
6678
7236
|
return parsed.map((batch) => {
|
|
6679
7237
|
const chunks = Array.isArray(batch.chunks) ? batch.chunks : [];
|
|
@@ -6690,15 +7248,15 @@ var Indexer = class {
|
|
|
6690
7248
|
}
|
|
6691
7249
|
saveFailedBatches(batches) {
|
|
6692
7250
|
if (batches.length === 0) {
|
|
6693
|
-
if ((0,
|
|
7251
|
+
if ((0, import_fs8.existsSync)(this.failedBatchesPath)) {
|
|
6694
7252
|
try {
|
|
6695
|
-
(0,
|
|
7253
|
+
(0, import_fs8.unlinkSync)(this.failedBatchesPath);
|
|
6696
7254
|
} catch {
|
|
6697
7255
|
}
|
|
6698
7256
|
}
|
|
6699
7257
|
return;
|
|
6700
7258
|
}
|
|
6701
|
-
|
|
7259
|
+
this.atomicWriteSync(this.failedBatchesPath, JSON.stringify(batches, null, 2));
|
|
6702
7260
|
}
|
|
6703
7261
|
collectRetryableFailedChunks(currentFileHashes, unchangedFilePaths, maxChunkTokens) {
|
|
6704
7262
|
const retryableById = /* @__PURE__ */ new Map();
|
|
@@ -6878,7 +7436,7 @@ var Indexer = class {
|
|
|
6878
7436
|
const intent = isLikelyImplementationPath(candidate.metadata.filePath) ? "implementation" : "doc_or_test";
|
|
6879
7437
|
parts.push(`intent_hint: ${intent}`);
|
|
6880
7438
|
try {
|
|
6881
|
-
const fileContent = await
|
|
7439
|
+
const fileContent = await import_fs8.promises.readFile(candidate.metadata.filePath, "utf-8");
|
|
6882
7440
|
const lines = fileContent.split("\n");
|
|
6883
7441
|
const snippetStartLine = Math.max(1, candidate.metadata.startLine - 2);
|
|
6884
7442
|
const snippetEndLine = Math.min(lines.length, candidate.metadata.endLine + 2);
|
|
@@ -6892,6 +7450,222 @@ var Indexer = class {
|
|
|
6892
7450
|
return parts.join("\n");
|
|
6893
7451
|
}
|
|
6894
7452
|
async initialize() {
|
|
7453
|
+
if (this.initializationPromise) {
|
|
7454
|
+
await this.initializationPromise;
|
|
7455
|
+
}
|
|
7456
|
+
if (this.isInitializedFor("reader")) {
|
|
7457
|
+
return;
|
|
7458
|
+
}
|
|
7459
|
+
await this.initializeOnce("reader", [], { skipAutoGc: true });
|
|
7460
|
+
}
|
|
7461
|
+
async initializeOnce(mode, recoveredOwners, options) {
|
|
7462
|
+
if (this.initializationPromise) {
|
|
7463
|
+
await this.initializationPromise;
|
|
7464
|
+
if (this.isInitializedFor(mode)) {
|
|
7465
|
+
return;
|
|
7466
|
+
}
|
|
7467
|
+
return this.initializeOnce(mode, recoveredOwners, options);
|
|
7468
|
+
}
|
|
7469
|
+
if (this.isInitializedFor(mode)) {
|
|
7470
|
+
return;
|
|
7471
|
+
}
|
|
7472
|
+
const initialization = this.initializeUnlocked(mode, recoveredOwners, options).catch((error) => {
|
|
7473
|
+
this.resetLoadedIndexState();
|
|
7474
|
+
throw error;
|
|
7475
|
+
}).finally(() => {
|
|
7476
|
+
if (this.initializationPromise === initialization) {
|
|
7477
|
+
this.initializationPromise = null;
|
|
7478
|
+
}
|
|
7479
|
+
});
|
|
7480
|
+
this.initializationPromise = initialization;
|
|
7481
|
+
await initialization;
|
|
7482
|
+
}
|
|
7483
|
+
isInitializedFor(mode) {
|
|
7484
|
+
const hasState = Boolean(
|
|
7485
|
+
this.store && this.provider && this.invertedIndex && this.configuredProviderInfo && this.database
|
|
7486
|
+
);
|
|
7487
|
+
if (!hasState) {
|
|
7488
|
+
return false;
|
|
7489
|
+
}
|
|
7490
|
+
return mode === "reader" ? this.initializationMode !== "none" : this.initializationMode === "writer";
|
|
7491
|
+
}
|
|
7492
|
+
recordReadIssue(component, message, error) {
|
|
7493
|
+
this.readIssues.push(this.createReadIssue(component, message));
|
|
7494
|
+
this.readerArtifactRetryAfter.set(component, Date.now() + READER_ARTIFACT_RETRY_INTERVAL_MS);
|
|
7495
|
+
this.logger.warn(message, error === void 0 ? void 0 : { error: getErrorMessage2(error) });
|
|
7496
|
+
}
|
|
7497
|
+
createReadIssue(component, message) {
|
|
7498
|
+
return {
|
|
7499
|
+
component,
|
|
7500
|
+
message,
|
|
7501
|
+
blocking: component !== "keyword"
|
|
7502
|
+
};
|
|
7503
|
+
}
|
|
7504
|
+
getVectorReadIssueMessage() {
|
|
7505
|
+
if (this.config.scope === "global") {
|
|
7506
|
+
return "Shared vector index could not be read. Restore or repair the complete fingerprinted shared vector artifacts; automatic reset is disabled for global scope.";
|
|
7507
|
+
}
|
|
7508
|
+
if (!this.isLocalProjectIndexPath()) {
|
|
7509
|
+
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.";
|
|
7510
|
+
}
|
|
7511
|
+
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.";
|
|
7512
|
+
}
|
|
7513
|
+
getKeywordReadIssueMessage() {
|
|
7514
|
+
if (this.config.scope === "global") {
|
|
7515
|
+
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.";
|
|
7516
|
+
}
|
|
7517
|
+
if (!this.isLocalProjectIndexPath()) {
|
|
7518
|
+
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.";
|
|
7519
|
+
}
|
|
7520
|
+
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.";
|
|
7521
|
+
}
|
|
7522
|
+
getDatabaseReadIssueMessage() {
|
|
7523
|
+
if (this.config.scope === "global") {
|
|
7524
|
+
return "Shared index database could not be read. Restore or repair the shared SQLite database; automatic reset is disabled for global scope.";
|
|
7525
|
+
}
|
|
7526
|
+
if (!this.isLocalProjectIndexPath()) {
|
|
7527
|
+
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.";
|
|
7528
|
+
}
|
|
7529
|
+
return "Index database could not be read. Run index_codebase after the active writer finishes to repair or migrate it under the writer lease.";
|
|
7530
|
+
}
|
|
7531
|
+
getReaderFileFingerprint(filePath, identityOnly = false) {
|
|
7532
|
+
try {
|
|
7533
|
+
const stats = (0, import_fs8.statSync)(filePath);
|
|
7534
|
+
if (identityOnly) {
|
|
7535
|
+
return `${stats.dev}:${stats.ino}`;
|
|
7536
|
+
}
|
|
7537
|
+
return `${stats.dev}:${stats.ino}:${stats.size}:${stats.mtimeMs}:${stats.ctimeMs}`;
|
|
7538
|
+
} catch (error) {
|
|
7539
|
+
return `unavailable:${getErrorMessage2(error)}`;
|
|
7540
|
+
}
|
|
7541
|
+
}
|
|
7542
|
+
captureReaderArtifactFingerprint() {
|
|
7543
|
+
const storePath = path12.join(this.indexPath, "vectors");
|
|
7544
|
+
return {
|
|
7545
|
+
vectors: `${this.getReaderFileFingerprint(storePath)}|${this.getReaderFileFingerprint(`${storePath}.meta.json`)}`,
|
|
7546
|
+
keyword: this.getReaderFileFingerprint(path12.join(this.indexPath, "inverted-index.json")),
|
|
7547
|
+
database: this.getReaderFileFingerprint(path12.join(this.indexPath, "codebase.db")),
|
|
7548
|
+
databaseIdentity: this.getReaderFileFingerprint(path12.join(this.indexPath, "codebase.db"), true)
|
|
7549
|
+
};
|
|
7550
|
+
}
|
|
7551
|
+
refreshReaderArtifacts() {
|
|
7552
|
+
if (this.initializationMode !== "reader" || !this.configuredProviderInfo) {
|
|
7553
|
+
return;
|
|
7554
|
+
}
|
|
7555
|
+
const previousFingerprint = this.readerArtifactFingerprint;
|
|
7556
|
+
const currentFingerprint = this.captureReaderArtifactFingerprint();
|
|
7557
|
+
const issues = new Map(this.readIssues.map((issue) => [issue.component, issue]));
|
|
7558
|
+
const retryDue = (component) => issues.has(component) && Date.now() >= (this.readerArtifactRetryAfter.get(component) ?? 0);
|
|
7559
|
+
const vectorsChanged = !previousFingerprint || currentFingerprint.vectors !== previousFingerprint.vectors;
|
|
7560
|
+
const keywordChanged = !previousFingerprint || currentFingerprint.keyword !== previousFingerprint.keyword;
|
|
7561
|
+
const databaseChanged = !previousFingerprint || currentFingerprint.database !== previousFingerprint.database;
|
|
7562
|
+
const databaseReplaced = !previousFingerprint || currentFingerprint.databaseIdentity !== previousFingerprint.databaseIdentity;
|
|
7563
|
+
if (previousFingerprint && !vectorsChanged && !keywordChanged && !databaseChanged && !Array.from(issues.keys()).some(retryDue)) {
|
|
7564
|
+
return;
|
|
7565
|
+
}
|
|
7566
|
+
const setIssue = (component, message, error) => {
|
|
7567
|
+
if (!issues.has(component)) {
|
|
7568
|
+
this.logger.warn(message, error === void 0 ? void 0 : { error: getErrorMessage2(error) });
|
|
7569
|
+
}
|
|
7570
|
+
issues.set(component, this.createReadIssue(component, message));
|
|
7571
|
+
this.readerArtifactRetryAfter.set(component, Date.now() + READER_ARTIFACT_RETRY_INTERVAL_MS);
|
|
7572
|
+
};
|
|
7573
|
+
const storePath = path12.join(this.indexPath, "vectors");
|
|
7574
|
+
const vectorMetadataPath = `${storePath}.meta.json`;
|
|
7575
|
+
const invertedIndexPath = path12.join(this.indexPath, "inverted-index.json");
|
|
7576
|
+
const dbPath = path12.join(this.indexPath, "codebase.db");
|
|
7577
|
+
if (vectorsChanged || retryDue("vectors")) {
|
|
7578
|
+
const vectorStoreExists = (0, import_fs8.existsSync)(storePath);
|
|
7579
|
+
const vectorMetadataExists = (0, import_fs8.existsSync)(vectorMetadataPath);
|
|
7580
|
+
if (vectorStoreExists && vectorMetadataExists) {
|
|
7581
|
+
try {
|
|
7582
|
+
const store = new VectorStore(storePath, this.configuredProviderInfo.modelInfo.dimensions);
|
|
7583
|
+
store.loadStrict();
|
|
7584
|
+
this.store = store;
|
|
7585
|
+
issues.delete("vectors");
|
|
7586
|
+
this.readerArtifactRetryAfter.delete("vectors");
|
|
7587
|
+
} catch (error) {
|
|
7588
|
+
setIssue("vectors", this.getVectorReadIssueMessage(), error);
|
|
7589
|
+
}
|
|
7590
|
+
} else if (vectorStoreExists !== vectorMetadataExists || issues.has("vectors")) {
|
|
7591
|
+
setIssue("vectors", this.getVectorReadIssueMessage());
|
|
7592
|
+
}
|
|
7593
|
+
}
|
|
7594
|
+
if (keywordChanged || retryDue("keyword") || !(0, import_fs8.existsSync)(invertedIndexPath) && (this.store?.count() ?? 0) > 0) {
|
|
7595
|
+
if ((0, import_fs8.existsSync)(invertedIndexPath)) {
|
|
7596
|
+
try {
|
|
7597
|
+
const invertedIndex = new InvertedIndex(invertedIndexPath);
|
|
7598
|
+
invertedIndex.load();
|
|
7599
|
+
this.invertedIndex = invertedIndex;
|
|
7600
|
+
issues.delete("keyword");
|
|
7601
|
+
this.readerArtifactRetryAfter.delete("keyword");
|
|
7602
|
+
} catch (error) {
|
|
7603
|
+
setIssue("keyword", this.getKeywordReadIssueMessage(), error);
|
|
7604
|
+
}
|
|
7605
|
+
} else if ((this.store?.count() ?? 0) > 0 || issues.has("keyword")) {
|
|
7606
|
+
setIssue("keyword", this.getKeywordReadIssueMessage());
|
|
7607
|
+
}
|
|
7608
|
+
}
|
|
7609
|
+
if (databaseReplaced || databaseChanged && issues.has("database") || retryDue("database")) {
|
|
7610
|
+
if ((0, import_fs8.existsSync)(dbPath)) {
|
|
7611
|
+
try {
|
|
7612
|
+
const database = Database.openReadOnly(dbPath);
|
|
7613
|
+
if (this.database) {
|
|
7614
|
+
this.retiredDatabases.push(this.database);
|
|
7615
|
+
}
|
|
7616
|
+
this.database = database;
|
|
7617
|
+
issues.delete("database");
|
|
7618
|
+
this.readerArtifactRetryAfter.delete("database");
|
|
7619
|
+
} catch (error) {
|
|
7620
|
+
setIssue("database", this.getDatabaseReadIssueMessage(), error);
|
|
7621
|
+
}
|
|
7622
|
+
} else if ((this.store?.count() ?? 0) > 0 || issues.has("database")) {
|
|
7623
|
+
setIssue("database", this.getDatabaseReadIssueMessage());
|
|
7624
|
+
}
|
|
7625
|
+
}
|
|
7626
|
+
if (!issues.has("database")) {
|
|
7627
|
+
try {
|
|
7628
|
+
this.indexCompatibility = this.validateIndexCompatibility(this.configuredProviderInfo);
|
|
7629
|
+
} catch (error) {
|
|
7630
|
+
setIssue("database", this.getDatabaseReadIssueMessage(), error);
|
|
7631
|
+
}
|
|
7632
|
+
}
|
|
7633
|
+
this.readIssues = Array.from(issues.values());
|
|
7634
|
+
this.readerArtifactFingerprint = currentFingerprint;
|
|
7635
|
+
}
|
|
7636
|
+
refreshInactiveWriterArtifacts() {
|
|
7637
|
+
if (this.initializationMode !== "writer" || this.activeIndexLease) {
|
|
7638
|
+
return true;
|
|
7639
|
+
}
|
|
7640
|
+
const previousFingerprint = this.writerArtifactFingerprint;
|
|
7641
|
+
const currentFingerprint = this.captureReaderArtifactFingerprint();
|
|
7642
|
+
const retryDue = this.readIssues.some(
|
|
7643
|
+
(issue) => Date.now() >= (this.readerArtifactRetryAfter.get(issue.component) ?? 0)
|
|
7644
|
+
);
|
|
7645
|
+
const artifactsChanged = !previousFingerprint || currentFingerprint.vectors !== previousFingerprint.vectors || currentFingerprint.keyword !== previousFingerprint.keyword || currentFingerprint.database !== previousFingerprint.database || currentFingerprint.databaseIdentity !== previousFingerprint.databaseIdentity;
|
|
7646
|
+
if (!artifactsChanged && !retryDue) {
|
|
7647
|
+
return true;
|
|
7648
|
+
}
|
|
7649
|
+
if (!previousFingerprint || currentFingerprint.databaseIdentity !== previousFingerprint.databaseIdentity) {
|
|
7650
|
+
return false;
|
|
7651
|
+
}
|
|
7652
|
+
this.initializationMode = "reader";
|
|
7653
|
+
this.readerArtifactFingerprint = previousFingerprint;
|
|
7654
|
+
try {
|
|
7655
|
+
this.refreshReaderArtifacts();
|
|
7656
|
+
this.writerArtifactFingerprint = this.readerArtifactFingerprint ?? currentFingerprint;
|
|
7657
|
+
} finally {
|
|
7658
|
+
this.readerArtifactFingerprint = null;
|
|
7659
|
+
this.initializationMode = "writer";
|
|
7660
|
+
}
|
|
7661
|
+
return true;
|
|
7662
|
+
}
|
|
7663
|
+
async initializeUnlocked(mode, recoveredOwners = [], options = {}) {
|
|
7664
|
+
if (mode === "writer") {
|
|
7665
|
+
this.requireActiveLease();
|
|
7666
|
+
}
|
|
7667
|
+
this.readIssues = [];
|
|
7668
|
+
this.readerArtifactRetryAfter.clear();
|
|
6895
7669
|
if (this.config.embeddingProvider === "custom") {
|
|
6896
7670
|
if (!this.config.customProvider) {
|
|
6897
7671
|
throw new Error("embeddingProvider is 'custom' but customProvider config is missing.");
|
|
@@ -6923,36 +7697,103 @@ var Indexer = class {
|
|
|
6923
7697
|
});
|
|
6924
7698
|
}
|
|
6925
7699
|
}
|
|
6926
|
-
await import_fs7.promises.mkdir(this.indexPath, { recursive: true });
|
|
6927
7700
|
const dimensions = this.configuredProviderInfo.modelInfo.dimensions;
|
|
6928
|
-
const storePath =
|
|
6929
|
-
|
|
6930
|
-
const
|
|
6931
|
-
|
|
6932
|
-
|
|
6933
|
-
|
|
6934
|
-
|
|
6935
|
-
|
|
6936
|
-
|
|
6937
|
-
|
|
6938
|
-
|
|
6939
|
-
|
|
6940
|
-
|
|
7701
|
+
const storePath = path12.join(this.indexPath, "vectors");
|
|
7702
|
+
const vectorMetadataPath = `${storePath}.meta.json`;
|
|
7703
|
+
const invertedIndexPath = path12.join(this.indexPath, "inverted-index.json");
|
|
7704
|
+
const dbPath = path12.join(this.indexPath, "codebase.db");
|
|
7705
|
+
let dbIsNew = !(0, import_fs8.existsSync)(dbPath);
|
|
7706
|
+
const readerArtifactFingerprint = mode === "reader" ? this.captureReaderArtifactFingerprint() : null;
|
|
7707
|
+
if (mode === "writer") {
|
|
7708
|
+
await import_fs8.promises.mkdir(this.indexPath, { recursive: true });
|
|
7709
|
+
if (recoveredOwners.length > 0 && this.config.scope === "project" && !this.isLocalProjectIndexPath()) {
|
|
7710
|
+
throw new Error(
|
|
7711
|
+
"Interrupted indexing recovery is unsafe while using an inherited worktree index. Run index_codebase with force=true to create a local project index boundary."
|
|
7712
|
+
);
|
|
7713
|
+
}
|
|
7714
|
+
for (const recoveredOwner of recoveredOwners) {
|
|
7715
|
+
recoverLeaseArtifacts(this.indexPath, recoveredOwner, [
|
|
7716
|
+
storePath,
|
|
7717
|
+
`${storePath}.meta.json`
|
|
7718
|
+
]);
|
|
7719
|
+
}
|
|
7720
|
+
if (recoveredOwners.length > 0 && this.config.scope === "project") {
|
|
7721
|
+
await this.resetLocalIndexArtifacts();
|
|
7722
|
+
}
|
|
7723
|
+
this.store = new VectorStore(storePath, dimensions);
|
|
7724
|
+
if ((0, import_fs8.existsSync)(storePath) || (0, import_fs8.existsSync)(vectorMetadataPath)) {
|
|
7725
|
+
this.store.load();
|
|
6941
7726
|
}
|
|
6942
7727
|
this.invertedIndex = new InvertedIndex(invertedIndexPath);
|
|
6943
|
-
|
|
6944
|
-
|
|
6945
|
-
|
|
6946
|
-
|
|
6947
|
-
|
|
6948
|
-
|
|
6949
|
-
|
|
6950
|
-
|
|
7728
|
+
try {
|
|
7729
|
+
this.invertedIndex.load();
|
|
7730
|
+
} catch {
|
|
7731
|
+
if ((0, import_fs8.existsSync)(invertedIndexPath)) {
|
|
7732
|
+
await import_fs8.promises.unlink(invertedIndexPath);
|
|
7733
|
+
}
|
|
7734
|
+
this.invertedIndex = new InvertedIndex(invertedIndexPath);
|
|
7735
|
+
}
|
|
7736
|
+
try {
|
|
7737
|
+
this.database = new Database(dbPath);
|
|
7738
|
+
} catch (error) {
|
|
7739
|
+
if (!await this.tryResetCorruptedIndex("initializing index database", error)) {
|
|
7740
|
+
throw error;
|
|
7741
|
+
}
|
|
7742
|
+
this.store = new VectorStore(storePath, dimensions);
|
|
7743
|
+
this.invertedIndex = new InvertedIndex(invertedIndexPath);
|
|
7744
|
+
this.database = new Database(dbPath);
|
|
7745
|
+
dbIsNew = true;
|
|
6951
7746
|
}
|
|
7747
|
+
} else {
|
|
6952
7748
|
this.store = new VectorStore(storePath, dimensions);
|
|
7749
|
+
const vectorStoreExists = (0, import_fs8.existsSync)(storePath);
|
|
7750
|
+
const vectorMetadataExists = (0, import_fs8.existsSync)(vectorMetadataPath);
|
|
7751
|
+
const vectorReadFailureMessage = this.getVectorReadIssueMessage();
|
|
7752
|
+
if (vectorStoreExists !== vectorMetadataExists) {
|
|
7753
|
+
this.recordReadIssue("vectors", vectorReadFailureMessage);
|
|
7754
|
+
} else if (vectorStoreExists) {
|
|
7755
|
+
try {
|
|
7756
|
+
this.store.loadStrict();
|
|
7757
|
+
} catch (error) {
|
|
7758
|
+
this.recordReadIssue("vectors", vectorReadFailureMessage, error);
|
|
7759
|
+
this.store = new VectorStore(storePath, dimensions);
|
|
7760
|
+
}
|
|
7761
|
+
}
|
|
6953
7762
|
this.invertedIndex = new InvertedIndex(invertedIndexPath);
|
|
6954
|
-
|
|
6955
|
-
|
|
7763
|
+
if ((0, import_fs8.existsSync)(invertedIndexPath)) {
|
|
7764
|
+
try {
|
|
7765
|
+
this.invertedIndex.load();
|
|
7766
|
+
} catch (error) {
|
|
7767
|
+
this.recordReadIssue(
|
|
7768
|
+
"keyword",
|
|
7769
|
+
this.getKeywordReadIssueMessage(),
|
|
7770
|
+
error
|
|
7771
|
+
);
|
|
7772
|
+
this.invertedIndex = new InvertedIndex(invertedIndexPath);
|
|
7773
|
+
}
|
|
7774
|
+
} else if (this.store.count() > 0) {
|
|
7775
|
+
this.recordReadIssue("keyword", this.getKeywordReadIssueMessage());
|
|
7776
|
+
}
|
|
7777
|
+
if ((0, import_fs8.existsSync)(dbPath)) {
|
|
7778
|
+
try {
|
|
7779
|
+
this.database = Database.openReadOnly(dbPath);
|
|
7780
|
+
} catch (error) {
|
|
7781
|
+
this.recordReadIssue(
|
|
7782
|
+
"database",
|
|
7783
|
+
this.getDatabaseReadIssueMessage(),
|
|
7784
|
+
error
|
|
7785
|
+
);
|
|
7786
|
+
this.database = Database.createEmptyReadOnly();
|
|
7787
|
+
}
|
|
7788
|
+
} else {
|
|
7789
|
+
this.database = Database.createEmptyReadOnly();
|
|
7790
|
+
if (this.store.count() > 0) {
|
|
7791
|
+
this.recordReadIssue(
|
|
7792
|
+
"database",
|
|
7793
|
+
`Index database is missing for the published vectors. ${this.getDatabaseReadIssueMessage()}`
|
|
7794
|
+
);
|
|
7795
|
+
}
|
|
7796
|
+
}
|
|
6956
7797
|
}
|
|
6957
7798
|
if (isGitRepo(this.projectRoot)) {
|
|
6958
7799
|
this.currentBranch = getBranchOrDefault(this.projectRoot);
|
|
@@ -6966,10 +7807,10 @@ var Indexer = class {
|
|
|
6966
7807
|
this.baseBranch = "default";
|
|
6967
7808
|
this.logger.branch("debug", "Not a git repository, using default branch");
|
|
6968
7809
|
}
|
|
6969
|
-
if (
|
|
6970
|
-
await this.
|
|
7810
|
+
if (mode === "writer" && recoveredOwners.length > 0) {
|
|
7811
|
+
await this.recoverFromInterruptedIndexingUnlocked(recoveredOwners);
|
|
6971
7812
|
}
|
|
6972
|
-
if (dbIsNew && this.store.count() > 0) {
|
|
7813
|
+
if (mode === "writer" && dbIsNew && this.store.count() > 0) {
|
|
6973
7814
|
this.migrateFromLegacyIndex();
|
|
6974
7815
|
}
|
|
6975
7816
|
this.loadFileHashCache();
|
|
@@ -6981,9 +7822,11 @@ var Indexer = class {
|
|
|
6981
7822
|
configuredProviderInfo: this.configuredProviderInfo
|
|
6982
7823
|
});
|
|
6983
7824
|
}
|
|
6984
|
-
if (this.config.indexing.autoGc) {
|
|
7825
|
+
if (mode === "writer" && this.config.indexing.autoGc && !options.skipAutoGc) {
|
|
6985
7826
|
await this.maybeRunAutoGc();
|
|
6986
7827
|
}
|
|
7828
|
+
this.initializationMode = mode;
|
|
7829
|
+
this.readerArtifactFingerprint = readerArtifactFingerprint;
|
|
6987
7830
|
}
|
|
6988
7831
|
async maybeRunAutoGc() {
|
|
6989
7832
|
if (!this.database) return;
|
|
@@ -7000,7 +7843,7 @@ var Indexer = class {
|
|
|
7000
7843
|
}
|
|
7001
7844
|
}
|
|
7002
7845
|
if (shouldRunGc) {
|
|
7003
|
-
const result = await this.
|
|
7846
|
+
const result = await this.healthCheckUnlocked();
|
|
7004
7847
|
if (result.warning) {
|
|
7005
7848
|
this.database.setMetadata(STARTUP_WARNING_METADATA_KEY, result.warning);
|
|
7006
7849
|
} else {
|
|
@@ -7022,7 +7865,7 @@ var Indexer = class {
|
|
|
7022
7865
|
if (await this.tryResetCorruptedIndex("running automatic orphan garbage collection", error)) {
|
|
7023
7866
|
return {
|
|
7024
7867
|
resetCorruptedIndex: true,
|
|
7025
|
-
warning: this.getCorruptedIndexWarning(
|
|
7868
|
+
warning: this.getCorruptedIndexWarning(path12.join(this.indexPath, "codebase.db"))
|
|
7026
7869
|
};
|
|
7027
7870
|
}
|
|
7028
7871
|
throw error;
|
|
@@ -7037,28 +7880,29 @@ var Indexer = class {
|
|
|
7037
7880
|
return;
|
|
7038
7881
|
}
|
|
7039
7882
|
const retainedEntries = store.getAllMetadata().filter(({ key }) => !excludedSet.has(key));
|
|
7040
|
-
const storeBasePath =
|
|
7041
|
-
const storeIndexPath =
|
|
7883
|
+
const storeBasePath = path12.join(this.indexPath, "vectors");
|
|
7884
|
+
const storeIndexPath = storeBasePath;
|
|
7042
7885
|
const storeMetadataPath = `${storeBasePath}.meta.json`;
|
|
7043
|
-
const
|
|
7044
|
-
const
|
|
7886
|
+
const lease = this.requireActiveLease();
|
|
7887
|
+
const backupIndexPath = createLeaseTemporaryPath(storeIndexPath, lease.owner, "bak");
|
|
7888
|
+
const backupMetadataPath = createLeaseTemporaryPath(storeMetadataPath, lease.owner, "bak");
|
|
7045
7889
|
let backedUpIndex = false;
|
|
7046
7890
|
let backedUpMetadata = false;
|
|
7047
7891
|
let rebuiltCount = 0;
|
|
7048
7892
|
let skippedCount = 0;
|
|
7049
|
-
if ((0,
|
|
7050
|
-
(0,
|
|
7893
|
+
if ((0, import_fs8.existsSync)(backupIndexPath)) {
|
|
7894
|
+
(0, import_fs8.unlinkSync)(backupIndexPath);
|
|
7051
7895
|
}
|
|
7052
|
-
if ((0,
|
|
7053
|
-
(0,
|
|
7896
|
+
if ((0, import_fs8.existsSync)(backupMetadataPath)) {
|
|
7897
|
+
(0, import_fs8.unlinkSync)(backupMetadataPath);
|
|
7054
7898
|
}
|
|
7055
7899
|
try {
|
|
7056
|
-
if ((0,
|
|
7057
|
-
(0,
|
|
7900
|
+
if ((0, import_fs8.existsSync)(storeIndexPath)) {
|
|
7901
|
+
(0, import_fs8.renameSync)(storeIndexPath, backupIndexPath);
|
|
7058
7902
|
backedUpIndex = true;
|
|
7059
7903
|
}
|
|
7060
|
-
if ((0,
|
|
7061
|
-
(0,
|
|
7904
|
+
if ((0, import_fs8.existsSync)(storeMetadataPath)) {
|
|
7905
|
+
(0, import_fs8.renameSync)(storeMetadataPath, backupMetadataPath);
|
|
7062
7906
|
backedUpMetadata = true;
|
|
7063
7907
|
}
|
|
7064
7908
|
store.clear();
|
|
@@ -7078,11 +7922,11 @@ var Indexer = class {
|
|
|
7078
7922
|
rebuiltCount += 1;
|
|
7079
7923
|
}
|
|
7080
7924
|
store.save();
|
|
7081
|
-
if (backedUpIndex && (0,
|
|
7082
|
-
(0,
|
|
7925
|
+
if (backedUpIndex && (0, import_fs8.existsSync)(backupIndexPath)) {
|
|
7926
|
+
(0, import_fs8.unlinkSync)(backupIndexPath);
|
|
7083
7927
|
}
|
|
7084
|
-
if (backedUpMetadata && (0,
|
|
7085
|
-
(0,
|
|
7928
|
+
if (backedUpMetadata && (0, import_fs8.existsSync)(backupMetadataPath)) {
|
|
7929
|
+
(0, import_fs8.unlinkSync)(backupMetadataPath);
|
|
7086
7930
|
}
|
|
7087
7931
|
this.logger.gc("info", "Rebuilt vector store to avoid native remove", {
|
|
7088
7932
|
excludedChunks: excludedSet.size,
|
|
@@ -7094,17 +7938,17 @@ var Indexer = class {
|
|
|
7094
7938
|
store.clear();
|
|
7095
7939
|
} catch {
|
|
7096
7940
|
}
|
|
7097
|
-
if ((0,
|
|
7098
|
-
(0,
|
|
7941
|
+
if ((0, import_fs8.existsSync)(storeIndexPath)) {
|
|
7942
|
+
(0, import_fs8.unlinkSync)(storeIndexPath);
|
|
7099
7943
|
}
|
|
7100
|
-
if ((0,
|
|
7101
|
-
(0,
|
|
7944
|
+
if ((0, import_fs8.existsSync)(storeMetadataPath)) {
|
|
7945
|
+
(0, import_fs8.unlinkSync)(storeMetadataPath);
|
|
7102
7946
|
}
|
|
7103
|
-
if (backedUpIndex && (0,
|
|
7104
|
-
(0,
|
|
7947
|
+
if (backedUpIndex && (0, import_fs8.existsSync)(backupIndexPath)) {
|
|
7948
|
+
(0, import_fs8.renameSync)(backupIndexPath, storeIndexPath);
|
|
7105
7949
|
}
|
|
7106
|
-
if (backedUpMetadata && (0,
|
|
7107
|
-
(0,
|
|
7950
|
+
if (backedUpMetadata && (0, import_fs8.existsSync)(backupMetadataPath)) {
|
|
7951
|
+
(0, import_fs8.renameSync)(backupMetadataPath, storeMetadataPath);
|
|
7108
7952
|
}
|
|
7109
7953
|
if (backedUpIndex || backedUpMetadata) {
|
|
7110
7954
|
store.load();
|
|
@@ -7118,11 +7962,37 @@ var Indexer = class {
|
|
|
7118
7962
|
}
|
|
7119
7963
|
return `Detected a corrupted local SQLite index at ${dbPath} and reset the local index. Run index_codebase to rebuild search data.`;
|
|
7120
7964
|
}
|
|
7965
|
+
async resetLocalIndexArtifacts() {
|
|
7966
|
+
this.store = null;
|
|
7967
|
+
this.invertedIndex = null;
|
|
7968
|
+
this.database?.close();
|
|
7969
|
+
this.database = null;
|
|
7970
|
+
this.indexCompatibility = null;
|
|
7971
|
+
this.initializationMode = "none";
|
|
7972
|
+
this.readIssues = [];
|
|
7973
|
+
this.readerArtifactFingerprint = null;
|
|
7974
|
+
this.writerArtifactFingerprint = null;
|
|
7975
|
+
this.readerArtifactRetryAfter.clear();
|
|
7976
|
+
this.fileHashCache.clear();
|
|
7977
|
+
const resetPaths = [
|
|
7978
|
+
path12.join(this.indexPath, "codebase.db"),
|
|
7979
|
+
path12.join(this.indexPath, "codebase.db-shm"),
|
|
7980
|
+
path12.join(this.indexPath, "codebase.db-wal"),
|
|
7981
|
+
path12.join(this.indexPath, "vectors"),
|
|
7982
|
+
path12.join(this.indexPath, "vectors.usearch"),
|
|
7983
|
+
path12.join(this.indexPath, "vectors.meta.json"),
|
|
7984
|
+
path12.join(this.indexPath, "inverted-index.json"),
|
|
7985
|
+
path12.join(this.indexPath, "file-hashes.json"),
|
|
7986
|
+
path12.join(this.indexPath, "failed-batches.json")
|
|
7987
|
+
];
|
|
7988
|
+
await Promise.all(resetPaths.map((targetPath) => import_fs8.promises.rm(targetPath, { recursive: true, force: true })));
|
|
7989
|
+
await import_fs8.promises.mkdir(this.indexPath, { recursive: true });
|
|
7990
|
+
}
|
|
7121
7991
|
async tryResetCorruptedIndex(stage, error) {
|
|
7122
7992
|
if (!isSqliteCorruptionError(error)) {
|
|
7123
7993
|
return false;
|
|
7124
7994
|
}
|
|
7125
|
-
const dbPath =
|
|
7995
|
+
const dbPath = path12.join(this.indexPath, "codebase.db");
|
|
7126
7996
|
const warning = this.getCorruptedIndexWarning(dbPath);
|
|
7127
7997
|
const errorMessage = getErrorMessage2(error);
|
|
7128
7998
|
if (this.config.scope === "global") {
|
|
@@ -7138,30 +8008,7 @@ var Indexer = class {
|
|
|
7138
8008
|
dbPath,
|
|
7139
8009
|
error: errorMessage
|
|
7140
8010
|
});
|
|
7141
|
-
this.
|
|
7142
|
-
this.invertedIndex = null;
|
|
7143
|
-
this.database?.close();
|
|
7144
|
-
this.database = null;
|
|
7145
|
-
this.indexCompatibility = null;
|
|
7146
|
-
this.fileHashCache.clear();
|
|
7147
|
-
const resetPaths = [
|
|
7148
|
-
path11.join(this.indexPath, "codebase.db"),
|
|
7149
|
-
path11.join(this.indexPath, "codebase.db-shm"),
|
|
7150
|
-
path11.join(this.indexPath, "codebase.db-wal"),
|
|
7151
|
-
path11.join(this.indexPath, "vectors.usearch"),
|
|
7152
|
-
path11.join(this.indexPath, "inverted-index.json"),
|
|
7153
|
-
path11.join(this.indexPath, "file-hashes.json"),
|
|
7154
|
-
path11.join(this.indexPath, "failed-batches.json"),
|
|
7155
|
-
path11.join(this.indexPath, "indexing.lock"),
|
|
7156
|
-
path11.join(this.indexPath, "vectors")
|
|
7157
|
-
];
|
|
7158
|
-
await Promise.all(resetPaths.map(async (targetPath) => {
|
|
7159
|
-
try {
|
|
7160
|
-
await import_fs7.promises.rm(targetPath, { recursive: true, force: true });
|
|
7161
|
-
} catch {
|
|
7162
|
-
}
|
|
7163
|
-
}));
|
|
7164
|
-
await import_fs7.promises.mkdir(this.indexPath, { recursive: true });
|
|
8011
|
+
await this.resetLocalIndexArtifacts();
|
|
7165
8012
|
return true;
|
|
7166
8013
|
}
|
|
7167
8014
|
migrateFromLegacyIndex() {
|
|
@@ -7280,8 +8127,62 @@ var Indexer = class {
|
|
|
7280
8127
|
return this.indexCompatibility;
|
|
7281
8128
|
}
|
|
7282
8129
|
async ensureInitialized() {
|
|
8130
|
+
let initializedReader = false;
|
|
8131
|
+
while (true) {
|
|
8132
|
+
if (this.initializationPromise) {
|
|
8133
|
+
await this.initializationPromise;
|
|
8134
|
+
}
|
|
8135
|
+
if (!this.isInitializedFor("reader")) {
|
|
8136
|
+
await this.initialize();
|
|
8137
|
+
initializedReader = true;
|
|
8138
|
+
continue;
|
|
8139
|
+
}
|
|
8140
|
+
if (this.initializationMode === "writer" && !this.activeIndexLease && !initializedReader) {
|
|
8141
|
+
if (!this.refreshInactiveWriterArtifacts()) {
|
|
8142
|
+
this.resetLoadedIndexState(true);
|
|
8143
|
+
await this.initialize();
|
|
8144
|
+
initializedReader = true;
|
|
8145
|
+
continue;
|
|
8146
|
+
}
|
|
8147
|
+
}
|
|
8148
|
+
if (this.initializationMode === "reader" && !initializedReader) {
|
|
8149
|
+
this.refreshReaderArtifacts();
|
|
8150
|
+
}
|
|
8151
|
+
const state = this.requireLoadedIndexState();
|
|
8152
|
+
return {
|
|
8153
|
+
...state,
|
|
8154
|
+
readIssues: [...this.readIssues],
|
|
8155
|
+
compatibility: this.indexCompatibility ?? this.validateIndexCompatibility(state.configuredProviderInfo)
|
|
8156
|
+
};
|
|
8157
|
+
}
|
|
8158
|
+
}
|
|
8159
|
+
async ensureInitializedUnlocked(recoveredOwners = []) {
|
|
8160
|
+
this.requireActiveLease();
|
|
8161
|
+
if (this.initializationPromise) {
|
|
8162
|
+
await this.initializationPromise;
|
|
8163
|
+
}
|
|
8164
|
+
if (recoveredOwners.length > 0 || !this.isInitializedFor("writer")) {
|
|
8165
|
+
const retireReaderDatabase = this.initializationMode === "reader";
|
|
8166
|
+
this.resetLoadedIndexState(retireReaderDatabase);
|
|
8167
|
+
await this.initializeOnce("writer", recoveredOwners, { skipAutoGc: true });
|
|
8168
|
+
} else {
|
|
8169
|
+
this.refreshLoadedIndexState();
|
|
8170
|
+
}
|
|
8171
|
+
if (this.config.indexing.autoGc) {
|
|
8172
|
+
await this.maybeRunAutoGc();
|
|
8173
|
+
}
|
|
8174
|
+
return this.requireLoadedIndexState();
|
|
8175
|
+
}
|
|
8176
|
+
requireReadableComponents(readIssues, ...components) {
|
|
8177
|
+
const componentSet = new Set(components);
|
|
8178
|
+
const issues = readIssues.filter((issue) => issue.blocking && componentSet.has(issue.component));
|
|
8179
|
+
if (issues.length > 0) {
|
|
8180
|
+
throw new Error(issues.map((issue) => issue.message).join(" "));
|
|
8181
|
+
}
|
|
8182
|
+
}
|
|
8183
|
+
requireLoadedIndexState() {
|
|
7283
8184
|
if (!this.store || !this.provider || !this.invertedIndex || !this.configuredProviderInfo || !this.database) {
|
|
7284
|
-
|
|
8185
|
+
throw new Error("Index state is not initialized");
|
|
7285
8186
|
}
|
|
7286
8187
|
return {
|
|
7287
8188
|
store: this.store,
|
|
@@ -7305,7 +8206,12 @@ var Indexer = class {
|
|
|
7305
8206
|
return createCostEstimate(files, configuredProviderInfo);
|
|
7306
8207
|
}
|
|
7307
8208
|
async index(onProgress) {
|
|
7308
|
-
|
|
8209
|
+
return this.withIndexMutationLease("index", async (recoveredOwners) => {
|
|
8210
|
+
return this.indexUnlocked(onProgress, recoveredOwners);
|
|
8211
|
+
});
|
|
8212
|
+
}
|
|
8213
|
+
async indexUnlocked(onProgress, recoveredOwners = [], stateReady = false) {
|
|
8214
|
+
const { store, provider, invertedIndex, database, configuredProviderInfo } = stateReady ? this.requireLoadedIndexState() : await this.ensureInitializedUnlocked(recoveredOwners);
|
|
7309
8215
|
const scopedRoots = this.config.scope === "global" ? this.getScopedRoots() : null;
|
|
7310
8216
|
const branchCatalogKey = this.getBranchCatalogKey();
|
|
7311
8217
|
const forceScopedReembed = scopedRoots !== null && database.getMetadata(this.getProjectForceReembedMetadataKey()) === "true";
|
|
@@ -7315,7 +8221,6 @@ var Indexer = class {
|
|
|
7315
8221
|
`${this.indexCompatibility?.reason} Run index_codebase with force=true to rebuild the index.`
|
|
7316
8222
|
);
|
|
7317
8223
|
}
|
|
7318
|
-
this.acquireIndexingLock();
|
|
7319
8224
|
this.logger.recordIndexingStart();
|
|
7320
8225
|
this.logger.info("Starting indexing", { projectRoot: this.projectRoot });
|
|
7321
8226
|
const startTime = Date.now();
|
|
@@ -7366,7 +8271,7 @@ var Indexer = class {
|
|
|
7366
8271
|
unchangedFilePaths.add(f.path);
|
|
7367
8272
|
this.logger.recordCacheHit();
|
|
7368
8273
|
} else {
|
|
7369
|
-
const content = await
|
|
8274
|
+
const content = await import_fs8.promises.readFile(f.path, "utf-8");
|
|
7370
8275
|
changedFiles.push({ path: f.path, content, hash: currentHash });
|
|
7371
8276
|
this.logger.recordCacheMiss();
|
|
7372
8277
|
}
|
|
@@ -7464,7 +8369,7 @@ var Indexer = class {
|
|
|
7464
8369
|
for (const parsed of parsedFiles) {
|
|
7465
8370
|
currentFilePaths.add(parsed.path);
|
|
7466
8371
|
if (parsed.chunks.length === 0) {
|
|
7467
|
-
const relativePath =
|
|
8372
|
+
const relativePath = path12.relative(this.projectRoot, parsed.path);
|
|
7468
8373
|
stats.parseFailures.push(relativePath);
|
|
7469
8374
|
}
|
|
7470
8375
|
let fileChunkCount = 0;
|
|
@@ -7664,7 +8569,9 @@ var Indexer = class {
|
|
|
7664
8569
|
database.addChunksToBranchBatch(branchCatalogKey, Array.from(currentChunkIds));
|
|
7665
8570
|
database.clearBranchSymbols(branchCatalogKey);
|
|
7666
8571
|
database.addSymbolsToBranchBatch(branchCatalogKey, Array.from(allSymbolIds));
|
|
7667
|
-
|
|
8572
|
+
const vectorPath = path12.join(this.indexPath, "vectors");
|
|
8573
|
+
const shouldFingerprintLegacyPair = !store.hasFingerprint() && (0, import_fs8.existsSync)(vectorPath) && (0, import_fs8.existsSync)(`${vectorPath}.meta.json`);
|
|
8574
|
+
if (backfilledBlameMetadata || shouldFingerprintLegacyPair) {
|
|
7668
8575
|
store.save();
|
|
7669
8576
|
}
|
|
7670
8577
|
if (scopedRoots) {
|
|
@@ -7685,7 +8592,6 @@ var Indexer = class {
|
|
|
7685
8592
|
chunksProcessed: 0,
|
|
7686
8593
|
totalChunks: 0
|
|
7687
8594
|
});
|
|
7688
|
-
this.releaseIndexingLock();
|
|
7689
8595
|
return stats;
|
|
7690
8596
|
}
|
|
7691
8597
|
if (pendingChunks.length === 0) {
|
|
@@ -7694,7 +8600,7 @@ var Indexer = class {
|
|
|
7694
8600
|
database.clearBranchSymbols(branchCatalogKey);
|
|
7695
8601
|
database.addSymbolsToBranchBatch(branchCatalogKey, Array.from(allSymbolIds));
|
|
7696
8602
|
store.save();
|
|
7697
|
-
|
|
8603
|
+
this.saveInvertedIndex(invertedIndex);
|
|
7698
8604
|
if (scopedRoots) {
|
|
7699
8605
|
this.replaceScopedFileHashCache(currentFileHashes, scopedRoots);
|
|
7700
8606
|
this.clearScopedFailedBatches(scopedRoots);
|
|
@@ -7713,7 +8619,6 @@ var Indexer = class {
|
|
|
7713
8619
|
chunksProcessed: 0,
|
|
7714
8620
|
totalChunks: 0
|
|
7715
8621
|
});
|
|
7716
|
-
this.releaseIndexingLock();
|
|
7717
8622
|
return stats;
|
|
7718
8623
|
}
|
|
7719
8624
|
onProgress?.({
|
|
@@ -7944,7 +8849,7 @@ var Indexer = class {
|
|
|
7944
8849
|
database.clearBranchSymbols(branchCatalogKey);
|
|
7945
8850
|
database.addSymbolsToBranchBatch(branchCatalogKey, Array.from(allSymbolIds));
|
|
7946
8851
|
store.save();
|
|
7947
|
-
|
|
8852
|
+
this.saveInvertedIndex(invertedIndex);
|
|
7948
8853
|
if (scopedRoots) {
|
|
7949
8854
|
this.replaceScopedFileHashCache(currentFileHashes, scopedRoots);
|
|
7950
8855
|
} else {
|
|
@@ -7996,7 +8901,6 @@ var Indexer = class {
|
|
|
7996
8901
|
chunksProcessed: stats.indexedChunks,
|
|
7997
8902
|
totalChunks: pendingChunks.length
|
|
7998
8903
|
});
|
|
7999
|
-
this.releaseIndexingLock();
|
|
8000
8904
|
return stats;
|
|
8001
8905
|
}
|
|
8002
8906
|
async getQueryEmbedding(query, provider) {
|
|
@@ -8062,8 +8966,8 @@ var Indexer = class {
|
|
|
8062
8966
|
return intersection / union;
|
|
8063
8967
|
}
|
|
8064
8968
|
async search(query, limit, options) {
|
|
8065
|
-
const { store, provider, database } = await this.ensureInitialized();
|
|
8066
|
-
|
|
8969
|
+
const { store, provider, invertedIndex, database, readIssues, compatibility } = await this.ensureInitialized();
|
|
8970
|
+
this.requireReadableComponents(readIssues, "vectors", "database");
|
|
8067
8971
|
if (!compatibility.compatible) {
|
|
8068
8972
|
throw new Error(
|
|
8069
8973
|
`${compatibility.reason ?? "Index is incompatible with current embedding provider."} A possible solution is to run index_codebase with force=true to rebuild the index.`
|
|
@@ -8077,6 +8981,7 @@ var Indexer = class {
|
|
|
8077
8981
|
const maxResults = limit ?? this.config.search.maxResults;
|
|
8078
8982
|
const hybridWeight = options?.hybridWeight ?? this.config.search.hybridWeight;
|
|
8079
8983
|
const fusionStrategy = this.config.search.fusionStrategy;
|
|
8984
|
+
const effectiveHybridWeight = fusionStrategy === "weighted" && readIssues.some((issue) => issue.component === "keyword") ? 0 : hybridWeight;
|
|
8080
8985
|
const rrfK = this.config.search.rrfK;
|
|
8081
8986
|
const rerankTopN = this.config.search.rerankTopN;
|
|
8082
8987
|
const filterByBranch = options?.filterByBranch ?? true;
|
|
@@ -8085,7 +8990,7 @@ var Indexer = class {
|
|
|
8085
8990
|
this.logger.search("debug", "Starting search", {
|
|
8086
8991
|
query,
|
|
8087
8992
|
maxResults,
|
|
8088
|
-
hybridWeight,
|
|
8993
|
+
hybridWeight: effectiveHybridWeight,
|
|
8089
8994
|
fusionStrategy,
|
|
8090
8995
|
rrfK,
|
|
8091
8996
|
rerankTopN,
|
|
@@ -8099,7 +9004,7 @@ var Indexer = class {
|
|
|
8099
9004
|
const semanticResults = store.search(embedding, maxResults * 4);
|
|
8100
9005
|
const vectorMs = import_perf_hooks.performance.now() - vectorStartTime;
|
|
8101
9006
|
const keywordStartTime = import_perf_hooks.performance.now();
|
|
8102
|
-
const keywordResults = await this.keywordSearch(query, maxResults * 4);
|
|
9007
|
+
const keywordResults = await this.keywordSearch(query, maxResults * 4, store, invertedIndex);
|
|
8103
9008
|
const keywordMs = import_perf_hooks.performance.now() - keywordStartTime;
|
|
8104
9009
|
let branchChunkIds = null;
|
|
8105
9010
|
if (filterByBranch && (this.config.scope === "global" || this.currentBranch !== "default")) {
|
|
@@ -8136,7 +9041,7 @@ var Indexer = class {
|
|
|
8136
9041
|
rrfK,
|
|
8137
9042
|
rerankTopN,
|
|
8138
9043
|
limit: maxResults,
|
|
8139
|
-
hybridWeight,
|
|
9044
|
+
hybridWeight: effectiveHybridWeight,
|
|
8140
9045
|
prioritizeSourcePaths: sourceIntent
|
|
8141
9046
|
});
|
|
8142
9047
|
const rerankedCombined = await this.rerankCandidatesWithApi(query, combined, {
|
|
@@ -8210,7 +9115,7 @@ var Indexer = class {
|
|
|
8210
9115
|
let contextEndLine = r.metadata.endLine;
|
|
8211
9116
|
if (!metadataOnly && this.config.search.includeContext) {
|
|
8212
9117
|
try {
|
|
8213
|
-
const fileContent = await
|
|
9118
|
+
const fileContent = await import_fs8.promises.readFile(
|
|
8214
9119
|
r.metadata.filePath,
|
|
8215
9120
|
"utf-8"
|
|
8216
9121
|
);
|
|
@@ -8236,8 +9141,7 @@ var Indexer = class {
|
|
|
8236
9141
|
})
|
|
8237
9142
|
);
|
|
8238
9143
|
}
|
|
8239
|
-
async keywordSearch(query, limit) {
|
|
8240
|
-
const { store, invertedIndex } = await this.ensureInitialized();
|
|
9144
|
+
async keywordSearch(query, limit, store, invertedIndex) {
|
|
8241
9145
|
const scores = invertedIndex.search(query);
|
|
8242
9146
|
if (scores.size === 0) {
|
|
8243
9147
|
return [];
|
|
@@ -8255,24 +9159,54 @@ var Indexer = class {
|
|
|
8255
9159
|
return results.slice(0, limit);
|
|
8256
9160
|
}
|
|
8257
9161
|
async getStatus() {
|
|
8258
|
-
const { store, configuredProviderInfo, database } = await this.ensureInitialized();
|
|
9162
|
+
const { store, configuredProviderInfo, database, readIssues, compatibility } = await this.ensureInitialized();
|
|
8259
9163
|
const failedBatchesCount = this.getFailedBatchesCount();
|
|
9164
|
+
const vectorCount = store.count();
|
|
9165
|
+
const statusReadIssues = [...readIssues];
|
|
9166
|
+
let startupWarning = "";
|
|
9167
|
+
if (!statusReadIssues.some((issue) => issue.component === "database")) {
|
|
9168
|
+
try {
|
|
9169
|
+
startupWarning = database.getMetadata(STARTUP_WARNING_METADATA_KEY) ?? "";
|
|
9170
|
+
} catch (error) {
|
|
9171
|
+
const message = this.getDatabaseReadIssueMessage();
|
|
9172
|
+
statusReadIssues.push(this.createReadIssue("database", message));
|
|
9173
|
+
if (!this.readIssues.some((issue) => issue.component === "database")) {
|
|
9174
|
+
this.recordReadIssue("database", message, error);
|
|
9175
|
+
}
|
|
9176
|
+
}
|
|
9177
|
+
}
|
|
9178
|
+
const readWarning = statusReadIssues.map((issue) => issue.message).join(" ");
|
|
9179
|
+
const warning = [readWarning, startupWarning].filter((message) => message.length > 0).join(" ");
|
|
9180
|
+
const hasBlockingReadIssue = statusReadIssues.some((issue) => issue.blocking);
|
|
8260
9181
|
return {
|
|
8261
|
-
indexed:
|
|
8262
|
-
vectorCount
|
|
9182
|
+
indexed: vectorCount > 0 && !hasBlockingReadIssue,
|
|
9183
|
+
vectorCount,
|
|
8263
9184
|
provider: configuredProviderInfo.provider,
|
|
8264
9185
|
model: configuredProviderInfo.modelInfo.model,
|
|
8265
9186
|
indexPath: this.indexPath,
|
|
8266
9187
|
currentBranch: this.currentBranch,
|
|
8267
9188
|
baseBranch: this.baseBranch,
|
|
8268
|
-
compatibility
|
|
9189
|
+
compatibility,
|
|
8269
9190
|
failedBatchesCount,
|
|
8270
9191
|
failedBatchesPath: failedBatchesCount > 0 ? this.failedBatchesPath : void 0,
|
|
8271
|
-
warning:
|
|
9192
|
+
warning: warning || void 0
|
|
8272
9193
|
};
|
|
8273
9194
|
}
|
|
9195
|
+
async forceIndex(onProgress) {
|
|
9196
|
+
return this.withIndexMutationLease("force-index", async (recoveredOwners) => {
|
|
9197
|
+
await this.ensureInitializedUnlocked(recoveredOwners);
|
|
9198
|
+
await this.clearIndexUnlocked();
|
|
9199
|
+
return this.indexUnlocked(onProgress, [], true);
|
|
9200
|
+
});
|
|
9201
|
+
}
|
|
8274
9202
|
async clearIndex() {
|
|
8275
|
-
|
|
9203
|
+
await this.withIndexMutationLease("clear", async (recoveredOwners) => {
|
|
9204
|
+
await this.ensureInitializedUnlocked(recoveredOwners);
|
|
9205
|
+
await this.clearIndexUnlocked();
|
|
9206
|
+
});
|
|
9207
|
+
}
|
|
9208
|
+
async clearIndexUnlocked() {
|
|
9209
|
+
const { store, invertedIndex, database } = this.requireLoadedIndexState();
|
|
8276
9210
|
if (this.config.scope === "global") {
|
|
8277
9211
|
store.load();
|
|
8278
9212
|
invertedIndex.load();
|
|
@@ -8299,7 +9233,7 @@ var Indexer = class {
|
|
|
8299
9233
|
store.clear();
|
|
8300
9234
|
store.save();
|
|
8301
9235
|
invertedIndex.clear();
|
|
8302
|
-
|
|
9236
|
+
this.saveInvertedIndex(invertedIndex);
|
|
8303
9237
|
this.fileHashCache.clear();
|
|
8304
9238
|
this.saveFileHashCache();
|
|
8305
9239
|
database.clearAllIndexedData();
|
|
@@ -8323,14 +9257,7 @@ var Indexer = class {
|
|
|
8323
9257
|
this.indexCompatibility = compatibility;
|
|
8324
9258
|
return;
|
|
8325
9259
|
}
|
|
8326
|
-
|
|
8327
|
-
if (this.host !== "opencode") {
|
|
8328
|
-
localProjectIndexPaths.push(path11.join(this.projectRoot, getHostProjectIndexRelativePath("opencode")));
|
|
8329
|
-
}
|
|
8330
|
-
const isLocalProjectIndex = localProjectIndexPaths.some(
|
|
8331
|
-
(localPath) => path11.resolve(this.indexPath) === path11.resolve(localPath)
|
|
8332
|
-
);
|
|
8333
|
-
if (!isLocalProjectIndex) {
|
|
9260
|
+
if (!this.isLocalProjectIndexPath()) {
|
|
8334
9261
|
throw new Error(
|
|
8335
9262
|
"Project-scoped force rebuild is unsafe while using an inherited worktree index. Create a local project config boundary before clearing the index."
|
|
8336
9263
|
);
|
|
@@ -8338,7 +9265,7 @@ var Indexer = class {
|
|
|
8338
9265
|
store.clear();
|
|
8339
9266
|
store.save();
|
|
8340
9267
|
invertedIndex.clear();
|
|
8341
|
-
|
|
9268
|
+
this.saveInvertedIndex(invertedIndex);
|
|
8342
9269
|
this.fileHashCache.clear();
|
|
8343
9270
|
this.saveFileHashCache();
|
|
8344
9271
|
database.clearAllIndexedData();
|
|
@@ -8356,7 +9283,13 @@ var Indexer = class {
|
|
|
8356
9283
|
this.indexCompatibility = this.validateIndexCompatibility(this.configuredProviderInfo);
|
|
8357
9284
|
}
|
|
8358
9285
|
async healthCheck() {
|
|
8359
|
-
|
|
9286
|
+
return this.withIndexMutationLease("health-check", async (recoveredOwners) => {
|
|
9287
|
+
await this.ensureInitializedUnlocked(recoveredOwners);
|
|
9288
|
+
return this.healthCheckUnlocked();
|
|
9289
|
+
});
|
|
9290
|
+
}
|
|
9291
|
+
async healthCheckUnlocked() {
|
|
9292
|
+
const { store, invertedIndex, database } = this.requireLoadedIndexState();
|
|
8360
9293
|
this.logger.gc("info", "Starting health check");
|
|
8361
9294
|
const allMetadata = store.getAllMetadata();
|
|
8362
9295
|
const filePathsToChunkKeys = /* @__PURE__ */ new Map();
|
|
@@ -8369,7 +9302,7 @@ var Indexer = class {
|
|
|
8369
9302
|
const removedChunkKeys = [];
|
|
8370
9303
|
const chunkKeysByRemovedFile = /* @__PURE__ */ new Map();
|
|
8371
9304
|
for (const [filePath, chunkKeys] of filePathsToChunkKeys) {
|
|
8372
|
-
if (!(0,
|
|
9305
|
+
if (!(0, import_fs8.existsSync)(filePath)) {
|
|
8373
9306
|
chunkKeysByRemovedFile.set(filePath, chunkKeys);
|
|
8374
9307
|
for (const key of chunkKeys) {
|
|
8375
9308
|
removedChunkKeys.push(key);
|
|
@@ -8394,7 +9327,7 @@ var Indexer = class {
|
|
|
8394
9327
|
const removedCount = removedChunkKeys.length;
|
|
8395
9328
|
if (removedCount > 0) {
|
|
8396
9329
|
store.save();
|
|
8397
|
-
|
|
9330
|
+
this.saveInvertedIndex(invertedIndex);
|
|
8398
9331
|
}
|
|
8399
9332
|
let gcOrphanEmbeddings;
|
|
8400
9333
|
let gcOrphanChunks;
|
|
@@ -8409,7 +9342,7 @@ var Indexer = class {
|
|
|
8409
9342
|
if (!await this.tryResetCorruptedIndex("running index health check", error)) {
|
|
8410
9343
|
throw error;
|
|
8411
9344
|
}
|
|
8412
|
-
await this.
|
|
9345
|
+
await this.initializeUnlocked("writer", [], { skipAutoGc: true });
|
|
8413
9346
|
return {
|
|
8414
9347
|
removed: 0,
|
|
8415
9348
|
filePaths: [],
|
|
@@ -8418,7 +9351,7 @@ var Indexer = class {
|
|
|
8418
9351
|
gcOrphanSymbols: 0,
|
|
8419
9352
|
gcOrphanCallEdges: 0,
|
|
8420
9353
|
resetCorruptedIndex: true,
|
|
8421
|
-
warning: this.getCorruptedIndexWarning(
|
|
9354
|
+
warning: this.getCorruptedIndexWarning(path12.join(this.indexPath, "codebase.db"))
|
|
8422
9355
|
};
|
|
8423
9356
|
}
|
|
8424
9357
|
this.logger.recordGc(removedCount, gcOrphanChunks, gcOrphanEmbeddings);
|
|
@@ -8431,7 +9364,13 @@ var Indexer = class {
|
|
|
8431
9364
|
return { removed: removedCount, filePaths: removedFilePaths, gcOrphanEmbeddings, gcOrphanChunks, gcOrphanSymbols, gcOrphanCallEdges };
|
|
8432
9365
|
}
|
|
8433
9366
|
async retryFailedBatches() {
|
|
8434
|
-
|
|
9367
|
+
return this.withIndexMutationLease("retry-failed-batches", async (recoveredOwners) => {
|
|
9368
|
+
await this.ensureInitializedUnlocked(recoveredOwners);
|
|
9369
|
+
return this.retryFailedBatchesUnlocked();
|
|
9370
|
+
});
|
|
9371
|
+
}
|
|
9372
|
+
async retryFailedBatchesUnlocked() {
|
|
9373
|
+
const { store, provider, invertedIndex, database, configuredProviderInfo } = this.requireLoadedIndexState();
|
|
8435
9374
|
const maxChunkTokens = getSafeEmbeddingChunkTokenLimit(configuredProviderInfo);
|
|
8436
9375
|
const providerRateLimits = this.getProviderRateLimits(configuredProviderInfo.provider);
|
|
8437
9376
|
const roots = this.config.scope === "global" ? this.getScopedRoots() : null;
|
|
@@ -8595,7 +9534,7 @@ var Indexer = class {
|
|
|
8595
9534
|
}
|
|
8596
9535
|
if (succeeded > 0) {
|
|
8597
9536
|
store.save();
|
|
8598
|
-
|
|
9537
|
+
this.saveInvertedIndex(invertedIndex);
|
|
8599
9538
|
}
|
|
8600
9539
|
if (roots && succeeded > 0 && persistedStillFailing.length === 0 && this.hasProjectForceReembedPending()) {
|
|
8601
9540
|
database.deleteMetadata(this.getProjectForceReembedMetadataKey());
|
|
@@ -8623,15 +9562,16 @@ var Indexer = class {
|
|
|
8623
9562
|
}
|
|
8624
9563
|
}
|
|
8625
9564
|
async getDatabaseStats() {
|
|
8626
|
-
const { database } = await this.ensureInitialized();
|
|
9565
|
+
const { database, readIssues } = await this.ensureInitialized();
|
|
9566
|
+
this.requireReadableComponents(readIssues, "database");
|
|
8627
9567
|
return database.getStats();
|
|
8628
9568
|
}
|
|
8629
9569
|
getLogger() {
|
|
8630
9570
|
return this.logger;
|
|
8631
9571
|
}
|
|
8632
9572
|
async findSimilar(code, limit = this.config.search.maxResults, options) {
|
|
8633
|
-
const { store, provider, database } = await this.ensureInitialized();
|
|
8634
|
-
|
|
9573
|
+
const { store, provider, database, readIssues, compatibility } = await this.ensureInitialized();
|
|
9574
|
+
this.requireReadableComponents(readIssues, "vectors", "database");
|
|
8635
9575
|
if (!compatibility.compatible) {
|
|
8636
9576
|
throw new Error(
|
|
8637
9577
|
`${compatibility.reason ?? "Index is incompatible with current embedding provider."} Run index_codebase with force=true to rebuild the index.`
|
|
@@ -8721,7 +9661,7 @@ var Indexer = class {
|
|
|
8721
9661
|
let content = "";
|
|
8722
9662
|
if (this.config.search.includeContext) {
|
|
8723
9663
|
try {
|
|
8724
|
-
const fileContent = await
|
|
9664
|
+
const fileContent = await import_fs8.promises.readFile(
|
|
8725
9665
|
r.metadata.filePath,
|
|
8726
9666
|
"utf-8"
|
|
8727
9667
|
);
|
|
@@ -8745,7 +9685,8 @@ var Indexer = class {
|
|
|
8745
9685
|
);
|
|
8746
9686
|
}
|
|
8747
9687
|
async getCallers(targetName, callTypeFilter) {
|
|
8748
|
-
const { database } = await this.ensureInitialized();
|
|
9688
|
+
const { database, readIssues } = await this.ensureInitialized();
|
|
9689
|
+
this.requireReadableComponents(readIssues, "database");
|
|
8749
9690
|
const seen = /* @__PURE__ */ new Set();
|
|
8750
9691
|
const results = [];
|
|
8751
9692
|
for (const branchKey of this.getBranchCatalogKeys()) {
|
|
@@ -8759,7 +9700,8 @@ var Indexer = class {
|
|
|
8759
9700
|
return results;
|
|
8760
9701
|
}
|
|
8761
9702
|
async getCallees(symbolId, callTypeFilter) {
|
|
8762
|
-
const { database } = await this.ensureInitialized();
|
|
9703
|
+
const { database, readIssues } = await this.ensureInitialized();
|
|
9704
|
+
this.requireReadableComponents(readIssues, "database");
|
|
8763
9705
|
const seen = /* @__PURE__ */ new Set();
|
|
8764
9706
|
const results = [];
|
|
8765
9707
|
for (const branchKey of this.getBranchCatalogKeys()) {
|
|
@@ -8773,43 +9715,50 @@ var Indexer = class {
|
|
|
8773
9715
|
return results;
|
|
8774
9716
|
}
|
|
8775
9717
|
async findCallPath(fromName, toName, maxDepth) {
|
|
8776
|
-
const { database } = await this.ensureInitialized();
|
|
9718
|
+
const { database, readIssues } = await this.ensureInitialized();
|
|
9719
|
+
this.requireReadableComponents(readIssues, "database");
|
|
8777
9720
|
let shortest = [];
|
|
8778
9721
|
for (const branchKey of this.getBranchCatalogKeys()) {
|
|
8779
|
-
const
|
|
8780
|
-
if (
|
|
8781
|
-
shortest =
|
|
9722
|
+
const path27 = database.findShortestPath(fromName, toName, branchKey, maxDepth);
|
|
9723
|
+
if (path27.length > 0 && (shortest.length === 0 || path27.length < shortest.length)) {
|
|
9724
|
+
shortest = path27;
|
|
8782
9725
|
}
|
|
8783
9726
|
}
|
|
8784
9727
|
return shortest;
|
|
8785
9728
|
}
|
|
8786
9729
|
async getSymbolsForBranch(branch) {
|
|
8787
|
-
const { database } = await this.ensureInitialized();
|
|
9730
|
+
const { database, readIssues } = await this.ensureInitialized();
|
|
9731
|
+
this.requireReadableComponents(readIssues, "database");
|
|
8788
9732
|
const resolvedBranch = branch ?? this.getBranchCatalogKey();
|
|
8789
9733
|
return database.getSymbolsForBranch(resolvedBranch);
|
|
8790
9734
|
}
|
|
8791
9735
|
async getSymbolsForFiles(filePaths, branch) {
|
|
8792
|
-
const { database } = await this.ensureInitialized();
|
|
9736
|
+
const { database, readIssues } = await this.ensureInitialized();
|
|
9737
|
+
this.requireReadableComponents(readIssues, "database");
|
|
8793
9738
|
const resolvedBranch = branch ?? this.getBranchCatalogKey();
|
|
8794
9739
|
return database.getSymbolsForFiles(filePaths, resolvedBranch);
|
|
8795
9740
|
}
|
|
8796
9741
|
async getTransitiveReachability(rootSymbolIds, direction, maxDepth) {
|
|
8797
|
-
const { database } = await this.ensureInitialized();
|
|
9742
|
+
const { database, readIssues } = await this.ensureInitialized();
|
|
9743
|
+
this.requireReadableComponents(readIssues, "database");
|
|
8798
9744
|
const branch = this.getBranchCatalogKey();
|
|
8799
9745
|
return database.getTransitiveReachability(rootSymbolIds, branch, direction, maxDepth);
|
|
8800
9746
|
}
|
|
8801
9747
|
async detectCommunities(branch, symbolIds) {
|
|
8802
|
-
const { database } = await this.ensureInitialized();
|
|
9748
|
+
const { database, readIssues } = await this.ensureInitialized();
|
|
9749
|
+
this.requireReadableComponents(readIssues, "database");
|
|
8803
9750
|
const resolvedBranch = branch ?? this.getBranchCatalogKey();
|
|
8804
9751
|
return database.detectCommunities(resolvedBranch, symbolIds);
|
|
8805
9752
|
}
|
|
8806
9753
|
async computeCentrality(branch) {
|
|
8807
|
-
const { database } = await this.ensureInitialized();
|
|
9754
|
+
const { database, readIssues } = await this.ensureInitialized();
|
|
9755
|
+
this.requireReadableComponents(readIssues, "database");
|
|
8808
9756
|
const resolvedBranch = branch ?? this.getBranchCatalogKey();
|
|
8809
9757
|
return database.computeCentrality(resolvedBranch);
|
|
8810
9758
|
}
|
|
8811
9759
|
async getPrImpact(opts) {
|
|
8812
|
-
const { database } = await this.ensureInitialized();
|
|
9760
|
+
const { database, readIssues } = await this.ensureInitialized();
|
|
9761
|
+
this.requireReadableComponents(readIssues, "database");
|
|
8813
9762
|
const execFileAsync3 = (0, import_util3.promisify)(import_child_process3.execFile);
|
|
8814
9763
|
const changedFilesResult = await getChangedFiles({
|
|
8815
9764
|
pr: opts.pr,
|
|
@@ -8832,7 +9781,7 @@ var Indexer = class {
|
|
|
8832
9781
|
"Run index_codebase first to build the call graph and symbol index for this branch."
|
|
8833
9782
|
);
|
|
8834
9783
|
}
|
|
8835
|
-
const absoluteChangedFiles = changedFiles.map((f) =>
|
|
9784
|
+
const absoluteChangedFiles = changedFiles.map((f) => path12.resolve(this.projectRoot, f));
|
|
8836
9785
|
const directSymbols = database.getSymbolsForFiles(absoluteChangedFiles, branchKey);
|
|
8837
9786
|
const directIds = directSymbols.map((s) => s.id);
|
|
8838
9787
|
const direction = opts.direction ?? "both";
|
|
@@ -8915,7 +9864,7 @@ var Indexer = class {
|
|
|
8915
9864
|
projectRoot: this.projectRoot,
|
|
8916
9865
|
baseBranch: this.baseBranch
|
|
8917
9866
|
});
|
|
8918
|
-
const otherAbsolute = otherChanged.files.map((f) =>
|
|
9867
|
+
const otherAbsolute = otherChanged.files.map((f) => path12.resolve(this.projectRoot, f));
|
|
8919
9868
|
const otherBranchKey = this.getBranchCatalogKeyFor(openPr.headRefName);
|
|
8920
9869
|
const otherSymbols = database.getSymbolsForFiles(otherAbsolute, otherBranchKey);
|
|
8921
9870
|
const otherLabels = /* @__PURE__ */ new Set();
|
|
@@ -8965,7 +9914,8 @@ var Indexer = class {
|
|
|
8965
9914
|
};
|
|
8966
9915
|
}
|
|
8967
9916
|
async getVisualizationData(options) {
|
|
8968
|
-
const { database, store } = await this.ensureInitialized();
|
|
9917
|
+
const { database, store, readIssues } = await this.ensureInitialized();
|
|
9918
|
+
this.requireReadableComponents(readIssues, "vectors", "database");
|
|
8969
9919
|
const seenSymbols = /* @__PURE__ */ new Map();
|
|
8970
9920
|
const seenEdges = /* @__PURE__ */ new Map();
|
|
8971
9921
|
for (const branchKey of this.getBranchCatalogKeys()) {
|
|
@@ -8978,12 +9928,12 @@ var Indexer = class {
|
|
|
8978
9928
|
if (meta.filePath) filePaths.add(meta.filePath);
|
|
8979
9929
|
}
|
|
8980
9930
|
const directory = options?.directory?.replace(/\/$/, "");
|
|
8981
|
-
const absoluteDirectoryFilter = directory ?
|
|
9931
|
+
const absoluteDirectoryFilter = directory ? path12.resolve(this.projectRoot, directory) : void 0;
|
|
8982
9932
|
for (const filePath of filePaths) {
|
|
8983
9933
|
if (directory) {
|
|
8984
|
-
const absoluteFilePath =
|
|
9934
|
+
const absoluteFilePath = path12.resolve(filePath);
|
|
8985
9935
|
const matchesRelative = filePath === directory || filePath.startsWith(directory + "/");
|
|
8986
|
-
const matchesProjectRelative = absoluteDirectoryFilter !== void 0 && (absoluteFilePath === absoluteDirectoryFilter || absoluteFilePath.startsWith(absoluteDirectoryFilter +
|
|
9936
|
+
const matchesProjectRelative = absoluteDirectoryFilter !== void 0 && (absoluteFilePath === absoluteDirectoryFilter || absoluteFilePath.startsWith(absoluteDirectoryFilter + path12.sep));
|
|
8987
9937
|
if (!matchesRelative && !matchesProjectRelative) {
|
|
8988
9938
|
continue;
|
|
8989
9939
|
}
|
|
@@ -9005,12 +9955,23 @@ var Indexer = class {
|
|
|
9005
9955
|
return { symbols: [...seenSymbols.values()], edges: [...seenEdges.values()] };
|
|
9006
9956
|
}
|
|
9007
9957
|
async close() {
|
|
9008
|
-
|
|
9958
|
+
this.database?.close();
|
|
9959
|
+
for (const database of this.retiredDatabases) {
|
|
9960
|
+
database.close();
|
|
9961
|
+
}
|
|
9962
|
+
this.retiredDatabases = [];
|
|
9009
9963
|
this.database = null;
|
|
9010
9964
|
this.store = null;
|
|
9011
9965
|
this.invertedIndex = null;
|
|
9012
9966
|
this.provider = null;
|
|
9013
9967
|
this.reranker = null;
|
|
9968
|
+
this.configuredProviderInfo = null;
|
|
9969
|
+
this.indexCompatibility = null;
|
|
9970
|
+
this.initializationMode = "none";
|
|
9971
|
+
this.readIssues = [];
|
|
9972
|
+
this.readerArtifactFingerprint = null;
|
|
9973
|
+
this.writerArtifactFingerprint = null;
|
|
9974
|
+
this.readerArtifactRetryAfter.clear();
|
|
9014
9975
|
}
|
|
9015
9976
|
};
|
|
9016
9977
|
|
|
@@ -9261,15 +10222,15 @@ function computeEvalMetrics(queries, perQuery, embeddingCallCount, embeddingToke
|
|
|
9261
10222
|
}
|
|
9262
10223
|
|
|
9263
10224
|
// src/eval/runner-config.ts
|
|
9264
|
-
var
|
|
9265
|
-
var
|
|
9266
|
-
var
|
|
10225
|
+
var import_fs9 = require("fs");
|
|
10226
|
+
var os5 = __toESM(require("os"), 1);
|
|
10227
|
+
var path14 = __toESM(require("path"), 1);
|
|
9267
10228
|
|
|
9268
10229
|
// src/config/rebase.ts
|
|
9269
|
-
var
|
|
10230
|
+
var path13 = __toESM(require("path"), 1);
|
|
9270
10231
|
function isWithinRoot(rootDir, targetPath) {
|
|
9271
|
-
const relativePath =
|
|
9272
|
-
return relativePath === "" || !relativePath.startsWith("..") && !
|
|
10232
|
+
const relativePath = path13.relative(rootDir, targetPath);
|
|
10233
|
+
return relativePath === "" || !relativePath.startsWith("..") && !path13.isAbsolute(relativePath);
|
|
9273
10234
|
}
|
|
9274
10235
|
function rebasePathEntries(values, fromDir, toDir) {
|
|
9275
10236
|
if (!Array.isArray(values)) {
|
|
@@ -9277,10 +10238,10 @@ function rebasePathEntries(values, fromDir, toDir) {
|
|
|
9277
10238
|
}
|
|
9278
10239
|
return values.filter((value) => typeof value === "string").map((value) => {
|
|
9279
10240
|
const trimmed = value.trim();
|
|
9280
|
-
if (!trimmed ||
|
|
10241
|
+
if (!trimmed || path13.isAbsolute(trimmed)) {
|
|
9281
10242
|
return trimmed;
|
|
9282
10243
|
}
|
|
9283
|
-
return normalizePathSeparators(
|
|
10244
|
+
return normalizePathSeparators(path13.normalize(path13.relative(toDir, path13.resolve(fromDir, trimmed))));
|
|
9284
10245
|
}).filter(Boolean);
|
|
9285
10246
|
}
|
|
9286
10247
|
function resolveInheritedKnowledgeBaseEntries(values, sourceRoot, targetRoot) {
|
|
@@ -9292,17 +10253,17 @@ function resolveInheritedKnowledgeBaseEntries(values, sourceRoot, targetRoot) {
|
|
|
9292
10253
|
if (!trimmed) {
|
|
9293
10254
|
return trimmed;
|
|
9294
10255
|
}
|
|
9295
|
-
if (
|
|
10256
|
+
if (path13.isAbsolute(trimmed)) {
|
|
9296
10257
|
if (isWithinRoot(sourceRoot, trimmed)) {
|
|
9297
|
-
return normalizePathSeparators(
|
|
10258
|
+
return normalizePathSeparators(path13.normalize(path13.relative(sourceRoot, trimmed) || "."));
|
|
9298
10259
|
}
|
|
9299
|
-
return
|
|
10260
|
+
return path13.normalize(trimmed);
|
|
9300
10261
|
}
|
|
9301
|
-
const resolvedFromSource =
|
|
10262
|
+
const resolvedFromSource = path13.resolve(sourceRoot, trimmed);
|
|
9302
10263
|
if (isWithinRoot(sourceRoot, resolvedFromSource)) {
|
|
9303
|
-
return normalizePathSeparators(
|
|
10264
|
+
return normalizePathSeparators(path13.normalize(trimmed));
|
|
9304
10265
|
}
|
|
9305
|
-
return normalizePathSeparators(
|
|
10266
|
+
return normalizePathSeparators(path13.normalize(path13.relative(targetRoot, resolvedFromSource)));
|
|
9306
10267
|
}).filter(Boolean);
|
|
9307
10268
|
}
|
|
9308
10269
|
|
|
@@ -9340,7 +10301,7 @@ function validateEvalConfigShape(rawConfig, filePath) {
|
|
|
9340
10301
|
}
|
|
9341
10302
|
function parseJsonConfigFile(filePath) {
|
|
9342
10303
|
try {
|
|
9343
|
-
return validateEvalConfigShape(JSON.parse((0,
|
|
10304
|
+
return validateEvalConfigShape(JSON.parse((0, import_fs9.readFileSync)(filePath, "utf-8")), filePath);
|
|
9344
10305
|
} catch (error) {
|
|
9345
10306
|
if (error instanceof Error && error.message.startsWith("Eval config at ")) {
|
|
9346
10307
|
throw error;
|
|
@@ -9350,20 +10311,20 @@ function parseJsonConfigFile(filePath) {
|
|
|
9350
10311
|
}
|
|
9351
10312
|
}
|
|
9352
10313
|
function toAbsolute(projectRoot, maybeRelative) {
|
|
9353
|
-
return
|
|
10314
|
+
return path14.isAbsolute(maybeRelative) ? maybeRelative : path14.join(projectRoot, maybeRelative);
|
|
9354
10315
|
}
|
|
9355
10316
|
function isProjectScopedConfigPath(configPath) {
|
|
9356
|
-
return
|
|
10317
|
+
return path14.basename(configPath) === "codebase-index.json" && path14.basename(path14.dirname(configPath)) === ".opencode";
|
|
9357
10318
|
}
|
|
9358
10319
|
function normalizeEvalConfigKnowledgeBases(rawConfig, projectRoot, resolvedConfigPath) {
|
|
9359
10320
|
const config = rawConfig && typeof rawConfig === "object" ? { ...rawConfig } : {};
|
|
9360
10321
|
const rebaseEntries = (values) => isProjectScopedConfigPath(resolvedConfigPath) ? resolveInheritedKnowledgeBaseEntries(
|
|
9361
10322
|
values,
|
|
9362
|
-
|
|
10323
|
+
path14.dirname(path14.dirname(resolvedConfigPath)),
|
|
9363
10324
|
projectRoot
|
|
9364
10325
|
) : rebasePathEntries(
|
|
9365
10326
|
values,
|
|
9366
|
-
|
|
10327
|
+
path14.dirname(resolvedConfigPath),
|
|
9367
10328
|
projectRoot
|
|
9368
10329
|
);
|
|
9369
10330
|
if (Array.isArray(config.knowledgeBases)) {
|
|
@@ -9376,7 +10337,7 @@ function normalizeEvalConfigKnowledgeBases(rawConfig, projectRoot, resolvedConfi
|
|
|
9376
10337
|
}
|
|
9377
10338
|
function loadRawConfig(projectRoot, configPath) {
|
|
9378
10339
|
const fromPath = configPath ? toAbsolute(projectRoot, configPath) : null;
|
|
9379
|
-
if (fromPath && (0,
|
|
10340
|
+
if (fromPath && (0, import_fs9.existsSync)(fromPath)) {
|
|
9380
10341
|
return normalizeEvalConfigKnowledgeBases(
|
|
9381
10342
|
parseJsonConfigFile(fromPath),
|
|
9382
10343
|
projectRoot,
|
|
@@ -9384,15 +10345,15 @@ function loadRawConfig(projectRoot, configPath) {
|
|
|
9384
10345
|
);
|
|
9385
10346
|
}
|
|
9386
10347
|
const projectConfig = resolveProjectConfigPath(projectRoot);
|
|
9387
|
-
if ((0,
|
|
10348
|
+
if ((0, import_fs9.existsSync)(projectConfig)) {
|
|
9388
10349
|
return normalizeEvalConfigKnowledgeBases(
|
|
9389
10350
|
parseJsonConfigFile(projectConfig),
|
|
9390
10351
|
projectRoot,
|
|
9391
10352
|
projectConfig
|
|
9392
10353
|
);
|
|
9393
10354
|
}
|
|
9394
|
-
const globalConfig =
|
|
9395
|
-
if ((0,
|
|
10355
|
+
const globalConfig = path14.join(os5.homedir(), ".config", "opencode", "codebase-index.json");
|
|
10356
|
+
if ((0, import_fs9.existsSync)(globalConfig)) {
|
|
9396
10357
|
return parseJsonConfigFile(globalConfig);
|
|
9397
10358
|
}
|
|
9398
10359
|
return {};
|
|
@@ -9401,24 +10362,24 @@ function getIndexRootPath(projectRoot, scope) {
|
|
|
9401
10362
|
return scope === "global" ? getGlobalIndexPath() : resolveProjectIndexPath(projectRoot, scope);
|
|
9402
10363
|
}
|
|
9403
10364
|
function getLocalProjectIndexRoot(projectRoot) {
|
|
9404
|
-
return
|
|
10365
|
+
return path14.join(projectRoot, ".opencode", "index");
|
|
9405
10366
|
}
|
|
9406
10367
|
function getLocalProjectConfigPath(projectRoot) {
|
|
9407
|
-
return
|
|
10368
|
+
return path14.join(projectRoot, ".opencode", "codebase-index.json");
|
|
9408
10369
|
}
|
|
9409
10370
|
function clearIndexRoot(projectRoot, scope) {
|
|
9410
10371
|
const indexRoot = scope === "global" ? getIndexRootPath(projectRoot, scope) : getLocalProjectIndexRoot(projectRoot);
|
|
9411
|
-
if ((0,
|
|
9412
|
-
(0,
|
|
10372
|
+
if ((0, import_fs9.existsSync)(indexRoot)) {
|
|
10373
|
+
(0, import_fs9.rmSync)(indexRoot, { recursive: true, force: true });
|
|
9413
10374
|
}
|
|
9414
10375
|
}
|
|
9415
10376
|
function ensureLocalEvalProjectConfig(projectRoot, configPath) {
|
|
9416
10377
|
const localConfigPath = getLocalProjectConfigPath(projectRoot);
|
|
9417
10378
|
const resolvedConfigPath = configPath ? toAbsolute(projectRoot, configPath) : resolveProjectConfigPath(projectRoot);
|
|
9418
|
-
if (!configPath && (0,
|
|
10379
|
+
if (!configPath && (0, import_fs9.existsSync)(localConfigPath)) {
|
|
9419
10380
|
return localConfigPath;
|
|
9420
10381
|
}
|
|
9421
|
-
if (!(0,
|
|
10382
|
+
if (!(0, import_fs9.existsSync)(resolvedConfigPath) || resolvedConfigPath === localConfigPath) {
|
|
9422
10383
|
return resolvedConfigPath;
|
|
9423
10384
|
}
|
|
9424
10385
|
const sourceConfig = normalizeEvalConfigKnowledgeBases(
|
|
@@ -9426,8 +10387,8 @@ function ensureLocalEvalProjectConfig(projectRoot, configPath) {
|
|
|
9426
10387
|
projectRoot,
|
|
9427
10388
|
resolvedConfigPath
|
|
9428
10389
|
);
|
|
9429
|
-
(0,
|
|
9430
|
-
(0,
|
|
10390
|
+
(0, import_fs9.mkdirSync)(path14.dirname(localConfigPath), { recursive: true });
|
|
10391
|
+
(0, import_fs9.writeFileSync)(localConfigPath, JSON.stringify(sourceConfig, null, 2), "utf-8");
|
|
9431
10392
|
return localConfigPath;
|
|
9432
10393
|
}
|
|
9433
10394
|
function loadParsedConfig(projectRoot, configPath) {
|
|
@@ -9460,9 +10421,9 @@ function getEmbeddingCostPer1MTokens(embeddingProvider) {
|
|
|
9460
10421
|
}
|
|
9461
10422
|
|
|
9462
10423
|
// src/eval/schema.ts
|
|
9463
|
-
var
|
|
10424
|
+
var import_fs10 = require("fs");
|
|
9464
10425
|
function parseJsonFile(filePath) {
|
|
9465
|
-
const content = (0,
|
|
10426
|
+
const content = (0, import_fs10.readFileSync)(filePath, "utf-8");
|
|
9466
10427
|
try {
|
|
9467
10428
|
return JSON.parse(content);
|
|
9468
10429
|
} catch (error) {
|
|
@@ -9476,23 +10437,23 @@ function isRecord2(value) {
|
|
|
9476
10437
|
function isStringArray3(value) {
|
|
9477
10438
|
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
9478
10439
|
}
|
|
9479
|
-
function asPositiveNumber(value,
|
|
10440
|
+
function asPositiveNumber(value, path27) {
|
|
9480
10441
|
if (typeof value !== "number" || Number.isNaN(value) || value < 0) {
|
|
9481
|
-
throw new Error(`${
|
|
10442
|
+
throw new Error(`${path27} must be a non-negative number`);
|
|
9482
10443
|
}
|
|
9483
10444
|
return value;
|
|
9484
10445
|
}
|
|
9485
|
-
function parseQueryType(value,
|
|
10446
|
+
function parseQueryType(value, path27) {
|
|
9486
10447
|
if (value === "definition" || value === "implementation-intent" || value === "similarity" || value === "keyword-heavy") {
|
|
9487
10448
|
return value;
|
|
9488
10449
|
}
|
|
9489
10450
|
throw new Error(
|
|
9490
|
-
`${
|
|
10451
|
+
`${path27} must be one of: definition, implementation-intent, similarity, keyword-heavy`
|
|
9491
10452
|
);
|
|
9492
10453
|
}
|
|
9493
|
-
function parseExpected(input,
|
|
10454
|
+
function parseExpected(input, path27) {
|
|
9494
10455
|
if (!isRecord2(input)) {
|
|
9495
|
-
throw new Error(`${
|
|
10456
|
+
throw new Error(`${path27} must be an object`);
|
|
9496
10457
|
}
|
|
9497
10458
|
const filePathRaw = input.filePath;
|
|
9498
10459
|
const acceptableFilesRaw = input.acceptableFiles;
|
|
@@ -9501,16 +10462,16 @@ function parseExpected(input, path26) {
|
|
|
9501
10462
|
const filePath = typeof filePathRaw === "string" ? filePathRaw : void 0;
|
|
9502
10463
|
const acceptableFiles = isStringArray3(acceptableFilesRaw) ? acceptableFilesRaw : void 0;
|
|
9503
10464
|
if (!filePath && (!acceptableFiles || acceptableFiles.length === 0)) {
|
|
9504
|
-
throw new Error(`${
|
|
10465
|
+
throw new Error(`${path27} must include either expected.filePath or expected.acceptableFiles`);
|
|
9505
10466
|
}
|
|
9506
10467
|
if (acceptableFilesRaw !== void 0 && !isStringArray3(acceptableFilesRaw)) {
|
|
9507
|
-
throw new Error(`${
|
|
10468
|
+
throw new Error(`${path27}.acceptableFiles must be an array of strings`);
|
|
9508
10469
|
}
|
|
9509
10470
|
if (symbolRaw !== void 0 && typeof symbolRaw !== "string") {
|
|
9510
|
-
throw new Error(`${
|
|
10471
|
+
throw new Error(`${path27}.symbol must be a string when provided`);
|
|
9511
10472
|
}
|
|
9512
10473
|
if (branchRaw !== void 0 && typeof branchRaw !== "string") {
|
|
9513
|
-
throw new Error(`${
|
|
10474
|
+
throw new Error(`${path27}.branch must be a string when provided`);
|
|
9514
10475
|
}
|
|
9515
10476
|
return {
|
|
9516
10477
|
filePath,
|
|
@@ -9520,25 +10481,25 @@ function parseExpected(input, path26) {
|
|
|
9520
10481
|
};
|
|
9521
10482
|
}
|
|
9522
10483
|
function parseQuery(input, index) {
|
|
9523
|
-
const
|
|
10484
|
+
const path27 = `queries[${index}]`;
|
|
9524
10485
|
if (!isRecord2(input)) {
|
|
9525
|
-
throw new Error(`${
|
|
10486
|
+
throw new Error(`${path27} must be an object`);
|
|
9526
10487
|
}
|
|
9527
10488
|
const id = input.id;
|
|
9528
10489
|
const query = input.query;
|
|
9529
10490
|
const queryType = input.queryType;
|
|
9530
10491
|
const expected = input.expected;
|
|
9531
10492
|
if (typeof id !== "string" || id.trim().length === 0) {
|
|
9532
|
-
throw new Error(`${
|
|
10493
|
+
throw new Error(`${path27}.id must be a non-empty string`);
|
|
9533
10494
|
}
|
|
9534
10495
|
if (typeof query !== "string" || query.trim().length === 0) {
|
|
9535
|
-
throw new Error(`${
|
|
10496
|
+
throw new Error(`${path27}.query must be a non-empty string`);
|
|
9536
10497
|
}
|
|
9537
10498
|
return {
|
|
9538
10499
|
id,
|
|
9539
10500
|
query,
|
|
9540
|
-
queryType: parseQueryType(queryType, `${
|
|
9541
|
-
expected: parseExpected(expected, `${
|
|
10501
|
+
queryType: parseQueryType(queryType, `${path27}.queryType`),
|
|
10502
|
+
expected: parseExpected(expected, `${path27}.expected`)
|
|
9542
10503
|
};
|
|
9543
10504
|
}
|
|
9544
10505
|
function parseGoldenDataset(raw, sourceLabel) {
|
|
@@ -9721,13 +10682,13 @@ async function runEvaluation(options) {
|
|
|
9721
10682
|
};
|
|
9722
10683
|
const outputDir = createRunDirectory(toAbsolute(options.projectRoot, options.outputRoot));
|
|
9723
10684
|
const perQueryArtifact = buildPerQueryArtifact(perQuery);
|
|
9724
|
-
writeJson(
|
|
9725
|
-
writeJson(
|
|
10685
|
+
writeJson(path15.join(outputDir, "summary.json"), summary);
|
|
10686
|
+
writeJson(path15.join(outputDir, "per-query.json"), perQueryArtifact);
|
|
9726
10687
|
let comparison;
|
|
9727
10688
|
if (againstPath) {
|
|
9728
10689
|
const baseline = loadSummary(againstPath);
|
|
9729
10690
|
comparison = compareSummaries(summary, baseline, againstPath);
|
|
9730
|
-
writeJson(
|
|
10691
|
+
writeJson(path15.join(outputDir, "compare.json"), comparison);
|
|
9731
10692
|
}
|
|
9732
10693
|
let gate;
|
|
9733
10694
|
if (options.ciMode) {
|
|
@@ -9737,10 +10698,10 @@ async function runEvaluation(options) {
|
|
|
9737
10698
|
const budget = loadBudget(budgetPath);
|
|
9738
10699
|
if (!comparison && budget.baselinePath) {
|
|
9739
10700
|
const resolvedBaseline = toAbsolute(options.projectRoot, budget.baselinePath);
|
|
9740
|
-
if ((0,
|
|
10701
|
+
if ((0, import_fs11.existsSync)(resolvedBaseline)) {
|
|
9741
10702
|
const baselineSummary = loadSummary(resolvedBaseline);
|
|
9742
10703
|
comparison = compareSummaries(summary, baselineSummary, resolvedBaseline);
|
|
9743
|
-
writeJson(
|
|
10704
|
+
writeJson(path15.join(outputDir, "compare.json"), comparison);
|
|
9744
10705
|
} else if (budget.failOnMissingBaseline) {
|
|
9745
10706
|
throw new Error(
|
|
9746
10707
|
`Budget baseline is missing: ${resolvedBaseline}. Set failOnMissingBaseline=false to allow CI run without baseline.`
|
|
@@ -9750,7 +10711,7 @@ async function runEvaluation(options) {
|
|
|
9750
10711
|
gate = evaluateBudgetGate(budget, summary, comparison);
|
|
9751
10712
|
}
|
|
9752
10713
|
const markdown = createSummaryMarkdown(summary, comparison, gate);
|
|
9753
|
-
writeText(
|
|
10714
|
+
writeText(path15.join(outputDir, "summary.md"), markdown);
|
|
9754
10715
|
return { outputDir, summary, perQuery, comparison, gate };
|
|
9755
10716
|
} finally {
|
|
9756
10717
|
await indexer.close();
|
|
@@ -9808,23 +10769,23 @@ async function runSweep(options, sweep) {
|
|
|
9808
10769
|
bestByMrrAt10,
|
|
9809
10770
|
bestByP95Latency
|
|
9810
10771
|
};
|
|
9811
|
-
writeJson(
|
|
10772
|
+
writeJson(path15.join(outputDir, "compare.json"), aggregate);
|
|
9812
10773
|
const md = createSummaryMarkdown(
|
|
9813
10774
|
bestByHitAt5?.summary ?? runs[0].summary,
|
|
9814
10775
|
bestByHitAt5?.comparison,
|
|
9815
10776
|
void 0,
|
|
9816
10777
|
aggregate
|
|
9817
10778
|
);
|
|
9818
|
-
writeText(
|
|
9819
|
-
writeJson(
|
|
10779
|
+
writeText(path15.join(outputDir, "summary.md"), md);
|
|
10780
|
+
writeJson(path15.join(outputDir, "summary.json"), bestByHitAt5?.summary ?? runs[0].summary);
|
|
9820
10781
|
return { outputDir, aggregate };
|
|
9821
10782
|
}
|
|
9822
10783
|
|
|
9823
10784
|
// src/eval/cli.ts
|
|
9824
|
-
var
|
|
10785
|
+
var path17 = __toESM(require("path"), 1);
|
|
9825
10786
|
|
|
9826
10787
|
// src/eval/cli-parser.ts
|
|
9827
|
-
var
|
|
10788
|
+
var path16 = __toESM(require("path"), 1);
|
|
9828
10789
|
function printUsage() {
|
|
9829
10790
|
console.log(`
|
|
9830
10791
|
Usage:
|
|
@@ -9896,12 +10857,12 @@ function parseEvalArgs(argv, cwd) {
|
|
|
9896
10857
|
const arg = argv[i];
|
|
9897
10858
|
const next = argv[i + 1];
|
|
9898
10859
|
if (arg === "--project" && next) {
|
|
9899
|
-
parsed.projectRoot =
|
|
10860
|
+
parsed.projectRoot = path16.resolve(cwd, next);
|
|
9900
10861
|
i += 1;
|
|
9901
10862
|
continue;
|
|
9902
10863
|
}
|
|
9903
10864
|
if (arg === "--config" && next) {
|
|
9904
|
-
parsed.configPath =
|
|
10865
|
+
parsed.configPath = path16.resolve(cwd, next);
|
|
9905
10866
|
i += 1;
|
|
9906
10867
|
continue;
|
|
9907
10868
|
}
|
|
@@ -10097,22 +11058,22 @@ async function handleEvalCommand(args, cwd) {
|
|
|
10097
11058
|
if (!parsed.againstPath.endsWith(".json")) {
|
|
10098
11059
|
throw new Error("eval diff --against must point to a summary JSON file");
|
|
10099
11060
|
}
|
|
10100
|
-
const currentSummary = loadSummary(
|
|
11061
|
+
const currentSummary = loadSummary(path17.resolve(parsed.projectRoot, currentPath), {
|
|
10101
11062
|
allowLegacyDiversityMetrics: true
|
|
10102
11063
|
});
|
|
10103
|
-
const baselineSummary = loadSummary(
|
|
11064
|
+
const baselineSummary = loadSummary(path17.resolve(parsed.projectRoot, parsed.againstPath), {
|
|
10104
11065
|
allowLegacyDiversityMetrics: true
|
|
10105
11066
|
});
|
|
10106
11067
|
const comparison = compareSummaries(
|
|
10107
11068
|
currentSummary,
|
|
10108
11069
|
baselineSummary,
|
|
10109
|
-
|
|
11070
|
+
path17.resolve(parsed.projectRoot, parsed.againstPath)
|
|
10110
11071
|
);
|
|
10111
|
-
const outputDir = createRunDirectory(
|
|
11072
|
+
const outputDir = createRunDirectory(path17.resolve(parsed.projectRoot, parsed.outputRoot));
|
|
10112
11073
|
const summaryMd = createSummaryMarkdown(currentSummary, comparison);
|
|
10113
|
-
writeJson(
|
|
10114
|
-
writeText(
|
|
10115
|
-
writeJson(
|
|
11074
|
+
writeJson(path17.join(outputDir, "compare.json"), comparison);
|
|
11075
|
+
writeText(path17.join(outputDir, "summary.md"), summaryMd);
|
|
11076
|
+
writeJson(path17.join(outputDir, "summary.json"), currentSummary);
|
|
10116
11077
|
console.log(`Eval diff complete. Artifacts: ${outputDir}`);
|
|
10117
11078
|
return 0;
|
|
10118
11079
|
}
|
|
@@ -10121,7 +11082,7 @@ async function handleEvalCommand(args, cwd) {
|
|
|
10121
11082
|
|
|
10122
11083
|
// src/mcp-server.ts
|
|
10123
11084
|
var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
10124
|
-
var
|
|
11085
|
+
var import_fs15 = require("fs");
|
|
10125
11086
|
|
|
10126
11087
|
// src/mcp-server/register-prompts.ts
|
|
10127
11088
|
var import_zod = require("zod");
|
|
@@ -10312,6 +11273,10 @@ function formatStatus(status) {
|
|
|
10312
11273
|
lines.push(`Failed batches: ${status.failedBatchesPath}`);
|
|
10313
11274
|
}
|
|
10314
11275
|
}
|
|
11276
|
+
if (status.warning) {
|
|
11277
|
+
lines.push("");
|
|
11278
|
+
lines.push(`INDEX WARNING: ${status.warning}`);
|
|
11279
|
+
}
|
|
10315
11280
|
if (status.compatibility && !status.compatibility.compatible) {
|
|
10316
11281
|
lines.push("");
|
|
10317
11282
|
lines.push(`COMPATIBILITY WARNING: ${status.compatibility.reason}`);
|
|
@@ -10417,16 +11382,16 @@ function formatCallGraphCallees(symbolId, callees, relationshipType) {
|
|
|
10417
11382
|
return `[${index + 1}] \u2192 ${edge.targetName} (${edge.callType})${confidence} at line ${edge.line}${edge.isResolved ? ` [resolved: ${edge.toSymbolId}]` : " [unresolved]"}`;
|
|
10418
11383
|
}).join("\n");
|
|
10419
11384
|
}
|
|
10420
|
-
function formatCallGraphPath(from, to,
|
|
10421
|
-
if (
|
|
11385
|
+
function formatCallGraphPath(from, to, path27) {
|
|
11386
|
+
if (path27.length === 0) {
|
|
10422
11387
|
return `No path found between "${from}" and "${to}". They may be in disconnected components, or the call graph index needs updating.`;
|
|
10423
11388
|
}
|
|
10424
|
-
const formatted =
|
|
11389
|
+
const formatted = path27.map((hop, index) => {
|
|
10425
11390
|
const prefix = index === 0 ? "[start]" : `--${hop.callType}-->`;
|
|
10426
11391
|
const location = hop.filePath ? ` (${hop.filePath}:${hop.line})` : "";
|
|
10427
11392
|
return `${prefix} ${hop.symbolName}${location}`;
|
|
10428
11393
|
});
|
|
10429
|
-
return `Path (${
|
|
11394
|
+
return `Path (${path27.length} hops):
|
|
10430
11395
|
${formatted.join("\n")}`;
|
|
10431
11396
|
}
|
|
10432
11397
|
function formatResultHeader(result, index) {
|
|
@@ -10517,12 +11482,12 @@ function formatPrImpact(result) {
|
|
|
10517
11482
|
}
|
|
10518
11483
|
|
|
10519
11484
|
// src/tools/operations.ts
|
|
10520
|
-
var
|
|
10521
|
-
var
|
|
11485
|
+
var import_fs14 = require("fs");
|
|
11486
|
+
var path21 = __toESM(require("path"), 1);
|
|
10522
11487
|
|
|
10523
11488
|
// src/config/merger.ts
|
|
10524
|
-
var
|
|
10525
|
-
var
|
|
11489
|
+
var import_fs12 = require("fs");
|
|
11490
|
+
var path18 = __toESM(require("path"), 1);
|
|
10526
11491
|
var PROJECT_OVERRIDE_KEYS = [
|
|
10527
11492
|
"embeddingProvider",
|
|
10528
11493
|
"customProvider",
|
|
@@ -10555,8 +11520,8 @@ function mergeUniqueStringArray(values) {
|
|
|
10555
11520
|
return [...new Set(values.map((value) => String(value).trim()))];
|
|
10556
11521
|
}
|
|
10557
11522
|
function normalizeKnowledgeBasePath(value) {
|
|
10558
|
-
let normalized =
|
|
10559
|
-
const root =
|
|
11523
|
+
let normalized = path18.normalize(String(value).trim());
|
|
11524
|
+
const root = path18.parse(normalized).root;
|
|
10560
11525
|
while (normalized.length > root.length && /[\\/]$/.test(normalized)) {
|
|
10561
11526
|
normalized = normalized.slice(0, -1);
|
|
10562
11527
|
}
|
|
@@ -10590,11 +11555,11 @@ function validateConfigLayerShape(rawConfig, filePath) {
|
|
|
10590
11555
|
return rawConfig;
|
|
10591
11556
|
}
|
|
10592
11557
|
function loadJsonFile(filePath) {
|
|
10593
|
-
if (!(0,
|
|
11558
|
+
if (!(0, import_fs12.existsSync)(filePath)) {
|
|
10594
11559
|
return null;
|
|
10595
11560
|
}
|
|
10596
11561
|
try {
|
|
10597
|
-
const content = (0,
|
|
11562
|
+
const content = (0, import_fs12.readFileSync)(filePath, "utf-8");
|
|
10598
11563
|
return validateConfigLayerShape(JSON.parse(content), filePath);
|
|
10599
11564
|
} catch (error) {
|
|
10600
11565
|
if (error instanceof Error && error.message.startsWith("Config file ")) {
|
|
@@ -10609,8 +11574,8 @@ function loadConfigFile(filePath) {
|
|
|
10609
11574
|
}
|
|
10610
11575
|
function materializeLocalProjectConfig(projectRoot, config, host = "opencode") {
|
|
10611
11576
|
const localConfigPath = resolveWritableProjectConfigPath(projectRoot, host);
|
|
10612
|
-
(0,
|
|
10613
|
-
(0,
|
|
11577
|
+
(0, import_fs12.mkdirSync)(path18.dirname(localConfigPath), { recursive: true });
|
|
11578
|
+
(0, import_fs12.writeFileSync)(localConfigPath, JSON.stringify(config, null, 2), "utf-8");
|
|
10614
11579
|
return localConfigPath;
|
|
10615
11580
|
}
|
|
10616
11581
|
function loadProjectConfigLayer(projectRoot, host = "opencode") {
|
|
@@ -10620,7 +11585,7 @@ function loadProjectConfigLayer(projectRoot, host = "opencode") {
|
|
|
10620
11585
|
return {};
|
|
10621
11586
|
}
|
|
10622
11587
|
const normalizedConfig = { ...projectConfig };
|
|
10623
|
-
const projectConfigBaseDir =
|
|
11588
|
+
const projectConfigBaseDir = path18.dirname(path18.dirname(projectConfigPath));
|
|
10624
11589
|
if (Array.isArray(normalizedConfig.knowledgeBases)) {
|
|
10625
11590
|
normalizedConfig.knowledgeBases = resolveInheritedKnowledgeBaseEntries(
|
|
10626
11591
|
normalizedConfig.knowledgeBases,
|
|
@@ -10684,19 +11649,19 @@ function loadMergedConfig(projectRoot, host = "opencode") {
|
|
|
10684
11649
|
}
|
|
10685
11650
|
|
|
10686
11651
|
// src/tools/knowledge-base-paths.ts
|
|
10687
|
-
var
|
|
11652
|
+
var path19 = __toESM(require("path"), 1);
|
|
10688
11653
|
function resolveConfigPathValue(value, baseDir) {
|
|
10689
11654
|
const trimmed = value.trim();
|
|
10690
11655
|
if (!trimmed) {
|
|
10691
11656
|
return trimmed;
|
|
10692
11657
|
}
|
|
10693
|
-
const absolutePath =
|
|
10694
|
-
return
|
|
11658
|
+
const absolutePath = path19.isAbsolute(trimmed) ? trimmed : path19.resolve(baseDir, trimmed);
|
|
11659
|
+
return path19.normalize(absolutePath);
|
|
10695
11660
|
}
|
|
10696
11661
|
|
|
10697
11662
|
// src/tools/config-state.ts
|
|
10698
|
-
var
|
|
10699
|
-
var
|
|
11663
|
+
var import_fs13 = require("fs");
|
|
11664
|
+
var path20 = __toESM(require("path"), 1);
|
|
10700
11665
|
function normalizeKnowledgeBasePaths(config, projectRoot) {
|
|
10701
11666
|
const normalized = { ...config };
|
|
10702
11667
|
if (Array.isArray(normalized.knowledgeBases)) {
|
|
@@ -10720,6 +11685,24 @@ function loadRuntimeConfig(projectRoot, host = "opencode") {
|
|
|
10720
11685
|
var indexerCache = /* @__PURE__ */ new Map();
|
|
10721
11686
|
var configCache = /* @__PURE__ */ new Map();
|
|
10722
11687
|
var defaultProjectRoots = /* @__PURE__ */ new Map();
|
|
11688
|
+
function getIndexBusyResult(error) {
|
|
11689
|
+
if (!isIndexLockContentionError(error)) return null;
|
|
11690
|
+
const owner = error.owner;
|
|
11691
|
+
const ownerText = owner ? `PID ${owner.pid}, operation ${owner.operation}, since ${owner.startedAt}` : "unreadable owner";
|
|
11692
|
+
if (error.reason === "legacy-lock") {
|
|
11693
|
+
return {
|
|
11694
|
+
kind: "busy",
|
|
11695
|
+
text: `INDEX_BUSY: legacy lock format detected (${ownerText}). Verify the PID and remove this lock manually only if it is stale.`
|
|
11696
|
+
};
|
|
11697
|
+
}
|
|
11698
|
+
if (error.reason === "unknown-owner") {
|
|
11699
|
+
return {
|
|
11700
|
+
kind: "busy",
|
|
11701
|
+
text: `INDEX_BUSY: unreadable or remote lock owner (${ownerText}). Automatic recovery was refused; manual verification is required.`
|
|
11702
|
+
};
|
|
11703
|
+
}
|
|
11704
|
+
return { kind: "busy", text: `INDEX_BUSY: another index operation is already in progress (${ownerText}).` };
|
|
11705
|
+
}
|
|
10723
11706
|
function getProjectRoot(projectRoot, host) {
|
|
10724
11707
|
if (projectRoot) {
|
|
10725
11708
|
return projectRoot;
|
|
@@ -10764,8 +11747,8 @@ function refreshIndexerForDirectory(projectRoot, host = "opencode", config = par
|
|
|
10764
11747
|
}
|
|
10765
11748
|
function shouldForceLocalizeProjectIndex(projectRoot, host = "opencode") {
|
|
10766
11749
|
const root = getProjectRoot(projectRoot, host);
|
|
10767
|
-
const localIndexPath =
|
|
10768
|
-
if ((0,
|
|
11750
|
+
const localIndexPath = path21.join(root, getHostProjectIndexRelativePath(host));
|
|
11751
|
+
if ((0, import_fs14.existsSync)(localIndexPath)) {
|
|
10769
11752
|
return false;
|
|
10770
11753
|
}
|
|
10771
11754
|
const inheritedIndexPath = resolveWorktreeFallbackProjectIndexPath(root, host);
|
|
@@ -10821,35 +11804,46 @@ async function getCallGraphPath(projectRoot, host, from, to, maxDepth) {
|
|
|
10821
11804
|
async function runIndexCodebase(projectRoot, host, args, onProgress) {
|
|
10822
11805
|
const root = getProjectRoot(projectRoot, host);
|
|
10823
11806
|
const key = getIndexerCacheKey(root, host);
|
|
10824
|
-
const cachedConfig = configCache.get(key);
|
|
10825
11807
|
let indexer = getIndexerForProject(root, host);
|
|
10826
|
-
|
|
10827
|
-
|
|
10828
|
-
|
|
10829
|
-
|
|
10830
|
-
|
|
10831
|
-
|
|
10832
|
-
|
|
10833
|
-
|
|
10834
|
-
|
|
10835
|
-
|
|
10836
|
-
|
|
10837
|
-
|
|
10838
|
-
|
|
10839
|
-
|
|
10840
|
-
|
|
10841
|
-
|
|
10842
|
-
|
|
10843
|
-
|
|
10844
|
-
|
|
10845
|
-
chunksProcessed: progress.chunksProcessed,
|
|
10846
|
-
totalChunks: progress.totalChunks,
|
|
10847
|
-
percentage: calculatePercentage(progress)
|
|
11808
|
+
const runtimeConfig = configCache.get(key);
|
|
11809
|
+
try {
|
|
11810
|
+
if (args.estimateOnly) {
|
|
11811
|
+
return { kind: "estimate", estimate: await indexer.estimateCost() };
|
|
11812
|
+
}
|
|
11813
|
+
const runIndex = async (target) => {
|
|
11814
|
+
const operation = args.force ? target.forceIndex.bind(target) : target.index.bind(target);
|
|
11815
|
+
return operation((progress) => {
|
|
11816
|
+
if (onProgress) {
|
|
11817
|
+
return onProgress(formatProgressTitle(progress), {
|
|
11818
|
+
phase: progress.phase,
|
|
11819
|
+
filesProcessed: progress.filesProcessed,
|
|
11820
|
+
totalFiles: progress.totalFiles,
|
|
11821
|
+
chunksProcessed: progress.chunksProcessed,
|
|
11822
|
+
totalChunks: progress.totalChunks,
|
|
11823
|
+
percentage: calculatePercentage(progress)
|
|
11824
|
+
});
|
|
11825
|
+
}
|
|
11826
|
+
return Promise.resolve();
|
|
10848
11827
|
});
|
|
11828
|
+
};
|
|
11829
|
+
let stats;
|
|
11830
|
+
if (args.force && shouldForceLocalizeProjectIndex(root, host)) {
|
|
11831
|
+
const inheritedIndexPath = resolveProjectIndexPath(root, runtimeConfig.scope, host);
|
|
11832
|
+
stats = await withIndexLock(inheritedIndexPath, "force-index", async () => {
|
|
11833
|
+
materializeLocalProjectConfig(root, loadProjectConfigLayer(root, host), host);
|
|
11834
|
+
refreshIndexerForDirectory(root, host, runtimeConfig);
|
|
11835
|
+
indexer = getIndexerForProject(root, host);
|
|
11836
|
+
return runIndex(indexer);
|
|
11837
|
+
}, { completeRecoveries: false });
|
|
11838
|
+
} else {
|
|
11839
|
+
stats = await runIndex(indexer);
|
|
10849
11840
|
}
|
|
10850
|
-
return
|
|
10851
|
-
})
|
|
10852
|
-
|
|
11841
|
+
return { kind: "stats", stats };
|
|
11842
|
+
} catch (error) {
|
|
11843
|
+
const busyResult = getIndexBusyResult(error);
|
|
11844
|
+
if (!busyResult) throw error;
|
|
11845
|
+
return busyResult;
|
|
11846
|
+
}
|
|
10853
11847
|
}
|
|
10854
11848
|
async function getIndexStatus(projectRoot, host) {
|
|
10855
11849
|
const indexer = getIndexerForProject(projectRoot, host);
|
|
@@ -10859,6 +11853,15 @@ async function getIndexHealthCheck(projectRoot, host) {
|
|
|
10859
11853
|
const indexer = getIndexerForProject(projectRoot, host);
|
|
10860
11854
|
return indexer.healthCheck();
|
|
10861
11855
|
}
|
|
11856
|
+
async function runIndexHealthCheck(projectRoot, host) {
|
|
11857
|
+
try {
|
|
11858
|
+
return { kind: "health", health: await getIndexHealthCheck(projectRoot, host) };
|
|
11859
|
+
} catch (error) {
|
|
11860
|
+
const busyResult = getIndexBusyResult(error);
|
|
11861
|
+
if (!busyResult) throw error;
|
|
11862
|
+
return busyResult;
|
|
11863
|
+
}
|
|
11864
|
+
}
|
|
10862
11865
|
async function getPrImpact(projectRoot, host, params) {
|
|
10863
11866
|
const indexer = getIndexerForProject(projectRoot, host);
|
|
10864
11867
|
return indexer.getPrImpact({
|
|
@@ -11017,8 +12020,13 @@ Use Read tool to examine specific files.` }] };
|
|
|
11017
12020
|
},
|
|
11018
12021
|
async (args) => {
|
|
11019
12022
|
const result = await runIndexCodebase(runtime.projectRoot, runtime.host, args);
|
|
11020
|
-
|
|
11021
|
-
|
|
12023
|
+
if (result.kind === "estimate") {
|
|
12024
|
+
return { content: [{ type: "text", text: formatCostEstimate(result.estimate) }] };
|
|
12025
|
+
}
|
|
12026
|
+
if (result.kind === "busy") {
|
|
12027
|
+
return { content: [{ type: "text", text: result.text }], isError: true };
|
|
12028
|
+
}
|
|
12029
|
+
return { content: [{ type: "text", text: formatIndexStats(result.stats, args.verbose ?? false) }] };
|
|
11022
12030
|
}
|
|
11023
12031
|
);
|
|
11024
12032
|
server.tool(
|
|
@@ -11035,8 +12043,11 @@ Use Read tool to examine specific files.` }] };
|
|
|
11035
12043
|
"Check index health and remove stale entries from deleted files. Run this to clean up the index after files have been deleted.",
|
|
11036
12044
|
{},
|
|
11037
12045
|
async () => {
|
|
11038
|
-
const result = await
|
|
11039
|
-
|
|
12046
|
+
const result = await runIndexHealthCheck(runtime.projectRoot, runtime.host);
|
|
12047
|
+
if (result.kind === "busy") {
|
|
12048
|
+
return { content: [{ type: "text", text: result.text }], isError: true };
|
|
12049
|
+
}
|
|
12050
|
+
return { content: [{ type: "text", text: formatHealthCheck(result.health) }] };
|
|
11040
12051
|
}
|
|
11041
12052
|
);
|
|
11042
12053
|
server.tool(
|
|
@@ -11136,8 +12147,8 @@ ${formatSearchResults(results)}` }] };
|
|
|
11136
12147
|
maxDepth: import_zod2.z.number().optional().default(10).describe("Maximum traversal depth (default: 10)")
|
|
11137
12148
|
},
|
|
11138
12149
|
async (args) => {
|
|
11139
|
-
const
|
|
11140
|
-
return { content: [{ type: "text", text: formatCallGraphPath(args.from, args.to,
|
|
12150
|
+
const path27 = await getCallGraphPath(runtime.projectRoot, runtime.host, args.from, args.to, args.maxDepth);
|
|
12151
|
+
return { content: [{ type: "text", text: formatCallGraphPath(args.from, args.to, path27) }] };
|
|
11141
12152
|
}
|
|
11142
12153
|
);
|
|
11143
12154
|
server.tool(
|
|
@@ -11173,7 +12184,7 @@ ${formatSearchResults(results)}` }] };
|
|
|
11173
12184
|
// src/mcp-server.ts
|
|
11174
12185
|
var import_meta2 = {};
|
|
11175
12186
|
function getPackageVersion() {
|
|
11176
|
-
const raw = JSON.parse((0,
|
|
12187
|
+
const raw = JSON.parse((0, import_fs15.readFileSync)(new URL("../package.json", import_meta2.url), "utf-8"));
|
|
11177
12188
|
if (raw && typeof raw === "object" && "version" in raw && typeof raw.version === "string") {
|
|
11178
12189
|
return raw.version;
|
|
11179
12190
|
}
|
|
@@ -11194,18 +12205,37 @@ function createMcpServer(projectRoot, config, host = "opencode") {
|
|
|
11194
12205
|
}
|
|
11195
12206
|
|
|
11196
12207
|
// src/utils/auto-index.ts
|
|
12208
|
+
var INITIAL_RETRY_DELAY_MS = 50;
|
|
12209
|
+
var MAX_RETRY_DELAY_MS = 500;
|
|
12210
|
+
var autoIndexStates = /* @__PURE__ */ new WeakMap();
|
|
11197
12211
|
function getErrorMessage3(error) {
|
|
11198
12212
|
return error instanceof Error ? error.message : String(error);
|
|
11199
12213
|
}
|
|
11200
|
-
function
|
|
11201
|
-
indexer.
|
|
11202
|
-
|
|
11203
|
-
console.error(`[codebase-index] Auto-index failed for "${projectRoot}": ${getErrorMessage3(error)}`);
|
|
11204
|
-
});
|
|
12214
|
+
function runAutoIndex(indexer, projectRoot, state) {
|
|
12215
|
+
void indexer.index().then(() => {
|
|
12216
|
+
autoIndexStates.delete(indexer);
|
|
11205
12217
|
}).catch((error) => {
|
|
11206
|
-
|
|
12218
|
+
if (!isTransientIndexLockContention(error)) {
|
|
12219
|
+
autoIndexStates.delete(indexer);
|
|
12220
|
+
console.error(`[codebase-index] Auto-index failed for "${projectRoot}": ${getErrorMessage3(error)}`);
|
|
12221
|
+
return;
|
|
12222
|
+
}
|
|
12223
|
+
const retryDelayMs = state.retryDelayMs;
|
|
12224
|
+
state.retryDelayMs = Math.min(retryDelayMs * 2, MAX_RETRY_DELAY_MS);
|
|
12225
|
+
const retryTimer = setTimeout(() => {
|
|
12226
|
+
runAutoIndex(indexer, projectRoot, state);
|
|
12227
|
+
}, retryDelayMs);
|
|
12228
|
+
retryTimer.unref?.();
|
|
11207
12229
|
});
|
|
11208
12230
|
}
|
|
12231
|
+
function startAutoIndex(indexer, projectRoot) {
|
|
12232
|
+
if (autoIndexStates.has(indexer)) return;
|
|
12233
|
+
const state = {
|
|
12234
|
+
retryDelayMs: INITIAL_RETRY_DELAY_MS
|
|
12235
|
+
};
|
|
12236
|
+
autoIndexStates.set(indexer, state);
|
|
12237
|
+
runAutoIndex(indexer, projectRoot, state);
|
|
12238
|
+
}
|
|
11209
12239
|
|
|
11210
12240
|
// node_modules/chokidar/index.js
|
|
11211
12241
|
var import_node_events = require("events");
|
|
@@ -11297,7 +12327,7 @@ var ReaddirpStream = class extends import_node_stream.Readable {
|
|
|
11297
12327
|
this._directoryFilter = normalizeFilter(opts.directoryFilter);
|
|
11298
12328
|
const statMethod = opts.lstat ? import_promises.lstat : import_promises.stat;
|
|
11299
12329
|
if (wantBigintFsStats) {
|
|
11300
|
-
this._stat = (
|
|
12330
|
+
this._stat = (path27) => statMethod(path27, { bigint: true });
|
|
11301
12331
|
} else {
|
|
11302
12332
|
this._stat = statMethod;
|
|
11303
12333
|
}
|
|
@@ -11322,8 +12352,8 @@ var ReaddirpStream = class extends import_node_stream.Readable {
|
|
|
11322
12352
|
const par = this.parent;
|
|
11323
12353
|
const fil = par && par.files;
|
|
11324
12354
|
if (fil && fil.length > 0) {
|
|
11325
|
-
const { path:
|
|
11326
|
-
const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent,
|
|
12355
|
+
const { path: path27, depth } = par;
|
|
12356
|
+
const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path27));
|
|
11327
12357
|
const awaited = await Promise.all(slice);
|
|
11328
12358
|
for (const entry of awaited) {
|
|
11329
12359
|
if (!entry)
|
|
@@ -11363,20 +12393,20 @@ var ReaddirpStream = class extends import_node_stream.Readable {
|
|
|
11363
12393
|
this.reading = false;
|
|
11364
12394
|
}
|
|
11365
12395
|
}
|
|
11366
|
-
async _exploreDir(
|
|
12396
|
+
async _exploreDir(path27, depth) {
|
|
11367
12397
|
let files;
|
|
11368
12398
|
try {
|
|
11369
|
-
files = await (0, import_promises.readdir)(
|
|
12399
|
+
files = await (0, import_promises.readdir)(path27, this._rdOptions);
|
|
11370
12400
|
} catch (error) {
|
|
11371
12401
|
this._onError(error);
|
|
11372
12402
|
}
|
|
11373
|
-
return { files, depth, path:
|
|
12403
|
+
return { files, depth, path: path27 };
|
|
11374
12404
|
}
|
|
11375
|
-
async _formatEntry(dirent,
|
|
12405
|
+
async _formatEntry(dirent, path27) {
|
|
11376
12406
|
let entry;
|
|
11377
12407
|
const basename5 = this._isDirent ? dirent.name : dirent;
|
|
11378
12408
|
try {
|
|
11379
|
-
const fullPath = (0, import_node_path.resolve)((0, import_node_path.join)(
|
|
12409
|
+
const fullPath = (0, import_node_path.resolve)((0, import_node_path.join)(path27, basename5));
|
|
11380
12410
|
entry = { path: (0, import_node_path.relative)(this._root, fullPath), fullPath, basename: basename5 };
|
|
11381
12411
|
entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
|
|
11382
12412
|
} catch (err) {
|
|
@@ -11776,16 +12806,16 @@ var delFromSet = (main2, prop, item) => {
|
|
|
11776
12806
|
};
|
|
11777
12807
|
var isEmptySet = (val) => val instanceof Set ? val.size === 0 : !val;
|
|
11778
12808
|
var FsWatchInstances = /* @__PURE__ */ new Map();
|
|
11779
|
-
function createFsWatchInstance(
|
|
12809
|
+
function createFsWatchInstance(path27, options, listener, errHandler, emitRaw) {
|
|
11780
12810
|
const handleEvent = (rawEvent, evPath) => {
|
|
11781
|
-
listener(
|
|
11782
|
-
emitRaw(rawEvent, evPath, { watchedPath:
|
|
11783
|
-
if (evPath &&
|
|
11784
|
-
fsWatchBroadcast(sp.resolve(
|
|
12811
|
+
listener(path27);
|
|
12812
|
+
emitRaw(rawEvent, evPath, { watchedPath: path27 });
|
|
12813
|
+
if (evPath && path27 !== evPath) {
|
|
12814
|
+
fsWatchBroadcast(sp.resolve(path27, evPath), KEY_LISTENERS, sp.join(path27, evPath));
|
|
11785
12815
|
}
|
|
11786
12816
|
};
|
|
11787
12817
|
try {
|
|
11788
|
-
return (0, import_node_fs.watch)(
|
|
12818
|
+
return (0, import_node_fs.watch)(path27, {
|
|
11789
12819
|
persistent: options.persistent
|
|
11790
12820
|
}, handleEvent);
|
|
11791
12821
|
} catch (error) {
|
|
@@ -11801,12 +12831,12 @@ var fsWatchBroadcast = (fullPath, listenerType, val1, val2, val3) => {
|
|
|
11801
12831
|
listener(val1, val2, val3);
|
|
11802
12832
|
});
|
|
11803
12833
|
};
|
|
11804
|
-
var setFsWatchListener = (
|
|
12834
|
+
var setFsWatchListener = (path27, fullPath, options, handlers) => {
|
|
11805
12835
|
const { listener, errHandler, rawEmitter } = handlers;
|
|
11806
12836
|
let cont = FsWatchInstances.get(fullPath);
|
|
11807
12837
|
let watcher;
|
|
11808
12838
|
if (!options.persistent) {
|
|
11809
|
-
watcher = createFsWatchInstance(
|
|
12839
|
+
watcher = createFsWatchInstance(path27, options, listener, errHandler, rawEmitter);
|
|
11810
12840
|
if (!watcher)
|
|
11811
12841
|
return;
|
|
11812
12842
|
return watcher.close.bind(watcher);
|
|
@@ -11817,7 +12847,7 @@ var setFsWatchListener = (path26, fullPath, options, handlers) => {
|
|
|
11817
12847
|
addAndConvert(cont, KEY_RAW, rawEmitter);
|
|
11818
12848
|
} else {
|
|
11819
12849
|
watcher = createFsWatchInstance(
|
|
11820
|
-
|
|
12850
|
+
path27,
|
|
11821
12851
|
options,
|
|
11822
12852
|
fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS),
|
|
11823
12853
|
errHandler,
|
|
@@ -11832,7 +12862,7 @@ var setFsWatchListener = (path26, fullPath, options, handlers) => {
|
|
|
11832
12862
|
cont.watcherUnusable = true;
|
|
11833
12863
|
if (isWindows && error.code === "EPERM") {
|
|
11834
12864
|
try {
|
|
11835
|
-
const fd = await (0, import_promises2.open)(
|
|
12865
|
+
const fd = await (0, import_promises2.open)(path27, "r");
|
|
11836
12866
|
await fd.close();
|
|
11837
12867
|
broadcastErr(error);
|
|
11838
12868
|
} catch (err) {
|
|
@@ -11863,7 +12893,7 @@ var setFsWatchListener = (path26, fullPath, options, handlers) => {
|
|
|
11863
12893
|
};
|
|
11864
12894
|
};
|
|
11865
12895
|
var FsWatchFileInstances = /* @__PURE__ */ new Map();
|
|
11866
|
-
var setFsWatchFileListener = (
|
|
12896
|
+
var setFsWatchFileListener = (path27, fullPath, options, handlers) => {
|
|
11867
12897
|
const { listener, rawEmitter } = handlers;
|
|
11868
12898
|
let cont = FsWatchFileInstances.get(fullPath);
|
|
11869
12899
|
const copts = cont && cont.options;
|
|
@@ -11885,7 +12915,7 @@ var setFsWatchFileListener = (path26, fullPath, options, handlers) => {
|
|
|
11885
12915
|
});
|
|
11886
12916
|
const currmtime = curr.mtimeMs;
|
|
11887
12917
|
if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {
|
|
11888
|
-
foreach(cont.listeners, (listener2) => listener2(
|
|
12918
|
+
foreach(cont.listeners, (listener2) => listener2(path27, curr));
|
|
11889
12919
|
}
|
|
11890
12920
|
})
|
|
11891
12921
|
};
|
|
@@ -11915,13 +12945,13 @@ var NodeFsHandler = class {
|
|
|
11915
12945
|
* @param listener on fs change
|
|
11916
12946
|
* @returns closer for the watcher instance
|
|
11917
12947
|
*/
|
|
11918
|
-
_watchWithNodeFs(
|
|
12948
|
+
_watchWithNodeFs(path27, listener) {
|
|
11919
12949
|
const opts = this.fsw.options;
|
|
11920
|
-
const directory = sp.dirname(
|
|
11921
|
-
const basename5 = sp.basename(
|
|
12950
|
+
const directory = sp.dirname(path27);
|
|
12951
|
+
const basename5 = sp.basename(path27);
|
|
11922
12952
|
const parent = this.fsw._getWatchedDir(directory);
|
|
11923
12953
|
parent.add(basename5);
|
|
11924
|
-
const absolutePath = sp.resolve(
|
|
12954
|
+
const absolutePath = sp.resolve(path27);
|
|
11925
12955
|
const options = {
|
|
11926
12956
|
persistent: opts.persistent
|
|
11927
12957
|
};
|
|
@@ -11931,12 +12961,12 @@ var NodeFsHandler = class {
|
|
|
11931
12961
|
if (opts.usePolling) {
|
|
11932
12962
|
const enableBin = opts.interval !== opts.binaryInterval;
|
|
11933
12963
|
options.interval = enableBin && isBinaryPath(basename5) ? opts.binaryInterval : opts.interval;
|
|
11934
|
-
closer = setFsWatchFileListener(
|
|
12964
|
+
closer = setFsWatchFileListener(path27, absolutePath, options, {
|
|
11935
12965
|
listener,
|
|
11936
12966
|
rawEmitter: this.fsw._emitRaw
|
|
11937
12967
|
});
|
|
11938
12968
|
} else {
|
|
11939
|
-
closer = setFsWatchListener(
|
|
12969
|
+
closer = setFsWatchListener(path27, absolutePath, options, {
|
|
11940
12970
|
listener,
|
|
11941
12971
|
errHandler: this._boundHandleError,
|
|
11942
12972
|
rawEmitter: this.fsw._emitRaw
|
|
@@ -11958,7 +12988,7 @@ var NodeFsHandler = class {
|
|
|
11958
12988
|
let prevStats = stats;
|
|
11959
12989
|
if (parent.has(basename5))
|
|
11960
12990
|
return;
|
|
11961
|
-
const listener = async (
|
|
12991
|
+
const listener = async (path27, newStats) => {
|
|
11962
12992
|
if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5))
|
|
11963
12993
|
return;
|
|
11964
12994
|
if (!newStats || newStats.mtimeMs === 0) {
|
|
@@ -11972,11 +13002,11 @@ var NodeFsHandler = class {
|
|
|
11972
13002
|
this.fsw._emit(EV.CHANGE, file, newStats2);
|
|
11973
13003
|
}
|
|
11974
13004
|
if ((isMacos || isLinux || isFreeBSD) && prevStats.ino !== newStats2.ino) {
|
|
11975
|
-
this.fsw._closeFile(
|
|
13005
|
+
this.fsw._closeFile(path27);
|
|
11976
13006
|
prevStats = newStats2;
|
|
11977
13007
|
const closer2 = this._watchWithNodeFs(file, listener);
|
|
11978
13008
|
if (closer2)
|
|
11979
|
-
this.fsw._addPathCloser(
|
|
13009
|
+
this.fsw._addPathCloser(path27, closer2);
|
|
11980
13010
|
} else {
|
|
11981
13011
|
prevStats = newStats2;
|
|
11982
13012
|
}
|
|
@@ -12008,7 +13038,7 @@ var NodeFsHandler = class {
|
|
|
12008
13038
|
* @param item basename of this item
|
|
12009
13039
|
* @returns true if no more processing is needed for this entry.
|
|
12010
13040
|
*/
|
|
12011
|
-
async _handleSymlink(entry, directory,
|
|
13041
|
+
async _handleSymlink(entry, directory, path27, item) {
|
|
12012
13042
|
if (this.fsw.closed) {
|
|
12013
13043
|
return;
|
|
12014
13044
|
}
|
|
@@ -12018,7 +13048,7 @@ var NodeFsHandler = class {
|
|
|
12018
13048
|
this.fsw._incrReadyCount();
|
|
12019
13049
|
let linkPath;
|
|
12020
13050
|
try {
|
|
12021
|
-
linkPath = await (0, import_promises2.realpath)(
|
|
13051
|
+
linkPath = await (0, import_promises2.realpath)(path27);
|
|
12022
13052
|
} catch (e) {
|
|
12023
13053
|
this.fsw._emitReady();
|
|
12024
13054
|
return true;
|
|
@@ -12028,12 +13058,12 @@ var NodeFsHandler = class {
|
|
|
12028
13058
|
if (dir.has(item)) {
|
|
12029
13059
|
if (this.fsw._symlinkPaths.get(full) !== linkPath) {
|
|
12030
13060
|
this.fsw._symlinkPaths.set(full, linkPath);
|
|
12031
|
-
this.fsw._emit(EV.CHANGE,
|
|
13061
|
+
this.fsw._emit(EV.CHANGE, path27, entry.stats);
|
|
12032
13062
|
}
|
|
12033
13063
|
} else {
|
|
12034
13064
|
dir.add(item);
|
|
12035
13065
|
this.fsw._symlinkPaths.set(full, linkPath);
|
|
12036
|
-
this.fsw._emit(EV.ADD,
|
|
13066
|
+
this.fsw._emit(EV.ADD, path27, entry.stats);
|
|
12037
13067
|
}
|
|
12038
13068
|
this.fsw._emitReady();
|
|
12039
13069
|
return true;
|
|
@@ -12063,9 +13093,9 @@ var NodeFsHandler = class {
|
|
|
12063
13093
|
return;
|
|
12064
13094
|
}
|
|
12065
13095
|
const item = entry.path;
|
|
12066
|
-
let
|
|
13096
|
+
let path27 = sp.join(directory, item);
|
|
12067
13097
|
current.add(item);
|
|
12068
|
-
if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory,
|
|
13098
|
+
if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path27, item)) {
|
|
12069
13099
|
return;
|
|
12070
13100
|
}
|
|
12071
13101
|
if (this.fsw.closed) {
|
|
@@ -12074,8 +13104,8 @@ var NodeFsHandler = class {
|
|
|
12074
13104
|
}
|
|
12075
13105
|
if (item === target || !target && !previous.has(item)) {
|
|
12076
13106
|
this.fsw._incrReadyCount();
|
|
12077
|
-
|
|
12078
|
-
this._addToNodeFs(
|
|
13107
|
+
path27 = sp.join(dir, sp.relative(dir, path27));
|
|
13108
|
+
this._addToNodeFs(path27, initialAdd, wh, depth + 1);
|
|
12079
13109
|
}
|
|
12080
13110
|
}).on(EV.ERROR, this._boundHandleError);
|
|
12081
13111
|
return new Promise((resolve15, reject) => {
|
|
@@ -12144,13 +13174,13 @@ var NodeFsHandler = class {
|
|
|
12144
13174
|
* @param depth Child path actually targeted for watch
|
|
12145
13175
|
* @param target Child path actually targeted for watch
|
|
12146
13176
|
*/
|
|
12147
|
-
async _addToNodeFs(
|
|
13177
|
+
async _addToNodeFs(path27, initialAdd, priorWh, depth, target) {
|
|
12148
13178
|
const ready = this.fsw._emitReady;
|
|
12149
|
-
if (this.fsw._isIgnored(
|
|
13179
|
+
if (this.fsw._isIgnored(path27) || this.fsw.closed) {
|
|
12150
13180
|
ready();
|
|
12151
13181
|
return false;
|
|
12152
13182
|
}
|
|
12153
|
-
const wh = this.fsw._getWatchHelpers(
|
|
13183
|
+
const wh = this.fsw._getWatchHelpers(path27);
|
|
12154
13184
|
if (priorWh) {
|
|
12155
13185
|
wh.filterPath = (entry) => priorWh.filterPath(entry);
|
|
12156
13186
|
wh.filterDir = (entry) => priorWh.filterDir(entry);
|
|
@@ -12166,8 +13196,8 @@ var NodeFsHandler = class {
|
|
|
12166
13196
|
const follow = this.fsw.options.followSymlinks;
|
|
12167
13197
|
let closer;
|
|
12168
13198
|
if (stats.isDirectory()) {
|
|
12169
|
-
const absPath = sp.resolve(
|
|
12170
|
-
const targetPath = follow ? await (0, import_promises2.realpath)(
|
|
13199
|
+
const absPath = sp.resolve(path27);
|
|
13200
|
+
const targetPath = follow ? await (0, import_promises2.realpath)(path27) : path27;
|
|
12171
13201
|
if (this.fsw.closed)
|
|
12172
13202
|
return;
|
|
12173
13203
|
closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
|
|
@@ -12177,29 +13207,29 @@ var NodeFsHandler = class {
|
|
|
12177
13207
|
this.fsw._symlinkPaths.set(absPath, targetPath);
|
|
12178
13208
|
}
|
|
12179
13209
|
} else if (stats.isSymbolicLink()) {
|
|
12180
|
-
const targetPath = follow ? await (0, import_promises2.realpath)(
|
|
13210
|
+
const targetPath = follow ? await (0, import_promises2.realpath)(path27) : path27;
|
|
12181
13211
|
if (this.fsw.closed)
|
|
12182
13212
|
return;
|
|
12183
13213
|
const parent = sp.dirname(wh.watchPath);
|
|
12184
13214
|
this.fsw._getWatchedDir(parent).add(wh.watchPath);
|
|
12185
13215
|
this.fsw._emit(EV.ADD, wh.watchPath, stats);
|
|
12186
|
-
closer = await this._handleDir(parent, stats, initialAdd, depth,
|
|
13216
|
+
closer = await this._handleDir(parent, stats, initialAdd, depth, path27, wh, targetPath);
|
|
12187
13217
|
if (this.fsw.closed)
|
|
12188
13218
|
return;
|
|
12189
13219
|
if (targetPath !== void 0) {
|
|
12190
|
-
this.fsw._symlinkPaths.set(sp.resolve(
|
|
13220
|
+
this.fsw._symlinkPaths.set(sp.resolve(path27), targetPath);
|
|
12191
13221
|
}
|
|
12192
13222
|
} else {
|
|
12193
13223
|
closer = this._handleFile(wh.watchPath, stats, initialAdd);
|
|
12194
13224
|
}
|
|
12195
13225
|
ready();
|
|
12196
13226
|
if (closer)
|
|
12197
|
-
this.fsw._addPathCloser(
|
|
13227
|
+
this.fsw._addPathCloser(path27, closer);
|
|
12198
13228
|
return false;
|
|
12199
13229
|
} catch (error) {
|
|
12200
13230
|
if (this.fsw._handleError(error)) {
|
|
12201
13231
|
ready();
|
|
12202
|
-
return
|
|
13232
|
+
return path27;
|
|
12203
13233
|
}
|
|
12204
13234
|
}
|
|
12205
13235
|
}
|
|
@@ -12242,24 +13272,24 @@ function createPattern(matcher) {
|
|
|
12242
13272
|
}
|
|
12243
13273
|
return () => false;
|
|
12244
13274
|
}
|
|
12245
|
-
function normalizePath2(
|
|
12246
|
-
if (typeof
|
|
13275
|
+
function normalizePath2(path27) {
|
|
13276
|
+
if (typeof path27 !== "string")
|
|
12247
13277
|
throw new Error("string expected");
|
|
12248
|
-
|
|
12249
|
-
|
|
13278
|
+
path27 = sp2.normalize(path27);
|
|
13279
|
+
path27 = path27.replace(/\\/g, "/");
|
|
12250
13280
|
let prepend = false;
|
|
12251
|
-
if (
|
|
13281
|
+
if (path27.startsWith("//"))
|
|
12252
13282
|
prepend = true;
|
|
12253
|
-
|
|
13283
|
+
path27 = path27.replace(DOUBLE_SLASH_RE, "/");
|
|
12254
13284
|
if (prepend)
|
|
12255
|
-
|
|
12256
|
-
return
|
|
13285
|
+
path27 = "/" + path27;
|
|
13286
|
+
return path27;
|
|
12257
13287
|
}
|
|
12258
13288
|
function matchPatterns(patterns, testString, stats) {
|
|
12259
|
-
const
|
|
13289
|
+
const path27 = normalizePath2(testString);
|
|
12260
13290
|
for (let index = 0; index < patterns.length; index++) {
|
|
12261
13291
|
const pattern = patterns[index];
|
|
12262
|
-
if (pattern(
|
|
13292
|
+
if (pattern(path27, stats)) {
|
|
12263
13293
|
return true;
|
|
12264
13294
|
}
|
|
12265
13295
|
}
|
|
@@ -12297,19 +13327,19 @@ var toUnix = (string) => {
|
|
|
12297
13327
|
}
|
|
12298
13328
|
return str;
|
|
12299
13329
|
};
|
|
12300
|
-
var normalizePathToUnix = (
|
|
12301
|
-
var normalizeIgnored = (cwd = "") => (
|
|
12302
|
-
if (typeof
|
|
12303
|
-
return normalizePathToUnix(sp2.isAbsolute(
|
|
13330
|
+
var normalizePathToUnix = (path27) => toUnix(sp2.normalize(toUnix(path27)));
|
|
13331
|
+
var normalizeIgnored = (cwd = "") => (path27) => {
|
|
13332
|
+
if (typeof path27 === "string") {
|
|
13333
|
+
return normalizePathToUnix(sp2.isAbsolute(path27) ? path27 : sp2.join(cwd, path27));
|
|
12304
13334
|
} else {
|
|
12305
|
-
return
|
|
13335
|
+
return path27;
|
|
12306
13336
|
}
|
|
12307
13337
|
};
|
|
12308
|
-
var getAbsolutePath = (
|
|
12309
|
-
if (sp2.isAbsolute(
|
|
12310
|
-
return
|
|
13338
|
+
var getAbsolutePath = (path27, cwd) => {
|
|
13339
|
+
if (sp2.isAbsolute(path27)) {
|
|
13340
|
+
return path27;
|
|
12311
13341
|
}
|
|
12312
|
-
return sp2.join(cwd,
|
|
13342
|
+
return sp2.join(cwd, path27);
|
|
12313
13343
|
};
|
|
12314
13344
|
var EMPTY_SET = Object.freeze(/* @__PURE__ */ new Set());
|
|
12315
13345
|
var DirEntry = class {
|
|
@@ -12374,10 +13404,10 @@ var WatchHelper = class {
|
|
|
12374
13404
|
dirParts;
|
|
12375
13405
|
followSymlinks;
|
|
12376
13406
|
statMethod;
|
|
12377
|
-
constructor(
|
|
13407
|
+
constructor(path27, follow, fsw) {
|
|
12378
13408
|
this.fsw = fsw;
|
|
12379
|
-
const watchPath =
|
|
12380
|
-
this.path =
|
|
13409
|
+
const watchPath = path27;
|
|
13410
|
+
this.path = path27 = path27.replace(REPLACER_RE, "");
|
|
12381
13411
|
this.watchPath = watchPath;
|
|
12382
13412
|
this.fullWatchPath = sp2.resolve(watchPath);
|
|
12383
13413
|
this.dirParts = [];
|
|
@@ -12517,20 +13547,20 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
12517
13547
|
this._closePromise = void 0;
|
|
12518
13548
|
let paths = unifyPaths(paths_);
|
|
12519
13549
|
if (cwd) {
|
|
12520
|
-
paths = paths.map((
|
|
12521
|
-
const absPath = getAbsolutePath(
|
|
13550
|
+
paths = paths.map((path27) => {
|
|
13551
|
+
const absPath = getAbsolutePath(path27, cwd);
|
|
12522
13552
|
return absPath;
|
|
12523
13553
|
});
|
|
12524
13554
|
}
|
|
12525
|
-
paths.forEach((
|
|
12526
|
-
this._removeIgnoredPath(
|
|
13555
|
+
paths.forEach((path27) => {
|
|
13556
|
+
this._removeIgnoredPath(path27);
|
|
12527
13557
|
});
|
|
12528
13558
|
this._userIgnored = void 0;
|
|
12529
13559
|
if (!this._readyCount)
|
|
12530
13560
|
this._readyCount = 0;
|
|
12531
13561
|
this._readyCount += paths.length;
|
|
12532
|
-
Promise.all(paths.map(async (
|
|
12533
|
-
const res = await this._nodeFsHandler._addToNodeFs(
|
|
13562
|
+
Promise.all(paths.map(async (path27) => {
|
|
13563
|
+
const res = await this._nodeFsHandler._addToNodeFs(path27, !_internal, void 0, 0, _origAdd);
|
|
12534
13564
|
if (res)
|
|
12535
13565
|
this._emitReady();
|
|
12536
13566
|
return res;
|
|
@@ -12552,17 +13582,17 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
12552
13582
|
return this;
|
|
12553
13583
|
const paths = unifyPaths(paths_);
|
|
12554
13584
|
const { cwd } = this.options;
|
|
12555
|
-
paths.forEach((
|
|
12556
|
-
if (!sp2.isAbsolute(
|
|
13585
|
+
paths.forEach((path27) => {
|
|
13586
|
+
if (!sp2.isAbsolute(path27) && !this._closers.has(path27)) {
|
|
12557
13587
|
if (cwd)
|
|
12558
|
-
|
|
12559
|
-
|
|
13588
|
+
path27 = sp2.join(cwd, path27);
|
|
13589
|
+
path27 = sp2.resolve(path27);
|
|
12560
13590
|
}
|
|
12561
|
-
this._closePath(
|
|
12562
|
-
this._addIgnoredPath(
|
|
12563
|
-
if (this._watched.has(
|
|
13591
|
+
this._closePath(path27);
|
|
13592
|
+
this._addIgnoredPath(path27);
|
|
13593
|
+
if (this._watched.has(path27)) {
|
|
12564
13594
|
this._addIgnoredPath({
|
|
12565
|
-
path:
|
|
13595
|
+
path: path27,
|
|
12566
13596
|
recursive: true
|
|
12567
13597
|
});
|
|
12568
13598
|
}
|
|
@@ -12626,38 +13656,38 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
12626
13656
|
* @param stats arguments to be passed with event
|
|
12627
13657
|
* @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag
|
|
12628
13658
|
*/
|
|
12629
|
-
async _emit(event,
|
|
13659
|
+
async _emit(event, path27, stats) {
|
|
12630
13660
|
if (this.closed)
|
|
12631
13661
|
return;
|
|
12632
13662
|
const opts = this.options;
|
|
12633
13663
|
if (isWindows)
|
|
12634
|
-
|
|
13664
|
+
path27 = sp2.normalize(path27);
|
|
12635
13665
|
if (opts.cwd)
|
|
12636
|
-
|
|
12637
|
-
const args = [
|
|
13666
|
+
path27 = sp2.relative(opts.cwd, path27);
|
|
13667
|
+
const args = [path27];
|
|
12638
13668
|
if (stats != null)
|
|
12639
13669
|
args.push(stats);
|
|
12640
13670
|
const awf = opts.awaitWriteFinish;
|
|
12641
13671
|
let pw;
|
|
12642
|
-
if (awf && (pw = this._pendingWrites.get(
|
|
13672
|
+
if (awf && (pw = this._pendingWrites.get(path27))) {
|
|
12643
13673
|
pw.lastChange = /* @__PURE__ */ new Date();
|
|
12644
13674
|
return this;
|
|
12645
13675
|
}
|
|
12646
13676
|
if (opts.atomic) {
|
|
12647
13677
|
if (event === EVENTS.UNLINK) {
|
|
12648
|
-
this._pendingUnlinks.set(
|
|
13678
|
+
this._pendingUnlinks.set(path27, [event, ...args]);
|
|
12649
13679
|
setTimeout(() => {
|
|
12650
|
-
this._pendingUnlinks.forEach((entry,
|
|
13680
|
+
this._pendingUnlinks.forEach((entry, path28) => {
|
|
12651
13681
|
this.emit(...entry);
|
|
12652
13682
|
this.emit(EVENTS.ALL, ...entry);
|
|
12653
|
-
this._pendingUnlinks.delete(
|
|
13683
|
+
this._pendingUnlinks.delete(path28);
|
|
12654
13684
|
});
|
|
12655
13685
|
}, typeof opts.atomic === "number" ? opts.atomic : 100);
|
|
12656
13686
|
return this;
|
|
12657
13687
|
}
|
|
12658
|
-
if (event === EVENTS.ADD && this._pendingUnlinks.has(
|
|
13688
|
+
if (event === EVENTS.ADD && this._pendingUnlinks.has(path27)) {
|
|
12659
13689
|
event = EVENTS.CHANGE;
|
|
12660
|
-
this._pendingUnlinks.delete(
|
|
13690
|
+
this._pendingUnlinks.delete(path27);
|
|
12661
13691
|
}
|
|
12662
13692
|
}
|
|
12663
13693
|
if (awf && (event === EVENTS.ADD || event === EVENTS.CHANGE) && this._readyEmitted) {
|
|
@@ -12675,16 +13705,16 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
12675
13705
|
this.emitWithAll(event, args);
|
|
12676
13706
|
}
|
|
12677
13707
|
};
|
|
12678
|
-
this._awaitWriteFinish(
|
|
13708
|
+
this._awaitWriteFinish(path27, awf.stabilityThreshold, event, awfEmit);
|
|
12679
13709
|
return this;
|
|
12680
13710
|
}
|
|
12681
13711
|
if (event === EVENTS.CHANGE) {
|
|
12682
|
-
const isThrottled = !this._throttle(EVENTS.CHANGE,
|
|
13712
|
+
const isThrottled = !this._throttle(EVENTS.CHANGE, path27, 50);
|
|
12683
13713
|
if (isThrottled)
|
|
12684
13714
|
return this;
|
|
12685
13715
|
}
|
|
12686
13716
|
if (opts.alwaysStat && stats === void 0 && (event === EVENTS.ADD || event === EVENTS.ADD_DIR || event === EVENTS.CHANGE)) {
|
|
12687
|
-
const fullPath = opts.cwd ? sp2.join(opts.cwd,
|
|
13717
|
+
const fullPath = opts.cwd ? sp2.join(opts.cwd, path27) : path27;
|
|
12688
13718
|
let stats2;
|
|
12689
13719
|
try {
|
|
12690
13720
|
stats2 = await (0, import_promises3.stat)(fullPath);
|
|
@@ -12715,23 +13745,23 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
12715
13745
|
* @param timeout duration of time to suppress duplicate actions
|
|
12716
13746
|
* @returns tracking object or false if action should be suppressed
|
|
12717
13747
|
*/
|
|
12718
|
-
_throttle(actionType,
|
|
13748
|
+
_throttle(actionType, path27, timeout) {
|
|
12719
13749
|
if (!this._throttled.has(actionType)) {
|
|
12720
13750
|
this._throttled.set(actionType, /* @__PURE__ */ new Map());
|
|
12721
13751
|
}
|
|
12722
13752
|
const action = this._throttled.get(actionType);
|
|
12723
13753
|
if (!action)
|
|
12724
13754
|
throw new Error("invalid throttle");
|
|
12725
|
-
const actionPath = action.get(
|
|
13755
|
+
const actionPath = action.get(path27);
|
|
12726
13756
|
if (actionPath) {
|
|
12727
13757
|
actionPath.count++;
|
|
12728
13758
|
return false;
|
|
12729
13759
|
}
|
|
12730
13760
|
let timeoutObject;
|
|
12731
13761
|
const clear = () => {
|
|
12732
|
-
const item = action.get(
|
|
13762
|
+
const item = action.get(path27);
|
|
12733
13763
|
const count = item ? item.count : 0;
|
|
12734
|
-
action.delete(
|
|
13764
|
+
action.delete(path27);
|
|
12735
13765
|
clearTimeout(timeoutObject);
|
|
12736
13766
|
if (item)
|
|
12737
13767
|
clearTimeout(item.timeoutObject);
|
|
@@ -12739,7 +13769,7 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
12739
13769
|
};
|
|
12740
13770
|
timeoutObject = setTimeout(clear, timeout);
|
|
12741
13771
|
const thr = { timeoutObject, clear, count: 0 };
|
|
12742
|
-
action.set(
|
|
13772
|
+
action.set(path27, thr);
|
|
12743
13773
|
return thr;
|
|
12744
13774
|
}
|
|
12745
13775
|
_incrReadyCount() {
|
|
@@ -12753,44 +13783,44 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
12753
13783
|
* @param event
|
|
12754
13784
|
* @param awfEmit Callback to be called when ready for event to be emitted.
|
|
12755
13785
|
*/
|
|
12756
|
-
_awaitWriteFinish(
|
|
13786
|
+
_awaitWriteFinish(path27, threshold, event, awfEmit) {
|
|
12757
13787
|
const awf = this.options.awaitWriteFinish;
|
|
12758
13788
|
if (typeof awf !== "object")
|
|
12759
13789
|
return;
|
|
12760
13790
|
const pollInterval = awf.pollInterval;
|
|
12761
13791
|
let timeoutHandler;
|
|
12762
|
-
let fullPath =
|
|
12763
|
-
if (this.options.cwd && !sp2.isAbsolute(
|
|
12764
|
-
fullPath = sp2.join(this.options.cwd,
|
|
13792
|
+
let fullPath = path27;
|
|
13793
|
+
if (this.options.cwd && !sp2.isAbsolute(path27)) {
|
|
13794
|
+
fullPath = sp2.join(this.options.cwd, path27);
|
|
12765
13795
|
}
|
|
12766
13796
|
const now = /* @__PURE__ */ new Date();
|
|
12767
13797
|
const writes = this._pendingWrites;
|
|
12768
13798
|
function awaitWriteFinishFn(prevStat) {
|
|
12769
13799
|
(0, import_node_fs2.stat)(fullPath, (err, curStat) => {
|
|
12770
|
-
if (err || !writes.has(
|
|
13800
|
+
if (err || !writes.has(path27)) {
|
|
12771
13801
|
if (err && err.code !== "ENOENT")
|
|
12772
13802
|
awfEmit(err);
|
|
12773
13803
|
return;
|
|
12774
13804
|
}
|
|
12775
13805
|
const now2 = Number(/* @__PURE__ */ new Date());
|
|
12776
13806
|
if (prevStat && curStat.size !== prevStat.size) {
|
|
12777
|
-
writes.get(
|
|
13807
|
+
writes.get(path27).lastChange = now2;
|
|
12778
13808
|
}
|
|
12779
|
-
const pw = writes.get(
|
|
13809
|
+
const pw = writes.get(path27);
|
|
12780
13810
|
const df = now2 - pw.lastChange;
|
|
12781
13811
|
if (df >= threshold) {
|
|
12782
|
-
writes.delete(
|
|
13812
|
+
writes.delete(path27);
|
|
12783
13813
|
awfEmit(void 0, curStat);
|
|
12784
13814
|
} else {
|
|
12785
13815
|
timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);
|
|
12786
13816
|
}
|
|
12787
13817
|
});
|
|
12788
13818
|
}
|
|
12789
|
-
if (!writes.has(
|
|
12790
|
-
writes.set(
|
|
13819
|
+
if (!writes.has(path27)) {
|
|
13820
|
+
writes.set(path27, {
|
|
12791
13821
|
lastChange: now,
|
|
12792
13822
|
cancelWait: () => {
|
|
12793
|
-
writes.delete(
|
|
13823
|
+
writes.delete(path27);
|
|
12794
13824
|
clearTimeout(timeoutHandler);
|
|
12795
13825
|
return event;
|
|
12796
13826
|
}
|
|
@@ -12801,8 +13831,8 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
12801
13831
|
/**
|
|
12802
13832
|
* Determines whether user has asked to ignore this path.
|
|
12803
13833
|
*/
|
|
12804
|
-
_isIgnored(
|
|
12805
|
-
if (this.options.atomic && DOT_RE.test(
|
|
13834
|
+
_isIgnored(path27, stats) {
|
|
13835
|
+
if (this.options.atomic && DOT_RE.test(path27))
|
|
12806
13836
|
return true;
|
|
12807
13837
|
if (!this._userIgnored) {
|
|
12808
13838
|
const { cwd } = this.options;
|
|
@@ -12812,17 +13842,17 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
12812
13842
|
const list = [...ignoredPaths.map(normalizeIgnored(cwd)), ...ignored];
|
|
12813
13843
|
this._userIgnored = anymatch(list, void 0);
|
|
12814
13844
|
}
|
|
12815
|
-
return this._userIgnored(
|
|
13845
|
+
return this._userIgnored(path27, stats);
|
|
12816
13846
|
}
|
|
12817
|
-
_isntIgnored(
|
|
12818
|
-
return !this._isIgnored(
|
|
13847
|
+
_isntIgnored(path27, stat4) {
|
|
13848
|
+
return !this._isIgnored(path27, stat4);
|
|
12819
13849
|
}
|
|
12820
13850
|
/**
|
|
12821
13851
|
* Provides a set of common helpers and properties relating to symlink handling.
|
|
12822
13852
|
* @param path file or directory pattern being watched
|
|
12823
13853
|
*/
|
|
12824
|
-
_getWatchHelpers(
|
|
12825
|
-
return new WatchHelper(
|
|
13854
|
+
_getWatchHelpers(path27) {
|
|
13855
|
+
return new WatchHelper(path27, this.options.followSymlinks, this);
|
|
12826
13856
|
}
|
|
12827
13857
|
// Directory helpers
|
|
12828
13858
|
// -----------------
|
|
@@ -12854,63 +13884,63 @@ var FSWatcher = class extends import_node_events.EventEmitter {
|
|
|
12854
13884
|
* @param item base path of item/directory
|
|
12855
13885
|
*/
|
|
12856
13886
|
_remove(directory, item, isDirectory) {
|
|
12857
|
-
const
|
|
12858
|
-
const fullPath = sp2.resolve(
|
|
12859
|
-
isDirectory = isDirectory != null ? isDirectory : this._watched.has(
|
|
12860
|
-
if (!this._throttle("remove",
|
|
13887
|
+
const path27 = sp2.join(directory, item);
|
|
13888
|
+
const fullPath = sp2.resolve(path27);
|
|
13889
|
+
isDirectory = isDirectory != null ? isDirectory : this._watched.has(path27) || this._watched.has(fullPath);
|
|
13890
|
+
if (!this._throttle("remove", path27, 100))
|
|
12861
13891
|
return;
|
|
12862
13892
|
if (!isDirectory && this._watched.size === 1) {
|
|
12863
13893
|
this.add(directory, item, true);
|
|
12864
13894
|
}
|
|
12865
|
-
const wp = this._getWatchedDir(
|
|
13895
|
+
const wp = this._getWatchedDir(path27);
|
|
12866
13896
|
const nestedDirectoryChildren = wp.getChildren();
|
|
12867
|
-
nestedDirectoryChildren.forEach((nested) => this._remove(
|
|
13897
|
+
nestedDirectoryChildren.forEach((nested) => this._remove(path27, nested));
|
|
12868
13898
|
const parent = this._getWatchedDir(directory);
|
|
12869
13899
|
const wasTracked = parent.has(item);
|
|
12870
13900
|
parent.remove(item);
|
|
12871
13901
|
if (this._symlinkPaths.has(fullPath)) {
|
|
12872
13902
|
this._symlinkPaths.delete(fullPath);
|
|
12873
13903
|
}
|
|
12874
|
-
let relPath =
|
|
13904
|
+
let relPath = path27;
|
|
12875
13905
|
if (this.options.cwd)
|
|
12876
|
-
relPath = sp2.relative(this.options.cwd,
|
|
13906
|
+
relPath = sp2.relative(this.options.cwd, path27);
|
|
12877
13907
|
if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
|
|
12878
13908
|
const event = this._pendingWrites.get(relPath).cancelWait();
|
|
12879
13909
|
if (event === EVENTS.ADD)
|
|
12880
13910
|
return;
|
|
12881
13911
|
}
|
|
12882
|
-
this._watched.delete(
|
|
13912
|
+
this._watched.delete(path27);
|
|
12883
13913
|
this._watched.delete(fullPath);
|
|
12884
13914
|
const eventName = isDirectory ? EVENTS.UNLINK_DIR : EVENTS.UNLINK;
|
|
12885
|
-
if (wasTracked && !this._isIgnored(
|
|
12886
|
-
this._emit(eventName,
|
|
12887
|
-
this._closePath(
|
|
13915
|
+
if (wasTracked && !this._isIgnored(path27))
|
|
13916
|
+
this._emit(eventName, path27);
|
|
13917
|
+
this._closePath(path27);
|
|
12888
13918
|
}
|
|
12889
13919
|
/**
|
|
12890
13920
|
* Closes all watchers for a path
|
|
12891
13921
|
*/
|
|
12892
|
-
_closePath(
|
|
12893
|
-
this._closeFile(
|
|
12894
|
-
const dir = sp2.dirname(
|
|
12895
|
-
this._getWatchedDir(dir).remove(sp2.basename(
|
|
13922
|
+
_closePath(path27) {
|
|
13923
|
+
this._closeFile(path27);
|
|
13924
|
+
const dir = sp2.dirname(path27);
|
|
13925
|
+
this._getWatchedDir(dir).remove(sp2.basename(path27));
|
|
12896
13926
|
}
|
|
12897
13927
|
/**
|
|
12898
13928
|
* Closes only file-specific watchers
|
|
12899
13929
|
*/
|
|
12900
|
-
_closeFile(
|
|
12901
|
-
const closers = this._closers.get(
|
|
13930
|
+
_closeFile(path27) {
|
|
13931
|
+
const closers = this._closers.get(path27);
|
|
12902
13932
|
if (!closers)
|
|
12903
13933
|
return;
|
|
12904
13934
|
closers.forEach((closer) => closer());
|
|
12905
|
-
this._closers.delete(
|
|
13935
|
+
this._closers.delete(path27);
|
|
12906
13936
|
}
|
|
12907
|
-
_addPathCloser(
|
|
13937
|
+
_addPathCloser(path27, closer) {
|
|
12908
13938
|
if (!closer)
|
|
12909
13939
|
return;
|
|
12910
|
-
let list = this._closers.get(
|
|
13940
|
+
let list = this._closers.get(path27);
|
|
12911
13941
|
if (!list) {
|
|
12912
13942
|
list = [];
|
|
12913
|
-
this._closers.set(
|
|
13943
|
+
this._closers.set(path27, list);
|
|
12914
13944
|
}
|
|
12915
13945
|
list.push(closer);
|
|
12916
13946
|
}
|
|
@@ -12940,7 +13970,7 @@ function watch(paths, options = {}) {
|
|
|
12940
13970
|
var chokidar_default = { watch, FSWatcher };
|
|
12941
13971
|
|
|
12942
13972
|
// src/watcher/file-watcher.ts
|
|
12943
|
-
var
|
|
13973
|
+
var path22 = __toESM(require("path"), 1);
|
|
12944
13974
|
var FileWatcher = class {
|
|
12945
13975
|
watcher = null;
|
|
12946
13976
|
projectRoot;
|
|
@@ -12951,6 +13981,8 @@ var FileWatcher = class {
|
|
|
12951
13981
|
debounceTimer = null;
|
|
12952
13982
|
debounceMs = 1e3;
|
|
12953
13983
|
onChanges = null;
|
|
13984
|
+
readyPromise = null;
|
|
13985
|
+
resolveReady = null;
|
|
12954
13986
|
constructor(projectRoot, config, host = "opencode", options = {}) {
|
|
12955
13987
|
this.projectRoot = projectRoot;
|
|
12956
13988
|
this.config = config;
|
|
@@ -12966,15 +13998,15 @@ var FileWatcher = class {
|
|
|
12966
13998
|
const watchTargets = this.configPath ? [this.projectRoot, this.configPath] : this.projectRoot;
|
|
12967
13999
|
this.watcher = chokidar_default.watch(watchTargets, {
|
|
12968
14000
|
ignored: (filePath) => {
|
|
12969
|
-
const relativePath =
|
|
14001
|
+
const relativePath = path22.relative(this.projectRoot, filePath);
|
|
12970
14002
|
if (!relativePath) return false;
|
|
12971
14003
|
if (this.isProjectConfigPathOrAncestor(relativePath)) {
|
|
12972
14004
|
return false;
|
|
12973
14005
|
}
|
|
12974
|
-
if (hasFilteredPathSegment(relativePath,
|
|
14006
|
+
if (hasFilteredPathSegment(relativePath, path22.sep)) {
|
|
12975
14007
|
return true;
|
|
12976
14008
|
}
|
|
12977
|
-
if (isRestrictedDirectory(relativePath,
|
|
14009
|
+
if (isRestrictedDirectory(relativePath, path22.sep)) {
|
|
12978
14010
|
return true;
|
|
12979
14011
|
}
|
|
12980
14012
|
if (ignoreFilter.ignores(relativePath)) {
|
|
@@ -12989,6 +14021,13 @@ var FileWatcher = class {
|
|
|
12989
14021
|
pollInterval: 100
|
|
12990
14022
|
}
|
|
12991
14023
|
});
|
|
14024
|
+
this.readyPromise = new Promise((resolve15) => {
|
|
14025
|
+
this.resolveReady = resolve15;
|
|
14026
|
+
});
|
|
14027
|
+
this.watcher.once("ready", () => {
|
|
14028
|
+
this.resolveReady?.();
|
|
14029
|
+
this.resolveReady = null;
|
|
14030
|
+
});
|
|
12992
14031
|
this.watcher.on("error", (error) => {
|
|
12993
14032
|
const err = error instanceof Error ? error : null;
|
|
12994
14033
|
if (err?.code === "EPERM" || err?.code === "EACCES") {
|
|
@@ -13020,24 +14059,24 @@ var FileWatcher = class {
|
|
|
13020
14059
|
this.scheduleFlush();
|
|
13021
14060
|
}
|
|
13022
14061
|
isProjectConfigPath(filePath) {
|
|
13023
|
-
const relativePath =
|
|
13024
|
-
const normalizedRelativePath =
|
|
14062
|
+
const relativePath = path22.relative(this.projectRoot, filePath);
|
|
14063
|
+
const normalizedRelativePath = path22.normalize(relativePath);
|
|
13025
14064
|
return this.getProjectConfigRelativePaths().some((configPath) => configPath === normalizedRelativePath);
|
|
13026
14065
|
}
|
|
13027
14066
|
isProjectConfigPathOrAncestor(relativePath) {
|
|
13028
|
-
const normalizedRelativePath =
|
|
14067
|
+
const normalizedRelativePath = path22.normalize(relativePath);
|
|
13029
14068
|
return this.getProjectConfigRelativePaths().some(
|
|
13030
|
-
(configPath) => configPath === normalizedRelativePath || configPath.startsWith(`${normalizedRelativePath}${
|
|
14069
|
+
(configPath) => configPath === normalizedRelativePath || configPath.startsWith(`${normalizedRelativePath}${path22.sep}`)
|
|
13031
14070
|
);
|
|
13032
14071
|
}
|
|
13033
14072
|
getProjectConfigRelativePaths() {
|
|
13034
14073
|
if (this.configPath) {
|
|
13035
|
-
return [
|
|
14074
|
+
return [path22.normalize(path22.relative(this.projectRoot, this.configPath))];
|
|
13036
14075
|
}
|
|
13037
14076
|
return [
|
|
13038
14077
|
resolveProjectConfigPath(this.projectRoot, this.host),
|
|
13039
14078
|
resolveWritableProjectConfigPath(this.projectRoot, this.host)
|
|
13040
|
-
].map((configPath) =>
|
|
14079
|
+
].map((configPath) => path22.normalize(path22.relative(this.projectRoot, configPath)));
|
|
13041
14080
|
}
|
|
13042
14081
|
scheduleFlush() {
|
|
13043
14082
|
if (this.debounceTimer) {
|
|
@@ -13052,7 +14091,7 @@ var FileWatcher = class {
|
|
|
13052
14091
|
return;
|
|
13053
14092
|
}
|
|
13054
14093
|
const changes = Array.from(this.pendingChanges.entries()).map(
|
|
13055
|
-
([
|
|
14094
|
+
([path27, type]) => ({ path: path27, type })
|
|
13056
14095
|
);
|
|
13057
14096
|
this.pendingChanges.clear();
|
|
13058
14097
|
try {
|
|
@@ -13061,25 +14100,32 @@ var FileWatcher = class {
|
|
|
13061
14100
|
console.error("Error handling file changes:", error);
|
|
13062
14101
|
}
|
|
13063
14102
|
}
|
|
13064
|
-
stop() {
|
|
14103
|
+
async stop() {
|
|
13065
14104
|
if (this.debounceTimer) {
|
|
13066
14105
|
clearTimeout(this.debounceTimer);
|
|
13067
14106
|
this.debounceTimer = null;
|
|
13068
14107
|
}
|
|
13069
14108
|
if (this.watcher) {
|
|
13070
|
-
this.watcher
|
|
14109
|
+
const watcher = this.watcher;
|
|
13071
14110
|
this.watcher = null;
|
|
14111
|
+
await watcher.close();
|
|
13072
14112
|
}
|
|
14113
|
+
this.resolveReady?.();
|
|
14114
|
+
this.resolveReady = null;
|
|
14115
|
+
this.readyPromise = null;
|
|
13073
14116
|
this.pendingChanges.clear();
|
|
13074
14117
|
this.onChanges = null;
|
|
13075
14118
|
}
|
|
13076
14119
|
isRunning() {
|
|
13077
14120
|
return this.watcher !== null;
|
|
13078
14121
|
}
|
|
14122
|
+
async waitUntilReady() {
|
|
14123
|
+
await (this.readyPromise ?? Promise.resolve());
|
|
14124
|
+
}
|
|
13079
14125
|
};
|
|
13080
14126
|
|
|
13081
14127
|
// src/watcher/git-head-watcher.ts
|
|
13082
|
-
var
|
|
14128
|
+
var path23 = __toESM(require("path"), 1);
|
|
13083
14129
|
var GitHeadWatcher = class {
|
|
13084
14130
|
watcher = null;
|
|
13085
14131
|
projectRoot;
|
|
@@ -13101,7 +14147,7 @@ var GitHeadWatcher = class {
|
|
|
13101
14147
|
this.onBranchChange = handler;
|
|
13102
14148
|
this.currentBranch = getCurrentBranch(this.projectRoot);
|
|
13103
14149
|
const headPath = getHeadPath(this.projectRoot);
|
|
13104
|
-
const refsPath =
|
|
14150
|
+
const refsPath = path23.join(this.projectRoot, ".git", "refs", "heads");
|
|
13105
14151
|
this.watcher = chokidar_default.watch([headPath, refsPath], {
|
|
13106
14152
|
persistent: true,
|
|
13107
14153
|
ignoreInitial: true,
|
|
@@ -13138,14 +14184,15 @@ var GitHeadWatcher = class {
|
|
|
13138
14184
|
getCurrentBranch() {
|
|
13139
14185
|
return this.currentBranch;
|
|
13140
14186
|
}
|
|
13141
|
-
stop() {
|
|
14187
|
+
async stop() {
|
|
13142
14188
|
if (this.debounceTimer) {
|
|
13143
14189
|
clearTimeout(this.debounceTimer);
|
|
13144
14190
|
this.debounceTimer = null;
|
|
13145
14191
|
}
|
|
13146
14192
|
if (this.watcher) {
|
|
13147
|
-
this.watcher
|
|
14193
|
+
const watcher = this.watcher;
|
|
13148
14194
|
this.watcher = null;
|
|
14195
|
+
await watcher.close();
|
|
13149
14196
|
}
|
|
13150
14197
|
this.onBranchChange = null;
|
|
13151
14198
|
}
|
|
@@ -13163,6 +14210,8 @@ var BackgroundReindexer = class {
|
|
|
13163
14210
|
running = false;
|
|
13164
14211
|
pending = false;
|
|
13165
14212
|
stopped = false;
|
|
14213
|
+
retryTimer = null;
|
|
14214
|
+
retryDelayMs = 50;
|
|
13166
14215
|
request() {
|
|
13167
14216
|
if (this.stopped) {
|
|
13168
14217
|
return;
|
|
@@ -13173,9 +14222,13 @@ var BackgroundReindexer = class {
|
|
|
13173
14222
|
stop() {
|
|
13174
14223
|
this.stopped = true;
|
|
13175
14224
|
this.pending = false;
|
|
14225
|
+
if (this.retryTimer) {
|
|
14226
|
+
clearTimeout(this.retryTimer);
|
|
14227
|
+
this.retryTimer = null;
|
|
14228
|
+
}
|
|
13176
14229
|
}
|
|
13177
14230
|
drain() {
|
|
13178
|
-
if (this.stopped || this.running || !this.pending) {
|
|
14231
|
+
if (this.stopped || this.running || this.retryTimer || !this.pending) {
|
|
13179
14232
|
return;
|
|
13180
14233
|
}
|
|
13181
14234
|
this.pending = false;
|
|
@@ -13183,13 +14236,29 @@ var BackgroundReindexer = class {
|
|
|
13183
14236
|
void this.run();
|
|
13184
14237
|
}
|
|
13185
14238
|
async run() {
|
|
14239
|
+
let retryAfterContention = false;
|
|
13186
14240
|
try {
|
|
13187
14241
|
await this.runIndex();
|
|
14242
|
+
this.retryDelayMs = 50;
|
|
13188
14243
|
} catch (error) {
|
|
13189
|
-
|
|
14244
|
+
if (isTransientIndexLockContention(error)) {
|
|
14245
|
+
this.pending = true;
|
|
14246
|
+
retryAfterContention = true;
|
|
14247
|
+
} else {
|
|
14248
|
+
console.error("[codebase-index] Background reindex failed:", error);
|
|
14249
|
+
}
|
|
13190
14250
|
} finally {
|
|
13191
14251
|
this.running = false;
|
|
13192
|
-
this.
|
|
14252
|
+
if (retryAfterContention && !this.stopped) {
|
|
14253
|
+
const delay = this.retryDelayMs;
|
|
14254
|
+
this.retryDelayMs = Math.min(this.retryDelayMs * 2, 500);
|
|
14255
|
+
this.retryTimer = setTimeout(() => {
|
|
14256
|
+
this.retryTimer = null;
|
|
14257
|
+
this.drain();
|
|
14258
|
+
}, delay);
|
|
14259
|
+
} else {
|
|
14260
|
+
this.drain();
|
|
14261
|
+
}
|
|
13193
14262
|
}
|
|
13194
14263
|
}
|
|
13195
14264
|
};
|
|
@@ -13223,10 +14292,12 @@ function createWatcherWithIndexer(getIndexer, projectRoot, config, host = "openc
|
|
|
13223
14292
|
return {
|
|
13224
14293
|
fileWatcher,
|
|
13225
14294
|
gitWatcher,
|
|
13226
|
-
|
|
14295
|
+
whenReady() {
|
|
14296
|
+
return fileWatcher.waitUntilReady();
|
|
14297
|
+
},
|
|
14298
|
+
async stop() {
|
|
13227
14299
|
backgroundReindexer.stop();
|
|
13228
|
-
fileWatcher.stop();
|
|
13229
|
-
gitWatcher?.stop();
|
|
14300
|
+
await Promise.all([fileWatcher.stop(), gitWatcher?.stop()]);
|
|
13230
14301
|
}
|
|
13231
14302
|
};
|
|
13232
14303
|
}
|
|
@@ -13245,7 +14316,7 @@ function getConfigPaths(projectRoot, host, options) {
|
|
|
13245
14316
|
|
|
13246
14317
|
// src/tools/visualize/activity.ts
|
|
13247
14318
|
var import_child_process4 = require("child_process");
|
|
13248
|
-
var
|
|
14319
|
+
var path24 = __toESM(require("path"), 1);
|
|
13249
14320
|
function attachRecentActivity(data, projectRoot) {
|
|
13250
14321
|
const activity = readGitActivity(projectRoot);
|
|
13251
14322
|
const changes = activity.size > 0 ? buildGitChanges(data, activity, projectRoot) : buildGraphChanges(data);
|
|
@@ -13407,7 +14478,7 @@ function normalizePath3(filePath) {
|
|
|
13407
14478
|
return filePath.replace(/\\/g, "/");
|
|
13408
14479
|
}
|
|
13409
14480
|
function toGitRelativePath(projectRoot, filePath) {
|
|
13410
|
-
const relativePath =
|
|
14481
|
+
const relativePath = path24.isAbsolute(filePath) ? path24.relative(projectRoot, filePath) : filePath;
|
|
13411
14482
|
return normalizePath3(relativePath);
|
|
13412
14483
|
}
|
|
13413
14484
|
|
|
@@ -13665,7 +14736,7 @@ render();
|
|
|
13665
14736
|
}
|
|
13666
14737
|
|
|
13667
14738
|
// src/tools/visualize/transform.ts
|
|
13668
|
-
var
|
|
14739
|
+
var path25 = __toESM(require("path"), 1);
|
|
13669
14740
|
|
|
13670
14741
|
// src/tools/visualize/modules.ts
|
|
13671
14742
|
var MAX_MODULES = 18;
|
|
@@ -13925,7 +14996,7 @@ function transformForVisualization(symbols, edges, options = {}) {
|
|
|
13925
14996
|
filePath: s.filePath,
|
|
13926
14997
|
kind: s.kind,
|
|
13927
14998
|
line: s.startLine,
|
|
13928
|
-
directory:
|
|
14999
|
+
directory: path25.dirname(s.filePath),
|
|
13929
15000
|
moduleId: "",
|
|
13930
15001
|
moduleLabel: ""
|
|
13931
15002
|
}));
|
|
@@ -13954,9 +15025,9 @@ function parseArgs(argv) {
|
|
|
13954
15025
|
let host = "opencode";
|
|
13955
15026
|
for (let i = 2; i < argv.length; i++) {
|
|
13956
15027
|
if (argv[i] === "--project" && argv[i + 1]) {
|
|
13957
|
-
project =
|
|
15028
|
+
project = path26.resolve(argv[++i]);
|
|
13958
15029
|
} else if (argv[i] === "--config" && argv[i + 1]) {
|
|
13959
|
-
config =
|
|
15030
|
+
config = path26.resolve(argv[++i]);
|
|
13960
15031
|
} else if (argv[i] === "--host" && argv[i + 1]) {
|
|
13961
15032
|
host = parseHostMode(argv[++i]);
|
|
13962
15033
|
} else if (argv[i] === "--host") {
|
|
@@ -13969,7 +15040,7 @@ function loadCliRawConfig(args) {
|
|
|
13969
15040
|
return args.config ? loadConfigFile(args.config) : loadMergedConfig(args.project, args.host);
|
|
13970
15041
|
}
|
|
13971
15042
|
function isCliEntrypoint(moduleUrl, argvPath) {
|
|
13972
|
-
return argvPath !== void 0 && (0,
|
|
15043
|
+
return argvPath !== void 0 && (0, import_fs16.realpathSync)((0, import_url2.fileURLToPath)(moduleUrl)) === (0, import_fs16.realpathSync)(argvPath);
|
|
13973
15044
|
}
|
|
13974
15045
|
function parseVisualizeArgs(argv, cwd) {
|
|
13975
15046
|
let project = cwd;
|
|
@@ -13979,7 +15050,7 @@ function parseVisualizeArgs(argv, cwd) {
|
|
|
13979
15050
|
for (let i = 0; i < argv.length; i++) {
|
|
13980
15051
|
const arg = argv[i];
|
|
13981
15052
|
if (arg === "--project" && argv[i + 1]) {
|
|
13982
|
-
project =
|
|
15053
|
+
project = path26.resolve(argv[++i]);
|
|
13983
15054
|
} else if (arg === "--max" && argv[i + 1]) {
|
|
13984
15055
|
maxNodes = Number(argv[++i]);
|
|
13985
15056
|
} else if (arg.startsWith("--max=") || arg.startsWith("max=")) {
|
|
@@ -14016,8 +15087,8 @@ async function handleVisualizeCommand(argv, cwd) {
|
|
|
14016
15087
|
console.error("No connected symbols found. Retry with: npm run visualize -- orphans");
|
|
14017
15088
|
return 1;
|
|
14018
15089
|
}
|
|
14019
|
-
const outputPath =
|
|
14020
|
-
(0,
|
|
15090
|
+
const outputPath = path26.join(os6.tmpdir(), `call-graph-${Date.now()}.html`);
|
|
15091
|
+
(0, import_fs16.writeFileSync)(outputPath, generateVisualizationHtml(vizData), "utf-8");
|
|
14021
15092
|
console.log(`Temporal call graph visualization generated: ${outputPath}`);
|
|
14022
15093
|
console.log(`Nodes: ${vizData.nodes.length} | Edges: ${vizData.edges.length}`);
|
|
14023
15094
|
console.log(`Recent change lenses: ${vizData.changes?.length ?? 0}`);
|
|
@@ -14046,7 +15117,7 @@ async function main() {
|
|
|
14046
15117
|
const transport = new import_stdio.StdioServerTransport();
|
|
14047
15118
|
await server.connect(transport);
|
|
14048
15119
|
let watcher = null;
|
|
14049
|
-
const isHomeDir =
|
|
15120
|
+
const isHomeDir = path26.resolve(args.project) === path26.resolve(os6.homedir());
|
|
14050
15121
|
const isValidProject = !isHomeDir && (!config.indexing.requireProjectMarker || hasProjectMarker(args.project));
|
|
14051
15122
|
if (config.indexing.autoIndex && isValidProject) {
|
|
14052
15123
|
const indexer = getIndexerForProject(args.project, args.host);
|
|
@@ -14061,14 +15132,25 @@ async function main() {
|
|
|
14061
15132
|
args.config ? { configPath: args.config } : {}
|
|
14062
15133
|
);
|
|
14063
15134
|
}
|
|
14064
|
-
|
|
14065
|
-
|
|
14066
|
-
|
|
14067
|
-
|
|
14068
|
-
|
|
15135
|
+
let shuttingDown = false;
|
|
15136
|
+
const shutdown = async () => {
|
|
15137
|
+
if (shuttingDown) return;
|
|
15138
|
+
shuttingDown = true;
|
|
15139
|
+
try {
|
|
15140
|
+
await watcher?.stop();
|
|
15141
|
+
await server.close();
|
|
15142
|
+
process.exit(0);
|
|
15143
|
+
} catch (error) {
|
|
15144
|
+
console.error("Failed to stop MCP server cleanly:", error);
|
|
15145
|
+
process.exit(1);
|
|
15146
|
+
}
|
|
14069
15147
|
};
|
|
14070
|
-
process.on("SIGINT",
|
|
14071
|
-
|
|
15148
|
+
process.on("SIGINT", () => {
|
|
15149
|
+
void shutdown();
|
|
15150
|
+
});
|
|
15151
|
+
process.on("SIGTERM", () => {
|
|
15152
|
+
void shutdown();
|
|
15153
|
+
});
|
|
14072
15154
|
}
|
|
14073
15155
|
function handleMainError(error) {
|
|
14074
15156
|
if (error instanceof Error && error.message.startsWith("Invalid host mode")) {
|