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.js
CHANGED
|
@@ -491,7 +491,7 @@ var require_ignore = __commonJS({
|
|
|
491
491
|
// path matching.
|
|
492
492
|
// - check `string` either `MODE_IGNORE` or `MODE_CHECK_IGNORE`
|
|
493
493
|
// @returns {TestResult} true if a file is ignored
|
|
494
|
-
test(
|
|
494
|
+
test(path27, checkUnignored, mode) {
|
|
495
495
|
let ignored = false;
|
|
496
496
|
let unignored = false;
|
|
497
497
|
let matchedRule;
|
|
@@ -500,7 +500,7 @@ var require_ignore = __commonJS({
|
|
|
500
500
|
if (unignored === negative && ignored !== unignored || negative && !ignored && !unignored && !checkUnignored) {
|
|
501
501
|
return;
|
|
502
502
|
}
|
|
503
|
-
const matched = rule[mode].test(
|
|
503
|
+
const matched = rule[mode].test(path27);
|
|
504
504
|
if (!matched) {
|
|
505
505
|
return;
|
|
506
506
|
}
|
|
@@ -521,17 +521,17 @@ var require_ignore = __commonJS({
|
|
|
521
521
|
var throwError = (message, Ctor) => {
|
|
522
522
|
throw new Ctor(message);
|
|
523
523
|
};
|
|
524
|
-
var checkPath = (
|
|
525
|
-
if (!isString(
|
|
524
|
+
var checkPath = (path27, originalPath, doThrow) => {
|
|
525
|
+
if (!isString(path27)) {
|
|
526
526
|
return doThrow(
|
|
527
527
|
`path must be a string, but got \`${originalPath}\``,
|
|
528
528
|
TypeError
|
|
529
529
|
);
|
|
530
530
|
}
|
|
531
|
-
if (!
|
|
531
|
+
if (!path27) {
|
|
532
532
|
return doThrow(`path must not be empty`, TypeError);
|
|
533
533
|
}
|
|
534
|
-
if (checkPath.isNotRelative(
|
|
534
|
+
if (checkPath.isNotRelative(path27)) {
|
|
535
535
|
const r = "`path.relative()`d";
|
|
536
536
|
return doThrow(
|
|
537
537
|
`path should be a ${r} string, but got "${originalPath}"`,
|
|
@@ -540,7 +540,7 @@ var require_ignore = __commonJS({
|
|
|
540
540
|
}
|
|
541
541
|
return true;
|
|
542
542
|
};
|
|
543
|
-
var isNotRelative = (
|
|
543
|
+
var isNotRelative = (path27) => REGEX_TEST_INVALID_PATH.test(path27);
|
|
544
544
|
checkPath.isNotRelative = isNotRelative;
|
|
545
545
|
checkPath.convert = (p) => p;
|
|
546
546
|
var Ignore2 = class {
|
|
@@ -570,19 +570,19 @@ var require_ignore = __commonJS({
|
|
|
570
570
|
}
|
|
571
571
|
// @returns {TestResult}
|
|
572
572
|
_test(originalPath, cache, checkUnignored, slices) {
|
|
573
|
-
const
|
|
573
|
+
const path27 = originalPath && checkPath.convert(originalPath);
|
|
574
574
|
checkPath(
|
|
575
|
-
|
|
575
|
+
path27,
|
|
576
576
|
originalPath,
|
|
577
577
|
this._strictPathCheck ? throwError : RETURN_FALSE
|
|
578
578
|
);
|
|
579
|
-
return this._t(
|
|
579
|
+
return this._t(path27, cache, checkUnignored, slices);
|
|
580
580
|
}
|
|
581
|
-
checkIgnore(
|
|
582
|
-
if (!REGEX_TEST_TRAILING_SLASH.test(
|
|
583
|
-
return this.test(
|
|
581
|
+
checkIgnore(path27) {
|
|
582
|
+
if (!REGEX_TEST_TRAILING_SLASH.test(path27)) {
|
|
583
|
+
return this.test(path27);
|
|
584
584
|
}
|
|
585
|
-
const slices =
|
|
585
|
+
const slices = path27.split(SLASH2).filter(Boolean);
|
|
586
586
|
slices.pop();
|
|
587
587
|
if (slices.length) {
|
|
588
588
|
const parent = this._t(
|
|
@@ -595,18 +595,18 @@ var require_ignore = __commonJS({
|
|
|
595
595
|
return parent;
|
|
596
596
|
}
|
|
597
597
|
}
|
|
598
|
-
return this._rules.test(
|
|
598
|
+
return this._rules.test(path27, false, MODE_CHECK_IGNORE);
|
|
599
599
|
}
|
|
600
|
-
_t(
|
|
601
|
-
if (
|
|
602
|
-
return cache[
|
|
600
|
+
_t(path27, cache, checkUnignored, slices) {
|
|
601
|
+
if (path27 in cache) {
|
|
602
|
+
return cache[path27];
|
|
603
603
|
}
|
|
604
604
|
if (!slices) {
|
|
605
|
-
slices =
|
|
605
|
+
slices = path27.split(SLASH2).filter(Boolean);
|
|
606
606
|
}
|
|
607
607
|
slices.pop();
|
|
608
608
|
if (!slices.length) {
|
|
609
|
-
return cache[
|
|
609
|
+
return cache[path27] = this._rules.test(path27, checkUnignored, MODE_IGNORE);
|
|
610
610
|
}
|
|
611
611
|
const parent = this._t(
|
|
612
612
|
slices.join(SLASH2) + SLASH2,
|
|
@@ -614,29 +614,29 @@ var require_ignore = __commonJS({
|
|
|
614
614
|
checkUnignored,
|
|
615
615
|
slices
|
|
616
616
|
);
|
|
617
|
-
return cache[
|
|
617
|
+
return cache[path27] = parent.ignored ? parent : this._rules.test(path27, checkUnignored, MODE_IGNORE);
|
|
618
618
|
}
|
|
619
|
-
ignores(
|
|
620
|
-
return this._test(
|
|
619
|
+
ignores(path27) {
|
|
620
|
+
return this._test(path27, this._ignoreCache, false).ignored;
|
|
621
621
|
}
|
|
622
622
|
createFilter() {
|
|
623
|
-
return (
|
|
623
|
+
return (path27) => !this.ignores(path27);
|
|
624
624
|
}
|
|
625
625
|
filter(paths) {
|
|
626
626
|
return makeArray(paths).filter(this.createFilter());
|
|
627
627
|
}
|
|
628
628
|
// @returns {TestResult}
|
|
629
|
-
test(
|
|
630
|
-
return this._test(
|
|
629
|
+
test(path27) {
|
|
630
|
+
return this._test(path27, this._testCache, true);
|
|
631
631
|
}
|
|
632
632
|
};
|
|
633
633
|
var factory = (options) => new Ignore2(options);
|
|
634
|
-
var isPathValid = (
|
|
634
|
+
var isPathValid = (path27) => checkPath(path27 && checkPath.convert(path27), path27, RETURN_FALSE);
|
|
635
635
|
var setupWindows = () => {
|
|
636
636
|
const makePosix = (str) => /^\\\\\?\\/.test(str) || /["<>|\u0000-\u001F]+/u.test(str) ? str : str.replace(/\\/g, "/");
|
|
637
637
|
checkPath.convert = makePosix;
|
|
638
638
|
const REGEX_TEST_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i;
|
|
639
|
-
checkPath.isNotRelative = (
|
|
639
|
+
checkPath.isNotRelative = (path27) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path27) || isNotRelative(path27);
|
|
640
640
|
};
|
|
641
641
|
if (
|
|
642
642
|
// Detect `process` so that it can run in browsers.
|
|
@@ -653,17 +653,17 @@ var require_ignore = __commonJS({
|
|
|
653
653
|
|
|
654
654
|
// src/cli.ts
|
|
655
655
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
656
|
-
import { realpathSync as
|
|
657
|
-
import * as
|
|
658
|
-
import * as
|
|
656
|
+
import { realpathSync as realpathSync3, writeFileSync as writeFileSync7 } from "fs";
|
|
657
|
+
import * as os6 from "os";
|
|
658
|
+
import * as path26 from "path";
|
|
659
659
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
660
660
|
|
|
661
661
|
// src/config/constants.ts
|
|
662
662
|
var DEFAULT_INCLUDE = [
|
|
663
|
-
"**/*.{ts,tsx,js,jsx,mjs,cjs}",
|
|
663
|
+
"**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}",
|
|
664
664
|
"**/*.{py,pyi}",
|
|
665
|
-
"**/*.{go,rs,java,kt,scala}",
|
|
666
|
-
"**/*.{c,cpp,cc,h,hpp}",
|
|
665
|
+
"**/*.{go,rs,java,cs,kt,scala}",
|
|
666
|
+
"**/*.{c,cpp,cc,cxx,h,hpp,hxx}",
|
|
667
667
|
"**/*.{rb,php,inc,swift}",
|
|
668
668
|
"**/*.{cls,trigger}",
|
|
669
669
|
"**/*.{vue,svelte,astro}",
|
|
@@ -1046,7 +1046,7 @@ var autoDetectProviders = AUTO_DETECT_PROVIDER_ORDER.filter(
|
|
|
1046
1046
|
);
|
|
1047
1047
|
|
|
1048
1048
|
// src/config/host.ts
|
|
1049
|
-
var HOST_MODES = ["opencode", "codex", "claude", "pi"];
|
|
1049
|
+
var HOST_MODES = ["opencode", "codex", "claude", "pi", "jcode"];
|
|
1050
1050
|
function isSupportedHostMode(value) {
|
|
1051
1051
|
return HOST_MODES.includes(value);
|
|
1052
1052
|
}
|
|
@@ -1101,9 +1101,9 @@ import { mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
|
1101
1101
|
import * as path from "path";
|
|
1102
1102
|
|
|
1103
1103
|
// src/eval/report-formatters.ts
|
|
1104
|
-
function assertFiniteNumber(value,
|
|
1104
|
+
function assertFiniteNumber(value, path27) {
|
|
1105
1105
|
if (typeof value !== "number" || Number.isNaN(value) || !Number.isFinite(value)) {
|
|
1106
|
-
throw new Error(`${
|
|
1106
|
+
throw new Error(`${path27} must be a finite number`);
|
|
1107
1107
|
}
|
|
1108
1108
|
return value;
|
|
1109
1109
|
}
|
|
@@ -1291,13 +1291,13 @@ function buildPerQueryArtifact(perQuery) {
|
|
|
1291
1291
|
}
|
|
1292
1292
|
|
|
1293
1293
|
// src/eval/runner.ts
|
|
1294
|
-
import { existsSync as
|
|
1295
|
-
import * as
|
|
1294
|
+
import { existsSync as existsSync9 } from "fs";
|
|
1295
|
+
import * as path15 from "path";
|
|
1296
1296
|
import { performance as performance3 } from "perf_hooks";
|
|
1297
1297
|
|
|
1298
1298
|
// src/indexer/index.ts
|
|
1299
|
-
import { existsSync as
|
|
1300
|
-
import * as
|
|
1299
|
+
import { existsSync as existsSync7, readFileSync as readFileSync7, statSync as statSync3, writeFileSync as writeFileSync3, renameSync as renameSync2, unlinkSync, mkdirSync as mkdirSync3, promises as fsPromises2 } from "fs";
|
|
1300
|
+
import * as path12 from "path";
|
|
1301
1301
|
import { performance as performance2 } from "perf_hooks";
|
|
1302
1302
|
import { execFile as execFile3 } from "child_process";
|
|
1303
1303
|
import { promisify as promisify3 } from "util";
|
|
@@ -2579,17 +2579,17 @@ function validateExternalUrl(urlString) {
|
|
|
2579
2579
|
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
2580
2580
|
return { valid: false, reason: `Blocked protocol: ${parsed.protocol}` };
|
|
2581
2581
|
}
|
|
2582
|
-
const
|
|
2583
|
-
if (BLOCKED_HOSTNAMES.has(
|
|
2584
|
-
return { valid: false, reason: `Blocked: cloud metadata service (${
|
|
2582
|
+
const hostname2 = parsed.hostname.toLowerCase();
|
|
2583
|
+
if (BLOCKED_HOSTNAMES.has(hostname2)) {
|
|
2584
|
+
return { valid: false, reason: `Blocked: cloud metadata service (${hostname2})` };
|
|
2585
2585
|
}
|
|
2586
2586
|
for (const pattern of BLOCKED_METADATA_IPS) {
|
|
2587
|
-
if (pattern.test(
|
|
2588
|
-
return { valid: false, reason: `Blocked: cloud metadata IP (${
|
|
2587
|
+
if (pattern.test(hostname2)) {
|
|
2588
|
+
return { valid: false, reason: `Blocked: cloud metadata IP (${hostname2})` };
|
|
2589
2589
|
}
|
|
2590
2590
|
}
|
|
2591
|
-
if (/^169\.254\./.test(
|
|
2592
|
-
return { valid: false, reason: `Blocked: link-local address (${
|
|
2591
|
+
if (/^169\.254\./.test(hostname2)) {
|
|
2592
|
+
return { valid: false, reason: `Blocked: link-local address (${hostname2})` };
|
|
2593
2593
|
}
|
|
2594
2594
|
return { valid: true };
|
|
2595
2595
|
}
|
|
@@ -3779,6 +3779,12 @@ function createMockNativeBinding() {
|
|
|
3779
3779
|
constructor() {
|
|
3780
3780
|
throw error;
|
|
3781
3781
|
}
|
|
3782
|
+
static openReadOnly() {
|
|
3783
|
+
throw error;
|
|
3784
|
+
}
|
|
3785
|
+
static createEmptyReadOnly() {
|
|
3786
|
+
throw error;
|
|
3787
|
+
}
|
|
3782
3788
|
close() {
|
|
3783
3789
|
throw error;
|
|
3784
3790
|
}
|
|
@@ -3882,6 +3888,12 @@ var VectorStore = class {
|
|
|
3882
3888
|
load() {
|
|
3883
3889
|
this.inner.load();
|
|
3884
3890
|
}
|
|
3891
|
+
loadStrict() {
|
|
3892
|
+
this.inner.loadStrict();
|
|
3893
|
+
}
|
|
3894
|
+
hasFingerprint() {
|
|
3895
|
+
return this.inner.hasFingerprint();
|
|
3896
|
+
}
|
|
3885
3897
|
count() {
|
|
3886
3898
|
return this.inner.count();
|
|
3887
3899
|
}
|
|
@@ -4164,12 +4176,24 @@ var InvertedIndex = class {
|
|
|
4164
4176
|
return this.inner.documentCount();
|
|
4165
4177
|
}
|
|
4166
4178
|
};
|
|
4167
|
-
var Database = class {
|
|
4179
|
+
var Database = class _Database {
|
|
4168
4180
|
inner;
|
|
4169
4181
|
closed = false;
|
|
4170
4182
|
constructor(dbPath) {
|
|
4171
4183
|
this.inner = new native.Database(dbPath);
|
|
4172
4184
|
}
|
|
4185
|
+
static fromNative(inner) {
|
|
4186
|
+
const database = Object.create(_Database.prototype);
|
|
4187
|
+
database.inner = inner;
|
|
4188
|
+
database.closed = false;
|
|
4189
|
+
return database;
|
|
4190
|
+
}
|
|
4191
|
+
static openReadOnly(dbPath) {
|
|
4192
|
+
return _Database.fromNative(native.Database.openReadOnly(dbPath));
|
|
4193
|
+
}
|
|
4194
|
+
static createEmptyReadOnly() {
|
|
4195
|
+
return _Database.fromNative(native.Database.createEmptyReadOnly());
|
|
4196
|
+
}
|
|
4173
4197
|
throwIfClosed() {
|
|
4174
4198
|
if (this.closed) {
|
|
4175
4199
|
throw new Error("Database is closed");
|
|
@@ -4638,6 +4662,9 @@ function getHostProjectIndexRelativePath(host) {
|
|
|
4638
4662
|
function hasHostProjectConfig(projectRoot, host) {
|
|
4639
4663
|
return existsSync5(path8.join(projectRoot, getProjectConfigRelativePath(host)));
|
|
4640
4664
|
}
|
|
4665
|
+
function hasHostGlobalConfig(host) {
|
|
4666
|
+
return existsSync5(getGlobalConfigPath(host));
|
|
4667
|
+
}
|
|
4641
4668
|
function getGlobalIndexPath(host = "opencode") {
|
|
4642
4669
|
switch (host) {
|
|
4643
4670
|
case "opencode":
|
|
@@ -4677,6 +4704,9 @@ function resolveGlobalIndexPath(host = "opencode") {
|
|
|
4677
4704
|
return hostIndexPath;
|
|
4678
4705
|
}
|
|
4679
4706
|
if (host !== "opencode") {
|
|
4707
|
+
if (hasHostGlobalConfig(host)) {
|
|
4708
|
+
return hostIndexPath;
|
|
4709
|
+
}
|
|
4680
4710
|
const legacyIndexPath = getGlobalIndexPath("opencode");
|
|
4681
4711
|
if (existsSync5(legacyIndexPath)) {
|
|
4682
4712
|
return legacyIndexPath;
|
|
@@ -4893,6 +4923,432 @@ async function getChunkGitBlame(projectRoot, filePath, startLine, endLine) {
|
|
|
4893
4923
|
}
|
|
4894
4924
|
}
|
|
4895
4925
|
|
|
4926
|
+
// src/indexer/index-lock.ts
|
|
4927
|
+
import { randomUUID } from "crypto";
|
|
4928
|
+
import {
|
|
4929
|
+
existsSync as existsSync6,
|
|
4930
|
+
lstatSync,
|
|
4931
|
+
mkdirSync as mkdirSync2,
|
|
4932
|
+
readFileSync as readFileSync6,
|
|
4933
|
+
readdirSync as readdirSync2,
|
|
4934
|
+
realpathSync,
|
|
4935
|
+
renameSync,
|
|
4936
|
+
rmSync,
|
|
4937
|
+
writeFileSync as writeFileSync2
|
|
4938
|
+
} from "fs";
|
|
4939
|
+
import * as os4 from "os";
|
|
4940
|
+
import * as path11 from "path";
|
|
4941
|
+
var OWNER_FILE_NAME = "owner.json";
|
|
4942
|
+
var RECLAIM_DIRECTORY_NAME = "reclaiming";
|
|
4943
|
+
var RECOVERY_MARKER_PREFIX = "indexing.lock.recovery.";
|
|
4944
|
+
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;
|
|
4945
|
+
var VALID_OPERATIONS = /* @__PURE__ */ new Set([
|
|
4946
|
+
"initialize",
|
|
4947
|
+
"index",
|
|
4948
|
+
"force-index",
|
|
4949
|
+
"clear",
|
|
4950
|
+
"health-check",
|
|
4951
|
+
"retry-failed-batches",
|
|
4952
|
+
"recovery"
|
|
4953
|
+
]);
|
|
4954
|
+
var temporaryCounter = 0;
|
|
4955
|
+
function getErrorCode(error) {
|
|
4956
|
+
return typeof error === "object" && error !== null && "code" in error ? String(error.code) : void 0;
|
|
4957
|
+
}
|
|
4958
|
+
function retryTransientFilesystemOperation(operation) {
|
|
4959
|
+
let lastError;
|
|
4960
|
+
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
4961
|
+
try {
|
|
4962
|
+
operation();
|
|
4963
|
+
return;
|
|
4964
|
+
} catch (error) {
|
|
4965
|
+
lastError = error;
|
|
4966
|
+
const code = getErrorCode(error);
|
|
4967
|
+
if (code !== "EBUSY" && code !== "EPERM") throw error;
|
|
4968
|
+
if (attempt < 2) {
|
|
4969
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, (attempt + 1) * 10);
|
|
4970
|
+
}
|
|
4971
|
+
}
|
|
4972
|
+
}
|
|
4973
|
+
throw lastError;
|
|
4974
|
+
}
|
|
4975
|
+
function parseOwner(value) {
|
|
4976
|
+
if (typeof value !== "object" || value === null) return null;
|
|
4977
|
+
const candidate = value;
|
|
4978
|
+
if (!Number.isInteger(candidate.pid) || (candidate.pid ?? 0) <= 0) return null;
|
|
4979
|
+
if (typeof candidate.hostname !== "string" || candidate.hostname.length === 0) return null;
|
|
4980
|
+
if (typeof candidate.startedAt !== "string" || Number.isNaN(Date.parse(candidate.startedAt))) return null;
|
|
4981
|
+
if (typeof candidate.operation !== "string" || !VALID_OPERATIONS.has(candidate.operation)) return null;
|
|
4982
|
+
if (typeof candidate.token !== "string" || !UUID_PATTERN.test(candidate.token)) return null;
|
|
4983
|
+
return candidate;
|
|
4984
|
+
}
|
|
4985
|
+
function parseReclaimOwner(value) {
|
|
4986
|
+
if (typeof value !== "object" || value === null) return null;
|
|
4987
|
+
const candidate = value;
|
|
4988
|
+
if (!Number.isInteger(candidate.pid) || (candidate.pid ?? 0) <= 0) return null;
|
|
4989
|
+
if (typeof candidate.hostname !== "string" || candidate.hostname.length === 0) return null;
|
|
4990
|
+
if (typeof candidate.startedAt !== "string" || Number.isNaN(Date.parse(candidate.startedAt))) return null;
|
|
4991
|
+
if (typeof candidate.token !== "string" || !UUID_PATTERN.test(candidate.token)) return null;
|
|
4992
|
+
if (typeof candidate.expectedOwnerToken !== "string" || !UUID_PATTERN.test(candidate.expectedOwnerToken)) return null;
|
|
4993
|
+
return candidate;
|
|
4994
|
+
}
|
|
4995
|
+
function readJsonDirectory(directoryPath, parser) {
|
|
4996
|
+
try {
|
|
4997
|
+
return parser(JSON.parse(readFileSync6(path11.join(directoryPath, OWNER_FILE_NAME), "utf-8")));
|
|
4998
|
+
} catch {
|
|
4999
|
+
return null;
|
|
5000
|
+
}
|
|
5001
|
+
}
|
|
5002
|
+
function readDirectoryOwner(lockPath) {
|
|
5003
|
+
return readJsonDirectory(lockPath, parseOwner);
|
|
5004
|
+
}
|
|
5005
|
+
function readReclaimOwner(markerPath) {
|
|
5006
|
+
return readJsonDirectory(markerPath, parseReclaimOwner);
|
|
5007
|
+
}
|
|
5008
|
+
function readRecoveryOwner(markerPath) {
|
|
5009
|
+
return readDirectoryOwner(markerPath);
|
|
5010
|
+
}
|
|
5011
|
+
function readLegacyOwner(lockPath) {
|
|
5012
|
+
try {
|
|
5013
|
+
const parsed = JSON.parse(readFileSync6(lockPath, "utf-8"));
|
|
5014
|
+
if (!Number.isInteger(parsed.pid) || Number(parsed.pid) <= 0) return null;
|
|
5015
|
+
if (typeof parsed.startedAt !== "string" || Number.isNaN(Date.parse(parsed.startedAt))) return null;
|
|
5016
|
+
return {
|
|
5017
|
+
pid: Number(parsed.pid),
|
|
5018
|
+
hostname: typeof parsed.hostname === "string" ? parsed.hostname : os4.hostname(),
|
|
5019
|
+
startedAt: parsed.startedAt,
|
|
5020
|
+
operation: typeof parsed.operation === "string" && VALID_OPERATIONS.has(parsed.operation) ? parsed.operation : "index",
|
|
5021
|
+
token: typeof parsed.token === "string" ? parsed.token : "legacy-v0.14.0"
|
|
5022
|
+
};
|
|
5023
|
+
} catch {
|
|
5024
|
+
return null;
|
|
5025
|
+
}
|
|
5026
|
+
}
|
|
5027
|
+
function getOwnerLiveness(owner) {
|
|
5028
|
+
if (owner.hostname !== os4.hostname()) return "unknown";
|
|
5029
|
+
try {
|
|
5030
|
+
process.kill(owner.pid, 0);
|
|
5031
|
+
return "alive";
|
|
5032
|
+
} catch (error) {
|
|
5033
|
+
const code = getErrorCode(error);
|
|
5034
|
+
if (code === "ESRCH") return "dead";
|
|
5035
|
+
if (code === "EPERM") return "alive";
|
|
5036
|
+
return "unknown";
|
|
5037
|
+
}
|
|
5038
|
+
}
|
|
5039
|
+
function sameOwner(left, right) {
|
|
5040
|
+
return left.pid === right.pid && left.hostname === right.hostname && left.token === right.token;
|
|
5041
|
+
}
|
|
5042
|
+
function sameReclaimOwner(left, right) {
|
|
5043
|
+
return left.pid === right.pid && left.hostname === right.hostname && left.token === right.token && left.expectedOwnerToken === right.expectedOwnerToken;
|
|
5044
|
+
}
|
|
5045
|
+
function publishJsonDirectory(finalPath, value) {
|
|
5046
|
+
const candidatePath = `${finalPath}.candidate.${process.pid}.${randomUUID()}`;
|
|
5047
|
+
mkdirSync2(candidatePath, { mode: 448 });
|
|
5048
|
+
try {
|
|
5049
|
+
writeFileSync2(path11.join(candidatePath, OWNER_FILE_NAME), JSON.stringify(value), {
|
|
5050
|
+
encoding: "utf-8",
|
|
5051
|
+
flag: "wx",
|
|
5052
|
+
mode: 384
|
|
5053
|
+
});
|
|
5054
|
+
if (existsSync6(finalPath)) return false;
|
|
5055
|
+
try {
|
|
5056
|
+
renameSync(candidatePath, finalPath);
|
|
5057
|
+
return true;
|
|
5058
|
+
} catch (error) {
|
|
5059
|
+
if (existsSync6(finalPath)) return false;
|
|
5060
|
+
throw error;
|
|
5061
|
+
}
|
|
5062
|
+
} finally {
|
|
5063
|
+
if (existsSync6(candidatePath)) rmSync(candidatePath, { recursive: true, force: true });
|
|
5064
|
+
}
|
|
5065
|
+
}
|
|
5066
|
+
function createOwner(operation) {
|
|
5067
|
+
return {
|
|
5068
|
+
pid: process.pid,
|
|
5069
|
+
hostname: os4.hostname(),
|
|
5070
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5071
|
+
operation,
|
|
5072
|
+
token: randomUUID()
|
|
5073
|
+
};
|
|
5074
|
+
}
|
|
5075
|
+
function recoveryMarkerPath(indexPath, owner) {
|
|
5076
|
+
return path11.join(indexPath, `${RECOVERY_MARKER_PREFIX}${owner.token}`);
|
|
5077
|
+
}
|
|
5078
|
+
function publishRecoveryMarker(indexPath, owner) {
|
|
5079
|
+
const markerPath = recoveryMarkerPath(indexPath, owner);
|
|
5080
|
+
if (!publishJsonDirectory(markerPath, owner)) {
|
|
5081
|
+
const markerOwner = readRecoveryOwner(markerPath);
|
|
5082
|
+
if (!markerOwner || !sameOwner(markerOwner, owner)) {
|
|
5083
|
+
throw new IndexLockContentionError(markerPath, markerOwner, "unknown-owner");
|
|
5084
|
+
}
|
|
5085
|
+
}
|
|
5086
|
+
return markerPath;
|
|
5087
|
+
}
|
|
5088
|
+
function getPendingRecoveries(indexPath) {
|
|
5089
|
+
const recoveries = [];
|
|
5090
|
+
const markerNames = readdirSync2(indexPath).filter((name) => {
|
|
5091
|
+
if (!name.startsWith(RECOVERY_MARKER_PREFIX)) return false;
|
|
5092
|
+
return UUID_PATTERN.test(name.slice(RECOVERY_MARKER_PREFIX.length));
|
|
5093
|
+
}).sort();
|
|
5094
|
+
for (const markerName of markerNames) {
|
|
5095
|
+
const markerPath = path11.join(indexPath, markerName);
|
|
5096
|
+
const markerToken = markerName.slice(RECOVERY_MARKER_PREFIX.length);
|
|
5097
|
+
let markerStats;
|
|
5098
|
+
try {
|
|
5099
|
+
markerStats = lstatSync(markerPath);
|
|
5100
|
+
} catch (error) {
|
|
5101
|
+
if (getErrorCode(error) === "ENOENT") continue;
|
|
5102
|
+
throw error;
|
|
5103
|
+
}
|
|
5104
|
+
if (!markerStats.isDirectory()) {
|
|
5105
|
+
throw new IndexLockContentionError(markerPath, null, "unknown-owner");
|
|
5106
|
+
}
|
|
5107
|
+
const owner = readRecoveryOwner(markerPath);
|
|
5108
|
+
if (!owner || owner.token !== markerToken || getOwnerLiveness(owner) !== "dead") {
|
|
5109
|
+
throw new IndexLockContentionError(markerPath, owner, "unknown-owner");
|
|
5110
|
+
}
|
|
5111
|
+
recoveries.push({ owner, markerPath });
|
|
5112
|
+
}
|
|
5113
|
+
recoveries.sort((left, right) => {
|
|
5114
|
+
const byTimestamp = left.owner.startedAt.localeCompare(right.owner.startedAt);
|
|
5115
|
+
return byTimestamp !== 0 ? byTimestamp : left.markerPath.localeCompare(right.markerPath);
|
|
5116
|
+
});
|
|
5117
|
+
return recoveries;
|
|
5118
|
+
}
|
|
5119
|
+
function cleanupDeadPublicationCandidates(indexPath) {
|
|
5120
|
+
const candidatePattern = /^indexing\.lock(?:\.recovery\.[0-9a-f-]{36})?\.candidate\.(\d+)\.[0-9a-f-]{36}$/i;
|
|
5121
|
+
for (const entry of readdirSync2(indexPath)) {
|
|
5122
|
+
const match = candidatePattern.exec(entry);
|
|
5123
|
+
if (!match) continue;
|
|
5124
|
+
const pid = Number(match[1]);
|
|
5125
|
+
if (!Number.isInteger(pid) || pid <= 0) continue;
|
|
5126
|
+
if (getOwnerLiveness({ pid, hostname: os4.hostname() }) !== "dead") continue;
|
|
5127
|
+
rmSync(path11.join(indexPath, entry), { recursive: true, force: true });
|
|
5128
|
+
}
|
|
5129
|
+
}
|
|
5130
|
+
function removeDeadReclaimMarker(lockPath, expectedOwner) {
|
|
5131
|
+
const markerPath = path11.join(lockPath, RECLAIM_DIRECTORY_NAME);
|
|
5132
|
+
const marker = readReclaimOwner(markerPath);
|
|
5133
|
+
if (!marker || marker.expectedOwnerToken !== expectedOwner.token || getOwnerLiveness(marker) !== "dead") {
|
|
5134
|
+
return false;
|
|
5135
|
+
}
|
|
5136
|
+
const currentOwner = readDirectoryOwner(lockPath);
|
|
5137
|
+
if (!currentOwner || !sameOwner(currentOwner, expectedOwner) || getOwnerLiveness(currentOwner) !== "dead") {
|
|
5138
|
+
return false;
|
|
5139
|
+
}
|
|
5140
|
+
const claimedMarkerPath = `${markerPath}.stale.${marker.pid}.${marker.token}.${randomUUID()}`;
|
|
5141
|
+
try {
|
|
5142
|
+
renameSync(markerPath, claimedMarkerPath);
|
|
5143
|
+
} catch (error) {
|
|
5144
|
+
if (getErrorCode(error) === "ENOENT") return false;
|
|
5145
|
+
throw error;
|
|
5146
|
+
}
|
|
5147
|
+
const claimedMarker = readReclaimOwner(claimedMarkerPath);
|
|
5148
|
+
const ownerAfterClaim = readDirectoryOwner(lockPath);
|
|
5149
|
+
if (!claimedMarker || !sameReclaimOwner(claimedMarker, marker) || !ownerAfterClaim || !sameOwner(ownerAfterClaim, expectedOwner) || getOwnerLiveness(ownerAfterClaim) !== "dead") {
|
|
5150
|
+
if (!existsSync6(markerPath) && existsSync6(claimedMarkerPath)) {
|
|
5151
|
+
renameSync(claimedMarkerPath, markerPath);
|
|
5152
|
+
}
|
|
5153
|
+
return false;
|
|
5154
|
+
}
|
|
5155
|
+
rmSync(claimedMarkerPath, { recursive: true, force: true });
|
|
5156
|
+
return true;
|
|
5157
|
+
}
|
|
5158
|
+
function reclaimDeadOwner(indexPath, lockPath, expectedOwner) {
|
|
5159
|
+
const reclaimPath = path11.join(lockPath, RECLAIM_DIRECTORY_NAME);
|
|
5160
|
+
const reclaimOwner = {
|
|
5161
|
+
pid: process.pid,
|
|
5162
|
+
hostname: os4.hostname(),
|
|
5163
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5164
|
+
token: randomUUID(),
|
|
5165
|
+
expectedOwnerToken: expectedOwner.token
|
|
5166
|
+
};
|
|
5167
|
+
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
5168
|
+
if (publishJsonDirectory(reclaimPath, reclaimOwner)) break;
|
|
5169
|
+
if (attempt === 0 && removeDeadReclaimMarker(lockPath, expectedOwner)) continue;
|
|
5170
|
+
return false;
|
|
5171
|
+
}
|
|
5172
|
+
try {
|
|
5173
|
+
const currentReclaimer = readReclaimOwner(reclaimPath);
|
|
5174
|
+
const currentOwner = readDirectoryOwner(lockPath);
|
|
5175
|
+
if (!currentReclaimer || !sameReclaimOwner(currentReclaimer, reclaimOwner) || !currentOwner || !sameOwner(currentOwner, expectedOwner) || getOwnerLiveness(currentOwner) !== "dead") {
|
|
5176
|
+
return false;
|
|
5177
|
+
}
|
|
5178
|
+
publishRecoveryMarker(indexPath, expectedOwner);
|
|
5179
|
+
const ownerBeforeQuarantine = readDirectoryOwner(lockPath);
|
|
5180
|
+
const reclaimerBeforeQuarantine = readReclaimOwner(reclaimPath);
|
|
5181
|
+
if (!ownerBeforeQuarantine || !sameOwner(ownerBeforeQuarantine, expectedOwner) || getOwnerLiveness(ownerBeforeQuarantine) !== "dead" || !reclaimerBeforeQuarantine || !sameReclaimOwner(reclaimerBeforeQuarantine, reclaimOwner)) {
|
|
5182
|
+
return false;
|
|
5183
|
+
}
|
|
5184
|
+
const quarantinePath = `${lockPath}.stale.${expectedOwner.token}.${reclaimOwner.token}`;
|
|
5185
|
+
renameSync(lockPath, quarantinePath);
|
|
5186
|
+
rmSync(quarantinePath, { recursive: true, force: true });
|
|
5187
|
+
return true;
|
|
5188
|
+
} catch (error) {
|
|
5189
|
+
if (getErrorCode(error) === "ENOENT") return false;
|
|
5190
|
+
throw error;
|
|
5191
|
+
}
|
|
5192
|
+
}
|
|
5193
|
+
var IndexLockContentionError = class extends Error {
|
|
5194
|
+
constructor(lockPath, owner, reason) {
|
|
5195
|
+
const ownerDescription = owner ? `PID ${owner.pid} on ${owner.hostname}, operation ${owner.operation}, since ${owner.startedAt}` : "an unreadable owner";
|
|
5196
|
+
super(`Index mutation already in progress: ${ownerDescription}`);
|
|
5197
|
+
this.lockPath = lockPath;
|
|
5198
|
+
this.owner = owner;
|
|
5199
|
+
this.reason = reason;
|
|
5200
|
+
this.name = "IndexLockContentionError";
|
|
5201
|
+
}
|
|
5202
|
+
lockPath;
|
|
5203
|
+
owner;
|
|
5204
|
+
reason;
|
|
5205
|
+
code = "INDEX_BUSY";
|
|
5206
|
+
};
|
|
5207
|
+
function isIndexLockContentionError(error) {
|
|
5208
|
+
return error instanceof IndexLockContentionError || typeof error === "object" && error !== null && "code" in error && error.code === "INDEX_BUSY";
|
|
5209
|
+
}
|
|
5210
|
+
function isTransientIndexLockContention(error) {
|
|
5211
|
+
if (!isIndexLockContentionError(error) || !("reason" in error)) return false;
|
|
5212
|
+
return error.reason === "active" || error.reason === "reclaiming";
|
|
5213
|
+
}
|
|
5214
|
+
function acquireIndexLock(indexPath, operation) {
|
|
5215
|
+
mkdirSync2(indexPath, { recursive: true });
|
|
5216
|
+
const canonicalIndexPath = realpathSync.native(indexPath);
|
|
5217
|
+
const lockPath = path11.join(canonicalIndexPath, "indexing.lock");
|
|
5218
|
+
cleanupDeadPublicationCandidates(canonicalIndexPath);
|
|
5219
|
+
for (let attempt = 0; attempt < 6; attempt += 1) {
|
|
5220
|
+
const owner = createOwner(operation);
|
|
5221
|
+
if (publishJsonDirectory(lockPath, owner)) {
|
|
5222
|
+
const lease = {
|
|
5223
|
+
canonicalIndexPath,
|
|
5224
|
+
lockPath,
|
|
5225
|
+
owner,
|
|
5226
|
+
recoveries: []
|
|
5227
|
+
};
|
|
5228
|
+
try {
|
|
5229
|
+
lease.recoveries = getPendingRecoveries(canonicalIndexPath);
|
|
5230
|
+
return lease;
|
|
5231
|
+
} catch (error) {
|
|
5232
|
+
releaseIndexLock(lease);
|
|
5233
|
+
throw error;
|
|
5234
|
+
}
|
|
5235
|
+
}
|
|
5236
|
+
let stats;
|
|
5237
|
+
try {
|
|
5238
|
+
stats = lstatSync(lockPath);
|
|
5239
|
+
} catch (error) {
|
|
5240
|
+
if (getErrorCode(error) === "ENOENT") continue;
|
|
5241
|
+
throw error;
|
|
5242
|
+
}
|
|
5243
|
+
if (!stats.isDirectory()) {
|
|
5244
|
+
const legacyOwner = readLegacyOwner(lockPath);
|
|
5245
|
+
throw new IndexLockContentionError(lockPath, legacyOwner, "legacy-lock");
|
|
5246
|
+
}
|
|
5247
|
+
const existingOwner = readDirectoryOwner(lockPath);
|
|
5248
|
+
if (!existingOwner) {
|
|
5249
|
+
throw new IndexLockContentionError(lockPath, null, "unknown-owner");
|
|
5250
|
+
}
|
|
5251
|
+
const liveness = getOwnerLiveness(existingOwner);
|
|
5252
|
+
if (liveness !== "dead") {
|
|
5253
|
+
throw new IndexLockContentionError(lockPath, existingOwner, liveness === "alive" ? "active" : "unknown-owner");
|
|
5254
|
+
}
|
|
5255
|
+
if (!reclaimDeadOwner(canonicalIndexPath, lockPath, existingOwner)) {
|
|
5256
|
+
throw new IndexLockContentionError(lockPath, existingOwner, "reclaiming");
|
|
5257
|
+
}
|
|
5258
|
+
}
|
|
5259
|
+
throw new IndexLockContentionError(lockPath, null, "reclaiming");
|
|
5260
|
+
}
|
|
5261
|
+
function releaseIndexLock(lease) {
|
|
5262
|
+
const currentOwner = readDirectoryOwner(lease.lockPath);
|
|
5263
|
+
if (!currentOwner || !sameOwner(currentOwner, lease.owner)) return false;
|
|
5264
|
+
const releasePath = `${lease.lockPath}.release.${lease.owner.pid}.${lease.owner.token}`;
|
|
5265
|
+
try {
|
|
5266
|
+
retryTransientFilesystemOperation(() => renameSync(lease.lockPath, releasePath));
|
|
5267
|
+
} catch (error) {
|
|
5268
|
+
if (getErrorCode(error) === "ENOENT") return false;
|
|
5269
|
+
throw error;
|
|
5270
|
+
}
|
|
5271
|
+
const claimedOwner = readDirectoryOwner(releasePath);
|
|
5272
|
+
if (!claimedOwner || !sameOwner(claimedOwner, lease.owner)) {
|
|
5273
|
+
if (!existsSync6(lease.lockPath) && existsSync6(releasePath)) {
|
|
5274
|
+
renameSync(releasePath, lease.lockPath);
|
|
5275
|
+
}
|
|
5276
|
+
return false;
|
|
5277
|
+
}
|
|
5278
|
+
try {
|
|
5279
|
+
retryTransientFilesystemOperation(() => rmSync(releasePath, { recursive: true, force: true }));
|
|
5280
|
+
} catch (error) {
|
|
5281
|
+
console.error(`[codebase-index] Lease released but tombstone cleanup failed: ${releasePath}`, error);
|
|
5282
|
+
}
|
|
5283
|
+
return true;
|
|
5284
|
+
}
|
|
5285
|
+
async function withIndexLock(indexPath, operation, callback, options = {}) {
|
|
5286
|
+
const lease = acquireIndexLock(indexPath, operation);
|
|
5287
|
+
let result;
|
|
5288
|
+
let callbackError;
|
|
5289
|
+
let callbackFailed = false;
|
|
5290
|
+
try {
|
|
5291
|
+
result = await callback(lease);
|
|
5292
|
+
} catch (error) {
|
|
5293
|
+
callbackFailed = true;
|
|
5294
|
+
callbackError = error;
|
|
5295
|
+
}
|
|
5296
|
+
if (!callbackFailed && options.completeRecoveries !== false) {
|
|
5297
|
+
try {
|
|
5298
|
+
completeLeaseRecovery(lease);
|
|
5299
|
+
} catch (error) {
|
|
5300
|
+
callbackFailed = true;
|
|
5301
|
+
callbackError = error;
|
|
5302
|
+
}
|
|
5303
|
+
}
|
|
5304
|
+
let releaseError;
|
|
5305
|
+
try {
|
|
5306
|
+
if (!releaseIndexLock(lease)) {
|
|
5307
|
+
releaseError = new Error(`Lost ownership of index mutation lease ${lease.owner.token}`);
|
|
5308
|
+
}
|
|
5309
|
+
} catch (error) {
|
|
5310
|
+
releaseError = error;
|
|
5311
|
+
}
|
|
5312
|
+
if (releaseError !== void 0) {
|
|
5313
|
+
if (callbackFailed) throw new AggregateError([callbackError, releaseError], "Index mutation and lease release both failed");
|
|
5314
|
+
throw releaseError;
|
|
5315
|
+
}
|
|
5316
|
+
if (callbackFailed) throw callbackError;
|
|
5317
|
+
return result;
|
|
5318
|
+
}
|
|
5319
|
+
function createLeaseTemporaryPath(targetPath, owner, kind = "tmp") {
|
|
5320
|
+
if (kind === "bak") return `${targetPath}.bak.${owner.pid}.${owner.token}`;
|
|
5321
|
+
temporaryCounter += 1;
|
|
5322
|
+
return `${targetPath}.tmp.${owner.pid}.${owner.token}.${temporaryCounter}`;
|
|
5323
|
+
}
|
|
5324
|
+
function removeLeaseTemporaryPath(temporaryPath) {
|
|
5325
|
+
if (existsSync6(temporaryPath)) rmSync(temporaryPath, { recursive: true, force: true });
|
|
5326
|
+
}
|
|
5327
|
+
function recoverLeaseArtifacts(indexPath, owner, backupTargets) {
|
|
5328
|
+
for (const targetPath of backupTargets) {
|
|
5329
|
+
const backupPath = createLeaseTemporaryPath(targetPath, owner, "bak");
|
|
5330
|
+
if (!existsSync6(backupPath)) continue;
|
|
5331
|
+
if (existsSync6(targetPath)) rmSync(targetPath, { recursive: true, force: true });
|
|
5332
|
+
renameSync(backupPath, targetPath);
|
|
5333
|
+
}
|
|
5334
|
+
const temporaryOwnerMarker = `.tmp.${owner.pid}.${owner.token}.`;
|
|
5335
|
+
for (const entry of readdirSync2(indexPath)) {
|
|
5336
|
+
if (!entry.includes(temporaryOwnerMarker)) continue;
|
|
5337
|
+
rmSync(path11.join(indexPath, entry), { recursive: true, force: true });
|
|
5338
|
+
}
|
|
5339
|
+
}
|
|
5340
|
+
function completeLeaseRecovery(lease) {
|
|
5341
|
+
for (const recovery of lease.recoveries) {
|
|
5342
|
+
const markerOwner = readRecoveryOwner(recovery.markerPath);
|
|
5343
|
+
if (!markerOwner || !sameOwner(markerOwner, recovery.owner)) {
|
|
5344
|
+
throw new Error(`Recovery marker ownership changed: ${recovery.markerPath}`);
|
|
5345
|
+
}
|
|
5346
|
+
}
|
|
5347
|
+
for (const recovery of lease.recoveries) {
|
|
5348
|
+
rmSync(recovery.markerPath, { recursive: true, force: true });
|
|
5349
|
+
}
|
|
5350
|
+
}
|
|
5351
|
+
|
|
4896
5352
|
// src/indexer/index.ts
|
|
4897
5353
|
var CALL_GRAPH_LANGUAGES = /* @__PURE__ */ new Set(["typescript", "tsx", "javascript", "jsx", "python", "go", "rust", "php", "apex", "zig", "gdscript", "matlab", "bash"]);
|
|
4898
5354
|
var CASE_INSENSITIVE_LANGUAGES = /* @__PURE__ */ new Set(["apex"]);
|
|
@@ -4974,6 +5430,7 @@ function isSqliteCorruptionError(error) {
|
|
|
4974
5430
|
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");
|
|
4975
5431
|
}
|
|
4976
5432
|
var STARTUP_WARNING_METADATA_KEY = "index.startupWarning";
|
|
5433
|
+
var READER_ARTIFACT_RETRY_INTERVAL_MS = 1e3;
|
|
4977
5434
|
function metadataFromBlame(blame) {
|
|
4978
5435
|
if (!blame) {
|
|
4979
5436
|
return {};
|
|
@@ -5171,9 +5628,9 @@ function hasAllEmbeddingParts(parts, expectedPartCount) {
|
|
|
5171
5628
|
return true;
|
|
5172
5629
|
}
|
|
5173
5630
|
function isPathWithinRoot(filePath, rootPath) {
|
|
5174
|
-
const normalizedFilePath =
|
|
5175
|
-
const normalizedRoot =
|
|
5176
|
-
return normalizedFilePath === normalizedRoot || normalizedFilePath.startsWith(`${normalizedRoot}${
|
|
5631
|
+
const normalizedFilePath = path12.resolve(filePath);
|
|
5632
|
+
const normalizedRoot = path12.resolve(rootPath);
|
|
5633
|
+
return normalizedFilePath === normalizedRoot || normalizedFilePath.startsWith(`${normalizedRoot}${path12.sep}`);
|
|
5177
5634
|
}
|
|
5178
5635
|
var rankingQueryTokenCache = /* @__PURE__ */ new Map();
|
|
5179
5636
|
var rankingNameTokenCache = /* @__PURE__ */ new Map();
|
|
@@ -6239,26 +6696,133 @@ var Indexer = class {
|
|
|
6239
6696
|
queryCacheTtlMs = 5 * 60 * 1e3;
|
|
6240
6697
|
querySimilarityThreshold = 0.85;
|
|
6241
6698
|
indexCompatibility = null;
|
|
6242
|
-
|
|
6699
|
+
activeIndexLease = null;
|
|
6700
|
+
initializationPromise = null;
|
|
6701
|
+
initializationMode = "none";
|
|
6702
|
+
readIssues = [];
|
|
6703
|
+
retiredDatabases = [];
|
|
6704
|
+
readerArtifactFingerprint = null;
|
|
6705
|
+
writerArtifactFingerprint = null;
|
|
6706
|
+
readerArtifactRetryAfter = /* @__PURE__ */ new Map();
|
|
6243
6707
|
constructor(projectRoot, config, host = "opencode") {
|
|
6244
6708
|
this.projectRoot = projectRoot;
|
|
6245
6709
|
this.config = config;
|
|
6246
6710
|
this.host = host;
|
|
6247
6711
|
this.indexPath = this.getIndexPath();
|
|
6248
|
-
this.fileHashCachePath =
|
|
6249
|
-
this.failedBatchesPath =
|
|
6250
|
-
this.indexingLockPath = path11.join(this.indexPath, "indexing.lock");
|
|
6712
|
+
this.fileHashCachePath = path12.join(this.indexPath, "file-hashes.json");
|
|
6713
|
+
this.failedBatchesPath = path12.join(this.indexPath, "failed-batches.json");
|
|
6251
6714
|
this.logger = initializeLogger(config.debug);
|
|
6252
6715
|
}
|
|
6253
6716
|
getIndexPath() {
|
|
6254
6717
|
return resolveProjectIndexPath(this.projectRoot, this.config.scope, this.host);
|
|
6255
6718
|
}
|
|
6719
|
+
isLocalProjectIndexPath() {
|
|
6720
|
+
const localProjectIndexPaths = [path12.join(this.projectRoot, getHostProjectIndexRelativePath(this.host))];
|
|
6721
|
+
if (this.host !== "opencode") {
|
|
6722
|
+
localProjectIndexPaths.push(path12.join(this.projectRoot, getHostProjectIndexRelativePath("opencode")));
|
|
6723
|
+
}
|
|
6724
|
+
return localProjectIndexPaths.some((localPath) => {
|
|
6725
|
+
if (!existsSync7(localPath) || !existsSync7(this.indexPath)) {
|
|
6726
|
+
return path12.resolve(this.indexPath) === path12.resolve(localPath);
|
|
6727
|
+
}
|
|
6728
|
+
const indexStats = statSync3(this.indexPath);
|
|
6729
|
+
const localStats = statSync3(localPath);
|
|
6730
|
+
return indexStats.dev === localStats.dev && indexStats.ino === localStats.ino;
|
|
6731
|
+
});
|
|
6732
|
+
}
|
|
6733
|
+
resetLoadedIndexState(retireDatabase = false) {
|
|
6734
|
+
if (this.database) {
|
|
6735
|
+
if (retireDatabase) {
|
|
6736
|
+
this.retiredDatabases.push(this.database);
|
|
6737
|
+
} else {
|
|
6738
|
+
this.database.close();
|
|
6739
|
+
}
|
|
6740
|
+
}
|
|
6741
|
+
this.store = null;
|
|
6742
|
+
this.invertedIndex = null;
|
|
6743
|
+
this.database = null;
|
|
6744
|
+
this.provider = null;
|
|
6745
|
+
this.configuredProviderInfo = null;
|
|
6746
|
+
this.reranker = null;
|
|
6747
|
+
this.indexCompatibility = null;
|
|
6748
|
+
this.initializationMode = "none";
|
|
6749
|
+
this.readIssues = [];
|
|
6750
|
+
this.readerArtifactFingerprint = null;
|
|
6751
|
+
this.writerArtifactFingerprint = null;
|
|
6752
|
+
this.readerArtifactRetryAfter.clear();
|
|
6753
|
+
this.fileHashCache.clear();
|
|
6754
|
+
}
|
|
6755
|
+
refreshLoadedIndexState() {
|
|
6756
|
+
if (!this.store || !this.invertedIndex || !this.configuredProviderInfo) return;
|
|
6757
|
+
this.store.load();
|
|
6758
|
+
this.invertedIndex.load();
|
|
6759
|
+
this.fileHashCache.clear();
|
|
6760
|
+
this.loadFileHashCache();
|
|
6761
|
+
this.indexCompatibility = this.validateIndexCompatibility(this.configuredProviderInfo);
|
|
6762
|
+
this.readIssues = [];
|
|
6763
|
+
this.readerArtifactRetryAfter.clear();
|
|
6764
|
+
}
|
|
6765
|
+
async withIndexMutationLease(operation, callback) {
|
|
6766
|
+
const lease = acquireIndexLock(this.indexPath, operation);
|
|
6767
|
+
this.indexPath = lease.canonicalIndexPath;
|
|
6768
|
+
this.fileHashCachePath = path12.join(this.indexPath, "file-hashes.json");
|
|
6769
|
+
this.failedBatchesPath = path12.join(this.indexPath, "failed-batches.json");
|
|
6770
|
+
this.activeIndexLease = lease;
|
|
6771
|
+
let result;
|
|
6772
|
+
let callbackError;
|
|
6773
|
+
let callbackFailed = false;
|
|
6774
|
+
try {
|
|
6775
|
+
result = await callback(lease.recoveries.map(({ owner }) => owner));
|
|
6776
|
+
} catch (error) {
|
|
6777
|
+
callbackFailed = true;
|
|
6778
|
+
callbackError = error;
|
|
6779
|
+
}
|
|
6780
|
+
if (!callbackFailed) {
|
|
6781
|
+
try {
|
|
6782
|
+
completeLeaseRecovery(lease);
|
|
6783
|
+
this.writerArtifactFingerprint = this.captureReaderArtifactFingerprint();
|
|
6784
|
+
} catch (error) {
|
|
6785
|
+
callbackFailed = true;
|
|
6786
|
+
callbackError = error;
|
|
6787
|
+
}
|
|
6788
|
+
}
|
|
6789
|
+
let releaseError;
|
|
6790
|
+
try {
|
|
6791
|
+
if (!releaseIndexLock(lease)) {
|
|
6792
|
+
releaseError = new Error(`Lost ownership of index mutation lease ${lease.owner.token}`);
|
|
6793
|
+
this.writerArtifactFingerprint = null;
|
|
6794
|
+
if (this.activeIndexLease?.owner.token === lease.owner.token) {
|
|
6795
|
+
this.activeIndexLease = null;
|
|
6796
|
+
}
|
|
6797
|
+
} else if (this.activeIndexLease?.owner.token === lease.owner.token) {
|
|
6798
|
+
this.activeIndexLease = null;
|
|
6799
|
+
}
|
|
6800
|
+
} catch (error) {
|
|
6801
|
+
releaseError = error;
|
|
6802
|
+
this.writerArtifactFingerprint = null;
|
|
6803
|
+
if (!existsSync7(lease.lockPath) && this.activeIndexLease?.owner.token === lease.owner.token) {
|
|
6804
|
+
this.activeIndexLease = null;
|
|
6805
|
+
}
|
|
6806
|
+
}
|
|
6807
|
+
if (releaseError !== void 0) {
|
|
6808
|
+
if (callbackFailed) throw new AggregateError([callbackError, releaseError], "Index mutation and lease release both failed");
|
|
6809
|
+
throw releaseError;
|
|
6810
|
+
}
|
|
6811
|
+
if (callbackFailed) throw callbackError;
|
|
6812
|
+
return result;
|
|
6813
|
+
}
|
|
6814
|
+
requireActiveLease() {
|
|
6815
|
+
if (!this.activeIndexLease) {
|
|
6816
|
+
throw new Error("Index mutation attempted without an active interprocess lease");
|
|
6817
|
+
}
|
|
6818
|
+
return this.activeIndexLease;
|
|
6819
|
+
}
|
|
6256
6820
|
loadFileHashCache() {
|
|
6257
|
-
if (!
|
|
6821
|
+
if (!existsSync7(this.fileHashCachePath)) {
|
|
6258
6822
|
return;
|
|
6259
6823
|
}
|
|
6260
6824
|
try {
|
|
6261
|
-
const data =
|
|
6825
|
+
const data = readFileSync7(this.fileHashCachePath, "utf-8");
|
|
6262
6826
|
const parsed = JSON.parse(data);
|
|
6263
6827
|
this.fileHashCache = new Map(Object.entries(parsed));
|
|
6264
6828
|
} catch (error) {
|
|
@@ -6278,15 +6842,26 @@ var Indexer = class {
|
|
|
6278
6842
|
this.atomicWriteSync(this.fileHashCachePath, JSON.stringify(obj));
|
|
6279
6843
|
}
|
|
6280
6844
|
atomicWriteSync(targetPath, data) {
|
|
6281
|
-
const
|
|
6282
|
-
|
|
6283
|
-
|
|
6284
|
-
|
|
6845
|
+
const lease = this.requireActiveLease();
|
|
6846
|
+
const tempPath = createLeaseTemporaryPath(targetPath, lease.owner, "tmp");
|
|
6847
|
+
mkdirSync3(path12.dirname(targetPath), { recursive: true });
|
|
6848
|
+
try {
|
|
6849
|
+
writeFileSync3(tempPath, data);
|
|
6850
|
+
renameSync2(tempPath, targetPath);
|
|
6851
|
+
} finally {
|
|
6852
|
+
removeLeaseTemporaryPath(tempPath);
|
|
6853
|
+
}
|
|
6854
|
+
}
|
|
6855
|
+
saveInvertedIndex(invertedIndex) {
|
|
6856
|
+
this.atomicWriteSync(
|
|
6857
|
+
path12.join(this.indexPath, "inverted-index.json"),
|
|
6858
|
+
invertedIndex.serialize()
|
|
6859
|
+
);
|
|
6285
6860
|
}
|
|
6286
6861
|
getScopedRoots() {
|
|
6287
|
-
const roots = /* @__PURE__ */ new Set([
|
|
6862
|
+
const roots = /* @__PURE__ */ new Set([path12.resolve(this.projectRoot)]);
|
|
6288
6863
|
for (const kbRoot of this.config.knowledgeBases) {
|
|
6289
|
-
roots.add(
|
|
6864
|
+
roots.add(path12.resolve(this.projectRoot, kbRoot));
|
|
6290
6865
|
}
|
|
6291
6866
|
return Array.from(roots);
|
|
6292
6867
|
}
|
|
@@ -6295,29 +6870,29 @@ var Indexer = class {
|
|
|
6295
6870
|
if (this.config.scope !== "global") {
|
|
6296
6871
|
return branchName;
|
|
6297
6872
|
}
|
|
6298
|
-
const projectHash = hashContent(
|
|
6873
|
+
const projectHash = hashContent(path12.resolve(this.projectRoot)).slice(0, 16);
|
|
6299
6874
|
return `${projectHash}:${branchName}`;
|
|
6300
6875
|
}
|
|
6301
6876
|
getBranchCatalogKeyFor(branchName) {
|
|
6302
6877
|
if (this.config.scope !== "global") {
|
|
6303
6878
|
return branchName;
|
|
6304
6879
|
}
|
|
6305
|
-
const projectHash = hashContent(
|
|
6880
|
+
const projectHash = hashContent(path12.resolve(this.projectRoot)).slice(0, 16);
|
|
6306
6881
|
return `${projectHash}:${branchName}`;
|
|
6307
6882
|
}
|
|
6308
6883
|
getLegacyBranchCatalogKey() {
|
|
6309
6884
|
return this.currentBranch || "default";
|
|
6310
6885
|
}
|
|
6311
6886
|
getLegacyMigrationMetadataKey() {
|
|
6312
|
-
const projectHash = hashContent(
|
|
6887
|
+
const projectHash = hashContent(path12.resolve(this.projectRoot)).slice(0, 16);
|
|
6313
6888
|
return `index.globalBranchMigration.${projectHash}`;
|
|
6314
6889
|
}
|
|
6315
6890
|
getProjectEmbeddingStrategyMetadataKey() {
|
|
6316
|
-
const projectHash = hashContent(
|
|
6891
|
+
const projectHash = hashContent(path12.resolve(this.projectRoot)).slice(0, 16);
|
|
6317
6892
|
return `index.embeddingStrategyVersion.${projectHash}`;
|
|
6318
6893
|
}
|
|
6319
6894
|
getProjectForceReembedMetadataKey() {
|
|
6320
|
-
const projectHash = hashContent(
|
|
6895
|
+
const projectHash = hashContent(path12.resolve(this.projectRoot)).slice(0, 16);
|
|
6321
6896
|
return `index.forceReembed.${projectHash}`;
|
|
6322
6897
|
}
|
|
6323
6898
|
hasProjectForceReembedPending() {
|
|
@@ -6411,7 +6986,7 @@ var Indexer = class {
|
|
|
6411
6986
|
if (!this.database) {
|
|
6412
6987
|
return { chunkIds, symbolIds };
|
|
6413
6988
|
}
|
|
6414
|
-
const projectRootPath =
|
|
6989
|
+
const projectRootPath = path12.resolve(this.projectRoot);
|
|
6415
6990
|
const projectLocalFilePaths = /* @__PURE__ */ new Set([
|
|
6416
6991
|
...Array.from(this.fileHashCache.keys()).filter(
|
|
6417
6992
|
(filePath) => this.isFileInCurrentScope(filePath, roots) && isPathWithinRoot(filePath, projectRootPath)
|
|
@@ -6434,7 +7009,7 @@ var Indexer = class {
|
|
|
6434
7009
|
if (this.config.scope !== "global") {
|
|
6435
7010
|
return this.getBranchCatalogCleanupKeys();
|
|
6436
7011
|
}
|
|
6437
|
-
const projectHash = hashContent(
|
|
7012
|
+
const projectHash = hashContent(path12.resolve(this.projectRoot)).slice(0, 16);
|
|
6438
7013
|
const keys = /* @__PURE__ */ new Set();
|
|
6439
7014
|
const projectChunkIdSet = new Set(projectChunkIds);
|
|
6440
7015
|
const projectSymbolIdSet = new Set(projectSymbolIds);
|
|
@@ -6518,7 +7093,7 @@ var Indexer = class {
|
|
|
6518
7093
|
if (!this.database || this.config.scope !== "global") {
|
|
6519
7094
|
return false;
|
|
6520
7095
|
}
|
|
6521
|
-
const projectHash = hashContent(
|
|
7096
|
+
const projectHash = hashContent(path12.resolve(this.projectRoot)).slice(0, 16);
|
|
6522
7097
|
const roots = this.getScopedRoots();
|
|
6523
7098
|
const { chunkIds: projectLocalChunkIds, symbolIds: projectLocalSymbolIds } = this.getProjectLocalScopedOwnershipIds(roots);
|
|
6524
7099
|
return this.database.getAllBranches().some(
|
|
@@ -6552,7 +7127,7 @@ var Indexer = class {
|
|
|
6552
7127
|
...Array.from(this.fileHashCache.keys()).filter((filePath) => this.isFileInCurrentScope(filePath, roots)),
|
|
6553
7128
|
...scopedEntries.map(({ metadata }) => metadata.filePath)
|
|
6554
7129
|
]);
|
|
6555
|
-
const projectRootPath =
|
|
7130
|
+
const projectRootPath = path12.resolve(this.projectRoot);
|
|
6556
7131
|
const projectLocalFilePaths = new Set(
|
|
6557
7132
|
Array.from(filePaths).filter((filePath) => isPathWithinRoot(filePath, projectRootPath))
|
|
6558
7133
|
);
|
|
@@ -6614,34 +7189,27 @@ var Indexer = class {
|
|
|
6614
7189
|
database.gcOrphanEmbeddings();
|
|
6615
7190
|
database.gcOrphanChunks();
|
|
6616
7191
|
store.save();
|
|
6617
|
-
|
|
7192
|
+
this.saveInvertedIndex(invertedIndex);
|
|
6618
7193
|
return {
|
|
6619
7194
|
removedChunkIds: removedChunkIdList,
|
|
6620
7195
|
hasForeignData: allMetadata.some(({ metadata }) => !this.isFileInCurrentScope(metadata.filePath, roots))
|
|
6621
7196
|
};
|
|
6622
7197
|
}
|
|
6623
|
-
|
|
6624
|
-
|
|
6625
|
-
|
|
6626
|
-
|
|
6627
|
-
|
|
6628
|
-
|
|
6629
|
-
|
|
6630
|
-
|
|
6631
|
-
writeFileSync2(this.indexingLockPath, JSON.stringify(lockData));
|
|
6632
|
-
}
|
|
6633
|
-
releaseIndexingLock() {
|
|
6634
|
-
if (existsSync6(this.indexingLockPath)) {
|
|
6635
|
-
unlinkSync(this.indexingLockPath);
|
|
7198
|
+
async recoverFromInterruptedIndexingUnlocked(owners) {
|
|
7199
|
+
for (const owner of owners) {
|
|
7200
|
+
this.logger.warn("Detected interrupted indexing session, recovering...", {
|
|
7201
|
+
pid: owner.pid,
|
|
7202
|
+
hostname: owner.hostname,
|
|
7203
|
+
operation: owner.operation,
|
|
7204
|
+
startedAt: owner.startedAt
|
|
7205
|
+
});
|
|
6636
7206
|
}
|
|
6637
|
-
|
|
6638
|
-
|
|
6639
|
-
|
|
6640
|
-
|
|
6641
|
-
|
|
7207
|
+
if (this.config.scope === "global") {
|
|
7208
|
+
if (existsSync7(this.fileHashCachePath)) {
|
|
7209
|
+
unlinkSync(this.fileHashCachePath);
|
|
7210
|
+
}
|
|
7211
|
+
await this.healthCheckUnlocked();
|
|
6642
7212
|
}
|
|
6643
|
-
await this.healthCheck();
|
|
6644
|
-
this.releaseIndexingLock();
|
|
6645
7213
|
this.logger.info("Recovery complete, next index will re-process all files");
|
|
6646
7214
|
}
|
|
6647
7215
|
loadFailedBatches(maxChunkTokens) {
|
|
@@ -6657,10 +7225,10 @@ var Indexer = class {
|
|
|
6657
7225
|
}
|
|
6658
7226
|
}
|
|
6659
7227
|
loadSerializedFailedBatches() {
|
|
6660
|
-
if (!
|
|
7228
|
+
if (!existsSync7(this.failedBatchesPath)) {
|
|
6661
7229
|
return [];
|
|
6662
7230
|
}
|
|
6663
|
-
const data =
|
|
7231
|
+
const data = readFileSync7(this.failedBatchesPath, "utf-8");
|
|
6664
7232
|
const parsed = JSON.parse(data);
|
|
6665
7233
|
return parsed.map((batch) => {
|
|
6666
7234
|
const chunks = Array.isArray(batch.chunks) ? batch.chunks : [];
|
|
@@ -6677,7 +7245,7 @@ var Indexer = class {
|
|
|
6677
7245
|
}
|
|
6678
7246
|
saveFailedBatches(batches) {
|
|
6679
7247
|
if (batches.length === 0) {
|
|
6680
|
-
if (
|
|
7248
|
+
if (existsSync7(this.failedBatchesPath)) {
|
|
6681
7249
|
try {
|
|
6682
7250
|
unlinkSync(this.failedBatchesPath);
|
|
6683
7251
|
} catch {
|
|
@@ -6685,7 +7253,7 @@ var Indexer = class {
|
|
|
6685
7253
|
}
|
|
6686
7254
|
return;
|
|
6687
7255
|
}
|
|
6688
|
-
|
|
7256
|
+
this.atomicWriteSync(this.failedBatchesPath, JSON.stringify(batches, null, 2));
|
|
6689
7257
|
}
|
|
6690
7258
|
collectRetryableFailedChunks(currentFileHashes, unchangedFilePaths, maxChunkTokens) {
|
|
6691
7259
|
const retryableById = /* @__PURE__ */ new Map();
|
|
@@ -6879,6 +7447,222 @@ var Indexer = class {
|
|
|
6879
7447
|
return parts.join("\n");
|
|
6880
7448
|
}
|
|
6881
7449
|
async initialize() {
|
|
7450
|
+
if (this.initializationPromise) {
|
|
7451
|
+
await this.initializationPromise;
|
|
7452
|
+
}
|
|
7453
|
+
if (this.isInitializedFor("reader")) {
|
|
7454
|
+
return;
|
|
7455
|
+
}
|
|
7456
|
+
await this.initializeOnce("reader", [], { skipAutoGc: true });
|
|
7457
|
+
}
|
|
7458
|
+
async initializeOnce(mode, recoveredOwners, options) {
|
|
7459
|
+
if (this.initializationPromise) {
|
|
7460
|
+
await this.initializationPromise;
|
|
7461
|
+
if (this.isInitializedFor(mode)) {
|
|
7462
|
+
return;
|
|
7463
|
+
}
|
|
7464
|
+
return this.initializeOnce(mode, recoveredOwners, options);
|
|
7465
|
+
}
|
|
7466
|
+
if (this.isInitializedFor(mode)) {
|
|
7467
|
+
return;
|
|
7468
|
+
}
|
|
7469
|
+
const initialization = this.initializeUnlocked(mode, recoveredOwners, options).catch((error) => {
|
|
7470
|
+
this.resetLoadedIndexState();
|
|
7471
|
+
throw error;
|
|
7472
|
+
}).finally(() => {
|
|
7473
|
+
if (this.initializationPromise === initialization) {
|
|
7474
|
+
this.initializationPromise = null;
|
|
7475
|
+
}
|
|
7476
|
+
});
|
|
7477
|
+
this.initializationPromise = initialization;
|
|
7478
|
+
await initialization;
|
|
7479
|
+
}
|
|
7480
|
+
isInitializedFor(mode) {
|
|
7481
|
+
const hasState = Boolean(
|
|
7482
|
+
this.store && this.provider && this.invertedIndex && this.configuredProviderInfo && this.database
|
|
7483
|
+
);
|
|
7484
|
+
if (!hasState) {
|
|
7485
|
+
return false;
|
|
7486
|
+
}
|
|
7487
|
+
return mode === "reader" ? this.initializationMode !== "none" : this.initializationMode === "writer";
|
|
7488
|
+
}
|
|
7489
|
+
recordReadIssue(component, message, error) {
|
|
7490
|
+
this.readIssues.push(this.createReadIssue(component, message));
|
|
7491
|
+
this.readerArtifactRetryAfter.set(component, Date.now() + READER_ARTIFACT_RETRY_INTERVAL_MS);
|
|
7492
|
+
this.logger.warn(message, error === void 0 ? void 0 : { error: getErrorMessage2(error) });
|
|
7493
|
+
}
|
|
7494
|
+
createReadIssue(component, message) {
|
|
7495
|
+
return {
|
|
7496
|
+
component,
|
|
7497
|
+
message,
|
|
7498
|
+
blocking: component !== "keyword"
|
|
7499
|
+
};
|
|
7500
|
+
}
|
|
7501
|
+
getVectorReadIssueMessage() {
|
|
7502
|
+
if (this.config.scope === "global") {
|
|
7503
|
+
return "Shared vector index could not be read. Restore or repair the complete fingerprinted shared vector artifacts; automatic reset is disabled for global scope.";
|
|
7504
|
+
}
|
|
7505
|
+
if (!this.isLocalProjectIndexPath()) {
|
|
7506
|
+
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.";
|
|
7507
|
+
}
|
|
7508
|
+
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.";
|
|
7509
|
+
}
|
|
7510
|
+
getKeywordReadIssueMessage() {
|
|
7511
|
+
if (this.config.scope === "global") {
|
|
7512
|
+
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.";
|
|
7513
|
+
}
|
|
7514
|
+
if (!this.isLocalProjectIndexPath()) {
|
|
7515
|
+
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.";
|
|
7516
|
+
}
|
|
7517
|
+
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.";
|
|
7518
|
+
}
|
|
7519
|
+
getDatabaseReadIssueMessage() {
|
|
7520
|
+
if (this.config.scope === "global") {
|
|
7521
|
+
return "Shared index database could not be read. Restore or repair the shared SQLite database; automatic reset is disabled for global scope.";
|
|
7522
|
+
}
|
|
7523
|
+
if (!this.isLocalProjectIndexPath()) {
|
|
7524
|
+
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.";
|
|
7525
|
+
}
|
|
7526
|
+
return "Index database could not be read. Run index_codebase after the active writer finishes to repair or migrate it under the writer lease.";
|
|
7527
|
+
}
|
|
7528
|
+
getReaderFileFingerprint(filePath, identityOnly = false) {
|
|
7529
|
+
try {
|
|
7530
|
+
const stats = statSync3(filePath);
|
|
7531
|
+
if (identityOnly) {
|
|
7532
|
+
return `${stats.dev}:${stats.ino}`;
|
|
7533
|
+
}
|
|
7534
|
+
return `${stats.dev}:${stats.ino}:${stats.size}:${stats.mtimeMs}:${stats.ctimeMs}`;
|
|
7535
|
+
} catch (error) {
|
|
7536
|
+
return `unavailable:${getErrorMessage2(error)}`;
|
|
7537
|
+
}
|
|
7538
|
+
}
|
|
7539
|
+
captureReaderArtifactFingerprint() {
|
|
7540
|
+
const storePath = path12.join(this.indexPath, "vectors");
|
|
7541
|
+
return {
|
|
7542
|
+
vectors: `${this.getReaderFileFingerprint(storePath)}|${this.getReaderFileFingerprint(`${storePath}.meta.json`)}`,
|
|
7543
|
+
keyword: this.getReaderFileFingerprint(path12.join(this.indexPath, "inverted-index.json")),
|
|
7544
|
+
database: this.getReaderFileFingerprint(path12.join(this.indexPath, "codebase.db")),
|
|
7545
|
+
databaseIdentity: this.getReaderFileFingerprint(path12.join(this.indexPath, "codebase.db"), true)
|
|
7546
|
+
};
|
|
7547
|
+
}
|
|
7548
|
+
refreshReaderArtifacts() {
|
|
7549
|
+
if (this.initializationMode !== "reader" || !this.configuredProviderInfo) {
|
|
7550
|
+
return;
|
|
7551
|
+
}
|
|
7552
|
+
const previousFingerprint = this.readerArtifactFingerprint;
|
|
7553
|
+
const currentFingerprint = this.captureReaderArtifactFingerprint();
|
|
7554
|
+
const issues = new Map(this.readIssues.map((issue) => [issue.component, issue]));
|
|
7555
|
+
const retryDue = (component) => issues.has(component) && Date.now() >= (this.readerArtifactRetryAfter.get(component) ?? 0);
|
|
7556
|
+
const vectorsChanged = !previousFingerprint || currentFingerprint.vectors !== previousFingerprint.vectors;
|
|
7557
|
+
const keywordChanged = !previousFingerprint || currentFingerprint.keyword !== previousFingerprint.keyword;
|
|
7558
|
+
const databaseChanged = !previousFingerprint || currentFingerprint.database !== previousFingerprint.database;
|
|
7559
|
+
const databaseReplaced = !previousFingerprint || currentFingerprint.databaseIdentity !== previousFingerprint.databaseIdentity;
|
|
7560
|
+
if (previousFingerprint && !vectorsChanged && !keywordChanged && !databaseChanged && !Array.from(issues.keys()).some(retryDue)) {
|
|
7561
|
+
return;
|
|
7562
|
+
}
|
|
7563
|
+
const setIssue = (component, message, error) => {
|
|
7564
|
+
if (!issues.has(component)) {
|
|
7565
|
+
this.logger.warn(message, error === void 0 ? void 0 : { error: getErrorMessage2(error) });
|
|
7566
|
+
}
|
|
7567
|
+
issues.set(component, this.createReadIssue(component, message));
|
|
7568
|
+
this.readerArtifactRetryAfter.set(component, Date.now() + READER_ARTIFACT_RETRY_INTERVAL_MS);
|
|
7569
|
+
};
|
|
7570
|
+
const storePath = path12.join(this.indexPath, "vectors");
|
|
7571
|
+
const vectorMetadataPath = `${storePath}.meta.json`;
|
|
7572
|
+
const invertedIndexPath = path12.join(this.indexPath, "inverted-index.json");
|
|
7573
|
+
const dbPath = path12.join(this.indexPath, "codebase.db");
|
|
7574
|
+
if (vectorsChanged || retryDue("vectors")) {
|
|
7575
|
+
const vectorStoreExists = existsSync7(storePath);
|
|
7576
|
+
const vectorMetadataExists = existsSync7(vectorMetadataPath);
|
|
7577
|
+
if (vectorStoreExists && vectorMetadataExists) {
|
|
7578
|
+
try {
|
|
7579
|
+
const store = new VectorStore(storePath, this.configuredProviderInfo.modelInfo.dimensions);
|
|
7580
|
+
store.loadStrict();
|
|
7581
|
+
this.store = store;
|
|
7582
|
+
issues.delete("vectors");
|
|
7583
|
+
this.readerArtifactRetryAfter.delete("vectors");
|
|
7584
|
+
} catch (error) {
|
|
7585
|
+
setIssue("vectors", this.getVectorReadIssueMessage(), error);
|
|
7586
|
+
}
|
|
7587
|
+
} else if (vectorStoreExists !== vectorMetadataExists || issues.has("vectors")) {
|
|
7588
|
+
setIssue("vectors", this.getVectorReadIssueMessage());
|
|
7589
|
+
}
|
|
7590
|
+
}
|
|
7591
|
+
if (keywordChanged || retryDue("keyword") || !existsSync7(invertedIndexPath) && (this.store?.count() ?? 0) > 0) {
|
|
7592
|
+
if (existsSync7(invertedIndexPath)) {
|
|
7593
|
+
try {
|
|
7594
|
+
const invertedIndex = new InvertedIndex(invertedIndexPath);
|
|
7595
|
+
invertedIndex.load();
|
|
7596
|
+
this.invertedIndex = invertedIndex;
|
|
7597
|
+
issues.delete("keyword");
|
|
7598
|
+
this.readerArtifactRetryAfter.delete("keyword");
|
|
7599
|
+
} catch (error) {
|
|
7600
|
+
setIssue("keyword", this.getKeywordReadIssueMessage(), error);
|
|
7601
|
+
}
|
|
7602
|
+
} else if ((this.store?.count() ?? 0) > 0 || issues.has("keyword")) {
|
|
7603
|
+
setIssue("keyword", this.getKeywordReadIssueMessage());
|
|
7604
|
+
}
|
|
7605
|
+
}
|
|
7606
|
+
if (databaseReplaced || databaseChanged && issues.has("database") || retryDue("database")) {
|
|
7607
|
+
if (existsSync7(dbPath)) {
|
|
7608
|
+
try {
|
|
7609
|
+
const database = Database.openReadOnly(dbPath);
|
|
7610
|
+
if (this.database) {
|
|
7611
|
+
this.retiredDatabases.push(this.database);
|
|
7612
|
+
}
|
|
7613
|
+
this.database = database;
|
|
7614
|
+
issues.delete("database");
|
|
7615
|
+
this.readerArtifactRetryAfter.delete("database");
|
|
7616
|
+
} catch (error) {
|
|
7617
|
+
setIssue("database", this.getDatabaseReadIssueMessage(), error);
|
|
7618
|
+
}
|
|
7619
|
+
} else if ((this.store?.count() ?? 0) > 0 || issues.has("database")) {
|
|
7620
|
+
setIssue("database", this.getDatabaseReadIssueMessage());
|
|
7621
|
+
}
|
|
7622
|
+
}
|
|
7623
|
+
if (!issues.has("database")) {
|
|
7624
|
+
try {
|
|
7625
|
+
this.indexCompatibility = this.validateIndexCompatibility(this.configuredProviderInfo);
|
|
7626
|
+
} catch (error) {
|
|
7627
|
+
setIssue("database", this.getDatabaseReadIssueMessage(), error);
|
|
7628
|
+
}
|
|
7629
|
+
}
|
|
7630
|
+
this.readIssues = Array.from(issues.values());
|
|
7631
|
+
this.readerArtifactFingerprint = currentFingerprint;
|
|
7632
|
+
}
|
|
7633
|
+
refreshInactiveWriterArtifacts() {
|
|
7634
|
+
if (this.initializationMode !== "writer" || this.activeIndexLease) {
|
|
7635
|
+
return true;
|
|
7636
|
+
}
|
|
7637
|
+
const previousFingerprint = this.writerArtifactFingerprint;
|
|
7638
|
+
const currentFingerprint = this.captureReaderArtifactFingerprint();
|
|
7639
|
+
const retryDue = this.readIssues.some(
|
|
7640
|
+
(issue) => Date.now() >= (this.readerArtifactRetryAfter.get(issue.component) ?? 0)
|
|
7641
|
+
);
|
|
7642
|
+
const artifactsChanged = !previousFingerprint || currentFingerprint.vectors !== previousFingerprint.vectors || currentFingerprint.keyword !== previousFingerprint.keyword || currentFingerprint.database !== previousFingerprint.database || currentFingerprint.databaseIdentity !== previousFingerprint.databaseIdentity;
|
|
7643
|
+
if (!artifactsChanged && !retryDue) {
|
|
7644
|
+
return true;
|
|
7645
|
+
}
|
|
7646
|
+
if (!previousFingerprint || currentFingerprint.databaseIdentity !== previousFingerprint.databaseIdentity) {
|
|
7647
|
+
return false;
|
|
7648
|
+
}
|
|
7649
|
+
this.initializationMode = "reader";
|
|
7650
|
+
this.readerArtifactFingerprint = previousFingerprint;
|
|
7651
|
+
try {
|
|
7652
|
+
this.refreshReaderArtifacts();
|
|
7653
|
+
this.writerArtifactFingerprint = this.readerArtifactFingerprint ?? currentFingerprint;
|
|
7654
|
+
} finally {
|
|
7655
|
+
this.readerArtifactFingerprint = null;
|
|
7656
|
+
this.initializationMode = "writer";
|
|
7657
|
+
}
|
|
7658
|
+
return true;
|
|
7659
|
+
}
|
|
7660
|
+
async initializeUnlocked(mode, recoveredOwners = [], options = {}) {
|
|
7661
|
+
if (mode === "writer") {
|
|
7662
|
+
this.requireActiveLease();
|
|
7663
|
+
}
|
|
7664
|
+
this.readIssues = [];
|
|
7665
|
+
this.readerArtifactRetryAfter.clear();
|
|
6882
7666
|
if (this.config.embeddingProvider === "custom") {
|
|
6883
7667
|
if (!this.config.customProvider) {
|
|
6884
7668
|
throw new Error("embeddingProvider is 'custom' but customProvider config is missing.");
|
|
@@ -6910,36 +7694,103 @@ var Indexer = class {
|
|
|
6910
7694
|
});
|
|
6911
7695
|
}
|
|
6912
7696
|
}
|
|
6913
|
-
await fsPromises2.mkdir(this.indexPath, { recursive: true });
|
|
6914
7697
|
const dimensions = this.configuredProviderInfo.modelInfo.dimensions;
|
|
6915
|
-
const storePath =
|
|
6916
|
-
|
|
6917
|
-
const
|
|
6918
|
-
|
|
6919
|
-
|
|
6920
|
-
|
|
6921
|
-
|
|
6922
|
-
|
|
6923
|
-
|
|
6924
|
-
|
|
6925
|
-
|
|
6926
|
-
|
|
6927
|
-
|
|
7698
|
+
const storePath = path12.join(this.indexPath, "vectors");
|
|
7699
|
+
const vectorMetadataPath = `${storePath}.meta.json`;
|
|
7700
|
+
const invertedIndexPath = path12.join(this.indexPath, "inverted-index.json");
|
|
7701
|
+
const dbPath = path12.join(this.indexPath, "codebase.db");
|
|
7702
|
+
let dbIsNew = !existsSync7(dbPath);
|
|
7703
|
+
const readerArtifactFingerprint = mode === "reader" ? this.captureReaderArtifactFingerprint() : null;
|
|
7704
|
+
if (mode === "writer") {
|
|
7705
|
+
await fsPromises2.mkdir(this.indexPath, { recursive: true });
|
|
7706
|
+
if (recoveredOwners.length > 0 && this.config.scope === "project" && !this.isLocalProjectIndexPath()) {
|
|
7707
|
+
throw new Error(
|
|
7708
|
+
"Interrupted indexing recovery is unsafe while using an inherited worktree index. Run index_codebase with force=true to create a local project index boundary."
|
|
7709
|
+
);
|
|
7710
|
+
}
|
|
7711
|
+
for (const recoveredOwner of recoveredOwners) {
|
|
7712
|
+
recoverLeaseArtifacts(this.indexPath, recoveredOwner, [
|
|
7713
|
+
storePath,
|
|
7714
|
+
`${storePath}.meta.json`
|
|
7715
|
+
]);
|
|
7716
|
+
}
|
|
7717
|
+
if (recoveredOwners.length > 0 && this.config.scope === "project") {
|
|
7718
|
+
await this.resetLocalIndexArtifacts();
|
|
7719
|
+
}
|
|
7720
|
+
this.store = new VectorStore(storePath, dimensions);
|
|
7721
|
+
if (existsSync7(storePath) || existsSync7(vectorMetadataPath)) {
|
|
7722
|
+
this.store.load();
|
|
6928
7723
|
}
|
|
6929
7724
|
this.invertedIndex = new InvertedIndex(invertedIndexPath);
|
|
6930
|
-
|
|
6931
|
-
|
|
6932
|
-
|
|
6933
|
-
|
|
6934
|
-
|
|
6935
|
-
|
|
6936
|
-
|
|
6937
|
-
|
|
7725
|
+
try {
|
|
7726
|
+
this.invertedIndex.load();
|
|
7727
|
+
} catch {
|
|
7728
|
+
if (existsSync7(invertedIndexPath)) {
|
|
7729
|
+
await fsPromises2.unlink(invertedIndexPath);
|
|
7730
|
+
}
|
|
7731
|
+
this.invertedIndex = new InvertedIndex(invertedIndexPath);
|
|
7732
|
+
}
|
|
7733
|
+
try {
|
|
7734
|
+
this.database = new Database(dbPath);
|
|
7735
|
+
} catch (error) {
|
|
7736
|
+
if (!await this.tryResetCorruptedIndex("initializing index database", error)) {
|
|
7737
|
+
throw error;
|
|
7738
|
+
}
|
|
7739
|
+
this.store = new VectorStore(storePath, dimensions);
|
|
7740
|
+
this.invertedIndex = new InvertedIndex(invertedIndexPath);
|
|
7741
|
+
this.database = new Database(dbPath);
|
|
7742
|
+
dbIsNew = true;
|
|
6938
7743
|
}
|
|
7744
|
+
} else {
|
|
6939
7745
|
this.store = new VectorStore(storePath, dimensions);
|
|
7746
|
+
const vectorStoreExists = existsSync7(storePath);
|
|
7747
|
+
const vectorMetadataExists = existsSync7(vectorMetadataPath);
|
|
7748
|
+
const vectorReadFailureMessage = this.getVectorReadIssueMessage();
|
|
7749
|
+
if (vectorStoreExists !== vectorMetadataExists) {
|
|
7750
|
+
this.recordReadIssue("vectors", vectorReadFailureMessage);
|
|
7751
|
+
} else if (vectorStoreExists) {
|
|
7752
|
+
try {
|
|
7753
|
+
this.store.loadStrict();
|
|
7754
|
+
} catch (error) {
|
|
7755
|
+
this.recordReadIssue("vectors", vectorReadFailureMessage, error);
|
|
7756
|
+
this.store = new VectorStore(storePath, dimensions);
|
|
7757
|
+
}
|
|
7758
|
+
}
|
|
6940
7759
|
this.invertedIndex = new InvertedIndex(invertedIndexPath);
|
|
6941
|
-
|
|
6942
|
-
|
|
7760
|
+
if (existsSync7(invertedIndexPath)) {
|
|
7761
|
+
try {
|
|
7762
|
+
this.invertedIndex.load();
|
|
7763
|
+
} catch (error) {
|
|
7764
|
+
this.recordReadIssue(
|
|
7765
|
+
"keyword",
|
|
7766
|
+
this.getKeywordReadIssueMessage(),
|
|
7767
|
+
error
|
|
7768
|
+
);
|
|
7769
|
+
this.invertedIndex = new InvertedIndex(invertedIndexPath);
|
|
7770
|
+
}
|
|
7771
|
+
} else if (this.store.count() > 0) {
|
|
7772
|
+
this.recordReadIssue("keyword", this.getKeywordReadIssueMessage());
|
|
7773
|
+
}
|
|
7774
|
+
if (existsSync7(dbPath)) {
|
|
7775
|
+
try {
|
|
7776
|
+
this.database = Database.openReadOnly(dbPath);
|
|
7777
|
+
} catch (error) {
|
|
7778
|
+
this.recordReadIssue(
|
|
7779
|
+
"database",
|
|
7780
|
+
this.getDatabaseReadIssueMessage(),
|
|
7781
|
+
error
|
|
7782
|
+
);
|
|
7783
|
+
this.database = Database.createEmptyReadOnly();
|
|
7784
|
+
}
|
|
7785
|
+
} else {
|
|
7786
|
+
this.database = Database.createEmptyReadOnly();
|
|
7787
|
+
if (this.store.count() > 0) {
|
|
7788
|
+
this.recordReadIssue(
|
|
7789
|
+
"database",
|
|
7790
|
+
`Index database is missing for the published vectors. ${this.getDatabaseReadIssueMessage()}`
|
|
7791
|
+
);
|
|
7792
|
+
}
|
|
7793
|
+
}
|
|
6943
7794
|
}
|
|
6944
7795
|
if (isGitRepo(this.projectRoot)) {
|
|
6945
7796
|
this.currentBranch = getBranchOrDefault(this.projectRoot);
|
|
@@ -6953,10 +7804,10 @@ var Indexer = class {
|
|
|
6953
7804
|
this.baseBranch = "default";
|
|
6954
7805
|
this.logger.branch("debug", "Not a git repository, using default branch");
|
|
6955
7806
|
}
|
|
6956
|
-
if (
|
|
6957
|
-
await this.
|
|
7807
|
+
if (mode === "writer" && recoveredOwners.length > 0) {
|
|
7808
|
+
await this.recoverFromInterruptedIndexingUnlocked(recoveredOwners);
|
|
6958
7809
|
}
|
|
6959
|
-
if (dbIsNew && this.store.count() > 0) {
|
|
7810
|
+
if (mode === "writer" && dbIsNew && this.store.count() > 0) {
|
|
6960
7811
|
this.migrateFromLegacyIndex();
|
|
6961
7812
|
}
|
|
6962
7813
|
this.loadFileHashCache();
|
|
@@ -6968,9 +7819,11 @@ var Indexer = class {
|
|
|
6968
7819
|
configuredProviderInfo: this.configuredProviderInfo
|
|
6969
7820
|
});
|
|
6970
7821
|
}
|
|
6971
|
-
if (this.config.indexing.autoGc) {
|
|
7822
|
+
if (mode === "writer" && this.config.indexing.autoGc && !options.skipAutoGc) {
|
|
6972
7823
|
await this.maybeRunAutoGc();
|
|
6973
7824
|
}
|
|
7825
|
+
this.initializationMode = mode;
|
|
7826
|
+
this.readerArtifactFingerprint = readerArtifactFingerprint;
|
|
6974
7827
|
}
|
|
6975
7828
|
async maybeRunAutoGc() {
|
|
6976
7829
|
if (!this.database) return;
|
|
@@ -6987,7 +7840,7 @@ var Indexer = class {
|
|
|
6987
7840
|
}
|
|
6988
7841
|
}
|
|
6989
7842
|
if (shouldRunGc) {
|
|
6990
|
-
const result = await this.
|
|
7843
|
+
const result = await this.healthCheckUnlocked();
|
|
6991
7844
|
if (result.warning) {
|
|
6992
7845
|
this.database.setMetadata(STARTUP_WARNING_METADATA_KEY, result.warning);
|
|
6993
7846
|
} else {
|
|
@@ -7009,7 +7862,7 @@ var Indexer = class {
|
|
|
7009
7862
|
if (await this.tryResetCorruptedIndex("running automatic orphan garbage collection", error)) {
|
|
7010
7863
|
return {
|
|
7011
7864
|
resetCorruptedIndex: true,
|
|
7012
|
-
warning: this.getCorruptedIndexWarning(
|
|
7865
|
+
warning: this.getCorruptedIndexWarning(path12.join(this.indexPath, "codebase.db"))
|
|
7013
7866
|
};
|
|
7014
7867
|
}
|
|
7015
7868
|
throw error;
|
|
@@ -7024,28 +7877,29 @@ var Indexer = class {
|
|
|
7024
7877
|
return;
|
|
7025
7878
|
}
|
|
7026
7879
|
const retainedEntries = store.getAllMetadata().filter(({ key }) => !excludedSet.has(key));
|
|
7027
|
-
const storeBasePath =
|
|
7028
|
-
const storeIndexPath =
|
|
7880
|
+
const storeBasePath = path12.join(this.indexPath, "vectors");
|
|
7881
|
+
const storeIndexPath = storeBasePath;
|
|
7029
7882
|
const storeMetadataPath = `${storeBasePath}.meta.json`;
|
|
7030
|
-
const
|
|
7031
|
-
const
|
|
7883
|
+
const lease = this.requireActiveLease();
|
|
7884
|
+
const backupIndexPath = createLeaseTemporaryPath(storeIndexPath, lease.owner, "bak");
|
|
7885
|
+
const backupMetadataPath = createLeaseTemporaryPath(storeMetadataPath, lease.owner, "bak");
|
|
7032
7886
|
let backedUpIndex = false;
|
|
7033
7887
|
let backedUpMetadata = false;
|
|
7034
7888
|
let rebuiltCount = 0;
|
|
7035
7889
|
let skippedCount = 0;
|
|
7036
|
-
if (
|
|
7890
|
+
if (existsSync7(backupIndexPath)) {
|
|
7037
7891
|
unlinkSync(backupIndexPath);
|
|
7038
7892
|
}
|
|
7039
|
-
if (
|
|
7893
|
+
if (existsSync7(backupMetadataPath)) {
|
|
7040
7894
|
unlinkSync(backupMetadataPath);
|
|
7041
7895
|
}
|
|
7042
7896
|
try {
|
|
7043
|
-
if (
|
|
7044
|
-
|
|
7897
|
+
if (existsSync7(storeIndexPath)) {
|
|
7898
|
+
renameSync2(storeIndexPath, backupIndexPath);
|
|
7045
7899
|
backedUpIndex = true;
|
|
7046
7900
|
}
|
|
7047
|
-
if (
|
|
7048
|
-
|
|
7901
|
+
if (existsSync7(storeMetadataPath)) {
|
|
7902
|
+
renameSync2(storeMetadataPath, backupMetadataPath);
|
|
7049
7903
|
backedUpMetadata = true;
|
|
7050
7904
|
}
|
|
7051
7905
|
store.clear();
|
|
@@ -7065,10 +7919,10 @@ var Indexer = class {
|
|
|
7065
7919
|
rebuiltCount += 1;
|
|
7066
7920
|
}
|
|
7067
7921
|
store.save();
|
|
7068
|
-
if (backedUpIndex &&
|
|
7922
|
+
if (backedUpIndex && existsSync7(backupIndexPath)) {
|
|
7069
7923
|
unlinkSync(backupIndexPath);
|
|
7070
7924
|
}
|
|
7071
|
-
if (backedUpMetadata &&
|
|
7925
|
+
if (backedUpMetadata && existsSync7(backupMetadataPath)) {
|
|
7072
7926
|
unlinkSync(backupMetadataPath);
|
|
7073
7927
|
}
|
|
7074
7928
|
this.logger.gc("info", "Rebuilt vector store to avoid native remove", {
|
|
@@ -7081,17 +7935,17 @@ var Indexer = class {
|
|
|
7081
7935
|
store.clear();
|
|
7082
7936
|
} catch {
|
|
7083
7937
|
}
|
|
7084
|
-
if (
|
|
7938
|
+
if (existsSync7(storeIndexPath)) {
|
|
7085
7939
|
unlinkSync(storeIndexPath);
|
|
7086
7940
|
}
|
|
7087
|
-
if (
|
|
7941
|
+
if (existsSync7(storeMetadataPath)) {
|
|
7088
7942
|
unlinkSync(storeMetadataPath);
|
|
7089
7943
|
}
|
|
7090
|
-
if (backedUpIndex &&
|
|
7091
|
-
|
|
7944
|
+
if (backedUpIndex && existsSync7(backupIndexPath)) {
|
|
7945
|
+
renameSync2(backupIndexPath, storeIndexPath);
|
|
7092
7946
|
}
|
|
7093
|
-
if (backedUpMetadata &&
|
|
7094
|
-
|
|
7947
|
+
if (backedUpMetadata && existsSync7(backupMetadataPath)) {
|
|
7948
|
+
renameSync2(backupMetadataPath, storeMetadataPath);
|
|
7095
7949
|
}
|
|
7096
7950
|
if (backedUpIndex || backedUpMetadata) {
|
|
7097
7951
|
store.load();
|
|
@@ -7105,11 +7959,37 @@ var Indexer = class {
|
|
|
7105
7959
|
}
|
|
7106
7960
|
return `Detected a corrupted local SQLite index at ${dbPath} and reset the local index. Run index_codebase to rebuild search data.`;
|
|
7107
7961
|
}
|
|
7962
|
+
async resetLocalIndexArtifacts() {
|
|
7963
|
+
this.store = null;
|
|
7964
|
+
this.invertedIndex = null;
|
|
7965
|
+
this.database?.close();
|
|
7966
|
+
this.database = null;
|
|
7967
|
+
this.indexCompatibility = null;
|
|
7968
|
+
this.initializationMode = "none";
|
|
7969
|
+
this.readIssues = [];
|
|
7970
|
+
this.readerArtifactFingerprint = null;
|
|
7971
|
+
this.writerArtifactFingerprint = null;
|
|
7972
|
+
this.readerArtifactRetryAfter.clear();
|
|
7973
|
+
this.fileHashCache.clear();
|
|
7974
|
+
const resetPaths = [
|
|
7975
|
+
path12.join(this.indexPath, "codebase.db"),
|
|
7976
|
+
path12.join(this.indexPath, "codebase.db-shm"),
|
|
7977
|
+
path12.join(this.indexPath, "codebase.db-wal"),
|
|
7978
|
+
path12.join(this.indexPath, "vectors"),
|
|
7979
|
+
path12.join(this.indexPath, "vectors.usearch"),
|
|
7980
|
+
path12.join(this.indexPath, "vectors.meta.json"),
|
|
7981
|
+
path12.join(this.indexPath, "inverted-index.json"),
|
|
7982
|
+
path12.join(this.indexPath, "file-hashes.json"),
|
|
7983
|
+
path12.join(this.indexPath, "failed-batches.json")
|
|
7984
|
+
];
|
|
7985
|
+
await Promise.all(resetPaths.map((targetPath) => fsPromises2.rm(targetPath, { recursive: true, force: true })));
|
|
7986
|
+
await fsPromises2.mkdir(this.indexPath, { recursive: true });
|
|
7987
|
+
}
|
|
7108
7988
|
async tryResetCorruptedIndex(stage, error) {
|
|
7109
7989
|
if (!isSqliteCorruptionError(error)) {
|
|
7110
7990
|
return false;
|
|
7111
7991
|
}
|
|
7112
|
-
const dbPath =
|
|
7992
|
+
const dbPath = path12.join(this.indexPath, "codebase.db");
|
|
7113
7993
|
const warning = this.getCorruptedIndexWarning(dbPath);
|
|
7114
7994
|
const errorMessage = getErrorMessage2(error);
|
|
7115
7995
|
if (this.config.scope === "global") {
|
|
@@ -7125,30 +8005,7 @@ var Indexer = class {
|
|
|
7125
8005
|
dbPath,
|
|
7126
8006
|
error: errorMessage
|
|
7127
8007
|
});
|
|
7128
|
-
this.
|
|
7129
|
-
this.invertedIndex = null;
|
|
7130
|
-
this.database?.close();
|
|
7131
|
-
this.database = null;
|
|
7132
|
-
this.indexCompatibility = null;
|
|
7133
|
-
this.fileHashCache.clear();
|
|
7134
|
-
const resetPaths = [
|
|
7135
|
-
path11.join(this.indexPath, "codebase.db"),
|
|
7136
|
-
path11.join(this.indexPath, "codebase.db-shm"),
|
|
7137
|
-
path11.join(this.indexPath, "codebase.db-wal"),
|
|
7138
|
-
path11.join(this.indexPath, "vectors.usearch"),
|
|
7139
|
-
path11.join(this.indexPath, "inverted-index.json"),
|
|
7140
|
-
path11.join(this.indexPath, "file-hashes.json"),
|
|
7141
|
-
path11.join(this.indexPath, "failed-batches.json"),
|
|
7142
|
-
path11.join(this.indexPath, "indexing.lock"),
|
|
7143
|
-
path11.join(this.indexPath, "vectors")
|
|
7144
|
-
];
|
|
7145
|
-
await Promise.all(resetPaths.map(async (targetPath) => {
|
|
7146
|
-
try {
|
|
7147
|
-
await fsPromises2.rm(targetPath, { recursive: true, force: true });
|
|
7148
|
-
} catch {
|
|
7149
|
-
}
|
|
7150
|
-
}));
|
|
7151
|
-
await fsPromises2.mkdir(this.indexPath, { recursive: true });
|
|
8008
|
+
await this.resetLocalIndexArtifacts();
|
|
7152
8009
|
return true;
|
|
7153
8010
|
}
|
|
7154
8011
|
migrateFromLegacyIndex() {
|
|
@@ -7267,8 +8124,62 @@ var Indexer = class {
|
|
|
7267
8124
|
return this.indexCompatibility;
|
|
7268
8125
|
}
|
|
7269
8126
|
async ensureInitialized() {
|
|
8127
|
+
let initializedReader = false;
|
|
8128
|
+
while (true) {
|
|
8129
|
+
if (this.initializationPromise) {
|
|
8130
|
+
await this.initializationPromise;
|
|
8131
|
+
}
|
|
8132
|
+
if (!this.isInitializedFor("reader")) {
|
|
8133
|
+
await this.initialize();
|
|
8134
|
+
initializedReader = true;
|
|
8135
|
+
continue;
|
|
8136
|
+
}
|
|
8137
|
+
if (this.initializationMode === "writer" && !this.activeIndexLease && !initializedReader) {
|
|
8138
|
+
if (!this.refreshInactiveWriterArtifacts()) {
|
|
8139
|
+
this.resetLoadedIndexState(true);
|
|
8140
|
+
await this.initialize();
|
|
8141
|
+
initializedReader = true;
|
|
8142
|
+
continue;
|
|
8143
|
+
}
|
|
8144
|
+
}
|
|
8145
|
+
if (this.initializationMode === "reader" && !initializedReader) {
|
|
8146
|
+
this.refreshReaderArtifacts();
|
|
8147
|
+
}
|
|
8148
|
+
const state = this.requireLoadedIndexState();
|
|
8149
|
+
return {
|
|
8150
|
+
...state,
|
|
8151
|
+
readIssues: [...this.readIssues],
|
|
8152
|
+
compatibility: this.indexCompatibility ?? this.validateIndexCompatibility(state.configuredProviderInfo)
|
|
8153
|
+
};
|
|
8154
|
+
}
|
|
8155
|
+
}
|
|
8156
|
+
async ensureInitializedUnlocked(recoveredOwners = []) {
|
|
8157
|
+
this.requireActiveLease();
|
|
8158
|
+
if (this.initializationPromise) {
|
|
8159
|
+
await this.initializationPromise;
|
|
8160
|
+
}
|
|
8161
|
+
if (recoveredOwners.length > 0 || !this.isInitializedFor("writer")) {
|
|
8162
|
+
const retireReaderDatabase = this.initializationMode === "reader";
|
|
8163
|
+
this.resetLoadedIndexState(retireReaderDatabase);
|
|
8164
|
+
await this.initializeOnce("writer", recoveredOwners, { skipAutoGc: true });
|
|
8165
|
+
} else {
|
|
8166
|
+
this.refreshLoadedIndexState();
|
|
8167
|
+
}
|
|
8168
|
+
if (this.config.indexing.autoGc) {
|
|
8169
|
+
await this.maybeRunAutoGc();
|
|
8170
|
+
}
|
|
8171
|
+
return this.requireLoadedIndexState();
|
|
8172
|
+
}
|
|
8173
|
+
requireReadableComponents(readIssues, ...components) {
|
|
8174
|
+
const componentSet = new Set(components);
|
|
8175
|
+
const issues = readIssues.filter((issue) => issue.blocking && componentSet.has(issue.component));
|
|
8176
|
+
if (issues.length > 0) {
|
|
8177
|
+
throw new Error(issues.map((issue) => issue.message).join(" "));
|
|
8178
|
+
}
|
|
8179
|
+
}
|
|
8180
|
+
requireLoadedIndexState() {
|
|
7270
8181
|
if (!this.store || !this.provider || !this.invertedIndex || !this.configuredProviderInfo || !this.database) {
|
|
7271
|
-
|
|
8182
|
+
throw new Error("Index state is not initialized");
|
|
7272
8183
|
}
|
|
7273
8184
|
return {
|
|
7274
8185
|
store: this.store,
|
|
@@ -7292,7 +8203,12 @@ var Indexer = class {
|
|
|
7292
8203
|
return createCostEstimate(files, configuredProviderInfo);
|
|
7293
8204
|
}
|
|
7294
8205
|
async index(onProgress) {
|
|
7295
|
-
|
|
8206
|
+
return this.withIndexMutationLease("index", async (recoveredOwners) => {
|
|
8207
|
+
return this.indexUnlocked(onProgress, recoveredOwners);
|
|
8208
|
+
});
|
|
8209
|
+
}
|
|
8210
|
+
async indexUnlocked(onProgress, recoveredOwners = [], stateReady = false) {
|
|
8211
|
+
const { store, provider, invertedIndex, database, configuredProviderInfo } = stateReady ? this.requireLoadedIndexState() : await this.ensureInitializedUnlocked(recoveredOwners);
|
|
7296
8212
|
const scopedRoots = this.config.scope === "global" ? this.getScopedRoots() : null;
|
|
7297
8213
|
const branchCatalogKey = this.getBranchCatalogKey();
|
|
7298
8214
|
const forceScopedReembed = scopedRoots !== null && database.getMetadata(this.getProjectForceReembedMetadataKey()) === "true";
|
|
@@ -7302,7 +8218,6 @@ var Indexer = class {
|
|
|
7302
8218
|
`${this.indexCompatibility?.reason} Run index_codebase with force=true to rebuild the index.`
|
|
7303
8219
|
);
|
|
7304
8220
|
}
|
|
7305
|
-
this.acquireIndexingLock();
|
|
7306
8221
|
this.logger.recordIndexingStart();
|
|
7307
8222
|
this.logger.info("Starting indexing", { projectRoot: this.projectRoot });
|
|
7308
8223
|
const startTime = Date.now();
|
|
@@ -7451,7 +8366,7 @@ var Indexer = class {
|
|
|
7451
8366
|
for (const parsed of parsedFiles) {
|
|
7452
8367
|
currentFilePaths.add(parsed.path);
|
|
7453
8368
|
if (parsed.chunks.length === 0) {
|
|
7454
|
-
const relativePath =
|
|
8369
|
+
const relativePath = path12.relative(this.projectRoot, parsed.path);
|
|
7455
8370
|
stats.parseFailures.push(relativePath);
|
|
7456
8371
|
}
|
|
7457
8372
|
let fileChunkCount = 0;
|
|
@@ -7651,7 +8566,9 @@ var Indexer = class {
|
|
|
7651
8566
|
database.addChunksToBranchBatch(branchCatalogKey, Array.from(currentChunkIds));
|
|
7652
8567
|
database.clearBranchSymbols(branchCatalogKey);
|
|
7653
8568
|
database.addSymbolsToBranchBatch(branchCatalogKey, Array.from(allSymbolIds));
|
|
7654
|
-
|
|
8569
|
+
const vectorPath = path12.join(this.indexPath, "vectors");
|
|
8570
|
+
const shouldFingerprintLegacyPair = !store.hasFingerprint() && existsSync7(vectorPath) && existsSync7(`${vectorPath}.meta.json`);
|
|
8571
|
+
if (backfilledBlameMetadata || shouldFingerprintLegacyPair) {
|
|
7655
8572
|
store.save();
|
|
7656
8573
|
}
|
|
7657
8574
|
if (scopedRoots) {
|
|
@@ -7672,7 +8589,6 @@ var Indexer = class {
|
|
|
7672
8589
|
chunksProcessed: 0,
|
|
7673
8590
|
totalChunks: 0
|
|
7674
8591
|
});
|
|
7675
|
-
this.releaseIndexingLock();
|
|
7676
8592
|
return stats;
|
|
7677
8593
|
}
|
|
7678
8594
|
if (pendingChunks.length === 0) {
|
|
@@ -7681,7 +8597,7 @@ var Indexer = class {
|
|
|
7681
8597
|
database.clearBranchSymbols(branchCatalogKey);
|
|
7682
8598
|
database.addSymbolsToBranchBatch(branchCatalogKey, Array.from(allSymbolIds));
|
|
7683
8599
|
store.save();
|
|
7684
|
-
|
|
8600
|
+
this.saveInvertedIndex(invertedIndex);
|
|
7685
8601
|
if (scopedRoots) {
|
|
7686
8602
|
this.replaceScopedFileHashCache(currentFileHashes, scopedRoots);
|
|
7687
8603
|
this.clearScopedFailedBatches(scopedRoots);
|
|
@@ -7700,7 +8616,6 @@ var Indexer = class {
|
|
|
7700
8616
|
chunksProcessed: 0,
|
|
7701
8617
|
totalChunks: 0
|
|
7702
8618
|
});
|
|
7703
|
-
this.releaseIndexingLock();
|
|
7704
8619
|
return stats;
|
|
7705
8620
|
}
|
|
7706
8621
|
onProgress?.({
|
|
@@ -7931,7 +8846,7 @@ var Indexer = class {
|
|
|
7931
8846
|
database.clearBranchSymbols(branchCatalogKey);
|
|
7932
8847
|
database.addSymbolsToBranchBatch(branchCatalogKey, Array.from(allSymbolIds));
|
|
7933
8848
|
store.save();
|
|
7934
|
-
|
|
8849
|
+
this.saveInvertedIndex(invertedIndex);
|
|
7935
8850
|
if (scopedRoots) {
|
|
7936
8851
|
this.replaceScopedFileHashCache(currentFileHashes, scopedRoots);
|
|
7937
8852
|
} else {
|
|
@@ -7983,7 +8898,6 @@ var Indexer = class {
|
|
|
7983
8898
|
chunksProcessed: stats.indexedChunks,
|
|
7984
8899
|
totalChunks: pendingChunks.length
|
|
7985
8900
|
});
|
|
7986
|
-
this.releaseIndexingLock();
|
|
7987
8901
|
return stats;
|
|
7988
8902
|
}
|
|
7989
8903
|
async getQueryEmbedding(query, provider) {
|
|
@@ -8049,8 +8963,8 @@ var Indexer = class {
|
|
|
8049
8963
|
return intersection / union;
|
|
8050
8964
|
}
|
|
8051
8965
|
async search(query, limit, options) {
|
|
8052
|
-
const { store, provider, database } = await this.ensureInitialized();
|
|
8053
|
-
|
|
8966
|
+
const { store, provider, invertedIndex, database, readIssues, compatibility } = await this.ensureInitialized();
|
|
8967
|
+
this.requireReadableComponents(readIssues, "vectors", "database");
|
|
8054
8968
|
if (!compatibility.compatible) {
|
|
8055
8969
|
throw new Error(
|
|
8056
8970
|
`${compatibility.reason ?? "Index is incompatible with current embedding provider."} A possible solution is to run index_codebase with force=true to rebuild the index.`
|
|
@@ -8064,6 +8978,7 @@ var Indexer = class {
|
|
|
8064
8978
|
const maxResults = limit ?? this.config.search.maxResults;
|
|
8065
8979
|
const hybridWeight = options?.hybridWeight ?? this.config.search.hybridWeight;
|
|
8066
8980
|
const fusionStrategy = this.config.search.fusionStrategy;
|
|
8981
|
+
const effectiveHybridWeight = fusionStrategy === "weighted" && readIssues.some((issue) => issue.component === "keyword") ? 0 : hybridWeight;
|
|
8067
8982
|
const rrfK = this.config.search.rrfK;
|
|
8068
8983
|
const rerankTopN = this.config.search.rerankTopN;
|
|
8069
8984
|
const filterByBranch = options?.filterByBranch ?? true;
|
|
@@ -8072,7 +8987,7 @@ var Indexer = class {
|
|
|
8072
8987
|
this.logger.search("debug", "Starting search", {
|
|
8073
8988
|
query,
|
|
8074
8989
|
maxResults,
|
|
8075
|
-
hybridWeight,
|
|
8990
|
+
hybridWeight: effectiveHybridWeight,
|
|
8076
8991
|
fusionStrategy,
|
|
8077
8992
|
rrfK,
|
|
8078
8993
|
rerankTopN,
|
|
@@ -8086,7 +9001,7 @@ var Indexer = class {
|
|
|
8086
9001
|
const semanticResults = store.search(embedding, maxResults * 4);
|
|
8087
9002
|
const vectorMs = performance2.now() - vectorStartTime;
|
|
8088
9003
|
const keywordStartTime = performance2.now();
|
|
8089
|
-
const keywordResults = await this.keywordSearch(query, maxResults * 4);
|
|
9004
|
+
const keywordResults = await this.keywordSearch(query, maxResults * 4, store, invertedIndex);
|
|
8090
9005
|
const keywordMs = performance2.now() - keywordStartTime;
|
|
8091
9006
|
let branchChunkIds = null;
|
|
8092
9007
|
if (filterByBranch && (this.config.scope === "global" || this.currentBranch !== "default")) {
|
|
@@ -8123,7 +9038,7 @@ var Indexer = class {
|
|
|
8123
9038
|
rrfK,
|
|
8124
9039
|
rerankTopN,
|
|
8125
9040
|
limit: maxResults,
|
|
8126
|
-
hybridWeight,
|
|
9041
|
+
hybridWeight: effectiveHybridWeight,
|
|
8127
9042
|
prioritizeSourcePaths: sourceIntent
|
|
8128
9043
|
});
|
|
8129
9044
|
const rerankedCombined = await this.rerankCandidatesWithApi(query, combined, {
|
|
@@ -8223,8 +9138,7 @@ var Indexer = class {
|
|
|
8223
9138
|
})
|
|
8224
9139
|
);
|
|
8225
9140
|
}
|
|
8226
|
-
async keywordSearch(query, limit) {
|
|
8227
|
-
const { store, invertedIndex } = await this.ensureInitialized();
|
|
9141
|
+
async keywordSearch(query, limit, store, invertedIndex) {
|
|
8228
9142
|
const scores = invertedIndex.search(query);
|
|
8229
9143
|
if (scores.size === 0) {
|
|
8230
9144
|
return [];
|
|
@@ -8242,24 +9156,54 @@ var Indexer = class {
|
|
|
8242
9156
|
return results.slice(0, limit);
|
|
8243
9157
|
}
|
|
8244
9158
|
async getStatus() {
|
|
8245
|
-
const { store, configuredProviderInfo, database } = await this.ensureInitialized();
|
|
9159
|
+
const { store, configuredProviderInfo, database, readIssues, compatibility } = await this.ensureInitialized();
|
|
8246
9160
|
const failedBatchesCount = this.getFailedBatchesCount();
|
|
9161
|
+
const vectorCount = store.count();
|
|
9162
|
+
const statusReadIssues = [...readIssues];
|
|
9163
|
+
let startupWarning = "";
|
|
9164
|
+
if (!statusReadIssues.some((issue) => issue.component === "database")) {
|
|
9165
|
+
try {
|
|
9166
|
+
startupWarning = database.getMetadata(STARTUP_WARNING_METADATA_KEY) ?? "";
|
|
9167
|
+
} catch (error) {
|
|
9168
|
+
const message = this.getDatabaseReadIssueMessage();
|
|
9169
|
+
statusReadIssues.push(this.createReadIssue("database", message));
|
|
9170
|
+
if (!this.readIssues.some((issue) => issue.component === "database")) {
|
|
9171
|
+
this.recordReadIssue("database", message, error);
|
|
9172
|
+
}
|
|
9173
|
+
}
|
|
9174
|
+
}
|
|
9175
|
+
const readWarning = statusReadIssues.map((issue) => issue.message).join(" ");
|
|
9176
|
+
const warning = [readWarning, startupWarning].filter((message) => message.length > 0).join(" ");
|
|
9177
|
+
const hasBlockingReadIssue = statusReadIssues.some((issue) => issue.blocking);
|
|
8247
9178
|
return {
|
|
8248
|
-
indexed:
|
|
8249
|
-
vectorCount
|
|
9179
|
+
indexed: vectorCount > 0 && !hasBlockingReadIssue,
|
|
9180
|
+
vectorCount,
|
|
8250
9181
|
provider: configuredProviderInfo.provider,
|
|
8251
9182
|
model: configuredProviderInfo.modelInfo.model,
|
|
8252
9183
|
indexPath: this.indexPath,
|
|
8253
9184
|
currentBranch: this.currentBranch,
|
|
8254
9185
|
baseBranch: this.baseBranch,
|
|
8255
|
-
compatibility
|
|
9186
|
+
compatibility,
|
|
8256
9187
|
failedBatchesCount,
|
|
8257
9188
|
failedBatchesPath: failedBatchesCount > 0 ? this.failedBatchesPath : void 0,
|
|
8258
|
-
warning:
|
|
9189
|
+
warning: warning || void 0
|
|
8259
9190
|
};
|
|
8260
9191
|
}
|
|
9192
|
+
async forceIndex(onProgress) {
|
|
9193
|
+
return this.withIndexMutationLease("force-index", async (recoveredOwners) => {
|
|
9194
|
+
await this.ensureInitializedUnlocked(recoveredOwners);
|
|
9195
|
+
await this.clearIndexUnlocked();
|
|
9196
|
+
return this.indexUnlocked(onProgress, [], true);
|
|
9197
|
+
});
|
|
9198
|
+
}
|
|
8261
9199
|
async clearIndex() {
|
|
8262
|
-
|
|
9200
|
+
await this.withIndexMutationLease("clear", async (recoveredOwners) => {
|
|
9201
|
+
await this.ensureInitializedUnlocked(recoveredOwners);
|
|
9202
|
+
await this.clearIndexUnlocked();
|
|
9203
|
+
});
|
|
9204
|
+
}
|
|
9205
|
+
async clearIndexUnlocked() {
|
|
9206
|
+
const { store, invertedIndex, database } = this.requireLoadedIndexState();
|
|
8263
9207
|
if (this.config.scope === "global") {
|
|
8264
9208
|
store.load();
|
|
8265
9209
|
invertedIndex.load();
|
|
@@ -8286,7 +9230,7 @@ var Indexer = class {
|
|
|
8286
9230
|
store.clear();
|
|
8287
9231
|
store.save();
|
|
8288
9232
|
invertedIndex.clear();
|
|
8289
|
-
|
|
9233
|
+
this.saveInvertedIndex(invertedIndex);
|
|
8290
9234
|
this.fileHashCache.clear();
|
|
8291
9235
|
this.saveFileHashCache();
|
|
8292
9236
|
database.clearAllIndexedData();
|
|
@@ -8310,14 +9254,7 @@ var Indexer = class {
|
|
|
8310
9254
|
this.indexCompatibility = compatibility;
|
|
8311
9255
|
return;
|
|
8312
9256
|
}
|
|
8313
|
-
|
|
8314
|
-
if (this.host !== "opencode") {
|
|
8315
|
-
localProjectIndexPaths.push(path11.join(this.projectRoot, getHostProjectIndexRelativePath("opencode")));
|
|
8316
|
-
}
|
|
8317
|
-
const isLocalProjectIndex = localProjectIndexPaths.some(
|
|
8318
|
-
(localPath) => path11.resolve(this.indexPath) === path11.resolve(localPath)
|
|
8319
|
-
);
|
|
8320
|
-
if (!isLocalProjectIndex) {
|
|
9257
|
+
if (!this.isLocalProjectIndexPath()) {
|
|
8321
9258
|
throw new Error(
|
|
8322
9259
|
"Project-scoped force rebuild is unsafe while using an inherited worktree index. Create a local project config boundary before clearing the index."
|
|
8323
9260
|
);
|
|
@@ -8325,7 +9262,7 @@ var Indexer = class {
|
|
|
8325
9262
|
store.clear();
|
|
8326
9263
|
store.save();
|
|
8327
9264
|
invertedIndex.clear();
|
|
8328
|
-
|
|
9265
|
+
this.saveInvertedIndex(invertedIndex);
|
|
8329
9266
|
this.fileHashCache.clear();
|
|
8330
9267
|
this.saveFileHashCache();
|
|
8331
9268
|
database.clearAllIndexedData();
|
|
@@ -8343,7 +9280,13 @@ var Indexer = class {
|
|
|
8343
9280
|
this.indexCompatibility = this.validateIndexCompatibility(this.configuredProviderInfo);
|
|
8344
9281
|
}
|
|
8345
9282
|
async healthCheck() {
|
|
8346
|
-
|
|
9283
|
+
return this.withIndexMutationLease("health-check", async (recoveredOwners) => {
|
|
9284
|
+
await this.ensureInitializedUnlocked(recoveredOwners);
|
|
9285
|
+
return this.healthCheckUnlocked();
|
|
9286
|
+
});
|
|
9287
|
+
}
|
|
9288
|
+
async healthCheckUnlocked() {
|
|
9289
|
+
const { store, invertedIndex, database } = this.requireLoadedIndexState();
|
|
8347
9290
|
this.logger.gc("info", "Starting health check");
|
|
8348
9291
|
const allMetadata = store.getAllMetadata();
|
|
8349
9292
|
const filePathsToChunkKeys = /* @__PURE__ */ new Map();
|
|
@@ -8356,7 +9299,7 @@ var Indexer = class {
|
|
|
8356
9299
|
const removedChunkKeys = [];
|
|
8357
9300
|
const chunkKeysByRemovedFile = /* @__PURE__ */ new Map();
|
|
8358
9301
|
for (const [filePath, chunkKeys] of filePathsToChunkKeys) {
|
|
8359
|
-
if (!
|
|
9302
|
+
if (!existsSync7(filePath)) {
|
|
8360
9303
|
chunkKeysByRemovedFile.set(filePath, chunkKeys);
|
|
8361
9304
|
for (const key of chunkKeys) {
|
|
8362
9305
|
removedChunkKeys.push(key);
|
|
@@ -8381,7 +9324,7 @@ var Indexer = class {
|
|
|
8381
9324
|
const removedCount = removedChunkKeys.length;
|
|
8382
9325
|
if (removedCount > 0) {
|
|
8383
9326
|
store.save();
|
|
8384
|
-
|
|
9327
|
+
this.saveInvertedIndex(invertedIndex);
|
|
8385
9328
|
}
|
|
8386
9329
|
let gcOrphanEmbeddings;
|
|
8387
9330
|
let gcOrphanChunks;
|
|
@@ -8396,7 +9339,7 @@ var Indexer = class {
|
|
|
8396
9339
|
if (!await this.tryResetCorruptedIndex("running index health check", error)) {
|
|
8397
9340
|
throw error;
|
|
8398
9341
|
}
|
|
8399
|
-
await this.
|
|
9342
|
+
await this.initializeUnlocked("writer", [], { skipAutoGc: true });
|
|
8400
9343
|
return {
|
|
8401
9344
|
removed: 0,
|
|
8402
9345
|
filePaths: [],
|
|
@@ -8405,7 +9348,7 @@ var Indexer = class {
|
|
|
8405
9348
|
gcOrphanSymbols: 0,
|
|
8406
9349
|
gcOrphanCallEdges: 0,
|
|
8407
9350
|
resetCorruptedIndex: true,
|
|
8408
|
-
warning: this.getCorruptedIndexWarning(
|
|
9351
|
+
warning: this.getCorruptedIndexWarning(path12.join(this.indexPath, "codebase.db"))
|
|
8409
9352
|
};
|
|
8410
9353
|
}
|
|
8411
9354
|
this.logger.recordGc(removedCount, gcOrphanChunks, gcOrphanEmbeddings);
|
|
@@ -8418,7 +9361,13 @@ var Indexer = class {
|
|
|
8418
9361
|
return { removed: removedCount, filePaths: removedFilePaths, gcOrphanEmbeddings, gcOrphanChunks, gcOrphanSymbols, gcOrphanCallEdges };
|
|
8419
9362
|
}
|
|
8420
9363
|
async retryFailedBatches() {
|
|
8421
|
-
|
|
9364
|
+
return this.withIndexMutationLease("retry-failed-batches", async (recoveredOwners) => {
|
|
9365
|
+
await this.ensureInitializedUnlocked(recoveredOwners);
|
|
9366
|
+
return this.retryFailedBatchesUnlocked();
|
|
9367
|
+
});
|
|
9368
|
+
}
|
|
9369
|
+
async retryFailedBatchesUnlocked() {
|
|
9370
|
+
const { store, provider, invertedIndex, database, configuredProviderInfo } = this.requireLoadedIndexState();
|
|
8422
9371
|
const maxChunkTokens = getSafeEmbeddingChunkTokenLimit(configuredProviderInfo);
|
|
8423
9372
|
const providerRateLimits = this.getProviderRateLimits(configuredProviderInfo.provider);
|
|
8424
9373
|
const roots = this.config.scope === "global" ? this.getScopedRoots() : null;
|
|
@@ -8582,7 +9531,7 @@ var Indexer = class {
|
|
|
8582
9531
|
}
|
|
8583
9532
|
if (succeeded > 0) {
|
|
8584
9533
|
store.save();
|
|
8585
|
-
|
|
9534
|
+
this.saveInvertedIndex(invertedIndex);
|
|
8586
9535
|
}
|
|
8587
9536
|
if (roots && succeeded > 0 && persistedStillFailing.length === 0 && this.hasProjectForceReembedPending()) {
|
|
8588
9537
|
database.deleteMetadata(this.getProjectForceReembedMetadataKey());
|
|
@@ -8610,15 +9559,16 @@ var Indexer = class {
|
|
|
8610
9559
|
}
|
|
8611
9560
|
}
|
|
8612
9561
|
async getDatabaseStats() {
|
|
8613
|
-
const { database } = await this.ensureInitialized();
|
|
9562
|
+
const { database, readIssues } = await this.ensureInitialized();
|
|
9563
|
+
this.requireReadableComponents(readIssues, "database");
|
|
8614
9564
|
return database.getStats();
|
|
8615
9565
|
}
|
|
8616
9566
|
getLogger() {
|
|
8617
9567
|
return this.logger;
|
|
8618
9568
|
}
|
|
8619
9569
|
async findSimilar(code, limit = this.config.search.maxResults, options) {
|
|
8620
|
-
const { store, provider, database } = await this.ensureInitialized();
|
|
8621
|
-
|
|
9570
|
+
const { store, provider, database, readIssues, compatibility } = await this.ensureInitialized();
|
|
9571
|
+
this.requireReadableComponents(readIssues, "vectors", "database");
|
|
8622
9572
|
if (!compatibility.compatible) {
|
|
8623
9573
|
throw new Error(
|
|
8624
9574
|
`${compatibility.reason ?? "Index is incompatible with current embedding provider."} Run index_codebase with force=true to rebuild the index.`
|
|
@@ -8732,7 +9682,8 @@ var Indexer = class {
|
|
|
8732
9682
|
);
|
|
8733
9683
|
}
|
|
8734
9684
|
async getCallers(targetName, callTypeFilter) {
|
|
8735
|
-
const { database } = await this.ensureInitialized();
|
|
9685
|
+
const { database, readIssues } = await this.ensureInitialized();
|
|
9686
|
+
this.requireReadableComponents(readIssues, "database");
|
|
8736
9687
|
const seen = /* @__PURE__ */ new Set();
|
|
8737
9688
|
const results = [];
|
|
8738
9689
|
for (const branchKey of this.getBranchCatalogKeys()) {
|
|
@@ -8746,7 +9697,8 @@ var Indexer = class {
|
|
|
8746
9697
|
return results;
|
|
8747
9698
|
}
|
|
8748
9699
|
async getCallees(symbolId, callTypeFilter) {
|
|
8749
|
-
const { database } = await this.ensureInitialized();
|
|
9700
|
+
const { database, readIssues } = await this.ensureInitialized();
|
|
9701
|
+
this.requireReadableComponents(readIssues, "database");
|
|
8750
9702
|
const seen = /* @__PURE__ */ new Set();
|
|
8751
9703
|
const results = [];
|
|
8752
9704
|
for (const branchKey of this.getBranchCatalogKeys()) {
|
|
@@ -8760,43 +9712,50 @@ var Indexer = class {
|
|
|
8760
9712
|
return results;
|
|
8761
9713
|
}
|
|
8762
9714
|
async findCallPath(fromName, toName, maxDepth) {
|
|
8763
|
-
const { database } = await this.ensureInitialized();
|
|
9715
|
+
const { database, readIssues } = await this.ensureInitialized();
|
|
9716
|
+
this.requireReadableComponents(readIssues, "database");
|
|
8764
9717
|
let shortest = [];
|
|
8765
9718
|
for (const branchKey of this.getBranchCatalogKeys()) {
|
|
8766
|
-
const
|
|
8767
|
-
if (
|
|
8768
|
-
shortest =
|
|
9719
|
+
const path27 = database.findShortestPath(fromName, toName, branchKey, maxDepth);
|
|
9720
|
+
if (path27.length > 0 && (shortest.length === 0 || path27.length < shortest.length)) {
|
|
9721
|
+
shortest = path27;
|
|
8769
9722
|
}
|
|
8770
9723
|
}
|
|
8771
9724
|
return shortest;
|
|
8772
9725
|
}
|
|
8773
9726
|
async getSymbolsForBranch(branch) {
|
|
8774
|
-
const { database } = await this.ensureInitialized();
|
|
9727
|
+
const { database, readIssues } = await this.ensureInitialized();
|
|
9728
|
+
this.requireReadableComponents(readIssues, "database");
|
|
8775
9729
|
const resolvedBranch = branch ?? this.getBranchCatalogKey();
|
|
8776
9730
|
return database.getSymbolsForBranch(resolvedBranch);
|
|
8777
9731
|
}
|
|
8778
9732
|
async getSymbolsForFiles(filePaths, branch) {
|
|
8779
|
-
const { database } = await this.ensureInitialized();
|
|
9733
|
+
const { database, readIssues } = await this.ensureInitialized();
|
|
9734
|
+
this.requireReadableComponents(readIssues, "database");
|
|
8780
9735
|
const resolvedBranch = branch ?? this.getBranchCatalogKey();
|
|
8781
9736
|
return database.getSymbolsForFiles(filePaths, resolvedBranch);
|
|
8782
9737
|
}
|
|
8783
9738
|
async getTransitiveReachability(rootSymbolIds, direction, maxDepth) {
|
|
8784
|
-
const { database } = await this.ensureInitialized();
|
|
9739
|
+
const { database, readIssues } = await this.ensureInitialized();
|
|
9740
|
+
this.requireReadableComponents(readIssues, "database");
|
|
8785
9741
|
const branch = this.getBranchCatalogKey();
|
|
8786
9742
|
return database.getTransitiveReachability(rootSymbolIds, branch, direction, maxDepth);
|
|
8787
9743
|
}
|
|
8788
9744
|
async detectCommunities(branch, symbolIds) {
|
|
8789
|
-
const { database } = await this.ensureInitialized();
|
|
9745
|
+
const { database, readIssues } = await this.ensureInitialized();
|
|
9746
|
+
this.requireReadableComponents(readIssues, "database");
|
|
8790
9747
|
const resolvedBranch = branch ?? this.getBranchCatalogKey();
|
|
8791
9748
|
return database.detectCommunities(resolvedBranch, symbolIds);
|
|
8792
9749
|
}
|
|
8793
9750
|
async computeCentrality(branch) {
|
|
8794
|
-
const { database } = await this.ensureInitialized();
|
|
9751
|
+
const { database, readIssues } = await this.ensureInitialized();
|
|
9752
|
+
this.requireReadableComponents(readIssues, "database");
|
|
8795
9753
|
const resolvedBranch = branch ?? this.getBranchCatalogKey();
|
|
8796
9754
|
return database.computeCentrality(resolvedBranch);
|
|
8797
9755
|
}
|
|
8798
9756
|
async getPrImpact(opts) {
|
|
8799
|
-
const { database } = await this.ensureInitialized();
|
|
9757
|
+
const { database, readIssues } = await this.ensureInitialized();
|
|
9758
|
+
this.requireReadableComponents(readIssues, "database");
|
|
8800
9759
|
const execFileAsync3 = promisify3(execFile3);
|
|
8801
9760
|
const changedFilesResult = await getChangedFiles({
|
|
8802
9761
|
pr: opts.pr,
|
|
@@ -8819,7 +9778,7 @@ var Indexer = class {
|
|
|
8819
9778
|
"Run index_codebase first to build the call graph and symbol index for this branch."
|
|
8820
9779
|
);
|
|
8821
9780
|
}
|
|
8822
|
-
const absoluteChangedFiles = changedFiles.map((f) =>
|
|
9781
|
+
const absoluteChangedFiles = changedFiles.map((f) => path12.resolve(this.projectRoot, f));
|
|
8823
9782
|
const directSymbols = database.getSymbolsForFiles(absoluteChangedFiles, branchKey);
|
|
8824
9783
|
const directIds = directSymbols.map((s) => s.id);
|
|
8825
9784
|
const direction = opts.direction ?? "both";
|
|
@@ -8902,7 +9861,7 @@ var Indexer = class {
|
|
|
8902
9861
|
projectRoot: this.projectRoot,
|
|
8903
9862
|
baseBranch: this.baseBranch
|
|
8904
9863
|
});
|
|
8905
|
-
const otherAbsolute = otherChanged.files.map((f) =>
|
|
9864
|
+
const otherAbsolute = otherChanged.files.map((f) => path12.resolve(this.projectRoot, f));
|
|
8906
9865
|
const otherBranchKey = this.getBranchCatalogKeyFor(openPr.headRefName);
|
|
8907
9866
|
const otherSymbols = database.getSymbolsForFiles(otherAbsolute, otherBranchKey);
|
|
8908
9867
|
const otherLabels = /* @__PURE__ */ new Set();
|
|
@@ -8952,7 +9911,8 @@ var Indexer = class {
|
|
|
8952
9911
|
};
|
|
8953
9912
|
}
|
|
8954
9913
|
async getVisualizationData(options) {
|
|
8955
|
-
const { database, store } = await this.ensureInitialized();
|
|
9914
|
+
const { database, store, readIssues } = await this.ensureInitialized();
|
|
9915
|
+
this.requireReadableComponents(readIssues, "vectors", "database");
|
|
8956
9916
|
const seenSymbols = /* @__PURE__ */ new Map();
|
|
8957
9917
|
const seenEdges = /* @__PURE__ */ new Map();
|
|
8958
9918
|
for (const branchKey of this.getBranchCatalogKeys()) {
|
|
@@ -8965,12 +9925,12 @@ var Indexer = class {
|
|
|
8965
9925
|
if (meta.filePath) filePaths.add(meta.filePath);
|
|
8966
9926
|
}
|
|
8967
9927
|
const directory = options?.directory?.replace(/\/$/, "");
|
|
8968
|
-
const absoluteDirectoryFilter = directory ?
|
|
9928
|
+
const absoluteDirectoryFilter = directory ? path12.resolve(this.projectRoot, directory) : void 0;
|
|
8969
9929
|
for (const filePath of filePaths) {
|
|
8970
9930
|
if (directory) {
|
|
8971
|
-
const absoluteFilePath =
|
|
9931
|
+
const absoluteFilePath = path12.resolve(filePath);
|
|
8972
9932
|
const matchesRelative = filePath === directory || filePath.startsWith(directory + "/");
|
|
8973
|
-
const matchesProjectRelative = absoluteDirectoryFilter !== void 0 && (absoluteFilePath === absoluteDirectoryFilter || absoluteFilePath.startsWith(absoluteDirectoryFilter +
|
|
9933
|
+
const matchesProjectRelative = absoluteDirectoryFilter !== void 0 && (absoluteFilePath === absoluteDirectoryFilter || absoluteFilePath.startsWith(absoluteDirectoryFilter + path12.sep));
|
|
8974
9934
|
if (!matchesRelative && !matchesProjectRelative) {
|
|
8975
9935
|
continue;
|
|
8976
9936
|
}
|
|
@@ -8992,12 +9952,23 @@ var Indexer = class {
|
|
|
8992
9952
|
return { symbols: [...seenSymbols.values()], edges: [...seenEdges.values()] };
|
|
8993
9953
|
}
|
|
8994
9954
|
async close() {
|
|
8995
|
-
|
|
9955
|
+
this.database?.close();
|
|
9956
|
+
for (const database of this.retiredDatabases) {
|
|
9957
|
+
database.close();
|
|
9958
|
+
}
|
|
9959
|
+
this.retiredDatabases = [];
|
|
8996
9960
|
this.database = null;
|
|
8997
9961
|
this.store = null;
|
|
8998
9962
|
this.invertedIndex = null;
|
|
8999
9963
|
this.provider = null;
|
|
9000
9964
|
this.reranker = null;
|
|
9965
|
+
this.configuredProviderInfo = null;
|
|
9966
|
+
this.indexCompatibility = null;
|
|
9967
|
+
this.initializationMode = "none";
|
|
9968
|
+
this.readIssues = [];
|
|
9969
|
+
this.readerArtifactFingerprint = null;
|
|
9970
|
+
this.writerArtifactFingerprint = null;
|
|
9971
|
+
this.readerArtifactRetryAfter.clear();
|
|
9001
9972
|
}
|
|
9002
9973
|
};
|
|
9003
9974
|
|
|
@@ -9248,15 +10219,15 @@ function computeEvalMetrics(queries, perQuery, embeddingCallCount, embeddingToke
|
|
|
9248
10219
|
}
|
|
9249
10220
|
|
|
9250
10221
|
// src/eval/runner-config.ts
|
|
9251
|
-
import { existsSync as
|
|
9252
|
-
import * as
|
|
9253
|
-
import * as
|
|
10222
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync4, readFileSync as readFileSync8, rmSync as rmSync2, writeFileSync as writeFileSync4 } from "fs";
|
|
10223
|
+
import * as os5 from "os";
|
|
10224
|
+
import * as path14 from "path";
|
|
9254
10225
|
|
|
9255
10226
|
// src/config/rebase.ts
|
|
9256
|
-
import * as
|
|
10227
|
+
import * as path13 from "path";
|
|
9257
10228
|
function isWithinRoot(rootDir, targetPath) {
|
|
9258
|
-
const relativePath =
|
|
9259
|
-
return relativePath === "" || !relativePath.startsWith("..") && !
|
|
10229
|
+
const relativePath = path13.relative(rootDir, targetPath);
|
|
10230
|
+
return relativePath === "" || !relativePath.startsWith("..") && !path13.isAbsolute(relativePath);
|
|
9260
10231
|
}
|
|
9261
10232
|
function rebasePathEntries(values, fromDir, toDir) {
|
|
9262
10233
|
if (!Array.isArray(values)) {
|
|
@@ -9264,10 +10235,10 @@ function rebasePathEntries(values, fromDir, toDir) {
|
|
|
9264
10235
|
}
|
|
9265
10236
|
return values.filter((value) => typeof value === "string").map((value) => {
|
|
9266
10237
|
const trimmed = value.trim();
|
|
9267
|
-
if (!trimmed ||
|
|
10238
|
+
if (!trimmed || path13.isAbsolute(trimmed)) {
|
|
9268
10239
|
return trimmed;
|
|
9269
10240
|
}
|
|
9270
|
-
return normalizePathSeparators(
|
|
10241
|
+
return normalizePathSeparators(path13.normalize(path13.relative(toDir, path13.resolve(fromDir, trimmed))));
|
|
9271
10242
|
}).filter(Boolean);
|
|
9272
10243
|
}
|
|
9273
10244
|
function resolveInheritedKnowledgeBaseEntries(values, sourceRoot, targetRoot) {
|
|
@@ -9279,17 +10250,17 @@ function resolveInheritedKnowledgeBaseEntries(values, sourceRoot, targetRoot) {
|
|
|
9279
10250
|
if (!trimmed) {
|
|
9280
10251
|
return trimmed;
|
|
9281
10252
|
}
|
|
9282
|
-
if (
|
|
10253
|
+
if (path13.isAbsolute(trimmed)) {
|
|
9283
10254
|
if (isWithinRoot(sourceRoot, trimmed)) {
|
|
9284
|
-
return normalizePathSeparators(
|
|
10255
|
+
return normalizePathSeparators(path13.normalize(path13.relative(sourceRoot, trimmed) || "."));
|
|
9285
10256
|
}
|
|
9286
|
-
return
|
|
10257
|
+
return path13.normalize(trimmed);
|
|
9287
10258
|
}
|
|
9288
|
-
const resolvedFromSource =
|
|
10259
|
+
const resolvedFromSource = path13.resolve(sourceRoot, trimmed);
|
|
9289
10260
|
if (isWithinRoot(sourceRoot, resolvedFromSource)) {
|
|
9290
|
-
return normalizePathSeparators(
|
|
10261
|
+
return normalizePathSeparators(path13.normalize(trimmed));
|
|
9291
10262
|
}
|
|
9292
|
-
return normalizePathSeparators(
|
|
10263
|
+
return normalizePathSeparators(path13.normalize(path13.relative(targetRoot, resolvedFromSource)));
|
|
9293
10264
|
}).filter(Boolean);
|
|
9294
10265
|
}
|
|
9295
10266
|
|
|
@@ -9327,7 +10298,7 @@ function validateEvalConfigShape(rawConfig, filePath) {
|
|
|
9327
10298
|
}
|
|
9328
10299
|
function parseJsonConfigFile(filePath) {
|
|
9329
10300
|
try {
|
|
9330
|
-
return validateEvalConfigShape(JSON.parse(
|
|
10301
|
+
return validateEvalConfigShape(JSON.parse(readFileSync8(filePath, "utf-8")), filePath);
|
|
9331
10302
|
} catch (error) {
|
|
9332
10303
|
if (error instanceof Error && error.message.startsWith("Eval config at ")) {
|
|
9333
10304
|
throw error;
|
|
@@ -9337,20 +10308,20 @@ function parseJsonConfigFile(filePath) {
|
|
|
9337
10308
|
}
|
|
9338
10309
|
}
|
|
9339
10310
|
function toAbsolute(projectRoot, maybeRelative) {
|
|
9340
|
-
return
|
|
10311
|
+
return path14.isAbsolute(maybeRelative) ? maybeRelative : path14.join(projectRoot, maybeRelative);
|
|
9341
10312
|
}
|
|
9342
10313
|
function isProjectScopedConfigPath(configPath) {
|
|
9343
|
-
return
|
|
10314
|
+
return path14.basename(configPath) === "codebase-index.json" && path14.basename(path14.dirname(configPath)) === ".opencode";
|
|
9344
10315
|
}
|
|
9345
10316
|
function normalizeEvalConfigKnowledgeBases(rawConfig, projectRoot, resolvedConfigPath) {
|
|
9346
10317
|
const config = rawConfig && typeof rawConfig === "object" ? { ...rawConfig } : {};
|
|
9347
10318
|
const rebaseEntries = (values) => isProjectScopedConfigPath(resolvedConfigPath) ? resolveInheritedKnowledgeBaseEntries(
|
|
9348
10319
|
values,
|
|
9349
|
-
|
|
10320
|
+
path14.dirname(path14.dirname(resolvedConfigPath)),
|
|
9350
10321
|
projectRoot
|
|
9351
10322
|
) : rebasePathEntries(
|
|
9352
10323
|
values,
|
|
9353
|
-
|
|
10324
|
+
path14.dirname(resolvedConfigPath),
|
|
9354
10325
|
projectRoot
|
|
9355
10326
|
);
|
|
9356
10327
|
if (Array.isArray(config.knowledgeBases)) {
|
|
@@ -9363,7 +10334,7 @@ function normalizeEvalConfigKnowledgeBases(rawConfig, projectRoot, resolvedConfi
|
|
|
9363
10334
|
}
|
|
9364
10335
|
function loadRawConfig(projectRoot, configPath) {
|
|
9365
10336
|
const fromPath = configPath ? toAbsolute(projectRoot, configPath) : null;
|
|
9366
|
-
if (fromPath &&
|
|
10337
|
+
if (fromPath && existsSync8(fromPath)) {
|
|
9367
10338
|
return normalizeEvalConfigKnowledgeBases(
|
|
9368
10339
|
parseJsonConfigFile(fromPath),
|
|
9369
10340
|
projectRoot,
|
|
@@ -9371,15 +10342,15 @@ function loadRawConfig(projectRoot, configPath) {
|
|
|
9371
10342
|
);
|
|
9372
10343
|
}
|
|
9373
10344
|
const projectConfig = resolveProjectConfigPath(projectRoot);
|
|
9374
|
-
if (
|
|
10345
|
+
if (existsSync8(projectConfig)) {
|
|
9375
10346
|
return normalizeEvalConfigKnowledgeBases(
|
|
9376
10347
|
parseJsonConfigFile(projectConfig),
|
|
9377
10348
|
projectRoot,
|
|
9378
10349
|
projectConfig
|
|
9379
10350
|
);
|
|
9380
10351
|
}
|
|
9381
|
-
const globalConfig =
|
|
9382
|
-
if (
|
|
10352
|
+
const globalConfig = path14.join(os5.homedir(), ".config", "opencode", "codebase-index.json");
|
|
10353
|
+
if (existsSync8(globalConfig)) {
|
|
9383
10354
|
return parseJsonConfigFile(globalConfig);
|
|
9384
10355
|
}
|
|
9385
10356
|
return {};
|
|
@@ -9388,24 +10359,24 @@ function getIndexRootPath(projectRoot, scope) {
|
|
|
9388
10359
|
return scope === "global" ? getGlobalIndexPath() : resolveProjectIndexPath(projectRoot, scope);
|
|
9389
10360
|
}
|
|
9390
10361
|
function getLocalProjectIndexRoot(projectRoot) {
|
|
9391
|
-
return
|
|
10362
|
+
return path14.join(projectRoot, ".opencode", "index");
|
|
9392
10363
|
}
|
|
9393
10364
|
function getLocalProjectConfigPath(projectRoot) {
|
|
9394
|
-
return
|
|
10365
|
+
return path14.join(projectRoot, ".opencode", "codebase-index.json");
|
|
9395
10366
|
}
|
|
9396
10367
|
function clearIndexRoot(projectRoot, scope) {
|
|
9397
10368
|
const indexRoot = scope === "global" ? getIndexRootPath(projectRoot, scope) : getLocalProjectIndexRoot(projectRoot);
|
|
9398
|
-
if (
|
|
9399
|
-
|
|
10369
|
+
if (existsSync8(indexRoot)) {
|
|
10370
|
+
rmSync2(indexRoot, { recursive: true, force: true });
|
|
9400
10371
|
}
|
|
9401
10372
|
}
|
|
9402
10373
|
function ensureLocalEvalProjectConfig(projectRoot, configPath) {
|
|
9403
10374
|
const localConfigPath = getLocalProjectConfigPath(projectRoot);
|
|
9404
10375
|
const resolvedConfigPath = configPath ? toAbsolute(projectRoot, configPath) : resolveProjectConfigPath(projectRoot);
|
|
9405
|
-
if (!configPath &&
|
|
10376
|
+
if (!configPath && existsSync8(localConfigPath)) {
|
|
9406
10377
|
return localConfigPath;
|
|
9407
10378
|
}
|
|
9408
|
-
if (!
|
|
10379
|
+
if (!existsSync8(resolvedConfigPath) || resolvedConfigPath === localConfigPath) {
|
|
9409
10380
|
return resolvedConfigPath;
|
|
9410
10381
|
}
|
|
9411
10382
|
const sourceConfig = normalizeEvalConfigKnowledgeBases(
|
|
@@ -9413,8 +10384,8 @@ function ensureLocalEvalProjectConfig(projectRoot, configPath) {
|
|
|
9413
10384
|
projectRoot,
|
|
9414
10385
|
resolvedConfigPath
|
|
9415
10386
|
);
|
|
9416
|
-
|
|
9417
|
-
|
|
10387
|
+
mkdirSync4(path14.dirname(localConfigPath), { recursive: true });
|
|
10388
|
+
writeFileSync4(localConfigPath, JSON.stringify(sourceConfig, null, 2), "utf-8");
|
|
9418
10389
|
return localConfigPath;
|
|
9419
10390
|
}
|
|
9420
10391
|
function loadParsedConfig(projectRoot, configPath) {
|
|
@@ -9447,9 +10418,9 @@ function getEmbeddingCostPer1MTokens(embeddingProvider) {
|
|
|
9447
10418
|
}
|
|
9448
10419
|
|
|
9449
10420
|
// src/eval/schema.ts
|
|
9450
|
-
import { readFileSync as
|
|
10421
|
+
import { readFileSync as readFileSync9 } from "fs";
|
|
9451
10422
|
function parseJsonFile(filePath) {
|
|
9452
|
-
const content =
|
|
10423
|
+
const content = readFileSync9(filePath, "utf-8");
|
|
9453
10424
|
try {
|
|
9454
10425
|
return JSON.parse(content);
|
|
9455
10426
|
} catch (error) {
|
|
@@ -9463,23 +10434,23 @@ function isRecord2(value) {
|
|
|
9463
10434
|
function isStringArray3(value) {
|
|
9464
10435
|
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
9465
10436
|
}
|
|
9466
|
-
function asPositiveNumber(value,
|
|
10437
|
+
function asPositiveNumber(value, path27) {
|
|
9467
10438
|
if (typeof value !== "number" || Number.isNaN(value) || value < 0) {
|
|
9468
|
-
throw new Error(`${
|
|
10439
|
+
throw new Error(`${path27} must be a non-negative number`);
|
|
9469
10440
|
}
|
|
9470
10441
|
return value;
|
|
9471
10442
|
}
|
|
9472
|
-
function parseQueryType(value,
|
|
10443
|
+
function parseQueryType(value, path27) {
|
|
9473
10444
|
if (value === "definition" || value === "implementation-intent" || value === "similarity" || value === "keyword-heavy") {
|
|
9474
10445
|
return value;
|
|
9475
10446
|
}
|
|
9476
10447
|
throw new Error(
|
|
9477
|
-
`${
|
|
10448
|
+
`${path27} must be one of: definition, implementation-intent, similarity, keyword-heavy`
|
|
9478
10449
|
);
|
|
9479
10450
|
}
|
|
9480
|
-
function parseExpected(input,
|
|
10451
|
+
function parseExpected(input, path27) {
|
|
9481
10452
|
if (!isRecord2(input)) {
|
|
9482
|
-
throw new Error(`${
|
|
10453
|
+
throw new Error(`${path27} must be an object`);
|
|
9483
10454
|
}
|
|
9484
10455
|
const filePathRaw = input.filePath;
|
|
9485
10456
|
const acceptableFilesRaw = input.acceptableFiles;
|
|
@@ -9488,16 +10459,16 @@ function parseExpected(input, path26) {
|
|
|
9488
10459
|
const filePath = typeof filePathRaw === "string" ? filePathRaw : void 0;
|
|
9489
10460
|
const acceptableFiles = isStringArray3(acceptableFilesRaw) ? acceptableFilesRaw : void 0;
|
|
9490
10461
|
if (!filePath && (!acceptableFiles || acceptableFiles.length === 0)) {
|
|
9491
|
-
throw new Error(`${
|
|
10462
|
+
throw new Error(`${path27} must include either expected.filePath or expected.acceptableFiles`);
|
|
9492
10463
|
}
|
|
9493
10464
|
if (acceptableFilesRaw !== void 0 && !isStringArray3(acceptableFilesRaw)) {
|
|
9494
|
-
throw new Error(`${
|
|
10465
|
+
throw new Error(`${path27}.acceptableFiles must be an array of strings`);
|
|
9495
10466
|
}
|
|
9496
10467
|
if (symbolRaw !== void 0 && typeof symbolRaw !== "string") {
|
|
9497
|
-
throw new Error(`${
|
|
10468
|
+
throw new Error(`${path27}.symbol must be a string when provided`);
|
|
9498
10469
|
}
|
|
9499
10470
|
if (branchRaw !== void 0 && typeof branchRaw !== "string") {
|
|
9500
|
-
throw new Error(`${
|
|
10471
|
+
throw new Error(`${path27}.branch must be a string when provided`);
|
|
9501
10472
|
}
|
|
9502
10473
|
return {
|
|
9503
10474
|
filePath,
|
|
@@ -9507,25 +10478,25 @@ function parseExpected(input, path26) {
|
|
|
9507
10478
|
};
|
|
9508
10479
|
}
|
|
9509
10480
|
function parseQuery(input, index) {
|
|
9510
|
-
const
|
|
10481
|
+
const path27 = `queries[${index}]`;
|
|
9511
10482
|
if (!isRecord2(input)) {
|
|
9512
|
-
throw new Error(`${
|
|
10483
|
+
throw new Error(`${path27} must be an object`);
|
|
9513
10484
|
}
|
|
9514
10485
|
const id = input.id;
|
|
9515
10486
|
const query = input.query;
|
|
9516
10487
|
const queryType = input.queryType;
|
|
9517
10488
|
const expected = input.expected;
|
|
9518
10489
|
if (typeof id !== "string" || id.trim().length === 0) {
|
|
9519
|
-
throw new Error(`${
|
|
10490
|
+
throw new Error(`${path27}.id must be a non-empty string`);
|
|
9520
10491
|
}
|
|
9521
10492
|
if (typeof query !== "string" || query.trim().length === 0) {
|
|
9522
|
-
throw new Error(`${
|
|
10493
|
+
throw new Error(`${path27}.query must be a non-empty string`);
|
|
9523
10494
|
}
|
|
9524
10495
|
return {
|
|
9525
10496
|
id,
|
|
9526
10497
|
query,
|
|
9527
|
-
queryType: parseQueryType(queryType, `${
|
|
9528
|
-
expected: parseExpected(expected, `${
|
|
10498
|
+
queryType: parseQueryType(queryType, `${path27}.queryType`),
|
|
10499
|
+
expected: parseExpected(expected, `${path27}.expected`)
|
|
9529
10500
|
};
|
|
9530
10501
|
}
|
|
9531
10502
|
function parseGoldenDataset(raw, sourceLabel) {
|
|
@@ -9708,13 +10679,13 @@ async function runEvaluation(options) {
|
|
|
9708
10679
|
};
|
|
9709
10680
|
const outputDir = createRunDirectory(toAbsolute(options.projectRoot, options.outputRoot));
|
|
9710
10681
|
const perQueryArtifact = buildPerQueryArtifact(perQuery);
|
|
9711
|
-
writeJson(
|
|
9712
|
-
writeJson(
|
|
10682
|
+
writeJson(path15.join(outputDir, "summary.json"), summary);
|
|
10683
|
+
writeJson(path15.join(outputDir, "per-query.json"), perQueryArtifact);
|
|
9713
10684
|
let comparison;
|
|
9714
10685
|
if (againstPath) {
|
|
9715
10686
|
const baseline = loadSummary(againstPath);
|
|
9716
10687
|
comparison = compareSummaries(summary, baseline, againstPath);
|
|
9717
|
-
writeJson(
|
|
10688
|
+
writeJson(path15.join(outputDir, "compare.json"), comparison);
|
|
9718
10689
|
}
|
|
9719
10690
|
let gate;
|
|
9720
10691
|
if (options.ciMode) {
|
|
@@ -9724,10 +10695,10 @@ async function runEvaluation(options) {
|
|
|
9724
10695
|
const budget = loadBudget(budgetPath);
|
|
9725
10696
|
if (!comparison && budget.baselinePath) {
|
|
9726
10697
|
const resolvedBaseline = toAbsolute(options.projectRoot, budget.baselinePath);
|
|
9727
|
-
if (
|
|
10698
|
+
if (existsSync9(resolvedBaseline)) {
|
|
9728
10699
|
const baselineSummary = loadSummary(resolvedBaseline);
|
|
9729
10700
|
comparison = compareSummaries(summary, baselineSummary, resolvedBaseline);
|
|
9730
|
-
writeJson(
|
|
10701
|
+
writeJson(path15.join(outputDir, "compare.json"), comparison);
|
|
9731
10702
|
} else if (budget.failOnMissingBaseline) {
|
|
9732
10703
|
throw new Error(
|
|
9733
10704
|
`Budget baseline is missing: ${resolvedBaseline}. Set failOnMissingBaseline=false to allow CI run without baseline.`
|
|
@@ -9737,7 +10708,7 @@ async function runEvaluation(options) {
|
|
|
9737
10708
|
gate = evaluateBudgetGate(budget, summary, comparison);
|
|
9738
10709
|
}
|
|
9739
10710
|
const markdown = createSummaryMarkdown(summary, comparison, gate);
|
|
9740
|
-
writeText(
|
|
10711
|
+
writeText(path15.join(outputDir, "summary.md"), markdown);
|
|
9741
10712
|
return { outputDir, summary, perQuery, comparison, gate };
|
|
9742
10713
|
} finally {
|
|
9743
10714
|
await indexer.close();
|
|
@@ -9795,23 +10766,23 @@ async function runSweep(options, sweep) {
|
|
|
9795
10766
|
bestByMrrAt10,
|
|
9796
10767
|
bestByP95Latency
|
|
9797
10768
|
};
|
|
9798
|
-
writeJson(
|
|
10769
|
+
writeJson(path15.join(outputDir, "compare.json"), aggregate);
|
|
9799
10770
|
const md = createSummaryMarkdown(
|
|
9800
10771
|
bestByHitAt5?.summary ?? runs[0].summary,
|
|
9801
10772
|
bestByHitAt5?.comparison,
|
|
9802
10773
|
void 0,
|
|
9803
10774
|
aggregate
|
|
9804
10775
|
);
|
|
9805
|
-
writeText(
|
|
9806
|
-
writeJson(
|
|
10776
|
+
writeText(path15.join(outputDir, "summary.md"), md);
|
|
10777
|
+
writeJson(path15.join(outputDir, "summary.json"), bestByHitAt5?.summary ?? runs[0].summary);
|
|
9807
10778
|
return { outputDir, aggregate };
|
|
9808
10779
|
}
|
|
9809
10780
|
|
|
9810
10781
|
// src/eval/cli.ts
|
|
9811
|
-
import * as
|
|
10782
|
+
import * as path17 from "path";
|
|
9812
10783
|
|
|
9813
10784
|
// src/eval/cli-parser.ts
|
|
9814
|
-
import * as
|
|
10785
|
+
import * as path16 from "path";
|
|
9815
10786
|
function printUsage() {
|
|
9816
10787
|
console.log(`
|
|
9817
10788
|
Usage:
|
|
@@ -9883,12 +10854,12 @@ function parseEvalArgs(argv, cwd) {
|
|
|
9883
10854
|
const arg = argv[i];
|
|
9884
10855
|
const next = argv[i + 1];
|
|
9885
10856
|
if (arg === "--project" && next) {
|
|
9886
|
-
parsed.projectRoot =
|
|
10857
|
+
parsed.projectRoot = path16.resolve(cwd, next);
|
|
9887
10858
|
i += 1;
|
|
9888
10859
|
continue;
|
|
9889
10860
|
}
|
|
9890
10861
|
if (arg === "--config" && next) {
|
|
9891
|
-
parsed.configPath =
|
|
10862
|
+
parsed.configPath = path16.resolve(cwd, next);
|
|
9892
10863
|
i += 1;
|
|
9893
10864
|
continue;
|
|
9894
10865
|
}
|
|
@@ -10084,22 +11055,22 @@ async function handleEvalCommand(args, cwd) {
|
|
|
10084
11055
|
if (!parsed.againstPath.endsWith(".json")) {
|
|
10085
11056
|
throw new Error("eval diff --against must point to a summary JSON file");
|
|
10086
11057
|
}
|
|
10087
|
-
const currentSummary = loadSummary(
|
|
11058
|
+
const currentSummary = loadSummary(path17.resolve(parsed.projectRoot, currentPath), {
|
|
10088
11059
|
allowLegacyDiversityMetrics: true
|
|
10089
11060
|
});
|
|
10090
|
-
const baselineSummary = loadSummary(
|
|
11061
|
+
const baselineSummary = loadSummary(path17.resolve(parsed.projectRoot, parsed.againstPath), {
|
|
10091
11062
|
allowLegacyDiversityMetrics: true
|
|
10092
11063
|
});
|
|
10093
11064
|
const comparison = compareSummaries(
|
|
10094
11065
|
currentSummary,
|
|
10095
11066
|
baselineSummary,
|
|
10096
|
-
|
|
11067
|
+
path17.resolve(parsed.projectRoot, parsed.againstPath)
|
|
10097
11068
|
);
|
|
10098
|
-
const outputDir = createRunDirectory(
|
|
11069
|
+
const outputDir = createRunDirectory(path17.resolve(parsed.projectRoot, parsed.outputRoot));
|
|
10099
11070
|
const summaryMd = createSummaryMarkdown(currentSummary, comparison);
|
|
10100
|
-
writeJson(
|
|
10101
|
-
writeText(
|
|
10102
|
-
writeJson(
|
|
11071
|
+
writeJson(path17.join(outputDir, "compare.json"), comparison);
|
|
11072
|
+
writeText(path17.join(outputDir, "summary.md"), summaryMd);
|
|
11073
|
+
writeJson(path17.join(outputDir, "summary.json"), currentSummary);
|
|
10103
11074
|
console.log(`Eval diff complete. Artifacts: ${outputDir}`);
|
|
10104
11075
|
return 0;
|
|
10105
11076
|
}
|
|
@@ -10108,7 +11079,7 @@ async function handleEvalCommand(args, cwd) {
|
|
|
10108
11079
|
|
|
10109
11080
|
// src/mcp-server.ts
|
|
10110
11081
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
10111
|
-
import { readFileSync as
|
|
11082
|
+
import { readFileSync as readFileSync11 } from "fs";
|
|
10112
11083
|
|
|
10113
11084
|
// src/mcp-server/register-prompts.ts
|
|
10114
11085
|
import { z } from "zod";
|
|
@@ -10299,6 +11270,10 @@ function formatStatus(status) {
|
|
|
10299
11270
|
lines.push(`Failed batches: ${status.failedBatchesPath}`);
|
|
10300
11271
|
}
|
|
10301
11272
|
}
|
|
11273
|
+
if (status.warning) {
|
|
11274
|
+
lines.push("");
|
|
11275
|
+
lines.push(`INDEX WARNING: ${status.warning}`);
|
|
11276
|
+
}
|
|
10302
11277
|
if (status.compatibility && !status.compatibility.compatible) {
|
|
10303
11278
|
lines.push("");
|
|
10304
11279
|
lines.push(`COMPATIBILITY WARNING: ${status.compatibility.reason}`);
|
|
@@ -10404,16 +11379,16 @@ function formatCallGraphCallees(symbolId, callees, relationshipType) {
|
|
|
10404
11379
|
return `[${index + 1}] \u2192 ${edge.targetName} (${edge.callType})${confidence} at line ${edge.line}${edge.isResolved ? ` [resolved: ${edge.toSymbolId}]` : " [unresolved]"}`;
|
|
10405
11380
|
}).join("\n");
|
|
10406
11381
|
}
|
|
10407
|
-
function formatCallGraphPath(from, to,
|
|
10408
|
-
if (
|
|
11382
|
+
function formatCallGraphPath(from, to, path27) {
|
|
11383
|
+
if (path27.length === 0) {
|
|
10409
11384
|
return `No path found between "${from}" and "${to}". They may be in disconnected components, or the call graph index needs updating.`;
|
|
10410
11385
|
}
|
|
10411
|
-
const formatted =
|
|
11386
|
+
const formatted = path27.map((hop, index) => {
|
|
10412
11387
|
const prefix = index === 0 ? "[start]" : `--${hop.callType}-->`;
|
|
10413
11388
|
const location = hop.filePath ? ` (${hop.filePath}:${hop.line})` : "";
|
|
10414
11389
|
return `${prefix} ${hop.symbolName}${location}`;
|
|
10415
11390
|
});
|
|
10416
|
-
return `Path (${
|
|
11391
|
+
return `Path (${path27.length} hops):
|
|
10417
11392
|
${formatted.join("\n")}`;
|
|
10418
11393
|
}
|
|
10419
11394
|
function formatResultHeader(result, index) {
|
|
@@ -10504,12 +11479,12 @@ function formatPrImpact(result) {
|
|
|
10504
11479
|
}
|
|
10505
11480
|
|
|
10506
11481
|
// src/tools/operations.ts
|
|
10507
|
-
import { existsSync as
|
|
10508
|
-
import * as
|
|
11482
|
+
import { existsSync as existsSync12, realpathSync as realpathSync2, statSync as statSync4 } from "fs";
|
|
11483
|
+
import * as path21 from "path";
|
|
10509
11484
|
|
|
10510
11485
|
// src/config/merger.ts
|
|
10511
|
-
import { existsSync as
|
|
10512
|
-
import * as
|
|
11486
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync5, readFileSync as readFileSync10, writeFileSync as writeFileSync5 } from "fs";
|
|
11487
|
+
import * as path18 from "path";
|
|
10513
11488
|
var PROJECT_OVERRIDE_KEYS = [
|
|
10514
11489
|
"embeddingProvider",
|
|
10515
11490
|
"customProvider",
|
|
@@ -10542,8 +11517,8 @@ function mergeUniqueStringArray(values) {
|
|
|
10542
11517
|
return [...new Set(values.map((value) => String(value).trim()))];
|
|
10543
11518
|
}
|
|
10544
11519
|
function normalizeKnowledgeBasePath(value) {
|
|
10545
|
-
let normalized =
|
|
10546
|
-
const root =
|
|
11520
|
+
let normalized = path18.normalize(String(value).trim());
|
|
11521
|
+
const root = path18.parse(normalized).root;
|
|
10547
11522
|
while (normalized.length > root.length && /[\\/]$/.test(normalized)) {
|
|
10548
11523
|
normalized = normalized.slice(0, -1);
|
|
10549
11524
|
}
|
|
@@ -10577,11 +11552,11 @@ function validateConfigLayerShape(rawConfig, filePath) {
|
|
|
10577
11552
|
return rawConfig;
|
|
10578
11553
|
}
|
|
10579
11554
|
function loadJsonFile(filePath) {
|
|
10580
|
-
if (!
|
|
11555
|
+
if (!existsSync10(filePath)) {
|
|
10581
11556
|
return null;
|
|
10582
11557
|
}
|
|
10583
11558
|
try {
|
|
10584
|
-
const content =
|
|
11559
|
+
const content = readFileSync10(filePath, "utf-8");
|
|
10585
11560
|
return validateConfigLayerShape(JSON.parse(content), filePath);
|
|
10586
11561
|
} catch (error) {
|
|
10587
11562
|
if (error instanceof Error && error.message.startsWith("Config file ")) {
|
|
@@ -10596,8 +11571,8 @@ function loadConfigFile(filePath) {
|
|
|
10596
11571
|
}
|
|
10597
11572
|
function materializeLocalProjectConfig(projectRoot, config, host = "opencode") {
|
|
10598
11573
|
const localConfigPath = resolveWritableProjectConfigPath(projectRoot, host);
|
|
10599
|
-
|
|
10600
|
-
|
|
11574
|
+
mkdirSync5(path18.dirname(localConfigPath), { recursive: true });
|
|
11575
|
+
writeFileSync5(localConfigPath, JSON.stringify(config, null, 2), "utf-8");
|
|
10601
11576
|
return localConfigPath;
|
|
10602
11577
|
}
|
|
10603
11578
|
function loadProjectConfigLayer(projectRoot, host = "opencode") {
|
|
@@ -10607,7 +11582,7 @@ function loadProjectConfigLayer(projectRoot, host = "opencode") {
|
|
|
10607
11582
|
return {};
|
|
10608
11583
|
}
|
|
10609
11584
|
const normalizedConfig = { ...projectConfig };
|
|
10610
|
-
const projectConfigBaseDir =
|
|
11585
|
+
const projectConfigBaseDir = path18.dirname(path18.dirname(projectConfigPath));
|
|
10611
11586
|
if (Array.isArray(normalizedConfig.knowledgeBases)) {
|
|
10612
11587
|
normalizedConfig.knowledgeBases = resolveInheritedKnowledgeBaseEntries(
|
|
10613
11588
|
normalizedConfig.knowledgeBases,
|
|
@@ -10671,19 +11646,19 @@ function loadMergedConfig(projectRoot, host = "opencode") {
|
|
|
10671
11646
|
}
|
|
10672
11647
|
|
|
10673
11648
|
// src/tools/knowledge-base-paths.ts
|
|
10674
|
-
import * as
|
|
11649
|
+
import * as path19 from "path";
|
|
10675
11650
|
function resolveConfigPathValue(value, baseDir) {
|
|
10676
11651
|
const trimmed = value.trim();
|
|
10677
11652
|
if (!trimmed) {
|
|
10678
11653
|
return trimmed;
|
|
10679
11654
|
}
|
|
10680
|
-
const absolutePath =
|
|
10681
|
-
return
|
|
11655
|
+
const absolutePath = path19.isAbsolute(trimmed) ? trimmed : path19.resolve(baseDir, trimmed);
|
|
11656
|
+
return path19.normalize(absolutePath);
|
|
10682
11657
|
}
|
|
10683
11658
|
|
|
10684
11659
|
// src/tools/config-state.ts
|
|
10685
|
-
import { existsSync as
|
|
10686
|
-
import * as
|
|
11660
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync6, writeFileSync as writeFileSync6 } from "fs";
|
|
11661
|
+
import * as path20 from "path";
|
|
10687
11662
|
function normalizeKnowledgeBasePaths(config, projectRoot) {
|
|
10688
11663
|
const normalized = { ...config };
|
|
10689
11664
|
if (Array.isArray(normalized.knowledgeBases)) {
|
|
@@ -10707,6 +11682,24 @@ function loadRuntimeConfig(projectRoot, host = "opencode") {
|
|
|
10707
11682
|
var indexerCache = /* @__PURE__ */ new Map();
|
|
10708
11683
|
var configCache = /* @__PURE__ */ new Map();
|
|
10709
11684
|
var defaultProjectRoots = /* @__PURE__ */ new Map();
|
|
11685
|
+
function getIndexBusyResult(error) {
|
|
11686
|
+
if (!isIndexLockContentionError(error)) return null;
|
|
11687
|
+
const owner = error.owner;
|
|
11688
|
+
const ownerText = owner ? `PID ${owner.pid}, operation ${owner.operation}, since ${owner.startedAt}` : "unreadable owner";
|
|
11689
|
+
if (error.reason === "legacy-lock") {
|
|
11690
|
+
return {
|
|
11691
|
+
kind: "busy",
|
|
11692
|
+
text: `INDEX_BUSY: legacy lock format detected (${ownerText}). Verify the PID and remove this lock manually only if it is stale.`
|
|
11693
|
+
};
|
|
11694
|
+
}
|
|
11695
|
+
if (error.reason === "unknown-owner") {
|
|
11696
|
+
return {
|
|
11697
|
+
kind: "busy",
|
|
11698
|
+
text: `INDEX_BUSY: unreadable or remote lock owner (${ownerText}). Automatic recovery was refused; manual verification is required.`
|
|
11699
|
+
};
|
|
11700
|
+
}
|
|
11701
|
+
return { kind: "busy", text: `INDEX_BUSY: another index operation is already in progress (${ownerText}).` };
|
|
11702
|
+
}
|
|
10710
11703
|
function getProjectRoot(projectRoot, host) {
|
|
10711
11704
|
if (projectRoot) {
|
|
10712
11705
|
return projectRoot;
|
|
@@ -10751,8 +11744,8 @@ function refreshIndexerForDirectory(projectRoot, host = "opencode", config = par
|
|
|
10751
11744
|
}
|
|
10752
11745
|
function shouldForceLocalizeProjectIndex(projectRoot, host = "opencode") {
|
|
10753
11746
|
const root = getProjectRoot(projectRoot, host);
|
|
10754
|
-
const localIndexPath =
|
|
10755
|
-
if (
|
|
11747
|
+
const localIndexPath = path21.join(root, getHostProjectIndexRelativePath(host));
|
|
11748
|
+
if (existsSync12(localIndexPath)) {
|
|
10756
11749
|
return false;
|
|
10757
11750
|
}
|
|
10758
11751
|
const inheritedIndexPath = resolveWorktreeFallbackProjectIndexPath(root, host);
|
|
@@ -10808,35 +11801,46 @@ async function getCallGraphPath(projectRoot, host, from, to, maxDepth) {
|
|
|
10808
11801
|
async function runIndexCodebase(projectRoot, host, args, onProgress) {
|
|
10809
11802
|
const root = getProjectRoot(projectRoot, host);
|
|
10810
11803
|
const key = getIndexerCacheKey(root, host);
|
|
10811
|
-
const cachedConfig = configCache.get(key);
|
|
10812
11804
|
let indexer = getIndexerForProject(root, host);
|
|
10813
|
-
|
|
10814
|
-
|
|
10815
|
-
|
|
10816
|
-
|
|
10817
|
-
|
|
10818
|
-
|
|
10819
|
-
|
|
10820
|
-
|
|
10821
|
-
|
|
10822
|
-
|
|
10823
|
-
|
|
10824
|
-
|
|
10825
|
-
|
|
10826
|
-
|
|
10827
|
-
|
|
10828
|
-
|
|
10829
|
-
|
|
10830
|
-
|
|
10831
|
-
|
|
10832
|
-
chunksProcessed: progress.chunksProcessed,
|
|
10833
|
-
totalChunks: progress.totalChunks,
|
|
10834
|
-
percentage: calculatePercentage(progress)
|
|
11805
|
+
const runtimeConfig = configCache.get(key);
|
|
11806
|
+
try {
|
|
11807
|
+
if (args.estimateOnly) {
|
|
11808
|
+
return { kind: "estimate", estimate: await indexer.estimateCost() };
|
|
11809
|
+
}
|
|
11810
|
+
const runIndex = async (target) => {
|
|
11811
|
+
const operation = args.force ? target.forceIndex.bind(target) : target.index.bind(target);
|
|
11812
|
+
return operation((progress) => {
|
|
11813
|
+
if (onProgress) {
|
|
11814
|
+
return onProgress(formatProgressTitle(progress), {
|
|
11815
|
+
phase: progress.phase,
|
|
11816
|
+
filesProcessed: progress.filesProcessed,
|
|
11817
|
+
totalFiles: progress.totalFiles,
|
|
11818
|
+
chunksProcessed: progress.chunksProcessed,
|
|
11819
|
+
totalChunks: progress.totalChunks,
|
|
11820
|
+
percentage: calculatePercentage(progress)
|
|
11821
|
+
});
|
|
11822
|
+
}
|
|
11823
|
+
return Promise.resolve();
|
|
10835
11824
|
});
|
|
11825
|
+
};
|
|
11826
|
+
let stats;
|
|
11827
|
+
if (args.force && shouldForceLocalizeProjectIndex(root, host)) {
|
|
11828
|
+
const inheritedIndexPath = resolveProjectIndexPath(root, runtimeConfig.scope, host);
|
|
11829
|
+
stats = await withIndexLock(inheritedIndexPath, "force-index", async () => {
|
|
11830
|
+
materializeLocalProjectConfig(root, loadProjectConfigLayer(root, host), host);
|
|
11831
|
+
refreshIndexerForDirectory(root, host, runtimeConfig);
|
|
11832
|
+
indexer = getIndexerForProject(root, host);
|
|
11833
|
+
return runIndex(indexer);
|
|
11834
|
+
}, { completeRecoveries: false });
|
|
11835
|
+
} else {
|
|
11836
|
+
stats = await runIndex(indexer);
|
|
10836
11837
|
}
|
|
10837
|
-
return
|
|
10838
|
-
})
|
|
10839
|
-
|
|
11838
|
+
return { kind: "stats", stats };
|
|
11839
|
+
} catch (error) {
|
|
11840
|
+
const busyResult = getIndexBusyResult(error);
|
|
11841
|
+
if (!busyResult) throw error;
|
|
11842
|
+
return busyResult;
|
|
11843
|
+
}
|
|
10840
11844
|
}
|
|
10841
11845
|
async function getIndexStatus(projectRoot, host) {
|
|
10842
11846
|
const indexer = getIndexerForProject(projectRoot, host);
|
|
@@ -10846,6 +11850,15 @@ async function getIndexHealthCheck(projectRoot, host) {
|
|
|
10846
11850
|
const indexer = getIndexerForProject(projectRoot, host);
|
|
10847
11851
|
return indexer.healthCheck();
|
|
10848
11852
|
}
|
|
11853
|
+
async function runIndexHealthCheck(projectRoot, host) {
|
|
11854
|
+
try {
|
|
11855
|
+
return { kind: "health", health: await getIndexHealthCheck(projectRoot, host) };
|
|
11856
|
+
} catch (error) {
|
|
11857
|
+
const busyResult = getIndexBusyResult(error);
|
|
11858
|
+
if (!busyResult) throw error;
|
|
11859
|
+
return busyResult;
|
|
11860
|
+
}
|
|
11861
|
+
}
|
|
10849
11862
|
async function getPrImpact(projectRoot, host, params) {
|
|
10850
11863
|
const indexer = getIndexerForProject(projectRoot, host);
|
|
10851
11864
|
return indexer.getPrImpact({
|
|
@@ -11004,8 +12017,13 @@ Use Read tool to examine specific files.` }] };
|
|
|
11004
12017
|
},
|
|
11005
12018
|
async (args) => {
|
|
11006
12019
|
const result = await runIndexCodebase(runtime.projectRoot, runtime.host, args);
|
|
11007
|
-
|
|
11008
|
-
|
|
12020
|
+
if (result.kind === "estimate") {
|
|
12021
|
+
return { content: [{ type: "text", text: formatCostEstimate(result.estimate) }] };
|
|
12022
|
+
}
|
|
12023
|
+
if (result.kind === "busy") {
|
|
12024
|
+
return { content: [{ type: "text", text: result.text }], isError: true };
|
|
12025
|
+
}
|
|
12026
|
+
return { content: [{ type: "text", text: formatIndexStats(result.stats, args.verbose ?? false) }] };
|
|
11009
12027
|
}
|
|
11010
12028
|
);
|
|
11011
12029
|
server.tool(
|
|
@@ -11022,8 +12040,11 @@ Use Read tool to examine specific files.` }] };
|
|
|
11022
12040
|
"Check index health and remove stale entries from deleted files. Run this to clean up the index after files have been deleted.",
|
|
11023
12041
|
{},
|
|
11024
12042
|
async () => {
|
|
11025
|
-
const result = await
|
|
11026
|
-
|
|
12043
|
+
const result = await runIndexHealthCheck(runtime.projectRoot, runtime.host);
|
|
12044
|
+
if (result.kind === "busy") {
|
|
12045
|
+
return { content: [{ type: "text", text: result.text }], isError: true };
|
|
12046
|
+
}
|
|
12047
|
+
return { content: [{ type: "text", text: formatHealthCheck(result.health) }] };
|
|
11027
12048
|
}
|
|
11028
12049
|
);
|
|
11029
12050
|
server.tool(
|
|
@@ -11123,8 +12144,8 @@ ${formatSearchResults(results)}` }] };
|
|
|
11123
12144
|
maxDepth: z2.number().optional().default(10).describe("Maximum traversal depth (default: 10)")
|
|
11124
12145
|
},
|
|
11125
12146
|
async (args) => {
|
|
11126
|
-
const
|
|
11127
|
-
return { content: [{ type: "text", text: formatCallGraphPath(args.from, args.to,
|
|
12147
|
+
const path27 = await getCallGraphPath(runtime.projectRoot, runtime.host, args.from, args.to, args.maxDepth);
|
|
12148
|
+
return { content: [{ type: "text", text: formatCallGraphPath(args.from, args.to, path27) }] };
|
|
11128
12149
|
}
|
|
11129
12150
|
);
|
|
11130
12151
|
server.tool(
|
|
@@ -11159,7 +12180,7 @@ ${formatSearchResults(results)}` }] };
|
|
|
11159
12180
|
|
|
11160
12181
|
// src/mcp-server.ts
|
|
11161
12182
|
function getPackageVersion() {
|
|
11162
|
-
const raw = JSON.parse(
|
|
12183
|
+
const raw = JSON.parse(readFileSync11(new URL("../package.json", import.meta.url), "utf-8"));
|
|
11163
12184
|
if (raw && typeof raw === "object" && "version" in raw && typeof raw.version === "string") {
|
|
11164
12185
|
return raw.version;
|
|
11165
12186
|
}
|
|
@@ -11180,18 +12201,37 @@ function createMcpServer(projectRoot, config, host = "opencode") {
|
|
|
11180
12201
|
}
|
|
11181
12202
|
|
|
11182
12203
|
// src/utils/auto-index.ts
|
|
12204
|
+
var INITIAL_RETRY_DELAY_MS = 50;
|
|
12205
|
+
var MAX_RETRY_DELAY_MS = 500;
|
|
12206
|
+
var autoIndexStates = /* @__PURE__ */ new WeakMap();
|
|
11183
12207
|
function getErrorMessage3(error) {
|
|
11184
12208
|
return error instanceof Error ? error.message : String(error);
|
|
11185
12209
|
}
|
|
11186
|
-
function
|
|
11187
|
-
indexer.
|
|
11188
|
-
|
|
11189
|
-
console.error(`[codebase-index] Auto-index failed for "${projectRoot}": ${getErrorMessage3(error)}`);
|
|
11190
|
-
});
|
|
12210
|
+
function runAutoIndex(indexer, projectRoot, state) {
|
|
12211
|
+
void indexer.index().then(() => {
|
|
12212
|
+
autoIndexStates.delete(indexer);
|
|
11191
12213
|
}).catch((error) => {
|
|
11192
|
-
|
|
12214
|
+
if (!isTransientIndexLockContention(error)) {
|
|
12215
|
+
autoIndexStates.delete(indexer);
|
|
12216
|
+
console.error(`[codebase-index] Auto-index failed for "${projectRoot}": ${getErrorMessage3(error)}`);
|
|
12217
|
+
return;
|
|
12218
|
+
}
|
|
12219
|
+
const retryDelayMs = state.retryDelayMs;
|
|
12220
|
+
state.retryDelayMs = Math.min(retryDelayMs * 2, MAX_RETRY_DELAY_MS);
|
|
12221
|
+
const retryTimer = setTimeout(() => {
|
|
12222
|
+
runAutoIndex(indexer, projectRoot, state);
|
|
12223
|
+
}, retryDelayMs);
|
|
12224
|
+
retryTimer.unref?.();
|
|
11193
12225
|
});
|
|
11194
12226
|
}
|
|
12227
|
+
function startAutoIndex(indexer, projectRoot) {
|
|
12228
|
+
if (autoIndexStates.has(indexer)) return;
|
|
12229
|
+
const state = {
|
|
12230
|
+
retryDelayMs: INITIAL_RETRY_DELAY_MS
|
|
12231
|
+
};
|
|
12232
|
+
autoIndexStates.set(indexer, state);
|
|
12233
|
+
runAutoIndex(indexer, projectRoot, state);
|
|
12234
|
+
}
|
|
11195
12235
|
|
|
11196
12236
|
// node_modules/chokidar/index.js
|
|
11197
12237
|
import { EventEmitter as EventEmitter2 } from "events";
|
|
@@ -11283,7 +12323,7 @@ var ReaddirpStream = class extends Readable {
|
|
|
11283
12323
|
this._directoryFilter = normalizeFilter(opts.directoryFilter);
|
|
11284
12324
|
const statMethod = opts.lstat ? lstat : stat;
|
|
11285
12325
|
if (wantBigintFsStats) {
|
|
11286
|
-
this._stat = (
|
|
12326
|
+
this._stat = (path27) => statMethod(path27, { bigint: true });
|
|
11287
12327
|
} else {
|
|
11288
12328
|
this._stat = statMethod;
|
|
11289
12329
|
}
|
|
@@ -11308,8 +12348,8 @@ var ReaddirpStream = class extends Readable {
|
|
|
11308
12348
|
const par = this.parent;
|
|
11309
12349
|
const fil = par && par.files;
|
|
11310
12350
|
if (fil && fil.length > 0) {
|
|
11311
|
-
const { path:
|
|
11312
|
-
const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent,
|
|
12351
|
+
const { path: path27, depth } = par;
|
|
12352
|
+
const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path27));
|
|
11313
12353
|
const awaited = await Promise.all(slice);
|
|
11314
12354
|
for (const entry of awaited) {
|
|
11315
12355
|
if (!entry)
|
|
@@ -11349,20 +12389,20 @@ var ReaddirpStream = class extends Readable {
|
|
|
11349
12389
|
this.reading = false;
|
|
11350
12390
|
}
|
|
11351
12391
|
}
|
|
11352
|
-
async _exploreDir(
|
|
12392
|
+
async _exploreDir(path27, depth) {
|
|
11353
12393
|
let files;
|
|
11354
12394
|
try {
|
|
11355
|
-
files = await readdir(
|
|
12395
|
+
files = await readdir(path27, this._rdOptions);
|
|
11356
12396
|
} catch (error) {
|
|
11357
12397
|
this._onError(error);
|
|
11358
12398
|
}
|
|
11359
|
-
return { files, depth, path:
|
|
12399
|
+
return { files, depth, path: path27 };
|
|
11360
12400
|
}
|
|
11361
|
-
async _formatEntry(dirent,
|
|
12401
|
+
async _formatEntry(dirent, path27) {
|
|
11362
12402
|
let entry;
|
|
11363
12403
|
const basename5 = this._isDirent ? dirent.name : dirent;
|
|
11364
12404
|
try {
|
|
11365
|
-
const fullPath = presolve(pjoin(
|
|
12405
|
+
const fullPath = presolve(pjoin(path27, basename5));
|
|
11366
12406
|
entry = { path: prelative(this._root, fullPath), fullPath, basename: basename5 };
|
|
11367
12407
|
entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
|
|
11368
12408
|
} catch (err) {
|
|
@@ -11762,16 +12802,16 @@ var delFromSet = (main2, prop, item) => {
|
|
|
11762
12802
|
};
|
|
11763
12803
|
var isEmptySet = (val) => val instanceof Set ? val.size === 0 : !val;
|
|
11764
12804
|
var FsWatchInstances = /* @__PURE__ */ new Map();
|
|
11765
|
-
function createFsWatchInstance(
|
|
12805
|
+
function createFsWatchInstance(path27, options, listener, errHandler, emitRaw) {
|
|
11766
12806
|
const handleEvent = (rawEvent, evPath) => {
|
|
11767
|
-
listener(
|
|
11768
|
-
emitRaw(rawEvent, evPath, { watchedPath:
|
|
11769
|
-
if (evPath &&
|
|
11770
|
-
fsWatchBroadcast(sp.resolve(
|
|
12807
|
+
listener(path27);
|
|
12808
|
+
emitRaw(rawEvent, evPath, { watchedPath: path27 });
|
|
12809
|
+
if (evPath && path27 !== evPath) {
|
|
12810
|
+
fsWatchBroadcast(sp.resolve(path27, evPath), KEY_LISTENERS, sp.join(path27, evPath));
|
|
11771
12811
|
}
|
|
11772
12812
|
};
|
|
11773
12813
|
try {
|
|
11774
|
-
return fs_watch(
|
|
12814
|
+
return fs_watch(path27, {
|
|
11775
12815
|
persistent: options.persistent
|
|
11776
12816
|
}, handleEvent);
|
|
11777
12817
|
} catch (error) {
|
|
@@ -11787,12 +12827,12 @@ var fsWatchBroadcast = (fullPath, listenerType, val1, val2, val3) => {
|
|
|
11787
12827
|
listener(val1, val2, val3);
|
|
11788
12828
|
});
|
|
11789
12829
|
};
|
|
11790
|
-
var setFsWatchListener = (
|
|
12830
|
+
var setFsWatchListener = (path27, fullPath, options, handlers) => {
|
|
11791
12831
|
const { listener, errHandler, rawEmitter } = handlers;
|
|
11792
12832
|
let cont = FsWatchInstances.get(fullPath);
|
|
11793
12833
|
let watcher;
|
|
11794
12834
|
if (!options.persistent) {
|
|
11795
|
-
watcher = createFsWatchInstance(
|
|
12835
|
+
watcher = createFsWatchInstance(path27, options, listener, errHandler, rawEmitter);
|
|
11796
12836
|
if (!watcher)
|
|
11797
12837
|
return;
|
|
11798
12838
|
return watcher.close.bind(watcher);
|
|
@@ -11803,7 +12843,7 @@ var setFsWatchListener = (path26, fullPath, options, handlers) => {
|
|
|
11803
12843
|
addAndConvert(cont, KEY_RAW, rawEmitter);
|
|
11804
12844
|
} else {
|
|
11805
12845
|
watcher = createFsWatchInstance(
|
|
11806
|
-
|
|
12846
|
+
path27,
|
|
11807
12847
|
options,
|
|
11808
12848
|
fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS),
|
|
11809
12849
|
errHandler,
|
|
@@ -11818,7 +12858,7 @@ var setFsWatchListener = (path26, fullPath, options, handlers) => {
|
|
|
11818
12858
|
cont.watcherUnusable = true;
|
|
11819
12859
|
if (isWindows && error.code === "EPERM") {
|
|
11820
12860
|
try {
|
|
11821
|
-
const fd = await open(
|
|
12861
|
+
const fd = await open(path27, "r");
|
|
11822
12862
|
await fd.close();
|
|
11823
12863
|
broadcastErr(error);
|
|
11824
12864
|
} catch (err) {
|
|
@@ -11849,7 +12889,7 @@ var setFsWatchListener = (path26, fullPath, options, handlers) => {
|
|
|
11849
12889
|
};
|
|
11850
12890
|
};
|
|
11851
12891
|
var FsWatchFileInstances = /* @__PURE__ */ new Map();
|
|
11852
|
-
var setFsWatchFileListener = (
|
|
12892
|
+
var setFsWatchFileListener = (path27, fullPath, options, handlers) => {
|
|
11853
12893
|
const { listener, rawEmitter } = handlers;
|
|
11854
12894
|
let cont = FsWatchFileInstances.get(fullPath);
|
|
11855
12895
|
const copts = cont && cont.options;
|
|
@@ -11871,7 +12911,7 @@ var setFsWatchFileListener = (path26, fullPath, options, handlers) => {
|
|
|
11871
12911
|
});
|
|
11872
12912
|
const currmtime = curr.mtimeMs;
|
|
11873
12913
|
if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {
|
|
11874
|
-
foreach(cont.listeners, (listener2) => listener2(
|
|
12914
|
+
foreach(cont.listeners, (listener2) => listener2(path27, curr));
|
|
11875
12915
|
}
|
|
11876
12916
|
})
|
|
11877
12917
|
};
|
|
@@ -11901,13 +12941,13 @@ var NodeFsHandler = class {
|
|
|
11901
12941
|
* @param listener on fs change
|
|
11902
12942
|
* @returns closer for the watcher instance
|
|
11903
12943
|
*/
|
|
11904
|
-
_watchWithNodeFs(
|
|
12944
|
+
_watchWithNodeFs(path27, listener) {
|
|
11905
12945
|
const opts = this.fsw.options;
|
|
11906
|
-
const directory = sp.dirname(
|
|
11907
|
-
const basename5 = sp.basename(
|
|
12946
|
+
const directory = sp.dirname(path27);
|
|
12947
|
+
const basename5 = sp.basename(path27);
|
|
11908
12948
|
const parent = this.fsw._getWatchedDir(directory);
|
|
11909
12949
|
parent.add(basename5);
|
|
11910
|
-
const absolutePath = sp.resolve(
|
|
12950
|
+
const absolutePath = sp.resolve(path27);
|
|
11911
12951
|
const options = {
|
|
11912
12952
|
persistent: opts.persistent
|
|
11913
12953
|
};
|
|
@@ -11917,12 +12957,12 @@ var NodeFsHandler = class {
|
|
|
11917
12957
|
if (opts.usePolling) {
|
|
11918
12958
|
const enableBin = opts.interval !== opts.binaryInterval;
|
|
11919
12959
|
options.interval = enableBin && isBinaryPath(basename5) ? opts.binaryInterval : opts.interval;
|
|
11920
|
-
closer = setFsWatchFileListener(
|
|
12960
|
+
closer = setFsWatchFileListener(path27, absolutePath, options, {
|
|
11921
12961
|
listener,
|
|
11922
12962
|
rawEmitter: this.fsw._emitRaw
|
|
11923
12963
|
});
|
|
11924
12964
|
} else {
|
|
11925
|
-
closer = setFsWatchListener(
|
|
12965
|
+
closer = setFsWatchListener(path27, absolutePath, options, {
|
|
11926
12966
|
listener,
|
|
11927
12967
|
errHandler: this._boundHandleError,
|
|
11928
12968
|
rawEmitter: this.fsw._emitRaw
|
|
@@ -11944,7 +12984,7 @@ var NodeFsHandler = class {
|
|
|
11944
12984
|
let prevStats = stats;
|
|
11945
12985
|
if (parent.has(basename5))
|
|
11946
12986
|
return;
|
|
11947
|
-
const listener = async (
|
|
12987
|
+
const listener = async (path27, newStats) => {
|
|
11948
12988
|
if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5))
|
|
11949
12989
|
return;
|
|
11950
12990
|
if (!newStats || newStats.mtimeMs === 0) {
|
|
@@ -11958,11 +12998,11 @@ var NodeFsHandler = class {
|
|
|
11958
12998
|
this.fsw._emit(EV.CHANGE, file, newStats2);
|
|
11959
12999
|
}
|
|
11960
13000
|
if ((isMacos || isLinux || isFreeBSD) && prevStats.ino !== newStats2.ino) {
|
|
11961
|
-
this.fsw._closeFile(
|
|
13001
|
+
this.fsw._closeFile(path27);
|
|
11962
13002
|
prevStats = newStats2;
|
|
11963
13003
|
const closer2 = this._watchWithNodeFs(file, listener);
|
|
11964
13004
|
if (closer2)
|
|
11965
|
-
this.fsw._addPathCloser(
|
|
13005
|
+
this.fsw._addPathCloser(path27, closer2);
|
|
11966
13006
|
} else {
|
|
11967
13007
|
prevStats = newStats2;
|
|
11968
13008
|
}
|
|
@@ -11994,7 +13034,7 @@ var NodeFsHandler = class {
|
|
|
11994
13034
|
* @param item basename of this item
|
|
11995
13035
|
* @returns true if no more processing is needed for this entry.
|
|
11996
13036
|
*/
|
|
11997
|
-
async _handleSymlink(entry, directory,
|
|
13037
|
+
async _handleSymlink(entry, directory, path27, item) {
|
|
11998
13038
|
if (this.fsw.closed) {
|
|
11999
13039
|
return;
|
|
12000
13040
|
}
|
|
@@ -12004,7 +13044,7 @@ var NodeFsHandler = class {
|
|
|
12004
13044
|
this.fsw._incrReadyCount();
|
|
12005
13045
|
let linkPath;
|
|
12006
13046
|
try {
|
|
12007
|
-
linkPath = await fsrealpath(
|
|
13047
|
+
linkPath = await fsrealpath(path27);
|
|
12008
13048
|
} catch (e) {
|
|
12009
13049
|
this.fsw._emitReady();
|
|
12010
13050
|
return true;
|
|
@@ -12014,12 +13054,12 @@ var NodeFsHandler = class {
|
|
|
12014
13054
|
if (dir.has(item)) {
|
|
12015
13055
|
if (this.fsw._symlinkPaths.get(full) !== linkPath) {
|
|
12016
13056
|
this.fsw._symlinkPaths.set(full, linkPath);
|
|
12017
|
-
this.fsw._emit(EV.CHANGE,
|
|
13057
|
+
this.fsw._emit(EV.CHANGE, path27, entry.stats);
|
|
12018
13058
|
}
|
|
12019
13059
|
} else {
|
|
12020
13060
|
dir.add(item);
|
|
12021
13061
|
this.fsw._symlinkPaths.set(full, linkPath);
|
|
12022
|
-
this.fsw._emit(EV.ADD,
|
|
13062
|
+
this.fsw._emit(EV.ADD, path27, entry.stats);
|
|
12023
13063
|
}
|
|
12024
13064
|
this.fsw._emitReady();
|
|
12025
13065
|
return true;
|
|
@@ -12049,9 +13089,9 @@ var NodeFsHandler = class {
|
|
|
12049
13089
|
return;
|
|
12050
13090
|
}
|
|
12051
13091
|
const item = entry.path;
|
|
12052
|
-
let
|
|
13092
|
+
let path27 = sp.join(directory, item);
|
|
12053
13093
|
current.add(item);
|
|
12054
|
-
if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory,
|
|
13094
|
+
if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path27, item)) {
|
|
12055
13095
|
return;
|
|
12056
13096
|
}
|
|
12057
13097
|
if (this.fsw.closed) {
|
|
@@ -12060,8 +13100,8 @@ var NodeFsHandler = class {
|
|
|
12060
13100
|
}
|
|
12061
13101
|
if (item === target || !target && !previous.has(item)) {
|
|
12062
13102
|
this.fsw._incrReadyCount();
|
|
12063
|
-
|
|
12064
|
-
this._addToNodeFs(
|
|
13103
|
+
path27 = sp.join(dir, sp.relative(dir, path27));
|
|
13104
|
+
this._addToNodeFs(path27, initialAdd, wh, depth + 1);
|
|
12065
13105
|
}
|
|
12066
13106
|
}).on(EV.ERROR, this._boundHandleError);
|
|
12067
13107
|
return new Promise((resolve15, reject) => {
|
|
@@ -12130,13 +13170,13 @@ var NodeFsHandler = class {
|
|
|
12130
13170
|
* @param depth Child path actually targeted for watch
|
|
12131
13171
|
* @param target Child path actually targeted for watch
|
|
12132
13172
|
*/
|
|
12133
|
-
async _addToNodeFs(
|
|
13173
|
+
async _addToNodeFs(path27, initialAdd, priorWh, depth, target) {
|
|
12134
13174
|
const ready = this.fsw._emitReady;
|
|
12135
|
-
if (this.fsw._isIgnored(
|
|
13175
|
+
if (this.fsw._isIgnored(path27) || this.fsw.closed) {
|
|
12136
13176
|
ready();
|
|
12137
13177
|
return false;
|
|
12138
13178
|
}
|
|
12139
|
-
const wh = this.fsw._getWatchHelpers(
|
|
13179
|
+
const wh = this.fsw._getWatchHelpers(path27);
|
|
12140
13180
|
if (priorWh) {
|
|
12141
13181
|
wh.filterPath = (entry) => priorWh.filterPath(entry);
|
|
12142
13182
|
wh.filterDir = (entry) => priorWh.filterDir(entry);
|
|
@@ -12152,8 +13192,8 @@ var NodeFsHandler = class {
|
|
|
12152
13192
|
const follow = this.fsw.options.followSymlinks;
|
|
12153
13193
|
let closer;
|
|
12154
13194
|
if (stats.isDirectory()) {
|
|
12155
|
-
const absPath = sp.resolve(
|
|
12156
|
-
const targetPath = follow ? await fsrealpath(
|
|
13195
|
+
const absPath = sp.resolve(path27);
|
|
13196
|
+
const targetPath = follow ? await fsrealpath(path27) : path27;
|
|
12157
13197
|
if (this.fsw.closed)
|
|
12158
13198
|
return;
|
|
12159
13199
|
closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
|
|
@@ -12163,29 +13203,29 @@ var NodeFsHandler = class {
|
|
|
12163
13203
|
this.fsw._symlinkPaths.set(absPath, targetPath);
|
|
12164
13204
|
}
|
|
12165
13205
|
} else if (stats.isSymbolicLink()) {
|
|
12166
|
-
const targetPath = follow ? await fsrealpath(
|
|
13206
|
+
const targetPath = follow ? await fsrealpath(path27) : path27;
|
|
12167
13207
|
if (this.fsw.closed)
|
|
12168
13208
|
return;
|
|
12169
13209
|
const parent = sp.dirname(wh.watchPath);
|
|
12170
13210
|
this.fsw._getWatchedDir(parent).add(wh.watchPath);
|
|
12171
13211
|
this.fsw._emit(EV.ADD, wh.watchPath, stats);
|
|
12172
|
-
closer = await this._handleDir(parent, stats, initialAdd, depth,
|
|
13212
|
+
closer = await this._handleDir(parent, stats, initialAdd, depth, path27, wh, targetPath);
|
|
12173
13213
|
if (this.fsw.closed)
|
|
12174
13214
|
return;
|
|
12175
13215
|
if (targetPath !== void 0) {
|
|
12176
|
-
this.fsw._symlinkPaths.set(sp.resolve(
|
|
13216
|
+
this.fsw._symlinkPaths.set(sp.resolve(path27), targetPath);
|
|
12177
13217
|
}
|
|
12178
13218
|
} else {
|
|
12179
13219
|
closer = this._handleFile(wh.watchPath, stats, initialAdd);
|
|
12180
13220
|
}
|
|
12181
13221
|
ready();
|
|
12182
13222
|
if (closer)
|
|
12183
|
-
this.fsw._addPathCloser(
|
|
13223
|
+
this.fsw._addPathCloser(path27, closer);
|
|
12184
13224
|
return false;
|
|
12185
13225
|
} catch (error) {
|
|
12186
13226
|
if (this.fsw._handleError(error)) {
|
|
12187
13227
|
ready();
|
|
12188
|
-
return
|
|
13228
|
+
return path27;
|
|
12189
13229
|
}
|
|
12190
13230
|
}
|
|
12191
13231
|
}
|
|
@@ -12228,24 +13268,24 @@ function createPattern(matcher) {
|
|
|
12228
13268
|
}
|
|
12229
13269
|
return () => false;
|
|
12230
13270
|
}
|
|
12231
|
-
function normalizePath2(
|
|
12232
|
-
if (typeof
|
|
13271
|
+
function normalizePath2(path27) {
|
|
13272
|
+
if (typeof path27 !== "string")
|
|
12233
13273
|
throw new Error("string expected");
|
|
12234
|
-
|
|
12235
|
-
|
|
13274
|
+
path27 = sp2.normalize(path27);
|
|
13275
|
+
path27 = path27.replace(/\\/g, "/");
|
|
12236
13276
|
let prepend = false;
|
|
12237
|
-
if (
|
|
13277
|
+
if (path27.startsWith("//"))
|
|
12238
13278
|
prepend = true;
|
|
12239
|
-
|
|
13279
|
+
path27 = path27.replace(DOUBLE_SLASH_RE, "/");
|
|
12240
13280
|
if (prepend)
|
|
12241
|
-
|
|
12242
|
-
return
|
|
13281
|
+
path27 = "/" + path27;
|
|
13282
|
+
return path27;
|
|
12243
13283
|
}
|
|
12244
13284
|
function matchPatterns(patterns, testString, stats) {
|
|
12245
|
-
const
|
|
13285
|
+
const path27 = normalizePath2(testString);
|
|
12246
13286
|
for (let index = 0; index < patterns.length; index++) {
|
|
12247
13287
|
const pattern = patterns[index];
|
|
12248
|
-
if (pattern(
|
|
13288
|
+
if (pattern(path27, stats)) {
|
|
12249
13289
|
return true;
|
|
12250
13290
|
}
|
|
12251
13291
|
}
|
|
@@ -12283,19 +13323,19 @@ var toUnix = (string) => {
|
|
|
12283
13323
|
}
|
|
12284
13324
|
return str;
|
|
12285
13325
|
};
|
|
12286
|
-
var normalizePathToUnix = (
|
|
12287
|
-
var normalizeIgnored = (cwd = "") => (
|
|
12288
|
-
if (typeof
|
|
12289
|
-
return normalizePathToUnix(sp2.isAbsolute(
|
|
13326
|
+
var normalizePathToUnix = (path27) => toUnix(sp2.normalize(toUnix(path27)));
|
|
13327
|
+
var normalizeIgnored = (cwd = "") => (path27) => {
|
|
13328
|
+
if (typeof path27 === "string") {
|
|
13329
|
+
return normalizePathToUnix(sp2.isAbsolute(path27) ? path27 : sp2.join(cwd, path27));
|
|
12290
13330
|
} else {
|
|
12291
|
-
return
|
|
13331
|
+
return path27;
|
|
12292
13332
|
}
|
|
12293
13333
|
};
|
|
12294
|
-
var getAbsolutePath = (
|
|
12295
|
-
if (sp2.isAbsolute(
|
|
12296
|
-
return
|
|
13334
|
+
var getAbsolutePath = (path27, cwd) => {
|
|
13335
|
+
if (sp2.isAbsolute(path27)) {
|
|
13336
|
+
return path27;
|
|
12297
13337
|
}
|
|
12298
|
-
return sp2.join(cwd,
|
|
13338
|
+
return sp2.join(cwd, path27);
|
|
12299
13339
|
};
|
|
12300
13340
|
var EMPTY_SET = Object.freeze(/* @__PURE__ */ new Set());
|
|
12301
13341
|
var DirEntry = class {
|
|
@@ -12360,10 +13400,10 @@ var WatchHelper = class {
|
|
|
12360
13400
|
dirParts;
|
|
12361
13401
|
followSymlinks;
|
|
12362
13402
|
statMethod;
|
|
12363
|
-
constructor(
|
|
13403
|
+
constructor(path27, follow, fsw) {
|
|
12364
13404
|
this.fsw = fsw;
|
|
12365
|
-
const watchPath =
|
|
12366
|
-
this.path =
|
|
13405
|
+
const watchPath = path27;
|
|
13406
|
+
this.path = path27 = path27.replace(REPLACER_RE, "");
|
|
12367
13407
|
this.watchPath = watchPath;
|
|
12368
13408
|
this.fullWatchPath = sp2.resolve(watchPath);
|
|
12369
13409
|
this.dirParts = [];
|
|
@@ -12503,20 +13543,20 @@ var FSWatcher = class extends EventEmitter2 {
|
|
|
12503
13543
|
this._closePromise = void 0;
|
|
12504
13544
|
let paths = unifyPaths(paths_);
|
|
12505
13545
|
if (cwd) {
|
|
12506
|
-
paths = paths.map((
|
|
12507
|
-
const absPath = getAbsolutePath(
|
|
13546
|
+
paths = paths.map((path27) => {
|
|
13547
|
+
const absPath = getAbsolutePath(path27, cwd);
|
|
12508
13548
|
return absPath;
|
|
12509
13549
|
});
|
|
12510
13550
|
}
|
|
12511
|
-
paths.forEach((
|
|
12512
|
-
this._removeIgnoredPath(
|
|
13551
|
+
paths.forEach((path27) => {
|
|
13552
|
+
this._removeIgnoredPath(path27);
|
|
12513
13553
|
});
|
|
12514
13554
|
this._userIgnored = void 0;
|
|
12515
13555
|
if (!this._readyCount)
|
|
12516
13556
|
this._readyCount = 0;
|
|
12517
13557
|
this._readyCount += paths.length;
|
|
12518
|
-
Promise.all(paths.map(async (
|
|
12519
|
-
const res = await this._nodeFsHandler._addToNodeFs(
|
|
13558
|
+
Promise.all(paths.map(async (path27) => {
|
|
13559
|
+
const res = await this._nodeFsHandler._addToNodeFs(path27, !_internal, void 0, 0, _origAdd);
|
|
12520
13560
|
if (res)
|
|
12521
13561
|
this._emitReady();
|
|
12522
13562
|
return res;
|
|
@@ -12538,17 +13578,17 @@ var FSWatcher = class extends EventEmitter2 {
|
|
|
12538
13578
|
return this;
|
|
12539
13579
|
const paths = unifyPaths(paths_);
|
|
12540
13580
|
const { cwd } = this.options;
|
|
12541
|
-
paths.forEach((
|
|
12542
|
-
if (!sp2.isAbsolute(
|
|
13581
|
+
paths.forEach((path27) => {
|
|
13582
|
+
if (!sp2.isAbsolute(path27) && !this._closers.has(path27)) {
|
|
12543
13583
|
if (cwd)
|
|
12544
|
-
|
|
12545
|
-
|
|
13584
|
+
path27 = sp2.join(cwd, path27);
|
|
13585
|
+
path27 = sp2.resolve(path27);
|
|
12546
13586
|
}
|
|
12547
|
-
this._closePath(
|
|
12548
|
-
this._addIgnoredPath(
|
|
12549
|
-
if (this._watched.has(
|
|
13587
|
+
this._closePath(path27);
|
|
13588
|
+
this._addIgnoredPath(path27);
|
|
13589
|
+
if (this._watched.has(path27)) {
|
|
12550
13590
|
this._addIgnoredPath({
|
|
12551
|
-
path:
|
|
13591
|
+
path: path27,
|
|
12552
13592
|
recursive: true
|
|
12553
13593
|
});
|
|
12554
13594
|
}
|
|
@@ -12612,38 +13652,38 @@ var FSWatcher = class extends EventEmitter2 {
|
|
|
12612
13652
|
* @param stats arguments to be passed with event
|
|
12613
13653
|
* @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag
|
|
12614
13654
|
*/
|
|
12615
|
-
async _emit(event,
|
|
13655
|
+
async _emit(event, path27, stats) {
|
|
12616
13656
|
if (this.closed)
|
|
12617
13657
|
return;
|
|
12618
13658
|
const opts = this.options;
|
|
12619
13659
|
if (isWindows)
|
|
12620
|
-
|
|
13660
|
+
path27 = sp2.normalize(path27);
|
|
12621
13661
|
if (opts.cwd)
|
|
12622
|
-
|
|
12623
|
-
const args = [
|
|
13662
|
+
path27 = sp2.relative(opts.cwd, path27);
|
|
13663
|
+
const args = [path27];
|
|
12624
13664
|
if (stats != null)
|
|
12625
13665
|
args.push(stats);
|
|
12626
13666
|
const awf = opts.awaitWriteFinish;
|
|
12627
13667
|
let pw;
|
|
12628
|
-
if (awf && (pw = this._pendingWrites.get(
|
|
13668
|
+
if (awf && (pw = this._pendingWrites.get(path27))) {
|
|
12629
13669
|
pw.lastChange = /* @__PURE__ */ new Date();
|
|
12630
13670
|
return this;
|
|
12631
13671
|
}
|
|
12632
13672
|
if (opts.atomic) {
|
|
12633
13673
|
if (event === EVENTS.UNLINK) {
|
|
12634
|
-
this._pendingUnlinks.set(
|
|
13674
|
+
this._pendingUnlinks.set(path27, [event, ...args]);
|
|
12635
13675
|
setTimeout(() => {
|
|
12636
|
-
this._pendingUnlinks.forEach((entry,
|
|
13676
|
+
this._pendingUnlinks.forEach((entry, path28) => {
|
|
12637
13677
|
this.emit(...entry);
|
|
12638
13678
|
this.emit(EVENTS.ALL, ...entry);
|
|
12639
|
-
this._pendingUnlinks.delete(
|
|
13679
|
+
this._pendingUnlinks.delete(path28);
|
|
12640
13680
|
});
|
|
12641
13681
|
}, typeof opts.atomic === "number" ? opts.atomic : 100);
|
|
12642
13682
|
return this;
|
|
12643
13683
|
}
|
|
12644
|
-
if (event === EVENTS.ADD && this._pendingUnlinks.has(
|
|
13684
|
+
if (event === EVENTS.ADD && this._pendingUnlinks.has(path27)) {
|
|
12645
13685
|
event = EVENTS.CHANGE;
|
|
12646
|
-
this._pendingUnlinks.delete(
|
|
13686
|
+
this._pendingUnlinks.delete(path27);
|
|
12647
13687
|
}
|
|
12648
13688
|
}
|
|
12649
13689
|
if (awf && (event === EVENTS.ADD || event === EVENTS.CHANGE) && this._readyEmitted) {
|
|
@@ -12661,16 +13701,16 @@ var FSWatcher = class extends EventEmitter2 {
|
|
|
12661
13701
|
this.emitWithAll(event, args);
|
|
12662
13702
|
}
|
|
12663
13703
|
};
|
|
12664
|
-
this._awaitWriteFinish(
|
|
13704
|
+
this._awaitWriteFinish(path27, awf.stabilityThreshold, event, awfEmit);
|
|
12665
13705
|
return this;
|
|
12666
13706
|
}
|
|
12667
13707
|
if (event === EVENTS.CHANGE) {
|
|
12668
|
-
const isThrottled = !this._throttle(EVENTS.CHANGE,
|
|
13708
|
+
const isThrottled = !this._throttle(EVENTS.CHANGE, path27, 50);
|
|
12669
13709
|
if (isThrottled)
|
|
12670
13710
|
return this;
|
|
12671
13711
|
}
|
|
12672
13712
|
if (opts.alwaysStat && stats === void 0 && (event === EVENTS.ADD || event === EVENTS.ADD_DIR || event === EVENTS.CHANGE)) {
|
|
12673
|
-
const fullPath = opts.cwd ? sp2.join(opts.cwd,
|
|
13713
|
+
const fullPath = opts.cwd ? sp2.join(opts.cwd, path27) : path27;
|
|
12674
13714
|
let stats2;
|
|
12675
13715
|
try {
|
|
12676
13716
|
stats2 = await stat3(fullPath);
|
|
@@ -12701,23 +13741,23 @@ var FSWatcher = class extends EventEmitter2 {
|
|
|
12701
13741
|
* @param timeout duration of time to suppress duplicate actions
|
|
12702
13742
|
* @returns tracking object or false if action should be suppressed
|
|
12703
13743
|
*/
|
|
12704
|
-
_throttle(actionType,
|
|
13744
|
+
_throttle(actionType, path27, timeout) {
|
|
12705
13745
|
if (!this._throttled.has(actionType)) {
|
|
12706
13746
|
this._throttled.set(actionType, /* @__PURE__ */ new Map());
|
|
12707
13747
|
}
|
|
12708
13748
|
const action = this._throttled.get(actionType);
|
|
12709
13749
|
if (!action)
|
|
12710
13750
|
throw new Error("invalid throttle");
|
|
12711
|
-
const actionPath = action.get(
|
|
13751
|
+
const actionPath = action.get(path27);
|
|
12712
13752
|
if (actionPath) {
|
|
12713
13753
|
actionPath.count++;
|
|
12714
13754
|
return false;
|
|
12715
13755
|
}
|
|
12716
13756
|
let timeoutObject;
|
|
12717
13757
|
const clear = () => {
|
|
12718
|
-
const item = action.get(
|
|
13758
|
+
const item = action.get(path27);
|
|
12719
13759
|
const count = item ? item.count : 0;
|
|
12720
|
-
action.delete(
|
|
13760
|
+
action.delete(path27);
|
|
12721
13761
|
clearTimeout(timeoutObject);
|
|
12722
13762
|
if (item)
|
|
12723
13763
|
clearTimeout(item.timeoutObject);
|
|
@@ -12725,7 +13765,7 @@ var FSWatcher = class extends EventEmitter2 {
|
|
|
12725
13765
|
};
|
|
12726
13766
|
timeoutObject = setTimeout(clear, timeout);
|
|
12727
13767
|
const thr = { timeoutObject, clear, count: 0 };
|
|
12728
|
-
action.set(
|
|
13768
|
+
action.set(path27, thr);
|
|
12729
13769
|
return thr;
|
|
12730
13770
|
}
|
|
12731
13771
|
_incrReadyCount() {
|
|
@@ -12739,44 +13779,44 @@ var FSWatcher = class extends EventEmitter2 {
|
|
|
12739
13779
|
* @param event
|
|
12740
13780
|
* @param awfEmit Callback to be called when ready for event to be emitted.
|
|
12741
13781
|
*/
|
|
12742
|
-
_awaitWriteFinish(
|
|
13782
|
+
_awaitWriteFinish(path27, threshold, event, awfEmit) {
|
|
12743
13783
|
const awf = this.options.awaitWriteFinish;
|
|
12744
13784
|
if (typeof awf !== "object")
|
|
12745
13785
|
return;
|
|
12746
13786
|
const pollInterval = awf.pollInterval;
|
|
12747
13787
|
let timeoutHandler;
|
|
12748
|
-
let fullPath =
|
|
12749
|
-
if (this.options.cwd && !sp2.isAbsolute(
|
|
12750
|
-
fullPath = sp2.join(this.options.cwd,
|
|
13788
|
+
let fullPath = path27;
|
|
13789
|
+
if (this.options.cwd && !sp2.isAbsolute(path27)) {
|
|
13790
|
+
fullPath = sp2.join(this.options.cwd, path27);
|
|
12751
13791
|
}
|
|
12752
13792
|
const now = /* @__PURE__ */ new Date();
|
|
12753
13793
|
const writes = this._pendingWrites;
|
|
12754
13794
|
function awaitWriteFinishFn(prevStat) {
|
|
12755
13795
|
statcb(fullPath, (err, curStat) => {
|
|
12756
|
-
if (err || !writes.has(
|
|
13796
|
+
if (err || !writes.has(path27)) {
|
|
12757
13797
|
if (err && err.code !== "ENOENT")
|
|
12758
13798
|
awfEmit(err);
|
|
12759
13799
|
return;
|
|
12760
13800
|
}
|
|
12761
13801
|
const now2 = Number(/* @__PURE__ */ new Date());
|
|
12762
13802
|
if (prevStat && curStat.size !== prevStat.size) {
|
|
12763
|
-
writes.get(
|
|
13803
|
+
writes.get(path27).lastChange = now2;
|
|
12764
13804
|
}
|
|
12765
|
-
const pw = writes.get(
|
|
13805
|
+
const pw = writes.get(path27);
|
|
12766
13806
|
const df = now2 - pw.lastChange;
|
|
12767
13807
|
if (df >= threshold) {
|
|
12768
|
-
writes.delete(
|
|
13808
|
+
writes.delete(path27);
|
|
12769
13809
|
awfEmit(void 0, curStat);
|
|
12770
13810
|
} else {
|
|
12771
13811
|
timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);
|
|
12772
13812
|
}
|
|
12773
13813
|
});
|
|
12774
13814
|
}
|
|
12775
|
-
if (!writes.has(
|
|
12776
|
-
writes.set(
|
|
13815
|
+
if (!writes.has(path27)) {
|
|
13816
|
+
writes.set(path27, {
|
|
12777
13817
|
lastChange: now,
|
|
12778
13818
|
cancelWait: () => {
|
|
12779
|
-
writes.delete(
|
|
13819
|
+
writes.delete(path27);
|
|
12780
13820
|
clearTimeout(timeoutHandler);
|
|
12781
13821
|
return event;
|
|
12782
13822
|
}
|
|
@@ -12787,8 +13827,8 @@ var FSWatcher = class extends EventEmitter2 {
|
|
|
12787
13827
|
/**
|
|
12788
13828
|
* Determines whether user has asked to ignore this path.
|
|
12789
13829
|
*/
|
|
12790
|
-
_isIgnored(
|
|
12791
|
-
if (this.options.atomic && DOT_RE.test(
|
|
13830
|
+
_isIgnored(path27, stats) {
|
|
13831
|
+
if (this.options.atomic && DOT_RE.test(path27))
|
|
12792
13832
|
return true;
|
|
12793
13833
|
if (!this._userIgnored) {
|
|
12794
13834
|
const { cwd } = this.options;
|
|
@@ -12798,17 +13838,17 @@ var FSWatcher = class extends EventEmitter2 {
|
|
|
12798
13838
|
const list = [...ignoredPaths.map(normalizeIgnored(cwd)), ...ignored];
|
|
12799
13839
|
this._userIgnored = anymatch(list, void 0);
|
|
12800
13840
|
}
|
|
12801
|
-
return this._userIgnored(
|
|
13841
|
+
return this._userIgnored(path27, stats);
|
|
12802
13842
|
}
|
|
12803
|
-
_isntIgnored(
|
|
12804
|
-
return !this._isIgnored(
|
|
13843
|
+
_isntIgnored(path27, stat4) {
|
|
13844
|
+
return !this._isIgnored(path27, stat4);
|
|
12805
13845
|
}
|
|
12806
13846
|
/**
|
|
12807
13847
|
* Provides a set of common helpers and properties relating to symlink handling.
|
|
12808
13848
|
* @param path file or directory pattern being watched
|
|
12809
13849
|
*/
|
|
12810
|
-
_getWatchHelpers(
|
|
12811
|
-
return new WatchHelper(
|
|
13850
|
+
_getWatchHelpers(path27) {
|
|
13851
|
+
return new WatchHelper(path27, this.options.followSymlinks, this);
|
|
12812
13852
|
}
|
|
12813
13853
|
// Directory helpers
|
|
12814
13854
|
// -----------------
|
|
@@ -12840,63 +13880,63 @@ var FSWatcher = class extends EventEmitter2 {
|
|
|
12840
13880
|
* @param item base path of item/directory
|
|
12841
13881
|
*/
|
|
12842
13882
|
_remove(directory, item, isDirectory) {
|
|
12843
|
-
const
|
|
12844
|
-
const fullPath = sp2.resolve(
|
|
12845
|
-
isDirectory = isDirectory != null ? isDirectory : this._watched.has(
|
|
12846
|
-
if (!this._throttle("remove",
|
|
13883
|
+
const path27 = sp2.join(directory, item);
|
|
13884
|
+
const fullPath = sp2.resolve(path27);
|
|
13885
|
+
isDirectory = isDirectory != null ? isDirectory : this._watched.has(path27) || this._watched.has(fullPath);
|
|
13886
|
+
if (!this._throttle("remove", path27, 100))
|
|
12847
13887
|
return;
|
|
12848
13888
|
if (!isDirectory && this._watched.size === 1) {
|
|
12849
13889
|
this.add(directory, item, true);
|
|
12850
13890
|
}
|
|
12851
|
-
const wp = this._getWatchedDir(
|
|
13891
|
+
const wp = this._getWatchedDir(path27);
|
|
12852
13892
|
const nestedDirectoryChildren = wp.getChildren();
|
|
12853
|
-
nestedDirectoryChildren.forEach((nested) => this._remove(
|
|
13893
|
+
nestedDirectoryChildren.forEach((nested) => this._remove(path27, nested));
|
|
12854
13894
|
const parent = this._getWatchedDir(directory);
|
|
12855
13895
|
const wasTracked = parent.has(item);
|
|
12856
13896
|
parent.remove(item);
|
|
12857
13897
|
if (this._symlinkPaths.has(fullPath)) {
|
|
12858
13898
|
this._symlinkPaths.delete(fullPath);
|
|
12859
13899
|
}
|
|
12860
|
-
let relPath =
|
|
13900
|
+
let relPath = path27;
|
|
12861
13901
|
if (this.options.cwd)
|
|
12862
|
-
relPath = sp2.relative(this.options.cwd,
|
|
13902
|
+
relPath = sp2.relative(this.options.cwd, path27);
|
|
12863
13903
|
if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
|
|
12864
13904
|
const event = this._pendingWrites.get(relPath).cancelWait();
|
|
12865
13905
|
if (event === EVENTS.ADD)
|
|
12866
13906
|
return;
|
|
12867
13907
|
}
|
|
12868
|
-
this._watched.delete(
|
|
13908
|
+
this._watched.delete(path27);
|
|
12869
13909
|
this._watched.delete(fullPath);
|
|
12870
13910
|
const eventName = isDirectory ? EVENTS.UNLINK_DIR : EVENTS.UNLINK;
|
|
12871
|
-
if (wasTracked && !this._isIgnored(
|
|
12872
|
-
this._emit(eventName,
|
|
12873
|
-
this._closePath(
|
|
13911
|
+
if (wasTracked && !this._isIgnored(path27))
|
|
13912
|
+
this._emit(eventName, path27);
|
|
13913
|
+
this._closePath(path27);
|
|
12874
13914
|
}
|
|
12875
13915
|
/**
|
|
12876
13916
|
* Closes all watchers for a path
|
|
12877
13917
|
*/
|
|
12878
|
-
_closePath(
|
|
12879
|
-
this._closeFile(
|
|
12880
|
-
const dir = sp2.dirname(
|
|
12881
|
-
this._getWatchedDir(dir).remove(sp2.basename(
|
|
13918
|
+
_closePath(path27) {
|
|
13919
|
+
this._closeFile(path27);
|
|
13920
|
+
const dir = sp2.dirname(path27);
|
|
13921
|
+
this._getWatchedDir(dir).remove(sp2.basename(path27));
|
|
12882
13922
|
}
|
|
12883
13923
|
/**
|
|
12884
13924
|
* Closes only file-specific watchers
|
|
12885
13925
|
*/
|
|
12886
|
-
_closeFile(
|
|
12887
|
-
const closers = this._closers.get(
|
|
13926
|
+
_closeFile(path27) {
|
|
13927
|
+
const closers = this._closers.get(path27);
|
|
12888
13928
|
if (!closers)
|
|
12889
13929
|
return;
|
|
12890
13930
|
closers.forEach((closer) => closer());
|
|
12891
|
-
this._closers.delete(
|
|
13931
|
+
this._closers.delete(path27);
|
|
12892
13932
|
}
|
|
12893
|
-
_addPathCloser(
|
|
13933
|
+
_addPathCloser(path27, closer) {
|
|
12894
13934
|
if (!closer)
|
|
12895
13935
|
return;
|
|
12896
|
-
let list = this._closers.get(
|
|
13936
|
+
let list = this._closers.get(path27);
|
|
12897
13937
|
if (!list) {
|
|
12898
13938
|
list = [];
|
|
12899
|
-
this._closers.set(
|
|
13939
|
+
this._closers.set(path27, list);
|
|
12900
13940
|
}
|
|
12901
13941
|
list.push(closer);
|
|
12902
13942
|
}
|
|
@@ -12926,7 +13966,7 @@ function watch(paths, options = {}) {
|
|
|
12926
13966
|
var chokidar_default = { watch, FSWatcher };
|
|
12927
13967
|
|
|
12928
13968
|
// src/watcher/file-watcher.ts
|
|
12929
|
-
import * as
|
|
13969
|
+
import * as path22 from "path";
|
|
12930
13970
|
var FileWatcher = class {
|
|
12931
13971
|
watcher = null;
|
|
12932
13972
|
projectRoot;
|
|
@@ -12937,6 +13977,8 @@ var FileWatcher = class {
|
|
|
12937
13977
|
debounceTimer = null;
|
|
12938
13978
|
debounceMs = 1e3;
|
|
12939
13979
|
onChanges = null;
|
|
13980
|
+
readyPromise = null;
|
|
13981
|
+
resolveReady = null;
|
|
12940
13982
|
constructor(projectRoot, config, host = "opencode", options = {}) {
|
|
12941
13983
|
this.projectRoot = projectRoot;
|
|
12942
13984
|
this.config = config;
|
|
@@ -12952,15 +13994,15 @@ var FileWatcher = class {
|
|
|
12952
13994
|
const watchTargets = this.configPath ? [this.projectRoot, this.configPath] : this.projectRoot;
|
|
12953
13995
|
this.watcher = chokidar_default.watch(watchTargets, {
|
|
12954
13996
|
ignored: (filePath) => {
|
|
12955
|
-
const relativePath =
|
|
13997
|
+
const relativePath = path22.relative(this.projectRoot, filePath);
|
|
12956
13998
|
if (!relativePath) return false;
|
|
12957
13999
|
if (this.isProjectConfigPathOrAncestor(relativePath)) {
|
|
12958
14000
|
return false;
|
|
12959
14001
|
}
|
|
12960
|
-
if (hasFilteredPathSegment(relativePath,
|
|
14002
|
+
if (hasFilteredPathSegment(relativePath, path22.sep)) {
|
|
12961
14003
|
return true;
|
|
12962
14004
|
}
|
|
12963
|
-
if (isRestrictedDirectory(relativePath,
|
|
14005
|
+
if (isRestrictedDirectory(relativePath, path22.sep)) {
|
|
12964
14006
|
return true;
|
|
12965
14007
|
}
|
|
12966
14008
|
if (ignoreFilter.ignores(relativePath)) {
|
|
@@ -12975,6 +14017,13 @@ var FileWatcher = class {
|
|
|
12975
14017
|
pollInterval: 100
|
|
12976
14018
|
}
|
|
12977
14019
|
});
|
|
14020
|
+
this.readyPromise = new Promise((resolve15) => {
|
|
14021
|
+
this.resolveReady = resolve15;
|
|
14022
|
+
});
|
|
14023
|
+
this.watcher.once("ready", () => {
|
|
14024
|
+
this.resolveReady?.();
|
|
14025
|
+
this.resolveReady = null;
|
|
14026
|
+
});
|
|
12978
14027
|
this.watcher.on("error", (error) => {
|
|
12979
14028
|
const err = error instanceof Error ? error : null;
|
|
12980
14029
|
if (err?.code === "EPERM" || err?.code === "EACCES") {
|
|
@@ -13006,24 +14055,24 @@ var FileWatcher = class {
|
|
|
13006
14055
|
this.scheduleFlush();
|
|
13007
14056
|
}
|
|
13008
14057
|
isProjectConfigPath(filePath) {
|
|
13009
|
-
const relativePath =
|
|
13010
|
-
const normalizedRelativePath =
|
|
14058
|
+
const relativePath = path22.relative(this.projectRoot, filePath);
|
|
14059
|
+
const normalizedRelativePath = path22.normalize(relativePath);
|
|
13011
14060
|
return this.getProjectConfigRelativePaths().some((configPath) => configPath === normalizedRelativePath);
|
|
13012
14061
|
}
|
|
13013
14062
|
isProjectConfigPathOrAncestor(relativePath) {
|
|
13014
|
-
const normalizedRelativePath =
|
|
14063
|
+
const normalizedRelativePath = path22.normalize(relativePath);
|
|
13015
14064
|
return this.getProjectConfigRelativePaths().some(
|
|
13016
|
-
(configPath) => configPath === normalizedRelativePath || configPath.startsWith(`${normalizedRelativePath}${
|
|
14065
|
+
(configPath) => configPath === normalizedRelativePath || configPath.startsWith(`${normalizedRelativePath}${path22.sep}`)
|
|
13017
14066
|
);
|
|
13018
14067
|
}
|
|
13019
14068
|
getProjectConfigRelativePaths() {
|
|
13020
14069
|
if (this.configPath) {
|
|
13021
|
-
return [
|
|
14070
|
+
return [path22.normalize(path22.relative(this.projectRoot, this.configPath))];
|
|
13022
14071
|
}
|
|
13023
14072
|
return [
|
|
13024
14073
|
resolveProjectConfigPath(this.projectRoot, this.host),
|
|
13025
14074
|
resolveWritableProjectConfigPath(this.projectRoot, this.host)
|
|
13026
|
-
].map((configPath) =>
|
|
14075
|
+
].map((configPath) => path22.normalize(path22.relative(this.projectRoot, configPath)));
|
|
13027
14076
|
}
|
|
13028
14077
|
scheduleFlush() {
|
|
13029
14078
|
if (this.debounceTimer) {
|
|
@@ -13038,7 +14087,7 @@ var FileWatcher = class {
|
|
|
13038
14087
|
return;
|
|
13039
14088
|
}
|
|
13040
14089
|
const changes = Array.from(this.pendingChanges.entries()).map(
|
|
13041
|
-
([
|
|
14090
|
+
([path27, type]) => ({ path: path27, type })
|
|
13042
14091
|
);
|
|
13043
14092
|
this.pendingChanges.clear();
|
|
13044
14093
|
try {
|
|
@@ -13047,25 +14096,32 @@ var FileWatcher = class {
|
|
|
13047
14096
|
console.error("Error handling file changes:", error);
|
|
13048
14097
|
}
|
|
13049
14098
|
}
|
|
13050
|
-
stop() {
|
|
14099
|
+
async stop() {
|
|
13051
14100
|
if (this.debounceTimer) {
|
|
13052
14101
|
clearTimeout(this.debounceTimer);
|
|
13053
14102
|
this.debounceTimer = null;
|
|
13054
14103
|
}
|
|
13055
14104
|
if (this.watcher) {
|
|
13056
|
-
this.watcher
|
|
14105
|
+
const watcher = this.watcher;
|
|
13057
14106
|
this.watcher = null;
|
|
14107
|
+
await watcher.close();
|
|
13058
14108
|
}
|
|
14109
|
+
this.resolveReady?.();
|
|
14110
|
+
this.resolveReady = null;
|
|
14111
|
+
this.readyPromise = null;
|
|
13059
14112
|
this.pendingChanges.clear();
|
|
13060
14113
|
this.onChanges = null;
|
|
13061
14114
|
}
|
|
13062
14115
|
isRunning() {
|
|
13063
14116
|
return this.watcher !== null;
|
|
13064
14117
|
}
|
|
14118
|
+
async waitUntilReady() {
|
|
14119
|
+
await (this.readyPromise ?? Promise.resolve());
|
|
14120
|
+
}
|
|
13065
14121
|
};
|
|
13066
14122
|
|
|
13067
14123
|
// src/watcher/git-head-watcher.ts
|
|
13068
|
-
import * as
|
|
14124
|
+
import * as path23 from "path";
|
|
13069
14125
|
var GitHeadWatcher = class {
|
|
13070
14126
|
watcher = null;
|
|
13071
14127
|
projectRoot;
|
|
@@ -13087,7 +14143,7 @@ var GitHeadWatcher = class {
|
|
|
13087
14143
|
this.onBranchChange = handler;
|
|
13088
14144
|
this.currentBranch = getCurrentBranch(this.projectRoot);
|
|
13089
14145
|
const headPath = getHeadPath(this.projectRoot);
|
|
13090
|
-
const refsPath =
|
|
14146
|
+
const refsPath = path23.join(this.projectRoot, ".git", "refs", "heads");
|
|
13091
14147
|
this.watcher = chokidar_default.watch([headPath, refsPath], {
|
|
13092
14148
|
persistent: true,
|
|
13093
14149
|
ignoreInitial: true,
|
|
@@ -13124,14 +14180,15 @@ var GitHeadWatcher = class {
|
|
|
13124
14180
|
getCurrentBranch() {
|
|
13125
14181
|
return this.currentBranch;
|
|
13126
14182
|
}
|
|
13127
|
-
stop() {
|
|
14183
|
+
async stop() {
|
|
13128
14184
|
if (this.debounceTimer) {
|
|
13129
14185
|
clearTimeout(this.debounceTimer);
|
|
13130
14186
|
this.debounceTimer = null;
|
|
13131
14187
|
}
|
|
13132
14188
|
if (this.watcher) {
|
|
13133
|
-
this.watcher
|
|
14189
|
+
const watcher = this.watcher;
|
|
13134
14190
|
this.watcher = null;
|
|
14191
|
+
await watcher.close();
|
|
13135
14192
|
}
|
|
13136
14193
|
this.onBranchChange = null;
|
|
13137
14194
|
}
|
|
@@ -13149,6 +14206,8 @@ var BackgroundReindexer = class {
|
|
|
13149
14206
|
running = false;
|
|
13150
14207
|
pending = false;
|
|
13151
14208
|
stopped = false;
|
|
14209
|
+
retryTimer = null;
|
|
14210
|
+
retryDelayMs = 50;
|
|
13152
14211
|
request() {
|
|
13153
14212
|
if (this.stopped) {
|
|
13154
14213
|
return;
|
|
@@ -13159,9 +14218,13 @@ var BackgroundReindexer = class {
|
|
|
13159
14218
|
stop() {
|
|
13160
14219
|
this.stopped = true;
|
|
13161
14220
|
this.pending = false;
|
|
14221
|
+
if (this.retryTimer) {
|
|
14222
|
+
clearTimeout(this.retryTimer);
|
|
14223
|
+
this.retryTimer = null;
|
|
14224
|
+
}
|
|
13162
14225
|
}
|
|
13163
14226
|
drain() {
|
|
13164
|
-
if (this.stopped || this.running || !this.pending) {
|
|
14227
|
+
if (this.stopped || this.running || this.retryTimer || !this.pending) {
|
|
13165
14228
|
return;
|
|
13166
14229
|
}
|
|
13167
14230
|
this.pending = false;
|
|
@@ -13169,13 +14232,29 @@ var BackgroundReindexer = class {
|
|
|
13169
14232
|
void this.run();
|
|
13170
14233
|
}
|
|
13171
14234
|
async run() {
|
|
14235
|
+
let retryAfterContention = false;
|
|
13172
14236
|
try {
|
|
13173
14237
|
await this.runIndex();
|
|
14238
|
+
this.retryDelayMs = 50;
|
|
13174
14239
|
} catch (error) {
|
|
13175
|
-
|
|
14240
|
+
if (isTransientIndexLockContention(error)) {
|
|
14241
|
+
this.pending = true;
|
|
14242
|
+
retryAfterContention = true;
|
|
14243
|
+
} else {
|
|
14244
|
+
console.error("[codebase-index] Background reindex failed:", error);
|
|
14245
|
+
}
|
|
13176
14246
|
} finally {
|
|
13177
14247
|
this.running = false;
|
|
13178
|
-
this.
|
|
14248
|
+
if (retryAfterContention && !this.stopped) {
|
|
14249
|
+
const delay = this.retryDelayMs;
|
|
14250
|
+
this.retryDelayMs = Math.min(this.retryDelayMs * 2, 500);
|
|
14251
|
+
this.retryTimer = setTimeout(() => {
|
|
14252
|
+
this.retryTimer = null;
|
|
14253
|
+
this.drain();
|
|
14254
|
+
}, delay);
|
|
14255
|
+
} else {
|
|
14256
|
+
this.drain();
|
|
14257
|
+
}
|
|
13179
14258
|
}
|
|
13180
14259
|
}
|
|
13181
14260
|
};
|
|
@@ -13209,10 +14288,12 @@ function createWatcherWithIndexer(getIndexer, projectRoot, config, host = "openc
|
|
|
13209
14288
|
return {
|
|
13210
14289
|
fileWatcher,
|
|
13211
14290
|
gitWatcher,
|
|
13212
|
-
|
|
14291
|
+
whenReady() {
|
|
14292
|
+
return fileWatcher.waitUntilReady();
|
|
14293
|
+
},
|
|
14294
|
+
async stop() {
|
|
13213
14295
|
backgroundReindexer.stop();
|
|
13214
|
-
fileWatcher.stop();
|
|
13215
|
-
gitWatcher?.stop();
|
|
14296
|
+
await Promise.all([fileWatcher.stop(), gitWatcher?.stop()]);
|
|
13216
14297
|
}
|
|
13217
14298
|
};
|
|
13218
14299
|
}
|
|
@@ -13231,7 +14312,7 @@ function getConfigPaths(projectRoot, host, options) {
|
|
|
13231
14312
|
|
|
13232
14313
|
// src/tools/visualize/activity.ts
|
|
13233
14314
|
import { execFileSync } from "child_process";
|
|
13234
|
-
import * as
|
|
14315
|
+
import * as path24 from "path";
|
|
13235
14316
|
function attachRecentActivity(data, projectRoot) {
|
|
13236
14317
|
const activity = readGitActivity(projectRoot);
|
|
13237
14318
|
const changes = activity.size > 0 ? buildGitChanges(data, activity, projectRoot) : buildGraphChanges(data);
|
|
@@ -13393,7 +14474,7 @@ function normalizePath3(filePath) {
|
|
|
13393
14474
|
return filePath.replace(/\\/g, "/");
|
|
13394
14475
|
}
|
|
13395
14476
|
function toGitRelativePath(projectRoot, filePath) {
|
|
13396
|
-
const relativePath =
|
|
14477
|
+
const relativePath = path24.isAbsolute(filePath) ? path24.relative(projectRoot, filePath) : filePath;
|
|
13397
14478
|
return normalizePath3(relativePath);
|
|
13398
14479
|
}
|
|
13399
14480
|
|
|
@@ -13651,7 +14732,7 @@ render();
|
|
|
13651
14732
|
}
|
|
13652
14733
|
|
|
13653
14734
|
// src/tools/visualize/transform.ts
|
|
13654
|
-
import * as
|
|
14735
|
+
import * as path25 from "path";
|
|
13655
14736
|
|
|
13656
14737
|
// src/tools/visualize/modules.ts
|
|
13657
14738
|
var MAX_MODULES = 18;
|
|
@@ -13911,7 +14992,7 @@ function transformForVisualization(symbols, edges, options = {}) {
|
|
|
13911
14992
|
filePath: s.filePath,
|
|
13912
14993
|
kind: s.kind,
|
|
13913
14994
|
line: s.startLine,
|
|
13914
|
-
directory:
|
|
14995
|
+
directory: path25.dirname(s.filePath),
|
|
13915
14996
|
moduleId: "",
|
|
13916
14997
|
moduleLabel: ""
|
|
13917
14998
|
}));
|
|
@@ -13939,9 +15020,9 @@ function parseArgs(argv) {
|
|
|
13939
15020
|
let host = "opencode";
|
|
13940
15021
|
for (let i = 2; i < argv.length; i++) {
|
|
13941
15022
|
if (argv[i] === "--project" && argv[i + 1]) {
|
|
13942
|
-
project =
|
|
15023
|
+
project = path26.resolve(argv[++i]);
|
|
13943
15024
|
} else if (argv[i] === "--config" && argv[i + 1]) {
|
|
13944
|
-
config =
|
|
15025
|
+
config = path26.resolve(argv[++i]);
|
|
13945
15026
|
} else if (argv[i] === "--host" && argv[i + 1]) {
|
|
13946
15027
|
host = parseHostMode(argv[++i]);
|
|
13947
15028
|
} else if (argv[i] === "--host") {
|
|
@@ -13954,7 +15035,7 @@ function loadCliRawConfig(args) {
|
|
|
13954
15035
|
return args.config ? loadConfigFile(args.config) : loadMergedConfig(args.project, args.host);
|
|
13955
15036
|
}
|
|
13956
15037
|
function isCliEntrypoint(moduleUrl, argvPath) {
|
|
13957
|
-
return argvPath !== void 0 &&
|
|
15038
|
+
return argvPath !== void 0 && realpathSync3(fileURLToPath2(moduleUrl)) === realpathSync3(argvPath);
|
|
13958
15039
|
}
|
|
13959
15040
|
function parseVisualizeArgs(argv, cwd) {
|
|
13960
15041
|
let project = cwd;
|
|
@@ -13964,7 +15045,7 @@ function parseVisualizeArgs(argv, cwd) {
|
|
|
13964
15045
|
for (let i = 0; i < argv.length; i++) {
|
|
13965
15046
|
const arg = argv[i];
|
|
13966
15047
|
if (arg === "--project" && argv[i + 1]) {
|
|
13967
|
-
project =
|
|
15048
|
+
project = path26.resolve(argv[++i]);
|
|
13968
15049
|
} else if (arg === "--max" && argv[i + 1]) {
|
|
13969
15050
|
maxNodes = Number(argv[++i]);
|
|
13970
15051
|
} else if (arg.startsWith("--max=") || arg.startsWith("max=")) {
|
|
@@ -14001,8 +15082,8 @@ async function handleVisualizeCommand(argv, cwd) {
|
|
|
14001
15082
|
console.error("No connected symbols found. Retry with: npm run visualize -- orphans");
|
|
14002
15083
|
return 1;
|
|
14003
15084
|
}
|
|
14004
|
-
const outputPath =
|
|
14005
|
-
|
|
15085
|
+
const outputPath = path26.join(os6.tmpdir(), `call-graph-${Date.now()}.html`);
|
|
15086
|
+
writeFileSync7(outputPath, generateVisualizationHtml(vizData), "utf-8");
|
|
14006
15087
|
console.log(`Temporal call graph visualization generated: ${outputPath}`);
|
|
14007
15088
|
console.log(`Nodes: ${vizData.nodes.length} | Edges: ${vizData.edges.length}`);
|
|
14008
15089
|
console.log(`Recent change lenses: ${vizData.changes?.length ?? 0}`);
|
|
@@ -14031,7 +15112,7 @@ async function main() {
|
|
|
14031
15112
|
const transport = new StdioServerTransport();
|
|
14032
15113
|
await server.connect(transport);
|
|
14033
15114
|
let watcher = null;
|
|
14034
|
-
const isHomeDir =
|
|
15115
|
+
const isHomeDir = path26.resolve(args.project) === path26.resolve(os6.homedir());
|
|
14035
15116
|
const isValidProject = !isHomeDir && (!config.indexing.requireProjectMarker || hasProjectMarker(args.project));
|
|
14036
15117
|
if (config.indexing.autoIndex && isValidProject) {
|
|
14037
15118
|
const indexer = getIndexerForProject(args.project, args.host);
|
|
@@ -14046,14 +15127,25 @@ async function main() {
|
|
|
14046
15127
|
args.config ? { configPath: args.config } : {}
|
|
14047
15128
|
);
|
|
14048
15129
|
}
|
|
14049
|
-
|
|
14050
|
-
|
|
14051
|
-
|
|
14052
|
-
|
|
14053
|
-
|
|
15130
|
+
let shuttingDown = false;
|
|
15131
|
+
const shutdown = async () => {
|
|
15132
|
+
if (shuttingDown) return;
|
|
15133
|
+
shuttingDown = true;
|
|
15134
|
+
try {
|
|
15135
|
+
await watcher?.stop();
|
|
15136
|
+
await server.close();
|
|
15137
|
+
process.exit(0);
|
|
15138
|
+
} catch (error) {
|
|
15139
|
+
console.error("Failed to stop MCP server cleanly:", error);
|
|
15140
|
+
process.exit(1);
|
|
15141
|
+
}
|
|
14054
15142
|
};
|
|
14055
|
-
process.on("SIGINT",
|
|
14056
|
-
|
|
15143
|
+
process.on("SIGINT", () => {
|
|
15144
|
+
void shutdown();
|
|
15145
|
+
});
|
|
15146
|
+
process.on("SIGTERM", () => {
|
|
15147
|
+
void shutdown();
|
|
15148
|
+
});
|
|
14057
15149
|
}
|
|
14058
15150
|
function handleMainError(error) {
|
|
14059
15151
|
if (error instanceof Error && error.message.startsWith("Invalid host mode")) {
|