@edgemodders/redcode-mcp 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/README.md +2 -1
- package/dist/ThirdPartyNotices.txt +1 -1
- package/dist/server.cjs +548 -122
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
What changed in each redcode-mcp release. The release notes on GitHub come from these sections; changes are grouped as **New**, **Changed**, **Fixed** and **Other**, and older sections keep the form they had.
|
|
4
|
+
|
|
5
|
+
## Unreleased
|
|
6
|
+
|
|
7
|
+
## 0.5.0 (2026-11-10)
|
|
8
|
+
|
|
9
|
+
For EDGEfab and EDGEhack: `library_add`, and image imports with a texture setup. `texture` needs REDbridge 0.12.0; the rest work as in 0.4.0.
|
|
10
|
+
|
|
11
|
+
**New**
|
|
12
|
+
|
|
13
|
+
- `library_add`: a game file or a project's file, with the files it needs, as one asset item in the EDGE Library (REDcode issue #19). With no Library yet or no folder given, it answers what to ask the modder. `gamePathFolders` is the Use the game path choice. Works with REDbridge 0.10.0 or later.
|
|
14
|
+
- `project_import` takes `texture`, an image's `.xbm` setup (a preset, a texture group, or its fields), and answers the setup read back. Needs REDbridge 0.12.0.
|
|
15
|
+
|
|
3
16
|
## 0.4.0 (2026-10-27)
|
|
4
17
|
|
|
5
18
|
The new tool needs REDbridge 0.11.0, the bridge for game 2.5; the rest work as in 0.3.0.
|
package/README.md
CHANGED
|
@@ -53,7 +53,8 @@ To build from source, run `npm install` and `npm run mcp:build` in the [REDcode
|
|
|
53
53
|
| `texture_to_png`, `texture_file_to_png` | Decode an `.xbm` to PNG |
|
|
54
54
|
| `hash_fnv1a64`, `hash_resolve` | Hash a string; look a hash up |
|
|
55
55
|
| `archive_open_mod`, `archive_extract_from_mod` | List the resources in a mod's `.archive` on disk; extract them, by game path or hash. No game needed |
|
|
56
|
-
| `project_export`, `project_import`, `project_pack` | WolvenKit's exporter (a mesh to glTF, a texture to an image, a `.wem` to Ogg) and importer between `source/archive` and `source/raw`; pack a folder into `<modName>.archive` |
|
|
56
|
+
| `project_export`, `project_import`, `project_pack` | WolvenKit's exporter (a mesh to glTF, a texture to an image, a `.wem` to Ogg) and importer between `source/archive` and `source/raw`; pack a folder into `<modName>.archive`. An image import takes `texture`, the `.xbm`'s setup (a preset, a texture group or its fields), and answers the setup read back (bridge 0.12.0) |
|
|
57
|
+
| `library_add` | A game file or a project's file, with the files it needs, as one asset item in the EDGE Library. With no Library yet or no folder given, it answers what to ask the modder |
|
|
57
58
|
| `loc_languages`, `loc_search`, `loc_get` | The game's on-screen texts (LocKeys) per language, base game and Phantom Liberty, read offline. Search by LocKey, secondary key or words of the text; get by LocKey or secondary key. With `projectDir`, the project's own onscreens entries win. Bridge 0.10.0 |
|
|
58
59
|
| `tweakdb_types`, `tweakdb_records`, `tweakdb_record`, `tweakdb_flat`, `tweakdb_search`, `tweakdb_names` | TweakDB as the game ships it, read offline from `tweakdb_ep1.bin` (the first call loads it, about 12 s). Records by type (subtypes included) or prefix with the flats you name, one record with every flat, one flat, a search by path words, and ids to paths. Record links come back as paths, LocKeys as `{ key, text }`. Bridge 0.8.0 |
|
|
59
60
|
| `mesh_items` | The TweakDB item records whose look shows a mesh, each with its display name, the mesh appearance it shows, the `.app` look and the root entity on the way. Covers items that pick their look by `appearanceName`, such as clothing. Bridge 0.11.0 |
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
@edgemodders/redcode-mcp 0.
|
|
1
|
+
@edgemodders/redcode-mcp 0.5.0 bundles the following packages into dist/server.cjs. Their notices follow.
|
|
2
2
|
|
|
3
3
|
------------------------------------------------------------------------------
|
|
4
4
|
|
package/dist/server.cjs
CHANGED
|
@@ -2236,8 +2236,8 @@ var require_resolve = __commonJS({
|
|
|
2236
2236
|
}
|
|
2237
2237
|
return count;
|
|
2238
2238
|
}
|
|
2239
|
-
function getFullPath(resolver, id = "",
|
|
2240
|
-
if (
|
|
2239
|
+
function getFullPath(resolver, id = "", normalize2) {
|
|
2240
|
+
if (normalize2 !== false)
|
|
2241
2241
|
id = normalizeId(id);
|
|
2242
2242
|
const p = resolver.parse(id);
|
|
2243
2243
|
return _getFullPath(resolver, p);
|
|
@@ -2985,7 +2985,7 @@ var require_compile = __commonJS({
|
|
|
2985
2985
|
const schOrFunc = root.refs[ref];
|
|
2986
2986
|
if (schOrFunc)
|
|
2987
2987
|
return schOrFunc;
|
|
2988
|
-
let _sch =
|
|
2988
|
+
let _sch = resolve4.call(this, root, ref);
|
|
2989
2989
|
if (_sch === void 0) {
|
|
2990
2990
|
const schema = (_a = root.localRefs) === null || _a === void 0 ? void 0 : _a[ref];
|
|
2991
2991
|
const { schemaId } = this.opts;
|
|
@@ -3012,7 +3012,7 @@ var require_compile = __commonJS({
|
|
|
3012
3012
|
function sameSchemaEnv(s1, s2) {
|
|
3013
3013
|
return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
|
|
3014
3014
|
}
|
|
3015
|
-
function
|
|
3015
|
+
function resolve4(root, ref) {
|
|
3016
3016
|
let sch;
|
|
3017
3017
|
while (typeof (sch = this.refs[ref]) == "string")
|
|
3018
3018
|
ref = sch;
|
|
@@ -3263,8 +3263,8 @@ var require_utils = __commonJS({
|
|
|
3263
3263
|
}
|
|
3264
3264
|
return ind;
|
|
3265
3265
|
}
|
|
3266
|
-
function removeDotSegments(
|
|
3267
|
-
let input =
|
|
3266
|
+
function removeDotSegments(path6) {
|
|
3267
|
+
let input = path6;
|
|
3268
3268
|
const output = [];
|
|
3269
3269
|
let nextSlash = -1;
|
|
3270
3270
|
let len = 0;
|
|
@@ -3673,8 +3673,8 @@ var require_schemes = __commonJS({
|
|
|
3673
3673
|
}
|
|
3674
3674
|
if (wsComponent.resourceName) {
|
|
3675
3675
|
const queryIndex = wsComponent.resourceName.indexOf("?");
|
|
3676
|
-
const
|
|
3677
|
-
wsComponent.path =
|
|
3676
|
+
const path6 = queryIndex === -1 ? wsComponent.resourceName : wsComponent.resourceName.slice(0, queryIndex);
|
|
3677
|
+
wsComponent.path = path6 && path6 !== "/" ? path6 : void 0;
|
|
3678
3678
|
wsComponent.query = queryIndex === -1 ? void 0 : wsComponent.resourceName.slice(queryIndex + 1);
|
|
3679
3679
|
wsComponent.resourceName = void 0;
|
|
3680
3680
|
}
|
|
@@ -3832,7 +3832,7 @@ var require_fast_uri = __commonJS({
|
|
|
3832
3832
|
}
|
|
3833
3833
|
return decodedScheme;
|
|
3834
3834
|
}
|
|
3835
|
-
function
|
|
3835
|
+
function normalize2(uri, options) {
|
|
3836
3836
|
if (typeof uri === "string") {
|
|
3837
3837
|
uri = /** @type {T} */
|
|
3838
3838
|
normalizeString(uri, options);
|
|
@@ -3842,7 +3842,7 @@ var require_fast_uri = __commonJS({
|
|
|
3842
3842
|
}
|
|
3843
3843
|
return uri;
|
|
3844
3844
|
}
|
|
3845
|
-
function
|
|
3845
|
+
function resolve4(baseURI, relativeURI, options) {
|
|
3846
3846
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
3847
3847
|
const {
|
|
3848
3848
|
parsed: baseParsed,
|
|
@@ -3875,49 +3875,49 @@ var require_fast_uri = __commonJS({
|
|
|
3875
3875
|
schemelessOptions.skipEscape = true;
|
|
3876
3876
|
return serialize(resolved, schemelessOptions);
|
|
3877
3877
|
}
|
|
3878
|
-
function resolveComponent(base,
|
|
3878
|
+
function resolveComponent(base, relative3, options, skipNormalization) {
|
|
3879
3879
|
const target = {};
|
|
3880
3880
|
if (!skipNormalization) {
|
|
3881
3881
|
base = parse3(serialize(base, options), options);
|
|
3882
|
-
|
|
3882
|
+
relative3 = parse3(serialize(relative3, options), options);
|
|
3883
3883
|
}
|
|
3884
3884
|
options = options || {};
|
|
3885
|
-
if (!options.tolerant &&
|
|
3886
|
-
target.scheme =
|
|
3887
|
-
target.userinfo =
|
|
3888
|
-
target.host =
|
|
3889
|
-
target.port =
|
|
3890
|
-
target.path = removeDotSegments(
|
|
3891
|
-
target.query =
|
|
3885
|
+
if (!options.tolerant && relative3.scheme) {
|
|
3886
|
+
target.scheme = relative3.scheme;
|
|
3887
|
+
target.userinfo = relative3.userinfo;
|
|
3888
|
+
target.host = relative3.host;
|
|
3889
|
+
target.port = relative3.port;
|
|
3890
|
+
target.path = removeDotSegments(relative3.path || "");
|
|
3891
|
+
target.query = relative3.query;
|
|
3892
3892
|
} else {
|
|
3893
|
-
if (
|
|
3894
|
-
target.userinfo =
|
|
3895
|
-
target.host =
|
|
3896
|
-
target.port =
|
|
3897
|
-
target.path = removeDotSegments(
|
|
3898
|
-
target.query =
|
|
3893
|
+
if (relative3.userinfo !== void 0 || relative3.host !== void 0 || relative3.port !== void 0) {
|
|
3894
|
+
target.userinfo = relative3.userinfo;
|
|
3895
|
+
target.host = relative3.host;
|
|
3896
|
+
target.port = relative3.port;
|
|
3897
|
+
target.path = removeDotSegments(relative3.path || "");
|
|
3898
|
+
target.query = relative3.query;
|
|
3899
3899
|
} else {
|
|
3900
|
-
if (!
|
|
3900
|
+
if (!relative3.path) {
|
|
3901
3901
|
target.path = base.path;
|
|
3902
|
-
if (
|
|
3903
|
-
target.query =
|
|
3902
|
+
if (relative3.query !== void 0) {
|
|
3903
|
+
target.query = relative3.query;
|
|
3904
3904
|
} else {
|
|
3905
3905
|
target.query = base.query;
|
|
3906
3906
|
}
|
|
3907
3907
|
} else {
|
|
3908
|
-
if (
|
|
3909
|
-
target.path = removeDotSegments(
|
|
3908
|
+
if (relative3.path[0] === "/") {
|
|
3909
|
+
target.path = removeDotSegments(relative3.path);
|
|
3910
3910
|
} else {
|
|
3911
3911
|
if ((base.userinfo !== void 0 || base.host !== void 0 || base.port !== void 0) && !base.path) {
|
|
3912
|
-
target.path = "/" +
|
|
3912
|
+
target.path = "/" + relative3.path;
|
|
3913
3913
|
} else if (!base.path) {
|
|
3914
|
-
target.path =
|
|
3914
|
+
target.path = relative3.path;
|
|
3915
3915
|
} else {
|
|
3916
|
-
target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) +
|
|
3916
|
+
target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative3.path;
|
|
3917
3917
|
}
|
|
3918
3918
|
target.path = removeDotSegments(target.path);
|
|
3919
3919
|
}
|
|
3920
|
-
target.query =
|
|
3920
|
+
target.query = relative3.query;
|
|
3921
3921
|
}
|
|
3922
3922
|
target.userinfo = base.userinfo;
|
|
3923
3923
|
target.host = base.host;
|
|
@@ -3925,7 +3925,7 @@ var require_fast_uri = __commonJS({
|
|
|
3925
3925
|
}
|
|
3926
3926
|
target.scheme = base.scheme;
|
|
3927
3927
|
}
|
|
3928
|
-
target.fragment =
|
|
3928
|
+
target.fragment = relative3.fragment;
|
|
3929
3929
|
return target;
|
|
3930
3930
|
}
|
|
3931
3931
|
function equal(uriA, uriB, options) {
|
|
@@ -4210,8 +4210,8 @@ var require_fast_uri = __commonJS({
|
|
|
4210
4210
|
}
|
|
4211
4211
|
var fastUri = {
|
|
4212
4212
|
SCHEMES,
|
|
4213
|
-
normalize,
|
|
4214
|
-
resolve:
|
|
4213
|
+
normalize: normalize2,
|
|
4214
|
+
resolve: resolve4,
|
|
4215
4215
|
resolveComponent,
|
|
4216
4216
|
equal,
|
|
4217
4217
|
serialize,
|
|
@@ -7187,12 +7187,12 @@ var require_dist = __commonJS({
|
|
|
7187
7187
|
throw new Error(`Unknown format "${name}"`);
|
|
7188
7188
|
return f;
|
|
7189
7189
|
};
|
|
7190
|
-
function addFormats(ajv, list,
|
|
7190
|
+
function addFormats(ajv, list, fs6, exportName) {
|
|
7191
7191
|
var _a;
|
|
7192
7192
|
var _b;
|
|
7193
7193
|
(_a = (_b = ajv.opts.code).formats) !== null && _a !== void 0 ? _a : _b.formats = (0, codegen_1._)`require("ajv-formats/dist/formats").${exportName}`;
|
|
7194
7194
|
for (const f of list)
|
|
7195
|
-
ajv.addFormat(f,
|
|
7195
|
+
ajv.addFormat(f, fs6[f]);
|
|
7196
7196
|
}
|
|
7197
7197
|
module2.exports = exports2 = formatsPlugin;
|
|
7198
7198
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
@@ -8416,8 +8416,8 @@ var require_semaphore = __commonJS({
|
|
|
8416
8416
|
this._waiting = [];
|
|
8417
8417
|
}
|
|
8418
8418
|
lock(thunk) {
|
|
8419
|
-
return new Promise((
|
|
8420
|
-
this._waiting.push({ thunk, resolve:
|
|
8419
|
+
return new Promise((resolve4, reject) => {
|
|
8420
|
+
this._waiting.push({ thunk, resolve: resolve4, reject });
|
|
8421
8421
|
this.runNext();
|
|
8422
8422
|
});
|
|
8423
8423
|
}
|
|
@@ -10043,9 +10043,9 @@ ${JSON.stringify(message, null, 4)}`);
|
|
|
10043
10043
|
if (typeof cancellationStrategy.sender.enableCancellation === "function") {
|
|
10044
10044
|
cancellationStrategy.sender.enableCancellation(requestMessage);
|
|
10045
10045
|
}
|
|
10046
|
-
return new Promise(async (
|
|
10046
|
+
return new Promise(async (resolve4, reject) => {
|
|
10047
10047
|
const resolveWithCleanup = (r) => {
|
|
10048
|
-
|
|
10048
|
+
resolve4(r);
|
|
10049
10049
|
cancellationStrategy.sender.cleanup(id);
|
|
10050
10050
|
disposable?.dispose();
|
|
10051
10051
|
};
|
|
@@ -10474,10 +10474,10 @@ var require_ril = __commonJS({
|
|
|
10474
10474
|
return api_1.Disposable.create(() => this.stream.off("end", listener));
|
|
10475
10475
|
}
|
|
10476
10476
|
write(data, encoding) {
|
|
10477
|
-
return new Promise((
|
|
10477
|
+
return new Promise((resolve4, reject) => {
|
|
10478
10478
|
const callback = (error2) => {
|
|
10479
10479
|
if (error2 === void 0 || error2 === null) {
|
|
10480
|
-
|
|
10480
|
+
resolve4();
|
|
10481
10481
|
} else {
|
|
10482
10482
|
reject(error2);
|
|
10483
10483
|
}
|
|
@@ -10613,9 +10613,9 @@ var require_main = __commonJS({
|
|
|
10613
10613
|
exports2.createMessageConnection = createMessageConnection2;
|
|
10614
10614
|
var ril_1 = __importDefault(require_ril());
|
|
10615
10615
|
ril_1.default.install();
|
|
10616
|
-
var
|
|
10617
|
-
var
|
|
10618
|
-
var
|
|
10616
|
+
var path6 = __importStar(require("path"));
|
|
10617
|
+
var os3 = __importStar(require("os"));
|
|
10618
|
+
var fs6 = __importStar(require("fs"));
|
|
10619
10619
|
var crypto_1 = require("crypto");
|
|
10620
10620
|
var net_1 = require("net");
|
|
10621
10621
|
var api_1 = require_api();
|
|
@@ -10756,7 +10756,7 @@ var require_main = __commonJS({
|
|
|
10756
10756
|
}
|
|
10757
10757
|
let randomLength = 32;
|
|
10758
10758
|
const fixedLength = "/lsp-.sock".length;
|
|
10759
|
-
const tmpDir =
|
|
10759
|
+
const tmpDir = fs6.realpathSync(XDG_RUNTIME_DIR ?? os3.tmpdir());
|
|
10760
10760
|
const limit = safeIpcPathLengths.get(process.platform);
|
|
10761
10761
|
if (limit !== void 0) {
|
|
10762
10762
|
randomLength = Math.min(limit - tmpDir.length - fixedLength, randomLength);
|
|
@@ -10765,14 +10765,14 @@ var require_main = __commonJS({
|
|
|
10765
10765
|
throw new Error(`Unable to generate a random pipe name with ${randomLength} characters.`);
|
|
10766
10766
|
}
|
|
10767
10767
|
const randomSuffix = (0, crypto_1.randomBytes)(Math.floor(randomLength / 2)).toString("hex");
|
|
10768
|
-
return
|
|
10768
|
+
return path6.join(tmpDir, `lsp-${randomSuffix}.sock`);
|
|
10769
10769
|
}
|
|
10770
10770
|
function createClientPipeTransport(pipeName, encoding = "utf-8") {
|
|
10771
10771
|
let connectResolve;
|
|
10772
|
-
const connected = new Promise((
|
|
10773
|
-
connectResolve =
|
|
10772
|
+
const connected = new Promise((resolve4, _reject) => {
|
|
10773
|
+
connectResolve = resolve4;
|
|
10774
10774
|
});
|
|
10775
|
-
return new Promise((
|
|
10775
|
+
return new Promise((resolve4, reject) => {
|
|
10776
10776
|
const server2 = (0, net_1.createServer)((socket) => {
|
|
10777
10777
|
server2.close();
|
|
10778
10778
|
connectResolve([
|
|
@@ -10783,7 +10783,7 @@ var require_main = __commonJS({
|
|
|
10783
10783
|
server2.on("error", reject);
|
|
10784
10784
|
server2.listen(pipeName, () => {
|
|
10785
10785
|
server2.removeListener("error", reject);
|
|
10786
|
-
|
|
10786
|
+
resolve4({
|
|
10787
10787
|
onConnected: () => {
|
|
10788
10788
|
return connected;
|
|
10789
10789
|
}
|
|
@@ -10800,10 +10800,10 @@ var require_main = __commonJS({
|
|
|
10800
10800
|
}
|
|
10801
10801
|
function createClientSocketTransport(port, encoding = "utf-8") {
|
|
10802
10802
|
let connectResolve;
|
|
10803
|
-
const connected = new Promise((
|
|
10804
|
-
connectResolve =
|
|
10803
|
+
const connected = new Promise((resolve4, _reject) => {
|
|
10804
|
+
connectResolve = resolve4;
|
|
10805
10805
|
});
|
|
10806
|
-
return new Promise((
|
|
10806
|
+
return new Promise((resolve4, reject) => {
|
|
10807
10807
|
const server2 = (0, net_1.createServer)((socket) => {
|
|
10808
10808
|
server2.close();
|
|
10809
10809
|
connectResolve([
|
|
@@ -10820,7 +10820,7 @@ var require_main = __commonJS({
|
|
|
10820
10820
|
return;
|
|
10821
10821
|
}
|
|
10822
10822
|
const boundPort = address.port;
|
|
10823
|
-
|
|
10823
|
+
resolve4({
|
|
10824
10824
|
port: () => boundPort,
|
|
10825
10825
|
onConnected: () => {
|
|
10826
10826
|
return connected;
|
|
@@ -10859,9 +10859,9 @@ var require_main = __commonJS({
|
|
|
10859
10859
|
});
|
|
10860
10860
|
|
|
10861
10861
|
// src/server.ts
|
|
10862
|
-
var
|
|
10863
|
-
var
|
|
10864
|
-
var
|
|
10862
|
+
var fs5 = __toESM(require("node:fs"));
|
|
10863
|
+
var os2 = __toESM(require("node:os"));
|
|
10864
|
+
var path5 = __toESM(require("node:path"));
|
|
10865
10865
|
|
|
10866
10866
|
// ../../node_modules/zod/v3/external.js
|
|
10867
10867
|
var external_exports = {};
|
|
@@ -11341,8 +11341,8 @@ function getErrorMap() {
|
|
|
11341
11341
|
|
|
11342
11342
|
// ../../node_modules/zod/v3/helpers/parseUtil.js
|
|
11343
11343
|
var makeIssue = (params) => {
|
|
11344
|
-
const { data, path:
|
|
11345
|
-
const fullPath = [...
|
|
11344
|
+
const { data, path: path6, errorMaps, issueData } = params;
|
|
11345
|
+
const fullPath = [...path6, ...issueData.path || []];
|
|
11346
11346
|
const fullIssue = {
|
|
11347
11347
|
...issueData,
|
|
11348
11348
|
path: fullPath
|
|
@@ -11458,11 +11458,11 @@ var errorUtil;
|
|
|
11458
11458
|
|
|
11459
11459
|
// ../../node_modules/zod/v3/types.js
|
|
11460
11460
|
var ParseInputLazyPath = class {
|
|
11461
|
-
constructor(parent, value,
|
|
11461
|
+
constructor(parent, value, path6, key) {
|
|
11462
11462
|
this._cachedPath = [];
|
|
11463
11463
|
this.parent = parent;
|
|
11464
11464
|
this.data = value;
|
|
11465
|
-
this._path =
|
|
11465
|
+
this._path = path6;
|
|
11466
11466
|
this._key = key;
|
|
11467
11467
|
}
|
|
11468
11468
|
get path() {
|
|
@@ -15099,10 +15099,10 @@ function assignProp(target, prop, value) {
|
|
|
15099
15099
|
configurable: true
|
|
15100
15100
|
});
|
|
15101
15101
|
}
|
|
15102
|
-
function getElementAtPath(obj,
|
|
15103
|
-
if (!
|
|
15102
|
+
function getElementAtPath(obj, path6) {
|
|
15103
|
+
if (!path6)
|
|
15104
15104
|
return obj;
|
|
15105
|
-
return
|
|
15105
|
+
return path6.reduce((acc, key) => acc?.[key], obj);
|
|
15106
15106
|
}
|
|
15107
15107
|
function promiseAllObject(promisesObj) {
|
|
15108
15108
|
const keys = Object.keys(promisesObj);
|
|
@@ -15422,11 +15422,11 @@ function aborted(x, startIndex = 0) {
|
|
|
15422
15422
|
}
|
|
15423
15423
|
return false;
|
|
15424
15424
|
}
|
|
15425
|
-
function prefixIssues(
|
|
15425
|
+
function prefixIssues(path6, issues) {
|
|
15426
15426
|
return issues.map((iss) => {
|
|
15427
15427
|
var _a;
|
|
15428
15428
|
(_a = iss).path ?? (_a.path = []);
|
|
15429
|
-
iss.path.unshift(
|
|
15429
|
+
iss.path.unshift(path6);
|
|
15430
15430
|
return iss;
|
|
15431
15431
|
});
|
|
15432
15432
|
}
|
|
@@ -18837,11 +18837,11 @@ function normalizeObjectSchema(schema) {
|
|
|
18837
18837
|
}
|
|
18838
18838
|
return void 0;
|
|
18839
18839
|
}
|
|
18840
|
-
function getDotPath(
|
|
18841
|
-
if (
|
|
18840
|
+
function getDotPath(path6) {
|
|
18841
|
+
if (path6.length === 0) {
|
|
18842
18842
|
return "object root";
|
|
18843
18843
|
}
|
|
18844
|
-
return
|
|
18844
|
+
return path6.reduce((acc, seg, index) => {
|
|
18845
18845
|
if (index === 0) {
|
|
18846
18846
|
return String(seg);
|
|
18847
18847
|
}
|
|
@@ -22992,7 +22992,7 @@ var Protocol = class {
|
|
|
22992
22992
|
return;
|
|
22993
22993
|
}
|
|
22994
22994
|
const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
|
|
22995
|
-
await new Promise((
|
|
22995
|
+
await new Promise((resolve4) => setTimeout(resolve4, pollInterval));
|
|
22996
22996
|
options?.signal?.throwIfAborted();
|
|
22997
22997
|
}
|
|
22998
22998
|
} catch (error2) {
|
|
@@ -23009,7 +23009,7 @@ var Protocol = class {
|
|
|
23009
23009
|
*/
|
|
23010
23010
|
request(request, resultSchema, options) {
|
|
23011
23011
|
const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
|
|
23012
|
-
return new Promise((
|
|
23012
|
+
return new Promise((resolve4, reject) => {
|
|
23013
23013
|
const earlyReject = (error2) => {
|
|
23014
23014
|
reject(error2);
|
|
23015
23015
|
};
|
|
@@ -23087,7 +23087,7 @@ var Protocol = class {
|
|
|
23087
23087
|
if (!parseResult.success) {
|
|
23088
23088
|
reject(parseResult.error);
|
|
23089
23089
|
} else {
|
|
23090
|
-
|
|
23090
|
+
resolve4(parseResult.data);
|
|
23091
23091
|
}
|
|
23092
23092
|
} catch (error2) {
|
|
23093
23093
|
reject(error2);
|
|
@@ -23349,12 +23349,12 @@ var Protocol = class {
|
|
|
23349
23349
|
}
|
|
23350
23350
|
} catch {
|
|
23351
23351
|
}
|
|
23352
|
-
return new Promise((
|
|
23352
|
+
return new Promise((resolve4, reject) => {
|
|
23353
23353
|
if (signal.aborted) {
|
|
23354
23354
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
23355
23355
|
return;
|
|
23356
23356
|
}
|
|
23357
|
-
const timeoutId = setTimeout(
|
|
23357
|
+
const timeoutId = setTimeout(resolve4, interval);
|
|
23358
23358
|
signal.addEventListener("abort", () => {
|
|
23359
23359
|
clearTimeout(timeoutId);
|
|
23360
23360
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
@@ -24485,7 +24485,7 @@ var McpServer = class {
|
|
|
24485
24485
|
let task = createTaskResult.task;
|
|
24486
24486
|
const pollInterval = task.pollInterval ?? 5e3;
|
|
24487
24487
|
while (task.status !== "completed" && task.status !== "failed" && task.status !== "cancelled") {
|
|
24488
|
-
await new Promise((
|
|
24488
|
+
await new Promise((resolve4) => setTimeout(resolve4, pollInterval));
|
|
24489
24489
|
const updatedTask = await extra.taskStore.getTask(taskId);
|
|
24490
24490
|
if (!updatedTask) {
|
|
24491
24491
|
throw new McpError(ErrorCode.InternalError, `Task ${taskId} not found during polling`);
|
|
@@ -25149,12 +25149,12 @@ var StdioServerTransport = class {
|
|
|
25149
25149
|
this.onclose?.();
|
|
25150
25150
|
}
|
|
25151
25151
|
send(message) {
|
|
25152
|
-
return new Promise((
|
|
25152
|
+
return new Promise((resolve4) => {
|
|
25153
25153
|
const json = serializeMessage(message);
|
|
25154
25154
|
if (this._stdout.write(json)) {
|
|
25155
|
-
|
|
25155
|
+
resolve4();
|
|
25156
25156
|
} else {
|
|
25157
|
-
this._stdout.once("drain",
|
|
25157
|
+
this._stdout.once("drain", resolve4);
|
|
25158
25158
|
}
|
|
25159
25159
|
});
|
|
25160
25160
|
}
|
|
@@ -25167,8 +25167,44 @@ var path2 = __toESM(require("node:path"));
|
|
|
25167
25167
|
// ../../extensions/redcode/src/exportCache.ts
|
|
25168
25168
|
var fs = __toESM(require("node:fs"));
|
|
25169
25169
|
var path = __toESM(require("node:path"));
|
|
25170
|
+
function gameStamp(gameDir) {
|
|
25171
|
+
if (!gameDir) return "no game";
|
|
25172
|
+
try {
|
|
25173
|
+
const s = fs.statSync(path.join(gameDir, "bin", "x64", "Cyberpunk2077.exe"));
|
|
25174
|
+
return `${path.resolve(gameDir).toLowerCase()}|${s.size}|${Math.round(s.mtimeMs)}`;
|
|
25175
|
+
} catch {
|
|
25176
|
+
return `${path.resolve(gameDir).toLowerCase()}|no exe`;
|
|
25177
|
+
}
|
|
25178
|
+
}
|
|
25179
|
+
function checkGameCache(cacheRoot, stamp) {
|
|
25180
|
+
const dir = path.join(cacheRoot, "game");
|
|
25181
|
+
const file = path.join(dir, "stamp.txt");
|
|
25182
|
+
let old;
|
|
25183
|
+
try {
|
|
25184
|
+
old = fs.readFileSync(file, "utf8").trim();
|
|
25185
|
+
} catch {
|
|
25186
|
+
}
|
|
25187
|
+
if (old === stamp) return stamp;
|
|
25188
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
25189
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
25190
|
+
fs.writeFileSync(file, `${stamp}
|
|
25191
|
+
`);
|
|
25192
|
+
return stamp;
|
|
25193
|
+
}
|
|
25194
|
+
var checkedStamps = /* @__PURE__ */ new Map();
|
|
25195
|
+
async function gameCacheReady(bridge2, cacheRoot) {
|
|
25196
|
+
const info = await bridge2.info();
|
|
25197
|
+
if (!info.gameDir) return void 0;
|
|
25198
|
+
const stamp = gameStamp(info.gameDir);
|
|
25199
|
+
if (checkedStamps.get(cacheRoot) !== stamp) {
|
|
25200
|
+
checkGameCache(cacheRoot, stamp);
|
|
25201
|
+
checkedStamps.set(cacheRoot, stamp);
|
|
25202
|
+
}
|
|
25203
|
+
return stamp;
|
|
25204
|
+
}
|
|
25170
25205
|
async function sourceFile(bridge2, cacheRoot, source) {
|
|
25171
25206
|
if (source.gamePath) {
|
|
25207
|
+
await gameCacheReady(bridge2, cacheRoot);
|
|
25172
25208
|
const archiveDir = path.join(cacheRoot, "game", "archive");
|
|
25173
25209
|
const file2 = path.join(archiveDir, ...source.gamePath.split("\\"));
|
|
25174
25210
|
if (!fs.existsSync(file2)) await bridge2.extract(source.gamePath, file2);
|
|
@@ -25345,11 +25381,11 @@ var BridgeClient = class extends import_node_events.EventEmitter {
|
|
|
25345
25381
|
if (!child || child.exitCode !== null) {
|
|
25346
25382
|
return Promise.resolve();
|
|
25347
25383
|
}
|
|
25348
|
-
return new Promise((
|
|
25349
|
-
const timer = setTimeout(
|
|
25384
|
+
return new Promise((resolve4) => {
|
|
25385
|
+
const timer = setTimeout(resolve4, timeoutMs);
|
|
25350
25386
|
child.once("exit", () => {
|
|
25351
25387
|
clearTimeout(timer);
|
|
25352
|
-
|
|
25388
|
+
resolve4();
|
|
25353
25389
|
});
|
|
25354
25390
|
});
|
|
25355
25391
|
}
|
|
@@ -25376,17 +25412,17 @@ var BridgeClient = class extends import_node_events.EventEmitter {
|
|
|
25376
25412
|
return this.call("archive.search", { query, limit });
|
|
25377
25413
|
}
|
|
25378
25414
|
/** One level of the archive folder tree. An empty path is the root. */
|
|
25379
|
-
tree(
|
|
25380
|
-
return this.call("archive.tree", { path:
|
|
25415
|
+
tree(path6 = "") {
|
|
25416
|
+
return this.call("archive.tree", { path: path6 });
|
|
25381
25417
|
}
|
|
25382
|
-
extract(
|
|
25383
|
-
return this.call("archive.extract", { path:
|
|
25418
|
+
extract(path6, outFile) {
|
|
25419
|
+
return this.call("archive.extract", { path: path6, outFile });
|
|
25384
25420
|
}
|
|
25385
25421
|
extractMany(items) {
|
|
25386
25422
|
return this.call("archive.extractMany", { items });
|
|
25387
25423
|
}
|
|
25388
|
-
toJson(
|
|
25389
|
-
return this.call("cr2w.toJson", outFile ? { path:
|
|
25424
|
+
toJson(path6, outFile) {
|
|
25425
|
+
return this.call("cr2w.toJson", outFile ? { path: path6, outFile } : { path: path6 });
|
|
25390
25426
|
}
|
|
25391
25427
|
/** Converts a CR2W file on disk, for example one in a project, to JSON. */
|
|
25392
25428
|
fileToJson(file, outFile) {
|
|
@@ -25395,8 +25431,8 @@ var BridgeClient = class extends import_node_events.EventEmitter {
|
|
|
25395
25431
|
fromJson(outFile, source) {
|
|
25396
25432
|
return this.call("cr2w.fromJson", { outFile, ...source });
|
|
25397
25433
|
}
|
|
25398
|
-
toPng(
|
|
25399
|
-
return this.call("texture.toPng", { path:
|
|
25434
|
+
toPng(path6, outFile) {
|
|
25435
|
+
return this.call("texture.toPng", { path: path6, outFile });
|
|
25400
25436
|
}
|
|
25401
25437
|
/** Converts a texture file on disk, for example one in a project, to PNG. Needs REDbridge 0.3.0. */
|
|
25402
25438
|
fileToPng(file, outFile) {
|
|
@@ -25415,16 +25451,16 @@ var BridgeClient = class extends import_node_events.EventEmitter {
|
|
|
25415
25451
|
return this.call("archive.extractFromMod", { archivePath, items });
|
|
25416
25452
|
}
|
|
25417
25453
|
/** Lists the resources inside a mod archive on disk. No game needed. Bridge 0.4.0. */
|
|
25418
|
-
openMod(
|
|
25419
|
-
return this.call("archive.openMod", { path:
|
|
25454
|
+
openMod(path6) {
|
|
25455
|
+
return this.call("archive.openMod", { path: path6 });
|
|
25420
25456
|
}
|
|
25421
25457
|
/** Exports a file under archiveDir into rawDir at the same relative path, through WolvenKit's exporter. Bridge 0.7.0. */
|
|
25422
25458
|
exportFile(file, archiveDir, rawDir, options) {
|
|
25423
25459
|
return this.call("project.export", { file, archiveDir, rawDir, options: options ?? null });
|
|
25424
25460
|
}
|
|
25425
25461
|
/** Imports a raw file under rawDir back into archiveDir, through WolvenKit's importer. keep: an existing file keeps its settings. Bridge 0.7.0. */
|
|
25426
|
-
importFile(file, rawDir, archiveDir, keep = true, template) {
|
|
25427
|
-
return this.call("project.import", { file, rawDir, archiveDir, keep, template: template ?? null });
|
|
25462
|
+
importFile(file, rawDir, archiveDir, keep = true, template, texture) {
|
|
25463
|
+
return this.call("project.import", { file, rawDir, archiveDir, keep, template: template ?? null, ...texture !== void 0 ? { texture } : {} });
|
|
25428
25464
|
}
|
|
25429
25465
|
/** The sound event names WolvenKit knows, with their Wwise hashes, which name the groups in an .opusinfo. Bridge 0.7.2. */
|
|
25430
25466
|
soundEvents() {
|
|
@@ -25441,11 +25477,11 @@ var BridgeClient = class extends import_node_events.EventEmitter {
|
|
|
25441
25477
|
tweakRecords(query) {
|
|
25442
25478
|
return this.call("tweakdb.records", { ...query });
|
|
25443
25479
|
}
|
|
25444
|
-
tweakRecord(
|
|
25445
|
-
return this.call("tweakdb.record", { path:
|
|
25480
|
+
tweakRecord(path6, resolveLocKeys = false, language = "en-us") {
|
|
25481
|
+
return this.call("tweakdb.record", { path: path6, resolveLocKeys, language });
|
|
25446
25482
|
}
|
|
25447
|
-
tweakFlat(
|
|
25448
|
-
return this.call("tweakdb.flat", { path:
|
|
25483
|
+
tweakFlat(path6, resolveLocKeys = false, language = "en-us") {
|
|
25484
|
+
return this.call("tweakdb.flat", { path: path6, resolveLocKeys, language });
|
|
25449
25485
|
}
|
|
25450
25486
|
tweakSearch(text, type, limit = 200) {
|
|
25451
25487
|
return this.call("tweakdb.search", { text, type: type ?? null, limit });
|
|
@@ -25458,8 +25494,8 @@ var BridgeClient = class extends import_node_events.EventEmitter {
|
|
|
25458
25494
|
* The item records whose look shows a mesh (a game path), and every .app look that shows it. With
|
|
25459
25495
|
* appearance, only the looks that show that mesh appearance. Loads TweakDB on the first call. Bridge 0.11.0.
|
|
25460
25496
|
*/
|
|
25461
|
-
meshItems(
|
|
25462
|
-
return this.call("mesh.items", { path:
|
|
25497
|
+
meshItems(path6, appearance, language = "en-us") {
|
|
25498
|
+
return this.call("mesh.items", { path: path6, appearance: appearance ?? null, language });
|
|
25463
25499
|
}
|
|
25464
25500
|
// ---- on-screen texts (bridge 0.10.0) ------------------------------------------------
|
|
25465
25501
|
/** The languages with on-screen texts in the game, and whether the base game and Phantom Liberty have them. */
|
|
@@ -25497,16 +25533,16 @@ var BridgeClient = class extends import_node_events.EventEmitter {
|
|
|
25497
25533
|
return this.call("cr2w.documents");
|
|
25498
25534
|
}
|
|
25499
25535
|
/** One node by dotted path from the root chunk; an empty path is the root. Arrays page from offset. */
|
|
25500
|
-
getNode(doc,
|
|
25501
|
-
return this.call("cr2w.getNode", { doc, path:
|
|
25536
|
+
getNode(doc, path6 = "", offset = 0, limit = 200) {
|
|
25537
|
+
return this.call("cr2w.getNode", { doc, path: path6, offset, limit });
|
|
25502
25538
|
}
|
|
25503
25539
|
/** Several nodes in one call; a path that fails gives a node of kind "error" whose type holds the message. */
|
|
25504
25540
|
getNodes(doc, paths, limit = 200) {
|
|
25505
25541
|
return this.call("cr2w.getNodes", { doc, paths, limit });
|
|
25506
25542
|
}
|
|
25507
25543
|
/** Leaves whose text or property name contains the query, below a path. Total counts all; hits holds the first limit. */
|
|
25508
|
-
searchDocument(doc, query,
|
|
25509
|
-
return this.call("cr2w.search", { doc, query, path:
|
|
25544
|
+
searchDocument(doc, query, path6 = "", limit = 200) {
|
|
25545
|
+
return this.call("cr2w.search", { doc, query, path: path6, limit });
|
|
25510
25546
|
}
|
|
25511
25547
|
/** Applies the operations atomically and returns the inverse, for undo. */
|
|
25512
25548
|
patch(doc, ops) {
|
|
@@ -25532,8 +25568,359 @@ var BridgeClient = class extends import_node_events.EventEmitter {
|
|
|
25532
25568
|
}
|
|
25533
25569
|
};
|
|
25534
25570
|
|
|
25571
|
+
// ../../extensions/redcode/src/library.ts
|
|
25572
|
+
var import_node_child_process2 = require("node:child_process");
|
|
25573
|
+
var import_node_crypto = require("node:crypto");
|
|
25574
|
+
var fs3 = __toESM(require("node:fs"));
|
|
25575
|
+
var os = __toESM(require("node:os"));
|
|
25576
|
+
var path3 = __toESM(require("node:path"));
|
|
25577
|
+
function libraryConfigFile() {
|
|
25578
|
+
return path3.join(process.env.APPDATA ?? path3.join(os.homedir(), "AppData", "Roaming"), "EDGE", "library.json");
|
|
25579
|
+
}
|
|
25580
|
+
function readLibraryRoot(file = libraryConfigFile()) {
|
|
25581
|
+
try {
|
|
25582
|
+
const root = JSON.parse(fs3.readFileSync(file, "utf8")).root;
|
|
25583
|
+
return typeof root === "string" && root.trim() ? root.trim() : void 0;
|
|
25584
|
+
} catch {
|
|
25585
|
+
return void 0;
|
|
25586
|
+
}
|
|
25587
|
+
}
|
|
25588
|
+
function writeLibraryRoot(root, file = libraryConfigFile()) {
|
|
25589
|
+
root = path3.resolve(root);
|
|
25590
|
+
fs3.mkdirSync(root, { recursive: true });
|
|
25591
|
+
let data = {};
|
|
25592
|
+
try {
|
|
25593
|
+
data = JSON.parse(fs3.readFileSync(file, "utf8"));
|
|
25594
|
+
} catch {
|
|
25595
|
+
}
|
|
25596
|
+
fs3.mkdirSync(path3.dirname(file), { recursive: true });
|
|
25597
|
+
fs3.writeFileSync(file, JSON.stringify({ ...data, root }, null, 2));
|
|
25598
|
+
return root;
|
|
25599
|
+
}
|
|
25600
|
+
function documentsFolder() {
|
|
25601
|
+
const fallback = path3.join(os.homedir(), "Documents");
|
|
25602
|
+
if (process.platform !== "win32") return fallback;
|
|
25603
|
+
try {
|
|
25604
|
+
const out = (0, import_node_child_process2.execFileSync)("reg", ["query", "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\User Shell Folders", "/v", "Personal"], { encoding: "utf8", windowsHide: true, timeout: 5e3 });
|
|
25605
|
+
const m = /Personal\s+REG_(?:EXPAND_)?SZ\s+(.+)/.exec(out);
|
|
25606
|
+
if (!m) return fallback;
|
|
25607
|
+
return m[1].trim().replace(/%([^%]+)%/g, (all, name) => process.env[name] ?? all);
|
|
25608
|
+
} catch {
|
|
25609
|
+
return fallback;
|
|
25610
|
+
}
|
|
25611
|
+
}
|
|
25612
|
+
function suggestedLibraryRoot() {
|
|
25613
|
+
return path3.join(documentsFolder(), "EDGE Library");
|
|
25614
|
+
}
|
|
25615
|
+
var ItemEnding = /\.(asset|bake)$/i;
|
|
25616
|
+
function libraryFolders(root, cap = 2e3) {
|
|
25617
|
+
const found = [];
|
|
25618
|
+
const walk = (dir, rel, depth) => {
|
|
25619
|
+
let entries;
|
|
25620
|
+
try {
|
|
25621
|
+
entries = fs3.readdirSync(dir, { withFileTypes: true });
|
|
25622
|
+
} catch {
|
|
25623
|
+
return;
|
|
25624
|
+
}
|
|
25625
|
+
for (const e of entries) {
|
|
25626
|
+
if (found.length >= cap) return;
|
|
25627
|
+
if (!e.isDirectory() || e.name.startsWith(".") || ItemEnding.test(e.name)) continue;
|
|
25628
|
+
const child = rel ? `${rel}/${e.name}` : e.name;
|
|
25629
|
+
found.push(child);
|
|
25630
|
+
if (depth < 16) walk(path3.join(dir, e.name), child, depth + 1);
|
|
25631
|
+
}
|
|
25632
|
+
};
|
|
25633
|
+
walk(root, "", 0);
|
|
25634
|
+
return found.sort((a, b) => a.localeCompare(b, void 0, { sensitivity: "base" }));
|
|
25635
|
+
}
|
|
25636
|
+
function safeFolder(folder) {
|
|
25637
|
+
const parts = (folder ?? "").split(/[\\/]+/).filter((p) => p !== "");
|
|
25638
|
+
for (const p of parts) {
|
|
25639
|
+
if (p === "." || p === "..") throw new Error(`A Library folder cannot go through "${p}": ${folder}`);
|
|
25640
|
+
if (/[<>:"|?*\u0000-\u001f]/.test(p) || /[. ]$/.test(p)) throw new Error(`Not a folder name: "${p}"`);
|
|
25641
|
+
if (ItemEnding.test(p)) throw new Error(`"${p}" is a Library item, not a folder.`);
|
|
25642
|
+
}
|
|
25643
|
+
return parts.join("/");
|
|
25644
|
+
}
|
|
25645
|
+
function isInside(child, parent) {
|
|
25646
|
+
const rel = path3.relative(parent, child);
|
|
25647
|
+
return rel === "" || !rel.startsWith("..") && !path3.isAbsolute(rel);
|
|
25648
|
+
}
|
|
25649
|
+
var versions = /* @__PURE__ */ new Map();
|
|
25650
|
+
function gameVersionOf(gameDir) {
|
|
25651
|
+
if (!gameDir) return Promise.resolve(void 0);
|
|
25652
|
+
let v = versions.get(gameDir);
|
|
25653
|
+
if (!v) {
|
|
25654
|
+
const exe = path3.join(gameDir, "bin", "x64", "Cyberpunk2077.exe");
|
|
25655
|
+
v = new Promise((resolve4) => {
|
|
25656
|
+
(0, import_node_child_process2.execFile)(
|
|
25657
|
+
"powershell.exe",
|
|
25658
|
+
["-NoProfile", "-NonInteractive", "-Command", `(Get-Item -LiteralPath '${exe.replace(/'/g, "''")}').VersionInfo.ProductVersion`],
|
|
25659
|
+
{ windowsHide: true, timeout: 15e3 },
|
|
25660
|
+
(e, out) => resolve4(e ? void 0 : out.trim() || void 0)
|
|
25661
|
+
);
|
|
25662
|
+
});
|
|
25663
|
+
versions.set(gameDir, v);
|
|
25664
|
+
}
|
|
25665
|
+
return v;
|
|
25666
|
+
}
|
|
25667
|
+
var TakenTypes = /* @__PURE__ */ new Set(["mesh", "ent", "app", "mi", "mlsetup", "mlmask", "xbm"]);
|
|
25668
|
+
var ReadTypes = /* @__PURE__ */ new Set(["mesh", "ent", "app", "mi", "mlsetup"]);
|
|
25669
|
+
var SharedFolders = /^(engine|(base|ep1)\\(surfaces|materials|fx))\\/i;
|
|
25670
|
+
var typeOf = (p) => path3.win32.extname(p).slice(1).toLowerCase();
|
|
25671
|
+
function assetTakes(gamePath) {
|
|
25672
|
+
return TakenTypes.has(typeOf(gamePath)) && !SharedFolders.test(gamePath);
|
|
25673
|
+
}
|
|
25674
|
+
function safeGamePath(p) {
|
|
25675
|
+
const parts = p.split(/[\\/]+/);
|
|
25676
|
+
if (!parts.length || parts.some((s) => s === "" || s === "." || s === ".." || /[<>:"|?*\u0000-\u001f]/.test(s))) return void 0;
|
|
25677
|
+
return parts.join("\\");
|
|
25678
|
+
}
|
|
25679
|
+
function referencesIn(json) {
|
|
25680
|
+
const named = /* @__PURE__ */ new Set();
|
|
25681
|
+
let unnamed = 0;
|
|
25682
|
+
const walk = (v) => {
|
|
25683
|
+
if (Array.isArray(v)) {
|
|
25684
|
+
for (const x of v) walk(x);
|
|
25685
|
+
} else if (v && typeof v === "object") {
|
|
25686
|
+
const o = v;
|
|
25687
|
+
if (o.$type === "ResourcePath") {
|
|
25688
|
+
if (o.$storage === "string" && typeof o.$value === "string" && o.$value) named.add(o.$value);
|
|
25689
|
+
else if (o.$value && o.$value !== "0") unnamed++;
|
|
25690
|
+
return;
|
|
25691
|
+
}
|
|
25692
|
+
for (const k of Object.keys(o)) walk(o[k]);
|
|
25693
|
+
}
|
|
25694
|
+
};
|
|
25695
|
+
walk(json);
|
|
25696
|
+
return { named, unnamed };
|
|
25697
|
+
}
|
|
25698
|
+
function kindOf(gamePath) {
|
|
25699
|
+
const kind = typeOf(gamePath);
|
|
25700
|
+
if (!/^[a-z0-9_]+$/.test(kind)) throw new Error(`${gamePath} has no file type, so it cannot be a Library asset.`);
|
|
25701
|
+
return kind;
|
|
25702
|
+
}
|
|
25703
|
+
var projectFile = (dir, gamePath) => path3.join(dir, "source", "archive", ...gamePath.split("\\"));
|
|
25704
|
+
async function planAsset(b, source) {
|
|
25705
|
+
const main2 = safeGamePath(source.path);
|
|
25706
|
+
if (!main2) throw new Error(`Not a game path: ${source.path}`);
|
|
25707
|
+
kindOf(main2);
|
|
25708
|
+
const plan = { main: main2, take: [], stay: [], unread: [], badPaths: [], unnamed: 0 };
|
|
25709
|
+
const seen = /* @__PURE__ */ new Set();
|
|
25710
|
+
const queue = [main2];
|
|
25711
|
+
while (queue.length) {
|
|
25712
|
+
const p = queue.shift();
|
|
25713
|
+
const key = p.toLowerCase();
|
|
25714
|
+
if (seen.has(key)) continue;
|
|
25715
|
+
seen.add(key);
|
|
25716
|
+
if (p !== main2 && !assetTakes(p)) {
|
|
25717
|
+
plan.stay.push(p);
|
|
25718
|
+
continue;
|
|
25719
|
+
}
|
|
25720
|
+
plan.take.push(p);
|
|
25721
|
+
if (!ReadTypes.has(typeOf(p))) continue;
|
|
25722
|
+
let text;
|
|
25723
|
+
try {
|
|
25724
|
+
const own = source.project && projectFile(source.project.dir, p);
|
|
25725
|
+
text = (own && fs3.existsSync(own) ? await b.fileToJson(own) : await b.toJson(p)).json;
|
|
25726
|
+
} catch (e) {
|
|
25727
|
+
if (p === main2) throw e;
|
|
25728
|
+
plan.unread.push(p);
|
|
25729
|
+
continue;
|
|
25730
|
+
}
|
|
25731
|
+
if (text === null) {
|
|
25732
|
+
plan.unread.push(p);
|
|
25733
|
+
continue;
|
|
25734
|
+
}
|
|
25735
|
+
const refs = referencesIn(JSON.parse(text));
|
|
25736
|
+
plan.unnamed += refs.unnamed;
|
|
25737
|
+
for (const r of refs.named) {
|
|
25738
|
+
const safe = safeGamePath(r);
|
|
25739
|
+
if (safe) queue.push(safe);
|
|
25740
|
+
else plan.badPaths.push(r);
|
|
25741
|
+
}
|
|
25742
|
+
}
|
|
25743
|
+
plan.stay.sort();
|
|
25744
|
+
return plan;
|
|
25745
|
+
}
|
|
25746
|
+
var ItemExists = class extends Error {
|
|
25747
|
+
constructor(item) {
|
|
25748
|
+
super(`${path3.basename(item)} is already in the Library: ${item}`);
|
|
25749
|
+
this.item = item;
|
|
25750
|
+
}
|
|
25751
|
+
item;
|
|
25752
|
+
};
|
|
25753
|
+
function itemFolder(root, folder, main2, gamePathFolders) {
|
|
25754
|
+
const gameFolders = gamePathFolders ? main2.split("\\").slice(0, -1) : [];
|
|
25755
|
+
return path3.join(root, ...safeFolder(folder).split("/").filter(Boolean), ...gameFolders, itemName(main2));
|
|
25756
|
+
}
|
|
25757
|
+
var stemOf = (main2) => path3.win32.basename(main2, path3.win32.extname(main2));
|
|
25758
|
+
function itemName(main2) {
|
|
25759
|
+
return `${stemOf(main2).toLowerCase()}.${kindOf(main2)}.asset`;
|
|
25760
|
+
}
|
|
25761
|
+
function freeFolder(wanted) {
|
|
25762
|
+
const parent = path3.dirname(wanted);
|
|
25763
|
+
const m = /^(.*)(\.[^.]+\.asset)$/.exec(path3.basename(wanted));
|
|
25764
|
+
for (let n = 2; ; n++) {
|
|
25765
|
+
const next = path3.join(parent, `${m[1]}_${n}${m[2]}`);
|
|
25766
|
+
if (!fs3.existsSync(next)) return next;
|
|
25767
|
+
}
|
|
25768
|
+
}
|
|
25769
|
+
function readAssetJson(item) {
|
|
25770
|
+
try {
|
|
25771
|
+
return JSON.parse(fs3.readFileSync(path3.join(item, "asset.json"), "utf8"));
|
|
25772
|
+
} catch {
|
|
25773
|
+
return {};
|
|
25774
|
+
}
|
|
25775
|
+
}
|
|
25776
|
+
function filesUnder(dir) {
|
|
25777
|
+
return fs3.readdirSync(dir, { withFileTypes: true }).flatMap((e) => e.isDirectory() ? filesUnder(path3.join(dir, e.name)) : [path3.join(dir, e.name)]);
|
|
25778
|
+
}
|
|
25779
|
+
async function renameFolder(from, to, waits = [50, 100, 200, 400, 800, 1600, 3200, 6400]) {
|
|
25780
|
+
for (let i = 0; ; i++) {
|
|
25781
|
+
try {
|
|
25782
|
+
fs3.renameSync(from, to);
|
|
25783
|
+
return;
|
|
25784
|
+
} catch (e) {
|
|
25785
|
+
const code = e.code;
|
|
25786
|
+
if (code !== "EPERM" && code !== "EACCES" && code !== "EBUSY") throw e;
|
|
25787
|
+
if (i >= waits.length) throw new Error(`Could not move ${from} to ${to}: something has a file in it open, such as an antivirus or the search indexer. Try again in a moment. (${code})`);
|
|
25788
|
+
await new Promise((resolve4) => setTimeout(resolve4, waits[i]));
|
|
25789
|
+
}
|
|
25790
|
+
}
|
|
25791
|
+
}
|
|
25792
|
+
async function writeAsset(b, source, plan, opts) {
|
|
25793
|
+
const root = path3.resolve(opts.root);
|
|
25794
|
+
if (opts.gameDir && isInside(root, opts.gameDir)) throw new Error(`The Library must not be inside the game's folder: ${root}`);
|
|
25795
|
+
let item = itemFolder(root, opts.folder, plan.main, opts.gamePathFolders);
|
|
25796
|
+
const had = fs3.existsSync(item);
|
|
25797
|
+
if (had && (opts.ifExists ?? "stop") === "stop") throw new ItemExists(item);
|
|
25798
|
+
if (had && opts.ifExists === "another") item = freeFolder(item);
|
|
25799
|
+
const replacing = had && opts.ifExists === "replace" ? readAssetJson(item) : void 0;
|
|
25800
|
+
const parent = path3.dirname(item);
|
|
25801
|
+
fs3.mkdirSync(parent, { recursive: true });
|
|
25802
|
+
const tag = (0, import_node_crypto.randomBytes)(6).toString("hex");
|
|
25803
|
+
const building = path3.join(parent, `.redcode-add-${tag}`);
|
|
25804
|
+
const archive = path3.join(building, "source", "archive");
|
|
25805
|
+
try {
|
|
25806
|
+
fs3.mkdirSync(archive, { recursive: true });
|
|
25807
|
+
const out = (p) => path3.join(archive, ...p.split("\\"));
|
|
25808
|
+
const fromGame = [];
|
|
25809
|
+
let files = 0;
|
|
25810
|
+
let bytes = 0;
|
|
25811
|
+
for (const p of plan.take) {
|
|
25812
|
+
const own = source.project && projectFile(source.project.dir, p);
|
|
25813
|
+
if (own && fs3.existsSync(own)) {
|
|
25814
|
+
fs3.mkdirSync(path3.dirname(out(p)), { recursive: true });
|
|
25815
|
+
fs3.copyFileSync(own, out(p));
|
|
25816
|
+
files++;
|
|
25817
|
+
bytes += fs3.statSync(own).size;
|
|
25818
|
+
} else {
|
|
25819
|
+
fromGame.push(p);
|
|
25820
|
+
}
|
|
25821
|
+
}
|
|
25822
|
+
let missing = [];
|
|
25823
|
+
if (fromGame.length) {
|
|
25824
|
+
const got = await b.extractMany(fromGame.map((p) => ({ path: p, outFile: out(p) })));
|
|
25825
|
+
files += got.extracted;
|
|
25826
|
+
bytes += got.bytes;
|
|
25827
|
+
missing = got.failed;
|
|
25828
|
+
}
|
|
25829
|
+
if (missing.some((m) => m.toLowerCase() === plan.main.toLowerCase())) throw new Error(`Could not get ${plan.main} from the game.`);
|
|
25830
|
+
const stem = stemOf(plan.main);
|
|
25831
|
+
const inGame = !source.project || !fs3.existsSync(projectFile(source.project.dir, plan.main)) || await existsInGame(b, plan.main);
|
|
25832
|
+
const entry = { tool: opts.tool, did: replacing ? source.project ? `Replaced from project ${source.project.name}` : "Replaced from the game" : source.project ? `Added from project ${source.project.name}` : "Added from the game", date: (/* @__PURE__ */ new Date()).toISOString() };
|
|
25833
|
+
const asset = {
|
|
25834
|
+
format: 1,
|
|
25835
|
+
id: replacing?.id ?? (0, import_node_crypto.randomBytes)(8).toString("hex"),
|
|
25836
|
+
name: replacing?.name ?? stem,
|
|
25837
|
+
kind: kindOf(plan.main),
|
|
25838
|
+
main: plan.main,
|
|
25839
|
+
version: (replacing?.version ?? 0) + 1,
|
|
25840
|
+
from: source.project ? { project: source.project.name, path: plan.main } : { game: plan.main, gameVersion: opts.gameVersion ?? null },
|
|
25841
|
+
// The asset's own terms, in the shape EDGEfab's assets have when they are not the game's.
|
|
25842
|
+
license: inGame ? "game" : { source: "own", note: "From a REDcode project: record its source and terms before releasing it." },
|
|
25843
|
+
history: [...replacing?.history ?? [], entry]
|
|
25844
|
+
};
|
|
25845
|
+
fs3.writeFileSync(path3.join(building, "asset.json"), JSON.stringify(asset, null, 2) + "\n");
|
|
25846
|
+
const longPaths = filesUnder(building).filter((f) => item.length + f.length - building.length > 259).length;
|
|
25847
|
+
if (replacing) {
|
|
25848
|
+
const old = path3.join(parent, `.redcode-old-${tag}`);
|
|
25849
|
+
await renameFolder(item, old);
|
|
25850
|
+
try {
|
|
25851
|
+
await renameFolder(building, item);
|
|
25852
|
+
} catch (e) {
|
|
25853
|
+
await renameFolder(old, item);
|
|
25854
|
+
throw e;
|
|
25855
|
+
}
|
|
25856
|
+
fs3.rmSync(old, { recursive: true, force: true, maxRetries: 5, retryDelay: 200 });
|
|
25857
|
+
} else {
|
|
25858
|
+
await renameFolder(building, item);
|
|
25859
|
+
}
|
|
25860
|
+
const relative3 = path3.relative(root, item).split(path3.sep).join("/");
|
|
25861
|
+
return { item, relative: relative3, id: asset.id, kind: asset.kind, main: plan.main, version: asset.version, files, bytes, stay: plan.stay, missing, unread: plan.unread, badPaths: plan.badPaths, unnamed: plan.unnamed, longPaths };
|
|
25862
|
+
} finally {
|
|
25863
|
+
fs3.rmSync(building, { recursive: true, force: true, maxRetries: 5, retryDelay: 200 });
|
|
25864
|
+
}
|
|
25865
|
+
}
|
|
25866
|
+
async function existsInGame(b, gamePath) {
|
|
25867
|
+
return b.toJson(gamePath).then(
|
|
25868
|
+
() => true,
|
|
25869
|
+
() => false
|
|
25870
|
+
);
|
|
25871
|
+
}
|
|
25872
|
+
|
|
25873
|
+
// ../../extensions/redcode/src/project.ts
|
|
25874
|
+
var fs4 = __toESM(require("node:fs"));
|
|
25875
|
+
var path4 = __toESM(require("node:path"));
|
|
25876
|
+
var ProjectFolders = {
|
|
25877
|
+
archive: path4.join("source", "archive"),
|
|
25878
|
+
raw: path4.join("source", "raw"),
|
|
25879
|
+
resources: path4.join("source", "resources"),
|
|
25880
|
+
/** REDmod custom sounds, named in source/resources/info.json. Made by the tool that writes the first one, not by a new project. */
|
|
25881
|
+
customSounds: path4.join("source", "customSounds"),
|
|
25882
|
+
/** EDGEbuilder's scenes (<name>.scene.json, REDcode issue #2). Made by EDGEbuilder; not packed. */
|
|
25883
|
+
scenes: path4.join("source", "scenes"),
|
|
25884
|
+
packed: "packed"
|
|
25885
|
+
};
|
|
25886
|
+
function findProject(dir) {
|
|
25887
|
+
let entries;
|
|
25888
|
+
try {
|
|
25889
|
+
entries = fs4.readdirSync(dir);
|
|
25890
|
+
} catch {
|
|
25891
|
+
return void 0;
|
|
25892
|
+
}
|
|
25893
|
+
const file = entries.find((e) => e.toLowerCase().endsWith(".cpmodproj"));
|
|
25894
|
+
return file ? readProject(path4.join(dir, file)) : void 0;
|
|
25895
|
+
}
|
|
25896
|
+
function readProject(file) {
|
|
25897
|
+
const xml = fs4.readFileSync(file, "utf8");
|
|
25898
|
+
const get = (tag) => {
|
|
25899
|
+
const m = xml.match(new RegExp(`<${tag}>([\\s\\S]*?)</${tag}>`));
|
|
25900
|
+
return m ? unescapeXml(m[1].trim()) : "";
|
|
25901
|
+
};
|
|
25902
|
+
const name = get("Name") || path4.basename(file, path4.extname(file));
|
|
25903
|
+
return {
|
|
25904
|
+
file,
|
|
25905
|
+
dir: path4.dirname(file),
|
|
25906
|
+
name,
|
|
25907
|
+
modName: get("ModName") || name,
|
|
25908
|
+
author: get("Author"),
|
|
25909
|
+
email: get("Email"),
|
|
25910
|
+
description: get("Description"),
|
|
25911
|
+
version: get("Version") || "1.0.0"
|
|
25912
|
+
};
|
|
25913
|
+
}
|
|
25914
|
+
function gamePathOf(project, file) {
|
|
25915
|
+
const root = path4.join(project.dir, ProjectFolders.archive) + path4.sep;
|
|
25916
|
+
return file.startsWith(root) ? file.slice(root.length).split(path4.sep).join("\\") : void 0;
|
|
25917
|
+
}
|
|
25918
|
+
function unescapeXml(s) {
|
|
25919
|
+
return s.replace(/"/g, '"').replace(/>/g, ">").replace(/</g, "<").replace(/&/g, "&");
|
|
25920
|
+
}
|
|
25921
|
+
|
|
25535
25922
|
// src/server.ts
|
|
25536
|
-
var Version = "0.
|
|
25923
|
+
var Version = "0.5.0";
|
|
25537
25924
|
function arg(name) {
|
|
25538
25925
|
const i = process.argv.indexOf(name);
|
|
25539
25926
|
return i >= 0 ? process.argv[i + 1] || void 0 : void 0;
|
|
@@ -25542,11 +25929,11 @@ function env(name) {
|
|
|
25542
25929
|
return process.env[name]?.trim() || void 0;
|
|
25543
25930
|
}
|
|
25544
25931
|
function vscodeSetting(key) {
|
|
25545
|
-
const base = process.platform === "win32" ? process.env.APPDATA : process.platform === "darwin" ?
|
|
25932
|
+
const base = process.platform === "win32" ? process.env.APPDATA : process.platform === "darwin" ? path5.join(process.env.HOME ?? "", "Library/Application Support") : path5.join(process.env.HOME ?? "", ".config");
|
|
25546
25933
|
if (!base) return void 0;
|
|
25547
|
-
const file =
|
|
25934
|
+
const file = path5.join(base, "Code", "User", "settings.json");
|
|
25548
25935
|
try {
|
|
25549
|
-
const text =
|
|
25936
|
+
const text = fs5.readFileSync(file, "utf8");
|
|
25550
25937
|
const match = text.match(new RegExp(`"${key.replace(/\./g, "\\.")}"\\s*:\\s*"((?:[^"\\\\]|\\\\.)*)"`));
|
|
25551
25938
|
return match ? JSON.parse(`"${match[1]}"`) || void 0 : void 0;
|
|
25552
25939
|
} catch {
|
|
@@ -25555,8 +25942,8 @@ function vscodeSetting(key) {
|
|
|
25555
25942
|
}
|
|
25556
25943
|
function resolveBridgeExe() {
|
|
25557
25944
|
let exe = arg("--bridge") ?? env("REDBRIDGE_EXE") ?? vscodeSetting("redcode.bridgePath") ?? "REDbridge.exe";
|
|
25558
|
-
if (exe &&
|
|
25559
|
-
exe =
|
|
25945
|
+
if (exe && fs5.existsSync(exe) && fs5.statSync(exe).isDirectory()) {
|
|
25946
|
+
exe = path5.join(exe, "REDbridge.exe");
|
|
25560
25947
|
}
|
|
25561
25948
|
return exe;
|
|
25562
25949
|
}
|
|
@@ -25688,8 +26075,47 @@ tool("project_import", "Import a raw file under a project's source/raw back into
|
|
|
25688
26075
|
rawDir: external_exports.string().describe("The project's source/raw folder."),
|
|
25689
26076
|
archiveDir: external_exports.string().describe("The project's source/archive folder."),
|
|
25690
26077
|
keep: external_exports.boolean().optional().describe("Keep the replaced file's settings; default true."),
|
|
25691
|
-
template: external_exports.string().optional().describe("For a GLB with no mesh to import over: a game path (base\\...mesh) or a .mesh file to start the new mesh from.")
|
|
25692
|
-
|
|
26078
|
+
template: external_exports.string().optional().describe("For a GLB with no mesh to import over: a game path (base\\...mesh) or a .mesh file to start the new mesh from."),
|
|
26079
|
+
texture: external_exports.union([
|
|
26080
|
+
external_exports.string(),
|
|
26081
|
+
external_exports.object({
|
|
26082
|
+
group: external_exports.string().optional(),
|
|
26083
|
+
compression: external_exports.string().optional(),
|
|
26084
|
+
rawFormat: external_exports.string().optional(),
|
|
26085
|
+
isGamma: external_exports.boolean().optional(),
|
|
26086
|
+
hasMipchain: external_exports.boolean().optional(),
|
|
26087
|
+
isStreamable: external_exports.boolean().optional()
|
|
26088
|
+
})
|
|
26089
|
+
]).optional().describe("For an image import: the .xbm's setup. A preset (color, normal, grayscale), a texture group name (TEXG_Generic_UI), or { group, compression, rawFormat, isGamma, hasMipchain, isStreamable } with the game's enum names, the rest from the group's defaults. Wins over the existing texture's setup; the answer's texture is the setup read back. Bridge 0.12.0.")
|
|
26090
|
+
}, (a, b) => b.importFile(a.file, a.rawDir, a.archiveDir, a.keep ?? true, a.template, a.texture));
|
|
26091
|
+
tool("library_add", "Add a game file or a project's file to the EDGE Library, the modder's store shared by every EDGE tool, as one asset item: <name>.<kind>.asset with asset.json and source/archive holding the file and the files it needs (a mesh's materials and textures; an entity's appearances and meshes), at their game paths. Game-wide files (base materials, templates, surfaces, effects, rigs) stay in the game and are referred to. Returns the item's folder, id, version, the files copied, those that stay, and those it names that are not in the game (the game's own files name some such). Writes outside the game, in the modder's Library only. With no Library yet, it answers where one would go: ask the modder, then call again with root. With no folder, it answers the modder's folders: ask which, or pass the folder, since the folders are theirs and tools never pick one on their own.", {
|
|
26092
|
+
path: external_exports.string().describe("A game path (base\\...\\comfy_rocker.mesh), or the full path of a file under a WolvenKit project's source/archive, whose project copies then win over the game's."),
|
|
26093
|
+
folder: external_exports.string().optional().describe("A folder under the Library's root, with /, for example Production/Meshes; made if missing. An empty string is the root itself."),
|
|
26094
|
+
gamePathFolders: external_exports.boolean().optional().describe("Puts the item in its game path's folders under the folder, each made if missing: <folder>/base/.../comfy_rocker.mesh.asset, so the Library browses like the game's files. The modder's choice."),
|
|
26095
|
+
root: external_exports.string().optional().describe("Only when there is no Library yet: where to make it, written to %APPDATA%\\EDGE\\library.json for every EDGE tool. Ask the modder first."),
|
|
26096
|
+
ifExists: external_exports.enum(["stop", "replace", "another"]).optional().describe("When the item is there already: stop (default), another (a second item beside it), or replace (a new copy, version raised, id kept; anything changed in the item since is lost, so ask the modder first)."),
|
|
26097
|
+
allowLarge: external_exports.boolean().optional().describe("Copy more than 500 files. An entity's walk can reach that; check with the modder first.")
|
|
26098
|
+
}, async (a, b) => {
|
|
26099
|
+
const known = readLibraryRoot();
|
|
26100
|
+
if (a.root && known) throw new Error(`The EDGE Library is already at ${known}. Leave root out; moving it is Relocate, in EDGEbuilder.`);
|
|
26101
|
+
if (!a.root && !known) return { needsRoot: true, suggested: suggestedLibraryRoot(), note: "No EDGE Library yet. Ask the modder where it should go, then call again with root." };
|
|
26102
|
+
const info = await b.info();
|
|
26103
|
+
if (a.root && info.gameDir && isInside(path5.resolve(a.root), info.gameDir)) throw new Error("The EDGE Library cannot be inside the game's folder.");
|
|
26104
|
+
const root = known ?? writeLibraryRoot(a.root);
|
|
26105
|
+
if (a.folder === void 0) return { needsFolder: true, root, folders: libraryFolders(root), note: "Ask the modder which folder, or for a new one, then call again with folder (an empty string is the root)." };
|
|
26106
|
+
const source = { path: a.path };
|
|
26107
|
+
if (path5.isAbsolute(a.path)) {
|
|
26108
|
+
const m = /^(.*?)[\\/]source[\\/]archive[\\/]/i.exec(a.path);
|
|
26109
|
+
const project = m ? findProject(m[1]) : void 0;
|
|
26110
|
+
const gamePath = project && gamePathOf(project, path5.normalize(a.path));
|
|
26111
|
+
if (!project || !gamePath) throw new Error(`${a.path} is not under a WolvenKit project's source/archive.`);
|
|
26112
|
+
source.path = gamePath;
|
|
26113
|
+
source.project = { name: project.name, dir: project.dir };
|
|
26114
|
+
}
|
|
26115
|
+
const plan = await planAsset(b, source);
|
|
26116
|
+
if (plan.take.length > 500 && !a.allowLarge) throw new Error(`${plan.main} needs ${plan.take.length} files. Check with the modder, then pass allowLarge.`);
|
|
26117
|
+
return writeAsset(b, source, plan, { root, folder: a.folder, gamePathFolders: a.gamePathFolders, ifExists: a.ifExists, tool: `redcode-mcp ${Version}`, gameVersion: await gameVersionOf(info.gameDir), gameDir: info.gameDir });
|
|
26118
|
+
});
|
|
25693
26119
|
tool("project_pack", "Pack a folder (a project's source/archive) into <modName>.archive in outDir through WolvenKit's packer. Returns the archive path, size, file count and warnings for paths with capital letters or spaces. Bridge 0.6.1.", {
|
|
25694
26120
|
dir: external_exports.string().describe("The folder to pack, laid out as game paths (base/..., ep1/...)."),
|
|
25695
26121
|
outDir: external_exports.string().describe("Where the archive goes, usually packed/archive/pc/mod."),
|
|
@@ -25750,13 +26176,13 @@ tool("loc_get", `The texts for LocKeys, by number or secondary key, with or with
|
|
|
25750
26176
|
language: external_exports.string().optional(),
|
|
25751
26177
|
projectDir: external_exports.string().optional()
|
|
25752
26178
|
}, (a, b) => b.locGet(a.keys, a.language ?? "en-us", a.projectDir));
|
|
25753
|
-
var soundCache =
|
|
26179
|
+
var soundCache = path5.join(os2.tmpdir(), "redcode-mcp", "sounds");
|
|
25754
26180
|
var soundIndex;
|
|
25755
26181
|
async function gameSounds(b) {
|
|
25756
26182
|
if (!soundIndex) {
|
|
25757
|
-
const file =
|
|
25758
|
-
if (!
|
|
25759
|
-
soundIndex = parseOpusInfo(
|
|
26183
|
+
const file = path5.join(soundCache, "sfx_container.opusinfo");
|
|
26184
|
+
if (!fs5.existsSync(file)) await b.extract(GameSoundIndex, file);
|
|
26185
|
+
soundIndex = parseOpusInfo(fs5.readFileSync(file));
|
|
25760
26186
|
}
|
|
25761
26187
|
return soundIndex;
|
|
25762
26188
|
}
|
|
@@ -25796,12 +26222,12 @@ tool("sound_extract", "Write game sound effects as .opus files (Ogg Opus, 48 kHz
|
|
|
25796
26222
|
} else {
|
|
25797
26223
|
throw new Error("event or sounds is required");
|
|
25798
26224
|
}
|
|
25799
|
-
|
|
26225
|
+
fs5.mkdirSync(a.outDir, { recursive: true });
|
|
25800
26226
|
const written = [];
|
|
25801
26227
|
for (const i of picks) {
|
|
25802
26228
|
const bytes = await opusSound(b, soundCache, { gamePath: GameSoundIndex }, index, i);
|
|
25803
|
-
const file =
|
|
25804
|
-
|
|
26229
|
+
const file = path5.join(a.outDir, `${index.hashes[i]}.opus`);
|
|
26230
|
+
fs5.writeFileSync(file, bytes);
|
|
25805
26231
|
const f = audioFormat(bytes);
|
|
25806
26232
|
written.push({ file, bytes: bytes.length, channels: f.channels });
|
|
25807
26233
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@edgemodders/redcode-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Cyberpunk 2077 modding tools for AI assistants over MCP: the game archives, CR2W files as JSON and node-by-node edits, textures, TweakDB, game sounds, export, import and pack, through REDbridge and WolvenKit's engine.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Edgemodders",
|