@adhisang/minecraft-modding-mcp 4.0.0 → 4.1.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 +61 -0
- package/README.md +40 -23
- package/dist/build-suggested-call.d.ts +29 -0
- package/dist/build-suggested-call.js +58 -0
- package/dist/cache-registry.d.ts +3 -1
- package/dist/cache-registry.js +50 -6
- package/dist/entry-tools/analyze-symbol-service.d.ts +16 -16
- package/dist/entry-tools/batch-class-members-service.d.ts +34 -0
- package/dist/entry-tools/batch-class-members-service.js +97 -0
- package/dist/entry-tools/batch-class-source-service.d.ts +37 -0
- package/dist/entry-tools/batch-class-source-service.js +100 -0
- package/dist/entry-tools/batch-mappings-service.d.ts +36 -0
- package/dist/entry-tools/batch-mappings-service.js +66 -0
- package/dist/entry-tools/batch-runner.d.ts +72 -0
- package/dist/entry-tools/batch-runner.js +90 -0
- package/dist/entry-tools/batch-symbol-exists-service.d.ts +46 -0
- package/dist/entry-tools/batch-symbol-exists-service.js +113 -0
- package/dist/entry-tools/compare-minecraft-service.d.ts +6 -6
- package/dist/entry-tools/inspect-minecraft/handlers/artifact.d.ts +5 -0
- package/dist/entry-tools/inspect-minecraft/handlers/artifact.js +83 -0
- package/dist/entry-tools/inspect-minecraft/handlers/class-members.d.ts +6 -0
- package/dist/entry-tools/inspect-minecraft/handlers/class-members.js +80 -0
- package/dist/entry-tools/inspect-minecraft/handlers/class-overview.d.ts +5 -0
- package/dist/entry-tools/inspect-minecraft/handlers/class-overview.js +248 -0
- package/dist/entry-tools/inspect-minecraft/handlers/class-source.d.ts +5 -0
- package/dist/entry-tools/inspect-minecraft/handlers/class-source.js +60 -0
- package/dist/entry-tools/inspect-minecraft/handlers/file.d.ts +5 -0
- package/dist/entry-tools/inspect-minecraft/handlers/file.js +54 -0
- package/dist/entry-tools/inspect-minecraft/handlers/list-files.d.ts +5 -0
- package/dist/entry-tools/inspect-minecraft/handlers/list-files.js +100 -0
- package/dist/entry-tools/inspect-minecraft/handlers/search.d.ts +5 -0
- package/dist/entry-tools/inspect-minecraft/handlers/search.js +155 -0
- package/dist/entry-tools/inspect-minecraft/handlers/versions.d.ts +6 -0
- package/dist/entry-tools/inspect-minecraft/handlers/versions.js +49 -0
- package/dist/entry-tools/inspect-minecraft/internal.d.ts +1042 -0
- package/dist/entry-tools/inspect-minecraft/internal.js +448 -0
- package/dist/entry-tools/inspect-minecraft-service.d.ts +193 -308
- package/dist/entry-tools/inspect-minecraft-service.js +20 -1244
- package/dist/entry-tools/manage-cache-service.d.ts +16 -16
- package/dist/entry-tools/validate-project/cases/access-transformer.d.ts +6 -0
- package/dist/entry-tools/validate-project/cases/access-transformer.js +106 -0
- package/dist/entry-tools/validate-project/cases/access-widener.d.ts +6 -0
- package/dist/entry-tools/validate-project/cases/access-widener.js +86 -0
- package/dist/entry-tools/validate-project/cases/mixin.d.ts +6 -0
- package/dist/entry-tools/validate-project/cases/mixin.js +90 -0
- package/dist/entry-tools/validate-project/cases/project-summary.d.ts +102 -0
- package/dist/entry-tools/validate-project/cases/project-summary.js +415 -0
- package/dist/entry-tools/validate-project/internal.d.ts +142 -0
- package/dist/entry-tools/validate-project/internal.js +303 -0
- package/dist/entry-tools/validate-project-service.d.ts +67 -47
- package/dist/entry-tools/validate-project-service.js +13 -563
- package/dist/entry-tools/verify-mixin-target-service.d.ts +133 -0
- package/dist/entry-tools/verify-mixin-target-service.js +323 -0
- package/dist/error-mapping.d.ts +40 -0
- package/dist/error-mapping.js +139 -0
- package/dist/errors.d.ts +6 -0
- package/dist/errors.js +6 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +147 -1354
- package/dist/mapping/internal-types.d.ts +54 -0
- package/dist/mapping/internal-types.js +14 -0
- package/dist/mapping/loaders/mojang.d.ts +2 -0
- package/dist/mapping/loaders/mojang.js +64 -0
- package/dist/mapping/loaders/tiny-loom.d.ts +2 -0
- package/dist/mapping/loaders/tiny-loom.js +73 -0
- package/dist/mapping/loaders/tiny-maven.d.ts +2 -0
- package/dist/mapping/loaders/tiny-maven.js +104 -0
- package/dist/mapping/loaders/types.d.ts +14 -0
- package/dist/mapping/loaders/types.js +2 -0
- package/dist/mapping/lookup.d.ts +52 -0
- package/dist/mapping/lookup.js +496 -0
- package/dist/mapping/parsers/normalize.d.ts +10 -0
- package/dist/mapping/parsers/normalize.js +52 -0
- package/dist/mapping/parsers/proguard.d.ts +20 -0
- package/dist/mapping/parsers/proguard.js +138 -0
- package/dist/mapping/parsers/symbol-records.d.ts +27 -0
- package/dist/mapping/parsers/symbol-records.js +216 -0
- package/dist/mapping/parsers/tiny.d.ts +9 -0
- package/dist/mapping/parsers/tiny.js +96 -0
- package/dist/mapping/types.d.ts +147 -0
- package/dist/mapping/types.js +2 -0
- package/dist/mapping-pipeline-service.js +3 -2
- package/dist/mapping-service.d.ts +8 -145
- package/dist/mapping-service.js +30 -1207
- package/dist/mixin/access-validators.d.ts +9 -0
- package/dist/mixin/access-validators.js +257 -0
- package/dist/mixin/annotation-validators.d.ts +5 -0
- package/dist/mixin/annotation-validators.js +162 -0
- package/dist/mixin/helpers.d.ts +28 -0
- package/dist/mixin/helpers.js +315 -0
- package/dist/mixin/parsed-validator.d.ts +8 -0
- package/dist/mixin/parsed-validator.js +337 -0
- package/dist/mixin/types.d.ts +208 -0
- package/dist/mixin/types.js +28 -0
- package/dist/mixin-validator.d.ts +9 -201
- package/dist/mixin-validator.js +8 -1020
- package/dist/source/access-validate.d.ts +4 -0
- package/dist/source/access-validate.js +254 -0
- package/dist/source/artifact-resolver.d.ts +111 -0
- package/dist/source/artifact-resolver.js +1271 -0
- package/dist/source/cache-metrics.d.ts +26 -0
- package/dist/source/cache-metrics.js +172 -0
- package/dist/source/class-source/members-builder.d.ts +34 -0
- package/dist/source/class-source/members-builder.js +46 -0
- package/dist/source/class-source/snippet-builder.d.ts +19 -0
- package/dist/source/class-source/snippet-builder.js +46 -0
- package/dist/source/class-source-helpers.d.ts +34 -0
- package/dist/source/class-source-helpers.js +140 -0
- package/dist/source/class-source.d.ts +42 -0
- package/dist/source/class-source.js +883 -0
- package/dist/source/descriptor-utils.d.ts +6 -0
- package/dist/source/descriptor-utils.js +37 -0
- package/dist/source/file-access.d.ts +4 -0
- package/dist/source/file-access.js +102 -0
- package/dist/source/indexer.d.ts +82 -0
- package/dist/source/indexer.js +522 -0
- package/dist/source/lifecycle/diff-utils.d.ts +9 -0
- package/dist/source/lifecycle/diff-utils.js +107 -0
- package/dist/source/lifecycle/diff.d.ts +2 -0
- package/dist/source/lifecycle/diff.js +265 -0
- package/dist/source/lifecycle/mapping-helpers.d.ts +22 -0
- package/dist/source/lifecycle/mapping-helpers.js +327 -0
- package/dist/source/lifecycle/runtime-check.d.ts +2 -0
- package/dist/source/lifecycle/runtime-check.js +142 -0
- package/dist/source/lifecycle/trace.d.ts +2 -0
- package/dist/source/lifecycle/trace.js +231 -0
- package/dist/source/lifecycle.d.ts +4 -0
- package/dist/source/lifecycle.js +5 -0
- package/dist/source/search.d.ts +51 -0
- package/dist/source/search.js +676 -0
- package/dist/source/shared-utils.d.ts +6 -0
- package/dist/source/shared-utils.js +55 -0
- package/dist/source/state.d.ts +26 -0
- package/dist/source/state.js +24 -0
- package/dist/source/symbol-resolver.d.ts +3 -0
- package/dist/source/symbol-resolver.js +212 -0
- package/dist/source/validate-mixin/pipeline/mapping-health.d.ts +3 -0
- package/dist/source/validate-mixin/pipeline/mapping-health.js +41 -0
- package/dist/source/validate-mixin/pipeline/parse.d.ts +2 -0
- package/dist/source/validate-mixin/pipeline/parse.js +10 -0
- package/dist/source/validate-mixin/pipeline/resolve.d.ts +3 -0
- package/dist/source/validate-mixin/pipeline/resolve.js +78 -0
- package/dist/source/validate-mixin/pipeline/target-lookup.d.ts +6 -0
- package/dist/source/validate-mixin/pipeline/target-lookup.js +260 -0
- package/dist/source/validate-mixin/pipeline-context.d.ts +72 -0
- package/dist/source/validate-mixin/pipeline-context.js +93 -0
- package/dist/source/validate-mixin.d.ts +22 -0
- package/dist/source/validate-mixin.js +799 -0
- package/dist/source/workspace-target.d.ts +18 -0
- package/dist/source/workspace-target.js +305 -0
- package/dist/source-resolver.d.ts +1 -0
- package/dist/source-resolver.js +1 -1
- package/dist/source-service.d.ts +164 -170
- package/dist/source-service.js +70 -6116
- package/dist/stage-emitter.d.ts +13 -0
- package/dist/stage-emitter.js +30 -0
- package/dist/stdio-supervisor.d.ts +61 -0
- package/dist/stdio-supervisor.js +326 -9
- package/dist/tool-contract-manifest.d.ts +1 -1
- package/dist/tool-contract-manifest.js +23 -6
- package/dist/tool-guidance.d.ts +82 -0
- package/dist/tool-guidance.js +734 -0
- package/dist/tool-schema-registry.d.ts +16 -0
- package/dist/tool-schema-registry.js +37 -0
- package/dist/tool-schemas.d.ts +3518 -0
- package/dist/tool-schemas.js +813 -0
- package/dist/types.d.ts +36 -0
- package/dist/version-service.js +7 -6
- package/dist/workspace-context-cache.d.ts +32 -0
- package/dist/workspace-context-cache.js +66 -0
- package/dist/workspace-mapping-service.d.ts +16 -0
- package/dist/workspace-mapping-service.js +173 -1
- package/docs/README-ja.md +416 -0
- package/docs/examples.md +483 -0
- package/docs/tool-reference.md +462 -0
- package/package.json +17 -4
|
@@ -0,0 +1,1271 @@
|
|
|
1
|
+
import fastGlob from "fast-glob";
|
|
2
|
+
import { buildArtifactAlias } from "../config.js";
|
|
3
|
+
import { buildSuggestedCall } from "../build-suggested-call.js";
|
|
4
|
+
import { ERROR_CODES, createError, isAppError } from "../errors.js";
|
|
5
|
+
import { buildLoaderRuntimeSearchRoots, buildVersionSourceSearchRoots, normalizeOptionalProjectPath } from "../gradle-paths.js";
|
|
6
|
+
import { log } from "../logger.js";
|
|
7
|
+
import { applyMappingPipeline } from "../mapping-pipeline-service.js";
|
|
8
|
+
import { parseCoordinate } from "../maven-resolver.js";
|
|
9
|
+
import { resolveMojangTinyFile } from "../mojang-tiny-mapping-service.js";
|
|
10
|
+
import { artifactSignatureFromFile } from "../path-resolver.js";
|
|
11
|
+
import { detectFabricLikeInputNamespace, listJavaEntries } from "../source-jar-reader.js";
|
|
12
|
+
import { artifactIdForJar, resolveSourceTarget as resolveSourceTargetInternal } from "../source-resolver.js";
|
|
13
|
+
import { resolveTinyRemapperJar } from "../tiny-remapper-resolver.js";
|
|
14
|
+
import { isUnobfuscatedVersion } from "../version-service.js";
|
|
15
|
+
import { dedupeQualityFlags, normalizeMapping, normalizeOptionalString, normalizePathStyle } from "./shared-utils.js";
|
|
16
|
+
const VERSION_TOKEN_REGEX_CACHE = new Map();
|
|
17
|
+
const MAX_HELPER_REGEX_CACHE = 128;
|
|
18
|
+
function rememberCachedRegex(cache, key, regex) {
|
|
19
|
+
if (cache.size >= MAX_HELPER_REGEX_CACHE) {
|
|
20
|
+
const oldestKey = cache.keys().next().value;
|
|
21
|
+
if (oldestKey) {
|
|
22
|
+
cache.delete(oldestKey);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
cache.set(key, regex);
|
|
26
|
+
return regex;
|
|
27
|
+
}
|
|
28
|
+
function escapeRegexLiteral(value) {
|
|
29
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
30
|
+
}
|
|
31
|
+
function hasPartialNetMinecraftCoverage(qualityFlags) {
|
|
32
|
+
return qualityFlags.includes("partial-source-no-net-minecraft");
|
|
33
|
+
}
|
|
34
|
+
function looksLikeMinecraftSourceArtifact(path, hasMinecraftNamespace) {
|
|
35
|
+
if (hasMinecraftNamespace) {
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
const normalizedPath = normalizePathStyle(path).toLowerCase();
|
|
39
|
+
return (normalizedPath.includes("/minecraftmaven/") ||
|
|
40
|
+
normalizedPath.includes("/net/minecraft/") ||
|
|
41
|
+
/(?:^|\/)minecraft(?:-[a-z0-9._+]+)*-sources\.jar$/i.test(normalizedPath) ||
|
|
42
|
+
normalizedPath.includes("minecraft-merged") ||
|
|
43
|
+
normalizedPath.includes("minecraft-common") ||
|
|
44
|
+
normalizedPath.includes("minecraft-clientonly") ||
|
|
45
|
+
normalizedPath.includes("minecraft-client") ||
|
|
46
|
+
normalizedPath.includes("minecraft-server"));
|
|
47
|
+
}
|
|
48
|
+
export function hasExactVersionToken(path, version) {
|
|
49
|
+
const normalizedPath = normalizePathStyle(path).toLowerCase();
|
|
50
|
+
const normalizedVersion = version.trim().toLowerCase();
|
|
51
|
+
if (!normalizedVersion) {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
// Avoid prefix false-positives like "1.21.1" matching "1.21.10".
|
|
55
|
+
const cached = VERSION_TOKEN_REGEX_CACHE.get(normalizedVersion);
|
|
56
|
+
const pattern = cached
|
|
57
|
+
?? rememberCachedRegex(VERSION_TOKEN_REGEX_CACHE, normalizedVersion, new RegExp(`(^|[^0-9a-z])${escapeRegexLiteral(normalizedVersion)}([^0-9a-z]|$)`, "i"));
|
|
58
|
+
return pattern.test(normalizedPath);
|
|
59
|
+
}
|
|
60
|
+
function inferMergedRuntimeNamespaceHint(path) {
|
|
61
|
+
const normalizedPath = normalizePathStyle(path).toLowerCase();
|
|
62
|
+
if (normalizedPath.includes("merged-intermediary-v2") ||
|
|
63
|
+
normalizedPath.includes("merged-intermediary")) {
|
|
64
|
+
return "intermediary";
|
|
65
|
+
}
|
|
66
|
+
if (normalizedPath.includes("minecraft-merged-mojang") ||
|
|
67
|
+
normalizedPath.includes("merged-mojang")) {
|
|
68
|
+
return "mojang";
|
|
69
|
+
}
|
|
70
|
+
if (normalizedPath.includes("merged-named")) {
|
|
71
|
+
return "named";
|
|
72
|
+
}
|
|
73
|
+
return undefined;
|
|
74
|
+
}
|
|
75
|
+
function runtimeJarNamespaceHintScore(hint) {
|
|
76
|
+
if (hint === "intermediary" || hint === "mojang") {
|
|
77
|
+
return 8_000;
|
|
78
|
+
}
|
|
79
|
+
if (hint === "named") {
|
|
80
|
+
return 1_000;
|
|
81
|
+
}
|
|
82
|
+
return 0;
|
|
83
|
+
}
|
|
84
|
+
function normalizeAccessTransformerNamespace(namespace) {
|
|
85
|
+
const normalized = namespace?.trim().toLowerCase();
|
|
86
|
+
if (!normalized) {
|
|
87
|
+
return undefined;
|
|
88
|
+
}
|
|
89
|
+
if (normalized === "srg" || normalized === "mojang" || normalized === "obfuscated") {
|
|
90
|
+
return normalized;
|
|
91
|
+
}
|
|
92
|
+
return undefined;
|
|
93
|
+
}
|
|
94
|
+
async function pathExists(filePath) {
|
|
95
|
+
try {
|
|
96
|
+
const { access } = await import("node:fs/promises");
|
|
97
|
+
await access(filePath);
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
function buildResolveArtifactParams(target, extra = {}) {
|
|
105
|
+
return {
|
|
106
|
+
target: {
|
|
107
|
+
kind: target.kind,
|
|
108
|
+
value: target.value
|
|
109
|
+
},
|
|
110
|
+
...extra
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
function buildProvenance(input) {
|
|
114
|
+
const provenance = {
|
|
115
|
+
target: input.requestedTarget,
|
|
116
|
+
resolvedAt: input.resolved.resolvedAt,
|
|
117
|
+
resolvedFrom: {
|
|
118
|
+
origin: input.resolved.origin,
|
|
119
|
+
sourceJarPath: input.resolved.sourceJarPath,
|
|
120
|
+
binaryJarPath: input.resolved.binaryJarPath,
|
|
121
|
+
coordinate: input.resolved.coordinate,
|
|
122
|
+
version: input.resolved.version,
|
|
123
|
+
repoUrl: input.resolved.repoUrl
|
|
124
|
+
},
|
|
125
|
+
transformChain: [...input.transformChain]
|
|
126
|
+
};
|
|
127
|
+
if (!provenance.resolvedAt || !provenance.target.kind || !provenance.target.value) {
|
|
128
|
+
throw createError({
|
|
129
|
+
code: ERROR_CODES.PROVENANCE_INCOMPLETE,
|
|
130
|
+
message: "Artifact provenance is incomplete.",
|
|
131
|
+
details: {
|
|
132
|
+
artifactId: input.resolved.artifactId,
|
|
133
|
+
provenance
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
return provenance;
|
|
138
|
+
}
|
|
139
|
+
export async function discoverVersionSourceJar(_svc, input) {
|
|
140
|
+
const normalizedProjectPath = normalizeOptionalProjectPath(input.projectPath);
|
|
141
|
+
const searchRoots = buildVersionSourceSearchRoots(normalizedProjectPath);
|
|
142
|
+
const searchedPaths = [];
|
|
143
|
+
const candidates = [];
|
|
144
|
+
const seen = new Set();
|
|
145
|
+
for (const root of searchRoots) {
|
|
146
|
+
searchedPaths.push(root);
|
|
147
|
+
let discovered = [];
|
|
148
|
+
try {
|
|
149
|
+
discovered = await fastGlob.glob("**/*sources.jar", {
|
|
150
|
+
cwd: root,
|
|
151
|
+
absolute: true,
|
|
152
|
+
onlyFiles: true
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
catch {
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
for (const candidatePath of discovered) {
|
|
159
|
+
const normalizedPath = normalizePathStyle(candidatePath);
|
|
160
|
+
if (seen.has(normalizedPath)) {
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
seen.add(normalizedPath);
|
|
164
|
+
const lower = normalizedPath.toLowerCase();
|
|
165
|
+
if (!lower.includes(input.version.toLowerCase()) && !lower.includes("minecraft")) {
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
let javaEntries = [];
|
|
169
|
+
try {
|
|
170
|
+
javaEntries = await listJavaEntries(normalizedPath);
|
|
171
|
+
}
|
|
172
|
+
catch {
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
if (javaEntries.length === 0) {
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
const hasMinecraftNamespace = javaEntries.some((entry) => normalizePathStyle(entry).startsWith("net/minecraft/"));
|
|
179
|
+
const looksLikeMinecraftArtifact = looksLikeMinecraftSourceArtifact(normalizedPath, hasMinecraftNamespace);
|
|
180
|
+
const exactVersionMatch = hasExactVersionToken(normalizedPath, input.version);
|
|
181
|
+
const score = (looksLikeMinecraftArtifact ? 20_000 : 0) +
|
|
182
|
+
(hasMinecraftNamespace ? 10_000 : 0) +
|
|
183
|
+
(lower.includes("minecraft-merged") ? 2_000 : 0) +
|
|
184
|
+
(exactVersionMatch ? 1_000 : 0) +
|
|
185
|
+
Math.min(javaEntries.length, 500);
|
|
186
|
+
candidates.push({
|
|
187
|
+
jarPath: normalizedPath,
|
|
188
|
+
javaEntryCount: javaEntries.length,
|
|
189
|
+
hasMinecraftNamespace,
|
|
190
|
+
looksLikeMinecraftArtifact,
|
|
191
|
+
score
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
candidates.sort((left, right) => {
|
|
196
|
+
if (right.score !== left.score) {
|
|
197
|
+
return right.score - left.score;
|
|
198
|
+
}
|
|
199
|
+
return left.jarPath.localeCompare(right.jarPath);
|
|
200
|
+
});
|
|
201
|
+
const selected = candidates.find((candidate) => candidate.looksLikeMinecraftArtifact && candidate.hasMinecraftNamespace) ??
|
|
202
|
+
candidates.find((candidate) => candidate.looksLikeMinecraftArtifact);
|
|
203
|
+
const candidateArtifacts = candidates
|
|
204
|
+
.slice(0, 20)
|
|
205
|
+
.map((candidate) => `${candidate.jarPath}#java=${candidate.javaEntryCount}#net_minecraft=${candidate.hasMinecraftNamespace ? 1 : 0}`);
|
|
206
|
+
return {
|
|
207
|
+
searchedPaths,
|
|
208
|
+
candidateArtifacts,
|
|
209
|
+
selectedSourceJarPath: selected?.jarPath,
|
|
210
|
+
selectedHasMinecraftNamespace: selected?.hasMinecraftNamespace
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
export async function probeMinecraftArtifact(svc, input) {
|
|
214
|
+
let value = input.target.value.trim();
|
|
215
|
+
const warnings = [];
|
|
216
|
+
const requestedMapping = normalizeMapping(input.mapping);
|
|
217
|
+
if (input.preferProjectVersion && input.projectPath) {
|
|
218
|
+
const detected = await svc.workspaceMappingService.detectProjectMinecraftVersion(input.projectPath);
|
|
219
|
+
if (detected && detected !== value) {
|
|
220
|
+
warnings.push(`Overriding version "${value}" with project version "${detected}" from gradle.properties.`);
|
|
221
|
+
}
|
|
222
|
+
value = detected ?? value;
|
|
223
|
+
}
|
|
224
|
+
if (!value) {
|
|
225
|
+
throw createError({
|
|
226
|
+
code: ERROR_CODES.INVALID_INPUT,
|
|
227
|
+
message: "target.value must be non-empty.",
|
|
228
|
+
details: { target: input.target }
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
const versionJar = await svc.versionService.resolveVersionJar(value);
|
|
232
|
+
const resolvedVersion = versionJar.version;
|
|
233
|
+
const runtimeNamesUnobfuscated = isUnobfuscatedVersion(resolvedVersion);
|
|
234
|
+
warnings.push(`Resolved Minecraft ${versionJar.version} from ${versionJar.clientJarUrl}.`);
|
|
235
|
+
let effectiveMapping = requestedMapping;
|
|
236
|
+
if ((requestedMapping === "intermediary" || requestedMapping === "yarn") &&
|
|
237
|
+
runtimeNamesUnobfuscated) {
|
|
238
|
+
warnings.push(`Version ${resolvedVersion} is unobfuscated; ${requestedMapping} mappings are not applicable. Using the obfuscated namespace label for the deobfuscated runtime names.`);
|
|
239
|
+
effectiveMapping = "obfuscated";
|
|
240
|
+
}
|
|
241
|
+
if ((effectiveMapping === "intermediary" || effectiveMapping === "yarn") &&
|
|
242
|
+
!runtimeNamesUnobfuscated) {
|
|
243
|
+
throw createError({
|
|
244
|
+
code: ERROR_CODES.MAPPING_NOT_APPLIED,
|
|
245
|
+
message: `Lightweight artifact probe cannot verify ${effectiveMapping} mapping availability without running the full resolver.`,
|
|
246
|
+
details: {
|
|
247
|
+
requestedMapping: effectiveMapping,
|
|
248
|
+
version: resolvedVersion,
|
|
249
|
+
nextAction: "Use a direct validation task for mapping-sensitive checks, or use mapping=obfuscated for the project-summary artifact probe."
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
if (effectiveMapping === "mojang" && !runtimeNamesUnobfuscated) {
|
|
254
|
+
// Match validate-mixin's resolve stage: omitted scope defaults to vanilla,
|
|
255
|
+
// avoiding a workspace-wide source-jar scan that validate-mixin would skip.
|
|
256
|
+
const effectiveScope = input.scope ?? "vanilla";
|
|
257
|
+
if (effectiveScope === "vanilla") {
|
|
258
|
+
throw createError({
|
|
259
|
+
code: ERROR_CODES.MAPPING_NOT_APPLIED,
|
|
260
|
+
message: "Lightweight artifact probe cannot verify mojang mapping with scope=vanilla on obfuscated runtime versions.",
|
|
261
|
+
details: {
|
|
262
|
+
requestedMapping: effectiveMapping,
|
|
263
|
+
version: resolvedVersion,
|
|
264
|
+
nextAction: "Retry with scope=merged and projectPath so the probe can use a Loom source jar, or use mapping=obfuscated."
|
|
265
|
+
}
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
const versionSourceDiscovery = await svc.discoverVersionSourceJar({
|
|
269
|
+
version: resolvedVersion,
|
|
270
|
+
projectPath: input.projectPath
|
|
271
|
+
});
|
|
272
|
+
if (!versionSourceDiscovery.selectedSourceJarPath) {
|
|
273
|
+
throw createError({
|
|
274
|
+
code: ERROR_CODES.MAPPING_NOT_APPLIED,
|
|
275
|
+
message: "Lightweight artifact probe cannot verify mojang mapping without a source-backed Loom artifact.",
|
|
276
|
+
details: {
|
|
277
|
+
requestedMapping: effectiveMapping,
|
|
278
|
+
version: resolvedVersion,
|
|
279
|
+
searchedPaths: versionSourceDiscovery.searchedPaths,
|
|
280
|
+
candidateArtifacts: versionSourceDiscovery.candidateArtifacts,
|
|
281
|
+
nextAction: "Use mapping=obfuscated for project-summary, or run a direct validation task when full source resolution is required."
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
const selectedSourceJarPath = versionSourceDiscovery.selectedSourceJarPath;
|
|
286
|
+
const sourceSignature = artifactSignatureFromFile(selectedSourceJarPath).signature;
|
|
287
|
+
const artifactId = artifactIdForJar("jar", selectedSourceJarPath, sourceSignature);
|
|
288
|
+
warnings.push(`Resolved source-backed artifact from Loom cache candidate: ${selectedSourceJarPath}.`);
|
|
289
|
+
if (versionSourceDiscovery.selectedHasMinecraftNamespace === false) {
|
|
290
|
+
warnings.push(`Source coverage does not include net.minecraft for ${selectedSourceJarPath}; class lookups may fall back to the binary artifact.`);
|
|
291
|
+
}
|
|
292
|
+
if (!hasExactVersionToken(selectedSourceJarPath, value)) {
|
|
293
|
+
warnings.push(`Requested version "${value}" but resolved source jar does not contain exact version string: ${selectedSourceJarPath}`);
|
|
294
|
+
}
|
|
295
|
+
return {
|
|
296
|
+
artifactId,
|
|
297
|
+
mappingApplied: "mojang",
|
|
298
|
+
...(warnings.length > 0 ? { warnings } : {})
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
const binarySignature = artifactSignatureFromFile(versionJar.jarPath).signature;
|
|
302
|
+
const artifactId = artifactIdForJar("jar", versionJar.jarPath, `${binarySignature}:decompile`);
|
|
303
|
+
return {
|
|
304
|
+
artifactId,
|
|
305
|
+
mappingApplied: effectiveMapping,
|
|
306
|
+
...(warnings.length > 0 ? { warnings } : {})
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
export async function discoverAccessWidenerRuntimeCandidates(_svc, input) {
|
|
310
|
+
const normalizedProjectPath = normalizeOptionalProjectPath(input.projectPath);
|
|
311
|
+
const normalizedProjectPathLower = normalizedProjectPath
|
|
312
|
+
? normalizePathStyle(normalizedProjectPath).toLowerCase()
|
|
313
|
+
: undefined;
|
|
314
|
+
const searchRoots = buildVersionSourceSearchRoots(normalizedProjectPath);
|
|
315
|
+
const searchedPaths = [];
|
|
316
|
+
const candidates = [];
|
|
317
|
+
const seen = new Set();
|
|
318
|
+
for (const root of searchRoots) {
|
|
319
|
+
searchedPaths.push(root);
|
|
320
|
+
let discovered = [];
|
|
321
|
+
try {
|
|
322
|
+
discovered = await fastGlob.glob(["**/*minecraft*.jar", "**/*merged*.jar"], {
|
|
323
|
+
cwd: root,
|
|
324
|
+
absolute: true,
|
|
325
|
+
onlyFiles: true,
|
|
326
|
+
ignore: ["**/*sources.jar", "**/node_modules/**", "**/.git/**", "**/build/**", "**/out/**"]
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
catch {
|
|
330
|
+
continue;
|
|
331
|
+
}
|
|
332
|
+
for (const candidatePath of discovered) {
|
|
333
|
+
const normalizedPath = normalizePathStyle(candidatePath);
|
|
334
|
+
if (seen.has(normalizedPath)) {
|
|
335
|
+
continue;
|
|
336
|
+
}
|
|
337
|
+
seen.add(normalizedPath);
|
|
338
|
+
const lower = normalizedPath.toLowerCase();
|
|
339
|
+
if (!lower.includes("minecraft")) {
|
|
340
|
+
continue;
|
|
341
|
+
}
|
|
342
|
+
const exactVersionMatch = hasExactVersionToken(normalizedPath, input.version);
|
|
343
|
+
const looksMerged = lower.includes("minecraft-merged") || lower.includes("/merged/") || lower.includes("-merged");
|
|
344
|
+
const namespaceHint = inferMergedRuntimeNamespaceHint(normalizedPath);
|
|
345
|
+
const appliedScope = looksMerged
|
|
346
|
+
? "merged"
|
|
347
|
+
: input.requestedScope === "loader"
|
|
348
|
+
? "merged"
|
|
349
|
+
: input.requestedScope;
|
|
350
|
+
const score = (exactVersionMatch ? 5_000 : 0) +
|
|
351
|
+
(looksMerged ? 4_000 : 0) +
|
|
352
|
+
runtimeJarNamespaceHintScore(namespaceHint) +
|
|
353
|
+
(normalizedProjectPathLower && lower.startsWith(normalizedProjectPathLower) ? 2_000 : 0) +
|
|
354
|
+
(lower.includes("loom-cache") || lower.includes("/caches/fabric-loom/") ? 500 : 0) +
|
|
355
|
+
(lower.includes("minecraft-client") || lower.includes("client") ? 100 : 0);
|
|
356
|
+
candidates.push({
|
|
357
|
+
jarPath: normalizedPath,
|
|
358
|
+
score,
|
|
359
|
+
appliedScope,
|
|
360
|
+
origin: lower.includes("loom-cache") || lower.includes("/caches/fabric-loom/")
|
|
361
|
+
? "loom-cache"
|
|
362
|
+
: "local-jar",
|
|
363
|
+
namespaceHint
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
candidates.sort((left, right) => {
|
|
368
|
+
if (right.score !== left.score) {
|
|
369
|
+
return right.score - left.score;
|
|
370
|
+
}
|
|
371
|
+
return left.jarPath.localeCompare(right.jarPath);
|
|
372
|
+
});
|
|
373
|
+
return {
|
|
374
|
+
searchedPaths,
|
|
375
|
+
candidateArtifacts: candidates.slice(0, 20).map((candidate) => candidate.jarPath),
|
|
376
|
+
selected: candidates[0]
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
export async function discoverAccessTransformerRuntimeCandidates(_svc, input) {
|
|
380
|
+
const normalizedProjectPath = normalizeOptionalProjectPath(input.projectPath);
|
|
381
|
+
const normalizedProjectPathLower = normalizedProjectPath
|
|
382
|
+
? normalizePathStyle(normalizedProjectPath).toLowerCase()
|
|
383
|
+
: undefined;
|
|
384
|
+
const searchRoots = buildLoaderRuntimeSearchRoots(normalizedProjectPath);
|
|
385
|
+
const searchedPaths = [];
|
|
386
|
+
const candidates = [];
|
|
387
|
+
const seen = new Set();
|
|
388
|
+
const globs = [
|
|
389
|
+
"**/*minecraft*.jar",
|
|
390
|
+
"**/*patched*.jar",
|
|
391
|
+
"**/*srg*.jar",
|
|
392
|
+
"**/*joined*.jar",
|
|
393
|
+
"**/*client-extra*.jar",
|
|
394
|
+
"**/*forge*.jar",
|
|
395
|
+
"**/*neoforge*.jar",
|
|
396
|
+
"**/*moddev*.jar",
|
|
397
|
+
"**/*neoform*.jar"
|
|
398
|
+
];
|
|
399
|
+
for (const root of searchRoots) {
|
|
400
|
+
searchedPaths.push(root);
|
|
401
|
+
if (!(await pathExists(root))) {
|
|
402
|
+
continue;
|
|
403
|
+
}
|
|
404
|
+
let discovered = [];
|
|
405
|
+
try {
|
|
406
|
+
discovered = await fastGlob.glob(globs, {
|
|
407
|
+
cwd: root,
|
|
408
|
+
absolute: true,
|
|
409
|
+
onlyFiles: true,
|
|
410
|
+
ignore: ["**/*sources.jar", "**/node_modules/**", "**/.git/**", "**/out/**"]
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
catch {
|
|
414
|
+
continue;
|
|
415
|
+
}
|
|
416
|
+
for (const candidatePath of discovered) {
|
|
417
|
+
const normalizedPath = normalizePathStyle(candidatePath);
|
|
418
|
+
if (seen.has(normalizedPath)) {
|
|
419
|
+
continue;
|
|
420
|
+
}
|
|
421
|
+
seen.add(normalizedPath);
|
|
422
|
+
const lower = normalizedPath.toLowerCase();
|
|
423
|
+
if (!hasExactVersionToken(normalizedPath, input.version)) {
|
|
424
|
+
continue;
|
|
425
|
+
}
|
|
426
|
+
const looksMerged = lower.includes("merged");
|
|
427
|
+
const looksSrg = lower.includes("srg");
|
|
428
|
+
const looksForge = lower.includes("forge");
|
|
429
|
+
const looksNeoForge = lower.includes("neoforge") || lower.includes("moddev") || lower.includes("neoform");
|
|
430
|
+
const looksPatchedRuntime = lower.includes("patched") || lower.includes("client-extra") || lower.includes("joined");
|
|
431
|
+
const appliedScope = looksMerged
|
|
432
|
+
? "merged"
|
|
433
|
+
: "loader";
|
|
434
|
+
if (input.atNamespace === "srg" && !looksSrg) {
|
|
435
|
+
continue;
|
|
436
|
+
}
|
|
437
|
+
if (input.loader === "forge" && !looksForge && !looksSrg && !looksPatchedRuntime) {
|
|
438
|
+
continue;
|
|
439
|
+
}
|
|
440
|
+
if (input.loader === "neoforge" && !looksNeoForge && !looksPatchedRuntime && !lower.includes("minecraft")) {
|
|
441
|
+
continue;
|
|
442
|
+
}
|
|
443
|
+
const score = 10_000 +
|
|
444
|
+
(normalizedProjectPathLower && lower.startsWith(normalizedProjectPathLower) ? 4_000 : 0) +
|
|
445
|
+
(looksPatchedRuntime ? 3_000 : 0) +
|
|
446
|
+
(looksSrg ? 2_500 : 0) +
|
|
447
|
+
(input.loader === "forge" && looksForge ? 1_500 : 0) +
|
|
448
|
+
(input.loader === "neoforge" && looksNeoForge ? 1_500 : 0) +
|
|
449
|
+
(input.requestedScope === appliedScope ? 1_000 : 0) +
|
|
450
|
+
(looksMerged ? -500 : 0);
|
|
451
|
+
candidates.push({
|
|
452
|
+
jarPath: normalizedPath,
|
|
453
|
+
score,
|
|
454
|
+
appliedScope,
|
|
455
|
+
origin: "local-jar"
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
candidates.sort((left, right) => {
|
|
460
|
+
if (right.score !== left.score) {
|
|
461
|
+
return right.score - left.score;
|
|
462
|
+
}
|
|
463
|
+
return left.jarPath.localeCompare(right.jarPath);
|
|
464
|
+
});
|
|
465
|
+
return {
|
|
466
|
+
searchedPaths,
|
|
467
|
+
candidateArtifacts: candidates.slice(0, 20).map((candidate) => candidate.jarPath),
|
|
468
|
+
selected: candidates[0]
|
|
469
|
+
};
|
|
470
|
+
}
|
|
471
|
+
export async function resolveAccessWidenerRuntimeArtifact(svc, input) {
|
|
472
|
+
const normalizedProjectPath = normalizeOptionalProjectPath(input.projectPath);
|
|
473
|
+
let version = input.version;
|
|
474
|
+
if (input.preferProjectVersion && normalizedProjectPath) {
|
|
475
|
+
const detected = await svc.workspaceMappingService.detectProjectMinecraftVersion(normalizedProjectPath);
|
|
476
|
+
version = detected ?? version;
|
|
477
|
+
}
|
|
478
|
+
const requestedScope = input.scope ?? (normalizedProjectPath ? "loader" : "vanilla");
|
|
479
|
+
if (requestedScope === "vanilla") {
|
|
480
|
+
const versionJar = await svc.versionService.resolveVersionJar(version);
|
|
481
|
+
return {
|
|
482
|
+
version: versionJar.version,
|
|
483
|
+
jarPath: versionJar.jarPath,
|
|
484
|
+
requestedScope,
|
|
485
|
+
appliedScope: "vanilla",
|
|
486
|
+
requestedMapping: input.awNamespace,
|
|
487
|
+
mappingApplied: "obfuscated",
|
|
488
|
+
origin: "version-jar"
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
const discovery = await svc.discoverAccessWidenerRuntimeCandidates({
|
|
492
|
+
version,
|
|
493
|
+
projectPath: normalizedProjectPath,
|
|
494
|
+
requestedScope
|
|
495
|
+
});
|
|
496
|
+
if (!discovery.selected) {
|
|
497
|
+
throw createError({
|
|
498
|
+
code: ERROR_CODES.CONTEXT_UNRESOLVED,
|
|
499
|
+
message: "Could not resolve a runtime jar for Access Widener validation.",
|
|
500
|
+
details: {
|
|
501
|
+
version,
|
|
502
|
+
requestedScope,
|
|
503
|
+
projectPath: normalizedProjectPath,
|
|
504
|
+
searchedPaths: discovery.searchedPaths,
|
|
505
|
+
candidateArtifacts: discovery.candidateArtifacts,
|
|
506
|
+
nextAction: "Provide projectPath for a Loom workspace with generated runtime jars, or run Gradle tasks that populate the Loom cache before retrying.",
|
|
507
|
+
...buildSuggestedCall({
|
|
508
|
+
tool: "validate-access-widener",
|
|
509
|
+
params: {
|
|
510
|
+
version,
|
|
511
|
+
scope: requestedScope,
|
|
512
|
+
...(normalizedProjectPath ? { projectPath: normalizedProjectPath } : {})
|
|
513
|
+
}
|
|
514
|
+
})
|
|
515
|
+
}
|
|
516
|
+
});
|
|
517
|
+
}
|
|
518
|
+
const appliedScope = discovery.selected.appliedScope;
|
|
519
|
+
const scopeFallback = requestedScope !== appliedScope
|
|
520
|
+
? {
|
|
521
|
+
requested: requestedScope,
|
|
522
|
+
applied: appliedScope,
|
|
523
|
+
reason: requestedScope === "loader"
|
|
524
|
+
? "Fabric loader runtime validation currently reuses the merged runtime jar."
|
|
525
|
+
: "Selected runtime jar matched a nearby merged artifact."
|
|
526
|
+
}
|
|
527
|
+
: undefined;
|
|
528
|
+
let detectedMapping;
|
|
529
|
+
const notes = [];
|
|
530
|
+
if (scopeFallback) {
|
|
531
|
+
notes.push(scopeFallback.reason);
|
|
532
|
+
}
|
|
533
|
+
if (isUnobfuscatedVersion(version)) {
|
|
534
|
+
detectedMapping = "obfuscated";
|
|
535
|
+
}
|
|
536
|
+
else if (discovery.selected.namespaceHint === "intermediary" ||
|
|
537
|
+
discovery.selected.namespaceHint === "mojang") {
|
|
538
|
+
detectedMapping = discovery.selected.namespaceHint;
|
|
539
|
+
}
|
|
540
|
+
else {
|
|
541
|
+
const detection = await detectFabricLikeInputNamespace(discovery.selected.jarPath);
|
|
542
|
+
detectedMapping = detection.fromNamespace;
|
|
543
|
+
if (detection.warnings.length > 0) {
|
|
544
|
+
notes.push(...detection.warnings);
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
return {
|
|
548
|
+
version,
|
|
549
|
+
jarPath: discovery.selected.jarPath,
|
|
550
|
+
requestedScope,
|
|
551
|
+
appliedScope,
|
|
552
|
+
requestedMapping: input.awNamespace,
|
|
553
|
+
mappingApplied: detectedMapping,
|
|
554
|
+
origin: discovery.selected.origin,
|
|
555
|
+
resolutionNotes: notes.length > 0 ? notes : undefined,
|
|
556
|
+
scopeFallback
|
|
557
|
+
};
|
|
558
|
+
}
|
|
559
|
+
export async function resolveAccessTransformerNamespace(svc, input) {
|
|
560
|
+
const explicit = normalizeAccessTransformerNamespace(input.atNamespace);
|
|
561
|
+
if (explicit) {
|
|
562
|
+
return explicit;
|
|
563
|
+
}
|
|
564
|
+
const normalizedProjectPath = normalizeOptionalProjectPath(input.projectPath);
|
|
565
|
+
if (!normalizedProjectPath) {
|
|
566
|
+
throw createError({
|
|
567
|
+
code: ERROR_CODES.INVALID_INPUT,
|
|
568
|
+
message: "atNamespace is required when projectPath is not provided.",
|
|
569
|
+
details: {
|
|
570
|
+
nextAction: "Pass atNamespace explicitly, or provide projectPath for a Forge/NeoForge workspace so the namespace can be inferred."
|
|
571
|
+
}
|
|
572
|
+
});
|
|
573
|
+
}
|
|
574
|
+
const loaderDetection = await svc.workspaceMappingService.detectProjectLoader(normalizedProjectPath);
|
|
575
|
+
if (loaderDetection.resolved && loaderDetection.loader === "forge") {
|
|
576
|
+
return "srg";
|
|
577
|
+
}
|
|
578
|
+
if (loaderDetection.resolved && loaderDetection.loader === "neoforge") {
|
|
579
|
+
return "mojang";
|
|
580
|
+
}
|
|
581
|
+
throw createError({
|
|
582
|
+
code: ERROR_CODES.INVALID_INPUT,
|
|
583
|
+
message: "Could not infer atNamespace from the workspace.",
|
|
584
|
+
details: {
|
|
585
|
+
projectPath: normalizedProjectPath,
|
|
586
|
+
evidence: loaderDetection.evidence,
|
|
587
|
+
warnings: loaderDetection.warnings,
|
|
588
|
+
nextAction: "Pass atNamespace explicitly, or point projectPath at a Forge/NeoForge workspace."
|
|
589
|
+
}
|
|
590
|
+
});
|
|
591
|
+
}
|
|
592
|
+
export async function resolveAccessTransformerRuntimeArtifact(svc, input) {
|
|
593
|
+
const normalizedProjectPath = normalizeOptionalProjectPath(input.projectPath);
|
|
594
|
+
let version = input.version;
|
|
595
|
+
if (input.preferProjectVersion && normalizedProjectPath) {
|
|
596
|
+
const detected = await svc.workspaceMappingService.detectProjectMinecraftVersion(normalizedProjectPath);
|
|
597
|
+
version = detected ?? version;
|
|
598
|
+
}
|
|
599
|
+
const requestedScope = input.scope ?? (normalizedProjectPath ? "loader" : "vanilla");
|
|
600
|
+
if (requestedScope === "vanilla") {
|
|
601
|
+
if (input.atNamespace === "srg") {
|
|
602
|
+
throw createError({
|
|
603
|
+
code: ERROR_CODES.INVALID_INPUT,
|
|
604
|
+
message: "atNamespace=srg requires projectPath and scope=loader so a Forge runtime jar can be resolved."
|
|
605
|
+
});
|
|
606
|
+
}
|
|
607
|
+
const versionJar = await svc.versionService.resolveVersionJar(version);
|
|
608
|
+
return {
|
|
609
|
+
version: versionJar.version,
|
|
610
|
+
jarPath: versionJar.jarPath,
|
|
611
|
+
requestedScope,
|
|
612
|
+
appliedScope: "vanilla",
|
|
613
|
+
requestedMapping: input.atNamespace,
|
|
614
|
+
mappingApplied: "obfuscated",
|
|
615
|
+
origin: "version-jar"
|
|
616
|
+
};
|
|
617
|
+
}
|
|
618
|
+
const loaderDetection = normalizedProjectPath
|
|
619
|
+
? await svc.workspaceMappingService.detectProjectLoader(normalizedProjectPath)
|
|
620
|
+
: { resolved: false, loader: undefined, evidence: [], warnings: [] };
|
|
621
|
+
const loader = loaderDetection.resolved ? loaderDetection.loader ?? "unknown" : "unknown";
|
|
622
|
+
const discovery = await svc.discoverAccessTransformerRuntimeCandidates({
|
|
623
|
+
version,
|
|
624
|
+
projectPath: normalizedProjectPath,
|
|
625
|
+
requestedScope,
|
|
626
|
+
atNamespace: input.atNamespace,
|
|
627
|
+
loader
|
|
628
|
+
});
|
|
629
|
+
if (!discovery.selected) {
|
|
630
|
+
throw createError({
|
|
631
|
+
code: ERROR_CODES.CONTEXT_UNRESOLVED,
|
|
632
|
+
message: "Could not resolve a runtime jar for Access Transformer validation.",
|
|
633
|
+
details: {
|
|
634
|
+
version,
|
|
635
|
+
requestedScope,
|
|
636
|
+
atNamespace: input.atNamespace,
|
|
637
|
+
projectPath: normalizedProjectPath,
|
|
638
|
+
searchedPaths: discovery.searchedPaths,
|
|
639
|
+
candidateArtifacts: discovery.candidateArtifacts,
|
|
640
|
+
loaderEvidence: loaderDetection.evidence,
|
|
641
|
+
loaderWarnings: loaderDetection.warnings,
|
|
642
|
+
nextAction: "Provide projectPath for a Forge/NeoForge workspace with generated runtime jars, or run the Gradle tasks that populate transformed runtime artifacts before retrying."
|
|
643
|
+
}
|
|
644
|
+
});
|
|
645
|
+
}
|
|
646
|
+
const selected = discovery.selected;
|
|
647
|
+
const selectedLower = selected.jarPath.toLowerCase();
|
|
648
|
+
const mappingApplied = input.atNamespace === "srg" || selectedLower.includes("srg")
|
|
649
|
+
? "srg"
|
|
650
|
+
: loader === "neoforge" || selectedLower.includes("moddev") || selectedLower.includes("neoforge")
|
|
651
|
+
? "mojang"
|
|
652
|
+
: "obfuscated";
|
|
653
|
+
const scopeFallback = requestedScope !== selected.appliedScope
|
|
654
|
+
? {
|
|
655
|
+
requested: requestedScope,
|
|
656
|
+
applied: selected.appliedScope,
|
|
657
|
+
reason: selected.appliedScope === "merged"
|
|
658
|
+
? "Resolved a nearby merged runtime jar because no transformed loader artifact was available."
|
|
659
|
+
: "Resolved the closest transformed runtime artifact for validation."
|
|
660
|
+
}
|
|
661
|
+
: undefined;
|
|
662
|
+
return {
|
|
663
|
+
version,
|
|
664
|
+
jarPath: selected.jarPath,
|
|
665
|
+
requestedScope,
|
|
666
|
+
appliedScope: selected.appliedScope,
|
|
667
|
+
requestedMapping: input.atNamespace,
|
|
668
|
+
mappingApplied,
|
|
669
|
+
origin: selected.origin,
|
|
670
|
+
resolutionNotes: scopeFallback ? [scopeFallback.reason] : undefined,
|
|
671
|
+
scopeFallback
|
|
672
|
+
};
|
|
673
|
+
}
|
|
674
|
+
export function inferVersionFromContext(input) {
|
|
675
|
+
const direct = normalizeOptionalString(input.version);
|
|
676
|
+
if (direct) {
|
|
677
|
+
return direct;
|
|
678
|
+
}
|
|
679
|
+
const resolvedFromVersion = normalizeOptionalString(input.provenance?.resolvedFrom.version);
|
|
680
|
+
if (resolvedFromVersion) {
|
|
681
|
+
return resolvedFromVersion;
|
|
682
|
+
}
|
|
683
|
+
if (input.provenance?.target.kind === "version") {
|
|
684
|
+
const targetVersion = normalizeOptionalString(input.provenance.target.value);
|
|
685
|
+
if (targetVersion) {
|
|
686
|
+
return targetVersion;
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
const coordinate = normalizeOptionalString(input.coordinate);
|
|
690
|
+
if (coordinate) {
|
|
691
|
+
try {
|
|
692
|
+
return parseCoordinate(coordinate).version;
|
|
693
|
+
}
|
|
694
|
+
catch {
|
|
695
|
+
return undefined;
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
return undefined;
|
|
699
|
+
}
|
|
700
|
+
export async function resolveVersionContext(svc, input) {
|
|
701
|
+
const inferredVersion = inferVersionFromContext(input);
|
|
702
|
+
if (inferredVersion) {
|
|
703
|
+
return inferredVersion;
|
|
704
|
+
}
|
|
705
|
+
if (!input.preferProjectVersion || !input.projectPath) {
|
|
706
|
+
return undefined;
|
|
707
|
+
}
|
|
708
|
+
const detected = await svc.workspaceMappingService.detectProjectMinecraftVersion(input.projectPath);
|
|
709
|
+
if (detected) {
|
|
710
|
+
input.warnings.push(`Using project version "${detected}" from gradle.properties because the artifact metadata did not include a version.`);
|
|
711
|
+
}
|
|
712
|
+
return detected;
|
|
713
|
+
}
|
|
714
|
+
export async function resolveBinaryFallbackArtifact(svc, input) {
|
|
715
|
+
const binaryJarPath = normalizeOptionalString(input.binaryJarPath);
|
|
716
|
+
if (!binaryJarPath) {
|
|
717
|
+
return undefined;
|
|
718
|
+
}
|
|
719
|
+
try {
|
|
720
|
+
const fallbackResolved = await resolveSourceTargetInternal({ kind: "jar", value: binaryJarPath }, { allowDecompile: true, preferBinaryOnly: true }, svc.config);
|
|
721
|
+
fallbackResolved.version = fallbackResolved.version ?? input.version;
|
|
722
|
+
fallbackResolved.coordinate = fallbackResolved.coordinate ?? input.coordinate;
|
|
723
|
+
fallbackResolved.requestedMapping = input.requestedMapping;
|
|
724
|
+
fallbackResolved.mappingApplied = input.mappingApplied;
|
|
725
|
+
fallbackResolved.provenance = input.provenance;
|
|
726
|
+
fallbackResolved.qualityFlags = dedupeQualityFlags([
|
|
727
|
+
...(fallbackResolved.qualityFlags ?? []),
|
|
728
|
+
...input.qualityFlags,
|
|
729
|
+
"binary-fallback"
|
|
730
|
+
]);
|
|
731
|
+
await svc.ingestIfNeeded(fallbackResolved);
|
|
732
|
+
return fallbackResolved;
|
|
733
|
+
}
|
|
734
|
+
catch {
|
|
735
|
+
return undefined;
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
function buildVersionSourceRecoveryCommand(projectPath) {
|
|
739
|
+
const normalizedProjectPath = normalizeOptionalProjectPath(projectPath);
|
|
740
|
+
const prefix = normalizedProjectPath
|
|
741
|
+
? `cd ${JSON.stringify(normalizedProjectPath)} && `
|
|
742
|
+
: "";
|
|
743
|
+
return `${prefix}./gradlew genSources --no-daemon`;
|
|
744
|
+
}
|
|
745
|
+
async function computeBinaryRemapGate(svc, input) {
|
|
746
|
+
const baseline = {
|
|
747
|
+
allowBinaryRemap: false,
|
|
748
|
+
mappingVariant: "pass",
|
|
749
|
+
warnings: []
|
|
750
|
+
};
|
|
751
|
+
if (input.forceBinaryRemapDisabled === true) {
|
|
752
|
+
return baseline;
|
|
753
|
+
}
|
|
754
|
+
if (input.requestedMapping !== "mojang" ||
|
|
755
|
+
input.runtimeNamesUnobfuscated ||
|
|
756
|
+
!input.version) {
|
|
757
|
+
return baseline;
|
|
758
|
+
}
|
|
759
|
+
if (input.targetKind !== "version") {
|
|
760
|
+
return baseline;
|
|
761
|
+
}
|
|
762
|
+
let tinyRemapperJarPath;
|
|
763
|
+
try {
|
|
764
|
+
tinyRemapperJarPath = await resolveTinyRemapperJar(svc.config.cacheDir, svc.config.tinyRemapperJarPath);
|
|
765
|
+
}
|
|
766
|
+
catch (caughtError) {
|
|
767
|
+
log("warn", "binary-remap.gate.tiny-remapper-unavailable", {
|
|
768
|
+
version: input.version,
|
|
769
|
+
error: caughtError instanceof Error ? caughtError.message : String(caughtError)
|
|
770
|
+
});
|
|
771
|
+
return baseline;
|
|
772
|
+
}
|
|
773
|
+
let mojangTinyFilePath;
|
|
774
|
+
try {
|
|
775
|
+
const mojangTiny = await resolveMojangTinyFile(input.version, svc.config);
|
|
776
|
+
mojangTinyFilePath = mojangTiny.path;
|
|
777
|
+
if (mojangTiny.warnings.length > 0) {
|
|
778
|
+
baseline.warnings.push(...mojangTiny.warnings);
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
catch (caughtError) {
|
|
782
|
+
log("warn", "binary-remap.gate.mojang-tiny-unavailable", {
|
|
783
|
+
version: input.version,
|
|
784
|
+
error: caughtError instanceof Error ? caughtError.message : String(caughtError)
|
|
785
|
+
});
|
|
786
|
+
return baseline;
|
|
787
|
+
}
|
|
788
|
+
let mojangAvailable = false;
|
|
789
|
+
try {
|
|
790
|
+
const health = await svc.mappingService.checkMappingHealth({
|
|
791
|
+
version: input.version,
|
|
792
|
+
requestedMapping: "mojang",
|
|
793
|
+
sourcePriority: input.sourcePriority
|
|
794
|
+
});
|
|
795
|
+
mojangAvailable = health.mojangMappingsAvailable;
|
|
796
|
+
}
|
|
797
|
+
catch (caughtError) {
|
|
798
|
+
log("warn", "binary-remap.gate.health-check-failed", {
|
|
799
|
+
version: input.version,
|
|
800
|
+
error: caughtError instanceof Error ? caughtError.message : String(caughtError)
|
|
801
|
+
});
|
|
802
|
+
return baseline;
|
|
803
|
+
}
|
|
804
|
+
if (!mojangAvailable) {
|
|
805
|
+
return baseline;
|
|
806
|
+
}
|
|
807
|
+
return {
|
|
808
|
+
allowBinaryRemap: true,
|
|
809
|
+
mappingVariant: "mojang-remapped",
|
|
810
|
+
tinyRemapperJarPath,
|
|
811
|
+
mojangTinyFilePath,
|
|
812
|
+
warnings: baseline.warnings
|
|
813
|
+
};
|
|
814
|
+
}
|
|
815
|
+
export function buildArtifactContentsSummary(_svc, input) {
|
|
816
|
+
const sourceKind = input.isDecompiled || input.origin === "decompiled" || !normalizeOptionalString(input.sourceJarPath)
|
|
817
|
+
? "decompiled-binary"
|
|
818
|
+
: "source-jar";
|
|
819
|
+
const sourceCoverage = hasPartialNetMinecraftCoverage(input.qualityFlags) ? "partial" : "full";
|
|
820
|
+
return {
|
|
821
|
+
sourceKind,
|
|
822
|
+
indexedContentKinds: ["java-source"],
|
|
823
|
+
resourcesIncluded: false,
|
|
824
|
+
sourceCoverage
|
|
825
|
+
};
|
|
826
|
+
}
|
|
827
|
+
export async function buildMappingFallbackSuggestedCall(svc, args) {
|
|
828
|
+
const { input, kind, value, scope, effectiveMapping } = args;
|
|
829
|
+
const isVanillaMojang = scope === "vanilla" && effectiveMapping === "mojang";
|
|
830
|
+
if (process.env.WORKSPACE_FALLBACK_LEGACY === "1") {
|
|
831
|
+
return buildLegacyMappingFallback({ kind, value, scope, isVanillaMojang, projectPath: input.projectPath });
|
|
832
|
+
}
|
|
833
|
+
const projectPath = input.projectPath?.trim();
|
|
834
|
+
if (!projectPath) {
|
|
835
|
+
return buildLegacyMappingFallback({ kind, value, scope, isVanillaMojang, projectPath: undefined });
|
|
836
|
+
}
|
|
837
|
+
if (kind !== "version") {
|
|
838
|
+
return buildLegacyMappingFallback({ kind, value, scope, isVanillaMojang, projectPath });
|
|
839
|
+
}
|
|
840
|
+
const cached = svc.workspaceContextCache.read(projectPath);
|
|
841
|
+
if (cached &&
|
|
842
|
+
!cached.partial &&
|
|
843
|
+
cached.compileMapping &&
|
|
844
|
+
cached.compileMapping !== "obfuscated" &&
|
|
845
|
+
cached.minecraftVersion === value) {
|
|
846
|
+
return {
|
|
847
|
+
...buildSuggestedCall({
|
|
848
|
+
tool: "resolve-artifact",
|
|
849
|
+
params: {
|
|
850
|
+
target: { kind: "workspace" },
|
|
851
|
+
projectPath,
|
|
852
|
+
mapping: cached.compileMapping
|
|
853
|
+
}
|
|
854
|
+
}),
|
|
855
|
+
nextAction: `Workspace at ${projectPath} maps as ${cached.compileMapping}. Retry with target.kind="workspace" to use the project's compile mapping.`
|
|
856
|
+
};
|
|
857
|
+
}
|
|
858
|
+
if (!cached) {
|
|
859
|
+
try {
|
|
860
|
+
const detectedVersion = await svc.workspaceMappingService.detectProjectMinecraftVersion(projectPath);
|
|
861
|
+
if (!detectedVersion || detectedVersion !== value) {
|
|
862
|
+
return buildLegacyMappingFallback({ kind, value, scope, isVanillaMojang, projectPath });
|
|
863
|
+
}
|
|
864
|
+
const detection = await svc.workspaceMappingService.detectCompileMapping({ projectPath });
|
|
865
|
+
if (detection.resolved && detection.mappingApplied && detection.mappingApplied !== "obfuscated") {
|
|
866
|
+
const partial = {
|
|
867
|
+
projectPath,
|
|
868
|
+
minecraftVersion: detectedVersion,
|
|
869
|
+
compileMapping: detection.mappingApplied,
|
|
870
|
+
detectedAt: Date.now(),
|
|
871
|
+
evidence: detection.evidence.map((entry) => ({
|
|
872
|
+
source: entry.filePath,
|
|
873
|
+
field: "compileMapping",
|
|
874
|
+
value: entry.mapping
|
|
875
|
+
})),
|
|
876
|
+
dependencyVersions: new Map(),
|
|
877
|
+
partial: true
|
|
878
|
+
};
|
|
879
|
+
svc.workspaceContextCache.write(partial);
|
|
880
|
+
return {
|
|
881
|
+
...buildSuggestedCall({
|
|
882
|
+
tool: "resolve-artifact",
|
|
883
|
+
params: {
|
|
884
|
+
target: { kind: "workspace" },
|
|
885
|
+
projectPath,
|
|
886
|
+
mapping: detection.mappingApplied
|
|
887
|
+
}
|
|
888
|
+
}),
|
|
889
|
+
nextAction: `Workspace at ${projectPath} maps as ${detection.mappingApplied}. Retry with target.kind="workspace" to use the project's compile mapping.`
|
|
890
|
+
};
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
catch {
|
|
894
|
+
// bounded detection failed; fall back to legacy
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
return buildLegacyMappingFallback({ kind, value, scope, isVanillaMojang, projectPath });
|
|
898
|
+
}
|
|
899
|
+
function buildLegacyMappingFallback(args) {
|
|
900
|
+
const { kind, value, scope, isVanillaMojang, projectPath } = args;
|
|
901
|
+
if (isVanillaMojang && projectPath) {
|
|
902
|
+
return {
|
|
903
|
+
...buildSuggestedCall({
|
|
904
|
+
tool: "resolve-artifact",
|
|
905
|
+
params: buildResolveArtifactParams({ kind, value }, { mapping: "mojang", scope: "merged", projectPath })
|
|
906
|
+
}),
|
|
907
|
+
nextAction: "scope=vanilla blocks Loom cache discovery needed for mojang mapping. " +
|
|
908
|
+
"Retry with scope=merged to allow source-jar resolution from the project cache."
|
|
909
|
+
};
|
|
910
|
+
}
|
|
911
|
+
if (isVanillaMojang) {
|
|
912
|
+
return {
|
|
913
|
+
...buildSuggestedCall({
|
|
914
|
+
tool: "resolve-artifact",
|
|
915
|
+
params: buildResolveArtifactParams({ kind, value }, { mapping: "obfuscated", scope: "vanilla" })
|
|
916
|
+
}),
|
|
917
|
+
nextAction: "scope=vanilla blocks Loom cache discovery needed for mojang mapping. " +
|
|
918
|
+
"Without a projectPath, use mapping=obfuscated to read vanilla runtime names directly."
|
|
919
|
+
};
|
|
920
|
+
}
|
|
921
|
+
return {
|
|
922
|
+
...buildSuggestedCall({
|
|
923
|
+
tool: "resolve-artifact",
|
|
924
|
+
params: buildResolveArtifactParams({ kind, value }, { mapping: "obfuscated", ...(scope ? { scope } : {}) })
|
|
925
|
+
}),
|
|
926
|
+
nextAction: "Retry with mapping=obfuscated to use the runtime obfuscated namespace."
|
|
927
|
+
};
|
|
928
|
+
}
|
|
929
|
+
export async function resolveArtifact(svc, input) {
|
|
930
|
+
let workspaceProvenance;
|
|
931
|
+
let dependencyProvenance;
|
|
932
|
+
let dependencyOrigin = false;
|
|
933
|
+
let dependencyRequestedMapping;
|
|
934
|
+
const synthesisWarnings = [];
|
|
935
|
+
if (input.target.kind === "workspace") {
|
|
936
|
+
const synthesized = await svc.synthesizeWorkspaceTarget(input, input.target);
|
|
937
|
+
workspaceProvenance = synthesized.provenance;
|
|
938
|
+
synthesisWarnings.push(...synthesized.warnings);
|
|
939
|
+
input = {
|
|
940
|
+
...input,
|
|
941
|
+
target: synthesized.target,
|
|
942
|
+
scope: synthesized.scope ?? input.scope,
|
|
943
|
+
mapping: synthesized.mapping
|
|
944
|
+
};
|
|
945
|
+
}
|
|
946
|
+
else if (input.target.kind === "dependency") {
|
|
947
|
+
const synthesized = await svc.synthesizeDependencyTarget(input, input.target);
|
|
948
|
+
dependencyProvenance = synthesized.provenance;
|
|
949
|
+
dependencyOrigin = true;
|
|
950
|
+
dependencyRequestedMapping = synthesized.requestedMapping;
|
|
951
|
+
synthesisWarnings.push(...synthesized.warnings);
|
|
952
|
+
input = { ...input, target: synthesized.target };
|
|
953
|
+
}
|
|
954
|
+
const target = input.target;
|
|
955
|
+
const kind = target.kind;
|
|
956
|
+
let value = target.value?.trim();
|
|
957
|
+
const mapping = normalizeMapping(input.mapping);
|
|
958
|
+
const scope = input.scope;
|
|
959
|
+
const warnings = [...synthesisWarnings];
|
|
960
|
+
if (input.preferProjectVersion && input.projectPath && kind === "version") {
|
|
961
|
+
const detected = await svc.workspaceMappingService.detectProjectMinecraftVersion(input.projectPath);
|
|
962
|
+
if (detected && detected !== value) {
|
|
963
|
+
warnings.push(`Overriding version "${value}" with project version "${detected}" from gradle.properties.`);
|
|
964
|
+
}
|
|
965
|
+
value = detected ?? value;
|
|
966
|
+
}
|
|
967
|
+
if (!value) {
|
|
968
|
+
throw createError({
|
|
969
|
+
code: ERROR_CODES.INVALID_INPUT,
|
|
970
|
+
message: "target.value must be non-empty.",
|
|
971
|
+
details: { target: input.target }
|
|
972
|
+
});
|
|
973
|
+
}
|
|
974
|
+
if (kind !== "jar" && kind !== "coordinate" && kind !== "version") {
|
|
975
|
+
throw createError({
|
|
976
|
+
code: ERROR_CODES.INVALID_INPUT,
|
|
977
|
+
message: `Unsupported target kind "${kind}".`,
|
|
978
|
+
details: { target: input.target }
|
|
979
|
+
});
|
|
980
|
+
}
|
|
981
|
+
if (kind === "jar" && !value.toLowerCase().endsWith(".jar")) {
|
|
982
|
+
throw createError({
|
|
983
|
+
code: ERROR_CODES.INVALID_INPUT,
|
|
984
|
+
message: "target.kind=jar requires a .jar path.",
|
|
985
|
+
details: { target: input.target }
|
|
986
|
+
});
|
|
987
|
+
}
|
|
988
|
+
const startedAt = Date.now();
|
|
989
|
+
try {
|
|
990
|
+
let resolvedTarget = { kind, value };
|
|
991
|
+
let resolvedVersion;
|
|
992
|
+
let versionSourceDiscovery;
|
|
993
|
+
let runtimeNamesUnobfuscated = false;
|
|
994
|
+
if (kind === "version") {
|
|
995
|
+
const versionJar = await svc.versionService.resolveVersionJar(value);
|
|
996
|
+
resolvedVersion = versionJar.version;
|
|
997
|
+
runtimeNamesUnobfuscated = isUnobfuscatedVersion(resolvedVersion);
|
|
998
|
+
resolvedTarget = {
|
|
999
|
+
kind: "jar",
|
|
1000
|
+
value: versionJar.jarPath
|
|
1001
|
+
};
|
|
1002
|
+
warnings.push(`Resolved Minecraft ${versionJar.version} from ${versionJar.clientJarUrl}.`);
|
|
1003
|
+
}
|
|
1004
|
+
if (kind === "coordinate") {
|
|
1005
|
+
try {
|
|
1006
|
+
resolvedVersion = parseCoordinate(value).version;
|
|
1007
|
+
}
|
|
1008
|
+
catch {
|
|
1009
|
+
// coordinate validity is validated by resolver
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
if (!runtimeNamesUnobfuscated && resolvedVersion && isUnobfuscatedVersion(resolvedVersion)) {
|
|
1013
|
+
runtimeNamesUnobfuscated = true;
|
|
1014
|
+
}
|
|
1015
|
+
let effectiveMapping = mapping;
|
|
1016
|
+
if ((mapping === "intermediary" || mapping === "yarn") &&
|
|
1017
|
+
resolvedVersion &&
|
|
1018
|
+
isUnobfuscatedVersion(resolvedVersion)) {
|
|
1019
|
+
warnings.push(`Version ${resolvedVersion} is unobfuscated; ${mapping} mappings are not applicable. Using the obfuscated namespace label for the deobfuscated runtime names.`);
|
|
1020
|
+
effectiveMapping = "obfuscated";
|
|
1021
|
+
}
|
|
1022
|
+
if (kind === "version" &&
|
|
1023
|
+
resolvedVersion &&
|
|
1024
|
+
effectiveMapping === "mojang" &&
|
|
1025
|
+
!runtimeNamesUnobfuscated &&
|
|
1026
|
+
scope !== "vanilla") {
|
|
1027
|
+
versionSourceDiscovery = await svc.discoverVersionSourceJar({
|
|
1028
|
+
version: resolvedVersion,
|
|
1029
|
+
projectPath: input.projectPath
|
|
1030
|
+
});
|
|
1031
|
+
if (versionSourceDiscovery.selectedSourceJarPath) {
|
|
1032
|
+
resolvedTarget = {
|
|
1033
|
+
kind: "jar",
|
|
1034
|
+
value: versionSourceDiscovery.selectedSourceJarPath
|
|
1035
|
+
};
|
|
1036
|
+
warnings.push(`Resolved source-backed artifact from Loom cache candidate: ${versionSourceDiscovery.selectedSourceJarPath}.`);
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
const sourceJarPreSelected = Boolean(versionSourceDiscovery?.selectedSourceJarPath);
|
|
1040
|
+
const binaryRemapGate = sourceJarPreSelected
|
|
1041
|
+
? { allowBinaryRemap: false, mappingVariant: "pass", warnings: [] }
|
|
1042
|
+
: await computeBinaryRemapGate(svc, {
|
|
1043
|
+
requestedMapping: effectiveMapping,
|
|
1044
|
+
runtimeNamesUnobfuscated,
|
|
1045
|
+
version: resolvedVersion,
|
|
1046
|
+
targetKind: kind,
|
|
1047
|
+
sourcePriority: input.sourcePriority,
|
|
1048
|
+
forceBinaryRemapDisabled: dependencyOrigin
|
|
1049
|
+
});
|
|
1050
|
+
if (binaryRemapGate.warnings.length > 0) {
|
|
1051
|
+
warnings.push(...binaryRemapGate.warnings);
|
|
1052
|
+
}
|
|
1053
|
+
const resolved = await resolveSourceTargetInternal(resolvedTarget, {
|
|
1054
|
+
allowDecompile: effectiveMapping === "mojang" ? true : input.allowDecompile ?? true,
|
|
1055
|
+
mappingVariant: binaryRemapGate.mappingVariant,
|
|
1056
|
+
onRepoFailover: (event) => {
|
|
1057
|
+
svc.metrics.recordRepoFailover();
|
|
1058
|
+
log("warn", "repo.failover", {
|
|
1059
|
+
stage: event.stage,
|
|
1060
|
+
repoUrl: event.repoUrl,
|
|
1061
|
+
statusCode: event.statusCode,
|
|
1062
|
+
reason: event.reason,
|
|
1063
|
+
attempt: event.attempt,
|
|
1064
|
+
totalAttempts: event.totalAttempts
|
|
1065
|
+
});
|
|
1066
|
+
}
|
|
1067
|
+
}, svc.config);
|
|
1068
|
+
resolved.version = resolvedVersion;
|
|
1069
|
+
let mappingDecision;
|
|
1070
|
+
try {
|
|
1071
|
+
mappingDecision = applyMappingPipeline({
|
|
1072
|
+
requestedMapping: effectiveMapping,
|
|
1073
|
+
target: { kind, value },
|
|
1074
|
+
resolved,
|
|
1075
|
+
runtimeNamesUnobfuscated,
|
|
1076
|
+
allowBinaryRemap: binaryRemapGate.allowBinaryRemap
|
|
1077
|
+
});
|
|
1078
|
+
}
|
|
1079
|
+
catch (caughtError) {
|
|
1080
|
+
if (dependencyOrigin &&
|
|
1081
|
+
isAppError(caughtError) &&
|
|
1082
|
+
caughtError.code === ERROR_CODES.MAPPING_NOT_APPLIED) {
|
|
1083
|
+
mappingDecision = {
|
|
1084
|
+
mappingApplied: "obfuscated",
|
|
1085
|
+
transformChain: [],
|
|
1086
|
+
qualityFlags: [
|
|
1087
|
+
...(resolved.qualityFlags ?? []),
|
|
1088
|
+
"dependency-mapping-unverified"
|
|
1089
|
+
]
|
|
1090
|
+
};
|
|
1091
|
+
}
|
|
1092
|
+
else if (isAppError(caughtError) && caughtError.code === ERROR_CODES.MAPPING_NOT_APPLIED) {
|
|
1093
|
+
const fallback = await svc.buildMappingFallbackSuggestedCall({
|
|
1094
|
+
input,
|
|
1095
|
+
kind,
|
|
1096
|
+
value,
|
|
1097
|
+
scope,
|
|
1098
|
+
effectiveMapping
|
|
1099
|
+
});
|
|
1100
|
+
const { nextAction, ...fallbackGated } = fallback;
|
|
1101
|
+
throw createError({
|
|
1102
|
+
code: ERROR_CODES.MAPPING_NOT_APPLIED,
|
|
1103
|
+
message: caughtError.message,
|
|
1104
|
+
details: {
|
|
1105
|
+
...(caughtError.details ?? {}),
|
|
1106
|
+
artifactOrigin: resolved.origin,
|
|
1107
|
+
searchedPaths: versionSourceDiscovery?.searchedPaths ?? [],
|
|
1108
|
+
candidateArtifacts: versionSourceDiscovery?.candidateArtifacts ?? resolved.adjacentSourceCandidates ?? [],
|
|
1109
|
+
recommendedCommand: buildVersionSourceRecoveryCommand(input.projectPath),
|
|
1110
|
+
nextAction,
|
|
1111
|
+
...fallbackGated
|
|
1112
|
+
}
|
|
1113
|
+
});
|
|
1114
|
+
}
|
|
1115
|
+
else {
|
|
1116
|
+
throw caughtError;
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
const additionalTransformChain = [];
|
|
1120
|
+
if (!dependencyOrigin && (effectiveMapping === "intermediary" || effectiveMapping === "yarn")) {
|
|
1121
|
+
if (!resolved.version) {
|
|
1122
|
+
throw createError({
|
|
1123
|
+
code: ERROR_CODES.MAPPING_NOT_APPLIED,
|
|
1124
|
+
message: `Requested ${effectiveMapping} mapping cannot be guaranteed because artifact version is unknown.`,
|
|
1125
|
+
details: {
|
|
1126
|
+
mapping: effectiveMapping,
|
|
1127
|
+
target: { kind, value },
|
|
1128
|
+
nextAction: "Use target: { kind: \"version\", value } or a versioned Maven coordinate so mapping artifacts can be resolved.",
|
|
1129
|
+
...buildSuggestedCall({
|
|
1130
|
+
tool: "resolve-artifact",
|
|
1131
|
+
params: buildResolveArtifactParams({ kind: "version", value }, { ...(scope ? { scope } : {}) })
|
|
1132
|
+
})
|
|
1133
|
+
}
|
|
1134
|
+
});
|
|
1135
|
+
}
|
|
1136
|
+
const mappingAvailability = await svc.mappingService.ensureMappingAvailable({
|
|
1137
|
+
version: resolved.version,
|
|
1138
|
+
sourceMapping: "obfuscated",
|
|
1139
|
+
targetMapping: effectiveMapping,
|
|
1140
|
+
sourcePriority: input.sourcePriority
|
|
1141
|
+
});
|
|
1142
|
+
additionalTransformChain.push(...mappingAvailability.transformChain);
|
|
1143
|
+
if (mappingAvailability.warnings.length > 0) {
|
|
1144
|
+
warnings.push(...mappingAvailability.warnings);
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
const provenance = buildProvenance({
|
|
1148
|
+
requestedTarget: { kind, value },
|
|
1149
|
+
resolved,
|
|
1150
|
+
transformChain: [...mappingDecision.transformChain, ...additionalTransformChain]
|
|
1151
|
+
});
|
|
1152
|
+
if (workspaceProvenance) {
|
|
1153
|
+
provenance.workspaceResolution = workspaceProvenance;
|
|
1154
|
+
}
|
|
1155
|
+
if (dependencyProvenance) {
|
|
1156
|
+
provenance.dependencyResolution = dependencyProvenance;
|
|
1157
|
+
}
|
|
1158
|
+
if (dependencyOrigin && dependencyRequestedMapping && dependencyRequestedMapping !== "obfuscated") {
|
|
1159
|
+
const coord = resolved.coordinate ?? value;
|
|
1160
|
+
warnings.push(`Dependency artifact ${coord} mapping "${dependencyRequestedMapping}" is not enforced (binary remap is disabled for non-vanilla artifacts); the JAR is returned in its native namespace and mappingApplied is reported as "obfuscated" with qualityFlag "dependency-mapping-unverified". Caller must validate symbol availability.`);
|
|
1161
|
+
}
|
|
1162
|
+
const provenanceWarnings = [...synthesisWarnings];
|
|
1163
|
+
if (provenanceWarnings.length > 0) {
|
|
1164
|
+
provenance.warnings = [...(provenance.warnings ?? []), ...provenanceWarnings];
|
|
1165
|
+
}
|
|
1166
|
+
resolved.requestedMapping = effectiveMapping;
|
|
1167
|
+
resolved.mappingApplied = mappingDecision.mappingApplied;
|
|
1168
|
+
resolved.provenance = provenance;
|
|
1169
|
+
resolved.qualityFlags = [...mappingDecision.qualityFlags];
|
|
1170
|
+
if (versionSourceDiscovery?.candidateArtifacts.length) {
|
|
1171
|
+
resolved.qualityFlags.push("source-jar-found");
|
|
1172
|
+
}
|
|
1173
|
+
if (versionSourceDiscovery?.selectedSourceJarPath) {
|
|
1174
|
+
resolved.qualityFlags.push("source-jar-validated");
|
|
1175
|
+
if (versionSourceDiscovery.selectedHasMinecraftNamespace === false) {
|
|
1176
|
+
resolved.qualityFlags.push("partial-source-no-net-minecraft");
|
|
1177
|
+
warnings.push(`Source coverage does not include net.minecraft for ${versionSourceDiscovery.selectedSourceJarPath}; class lookups may fall back to the binary artifact.`);
|
|
1178
|
+
}
|
|
1179
|
+
if (kind === "version" && !hasExactVersionToken(versionSourceDiscovery.selectedSourceJarPath, value)) {
|
|
1180
|
+
if (input.strictVersion) {
|
|
1181
|
+
throw createError({
|
|
1182
|
+
code: ERROR_CODES.VERSION_NOT_FOUND,
|
|
1183
|
+
message: `Strict version match failed: requested "${value}" but nearest source jar is for a different version.`,
|
|
1184
|
+
details: {
|
|
1185
|
+
requestedVersion: value,
|
|
1186
|
+
selectedSourceJar: versionSourceDiscovery.selectedSourceJarPath,
|
|
1187
|
+
candidateArtifacts: versionSourceDiscovery.candidateArtifacts,
|
|
1188
|
+
nextAction: "Use strictVersion=false (default) to allow approximation, or ensure the exact version source jar is in the Loom cache.",
|
|
1189
|
+
...buildSuggestedCall({
|
|
1190
|
+
tool: "resolve-artifact",
|
|
1191
|
+
params: buildResolveArtifactParams({ kind: "version", value }, { strictVersion: false })
|
|
1192
|
+
})
|
|
1193
|
+
}
|
|
1194
|
+
});
|
|
1195
|
+
}
|
|
1196
|
+
resolved.qualityFlags.push("version-approximated");
|
|
1197
|
+
warnings.push(`Requested version "${value}" but resolved source jar does not contain exact version string: ${versionSourceDiscovery.selectedSourceJarPath}`);
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
resolved.qualityFlags = dedupeQualityFlags(resolved.qualityFlags);
|
|
1201
|
+
const aliasValue = kind === "jar"
|
|
1202
|
+
? (resolved.sourceJarPath ?? resolved.binaryJarPath ?? value)
|
|
1203
|
+
: value;
|
|
1204
|
+
const artifactAlias = buildArtifactAlias({
|
|
1205
|
+
artifactId: resolved.artifactId,
|
|
1206
|
+
kind,
|
|
1207
|
+
value: aliasValue,
|
|
1208
|
+
mappingVariant: binaryRemapGate.mappingVariant,
|
|
1209
|
+
resolvedVersion: resolvedVersion ?? resolved.version,
|
|
1210
|
+
coordinate: resolved.coordinate
|
|
1211
|
+
});
|
|
1212
|
+
resolved.artifactAlias = artifactAlias;
|
|
1213
|
+
await svc.ingestIfNeeded(resolved);
|
|
1214
|
+
let sampleEntries;
|
|
1215
|
+
if (input.compact === false && resolved.sourceJarPath) {
|
|
1216
|
+
try {
|
|
1217
|
+
const javaEntries = await listJavaEntries(resolved.sourceJarPath);
|
|
1218
|
+
const MAX_SAMPLE = 10;
|
|
1219
|
+
sampleEntries = javaEntries.slice(0, MAX_SAMPLE);
|
|
1220
|
+
if (javaEntries.length > MAX_SAMPLE) {
|
|
1221
|
+
sampleEntries.push(`... and ${javaEntries.length - MAX_SAMPLE} more .java entries`);
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
catch {
|
|
1225
|
+
// non-fatal
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
return {
|
|
1229
|
+
artifactId: resolved.artifactId,
|
|
1230
|
+
artifactAlias,
|
|
1231
|
+
origin: resolved.origin,
|
|
1232
|
+
isDecompiled: resolved.isDecompiled,
|
|
1233
|
+
resolvedSourceJarPath: resolved.sourceJarPath,
|
|
1234
|
+
adjacentSourceCandidates: resolved.adjacentSourceCandidates,
|
|
1235
|
+
binaryJarPath: resolved.binaryJarPath,
|
|
1236
|
+
coordinate: resolved.coordinate,
|
|
1237
|
+
version: resolved.version,
|
|
1238
|
+
requestedMapping: effectiveMapping,
|
|
1239
|
+
mappingApplied: mappingDecision.mappingApplied,
|
|
1240
|
+
provenance,
|
|
1241
|
+
qualityFlags: resolved.qualityFlags,
|
|
1242
|
+
repoUrl: resolved.repoUrl,
|
|
1243
|
+
artifactContents: svc.buildArtifactContentsSummary({
|
|
1244
|
+
origin: resolved.origin,
|
|
1245
|
+
sourceJarPath: resolved.sourceJarPath,
|
|
1246
|
+
isDecompiled: resolved.isDecompiled,
|
|
1247
|
+
qualityFlags: resolved.qualityFlags
|
|
1248
|
+
}),
|
|
1249
|
+
warnings,
|
|
1250
|
+
sampleEntries
|
|
1251
|
+
};
|
|
1252
|
+
}
|
|
1253
|
+
catch (caughtError) {
|
|
1254
|
+
if (isAppError(caughtError)) {
|
|
1255
|
+
throw caughtError;
|
|
1256
|
+
}
|
|
1257
|
+
throw createError({
|
|
1258
|
+
code: ERROR_CODES.ARTIFACT_RESOLUTION_FAILED,
|
|
1259
|
+
message: "Failed to resolve artifact.",
|
|
1260
|
+
details: {
|
|
1261
|
+
target: input.target,
|
|
1262
|
+
mapping,
|
|
1263
|
+
reason: caughtError instanceof Error ? caughtError.message : String(caughtError)
|
|
1264
|
+
}
|
|
1265
|
+
});
|
|
1266
|
+
}
|
|
1267
|
+
finally {
|
|
1268
|
+
svc.metrics.recordDuration("resolve_duration_ms", Date.now() - startedAt);
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
//# sourceMappingURL=artifact-resolver.js.map
|