@adhisang/minecraft-modding-mcp 6.3.0 → 7.0.0-rc.1
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/CHANGELOG.md +90 -0
- package/README.md +13 -3
- package/dist/cache-policy.d.ts +71 -0
- package/dist/cache-policy.js +83 -0
- package/dist/cache-registry.js +45 -8
- package/dist/cli.js +74 -3
- package/dist/compat-stdio-transport.d.ts +1 -1
- package/dist/compat-stdio-transport.js +13 -1
- package/dist/config.d.ts +3 -0
- package/dist/config.js +8 -2
- package/dist/decompiler/vineflower.d.ts +1 -0
- package/dist/decompiler/vineflower.js +8 -5
- package/dist/entry-tools/analyze-mod-service.d.ts +70 -136
- package/dist/entry-tools/analyze-symbol-service.d.ts +112 -150
- package/dist/entry-tools/compare-minecraft-service.d.ts +59 -145
- package/dist/entry-tools/entry-tool-schema.d.ts +38 -4
- package/dist/entry-tools/entry-tool-schema.js +4 -1
- package/dist/entry-tools/inspect-minecraft/internal.d.ts +235 -799
- package/dist/entry-tools/inspect-minecraft/internal.js +50 -13
- package/dist/entry-tools/inspect-minecraft-service.d.ts +372 -1736
- package/dist/entry-tools/manage-cache-service.d.ts +81 -91
- package/dist/entry-tools/validate-project/cases/mixin.js +26 -6
- package/dist/entry-tools/validate-project/cases/project-summary.d.ts +7 -7
- package/dist/entry-tools/validate-project-service.d.ts +164 -592
- package/dist/entry-tools/verify-mixin-target-service.d.ts +3 -19
- package/dist/entry-tools/verify-mixin-target-service.js +19 -1
- package/dist/era-classifier.d.ts +161 -0
- package/dist/era-classifier.js +292 -0
- package/dist/error-mapping.d.ts +76 -0
- package/dist/error-mapping.js +116 -8
- package/dist/index.d.ts +42 -4
- package/dist/index.js +636 -473
- package/dist/java-process.d.ts +2 -0
- package/dist/java-process.js +22 -2
- package/dist/json-rpc-framing.d.ts +77 -1
- package/dist/json-rpc-framing.js +249 -13
- package/dist/mapping/loaders/tiny-loom-selection.d.ts +88 -0
- package/dist/mapping/loaders/tiny-loom-selection.js +223 -0
- package/dist/mapping/loaders/tiny-loom.js +45 -33
- package/dist/mapping/loaders/tiny-maven.js +6 -11
- package/dist/mapping/parsers/tiny.d.ts +57 -0
- package/dist/mapping/parsers/tiny.js +99 -22
- package/dist/mapping-service.d.ts +19 -0
- package/dist/mapping-service.js +93 -9
- package/dist/maven-resolver.d.ts +18 -0
- package/dist/maven-resolver.js +20 -0
- package/dist/mcp-helpers.d.ts +19 -2
- package/dist/mcp-helpers.js +58 -9
- package/dist/minecraft-explorer-service.d.ts +1 -1
- package/dist/mixin/types.d.ts +8 -0
- package/dist/mod-analyzer.js +7 -7
- package/dist/mod-decompile-service.js +1 -0
- package/dist/nbt/java-nbt-codec.js +12 -2
- package/dist/nbt/json-patch.js +14 -3
- package/dist/nbt/pipeline.js +40 -3
- package/dist/nbt/typed-json.js +26 -1
- package/dist/registration-adapter.d.ts +32 -0
- package/dist/registration-adapter.js +52 -0
- package/dist/repo-downloader.d.ts +165 -0
- package/dist/repo-downloader.js +568 -10
- package/dist/request-context.d.ts +7 -0
- package/dist/request-context.js +9 -0
- package/dist/resources.d.ts +1 -1
- package/dist/resources.js +25 -19
- package/dist/server-identity.d.ts +27 -0
- package/dist/server-identity.js +26 -0
- package/dist/source/access-validate.js +53 -0
- package/dist/source/artifact-resolver.d.ts +81 -2
- package/dist/source/artifact-resolver.js +227 -15
- package/dist/source/class-source.d.ts +36 -0
- package/dist/source/class-source.js +222 -38
- package/dist/source/did-you-mean.d.ts +12 -1
- package/dist/source/did-you-mean.js +6 -2
- package/dist/source/file-access.js +150 -46
- package/dist/source/indexer.js +1 -0
- package/dist/source/shared-utils.d.ts +21 -0
- package/dist/source/shared-utils.js +23 -0
- package/dist/source-resolver.js +224 -57
- package/dist/source-service.d.ts +12 -1
- package/dist/stdio-supervisor.d.ts +357 -2
- package/dist/stdio-supervisor.js +1031 -80
- package/dist/storage/db.d.ts +2 -1
- package/dist/storage/db.js +15 -8
- package/dist/synthetic-decorator.d.ts +24 -0
- package/dist/synthetic-decorator.js +48 -0
- package/dist/tool-guidance.d.ts +17 -1
- package/dist/tool-guidance.js +323 -18
- package/dist/tool-schema-registry.d.ts +2 -0
- package/dist/tool-schema-registry.js +4 -0
- package/dist/tool-schemas.d.ts +2212 -3919
- package/dist/tool-schemas.js +33 -7
- package/dist/types.d.ts +35 -0
- package/dist/v1-parity-schemas.d.ts +7 -0
- package/dist/v1-parity-schemas.js +5584 -0
- package/dist/version-diff-service.d.ts +33 -0
- package/dist/version-diff-service.js +148 -3
- package/dist/version-service.js +36 -14
- package/dist/warning-details.js +18 -1
- package/docs/README-ja.md +5 -3
- package/docs/tool-reference.md +196 -19
- package/package.json +13 -8
|
@@ -10,7 +10,7 @@ import { collectDidYouMeanCandidates } from "./did-you-mean.js";
|
|
|
10
10
|
import { matchesMemberPattern } from "./member-pattern.js";
|
|
11
11
|
import { findNestedJarClasses, resolveUniqueNestedJarForClass } from "./nested-jars.js";
|
|
12
12
|
import { buildPageContextKey, encodeOffsetCursor, resolveCursorOffset } from "../page-cursor.js";
|
|
13
|
-
import { dedupeQualityFlags, normalizeMapping, normalizeOptionalString, normalizePathStyle } from "./shared-utils.js";
|
|
13
|
+
import { dedupeQualityFlags, inheritArtifactMapping, normalizeMapping, normalizeOptionalString, normalizePathStyle } from "./shared-utils.js";
|
|
14
14
|
import { isUnobfuscatedVersion } from "../version-service.js";
|
|
15
15
|
const MEMBERS_STATUS_LEGACY = process.env.MEMBERS_STATUS_LEGACY === "1";
|
|
16
16
|
/**
|
|
@@ -82,13 +82,35 @@ function obfuscatedNamespaceHint(className) {
|
|
|
82
82
|
function hasPartialNetMinecraftCoverage(qualityFlags) {
|
|
83
83
|
return qualityFlags.includes("partial-source-no-net-minecraft");
|
|
84
84
|
}
|
|
85
|
+
/**
|
|
86
|
+
* Whether "this artifact is indexed in obfuscated names, ask for mapping=mojang"
|
|
87
|
+
* is TRUE for this artifact, from the pieces of it the caller sees.
|
|
88
|
+
*
|
|
89
|
+
* Two artifact kinds report `mappingApplied: "obfuscated"` without being an
|
|
90
|
+
* obfuscated Minecraft index, and the hint is simply false for them:
|
|
91
|
+
* - a native dependency artifact, where the resolver SUBSTITUTES "obfuscated"
|
|
92
|
+
* after the mapping pipeline declines (its class names are already the
|
|
93
|
+
* library's own, so no mapping exists to ask for), and
|
|
94
|
+
* - a shell jar, which holds no classes at all — its content lives in the
|
|
95
|
+
* nested jars, and no mapping choice can change that.
|
|
96
|
+
*
|
|
97
|
+
* `looksLikeDeobfuscatedClassName` matches any capitalized simple name, so
|
|
98
|
+
* without these exclusions an ordinary library class such as "GameTest"
|
|
99
|
+
* qualifies and the caller is told to remap a jar that was never obfuscated.
|
|
100
|
+
*/
|
|
101
|
+
function isObfuscatedNamespaceHintTrue(input) {
|
|
102
|
+
return (input.mappingApplied === "obfuscated" &&
|
|
103
|
+
!input.nativeDependency &&
|
|
104
|
+
!input.qualityFlags.includes("shell-jar") &&
|
|
105
|
+
looksLikeDeobfuscatedClassName(input.className));
|
|
106
|
+
}
|
|
85
107
|
function shouldSuggestObfuscatedMapping(artifact, className) {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
108
|
+
return isObfuscatedNamespaceHintTrue({
|
|
109
|
+
mappingApplied: artifact.mappingApplied,
|
|
110
|
+
qualityFlags: artifact.qualityFlags,
|
|
111
|
+
nativeDependency: artifact.provenance?.dependencyResolution != null,
|
|
112
|
+
className
|
|
113
|
+
});
|
|
92
114
|
}
|
|
93
115
|
function classNameToClassPath(className) {
|
|
94
116
|
const normalized = normalizePathStyle(className.trim()).replace(/\//g, ".");
|
|
@@ -175,10 +197,26 @@ export function buildFallbackProvenance(svc, input) {
|
|
|
175
197
|
transformChain
|
|
176
198
|
};
|
|
177
199
|
}
|
|
200
|
+
/**
|
|
201
|
+
* Near-miss candidates from the requested artifact's index followed by any the
|
|
202
|
+
* lookup's final artifact contributes, deduplicated by FQN so a class present in
|
|
203
|
+
* both is reported once, under the requested artifact.
|
|
204
|
+
*/
|
|
205
|
+
function unionDidYouMeanCandidates(svc, requestedArtifactId, endedOnArtifactId, className) {
|
|
206
|
+
const requested = collectDidYouMeanCandidates(svc, requestedArtifactId, className);
|
|
207
|
+
if (endedOnArtifactId === requestedArtifactId) {
|
|
208
|
+
return requested;
|
|
209
|
+
}
|
|
210
|
+
const seen = new Set(requested.map((candidate) => candidate.className));
|
|
211
|
+
const fromFallback = collectDidYouMeanCandidates(svc, endedOnArtifactId, className, endedOnArtifactId).filter((candidate) => !seen.has(candidate.className));
|
|
212
|
+
return [...requested, ...fromFallback];
|
|
213
|
+
}
|
|
178
214
|
export function buildClassSourceNotFoundError(svc, input) {
|
|
179
215
|
const simpleName = input.className.split(/[.$]/).at(-1) ?? input.className;
|
|
216
|
+
const requestedArtifactId = input.requestedArtifactId ?? input.artifactId;
|
|
180
217
|
const details = {
|
|
181
|
-
artifactId:
|
|
218
|
+
artifactId: requestedArtifactId,
|
|
219
|
+
...(input.artifactId !== requestedArtifactId ? { fallbackArtifactId: input.artifactId } : {}),
|
|
182
220
|
className: input.className,
|
|
183
221
|
mapping: input.mappingApplied,
|
|
184
222
|
qualityFlags: input.qualityFlags,
|
|
@@ -191,12 +229,18 @@ export function buildClassSourceNotFoundError(svc, input) {
|
|
|
191
229
|
...(input.nestedJars && input.nestedJars.length > 0 ? { nestedJars: input.nestedJars } : {}),
|
|
192
230
|
// Candidates are hints from the symbol index, never assertions that the
|
|
193
231
|
// class exists at the suggested location; empty when nothing usable.
|
|
194
|
-
|
|
232
|
+
//
|
|
233
|
+
// Both indexes are consulted, requested artifact first. Collecting from the
|
|
234
|
+
// requested artifact ALONE is empty by construction in the scenario the
|
|
235
|
+
// partial-source fallback exists to serve: that artifact is the one without
|
|
236
|
+
// net.minecraft symbols, which is why the fallback fired and indexed the other
|
|
237
|
+
// jar. Candidates from the artifact the caller did not name carry its id.
|
|
238
|
+
didYouMean: unionDidYouMeanCandidates(svc, requestedArtifactId, input.artifactId, input.className)
|
|
195
239
|
};
|
|
196
240
|
let nextAction = `Use find-class to resolve the correct fully-qualified name for "${simpleName}".`;
|
|
197
241
|
let suggestionSpec = {
|
|
198
242
|
tool: "find-class",
|
|
199
|
-
params: { className: simpleName, artifactId:
|
|
243
|
+
params: { className: simpleName, artifactId: requestedArtifactId }
|
|
200
244
|
};
|
|
201
245
|
if (input.targetKind === "version" && input.scope && input.scope !== "merged" && !input.projectPath) {
|
|
202
246
|
nextAction +=
|
|
@@ -225,11 +269,32 @@ export function buildClassSourceNotFoundError(svc, input) {
|
|
|
225
269
|
else {
|
|
226
270
|
suggestionSpec = {
|
|
227
271
|
tool: "find-class",
|
|
228
|
-
params: { className: simpleName, artifactId:
|
|
272
|
+
params: { className: simpleName, artifactId: requestedArtifactId }
|
|
229
273
|
};
|
|
230
274
|
}
|
|
231
275
|
}
|
|
232
|
-
|
|
276
|
+
// Two gates, because the hint fails in two independent ways.
|
|
277
|
+
//
|
|
278
|
+
// `isObfuscatedNamespaceHintTrue` decides whether the claim is even true of
|
|
279
|
+
// this artifact — a dependency jar or a shell jar reports "obfuscated"
|
|
280
|
+
// without being an obfuscated index, and used to be told to remap itself.
|
|
281
|
+
//
|
|
282
|
+
// The second gate is about the ASK, not the claim: the hint ends in
|
|
283
|
+
// `mapping="mojang"`, so firing it at a caller who already sent a
|
|
284
|
+
// non-obfuscated mapping asks for the argument they just supplied and sends
|
|
285
|
+
// them round the same call again. The generic backstop in
|
|
286
|
+
// `dropSatisfiedParameterAsks` cannot rescue this one — it matches an
|
|
287
|
+
// imperative "Provide/Pass mapping", and the sentence is concatenated into
|
|
288
|
+
// the same `nextAction` string as the find-class guidance, which is published
|
|
289
|
+
// as a single hint that no mid-string excision can repair.
|
|
290
|
+
const callerAskedForNonObfuscatedMapping = input.callerSuppliedMapping != null && input.callerSuppliedMapping !== "obfuscated";
|
|
291
|
+
if (!callerAskedForNonObfuscatedMapping &&
|
|
292
|
+
isObfuscatedNamespaceHintTrue({
|
|
293
|
+
mappingApplied: input.mappingApplied,
|
|
294
|
+
qualityFlags: input.qualityFlags,
|
|
295
|
+
nativeDependency: input.nativeDependency === true,
|
|
296
|
+
className: input.className
|
|
297
|
+
})) {
|
|
233
298
|
nextAction += ` ${obfuscatedNamespaceHint(input.className)}`;
|
|
234
299
|
}
|
|
235
300
|
details.nextAction = nextAction;
|
|
@@ -403,13 +468,14 @@ export function findClass(svc, input) {
|
|
|
403
468
|
const filteredMatches = partialVanillaLookup && matches.every((match) => !match.qualifiedName.startsWith("net.minecraft.") && !match.qualifiedName.startsWith("com.mojang."))
|
|
404
469
|
? []
|
|
405
470
|
: matches;
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
471
|
+
return finishFindClass(svc, {
|
|
472
|
+
artifact,
|
|
473
|
+
artifactId,
|
|
474
|
+
className,
|
|
475
|
+
matches: filteredMatches,
|
|
476
|
+
partialVanillaLookup,
|
|
477
|
+
warnings
|
|
478
|
+
});
|
|
413
479
|
}
|
|
414
480
|
const result = svc.symbolsRepo.findScopedSymbols({
|
|
415
481
|
artifactId,
|
|
@@ -418,32 +484,82 @@ export function findClass(svc, input) {
|
|
|
418
484
|
symbolKinds: TYPE_SYMBOL_KINDS,
|
|
419
485
|
limit: limit * 5
|
|
420
486
|
});
|
|
421
|
-
|
|
487
|
+
// The extractor stores ONE qualifiedName per FILE (the top-level type), so a
|
|
488
|
+
// nested type's row carries its OUTER type's FQN. Reporting that verbatim
|
|
489
|
+
// returned a qualifiedName that did not contain the searched token at all —
|
|
490
|
+
// feeding match[0] into get-class-source then fetched the wrong class.
|
|
491
|
+
const candidates = [];
|
|
422
492
|
for (const row of result.items) {
|
|
423
|
-
if (matches.length >= limit)
|
|
424
|
-
break;
|
|
425
493
|
const isTypeSymbol = row.symbolKind === "class" || row.symbolKind === "interface" ||
|
|
426
494
|
row.symbolKind === "enum" || row.symbolKind === "record";
|
|
427
495
|
if (!isTypeSymbol)
|
|
428
496
|
continue;
|
|
429
|
-
|
|
430
|
-
|
|
497
|
+
const enclosingQualifiedName = row.qualifiedName ?? row.filePath.replace(/\.java$/, "").replaceAll("/", ".");
|
|
498
|
+
const enclosingSimpleName = enclosingQualifiedName.split(".").at(-1) ?? enclosingQualifiedName;
|
|
499
|
+
const nested = enclosingSimpleName !== row.symbolName;
|
|
500
|
+
candidates.push({
|
|
501
|
+
qualifiedName: nested ? `${enclosingQualifiedName}.${row.symbolName}` : enclosingQualifiedName,
|
|
431
502
|
filePath: row.filePath,
|
|
432
503
|
line: row.line,
|
|
433
|
-
symbolKind: row.symbolKind
|
|
504
|
+
symbolKind: row.symbolKind,
|
|
505
|
+
...(nested ? { nested: true, enclosingClass: enclosingQualifiedName } : {})
|
|
434
506
|
});
|
|
435
507
|
}
|
|
508
|
+
// A top-level type named exactly like the query is what the caller almost
|
|
509
|
+
// always means; a nested type that merely shares the simple name comes after.
|
|
510
|
+
candidates.sort((left, right) => {
|
|
511
|
+
const leftNested = left.nested === true ? 1 : 0;
|
|
512
|
+
const rightNested = right.nested === true ? 1 : 0;
|
|
513
|
+
if (leftNested !== rightNested)
|
|
514
|
+
return leftNested - rightNested;
|
|
515
|
+
return left.qualifiedName.localeCompare(right.qualifiedName);
|
|
516
|
+
});
|
|
517
|
+
const matches = candidates.slice(0, limit);
|
|
436
518
|
const partialVanillaLookup = hasPartialNetMinecraftCoverage(artifact.qualityFlags) && looksLikeDeobfuscatedClassName(className);
|
|
437
519
|
const filteredMatches = partialVanillaLookup && matches.every((match) => !match.qualifiedName.startsWith("net.minecraft.") && !match.qualifiedName.startsWith("com.mojang."))
|
|
438
520
|
? []
|
|
439
521
|
: matches;
|
|
440
|
-
|
|
522
|
+
return finishFindClass(svc, {
|
|
523
|
+
artifact,
|
|
524
|
+
artifactId,
|
|
525
|
+
className,
|
|
526
|
+
matches: filteredMatches,
|
|
527
|
+
partialVanillaLookup,
|
|
528
|
+
warnings
|
|
529
|
+
});
|
|
530
|
+
}
|
|
531
|
+
/**
|
|
532
|
+
* Shared tail for both findClass branches: coverage/namespace warnings plus a
|
|
533
|
+
* machine-usable recovery route.
|
|
534
|
+
*
|
|
535
|
+
* An empty result on an artifact whose index excludes net.minecraft is
|
|
536
|
+
* internally consistent but externally contradictory — get-class-source answers
|
|
537
|
+
* the SAME class from the binary fallback. The caller therefore gets both the
|
|
538
|
+
* reason and the exact call that works.
|
|
539
|
+
*/
|
|
540
|
+
function finishFindClass(svc, input) {
|
|
541
|
+
const { artifact, artifactId, className, matches, partialVanillaLookup, warnings } = input;
|
|
542
|
+
let suggestedCall;
|
|
543
|
+
if (matches.length === 0 && partialVanillaLookup) {
|
|
441
544
|
warnings.push(`Artifact source coverage is partial and excludes net.minecraft; returning non-vanilla matches for "${className}" would be misleading. Use get-class-source/get-class-members for binary fallback or get-class-api-matrix for mapped API inspection.`);
|
|
545
|
+
suggestedCall = buildSuggestedCall({
|
|
546
|
+
tool: "get-class-source",
|
|
547
|
+
params: {
|
|
548
|
+
className,
|
|
549
|
+
target: { kind: "artifact", artifactId },
|
|
550
|
+
mode: "metadata"
|
|
551
|
+
}
|
|
552
|
+
}).suggestedCall;
|
|
442
553
|
}
|
|
443
|
-
if (
|
|
554
|
+
if (matches.length === 0 && shouldSuggestObfuscatedMapping(artifact, className)) {
|
|
444
555
|
warnings.push(`No exact class symbol matched "${className}". ${obfuscatedNamespaceHint(className)}`);
|
|
445
556
|
}
|
|
446
|
-
return {
|
|
557
|
+
return {
|
|
558
|
+
matches,
|
|
559
|
+
total: matches.length,
|
|
560
|
+
warnings,
|
|
561
|
+
...(suggestedCall ? { suggestedCall } : {})
|
|
562
|
+
};
|
|
447
563
|
}
|
|
448
564
|
export async function findClassIncludingNested(svc, input) {
|
|
449
565
|
const indexed = findClass(svc, input);
|
|
@@ -507,6 +623,9 @@ export async function getClassSource(svc, input) {
|
|
|
507
623
|
}
|
|
508
624
|
let artifactId = normalizedArtifactId;
|
|
509
625
|
let origin = "local-jar";
|
|
626
|
+
// Derivation, tracked separately from origin: whether the indexed text was
|
|
627
|
+
// produced by decompiling bytecode. Only the persisted flag answers that.
|
|
628
|
+
let isDecompiled = false;
|
|
510
629
|
let warnings = [];
|
|
511
630
|
let requestedMapping = normalizeMapping(input.mapping);
|
|
512
631
|
let mappingApplied = requestedMapping;
|
|
@@ -536,6 +655,7 @@ export async function getClassSource(svc, input) {
|
|
|
536
655
|
});
|
|
537
656
|
artifactId = resolved.artifactId;
|
|
538
657
|
origin = resolved.origin;
|
|
658
|
+
isDecompiled = resolved.isDecompiled;
|
|
539
659
|
warnings = [...resolved.warnings];
|
|
540
660
|
requestedMapping = resolved.requestedMapping;
|
|
541
661
|
mappingApplied = resolved.mappingApplied;
|
|
@@ -550,7 +670,8 @@ export async function getClassSource(svc, input) {
|
|
|
550
670
|
const artifact = svc.getArtifact(artifactId);
|
|
551
671
|
artifactId = artifact.artifactId;
|
|
552
672
|
origin = artifact.origin;
|
|
553
|
-
|
|
673
|
+
isDecompiled = artifact.isDecompiled;
|
|
674
|
+
requestedMapping = inheritArtifactMapping(input.mapping, artifact);
|
|
554
675
|
mappingApplied = artifact.mappingApplied ?? requestedMapping;
|
|
555
676
|
provenance = artifact.provenance;
|
|
556
677
|
qualityFlags = artifact.qualityFlags;
|
|
@@ -570,6 +691,7 @@ export async function getClassSource(svc, input) {
|
|
|
570
691
|
mappingApplied = reconcileUnobfuscatedNamespace(version, requestedMapping, mappingApplied);
|
|
571
692
|
let activeArtifactId = artifactId;
|
|
572
693
|
let activeOrigin = origin;
|
|
694
|
+
let activeIsDecompiled = isDecompiled;
|
|
573
695
|
let activeProvenance = provenance;
|
|
574
696
|
let activeQualityFlags = [...qualityFlags];
|
|
575
697
|
let activeMappingApplied = mappingApplied;
|
|
@@ -595,13 +717,18 @@ export async function getClassSource(svc, input) {
|
|
|
595
717
|
requestedMapping,
|
|
596
718
|
mappingApplied,
|
|
597
719
|
provenance: activeProvenance,
|
|
598
|
-
qualityFlags: activeQualityFlags
|
|
720
|
+
qualityFlags: activeQualityFlags,
|
|
721
|
+
// A caller who declined decompilation must not pay for one here: the
|
|
722
|
+
// fallback used to hardcode allowDecompile:true and silently ran a full
|
|
723
|
+
// Vineflower pass on the binary jar.
|
|
724
|
+
allowDecompile: input.allowDecompile
|
|
599
725
|
});
|
|
600
726
|
if (!fallbackResolved || fallbackResolved.artifactId === activeArtifactId) {
|
|
601
727
|
return false;
|
|
602
728
|
}
|
|
603
729
|
activeArtifactId = fallbackResolved.artifactId;
|
|
604
730
|
activeOrigin = fallbackResolved.origin;
|
|
731
|
+
activeIsDecompiled = fallbackResolved.isDecompiled;
|
|
605
732
|
activeMappingApplied = fallbackResolved.mappingApplied ?? activeMappingApplied;
|
|
606
733
|
activeProvenance = fallbackResolved.provenance ?? activeProvenance;
|
|
607
734
|
activeQualityFlags = dedupeQualityFlags([...(fallbackResolved.qualityFlags ?? []), "binary-fallback"]);
|
|
@@ -644,6 +771,9 @@ export async function getClassSource(svc, input) {
|
|
|
644
771
|
});
|
|
645
772
|
activeArtifactId = redirectResolved.artifactId;
|
|
646
773
|
activeOrigin = redirectResolved.origin;
|
|
774
|
+
// The INNER jar's derivation: the shell that bundled it holds no source of
|
|
775
|
+
// its own, so its flag says nothing about the text being returned here.
|
|
776
|
+
activeIsDecompiled = redirectResolved.isDecompiled;
|
|
647
777
|
activeMappingApplied = redirectResolved.mappingApplied ?? activeMappingApplied;
|
|
648
778
|
activeProvenance = redirectResolved.provenance
|
|
649
779
|
? {
|
|
@@ -689,11 +819,17 @@ export async function getClassSource(svc, input) {
|
|
|
689
819
|
if (!filePath) {
|
|
690
820
|
throw buildClassSourceNotFoundError(svc, {
|
|
691
821
|
artifactId: activeArtifactId,
|
|
822
|
+
requestedArtifactId: artifactId,
|
|
692
823
|
className,
|
|
693
824
|
lookupClassName: activeLookupClassName,
|
|
694
|
-
|
|
825
|
+
// The REQUESTED artifact's namespace and quality, matching the artifactId this
|
|
826
|
+
// error reports. The nested-jar redirect replaces the active values with the
|
|
827
|
+
// inner jar's, which would otherwise describe an artifact the caller never named.
|
|
828
|
+
mappingApplied,
|
|
695
829
|
requestedMapping,
|
|
696
|
-
qualityFlags
|
|
830
|
+
qualityFlags,
|
|
831
|
+
callerSuppliedMapping: input.mapping,
|
|
832
|
+
nativeDependency: provenance?.dependencyResolution != null,
|
|
697
833
|
attemptedBinaryFallback,
|
|
698
834
|
targetKind: input.target?.kind,
|
|
699
835
|
targetValue: input.target && "value" in input.target ? input.target.value : undefined,
|
|
@@ -728,11 +864,17 @@ export async function getClassSource(svc, input) {
|
|
|
728
864
|
if (!row) {
|
|
729
865
|
throw buildClassSourceNotFoundError(svc, {
|
|
730
866
|
artifactId: activeArtifactId,
|
|
867
|
+
requestedArtifactId: artifactId,
|
|
731
868
|
className,
|
|
732
869
|
lookupClassName: activeLookupClassName,
|
|
733
|
-
|
|
870
|
+
// The REQUESTED artifact's namespace and quality, matching the artifactId this
|
|
871
|
+
// error reports. The nested-jar redirect replaces the active values with the
|
|
872
|
+
// inner jar's, which would otherwise describe an artifact the caller never named.
|
|
873
|
+
mappingApplied,
|
|
734
874
|
requestedMapping,
|
|
735
|
-
qualityFlags
|
|
875
|
+
qualityFlags,
|
|
876
|
+
callerSuppliedMapping: input.mapping,
|
|
877
|
+
nativeDependency: provenance?.dependencyResolution != null,
|
|
736
878
|
attemptedBinaryFallback,
|
|
737
879
|
filePath,
|
|
738
880
|
targetKind: input.target?.kind,
|
|
@@ -800,7 +942,16 @@ export async function getClassSource(svc, input) {
|
|
|
800
942
|
// decompiled source vs `gameRenderState()` in the runtime jar). Flag it so
|
|
801
943
|
// callers verify names against get-class-members (bytecode-derived) before
|
|
802
944
|
// copying signatures out of this source.
|
|
803
|
-
|
|
945
|
+
//
|
|
946
|
+
// The gate is the persisted `isDecompiled` flag, never `origin === "decompiled"`:
|
|
947
|
+
// origin records WHERE the bytes came from, derivation records WHETHER the text
|
|
948
|
+
// was decompiled, and the two disagree. A Jar-in-Jar shell keeps the origin the
|
|
949
|
+
// resolver chose while ingest clears its derivation flag (src/source/indexer.ts),
|
|
950
|
+
// so an origin-keyed gate warned about decompiled signatures for a jar that holds
|
|
951
|
+
// no source at all. file-access, search, artifact-resolver and the mapping
|
|
952
|
+
// pipeline all read the boolean; do not "restore consistency" by keying this
|
|
953
|
+
// back on the origin.
|
|
954
|
+
if (activeIsDecompiled) {
|
|
804
955
|
activeQualityFlags = dedupeQualityFlags([
|
|
805
956
|
...activeQualityFlags,
|
|
806
957
|
"decompiled-source-signatures-unverified"
|
|
@@ -832,7 +983,7 @@ export async function getClassSource(svc, input) {
|
|
|
832
983
|
artifactContents: svc.buildArtifactContentsSummary({
|
|
833
984
|
origin: activeOrigin,
|
|
834
985
|
sourceJarPath: activeSourceJarPath,
|
|
835
|
-
isDecompiled:
|
|
986
|
+
isDecompiled: activeIsDecompiled,
|
|
836
987
|
qualityFlags: activeQualityFlags
|
|
837
988
|
}),
|
|
838
989
|
...(continuation?.suggestedCall ? { suggestedCall: continuation.suggestedCall } : {}),
|
|
@@ -865,6 +1016,8 @@ export async function getClassMembers(svc, input) {
|
|
|
865
1016
|
}
|
|
866
1017
|
let artifactId = normalizedArtifactId;
|
|
867
1018
|
let origin = "local-jar";
|
|
1019
|
+
// See getClassSource: derivation is the persisted flag, not the origin.
|
|
1020
|
+
let isDecompiled = false;
|
|
868
1021
|
let warnings = [];
|
|
869
1022
|
let mappingApplied = requestedMapping;
|
|
870
1023
|
let provenance;
|
|
@@ -896,6 +1049,7 @@ export async function getClassMembers(svc, input) {
|
|
|
896
1049
|
});
|
|
897
1050
|
artifactId = resolved.artifactId;
|
|
898
1051
|
origin = resolved.origin;
|
|
1052
|
+
isDecompiled = resolved.isDecompiled;
|
|
899
1053
|
warnings.push(...resolved.warnings);
|
|
900
1054
|
requestedMapping = resolved.requestedMapping;
|
|
901
1055
|
mappingApplied = resolved.mappingApplied;
|
|
@@ -910,6 +1064,8 @@ export async function getClassMembers(svc, input) {
|
|
|
910
1064
|
const artifact = svc.getArtifact(artifactId);
|
|
911
1065
|
artifactId = artifact.artifactId;
|
|
912
1066
|
origin = artifact.origin;
|
|
1067
|
+
isDecompiled = artifact.isDecompiled;
|
|
1068
|
+
requestedMapping = inheritArtifactMapping(input.mapping, artifact);
|
|
913
1069
|
mappingApplied = artifact.mappingApplied ?? requestedMapping;
|
|
914
1070
|
provenance = artifact.provenance;
|
|
915
1071
|
qualityFlags = artifact.qualityFlags;
|
|
@@ -939,12 +1095,27 @@ export async function getClassMembers(svc, input) {
|
|
|
939
1095
|
});
|
|
940
1096
|
}
|
|
941
1097
|
if (!binaryJarPath) {
|
|
1098
|
+
// The override below applies only when the TOOL picked this artifact
|
|
1099
|
+
// without the caller expressing an opinion on it: a version/coordinate
|
|
1100
|
+
// target, or falling through the workspace/dependency shapes. A caller who
|
|
1101
|
+
// passed `artifactId` directly, or `target: { kind: "jar", ... }`, named
|
|
1102
|
+
// the exact artifact/jar themselves - having no binary companion is then
|
|
1103
|
+
// exactly the input they can change.
|
|
1104
|
+
const artifactWasCallerNamed = Boolean(normalizedArtifactId) || input.target?.kind === "jar";
|
|
942
1105
|
throw createError({
|
|
943
1106
|
code: ERROR_CODES.CONTEXT_UNRESOLVED,
|
|
944
1107
|
message: `Class members require a binary jar, but artifact "${artifactId}" has no binaryJarPath.`,
|
|
945
1108
|
details: {
|
|
946
1109
|
artifactId,
|
|
947
1110
|
className,
|
|
1111
|
+
// `ERR_CONTEXT_UNRESOLVED` classifies as `code_issue` by code, which is
|
|
1112
|
+
// right for the sibling case (a caller naming a version no artifact
|
|
1113
|
+
// carries) and wrong when the artifact was resolved by the TOOL, since
|
|
1114
|
+
// whether it carries a binary jar is not something that request could
|
|
1115
|
+
// express. Published as caller-fixable it invites an endless retry of
|
|
1116
|
+
// an input that was never at fault, so this site overrides the default
|
|
1117
|
+
// for the tool-resolved case only.
|
|
1118
|
+
issueOrigin: artifactWasCallerNamed ? undefined : "tool_issue",
|
|
948
1119
|
nextAction: "Resolve with target: { kind: \"jar\" | \"version\", value: ... } or use an artifact that has a binary jar."
|
|
949
1120
|
}
|
|
950
1121
|
});
|
|
@@ -1037,6 +1208,8 @@ export async function getClassMembers(svc, input) {
|
|
|
1037
1208
|
mappingApplied,
|
|
1038
1209
|
requestedMapping,
|
|
1039
1210
|
qualityFlags,
|
|
1211
|
+
callerSuppliedMapping: input.mapping,
|
|
1212
|
+
nativeDependency: provenance?.dependencyResolution != null,
|
|
1040
1213
|
attemptedBinaryFallback: true,
|
|
1041
1214
|
targetKind: input.target?.kind,
|
|
1042
1215
|
targetValue: input.target && "value" in input.target ? input.target.value : undefined,
|
|
@@ -1179,7 +1352,18 @@ export async function getClassMembers(svc, input) {
|
|
|
1179
1352
|
truncated,
|
|
1180
1353
|
...(nextCursor ? { nextCursor } : {}),
|
|
1181
1354
|
...(memberCursorIgnored ? { cursorIgnored: true } : {}),
|
|
1182
|
-
context:
|
|
1355
|
+
context: {
|
|
1356
|
+
...signatureContext,
|
|
1357
|
+
// The jar-derived context describes the BYTECODE the reader opened. The
|
|
1358
|
+
// members handed back have already been remapped into requestedMapping,
|
|
1359
|
+
// so echoing the jar namespace here contradicted `returnedNamespace` in
|
|
1360
|
+
// the same payload. Report what the response actually contains, and fill
|
|
1361
|
+
// the version the resolver established when the jar path yielded none.
|
|
1362
|
+
...(signatureContext.minecraftVersion === "unknown" && version
|
|
1363
|
+
? { minecraftVersion: version }
|
|
1364
|
+
: {}),
|
|
1365
|
+
mappingNamespace: requestedMapping
|
|
1366
|
+
},
|
|
1183
1367
|
origin,
|
|
1184
1368
|
artifactId,
|
|
1185
1369
|
requestedMapping,
|
|
@@ -1190,7 +1374,7 @@ export async function getClassMembers(svc, input) {
|
|
|
1190
1374
|
artifactContents: svc.buildArtifactContentsSummary({
|
|
1191
1375
|
origin,
|
|
1192
1376
|
sourceJarPath,
|
|
1193
|
-
isDecompiled
|
|
1377
|
+
isDecompiled,
|
|
1194
1378
|
qualityFlags: fallbackQualityFlags
|
|
1195
1379
|
}),
|
|
1196
1380
|
...(decompiledFallback ? { decompiledFallback } : {}),
|
|
@@ -2,6 +2,13 @@ import type { SourceService } from "../source-service.js";
|
|
|
2
2
|
export interface DidYouMeanCandidate {
|
|
3
3
|
className: string;
|
|
4
4
|
matchReason: string;
|
|
5
|
+
/**
|
|
6
|
+
* Set only when the candidate was found in an artifact OTHER than the one the caller
|
|
7
|
+
* named — today, the internal binary fallback or nested-jar redirect the lookup ended
|
|
8
|
+
* on. An unattributed candidate always means "in the artifact you asked about", so the
|
|
9
|
+
* field marks the exception rather than restating the common case on every entry.
|
|
10
|
+
*/
|
|
11
|
+
artifactId?: string;
|
|
5
12
|
}
|
|
6
13
|
/**
|
|
7
14
|
* Collects ranked near-miss class candidates from the artifact's symbol
|
|
@@ -10,5 +17,9 @@ export interface DidYouMeanCandidate {
|
|
|
10
17
|
* edit-distance suggestions. Candidates are hints, never assertions that the
|
|
11
18
|
* class exists at the suggested location. Returns an empty array when the
|
|
12
19
|
* index has nothing usable.
|
|
20
|
+
*
|
|
21
|
+
* `attributeTo` stamps every produced candidate with that artifact id; pass it when
|
|
22
|
+
* collecting from an artifact the caller did not name, and leave it unset for the
|
|
23
|
+
* requested artifact.
|
|
13
24
|
*/
|
|
14
|
-
export declare function collectDidYouMeanCandidates(svc: SourceService, artifactId: string, className: string): DidYouMeanCandidate[];
|
|
25
|
+
export declare function collectDidYouMeanCandidates(svc: SourceService, artifactId: string, className: string, attributeTo?: string): DidYouMeanCandidate[];
|
|
@@ -15,8 +15,12 @@ function fqnOfRow(row) {
|
|
|
15
15
|
* edit-distance suggestions. Candidates are hints, never assertions that the
|
|
16
16
|
* class exists at the suggested location. Returns an empty array when the
|
|
17
17
|
* index has nothing usable.
|
|
18
|
+
*
|
|
19
|
+
* `attributeTo` stamps every produced candidate with that artifact id; pass it when
|
|
20
|
+
* collecting from an artifact the caller did not name, and leave it unset for the
|
|
21
|
+
* requested artifact.
|
|
18
22
|
*/
|
|
19
|
-
export function collectDidYouMeanCandidates(svc, artifactId, className) {
|
|
23
|
+
export function collectDidYouMeanCandidates(svc, artifactId, className, attributeTo) {
|
|
20
24
|
try {
|
|
21
25
|
const simpleName = className.split(/[.$]/).at(-1) ?? className;
|
|
22
26
|
if (!simpleName) {
|
|
@@ -29,7 +33,7 @@ export function collectDidYouMeanCandidates(svc, artifactId, className) {
|
|
|
29
33
|
return;
|
|
30
34
|
}
|
|
31
35
|
seen.add(fqn);
|
|
32
|
-
out.push({ className: fqn, matchReason });
|
|
36
|
+
out.push({ className: fqn, matchReason, ...(attributeTo ? { artifactId: attributeTo } : {}) });
|
|
33
37
|
};
|
|
34
38
|
const exact = svc.symbolsRepo.findScopedSymbols({
|
|
35
39
|
artifactId,
|