opencode-codebase-index 0.8.1 → 0.9.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 +28 -11
- package/dist/cli.cjs +1520 -1288
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +1519 -1287
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +1107 -921
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1076 -890
- package/dist/index.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 +8 -2
package/dist/index.js
CHANGED
|
@@ -324,7 +324,7 @@ var require_ignore = __commonJS({
|
|
|
324
324
|
// path matching.
|
|
325
325
|
// - check `string` either `MODE_IGNORE` or `MODE_CHECK_IGNORE`
|
|
326
326
|
// @returns {TestResult} true if a file is ignored
|
|
327
|
-
test(
|
|
327
|
+
test(path18, checkUnignored, mode) {
|
|
328
328
|
let ignored = false;
|
|
329
329
|
let unignored = false;
|
|
330
330
|
let matchedRule;
|
|
@@ -333,7 +333,7 @@ var require_ignore = __commonJS({
|
|
|
333
333
|
if (unignored === negative && ignored !== unignored || negative && !ignored && !unignored && !checkUnignored) {
|
|
334
334
|
return;
|
|
335
335
|
}
|
|
336
|
-
const matched = rule[mode].test(
|
|
336
|
+
const matched = rule[mode].test(path18);
|
|
337
337
|
if (!matched) {
|
|
338
338
|
return;
|
|
339
339
|
}
|
|
@@ -354,17 +354,17 @@ var require_ignore = __commonJS({
|
|
|
354
354
|
var throwError = (message, Ctor) => {
|
|
355
355
|
throw new Ctor(message);
|
|
356
356
|
};
|
|
357
|
-
var checkPath = (
|
|
358
|
-
if (!isString(
|
|
357
|
+
var checkPath = (path18, originalPath, doThrow) => {
|
|
358
|
+
if (!isString(path18)) {
|
|
359
359
|
return doThrow(
|
|
360
360
|
`path must be a string, but got \`${originalPath}\``,
|
|
361
361
|
TypeError
|
|
362
362
|
);
|
|
363
363
|
}
|
|
364
|
-
if (!
|
|
364
|
+
if (!path18) {
|
|
365
365
|
return doThrow(`path must not be empty`, TypeError);
|
|
366
366
|
}
|
|
367
|
-
if (checkPath.isNotRelative(
|
|
367
|
+
if (checkPath.isNotRelative(path18)) {
|
|
368
368
|
const r = "`path.relative()`d";
|
|
369
369
|
return doThrow(
|
|
370
370
|
`path should be a ${r} string, but got "${originalPath}"`,
|
|
@@ -373,7 +373,7 @@ var require_ignore = __commonJS({
|
|
|
373
373
|
}
|
|
374
374
|
return true;
|
|
375
375
|
};
|
|
376
|
-
var isNotRelative = (
|
|
376
|
+
var isNotRelative = (path18) => REGEX_TEST_INVALID_PATH.test(path18);
|
|
377
377
|
checkPath.isNotRelative = isNotRelative;
|
|
378
378
|
checkPath.convert = (p) => p;
|
|
379
379
|
var Ignore2 = class {
|
|
@@ -403,19 +403,19 @@ var require_ignore = __commonJS({
|
|
|
403
403
|
}
|
|
404
404
|
// @returns {TestResult}
|
|
405
405
|
_test(originalPath, cache, checkUnignored, slices) {
|
|
406
|
-
const
|
|
406
|
+
const path18 = originalPath && checkPath.convert(originalPath);
|
|
407
407
|
checkPath(
|
|
408
|
-
|
|
408
|
+
path18,
|
|
409
409
|
originalPath,
|
|
410
410
|
this._strictPathCheck ? throwError : RETURN_FALSE
|
|
411
411
|
);
|
|
412
|
-
return this._t(
|
|
412
|
+
return this._t(path18, cache, checkUnignored, slices);
|
|
413
413
|
}
|
|
414
|
-
checkIgnore(
|
|
415
|
-
if (!REGEX_TEST_TRAILING_SLASH.test(
|
|
416
|
-
return this.test(
|
|
414
|
+
checkIgnore(path18) {
|
|
415
|
+
if (!REGEX_TEST_TRAILING_SLASH.test(path18)) {
|
|
416
|
+
return this.test(path18);
|
|
417
417
|
}
|
|
418
|
-
const slices =
|
|
418
|
+
const slices = path18.split(SLASH2).filter(Boolean);
|
|
419
419
|
slices.pop();
|
|
420
420
|
if (slices.length) {
|
|
421
421
|
const parent = this._t(
|
|
@@ -428,18 +428,18 @@ var require_ignore = __commonJS({
|
|
|
428
428
|
return parent;
|
|
429
429
|
}
|
|
430
430
|
}
|
|
431
|
-
return this._rules.test(
|
|
431
|
+
return this._rules.test(path18, false, MODE_CHECK_IGNORE);
|
|
432
432
|
}
|
|
433
|
-
_t(
|
|
434
|
-
if (
|
|
435
|
-
return cache[
|
|
433
|
+
_t(path18, cache, checkUnignored, slices) {
|
|
434
|
+
if (path18 in cache) {
|
|
435
|
+
return cache[path18];
|
|
436
436
|
}
|
|
437
437
|
if (!slices) {
|
|
438
|
-
slices =
|
|
438
|
+
slices = path18.split(SLASH2).filter(Boolean);
|
|
439
439
|
}
|
|
440
440
|
slices.pop();
|
|
441
441
|
if (!slices.length) {
|
|
442
|
-
return cache[
|
|
442
|
+
return cache[path18] = this._rules.test(path18, checkUnignored, MODE_IGNORE);
|
|
443
443
|
}
|
|
444
444
|
const parent = this._t(
|
|
445
445
|
slices.join(SLASH2) + SLASH2,
|
|
@@ -447,29 +447,29 @@ var require_ignore = __commonJS({
|
|
|
447
447
|
checkUnignored,
|
|
448
448
|
slices
|
|
449
449
|
);
|
|
450
|
-
return cache[
|
|
450
|
+
return cache[path18] = parent.ignored ? parent : this._rules.test(path18, checkUnignored, MODE_IGNORE);
|
|
451
451
|
}
|
|
452
|
-
ignores(
|
|
453
|
-
return this._test(
|
|
452
|
+
ignores(path18) {
|
|
453
|
+
return this._test(path18, this._ignoreCache, false).ignored;
|
|
454
454
|
}
|
|
455
455
|
createFilter() {
|
|
456
|
-
return (
|
|
456
|
+
return (path18) => !this.ignores(path18);
|
|
457
457
|
}
|
|
458
458
|
filter(paths) {
|
|
459
459
|
return makeArray(paths).filter(this.createFilter());
|
|
460
460
|
}
|
|
461
461
|
// @returns {TestResult}
|
|
462
|
-
test(
|
|
463
|
-
return this._test(
|
|
462
|
+
test(path18) {
|
|
463
|
+
return this._test(path18, this._testCache, true);
|
|
464
464
|
}
|
|
465
465
|
};
|
|
466
466
|
var factory = (options) => new Ignore2(options);
|
|
467
|
-
var isPathValid = (
|
|
467
|
+
var isPathValid = (path18) => checkPath(path18 && checkPath.convert(path18), path18, RETURN_FALSE);
|
|
468
468
|
var setupWindows = () => {
|
|
469
469
|
const makePosix = (str) => /^\\\\\?\\/.test(str) || /["<>|\u0000-\u001F]+/u.test(str) ? str : str.replace(/\\/g, "/");
|
|
470
470
|
checkPath.convert = makePosix;
|
|
471
471
|
const REGEX_TEST_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i;
|
|
472
|
-
checkPath.isNotRelative = (
|
|
472
|
+
checkPath.isNotRelative = (path18) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path18) || isNotRelative(path18);
|
|
473
473
|
};
|
|
474
474
|
if (
|
|
475
475
|
// Detect `process` so that it can run in browsers.
|
|
@@ -647,7 +647,7 @@ var require_eventemitter3 = __commonJS({
|
|
|
647
647
|
});
|
|
648
648
|
|
|
649
649
|
// src/index.ts
|
|
650
|
-
import * as
|
|
650
|
+
import * as path17 from "path";
|
|
651
651
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
652
652
|
|
|
653
653
|
// src/config/constants.ts
|
|
@@ -664,7 +664,8 @@ var DEFAULT_INCLUDE = [
|
|
|
664
664
|
"**/*.{md,mdx}",
|
|
665
665
|
"**/*.{sh,bash,zsh}",
|
|
666
666
|
"**/*.{txt,html,htm}",
|
|
667
|
-
"**/*.zig"
|
|
667
|
+
"**/*.zig",
|
|
668
|
+
"**/*.gd"
|
|
668
669
|
];
|
|
669
670
|
var DEFAULT_EXCLUDE = [
|
|
670
671
|
"**/node_modules/**",
|
|
@@ -763,28 +764,7 @@ var AUTO_DETECT_PROVIDER_ORDER = [
|
|
|
763
764
|
"google"
|
|
764
765
|
];
|
|
765
766
|
|
|
766
|
-
// src/config/
|
|
767
|
-
var ENV_REFERENCE_PATTERN = /^\{env:([A-Z_][A-Z0-9_]*)\}$/;
|
|
768
|
-
var ENV_REFERENCE_LIKE_PATTERN = /\{env:[^}]+\}/;
|
|
769
|
-
function substituteEnvString(value, keyPath) {
|
|
770
|
-
const match = value.match(ENV_REFERENCE_PATTERN);
|
|
771
|
-
if (!match) {
|
|
772
|
-
if (ENV_REFERENCE_LIKE_PATTERN.test(value)) {
|
|
773
|
-
throw new Error(
|
|
774
|
-
`Invalid environment variable reference at '${keyPath}'. Expected the entire string to match '{env:VAR_NAME}' with VAR_NAME matching [A-Z_][A-Z0-9_]*.`
|
|
775
|
-
);
|
|
776
|
-
}
|
|
777
|
-
return value;
|
|
778
|
-
}
|
|
779
|
-
const variableName = match[1];
|
|
780
|
-
const envValue = process.env[variableName];
|
|
781
|
-
if (envValue === void 0) {
|
|
782
|
-
throw new Error(`Missing environment variable '${variableName}' referenced by config at '${keyPath}'.`);
|
|
783
|
-
}
|
|
784
|
-
return envValue;
|
|
785
|
-
}
|
|
786
|
-
|
|
787
|
-
// src/config/schema.ts
|
|
767
|
+
// src/config/defaults.ts
|
|
788
768
|
function getDefaultIndexingConfig() {
|
|
789
769
|
return {
|
|
790
770
|
autoIndex: false,
|
|
@@ -816,12 +796,6 @@ function getDefaultSearchConfig() {
|
|
|
816
796
|
routingHints: true
|
|
817
797
|
};
|
|
818
798
|
}
|
|
819
|
-
function isValidFusionStrategy(value) {
|
|
820
|
-
return value === "weighted" || value === "rrf";
|
|
821
|
-
}
|
|
822
|
-
function isValidRerankerProvider(value) {
|
|
823
|
-
return value === "cohere" || value === "jina" || value === "custom";
|
|
824
|
-
}
|
|
825
799
|
function getDefaultRerankerBaseUrl(provider) {
|
|
826
800
|
switch (provider) {
|
|
827
801
|
case "cohere":
|
|
@@ -844,8 +818,37 @@ function getDefaultDebugConfig() {
|
|
|
844
818
|
metrics: true
|
|
845
819
|
};
|
|
846
820
|
}
|
|
821
|
+
|
|
822
|
+
// src/config/env-substitution.ts
|
|
823
|
+
var ENV_REFERENCE_PATTERN = /^\{env:([A-Z_][A-Z0-9_]*)\}$/;
|
|
824
|
+
var ENV_REFERENCE_LIKE_PATTERN = /\{env:[^}]+\}/;
|
|
825
|
+
function substituteEnvString(value, keyPath) {
|
|
826
|
+
const match = value.match(ENV_REFERENCE_PATTERN);
|
|
827
|
+
if (!match) {
|
|
828
|
+
if (ENV_REFERENCE_LIKE_PATTERN.test(value)) {
|
|
829
|
+
throw new Error(
|
|
830
|
+
`Invalid environment variable reference at '${keyPath}'. Expected the entire string to match '{env:VAR_NAME}' with VAR_NAME matching [A-Z_][A-Z0-9_]*.`
|
|
831
|
+
);
|
|
832
|
+
}
|
|
833
|
+
return value;
|
|
834
|
+
}
|
|
835
|
+
const variableName = match[1];
|
|
836
|
+
const envValue = process.env[variableName];
|
|
837
|
+
if (envValue === void 0) {
|
|
838
|
+
throw new Error(`Missing environment variable '${variableName}' referenced by config at '${keyPath}'.`);
|
|
839
|
+
}
|
|
840
|
+
return envValue;
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
// src/config/validators.ts
|
|
847
844
|
var VALID_SCOPES = ["project", "global"];
|
|
848
845
|
var VALID_LOG_LEVELS = ["error", "warn", "info", "debug"];
|
|
846
|
+
function isValidFusionStrategy(value) {
|
|
847
|
+
return value === "weighted" || value === "rrf";
|
|
848
|
+
}
|
|
849
|
+
function isValidRerankerProvider(value) {
|
|
850
|
+
return value === "cohere" || value === "jina" || value === "custom";
|
|
851
|
+
}
|
|
849
852
|
function isValidProvider(value) {
|
|
850
853
|
return typeof value === "string" && Object.keys(EMBEDDING_MODELS).includes(value);
|
|
851
854
|
}
|
|
@@ -873,6 +876,8 @@ function getResolvedStringArray(value, keyPath) {
|
|
|
873
876
|
function isValidLogLevel(value) {
|
|
874
877
|
return typeof value === "string" && VALID_LOG_LEVELS.includes(value);
|
|
875
878
|
}
|
|
879
|
+
|
|
880
|
+
// src/config/schema.ts
|
|
876
881
|
function parseConfig(raw) {
|
|
877
882
|
const input = raw && typeof raw === "object" ? raw : {};
|
|
878
883
|
const embeddingProviderValue = getResolvedString(input.embeddingProvider, "$root.embeddingProvider");
|
|
@@ -927,9 +932,9 @@ function parseConfig(raw) {
|
|
|
927
932
|
const rawAdditionalInclude = input.additionalInclude;
|
|
928
933
|
const additionalInclude = isStringArray(rawAdditionalInclude) ? rawAdditionalInclude.filter((p) => typeof p === "string" && p.trim().length > 0).map((p) => p.trim()) : [];
|
|
929
934
|
let embeddingProvider;
|
|
930
|
-
let embeddingModel
|
|
931
|
-
let customProvider
|
|
932
|
-
let reranker
|
|
935
|
+
let embeddingModel;
|
|
936
|
+
let customProvider;
|
|
937
|
+
let reranker;
|
|
933
938
|
if (embeddingProviderValue === "custom") {
|
|
934
939
|
embeddingProvider = "custom";
|
|
935
940
|
const rawCustom = input.customProvider && typeof input.customProvider === "object" ? input.customProvider : null;
|
|
@@ -962,7 +967,7 @@ function parseConfig(raw) {
|
|
|
962
967
|
embeddingProvider = embeddingProviderValue;
|
|
963
968
|
const rawEmbeddingModel = input.embeddingModel;
|
|
964
969
|
if (typeof rawEmbeddingModel === "string") {
|
|
965
|
-
const embeddingModelValue =
|
|
970
|
+
const embeddingModelValue = getResolvedString(rawEmbeddingModel, "$root.embeddingModel");
|
|
966
971
|
if (embeddingModelValue) {
|
|
967
972
|
embeddingModel = isValidModel(embeddingModelValue, embeddingProvider) ? embeddingModelValue : DEFAULT_PROVIDER_MODELS[embeddingProvider];
|
|
968
973
|
}
|
|
@@ -1025,16 +1030,20 @@ var autoDetectProviders = AUTO_DETECT_PROVIDER_ORDER.filter(
|
|
|
1025
1030
|
);
|
|
1026
1031
|
|
|
1027
1032
|
// src/config/merger.ts
|
|
1028
|
-
import { existsSync as
|
|
1033
|
+
import { existsSync as existsSync4, mkdirSync, readFileSync as readFileSync3, writeFileSync } from "fs";
|
|
1029
1034
|
import * as os2 from "os";
|
|
1030
|
-
import * as
|
|
1035
|
+
import * as path6 from "path";
|
|
1031
1036
|
|
|
1032
1037
|
// src/config/paths.ts
|
|
1033
|
-
import { existsSync as
|
|
1038
|
+
import { existsSync as existsSync3 } from "fs";
|
|
1034
1039
|
import * as os from "os";
|
|
1035
|
-
import * as
|
|
1040
|
+
import * as path3 from "path";
|
|
1036
1041
|
|
|
1037
1042
|
// src/git/index.ts
|
|
1043
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2, statSync as statSync2 } from "fs";
|
|
1044
|
+
import * as path2 from "path";
|
|
1045
|
+
|
|
1046
|
+
// src/git/refs.ts
|
|
1038
1047
|
import { existsSync, readFileSync, readdirSync, statSync } from "fs";
|
|
1039
1048
|
import * as path from "path";
|
|
1040
1049
|
function readPackedRefs(gitDir) {
|
|
@@ -1067,38 +1076,40 @@ function resolveCommonGitDir(gitDir) {
|
|
|
1067
1076
|
}
|
|
1068
1077
|
return gitDir;
|
|
1069
1078
|
}
|
|
1079
|
+
|
|
1080
|
+
// src/git/index.ts
|
|
1070
1081
|
function resolveWorktreeMainRepoRoot(repoRoot) {
|
|
1071
1082
|
const gitDir = resolveGitDir(repoRoot);
|
|
1072
1083
|
if (!gitDir) {
|
|
1073
1084
|
return null;
|
|
1074
1085
|
}
|
|
1075
1086
|
const commonGitDir = resolveCommonGitDir(gitDir);
|
|
1076
|
-
if (commonGitDir === gitDir ||
|
|
1087
|
+
if (commonGitDir === gitDir || path2.basename(commonGitDir) !== ".git") {
|
|
1077
1088
|
return null;
|
|
1078
1089
|
}
|
|
1079
|
-
const mainRepoRoot =
|
|
1080
|
-
if (!
|
|
1090
|
+
const mainRepoRoot = path2.dirname(commonGitDir);
|
|
1091
|
+
if (!existsSync2(mainRepoRoot)) {
|
|
1081
1092
|
return null;
|
|
1082
1093
|
}
|
|
1083
|
-
return
|
|
1094
|
+
return path2.resolve(mainRepoRoot) === path2.resolve(repoRoot) ? null : mainRepoRoot;
|
|
1084
1095
|
}
|
|
1085
1096
|
function resolveGitDir(repoRoot) {
|
|
1086
|
-
const gitPath =
|
|
1087
|
-
if (!
|
|
1097
|
+
const gitPath = path2.join(repoRoot, ".git");
|
|
1098
|
+
if (!existsSync2(gitPath)) {
|
|
1088
1099
|
return null;
|
|
1089
1100
|
}
|
|
1090
1101
|
try {
|
|
1091
|
-
const stat4 =
|
|
1102
|
+
const stat4 = statSync2(gitPath);
|
|
1092
1103
|
if (stat4.isDirectory()) {
|
|
1093
1104
|
return gitPath;
|
|
1094
1105
|
}
|
|
1095
1106
|
if (stat4.isFile()) {
|
|
1096
|
-
const content =
|
|
1107
|
+
const content = readFileSync2(gitPath, "utf-8").trim();
|
|
1097
1108
|
const match = content.match(/^gitdir:\s*(.+)$/);
|
|
1098
1109
|
if (match) {
|
|
1099
1110
|
const gitdir = match[1];
|
|
1100
|
-
const resolvedPath =
|
|
1101
|
-
if (
|
|
1111
|
+
const resolvedPath = path2.isAbsolute(gitdir) ? gitdir : path2.resolve(repoRoot, gitdir);
|
|
1112
|
+
if (existsSync2(resolvedPath)) {
|
|
1102
1113
|
return resolvedPath;
|
|
1103
1114
|
}
|
|
1104
1115
|
}
|
|
@@ -1115,12 +1126,12 @@ function getCurrentBranch(repoRoot) {
|
|
|
1115
1126
|
if (!gitDir) {
|
|
1116
1127
|
return null;
|
|
1117
1128
|
}
|
|
1118
|
-
const headPath =
|
|
1119
|
-
if (!
|
|
1129
|
+
const headPath = path2.join(gitDir, "HEAD");
|
|
1130
|
+
if (!existsSync2(headPath)) {
|
|
1120
1131
|
return null;
|
|
1121
1132
|
}
|
|
1122
1133
|
try {
|
|
1123
|
-
const headContent =
|
|
1134
|
+
const headContent = readFileSync2(headPath, "utf-8").trim();
|
|
1124
1135
|
const match = headContent.match(/^ref: refs\/heads\/(.+)$/);
|
|
1125
1136
|
if (match) {
|
|
1126
1137
|
return match[1];
|
|
@@ -1139,8 +1150,8 @@ function getBaseBranch(repoRoot) {
|
|
|
1139
1150
|
const candidates = ["main", "master", "develop", "trunk"];
|
|
1140
1151
|
if (refStoreDir) {
|
|
1141
1152
|
for (const candidate of candidates) {
|
|
1142
|
-
const refPath =
|
|
1143
|
-
if (
|
|
1153
|
+
const refPath = path2.join(refStoreDir, "refs", "heads", candidate);
|
|
1154
|
+
if (existsSync2(refPath)) {
|
|
1144
1155
|
return candidate;
|
|
1145
1156
|
}
|
|
1146
1157
|
const packedRefs = readPackedRefs(refStoreDir);
|
|
@@ -1160,44 +1171,44 @@ function getBranchOrDefault(repoRoot) {
|
|
|
1160
1171
|
function getHeadPath(repoRoot) {
|
|
1161
1172
|
const gitDir = resolveGitDir(repoRoot);
|
|
1162
1173
|
if (gitDir) {
|
|
1163
|
-
return
|
|
1174
|
+
return path2.join(gitDir, "HEAD");
|
|
1164
1175
|
}
|
|
1165
|
-
return
|
|
1176
|
+
return path2.join(repoRoot, ".git", "HEAD");
|
|
1166
1177
|
}
|
|
1167
1178
|
|
|
1168
1179
|
// src/config/paths.ts
|
|
1169
|
-
var PROJECT_CONFIG_RELATIVE_PATH =
|
|
1170
|
-
var PROJECT_INDEX_RELATIVE_PATH =
|
|
1180
|
+
var PROJECT_CONFIG_RELATIVE_PATH = path3.join(".opencode", "codebase-index.json");
|
|
1181
|
+
var PROJECT_INDEX_RELATIVE_PATH = path3.join(".opencode", "index");
|
|
1171
1182
|
function resolveWorktreeFallbackPath(projectRoot, relativePath) {
|
|
1172
1183
|
const mainRepoRoot = resolveWorktreeMainRepoRoot(projectRoot);
|
|
1173
1184
|
if (!mainRepoRoot) {
|
|
1174
1185
|
return null;
|
|
1175
1186
|
}
|
|
1176
|
-
const fallbackPath =
|
|
1177
|
-
return
|
|
1187
|
+
const fallbackPath = path3.join(mainRepoRoot, relativePath);
|
|
1188
|
+
return existsSync3(fallbackPath) ? fallbackPath : null;
|
|
1178
1189
|
}
|
|
1179
1190
|
function hasProjectConfig(projectRoot) {
|
|
1180
|
-
return
|
|
1191
|
+
return existsSync3(path3.join(projectRoot, PROJECT_CONFIG_RELATIVE_PATH));
|
|
1181
1192
|
}
|
|
1182
1193
|
function getGlobalIndexPath() {
|
|
1183
|
-
return
|
|
1194
|
+
return path3.join(os.homedir(), ".opencode", "global-index");
|
|
1184
1195
|
}
|
|
1185
1196
|
function resolveProjectConfigPath(projectRoot) {
|
|
1186
|
-
const localConfigPath =
|
|
1187
|
-
if (
|
|
1197
|
+
const localConfigPath = path3.join(projectRoot, PROJECT_CONFIG_RELATIVE_PATH);
|
|
1198
|
+
if (existsSync3(localConfigPath)) {
|
|
1188
1199
|
return localConfigPath;
|
|
1189
1200
|
}
|
|
1190
1201
|
return resolveWorktreeFallbackPath(projectRoot, PROJECT_CONFIG_RELATIVE_PATH) ?? localConfigPath;
|
|
1191
1202
|
}
|
|
1192
1203
|
function resolveWritableProjectConfigPath(projectRoot) {
|
|
1193
|
-
return
|
|
1204
|
+
return path3.join(projectRoot, PROJECT_CONFIG_RELATIVE_PATH);
|
|
1194
1205
|
}
|
|
1195
1206
|
function resolveProjectIndexPath(projectRoot, scope) {
|
|
1196
1207
|
if (scope === "global") {
|
|
1197
1208
|
return getGlobalIndexPath();
|
|
1198
1209
|
}
|
|
1199
|
-
const localIndexPath =
|
|
1200
|
-
if (
|
|
1210
|
+
const localIndexPath = path3.join(projectRoot, PROJECT_INDEX_RELATIVE_PATH);
|
|
1211
|
+
if (existsSync3(localIndexPath)) {
|
|
1201
1212
|
return localIndexPath;
|
|
1202
1213
|
}
|
|
1203
1214
|
if (hasProjectConfig(projectRoot)) {
|
|
@@ -1206,23 +1217,30 @@ function resolveProjectIndexPath(projectRoot, scope) {
|
|
|
1206
1217
|
return resolveWorktreeFallbackPath(projectRoot, PROJECT_INDEX_RELATIVE_PATH) ?? localIndexPath;
|
|
1207
1218
|
}
|
|
1208
1219
|
|
|
1209
|
-
// src/config/
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
return
|
|
1220
|
+
// src/config/rebase.ts
|
|
1221
|
+
import * as path5 from "path";
|
|
1222
|
+
|
|
1223
|
+
// src/utils/paths.ts
|
|
1224
|
+
import * as path4 from "path";
|
|
1225
|
+
function normalizePathSeparators(value) {
|
|
1226
|
+
return value.replace(/\\/g, "/");
|
|
1227
|
+
}
|
|
1228
|
+
function isHiddenPathSegment(part) {
|
|
1229
|
+
return part.startsWith(".") && part !== "." && part !== "..";
|
|
1230
|
+
}
|
|
1231
|
+
function isBuildPathSegment(part) {
|
|
1232
|
+
return part.toLowerCase().includes("build");
|
|
1219
1233
|
}
|
|
1220
|
-
function
|
|
1221
|
-
return
|
|
1234
|
+
function hasFilteredPathSegment(relativePath, separator = path4.sep) {
|
|
1235
|
+
return relativePath.split(separator).some(
|
|
1236
|
+
(part) => isHiddenPathSegment(part) || isBuildPathSegment(part)
|
|
1237
|
+
);
|
|
1222
1238
|
}
|
|
1239
|
+
|
|
1240
|
+
// src/config/rebase.ts
|
|
1223
1241
|
function isWithinRoot(rootDir, targetPath) {
|
|
1224
|
-
const relativePath =
|
|
1225
|
-
return relativePath === "" || !relativePath.startsWith("..") && !
|
|
1242
|
+
const relativePath = path5.relative(rootDir, targetPath);
|
|
1243
|
+
return relativePath === "" || !relativePath.startsWith("..") && !path5.isAbsolute(relativePath);
|
|
1226
1244
|
}
|
|
1227
1245
|
function resolveInheritedKnowledgeBaseEntries(values, sourceRoot, targetRoot) {
|
|
1228
1246
|
if (!Array.isArray(values)) {
|
|
@@ -1233,22 +1251,106 @@ function resolveInheritedKnowledgeBaseEntries(values, sourceRoot, targetRoot) {
|
|
|
1233
1251
|
if (!trimmed) {
|
|
1234
1252
|
return trimmed;
|
|
1235
1253
|
}
|
|
1236
|
-
if (
|
|
1254
|
+
if (path5.isAbsolute(trimmed)) {
|
|
1237
1255
|
if (isWithinRoot(sourceRoot, trimmed)) {
|
|
1238
|
-
return
|
|
1256
|
+
return normalizePathSeparators(path5.normalize(path5.relative(sourceRoot, trimmed) || "."));
|
|
1239
1257
|
}
|
|
1240
|
-
return
|
|
1258
|
+
return path5.normalize(trimmed);
|
|
1241
1259
|
}
|
|
1242
|
-
const resolvedFromSource =
|
|
1260
|
+
const resolvedFromSource = path5.resolve(sourceRoot, trimmed);
|
|
1243
1261
|
if (isWithinRoot(sourceRoot, resolvedFromSource)) {
|
|
1244
|
-
return
|
|
1262
|
+
return normalizePathSeparators(path5.normalize(trimmed));
|
|
1245
1263
|
}
|
|
1246
|
-
return
|
|
1264
|
+
return normalizePathSeparators(path5.normalize(path5.relative(targetRoot, resolvedFromSource)));
|
|
1247
1265
|
}).filter(Boolean);
|
|
1248
1266
|
}
|
|
1267
|
+
|
|
1268
|
+
// src/config/merger.ts
|
|
1269
|
+
var PROJECT_OVERRIDE_KEYS = [
|
|
1270
|
+
"embeddingProvider",
|
|
1271
|
+
"customProvider",
|
|
1272
|
+
"embeddingModel",
|
|
1273
|
+
"reranker",
|
|
1274
|
+
"include",
|
|
1275
|
+
"exclude",
|
|
1276
|
+
"indexing",
|
|
1277
|
+
"search",
|
|
1278
|
+
"debug",
|
|
1279
|
+
"scope"
|
|
1280
|
+
];
|
|
1281
|
+
var MERGE_ARRAY_KEYS = ["knowledgeBases", "additionalInclude"];
|
|
1282
|
+
function isRecord(value) {
|
|
1283
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1284
|
+
}
|
|
1285
|
+
function isStringArray2(value) {
|
|
1286
|
+
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
1287
|
+
}
|
|
1288
|
+
function applyProjectOverride(merged, normalizedProjectConfig, globalConfig, key) {
|
|
1289
|
+
if (key in normalizedProjectConfig) {
|
|
1290
|
+
merged[key] = normalizedProjectConfig[key];
|
|
1291
|
+
return;
|
|
1292
|
+
}
|
|
1293
|
+
if (key in globalConfig) {
|
|
1294
|
+
merged[key] = globalConfig[key];
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
function mergeUniqueStringArray(values) {
|
|
1298
|
+
return [...new Set(values.map((value) => String(value).trim()))];
|
|
1299
|
+
}
|
|
1300
|
+
function normalizeKnowledgeBasePath(value) {
|
|
1301
|
+
let normalized = path6.normalize(String(value).trim());
|
|
1302
|
+
const root = path6.parse(normalized).root;
|
|
1303
|
+
while (normalized.length > root.length && /[\\/]$/.test(normalized)) {
|
|
1304
|
+
normalized = normalized.slice(0, -1);
|
|
1305
|
+
}
|
|
1306
|
+
return normalized;
|
|
1307
|
+
}
|
|
1308
|
+
function mergeKnowledgeBasePaths(values) {
|
|
1309
|
+
return [...new Set(values.map((value) => normalizeKnowledgeBasePath(value)).filter((value) => value.length > 0))];
|
|
1310
|
+
}
|
|
1311
|
+
function validateConfigLayerShape(rawConfig, filePath) {
|
|
1312
|
+
if (!isRecord(rawConfig)) {
|
|
1313
|
+
throw new Error(`Config file ${filePath} must contain a JSON object at the root.`);
|
|
1314
|
+
}
|
|
1315
|
+
if (rawConfig.knowledgeBases !== void 0 && !isStringArray2(rawConfig.knowledgeBases)) {
|
|
1316
|
+
throw new Error(`Config file ${filePath} field 'knowledgeBases' must be an array of strings.`);
|
|
1317
|
+
}
|
|
1318
|
+
if (rawConfig.additionalInclude !== void 0 && !isStringArray2(rawConfig.additionalInclude)) {
|
|
1319
|
+
throw new Error(`Config file ${filePath} field 'additionalInclude' must be an array of strings.`);
|
|
1320
|
+
}
|
|
1321
|
+
if (rawConfig.include !== void 0 && !isStringArray2(rawConfig.include)) {
|
|
1322
|
+
throw new Error(`Config file ${filePath} field 'include' must be an array of strings.`);
|
|
1323
|
+
}
|
|
1324
|
+
if (rawConfig.exclude !== void 0 && !isStringArray2(rawConfig.exclude)) {
|
|
1325
|
+
throw new Error(`Config file ${filePath} field 'exclude' must be an array of strings.`);
|
|
1326
|
+
}
|
|
1327
|
+
for (const section of ["customProvider", "indexing", "search", "debug", "reranker"]) {
|
|
1328
|
+
const value = rawConfig[section];
|
|
1329
|
+
if (value !== void 0 && !isRecord(value)) {
|
|
1330
|
+
throw new Error(`Config file ${filePath} field '${section}' must be an object.`);
|
|
1331
|
+
}
|
|
1332
|
+
}
|
|
1333
|
+
return rawConfig;
|
|
1334
|
+
}
|
|
1335
|
+
function loadJsonFile(filePath) {
|
|
1336
|
+
if (!existsSync4(filePath)) {
|
|
1337
|
+
return null;
|
|
1338
|
+
}
|
|
1339
|
+
try {
|
|
1340
|
+
const content = readFileSync3(filePath, "utf-8");
|
|
1341
|
+
return validateConfigLayerShape(JSON.parse(content), filePath);
|
|
1342
|
+
} catch (error) {
|
|
1343
|
+
if (error instanceof Error && error.message.startsWith("Config file ")) {
|
|
1344
|
+
throw error;
|
|
1345
|
+
}
|
|
1346
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1347
|
+
throw new Error(`Failed to load config file ${filePath}: ${message}`);
|
|
1348
|
+
}
|
|
1349
|
+
return null;
|
|
1350
|
+
}
|
|
1249
1351
|
function materializeLocalProjectConfig(projectRoot, config) {
|
|
1250
|
-
const localConfigPath =
|
|
1251
|
-
mkdirSync(
|
|
1352
|
+
const localConfigPath = path6.join(projectRoot, ".opencode", "codebase-index.json");
|
|
1353
|
+
mkdirSync(path6.dirname(localConfigPath), { recursive: true });
|
|
1252
1354
|
writeFileSync(localConfigPath, JSON.stringify(config, null, 2), "utf-8");
|
|
1253
1355
|
return localConfigPath;
|
|
1254
1356
|
}
|
|
@@ -1259,7 +1361,7 @@ function loadProjectConfigLayer(projectRoot) {
|
|
|
1259
1361
|
return {};
|
|
1260
1362
|
}
|
|
1261
1363
|
const normalizedConfig = { ...projectConfig };
|
|
1262
|
-
const projectConfigBaseDir =
|
|
1364
|
+
const projectConfigBaseDir = path6.dirname(path6.dirname(projectConfigPath));
|
|
1263
1365
|
if (Array.isArray(normalizedConfig.knowledgeBases)) {
|
|
1264
1366
|
normalizedConfig.knowledgeBases = resolveInheritedKnowledgeBaseEntries(
|
|
1265
1367
|
normalizedConfig.knowledgeBases,
|
|
@@ -1271,10 +1373,22 @@ function loadProjectConfigLayer(projectRoot) {
|
|
|
1271
1373
|
}
|
|
1272
1374
|
function loadMergedConfig(projectRoot) {
|
|
1273
1375
|
const globalConfigPath = os2.homedir() + "/.config/opencode/codebase-index.json";
|
|
1274
|
-
const globalConfig = loadJsonFile(globalConfigPath);
|
|
1275
1376
|
const projectConfigPath = resolveProjectConfigPath(projectRoot);
|
|
1377
|
+
let globalConfig = null;
|
|
1378
|
+
let globalConfigError = null;
|
|
1379
|
+
try {
|
|
1380
|
+
globalConfig = loadJsonFile(globalConfigPath);
|
|
1381
|
+
} catch (error) {
|
|
1382
|
+
globalConfigError = error instanceof Error ? error : new Error(String(error));
|
|
1383
|
+
}
|
|
1276
1384
|
const projectConfig = loadJsonFile(projectConfigPath);
|
|
1277
1385
|
const normalizedProjectConfig = loadProjectConfigLayer(projectRoot);
|
|
1386
|
+
if (globalConfigError) {
|
|
1387
|
+
if (!projectConfig) {
|
|
1388
|
+
throw globalConfigError;
|
|
1389
|
+
}
|
|
1390
|
+
globalConfig = null;
|
|
1391
|
+
}
|
|
1278
1392
|
if (!globalConfig && !projectConfig) {
|
|
1279
1393
|
return {};
|
|
1280
1394
|
}
|
|
@@ -1284,60 +1398,16 @@ function loadMergedConfig(projectRoot) {
|
|
|
1284
1398
|
if (!globalConfig && projectConfig) {
|
|
1285
1399
|
return normalizedProjectConfig;
|
|
1286
1400
|
}
|
|
1401
|
+
if (!globalConfig || !projectConfig) {
|
|
1402
|
+
return globalConfig ?? normalizedProjectConfig;
|
|
1403
|
+
}
|
|
1287
1404
|
const merged = { ...globalConfig };
|
|
1288
|
-
|
|
1289
|
-
merged
|
|
1290
|
-
} else if (globalConfig && globalConfig.embeddingProvider) {
|
|
1291
|
-
merged.embeddingProvider = globalConfig.embeddingProvider;
|
|
1292
|
-
}
|
|
1293
|
-
if (projectConfig && "customProvider" in normalizedProjectConfig) {
|
|
1294
|
-
merged.customProvider = normalizedProjectConfig.customProvider;
|
|
1295
|
-
} else if (globalConfig && globalConfig.customProvider) {
|
|
1296
|
-
merged.customProvider = globalConfig.customProvider;
|
|
1297
|
-
}
|
|
1298
|
-
if (projectConfig && "embeddingModel" in normalizedProjectConfig) {
|
|
1299
|
-
merged.embeddingModel = normalizedProjectConfig.embeddingModel;
|
|
1300
|
-
} else if (globalConfig && globalConfig.embeddingModel) {
|
|
1301
|
-
merged.embeddingModel = globalConfig.embeddingModel;
|
|
1302
|
-
}
|
|
1303
|
-
if (projectConfig && "reranker" in normalizedProjectConfig) {
|
|
1304
|
-
merged.reranker = normalizedProjectConfig.reranker;
|
|
1305
|
-
} else if (globalConfig && globalConfig.reranker) {
|
|
1306
|
-
merged.reranker = globalConfig.reranker;
|
|
1307
|
-
}
|
|
1308
|
-
if (projectConfig && "include" in normalizedProjectConfig) {
|
|
1309
|
-
merged.include = normalizedProjectConfig.include;
|
|
1310
|
-
} else if (globalConfig && globalConfig.include) {
|
|
1311
|
-
merged.include = globalConfig.include;
|
|
1312
|
-
}
|
|
1313
|
-
if (projectConfig && "exclude" in normalizedProjectConfig) {
|
|
1314
|
-
merged.exclude = normalizedProjectConfig.exclude;
|
|
1315
|
-
} else if (globalConfig && globalConfig.exclude) {
|
|
1316
|
-
merged.exclude = globalConfig.exclude;
|
|
1317
|
-
}
|
|
1318
|
-
if (projectConfig && "indexing" in normalizedProjectConfig) {
|
|
1319
|
-
merged.indexing = normalizedProjectConfig.indexing;
|
|
1320
|
-
} else if (globalConfig && globalConfig.indexing) {
|
|
1321
|
-
merged.indexing = globalConfig.indexing;
|
|
1322
|
-
}
|
|
1323
|
-
if (projectConfig && "search" in normalizedProjectConfig) {
|
|
1324
|
-
merged.search = normalizedProjectConfig.search;
|
|
1325
|
-
} else if (globalConfig && globalConfig.search) {
|
|
1326
|
-
merged.search = globalConfig.search;
|
|
1327
|
-
}
|
|
1328
|
-
if (projectConfig && "debug" in normalizedProjectConfig) {
|
|
1329
|
-
merged.debug = normalizedProjectConfig.debug;
|
|
1330
|
-
} else if (globalConfig && globalConfig.debug) {
|
|
1331
|
-
merged.debug = globalConfig.debug;
|
|
1332
|
-
}
|
|
1333
|
-
if (projectConfig && "scope" in normalizedProjectConfig) {
|
|
1334
|
-
merged.scope = normalizedProjectConfig.scope;
|
|
1335
|
-
} else if (globalConfig && "scope" in globalConfig) {
|
|
1336
|
-
merged.scope = globalConfig.scope;
|
|
1405
|
+
for (const key of PROJECT_OVERRIDE_KEYS) {
|
|
1406
|
+
applyProjectOverride(merged, normalizedProjectConfig, globalConfig, key);
|
|
1337
1407
|
}
|
|
1338
1408
|
if (projectConfig) {
|
|
1339
1409
|
for (const key of Object.keys(projectConfig)) {
|
|
1340
|
-
if (key
|
|
1410
|
+
if (PROJECT_OVERRIDE_KEYS.includes(key) || MERGE_ARRAY_KEYS.includes(key)) {
|
|
1341
1411
|
continue;
|
|
1342
1412
|
}
|
|
1343
1413
|
merged[key] = normalizedProjectConfig[key];
|
|
@@ -1346,13 +1416,11 @@ function loadMergedConfig(projectRoot) {
|
|
|
1346
1416
|
const globalKbs = globalConfig && Array.isArray(globalConfig.knowledgeBases) ? globalConfig.knowledgeBases : [];
|
|
1347
1417
|
const projectKbs = projectConfig ? Array.isArray(normalizedProjectConfig.knowledgeBases) ? normalizedProjectConfig.knowledgeBases : [] : [];
|
|
1348
1418
|
const allKbs = [...globalKbs, ...projectKbs];
|
|
1349
|
-
|
|
1350
|
-
merged.knowledgeBases = uniqueKbs;
|
|
1419
|
+
merged.knowledgeBases = mergeKnowledgeBasePaths(allKbs);
|
|
1351
1420
|
const globalAdditional = globalConfig && Array.isArray(globalConfig.additionalInclude) ? globalConfig.additionalInclude : [];
|
|
1352
1421
|
const projectAdditional = projectConfig && Array.isArray(projectConfig.additionalInclude) ? projectConfig.additionalInclude : [];
|
|
1353
1422
|
const allAdditional = [...globalAdditional, ...projectAdditional];
|
|
1354
|
-
|
|
1355
|
-
merged.additionalInclude = uniqueAdditional;
|
|
1423
|
+
merged.additionalInclude = mergeUniqueStringArray(allAdditional);
|
|
1356
1424
|
return merged;
|
|
1357
1425
|
}
|
|
1358
1426
|
|
|
@@ -1446,7 +1514,7 @@ var ReaddirpStream = class extends Readable {
|
|
|
1446
1514
|
this._directoryFilter = normalizeFilter(opts.directoryFilter);
|
|
1447
1515
|
const statMethod = opts.lstat ? lstat : stat;
|
|
1448
1516
|
if (wantBigintFsStats) {
|
|
1449
|
-
this._stat = (
|
|
1517
|
+
this._stat = (path18) => statMethod(path18, { bigint: true });
|
|
1450
1518
|
} else {
|
|
1451
1519
|
this._stat = statMethod;
|
|
1452
1520
|
}
|
|
@@ -1471,8 +1539,8 @@ var ReaddirpStream = class extends Readable {
|
|
|
1471
1539
|
const par = this.parent;
|
|
1472
1540
|
const fil = par && par.files;
|
|
1473
1541
|
if (fil && fil.length > 0) {
|
|
1474
|
-
const { path:
|
|
1475
|
-
const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent,
|
|
1542
|
+
const { path: path18, depth } = par;
|
|
1543
|
+
const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path18));
|
|
1476
1544
|
const awaited = await Promise.all(slice);
|
|
1477
1545
|
for (const entry of awaited) {
|
|
1478
1546
|
if (!entry)
|
|
@@ -1512,20 +1580,20 @@ var ReaddirpStream = class extends Readable {
|
|
|
1512
1580
|
this.reading = false;
|
|
1513
1581
|
}
|
|
1514
1582
|
}
|
|
1515
|
-
async _exploreDir(
|
|
1583
|
+
async _exploreDir(path18, depth) {
|
|
1516
1584
|
let files;
|
|
1517
1585
|
try {
|
|
1518
|
-
files = await readdir(
|
|
1586
|
+
files = await readdir(path18, this._rdOptions);
|
|
1519
1587
|
} catch (error) {
|
|
1520
1588
|
this._onError(error);
|
|
1521
1589
|
}
|
|
1522
|
-
return { files, depth, path:
|
|
1590
|
+
return { files, depth, path: path18 };
|
|
1523
1591
|
}
|
|
1524
|
-
async _formatEntry(dirent,
|
|
1592
|
+
async _formatEntry(dirent, path18) {
|
|
1525
1593
|
let entry;
|
|
1526
1594
|
const basename5 = this._isDirent ? dirent.name : dirent;
|
|
1527
1595
|
try {
|
|
1528
|
-
const fullPath = presolve(pjoin(
|
|
1596
|
+
const fullPath = presolve(pjoin(path18, basename5));
|
|
1529
1597
|
entry = { path: prelative(this._root, fullPath), fullPath, basename: basename5 };
|
|
1530
1598
|
entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
|
|
1531
1599
|
} catch (err) {
|
|
@@ -1925,16 +1993,16 @@ var delFromSet = (main, prop, item) => {
|
|
|
1925
1993
|
};
|
|
1926
1994
|
var isEmptySet = (val) => val instanceof Set ? val.size === 0 : !val;
|
|
1927
1995
|
var FsWatchInstances = /* @__PURE__ */ new Map();
|
|
1928
|
-
function createFsWatchInstance(
|
|
1996
|
+
function createFsWatchInstance(path18, options, listener, errHandler, emitRaw) {
|
|
1929
1997
|
const handleEvent = (rawEvent, evPath) => {
|
|
1930
|
-
listener(
|
|
1931
|
-
emitRaw(rawEvent, evPath, { watchedPath:
|
|
1932
|
-
if (evPath &&
|
|
1933
|
-
fsWatchBroadcast(sp.resolve(
|
|
1998
|
+
listener(path18);
|
|
1999
|
+
emitRaw(rawEvent, evPath, { watchedPath: path18 });
|
|
2000
|
+
if (evPath && path18 !== evPath) {
|
|
2001
|
+
fsWatchBroadcast(sp.resolve(path18, evPath), KEY_LISTENERS, sp.join(path18, evPath));
|
|
1934
2002
|
}
|
|
1935
2003
|
};
|
|
1936
2004
|
try {
|
|
1937
|
-
return fs_watch(
|
|
2005
|
+
return fs_watch(path18, {
|
|
1938
2006
|
persistent: options.persistent
|
|
1939
2007
|
}, handleEvent);
|
|
1940
2008
|
} catch (error) {
|
|
@@ -1950,12 +2018,12 @@ var fsWatchBroadcast = (fullPath, listenerType, val1, val2, val3) => {
|
|
|
1950
2018
|
listener(val1, val2, val3);
|
|
1951
2019
|
});
|
|
1952
2020
|
};
|
|
1953
|
-
var setFsWatchListener = (
|
|
2021
|
+
var setFsWatchListener = (path18, fullPath, options, handlers) => {
|
|
1954
2022
|
const { listener, errHandler, rawEmitter } = handlers;
|
|
1955
2023
|
let cont = FsWatchInstances.get(fullPath);
|
|
1956
2024
|
let watcher;
|
|
1957
2025
|
if (!options.persistent) {
|
|
1958
|
-
watcher = createFsWatchInstance(
|
|
2026
|
+
watcher = createFsWatchInstance(path18, options, listener, errHandler, rawEmitter);
|
|
1959
2027
|
if (!watcher)
|
|
1960
2028
|
return;
|
|
1961
2029
|
return watcher.close.bind(watcher);
|
|
@@ -1966,7 +2034,7 @@ var setFsWatchListener = (path12, fullPath, options, handlers) => {
|
|
|
1966
2034
|
addAndConvert(cont, KEY_RAW, rawEmitter);
|
|
1967
2035
|
} else {
|
|
1968
2036
|
watcher = createFsWatchInstance(
|
|
1969
|
-
|
|
2037
|
+
path18,
|
|
1970
2038
|
options,
|
|
1971
2039
|
fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS),
|
|
1972
2040
|
errHandler,
|
|
@@ -1981,7 +2049,7 @@ var setFsWatchListener = (path12, fullPath, options, handlers) => {
|
|
|
1981
2049
|
cont.watcherUnusable = true;
|
|
1982
2050
|
if (isWindows && error.code === "EPERM") {
|
|
1983
2051
|
try {
|
|
1984
|
-
const fd = await open(
|
|
2052
|
+
const fd = await open(path18, "r");
|
|
1985
2053
|
await fd.close();
|
|
1986
2054
|
broadcastErr(error);
|
|
1987
2055
|
} catch (err) {
|
|
@@ -2012,7 +2080,7 @@ var setFsWatchListener = (path12, fullPath, options, handlers) => {
|
|
|
2012
2080
|
};
|
|
2013
2081
|
};
|
|
2014
2082
|
var FsWatchFileInstances = /* @__PURE__ */ new Map();
|
|
2015
|
-
var setFsWatchFileListener = (
|
|
2083
|
+
var setFsWatchFileListener = (path18, fullPath, options, handlers) => {
|
|
2016
2084
|
const { listener, rawEmitter } = handlers;
|
|
2017
2085
|
let cont = FsWatchFileInstances.get(fullPath);
|
|
2018
2086
|
const copts = cont && cont.options;
|
|
@@ -2034,7 +2102,7 @@ var setFsWatchFileListener = (path12, fullPath, options, handlers) => {
|
|
|
2034
2102
|
});
|
|
2035
2103
|
const currmtime = curr.mtimeMs;
|
|
2036
2104
|
if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {
|
|
2037
|
-
foreach(cont.listeners, (listener2) => listener2(
|
|
2105
|
+
foreach(cont.listeners, (listener2) => listener2(path18, curr));
|
|
2038
2106
|
}
|
|
2039
2107
|
})
|
|
2040
2108
|
};
|
|
@@ -2064,13 +2132,13 @@ var NodeFsHandler = class {
|
|
|
2064
2132
|
* @param listener on fs change
|
|
2065
2133
|
* @returns closer for the watcher instance
|
|
2066
2134
|
*/
|
|
2067
|
-
_watchWithNodeFs(
|
|
2135
|
+
_watchWithNodeFs(path18, listener) {
|
|
2068
2136
|
const opts = this.fsw.options;
|
|
2069
|
-
const directory = sp.dirname(
|
|
2070
|
-
const basename5 = sp.basename(
|
|
2137
|
+
const directory = sp.dirname(path18);
|
|
2138
|
+
const basename5 = sp.basename(path18);
|
|
2071
2139
|
const parent = this.fsw._getWatchedDir(directory);
|
|
2072
2140
|
parent.add(basename5);
|
|
2073
|
-
const absolutePath = sp.resolve(
|
|
2141
|
+
const absolutePath = sp.resolve(path18);
|
|
2074
2142
|
const options = {
|
|
2075
2143
|
persistent: opts.persistent
|
|
2076
2144
|
};
|
|
@@ -2080,12 +2148,12 @@ var NodeFsHandler = class {
|
|
|
2080
2148
|
if (opts.usePolling) {
|
|
2081
2149
|
const enableBin = opts.interval !== opts.binaryInterval;
|
|
2082
2150
|
options.interval = enableBin && isBinaryPath(basename5) ? opts.binaryInterval : opts.interval;
|
|
2083
|
-
closer = setFsWatchFileListener(
|
|
2151
|
+
closer = setFsWatchFileListener(path18, absolutePath, options, {
|
|
2084
2152
|
listener,
|
|
2085
2153
|
rawEmitter: this.fsw._emitRaw
|
|
2086
2154
|
});
|
|
2087
2155
|
} else {
|
|
2088
|
-
closer = setFsWatchListener(
|
|
2156
|
+
closer = setFsWatchListener(path18, absolutePath, options, {
|
|
2089
2157
|
listener,
|
|
2090
2158
|
errHandler: this._boundHandleError,
|
|
2091
2159
|
rawEmitter: this.fsw._emitRaw
|
|
@@ -2107,7 +2175,7 @@ var NodeFsHandler = class {
|
|
|
2107
2175
|
let prevStats = stats;
|
|
2108
2176
|
if (parent.has(basename5))
|
|
2109
2177
|
return;
|
|
2110
|
-
const listener = async (
|
|
2178
|
+
const listener = async (path18, newStats) => {
|
|
2111
2179
|
if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5))
|
|
2112
2180
|
return;
|
|
2113
2181
|
if (!newStats || newStats.mtimeMs === 0) {
|
|
@@ -2121,11 +2189,11 @@ var NodeFsHandler = class {
|
|
|
2121
2189
|
this.fsw._emit(EV.CHANGE, file, newStats2);
|
|
2122
2190
|
}
|
|
2123
2191
|
if ((isMacos || isLinux || isFreeBSD) && prevStats.ino !== newStats2.ino) {
|
|
2124
|
-
this.fsw._closeFile(
|
|
2192
|
+
this.fsw._closeFile(path18);
|
|
2125
2193
|
prevStats = newStats2;
|
|
2126
2194
|
const closer2 = this._watchWithNodeFs(file, listener);
|
|
2127
2195
|
if (closer2)
|
|
2128
|
-
this.fsw._addPathCloser(
|
|
2196
|
+
this.fsw._addPathCloser(path18, closer2);
|
|
2129
2197
|
} else {
|
|
2130
2198
|
prevStats = newStats2;
|
|
2131
2199
|
}
|
|
@@ -2157,7 +2225,7 @@ var NodeFsHandler = class {
|
|
|
2157
2225
|
* @param item basename of this item
|
|
2158
2226
|
* @returns true if no more processing is needed for this entry.
|
|
2159
2227
|
*/
|
|
2160
|
-
async _handleSymlink(entry, directory,
|
|
2228
|
+
async _handleSymlink(entry, directory, path18, item) {
|
|
2161
2229
|
if (this.fsw.closed) {
|
|
2162
2230
|
return;
|
|
2163
2231
|
}
|
|
@@ -2167,7 +2235,7 @@ var NodeFsHandler = class {
|
|
|
2167
2235
|
this.fsw._incrReadyCount();
|
|
2168
2236
|
let linkPath;
|
|
2169
2237
|
try {
|
|
2170
|
-
linkPath = await fsrealpath(
|
|
2238
|
+
linkPath = await fsrealpath(path18);
|
|
2171
2239
|
} catch (e) {
|
|
2172
2240
|
this.fsw._emitReady();
|
|
2173
2241
|
return true;
|
|
@@ -2177,12 +2245,12 @@ var NodeFsHandler = class {
|
|
|
2177
2245
|
if (dir.has(item)) {
|
|
2178
2246
|
if (this.fsw._symlinkPaths.get(full) !== linkPath) {
|
|
2179
2247
|
this.fsw._symlinkPaths.set(full, linkPath);
|
|
2180
|
-
this.fsw._emit(EV.CHANGE,
|
|
2248
|
+
this.fsw._emit(EV.CHANGE, path18, entry.stats);
|
|
2181
2249
|
}
|
|
2182
2250
|
} else {
|
|
2183
2251
|
dir.add(item);
|
|
2184
2252
|
this.fsw._symlinkPaths.set(full, linkPath);
|
|
2185
|
-
this.fsw._emit(EV.ADD,
|
|
2253
|
+
this.fsw._emit(EV.ADD, path18, entry.stats);
|
|
2186
2254
|
}
|
|
2187
2255
|
this.fsw._emitReady();
|
|
2188
2256
|
return true;
|
|
@@ -2212,9 +2280,9 @@ var NodeFsHandler = class {
|
|
|
2212
2280
|
return;
|
|
2213
2281
|
}
|
|
2214
2282
|
const item = entry.path;
|
|
2215
|
-
let
|
|
2283
|
+
let path18 = sp.join(directory, item);
|
|
2216
2284
|
current.add(item);
|
|
2217
|
-
if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory,
|
|
2285
|
+
if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path18, item)) {
|
|
2218
2286
|
return;
|
|
2219
2287
|
}
|
|
2220
2288
|
if (this.fsw.closed) {
|
|
@@ -2223,11 +2291,11 @@ var NodeFsHandler = class {
|
|
|
2223
2291
|
}
|
|
2224
2292
|
if (item === target || !target && !previous.has(item)) {
|
|
2225
2293
|
this.fsw._incrReadyCount();
|
|
2226
|
-
|
|
2227
|
-
this._addToNodeFs(
|
|
2294
|
+
path18 = sp.join(dir, sp.relative(dir, path18));
|
|
2295
|
+
this._addToNodeFs(path18, initialAdd, wh, depth + 1);
|
|
2228
2296
|
}
|
|
2229
2297
|
}).on(EV.ERROR, this._boundHandleError);
|
|
2230
|
-
return new Promise((
|
|
2298
|
+
return new Promise((resolve11, reject) => {
|
|
2231
2299
|
if (!stream)
|
|
2232
2300
|
return reject();
|
|
2233
2301
|
stream.once(STR_END, () => {
|
|
@@ -2236,7 +2304,7 @@ var NodeFsHandler = class {
|
|
|
2236
2304
|
return;
|
|
2237
2305
|
}
|
|
2238
2306
|
const wasThrottled = throttler ? throttler.clear() : false;
|
|
2239
|
-
|
|
2307
|
+
resolve11(void 0);
|
|
2240
2308
|
previous.getChildren().filter((item) => {
|
|
2241
2309
|
return item !== directory && !current.has(item);
|
|
2242
2310
|
}).forEach((item) => {
|
|
@@ -2293,13 +2361,13 @@ var NodeFsHandler = class {
|
|
|
2293
2361
|
* @param depth Child path actually targeted for watch
|
|
2294
2362
|
* @param target Child path actually targeted for watch
|
|
2295
2363
|
*/
|
|
2296
|
-
async _addToNodeFs(
|
|
2364
|
+
async _addToNodeFs(path18, initialAdd, priorWh, depth, target) {
|
|
2297
2365
|
const ready = this.fsw._emitReady;
|
|
2298
|
-
if (this.fsw._isIgnored(
|
|
2366
|
+
if (this.fsw._isIgnored(path18) || this.fsw.closed) {
|
|
2299
2367
|
ready();
|
|
2300
2368
|
return false;
|
|
2301
2369
|
}
|
|
2302
|
-
const wh = this.fsw._getWatchHelpers(
|
|
2370
|
+
const wh = this.fsw._getWatchHelpers(path18);
|
|
2303
2371
|
if (priorWh) {
|
|
2304
2372
|
wh.filterPath = (entry) => priorWh.filterPath(entry);
|
|
2305
2373
|
wh.filterDir = (entry) => priorWh.filterDir(entry);
|
|
@@ -2315,8 +2383,8 @@ var NodeFsHandler = class {
|
|
|
2315
2383
|
const follow = this.fsw.options.followSymlinks;
|
|
2316
2384
|
let closer;
|
|
2317
2385
|
if (stats.isDirectory()) {
|
|
2318
|
-
const absPath = sp.resolve(
|
|
2319
|
-
const targetPath = follow ? await fsrealpath(
|
|
2386
|
+
const absPath = sp.resolve(path18);
|
|
2387
|
+
const targetPath = follow ? await fsrealpath(path18) : path18;
|
|
2320
2388
|
if (this.fsw.closed)
|
|
2321
2389
|
return;
|
|
2322
2390
|
closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
|
|
@@ -2326,29 +2394,29 @@ var NodeFsHandler = class {
|
|
|
2326
2394
|
this.fsw._symlinkPaths.set(absPath, targetPath);
|
|
2327
2395
|
}
|
|
2328
2396
|
} else if (stats.isSymbolicLink()) {
|
|
2329
|
-
const targetPath = follow ? await fsrealpath(
|
|
2397
|
+
const targetPath = follow ? await fsrealpath(path18) : path18;
|
|
2330
2398
|
if (this.fsw.closed)
|
|
2331
2399
|
return;
|
|
2332
2400
|
const parent = sp.dirname(wh.watchPath);
|
|
2333
2401
|
this.fsw._getWatchedDir(parent).add(wh.watchPath);
|
|
2334
2402
|
this.fsw._emit(EV.ADD, wh.watchPath, stats);
|
|
2335
|
-
closer = await this._handleDir(parent, stats, initialAdd, depth,
|
|
2403
|
+
closer = await this._handleDir(parent, stats, initialAdd, depth, path18, wh, targetPath);
|
|
2336
2404
|
if (this.fsw.closed)
|
|
2337
2405
|
return;
|
|
2338
2406
|
if (targetPath !== void 0) {
|
|
2339
|
-
this.fsw._symlinkPaths.set(sp.resolve(
|
|
2407
|
+
this.fsw._symlinkPaths.set(sp.resolve(path18), targetPath);
|
|
2340
2408
|
}
|
|
2341
2409
|
} else {
|
|
2342
2410
|
closer = this._handleFile(wh.watchPath, stats, initialAdd);
|
|
2343
2411
|
}
|
|
2344
2412
|
ready();
|
|
2345
2413
|
if (closer)
|
|
2346
|
-
this.fsw._addPathCloser(
|
|
2414
|
+
this.fsw._addPathCloser(path18, closer);
|
|
2347
2415
|
return false;
|
|
2348
2416
|
} catch (error) {
|
|
2349
2417
|
if (this.fsw._handleError(error)) {
|
|
2350
2418
|
ready();
|
|
2351
|
-
return
|
|
2419
|
+
return path18;
|
|
2352
2420
|
}
|
|
2353
2421
|
}
|
|
2354
2422
|
}
|
|
@@ -2391,24 +2459,24 @@ function createPattern(matcher) {
|
|
|
2391
2459
|
}
|
|
2392
2460
|
return () => false;
|
|
2393
2461
|
}
|
|
2394
|
-
function normalizePath(
|
|
2395
|
-
if (typeof
|
|
2462
|
+
function normalizePath(path18) {
|
|
2463
|
+
if (typeof path18 !== "string")
|
|
2396
2464
|
throw new Error("string expected");
|
|
2397
|
-
|
|
2398
|
-
|
|
2465
|
+
path18 = sp2.normalize(path18);
|
|
2466
|
+
path18 = path18.replace(/\\/g, "/");
|
|
2399
2467
|
let prepend = false;
|
|
2400
|
-
if (
|
|
2468
|
+
if (path18.startsWith("//"))
|
|
2401
2469
|
prepend = true;
|
|
2402
|
-
|
|
2470
|
+
path18 = path18.replace(DOUBLE_SLASH_RE, "/");
|
|
2403
2471
|
if (prepend)
|
|
2404
|
-
|
|
2405
|
-
return
|
|
2472
|
+
path18 = "/" + path18;
|
|
2473
|
+
return path18;
|
|
2406
2474
|
}
|
|
2407
2475
|
function matchPatterns(patterns, testString, stats) {
|
|
2408
|
-
const
|
|
2476
|
+
const path18 = normalizePath(testString);
|
|
2409
2477
|
for (let index = 0; index < patterns.length; index++) {
|
|
2410
2478
|
const pattern = patterns[index];
|
|
2411
|
-
if (pattern(
|
|
2479
|
+
if (pattern(path18, stats)) {
|
|
2412
2480
|
return true;
|
|
2413
2481
|
}
|
|
2414
2482
|
}
|
|
@@ -2446,19 +2514,19 @@ var toUnix = (string) => {
|
|
|
2446
2514
|
}
|
|
2447
2515
|
return str;
|
|
2448
2516
|
};
|
|
2449
|
-
var normalizePathToUnix = (
|
|
2450
|
-
var normalizeIgnored = (cwd = "") => (
|
|
2451
|
-
if (typeof
|
|
2452
|
-
return normalizePathToUnix(sp2.isAbsolute(
|
|
2517
|
+
var normalizePathToUnix = (path18) => toUnix(sp2.normalize(toUnix(path18)));
|
|
2518
|
+
var normalizeIgnored = (cwd = "") => (path18) => {
|
|
2519
|
+
if (typeof path18 === "string") {
|
|
2520
|
+
return normalizePathToUnix(sp2.isAbsolute(path18) ? path18 : sp2.join(cwd, path18));
|
|
2453
2521
|
} else {
|
|
2454
|
-
return
|
|
2522
|
+
return path18;
|
|
2455
2523
|
}
|
|
2456
2524
|
};
|
|
2457
|
-
var getAbsolutePath = (
|
|
2458
|
-
if (sp2.isAbsolute(
|
|
2459
|
-
return
|
|
2525
|
+
var getAbsolutePath = (path18, cwd) => {
|
|
2526
|
+
if (sp2.isAbsolute(path18)) {
|
|
2527
|
+
return path18;
|
|
2460
2528
|
}
|
|
2461
|
-
return sp2.join(cwd,
|
|
2529
|
+
return sp2.join(cwd, path18);
|
|
2462
2530
|
};
|
|
2463
2531
|
var EMPTY_SET = Object.freeze(/* @__PURE__ */ new Set());
|
|
2464
2532
|
var DirEntry = class {
|
|
@@ -2523,10 +2591,10 @@ var WatchHelper = class {
|
|
|
2523
2591
|
dirParts;
|
|
2524
2592
|
followSymlinks;
|
|
2525
2593
|
statMethod;
|
|
2526
|
-
constructor(
|
|
2594
|
+
constructor(path18, follow, fsw) {
|
|
2527
2595
|
this.fsw = fsw;
|
|
2528
|
-
const watchPath =
|
|
2529
|
-
this.path =
|
|
2596
|
+
const watchPath = path18;
|
|
2597
|
+
this.path = path18 = path18.replace(REPLACER_RE, "");
|
|
2530
2598
|
this.watchPath = watchPath;
|
|
2531
2599
|
this.fullWatchPath = sp2.resolve(watchPath);
|
|
2532
2600
|
this.dirParts = [];
|
|
@@ -2666,20 +2734,20 @@ var FSWatcher = class extends EventEmitter {
|
|
|
2666
2734
|
this._closePromise = void 0;
|
|
2667
2735
|
let paths = unifyPaths(paths_);
|
|
2668
2736
|
if (cwd) {
|
|
2669
|
-
paths = paths.map((
|
|
2670
|
-
const absPath = getAbsolutePath(
|
|
2737
|
+
paths = paths.map((path18) => {
|
|
2738
|
+
const absPath = getAbsolutePath(path18, cwd);
|
|
2671
2739
|
return absPath;
|
|
2672
2740
|
});
|
|
2673
2741
|
}
|
|
2674
|
-
paths.forEach((
|
|
2675
|
-
this._removeIgnoredPath(
|
|
2742
|
+
paths.forEach((path18) => {
|
|
2743
|
+
this._removeIgnoredPath(path18);
|
|
2676
2744
|
});
|
|
2677
2745
|
this._userIgnored = void 0;
|
|
2678
2746
|
if (!this._readyCount)
|
|
2679
2747
|
this._readyCount = 0;
|
|
2680
2748
|
this._readyCount += paths.length;
|
|
2681
|
-
Promise.all(paths.map(async (
|
|
2682
|
-
const res = await this._nodeFsHandler._addToNodeFs(
|
|
2749
|
+
Promise.all(paths.map(async (path18) => {
|
|
2750
|
+
const res = await this._nodeFsHandler._addToNodeFs(path18, !_internal, void 0, 0, _origAdd);
|
|
2683
2751
|
if (res)
|
|
2684
2752
|
this._emitReady();
|
|
2685
2753
|
return res;
|
|
@@ -2701,17 +2769,17 @@ var FSWatcher = class extends EventEmitter {
|
|
|
2701
2769
|
return this;
|
|
2702
2770
|
const paths = unifyPaths(paths_);
|
|
2703
2771
|
const { cwd } = this.options;
|
|
2704
|
-
paths.forEach((
|
|
2705
|
-
if (!sp2.isAbsolute(
|
|
2772
|
+
paths.forEach((path18) => {
|
|
2773
|
+
if (!sp2.isAbsolute(path18) && !this._closers.has(path18)) {
|
|
2706
2774
|
if (cwd)
|
|
2707
|
-
|
|
2708
|
-
|
|
2775
|
+
path18 = sp2.join(cwd, path18);
|
|
2776
|
+
path18 = sp2.resolve(path18);
|
|
2709
2777
|
}
|
|
2710
|
-
this._closePath(
|
|
2711
|
-
this._addIgnoredPath(
|
|
2712
|
-
if (this._watched.has(
|
|
2778
|
+
this._closePath(path18);
|
|
2779
|
+
this._addIgnoredPath(path18);
|
|
2780
|
+
if (this._watched.has(path18)) {
|
|
2713
2781
|
this._addIgnoredPath({
|
|
2714
|
-
path:
|
|
2782
|
+
path: path18,
|
|
2715
2783
|
recursive: true
|
|
2716
2784
|
});
|
|
2717
2785
|
}
|
|
@@ -2775,38 +2843,38 @@ var FSWatcher = class extends EventEmitter {
|
|
|
2775
2843
|
* @param stats arguments to be passed with event
|
|
2776
2844
|
* @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag
|
|
2777
2845
|
*/
|
|
2778
|
-
async _emit(event,
|
|
2846
|
+
async _emit(event, path18, stats) {
|
|
2779
2847
|
if (this.closed)
|
|
2780
2848
|
return;
|
|
2781
2849
|
const opts = this.options;
|
|
2782
2850
|
if (isWindows)
|
|
2783
|
-
|
|
2851
|
+
path18 = sp2.normalize(path18);
|
|
2784
2852
|
if (opts.cwd)
|
|
2785
|
-
|
|
2786
|
-
const args = [
|
|
2853
|
+
path18 = sp2.relative(opts.cwd, path18);
|
|
2854
|
+
const args = [path18];
|
|
2787
2855
|
if (stats != null)
|
|
2788
2856
|
args.push(stats);
|
|
2789
2857
|
const awf = opts.awaitWriteFinish;
|
|
2790
2858
|
let pw;
|
|
2791
|
-
if (awf && (pw = this._pendingWrites.get(
|
|
2859
|
+
if (awf && (pw = this._pendingWrites.get(path18))) {
|
|
2792
2860
|
pw.lastChange = /* @__PURE__ */ new Date();
|
|
2793
2861
|
return this;
|
|
2794
2862
|
}
|
|
2795
2863
|
if (opts.atomic) {
|
|
2796
2864
|
if (event === EVENTS.UNLINK) {
|
|
2797
|
-
this._pendingUnlinks.set(
|
|
2865
|
+
this._pendingUnlinks.set(path18, [event, ...args]);
|
|
2798
2866
|
setTimeout(() => {
|
|
2799
|
-
this._pendingUnlinks.forEach((entry,
|
|
2867
|
+
this._pendingUnlinks.forEach((entry, path19) => {
|
|
2800
2868
|
this.emit(...entry);
|
|
2801
2869
|
this.emit(EVENTS.ALL, ...entry);
|
|
2802
|
-
this._pendingUnlinks.delete(
|
|
2870
|
+
this._pendingUnlinks.delete(path19);
|
|
2803
2871
|
});
|
|
2804
2872
|
}, typeof opts.atomic === "number" ? opts.atomic : 100);
|
|
2805
2873
|
return this;
|
|
2806
2874
|
}
|
|
2807
|
-
if (event === EVENTS.ADD && this._pendingUnlinks.has(
|
|
2875
|
+
if (event === EVENTS.ADD && this._pendingUnlinks.has(path18)) {
|
|
2808
2876
|
event = EVENTS.CHANGE;
|
|
2809
|
-
this._pendingUnlinks.delete(
|
|
2877
|
+
this._pendingUnlinks.delete(path18);
|
|
2810
2878
|
}
|
|
2811
2879
|
}
|
|
2812
2880
|
if (awf && (event === EVENTS.ADD || event === EVENTS.CHANGE) && this._readyEmitted) {
|
|
@@ -2824,16 +2892,16 @@ var FSWatcher = class extends EventEmitter {
|
|
|
2824
2892
|
this.emitWithAll(event, args);
|
|
2825
2893
|
}
|
|
2826
2894
|
};
|
|
2827
|
-
this._awaitWriteFinish(
|
|
2895
|
+
this._awaitWriteFinish(path18, awf.stabilityThreshold, event, awfEmit);
|
|
2828
2896
|
return this;
|
|
2829
2897
|
}
|
|
2830
2898
|
if (event === EVENTS.CHANGE) {
|
|
2831
|
-
const isThrottled = !this._throttle(EVENTS.CHANGE,
|
|
2899
|
+
const isThrottled = !this._throttle(EVENTS.CHANGE, path18, 50);
|
|
2832
2900
|
if (isThrottled)
|
|
2833
2901
|
return this;
|
|
2834
2902
|
}
|
|
2835
2903
|
if (opts.alwaysStat && stats === void 0 && (event === EVENTS.ADD || event === EVENTS.ADD_DIR || event === EVENTS.CHANGE)) {
|
|
2836
|
-
const fullPath = opts.cwd ? sp2.join(opts.cwd,
|
|
2904
|
+
const fullPath = opts.cwd ? sp2.join(opts.cwd, path18) : path18;
|
|
2837
2905
|
let stats2;
|
|
2838
2906
|
try {
|
|
2839
2907
|
stats2 = await stat3(fullPath);
|
|
@@ -2864,23 +2932,23 @@ var FSWatcher = class extends EventEmitter {
|
|
|
2864
2932
|
* @param timeout duration of time to suppress duplicate actions
|
|
2865
2933
|
* @returns tracking object or false if action should be suppressed
|
|
2866
2934
|
*/
|
|
2867
|
-
_throttle(actionType,
|
|
2935
|
+
_throttle(actionType, path18, timeout) {
|
|
2868
2936
|
if (!this._throttled.has(actionType)) {
|
|
2869
2937
|
this._throttled.set(actionType, /* @__PURE__ */ new Map());
|
|
2870
2938
|
}
|
|
2871
2939
|
const action = this._throttled.get(actionType);
|
|
2872
2940
|
if (!action)
|
|
2873
2941
|
throw new Error("invalid throttle");
|
|
2874
|
-
const actionPath = action.get(
|
|
2942
|
+
const actionPath = action.get(path18);
|
|
2875
2943
|
if (actionPath) {
|
|
2876
2944
|
actionPath.count++;
|
|
2877
2945
|
return false;
|
|
2878
2946
|
}
|
|
2879
2947
|
let timeoutObject;
|
|
2880
2948
|
const clear = () => {
|
|
2881
|
-
const item = action.get(
|
|
2949
|
+
const item = action.get(path18);
|
|
2882
2950
|
const count = item ? item.count : 0;
|
|
2883
|
-
action.delete(
|
|
2951
|
+
action.delete(path18);
|
|
2884
2952
|
clearTimeout(timeoutObject);
|
|
2885
2953
|
if (item)
|
|
2886
2954
|
clearTimeout(item.timeoutObject);
|
|
@@ -2888,7 +2956,7 @@ var FSWatcher = class extends EventEmitter {
|
|
|
2888
2956
|
};
|
|
2889
2957
|
timeoutObject = setTimeout(clear, timeout);
|
|
2890
2958
|
const thr = { timeoutObject, clear, count: 0 };
|
|
2891
|
-
action.set(
|
|
2959
|
+
action.set(path18, thr);
|
|
2892
2960
|
return thr;
|
|
2893
2961
|
}
|
|
2894
2962
|
_incrReadyCount() {
|
|
@@ -2902,44 +2970,44 @@ var FSWatcher = class extends EventEmitter {
|
|
|
2902
2970
|
* @param event
|
|
2903
2971
|
* @param awfEmit Callback to be called when ready for event to be emitted.
|
|
2904
2972
|
*/
|
|
2905
|
-
_awaitWriteFinish(
|
|
2973
|
+
_awaitWriteFinish(path18, threshold, event, awfEmit) {
|
|
2906
2974
|
const awf = this.options.awaitWriteFinish;
|
|
2907
2975
|
if (typeof awf !== "object")
|
|
2908
2976
|
return;
|
|
2909
2977
|
const pollInterval = awf.pollInterval;
|
|
2910
2978
|
let timeoutHandler;
|
|
2911
|
-
let fullPath =
|
|
2912
|
-
if (this.options.cwd && !sp2.isAbsolute(
|
|
2913
|
-
fullPath = sp2.join(this.options.cwd,
|
|
2979
|
+
let fullPath = path18;
|
|
2980
|
+
if (this.options.cwd && !sp2.isAbsolute(path18)) {
|
|
2981
|
+
fullPath = sp2.join(this.options.cwd, path18);
|
|
2914
2982
|
}
|
|
2915
2983
|
const now = /* @__PURE__ */ new Date();
|
|
2916
2984
|
const writes = this._pendingWrites;
|
|
2917
2985
|
function awaitWriteFinishFn(prevStat) {
|
|
2918
2986
|
statcb(fullPath, (err, curStat) => {
|
|
2919
|
-
if (err || !writes.has(
|
|
2987
|
+
if (err || !writes.has(path18)) {
|
|
2920
2988
|
if (err && err.code !== "ENOENT")
|
|
2921
2989
|
awfEmit(err);
|
|
2922
2990
|
return;
|
|
2923
2991
|
}
|
|
2924
2992
|
const now2 = Number(/* @__PURE__ */ new Date());
|
|
2925
2993
|
if (prevStat && curStat.size !== prevStat.size) {
|
|
2926
|
-
writes.get(
|
|
2994
|
+
writes.get(path18).lastChange = now2;
|
|
2927
2995
|
}
|
|
2928
|
-
const pw = writes.get(
|
|
2996
|
+
const pw = writes.get(path18);
|
|
2929
2997
|
const df = now2 - pw.lastChange;
|
|
2930
2998
|
if (df >= threshold) {
|
|
2931
|
-
writes.delete(
|
|
2999
|
+
writes.delete(path18);
|
|
2932
3000
|
awfEmit(void 0, curStat);
|
|
2933
3001
|
} else {
|
|
2934
3002
|
timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);
|
|
2935
3003
|
}
|
|
2936
3004
|
});
|
|
2937
3005
|
}
|
|
2938
|
-
if (!writes.has(
|
|
2939
|
-
writes.set(
|
|
3006
|
+
if (!writes.has(path18)) {
|
|
3007
|
+
writes.set(path18, {
|
|
2940
3008
|
lastChange: now,
|
|
2941
3009
|
cancelWait: () => {
|
|
2942
|
-
writes.delete(
|
|
3010
|
+
writes.delete(path18);
|
|
2943
3011
|
clearTimeout(timeoutHandler);
|
|
2944
3012
|
return event;
|
|
2945
3013
|
}
|
|
@@ -2950,8 +3018,8 @@ var FSWatcher = class extends EventEmitter {
|
|
|
2950
3018
|
/**
|
|
2951
3019
|
* Determines whether user has asked to ignore this path.
|
|
2952
3020
|
*/
|
|
2953
|
-
_isIgnored(
|
|
2954
|
-
if (this.options.atomic && DOT_RE.test(
|
|
3021
|
+
_isIgnored(path18, stats) {
|
|
3022
|
+
if (this.options.atomic && DOT_RE.test(path18))
|
|
2955
3023
|
return true;
|
|
2956
3024
|
if (!this._userIgnored) {
|
|
2957
3025
|
const { cwd } = this.options;
|
|
@@ -2961,17 +3029,17 @@ var FSWatcher = class extends EventEmitter {
|
|
|
2961
3029
|
const list = [...ignoredPaths.map(normalizeIgnored(cwd)), ...ignored];
|
|
2962
3030
|
this._userIgnored = anymatch(list, void 0);
|
|
2963
3031
|
}
|
|
2964
|
-
return this._userIgnored(
|
|
3032
|
+
return this._userIgnored(path18, stats);
|
|
2965
3033
|
}
|
|
2966
|
-
_isntIgnored(
|
|
2967
|
-
return !this._isIgnored(
|
|
3034
|
+
_isntIgnored(path18, stat4) {
|
|
3035
|
+
return !this._isIgnored(path18, stat4);
|
|
2968
3036
|
}
|
|
2969
3037
|
/**
|
|
2970
3038
|
* Provides a set of common helpers and properties relating to symlink handling.
|
|
2971
3039
|
* @param path file or directory pattern being watched
|
|
2972
3040
|
*/
|
|
2973
|
-
_getWatchHelpers(
|
|
2974
|
-
return new WatchHelper(
|
|
3041
|
+
_getWatchHelpers(path18) {
|
|
3042
|
+
return new WatchHelper(path18, this.options.followSymlinks, this);
|
|
2975
3043
|
}
|
|
2976
3044
|
// Directory helpers
|
|
2977
3045
|
// -----------------
|
|
@@ -3003,63 +3071,63 @@ var FSWatcher = class extends EventEmitter {
|
|
|
3003
3071
|
* @param item base path of item/directory
|
|
3004
3072
|
*/
|
|
3005
3073
|
_remove(directory, item, isDirectory) {
|
|
3006
|
-
const
|
|
3007
|
-
const fullPath = sp2.resolve(
|
|
3008
|
-
isDirectory = isDirectory != null ? isDirectory : this._watched.has(
|
|
3009
|
-
if (!this._throttle("remove",
|
|
3074
|
+
const path18 = sp2.join(directory, item);
|
|
3075
|
+
const fullPath = sp2.resolve(path18);
|
|
3076
|
+
isDirectory = isDirectory != null ? isDirectory : this._watched.has(path18) || this._watched.has(fullPath);
|
|
3077
|
+
if (!this._throttle("remove", path18, 100))
|
|
3010
3078
|
return;
|
|
3011
3079
|
if (!isDirectory && this._watched.size === 1) {
|
|
3012
3080
|
this.add(directory, item, true);
|
|
3013
3081
|
}
|
|
3014
|
-
const wp = this._getWatchedDir(
|
|
3082
|
+
const wp = this._getWatchedDir(path18);
|
|
3015
3083
|
const nestedDirectoryChildren = wp.getChildren();
|
|
3016
|
-
nestedDirectoryChildren.forEach((nested) => this._remove(
|
|
3084
|
+
nestedDirectoryChildren.forEach((nested) => this._remove(path18, nested));
|
|
3017
3085
|
const parent = this._getWatchedDir(directory);
|
|
3018
3086
|
const wasTracked = parent.has(item);
|
|
3019
3087
|
parent.remove(item);
|
|
3020
3088
|
if (this._symlinkPaths.has(fullPath)) {
|
|
3021
3089
|
this._symlinkPaths.delete(fullPath);
|
|
3022
3090
|
}
|
|
3023
|
-
let relPath =
|
|
3091
|
+
let relPath = path18;
|
|
3024
3092
|
if (this.options.cwd)
|
|
3025
|
-
relPath = sp2.relative(this.options.cwd,
|
|
3093
|
+
relPath = sp2.relative(this.options.cwd, path18);
|
|
3026
3094
|
if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
|
|
3027
3095
|
const event = this._pendingWrites.get(relPath).cancelWait();
|
|
3028
3096
|
if (event === EVENTS.ADD)
|
|
3029
3097
|
return;
|
|
3030
3098
|
}
|
|
3031
|
-
this._watched.delete(
|
|
3099
|
+
this._watched.delete(path18);
|
|
3032
3100
|
this._watched.delete(fullPath);
|
|
3033
3101
|
const eventName = isDirectory ? EVENTS.UNLINK_DIR : EVENTS.UNLINK;
|
|
3034
|
-
if (wasTracked && !this._isIgnored(
|
|
3035
|
-
this._emit(eventName,
|
|
3036
|
-
this._closePath(
|
|
3102
|
+
if (wasTracked && !this._isIgnored(path18))
|
|
3103
|
+
this._emit(eventName, path18);
|
|
3104
|
+
this._closePath(path18);
|
|
3037
3105
|
}
|
|
3038
3106
|
/**
|
|
3039
3107
|
* Closes all watchers for a path
|
|
3040
3108
|
*/
|
|
3041
|
-
_closePath(
|
|
3042
|
-
this._closeFile(
|
|
3043
|
-
const dir = sp2.dirname(
|
|
3044
|
-
this._getWatchedDir(dir).remove(sp2.basename(
|
|
3109
|
+
_closePath(path18) {
|
|
3110
|
+
this._closeFile(path18);
|
|
3111
|
+
const dir = sp2.dirname(path18);
|
|
3112
|
+
this._getWatchedDir(dir).remove(sp2.basename(path18));
|
|
3045
3113
|
}
|
|
3046
3114
|
/**
|
|
3047
3115
|
* Closes only file-specific watchers
|
|
3048
3116
|
*/
|
|
3049
|
-
_closeFile(
|
|
3050
|
-
const closers = this._closers.get(
|
|
3117
|
+
_closeFile(path18) {
|
|
3118
|
+
const closers = this._closers.get(path18);
|
|
3051
3119
|
if (!closers)
|
|
3052
3120
|
return;
|
|
3053
3121
|
closers.forEach((closer) => closer());
|
|
3054
|
-
this._closers.delete(
|
|
3122
|
+
this._closers.delete(path18);
|
|
3055
3123
|
}
|
|
3056
|
-
_addPathCloser(
|
|
3124
|
+
_addPathCloser(path18, closer) {
|
|
3057
3125
|
if (!closer)
|
|
3058
3126
|
return;
|
|
3059
|
-
let list = this._closers.get(
|
|
3127
|
+
let list = this._closers.get(path18);
|
|
3060
3128
|
if (!list) {
|
|
3061
3129
|
list = [];
|
|
3062
|
-
this._closers.set(
|
|
3130
|
+
this._closers.set(path18, list);
|
|
3063
3131
|
}
|
|
3064
3132
|
list.push(closer);
|
|
3065
3133
|
}
|
|
@@ -3088,13 +3156,13 @@ function watch(paths, options = {}) {
|
|
|
3088
3156
|
}
|
|
3089
3157
|
var chokidar_default = { watch, FSWatcher };
|
|
3090
3158
|
|
|
3091
|
-
// src/watcher/
|
|
3092
|
-
import * as
|
|
3159
|
+
// src/watcher/file-watcher.ts
|
|
3160
|
+
import * as path8 from "path";
|
|
3093
3161
|
|
|
3094
3162
|
// src/utils/files.ts
|
|
3095
3163
|
var import_ignore = __toESM(require_ignore(), 1);
|
|
3096
|
-
import { existsSync as
|
|
3097
|
-
import * as
|
|
3164
|
+
import { existsSync as existsSync5, readFileSync as readFileSync4, promises as fsPromises } from "fs";
|
|
3165
|
+
import * as path7 from "path";
|
|
3098
3166
|
var PROJECT_MARKERS = [
|
|
3099
3167
|
".git",
|
|
3100
3168
|
"package.json",
|
|
@@ -3113,7 +3181,7 @@ var PROJECT_MARKERS = [
|
|
|
3113
3181
|
];
|
|
3114
3182
|
function hasProjectMarker(projectRoot) {
|
|
3115
3183
|
for (const marker of PROJECT_MARKERS) {
|
|
3116
|
-
if (
|
|
3184
|
+
if (existsSync5(path7.join(projectRoot, marker))) {
|
|
3117
3185
|
return true;
|
|
3118
3186
|
}
|
|
3119
3187
|
}
|
|
@@ -3139,23 +3207,17 @@ function createIgnoreFilter(projectRoot) {
|
|
|
3139
3207
|
"**/*build*/**"
|
|
3140
3208
|
];
|
|
3141
3209
|
ig.add(defaultIgnores);
|
|
3142
|
-
const gitignorePath =
|
|
3143
|
-
if (
|
|
3144
|
-
const gitignoreContent =
|
|
3210
|
+
const gitignorePath = path7.join(projectRoot, ".gitignore");
|
|
3211
|
+
if (existsSync5(gitignorePath)) {
|
|
3212
|
+
const gitignoreContent = readFileSync4(gitignorePath, "utf-8");
|
|
3145
3213
|
ig.add(gitignoreContent);
|
|
3146
3214
|
}
|
|
3147
3215
|
return ig;
|
|
3148
3216
|
}
|
|
3149
3217
|
function shouldIncludeFile(filePath, projectRoot, includePatterns, excludePatterns, ignoreFilter) {
|
|
3150
|
-
const relativePath =
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
if (part.startsWith(".") && part !== "." && part !== "..") {
|
|
3154
|
-
return false;
|
|
3155
|
-
}
|
|
3156
|
-
if (part.toLowerCase().includes("build")) {
|
|
3157
|
-
return false;
|
|
3158
|
-
}
|
|
3218
|
+
const relativePath = path7.relative(projectRoot, filePath);
|
|
3219
|
+
if (hasFilteredPathSegment(relativePath, path7.sep)) {
|
|
3220
|
+
return false;
|
|
3159
3221
|
}
|
|
3160
3222
|
if (ignoreFilter.ignores(relativePath)) {
|
|
3161
3223
|
return false;
|
|
@@ -3192,15 +3254,15 @@ async function* walkDirectory(dir, projectRoot, includePatterns, excludePatterns
|
|
|
3192
3254
|
const filesInDir = [];
|
|
3193
3255
|
const subdirs = [];
|
|
3194
3256
|
for (const entry of entries) {
|
|
3195
|
-
const fullPath =
|
|
3196
|
-
const relativePath =
|
|
3197
|
-
if (
|
|
3257
|
+
const fullPath = path7.join(dir, entry.name);
|
|
3258
|
+
const relativePath = path7.relative(projectRoot, fullPath);
|
|
3259
|
+
if (isHiddenPathSegment(entry.name)) {
|
|
3198
3260
|
if (entry.isDirectory()) {
|
|
3199
3261
|
skipped.push({ path: relativePath, reason: "excluded" });
|
|
3200
3262
|
}
|
|
3201
3263
|
continue;
|
|
3202
3264
|
}
|
|
3203
|
-
if (entry.isDirectory() && entry.name
|
|
3265
|
+
if (entry.isDirectory() && isBuildPathSegment(entry.name)) {
|
|
3204
3266
|
skipped.push({ path: relativePath, reason: "excluded" });
|
|
3205
3267
|
continue;
|
|
3206
3268
|
}
|
|
@@ -3242,7 +3304,7 @@ async function* walkDirectory(dir, projectRoot, includePatterns, excludePatterns
|
|
|
3242
3304
|
yield f;
|
|
3243
3305
|
}
|
|
3244
3306
|
for (let i = options.maxFilesPerDirectory; i < filesInDir.length; i++) {
|
|
3245
|
-
skipped.push({ path:
|
|
3307
|
+
skipped.push({ path: path7.relative(projectRoot, filesInDir[i].path), reason: "excluded" });
|
|
3246
3308
|
}
|
|
3247
3309
|
const canRecurse = options.maxDepth === -1 || currentDepth < options.maxDepth;
|
|
3248
3310
|
if (canRecurse) {
|
|
@@ -3282,8 +3344,8 @@ async function collectFiles(projectRoot, includePatterns, excludePatterns, maxFi
|
|
|
3282
3344
|
if (additionalRoots && additionalRoots.length > 0) {
|
|
3283
3345
|
const normalizedRoots = /* @__PURE__ */ new Set();
|
|
3284
3346
|
for (const kbRoot of additionalRoots) {
|
|
3285
|
-
const resolved =
|
|
3286
|
-
|
|
3347
|
+
const resolved = path7.normalize(
|
|
3348
|
+
path7.isAbsolute(kbRoot) ? kbRoot : path7.resolve(projectRoot, kbRoot)
|
|
3287
3349
|
);
|
|
3288
3350
|
normalizedRoots.add(resolved);
|
|
3289
3351
|
}
|
|
@@ -3316,7 +3378,7 @@ async function collectFiles(projectRoot, includePatterns, excludePatterns, maxFi
|
|
|
3316
3378
|
return { files, skipped };
|
|
3317
3379
|
}
|
|
3318
3380
|
|
|
3319
|
-
// src/watcher/
|
|
3381
|
+
// src/watcher/file-watcher.ts
|
|
3320
3382
|
var FileWatcher = class {
|
|
3321
3383
|
watcher = null;
|
|
3322
3384
|
projectRoot;
|
|
@@ -3337,16 +3399,10 @@ var FileWatcher = class {
|
|
|
3337
3399
|
const ignoreFilter = createIgnoreFilter(this.projectRoot);
|
|
3338
3400
|
this.watcher = chokidar_default.watch(this.projectRoot, {
|
|
3339
3401
|
ignored: (filePath) => {
|
|
3340
|
-
const relativePath =
|
|
3402
|
+
const relativePath = path8.relative(this.projectRoot, filePath);
|
|
3341
3403
|
if (!relativePath) return false;
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
if (part.startsWith(".") && part !== "." && part !== "..") {
|
|
3345
|
-
return true;
|
|
3346
|
-
}
|
|
3347
|
-
if (part.toLowerCase().includes("build")) {
|
|
3348
|
-
return true;
|
|
3349
|
-
}
|
|
3404
|
+
if (hasFilteredPathSegment(relativePath, path8.sep)) {
|
|
3405
|
+
return true;
|
|
3350
3406
|
}
|
|
3351
3407
|
if (ignoreFilter.ignores(relativePath)) {
|
|
3352
3408
|
return true;
|
|
@@ -3391,7 +3447,7 @@ var FileWatcher = class {
|
|
|
3391
3447
|
return;
|
|
3392
3448
|
}
|
|
3393
3449
|
const changes = Array.from(this.pendingChanges.entries()).map(
|
|
3394
|
-
([
|
|
3450
|
+
([path18, type]) => ({ path: path18, type })
|
|
3395
3451
|
);
|
|
3396
3452
|
this.pendingChanges.clear();
|
|
3397
3453
|
try {
|
|
@@ -3416,6 +3472,9 @@ var FileWatcher = class {
|
|
|
3416
3472
|
return this.watcher !== null;
|
|
3417
3473
|
}
|
|
3418
3474
|
};
|
|
3475
|
+
|
|
3476
|
+
// src/watcher/git-head-watcher.ts
|
|
3477
|
+
import * as path9 from "path";
|
|
3419
3478
|
var GitHeadWatcher = class {
|
|
3420
3479
|
watcher = null;
|
|
3421
3480
|
projectRoot;
|
|
@@ -3437,7 +3496,7 @@ var GitHeadWatcher = class {
|
|
|
3437
3496
|
this.onBranchChange = handler;
|
|
3438
3497
|
this.currentBranch = getCurrentBranch(this.projectRoot);
|
|
3439
3498
|
const headPath = getHeadPath(this.projectRoot);
|
|
3440
|
-
const refsPath =
|
|
3499
|
+
const refsPath = path9.join(this.projectRoot, ".git", "refs", "heads");
|
|
3441
3500
|
this.watcher = chokidar_default.watch([headPath, refsPath], {
|
|
3442
3501
|
persistent: true,
|
|
3443
3502
|
ignoreInitial: true,
|
|
@@ -3489,6 +3548,8 @@ var GitHeadWatcher = class {
|
|
|
3489
3548
|
return this.watcher !== null;
|
|
3490
3549
|
}
|
|
3491
3550
|
};
|
|
3551
|
+
|
|
3552
|
+
// src/watcher/index.ts
|
|
3492
3553
|
function createWatcherWithIndexer(getIndexer2, projectRoot, config) {
|
|
3493
3554
|
const fileWatcher = new FileWatcher(projectRoot, config);
|
|
3494
3555
|
fileWatcher.start(async (changes) => {
|
|
@@ -3522,8 +3583,8 @@ function createWatcherWithIndexer(getIndexer2, projectRoot, config) {
|
|
|
3522
3583
|
import { tool } from "@opencode-ai/plugin";
|
|
3523
3584
|
|
|
3524
3585
|
// src/indexer/index.ts
|
|
3525
|
-
import { existsSync as
|
|
3526
|
-
import * as
|
|
3586
|
+
import { existsSync as existsSync7, readFileSync as readFileSync6, writeFileSync as writeFileSync2, renameSync, unlinkSync, mkdirSync as mkdirSync2, promises as fsPromises2 } from "fs";
|
|
3587
|
+
import * as path12 from "path";
|
|
3527
3588
|
import { performance as performance2 } from "perf_hooks";
|
|
3528
3589
|
|
|
3529
3590
|
// node_modules/eventemitter3/index.mjs
|
|
@@ -3548,7 +3609,7 @@ function pTimeout(promise, options) {
|
|
|
3548
3609
|
} = options;
|
|
3549
3610
|
let timer;
|
|
3550
3611
|
let abortHandler;
|
|
3551
|
-
const wrappedPromise = new Promise((
|
|
3612
|
+
const wrappedPromise = new Promise((resolve11, reject) => {
|
|
3552
3613
|
if (typeof milliseconds !== "number" || Math.sign(milliseconds) !== 1) {
|
|
3553
3614
|
throw new TypeError(`Expected \`milliseconds\` to be a positive number, got \`${milliseconds}\``);
|
|
3554
3615
|
}
|
|
@@ -3562,7 +3623,7 @@ function pTimeout(promise, options) {
|
|
|
3562
3623
|
};
|
|
3563
3624
|
signal.addEventListener("abort", abortHandler, { once: true });
|
|
3564
3625
|
}
|
|
3565
|
-
promise.then(
|
|
3626
|
+
promise.then(resolve11, reject);
|
|
3566
3627
|
if (milliseconds === Number.POSITIVE_INFINITY) {
|
|
3567
3628
|
return;
|
|
3568
3629
|
}
|
|
@@ -3570,7 +3631,7 @@ function pTimeout(promise, options) {
|
|
|
3570
3631
|
timer = customTimers.setTimeout.call(void 0, () => {
|
|
3571
3632
|
if (fallback) {
|
|
3572
3633
|
try {
|
|
3573
|
-
|
|
3634
|
+
resolve11(fallback());
|
|
3574
3635
|
} catch (error) {
|
|
3575
3636
|
reject(error);
|
|
3576
3637
|
}
|
|
@@ -3580,7 +3641,7 @@ function pTimeout(promise, options) {
|
|
|
3580
3641
|
promise.cancel();
|
|
3581
3642
|
}
|
|
3582
3643
|
if (message === false) {
|
|
3583
|
-
|
|
3644
|
+
resolve11();
|
|
3584
3645
|
} else if (message instanceof Error) {
|
|
3585
3646
|
reject(message);
|
|
3586
3647
|
} else {
|
|
@@ -3982,7 +4043,7 @@ var PQueue = class extends import_index.default {
|
|
|
3982
4043
|
// Assign unique ID if not provided
|
|
3983
4044
|
id: options.id ?? (this.#idAssigner++).toString()
|
|
3984
4045
|
};
|
|
3985
|
-
return new Promise((
|
|
4046
|
+
return new Promise((resolve11, reject) => {
|
|
3986
4047
|
const taskSymbol = /* @__PURE__ */ Symbol(`task-${options.id}`);
|
|
3987
4048
|
let cleanupQueueAbortHandler = () => void 0;
|
|
3988
4049
|
const run = async () => {
|
|
@@ -4022,7 +4083,7 @@ var PQueue = class extends import_index.default {
|
|
|
4022
4083
|
})]);
|
|
4023
4084
|
}
|
|
4024
4085
|
const result = await operation;
|
|
4025
|
-
|
|
4086
|
+
resolve11(result);
|
|
4026
4087
|
this.emit("completed", result);
|
|
4027
4088
|
} catch (error) {
|
|
4028
4089
|
reject(error);
|
|
@@ -4210,13 +4271,13 @@ var PQueue = class extends import_index.default {
|
|
|
4210
4271
|
});
|
|
4211
4272
|
}
|
|
4212
4273
|
async #onEvent(event, filter) {
|
|
4213
|
-
return new Promise((
|
|
4274
|
+
return new Promise((resolve11) => {
|
|
4214
4275
|
const listener = () => {
|
|
4215
4276
|
if (filter && !filter()) {
|
|
4216
4277
|
return;
|
|
4217
4278
|
}
|
|
4218
4279
|
this.off(event, listener);
|
|
4219
|
-
|
|
4280
|
+
resolve11();
|
|
4220
4281
|
};
|
|
4221
4282
|
this.on(event, listener);
|
|
4222
4283
|
});
|
|
@@ -4502,7 +4563,7 @@ async function onAttemptFailure({ error, attemptNumber, retriesConsumed, startTi
|
|
|
4502
4563
|
const finalDelay = Math.min(delayTime, remainingTime);
|
|
4503
4564
|
options.signal?.throwIfAborted();
|
|
4504
4565
|
if (finalDelay > 0) {
|
|
4505
|
-
await new Promise((
|
|
4566
|
+
await new Promise((resolve11, reject) => {
|
|
4506
4567
|
const onAbort = () => {
|
|
4507
4568
|
clearTimeout(timeoutToken);
|
|
4508
4569
|
options.signal?.removeEventListener("abort", onAbort);
|
|
@@ -4510,7 +4571,7 @@ async function onAttemptFailure({ error, attemptNumber, retriesConsumed, startTi
|
|
|
4510
4571
|
};
|
|
4511
4572
|
const timeoutToken = setTimeout(() => {
|
|
4512
4573
|
options.signal?.removeEventListener("abort", onAbort);
|
|
4513
|
-
|
|
4574
|
+
resolve11();
|
|
4514
4575
|
}, finalDelay);
|
|
4515
4576
|
if (options.unref) {
|
|
4516
4577
|
timeoutToken.unref?.();
|
|
@@ -4571,17 +4632,17 @@ async function pRetry(input, options = {}) {
|
|
|
4571
4632
|
}
|
|
4572
4633
|
|
|
4573
4634
|
// src/embeddings/detector.ts
|
|
4574
|
-
import { existsSync as
|
|
4575
|
-
import * as
|
|
4635
|
+
import { existsSync as existsSync6, readFileSync as readFileSync5 } from "fs";
|
|
4636
|
+
import * as path10 from "path";
|
|
4576
4637
|
import * as os3 from "os";
|
|
4577
4638
|
function getOpenCodeAuthPath() {
|
|
4578
|
-
return
|
|
4639
|
+
return path10.join(os3.homedir(), ".local", "share", "opencode", "auth.json");
|
|
4579
4640
|
}
|
|
4580
4641
|
function loadOpenCodeAuth() {
|
|
4581
4642
|
const authPath = getOpenCodeAuthPath();
|
|
4582
4643
|
try {
|
|
4583
|
-
if (
|
|
4584
|
-
return JSON.parse(
|
|
4644
|
+
if (existsSync6(authPath)) {
|
|
4645
|
+
return JSON.parse(readFileSync5(authPath, "utf-8"));
|
|
4585
4646
|
}
|
|
4586
4647
|
} catch {
|
|
4587
4648
|
}
|
|
@@ -4648,7 +4709,8 @@ function getGitHubCopilotCredentials() {
|
|
|
4648
4709
|
if (!copilotAuth || copilotAuth.type !== "oauth") {
|
|
4649
4710
|
return null;
|
|
4650
4711
|
}
|
|
4651
|
-
const
|
|
4712
|
+
const auth = copilotAuth;
|
|
4713
|
+
const baseUrl = auth.enterpriseUrl ? `https://copilot-api.${auth.enterpriseUrl.replace(/^https?:\/\//, "").replace(/\/$/, "")}` : "https://models.github.ai";
|
|
4652
4714
|
return {
|
|
4653
4715
|
provider: "github-copilot",
|
|
4654
4716
|
baseUrl,
|
|
@@ -4744,42 +4806,12 @@ function createCustomProviderInfo(config) {
|
|
|
4744
4806
|
};
|
|
4745
4807
|
}
|
|
4746
4808
|
|
|
4747
|
-
// src/embeddings/provider.ts
|
|
4748
|
-
var
|
|
4749
|
-
constructor(message) {
|
|
4750
|
-
super(message);
|
|
4751
|
-
this.name = "CustomProviderNonRetryableError";
|
|
4752
|
-
}
|
|
4753
|
-
};
|
|
4754
|
-
function createEmbeddingProvider(configuredProviderInfo) {
|
|
4755
|
-
switch (configuredProviderInfo.provider) {
|
|
4756
|
-
case "github-copilot":
|
|
4757
|
-
return new GitHubCopilotEmbeddingProvider(configuredProviderInfo.credentials, configuredProviderInfo.modelInfo);
|
|
4758
|
-
case "openai":
|
|
4759
|
-
return new OpenAIEmbeddingProvider(configuredProviderInfo.credentials, configuredProviderInfo.modelInfo);
|
|
4760
|
-
case "google":
|
|
4761
|
-
return new GoogleEmbeddingProvider(configuredProviderInfo.credentials, configuredProviderInfo.modelInfo);
|
|
4762
|
-
case "ollama":
|
|
4763
|
-
return new OllamaEmbeddingProvider(configuredProviderInfo.credentials, configuredProviderInfo.modelInfo);
|
|
4764
|
-
case "custom":
|
|
4765
|
-
return new CustomEmbeddingProvider(configuredProviderInfo.credentials, configuredProviderInfo.modelInfo);
|
|
4766
|
-
default: {
|
|
4767
|
-
const _exhaustive = configuredProviderInfo;
|
|
4768
|
-
throw new Error(`Unsupported embedding provider: ${_exhaustive.provider}`);
|
|
4769
|
-
}
|
|
4770
|
-
}
|
|
4771
|
-
}
|
|
4772
|
-
var GitHubCopilotEmbeddingProvider = class {
|
|
4809
|
+
// src/embeddings/provider-types.ts
|
|
4810
|
+
var BaseEmbeddingProvider = class {
|
|
4773
4811
|
constructor(credentials, modelInfo) {
|
|
4774
4812
|
this.credentials = credentials;
|
|
4775
4813
|
this.modelInfo = modelInfo;
|
|
4776
4814
|
}
|
|
4777
|
-
getToken() {
|
|
4778
|
-
if (!this.credentials.refreshToken) {
|
|
4779
|
-
throw new Error("No OAuth token available for GitHub");
|
|
4780
|
-
}
|
|
4781
|
-
return this.credentials.refreshToken;
|
|
4782
|
-
}
|
|
4783
4815
|
async embedQuery(query) {
|
|
4784
4816
|
const result = await this.embedBatch([query]);
|
|
4785
4817
|
return {
|
|
@@ -4794,69 +4826,204 @@ var GitHubCopilotEmbeddingProvider = class {
|
|
|
4794
4826
|
tokensUsed: result.totalTokensUsed
|
|
4795
4827
|
};
|
|
4796
4828
|
}
|
|
4797
|
-
async embedBatch(texts) {
|
|
4798
|
-
const token = this.getToken();
|
|
4799
|
-
const response = await fetch(`${this.credentials.baseUrl}/inference/embeddings`, {
|
|
4800
|
-
method: "POST",
|
|
4801
|
-
headers: {
|
|
4802
|
-
Authorization: `Bearer ${token}`,
|
|
4803
|
-
"Content-Type": "application/json",
|
|
4804
|
-
Accept: "application/vnd.github+json",
|
|
4805
|
-
"X-GitHub-Api-Version": "2022-11-28"
|
|
4806
|
-
},
|
|
4807
|
-
body: JSON.stringify({
|
|
4808
|
-
model: `openai/${this.modelInfo.model}`,
|
|
4809
|
-
input: texts
|
|
4810
|
-
})
|
|
4811
|
-
});
|
|
4812
|
-
if (!response.ok) {
|
|
4813
|
-
const error = await response.text();
|
|
4814
|
-
throw new Error(`GitHub Copilot embedding API error: ${response.status} - ${error}`);
|
|
4815
|
-
}
|
|
4816
|
-
const data = await response.json();
|
|
4817
|
-
return {
|
|
4818
|
-
embeddings: data.data.map((d) => d.embedding),
|
|
4819
|
-
totalTokensUsed: data.usage.total_tokens
|
|
4820
|
-
};
|
|
4821
|
-
}
|
|
4822
4829
|
getModelInfo() {
|
|
4823
4830
|
return this.modelInfo;
|
|
4824
4831
|
}
|
|
4825
4832
|
};
|
|
4826
|
-
var
|
|
4833
|
+
var CustomProviderNonRetryableError = class extends Error {
|
|
4834
|
+
constructor(message) {
|
|
4835
|
+
super(message);
|
|
4836
|
+
this.name = "CustomProviderNonRetryableError";
|
|
4837
|
+
}
|
|
4838
|
+
};
|
|
4839
|
+
|
|
4840
|
+
// src/utils/url-validation.ts
|
|
4841
|
+
var BLOCKED_METADATA_IPS = [
|
|
4842
|
+
/^169\.254\.169\.254$/,
|
|
4843
|
+
// AWS/Azure/GCP metadata
|
|
4844
|
+
/^169\.254\.170\.2$/,
|
|
4845
|
+
// AWS ECS task metadata
|
|
4846
|
+
/^fd00:ec2::254$/
|
|
4847
|
+
// AWS IMDSv2 IPv6
|
|
4848
|
+
];
|
|
4849
|
+
var BLOCKED_HOSTNAMES = /* @__PURE__ */ new Set([
|
|
4850
|
+
"metadata.google.internal",
|
|
4851
|
+
"metadata.google",
|
|
4852
|
+
"metadata.goog",
|
|
4853
|
+
"kubernetes.default.svc"
|
|
4854
|
+
]);
|
|
4855
|
+
function validateExternalUrl(urlString) {
|
|
4856
|
+
let parsed;
|
|
4857
|
+
try {
|
|
4858
|
+
parsed = new URL(urlString);
|
|
4859
|
+
} catch {
|
|
4860
|
+
return { valid: false, reason: `Invalid URL: ${sanitizeUrlForError(urlString)}` };
|
|
4861
|
+
}
|
|
4862
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
4863
|
+
return { valid: false, reason: `Blocked protocol: ${parsed.protocol}` };
|
|
4864
|
+
}
|
|
4865
|
+
const hostname = parsed.hostname.toLowerCase();
|
|
4866
|
+
if (BLOCKED_HOSTNAMES.has(hostname)) {
|
|
4867
|
+
return { valid: false, reason: `Blocked: cloud metadata service (${hostname})` };
|
|
4868
|
+
}
|
|
4869
|
+
for (const pattern of BLOCKED_METADATA_IPS) {
|
|
4870
|
+
if (pattern.test(hostname)) {
|
|
4871
|
+
return { valid: false, reason: `Blocked: cloud metadata IP (${hostname})` };
|
|
4872
|
+
}
|
|
4873
|
+
}
|
|
4874
|
+
if (/^169\.254\./.test(hostname)) {
|
|
4875
|
+
return { valid: false, reason: `Blocked: link-local address (${hostname})` };
|
|
4876
|
+
}
|
|
4877
|
+
return { valid: true };
|
|
4878
|
+
}
|
|
4879
|
+
function sanitizeUrlForError(url) {
|
|
4880
|
+
try {
|
|
4881
|
+
const parsed = new URL(url);
|
|
4882
|
+
parsed.username = "";
|
|
4883
|
+
parsed.password = "";
|
|
4884
|
+
return parsed.toString();
|
|
4885
|
+
} catch {
|
|
4886
|
+
const maxLen = 80;
|
|
4887
|
+
if (url.length > maxLen) {
|
|
4888
|
+
return url.slice(0, maxLen) + "...";
|
|
4889
|
+
}
|
|
4890
|
+
return url;
|
|
4891
|
+
}
|
|
4892
|
+
}
|
|
4893
|
+
|
|
4894
|
+
// src/embeddings/providers/custom.ts
|
|
4895
|
+
var CustomEmbeddingProvider = class extends BaseEmbeddingProvider {
|
|
4827
4896
|
constructor(credentials, modelInfo) {
|
|
4828
|
-
|
|
4829
|
-
this.modelInfo = modelInfo;
|
|
4897
|
+
super(credentials, modelInfo);
|
|
4830
4898
|
}
|
|
4831
|
-
|
|
4832
|
-
const
|
|
4833
|
-
|
|
4834
|
-
|
|
4835
|
-
|
|
4899
|
+
splitIntoRequestBatches(texts) {
|
|
4900
|
+
const maxBatchSize = this.modelInfo.maxBatchSize;
|
|
4901
|
+
if (!maxBatchSize || texts.length <= maxBatchSize) {
|
|
4902
|
+
return [texts];
|
|
4903
|
+
}
|
|
4904
|
+
const batches = [];
|
|
4905
|
+
for (let i = 0; i < texts.length; i += maxBatchSize) {
|
|
4906
|
+
batches.push(texts.slice(i, i + maxBatchSize));
|
|
4907
|
+
}
|
|
4908
|
+
return batches;
|
|
4909
|
+
}
|
|
4910
|
+
async embedRequest(texts) {
|
|
4911
|
+
if (texts.length === 0) {
|
|
4912
|
+
return {
|
|
4913
|
+
embeddings: [],
|
|
4914
|
+
totalTokensUsed: 0
|
|
4915
|
+
};
|
|
4916
|
+
}
|
|
4917
|
+
const headers = {
|
|
4918
|
+
"Content-Type": "application/json"
|
|
4836
4919
|
};
|
|
4920
|
+
if (this.credentials.apiKey) {
|
|
4921
|
+
headers.Authorization = `Bearer ${this.credentials.apiKey}`;
|
|
4922
|
+
}
|
|
4923
|
+
const baseUrl = this.credentials.baseUrl ?? "";
|
|
4924
|
+
const fullUrl = `${baseUrl}/embeddings`;
|
|
4925
|
+
const urlCheck = validateExternalUrl(fullUrl);
|
|
4926
|
+
if (!urlCheck.valid) {
|
|
4927
|
+
throw new CustomProviderNonRetryableError(
|
|
4928
|
+
`Custom embedding provider URL blocked (SSRF protection): ${urlCheck.reason}`
|
|
4929
|
+
);
|
|
4930
|
+
}
|
|
4931
|
+
const timeoutMs = this.modelInfo.timeoutMs;
|
|
4932
|
+
const controller = new AbortController();
|
|
4933
|
+
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
4934
|
+
let response;
|
|
4935
|
+
try {
|
|
4936
|
+
response = await fetch(fullUrl, {
|
|
4937
|
+
method: "POST",
|
|
4938
|
+
headers,
|
|
4939
|
+
body: JSON.stringify({
|
|
4940
|
+
model: this.modelInfo.model,
|
|
4941
|
+
input: texts
|
|
4942
|
+
}),
|
|
4943
|
+
signal: controller.signal
|
|
4944
|
+
});
|
|
4945
|
+
} catch (error) {
|
|
4946
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
4947
|
+
throw new Error(`Custom embedding API request timed out after ${timeoutMs}ms for ${sanitizeUrlForError(fullUrl)}`);
|
|
4948
|
+
}
|
|
4949
|
+
throw error;
|
|
4950
|
+
} finally {
|
|
4951
|
+
clearTimeout(timeout);
|
|
4952
|
+
}
|
|
4953
|
+
if (!response.ok) {
|
|
4954
|
+
const errorText = (await response.text()).slice(0, 500);
|
|
4955
|
+
if (response.status >= 400 && response.status < 500 && response.status !== 429) {
|
|
4956
|
+
throw new CustomProviderNonRetryableError(`Custom embedding API error (non-retryable): ${response.status} - ${errorText}`);
|
|
4957
|
+
}
|
|
4958
|
+
throw new Error(`Custom embedding API error: ${response.status} - ${errorText}`);
|
|
4959
|
+
}
|
|
4960
|
+
const data = await response.json();
|
|
4961
|
+
if (data.data && Array.isArray(data.data)) {
|
|
4962
|
+
if (data.data.length > 0) {
|
|
4963
|
+
const actualDims = data.data[0].embedding.length;
|
|
4964
|
+
if (actualDims !== this.modelInfo.dimensions) {
|
|
4965
|
+
throw new Error(
|
|
4966
|
+
`Dimension mismatch: customProvider.dimensions is ${this.modelInfo.dimensions}, but the API returned vectors with ${actualDims} dimensions. Update your config to match the model's actual output dimensions.`
|
|
4967
|
+
);
|
|
4968
|
+
}
|
|
4969
|
+
}
|
|
4970
|
+
if (data.data.length !== texts.length) {
|
|
4971
|
+
throw new Error(
|
|
4972
|
+
`Embedding count mismatch: sent ${texts.length} texts but received ${data.data.length} embeddings. The custom embedding server may not support batch input.`
|
|
4973
|
+
);
|
|
4974
|
+
}
|
|
4975
|
+
return {
|
|
4976
|
+
embeddings: data.data.map((d) => d.embedding),
|
|
4977
|
+
totalTokensUsed: data.usage?.total_tokens ?? texts.reduce((sum, t) => sum + Math.ceil(t.length / 4), 0)
|
|
4978
|
+
};
|
|
4979
|
+
}
|
|
4980
|
+
throw new Error("Custom embedding API returned unexpected response format. Expected OpenAI-compatible format with data[].embedding.");
|
|
4837
4981
|
}
|
|
4838
|
-
async
|
|
4839
|
-
const
|
|
4982
|
+
async embedBatch(texts) {
|
|
4983
|
+
const requestBatches = this.splitIntoRequestBatches(texts);
|
|
4984
|
+
const embeddings = [];
|
|
4985
|
+
let totalTokensUsed = 0;
|
|
4986
|
+
for (const batch of requestBatches) {
|
|
4987
|
+
const result = await this.embedRequest(batch);
|
|
4988
|
+
embeddings.push(...result.embeddings);
|
|
4989
|
+
totalTokensUsed += result.totalTokensUsed;
|
|
4990
|
+
}
|
|
4840
4991
|
return {
|
|
4841
|
-
|
|
4842
|
-
|
|
4992
|
+
embeddings,
|
|
4993
|
+
totalTokensUsed
|
|
4843
4994
|
};
|
|
4844
4995
|
}
|
|
4996
|
+
};
|
|
4997
|
+
|
|
4998
|
+
// src/embeddings/providers/github-copilot.ts
|
|
4999
|
+
var GitHubCopilotEmbeddingProvider = class extends BaseEmbeddingProvider {
|
|
5000
|
+
constructor(credentials, modelInfo) {
|
|
5001
|
+
super(credentials, modelInfo);
|
|
5002
|
+
}
|
|
5003
|
+
getToken() {
|
|
5004
|
+
if (!this.credentials.refreshToken) {
|
|
5005
|
+
throw new Error("No OAuth token available for GitHub");
|
|
5006
|
+
}
|
|
5007
|
+
return this.credentials.refreshToken;
|
|
5008
|
+
}
|
|
4845
5009
|
async embedBatch(texts) {
|
|
4846
|
-
const
|
|
5010
|
+
const token = this.getToken();
|
|
5011
|
+
const response = await fetch(`${this.credentials.baseUrl}/inference/embeddings`, {
|
|
4847
5012
|
method: "POST",
|
|
4848
5013
|
headers: {
|
|
4849
|
-
Authorization: `Bearer ${
|
|
4850
|
-
"Content-Type": "application/json"
|
|
5014
|
+
Authorization: `Bearer ${token}`,
|
|
5015
|
+
"Content-Type": "application/json",
|
|
5016
|
+
Accept: "application/vnd.github+json",
|
|
5017
|
+
"X-GitHub-Api-Version": "2022-11-28"
|
|
4851
5018
|
},
|
|
4852
5019
|
body: JSON.stringify({
|
|
4853
|
-
model: this.modelInfo.model
|
|
5020
|
+
model: `openai/${this.modelInfo.model}`,
|
|
4854
5021
|
input: texts
|
|
4855
5022
|
})
|
|
4856
5023
|
});
|
|
4857
5024
|
if (!response.ok) {
|
|
4858
|
-
const error = await response.text();
|
|
4859
|
-
throw new Error(`
|
|
5025
|
+
const error = (await response.text()).slice(0, 500);
|
|
5026
|
+
throw new Error(`GitHub Copilot embedding API error: ${response.status} - ${error}`);
|
|
4860
5027
|
}
|
|
4861
5028
|
const data = await response.json();
|
|
4862
5029
|
return {
|
|
@@ -4864,16 +5031,14 @@ var OpenAIEmbeddingProvider = class {
|
|
|
4864
5031
|
totalTokensUsed: data.usage.total_tokens
|
|
4865
5032
|
};
|
|
4866
5033
|
}
|
|
4867
|
-
getModelInfo() {
|
|
4868
|
-
return this.modelInfo;
|
|
4869
|
-
}
|
|
4870
5034
|
};
|
|
4871
|
-
|
|
5035
|
+
|
|
5036
|
+
// src/embeddings/providers/google.ts
|
|
5037
|
+
var GoogleEmbeddingProvider = class _GoogleEmbeddingProvider extends BaseEmbeddingProvider {
|
|
5038
|
+
static BATCH_SIZE = 20;
|
|
4872
5039
|
constructor(credentials, modelInfo) {
|
|
4873
|
-
|
|
4874
|
-
this.modelInfo = modelInfo;
|
|
5040
|
+
super(credentials, modelInfo);
|
|
4875
5041
|
}
|
|
4876
|
-
static BATCH_SIZE = 20;
|
|
4877
5042
|
async embedQuery(query) {
|
|
4878
5043
|
const taskType = this.modelInfo.taskAble ? "CODE_RETRIEVAL_QUERY" : void 0;
|
|
4879
5044
|
const result = await this.embedWithTaskType([query], taskType);
|
|
@@ -4894,12 +5059,6 @@ var GoogleEmbeddingProvider = class _GoogleEmbeddingProvider {
|
|
|
4894
5059
|
const taskType = this.modelInfo.taskAble ? "RETRIEVAL_DOCUMENT" : void 0;
|
|
4895
5060
|
return this.embedWithTaskType(texts, taskType);
|
|
4896
5061
|
}
|
|
4897
|
-
/**
|
|
4898
|
-
* Embeds texts using the Google embedContent API.
|
|
4899
|
-
* Sends multiple texts as parts in batched requests (up to BATCH_SIZE per call).
|
|
4900
|
-
* When taskType is provided (gemini-embedding-001), includes it in the request
|
|
4901
|
-
* for task-specific embedding optimization.
|
|
4902
|
-
*/
|
|
4903
5062
|
async embedWithTaskType(texts, taskType) {
|
|
4904
5063
|
const batches = [];
|
|
4905
5064
|
for (let i = 0; i < texts.length; i += _GoogleEmbeddingProvider.BATCH_SIZE) {
|
|
@@ -4916,17 +5075,18 @@ var GoogleEmbeddingProvider = class _GoogleEmbeddingProvider {
|
|
|
4916
5075
|
outputDimensionality: this.modelInfo.dimensions
|
|
4917
5076
|
}));
|
|
4918
5077
|
const response = await fetch(
|
|
4919
|
-
`${this.credentials.baseUrl}/models/${this.modelInfo.model}:batchEmbedContents
|
|
5078
|
+
`${this.credentials.baseUrl}/models/${this.modelInfo.model}:batchEmbedContents`,
|
|
4920
5079
|
{
|
|
4921
5080
|
method: "POST",
|
|
4922
5081
|
headers: {
|
|
4923
|
-
"Content-Type": "application/json"
|
|
5082
|
+
"Content-Type": "application/json",
|
|
5083
|
+
...this.credentials.apiKey && { "x-goog-api-key": this.credentials.apiKey }
|
|
4924
5084
|
},
|
|
4925
5085
|
body: JSON.stringify({ requests })
|
|
4926
5086
|
}
|
|
4927
5087
|
);
|
|
4928
5088
|
if (!response.ok) {
|
|
4929
|
-
const error = await response.text();
|
|
5089
|
+
const error = (await response.text()).slice(0, 500);
|
|
4930
5090
|
throw new Error(`Google embedding API error: ${response.status} - ${error}`);
|
|
4931
5091
|
}
|
|
4932
5092
|
const data = await response.json();
|
|
@@ -4941,29 +5101,13 @@ var GoogleEmbeddingProvider = class _GoogleEmbeddingProvider {
|
|
|
4941
5101
|
totalTokensUsed: batchResults.reduce((sum, r) => sum + r.tokensUsed, 0)
|
|
4942
5102
|
};
|
|
4943
5103
|
}
|
|
4944
|
-
getModelInfo() {
|
|
4945
|
-
return this.modelInfo;
|
|
4946
|
-
}
|
|
4947
5104
|
};
|
|
4948
|
-
|
|
4949
|
-
|
|
4950
|
-
|
|
4951
|
-
this.modelInfo = modelInfo;
|
|
4952
|
-
}
|
|
5105
|
+
|
|
5106
|
+
// src/embeddings/providers/ollama.ts
|
|
5107
|
+
var OllamaEmbeddingProvider = class _OllamaEmbeddingProvider extends BaseEmbeddingProvider {
|
|
4953
5108
|
static MIN_TRUNCATION_CHARS = 512;
|
|
4954
|
-
|
|
4955
|
-
|
|
4956
|
-
return {
|
|
4957
|
-
embedding: result.embeddings[0],
|
|
4958
|
-
tokensUsed: result.totalTokensUsed
|
|
4959
|
-
};
|
|
4960
|
-
}
|
|
4961
|
-
async embedDocument(document) {
|
|
4962
|
-
const result = await this.embedBatch([document]);
|
|
4963
|
-
return {
|
|
4964
|
-
embedding: result.embeddings[0],
|
|
4965
|
-
tokensUsed: result.totalTokensUsed
|
|
4966
|
-
};
|
|
5109
|
+
constructor(credentials, modelInfo) {
|
|
5110
|
+
super(credentials, modelInfo);
|
|
4967
5111
|
}
|
|
4968
5112
|
estimateTokens(text) {
|
|
4969
5113
|
return Math.ceil(text.length / 4);
|
|
@@ -5028,165 +5172,96 @@ var OllamaEmbeddingProvider = class _OllamaEmbeddingProvider {
|
|
|
5028
5172
|
} catch (retryError) {
|
|
5029
5173
|
if (!this.isContextLengthError(retryError)) {
|
|
5030
5174
|
throw retryError;
|
|
5031
|
-
}
|
|
5032
|
-
lastError = retryError;
|
|
5033
|
-
}
|
|
5034
|
-
}
|
|
5035
|
-
throw lastError;
|
|
5036
|
-
}
|
|
5037
|
-
}
|
|
5038
|
-
async embedSingle(text) {
|
|
5039
|
-
const response = await fetch(`${this.credentials.baseUrl}/api/embeddings`, {
|
|
5040
|
-
method: "POST",
|
|
5041
|
-
headers: {
|
|
5042
|
-
"Content-Type": "application/json"
|
|
5043
|
-
},
|
|
5044
|
-
body: JSON.stringify({
|
|
5045
|
-
model: this.modelInfo.model,
|
|
5046
|
-
prompt: text,
|
|
5047
|
-
truncate: false
|
|
5048
|
-
})
|
|
5049
|
-
});
|
|
5050
|
-
if (!response.ok) {
|
|
5051
|
-
const error = await response.text();
|
|
5052
|
-
throw new Error(`Ollama embedding API error: ${response.status} - ${error}`);
|
|
5053
|
-
}
|
|
5054
|
-
const data = await response.json();
|
|
5055
|
-
return {
|
|
5056
|
-
embedding: data.embedding,
|
|
5057
|
-
tokensUsed: this.estimateTokens(text)
|
|
5058
|
-
};
|
|
5059
|
-
}
|
|
5060
|
-
async embedBatch(texts) {
|
|
5061
|
-
const results = [];
|
|
5062
|
-
for (const text of texts) {
|
|
5063
|
-
results.push(await this.embedSingleWithFallback(text));
|
|
5064
|
-
}
|
|
5065
|
-
return {
|
|
5066
|
-
embeddings: results.map((r) => r.embedding),
|
|
5067
|
-
totalTokensUsed: results.reduce((sum, r) => sum + r.tokensUsed, 0)
|
|
5068
|
-
};
|
|
5069
|
-
}
|
|
5070
|
-
getModelInfo() {
|
|
5071
|
-
return this.modelInfo;
|
|
5072
|
-
}
|
|
5073
|
-
};
|
|
5074
|
-
var CustomEmbeddingProvider = class {
|
|
5075
|
-
constructor(credentials, modelInfo) {
|
|
5076
|
-
this.credentials = credentials;
|
|
5077
|
-
this.modelInfo = modelInfo;
|
|
5078
|
-
}
|
|
5079
|
-
splitIntoRequestBatches(texts) {
|
|
5080
|
-
const maxBatchSize = this.modelInfo.maxBatchSize;
|
|
5081
|
-
if (!maxBatchSize || texts.length <= maxBatchSize) {
|
|
5082
|
-
return [texts];
|
|
5083
|
-
}
|
|
5084
|
-
const batches = [];
|
|
5085
|
-
for (let i = 0; i < texts.length; i += maxBatchSize) {
|
|
5086
|
-
batches.push(texts.slice(i, i + maxBatchSize));
|
|
5087
|
-
}
|
|
5088
|
-
return batches;
|
|
5089
|
-
}
|
|
5090
|
-
async embedRequest(texts) {
|
|
5091
|
-
if (texts.length === 0) {
|
|
5092
|
-
return {
|
|
5093
|
-
embeddings: [],
|
|
5094
|
-
totalTokensUsed: 0
|
|
5095
|
-
};
|
|
5096
|
-
}
|
|
5097
|
-
const headers = {
|
|
5098
|
-
"Content-Type": "application/json"
|
|
5099
|
-
};
|
|
5100
|
-
if (this.credentials.apiKey) {
|
|
5101
|
-
headers["Authorization"] = `Bearer ${this.credentials.apiKey}`;
|
|
5102
|
-
}
|
|
5103
|
-
const baseUrl = this.credentials.baseUrl ?? "";
|
|
5104
|
-
const timeoutMs = this.modelInfo.timeoutMs;
|
|
5105
|
-
const controller = new AbortController();
|
|
5106
|
-
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
5107
|
-
let response;
|
|
5108
|
-
try {
|
|
5109
|
-
response = await fetch(`${baseUrl}/embeddings`, {
|
|
5110
|
-
method: "POST",
|
|
5111
|
-
headers,
|
|
5112
|
-
body: JSON.stringify({
|
|
5113
|
-
model: this.modelInfo.model,
|
|
5114
|
-
input: texts
|
|
5115
|
-
}),
|
|
5116
|
-
signal: controller.signal
|
|
5117
|
-
});
|
|
5118
|
-
} catch (error) {
|
|
5119
|
-
if (error instanceof Error && error.name === "AbortError") {
|
|
5120
|
-
throw new Error(`Custom embedding API request timed out after ${timeoutMs}ms for ${baseUrl}/embeddings`);
|
|
5121
|
-
}
|
|
5122
|
-
throw error;
|
|
5123
|
-
} finally {
|
|
5124
|
-
clearTimeout(timeout);
|
|
5125
|
-
}
|
|
5126
|
-
if (!response.ok) {
|
|
5127
|
-
const errorText = await response.text();
|
|
5128
|
-
if (response.status >= 400 && response.status < 500 && response.status !== 429) {
|
|
5129
|
-
throw new CustomProviderNonRetryableError(`Custom embedding API error (non-retryable): ${response.status} - ${errorText}`);
|
|
5130
|
-
}
|
|
5131
|
-
throw new Error(`Custom embedding API error: ${response.status} - ${errorText}`);
|
|
5132
|
-
}
|
|
5133
|
-
const data = await response.json();
|
|
5134
|
-
if (data.data && Array.isArray(data.data)) {
|
|
5135
|
-
if (data.data.length > 0) {
|
|
5136
|
-
const actualDims = data.data[0].embedding.length;
|
|
5137
|
-
if (actualDims !== this.modelInfo.dimensions) {
|
|
5138
|
-
throw new Error(
|
|
5139
|
-
`Dimension mismatch: customProvider.dimensions is ${this.modelInfo.dimensions}, but the API returned vectors with ${actualDims} dimensions. Update your config to match the model's actual output dimensions.`
|
|
5140
|
-
);
|
|
5175
|
+
}
|
|
5176
|
+
lastError = retryError;
|
|
5141
5177
|
}
|
|
5142
5178
|
}
|
|
5143
|
-
|
|
5144
|
-
throw new Error(
|
|
5145
|
-
`Embedding count mismatch: sent ${texts.length} texts but received ${data.data.length} embeddings. The custom embedding server may not support batch input.`
|
|
5146
|
-
);
|
|
5147
|
-
}
|
|
5148
|
-
return {
|
|
5149
|
-
embeddings: data.data.map((d) => d.embedding),
|
|
5150
|
-
// Rough estimate: ~4 chars per token. Used as fallback when the server
|
|
5151
|
-
// doesn't return usage.total_tokens (e.g. llama.cpp, some vLLM configs).
|
|
5152
|
-
totalTokensUsed: data.usage?.total_tokens ?? texts.reduce((sum, t) => sum + Math.ceil(t.length / 4), 0)
|
|
5153
|
-
};
|
|
5179
|
+
throw lastError;
|
|
5154
5180
|
}
|
|
5155
|
-
throw new Error("Custom embedding API returned unexpected response format. Expected OpenAI-compatible format with data[].embedding.");
|
|
5156
5181
|
}
|
|
5157
|
-
async
|
|
5158
|
-
const
|
|
5182
|
+
async embedSingle(text) {
|
|
5183
|
+
const response = await fetch(`${this.credentials.baseUrl}/api/embeddings`, {
|
|
5184
|
+
method: "POST",
|
|
5185
|
+
headers: {
|
|
5186
|
+
"Content-Type": "application/json"
|
|
5187
|
+
},
|
|
5188
|
+
body: JSON.stringify({
|
|
5189
|
+
model: this.modelInfo.model,
|
|
5190
|
+
prompt: text,
|
|
5191
|
+
truncate: false
|
|
5192
|
+
})
|
|
5193
|
+
});
|
|
5194
|
+
if (!response.ok) {
|
|
5195
|
+
const error = (await response.text()).slice(0, 500);
|
|
5196
|
+
throw new Error(`Ollama embedding API error: ${response.status} - ${error}`);
|
|
5197
|
+
}
|
|
5198
|
+
const data = await response.json();
|
|
5159
5199
|
return {
|
|
5160
|
-
embedding:
|
|
5161
|
-
tokensUsed:
|
|
5200
|
+
embedding: data.embedding,
|
|
5201
|
+
tokensUsed: this.estimateTokens(text)
|
|
5162
5202
|
};
|
|
5163
5203
|
}
|
|
5164
|
-
async
|
|
5165
|
-
const
|
|
5204
|
+
async embedBatch(texts) {
|
|
5205
|
+
const results = [];
|
|
5206
|
+
for (const text of texts) {
|
|
5207
|
+
results.push(await this.embedSingleWithFallback(text));
|
|
5208
|
+
}
|
|
5166
5209
|
return {
|
|
5167
|
-
|
|
5168
|
-
|
|
5210
|
+
embeddings: results.map((r) => r.embedding),
|
|
5211
|
+
totalTokensUsed: results.reduce((sum, r) => sum + r.tokensUsed, 0)
|
|
5169
5212
|
};
|
|
5170
5213
|
}
|
|
5214
|
+
};
|
|
5215
|
+
|
|
5216
|
+
// src/embeddings/providers/openai.ts
|
|
5217
|
+
var OpenAIEmbeddingProvider = class extends BaseEmbeddingProvider {
|
|
5218
|
+
constructor(credentials, modelInfo) {
|
|
5219
|
+
super(credentials, modelInfo);
|
|
5220
|
+
}
|
|
5171
5221
|
async embedBatch(texts) {
|
|
5172
|
-
const
|
|
5173
|
-
|
|
5174
|
-
|
|
5175
|
-
|
|
5176
|
-
|
|
5177
|
-
|
|
5178
|
-
|
|
5222
|
+
const response = await fetch(`${this.credentials.baseUrl}/embeddings`, {
|
|
5223
|
+
method: "POST",
|
|
5224
|
+
headers: {
|
|
5225
|
+
Authorization: `Bearer ${this.credentials.apiKey}`,
|
|
5226
|
+
"Content-Type": "application/json"
|
|
5227
|
+
},
|
|
5228
|
+
body: JSON.stringify({
|
|
5229
|
+
model: this.modelInfo.model,
|
|
5230
|
+
input: texts
|
|
5231
|
+
})
|
|
5232
|
+
});
|
|
5233
|
+
if (!response.ok) {
|
|
5234
|
+
const error = (await response.text()).slice(0, 500);
|
|
5235
|
+
throw new Error(`OpenAI embedding API error: ${response.status} - ${error}`);
|
|
5179
5236
|
}
|
|
5237
|
+
const data = await response.json();
|
|
5180
5238
|
return {
|
|
5181
|
-
embeddings,
|
|
5182
|
-
totalTokensUsed
|
|
5239
|
+
embeddings: data.data.map((d) => d.embedding),
|
|
5240
|
+
totalTokensUsed: data.usage.total_tokens
|
|
5183
5241
|
};
|
|
5184
5242
|
}
|
|
5185
|
-
getModelInfo() {
|
|
5186
|
-
return this.modelInfo;
|
|
5187
|
-
}
|
|
5188
5243
|
};
|
|
5189
5244
|
|
|
5245
|
+
// src/embeddings/provider.ts
|
|
5246
|
+
function createEmbeddingProvider(configuredProviderInfo) {
|
|
5247
|
+
switch (configuredProviderInfo.provider) {
|
|
5248
|
+
case "github-copilot":
|
|
5249
|
+
return new GitHubCopilotEmbeddingProvider(configuredProviderInfo.credentials, configuredProviderInfo.modelInfo);
|
|
5250
|
+
case "openai":
|
|
5251
|
+
return new OpenAIEmbeddingProvider(configuredProviderInfo.credentials, configuredProviderInfo.modelInfo);
|
|
5252
|
+
case "google":
|
|
5253
|
+
return new GoogleEmbeddingProvider(configuredProviderInfo.credentials, configuredProviderInfo.modelInfo);
|
|
5254
|
+
case "ollama":
|
|
5255
|
+
return new OllamaEmbeddingProvider(configuredProviderInfo.credentials, configuredProviderInfo.modelInfo);
|
|
5256
|
+
case "custom":
|
|
5257
|
+
return new CustomEmbeddingProvider(configuredProviderInfo.credentials, configuredProviderInfo.modelInfo);
|
|
5258
|
+
default: {
|
|
5259
|
+
const _exhaustive = configuredProviderInfo;
|
|
5260
|
+
throw new Error(`Unsupported embedding provider: ${_exhaustive.provider}`);
|
|
5261
|
+
}
|
|
5262
|
+
}
|
|
5263
|
+
}
|
|
5264
|
+
|
|
5190
5265
|
// src/rerank/index.ts
|
|
5191
5266
|
function createReranker(config) {
|
|
5192
5267
|
if (!config.enabled) {
|
|
@@ -5222,7 +5297,10 @@ var SiliconFlowReranker = class {
|
|
|
5222
5297
|
if (this.config.apiKey) {
|
|
5223
5298
|
headers["Authorization"] = `Bearer ${this.config.apiKey}`;
|
|
5224
5299
|
}
|
|
5225
|
-
const baseUrl = this.config.baseUrl
|
|
5300
|
+
const baseUrl = this.config.baseUrl;
|
|
5301
|
+
if (!baseUrl) {
|
|
5302
|
+
throw new Error("Reranker baseUrl is required. Configure reranker.baseUrl in your codebase-index.json.");
|
|
5303
|
+
}
|
|
5226
5304
|
const timeoutMs = this.config.timeoutMs ?? 3e4;
|
|
5227
5305
|
const controller = new AbortController();
|
|
5228
5306
|
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
@@ -5296,20 +5374,21 @@ function createCostEstimate(files, provider) {
|
|
|
5296
5374
|
}
|
|
5297
5375
|
function formatCostEstimate(estimate) {
|
|
5298
5376
|
const sizeFormatted = formatBytes(estimate.totalSizeBytes);
|
|
5377
|
+
const filesFormatted = `${estimate.filesCount.toLocaleString()} files`;
|
|
5299
5378
|
const costFormatted = estimate.isFree ? "Free" : `~$${estimate.estimatedCost.toFixed(4)}`;
|
|
5300
5379
|
return `
|
|
5301
5380
|
\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510
|
|
5302
5381
|
\u2502 \u{1F4CA} Indexing Estimate \u2502
|
|
5303
5382
|
\u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524
|
|
5304
5383
|
\u2502 \u2502
|
|
5305
|
-
\u2502 Files to index: ${
|
|
5306
|
-
\u2502 Total size: ${
|
|
5307
|
-
\u2502 Estimated chunks: ${
|
|
5308
|
-
\u2502 Estimated tokens: ${
|
|
5384
|
+
\u2502 Files to index: ${filesFormatted.padEnd(40)}\u2502
|
|
5385
|
+
\u2502 Total size: ${sizeFormatted.padEnd(40)}\u2502
|
|
5386
|
+
\u2502 Estimated chunks: ${("~" + estimate.estimatedChunks.toLocaleString() + " chunks").padEnd(40)}\u2502
|
|
5387
|
+
\u2502 Estimated tokens: ${("~" + estimate.estimatedTokens.toLocaleString() + " tokens").padEnd(40)}\u2502
|
|
5309
5388
|
\u2502 \u2502
|
|
5310
|
-
\u2502 Provider: ${
|
|
5311
|
-
\u2502 Model: ${
|
|
5312
|
-
\u2502 Cost: ${
|
|
5389
|
+
\u2502 Provider: ${estimate.provider.padEnd(52)}\u2502
|
|
5390
|
+
\u2502 Model: ${estimate.model.padEnd(52)}\u2502
|
|
5391
|
+
\u2502 Cost: ${costFormatted.padEnd(52)}\u2502
|
|
5313
5392
|
\u2502 \u2502
|
|
5314
5393
|
\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518
|
|
5315
5394
|
`;
|
|
@@ -5321,9 +5400,6 @@ function formatBytes(bytes) {
|
|
|
5321
5400
|
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
5322
5401
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + " " + sizes[i];
|
|
5323
5402
|
}
|
|
5324
|
-
function padRight(str, length) {
|
|
5325
|
-
return str.padEnd(length);
|
|
5326
|
-
}
|
|
5327
5403
|
|
|
5328
5404
|
// src/utils/logger.ts
|
|
5329
5405
|
var LOG_LEVEL_PRIORITY = {
|
|
@@ -5390,6 +5466,10 @@ var Logger = class {
|
|
|
5390
5466
|
this.logs.shift();
|
|
5391
5467
|
}
|
|
5392
5468
|
}
|
|
5469
|
+
withMetrics(fn) {
|
|
5470
|
+
if (!this.config.metrics) return;
|
|
5471
|
+
fn();
|
|
5472
|
+
}
|
|
5393
5473
|
search(level, message, data) {
|
|
5394
5474
|
if (this.config.logSearch) {
|
|
5395
5475
|
this.log(level, "search", message, data);
|
|
@@ -5428,89 +5508,107 @@ var Logger = class {
|
|
|
5428
5508
|
this.log("debug", "general", message, data);
|
|
5429
5509
|
}
|
|
5430
5510
|
recordIndexingStart() {
|
|
5431
|
-
|
|
5432
|
-
|
|
5511
|
+
this.withMetrics(() => {
|
|
5512
|
+
this.metrics.indexingStartTime = Date.now();
|
|
5513
|
+
});
|
|
5433
5514
|
}
|
|
5434
5515
|
recordIndexingEnd() {
|
|
5435
|
-
|
|
5436
|
-
|
|
5516
|
+
this.withMetrics(() => {
|
|
5517
|
+
this.metrics.indexingEndTime = Date.now();
|
|
5518
|
+
});
|
|
5437
5519
|
}
|
|
5438
5520
|
recordFilesScanned(count) {
|
|
5439
|
-
|
|
5440
|
-
|
|
5521
|
+
this.withMetrics(() => {
|
|
5522
|
+
this.metrics.filesScanned = count;
|
|
5523
|
+
});
|
|
5441
5524
|
}
|
|
5442
5525
|
recordFilesParsed(count) {
|
|
5443
|
-
|
|
5444
|
-
|
|
5526
|
+
this.withMetrics(() => {
|
|
5527
|
+
this.metrics.filesParsed = count;
|
|
5528
|
+
});
|
|
5445
5529
|
}
|
|
5446
5530
|
recordParseDuration(durationMs) {
|
|
5447
|
-
|
|
5448
|
-
|
|
5531
|
+
this.withMetrics(() => {
|
|
5532
|
+
this.metrics.parseMs = durationMs;
|
|
5533
|
+
});
|
|
5449
5534
|
}
|
|
5450
5535
|
recordChunksProcessed(count) {
|
|
5451
|
-
|
|
5452
|
-
|
|
5536
|
+
this.withMetrics(() => {
|
|
5537
|
+
this.metrics.chunksProcessed += count;
|
|
5538
|
+
});
|
|
5453
5539
|
}
|
|
5454
5540
|
recordChunksEmbedded(count) {
|
|
5455
|
-
|
|
5456
|
-
|
|
5541
|
+
this.withMetrics(() => {
|
|
5542
|
+
this.metrics.chunksEmbedded += count;
|
|
5543
|
+
});
|
|
5457
5544
|
}
|
|
5458
5545
|
recordChunksFromCache(count) {
|
|
5459
|
-
|
|
5460
|
-
|
|
5546
|
+
this.withMetrics(() => {
|
|
5547
|
+
this.metrics.chunksFromCache += count;
|
|
5548
|
+
});
|
|
5461
5549
|
}
|
|
5462
5550
|
recordChunksRemoved(count) {
|
|
5463
|
-
|
|
5464
|
-
|
|
5551
|
+
this.withMetrics(() => {
|
|
5552
|
+
this.metrics.chunksRemoved += count;
|
|
5553
|
+
});
|
|
5465
5554
|
}
|
|
5466
5555
|
recordEmbeddingApiCall(tokens) {
|
|
5467
|
-
|
|
5468
|
-
|
|
5469
|
-
|
|
5556
|
+
this.withMetrics(() => {
|
|
5557
|
+
this.metrics.embeddingApiCalls++;
|
|
5558
|
+
this.metrics.embeddingTokensUsed += tokens;
|
|
5559
|
+
});
|
|
5470
5560
|
}
|
|
5471
5561
|
recordEmbeddingError() {
|
|
5472
|
-
|
|
5473
|
-
|
|
5562
|
+
this.withMetrics(() => {
|
|
5563
|
+
this.metrics.embeddingErrors++;
|
|
5564
|
+
});
|
|
5474
5565
|
}
|
|
5475
5566
|
recordSearch(durationMs, breakdown) {
|
|
5476
|
-
|
|
5477
|
-
|
|
5478
|
-
|
|
5479
|
-
|
|
5480
|
-
|
|
5481
|
-
|
|
5482
|
-
|
|
5483
|
-
|
|
5484
|
-
|
|
5485
|
-
|
|
5486
|
-
|
|
5567
|
+
this.withMetrics(() => {
|
|
5568
|
+
this.metrics.searchCount++;
|
|
5569
|
+
this.metrics.searchTotalMs += durationMs;
|
|
5570
|
+
this.metrics.searchLastMs = durationMs;
|
|
5571
|
+
this.metrics.searchAvgMs = this.metrics.searchTotalMs / this.metrics.searchCount;
|
|
5572
|
+
if (breakdown) {
|
|
5573
|
+
this.metrics.embeddingCallMs = breakdown.embeddingMs;
|
|
5574
|
+
this.metrics.vectorSearchMs = breakdown.vectorMs;
|
|
5575
|
+
this.metrics.keywordSearchMs = breakdown.keywordMs;
|
|
5576
|
+
this.metrics.fusionMs = breakdown.fusionMs;
|
|
5577
|
+
}
|
|
5578
|
+
});
|
|
5487
5579
|
}
|
|
5488
5580
|
recordCacheHit() {
|
|
5489
|
-
|
|
5490
|
-
|
|
5581
|
+
this.withMetrics(() => {
|
|
5582
|
+
this.metrics.cacheHits++;
|
|
5583
|
+
});
|
|
5491
5584
|
}
|
|
5492
5585
|
recordCacheMiss() {
|
|
5493
|
-
|
|
5494
|
-
|
|
5586
|
+
this.withMetrics(() => {
|
|
5587
|
+
this.metrics.cacheMisses++;
|
|
5588
|
+
});
|
|
5495
5589
|
}
|
|
5496
5590
|
recordQueryCacheHit() {
|
|
5497
|
-
|
|
5498
|
-
|
|
5591
|
+
this.withMetrics(() => {
|
|
5592
|
+
this.metrics.queryCacheHits++;
|
|
5593
|
+
});
|
|
5499
5594
|
}
|
|
5500
5595
|
recordQueryCacheSimilarHit() {
|
|
5501
|
-
|
|
5502
|
-
|
|
5596
|
+
this.withMetrics(() => {
|
|
5597
|
+
this.metrics.queryCacheSimilarHits++;
|
|
5598
|
+
});
|
|
5503
5599
|
}
|
|
5504
5600
|
recordQueryCacheMiss() {
|
|
5505
|
-
|
|
5506
|
-
|
|
5601
|
+
this.withMetrics(() => {
|
|
5602
|
+
this.metrics.queryCacheMisses++;
|
|
5603
|
+
});
|
|
5507
5604
|
}
|
|
5508
5605
|
recordGc(orphans, chunks, embeddings) {
|
|
5509
|
-
|
|
5510
|
-
|
|
5511
|
-
|
|
5512
|
-
|
|
5513
|
-
|
|
5606
|
+
this.withMetrics(() => {
|
|
5607
|
+
this.metrics.gcRuns++;
|
|
5608
|
+
this.metrics.gcOrphansRemoved += orphans;
|
|
5609
|
+
this.metrics.gcChunksRemoved += chunks;
|
|
5610
|
+
this.metrics.gcEmbeddingsRemoved += embeddings;
|
|
5611
|
+
});
|
|
5514
5612
|
}
|
|
5515
5613
|
getMetrics() {
|
|
5516
5614
|
return { ...this.metrics };
|
|
@@ -5617,7 +5715,7 @@ function initializeLogger(config) {
|
|
|
5617
5715
|
}
|
|
5618
5716
|
|
|
5619
5717
|
// src/native/index.ts
|
|
5620
|
-
import * as
|
|
5718
|
+
import * as path11 from "path";
|
|
5621
5719
|
import * as os4 from "os";
|
|
5622
5720
|
import * as module from "module";
|
|
5623
5721
|
import { fileURLToPath } from "url";
|
|
@@ -5641,19 +5739,19 @@ function getNativeBinding() {
|
|
|
5641
5739
|
let currentDir;
|
|
5642
5740
|
let requireTarget;
|
|
5643
5741
|
if (typeof import.meta !== "undefined" && import.meta.url) {
|
|
5644
|
-
currentDir =
|
|
5742
|
+
currentDir = path11.dirname(fileURLToPath(import.meta.url));
|
|
5645
5743
|
requireTarget = import.meta.url;
|
|
5646
5744
|
} else if (typeof __dirname !== "undefined") {
|
|
5647
5745
|
currentDir = __dirname;
|
|
5648
5746
|
requireTarget = __filename;
|
|
5649
5747
|
} else {
|
|
5650
5748
|
currentDir = process.cwd();
|
|
5651
|
-
requireTarget =
|
|
5749
|
+
requireTarget = path11.join(currentDir, "index.js");
|
|
5652
5750
|
}
|
|
5653
5751
|
const normalizedDir = currentDir.replace(/\\/g, "/");
|
|
5654
|
-
const isDevMode = normalizedDir.includes("/src/native") || currentDir.includes(
|
|
5655
|
-
const packageRoot = isDevMode ?
|
|
5656
|
-
const nativePath =
|
|
5752
|
+
const isDevMode = normalizedDir.includes("/src/native") || currentDir.includes(path11.join("src", "native"));
|
|
5753
|
+
const packageRoot = isDevMode ? path11.resolve(currentDir, "../..") : path11.resolve(currentDir, "..");
|
|
5754
|
+
const nativePath = path11.join(packageRoot, "native", bindingName);
|
|
5657
5755
|
const require2 = module.createRequire(requireTarget);
|
|
5658
5756
|
return require2(nativePath);
|
|
5659
5757
|
}
|
|
@@ -6223,7 +6321,6 @@ var Database = class {
|
|
|
6223
6321
|
this.throwIfClosed();
|
|
6224
6322
|
return this.inner.getStats();
|
|
6225
6323
|
}
|
|
6226
|
-
// ── Symbol methods ──────────────────────────────────────────────
|
|
6227
6324
|
upsertSymbol(symbol) {
|
|
6228
6325
|
this.throwIfClosed();
|
|
6229
6326
|
this.inner.upsertSymbol(symbol);
|
|
@@ -6253,7 +6350,6 @@ var Database = class {
|
|
|
6253
6350
|
this.throwIfClosed();
|
|
6254
6351
|
return this.inner.deleteSymbolsByFile(filePath);
|
|
6255
6352
|
}
|
|
6256
|
-
// ── Call Edge methods ────────────────────────────────────────────
|
|
6257
6353
|
upsertCallEdge(edge) {
|
|
6258
6354
|
this.throwIfClosed();
|
|
6259
6355
|
this.inner.upsertCallEdge(edge);
|
|
@@ -6283,7 +6379,6 @@ var Database = class {
|
|
|
6283
6379
|
this.throwIfClosed();
|
|
6284
6380
|
this.inner.resolveCallEdge(edgeId, toSymbolId);
|
|
6285
6381
|
}
|
|
6286
|
-
// ── Branch Symbol methods ────────────────────────────────────────
|
|
6287
6382
|
addSymbolsToBranch(branch, symbolIds) {
|
|
6288
6383
|
this.throwIfClosed();
|
|
6289
6384
|
this.inner.addSymbolsToBranch(branch, symbolIds);
|
|
@@ -6316,7 +6411,6 @@ var Database = class {
|
|
|
6316
6411
|
if (symbolIds.length === 0) return 0;
|
|
6317
6412
|
return this.inner.deleteBranchSymbolsForBranch(branch, symbolIds);
|
|
6318
6413
|
}
|
|
6319
|
-
// ── GC methods for symbols/edges ─────────────────────────────────
|
|
6320
6414
|
gcOrphanSymbols() {
|
|
6321
6415
|
this.throwIfClosed();
|
|
6322
6416
|
return this.inner.gcOrphanSymbols();
|
|
@@ -6328,7 +6422,7 @@ var Database = class {
|
|
|
6328
6422
|
};
|
|
6329
6423
|
|
|
6330
6424
|
// src/indexer/index.ts
|
|
6331
|
-
var CALL_GRAPH_LANGUAGES = /* @__PURE__ */ new Set(["typescript", "tsx", "javascript", "jsx", "python", "go", "rust", "php", "apex", "zig"]);
|
|
6425
|
+
var CALL_GRAPH_LANGUAGES = /* @__PURE__ */ new Set(["typescript", "tsx", "javascript", "jsx", "python", "go", "rust", "php", "apex", "zig", "gdscript", "matlab"]);
|
|
6332
6426
|
var CASE_INSENSITIVE_LANGUAGES = /* @__PURE__ */ new Set(["apex"]);
|
|
6333
6427
|
var CALL_GRAPH_SYMBOL_CHUNK_TYPES = /* @__PURE__ */ new Set([
|
|
6334
6428
|
"function_declaration",
|
|
@@ -6355,7 +6449,15 @@ var CALL_GRAPH_SYMBOL_CHUNK_TYPES = /* @__PURE__ */ new Set([
|
|
|
6355
6449
|
"trigger_declaration",
|
|
6356
6450
|
"test_declaration",
|
|
6357
6451
|
"struct_declaration",
|
|
6358
|
-
"union_declaration"
|
|
6452
|
+
"union_declaration",
|
|
6453
|
+
// GDScript declarations whose names participate in the call graph.
|
|
6454
|
+
// `function_definition` and `class_definition` are already in the set
|
|
6455
|
+
// above (shared with Python/C/Bash and Python, respectively).
|
|
6456
|
+
"constructor_definition",
|
|
6457
|
+
"enum_definition",
|
|
6458
|
+
"signal_statement",
|
|
6459
|
+
"const_statement",
|
|
6460
|
+
"class_name_statement"
|
|
6359
6461
|
]);
|
|
6360
6462
|
function float32ArrayToBuffer(arr) {
|
|
6361
6463
|
const float32 = new Float32Array(arr);
|
|
@@ -6557,9 +6659,9 @@ function hasAllEmbeddingParts(parts, expectedPartCount) {
|
|
|
6557
6659
|
return true;
|
|
6558
6660
|
}
|
|
6559
6661
|
function isPathWithinRoot(filePath, rootPath) {
|
|
6560
|
-
const normalizedFilePath =
|
|
6561
|
-
const normalizedRoot =
|
|
6562
|
-
return normalizedFilePath === normalizedRoot || normalizedFilePath.startsWith(`${normalizedRoot}${
|
|
6662
|
+
const normalizedFilePath = path12.resolve(filePath);
|
|
6663
|
+
const normalizedRoot = path12.resolve(rootPath);
|
|
6664
|
+
return normalizedFilePath === normalizedRoot || normalizedFilePath.startsWith(`${normalizedRoot}${path12.sep}`);
|
|
6563
6665
|
}
|
|
6564
6666
|
var rankingQueryTokenCache = /* @__PURE__ */ new Map();
|
|
6565
6667
|
var rankingNameTokenCache = /* @__PURE__ */ new Map();
|
|
@@ -7612,22 +7714,28 @@ var Indexer = class {
|
|
|
7612
7714
|
this.projectRoot = projectRoot;
|
|
7613
7715
|
this.config = config;
|
|
7614
7716
|
this.indexPath = this.getIndexPath();
|
|
7615
|
-
this.fileHashCachePath =
|
|
7616
|
-
this.failedBatchesPath =
|
|
7617
|
-
this.indexingLockPath =
|
|
7717
|
+
this.fileHashCachePath = path12.join(this.indexPath, "file-hashes.json");
|
|
7718
|
+
this.failedBatchesPath = path12.join(this.indexPath, "failed-batches.json");
|
|
7719
|
+
this.indexingLockPath = path12.join(this.indexPath, "indexing.lock");
|
|
7618
7720
|
this.logger = initializeLogger(config.debug);
|
|
7619
7721
|
}
|
|
7620
7722
|
getIndexPath() {
|
|
7621
7723
|
return resolveProjectIndexPath(this.projectRoot, this.config.scope);
|
|
7622
7724
|
}
|
|
7623
7725
|
loadFileHashCache() {
|
|
7726
|
+
if (!existsSync7(this.fileHashCachePath)) {
|
|
7727
|
+
return;
|
|
7728
|
+
}
|
|
7624
7729
|
try {
|
|
7625
|
-
|
|
7626
|
-
|
|
7627
|
-
|
|
7628
|
-
|
|
7629
|
-
|
|
7630
|
-
|
|
7730
|
+
const data = readFileSync6(this.fileHashCachePath, "utf-8");
|
|
7731
|
+
const parsed = JSON.parse(data);
|
|
7732
|
+
this.fileHashCache = new Map(Object.entries(parsed));
|
|
7733
|
+
} catch (error) {
|
|
7734
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
7735
|
+
this.logger.warn("Failed to load file hash cache, resetting cache state", {
|
|
7736
|
+
fileHashCachePath: this.fileHashCachePath,
|
|
7737
|
+
error: message
|
|
7738
|
+
});
|
|
7631
7739
|
this.fileHashCache = /* @__PURE__ */ new Map();
|
|
7632
7740
|
}
|
|
7633
7741
|
}
|
|
@@ -7640,14 +7748,14 @@ var Indexer = class {
|
|
|
7640
7748
|
}
|
|
7641
7749
|
atomicWriteSync(targetPath, data) {
|
|
7642
7750
|
const tempPath = `${targetPath}.tmp`;
|
|
7643
|
-
mkdirSync2(
|
|
7751
|
+
mkdirSync2(path12.dirname(targetPath), { recursive: true });
|
|
7644
7752
|
writeFileSync2(tempPath, data);
|
|
7645
7753
|
renameSync(tempPath, targetPath);
|
|
7646
7754
|
}
|
|
7647
7755
|
getScopedRoots() {
|
|
7648
|
-
const roots = /* @__PURE__ */ new Set([
|
|
7756
|
+
const roots = /* @__PURE__ */ new Set([path12.resolve(this.projectRoot)]);
|
|
7649
7757
|
for (const kbRoot of this.config.knowledgeBases) {
|
|
7650
|
-
roots.add(
|
|
7758
|
+
roots.add(path12.resolve(this.projectRoot, kbRoot));
|
|
7651
7759
|
}
|
|
7652
7760
|
return Array.from(roots);
|
|
7653
7761
|
}
|
|
@@ -7656,22 +7764,22 @@ var Indexer = class {
|
|
|
7656
7764
|
if (this.config.scope !== "global") {
|
|
7657
7765
|
return branchName;
|
|
7658
7766
|
}
|
|
7659
|
-
const projectHash = hashContent(
|
|
7767
|
+
const projectHash = hashContent(path12.resolve(this.projectRoot)).slice(0, 16);
|
|
7660
7768
|
return `${projectHash}:${branchName}`;
|
|
7661
7769
|
}
|
|
7662
7770
|
getLegacyBranchCatalogKey() {
|
|
7663
7771
|
return this.currentBranch || "default";
|
|
7664
7772
|
}
|
|
7665
7773
|
getLegacyMigrationMetadataKey() {
|
|
7666
|
-
const projectHash = hashContent(
|
|
7774
|
+
const projectHash = hashContent(path12.resolve(this.projectRoot)).slice(0, 16);
|
|
7667
7775
|
return `index.globalBranchMigration.${projectHash}`;
|
|
7668
7776
|
}
|
|
7669
7777
|
getProjectEmbeddingStrategyMetadataKey() {
|
|
7670
|
-
const projectHash = hashContent(
|
|
7778
|
+
const projectHash = hashContent(path12.resolve(this.projectRoot)).slice(0, 16);
|
|
7671
7779
|
return `index.embeddingStrategyVersion.${projectHash}`;
|
|
7672
7780
|
}
|
|
7673
7781
|
getProjectForceReembedMetadataKey() {
|
|
7674
|
-
const projectHash = hashContent(
|
|
7782
|
+
const projectHash = hashContent(path12.resolve(this.projectRoot)).slice(0, 16);
|
|
7675
7783
|
return `index.forceReembed.${projectHash}`;
|
|
7676
7784
|
}
|
|
7677
7785
|
hasProjectForceReembedPending() {
|
|
@@ -7765,7 +7873,7 @@ var Indexer = class {
|
|
|
7765
7873
|
if (!this.database) {
|
|
7766
7874
|
return { chunkIds, symbolIds };
|
|
7767
7875
|
}
|
|
7768
|
-
const projectRootPath =
|
|
7876
|
+
const projectRootPath = path12.resolve(this.projectRoot);
|
|
7769
7877
|
const projectLocalFilePaths = /* @__PURE__ */ new Set([
|
|
7770
7878
|
...Array.from(this.fileHashCache.keys()).filter(
|
|
7771
7879
|
(filePath) => this.isFileInCurrentScope(filePath, roots) && isPathWithinRoot(filePath, projectRootPath)
|
|
@@ -7788,7 +7896,7 @@ var Indexer = class {
|
|
|
7788
7896
|
if (this.config.scope !== "global") {
|
|
7789
7897
|
return this.getBranchCatalogCleanupKeys();
|
|
7790
7898
|
}
|
|
7791
|
-
const projectHash = hashContent(
|
|
7899
|
+
const projectHash = hashContent(path12.resolve(this.projectRoot)).slice(0, 16);
|
|
7792
7900
|
const keys = /* @__PURE__ */ new Set();
|
|
7793
7901
|
const projectChunkIdSet = new Set(projectChunkIds);
|
|
7794
7902
|
const projectSymbolIdSet = new Set(projectSymbolIds);
|
|
@@ -7872,7 +7980,7 @@ var Indexer = class {
|
|
|
7872
7980
|
if (!this.database || this.config.scope !== "global") {
|
|
7873
7981
|
return false;
|
|
7874
7982
|
}
|
|
7875
|
-
const projectHash = hashContent(
|
|
7983
|
+
const projectHash = hashContent(path12.resolve(this.projectRoot)).slice(0, 16);
|
|
7876
7984
|
const roots = this.getScopedRoots();
|
|
7877
7985
|
const { chunkIds: projectLocalChunkIds, symbolIds: projectLocalSymbolIds } = this.getProjectLocalScopedOwnershipIds(roots);
|
|
7878
7986
|
return this.database.getAllBranches().some(
|
|
@@ -7906,7 +8014,7 @@ var Indexer = class {
|
|
|
7906
8014
|
...Array.from(this.fileHashCache.keys()).filter((filePath) => this.isFileInCurrentScope(filePath, roots)),
|
|
7907
8015
|
...scopedEntries.map(({ metadata }) => metadata.filePath)
|
|
7908
8016
|
]);
|
|
7909
|
-
const projectRootPath =
|
|
8017
|
+
const projectRootPath = path12.resolve(this.projectRoot);
|
|
7910
8018
|
const projectLocalFilePaths = new Set(
|
|
7911
8019
|
Array.from(filePaths).filter((filePath) => isPathWithinRoot(filePath, projectRootPath))
|
|
7912
8020
|
);
|
|
@@ -7975,7 +8083,7 @@ var Indexer = class {
|
|
|
7975
8083
|
};
|
|
7976
8084
|
}
|
|
7977
8085
|
checkForInterruptedIndexing() {
|
|
7978
|
-
return
|
|
8086
|
+
return existsSync7(this.indexingLockPath);
|
|
7979
8087
|
}
|
|
7980
8088
|
acquireIndexingLock() {
|
|
7981
8089
|
const lockData = {
|
|
@@ -7985,13 +8093,13 @@ var Indexer = class {
|
|
|
7985
8093
|
writeFileSync2(this.indexingLockPath, JSON.stringify(lockData));
|
|
7986
8094
|
}
|
|
7987
8095
|
releaseIndexingLock() {
|
|
7988
|
-
if (
|
|
8096
|
+
if (existsSync7(this.indexingLockPath)) {
|
|
7989
8097
|
unlinkSync(this.indexingLockPath);
|
|
7990
8098
|
}
|
|
7991
8099
|
}
|
|
7992
8100
|
async recoverFromInterruptedIndexing() {
|
|
7993
8101
|
this.logger.warn("Detected interrupted indexing session, recovering...");
|
|
7994
|
-
if (
|
|
8102
|
+
if (existsSync7(this.fileHashCachePath)) {
|
|
7995
8103
|
unlinkSync(this.fileHashCachePath);
|
|
7996
8104
|
}
|
|
7997
8105
|
await this.healthCheck();
|
|
@@ -8001,15 +8109,20 @@ var Indexer = class {
|
|
|
8001
8109
|
loadFailedBatches(maxChunkTokens) {
|
|
8002
8110
|
try {
|
|
8003
8111
|
return this.loadSerializedFailedBatches().map((batch) => normalizeFailedBatch(batch, maxChunkTokens)).filter((batch) => batch !== null);
|
|
8004
|
-
} catch {
|
|
8112
|
+
} catch (error) {
|
|
8113
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
8114
|
+
this.logger.warn("Failed to load failed batch state, skipping persisted retries", {
|
|
8115
|
+
failedBatchesPath: this.failedBatchesPath,
|
|
8116
|
+
error: message
|
|
8117
|
+
});
|
|
8005
8118
|
return [];
|
|
8006
8119
|
}
|
|
8007
8120
|
}
|
|
8008
8121
|
loadSerializedFailedBatches() {
|
|
8009
|
-
if (!
|
|
8122
|
+
if (!existsSync7(this.failedBatchesPath)) {
|
|
8010
8123
|
return [];
|
|
8011
8124
|
}
|
|
8012
|
-
const data =
|
|
8125
|
+
const data = readFileSync6(this.failedBatchesPath, "utf-8");
|
|
8013
8126
|
const parsed = JSON.parse(data);
|
|
8014
8127
|
return parsed.map((batch) => {
|
|
8015
8128
|
const chunks = Array.isArray(batch.chunks) ? batch.chunks : [];
|
|
@@ -8026,7 +8139,7 @@ var Indexer = class {
|
|
|
8026
8139
|
}
|
|
8027
8140
|
saveFailedBatches(batches) {
|
|
8028
8141
|
if (batches.length === 0) {
|
|
8029
|
-
if (
|
|
8142
|
+
if (existsSync7(this.failedBatchesPath)) {
|
|
8030
8143
|
try {
|
|
8031
8144
|
unlinkSync(this.failedBatchesPath);
|
|
8032
8145
|
} catch {
|
|
@@ -8261,24 +8374,24 @@ var Indexer = class {
|
|
|
8261
8374
|
}
|
|
8262
8375
|
await fsPromises2.mkdir(this.indexPath, { recursive: true });
|
|
8263
8376
|
const dimensions = this.configuredProviderInfo.modelInfo.dimensions;
|
|
8264
|
-
const storePath =
|
|
8377
|
+
const storePath = path12.join(this.indexPath, "vectors");
|
|
8265
8378
|
this.store = new VectorStore(storePath, dimensions);
|
|
8266
|
-
const indexFilePath =
|
|
8267
|
-
if (
|
|
8379
|
+
const indexFilePath = path12.join(this.indexPath, "vectors.usearch");
|
|
8380
|
+
if (existsSync7(indexFilePath)) {
|
|
8268
8381
|
this.store.load();
|
|
8269
8382
|
}
|
|
8270
|
-
const invertedIndexPath =
|
|
8383
|
+
const invertedIndexPath = path12.join(this.indexPath, "inverted-index.json");
|
|
8271
8384
|
this.invertedIndex = new InvertedIndex(invertedIndexPath);
|
|
8272
8385
|
try {
|
|
8273
8386
|
this.invertedIndex.load();
|
|
8274
8387
|
} catch {
|
|
8275
|
-
if (
|
|
8388
|
+
if (existsSync7(invertedIndexPath)) {
|
|
8276
8389
|
await fsPromises2.unlink(invertedIndexPath);
|
|
8277
8390
|
}
|
|
8278
8391
|
this.invertedIndex = new InvertedIndex(invertedIndexPath);
|
|
8279
8392
|
}
|
|
8280
|
-
const dbPath =
|
|
8281
|
-
let dbIsNew = !
|
|
8393
|
+
const dbPath = path12.join(this.indexPath, "codebase.db");
|
|
8394
|
+
let dbIsNew = !existsSync7(dbPath);
|
|
8282
8395
|
try {
|
|
8283
8396
|
this.database = new Database(dbPath);
|
|
8284
8397
|
} catch (error) {
|
|
@@ -8358,7 +8471,7 @@ var Indexer = class {
|
|
|
8358
8471
|
if (await this.tryResetCorruptedIndex("running automatic orphan garbage collection", error)) {
|
|
8359
8472
|
return {
|
|
8360
8473
|
resetCorruptedIndex: true,
|
|
8361
|
-
warning: this.getCorruptedIndexWarning(
|
|
8474
|
+
warning: this.getCorruptedIndexWarning(path12.join(this.indexPath, "codebase.db"))
|
|
8362
8475
|
};
|
|
8363
8476
|
}
|
|
8364
8477
|
throw error;
|
|
@@ -8373,7 +8486,7 @@ var Indexer = class {
|
|
|
8373
8486
|
return;
|
|
8374
8487
|
}
|
|
8375
8488
|
const retainedEntries = store.getAllMetadata().filter(({ key }) => !excludedSet.has(key));
|
|
8376
|
-
const storeBasePath =
|
|
8489
|
+
const storeBasePath = path12.join(this.indexPath, "vectors");
|
|
8377
8490
|
const storeIndexPath = `${storeBasePath}.usearch`;
|
|
8378
8491
|
const storeMetadataPath = `${storeBasePath}.meta.json`;
|
|
8379
8492
|
const backupIndexPath = `${storeIndexPath}.bak`;
|
|
@@ -8382,18 +8495,18 @@ var Indexer = class {
|
|
|
8382
8495
|
let backedUpMetadata = false;
|
|
8383
8496
|
let rebuiltCount = 0;
|
|
8384
8497
|
let skippedCount = 0;
|
|
8385
|
-
if (
|
|
8498
|
+
if (existsSync7(backupIndexPath)) {
|
|
8386
8499
|
unlinkSync(backupIndexPath);
|
|
8387
8500
|
}
|
|
8388
|
-
if (
|
|
8501
|
+
if (existsSync7(backupMetadataPath)) {
|
|
8389
8502
|
unlinkSync(backupMetadataPath);
|
|
8390
8503
|
}
|
|
8391
8504
|
try {
|
|
8392
|
-
if (
|
|
8505
|
+
if (existsSync7(storeIndexPath)) {
|
|
8393
8506
|
renameSync(storeIndexPath, backupIndexPath);
|
|
8394
8507
|
backedUpIndex = true;
|
|
8395
8508
|
}
|
|
8396
|
-
if (
|
|
8509
|
+
if (existsSync7(storeMetadataPath)) {
|
|
8397
8510
|
renameSync(storeMetadataPath, backupMetadataPath);
|
|
8398
8511
|
backedUpMetadata = true;
|
|
8399
8512
|
}
|
|
@@ -8414,10 +8527,10 @@ var Indexer = class {
|
|
|
8414
8527
|
rebuiltCount += 1;
|
|
8415
8528
|
}
|
|
8416
8529
|
store.save();
|
|
8417
|
-
if (backedUpIndex &&
|
|
8530
|
+
if (backedUpIndex && existsSync7(backupIndexPath)) {
|
|
8418
8531
|
unlinkSync(backupIndexPath);
|
|
8419
8532
|
}
|
|
8420
|
-
if (backedUpMetadata &&
|
|
8533
|
+
if (backedUpMetadata && existsSync7(backupMetadataPath)) {
|
|
8421
8534
|
unlinkSync(backupMetadataPath);
|
|
8422
8535
|
}
|
|
8423
8536
|
this.logger.gc("info", "Rebuilt vector store to avoid native remove", {
|
|
@@ -8430,16 +8543,16 @@ var Indexer = class {
|
|
|
8430
8543
|
store.clear();
|
|
8431
8544
|
} catch {
|
|
8432
8545
|
}
|
|
8433
|
-
if (
|
|
8546
|
+
if (existsSync7(storeIndexPath)) {
|
|
8434
8547
|
unlinkSync(storeIndexPath);
|
|
8435
8548
|
}
|
|
8436
|
-
if (
|
|
8549
|
+
if (existsSync7(storeMetadataPath)) {
|
|
8437
8550
|
unlinkSync(storeMetadataPath);
|
|
8438
8551
|
}
|
|
8439
|
-
if (backedUpIndex &&
|
|
8552
|
+
if (backedUpIndex && existsSync7(backupIndexPath)) {
|
|
8440
8553
|
renameSync(backupIndexPath, storeIndexPath);
|
|
8441
8554
|
}
|
|
8442
|
-
if (backedUpMetadata &&
|
|
8555
|
+
if (backedUpMetadata && existsSync7(backupMetadataPath)) {
|
|
8443
8556
|
renameSync(backupMetadataPath, storeMetadataPath);
|
|
8444
8557
|
}
|
|
8445
8558
|
if (backedUpIndex || backedUpMetadata) {
|
|
@@ -8458,7 +8571,7 @@ var Indexer = class {
|
|
|
8458
8571
|
if (!isSqliteCorruptionError(error)) {
|
|
8459
8572
|
return false;
|
|
8460
8573
|
}
|
|
8461
|
-
const dbPath =
|
|
8574
|
+
const dbPath = path12.join(this.indexPath, "codebase.db");
|
|
8462
8575
|
const warning = this.getCorruptedIndexWarning(dbPath);
|
|
8463
8576
|
const errorMessage = getErrorMessage(error);
|
|
8464
8577
|
if (this.config.scope === "global") {
|
|
@@ -8481,15 +8594,15 @@ var Indexer = class {
|
|
|
8481
8594
|
this.indexCompatibility = null;
|
|
8482
8595
|
this.fileHashCache.clear();
|
|
8483
8596
|
const resetPaths = [
|
|
8484
|
-
|
|
8485
|
-
|
|
8486
|
-
|
|
8487
|
-
|
|
8488
|
-
|
|
8489
|
-
|
|
8490
|
-
|
|
8491
|
-
|
|
8492
|
-
|
|
8597
|
+
path12.join(this.indexPath, "codebase.db"),
|
|
8598
|
+
path12.join(this.indexPath, "codebase.db-shm"),
|
|
8599
|
+
path12.join(this.indexPath, "codebase.db-wal"),
|
|
8600
|
+
path12.join(this.indexPath, "vectors.usearch"),
|
|
8601
|
+
path12.join(this.indexPath, "inverted-index.json"),
|
|
8602
|
+
path12.join(this.indexPath, "file-hashes.json"),
|
|
8603
|
+
path12.join(this.indexPath, "failed-batches.json"),
|
|
8604
|
+
path12.join(this.indexPath, "indexing.lock"),
|
|
8605
|
+
path12.join(this.indexPath, "vectors")
|
|
8493
8606
|
];
|
|
8494
8607
|
await Promise.all(resetPaths.map(async (targetPath) => {
|
|
8495
8608
|
try {
|
|
@@ -8754,7 +8867,7 @@ var Indexer = class {
|
|
|
8754
8867
|
for (const parsed of parsedFiles) {
|
|
8755
8868
|
currentFilePaths.add(parsed.path);
|
|
8756
8869
|
if (parsed.chunks.length === 0) {
|
|
8757
|
-
const relativePath =
|
|
8870
|
+
const relativePath = path12.relative(this.projectRoot, parsed.path);
|
|
8758
8871
|
stats.parseFailures.push(relativePath);
|
|
8759
8872
|
}
|
|
8760
8873
|
let fileChunkCount = 0;
|
|
@@ -9037,7 +9150,7 @@ var Indexer = class {
|
|
|
9037
9150
|
for (const requestBatch of requestBatches) {
|
|
9038
9151
|
queue.add(async () => {
|
|
9039
9152
|
if (rateLimitBackoffMs > 0) {
|
|
9040
|
-
await new Promise((
|
|
9153
|
+
await new Promise((resolve11) => setTimeout(resolve11, rateLimitBackoffMs));
|
|
9041
9154
|
}
|
|
9042
9155
|
try {
|
|
9043
9156
|
const result = await pRetry(
|
|
@@ -9598,8 +9711,8 @@ var Indexer = class {
|
|
|
9598
9711
|
this.indexCompatibility = compatibility;
|
|
9599
9712
|
return;
|
|
9600
9713
|
}
|
|
9601
|
-
const localProjectIndexPath =
|
|
9602
|
-
if (
|
|
9714
|
+
const localProjectIndexPath = path12.join(this.projectRoot, ".opencode", "index");
|
|
9715
|
+
if (path12.resolve(this.indexPath) !== path12.resolve(localProjectIndexPath)) {
|
|
9603
9716
|
throw new Error(
|
|
9604
9717
|
"Project-scoped force rebuild is unsafe while using an inherited worktree index. Create a local project config boundary before clearing the index."
|
|
9605
9718
|
);
|
|
@@ -9638,7 +9751,7 @@ var Indexer = class {
|
|
|
9638
9751
|
const removedChunkKeys = [];
|
|
9639
9752
|
const chunkKeysByRemovedFile = /* @__PURE__ */ new Map();
|
|
9640
9753
|
for (const [filePath, chunkKeys] of filePathsToChunkKeys) {
|
|
9641
|
-
if (!
|
|
9754
|
+
if (!existsSync7(filePath)) {
|
|
9642
9755
|
chunkKeysByRemovedFile.set(filePath, chunkKeys);
|
|
9643
9756
|
for (const key of chunkKeys) {
|
|
9644
9757
|
removedChunkKeys.push(key);
|
|
@@ -9687,7 +9800,7 @@ var Indexer = class {
|
|
|
9687
9800
|
gcOrphanSymbols: 0,
|
|
9688
9801
|
gcOrphanCallEdges: 0,
|
|
9689
9802
|
resetCorruptedIndex: true,
|
|
9690
|
-
warning: this.getCorruptedIndexWarning(
|
|
9803
|
+
warning: this.getCorruptedIndexWarning(path12.join(this.indexPath, "codebase.db"))
|
|
9691
9804
|
};
|
|
9692
9805
|
}
|
|
9693
9806
|
this.logger.recordGc(removedCount, gcOrphanChunks, gcOrphanEmbeddings);
|
|
@@ -10239,12 +10352,15 @@ function formatLogs(logs) {
|
|
|
10239
10352
|
return `[${l.timestamp}] [${l.level.toUpperCase()}] [${l.category}] ${l.message}${dataStr}`;
|
|
10240
10353
|
}).join("\n");
|
|
10241
10354
|
}
|
|
10355
|
+
function formatResultHeader(result, index) {
|
|
10356
|
+
return result.name ? `[${index + 1}] ${result.chunkType} "${result.name}" in ${result.filePath}:${result.startLine}-${result.endLine}` : `[${index + 1}] ${result.chunkType} in ${result.filePath}:${result.startLine}-${result.endLine}`;
|
|
10357
|
+
}
|
|
10242
10358
|
function formatDefinitionLookup(results, query) {
|
|
10243
10359
|
if (results.length === 0) {
|
|
10244
10360
|
return `No definition found for "${query}". Try codebase_search for broader discovery, or verify the symbol name.`;
|
|
10245
10361
|
}
|
|
10246
10362
|
const formatted = results.map((r, idx) => {
|
|
10247
|
-
const header = r
|
|
10363
|
+
const header = formatResultHeader(r, idx);
|
|
10248
10364
|
return `${header} (score: ${r.score.toFixed(2)})
|
|
10249
10365
|
\`\`\`
|
|
10250
10366
|
${truncateContent(r.content)}
|
|
@@ -10254,7 +10370,7 @@ ${truncateContent(r.content)}
|
|
|
10254
10370
|
}
|
|
10255
10371
|
function formatSearchResults(results, scoreFormat = "similarity") {
|
|
10256
10372
|
const formatted = results.map((r, idx) => {
|
|
10257
|
-
const header = r
|
|
10373
|
+
const header = formatResultHeader(r, idx);
|
|
10258
10374
|
const scoreLabel = scoreFormat === "similarity" ? `(similarity: ${(r.score * 100).toFixed(1)}%)` : `(score: ${r.score.toFixed(2)})`;
|
|
10259
10375
|
return `${header} ${scoreLabel}
|
|
10260
10376
|
\`\`\`
|
|
@@ -10264,104 +10380,83 @@ ${truncateContent(r.content)}
|
|
|
10264
10380
|
return formatted.join("\n\n");
|
|
10265
10381
|
}
|
|
10266
10382
|
|
|
10267
|
-
// src/tools/
|
|
10268
|
-
import
|
|
10269
|
-
|
|
10270
|
-
var z = tool.schema;
|
|
10271
|
-
var sharedIndexer = null;
|
|
10272
|
-
var sharedProjectRoot = "";
|
|
10273
|
-
function initializeTools(projectRoot, config) {
|
|
10274
|
-
sharedProjectRoot = projectRoot;
|
|
10275
|
-
sharedIndexer = new Indexer(projectRoot, config);
|
|
10276
|
-
}
|
|
10277
|
-
function getSharedIndexer() {
|
|
10278
|
-
return getIndexer();
|
|
10279
|
-
}
|
|
10280
|
-
function refreshIndexerFromConfig() {
|
|
10281
|
-
if (!sharedProjectRoot) {
|
|
10282
|
-
throw new Error("Codebase index tools not initialized. Plugin may not be loaded correctly.");
|
|
10283
|
-
}
|
|
10284
|
-
sharedIndexer = new Indexer(sharedProjectRoot, parseConfig(loadRuntimeConfig()));
|
|
10285
|
-
}
|
|
10286
|
-
function shouldForceLocalizeProjectIndex() {
|
|
10287
|
-
const currentConfig = parseConfig(loadRuntimeConfig());
|
|
10288
|
-
if (currentConfig.scope !== "project") {
|
|
10289
|
-
return false;
|
|
10290
|
-
}
|
|
10291
|
-
const localIndexPath = path9.join(sharedProjectRoot, ".opencode", "index");
|
|
10292
|
-
const mainRepoRoot = resolveWorktreeMainRepoRoot(sharedProjectRoot);
|
|
10293
|
-
if (!mainRepoRoot) {
|
|
10294
|
-
return false;
|
|
10295
|
-
}
|
|
10296
|
-
const inheritedIndexPath = path9.join(mainRepoRoot, ".opencode", "index");
|
|
10297
|
-
return !existsSync7(localIndexPath) && existsSync7(inheritedIndexPath);
|
|
10298
|
-
}
|
|
10299
|
-
function getIndexer() {
|
|
10300
|
-
if (!sharedIndexer) {
|
|
10301
|
-
throw new Error("Codebase index tools not initialized. Plugin may not be loaded correctly.");
|
|
10302
|
-
}
|
|
10303
|
-
return sharedIndexer;
|
|
10304
|
-
}
|
|
10305
|
-
function getConfigPath() {
|
|
10306
|
-
return resolveWritableProjectConfigPath(sharedProjectRoot);
|
|
10307
|
-
}
|
|
10308
|
-
function normalizeConfigPathValue(value, baseDir) {
|
|
10383
|
+
// src/tools/knowledge-base-paths.ts
|
|
10384
|
+
import * as path13 from "path";
|
|
10385
|
+
function resolveConfigPathValue(value, baseDir) {
|
|
10309
10386
|
const trimmed = value.trim();
|
|
10310
10387
|
if (!trimmed) {
|
|
10311
10388
|
return trimmed;
|
|
10312
10389
|
}
|
|
10313
|
-
const absolutePath =
|
|
10314
|
-
return
|
|
10390
|
+
const absolutePath = path13.isAbsolute(trimmed) ? trimmed : path13.resolve(baseDir, trimmed);
|
|
10391
|
+
return path13.normalize(absolutePath);
|
|
10315
10392
|
}
|
|
10316
10393
|
function serializeConfigPathValue(value, baseDir) {
|
|
10317
10394
|
const trimmed = value.trim();
|
|
10318
10395
|
if (!trimmed) {
|
|
10319
10396
|
return trimmed;
|
|
10320
10397
|
}
|
|
10321
|
-
|
|
10322
|
-
|
|
10323
|
-
return normalizeRelativePath(path9.normalize(trimmed));
|
|
10398
|
+
if (!path13.isAbsolute(trimmed)) {
|
|
10399
|
+
return normalizePathSeparators(path13.normalize(trimmed));
|
|
10324
10400
|
}
|
|
10325
|
-
const relativePath =
|
|
10326
|
-
if (!relativePath || !relativePath.startsWith("..") && !
|
|
10327
|
-
return
|
|
10401
|
+
const relativePath = path13.relative(baseDir, trimmed);
|
|
10402
|
+
if (!relativePath || !relativePath.startsWith("..") && !path13.isAbsolute(relativePath)) {
|
|
10403
|
+
return normalizePathSeparators(path13.normalize(relativePath || "."));
|
|
10328
10404
|
}
|
|
10329
|
-
return
|
|
10405
|
+
return path13.normalize(trimmed);
|
|
10406
|
+
}
|
|
10407
|
+
function resolveKnowledgeBasePath(value, projectRoot) {
|
|
10408
|
+
return path13.isAbsolute(value) ? value : path13.resolve(projectRoot, value);
|
|
10409
|
+
}
|
|
10410
|
+
function normalizeKnowledgeBasePath2(value, projectRoot) {
|
|
10411
|
+
return path13.normalize(resolveKnowledgeBasePath(value, projectRoot));
|
|
10412
|
+
}
|
|
10413
|
+
function hasMatchingKnowledgeBasePath(knowledgeBases, inputPath, projectRoot) {
|
|
10414
|
+
const normalizedInput = path13.normalize(inputPath);
|
|
10415
|
+
return knowledgeBases.some((kb) => normalizeKnowledgeBasePath2(kb, projectRoot) === normalizedInput);
|
|
10330
10416
|
}
|
|
10331
|
-
function
|
|
10417
|
+
function findKnowledgeBasePathIndex(knowledgeBases, inputPath, projectRoot) {
|
|
10418
|
+
const normalizedInput = path13.normalize(inputPath);
|
|
10419
|
+
return knowledgeBases.findIndex(
|
|
10420
|
+
(kb) => path13.normalize(kb) === normalizedInput || normalizeKnowledgeBasePath2(kb, projectRoot) === normalizedInput
|
|
10421
|
+
);
|
|
10422
|
+
}
|
|
10423
|
+
|
|
10424
|
+
// src/tools/index.ts
|
|
10425
|
+
import { existsSync as existsSync9, realpathSync, statSync as statSync3 } from "fs";
|
|
10426
|
+
import * as path15 from "path";
|
|
10427
|
+
|
|
10428
|
+
// src/tools/config-state.ts
|
|
10429
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
10430
|
+
import * as path14 from "path";
|
|
10431
|
+
function normalizeKnowledgeBasePaths(config, projectRoot) {
|
|
10332
10432
|
const normalized = { ...config };
|
|
10333
10433
|
if (Array.isArray(normalized.knowledgeBases)) {
|
|
10334
|
-
normalized.knowledgeBases = normalized.knowledgeBases.map(
|
|
10335
|
-
|
|
10336
|
-
|
|
10434
|
+
normalized.knowledgeBases = normalized.knowledgeBases.map(
|
|
10435
|
+
(kb) => resolveConfigPathValue(kb, projectRoot)
|
|
10436
|
+
);
|
|
10337
10437
|
}
|
|
10338
10438
|
return normalized;
|
|
10339
10439
|
}
|
|
10340
|
-
function
|
|
10341
|
-
|
|
10342
|
-
|
|
10343
|
-
if (rawConfig && typeof rawConfig === "object") {
|
|
10344
|
-
for (const key of Object.keys(rawConfig)) {
|
|
10345
|
-
config[key] = rawConfig[key];
|
|
10346
|
-
}
|
|
10440
|
+
function toConfigRecord(rawConfig) {
|
|
10441
|
+
if (!rawConfig || typeof rawConfig !== "object") {
|
|
10442
|
+
return {};
|
|
10347
10443
|
}
|
|
10348
|
-
return
|
|
10444
|
+
return { ...rawConfig };
|
|
10349
10445
|
}
|
|
10350
|
-
function
|
|
10351
|
-
|
|
10352
|
-
|
|
10353
|
-
|
|
10354
|
-
|
|
10355
|
-
config[key] = rawConfig[key];
|
|
10356
|
-
}
|
|
10357
|
-
}
|
|
10358
|
-
return normalizeKnowledgeBasePaths(config);
|
|
10446
|
+
function getConfigPath(projectRoot) {
|
|
10447
|
+
return resolveWritableProjectConfigPath(projectRoot);
|
|
10448
|
+
}
|
|
10449
|
+
function loadRuntimeConfig(projectRoot) {
|
|
10450
|
+
return normalizeKnowledgeBasePaths(toConfigRecord(loadMergedConfig(projectRoot)), projectRoot);
|
|
10359
10451
|
}
|
|
10360
|
-
function
|
|
10361
|
-
|
|
10362
|
-
|
|
10363
|
-
|
|
10364
|
-
|
|
10452
|
+
function loadEditableConfig(projectRoot) {
|
|
10453
|
+
return normalizeKnowledgeBasePaths(toConfigRecord(loadProjectConfigLayer(projectRoot)), projectRoot);
|
|
10454
|
+
}
|
|
10455
|
+
function saveConfig(projectRoot, config) {
|
|
10456
|
+
const configPath = getConfigPath(projectRoot);
|
|
10457
|
+
const configDir = path14.dirname(configPath);
|
|
10458
|
+
const configBaseDir = path14.dirname(configDir);
|
|
10459
|
+
if (!existsSync8(configDir)) {
|
|
10365
10460
|
mkdirSync3(configDir, { recursive: true });
|
|
10366
10461
|
}
|
|
10367
10462
|
const serializableConfig = { ...config };
|
|
@@ -10372,6 +10467,47 @@ function saveConfig(config) {
|
|
|
10372
10467
|
}
|
|
10373
10468
|
writeFileSync3(configPath, JSON.stringify(serializableConfig, null, 2) + "\n", "utf-8");
|
|
10374
10469
|
}
|
|
10470
|
+
|
|
10471
|
+
// src/tools/index.ts
|
|
10472
|
+
import * as os5 from "os";
|
|
10473
|
+
function ensureStringArray(value) {
|
|
10474
|
+
return Array.isArray(value) ? value : [];
|
|
10475
|
+
}
|
|
10476
|
+
var z = tool.schema;
|
|
10477
|
+
var sharedIndexer = null;
|
|
10478
|
+
var sharedProjectRoot = "";
|
|
10479
|
+
function initializeTools(projectRoot, config) {
|
|
10480
|
+
sharedProjectRoot = projectRoot;
|
|
10481
|
+
sharedIndexer = new Indexer(projectRoot, config);
|
|
10482
|
+
}
|
|
10483
|
+
function getSharedIndexer() {
|
|
10484
|
+
return getIndexer();
|
|
10485
|
+
}
|
|
10486
|
+
function refreshIndexerFromConfig() {
|
|
10487
|
+
if (!sharedProjectRoot) {
|
|
10488
|
+
throw new Error("Codebase index tools not initialized. Plugin may not be loaded correctly.");
|
|
10489
|
+
}
|
|
10490
|
+
sharedIndexer = new Indexer(sharedProjectRoot, parseConfig(loadRuntimeConfig(sharedProjectRoot)));
|
|
10491
|
+
}
|
|
10492
|
+
function shouldForceLocalizeProjectIndex() {
|
|
10493
|
+
const currentConfig = parseConfig(loadRuntimeConfig(sharedProjectRoot));
|
|
10494
|
+
if (currentConfig.scope !== "project") {
|
|
10495
|
+
return false;
|
|
10496
|
+
}
|
|
10497
|
+
const localIndexPath = path15.join(sharedProjectRoot, ".opencode", "index");
|
|
10498
|
+
const mainRepoRoot = resolveWorktreeMainRepoRoot(sharedProjectRoot);
|
|
10499
|
+
if (!mainRepoRoot) {
|
|
10500
|
+
return false;
|
|
10501
|
+
}
|
|
10502
|
+
const inheritedIndexPath = path15.join(mainRepoRoot, ".opencode", "index");
|
|
10503
|
+
return !existsSync9(localIndexPath) && existsSync9(inheritedIndexPath);
|
|
10504
|
+
}
|
|
10505
|
+
function getIndexer() {
|
|
10506
|
+
if (!sharedIndexer) {
|
|
10507
|
+
throw new Error("Codebase index tools not initialized. Plugin may not be loaded correctly.");
|
|
10508
|
+
}
|
|
10509
|
+
return sharedIndexer;
|
|
10510
|
+
}
|
|
10375
10511
|
var codebase_peek = tool({
|
|
10376
10512
|
description: "Quick lookup of code locations by meaning. Returns only metadata (file, line, name, type) WITHOUT code content. Use this first to find WHERE code is, then use Read tool to examine specific files. Saves tokens by not returning full code blocks. Best for: discovery, navigation, finding multiple related locations.",
|
|
10377
10513
|
args: {
|
|
@@ -10591,36 +10727,64 @@ var add_knowledge_base = tool({
|
|
|
10591
10727
|
},
|
|
10592
10728
|
async execute(args) {
|
|
10593
10729
|
const inputPath = args.path.trim();
|
|
10594
|
-
const
|
|
10595
|
-
|
|
10596
|
-
|
|
10730
|
+
const normalizedPath = path15.resolve(
|
|
10731
|
+
path15.isAbsolute(inputPath) ? inputPath : resolveKnowledgeBasePath(inputPath, sharedProjectRoot)
|
|
10732
|
+
);
|
|
10733
|
+
if (!existsSync9(normalizedPath)) {
|
|
10734
|
+
return `Error: Directory does not exist: ${normalizedPath}`;
|
|
10735
|
+
}
|
|
10736
|
+
let realPath;
|
|
10737
|
+
try {
|
|
10738
|
+
realPath = realpathSync(normalizedPath);
|
|
10739
|
+
} catch {
|
|
10740
|
+
return `Error: Cannot resolve path: ${normalizedPath}`;
|
|
10741
|
+
}
|
|
10742
|
+
const blockedPrefixes = [
|
|
10743
|
+
"/etc",
|
|
10744
|
+
"/proc",
|
|
10745
|
+
"/sys",
|
|
10746
|
+
"/dev",
|
|
10747
|
+
"/boot",
|
|
10748
|
+
"/root",
|
|
10749
|
+
"/var/run",
|
|
10750
|
+
"/var/log"
|
|
10751
|
+
];
|
|
10752
|
+
const homeDir = os5.homedir();
|
|
10753
|
+
const sensitiveDotDirs = [".ssh", ".gnupg", ".aws", ".config/gcloud", ".docker", ".kube"];
|
|
10754
|
+
for (const prefix of blockedPrefixes) {
|
|
10755
|
+
if (realPath === prefix || realPath.startsWith(prefix + "/")) {
|
|
10756
|
+
return `Error: Adding system directory as knowledge base is not allowed: ${normalizedPath}`;
|
|
10757
|
+
}
|
|
10758
|
+
}
|
|
10759
|
+
for (const dotDir of sensitiveDotDirs) {
|
|
10760
|
+
const sensitiveDir = path15.join(homeDir, dotDir);
|
|
10761
|
+
if (realPath === sensitiveDir || realPath.startsWith(sensitiveDir + "/")) {
|
|
10762
|
+
return `Error: Adding sensitive directory as knowledge base is not allowed: ${normalizedPath}`;
|
|
10763
|
+
}
|
|
10597
10764
|
}
|
|
10598
10765
|
try {
|
|
10599
|
-
const stat4 =
|
|
10766
|
+
const stat4 = statSync3(normalizedPath);
|
|
10600
10767
|
if (!stat4.isDirectory()) {
|
|
10601
|
-
return `Error: Path is not a directory: ${
|
|
10768
|
+
return `Error: Path is not a directory: ${normalizedPath}`;
|
|
10602
10769
|
}
|
|
10603
10770
|
} catch (error) {
|
|
10604
|
-
return `Error: Cannot access directory: ${
|
|
10771
|
+
return `Error: Cannot access directory: ${normalizedPath} - ${error instanceof Error ? error.message : String(error)}`;
|
|
10605
10772
|
}
|
|
10606
|
-
const config = loadEditableConfig();
|
|
10607
|
-
const knowledgeBases =
|
|
10608
|
-
const
|
|
10609
|
-
const alreadyExists = knowledgeBases.some(
|
|
10610
|
-
(kb) => path9.normalize(path9.isAbsolute(kb) ? kb : path9.resolve(sharedProjectRoot, kb)) === normalizedPath
|
|
10611
|
-
);
|
|
10773
|
+
const config = loadEditableConfig(sharedProjectRoot);
|
|
10774
|
+
const knowledgeBases = ensureStringArray(config.knowledgeBases);
|
|
10775
|
+
const alreadyExists = hasMatchingKnowledgeBasePath(knowledgeBases, normalizedPath, sharedProjectRoot);
|
|
10612
10776
|
if (alreadyExists) {
|
|
10613
|
-
return `Knowledge base already configured: ${
|
|
10777
|
+
return `Knowledge base already configured: ${normalizedPath}`;
|
|
10614
10778
|
}
|
|
10615
|
-
knowledgeBases.push(
|
|
10779
|
+
knowledgeBases.push(normalizedPath);
|
|
10616
10780
|
config.knowledgeBases = knowledgeBases;
|
|
10617
|
-
saveConfig(config);
|
|
10781
|
+
saveConfig(sharedProjectRoot, config);
|
|
10618
10782
|
refreshIndexerFromConfig();
|
|
10619
|
-
let result = `${
|
|
10783
|
+
let result = `${normalizedPath}
|
|
10620
10784
|
`;
|
|
10621
10785
|
result += `Total knowledge bases: ${knowledgeBases.length}
|
|
10622
10786
|
`;
|
|
10623
|
-
result += `Config saved to: ${getConfigPath()}
|
|
10787
|
+
result += `Config saved to: ${getConfigPath(sharedProjectRoot)}
|
|
10624
10788
|
`;
|
|
10625
10789
|
result += `
|
|
10626
10790
|
Run /index to rebuild the index with the new knowledge base.`;
|
|
@@ -10631,8 +10795,8 @@ var list_knowledge_bases = tool({
|
|
|
10631
10795
|
description: "List all configured knowledge base folders that are indexed alongside the main project.",
|
|
10632
10796
|
args: {},
|
|
10633
10797
|
async execute() {
|
|
10634
|
-
const config = loadRuntimeConfig();
|
|
10635
|
-
const knowledgeBases =
|
|
10798
|
+
const config = loadRuntimeConfig(sharedProjectRoot);
|
|
10799
|
+
const knowledgeBases = ensureStringArray(config.knowledgeBases);
|
|
10636
10800
|
if (knowledgeBases.length === 0) {
|
|
10637
10801
|
return "No knowledge bases configured. Use add_knowledge_base to add folders.";
|
|
10638
10802
|
}
|
|
@@ -10641,8 +10805,8 @@ var list_knowledge_bases = tool({
|
|
|
10641
10805
|
`;
|
|
10642
10806
|
for (let i = 0; i < knowledgeBases.length; i++) {
|
|
10643
10807
|
const kb = knowledgeBases[i];
|
|
10644
|
-
const resolvedPath =
|
|
10645
|
-
const exists =
|
|
10808
|
+
const resolvedPath = resolveKnowledgeBasePath(kb, sharedProjectRoot);
|
|
10809
|
+
const exists = existsSync9(resolvedPath);
|
|
10646
10810
|
result += `[${i + 1}] ${kb}
|
|
10647
10811
|
`;
|
|
10648
10812
|
result += ` Resolved: ${resolvedPath}
|
|
@@ -10651,7 +10815,7 @@ var list_knowledge_bases = tool({
|
|
|
10651
10815
|
`;
|
|
10652
10816
|
if (exists) {
|
|
10653
10817
|
try {
|
|
10654
|
-
const stat4 =
|
|
10818
|
+
const stat4 = statSync3(resolvedPath);
|
|
10655
10819
|
result += ` Type: ${stat4.isDirectory() ? "Directory" : "File"}
|
|
10656
10820
|
`;
|
|
10657
10821
|
} catch {
|
|
@@ -10659,7 +10823,7 @@ var list_knowledge_bases = tool({
|
|
|
10659
10823
|
}
|
|
10660
10824
|
result += "\n";
|
|
10661
10825
|
}
|
|
10662
|
-
result += `Config file: ${getConfigPath()}`;
|
|
10826
|
+
result += `Config file: ${getConfigPath(sharedProjectRoot)}`;
|
|
10663
10827
|
return result;
|
|
10664
10828
|
}
|
|
10665
10829
|
});
|
|
@@ -10670,15 +10834,12 @@ var remove_knowledge_base = tool({
|
|
|
10670
10834
|
},
|
|
10671
10835
|
async execute(args) {
|
|
10672
10836
|
const inputPath = args.path.trim();
|
|
10673
|
-
const config = loadEditableConfig();
|
|
10674
|
-
const knowledgeBases =
|
|
10837
|
+
const config = loadEditableConfig(sharedProjectRoot);
|
|
10838
|
+
const knowledgeBases = ensureStringArray(config.knowledgeBases);
|
|
10675
10839
|
if (knowledgeBases.length === 0) {
|
|
10676
10840
|
return "No knowledge bases configured.";
|
|
10677
10841
|
}
|
|
10678
|
-
const
|
|
10679
|
-
const index = knowledgeBases.findIndex(
|
|
10680
|
-
(kb) => path9.normalize(kb) === normalizedInput || path9.normalize(path9.isAbsolute(kb) ? kb : path9.resolve(sharedProjectRoot, kb)) === normalizedInput
|
|
10681
|
-
);
|
|
10842
|
+
const index = findKnowledgeBasePathIndex(knowledgeBases, inputPath, sharedProjectRoot);
|
|
10682
10843
|
if (index === -1) {
|
|
10683
10844
|
let result2 = `Knowledge base not found: ${inputPath}
|
|
10684
10845
|
|
|
@@ -10693,14 +10854,14 @@ var remove_knowledge_base = tool({
|
|
|
10693
10854
|
}
|
|
10694
10855
|
const removed = knowledgeBases.splice(index, 1)[0];
|
|
10695
10856
|
config.knowledgeBases = knowledgeBases;
|
|
10696
|
-
saveConfig(config);
|
|
10857
|
+
saveConfig(sharedProjectRoot, config);
|
|
10697
10858
|
refreshIndexerFromConfig();
|
|
10698
10859
|
let result = `Removed: ${removed}
|
|
10699
10860
|
|
|
10700
10861
|
`;
|
|
10701
10862
|
result += `Remaining knowledge bases: ${knowledgeBases.length}
|
|
10702
10863
|
`;
|
|
10703
|
-
result += `Config saved to: ${getConfigPath()}
|
|
10864
|
+
result += `Config saved to: ${getConfigPath(sharedProjectRoot)}
|
|
10704
10865
|
`;
|
|
10705
10866
|
result += `
|
|
10706
10867
|
Run /index to rebuild the index without the removed knowledge base.`;
|
|
@@ -10709,8 +10870,8 @@ Run /index to rebuild the index without the removed knowledge base.`;
|
|
|
10709
10870
|
});
|
|
10710
10871
|
|
|
10711
10872
|
// src/commands/loader.ts
|
|
10712
|
-
import { existsSync as
|
|
10713
|
-
import * as
|
|
10873
|
+
import { existsSync as existsSync10, readdirSync as readdirSync2, readFileSync as readFileSync7 } from "fs";
|
|
10874
|
+
import * as path16 from "path";
|
|
10714
10875
|
function parseFrontmatter(content) {
|
|
10715
10876
|
const frontmatterRegex = /^---\s*\n([\s\S]*?)\n---\s*\n([\s\S]*)$/;
|
|
10716
10877
|
const match = content.match(frontmatterRegex);
|
|
@@ -10731,15 +10892,21 @@ function parseFrontmatter(content) {
|
|
|
10731
10892
|
}
|
|
10732
10893
|
function loadCommandsFromDirectory(commandsDir) {
|
|
10733
10894
|
const commands = /* @__PURE__ */ new Map();
|
|
10734
|
-
if (!
|
|
10895
|
+
if (!existsSync10(commandsDir)) {
|
|
10735
10896
|
return commands;
|
|
10736
10897
|
}
|
|
10737
10898
|
const files = readdirSync2(commandsDir).filter((f) => f.endsWith(".md"));
|
|
10738
10899
|
for (const file of files) {
|
|
10739
|
-
const filePath =
|
|
10740
|
-
|
|
10900
|
+
const filePath = path16.join(commandsDir, file);
|
|
10901
|
+
let content;
|
|
10902
|
+
try {
|
|
10903
|
+
content = readFileSync7(filePath, "utf-8");
|
|
10904
|
+
} catch (error) {
|
|
10905
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
10906
|
+
throw new Error(`Failed to load command file ${filePath}: ${message}`);
|
|
10907
|
+
}
|
|
10741
10908
|
const { frontmatter, body } = parseFrontmatter(content);
|
|
10742
|
-
const name =
|
|
10909
|
+
const name = path16.basename(file, ".md");
|
|
10743
10910
|
const description = frontmatter.description || `Run the ${name} command`;
|
|
10744
10911
|
commands.set(name, {
|
|
10745
10912
|
description,
|
|
@@ -10749,7 +10916,7 @@ function loadCommandsFromDirectory(commandsDir) {
|
|
|
10749
10916
|
return commands;
|
|
10750
10917
|
}
|
|
10751
10918
|
|
|
10752
|
-
// src/routing-hints.ts
|
|
10919
|
+
// src/routing-hints-patterns.ts
|
|
10753
10920
|
var EXTERNAL_HINTS = [
|
|
10754
10921
|
"docs",
|
|
10755
10922
|
"documentation",
|
|
@@ -10840,6 +11007,8 @@ var SNAKE_PATTERN = /\b[a-z0-9]+_[a-z0-9_]+\b/g;
|
|
|
10840
11007
|
var KEBAB_PATTERN = /\b[a-z0-9]+-[a-z0-9-]+\b/g;
|
|
10841
11008
|
var BACKTICK_IDENTIFIER_PATTERN = /`([^`]+)`/g;
|
|
10842
11009
|
var BACKTICK_IDENTIFIER_PRESENCE_PATTERN = /`([^`]+)`/;
|
|
11010
|
+
var DOUBLE_QUOTED_PATTERN = /"[^"]+"/;
|
|
11011
|
+
var SINGLE_QUOTED_PATTERN = /'[^']+'/;
|
|
10843
11012
|
function normalizeText(text) {
|
|
10844
11013
|
return text.trim().replace(/\s+/g, " ");
|
|
10845
11014
|
}
|
|
@@ -10852,6 +11021,21 @@ function countWords(text) {
|
|
|
10852
11021
|
}
|
|
10853
11022
|
return text.split(/\s+/).filter(Boolean).length;
|
|
10854
11023
|
}
|
|
11024
|
+
function isExternalLookup(text) {
|
|
11025
|
+
return URL_PATTERN.test(text) || includesHint(text, EXTERNAL_HINTS);
|
|
11026
|
+
}
|
|
11027
|
+
function hasConceptualDiscoveryHint(text) {
|
|
11028
|
+
return includesHint(text, CONCEPTUAL_DISCOVERY_HINTS);
|
|
11029
|
+
}
|
|
11030
|
+
function hasDefinitionHint(text) {
|
|
11031
|
+
return includesHint(text, DEFINITION_HINTS);
|
|
11032
|
+
}
|
|
11033
|
+
function hasExactMatchHint(text) {
|
|
11034
|
+
return includesHint(text, EXACT_MATCH_HINTS);
|
|
11035
|
+
}
|
|
11036
|
+
function hasNonDiscoveryHint(text) {
|
|
11037
|
+
return includesHint(text, NON_DISCOVERY_HINTS);
|
|
11038
|
+
}
|
|
10855
11039
|
function hasIdentifierShape(text) {
|
|
10856
11040
|
const matches = [
|
|
10857
11041
|
...text.match(CAMEL_OR_PASCAL_PATTERN) ?? [],
|
|
@@ -10867,11 +11051,13 @@ function hasIdentifierShape(text) {
|
|
|
10867
11051
|
});
|
|
10868
11052
|
}
|
|
10869
11053
|
function containsQuotedIdentifier(text) {
|
|
10870
|
-
return BACKTICK_IDENTIFIER_PRESENCE_PATTERN.test(text) ||
|
|
11054
|
+
return BACKTICK_IDENTIFIER_PRESENCE_PATTERN.test(text) || DOUBLE_QUOTED_PATTERN.test(text) || SINGLE_QUOTED_PATTERN.test(text);
|
|
10871
11055
|
}
|
|
10872
11056
|
function looksLikeDirectPath(text) {
|
|
10873
11057
|
return FILE_PATH_PATTERN.test(text) || /\b[a-z0-9_-]+\.(ts|tsx|js|jsx|rs|py|go|java|json|md|yaml|yml)\b/i.test(text);
|
|
10874
11058
|
}
|
|
11059
|
+
|
|
11060
|
+
// src/routing-hints.ts
|
|
10875
11061
|
function extractUserText(parts) {
|
|
10876
11062
|
return normalizeText(
|
|
10877
11063
|
parts.filter((part) => part.type === "text" && typeof part.text === "string").map((part) => part.text ?? "").join(" ")
|
|
@@ -10887,21 +11073,21 @@ function assessRoutingIntent(text) {
|
|
|
10887
11073
|
reason: "empty_text"
|
|
10888
11074
|
};
|
|
10889
11075
|
}
|
|
10890
|
-
if (
|
|
11076
|
+
if (isExternalLookup(lowered)) {
|
|
10891
11077
|
return {
|
|
10892
11078
|
intent: "external",
|
|
10893
11079
|
text: normalizedText,
|
|
10894
11080
|
reason: "external_lookup"
|
|
10895
11081
|
};
|
|
10896
11082
|
}
|
|
10897
|
-
const
|
|
10898
|
-
const
|
|
10899
|
-
const
|
|
10900
|
-
const
|
|
11083
|
+
const matchedConceptualHint = hasConceptualDiscoveryHint(lowered);
|
|
11084
|
+
const matchedDefinitionHint = hasDefinitionHint(lowered);
|
|
11085
|
+
const matchedExactMatchHint = hasExactMatchHint(lowered);
|
|
11086
|
+
const matchedNonDiscoveryHint = hasNonDiscoveryHint(lowered);
|
|
10901
11087
|
const hasIdentifier = hasIdentifierShape(normalizedText);
|
|
10902
11088
|
const hasQuotedIdentifier = containsQuotedIdentifier(normalizedText);
|
|
10903
11089
|
const shortQuery = countWords(lowered) <= 10;
|
|
10904
|
-
if (
|
|
11090
|
+
if (matchedNonDiscoveryHint && !matchedConceptualHint) {
|
|
10905
11091
|
return {
|
|
10906
11092
|
intent: "other",
|
|
10907
11093
|
text: normalizedText,
|
|
@@ -10915,21 +11101,21 @@ function assessRoutingIntent(text) {
|
|
|
10915
11101
|
reason: "direct_path_reference"
|
|
10916
11102
|
};
|
|
10917
11103
|
}
|
|
10918
|
-
if ((
|
|
11104
|
+
if ((matchedDefinitionHint || lowered.includes("where is") || lowered.includes("where are")) && (lowered.includes("defined") || lowered.includes("definition"))) {
|
|
10919
11105
|
return {
|
|
10920
11106
|
intent: "definition_lookup",
|
|
10921
11107
|
text: normalizedText,
|
|
10922
11108
|
reason: "definition_lookup_request"
|
|
10923
11109
|
};
|
|
10924
11110
|
}
|
|
10925
|
-
if ((
|
|
11111
|
+
if ((matchedExactMatchHint || hasQuotedIdentifier || hasIdentifier) && !matchedConceptualHint && shortQuery) {
|
|
10926
11112
|
return {
|
|
10927
11113
|
intent: "exact_identifier",
|
|
10928
11114
|
text: normalizedText,
|
|
10929
|
-
reason:
|
|
11115
|
+
reason: matchedExactMatchHint || hasQuotedIdentifier ? "exact_match_request" : "identifier_shaped_query"
|
|
10930
11116
|
};
|
|
10931
11117
|
}
|
|
10932
|
-
if (
|
|
11118
|
+
if (matchedConceptualHint) {
|
|
10933
11119
|
return {
|
|
10934
11120
|
intent: "local_conceptual",
|
|
10935
11121
|
text: normalizedText,
|
|
@@ -11026,9 +11212,9 @@ function replaceActiveWatcher(nextWatcher) {
|
|
|
11026
11212
|
function getCommandsDir() {
|
|
11027
11213
|
let currentDir = process.cwd();
|
|
11028
11214
|
if (typeof import.meta !== "undefined" && import.meta.url) {
|
|
11029
|
-
currentDir =
|
|
11215
|
+
currentDir = path17.dirname(fileURLToPath2(import.meta.url));
|
|
11030
11216
|
}
|
|
11031
|
-
return
|
|
11217
|
+
return path17.join(currentDir, "..", "commands");
|
|
11032
11218
|
}
|
|
11033
11219
|
var plugin = async ({ directory }) => {
|
|
11034
11220
|
try {
|