@modwrench/workbench 0.0.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/data/conflicts/README.md +39 -0
- package/data/conflicts/fallout4.json +1 -0
- package/data/conflicts/lethalcompany.json +1 -0
- package/data/conflicts/skyrimspecialedition.json +1 -0
- package/dist/conflicts/community.d.ts +3 -0
- package/dist/conflicts/community.d.ts.map +1 -0
- package/dist/conflicts/community.js +96 -0
- package/dist/conflicts/community.js.map +1 -0
- package/dist/conflicts/index.d.ts +6 -0
- package/dist/conflicts/index.d.ts.map +1 -0
- package/dist/conflicts/index.js +114 -0
- package/dist/conflicts/index.js.map +1 -0
- package/dist/conflicts/loot.d.ts +19 -0
- package/dist/conflicts/loot.d.ts.map +1 -0
- package/dist/conflicts/loot.js +148 -0
- package/dist/conflicts/loot.js.map +1 -0
- package/dist/conflicts/types.d.ts +31 -0
- package/dist/conflicts/types.d.ts.map +1 -0
- package/dist/conflicts/types.js +6 -0
- package/dist/conflicts/types.js.map +1 -0
- package/dist/crashlog/bepinex.d.ts +3 -0
- package/dist/crashlog/bepinex.d.ts.map +1 -0
- package/dist/crashlog/bepinex.js +105 -0
- package/dist/crashlog/bepinex.js.map +1 -0
- package/dist/crashlog/crashlogger-sse.d.ts +3 -0
- package/dist/crashlog/crashlogger-sse.d.ts.map +1 -0
- package/dist/crashlog/crashlogger-sse.js +226 -0
- package/dist/crashlog/crashlogger-sse.js.map +1 -0
- package/dist/crashlog/detect.d.ts +3 -0
- package/dist/crashlog/detect.d.ts.map +1 -0
- package/dist/crashlog/detect.js +44 -0
- package/dist/crashlog/detect.js.map +1 -0
- package/dist/crashlog/index.d.ts +15 -0
- package/dist/crashlog/index.d.ts.map +1 -0
- package/dist/crashlog/index.js +65 -0
- package/dist/crashlog/index.js.map +1 -0
- package/dist/crashlog/minecraft.d.ts +3 -0
- package/dist/crashlog/minecraft.d.ts.map +1 -0
- package/dist/crashlog/minecraft.js +145 -0
- package/dist/crashlog/minecraft.js.map +1 -0
- package/dist/crashlog/netscriptframework.d.ts +3 -0
- package/dist/crashlog/netscriptframework.d.ts.map +1 -0
- package/dist/crashlog/netscriptframework.js +109 -0
- package/dist/crashlog/netscriptframework.js.map +1 -0
- package/dist/crashlog/types.d.ts +35 -0
- package/dist/crashlog/types.d.ts.map +1 -0
- package/dist/crashlog/types.js +9 -0
- package/dist/crashlog/types.js.map +1 -0
- package/dist/detect/environment.d.ts +35 -0
- package/dist/detect/environment.d.ts.map +1 -0
- package/dist/detect/environment.js +65 -0
- package/dist/detect/environment.js.map +1 -0
- package/dist/detect/games.d.ts +21 -0
- package/dist/detect/games.d.ts.map +1 -0
- package/dist/detect/games.js +159 -0
- package/dist/detect/games.js.map +1 -0
- package/dist/detect/loader.d.ts +12 -0
- package/dist/detect/loader.d.ts.map +1 -0
- package/dist/detect/loader.js +20 -0
- package/dist/detect/loader.js.map +1 -0
- package/dist/detect/manager.d.ts +26 -0
- package/dist/detect/manager.d.ts.map +1 -0
- package/dist/detect/manager.js +157 -0
- package/dist/detect/manager.js.map +1 -0
- package/dist/detect/os.d.ts +21 -0
- package/dist/detect/os.d.ts.map +1 -0
- package/dist/detect/os.js +58 -0
- package/dist/detect/os.js.map +1 -0
- package/dist/detect/steam.d.ts +32 -0
- package/dist/detect/steam.d.ts.map +1 -0
- package/dist/detect/steam.js +155 -0
- package/dist/detect/steam.js.map +1 -0
- package/dist/detect/vdf.d.ts +12 -0
- package/dist/detect/vdf.d.ts.map +1 -0
- package/dist/detect/vdf.js +112 -0
- package/dist/detect/vdf.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +27 -0
- package/dist/index.js.map +1 -0
- package/dist/loadorder/index.d.ts +20 -0
- package/dist/loadorder/index.d.ts.map +1 -0
- package/dist/loadorder/index.js +76 -0
- package/dist/loadorder/index.js.map +1 -0
- package/dist/loadorder/mo2.d.ts +21 -0
- package/dist/loadorder/mo2.d.ts.map +1 -0
- package/dist/loadorder/mo2.js +201 -0
- package/dist/loadorder/mo2.js.map +1 -0
- package/dist/loadorder/r2modman.d.ts +12 -0
- package/dist/loadorder/r2modman.d.ts.map +1 -0
- package/dist/loadorder/r2modman.js +115 -0
- package/dist/loadorder/r2modman.js.map +1 -0
- package/dist/loadorder/types.d.ts +30 -0
- package/dist/loadorder/types.d.ts.map +1 -0
- package/dist/loadorder/types.js +5 -0
- package/dist/loadorder/types.js.map +1 -0
- package/dist/loadorder/vortex.d.ts +8 -0
- package/dist/loadorder/vortex.d.ts.map +1 -0
- package/dist/loadorder/vortex.js +77 -0
- package/dist/loadorder/vortex.js.map +1 -0
- package/dist/metadata/clients.d.ts +11 -0
- package/dist/metadata/clients.d.ts.map +1 -0
- package/dist/metadata/clients.js +90 -0
- package/dist/metadata/clients.js.map +1 -0
- package/dist/metadata/index.d.ts +3 -0
- package/dist/metadata/index.d.ts.map +1 -0
- package/dist/metadata/index.js +149 -0
- package/dist/metadata/index.js.map +1 -0
- package/dist/metadata/normalize.d.ts +43 -0
- package/dist/metadata/normalize.d.ts.map +1 -0
- package/dist/metadata/normalize.js +88 -0
- package/dist/metadata/normalize.js.map +1 -0
- package/dist/metadata/types.d.ts +57 -0
- package/dist/metadata/types.d.ts.map +1 -0
- package/dist/metadata/types.js +9 -0
- package/dist/metadata/types.js.map +1 -0
- package/dist/register.d.ts +13 -0
- package/dist/register.d.ts.map +1 -0
- package/dist/register.js +209 -0
- package/dist/register.js.map +1 -0
- package/package.json +58 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { createHttpClient, getEnv, log, loadCredential, } from "@modwrench/core";
|
|
2
|
+
// Minimal HTTP clients for the platforms we already cover via dedicated MCP
|
|
3
|
+
// packages. We re-implement at this layer rather than depending on
|
|
4
|
+
// @modwrench/nexus / @modwrench/modio because that would invert the dep
|
|
5
|
+
// direction (workbench is supposed to be the leaner package, and the platform
|
|
6
|
+
// packages don't need to know about workbench).
|
|
7
|
+
//
|
|
8
|
+
// Both clients use the shared @modwrench/core HTTP client for retry/backoff
|
|
9
|
+
// /429 handling /concurrency cap — keeps behavior consistent with what the
|
|
10
|
+
// platform packages do. Credentials are loaded best-effort at register time
|
|
11
|
+
// via loadCredential; if neither keychain nor env is configured for a given
|
|
12
|
+
// platform, the client is null and the metadata tool surfaces a clear error.
|
|
13
|
+
const USER_AGENT = "ModWrench/0.0.1 (+https://github.com/171county/modwrench)";
|
|
14
|
+
export function tryCreateNexusClient() {
|
|
15
|
+
let credential;
|
|
16
|
+
try {
|
|
17
|
+
credential = loadCredential({
|
|
18
|
+
service: "nexus",
|
|
19
|
+
envVar: "NEXUS_API_KEY",
|
|
20
|
+
authHint: "no-op",
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
const baseUrl = getEnv("NEXUS_BASE_URL", "https://api.nexusmods.com/v1");
|
|
27
|
+
const http = createHttpClient({
|
|
28
|
+
baseUrl,
|
|
29
|
+
userAgent: USER_AGENT,
|
|
30
|
+
errorCodePrefix: "nexus",
|
|
31
|
+
authHeaders: () => {
|
|
32
|
+
if (credential.source === "keychain") {
|
|
33
|
+
return { Authorization: `Bearer ${credential.accessToken}` };
|
|
34
|
+
}
|
|
35
|
+
return { apikey: credential.apiKey };
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
return {
|
|
39
|
+
baseUrl,
|
|
40
|
+
async request(path) {
|
|
41
|
+
log("debug", "workbench.nexus.request", { path });
|
|
42
|
+
return http.request(path);
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
export function tryCreateModioClient() {
|
|
47
|
+
let credential;
|
|
48
|
+
try {
|
|
49
|
+
credential = loadCredential({
|
|
50
|
+
service: "modio",
|
|
51
|
+
envVar: "MODIO_API_KEY",
|
|
52
|
+
authHint: "no-op",
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
const baseUrl = getEnv("MODIO_BASE_URL", "https://api.mod.io/v1");
|
|
59
|
+
const http = createHttpClient({
|
|
60
|
+
baseUrl,
|
|
61
|
+
userAgent: USER_AGENT,
|
|
62
|
+
errorCodePrefix: "modio",
|
|
63
|
+
authHeaders: () => {
|
|
64
|
+
if (credential.source === "keychain") {
|
|
65
|
+
return { Authorization: `Bearer ${credential.accessToken}` };
|
|
66
|
+
}
|
|
67
|
+
return {};
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
return {
|
|
71
|
+
baseUrl,
|
|
72
|
+
async request(path, query) {
|
|
73
|
+
const finalQuery = {
|
|
74
|
+
...(query ?? {}),
|
|
75
|
+
};
|
|
76
|
+
// mod.io's quirk: legacy API key goes on the query string, not as a
|
|
77
|
+
// header. OAuth tokens use the Authorization header (handled by
|
|
78
|
+
// authHeaders above).
|
|
79
|
+
if (credential.source === "env") {
|
|
80
|
+
finalQuery["api_key"] = credential.apiKey;
|
|
81
|
+
}
|
|
82
|
+
log("debug", "workbench.modio.request", {
|
|
83
|
+
path,
|
|
84
|
+
auth: credential.source === "env" ? "api_key (query)" : "Bearer (header)",
|
|
85
|
+
});
|
|
86
|
+
return http.request(path, { query: finalQuery });
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=clients.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clients.js","sourceRoot":"","sources":["../../src/metadata/clients.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/metadata/index.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAEV,qBAAqB,EACrB,sBAAsB,EACvB,MAAM,YAAY,CAAC;AAuDpB,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,qBAAqB,GAC3B,OAAO,CAAC,sBAAsB,CAAC,CAEjC"}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { tryCreateNexusClient, tryCreateModioClient, } from "./clients.js";
|
|
2
|
+
import { normalizeNexusMod, normalizeModioMod, } from "./normalize.js";
|
|
3
|
+
// ─── Per-platform query implementations ───────────────────────────────────────
|
|
4
|
+
async function queryNexus(client, gameDomain, modIdStr) {
|
|
5
|
+
const modId = Number.parseInt(modIdStr, 10);
|
|
6
|
+
if (!Number.isFinite(modId) || modId <= 0) {
|
|
7
|
+
throw new Error(`Nexus mod IDs are numeric; got "${modIdStr}". Pass the mod's numeric Nexus ID.`);
|
|
8
|
+
}
|
|
9
|
+
return client.request(`/games/${gameDomain}/mods/${modId}.json`);
|
|
10
|
+
}
|
|
11
|
+
async function queryModioById(client, gameIdStr, modIdStr) {
|
|
12
|
+
const gameId = Number.parseInt(gameIdStr, 10);
|
|
13
|
+
const modId = Number.parseInt(modIdStr, 10);
|
|
14
|
+
if (!Number.isFinite(gameId) || !Number.isFinite(modId)) {
|
|
15
|
+
throw new Error(`mod.io IDs are numeric; got gameId="${gameIdStr}", modId="${modIdStr}".`);
|
|
16
|
+
}
|
|
17
|
+
return client.request(`/games/${gameId}/mods/${modId}`);
|
|
18
|
+
}
|
|
19
|
+
async function searchModioByName(client, gameIdStr, name) {
|
|
20
|
+
const gameId = Number.parseInt(gameIdStr, 10);
|
|
21
|
+
if (!Number.isFinite(gameId)) {
|
|
22
|
+
throw new Error(`mod.io gameId must be numeric; got "${gameIdStr}".`);
|
|
23
|
+
}
|
|
24
|
+
const list = await client.request(`/games/${gameId}/mods`, { _q: name, _limit: 1 });
|
|
25
|
+
return list.data?.[0] ?? null;
|
|
26
|
+
}
|
|
27
|
+
// ─── Orchestration ────────────────────────────────────────────────────────────
|
|
28
|
+
export function queryModMetadata(input) {
|
|
29
|
+
return queryModMetadataInner(input);
|
|
30
|
+
}
|
|
31
|
+
async function queryModMetadataInner(input) {
|
|
32
|
+
const platform = input.platform ?? "any";
|
|
33
|
+
const platformErrors = {};
|
|
34
|
+
const attempted = [];
|
|
35
|
+
if (!input.modId && !input.modName) {
|
|
36
|
+
return {
|
|
37
|
+
found: false,
|
|
38
|
+
reason: "Provide either modId or modName.",
|
|
39
|
+
attemptedPlatforms: [],
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
const tryNexus = (platform === "nexus" || platform === "any") &&
|
|
43
|
+
input.modId !== undefined &&
|
|
44
|
+
input.gameId !== undefined;
|
|
45
|
+
const tryModioById = (platform === "modio" || platform === "any") &&
|
|
46
|
+
input.modId !== undefined &&
|
|
47
|
+
input.gameId !== undefined;
|
|
48
|
+
const tryModioByName = (platform === "modio" || platform === "any") &&
|
|
49
|
+
input.modName !== undefined &&
|
|
50
|
+
input.gameId !== undefined;
|
|
51
|
+
if (platform === "thunderstore" || platform === "curseforge") {
|
|
52
|
+
return {
|
|
53
|
+
found: false,
|
|
54
|
+
reason: `Platform "${platform}" support is not implemented yet (planned for v3+). Use platform="nexus" or "modio" for now.`,
|
|
55
|
+
attemptedPlatforms: [],
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
// Try Nexus first (it's the dominant home for Bethesda games, which is
|
|
59
|
+
// where the bulk of crash-diagnosis traffic will come from).
|
|
60
|
+
if (tryNexus) {
|
|
61
|
+
attempted.push("nexus");
|
|
62
|
+
const client = tryCreateNexusClient();
|
|
63
|
+
if (!client) {
|
|
64
|
+
platformErrors["nexus"] =
|
|
65
|
+
"No Nexus credential configured. Run `modwrench auth login nexus` " +
|
|
66
|
+
"(OAuth) or set NEXUS_API_KEY in your .env.";
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
try {
|
|
70
|
+
const raw = await queryNexus(client, input.gameId, input.modId);
|
|
71
|
+
return {
|
|
72
|
+
found: true,
|
|
73
|
+
mod: normalizeNexusMod(raw, input.gameId),
|
|
74
|
+
attemptedPlatforms: attempted,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
catch (err) {
|
|
78
|
+
platformErrors["nexus"] =
|
|
79
|
+
err instanceof Error ? err.message : String(err);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (tryModioById) {
|
|
84
|
+
attempted.push("modio");
|
|
85
|
+
const client = tryCreateModioClient();
|
|
86
|
+
if (!client) {
|
|
87
|
+
platformErrors["modio"] =
|
|
88
|
+
"No mod.io credential configured. Run `modwrench auth login modio` " +
|
|
89
|
+
"(OAuth) or set MODIO_API_KEY in your .env.";
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
try {
|
|
93
|
+
const raw = await queryModioById(client, input.gameId, input.modId);
|
|
94
|
+
return {
|
|
95
|
+
found: true,
|
|
96
|
+
mod: normalizeModioMod(raw),
|
|
97
|
+
attemptedPlatforms: attempted,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
catch (err) {
|
|
101
|
+
platformErrors["modio"] =
|
|
102
|
+
err instanceof Error ? err.message : String(err);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
if (tryModioByName) {
|
|
107
|
+
if (!attempted.includes("modio"))
|
|
108
|
+
attempted.push("modio");
|
|
109
|
+
const client = tryCreateModioClient();
|
|
110
|
+
if (!client) {
|
|
111
|
+
platformErrors["modio"] ??=
|
|
112
|
+
"No mod.io credential configured. Run `modwrench auth login modio`.";
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
try {
|
|
116
|
+
const raw = await searchModioByName(client, input.gameId, input.modName);
|
|
117
|
+
if (raw) {
|
|
118
|
+
return {
|
|
119
|
+
found: true,
|
|
120
|
+
mod: normalizeModioMod(raw),
|
|
121
|
+
attemptedPlatforms: attempted,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
platformErrors["modio"] = `No mod.io match for name "${input.modName}".`;
|
|
125
|
+
}
|
|
126
|
+
catch (err) {
|
|
127
|
+
platformErrors["modio"] =
|
|
128
|
+
err instanceof Error ? err.message : String(err);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
// No platform attempted? That means the input was insufficient for the
|
|
133
|
+
// platforms we actually support — surface a helpful error.
|
|
134
|
+
if (attempted.length === 0) {
|
|
135
|
+
return {
|
|
136
|
+
found: false,
|
|
137
|
+
reason: "Insufficient input. Nexus requires (modId + gameId as Nexus domain). " +
|
|
138
|
+
"mod.io requires (modId + numeric gameId) or (modName + numeric gameId).",
|
|
139
|
+
attemptedPlatforms: [],
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
return {
|
|
143
|
+
found: false,
|
|
144
|
+
reason: `No matching mod found across ${attempted.join(", ")}.`,
|
|
145
|
+
attemptedPlatforms: attempted,
|
|
146
|
+
platformErrors,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/metadata/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { NormalizedMod } from "./types.js";
|
|
2
|
+
type NexusModResponse = {
|
|
3
|
+
mod_id: number;
|
|
4
|
+
domain_name: string;
|
|
5
|
+
name: string;
|
|
6
|
+
summary?: string;
|
|
7
|
+
description?: string;
|
|
8
|
+
version?: string;
|
|
9
|
+
author: string;
|
|
10
|
+
uploaded_by?: string;
|
|
11
|
+
uploaded_users_profile_url?: string;
|
|
12
|
+
mod_downloads?: number;
|
|
13
|
+
endorsement_count?: number;
|
|
14
|
+
updated_timestamp?: number;
|
|
15
|
+
updated_time?: string;
|
|
16
|
+
};
|
|
17
|
+
export declare function normalizeNexusMod(mod: NexusModResponse, gameDomain: string): NormalizedMod;
|
|
18
|
+
type ModioModResponse = {
|
|
19
|
+
id: number;
|
|
20
|
+
game_id: number;
|
|
21
|
+
name: string;
|
|
22
|
+
name_id: string;
|
|
23
|
+
summary?: string;
|
|
24
|
+
description?: string;
|
|
25
|
+
description_plaintext?: string;
|
|
26
|
+
profile_url?: string;
|
|
27
|
+
date_updated?: number;
|
|
28
|
+
submitted_by?: {
|
|
29
|
+
username?: string;
|
|
30
|
+
profile_url?: string;
|
|
31
|
+
};
|
|
32
|
+
modfile?: {
|
|
33
|
+
version?: string;
|
|
34
|
+
};
|
|
35
|
+
stats?: {
|
|
36
|
+
downloads_total?: number;
|
|
37
|
+
ratings_total?: number;
|
|
38
|
+
ratings_positive?: number;
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
export declare function normalizeModioMod(mod: ModioModResponse): NormalizedMod;
|
|
42
|
+
export type { NexusModResponse, ModioModResponse };
|
|
43
|
+
//# sourceMappingURL=normalize.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"normalize.d.ts","sourceRoot":"","sources":["../../src/metadata/normalize.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAkB,MAAM,YAAY,CAAC;AA0BhE,KAAK,gBAAgB,GAAG;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0BAA0B,CAAC,EAAE,MAAM,CAAC;IACpC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,gBAAgB,EACrB,UAAU,EAAE,MAAM,GACjB,aAAa,CA0Bf;AAID,KAAK,gBAAgB,GAAG;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3D,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/B,KAAK,CAAC,EAAE;QACN,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;KAC3B,CAAC;CACH,CAAC;AAEF,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,gBAAgB,GAAG,aAAa,CAiCtE;AAED,YAAY,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,CAAC"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// Best-effort response normalization. The wiring prompt's permissions matrix
|
|
2
|
+
// (modificationAllowed / assetReuseAllowed / conversionAllowed) is NOT fully
|
|
3
|
+
// exposed by either platform's public API — those flags are set per-mod on
|
|
4
|
+
// the page itself. We honor the spec shape but mark fields null when unknown
|
|
5
|
+
// and surface a note so the LLM doesn't fabricate certainty.
|
|
6
|
+
const PERMISSIONS_NOTE = "Author permissions (modification, asset reuse, conversion) are set on the " +
|
|
7
|
+
"mod's page and may not be fully exposed via API. Always confirm via " +
|
|
8
|
+
"pageUrl before reusing assets or republishing.";
|
|
9
|
+
function unknownPermissions() {
|
|
10
|
+
return {
|
|
11
|
+
modificationAllowed: null,
|
|
12
|
+
assetReuseAllowed: null,
|
|
13
|
+
conversionAllowed: null,
|
|
14
|
+
note: PERMISSIONS_NOTE,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
export function normalizeNexusMod(mod, gameDomain) {
|
|
18
|
+
const idStr = String(mod.mod_id);
|
|
19
|
+
const pageUrl = `https://www.nexusmods.com/${gameDomain}/mods/${idStr}`;
|
|
20
|
+
const out = {
|
|
21
|
+
id: idStr,
|
|
22
|
+
name: mod.name,
|
|
23
|
+
platform: "nexus",
|
|
24
|
+
pageUrl,
|
|
25
|
+
attribution: {
|
|
26
|
+
author: mod.author,
|
|
27
|
+
sourcePlatform: "nexus",
|
|
28
|
+
sourceModId: idStr,
|
|
29
|
+
pageUrl,
|
|
30
|
+
},
|
|
31
|
+
permissions: unknownPermissions(),
|
|
32
|
+
};
|
|
33
|
+
if (mod.version)
|
|
34
|
+
out.version = mod.version;
|
|
35
|
+
if (mod.summary)
|
|
36
|
+
out.summary = mod.summary;
|
|
37
|
+
if (mod.description)
|
|
38
|
+
out.description = mod.description;
|
|
39
|
+
if (mod.mod_downloads !== undefined)
|
|
40
|
+
out.downloadCount = mod.mod_downloads;
|
|
41
|
+
if (mod.endorsement_count !== undefined)
|
|
42
|
+
out.endorsements = mod.endorsement_count;
|
|
43
|
+
if (mod.updated_time)
|
|
44
|
+
out.lastUpdated = mod.updated_time;
|
|
45
|
+
else if (mod.updated_timestamp) {
|
|
46
|
+
out.lastUpdated = new Date(mod.updated_timestamp * 1000).toISOString();
|
|
47
|
+
}
|
|
48
|
+
return out;
|
|
49
|
+
}
|
|
50
|
+
export function normalizeModioMod(mod) {
|
|
51
|
+
const idStr = String(mod.id);
|
|
52
|
+
const pageUrl = mod.profile_url ?? `https://mod.io/g/${mod.game_id}/m/${mod.name_id}`;
|
|
53
|
+
const author = mod.submitted_by?.username ?? "(unknown)";
|
|
54
|
+
const out = {
|
|
55
|
+
id: idStr,
|
|
56
|
+
name: mod.name,
|
|
57
|
+
platform: "modio",
|
|
58
|
+
pageUrl,
|
|
59
|
+
attribution: {
|
|
60
|
+
author,
|
|
61
|
+
sourcePlatform: "modio",
|
|
62
|
+
sourceModId: idStr,
|
|
63
|
+
pageUrl,
|
|
64
|
+
},
|
|
65
|
+
permissions: unknownPermissions(),
|
|
66
|
+
};
|
|
67
|
+
if (mod.modfile?.version)
|
|
68
|
+
out.version = mod.modfile.version;
|
|
69
|
+
if (mod.summary)
|
|
70
|
+
out.summary = mod.summary;
|
|
71
|
+
// Prefer plaintext when available — it's friendlier for LLM ingestion than
|
|
72
|
+
// the HTML-laden description field.
|
|
73
|
+
if (mod.description_plaintext)
|
|
74
|
+
out.description = mod.description_plaintext;
|
|
75
|
+
else if (mod.description)
|
|
76
|
+
out.description = mod.description;
|
|
77
|
+
if (mod.stats?.downloads_total !== undefined) {
|
|
78
|
+
out.downloadCount = mod.stats.downloads_total;
|
|
79
|
+
}
|
|
80
|
+
if (mod.stats?.ratings_positive !== undefined) {
|
|
81
|
+
out.endorsements = mod.stats.ratings_positive;
|
|
82
|
+
}
|
|
83
|
+
if (mod.date_updated) {
|
|
84
|
+
out.lastUpdated = new Date(mod.date_updated * 1000).toISOString();
|
|
85
|
+
}
|
|
86
|
+
return out;
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=normalize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"normalize.js","sourceRoot":"","sources":["../../src/metadata/normalize.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
export type ModPlatform = "nexus" | "modio" | "thunderstore" | "curseforge";
|
|
2
|
+
export type ModPermissions = {
|
|
3
|
+
/** Allow modification / patches by other authors. Null = not exposed via API. */
|
|
4
|
+
modificationAllowed: boolean | null;
|
|
5
|
+
/** Allow asset reuse in other mods. Null = not exposed via API. */
|
|
6
|
+
assetReuseAllowed: boolean | null;
|
|
7
|
+
/** Allow conversion to other games. Null = not exposed via API. */
|
|
8
|
+
conversionAllowed: boolean | null;
|
|
9
|
+
/** Human-readable note about why fields may be null. */
|
|
10
|
+
note: string;
|
|
11
|
+
};
|
|
12
|
+
export type ModAttribution = {
|
|
13
|
+
author: string;
|
|
14
|
+
sourcePlatform: ModPlatform;
|
|
15
|
+
sourceModId: string;
|
|
16
|
+
pageUrl: string;
|
|
17
|
+
};
|
|
18
|
+
export type NormalizedMod = {
|
|
19
|
+
id: string;
|
|
20
|
+
name: string;
|
|
21
|
+
/** Mandatory attribution block, non-optional and always populated. */
|
|
22
|
+
attribution: ModAttribution;
|
|
23
|
+
platform: ModPlatform;
|
|
24
|
+
version?: string;
|
|
25
|
+
lastUpdated?: string;
|
|
26
|
+
summary?: string;
|
|
27
|
+
description?: string;
|
|
28
|
+
downloadCount?: number;
|
|
29
|
+
endorsements?: number;
|
|
30
|
+
permissions?: ModPermissions;
|
|
31
|
+
pageUrl: string;
|
|
32
|
+
};
|
|
33
|
+
export type QueryModMetadataInput = {
|
|
34
|
+
modId?: string;
|
|
35
|
+
modName?: string;
|
|
36
|
+
platform?: ModPlatform | "any";
|
|
37
|
+
/**
|
|
38
|
+
* Platform-specific game identifier:
|
|
39
|
+
* - nexus: domain name string ("skyrimspecialedition")
|
|
40
|
+
* - modio: numeric game id as a string ("6195")
|
|
41
|
+
* For "any" with a canonical workbench gameId, the lookup resolves per
|
|
42
|
+
* platform via the KNOWN_GAMES catalogue.
|
|
43
|
+
*/
|
|
44
|
+
gameId?: string;
|
|
45
|
+
};
|
|
46
|
+
export type QueryModMetadataResult = {
|
|
47
|
+
found: true;
|
|
48
|
+
mod: NormalizedMod;
|
|
49
|
+
attemptedPlatforms: ModPlatform[];
|
|
50
|
+
} | {
|
|
51
|
+
found: false;
|
|
52
|
+
reason: string;
|
|
53
|
+
attemptedPlatforms: ModPlatform[];
|
|
54
|
+
/** Per-platform diagnostics so the LLM can suggest next steps. */
|
|
55
|
+
platformErrors?: Record<string, string>;
|
|
56
|
+
};
|
|
57
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/metadata/types.ts"],"names":[],"mappings":"AAQA,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,OAAO,GAAG,cAAc,GAAG,YAAY,CAAC;AAE5E,MAAM,MAAM,cAAc,GAAG;IAC3B,iFAAiF;IACjF,mBAAmB,EAAE,OAAO,GAAG,IAAI,CAAC;IACpC,mEAAmE;IACnE,iBAAiB,EAAE,OAAO,GAAG,IAAI,CAAC;IAClC,mEAAmE;IACnE,iBAAiB,EAAE,OAAO,GAAG,IAAI,CAAC;IAClC,wDAAwD;IACxD,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,WAAW,CAAC;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,sEAAsE;IACtE,WAAW,EAAE,cAAc,CAAC;IAC5B,QAAQ,EAAE,WAAW,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,cAAc,CAAC;IAC7B,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,WAAW,GAAG,KAAK,CAAC;IAC/B;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAC9B;IAAE,KAAK,EAAE,IAAI,CAAC;IAAC,GAAG,EAAE,aAAa,CAAC;IAAC,kBAAkB,EAAE,WAAW,EAAE,CAAA;CAAE,GACtE;IACE,KAAK,EAAE,KAAK,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,kBAAkB,EAAE,WAAW,EAAE,CAAC;IAClC,kEAAkE;IAClE,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACzC,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// Normalized cross-platform mod metadata. The wiring prompt is explicit about
|
|
2
|
+
// what must NEVER be stripped: author name, source platform, and source mod
|
|
3
|
+
// id. Those fields are non-optional on every mod that comes back.
|
|
4
|
+
//
|
|
5
|
+
// The "Trust Architecture" rule from the wiring prompt:
|
|
6
|
+
// "Attribution preserved end-to-end. Author names, source platforms, and
|
|
7
|
+
// original mod URLs appear in every output that mentions a mod."
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/metadata/types.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,4EAA4E;AAC5E,kEAAkE;AAClE,EAAE;AACF,wDAAwD;AACxD,2EAA2E;AAC3E,mEAAmE"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
/**
|
|
3
|
+
* Register all Workbench tools on the given MCP server. Workbench tools are
|
|
4
|
+
* uncredentialed — they read local filesystem state, not platform APIs. That
|
|
5
|
+
* makes the registration shape slightly different from @modwrench/nexus and
|
|
6
|
+
* @modwrench/modio: no Credential parameter required.
|
|
7
|
+
*
|
|
8
|
+
* Returns metadata useful for boot logging.
|
|
9
|
+
*/
|
|
10
|
+
export declare function registerWorkbenchTools(server: McpServer): {
|
|
11
|
+
toolCount: number;
|
|
12
|
+
};
|
|
13
|
+
//# sourceMappingURL=register.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../src/register.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AASzE;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,SAAS,GAAG;IACzD,SAAS,EAAE,MAAM,CAAC;CACnB,CAuQA"}
|